diff --git a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h index e1c0c1547a..ba3b9f750b 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h @@ -100,6 +100,11 @@ class GlobalData : public SubsystemInterface Bool m_dumpAssetUsage; Int m_framesPerSecondLimit; Int m_chipSetType; ///m_headless = TRUE; + } + return 1; +} + Int parseConstantDebug( char *args[], int num ) { if (TheWritableGlobalData) @@ -1205,6 +1214,10 @@ static CommandLineParam params[] = { "-noshaders", parseNoShaders }, { "-quickstart", parseQuickStart }, + // TheSuperHackers @feature helmutbuhler 11/04/2025 + // This runs the game without a window, graphics, input and audio. Used for testing. + { "-headless", parseHeadless }, + #if (defined(_DEBUG) || defined(_INTERNAL)) { "-noaudio", parseNoAudio }, { "-map", parseMapName }, diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp index afd0e0c42a..15ea12e47e 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp @@ -507,7 +507,7 @@ void GameEngine::init( int argc, char *argv[] ) initSubsystem(TheCrateSystem,"TheCrateSystem", MSGNEW("GameEngineSubsystem") CrateSystem(), &xferCRC, "Data\\INI\\Default\\Crate.ini", "Data\\INI\\Crate.ini"); initSubsystem(ThePlayerList,"ThePlayerList", MSGNEW("GameEngineSubsystem") PlayerList(), NULL); initSubsystem(TheRecorder,"TheRecorder", createRecorder(), NULL); - initSubsystem(TheRadar,"TheRadar", createRadar(), NULL); + initSubsystem(TheRadar,"TheRadar", TheGlobalData->m_headless ? NEW RadarDummy : createRadar(), NULL); initSubsystem(TheVictoryConditions,"TheVictoryConditions", createVictoryConditions(), NULL); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp index fa65dc5407..36b3b3ecaa 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp @@ -613,6 +613,7 @@ GlobalData::GlobalData() m_dumpAssetUsage = FALSE; m_framesPerSecondLimit = 0; m_chipSetType = 0; + m_headless = FALSE; m_windowed = 0; m_xResolution = 800; m_yResolution = 600; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/RandomValue.cpp b/GeneralsMD/Code/GameEngine/Source/Common/RandomValue.cpp index 289eb1f10a..11989e118c 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/RandomValue.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/RandomValue.cpp @@ -249,7 +249,7 @@ Int GetGameClientRandomValue( int lo, int hi, const char *file, int line ) /**/ #ifdef DEBUG_RANDOM_CLIENT DEBUG_LOG(( "%d: GetGameClientRandomValue = %d (%d - %d), %s line %d\n", - TheGameLogic->getFrame(), rval, lo, hi, file, line )); + TheGameLogic ? TheGameLogic->getFrame() : -1, rval, lo, hi, file, line )); #endif /**/ diff --git a/GeneralsMD/Code/GameEngine/Source/Common/System/MemoryInit.cpp b/GeneralsMD/Code/GameEngine/Source/Common/System/MemoryInit.cpp index 032121e26d..90e3b0199e 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/System/MemoryInit.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/System/MemoryInit.cpp @@ -416,6 +416,7 @@ static PoolSizeRec sizes[] = { "Overridable", 32, 32 }, { "W3DGameWindow", 700, 256 }, + { "DummyGameWindow", 700, 256 }, { "SuccessState", 32, 32 }, { "FailureState", 32, 32 }, { "ContinueState", 32, 32 }, diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp index 4d9a8774fa..8164ee45ff 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp @@ -1382,6 +1382,8 @@ void ControlBar::reset( void ) //------------------------------------------------------------------------------------------------- void ControlBar::update( void ) { + if (TheGlobalData->m_headless) + return; getStarImage(); updateRadarAttackGlow(); if(m_controlBarSchemeManager) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp index 93c40ade6f..a8a0c8b0ff 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp @@ -4097,3 +4097,36 @@ void GameWindowManager::clearTabList( void ) { m_tabList.clear(); } + + +GameWindow *DummyGameWindowManager::winGetWindowFromId(GameWindow *window, Int id) +{ + window = GameWindowManager::winGetWindowFromId(window, id); + if (window != NULL) + return window; + + // Just return any window, callers expect this to be non-null + return m_windowList; +} + +WindowMsgHandledType DummyWindowSystem(GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2) +{ + return MSG_IGNORED; +} + +GameWindow *DummyGameWindowManager::winCreateFromScript(AsciiString filenameString, WindowLayoutInfo *info) +{ + WindowLayoutInfo scriptInfo; + GameWindow* dummyWindow = winCreate(NULL, 0, 0, 0, 100, 100, DummyWindowSystem, NULL); + scriptInfo.windows.push_back(dummyWindow); + if (info) + *info = scriptInfo; + return dummyWindow; +} + +DummyGameWindow::~DummyGameWindow() +{ +} + + + diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp index 21be7ea196..103721042f 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp @@ -270,10 +270,13 @@ void GameClient::init( void ) TheDisplayStringManager->setName("TheDisplayStringManager"); } - // create the keyboard - TheKeyboard = createKeyboard(); - TheKeyboard->init(); - TheKeyboard->setName("TheKeyboard"); + if (!TheGlobalData->m_headless) + { + // create the keyboard + TheKeyboard = createKeyboard(); + TheKeyboard->init(); + TheKeyboard->setName("TheKeyboard"); + } // allocate and load image collection for the GUI and just load the 256x256 ones for now TheMappedImageCollection = MSGNEW("GameClientSubsystem") ImageCollection; @@ -321,11 +324,14 @@ void GameClient::init( void ) if( TheFontLibrary ) TheFontLibrary->init(); - // create the mouse - TheMouse = createMouse(); - TheMouse->parseIni(); - TheMouse->initCursorResources(); - TheMouse->setName("TheMouse"); + if (!TheGlobalData->m_headless) + { + // create the mouse + TheMouse = createMouse(); + TheMouse->parseIni(); + TheMouse->initCursorResources(); + TheMouse->setName("TheMouse"); + } // instantiate the display TheDisplay = createGameDisplay(); @@ -340,7 +346,7 @@ void GameClient::init( void ) } // create the window manager - TheWindowManager = createWindowManager(); + TheWindowManager = TheGlobalData->m_headless ? NEW DummyGameWindowManager : createWindowManager(); if( TheWindowManager ) { @@ -397,11 +403,10 @@ void GameClient::init( void ) TheRayEffects->setName("TheRayEffects"); } - TheMouse->init(); //finish initializing the mouse. - // set the limits of the mouse now that we've created the display and such if( TheMouse ) { + TheMouse->init(); //finish initializing the mouse. TheMouse->setPosition( 0, 0 ); TheMouse->setMouseLimits(); TheMouse->setName("TheMouse"); @@ -532,7 +537,7 @@ void GameClient::update( void ) //Initial Game Codition. We must show the movie first and then we can display the shell if(TheGlobalData->m_afterIntro && !TheDisplay->isMoviePlaying()) { - if( playSizzle && TheGlobalData->m_playSizzle ) + if( playSizzle && TheGlobalData->m_playSizzle && !TheGlobalData->m_headless ) { TheWritableGlobalData->m_allowExitOutOfMovies = TRUE; if(TheGameLODManager && TheGameLODManager->didMemPass()) @@ -623,10 +628,9 @@ void GameClient::update( void ) if(TheGlobalData->m_playIntro || TheGlobalData->m_afterIntro) { // redraw all views, update the GUI + if (!TheGlobalData->m_headless) { TheDisplay->DRAW(); - } - { TheDisplay->UPDATE(); } return; @@ -746,10 +750,12 @@ void GameClient::update( void ) } // update display + if (!TheGlobalData->m_headless) { TheDisplay->UPDATE(); } + if (!TheGlobalData->m_headless) { USE_PERF_TIMER(GameClient_draw) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp index 1fa290fa39..dab86e26a5 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp @@ -1298,7 +1298,7 @@ void InGameUI::setRadiusCursor(RadiusCursorType cursorType, const SpecialPowerTe //------------------------------------------------------------------------------------------------- void InGameUI::handleRadiusCursor() { - if (!m_curRadiusCursor.isEmpty()) + if (!m_curRadiusCursor.isEmpty() && TheMouse != NULL) { const MouseIO* mouseIO = TheMouse->getMouseStatus(); Coord3D pos; @@ -1337,6 +1337,8 @@ void InGameUI::handleRadiusCursor() void InGameUI::triggerDoubleClickAttackMoveGuardHint( void ) { + if (TheMouse == NULL) + return; m_duringDoubleClickAttackMoveGuardHintTimer = 11; const MouseIO* mouseIO = TheMouse->getMouseStatus(); TheTacticalView->screenToTerrain( &mouseIO->pos, &m_duringDoubleClickAttackMoveGuardHintStashedPosition ); @@ -2936,8 +2938,9 @@ void InGameUI::setGUICommand( const CommandButton *command ) } setRadiusCursorNone(); } - - m_mouseModeCursor = TheMouse->getMouseCursor(); + + if (TheMouse != NULL) + m_mouseModeCursor = TheMouse->getMouseCursor(); } // end setGUICommand diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp index c18e65726f..b328a02a88 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp @@ -2218,6 +2218,8 @@ const WaterHandle* TerrainLogic::getWaterHandle( Real x, Real y ) /**@todo: Remove this after we have all water types included in water triggers. For now do special check for water grid mesh. */ + // TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025 + // We shouldn't depend on TerrainVisual here. Real meshZ; if( TheTerrainVisual->getWaterGridHeight( x, y, &meshZ ) ) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index 37a44e14bd..7ad6c38542 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -6096,6 +6096,9 @@ const AsciiString& Object::getCommandSetString() const //============================================================================= Bool Object::canProduceUpgrade( const UpgradeTemplate *upgrade ) { + // TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025 + // TheControlBar belongs to the client, we shouldn't depend on that to check this. + // We need to have the button to make the upgrade. CommandSets are a weird Logic/Client hybrid. const CommandSet *set = TheControlBar->findCommandSet(getCommandSetString()); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp index b3571f6463..dfa7399a90 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp @@ -510,6 +510,10 @@ void DockUpdate::loadDockPositions() if( m_numberApproachPositions != DYNAMIC_APPROACH_VECTOR_FLAG ) { // Dynamic means no bones + + // TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025 + // We shouldn't depend on bones of a drawable here! + Coord3D approachBones[DEFAULT_APPROACH_VECTOR_SIZE]; m_numberApproachPositionBones = myDrawable->getPristineBonePositions( "DockWaiting", 1, approachBones, NULL, m_numberApproachPositions); if( m_numberApproachPositions == m_approachPositions.size() )//safeguard: will always be true diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp index 0f22d5b3a7..66e2298826 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp @@ -2598,6 +2598,8 @@ Bool Weapon::privateFireWeapon( Bool reloaded = false; if (m_ammoInClip > 0) { + // TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025 + // barrelCount shouln't depend on Drawable, which belongs to client. Int barrelCount = sourceObj->getDrawable()->getBarrelCount(m_wslot); if (m_curBarrel >= barrelCount) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp index 5c4f05b4c4..2e9c8da0ac 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp @@ -3205,7 +3205,8 @@ void ScriptActions::doDisableInput() #endif { TheInGameUI->setInputEnabled(false); - TheMouse->setVisibility(false); + if (TheMouse != NULL) + TheMouse->setVisibility(false); TheInGameUI->deselectAllDrawables(); TheInGameUI->clearAttackMoveToMode(); TheInGameUI->setWaypointMode( FALSE ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index bf74873267..77ff8271c9 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -737,6 +737,9 @@ static void populateRandomSideAndColor( GameInfo *game ) // Prevent from selecting the disabled Generals. // This is also enforced at GUI setup (GUIUtil.cpp). // @todo: unlock these when something rad happens + + // TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025 + // TheChallengeGenerals belongs to client, we shouldn't depend on that here. Bool disallowLockedGenerals = TRUE; const GeneralPersona *general = TheChallengeGenerals->getGeneralByTemplateName(ptTest->getName()); Bool startsLocked = general ? !general->isStartingEnabled() : FALSE; @@ -1267,7 +1270,7 @@ void GameLogic::startNewGame( Bool loadingSaveGame ) //****************************// // Get the m_loadScreen for this kind of game - if(!m_loadScreen) + if(!m_loadScreen && !TheGlobalData->m_headless) { m_loadScreen = getLoadScreen( loadingSaveGame ); if(m_loadScreen) @@ -1982,6 +1985,9 @@ void GameLogic::startNewGame( Bool loadingSaveGame ) // The game will start, but the cheater will be instantly defeated because he has no troops. // This is also enforced at GUI setup (GUIUtil.cpp and UserPreferences.cpp). // @todo: unlock these when something rad happens + + // TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025 + // TheChallengeGenerals belongs to client, we shouldn't depend on that here. Bool disallowLockedGenerals = TRUE; const GeneralPersona *general = TheChallengeGenerals->getGeneralByTemplateName(pt->getName()); Bool startsLocked = general ? !general->isStartingEnabled() : FALSE; @@ -2187,7 +2193,7 @@ void GameLogic::startNewGame( Bool loadingSaveGame ) } // if we're in a load game, don't fade yet - if( loadingSaveGame == FALSE ) + if(loadingSaveGame == FALSE && TheTransitionHandler != NULL) { TheTransitionHandler->setGroup("FadeWholeScreen"); while(!TheTransitionHandler->isFinished()) @@ -2227,14 +2233,17 @@ void GameLogic::startNewGame( Bool loadingSaveGame ) if(m_gameMode == GAME_SHELL) { - if(TheShell->getScreenCount() == 0) - TheShell->push( AsciiString("Menus/MainMenu.wnd") ); - else if (TheShell->top()) + if (!TheGlobalData->m_headless) { - TheShell->top()->hide(FALSE); - TheShell->top()->bringForward(); + if(TheShell->getScreenCount() == 0) + TheShell->push( AsciiString("Menus/MainMenu.wnd") ); + else if (TheShell->top()) + { + TheShell->top()->hide(FALSE); + TheShell->top()->bringForward(); + } + HideControlBar(); } - HideControlBar(); } else { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp index 2434afa6e6..808a8b1e80 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp @@ -265,7 +265,7 @@ void GameLogic::clearGameData( Bool showScoreScreen ) TheScriptActions->closeWindows(FALSE); // Close victory or defeat windows. Bool shellGame = FALSE; - if ((!isInShellGame() || !isInGame()) && showScoreScreen) + if ((!isInShellGame() || !isInGame()) && showScoreScreen && !TheGlobalData->m_headless) { shellGame = TRUE; TheTransitionHandler->setGroup("FadeWholeScreen"); @@ -291,7 +291,8 @@ void GameLogic::clearGameData( Bool showScoreScreen ) HideControlBar(); closeWindows(); - TheMouse->setVisibility(TRUE); + if (TheMouse != NULL) + TheMouse->setVisibility(TRUE); if(m_background) { diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp index dfa2d339bd..3b2ec477b5 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp @@ -288,36 +288,41 @@ BaseHeightMapRenderObjClass::BaseHeightMapRenderObjClass(void) m_useDepthFade = false; m_disableTextures = false; TheTerrainRenderObject = this; - m_treeBuffer = NULL; - - m_treeBuffer = NEW W3DTreeBuffer; + m_treeBuffer = NULL; m_propBuffer = NULL; - - m_propBuffer = NEW W3DPropBuffer; - - m_bibBuffer = NULL; - m_bibBuffer = NEW W3DBibBuffer; - m_curImpassableSlope = 45.0f; // default to 45 degrees. m_bridgeBuffer = NULL; - m_bridgeBuffer = NEW W3DBridgeBuffer; - m_waypointBuffer = NEW W3DWaypointBuffer; + m_waypointBuffer = NULL; #ifdef DO_ROADS m_roadBuffer = NULL; - m_roadBuffer = NEW W3DRoadBuffer; #endif #ifdef DO_SCORCH m_vertexScorch = NULL; m_indexScorch = NULL; m_scorchTexture = NULL; clearAllScorches(); + m_shroud = NULL; +#endif + + if (TheGlobalData->m_headless) + return; + + m_treeBuffer = NEW W3DTreeBuffer; + + m_propBuffer = NEW W3DPropBuffer; + + m_bibBuffer = NEW W3DBibBuffer; + + m_curImpassableSlope = 45.0f; // default to 45 degrees. + m_bridgeBuffer = NEW W3DBridgeBuffer; + m_waypointBuffer = NEW W3DWaypointBuffer; +#ifdef DO_ROADS + m_roadBuffer = NEW W3DRoadBuffer; #endif #if defined(_DEBUG) || defined(_INTERNAL) if (TheGlobalData->m_shroudOn) m_shroud = NEW W3DShroud; - else - m_shroud = NULL; #else m_shroud = NEW W3DShroud; #endif @@ -1791,7 +1796,8 @@ Int BaseHeightMapRenderObjClass::initHeightData(Int x, Int y, WorldHeightMap *pM if (m_shroud) m_shroud->init(m_map,TheGlobalData->m_partitionCellSize,TheGlobalData->m_partitionCellSize); #ifdef DO_ROADS - m_roadBuffer->setMap(m_map); + if (m_roadBuffer) + m_roadBuffer->setMap(m_map); #endif HeightSampleType *data = NULL; if (pMap) { @@ -1846,7 +1852,7 @@ Int BaseHeightMapRenderObjClass::initHeightData(Int x, Int y, WorldHeightMap *pM m_curNumScorchIndices=0; // If the textures aren't allocated (usually because of a hardware reset) need to allocate. Bool needToAllocate = false; - if (m_stageTwoTexture == NULL) { + if (m_stageTwoTexture == NULL && m_treeBuffer) { needToAllocate = true; } if (data && needToAllocate) @@ -2300,7 +2306,8 @@ void BaseHeightMapRenderObjClass::notifyShroudChanged(void) void BaseHeightMapRenderObjClass::addTerrainBib(Vector3 corners[4], ObjectID id, Bool highlight) { - m_bibBuffer->addBib(corners, id, highlight); + if (m_bibBuffer) + m_bibBuffer->addBib(corners, id, highlight); }; //============================================================================= @@ -2311,7 +2318,8 @@ void BaseHeightMapRenderObjClass::addTerrainBib(Vector3 corners[4], void BaseHeightMapRenderObjClass::addTerrainBibDrawable(Vector3 corners[4], DrawableID id, Bool highlight) { - m_bibBuffer->addBibDrawable(corners, id, highlight); + if (m_bibBuffer) + m_bibBuffer->addBibDrawable(corners, id, highlight); }; //============================================================================= @@ -2321,7 +2329,8 @@ void BaseHeightMapRenderObjClass::addTerrainBibDrawable(Vector3 corners[4], //============================================================================= void BaseHeightMapRenderObjClass::removeTerrainBibHighlighting() { - m_bibBuffer->removeHighlighting( ); + if (m_bibBuffer) + m_bibBuffer->removeHighlighting( ); }; //============================================================================= @@ -2331,7 +2340,8 @@ void BaseHeightMapRenderObjClass::removeTerrainBibHighlighting() //============================================================================= void BaseHeightMapRenderObjClass::removeAllTerrainBibs() { - m_bibBuffer->clearAllBibs( ); + if (m_bibBuffer) + m_bibBuffer->clearAllBibs( ); }; //============================================================================= @@ -2341,7 +2351,8 @@ void BaseHeightMapRenderObjClass::removeAllTerrainBibs() //============================================================================= void BaseHeightMapRenderObjClass::removeTerrainBib(ObjectID id) { - m_bibBuffer->removeBib( id ); + if (m_bibBuffer) + m_bibBuffer->removeBib( id ); }; //============================================================================= @@ -2351,7 +2362,8 @@ void BaseHeightMapRenderObjClass::removeTerrainBib(ObjectID id) //============================================================================= void BaseHeightMapRenderObjClass::removeTerrainBibDrawable(DrawableID id) { - m_bibBuffer->removeBibDrawable( id ); + if (m_bibBuffer) + m_bibBuffer->removeBibDrawable( id ); }; //============================================================================= @@ -2368,7 +2380,8 @@ void BaseHeightMapRenderObjClass::staticLightingChanged( void ) m_scorchesInBuffer = 0; // If we just allocated the buffers, we got no scorches in the buffer. m_curNumScorchVertices=0; m_curNumScorchIndices=0; - m_roadBuffer->updateLighting(); + if (m_roadBuffer) + m_roadBuffer->updateLighting(); } diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DDebrisDraw.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DDebrisDraw.cpp index 1e709a3c97..f4fca38dc7 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DDebrisDraw.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DDebrisDraw.cpp @@ -73,7 +73,8 @@ W3DDebrisDraw::~W3DDebrisDraw(void) } if (m_renderObject) { - W3DDisplay::m_3DScene->Remove_Render_Object(m_renderObject); + if (W3DDisplay::m_3DScene != NULL) + W3DDisplay::m_3DScene->Remove_Render_Object(m_renderObject); REF_PTR_RELEASE(m_renderObject); m_renderObject = NULL; } @@ -111,7 +112,8 @@ void W3DDebrisDraw::setModelName(AsciiString name, Color color, ShadowType t) DEBUG_ASSERTCRASH(m_renderObject, ("Debris model %s not found!\n",name.str())); if (m_renderObject) { - W3DDisplay::m_3DScene->Add_Render_Object(m_renderObject); + if (W3DDisplay::m_3DScene != NULL) + W3DDisplay::m_3DScene->Add_Render_Object(m_renderObject); m_renderObject->Set_User_Data(getDrawable()->getDrawableInfo()); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DLaserDraw.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DLaserDraw.cpp index 7306011ab8..93349f000c 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DLaserDraw.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DLaserDraw.cpp @@ -205,7 +205,8 @@ W3DLaserDraw::W3DLaserDraw( Thing *thing, const ModuleData* moduleData ) : } // add to scene - W3DDisplay::m_3DScene->Add_Render_Object( line ); //add it to our scene so it gets rendered with other objects. + if (W3DDisplay::m_3DScene != NULL) + W3DDisplay::m_3DScene->Add_Render_Object( line ); //add it to our scene so it gets rendered with other objects. // hide the render object until the first time we come to draw it and // set the correct position @@ -229,7 +230,8 @@ W3DLaserDraw::~W3DLaserDraw( void ) { // remove line from scene - W3DDisplay::m_3DScene->Remove_Render_Object( m_line3D[ i ] ); + if (W3DDisplay::m_3DScene != NULL) + W3DDisplay::m_3DScene->Remove_Render_Object( m_line3D[ i ] ); // delete line REF_PTR_RELEASE( m_line3D[ i ] ); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp index 59f2b8f778..e6fba5bcad 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp @@ -2808,7 +2808,8 @@ void W3DModelDraw::nukeCurrentRender(Matrix3D* xform) // save the transform for the new model if (xform) *xform = m_renderObject->Get_Transform(); - W3DDisplay::m_3DScene->Remove_Render_Object(m_renderObject); + if (W3DDisplay::m_3DScene != NULL) + W3DDisplay::m_3DScene->Remove_Render_Object(m_renderObject); REF_PTR_RELEASE(m_renderObject); m_renderObject = NULL; } @@ -3135,7 +3136,8 @@ void W3DModelDraw::setModelState(const ModelConditionInfo* newState) } // add render object to our scene - W3DDisplay::m_3DScene->Add_Render_Object(m_renderObject); + if (W3DDisplay::m_3DScene != NULL) + W3DDisplay::m_3DScene->Add_Render_Object(m_renderObject); // tie in our drawable as the user data pointer in the render object m_renderObject->Set_User_Data(draw->getDrawableInfo()); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp index d4684d871c..e133e6fadd 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp @@ -107,8 +107,11 @@ void W3DRopeDraw::buildSegments() m_color.blue, // blue 0.5f ); // transparency - W3DDisplay::m_3DScene->Add_Render_Object( info.line ); - W3DDisplay::m_3DScene->Add_Render_Object( info.softLine ); + if (W3DDisplay::m_3DScene) + { + W3DDisplay::m_3DScene->Add_Render_Object( info.line ); + W3DDisplay::m_3DScene->Add_Render_Object( info.softLine ); + } m_segments.push_back(info); } } @@ -122,12 +125,14 @@ void W3DRopeDraw::tossSegments() { if (it->line) { - W3DDisplay::m_3DScene->Remove_Render_Object(it->line); + if (W3DDisplay::m_3DScene) + W3DDisplay::m_3DScene->Remove_Render_Object(it->line); REF_PTR_RELEASE((it->line)); } if (it->softLine) { - W3DDisplay::m_3DScene->Remove_Render_Object(it->softLine); + if (W3DDisplay::m_3DScene) + W3DDisplay::m_3DScene->Remove_Render_Object(it->softLine); REF_PTR_RELEASE((it->softLine)); } } diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp index 484431e66c..31296a08c8 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp @@ -1026,7 +1026,7 @@ Int HeightMapRenderObjClass::updateBlock(Int x0, Int y0, Int x1, Int y1, WorldH DEBUG_ASSERTCRASH(y0<=y1, ("HeightMapRenderObjClass::UpdateBlock parameters have inside-out rectangle (on Y).")); #endif Invalidate_Cached_Bounding_Volumes(); - if (pMap) { + if (pMap && m_treeBuffer != NULL) { REF_PTR_SET(m_stageZeroTexture, pMap->getTerrainTexture()); REF_PTR_SET(m_stageOneTexture, pMap->getAlphaTerrainTexture()); } @@ -1326,7 +1326,7 @@ Int HeightMapRenderObjClass::initHeightData(Int x, Int y, WorldHeightMap *pMap, if (m_stageOneTexture == NULL) { needToAllocate = true; } - if (data && needToAllocate) + if (data && needToAllocate && m_treeBuffer != NULL) { //requested heightmap different from old one. freeIndexVertexBuffers(); //Create static index buffers. These will index the vertex buffers holding the map. diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp index 43c0256771..90950e4316 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp @@ -855,6 +855,9 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( */ int W3DAssetManager::Recolor_Asset(RenderObjClass *robj, const int color) { + if (TheGlobalData->m_headless) + return 0; + switch (robj->Class_ID()) { case RenderObjClass::CLASSID_MESH: return Recolor_Mesh(robj,color); @@ -871,6 +874,9 @@ int W3DAssetManager::Recolor_Asset(RenderObjClass *robj, const int color) */ int W3DAssetManager::Recolor_Mesh(RenderObjClass *robj, const int color) { + if (TheGlobalData->m_headless) + return 0; + int i; int didRecolor=0; const char *meshName; @@ -916,6 +922,9 @@ int W3DAssetManager::Recolor_Mesh(RenderObjClass *robj, const int color) int W3DAssetManager::Recolor_HLOD(RenderObjClass *robj, const int color) { + if (TheGlobalData->m_headless) + return 0; + int didRecolor=0; int num_sub = robj->Get_Num_Sub_Objects(); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp index f551296241..0674d70151 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp @@ -457,12 +457,15 @@ W3DDisplay::~W3DDisplay() // shutdown Debug_Statistics::Shutdown_Statistics(); - W3DShaderManager::shutdown(); + if (!TheGlobalData->m_headless) + W3DShaderManager::shutdown(); m_assetManager->Free_Assets(); delete m_assetManager; - WW3D::Shutdown(); + if (!TheGlobalData->m_headless) + WW3D::Shutdown(); WWMath::Shutdown(); - DX8WebBrowser::Shutdown(); + if (!TheGlobalData->m_headless) + DX8WebBrowser::Shutdown(); delete TheW3DFileSystem; TheW3DFileSystem = NULL; @@ -675,49 +678,54 @@ void W3DDisplay::init( void ) // init the Westwood math library WWMath::Init(); - // create our 3D interface scene - m_3DInterfaceScene = NEW_REF( RTS3DInterfaceScene, () ); - m_3DInterfaceScene->Set_Ambient_Light( Vector3( 1, 1, 1 ) ); + if (!TheGlobalData->m_headless) + { - // create our 2D scene - m_2DScene = NEW_REF( RTS2DScene, () ); - m_2DScene->Set_Ambient_Light( Vector3( 1, 1, 1 ) ); + // create our 3D interface scene + m_3DInterfaceScene = NEW_REF( RTS3DInterfaceScene, () ); + m_3DInterfaceScene->Set_Ambient_Light( Vector3( 1, 1, 1 ) ); - // create our 3D scene - m_3DScene =NEW_REF( RTS3DScene, () ); -#if defined(_DEBUG) || defined(_INTERNAL) - if( TheGlobalData->m_wireframe ) - m_3DScene->Set_Polygon_Mode( SceneClass::LINE ); -#endif -//============================================================================ - // m_myLight = NEW_REF -//============================================================================ - Int lindex; - for (lindex=0; lindexm_numGlobalLights; lindex++) - { m_myLight[lindex] = NEW_REF( LightClass, (LightClass::DIRECTIONAL) ); - } + // create our 2D scene + m_2DScene = NEW_REF( RTS2DScene, () ); + m_2DScene->Set_Ambient_Light( Vector3( 1, 1, 1 ) ); + + // create our 3D scene + m_3DScene =NEW_REF( RTS3DScene, () ); + #if defined(_DEBUG) || defined(_INTERNAL) + if( TheGlobalData->m_wireframe ) + m_3DScene->Set_Polygon_Mode( SceneClass::LINE ); + #endif + //============================================================================ + // m_myLight = NEW_REF + //============================================================================ + Int lindex; + for (lindex=0; lindexm_numGlobalLights; lindex++) + { m_myLight[lindex] = NEW_REF( LightClass, (LightClass::DIRECTIONAL) ); + } - setTimeOfDay( TheGlobalData->m_timeOfDay ); //set each light to correct values for given time + setTimeOfDay( TheGlobalData->m_timeOfDay ); //set each light to correct values for given time - for (lindex=0; lindexm_numGlobalLights; lindex++) - { m_3DScene->setGlobalLight( m_myLight[lindex], lindex ); - } + for (lindex=0; lindexm_numGlobalLights; lindex++) + { m_3DScene->setGlobalLight( m_myLight[lindex], lindex ); + } -#ifdef SAMPLE_DYNAMIC_LIGHT - theDynamicLight = NEW_REF(W3DDynamicLight, ()); - Real red = 1; - Real green = 1; - Real blue = 0; - if(red==0 && blue==0 && green==0) { - red = green = blue = 1; - } - theDynamicLight->Set_Ambient( Vector3( red, green, blue ) ); - theDynamicLight->Set_Diffuse( Vector3( red, green, blue) ); - theDynamicLight->Set_Position(Vector3(0, 0, 4)); - theDynamicLight->Set_Far_Attenuation_Range(1, 8); - // Note: Don't Add_Render_Object dynamic lights. - m_3DScene->addDynamicLight( theDynamicLight ); -#endif + #ifdef SAMPLE_DYNAMIC_LIGHT + theDynamicLight = NEW_REF(W3DDynamicLight, ()); + Real red = 1; + Real green = 1; + Real blue = 0; + if(red==0 && blue==0 && green==0) { + red = green = blue = 1; + } + theDynamicLight->Set_Ambient( Vector3( red, green, blue ) ); + theDynamicLight->Set_Diffuse( Vector3( red, green, blue) ); + theDynamicLight->Set_Position(Vector3(0, 0, 4)); + theDynamicLight->Set_Far_Attenuation_Range(1, 8); + // Note: Don't Add_Render_Object dynamic lights. + m_3DScene->addDynamicLight( theDynamicLight ); + #endif + + } // create a new asset manager m_assetManager = NEW W3DAssetManager; @@ -725,43 +733,35 @@ void W3DDisplay::init( void ) m_assetManager->Register_Prototype_Loader(&_AggregateLoader); m_assetManager->Set_WW3D_Load_On_Demand( true ); - - if (TheGlobalData->m_incrementalAGPBuf) + if (!TheGlobalData->m_headless) { - SortingRendererClass::SetMinVertexBufferSize(1); - } - if (WW3D::Init( ApplicationHWnd ) != WW3D_ERROR_OK) - throw ERROR_INVALID_D3D; //failed to initialize. User probably doesn't have DX 8.1 - WW3D::Set_Prelit_Mode( WW3D::PRELIT_MODE_LIGHTMAP_MULTI_PASS ); - WW3D::Set_Collision_Box_Display_Mask(0x00); ///m_incrementalAGPBuf) + { + SortingRendererClass::SetMinVertexBufferSize(1); + } + if (WW3D::Init( ApplicationHWnd ) != WW3D_ERROR_OK) + throw ERROR_INVALID_D3D; //failed to initialize. User probably doesn't have DX 8.1 + + WW3D::Set_Prelit_Mode( WW3D::PRELIT_MODE_LIGHTMAP_MULTI_PASS ); + WW3D::Set_Collision_Box_Display_Mask(0x00); ///m_windowed ); - - // create a 2D renderer helper - m_2DRender = NEW Render2DClass; - DEBUG_ASSERTCRASH( m_2DRender, ("Cannot create Render2DClass") ); - - // set our default width and height and bit depth - /// @todo we should set this according to options read from a file - setWidth( TheGlobalData->m_xResolution ); - setHeight( TheGlobalData->m_yResolution ); - setBitDepth( W3D_DISPLAY_DEFAULT_BIT_DEPTH ); - - if( WW3D::Set_Render_Device( 0, - getWidth(), - getHeight(), - getBitDepth(), - getWindowed(), - true ) != WW3D_ERROR_OK ) - { - // Getting the device at the default bit depth (32) didn't work, so try - // getting a 16 bit display. (Voodoo 1-3 only supported 16 bit.) jba. - setBitDepth( 16 ); + setWindowed( TheGlobalData->m_windowed ); + + // create a 2D renderer helper + m_2DRender = NEW Render2DClass; + DEBUG_ASSERTCRASH( m_2DRender, ("Cannot create Render2DClass") ); + + // set our default width and height and bit depth + /// @todo we should set this according to options read from a file + setWidth( TheGlobalData->m_xResolution ); + setHeight( TheGlobalData->m_yResolution ); + setBitDepth( W3D_DISPLAY_DEFAULT_BIT_DEPTH ); + if( WW3D::Set_Render_Device( 0, getWidth(), getHeight(), @@ -769,63 +769,78 @@ void W3DDisplay::init( void ) getWindowed(), true ) != WW3D_ERROR_OK ) { + // Getting the device at the default bit depth (32) didn't work, so try + // getting a 16 bit display. (Voodoo 1-3 only supported 16 bit.) jba. + setBitDepth( 16 ); + if( WW3D::Set_Render_Device( 0, + getWidth(), + getHeight(), + getBitDepth(), + getWindowed(), + true ) != WW3D_ERROR_OK ) + { - WW3D::Shutdown(); - WWMath::Shutdown(); - throw ERROR_INVALID_D3D; //failed to initialize. User probably doesn't have DX 8.1 - DEBUG_ASSERTCRASH( 0, ("Unable to set render device\n") ); - return; - } + WW3D::Shutdown(); + WWMath::Shutdown(); + throw ERROR_INVALID_D3D; //failed to initialize. User probably doesn't have DX 8.1 + DEBUG_ASSERTCRASH( 0, ("Unable to set render device\n") ); + return; + } - } // end if + } // end if - //Check if level was never set and default to setting most suitable for system. - if (TheGameLODManager->getStaticLODLevel() == STATIC_GAME_LOD_UNKNOWN) - TheGameLODManager->setStaticLODLevel(TheGameLODManager->findStaticLODLevel()); - else - { //Static LOD level was applied during GameLOD manager init except for texture reduction - //which needs to be applied here. - Int txtReduction=TheWritableGlobalData->m_textureReductionFactor; - if (txtReduction > 0) - { WW3D::Set_Texture_Reduction(txtReduction,32); - //Tell LOD manager that texture reduction was applied. - TheGameLODManager->setCurrentTextureReduction(txtReduction); + //Check if level was never set and default to setting most suitable for system. + if (TheGameLODManager->getStaticLODLevel() == STATIC_GAME_LOD_UNKNOWN) + TheGameLODManager->setStaticLODLevel(TheGameLODManager->findStaticLODLevel()); + else + { //Static LOD level was applied during GameLOD manager init except for texture reduction + //which needs to be applied here. + Int txtReduction=TheWritableGlobalData->m_textureReductionFactor; + if (txtReduction > 0) + { WW3D::Set_Texture_Reduction(txtReduction,32); + //Tell LOD manager that texture reduction was applied. + TheGameLODManager->setCurrentTextureReduction(txtReduction); + } } - } - if (TheGlobalData->m_displayGamma != 1.0f) - setGamma(TheGlobalData->m_displayGamma,0.0f,1.0f,FALSE); + if (TheGlobalData->m_displayGamma != 1.0f) + setGamma(TheGlobalData->m_displayGamma,0.0f,1.0f,FALSE); + } initAssets(); - init2DScene(); - init3DScene(); - W3DShaderManager::init(); - // Create and initialize the debug display - m_nativeDebugDisplay = NEW W3DDebugDisplay(); - m_debugDisplay = m_nativeDebugDisplay; - if ( m_nativeDebugDisplay ) + if (!TheGlobalData->m_headless) { - m_nativeDebugDisplay->init(); - GameFont *font; + init2DScene(); + init3DScene(); + W3DShaderManager::init(); - if (TheGlobalLanguageData && TheGlobalLanguageData->m_nativeDebugDisplay.name.isNotEmpty()) + // Create and initialize the debug display + m_nativeDebugDisplay = NEW W3DDebugDisplay(); + m_debugDisplay = m_nativeDebugDisplay; + if ( m_nativeDebugDisplay ) { - font=TheFontLibrary->getFont( - TheGlobalLanguageData->m_nativeDebugDisplay.name, - TheGlobalLanguageData->m_nativeDebugDisplay.size, - TheGlobalLanguageData->m_nativeDebugDisplay.bold); + m_nativeDebugDisplay->init(); + GameFont *font; + + if (TheGlobalLanguageData && TheGlobalLanguageData->m_nativeDebugDisplay.name.isNotEmpty()) + { + font=TheFontLibrary->getFont( + TheGlobalLanguageData->m_nativeDebugDisplay.name, + TheGlobalLanguageData->m_nativeDebugDisplay.size, + TheGlobalLanguageData->m_nativeDebugDisplay.bold); + } + else + font=TheFontLibrary->getFont( AsciiString("FixedSys"), 8, FALSE ); + + m_nativeDebugDisplay->setFont( font ); + m_nativeDebugDisplay->setFontHeight( 13 ); + m_nativeDebugDisplay->setFontWidth( 9 ); } - else - font=TheFontLibrary->getFont( AsciiString("FixedSys"), 8, FALSE ); - m_nativeDebugDisplay->setFont( font ); - m_nativeDebugDisplay->setFontHeight( 13 ); - m_nativeDebugDisplay->setFontWidth( 9 ); + DX8WebBrowser::Initialize(); } - DX8WebBrowser::Initialize(); - // we're now online m_initialized = true; if( TheGlobalData->m_displayDebug ) @@ -845,16 +860,19 @@ void W3DDisplay::reset( void ) // Remove all render objects. - SceneIterator *sceneIter = m_3DScene->Create_Iterator(); - sceneIter->First(); - while(!sceneIter->Is_Done()) { - RenderObjClass * robj = sceneIter->Current_Item(); - robj->Add_Ref(); - m_3DScene->Remove_Render_Object(robj); - robj->Release_Ref(); - sceneIter->Next(); + if (m_3DScene != NULL) + { + SceneIterator *sceneIter = m_3DScene->Create_Iterator(); + sceneIter->First(); + while(!sceneIter->Is_Done()) { + RenderObjClass * robj = sceneIter->Current_Item(); + robj->Add_Ref(); + m_3DScene->Remove_Render_Object(robj); + robj->Release_Ref(); + sceneIter->Next(); + } + m_3DScene->Destroy_Iterator(sceneIter); } - m_3DScene->Destroy_Iterator(sceneIter); m_isClippedEnabled = FALSE; @@ -1671,6 +1689,8 @@ void W3DDisplay::draw( void ) return; } + if (TheGlobalData->m_headless) + return; updateAverageFPS(); if (TheGlobalData->m_enableDynamicLOD && TheGameLogic->getShowDynamicLOD()) @@ -2082,7 +2102,7 @@ void W3DDisplay::createLightPulse( const Coord3D *pos, const RGBColor *color, UnsignedInt decayFrameTime//, Bool donut ) { - if (innerRadius+attenuationWidth<2.0*PATHFIND_CELL_SIZE_F + 1.0f) { + if (innerRadius+attenuationWidth<2.0*PATHFIND_CELL_SIZE_F + 1.0f || m_3DScene == NULL) { return; // it basically won't make any visual difference. jba. } W3DDynamicLight * theDynamicLight = m_3DScene->getADynamicLight(); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp index 41b22b57fa..8f2ffea548 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp @@ -232,22 +232,24 @@ void W3DTerrainVisual::init( void ) m_terrainRenderObject->Set_Collision_Type( PICK_TYPE_TERRAIN ); TheTerrainRenderObject = m_terrainRenderObject; - // initialize track drawing system - TheTerrainTracksRenderObjClassSystem = NEW TerrainTracksRenderObjClassSystem; - TheTerrainTracksRenderObjClassSystem->init(W3DDisplay::m_3DScene); + if (!TheGlobalData->m_headless) + { + // initialize track drawing system + TheTerrainTracksRenderObjClassSystem = NEW TerrainTracksRenderObjClassSystem; + TheTerrainTracksRenderObjClassSystem->init(W3DDisplay::m_3DScene); - // initialize object shadow drawing system - TheW3DShadowManager = NEW W3DShadowManager; - TheW3DShadowManager->init(); + // initialize object shadow drawing system + TheW3DShadowManager = NEW W3DShadowManager; + TheW3DShadowManager->init(); - // create a water plane render object - TheWaterRenderObj=m_waterRenderObject = NEW_REF( WaterRenderObjClass, () ); - m_waterRenderObject->init(TheGlobalData->m_waterPositionZ, TheGlobalData->m_waterExtentX, TheGlobalData->m_waterExtentY, W3DDisplay::m_3DScene, (WaterRenderObjClass::WaterType)TheGlobalData->m_waterType); //create a water plane that's 128x128 units - m_waterRenderObject->Set_Position(Vector3(TheGlobalData->m_waterPositionX,TheGlobalData->m_waterPositionY,TheGlobalData->m_waterPositionZ)); //place water in world + // create a water plane render object + TheWaterRenderObj=m_waterRenderObject = NEW_REF( WaterRenderObjClass, () ); + m_waterRenderObject->init(TheGlobalData->m_waterPositionZ, TheGlobalData->m_waterExtentX, TheGlobalData->m_waterExtentY, W3DDisplay::m_3DScene, (WaterRenderObjClass::WaterType)TheGlobalData->m_waterType); //create a water plane that's 128x128 units + m_waterRenderObject->Set_Position(Vector3(TheGlobalData->m_waterPositionX,TheGlobalData->m_waterPositionY,TheGlobalData->m_waterPositionZ)); //place water in world - // create smudge rendering system. - TheSmudgeManager = NEW(W3DSmudgeManager); - TheSmudgeManager->init(); + // create smudge rendering system. + TheSmudgeManager = NEW(W3DSmudgeManager); + TheSmudgeManager->init(); #ifdef DO_UNIT_TIMINGS #pragma MESSAGE("********************* WARNING- Doing UNIT TIMINGS. ") @@ -261,10 +263,11 @@ void W3DTerrainVisual::init( void ) W3DDisplay::m_3DScene->Add_Render_Object( m_waterRenderObject); } #endif - if (TheGlobalData->m_useCloudPlane) - m_waterRenderObject->toggleCloudLayer(true); - else - m_waterRenderObject->toggleCloudLayer(false); + if (TheGlobalData->m_useCloudPlane) + m_waterRenderObject->toggleCloudLayer(true); + else + m_waterRenderObject->toggleCloudLayer(false); + } // set the vertex animated water properties Int waterSettingIndex = 0; // use index 0 settings by default @@ -634,7 +637,7 @@ Bool W3DTerrainVisual::load( AsciiString filename ) } - RefRenderObjListIterator *it = W3DDisplay::m_3DScene->createLightsIterator(); + RefRenderObjListIterator *it = W3DDisplay::m_3DScene ? W3DDisplay::m_3DScene->createLightsIterator() : NULL; // apply the heightmap to the terrain render object #ifdef DO_SEISMIC_SIMULATIONS @@ -655,13 +658,17 @@ Bool W3DTerrainVisual::load( AsciiString filename ) it = NULL; } // add our terrain render object to the scene - W3DDisplay::m_3DScene->Add_Render_Object( m_terrainRenderObject ); + if (W3DDisplay::m_3DScene != NULL) + W3DDisplay::m_3DScene->Add_Render_Object( m_terrainRenderObject ); #if defined _DEBUG || defined _INTERNAL // Icon drawing utility object for pathfinding. - W3DDebugIcons *icons = NEW W3DDebugIcons; - W3DDisplay::m_3DScene->Add_Render_Object( icons ); - icons->Release_Ref(); // belongs to scene. + if (W3DDisplay::m_3DScene != NULL) + { + W3DDebugIcons *icons = NEW W3DDebugIcons; + W3DDisplay::m_3DScene->Add_Render_Object( icons ); + icons->Release_Ref(); // belongs to scene. + } #endif #ifdef DO_UNIT_TIMINGS diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp index 906ebb2c1a..332947c69c 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp @@ -616,7 +616,7 @@ void W3DView::setCameraTransform( void ) buildCameraTransform( &cameraTransform ); m_3DCamera->Set_Transform( cameraTransform ); - if (TheTerrainRenderObject) + if (TheTerrainRenderObject && W3DDisplay::m_3DScene != NULL) { RefRenderObjListIterator *it = W3DDisplay::m_3DScene->createLightsIterator(); TheTerrainRenderObject->updateCenter(m_3DCamera, it); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DGhostObject.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DGhostObject.cpp index 0af9735af9..645ae89227 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DGhostObject.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DGhostObject.cpp @@ -155,7 +155,8 @@ void W3DRenderObjectSnapshot::update(RenderObjClass *robj, DrawableInfo *drawInf // ------------------------------------------------------------------------------------------------ void W3DRenderObjectSnapshot::addToScene(void) { - ((SimpleSceneClass *)W3DDisplay::m_3DScene)->Add_Render_Object(m_robj); + if (W3DDisplay::m_3DScene != NULL) + ((SimpleSceneClass *)W3DDisplay::m_3DScene)->Add_Render_Object(m_robj); } // ------------------------------------------------------------------------------------------------ @@ -457,7 +458,7 @@ void W3DGhostObject::restoreParentObject(void) //removed by the ghost object manager, so restore it. If we have a render //object that is in the scene, then it was probably added because the model //changed while the object was ghosted (for damage states, garrison, etc.). - if (robj->Peek_Scene() == NULL) + if (robj->Peek_Scene() == NULL && W3DDisplay::m_3DScene != NULL) ((SimpleSceneClass *)W3DDisplay::m_3DScene)->Add_Render_Object(robj); } } diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DTerrainLogic.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DTerrainLogic.cpp index 8682b13c2d..e3d44ce007 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DTerrainLogic.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DTerrainLogic.cpp @@ -281,6 +281,8 @@ Real W3DTerrainLogic::getGroundHeight( Real x, Real y, Coord3D* normal ) const { #define USE_THE_TERRAIN_OBJECT #ifdef USE_THE_TERRAIN_OBJECT + // TheSuperHackers @logic-client-separation helmutbuhler 04/11/2025 + // W3DTerrainLogic shouldn't depend on TheTerrainRenderObject! if (TheTerrainRenderObject) { return TheTerrainRenderObject->getHeightMapHeight(x,y,normal); @@ -358,7 +360,8 @@ Real W3DTerrainLogic::getLayerHeight( Real x, Real y, PathfindLayerEnum layer, C //------------------------------------------------------------------------------------------------- Bool W3DTerrainLogic::isCliffCell( Real x, Real y) const { - + // TheSuperHackers @logic-client-separation helmutbuhler 04/11/2025 + // W3DTerrainLogic shouldn't depend on TheTerrainRenderObject! return TheTerrainRenderObject->isCliffCell(x,y); } // end isCliffCell diff --git a/GeneralsMD/Code/Main/WinMain.cpp b/GeneralsMD/Code/Main/WinMain.cpp index ca0ffcf603..98850531ea 100644 --- a/GeneralsMD/Code/Main/WinMain.cpp +++ b/GeneralsMD/Code/Main/WinMain.cpp @@ -745,8 +745,7 @@ static Bool initializeAppWindows( HINSTANCE hInstance, Int nCmdShow, Bool runWin ShowWindow( hWnd, nCmdShow ); UpdateWindow( hWnd ); - // save our application instance and window handle for future use - ApplicationHInstance = hInstance; + // save our application window handle for future use ApplicationHWnd = hWnd; gInitializing = false; if (!runWindowed) { @@ -885,13 +884,21 @@ Int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, char * argv[20]; argv[0] = NULL; + Bool headless = false; + char *token; token = nextParam(lpCmdLine, "\" "); while (argc < 20 && token != NULL) { argv[argc++] = strtrim(token); + //added a preparse step for this flag because it affects window creation style - if (stricmp(token,"-win")==0) - ApplicationIsWindowed=true; + if (stricmp(token, "-win") == 0) + ApplicationIsWindowed = true; + + // preparse for headless as well. We need to know about this before we create the window. + if (stricmp(token, "-headless") == 0) + headless = true; + token = nextParam(NULL, "\" "); } @@ -950,10 +957,12 @@ Int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, gLoadScreenBitmap = (HBITMAP)LoadImage(hInstance, "Install_Final.bmp", IMAGE_BITMAP, 0, 0, LR_SHARED|LR_LOADFROMFILE); #endif - // register windows class and create application window - if( initializeAppWindows( hInstance, nCmdShow, ApplicationIsWindowed) == false ) + if(!headless && initializeAppWindows(hInstance, nCmdShow, ApplicationIsWindowed) == false) return 0; + + // save our application instance for future use + ApplicationHInstance = hInstance; if (gLoadScreenBitmap!=NULL) { ::DeleteObject(gLoadScreenBitmap);