diff --git a/GeneralsMD/Code/GameEngine/Include/Common/GameAudio.h b/GeneralsMD/Code/GameEngine/Include/Common/GameAudio.h index 49581fb8d4..fea92a0ee1 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/GameAudio.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/GameAudio.h @@ -372,6 +372,57 @@ class AudioManager : public SubsystemInterface Bool m_disallowSpeech : 1; }; +// TheSuperHackers @feature helmutbuhler 17/05/2025 +// AudioManager that does nothing. Used for Headless Mode. +class AudioManagerDummy : public AudioManager +{ +#if defined(RTS_DEBUG) || defined(RTS_INTERNAL) + virtual void audioDebugDisplay(DebugDisplayInterface* dd, void* userData, FILE* fp) {} +#endif + virtual void stopAudio(AudioAffect which) {} + virtual void pauseAudio(AudioAffect which) {} + virtual void resumeAudio(AudioAffect which) {} + virtual void pauseAmbient(Bool shouldPause) {} + virtual void killAudioEventImmediately(AudioHandle audioEvent) {} + virtual void nextMusicTrack() {} + virtual void prevMusicTrack() {} + virtual Bool isMusicPlaying() const { return false; } + virtual Bool hasMusicTrackCompleted(const AsciiString& trackName, Int numberOfTimes) const { return false; } + virtual AsciiString getMusicTrackName() const { return ""; } + virtual void openDevice() {} + virtual void closeDevice() {} + virtual void* getDevice() { return NULL; } + virtual void notifyOfAudioCompletion(UnsignedInt audioCompleted, UnsignedInt flags) {} + virtual UnsignedInt getProviderCount(void) const { return 0; }; + virtual AsciiString getProviderName(UnsignedInt providerNum) const { return ""; } + virtual UnsignedInt getProviderIndex(AsciiString providerName) const { return 0; } + virtual void selectProvider(UnsignedInt providerNdx) {} + virtual void unselectProvider(void) {} + virtual UnsignedInt getSelectedProvider(void) const { return 0; } + virtual void setSpeakerType(UnsignedInt speakerType) {} + virtual UnsignedInt getSpeakerType(void) { return 0; } + virtual UnsignedInt getNum2DSamples(void) const { return 0; } + virtual UnsignedInt getNum3DSamples(void) const { return 0; } + virtual UnsignedInt getNumStreams(void) const { return 0; } + virtual Bool doesViolateLimit(AudioEventRTS* event) const { return false; } + virtual Bool isPlayingLowerPriority(AudioEventRTS* event) const { return false; } + virtual Bool isPlayingAlready(AudioEventRTS* event) const { return false; } + virtual Bool isObjectPlayingVoice(UnsignedInt objID) const { return false; } + virtual void adjustVolumeOfPlayingAudio(AsciiString eventName, Real newVolume) {} + virtual void removePlayingAudio(AsciiString eventName) {} + virtual void removeAllDisabledAudio() {} + virtual Bool has3DSensitiveStreamsPlaying(void) const { return false; } + virtual void* getHandleForBink(void) { return NULL; } + virtual void releaseHandleForBink(void) {} + virtual void friend_forcePlayAudioEventRTS(const AudioEventRTS* eventToPlay) {} + virtual void setPreferredProvider(AsciiString providerNdx) {} + virtual void setPreferredSpeaker(AsciiString speakerType) {} + virtual Real getFileLengthMS(AsciiString strToLoad) const { return -1; } + virtual void closeAnySamplesUsingFile(const void* fileToClose) {} + virtual void setDeviceListenerPosition(void) {} +}; + + extern AudioManager *TheAudio; #endif // __COMMON_GAMEAUDIO_H_ diff --git a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h index 73e3eadb21..5c7957e88f 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) @@ -1206,6 +1215,10 @@ static CommandLineParam params[] = { "-quickstart", parseQuickStart }, { "-useWaveEditor", parseUseWaveEditor }, + // TheSuperHackers @feature helmutbuhler 11/04/2025 + // This runs the game without a window, graphics, input and audio. Used for testing. + { "-headless", parseHeadless }, + #if (defined(RTS_DEBUG) || defined(RTS_INTERNAL)) { "-noaudio", parseNoAudio }, { "-map", parseMapName }, diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp index 9e79d39979..3a23c83a3b 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp @@ -431,7 +431,7 @@ void GameEngine::init( int argc, char *argv[] ) startTime64 = endTime64;//Reset the clock //////////////////////////////////////////////////////// DEBUG_LOG(("%s", Buf));//////////////////////////////////////////////////////////////////////////// #endif///////////////////////////////////////////////////////////////////////////////////////////// - initSubsystem(TheAudio,"TheAudio", createAudioManager(), NULL); + initSubsystem(TheAudio,"TheAudio", TheGlobalData->m_headless ? NEW AudioManagerDummy : createAudioManager(), NULL); if (!TheAudio->isMusicAlreadyLoaded()) setQuitting(TRUE); @@ -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 21875cdab6..bf4bc3d5c9 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 eb5f09c6ad..5fa23da03b 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 b8ebaa8488..5c377d6ffe 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 }, + { "GameWindowDummy", 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 663491e05c..56e8a6d56b 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/Gadget/GadgetListBox.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Gadget/GadgetListBox.cpp index 17d9b65619..a1aeefb4d7 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Gadget/GadgetListBox.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Gadget/GadgetListBox.cpp @@ -2221,6 +2221,8 @@ Int GadgetListBoxAddEntryText( GameWindow *listbox, addInfo.width = -1; ListboxData *listData = (ListboxData *)listbox->winGetUserData(); + if (listData == NULL) + return -1; Bool wasFull = (listData->listLength <= listData->endPos); Int newEntryOffset = (wasFull)?0:1; Int oldBottomIndex = GadgetListBoxGetBottomVisibleEntry(listbox); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp index 4be961bc34..e2c9a84b0b 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 *GameWindowManagerDummy::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 *GameWindowManagerDummy::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; +} + +GameWindowDummy::~GameWindowDummy() +{ +} + + + diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp index ee53c2779e..b7b15cced5 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; @@ -322,7 +325,7 @@ void GameClient::init( void ) TheFontLibrary->init(); // create the mouse - TheMouse = createMouse(); + TheMouse = TheGlobalData->m_headless ? NEW MouseDummy : createMouse(); TheMouse->parseIni(); TheMouse->initCursorResources(); TheMouse->setName("TheMouse"); @@ -340,7 +343,7 @@ void GameClient::init( void ) } // create the window manager - TheWindowManager = createWindowManager(); + TheWindowManager = TheGlobalData->m_headless ? NEW GameWindowManagerDummy : createWindowManager(); if( TheWindowManager ) { @@ -535,7 +538,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 )// Remove headless-check with Replay Simulation PR { TheWritableGlobalData->m_allowExitOutOfMovies = TRUE; if(TheGameLODManager && TheGameLODManager->didMemPass()) @@ -626,10 +629,9 @@ void GameClient::update( void ) if(TheGlobalData->m_playIntro || TheGlobalData->m_afterIntro) { // redraw all views, update the GUI + if (!TheGlobalData->m_headless)// Remove headless-check with Replay Simulation PR { TheDisplay->DRAW(); - } - { TheDisplay->UPDATE(); } return; @@ -749,10 +751,12 @@ void GameClient::update( void ) } // update display + if (!TheGlobalData->m_headless)// Remove headless-check with Replay Simulation PR { TheDisplay->UPDATE(); } + if (!TheGlobalData->m_headless)// Remove headless-check with Replay Simulation PR { USE_PERF_TIMER(GameClient_draw) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp index 27f96ef64d..fabf6fa46e 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 71b18c5d98..540cdfef09 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -6094,6 +6094,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 75a770a214..4478c038ed 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! + // TheSuperHackers @fix helmutbuhler 19/04/2025 Zero initialize array to prevent uninitialized memory reads. // Important: the entire target vector is used for serialization and crc and must not contain random data. Coord3D approachBones[DEFAULT_APPROACH_VECTOR_SIZE] = {0}; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp index fddde9868a..97b0f03a92 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/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index 74718ca7bf..c19b42f9b9 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -736,6 +736,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; @@ -1266,7 +1269,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) @@ -1981,6 +1984,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; @@ -2186,7 +2192,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()) @@ -2226,14 +2232,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 18d7ab1c73..c3430b2a7a 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"); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp index ba8add5ebb..bc7601a24d 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 + m_bridgeBuffer = NEW W3DBridgeBuffer; + + 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_waypointBuffer = NEW W3DWaypointBuffer; +#ifdef DO_ROADS + m_roadBuffer = NEW W3DRoadBuffer; #endif #if defined(RTS_DEBUG) || defined(RTS_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 31ef5c6582..d062579a00 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 54e6f71fa0..3cf5234843 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 50d3ba8eb0..1cb9aebe9a 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 95bd772b47..16f5ece863 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 01a5038249..ef969e2087 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/W3DBridgeBuffer.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DBridgeBuffer.cpp index df141f96c9..aaf0378b3d 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DBridgeBuffer.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DBridgeBuffer.cpp @@ -768,6 +768,8 @@ void W3DBridgeBuffer::freeBridgeBuffers(void) //============================================================================= void W3DBridgeBuffer::allocateBridgeBuffers(void) { + if (TheGlobalData->m_headless) + return; m_vertexBridge=NEW_REF(DX8VertexBufferClass,(DX8_FVF_XYZNDUV1,MAX_BRIDGE_VERTEX+4,DX8VertexBufferClass::USAGE_DYNAMIC)); m_indexBridge=NEW_REF(DX8IndexBufferClass,(MAX_BRIDGE_INDEX+4, DX8IndexBufferClass::USAGE_DYNAMIC)); m_vertexMaterial=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE); @@ -808,6 +810,8 @@ void W3DBridgeBuffer::clearAllBridges(void) //============================================================================= void W3DBridgeBuffer::loadBridges(W3DTerrainLogic *pTerrainLogic, Bool saveGame) { + // TheSuperHackers @logic-client-separation helmutbuhler 26/4/2025 + // W3DBridgeBuffer shouldn't add objects to W3DTerrainLogic clearAllBridges(); MapObject *pMapObj; MapObject *pMapObj2; diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp index 7b5a8bddc7..39cf955ec0 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp @@ -464,12 +464,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; @@ -682,49 +685,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(RTS_DEBUG) || defined(RTS_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(RTS_DEBUG) || defined(RTS_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; @@ -732,43 +740,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(), @@ -776,63 +776,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 ) @@ -852,16 +867,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; @@ -1678,6 +1696,8 @@ void W3DDisplay::draw( void ) return; } + if (TheGlobalData->m_headless) + return; updateAverageFPS(); if (TheGlobalData->m_enableDynamicLOD && TheGameLogic->getShowDynamicLOD()) @@ -2089,6 +2109,8 @@ void W3DDisplay::createLightPulse( const Coord3D *pos, const RGBColor *color, UnsignedInt decayFrameTime//, Bool donut ) { + if (m_3DScene == NULL) + return; if (innerRadius+attenuationWidth<2.0*PATHFIND_CELL_SIZE_F + 1.0f) { return; // it basically won't make any visual difference. jba. } diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp index 0204f68aae..71bd8a9a3c 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 RTS_DEBUG || defined RTS_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 ac7bbe9792..5ed7c1f8af 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp @@ -560,6 +560,8 @@ void W3DView::getPickRay(const ICoord2D *screen, Vector3 *rayStart, Vector3 *ray //------------------------------------------------------------------------------------------------- void W3DView::setCameraTransform( void ) { + if (TheGlobalData->m_headless) + return; m_cameraHasMovedSinceRequest = true; Matrix3D cameraTransform( 1 ); @@ -616,7 +618,7 @@ void W3DView::setCameraTransform( void ) buildCameraTransform( &cameraTransform ); m_3DCamera->Set_Transform( cameraTransform ); - if (TheTerrainRenderObject) + if (TheTerrainRenderObject) { RefRenderObjListIterator *it = W3DDisplay::m_3DScene->createLightsIterator(); TheTerrainRenderObject->updateCenter(m_3DCamera, it); @@ -1817,7 +1819,7 @@ void W3DView::scrollBy( Coord2D *delta ) start.X = getWidth(); start.Y = getHeight(); - Real aspect = getWidth()/getHeight(); + Real aspect = getHeight() == 0 ? 1 : getWidth()/getHeight(); end.X = start.X + delta->x * SCROLL_RESOLUTION; end.Y = start.Y + delta->y * SCROLL_RESOLUTION*aspect; diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DGhostObject.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DGhostObject.cpp index 8dd1de1519..a3e4ed6aae 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DGhostObject.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DGhostObject.cpp @@ -35,6 +35,7 @@ #include "Common/PlayerList.h" #include "Common/ThingTemplate.h" #include "Common/Xfer.h" +#include "Common/GlobalData.h" #include "GameClient/Drawable.h" #include "GameClient/GameClient.h" #include "GameLogic/GameLogic.h" @@ -155,7 +156,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); } // ------------------------------------------------------------------------------------------------ @@ -433,6 +435,8 @@ void W3DGhostObject::removeParentObject(void) // ------------------------------------------------------------------------------------------------ void W3DGhostObject::restoreParentObject(void) { + if (TheGlobalData->m_headless) + return; Drawable *draw=m_parentObject->getDrawable(); if (!draw) return; diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DTerrainLogic.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DTerrainLogic.cpp index f174eb245b..181fb1215c 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 11/04/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 11/04/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 b365a7ec9a..c7877ecb23 100644 --- a/GeneralsMD/Code/Main/WinMain.cpp +++ b/GeneralsMD/Code/Main/WinMain.cpp @@ -746,8 +746,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) { @@ -886,13 +885,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, "\" "); } @@ -951,10 +958,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);