// // Copyright 2020 Electronic Arts Inc. // // TiberianDawn.DLL and RedAlert.dll and corresponding source code 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. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed // in the hope that it will be useful, but with permitted additional restrictions // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT // distributed with this program. You should have received a copy of the // GNU General Public License along with permitted additional restrictions // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection /* $Header: /counterstrike/SIDEBAR.CPP 2 3/17/97 1:05a Steve_tall $ */ /*********************************************************************************************** *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** *********************************************************************************************** * * * Project Name : Command & Conquer * * * * File Name : SIDEBAR.CPP * * * * Programmer : Joe L. Bostic * * * * Start Date : October 20, 1994 * * * * Last Update : October 9, 1996 [JLB] * * * *---------------------------------------------------------------------------------------------* * Functions: * * SidebarClass::AI -- Handles player clicking on sidebar area. * * SidebarClass::Abandon_Production -- Stops production of the object specified. * * SidebarClass::Activate -- Controls the sidebar activation. * * SidebarClass::Activate_Demolish -- Controls the demolish button on the sidebar. * * SidebarClass::Activate_Repair -- Controls the repair button on the sidebar. * * SidebarClass::Activate_Upgrade -- Controls the upgrade button on the sidebar. * * SidebarClass::Add -- Adds a game object to the sidebar list. * * SidebarClass::Draw_It -- Renders the sidebar display. * * SidebarClass::Factory_Link -- Links a factory to a sidebar strip. * * SidebarClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * * SidebarClass::Init_IO -- Adds buttons to the button list * * SidebarClass::Init_Theater -- Performs theater-specific initialization * * SidebarClass::One_Time -- Handles the one time game initializations. * * SidebarClass::One_Time -- Handles the one time game initializations. * * SidebarClass::Recalc -- Examines the sidebar data and updates it as necessary. * * SidebarClass::Refresh_Cells -- Intercepts the refresh, looking for sidebar controls. * * SidebarClass::SBGadgetClass::Action -- Special function that controls the mouse over the s* * SidebarClass::Scroll -- Handles scrolling the sidebar object strip. * * SidebarClass::Set_Current -- Sets a specified object that controls the sidebar display. * * SidebarClass::SidebarClass -- Default constructor for the sidebar. * * SidebarClass::SidebarClass -- This is the no initialization constructor for the sidebar. * * SidebarClass::StripClass::AI -- Input and AI processing for the side strip. * * SidebarClass::StripClass::Abandon_Produ -- Abandons production associated with sidebar. * * SidebarClass::StripClass::Activate -- Adds the strip buttons to the input system. * * SidebarClass::StripClass::Add -- Add an object to the side strip. * * SidebarClass::StripClass::Deactivate -- Removes the side strip buttons from the input syst* * SidebarClass::StripClass::Draw_It -- Render the sidebar display. * * SidebarClass::StripClass::Factory_Link -- Links a factory to a sidebar button. * * SidebarClass::StripClass::Flag_To_Redra -- Flags the sidebar strip to be redrawn. * * SidebarClass::StripClass::Get_Special_Cameo -- Fetches the special event cameo shape. * * SidebarClass::StripClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * * SidebarClass::StripClass::Init_IO -- Adds buttons to the button list * * SidebarClass::StripClass::Init_Theater -- Performs theater-specific initialization * * SidebarClass::StripClass::One_Time -- Performs one time actions necessary for the side str* * SidebarClass::StripClass::Recalc -- Revalidates the current sidebar list of objects. * * SidebarClass::StripClass::Scroll -- Causes the side strip to scroll. * * SidebarClass::StripClass::SelectClass:: -- Action function when buildable cameo is selecte* * SidebarClass::StripClass::SelectClass:: -- Assigns special values to a buildable select bu* * SidebarClass::StripClass::SelectClass::SelectClass -- Default constructor. * * SidebarClass::StripClass::StripClass -- Default constructor for the side strip class. * * SidebarClass::Which_Column -- Determines which column a given type should appear. * * SidebarClass::Zoom_Mode_Control -- Handles the zoom mode toggle operation. * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "function.h" void * SidebarClass::SidebarShape = NULL; void * SidebarClass::SidebarMiddleShape = NULL; void * SidebarClass::SidebarBottomShape = NULL; /*************************************************************************** ** This holds the translucent table for use with the construction clock ** animation. */ char SidebarClass::StripClass::ClockTranslucentTable[(1+1)*256]; /*************************************************************************** ** This points to the main sidebar shapes. These include the upgrade and ** repair buttons. */ //TheaterType SidebarClass::StripClass::LastTheater = THEATER_NONE; typedef enum ButtonNumberType { BUTTON_RADAR = 100, BUTTON_REPAIR, BUTTON_DEMOLISH, BUTTON_UPGRADE, BUTTON_SELECT, BUTTON_ZOOM } ButtonNumberType; /* ** Sidebar buttons */ SidebarClass::SBGadgetClass SidebarClass::Background; ShapeButtonClass SidebarClass::Repair; ShapeButtonClass SidebarClass::Upgrade; ShapeButtonClass SidebarClass::Zoom; ShapeButtonClass SidebarClass::StripClass::UpButton[COLUMNS]; ShapeButtonClass SidebarClass::StripClass::DownButton[COLUMNS]; SidebarClass::StripClass::SelectClass SidebarClass::StripClass::SelectButton[COLUMNS][MAX_VISIBLE]; /* ** Shape data pointers */ void * SidebarClass::StripClass::LogoShapes = NULL; void const * SidebarClass::StripClass::ClockShapes; void const * SidebarClass::StripClass::SpecialShapes[SPC_COUNT]; /*********************************************************************************************** * SidebarClass::SidebarClass -- Default constructor for the sidebar. * * * * Constructor for the sidebar handler. It basically sets up the sidebar to the empty * * condition. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 11/17/1994 JLB : Created. * *=============================================================================================*/ SidebarClass::SidebarClass(void) : IsSidebarActive(false), IsToRedraw(true), IsRepairActive(false), IsUpgradeActive(false), IsDemolishActive(false) { /* ** This sets up the clipping window. This window is used by the shape drawing ** code so that as the sidebar buildable buttons scroll, they get properly ** clipped at the top and bottom edges. */ WindowList[WINDOW_SIDEBAR][WINDOWX] = (SIDE_X+8); WindowList[WINDOW_SIDEBAR][WINDOWY] = SIDE_Y + 1 + TOP_HEIGHT; WindowList[WINDOW_SIDEBAR][WINDOWWIDTH] = SIDE_WIDTH; WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = StripClass::MAX_VISIBLE * StripClass::OBJECT_HEIGHT; // WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = StripClass::MAX_VISIBLE * StripClass::OBJECT_HEIGHT-1; /* ** Set up the coordinates for the sidebar strips. These coordinates are for ** the upper left corner. */ new (&Column[0]) StripClass(InitClass()); new (&Column[1]) StripClass(InitClass()); Column[0].X = COLUMN_ONE_X * RESFACTOR; Column[0].Y = COLUMN_ONE_Y * RESFACTOR; Column[1].X = COLUMN_TWO_X * RESFACTOR; Column[1].Y = COLUMN_TWO_Y * RESFACTOR; } /*********************************************************************************************** * SidebarClass::SidebarClass -- This is the no initialization constructor for the sidebar. * * * * Unlike the normal constructor, this one doesn't do any initialization. There is one * * exception to this. The stip classes can't call an explicit NoInitClass constructor * * since they are an array. Since the default constructor is called for these strips, we * * must reset the X and Y location to what we know they should be. * * * * INPUT: flag to indicate that this is a no initialization constructor. * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 08/06/1996 JLB : Created. * *=============================================================================================*/ SidebarClass::SidebarClass(NoInitClass const & x) : PowerClass(x) { /* ** Set up the coordinates for the sidebar strips. These coordinates are for ** the upper left corner. */ // Column[0].X = COLUMN_ONE_X * RESFACTOR; // Column[0].Y = COLUMN_ONE_Y * RESFACTOR; // Column[1].X = COLUMN_TWO_X * RESFACTOR; // Column[1].Y = COLUMN_TWO_Y * RESFACTOR; } /*********************************************************************************************** * SidebarClass::One_Time -- Handles the one time game initializations. * * * * This routine is used to load the graphic data that is needed by the sidebar display. It * * should only be called ONCE. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: Only call this routine once when the game first starts. * * * * HISTORY: * * 10/28/94 JLB : Created. * *=============================================================================================*/ void SidebarClass::One_Time(void) { PowerClass::One_Time(); /* ** This sets up the clipping window. This window is used by the shape drawing ** code so that as the sidebar buildable buttons scroll, they get properly ** clipped at the top and bottom edges. */ WindowList[WINDOW_SIDEBAR][WINDOWX] = ((SIDE_X+8)) * RESFACTOR; WindowList[WINDOW_SIDEBAR][WINDOWY] = (SIDE_Y + 1 + TOP_HEIGHT) * RESFACTOR; WindowList[WINDOW_SIDEBAR][WINDOWWIDTH] = (SIDE_WIDTH) * RESFACTOR; WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = (StripClass::MAX_VISIBLE * StripClass::OBJECT_HEIGHT) * RESFACTOR; // WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = (StripClass::MAX_VISIBLE * StripClass::OBJECT_HEIGHT-1) * RESFACTOR; /* ** Top of the window seems to be wrong for the new sidebar. ST - 5/2/96 2:49AM */ WindowList[WINDOW_SIDEBAR][WINDOWY] -= 1*RESFACTOR; /* ** Set up the coordinates for the sidebar strips. These coordinates are for ** the upper left corner. */ // Column[0].X = COLUMN_ONE_X * RESFACTOR; // Column[0].Y = COLUMN_ONE_Y * RESFACTOR; // Column[1].X = COLUMN_TWO_X * RESFACTOR; // Column[1].Y = COLUMN_TWO_Y * RESFACTOR; Column[0].One_Time(0); Column[1].One_Time(1); /* ** Load the sidebar shape in at this time. (Hi-Res sidebar is theater dependant) */ if (SidebarShape == NULL) { SidebarShape = (void*)MFCD::Retrieve("SIDEBAR.SHP"); } } /*********************************************************************************************** * SidebarClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 12/24/1994 JLB : Created. * *=============================================================================================*/ void SidebarClass::Init_Clear(void) { PowerClass::Init_Clear(); IsToRedraw = true; IsRepairActive = false; IsUpgradeActive = false; IsDemolishActive = false; Column[0].Init_Clear(); Column[1].Init_Clear(); Activate(false); } /*********************************************************************************************** * SidebarClass::Init_IO -- Adds buttons to the button list * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 12/24/1994 JLB : Created. * *=============================================================================================*/ void SidebarClass::Init_IO(void) { PowerClass::Init_IO(); /* ** Add the sidebar's buttons only if we're not in editor mode. */ if (!Debug_Map) { Repair.IsSticky = true; Repair.ID = BUTTON_REPAIR; Repair.X = (0x1f2/2)*RESFACTOR; Repair.Y = (0x96/2)*RESFACTOR; Repair.IsPressed = false; Repair.IsToggleType = true; Repair.ReflectButtonState = true; Repair.Set_Shape(MFCD::Retrieve("REPAIR.SHP")); Upgrade.IsSticky = true; Upgrade.ID = BUTTON_UPGRADE; #ifdef WIN32 Upgrade.X = 0x21f; #else Upgrade.X = ((0x21f/2)+1)*RESFACTOR; #endif Upgrade.Y = (0x96/2)*RESFACTOR; Upgrade.IsPressed = false; Upgrade.IsToggleType = true; Upgrade.ReflectButtonState = true; Upgrade.Set_Shape(MFCD::Retrieve("SELL.SHP")); Zoom.IsSticky = true; Zoom.ID = BUTTON_ZOOM; Zoom.X = (0x24c/2)*RESFACTOR; Zoom.Y = (0x96/2)*RESFACTOR; Zoom.IsPressed = false; Zoom.Set_Shape(MFCD::Retrieve("MAP.SHP")); if ((IsRadarActive && Is_Zoomable()) || Session.Type != GAME_NORMAL) { Zoom.Enable(); } else { Zoom.Disable(); } Column[0].Init_IO(0); Column[1].Init_IO(1); /* ** If a game was loaded & the sidebar was enabled, pop it up now */ if (IsSidebarActive) { IsSidebarActive = false; Activate(1); // Background.Zap(); // Add_A_Button(Background); } } } /*********************************************************************************************** * SidebarClass::Init_Theater -- Performs theater-specific initialization * * * * INPUT: theater -- The theater that is being initialized. Sometimes this has an effect on * * the data that is loaded. * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 12/24/1994 JLB : Created. * *=============================================================================================*/ void SidebarClass::Init_Theater(TheaterType theater) { Reload_Sidebar(); PowerClass::Init_Theater(theater); Column[0].Init_Theater(theater); Column[1].Init_Theater(theater); } /*********************************************************************************************** * SidebarClass::Reload_Sidebar -- Loads appropriate sidebar shapes depending on house * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 9/18/1996 BWG : Created. * *=============================================================================================*/ void SidebarClass::Reload_Sidebar(void) { static char * sidebarnames[]={ "SIDE?NA.SHP", //NATO "SIDE?NA.SHP", "SIDE?US.SHP", //USSR "SIDE?NA.SHP", "SIDE?US.SHP", //UKRAINE "SIDE?NA.SHP", "SIDE?NA.SHP", "SIDE?NA.SHP", "SIDE?NA.SHP", //HOUSE_GOOD "SIDE?US.SHP" //HOUSE_BAD }; int houseloaded = 0; if(PlayerPtr) { houseloaded = PlayerPtr->ActLike; } /* Don't have write access to the static char array. ST - 5/20/2019 */ #if (0) char * sidename = sidebarnames[houseloaded]; *(sidename+4) = '1'; SidebarShape = (void*)MFCD::Retrieve(sidename); *(sidename+4) = '2'; SidebarMiddleShape = (void*)MFCD::Retrieve(sidename); *(sidename+4) = '3'; SidebarBottomShape = (void*)MFCD::Retrieve(sidename); #else char sb_name[16]; strcpy(sb_name, sidebarnames[houseloaded]); sb_name[4] = '1'; SidebarShape = (void*)MFCD::Retrieve(sb_name); sb_name[4] = '2'; SidebarMiddleShape = (void*)MFCD::Retrieve(sb_name); sb_name[4] = '3'; SidebarBottomShape = (void*)MFCD::Retrieve(sb_name); #endif Column[0].Reload_LogoShapes(); Column[1].Reload_LogoShapes(); } /*********************************************************************************************** * SidebarClass::Which_Column -- Determines which column a given type should appear. * * * * Use this function to resolve what column the specified object type should be placed * * into. * * * * INPUT: otype -- Pointer to the object type class of the object in question. * * * * OUTPUT: Returns with the column number that the object should be placed in. * * * * WARNINGS: none * * * * HISTORY: * * 01/01/1995 JLB : Created. * *=============================================================================================*/ int SidebarClass::Which_Column(RTTIType type) { if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING) { return(0); } return(1); } /*********************************************************************************************** * SidebarClass::Factory_Link -- Links a factory to a sidebar strip. * * * * This routine will link the specified factory to the sidebar strip. A factory must be * * linked to the sidebar so that as the factory production progresses, the sidebar will * * show the production progress. * * * * INPUT: factory -- The factory number to attach. * * * * type -- The object type number. * * * * id -- The object sub-type number. * * * * OUTPUT: Was the factory successfully attached to the sidebar strip? * * * * WARNINGS: none * * * * HISTORY: * * 05/19/1995 JLB : Created. * *=============================================================================================*/ bool SidebarClass::Factory_Link(int factory, RTTIType type, int id) { assert((unsigned)type < RTTI_COUNT); assert(id >= 0); return(Column[Which_Column(type)].Factory_Link(factory, type, id)); } /*********************************************************************************************** * SidebarClass::Refresh_Cells -- Intercepts the refresh, looking for sidebar controls. * * * * This routine intercepts the Refresh_Cells call in order to see if the sidebar needs * * to be refreshed as well. If the special code to refresh the sidebar was found, it * * flags the sidebar to be redrawn and then removes the code from the list. * * * * INPUT: cell -- The cell to base the refresh list on. * * * * list -- Pointer to the cell offset list that elaborates all the cells that * * need to be flagged for redraw. * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 01/19/1995 JLB : Created. * *=============================================================================================*/ void SidebarClass::Refresh_Cells(CELL cell, short const * list) { if (*list == REFRESH_SIDEBAR) { IsToRedraw = true; Column[0].IsToRedraw = true; Column[1].IsToRedraw = true; Flag_To_Redraw(false); } PowerClass::Refresh_Cells(cell, list); } /*********************************************************************************************** * SidebarClass::Activate_Repair -- Controls the repair button on the sidebar. * * * * Use this routine to turn the repair sidebar button on and off. Typically, the button * * is enabled when the currently selected structure is friendly and damaged. * * * * INPUT: control -- The controls how the button is to be activated or deactivated; * * 0 -- Turn button off. * * 1 -- Turn button on. * * -1 -- Toggle button state. * * * * OUTPUT: bool; Was the button previously activated? * * * * WARNINGS: none * * * * HISTORY: * * 12/24/1994 JLB : Created. * *=============================================================================================*/ bool SidebarClass::Activate_Repair(int control) { bool old = IsRepairActive; if (control == -1) { control = IsRepairActive ? 0 : 1; } switch (control) { case 1: IsRepairActive = true; break; default: case 0: IsRepairActive = false; break; } if (old != IsRepairActive) { Flag_To_Redraw(false); IsToRedraw = true; if (!IsRepairActive) { Help_Text(TXT_NONE); Set_Default_Mouse(MOUSE_NORMAL, false); } } return(old); } /*********************************************************************************************** * SidebarClass::Activate_Upgrade -- Controls the upgrade button on the sidebar. * * * * Use this routine to turn the upgrade sidebar button on and off. Typically, the button * * is enabled when the currently selected structure can be upgraded and disabled otherwise. * * * * INPUT: control -- The controls how the button is to be activated or deactivated; * * 0 -- Turn button off. * * 1 -- Turn button on. * * -1 -- Toggle button state. * * * * OUTPUT: bool; Was the button previously activated? * * * * WARNINGS: none * * * * HISTORY: * * 12/24/1994 JLB : Created. * *=============================================================================================*/ bool SidebarClass::Activate_Upgrade(int control) { bool old = IsUpgradeActive; if (control == -1) { control = IsUpgradeActive ? 0 : 1; } switch (control) { case 1: IsUpgradeActive = true; break; default: case 0: IsUpgradeActive = false; break; } if (old != IsUpgradeActive) { Flag_To_Redraw(false); IsToRedraw = true; if (!IsUpgradeActive) { Set_Default_Mouse(MOUSE_NORMAL, false); } } return(old); } /*********************************************************************************************** * SidebarClass::Activate_Demolish -- Controls the demolish button on the sidebar. * * * * Use this routine to turn the demolish/dismantle sidebar button on and off. Typically, * * the button is enabled when a friendly building is selected and disabled otherwise. * * * * INPUT: control -- The controls how the button is to be activated or deactivated; * * 0 -- Turn button off. * * 1 -- Turn button on. * * -1 -- Toggle button state. * * * * OUTPUT: bool; Was the button previously activated? * * * * WARNINGS: none * * * * HISTORY: * * 12/24/1994 JLB : Created. * *=============================================================================================*/ bool SidebarClass::Activate_Demolish(int control) { bool old = IsDemolishActive; if (control == -1) { control = IsDemolishActive ? 0 : 1; } switch (control) { case 1: IsDemolishActive = true; break; default: case 0: IsDemolishActive = false; break; } if (old != IsDemolishActive) { Flag_To_Redraw(false); IsToRedraw = true; if (!IsDemolishActive) { Set_Default_Mouse(MOUSE_NORMAL, false); } } return(old); } /*********************************************************************************************** * SidebarClass::Add -- Adds a game object to the sidebar list. * * * * This routine is used to add a game object to the sidebar. Call this routine when a * * factory type building is created. It handles the case of adding an item that has already * * been added -- it just ignores it. * * * * INPUT: object -- Pointer to the object that is being added. * * * * OUTPUT: bool; Was the object added to the sidebar? * * * * WARNINGS: none * * * * HISTORY: * * 11/17/1994 JLB : Created. * * 9/24/2019 3:17PM : Added via capture parameter for new sidebar functionality * *=============================================================================================*/ bool SidebarClass::Add(RTTIType type, int id, bool via_capture) { assert((unsigned)type < RTTI_COUNT); /* ** Add the sidebar only if we're not in editor mode. */ if (!Debug_Map) { int column = Which_Column(type); if (Column[column].Add(type, id, via_capture)) { Activate(1); IsToRedraw = true; Flag_To_Redraw(false); return(true); } return(false); } return(false); } /*********************************************************************************************** * SidebarClass::Scroll -- Handles scrolling the sidebar object strip. * * * * This routine is used to scroll the sidebar strip of objects. The strip appears whenever * * a building is selected that can produce units. If the number of units to produce is * * greater than what the sidebar can hold, this routine is used to scroll the other object * * into view so they can be selected. * * * * INPUT: up -- Should the scroll be upwards? Upward scrolling reveals object that are * * later in the list of objects. * * * * OUTPUT: bool; Did scrolling occur? * * * * WARNINGS: none * * * * HISTORY: * * 10/28/94 JLB : Created. * *=============================================================================================*/ bool SidebarClass::Scroll(bool up, int column) { if (column == -1) { bool scr = false; scr |= Column[0].Scroll(up); scr |= Column[1].Scroll(up); if (!scr) { Sound_Effect(VOC_SCOLD); } if (scr) { IsToRedraw = true; Flag_To_Redraw(false); return(true); } return(false); } if (Column[column].Scroll(up)) { // No need to redraw the whole sidebar juts because we scrolled a strip is there? ST - 10/15/96 7:29PM //IsToRedraw = true; Flag_To_Redraw(false); return(true); } return(false); } /*********************************************************************************************** * SidebarClass::Draw_It -- Renders the sidebar display. * * * * This routine performs the actual drawing of the sidebar display. * * * * INPUT: none * * * * OUTPUT: bool; Was the sidebar imagery changed at all? * * * * WARNINGS: none * * * * HISTORY: * * 10/28/94 JLB : Created. * * 12/31/1994 JLB : Split rendering off into the sidebar strip class. * *=============================================================================================*/ void SidebarClass::Draw_It(bool complete) { PowerClass::Draw_It(complete); BStart(BENCH_SIDEBAR); if (IsSidebarActive && (IsToRedraw || complete) && !Debug_Map) { IsToRedraw = false; if (LogicPage->Lock()) { /* ** Draw the outline box around the sidebar buttons. */ int shape = complete ? 0 : 1; /* ** The sidebar shape is too big in 640x400 so it needs to be drawn in three chunks. */ CC_Draw_Shape(SidebarShape, 0, SIDE_X * RESFACTOR, 8*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL); CC_Draw_Shape(SidebarMiddleShape, shape, SIDE_X * RESFACTOR, (8+80)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL); CC_Draw_Shape(SidebarBottomShape, shape, SIDE_X * RESFACTOR, (8+80+50)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL); Repair.Draw_Me(true); Upgrade.Draw_Me(true); Zoom.Draw_Me(true); LogicPage->Unlock(); } } /* ** Draw the side strip elements by calling their respective draw functions. */ if (IsSidebarActive) { Column[0].Draw_It(complete); Column[1].Draw_It(complete); if (complete || IsToRedraw) { Repair.Draw_Me(true); Upgrade.Draw_Me(true); Zoom.Draw_Me(true); } } IsToRedraw = false; BEnd(BENCH_SIDEBAR); } /*********************************************************************************************** * SidebarClass::AI -- Handles player clicking on sidebar area. * * * * This routine handles the processing necessary when the player clicks on the sidebar. * * Typically, this is selection of the item to build. * * * * INPUT: input -- Reference to the keyboard input value. * * * * x,y -- Mouse coordinates at time of input. * * * * OUTPUT: bool; Was the click handled? * * * * WARNINGS: none * * * * HISTORY: * * 10/28/94 JLB : Created. * * 11/11/1994 JLB : Processes input directly. * * 12/26/1994 JLB : Uses factory manager class for construction handling. * * 12/31/1994 JLB : Simplified to use the sidebar strip class handlers. * * 12/31/1994 JLB : Uses mouse coordinate parameters. * * 06/27/1995 JLB : key toggles sidebar. * *=============================================================================================*/ void SidebarClass::AI(KeyNumType & input, int x, int y) { bool redraw = false; // // We need to process the sidebar differently in multiplayer. ST - 8/7/2019 10:48AM // if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { PowerClass::AI(input, x, y); return; } /* ** Toggle the sidebar in and out with the key. */ #ifndef WIN32 if (input == KN_TAB) { Activate(-1); } #else if (!Debug_Map) { Activate(1); // Force the sidebar always on in Win95 mode } #endif //WIN32 if (!Debug_Map) { Column[0].AI(input, x, y); Column[1].AI(input, x, y); } #ifdef NEVER if (IsSidebarActive && !Debug_Map) { if (input == KN_DOWN) { int scr = 0; scr |= Column[0].Scroll(false); scr |= Column[1].Scroll(false); if (!scr) { Sound_Effect(VOC_SCOLD); } redraw |= scr; input = KN_NONE; } if (input == KN_UP) { int scr = 0; scr |= Column[0].Scroll(true); scr |= Column[1].Scroll(true); if (!scr) { Sound_Effect(VOC_SCOLD); } redraw |= scr; input = KN_NONE; } } #endif if (IsSidebarActive) { /* ** If there are any buildings in the payer's inventory, then allow the repair ** option. */ if (PlayerPtr->BScan) { Activate_Repair(true); } else { Activate_Repair(false); } if (input == (BUTTON_REPAIR|KN_BUTTON)) { Repair_Mode_Control(-1); } if (input == (BUTTON_ZOOM|KN_BUTTON)) { Zoom_Mode_Control(); } if (input == (BUTTON_UPGRADE|KN_BUTTON)) { Sell_Mode_Control(-1); } if (redraw) { //IsToRedraw = true; Column[0].Flag_To_Redraw(); Column[1].Flag_To_Redraw(); Flag_To_Redraw(false); } } if ((!IsRepairMode) && Repair.IsOn) { Repair.Turn_Off(); } if ((!IsSellMode) && Upgrade.IsOn) { Upgrade.Turn_Off(); } PowerClass::AI(input, x, y); } /*********************************************************************************************** * SidebarClass::Recalc -- Examines the sidebar data and updates it as necessary. * * * * Occasionally a factory gets destroyed. This routine must be called in such a case * * because it might be possible that sidebar object need to be removed. This routine will * * examine all existing objects in the sidebar class and if no possible factory can * * produce it, then it will be removed. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: This routine is exhaustive and thus time consuming. Only call it when really * * necessary. Such as when a factory is destroyed rather than when a non-factory * * is destroyed. * * * * HISTORY: * * 11/30/1994 JLB : Created. * *=============================================================================================*/ void SidebarClass::Recalc(void) { bool redraw = false; // Done elsewhere for new multiplayer. ST - 8/7/2019 10:49AM if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { return; } redraw |= Column[0].Recalc(); redraw |= Column[1].Recalc(); if (redraw) { IsToRedraw = true; Flag_To_Redraw(false); } } /*********************************************************************************************** * SidebarClass::Activate -- Controls the sidebar activation. * * * * Use this routine to turn the sidebar on or off. This routine handles updating the * * necessary flags. * * * * INPUT: control -- Tells what to do with the sidebar according to the following: * * 0 = Turn sidebar off. * * 1 = Turn sidebar on. * * -1= Toggle sidebar on or off. * * * * OUTPUT: bool; Was the sidebar already on? * * * * WARNINGS: none * * * * HISTORY: * * 12/09/1994 JLB : Created. * *=============================================================================================*/ bool SidebarClass::Activate(int control) { // // We don't want the original sidebar to be visible. ST - 1/31/2019 11:28AM // if (control < 100) { return IsSidebarActive; } bool old = IsSidebarActive; if (Session.Play) return (old); /* ** Determine the new state of the sidebar. */ switch (control) { case -1: IsSidebarActive = IsSidebarActive == false; break; case 1: IsSidebarActive = true; break; default: case 0: IsSidebarActive = false; break; } /* ** Only if there is a change in the state of the sidebar will anything ** be done to change it. */ if (IsSidebarActive != old) { /* ** If the sidebar is activated but was on the right side of the screen, then ** activate it on the left side of the screen. */ if (IsSidebarActive /*&& X*/) { Set_View_Dimensions(0, 8 * RESFACTOR, ((320-SIDE_WIDTH)/ICON_PIXEL_W) * RESFACTOR); IsToRedraw = true; Help_Text(TXT_NONE); Repair.Zap(); Add_A_Button(Repair); Upgrade.Zap(); Add_A_Button(Upgrade); Zoom.Zap(); Add_A_Button(Zoom); Column[0].Activate(); Column[1].Activate(); Background.Zap(); Add_A_Button(Background); Map.RadarButton.Zap(); Add_A_Button(Map.RadarButton); Map.PowerButton.Zap(); Add_A_Button(Map.PowerButton); } else { Help_Text(TXT_NONE); Set_View_Dimensions(0, 8 * RESFACTOR); Remove_A_Button(Repair); Remove_A_Button(Upgrade); Remove_A_Button(Zoom); Remove_A_Button(Background); Column[0].Deactivate(); Column[1].Deactivate(); Remove_A_Button(Map.RadarButton); Remove_A_Button(Map.PowerButton); } /* ** Since the sidebar status has changed, update the map so that the graphics ** will be rendered correctly. */ Flag_To_Redraw(true); } return(old); } /*********************************************************************************************** * SidebarClass::StripClass::StripClass -- Default constructor for the side strip class. * * * * This constructor is used to reset the side strip to default empty state. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 12/31/1994 JLB : Created. * *=============================================================================================*/ SidebarClass::StripClass::StripClass(InitClass const & ) : X(0), Y(0), ID(0), IsToRedraw(true), IsBuilding(false), IsScrollingDown(false), IsScrolling(false), Flasher(-1), TopIndex(0), Scroller(0), Slid(0), BuildableCount(0) { for (int index = 0; index < MAX_BUILDABLES; index++) { Buildables[index].BuildableID = 0; Buildables[index].BuildableType = RTTI_NONE; Buildables[index].Factory = -1; Buildables[index].BuildableViaCapture = false; // Added for new sidebar functionality. ST - 9/24/2019 3:10PM } } /*********************************************************************************************** * SidebarClass::StripClass::One_Time -- Performs one time actions necessary for the side stri * * * * Call this routine ONCE at the beginning of the game. It handles retrieving pointers to * * the shape files it needs for rendering. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 12/31/1994 JLB : Created. * *=============================================================================================*/ void SidebarClass::StripClass::One_Time(int ) { /* ** Sidebar is player team specific in Hires */ ClockShapes = MFCD::Retrieve("CLOCK.SHP"); for (SpecialWeaponType lp = SPC_FIRST; lp < SPC_COUNT; lp++) { char buffer[_MAX_FNAME]; sprintf(buffer, "%sICON", SpecialWeaponFile[lp]); char fullname[_MAX_FNAME+_MAX_EXT]; _makepath(fullname, NULL, NULL, buffer, ".SHP"); SpecialShapes[lp] = MFCD::Retrieve(fullname); } } /*********************************************************************************************** * SidebarClass::StripClass::Get_Special_Cameo -- Fetches the special event cameo shape. * * * * This routine will return with a pointer to the cameo data for the special objects that * * can appear on the sidebar (e.g., nuclear bomb). * * * * INPUT: type -- The special type to fetch the cameo imagery for. * * * * OUTPUT: Returns with a pointer to the cameo imagery for the specified special object. * * * * WARNINGS: none * * * * HISTORY: * * 05/19/1995 JLB : commented * *=============================================================================================*/ void const * SidebarClass::StripClass::Get_Special_Cameo(SpecialWeaponType type) { if ((unsigned)type < SPC_COUNT) { return(SpecialShapes[type]); } return(0); } /*********************************************************************************************** * SidebarClass::StripClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 12/24/1994 JLB : Created. * *=============================================================================================*/ void SidebarClass::StripClass::Init_Clear(void) { IsScrollingDown = false; IsScrolling = false; IsBuilding = false; Flasher = -1; TopIndex = 0; Slid = 0; BuildableCount = 0; /* ** Since we're resetting the strips, clear out all the buildables & factory pointers. */ for (int index = 0; index < MAX_BUILDABLES; index++) { Buildables[index].BuildableID = 0; Buildables[index].BuildableType = RTTI_NONE; Buildables[index].Factory = -1; Buildables[index].BuildableViaCapture = false; // Added for new sidebar functionality. ST - 9/24/2019 3:10PM } } /*********************************************************************************************** * SidebarClass::StripClass::Init_IO -- Initializes the strip's buttons * * * * This routine doesn't actually add any buttons to the list. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 12/24/1994 JLB : Created. * *=============================================================================================*/ void SidebarClass::StripClass::Init_IO(int id) { ID = id; UpButton[ID].IsSticky = true; UpButton[ID].ID = BUTTON_UP+id; UpButton[ID].X = X+(UP_X_OFFSET * RESFACTOR); UpButton[ID].Y = Y+(UP_Y_OFFSET * RESFACTOR); #if (FRENCH) #ifdef WIN32 UpButton[ID].Set_Shape(MFCD::Retrieve("STRIPUP.SHP")); #else UpButton[ID].Set_Shape(MFCD::Retrieve("STUP_FIX.SHP")); #endif #else //FRENCH UpButton[ID].Set_Shape(MFCD::Retrieve("STRIPUP.SHP")); #endif //FRENCH DownButton[ID].IsSticky = true; DownButton[ID].ID = BUTTON_DOWN+id; DownButton[ID].X = X+(DOWN_X_OFFSET * RESFACTOR); DownButton[ID].Y = Y+(DOWN_Y_OFFSET * RESFACTOR); /* ** Buttons are in a slightly different position in the new sidebar */ UpButton[ID].Y--; DownButton[ID].Y--; DownButton[ID].Set_Shape(MFCD::Retrieve("STRIPDN.SHP")); for (int index = 0; index < MAX_VISIBLE; index++) { SelectClass & g = SelectButton[ID][index]; g.ID = BUTTON_SELECT; g.X = X; g.Y = Y + ((OBJECT_HEIGHT*index) * RESFACTOR); g.Width = OBJECT_WIDTH * RESFACTOR; g.Height = OBJECT_HEIGHT * RESFACTOR; g.Set_Owner(*this, index); } } /*********************************************************************************************** * SidebarClass::StripClass::Init_Theater -- Performs theater-specific initialization * * * * INPUT: theater * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 12/24/1994 JLB : Created. * *=============================================================================================*/ void SidebarClass::StripClass::Init_Theater(TheaterType theater) { Reload_LogoShapes(); if ( (theater != THEATER_NONE) && (theater != ::LastTheater)) { static TLucentType const ClockCols[1] = { {GREEN, BLACK, 100, 0} // {GREEN, LTGREY, 180, 0} }; /* ** Make sure that remapping doesn't occur on the colors that cycle. */ PaletteClass pal = OriginalPalette; memset(&pal[CYCLE_COLOR_START*3], 0x3f, CYCLE_COLOR_COUNT*3); Build_Translucent_Table(pal, &ClockCols[0], 1, (void*)ClockTranslucentTable); // Mem_Copy(GamePalette, OriginalPalette, 768); // memset(&GamePalette[CYCLE_COLOR_START*3], 0x3f, CYCLE_COLOR_COUNT*3); /* ** Create the translucent table used for the sidebar. */ // Build_Translucent_Table(GamePalette, &ClockCols[0], 1, (void*)ClockTranslucentTable); // GamePalette = OriginalPalette; Conquer_Build_Fading_Table(GamePalette, &ClockTranslucentTable[256], BLACK, 100); } } void SidebarClass::StripClass::Reload_LogoShapes(void) { /* ** Load hi-res strip art here since it is player side specific */ static char * stripnames[]={ "stripna.shp", //Nato "stripna.shp", "stripus.shp", //USSR "stripna.shp", "stripus.shp", //UKRAINE "stripna.shp", "stripna.shp", "stripna.shp", "stripna.shp", //HOUSE_GOOD "stripus.shp", //HOUSE_BAD }; int houseloaded = 0; /* ** Sidebar art is dependent on the side of the player */ if(PlayerPtr) { houseloaded = PlayerPtr->ActLike; } LogoShapes = (void*)MFCD::Retrieve(stripnames[houseloaded]); } /*********************************************************************************************** * SidebarClass::StripClass::Activate -- Adds the strip buttons to the input system. * * * * This routine will add the side strip buttons to the map's input system. This routine * * should be called once when the sidebar activates. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: Never call this routine a second time without first calling Deactivate(). * * * * HISTORY: * * 01/19/1995 JLB : Created. * *=============================================================================================*/ void SidebarClass::StripClass::Activate(void) { UpButton[ID].Zap(); Map.Add_A_Button(UpButton[ID]); DownButton[ID].Zap(); Map.Add_A_Button(DownButton[ID]); for (int index = 0; index < MAX_VISIBLE; index++) { SelectButton[ID][index].Zap(); Map.Add_A_Button(SelectButton[ID][index]); } } /*********************************************************************************************** * SidebarClass::StripClass::Deactivate -- Removes the side strip buttons from the input syste * * * * Call this routine to remove all the buttons on the side strip from the map's input * * system. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: Never call this routine unless the Activate() function was previously called. * * * * HISTORY: * * 01/19/1995 JLB : Created. * *=============================================================================================*/ void SidebarClass::StripClass::Deactivate(void) { Map.Remove_A_Button(UpButton[ID]); Map.Remove_A_Button(DownButton[ID]); for (int index = 0; index < MAX_VISIBLE; index++) { Map.Remove_A_Button(SelectButton[ID][index]); } } /*********************************************************************************************** * SidebarClass::StripClass::Add -- Add an object to the side strip. * * * * Use this routine to add a buildable object to the side strip. * * * * INPUT: object -- Pointer to the object type that can be built and is to be added to * * the side strip. * * * * OUTPUT: bool; Was the object successfully added to the side strip? Failure could be the * * result of running out of room in the side strip array or the object might * * already be in the list. * * * * WARNINGS: none. * * * * HISTORY: * * 12/31/1994 JLB : Created. * * 9/24/2019 3:17PM : Added via capture parameter for new sidebar functionality * *=============================================================================================*/ bool SidebarClass::StripClass::Add(RTTIType type, int id, bool via_capture) { if (BuildableCount <= MAX_BUILDABLES) { for (int index = 0; index < BuildableCount; index++) { if (Buildables[index].BuildableType == type && Buildables[index].BuildableID == id) { return(false); } } if (!ScenarioInit && type != RTTI_SPECIAL) { Speak(VOX_NEW_CONSTRUCT); } Buildables[BuildableCount].BuildableType = type; Buildables[BuildableCount].BuildableID = id; Buildables[BuildableCount].BuildableViaCapture = via_capture; BuildableCount++; IsToRedraw = true; return(true); } return(false); } /*********************************************************************************************** * SidebarClass::StripClass::Scroll -- Causes the side strip to scroll. * * * * Use this routine to flag the side strip to scroll. The direction scrolled is controlled * * by the parameter. Scrolling is merely initiated by this routine. Subsequent calls to * * the AI function and the Draw_It function are required to properly give the appearance * * of scrolling. * * * * INPUT: bool; Should the side strip scroll UP? If it is to scroll down then pass false. * * * * OUTPUT: bool; Was the side strip started to scroll in the desired direction? * * * * WARNINGS: none * * * * HISTORY: * * 12/31/1994 JLB : Created. * * 07/29/1995 JLB : Simplified scrolling logic. * *=============================================================================================*/ bool SidebarClass::StripClass::Scroll(bool up) { if (up) { if (!TopIndex) return(false); Scroller--; } else { if (TopIndex+MAX_VISIBLE >= BuildableCount) return(false); Scroller++; } return(true); } /*********************************************************************************************** * SidebarClass::StripClass::Flag_To_Redra -- Flags the sidebar strip to be redrawn. * * * * This utility routine is called when something changes on the sidebar and it must be * * reflected the next time drawing is performed. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 05/18/1995 JLB : Created. * *=============================================================================================*/ void SidebarClass::StripClass::Flag_To_Redraw(void) { IsToRedraw = true; //Map.SidebarClass::IsToRedraw = true; Map.Flag_To_Redraw(false); } /*********************************************************************************************** * SidebarClass::StripClass::AI -- Input and AI processing for the side strip. * * * * The side strip AI processing is performed by this function. This function not only * * checks for player input, but also handles any graphic logic updating necessary as a * * result of flashing or construction animation. * * * * INPUT: input -- The player input code. * * * * x,y -- Mouse coordinate to use. * * * * OUTPUT: bool; Did the AI detect that it will need a rendering change? If this routine * * returns true, then the Draw_It function should be called at the * * earliest opportunity. * * * * WARNINGS: none * * * * HISTORY: * * 12/31/1994 JLB : Created. * * 12/31/1994 JLB : Uses mouse coordinate parameters. * *=============================================================================================*/ bool SidebarClass::StripClass::AI(KeyNumType & input, int , int ) { bool redraw = false; /* ** If this is scroll button for this side strip, then scroll the strip as ** indicated. */ if (input == (UpButton[ID].ID|KN_BUTTON)) { // && !IsScrolling UpButton[ID].IsPressed = false; if (!Scroll(true)) { Sound_Effect(VOC_SCOLD); } } if (input == (DownButton[ID].ID|KN_BUTTON)) { // && !IsScrolling DownButton[ID].IsPressed = false; if (!Scroll(false)) { Sound_Effect(VOC_SCOLD); } } /* ** Reflect the scroll desired direction/value into the scroll ** logic handler. This might result in up or down scrolling. */ if (!IsScrolling && Scroller) { if (BuildableCount <= MAX_VISIBLE) { Scroller = 0; } else { /* ** Top of list is moving toward lower ordered entries in the object list. It looks like ** the "window" to the object list is moving up even though the actual object images are ** scrolling downward. */ if (Scroller < 0) { if (!TopIndex) { Scroller = 0; } else { Scroller++; IsScrollingDown = false; IsScrolling = true; TopIndex--; Slid = 0; } } else { if (TopIndex+MAX_VISIBLE >= BuildableCount) { Scroller = 0; } else { Scroller--; Slid = OBJECT_HEIGHT; IsScrollingDown = true; IsScrolling = true; } } } } /* ** Scroll logic is handled here. */ if (IsScrolling) { if (IsScrollingDown) { Slid -= SCROLL_RATE; if (Slid <= 0) { IsScrolling = false; Slid = 0; TopIndex++; } } else { Slid += SCROLL_RATE; if (Slid >= OBJECT_HEIGHT) { IsScrolling = false; Slid = 0; } } redraw = true; } /* ** Handle any flashing logic. Flashing occurs when the player selects an object ** and provides the visual feedback of a recognized and legal selection. */ if (Flasher != -1) { if (Graphic_Logic()) { redraw = true; if (Fetch_Stage() >= 7) { Set_Rate(0); Set_Stage(0); Flasher = -1; } } } /* ** Handle any building clock animation logic. */ if (IsBuilding) { for (int index = 0; index < BuildableCount; index++) { int factoryid = Buildables[index].Factory; if (factoryid != -1) { FactoryClass * factory = Factories.Raw_Ptr(factoryid); if (factory && (factory->Has_Changed() || factory->Is_Blocked())) { redraw = true; if (factory->Has_Completed()) { /* ** Construction has been completed. Announce this fact to the player and ** try to get the object to automatically leave the factory. Buildings are ** the main exception to the ability to leave the factory under their own ** power. */ TechnoClass * pending = factory->Get_Object(); if (pending != NULL) { switch (pending->What_Am_I()) { case RTTI_VESSEL: case RTTI_UNIT: case RTTI_AIRCRAFT: OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); if (!factory->Is_Blocked()) { Speak(VOX_UNIT_READY); } break; case RTTI_BUILDING: Speak(VOX_CONSTRUCTION); break; case RTTI_INFANTRY: OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); if (!factory->Is_Blocked()) { Speak(VOX_UNIT_READY); } break; } } } } } } } /* ** If any of the logic determined that this side strip needs to be redrawn, then ** set the redraw flag for this side strip. */ if (redraw) { Flag_To_Redraw(); } return(redraw); } /*********************************************************************************************** * SidebarClass::StripClass::Draw_It -- Render the sidebar display. * * * * Use this routine to render the sidebar display. It checks to see if it needs to be * * redrawn and only redraw if necessary. If the "complete" parameter is true, then it * * will force redraw the entire strip. * * * * INPUT: complete -- Should the redraw be forced? A force redraw will ignore the redraw * * flag. * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 12/31/1994 JLB : Created. * * 08/06/1995 JLB : Handles multi factory tracking in same strip. * *=============================================================================================*/ void SidebarClass::StripClass::Draw_It(bool complete) { if (IsToRedraw || complete) { IsToRedraw = false; if (RunningAsDLL) { return; } SidebarRedraws++; /* ** Fills the background to the side strip. We shouldnt need to do this if the strip ** has a full complement of icons. */ /* ** New sidebar needs to be drawn not filled */ if (BuildableCount < MAX_VISIBLE) { CC_Draw_Shape(LogoShapes, ID, X+(2*RESFACTOR), Y, WINDOW_MAIN, SHAPE_WIN_REL|SHAPE_NORMAL, 0); } /* ** Redraw the scroll buttons. */ UpButton[ID].Draw_Me(true); DownButton[ID].Draw_Me(true); /* ** Loop through all the buildable objects that are visible in the strip and render ** them. Their Y offset may be adjusted if the strip is in the process of scrolling. */ for (int i = 0; i < MAX_VISIBLE + (IsScrolling ? 1 : 0); i++) { bool production; bool completed; int stage; bool darken = false; void const * shapefile = 0; int shapenum = 0; void const * remapper = 0; FactoryClass * factory = 0; int index = i+TopIndex; int x = X; int y = Y + (i*OBJECT_HEIGHT * RESFACTOR); /* ** If the strip is scrolling, then the offset is adjusted accordingly. */ if (IsScrolling) { y -= (OBJECT_HEIGHT - Slid) * RESFACTOR; // y -= OBJECT_HEIGHT - Slid; } /* ** Fetch the shape number for the object type located at this current working ** slot. This shape pointer is used to draw the underlying graphic there. */ if (index < BuildableCount) { ObjectTypeClass const * obj = NULL; SpecialWeaponType spc = SPC_NONE; if (Buildables[index].BuildableType != RTTI_SPECIAL) { obj = Fetch_Techno_Type(Buildables[index].BuildableType, Buildables[index].BuildableID); if (obj != NULL) { /* ** Fetch the remap table that is appropriate for this object ** type. */ remapper = PlayerPtr->Remap_Table(false, ((TechnoTypeClass const *)obj)->Remap); /* ** If there is already a factory producing this kind of object, then all ** objects of this type are displays in a disabled state. */ bool isbusy = (PlayerPtr->Fetch_Factory(Buildables[index].BuildableType) != NULL); if (!isbusy && PlayerPtr->Is_Hack_Prevented(Buildables[index].BuildableType, Buildables[index].BuildableID)) { isbusy = true; } /* ** Infantry don't get remapped in the sidebar (special case). */ if (Buildables[index].BuildableType == RTTI_INFANTRYTYPE) { remapper = 0; } shapefile = obj->Get_Cameo_Data(); shapenum = 0; if (Buildables[index].Factory != -1) { factory = Factories.Raw_Ptr(Buildables[index].Factory); production = true; completed = factory->Has_Completed(); stage = factory->Completion(); darken = false; } else { production = false; // darken = IsBuilding; /* ** Darken the imagery if a factory of a matching type is ** already busy. */ darken = isbusy; } } else { darken = PlayerPtr->Is_Hack_Prevented(Buildables[index].BuildableType, Buildables[index].BuildableID); } } else { spc = SpecialWeaponType(Buildables[index].BuildableID); shapefile = Get_Special_Cameo(spc); shapenum = 0; production = true; completed = PlayerPtr->SuperWeapon[spc].Is_Ready(); stage = PlayerPtr->SuperWeapon[spc].Anim_Stage(); darken = false; } if (obj != NULL || spc != SPC_NONE) { /* ** If this item is flashing then take care of it. ** */ if (Flasher == index && (Fetch_Stage() & 0x01)) { remapper = Map.FadingLight; } } else { shapefile = LogoShapes; if (!darken) { shapenum = SB_BLANK; } } } else { shapefile = LogoShapes; shapenum = SB_BLANK; production = false; } remapper = 0; /* ** Now that the shape of the object at the current working slot has been found, ** draw it and any graphic overlays as necessary. ** ** Don't draw blank shapes over the new 640x400 sidebar art - ST 5/1/96 6:01PM */ if (shapenum != SB_BLANK || shapefile != LogoShapes) { CC_Draw_Shape(shapefile, shapenum, x-(WindowList[WINDOW_SIDEBAR][WINDOWX])+(LEFT_EDGE_OFFSET * RESFACTOR), y-WindowList[WINDOW_SIDEBAR][WINDOWY], WINDOW_SIDEBAR, SHAPE_NORMAL|SHAPE_WIN_REL| (remapper ? SHAPE_FADING : SHAPE_NORMAL), remapper); /* ** Darken this object because it cannot be produced or is otherwise ** unavailable. */ if (darken) { CC_Draw_Shape(ClockShapes, 0, x-(WindowList[WINDOW_SIDEBAR][WINDOWX])+(LEFT_EDGE_OFFSET * RESFACTOR), y-WindowList[WINDOW_SIDEBAR][WINDOWY], WINDOW_SIDEBAR, SHAPE_NORMAL|SHAPE_WIN_REL|SHAPE_GHOST, NULL, ClockTranslucentTable); } } /* ** Draw the overlapping clock shape if this is object is being constructed. ** If the object is completed, then display "Ready" with no clock shape. */ if (production) { if (completed) { /* ** Display text showing that the object is ready to place. */ CC_Draw_Shape(ObjectTypeClass::PipShapes, PIP_READY, (x-(WindowList[WINDOW_SIDEBAR][WINDOWX]))+(LEFT_EDGE_OFFSET+15) * RESFACTOR, (y-WindowList[WINDOW_SIDEBAR][WINDOWY])+(4 * RESFACTOR), WINDOW_SIDEBAR, SHAPE_CENTER); } else { CC_Draw_Shape(ClockShapes, stage+1, x-(WindowList[WINDOW_SIDEBAR][WINDOWX])+(LEFT_EDGE_OFFSET * RESFACTOR), y-WindowList[WINDOW_SIDEBAR][WINDOWY], WINDOW_SIDEBAR, SHAPE_NORMAL|SHAPE_WIN_REL|SHAPE_GHOST, NULL, ClockTranslucentTable); /* ** Display text showing that the construction is temporarily on hold. */ if (factory && !factory->Is_Building()) { CC_Draw_Shape(ObjectTypeClass::PipShapes, PIP_HOLDING, (x-(WindowList[WINDOW_SIDEBAR][WINDOWX])) + ((LEFT_EDGE_OFFSET+15) * RESFACTOR), (y-WindowList[WINDOW_SIDEBAR][WINDOWY])+(4 * RESFACTOR), WINDOW_SIDEBAR, SHAPE_CENTER); } } } } LastSlid = Slid; } } /*********************************************************************************************** * SidebarClass::StripClass::Recalc -- Revalidates the current sidebar list of objects. * * * * This routine will revalidate all the buildable objects in the sidebar. This routine * * comes in handy when a factory has been destroyed, and the sidebar needs to reflect any * * change that this requires. It checks every object to see if there is a factory available * * that could produce it. If none can be found, then the object is removed from the * * sidebar. * * * * INPUT: none * * * * OUTPUT: bool; The sidebar has changed as a result of this call? * * * * WARNINGS: none * * * * HISTORY: * * 01/19/1995 JLB : Created. * * 06/26/1995 JLB : Doesn't collapse sidebar when buildables removed. * *=============================================================================================*/ bool SidebarClass::StripClass::Recalc(void) { int ok; if (Debug_Map || !BuildableCount) { return(false); } /* ** Sweep through all objects listed in the sidebar. If any of those object can ** not be created -- even in theory -- then they must be removed form the sidebar and ** any current production must be abandoned. */ bool redraw = false; for (int index = 0; index < BuildableCount; index++) { TechnoTypeClass const * tech = Fetch_Techno_Type(Buildables[index].BuildableType, Buildables[index].BuildableID); if (tech != NULL) { ok = tech->Who_Can_Build_Me(true, false, PlayerPtr->Class->House) != NULL; } else { if ((unsigned)Buildables[index].BuildableID < SPC_COUNT) { ok = PlayerPtr->SuperWeapon[Buildables[index].BuildableID].Is_Present(); } else { ok = false; } } if (!ok) { /* ** Removes this entry from the list. */ if (BuildableCount > 1 && index < BuildableCount-1) { memcpy(&Buildables[index], &Buildables[index+1], sizeof(Buildables[0])*((BuildableCount-index)-1)); } TopIndex = 0; IsToRedraw = true; redraw = true; BuildableCount--; index--; } } #ifdef NEVER /* ** If there are no more buildable objects to display, make the sidebar go away. */ if (!BuildableCount) { Map.SidebarClass::Activate(0); } #endif return(redraw); } /*********************************************************************************************** * SidebarClass::StripClass::SelectClass::SelectClass -- Default constructor. * * * * This is the default constructor for the button that controls the buildable cameos on * * the sidebar strip. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: The coordinates are set to zero by this routine. They must be set to the * * correct values before this button will function. * * * * HISTORY: * * 01/19/1995 JLB : Created. * *=============================================================================================*/ SidebarClass::StripClass::SelectClass::SelectClass(void) : ControlClass(0, 0, 0, (OBJECT_WIDTH-1) * RESFACTOR, OBJECT_HEIGHT * RESFACTOR, LEFTPRESS|RIGHTPRESS|LEFTUP), Strip(0), Index(0) { } /*********************************************************************************************** * SidebarClass::StripClass::SelectClass:: -- Assigns special values to a buildable select but * * * * Use this routine to set custom buildable vars for this particular select button. It * * uses this information to properly know what buildable object to start or stop production * * on. * * * * INPUT: strip -- Reference to the strip that owns this buildable button. * * * * index -- The index (0 .. MAX_VISIBLE-1) of this button. This is used to let * * the owning strip know what index this button refers to. * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 01/19/1995 JLB : Created. * *=============================================================================================*/ void SidebarClass::StripClass::SelectClass::Set_Owner(StripClass & strip, int index) { Strip = &strip; Index = index; X = strip.X; Y = strip.Y + ((index * OBJECT_HEIGHT) * RESFACTOR); } /*********************************************************************************************** * SidebarClass::StripClass::SelectClass:: -- Action function when buildable cameo is selected * * * * This function is called when the buildable icon (cameo) is clicked on. It handles * * starting and stopping production as indicated. * * * * INPUT: flags -- The input event that triggered the call. * * * * key -- The keyboard value at the time of the input. * * * * OUTPUT: Returns with whether the input list should be scanned further. * * * * WARNINGS: none * * * * HISTORY: * * 01/19/1995 JLB : Created. * * 10/09/1996 JLB : Sonar pulse converted to regular event type. * *=============================================================================================*/ int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & key) { int index = Strip->TopIndex + Index; RTTIType otype = Strip->Buildables[index].BuildableType; int oid = Strip->Buildables[index].BuildableID; int fnumber = Strip->Buildables[index].Factory; RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); ObjectTypeClass const * choice = NULL; SpecialWeaponType spc = SPC_NONE; /* ** Determine the factory number that would apply to objects of the type ** the mouse is currently addressing. This doesn't mean that the factory number ** fetched is actually producing the indicated object, merely that that particular ** kind of factory is specified by the "genfactory" value. This can be used to see ** if the factory type is currently busy or not. */ FactoryClass * factory = PlayerPtr->Fetch_Factory(otype); Map.Override_Mouse_Shape(MOUSE_NORMAL); if (index < Strip->BuildableCount) { if (otype != RTTI_SPECIAL) { choice = Fetch_Techno_Type(otype, oid); } else { spc = SpecialWeaponType(oid); } if (fnumber != -1) { factory = Factories.Raw_Ptr(fnumber); } } else { Map.Help_Text(TXT_NONE); } if (spc != SPC_NONE) { /* ** Display the help text if the mouse is over the button. */ if (flags & LEFTUP) { Map.Help_Text(SpecialWeaponHelp[spc], X, Y, scheme->Color, true); flags &= ~LEFTUP; } /* ** A right mouse button signals "cancel". If we are in targeting ** mode then we don't want to be any more. */ if (flags & RIGHTPRESS) { Map.IsTargettingMode = SPC_NONE; } /* ** A left mouse press signal "activate". If our weapon type is ** available then we should activate it. */ if (flags & LEFTPRESS) { if ((unsigned)spc < SPC_COUNT) { if (PlayerPtr->SuperWeapon[spc].Is_Ready()) { if (spc != SPC_SONAR_PULSE) { Map.IsTargettingMode = spc; Unselect_All(); Speak(VOX_SELECT_TARGET); } else { OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_SONAR_PULSE, 0)); } } else { PlayerPtr->SuperWeapon[spc].Impatient_Click(); } } } } else { if (choice != NULL) { /* ** Display the help text if the mouse is over the button. */ if (flags & LEFTUP) { Map.Help_Text(choice->Full_Name(), X, Y, scheme->Color, true); Map.Set_Cost(choice->Cost_Of() * PlayerPtr->CostBias); flags &= ~LEFTUP; } /* ** A right mouse button signals "cancel". */ if (flags & RIGHTPRESS) { /* ** If production is in progress, put it on hold. If production is already ** on hold, then abandon it. Money will be refunded, the factory ** manager deleted, and the object under construction is returned to ** the free pool. */ if (factory != NULL) { /* ** Cancels placement mode if the sidebar factory is abandoned or ** suspended. */ if (Map.PendingObjectPtr && Map.PendingObjectPtr->Is_Techno()) { Map.PendingObjectPtr = 0; Map.PendingObject = 0; Map.PendingHouse = HOUSE_NONE; Map.Set_Cursor_Shape(0); } if (!factory->Is_Building()) { Speak(VOX_CANCELED); OutList.Add(EventClass(EventClass::ABANDON, otype, oid)); } else { Speak(VOX_SUSPENDED); OutList.Add(EventClass(EventClass::SUSPEND, otype, oid)); Map.Column[0].IsToRedraw = true; Map.Column[1].IsToRedraw = true; } } } if (flags & LEFTPRESS) { /* ** If there is already a factory attached to this strip but the player didn't click ** on the icon that has the attached factory, then say that the factory is busy and ** ignore the click. */ if (fnumber == -1 && factory != NULL) { Speak(VOX_NO_FACTORY); ControlClass::Action(flags, key); return(true); } if (factory != NULL) { /* ** If this object is currently being built, then give a scold sound and text and then ** bail. */ if (factory->Is_Building()) { Speak(VOX_NO_FACTORY); } else { /* ** If production has completed, then attempt to have the object exit ** the factory or go into placement mode. */ if (factory->Has_Completed()) { TechnoClass * pending = factory->Get_Object(); if (!pending && factory->Get_Special_Item()) { Map.IsTargettingMode = SPC_ANY; } else { BuildingClass * builder = pending->Who_Can_Build_Me(false, false); if (!builder) { OutList.Add(EventClass(EventClass::ABANDON, otype, oid)); Speak(VOX_NO_FACTORY); } else { /* ** If the completed object is a building, then change the ** game state into building placement mode. This fact is ** not transmitted to any linked computers until the moment ** the building is actually placed down. */ if (pending->What_Am_I() == RTTI_BUILDING) { PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); } else { /* ** For objects that can leave the factory under their own ** power, queue this event and process through normal house ** production channels. */ OutList.Add(EventClass(EventClass::PLACE, otype, -1)); } } } } else { if (PlayerPtr->Is_Hack_Prevented(otype, oid)) { // Eva scolds the player here. } else { /* ** The factory must have been in a suspended state. Resume construction ** normally. */ if (otype == RTTI_INFANTRYTYPE) { Speak(VOX_TRAINING); } else { Speak(VOX_BUILDING); } OutList.Add(EventClass(EventClass::PRODUCE, Strip->Buildables[index].BuildableType, Strip->Buildables[index].BuildableID)); } } } } else { if (PlayerPtr->Is_Hack_Prevented(otype, oid)) { // Eva scolds the player here. } else { /* ** If this side strip is already busy with production, then ignore the ** input and announce this fact. */ if (otype == RTTI_INFANTRYTYPE) { Speak(VOX_TRAINING); } else { Speak(VOX_BUILDING); } OutList.Add(EventClass(EventClass::PRODUCE, Strip->Buildables[index].BuildableType, Strip->Buildables[index].BuildableID)); } } } } else { flags = 0; } } ControlClass::Action(flags, key); return(true); } /*********************************************************************************************** * SidebarClass::SBGadgetClass::Action -- Special function that controls the mouse over the si * * * * This routine is called whenever the mouse is over the sidebar. It makes sure that the * * mouse is always the normal shape while over the sidebar. * * * * INPUT: flags -- The event flags that resulted in this routine being called. * * * * key -- Reference the keyboard code that may be present. * * * * OUTPUT: Returns that no further keyboard processing is necessary. * * * * WARNINGS: none * * * * HISTORY: * * 03/28/1995 JLB : Created. * *=============================================================================================*/ int SidebarClass::SBGadgetClass::Action(unsigned , KeyNumType & ) { Map.Help_Text(TXT_NONE); Map.Override_Mouse_Shape(MOUSE_NORMAL, false); return(true); } /*********************************************************************************************** * SidebarClass::StripClass::Factory_Link -- Links a factory to a sidebar button. * * * * This routine will link the specified factory to this sidebar strip. The exact button to * * link to is determined from the object type and id specified. A linked button is one that * * will show appropriate construction animation (clock shape) that matches the state of * * the factory. * * * * INPUT: factory -- The factory number to link to the sidebar. * * * * type -- The object type that this factory refers to. * * * * id -- The object sub-type that this factory refers to. * * * * OUTPUT: Was the factory successfully attached? Failure would indicate that there is no * * object of the specified type and sub-type in the sidebar list. * * * * WARNINGS: none * * * * HISTORY: * * 05/18/1995 JLB : Created. * *=============================================================================================*/ bool SidebarClass::StripClass::Factory_Link(int factory, RTTIType type, int id) { for (int index = 0; index < BuildableCount; index++) { if (Buildables[index].BuildableType == type && Buildables[index].BuildableID == id) { Buildables[index].Factory = factory; IsBuilding = true; /* ** Flag that all the icons on this strip need to be redrawn */ Flag_To_Redraw(); return(true); } } return(false); } /*********************************************************************************************** * SidebarClass::Abandon_Production -- Stops production of the object specified. * * * * This routine is used to abandon production of the object specified. The factory will * * be completely disabled by this call. * * * * INPUT: type -- The object type that is to be abandoned. The sub-type is not needed * * since it is presumed there can be only one type in production at any * * one time. * * * * factory -- The factory number that is doing the production. * * * * OUTPUT: Was the factory successfully abandoned? * * * * WARNINGS: none * * * * HISTORY: * * 05/18/1995 JLB : Created. * *=============================================================================================*/ bool SidebarClass::Abandon_Production(RTTIType type, int factory) { return(Column[Which_Column(type)].Abandon_Production(factory)); } /*********************************************************************************************** * SidebarClass::StripClass::Abandon_Produ -- Abandons production associated with sidebar. * * * * Production of the object associated with this sidebar is abandoned when this routine is * * called. * * * * INPUT: factory -- The factory index that is to be suspended. * * * * OUTPUT: Was the production abandonment successful? * * * * WARNINGS: none * * * * HISTORY: * * 05/18/1995 JLB : Created. * * 08/06/1995 JLB : More intelligent abandon logic for multiple factories. * *=============================================================================================*/ bool SidebarClass::StripClass::Abandon_Production(int factory) { bool noprod = true; bool abandon = false; for (int index = 0; index < BuildableCount; index++) { if (Buildables[index].Factory == factory) { Factories.Raw_Ptr(factory)->Abandon(); Buildables[index].Factory = -1; abandon = true; } else { if (Buildables[index].Factory != -1) { noprod = false; } } } /* ** If there was a change to the strip, then flag the strip to be redrawn. */ if (abandon) { Flag_To_Redraw(); } /* ** If there is no production whatsoever on this strip, then flag it so. */ if (noprod) { IsBuilding = false; } return(abandon); } /*********************************************************************************************** * SidebarClass::Zoom_Mode_Control -- Handles the zoom mode toggle operation. * * * * This is the function that is called when the map button is pressed. It will toggle * * between the different modes that the radar map can assume. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 07/31/1996 JLB : Created. * *=============================================================================================*/ void SidebarClass::Zoom_Mode_Control(void) { #ifdef WIN32 /* ** If radar is active, cycle as follows: ** Zoomed => not zoomed ** not zoomed => player status (multiplayer only) ** player status => radar spying readout ** radar spying readout => zoomed */ if (IsRadarActive) { if (Is_Zoomed() || Session.Type==GAME_NORMAL) { if (Is_Zoomed() || !Spy_Next_House()) { Zoom_Mode(Coord_Cell(TacticalCoord)); } } else { if (!Spying_On_House() && !Is_Player_Names()) { Player_Names(1); } else { Player_Names(0); if (!Spy_Next_House()) { Zoom_Mode(Coord_Cell(TacticalCoord)); } } } } else { if (Session.Type!=GAME_NORMAL) { Player_Names(Is_Player_Names()==0); } } #else /* ** If radar is active, cycle as follows: ** not zoomed => player status (multiplayer only) ** player status => radar spying readout ** radar spying readout => not zoomed */ if (IsRadarActive) { if (Session.Type==GAME_NORMAL) { if (!Spy_Next_House()) { Zoom_Mode(Coord_Cell(TacticalCoord)); } } else { if (!Spying_On_House() && !Is_Player_Names()) { Player_Names(1); } else { Player_Names(0); if (!Spy_Next_House()) { Zoom_Mode(Coord_Cell(TacticalCoord)); } } } } else { if (Session.Type!=GAME_NORMAL) { Player_Names(Is_Player_Names()==0); } } #endif }