/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 BFG Edition Source Code. If not, see . In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #include "Precompiled.h" #include "globaldata.h" #include "doomlib.h" #include #include "Main.h" #include "sys/sys_session.h" #include "idlib/Thread.h" #include // Store master volume settings in archived cvars, becausue we want them to apply // even if a user isn't signed in. // The range is from 0 to 15, which matches the setting in vanilla DOOM. idCVar s_volume_sound( "s_volume_sound", "8", CVAR_ARCHIVE | CVAR_INTEGER, "sound volume", 0, 15 ); idCVar s_volume_midi( "s_volume_midi", "8", CVAR_ARCHIVE | CVAR_INTEGER, "music volume", 0, 15 ); idCVar m_show_messages( "m_show_messages", "1", CVAR_ARCHIVE | CVAR_INTEGER, "show messages", 0, 1 ); idCVar m_inDemoMode( "m_inDemoMode", "1", CVAR_INTEGER, "in demo mode", 0, 1 ); bool globalNetworking = false; bool globalPauseTime = false; int PLAYERCOUNT = 1; #ifdef _DEBUG bool debugOutput = true; #else bool debugOutput = false; #endif namespace DoomLib { static const char * Expansion_Names[] = { "Ultimate DOOM", "DOOM II: Hell On Earth", "Final DOOM: TNT Evilution", "Final DOOM: Plutonia Experiment", "DOOM II: Master Levels", "DOOM II: No Rest For The Living" }; static const char* Skill_Names[] = { "I'm Too Young To Die!", "Hey, Not Too Rough!", "Hurt Me Plenty!", "Ultra-Violence", "Nightmare" }; static const char* Filter_Names[] = { "#str_friends", "#str_around", "#str_top15" }; // Game-specific setup values. static const char * Doom_MapNames[] = { "E1M1: Hangar", "E1M2: Nuclear Plant", "E1M3: Toxin Refinery", "E1M4: Command Control", "E1M5: Phobos Lab", "E1M6: Central Processing", "E1M7: Computer Station", "E1M8: Phobos Anomaly", "E1M9: Military Base", "E2M1: Deimos Anomaly", "E2M2: Containment Area", "E2M3: Refinery", "E2M4: Deimos Lab", "E2M5: Command Center", "E2M6: Halls of the Damned", "E2M7: Spawning Vats", "E2M8: Tower of Babel", "E2M9: Fortress of Mystery", "E3M1: Hell Keep", "E3M2: Slough of Despair", "E3M3: Pandemonium", "E3M4: House of Pain", "E3M5: Unholy Cathedral", "E3M6: MT. Erebus", "E3M7: Gate to Limbo", "E3M8: DIS", "E3M9: Warrens", "E4M1: Hell Beneath", "E4M2: Perfect Hatred", "E4M3: Sever The Wicked", "E4M4: Unruly Evil", "E4M5: They Will Repent", "E4M6: Against Thee Wickedly", "E4M7: And Hell Followed", "E4M8: Unto The Cruel", "E4M9: Fear" }; static const char * Doom2_MapNames[] = { "1: Entryway", "2: Underhalls", "3: The Gantlet", "4: The Focus", "5: The Waste Tunnels", "6: The Crusher", "7: Dead Simple", "8: Tricks and Traps", "9: The Pit", "10: Refueling Base", "11: Circle of Death", "12: The Factory", "13: Downtown", "14: The Inmost Dens", "15: Industrial Zone", "16: Suburbs", "17: Tenements", "18: The Courtyard", "19: The Citadel", "20: Gotcha!", "21: Nirvana", "22: The Catacombs", "23: Barrels O' Fun", "24: The Chasm", "25: Bloodfalls", "26: The Abandoned Mines", "27: Monster Condo", "28: The Spirit World", "29: The Living End", "30: Icon of Sin", "31: IDKFA", "32: Keen" }; static const char * TNT_MapNames[] = { "1: System Control", "2: Human BBQ", "3: Power Control", "4: Wormhole", "5: Hangar", "6: Open Season", "7: Prison", "8: Metal", "9: Stronghold", "10: Redemption", "11: Storage Facility", "12: Crater", "13: Nukage Processing", "14: Steel Works", "15: Dead Zone", "16: Deepest Reaches", "17: Processing Area", "18: Mill", "19: Shipping & Respawning", "20: Central Processing", "21: Administration Center", "22: Habitat", "23: Lunar Mining Project", "24: Quarry", "25: Baron's Den", "26: Ballistyx", "27: Mount Pain", "28: Heck", "29: River Styx", "30: Last Call", "31: Pharaoh", "32: Caribbean" }; static const char * Plut_MapNames[] = { "1: Congo", "2: Well of Souls", "3: Aztec", "4: Caged", "5: Ghost Town", "6: Baron's Lair", "7: Caughtyard", "8: Realm", "9: Abattoire", "10: Onslaught", "11: Hunted", "12: Speed", "13: The Crypt", "14: Genesis", "15: The Twilight", "16: The Omen", "17: Compound", "18: Neurosphere", "19: NME", "20: The Death Domain", "21: Slayer", "22: Impossible Mission", "23: Tombstone", "24: The Final Frontier", "25: The Temple of Darkness", "26: Bunker", "27: Anti-Christ", "28: The Sewers", "29: Odyssey of Noises", "30: The Gateway of Hell", "31: Cyberden", "32: Go 2 It" }; static const char * Mast_MapNames[] = { "1: Attack", "2: Canyon","3: The Catwalk", "4: The Combine", "5: The Fistula", "6: The Garrison", "7: Titan Manor", "8: Paradox", "9: Subspace", "10: Subterra", "11: Trapped On Titan", "12: Virgil's Lead", "13: Minos' Judgement", "14: Bloodsea Keep", "15: Mephisto's Maosoleum", "16: Nessus", "17: Geryon", "18: Vesperas", "19: Black Tower", "20: The Express Elevator To Hell", "21: Bad Dream" }; static const char * Nerve_MapNames[] = { "1: The Earth Base", "2: The Pain Labs", "3: Canyon of the Dead", "4: Hell Mountain", "5: Vivisection", "6: Inferno of Blood", "7: Baron's Banquet", "8: Tomb of Malevolence", "9: March of Demons" }; const ExpansionData App_Expansion_Data_Local[] = { { ExpansionData::IWAD, retail, doom, "DOOM", DOOMWADDIR"DOOM.WAD", NULL, "base/textures/DOOMICON.PNG" , Doom_MapNames }, { ExpansionData::IWAD, commercial, doom2, "DOOM 2", DOOMWADDIR"DOOM2.WAD", NULL, "base/textures/DOOM2ICON.PNG" , Doom2_MapNames }, { ExpansionData::IWAD, commercial, pack_tnt, "FINAL DOOM: TNT EVILUTION", DOOMWADDIR"TNT.WAD", NULL, "base/textures/TNTICON.PNG" , TNT_MapNames }, { ExpansionData::IWAD, commercial, pack_plut, "FINAL DOOM: PLUTONIA EXPERIMENT", DOOMWADDIR"PLUTONIA.WAD", NULL, "base/textures/PLUTICON.PNG" , Plut_MapNames }, { ExpansionData::PWAD, commercial, pack_master, "DOOM 2: MASTER LEVELS", DOOMWADDIR"DOOM2.WAD", DOOMWADDIR"MASTERLEVELS.WAD", "base/textures/MASTICON.PNG" , Mast_MapNames }, { ExpansionData::PWAD, commercial, pack_nerve, "DOOM 2: NO REST FOR THE LIVING", DOOMWADDIR"DOOM2.WAD", DOOMWADDIR"NERVE.WAD", "base/textures/NERVEICON.PNG" , Nerve_MapNames }, }; int classicRemap[K_LAST_KEY]; const ExpansionData * GetCurrentExpansion() { return &App_Expansion_Data_Local[ DoomLib::expansionSelected ]; } void SetCurrentExpansion( int expansion ) { expansionDirty = true; expansionSelected = expansion; } void SetIdealExpansion( int expansion ) { idealExpansion = expansion; } idStr currentMapName; idStr currentDifficulty; void SetCurrentMapName( idStr name ) { currentMapName = name; } const idStr & GetCurrentMapName() { return currentMapName; } void SetCurrentDifficulty( idStr name ) { currentDifficulty = name; } const idStr & GetCurrentDifficulty() { return currentDifficulty; } int currentplayer = -1; Globals *globaldata[4]; RecvFunc Recv; SendFunc Send; SendRemoteFunc SendRemote; bool Active = true; DoomInterface Interface; int idealExpansion = 0; int expansionSelected = 0; bool expansionDirty = true; bool skipToLoad = false; char loadGamePath[MAX_PATH]; bool skipToNew = false; int chosenSkill = 0; int chosenEpisode = 1; idMatchParameters matchParms; void * (*Z_Malloc)( int size, int tag, void* user ) = NULL; void (*Z_FreeTag)(int lowtag ); idArray< idSysMutex, 4 > playerScreenMutexes; void ExitGame() { // TODO: If we ever support splitscreen and online, // we'll have to call D_QuitNetGame for all local players. DoomLib::SetPlayer( 0 ); D_QuitNetGame(); session->QuitMatch(); } void ShowXToContinue( bool activate ) { } /* ======================== DoomLib::GetGameSKU ======================== */ gameSKU_t GetGameSKU() { if ( common->GetCurrentGame() == DOOM_CLASSIC ) { return GAME_SKU_DOOM1_BFG; } else if ( common->GetCurrentGame() == DOOM2_CLASSIC ) { return GAME_SKU_DOOM2_BFG; } assert( false && "Invalid basepath" ); return GAME_SKU_DCC; } /* ======================== DoomLib::ActivateGame ======================== */ void ActivateGame() { Active = true; // Turn off menu toggler int originalPlayer = DoomLib::GetPlayer(); for ( int i = 0; i < Interface.GetNumPlayers(); i++ ) { DoomLib::SetPlayer(i); ::g->menuactive = false; } globalPauseTime = false; DoomLib::SetPlayer( originalPlayer ); } /* ======================== DoomLib::HandleEndMatch ======================== */ void HandleEndMatch() { if ( session->GetGameLobbyBase().IsHost() ) { ShowXToContinue( false ); session->EndMatch(); } } }; extern void I_InitGraphics(); extern void D_DoomMain(); extern bool D_DoomMainPoll(); extern void I_InitInput(); extern void D_RunFrame( bool ); extern void I_ShutdownSound(); extern void I_ShutdownMusic(); extern void I_ShutdownGraphics(); extern void I_ProcessSoundEvents( void ); void DoomLib::InitGlobals( void *ptr /* = NULL */ ) { if (ptr == NULL) ptr = new Globals; globaldata[currentplayer] = static_cast(ptr); memset( globaldata[currentplayer], 0, sizeof(Globals) ); g = globaldata[currentplayer]; g->InitGlobals(); } void *DoomLib::GetGlobalData( int player ) { return globaldata[player]; } void DoomLib::InitControlRemap() { memset( classicRemap, K_NONE, sizeof( classicRemap ) ); classicRemap[K_JOY3] = KEY_TAB ; classicRemap[K_JOY4] = K_MINUS; classicRemap[K_JOY2] = K_EQUALS; classicRemap[K_JOY9] = K_ESCAPE ; classicRemap[K_JOY_STICK1_UP] = K_UPARROW ; classicRemap[K_JOY_DPAD_UP] = K_UPARROW ; classicRemap[K_JOY_STICK1_DOWN] = K_DOWNARROW ; classicRemap[K_JOY_DPAD_DOWN] = K_DOWNARROW ; classicRemap[K_JOY_STICK1_LEFT] = K_LEFTARROW ; classicRemap[K_JOY_DPAD_LEFT] = K_LEFTARROW ; classicRemap[K_JOY_STICK1_RIGHT] = K_RIGHTARROW ; classicRemap[K_JOY_DPAD_RIGHT] = K_RIGHTARROW ; classicRemap[K_JOY1] = K_ENTER; } keyNum_t DoomLib::RemapControl( keyNum_t key ) { if( classicRemap[ key ] == K_NONE ) { return key; } else { if( ::g->menuactive && key == K_JOY2 ) { return K_BACKSPACE; } return (keyNum_t)classicRemap[ key ]; } } void DoomLib::InitGame( int argc, char** argv ) { ::g->myargc = argc; ::g->myargv = argv; InitControlRemap(); D_DoomMain(); } bool DoomLib::Poll() { return D_DoomMainPoll(); } bool TryRunTics( idUserCmdMgr * userCmdMgr ); bool DoomLib::Tic( idUserCmdMgr * userCmdMgr ) { return TryRunTics( userCmdMgr ); } void D_Wipe(); void DoomLib::Wipe() { D_Wipe(); } void DoomLib::Frame( int realoffset, int buffer ) { ::g->realoffset = realoffset; // The render thread needs to read the player's screens[0] array, // so updating it needs to be in a critical section. // This may seem like a really broad mutex (which it is), and if performance // suffers too much, we can try to narrow the scope. // Just be careful, because the player's screen data is updated in many different // places. if ( 0 <= currentplayer && currentplayer <= 4 ) { idScopedCriticalSection crit( playerScreenMutexes[currentplayer] ); D_RunFrame( true ); } } void DoomLib::Draw() { R_RenderPlayerView (&::g->players[::g->displayplayer]); } angle_t GetViewAngle() { return g->viewangle; } void SetViewAngle( angle_t ang ) { g->viewangle = ang; ::g->viewxoffset = (finesine[g->viewangle>>ANGLETOFINESHIFT]*::g->realoffset) >> 8; ::g->viewyoffset = (finecosine[g->viewangle>>ANGLETOFINESHIFT]*::g->realoffset) >> 8; } void SetViewX( fixed_t x ) { ::g->viewx = x; } void SetViewY( fixed_t y ) { ::g->viewy = y; } fixed_t GetViewX() { return ::g->viewx + ::g->viewxoffset; } fixed_t GetViewY() { return ::g->viewy + ::g->viewyoffset; } void DoomLib::Shutdown() { //D_QuitNetGame (); I_ShutdownSound(); I_ShutdownGraphics(); W_Shutdown(); // De-allocate the zone memory (never happened in original doom, until quit) if ( ::g->mainzone ) { free( ::g->mainzone ); } // Delete the globals if ( globaldata[currentplayer] ) { delete globaldata[currentplayer]; globaldata[currentplayer] = NULL; } } // static void DoomLib::SetPlayer( int id ) { currentplayer = id; if ( id < 0 || id >= MAX_PLAYERS ) { g = NULL; } else { // Big Fucking hack. if( globalNetworking && session->GetGameLobbyBase().GetMatchParms().matchFlags | MATCH_ONLINE ) { currentplayer = 0; } g = globaldata[currentplayer]; } } void DoomLib::SetNetworking( RecvFunc rf, SendFunc sf, SendRemoteFunc sendRemote ) { Recv = rf; Send = sf; SendRemote = sendRemote; } int DoomLib::GetPlayer() { return currentplayer; } byte DoomLib::BuildSourceDest( int toNode ) { byte sourceDest = 0; sourceDest |= ::g->consoleplayer << 2; sourceDest |= RemoteNodeToPlayerIndex( toNode ); return sourceDest; } void I_Printf(char *error, ...); void DoomLib::GetSourceDest( byte sourceDest, int* source, int* dest ) { int src = (sourceDest & 12) >> 2; int dst = sourceDest & 3; *source = PlayerIndexToRemoteNode( src ); //I_Printf( "GetSourceDest Current Player(%d) %d --> %d\n", GetPlayer(), src, *source ); *dest = PlayerIndexToRemoteNode( dst ); } int nodeMap[4][4] = { {0, 1, 2, 3}, //Player 0 {1, 0, 2, 3}, //Player 1 {2, 0, 1, 3}, //Player 2 {3, 0, 1, 2} //Player 3 }; int DoomLib::RemoteNodeToPlayerIndex( int node ) { //This needs to be called with the proper doom globals set so this calculation will work properly /* int player = ::g->consoleplayer; if (player == 2 && node == 2 ) { int suck = 0; } if( node == player ) { return 0; } if( node - player <= 0 ) { return node+1; } return node;*/ return nodeMap[::g->consoleplayer][node]; } int indexMap[4][4] = { {0, 1, 2, 3}, //Player 0 {1, 0, 2, 3}, //Player 1 {1, 2, 0, 3}, //Player 2 {1, 2, 3, 0} //Player 3 }; int DoomLib::PlayerIndexToRemoteNode( int index ) { /*int player = ::g->consoleplayer; if( index == 0 ) { return player; } if( index <= player ) { return index-1; } return index;*/ return indexMap[::g->consoleplayer][index]; } void I_Error (char *error, ...); extern bool useTech5Packets; void DoomLib::PollNetwork() { #if 0 if ( !useTech5Packets ) { if ( !globalNetworking ) { return; } int c; struct sockaddr fromaddress; socklen_t fromlen; doomdata_t sw; while(1) { int receivedSize = recvfrom( ::g->insocket, &sw, sizeof( doomdata_t ), MSG_DONTWAIT, &fromaddress, &fromlen ); //c = WSARecvFrom(::g->insocket, &buffer, 1, &num_recieved, &flags, (struct sockaddr*)&fromaddress, &fromlen, 0, 0); if ( receivedSize < 0 ) { int err = sys_net_errno; if (err != SYS_NET_EWOULDBLOCK ) { I_Error ("GetPacket: %d", err ); //I_Printf ("GetPacket: %s",strerror(errno)); } return; } printf( "RECEIVED PACKET!!\n" ); int source; int dest; GetSourceDest( sw.sourceDest, &source, &dest ); //Push the packet onto the network stack to be processed. DoomLib::Send( (char*)&sw, receivedSize, NULL, dest ); } } #endif } void DoomLib::SendNetwork() { if ( !globalNetworking ) { return; } DoomLib::SendRemote(); } void DoomLib::RunSound() { I_ProcessSoundEvents(); }