/*============================================================================= Name : ConsMgr.c Purpose : Logic for the Construction Manager Created 7/18/1997 by lmoloney Copyright Relic Entertainment, Inc. All rights reserved. =============================================================================*/ #define WIN32_LEAN_AND_MEAN #include #include #include "types.h" #include "linkedlist.h" #include "universe.h" #include "region.h" #include "uicontrols.h" #include "feflow.h" #include "font.h" #include "fontreg.h" #include "objtypes.h" #include "task.h" #include "mouse.h" #include "MultiplayerGame.h" #include "CommandLayer.h" #include "piePlate.h" #include "ConsMgr.h" #include "globals.h" #include "CommandWrap.h" #include "scroller.h" #include "soundevent.h" #include "randy.h" #include "strings.h" #include "researchapi.h" #include "mainrgn.h" #include "taskbar.h" #include "shipview.h" #include "glinc.h" #include "glcaps.h" #include "render.h" #include "mainrgn.h" #include "sensors.h" #include "singleplayer.h" #include "ping.h" #include "texreg.h" #include "fereg.h" #include "shipdefs.h" #include "nis.h" #include "tutor.h" #include "options.h" #include "FEColour.h" #include "glcompat.h" #include "infooverlay.h" #include "SaveGame.h" /*============================================================================= Definitions: =============================================================================*/ #define CM_FIBFile "FEMan\\Construction_Manager.fib" #define CM_ConstructionScreen "Construction_manager" //#define CM_BuildScreen "Build_screen" #define CM_DefaultFont "default.hff" #define CM_ShipListFont "HW_EuroseCond_11.hff" #define CM_FontNameLength 64 #define CM_HeadingColorFactor 1.5f //margins for printing RU's #define CM_RUMarginRight 2 #define CM_RUMarginTop 2 //margins for available ships #define CM_ASHeadingMarginLeft 3 #define CM_ASMarginLeft 9 #define CM_ASMarginTop 2 #define CM_ASMarginRight 2 #define CM_ASInterSpacing 2 #define CM_NumberJobsPerShip 128 #define CM_NumberJobsPerClass 10 #define CM_PausedColor colRGB(80,120,80) #define CM_SelectedColorSolid colRGB(60,030,0) #define CM_SelectedColorOutline colRGB(120,070,0) #define CM_BackgroundColor colRGB(0,35,79) #define CM_SelectBoxX0 0 #define CM_SelectBoxX1 190 #define CM_SelectBoxY0 0 #define CM_SelectBoxY1 12 sdword cmSelectTopAdjust = 0; sdword cmSelectBottomAdjust = 0; sdword cmSelectLeftAdjust = 0; sdword cmSelectRightAdjust = 0; #define CM_FIVEKEY CONTROLKEY //hold to increase/decrease by 5 //task parameters #define CM_TaskStackSize 4096 #define CM_TaskServicePeriod UNIVERSE_UPDATE_PERIOD //parameter defaults #define CM_SelectionTextColor colRGB(255, 255, 105) #define CM_BuildingTextColor colRGB(0, 255, 0) #define CM_SelectionRectColor colRGB(50, 120, 20) #define CM_ProgressShipColor colRGB(100, 100, 55) #define CM_ProgressTotalColor colRGB(180, 180, 80) #define CM_StandardTextColor colRGB(0,110,172) #define CM_ClassHeadingTextColor colRGB(255, 200, 0) #define CM_CapsExceededTextColor colRGB(255,25,25) #define CM_RefundRatio 39321 #define CM_FloatRefundRatio 1.0f // defines for shipavailable status word #define ITEM_CLASS 0 #define ITEM_SHIP 1 #define STAT_CANBUILD 0 #define STAT_CANTBUILD 1 #define STAT_PRINT 2 #define STAT_DONTPRINT 3 // frame count for red flash when ship limits exceeded #define CAPS_REDFRAMECOUNT 5; //margins for printing construction progress #define CM_ProgressMarginLeft 2 #define CM_ProgressMarginRight 90 #define CM_ProgressMarginTop 2 #define CM_ProgressInterSpacing 2 #define CM_SHIP_IMAGE_INSET 4 #define NUM_CMTEXTURES 2 #define NUM_CMCARRIERS 4 #define UNIVERSE_TURBO_REPEAT 19 #define CM_MaxJobsMothership 2 #define CM_MaxJobsHeavyCruiser 2 #define CM_MaxJobsCarrier 2 #define CM_MaxJobsDestroyer 2 #define CM_MaxJobsFrigate 2 #define CM_MaxJobsCorvette 2 #define CM_MaxJobsFighter 3 #define CM_MaxJobsResource 2 #define CM_MaxJobsNonCombat 2 /*============================================================================= Data: =============================================================================*/ static sdword cmRenderEverythingCounter; bool cmPaletted; #if CM_CHEAP_SHIPS bool cmCheapShips = FALSE; #endif void cmDeterministicStartup(void); void cmDeterministicShutdown(void); //callback tables for this screen void cmClose(char *string, featom *atom); void cmCancelJobs(char *string, featom *atom); void cmPauseJobs(char *string, featom *atom); void cmBuildShips(char *string, featom *atom); void cmNumberRUsDraw(featom *atom, regionhandle region); void cmShipCostsDraw(featom *atom, regionhandle region); void cmShipInfoDraw(featom *atom, regionhandle region); void cmShipNumbersDraw(featom *atom, regionhandle region); void cmTotalRUsDraw(featom *atom, regionhandle region); void cmTotalShipsDraw(featom *atom, regionhandle region); void cmConnectorDraw(featom *atom, regionhandle region); void cmMotherShipDraw(featom *atom, regionhandle region); void cmCarrier1Draw(featom *atom, regionhandle region); void cmCarrier2Draw(featom *atom, regionhandle region); void cmCarrier3Draw(featom *atom, regionhandle region); void cmCarrier4Draw(featom *atom, regionhandle region); void cmLeftArrowDraw(featom *atom, regionhandle region); void cmRightArrowDraw(featom *atom, regionhandle region); void cmScroller(char *string, featom *atom); void cmFillInCarrierXs(void); ShipType cmKeyToShipType(uword key); LinkedList listofShipsInProgress; fecallback cmCallback[] = { {cmClose, "CM_Close" }, {cmBuildShips, "CM_BuildShips" }, {cmCancelJobs, "CM_CancelJobs" }, {cmPauseJobs, "CM_PauseJobs" }, {cmScroller, "CM_Scroller" }, {NULL, NULL } }; fedrawcallback cmDrawCallback[] = { {cmNumberRUsDraw, "CM_RUs" }, {cmShipCostsDraw, "CM_ShipCost" }, {cmShipInfoDraw, "CM_Ships" }, {cmShipNumbersDraw, "CM_ShipNumbers" }, {cmTotalRUsDraw, "CM_TotalCost" }, {cmTotalShipsDraw, "CM_TotalShips" }, {cmConnectorDraw, "CM_ConnectorDraw" }, {cmMotherShipDraw, "CM_MotherShipDraw" }, {cmCarrier1Draw, "CM_Carrier1Draw" }, {cmCarrier2Draw, "CM_Carrier2Draw" }, {cmCarrier3Draw, "CM_Carrier3Draw" }, {cmCarrier4Draw, "CM_Carrier4Draw" }, {cmLeftArrowDraw, "CM_LeftArrowDraw" }, {cmRightArrowDraw, "CM_RightArrowDraw" }, {NULL, NULL } }; extern ShipType svShipType; KeysToShips cmShipTypes[]= { // {AKEY|CM_SHIFT, AdvanceSupportFrigate}, {F5KEY, AttackBomber}, // {KKEY, Carrier}, {F4KEY, CloakedFighter}, // {LKEY|CM_SHIFT, CloakGenerator}, // {UKEY|CM_SHIFT, DDDFrigate}, {F4KEY, DefenseFighter}, // {UKEY, DFGFrigate}, // {GKEY, GravWellGenerator}, {F7KEY, HeavyCorvette}, // {ZKEY, HeavyCruiser}, {F1KEY, HeavyDefender}, {F3KEY, HeavyInterceptor}, // {OKEY, IonCannonFrigate}, {F6KEY, LightCorvette}, // {DKEY, LightDefender}, {F2KEY, LightInterceptor}, {F10KEY, MinelayerCorvette}, // {MKEY|CM_SHIFT, MissileDestroyer}, //{KEY , Mothership}, {F11KEY, MultiGunCorvette}, // {WKEY, Probe}, // {XKEY|CM_SHIFT, ProximitySensor}, {F8KEY, RepairCorvette}, // {XKEY, ResearchShip}, {F12KEY, ResourceCollector}, // {RKEY|CM_SHIFT, ResourceController}, {F9KEY, SalCapCorvette}, // {SKEY|CM_SHIFT, SensorArray}, // {YKEY, StandardDestroyer}, // {EKEY, StandardFrigate}, // {TKEY, Drone}, // {TKEY|CM_SHIFT, TargetDrone}, /*{KEY , P1Fighter}, {KEY , P1IonArrayFrigate}, {KEY , P1MissileCorvette}, {KEY , P1Mothership}, {KEY , P1StandardCorvette}, {KEY , P2AdvanceSwarmer}, {KEY , P2FuelPod}, {KEY , P2Mothership}, {KEY , P2MultiBeamFrigate}, {KEY , P2Swarmer}, {KEY , P3Destroyer}, {KEY , P3Frigate}, {KEY , P3Megaship}, {KEY , FloatingCity}, {KEY , CargoBarge}, {KEY , MiningBase}, {KEY , ResearchStation}, {KEY , JunkYardDawg} {KEY , JunkYardHQ} */ }; bool cmPrintHotKey = FALSE; //index of first displayed element sword cmUpperIndex = 0; //maximum number of displayable elements in our list uword cmMaxIndex = 18; //For drawing accelerator arrows sdword cmArrowIndex = -1; static scrollbarhandle g_shandle; //flag indicating loaded status of this screen fibfileheader *cmScreensHandle = NULL; //handle to construction task taskhandle cmBuildTask = -1; #define NSHIPS_AVAILABLE_R1REG 22 + 5 // Modified by Daly on 04.21.99 (was 20 + 5) #define NSHIPS_AVAILABLE_R1BIG 27 + 6 #define NSHIPS_AVAILABLE_R2REG 22 + 5 // Modified by Daly on 04.21.99 (was 20 + 5) #define NSHIPS_AVAILABLE_R2BIG 27 + 6 #define NSHIPS_AVAILABLE_P1 4 + 3 #define NSHIPS_AVAILABLE_P2 4 + 2 regionhandle cmBaseRegion = NULL; regionhandle cmShipInfoRegion = NULL; regionhandle cmShipNumbersRegion = NULL; regionhandle cmNumberRUsRegion = NULL; regionhandle cmTotalRUsRegion = NULL; regionhandle cmTotalShipsRegion = NULL; regionhandle cmShipCostsRegion = NULL; regionhandle cmConnectorRegion = NULL; regionhandle cmMotherShipRegion = NULL; regionhandle cmCarrierRegions[NUM_CMCARRIERS] = { NULL, NULL, NULL, NULL }; /* typedef struct { sdword disabled; ShipType type; } shipdisabled; //use [0,1], ![FALSE,TRUE] for disabling shipdisabled cmShipsDisabled[] = { {0, AdvanceSupportFrigate}, {1, AttackBomber}, {0, Carrier}, {0, CloakedFighter}, {1, CloakGenerator}, {1, DDDFrigate}, {0, DefenseFighter}, {1, DFGFrigate}, {1, GravWellGenerator}, {0, HeavyCorvette}, {1, HeavyCruiser}, {0, HeavyDefender}, {0, HeavyInterceptor}, {0, IonCannonFrigate}, {0, LightCorvette}, {0, LightDefender}, {0, LightInterceptor}, {1, MinelayerCorvette}, {1, MissileDestroyer}, {1, MultiGunCorvette}, {0, Probe}, {0, ProximitySensor}, {0, RepairCorvette}, {0, ResearchShip}, {0, ResourceCollector}, {0, ResourceController}, {1, SalCapCorvette}, {0, SensorArray}, {0, StandardDestroyer}, {0, StandardFrigate}, {0, Drone}, {-1, 0}, }; */ //ships available for construction shipavailable cmShipsAvailableR1Reg[NSHIPS_AVAILABLE_R1REG + 1] = { {0,ITEM_CLASS,0, CLASS_Fighter , NULL}, {0,ITEM_SHIP ,0, HeavyDefender , NULL}, {0,ITEM_SHIP ,0, LightInterceptor , NULL}, {0,ITEM_SHIP ,0, HeavyInterceptor , NULL}, {0,ITEM_SHIP ,0, CloakedFighter , NULL}, {0,ITEM_SHIP ,0, AttackBomber , NULL}, {0,ITEM_CLASS,0, CLASS_Corvette , NULL}, {0,ITEM_SHIP ,0, LightCorvette , NULL}, {0,ITEM_SHIP ,0, HeavyCorvette , NULL}, {0,ITEM_SHIP ,0, RepairCorvette , NULL}, {0,ITEM_SHIP ,0, SalCapCorvette , NULL}, {0,ITEM_SHIP ,0, MinelayerCorvette , NULL}, {0,ITEM_SHIP ,0, MultiGunCorvette , NULL}, {0,ITEM_CLASS,0, CLASS_Resource , NULL}, {0,ITEM_SHIP ,0, ResourceCollector , NULL}, {0,ITEM_CLASS,0, CLASS_Frigate , NULL}, {0,ITEM_SHIP ,0, StandardFrigate , NULL}, {0,ITEM_SHIP ,0, AdvanceSupportFrigate, NULL}, {0,ITEM_SHIP ,0, DDDFrigate , NULL}, {0,ITEM_SHIP ,0, IonCannonFrigate , NULL}, {0,ITEM_CLASS,0, CLASS_NonCombat , NULL}, {0,ITEM_SHIP ,0, GravWellGenerator , NULL}, // Added by Daly on 04.21.99 {0,ITEM_SHIP ,0, CloakGenerator , NULL}, // Added by Daly on 04.21.99 {0,ITEM_SHIP ,0, Probe , NULL}, {0,ITEM_SHIP ,0, ProximitySensor , NULL}, {0,ITEM_SHIP ,0, SensorArray , NULL}, {0,ITEM_SHIP ,0, ResearchShip , NULL}, {-1,0,0, 0 , NULL} }; shipavailable cmShipsAvailableR1Big[NSHIPS_AVAILABLE_R1BIG + 1] = { {0,ITEM_CLASS,0, CLASS_Fighter , NULL}, {0,ITEM_SHIP ,0, HeavyDefender , NULL}, {0,ITEM_SHIP ,0, LightInterceptor , NULL}, {0,ITEM_SHIP ,0, HeavyInterceptor , NULL}, {0,ITEM_SHIP ,0, CloakedFighter , NULL}, {0,ITEM_SHIP ,0, AttackBomber , NULL}, {0,ITEM_CLASS,0, CLASS_Corvette , NULL}, {0,ITEM_SHIP ,0, LightCorvette , NULL}, {0,ITEM_SHIP ,0, HeavyCorvette , NULL}, {0,ITEM_SHIP ,0, RepairCorvette , NULL}, {0,ITEM_SHIP ,0, SalCapCorvette , NULL}, {0,ITEM_SHIP ,0, MinelayerCorvette , NULL}, {0,ITEM_SHIP ,0, MultiGunCorvette , NULL}, {0,ITEM_CLASS,0, CLASS_Resource , NULL}, {0,ITEM_SHIP ,0, ResourceCollector , NULL}, {0,ITEM_SHIP ,0, ResourceController , NULL}, {0,ITEM_CLASS,0, CLASS_Frigate , NULL}, {0,ITEM_SHIP ,0, StandardFrigate , NULL}, {0,ITEM_SHIP ,0, AdvanceSupportFrigate, NULL}, {0,ITEM_SHIP ,0, DDDFrigate , NULL}, {0,ITEM_SHIP ,0, IonCannonFrigate , NULL}, {0,ITEM_CLASS,0, CLASS_Destroyer , NULL}, {0,ITEM_SHIP ,0, StandardDestroyer , NULL}, {0,ITEM_SHIP ,0, MissileDestroyer , NULL}, {0,ITEM_SHIP ,0, HeavyCruiser , NULL}, {0,ITEM_SHIP ,0, Carrier , NULL}, {0,ITEM_CLASS,0, CLASS_NonCombat , NULL}, {0,ITEM_SHIP ,0, Probe , NULL}, {0,ITEM_SHIP ,0, ProximitySensor , NULL}, {0,ITEM_SHIP ,0, SensorArray , NULL}, {0,ITEM_SHIP ,0, ResearchShip , NULL}, {0,ITEM_SHIP ,0, GravWellGenerator , NULL}, {0,ITEM_SHIP ,0, CloakGenerator , NULL}, {-1,0,0, 0 , NULL} }; shipavailable cmShipsAvailableR2Reg[NSHIPS_AVAILABLE_R2REG + 1] = { {0,ITEM_CLASS,0, CLASS_Fighter , NULL}, {0,ITEM_SHIP ,0, HeavyDefender , NULL}, {0,ITEM_SHIP ,0, LightInterceptor , NULL}, {0,ITEM_SHIP ,0, HeavyInterceptor , NULL}, {0,ITEM_SHIP ,0, DefenseFighter , NULL}, {0,ITEM_SHIP ,0, AttackBomber , NULL}, {0,ITEM_CLASS,0, CLASS_Corvette , NULL}, {0,ITEM_SHIP ,0, LightCorvette , NULL}, {0,ITEM_SHIP ,0, HeavyCorvette , NULL}, {0,ITEM_SHIP ,0, RepairCorvette , NULL}, {0,ITEM_SHIP ,0, SalCapCorvette , NULL}, {0,ITEM_SHIP ,0, MinelayerCorvette , NULL}, {0,ITEM_SHIP ,0, MultiGunCorvette , NULL}, {0,ITEM_CLASS,0, CLASS_Resource , NULL}, {0,ITEM_SHIP ,0, ResourceCollector , NULL}, {0,ITEM_CLASS,0, CLASS_Frigate , NULL}, {0,ITEM_SHIP ,0, StandardFrigate , NULL}, {0,ITEM_SHIP ,0, AdvanceSupportFrigate, NULL}, {0,ITEM_SHIP ,0, DFGFrigate , NULL}, {0,ITEM_SHIP ,0, IonCannonFrigate , NULL}, {0,ITEM_CLASS,0, CLASS_NonCombat , NULL}, {0,ITEM_SHIP ,0, GravWellGenerator , NULL}, // Added by Daly on 04.21.99 {0,ITEM_SHIP ,0, CloakGenerator , NULL}, // Added by Daly on 04.21.99 {0,ITEM_SHIP ,0, Probe , NULL}, {0,ITEM_SHIP ,0, ProximitySensor , NULL}, {0,ITEM_SHIP ,0, SensorArray , NULL}, {0,ITEM_SHIP ,0, ResearchShip , NULL}, {-1,0,0, 0 , NULL} }; shipavailable cmShipsAvailableR2Big[NSHIPS_AVAILABLE_R2BIG + 1] = { {0,ITEM_CLASS,0, CLASS_Fighter , NULL}, {0,ITEM_SHIP ,0, HeavyDefender , NULL}, {0,ITEM_SHIP ,0, LightInterceptor , NULL}, {0,ITEM_SHIP ,0, HeavyInterceptor , NULL}, {0,ITEM_SHIP ,0, DefenseFighter , NULL}, {0,ITEM_SHIP ,0, AttackBomber , NULL}, {0,ITEM_CLASS,0, CLASS_Corvette , NULL}, {0,ITEM_SHIP ,0, LightCorvette , NULL}, {0,ITEM_SHIP ,0, HeavyCorvette , NULL}, {0,ITEM_SHIP ,0, RepairCorvette , NULL}, {0,ITEM_SHIP ,0, SalCapCorvette , NULL}, {0,ITEM_SHIP ,0, MinelayerCorvette , NULL}, {0,ITEM_SHIP ,0, MultiGunCorvette , NULL}, {0,ITEM_CLASS,0, CLASS_Resource , NULL}, {0,ITEM_SHIP ,0, ResourceCollector , NULL}, {0,ITEM_SHIP ,0, ResourceController , NULL}, {0,ITEM_CLASS,0, CLASS_Frigate , NULL}, {0,ITEM_SHIP ,0, StandardFrigate , NULL}, {0,ITEM_SHIP ,0, AdvanceSupportFrigate, NULL}, {0,ITEM_SHIP ,0, DFGFrigate , NULL}, {0,ITEM_SHIP ,0, IonCannonFrigate , NULL}, {0,ITEM_CLASS,0, CLASS_Destroyer , NULL}, {0,ITEM_SHIP ,0, StandardDestroyer , NULL}, {0,ITEM_SHIP ,0, MissileDestroyer , NULL}, {0,ITEM_SHIP ,0, HeavyCruiser , NULL}, {0,ITEM_SHIP ,0, Carrier , NULL}, {0,ITEM_CLASS,0, CLASS_NonCombat , NULL}, {0,ITEM_SHIP ,0, Probe , NULL}, {0,ITEM_SHIP ,0, ProximitySensor , NULL}, {0,ITEM_SHIP ,0, SensorArray , NULL}, {0,ITEM_SHIP ,0, ResearchShip , NULL}, {0,ITEM_SHIP ,0, GravWellGenerator , NULL}, {0,ITEM_SHIP ,0, CloakGenerator , NULL}, {-1,0,0, 0 , NULL} }; shipavailable cmShipsAvailableP1[NSHIPS_AVAILABLE_P1 + 1] = { {0,ITEM_CLASS,0, CLASS_Fighter , NULL}, {0,ITEM_SHIP ,0, P1Fighter , NULL}, {0,ITEM_CLASS,0, CLASS_Corvette , NULL}, {0,ITEM_SHIP ,0, P1StandardCorvette , NULL}, {0,ITEM_SHIP ,0, P1MissileCorvette , NULL}, {0,ITEM_CLASS,0, CLASS_Frigate , NULL}, {0,ITEM_SHIP ,0, P1IonArrayFrigate , NULL}, {-1,0,0,0,NULL} }; shipavailable cmShipsAvailableP2[NSHIPS_AVAILABLE_P2 + 1] = { {0,ITEM_CLASS,0, CLASS_Fighter , NULL}, {0,ITEM_SHIP ,0, P2Swarmer , NULL}, {0,ITEM_SHIP ,0, P2AdvanceSwarmer , NULL}, {0,ITEM_CLASS,0, CLASS_Frigate , NULL}, {0,ITEM_SHIP ,0, P2FuelPod , NULL}, {0,ITEM_SHIP ,0, P2MultiBeamFrigate , NULL}, {-1,0,0,0,NULL} }; shipsinprogress *curshipsInProgress; sdword NUMBER_SHIPS_AVAILABLE = 0; shipavailable *cmShipsAvailable; CarrierInfo *cmCarriers = NULL; sdword cmNumCarriers = 0; sdword cmNumPlayersCarriers = 0; static Ship *cmCarrierX[NUM_CMCARRIERS] = { NULL, NULL, NULL, NULL }; // don't export these for sync reasons //sdword cmSelectedShip = -1; //colors for printing selections and stuff color cmSelectionTextColor = CM_SelectionTextColor; color cmBuildingTextColor = CM_BuildingTextColor; color cmProgressShipColor = CM_ProgressShipColor; color cmProgressTotalColor = CM_ProgressTotalColor; color cmStandardTextColor = CM_StandardTextColor; color cmClassHeadingTextColor = CM_ClassHeadingTextColor; color cmCapsExceededTextColor = CM_CapsExceededTextColor; udword cmRefundRatio = CM_RefundRatio; real32 cmFloatRefundRatio = CM_FloatRefundRatio; bool cmIoSaveState; udword cmShipTexture[MAX_RACES][NUM_CMTEXTURES] = { {TR_InvalidInternalHandle, TR_InvalidInternalHandle}, {TR_InvalidInternalHandle, TR_InvalidInternalHandle} }; lifheader *cmShipImage[MAX_RACES][NUM_CMTEXTURES] = { { NULL, NULL }, { NULL, NULL } }; char *ShipImagePaths[] = { {"FEMan\\Construction_Manager\\Build_race1_"}, {"FEMan\\Construction_Manager\\Build_race2_"} }; char *ShipIcons[] = { {"icon1.lif"}, // Mothership {"icon2.lif"} // Carrier }; udword cmArrowTexture[6] = { TR_InvalidInternalHandle, TR_InvalidInternalHandle,TR_InvalidInternalHandle, TR_InvalidInternalHandle, TR_InvalidInternalHandle,TR_InvalidInternalHandle }; lifheader *cmArrowIcon[6] = { NULL, NULL, NULL, NULL, NULL, NULL }; #define ARROW_ICON_PATH "FEMan\\Textures\\" char *ArrowIcons[] = { {"Build_arrow_left_mouse.lif"}, {"Build_Arrow_Left_on.lif"}, {"Build_Arrow_Left_off.lif"}, {"Build_Arrow_right_mouse.lif"}, {"Build_Arrow_right_on.lif"}, {"Build_Arrow_right_off.lif"}, }; sdword cmCurrentSelect = 0; //font for the ship listing fonthandle cmDefaultFont = 0; fonthandle cmShipListFont = 0; char cmDefaultFontName[CM_FontNameLength] = CM_DefaultFont; char cmShipListFontName[CM_FontNameLength] = CM_ShipListFont; // frame count for red flash when ship limits exceeded #define CAPS_REDFRAMECOUNT 5; // Ship Caps exceeded falgs real32 ExceededCapsFrames = 0.0; uword ShipNumberExceeded = 0; bool8 cmLeftArrowActive = FALSE; bool8 cmRightArrowActive = FALSE; // Variable for storing the last viewed ship ShipType curshipview=DefaultShip; // Hack to fix Johan's hack ... #define CM_WAIT_ACC 0.25f extern real32 mgAccTime; // Fix for unit caps in internet game ... sdword shiplagtotals[TOTAL_NUM_SHIPS]; bool cmReallyPausedAll = FALSE; bool cmActive=FALSE; typedef struct cmDetermProgress_t { Node node; sdword numShips; ShipType shipType; ShipRace shipRace; sdword costPerShip; real32 costPerTick; real32 costSoFar; sdword ticksForOne; sdword ticksLeft; sdword playerIndex; ShipPtr creator; bool paused; } cmDetermProgress_t; LinkedList cmDetermProgress; //max number of ship types per class that can be built sdword cmClassCapRemap[NUM_CLASSES]= { CLASS_Mothership, CLASS_Destroyer, CLASS_Destroyer, CLASS_Destroyer, CLASS_Frigate, CLASS_Corvette, CLASS_Fighter, CLASS_Resource, CLASS_NonCombat }; sdword cmMaxJobsPerClass[NUM_CLASSES] = { CM_MaxJobsMothership, CM_MaxJobsHeavyCruiser, CM_MaxJobsCarrier, CM_MaxJobsDestroyer, CM_MaxJobsFrigate, CM_MaxJobsCorvette, CM_MaxJobsFighter, CM_MaxJobsResource, CM_MaxJobsNonCombat, }; /*============================================================================= Private functions: =============================================================================*/ void updateShipView(void) { udword index; bool set=TRUE; if (curshipview == DefaultShip) { for (index = 0; cmShipsAvailable[index].nJobs != -1; index++) { //for all available ships if (cmShipsAvailable[index].itemtype==ITEM_SHIP) { if(cmShipsAvailable[index].itemstat==STAT_CANBUILD) { svSelectShip(cmShipsAvailable[index].info->shiptype); curshipview = cmShipsAvailable[index].info->shiptype; break; } } } } else { for (index = 0; cmShipsAvailable[index].nJobs != -1; index++) { //for all available ships if ( (cmShipsAvailable[index].itemtype==ITEM_SHIP) && (cmShipsAvailable[index].info->shiptype==curshipview) ) { if (cmShipsAvailable[index].itemstat!=STAT_CANBUILD) { break; } else { svSelectShip(curshipview); set = FALSE; } } } if (set) { for (index = 0; cmShipsAvailable[index].nJobs != -1; index++) { //for all available ships if (cmShipsAvailable[index].itemtype==ITEM_SHIP) { if(cmShipsAvailable[index].itemstat==STAT_CANBUILD) { svSelectShip(cmShipsAvailable[index].info->shiptype); curshipview = cmShipsAvailable[index].info->shiptype; break; } } } } } } //return the progress pointed to by index shipinprogress *cmSIP(udword index) { //return &curshipsInProgress->progress[index]; shipsinprogress *factory; factory = curshipsInProgress; return &factory->progress[index]; } void cmWheelNegative() { sdword ui = (sdword)cmUpperIndex-3; if (ui < 0) ui = 0; cmUpperIndex = (uword)ui; } void cmWheelPositive() { sdword ui = (sdword)cmUpperIndex + 3; while (ui + cmMaxIndex > NUMBER_SHIPS_AVAILABLE) { ui--; if (ui < 0) { ui = 0; break; } } cmUpperIndex = (sword)ui; } void cmDirtyShipInfo(void) { sdword i; if (cmShipInfoRegion != NULL) { #ifdef DEBUG_STOMP regVerify(cmShipInfoRegion); #endif cmShipInfoRegion->status |= RSF_DrawThisFrame; } if (cmNumberRUsRegion != NULL) { #ifdef DEBUG_STOMP regVerify(cmNumberRUsRegion); #endif cmNumberRUsRegion->status |= RSF_DrawThisFrame; } if (cmTotalRUsRegion != NULL) { #ifdef DEBUG_STOMP regVerify(cmTotalRUsRegion); #endif cmTotalRUsRegion->status |= RSF_DrawThisFrame; } if (cmTotalShipsRegion != NULL) { #ifdef DEBUG_STOMP regVerify(cmTotalShipsRegion); #endif cmTotalShipsRegion->status |= RSF_DrawThisFrame; } if (cmShipCostsRegion != NULL) { #ifdef DEBUG_STOMP regVerify(cmShipCostsRegion); #endif cmShipCostsRegion->status |= RSF_DrawThisFrame; } if (cmShipNumbersRegion != NULL) { #ifdef DEBUG_STOMP regVerify(cmShipNumbersRegion); #endif cmShipNumbersRegion->status |= RSF_DrawThisFrame; } if (cmConnectorRegion != NULL) { #ifdef DEBUG_STOMP regVerify(cmConnectorRegion); #endif cmConnectorRegion->status |= RSF_DrawThisFrame; } if (cmMotherShipRegion != NULL) { #ifdef DEBUG_STOMP regVerify(cmMotherShipRegion); #endif cmMotherShipRegion->status |= RSF_DrawThisFrame; } for (i=0;istatus |= RSF_DrawThisFrame; } } } void cmScroller(char *string, featom *atom) { scrollbarhandle shandle; sword ind; if (FEFIRSTCALL(atom)) { g_shandle = scSetupThumbwheel(cmUpperIndex, cmMaxIndex, (uword)NUMBER_SHIPS_AVAILABLE, atom); return; } shandle = (scrollbarhandle)atom->pData; if (shandle->mouseMoved != 0) { sword divs, up; real32 mm; cmDirtyShipInfo(); mm = (real32)shandle->mouseMoved; divs = (sword)(mm / shandle->divSize); up = (sword)(cmUpperIndex + divs); if (up < 0) up = 0; else if (up > NUMBER_SHIPS_AVAILABLE - cmMaxIndex) up = (sword)(NUMBER_SHIPS_AVAILABLE - cmMaxIndex); if (up <0) up = 0; cmUpperIndex = (uword)up; scAdjustThumbwheel(shandle, cmUpperIndex, cmMaxIndex, (uword)NUMBER_SHIPS_AVAILABLE); shandle->mouseMoved = 0; //clear flag return; } switch (shandle->event) { case SC_Other: ind = (sword)cmUpperIndex; if (mouseCursorY() < shandle->thumb.y0) { ind = (sword)(ind - cmMaxIndex); if (ind < 0) ind = 0; cmUpperIndex = (uword)ind; } else { ind = (sword)(ind + cmMaxIndex); if (ind > NUMBER_SHIPS_AVAILABLE - cmMaxIndex) ind = (sword)(NUMBER_SHIPS_AVAILABLE - cmMaxIndex); if (ind <0) ind = 0; cmUpperIndex = (uword)ind; } cmDirtyShipInfo(); break; case SC_Thumb: break; case SC_Negative: cmWheelNegative(); cmDirtyShipInfo(); break; case SC_Positive: cmWheelPositive(); cmDirtyShipInfo(); break; default: dbgMessagef("\nunhandled consMgr scroll event %d", shandle->clickType); } scAdjustThumbwheel(shandle, cmUpperIndex, cmMaxIndex, (uword)NUMBER_SHIPS_AVAILABLE); } shipsinprogress *cmFindFactory(struct Ship *ship) { Node *node = listofShipsInProgress.head; shipsinprogress *sinprogress; while (node != NULL) { sinprogress = (shipsinprogress *)listGetStructOfNode(node); if (sinprogress->ship == ship) { return sinprogress; } node = node->next; } return NULL; } void cmAddFactory(struct Ship *ship,bool canBuildBigShips) { shipsinprogress *sinprogress; sinprogress = memAlloc(sizeof(shipsinprogress),"sinprogress",NonVolatile); memset(sinprogress,0,sizeof(shipsinprogress)); if (ship->shiptype == Carrier) // add even if not for the player, because we want cmCarriers to be // deterministic for all machines to prevent sync problems { cmNumCarriers++; cmCarriers = memRealloc(cmCarriers, sizeof(CarrierInfo) * cmNumCarriers, "cmCarriers", NonVolatile); cmCarriers[cmNumCarriers - 1].ship = ship; cmCarriers[cmNumCarriers - 1].owner = ship->playerowner; } sinprogress->ship = ship; sinprogress->canBuildBigShips = canBuildBigShips; listAddNode(&listofShipsInProgress,&sinprogress->node,sinprogress); cmDirtyShipInfo(); cmFillInCarrierXs(); } void cmReset(void) { if (!singlePlayerGameLoadNewLevelFlag) // preserve this stuff if inbetween single player levels { listDeleteAll(&listofShipsInProgress); if (cmCarriers != NULL) { sdword i; memFree(cmCarriers); cmCarriers = NULL; cmNumCarriers = 0; for (i=0;inode); if (curshipsInProgress == sinprogress) { curshipsInProgress = NULL; cmCloseIfOpen(); } } node = cmDetermProgress.head; while (node != NULL) { dprog = (cmDetermProgress_t *)listGetStructOfNode(node); nextnode = node->next; if (dprog->creator == ship) { listDeleteNode(node); } node = nextnode; } for (i = 0; i < cmNumCarriers; i++) { if (cmCarriers[i].ship == ship) { for (k=0;k 0) { cmCarriers = memRealloc(cmCarriers, sizeof(CarrierInfo) * cmNumCarriers, "cmCarriers", NonVolatile); } else { memFree(cmCarriers); cmCarriers = NULL; } break; } } cmDirtyShipInfo(); cmFillInCarrierXs(); } static bool resourcesHaveBeenAboveBefore = TRUE; #pragma optimize("gy", off) //turn on stack frame (we need ebp for this function) void cmBuildTaskFunction(void) { static sdword index; static shipinprogress *progress; static Node *node; static shipsinprogress *sinprogress; static Ship *factoryship; static sdword decrement; if ((!gameIsRunning) || (cmReallyPausedAll)) { return; } if (playPackets) { return; // don't want to execute this routine which messes up resourceUnits, etc in record playback } /* taskYield(0); while (1) { if ((!gameIsRunning) || (cmReallyPausedAll)) { taskYield(0); continue; } taskStackSaveCond(0);*/ if (universe.curPlayerPtr->resourceUnits >= 300) { resourcesHaveBeenAboveBefore = TRUE; } node = listofShipsInProgress.head; while (node != NULL) { sinprogress = (shipsinprogress *)listGetStructOfNode(node); factoryship = sinprogress->ship; //this is a patch to avoid a very intermittent crash. if (!factoryship) { node = node->next; continue; } if (factoryship->playerowner == universe.curPlayerPtr) { for (index = 0, progress = &sinprogress->progress[0]; index < TOTAL_STD_SHIPS; index++, progress++) { //for all classes of ships if (universe.curPlayerPtr->resourceUnits <= 0) { break; } if (!progress->paused) { if (progress->nJobs > 0) { //for each construction job dbgAssert(progress->info != NULL); // Simulating fixed point because we need to save fractional bits across frames // RUs -= 0.5 cannot be simulated properly with integer #if CM_CHEAP_SHIPS if (cmCheapShips && !multiPlayerGame) { decrement = 1; } else #endif { decrement = ((progress->info->buildCost << 16) / progress->timeStart) + progress->timeFraction; progress->timeFraction = decrement & 0x0000FFFF; // save fraction bits decrement >>= 16; // Convert back to integer } /*#if (!UNIVERSE_TURBORECORD_ONLY) #if UNIVERSE_TURBOPAUSE_DEBUG if (universeTurbo) { decrement *= (UNIVERSE_TURBO_REPEAT+1); } #endif #endif*/ if (!universePause) { progress->costSoFar += decrement; if (!multiPlayerGame) { //xxx universe.curPlayerPtr->resourceUnits -= decrement;// decrement RU's } if(universe.curPlayerPtr->resourceUnits <= 0) { if (resourcesHaveBeenAboveBefore) { // speechEventFleet(STAT_F_ResLevels_Nil, 0, universe.curPlayerIndex); speechEventFleet(STAT_F_ConstInProgs_ResInsufficient, 0, universe.curPlayerIndex); resourcesHaveBeenAboveBefore = FALSE; } if (!multiPlayerGame) { //xxx universe.curPlayerPtr->resourceUnits = 0; } } #if CM_CHEAP_SHIPS if (cmCheapShips && !multiPlayerGame) { progress->timeLeft = 0; } else #endif { /*#if (!UNIVERSE_TURBORECORD_ONLY) #if UNIVERSE_TURBOPAUSE_DEBUG if (universeTurbo) { progress->timeLeft-=UNIVERSE_TURBO_REPEAT; } else #endif #endif*/ { progress->timeLeft--; //decrement the time } } } if (progress->timeLeft <= 0) { //if job has ended if(progress->timeFraction > 32767) // > 0.5, round up and reduce the resourceUnits by 1 { if (!multiPlayerGame) { //xxx universe.curPlayerPtr->resourceUnits--; } if(universe.curPlayerPtr->resourceUnits <= 0) { if (resourcesHaveBeenAboveBefore) { speechEventFleet(STAT_F_ResLevels_Nil, 0, universe.curPlayerIndex); resourcesHaveBeenAboveBefore = FALSE; } if (!multiPlayerGame) { //xxx universe.curPlayerPtr->resourceUnits = 0; } } } #if CM_VERBOSE_LEVEL >= 1 dbgMessagef("\nConstruction job #%d has finished a %s", index, ShipTypeToNiceStr(progress->info->shiptype)); #endif //yyy clWrapRUTransfer(&universe.mainCommandLayer,0,universe.curPlayerPtr->playerIndex,progress->info->buildCost,RUTRANS_BUILTSHIP); if (!multiPlayerGame) { //xxx clWrapCreateShip(&universe.mainCommandLayer, progress->info->shiptype, universe.curPlayerPtr->race, universe.curPlayerIndex, factoryship); } // fix for lag in internet games if (multiPlayerGame) { shiplagtotals[progress->info->shiptype] ++; } progress->nJobs--; //one less job progress->costSoFar -= progress->info->buildCost; if (progress->nJobs > 0) { //if still ships to build if ((progress->info->shipclass < CLASS_Corvette) || (progress->info->shipclass > CLASS_Fighter)) { speechEventFleet(STAT_F_Const_CapShipComplete, progress->info->shiptype, universe.curPlayerIndex); } progress->timeFraction = 0; // reset fixed point fractional bits progress->timeLeft = progress->timeStart = progress->info->buildTime; //restart the job } else { progress->nJobsTotal = 0; //all jobs for this type complete pingNewShipPingCreate(&factoryship->posinfo.position); speechEventFleet(STAT_F_Const_CapShipComplete, progress->info->shiptype, universe.curPlayerIndex); } } } } } } node = node->next; } /* taskStackRestoreCond(); taskYield(0); }*/ } #pragma optimize("", on) /*============================================================================= Functions: =============================================================================*/ /*----------------------------------------------------------------------------- Name : cmLoadTextures Description : load textures used by the construction manager Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void cmLoadTextures(void) { sdword race, index; char filename[128]; for (race = 0; race < MAX_RACES; race++) { for (index = 0; index < NUM_CMTEXTURES; index++) { // build filename for loading texture from file strcpy(filename, ShipImagePaths[race]); strcat(filename, ShipIcons[index]); // load the image cmShipImage[race][index] = trLIFFileLoad(filename, NonVolatile); if (bitTest(cmShipImage[race][index]->flags, TRF_Paletted)) { cmPaletted = TRUE; } else { cmPaletted = FALSE; } if (cmPaletted) { cmShipTexture[race][index] = trPalettedTextureCreate( cmShipImage[race][index]->data, cmShipImage[race][index]->palette, cmShipImage[race][index]->width, cmShipImage[race][index]->height); } else { cmShipTexture[race][index] = trRGBTextureCreate( (color*)cmShipImage[race][index]->data, cmShipImage[race][index]->width, cmShipImage[race][index]->height, TRUE); } } } for (index = 0; index < 6; index++) { #if 1 cmArrowIcon[index] = NULL; cmArrowTexture[index] = TR_InvalidInternalHandle; #else strcpy(filename, ARROW_ICON_PATH); strcat(filename, ArrowIcons[index]); cmArrowIcon[index] = trLIFFileLoad(filename, NonVolatile); dbgAssert(cmArrowIcon[index]); cmArrowTexture[index] = trRGBTextureCreate( (color*)cmArrowIcon[index]->data, cmArrowIcon[index]->width, cmArrowIcon[index]->height, TRUE); #endif } } /*----------------------------------------------------------------------------- Name : cmCloseTextures Description : free memory and handles associated with any textures we might be using Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void cmCloseTextures(void) { sdword race, index; for (race = 0; race < MAX_RACES; race++) { for (index = 0; index < NUM_CMTEXTURES; index++) { if (cmShipImage[race][index] != NULL) { memFree(cmShipImage[race][index]); cmShipImage[race][index] = NULL; } if (cmShipTexture[race][index] != TR_InvalidInternalHandle) { trRGBTextureDelete(cmShipTexture[race][index]); cmShipTexture[race][index] = TR_InvalidInternalHandle; } } } for (index = 0; index < 6; index++) { if (cmArrowIcon[index] != NULL) { memFree(cmArrowIcon[index]); cmArrowIcon[index] = NULL; } if (cmArrowTexture[index] != TR_InvalidInternalHandle) { trRGBTextureDelete(cmArrowTexture[index]); cmArrowTexture[index] = TR_InvalidInternalHandle; } } } /*----------------------------------------------------------------------------- Name : cmStartup Description : Start the construction manager module. Inputs : void Outputs : Starts a task to take care of construction. Return : void ----------------------------------------------------------------------------*/ void cmStartup(void) { sdword index; cmDeterministicStartup(); listInit(&listofShipsInProgress); // cmBuildTask = taskStart(cmBuildTaskFunction, CM_TaskServicePeriod, 0); cmDefaultFont = frFontRegister(cmDefaultFontName); cmShipListFont = frFontRegister(cmShipListFontName); cmStandardTextColor = FEC_ListItemStandard; cmLoadTextures(); for (index=0;indexrect.x0 + ((region->rect.x1 - region->rect.x0) / 2); cmConnectorRegion = region; primLine2(centerX, region->rect.y0, centerX, region->rect.y1, colRGB(255, 0, 0)); primCircleSolid2(centerX, region->rect.y1, 3, 16, colRGB(255, 0, 0)); } /*----------------------------------------------------------------------------- Name : cmShipDisabled Description : determines whether a given shiptype has been disabled for building or not Inputs : type - ship type Outputs : Return : TRUE or FALSE ----------------------------------------------------------------------------*/ /* bool cmShipDisabled(ShipType type) { sdword index; for (index = 0; cmShipsDisabled[index].disabled != -1; index++) { if (cmShipsDisabled[index].type == type && cmShipsDisabled[index].disabled) { return TRUE; } } return FALSE; } */ /*----------------------------------------------------------------------------- Name : cmUpdateShipAvailable Description : updates the ship build list for technology state. Inputs : none Outputs : none Return : void ----------------------------------------------------------------------------*/ void cmUpdateShipAvailable(void) { sdword index; sdword isclass=STAT_DONTPRINT; NUMBER_SHIPS_AVAILABLE=0; for (index = 0; cmShipsAvailable[index].nJobs != -1; index++) { if (cmShipsAvailable[index].itemtype==ITEM_SHIP) { if (rmCanBuildShip(universe.curPlayerPtr, cmShipsAvailable[index].type) && bitTest(cmShipsAvailable[index].info->staticheader.infoFlags, IF_InfoLoaded)) { cmShipsAvailable[index].itemstat = STAT_CANBUILD; NUMBER_SHIPS_AVAILABLE++; } else { cmShipsAvailable[index].itemstat = STAT_CANTBUILD; } } } for (; index >= 0; index--) { if (cmShipsAvailable[index].itemtype==ITEM_SHIP) { if (cmShipsAvailable[index].itemstat == STAT_CANBUILD) isclass=STAT_PRINT; } else if (cmShipsAvailable[index].itemtype==ITEM_CLASS) { cmShipsAvailable[index].itemstat = (sword)isclass; if (isclass==STAT_PRINT) NUMBER_SHIPS_AVAILABLE++; isclass=STAT_DONTPRINT; } } } /*----------------------------------------------------------------------------- Name : cmClose Description : Callback function to close the construction manager Inputs : Outputs : Deletes all regions associated with construction manager Return : ----------------------------------------------------------------------------*/ void cmClose(char *string, featom *atom) { //close the construction manager sdword i; if((tutorial==TUTORIAL_ONLY) && !tutEnable.bBuildClose) return; cmRenderEverythingCounter = 0; #if CM_VERBOSE_LEVEL >= 1 dbgMessagef("\nClose construction manager."); #endif curshipsInProgress = NULL; cmUpperIndex=0; feScreenDeleteFlags(cmBaseRegion,FE_DONT_DELETE_REGION_IF_SCREEN_NOT_FOUND); cmBaseRegion = NULL; if (cmIoSaveState) ioEnable(); // enable rendering of main game screen mrRenderMainScreen = TRUE; glcFullscreen(FALSE); /* play the exit sound */ soundEvent(NULL, UI_ManagerExit); //restart the sound of space ambient soundEvent(NULL, UI_SoundOfSpace); spUnlockout(); cmShipInfoRegion = NULL; cmNumberRUsRegion = NULL; cmTotalRUsRegion = NULL; cmTotalShipsRegion = NULL; cmShipCostsRegion = NULL; cmShipNumbersRegion = NULL; cmConnectorRegion = NULL; cmMotherShipRegion = NULL; for (i=0;i= 1 dbgMessagef("\nBuild selected ships.."); #endif for (index = 0; cmShipsAvailable[index].nJobs != -1; index++) { //for all available ships /* if (cmSIP(index)->paused) { cmSIP(index)->paused = FALSE; }*/ if (cmShipsAvailable[index].nJobs > 0) { builtSomething = TRUE; cmBuildJobsAdd(curshipsInProgress, cmShipsAvailable[index].info, cmShipsAvailable[index].nJobs); if (cmShipsAvailable[index].info->shipclass <= CLASS_Frigate) { capShipBuilt = TRUE; } } } for (index = 0; cmShipsAvailable[index].nJobs != -1; index++) { //for all available ships cmShipsAvailable[index].nJobs = 0; //no jobs queued yet } if (builtSomething) { speechEventFleet(COMM_F_Const_Start, 0, universe.curPlayerIndex); if (capShipBuilt) { speechEvent(curshipsInProgress->ship, COMM_Const_BuildCapShip, ranRandom(RAN_SoundGameThread) % 26); } } cmDirtyShipInfo(); } /*----------------------------------------------------------------------------- Name : cmCancelShipType Description : Cancels all jobs that refer to a certain shiptype Inputs : type - the shiptype to cancel Outputs : Cancels the build requests of a shiptype Return : void ----------------------------------------------------------------------------*/ void cmForceBuildShipType(ShipType type) { // udword i; curshipsInProgress = cmFindFactory(universe.curPlayerPtr->PlayerMothership); curshipsInProgress->progress[type].timeLeft = 0; } /*----------------------------------------------------------------------------- Name : cmCancelJobs Description : Callback to cancel all current jobs Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void cmCancelJobs(char *string, featom *atom) { sdword index, jobCount; shipinprogress *progress; sdword refund/*,totalSpent*/; bool canceled=FALSE; if((tutorial==TUTORIAL_ONLY) && !tutEnable.bBuildCancelJobs) return; progress = &curshipsInProgress->progress[0]; for (index = jobCount = 0; cmShipsAvailable[index].nJobs != -1; index++) { //for all available ships if ( (cmShipsAvailable[index].itemtype==ITEM_SHIP) && (progress[cmShipsAvailable[index].type].selected) ) { cmShipsAvailable[index].nJobs = 0; canceled = TRUE; if (multiPlayerGame) { if (cmShipsAvailable[index].info) { //xxx clWrapDeterministicBuild(CMD_CANCEL, &universe.mainCommandLayer, 0, /*numShips == as.many.as.there.are*/ cmShipsAvailable[index].type, cmShipsAvailable[index].info->shiprace, universe.curPlayerIndex, curshipsInProgress->ship); } } } } //speechEventFleet(COMM_F_Const_CancelAll, 0, universe.curPlayerIndex); for (index = 0; index < TOTAL_STD_SHIPS; index++, progress++) { //for all classes of ships if (progress->selected) { if (progress->nJobsTotal != 0) { refund = progress->costSoFar; refund = (sdword)((real32)refund * cmFloatRefundRatio); //yyy /* clWrapRUTransfer(&universe.mainCommandLayer, 0, universe.curPlayerPtr->playerIndex, refund, RUTRANS_BUILDCANCEL); */ if (!multiPlayerGame) { //xxx universe.curPlayerPtr->resourceUnits += refund; } } progress->timeFraction = 0; // No fixed point fraction bits (used for decrementing RUs) progress->timeStart = progress->timeLeft = 0; //job all done progress->nJobsTotal = progress->nJobs = 0; //cancel building jobs progress->costSoFar = 0; canceled = TRUE; if (multiPlayerGame) { if (progress->info) { //xxx clWrapDeterministicBuild(CMD_CANCEL, &universe.mainCommandLayer, 0, /*numShips == as.many.as.there.are*/ progress->info->shiptype, progress->info->shiprace, universe.curPlayerIndex, curshipsInProgress->ship); } } } } if (canceled == TRUE) speechEventFleet(COMM_F_Const_CancelBatch, 0, universe.curPlayerIndex); cmDirtyShipInfo(); } /*----------------------------------------------------------------------------- Name : cmPauseJobs Description : Callback to pause/unpause all current jobs Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void cmPauseJobs(char *string, featom *atom) { sdword index; shipinprogress *progress; bool allpaused = TRUE, changed=FALSE; //speechEventFleet(COMM_F_Const_CancelAll, 0, universe.curPlayerIndex); // later change to ConstructionCancel if((tutorial==TUTORIAL_ONLY) & !tutEnable.bBuildPauseJobs) return; //all paused? progress = &curshipsInProgress->progress[0]; for (index = 0; (index < TOTAL_STD_SHIPS) && allpaused; index++, progress++) { //for all classes of ships if (progress->selected) { if (!progress->paused) { allpaused = FALSE; } } } //set all jobs to opposite of allpaused progress = &curshipsInProgress->progress[0]; for (index = 0; index < TOTAL_STD_SHIPS; index++, progress++) { //for all classes of ships if (progress->selected) { udword command; progress->paused = !allpaused; if (progress->nJobs>0) changed = TRUE; command = progress->paused ? CMD_PAUSE : CMD_UNPAUSE; if (multiPlayerGame) { if (progress->info) { //xxx clWrapDeterministicBuild(command, &universe.mainCommandLayer, 0, /*numShips doesn't matter here*/ progress->info->shiptype, progress->info->shiprace, universe.curPlayerIndex, curshipsInProgress->ship); } } } } if (changed) { if (!allpaused) { speechEventFleet(COMM_F_Const_Pause, 0, universe.curPlayerIndex); } else { speechEventFleet(COMM_F_Const_Resume, 0, universe.curPlayerIndex); } } cmDirtyShipInfo(); } #if(0) /*----------------------------------------------------------------------------- Name : cmPauseJobs Description : Callback to pause/unpause all current jobs Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void cmPauseJobsOld(char *string, featom *atom) { udword index; bool allpaused = TRUE; shipinprogress *sip; if((tutorial==TUTORIAL_ONLY) & !tutEnable.bBuildPauseJobs) return; cmDirtyShipInfo(); /* if (bitTest(taskData[cmBuildTask]->flags, TF_Paused)) { speechEventFleet(COMM_F_Const_Resume, 0, universe.curPlayerIndex); taskResume(cmBuildTask); } else { speechEventFleet(COMM_F_Const_Pause, 0, universe.curPlayerIndex); taskPause(cmBuildTask); } */ //check if all selected ships paused for (index=0; (cmShipsAvailable[index].nJobs!=-1) && allpaused; index++) { //if (cmShipsAvailable[index].selected) sip = cmSIP(index); if (sip->selected) { if (!sip->paused) { allpaused = FALSE; } } } if (allpaused) //unpause them all { for (index=0; (cmShipsAvailable[index].nJobs!=-1) && allpaused; index++) { sip = cmSIP(index); if (sip->selected) { sip->paused = FALSE; } } } else //pause them all { for (index=0; (cmShipsAvailable[index].nJobs!=-1) && allpaused; index++) { sip = cmSIP(index); if (sip->selected) { sip->paused = TRUE; } } } } #endif /*----------------------------------------------------------------------------- Name : cmShipCostsDraw Description : Draw the cost of building selected ships. Inputs : feflow callback Outputs : .. Return : void ----------------------------------------------------------------------------*/ void cmShipCostsDraw(featom *atom, regionhandle region) { sdword x, y, index; rectangle *rect = ®ion->rect; bool newline=FALSE; color c; shipsinprogress *factory; shipinprogress *progress; fonthandle currentFont; sdword numlines,startind=0; currentFont = fontCurrentGet(); fontMakeCurrent(cmDefaultFont); feStaticRectangleDraw(region); //draw standard rectangle primRectSolid2(rect, CM_BackgroundColor); primLine2(rect->x0, rect->y0, rect->x0, rect->y1, colRGB(0, 100, 160)); cmShipCostsRegion = region; numlines = 0; for (index=0; cmShipsAvailable[index].nJobs!=-1; index++) { if ( (cmShipsAvailable[index].itemtype==ITEM_SHIP) && (cmShipsAvailable[index].itemstat != STAT_CANTBUILD) ) newline=TRUE; else if ( (cmShipsAvailable[index].itemtype==ITEM_CLASS) && (cmShipsAvailable[index].itemstat==STAT_PRINT) ) newline = TRUE; if (newline) { newline = FALSE; numlines++; if (numlines==cmUpperIndex+1) { startind = index; break; } } } newline=FALSE; numlines=0; y = rect->y0 + CM_ASMarginTop; factory = curshipsInProgress; for (index=startind;cmShipsAvailable[index].nJobs!=-1; index++) { if (y + fontHeight(" ") >= rect->y1) { break; } if ( (cmShipsAvailable[index].itemtype==ITEM_SHIP) && (cmShipsAvailable[index].itemstat==STAT_CANBUILD) ) { progress = &factory->progress[cmShipsAvailable[index].info->shiptype]; if (cmShipsAvailable[index].nJobs > 0) { //if this ship already selected c = cmSelectionTextColor; } else { c = cmStandardTextColor; } if (progress->nJobs > 0) { //if working on this class c = cmBuildingTextColor; } #if CM_CHEAP_SHIPS if (cmCheapShips && !multiPlayerGame) { x = rect->x1 - CM_ASMarginLeft - fontWidth("1"); fontPrint(x, y, c, "1"); } else #endif { x = rect->x1 - CM_ASMarginLeft - fontWidthf("%d", cmShipsAvailable[index].info->buildCost); fontPrintf(x, y, c, "%d", cmShipsAvailable[index].info->buildCost); } newline=TRUE; } else if ( (cmShipsAvailable[index].itemtype==ITEM_CLASS) && (cmShipsAvailable[index].itemstat==STAT_PRINT) ) { newline=TRUE; } if (newline) { newline = FALSE; y+= fontHeight(" ") + CM_ASInterSpacing; } } fontMakeCurrent(currentFont); } /*----------------------------------------------------------------------------- Name : cmSelectShipType Description : Find what type of ship was selected Inputs : reg - region clicked on yClicked - vertical location clicked on. Outputs : Return : type of ship clicked on or -1 if none ----------------------------------------------------------------------------*/ sdword cmSelectShipType(regionhandle region, sdword yClicked) { sdword index, y, numlines, startind; fonthandle currentFont; bool newline = FALSE; currentFont = fontMakeCurrent(cmDefaultFont); numlines = 0; for (index=0; cmShipsAvailable[index].nJobs!=-1; index++) { if (cmShipsAvailable[index].itemtype==ITEM_SHIP) { if (cmShipsAvailable[index].itemstat != STAT_CANTBUILD) newline=TRUE; } else if (cmShipsAvailable[index].itemtype==ITEM_CLASS) newline = TRUE; if (newline) { newline = FALSE; numlines++; if (numlines==cmUpperIndex+1) { startind = index; break; } } } y = region->rect.y0 + CM_ASMarginTop; for (index=startind;cmShipsAvailable[index].nJobs!=-1; index++) { if (y + fontHeight(" ") >= region->rect.y1) { break; } if ( (cmShipsAvailable[index].itemtype==ITEM_SHIP) && (cmShipsAvailable[index].itemstat==STAT_CANBUILD) ) { newline=TRUE; } else if ( (cmShipsAvailable[index].itemtype==ITEM_CLASS) && (cmShipsAvailable[index].itemstat==STAT_PRINT) ) { newline=TRUE; } if ((yClicked < y + fontHeight(" ") + CM_ASInterSpacing)&&(newline)) { if ((cmShipsAvailable[index].itemtype==ITEM_SHIP)&& (cmShipsAvailable[index].itemstat==STAT_CANBUILD)) { if( (tutorial==TUTORIAL_ONLY) && tutIsBuildShipRestricted((sdword)cmShipsAvailable[index].type) ) return(-1); else return(index); } else return(-1); } if (newline) { newline = FALSE; y+= fontHeight(" ") + CM_ASInterSpacing; } } fontMakeCurrent(currentFont); return (-1); } /*----------------------------------------------------------------------------- Name : cmClassCapClassRemap Description : Get the build manager class for a given ship Inputs : shipType - type of a ship shipClass - class of said ship Outputs : Return : build manager class ----------------------------------------------------------------------------*/ ShipClass cmClassCapClassRemap(ShipType shipType, ShipClass shipClass) { if (shipType == ResourceController) { //resource controller is a special case return(CLASS_Resource); } return(cmClassCapRemap[shipClass]); } /*----------------------------------------------------------------------------- Name : cmClassCapCanCreateShip Description : Can we build a ship of this class or are all the berths in use? Inputs : shipType - what type of ship we want to build. factory - where we want to build it from. cmShipsAvail - what ships are in the works. Outputs : Return : TRUE if we can build it, false otherwise ----------------------------------------------------------------------------*/ bool cmClassCapCanCreateShip(ShipType shipType, shipsinprogress *factory, shipavailable *cmShipsAvail) { ShipStaticInfo *shipStaticInfo; ShipClass shipClass; ShipRace shipRace; sdword index, nInThisClass; bool shipsQueuedUp[TOTAL_NUM_SHIPS]; shipStaticInfo = GetShipStaticInfo(shipType, factory->ship->shiprace);//get static info shipClass = cmClassCapClassRemap(shipType, shipStaticInfo->shipclass); //get ship class shipRace = factory->ship->playerowner->race; //get race of player who owns ship, not race of ship //clear out list of queued ships memset(shipsQueuedUp, 0, TOTAL_NUM_SHIPS * sizeof(bool)); //find all ships of same class queued up for construction for (index = nInThisClass = 0; cmShipsAvail[index].nJobs != -1; index++) { shipStaticInfo = GetShipStaticInfo(cmShipsAvail[index].type, shipRace); if (cmShipsAvail[index].nJobs > 0) { //if this ship being built if (cmClassCapClassRemap(shipStaticInfo->shiptype, shipStaticInfo->shipclass) == shipClass) { //if this job the same class as the one we want to build shipsQueuedUp[shipStaticInfo->shiptype] = TRUE; if (shipStaticInfo->shiptype != shipType) { //don't count this one if it's the one we're clicking on nInThisClass++; } } } } //find all ships of same class already under construction in this factory for (index = 0; index < TOTAL_STD_SHIPS; index++) { shipStaticInfo = GetShipStaticInfo(index, shipRace); if (factory->progress[index].nJobs > 0) { if (cmClassCapClassRemap(shipStaticInfo->shiptype, shipStaticInfo->shipclass) == shipClass) { //if this job the same class as the one we want to build if (!shipsQueuedUp[index]) { shipsQueuedUp[index] = TRUE; if (shipStaticInfo->shiptype != shipType) { //don't count this one if it's the one we're clicking on nInThisClass++; } } } } } //find all ships not yet started due to network lag. This does not seem to know of it's factory. //...or would that be covered by the first check? for (index = 0; index < TOTAL_STD_SHIPS; index++) { shipStaticInfo = GetShipStaticInfo(index, shipRace); if (shiplagtotals[index] > 0) { if (cmClassCapClassRemap(shipStaticInfo->shiptype, shipStaticInfo->shipclass) == shipClass) { //if this job the same class as the one we want to build if (!shipsQueuedUp[index]) { shipsQueuedUp[index] = TRUE; if (shipStaticInfo->shiptype != shipType) { //don't count this one if it's the one we're clicking on nInThisClass++; } } } } } return(nInThisClass < cmMaxJobsPerClass[shipClass]); } bool cmIncrement(shipsinprogress *factory, uword index) { shipinprogress *progress; bool flag = FALSE; /* bool disabled; //ctrl = add 5? disabled = cmShipDisabled(cmShipsAvailable[index].type); */ progress = &factory->progress[cmShipsAvailable[index].info->shiptype]; /* if (unitCapCanCreateShip((uword)cmShipsAvailable[index].info->shiptype,factory,cmShipsAvailable) && !disabled) */ if (unitCapCanCreateShip((uword)cmShipsAvailable[index].info->shiptype,factory,cmShipsAvailable) && cmClassCapCanCreateShip((ShipType)cmShipsAvailable[index].info->shiptype,factory,cmShipsAvailable)) { flag = TRUE; if (progress->nJobsTotal > 0) { progress->nJobsTotal++; progress->nJobs++; if (multiPlayerGame) { //xxx clWrapDeterministicBuild(CMD_START, &universe.mainCommandLayer, 1, progress->info->shiptype, universe.curPlayerPtr->race, universe.curPlayerIndex, factory->ship); } } else { cmShipsAvailable[index].nJobs++; } } else /* if (!disabled) */ { //put sound effect for too many ships HERE ShipNumberExceeded = (uword)index; ExceededCapsFrames = taskTimeElapsed; } svSelectShip(cmShipsAvailable[index].info->shiptype); curshipview = cmShipsAvailable[index].info->shiptype; return flag; } bool cmDecrement(shipsinprogress *factory, uword index) { shipinprogress *progress; bool flag = TRUE, skip = FALSE; //ctrl = add 5? progress = &factory->progress[cmShipsAvailable[index].info->shiptype]; //inactive jobs if (cmShipsAvailable[index].nJobs > 0) { cmShipsAvailable[index].nJobs--; skip = TRUE; } //active jobs if (!skip && progress->nJobs > 1) { progress->nJobs--; progress->nJobsTotal--; dbgMessagef("\n!! removed a job, jobs %d total %d !!", progress->nJobs, progress->nJobsTotal); if (multiPlayerGame) { //xxx clWrapDeterministicBuild(CMD_START, &universe.mainCommandLayer, 1 | 0x4000, progress->info->shiptype, universe.curPlayerPtr->race, universe.curPlayerIndex, factory->ship); } } svSelectShip(cmShipsAvailable[index].info->shiptype); curshipview = cmShipsAvailable[index].info->shiptype; return flag; } /*----------------------------------------------------------------------------- Name : cmRightArrowProcess Description : Inputs : standard region callback Outputs : Return : ----------------------------------------------------------------------------*/ sdword cmRightArrowProcess(regionhandle region, sdword ID, udword event, udword data) { shipsinprogress *factory; //shipinprogress *progress; if (multiPlayerGame) { if (!multiPlayerGameUnderWay) { return (0); } } if (event == RPE_PressLeft) { mgAccTime = taskTimeElapsed - CM_WAIT_ACC; return (0); } if (event == RPE_ReleaseLeft) { cmArrowIndex = -1; // mgAcceleratorRelease(); return (0); } //if(!mouseInRect(®ion->rect)) //{ // return (0); //} cmArrowIndex = cmSelectShipType(region, mouseCursorY()); factory = curshipsInProgress; if ( (event == RPE_HoldLeft) && (cmArrowIndex!=-1) && mgAccelerator() ) { if ((tutorial==TUTORIAL_ONLY) && !tutEnable.bBuildArrows) return(0); if( (tutorial==TUTORIAL_ONLY) && tutIsBuildShipRestricted(cmShipsAvailable[cmArrowIndex].info->shiptype) ) return(0); if (keyIsHit(CM_FIVEKEY)) { uword j; for (j=0; j<5; j++) { if (cmIncrement(factory,(uword)cmArrowIndex)) cmRightArrowActive = TRUE; else break; } } else { if (cmIncrement(factory,(uword)cmArrowIndex)) cmRightArrowActive = TRUE; } } else if (event == RPE_WheelUp) { cmWheelNegative(); scAdjustThumbwheel(g_shandle, cmUpperIndex, cmMaxIndex, (uword)NUMBER_SHIPS_AVAILABLE); } else if (event == RPE_WheelDown) { cmWheelPositive(); scAdjustThumbwheel(g_shandle, cmUpperIndex, cmMaxIndex, (uword)NUMBER_SHIPS_AVAILABLE); } cmDirtyShipInfo(); return(0); } /*----------------------------------------------------------------------------- Name : cmLeftArrowProcess Description : Inputs : standard region callback Outputs : Return : ----------------------------------------------------------------------------*/ sdword cmLeftArrowProcess(regionhandle region, sdword ID, udword event, udword data) { shipsinprogress *factory; //shipinprogress *progress; if (multiPlayerGame) { if (!multiPlayerGameUnderWay) { return (0); } } if (event == RPE_PressLeft) { mgAccTime = taskTimeElapsed - CM_WAIT_ACC; return (0); } if (event == RPE_ReleaseLeft) { cmArrowIndex = -1; // mgAcceleratorRelease(); return (0); } //if(!mouseInRect(®ion->rect)) //{ // return (0); //} cmArrowIndex = cmSelectShipType(region, mouseCursorY()); factory = curshipsInProgress; if ( (event == RPE_HoldLeft) && (cmArrowIndex!=-1) && mgAccelerator()) { bool8 skip = FALSE; if ((tutorial==TUTORIAL_ONLY) && !tutEnable.bBuildArrows) return(0); cmLeftArrowActive = TRUE; if (keyIsHit(CM_FIVEKEY)) { uword j; for (j=0; j<5; j++) { if (cmDecrement(factory,(uword)cmArrowIndex)) cmLeftArrowActive = TRUE; } } else { if (cmDecrement(factory,(uword)cmArrowIndex)) cmLeftArrowActive = TRUE; } } else if (event == RPE_WheelUp) { cmWheelNegative(); scAdjustThumbwheel(g_shandle, cmUpperIndex, cmMaxIndex, (uword)NUMBER_SHIPS_AVAILABLE); } else if (event == RPE_WheelDown) { cmWheelPositive(); scAdjustThumbwheel(g_shandle, cmUpperIndex, cmMaxIndex, (uword)NUMBER_SHIPS_AVAILABLE); } cmDirtyShipInfo(); return(0); } /*----------------------------------------------------------------------------- Name : cmDrawArrow Description : Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void new_cmDrawArrow(regionhandle region, bool leftArrow) { sdword y, index, numlines, startind=0; bool newline = FALSE; rectangle *rect = ®ion->rect; rectangle arrowrect; triangle tri; sdword width = rect->x1 - rect->x0 - 4; shipsinprogress *factory; shipinprogress *progress; fonthandle currentFont; sdword leftoffset = 0; numlines = 0; currentFont = fontMakeCurrent(cmDefaultFont); for (index=0; cmShipsAvailable[index].nJobs!=-1; index++) { if ( (cmShipsAvailable[index].itemtype==ITEM_SHIP) && (cmShipsAvailable[index].itemstat != STAT_CANTBUILD) ) newline=TRUE; else if ( (cmShipsAvailable[index].itemtype==ITEM_CLASS) && (cmShipsAvailable[index].itemstat==STAT_PRINT) ) newline = TRUE; if (newline) { newline = FALSE; numlines++; if (numlines==cmUpperIndex+1) { startind = index; break; } } } #define YOFFSET 200 newline=FALSE; numlines=0; y = rect->y0 + CM_ASMarginTop; factory = curshipsInProgress; if(leftArrow) { tri.x0 = rect->x1 - 2; tri.x1 = rect->x0 + 2; tri.x2 = rect->x1 - 2; } else { tri.x0 = rect->x0 + 2; tri.x1 = rect->x0 + 2; tri.x2 = rect->x1 - 2; } for (index=startind;cmShipsAvailable[index].nJobs!=-1; index++) { if (y + fontHeight(" ") >= rect->y1) { break; } if ( (cmShipsAvailable[index].itemtype==ITEM_SHIP) && (cmShipsAvailable[index].itemstat==STAT_CANBUILD) ) { progress = &factory->progress[cmShipsAvailable[index].info->shiptype]; if(leftArrow) { tri.y0 = YOFFSET + y; tri.y1 = YOFFSET + y + (width / 2); tri.y2 = YOFFSET + y + width; } else { tri.y0 = YOFFSET + y; tri.y1 = YOFFSET + y + width; tri.y2 = YOFFSET + y + (width / 2); } if(((leftArrow && cmLeftArrowActive) || (!leftArrow && cmRightArrowActive)) && (index==cmArrowIndex)) { cmLeftArrowActive = FALSE; cmRightArrowActive = FALSE; cmArrowIndex = -1; primTriSolid2(&tri, colRGB(200, 0, 0)); } primTriOutline2(&tri, 1, colRGB(200, 200, 0)); if (leftArrow) leftoffset = 3; else leftoffset = 0; arrowrect.x0 = rect->x0; arrowrect.x1 = rect->x0 + 20; arrowrect.y0 = y; arrowrect.y1 = y + 20; //trPalettedTextureMakeCurrent(cmArrowTexture[0 + leftoffset], cmArrowIcon[0 + leftoffset]->palette); trRGBTextureMakeCurrent(cmArrowTexture[0 + leftoffset]); rndPerspectiveCorrection(FALSE); glDisable(GL_ALPHA_TEST); glDisable(GL_BLEND); primRectSolidTextured2(&arrowrect); newline=TRUE; } else if ( (cmShipsAvailable[index].itemtype==ITEM_CLASS) && (cmShipsAvailable[index].itemstat==STAT_PRINT) ) { newline=TRUE; } if (newline) { newline = FALSE; y+= fontHeight(" ") + CM_ASInterSpacing; } } fontMakeCurrent(currentFont); } /*----------------------------------------------------------------------------- Name : cmDrawArrow Description : Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void cmDrawArrow(regionhandle region, bool leftArrow) { sdword y, index, numlines, startind=0; bool newline = FALSE; rectangle *rect = ®ion->rect; triangle tri; sdword width = rect->x1 - rect->x0 - 4; shipsinprogress *factory; shipinprogress *progress; fonthandle currentFont; numlines = 0; currentFont = fontMakeCurrent(cmDefaultFont); for (index=0; cmShipsAvailable[index].nJobs!=-1; index++) { if ( (cmShipsAvailable[index].itemtype==ITEM_SHIP) && (cmShipsAvailable[index].itemstat != STAT_CANTBUILD) ) newline=TRUE; else if ( (cmShipsAvailable[index].itemtype==ITEM_CLASS) && (cmShipsAvailable[index].itemstat==STAT_PRINT) ) newline = TRUE; if (newline) { newline = FALSE; numlines++; if (numlines==cmUpperIndex+1) { startind = index; break; } } } newline=FALSE; numlines=0; y = rect->y0 + CM_ASMarginTop; factory = curshipsInProgress; if(leftArrow) { tri.x0 = rect->x1 - 2; tri.x1 = rect->x0 + 2; tri.x2 = rect->x1 - 2; } else { tri.x0 = rect->x0 + 2; tri.x1 = rect->x0 + 2; tri.x2 = rect->x1 - 2; } for (index=startind;cmShipsAvailable[index].nJobs!=-1; index++) { if (y + fontHeight(" ") >= rect->y1) { break; } if ( (cmShipsAvailable[index].itemtype==ITEM_SHIP) && (cmShipsAvailable[index].itemstat==STAT_CANBUILD) ) { progress = &factory->progress[cmShipsAvailable[index].info->shiptype]; if(leftArrow) { tri.y0 = y; tri.y1 = y + (width / 2); tri.y2 = y + width; } else { tri.y0 = y; tri.y1 = y + width; tri.y2 = y + (width / 2); } if(((leftArrow && cmLeftArrowActive) || (!leftArrow && cmRightArrowActive)) && (index==cmArrowIndex)) { cmLeftArrowActive = FALSE; cmRightArrowActive = FALSE; cmArrowIndex = -1; primTriSolid2(&tri, colRGB(200, 0, 0)); } primTriOutline2(&tri, 1, colRGB(200, 200, 0)); newline=TRUE; } else if ( (cmShipsAvailable[index].itemtype==ITEM_CLASS) && (cmShipsAvailable[index].itemstat==STAT_PRINT) ) { newline=TRUE; } if (newline) { newline = FALSE; y+= fontHeight(" ") + CM_ASInterSpacing; } } fontMakeCurrent(currentFont); } /*----------------------------------------------------------------------------- Name : cmLeftArrowDraw Description : Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void cmLeftArrowDraw(featom *atom, regionhandle region) { if (region->flags == 0 || region->flags == RPE_DrawFunctionAdded) { //if region not processed yet region->flags = RPE_PressLeft | RPE_ReleaseLeft | RPE_HoldLeft | RPE_WheelUp | RPE_WheelDown; //receive mouse presses from now on regFunctionSet(region, (regionfunction)cmLeftArrowProcess); //set new region handler function } cmDrawArrow(region, TRUE); } /*----------------------------------------------------------------------------- Name : cmRightArrowDraw Description : Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void cmRightArrowDraw(featom *atom, regionhandle region) { if (region->flags == 0 || region->flags == RPE_DrawFunctionAdded) { //if region not processed yet region->flags = RPE_PressLeft | RPE_ReleaseLeft | RPE_HoldLeft | RPE_WheelUp | RPE_WheelDown; //receive mouse presses from now on regFunctionSet(region, (regionfunction)cmRightArrowProcess); //set new region handler function } cmDrawArrow(region, FALSE); } /*----------------------------------------------------------------------------- Name : cmShipNumbersDraw Description : Draw the number of ships being built. Inputs : feflow callback Outputs : .. Return : void ----------------------------------------------------------------------------*/ void cmShipNumbersDraw(featom *atom, regionhandle region) { sdword x, y, index, numShips; rectangle *rect = ®ion->rect; bool newline = FALSE; color c; shipsinprogress *factory; shipinprogress *progress; fonthandle currentFont; sdword numlines,startind=0; currentFont = fontCurrentGet(); fontMakeCurrent(cmDefaultFont); //if (region->flags == 0 || region->flags == RPE_DrawFunctionAdded) //{ //if region not processed yet // region->flags = RPE_PressLeft | RPE_PressRight | // RPE_WheelUp | RPE_WheelDown; //receive mouse presses from now on // regFunctionSet(region, cmSelectNumber); //set new region handler function //} feStaticRectangleDraw(region); //draw standard rectangle primRectSolid2(rect, CM_BackgroundColor); primLine2(rect->x0, rect->y0, rect->x0, rect->y1, colRGB(0, 100, 160)); cmShipNumbersRegion = region; numlines = 0; for (index=0; cmShipsAvailable[index].nJobs!=-1; index++) { if ( (cmShipsAvailable[index].itemtype==ITEM_SHIP) && (cmShipsAvailable[index].itemstat != STAT_CANTBUILD) ) newline=TRUE; else if ( (cmShipsAvailable[index].itemtype==ITEM_CLASS) && (cmShipsAvailable[index].itemstat==STAT_PRINT) ) newline = TRUE; if (newline) { newline = FALSE; numlines++; if (numlines==cmUpperIndex+1) { startind = index; break; } } } newline=FALSE; numlines=0; y = rect->y0 + CM_ASMarginTop; factory = curshipsInProgress; for (index=startind;cmShipsAvailable[index].nJobs!=-1; index++) { if (y + fontHeight(" ") >= rect->y1) { break; } if ( (cmShipsAvailable[index].itemtype==ITEM_SHIP) && (cmShipsAvailable[index].itemstat==STAT_CANBUILD) ) { progress = &factory->progress[cmShipsAvailable[index].info->shiptype]; if (cmShipsAvailable[index].nJobs > 0) { //if this ship already selected c = cmSelectionTextColor; } else { c = cmStandardTextColor; } if (progress->nJobs > 0) { //if working on this class c = cmBuildingTextColor; } numShips = cmShipsAvailable[index].nJobs; numShips += progress->nJobs; //active jobs, too x = rect->x1 - CM_ASMarginLeft - fontWidthf("%d", numShips) - 5; fontPrintf(x, y, c, "%d", numShips); newline=TRUE; } else if ( (cmShipsAvailable[index].itemtype==ITEM_CLASS) && (cmShipsAvailable[index].itemstat==STAT_PRINT) ) { newline=TRUE; } if (newline) { newline = FALSE; y+= fontHeight(" ") + CM_ASInterSpacing; } } fontMakeCurrent(currentFont); } /*----------------------------------------------------------------------------- Name : cmSelectAvailable Description : Callback for selecting/deselecting ships Inputs : standard region callback Outputs : selects or deselects a ship for construction Return : ----------------------------------------------------------------------------*/ sdword cmSelectAvailable(regionhandle region, sdword ID, udword event, udword data) { sdword index; shipsinprogress *factory; shipinprogress *progress, *kprogress; if (multiPlayerGame) { if (!multiPlayerGameUnderWay) { return (0); } } index = cmSelectShipType(region, mouseCursorY()); factory = curshipsInProgress; if ( (event == RPE_PressLeft || event == RPE_DoubleLeft) && (index!=-1) ) { progress = &factory->progress[cmShipsAvailable[index].info->shiptype]; if (progress->selected && !keyIsHit(SHIFTKEY)) { //left press (select/add job) if((tutorial==TUTORIAL_ONLY) && tutIsBuildShipRestricted(cmShipsAvailable[index].info->shiptype) ) return(0); if (keyIsHit(CM_FIVEKEY)) { uword j; for (j=0; j<5; j++) { if (cmIncrement(factory,(uword)index)) cmRightArrowActive = TRUE; } } else { if (cmIncrement(factory,(uword)index)) cmRightArrowActive = TRUE; } } else { if (keyIsHit(SHIFTKEY)) { progress->selected = !progress->selected; } else { udword i; kprogress = progress; progress = &curshipsInProgress->progress[0]; for (i = 0; i < TOTAL_STD_SHIPS; i++, progress++) { //for all classes of ships progress->selected = FALSE; } kprogress->selected = TRUE; /* for (i=0; cmShipsAvailable[i].nJobs!=-1; i++) { sip = cmSIP(index); if (i == index) sip->selected = TRUE; else sip->selected = FALSE;*/ } } svSelectShip(cmShipsAvailable[index].info->shiptype); curshipview = cmShipsAvailable[index].info->shiptype; } else if ( (event == RPE_PressRight || event == RPE_DoubleRight) && (index!=-1) ) { progress = &factory->progress[cmShipsAvailable[index].info->shiptype]; //cmDecrement(factory, (uword)index); if (keyIsHit(CM_FIVEKEY)) { uword j; for (j=0; j<5; j++) { if (cmDecrement(factory,(uword)index)) cmLeftArrowActive = TRUE; } } else { if (cmDecrement(factory,(uword)index)) cmLeftArrowActive = TRUE; if (keyIsHit(SHIFTKEY)) { progress->selected = !progress->selected; } else { udword i; kprogress = progress; progress = &curshipsInProgress->progress[0]; for (i = 0; i < TOTAL_STD_SHIPS; i++, progress++) { //for all classes of ships progress->selected = FALSE; } kprogress->selected = TRUE; } } svSelectShip(cmShipsAvailable[index].info->shiptype); curshipview = cmShipsAvailable[index].info->shiptype; } else if (event == RPE_WheelUp) { cmWheelNegative(); scAdjustThumbwheel(g_shandle, cmUpperIndex, cmMaxIndex, (uword)NUMBER_SHIPS_AVAILABLE); } else if (event == RPE_WheelDown) { cmWheelPositive(); scAdjustThumbwheel(g_shandle, cmUpperIndex, cmMaxIndex, (uword)NUMBER_SHIPS_AVAILABLE); } cmDirtyShipInfo(); return(0); } /*----------------------------------------------------------------------------- Name : cmShipInfoDraw Description : Draw the available ships and construction progress Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void cmShipInfoDraw(featom *atom, regionhandle region) { sdword x, y, index, percent; rectangle rect = region->rect; color c; shipsinprogress *factory; shipinprogress *progress; fonthandle currentFont; bool newline; sdword numlines, startind; if (cmRenderEverythingCounter > 0) { cmRenderEverythingCounter--; feRenderEverything = TRUE; } cmShipInfoRegion = region; currentFont = fontMakeCurrent(cmShipListFont); if (region->flags == 0 || region->flags == RPE_DrawFunctionAdded) { //if region not processed yet region->flags = RPE_PressLeft | RPE_PressRight | RPE_WheelUp | RPE_WheelDown; //receive mouse presses from now on regFunctionSet(region, (regionfunction)cmSelectAvailable); //set new region handler function regFilterSet(region, region->flags | RPE_DoubleLeft | RPE_DoubleRight); } feStaticRectangleDraw(region); //draw standard rectangle rect.x1 -= CM_ProgressMarginRight; primRectSolid2(&rect, CM_BackgroundColor); numlines = 0; for (index=0; cmShipsAvailable[index].nJobs!=-1; index++) { if ( (cmShipsAvailable[index].itemtype==ITEM_SHIP) && (cmShipsAvailable[index].itemstat != STAT_CANTBUILD) ) newline=TRUE; else if ( (cmShipsAvailable[index].itemtype==ITEM_CLASS) && (cmShipsAvailable[index].itemstat==STAT_PRINT) ) newline = TRUE; if (newline) { newline = FALSE; numlines++; if (numlines==cmUpperIndex+1) { startind = index; break; } } } y = region->rect.y0 + CM_ASMarginTop; newline=FALSE; numlines=0; factory = curshipsInProgress; for (index=startind;cmShipsAvailable[index].nJobs!=-1; index++) { if (y + fontHeight(" ") >= region->rect.y1) { break; } if ( (cmShipsAvailable[index].itemtype==ITEM_SHIP) && (cmShipsAvailable[index].itemstat==STAT_CANBUILD) ) { progress = &factory->progress[cmShipsAvailable[index].info->shiptype]; /* if (cmShipDisabled(cmShipsAvailable[index].type)) { c = FEC_ListItemInactive; } else */ if (cmShipsAvailable[index].nJobs > 0) { //if this ship already selected c = cmSelectionTextColor; } else { if (progress->selected) { c = FEC_ListItemSelected; } else { c = cmStandardTextColor; } } if (progress->selected) { rect.x0 = region->rect.x0 + CM_SelectBoxX0 + cmSelectLeftAdjust; rect.y0 = y + CM_SelectBoxY0 + cmSelectTopAdjust; rect.x1 = region->rect.x0 + CM_SelectBoxX1 + cmSelectRightAdjust; rect.y1 = y + CM_SelectBoxY1 + cmSelectBottomAdjust; primRectTranslucent2(&rect, FEC_ListItemTranslucent/*CM_SelectedColorSolid*/); primRectOutline2(&rect, 1, FEC_ListItemTranslucentBorder/*CM_SelectedColorOutline*/); } if (progress->nJobs > 0) { //if working on this class c = cmBuildingTextColor; if (progress->paused) c = CM_PausedColor; percent = 100 - progress->timeLeft * 100 / progress->timeStart; rect.x0 = region->rect.x0 + 2 + cmSelectLeftAdjust; //increment past border of window rect.y0 = y + CM_SelectBoxY0 + cmSelectTopAdjust; //draw top bar (progress of current ship) rect.y1 = y + fontHeight(" ") / 2 + cmSelectTopAdjust; rect.x1 = rect.x0 - 1 + cmSelectRightAdjust + percent * (region->rect.x1 - region->rect.x0 - CM_ProgressMarginLeft - CM_ProgressMarginRight) / 100; primRectSolid2(&rect, cmProgressShipColor); percent = 100 - (progress->timeLeft + (progress->nJobs - 1) * progress->timeStart) * 100 / (progress->timeStart * progress->nJobsTotal); rect.y0 = rect.y1; //draw bottom bar (progress of total job) rect.y1 = y + CM_SelectBoxY1 + cmSelectBottomAdjust - 1; rect.x1 = rect.x0 - 1 + cmSelectRightAdjust + percent * (region->rect.x1 - region->rect.x0 - CM_ProgressMarginLeft - CM_ProgressMarginRight) / 100; primRectSolid2(&rect, cmProgressTotalColor); } if ((taskTimeElapsed-ExceededCapsFrames < FLASH_TIMER) && (ShipNumberExceeded==index)) { c = FEC_ListItemInvalid;//cmCapsExceededTextColor; } /* if (progress->paused) { rect.x0 = region->rect.x0 + CM_SelectBoxX0+2; rect.y0 = y + CM_SelectBoxY0+2; rect.x1 = region->rect.x0 + CM_SelectBoxX1+2; rect.y1 = y + CM_SelectBoxY1+2; primRectOutline2(&rect, 1, CM_PausedColor); } */ //ship fancy name x = region->rect.x0 + CM_ASMarginLeft; fontPrint(x, y, c, ShipTypeToNiceStr(cmShipsAvailable[index].info->shiptype)); newline = TRUE; cmDirtyShipInfo(); } else if ( (cmShipsAvailable[index].itemtype==ITEM_CLASS) && (cmShipsAvailable[index].itemstat==STAT_PRINT) ) { char* classString; c = cmClassHeadingTextColor; if (cmShipsAvailable[index].type == CLASS_Destroyer) { classString = strGetString(strCLASS_SuperCapital); } else { classString = ShipClassToNiceStr(cmShipsAvailable[index].type); } fontPrintf(region->rect.x0 + CM_ASHeadingMarginLeft, y, c, "%s %s", classString, strGetString(strCLASS_Class)); //next line newline = TRUE; } if (newline) { newline = FALSE; y+= fontHeight(" ") + CM_ASInterSpacing; numlines++; } } fontMakeCurrent(currentFont); // cmDirtyShipInfo(); } /*----------------------------------------------------------------------------- Name : cmNumberRUsDraw Description : Callback to draw number of RU's available Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void cmNumberRUsDraw(featom *atom, regionhandle region) { sdword width; fonthandle oldfont; rectangle rect = region->rect; cmNumberRUsRegion = region; oldfont = fontMakeCurrent(cmShipListFont); primModeSet2(); primRectSolid2(&rect, CM_BackgroundColor); glEnable(GL_SCISSOR_TEST); glScissor(rect.x0, MAIN_WindowHeight - rect.y1, rect.x1 - rect.x0, rect.y1 - rect.y0); glClear(GL_DEPTH_BUFFER_BIT); glDisable(GL_SCISSOR_TEST); width = fontWidthf("%d", universe.curPlayerPtr->resourceUnits);//width of number feStaticRectangleDraw(region); //draw regular rectangle as backdrop fontPrintf(region->rect.x1 - width - CM_RUMarginRight, (region->rect.y1 - region->rect.y0 - fontHeight(NULL)) / 2 + region->rect.y0, atom->borderColor, "%d", universe.curPlayerPtr->resourceUnits); fontMakeCurrent(oldfont); } /*----------------------------------------------------------------------------- Name : cmTotalRUsDraw Description : Callback to draw total cost of build queue for all ship types Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void cmTotalRUsDraw(featom *atom, regionhandle region) { sdword width; sdword index, RUs; shipsinprogress *factory; shipinprogress *progress; rectangle rect = region->rect; fonthandle oldfont; real32 RUsAlready; cmTotalRUsRegion = region; oldfont = fontMakeCurrent(cmShipListFont); factory = curshipsInProgress; primModeSet2(); primRectSolid2(&rect, CM_BackgroundColor); glEnable(GL_SCISSOR_TEST); glScissor(rect.x0, MAIN_WindowHeight - rect.y1, rect.x1 - rect.x0, rect.y1 - rect.y0); glClear(GL_DEPTH_BUFFER_BIT); glDisable(GL_SCISSOR_TEST); for (index = RUs = 0; cmShipsAvailable[index].nJobs != -1; index++) { if (cmShipsAvailable[index].itemtype==ITEM_CLASS) { continue; } progress = &factory->progress[cmShipsAvailable[index].info->shiptype]; #if CM_CHEAP_SHIPS if (cmCheapShips && !multiPlayerGame) { RUs++; } else #endif { RUs += cmShipsAvailable[index].nJobs * cmShipsAvailable[index].info->buildCost; } if (progress->nJobs > 0) { dbgAssert(progress->info != NULL); #if CM_CHEAP_SHIPS if (cmCheapShips && !multiPlayerGame) { RUs += progress->nJobs; } else #endif { RUs += progress->nJobs * progress->info->buildCost; RUsAlready = (real32)progress->info->buildCost * (real32)(progress->timeStart - progress->timeLeft) / (real32)progress->info->buildTime; RUs -= (sdword)RUsAlready; } } } width = fontWidthf("%d", RUs); //width of number feStaticRectangleDraw(region); //draw regular rectangle as backdrop fontPrintf(region->rect.x1 - width - CM_RUMarginRight, (region->rect.y1 - region->rect.y0 - fontHeight(NULL)) / 2 + region->rect.y0, atom->borderColor, "%d", RUs); fontMakeCurrent(oldfont); } /*----------------------------------------------------------------------------- Name : cmTotalShipsDraw Description : Callback to draw total number of ships queued Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void cmTotalShipsDraw(featom *atom, regionhandle region) { sdword width; sdword index, nShips; shipsinprogress *factory; shipinprogress *progress; rectangle rect = region->rect; fonthandle oldfont; cmTotalShipsRegion = region; oldfont = fontMakeCurrent(cmShipListFont); factory = curshipsInProgress; primModeSet2(); primRectSolid2(&rect, CM_BackgroundColor); glEnable(GL_SCISSOR_TEST); glScissor(rect.x0, MAIN_WindowHeight - rect.y1, rect.x1 - rect.x0, rect.y1 - rect.y0); glClear(GL_DEPTH_BUFFER_BIT); glDisable(GL_SCISSOR_TEST); for (index = nShips = 0; cmShipsAvailable[index].nJobs != -1; index++) { if (cmShipsAvailable[index].itemtype==ITEM_SHIP) { progress = &factory->progress[cmShipsAvailable[index].info->shiptype]; nShips += cmShipsAvailable[index].nJobs; nShips += progress->nJobs; //active jobs, too } } width = fontWidthf("%d", nShips); //width of number feStaticRectangleDraw(region); //draw regular rectangle as backdrop fontPrintf(region->rect.x1 - width - CM_RUMarginRight, (region->rect.y1 - region->rect.y0 - fontHeight(NULL)) / 2 + region->rect.y0, atom->borderColor, "%d", nShips); fontMakeCurrent(oldfont); } bool cmCanBuildShipType(Ship *factoryship,ShipType shiptype,bool checkResearch) { ShipStaticInfo *factorystatic; shipavailable *shipsavail; sdword index; if (factoryship == NULL) { return FALSE; } factorystatic = factoryship->staticinfo; if (factorystatic->shiprace == R1) { if (factorystatic->canBuildBigShips) { shipsavail = cmShipsAvailableR1Big; } else { shipsavail = cmShipsAvailableR1Reg; } } else if (factorystatic->shiprace == R2) { if (factorystatic->canBuildBigShips) { shipsavail = cmShipsAvailableR2Big; } else { shipsavail = cmShipsAvailableR2Reg; } } else if (factorystatic->shiprace == P1) { shipsavail = cmShipsAvailableP1; } else if (factorystatic->shiprace == P2) { shipsavail = cmShipsAvailableP2; } else { dbgAssert(FALSE); // do we need to deal with P3? return FALSE; } for (index = 0; shipsavail[index].nJobs != -1; index++) { if (shipsavail[index].itemtype==ITEM_SHIP) { if (shipsavail[index].type == shiptype) { if (!checkResearch) { return TRUE; } if (rmCanBuildShip(factoryship->playerowner, shiptype)) { return TRUE; } else { return FALSE; } } } } return FALSE; } /*----------------------------------------------------------------------------- Name : cmUpdateFactory Description : Inputs : Outputs : Return : void ----------------------------------------------------------------------------*/ sdword cmUpdateFactoryNoReset(Ship* shipfactory) { ShipRace race; if ( (shipfactory == NULL) || (!((ShipStaticInfo *)(shipfactory->staticinfo))->canBuildShips) ) { shipfactory = universe.curPlayerPtr->PlayerMothership; } if (shipfactory == NULL) { #if CM_VERBOSE_LEVEL >= 1 dbgMessagef("\nShipfactory dead. Can't build anything!"); #endif return(0); } race = shipfactory->playerowner->race; if (!((ShipStaticInfo *)(shipfactory->staticinfo))->canBuildShips) { #if CM_VERBOSE_LEVEL >= 1 dbgMessagef("\nThis ship can't build anything!"); #endif return(0); } //if (bitTest(taskData[cmBuildTask]->flags, TF_Paused)) //{ //if build task paused // taskResume(cmBuildTask); //continue it //} curshipsInProgress = cmFindFactory(shipfactory); dbgAssert(curshipsInProgress != NULL); if (race == R1) { if (curshipsInProgress->canBuildBigShips) { cmShipsAvailable = cmShipsAvailableR1Big; } else { cmShipsAvailable = cmShipsAvailableR1Reg; } } else if (race == R2) { if (curshipsInProgress->canBuildBigShips) { cmShipsAvailable = cmShipsAvailableR2Big; } else { cmShipsAvailable = cmShipsAvailableR2Reg; } } else if (race == P1) { cmShipsAvailable = cmShipsAvailableP1; } else if (race == P2) { cmShipsAvailable = cmShipsAvailableP2; } else { dbgAssert(FALSE); } cmDirtyShipInfo(); cmUpdateShipAvailable(); return(1); } /*----------------------------------------------------------------------------- Name : cmUpdateShipsAvailable Description : Inputs : Outputs : Return : void ----------------------------------------------------------------------------*/ void cmUpdateShipsAvailable(void) { if (cmCurrentSelect==0) { cmUpdateFactoryNoReset(universe.curPlayerPtr->PlayerMothership); } else { cmUpdateFactoryNoReset(cmCarrierX[cmCurrentSelect-1]); } } /*----------------------------------------------------------------------------- Name : cmUpdateFactory Description : Inputs : Outputs : Return : void ----------------------------------------------------------------------------*/ sdword cmUpdateFactory(Ship* shipfactory) { sdword index; ShipRace race; shipinprogress *sip; if ( (shipfactory == NULL) || (!((ShipStaticInfo *)(shipfactory->staticinfo))->canBuildShips) ) { shipfactory = universe.curPlayerPtr->PlayerMothership; } if (shipfactory == NULL) { #if CM_VERBOSE_LEVEL >= 1 dbgMessagef("\nShipfactory dead. Can't build anything!"); #endif return(0); } race = shipfactory->playerowner->race; if (!((ShipStaticInfo *)(shipfactory->staticinfo))->canBuildShips) { #if CM_VERBOSE_LEVEL >= 1 dbgMessagef("\nThis ship can't build anything!"); #endif return(0); } //if (bitTest(taskData[cmBuildTask]->flags, TF_Paused)) //{ //if build task paused // taskResume(cmBuildTask); //continue it //} curshipsInProgress = cmFindFactory(shipfactory); dbgAssert(curshipsInProgress != NULL); if (race == R1) { if (curshipsInProgress->canBuildBigShips) { cmShipsAvailable = cmShipsAvailableR1Big; } else { cmShipsAvailable = cmShipsAvailableR1Reg; } } else if (race == R2) { if (curshipsInProgress->canBuildBigShips) { cmShipsAvailable = cmShipsAvailableR2Big; } else { cmShipsAvailable = cmShipsAvailableR2Reg; } } else if (race == P1) { cmShipsAvailable = cmShipsAvailableP1; } else if (race == P2) { cmShipsAvailable = cmShipsAvailableP2; } else { dbgAssert(FALSE); } for (index = 0; cmShipsAvailable[index].nJobs != -1; index++) { //for all available ships cmShipsAvailable[index].nJobs = 0; //no jobs queued yet if (cmShipsAvailable[index].itemtype==ITEM_SHIP) { cmShipsAvailable[index].info = //store the static info GetShipStaticInfo(cmShipsAvailable[index].type, race); } } for (index=0; (cmShipsAvailable[index].nJobs!=-1); index++) { //if (cmShipsAvailable[index].selected) sip = cmSIP(index); sip->selected = FALSE; } cmDirtyShipInfo(); cmUpdateShipAvailable(); return(1); } /*----------------------------------------------------------------------------- Name : cmConstructionBegin Description : Region callback handler for starting the construction manager. Inputs : Standard region callback. Outputs : Starts up the construction manager and returns. Return : void ----------------------------------------------------------------------------*/ sdword cmConstructionBegin(regionhandle region, sdword ID, udword event, udword data) { sdword status = 0; ShipPtr bship=NULL; if ((playPackets) || (universePause)) return 0; if((tutorial==TUTORIAL_ONLY) && !tutEnable.bBuildManager) return 0; if (smZoomingIn || smZoomingOut || universe.curPlayerPtr->PlayerMothership == NULL) { return(status); } cmFillInCarrierXs(); bship = (Ship *)ID; if ((bship == NULL)||((bship->shiptype!=Mothership)&&(bship->shiptype!=Carrier))) { if (cmCurrentSelect==0) { bship = universe.curPlayerPtr->PlayerMothership; if (universe.curPlayerPtr->PlayerMothership->shiptype == Carrier) { cmCurrentSelect = 1; } else if (universe.curPlayerPtr->PlayerMothership->shiptype == Mothership) { cmCurrentSelect = 0; } } else if (cmCarrierX[cmCurrentSelect-1]!=NULL) { bship = cmCarrierX[cmCurrentSelect-1]; } else { bship = universe.curPlayerPtr->PlayerMothership; if (universe.curPlayerPtr->PlayerMothership->shiptype == Carrier) { cmCurrentSelect = 1; } else if (universe.curPlayerPtr->PlayerMothership->shiptype == Mothership) { cmCurrentSelect = 0; } } } else if (bship->shiptype == Mothership) { cmCurrentSelect = 0; } else { sdword i; for (i=0;i= 1 dbgMessagef("\ncmBuildJobAdd: Building %s", ShipTypeToNiceStr(info->shiptype)); #endif dbgAssert(info->shiptype < TOTAL_STD_SHIPS); //make sure we can add a job progress = &factory->progress[info->shiptype]; //update the progress #if CM_CAP_JOBS_PER_CLASS if (factory->progress[info->shiptype].nJobs >= CM_NumberJobsPerClass) { //only so much we can do man! return; //??? do we need this??? } #endif if (progress->nJobs == 0) { //if no jobs currently progress->timeLeft = progress->timeStart = info->buildTime; progress->timeFraction = 0; // no fractional bits in fixed point for RU decrement progress->paused = FALSE; progress->costSoFar = 0; } progress->info = info; //init the progress structure #if CM_CAP_JOBS_PER_CLASS progress->nJobs = min(progress->nJobs + nJobs, CM_NumberJobsPerClass);//add the new jobs #else progress->nJobs = progress->nJobs + nJobs; //add the new jobs #endif prevJobsTotal = progress->nJobsTotal; progress->nJobsTotal += nJobs; if (multiPlayerGame) { //xxx clWrapDeterministicBuild(CMD_START, &universe.mainCommandLayer, progress->nJobsTotal | 0x8000, info->shiptype, universe.curPlayerPtr->race, universe.curPlayerIndex, factory->ship); } } /*----------------------------------------------------------------------------- Name : cmSelectMotherShip Description : processes region callback for mouse clicks Inputs : region info Outputs : none Return : void ----------------------------------------------------------------------------*/ sdword cmSelectMotherShip(regionhandle region, sdword ID, udword event, udword data) { if ((event == RPE_PressLeft) && (cmCurrentSelect!=0)) { cmCurrentSelect = 0; cmUpdateFactory(universe.curPlayerPtr->PlayerMothership); dbgAssert(region!=NULL); #ifdef DEBUG_STOMP regVerify(region); #endif bitSet(region->status, RSF_DrawThisFrame); // speechEventFleet(COMM_F_BuildCenterSelected, Mothership, universe.curPlayerIndex); //@@@ speechEventFleetVar(COMM_F_BuildCenterSelected, 0, 0, universe.curPlayerIndex); updateShipView(); } return(0); } /*----------------------------------------------------------------------------- Name : cmSelectCarrierX Description : processes region callback for mouse clicks Inputs : region info Outputs : none Return : void ----------------------------------------------------------------------------*/ sdword cmSelectCarrierX(regionhandle region, sdword ID, udword event, udword data, udword x) { if ((event == RPE_PressLeft) && (cmCarrierX[x-1] != NULL) && (cmCurrentSelect != x)) { cmCurrentSelect = x; cmUpdateFactory(cmCarrierX[x-1]); dbgAssert(region!=NULL); #ifdef DEBUG_STOMP regVerify(region); #endif bitSet(region->status, RSF_DrawThisFrame); // speechEventFleet(COMM_F_BuildCenterSelected, Carrier, universe.curPlayerIndex); speechEventFleetVar(COMM_F_BuildCenterSelected, 0, 1, universe.curPlayerIndex); updateShipView(); } return(0); } sdword cmSelectCarrier1(regionhandle region, sdword ID, udword event, udword data) { return cmSelectCarrierX(region,ID,event,data,1); } sdword cmSelectCarrier2(regionhandle region, sdword ID, udword event, udword data) { return cmSelectCarrierX(region,ID,event,data,2); } sdword cmSelectCarrier3(regionhandle region, sdword ID, udword event, udword data) { return cmSelectCarrierX(region,ID,event,data,3); } sdword cmSelectCarrier4(regionhandle region, sdword ID, udword event, udword data) { return cmSelectCarrierX(region,ID,event,data,4); } /*----------------------------------------------------------------------------- Name : cmDrawShipImage Description : draws the button image Inputs : region Outputs : Return : void ----------------------------------------------------------------------------*/ void cmDrawShipImage(regionhandle region, sdword shipID) { rectangle rect; udword usetexture; usetexture = (shipID == 0) ? 0 : 1; rect.x0 = region->rect.x0 + CM_SHIP_IMAGE_INSET; rect.y0 = region->rect.y0 + CM_SHIP_IMAGE_INSET; rect.x1 = region->rect.x1 - CM_SHIP_IMAGE_INSET; rect.y1 = region->rect.y1 - CM_SHIP_IMAGE_INSET; if (shipID == cmCurrentSelect) ferDrawFocusWindow(region, lw_focus); else ferDrawFocusWindow(region, lw_normal); if (glcActive()) { lifheader* lif = cmShipImage[universe.curPlayerPtr->race][usetexture]; glcRectSolidTexturedScaled2(&rect, lif->width, lif->height, lif->data, cmPaletted ? lif->palette : NULL, TRUE); } else { if (cmPaletted) { trPalettedTextureMakeCurrent(cmShipTexture[universe.curPlayerPtr->race][usetexture], cmShipImage[universe.curPlayerPtr->race][usetexture]->palette); } else { trRGBTextureMakeCurrent(cmShipTexture[universe.curPlayerPtr->race][usetexture]); } rndPerspectiveCorrection(FALSE); primRectSolidTextured2(&rect); } } /*----------------------------------------------------------------------------- Name : cmMotherShipDraw Description : draws the mothership button Inputs : region Outputs : Return : void ----------------------------------------------------------------------------*/ void cmMotherShipDraw(featom *atom, regionhandle region) { Ship *shipfactory = universe.curPlayerPtr->PlayerMothership; cmMotherShipRegion = region; if (shipfactory != NULL && shipfactory->shiptype == Mothership) { if (region->flags == 0 || region->flags == RPE_DrawFunctionAdded) { //if region not processed yet region->flags = RPE_PressLeft; //receive mouse presses from now on region->flags |= RPE_DrawEveryFrame; regFunctionSet(region, (regionfunction)cmSelectMotherShip); //set new region handler function } cmDrawShipImage(region, 0); } } void cmFillInCarrierXs(void) { sdword i,insert,index; for (i=0;i (x-1)) { cmCarrierX[x-1] = cmCarriers[index].ship; if (region->flags == 0 || region->flags == RPE_DrawFunctionAdded) { //if region not processed yet region->flags = RPE_PressLeft; //receive mouse presses from now on region->flags |= RPE_DrawEveryFrame; switch (x) { case 1: regFunctionSet(region, (regionfunction)cmSelectCarrier1); break; case 2: regFunctionSet(region, (regionfunction)cmSelectCarrier2); break; case 3: regFunctionSet(region, (regionfunction)cmSelectCarrier3); break; case 4: regFunctionSet(region, (regionfunction)cmSelectCarrier4); break; default: dbgAssert(FALSE); break; } } cmDrawShipImage(region, x); break; } } } } void cmCarrier1Draw(featom *atom, regionhandle region) { cmCarrierXDraw(atom,region,1); } void cmCarrier2Draw(featom *atom, regionhandle region) { cmCarrierXDraw(atom,region,2); } void cmCarrier3Draw(featom *atom, regionhandle region) { cmCarrierXDraw(atom,region,3); } void cmCarrier4Draw(featom *atom, regionhandle region) { cmCarrierXDraw(atom,region,4); } ShipType cmKeyToShipType(uword key) { uword i; for (i=0; cmShipTypes[i].key!=-1; i++) { if (cmShipTypes[i].key == key) { return (ShipType)cmShipTypes[i].ship; } } return 0; } uword cmShipTypeToKey(ShipType ship) { uword i; for (i=0; cmShipTypes[i].key!=-1; i++) { if (cmShipTypes[i].ship == ship) { return cmShipTypes[i].key; } } return 0; } //detoured keypresses: bool cmBuildHotKey(keyindex key, bool shift) { shipinprogress *progress; uword index; uword skey; bool buildhotkey = FALSE; ShipType ship; if (((key>=AKEY) && (key<=ZKEY) && (key!=BKEY) && (key!=PKEY) && (key!=CKEY) ) || ((key>=F1KEY) && (key<=F12KEY)) ) { buildhotkey = TRUE; } else if ((key>=ONEKEY) && (key<=THREEKEY)) { buildhotkey = TRUE; } if (buildhotkey) { if (shift) skey = key | CM_SHIFT; else skey = key; if ((ship = cmKeyToShipType(skey)) > 0) { progress = &curshipsInProgress->progress[0]; for (index = 0; index < TOTAL_STD_SHIPS; index++, progress++) { if ( (cmShipsAvailable[index].itemtype==ITEM_SHIP) && (cmShipsAvailable[index].itemstat==STAT_CANBUILD) ) { if (cmShipsAvailable[index].type == ship) { if (cmIncrement(curshipsInProgress,index)) { cmDecrement(curshipsInProgress,index); //start building now cmBuildJobsAdd(curshipsInProgress, cmShipsAvailable[index].info, 1 /*cmShipsAvailable[index].nJobs*/ ); } else { //sound effect } break; } } } } return TRUE; } else return FALSE; //don't bypass - it's a standard region key } void cmDeterministicStartup(void) { listInit(&cmDetermProgress); } void cmDeterministicShutdown(void) { listDeleteAll(&cmDetermProgress); } void cmDeterministicReset(void) { listDeleteAll(&cmDetermProgress); } crc32 cmDeterministicCRC(void) { Node* node; cmDetermProgress_t* block; cmDetermProgress_t* ptr; cmDetermProgress_t* curNode; sdword size; crc32 crc; if (cmDetermProgress.num == 0) { return 0; } size = cmDetermProgress.num * sizeof(cmDetermProgress_t); block = (cmDetermProgress_t*)memAlloc(size, "temp dblock", Volatile); ptr = block; node = cmDetermProgress.head; while (node != NULL) { curNode = (cmDetermProgress_t*)listGetStructOfNode(node); //copy node into block memcpy(ptr, curNode, sizeof(cmDetermProgress_t)); //clear non-deterministic members memset(&ptr->node, 0, sizeof(Node)); ptr->creator = NULL; //advance ptr++; node = node->next; } crc = crc32Compute((ubyte*)block, size); memFree(block); return crc; } void cmDeterministicBuildDisplay(void) { Node* node; cmDetermProgress_t* dprog; sdword y, height; height = fontHeight(NULL); y = 4 * height; node = cmDetermProgress.head; while (node != NULL) { dprog = (cmDetermProgress_t*)listGetStructOfNode(node); fontPrintf( 0, y, colReddish, "pl %d, num %d, type %s, race %s, tleft %d, ru %d", (sdword)dprog->playerIndex, dprog->numShips, ShipTypeToStr(dprog->shipType), ShipRaceToStr(dprog->shipRace), dprog->ticksLeft, universe.players[dprog->playerIndex].resourceUnits ); //next line, next node y += height; if ((y + height) > MAIN_WindowHeight) { //don't bother w/ off-screen lines break; } node = node->next; } } bool cmDeterministicSanityCheck(cmDetermProgress_t* dprog) { bool rval = TRUE; Player* player = &universe.players[dprog->playerIndex]; //check race if (dprog->shipRace != player->race) { rval = FALSE; } //check type if (dprog->shipType < STD_FIRST_SHIP || dprog->shipType > STD_LAST_SHIP) { rval = FALSE; } return rval; } void cmDeterministicBuildStart( sdword numShips, ShipType shipType, ShipRace shipRace, sdword playerIndex, ShipPtr creator) { Node* node; cmDetermProgress_t* dprog; ShipStaticInfo* info; //look for existing match in cmDetermProgress node = cmDetermProgress.head; while (node != NULL) { dprog = (cmDetermProgress_t*)listGetStructOfNode(node); if (dprog->shipType == shipType && dprog->shipRace == shipRace && dprog->playerIndex == playerIndex && dprog->creator == creator) { if (bitTest(numShips, 0x8000)) { //hi-bit (of uword) set, we're an absolute number bitClear(numShips, 0x8000); dprog->numShips = numShips; if (numShips <= 0) { //we've run out of jobs in the batch listDeleteNode(node); } return; } else { //match found, increment count & exit if (bitTest(numShips, 0x4000)) { bitClear(numShips, 0x4000); if (dprog->numShips > 1) { dprog->numShips -= numShips; } } else { dprog->numShips += numShips; } return; } } //move on node = node->next; } //not found, create new node info = GetShipStaticInfo(shipType, shipRace); dprog = (cmDetermProgress_t*)memAlloc(sizeof(cmDetermProgress_t), "dprog_t", NonVolatile); if (bitTest(numShips, 0x8000)) { bitClear(numShips, 0x8000); } if (bitTest(numShips, 0x4000)) { //we're subtracting from nothing return; } dprog->numShips = numShips; dprog->shipType = shipType; dprog->shipRace = shipRace; //@todo is buildTime expressed in universe ticks or seconds ? dprog->ticksForOne = info->buildTime; dprog->ticksLeft = dprog->ticksForOne; dprog->costPerShip = info->buildCost; dprog->costPerTick = (real32)dprog->costPerShip / (real32)dprog->ticksForOne; dprog->costSoFar = 0.0f; dprog->playerIndex = playerIndex; dprog->creator = creator; dprog->paused = FALSE; if (cmDeterministicSanityCheck(dprog)) { listAddNode(&cmDetermProgress, &dprog->node, dprog); } } void cmAdjustRU(cmDetermProgress_t* dprog, sdword amount) { Player* player = &universe.players[dprog->playerIndex]; player->resourceUnits += amount; universe.gameStats.playerStats[player->playerIndex].totalResourceUnitsSpent += min(player->resourceUnits, abs(amount)); if (player->resourceUnits < 0) { player->resourceUnits = 0; } } void cmDeterministicBuildCancel(ShipType shipType, ShipRace shipRace, sdword playerIndex, ShipPtr creator) { Node* node; cmDetermProgress_t* dprog; //look for match node = cmDetermProgress.head; while (node != NULL) { dprog = (cmDetermProgress_t*)listGetStructOfNode(node); if (dprog->shipType == shipType && dprog->shipRace == shipRace && dprog->playerIndex == playerIndex && dprog->creator == creator) { //match found, cancel & refund RUs cmAdjustRU(dprog, (sdword)(dprog->costSoFar * cmFloatRefundRatio)); listDeleteNode(node); return; } //move on node = node->next; } } void cmDeterministicBuildPause( bool pause, ShipType shipType, ShipRace shipRace, sdword playerIndex, ShipPtr creator) { Node* node; cmDetermProgress_t* dprog; //look for match node = cmDetermProgress.head; while (node != NULL) { dprog = (cmDetermProgress_t*)listGetStructOfNode(node); if (dprog->shipType == shipType && dprog->shipRace == shipRace && dprog->playerIndex == playerIndex && dprog->creator == creator) { //match found, adjust pause state dprog->paused = pause; } //next node node = node->next; } } void cmDeterministicBuild(udword command, sdword numShips, ShipType shipType, ShipRace shipRace, uword playerIndex, ShipPtr creator) { switch (command) { case CMD_START: cmDeterministicBuildStart(numShips, shipType, shipRace, playerIndex, creator); break; case CMD_CANCEL: cmDeterministicBuildCancel(shipType, shipRace, playerIndex, creator); break; case CMD_PAUSE: cmDeterministicBuildPause(TRUE, shipType, shipRace, playerIndex, creator); break; case CMD_UNPAUSE: cmDeterministicBuildPause(FALSE, shipType, shipRace, playerIndex, creator); break; default: dbgFatalf(DBG_Loc, "unknown dbuild command: %u", command); } } void cmDeterministicBuildProcess(void) { Node* node; Node* curNode; cmDetermProgress_t* dprog; Player* player; if (universePause) { //universe is paused, we won't be building anything return; } node = cmDetermProgress.head; while (node != NULL) { dprog = (cmDetermProgress_t*)listGetStructOfNode(node); player = &universe.players[dprog->playerIndex]; if (player->playerState != PLAYER_ALIVE) { //dead player, clear batch curNode = node; node = node->next; listDeleteNode(curNode); continue; } //advance curNode = node; node = node->next; if ((sdword)dprog->numShips <= 0) { listDeleteNode(curNode); continue; } if (!dprog->paused) { //increment cost, decrement RUs sdword lastCost, newCost; lastCost = (sdword)dprog->costSoFar; newCost = (sdword)(dprog->costSoFar + dprog->costPerTick); if (player->resourceUnits == 0) { continue; } if (newCost != lastCost) { sdword amount = newCost - lastCost; //only adjust RUs if we have enough cmAdjustRU(dprog, -amount); } dprog->costSoFar += dprog->costPerTick; dprog->ticksLeft--; if (dprog->ticksLeft <= 0) { //finished, make the ship appear clCreateShip(&universe.mainCommandLayer, dprog->shipType, dprog->shipRace, (uword)dprog->playerIndex, dprog->creator); //@todo is this adjustment correct ? if (player->resourceUnits > 0) { cmAdjustRU(dprog, -1); } //back to 0 cost for current ship dprog->costSoFar = 0.0f; dprog->numShips--; if (dprog->numShips == 0) { //no more ships, delete progress node listDeleteNode(curNode); } else { //still more to go, reset tick counter for next ship dprog->ticksLeft = dprog->ticksForOne; } } } } } /*============================================================================= Save Game Stuff: =============================================================================*/ #pragma warning( 4 : 4047) // turns off "different levels of indirection warning" #define RaceAndTypeToRaceType(race,type) ( ((race)<<16) + (type) ) #define GetRaceFromRaceType(racetype) ( ((racetype)>>16) & 0x0000ffff ) #define GetTypeFromRaceType(racetype) ( (racetype) & 0x0000ffff ) sdword ConvertShipStaticInfoToRaceType(ShipStaticInfo *shipstatic) { if (shipstatic == NULL) { return -1; } else { return RaceAndTypeToRaceType(shipstatic->shiprace,shipstatic->shiptype); } } ShipStaticInfo *ConvertRaceTypeToShipStaticInfo(sdword racetype) { if (racetype == -1) { return NULL; } else { return GetShipStaticInfo(GetTypeFromRaceType(racetype),GetRaceFromRaceType(racetype)); } } void Save_shipsinprogress(shipsinprogress *sinprogress) { SaveChunk *chunk; shipsinprogress *savecontents; sdword i; chunk = CreateChunk(BASIC_STRUCTURE|SAVE_SHIPSINPROGRESS,sizeof(shipsinprogress),sinprogress); savecontents = (shipsinprogress *)chunkContents(chunk); savecontents->ship = (Ship *)SpaceObjRegistryGetID((SpaceObj *)savecontents->ship); dbgAssert((sdword)savecontents->ship != -1); for (i=0;iprogress[i].info = (ShipStaticInfo *)ConvertShipStaticInfoToRaceType(savecontents->progress[i].info); } SaveThisChunk(chunk); memFree(chunk); } void SaveConsMgr() { sdword num = listofShipsInProgress.num; sdword cur = 0; Node *node = listofShipsInProgress.head; SaveInfoNumber(num); while (node != NULL) { cur++; Save_shipsinprogress(listGetStructOfNode(node)); node = node->next; } dbgAssert(cur == num); SaveInfoNumber(cmNumCarriers); if (cmNumCarriers > 0) { SaveChunk *chunk; CarrierInfo *savecontents; sdword i; chunk = CreateChunk(BASIC_STRUCTURE,sizeof(CarrierInfo) * cmNumCarriers,cmCarriers); savecontents = (CarrierInfo *)chunkContents(chunk); for (i=0;iship = SpaceObjRegistryGetShip((sdword)sinprogress->ship); dbgAssert(sinprogress->ship != NULL); for (i=0;iprogress[i].info = ConvertRaceTypeToShipStaticInfo((sdword)sinprogress->progress[i].info); } return sinprogress; } void LoadConsMgr() { sdword num; sdword i; shipsinprogress *sinprogress; num = LoadInfoNumber(); dbgAssert(listofShipsInProgress.num == 0); listInit(&listofShipsInProgress); for (i=0;inode,sinprogress); } cmNumCarriers = LoadInfoNumber(); if (cmNumCarriers > 0) { SaveChunk *chunk; sdword size = sizeof(CarrierInfo) * cmNumCarriers; chunk = LoadNextChunk(); VerifyChunk(chunk,BASIC_STRUCTURE,size); cmCarriers = memAlloc(size, "cmCarriers", NonVolatile); memcpy(cmCarriers,chunkContents(chunk),size); for (i=0;icreator = SpaceObjRegistryGetShip((sdword)dp->creator); listAddNode(list,&dp->node,dp); } #define BUILD_OPTIONAL_FLAG 0xb11db11d void SaveConsMgrDetermOptional() { if (cmDetermProgress.num == 0) { return; // don't save anything - optional block } SaveInfoNumber(BUILD_OPTIONAL_FLAG); SaveLinkedListOfStuff(&cmDetermProgress,SaveConsMgrDetermListCB); } void LoadConsMgrDetermOptional() { sdword optional; if (LoadInfoNumberOptional(&optional) && (optional == BUILD_OPTIONAL_FLAG)) { LoadLinkedListOfStuff(&cmDetermProgress,LoadConsMgrDetermListCB); } else { listInit(&cmDetermProgress); } } #pragma warning( 2 : 4047) // turn back on "different levels of indirection warning"