OSDN Git Service

Project: Starfighter 1.4 オリジナル
authormaruhiro <maruhiro@bridge.ocn.ne.jp>
Sun, 27 Dec 2015 09:24:33 +0000 (18:24 +0900)
committermaruhiro <maruhiro@bridge.ocn.ne.jp>
Sun, 27 Dec 2015 09:24:33 +0000 (18:24 +0900)
346 files changed:
COPYING [new file with mode: 0644]
LICENSES [new file with mode: 0644]
Makefile [new file with mode: 0644]
README.txt [new file with mode: 0644]
data/aliens0.dat [new file with mode: 0644]
data/aliens11.dat [new file with mode: 0644]
data/aliens13.dat [new file with mode: 0644]
data/aliens14.dat [new file with mode: 0644]
data/aliens16.dat [new file with mode: 0644]
data/aliens17.dat [new file with mode: 0644]
data/aliens18.dat [new file with mode: 0644]
data/aliens19.dat [new file with mode: 0644]
data/aliens2.dat [new file with mode: 0644]
data/aliens20.dat [new file with mode: 0644]
data/aliens21.dat [new file with mode: 0644]
data/aliens22.dat [new file with mode: 0644]
data/aliens23.dat [new file with mode: 0644]
data/aliens25.dat [new file with mode: 0644]
data/aliens26.dat [new file with mode: 0644]
data/aliens3.dat [new file with mode: 0644]
data/aliens4.dat [new file with mode: 0644]
data/aliens5.dat [new file with mode: 0644]
data/aliens7.dat [new file with mode: 0644]
data/aliens8.dat [new file with mode: 0644]
data/aliens9.dat [new file with mode: 0644]
data/brief1.txt [new file with mode: 0644]
data/brief10.txt [new file with mode: 0644]
data/brief11.txt [new file with mode: 0644]
data/brief12.txt [new file with mode: 0644]
data/brief13.txt [new file with mode: 0644]
data/brief14.txt [new file with mode: 0644]
data/brief15.txt [new file with mode: 0644]
data/brief16.txt [new file with mode: 0644]
data/brief17.txt [new file with mode: 0644]
data/brief18.txt [new file with mode: 0644]
data/brief19.txt [new file with mode: 0644]
data/brief2.txt [new file with mode: 0644]
data/brief20.txt [new file with mode: 0644]
data/brief21.txt [new file with mode: 0644]
data/brief22.txt [new file with mode: 0644]
data/brief23.txt [new file with mode: 0644]
data/brief24.txt [new file with mode: 0644]
data/brief25.txt [new file with mode: 0644]
data/brief26.txt [new file with mode: 0644]
data/brief3.txt [new file with mode: 0644]
data/brief4.txt [new file with mode: 0644]
data/brief5.txt [new file with mode: 0644]
data/brief6.txt [new file with mode: 0644]
data/brief7.txt [new file with mode: 0644]
data/brief8.txt [new file with mode: 0644]
data/brief9.txt [new file with mode: 0644]
data/credits.txt [new file with mode: 0644]
data/cutscene0.dat [new file with mode: 0644]
data/cutscene1.dat [new file with mode: 0644]
data/cutscene2.dat [new file with mode: 0644]
data/cutscene3.dat [new file with mode: 0644]
data/cutscene4.dat [new file with mode: 0644]
data/cutscene5.dat [new file with mode: 0644]
data/cutscene6.dat [new file with mode: 0644]
data/intro.txt [new file with mode: 0644]
data/planets_eyananth.dat [new file with mode: 0644]
data/planets_mordor.dat [new file with mode: 0644]
data/planets_sol.dat [new file with mode: 0644]
data/planets_spirit.dat [new file with mode: 0644]
data/resources_all.dat [new file with mode: 0644]
data/resources_eyananth.dat [new file with mode: 0644]
data/resources_mordor.dat [new file with mode: 0644]
data/resources_sol.dat [new file with mode: 0644]
data/resources_spirit.dat [new file with mode: 0644]
data/script11.txt [new file with mode: 0644]
data/script13.txt [new file with mode: 0644]
data/script16.txt [new file with mode: 0644]
data/script17.txt [new file with mode: 0644]
data/script22.txt [new file with mode: 0644]
data/script23.txt [new file with mode: 0644]
data/script25.txt [new file with mode: 0644]
data/script26.txt [new file with mode: 0644]
data/script5.txt [new file with mode: 0644]
data/script7.txt [new file with mode: 0644]
data/script8.txt [new file with mode: 0644]
docs/COPYING [new file with mode: 0644]
docs/arrowNorthEast.png [new file with mode: 0644]
docs/dollar.png [new file with mode: 0644]
docs/heart.png [new file with mode: 0644]
docs/index.html [new file with mode: 0644]
docs/plasmaAmmo.png [new file with mode: 0644]
docs/plasmaDamage.png [new file with mode: 0644]
docs/plasmaRate.png [new file with mode: 0644]
docs/rocket.png [new file with mode: 0644]
docs/rocketAmmo.png [new file with mode: 0644]
docs/sflogo.png [new file with mode: 0644]
docs/superCharge.png [new file with mode: 0644]
gfx/aimFighter1.png [new file with mode: 0644]
gfx/aimFighter2.png [new file with mode: 0644]
gfx/alienDevice.png [new file with mode: 0644]
gfx/arrowEast.png [new file with mode: 0644]
gfx/arrowNorth.png [new file with mode: 0644]
gfx/arrowNorthEast.png [new file with mode: 0644]
gfx/arrowNorthWest.png [new file with mode: 0644]
gfx/arrowSouth.png [new file with mode: 0644]
gfx/arrowSouthEast.png [new file with mode: 0644]
gfx/arrowSouthWest.png [new file with mode: 0644]
gfx/arrowWest.png [new file with mode: 0644]
gfx/asteroid1.png [new file with mode: 0644]
gfx/asteroid2.png [new file with mode: 0644]
gfx/asteroid3.png [new file with mode: 0644]
gfx/barrier.png [new file with mode: 0644]
gfx/buyIcon.png [new file with mode: 0644]
gfx/cargo1.png [new file with mode: 0644]
gfx/chainLink.png [new file with mode: 0644]
gfx/cloakShip1.png [new file with mode: 0644]
gfx/cloakShip2.png [new file with mode: 0644]
gfx/credits.jpg [new file with mode: 0644]
gfx/cursor.png [new file with mode: 0644]
gfx/dollar.png [new file with mode: 0644]
gfx/drone1.png [new file with mode: 0644]
gfx/drone2.png [new file with mode: 0644]
gfx/dualFighter1.png [new file with mode: 0644]
gfx/dualFighter2.png [new file with mode: 0644]
gfx/elec1.png [new file with mode: 0644]
gfx/elec2.png [new file with mode: 0644]
gfx/elec3.png [new file with mode: 0644]
gfx/elec4.png [new file with mode: 0644]
gfx/eliteFighter1.png [new file with mode: 0644]
gfx/eliteFighter2.png [new file with mode: 0644]
gfx/escort1.png [new file with mode: 0644]
gfx/escort2.png [new file with mode: 0644]
gfx/evilUrsula1.png [new file with mode: 0644]
gfx/evilUrsula2.png [new file with mode: 0644]
gfx/execTrans1.png [new file with mode: 0644]
gfx/execTrans2.png [new file with mode: 0644]
gfx/explode05.png [new file with mode: 0644]
gfx/explode06.png [new file with mode: 0644]
gfx/explode07.png [new file with mode: 0644]
gfx/explode08.png [new file with mode: 0644]
gfx/explode1.png [new file with mode: 0644]
gfx/explode10.png [new file with mode: 0644]
gfx/explode11.png [new file with mode: 0644]
gfx/explode12.png [new file with mode: 0644]
gfx/explode13.png [new file with mode: 0644]
gfx/explode14.png [new file with mode: 0644]
gfx/explode15.png [new file with mode: 0644]
gfx/explode16.png [new file with mode: 0644]
gfx/explode2.png [new file with mode: 0644]
gfx/explode3.png [new file with mode: 0644]
gfx/explode4.png [new file with mode: 0644]
gfx/explode9.png [new file with mode: 0644]
gfx/eyananth.jpg [new file with mode: 0644]
gfx/face_chris.png [new file with mode: 0644]
gfx/face_crew.png [new file with mode: 0644]
gfx/face_kline.png [new file with mode: 0644]
gfx/face_krass.png [new file with mode: 0644]
gfx/face_phoebe.png [new file with mode: 0644]
gfx/face_sid.png [new file with mode: 0644]
gfx/face_ursula.png [new file with mode: 0644]
gfx/firefly-big.png [new file with mode: 0644]
gfx/firefly1.png [new file with mode: 0644]
gfx/firefly2.png [new file with mode: 0644]
gfx/frigateBody1.png [new file with mode: 0644]
gfx/frigateBody2.png [new file with mode: 0644]
gfx/frigateGun11.png [new file with mode: 0644]
gfx/frigateGun12.png [new file with mode: 0644]
gfx/frigateGun21.png [new file with mode: 0644]
gfx/frigateGun22.png [new file with mode: 0644]
gfx/gameover.png [new file with mode: 0644]
gfx/goodTrans1.png [new file with mode: 0644]
gfx/goodTrans2.png [new file with mode: 0644]
gfx/greenDir.png [new file with mode: 0644]
gfx/heart.png [new file with mode: 0644]
gfx/icon1.png [new file with mode: 0644]
gfx/icon10.png [new file with mode: 0644]
gfx/icon11.png [new file with mode: 0644]
gfx/icon12.png [new file with mode: 0644]
gfx/icon13.png [new file with mode: 0644]
gfx/icon14.png [new file with mode: 0644]
gfx/icon15.png [new file with mode: 0644]
gfx/icon16.png [new file with mode: 0644]
gfx/icon17.png [new file with mode: 0644]
gfx/icon18.png [new file with mode: 0644]
gfx/icon19.png [new file with mode: 0644]
gfx/icon2.png [new file with mode: 0644]
gfx/icon20.png [new file with mode: 0644]
gfx/icon21.png [new file with mode: 0644]
gfx/icon22.png [new file with mode: 0644]
gfx/icon23.png [new file with mode: 0644]
gfx/icon24.png [new file with mode: 0644]
gfx/icon25.png [new file with mode: 0644]
gfx/icon26.png [new file with mode: 0644]
gfx/icon3.png [new file with mode: 0644]
gfx/icon4.png [new file with mode: 0644]
gfx/icon5.png [new file with mode: 0644]
gfx/icon6.png [new file with mode: 0644]
gfx/icon7.png [new file with mode: 0644]
gfx/icon8.png [new file with mode: 0644]
gfx/icon9.png [new file with mode: 0644]
gfx/iconBase.png [new file with mode: 0644]
gfx/kline11.png [new file with mode: 0644]
gfx/kline12.png [new file with mode: 0644]
gfx/merc1.png [new file with mode: 0644]
gfx/merc2.png [new file with mode: 0644]
gfx/mine.png [new file with mode: 0644]
gfx/mineBoss1.png [new file with mode: 0644]
gfx/mineBoss2.png [new file with mode: 0644]
gfx/mineBossWing11.png [new file with mode: 0644]
gfx/mineBossWing12.png [new file with mode: 0644]
gfx/mineBossWing21.png [new file with mode: 0644]
gfx/mineBossWing22.png [new file with mode: 0644]
gfx/mineBossWing31.png [new file with mode: 0644]
gfx/mineBossWing32.png [new file with mode: 0644]
gfx/mineBossWing41.png [new file with mode: 0644]
gfx/mineBossWing42.png [new file with mode: 0644]
gfx/miner1.png [new file with mode: 0644]
gfx/miner2.png [new file with mode: 0644]
gfx/missileBoat1.png [new file with mode: 0644]
gfx/missileBoat2.png [new file with mode: 0644]
gfx/mobileCannon1.png [new file with mode: 0644]
gfx/mobileCannon2.png [new file with mode: 0644]
gfx/mobileShield1.png [new file with mode: 0644]
gfx/mobileShield2.png [new file with mode: 0644]
gfx/mordor.jpg [new file with mode: 0644]
gfx/ore1.png [new file with mode: 0644]
gfx/ore2.png [new file with mode: 0644]
gfx/ore3.png [new file with mode: 0644]
gfx/planet_blue.png [new file with mode: 0644]
gfx/planet_green.png [new file with mode: 0644]
gfx/planet_orange.png [new file with mode: 0644]
gfx/planet_red.png [new file with mode: 0644]
gfx/planet_sun.png [new file with mode: 0644]
gfx/plasmaAmmo.png [new file with mode: 0644]
gfx/plasmaDamage.png [new file with mode: 0644]
gfx/plasmaGreen.png [new file with mode: 0644]
gfx/plasmaRate.png [new file with mode: 0644]
gfx/plasmaRed.png [new file with mode: 0644]
gfx/plutoBoss1.png [new file with mode: 0644]
gfx/plutoBoss2.png [new file with mode: 0644]
gfx/pod.png [new file with mode: 0644]
gfx/prlogo.png [new file with mode: 0644]
gfx/rebelCarrier1.png [new file with mode: 0644]
gfx/rebelCarrier2.png [new file with mode: 0644]
gfx/redDir.png [new file with mode: 0644]
gfx/rocket.png [new file with mode: 0644]
gfx/rocket1.png [new file with mode: 0644]
gfx/rocket2.png [new file with mode: 0644]
gfx/rocketAmmo.png [new file with mode: 0644]
gfx/sellIcon.png [new file with mode: 0644]
gfx/sflogo.png [new file with mode: 0644]
gfx/sid1.png [new file with mode: 0644]
gfx/sid2.png [new file with mode: 0644]
gfx/slaveTrans1.png [new file with mode: 0644]
gfx/slaveTrans2.png [new file with mode: 0644]
gfx/smallFont.png [new file with mode: 0644]
gfx/sol.jpg [new file with mode: 0644]
gfx/spirit.jpg [new file with mode: 0644]
gfx/splitBoss11.png [new file with mode: 0644]
gfx/splitBoss12.png [new file with mode: 0644]
gfx/splitBoss21.png [new file with mode: 0644]
gfx/splitBoss22.png [new file with mode: 0644]
gfx/splitBoss31.png [new file with mode: 0644]
gfx/splitBoss32.png [new file with mode: 0644]
gfx/startUp.jpg [new file with mode: 0644]
gfx/stunBolt.png [new file with mode: 0644]
gfx/superCharge.png [new file with mode: 0644]
gfx/targetText.png [new file with mode: 0644]
gfx/transport1.png [new file with mode: 0644]
gfx/transport2.png [new file with mode: 0644]
gfx/tug1.png [new file with mode: 0644]
gfx/tug2.png [new file with mode: 0644]
gfx/wingmate1.png [new file with mode: 0644]
gfx/wingmate2.png [new file with mode: 0644]
music/RE.ogg [new file with mode: 0644]
music/death.ogg [new file with mode: 0644]
music/frozen_jam.ogg [new file with mode: 0644]
music/last_cyber_dance.ogg [new file with mode: 0644]
music/orbital_colossus.ogg [new file with mode: 0644]
music/railjet_short.ogg [new file with mode: 0644]
music/rise_of_spirit.ogg [new file with mode: 0644]
music/sound_and_silence.ogg [new file with mode: 0644]
music/space_dimensions.ogg [new file with mode: 0644]
music/through_space.ogg [new file with mode: 0644]
music/walking_among_androids.ogg [new file with mode: 0644]
sound/beamLaser.ogg [new file with mode: 0644]
sound/cloak.ogg [new file with mode: 0644]
sound/clock.ogg [new file with mode: 0644]
sound/explode.ogg [new file with mode: 0644]
sound/explode2.ogg [new file with mode: 0644]
sound/explode3.ogg [new file with mode: 0644]
sound/explode4.ogg [new file with mode: 0644]
sound/flyby.ogg [new file with mode: 0644]
sound/hyperSpace.ogg [new file with mode: 0644]
sound/item.ogg [new file with mode: 0644]
sound/laser.ogg [new file with mode: 0644]
sound/maledeath.ogg [new file with mode: 0644]
sound/missile.ogg [new file with mode: 0644]
sound/missile2.ogg [new file with mode: 0644]
sound/plasma.ogg [new file with mode: 0644]
sound/plasma2.ogg [new file with mode: 0644]
sound/plasma3.ogg [new file with mode: 0644]
sound/shield.ogg [new file with mode: 0644]
src/Starfighter.cpp [new file with mode: 0644]
src/Starfighter.h [new file with mode: 0644]
src/alien.cpp [new file with mode: 0644]
src/alien.h [new file with mode: 0644]
src/audio.cpp [new file with mode: 0644]
src/audio.h [new file with mode: 0644]
src/bullet.cpp [new file with mode: 0644]
src/bullet.h [new file with mode: 0644]
src/cargo.cpp [new file with mode: 0644]
src/cargo.h [new file with mode: 0644]
src/collectable.cpp [new file with mode: 0644]
src/collectable.h [new file with mode: 0644]
src/defs.h [new file with mode: 0644]
src/engine.cpp [new file with mode: 0644]
src/engine.h [new file with mode: 0644]
src/explosion.cpp [new file with mode: 0644]
src/explosion.h [new file with mode: 0644]
src/game.cpp [new file with mode: 0644]
src/game.h [new file with mode: 0644]
src/graphics.cpp [new file with mode: 0644]
src/graphics.h [new file with mode: 0644]
src/init.cpp [new file with mode: 0644]
src/init.h [new file with mode: 0644]
src/intermission.cpp [new file with mode: 0644]
src/intermission.h [new file with mode: 0644]
src/loadSave.cpp [new file with mode: 0644]
src/loadSave.h [new file with mode: 0644]
src/messages.cpp [new file with mode: 0644]
src/messages.h [new file with mode: 0644]
src/misc.cpp [new file with mode: 0644]
src/misc.h [new file with mode: 0644]
src/missions.cpp [new file with mode: 0644]
src/missions.h [new file with mode: 0644]
src/player.cpp [new file with mode: 0644]
src/player.h [new file with mode: 0644]
src/resources.cpp [new file with mode: 0644]
src/resources.h [new file with mode: 0644]
src/script.cpp [new file with mode: 0644]
src/script.h [new file with mode: 0644]
src/ship.cpp [new file with mode: 0644]
src/ship.h [new file with mode: 0644]
src/shop.cpp [new file with mode: 0644]
src/shop.h [new file with mode: 0644]
src/structs.h [new file with mode: 0644]
src/title.cpp [new file with mode: 0644]
src/title.h [new file with mode: 0644]
src/weapons.cpp [new file with mode: 0644]
src/weapons.h [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  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.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  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.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            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
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU 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 Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/LICENSES b/LICENSES
new file mode 100644 (file)
index 0000000..a3984b1
--- /dev/null
+++ b/LICENSES
@@ -0,0 +1,628 @@
+data/*
+docs/*
+
+License: GNU GPLv3 or later <https://gnu.org/licenses/gpl.html>
+
+------------------------------------------------------------------------
+
+gfx/alienDevice.png
+gfx/prlogo.png
+gfx/sflogo.png
+
+Author: Parallel Realities <http://www.parallelrealities.co.uk/>
+License: GNU GPLv2 or later <https://gnu.org/licenses/old-licenses/gpl-2.0.html>
+
+------------------------------------------------------------------------
+
+gfx/arrowEast.png
+gfx/arrowNorth.png
+gfx/arrowNorthEast.png
+gfx/arrowNorthWest.png
+gfx/arrowSouth.png
+gfx/arrowSouthEast.png
+gfx/arrowSouthWest.png
+gfx/arrowWest.png
+gfx/buyIcon.png
+gfx/chainLink.png
+gfx/cursor.png
+gfx/elec1.png
+gfx/elec2.png
+gfx/elec3.png
+gfx/elec4.png
+gfx/explode1.png
+gfx/explode2.png
+gfx/explode3.png
+gfx/explode4.png
+gfx/explode9.png
+gfx/explode10.png
+gfx/explode11.png
+gfx/explode12.png
+gfx/explode13.png
+gfx/explode14.png
+gfx/explode15.png
+gfx/explode16.png
+gfx/gameover.png
+gfx/greenDir.png
+gfx/icon1.png
+gfx/icon2.png
+gfx/icon3.png
+gfx/icon6.png
+gfx/icon7.png
+gfx/icon8.png
+gfx/icon11.png
+gfx/iconBase.png
+gfx/plasmaGreen.png
+gfx/plasmaRed.png
+gfx/redDir.png
+gfx/sellIcon.png
+gfx/smallFont.png
+gfx/stunBolt.png
+gfx/targetText.png
+
+Author: Astrid S. de Wijn
+License: CC BY-SA 3.0 <https://creativecommons.org/licenses/by-sa/3.0/>
+
+------------------------------------------------------------------------
+
+gfx/face_chris.png
+gfx/face_phoebe.png
+gfx/face_sid.png
+gfx/face_ursula.png
+
+Authors:
+  CharlesGabriel <http://cgartsenal.blogspot.com/>
+  Lachlan Cartland <http://lachlancartland.weebly.com/>
+License: CC BY 3.0 <https://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/48x48-faces-2nd-sheet
+
+------------------------------------------------------------------------
+
+gfx/face_crew.png
+gfx/face_krass.png
+
+Authors:
+  CharlesGabriel <http://cgartsenal.blogspot.com/>
+  Lachlan Cartland <http://lachlancartland.weebly.com/>
+License: CC BY 3.0 <https://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/48x48-faces-3rd-sheet
+
+------------------------------------------------------------------------
+
+gfx/face_kline.png
+
+Authors:
+  CharlesGabriel <http://cgartsenal.blogspot.com/>
+  Astrid
+  Lachlan Cartland <http://lachlancartland.weebly.com/>
+License: CC BY 3.0 <https://creativecommons.org/licenses/by/3.0/>
+Sources:
+  http://opengameart.org/content/48x48-faces-2nd-sheet
+  http://opengameart.org/content/48x48-faces-4th-sheet
+Details:
+  Astrid combined parts from the two sheets to make the initial version.
+  Lachlan Cartland added his own changes after that.
+
+------------------------------------------------------------------------
+
+gfx/planet_sun.png
+
+Author: NASA <http://sdo.gsfc.nasa.gov/>
+License: Public Domain
+Source: http://opengameart.org/content/the-sun
+
+------------------------------------------------------------------------
+
+gfx/icon26.png
+gfx/planet_blue.png
+gfx/planet_green.png
+gfx/planet_orange.png
+gfx/planet_red.png
+
+Author: athile <http://opengameart.org/users/athile>
+License: CC BY 3.0 <https://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/planets-pack-8
+
+------------------------------------------------------------------------
+
+gfx/drone1.png
+gfx/drone2.png
+gfx/merc1.png
+gfx/merc2.png
+gfx/mineBoss1.png
+gfx/mineBoss2.png
+gfx/mineBossWing11.png
+gfx/mineBossWing12.png
+gfx/mineBossWing21.png
+gfx/mineBossWing22.png
+gfx/mineBossWing31.png
+gfx/mineBossWing32.png
+gfx/mineBossWing41.png
+gfx/mineBossWing42.png
+gfx/missileBoat1.png
+gfx/missileBoat2.png
+gfx/tug1.png
+gfx/tug2.png
+
+Authors:
+  surt <http://opengameart.org/users/surt>
+  astrid
+License: CC BY-SA 3.0 <https://creativecommons.org/licenses/by-sa/3.0/>
+Source: http://opengameart.org/content/astrominer-sprites
+
+------------------------------------------------------------------------
+
+gfx/mine.png
+
+Author: Daniel Cook <http://lostgarden.com/>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/iron-plague-minebmp
+
+------------------------------------------------------------------------
+
+gfx/transport1.png
+gfx/transport2.png
+
+Author: Daniel Cook <http://lostgarden.com/>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/iron-plague-cargo2bmp
+
+------------------------------------------------------------------------
+
+gfx/dualFighter1.png
+gfx/dualFighter2.png
+
+Author: Daniel Cook <http://lostgarden.com/>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/iron-plague-hunter1bmp
+
+------------------------------------------------------------------------
+
+gfx/mobileCannon1.png
+gfx/mobileCannon2.png
+
+Author: Daniel Cook <http://lostgarden.com/>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/iron-plague-ship4bmp
+
+------------------------------------------------------------------------
+
+gfx/aimFighter1.png
+gfx/aimFighter2.png
+
+Author: Daniel Cook <http://lostgarden.com/>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/iron-plague-doomdaybmp
+
+------------------------------------------------------------------------
+
+gfx/barrier.png
+
+Author: Daniel Cook <http://lostgarden.com/>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/iron-plague-beacon2bmp
+
+------------------------------------------------------------------------
+
+gfx/wingmate1.png
+gfx/wingmate2.png
+gfx/evilUrsula1.png
+gfx/evilUrsula2.png
+
+Author: Daniel Cook <http://lostgarden.com/>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/iron-plague-bshipbmp
+
+------------------------------------------------------------------------
+
+gfx/rebelCarrier1.png
+gfx/rebelCarrier2.png
+
+Author: Daniel Cook <http://lostgarden.com/>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/iron-plague-cargo1bmp
+
+------------------------------------------------------------------------
+
+gfx/sid1.png
+gfx/sid2.png
+
+Author: Daniel Cook <http://lostgarden.com/>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/iron-plague-ship2bmp
+
+------------------------------------------------------------------------
+
+gfx/kline11.png
+gfx/kline12.png
+
+Author: Daniel Cook <http://lostgarden.com/>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/iron-plague-hunter2bmp
+
+------------------------------------------------------------------------
+
+gfx/plutoBoss1.png
+gfx/plutoBoss2.png
+gfx/splitBoss11.png
+gfx/splitBoss12.png
+gfx/splitBoss21.png
+gfx/splitBoss22.png
+gfx/splitBoss31.png
+gfx/splitBoss32.png
+
+Author: Daniel Cook <http://lostgarden.com/>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/iron-plague-factory2bmp
+
+------------------------------------------------------------------------
+
+gfx/mobileShield1.png
+gfx/mobileShield2.png
+
+Author: Daniel Cook <http://lostgarden.com/>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/iron-plague-warpbmp
+
+------------------------------------------------------------------------
+
+gfx/cloakShip1.png
+gfx/cloakShip2.png
+
+Author: Archanix <http://www.archanix.webs.com/>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/battlefront-operation-x-final-striker
+
+------------------------------------------------------------------------
+
+gfx/goodTrans1.png
+gfx/goodTrans2.png
+
+Authors:
+  Rusty
+  Archanix
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/ship-recoloring-001-for-gm-game
+
+------------------------------------------------------------------------
+
+gfx/escort1.png
+gfx/escort2.png
+
+Author: Clint Bellanger <http://opengameart.org/users/clint-bellanger>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/spaceship-360
+
+------------------------------------------------------------------------
+
+gfx/execTrans1.png
+gfx/execTrans2.png
+gfx/frigateBody1.png
+gfx/frigateBody2.png
+gfx/frigateGun11.png
+gfx/frigateGun12.png
+gfx/frigateGun21.png
+gfx/frigateGun22.png
+
+Authors:
+  Skorpio <http://opengameart.org/users/skorpio>
+  Astrid
+License: CC BY-SA 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/spaceships-top-down
+
+------------------------------------------------------------------------
+
+gfx/miner1.png
+gfx/miner2.png
+gfx/slaveTrans1.png
+gfx/slaveTrans2.png
+gfx/eliteFighter1.png
+gfx/eliteFighter2.png
+
+Author: Skorpio <http://opengameart.org/users/skorpio>
+License: CC BY-SA 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/spaceships2-top-down
+
+------------------------------------------------------------------------
+
+gfx/icon5.png
+gfx/firefly1.png
+gfx/firefly2.png
+gfx/firefly-big.png
+
+Author: Lamoot
+License: CC0 <http://creativecommons.org/publicdomain/zero/1.0/>
+Source: http://opengameart.org/content/top-down-space-fighter-sprite
+
+------------------------------------------------------------------------
+
+gfx/icon9.png
+gfx/icon10.png
+gfx/icon12.png
+gfx/icon14.png
+gfx/icon15.png
+gfx/icon16.png
+gfx/icon17.png
+gfx/icon23.png
+gfx/rocket.png
+gfx/superCharge.png
+gfx/cargo1.png
+gfx/plasmaDamage.png
+gfx/plasmaAmmo.png
+gfx/plasmaRate.png
+gfx/heart.png
+gfx/dollar.png
+gfx/pod.png
+
+Authors:
+  Ails <http://ails.deviantart.com/>
+  Astrid
+License: CC BY-SA 3.0 <http://creativecommons.org/licenses/by-sa/3.0/>
+Source: http://opengameart.org/content/420-pixel-art-icons-for-medievalfantasy-rpg
+
+------------------------------------------------------------------------
+
+gfx/icon13.png
+gfx/icon18.png
+gfx/icon19.png
+gfx/icon20.png
+gfx/icon22.png
+gfx/icon24.png
+gfx/icon25.png
+gfx/rocket1.png
+gfx/rocket2.png
+gfx/rocketAmmo.png
+
+Authors:
+  OceansDream <http://oceansdream.deviantart.com/art/Pixel-Icons-available-for-use-121152370>
+  Astrid
+License: CC BY-SA 3.0 <http://creativecommons.org/licenses/by-sa/3.0/>
+Source: http://opengameart.org/content/various-inventory-24-pixel-icon-set
+
+------------------------------------------------------------------------
+
+icon21.png
+
+Authors:
+  Torsten Henning
+  Astrid
+License: Public Domain
+Source: http://en.wikipedia.org/wiki/File:DIN_4844-2_Warnung_vor_Laserstrahl_D-W010.svg
+
+------------------------------------------------------------------------
+
+icon4.png
+
+Authors:
+  Gobusto <http://opengameart.org/users/gobusto>
+  Astrid
+License: CC BY-SA 3.0 <http://creativecommons.org/licenses/by-sa/3.0/>
+Source: http://opengameart.org/content/floppy-disk
+
+------------------------------------------------------------------------
+
+gfx/explode05.png
+gfx/explode06.png
+gfx/explode07.png
+gfx/explode08.png
+
+Author: Cuzco <http://opengameart.org/users/cuzco>
+License: CC0 <http://creativecommons.org/publicdomain/zero/1.0/>
+Source: http://opengameart.org/content/explosion
+
+------------------------------------------------------------------------
+
+gfx/asteroid1.png
+gfx/asteroid2.png
+gfx/asteroid3.png
+
+Author: dczanik <http://opengameart.org/users/dczanik>
+License: CC BY-SA 3.0 <http://creativecommons.org/licenses/by-sa/3.0/>
+Source: http://opengameart.org/content/asteroid-rotating
+
+------------------------------------------------------------------------
+
+gfx/ore2.png
+
+Author: phaelax <http://opengameart.org/users/phaelax>
+License: CC BY-SA 3.0 <http://creativecommons.org/licenses/by-sa/3.0/>
+Source: http://opengameart.org/content/asteroids
+
+------------------------------------------------------------------------
+
+gfx/ore1.png
+gfx/ore3.png
+
+Author: phaelax <http://opengameart.org/users/phaelax>
+License: CC BY-SA 3.0 <http://creativecommons.org/licenses/by-sa/3.0/>
+Source: http://opengameart.org/content/asteroids
+Details: Colorized with the GIMP.
+
+------------------------------------------------------------------------
+
+gfx/startUp.jpg
+
+Author: NASA
+License: Public Domain
+Source: http://hubblesite.org/gallery/album/galaxy/pr2009014a/
+
+------------------------------------------------------------------------
+
+gfx/spirit.jpg
+
+Author: NASA
+License: Public Domain
+Source: http://hubblesite.org/gallery/album/star/pr1992029a/warn/npp/all/
+
+------------------------------------------------------------------------
+
+gfx/eyananth.jpg
+
+Author: NASA
+License: Public Domain
+Source: http://hubblesite.org/gallery/album/nebula/pr2009025e/
+
+------------------------------------------------------------------------
+
+gfx/mordor.jpg
+
+Author: NASA
+License: Public Domain
+Source: http://hubblesite.org/gallery/album/nebula/pr2006001q/
+
+------------------------------------------------------------------------
+
+gfx/sol.jpg
+
+Author: NASA
+License: Public Domain
+Source: http://hubblesite.org/gallery/album/star/pr2007004a/npp/all/
+
+------------------------------------------------------------------------
+
+gfx/credits.jpg
+
+Author: NASA
+License: Public Domain
+Source: http://hubblesite.org/gallery/album/nebula/pr2005012b/
+
+------------------------------------------------------------------------
+
+sound/flyby.ogg
+
+Author: Person <http://opengameart.org/users/person>
+License: CC0 <http://creativecommons.org/publicdomain/zero/1.0/>
+Source: http://opengameart.org/content/rocket-fly-8-bit
+
+------------------------------------------------------------------------
+
+sound/clock.ogg
+sound/explode.ogg
+sound/explode2.ogg
+sound/hyperSpace.ogg
+sound/item.ogg
+sound/laser.ogg
+sound/missile.ogg
+sound/missile2.ogg
+sound/plasma3.ogg
+
+
+Author: Stephen M. Cameron <http://sourceforge.net/users/smcameron>
+License: CC BY-SA 3.0 <http://creativecommons.org/licenses/by-sa/3.0/>
+Source: http://opengameart.org/content/action-shooter-soundset-wwvi
+
+------------------------------------------------------------------------
+
+sound/beamLaser.ogg
+sound/cloak.ogg
+sound/explode3.ogg
+sound/explode4.ogg
+sound/plasma.ogg
+sound/plasma2.ogg
+sound/shield.ogg
+
+Author: p0ss <http://opengameart.org/users/p0ss>
+License: CC BY-SA 3.0 <http://creativecommons.org/licenses/by-sa/3.0/>
+Source: http://opengameart.org/content/spell-sounds-starter-pack
+
+------------------------------------------------------------------------
+
+sound/maledeath.ogg
+
+Authors: Berklee
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: https://archive.org/details/Berklee44v5 (scream_1.wav)
+Details: Tuned down 5 half-notes.
+
+------------------------------------------------------------------------
+
+music/walking_among_androids.ogg
+
+Author: Zander Noriega <https://soundcloud.com/zander-noriega>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/walking-among-androids-20
+
+------------------------------------------------------------------------
+
+music/through_space.ogg
+
+Author: maxstack <http://opengameart.org/users/maxstack>
+License: CC BY-SA 3.0 <http://creativecommons.org/licenses/by-sa/3.0/>
+Source: http://opengameart.org/content/through-space
+Changes:
+  Amplified +0.83 dB, rate changed from 48000 to 44100, and end trimmed
+  off in Audacity.
+
+------------------------------------------------------------------------
+
+music/railjet_short.ogg
+
+Author: DST <http://www.nosoapradio.us/>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/railjet-qubodup-short-cut-loop
+
+------------------------------------------------------------------------
+
+music/space_dimensions.ogg
+
+Author: matthew.pablo <http://opengameart.org/users/matthewpablo>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/space-dimensions-8bitretro-version
+
+------------------------------------------------------------------------
+
+music/frozen_jam.ogg
+
+Author: tgfcoder <https://twitter.com/tgfcoder>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/frozen-jam-seamless-loop
+Changes: Amplified -1 dB with Audacity
+
+------------------------------------------------------------------------
+
+music/sound_and_silence.ogg
+
+Author: JunglistMilitia <http://sampleswap.org/profile.php?mode=viewprofile&u=57802&sid=d08796b3d86eaf235bc1a460d5869b37>
+License: CC BY-SA 3.0 <http://creativecommons.org/licenses/by-sa/3.0/>
+Source: http://opengameart.org/content/sound-silence
+
+------------------------------------------------------------------------
+
+music/orbital_colossus.ogg
+
+Author: matthew.pablo <http://opengameart.org/users/matthewpablo>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/space-boss-battle-theme
+
+------------------------------------------------------------------------
+
+music/last_cyber_dance.ogg
+
+Author: neocrey <https://twitter.com/artnocy>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/last-cyber-dance
+
+------------------------------------------------------------------------
+
+music/RE.ogg
+
+Author: WeskerHunter <http://redeclipse.net/forum/viewtopic.php?f=5&t=26>
+License: CC BY-SA 3.0 <http://creativecommons.org/licenses/by-sa/3.0/>
+Source: http://opengameart.org/content/fast-high-music
+Changes: Amplified -1 dB and slowed down 5% with Audacity
+
+------------------------------------------------------------------------
+
+music/death.ogg
+
+Author: Jan125 <http://opengameart.org/users/jan125>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/stereotypical-90s-space-shooter-music
+
+------------------------------------------------------------------------
+
+music/rise_of_spirit.ogg
+
+Author: Alexandr Zhelanov <https://soundcloud.com/alexandr-zhelanov>
+License: CC BY 3.0 <http://creativecommons.org/licenses/by/3.0/>
+Source: http://opengameart.org/content/rise-of-spirit
+Changes: Amplified +6 dB with Audacity
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..0a514db
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,56 @@
+CXXFLAGS ?= -O2 -Wall -g
+CXXFLAGS += `pkg-config --cflags sdl2 SDL2_image SDL2_mixer`
+LIBS = `pkg-config --libs sdl2 SDL2_image SDL2_mixer`
+OBJS = alien.o audio.o bullet.o cargo.o collectable.o engine.o explosion.o game.o graphics.o init.o intermission.o loadSave.o messages.o misc.o missions.o player.o resources.o script.o ship.o shop.o Starfighter.o title.o weapons.o
+
+VERSION = 1.4
+PROG = starfighter
+DOCS = docs/*
+DATA = data gfx sound music
+DATAFILES = data/* gfx/* sound/* music/*
+
+PREFIX ?= /usr
+BINDIR ?= $(PREFIX)/games/
+DATADIR ?= $(PREFIX)/share/games/parallelrealities/
+DOCDIR ?= $(PREFIX)/share/doc/$(PROG)/
+
+# top-level rule to create the program.
+ALL = $(PROG)
+
+all: $(ALL)
+
+# compiling other source files.
+%.o: src/%.cpp src/*.h
+       $(CXX) $(CXXFLAGS) -c -DVERSION=\"$(VERSION)\" -DDATADIR=\"$(DATADIR)\" $<
+
+# linking the program.
+$(PROG): $(OBJS)
+       $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $(PROG) $(LIBS)
+
+# cleaning everything that can be automatically recreated with "make".
+clean:
+       $(RM) $(OBJS) $(ALL)
+
+# install
+install: $(ALL)
+       mkdir -p $(DESTDIR)$(BINDIR)
+       mkdir -p $(DESTDIR)$(DATADIR)
+       mkdir -p $(DESTDIR)$(DOCDIR)
+
+       install -m 755 $(PROG) $(DESTDIR)$(BINDIR)$(PROG)
+       cp -pr $(DATA) $(DESTDIR)$(DATADIR)
+       cp -p $(DOCS) $(DESTDIR)$(DOCDIR)
+
+optimise:
+       advpng -z gfx/*.png
+       jpegoptim --strip-all gfx/*.jpg
+
+dist:
+       rm -rf starfighter-$(VERSION)
+       mkdir starfighter-$(VERSION)
+       cp --parents -lt starfighter-$(VERSION) `git ls-files`
+       git log >starfighter-$(VERSION)/ChangeLog
+       tar czf starfighter-$(VERSION).tar.gz starfighter-$(VERSION)
+       rm -rf starfighter-$(VERSION)
+
+.PHONY: all clean install optimise dist
diff --git a/README.txt b/README.txt
new file mode 100644 (file)
index 0000000..a7ee90b
--- /dev/null
@@ -0,0 +1,61 @@
+Copyright (C) 2015 Julian Marchant <onpon4@riseup.net>
+
+Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved.  This file is offered as-is,
+without any warranty.
+
+========================================================================
+
+
+Thank you for downloading Project: Starfighter! This readme is just a
+basic overview of the game and instructions for building; for
+instructions on how to play the game, see docs/index.html.
+
+Project: Starfighter is a space shoot 'em up game originally developed
+by Parallel Realities in 2002, and released in 2003. You assume the role
+of Chris Bainfield in his quest to put an end to WEAPCO, the weapons
+corporation which has enslaved the galaxy.
+
+If you played one of the versions of Project: Starfighter distributed by
+Parallel Realities, you might notice some differences in this version
+compared to the original:
+
+* The graphics, sounds, and music are all completely different. This is
+  because most of the original media was not properly licensed, and
+  therefore needed to be replaced to make the game 100% libre and legal.
+  Most of this work was done by the previous project at SourceForge;
+  this project completed that work by adding music and replacing one
+  non-libre graphic that got into version 1.2 by mistake.
+
+* There are several gameplay changes. These changes were mostly done to
+  balance the game better. However, if you want the original experience,
+  "original" difficulty approximates it as closely as possible (short of
+  implementing bugs on purpose).
+
+* Typing "humansdoitbetter" in the title screen no longer enables
+  cheats. This is actually because the switch to SDL2 broke the original
+  feature, and rather than fixing it, I just replaced it with something
+  else. I'll let you figure out what the new method to access the cheat
+  menu is. :)
+
+
+COMPILING FROM SOURCE
+
+Project: Starfighter depends on the following libraries to build:
+
+* SDL2 <http://libsdl.org>
+* SDL2_image <http://www.libsdl.org/projects/SDL_image/>
+* SDL2_mixer <http://www.libsdl.org/projects/SDL_mixer/>
+
+Once you have all dependencies installed, simply run:
+
+    make
+
+And optionally (as root):
+
+    make install
+
+If this doesn't work, you may need to tweak the makefile.
+
+To play, simply run the starfighter binary.
\ No newline at end of file
diff --git a/data/aliens0.dat b/data/aliens0.dat
new file mode 100644 (file)
index 0000000..ff59ce6
--- /dev/null
@@ -0,0 +1,2 @@
+0 7
+-1
diff --git a/data/aliens11.dat b/data/aliens11.dat
new file mode 100644 (file)
index 0000000..de4b8fe
--- /dev/null
@@ -0,0 +1,10 @@
+14 15
+13 17
+12 18
+11 16
+10 19
+15 10
+
+20 12
+21 12
+-1
diff --git a/data/aliens13.dat b/data/aliens13.dat
new file mode 100644 (file)
index 0000000..dded96f
--- /dev/null
@@ -0,0 +1,2 @@
+14 22
+-1
diff --git a/data/aliens14.dat b/data/aliens14.dat
new file mode 100644 (file)
index 0000000..7999c55
--- /dev/null
@@ -0,0 +1,8 @@
+14 15
+13 17
+12 18
+11 16
+10 19
+19 29
+20 29
+-1
diff --git a/data/aliens16.dat b/data/aliens16.dat
new file mode 100644 (file)
index 0000000..9938270
--- /dev/null
@@ -0,0 +1,5 @@
+10 21
+11 21
+12 21
+19 23
+-1
diff --git a/data/aliens17.dat b/data/aliens17.dat
new file mode 100644 (file)
index 0000000..7fc91a1
--- /dev/null
@@ -0,0 +1,7 @@
+0 27
+1 27
+2 27
+3 27
+4 27
+14 24
+-1
diff --git a/data/aliens18.dat b/data/aliens18.dat
new file mode 100644 (file)
index 0000000..12f8a05
--- /dev/null
@@ -0,0 +1,2 @@
+14 24
+-1
diff --git a/data/aliens19.dat b/data/aliens19.dat
new file mode 100644 (file)
index 0000000..5d3a477
--- /dev/null
@@ -0,0 +1,8 @@
+10 31
+11 31
+12 31
+13 31
+14 30
+20 31
+21 31
+-1
diff --git a/data/aliens2.dat b/data/aliens2.dat
new file mode 100644 (file)
index 0000000..d12520e
--- /dev/null
@@ -0,0 +1,7 @@
+0 8
+1 8
+2 8
+3 8
+4 8
+5 8
+-1
diff --git a/data/aliens20.dat b/data/aliens20.dat
new file mode 100644 (file)
index 0000000..a28e594
--- /dev/null
@@ -0,0 +1,7 @@
+10 33
+11 33
+12 33
+13 33
+14 32
+20 33
+-1
diff --git a/data/aliens21.dat b/data/aliens21.dat
new file mode 100644 (file)
index 0000000..6156c6c
--- /dev/null
@@ -0,0 +1,4 @@
+12 37
+13 38
+14 36
+-1
diff --git a/data/aliens22.dat b/data/aliens22.dat
new file mode 100644 (file)
index 0000000..56d2099
--- /dev/null
@@ -0,0 +1,7 @@
+9 28
+10 28
+11 28
+12 28
+13 28
+14 28
+-1
diff --git a/data/aliens23.dat b/data/aliens23.dat
new file mode 100644 (file)
index 0000000..5284dbd
--- /dev/null
@@ -0,0 +1,6 @@
+10 35
+11 35
+12 35
+13 35
+14 23
+-1
diff --git a/data/aliens25.dat b/data/aliens25.dat
new file mode 100644 (file)
index 0000000..d5d45d4
--- /dev/null
@@ -0,0 +1,2 @@
+15 10
+-1
diff --git a/data/aliens26.dat b/data/aliens26.dat
new file mode 100644 (file)
index 0000000..d5d45d4
--- /dev/null
@@ -0,0 +1,2 @@
+15 10
+-1
diff --git a/data/aliens3.dat b/data/aliens3.dat
new file mode 100644 (file)
index 0000000..569640d
--- /dev/null
@@ -0,0 +1,6 @@
+0 1
+1 1
+2 1
+3 1
+4 1
+-1
diff --git a/data/aliens4.dat b/data/aliens4.dat
new file mode 100644 (file)
index 0000000..3c64cae
--- /dev/null
@@ -0,0 +1,10 @@
+0 9
+1 9
+2 9
+3 9
+4 9
+5 9
+6 9
+7 9
+8 9
+-1 
diff --git a/data/aliens5.dat b/data/aliens5.dat
new file mode 100644 (file)
index 0000000..ac2fe0a
--- /dev/null
@@ -0,0 +1,4 @@
+12 6
+13 5
+14 4
+-1
diff --git a/data/aliens7.dat b/data/aliens7.dat
new file mode 100644 (file)
index 0000000..682bc72
--- /dev/null
@@ -0,0 +1,2 @@
+0 8
+-1
diff --git a/data/aliens8.dat b/data/aliens8.dat
new file mode 100644 (file)
index 0000000..4246b4d
--- /dev/null
@@ -0,0 +1,2 @@
+19 13
+-1
diff --git a/data/aliens9.dat b/data/aliens9.dat
new file mode 100644 (file)
index 0000000..4d7d6d2
--- /dev/null
@@ -0,0 +1,6 @@
+20 8
+21 8
+22 8
+23 8
+24 8
+-1
diff --git a/data/brief1.txt b/data/brief1.txt
new file mode 100644 (file)
index 0000000..b0da8cb
--- /dev/null
@@ -0,0 +1,10 @@
+Krass Tyler
+FACE_KRASS
+Hey, boy! You still owe me money for the Firefly I stole for you! But instead, I want you to go to the WEAPCO training ground and destroy all the craft there.
+FACE_CHRIS
+Oh? That's the job I contracted you to do, was it not?
+FACE_KRASS
+I know, but this way we can resolve your debt right now. Do this job, and also collect $500, and we will call it quits. And if you die... well, I guess the ship was not worth stealing! HA HA HA!
+FACE_CHRIS
+As usual, you take me too lightly, Krass.
+
diff --git a/data/brief10.txt b/data/brief10.txt
new file mode 100644 (file)
index 0000000..ce65ed1
--- /dev/null
@@ -0,0 +1,5 @@
+Sid Wilson
+FACE_SID
+A WEAPCO scientist just ran off in an escape pod and hid in the asteroid belt. If we capture him, we may be able to get some information about Mordor.
+FACE_CHRIS
+I'm on it.
diff --git a/data/brief11.txt b/data/brief11.txt
new file mode 100644 (file)
index 0000000..725721f
--- /dev/null
@@ -0,0 +1,9 @@
+Phoebe Lexx
+FACE_SID
+I've received word that the slaves we rescued have started a rebellion. Looks like the plan worked.
+FACE_PHOEBE
+WEAPCO has an automated mining ship in orbit around Elamale. How about we take it out and cause some confusion?
+FACE_CHRIS
+I like that idea!
+FACE_SID
+It'll work, but be careful.
diff --git a/data/brief12.txt b/data/brief12.txt
new file mode 100644 (file)
index 0000000..203010d
--- /dev/null
@@ -0,0 +1,9 @@
+Sid Wilson
+FACE_CHRIS
+What have you found out about that experimental fighter?
+FACE_SID
+It's got some kind of cloaking device that makes it invisible to radar. Could prove hard to track down.
+FACE_CHRIS
+I'll just have to run around the system until I run into it.
+FACE_SID
+It's likely to run away if you engage it in battle, so try and do as much damage to it as possible.
diff --git a/data/brief13.txt b/data/brief13.txt
new file mode 100644 (file)
index 0000000..54d9aa0
--- /dev/null
@@ -0,0 +1,11 @@
+Phoebe Lexx
+FACE_PHOEBE
+I've located my sister's ship currently in orbit around Odeon. She's ignoring my hails though.
+FACE_SID
+Something's off here. She seems to be travelling freely with a WEAPCO group.
+FACE_CHRIS
+Do you think she's turned traitor?
+FACE_PHOEBE
+No way. She hates WEAPCO with a passion.
+FACE_SID
+She must be under some kind of mind control. I've heard of WEAPCO developing a new "AI training program" recently. We'd better rescue her!
diff --git a/data/brief14.txt b/data/brief14.txt
new file mode 100644 (file)
index 0000000..dc404b5
--- /dev/null
@@ -0,0 +1,5 @@
+Sid Wilson
+FACE_SID
+A rebel group has organized a counter strike. If we can help them secure a victory it will be a real boost to morale.
+FACE_CHRIS
+Awesome! I'm on it!
diff --git a/data/brief15.txt b/data/brief15.txt
new file mode 100644 (file)
index 0000000..7021404
--- /dev/null
@@ -0,0 +1,7 @@
+Sid Wilson
+FACE_SID
+Seems like taking out that WEAPCO mining ship wasn't such a good idea. The Ore it collected from those asteroids is needed in weapon production.
+FACE_CHRIS
+Damn! I guess that means I'll have to mine some myself, then, huh?
+FACE_SID
+Yes. Be careful, Chris. Your weapons weren't designed for that sort of work and could have some devastating results.
diff --git a/data/brief16.txt b/data/brief16.txt
new file mode 100644 (file)
index 0000000..d11ad0f
--- /dev/null
@@ -0,0 +1,5 @@
+Krass Tyler
+FACE_CHRIS
+Hey, Krass! I need you to help us out with something. Phoebe and Ursula are taking out key WEAPCO plants. Can you help me create a diversion by wreaking havoc a little bit away from that?
+FACE_KRASS
+Sure, I can help you out, boy. But I'll be needing my fee...
diff --git a/data/brief17.txt b/data/brief17.txt
new file mode 100644 (file)
index 0000000..afed732
--- /dev/null
@@ -0,0 +1,7 @@
+Ursula Lexx
+FACE_URSULA
+I've remembered something. WEAPCO is transporting some high level personnel to Poswic.
+FACE_SID
+This could be really important to our success in Sol. I'll need some cover so I can disable that ship.
+FACE_CHRIS
+You got it!
diff --git a/data/brief18.txt b/data/brief18.txt
new file mode 100644 (file)
index 0000000..1ccdeb8
--- /dev/null
@@ -0,0 +1,7 @@
+Phoebe Lexx
+FACE_CHRIS
+Phoebe, I need you to keep an eye on things here. I'm going after that ship!
+FACE_PHOEBE
+Are you sure you can catch up to it?
+FACE_CHRIS
+Is that a challenge? Heh heh, don't worry about it. One thing that's really nice about the Firefly is its speed. I'll see you in a bit.
diff --git a/data/brief19.txt b/data/brief19.txt
new file mode 100644 (file)
index 0000000..20b8c5d
--- /dev/null
@@ -0,0 +1,9 @@
+Sid Wilson
+FACE_SID
+We've got to start from the outside and work our way in. That will give us less chance of being flanked during the final operation.
+FACE_PHOEBE
+Gotcha.
+FACE_URSULA
+Okay.
+FACE_CHRIS
+Alright.
diff --git a/data/brief2.txt b/data/brief2.txt
new file mode 100644 (file)
index 0000000..1f5aaca
--- /dev/null
@@ -0,0 +1,10 @@
+Sid Wilson
+FACE_CHRIS
+Hey, Sid, what's up?
+FACE_SID
+Chris, I've intercepted a communication from WEAPCO. Seems they're transporting some medical supplies around Ceradse. We need to get hold of those pods to save some lives!
+FACE_CHRIS
+How many do we need?
+FACE_SID
+All six, Chris! If you lose even a single one, thousands of people could perish in Spirit within the next few months.
+
diff --git a/data/brief20.txt b/data/brief20.txt
new file mode 100644 (file)
index 0000000..20b8c5d
--- /dev/null
@@ -0,0 +1,9 @@
+Sid Wilson
+FACE_SID
+We've got to start from the outside and work our way in. That will give us less chance of being flanked during the final operation.
+FACE_PHOEBE
+Gotcha.
+FACE_URSULA
+Okay.
+FACE_CHRIS
+Alright.
diff --git a/data/brief21.txt b/data/brief21.txt
new file mode 100644 (file)
index 0000000..20b8c5d
--- /dev/null
@@ -0,0 +1,9 @@
+Sid Wilson
+FACE_SID
+We've got to start from the outside and work our way in. That will give us less chance of being flanked during the final operation.
+FACE_PHOEBE
+Gotcha.
+FACE_URSULA
+Okay.
+FACE_CHRIS
+Alright.
diff --git a/data/brief22.txt b/data/brief22.txt
new file mode 100644 (file)
index 0000000..9ff7d86
--- /dev/null
@@ -0,0 +1,9 @@
+Sid Wilson
+FACE_SID
+WEAPCO has set up a highly dangerous defense line between Saturn and Uranus. We'll need to take it out.
+FACE_URSULA
+What kind of defense system?
+FACE_SID
+Several mobile Energy Ray cannons, not unlike the weapon used by the Star Killer back in Spirit.
+FACE_PHOEBE
+Best check my ejection system then!
diff --git a/data/brief23.txt b/data/brief23.txt
new file mode 100644 (file)
index 0000000..4d8c3e4
--- /dev/null
@@ -0,0 +1,7 @@
+Sid Wilson
+FACE_SID
+While you were gone I picked up a distress call coming from around Jupiter.
+FACE_URSULA
+Who would be sending out a distress call within Sol?
+FACE_CHRIS
+Let's check it out. Even if it's a trap, I think we can handle it.
diff --git a/data/brief24.txt b/data/brief24.txt
new file mode 100644 (file)
index 0000000..a7492a4
--- /dev/null
@@ -0,0 +1,5 @@
+Sid Wilson
+FACE_SID
+Chris, we've got a small problem. WEAPCO has a minefield in the asteroid belt. We'll need you to clear a way through.
+FACE_CHRIS
+Alright. I'll radio in once I've cleared a safe path.
diff --git a/data/brief25.txt b/data/brief25.txt
new file mode 100644 (file)
index 0000000..4b8a0a6
--- /dev/null
@@ -0,0 +1,9 @@
+Everyone
+FACE_CHRIS
+Okay people, this is the big one. We go in fast and we go in hard. Don't hold back and hit them with everything we've got!
+FACE_SID
+We've come too far to turn back now. None of us better die out there!
+FACE_PHOEBE
+Right with you, Chris.
+FACE_URSULA
+WEAPCO'll regret sticking probes into my head!
diff --git a/data/brief26.txt b/data/brief26.txt
new file mode 100644 (file)
index 0000000..47d6843
--- /dev/null
@@ -0,0 +1,5 @@
+Sid Wilson
+FACE_CHRIS
+Kethlan has run off to Venus. I'm going after him.
+FACE_SID
+Be careful, Chris. We've won the war, but it would be a real shame if you died now!
diff --git a/data/brief3.txt b/data/brief3.txt
new file mode 100644 (file)
index 0000000..43a3264
--- /dev/null
@@ -0,0 +1,10 @@
+Sid Wilson
+FACE_CHRIS
+Wow! Missile boats?
+FACE_SID
+Yup. Looks like WEAPCO is starting to take notice of your actions.
+FACE_CHRIS
+Sounds like fun! This will really put the Firefly's fighting ability to the test!
+FACE_SID
+Please be careful, Chris. A single missile boat carries enough rockets to level most major cities. Try not to face them head-on, and keep your distance.
+
diff --git a/data/brief4.txt b/data/brief4.txt
new file mode 100644 (file)
index 0000000..ca3e929
--- /dev/null
@@ -0,0 +1,8 @@
+Sid Wilson
+FACE_SID
+We're going to have to get rid of the mine deployment unit around Joldar. The minefield is stopping interplanetary traffic.
+FACE_CHRIS
+Sounds kind of boring. Any fighters?
+FACE_SID
+Not at the moment, but that doesn't mean they won't turn up. Be very careful of those mines! They'll only explode when they encounter a ship that's not transmitting a WEAPCO signal. Shoot them down if they get in your way.
+
diff --git a/data/brief5.txt b/data/brief5.txt
new file mode 100644 (file)
index 0000000..e75db85
--- /dev/null
@@ -0,0 +1,7 @@
+Sid Wilson
+FACE_SID
+We've got a major problem here! WEAPCO has decided to stop our resistance by destroying Spirit! The explosion will incinerate everything in the system! You've got to destroy that frigate before it gets in range!
+FACE_CHRIS
+Damn! I'll get right on it, then!
+FACE_SID
+We're all counting on you, Chris! But just remember - They didn't call that thing "Star Killer" just because it sounded nice!
diff --git a/data/brief6.txt b/data/brief6.txt
new file mode 100644 (file)
index 0000000..dfd47ea
--- /dev/null
@@ -0,0 +1,7 @@
+Sid Wilson
+FACE_SID
+As you know, WEAPCO has many slaves in this system. If we free a large number of them, it might help to spark a rebellion. I estimate that we will need to rescue around 250 to make a difference.
+FACE_CHRIS
+Most of the slaves are working in ore mines, aren't they?
+FACE_SID
+Yes, but attacking the ore mines directly would be dangerous. You'd be better off intercepting slave transports. What you'll have to do is fly around and see if you can intercept a WEAPCO patrol. Of course, they might not be escorting any slave units, so be careful!
diff --git a/data/brief7.txt b/data/brief7.txt
new file mode 100644 (file)
index 0000000..6eac2d9
--- /dev/null
@@ -0,0 +1,9 @@
+(unknown)
+FACE_PHOEBE
+Help! This is an SOS! Can anyone hear me?!
+FACE_CHRIS
+I'm hearing you loud and clear! What's up?
+FACE_PHOEBE
+Oh, thank God! I was intercepted by a large WEAPCO force near Nerod! I'm in need of assistance!
+FACE_CHRIS
+I'm on my way!
diff --git a/data/brief8.txt b/data/brief8.txt
new file mode 100644 (file)
index 0000000..1357905
--- /dev/null
@@ -0,0 +1,5 @@
+Sid Wilson
+FACE_SID
+I've just received another SOS. This one is coming from a supply craft carrying essential medical supplies.
+FACE_CHRIS
+Alright, Tell 'em I'm on my way.
diff --git a/data/brief9.txt b/data/brief9.txt
new file mode 100644 (file)
index 0000000..48e57b2
--- /dev/null
@@ -0,0 +1,9 @@
+Sid Wilson
+FACE_SID
+I need some resources before we leave, it'll make life a lot easier in Mordor. Problem is that WEAPCO hoards these parts.
+FACE_CHRIS
+Where can we get them, then?
+FACE_SID
+There's a big shipment of them nearby. I can disable the supply craft carrying them; I just need you to give me some cover while I do it.
+FACE_CHRIS
+You got it!
diff --git a/data/credits.txt b/data/credits.txt
new file mode 100644 (file)
index 0000000..def26ea
--- /dev/null
@@ -0,0 +1,69 @@
+050 +++ CAST +++
+030 Chris Bainfield
+020 Krass Tylar
+020 Sid Wilson
+020 Kline Kethlan
+020 Phoebe Lexx
+020 Ursula Lexx
+050 +++ MAIN PROGRAMMING AND DESIGN +++
+020 Stephen Sweeney
+030 +++ ADDITIONAL PROGRAMMING +++
+020 Richard Sweeney
+020 Guus Sliepen
+020 Julian Marchant
+050 +++ QUALITY ASSURANCE AND ADVICE +++
+030 Sam Lantinga  Ian Bissett
+020 Chris Taylor  Xan  Christoph Reichenbach
+020 Kevin Mills  Michael Edwards  Kyle
+020 Michael Edwards  Halfabee  Doug
+020 Boris  Frank S  Martin Svensson
+020 Willy Gardiol  Liam  Keith Frost
+050 +++ GRAPHICS +++
+030 Parallel Realities
+020 Astrid S. de Wijn
+020 CharlesGabriel
+020 Lachlan Cartland
+020 NASA
+020 athile
+020 surt
+020 Daniel Cook
+020 Archanix
+020 Rusty
+020 Clint Bellanger
+020 Skorpio
+020 Lamoot
+020 Ails
+020 OceansDream
+020 Torsten Henning
+020 Gobusto
+020 Cuzco
+020 dczanik
+020 phaelax
+050 +++ SOUND +++
+030 Person
+020 Stephen M. Cameron
+020 p0ss
+020 Berklee
+050 +++ MUSIC +++
+030 Zander Noriega
+020 maxstack
+020 Deceased Superior Technician
+020 matthew.pablo
+020 tgfcoder
+020 JunglistMilitia
+020 neocrey
+020 WeskerHunter
+020 Jan125
+020 Alexandr Zhelanov
+050 +++ SPECIAL THANKS +++
+030 Wes Whitmore  Ceri Done
+020 Faye Price  Emma Hamilton
+020 Rob Jones  Ian Bissett  Maria Kalashnikoff
+020 Sam Lantinga  Claus Windeler  Jolan Luff
+020 Torbjorn Andersson  Atrix Wolfe
+020 and all on the SDL mailing list
+350 Project: Starfighter
+020 Copyright 2003 Parallel Realities
+020 Copyright 2012 Guus Sliepen, Astrid S. de Wijn and others
+020 Copyright 2015 Julian Marchant
+120 starfighter.nongnu.org
diff --git a/data/cutscene0.dat b/data/cutscene0.dat
new file mode 100644 (file)
index 0000000..98fc271
--- /dev/null
@@ -0,0 +1,27 @@
+gfx/spirit.jpg
+-3 0
+
+0 0 480 300 3.1
+1 2 -100 -550 3
+2 2 -100 -550 3
+3 2 -100 -550 3
+4 2 -100 -550 3
+5 2 -100 -550 3
+6 2 -100 -550 3
+7 2 -100 -550 3
+8 2 -100 -550 3
+9 2 -100 -550 3
+10 2 -100 -550 3
+11 2 -100 -550 3
+12 2 -100 -550 3
+13 2 -100 -550 3
+14 2 -100 -550 3
+
+FACE_NONE
+While escaping with his newly acquired Firefly, Chris Bainfield is intercepted by a WEAPCO patrol...
+FACE_CHRIS
+These things just won't give up, will they?
+FACE_CHRIS
+What a dilemma! I'm not all that familiar with this ship's controls, but I can't let this patrol reach my rendezvous point...
+FACE_CHRIS
+I guess I'll have to fight them, then. Let's see what this ship can do!
diff --git a/data/cutscene1.dat b/data/cutscene1.dat
new file mode 100644 (file)
index 0000000..79c9d57
--- /dev/null
@@ -0,0 +1,29 @@
+gfx/sol.jpg
+-0.5 0
+
+0 18 400 300 0.5
+1 2 -800 -550 -3
+2 2 -800 -550 -3
+3 2 -800 -550 -3
+4 2 -800 -550 -3
+5 2 -800 -550 -3
+6 2 -800 -550 -3
+7 2 -800 -550 -3
+8 2 -800 -550 -3
+9 2 -800 -550 -3
+10 2 -800 -550 -3
+11 2 -800 -550 -3
+12 2 -800 -550 -3
+13 2 -800 -550 -3
+14 2 -800 -550 -3
+
+FACE_NONE
+A few hours later, in the Sol system, news has already spread of Chris Bainfield's heroic actions. The commander of WEAPCO's navy considers his options.
+FACE_KLINE
+The Emperor will not be pleased. Spirit is now a free star system thanks to that interfering rebel pilot.
+FACE_KLINE
+It was reported that he was able to take down one of our most powerful frigates in under 3 minutes!
+FACE_KLINE
+Talent like that does not appear every day. He would be a perfect candidate for our new AI training program!
+FACE_KLINE
+What a pity I must kill him...
diff --git a/data/cutscene2.dat b/data/cutscene2.dat
new file mode 100644 (file)
index 0000000..29b75e9
--- /dev/null
@@ -0,0 +1,33 @@
+gfx/spirit.jpg
+-1.4 0
+
+0 0 200 300 1.5
+1 24 150 260 1.5
+2 -1 0 0 0
+3 -1 0 0 0
+4 -1 0 0 0
+5 -1 0 0 0
+6 -1 0 0 0
+7 -1 0 0 0
+8 -1 0 0 0
+9 -1 0 0 0
+10 -1 0 0 0
+11 -1 0 0 0
+12 -1 0 0 0
+13 -1 0 0 0
+14 -1 0 0 0
+
+FACE_SID
+We're nearly ready to make the jump to Eyananth.
+FACE_CHRIS
+Aren't there a lot of WEAPCO slaves in this system?
+FACE_SID
+Correct. It's got one of the highest mortality rates in the galaxy.
+FACE_CHRIS
+You think we can use that to our advantage?
+FACE_SID
+I think so. I'll come up with a plan of action.
+FACE_SID
+Just remember that we won't be in Spirit anymore and you may run into WEAPCO patrols while travelling.
+FACE_CHRIS
+Sounds like fun!
diff --git a/data/cutscene3.dat b/data/cutscene3.dat
new file mode 100644 (file)
index 0000000..6e7b78d
--- /dev/null
@@ -0,0 +1,35 @@
+gfx/eyananth.jpg
+-0.5 0
+
+0 0 300 300 0.5
+1 24 250 260 0.5
+2 20 250 360 0.5
+3 -1 0 0 0
+4 -1 0 0 0
+5 -1 0 0 0
+6 -1 0 0 0
+7 -1 0 0 0
+8 -1 0 0 0
+9 -1 0 0 0
+10 -1 0 0 0
+11 -1 0 0 0
+12 -1 0 0 0
+13 -1 0 0 0
+14 -1 0 0 0
+
+FACE_PHOEBE
+Nice head gear! You shop at the same place as me, huh?
+FACE_CHRIS
+More importantly, what were you doing out there? You're lucky I was around!
+FACE_PHOEBE
+I'm looking for my sister. She vanished about a week ago.
+FACE_PHOEBE
+Hey! Wait a moment! You're that rebel from Spirit! I think you're so cool! Can I come with you?
+FACE_CHRIS
+Extra firepower? I wouldn't mind one bit! What do you think, Sid?
+FACE_SID
+I agree. I'm also interested in studying her homing missile launcher; it could come in handy.
+FACE_CHRIS
+In that case, welcome aboard!
+FACE_NONE
+Phoebe Lexx will now join you as a Wingmate. However, she will not be available on certain missions.
diff --git a/data/cutscene4.dat b/data/cutscene4.dat
new file mode 100644 (file)
index 0000000..97785b4
--- /dev/null
@@ -0,0 +1,37 @@
+gfx/eyananth.jpg
+-1.4 0
+
+0 0 200 300 1.5
+1 24 150 260 1.5
+2 20 110 300 1.5
+3 -1 0 0 0
+4 -1 0 0 0
+5 -1 0 0 0
+6 -1 0 0 0
+7 -1 0 0 0
+8 -1 0 0 0
+9 -1 0 0 0
+10 -1 0 0 0
+11 -1 0 0 0
+12 -1 0 0 0
+13 -1 0 0 0
+14 -1 0 0 0
+
+FACE_SID
+What happened back there, Chris? The video feed was jammed.
+FACE_CHRIS
+We took down the WEAPCO mining vessel and then I was jumped by a man claiming to be Kline Kethlan.
+FACE_SID
+I've heard of him. He's the Commander of WEAPCO's naval forces. One of the best pilots they ever had.
+FACE_CHRIS
+He did put up one hell of a fight! He didn't stick around for long, though. I guess he was just testing me.
+FACE_CHRIS
+Anyway, what's the scoop on Mordor, Sid?
+FACE_SID
+I've learned from the scientist we captured that WEAPCO is testing a new fighter craft there.
+FACE_CHRIS
+We should probably destroy that craft, then. We might be able to thwart its development somewhat.
+FACE_SID
+Agreed.
+FACE_PHOEBE
+I wonder if my sister will be here...
diff --git a/data/cutscene5.dat b/data/cutscene5.dat
new file mode 100644 (file)
index 0000000..41ba588
--- /dev/null
@@ -0,0 +1,31 @@
+gfx/mordor.jpg
+-0.5 0
+
+0 0 300 300 0.5
+1 24 250 260 0.5
+2 20 250 360 0.5
+3 -1 0 0 0
+4 -1 0 0 0
+5 -1 0 0 0
+6 -1 0 0 0
+7 -1 0 0 0
+8 -1 0 0 0
+9 -1 0 0 0
+10 -1 0 0 0
+11 -1 0 0 0
+12 -1 0 0 0
+13 -1 0 0 0
+14 -1 0 0 0
+
+FACE_PHOEBE
+Will she be okay?
+FACE_SID
+I've had a look at the implants and they should be easy to remove. She'll just have a headache for a while.
+FACE_CHRIS
+Will she be able to tell us anything useful?
+FACE_SID
+We'll have to wait for her memory to come back. She might not be able to remember anything she did while the implants were in.
+FACE_SID
+She'll still be able to pilot a ship though.
+FACE_NONE
+Ursula Lexx will now join you as a Wingmate. However, like Phoebe, she will not be available on certain missions.
diff --git a/data/cutscene6.dat b/data/cutscene6.dat
new file mode 100644 (file)
index 0000000..6fb950e
--- /dev/null
@@ -0,0 +1,29 @@
+gfx/mordor.jpg
+-1.4 0
+
+0 0 200 300 1.5
+1 24 150 260 1.5
+2 20 110 300 1.5
+3 20 150 360 1.5
+4 -1 0 0 0
+5 -1 0 0 0
+6 -1 0 0 0
+7 -1 0 0 0
+8 -1 0 0 0
+9 -1 0 0 0
+10 -1 0 0 0
+11 -1 0 0 0
+12 -1 0 0 0
+13 -1 0 0 0
+14 -1 0 0 0
+
+FACE_CHRIS
+Sorry folks, we just lost our bargaining chip.
+FACE_SID
+Knowing how cut throat WEAPCO is I doubt it would have made a difference anyway.
+FACE_SID
+Sol is going to be difficult. I've heard they have a lot of heavy defenses on the outer planets.
+FACE_PHOEBE
+We'll have to start there then.
+FACE_SID
+The forces here will be unlike anything we've met so far. Just be careful, everyone.
diff --git a/data/intro.txt b/data/intro.txt
new file mode 100644 (file)
index 0000000..0be315d
--- /dev/null
@@ -0,0 +1,9 @@
+00 2579AD
+60 After decades of war the intergalatic weapons corportation, WEAPCO, 
+20 has defeated all opposition itself and now rules the known universe
+20 with an iron fist. Using a huge number of powerful AI driven craft
+20 the company was completely unstoppable.
+40 Under WEAPCO's rule, millions suffered and died.
+40 The people cried out for a saviour... for someone to come and
+20 light this dark hour...
+80 ... and someone did.
diff --git a/data/planets_eyananth.dat b/data/planets_eyananth.dat
new file mode 100644 (file)
index 0000000..edbb403
--- /dev/null
@@ -0,0 +1,81 @@
+15
+Nerod
+31
+30
+Allez
+32
+30
+Urusor
+33
+20
+Dorim
+31
+40
+Elamale
+34
+-1
+NONE
+0
+-1
+NONE
+0
+-1
+NONE
+0
+-1
+NONE
+0
+-1
+NONE
+0
+
+7
+1
+FACE_PHOEBE
+??????????? (Nerod)
+SOS
+8
+2
+FACE_SID
+Sid Wilson (Allez)
+Assist medical supply craft
+9
+0
+FACE_SID
+Sid Wilson (Urusor)
+Capture five WEAPCO supply craft
+10
+1
+FACE_SID
+Sid Wilson (Dorim)
+Find WEAPCO scientist
+11
+0
+FACE_SID
+Sid Wilson (Elamale)
+Destroy WEAPCO Ore Mining craft
+-1
+-1
+NONE
+NONE
+NONE
+-1
+-1
+NONE
+NONE
+NONE
+-1
+-1
+NONE
+NONE
+NONE
+-1
+-1
+NONE
+NONE
+NONE
+6
+0
+FACE_SID
+Sid Wilson (none)
+Intercept slave transports
diff --git a/data/planets_mordor.dat b/data/planets_mordor.dat
new file mode 100644 (file)
index 0000000..6e4775d
--- /dev/null
@@ -0,0 +1,81 @@
+15
+Odeon
+31
+30
+Fellon
+32
+30
+Sivedi
+33
+20
+Almartha
+31
+20
+Poswic
+34
+40
+Ellesh
+31
+-1
+NONE
+0
+-1
+NONE
+0
+-1
+NONE
+0
+-1
+NONE
+0
+
+13
+1
+FACE_PHOEBE
+Phoebe Lexx (Odeon)
+Rendevous with Ursula
+14
+2
+FACE_SID
+Sid Wilson (Fellon)
+Escort military operation units
+15
+0
+FACE_SID
+Sid Wilson (Sivedi)
+Mine ore from asteroid belt
+16
+1
+FACE_KRASS
+Krass Tyler (Almartha)
+Create a diversion
+17
+0
+FACE_URSULA
+Ursula Lexx (Poswic)
+Intercept WEAPCO private transport
+18
+0
+FACE_PHOEBE
+Phoebe Lexx (Ellesh)
+Pursue WEAPCO executive
+-1
+-1
+NONE
+NONE
+NONE
+-1
+-1
+NONE
+NONE
+NONE
+-1
+-1
+NONE
+NONE
+NONE
+12
+0
+FACE_SID
+Sid Wilson (none)
+Destroy experimental fighter
diff --git a/data/planets_sol.dat b/data/planets_sol.dat
new file mode 100644 (file)
index 0000000..5862c18
--- /dev/null
@@ -0,0 +1,81 @@
+15
+Mercury
+33
+20
+Venus
+34
+20
+Earth
+32
+20
+Mars
+33
+30
+Jupiter
+34
+20
+Saturn
+31
+20
+Uranus
+31
+20
+Neptune
+31
+20
+Pluto
+31
+-1
+NONE
+0
+
+-1
+-1
+NONE
+NONE
+NONE
+26
+0
+FACE_SID
+Sid Wilson (Venus)
+End Game
+25
+0
+FACE_CHRIS
+Everyone (Earth)
+Take back Earth
+24
+0
+FACE_SID
+Sid Wilson (Mars)
+Destroy inner defence system
+23
+0
+FACE_SID
+Sid Wilson (Jupiter)
+Investigate distress call
+22
+0
+FACE_SID
+Sid Wilson (Saturn)
+Destroy outer defence system
+21
+2
+FACE_URSULA
+Ursula Lexx (Uranus)
+Secure Uranus
+20
+1
+FACE_PHOEBE
+Phoebe Lexx (Neptune)
+Secure Neptune
+19
+0
+FACE_SID
+Sid Wilson (Pluto)
+Secure Pluto
+-1
+-1
+NONE
+NONE
+NONE
diff --git a/data/planets_spirit.dat b/data/planets_spirit.dat
new file mode 100644 (file)
index 0000000..f65504f
--- /dev/null
@@ -0,0 +1,82 @@
+15
+Hail
+31
+30
+Ceradse
+32
+30
+Hinstag
+33
+20
+Joldar
+31
+40
+Moebo
+34
+-1
+NONE
+0
+-1
+NONE
+0
+-1
+NONE
+0
+-1
+NONE
+0
+-1
+NONE
+0
+
+1
+0
+FACE_KRASS
+Krass Tyler (Hail)
+Destroy WEAPCO training ground
+2
+1
+FACE_SID
+Sid Wilson (Ceradse)
+Collect 6 cargo pods
+3
+2
+FACE_SID
+Sid Wilson (Hinstag)
+Destroy 5 WEAPCO missile boats
+4
+3
+FACE_SID
+Sid Wilson (Joldar)
+Clear the mine field around Joldar
+5
+0
+FACE_SID
+Sid Wilson (Moebo)
+Destroy WEAPCO frigate
+-1
+-1
+NONE
+NONE
+NONE
+-1
+-1
+NONE
+NONE
+NONE
+-1
+-1
+NONE
+NONE
+NONE
+-1
+-1
+NONE
+NONE
+NONE
+-1
+-1
+NONE
+NONE
+NONE
+
diff --git a/data/resources_all.dat b/data/resources_all.dat
new file mode 100644 (file)
index 0000000..c9442ff
--- /dev/null
@@ -0,0 +1,59 @@
+0 gfx/plasmaGreen.png
+1 gfx/plasmaRed.png
+2 gfx/rocket1.png
+3 gfx/rocket2.png
+4 gfx/explode1.png
+5 gfx/explode2.png
+6 gfx/explode3.png
+7 gfx/explode4.png
+8 gfx/explode05.png
+9 gfx/explode06.png
+10 gfx/explode07.png
+11 gfx/explode08.png
+12 gfx/explode9.png
+13 gfx/explode10.png
+14 gfx/explode11.png
+15 gfx/explode12.png
+16 gfx/explode13.png
+17 gfx/explode14.png
+18 gfx/explode15.png
+19 gfx/explode16.png
+20 gfx/elec1.png
+21 gfx/elec2.png
+22 gfx/elec3.png
+23 gfx/elec4.png
+24 gfx/dollar.png
+25 gfx/rocket.png
+26 gfx/heart.png
+27 gfx/plasmaAmmo.png
+28 gfx/plasmaRate.png
+29 gfx/plasmaDamage.png
+30 gfx/chainLink.png
+31 gfx/mine.png
+32 gfx/cargo1.png
+33 gfx/greenDir.png
+34 gfx/redDir.png
+35 gfx/stunBolt.png
+36 gfx/arrowNorth.png
+37 gfx/arrowNorthEast.png
+38 gfx/arrowEast.png
+39 gfx/arrowSouthEast.png
+40 gfx/arrowSouth.png
+41 gfx/arrowSouthWest.png
+42 gfx/arrowWest.png
+43 gfx/arrowNorthWest.png
+44 gfx/targetText.png
+45 gfx/pod.png
+46 gfx/ore1.png
+47 gfx/ore2.png
+48 gfx/ore3.png
+49 gfx/rocketAmmo.png
+50 gfx/superCharge.png
+90 gfx/face_chris.png
+91 gfx/face_sid.png
+92 gfx/face_krass.png
+93 gfx/face_kline.png
+94 gfx/face_phoebe.png
+95 gfx/face_ursula.png
+96 gfx/face_crew.png
+-1
diff --git a/data/resources_eyananth.dat b/data/resources_eyananth.dat
new file mode 100644 (file)
index 0000000..737aae3
--- /dev/null
@@ -0,0 +1,40 @@
+2 gfx/dualFighter1.png
+3 gfx/dualFighter2.png
+4 gfx/missileBoat1.png
+5 gfx/missileBoat2.png
+6 gfx/eliteFighter1.png
+7 gfx/eliteFighter2.png
+8 gfx/aimFighter1.png
+9 gfx/aimFighter2.png
+10 gfx/slaveTrans1.png
+11 gfx/slaveTrans2.png
+12 gfx/goodTrans1.png
+13 gfx/goodTrans2.png
+14 gfx/transport1.png
+15 gfx/transport2.png
+16 gfx/miner1.png
+17 gfx/miner2.png
+18 gfx/kline11.png
+19 gfx/kline12.png
+20 gfx/wingmate1.png
+21 gfx/wingmate2.png
+22 gfx/tug1.png
+23 gfx/tug2.png
+24 gfx/sid1.png
+25 gfx/sid2.png
+26 gfx/mineBoss1.png
+27 gfx/mineBoss2.png
+28 gfx/mineBossWing11.png
+29 gfx/mineBossWing12.png
+30 gfx/mineBossWing21.png
+31 gfx/mineBossWing22.png
+32 gfx/mineBossWing31.png
+33 gfx/mineBossWing32.png
+34 gfx/mineBossWing41.png
+35 gfx/mineBossWing42.png
+36 gfx/drone1.png
+37 gfx/drone2.png
+38 gfx/asteroid1.png
+39 gfx/asteroid2.png
+40 gfx/asteroid3.png
+-1
diff --git a/data/resources_mordor.dat b/data/resources_mordor.dat
new file mode 100644 (file)
index 0000000..e84bebe
--- /dev/null
@@ -0,0 +1,51 @@
+2 gfx/dualFighter1.png
+3 gfx/dualFighter2.png
+4 gfx/missileBoat1.png
+5 gfx/missileBoat2.png
+6 gfx/eliteFighter1.png
+7 gfx/eliteFighter2.png
+8 gfx/aimFighter1.png
+9 gfx/aimFighter2.png
+10 gfx/cloakShip1.png
+11 gfx/cloakShip2.png
+12 gfx/evilUrsula1.png
+13 gfx/evilUrsula2.png
+14 gfx/transport1.png
+15 gfx/transport2.png
+16 gfx/miner1.png
+17 gfx/miner2.png
+18 gfx/kline11.png
+19 gfx/kline12.png
+20 gfx/wingmate1.png
+21 gfx/wingmate2.png
+22 gfx/tug1.png
+23 gfx/tug2.png
+24 gfx/sid1.png
+25 gfx/sid2.png
+26 gfx/merc1.png
+27 gfx/merc2.png
+28 gfx/execTrans1.png
+29 gfx/execTrans2.png
+30 gfx/escort1.png
+31 gfx/escort2.png
+32 gfx/rebelCarrier1.png
+33 gfx/rebelCarrier2.png
+34 gfx/drone1.png
+35 gfx/drone2.png
+36 gfx/drone1.png
+37 gfx/drone2.png
+38 gfx/asteroid1.png
+39 gfx/asteroid2.png
+40 gfx/asteroid3.png
+41 gfx/mineBoss1.png
+42 gfx/mineBoss2.png
+43 gfx/mineBossWing11.png
+44 gfx/mineBossWing12.png
+45 gfx/mineBossWing21.png
+46 gfx/mineBossWing22.png
+47 gfx/mineBossWing31.png
+48 gfx/mineBossWing32.png
+49 gfx/mineBossWing41.png
+50 gfx/mineBossWing42.png
+
+-1
diff --git a/data/resources_sol.dat b/data/resources_sol.dat
new file mode 100644 (file)
index 0000000..8b361e4
--- /dev/null
@@ -0,0 +1,44 @@
+2 gfx/dualFighter1.png
+3 gfx/dualFighter2.png
+4 gfx/missileBoat1.png
+5 gfx/missileBoat2.png
+6 gfx/eliteFighter1.png
+7 gfx/eliteFighter2.png
+8 gfx/aimFighter1.png
+9 gfx/aimFighter2.png
+10 gfx/mobileCannon1.png
+11 gfx/mobileCannon2.png
+12 gfx/plutoBoss1.png
+13 gfx/plutoBoss2.png
+14 gfx/transport1.png
+15 gfx/transport2.png
+16 gfx/miner1.png
+17 gfx/miner2.png
+18 gfx/kline11.png
+19 gfx/kline12.png
+20 gfx/wingmate1.png
+21 gfx/wingmate2.png
+22 gfx/tug1.png
+23 gfx/tug2.png
+24 gfx/sid1.png
+25 gfx/sid2.png
+26 gfx/merc1.png
+27 gfx/merc2.png
+30 gfx/escort1.png
+31 gfx/escort2.png
+32 gfx/barrier.png
+33 gfx/barrier.png
+34 gfx/mobileShield1.png
+35 gfx/mobileShield2.png
+36 gfx/drone1.png
+37 gfx/drone2.png
+38 gfx/asteroid1.png
+39 gfx/asteroid2.png
+40 gfx/asteroid3.png
+41 gfx/splitBoss11.png
+42 gfx/splitBoss12.png
+43 gfx/splitBoss21.png
+44 gfx/splitBoss22.png
+45 gfx/splitBoss31.png
+46 gfx/splitBoss32.png
+-1
diff --git a/data/resources_spirit.dat b/data/resources_spirit.dat
new file mode 100644 (file)
index 0000000..ca1493b
--- /dev/null
@@ -0,0 +1,23 @@
+2 gfx/dualFighter1.png
+3 gfx/dualFighter2.png
+4 gfx/missileBoat1.png
+5 gfx/missileBoat2.png
+6 gfx/eliteFighter1.png
+7 gfx/eliteFighter2.png
+8 gfx/frigateBody1.png
+9 gfx/frigateBody2.png
+10 gfx/frigateGun11.png
+11 gfx/frigateGun12.png
+12 gfx/frigateGun21.png
+13 gfx/frigateGun22.png
+14 gfx/transport1.png
+15 gfx/transport2.png
+16 gfx/miner1.png
+17 gfx/miner2.png
+18 gfx/kline11.png
+19 gfx/kline12.png
+22 gfx/tug1.png
+23 gfx/tug2.png
+24 gfx/sid1.png
+25 gfx/sid2.png
+-1
diff --git a/data/script11.txt b/data/script11.txt
new file mode 100644 (file)
index 0000000..8f8ffdb
--- /dev/null
@@ -0,0 +1,61 @@
+5
+FACE_PHOEBE
+-1
+0
+Ummm... something about this doesn't look right! They should be offering more resistance than this!
+
+-8
+FACE_PHOEBE
+-1
+0
+DAMMIT! It's a trap! Come on, Chris, we have to leave NOW!!
+
+-9
+-1
+16
+128
+@none@
+
+-13
+FACE_SID
+-1
+0
+Chris, wh...
+
+-15
+FACE_KLINE
+-1
+0
+So finally I meet the man who has been causing us so much trouble!
+
+-20
+FACE_CHRIS
+-1
+0
+I see you've jammed up my warp drive. Who are you?
+
+-25
+FACE_KLINE
+-1
+0
+I am Kline Kethlan. And I am here to put to a stop to your worthless little crusade!
+
+-31
+FACE_CHRIS
+-1
+0
+Kline Kethlan, huh? You sure sound confident.
+
+-36
+FACE_KLINE
+-1
+0
+Nowhere to run, nowhere to hide, and no one to help you! This will certainly be interesting...
+
+-42
+-1
+15
+-20
+@none@
+
+0
diff --git a/data/script13.txt b/data/script13.txt
new file mode 100644 (file)
index 0000000..e4efb6e
--- /dev/null
@@ -0,0 +1,31 @@
+2
+FACE_URSULA
+-1
+0
+Unidentified craft sighted. Will proceed to destroy all targets.
+
+8
+FACE_CHRIS
+-1
+0
+Looks like she's got one of those homing missile launchers too! Any advice, Phoebe?
+
+14
+FACE_PHOEBE
+-1
+0
+Just try and dodge them!
+
+180
+FACE_SID
+-1
+0
+Chris! Ursula's running away!
+
+195
+-1
+0
+14
+128
+
+0
diff --git a/data/script16.txt b/data/script16.txt
new file mode 100644 (file)
index 0000000..6861e25
--- /dev/null
@@ -0,0 +1,61 @@
+1
+FACE_CHRIS
+-1
+0
+Nice ship!
+
+7
+FACE_KRASS
+-1
+0
+Thanks, boy. Made it myself!
+
+60
+FACE_KRASS
+-1
+0
+Hey, boy, we've got company! Looks like your friends didn't do a very good job after all!
+
+60
+-1
+10
+-1048576
+@none@
+
+90
+-1
+11
+-1048576
+@none@
+
+93
+FACE_KRASS
+-1
+0
+Keep those things off my back or it'll cost you extra!!!
+
+120
+-1
+12
+-1048576
+@none@
+
+140
+FACE_PHOEBE
+16
+-1048576
+Hey!! Did we miss anything exciting?
+
+140
+-1
+17
+-1048576
+@none@
+
+150
+FACE_KRASS
+19
+128
+I've earned my fee. I'll see you around, boy!
+
+0
diff --git a/data/script17.txt b/data/script17.txt
new file mode 100644 (file)
index 0000000..812e242
--- /dev/null
@@ -0,0 +1,8 @@
+90
+FACE_SID
+14
+128
+DAMMIT! It's getting away! We've got to stop it!
+
+
+0
diff --git a/data/script22.txt b/data/script22.txt
new file mode 100644 (file)
index 0000000..be940a2
--- /dev/null
@@ -0,0 +1,37 @@
+45
+FACE_SID
+-1
+0
+Chris! Another two of those ray cannons just arrived in your sector!
+
+45
+-1
+11
+-1048576
+@none@
+
+45
+-1
+12
+-1048576
+@none@
+
+90
+FACE_SID
+-1
+0
+Two more!
+
+90
+-1
+13
+-1048576
+@none@
+
+90
+-1
+14
+-1048576
+@none@
+
+0
diff --git a/data/script23.txt b/data/script23.txt
new file mode 100644 (file)
index 0000000..ef85896
--- /dev/null
@@ -0,0 +1,55 @@
+1
+-1
+10
+-1048576
+@none@
+
+2
+FACE_URSULA
+-1
+0
+It's a trap! My God! It's Tyler!
+
+8
+FACE_KRASS
+-1
+0
+I'm a mercenary, what do you expect?! WEAPCO hired me to do a job, just like you have been doing.
+
+14
+FACE_CHRIS
+-1
+0
+Good point. It would be foolish to expect anything else.
+
+15
+-1
+11
+-1048576
+@none@
+
+20
+FACE_KRASS
+-1
+0
+Now if you'll assist me in dying quickly, I have a cheque to earn, boy...
+
+25
+FACE_CHRIS
+-1
+0
+I have a name, you know. Do you remember it?
+
+45
+-1
+12
+-1048576
+@none@
+
+45
+-1
+13
+-1048576
+@none@
+
+0
diff --git a/data/script25.txt b/data/script25.txt
new file mode 100644 (file)
index 0000000..0604dc7
--- /dev/null
@@ -0,0 +1,13 @@
+2
+FACE_KLINE
+-1
+0
+That's far enough, Bainfield. You've been lucky so far, but your luck is about to run out!
+
+8
+FACE_CHRIS
+-1
+0
+Yeah, right! Like I'd lose to you after coming this far!
+
+0
diff --git a/data/script26.txt b/data/script26.txt
new file mode 100644 (file)
index 0000000..96374ee
--- /dev/null
@@ -0,0 +1,11 @@
+2
+FACE_CHRIS
+-1
+0
+WEAPCO is finished, Kethlan! You have nowhere to run, nowhere to hide, and no one to help you.
+
+8
+FACE_KLINE
+-1
+0
+You underestimate me, Bainfield.
diff --git a/data/script5.txt b/data/script5.txt
new file mode 100644 (file)
index 0000000..66d43e5
--- /dev/null
@@ -0,0 +1,37 @@
+2
+FACE_SID
+-1
+0
+You should try and destroy some of the frigate's weaponary, it'll help!
+
+120
+FACE_SID
+-1
+0
+Chris, get a move on! The frigate is almost in range!
+
+150
+FACE_SID
+-1
+0
+The frigate is charging its cannon!
+
+170
+FACE_SID
+-1
+0
+Chris, HURRY!!!
+
+180
+-1
+14
+132
+@none@
+
+180
+FACE_SID
+-1
+0
+oh my god...
+
+0
diff --git a/data/script7.txt b/data/script7.txt
new file mode 100644 (file)
index 0000000..9c15b97
--- /dev/null
@@ -0,0 +1,7 @@
+2
+FACE_PHOEBE
+-1
+0
+Hey, over here! Destroy this tug so I can break free!
+
+0
diff --git a/data/script8.txt b/data/script8.txt
new file mode 100644 (file)
index 0000000..df7e490
--- /dev/null
@@ -0,0 +1,61 @@
+2
+FACE_CREW
+-1
+0
+Thank God! Please, help us! If they destroy this ship...
+
+6
+FACE_CHRIS
+-1
+0
+How long do you need?
+
+10
+FACE_CREW
+-1
+0
+Just a few minutes!
+
+60
+FACE_CREW
+19
+-16
+Alright! We've got the weapons working again! Now to look at the engines...
+
+80
+FACE_CREW
+19
+16
+DAMN! The guns are offline again! What you guys doing back there?!
+
+100
+FACE_CREW
+19
+2048
+Chris, HELP!! We've lost all system power! We're a sitting duck out here!
+
+120
+FACE_CREW
+-1
+0
+Just a little longer...
+
+140
+FACE_CREW
+19
+-2048
+Okay! We've fixed that one. We think we've found the problem with the engines too...
+
+177
+FACE_CREW
+-1
+0
+Engines fixed! Let's move!
+
+180
+-1
+19
+132
+@none@
+
+0
diff --git a/docs/COPYING b/docs/COPYING
new file mode 100644 (file)
index 0000000..94a9ed0
--- /dev/null
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  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.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  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.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            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
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU 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 Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/docs/arrowNorthEast.png b/docs/arrowNorthEast.png
new file mode 100644 (file)
index 0000000..338e6f8
--- /dev/null
@@ -0,0 +1 @@
+../gfx/arrowNorthEast.png
\ No newline at end of file
diff --git a/docs/dollar.png b/docs/dollar.png
new file mode 100644 (file)
index 0000000..3f074f5
--- /dev/null
@@ -0,0 +1 @@
+../gfx/dollar.png
\ No newline at end of file
diff --git a/docs/heart.png b/docs/heart.png
new file mode 100644 (file)
index 0000000..a5559a7
--- /dev/null
@@ -0,0 +1 @@
+../gfx/heart.png
\ No newline at end of file
diff --git a/docs/index.html b/docs/index.html
new file mode 100644 (file)
index 0000000..9c7c960
--- /dev/null
@@ -0,0 +1,449 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+
+<html>
+<head>
+  <title>Starfighter gameplay manual</title>
+  <style type="text/css">
+  a {color: #499FFF; text-decoration: none; font-family: helvetica;}
+  a:hover {color: #499FFF; text-decoration: underline}
+  body {color: white; background: black; font: 10pt helvetica; max-width: 72em; margin-left: auto; margin-right: auto;}
+  table {color: white; font: 10pt helvetica;}
+  td {color: white;}
+  h1 {color: white; font: 12pt helvetica; font-weight: bold;}
+  h2 {color: white; background: #000099; width: 100%; padding: 2px; font: 10pt helvetica; font-weight: bold;}
+  ul.menu li {color: white; background: #FF0000; border-style: solid; border-color: white; border-width: 1px; width: 14em; list-style-type: none; margin: 2px; text-align: center; text-shadow: 1px 0 black, -1px 0 black, 0 1px black, 0 -1px black;}
+  dl dt {font-weight: bold;}
+  dl dd {margin: 1em 0 1em 2em;}
+  </style>
+</head>
+
+<body>
+  <center>
+    <img src="sflogo.png" alt="Starfighter">
+
+    <h1>Gameplay Manual</h1>
+  </center>
+
+  <h2>License Agreement</h2>
+
+  <pre>
+Project: Starfighter
+Copyright &copy; 2003 Parallel Realities
+Copyright &copy; 2012 Guus Sliepen, Astrid S. de Wijn and others
+Copyright &copy; 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+</pre>
+
+  <h2>Introduction</h2>
+
+  <p>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.</p>
+
+  <h2>Controls</h2>
+
+  <p>Menus</p>
+
+  <ul>
+  <li>Arrow Keys - Highlight option</li>
+  <li>Control / Enter - Select menu option</li>
+  </ul>
+
+  <p>Intermission Screen</p>
+
+  <ul>
+  <li>Mouse or Arrow Keys - Move cursor</li>
+  <li>Left Mouse Button or Control / Enter - Selected option</li>
+  <li>Right Mouse Button or Space - Toggle planet orbit On / Off</li>
+  </ul>
+
+  <p>In Game</p>
+
+  <ul>
+  <li>Arrow Keys - Control Firefly</li>
+  <li>Ctrl - Fire primary weapon</li>
+  <li>Space - Fire secondary weapon</li>
+  <li>Shift - Toggle Primary Weapon Concentrate / Spread (see Weaponry)</li>
+  <li>P - Pause / Unpause</li>
+  <li>Escape - Flee (not possible until all primary missions completed)</li>
+  <li>Escape whilst paused - Quit to title screen</li>
+  </ul>
+
+  <p>Joystick and Gamepad support</p>
+
+  <ul>
+  <li>Stick or Hat - Same as Arrow Keys</li>
+  <li>Button 1 / 4 - Fire primary weapon / Select menu option</li>
+  <li>Button 2 / 3 - Fire secondary weapon / Select menu option</li>
+  <li>Button 6 / 8 / 9 - Toggle Primary Weapon Concentrate / Spread</li>
+  <li>Button 10 - Pause / Unpause</li>
+  <li>Button 5 / 7 - Flee (not possible until all primary missions completed)</li>
+  <li>Button 5 / 7 whilst paused - Quit to title screen</li>
+  </ul>
+
+  <p>Note - Keys cannot be changed</p>
+
+  <h2>How to Play</h2>
+
+  <p>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.</p>
+
+  <p>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:</p>
+
+  <ul class="menu">
+  <li>Start New Game</li>
+  <li>Load Game*</li>
+  <li>Continue Current Game*</li>
+  <li>Options</li>
+  <li>Quit</li>
+  </ul>
+
+  <p><i>* Will only be shown if there are saved games
+  available.</i></p>
+
+  <dl>
+  <dt>Start New Game</dt>
+  <dd>This will start a new game.</dd>
+
+  <dt>Load Game</dt>
+  <dd>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.</dd>
+
+  <dt>Continue Current Game</dt>
+  <dd>This will automatically load the most recently saved game. This
+  option is only shown when there is at least one saved game
+  available.</dd>
+
+  <dt>Options</dt>
+  <dd>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.</dd>
+
+  <dt>Quit</dt>
+  <dd>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).</dd>
+  </dl>
+
+  <h2>Loading and Saving Games</h2>
+
+  <p>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.</p>
+
+  <p>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.</p>
+
+  <h2>Getting Missions</h2>
+
+  <p>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.</p>
+
+  <h2>Mission Briefing</h2>
+
+  <p>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</p>
+
+  <h2>Completing Missions</h2>
+
+  <p>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.</p>
+
+  <p>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.</p>
+
+  <p>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.</p>
+
+  <ul>
+  <li><font color="#FFFFFF">White messages</font> are standard for picking up items such as cash and power ups.</li>
+  <li><font color="#00FF00">Green messages</font> signify successful completion of mission objectives.</li>
+  <li><font color="#00FFFF">Light Blue messages</font> give further details about Primary mission requirements.</li>
+  <li><font color="#FF0000">Red messages</font> indicate mission failures, warnings and wing mate ejections.</li>
+  <li><font color="#FFFF00">Yellow messages</font> give further details about Secondary mission requirements.</li>
+  </ul>
+
+  <h2>The Target Arrow</h2>
+
+  <p><img src="arrowNorthEast.png" align="left" alt="Target Arrow">
+  The Target Arrow can be used to locate enemies and point you in
+  the direction that the enemy craft is. You can toggle the Target
+  Arrow between three mode by pressing "T" on the keyboard: Off,
+  Mission target only, and All targets. 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 arrows to find the enemy. The arrows will not be
+  displayed when you are within the immediate vacinity of the
+  target. On certain missions (such as Bosses) the mission target
+  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.</p>
+
+  <h2>Moving Around Systems</h2>
+
+  <p>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.</p>
+
+  <p>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.</p>
+
+  <p>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.</p>
+
+  <h2>Interceptions</h2>
+
+  <p>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.</p>
+
+  <p>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.</p>
+
+  <p>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.</p>
+
+  <h2>Weaponry and Upgrades</h2>
+
+  <p>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).</p>
+
+  <p>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.</p>
+
+  <dl>
+  <dt>Temporary Upgrades</dt>
+
+  <dd>
+    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).
+  </dd>
+
+  <dt>Permanent Upgrades</dt>
+
+  <dd>
+    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.
+  </dd>
+
+  <dt>Secondary Weapons</dt>
+
+  <dd>
+    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 overheating and must be allowed to cool
+    after usage.
+  </dd>
+  </dl>
+
+  <p>Certain weapons and upgrades will not be available to you
+  until later in the game, so remember to save your money for
+  them.</p>
+
+  <h2>Item Spheres</h2>
+
+  <p>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:</p>
+
+  <table summary="Description of all item spheres.">
+    <tr>
+      <td><img src="dollar.png" alt="Cash"></td>
+      <td><b>Cash</b> - Provides you with an additional cash bonus.</td>
+    </tr>
+
+    <tr>
+      <td><img src="rocket.png" alt="Plasma Ammo"></td>
+      <td><b>Plasma Ammo</b> - Increases your current plasma ammo.</td>
+    </tr>
+
+    <tr>
+      <td><img src="rocketAmmo.png" alt="Rocket Ammo"></td>
+      <td><b>Rocket Ammo</b> - Increases your current rocket ammo.</td>
+    </tr>
+
+    <tr>
+      <td><img src="heart.png" alt="Shield Sphere"></td>
+      <td><b>Shield Sphere</b> - Replenishes your shield reserves.</td>
+    </tr>
+
+    <tr>
+      <td><img src="plasmaDamage.png" alt="Plasma Power"></td>
+      <td><b>Plasma Power</b> - Boosts your plasma power.</td>
+    </tr>
+
+    <tr>
+      <td><img src="plasmaAmmo.png" alt="Plasma Output"></td>
+      <td><b>Plasma Ouput</b> - Boosts the amount of plasma shots you can fire.</td>
+    </tr>
+
+    <tr>
+      <td><img src="plasmaRate.png" alt="Plasma Coolor"></td>
+      <td><b>Plasma Cooler</b> - Increases your plasma firing rate.</td>
+    </tr>
+
+    <tr>
+      <td><img src="superCharge.png" alt="Super Charge"></td>
+      <td><b>Super Charge</b> - Five way spread, massive power and cooling (rare).</td>
+    </tr>
+  </table>
+
+  <h2>Cut Scenes</h2>
+
+  <p>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.</p>
+
+  <h2>Ending the Game</h2>
+
+  <p>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.</p>
+
+  <h2>About</h2>
+
+  <p>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.</p>
+
+  <p>Project: Starfighter<br>
+  Copyright &copy; 2003 Parallel Realities<br>
+  Copyright &copy; 2012 Guus Sliepen, Astrid S. de Wijn and
+  others<br></p>
+
+  <p>Created using the <a href="http://www.libsdl.org">SDL
+  library</a>.</p>
+
+  <p align="right"><a href="http://starfighter.nongnu.org">http://starfighter.nongnu.org</a></p>
+</body>
+</html>
diff --git a/docs/plasmaAmmo.png b/docs/plasmaAmmo.png
new file mode 100644 (file)
index 0000000..9524eef
--- /dev/null
@@ -0,0 +1 @@
+../gfx/plasmaAmmo.png
\ No newline at end of file
diff --git a/docs/plasmaDamage.png b/docs/plasmaDamage.png
new file mode 100644 (file)
index 0000000..7c42a0c
--- /dev/null
@@ -0,0 +1 @@
+../gfx/plasmaDamage.png
\ No newline at end of file
diff --git a/docs/plasmaRate.png b/docs/plasmaRate.png
new file mode 100644 (file)
index 0000000..a25ea04
--- /dev/null
@@ -0,0 +1 @@
+../gfx/plasmaRate.png
\ No newline at end of file
diff --git a/docs/rocket.png b/docs/rocket.png
new file mode 100644 (file)
index 0000000..22a5c43
--- /dev/null
@@ -0,0 +1 @@
+../gfx/rocket.png
\ No newline at end of file
diff --git a/docs/rocketAmmo.png b/docs/rocketAmmo.png
new file mode 100644 (file)
index 0000000..ae4792f
--- /dev/null
@@ -0,0 +1 @@
+../gfx/rocketAmmo.png
\ No newline at end of file
diff --git a/docs/sflogo.png b/docs/sflogo.png
new file mode 100644 (file)
index 0000000..e9e0ecb
--- /dev/null
@@ -0,0 +1 @@
+../gfx/sflogo.png
\ No newline at end of file
diff --git a/docs/superCharge.png b/docs/superCharge.png
new file mode 100644 (file)
index 0000000..17e3009
--- /dev/null
@@ -0,0 +1 @@
+../gfx/superCharge.png
\ No newline at end of file
diff --git a/gfx/aimFighter1.png b/gfx/aimFighter1.png
new file mode 100644 (file)
index 0000000..ee27c8d
Binary files /dev/null and b/gfx/aimFighter1.png differ
diff --git a/gfx/aimFighter2.png b/gfx/aimFighter2.png
new file mode 100644 (file)
index 0000000..a3cffa4
Binary files /dev/null and b/gfx/aimFighter2.png differ
diff --git a/gfx/alienDevice.png b/gfx/alienDevice.png
new file mode 100644 (file)
index 0000000..633d87e
Binary files /dev/null and b/gfx/alienDevice.png differ
diff --git a/gfx/arrowEast.png b/gfx/arrowEast.png
new file mode 100644 (file)
index 0000000..87bf927
Binary files /dev/null and b/gfx/arrowEast.png differ
diff --git a/gfx/arrowNorth.png b/gfx/arrowNorth.png
new file mode 100644 (file)
index 0000000..6e1d6d0
Binary files /dev/null and b/gfx/arrowNorth.png differ
diff --git a/gfx/arrowNorthEast.png b/gfx/arrowNorthEast.png
new file mode 100644 (file)
index 0000000..8792678
Binary files /dev/null and b/gfx/arrowNorthEast.png differ
diff --git a/gfx/arrowNorthWest.png b/gfx/arrowNorthWest.png
new file mode 100644 (file)
index 0000000..11b60ad
Binary files /dev/null and b/gfx/arrowNorthWest.png differ
diff --git a/gfx/arrowSouth.png b/gfx/arrowSouth.png
new file mode 100644 (file)
index 0000000..bd71b17
Binary files /dev/null and b/gfx/arrowSouth.png differ
diff --git a/gfx/arrowSouthEast.png b/gfx/arrowSouthEast.png
new file mode 100644 (file)
index 0000000..cc1c8a9
Binary files /dev/null and b/gfx/arrowSouthEast.png differ
diff --git a/gfx/arrowSouthWest.png b/gfx/arrowSouthWest.png
new file mode 100644 (file)
index 0000000..0c7ecf1
Binary files /dev/null and b/gfx/arrowSouthWest.png differ
diff --git a/gfx/arrowWest.png b/gfx/arrowWest.png
new file mode 100644 (file)
index 0000000..548c978
Binary files /dev/null and b/gfx/arrowWest.png differ
diff --git a/gfx/asteroid1.png b/gfx/asteroid1.png
new file mode 100644 (file)
index 0000000..5dc7b53
Binary files /dev/null and b/gfx/asteroid1.png differ
diff --git a/gfx/asteroid2.png b/gfx/asteroid2.png
new file mode 100644 (file)
index 0000000..f7ee860
Binary files /dev/null and b/gfx/asteroid2.png differ
diff --git a/gfx/asteroid3.png b/gfx/asteroid3.png
new file mode 100644 (file)
index 0000000..18c7821
Binary files /dev/null and b/gfx/asteroid3.png differ
diff --git a/gfx/barrier.png b/gfx/barrier.png
new file mode 100644 (file)
index 0000000..507049c
Binary files /dev/null and b/gfx/barrier.png differ
diff --git a/gfx/buyIcon.png b/gfx/buyIcon.png
new file mode 100644 (file)
index 0000000..64c8549
Binary files /dev/null and b/gfx/buyIcon.png differ
diff --git a/gfx/cargo1.png b/gfx/cargo1.png
new file mode 100644 (file)
index 0000000..1c2aa2f
Binary files /dev/null and b/gfx/cargo1.png differ
diff --git a/gfx/chainLink.png b/gfx/chainLink.png
new file mode 100644 (file)
index 0000000..f7dc7aa
Binary files /dev/null and b/gfx/chainLink.png differ
diff --git a/gfx/cloakShip1.png b/gfx/cloakShip1.png
new file mode 100644 (file)
index 0000000..d2d3a5e
Binary files /dev/null and b/gfx/cloakShip1.png differ
diff --git a/gfx/cloakShip2.png b/gfx/cloakShip2.png
new file mode 100644 (file)
index 0000000..f555886
Binary files /dev/null and b/gfx/cloakShip2.png differ
diff --git a/gfx/credits.jpg b/gfx/credits.jpg
new file mode 100644 (file)
index 0000000..d5e9257
Binary files /dev/null and b/gfx/credits.jpg differ
diff --git a/gfx/cursor.png b/gfx/cursor.png
new file mode 100644 (file)
index 0000000..598d71e
Binary files /dev/null and b/gfx/cursor.png differ
diff --git a/gfx/dollar.png b/gfx/dollar.png
new file mode 100644 (file)
index 0000000..4babf17
Binary files /dev/null and b/gfx/dollar.png differ
diff --git a/gfx/drone1.png b/gfx/drone1.png
new file mode 100644 (file)
index 0000000..87fbfb2
Binary files /dev/null and b/gfx/drone1.png differ
diff --git a/gfx/drone2.png b/gfx/drone2.png
new file mode 100644 (file)
index 0000000..fc7b685
Binary files /dev/null and b/gfx/drone2.png differ
diff --git a/gfx/dualFighter1.png b/gfx/dualFighter1.png
new file mode 100644 (file)
index 0000000..7d2487a
Binary files /dev/null and b/gfx/dualFighter1.png differ
diff --git a/gfx/dualFighter2.png b/gfx/dualFighter2.png
new file mode 100644 (file)
index 0000000..21193b3
Binary files /dev/null and b/gfx/dualFighter2.png differ
diff --git a/gfx/elec1.png b/gfx/elec1.png
new file mode 100644 (file)
index 0000000..b78bf34
Binary files /dev/null and b/gfx/elec1.png differ
diff --git a/gfx/elec2.png b/gfx/elec2.png
new file mode 100644 (file)
index 0000000..cd98881
Binary files /dev/null and b/gfx/elec2.png differ
diff --git a/gfx/elec3.png b/gfx/elec3.png
new file mode 100644 (file)
index 0000000..e1bb7d0
Binary files /dev/null and b/gfx/elec3.png differ
diff --git a/gfx/elec4.png b/gfx/elec4.png
new file mode 100644 (file)
index 0000000..19606bf
Binary files /dev/null and b/gfx/elec4.png differ
diff --git a/gfx/eliteFighter1.png b/gfx/eliteFighter1.png
new file mode 100644 (file)
index 0000000..1c6b001
Binary files /dev/null and b/gfx/eliteFighter1.png differ
diff --git a/gfx/eliteFighter2.png b/gfx/eliteFighter2.png
new file mode 100644 (file)
index 0000000..a945add
Binary files /dev/null and b/gfx/eliteFighter2.png differ
diff --git a/gfx/escort1.png b/gfx/escort1.png
new file mode 100644 (file)
index 0000000..9674988
Binary files /dev/null and b/gfx/escort1.png differ
diff --git a/gfx/escort2.png b/gfx/escort2.png
new file mode 100644 (file)
index 0000000..b0babee
Binary files /dev/null and b/gfx/escort2.png differ
diff --git a/gfx/evilUrsula1.png b/gfx/evilUrsula1.png
new file mode 100644 (file)
index 0000000..fbec59e
Binary files /dev/null and b/gfx/evilUrsula1.png differ
diff --git a/gfx/evilUrsula2.png b/gfx/evilUrsula2.png
new file mode 100644 (file)
index 0000000..ba78fbc
Binary files /dev/null and b/gfx/evilUrsula2.png differ
diff --git a/gfx/execTrans1.png b/gfx/execTrans1.png
new file mode 100644 (file)
index 0000000..8b590b1
Binary files /dev/null and b/gfx/execTrans1.png differ
diff --git a/gfx/execTrans2.png b/gfx/execTrans2.png
new file mode 100644 (file)
index 0000000..330245e
Binary files /dev/null and b/gfx/execTrans2.png differ
diff --git a/gfx/explode05.png b/gfx/explode05.png
new file mode 100644 (file)
index 0000000..4d88aaf
Binary files /dev/null and b/gfx/explode05.png differ
diff --git a/gfx/explode06.png b/gfx/explode06.png
new file mode 100644 (file)
index 0000000..1452f90
Binary files /dev/null and b/gfx/explode06.png differ
diff --git a/gfx/explode07.png b/gfx/explode07.png
new file mode 100644 (file)
index 0000000..8b53c8d
Binary files /dev/null and b/gfx/explode07.png differ
diff --git a/gfx/explode08.png b/gfx/explode08.png
new file mode 100644 (file)
index 0000000..0c4fa9b
Binary files /dev/null and b/gfx/explode08.png differ
diff --git a/gfx/explode1.png b/gfx/explode1.png
new file mode 100644 (file)
index 0000000..0686a4c
Binary files /dev/null and b/gfx/explode1.png differ
diff --git a/gfx/explode10.png b/gfx/explode10.png
new file mode 100644 (file)
index 0000000..ba06ba3
Binary files /dev/null and b/gfx/explode10.png differ
diff --git a/gfx/explode11.png b/gfx/explode11.png
new file mode 100644 (file)
index 0000000..4f64878
Binary files /dev/null and b/gfx/explode11.png differ
diff --git a/gfx/explode12.png b/gfx/explode12.png
new file mode 100644 (file)
index 0000000..029fc1f
Binary files /dev/null and b/gfx/explode12.png differ
diff --git a/gfx/explode13.png b/gfx/explode13.png
new file mode 100644 (file)
index 0000000..03a9946
Binary files /dev/null and b/gfx/explode13.png differ
diff --git a/gfx/explode14.png b/gfx/explode14.png
new file mode 100644 (file)
index 0000000..886ac90
Binary files /dev/null and b/gfx/explode14.png differ
diff --git a/gfx/explode15.png b/gfx/explode15.png
new file mode 100644 (file)
index 0000000..818ee03
Binary files /dev/null and b/gfx/explode15.png differ
diff --git a/gfx/explode16.png b/gfx/explode16.png
new file mode 100644 (file)
index 0000000..2d4eb37
Binary files /dev/null and b/gfx/explode16.png differ
diff --git a/gfx/explode2.png b/gfx/explode2.png
new file mode 100644 (file)
index 0000000..62f5ade
Binary files /dev/null and b/gfx/explode2.png differ
diff --git a/gfx/explode3.png b/gfx/explode3.png
new file mode 100644 (file)
index 0000000..75998e8
Binary files /dev/null and b/gfx/explode3.png differ
diff --git a/gfx/explode4.png b/gfx/explode4.png
new file mode 100644 (file)
index 0000000..2376e71
Binary files /dev/null and b/gfx/explode4.png differ
diff --git a/gfx/explode9.png b/gfx/explode9.png
new file mode 100644 (file)
index 0000000..a5a1d15
Binary files /dev/null and b/gfx/explode9.png differ
diff --git a/gfx/eyananth.jpg b/gfx/eyananth.jpg
new file mode 100644 (file)
index 0000000..e3ccb2e
Binary files /dev/null and b/gfx/eyananth.jpg differ
diff --git a/gfx/face_chris.png b/gfx/face_chris.png
new file mode 100644 (file)
index 0000000..9c2340c
Binary files /dev/null and b/gfx/face_chris.png differ
diff --git a/gfx/face_crew.png b/gfx/face_crew.png
new file mode 100644 (file)
index 0000000..90d3348
Binary files /dev/null and b/gfx/face_crew.png differ
diff --git a/gfx/face_kline.png b/gfx/face_kline.png
new file mode 100644 (file)
index 0000000..d0c9164
Binary files /dev/null and b/gfx/face_kline.png differ
diff --git a/gfx/face_krass.png b/gfx/face_krass.png
new file mode 100644 (file)
index 0000000..c1e1a48
Binary files /dev/null and b/gfx/face_krass.png differ
diff --git a/gfx/face_phoebe.png b/gfx/face_phoebe.png
new file mode 100644 (file)
index 0000000..786f5b4
Binary files /dev/null and b/gfx/face_phoebe.png differ
diff --git a/gfx/face_sid.png b/gfx/face_sid.png
new file mode 100644 (file)
index 0000000..b2667ea
Binary files /dev/null and b/gfx/face_sid.png differ
diff --git a/gfx/face_ursula.png b/gfx/face_ursula.png
new file mode 100644 (file)
index 0000000..0b5563f
Binary files /dev/null and b/gfx/face_ursula.png differ
diff --git a/gfx/firefly-big.png b/gfx/firefly-big.png
new file mode 100644 (file)
index 0000000..e422d1d
Binary files /dev/null and b/gfx/firefly-big.png differ
diff --git a/gfx/firefly1.png b/gfx/firefly1.png
new file mode 100644 (file)
index 0000000..1b726e0
Binary files /dev/null and b/gfx/firefly1.png differ
diff --git a/gfx/firefly2.png b/gfx/firefly2.png
new file mode 100644 (file)
index 0000000..9c2b172
Binary files /dev/null and b/gfx/firefly2.png differ
diff --git a/gfx/frigateBody1.png b/gfx/frigateBody1.png
new file mode 100644 (file)
index 0000000..7a04933
Binary files /dev/null and b/gfx/frigateBody1.png differ
diff --git a/gfx/frigateBody2.png b/gfx/frigateBody2.png
new file mode 100644 (file)
index 0000000..793986d
Binary files /dev/null and b/gfx/frigateBody2.png differ
diff --git a/gfx/frigateGun11.png b/gfx/frigateGun11.png
new file mode 100644 (file)
index 0000000..18d0d8b
Binary files /dev/null and b/gfx/frigateGun11.png differ
diff --git a/gfx/frigateGun12.png b/gfx/frigateGun12.png
new file mode 100644 (file)
index 0000000..23b3ed9
Binary files /dev/null and b/gfx/frigateGun12.png differ
diff --git a/gfx/frigateGun21.png b/gfx/frigateGun21.png
new file mode 100644 (file)
index 0000000..472c1fb
Binary files /dev/null and b/gfx/frigateGun21.png differ
diff --git a/gfx/frigateGun22.png b/gfx/frigateGun22.png
new file mode 100644 (file)
index 0000000..62510b4
Binary files /dev/null and b/gfx/frigateGun22.png differ
diff --git a/gfx/gameover.png b/gfx/gameover.png
new file mode 100644 (file)
index 0000000..f440a34
Binary files /dev/null and b/gfx/gameover.png differ
diff --git a/gfx/goodTrans1.png b/gfx/goodTrans1.png
new file mode 100644 (file)
index 0000000..d6c9321
Binary files /dev/null and b/gfx/goodTrans1.png differ
diff --git a/gfx/goodTrans2.png b/gfx/goodTrans2.png
new file mode 100644 (file)
index 0000000..bdb3aa6
Binary files /dev/null and b/gfx/goodTrans2.png differ
diff --git a/gfx/greenDir.png b/gfx/greenDir.png
new file mode 100644 (file)
index 0000000..b8cf74f
Binary files /dev/null and b/gfx/greenDir.png differ
diff --git a/gfx/heart.png b/gfx/heart.png
new file mode 100644 (file)
index 0000000..d733778
Binary files /dev/null and b/gfx/heart.png differ
diff --git a/gfx/icon1.png b/gfx/icon1.png
new file mode 100644 (file)
index 0000000..a33edc0
Binary files /dev/null and b/gfx/icon1.png differ
diff --git a/gfx/icon10.png b/gfx/icon10.png
new file mode 100644 (file)
index 0000000..df422e7
Binary files /dev/null and b/gfx/icon10.png differ
diff --git a/gfx/icon11.png b/gfx/icon11.png
new file mode 100644 (file)
index 0000000..dccb38f
Binary files /dev/null and b/gfx/icon11.png differ
diff --git a/gfx/icon12.png b/gfx/icon12.png
new file mode 100644 (file)
index 0000000..409095b
Binary files /dev/null and b/gfx/icon12.png differ
diff --git a/gfx/icon13.png b/gfx/icon13.png
new file mode 100644 (file)
index 0000000..6b08948
Binary files /dev/null and b/gfx/icon13.png differ
diff --git a/gfx/icon14.png b/gfx/icon14.png
new file mode 100644 (file)
index 0000000..bc3090e
Binary files /dev/null and b/gfx/icon14.png differ
diff --git a/gfx/icon15.png b/gfx/icon15.png
new file mode 100644 (file)
index 0000000..789d53e
Binary files /dev/null and b/gfx/icon15.png differ
diff --git a/gfx/icon16.png b/gfx/icon16.png
new file mode 100644 (file)
index 0000000..033ac8d
Binary files /dev/null and b/gfx/icon16.png differ
diff --git a/gfx/icon17.png b/gfx/icon17.png
new file mode 100644 (file)
index 0000000..7b6d991
Binary files /dev/null and b/gfx/icon17.png differ
diff --git a/gfx/icon18.png b/gfx/icon18.png
new file mode 100644 (file)
index 0000000..eb6a9a1
Binary files /dev/null and b/gfx/icon18.png differ
diff --git a/gfx/icon19.png b/gfx/icon19.png
new file mode 100644 (file)
index 0000000..41dd862
Binary files /dev/null and b/gfx/icon19.png differ
diff --git a/gfx/icon2.png b/gfx/icon2.png
new file mode 100644 (file)
index 0000000..3abc295
Binary files /dev/null and b/gfx/icon2.png differ
diff --git a/gfx/icon20.png b/gfx/icon20.png
new file mode 100644 (file)
index 0000000..5111e13
Binary files /dev/null and b/gfx/icon20.png differ
diff --git a/gfx/icon21.png b/gfx/icon21.png
new file mode 100644 (file)
index 0000000..7407559
Binary files /dev/null and b/gfx/icon21.png differ
diff --git a/gfx/icon22.png b/gfx/icon22.png
new file mode 100644 (file)
index 0000000..cb0170f
Binary files /dev/null and b/gfx/icon22.png differ
diff --git a/gfx/icon23.png b/gfx/icon23.png
new file mode 100644 (file)
index 0000000..b14c09e
Binary files /dev/null and b/gfx/icon23.png differ
diff --git a/gfx/icon24.png b/gfx/icon24.png
new file mode 100644 (file)
index 0000000..0e130bb
Binary files /dev/null and b/gfx/icon24.png differ
diff --git a/gfx/icon25.png b/gfx/icon25.png
new file mode 100644 (file)
index 0000000..6f458a8
Binary files /dev/null and b/gfx/icon25.png differ
diff --git a/gfx/icon26.png b/gfx/icon26.png
new file mode 100644 (file)
index 0000000..58f6950
Binary files /dev/null and b/gfx/icon26.png differ
diff --git a/gfx/icon3.png b/gfx/icon3.png
new file mode 100644 (file)
index 0000000..f6f3a0e
Binary files /dev/null and b/gfx/icon3.png differ
diff --git a/gfx/icon4.png b/gfx/icon4.png
new file mode 100644 (file)
index 0000000..200aa64
Binary files /dev/null and b/gfx/icon4.png differ
diff --git a/gfx/icon5.png b/gfx/icon5.png
new file mode 100644 (file)
index 0000000..3625135
Binary files /dev/null and b/gfx/icon5.png differ
diff --git a/gfx/icon6.png b/gfx/icon6.png
new file mode 100644 (file)
index 0000000..c645df1
Binary files /dev/null and b/gfx/icon6.png differ
diff --git a/gfx/icon7.png b/gfx/icon7.png
new file mode 100644 (file)
index 0000000..ce5e967
Binary files /dev/null and b/gfx/icon7.png differ
diff --git a/gfx/icon8.png b/gfx/icon8.png
new file mode 100644 (file)
index 0000000..f2db4d0
Binary files /dev/null and b/gfx/icon8.png differ
diff --git a/gfx/icon9.png b/gfx/icon9.png
new file mode 100644 (file)
index 0000000..3d90ca0
Binary files /dev/null and b/gfx/icon9.png differ
diff --git a/gfx/iconBase.png b/gfx/iconBase.png
new file mode 100644 (file)
index 0000000..57c7f2f
Binary files /dev/null and b/gfx/iconBase.png differ
diff --git a/gfx/kline11.png b/gfx/kline11.png
new file mode 100644 (file)
index 0000000..d899156
Binary files /dev/null and b/gfx/kline11.png differ
diff --git a/gfx/kline12.png b/gfx/kline12.png
new file mode 100644 (file)
index 0000000..994f466
Binary files /dev/null and b/gfx/kline12.png differ
diff --git a/gfx/merc1.png b/gfx/merc1.png
new file mode 100644 (file)
index 0000000..54fd93c
Binary files /dev/null and b/gfx/merc1.png differ
diff --git a/gfx/merc2.png b/gfx/merc2.png
new file mode 100644 (file)
index 0000000..3010f43
Binary files /dev/null and b/gfx/merc2.png differ
diff --git a/gfx/mine.png b/gfx/mine.png
new file mode 100644 (file)
index 0000000..16bd558
Binary files /dev/null and b/gfx/mine.png differ
diff --git a/gfx/mineBoss1.png b/gfx/mineBoss1.png
new file mode 100644 (file)
index 0000000..3165f3b
Binary files /dev/null and b/gfx/mineBoss1.png differ
diff --git a/gfx/mineBoss2.png b/gfx/mineBoss2.png
new file mode 100644 (file)
index 0000000..ab0ed66
Binary files /dev/null and b/gfx/mineBoss2.png differ
diff --git a/gfx/mineBossWing11.png b/gfx/mineBossWing11.png
new file mode 100644 (file)
index 0000000..6fcc489
Binary files /dev/null and b/gfx/mineBossWing11.png differ
diff --git a/gfx/mineBossWing12.png b/gfx/mineBossWing12.png
new file mode 100644 (file)
index 0000000..447ae13
Binary files /dev/null and b/gfx/mineBossWing12.png differ
diff --git a/gfx/mineBossWing21.png b/gfx/mineBossWing21.png
new file mode 100644 (file)
index 0000000..0e9e926
Binary files /dev/null and b/gfx/mineBossWing21.png differ
diff --git a/gfx/mineBossWing22.png b/gfx/mineBossWing22.png
new file mode 100644 (file)
index 0000000..9823cf0
Binary files /dev/null and b/gfx/mineBossWing22.png differ
diff --git a/gfx/mineBossWing31.png b/gfx/mineBossWing31.png
new file mode 100644 (file)
index 0000000..af2c644
Binary files /dev/null and b/gfx/mineBossWing31.png differ
diff --git a/gfx/mineBossWing32.png b/gfx/mineBossWing32.png
new file mode 100644 (file)
index 0000000..97e7dc9
Binary files /dev/null and b/gfx/mineBossWing32.png differ
diff --git a/gfx/mineBossWing41.png b/gfx/mineBossWing41.png
new file mode 100644 (file)
index 0000000..3e84500
Binary files /dev/null and b/gfx/mineBossWing41.png differ
diff --git a/gfx/mineBossWing42.png b/gfx/mineBossWing42.png
new file mode 100644 (file)
index 0000000..1f8f1fd
Binary files /dev/null and b/gfx/mineBossWing42.png differ
diff --git a/gfx/miner1.png b/gfx/miner1.png
new file mode 100644 (file)
index 0000000..776fff0
Binary files /dev/null and b/gfx/miner1.png differ
diff --git a/gfx/miner2.png b/gfx/miner2.png
new file mode 100644 (file)
index 0000000..97286e4
Binary files /dev/null and b/gfx/miner2.png differ
diff --git a/gfx/missileBoat1.png b/gfx/missileBoat1.png
new file mode 100644 (file)
index 0000000..01c0f3e
Binary files /dev/null and b/gfx/missileBoat1.png differ
diff --git a/gfx/missileBoat2.png b/gfx/missileBoat2.png
new file mode 100644 (file)
index 0000000..c5f2895
Binary files /dev/null and b/gfx/missileBoat2.png differ
diff --git a/gfx/mobileCannon1.png b/gfx/mobileCannon1.png
new file mode 100644 (file)
index 0000000..de1403e
Binary files /dev/null and b/gfx/mobileCannon1.png differ
diff --git a/gfx/mobileCannon2.png b/gfx/mobileCannon2.png
new file mode 100644 (file)
index 0000000..2f994b7
Binary files /dev/null and b/gfx/mobileCannon2.png differ
diff --git a/gfx/mobileShield1.png b/gfx/mobileShield1.png
new file mode 100644 (file)
index 0000000..1b4ba7c
Binary files /dev/null and b/gfx/mobileShield1.png differ
diff --git a/gfx/mobileShield2.png b/gfx/mobileShield2.png
new file mode 100644 (file)
index 0000000..7c74394
Binary files /dev/null and b/gfx/mobileShield2.png differ
diff --git a/gfx/mordor.jpg b/gfx/mordor.jpg
new file mode 100644 (file)
index 0000000..7624b55
Binary files /dev/null and b/gfx/mordor.jpg differ
diff --git a/gfx/ore1.png b/gfx/ore1.png
new file mode 100644 (file)
index 0000000..33bfc4d
Binary files /dev/null and b/gfx/ore1.png differ
diff --git a/gfx/ore2.png b/gfx/ore2.png
new file mode 100644 (file)
index 0000000..beeb0d1
Binary files /dev/null and b/gfx/ore2.png differ
diff --git a/gfx/ore3.png b/gfx/ore3.png
new file mode 100644 (file)
index 0000000..2c30ae0
Binary files /dev/null and b/gfx/ore3.png differ
diff --git a/gfx/planet_blue.png b/gfx/planet_blue.png
new file mode 100644 (file)
index 0000000..e3267c5
Binary files /dev/null and b/gfx/planet_blue.png differ
diff --git a/gfx/planet_green.png b/gfx/planet_green.png
new file mode 100644 (file)
index 0000000..aac83ad
Binary files /dev/null and b/gfx/planet_green.png differ
diff --git a/gfx/planet_orange.png b/gfx/planet_orange.png
new file mode 100644 (file)
index 0000000..f09bf91
Binary files /dev/null and b/gfx/planet_orange.png differ
diff --git a/gfx/planet_red.png b/gfx/planet_red.png
new file mode 100644 (file)
index 0000000..37ad806
Binary files /dev/null and b/gfx/planet_red.png differ
diff --git a/gfx/planet_sun.png b/gfx/planet_sun.png
new file mode 100644 (file)
index 0000000..f8af514
Binary files /dev/null and b/gfx/planet_sun.png differ
diff --git a/gfx/plasmaAmmo.png b/gfx/plasmaAmmo.png
new file mode 100644 (file)
index 0000000..1fd89ec
Binary files /dev/null and b/gfx/plasmaAmmo.png differ
diff --git a/gfx/plasmaDamage.png b/gfx/plasmaDamage.png
new file mode 100644 (file)
index 0000000..8339e68
Binary files /dev/null and b/gfx/plasmaDamage.png differ
diff --git a/gfx/plasmaGreen.png b/gfx/plasmaGreen.png
new file mode 100644 (file)
index 0000000..233572a
Binary files /dev/null and b/gfx/plasmaGreen.png differ
diff --git a/gfx/plasmaRate.png b/gfx/plasmaRate.png
new file mode 100644 (file)
index 0000000..a2b6d91
Binary files /dev/null and b/gfx/plasmaRate.png differ
diff --git a/gfx/plasmaRed.png b/gfx/plasmaRed.png
new file mode 100644 (file)
index 0000000..de19903
Binary files /dev/null and b/gfx/plasmaRed.png differ
diff --git a/gfx/plutoBoss1.png b/gfx/plutoBoss1.png
new file mode 100644 (file)
index 0000000..ef8f671
Binary files /dev/null and b/gfx/plutoBoss1.png differ
diff --git a/gfx/plutoBoss2.png b/gfx/plutoBoss2.png
new file mode 100644 (file)
index 0000000..bffe47a
Binary files /dev/null and b/gfx/plutoBoss2.png differ
diff --git a/gfx/pod.png b/gfx/pod.png
new file mode 100644 (file)
index 0000000..ac64bf5
Binary files /dev/null and b/gfx/pod.png differ
diff --git a/gfx/prlogo.png b/gfx/prlogo.png
new file mode 100644 (file)
index 0000000..18d358a
Binary files /dev/null and b/gfx/prlogo.png differ
diff --git a/gfx/rebelCarrier1.png b/gfx/rebelCarrier1.png
new file mode 100644 (file)
index 0000000..60124dd
Binary files /dev/null and b/gfx/rebelCarrier1.png differ
diff --git a/gfx/rebelCarrier2.png b/gfx/rebelCarrier2.png
new file mode 100644 (file)
index 0000000..e9216e7
Binary files /dev/null and b/gfx/rebelCarrier2.png differ
diff --git a/gfx/redDir.png b/gfx/redDir.png
new file mode 100644 (file)
index 0000000..ab159bf
Binary files /dev/null and b/gfx/redDir.png differ
diff --git a/gfx/rocket.png b/gfx/rocket.png
new file mode 100644 (file)
index 0000000..a85187a
Binary files /dev/null and b/gfx/rocket.png differ
diff --git a/gfx/rocket1.png b/gfx/rocket1.png
new file mode 100644 (file)
index 0000000..1cbb881
Binary files /dev/null and b/gfx/rocket1.png differ
diff --git a/gfx/rocket2.png b/gfx/rocket2.png
new file mode 100644 (file)
index 0000000..d2b4be0
Binary files /dev/null and b/gfx/rocket2.png differ
diff --git a/gfx/rocketAmmo.png b/gfx/rocketAmmo.png
new file mode 100644 (file)
index 0000000..eea41cd
Binary files /dev/null and b/gfx/rocketAmmo.png differ
diff --git a/gfx/sellIcon.png b/gfx/sellIcon.png
new file mode 100644 (file)
index 0000000..3350596
Binary files /dev/null and b/gfx/sellIcon.png differ
diff --git a/gfx/sflogo.png b/gfx/sflogo.png
new file mode 100644 (file)
index 0000000..b0aca78
Binary files /dev/null and b/gfx/sflogo.png differ
diff --git a/gfx/sid1.png b/gfx/sid1.png
new file mode 100644 (file)
index 0000000..0482e4f
Binary files /dev/null and b/gfx/sid1.png differ
diff --git a/gfx/sid2.png b/gfx/sid2.png
new file mode 100644 (file)
index 0000000..e25df2e
Binary files /dev/null and b/gfx/sid2.png differ
diff --git a/gfx/slaveTrans1.png b/gfx/slaveTrans1.png
new file mode 100644 (file)
index 0000000..32429ce
Binary files /dev/null and b/gfx/slaveTrans1.png differ
diff --git a/gfx/slaveTrans2.png b/gfx/slaveTrans2.png
new file mode 100644 (file)
index 0000000..4cfd5ca
Binary files /dev/null and b/gfx/slaveTrans2.png differ
diff --git a/gfx/smallFont.png b/gfx/smallFont.png
new file mode 100644 (file)
index 0000000..7a9b735
Binary files /dev/null and b/gfx/smallFont.png differ
diff --git a/gfx/sol.jpg b/gfx/sol.jpg
new file mode 100644 (file)
index 0000000..f0140aa
Binary files /dev/null and b/gfx/sol.jpg differ
diff --git a/gfx/spirit.jpg b/gfx/spirit.jpg
new file mode 100644 (file)
index 0000000..8533156
Binary files /dev/null and b/gfx/spirit.jpg differ
diff --git a/gfx/splitBoss11.png b/gfx/splitBoss11.png
new file mode 100644 (file)
index 0000000..0576b03
Binary files /dev/null and b/gfx/splitBoss11.png differ
diff --git a/gfx/splitBoss12.png b/gfx/splitBoss12.png
new file mode 100644 (file)
index 0000000..8b7e6f9
Binary files /dev/null and b/gfx/splitBoss12.png differ
diff --git a/gfx/splitBoss21.png b/gfx/splitBoss21.png
new file mode 100644 (file)
index 0000000..d54d34c
Binary files /dev/null and b/gfx/splitBoss21.png differ
diff --git a/gfx/splitBoss22.png b/gfx/splitBoss22.png
new file mode 100644 (file)
index 0000000..b3fe7ab
Binary files /dev/null and b/gfx/splitBoss22.png differ
diff --git a/gfx/splitBoss31.png b/gfx/splitBoss31.png
new file mode 100644 (file)
index 0000000..96cd0d5
Binary files /dev/null and b/gfx/splitBoss31.png differ
diff --git a/gfx/splitBoss32.png b/gfx/splitBoss32.png
new file mode 100644 (file)
index 0000000..4ebd36d
Binary files /dev/null and b/gfx/splitBoss32.png differ
diff --git a/gfx/startUp.jpg b/gfx/startUp.jpg
new file mode 100644 (file)
index 0000000..08c402d
Binary files /dev/null and b/gfx/startUp.jpg differ
diff --git a/gfx/stunBolt.png b/gfx/stunBolt.png
new file mode 100644 (file)
index 0000000..edd8630
Binary files /dev/null and b/gfx/stunBolt.png differ
diff --git a/gfx/superCharge.png b/gfx/superCharge.png
new file mode 100644 (file)
index 0000000..4d3dddd
Binary files /dev/null and b/gfx/superCharge.png differ
diff --git a/gfx/targetText.png b/gfx/targetText.png
new file mode 100644 (file)
index 0000000..aa84751
Binary files /dev/null and b/gfx/targetText.png differ
diff --git a/gfx/transport1.png b/gfx/transport1.png
new file mode 100644 (file)
index 0000000..bdf4e0f
Binary files /dev/null and b/gfx/transport1.png differ
diff --git a/gfx/transport2.png b/gfx/transport2.png
new file mode 100644 (file)
index 0000000..347a873
Binary files /dev/null and b/gfx/transport2.png differ
diff --git a/gfx/tug1.png b/gfx/tug1.png
new file mode 100644 (file)
index 0000000..3a6deb7
Binary files /dev/null and b/gfx/tug1.png differ
diff --git a/gfx/tug2.png b/gfx/tug2.png
new file mode 100644 (file)
index 0000000..e854511
Binary files /dev/null and b/gfx/tug2.png differ
diff --git a/gfx/wingmate1.png b/gfx/wingmate1.png
new file mode 100644 (file)
index 0000000..c6fc6cb
Binary files /dev/null and b/gfx/wingmate1.png differ
diff --git a/gfx/wingmate2.png b/gfx/wingmate2.png
new file mode 100644 (file)
index 0000000..99ea4c2
Binary files /dev/null and b/gfx/wingmate2.png differ
diff --git a/music/RE.ogg b/music/RE.ogg
new file mode 100644 (file)
index 0000000..12513fb
Binary files /dev/null and b/music/RE.ogg differ
diff --git a/music/death.ogg b/music/death.ogg
new file mode 100644 (file)
index 0000000..5485539
Binary files /dev/null and b/music/death.ogg differ
diff --git a/music/frozen_jam.ogg b/music/frozen_jam.ogg
new file mode 100644 (file)
index 0000000..24b42a0
Binary files /dev/null and b/music/frozen_jam.ogg differ
diff --git a/music/last_cyber_dance.ogg b/music/last_cyber_dance.ogg
new file mode 100644 (file)
index 0000000..167a646
Binary files /dev/null and b/music/last_cyber_dance.ogg differ
diff --git a/music/orbital_colossus.ogg b/music/orbital_colossus.ogg
new file mode 100644 (file)
index 0000000..f827499
Binary files /dev/null and b/music/orbital_colossus.ogg differ
diff --git a/music/railjet_short.ogg b/music/railjet_short.ogg
new file mode 100644 (file)
index 0000000..178ee5f
Binary files /dev/null and b/music/railjet_short.ogg differ
diff --git a/music/rise_of_spirit.ogg b/music/rise_of_spirit.ogg
new file mode 100644 (file)
index 0000000..c31277c
Binary files /dev/null and b/music/rise_of_spirit.ogg differ
diff --git a/music/sound_and_silence.ogg b/music/sound_and_silence.ogg
new file mode 100644 (file)
index 0000000..e261bf1
Binary files /dev/null and b/music/sound_and_silence.ogg differ
diff --git a/music/space_dimensions.ogg b/music/space_dimensions.ogg
new file mode 100644 (file)
index 0000000..d01c37d
Binary files /dev/null and b/music/space_dimensions.ogg differ
diff --git a/music/through_space.ogg b/music/through_space.ogg
new file mode 100644 (file)
index 0000000..c067a93
Binary files /dev/null and b/music/through_space.ogg differ
diff --git a/music/walking_among_androids.ogg b/music/walking_among_androids.ogg
new file mode 100644 (file)
index 0000000..67ccfd7
Binary files /dev/null and b/music/walking_among_androids.ogg differ
diff --git a/sound/beamLaser.ogg b/sound/beamLaser.ogg
new file mode 100644 (file)
index 0000000..0c240a5
Binary files /dev/null and b/sound/beamLaser.ogg differ
diff --git a/sound/cloak.ogg b/sound/cloak.ogg
new file mode 100644 (file)
index 0000000..8870ac3
Binary files /dev/null and b/sound/cloak.ogg differ
diff --git a/sound/clock.ogg b/sound/clock.ogg
new file mode 100644 (file)
index 0000000..a4834b5
Binary files /dev/null and b/sound/clock.ogg differ
diff --git a/sound/explode.ogg b/sound/explode.ogg
new file mode 100644 (file)
index 0000000..78ee378
Binary files /dev/null and b/sound/explode.ogg differ
diff --git a/sound/explode2.ogg b/sound/explode2.ogg
new file mode 100644 (file)
index 0000000..8be1b86
Binary files /dev/null and b/sound/explode2.ogg differ
diff --git a/sound/explode3.ogg b/sound/explode3.ogg
new file mode 100644 (file)
index 0000000..531eb34
Binary files /dev/null and b/sound/explode3.ogg differ
diff --git a/sound/explode4.ogg b/sound/explode4.ogg
new file mode 100644 (file)
index 0000000..b374ca7
Binary files /dev/null and b/sound/explode4.ogg differ
diff --git a/sound/flyby.ogg b/sound/flyby.ogg
new file mode 100644 (file)
index 0000000..ca03d30
Binary files /dev/null and b/sound/flyby.ogg differ
diff --git a/sound/hyperSpace.ogg b/sound/hyperSpace.ogg
new file mode 100644 (file)
index 0000000..073dd2f
Binary files /dev/null and b/sound/hyperSpace.ogg differ
diff --git a/sound/item.ogg b/sound/item.ogg
new file mode 100644 (file)
index 0000000..8279f88
Binary files /dev/null and b/sound/item.ogg differ
diff --git a/sound/laser.ogg b/sound/laser.ogg
new file mode 100644 (file)
index 0000000..c3d8657
Binary files /dev/null and b/sound/laser.ogg differ
diff --git a/sound/maledeath.ogg b/sound/maledeath.ogg
new file mode 100644 (file)
index 0000000..8f548bf
Binary files /dev/null and b/sound/maledeath.ogg differ
diff --git a/sound/missile.ogg b/sound/missile.ogg
new file mode 100644 (file)
index 0000000..4da5005
Binary files /dev/null and b/sound/missile.ogg differ
diff --git a/sound/missile2.ogg b/sound/missile2.ogg
new file mode 100644 (file)
index 0000000..ad94802
Binary files /dev/null and b/sound/missile2.ogg differ
diff --git a/sound/plasma.ogg b/sound/plasma.ogg
new file mode 100644 (file)
index 0000000..094481d
Binary files /dev/null and b/sound/plasma.ogg differ
diff --git a/sound/plasma2.ogg b/sound/plasma2.ogg
new file mode 100644 (file)
index 0000000..feea895
Binary files /dev/null and b/sound/plasma2.ogg differ
diff --git a/sound/plasma3.ogg b/sound/plasma3.ogg
new file mode 100644 (file)
index 0000000..4bf26da
Binary files /dev/null and b/sound/plasma3.ogg differ
diff --git a/sound/shield.ogg b/sound/shield.ogg
new file mode 100644 (file)
index 0000000..f7ff509
Binary files /dev/null and b/sound/shield.ogg differ
diff --git a/src/Starfighter.cpp b/src/Starfighter.cpp
new file mode 100644 (file)
index 0000000..ed91b8b
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+Project: Starfighter
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+int main(int argc, char **argv)
+{
+       bool cheatAttempt;
+       int cheatCount;
+       int section;
+
+       if (chdir(DATADIR) == -1)
+               printf("Warning: failed to change directory to \"%s\"\n", DATADIR);
+
+       engine_init(); // Must do this first!
+
+       cheatAttempt = false;
+       cheatCount = 0;
+
+       if (argc > 1)
+       {
+               if (strcmp("--help", argv[1]) == 0)
+               {
+                       printf("\nProject: Starfighter %s\n", VERSION);
+                       printf("Copyright Parallel Realities 2003\n");
+                       printf("Copyright Guus Sliepen, Astrid S. de Wijn and others 2012\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("http://starfighter.nongnu.org\n");
+                       printf("\n");
+                       exit(0);
+               }
+       }
+
+       for (int i = 1 ; i < argc ; i++)
+       {
+               if (strcmp(argv[i], "-cheat") == 0)
+                       cheatAttempt = true;
+               if (strcmp(argv[i], "-noaudio") == 0)
+               {
+                       printf("No Audio\n");
+                       engine.useAudio = false;
+               }
+               if (strcmp(argv[i], "-mono") == 0)
+               {
+                       printf("Mono sound output\n");
+                       engine.useAudio = true;
+               }
+               if ((strcmp(argv[i], "humans") == 0) && (cheatCount == 0))
+                       cheatCount = 1;
+               if ((strcmp(argv[i], "do") == 0) && (cheatCount == 1))
+                       cheatCount = 2;
+               if ((strcmp(argv[i], "it") == 0) && (cheatCount == 2))
+                       cheatCount = 3;
+               if (((strcmp(argv[i], "better") == 0) && (cheatCount == 3)) ||
+                       (strcmp(argv[i], "humansdoitbetter") == 0))
+               {
+                       printf("Humans do it better! Cheats enabled.\n");
+                       engine.cheat = true;
+               }
+       }
+
+       atexit(cleanUp);
+
+       initGraphics();
+       initSystem(); // Opens video mode and sound
+       loadFont();
+
+       if (cheatAttempt && !engine.cheat)
+       {
+               clearScreen(black);
+               drawString("That doesn't work anymore", -1, 285, FONT_WHITE);
+               drawString("Try harder...", -1, 315, FONT_WHITE);
+               updateScreen();
+               SDL_Delay(2000);
+               clearScreen(black);
+               updateScreen();
+               SDL_Delay(500);
+       }
+
+       freeGraphics();
+       audio_loadSounds();
+
+       initWeapons();
+       initVars();
+       alien_defs_init();
+
+       setColorIndexes();
+
+       showStory();
+
+       // Determine which part of the game we will go to...
+       section = 0;
+
+       game.difficulty = DIFFICULTY_NORMAL;
+       game_init();
+
+       while (true)
+       {
+               switch (section)
+               {
+                       case 0:
+                               section = doTitle();
+                               break;
+
+                       case 1:
+                               section = intermission();
+                               break;
+
+                       case 2:
+                               if (game.stationedPlanet == -1)
+                                       doCutscene(0);
+                               section = game_mainLoop();
+                               break;
+               }
+       }
+
+       return(0);
+}
diff --git a/src/Starfighter.h b/src/Starfighter.h
new file mode 100644 (file)
index 0000000..dd17d87
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef STARFIGHTER_H
+#define STARFIGHTER_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <math.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "SDL.h"
+#include "SDL_image.h"
+#include "SDL_mixer.h"
+
+#include "defs.h"
+#include "structs.h"
+
+#include "alien.h"
+#include "audio.h"
+#include "bullet.h"
+#include "cargo.h"
+#include "collectable.h"
+#include "engine.h"
+#include "explosion.h"
+#include "game.h"
+#include "graphics.h"
+#include "init.h"
+#include "intermission.h"
+#include "loadSave.h"
+#include "messages.h"
+#include "misc.h"
+#include "missions.h"
+#include "player.h"
+#include "resources.h"
+#include "script.h"
+#include "ship.h"
+#include "shop.h"
+#include "title.h"
+#include "weapons.h"
+
+#endif
diff --git a/src/alien.cpp b/src/alien.cpp
new file mode 100644 (file)
index 0000000..040b0ec
--- /dev/null
@@ -0,0 +1,1711 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012 Guus Sliepen
+Copyright (C) 2012, 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+object alien_defs[CD_MAX];
+object aliens[ALIEN_MAX];
+
+/*
+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.
+*/
+static int alien_getFreeIndex()
+{
+       for (int i = 0 ; i < engine.maxAliens ; i++)
+       {
+               if (!aliens[i].active)
+               {
+                       return i;
+               }
+       }
+
+       return -1;
+}
+
+void alien_defs_init()
+{
+       // Dual Plasma Fighter.
+       alien_defs[CD_DUALFIGHTER].classDef = CD_DUALFIGHTER;
+       alien_defs[CD_DUALFIGHTER].AIType = AI_NORMAL;
+       alien_defs[CD_DUALFIGHTER].speed = 4;
+       alien_defs[CD_DUALFIGHTER].maxShield = 5;
+       alien_defs[CD_DUALFIGHTER].shield = 5;
+       alien_defs[CD_DUALFIGHTER].imageIndex[0] = 2;
+       alien_defs[CD_DUALFIGHTER].imageIndex[1] = 3;
+       alien_defs[CD_DUALFIGHTER].weaponType[0] = W_DOUBLE_SHOT;
+       alien_defs[CD_DUALFIGHTER].weaponType[1] = W_ROCKETS;
+       alien_defs[CD_DUALFIGHTER].chance[0] = 100;
+       alien_defs[CD_DUALFIGHTER].chance[1] = 1;
+       alien_defs[CD_DUALFIGHTER].collectChance = 50;
+       alien_defs[CD_DUALFIGHTER].collectType = P_ANYTHING;
+       alien_defs[CD_DUALFIGHTER].collectValue = 50;
+       alien_defs[CD_DUALFIGHTER].flags = FL_WEAPCO;
+
+       // Missile Boat
+       alien_defs[CD_MISSILEBOAT].classDef = CD_MISSILEBOAT;
+       alien_defs[CD_MISSILEBOAT].AIType = AI_DEFENSIVE;
+       alien_defs[CD_MISSILEBOAT].speed = 2;
+       alien_defs[CD_MISSILEBOAT].maxShield = 50;
+       alien_defs[CD_MISSILEBOAT].shield = 50;
+       alien_defs[CD_MISSILEBOAT].imageIndex[0] = 4;
+       alien_defs[CD_MISSILEBOAT].imageIndex[1] = 5;
+       alien_defs[CD_MISSILEBOAT].weaponType[0] = W_ROCKETS;
+       alien_defs[CD_MISSILEBOAT].weaponType[1] = W_DOUBLE_ROCKETS;
+       alien_defs[CD_MISSILEBOAT].chance[0] = 25;
+       alien_defs[CD_MISSILEBOAT].chance[1] = 4;
+       alien_defs[CD_MISSILEBOAT].collectChance = 25;
+       alien_defs[CD_MISSILEBOAT].collectType = P_ANYTHING;
+       alien_defs[CD_MISSILEBOAT].collectValue = 75;
+       alien_defs[CD_MISSILEBOAT].flags = FL_WEAPCO;
+
+       //Prototype fighter
+       alien_defs[CD_PROTOFIGHTER].classDef = CD_PROTOFIGHTER;
+       alien_defs[CD_PROTOFIGHTER].AIType = AI_DEFENSIVE;
+       alien_defs[CD_PROTOFIGHTER].speed = 5;
+       alien_defs[CD_PROTOFIGHTER].maxShield = 15;
+       alien_defs[CD_PROTOFIGHTER].shield = 15;
+       alien_defs[CD_PROTOFIGHTER].imageIndex[0] = 6;
+       alien_defs[CD_PROTOFIGHTER].imageIndex[1] = 7;
+       alien_defs[CD_PROTOFIGHTER].weaponType[0] = W_TRIPLE_SHOT;
+       alien_defs[CD_PROTOFIGHTER].weaponType[1] = P_ANYTHING;
+       alien_defs[CD_PROTOFIGHTER].chance[0] = 100;
+       alien_defs[CD_PROTOFIGHTER].chance[1] = 1;
+       alien_defs[CD_PROTOFIGHTER].collectChance = 50;
+       alien_defs[CD_PROTOFIGHTER].collectType = P_ANYTHING;
+       alien_defs[CD_PROTOFIGHTER].collectValue = 50;
+       alien_defs[CD_PROTOFIGHTER].flags = FL_WEAPCO;
+
+       // Phoebe and Ursula
+       alien_defs[CD_FRIEND].classDef = CD_FRIEND;
+       alien_defs[CD_FRIEND].AIType = AI_OFFENSIVE;
+       alien_defs[CD_FRIEND].speed = 3;
+       alien_defs[CD_FRIEND].maxShield = 50;
+       alien_defs[CD_FRIEND].shield = 50;
+       alien_defs[CD_FRIEND].imageIndex[0] = 20;
+       alien_defs[CD_FRIEND].imageIndex[1] = 21;
+       alien_defs[CD_FRIEND].weaponType[0] = W_DOUBLE_SHOT;
+       alien_defs[CD_FRIEND].weaponType[1] = W_HOMING_MISSILE;
+       alien_defs[CD_FRIEND].chance[0] = 100;
+       alien_defs[CD_FRIEND].chance[1] = 5;
+       alien_defs[CD_FRIEND].collectChance = 0;
+       alien_defs[CD_FRIEND].collectType = P_CASH;
+       alien_defs[CD_FRIEND].collectValue = 0;
+       alien_defs[CD_FRIEND].flags = FL_FRIEND;
+
+       // Boss 1
+       alien_defs[CD_FRIGATE].classDef = CD_BOSS;
+       alien_defs[CD_FRIGATE].AIType = AI_NORMAL;
+       alien_defs[CD_FRIGATE].speed = 2;
+       alien_defs[CD_FRIGATE].maxShield = 550;
+       alien_defs[CD_FRIGATE].shield = 550;
+       alien_defs[CD_FRIGATE].imageIndex[0] = 8;
+       alien_defs[CD_FRIGATE].imageIndex[1] = 9;
+       alien_defs[CD_FRIGATE].weaponType[0] = W_MICRO_ROCKETS;
+       alien_defs[CD_FRIGATE].weaponType[1] = W_ENERGYRAY;
+       alien_defs[CD_FRIGATE].chance[0] = 100;
+       alien_defs[CD_FRIGATE].chance[1] = 85;
+       alien_defs[CD_FRIGATE].collectChance = 100;
+       alien_defs[CD_FRIGATE].collectType = P_CASH;
+       alien_defs[CD_FRIGATE].collectValue = 500;
+       alien_defs[CD_FRIGATE].flags = FL_WEAPCO;
+
+       alien_defs[CD_FRIGATE_WING1].classDef = CD_FRIGATE_WING1;
+       alien_defs[CD_FRIGATE_WING1].AIType = AI_NORMAL;
+       alien_defs[CD_FRIGATE_WING1].speed = 2;
+       alien_defs[CD_FRIGATE_WING1].maxShield = 100;
+       alien_defs[CD_FRIGATE_WING1].shield = 100;
+       alien_defs[CD_FRIGATE_WING1].imageIndex[0] = 10;
+       alien_defs[CD_FRIGATE_WING1].imageIndex[1] = 11;
+       alien_defs[CD_FRIGATE_WING1].weaponType[0] = W_TRIPLE_SHOT;
+       alien_defs[CD_FRIGATE_WING1].weaponType[1] = W_ROCKETS;
+       alien_defs[CD_FRIGATE_WING1].chance[0] = 100;
+       alien_defs[CD_FRIGATE_WING1].chance[1] = 10;
+       alien_defs[CD_FRIGATE_WING1].collectChance = 100;
+       alien_defs[CD_FRIGATE_WING1].collectType = P_ANYTHING;
+       alien_defs[CD_FRIGATE_WING1].collectValue = 250;
+       alien_defs[CD_FRIGATE_WING1].flags = FL_WEAPCO | FL_DAMAGEOWNER;
+
+       alien_defs[CD_FRIGATE_WING2].classDef = CD_FRIGATE_WING2;
+       alien_defs[CD_FRIGATE_WING2].AIType = AI_NORMAL;
+       alien_defs[CD_FRIGATE_WING2].speed = 2;
+       alien_defs[CD_FRIGATE_WING2].maxShield = 100;
+       alien_defs[CD_FRIGATE_WING2].shield = 100;
+       alien_defs[CD_FRIGATE_WING2].imageIndex[0] = 12;
+       alien_defs[CD_FRIGATE_WING2].imageIndex[1] = 13;
+       alien_defs[CD_FRIGATE_WING2].weaponType[0] = W_TRIPLE_SHOT;
+       alien_defs[CD_FRIGATE_WING2].weaponType[1] = W_ROCKETS;
+       alien_defs[CD_FRIGATE_WING2].chance[0] = 100;
+       alien_defs[CD_FRIGATE_WING2].chance[1] = 10;
+       alien_defs[CD_FRIGATE_WING2].collectChance = 100;
+       alien_defs[CD_FRIGATE_WING2].collectType = P_ANYTHING;
+       alien_defs[CD_FRIGATE_WING2].collectValue = 250;
+       alien_defs[CD_FRIGATE_WING2].flags = FL_WEAPCO | FL_DAMAGEOWNER;
+
+       // Transport ship
+       alien_defs[CD_TRANSPORTSHIP].classDef = CD_TRANSPORTSHIP;
+       alien_defs[CD_TRANSPORTSHIP].AIType = AI_EVASIVE;
+       alien_defs[CD_TRANSPORTSHIP].speed = 4;
+       alien_defs[CD_TRANSPORTSHIP].maxShield = 10;
+       alien_defs[CD_TRANSPORTSHIP].shield = 10;
+       alien_defs[CD_TRANSPORTSHIP].imageIndex[0] = 14;
+       alien_defs[CD_TRANSPORTSHIP].imageIndex[1] = 15;
+       alien_defs[CD_TRANSPORTSHIP].weaponType[0] = W_DOUBLE_SHOT;
+       alien_defs[CD_TRANSPORTSHIP].weaponType[1] = W_DOUBLE_SHOT;
+       alien_defs[CD_TRANSPORTSHIP].chance[0] = 0;
+       alien_defs[CD_TRANSPORTSHIP].chance[1] = 0;
+       alien_defs[CD_TRANSPORTSHIP].collectChance = 100;
+       alien_defs[CD_TRANSPORTSHIP].collectType = P_WEAPONS;
+       alien_defs[CD_TRANSPORTSHIP].collectValue = 40;
+       alien_defs[CD_TRANSPORTSHIP].flags = FL_WEAPCO | FL_NOFIRE;
+
+       // Cargo ship
+       alien_defs[CD_CARGOSHIP].classDef = CD_CARGOSHIP;
+       alien_defs[CD_CARGOSHIP].AIType = AI_EVASIVE;
+       alien_defs[CD_CARGOSHIP].speed = 4;
+       alien_defs[CD_CARGOSHIP].maxShield = 10;
+       alien_defs[CD_CARGOSHIP].shield = 10;
+       alien_defs[CD_CARGOSHIP].imageIndex[0] = 22;
+       alien_defs[CD_CARGOSHIP].imageIndex[1] = 23;
+       alien_defs[CD_CARGOSHIP].weaponType[0] = W_DOUBLE_SHOT;
+       alien_defs[CD_CARGOSHIP].weaponType[1] = W_DOUBLE_SHOT;
+       alien_defs[CD_CARGOSHIP].chance[0] = 0;
+       alien_defs[CD_CARGOSHIP].chance[1] = 0;
+       alien_defs[CD_CARGOSHIP].collectChance = 50;
+       alien_defs[CD_CARGOSHIP].collectType = P_ANYTHING;
+       alien_defs[CD_CARGOSHIP].collectValue = 100;
+       alien_defs[CD_CARGOSHIP].flags = FL_WEAPCO | FL_NOFIRE;
+
+       // Weapco Miner
+       alien_defs[CD_MINER].classDef = CD_MINER;
+       alien_defs[CD_MINER].AIType = AI_EVASIVE;
+       alien_defs[CD_MINER].speed = 4;
+       alien_defs[CD_MINER].maxShield = 25;
+       alien_defs[CD_MINER].shield = 25;
+       alien_defs[CD_MINER].imageIndex[0] = 16;
+       alien_defs[CD_MINER].imageIndex[1] = 17;
+       alien_defs[CD_MINER].weaponType[0] = W_DOUBLE_SHOT;
+       alien_defs[CD_MINER].weaponType[1] = W_DOUBLE_SHOT;
+       alien_defs[CD_MINER].chance[0] = 0;
+       alien_defs[CD_MINER].chance[1] = 0;
+       alien_defs[CD_MINER].collectChance = 100;
+       alien_defs[CD_MINER].collectType = P_ANYTHING;
+       alien_defs[CD_MINER].collectValue = 30;
+       alien_defs[CD_MINER].flags = FL_WEAPCO | FL_NOFIRE | FL_DROPMINES;
+
+       // Kline
+       alien_defs[CD_KLINE].classDef = CD_KLINE;
+       alien_defs[CD_KLINE].AIType = AI_OFFENSIVE;
+       alien_defs[CD_KLINE].speed = 5;
+       alien_defs[CD_KLINE].maxShield = 2000;
+       alien_defs[CD_KLINE].shield = 2000;
+       alien_defs[CD_KLINE].imageIndex[0] = 18;
+       alien_defs[CD_KLINE].imageIndex[1] = 19;
+       alien_defs[CD_KLINE].weaponType[0] = W_TRIPLE_SHOT;
+       alien_defs[CD_KLINE].weaponType[1] = W_MICRO_ROCKETS;
+       alien_defs[CD_KLINE].chance[0] = 100;
+       alien_defs[CD_KLINE].chance[1] = 2;
+       alien_defs[CD_KLINE].collectChance = 0;
+       alien_defs[CD_KLINE].collectType = P_ANYTHING;
+       alien_defs[CD_KLINE].collectValue = 0;
+       alien_defs[CD_KLINE].flags = FL_WEAPCO | FL_ALWAYSFACE | FL_CIRCLES;
+
+       // Aim Fighter
+       alien_defs[CD_AIMFIGHTER].classDef = CD_AIMFIGHTER;
+       alien_defs[CD_AIMFIGHTER].AIType = AI_NORMAL;
+       alien_defs[CD_AIMFIGHTER].speed = 3;
+       alien_defs[CD_AIMFIGHTER].maxShield = 15;
+       alien_defs[CD_AIMFIGHTER].shield = 15;
+       alien_defs[CD_AIMFIGHTER].imageIndex[0] = 8;
+       alien_defs[CD_AIMFIGHTER].imageIndex[1] = 9;
+       alien_defs[CD_AIMFIGHTER].weaponType[0] = W_AIMED_SHOT;
+       alien_defs[CD_AIMFIGHTER].weaponType[1] = W_AIMED_SHOT;
+       alien_defs[CD_AIMFIGHTER].chance[0] = 7;
+       alien_defs[CD_AIMFIGHTER].chance[1] = 1;
+       alien_defs[CD_AIMFIGHTER].collectChance = 75;
+       alien_defs[CD_AIMFIGHTER].collectType = P_ANYTHING;
+       alien_defs[CD_AIMFIGHTER].collectValue = 100;
+       alien_defs[CD_AIMFIGHTER].flags = FL_WEAPCO | FL_AIMS;
+
+       // Slave ship
+       alien_defs[CD_SLAVETRANSPORT].classDef = CD_SLAVETRANSPORT;
+       alien_defs[CD_SLAVETRANSPORT].AIType = AI_EVASIVE;
+       alien_defs[CD_SLAVETRANSPORT].speed = 2;
+       alien_defs[CD_SLAVETRANSPORT].maxShield = 10;
+       alien_defs[CD_SLAVETRANSPORT].shield = 20;
+       alien_defs[CD_SLAVETRANSPORT].imageIndex[0] = 10;
+       alien_defs[CD_SLAVETRANSPORT].imageIndex[1] = 11;
+       alien_defs[CD_SLAVETRANSPORT].weaponType[0] = W_DOUBLE_SHOT;
+       alien_defs[CD_SLAVETRANSPORT].weaponType[1] = W_DOUBLE_SHOT;
+       alien_defs[CD_SLAVETRANSPORT].chance[0] = 0;
+       alien_defs[CD_SLAVETRANSPORT].chance[1] = 0;
+       alien_defs[CD_SLAVETRANSPORT].collectChance = 100;
+       alien_defs[CD_SLAVETRANSPORT].collectType = P_SLAVES;
+       alien_defs[CD_SLAVETRANSPORT].collectValue = 25;
+       alien_defs[CD_SLAVETRANSPORT].flags = FL_WEAPCO | FL_NOFIRE;
+
+       // Good Transport
+       alien_defs[CD_GOODTRANSPORT].classDef = CD_GOODTRANSPORT;
+       alien_defs[CD_GOODTRANSPORT].AIType = AI_EVASIVE;
+       alien_defs[CD_GOODTRANSPORT].speed = 3;
+       alien_defs[CD_GOODTRANSPORT].maxShield = 75;
+       alien_defs[CD_GOODTRANSPORT].shield = 75;
+       alien_defs[CD_GOODTRANSPORT].imageIndex[0] = 12;
+       alien_defs[CD_GOODTRANSPORT].imageIndex[1] = 13;
+       alien_defs[CD_GOODTRANSPORT].weaponType[0] = W_AIMED_SHOT;
+       alien_defs[CD_GOODTRANSPORT].weaponType[1] = W_AIMED_SHOT;
+       alien_defs[CD_GOODTRANSPORT].chance[0] = 100;
+       alien_defs[CD_GOODTRANSPORT].chance[1] = 100;
+       alien_defs[CD_GOODTRANSPORT].collectChance = 0;
+       alien_defs[CD_GOODTRANSPORT].collectType = P_ANYTHING;
+       alien_defs[CD_GOODTRANSPORT].collectValue = 0;
+       alien_defs[CD_GOODTRANSPORT].flags = FL_FRIEND | FL_NOFIRE | FL_AIMS;
+
+       // Sid Wilson
+       alien_defs[CD_SID].classDef = CD_SID;
+       alien_defs[CD_SID].AIType = AI_NORMAL;
+       alien_defs[CD_SID].speed = 3;
+       alien_defs[CD_SID].maxShield = 50;
+       alien_defs[CD_SID].shield = 50;
+       alien_defs[CD_SID].imageIndex[0] = 24;
+       alien_defs[CD_SID].imageIndex[1] = 25;
+       alien_defs[CD_SID].weaponType[0] = W_IONCANNON;
+       alien_defs[CD_SID].weaponType[1] = W_IONCANNON;
+       alien_defs[CD_SID].chance[0] = 100;
+       alien_defs[CD_SID].chance[1] = 0;
+       alien_defs[CD_SID].collectChance = 0;
+       alien_defs[CD_SID].collectType = P_ANYTHING;
+       alien_defs[CD_SID].collectValue = 0;
+       alien_defs[CD_SID].flags = FL_FRIEND | FL_AIMS;
+
+       // Mining Vessel Boss
+       alien_defs[CD_MINEBOSS].classDef = CD_BOSS;
+       alien_defs[CD_MINEBOSS].AIType = AI_NORMAL;
+       alien_defs[CD_MINEBOSS].speed = 3;
+       alien_defs[CD_MINEBOSS].maxShield = 1000;
+       alien_defs[CD_MINEBOSS].shield = 1000;
+       alien_defs[CD_MINEBOSS].imageIndex[0] = 26;
+       alien_defs[CD_MINEBOSS].imageIndex[1] = 27;
+       alien_defs[CD_MINEBOSS].weaponType[0] = W_TRIPLE_SHOT;
+       alien_defs[CD_MINEBOSS].weaponType[1] = W_SPREADSHOT;
+       alien_defs[CD_MINEBOSS].chance[0] = 0;
+       alien_defs[CD_MINEBOSS].chance[1] = 0;
+       alien_defs[CD_MINEBOSS].collectChance = 100;
+       alien_defs[CD_MINEBOSS].collectType = P_ANYTHING;
+       alien_defs[CD_MINEBOSS].collectValue = 1000;
+       alien_defs[CD_MINEBOSS].flags = FL_WEAPCO | FL_IMMORTAL;
+
+       alien_defs[CD_BOSS2_WING1].classDef = CD_BOSS2_WING1;
+       alien_defs[CD_BOSS2_WING1].AIType = AI_NORMAL;
+       alien_defs[CD_BOSS2_WING1].speed = 1;
+       alien_defs[CD_BOSS2_WING1].maxShield = 250;
+       alien_defs[CD_BOSS2_WING1].shield = 250;
+       alien_defs[CD_BOSS2_WING1].imageIndex[0] = 28;
+       alien_defs[CD_BOSS2_WING1].imageIndex[1] = 29;
+       alien_defs[CD_BOSS2_WING1].weaponType[0] = W_TRIPLE_SHOT;
+       alien_defs[CD_BOSS2_WING1].weaponType[1] = W_SPREADSHOT;
+       alien_defs[CD_BOSS2_WING1].chance[0] = 0;
+       alien_defs[CD_BOSS2_WING1].chance[1] = 0;
+       alien_defs[CD_BOSS2_WING1].collectChance = 100;
+       alien_defs[CD_BOSS2_WING1].collectType = P_ANYTHING;
+       alien_defs[CD_BOSS2_WING1].collectValue = 250;
+       alien_defs[CD_BOSS2_WING1].flags = FL_WEAPCO | FL_DAMAGEOWNER;
+
+       alien_defs[CD_BOSS2_WING2].classDef = CD_BOSS2_WING2;
+       alien_defs[CD_BOSS2_WING2].AIType = AI_NORMAL;
+       alien_defs[CD_BOSS2_WING2].speed = 1;
+       alien_defs[CD_BOSS2_WING2].maxShield = 500;
+       alien_defs[CD_BOSS2_WING2].shield = 500;
+       alien_defs[CD_BOSS2_WING2].imageIndex[0] = 30;
+       alien_defs[CD_BOSS2_WING2].imageIndex[1] = 31;
+       alien_defs[CD_BOSS2_WING2].weaponType[0] = W_TRIPLE_SHOT;
+       alien_defs[CD_BOSS2_WING2].weaponType[1] = W_SPREADSHOT;
+       alien_defs[CD_BOSS2_WING2].chance[0] = 0;
+       alien_defs[CD_BOSS2_WING2].chance[1] = 0;
+       alien_defs[CD_BOSS2_WING2].collectChance = 100;
+       alien_defs[CD_BOSS2_WING2].collectType = P_ANYTHING;
+       alien_defs[CD_BOSS2_WING2].collectValue = 250;
+       alien_defs[CD_BOSS2_WING2].flags = FL_WEAPCO | FL_DEPLOYDRONES | FL_DAMAGEOWNER;
+
+       alien_defs[CD_BOSS2_WING3].classDef = CD_BOSS2_WING3;
+       alien_defs[CD_BOSS2_WING3].AIType = AI_NORMAL;
+       alien_defs[CD_BOSS2_WING3].speed = 1;
+       alien_defs[CD_BOSS2_WING3].maxShield = 500;
+       alien_defs[CD_BOSS2_WING3].shield = 500;
+       alien_defs[CD_BOSS2_WING3].imageIndex[0] = 32;
+       alien_defs[CD_BOSS2_WING3].imageIndex[1] = 33;
+       alien_defs[CD_BOSS2_WING3].weaponType[0] = W_TRIPLE_SHOT;
+       alien_defs[CD_BOSS2_WING3].weaponType[1] = W_SPREADSHOT;
+       alien_defs[CD_BOSS2_WING3].chance[0] = 0;
+       alien_defs[CD_BOSS2_WING3].chance[1] = 0;
+       alien_defs[CD_BOSS2_WING3].collectChance = 100;
+       alien_defs[CD_BOSS2_WING3].collectType = P_ANYTHING;
+       alien_defs[CD_BOSS2_WING3].collectValue = 250;
+       alien_defs[CD_BOSS2_WING3].flags = FL_WEAPCO | FL_DEPLOYDRONES | FL_DAMAGEOWNER;
+
+       alien_defs[CD_BOSS2_WING4].classDef = CD_BOSS2_WING4;
+       alien_defs[CD_BOSS2_WING4].AIType = AI_NORMAL;
+       alien_defs[CD_BOSS2_WING4].speed = 1;
+       alien_defs[CD_BOSS2_WING4].maxShield = 250;
+       alien_defs[CD_BOSS2_WING4].shield = 250;
+       alien_defs[CD_BOSS2_WING4].imageIndex[0] = 34;
+       alien_defs[CD_BOSS2_WING4].imageIndex[1] = 35;
+       alien_defs[CD_BOSS2_WING4].weaponType[0] = W_TRIPLE_SHOT;
+       alien_defs[CD_BOSS2_WING4].weaponType[1] = W_SPREADSHOT;
+       alien_defs[CD_BOSS2_WING4].chance[0] = 0;
+       alien_defs[CD_BOSS2_WING4].chance[1] = 0;
+       alien_defs[CD_BOSS2_WING4].collectChance = 100;
+       alien_defs[CD_BOSS2_WING4].collectType = P_ANYTHING;
+       alien_defs[CD_BOSS2_WING4].collectValue = 250;
+       alien_defs[CD_BOSS2_WING4].flags = FL_WEAPCO | FL_DAMAGEOWNER;
+
+       // Drone
+       alien_defs[CD_DRONE].classDef = CD_DRONE;
+       alien_defs[CD_DRONE].AIType = AI_OFFENSIVE;
+       alien_defs[CD_DRONE].speed = 8;
+       alien_defs[CD_DRONE].maxShield = 5;
+       alien_defs[CD_DRONE].shield = 5;
+       alien_defs[CD_DRONE].imageIndex[0] = 36;
+       alien_defs[CD_DRONE].imageIndex[1] = 37;
+       alien_defs[CD_DRONE].weaponType[0] = W_DOUBLE_SHOT;
+       alien_defs[CD_DRONE].weaponType[1] = W_LASER;
+       alien_defs[CD_DRONE].chance[0] = 100;
+       alien_defs[CD_DRONE].chance[1] = 0;
+       alien_defs[CD_DRONE].collectChance = 10;
+       alien_defs[CD_DRONE].collectType = P_SHIELD;
+       alien_defs[CD_DRONE].collectValue = 1;
+       alien_defs[CD_DRONE].flags = FL_WEAPCO;
+
+       // Experimental Fighter
+       alien_defs[CD_CLOAKFIGHTER].classDef = CD_CLOAKFIGHTER;
+       alien_defs[CD_CLOAKFIGHTER].AIType = AI_OFFENSIVE;
+       alien_defs[CD_CLOAKFIGHTER].speed = 6;
+       alien_defs[CD_CLOAKFIGHTER].maxShield = 1000;
+       alien_defs[CD_CLOAKFIGHTER].shield = 1000;
+       alien_defs[CD_CLOAKFIGHTER].imageIndex[0] = 10;
+       alien_defs[CD_CLOAKFIGHTER].imageIndex[1] = 11;
+       alien_defs[CD_CLOAKFIGHTER].weaponType[0] = W_SPREADSHOT;
+       alien_defs[CD_CLOAKFIGHTER].weaponType[1] = W_DOUBLE_ROCKETS;
+       alien_defs[CD_CLOAKFIGHTER].chance[0] = 100;
+       alien_defs[CD_CLOAKFIGHTER].chance[1] = 5;
+       alien_defs[CD_CLOAKFIGHTER].collectChance = 100;
+       alien_defs[CD_CLOAKFIGHTER].collectType = P_CASH;
+       alien_defs[CD_CLOAKFIGHTER].collectValue = 250;
+       alien_defs[CD_CLOAKFIGHTER].flags = FL_WEAPCO | FL_CANCLOAK | FL_RUNSAWAY;
+
+       // Evil Ursula
+       alien_defs[CD_EVILURSULA].classDef = CD_EVILURSULA;
+       alien_defs[CD_EVILURSULA].AIType = AI_OFFENSIVE;
+       alien_defs[CD_EVILURSULA].speed = 5;
+       alien_defs[CD_EVILURSULA].maxShield = 500;
+       alien_defs[CD_EVILURSULA].shield = 500;
+       alien_defs[CD_EVILURSULA].imageIndex[0] = 12;
+       alien_defs[CD_EVILURSULA].imageIndex[1] = 13;
+       alien_defs[CD_EVILURSULA].weaponType[0] = W_TRIPLE_SHOT;
+       alien_defs[CD_EVILURSULA].weaponType[1] = W_HOMING_MISSILE;
+       alien_defs[CD_EVILURSULA].chance[0] = 100;
+       alien_defs[CD_EVILURSULA].chance[1] = 100;
+       alien_defs[CD_EVILURSULA].collectChance = 100;
+       alien_defs[CD_EVILURSULA].collectType = P_ESCAPEPOD;
+       alien_defs[CD_EVILURSULA].collectValue = 1;
+       alien_defs[CD_EVILURSULA].flags = FL_WEAPCO;
+
+       // Mercenary
+       alien_defs[CD_KRASS].classDef = CD_KRASS;
+       alien_defs[CD_KRASS].AIType = AI_OFFENSIVE;
+       alien_defs[CD_KRASS].speed = 5;
+       alien_defs[CD_KRASS].maxShield = 1000;
+       alien_defs[CD_KRASS].shield = 1000;
+       alien_defs[CD_KRASS].imageIndex[0] = 26;
+       alien_defs[CD_KRASS].imageIndex[1] = 27;
+       alien_defs[CD_KRASS].weaponType[0] = W_SPREADSHOT;
+       alien_defs[CD_KRASS].weaponType[1] = W_CHARGER;
+       alien_defs[CD_KRASS].chance[0] = 100;
+       alien_defs[CD_KRASS].chance[1] = 0;
+       alien_defs[CD_KRASS].collectChance = 100;
+       alien_defs[CD_KRASS].collectType = P_ANYTHING;
+       alien_defs[CD_KRASS].collectValue = 2250;
+       alien_defs[CD_KRASS].flags = FL_FRIEND | FL_IMMORTAL;
+
+       // Executive Transport
+       alien_defs[CD_EXEC].classDef = CD_BOSS;
+       alien_defs[CD_EXEC].AIType = AI_NORMAL;
+       alien_defs[CD_EXEC].speed = 5;
+       alien_defs[CD_EXEC].maxShield = 1000;
+       alien_defs[CD_EXEC].shield = 1000;
+       alien_defs[CD_EXEC].imageIndex[0] = 28;
+       alien_defs[CD_EXEC].imageIndex[1] = 28;
+       alien_defs[CD_EXEC].weaponType[0] = W_SPREADSHOT;
+       alien_defs[CD_EXEC].weaponType[1] = W_HOMING_MISSILE;
+       alien_defs[CD_EXEC].chance[0] = 0;
+       alien_defs[CD_EXEC].chance[1] = 0;
+       alien_defs[CD_EXEC].collectChance = 0;
+       alien_defs[CD_EXEC].collectType = P_ANYTHING;
+       alien_defs[CD_EXEC].collectValue = 2000;
+       alien_defs[CD_EXEC].flags = FL_WEAPCO | FL_NOFIRE;
+
+       // Asteroid
+       alien_defs[CD_ASTEROID].classDef = CD_ASTEROID;
+       alien_defs[CD_ASTEROID].AIType = AI_WANDER;
+       alien_defs[CD_ASTEROID].speed = 1;
+       alien_defs[CD_ASTEROID].maxShield = 50;
+       alien_defs[CD_ASTEROID].shield = 50;
+       alien_defs[CD_ASTEROID].imageIndex[0] = 38;
+       alien_defs[CD_ASTEROID].imageIndex[1] = 38;
+       alien_defs[CD_ASTEROID].weaponType[0] = W_SPREADSHOT;
+       alien_defs[CD_ASTEROID].weaponType[1] = W_HOMING_MISSILE;
+       alien_defs[CD_ASTEROID].chance[0] = 0;
+       alien_defs[CD_ASTEROID].chance[1] = 0;
+       alien_defs[CD_ASTEROID].collectChance = 25;
+       alien_defs[CD_ASTEROID].collectType = P_ORE;
+       alien_defs[CD_ASTEROID].collectValue = 1;
+       alien_defs[CD_ASTEROID].flags = FL_WEAPCO;
+
+       alien_defs[CD_ASTEROID2].classDef = CD_ASTEROID2;
+       alien_defs[CD_ASTEROID2].AIType = AI_WANDER;
+       alien_defs[CD_ASTEROID2].speed = 1;
+       alien_defs[CD_ASTEROID2].maxShield = 10;
+       alien_defs[CD_ASTEROID2].shield = 10;
+       alien_defs[CD_ASTEROID2].imageIndex[0] = 39;
+       alien_defs[CD_ASTEROID2].imageIndex[1] = 40;
+       alien_defs[CD_ASTEROID2].weaponType[0] = W_SPREADSHOT;
+       alien_defs[CD_ASTEROID2].weaponType[1] = W_HOMING_MISSILE;
+       alien_defs[CD_ASTEROID2].chance[0] = 0;
+       alien_defs[CD_ASTEROID2].chance[1] = 0;
+       alien_defs[CD_ASTEROID2].collectChance = 25;
+       alien_defs[CD_ASTEROID2].collectType = P_ORE;
+       alien_defs[CD_ASTEROID2].collectValue = 1;
+       alien_defs[CD_ASTEROID2].flags = FL_WEAPCO;
+
+       // Escort
+       alien_defs[CD_ESCORT].classDef = CD_ESCORT;
+       alien_defs[CD_ESCORT].AIType = AI_NORMAL;
+       alien_defs[CD_ESCORT].speed = 3;
+       alien_defs[CD_ESCORT].maxShield = 200;
+       alien_defs[CD_ESCORT].shield = 200;
+       alien_defs[CD_ESCORT].imageIndex[0] = 30;
+       alien_defs[CD_ESCORT].imageIndex[1] = 31;
+       alien_defs[CD_ESCORT].weaponType[0] = W_LASER;
+       alien_defs[CD_ESCORT].weaponType[1] = W_LASER;
+       alien_defs[CD_ESCORT].chance[0] = 25;
+       alien_defs[CD_ESCORT].chance[1] = 25;
+       alien_defs[CD_ESCORT].collectChance = 100;
+       alien_defs[CD_ESCORT].collectType = P_ANYTHING;
+       alien_defs[CD_ESCORT].collectValue = 100;
+       alien_defs[CD_ESCORT].flags = FL_WEAPCO;
+
+       // Mobile Ray Cannon
+       alien_defs[CD_MOBILE_RAY].classDef = CD_MOBILE_RAY;
+       alien_defs[CD_MOBILE_RAY].AIType = AI_OFFENSIVE;
+       alien_defs[CD_MOBILE_RAY].speed = 5;
+       alien_defs[CD_MOBILE_RAY].maxShield = 250;
+       alien_defs[CD_MOBILE_RAY].shield = 250;
+       alien_defs[CD_MOBILE_RAY].imageIndex[0] = 10;
+       alien_defs[CD_MOBILE_RAY].imageIndex[1] = 11;
+       alien_defs[CD_MOBILE_RAY].weaponType[0] = W_ENERGYRAY;
+       alien_defs[CD_MOBILE_RAY].weaponType[1] = W_ENERGYRAY;
+       alien_defs[CD_MOBILE_RAY].chance[0] = 50;
+       alien_defs[CD_MOBILE_RAY].chance[1] = 50;
+       alien_defs[CD_MOBILE_RAY].collectChance = 75;
+       alien_defs[CD_MOBILE_RAY].collectType = P_ANYTHING;
+       alien_defs[CD_MOBILE_RAY].collectValue = 100;
+       alien_defs[CD_MOBILE_RAY].flags = FL_WEAPCO;
+
+       // Rebel Carrier
+       alien_defs[CD_REBELCARRIER].classDef = CD_REBELCARRIER;
+       alien_defs[CD_REBELCARRIER].AIType = AI_OFFENSIVE;
+       alien_defs[CD_REBELCARRIER].speed = 2;
+       alien_defs[CD_REBELCARRIER].maxShield = 100;
+       alien_defs[CD_REBELCARRIER].shield = 100;
+       alien_defs[CD_REBELCARRIER].imageIndex[0] = 32;
+       alien_defs[CD_REBELCARRIER].imageIndex[1] = 33;
+       alien_defs[CD_REBELCARRIER].weaponType[0] = W_DOUBLE_ROCKETS;
+       alien_defs[CD_REBELCARRIER].weaponType[1] = W_MICRO_ROCKETS;
+       alien_defs[CD_REBELCARRIER].chance[0] = 50;
+       alien_defs[CD_REBELCARRIER].chance[1] = 2;
+       alien_defs[CD_REBELCARRIER].collectChance = 0;
+       alien_defs[CD_REBELCARRIER].collectType = P_SHIELD;
+       alien_defs[CD_REBELCARRIER].collectValue = 0;
+       alien_defs[CD_REBELCARRIER].flags = FL_FRIEND;
+
+       // Pluto Boss
+       alien_defs[CD_PLUTOBOSS].classDef = CD_PLUTOBOSS;
+       alien_defs[CD_PLUTOBOSS].AIType = AI_OFFENSIVE;
+       alien_defs[CD_PLUTOBOSS].speed = 4;
+       alien_defs[CD_PLUTOBOSS].maxShield = 500;
+       alien_defs[CD_PLUTOBOSS].shield = 500;
+       alien_defs[CD_PLUTOBOSS].imageIndex[0] = 12;
+       alien_defs[CD_PLUTOBOSS].imageIndex[1] = 13;
+       alien_defs[CD_PLUTOBOSS].weaponType[0] = W_DOUBLE_ROCKETS;
+       alien_defs[CD_PLUTOBOSS].weaponType[1] = W_MICRO_ROCKETS;
+       alien_defs[CD_PLUTOBOSS].chance[0] = 50;
+       alien_defs[CD_PLUTOBOSS].chance[1] = 2;
+       alien_defs[CD_PLUTOBOSS].collectChance = 0;
+       alien_defs[CD_PLUTOBOSS].collectType = P_ANYTHING;
+       alien_defs[CD_PLUTOBOSS].collectValue = 1000;
+       alien_defs[CD_PLUTOBOSS].flags = FL_WEAPCO;
+
+       // Pluto Boss Barrier
+       alien_defs[CD_BARRIER].classDef = CD_BARRIER;
+       alien_defs[CD_BARRIER].AIType = AI_OFFENSIVE;
+       alien_defs[CD_BARRIER].speed = 1;
+       alien_defs[CD_BARRIER].maxShield = 250;
+       alien_defs[CD_BARRIER].shield = 250;
+       alien_defs[CD_BARRIER].imageIndex[0] = 32;
+       alien_defs[CD_BARRIER].imageIndex[1] = 33;
+       alien_defs[CD_BARRIER].weaponType[0] = W_DOUBLE_SHOT;
+       alien_defs[CD_BARRIER].weaponType[1] = W_MICRO_ROCKETS;
+       alien_defs[CD_BARRIER].chance[0] = 0;
+       alien_defs[CD_BARRIER].chance[1] = 0;
+       alien_defs[CD_BARRIER].collectChance = 100;
+       alien_defs[CD_BARRIER].collectType = P_ANYTHING;
+       alien_defs[CD_BARRIER].collectValue = 25;
+       alien_defs[CD_BARRIER].flags = FL_WEAPCO | FL_NOFIRE;
+
+       // Neptune Boss
+       alien_defs[CD_NEPTUNEBOSS].classDef = CD_NEPTUNEBOSS;
+       alien_defs[CD_NEPTUNEBOSS].AIType = AI_OFFENSIVE;
+       alien_defs[CD_NEPTUNEBOSS].speed = 4;
+       alien_defs[CD_NEPTUNEBOSS].maxShield = 800;
+       alien_defs[CD_NEPTUNEBOSS].shield = 800;
+       alien_defs[CD_NEPTUNEBOSS].imageIndex[0] = 12;
+       alien_defs[CD_NEPTUNEBOSS].imageIndex[1] = 13;
+       alien_defs[CD_NEPTUNEBOSS].weaponType[0] = W_DOUBLE_SHOT;
+       alien_defs[CD_NEPTUNEBOSS].weaponType[1] = W_MICRO_ROCKETS;
+       alien_defs[CD_NEPTUNEBOSS].chance[0] = 100;
+       alien_defs[CD_NEPTUNEBOSS].chance[1] = 0;
+       alien_defs[CD_NEPTUNEBOSS].collectChance = 100;
+       alien_defs[CD_NEPTUNEBOSS].collectType = P_ANYTHING;
+       alien_defs[CD_NEPTUNEBOSS].collectValue = 1000;
+       alien_defs[CD_NEPTUNEBOSS].flags = FL_WEAPCO;
+
+       // Mobile Shield
+       alien_defs[CD_MOBILESHIELD].classDef = CD_MOBILESHIELD;
+       alien_defs[CD_MOBILESHIELD].AIType = AI_EVASIVE;
+       alien_defs[CD_MOBILESHIELD].speed = 6;
+       alien_defs[CD_MOBILESHIELD].maxShield = 150;
+       alien_defs[CD_MOBILESHIELD].shield = 150;
+       alien_defs[CD_MOBILESHIELD].imageIndex[0] = 34;
+       alien_defs[CD_MOBILESHIELD].imageIndex[1] = 35;
+       alien_defs[CD_MOBILESHIELD].weaponType[0] = W_DOUBLE_SHOT;
+       alien_defs[CD_MOBILESHIELD].weaponType[1] = W_MICRO_ROCKETS;
+       alien_defs[CD_MOBILESHIELD].chance[0] = 0;
+       alien_defs[CD_MOBILESHIELD].chance[1] = 0;
+       alien_defs[CD_MOBILESHIELD].collectChance = 100;
+       alien_defs[CD_MOBILESHIELD].collectType = P_ANYTHING;
+       alien_defs[CD_MOBILESHIELD].collectValue = 25;
+       alien_defs[CD_MOBILESHIELD].flags = FL_WEAPCO | FL_NOFIRE;
+
+       // Firefly
+       alien_defs[CD_FIREFLY].classDef = CD_FIREFLY;
+       alien_defs[CD_FIREFLY].AIType = AI_OFFENSIVE;
+       alien_defs[CD_FIREFLY].speed = 5;
+       alien_defs[CD_FIREFLY].maxShield = 250;
+       alien_defs[CD_FIREFLY].shield = 250;
+       alien_defs[CD_FIREFLY].imageIndex[0] = 0;
+       alien_defs[CD_FIREFLY].imageIndex[1] = 1;
+       alien_defs[CD_FIREFLY].weaponType[0] = W_TRIPLE_SHOT;
+       alien_defs[CD_FIREFLY].weaponType[1] = W_DOUBLE_ROCKETS;
+       alien_defs[CD_FIREFLY].chance[0] = 100;
+       alien_defs[CD_FIREFLY].chance[1] = 5;
+       alien_defs[CD_FIREFLY].collectChance = 100;
+       alien_defs[CD_FIREFLY].collectType = P_ANYTHING;
+       alien_defs[CD_FIREFLY].collectValue = 250;
+       alien_defs[CD_FIREFLY].flags = FL_WEAPCO;
+
+       // Uranus Boss
+       alien_defs[CD_URANUSBOSS].classDef = CD_URANUSBOSS;
+       alien_defs[CD_URANUSBOSS].AIType = AI_OFFENSIVE;
+       alien_defs[CD_URANUSBOSS].speed = 4;
+       alien_defs[CD_URANUSBOSS].maxShield = 750;
+       alien_defs[CD_URANUSBOSS].shield = 750;
+       alien_defs[CD_URANUSBOSS].imageIndex[0] = 41;
+       alien_defs[CD_URANUSBOSS].imageIndex[1] = 42;
+       alien_defs[CD_URANUSBOSS].weaponType[0] = W_SPREADSHOT;
+       alien_defs[CD_URANUSBOSS].weaponType[1] = W_DOUBLE_ROCKETS;
+       alien_defs[CD_URANUSBOSS].chance[0] = 100;
+       alien_defs[CD_URANUSBOSS].chance[1] = 5;
+       alien_defs[CD_URANUSBOSS].collectChance = 100;
+       alien_defs[CD_URANUSBOSS].collectType = P_ANYTHING;
+       alien_defs[CD_URANUSBOSS].collectValue = 500;
+       alien_defs[CD_URANUSBOSS].flags = FL_WEAPCO;
+
+       // Uranus Boss Wing 1
+       alien_defs[CD_URANUSBOSSWING1].classDef = CD_URANUSBOSSWING1;
+       alien_defs[CD_URANUSBOSSWING1].AIType = AI_OFFENSIVE;
+       alien_defs[CD_URANUSBOSSWING1].speed = 4;
+       alien_defs[CD_URANUSBOSSWING1].maxShield = 250;
+       alien_defs[CD_URANUSBOSSWING1].shield = 250;
+       alien_defs[CD_URANUSBOSSWING1].imageIndex[0] = 43;
+       alien_defs[CD_URANUSBOSSWING1].imageIndex[1] = 44;
+       alien_defs[CD_URANUSBOSSWING1].weaponType[0] = W_DOUBLE_ROCKETS;
+       alien_defs[CD_URANUSBOSSWING1].weaponType[1] = W_DOUBLE_ROCKETS;
+       alien_defs[CD_URANUSBOSSWING1].chance[0] = 5;
+       alien_defs[CD_URANUSBOSSWING1].chance[1] = 0;
+       alien_defs[CD_URANUSBOSSWING1].collectChance = 100;
+       alien_defs[CD_URANUSBOSSWING1].collectType = P_ANYTHING;
+       alien_defs[CD_URANUSBOSSWING1].collectValue = 250;
+       alien_defs[CD_URANUSBOSSWING1].flags = FL_WEAPCO | FL_IMMORTAL | FL_DAMAGEOWNER;
+
+       // Uranus Boss Wing 2
+       alien_defs[CD_URANUSBOSSWING2].classDef = CD_URANUSBOSSWING2;
+       alien_defs[CD_URANUSBOSSWING2].AIType = AI_OFFENSIVE;
+       alien_defs[CD_URANUSBOSSWING2].speed = 4;
+       alien_defs[CD_URANUSBOSSWING2].maxShield = 250;
+       alien_defs[CD_URANUSBOSSWING2].shield = 250;
+       alien_defs[CD_URANUSBOSSWING2].imageIndex[0] = 45;
+       alien_defs[CD_URANUSBOSSWING2].imageIndex[1] = 46;
+       alien_defs[CD_URANUSBOSSWING2].weaponType[0] = W_DOUBLE_ROCKETS;
+       alien_defs[CD_URANUSBOSSWING2].weaponType[1] = W_DOUBLE_ROCKETS;
+       alien_defs[CD_URANUSBOSSWING2].chance[0] = 5;
+       alien_defs[CD_URANUSBOSSWING2].chance[1] = 0;
+       alien_defs[CD_URANUSBOSSWING2].collectChance = 100;
+       alien_defs[CD_URANUSBOSSWING2].collectType = P_ANYTHING;
+       alien_defs[CD_URANUSBOSSWING2].collectValue = 250;
+       alien_defs[CD_URANUSBOSSWING2].flags = FL_WEAPCO | FL_IMMORTAL | FL_DAMAGEOWNER;
+}
+
+void aliens_init()
+{
+       FILE *fp;
+       char string[255];
+       int index;
+       int alienType;
+       int placeAttempt;
+       int barrierSpeed;
+
+       for (int i = 0 ; i < ALIEN_MAX ; i++)
+       {
+               aliens[i].active = false;
+               aliens[i].shield = -1;
+               aliens[i].flags = 0;
+       }
+
+       engine.targetIndex = -1;
+
+       strcpy(string, "");
+       barrierSpeed = 1;
+
+       sprintf(string, "data/aliens%d.dat", game.area);
+       fp = fopen(string, "rb");
+
+       if (fp != NULL)
+       {
+               while (fscanf(fp, "%d %d ", &index, &alienType) == 2)
+               {
+                       placeAttempt = 0;
+
+                       aliens[index] = alien_defs[alienType];
+                       aliens[index].owner = &aliens[index];
+                       aliens[index].target = &aliens[index];
+                       aliens[index].face = rand() % 2;
+                       aliens[index].active = true;
+
+                       /*
+                       we make 1000 attempts to place this enemy since it is required. If after
+                       1000 attempts we still haven't managed to place the alien, then it
+                       simply isn't going to happen and we will just exit the game. The chances
+                       of this happening are very very low!
+                       */
+                       while (true)
+                       {
+                               placeAttempt++;
+
+                               if (alien_place(&aliens[index]))
+                                       break;
+
+                               if (placeAttempt > 1000)
+                                       showErrorAndExit(2, "");
+                       }
+
+                       if (game.area == MISN_CERADSE)
+                               cargo_add(&aliens[index], P_CARGO);
+                       else if (game.area == MISN_NEROD)
+                               cargo_add(&aliens[index], P_PHOEBE);
+
+                       if (index == ALIEN_KLINE)
+                       {
+                               aliens[ALIEN_KLINE].target = &player;
+                       }
+
+                       if (aliens[index].classDef == CD_CLOAKFIGHTER)
+                       {
+                               aliens[index].active = false;
+                               aliens[index].maxShield = aliens[index].shield = 400;
+                               aliens[index].flags &= ~FL_RUNSAWAY;
+                               aliens[index].speed = 3;
+                       }
+
+                       if ((aliens[index].classDef == CD_MOBILE_RAY) && (index >= 11))
+                       {
+                               aliens[index].active = false;
+                       }
+
+                       if (aliens[index].classDef == CD_FIREFLY)
+                       {
+                               aliens[index].active = false;
+                       }
+
+                       if (aliens[index].classDef == CD_BARRIER)
+                       {
+                               aliens[index].owner = &aliens[ALIEN_BOSS];
+                               aliens[index].speed = barrierSpeed;
+                               barrierSpeed++;
+                       }
+
+                       if ((game.area == MISN_POSWIC) &&
+                               (aliens[index].classDef == CD_BOSS))
+                       {
+                               aliens[index].imageIndex[1] = 29;
+                               aliens[index].flags |= FL_IMMORTAL;
+                       }
+
+                       if (game.area == MISN_ELLESH)
+                               aliens[index].flags |= FL_HASMINIMUMSPEED;
+
+                       if (game.area == MISN_JUPITER)
+                       {
+                               aliens[index].flags = FL_WEAPCO;
+                               if (index == ALIEN_BOSS)
+                                       aliens[index].chance[1] = 5;
+                       }
+               }
+
+               fclose(fp);
+
+               if (game.area == MISN_MOEBO)
+               {
+                       aliens[ALIEN_BOSS].target = &player;
+                       aliens[ALIEN_BOSS].x = -screen->w / 2;
+                       aliens[ALIEN_BOSS].y = screen->h / 2;
+
+                       aliens[ALIEN_BOSS_PART1].owner = &aliens[ALIEN_BOSS];
+                       aliens[ALIEN_BOSS_PART1].target = &player;
+                       aliens[ALIEN_BOSS_PART1].dx = -25;
+                       aliens[ALIEN_BOSS_PART1].dy = -21;
+
+                       aliens[ALIEN_BOSS_PART2].owner = &aliens[ALIEN_BOSS];
+                       aliens[ALIEN_BOSS_PART2].target = &player;
+                       aliens[ALIEN_BOSS_PART2].dx = -20;
+                       aliens[ALIEN_BOSS_PART2].dy = 37;
+               }
+               else if ((game.area == MISN_ELAMALE) ||
+                       (game.area == MISN_FELLON))
+               {
+                       aliens[ALIEN_BOSS].target = &player;
+                       aliens[ALIEN_BOSS].x = -screen->w / 2;
+                       aliens[ALIEN_BOSS].y = screen->h / 2;
+
+                       aliens[ALIEN_BOSS_PART1].owner = &aliens[ALIEN_BOSS];
+                       aliens[ALIEN_BOSS_PART1].target = &player;
+                       aliens[ALIEN_BOSS_PART1].dx = 15;
+                       aliens[ALIEN_BOSS_PART1].dy = -22;
+
+                       aliens[ALIEN_BOSS_PART2].owner = &aliens[ALIEN_BOSS];
+                       aliens[ALIEN_BOSS_PART2].target = &player;
+                       aliens[ALIEN_BOSS_PART2].dx = 15;
+                       aliens[ALIEN_BOSS_PART2].dy = 22;
+
+                       aliens[ALIEN_BOSS_PART3].owner = &aliens[ALIEN_BOSS_PART1];
+                       aliens[ALIEN_BOSS_PART3].target = &player;
+                       aliens[ALIEN_BOSS_PART3].dx = -35;
+                       aliens[ALIEN_BOSS_PART3].dy = -12;
+
+                       aliens[ALIEN_BOSS_PART4].owner = &aliens[ALIEN_BOSS_PART2];
+                       aliens[ALIEN_BOSS_PART4].target = &player;
+                       aliens[ALIEN_BOSS_PART4].dx = -35;
+                       aliens[ALIEN_BOSS_PART4].dy = 20;
+
+                       if (game.area == MISN_FELLON)
+                       {
+                               aliens[ALIEN_BOSS].AIType = AI_EVASIVE;
+
+                               for (int i = 10 ; i < 15 ; i++)
+                               {
+                                       aliens[i].imageIndex[0] += 15;
+                                       aliens[i].imageIndex[1] += 15;
+
+                                       aliens[i].image[0] = shipShape[aliens[i].imageIndex[0]];
+                                       aliens[i].image[1] = shipShape[aliens[i].imageIndex[1]];
+                               }
+                       }
+               }
+               else if (game.area == MISN_URANUS)
+               {
+                       aliens[ALIEN_BOSS].target = &player;
+                       aliens[ALIEN_BOSS].x = -screen->w / 2;
+                       aliens[ALIEN_BOSS].y = screen->h / 2;
+
+                       aliens[ALIEN_BOSS_PART1].owner = &aliens[ALIEN_BOSS];
+                       aliens[ALIEN_BOSS_PART1].dy = 20;
+
+                       aliens[ALIEN_BOSS_PART2].owner = &aliens[ALIEN_BOSS];
+                       aliens[ALIEN_BOSS_PART2].dy = -16;
+               }
+       }
+}
+
+bool alien_add()
+{
+       int index = alien_getFreeIndex();
+
+       if ((index == -1) || (game.area == MISN_JUPITER) ||
+                       (game.area == MISN_VENUS))
+               return 0;
+
+       signed char *alienArray;
+       signed char numberOfAliens = 1;
+
+       alienArray = new signed char[8];
+
+       switch(game.area)
+       {
+               case MISN_START:
+               case MISN_HINSTAG:
+               case MISN_ELAMALE:
+                       numberOfAliens = 1;
+                       alienArray[0] = CD_DUALFIGHTER;
+                       break;
+               case MISN_HAIL:
+               case MISN_CERADSE:
+               case MISN_JOLDAR:
+               case MISN_MOEBO:
+                       numberOfAliens = 2;
+                       alienArray[0] = CD_DUALFIGHTER;
+                       alienArray[1] = CD_PROTOFIGHTER;
+                       break;
+               case MISN_NEROD:
+               case MISN_ALLEZ:
+                       numberOfAliens = 3;
+                       alienArray[0] = CD_DUALFIGHTER;
+                       alienArray[1] = CD_PROTOFIGHTER;
+                       alienArray[2] = CD_AIMFIGHTER;
+                       break;
+               case MISN_URUSOR:
+                       // This is the mission where you need to disable cargo ships.
+                       // Missiles are extremely bad in this mission, not because
+                       // of the damage they do to you, but because they tend to
+                       // accidentally destroy the cargo ships. Therefore, ships
+                       // with missiles (dual fighters and missile boats) are
+                       // excluded from this mission.
+                       numberOfAliens = 2;
+                       alienArray[0] = CD_PROTOFIGHTER;
+                       alienArray[1] = CD_AIMFIGHTER;
+                       break;
+               case MISN_DORIM:
+               case MISN_SIVEDI:
+                       numberOfAliens = 1;
+                       alienArray[0] = CD_ASTEROID;
+                       break;
+               case MISN_ODEON:
+               case MISN_FELLON:
+               case MISN_ALMARTHA:
+                       numberOfAliens = 4;
+                       alienArray[0] = CD_DUALFIGHTER;
+                       alienArray[1] = CD_PROTOFIGHTER;
+                       alienArray[2] = CD_MISSILEBOAT;
+                       alienArray[3] = CD_AIMFIGHTER;
+                       break;
+               case MISN_ELLESH:
+                       numberOfAliens = 2;
+                       alienArray[0] = CD_DUALFIGHTER;
+                       alienArray[1] = CD_MINER;
+                       break;
+               case MISN_SATURN:
+                       numberOfAliens = 2;
+                       alienArray[0] = CD_AIMFIGHTER;
+                       alienArray[1] = CD_DUALFIGHTER;
+                       break;
+               case MISN_MARS:
+                       numberOfAliens = 2;
+                       alienArray[0] = CD_ASTEROID;
+                       alienArray[1] = CD_ASTEROID2;
+                       break;
+               case MISN_EARTH:
+                       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 MISN_INTERCEPTION:
+                       numberOfAliens = 3;
+                       alienArray[0] = CD_DUALFIGHTER;
+                       alienArray[1] = CD_MISSILEBOAT;
+                       alienArray[2] = CD_AIMFIGHTER;
+                       if (game.system == 2)
+                       {
+                               numberOfAliens = 4;
+                               alienArray[3] = CD_PROTOFIGHTER;
+                       }
+                       break;
+               default:
+                       numberOfAliens = 1;
+                       alienArray[0] = CD_DUALFIGHTER;
+                       break;
+       }
+
+       signed char randEnemy = alienArray[rand() % numberOfAliens];
+
+       if ((game.area != MISN_DORIM) &&
+               (game.area != MISN_SIVEDI) &&
+               (game.area != MISN_MARS))
+       {
+               if ((game.system == 1) && (game.area == MISN_INTERCEPTION))
+               {
+                       if ((rand() % 5) == 0)
+                               randEnemy = CD_SLAVETRANSPORT;
+               }
+
+               if ((rand() % 6) == 0)
+                       randEnemy = CD_TRANSPORTSHIP;
+       }
+
+       delete[] alienArray;
+
+       aliens[index] = alien_defs[randEnemy];
+       aliens[index].active = true;
+       aliens[index].face = rand() % 2;
+       aliens[index].owner = &aliens[index]; // Most enemies will own themselves
+       aliens[index].target = &aliens[index];
+       aliens[index].thinktime = (50 + rand() % 50);
+       aliens[index].systemPower = aliens[index].maxShield;
+       aliens[index].deathCounter = 0 - (aliens[index].maxShield * 3);
+       aliens[index].hit = 0;
+
+       LIMIT(aliens[index].deathCounter, -250, 0);
+
+       // Attempts to place an alien. If it fails, the alien is deactivated.
+       for (int i = 0 ; i < 100 ; i++)
+       {
+               if (alien_place(&aliens[index]))
+                       break;
+               aliens[index].active = false;
+
+               return false;
+       }
+
+       if (aliens[index].classDef == CD_CARGOSHIP)
+               cargo_add(&aliens[index], P_CARGO);
+
+       if (aliens[index].classDef == CD_MOBILE_RAY)
+               aliens[index].shield = 25;
+
+       if (aliens[index].classDef == CD_ESCORT)
+               aliens[index].shield = 50;
+
+       aliens[index].dx = RANDRANGE(-2, 2);
+       aliens[index].dy = RANDRANGE(-2, 2);
+
+       aliens[index].ammo[0] = 0;
+
+       if (game.area == MISN_ELLESH)
+               aliens[index].flags |= FL_HASMINIMUMSPEED;
+
+       return true;
+}
+
+void alien_addDrone(object *hostAlien)
+{
+       int index = alien_getFreeIndex();
+
+       if (index == -1)
+               return;
+
+       aliens[index] = alien_defs[CD_DRONE];
+       aliens[index].active = true;
+       aliens[index].face = rand() % 2;
+       aliens[index].owner = &aliens[index]; // Most enemies will own themselves
+       aliens[index].target = &aliens[index];
+       aliens[index].thinktime = (50 + rand() % 50);
+       aliens[index].systemPower = aliens[index].maxShield;
+       aliens[index].deathCounter = 0 - (aliens[index].maxShield * 3);
+       aliens[index].hit = 0;
+
+       aliens[index].x = hostAlien->x + rand() % 50;
+       aliens[index].y = hostAlien->y + rand() % 50;
+}
+
+void alien_addSmallAsteroid(object *hostAlien)
+{
+       if (engine.missionCompleteTimer != 0)
+               return;
+
+       int index = -1;
+       int debris = RANDRANGE(1, 10);
+
+       for (int i = 0 ; i < debris ; i++)
+               bullet_add(&weapon[W_ROCKETS], hostAlien, 0, 0);
+
+       for (int i = 10 ; i < 20 ; i++)
+               if (!aliens[i].active)
+                       index = i;
+
+       if (index == -1)
+               return;
+
+       if ((rand() % 10) > 3)
+       {
+               aliens[index] = alien_defs[CD_ASTEROID2];
+               aliens[index].imageIndex[0] = aliens[index].imageIndex[1] = 39 + rand() % 2;
+               aliens[index].image[0] = shipShape[aliens[index].imageIndex[0]];
+               aliens[index].image[1] = shipShape[aliens[index].imageIndex[1]];
+       }
+       else
+       {
+               aliens[index] = alien_defs[CD_DRONE];
+       }
+
+       aliens[index].owner = &aliens[index]; // Most enemies will own themselves
+       aliens[index].target = &aliens[index];
+       aliens[index].thinktime = 1;
+       aliens[index].systemPower = aliens[index].maxShield;
+       aliens[index].deathCounter = 0 - (aliens[index].maxShield * 3);
+       aliens[index].hit = 0;
+
+       aliens[index].x = hostAlien->x;
+       aliens[index].y = hostAlien->y;
+       aliens[index].active = true;
+}
+
+void alien_addFriendly(int type)
+{
+       if (type != ALIEN_SID)
+               aliens[type] = alien_defs[CD_FRIEND];
+       else
+               aliens[type] = alien_defs[CD_SID];
+
+       aliens[type].owner = &aliens[type];
+       aliens[type].target = &aliens[type];
+       aliens[type].active = true;
+       aliens[type].x = RANDRANGE((screen->w / 2) - 150, (screen->w / 2) + 150);
+       aliens[type].y = RANDRANGE((screen->h / 2) - 150, (screen->h / 2) + 150);
+
+       if (type == ALIEN_PHOEBE)
+               aliens[type].classDef = CD_PHOEBE;
+
+       if (type == ALIEN_URSULA)
+               aliens[type].classDef = CD_URSULA;
+
+       // For the sake of it being the final battle :)
+       if (game.area == MISN_EARTH)
+               aliens[type].flags |= FL_IMMORTAL;
+}
+
+bool alien_place(object *alien)
+{
+       if (rand() % 2 == 0)
+               alien->x = RANDRANGE(screen->w, screen->w * 2);
+       else
+               alien->x = RANDRANGE(-screen->w, 0);
+
+       if (rand() % 2 == 0)
+               alien->y = RANDRANGE(screen->h, screen->h * 2);
+       else
+               alien->y = RANDRANGE(-screen->h, 0);
+
+       if (game.area == MISN_MARS)
+       {
+               alien->x = screen->w + RANDRANGE(0, 400);
+               alien->y = RANDRANGE(-screen->h / 3, (4 * screen->h) / 3);
+       }
+
+       for (int i = 0 ; i < ALIEN_MAX ; i++)
+       {
+               if ((aliens[i].owner != alien) && (aliens[i].shield > 0))
+               {
+                       if (collision(alien->x, alien->y, alien->image[0]->w,
+                                       alien->image[0]->h, aliens[i].x, aliens[i].y,
+                                       aliens[i].image[0]->w, aliens[i].image[0]->h))
+                               return false;
+               }
+       }
+
+       return true;
+}
+
+void alien_setAI(object *alien)
+{
+       // Make friendly craft generally concentrate on smaller fighters
+       if ((alien->flags & FL_FRIEND) && (alien->target == &aliens[ALIEN_BOSS]))
+       {
+               if ((rand() % 5) == 0)
+               {
+                       alien->target = alien;
+                       alien->thinktime = 0;
+                       return;
+               }
+       }
+
+       int i = rand() % 10;
+       float tx = alien->target->x;
+       float ty = alien->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 (alien->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
+               alien->dx = ((alien->x - tx) / ((300 / alien->speed) + rand() % 100));
+               alien->dy = ((alien->y - ty) / ((300 / alien->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));
+               alien->dx = ((alien->x - tx) / ((300 / alien->speed) + rand() % 100));
+               alien->dy = ((alien->y - ty) / ((300 / alien->speed) + rand() % 100));
+               return;
+       }
+       else
+       {
+               // Hang back
+               alien->dx = 0;
+               alien->dy = 0;
+               return;
+       }
+}
+
+void alien_setKlineAttackMethod(object *alien)
+{
+       if (alien->shield <= 500)
+       {
+               setRadioMessage(FACE_KLINE, "ENOUGH!! THIS ENDS NOW!!!", 1);
+               alien->weaponType[0] = W_AIMED_SHOT;
+               alien->weaponType[1] = W_MICRO_HOMING_MISSILES;
+               alien->flags |= FL_CANCLOAK;
+               alien->chance[0] = 100;
+               alien->chance[1] = 2;
+               alien->flags &= ~FL_CANNOTDIE;
+       }
+       else if (alien->shield <= 1000)
+       {
+               setRadioMessage(FACE_KLINE, "Your ability to stay alive irritates me!! Try dodging some of these!!", 1);
+               alien->weaponType[0] = W_DIRSHOCKMISSILE;
+               alien->weaponType[1] = W_DIRSHOCKMISSILE;
+               alien->chance[0] = 2;
+               alien->chance[1] = 2;
+               alien->flags |= FL_AIMS;
+       }
+       else
+       {
+               setRadioMessage(FACE_KLINE, "Very good, Bainfield. Now let's get a little more serious...", 1);
+               alien->weaponType[0] = W_SPREADSHOT;
+               alien->chance[1] = 40;
+       }
+}
+
+/*
+This AI is exclusively for Kline.
+*/
+void alien_setKlineAI(object *alien)
+{
+       // Weapon type change
+       if ((rand() % 3) == 0)
+       {
+               if (game.area != MISN_VENUS)
+               {
+                       alien->flags &= ~FL_AIMS;
+
+                       switch(rand() % 2)
+                       {
+                               case 0:
+                                       alien->weaponType[0] = W_TRIPLE_SHOT;
+                                       break;
+                               case 1:
+                                       alien->weaponType[0] = W_AIMED_SHOT;
+                                       alien->flags |= FL_AIMS;
+                                       break;
+                       }
+               }
+       }
+
+       alien->flags &= ~(FL_CIRCLES | FL_CONTINUOUS_FIRE | FL_DROPMINES);
+
+       switch(rand() % 10)
+       {
+               case 0:
+                       if ((alien->weaponType[0] != W_DIRSHOCKMISSILE) &&
+                                       (alien->weaponType[1] != W_MICRO_HOMING_MISSILES))
+                               alien->flags |= FL_CONTINUOUS_FIRE;
+                       alien->dx = ((alien->x - alien->target->x) /
+                               ((300 / alien->speed)  + rand() % 100));
+                       alien->dy = ((alien->y - alien->target->y) /
+                               ((300 / alien->speed)  + rand() % 100));
+                       break;
+               case 1:
+               case 2:
+                       // Kline only attacks then he is ready!
+                       if ((!(alien->flags & FL_NOFIRE)) &&
+                                       (game.area == MISN_MOEBO))
+                               alien->flags |= FL_DROPMINES;
+                       break;
+               case 3:
+               case 4:
+                       alien->flags |= FL_CIRCLES;
+                       break;
+               default:
+                       alien_setAI(alien);
+                       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 alien_searchForTarget(object *alien)
+{
+       int i;
+
+       if (alien->flags & FL_WEAPCO)
+       {
+               i = (rand() % 10);
+
+               if (i == 0)
+               {
+                       alien->target = &player;
+                       return;
+               }
+       }
+
+       i = rand() % ALIEN_MAX;
+
+       object *targetEnemy = &aliens[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 ((alien->classDef == CD_SID) && (game.area != MISN_EARTH))
+       {
+               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 (game.area != MISN_EARTH)
+       {
+               if ((alien->classDef == CD_PHOEBE) || (alien->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) && (alien->flags & FL_WEAPCO))
+               return;
+
+       if ((targetEnemy->flags & FL_FRIEND) && (alien->flags & FL_FRIEND))
+               return;
+
+       if (abs((int)alien->x - (int)alien->target->x) > 550)
+               return;
+
+       if (abs((int)alien->y - (int)alien->target->y) > 400)
+               return;
+
+       alien->target = targetEnemy;
+}
+
+/*
+Do various checks to see if the alien can fire at the target.
+*/
+int alien_checkTarget(object *alien)
+{
+       // No target
+       if (alien->target == alien)
+               return 0;
+
+       // Whilst firing a Ray, no other weapons can be fired!
+       if (alien->flags & FL_FIRERAY)
+               return 0;
+
+       // The target is on the same side as you!
+       if ((alien->flags & FL_WEAPCO) && (alien->target->flags & FL_WEAPCO))
+               return 0;
+       if ((alien->flags & FL_FRIEND) && (alien->target->flags & FL_FRIEND))
+               return 0;
+
+       // You're facing the wrong way
+       if ((alien->face == 0) && (alien->target->x < alien->x))
+               return 0;
+       if ((alien->face == 1) && (alien->target->x > alien->x))
+               return 0;
+
+       // Slightly more than half a screen away from you
+       if (abs((int)alien->x - (int)alien->target->x) > 550)
+               return 0;
+
+       if ((alien->flags & FL_AIMS) || (alien->flags & FL_CONTINUOUS_FIRE))
+               return 1;
+
+       // Not at the correct vertical height
+       if ((alien->y < alien->target->y - 15) ||
+                       (alien->y > alien->target->y + alien->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 alien_enemiesInFront(object *alien)
+{
+       for (int i = 0 ; i < ALIEN_MAX ; i++)
+       {
+               if ((alien != &aliens[i]) && (aliens[i].flags & FL_WEAPCO) &&
+                       (aliens[i].shield > 0))
+               {
+                       if ((alien->y > aliens[i].y - 15) &&
+                               (alien->y < aliens[i].y + aliens[i].image[0]->h + 15))
+                       {
+                               if ((alien->face == 1) && (aliens[i].x < alien->x))
+                                       return 1;
+                               if ((alien->face == 0) && (aliens[i].x > alien->x))
+                                       return 1;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+void alien_move(object *alien)
+{
+       bool checkCollisions;
+
+       if ((alien->flags & FL_LEAVESECTOR) || (alien->shield < 1))
+               checkCollisions = false;
+       else
+               checkCollisions = true;
+
+       if (alien->owner == alien)
+       {
+               if (alien->flags & FL_CIRCLES)
+               {
+                       if (alien->face == 0)
+                       {
+                               alien->dx += 0.02;
+                               alien->dy += 0.02;
+                       }
+                       else
+                       {
+                               alien->dx -= 0.02;
+                               alien->dy -= 0.02;
+                       }
+
+                       alien->x -= (sinf(alien->dx) * 4);
+                       alien->y -= (cosf(alien->dy) * 4);
+               }
+               else
+               {
+                       alien->x -= alien->dx;
+                       alien->y -= alien->dy;
+               }
+       }
+
+       if (checkCollisions)
+       {
+               for (int i = 0 ; i < ALIEN_MAX ; i++)
+               {
+                       if ((alien->flags & FL_LEAVESECTOR) ||
+                               (alien->classDef == CD_DRONE) ||
+                               (alien->classDef == CD_ASTEROID2) ||
+                               (alien->owner == aliens[i].owner) ||
+                               (alien->owner->owner == aliens[i].owner) ||
+                               (aliens[i].shield < 1))
+                       {
+                               continue;
+                       }
+
+                       if (collision(alien, &aliens[i]))
+                       {
+                               if ((aliens[i].classDef == CD_BARRIER) &&
+                                       (aliens[i].owner != alien))
+                               {
+                                       alien->shield--;
+                                       alien->hit = 3;
+                                       alien->dx *= -1;
+                                       alien->dy *= -1;
+                                       audio_playSound(SFX_HIT, alien->x);
+                               }
+                       }
+               }
+       }
+
+       // Handle a collision with the player
+       if ((player.shield > 0) && (alien->shield > 0) && (checkCollisions))
+       {
+               if (collision(alien, &player))
+               {
+                       if (alien->classDef == CD_ASTEROID)
+                       {
+                               if (!engine.cheatShield)
+                                       player.shield -= alien->shield;
+                               alien->shield = 0;
+                               audio_playSound(SFX_EXPLOSION, alien->x);
+                               player.hit = 5;
+                               audio_playSound(SFX_HIT, player.x);
+                       }
+
+                       if (alien->classDef == CD_ASTEROID2)
+                       {
+                               if (!engine.cheatShield)
+                                       player.shield -= alien->shield;
+                               alien->shield = 0;
+                               audio_playSound(SFX_EXPLOSION, alien->x);
+                               player.hit = 5;
+                               audio_playSound(SFX_HIT, player.x);
+                       }
+
+                       if (alien->classDef == CD_BARRIER)
+                       {
+                               if (!engine.cheatShield)
+                                       player.shield--;
+                               player.hit = 5;
+                               audio_playSound(SFX_HIT, player.x);
+                       }
+               }
+       }
+}
+
+/*
+Fill in later...
+*/
+void alien_destroy(object *alien, object *attacker)
+{
+       audio_playSound(SFX_EXPLOSION, alien->x);
+
+       if (alien->flags & FL_FRIEND)
+       {
+               if (alien->classDef == CD_PHOEBE)
+                       game.wingMate1Ejects++;
+               else if (alien->classDef == CD_URSULA)
+                       game.wingMate2Ejects++;
+
+               // Phoebe cannot eject on the rescue mission
+               if (game.area != MISN_NEROD)
+               {
+                       if ((alien->classDef == CD_PHOEBE) || (alien->classDef == CD_URSULA))
+                               setInfoLine(">> Ally has ejected! <<\n", FONT_RED);
+                       else
+                               setInfoLine(">> Friendly craft has been destroyed! <<\n", FONT_RED);
+               }
+       }
+
+       if (attacker != NULL)
+       {
+               if (attacker == &player)
+               {
+                       game.totalKills++;
+               }
+               else if (attacker->classDef == CD_PHOEBE)
+               {
+                       game.wingMate1Kills++;
+               }
+               else if (attacker->classDef == CD_URSULA)
+               {
+                       game.wingMate2Kills++;
+               }
+               else
+               {
+                       game.totalOtherKills++;
+               }
+
+               if ((attacker->classDef == CD_PHOEBE) || (attacker->classDef == CD_URSULA))
+               {
+                       if ((rand() % 8) == 0)
+                       {
+                               getKillMessage(attacker);
+                       }
+               }
+       }
+
+       updateMissionRequirements(M_DESTROY_TARGET_TYPE, alien->classDef, 1);
+       updateMissionRequirements(M_PROTECT_TARGET, alien->classDef, 1);
+
+       if (rand() % 100 <= alien->collectChance)
+       {
+               unsigned char value;
+
+               if ((rand() % 10) == 0)
+                       alien->collectValue *= 2;
+
+               while (alien->collectValue > 0)
+               {
+                       value = (10 + (rand() % alien->collectValue));
+                       if (value > alien->collectValue)
+                               value = alien->collectValue;
+                       collectable_add(alien->x, alien->y, alien->collectType, value, 600);
+                       alien->collectValue -= value;
+               }
+       }
+
+       // Make it explode immediately
+       if (alien->classDef == CD_ASTEROID)
+       {
+               alien->shield = -999;
+       }
+
+       if ((alien->classDef == CD_KRASS) && (attacker == &player))
+               setRadioMessage(FACE_CHRIS, "The name's CHRIS, old-timer.", 1);
+
+       if (alien->classDef == CD_KLINE)
+       {
+               setRadioMessage(FACE_KLINE, "It was an honor... to have fought you...", 1);
+               alien->dx = alien->dy = 0;
+               alien->shield = -150;
+       }
+}
+
+void alien_hurt(object *alien, object *attacker, int damage, bool ion)
+{
+       if (ion)
+               alien->systemPower -= damage;
+       else
+               alien->shield -= damage;
+
+       // Chain reaction damage if needed
+       if ((alien->owner != alien) && (alien->flags & FL_DAMAGEOWNER))
+       {
+               alien_hurt(alien->owner, attacker, damage, ion);
+       }
+
+       if (alien->classDef == CD_KLINE)
+       {
+               if (game.area == MISN_ELAMALE)
+               {
+                       if ((alien->shield <= alien->maxShield - 500) &&
+                               !(alien->flags & FL_LEAVESECTOR))
+                       {
+                               alien->flags |= FL_LEAVESECTOR;
+                               alien->flags &= ~FL_CIRCLES;
+                               setRadioMessage(FACE_KLINE, "Seems I underestimated you, Bainfield. We'll meet again!", 1);
+                       }
+               }
+               else if (game.area == MISN_EARTH)
+               {
+                       if ((alien->shield <= alien->maxShield - 750) &&
+                               !(alien->flags & FL_LEAVESECTOR))
+                       {
+                               alien->flags |= FL_LEAVESECTOR;
+                               alien->flags &= ~FL_CIRCLES;
+                               setRadioMessage(FACE_SID, "Chris, Kethlan is getting away!", 1);
+                       }
+               }
+               else if (game.area == MISN_VENUS)
+               {
+                       if (alien->shield + damage > 1500 &&
+                                       alien->shield <= 1500)
+                               alien_setKlineAttackMethod(alien);
+                       else if (alien->shield + damage > 1000 &&
+                                       alien->shield <= 1000)
+                               alien_setKlineAttackMethod(alien);
+                       else if (alien->shield + damage > 500 &&
+                                       alien->shield <= 500)
+                               alien_setKlineAttackMethod(alien);
+               }
+               else
+               {
+                       if ((alien->shield <= alien->maxShield - 100) &&
+                               !(alien->flags & FL_LEAVESECTOR))
+                       {
+                               alien->flags |= FL_LEAVESECTOR;
+                               alien->flags &= ~FL_CIRCLES;
+                       }
+               }
+       }
+
+       if ((alien->flags & FL_RUNSAWAY) && ((rand() % 50) == 0))
+       {
+               alien->flags |= FL_LEAVESECTOR;
+       }
+
+       audio_playSound(SFX_HIT, alien->x);
+       if (alien->AIType == AI_EVASIVE)
+               alien->thinktime = 0;
+       
+       if (alien->shield < 1)
+               alien_destroy(alien, attacker);
+
+       if (alien->systemPower < 1)
+       {
+               if (!(alien->flags & FL_DISABLED))
+               {
+                       alien->flags += FL_DISABLED;
+                       updateMissionRequirements(M_DISABLE_TARGET,
+                               alien->classDef, 1);
+               }
+
+               alien->systemPower = 0;
+               if (alien->classDef == CD_KLINE)
+                       alien->systemPower = alien->maxShield;
+       }
+}
diff --git a/src/alien.h b/src/alien.h
new file mode 100644 (file)
index 0000000..2711afa
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ALIEN_H
+#define ALIEN_H
+
+extern object alien_defs[CD_MAX];
+extern object aliens[ALIEN_MAX];
+
+void alien_defs_init();
+void aliens_init();
+bool alien_add();
+void alien_addDrone(object *hostAlien);
+void alien_addSmallAsteroid(object *hostAlien);
+void alien_addFriendly(int type);
+bool alien_place(object *alien);
+void alien_setAI(object *alien);
+void alien_setKlineAttackMethod(object *alien);
+void alien_setKlineAI(object *alien);
+void alien_searchForTarget(object *alien);
+int alien_checkTarget(object *alien);
+int alien_enemiesInFront(object *alien);
+void alien_move(object *alien);
+void alien_destroy(object *alien, object *attacker);
+void alien_hurt(object *alien, object *attacker, int damage, bool ion);
+
+#endif
diff --git a/src/audio.cpp b/src/audio.cpp
new file mode 100644 (file)
index 0000000..8bfcf68
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+static Mix_Chunk *sound[SFX_MAX];
+static Mix_Music *music = NULL;
+
+void audio_loadSounds()
+{
+       sound[SFX_EXPLOSION] = Mix_LoadWAV("sound/explode.ogg");
+       sound[SFX_HIT] = Mix_LoadWAV("sound/explode2.ogg");
+       sound[SFX_DEATH] = Mix_LoadWAV("sound/maledeath.ogg");
+       sound[SFX_MISSILE] = Mix_LoadWAV("sound/missile.ogg");
+       sound[SFX_PLASMA] = Mix_LoadWAV("sound/plasma.ogg");
+       sound[SFX_CLOCK] = Mix_LoadWAV("sound/clock.ogg");
+       sound[SFX_FLY] = Mix_LoadWAV("sound/flyby.ogg");
+       sound[SFX_ENERGYRAY] = Mix_LoadWAV("sound/beamLaser.ogg");
+       sound[SFX_PICKUP] = Mix_LoadWAV("sound/item.ogg");
+       sound[SFX_SHIELDUP] = Mix_LoadWAV("sound/shield.ogg");
+       sound[SFX_CLOAK] = Mix_LoadWAV("sound/cloak.ogg");
+       sound[SFX_DEBRIS] = Mix_LoadWAV("sound/explode3.ogg");
+       sound[SFX_DEBRIS2] = Mix_LoadWAV("sound/explode4.ogg");
+       sound[SFX_LASER] = Mix_LoadWAV("sound/laser.ogg");
+       sound[SFX_PLASMA2] = Mix_LoadWAV("sound/plasma2.ogg");
+       sound[SFX_PLASMA3] = Mix_LoadWAV("sound/plasma3.ogg");
+}
+
+void audio_playSound(int sid, float x)
+{
+       if ((!engine.useSound) || (!engine.useAudio))
+               return;
+
+       int channel = -1;
+       static int freechannel = 4;
+
+       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:
+                       channel = -1;   
+                       break;
+               case SFX_PLASMA:
+               case SFX_LASER:
+                       channel = 0;
+                       break;
+               case SFX_ENERGYRAY:
+               case SFX_MISSILE:
+                       channel = 1;
+                       break;
+               case SFX_HIT:
+                       channel = 2;
+                       break;
+               case SFX_EXPLOSION:
+               case SFX_DEBRIS:
+               case SFX_DEBRIS2:
+                       channel = 3;
+                       break;
+       }
+
+       if(channel == -1) {
+               channel = freechannel++;
+               if(freechannel >= 8)
+                       freechannel = 4;
+       }
+
+       int angle = atanf((x - (screen->w / 2)) / (screen->w / 2)) * 180 / M_PI;
+       int attenuation = fabsf(x - (screen->w / 2)) / 40;
+
+       if (angle < 0)
+               angle += 360;
+
+       if (attenuation > 255)
+               attenuation = 255;
+
+       Mix_SetPosition(channel, angle, attenuation);
+       Mix_PlayChannel(channel, sound[sid], 0);
+}
+
+void audio_haltMusic()
+{
+       if (Mix_PlayingMusic())
+       {
+               Mix_ResumeMusic();
+               Mix_HaltMusic();
+       }
+
+       if (music != NULL)
+       {
+               Mix_FreeMusic(music);
+               music = NULL;
+       }
+}
+
+void audio_pauseMusic()
+{
+       if (Mix_PlayingMusic() && !Mix_PausedMusic())
+               Mix_PauseMusic();
+}
+
+void audio_resumeMusic()
+{
+       Mix_ResumeMusic();
+}
+
+void audio_setMusicVolume(int volume)
+{
+       if (engine.useMusic && engine.useAudio)
+               Mix_VolumeMusic(volume);
+}
+
+void audio_playMusic(const char *filename, int loops)
+{
+       if (engine.useMusic && engine.useAudio)
+       {
+               audio_haltMusic();
+               music = Mix_LoadMUS(filename);
+               audio_setMusicVolume(100);
+               Mix_PlayMusic(music, loops);
+       }
+}
+
+void audio_playRandomTrack()
+{
+       if ((!engine.useMusic) || (!engine.useAudio))
+               return;
+
+       int tracks = 4;
+       char track[][64] = {
+               "music/railjet_short.ogg", "music/space_dimensions.ogg",
+               "music/frozen_jam.ogg", "music/sound_and_silence.ogg"
+       };
+
+       switch(game.area)
+       {
+               case MISN_START:
+                       audio_playMusic("music/railjet_short.ogg", -1);
+                       break;
+               case MISN_MOEBO:
+               case MISN_ELAMALE:
+               case MISN_ELLESH:
+               case MISN_EARTH:
+                       audio_playMusic("music/orbital_colossus.ogg", -1);
+                       break;
+               case MISN_VENUS:
+                       audio_playMusic("music/RE.ogg", -1);
+                       break;
+               default:
+                       audio_playMusic(track[rand() % tracks], -1);
+       }
+}
+
+void audio_free()
+{
+       for (int i = 0 ; i < SFX_MAX ; i++)
+       {
+               if (sound[i] != NULL)
+               {
+                       Mix_FreeChunk(sound[i]);
+                       sound[i] = NULL;
+               }
+       }
+
+       if (music != NULL)
+       {
+               Mix_FreeMusic(music);
+               music = NULL;
+       }
+}
diff --git a/src/audio.h b/src/audio.h
new file mode 100644 (file)
index 0000000..8351253
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef AUDIO_H
+#define AUDIO_H
+
+void audio_loadSounds();
+void audio_playSound(int sid, float x);
+void audio_haltMusic();
+void audio_pauseMusic();
+void audio_resumeMusic();
+void audio_setMusicVolume(int volume);
+void audio_playMusic(const char *filename, int loops);
+void audio_playRandomTrack();
+void audio_free();
+
+#endif
diff --git a/src/bullet.cpp b/src/bullet.cpp
new file mode 100644 (file)
index 0000000..e660fb2
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+void bullet_add(object *theWeapon, object *attacker, int y, int dy)
+{
+       object *bullet;
+       signed char imageIndex;
+       int tempX, tempY, steps;
+
+       bullet = new object;
+
+       if (attacker == &player)
+               game.shots++;
+
+       bullet->next = NULL;
+       bullet->active = true;
+       bullet->x = attacker->x + (attacker->image[0]->w / 2) -
+               (theWeapon->image[0]->w * 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 ((game.area == MISN_ELLESH) ||
+                               (game.area == MISN_MARS))
+                       bullet->dx += fabsf(engine.ssx + engine.smx);
+       }
+       else
+       {
+               bullet->dx = -theWeapon->speed;
+       }
+
+       if (bullet->flags & WF_VARIABLE_SPEED)
+       {
+               bullet->dx = RANDRANGE(100, 200) / 10;
+               if (attacker->face == 1)
+                       bullet->dx = -bullet->dx;
+       }
+
+       bullet->dy = dy;
+
+       if (bullet->flags & WF_SCATTER)
+       {
+               bullet->dy = RANDRANGE(-200, 200) / 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)
+       {
+               if (game.difficulty == DIFFICULTY_ORIGINAL)
+                       bullet->damage = attacker->ammo[1];
+               else
+                       bullet->damage = attacker->ammo[1] / 2;
+
+               if (bullet->damage < 15)
+               {
+                       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)fabsf(attacker->target->x - attacker->x);
+               tempY = (int)fabsf(attacker->target->y - attacker->y);
+               steps = MAX(tempX, tempY);
+
+               if (steps < 12)
+                       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 = RANDRANGE(-20, 20);
+               bullet->dy = RANDRANGE(-20, 20);
+               bullet->image[0] = shape[4];
+       }
+
+       engine.bulletTail->next = bullet;
+       engine.bulletTail = bullet;
+}
+
+/*
+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 *bullet_getTarget(object *bullet)
+{
+       int i;
+
+       if (bullet->owner->flags & FL_WEAPCO)
+       {
+               i = (rand() % 10);
+
+               if (i < 1)
+                       return &player;
+       }
+
+       i = rand() % ALIEN_MAX;
+
+       if ((aliens[i].shield < 1) || (!aliens[i].active))
+               return NULL;
+
+       if (aliens[i].flags & FL_IMMORTAL)
+               return NULL;
+
+       if ((bullet->owner->flags & FL_WEAPCO) && (aliens[i].flags & FL_WEAPCO))
+               return NULL;
+
+       if ((bullet->owner->flags & FL_FRIEND) && (aliens[i].flags & FL_FRIEND))
+               return NULL;
+
+       if (abs((int)bullet->x - (int)aliens[i].target->x) > 800)
+               return NULL;
+
+       if (abs((int)bullet->y - (int)aliens[i].target->y) > 200)
+               return NULL;
+
+       return &aliens[i];
+}
diff --git a/src/bullet.h b/src/bullet.h
new file mode 100644 (file)
index 0000000..9489383
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef BULLETS_H
+#define BULLETS_H
+
+void bullet_add(object *theWeapon, object *attacker, int y, int dy);
+object *bullet_getTarget(object *bullet);
+
+#endif
diff --git a/src/cargo.cpp b/src/cargo.cpp
new file mode 100644 (file)
index 0000000..b754ae6
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+object cargo[MAX_CARGO];
+
+void cargo_init()
+{
+       for (int i = 0 ; i < MAX_CARGO ; i++)
+       {
+               cargo[i].active = false;
+               cargo[i].owner = NULL;
+       }
+}
+
+/*
+* I think you all know what this does by now! ;)
+*/
+static int cargo_get()
+{
+       for (int i = 0 ; i < MAX_CARGO ; i++)
+       {
+               if (!cargo[i].active)
+                       return i;
+       }
+
+       return -1;
+}
+
+object *cargo_add(object *owner, int cargoType)
+{
+       int index = cargo_get();
+
+       if (index == -1)
+               return NULL;
+
+       cargo[index].active = true;
+       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] = shape[32];
+       if (cargoType == P_PHOEBE)
+               cargo[index].image[0] = shipShape[20];
+
+       return &cargo[index];
+}
+
+void cargo_becomeCollectable(int i)
+{
+       if (cargo[i].collectType != P_PHOEBE)
+       {
+               collectable_add(cargo[i].x, cargo[i].y, cargo[i].collectType, 1, 600);
+       }
+       else
+       {
+               aliens[ALIEN_PHOEBE].active = true;
+               aliens[ALIEN_PHOEBE].x = cargo[i].x;
+               aliens[ALIEN_PHOEBE].y = cargo[i].y;
+               setRadioMessage(FACE_PHOEBE, "Thanks!! Watch out, WEAPCO! Phoebe's loose and she's ANGRY!!!", 1);
+       }
+
+       cargo[i].active = false;
+}
diff --git a/src/cargo.h b/src/cargo.h
new file mode 100644 (file)
index 0000000..5d1c3bf
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _CARGO_H_
+#define _CARGO_H_
+
+extern object cargo[MAX_CARGO];
+
+void cargo_init();
+object *cargo_add(object *owner, int cargoType);
+void cargo_becomeCollectable(int i);
+
+#endif
diff --git a/src/collectable.cpp b/src/collectable.cpp
new file mode 100644 (file)
index 0000000..cf0c155
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+/*
+Create a new collectable item based on supplied arguments.
+*/
+void collectable_add(float x, float y, int type, int value, int life)
+{
+       int r;
+
+       if (type == P_ANYTHING)
+       {
+               type = P_CASH;
+
+               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;
+
+               if ((game.difficulty == DIFFICULTY_NIGHTMARE) ||
+                       ((game.difficulty != DIFFICULTY_EASY) &&
+                               (game.difficulty != DIFFICULTY_ORIGINAL) &&
+                               ((game.area == MISN_MOEBO) ||
+                                       (game.area == MISN_ELAMALE) ||
+                                       (game.area == MISN_ELLESH) ||
+                                       (game.area == MISN_EARTH))))
+               {
+                       // Deny the Super Charge in Nightmare difficulty, and on bosses.
+                       r = rand() % 59;
+               }
+               else
+                       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 = MAX(value, 1);
+
+       if (value == 0)
+               return; // don't bother!
+
+       // No point in giving the player plasma ammo if the weapons aren't
+       // upgraded! Give them money instead.
+       if (type == P_PLASMA_AMMO)
+       {
+               if ((weapon[W_PLAYER_WEAPON].reload[0] >= rate2reload[game.minPlasmaRate]) &&
+                       (weapon[W_PLAYER_WEAPON].ammo[0] <= game.minPlasmaOutput) &&
+                       (weapon[W_PLAYER_WEAPON].damage <= game.minPlasmaDamage))
+               {
+                       type = P_CASH;
+               }
+       }
+
+       // 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;
+               }
+       }
+
+       // Shield bonus is useless in Nightmare difficulty; give cash instead.
+       if (type == P_SHIELD)
+       {
+               if (game.difficulty == DIFFICULTY_NIGHTMARE)
+               {
+                       type = P_CASH;
+               }
+       }
+
+       if (game.difficulty == DIFFICULTY_ORIGINAL)
+       {
+               // Cash is just rare in the original game. You can still grind,
+               // just not as much.
+               if ((game.area == MISN_INTERCEPTION) && (type == P_CASH))
+               {
+                       if (rand() % 10 > 0)
+                               return;
+               }
+       }
+       else
+       {
+               // No cash or ammo on interceptions. Completely stops grinding.
+               if ((game.area == MISN_INTERCEPTION) &&
+                       ((type == P_CASH) || (type == P_PLASMA_AMMO) || (type == P_ROCKET)))
+               {
+                       return;
+               }
+       }
+
+       collectables *collectable = new collectables;
+
+       collectable->next = NULL;
+       collectable->active = true;
+       collectable->x = x;
+       collectable->y = y;
+
+       collectable->dx = RANDRANGE(-100, 100);
+       if (collectable->dx != 0)
+               collectable->dx /= 100;
+
+       collectable->dy = RANDRANGE(-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 = shape[24];
+                       break;
+
+               case P_ROCKET:
+                       collectable->image = shape[49];
+                       break;
+
+               case P_PLASMA_AMMO:
+                       collectable->image = shape[25];
+                       break;
+
+               case P_SHIELD:
+                       collectable->image = shape[26];
+                       break;
+
+               case P_PLASMA_SHOT:
+                       collectable->image = shape[27];
+                       break;
+
+               case P_PLASMA_RATE:
+                       collectable->image = shape[28];
+                       break;
+
+               case P_PLASMA_DAMAGE:
+                       collectable->image = shape[29];
+                       break;
+
+               case P_CARGO:
+                       collectable->image = shape[32];
+                       break;
+
+               case P_SUPER:
+                       collectable->image = shape[50];
+                       break;
+
+               case P_MINE:
+                       collectable->image = shape[31];
+                       break;
+
+               case P_SLAVES:
+               case P_ESCAPEPOD:
+                       collectable->image = shape[45];
+                       break;
+
+               case P_ORE:
+                       collectable->image = shape[46 + rand() % 3];
+                       break;
+       }
+
+       engine.collectableTail->next = collectable;
+       engine.collectableTail = collectable;
+}
+
+void collectable_explode(collectables *collectable)
+{
+       if ((collectable->x >= 0) && (collectable->x <= screen->w) &&
+                       (collectable->y >= 0) && (collectable->y <= screen->h))
+               audio_playSound(SFX_EXPLOSION, collectable->x);
+
+       for (int i = 0 ; i < 10 ; i++)
+               explosion_add(collectable->x + rand() % 25 - rand() % 25,
+                       collectable->y + rand() % 25 - rand() % 25, E_BIG_EXPLOSION);
+
+       player_checkShockDamage(collectable->x, collectable->y);
+}
diff --git a/src/collectable.h b/src/collectable.h
new file mode 100644 (file)
index 0000000..343ae1a
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef COLLECTABLE_H
+#define COLLECTABLE_H
+
+void collectable_add(float x, float y, int type, int value, int life);
+void collectable_explode(collectables *collectable);
+
+#endif
diff --git a/src/defs.h b/src/defs.h
new file mode 100644 (file)
index 0000000..c66b4aa
--- /dev/null
@@ -0,0 +1,438 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012 Guus Sliepen
+Copyright (C) 2012, 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef DEFS_H
+#define DEFS_H
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define LIMIT(x, a, b) x = ((x) < (b) ? ((x) > (a) ? (x) : (a)) : (b))
+#define LIMIT_ADD(x, y, a, b) x = (((x) + (y)) < (b) ? \
+       (((x) + (y)) > (a) ? \
+               ((x) + (y)) : (a)) : (b))
+#define WRAP_ADD(x, y, a, b) x = (((x) + (y)) + \
+       ((x) + (y) < (a) ? ((b) - (a)) : 0) + \
+       ((x) + (y) > (b) ? ((a) - (b)) : 0))
+#define RANDRANGE(x, y) (((x) < (y)) ? ((x) + (rand() % (1 + (y) - (x)))) : (x))
+
+// ALL
+#define NONE 0
+
+// Compile-time options
+#ifndef VERSION
+#define VERSION "???"
+#endif
+
+#ifndef DATADIR
+#define DATADIR "."
+#endif 
+
+#ifndef PATH_MAX
+#define PATH_MAX       4096
+#endif
+
+// 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 32768L // Kline can circle around
+#define FL_CONTINUOUS_FIRE 65536L // Go absolutely nutts(!)
+#define FL_DEPLOYDRONES 131072L // Deploys small drone - Used by Boss 2
+#define FL_CANCLOAK 262144L
+#define FL_ISCLOAKED 524288L
+#define FL_ACTIVATE 1048576L
+#define FL_HASMINIMUMSPEED 2097152L
+#define FL_FIRELASER 4194304L
+
+// Explosions
+#define E_SMALL_EXPLOSION 4
+#define E_BIG_EXPLOSION 8
+#define E_SMOKE 12
+#define E_TINY_EXPLOSION 16
+#define E_ELECTRICAL 20
+
+// Weapon flags
+#define WF_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
+
+#define MAX_WEAPONS 20
+#define MAX_SHAPES 100
+#define MAX_SHIPSHAPES 120
+#define MAX_TEXTSHAPES 150
+#define MAX_FONTSHAPES 6
+#define MAX_SHOPSHAPES 6
+#define MAX_CARGO 20
+
+#define SHIP_HIT_INDEX 60
+
+// AI Types
+enum {
+
+       AI_NORMAL = 1,
+       AI_DEFENSIVE,
+       AI_OFFENSIVE,
+       AI_EVASIVE,
+       AI_WANDER
+};
+
+// These are for Alien *indexes* NOT classdefs!!
+enum {
+
+       ALIEN_BOSS_PART4 = 10,
+       ALIEN_BOSS_PART3 = 11,
+       ALIEN_BOSS_PART2 = 12,
+       ALIEN_BOSS_PART1 = 13,
+       ALIEN_BOSS = 14,
+       ALIEN_KLINE,
+       ALIEN_PHOEBE,
+       ALIEN_URSULA,
+       ALIEN_SID,
+       ALIEN_GOODTRANSPORT = 19,
+       ALIEN_MAX = 25
+};
+
+// 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
+};
+
+// 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
+};
+
+// Mission types
+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,
+       CD_MAX,
+
+       // Some special ones
+       CD_ANY = 100,
+       CD_BOSS,
+       CD_PHOEBE,
+       CD_URSULA
+};
+
+// Text shapes
+enum {
+
+       TS_PRESENTS,
+       TS_AN_SDL_GAME,
+       TS_START_NEW_GAME,
+       TS_LOAD_GAME,
+       TS_CONTINUE_CURRENT_GAME,
+       TS_OPTIONS,
+       TS_CHEAT_OPTIONS,
+       TS_QUIT,
+       TS_SOUND,
+       TS_MUSIC,
+       TS_FULLSCREEN,
+       TS_AUTOPAUSE,
+       TS_BACK_TO_MAIN_MENU,
+       TS_SAVESLOT_0,
+       TS_SAVESLOT_1,
+       TS_SAVESLOT_2,
+       TS_SAVESLOT_3,
+       TS_SAVESLOT_4,
+       TS_SAVESLOT_5,
+       TS_UNLIMITED_SHIELD,
+       TS_UNLIMITED_AMMO,
+       TS_UNLIMITED_CASH,
+       TS_UNLIMITED_TIME,
+       TS_START_GAME,
+       TS_DIFFICULTY
+};
+
+// Menu types
+enum {
+
+       MENU_MAIN,
+       MENU_DIFFICULTY,
+       MENU_LOAD,
+       MENU_OPTIONS,
+       MENU_CHEAT
+};
+
+// Shop items
+enum {
+
+       SHOP_PLASMA_MAX_OUTPUT,
+       SHOP_PLASMA_MAX_DAMAGE,
+       SHOP_PLASMA_MAX_RATE,
+       SHOP_PLASMA_MIN_OUTPUT,
+       SHOP_PLASMA_MIN_DAMAGE,
+       SHOP_PLASMA_MIN_RATE,
+       SHOP_PLASMA_AMMO,
+       SHOP_ROCKET_AMMO,
+       SHOP_PLASMA_MAX_AMMO,
+       SHOP_ROCKET_MAX_AMMO,
+       SHOP_DOUBLE_ROCKETS,
+       SHOP_MICRO_ROCKETS,
+       SHOP_LASER,
+       SHOP_HOMING_MISSILE,
+       SHOP_CHARGER,
+       SHOP_DOUBLE_HOMING_MISSILES,
+       SHOP_MICRO_HOMING_MISSILES,
+       MAX_SHOPITEMS
+};
+
+// 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,
+       SFX_MAX
+};
+
+// Sections
+enum {
+
+       SECTION_TITLE,
+       SECTION_INTERMISSION,
+       SECTION_GAME
+};
+
+// Faces
+enum {
+
+       FACE_CHRIS = 90,
+       FACE_SID,
+       FACE_KRASS,
+       FACE_KLINE,
+       FACE_PHOEBE,
+       FACE_URSULA,
+       FACE_CREW
+};
+
+// Missions
+enum {
+
+       MISN_START,
+       MISN_HAIL,
+       MISN_CERADSE,
+       MISN_HINSTAG,
+       MISN_JOLDAR,
+       MISN_MOEBO,
+       MISN_RESCUESLAVES,
+       MISN_NEROD,
+       MISN_ALLEZ,
+       MISN_URUSOR,
+       MISN_DORIM,
+       MISN_ELAMALE,
+       MISN_CLOAKFIGHTER,
+       MISN_ODEON,
+       MISN_FELLON,
+       MISN_SIVEDI,
+       MISN_ALMARTHA,
+       MISN_POSWIC,
+       MISN_ELLESH,
+       MISN_PLUTO,
+       MISN_NEPTUNE,
+       MISN_URANUS,
+       MISN_SATURN,
+       MISN_JUPITER,
+       MISN_MARS,
+       MISN_EARTH,
+       MISN_VENUS,
+       MISN_INTERCEPTION,
+       MISN_MAX
+};
+
+// Difficulties
+enum {
+       DIFFICULTY_EASY = 0,
+       DIFFICULTY_NORMAL,
+       DIFFICULTY_HARD,
+       DIFFICULTY_NIGHTMARE,
+       DIFFICULTY_ORIGINAL,
+       DIFFICULTY_MAX
+};
+
+const char * const systemNames[] = {"Spirit", "Eyananth", "Mordor", "Sol"};
+
+const char * const systemBackground[] = {
+       "gfx/spirit.jpg", "gfx/eyananth.jpg", "gfx/mordor.jpg", "gfx/sol.jpg"
+};
+
+const int rate2reload[6] = {15, 15, 13, 11, 9, 7};
+const int screenWidth = 800;
+const int screenHeight = 600;
+const int xViewBorder = 100;
+const int yViewBorder = 100;
+const float cameraMaxSpeed = 3.;
+const int maxHoming = 20;
+const int maxDoubleHoming = 15;
+const int maxMicroHoming = 10;
+
+#endif
diff --git a/src/engine.cpp b/src/engine.cpp
new file mode 100644 (file)
index 0000000..7dc7f8f
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012, 2013 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+Engine engine;
+
+void engine_init()
+{
+       engine.musicVolume = 100;
+       engine.useAudio = true;
+
+       engine.maxAliens = 9;
+
+       engine.ssx = 0;
+       engine.ssy = 0;
+       engine.smx = 0;
+       engine.smy = 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 < KEY_LAST; 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 = false;
+       engine.gameSection = SECTION_TITLE;
+
+       engine.cheat = false;
+       engine.cheatShield = false;
+       engine.cheatAmmo = false;
+       engine.cheatCash = false;
+}
diff --git a/src/engine.h b/src/engine.h
new file mode 100644 (file)
index 0000000..4f510f5
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef GLOBALS_H
+#define GLOBALS_H
+
+extern Engine engine;
+
+void engine_init();
+
+#endif
diff --git a/src/explosion.cpp b/src/explosion.cpp
new file mode 100644 (file)
index 0000000..d56eef7
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.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 explosion_add(float x, float y, int type)
+{
+       object *explosion = new object;
+
+       explosion->next = NULL;
+       explosion->active = true;
+       explosion->x = x;
+       explosion->y = y;
+       explosion->thinktime = 28;
+       explosion->face = type;
+       explosion->image[0] = 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 explosion_addEngine(object *craft)
+{
+       if (rand() % 2 == 0)
+               return;
+
+       float x = craft->x + (craft->engineX * craft->face);
+       float y = craft->y + craft->engineY;
+
+       y += RANDRANGE(-3, 3);
+       explosion_add(x, y, E_TINY_EXPLOSION);
+}
diff --git a/src/explosion.h b/src/explosion.h
new file mode 100644 (file)
index 0000000..95b80a7
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef EXPLOSIONS_H
+#define EXPLOSIONS_H
+
+void explosion_add(float x, float y, int type);
+void explosion_addEngine(object *craft);
+
+#endif
diff --git a/src/game.cpp b/src/game.cpp
new file mode 100644 (file)
index 0000000..0e26b44
--- /dev/null
@@ -0,0 +1,2273 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012, 2013 Guus Sliepen
+Copyright (C) 2012, 2014, 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+Game game;
+
+void game_init()
+{
+       game.system = 0;
+       game.area = MISN_START;
+       game.sfxVolume = 0;
+       game.musicVolume = 0;
+
+       if (!engine.useAudio)
+       {
+               engine.useSound = false;
+               engine.useMusic = false;
+       }
+
+       game.cash = 0;
+       game.cashEarned = 0;
+       game.shots = 0;
+       game.hits = 0;
+       game.accuracy = 0;
+       game.totalKills = game.wingMate1Kills = game.wingMate2Kills = 0;
+       game.totalOtherKills = 0;
+       game.hasWingMate1 = game.hasWingMate2 = 0;
+       game.wingMate1Ejects = game.wingMate2Ejects = 0;
+       game.secondaryMissions = game.secondaryMissionsCompleted = 0;
+       game.shieldPickups = game.rocketPickups = game.cellPickups = 0;
+       game.powerups = game.minesKilled = game.cargoPickups = 0;
+
+       game.slavesRescued = 0;
+       game.experimentalShield = 1000;
+
+       game.timeTaken = 0;
+
+       game.stationedPlanet = -1;
+       game.destinationPlanet = -1;
+       for (int i = 0 ; i < 10 ; i++)
+               game.missionCompleted[i] = 0;
+       game.distanceCovered = 0;
+
+       game.minPlasmaRate = 1;
+       game.minPlasmaOutput = 1;
+       game.minPlasmaDamage = 1;
+       game.maxPlasmaRate = 2;
+       game.maxPlasmaOutput = 2;
+       game.maxPlasmaDamage = 2;
+       game.maxPlasmaAmmo = 100;
+       game.maxRocketAmmo = 10;
+
+       game.minPlasmaRateLimit = 2;
+       game.minPlasmaDamageLimit = 2;
+       game.minPlasmaOutputLimit = 2;
+       game.maxPlasmaRateLimit = 3;
+       game.maxPlasmaDamageLimit = 3;
+       game.maxPlasmaOutputLimit = 3;
+       game.maxPlasmaAmmoLimit = 250;
+       game.maxRocketAmmoLimit = 50;
+
+       player.maxShield = 50;
+
+       switch (game.difficulty)
+       {
+               case DIFFICULTY_EASY:
+                       player.maxShield = 100;
+
+                       game.minPlasmaRate = 2;
+                       game.minPlasmaOutput = 2;
+                       game.minPlasmaDamage = 2;
+                       game.maxPlasmaRate = 3;
+                       game.maxPlasmaOutput = 3;
+                       game.maxPlasmaDamage = 3;
+                       game.maxPlasmaAmmo = 150;
+                       game.maxRocketAmmo = 20;
+
+                       game.minPlasmaRateLimit = 3;
+                       game.minPlasmaDamageLimit = 3;
+                       game.minPlasmaOutputLimit = 3;
+                       game.maxPlasmaRateLimit = 5;
+                       game.maxPlasmaDamageLimit = 5;
+                       game.maxPlasmaOutputLimit = 5;
+                       break;
+               case DIFFICULTY_HARD:
+                       player.maxShield = 25;
+                       break;
+               case DIFFICULTY_NIGHTMARE:
+                       player.maxShield = 1;
+                       game.maxRocketAmmo = 5;
+                       break;
+               case DIFFICULTY_ORIGINAL:
+                       player.maxShield = 25;
+
+                       game.minPlasmaRateLimit = 3;
+                       game.minPlasmaDamageLimit = 3;
+                       game.minPlasmaOutputLimit = 3;
+                       game.maxPlasmaRateLimit = 5;
+                       game.maxPlasmaDamageLimit = 5;
+                       game.maxPlasmaOutputLimit = 5;
+                       break;
+       }
+
+       player.shield = player.maxShield;
+       player.ammo[0] = 0;
+       player.ammo[1] = game.maxRocketAmmo / 2;
+       player.weaponType[0] = W_PLAYER_WEAPON;
+       player.weaponType[1] = W_ROCKETS;
+
+       initWeapons();
+       initMissions();
+       initPlanetMissions(game.system);
+}
+
+static void game_addDebris(int x, int y, int amount)
+{
+       if ((rand() % 2) == 0)
+               audio_playSound(SFX_DEBRIS, x);
+       else
+               audio_playSound(SFX_DEBRIS2, x);
+
+       object *debris;
+       
+       amount = RANDRANGE(3, amount);
+       LIMIT(amount, 3, 8);
+
+       for (int i = 0 ; i < amount ; i++)
+       {
+               debris = new object;
+
+               debris->next = NULL;
+               debris->x = x;
+               debris->y = y;
+
+               debris->thinktime = RANDRANGE(60, 180);
+
+               debris->dx = RANDRANGE(-500, 500);
+               debris->dy = RANDRANGE(-500, 500);
+
+               if (debris->dx != 0)
+                       debris->dx /= 100;
+
+               if (debris->dy != 0)
+                       debris->dy /= 100;
+
+               engine.debrisTail->next = debris;
+               engine.debrisTail = debris;
+       }
+}
+
+/*
+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.
+*/
+static void game_doCollectables()
+{
+       collectables *collectable = engine.collectableHead;
+       collectables *prevCollectable = engine.collectableHead;
+       engine.collectableTail = engine.collectableHead;
+       char temp[40];
+
+       while (collectable->next != NULL)
+       {
+               collectable = collectable->next;
+
+               if (collectable->active)
+               {
+                       if ((collectable->x + collectable->image->w > 0) &&
+                                       (collectable->x < screen->w) &&
+                                       (collectable->y + collectable->image->h > 0) &&
+                                       (collectable->y < screen->h))
+                               blit(collectable->image, (int)collectable->x, (int)collectable->y);
+
+                       collectable->x += engine.ssx + engine.smx;
+                       collectable->y += engine.ssy + engine.smy;
+                       collectable->x += collectable->dx;
+                       collectable->y += collectable->dy;
+
+                       collectable->life--;
+
+                       if ((player.shield > 0) && (collision(collectable, &player)))
+                       {
+                               switch(collectable->type)
+                               {
+                                       case P_CASH:
+                                               if (game.difficulty == DIFFICULTY_ORIGINAL)
+                                                       collectable->value *= 2;
+
+                                               game.cash += collectable->value;
+                                               game.cashEarned += collectable->value;
+                                               sprintf(temp, "Got $%d ", collectable->value);
+                                               break;
+
+                                       case P_ROCKET:
+                                               LIMIT_ADD(player.ammo[1], collectable->value, 0,
+                                                       game.maxRocketAmmo);
+                                               if (player.ammo[1] == game.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");
+                                               }
+                                               game.rocketPickups += collectable->value;
+                                               break;
+
+                                       case P_SHIELD:
+                                               LIMIT_ADD(player.shield, 10, 0, player.maxShield);
+                                               game.shieldPickups ++;
+                                               sprintf(temp, "Restored 10 shield points");
+                                               break;
+
+                                       case P_PLASMA_RATE:
+                                               game.powerups++;
+                                               if ((game.area != MISN_INTERCEPTION) ||
+                                                       (game.difficulty == DIFFICULTY_ORIGINAL) ||
+                                                       (player.ammo[0] > 0))
+                                               {
+                                                       if ((game.area != MISN_INTERCEPTION) ||
+                                                                       (game.difficulty == DIFFICULTY_ORIGINAL))
+                                                               LIMIT_ADD(player.ammo[0], collectable->value,
+                                                                       0, game.maxPlasmaAmmo);
+
+                                                       if (weapon[W_PLAYER_WEAPON].reload[0] <= rate2reload[game.maxPlasmaRate])
+                                                               sprintf(temp, "Firing rate already at maximum");
+                                                       else
+                                                       {
+                                                               weapon[W_PLAYER_WEAPON].reload[0] -= 2;
+                                                               sprintf(temp, "Firing rate increased");
+                                                       }
+                                               }
+                                               else
+                                               {
+                                                       sprintf(temp, "Upgrade failed (no plasma ammo)");
+                                               }
+                                               break;
+
+                                       case P_PLASMA_SHOT:
+                                               game.powerups++;
+                                               if ((game.area != MISN_INTERCEPTION) ||
+                                                       (game.difficulty == DIFFICULTY_ORIGINAL) ||
+                                                       (player.ammo[0] > 0))
+                                               {
+                                                       if ((game.area != MISN_INTERCEPTION) ||
+                                                                       (game.difficulty == DIFFICULTY_ORIGINAL))
+                                                               LIMIT_ADD(player.ammo[0], collectable->value,
+                                                                       0, game.maxPlasmaAmmo);
+
+                                                       if (weapon[W_PLAYER_WEAPON].ammo[0] >= game.maxPlasmaOutput)
+                                                               sprintf(temp, "Plasma output already at maximum");
+                                                       else
+                                                       {
+                                                               weapon[W_PLAYER_WEAPON].ammo[0]++;
+                                                               sprintf(temp, "Plasma output increased");
+                                                       }
+                                               }
+                                               else
+                                               {
+                                                       sprintf(temp, "Upgrade failed (no plasma ammo)");
+                                               }
+                                               break;
+
+                                       case P_PLASMA_DAMAGE:
+                                               game.powerups++;
+                                               if ((game.area != MISN_INTERCEPTION) ||
+                                                       (game.difficulty == DIFFICULTY_ORIGINAL) ||
+                                                       (player.ammo[0] > 0))
+                                               {
+                                                       if ((game.area != MISN_INTERCEPTION) ||
+                                                                       (game.difficulty == DIFFICULTY_ORIGINAL))
+                                                               LIMIT_ADD(player.ammo[0], collectable->value,
+                                                                       0, game.maxPlasmaAmmo);
+
+                                                       if (weapon[W_PLAYER_WEAPON].damage >= game.maxPlasmaDamage)
+                                                               sprintf(temp, "Plasma damage already at maximum");
+                                                       else {
+                                                               weapon[W_PLAYER_WEAPON].damage++;
+                                                               sprintf(temp, "Plasma damage increased");
+                                                       }
+                                               }
+                                               else
+                                               {
+                                                       sprintf(temp, "Upgrade failed (no plasma ammo)");
+                                               }
+                                               break;
+
+                                       case P_SUPER:
+                                               game.powerups++;
+                                               if ((game.area != MISN_INTERCEPTION) ||
+                                                       (game.difficulty == DIFFICULTY_ORIGINAL) ||
+                                                       (player.ammo[0] > 0))
+                                               {
+                                                       if ((game.area != MISN_INTERCEPTION) ||
+                                                                       (game.difficulty == DIFFICULTY_ORIGINAL))
+                                                               LIMIT_ADD(player.ammo[0], collectable->value,
+                                                                       0, game.maxPlasmaAmmo);
+
+                                                       weapon[W_PLAYER_WEAPON].ammo[0] = 5;
+                                                       weapon[W_PLAYER_WEAPON].damage = 5;
+                                                       weapon[W_PLAYER_WEAPON].reload[0] = rate2reload[5];
+                                                       weapon[W_PLAYER_WEAPON].flags |= WF_SPREAD;
+
+                                                       sprintf(temp, "Picked up a Super Charge!!");
+                                               }
+                                               else
+                                               {
+                                                       sprintf(temp, "Damn! Upgrade failed (no plasma ammo)");
+                                               }
+                                               break;
+
+                                       case P_PLASMA_AMMO:
+                                               if (player.ammo[0] >= game.maxPlasmaAmmo)
+                                                       sprintf(temp, "Plasma cells already at Maximum");
+                                               else
+                                               {
+                                                       LIMIT_ADD(player.ammo[0], collectable->value,
+                                                               0, game.maxPlasmaAmmo);
+                                                       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!)");
+                                                       }
+                                               }
+                                               game.cellPickups += collectable->value;
+                                               break;
+
+                                       case P_CARGO:
+                                               strcpy(temp, "Picked up some Cargo");
+                                               game.cargoPickups++;
+                                               break;
+
+                                       case P_SLAVES:
+                                               sprintf(temp, "Rescued %d slaves", collectable->value);
+                                               game.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 = false;
+                               if (collectable->type != P_MINE)
+                               {
+                                       setInfoLine(temp, FONT_WHITE);
+                                       if (collectable->type == P_SHIELD)
+                                               audio_playSound(SFX_SHIELDUP, player.x);
+                                       else
+                                               audio_playSound(SFX_PICKUP, player.x);
+                               }
+                       }
+
+                       // stop people from exploiting a weapon check condition
+                       if (player.ammo[0] == 0)
+                       {
+                               weapon[W_PLAYER_WEAPON].ammo[0] = game.minPlasmaOutput;
+                               weapon[W_PLAYER_WEAPON].damage = game.minPlasmaDamage;
+                               weapon[W_PLAYER_WEAPON].reload[0] = rate2reload[game.minPlasmaRate];
+                       }
+               }
+
+               if (collectable->life < 1)
+               {
+                       collectable->active = false;
+                       if ((collectable->type == P_CARGO) ||
+                                       (collectable->type == P_ESCAPEPOD) ||
+                                       (collectable->type == P_SLAVES))
+                               updateMissionRequirements(M_PROTECT_PICKUP, collectable->type, 1);
+               }
+
+               if (collectable->active)
+               {
+                       prevCollectable = collectable;
+                       engine.collectableTail = collectable;
+               }
+               else
+               {
+                       if (collectable->type == P_MINE)
+                               collectable_explode(collectable);
+                       prevCollectable->next = collectable->next;
+                       delete collectable;
+                       collectable = prevCollectable;
+               }
+       }
+}
+
+/*
+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.
+*/
+static void game_doBullets()
+{
+       object *bullet = engine.bulletHead;
+       object *prevBullet = engine.bulletHead;
+
+       collectables *collectable;
+       collectables *prevCollectable;
+
+       bool okayToHit = false;
+       int old_shield;
+       float homingMissileSpeed = 0;
+       int charger_num;
+
+       bullet = engine.bulletHead;
+       prevBullet = engine.bulletHead;
+       engine.bulletTail = engine.bulletHead;
+
+       while (bullet->next != NULL)
+       {
+               bullet = bullet->next;
+
+               if (bullet->active)
+               {
+                       if (bullet->flags & WF_HOMING)
+                       {
+                               if (bullet->target == NULL)
+                                       bullet->target = bullet_getTarget(bullet);
+
+                               if (bullet->owner->flags & FL_FRIEND)
+                                       homingMissileSpeed = 0.25;
+                               else
+                                       homingMissileSpeed = 0.05;
+                       }
+
+                       if (bullet->id == WT_ROCKET)
+                       {
+                               explosion_add(bullet->x, bullet->y, E_SMALL_EXPLOSION);
+                       }
+                       else if (bullet->id == WT_MICROROCKET)
+                       {
+                               explosion_add(bullet->x, bullet->y, E_TINY_EXPLOSION);
+                       }
+
+                       if ((bullet->flags & WF_AIMED))
+                       {
+                               blit(bullet->image[0], (int)(bullet->x - bullet->dx),
+                                       (int)(bullet->y - bullet->dy));
+                       }
+
+                       if (bullet->id == WT_CHARGER)
+                       {
+                               if (game.difficulty == DIFFICULTY_ORIGINAL)
+                                       charger_num = bullet->damage;
+                               else
+                                       charger_num = bullet->damage * 2;
+
+                               for (int i = 0 ; i < charger_num ; i++)
+                                       blit(bullet->image[0],
+                                               (int)(bullet->x - RANDRANGE(
+                                                       -(charger_num / 6), charger_num / 6)),
+                                               (int)(bullet->y + RANDRANGE(-3, 3)));
+                       }
+
+                       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)
+                                       LIMIT_ADD(bullet->dx, homingMissileSpeed, -15, 15);
+                               else if (bullet->x > bullet->target->x)
+                                       LIMIT_ADD(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)
+                                       LIMIT_ADD(bullet->dy, homingMissileSpeed, -15, 15);
+                               else if (bullet->y > bullet->target->y)
+                                       LIMIT_ADD(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 + engine.smx;
+                       bullet->y += engine.ssy + engine.smy;
+
+                       for (int i = 0 ; i < ALIEN_MAX ; i++)
+                       {
+                               if ((aliens[i].shield < 1) || (!aliens[i].active))
+                                       continue;
+
+                               okayToHit = false;
+
+                               if ((bullet->flags & WF_FRIEND) && (aliens[i].flags & FL_WEAPCO))
+                                       okayToHit = true;
+                               if ((bullet->flags & WF_WEAPCO) && (aliens[i].flags & FL_FRIEND))
+                                       okayToHit = true;
+                               if ((bullet->id == WT_ROCKET) || (bullet->id == WT_LASER) ||
+                                               (bullet->id == WT_CHARGER))
+                                       okayToHit = true;
+
+                               if (bullet->owner == aliens[i].owner)
+                                       okayToHit = false;
+
+                               if (okayToHit)
+                               {
+                                       if ((bullet->active) && (collision(bullet, &aliens[i])))
+                                       {
+                                               old_shield = aliens[i].shield;
+
+                                               if (bullet->owner == &player)
+                                               {
+                                                       game.hits++;
+                                                       if ((aliens[i].classDef == CD_PHOEBE) ||
+                                                                       (aliens[i].classDef == CD_URSULA))
+                                                               getMissFireMessage(&aliens[i]);
+                                               }
+
+                                               if (!(aliens[i].flags & FL_IMMORTAL))
+                                               {
+                                                       alien_hurt(&aliens[i], bullet->owner,
+                                                               bullet->damage, (bullet->flags & WF_DISABLE));
+
+                                                       aliens[i].hit = 5;
+                                               }
+                                               else if (aliens[i].flags & FL_DAMAGEOWNER)
+                                               {
+                                                       alien_hurt(aliens[i].owner, bullet->owner,
+                                                               bullet->damage, (bullet->flags & WF_DISABLE));
+
+                                                       aliens[i].owner->hit = 5;
+                                               }
+
+                                               if (bullet->id == WT_CHARGER)
+                                               {
+                                                       bullet->damage -= old_shield;
+                                                       if (bullet->damage <= 0)
+                                                       {
+                                                               bullet->active = false;
+                                                               bullet->shield = 0;
+                                                               audio_playSound(SFX_EXPLOSION, bullet->x);
+                                                               for (int i = 0 ; i < 10 ; i++)
+                                                                       explosion_add(bullet->x + RANDRANGE(-35, 35),
+                                                                               bullet->y + RANDRANGE(-35, 35),
+                                                                               E_BIG_EXPLOSION);
+                                                       }
+                                               }
+                                               else
+                                               {
+                                                       bullet->active = false;
+                                                       bullet->shield = 0;
+                                               }
+
+                                               if (bullet->id == WT_ROCKET)
+                                                       explosion_add(bullet->x, bullet->y, E_BIG_EXPLOSION);
+                                               else
+                                                       explosion_add(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 && (player.shield > 0) &&
+                                       (bullet->owner != &player) && collision(bullet, &player))
+                               {
+                                       old_shield = player.shield;
+
+                                       if ((!engine.cheatShield) || (engine.missionCompleteTimer != 0))
+                                       {
+                                               if ((player.shield > engine.lowShield) &&
+                                                               (player.shield - bullet->damage <= engine.lowShield))
+                                                       setInfoLine("!!! WARNING: SHIELD LOW !!!", FONT_RED);
+
+                                               player.shield -= bullet->damage;
+                                               LIMIT(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->damage -= old_shield;
+                                               if (bullet->damage <= 0)
+                                               {
+                                                       bullet->active = false;
+                                                       bullet->shield = 0;
+                                                       audio_playSound(SFX_EXPLOSION, bullet->x);
+                                                       for (int i = 0 ; i < 10 ; i++)
+                                                               explosion_add(bullet->x + RANDRANGE(-35, 35),
+                                                                       bullet->y + RANDRANGE(-35, 35), E_BIG_EXPLOSION);
+                                               }
+                                       }
+                                       else
+                                       {
+                                               bullet->active = false;
+                                               bullet->shield = 0;
+                                       }
+
+                                       audio_playSound(SFX_HIT, player.x);
+
+                                       if (bullet->id == WT_ROCKET)
+                                               explosion_add(bullet->x, bullet->y, E_BIG_EXPLOSION);
+                                       else
+                                               explosion_add(bullet->x, bullet->y, E_SMALL_EXPLOSION);
+                               }
+                       }
+               }
+
+               if ((game.difficulty != DIFFICULTY_EASY) &&
+                       ((bullet->owner == &player) || (bullet->id == WT_ROCKET)))
+               {
+                       for (int j = 0 ; j < 20 ; j++)
+                       {
+                               if (cargo[j].active)
+                               {
+                                       if (collision(bullet, &cargo[j]))
+                                       {
+                                               bullet->active = false;
+                                               explosion_add(bullet->x, bullet->y, E_SMALL_EXPLOSION);
+                                               audio_playSound(SFX_HIT, cargo[j].x);
+                                               if (cargo[j].collectType != P_PHOEBE)
+                                               {
+                                                       cargo[j].active = false;
+                                                       audio_playSound(SFX_EXPLOSION, cargo[j].x);
+                                                       for (int i = 0 ; i < 10 ; i++)
+                                                               explosion_add(cargo[j].x + RANDRANGE(-15, 15),
+                                                                       cargo[j].y + RANDRANGE(-15, 15),
+                                                                       E_BIG_EXPLOSION);
+                                                       updateMissionRequirements(M_PROTECT_PICKUP,
+                                                               P_CARGO, 1);
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               // check to see if a bullet (on any side) hits a mine
+               collectable = engine.collectableHead;
+               prevCollectable = engine.collectableHead;
+               engine.collectableTail = engine.collectableHead;
+               while (collectable->next != NULL)
+               {
+                       collectable = collectable->next;
+
+                       if (collectable->type == P_MINE)
+                       {
+                               if (collision(collectable, bullet))
+                               {
+                                       collectable->active = false;
+                                       
+                                       if (bullet->id != WT_CHARGER)
+                                       {
+                                               bullet->active = false;
+                                       }
+                                       else
+                                       {
+                                               bullet->shield--;
+                                               if (bullet->shield < 0)
+                                                       bullet->active = false;
+                                       }
+
+                                       if (bullet->owner == &player)
+                                       {
+                                               game.minesKilled++;
+                                               game.hits++;
+                                       }
+                               }
+                       }
+
+                       if (collectable->active)
+                       {
+                               prevCollectable = collectable;
+                               engine.collectableTail = collectable;
+                       }
+                       else
+                       {
+                               collectable_explode(collectable);
+                               prevCollectable->next = collectable->next;
+                               delete collectable;
+                               collectable = prevCollectable;
+                       }
+               }
+
+               bullet->shield--;
+
+               if (bullet->shield < 1)
+               {
+                       if (bullet->flags & WF_TIMEDEXPLOSION)
+                       {
+                               audio_playSound(SFX_EXPLOSION, bullet->x);
+                               for (int i = 0 ; i < 10 ; i++)
+                                       explosion_add(bullet->x + RANDRANGE(-35, 35),
+                                               bullet->y + RANDRANGE(-35, 35), E_BIG_EXPLOSION);
+
+                               player_checkShockDamage(bullet->x, bullet->y);
+                       }
+                       bullet->active = false;
+               }
+
+               if (bullet->active)
+               {
+                       prevBullet = bullet;
+                       engine.bulletTail = bullet;
+               }
+               else
+               {
+                       prevBullet->next = bullet->next;
+                       delete bullet;
+                       bullet = prevBullet;
+               }
+       }
+}
+
+static void game_doAliens()
+{
+       static float barrierLoop = 0;
+
+       int shapeToUse;
+       bool canFire;
+       int n;
+
+       barrierLoop += 0.2;
+
+       // A global variable for checking if all the aliens are dead
+       engine.allAliensDead = 1;
+
+       for (int i = 0 ; i < ALIEN_MAX ; i++)
+       {
+               if (aliens[i].active)
+               {
+                       if (aliens[i].shield > 0)
+                       {
+                               if ((aliens[i].flags & FL_WEAPCO) && (!(aliens[i].flags & FL_DISABLED)))
+                                       engine.allAliensDead = 0;
+
+                               // Set part attributes
+                               if (aliens[i].owner != &aliens[i])
+                               {
+                                       aliens[i].face = aliens[i].owner->face;
+
+                                       if (aliens[i].face == 0)
+                                               aliens[i].x = aliens[i].owner->x - aliens[i].dx;
+                                       else
+                                               aliens[i].x = aliens[i].owner->x + aliens[i].owner->image[0]->w + aliens[i].dx - aliens[i].image[0]->w;
+
+                                       aliens[i].y = (aliens[i].owner->y + aliens[i].dy);
+
+                                       if (aliens[i].owner->shield < 1)
+                                       {
+                                               if ((aliens[i].classDef != CD_URANUSBOSSWING1) &&
+                                                       (aliens[i].classDef != CD_URANUSBOSSWING2))
+                                               {
+                                                       aliens[i].shield = 0;
+                                               }
+                                               else
+                                               {
+                                                       aliens[i].flags &= ~FL_IMMORTAL;
+                                                       aliens[i].owner = &aliens[i];
+                                                       aliens[i].chance[0] = 25;
+                                               }
+                                       }
+                               }
+
+                               canFire = true; // The alien is allowed to fire
+
+                               LIMIT_ADD(aliens[i].thinktime, -1, 0, 250);
+
+                               if (aliens[i].target->shield < 1)
+                                       aliens[i].target = &aliens[i];
+
+                               // Specific to Sid to stop him pissing about(!)
+                               if ((aliens[i].classDef == CD_SID) &&
+                                               (aliens[i].target->flags & FL_DISABLED))
+                                       aliens[i].target = &aliens[i];
+
+                               if (aliens[i].target == &aliens[i])
+                               {
+                                       if (engine.missionCompleteTimer == 0)
+                                       {
+                                               alien_searchForTarget(&aliens[i]);
+                                       }
+                                       else
+                                       {
+                                               if (aliens[i].flags & FL_FRIEND)
+                                               {
+                                                       aliens[i].target = &player;
+                                                       aliens[i].thinktime = 1;
+                                               }
+                                       }
+                               }
+
+                               if ((!(aliens[i].flags & FL_DISABLED)) &&
+                                       (aliens[i].thinktime == 0) && (aliens[i].target != &aliens[i]) &&
+                                       (aliens[i].owner == &aliens[i]))
+                               {
+                                       if (aliens[i].classDef == CD_KLINE)
+                                               alien_setKlineAI(&aliens[i]);
+                                       else
+                                               alien_setAI(&aliens[i]);
+
+                                       aliens[i].thinktime = (rand() % 25) * 10;
+
+                                       // Face direction of movement unless you always face
+                                       // your target(!)
+                                       if (!(aliens[i].flags & FL_ALWAYSFACE))
+                                       {
+                                               aliens[i].face = (aliens[i].dx > 0);
+                                       }
+
+                                       LIMIT(aliens[i].dx, -aliens[i].speed, aliens[i].speed);
+                                       LIMIT(aliens[i].dy, -aliens[i].speed, aliens[i].speed);
+
+                               }
+
+                               if (aliens[i].flags & FL_ALWAYSFACE)
+                               {
+                                       aliens[i].face = 0;
+                                       if (aliens[i].x > aliens[i].target->x) aliens[i].face = 1;
+                               }
+
+                               if ((game.area == MISN_ELLESH) &&
+                                               ((aliens[i].classDef == CD_BOSS) ||
+                                                   game.difficulty != DIFFICULTY_ORIGINAL))
+                                       aliens[i].face = 0;
+
+                               if ((aliens[i].flags & FL_DEPLOYDRONES) && ((rand() % 300) == 0))
+                                       alien_addDrone(&aliens[i]);
+
+                               if (aliens[i].flags & FL_LEAVESECTOR)
+                               {
+                                       // Note: The original version of this line incorrectly
+                                       // specified -15 as the *maximum* instead of the
+                                       // *minimum*, which at the time was equivalent to
+                                       // ``aliens[i].dx = -15``.
+                                       LIMIT_ADD(aliens[i].dx, -0.5, -15, 0);
+                                       aliens[i].dy = 0;
+                                       aliens[i].thinktime = 999;
+                                       aliens[i].face = 0;
+
+                                       if (aliens[i].x >= 5000)
+                                       {
+                                               aliens[i].flags -= FL_LEAVESECTOR;
+                                               aliens[i].flags += FL_ESCAPED;
+                                               aliens[i].active = false;
+
+                                               if (aliens[i].classDef == CD_CLOAKFIGHTER)
+                                               {
+                                                       game.experimentalShield = aliens[i].shield;
+                                                       setInfoLine("Experimental Fighter has fled",
+                                                               FONT_CYAN);
+                                               }
+
+                                               aliens[i].shield = 0;
+                                               updateMissionRequirements(M_ESCAPE_TARGET,
+                                                       aliens[i].classDef, 1);
+                                       
+                                               if (aliens[i].classDef != CD_CLOAKFIGHTER)
+                                                       updateMissionRequirements(M_DESTROY_TARGET_TYPE,
+                                                               aliens[i].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,
+                               // its sprite is not displayed. However the engine
+                               // trail is still visible!
+                               if ((aliens[i].flags & FL_CANCLOAK) && ((rand() % 500) == 0))
+                               {
+                                       if (aliens[i].flags & FL_ISCLOAKED)
+                                               aliens[i].flags -= FL_ISCLOAKED;
+                                       else
+                                               aliens[i].flags += FL_ISCLOAKED;
+                                       audio_playSound(SFX_CLOAK, aliens[i].x);
+                               }
+
+                               if (aliens[i].classDef == CD_BARRIER)
+                               {
+                                       aliens[i].dx = -10 + (sinf(barrierLoop + aliens[i].speed) * 60);
+                                       aliens[i].dy = 20 + (cosf(barrierLoop + aliens[i].speed) * 40);
+                               }
+
+                               if (aliens[i].classDef == CD_MOBILESHIELD)
+                               {
+                                       LIMIT_ADD(aliens[ALIEN_BOSS].shield, 1, 0,
+                                               aliens[ALIEN_BOSS].maxShield);
+                               }
+
+                               LIMIT_ADD(aliens[i].reload[0], -1, 0, 999);
+                               LIMIT_ADD(aliens[i].reload[1], -1, 0, 999);
+
+                               if ((!(aliens[i].flags & FL_DISABLED)) &&
+                                       (!(aliens[i].flags & FL_NOFIRE)))
+                               {
+                                       if ((aliens[i].target->shield > 0))
+                                               canFire = alien_checkTarget(&aliens[i]);
+
+                                       if (((aliens[i].thinktime % 2) == 0) &&
+                                                       (aliens[i].flags & FL_FRIEND))
+                                               canFire = alien_enemiesInFront(&aliens[i]);
+                               }
+                               else
+                               {
+                                       canFire = false;
+                               }
+
+                               if (canFire)
+                               {
+                                       if ((aliens[i].reload[0] == 0) &&
+                                               ((rand() % 1000 < aliens[i].chance[0]) ||
+                                                       (aliens[i].flags & FL_CONTINUOUS_FIRE)))
+                                       {
+                                               ship_fireBullet(&aliens[i], 0);
+                                       }
+                                       if ((aliens[i].reload[1] == 0) &&
+                                               ((rand() % 1000 < aliens[i].chance[1]) ||
+                                                       (aliens[i].flags & FL_CONTINUOUS_FIRE)))
+                                       {
+                                               if ((aliens[i].weaponType[1] != W_ENERGYRAY) &&
+                                                       (aliens[i].weaponType[1] != W_LASER))
+                                               {
+                                                       if (aliens[i].weaponType[1] == W_CHARGER)
+                                                               aliens[i].ammo[1] = 50 + rand() % 150;
+                                                       ship_fireBullet(&aliens[i], 1);
+                                               }
+                                               else if (aliens[i].weaponType[1] == W_LASER)
+                                               {
+                                                       aliens[i].flags += FL_FIRELASER;
+                                               }
+                                               else if ((aliens[i].weaponType[1] == W_ENERGYRAY) &&
+                                                       (aliens[i].ammo[0] == 250))
+                                               {
+                                                       aliens[i].flags += FL_FIRERAY;
+                                                       audio_playSound(SFX_ENERGYRAY, aliens[i].x);
+                                               }
+                                       }
+                               }
+
+                               if (aliens[i].flags & FL_FIRERAY)
+                               {
+                                       ship_fireRay(&aliens[i]);
+                               }
+                               else
+                               {
+                                       LIMIT_ADD(aliens[i].ammo[0], 1, 0, 250);
+                               }
+
+                               if (aliens[i].flags & FL_FIRELASER)
+                               {
+                                       ship_fireBullet(&aliens[i], 1);
+                                       if ((rand() % 25) == 0)
+                                               aliens[i].flags -= FL_FIRELASER;
+                               }
+
+                               if (aliens[i].flags & FL_DROPMINES)
+                               {
+                                       if ((rand() % 150) == 0)
+                                               collectable_add(aliens[i].x, aliens[i].y, P_MINE, 25,
+                                                       600 + rand() % 2400);
+
+                                       // Kline drops mines a lot more often
+                                       if ((&aliens[i] == &aliens[ALIEN_KLINE]))
+                                       {
+                                               if ((rand() % 10) == 0)
+                                                       collectable_add(aliens[i].x, aliens[i].y, P_MINE, 25,
+                                                               600 + rand() % 2400);
+                                       }
+                               }
+
+                               shapeToUse = aliens[i].imageIndex[aliens[i].face];
+
+                               if (aliens[i].hit)
+                                       shapeToUse += SHIP_HIT_INDEX;
+
+                               LIMIT_ADD(aliens[i].hit, -1, 0, 100);
+
+                               if ((aliens[i].x + aliens[i].image[0]->w > 0) &&
+                                       (aliens[i].x < screen->w) &&
+                                       (aliens[i].y + aliens[i].image[0]->h > 0) &&
+                                       (aliens[i].y < screen->h))
+                               {
+                                       if ((!(aliens[i].flags & FL_DISABLED)) &&
+                                                       (aliens[i].classDef != CD_ASTEROID) &&
+                                                       (aliens[i].classDef != CD_ASTEROID2))
+                                               explosion_addEngine(&aliens[i]);
+                                       if ((!(aliens[i].flags & FL_ISCLOAKED)) || (aliens[i].hit > 0))
+                                               blit(shipShape[shapeToUse], (int)aliens[i].x,
+                                                       (int)aliens[i].y);
+                                       if (aliens[i].flags & FL_DISABLED)
+                                       {
+                                               if ((rand() % 10) == 0)
+                                                       explosion_add(aliens[i].x + (rand() % aliens[i].image[0]->w),
+                                                               aliens[i].y + (rand() % aliens[i].image[0]->h),
+                                                               E_ELECTRICAL);
+                                       }
+                               }
+
+                               if ((game.area == MISN_MARS) && (aliens[i].x < -60))
+                                       aliens[i].active = false;
+                       }
+                       else
+                       {
+                               aliens[i].shield--;
+                               if ((aliens[i].x > 0) && (aliens[i].x < screen->w) &&
+                                       (aliens[i].y > 0) && (aliens[i].y < screen->h))
+                               {
+                                       blit(aliens[i].image[aliens[i].face], (int)aliens[i].x,
+                                               (int)aliens[i].y);
+                                       explosion_add(aliens[i].x + (rand() % aliens[i].image[0]->w),
+                                               aliens[i].y + (rand() % aliens[i].image[0]->h),
+                                               E_BIG_EXPLOSION);
+                               }
+                               if (aliens[i].shield < aliens[i].deathCounter)
+                               {
+                                       aliens[i].active = false;
+                                       if ((aliens[i].classDef == CD_BOSS) ||
+                                                       (aliens[i].owner == &aliens[ALIEN_BOSS]) ||
+                                                       (aliens[i].flags & FL_FRIEND) ||
+                                                       (aliens[i].classDef == CD_ASTEROID) ||
+                                                       (aliens[i].classDef == CD_KLINE))
+                                               game_addDebris((int)aliens[i].x, (int)aliens[i].y,
+                                                       aliens[i].maxShield);
+
+                                       if (aliens[i].classDef == CD_ASTEROID)
+                                       {
+                                               n = 1 + (rand() % 3);
+                                               for (int j = 0 ; j < n ; j++)
+                                               {
+                                                       alien_addSmallAsteroid(&aliens[i]);
+                                               }
+                                       }
+                               }
+                       }
+
+                       // Adjust the movement even whilst exploding
+                       if ((!(aliens[i].flags & FL_NOMOVE)) &&
+                                       (!(aliens[i].flags & FL_DISABLED)))
+                               alien_move(&aliens[i]);
+
+                       if ((game.area != MISN_ELLESH) || (aliens[i].shield < 0))
+                               aliens[i].x += engine.ssx;
+
+                       aliens[i].x += engine.smx;
+                       aliens[i].y += engine.ssy + engine.smy;
+               }
+       }
+}
+
+static void game_doPlayer()
+{
+       // This causes the motion to slow
+       engine.ssx *= 0.99;
+       engine.ssy *= 0.99;
+
+       engine.smx = 0;
+       engine.smy = 0;
+
+       int shapeToUse;
+       float cd;
+       float cc;
+       bool xmoved = false;
+       bool ymoved = false;
+
+       if (player.shield > -100)
+       {
+               if (player.shield > 0)
+               {
+                       if ((engine.keyState[KEY_FIRE]))
+                               ship_fireBullet(&player, 0);
+
+                       if ((engine.keyState[KEY_ALTFIRE]) && (player.weaponType[1] != W_NONE))
+                       {
+                               if ((player.weaponType[1] != W_CHARGER) &&
+                                       (player.weaponType[1] != W_LASER) && (player.ammo[1] > 0))
+                               {
+                                       ship_fireBullet(&player, 1);
+                               }
+
+                               if (player.weaponType[1] == W_LASER)
+                               {
+                                       if (player.ammo[1] < 100)
+                                       {
+                                               ship_fireBullet(&player, 1);
+
+                                               if (!engine.cheatAmmo)
+                                                       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[KEY_ALTFIRE] &&
+                                       ((game.difficulty == DIFFICULTY_ORIGINAL) ||
+                                               !(engine.keyState[KEY_FIRE])))
+                               {
+                                       if (!player_chargerFired)
+                                       {
+                                               // With ammo cheat, cause the charge cannon to
+                                               // fire at full blast immediately.
+                                               if (engine.cheatAmmo)
+                                                       player.ammo[1] = 200;
+
+                                               if (game.difficulty == DIFFICULTY_ORIGINAL)
+                                               {
+                                                       LIMIT_ADD(player.ammo[1], 1, 0, 200);
+                                               }
+                                               else
+                                               {
+                                                       LIMIT_ADD(player.ammo[1], 1, 0, 150);
+                                                       if (player.ammo[1] >= 150)
+                                                       {
+                                                               ship_fireBullet(&player, 1);
+                                                               player.ammo[1] = 0;
+                                                               player_chargerFired = true;
+                                                       }
+                                               }
+                                       }
+                               }
+                               else
+                               {
+                                       if (player.ammo[1] > 0)
+                                               ship_fireBullet(&player, 1);
+                                       player.ammo[1] = 0;
+                                       player_chargerFired = false;
+                               }
+                       }
+
+                       if ((engine.keyState[KEY_SWITCH]))
+                       {
+                               if ((weapon[W_PLAYER_WEAPON].ammo[0] >= 3) &&
+                                       (weapon[W_PLAYER_WEAPON].ammo[0] <= game.maxPlasmaOutput))
+                               {
+                                       weapon[W_PLAYER_WEAPON].flags ^= WF_SPREAD;
+
+                                       if (weapon[W_PLAYER_WEAPON].flags & WF_SPREAD)
+                                       {
+                                               setInfoLine("Weapon set to Spread", FONT_WHITE);
+                                       }
+                                       else
+                                       {
+                                               setInfoLine("Weapon set to Concentrate", FONT_WHITE);
+                                       }
+                               }
+
+                               engine.keyState[KEY_SWITCH] = 0;
+                       }
+
+                       LIMIT_ADD(player.reload[0], -1, 0, 999);
+                       LIMIT_ADD(player.reload[1], -1, 0, 999);
+
+                       if (engine.keyState[KEY_UP])
+                       {
+                               player.y -= player.speed;
+                               engine.ssy += 0.1;
+                               ymoved = true;
+                       }
+
+                       if (engine.keyState[KEY_DOWN])
+                       {
+                               player.y += player.speed;
+                               engine.ssy -= 0.1;
+                               ymoved = true;
+                       }
+
+                       if (engine.keyState[KEY_LEFT])
+                       {
+                               player.x -= player.speed;
+                               engine.ssx += 0.1;
+                               player.face = 1;
+                               xmoved = true;
+                       }
+
+                       if (engine.keyState[KEY_RIGHT])
+                       {
+                               player.x += player.speed;
+                               engine.ssx -= 0.1;
+                               player.face = 0;
+                               xmoved = true;
+                       }
+
+                       if (engine.keyState[KEY_ESCAPE])
+                       {
+                               if ((engine.done == 0) && (engine.gameSection == SECTION_GAME) &&
+                                       (currentMission.remainingObjectives1 == 0))
+                               {
+                                       audio_playSound(SFX_FLY, screen->w / 2);
+                                       engine.done = 2;
+                                       engine.missionCompleteTimer = (SDL_GetTicks() - 1);
+                               }
+                       }
+
+                       if (engine.keyState[KEY_PAUSE])
+                       {
+                               engine.paused = true;
+                               engine.keyState[KEY_PAUSE] = 0;
+                       }
+
+                       if (((game.area == MISN_ELLESH) &&
+                                       (aliens[ALIEN_BOSS].shield > 0)) ||
+                               (game.area == MISN_MARS))
+                       {
+                               player.face = 0;
+                               xmoved = true;
+                               ymoved = true;
+                       }
+
+                       if (engine.done == 0)
+                       {
+                               if ((game.difficulty != DIFFICULTY_ORIGINAL) ||
+                                       ((game.area != MISN_ELLESH) &&
+                                               (game.area != MISN_MARS)))
+                               {
+                                       if (xmoved)
+                                       {
+                                               if (player.x < xViewBorder)
+                                               {
+                                                       engine.smx += xViewBorder - player.x;
+                                                       player.x = xViewBorder;
+                                               }
+                                               else if (player.x > screen->w - xViewBorder)
+                                               {
+                                                       engine.smx += (screen->w - xViewBorder) - player.x;
+                                                       player.x = screen->w - xViewBorder;
+                                               }
+                                       }
+                                       else if (game.difficulty != DIFFICULTY_ORIGINAL)
+                                       {
+                                               cd = player.x - screen->w / 2;
+                                               if (cd < 0)
+                                               {
+                                                       cc = MAX(cd / 10, MAX(0, engine.ssx) - cameraMaxSpeed);
+                                                       player.x -= cc;
+                                                       engine.smx -= cc;
+                                               }
+                                               else if (cd > 0)
+                                               {
+                                                       cc = MIN(cd / 10, cameraMaxSpeed + MIN(0, engine.ssx));
+                                                       player.x -= cc;
+                                                       engine.smx -= cc;
+                                               }
+                                       }
+
+                                       if (ymoved)
+                                       {
+                                               if (player.y < yViewBorder)
+                                               {
+                                                       engine.smy += yViewBorder - player.y;
+                                                       player.y = yViewBorder;
+                                               }
+                                               else if (player.y > screen->h - yViewBorder)
+                                               {
+                                                       engine.smy += (screen->h - yViewBorder) - player.y;
+                                                       player.y = screen->h - yViewBorder;
+                                               }
+                                       }
+                                       else if (game.difficulty != DIFFICULTY_ORIGINAL)
+                                       {
+                                               cd = player.y - screen->h / 2;
+                                               if (cd < 0)
+                                               {
+                                                       cc = MAX(cd / 10, MAX(0, engine.ssy) - cameraMaxSpeed);
+                                                       player.y -= cc;
+                                                       engine.smy -= cc;
+                                               }
+                                               else if (cd > 0)
+                                               {
+                                                       cc = MIN(cd / 10, cameraMaxSpeed + MIN(0, engine.ssy));
+                                                       player.y -= cc;
+                                                       engine.smy -= cc;
+                                               }
+                                       }
+                               }
+                               else
+                               {
+                                       LIMIT(player.x, xViewBorder, screen->w - xViewBorder);
+                                       LIMIT(player.y, yViewBorder, screen->h - yViewBorder);
+                               }
+                       }
+
+                       if ((player.maxShield <= 1) || (player.shield > engine.lowShield))
+                               explosion_addEngine(&player);
+
+                       shapeToUse = player.face;
+
+                       if (player.hit)
+                               shapeToUse += SHIP_HIT_INDEX;
+
+                       LIMIT_ADD(player.hit, -1, 0, 100);
+
+                       blit(shipShape[shapeToUse], (int)player.x, (int)player.y);
+                       if ((player.maxShield > 1) && (player.shield <= engine.lowShield) &&
+                                       (rand() % 5 < 1))
+                               explosion_add(player.x + RANDRANGE(-10, 10),
+                                       player.y + RANDRANGE(-10, 20), E_SMOKE);
+               }
+               else
+               {
+                       player.active = false;
+                       player.shield--;
+                       if (player.shield == -1)
+                       {
+                               if ((game.hasWingMate1) || (aliens[ALIEN_KLINE].active))
+                                       getPlayerDeathMessage();
+
+                               // Make it look like the ships are all still moving...
+                               if (game.area == MISN_ELLESH)
+                               {
+                                       for (int i = 0 ; i < ALIEN_MAX ; i++)
+                                               aliens[i].flags |= FL_LEAVESECTOR;
+                               }
+
+                               audio_playSound(SFX_DEATH, player.x);
+                               audio_playSound(SFX_EXPLOSION, player.x);
+                       }
+
+                       engine.keyState[KEY_UP] = engine.keyState[KEY_DOWN] = engine.keyState[KEY_LEFT] = engine.keyState[KEY_RIGHT] = 0;
+                       if ((rand() % 3) == 0)
+                               explosion_add(player.x + RANDRANGE(-10, 10),
+                                       player.y + RANDRANGE(-10, 10), E_BIG_EXPLOSION);
+                       if (player.shield == -99)
+                               game_addDebris((int)player.x, (int)player.y, player.maxShield);
+               }
+       }
+
+       LIMIT(engine.ssx, -cameraMaxSpeed, cameraMaxSpeed);
+       LIMIT(engine.ssy, -cameraMaxSpeed, cameraMaxSpeed);
+
+       // Specific for the mission were you have to chase the Executive Transport
+       if ((game.area == MISN_ELLESH) && (aliens[ALIEN_BOSS].shield > 0) &&
+               (player.shield > 0))
+       {
+               engine.ssx = -6;
+               engine.ssy = 0;
+       }
+       
+       if (game.area == MISN_MARS)
+       {
+               engine.ssx = -6;
+               engine.ssy = 0;
+       }
+
+       player.dx = engine.ssx;
+       player.dy = engine.ssy;
+}
+
+static void game_doCargo()
+{
+       float dx, dy, chainX, chainY;
+
+       for (int i = 0 ; i < MAX_CARGO ; i++)
+       {
+               if (cargo[i].active)
+               {
+                       if (!cargo[i].owner->active)
+                       {
+                               cargo_becomeCollectable(i);
+                               continue;
+                       }
+
+                       blit(cargo[i].image[0], (int)cargo[i].x, (int)cargo[i].y);
+
+                       cargo[i].x += engine.ssx + engine.smx;
+                       cargo[i].y += engine.ssy + engine.smy;
+
+                       LIMIT(cargo[i].x, cargo[i].owner->x - 50, cargo[i].owner->x + 50);
+                       LIMIT(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++)
+                       {
+                               blit(shape[30], (int)chainX, (int)chainY);
+                               chainX -= dx;
+                               chainY -= dy;
+                       }
+               }
+       }
+}
+
+static void game_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 + engine.smx;
+                       debris->y += engine.ssy + engine.smy;
+                       debris->x += debris->dx;
+                       debris->y += debris->dy;
+
+                       explosion_add(debris->x + RANDRANGE(-10, 10), debris->y + RANDRANGE(-10, 10), E_BIG_EXPLOSION);
+               }
+
+               if (debris->thinktime < 1)
+               {
+                       prevDebris->next = debris->next;
+                       delete debris;
+                       debris = prevDebris;
+               }
+               else
+               {
+                       prevDebris = debris;
+                       engine.debrisTail = debris;
+               }
+
+       }
+}
+
+/*
+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 game_doExplosions()
+{
+       object *prevExplosion = engine.explosionHead;
+       object *explosion = engine.explosionHead;
+       engine.explosionTail = engine.explosionHead;
+       
+       while (explosion->next != NULL)
+       {
+               explosion = explosion->next;
+
+               if (explosion->active)
+               {
+                       explosion->x += engine.ssx + engine.smx;
+                       explosion->y += engine.ssy + engine.smy;
+
+                       blit(explosion->image[0], (int)explosion->x, (int)explosion->y);
+
+                       if(rand() % 7 == 0)
+                       {
+                               explosion->thinktime -= 7;
+
+                               if(explosion->thinktime < 1)
+                               {
+                                       explosion->active = false;
+                               }
+                               else
+                               {
+                                       explosion->face++;
+                                       explosion->image[0] = shape[explosion->face];
+                               }
+                       }
+               }
+
+               if (explosion->active)
+               {
+                       prevExplosion = explosion;
+                       engine.explosionTail = explosion;
+               }
+               else
+               {
+                       prevExplosion->next = explosion->next;
+                       delete explosion;
+                       explosion = prevExplosion;
+               }
+       }
+}
+
+/*
+Draw an arrow at the edge of the screen for each enemy ship that is not visible.
+*/
+static void game_doArrow(int i)
+{
+       if (i < 0 || !aliens[i].active || aliens[i].shield <= 0 || aliens[i].flags & FL_ISCLOAKED)
+               return;
+
+       int x = aliens[i].x + aliens[i].image[0]->w / 2;
+       int y = aliens[i].y + aliens[i].image[0]->h / 2;
+
+       float sx = fabsf((x - (screen->w / 2)) / (screen->w / 2.0));
+       float sy = fabsf((y - (screen->h / 2)) / (screen->h / 2.0));
+       float sxy = MAX(sx, sy);
+
+       if (sxy < 1) 
+               return;
+
+       x = screen->w / 2 + (x - screen->w / 2) / sxy;
+       y = screen->h / 2 + (y - screen->h / 2) / sxy;
+
+       int arrow;
+
+       if (sxy == sx) {
+               arrow = x < screen->w / 2 ? 42 : 38;
+               x -= x > screen->w / 2 ? shape[arrow]->w : 0;
+               y -= shape[arrow]->h / 2;
+       } else {
+               arrow = y < screen->h / 2 ? 36 : 40;
+               x -= shape[arrow]->w / 2;
+               y -= y > screen->h / 2 ? shape[arrow]->h : 0;
+       }
+
+       blit(shape[arrow], x, y);
+
+       if (i != engine.targetIndex)
+               return;
+
+       if (textShape[3].life > 0)
+               return;
+
+       if (sxy == sx) {
+               x -= x > screen->w / 2 ? 5 + shape[44]->w : -5 - shape[arrow]->w;
+               y -= (shape[44]->h - shape[arrow]->h) / 2;
+       } else {
+               x -= (shape[44]->w - shape[arrow]->w) / 2;
+               y -= y > screen->h / 2 ? 5 + shape[44]->h : -5 - shape[arrow]->h;
+       }
+
+       blit(shape[44], x, y);
+}
+
+static void game_doHud()
+{
+       int shieldColor = 0;
+       SDL_Rect bar;
+       signed char fontColor;
+       char text[25];
+
+       addBuffer(0, 20, 800, 25);
+       addBuffer(0, 550, 800, 34);
+
+       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;
+               blitText(10); // time remaining
+               sprintf(text, "%.2d:%.2d", engine.minutes, engine.seconds);
+               textSurface(30, text, 410, 21, fontColor);
+               blitText(30);
+       }
+
+       if (game.area != MISN_INTERCEPTION)
+       {
+               blitText(9); // mission objectives
+               sprintf(text, "%d", (currentMission.remainingObjectives1 + currentMission.remainingObjectives2));
+               textSurface(39, text, 745, 21, FONT_WHITE);
+               blitText(39);
+       }
+
+       blitText(8); // cash
+       sprintf(text, "%.6d", game.cash);
+       textSurface(38, text, 90, 21, FONT_WHITE);
+       blitText(38);
+
+       for (int i = 0; i < ALIEN_MAX; i++)
+               game_doArrow(i);
+
+       fontColor = FONT_WHITE;
+       if (player.ammo[0] > 0)
+       {
+               if (player.ammo[0] <= 25) fontColor = FONT_YELLOW;
+               if (player.ammo[0] <= 10) fontColor = FONT_RED;
+       }
+       blitText(5); // plasma ammo
+       sprintf(text, "%.3d", player.ammo[0]);
+       textSurface(35, text, 320, 551, fontColor);
+       blitText(35);
+
+       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
+               textSurface(36, text, 465, 551, fontColor);
+               blitText(36);
+       }
+
+       if (((player.weaponType[1] == W_CHARGER) || (player.weaponType[1] == W_LASER)) && (player.ammo[1] > 0))
+       {
+               int c = white;
+               if (player.ammo[1] > 100)
+                       c = 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(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))
+                       {
+                               audio_playSound(SFX_CLOCK, screen->w / 2);
+                       }
+
+                       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 = (SDL_GetTicks() + 1000);
+                       }
+               }
+       }
+
+       for (int i = 0 ; i < 3 ; i++)
+       {
+               if (textShape[i].life > 0)
+               {
+                       textShape[i].y = (525 - (i * 20));
+                       blitText(i);
+                       textShape[i].life--;
+
+                       if (textShape[i].life == 0)
+                       {
+                               copyInfoLine(i + 1, i);
+                               copyInfoLine(i + 2, i + 1);
+                               textShape[2].life = 0;
+                       }
+               }
+       }
+
+       // Show the radio message if there is one
+       if (textShape[3].life > 0)
+       {
+               blit(messageBox, (800 - messageBox->w) / 2, 50);
+               textShape[3].life--;
+       }
+
+       // Do the target's remaining shield (if required)
+       if (game.area != MISN_DORIM)
+       {
+               if ((engine.targetIndex > -1) && (aliens[engine.targetIndex].shield > 0) && (engine.targetIndex > 9))
+               {
+                       blitText(7);
+                       bar.w = 1;
+                       bar.h = 12;
+                       bar.x = 620;
+                       bar.y = 550;
+
+                       for (float i = 0 ; i < (engine.targetShield * aliens[engine.targetIndex].shield) ; i++)
+                       {
+                               if (i > 50)
+                                       shieldColor = green;
+                               else if ((i >= 25) && (i <= 50))
+                                       shieldColor = yellow;
+                               else
+                                       shieldColor = red;
+                               SDL_FillRect(screen, &bar, shieldColor);
+                               bar.x += 2;
+                       }
+               }
+       }
+
+       blitText(11);
+
+       bar.w = 25;
+       bar.h = 12;
+       bar.x = 80;
+       bar.y = 571;
+
+       for (int i = 1 ; i <= 5 ; i++)
+       {
+               if (weapon[W_PLAYER_WEAPON].damage >= i) {
+                       if(i <= game.maxPlasmaDamage || (SDL_GetTicks() % 1000 > (unsigned)i * 100))
+                       {
+                               SDL_FillRect(screen, &bar, green);
+                       }
+               } else if (i <= game.maxPlasmaDamage)
+                       SDL_FillRect(screen, &bar, darkGreen);
+               bar.x += 30;
+       }
+
+       blitText(12);
+
+       bar.w = 25;
+       bar.h = 12;
+       bar.x = 315;
+       bar.y = 571;
+       SDL_FillRect(screen, &bar, yellow);
+
+       for (int i = 1 ; i <= 5 ; i++)
+       {
+               if (weapon[W_PLAYER_WEAPON].ammo[0] >= i) {
+                       if(i <= game.maxPlasmaOutput || (SDL_GetTicks() % 1000 > (unsigned)i * 100))
+                       {
+                               SDL_FillRect(screen, &bar, yellow);
+                       }
+               }
+               else if (i <= game.maxPlasmaOutput)
+                       SDL_FillRect(screen, &bar, darkYellow);
+               bar.x += 30;
+       }
+
+       blitText(13);
+
+       bar.w = 25;
+       bar.h = 12;
+       bar.x = 550;
+       bar.y = 571;
+
+       for (int i = 1 ; i <= 5 ; i++)
+       {
+               if (weapon[W_PLAYER_WEAPON].reload[0] <= rate2reload[i]) {
+                       if(i <= game.maxPlasmaRate || (SDL_GetTicks() % 1000 > (unsigned)i * 100))
+                       {
+                               SDL_FillRect(screen, &bar, blue);
+                       }
+               }
+               else if (i <= game.maxPlasmaRate)
+                       SDL_FillRect(screen, &bar, darkerBlue);
+               bar.x += 30;
+       }
+
+       blitText(4);
+       if (player.shield < 1)
+               return;
+
+       if ((!engine.keyState[KEY_ALTFIRE]) && (player.weaponType[1] == W_LASER) && (engine.eventTimer % 8 == 1))
+               LIMIT_ADD(player.ammo[1], -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 = green;
+               else if ((i >= engine.lowShield) && (i < engine.averageShield))
+                       shieldColor = yellow;
+               else
+                       shieldColor = red;
+               SDL_FillRect(screen, &bar, shieldColor);
+               bar.x += blockSize;
+               if (player.maxShield < 75)
+                       bar.x++;
+       }
+}
+
+/*
+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
+*/
+static bool game_checkPauseRequest()
+{
+       getPlayerInput();
+               
+       if (engine.keyState[KEY_ESCAPE])
+       {
+               engine.paused = false;
+               engine.done = 1;
+               player.shield = 0;
+               return true;
+       }
+       
+       if (engine.keyState[KEY_PAUSE])
+       {
+               engine.paused = false;
+               engine.keyState[KEY_PAUSE] = 0;
+       }
+
+       return false;
+}
+
+int game_mainLoop()
+{
+       resetLists();
+
+       setMission(game.area);
+       missionBriefScreen();
+
+       cargo_init();
+       initPlayer();
+       aliens_init();
+
+       // specific for Phoebe being captured!
+       if (game.area == MISN_NEROD)
+               game.hasWingMate1 = 1;
+
+       if (game.area == MISN_ELAMALE)
+               aliens[ALIEN_KLINE].active = false;
+
+       for (int i = 0 ; i < engine.maxAliens ; i++)
+               alien_add();
+
+       if (game.hasWingMate1)
+               alien_addFriendly(ALIEN_PHOEBE);
+
+       if (game.hasWingMate2)
+               alien_addFriendly(ALIEN_URSULA);
+
+       if ((game.area == MISN_URUSOR) ||
+                       (game.area == MISN_POSWIC) ||
+                       (game.area == MISN_EARTH))
+               alien_addFriendly(ALIEN_SID);
+
+       // Disable Wingmates for certain missions
+       switch (game.area)
+       {
+               case MISN_NEROD:
+               case MISN_URUSOR:
+               case MISN_DORIM:
+               case MISN_SIVEDI:
+               case MISN_ALMARTHA:
+               case MISN_ELLESH:
+               case MISN_MARS:
+               case MISN_VENUS:
+                       aliens[ALIEN_PHOEBE].active = false;
+                       aliens[ALIEN_URSULA].active = false;
+                       break;
+       }
+
+       if (game.area == MISN_DORIM)
+       {
+               aliens[0].collectChance = 100;
+               aliens[0].collectType = P_ESCAPEPOD;
+       }
+
+       // Some specifics for interception missions
+       if (game.area == MISN_INTERCEPTION)
+       {
+               if ((game.system > 1) && ((rand() % 5) == 0))
+               {
+                       aliens[ALIEN_KLINE] = alien_defs[CD_KLINE];
+                       aliens[ALIEN_KLINE].owner = &aliens[ALIEN_KLINE];
+                       aliens[ALIEN_KLINE].target = &player;
+                       aliens[ALIEN_KLINE].active = true;
+                       aliens[ALIEN_KLINE].x = player.x + 1000;
+                       aliens[ALIEN_KLINE].y = player.y;
+                       player_setTarget(ALIEN_KLINE);
+               }
+
+               if ((game.system == 2) && (game.experimentalShield > 0))
+               {
+                       if ((rand() % 5) > 0)
+                       {
+                               aliens[10] = alien_defs[CD_CLOAKFIGHTER];
+                               aliens[10].owner = &aliens[10];
+                               aliens[10].target = &aliens[10];
+                               aliens[10].shield = 1000;
+                               aliens[10].active = true;
+                               aliens[10].x = player.x - 1000;
+                               aliens[10].y = player.y;
+                               player_setTarget(10);
+                               aliens[10].shield = game.experimentalShield;
+                       }
+               }
+       }
+
+       if (game.area == MISN_VENUS)
+       {
+               aliens[ALIEN_KLINE].x = player.x + 1000;
+               aliens[ALIEN_KLINE].y = player.y;
+       }
+
+       for (int i = 0 ; i < ALIEN_MAX ; i++)
+       {
+               aliens[i].systemPower = aliens[i].maxShield;
+               aliens[i].deathCounter = 0 - (aliens[i].maxShield * 3);
+               LIMIT(aliens[i].deathCounter, -350, 0);
+       }
+
+       // Set target energy meter
+       switch (game.area)
+       {
+               case MISN_MOEBO:
+               case MISN_ELAMALE:
+               case MISN_ODEON:
+               case MISN_FELLON:
+               case MISN_POSWIC:
+               case MISN_ELLESH:
+               case MISN_PLUTO:
+               case MISN_NEPTUNE:
+               case MISN_URANUS:
+               case MISN_JUPITER:
+                       player_setTarget(ALIEN_BOSS);
+                       break;
+               case MISN_NEROD:
+                       player_setTarget(ALIEN_PHOEBE);
+                       break;
+               case MISN_ALLEZ:
+                       player_setTarget(ALIEN_GOODTRANSPORT);
+                       break;
+               case MISN_URUSOR:
+                       player_setTarget(ALIEN_SID);
+                       break;
+               case MISN_EARTH:
+               case MISN_VENUS:
+                       player_setTarget(ALIEN_KLINE);
+                       break;
+       }
+
+       clearInfoLines();
+
+       loadScriptEvents();
+
+       engine.ssx = 0;
+       engine.ssy = 0;
+       engine.smx = 0;
+       engine.smy = 0;
+
+       engine.done = 0;
+
+       engine.counter = (SDL_GetTicks() + 1000);
+       engine.counter2 = (SDL_GetTicks() + 1000);
+
+       engine.missionCompleteTimer = 0;
+       engine.musicVolume = 100;
+
+       int rtn = 0;
+
+       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 < ALIEN_MAX ; i++)
+       {
+               if ((aliens[i].active) && (aliens[i].flags & FL_WEAPCO))
+               {
+                       allowableAliens--;
+               }
+       }
+
+       drawBackGround();
+       flushBuffer();
+
+       // Default to no aliens dead...
+       engine.allAliensDead = 0;
+
+       engine.keyState[KEY_FIRE] = 0;
+       engine.keyState[KEY_ALTFIRE] = 0;
+       flushInput();
+
+       while (engine.done != 1)
+       {
+               updateScreen();
+
+               if ((allMissionsCompleted()) && (engine.missionCompleteTimer == 0))
+               {
+                       engine.missionCompleteTimer = SDL_GetTicks() + 4000;
+               }
+
+               if ((missionFailed()) && (engine.missionCompleteTimer == 0))
+               {
+                       if (game.area != MISN_MOEBO)
+                               engine.missionCompleteTimer = SDL_GetTicks() + 4000;
+               }
+
+               if (engine.missionCompleteTimer != 0)
+               {
+                       engine.gameSection = SECTION_INTERMISSION;
+                       if (player.shield > 0)
+                       {
+                               if (SDL_GetTicks() >= engine.missionCompleteTimer)
+                               {
+                                       if ((!missionFailed()) && (game.area != MISN_VENUS))
+                                       {
+                                               leaveSector();
+                                               if ((engine.done == 2) &&
+                                                       (game.area != MISN_DORIM) &&
+                                                       (game.area != MISN_SIVEDI))
+                                               {
+                                                       if ((aliens[ALIEN_PHOEBE].shield > 0) &&
+                                                               (game.area != MISN_EARTH))
+                                                       {
+                                                               aliens[ALIEN_PHOEBE].x = player.x - 40;
+                                                               aliens[ALIEN_PHOEBE].y = player.y - 35;
+                                                               aliens[ALIEN_PHOEBE].face = 0;
+                                                       }
+
+                                                       if ((aliens[ALIEN_URSULA].shield > 0) &&
+                                                               (game.area != MISN_EARTH))
+                                                       {
+                                                               aliens[ALIEN_URSULA].x = player.x - 40;
+                                                               aliens[ALIEN_URSULA].y = player.y + 45;
+                                                               aliens[ALIEN_URSULA].face = 0;
+                                                       }
+
+                                                       if ((game.area == MISN_URUSOR) ||
+                                                               (game.area == MISN_POSWIC))
+                                                       {
+                                                               aliens[ALIEN_SID].x = player.x - 100;
+                                                               aliens[ALIEN_SID].y = player.y;
+                                                               aliens[ALIEN_SID].face = 0;
+                                                       }
+                                               }
+                                       }
+                                       else if ((game.area == MISN_VENUS) &&
+                                               (engine.musicVolume > 0))
+                                       {
+                                               engine.keyState[KEY_UP] = 0;
+                                               engine.keyState[KEY_DOWN] = 0;
+                                               engine.keyState[KEY_LEFT] = 0;
+                                               engine.keyState[KEY_RIGHT] = 0;
+                                               engine.keyState[KEY_FIRE] = 0;
+                                               engine.keyState[KEY_ALTFIRE] = 0;
+                                               LIMIT_ADD(engine.musicVolume, -0.2, 0, 100);
+                                               audio_setMusicVolume(engine.musicVolume);
+                                       }
+                                       else
+                                       {
+                                               engine.done = 1;
+                                       }
+                               }
+                               else
+                               {
+                                       getPlayerInput();
+                               }
+                       }
+                       else
+                       {
+                               LIMIT_ADD(engine.musicVolume, -0.2, 0, 100);
+                               audio_setMusicVolume(engine.musicVolume);
+                               if (SDL_GetTicks() >= engine.missionCompleteTimer)
+                               {
+                                       engine.done = 1;
+                               }
+                       }
+               }
+               else
+               {
+                       getPlayerInput();
+               }
+
+               unBuffer();
+               doStarfield();
+               game_doCollectables();
+               game_doBullets();
+               game_doAliens();
+               game_doPlayer();
+               game_doCargo();
+               game_doDebris();
+               game_doExplosions();
+               game_doHud();
+
+               WRAP_ADD(engine.eventTimer, -1, 0, 60);
+
+               if (engine.paused)
+               {
+                       textSurface(22, "PAUSED", -1, screen->h / 2, FONT_WHITE);
+                       blitText(22);
+                       updateScreen();
+                       audio_pauseMusic();
+
+                       while (engine.paused)
+                       {
+                               engine.done = game_checkPauseRequest();
+                               delayFrame();
+                       }
+
+                       audio_resumeMusic();
+               }
+
+               if ((game.area == MISN_MARS) && (engine.addAliens > -1))
+               {
+                       if ((rand() % 5) == 0)
+                               // XXX: The originally specified range for x was [800, 100],
+                               // which with the original rrand function caused the result
+                               // returned to be `800 + rand() % -699`. Clearly a mistake,
+                               // but I'm not entirely sure what the original intention was.
+                               // For now, I've set the range to [800, 1500], which
+                               // approximately replicates the original's results.
+                               collectable_add(RANDRANGE(800, 1500),
+                                       RANDRANGE(-screen->h / 3, (4 * screen->h) / 3), P_MINE, 25,
+                                       180 + rand() % 60);
+               }
+
+               if (engine.addAliens > -1)
+               {
+                       WRAP_ADD(engine.addAliens, -1, 0, currentMission.addAliens);
+                       if ((engine.addAliens == 0) && (allowableAliens > 0))
+                       {
+                               allowableAliens -= alien_add();
+                       }
+               }
+
+               if ((player.shield <= 0) && (engine.missionCompleteTimer == 0))
+                       engine.missionCompleteTimer = SDL_GetTicks() + 7000;
+
+               // specific to Boss 1
+               if ((game.area == MISN_MOEBO) &&
+                       (aliens[ALIEN_BOSS].flags & FL_ESCAPED))
+               {
+                       audio_playSound(SFX_DEATH, aliens[ALIEN_BOSS].x);
+                       clearScreen(white);
+                       updateScreen();
+                       for (int i = 0 ; i < 300 ; i++)
+                       {
+                               SDL_Delay(10);
+                               if ((rand() % 25) == 0)
+                                       audio_playSound(SFX_EXPLOSION, aliens[ALIEN_BOSS].x);
+                       }
+                       SDL_Delay(1000);
+                       break;
+               }
+
+               delayFrame();
+       }
+
+       flushBuffer();
+
+       if ((player.shield > 0) && (!missionFailed()))
+       {
+               if (game.area < MISN_VENUS)
+                       missionFinishedScreen();
+
+               switch (game.area)
+               {
+                       case MISN_MOEBO:
+                               doCutscene(1);
+                               doCutscene(2);
+                               break;
+                       case MISN_NEROD:
+                               doCutscene(3);
+                               break;
+                       case MISN_ELAMALE:
+                               doCutscene(4);
+                               break;
+                       case MISN_ODEON:
+                               doCutscene(5);
+                               break;
+                       case MISN_ELLESH:
+                               doCutscene(6);
+                               break;
+                       case MISN_VENUS:
+                               doCredits();
+                               break;
+               }
+               
+               if (game.area < MISN_VENUS)
+               {
+                       updateSystemStatus();
+                       saveGame(0);
+               }
+
+               rtn = 1;
+               
+               if (game.area == MISN_VENUS)
+                       rtn = 0;
+       }
+       else
+       {
+               gameover();
+               rtn = 0;
+       }
+
+       exitPlayer();
+
+       return rtn;
+}
diff --git a/src/game.h b/src/game.h
new file mode 100644 (file)
index 0000000..0e05f46
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef GAME_H
+#define GAME_H
+
+extern Game game;
+
+void game_init();
+void game_doExplosions();
+int game_mainLoop();
+
+#endif
diff --git a/src/graphics.cpp b/src/graphics.cpp
new file mode 100644 (file)
index 0000000..b25bf48
--- /dev/null
@@ -0,0 +1,772 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012, 2013 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <ctype.h>
+
+#include "Starfighter.h"
+
+Star star[200];
+
+static unsigned long frameLimit;
+static int thirds;
+
+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_Window *window;
+SDL_Renderer *renderer;
+SDL_Texture *texture;
+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_Surface *messageBox;
+
+bool 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 !(x1<x2 || x3<x0 || y1<y2 || y3<y0);
+}
+
+bool collision(object *object1, object *object2)
+{
+       float x0 = object1->x;
+       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 !(x1<x2 || x3<x0 || y1<y2 || y3<y0);
+}
+
+bool collision(collectables *object1, object *object2)
+{
+       float x0 = object1->x;
+       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<x2 || x3<x0 || y1<y2 || y3<y0);
+}
+
+void initGraphics()
+{
+       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;
+
+       for (int i = 0 ; i < MAX_FONTSHAPES ; i++)
+               fontShape[i] = NULL;
+
+       background = NULL;
+       messageBox = NULL;
+
+       frameLimit = 0;
+       thirds = 0;
+       screen = NULL;
+}
+
+SDL_Surface *setTransparent(SDL_Surface *sprite)
+{
+       SDL_SetColorKey(sprite, SDL_TRUE, 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)
+{
+       // Exit early if image is not on dest at all
+       if (x + image->w < 0 || x >= dest->w || y + image->h < 0 || y >= dest->h)
+               return;
+
+       // Set up a rectangle to draw to
+       SDL_Rect blitRect;
+
+       blitRect.x = x;
+       blitRect.y = y;
+       blitRect.w = image->w;
+       blitRect.h = image->h;
+
+       /* Blit onto the destination surface */
+       if (SDL_BlitSurface(image, NULL, dest, &blitRect) < 0)
+       {
+               printf("BlitSurface error: %s\n", SDL_GetError());
+               showErrorAndExit(2, "");
+       }
+
+       // Only if it is to the screen, mark the region as damaged
+       if (dest == screen)
+               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;
+
+               SDL_Rect blitRect;
+
+               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)
+*/
+static int renderString(const char *in, int x, int y, int fontColor, signed char wrap, SDL_Surface *dest)
+{
+       int i;
+       int splitword;
+       SDL_Rect area;
+       SDL_Rect letter;
+
+       area.x = x;
+       area.y = y;
+       area.w = 8;
+       area.h = 14;
+
+       letter.y = 0;
+       letter.w = 8;
+       letter.h = 14;
+
+       while (*in != '\0')
+       {
+               if (*in != ' ')
+               {
+                       letter.x = (*in - 33);
+                       letter.x *= 8;
+
+                       /* 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 == ' '))
+                       {
+                               area.y += 16;
+                               area.x = x;
+                       }
+                       else if (area.x > (dest->w - 31))
+                       {
+                               splitword = 1;
+                               for (i = 0 ; i < 4 ; i++)
+                               {
+                                       if (!isalpha(*(in + i)))
+                                       {
+                                               splitword = 0;
+                                               break;
+                                       }
+                               }
+
+                               if (splitword)
+                               {
+                                       letter.x = (int)('-') - 33;
+                                       letter.x *= 8;
+                                       if (SDL_BlitSurface(fontShape[fontColor], &letter, dest, &area) < 0)
+                                       {
+                                               printf("BlitSurface error: %s\n", SDL_GetError());
+                                               showErrorAndExit(2, "");
+                                       }
+                                       area.y += 16;
+                                       area.x = x;
+                               }
+                       }
+               }
+
+               in++;
+       }
+
+       return area.y;
+}
+
+int drawString(const 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(const 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(const 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_UpdateTexture(texture, NULL, screen->pixels, screen->w * 4);
+       SDL_RenderCopy(renderer, texture, NULL, NULL);
+       SDL_RenderPresent(renderer);
+}
+
+/*
+ * Delay until the next 60 Hz frame
+ */
+void delayFrame()
+{
+       unsigned long now = SDL_GetTicks();
+
+       // Add 16 2/3 to frameLimit
+       frameLimit += 16;
+       if (thirds >= 2)
+       {
+               thirds = 0;
+       }
+       else
+       {
+               thirds++;
+               frameLimit++;
+       }
+
+       if(now < frameLimit)
+               SDL_Delay(frameLimit - now);
+       else
+               frameLimit = now;
+}
+
+/*
+ * 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 = 2 * 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 = {(int16_t)x, (int16_t)y, (uint16_t)w, (uint16_t)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;
+       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, "");
+       }
+
+       return surface;
+}
+
+SDL_Surface *textSurface(const 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, const char *inString, int x, int y, int fontColor)
+{
+       /* Shortcut: if we already rendered the same string in the same color, don't render it again. */
+       if(textShape[index].text && textShape[index].image && textShape[index].fontColor == fontColor && !strcmp(textShape[index].text, inString)) {
+               textShape[index].x = x;
+               textShape[index].y = y;
+               if (x == -1)
+                       textShape[index].x = (800 - textShape[index].image->w) / 2;
+               return;
+       }
+
+       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_SetSurfaceAlphaMod(surface, 128);
+
+       return surface;
+}
+
+void createMessageBox(SDL_Surface *face, const 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;
+       }
+}
+
+
+SDL_Surface *loadImage(const char *filename)
+{
+       SDL_Surface *image, *newImage;
+
+       image = IMG_Load(filename);
+
+       if (image == NULL) {
+               printf("Couldn't load %s: %s\n", filename, SDL_GetError());
+               showErrorAndExit(0, filename);
+       }
+
+       newImage = SDL_ConvertSurface(image, screen->format, 0);
+       if ( newImage ) {
+               SDL_FreeSurface(image);
+       } else {
+               // This happens when we are loading the window icon image
+               newImage = image;
+       }
+
+       return setTransparent(newImage);
+}
+
+/*
+Simply draws the stars in their positions on screen and moves
+them around.
+*/
+void doStarfield()
+{
+       /* Lock the screen for direct access to the pixels */
+       if (SDL_MUSTLOCK(screen))
+       {
+               if (SDL_LockSurface(screen) < 0 )
+                       showErrorAndExit(2, "");
+       }
+
+       int color = 0;
+
+       SDL_Rect r;
+
+       for (int i = 0 ; i < 200 ; i++)
+       {
+               if (star[i].speed == 3)
+                       color = white;
+               else if (star[i].speed == 2)
+                       color = lightGrey;
+               else if (star[i].speed == 1)
+                       color = darkGrey;
+
+               WRAP_ADD(star[i].x, (engine.ssx + engine.smx) * star[i].speed, 0,
+                       screen->w - 1);
+               WRAP_ADD(star[i].y, (engine.ssy + engine.smy) * star[i].speed, 0,
+                       screen->h - 1);
+
+               putpixel(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;
+
+               addBuffer(r.x, r.y, r.w, r.h);
+       }
+
+       if (SDL_MUSTLOCK(screen))
+       {
+               SDL_UnlockSurface(screen);
+       }
+}
diff --git a/src/graphics.h b/src/graphics.h
new file mode 100644 (file)
index 0000000..3f4e71f
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef GRAPHICS_H
+#define GRAPHICS_H
+
+extern Star star[200];
+
+extern Uint32 red;
+extern Uint32 darkRed;
+extern Uint32 yellow;
+extern Uint32 darkYellow;
+extern Uint32 green;
+extern Uint32 darkGreen;
+extern Uint32 blue;
+extern Uint32 darkBlue;
+extern Uint32 darkerBlue;
+extern Uint32 black;
+extern Uint32 white;
+extern Uint32 lightGrey;
+extern Uint32 darkGrey;
+extern SDL_Window *window;
+extern SDL_Renderer *renderer;
+extern SDL_Texture *texture;
+extern SDL_Surface *screen, *background;
+extern SDL_Surface *shape[MAX_SHAPES];
+extern SDL_Surface *shipShape[MAX_SHIPSHAPES];
+extern SDL_Surface *fontShape[MAX_FONTSHAPES];
+extern SDL_Surface *shopSurface[MAX_SHOPSHAPES];
+extern bRect *bufferHead;
+extern bRect *bufferTail;
+extern textObject textShape[MAX_TEXTSHAPES];
+extern SDL_Surface *messageBox;
+
+
+extern bool collision(float x0, float y0, int w0, int h0, float x2, float y2, int w1, int h1);
+extern bool collision(object *object1, object *object2);
+extern bool collision(collectables *object1, object *object2);
+
+extern void initGraphics();
+extern SDL_Surface *setTransparent(SDL_Surface *sprite);
+extern void addBuffer(int x, int y, int w, int h);
+extern void blit(SDL_Surface *image, int x, int y, SDL_Surface *dest);
+extern void blit(SDL_Surface *image, int x, int y);
+extern void blitText(int i);
+extern void flushBuffer();
+extern void unBuffer();
+extern int drawString(const char *in, int x, int y, int fontColor, signed char wrap, SDL_Surface *dest);
+extern int drawString(const char *in, int x, int y, int fontColor, SDL_Surface *dest);
+extern int drawString(const char *in, int x, int y, int fontColor);
+extern void setColorIndexes();
+extern void drawBackGround();
+extern void clearScreen(Uint32 color);
+extern void updateScreen();
+extern void delayFrame();
+extern void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel);
+extern void drawLine(SDL_Surface *dest, int x1, int y1, int x2, int y2, int col);
+extern void drawLine(int x1, int y1, int x2, int y2, int col);
+extern void circle(int xc, int yc, int R, SDL_Surface *PIX, int col);
+extern void blevelRect(SDL_Surface *dest, int x, int y, int w, int h, Uint8 red, Uint8 green, Uint8 blue);
+extern void blevelRect(int x, int y, int w, int h, Uint8 red, Uint8 green, Uint8 blue);
+extern SDL_Surface *createSurface(int width, int height);
+extern SDL_Surface *textSurface(const char *inString, int color);
+extern void textSurface(int index, const char *inString, int x, int y, int fontColor);
+extern SDL_Surface *alphaRect(int width, int height, Uint8 red, Uint8 green, Uint8 blue);
+extern void createMessageBox(SDL_Surface *face, const char *message, signed char transparent);
+extern void freeGraphics();
+
+extern SDL_Surface *loadImage(const char *filename);
+extern void doStarfield();
+
+#endif
diff --git a/src/init.cpp b/src/init.cpp
new file mode 100644 (file)
index 0000000..89a2665
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012, 2013 Guus Sliepen
+Copyright (C) 2012, 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <errno.h>
+
+/*
+Initalises a whole load of variables
+*/
+void initVars()
+{
+       srand(time(NULL));
+
+       for (int i = 0 ; i < (int)(screen->w * screen->h / 2400) ; i++)
+       {
+               star[i].x = rand() % screen->w;
+               star[i].y = rand() % screen->h;
+               star[i].speed = 1 + (rand() % 3);
+       }
+
+       if (engine.useAudio)
+       {
+               Mix_Volume(-1, 100);
+               Mix_VolumeMusic(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, const char *name)
+{
+       clearScreen(black);
+
+       if (errorId != 2)
+       {
+               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:
+                       sprintf(string, "%s was not found in the Starfighter data package", name);
+                       drawString(string, -1, 250, FONT_WHITE);
+                       drawString("Please try again. If this error persists, contact the authors", -1, 275, FONT_WHITE);
+                       drawString("or reinstall the game", -1, 300, FONT_WHITE);
+                       break;
+               case 1:
+                       drawString("Project: Starfighter encountered an error whilst", -1, 250, FONT_WHITE);
+                       drawString("attempting to load game data. Please try running", -1, 275, FONT_WHITE);
+                       drawString("the game again. If the errors persist, reinstall the game", -1, 300, FONT_WHITE);
+                       break;
+               case 2:
+                       drawString("Project: Starfighter encountered a critical error", -1, 250, FONT_WHITE);
+                       drawString("while attempting to perform a required program function.", -1, 275, FONT_WHITE);
+                       drawString("Please contact the authors with details.", -1, 300, FONT_WHITE);
+                       break;
+       }
+
+       drawString("Project: Starfighter will now exit", -1, 450, FONT_WHITE);
+       drawString("Press Space to continue", -1, 475, FONT_WHITE);
+
+       updateScreen();
+
+       engine.keyState[KEY_ALTFIRE] = 0;
+
+       while (!engine.keyState[KEY_ALTFIRE])
+       {
+               getPlayerInput();
+               delayFrame();
+       }
+
+       exit(1);
+}
+
+/*
+This gets the user's home directory, then creates the .parallelrealities
+and .parallelrealities/starfighter directories so that saves and
+temporary data files can be written there.
+*/
+static void setupUserHomeDirectory()
+{
+       const char *userHome;
+
+       if ((userHome = getenv("HOME")) == NULL)
+               userHome = getpwuid(getuid())->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);
+}
+
+/*
+Chugg chugg chugg.... brrr... chugg chugg chugg...brrrrrr... chugg ch..
+BRRRRRRRRRRRRRRRRRMMMMMMMMMMMMMMMMMMM!! Well, hopefully anyway! ;)
+*/
+void initSystem()
+{
+       strcpy(engine.userHomeDirectory, "");
+
+       setupUserHomeDirectory();
+
+       /* Initialize the SDL library */
+       if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_JOYSTICK) < 0)
+       {
+               printf("Couldn't initialize SDL: %s\n", SDL_GetError());
+               exit(1);
+       }
+
+       char filename[PATH_MAX];
+       int fullScreen = 0;
+       int useSound = 1;
+       int useMusic = 1;
+       int autoPause = 0;
+
+       FILE *fp;
+       sprintf(filename, "%sconf", engine.userHomeDirectory);
+       fp = fopen(filename, "rb");
+
+       if (fp != NULL)
+       {
+               if (fscanf(fp, "%d %d %d %d", &fullScreen, &useSound, &useMusic, &autoPause) < 4)
+                       printf("Warning: Config file \"%s\" is not correctly formatted\n", filename);
+               fclose(fp);
+       }
+
+       engine.fullScreen = fullScreen;
+       engine.useSound = useSound;
+       engine.useMusic = useMusic;
+       engine.autoPause = autoPause;
+
+       screen = SDL_CreateRGBSurface(0, screenWidth, screenHeight, 32, 0xff0000, 0xff00, 0xff, 0xff000000);
+
+       if (!screen)
+       {
+               printf("Couldn't create %ix%ix32 surface: %s\n", screenWidth, screenHeight, SDL_GetError());
+               exit(1);
+       }
+
+       window = SDL_CreateWindow("Project: Starfighter", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen->w, screen->h, 0);
+
+       if (window == NULL)
+       {
+               printf("Could not create window: %s\n", SDL_GetError());
+               exit(1);
+       }
+
+       SDL_SetWindowIcon(window, loadImage("gfx/alienDevice.png"));
+       SDL_SetWindowFullscreen(window, engine.fullScreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
+
+       renderer = SDL_CreateRenderer(window, -1, 0);
+
+       if (!renderer)
+       {
+               printf("Could not create renderer: %s\n", SDL_GetError());
+               exit(1);
+       }
+
+       SDL_RenderSetLogicalSize(renderer, screen->w, screen->h);
+
+       texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, screen->w, screen->h);
+
+       if (!texture)
+       {
+               printf("Couldn't create %ix%ix32 texture: %s\n", screen->w, screen->h, SDL_GetError());
+               exit(1);
+       }
+
+       if (engine.useAudio)
+       {
+               if (Mix_OpenAudio(44100, AUDIO_S16, engine.useAudio * 2, 1024) < 0)
+               {
+                       printf("Warning: Couldn't set 44100 Hz 16-bit stereo audio - Reason:\n%s\n", Mix_GetError());
+                       printf("Sound and Music will be disabled\n");
+                       engine.useAudio = false;
+               }
+       }
+
+       SDL_ShowCursor(SDL_DISABLE);
+       SDL_EventState(SDL_MOUSEMOTION, SDL_DISABLE);
+
+       SDL_JoystickEventState(SDL_ENABLE);
+       SDL_JoystickOpen(0);
+}
+
+/*
+Removes [hopefully] all the resources that has been
+loaded and created during the game. This is called by
+atexit();
+*/
+void cleanUp()
+{
+       freeGraphics();
+       SDL_FreeSurface(background);
+       audio_free();
+       resetLists();
+       delete(engine.bulletHead);
+       delete(engine.explosionHead);
+       delete(engine.collectableHead);
+       delete(bufferHead);
+
+       for (int i = 0 ; i < MAX_FONTSHAPES ; i++)
+       {
+               if (fontShape[i] != NULL)
+                       SDL_FreeSurface(fontShape[i]);
+       }
+
+       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)
+       {
+               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 %d\n", engine.fullScreen, engine.useSound,
+                       engine.useMusic, engine.autoPause);
+               fclose(fp);
+       }
+       else
+       {
+               printf("Error saving config\n");
+       }
+
+       SDL_Quit();
+       printf("Thank You for playing Starfighter\n");
+}
+
diff --git a/src/init.h b/src/init.h
new file mode 100644 (file)
index 0000000..a58848c
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef INIT_H
+#define INIT_H
+
+extern void initVars();
+extern void showErrorAndExit(int errorId, const char *name);
+extern void initSystem();
+extern void cleanUp();
+
+#endif
diff --git a/src/intermission.cpp b/src/intermission.cpp
new file mode 100644 (file)
index 0000000..cba6975
--- /dev/null
@@ -0,0 +1,967 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012, 2013 Guus Sliepen
+Copyright (C) 2012, 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+/*
+Drives the cursor. Is used by some other screens too
+*/
+static void intermission_doCursor()
+{
+       getPlayerInput();
+
+       LIMIT(engine.cursor_x, 10, screen->w - 10 - shape[0]->w);
+       LIMIT(engine.cursor_y, 10, screen->h - 10 - shape[0]->h);
+       blit(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.
+*/
+static void intermission_setStatusLines()
+{
+       char string[50];
+
+       sprintf(string, "System : %s", systemNames[game.system]);
+
+       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++)
+               textSurface(i, "", 0, 0, FONT_WHITE);
+
+       sprintf(string, "Missions Completed : %d/%d", completed, total);
+       textSurface(1, string, 0, 0, FONT_WHITE);
+
+       sprintf(string, "Shots Fired : %d", game.shots);
+       textSurface(2, string, 0, 0, FONT_WHITE);
+
+       sprintf(string, "Hits Scored : %d", game.hits);
+       textSurface(3, string, 0, 0, FONT_WHITE);
+
+       sprintf(string, "Accuracy : %d%%", game.accuracy);
+       textSurface(4, string, 0, 0, FONT_WHITE);
+
+       sprintf(string, "Enemies Killed by Others : %d", game.totalOtherKills);
+       textSurface(5, string, 0, 0, FONT_WHITE);
+
+       sprintf(string, "Total Cash Earned : %d", game.cashEarned);
+       textSurface(6, string, 0, 0, FONT_WHITE);
+
+       textSurface(7, "*** Chris ***", 0, 0, FONT_WHITE);
+
+       sprintf(string, "Enemies Killed : %d", game.totalKills);
+       textSurface(8, string, 0, 0, FONT_WHITE);
+
+       sprintf(string, "Shield Restores Picked Up : %d", game.shieldPickups);
+       textSurface(9, string, 0, 0, FONT_WHITE);
+
+       sprintf(string, "Plasma Cells Picked Up : %d", game.cellPickups);
+       textSurface(10, string, 0, 0, FONT_WHITE);
+
+       sprintf(string, "Rockets Picked Up : %d", game.rocketPickups);
+       textSurface(11, string, 0, 0, FONT_WHITE);
+
+       sprintf(string, "Powerups Picked Up : %d", game.rocketPickups);
+       textSurface(12, string, 0, 0, FONT_WHITE);
+
+       sprintf(string, "Mines Destroyed : %d", game.minesKilled);
+       textSurface(13, string, 0, 0, FONT_WHITE);
+
+       sprintf(string, "Slaves Rescued : %d", game.slavesRescued);
+       textSurface(14, string, 0, 0, FONT_WHITE);
+
+       sprintf(string, "Cargo Picked Up : %d", game.cargoPickups);
+       textSurface(15, string, 0, 0, FONT_WHITE);
+
+       if (game.hasWingMate1)
+       {
+               textSurface(16, "*** Phoebe ***", 0, 0, FONT_WHITE);
+
+               sprintf(string, "Enemies Killed : %d", game.wingMate1Kills);
+               textSurface(17, string, 0, 0, FONT_WHITE);
+
+               sprintf(string, "Ejections : %d", game.wingMate1Ejects);
+               textSurface(18, string, 0, 0, FONT_WHITE);
+       }
+
+       if (game.hasWingMate2)
+       {
+               textSurface(19, "*** Ursula ***", 0, 0, FONT_WHITE);
+
+               sprintf(string, "Enemies Killed : %d", game.wingMate2Kills);
+               textSurface(20, string, 0, 0, FONT_WHITE);
+
+               sprintf(string, "Ejections : %d", game.wingMate2Ejects);
+               textSurface(21, string, 0, 0, FONT_WHITE);
+       }
+
+       signed char percentage = 0;
+       if ((game.secondaryMissions > 0) && (game.secondaryMissionsCompleted > 0))
+               percentage = (game.secondaryMissionsCompleted / game.secondaryMissions) * 100;
+       sprintf(string, "Seconday Missions Completed : %d / %d (%d%%)", game.secondaryMissionsCompleted, game.secondaryMissions, percentage);
+       textSurface(24, string, 0, 0, FONT_WHITE);
+
+       int timeTaken = game.timeTaken;
+
+       snprintf(string, sizeof string, "Total Time : %2d:%02d:%02d", timeTaken / 3600, (timeTaken / 60) % 60, timeTaken % 60);
+
+       textSurface(26, string, -1, 0, FONT_WHITE);
+       textSurface(27, "Current Status", -1, 0, FONT_WHITE);
+
+       textShape[0].y = 400;
+       textShape[0].x = 150;
+
+       for (int i = 1 ; i < 25 ; i++)
+       {
+               textShape[i].y = textShape[i - 1].y + 20;
+               if ((i == 7) || (i == 16) || (i == 19))
+                       textShape[i].y += 25;
+
+               textShape[i].x = 150;
+       }
+
+       textShape[26].y = 404;
+       textShape[27].y = 83;
+}
+
+/*
+Sets the names and stats of the planets within the current system.
+*/
+static void intermission_setSystemPlanets()
+{
+       FILE *fp;
+
+       char string[100];
+       strcpy(string, "");
+
+       switch (game.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;
+       }
+
+       fp = fopen(string, "rb");
+
+       int distance;
+       char name[50];
+       int image;
+
+       for (int i = 0 ; i < 10 ; i++)
+       {
+               if (fscanf(fp, "%d %s %d", &distance, name, &image) < 3)
+               {
+                       printf("Warning: Data for planet %i in \"%s\" is not correctly formatted\n", i, string);
+                       break;
+               }
+
+               systemPlanet[i].y = distance;
+               strcpy(systemPlanet[i].name, name);
+               systemPlanet[i].image = shape[image];
+       }
+
+       int messageMission;
+       int messageSlot;
+       char face[50];
+       char from[100];
+       char subject[100];
+
+       for (int i = 0 ; i < 10 ; i++)
+       {
+               if ((fscanf(fp, "%d %d %s%*c", &messageMission, &messageSlot, face) < 3) ||
+                       (fscanf(fp, "%[^\n]%*c", from) < 1) ||
+                       (fscanf(fp, "%[^\n]%*c", subject) < 1))
+               {
+                       printf("Warning: Mission data for planet %i in \"%s\" is not correctly formatted\n", i, string);
+                       break;
+               }
+
+               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 intermission_setSystemPlanets(). Moving the cursor over the planet
+will show their name and their current status
+*/
+static bool intermission_showSystem(float x, float y, bool selectable)
+{
+       SDL_Rect r;
+       signed char planet = 0;
+       int planetSpace = systemPlanet[planet].y;
+       bool rtn = false;
+
+       // Blit the sun
+       blit(shape[30], 370, 220);
+
+       for (int i = 50 ; i < 300 ; i+= planetSpace)
+       {
+               x *= 0.75;
+               y *= 0.75;
+
+               circle(400, 250, i, screen, darkGrey);
+
+               r.x = int(400 + (sinf(x) * i));
+               r.y = int(250 + (cosf(y) * i));
+               r.w = 10;
+               r.h = 10;
+
+               r.x -= (systemPlanet[planet].image->w / 2);
+               r.y -= (systemPlanet[planet].image->h / 2);
+               blit(systemPlanet[planet].image, r.x, r.y);
+
+               if (selectable &&
+                       collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6,
+                               r.x, r.y, systemPlanet[planet].image->w,
+                               systemPlanet[planet].image->h))
+               {
+                       drawString(systemPlanet[planet].name, -1, 545, FONT_WHITE);
+                       if ((engine.keyState[KEY_FIRE]))
+                       {
+                               if (game.system == 0)
+                               {
+                                       game.stationedPlanet = planet;
+                                       game.destinationPlanet = planet;
+                                       game.area = systemPlanet[game.stationedPlanet].missionNumber;
+                                       strcpy(game.stationedName, systemPlanet[game.stationedPlanet].name);
+                                       saveGame(0);
+                               }
+                               else
+                               {
+                                       game.destinationPlanet = planet;
+                                       strcpy(game.destinationName, systemPlanet[game.destinationPlanet].name);
+                               }
+
+                               rtn = true;
+                               engine.keyState[KEY_FIRE] = 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).
+*/
+static void intermission_showStatus(SDL_Surface *infoSurface)
+{
+       float speed = 0.25;
+
+       if(engine.keyState[KEY_DOWN])
+               speed = 1;
+       else if(engine.keyState[KEY_UP])
+               speed = -1;
+
+       blit(infoSurface, 100, 80);
+
+       for (int i = 0 ; i < 22 ; i++)
+       {
+               textShape[i].y -= speed;
+               if ((textShape[i].y > 80) && (textShape[i].y < 400))
+                       blitText(i);
+       }
+
+       if (textShape[21].y < 65)
+       {
+               textShape[0].y = 400;
+
+               for (int i = 1 ; i < 25 ; i++)
+               {
+                       textShape[i].y = textShape[i - 1].y + 20;
+                       if ((i == 7) || (i == 16) || (i == 19))
+                               textShape[i].y += 25;
+               }
+       }
+
+       blevelRect(100, 80, 600, 20, 0x00, 0x00, 0x99);
+
+       blevelRect(100, 400, 600, 20, 0x00, 0x00, 0x99);
+
+       blitText(26);
+       blitText(27);
+}
+
+static void intermission_updateCommsSurface(SDL_Surface *comms)
+{
+       if (engine.commsSection == 1)
+               return;
+
+       char string[255];
+
+       blevelRect(comms, 0, 10, comms->w - 1, 55, 0x00, 0x22, 0x00);
+       blit(shape[FACE_CHRIS], 20, 15, comms);
+       drawString("Chris Bainfield", 80, 15, FONT_WHITE, comms);
+       sprintf(string, "Current Location: %s", systemPlanet[game.stationedPlanet].name);
+       drawString(string, 80, 35, FONT_WHITE, comms);
+}
+
+static void intermission_createCommsSurface(SDL_Surface *comms)
+{
+       engine.commsSection = 0;
+
+       blevelRect(comms, 0, 0, comms->w - 1, comms->h - 1, 0x00, 0x00, 0x25);
+
+       drawString("+++ RECEIVED 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;
+                       blevelRect(comms, 0, 105 + yOffset, comms->w - 1, 55, 0x00, 0x00, 0x77);
+                       blit(shape[systemPlanet[i].faceImage], 20, 110 + yOffset, comms);
+                       drawString(systemPlanet[i].from, 80, 110 + yOffset, FONT_WHITE, comms);
+                       drawString(systemPlanet[i].subject, 80, 130 + yOffset, FONT_CYAN, comms);
+                       drawString("INCOMPLETE", 350, 110 + yOffset, FONT_RED, comms);
+               }
+       }
+
+       intermission_updateCommsSurface(comms);
+}
+
+static void intermission_createMissionDetailSurface(SDL_Surface *comms, int missionSlot)
+{
+       char name[50];
+       char string[2000];
+       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))
+               {
+                       mission = systemPlanet[i].messageMission;
+               }
+       }
+
+       if (mission == -1)
+               return;
+
+       blevelRect(comms, 0, 0, comms->w - 1, comms->h - 1, 0x00, 0x00, 0x25);
+
+       sprintf(string, "data/brief%d.txt", mission);
+
+       fp = fopen(string, "rb");
+
+       if (fscanf(fp, "%[^\n]%*c", name) < 1)
+       {
+               printf("Warning: Failed to retrieve name from \"%s\"\n", string);
+               strcpy(name, "Error");
+       }
+       sprintf(string, "+++ Communication with %s +++", name);
+       drawString(string, -1, 20, FONT_GREEN, comms);
+
+       while (fscanf(fp, "%[^\n]%*c", string) == 1)
+       {
+               faceNumber = getFace(string);
+               if (faceNumber > -1)
+               {
+                       blit(shape[faceNumber], 10, y, comms);
+                       col = FONT_WHITE;
+               }
+               else
+               {
+                       newY = drawString(string, 80, y, col, 1, comms) + 25;
+                       if (newY < y + 60)
+                               newY += (60 - (newY - y));
+                       y = newY;
+               }
+       }
+
+       fclose(fp);
+
+       blevelRect(comms, 5, comms->h - 28, 180, 20, 0x25, 0x00, 0x00);
+       drawString("RETURN TO MESSAGES", 15, comms->h - 25, FONT_WHITE, 1, comms);
+
+       engine.commsSection = 1;
+}
+
+static void intermission_doComms(SDL_Surface *comms)
+{
+       if ((engine.keyState[KEY_FIRE]))
+       {
+               if (engine.commsSection == 0)
+               {
+                       for (int i = 0 ; i < 4 ; i++)
+                       {
+                               if (collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 170, 180 + (i * 60), 430, 50))
+                               {
+                                       intermission_createMissionDetailSurface(comms, i);
+                                       engine.keyState[KEY_FIRE] = 0;
+                               }
+                       }
+               }
+               else
+               {
+                       if (collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 170, 440, 160, 20))
+                       {
+                               intermission_createCommsSurface(comms);
+                               engine.keyState[KEY_FIRE] = 0;
+                       }
+               }
+       }
+}
+
+static void intermission_createOptions(SDL_Surface *optionsSurface)
+{
+       SDL_FillRect(optionsSurface, NULL, black);
+
+       blevelRect(optionsSurface, 0, 0, optionsSurface->w - 2, optionsSurface->h - 2, 0x00, 0x00, 0x44);
+
+       drawString("++ OPTIONS ++", 105, 8, FONT_WHITE, optionsSurface);
+
+       blevelRect(optionsSurface, 190, 45, 50, 22, 0x00, 0x00, 0x00);
+       blevelRect(optionsSurface, 250, 45, 50, 22, 0x00, 0x00, 0x00);
+       blevelRect(optionsSurface, 20, 45, 150, 22, 0x00, 0x00, 0x00);
+       if (engine.useSound)
+               blevelRect(optionsSurface, 190, 45, 50, 22, 0xff, 0x00, 0x00);
+       else
+               blevelRect(optionsSurface, 250, 45, 50, 22, 0xff, 0x00, 0x00);
+       drawString("ON", 207, 50, FONT_WHITE, optionsSurface);
+       drawString("OFF", 263, 50, FONT_WHITE, optionsSurface);
+       drawString("SOUND", 30, 50, FONT_WHITE, optionsSurface);
+
+       blevelRect(optionsSurface, 190, 95, 50, 22, 0x00, 0x00, 0x00);
+       blevelRect(optionsSurface, 250, 95, 50, 22, 0x00, 0x00, 0x00);
+       blevelRect(optionsSurface, 20, 95, 150, 22, 0x00, 0x00, 0x00);
+       if (engine.useMusic)
+               blevelRect(optionsSurface, 190, 95, 50, 22, 0xff, 0x00, 0x00);
+       else
+               blevelRect(optionsSurface, 250, 95, 50, 22, 0xff, 0x00, 0x00);
+       drawString("ON", 207, 100, FONT_WHITE, optionsSurface);
+       drawString("OFF", 263, 100, FONT_WHITE, optionsSurface);
+       drawString("MUSIC", 30, 100, FONT_WHITE, optionsSurface);
+
+       blevelRect(optionsSurface, 190, 145, 50, 22, 0x00, 0x00, 0x00);
+       blevelRect(optionsSurface, 250, 145, 50, 22, 0x00, 0x00, 0x00);
+       blevelRect(optionsSurface, 20, 145, 150, 22, 0x00, 0x00, 0x00);
+       if (engine.fullScreen)
+               blevelRect(optionsSurface, 190, 145, 50, 22, 0xff, 0x00, 0x00);
+       else
+               blevelRect(optionsSurface, 250, 145, 50, 22, 0xff, 0x00, 0x00);
+       drawString("ON", 207, 150, FONT_WHITE, optionsSurface);
+       drawString("OFF", 263, 150, FONT_WHITE, optionsSurface);
+       drawString("FULLSCREEN", 30, 150, FONT_WHITE, optionsSurface);
+}
+
+static void intermission_doOptions(SDL_Surface *optionsSurface)
+{
+       if ((engine.keyState[KEY_FIRE]))
+       {
+               if (collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 417, 172, 45, 22))
+                       engine.useSound = true;
+
+               if (collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 478, 172, 45, 22))
+                       engine.useSound = false;
+
+
+               if (collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 417, 222, 45, 22))
+               {
+                       engine.useMusic = true;
+                       audio_playMusic("music/through_space.ogg", -1);
+               }
+
+               if (collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 478, 222, 45, 22))
+               {
+                       engine.useMusic = false;
+                       audio_haltMusic();
+               }
+
+               if (collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 417, 272, 45, 22))
+               {
+                       if (!engine.fullScreen)
+                       {
+                               SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
+                               engine.fullScreen = true;
+                       }
+               }
+
+               if (collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 478, 272, 45, 22))
+               {
+                       if (engine.fullScreen)
+                       {
+                               SDL_SetWindowFullscreen(window, 0);
+                               engine.fullScreen = false;
+                       }
+               }
+
+               intermission_createOptions(optionsSurface);
+       }
+}
+
+/*
+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 intermission()
+{
+       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;
+
+       clearScreen(black);
+       updateScreen();
+       clearScreen(black);
+
+       initSaveSlots();
+
+       loadBackground(systemBackground[game.system]);
+
+       char string[25];
+
+       engine.cursor_x = screen->w / 2;
+       engine.cursor_y = screen->h / 2;
+       shape[0] = loadImage("gfx/cursor.png");
+
+       // Icons 1 - 29
+       for (int i = 0 ; i < 26 ; i++)
+       {
+               sprintf(string, "gfx/icon%d.png", (i + 1));
+               shape[i + 1] = loadImage(string);
+       }
+
+       shape[27] = loadImage("gfx/buyIcon.png");
+       shape[28] = loadImage("gfx/sellIcon.png");
+       shape[29] = loadImage("gfx/firefly1.png");
+
+       // Planets 30 - 39
+       shape[30] = loadImage("gfx/planet_sun.png");
+       shape[31] = loadImage("gfx/planet_green.png");
+       shape[32] = loadImage("gfx/planet_blue.png");
+       shape[33] = loadImage("gfx/planet_red.png");
+       shape[34] = loadImage("gfx/planet_orange.png");
+
+       // Faces (as defines)
+       shape[FACE_CHRIS] = loadImage("gfx/face_chris.png");
+       shape[FACE_SID] = loadImage("gfx/face_sid.png");
+       shape[FACE_KRASS] = loadImage("gfx/face_krass.png");
+       shape[FACE_PHOEBE] = loadImage("gfx/face_phoebe.png");
+       shape[FACE_URSULA] = loadImage("gfx/face_ursula.png");
+       shape[FACE_KLINE] = loadImage("gfx/face_kline.png");
+
+       engine.done = 0;
+       engine.keyState[KEY_FIRE] = 0;
+       engine.ssx = 0;
+       engine.ssy = 0;
+
+       SDL_Rect r;
+       SDL_Rect destRect;
+       int distance = 0;
+       int interceptionChance = 0;
+
+       intermission_setStatusLines();
+       initShop();
+       intermission_setSystemPlanets();
+
+       SDL_Surface *statsSurface = alphaRect(600, 330, 0x00, 0x00, 0x99);
+       SDL_Surface *savesSurface = createSurface(350, 300);
+       SDL_Surface *optionsSurface = createSurface(320, 240);
+       SDL_Surface *commsSurface = createSurface(450, 400);
+
+       createSavesSurface(savesSurface, -1);
+       intermission_createOptions(optionsSurface);
+       intermission_createCommsSurface(commsSurface);
+
+       signed char section = 1;
+
+       float sinX = 300;
+       float cosY = 300;
+       bool movePlanets = true;
+       signed char saveSlot = -1;
+
+       // Remove the Supercharge, if it is there
+       if ((game.difficulty != DIFFICULTY_EASY) &&
+               (game.difficulty != DIFFICULTY_ORIGINAL))
+       {
+               weapon[W_PLAYER_WEAPON].reload[0] = MAX(
+                       weapon[W_PLAYER_WEAPON].reload[0],
+                       rate2reload[game.maxPlasmaRate]);
+               weapon[W_PLAYER_WEAPON].ammo[0] = MIN(weapon[W_PLAYER_WEAPON].ammo[0],
+                       game.maxPlasmaOutput);
+               weapon[W_PLAYER_WEAPON].damage = MIN(weapon[W_PLAYER_WEAPON].damage,
+                       game.maxPlasmaDamage);
+       }
+
+       if (game.system > 0)
+               interceptionChance = (300 / game.system);
+
+       // There is no chance of being interceptted after the final attack on Earth
+       if ((game.system == 3) && (systemPlanet[2].missionCompleted))
+               interceptionChance = 0;
+
+       int rtn = 0;
+
+       if ((engine.useAudio) && (engine.useMusic))
+               audio_playMusic("music/through_space.ogg", -1);
+
+       textObject iconInfo[12];
+
+       iconInfo[0].image = textSurface("Start Next Mission", FONT_WHITE);
+       iconInfo[1].image = textSurface("View System Map", FONT_WHITE);
+       iconInfo[2].image = textSurface("Current Status", FONT_WHITE);
+       iconInfo[3].image = textSurface("Save Game", FONT_WHITE);
+       iconInfo[4].image = textSurface("Upgrade FIREFLY", FONT_WHITE);
+       iconInfo[5].image = textSurface("Comms", FONT_WHITE);
+       iconInfo[6].image = textSurface("Options", FONT_WHITE);
+       iconInfo[7].image = textSurface("Exit to Title Screen", FONT_WHITE);
+
+       sprintf(string, "System : %s", systemNames[game.system]);
+       iconInfo[8].image = textSurface(string, FONT_WHITE);
+
+       sprintf(string, "Stationed At: %s", systemPlanet[game.stationedPlanet].name);
+       iconInfo[9].image = textSurface(string, FONT_WHITE);
+
+       if (game.destinationPlanet > -1)
+               sprintf(string, "Destination: %s", systemPlanet[game.destinationPlanet].name);
+       else
+               strcpy(string, "Destination: None");
+       iconInfo[10].image = textSurface(string, FONT_WHITE);
+       for (int i = 0 ; i < 9 ; i++)
+               iconInfo[i].x = (800 - iconInfo[i].image->w) / 2;
+
+       iconInfo[11].image = textSurface("Go to Destination Planet", FONT_WHITE);
+
+       bool redrawBackGround = true;
+
+       if (game.distanceCovered > 0)
+               section = 0;
+       else
+               player.shield = player.maxShield;
+
+       flushInput();
+       engine.keyState[KEY_FIRE] = engine.keyState[KEY_ALTFIRE] = 0;
+       engine.done = 0;
+
+       while (!engine.done)
+       {
+               updateScreen();
+
+               if (redrawBackGround)
+               {
+                       drawBackGround();
+                       redrawBackGround = false;
+               }
+               else
+               {
+                       unBuffer();
+               }
+
+               doStarfield();
+
+               r.x = 0;
+               r.y = 0;
+               r.h = screen->h;
+               r.w = 1;
+               for (int i = 40 ; i < screen->w ; i+= 40)
+               {
+                       r.x = i;
+                       SDL_FillRect(screen, &r, darkerBlue);
+               }
+
+               r.x = 0;
+               r.y = 0;
+               r.h = 1;
+               r.w = screen->w;
+               for (int i = 40 ; i < screen->h ; i+= 40)
+               {
+                       r.y = i;
+                       SDL_FillRect(screen, &r, darkerBlue);
+               }
+
+
+               if (rand() % 1000 < 2)
+               {
+                       // XXX: This code originally had the range set to [100, 100],
+                       // which effectively always caused the star speed to be set to
+                       // 1. I don't think this was the intention, so I changed the
+                       // minimum from 100 to -100, which is what I think was probably
+                       // intended.
+                       engine.ssx = RANDRANGE(-100, 100);
+                       engine.ssy = RANDRANGE(-100, 100);
+                       engine.ssx /= 100;
+                       engine.ssy /= 100;
+               }
+
+               blit(iconInfo[8].image, (int)iconInfo[8].x, 15);
+
+               switch(section)
+               {
+                       case 0:
+                               if (game.stationedPlanet == game.destinationPlanet)
+                               {
+                                       game.area = systemPlanet[game.stationedPlanet].missionNumber;
+                                       rtn = 2;
+                                       engine.done = 1;
+                               }
+                               else
+                               {
+                                       distance = abs(game.stationedPlanet - game.destinationPlanet);
+                                       distance = (5 / distance);
+                                       if (distance < 1)
+                                               distance = 1;
+
+                                       SDL_FreeSurface(iconInfo[9].image);
+                                       iconInfo[9].image = textSurface(systemPlanet[game.stationedPlanet].name, FONT_WHITE);
+
+                                       SDL_FreeSurface(iconInfo[10].image);
+                                       iconInfo[10].image = textSurface(systemPlanet[game.destinationPlanet].name, FONT_WHITE);
+
+                                       section = 8;
+
+                                       destRect.x = 180;
+                                       destRect.y = 450;
+                                       destRect.w = 1;
+                                       if (game.distanceCovered > 0)
+                                               destRect.w = game.distanceCovered;
+                                       destRect.h = 20;
+                               }
+                               break;
+
+                       case 1:
+                               if (engine.keyState[KEY_ALTFIRE])
+                               {
+                                       movePlanets = !movePlanets;
+                                       engine.keyState[KEY_ALTFIRE] = 0;
+                               }
+
+                               if (movePlanets)
+                               {
+                                       sinX += 0.01;
+                                       cosY += 0.01;
+                               }
+
+                               if (intermission_showSystem(sinX, cosY, true))
+                               {
+                                       if (game.system == 0)
+                                       {
+                                               sprintf(string, "Stationed At: %s", systemPlanet[game.stationedPlanet].name);
+                                               SDL_FreeSurface(iconInfo[9].image);
+                                               iconInfo[9].image = textSurface(string, FONT_WHITE);
+                                               intermission_updateCommsSurface(commsSurface);
+                                       }
+                                       else
+                                       {
+                                               sprintf(string, "Destination: %s", systemPlanet[game.destinationPlanet].name);
+                                               SDL_FreeSurface(iconInfo[10].image);
+                                               iconInfo[10].image = textSurface(string, FONT_WHITE);
+                                       }
+                               }
+
+                               blit(iconInfo[9].image, 90, 450);
+                               if ((game.system > 0) && (game.stationedPlanet != game.destinationPlanet))
+                                       blit(iconInfo[10].image, 550, 450);
+                               break;
+
+                       case 2:
+                               intermission_showStatus(statsSurface);
+                               break;
+
+                       case 3:
+                               blit(savesSurface, 200, 100);
+                               saveSlot = showSaveSlots(savesSurface, saveSlot);
+                               break;
+
+                       case 4:
+                               showShop();
+                               break;
+
+                       case 5:
+                               blit(commsSurface, 170, 70);
+                               intermission_doComms(commsSurface);
+                               break;
+
+                       case 6:
+                               blit(optionsSurface, 230, 130);
+                               intermission_doOptions(optionsSurface);
+                               break;
+
+                       case 7:
+                               rtn = 0;
+                               engine.done = 1;
+                               break;
+
+                       case 8:
+                               intermission_showSystem(sinX, cosY, false);
+
+                               blit(systemPlanet[game.stationedPlanet].image, 150, 450);
+                               blit(iconInfo[9].image, 135, 480);
+                               blit(systemPlanet[game.destinationPlanet].image, 650, 450);
+                               blit(iconInfo[10].image, 635, 480);
+
+                               destRect.w += distance;
+                               SDL_FillRect(screen, &destRect, red);
+
+                               if (destRect.w >= 450)
+                               {
+                                       game.stationedPlanet = game.destinationPlanet;
+                                       game.distanceCovered = 0;
+                                       player.shield = player.maxShield;
+                                       sprintf(string, "Stationed At: %s",
+                                               systemPlanet[game.stationedPlanet].name);
+                                       strcpy(game.stationedName,
+                                               systemPlanet[game.stationedPlanet].name);
+                                       SDL_FreeSurface(iconInfo[9].image);
+                                       iconInfo[9].image = textSurface(string, FONT_WHITE);
+                                       intermission_updateCommsSurface(commsSurface);
+                                       section = 1;
+                                       redrawBackGround = true;
+                                       saveGame(0);
+                               }
+                               else if (interceptionChance > 0)
+                               {
+                                       if ((rand() % interceptionChance) == 0)
+                                       {
+                                               game.area = MISN_INTERCEPTION;
+                                               rtn = 2;
+                                               engine.done = 1;
+                                               game.distanceCovered = destRect.w;
+                                       }
+                               }
+
+                               break;
+               }
+
+               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 ((game.stationedPlanet == game.destinationPlanet) &&
+                                                       (systemPlanet[game.stationedPlanet].missionCompleted != 0))
+                                               continue;
+                                       else if (game.stationedPlanet == game.destinationPlanet)
+                                               blit(shape[1], 80 + (i * 90), 500);
+                                       else if (game.stationedPlanet != game.destinationPlanet)
+                                               blit(shape[26], 80 + (i * 90), 500);
+                               }
+                               else
+                               {
+                                       blit(shape[i + 1], 80 + (i * 90), 500);
+                               }
+
+                               if (collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 80 + (i * 90), 500, 32, 32))
+                               {
+                                       if (i != 0)
+                                       {
+                                               blit(iconInfo[i].image, (int)iconInfo[i].x, 545);
+                                       }
+                                       else
+                                       {
+                                               if (game.stationedPlanet == game.destinationPlanet)
+                                                       blit(iconInfo[0].image, (int)iconInfo[i].x, 545);
+                                               else
+                                                       blit(iconInfo[11].image, (int)iconInfo[i].x, 545);
+                                       }
+
+                                       if ((engine.keyState[KEY_FIRE]))
+                                       {
+                                               redrawBackGround = true;
+                                               section = i;
+                                               engine.keyState[KEY_FIRE] = 0;
+                                       }
+                               }
+                       }
+               }
+
+               engine.keyState[KEY_FIRE] = 0;
+               engine.keyState[KEY_ALTFIRE] = 0;
+               intermission_doCursor();
+
+               delayFrame();
+       }
+
+       audio_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);
+
+       if (game.distanceCovered == 0)
+               player.shield = player.maxShield;
+
+       return rtn;
+}
+
diff --git a/src/intermission.h b/src/intermission.h
new file mode 100644 (file)
index 0000000..e6ae69c
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef INTERMISSION_H
+#define INTERMISSION_H
+
+int intermission();
+
+#endif
diff --git a/src/loadSave.cpp b/src/loadSave.cpp
new file mode 100644 (file)
index 0000000..758055e
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012, 2013 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+static char saveSlot[10][25];
+
+/*
+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);
+
+               fp = fopen(fileName, "rb");
+               if (fp == NULL)
+               {
+                       sprintf(saveSlot[i], (i == 0 ? "AUTOSAVE (Empty)" : "Empty"));
+                       if (engine.gameSection == SECTION_TITLE)
+                               textSurface(TS_SAVESLOT_0 + i, saveSlot[i], -1, imagePos,
+                                       FONT_WHITE);
+               }
+               else
+               {
+                       if (i == 0)
+                       {
+                               sprintf(saveSlot[i], "AUTOSAVE");
+                       }
+                       else
+                       {
+                               if (fread(&tempGame, sizeof(Game), 1, fp) != 1)
+                               {
+                                       sprintf(saveSlot[i], "Corrupt Game Data");
+                               }
+                               else
+                               {
+                                       sprintf(saveSlot[i], "%s, %s", systemNames[tempGame.system],
+                                               tempGame.stationedName);
+                               }
+                       }
+
+                       if (engine.gameSection == SECTION_TITLE)
+                               textSurface(TS_SAVESLOT_0 + i, saveSlot[i], -1,
+                                       imagePos, FONT_WHITE);
+
+                       if (stat(fileName, &fileInfo) != -1)
+                       {
+                               if (fileInfo.st_mtime > modTime)
+                                       {modTime = fileInfo.st_mtime; continueSaveIndex = i;}
+                       }
+
+                       fclose(fp);
+               }
+               imagePos += 20;
+       }
+
+       return continueSaveIndex;
+}
+
+/*
+Fill in later...
+*/
+bool 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 false;
+
+       if (fread(&game, sizeof(Game), 1, fp) != 1)
+       {
+               printf("Save game error. The file was not of the expected format.\n");
+               fclose(fp);
+               return false;
+       }
+
+       fclose(fp);
+
+       if (game.saveFormat < 2)
+               game.difficulty = DIFFICULTY_NORMAL;
+
+       weapon[W_PLAYER_WEAPON] = game.playerWeapon;
+       player = game.thePlayer;
+
+       // Re-init all the planets in this system...
+       initPlanetMissions(game.system);
+
+       // ... and then override with completition status
+       for (int i = 0 ; i < 10 ; i++)
+               systemPlanet[i].missionCompleted = game.missionCompleted[i];
+
+       return true;
+}
+
+void saveGame(int slot)
+{
+       FILE *fp;
+       char fileName[PATH_MAX];
+
+       if ((slot < 0) || (slot > 5))
+       {
+               printf("Error - Saves may only be 0 to 5\n");
+               return;
+       }
+
+       sprintf(fileName, "%ssave%.2d.dat", engine.userHomeDirectory, slot);
+       fp = fopen(fileName, "wb");
+
+       game.saveFormat = 3;
+       game.playerWeapon = weapon[W_PLAYER_WEAPON];
+       game.thePlayer = player;
+       for (int i = 0 ; i < 10 ; i++)
+               game.missionCompleted[i] = systemPlanet[i].missionCompleted;
+
+       if (fp != NULL)
+       {
+               if (fwrite(&game, 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[KEY_FIRE] = 0;
+}
+
+void createSavesSurface(SDL_Surface *savesSurface, signed char clickedSlot)
+{
+       int y = 10;
+
+       blevelRect(savesSurface, 0, 0, 348, 298, 0x00, 0x00, 0x00);
+
+       for (int i = 1 ; i <= 5 ; i++)
+       {
+               if (clickedSlot == i)
+                       blevelRect(savesSurface, 5, y, 338, 25, 0x99, 0x00, 0x00);
+               else
+                       blevelRect(savesSurface, 5, y, 338, 25, 0x00, 0x00, 0x99);
+               drawString(saveSlot[i], 70, y + 5, FONT_WHITE, savesSurface);
+               y += 30;
+       }
+
+       drawString("*** HELP ***", 120, 170, FONT_WHITE, savesSurface);
+
+       switch (clickedSlot)
+       {
+               case 1:
+               case 2:
+               case 3:
+               case 4:
+               case 5:
+                       blevelRect(savesSurface, 5, 265, 100, 25, 0x00, 0x99, 0x00);
+                       blevelRect(savesSurface, 125, 265, 100, 25, 0x99, 0x99, 0x00);
+                       blevelRect(savesSurface, 243, 265, 100, 25, 0x99, 0x00, 0x00);
+                       drawString("SAVE", 40, 270, FONT_WHITE, savesSurface);
+                       drawString("CANCEL", 150, 270, FONT_WHITE, savesSurface);
+                       drawString("DELETE", 270, 270, FONT_WHITE, savesSurface);
+
+                       drawString("SAVE will save the game", 17, 200, FONT_WHITE, savesSurface);
+                       drawString("CANCEL will unselect that slot", 17, 220, FONT_WHITE,
+                               savesSurface);
+                       drawString("DELETE will remove the save", 17, 240, FONT_WHITE,
+                               savesSurface);
+                       break;
+               case -1:
+                       drawString("First click a Save game slot to use", 17, 200,
+                               FONT_WHITE, savesSurface);
+                       break;
+               case -10:
+                       drawString("Game Saved", 130, 200, FONT_WHITE, savesSurface);
+                       break;
+               case -11:
+                       drawString("Save Deleted", 130, 200, FONT_WHITE, savesSurface);
+                       break;
+       }
+
+       engine.keyState[KEY_FIRE] = 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)
+{
+       int clickedSlot = -1;
+
+       SDL_Rect r;
+       r.x = 201;
+       r.y = 115;
+       r.w = 348;
+       r.h = 25;
+
+       if ((engine.keyState[KEY_FIRE]))
+       {
+               for (int i = 1 ; i <= 5 ; i++)
+               {
+                       if (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(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 215,
+                       365, 100, 25))
+               {
+                       saveGame(saveSlot);
+                       createSavesSurface(savesSurface, -10);
+               }
+
+               if (collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 335,
+                               365, 100, 25))
+                       createSavesSurface(savesSurface, -1);
+
+               if (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);
+                       remove(filename);
+                       initSaveSlots();
+                       createSavesSurface(savesSurface, -11);
+               }
+       }
+
+       if (clickedSlot > -1)
+               saveSlot = clickedSlot;
+
+       return saveSlot;
+}
diff --git a/src/loadSave.h b/src/loadSave.h
new file mode 100644 (file)
index 0000000..8356291
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef LOADSAVE_H
+#define LOADSAVE_H
+
+extern int initSaveSlots();
+extern bool loadGame(int slot);
+extern void saveGame(int slot);
+extern void createSavesSurface(SDL_Surface *savesSurface, signed char clickedSlot);
+extern int showSaveSlots(SDL_Surface *savesSurface, signed char saveSlot);
+
+#endif
diff --git a/src/messages.cpp b/src/messages.cpp
new file mode 100644 (file)
index 0000000..bbd7fa2
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012 Guus Sliepen
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+static const char *deathMessage[6] = {
+       "Oh my God... No!",
+       "NOOOOOOOOOOOOOOOOOOOOOOOOOOO!!!!",
+       "Please tell me that didn't just happen...",
+       "Chris, Answer Me!!",
+       "What the hell happened?!",
+       "Chriiiiiiiiiiiiiiiiiiiiiiiiiiis!!!!",
+};
+
+static const char *killMessage[10] = {
+       "Chalk another one up for me!",
+       "That'll teach you!",
+       "One more for me!",
+       "Target destroyed!",
+       "You ain't so tough!",
+       "Kicked your ass!",
+
+       "That takes me up to %d",
+
+       "Hey %s, you asleep over there?!",
+       "I'm catching up with you, %s!",
+
+       "Number One, Baby!",
+};
+
+static const char *missFireMessage[5] = {
+       "I am NOT your enemy!",
+       "Hey! Watch it!",
+       "What are you doing?! Shoot THEM!",
+       "OW!!! I hope that was an accident!",
+       "Open your eyes!!",
+};
+
+static const char *playerHitMessage[3] = {
+       "Oops! Sorry!",
+       "Get out of the way!",
+       "Don't fly into my missiles!",
+};
+
+void getKillMessage(object *ally)
+{
+       char in[50], otherName[30];
+       int kills, difference;
+       bool firstPlace = false;
+       int faceToUse = FACE_PHOEBE;
+
+       if (ally == &aliens[ALIEN_PHOEBE])
+       {
+               strcpy(otherName, "Ursula");
+               kills = game.wingMate1Kills;
+               difference = game.wingMate1Kills - game.wingMate2Kills;
+               if ((game.wingMate1Kills > game.wingMate2Kills) &&
+                               (game.wingMate1Kills > game.totalKills))
+                       firstPlace = true;
+               faceToUse = FACE_PHOEBE;
+       }
+       else
+       {
+               strcpy(otherName, "Phoebe");
+               kills = game.wingMate2Kills;
+               difference = game.wingMate2Kills - game.wingMate1Kills;
+               if ((game.wingMate2Kills > game.wingMate1Kills) &&
+                               (game.wingMate2Kills > game.totalKills))
+                       firstPlace = true;
+               faceToUse = FACE_URSULA;
+       }
+
+       int r = rand() % 10;
+       
+       if (game.hasWingMate2 == 0)
+               r = rand() % 7;
+
+       switch(r)
+       {
+               case 0:
+               case 1:
+               case 2:
+               case 3:
+               case 4:
+               case 5:
+                       strcpy(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)
+                       {
+                               strcpy(in, killMessage[9]);
+                       }
+                       else
+                       {
+                               strcpy(in, killMessage[rand() % 6]);
+                       }
+                       break;
+       }
+
+       setRadioMessage(faceToUse, in, 0);
+}
+
+const char *getKlineInsult()
+{
+       static const char *insult[] = {
+               "Pathetic.", "How very disappointing...", "Heroic. And stupid.", "Fool.",
+               "And now you're nothing but a DEAD hero."
+       };
+
+       if (game.area != MISN_VENUS)
+               return (insult[rand() % 3]);
+       else
+               return (insult[3 + (rand() % 2)]);
+}
+
+void getPlayerDeathMessage()
+{
+       if (aliens[ALIEN_KLINE].active)
+       {
+               setRadioMessage(FACE_KLINE, getKlineInsult(), 1);
+               return;
+       }
+       
+       if ((aliens[ALIEN_BOSS].active) && (aliens[ALIEN_BOSS].classDef == CD_KRASS))
+       {
+               setRadioMessage(FACE_KRASS, "That was the easiest $90,000,000 I've ever earned! Bwwah!! Ha!! Ha!! Ha!!", 1);
+               return;
+       }
+
+       int faceToUse = FACE_PHOEBE;
+       
+
+       if ((game.area == MISN_URUSOR) || (game.area == MISN_POSWIC) ||
+               (game.area == MISN_EARTH))
+       {
+               faceToUse = FACE_SID;
+       }
+       else if (game.hasWingMate2)
+       {
+               if ((rand() % 2) == 0)
+                       faceToUse = FACE_URSULA;
+       }
+
+       setRadioMessage(faceToUse, deathMessage[rand() % 6], 1);
+}
+
+void getMissFireMessage(object *ally)
+{
+       int faceToUse = FACE_PHOEBE;
+
+       if (ally == &aliens[ALIEN_PHOEBE])
+               faceToUse = FACE_PHOEBE;
+       else
+               faceToUse = FACE_URSULA;
+
+       setRadioMessage(faceToUse, missFireMessage[rand() % 5], 0);
+}
+
+void getPlayerHitMessage(object *ally)
+{
+       int faceToUse = FACE_PHOEBE;
+
+       if (ally == &aliens[ALIEN_PHOEBE])
+               faceToUse = FACE_PHOEBE;
+       else
+               faceToUse = FACE_URSULA;
+
+       setRadioMessage(faceToUse, playerHitMessage[rand() % 3], 0);
+}
diff --git a/src/messages.h b/src/messages.h
new file mode 100644 (file)
index 0000000..ca0d0af
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MESSAGES_H
+#define MESSAGES_H
+
+extern void getKillMessage(object *ally);
+extern const char *getKlineInsult();
+extern void getPlayerDeathMessage();
+extern void getMissFireMessage(object *ally);
+extern void getPlayerHitMessage(object *ally);
+
+#endif
diff --git a/src/misc.cpp b/src/misc.cpp
new file mode 100644 (file)
index 0000000..d93544b
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012, 2013 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+void clearInfoLines()
+{
+       for (int i = 0 ; i < 4 ; i++)
+       {
+               textShape[i].life = 0;
+       }
+}
+
+// from a to b
+void copyInfoLine(int a, int b)
+{
+       textSurface(b, textShape[a].text, -1, 0, textShape[a].fontColor);
+       textShape[b].life = 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(const char *in, int color)
+{
+       int index = -1;
+
+       for (int i = 0 ; i < 3 ; i++)
+       {
+               if ((textShape[i].life == 0) && (index == -1))
+               {
+                       index = i;
+               }
+       }
+
+       // Bump down
+       if (index == -1)
+       {
+               index = 2;
+               copyInfoLine(1, 0);
+               copyInfoLine(2, 1);
+       }
+
+       textSurface(index, in, -1, 0, color);
+       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, const char *in, int priority)
+{
+       if ((textShape[3].life > 0) && (priority == 0))
+               return;
+
+       textSurface(3, in, -1, 50, FONT_WHITE);
+       textShape[3].life = 240;
+
+       SDL_Surface *faceShape = NULL;
+       if (face > -1)
+               faceShape = shape[face];
+
+       createMessageBox(faceShape, in, 1);
+}
+
+static const char *faces[] = {
+                       "FACE_CHRIS", "FACE_SID", "FACE_KRASS",
+                       "FACE_KLINE", "FACE_PHOEBE", "FACE_URSULA",
+                       "FACE_CREW"
+};
+
+int getFace(const 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 = bufferHead->next;
+       while (r1 != NULL)
+       {
+               r2 = r1;
+               r1 = r1->next;
+               delete r2;
+       }
+       
+       bufferHead->next = NULL;
+       bufferTail = 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/src/misc.h b/src/misc.h
new file mode 100644 (file)
index 0000000..78efea7
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MISC_H
+#define MISC_H
+
+extern void clearInfoLines();
+void copyInfoLine(int a, int b);
+extern void setInfoLine(const char *in, int color);
+extern void setRadioMessage(signed char face, const char *in, int priority);
+extern int getFace(const char *face);
+extern void resetLists();
+
+#endif
diff --git a/src/missions.cpp b/src/missions.cpp
new file mode 100644 (file)
index 0000000..5d4c8ac
--- /dev/null
@@ -0,0 +1,1395 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012, 2013 Guus Sliepen
+Copyright (C) 2012, 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+// God, I hate this file! :((
+
+Planet systemPlanet[10];
+mission currentMission;
+static mission missions[MISN_MAX];
+
+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:
+                       systemPlanet[0].missionNumber = MISN_HAIL;
+                       systemPlanet[0].missionCompleted = 0;
+
+                       systemPlanet[1].missionNumber = MISN_CERADSE;
+                       systemPlanet[1].missionCompleted = 0;
+
+                       systemPlanet[2].missionNumber = MISN_HINSTAG;
+                       systemPlanet[2].missionCompleted = 0;
+
+                       systemPlanet[3].missionNumber = MISN_JOLDAR;
+                       systemPlanet[3].missionCompleted = 0;
+
+                       systemPlanet[4].missionNumber = MISN_MOEBO;
+                       systemPlanet[4].missionCompleted = -1;
+
+                       break;
+
+               // Eyananth
+               case 1:
+                       systemPlanet[0].missionNumber = MISN_NEROD;
+                       systemPlanet[0].missionCompleted = 0;
+
+                       systemPlanet[1].missionNumber = MISN_ALLEZ;
+                       systemPlanet[1].missionCompleted = 0;
+
+                       systemPlanet[2].missionNumber = MISN_URUSOR;
+                       systemPlanet[2].missionCompleted = -1;
+
+                       systemPlanet[3].missionNumber = MISN_DORIM;
+                       systemPlanet[3].missionCompleted = -1;
+
+                       systemPlanet[4].missionNumber = MISN_ELAMALE;
+                       systemPlanet[4].missionCompleted = -2;
+
+                       // This one is for the slaves
+                       systemPlanet[9].missionNumber = MISN_RESCUESLAVES;
+                       systemPlanet[9].missionCompleted = 0;
+
+                       break;
+
+               // Mordor
+               case 2:
+                       systemPlanet[0].missionNumber = MISN_ODEON;
+                       systemPlanet[0].missionCompleted = 0;
+
+                       systemPlanet[1].missionNumber = MISN_FELLON;
+                       systemPlanet[1].missionCompleted = 0;
+
+                       systemPlanet[2].missionNumber = MISN_SIVEDI;
+                       systemPlanet[2].missionCompleted = -1;
+
+                       systemPlanet[3].missionNumber = MISN_ALMARTHA;
+                       systemPlanet[3].missionCompleted = -1;
+
+                       systemPlanet[4].missionNumber = MISN_POSWIC;
+                       systemPlanet[4].missionCompleted = -2;
+
+                       systemPlanet[5].missionNumber = MISN_ELLESH;
+                       systemPlanet[5].missionCompleted = -3;
+
+                       // This one is for the experimental fighter
+                       systemPlanet[9].missionNumber = MISN_CLOAKFIGHTER;
+                       systemPlanet[9].missionCompleted = 0;
+
+                       break;
+
+               // Sol
+               case 3:
+                       systemPlanet[8].missionNumber = MISN_PLUTO;
+                       systemPlanet[8].missionCompleted = 0;
+
+                       systemPlanet[7].missionNumber = MISN_NEPTUNE;
+                       systemPlanet[7].missionCompleted = 0;
+
+                       systemPlanet[6].missionNumber = MISN_URANUS;
+                       systemPlanet[6].missionCompleted = 0;
+
+                       systemPlanet[5].missionNumber = MISN_SATURN;
+                       systemPlanet[5].missionCompleted = -1;
+
+                       systemPlanet[4].missionNumber = MISN_JUPITER;
+                       systemPlanet[4].missionCompleted = -2;
+
+                       systemPlanet[3].missionNumber = MISN_MARS;
+                       systemPlanet[3].missionCompleted = -3;
+
+                       systemPlanet[2].missionNumber = MISN_EARTH;
+                       systemPlanet[2].missionCompleted = -4;
+
+                       systemPlanet[1].missionNumber = MISN_VENUS;
+                       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 (game.area == MISN_START)
+       {
+               game.stationedPlanet = 0;
+               game.area = 1;
+               strcpy(game.stationedName, "Hail");
+               initPlanetMissions(game.system);
+       }
+       else if (game.area == MISN_MOEBO)
+       {
+               game.stationedPlanet = 0;
+               game.system = 1;
+               game.area = MISN_RESCUESLAVES;
+               strcpy(game.stationedName, "Nerod");
+               initPlanetMissions(game.system);
+
+               if (game.difficulty == DIFFICULTY_ORIGINAL)
+                       player.maxShield = 50;
+       }
+       else if (game.area == MISN_ELAMALE)
+       {
+               game.stationedPlanet = 0;
+               game.system = 2;
+               game.area = MISN_CLOAKFIGHTER;
+               strcpy(game.stationedName, "Odeon");
+               initPlanetMissions(game.system);
+
+               if (game.difficulty == DIFFICULTY_ORIGINAL)
+                       player.maxShield = 75;
+       }
+       else if (game.area == MISN_ELLESH)
+       {
+               game.stationedPlanet = 8;
+               game.system = 3;
+               game.area = MISN_PLUTO;
+               strcpy(game.stationedName, "Pluto");
+               initPlanetMissions(game.system);
+
+               if (game.difficulty == DIFFICULTY_ORIGINAL)
+                       player.maxShield = 100;
+       }
+       else // Update the mission for the planet
+       {
+               systemPlanet[game.stationedPlanet].missionCompleted = 1;
+       }
+
+       strcpy(game.destinationName, "None");
+       game.destinationPlanet = game.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
+*/
+static void clearAllMissions()
+{
+       for (int m = 0 ; m < MISN_MAX ; 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;
+}
+
+/*
+Call this whenever a mission requires all the remaining aliens to
+automatically die
+*/
+static void mission_killAllEnemies()
+{
+       for (int i = 0 ; i < ALIEN_MAX ; i++)
+       {
+               if ((aliens[i].flags & FL_WEAPCO) && (aliens[i].active) &&
+                               (aliens[i].shield > 0))
+                       aliens[i].shield = 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 ((game.area == MISN_MOEBO) &&
+                       (currentMission.completed1[0] < OB_INCOMPLETE))
+               engine.timeMission = 1;
+
+       // specific to the Asteroid belt
+       if ((game.area == MISN_MARS) &&
+               (currentMission.completed1[0] < OB_INCOMPLETE))
+       {
+               currentMission.completed1[0] = OB_COMPLETED;
+               mission_killAllEnemies();
+               engine.addAliens = -1;
+               setInfoLine("*** All Primary Objectives Completed ***", FONT_GREEN);
+       }
+}
+
+static void evaluateRequirement(int type, int id, int *completed, int *targetValue, int fontColor)
+{
+       char message[25];
+
+       if ((*targetValue <= 0) && (type != M_PROTECT_TARGET) &&
+               (type != M_PROTECT_PICKUP))
+       {
+               *completed = 2;
+               checkTimer();
+               if ((game.area == MISN_URUSOR) && (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 destroyed!");
+                                               if (game.area == MISN_CERADSE) // 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 (game.area == MISN_ODEON) // Get lectured by Phoebe
+                                                       setRadioMessage(FACE_PHOEBE, "No... Ursula...", 1);
+                                               break;
+                               }
+                               break;
+                       case M_PROTECT_TARGET:
+                               if (*targetValue <= 0)
+                               {
+                                       *completed = -2;
+                                       switch (game.area)
+                                       {
+                                               case MISN_NEROD:
+                                                       setRadioMessage(FACE_SID, "Dammit, Chris! We just lost her!", 1);
+                                                       break;
+                                               case MISN_ALLEZ:
+                                                       setRadioMessage(FACE_CREW, "Noooo!! Hull bre...", 1);
+                                                       break;
+                                               case MISN_URUSOR:
+                                                       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))
+                               {
+                                       if ((rand() % 2) == 0)
+                                               sprintf(message, "%d more to go...", *targetValue);
+                                       else
+                                               sprintf(message, "Destroy %d more...", *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 (player.shield <= 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;
+                               evaluateRequirement(type, id, &currentMission.completed1[i], &currentMission.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;
+                               evaluateRequirement(type, id, &currentMission.completed2[i], &currentMission.targetValue2[i], FONT_YELLOW);
+                               return;
+                       }
+               }
+       }
+
+       // Special Case - Interceptions
+       if (game.area == MISN_INTERCEPTION)
+       {
+               if ((type == M_COLLECT) && (id == P_SLAVES))
+               {
+                       if (systemPlanet[9].missionCompleted == 0)
+                       {
+                               if (game.slavesRescued >= 250)
+                               {
+                                       setInfoLine("*** Slaves Rescued - Mission Completed ***", FONT_GREEN);
+                                       systemPlanet[9].missionCompleted = 1;
+                               }
+                               else
+                               {
+                                       sprintf(message, "Rescue %d more...", 250 - game.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);
+                       game.experimentalShield = 0;
+               }
+       }
+}
+
+/*
+This is only used as few times in the game.
+Missions 11 and 23 to be exact!
+*/
+static char revealHiddenObjectives()
+{
+       char allDone = 1;
+       char string[255] = "";
+
+       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 (game.area == MISN_ELAMALE)
+               {
+                       mission_killAllEnemies();
+                       syncScriptEvents();
+                       aliens[ALIEN_KLINE].active = true;
+                       aliens[ALIEN_KLINE].x = player.x + 1000;
+                       aliens[ALIEN_KLINE].y = player.y;
+                       aliens[ALIEN_KLINE].flags |= FL_IMMORTAL | FL_NOFIRE;
+                       player_setTarget(ALIEN_KLINE);
+                       audio_playMusic("music/last_cyber_dance.ogg", -1);
+               }
+       }
+
+       return allDone;
+}
+
+bool 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 ((game.area != MISN_POSWIC) || (i != 1))
+                                       setInfoLine("*** Primary Objective Completed ***", FONT_GREEN);
+                               else
+                                       setInfoLine(">>> Primary Objective Failed <<<", FONT_RED);
+                               currentMission.completed1[i] = OB_COMPLETED;
+                       }
+                       else
+                       {
+                               if (game.area != MISN_INTERCEPTION)
+                                       setInfoLine("*** All Primary Objectives Completed ***", FONT_GREEN);
+                               else
+                                       setInfoLine("*** Interception Destroyed ***", FONT_GREEN);
+                               currentMission.completed1[i] = OB_COMPLETED;
+
+                               // do some area specific things
+                               if ((game.area == MISN_MOEBO) ||
+                                       (game.area == MISN_DORIM) ||
+                                       (game.area == MISN_ELLESH) ||
+                                       (game.area == MISN_MARS))
+                               {
+                                       if (currentMission.remainingObjectives2 == 0)
+                                       {
+                                               mission_killAllEnemies();
+                                               engine.addAliens = -1;
+                                       }
+                               }
+
+                               if (game.area == MISN_EARTH)
+                                       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 ((game.area == MISN_DORIM) &&
+                                       (currentMission.remainingObjectives1 == 0))
+                               {
+                                       mission_killAllEnemies();
+                                       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;
+       bool add = false;
+       bool allDone = true;
+
+       // 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 = true;
+                               allDone = false;
+                       }
+
+                       if (currentMission.completed1[i] < 0)
+                               return false;
+               }
+               if (currentMission.secondaryType[i] != NONE)
+               {
+                       if (currentMission.completed2[i] == 0)
+                       {
+                               currentMission.remainingObjectives2++;
+                               if (currentMission.secondaryType[i] == M_DESTROY_ALL_TARGETS)
+                                       add = true;
+                               allDone = false;
+                       }
+               }
+       }
+
+       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) && (remaining == 1))
+               engine.addAliens = -1;
+
+       return allDone;
+}
+
+bool missionFailed()
+{
+       for (int i = 0 ; i < 3 ; i++)
+       {
+               if (currentMission.completed1[i] < 0)
+               {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static void drawBriefScreen()
+{
+       SDL_Rect r = {0, 0, (uint16_t)screen->w, 2};
+
+       for (int i = 0 ; i < (int)(screen->h / 4) - 30 ; i++)
+       {
+               r.y = (i * 2) + 62; // Not a typo; a black gap is left in the middle if it's 60.
+               SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 0, i, 0));
+               r.y = (screen->h - (i * 2) - 60);
+               SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 0, i, 0));
+       }
+
+       blevelRect(140, 70, 500, 20, 0x00, 0x77, 0x00);
+       blevelRect(140, 90, 500, 130, 0x00, 0x33, 0x00);
+       drawString("Primary Objectives", 150, 74, FONT_WHITE);
+
+       for (int i = 0 ; i < 3 ; i++)
+       {
+               if ((currentMission.primaryType[i] != NONE) && (currentMission.completed1[i] != OB_HIDDEN))
+               {
+                       drawString(currentMission.primaryObjective[i], 160, 114 + (i * 30), FONT_WHITE);
+               }
+       }
+
+       if (currentMission.secondaryType[0] != NONE)
+       {
+               blevelRect(140, 230, 500, 20, 0x00, 0x77, 0x77);
+               blevelRect(140, 250, 500, 130, 0x00, 0x33, 0x33);
+               drawString("Secondary Objectives", 150, 234, FONT_WHITE);
+
+               for (int i = 0 ; i < 3 ; i++)
+               {
+                       if (currentMission.secondaryType[i] != NONE)
+                       {
+                               drawString(currentMission.secondaryObjective[i], 160, 274 + (i * 30), FONT_WHITE);
+                               game.secondaryMissions++;
+                       }
+               }
+       }
+
+       blevelRect(140, 390, 500, 20, 0x77, 0x77, 0x00);
+       blevelRect(140, 410, 500, 130, 0x33, 0x33, 0x00);
+       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()
+{
+       clearScreen(black);
+       updateScreen();
+
+       if (game.area != MISN_INTERCEPTION)
+       {
+               clearScreen(black);
+               drawBriefScreen();
+
+               if (currentMission.timeLimit1[0] > 0)
+               {
+                       char temp[50];
+                       if (game.area != MISN_MARS)
+                               sprintf(temp, "TIME LIMIT: %d minutes", currentMission.timeLimit1[0]);
+                       else
+                               sprintf(temp, "SURVIVAL FOR %d minutes", currentMission.timeLimit1[0]);
+                       drawString(temp, -1, 500, FONT_RED);
+               }
+
+               switch (game.area)
+               {
+                       case MISN_URUSOR:
+                       case MISN_DORIM:
+                       case MISN_SIVEDI:
+                       case MISN_ALMARTHA:
+                       case MISN_ELLESH:
+                       case MISN_MARS:
+                       case MISN_VENUS:
+                               drawString("Phoebe Lexx will not be present", 160, 420, FONT_WHITE);
+                               if (game.hasWingMate2)
+                                       drawString("Ursula Lexx will not be present", 160, 450, FONT_WHITE);
+                               break;
+               }
+
+               if ((game.area == MISN_URUSOR) ||
+                               (game.area == MISN_POSWIC) ||
+                               (game.area == MISN_EARTH))
+                       drawString("Sid Wilson will join you on this mission", 160, 480, FONT_WHITE);
+
+               updateScreen();
+       }
+
+       loadGameGraphics();
+       textSurface(4, "Shield", 25, 550, FONT_WHITE);
+       textSurface(5, "Plasma:", 250, 550, FONT_WHITE);
+
+       if (player.weaponType[1] == W_CHARGER)
+               textSurface(6, "Charge", 385, 550, FONT_WHITE);
+       else if (player.weaponType[1] == W_LASER)
+               textSurface(6, "Heat", 405, 550, FONT_WHITE);
+       else
+               textSurface(6, "Rockets:", 385, 550, FONT_WHITE);
+
+       textSurface(7, "Target", 550, 550, FONT_WHITE);
+       textSurface(8, "Cash: $", 25, 20, FONT_WHITE);
+       textSurface(9, "Objectives Remaining:", 550, 20, FONT_WHITE);
+       textSurface(10, "Time Remaining - ", 260, 20, FONT_WHITE);
+       textSurface(11, "Power", 25, 570, FONT_WHITE);
+       textSurface(12, "Output", 250, 570, FONT_WHITE);
+       textSurface(13, "Cooler", 485, 570, FONT_WHITE);
+       audio_playRandomTrack();
+
+       if (game.area != MISN_INTERCEPTION)
+       {
+               drawString("PRESS ENTER TO CONTINUE...", -1, 550, FONT_WHITE);
+
+               updateScreen();
+
+               flushInput();
+               engine.done = 0;
+               engine.keyState[KEY_FIRE] = 0;
+               engine.keyState[KEY_ALTFIRE] = 0;
+               engine.keyState[KEY_ESCAPE] = 0;
+
+               while (true)
+               {
+                       delayFrame();
+                       getPlayerInput();
+                       if ((engine.keyState[KEY_FIRE]) || (engine.keyState[KEY_ALTFIRE]) ||
+                                       (engine.keyState[KEY_ESCAPE]))
+                               break;
+               }
+
+               clearScreen(black);
+               updateScreen();
+               clearScreen(black);
+       }
+
+       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()
+{
+       int shield_bonus;
+       char temp[100];
+
+       if (game.area != MISN_INTERCEPTION)
+       {
+               clearScreen(black);
+               updateScreen();
+
+               if (game.shots > 0)
+                       game.accuracy = (game.hits * 100) / game.shots;
+
+               clearScreen(black);
+               drawBriefScreen();
+
+               for (int i = 0 ; i < 3 ; i++)
+               {
+                       if (currentMission.primaryType[i] != NONE)
+                       {
+                               if ((game.area != MISN_POSWIC) || (i != 1))
+                                       drawString("COMPLETED", 550, 114 + (i * 30), FONT_GREEN);
+                               else
+                                       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)
+                               {
+                                       strcpy(temp, currentMission.secondaryObjective[i]);
+                                       if (currentMission.completed2[i] >= 1)
+                                       {
+                                               drawString("COMPLETED", 550, 274 + (i * 30), FONT_GREEN);
+                                               game.secondaryMissionsCompleted++;
+                                       }
+                                       else
+                                       {
+                                               drawString("FAILED", 550, 274 + (i * 30), FONT_RED);
+                                       }
+                               }
+                       }
+               }
+
+               if (currentMission.remainingObjectives1 + currentMission.remainingObjectives2 == 0)
+               {
+                       shield_bonus = player.shield * 10;
+                       sprintf(temp, "Shield Bonus: $%.3d", shield_bonus);
+                       drawString(temp, -1, 430, FONT_WHITE);
+                       game.cash += shield_bonus;
+                       game.cashEarned += shield_bonus;
+               }
+
+               game.timeTaken += engine.timeTaken;
+
+               snprintf(temp, sizeof temp, "Mission Time: %2ld:%02ld:%02ld",
+                       engine.timeTaken / 3600, (engine.timeTaken / 60) % 60,
+                       engine.timeTaken % 60);
+
+               drawString(temp, -1, 500, FONT_WHITE);
+
+               // Do some mission specific stuff here...
+               if (game.area == MISN_HAIL)
+                       game.cash -= 500;
+               else if (game.area == MISN_ODEON)
+                       game.hasWingMate2 = 1;
+               else if (game.area == MISN_ALMARTHA)
+                       game.cash -= 2000;
+
+               checkForBossMission();
+
+               updateScreen();
+
+               flushInput();
+               engine.done = 0;
+               engine.keyState[KEY_FIRE] = 0;
+
+               while (true)
+               {
+                       delayFrame();
+                       getPlayerInput();
+                       if ((engine.keyState[KEY_FIRE]))
+                               break;
+               }
+       }
+
+       // Stop people from "selling" Laser ammo as rockets.
+       if (player.weaponType[1] == W_LASER)
+               player.ammo[1] = 1;
+
+       audio_haltMusic();
+}
+
+/*
+This is where all the missions are defined.
+*/
+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;
+
+
+       sprintf(missions[MISN_START].primaryObjective[0],
+               "Escape from WEAPCO Persuit");
+       missions[MISN_START].primaryType[0] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_START].completed1[0] = OB_INCOMPLETE;
+
+
+       sprintf(missions[MISN_HAIL].primaryObjective[0],
+               "Collect $500 to pay Mercenary for FIREFLY");
+       missions[MISN_HAIL].primaryType[0] = M_COLLECT;
+       missions[MISN_HAIL].target1[0] = P_CASH;
+       missions[MISN_HAIL].targetValue1[0] = 500;
+       missions[MISN_HAIL].completed1[0] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_HAIL].primaryObjective[1],
+               "Destroy all remaining WEAPCO fighters");
+       missions[MISN_HAIL].primaryType[1] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_HAIL].completed1[1] = OB_INCOMPLETE;
+
+       missions[MISN_HAIL].addAliens = FREQUENT;
+
+
+       sprintf(missions[MISN_CERADSE].primaryObjective[0],
+               "Collect 6 Cargo Pods");
+       missions[MISN_CERADSE].primaryType[0] = M_COLLECT;
+       missions[MISN_CERADSE].target1[0] = P_CARGO;
+       missions[MISN_CERADSE].targetValue1[0] = 6;
+       missions[MISN_CERADSE].completed1[0] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_CERADSE].primaryObjective[1],
+               "Do not destroy *ANY* Cargo Pods");
+       missions[MISN_CERADSE].primaryType[1] = M_PROTECT_PICKUP;
+       missions[MISN_CERADSE].target1[1] = P_CARGO;
+       missions[MISN_CERADSE].targetValue1[1] = 0;
+       missions[MISN_CERADSE].completed1[1] = OB_CONDITION;
+
+       sprintf(missions[MISN_CERADSE].secondaryObjective[0],
+               "Destroy all remaining WEAPCO fighters");
+       missions[MISN_CERADSE].secondaryType[0] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_CERADSE].completed2[0] = OB_INCOMPLETE;
+
+       missions[MISN_CERADSE].addAliens = FREQUENT;
+
+
+       sprintf(missions[MISN_HINSTAG].primaryObjective[0],
+               "Destroy 5 WEAPCO Missile Boats");
+       missions[MISN_HINSTAG].primaryType[0] = M_DESTROY_TARGET_TYPE;
+       missions[MISN_HINSTAG].target1[0] = CD_MISSILEBOAT;
+       missions[MISN_HINSTAG].targetValue1[0] = 5;
+       missions[MISN_HINSTAG].completed1[0] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_HINSTAG].secondaryObjective[0],
+               "Destroy all remaining WEAPCO fighters");
+       missions[MISN_HINSTAG].secondaryType[0] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_HINSTAG].completed2[0] = OB_INCOMPLETE;
+
+       missions[MISN_HINSTAG].addAliens = NORMAL;
+
+
+       sprintf(missions[MISN_JOLDAR].primaryObjective[0],
+               "Destroy 9 WEAPCO Miners");
+       missions[MISN_JOLDAR].primaryType[0] = M_DESTROY_TARGET_TYPE;
+       missions[MISN_JOLDAR].target1[0] = CD_MINER;
+       missions[MISN_JOLDAR].targetValue1[0] = 9;
+       missions[MISN_JOLDAR].completed1[0] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_JOLDAR].secondaryObjective[0],
+               "Destroy all remaining WEAPCO fighters");
+       missions[MISN_JOLDAR].secondaryType[0] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_JOLDAR].completed2[0] = OB_INCOMPLETE;
+
+       missions[MISN_JOLDAR].addAliens = NORMAL;
+
+
+       sprintf(missions[MISN_MOEBO].primaryObjective[0],
+               "Destroy WEAPCO Frigate");
+       missions[MISN_MOEBO].primaryType[0] = M_DESTROY_TARGET_TYPE;
+       missions[MISN_MOEBO].target1[0] = CD_BOSS;
+       missions[MISN_MOEBO].targetValue1[0] = 1;
+       missions[MISN_MOEBO].completed1[0] = OB_INCOMPLETE;
+       
+       missions[MISN_MOEBO].timeLimit1[0] = 3;
+
+       missions[MISN_MOEBO].addAliens = SOMETIMES;
+
+
+       sprintf(missions[MISN_NEROD].primaryObjective[0], "Rescue Phoebe Lexx");
+       missions[MISN_NEROD].primaryType[0] = M_DESTROY_TARGET_TYPE;
+       missions[MISN_NEROD].target1[0] = CD_CARGOSHIP;
+       missions[MISN_NEROD].targetValue1[0] = 1;
+       missions[MISN_NEROD].completed1[0] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_NEROD].primaryObjective[1],
+               "Do not allow Phoebe to be killed");
+       missions[MISN_NEROD].primaryType[1] = M_PROTECT_TARGET;
+       missions[MISN_NEROD].target1[1] = CD_PHOEBE;
+       missions[MISN_NEROD].targetValue1[1] = 0;
+       missions[MISN_NEROD].completed1[1] = OB_CONDITION;
+
+       sprintf(missions[MISN_NEROD].primaryObjective[2],
+               "Destroy all WEAPCO forces");
+       missions[MISN_NEROD].primaryType[2] = M_DESTROY_TARGET_TYPE;
+       missions[MISN_NEROD].target1[2] = CD_ANY;
+       missions[MISN_NEROD].targetValue1[2] = 35;
+       missions[MISN_NEROD].completed1[2] = OB_INCOMPLETE;
+
+       missions[MISN_NEROD].addAliens = ALWAYS;
+
+
+       sprintf(missions[MISN_ALLEZ].primaryObjective[0],
+               "Assist medical supply craft");
+       missions[MISN_ALLEZ].primaryType[0] = M_ESCAPE_TARGET;
+       missions[MISN_ALLEZ].target1[0] = CD_GOODTRANSPORT;
+       missions[MISN_ALLEZ].targetValue1[0] = 0;
+       missions[MISN_ALLEZ].completed1[0] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_ALLEZ].primaryObjective[1],
+               "Do not allow supply craft to be destroyed");
+       missions[MISN_ALLEZ].primaryType[1] = M_PROTECT_TARGET;
+       missions[MISN_ALLEZ].target1[1] = CD_GOODTRANSPORT;
+       missions[MISN_ALLEZ].targetValue1[1] = 0;
+       missions[MISN_ALLEZ].completed1[1] = OB_CONDITION;
+       
+       sprintf(missions[MISN_ALLEZ].secondaryObjective[0],
+               "Destroy all remaining WEAPCO fighters");
+       missions[MISN_ALLEZ].secondaryType[0] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_ALLEZ].completed2[0] = OB_INCOMPLETE;
+
+       missions[MISN_ALLEZ].addAliens = FREQUENT;
+
+
+       sprintf(missions[MISN_URUSOR].primaryObjective[0],
+               "Disable five WEAPCO supply craft");
+       missions[MISN_URUSOR].primaryType[0] = M_DISABLE_TARGET;
+       missions[MISN_URUSOR].target1[0] = CD_CARGOSHIP;
+       missions[MISN_URUSOR].targetValue1[0] = 5;
+       missions[MISN_URUSOR].completed1[0] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_URUSOR].primaryObjective[1],
+               "Destroy all remaining WEAPCO fighters");
+       missions[MISN_URUSOR].primaryType[1] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_URUSOR].completed1[1] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_URUSOR].primaryObjective[2],
+               "Protect supply craft AND Sid Wilson");
+       missions[MISN_URUSOR].primaryType[2] = M_PROTECT_TARGET;
+       missions[MISN_URUSOR].target1[2] = CD_CARGOSHIP;
+       missions[MISN_URUSOR].targetValue1[2] = 0;
+       missions[MISN_URUSOR].completed1[2] = OB_CONDITION;
+
+       missions[MISN_URUSOR].addAliens = FREQUENT;
+
+
+       sprintf(missions[MISN_DORIM].primaryObjective[0],
+               "Locate doctor's escape pod");
+       missions[MISN_DORIM].primaryType[0] = M_COLLECT;
+       missions[MISN_DORIM].target1[0] = P_ESCAPEPOD;
+       missions[MISN_DORIM].targetValue1[0] = 1;
+       missions[MISN_DORIM].completed1[0] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_DORIM].primaryObjective[1],
+               "Do not destroy doctor's escape pod");
+       missions[MISN_DORIM].primaryType[1] = M_PROTECT_PICKUP;
+       missions[MISN_DORIM].target1[1] = P_ESCAPEPOD;
+       missions[MISN_DORIM].targetValue1[1] = 1; // DONE ON PURPOSE!! DO NOT CHANGE THIS!!!!
+       missions[MISN_DORIM].completed1[1] = OB_CONDITION;
+
+       sprintf(missions[MISN_DORIM].secondaryObjective[0],
+               "Collect 10 pieces of Ore");
+       missions[MISN_DORIM].secondaryType[0] = M_COLLECT;
+       missions[MISN_DORIM].target2[0] = P_ORE;
+       missions[MISN_DORIM].targetValue2[0] = 10;
+       missions[MISN_DORIM].completed2[0] = OB_INCOMPLETE;
+
+       missions[MISN_DORIM].addAliens = ALWAYS;
+
+       missions[MISN_DORIM].timeLimit1[0] = 3;
+       missions[MISN_DORIM].timeLimit2[0] = 3;
+
+
+       sprintf(missions[MISN_ELAMALE].primaryObjective[0],
+               "Destroy WEAPCO ore mining craft");
+       missions[MISN_ELAMALE].primaryType[0] = M_DESTROY_TARGET_TYPE;
+       missions[MISN_ELAMALE].target1[0] = CD_BOSS;
+       missions[MISN_ELAMALE].targetValue1[0] = 1;
+       missions[MISN_ELAMALE].completed1[0] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_ELAMALE].secondaryObjective[0],
+               "Save present slaves");
+       missions[MISN_ELAMALE].secondaryType[0] = M_PROTECT_PICKUP;
+       missions[MISN_ELAMALE].target2[0] = P_SLAVES;
+       missions[MISN_ELAMALE].completed2[0] = OB_CONDITION;
+
+       sprintf(missions[MISN_ELAMALE].primaryObjective[1], "Battle Kline");
+       missions[MISN_ELAMALE].primaryType[1] = M_ESCAPE_TARGET;
+       missions[MISN_ELAMALE].target1[1] = CD_KLINE;
+       missions[MISN_ELAMALE].targetValue1[1] = 1;
+       missions[MISN_ELAMALE].completed1[1] = OB_HIDDEN;
+
+       missions[MISN_ELAMALE].addAliens = NEVER;
+
+
+       sprintf(missions[MISN_ODEON].primaryObjective[0], "Destroy Ursula's ship");
+       missions[MISN_ODEON].primaryType[0] = M_DESTROY_TARGET_TYPE;
+       missions[MISN_ODEON].target1[0] = CD_EVILURSULA;
+       missions[MISN_ODEON].targetValue1[0] = 0;
+       missions[MISN_ODEON].completed1[0] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_ODEON].primaryObjective[1],
+               "Capture Ursula's escape pod");
+       missions[MISN_ODEON].primaryType[1] = M_COLLECT;
+       missions[MISN_ODEON].target1[1] = P_ESCAPEPOD;
+       missions[MISN_ODEON].targetValue1[1] = 1;
+       missions[MISN_ODEON].completed1[1] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_ODEON].primaryObjective[2], "Do not kill Ursula");
+       missions[MISN_ODEON].primaryType[2] = M_PROTECT_PICKUP;
+       missions[MISN_ODEON].target1[2] = P_ESCAPEPOD;
+       missions[MISN_ODEON].targetValue1[2] = 0;
+       missions[MISN_ODEON].completed1[2] = OB_CONDITION;
+
+       sprintf(missions[MISN_ODEON].secondaryObjective[0],
+               "Destroy all remaining WEAPCO fighters");
+       missions[MISN_ODEON].secondaryType[0] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_ODEON].completed2[0] = OB_INCOMPLETE;
+
+       missions[MISN_ODEON].addAliens = FREQUENT;
+
+
+       sprintf(missions[MISN_FELLON].primaryObjective[0],
+               "Assist attack on WEAPCO ore mining craft");
+       missions[MISN_FELLON].primaryType[0] = M_DESTROY_TARGET_TYPE;
+       missions[MISN_FELLON].target1[0] = CD_BOSS;
+       missions[MISN_FELLON].targetValue1[0] = 1;
+       missions[MISN_FELLON].completed1[0] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_FELLON].primaryObjective[1],
+               "At least 1 rebel craft must survive");
+       missions[MISN_FELLON].primaryType[1] = M_PROTECT_TARGET;
+       missions[MISN_FELLON].target1[1] = CD_REBELCARRIER;
+       missions[MISN_FELLON].targetValue1[1] = 2;
+       missions[MISN_FELLON].completed1[1] = OB_CONDITION;
+
+       sprintf(missions[MISN_FELLON].primaryObjective[2],
+               "Destroy all present WEAPCO forces");
+       missions[MISN_FELLON].primaryType[2] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_FELLON].completed1[2] = OB_INCOMPLETE;
+
+       missions[MISN_FELLON].addAliens = ALWAYS;
+
+
+       sprintf(missions[MISN_SIVEDI].primaryObjective[0],
+               "Collect 25 pieces of Ore");
+       missions[MISN_SIVEDI].primaryType[0] = M_COLLECT;
+       missions[MISN_SIVEDI].target1[0] = P_ORE;
+       missions[MISN_SIVEDI].targetValue1[0] = 25;
+       missions[MISN_SIVEDI].completed1[0] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_SIVEDI].secondaryObjective[0],
+               "Collect 25 pieces of Ore");
+       missions[MISN_SIVEDI].secondaryType[0] = M_COLLECT;
+       missions[MISN_SIVEDI].target2[0] = P_ORE;
+       missions[MISN_SIVEDI].targetValue2[0] = 25;
+       missions[MISN_SIVEDI].completed2[0] = OB_INCOMPLETE;
+
+       missions[MISN_SIVEDI].addAliens = ALWAYS;
+
+
+       sprintf(missions[MISN_ALMARTHA].primaryObjective[0],
+               "Collect $2000 to pay mercenary");
+       missions[MISN_ALMARTHA].primaryType[0] = M_COLLECT;
+       missions[MISN_ALMARTHA].target1[0] = P_CASH;
+       missions[MISN_ALMARTHA].targetValue1[0] = 2000;
+       missions[MISN_ALMARTHA].completed1[0] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_ALMARTHA].primaryObjective[1],
+               "Destroy all remaining WEAPCO fighters");
+       missions[MISN_ALMARTHA].primaryType[1] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_ALMARTHA].completed1[1] = OB_INCOMPLETE;
+
+       missions[MISN_ALMARTHA].addAliens = ALWAYS;
+
+
+       sprintf(missions[MISN_POSWIC].primaryObjective[0], "Destroy escorts");
+       missions[MISN_POSWIC].primaryType[0] = M_DESTROY_TARGET_TYPE;
+       missions[MISN_POSWIC].target1[0] = CD_ESCORT;
+       missions[MISN_POSWIC].targetValue1[0] = 5;
+       missions[MISN_POSWIC].completed1[0] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_POSWIC].primaryObjective[1],
+               "Disable executive transport");
+       missions[MISN_POSWIC].primaryType[1] = M_ESCAPE_TARGET;
+       missions[MISN_POSWIC].target1[1] = CD_BOSS;
+       missions[MISN_POSWIC].targetValue1[1] = 1;
+       missions[MISN_POSWIC].completed1[1] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_POSWIC].primaryObjective[2],
+               "Destroy all remaining WEAPCO fighters");
+       missions[MISN_POSWIC].primaryType[2] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_POSWIC].completed1[2] = OB_INCOMPLETE;
+
+       missions[MISN_POSWIC].addAliens = NORMAL;
+
+
+       sprintf(missions[MISN_ELLESH].primaryObjective[0],
+               "Destroy executive transport");
+       missions[MISN_ELLESH].primaryType[0] = M_DESTROY_TARGET_TYPE;
+       missions[MISN_ELLESH].target1[0] = CD_BOSS;
+       missions[MISN_ELLESH].targetValue1[0] = 1;
+       missions[MISN_ELLESH].completed1[0] = OB_INCOMPLETE;
+
+       missions[MISN_ELLESH].addAliens = ALWAYS;
+
+
+       sprintf(missions[MISN_PLUTO].primaryObjective[0],
+               "Destroy planetary guardian");
+       missions[MISN_PLUTO].primaryType[0] = M_DESTROY_TARGET_TYPE;
+       missions[MISN_PLUTO].target1[0] = CD_PLUTOBOSS;
+       missions[MISN_PLUTO].targetValue1[0] = 1;
+       missions[MISN_PLUTO].completed1[0] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_PLUTO].primaryObjective[1],
+               "Destroy all remaining WEAPCO fighters");
+       missions[MISN_PLUTO].primaryType[1] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_PLUTO].completed1[1] = OB_INCOMPLETE;
+
+       missions[MISN_PLUTO].timeLimit1[0] = 5;
+       missions[MISN_PLUTO].timeLimit1[1] = 5;
+
+       missions[MISN_PLUTO].addAliens = ALWAYS;
+
+
+       sprintf(missions[MISN_NEPTUNE].primaryObjective[0],
+               "Destroy planetary guardian");
+       missions[MISN_NEPTUNE].primaryType[0] = M_DESTROY_TARGET_TYPE;
+       missions[MISN_NEPTUNE].target1[0] = CD_NEPTUNEBOSS;
+       missions[MISN_NEPTUNE].targetValue1[0] = 1;
+       missions[MISN_NEPTUNE].completed1[0] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_NEPTUNE].primaryObjective[1],
+               "Destroy all remaining WEAPCO fighters");
+       missions[MISN_NEPTUNE].primaryType[1] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_NEPTUNE].completed1[1] = OB_INCOMPLETE;
+
+       missions[MISN_NEPTUNE].timeLimit1[0] = 5;
+       missions[MISN_NEPTUNE].timeLimit1[1] = 5;
+
+       missions[MISN_NEPTUNE].addAliens = ALWAYS;
+
+
+       sprintf(missions[MISN_URANUS].primaryObjective[0],
+               "Destroy all present WEAPCO forces");
+       missions[MISN_URANUS].primaryType[0] = M_DESTROY_TARGET_TYPE;
+       missions[MISN_URANUS].target1[0] = CD_URANUSBOSS;
+       missions[MISN_URANUS].targetValue1[0] = 1;
+       missions[MISN_URANUS].completed1[0] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_URANUS].primaryObjective[1],
+               "Destroy all remaining WEAPCO fighters");
+       missions[MISN_URANUS].primaryType[1] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_URANUS].completed1[1] = OB_INCOMPLETE;
+
+       missions[MISN_URANUS].timeLimit1[0] = 5;
+       missions[MISN_URANUS].timeLimit1[1] = 5;
+
+       missions[MISN_URANUS].addAliens = ALWAYS;
+
+
+       sprintf(missions[MISN_SATURN].primaryObjective[0],
+               "Destroy outer defence systems");
+       missions[MISN_SATURN].primaryType[0] = M_DESTROY_TARGET_TYPE;
+       missions[MISN_SATURN].target1[0] = CD_MOBILE_RAY;
+       missions[MISN_SATURN].targetValue1[0] = 6;
+       missions[MISN_SATURN].completed1[0] = OB_INCOMPLETE;
+
+       sprintf(missions[MISN_SATURN].primaryObjective[1],
+               "Destroy all remaining WEAPCO craft");
+       missions[MISN_SATURN].primaryType[1] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_SATURN].completed1[1] = OB_INCOMPLETE;
+
+       missions[MISN_SATURN].addAliens = NORMAL;
+
+
+       sprintf(missions[MISN_JUPITER].primaryObjective[0],
+               "Investigate distress call");
+       missions[MISN_JUPITER].primaryType[0] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_JUPITER].completed1[0] = OB_CONDITION;
+
+       sprintf(missions[MISN_JUPITER].primaryObjective[1], "Defeat Krass Tyler");
+       missions[MISN_JUPITER].primaryType[1] = M_DESTROY_TARGET_TYPE;
+       missions[MISN_JUPITER].target1[1] = CD_KRASS;
+       missions[MISN_JUPITER].targetValue1[1] = 1;
+       missions[MISN_JUPITER].completed1[1] = OB_HIDDEN;
+
+       sprintf(missions[MISN_JUPITER].primaryObjective[2],
+               "Destroy Krass' support group");
+       missions[MISN_JUPITER].primaryType[2] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_JUPITER].target1[1] = CD_FIREFLY;
+       missions[MISN_JUPITER].targetValue1[1] = 4;
+       missions[MISN_JUPITER].completed1[2] = OB_HIDDEN;
+
+       missions[MISN_JUPITER].addAliens = ALWAYS;
+
+
+       sprintf(missions[MISN_MARS].primaryObjective[0], "Navigate asteroid belt");
+       missions[MISN_MARS].primaryType[0] = M_DESTROY_TARGET_TYPE;
+       missions[MISN_MARS].target1[0] = CD_BOSS;
+       missions[MISN_MARS].targetValue1[0] = 1;
+       missions[MISN_MARS].completed1[0] = OB_INCOMPLETE;
+
+       missions[MISN_MARS].timeLimit1[0] = 2;
+
+       missions[MISN_MARS].addAliens = ALWAYS;
+
+
+       sprintf(missions[MISN_EARTH].primaryObjective[0],
+               "Destroy WEAPCO frontline forces");
+       missions[MISN_EARTH].primaryType[0] = M_DESTROY_TARGET_TYPE;
+       missions[MISN_EARTH].target1[0] = CD_ANY;
+       missions[MISN_EARTH].targetValue1[0] = 100;
+       missions[MISN_EARTH].completed1[0] = OB_INCOMPLETE;
+
+       missions[MISN_EARTH].addAliens = ALWAYS;
+
+
+       sprintf(missions[MISN_VENUS].primaryObjective[0], "Defeat Kline");
+       missions[MISN_VENUS].primaryType[0] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_VENUS].completed1[0] = OB_INCOMPLETE;
+
+
+       strcpy(missions[MISN_INTERCEPTION].primaryObjective[0], "");
+       missions[MISN_INTERCEPTION].primaryType[0] = M_DESTROY_ALL_TARGETS;
+       missions[MISN_INTERCEPTION].completed1[0] = OB_INCOMPLETE;
+}
diff --git a/src/missions.h b/src/missions.h
new file mode 100644 (file)
index 0000000..e7f4008
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MISSIONS_H
+#define MISSIONS_H
+
+extern Planet systemPlanet[10];
+extern mission currentMission;
+
+extern void initPlanetMissions(signed char system);
+extern void checkForBossMission();
+extern void updateSystemStatus();
+extern void setMission(int mission);
+extern void checkTimer();
+extern void updateMissionRequirements(int type, int id, int value);
+extern bool allMissionsCompleted();
+extern bool missionFailed();
+extern void missionBriefScreen();
+extern void missionFinishedScreen();
+extern void initMissions();
+
+#endif
diff --git a/src/player.cpp b/src/player.cpp
new file mode 100644 (file)
index 0000000..188cbe1
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012, 2013 Guus Sliepen
+Copyright (C) 2012, 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+object player;
+bool player_chargerFired = false;
+
+/*
+Initialises the player for a new game.
+*/
+void initPlayer()
+{
+       player.active = true;
+       player.x = screen->w / 2;
+       player.y = screen->h / 2;
+       player.speed = 2;
+       player.systemPower = player.maxShield;
+       player.face = 0;
+
+       player.image[0] = shipShape[0];
+       player.image[1] = 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 (weapon[W_PLAYER_WEAPON].ammo[0] < game.minPlasmaOutput)
+               weapon[W_PLAYER_WEAPON].ammo[0] = game.minPlasmaOutput;
+       if (weapon[W_PLAYER_WEAPON].damage < game.minPlasmaDamage)
+               weapon[W_PLAYER_WEAPON].damage = game.minPlasmaDamage;
+       if (weapon[W_PLAYER_WEAPON].reload[0] > rate2reload[game.minPlasmaRate])
+               weapon[W_PLAYER_WEAPON].reload[0] = rate2reload[game.minPlasmaRate];
+
+       player.hit = 0;
+
+       engine.lowShield = (player.maxShield >= 3) ? (player.maxShield / 3) : 1;
+       engine.averageShield = engine.lowShield + engine.lowShield;
+
+       if ((player.weaponType[1] == W_CHARGER) || (player.weaponType[1] == W_LASER))
+               player.ammo[1] = 0;
+}
+
+void player_setTarget(int index)
+{
+       engine.targetIndex = index;
+       engine.targetShield = 85;
+       engine.targetShield /= aliens[index].shield;
+}
+
+void player_checkShockDamage(float x, float y)
+{
+       float distX = fabsf(x - player.x);
+       float distY = fabsf(y - player.y);
+
+       // 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;
+
+       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);
+               LIMIT(player.shield, 0, player.maxShield);
+               player.hit = 10;
+       }
+}
+
+void exitPlayer()
+{
+       player_chargerFired = false;
+
+       if ((player.weaponType[1] == W_CHARGER) || (player.weaponType[1] == W_LASER))
+               player.ammo[1] = 0;
+}
+
+void flushInput()
+{
+       for (int i = 0; i < KEY_LAST; i++)
+               engine.keyState[i] = 0;
+
+       while (SDL_PollEvent(&engine.event)){}
+}
+
+static enum keys mapkey(uint32_t code) {
+       switch (code) {
+               case SDLK_UP:
+               case SDLK_KP_8:
+                       return KEY_UP;
+               case SDLK_DOWN:
+               case SDLK_KP_2:
+               case SDLK_KP_5:
+                       return KEY_DOWN;
+               case SDLK_LEFT:
+               case SDLK_KP_4:
+                       return KEY_LEFT;
+               case SDLK_RIGHT:
+               case SDLK_KP_6:
+                       return KEY_RIGHT;
+               case SDLK_LCTRL:
+               case SDLK_RCTRL:
+               case SDLK_RETURN:
+               case SDLK_z:
+               case SDLK_y:
+               case SDLK_c:
+               case SDLK_a:
+               case SDLK_d:
+               case SDLK_f:
+               case SDLK_SLASH:
+               case SDLK_COMMA:
+               case SDLK_1:
+               case SDLK_3:
+               case SDLK_KP_0:
+                       return KEY_FIRE;
+               case SDLK_SPACE:
+               case SDLK_x:
+               case SDLK_s:
+               case SDLK_PERIOD:
+               case SDLK_2:
+               case SDLK_KP_1:
+                       return KEY_ALTFIRE;
+               case SDLK_LSHIFT:
+               case SDLK_RSHIFT:
+               case SDLK_LALT:
+               case SDLK_RALT:
+               case SDLK_KP_7:
+               case SDLK_KP_9:
+                       return KEY_SWITCH;
+               case SDLK_p:
+                       return KEY_PAUSE;
+               case SDLK_ESCAPE:
+               case SDLK_q:
+               case SDLK_BACKSPACE:
+                       return KEY_ESCAPE;
+               case SDLK_F11:
+                       return KEY_FULLSCREEN;
+               default:
+                       return KEY_DUMMY;
+       }
+}
+
+void getPlayerInput()
+{
+       while (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[KEY_FIRE] = 1;
+                                       if (engine.event.button.button == SDL_BUTTON_RIGHT) engine.keyState[KEY_ALTFIRE] = 1;
+                               }
+                               break;
+
+                       case SDL_KEYDOWN:
+                               engine.keyState[mapkey(engine.event.key.keysym.sym)] = 1;
+
+                               if (engine.gameSection != SECTION_GAME)
+                                       engine.paused = false;
+
+                               break;
+
+                       case SDL_KEYUP:
+                               if (engine.event.key.keysym.sym != SDLK_p)
+                                       engine.keyState[mapkey(engine.event.key.keysym.sym)] = 0;
+                               break;
+
+                       case SDL_JOYBUTTONDOWN:
+                       case SDL_JOYBUTTONUP:
+                               switch (engine.event.jbutton.button)
+                               {
+                                       case 0:
+                                       case 3:
+                                               engine.keyState[KEY_ALTFIRE] = engine.event.jbutton.state;
+                                               break;
+                                       case 1:
+                                       case 2:
+                                               engine.keyState[KEY_FIRE] = engine.event.jbutton.state;
+                                               break;
+                                       case 4:
+                                       case 6:
+                                               engine.keyState[KEY_ESCAPE] = engine.event.jbutton.state;
+                                               break;
+                                       case 5:
+                                       case 7:
+                                       case 8:
+                                               engine.keyState[KEY_SWITCH] = engine.event.jbutton.state;
+                                               break;
+                                       case 9:
+                                               if (engine.event.jbutton.state)
+                                                       engine.keyState[KEY_PAUSE] = 1;
+                                               break;
+                               }
+                               break;
+
+                       case SDL_JOYHATMOTION:
+                               engine.keyState[KEY_UP]    = engine.event.jhat.value & SDL_HAT_UP;
+                               engine.keyState[KEY_DOWN]  = engine.event.jhat.value & SDL_HAT_DOWN;
+                               engine.keyState[KEY_LEFT]  = engine.event.jhat.value & SDL_HAT_LEFT;
+                               engine.keyState[KEY_RIGHT] = engine.event.jhat.value & SDL_HAT_RIGHT;
+                               break;
+
+                       case SDL_JOYAXISMOTION:
+                               static bool prevjoyup, prevjoydown, prevjoyleft, prevjoyright;
+                               if (engine.event.jaxis.axis & 1) {
+                                       bool joyup = engine.event.jaxis.value < -16384;
+                                       bool joydown = engine.event.jaxis.value >= 16384;
+                                       if(joyup != prevjoyup)
+                                               engine.keyState[KEY_UP] = prevjoyup = joyup;
+                                       if(joydown != prevjoydown)
+                                               engine.keyState[KEY_DOWN] = prevjoydown = joydown;
+                               } else {
+                                       bool joyleft = engine.event.jaxis.value < -16384;
+                                       bool joyright = engine.event.jaxis.value >= 16384;
+                                       if(joyleft != prevjoyleft)
+                                               engine.keyState[KEY_LEFT] = prevjoyleft = joyleft;
+                                       if(joyright != prevjoyright)
+                                               engine.keyState[KEY_RIGHT] = prevjoyright = joyright;
+                               }
+                               break;
+
+                       case SDL_WINDOWEVENT:
+                               if (engine.autoPause &&
+                                               (engine.event.window.event == SDL_WINDOWEVENT_FOCUS_LOST))
+                                       engine.paused = true;
+                               break;
+               }
+
+               if (engine.keyState[KEY_FULLSCREEN])
+               {
+                       engine.fullScreen = !engine.fullScreen;
+                       SDL_SetWindowFullscreen(window, engine.fullScreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
+                       engine.keyState[KEY_FULLSCREEN] = 0;
+               }
+       }
+
+       if (engine.gameSection == SECTION_INTERMISSION)
+       {
+               // Get the current mouse position
+               static int px = -1, py = -1;
+               int x, y;
+               SDL_GetMouseState(&x, &y);
+               if (px == x && py == y) {
+                       if(engine.keyState[KEY_UP] && engine.cursor_y > 0)
+                               engine.cursor_y -= 4;
+                       if(engine.keyState[KEY_DOWN] && engine.cursor_y < screen->h - 4)
+                               engine.cursor_y += 4;
+                       if(engine.keyState[KEY_LEFT] && engine.cursor_x > 0)
+                               engine.cursor_x -= 4;
+                       if(engine.keyState[KEY_RIGHT] && engine.cursor_x < screen->w - 4)
+                               engine.cursor_x += 4;
+               } else {
+                       engine.cursor_x = px = x;
+                       engine.cursor_y = py = y;
+               }
+       }
+
+}
+
+void leaveSector()
+{
+       engine.keyState[KEY_UP] = 0;
+       engine.keyState[KEY_DOWN] = 0;
+       engine.keyState[KEY_LEFT] = 0;
+       engine.keyState[KEY_RIGHT] = 0;
+       engine.keyState[KEY_FIRE] = 0;
+       engine.keyState[KEY_ALTFIRE] = 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 > screen->h / 2)
+                               player.y--;
+                       if (player.y < screen->h / 2)
+                               player.y++;
+               }
+
+               if (player.x <= -100)
+               {
+                       engine.done = 2;
+                       audio_playSound(SFX_FLY, screen->w / 2);
+               }
+       }
+
+       if (engine.done == 2)
+       {
+               player.face = 0;
+               player.x += 12;
+               engine.ssx -= 0.2;
+               if (player.x > (2 * screen->w))
+                       engine.done = 1;
+       }
+}
+
diff --git a/src/player.h b/src/player.h
new file mode 100644 (file)
index 0000000..0f70ada
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PLAYER_H
+#define PLAYER_H
+
+extern object player;
+extern bool player_chargerFired;
+
+extern void initPlayer();
+void player_setTarget(int index);
+void player_checkShockDamage(float x, float y);
+extern void exitPlayer();
+extern void flushInput();
+extern void getPlayerInput();
+extern void leaveSector();
+
+#endif
diff --git a/src/resources.cpp b/src/resources.cpp
new file mode 100644 (file)
index 0000000..09ead65
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012, 2013 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+void loadBackground(const char *filename)
+{
+       if (background != NULL)
+       {
+               SDL_FreeSurface(background);
+               background = NULL;
+       }
+       background = loadImage(filename);
+       SDL_SetColorKey(background, 0, 0);
+}
+
+void loadGameGraphics()
+{
+       int index;
+       char string[75] = "";
+       FILE *fp;
+       Uint32 *p32;
+       Uint16 *p16;
+       Uint8 *p8;
+
+       freeGraphics();
+
+       shipShape[0] = loadImage("gfx/firefly1.png");
+       shipShape[1] = loadImage("gfx/firefly2.png");
+
+       switch(game.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;
+       }
+
+       fp = fopen(string, "rb");
+
+       if (fp == NULL)
+               exit(1);
+
+       while (fscanf(fp, "%d %s", &index, string) == 2)
+       {
+               shipShape[index] = loadImage(string);
+       }
+
+       fclose(fp);
+
+       /*
+       Create images of ships being hit that show a lot of red
+       */
+       for (int i = SHIP_HIT_INDEX ; i < MAX_SHIPSHAPES ; i++)
+       {
+               if (shipShape[i - SHIP_HIT_INDEX] == NULL)
+                       continue;
+               shipShape[i] = createSurface(shipShape[i - SHIP_HIT_INDEX]->w,
+                       shipShape[i - SHIP_HIT_INDEX]->h);
+               SDL_SetSurfaceBlendMode(shipShape[i - SHIP_HIT_INDEX], SDL_BLENDMODE_NONE);
+               blit(shipShape[i - SHIP_HIT_INDEX], 0, 0, shipShape[i]);
+               SDL_SetSurfaceBlendMode(shipShape[i - SHIP_HIT_INDEX], SDL_BLENDMODE_BLEND);
+
+               switch (shipShape[i]->format->BitsPerPixel)
+               {
+                       case 32:
+                               SDL_LockSurface(shipShape[i]);
+                               p32 = (Uint32 *)shipShape[i]->pixels;
+                               for (int j = 0; j < shipShape[i]->w * shipShape[i]->h; j++)
+                               {
+                                       if (p32[j])
+                                               p32[j] |= shipShape[i]->format->Rmask;
+                               }
+                               SDL_UnlockSurface(shipShape[i]);
+                               break;
+
+                       case 16:
+                               SDL_LockSurface(shipShape[i]);
+                               p16 = (Uint16 *)shipShape[i]->pixels;
+                               for (int j = 0; j < shipShape[i]->w * shipShape[i]->h; j++)
+                               {
+                                       if (p16[j])
+                                               p16[j] |= shipShape[i]->format->Rmask;
+                               }
+                               SDL_UnlockSurface(shipShape[i]);
+                               break;
+
+                       case 8:
+                               SDL_LockSurface(shipShape[i]);
+                               p8 = (Uint8 *)shipShape[i]->pixels;
+                               for (int j = 0; j < shipShape[i]->w * shipShape[i]->h; j++)
+                               {
+                                       if (p8[j])
+                                               p8[j] = SDL_MapRGB(shipShape[i]->format, 255, 0, 0);
+                               }
+                               SDL_UnlockSurface(shipShape[i]);
+                               break;
+               }
+
+               SDL_SetColorKey(shipShape[i], SDL_TRUE,
+                       SDL_MapRGB(shipShape[i]->format, 0, 0, 0));
+       }
+
+       strcpy(string, "data/resources_all.dat");
+
+       fp = fopen(string, "rb");
+
+       while (fscanf(fp, "%d %s", &index, string) == 2)
+       {
+               shape[index] = loadImage(string);
+       }
+
+       fclose(fp);
+
+       loadBackground(systemBackground[game.system]);
+
+       for (int i = 0 ; i < CD_MAX ; i++)
+       {
+               if (shipShape[alien_defs[i].imageIndex[0]] != NULL)
+               {
+                       alien_defs[i].image[0] = shipShape[alien_defs[i].imageIndex[0]];
+                       alien_defs[i].image[1] = shipShape[alien_defs[i].imageIndex[1]];
+                       alien_defs[i].engineX = alien_defs[i].image[0]->w;
+                       alien_defs[i].engineY = (alien_defs[i].image[0]->h / 2);
+               }
+       }
+
+       setWeaponShapes();
+}
+
+
+/*
+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++)
+       {
+               image = IMG_Load("gfx/smallFont.png");
+
+               if (image == NULL) {
+                       printf("Couldn't load game font! (%s) Exitting.\n", SDL_GetError());
+                  exit(1);
+               }
+
+               switch(i)
+               {
+                       case 1:
+                               SDL_SetSurfaceColorMod(image, 255, 0, 0);
+                               break;
+                       case 2:
+                               SDL_SetSurfaceColorMod(image, 255, 255, 0);
+                               break;
+                       case 3:
+                               SDL_SetSurfaceColorMod(image, 0, 255, 0);
+                               break;
+                       case 4:
+                               SDL_SetSurfaceColorMod(image, 0, 255, 255);
+                               break;
+                       case 5:
+                               SDL_SetSurfaceColorMod(image, 0, 0, 10);
+                               break;
+               }
+
+               newImage = SDL_ConvertSurface(image, screen->format, 0);
+
+               fontShape[i] = setTransparent(newImage);
+
+               SDL_FreeSurface(image);
+       }
+}
diff --git a/src/resources.h b/src/resources.h
new file mode 100644 (file)
index 0000000..f0e75c5
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef RESOURCES_H
+#define RESOURCES_H
+
+extern void loadBackground(const char *filename);
+extern void loadGameGraphics();
+extern void loadFont();
+
+#endif
diff --git a/src/script.cpp b/src/script.cpp
new file mode 100644 (file)
index 0000000..a2bc6fa
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012, 2013 Guus Sliepen
+Copyright (C) 2012, 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+static cutMsg cutMessage[10];
+static event gameEvent[20];
+
+static void setKlineGreeting()
+{
+       static const char *greet[] = {
+               "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 < 20 ; i++)
+       {
+               gameEvent[i].time = 0;
+               strcpy(gameEvent[i].message, "");
+               gameEvent[i].entity = -1;
+               gameEvent[i].flag = 0;
+       }
+       
+       if ((game.area == MISN_INTERCEPTION) &&
+                       (aliens[ALIEN_KLINE].classDef == CD_KLINE) &&
+                       (aliens[ALIEN_KLINE].active))
+               setKlineGreeting();
+
+       char filename[255];
+       sprintf(filename, "data/script%d.txt", game.area);
+
+       FILE *fp;
+       int i = 0;
+
+       int time, entity, flags;
+       char face[255], message[255];
+
+       fp = fopen(filename, "rb");
+       if (fp == NULL)
+               return;
+
+       while (fscanf(fp, "%d %s %d %d ", &time, face, &entity, &flags) == 4)
+       {
+               if (fscanf(fp, "%[^\n]%*c", message) < 1)
+                       strcpy(message, "Error: Text missing");
+
+               gameEvent[i].time = time;
+               gameEvent[i].face = getFace(face);
+               gameEvent[i].entity = entity;
+               gameEvent[i].flag = flags;
+               strcpy(gameEvent[i].message, message);
+
+               i++;
+       }
+
+       fclose(fp);
+}
+
+void checkScriptEvents()
+{
+       for (int i = 0 ; i < 20 ; 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)
+                               {
+                                       aliens[gameEvent[i].entity].flags += gameEvent[i].flag;
+                               }
+                               else
+                               {
+                                       aliens[gameEvent[i].entity].active = true;
+                                       aliens[gameEvent[i].entity].x = ((int)player.x +
+                                               RANDRANGE(400, 800));
+                                       aliens[gameEvent[i].entity].y = ((int)player.y +
+                                               RANDRANGE(-400, 800));
+                               }
+                       }
+
+                       gameEvent[i].time = 0;
+               }
+       }
+}
+
+void syncScriptEvents()
+{
+       for (int i = 0 ; i < 20 ; i++)
+       {
+               if (gameEvent[i].time < 0)
+               {
+                       gameEvent[i].time = engine.timeTaken + abs(gameEvent[i].time);
+               }
+       }
+}
+
+static void setScene(int scene)
+{
+       FILE *fp;
+       char string[255], face[255];
+       float sx, sy, x, y, speed;
+       int index, shape;
+
+       sprintf(string, "data/cutscene%d.dat", scene);
+
+       fp = fopen(string, "rb");
+
+       // Load in the specified background
+       if (fscanf(fp, "%s", string) < 1)
+       {
+               printf("Warning: didn't find a background definition for \"%s\"\n", string);
+               strcpy(string, "gfx/spirit.jpg");
+       }
+       loadBackground(string);
+
+       // Set the star speed
+       if (fscanf(fp, "%f %f", &sx, &sy) < 2)
+               printf("Warning: failed to read star speed data for cutscene");
+       engine.ssx = sx;
+       engine.ssy = sy;
+
+       // Read in the specs for each ship
+       while (fscanf(fp, "%d %d %f %f %f", &index, &shape, &x, &y, &speed) == 5)
+       {
+               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)
+               {
+                       aliens[index].image[0] = shipShape[shape];
+                       aliens[index].x = x;
+                       aliens[index].y = y;
+                       aliens[index].dx = speed;
+                       aliens[index].active = true;
+               }
+       }
+
+       // And finally read in the messages
+       index = 0;
+       while (fscanf(fp, "%s%*c %[^\n]", face, string) == 2)
+       {
+               if (strcmp(string, "@none@") == 0)
+                       break;
+
+               cutMessage[index].face = getFace(face);
+               strcpy(cutMessage[index].message, string);
+
+               index++;
+       }
+
+       fclose(fp);
+}
+
+void doCutscene(int scene)
+{
+       clearScreen(black);
+       updateScreen();
+       clearScreen(black);
+
+       engine.keyState[KEY_FIRE] = 0; 
+       engine.keyState[KEY_ALTFIRE] = 0;
+
+       engine.ssx = -0.5;
+       engine.ssy = 0;
+       engine.smx = 0;
+       engine.smy = 0;
+
+       flushBuffer();
+       freeGraphics();
+       resetLists();
+       loadGameGraphics();
+
+       for (int i = 0 ; i < 15 ; i++)
+       {
+               aliens[i] = alien_defs[0];
+               aliens[i].face = 0;
+               aliens[i].active = false;
+       }
+
+       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++)
+       {
+               aliens[i].engineX = aliens[i].image[0]->w;
+               aliens[i].engineY = (aliens[i].image[0]->h / 2);
+       }
+
+       bool showMessage = false;
+       signed char currentMessage = -1;
+       int timer = 60 * 4;
+
+       drawBackGround();
+
+       SDL_Surface *face;
+       
+       flushInput();
+
+       while (true)
+       {
+               updateScreen();
+               unBuffer();
+               getPlayerInput();
+               doStarfield();
+               game_doExplosions();
+
+               for (int i = 0 ; i < 15 ; i++)
+               {
+                       if (aliens[i].active)
+                       {
+                               explosion_addEngine(&aliens[i]);
+                               if (scene == 0 && i > 0 && (timer % 15) == i) {
+                                       aliens[i].dx += (drand48() - 0.5) * 0.1;
+                                       aliens[i].dy += (drand48() - 0.5) * 0.1;
+                                       if (aliens[i].x > 500 - timer)
+                                               aliens[i].dx -= 0.2;
+                                       if (aliens[i].x < 0)
+                                               aliens[i].dx += 0.2;
+                               }
+                               aliens[i].x += aliens[i].dx;
+                               aliens[i].y += aliens[i].dy;
+                               aliens[i].x += engine.ssx + engine.smx;
+                               blit(aliens[i].image[0], (int)aliens[i].x, (int)aliens[i].y);
+                               if (aliens[i].x > (screen->w + 50))
+                               {
+                                       aliens[i].x = -50;
+                                       aliens[i].y = rand() % (screen->h - 40);
+                               }
+                               if (aliens[i].y < -50)
+                                       aliens[i].y = (screen->h + 50);
+                               if (aliens[i].y > (screen->h + 50))
+                                       aliens[i].y = -50;
+                       }
+               }
+
+               timer--;
+               if (timer == 0)
+               {
+                       showMessage = !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 = shape[cutMessage[currentMessage].face];
+                               createMessageBox(face, cutMessage[currentMessage].message, 0);
+                       }
+               }
+
+               if ((showMessage) && (messageBox != NULL))
+                       blit(messageBox, (screen->w - messageBox->w) / 2, screen->h - 100);
+
+               drawString("Press [Escape] to skip", -1, 580, FONT_WHITE);
+
+               delayFrame();
+
+               if ((engine.keyState[KEY_ESCAPE]) || (engine.keyState[KEY_FIRE]) ||
+                               (engine.keyState[KEY_ALTFIRE]))
+                       break;
+       }
+
+       flushBuffer();
+       freeGraphics();
+       clearScreen(black);
+       updateScreen();
+}
diff --git a/src/script.h b/src/script.h
new file mode 100644 (file)
index 0000000..29ec9ec
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SCRIPT_H
+#define SCRIPT_H
+
+extern void loadScriptEvents();
+extern void checkScriptEvents();
+extern void syncScriptEvents();
+extern void doCutscene(int scene);
+
+#endif
diff --git a/src/ship.cpp b/src/ship.cpp
new file mode 100644 (file)
index 0000000..45e8b95
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+/*
+Fill in later...
+*/
+void ship_fireBullet(object *ship, int weaponType)
+{
+       if (ship->reload[weaponType] > 0)
+               return;
+
+       int y = (ship->image[0]->h) / 5;
+
+       // Remove some ammo from the player
+       if ((ship == &player) && (weaponType == 1) && (!engine.cheatAmmo))
+               player.ammo[1]--;
+
+       object *theWeapon = &weapon[ship->weaponType[weaponType]];
+
+       switch(theWeapon->id)
+       {
+               case WT_PLASMA:
+               case WT_SPREAD:
+               case WT_DIRECTIONAL:
+                       audio_playSound(SFX_PLASMA, ship->x);
+                       break;
+               case WT_ROCKET:
+                       audio_playSound(SFX_MISSILE, ship->x);
+                       break;
+               case WT_LASER:
+                       audio_playSound(SFX_LASER, ship->x);
+                       break;
+               case WT_CHARGER:
+                       audio_playSound(SFX_PLASMA3, ship->x);
+                       break;
+       }
+
+       if (theWeapon->flags & WF_SPREAD && theWeapon->ammo[0] >= 3)
+       {
+               bullet_add(theWeapon, ship, y * 2, -1);
+               bullet_add(theWeapon, ship, y * 4, 1);
+
+               if (theWeapon->ammo[0] != 4)
+                       bullet_add(theWeapon, ship, y * 3, 0);
+               else
+               {
+                       bullet_add(theWeapon, ship, y * 2, 0);
+                       bullet_add(theWeapon, ship, y * 4, 0);
+               }
+
+               if (theWeapon->ammo[0] == 5)
+               {
+                       bullet_add(theWeapon, ship, y * 1, -2);
+                       bullet_add(theWeapon, ship, y * 5, 2);
+               }
+       }
+       else
+       {
+               if(theWeapon->ammo[0] & 1)
+                       bullet_add(theWeapon, ship, y * 3, 0);
+
+               if(theWeapon->ammo[0] >= 2)
+               {
+                       bullet_add(theWeapon, ship, y * 2, 0);
+                       bullet_add(theWeapon, ship, y * 4, 0);
+               }
+
+               if(theWeapon->ammo[0] >= 4)
+               {
+                       bullet_add(theWeapon, ship, y * 1, 0);
+                       bullet_add(theWeapon, ship, y * 5, 0);
+               }
+       }
+
+       // Reset the weapon reload time. Double it if it is not friendly or
+       // a boss or Kline
+       ship->reload[weaponType] = theWeapon->reload[0];
+       if ((ship->flags & FL_WEAPCO) && (ship != &aliens[ALIEN_BOSS]) &&
+                       (ship != &aliens[ALIEN_KLINE]) && (theWeapon->id != W_LASER))
+               ship->reload[weaponType] *= 2;
+
+       if ((engine.cheatAmmo) || (theWeapon->id == WT_LASER))
+               return;
+
+       if ((ship == &player) && (weaponType == 0))
+       {
+               if (player.ammo[0] > 0)
+               {
+                       player.ammo[0]--;
+                       if (player.ammo[0] <= 0)
+                       {
+                               weapon[W_PLAYER_WEAPON].ammo[0] = game.minPlasmaOutput;
+                               weapon[W_PLAYER_WEAPON].damage = game.minPlasmaDamage;
+                               weapon[W_PLAYER_WEAPON].reload[0] = rate2reload[game.minPlasmaRate];
+                       }
+               }
+       }
+}
+
+/*
+Fill in later...
+*/
+void ship_fireRay(object *ship)
+{
+       SDL_Rect ray;
+
+       if (ship->face == 0)
+       {
+               ray.x = (int)(ship->x + ship->image[0]->w);
+       }
+       else
+       {
+               ray.x = (int)(ship->x - 800);
+       }
+       ray.y = (int)(ship->y + ship->engineY - 1);
+       ray.h = 3;
+       ray.w = 800;
+
+       int red = SDL_MapRGB(screen->format, rand() % 256, 0x00, 0x00);
+       SDL_FillRect(screen, &ray, red);
+       addBuffer(ray.x, ray.y, ray.w, ray.h);
+
+       if (ship != &player)
+       {
+               if (player.shield > 0)
+               {
+                       if (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--;
+
+                               explosion_add(player.x, player.y, E_SMALL_EXPLOSION);
+                               audio_playSound(SFX_HIT, player.x);
+                               if (player.shield < 1)
+                               {
+                                       audio_playSound(SFX_DEATH, player.x);
+                                       audio_playSound(SFX_EXPLOSION, player.x);
+                               }
+                       }
+               }
+       }
+
+       for (int i = 0 ; i < ALIEN_MAX ; i++)
+       {
+               if (aliens[i].flags & FL_IMMORTAL)
+                       continue;
+
+               if ((aliens[i].shield > 0) && (ship != &aliens[i]) &&
+                       (ship->classDef != aliens[i].classDef))
+               {
+                       if (collision(aliens[i].x, aliens[i].y, aliens[i].image[0]->w,
+                                       aliens[i].image[0]->h, ray.x, ray.y, ray.w, ray.h))
+                       {
+                               alien_hurt(&aliens[i], ship->owner, 1, false);
+                       }
+               }
+       }
+
+       ship->ammo[0]--;
+       if (ship->ammo[0] < 1)
+               ship->flags &= ~FL_FIRERAY;
+}
diff --git a/src/ship.h b/src/ship.h
new file mode 100644 (file)
index 0000000..63b1025
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SHIP_H
+#define SHIP_H
+
+void ship_fireBullet(object *ship, int weaponType);
+void ship_fireRay(object *ship);
+
+#endif
diff --git a/src/shop.cpp b/src/shop.cpp
new file mode 100644 (file)
index 0000000..2e63cad
--- /dev/null
@@ -0,0 +1,1073 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012, 2013 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+static ShopItem shopItems[MAX_SHOPITEMS];
+static signed char shopSelectedItem;
+
+static void sell(int i);
+
+static void drawSecondaryWeaponSurface()
+{
+       char description[50] = "";
+
+       drawString("Secondary Weapon", 10, 3, FONT_WHITE, 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;
+       }
+       drawString(description, 10, 22, FONT_WHITE, shopSurface[2]);
+
+       if ((player.weaponType[1] != W_LASER) &&
+               (player.weaponType[1] != W_CHARGER) && (player.weaponType[1] != W_NONE))
+       {
+               sprintf(description, "Capacity : %d", game.maxRocketAmmo);
+               drawString(description, 10, 37, FONT_WHITE, shopSurface[2]);
+       }
+}
+
+static void adjustShopPrices()
+{
+       if (game.difficulty == DIFFICULTY_ORIGINAL)
+       {
+               shopItems[SHOP_PLASMA_MAX_OUTPUT].price = (500 *
+                       game.maxPlasmaOutput);
+               shopItems[SHOP_PLASMA_MAX_DAMAGE].price = (500 *
+                       game.maxPlasmaDamage);
+               shopItems[SHOP_PLASMA_MAX_RATE].price = (500 *
+                       (game.maxPlasmaRate * 2 - 1));
+
+               shopItems[SHOP_PLASMA_MIN_OUTPUT].price = (2000 *
+                       game.minPlasmaOutput);
+               shopItems[SHOP_PLASMA_MIN_DAMAGE].price = (2000 *
+                       game.minPlasmaDamage);
+               shopItems[SHOP_PLASMA_MIN_RATE].price = (2000 *
+                       (game.minPlasmaRate * 2 - 1));
+       }
+       else
+       {
+               shopItems[SHOP_PLASMA_MAX_OUTPUT].price = (1000 *
+                       (game.maxPlasmaOutput + 1));
+               shopItems[SHOP_PLASMA_MAX_DAMAGE].price = (1000 *
+                       (game.maxPlasmaDamage + 1));
+               shopItems[SHOP_PLASMA_MAX_RATE].price = (1000 *
+                       (game.maxPlasmaRate + 1));
+
+               shopItems[SHOP_PLASMA_MIN_OUTPUT].price = (1500 *
+                       (game.minPlasmaOutput + 1));
+               shopItems[SHOP_PLASMA_MIN_DAMAGE].price = (1500 *
+                       (game.minPlasmaDamage + 1));
+               shopItems[SHOP_PLASMA_MIN_RATE].price = (1500 *
+                       (game.minPlasmaRate + 1));
+       }
+
+       if (game.maxPlasmaOutput <= game.minPlasmaOutput)
+               shopItems[SHOP_PLASMA_MIN_OUTPUT].price += shopItems[SHOP_PLASMA_MAX_OUTPUT].price;
+       if (game.maxPlasmaDamage <= game.minPlasmaDamage)
+               shopItems[SHOP_PLASMA_MIN_DAMAGE].price += shopItems[SHOP_PLASMA_MAX_DAMAGE].price;
+       if (game.maxPlasmaRate <= game.minPlasmaRate)
+               shopItems[SHOP_PLASMA_MIN_RATE].price += shopItems[SHOP_PLASMA_MAX_RATE].price;
+
+       shopItems[SHOP_PLASMA_MAX_AMMO].price = (10 * (game.maxPlasmaAmmo - 75));
+       shopItems[SHOP_ROCKET_MAX_AMMO].price = (25 * game.maxRocketAmmo);
+       
+       if (game.maxPlasmaOutput >= game.maxPlasmaOutputLimit)
+               shopItems[SHOP_PLASMA_MAX_OUTPUT].price = 0;
+
+       if (game.maxPlasmaDamage >= game.maxPlasmaDamageLimit)
+               shopItems[SHOP_PLASMA_MAX_DAMAGE].price = 0;
+
+       if (game.maxPlasmaRate >= game.maxPlasmaRateLimit)
+               shopItems[SHOP_PLASMA_MAX_RATE].price = 0;
+               
+       if (game.minPlasmaOutput >= game.minPlasmaOutputLimit)
+               shopItems[SHOP_PLASMA_MIN_OUTPUT].price = 0;
+
+       if (game.minPlasmaDamage >= game.minPlasmaDamageLimit)
+               shopItems[SHOP_PLASMA_MIN_DAMAGE].price = 0;
+
+       if (game.minPlasmaRate >= game.minPlasmaRateLimit)
+               shopItems[SHOP_PLASMA_MIN_RATE].price = 0;
+
+       if (game.maxPlasmaAmmo >= game.maxPlasmaAmmoLimit)
+               shopItems[SHOP_PLASMA_MAX_AMMO].price = 0;
+
+       if (game.maxRocketAmmo >= game.maxRocketAmmoLimit)
+               shopItems[SHOP_ROCKET_MAX_AMMO].price = 0;
+}
+
+static void drawShop()
+{
+       adjustShopPrices();
+
+       for (int i = 0 ; i < MAX_SHOPSHAPES ; i++)
+       {
+               if (shopSurface[i] != NULL)
+               {
+                       SDL_FreeSurface(shopSurface[i]);
+               }
+       }
+
+       for (int i = 0 ; i < 3 ; i++)
+               shopSurface[i] = createSurface(246, 91);
+
+       for (int i = 0 ; i < 3 ; i++)
+       {
+               blevelRect(shopSurface[i], 0, 0, 245, 90, 0x00, 0x00, 0x55);
+               blevelRect(shopSurface[i], 0, 0, 245, 20, 0x00, 0x00, 0x99);
+       }
+
+       shopSurface[4] = alphaRect(601, 101, 0x00, 0x00, 0x00);
+       blevelRect(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:
+                       blevelRect(shopSurface[1], 0, 0, 245, 90, 0x55, 0x00, 0x00);
+                       blevelRect(shopSurface[1], 0, 0, 245, 20, 0x99, 0x00, 0x00);
+                       break;
+               case 3:
+               case 4:
+                       blevelRect(shopSurface[4], 0, 0, 600, 100, 0x33, 0x00, 0x00);
+                       break;
+               case 5:
+               case 6:
+               case 7:
+                       blevelRect(shopSurface[0], 0, 0, 245, 90, 0x55, 0x00, 0x00);
+                       blevelRect(shopSurface[0], 0, 0, 245, 20, 0x99, 0x00, 0x00);
+                       break;
+               default:
+                       blevelRect(shopSurface[2], 0, 0, 245, 90, 0x55, 0x00, 0x00);
+                       blevelRect(shopSurface[2], 0, 0, 245, 20, 0x99, 0x00, 0x00);
+                       break;
+       }
+
+       char description[100];
+       strcpy(description, "");
+
+       drawString("Primary Weapon", 10, 3, FONT_WHITE, shopSurface[0]);
+       sprintf(description, "Plasma Cannons : %d", game.minPlasmaOutput);
+       drawString(description, 10, 22, FONT_WHITE, shopSurface[0]);
+       sprintf(description, "Plasma Power   : Stage %d",
+               game.minPlasmaDamage);
+       drawString(description, 10, 37, FONT_WHITE, shopSurface[0]);
+       sprintf(description, "Cooler         : Stage %d",
+               game.minPlasmaRate);
+       drawString(description, 10, 52, FONT_WHITE, shopSurface[0]);
+
+       drawString("Powerup Weapon", 10, 3, FONT_WHITE, shopSurface[1]);
+       sprintf(description, "Plasma Output    : Stage %d",
+               game.maxPlasmaOutput);
+       drawString(description, 10, 22, FONT_WHITE, shopSurface[1]);
+       sprintf(description, "Plasma Condensor : Stage %d",
+               game.maxPlasmaDamage);
+       drawString(description, 10, 37, FONT_WHITE, shopSurface[1]);
+       sprintf(description, "Liquid Nitrogen  : Stage %d",
+               game.maxPlasmaRate);
+       drawString(description, 10, 52, FONT_WHITE, shopSurface[1]);
+       sprintf(description, "Plasma Capacity  : %d", game.maxPlasmaAmmo);
+       drawString(description, 10, 67, FONT_WHITE, shopSurface[1]);
+
+       drawSecondaryWeaponSurface();
+
+       shopSurface[3] = createSurface(601, 121);
+
+       blevelRect(shopSurface[3], 0, 0, 600, 120, 0x00, 0x00, 0x22);
+
+       drawString("Temporary Weapons", 10, 2, FONT_WHITE, shopSurface[3]);
+       drawString("Ammo and Storage", 260, 2, FONT_WHITE, shopSurface[3]);
+
+       drawString("Primary Weapons", 10, 62, FONT_WHITE, shopSurface[3]);
+
+       drawString("Secondary Weapons", 260, 62, FONT_WHITE, shopSurface[3]);
+
+       signed char icons = MAX_SHOPITEMS;
+
+       if (game.system == 0)
+               icons = SHOP_DOUBLE_ROCKETS + 1;
+       else if (game.system == 1)
+               icons = SHOP_LASER + 1;
+       else if (game.system == 2)
+               icons = SHOP_CHARGER + 1;
+
+       for (int i = 0 ; i < icons ; i++)
+       {
+               blit(shape[shopItems[i].image], shopItems[i].x - 90,
+                       shopItems[i].y - 178, shopSurface[3]);
+       }
+
+       sprintf(description, "Shield Units : %d", player.maxShield);
+       drawString(description, 10, 4, FONT_WHITE, shopSurface[4]);
+       sprintf(description, "Cash : $%d", game.cash);
+       drawString(description, 10, 80, FONT_WHITE, shopSurface[4]);
+       sprintf(description, "Plasma Cells : %.3d", player.ammo[0]);
+       drawString(description, 430, 4, FONT_WHITE, shopSurface[4]);
+       sprintf(description, "Rockets : %.3d", player.ammo[1]);
+       drawString(description, 475, 80, FONT_WHITE, shopSurface[4]);
+
+       shopSurface[5] = createSurface(601, 56);
+       blevelRect(shopSurface[5], 0, 0, 600, 35, 0x00, 0x99, 0x00);
+       blevelRect(shopSurface[5], 0, 20, 600, 35, 0x00, 0x33, 0x00);
+       drawString("Information", 5, 4, FONT_WHITE, shopSurface[5]);
+
+       switch (shopSelectedItem)
+       {
+               case -1:
+                       break;
+               case -2:
+                       drawString("You don't have enough money", 20, 30, FONT_WHITE,
+                               shopSurface[5]);
+                       break;
+               case -3:
+                       drawString("Cannot upgrade ship", 5, 22, FONT_WHITE, shopSurface[5]);
+                       drawString("Hardware capacity has been reached", 20, 38, FONT_CYAN,
+                               shopSurface[5]);
+                       break;
+               case -4:
+                       drawString("Ammunition limit reached", 20, 30, FONT_WHITE,
+                               shopSurface[5]);
+                       break;
+               case -5:
+                       drawString("You cannot sell that item", 20, 30, FONT_WHITE,
+                               shopSurface[5]);
+                       break;
+               case -6:
+                       drawString("Nothing to sell", 20, 30, FONT_WHITE, shopSurface[5]);
+                       break;
+               case -7:
+                       drawString("Rockets cannot be bought for Laser or Charger Cannon",
+                               5, 30, FONT_WHITE, shopSurface[5]);
+                       break;
+               case -8:
+                       drawString("You already have that weapon", 20, 30, FONT_WHITE,
+                               shopSurface[5]);
+                       break;
+               case -9:
+                       drawString("This weapon's ammo limit has been reached", 20, 30,
+                               FONT_WHITE, 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);
+                       }
+                       drawString(shopItems[shopSelectedItem].name, 5, 22, FONT_WHITE,
+                               shopSurface[5]);
+                       drawString(description, 20, 38, FONT_CYAN, shopSurface[5]);
+                       break;
+       }
+}
+
+void initShop()
+{
+       /* ----------- Temporary Items ----------- */
+
+       shopItems[SHOP_PLASMA_MAX_OUTPUT].price = 0; // Overwritten later
+       strcpy(shopItems[SHOP_PLASMA_MAX_OUTPUT].name, "Plasma channel splitter");
+       strcpy(shopItems[SHOP_PLASMA_MAX_OUTPUT].description,
+               "Improves poweredup plasma output");
+       shopItems[SHOP_PLASMA_MAX_OUTPUT].image = 9;
+
+       shopItems[SHOP_PLASMA_MAX_DAMAGE].price = 0; // Overwritten later
+       strcpy(shopItems[SHOP_PLASMA_MAX_DAMAGE].name, "Plasma capacity condensor");
+       strcpy(shopItems[SHOP_PLASMA_MAX_DAMAGE].description,
+               "Increases poweredup plasma damage");
+       shopItems[SHOP_PLASMA_MAX_DAMAGE].image = 10;
+
+       shopItems[SHOP_PLASMA_MAX_RATE].price = 0; // Overwritten later
+       strcpy(shopItems[SHOP_PLASMA_MAX_RATE].name, "Liquid nitrogen capsules");
+       strcpy(shopItems[SHOP_PLASMA_MAX_RATE].description,
+               "Increases plasma firing rate");
+       shopItems[SHOP_PLASMA_MAX_RATE].image = 11;
+
+       shopItems[SHOP_PLASMA_AMMO].price = 10;
+       strcpy(shopItems[SHOP_PLASMA_AMMO].name, "10 Plasma cells");
+       strcpy(shopItems[SHOP_PLASMA_AMMO].description, "Plasma ammunition");
+       shopItems[SHOP_PLASMA_AMMO].image = 12;
+
+       shopItems[SHOP_ROCKET_AMMO].price = 10;
+       strcpy(shopItems[SHOP_ROCKET_AMMO].name, "Rocket Ammo");
+       strcpy(shopItems[SHOP_ROCKET_AMMO].description,
+               "High velocity dumb fire rocket");
+       shopItems[SHOP_ROCKET_AMMO].image = 13;
+
+       /* ----------- Permanent Items ----------- */
+
+       shopItems[SHOP_PLASMA_MIN_OUTPUT].price = 0; // Overwritten later
+       strcpy(shopItems[SHOP_PLASMA_MIN_OUTPUT].name, "Additional Plasma Cannon");
+       strcpy(shopItems[SHOP_PLASMA_MIN_OUTPUT].description,
+               "Adds an extra plasma cannon to the Firefly");
+       shopItems[SHOP_PLASMA_MIN_OUTPUT].image = 14;
+
+       shopItems[SHOP_PLASMA_MIN_DAMAGE].price = 0; // Overwritten later
+       strcpy(shopItems[SHOP_PLASMA_MIN_DAMAGE].name, "Plasma Power Booster");
+       strcpy(shopItems[SHOP_PLASMA_MIN_DAMAGE].description,
+               "Increases power of plasma shots");
+       shopItems[SHOP_PLASMA_MIN_DAMAGE].image = 15;
+
+       shopItems[SHOP_PLASMA_MIN_RATE].price = 0; // Overwritten later
+       strcpy(shopItems[SHOP_PLASMA_MIN_RATE].name, "Plasma Cooling Booster");
+       strcpy(shopItems[SHOP_PLASMA_MIN_RATE].description,
+               "Permanently increases firing rate");
+       shopItems[SHOP_PLASMA_MIN_RATE].image = 16;
+
+       /* ----------- Ammo Items -------------- */
+
+       shopItems[SHOP_PLASMA_MAX_AMMO].price = 0; // Overwritten later
+       strcpy(shopItems[SHOP_PLASMA_MAX_AMMO].name, "Plasma compressor");
+       strcpy(shopItems[SHOP_PLASMA_MAX_AMMO].description,
+               "Increases plasma ammo capacity");
+       shopItems[SHOP_PLASMA_MAX_AMMO].image = 17;
+
+       shopItems[SHOP_ROCKET_MAX_AMMO].price = 0; // Overwritten later
+       strcpy(shopItems[SHOP_ROCKET_MAX_AMMO].name, "Rocket Pod");
+       strcpy(shopItems[SHOP_ROCKET_MAX_AMMO].description,
+               "Allows for an additional 5 rockets to be carried");
+       shopItems[SHOP_ROCKET_MAX_AMMO].image = 18;
+
+       /* ---------- Weaponary --------------- */
+
+       shopItems[SHOP_DOUBLE_ROCKETS].price = 2000;
+       strcpy(shopItems[SHOP_DOUBLE_ROCKETS].name, "Dual Rocket Launcher");
+       strcpy(shopItems[SHOP_DOUBLE_ROCKETS].description,
+               "Allows for two rockets to be fired at once");
+       shopItems[SHOP_DOUBLE_ROCKETS].image = 19;
+
+       shopItems[SHOP_MICRO_ROCKETS].price = 2500;
+       strcpy(shopItems[SHOP_MICRO_ROCKETS].name, "Micro Rocket Launcher");
+       strcpy(shopItems[SHOP_MICRO_ROCKETS].description,
+               "Launches several less powerful rockets at once");
+       shopItems[SHOP_MICRO_ROCKETS].image = 20;
+
+       shopItems[SHOP_LASER].price = 5000;
+       strcpy(shopItems[SHOP_LASER].name, "Laser Cannon");
+       strcpy(shopItems[SHOP_LASER].description, "Laser Cannon");
+       shopItems[SHOP_LASER].image = 21;
+
+       shopItems[SHOP_HOMING_MISSILE].price = 7500;
+       strcpy(shopItems[SHOP_HOMING_MISSILE].name, "Homing Missile Launcher");
+       sprintf(shopItems[SHOP_HOMING_MISSILE].description,
+               "Fires homing missile (max %i missiles)", maxHoming);
+       shopItems[SHOP_HOMING_MISSILE].image = 22;
+
+       shopItems[SHOP_CHARGER].price = 10000;
+       strcpy(shopItems[SHOP_CHARGER].name, "Charge Cannon");
+       strcpy(shopItems[SHOP_CHARGER].description, "A charge up cannon");
+       shopItems[SHOP_CHARGER].image = 23;
+
+       shopItems[SHOP_DOUBLE_HOMING_MISSILES].price = 10000;
+       strcpy(shopItems[SHOP_DOUBLE_HOMING_MISSILES].name,
+               "Dual Homing Missile Launcher");
+       sprintf(shopItems[SHOP_DOUBLE_HOMING_MISSILES].description,
+               "Fires two homing missiles (max %i missiles)", maxDoubleHoming);
+       shopItems[SHOP_DOUBLE_HOMING_MISSILES].image = 24;
+
+       shopItems[SHOP_MICRO_HOMING_MISSILES].price = 15000;
+       strcpy(shopItems[SHOP_MICRO_HOMING_MISSILES].name,
+               "Homing Micro Missile Launcher");
+       sprintf(shopItems[SHOP_MICRO_HOMING_MISSILES].description,
+               "Fires several small homing missiles (max %i missiles)", maxMicroHoming);
+       shopItems[SHOP_MICRO_HOMING_MISSILES].image = 25;
+
+       shopItems[SHOP_PLASMA_MAX_OUTPUT].x = 100;
+       shopItems[SHOP_PLASMA_MAX_OUTPUT].y = 200;
+
+       shopItems[SHOP_PLASMA_MAX_DAMAGE].x = 150;
+       shopItems[SHOP_PLASMA_MAX_DAMAGE].y = 200;
+
+       shopItems[SHOP_PLASMA_MAX_RATE].x = 200;
+       shopItems[SHOP_PLASMA_MAX_RATE].y = 200;
+
+       shopItems[SHOP_PLASMA_MIN_OUTPUT].x = 100;
+       shopItems[SHOP_PLASMA_MIN_OUTPUT].y = 260;
+
+       shopItems[SHOP_PLASMA_MIN_DAMAGE].x = 150;
+       shopItems[SHOP_PLASMA_MIN_DAMAGE].y = 260;
+
+       shopItems[SHOP_PLASMA_MIN_RATE].x = 200;
+       shopItems[SHOP_PLASMA_MIN_RATE].y = 260;
+
+       shopItems[SHOP_PLASMA_AMMO].x = 350;
+       shopItems[SHOP_PLASMA_AMMO].y = 200;
+
+       shopItems[SHOP_ROCKET_AMMO].x = 400;
+       shopItems[SHOP_ROCKET_AMMO].y = 200;
+
+       shopItems[SHOP_PLASMA_MAX_AMMO].x = 450;
+       shopItems[SHOP_PLASMA_MAX_AMMO].y = 200;
+
+       shopItems[SHOP_ROCKET_MAX_AMMO].x = 500;
+       shopItems[SHOP_ROCKET_MAX_AMMO].y = 200;
+
+       shopItems[SHOP_DOUBLE_ROCKETS].x = 350;
+       shopItems[SHOP_DOUBLE_ROCKETS].y = 260;
+
+       shopItems[SHOP_MICRO_ROCKETS].x = 400;
+       shopItems[SHOP_MICRO_ROCKETS].y = 260;
+
+       shopItems[SHOP_LASER].x = 450;
+       shopItems[SHOP_LASER].y = 260;
+
+       shopItems[SHOP_HOMING_MISSILE].x = 500;
+       shopItems[SHOP_HOMING_MISSILE].y = 260;
+
+       shopItems[SHOP_CHARGER].x = 550;
+       shopItems[SHOP_CHARGER].y = 260;
+
+       shopItems[SHOP_DOUBLE_HOMING_MISSILES].x = 600;
+       shopItems[SHOP_DOUBLE_HOMING_MISSILES].y = 260;
+
+       shopItems[SHOP_MICRO_HOMING_MISSILES].x = 650;
+       shopItems[SHOP_MICRO_HOMING_MISSILES].y = 260;
+
+       shopSelectedItem = -1;
+
+       player.image[0] = shape[0];
+       player.x = 380;
+       player.y = 95;
+
+       drawShop();
+}
+
+static void shop_sellSecondaryWeapon()
+{
+       switch (player.weaponType[1])
+       {
+               case W_DOUBLE_ROCKETS:
+                       sell(SHOP_DOUBLE_ROCKETS);
+                       break;
+
+               case W_MICRO_ROCKETS:
+                       sell(SHOP_MICRO_ROCKETS);
+                       break;
+
+               case W_LASER:
+                       sell(SHOP_LASER);
+                       break;
+
+               case W_HOMING_MISSILE:
+                       sell(SHOP_HOMING_MISSILE);
+                       break;
+
+               case W_CHARGER:
+                       sell(SHOP_CHARGER);
+                       break;
+
+               case W_DOUBLE_HOMING_MISSILES:
+                       sell(SHOP_DOUBLE_HOMING_MISSILES);
+                       break;
+
+               case W_MICRO_HOMING_MISSILES:
+                       sell(SHOP_MICRO_HOMING_MISSILES);
+                       break;
+       }
+}
+
+static void buy(int i)
+{
+       if ((game.cash < shopItems[i].price) && (!engine.cheatCash))
+       {
+               shopSelectedItem = -2;
+               drawShop();
+               return;
+       }
+
+       switch (i)
+       {
+               case SHOP_PLASMA_MAX_OUTPUT:
+                       if (game.maxPlasmaOutput >= game.maxPlasmaOutputLimit)
+                       {
+                               shopSelectedItem = -3;
+                               return;
+                       }
+                       game.maxPlasmaOutput++;
+                       break;
+
+               case SHOP_PLASMA_MAX_DAMAGE:
+                       if (game.maxPlasmaDamage >= game.maxPlasmaDamageLimit)
+                       {
+                               shopSelectedItem = -3;
+                               return;
+                       }
+                       game.maxPlasmaDamage++;
+                       break;
+
+               case SHOP_PLASMA_MAX_RATE:
+                       if (game.maxPlasmaRate >= game.maxPlasmaRateLimit)
+                       {
+                               shopSelectedItem = -3;
+                               return;
+                       }
+                       game.maxPlasmaRate++;
+                       break;
+
+               case SHOP_PLASMA_AMMO:
+                       if (player.ammo[0] >= game.maxPlasmaAmmo)
+                       {
+                               shopSelectedItem = -4;
+                               return;
+                       }
+                       LIMIT_ADD(player.ammo[0], 10, 0, game.maxPlasmaAmmo);
+                       break;
+
+               case SHOP_ROCKET_AMMO:
+                       if ((player.weaponType[1] == W_CHARGER) ||
+                               (player.weaponType[1] == W_LASER))
+                       {
+                               shopSelectedItem = -7;
+                               return;
+                       }
+                       if (player.ammo[1] == game.maxRocketAmmo)
+                       {
+                               shopSelectedItem = -4;
+                               return;
+                       }
+                       if ((player.weaponType[1] == W_HOMING_MISSILE) &&
+                               (player.ammo[1] >= maxHoming))
+                       {
+                               shopSelectedItem = -9;
+                               return;
+                       }
+                       if ((player.weaponType[1] == W_DOUBLE_HOMING_MISSILES) &&
+                               (player.ammo[1] >= maxDoubleHoming))
+                       {
+                               shopSelectedItem = -9;
+                               return;
+                       }
+                       if ((player.weaponType[1] == W_MICRO_HOMING_MISSILES) &&
+                               (player.ammo[1] >= maxMicroHoming))
+                       {
+                               shopSelectedItem = -9;
+                               return;
+                       }
+                       player.ammo[1]++;
+                       break;
+
+               case SHOP_PLASMA_MIN_OUTPUT:
+                       if (game.minPlasmaOutput >= game.minPlasmaOutputLimit)
+                       {
+                               shopSelectedItem = -3;
+                               return;
+                       }
+                       game.minPlasmaOutput++;
+                       if (game.maxPlasmaOutput < game.minPlasmaOutput)
+                               game.maxPlasmaOutput = game.minPlasmaOutput;
+                       break;
+
+               case SHOP_PLASMA_MIN_DAMAGE:
+                       if (game.minPlasmaDamage >= game.minPlasmaDamageLimit)
+                       {
+                               shopSelectedItem = -3;
+                               return;
+                       }
+                       game.minPlasmaDamage++;
+                       if (game.maxPlasmaDamage < game.minPlasmaDamage)
+                               game.maxPlasmaDamage = game.minPlasmaDamage;
+                       break;
+
+               case SHOP_PLASMA_MIN_RATE:
+                       if (game.minPlasmaRate >= game.minPlasmaRateLimit)
+                       {
+                               shopSelectedItem = -3;
+                               return;
+                       }
+                       game.minPlasmaRate++;
+                       if (game.maxPlasmaRate < game.minPlasmaRate)
+                               game.maxPlasmaRate = game.minPlasmaRate;
+                       break;
+
+               case SHOP_PLASMA_MAX_AMMO:
+                       if (game.maxPlasmaAmmo >= game.maxPlasmaAmmoLimit)
+                       {
+                               shopSelectedItem = -3;
+                               return;
+                       }
+                       game.maxPlasmaAmmo += 25;
+                       break;
+
+               case SHOP_ROCKET_MAX_AMMO:
+                       if ((player.weaponType[1] == W_CHARGER) ||
+                               (player.weaponType[1] == W_LASER))
+                       {
+                               shopSelectedItem = -7;
+                               return;
+                       }
+                       if ((player.weaponType[1] == W_HOMING_MISSILE) &&
+                               (game.maxRocketAmmo >= maxHoming))
+                       {
+                               shopSelectedItem = -9;
+                               return;
+                       }
+                       if ((player.weaponType[1] == W_DOUBLE_HOMING_MISSILES) &&
+                               (game.maxRocketAmmo >= maxDoubleHoming))
+                       {
+                               shopSelectedItem = -9;
+                               return;
+                       }
+                       if ((player.weaponType[1] == W_MICRO_HOMING_MISSILES) &&
+                               (game.maxRocketAmmo >= maxMicroHoming))
+                       {
+                               shopSelectedItem = -9;
+                               return;
+                       }
+                       if (game.maxRocketAmmo >= game.maxRocketAmmoLimit)
+                       {
+                               shopSelectedItem = -3;
+                               return;
+                       }
+                       game.maxRocketAmmo += 5;
+                       break;
+
+               case SHOP_DOUBLE_ROCKETS:
+                       if (player.weaponType[1] == W_DOUBLE_ROCKETS)
+                       {
+                               shopSelectedItem = -8;
+                               return;
+                       }
+                       shop_sellSecondaryWeapon();
+                       player.weaponType[1] = W_DOUBLE_ROCKETS;
+                       LIMIT(game.maxRocketAmmo, 5, 50);
+                       shopSelectedItem = -1;
+                       break;
+
+               case SHOP_MICRO_ROCKETS:
+                       if (player.weaponType[1] == W_MICRO_ROCKETS)
+                       {
+                               shopSelectedItem = -8;
+                               return;
+                       }
+                       shop_sellSecondaryWeapon();
+                       player.weaponType[1] = W_MICRO_ROCKETS;
+                       LIMIT(game.maxRocketAmmo, 5, 50);
+                       shopSelectedItem = -1;
+                       break;
+
+               case SHOP_LASER:
+                       if (player.weaponType[1] == W_LASER)
+                       {
+                               shopSelectedItem = -8;
+                               return;
+                       }
+                       shop_sellSecondaryWeapon();
+                       player.weaponType[1] = W_LASER;
+
+                       while (game.maxRocketAmmo > 5)
+                               sell(SHOP_ROCKET_MAX_AMMO);
+                       while (player.ammo[1] > 0)
+                               sell(SHOP_ROCKET_AMMO);
+
+                       shopSelectedItem = -1;
+                       break;
+
+               case SHOP_HOMING_MISSILE:
+                       if (player.weaponType[1] == W_HOMING_MISSILE)
+                       {
+                               shopSelectedItem = -8;
+                               return;
+                       }
+                       shop_sellSecondaryWeapon();
+                       player.weaponType[1] = W_HOMING_MISSILE;
+
+                       while (game.maxRocketAmmo > maxHoming)
+                               sell(SHOP_ROCKET_MAX_AMMO);
+
+                       LIMIT(game.maxRocketAmmo, 5, maxHoming);
+                       shopSelectedItem = -1;
+                       break;
+
+               case SHOP_CHARGER:
+                       if (player.weaponType[1] == W_CHARGER)
+                       {
+                               shopSelectedItem = -8;
+                               return;
+                       }
+                       shop_sellSecondaryWeapon();
+                       player.weaponType[1] = W_CHARGER;
+
+                       while (game.maxRocketAmmo > 5)
+                               sell(SHOP_ROCKET_MAX_AMMO);
+                       while (player.ammo[1] > 0)
+                               sell(SHOP_ROCKET_AMMO);
+
+                       shopSelectedItem = -1;
+                       break;
+
+               case SHOP_DOUBLE_HOMING_MISSILES:
+                       if (player.weaponType[1] == W_DOUBLE_HOMING_MISSILES)
+                       {
+                               shopSelectedItem = -8;
+                               return;
+                       }
+                       shop_sellSecondaryWeapon();
+                       player.weaponType[1] = W_DOUBLE_HOMING_MISSILES;
+
+                       while (game.maxRocketAmmo > maxDoubleHoming)
+                               sell(SHOP_ROCKET_MAX_AMMO);
+
+                       LIMIT(game.maxRocketAmmo, 5, maxDoubleHoming);
+                       shopSelectedItem = -1;
+                       break;
+
+               case SHOP_MICRO_HOMING_MISSILES:
+                       if (player.weaponType[1] == W_MICRO_HOMING_MISSILES)
+                       {
+                               shopSelectedItem = -8;
+                               return;
+                       }
+                       shop_sellSecondaryWeapon();
+                       player.weaponType[1] = W_MICRO_HOMING_MISSILES;
+
+                       while (game.maxRocketAmmo > maxMicroHoming)
+                               sell(SHOP_ROCKET_MAX_AMMO);
+
+                       LIMIT(game.maxRocketAmmo, 5, maxMicroHoming);
+                       shopSelectedItem = -1;
+                       break;
+       }
+
+       if (!engine.cheatCash)
+               game.cash -= shopItems[i].price;
+       saveGame(0);
+}
+
+static void sell(int i)
+{
+       switch (i)
+       {
+               case SHOP_PLASMA_MAX_OUTPUT:
+                       if (game.maxPlasmaOutput <= 1)
+                       {
+                               shopSelectedItem = -5;
+                               return;
+                       }
+
+                       while (game.minPlasmaOutput >= game.maxPlasmaOutput)
+                               sell(SHOP_PLASMA_MIN_OUTPUT);
+
+                       game.maxPlasmaOutput--;
+                       if (weapon[W_PLAYER_WEAPON].ammo[0] <= game.maxPlasmaOutput + 1)
+                               weapon[W_PLAYER_WEAPON].ammo[0] = MIN(
+                                       weapon[W_PLAYER_WEAPON].ammo[0],
+                                       game.maxPlasmaOutput);
+                       break;
+
+               case SHOP_PLASMA_MAX_DAMAGE:
+                       if (game.maxPlasmaDamage <= 1)
+                       {
+                               shopSelectedItem = -5;
+                               return;
+                       }
+
+                       while (game.minPlasmaDamage >= game.maxPlasmaDamage)
+                               sell(SHOP_PLASMA_MIN_DAMAGE);
+
+                       game.maxPlasmaDamage--;
+                       if (weapon[W_PLAYER_WEAPON].damage <= game.maxPlasmaDamage + 1)
+                               weapon[W_PLAYER_WEAPON].damage = MIN(
+                                       weapon[W_PLAYER_WEAPON].damage,
+                                       game.maxPlasmaDamage);
+                       break;
+
+               case SHOP_PLASMA_MAX_RATE:
+                       if (game.maxPlasmaRate <= 1)
+                       {
+                               shopSelectedItem = -5;
+                               return;
+                       }
+
+                       while (game.minPlasmaRate >= game.maxPlasmaRate)
+                               sell(SHOP_PLASMA_MIN_RATE);
+
+                       game.maxPlasmaRate--;
+                       if (weapon[W_PLAYER_WEAPON].reload[0] >= rate2reload[game.maxPlasmaRate + 1])
+                               weapon[W_PLAYER_WEAPON].reload[0] = MAX(
+                                       weapon[W_PLAYER_WEAPON].reload[0],
+                                       rate2reload[game.maxPlasmaRate]);
+                       break;
+
+               case SHOP_PLASMA_MIN_OUTPUT:
+                       if (game.minPlasmaOutput <= 1)
+                       {
+                               shopSelectedItem = -5;
+                               return;
+                       }
+                       game.minPlasmaOutput--;
+                       if (player.ammo[0] <= 0)
+                               weapon[W_PLAYER_WEAPON].ammo[0] = game.minPlasmaOutput;
+                       break;
+
+               case SHOP_PLASMA_MIN_DAMAGE:
+                       if (game.minPlasmaDamage <= 1)
+                       {
+                               shopSelectedItem = -5;
+                               return;
+                       }
+                       game.minPlasmaDamage--;
+                       if (player.ammo[0] <= 0)
+                               weapon[W_PLAYER_WEAPON].damage = game.minPlasmaDamage;
+                       break;
+
+               case SHOP_PLASMA_MIN_RATE:
+                       if (game.minPlasmaRate <= 1)
+                       {
+                               shopSelectedItem = -5;
+                               return;
+                       }
+                       game.minPlasmaRate--;
+                       if (player.ammo[0] <= 0)
+                               weapon[W_PLAYER_WEAPON].reload[0] = rate2reload[game.minPlasmaRate];
+                       break;
+
+               case SHOP_PLASMA_AMMO:
+                       if (player.ammo[0] <= 0)
+                       {
+                               shopSelectedItem = -6;
+                               return;
+                       }
+                       if (player.ammo[0] > 10)
+                               player.ammo[0] -= 10;
+                       else
+                       {
+                               player.ammo[0] = 0;
+                               weapon[W_PLAYER_WEAPON].ammo[0] = game.minPlasmaOutput;
+                               weapon[W_PLAYER_WEAPON].damage = game.minPlasmaDamage;
+                               weapon[W_PLAYER_WEAPON].reload[0] = rate2reload[game.minPlasmaRate];
+                       }
+                       break;
+
+               case SHOP_ROCKET_AMMO:
+                       if (player.ammo[1] <= 0)
+                       {
+                               shopSelectedItem = -6;
+                               return;
+                       }
+                       player.ammo[1]--;
+                       break;
+
+               case SHOP_PLASMA_MAX_AMMO:
+                       if (game.maxPlasmaAmmo <= 100)
+                       {
+                               shopSelectedItem = -1;
+                               return;
+                       }
+                       game.maxPlasmaAmmo -= 25;
+
+                       while (player.ammo[0] > game.maxPlasmaAmmo)
+                               sell(SHOP_PLASMA_AMMO);
+
+                       break;
+
+               case SHOP_ROCKET_MAX_AMMO:
+                       if (game.maxRocketAmmo <= 5)
+                       {
+                               shopSelectedItem = -1;
+                               return;
+                       }
+                       game.maxRocketAmmo -= 5;
+
+                       while (player.ammo[1] > game.maxRocketAmmo)
+                               sell(SHOP_ROCKET_AMMO);
+
+                       break;
+
+               case SHOP_DOUBLE_ROCKETS:
+                       if (player.weaponType[1] != W_DOUBLE_ROCKETS)
+                       {
+                               shopSelectedItem = -1;
+                               return;
+                       }
+                       player.weaponType[1] = W_ROCKETS;
+                       shopSelectedItem = -1;
+                       break;
+
+               case SHOP_MICRO_ROCKETS:
+                       if (player.weaponType[1] != W_MICRO_ROCKETS)
+                       {
+                               shopSelectedItem = -1;
+                               return;
+                       }
+                       player.weaponType[1] = W_ROCKETS;
+                       shopSelectedItem = -1;
+                       break;
+
+               case SHOP_LASER:
+                       if (player.weaponType[1] != W_LASER)
+                       {
+                               shopSelectedItem = -1;
+                               return;
+                       }
+                       player.weaponType[1] = W_ROCKETS;
+                       player.ammo[1] = 0;
+                       shopSelectedItem = -1;
+                       break;
+
+               case SHOP_HOMING_MISSILE:
+                       if (player.weaponType[1] != W_HOMING_MISSILE)
+                       {
+                               shopSelectedItem = -1;
+                               return;
+                       }
+                       player.weaponType[1] = W_ROCKETS;
+                       shopSelectedItem = -1;
+                       break;
+
+               case SHOP_CHARGER:
+                       if (player.weaponType[1] != W_CHARGER)
+                       {
+                               shopSelectedItem = -1;
+                               return;
+                       }
+                       player.weaponType[1] = W_ROCKETS;
+                       player.ammo[1] = 0;
+                       shopSelectedItem = -1;
+                       break;
+
+               case SHOP_DOUBLE_HOMING_MISSILES:
+                       if (player.weaponType[1] != W_DOUBLE_HOMING_MISSILES)
+                       {
+                               shopSelectedItem = -1;
+                               return;
+                       }
+                       player.weaponType[1] = W_ROCKETS;
+                       shopSelectedItem = -1;
+                       break;
+
+               case SHOP_MICRO_HOMING_MISSILES:
+                       if (player.weaponType[1] != W_MICRO_HOMING_MISSILES)
+                       {
+                               shopSelectedItem = -1;
+                               return;
+                       }
+                       player.weaponType[1] = W_ROCKETS;
+                       shopSelectedItem = -1;
+                       break;
+       }
+
+       adjustShopPrices();
+
+       if (game.difficulty == DIFFICULTY_ORIGINAL)
+               game.cash += shopItems[i].price / 2;
+       else
+               game.cash += shopItems[i].price;
+
+       saveGame(0);
+}
+
+void showShop()
+{
+       blit(shopSurface[0], 20, 395);
+       blit(shopSurface[1], 275, 395);
+       blit(shopSurface[2], 530, 395);
+       blit(shopSurface[3], 100, 180);
+       blit(shopSurface[4], 100, 50);
+       blit(shopSurface[5], 100, 320);
+
+       if (shopSelectedItem > -1)
+       {
+               blit(shape[27], 60, 350);
+               blit(shape[28], 710, 350);
+       }
+
+       blit(shape[29], (int)player.x, (int)player.y);
+
+       signed char icons = MAX_SHOPITEMS;
+
+       if (game.system == 0)
+               icons = SHOP_DOUBLE_ROCKETS + 1;
+       else if (game.system == 1)
+               icons = SHOP_LASER + 1;
+       else if (game.system == 2)
+               icons = SHOP_CHARGER + 1;
+
+       if ((engine.keyState[KEY_FIRE]))
+       {
+               for (int i = 0 ; i < icons ; i++)
+               {
+                       if (collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6,
+                               shopItems[i].x, shopItems[i].y, 32, 25))
+                       {
+                               shopSelectedItem = i;
+                               engine.keyState[KEY_FIRE] = 0;
+                               drawShop();
+                       }
+               }
+
+               if (shopSelectedItem > -1)
+               {
+                       if (collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 60, 350, 24, 16))
+                       {
+                               buy(shopSelectedItem);
+                               engine.keyState[KEY_FIRE] = 0;
+                               drawShop();
+                       }
+
+                       if (collision(engine.cursor_x + 13, engine.cursor_y + 13, 6, 6, 700, 350, 24, 16))
+                       {
+                               sell(shopSelectedItem);
+                               engine.keyState[KEY_FIRE] = 0;
+                               drawShop();
+                       }
+               }
+       }
+}
diff --git a/src/shop.h b/src/shop.h
new file mode 100644 (file)
index 0000000..ba93fe9
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SHOP_H
+#define SHOP_H
+
+extern void initShop();
+extern void showShop();
+
+#endif
diff --git a/src/structs.h b/src/structs.h
new file mode 100644 (file)
index 0000000..58ca24c
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012, 2013 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef STRUCTS_H
+#define STRUCTS_H
+
+struct object {
+
+       bool active;
+       int classDef; // Used by aliens to determine what they are
+       int AIType; // Type of articifial intelligence
+
+       int id; // The "job" of the object
+       object *target; //  index target in aliens array
+
+       int 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
+
+       int speed;
+       int damage; // Contact damage for bullets
+       int ammo[2]; // Ammo for 2nd weapon.
+
+       int 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
+       int imageIndex[2]; // used for loading
+       int 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
+       int weaponType[2]; // Weapon types
+
+       int collectChance; // Chance of dropping the object
+       int collectType; // What the object is carrying
+       int collectValue; // What it is worth
+
+       unsigned long int flags; // Various flags for an object
+
+       float x, y, dx, dy;
+
+       object *next;
+
+};
+
+struct mission {
+
+       char primaryObjective[3][50]; // Description
+       int primaryType[3]; // The type of mission this is
+       int target1[3]; // index of target in aliens array
+       int targetValue1[3]; // Number of things to collect (slaves, cash, etc)
+       int timeLimit1[3]; // In minutes
+       int completed1[3];
+
+       char secondaryObjective[3][50]; // Description
+       int secondaryType[3]; // The type of mission this is
+       int target2[3]; // index of target in aliens array
+       int targetValue2[3]; // Number of things to collect (slaves, cash, etc)
+       int timeLimit2[3]; // In minutes
+       int completed2[3];
+
+       int remainingObjectives1;
+       int remainingObjectives2;
+       int addAliens; // How often to add new enemies
+
+};
+
+struct Star {
+
+       float x, y, dx, dy;
+       int speed; // How fast the star moves
+
+};
+
+struct collectables {
+
+       bool active;
+       float x, y, dx, dy;
+       SDL_Surface *image;
+       int type; // What kind of collectable is it?
+       int value; // How much is it worth?
+       int life; // How long it will stay around for
+
+       collectables *next;
+
+};
+
+struct textObject {
+
+       SDL_Surface *image;
+       int life;
+       float x, y;
+       int fontColor;
+       char text[255];
+
+};
+
+struct Game {
+       object thePlayer;
+       object playerWeapon;
+
+       int saveFormat;
+
+       int difficulty;
+
+       int system;
+       int area;
+       int musicVolume;
+       int sfxVolume;
+
+       int cash;
+       int cashEarned;
+       
+       int shots;
+       int hits;
+       int accuracy;
+       int hasWingMate1;
+       int hasWingMate2;
+       int totalKills;
+       int wingMate1Kills;
+       int wingMate2Kills;
+       int wingMate1Ejects;
+       int wingMate2Ejects;
+       int totalOtherKills;
+       int secondaryMissions;
+       int secondaryMissionsCompleted;
+       int shieldPickups;
+       int rocketPickups;
+       int cellPickups;
+       int powerups;
+       int minesKilled;
+       int cargoPickups;
+
+       // slaves for Eyananth
+       int slavesRescued;
+
+       // remaining shield for experimental fighter
+       int experimentalShield;
+
+       long int timeTaken; // In seconds
+       int missionCompleted[10];
+
+       int stationedPlanet;
+       int destinationPlanet;
+
+       char stationedName[20];
+       char destinationName[20];
+       int distanceCovered;
+
+       int minPlasmaRate;
+       int minPlasmaDamage;
+       int minPlasmaOutput;
+       int maxPlasmaRate;
+       int maxPlasmaDamage;
+       int maxPlasmaOutput;
+       int maxPlasmaAmmo;
+       int maxRocketAmmo;
+
+       // Limits on shop upgrades
+       int minPlasmaRateLimit;
+       int minPlasmaDamageLimit;
+       int minPlasmaOutputLimit;
+       int maxPlasmaRateLimit;
+       int maxPlasmaDamageLimit;
+       int maxPlasmaOutputLimit;
+       int maxPlasmaAmmoLimit;
+       int maxRocketAmmoLimit;
+
+};
+
+struct ShopItem {
+
+       int x, y;
+       int price;
+       char name[50];
+       char description[255];
+       int image;
+};
+
+struct bRect {
+
+       int x, y, w, h;
+       bRect *next;
+
+};
+
+struct Planet {
+
+       int y;
+       char name[50];
+       SDL_Surface *image;
+
+       int missionNumber; // associated mission number
+       int missionCompleted; // whether it has been completed
+
+       int messageMission;
+       int messageSlot;
+       int faceImage;
+       char from[50];
+       char subject[100];
+};
+
+enum keys {
+       KEY_UP,
+       KEY_DOWN,
+       KEY_LEFT,
+       KEY_RIGHT,
+       KEY_FIRE,
+       KEY_ALTFIRE,
+       KEY_SWITCH,
+       KEY_PAUSE,
+       KEY_ESCAPE,
+       KEY_FULLSCREEN,
+       KEY_DUMMY,
+       KEY_LAST
+};
+
+struct Engine {
+
+       SDL_Event event;
+       int done;
+
+       SDL_RWops *sdlrw;
+
+       float musicVolume;
+
+       int maxAliens;
+
+       float ssx;
+       float ssy;
+       float smx;
+       float smy;
+
+       object *bulletHead;
+       object *bulletTail;
+       object *explosionHead;
+       object *explosionTail;
+       collectables *collectableHead;
+       collectables *collectableTail;
+       object *debrisHead;
+       object *debrisTail;
+
+       int cursor_x, cursor_y;
+
+       int commsSection;
+
+       int eventTimer;
+
+       int lowShield;
+       int averageShield;
+
+       float targetShield;
+       int targetIndex;
+
+       // Mission completion timer (allows for 4 seconds before leaving sector)
+       long missionCompleteTimer;
+
+       // Times the mission normally
+       Uint32 counter2;
+       long int timeTaken; // In seconds
+
+       // For missions with a time limit
+       int timeMission;
+       Uint32 counter;
+       int seconds;
+       int minutes;
+
+       // Mission Related stuff
+       int allAliensDead;
+       int addAliens;
+
+       bool paused;
+       int gameSection;
+
+       bool useAudio;
+       bool useSound;
+       bool useMusic;
+       bool fullScreen;
+       bool autoPause;
+
+       char userHomeDirectory[1024];
+       
+       char keyState[KEY_LAST];
+
+       bool cheat; // overall cheat
+       bool cheatShield;
+       bool cheatCash;
+       bool cheatAmmo;
+       bool cheatTime;
+       bool cheatCredits;
+};
+
+struct event {
+
+       int time;
+       char message[255];
+       int face;
+       int entity;
+       int flag;
+};
+
+struct cutMsg {
+
+       int face;
+       char message[255];
+
+};
+
+#endif
diff --git a/src/title.cpp b/src/title.cpp
new file mode 100644 (file)
index 0000000..b4506b3
--- /dev/null
@@ -0,0 +1,758 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2012, 2013 Guus Sliepen
+Copyright (C) 2012, 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+static signed char showGameMenu(signed char continueSaveSlot)
+{
+       blitText(TS_START_NEW_GAME);
+       if (continueSaveSlot != -1)
+       {
+               blitText(TS_LOAD_GAME);
+               blitText(TS_CONTINUE_CURRENT_GAME);
+       }
+       blitText(TS_OPTIONS);
+       if (engine.cheat)
+       {
+               textShape[TS_QUIT].y = 450;
+               blitText(TS_CHEAT_OPTIONS);
+       }
+       else
+       {
+               textShape[TS_QUIT].y = 430;
+       }
+       blitText(TS_QUIT);
+
+       if (engine.cheat)
+               return 6;
+
+       return 5;
+}
+
+static signed char showLoadMenu()
+{
+       signed char rtn = 1;
+
+       for (int i = TS_SAVESLOT_0 ; i <= TS_SAVESLOT_5 ; i++)
+       {
+               rtn++;
+               if (textShape[i].image != NULL)
+               {
+                       blitText(i);
+                       textShape[TS_BACK_TO_MAIN_MENU].y = textShape[i].y + 40;
+               }
+       }
+       blitText(TS_BACK_TO_MAIN_MENU);
+
+       return rtn;
+}
+
+static void createDifficultyMenu()
+{
+       textSurface(TS_START_GAME, "START GAME", -1, 350, FONT_WHITE);
+
+       if (game.difficulty == DIFFICULTY_EASY)
+               textSurface(TS_DIFFICULTY, "DIFFICULTY - EASY", -1, 370, FONT_WHITE);
+       else if (game.difficulty == DIFFICULTY_HARD)
+               textSurface(TS_DIFFICULTY, "DIFFICULTY - HARD", -1, 370, FONT_WHITE);
+       else if (game.difficulty == DIFFICULTY_NIGHTMARE)
+               textSurface(TS_DIFFICULTY, "DIFFICULTY - NIGHTMARE!", -1, 370, FONT_WHITE);
+       else if (game.difficulty == DIFFICULTY_ORIGINAL)
+               textSurface(TS_DIFFICULTY, "DIFFICULTY - ORIGINAL", -1, 370, FONT_WHITE);
+       else
+               textSurface(TS_DIFFICULTY, "DIFFICULTY - NORMAL", -1, 370, FONT_WHITE);
+}
+
+static signed char showDifficultyMenu()
+{
+       textShape[TS_BACK_TO_MAIN_MENU].y = 410;
+
+       blitText(TS_START_GAME);
+       blitText(TS_DIFFICULTY);
+       blitText(TS_BACK_TO_MAIN_MENU);
+
+       return 3;
+}
+
+static void createOptionsMenu()
+{
+       if (engine.useSound)
+               textSurface(TS_SOUND, "SOUND - ON", -1, 350, FONT_WHITE);
+       else
+               textSurface(TS_SOUND, "SOUND - OFF", -1, 350, FONT_WHITE);
+
+       if (engine.useMusic)
+               textSurface(TS_MUSIC, "MUSIC - ON", -1, 370, FONT_WHITE);
+       else
+               textSurface(TS_MUSIC, "MUSIC - OFF", -1, 370, FONT_WHITE);
+
+       if (engine.fullScreen)
+               textSurface(TS_FULLSCREEN, "FULLSCREEN - ON", -1, 390, FONT_WHITE);
+       else
+               textSurface(TS_FULLSCREEN, "FULLSCREEN - OFF", -1, 390, FONT_WHITE);
+
+       if (engine.autoPause)
+               textSurface(TS_AUTOPAUSE, "AUTOPAUSE - ON", -1, 410, FONT_WHITE);
+       else
+               textSurface(TS_AUTOPAUSE, "AUTOPAUSE - OFF", -1, 410, FONT_WHITE);
+}
+
+static signed char showOptionsMenu()
+{
+       textShape[TS_BACK_TO_MAIN_MENU].y = 450;
+
+       blitText(TS_SOUND);
+       blitText(TS_MUSIC);
+       blitText(TS_FULLSCREEN);
+       blitText(TS_AUTOPAUSE);
+       blitText(TS_BACK_TO_MAIN_MENU);
+
+       return 5;
+}
+
+static void createCheatMenu()
+{
+       if (engine.cheatShield)
+               textSurface(TS_UNLIMITED_SHIELD, "UNLIMITED SHIELD - ON", -1, 350,
+                       FONT_WHITE);
+       else
+               textSurface(TS_UNLIMITED_SHIELD, "UNLIMITED SHIELD - OFF", -1, 350,
+                       FONT_WHITE);
+
+       if (engine.cheatAmmo)
+               textSurface(TS_UNLIMITED_AMMO, "UNLIMITED AMMO - ON", -1, 370,
+                       FONT_WHITE);
+       else
+               textSurface(TS_UNLIMITED_AMMO, "UNLIMITED AMMO - OFF", -1, 370,
+                       FONT_WHITE);
+
+       if (engine.cheatCash)
+               textSurface(TS_UNLIMITED_CASH, "UNLIMITED CASH - ON", -1, 390,
+                       FONT_WHITE);
+       else
+               textSurface(TS_UNLIMITED_CASH, "UNLIMITED CASH - OFF", -1, 390,
+                       FONT_WHITE);
+
+       if (engine.cheatTime)
+               textSurface(TS_UNLIMITED_TIME, "UNLIMITED TIME - ON", -1, 410,
+                       FONT_WHITE);
+       else
+               textSurface(TS_UNLIMITED_TIME, "UNLIMITED TIME - OFF", -1, 410,
+                       FONT_WHITE);
+}
+
+static signed char showCheatMenu()
+{
+       textShape[TS_BACK_TO_MAIN_MENU].y = 450;
+
+       blitText(TS_UNLIMITED_SHIELD);
+       blitText(TS_UNLIMITED_AMMO);
+       blitText(TS_UNLIMITED_CASH);
+       blitText(TS_UNLIMITED_TIME);
+       blitText(TS_BACK_TO_MAIN_MENU);
+
+       return 5;
+}
+
+/*
+This is the main title screen, with the stars whirling past and the
+"Parallel Realities, Present..." text. Nothing too special.
+*/
+int doTitle()
+{
+       game_init();
+
+       engine.gameSection = SECTION_TITLE;
+
+       flushBuffer();
+       freeGraphics();
+       resetLists();
+       
+       // required to stop the title screen crashing
+       game.system = 0;
+       game.area = MISN_START;
+
+       loadGameGraphics();
+
+       clearScreen(black);
+       updateScreen();
+       clearScreen(black);
+
+       signed char continueSaveSlot = initSaveSlots();
+
+       loadBackground("gfx/spirit.jpg");
+
+       SDL_Surface *prlogo, *sflogo;
+       prlogo = loadImage("gfx/prlogo.png");
+       sflogo = loadImage("gfx/sflogo.png");
+
+       int prx = ((screen->w - prlogo->w) / 2);
+       int pry = ((screen->h - prlogo->h) / 2);
+
+       int sfx = ((screen->w - sflogo->w) / 2);
+       int sfy = ((screen->h - sflogo->h) / 2);
+
+       textSurface(TS_PRESENTS, "PRESENTS", -1, 300, FONT_WHITE);
+       textSurface(TS_AN_SDL_GAME, "AN SDL GAME", -1, 300, FONT_WHITE);
+       textSurface(TS_START_NEW_GAME, "START NEW GAME", -1, 350, FONT_WHITE);
+       textSurface(TS_LOAD_GAME, "LOAD GAME", -1, 370, FONT_WHITE);
+       textSurface(TS_CONTINUE_CURRENT_GAME, "CONTINUE CURRENT GAME", -1, 390,
+               FONT_WHITE);
+       textSurface(TS_OPTIONS, "OPTIONS", -1, 410, FONT_WHITE);
+       textSurface(TS_CHEAT_OPTIONS, "CHEAT OPTIONS", -1, 430, FONT_WHITE);
+       textSurface(TS_QUIT, "QUIT", -1, 430, FONT_WHITE);
+
+       createOptionsMenu();
+       createDifficultyMenu();
+       textSurface(TS_BACK_TO_MAIN_MENU, "BACK TO MAIN MENU", -1, 0, FONT_WHITE);
+
+       createCheatMenu();
+
+       // Set the star motion
+       engine.ssx = -0.5;
+       engine.ssy = 0;
+       engine.smx = 0;
+       engine.smy = 0;
+
+       int then = SDL_GetTicks();
+       int now;
+
+       for (int i = 0 ; i < 15 ; i++)
+       {
+               aliens[i] = alien_defs[rand() % 3];
+               if ((rand() % 5) == 0)
+                       aliens[i] = alien_defs[CD_TRANSPORTSHIP];
+               if ((rand() % 5) == 0)
+                       aliens[i] = alien_defs[CD_MINER];
+               aliens[i].x = rand() % screen->w;
+               aliens[i].y = rand() % (screen->h - 40);
+               aliens[i].dx = 1 + rand() % 3;
+               aliens[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 > -1)
+               {selectedOption = 3; optionRec.y += 40;}
+
+       bool skip = false;
+       signed char listLength = 5; // menu list length
+       signed char menuType = MENU_MAIN;
+
+       drawBackGround();
+
+       engine.done = 0;
+       flushInput();
+       engine.keyState[KEY_FIRE] = engine.keyState[KEY_ALTFIRE] = 0;
+
+       audio_playMusic("music/walking_among_androids.ogg", 1);
+
+       while (!engine.done)
+       {
+               updateScreen();
+               unBuffer();
+
+               now = SDL_GetTicks();
+
+               doStarfield();
+               game_doExplosions();
+
+               for (int i = 0 ; i < 15 ; i++)
+               {
+                       explosion_addEngine(&aliens[i]);
+                       aliens[i].x += aliens[i].dx;
+                       blit(aliens[i].image[0], (int)aliens[i].x, (int)aliens[i].y);
+                       if (aliens[i].x > 830)
+                       {
+                               aliens[i].x = -40;
+                               aliens[i].y = rand() % (screen->h - 20);
+                               aliens[i].dx = 1 + rand() % 3;
+                       }
+               }
+
+               if ((now - then > 2000) && (now - then < 8000) && (!skip))
+               {
+                       blit(prlogo, prx, pry);
+               }
+               else if ((now - then > 9000) && (now - then < 15000) && (!skip))
+               {
+                       blitText(TS_PRESENTS);
+               }
+               else if ((now - then > 16000) && (now - then < 21000) && (!skip))
+               {
+                       blitText(TS_AN_SDL_GAME);
+               }
+               else if ((now - then > 25500) || (skip))
+               {
+                       blit(sflogo, sfx, sfy);
+
+                       if ((now - then >= 27500) || (skip))
+                       {
+                               addBuffer(0, 0, screen->w, screen->h);
+
+                               blevelRect(optionRec.x, optionRec.y, optionRec.w, optionRec.h, redGlow, 0x00, 0x00);
+
+                               switch(menuType)
+                               {
+                                       case MENU_MAIN:
+                                               listLength = showGameMenu(continueSaveSlot);
+                                               break;
+                                       case MENU_DIFFICULTY:
+                                               listLength = showDifficultyMenu();
+                                               break;
+                                       case MENU_LOAD:
+                                               listLength = showLoadMenu();
+                                               break;
+                                       case MENU_OPTIONS:
+                                               listLength = showOptionsMenu();
+                                               break;
+                                       case MENU_CHEAT:
+                                               listLength = showCheatMenu();
+                                               break;
+                               }
+
+                               redGlow += redDir;
+                               if (redGlow <= 0) {redDir = 2; redGlow = 0;}
+                               if (redGlow >= 255) {redDir = -2; redGlow = 255;}
+
+                               if (engine.keyState[KEY_UP])
+                               {
+                                       engine.keyState[KEY_UP] = 0;
+                                       WRAP_ADD(selectedOption, -1, 1, listLength + 1);
+                                       if (menuType == MENU_MAIN)
+                                               if ((selectedOption == 2) || (selectedOption == 3))
+                                                       if (continueSaveSlot == -1)
+                                                               selectedOption = 1;
+                               }
+                               if (engine.keyState[KEY_DOWN])
+                               {
+                                       engine.keyState[KEY_DOWN] = 0;
+                                       WRAP_ADD(selectedOption, 1, 0, listLength);
+                                       if (menuType == MENU_MAIN)
+                                               if ((selectedOption == 2) || (selectedOption == 3))
+                                                       if (continueSaveSlot == -1)
+                                                               selectedOption = 4;
+                               }
+
+                               optionRec.y = 326 + (20 * selectedOption);
+                               if (menuType > MENU_MAIN)
+                                       if (selectedOption == listLength)
+                                               optionRec.y += 20;
+
+                               if (!skip)
+                               {
+                                       drawString("Copyright Parallel Realities 2003", 5, 560, FONT_WHITE, background);
+                                       drawString("Copyright Guus Sliepen, Astrid S. de Wijn and others 2012", 5, 580, FONT_WHITE, background);
+                                       drawString(buildVersion, 794 - strlen(buildVersion) * 9, 580, FONT_WHITE, background);
+                                       addBuffer(0, 560, 800, 40);
+                                       skip = true;
+                               }
+                       }
+               }
+
+               getPlayerInput();
+
+               // if someone has invoked the credits cheat
+               if (engine.cheatCredits)
+               {
+                       doCredits();
+                       engine.cheatCredits = false;
+               }
+
+               if ((engine.keyState[KEY_FIRE] || engine.keyState[KEY_ALTFIRE]))
+               {
+                       if ((now - then <= 27500) && (!skip))
+                       {
+                               drawString("Copyright Parallel Realities 2003", 5, 560, FONT_WHITE, background);
+                               drawString("Copyright Guus Sliepen, Astrid S. de Wijn and others 2012", 5, 580, FONT_WHITE, background);
+                               drawString(buildVersion, 794 - strlen(buildVersion) * 9, 580, FONT_WHITE, background);
+                               addBuffer(0, 560, 800, 40);
+                               skip = true;
+                       }
+                       else
+                       {
+                               switch (menuType)
+                               {
+                                       case MENU_MAIN:
+                                               if (selectedOption == 1)
+                                               {
+                                                       menuType = MENU_DIFFICULTY;
+                                                       selectedOption = 1;
+                                               }
+                                               else if (selectedOption == 2)
+                                               {
+                                                       menuType = MENU_LOAD;
+                                                       selectedOption = 1;
+                                               }
+                                               else if (selectedOption == 3)
+                                                       engine.done = 1;
+                                               else if (selectedOption == 4)
+                                               {
+                                                       menuType = MENU_OPTIONS;
+                                                       selectedOption = 1;
+                                               }
+                                               else if (selectedOption == 5)
+                                               {
+                                                       if (engine.cheat)
+                                                       {
+                                                               menuType = MENU_CHEAT;
+                                                               selectedOption = 1;
+                                                       }
+                                                       else
+                                                               engine.done = 1;
+                                               }
+                                               else if (selectedOption == 6)
+                                                       engine.done = 1;
+                                               break;
+
+                                       case MENU_DIFFICULTY:
+                                               if (selectedOption == 1)
+                                                       engine.done = 1;
+                                               else if (selectedOption == 2)
+                                               {
+                                                       game.difficulty++;
+                                                       game.difficulty %= DIFFICULTY_MAX;
+                                               }
+                                               else if (selectedOption == listLength)
+                                               {
+                                                       menuType = MENU_MAIN;
+                                                       selectedOption = 1;
+                                               }
+                                               createDifficultyMenu();
+                                               break;
+
+                                       case MENU_LOAD:
+                                               if (selectedOption != listLength)
+                                               {
+                                                       engine.done = 1;
+                                                       continueSaveSlot = selectedOption - 1;
+                                                       selectedOption = 3;
+                                               }
+                                               else
+                                               {
+                                                       menuType = MENU_MAIN;
+                                                       selectedOption = 1;
+                                               }
+                                               break;
+
+                                       case MENU_OPTIONS:
+                                               if ((selectedOption == 1) && (engine.useAudio))
+                                                       engine.useSound = !engine.useSound;
+                                               else if ((selectedOption == 2) && (engine.useAudio))
+                                               {
+                                                       engine.useMusic = !engine.useMusic;
+
+                                                       if (engine.useMusic)
+                                                       {
+                                                               audio_playMusic(
+                                                                       "music/walking_among_androids.ogg", 1);
+                                                       }
+                                                       else
+                                                       {
+                                                               audio_haltMusic();
+                                                       }
+                                               }
+                                               else if (selectedOption == 3)
+                                               {
+                                                       engine.fullScreen = !engine.fullScreen;
+                                                       SDL_SetWindowFullscreen(window,
+                                                               (engine.fullScreen ?
+                                                                       SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
+                                               }
+                                               else if (selectedOption == 4)
+                                               {
+                                                       engine.autoPause = !engine.autoPause;
+                                               }
+                                               else if (selectedOption == listLength)
+                                               {
+                                                       menuType = MENU_MAIN;
+                                                       selectedOption = 1;
+                                               }
+                                               createOptionsMenu();
+                                               break;
+
+                                       case MENU_CHEAT:
+                                               if (selectedOption == 1)
+                                                       engine.cheatShield = !engine.cheatShield;
+                                               else if (selectedOption == 2)
+                                                       engine.cheatAmmo = !engine.cheatAmmo;
+                                               else if (selectedOption == 3)
+                                                       engine.cheatCash = !engine.cheatCash;
+                                               else if (selectedOption == 4)
+                                                       engine.cheatTime = !engine.cheatTime;
+                                               else if (selectedOption == listLength)
+                                               {
+                                                       menuType = MENU_MAIN;
+                                                       selectedOption = 1;
+                                               }
+                                               createCheatMenu();
+                                               break;
+
+                                       default:
+                                               menuType = MENU_MAIN;
+                                               selectedOption = 1;
+                                               break;
+                               }
+                       }
+                       engine.keyState[KEY_FIRE] = engine.keyState[KEY_ALTFIRE] = 0;
+               }
+
+               delayFrame();
+       }
+
+       audio_haltMusic();
+
+       SDL_FreeSurface(prlogo);
+       SDL_FreeSurface(sflogo);
+
+       engine.keyState[KEY_FIRE] = engine.keyState[KEY_ALTFIRE] = 0;
+
+       resetLists();
+
+       if (selectedOption == 1)
+       {
+               game_init();
+               selectedOption = 2; // go straight to mission 0
+       }
+
+       if (selectedOption == 3)
+       {
+               game_init();
+               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.
+*/
+void showStory()
+{
+       freeGraphics();
+
+       int y = screen->h + 20;
+
+       FILE *fp;
+
+       fp = fopen("data/intro.txt", "rb");
+
+       int i = 0;
+       int nextPos = -1;
+       char string[255];
+
+       while (fscanf(fp, "%d %[^\n]%*c", &nextPos, string) == 2)
+       {
+               y += nextPos;
+               textSurface(i, string, -1, y, FONT_WHITE);
+
+               i++;
+       }
+
+       fclose(fp);
+
+       loadBackground("gfx/startUp.jpg");
+       blit(background, 0, 0);
+       flushBuffer();
+
+       flushInput();
+       engine.keyState[KEY_FIRE] = engine.keyState[KEY_ALTFIRE] = 0;
+
+       while (true)
+       {
+               updateScreen();
+               unBuffer();
+
+               getPlayerInput();
+
+               if ((engine.keyState[KEY_FIRE] || engine.keyState[KEY_ALTFIRE]))
+                       break;
+
+               if (textShape[8].y > (screen->h / 2) + 150)
+               {
+                       for (int i = 0 ; i < 9 ; i++)
+                       {
+                               textShape[i].y -= 0.33333;
+                               blitText(i);
+                       }
+               }
+               else
+               {
+                       SDL_Delay(3000);
+                       break;
+               }
+
+               delayFrame();
+       }
+}
+
+/*
+The game over screen :(
+*/
+void gameover()
+{
+       flushBuffer();
+       freeGraphics();
+       SDL_FillRect(background, NULL, black);
+
+       engine.keyState[KEY_FIRE] = engine.keyState[KEY_ALTFIRE] = 0;
+       engine.gameSection = SECTION_INTERMISSION;
+
+       SDL_Surface *gameover = loadImage("gfx/gameover.png");
+
+       clearScreen(black);
+       updateScreen();
+       clearScreen(black);
+       SDL_Delay(1000);
+
+       audio_playMusic("music/death.ogg", -1);
+
+       int x = (screen->w - gameover->w) / 2;
+       int y = (screen->h - gameover->h) / 2;
+
+       updateScreen();
+
+       flushInput();
+       engine.keyState[KEY_FIRE] = engine.keyState[KEY_ALTFIRE] = 0;
+
+       while (true)
+       {
+               getPlayerInput();
+
+               if (engine.keyState[KEY_FIRE] || engine.keyState[KEY_ALTFIRE])
+                       break;
+
+               updateScreen();
+
+               unBuffer();
+               x = ((screen->w - gameover->w) / 2) - RANDRANGE(-2, 2);
+               y = ((screen->h - gameover->h) / 2)  - RANDRANGE(-2, 2);
+               blit(gameover, x,  y);
+
+               delayFrame();
+       }
+
+       SDL_FreeSurface(gameover);
+       audio_haltMusic();
+       flushBuffer();
+}
+
+void doCredits()
+{
+       loadBackground("gfx/credits.jpg");
+       flushBuffer();
+       freeGraphics();
+
+       FILE *fp;
+       int lastCredit = -1;
+
+       int yPos = 0;
+       int yPos2 = screen->h;
+       char text[255];
+       int i;
+
+       textObject *credit;
+
+       clearScreen(black);
+       updateScreen();
+       clearScreen(black);
+
+       drawBackGround();
+
+       audio_playMusic("music/rise_of_spirit.ogg", 1);
+
+       fp = fopen("data/credits.txt", "rb");
+       // FIXME: It would be nice for the size of this array to be determined
+       // by the number of lines in the text file. I'm not sure how to do
+       // that at the moment, so just giving it a very large number for now.
+       credit = (textObject*) malloc(sizeof(textObject) * 300);
+
+       while (fscanf(fp, "%d %[^\n]%*c", &yPos, text) == 2)
+       {
+               lastCredit++;
+               credit[lastCredit].image = textSurface(text, FONT_WHITE);
+               credit[lastCredit].x = (screen->w - credit[lastCredit].image->w) / 2;
+               yPos2 += yPos;
+               credit[lastCredit].y = yPos2;
+       }
+
+       fclose(fp);
+
+       engine.done = 0;
+
+       engine.keyState[KEY_ESCAPE] = 0;
+       engine.keyState[KEY_FIRE] = 0;
+       engine.keyState[KEY_ALTFIRE] = 0;
+       flushInput();
+
+       while (true)
+       {
+               updateScreen();
+               unBuffer();
+
+               getPlayerInput();
+               if (engine.keyState[KEY_ESCAPE] || engine.keyState[KEY_FIRE] ||
+                               engine.keyState[KEY_ALTFIRE])
+                       break;
+
+               float speed = 0.5;
+               if(engine.keyState[KEY_DOWN])
+                       speed = 2;
+               else if(engine.keyState[KEY_UP])
+                       speed = -2;
+
+               for (i = 0 ; i <= lastCredit ; i++)
+               {
+                       if ((credit[i].y > -10) && (credit[i].y < (screen->h + 10)))
+                               blit(credit[i].image, (int)credit[i].x, (int)credit[i].y);
+                       if (speed > 0 && credit[lastCredit].y > ((screen->h / 2) + 100))
+                               credit[i].y -= speed;
+                       else if(speed < 0 && credit[0].y < screen->h)
+                               credit[i].y -= speed;
+               }
+
+               delayFrame();
+       }
+
+       for (i = 0 ; i <= lastCredit ; i++)
+       {
+               SDL_FreeSurface(credit[i].image);
+       }
+
+       free(credit);
+}
+
diff --git a/src/title.h b/src/title.h
new file mode 100644 (file)
index 0000000..316cae3
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef TITLE_H
+#define TITLE_H
+
+extern int doTitle();
+extern void showStory();
+extern void gameover();
+extern void doCredits();
+
+#endif
diff --git a/src/weapons.cpp b/src/weapons.cpp
new file mode 100644 (file)
index 0000000..79be417
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011, 2013 Guus Sliepen
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Starfighter.h"
+
+object weapon[MAX_WEAPONS];
+
+void setWeaponShapes()
+{
+       for (int i = 0 ; i < MAX_WEAPONS ; i++)
+       {
+               weapon[i].image[0] = shape[weapon[i].imageIndex[0]];
+               weapon[i].image[1] = shape[weapon[i].imageIndex[1]];
+       }
+}
+
+/*
+A list of predefined weaponary.
+*/
+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_SPREAD;
+
+       // 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 = 0;
+
+       // 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 = 0;
+       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 = 6;
+       weapon[W_MICRO_ROCKETS].reload[0] = 30;
+       weapon[W_MICRO_ROCKETS].speed = 15;
+       weapon[W_MICRO_ROCKETS].flags = 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 = 0;
+
+       // 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 = 0;
+
+       // 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 = 0;
+       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_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_HOMING;
+       weapon[W_MICRO_HOMING_MISSILES].imageIndex[0] = 4;
+       weapon[W_MICRO_HOMING_MISSILES].imageIndex[1] = 4;
+
+       // Aimed plasma bolt
+       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_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 = 2;
+       weapon[W_SPREADSHOT].reload[0] = 10;
+       weapon[W_SPREADSHOT].speed = 10;
+       weapon[W_SPREADSHOT].flags = WF_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_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_AIMED | WF_TIMEDEXPLOSION;
+       weapon[W_DIRSHOCKMISSILE].imageIndex[0] = 4;
+       weapon[W_DIRSHOCKMISSILE].imageIndex[1] = 4;
+}
diff --git a/src/weapons.h b/src/weapons.h
new file mode 100644 (file)
index 0000000..0fd8493
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+Copyright (C) 2003 Parallel Realities
+Copyright (C) 2011 Guus Sliepen
+Copyright (C) 2015 Julian Marchant
+
+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 3
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef WEAPONS_H
+#define WEAPONS_H
+
+extern object weapon[MAX_WEAPONS];
+
+extern void setWeaponShapes();
+extern void initWeapons();
+
+#endif