summaryrefslogtreecommitdiff
path: root/src/uqm/starbase.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/starbase.c')
-rw-r--r--src/uqm/starbase.c602
1 files changed, 602 insertions, 0 deletions
diff --git a/src/uqm/starbase.c b/src/uqm/starbase.c
new file mode 100644
index 0000000..f119208
--- /dev/null
+++ b/src/uqm/starbase.c
@@ -0,0 +1,602 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "build.h"
+#include "colors.h"
+#include "controls.h"
+#include "starmap.h"
+#include "comm.h"
+#include "gamestr.h"
+#include "save.h"
+#include "starbase.h"
+#include "sis.h"
+#include "resinst.h"
+#include "nameref.h"
+#include "settings.h"
+#include "setup.h"
+#include "sounds.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/tasklib.h"
+
+
+static void CleanupAfterStarBase (void);
+
+static void
+DrawBaseStateStrings (STARBASE_STATE OldState, STARBASE_STATE NewState)
+{
+ TEXT t;
+ //STRING locString;
+
+ SetContext (ScreenContext);
+ SetContextFont (StarConFont);
+ SetContextForeGroundColor (BLACK_COLOR);
+
+ t.baseline.x = 73 - 4 + SAFE_X;
+ t.align = ALIGN_CENTER;
+
+ if (OldState == (STARBASE_STATE)~0)
+ {
+ t.baseline.y = 106 + 28 + (SAFE_Y + 4);
+ for (OldState = TALK_COMMANDER; OldState < DEPART_BASE; ++OldState)
+ {
+ if (OldState != NewState)
+ {
+ t.pStr = GAME_STRING (STARBASE_STRING_BASE + 1 + OldState);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ }
+ t.baseline.y += (23 - 4);
+ }
+ }
+
+ t.baseline.y = 106 + 28 + (SAFE_Y + 4) + ((23 - 4) * OldState);
+ t.pStr = GAME_STRING (STARBASE_STRING_BASE + 1 + OldState);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x0A), 0x0E));
+ t.baseline.y = 106 + 28 + (SAFE_Y + 4) + ((23 - 4) * NewState);
+ t.pStr = GAME_STRING (STARBASE_STRING_BASE + 1 + NewState);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+}
+
+void
+DrawShipPiece (FRAME ModuleFrame, COUNT which_piece, COUNT which_slot,
+ BOOLEAN DrawBluePrint)
+{
+ Color OldColor = UNDEFINED_COLOR;
+ // Initialisation is just to keep the compiler silent.
+ RECT r;
+ STAMP Side, Top;
+ SBYTE RepairSlot;
+
+ RepairSlot = 0;
+ switch (which_piece)
+ {
+ case FUSION_THRUSTER:
+ case EMPTY_SLOT + 0:
+ Side.origin.x = DRIVE_SIDE_X;
+ Side.origin.y = DRIVE_SIDE_Y;
+ Top.origin.x = DRIVE_TOP_X;
+ Top.origin.y = DRIVE_TOP_Y;
+ break;
+ case TURNING_JETS:
+ case EMPTY_SLOT + 1:
+ Side.origin.x = JET_SIDE_X;
+ Side.origin.y = JET_SIDE_Y;
+ Top.origin.x = JET_TOP_X;
+ Top.origin.y = JET_TOP_Y;
+ break;
+ default:
+ if (which_piece < EMPTY_SLOT + 2)
+ {
+ RepairSlot = 1;
+ if (which_piece < EMPTY_SLOT
+ && (which_slot == 0
+ || GLOBAL_SIS (ModuleSlots[
+ which_slot - 1
+ ]) < EMPTY_SLOT))
+ ++RepairSlot;
+ }
+ else if (!DrawBluePrint)
+ {
+ if (which_slot == 0 || which_slot >= NUM_MODULE_SLOTS - 3)
+ ++which_piece;
+
+ if (which_slot < NUM_MODULE_SLOTS - 1
+ && GLOBAL_SIS (ModuleSlots[
+ which_slot + 1
+ ]) < EMPTY_SLOT)
+ {
+ RepairSlot = -1;
+ if (which_piece == EMPTY_SLOT + 3
+ || which_slot + 1 == NUM_MODULE_SLOTS - 3)
+ --RepairSlot;
+ }
+ }
+ Side.origin.x = MODULE_SIDE_X;
+ Side.origin.y = MODULE_SIDE_Y;
+ Top.origin.x = MODULE_TOP_X;
+ Top.origin.y = MODULE_TOP_Y;
+ break;
+ }
+
+ Side.origin.x += which_slot * SHIP_PIECE_OFFSET;
+ Side.frame = NULL;
+ if (RepairSlot < 0)
+ {
+ Side.frame = SetAbsFrameIndex (ModuleFrame,
+ ((NUM_MODULES - 1) + (6 - 2)) + (NUM_MODULES + 6)
+ - (RepairSlot + 1));
+ DrawStamp (&Side);
+ }
+ else if (RepairSlot)
+ {
+ r.corner = Side.origin;
+ r.extent.width = SHIP_PIECE_OFFSET;
+ r.extent.height = 1;
+ OldColor = SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&r);
+ r.corner.y += 23 - 1;
+ DrawFilledRectangle (&r);
+
+ r.extent.width = 1;
+ r.extent.height = 8;
+ if (RepairSlot == 2)
+ {
+ r.corner = Side.origin;
+ DrawFilledRectangle (&r);
+ r.corner.y += 15;
+ DrawFilledRectangle (&r);
+ }
+ if (which_slot < (NUM_MODULE_SLOTS - 1))
+ {
+ r.corner = Side.origin;
+ r.corner.x += SHIP_PIECE_OFFSET;
+ DrawFilledRectangle (&r);
+ r.corner.y += 15;
+ DrawFilledRectangle (&r);
+ }
+ }
+
+ if (DrawBluePrint)
+ {
+ if (RepairSlot)
+ SetContextForeGroundColor (OldColor);
+ Side.frame = SetAbsFrameIndex (ModuleFrame, which_piece - 1);
+ DrawFilledStamp (&Side);
+ }
+ else
+ {
+ Top.origin.x += which_slot * SHIP_PIECE_OFFSET;
+ if (RepairSlot < 0)
+ {
+ Top.frame = SetRelFrameIndex (Side.frame, -((NUM_MODULES - 1) + 6));
+ DrawStamp (&Top);
+ }
+ else if (RepairSlot)
+ {
+ r.corner = Top.origin;
+ r.extent.width = SHIP_PIECE_OFFSET;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+ r.corner.y += 32 - 1;
+ DrawFilledRectangle (&r);
+
+ r.extent.width = 1;
+ r.extent.height = 12;
+ if (RepairSlot == 2)
+ {
+ r.corner = Top.origin;
+ DrawFilledRectangle (&r);
+ r.corner.y += 20;
+ DrawFilledRectangle (&r);
+ }
+ RepairSlot = (which_slot < NUM_MODULE_SLOTS - 1);
+ if (RepairSlot)
+ {
+ r.corner = Top.origin;
+ r.corner.x += SHIP_PIECE_OFFSET;
+ DrawFilledRectangle (&r);
+ r.corner.y += 20;
+ DrawFilledRectangle (&r);
+ }
+ }
+
+ Top.frame = SetAbsFrameIndex (ModuleFrame, which_piece);
+ DrawStamp (&Top);
+
+ Side.frame = SetRelFrameIndex (Top.frame, (NUM_MODULES - 1) + 6);
+ DrawStamp (&Side);
+
+ if (which_slot == 1 && which_piece == EMPTY_SLOT + 2)
+ {
+ STAMP s;
+
+ s.origin = Top.origin;
+ s.origin.x -= SHIP_PIECE_OFFSET;
+ s.frame = SetAbsFrameIndex (ModuleFrame, NUM_MODULES + 5);
+ DrawStamp (&s);
+ s.origin = Side.origin;
+ s.origin.x -= SHIP_PIECE_OFFSET;
+ s.frame = SetRelFrameIndex (s.frame, (NUM_MODULES - 1) + 6);
+ DrawStamp (&s);
+ }
+
+ if (RepairSlot)
+ {
+ Top.origin.x += SHIP_PIECE_OFFSET;
+ Side.origin.x += SHIP_PIECE_OFFSET;
+ which_piece = GLOBAL_SIS (ModuleSlots[++which_slot]);
+ if (which_piece == EMPTY_SLOT + 2
+ && which_slot >= NUM_MODULE_SLOTS - 3)
+ ++which_piece;
+
+ Top.frame = SetAbsFrameIndex (ModuleFrame, which_piece);
+ DrawStamp (&Top);
+
+ Side.frame = SetRelFrameIndex (Top.frame, (NUM_MODULES - 1) + 6);
+ DrawStamp (&Side);
+ }
+ }
+}
+
+static void
+rotateStarbase (MENU_STATE *pMS, FRAME AniFrame)
+{
+ static TimeCount NextTime = 0;
+ TimeCount Now = GetTimeCounter ();
+
+ if (AniFrame)
+ { // Setup the animation
+ pMS->flash_frame0 = AniFrame;
+ pMS->flash_rect0.corner.x = SAFE_X;
+ pMS->flash_rect0.corner.y = SAFE_Y + 4;
+ }
+
+ if (Now >= NextTime || AniFrame)
+ {
+ STAMP s;
+
+ NextTime = Now + (ONE_SECOND / 20);
+
+ s.origin = pMS->flash_rect0.corner;
+ s.frame = pMS->flash_frame0;
+ DrawStamp (&s);
+
+ s.frame = IncFrameIndex (s.frame);
+ if (GetFrameIndex (s.frame) == 0)
+ { // Do not redraw the base frame, animation loops to frame 1
+ s.frame = IncFrameIndex (s.frame);
+ }
+ pMS->flash_frame0 = s.frame;
+ }
+}
+
+BOOLEAN
+DoStarBase (MENU_STATE *pMS)
+{
+ // XXX: This function is full of hacks and otherwise strange code
+
+ if (GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ {
+ pMS->CurState = DEPART_BASE;
+ goto ExitStarBase;
+ }
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+
+ if (!pMS->Initialized)
+ {
+ LastActivity &= ~CHECK_LOAD;
+ pMS->InputFunc = DoStarBase;
+
+ SetFlashRect (NULL);
+
+ if (pMS->hMusic)
+ {
+ StopMusic ();
+ DestroyMusic (pMS->hMusic);
+ pMS->hMusic = 0;
+ }
+
+ pMS->Initialized = TRUE;
+
+ pMS->CurFrame = CaptureDrawable (LoadGraphic (STARBASE_ANIM));
+ pMS->hMusic = LoadMusic (STARBASE_MUSIC);
+
+ SetContext (ScreenContext);
+ SetTransitionSource (NULL);
+ BatchGraphics ();
+ SetContextBackGroundColor (BLACK_COLOR);
+ ClearDrawable ();
+ rotateStarbase (pMS, pMS->CurFrame);
+ DrawBaseStateStrings ((STARBASE_STATE)~0, pMS->CurState);
+ ScreenTransition (3, NULL);
+ PlayMusic (pMS->hMusic, TRUE, 1);
+ UnbatchGraphics ();
+ }
+ else if (PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ExitStarBase:
+ DestroyDrawable (ReleaseDrawable (pMS->CurFrame));
+ pMS->CurFrame = 0;
+ StopMusic ();
+ if (pMS->hMusic)
+ {
+ DestroyMusic (pMS->hMusic);
+ pMS->hMusic = 0;
+ }
+
+ if (pMS->CurState == DEPART_BASE)
+ {
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ SET_GAME_STATE (STARBASE_VISITED, 0);
+ }
+ return (FALSE);
+ }
+
+ pMS->Initialized = FALSE;
+ if (pMS->CurState == TALK_COMMANDER)
+ {
+ FlushInput ();
+ InitCommunication (COMMANDER_CONVERSATION);
+ // XXX: InitCommunication() clears these flags, and we need them
+ // This marks that we are in Starbase.
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, (BYTE)~0);
+ }
+ else
+ {
+ BYTE OldState;
+
+ switch (OldState = pMS->CurState)
+ {
+ case OUTFIT_STARSHIP:
+ pMS->InputFunc = DoOutfit;
+ break;
+ case SHIPYARD:
+ pMS->InputFunc = DoShipyard;
+ break;
+ }
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ DoInput (pMS, TRUE);
+
+ pMS->Initialized = FALSE;
+ pMS->CurState = OldState;
+ pMS->InputFunc = DoStarBase;
+ }
+ }
+ else
+ {
+ STARBASE_STATE NewState;
+
+ NewState = pMS->CurState;
+ if (PulsedInputState.menu[KEY_MENU_LEFT] || PulsedInputState.menu[KEY_MENU_UP])
+ {
+ if (NewState-- == TALK_COMMANDER)
+ NewState = DEPART_BASE;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_RIGHT] || PulsedInputState.menu[KEY_MENU_DOWN])
+ {
+ if (NewState++ == DEPART_BASE)
+ NewState = TALK_COMMANDER;
+ }
+
+ BatchGraphics ();
+ SetContext (ScreenContext);
+
+ if (NewState != pMS->CurState)
+ {
+ DrawBaseStateStrings (pMS->CurState, NewState);
+ pMS->CurState = NewState;
+ }
+
+ rotateStarbase (pMS, NULL);
+
+ UnbatchGraphics ();
+
+ SleepThread (ONE_SECOND / 30);
+ }
+
+ return (TRUE);
+}
+
+static void
+DoTimePassage (void)
+{
+#define LOST_DAYS 14
+ SleepThreadUntil (FadeScreen (FadeAllToBlack, ONE_SECOND * 2));
+ MoveGameClockDays (LOST_DAYS);
+}
+
+void
+VisitStarBase (void)
+{
+ MENU_STATE MenuState;
+ CONTEXT OldContext;
+ StatMsgMode prevMsgMode = SMM_UNDEFINED;
+
+ // XXX: This should probably be moved out to Starcon2Main()
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) == 2)
+ { // We were just transported by Chmmr to the Starbase
+ // Force a reload of the SolarSys
+ CurStarDescPtr = NULL;
+ // This marks that we are in Starbase.
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, (BYTE)~0);
+ }
+
+ if (!GET_GAME_STATE (STARBASE_AVAILABLE))
+ {
+ HSHIPFRAG hStarShip;
+ SHIP_FRAGMENT *FragPtr;
+
+ // Unallied Starbase conversation
+ SetCommIntroMode (CIM_CROSSFADE_SCREEN, 0);
+ InitCommunication (COMMANDER_CONVERSATION);
+ if (!GET_GAME_STATE (PROBE_ILWRATH_ENCOUNTER)
+ || (GLOBAL (CurrentActivity) & CHECK_ABORT))
+ {
+ CleanupAfterStarBase ();
+ return;
+ }
+
+ /* Create an Ilwrath ship responding to the Ur-Quan
+ * probe's broadcast */
+ hStarShip = CloneShipFragment (ILWRATH_SHIP,
+ &GLOBAL (npc_built_ship_q), 7);
+ FragPtr = LockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+ /* Hack (sort of): Suppress the tally and salvage info
+ * after the battle */
+ FragPtr->race_id = (BYTE)~0;
+ UnlockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+
+ InitCommunication (ILWRATH_CONVERSATION);
+ if (GLOBAL_SIS (CrewEnlisted) == (COUNT)~0
+ || (GLOBAL (CurrentActivity) & CHECK_ABORT))
+ return; // Killed by Ilwrath
+
+ // After Ilwrath battle, about-to-ally Starbase conversation
+ SetCommIntroMode (CIM_CROSSFADE_SCREEN, 0);
+ InitCommunication (COMMANDER_CONVERSATION);
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return;
+ // XXX: InitCommunication() clears these flags, and we need them
+ // This marks that we are in Starbase.
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, (BYTE)~0);
+ }
+
+ prevMsgMode = SetStatusMessageMode (SMM_RES_UNITS);
+
+ if (GET_GAME_STATE (MOONBASE_ON_SHIP)
+ || GET_GAME_STATE (CHMMR_BOMB_STATE) == 2)
+ { // Go immediately into a conversation with the Commander when the
+ // Starbase becomes available for the first time, or after Chmmr
+ // install the bomb.
+ DoTimePassage ();
+ if (GLOBAL_SIS (CrewEnlisted) == (COUNT)~0)
+ return; // You are now dead! Thank you! (killed by Kohr-Ah)
+
+ SetCommIntroMode (CIM_FADE_IN_SCREEN, ONE_SECOND * 2);
+ InitCommunication (COMMANDER_CONVERSATION);
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return;
+ // XXX: InitCommunication() clears these flags, and we need them
+ // This marks that we are in Starbase.
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, (BYTE)~0);
+ }
+
+ memset (&MenuState, 0, sizeof (MenuState));
+ MenuState.InputFunc = DoStarBase;
+
+ OldContext = SetContext (ScreenContext);
+ DoInput (&MenuState, TRUE);
+ SetContext (OldContext);
+
+ SetStatusMessageMode (prevMsgMode);
+ CleanupAfterStarBase ();
+}
+
+static void
+CleanupAfterStarBase (void)
+{
+ if (!(GLOBAL (CurrentActivity) & (CHECK_LOAD | CHECK_ABORT)))
+ {
+ // Mark as not in Starbase anymore
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 0);
+ // Fake a load so Starcon2Main takes us to IP
+ GLOBAL (CurrentActivity) = CHECK_LOAD;
+ NextActivity = MAKE_WORD (IN_INTERPLANETARY, 0)
+ | START_INTERPLANETARY;
+ }
+}
+
+void
+InstallBombAtEarth (void)
+{
+ DoTimePassage ();
+
+ SetContext (ScreenContext);
+ SetTransitionSource (NULL);
+ SetContextBackGroundColor (BLACK_COLOR);
+ ClearDrawable ();
+
+ SleepThreadUntil (FadeScreen (FadeAllToColor, 0));
+
+ SET_GAME_STATE (CHMMR_BOMB_STATE, 3); /* bomb processed */
+ GLOBAL (CurrentActivity) = CHECK_LOAD; /* fake a load game */
+ NextActivity = MAKE_WORD (IN_INTERPLANETARY, 0) | START_INTERPLANETARY;
+ CurStarDescPtr = 0; /* force SolarSys reload */
+}
+
+// XXX: Doesn't really belong in this file.
+COUNT
+WrapText (const UNICODE *pStr, COUNT len, TEXT *tarray, SIZE field_width)
+{
+ COUNT num_lines;
+
+ num_lines = 0;
+ do
+ {
+ RECT r;
+ COUNT OldCount;
+
+ tarray->align = ALIGN_LEFT; /* set alignment to something */
+ tarray->pStr = pStr;
+ tarray->CharCount = 1;
+ ++num_lines;
+
+ do
+ {
+ OldCount = tarray->CharCount;
+ while (*++pStr != ' ' && (COUNT)(pStr - tarray->pStr) < len)
+ ;
+ tarray->CharCount = pStr - tarray->pStr;
+ TextRect (tarray, &r, NULL);
+ } while (tarray->CharCount < len && r.extent.width < field_width);
+
+ if (r.extent.width >= field_width)
+ {
+ if ((tarray->CharCount = OldCount) == 1)
+ {
+ do
+ {
+ ++tarray->CharCount;
+ TextRect (tarray, &r, NULL);
+ } while (r.extent.width < field_width);
+ --tarray->CharCount;
+ }
+ }
+
+ pStr = tarray->pStr + tarray->CharCount;
+ len -= tarray->CharCount;
+ ++tarray;
+
+ if (len && (r.extent.width < field_width || OldCount > 1))
+ {
+ ++pStr; /* skip white space */
+ --len;
+ }
+
+ } while (len);
+
+ return (num_lines);
+}
+