//------------------------------------------------------------------------- /* Copyright (C) 1997, 2005 - 3D Realms Entertainment This file is part of Shadow Warrior version 1.2 Shadow Warrior 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 2 of the License, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Original Source: 1997 - Frank Maddin and Jim Norwood Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms */ //------------------------------------------------------------------------- #include "ns.h" #define MAIN #define QUIET #include "build.h" #include "baselayer.h" #include "baselayer.h" #include "names2.h" #include "panel.h" #include "game.h" #include "interp.h" #include "interpso.h" #include "tags.h" #include "sector.h" #include "sprite.h" #include "weapon.h" #include "player.h" #include "lists.h" #include "network.h" #include "pal.h" #include "mytypes.h" #include "menus.h" #include "gamecontrol.h" #include "gamedefs.h" #include "demo.h" #include "misc.h" //#include "exports.h" #include "misc.h" #include "break.h" #include "ninja.h" #include "light.h" #include "misc.h" #include "jsector.h" #include "common.h" #include "gameconfigfile.h" #include "printf.h" #include "m_argv.h" #include "debugbreak.h" #include "menu.h" #include "raze_music.h" #include "statistics.h" #include "gstrings.h" #include "mapinfo.h" #include "v_video.h" #include "raze_sound.h" #include "secrets.h" #include "osdcmds.h" #include "screenjob.h" #include "inputstate.h" #include "gamestate.h" //#include "crc32.h" CVAR(Bool, sw_ninjahack, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); CVAR(Bool, sw_darts, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); BEGIN_SW_NS void pClearSpriteList(PLAYERp pp); signed char MNU_InputSmallString(char*, short); signed char MNU_InputString(char*, short); SWBOOL IsCommand(const char* str); SWBOOL MNU_StartNetGame(void); extern SWBOOL mapcheat; extern SWBOOL MultiPlayQuitFlag; extern int sw_snd_scratch; #if DEBUG #define BETA 0 #endif #define TITLE_ROT_FLAGS (RS_TOPLEFT|ROTATE_SPRITE_SCREEN_CLIP|ROTATE_SPRITE_NON_MASK) #define PAL_SIZE (256*3) char DemoName[15][16]; // Stupid WallMart version! //#define PLOCK_VERSION TRUE #if PLOCK_VERSION SWBOOL Global_PLock = TRUE; #else SWBOOL Global_PLock = FALSE; #endif int GameVersion = 20; char DemoText[3][64]; int DemoTextYstart = 0; int Follow_posx=0,Follow_posy=0; SWBOOL NoMeters = FALSE; SWBOOL GraphicsMode = FALSE; char CacheLastLevel[32] = ""; char PlayerNameArg[32] = ""; SWBOOL CleanExit = FALSE; SWBOOL DemoModeMenuInit = FALSE; SWBOOL FinishAnim = 0; SWBOOL ShortGameMode = FALSE; SWBOOL ReloadPrompt = FALSE; SWBOOL NewGame = TRUE; SWBOOL InMenuLevel = FALSE; SWBOOL LoadGameOutsideMoveLoop = FALSE; SWBOOL LoadGameFromDemo = FALSE; extern SWBOOL NetBroadcastMode, NetModeOverride; SWBOOL MultiPlayQuitFlag = FALSE; //Miscellaneous variables char MessageInputString[256]; char MessageOutputString[256]; SWBOOL ConInputMode = FALSE; SWBOOL ConPanel = FALSE; SWBOOL FinishedLevel = FALSE; SWBOOL PanelUpdateMode = TRUE; short HelpPage = 0; short HelpPagePic[] = { 5115, 5116, 5117 }; SWBOOL InputMode = FALSE; SWBOOL MessageInput = FALSE; short screenpeek = 0; SWBOOL NoDemoStartup = FALSE; SWBOOL FirstTimeIntoGame; SWBOOL PedanticMode; SWBOOL LocationInfo = 0; void drawoverheadmap(int cposx, int cposy, int czoom, short cang); int DispFrameRate = FALSE; int DispMono = TRUE; int Fog = FALSE; int FogColor; SWBOOL PreCaching = TRUE; int GodMode = FALSE; SWBOOL BotMode = FALSE; short Skill = 2; short BetaVersion = 900; short TotalKillable; AUTO_NET Auto; SWBOOL AutoNet = FALSE; SWBOOL HasAutoColor = FALSE; uint8_t AutoColor; const GAME_SET gs_defaults = { 2, // border 0, // border tile // Network game settings 0, // GameType 0, // Level 0, // Monsters FALSE, // HurtTeammate TRUE, // SpawnMarkers Markers FALSE, // TeamPlay 0, // Kill Limit 0, // Time Limit 0, // Color TRUE, // nuke }; GAME_SET gs; SWBOOL PlayerTrackingMode = FALSE; SWBOOL SlowMode = FALSE; SWBOOL FrameAdvanceTics = 3; SWBOOL ScrollMode2D = FALSE; SWBOOL DebugSO = FALSE; SWBOOL DebugPanel = FALSE; SWBOOL DebugSector = FALSE; SWBOOL DebugActor = FALSE; SWBOOL DebugAnim = FALSE; SWBOOL DebugOperate = FALSE; SWBOOL DebugActorFreeze = FALSE; void LoadingLevelScreen(void); uint8_t FakeMultiNumPlayers; int totalsynctics; short Level = 0; SWBOOL ExitLevel = FALSE; int OrigCommPlayers=0; extern uint8_t CommPlayers; extern SWBOOL CommEnabled; extern int bufferjitter; SWBOOL CameraTestMode = FALSE; char ds[645]; // debug string extern short NormalVisibility; extern int quotebot, quotebotgoal; // Multiplayer typing buffer char recbuf[80]; // Used as a temp buffer to hold typing text #define ACT_STATUE 0 int score; SWBOOL QuitFlag = FALSE; SWBOOL InGame = FALSE; SWBOOL CommandSetup = FALSE; char UserMapName[80]="", buffer[80], ch; char LevelName[20]; uint8_t DebugPrintColor = 255; int krandcount; /// L O C A L P R O T O T Y P E S ///////////////////////////////////////////////////////// void BOT_DeleteAllBots(void); void BotPlayerInsert(PLAYERp pp); void SybexScreen(void); void MenuLevel(void); void StatScreen(PLAYERp mpp); void InitRunLevel(void); void RunLevel(void); ///////////////////////////////////////////////////////////////////////////////////////////// static FILE *debug_fout = NULL; // Transitioning helper. void Logo(const CompletionFunc& completion); int SyncScreenJob() { while (gamestate == GS_INTERMISSION || gamestate == GS_INTRO) { DoUpdateSounds(); handleevents(); updatePauseStatus(); D_ProcessEvents(); ControlInfo info; CONTROL_GetInput(&info); C_RunDelayedCommands(); RunScreenJobFrame(); // This handles continuation through its completion callback. videoNextPage(); } return 0; } extern SWBOOL DrawScreen; int krand1(void) { ASSERT(!DrawScreen); krandcount++; randomseed = ((randomseed * 21 + 1) & 65535); return randomseed; } int PointOnLine(int x, int y, int x1, int y1, int x2, int y2) { // the closer to 0 the closer to the line the point is return ((x2 - x1) * (y - y1)) - ((y2 - y1) * (x - x1)); } int Distance(int x1, int y1, int x2, int y2) { int min; if ((x2 = x2 - x1) < 0) x2 = -x2; if ((y2 = y2 - y1) < 0) y2 = -y2; if (x2 > y2) min = y2; else min = x2; return x2 + y2 - DIV2(min); } void TerminateGame(void) { DemoTerm(); ErrorCorrectionQuit(); // uninitmultiplayers(); if (CleanExit) { //SybexScreen(); } throw CExitEvent(3); } bool LoadLevel(const char *filename) { int16_t ang; if (engineLoadBoard(filename, SW_SHAREWARE ? 1 : 0, (vec3_t *)&Player[0], &ang, &Player[0].cursectnum) == -1) { Printf("Level not found: %s", filename); return false; } currentLevel = &mapList[Level]; SECRET_SetMapName(currentLevel->DisplayName(), currentLevel->name); STAT_NewLevel(currentLevel->labelName); Player[0].q16ang = fix16_from_int(ang); return true; } void DisplayDemoText(void) { short w,h; short i; for (i = 0; i < 3; i++) { MNU_MeasureString(DemoText[i], &w, &h); PutStringTimer(Player, TEXT_TEST_COL(w), DemoTextYstart+(i*12), DemoText[i], 999); } } void MultiSharewareCheck(void) { if (!SW_SHAREWARE) return; if (numplayers > 4) { I_FatalError("To play a Network game with more than 4 players you must purchase " "the full version. Read the Ordering Info screens for details."); } } // Some mem crap for Jim // I reserve 1 meg of heap space for our use out side the cache int TotalMemory = 0; int ActualHeap = 0; void InitAutoNet(void) { if (!AutoNet) return; gs.NetGameType = Auto.Rules; gs.NetLevel = Auto.Level; gs.NetMonsters = Auto.Enemy; gs.NetSpawnMarkers = Auto.Markers; gs.NetTeamPlay = Auto.Team; gs.NetHurtTeammate = Auto.HurtTeam; gs.NetKillLimit = Auto.Kill; gs.NetTimeLimit = Auto.Time; gs.NetColor = Auto.Color; gs.NetNuke = Auto.Nuke; } static int firstnet = 0; // JBF void SW_InitMultiPsky(void) { // default psky_t* const defaultsky = tileSetupSky(DEFAULTPSKY); defaultsky->lognumtiles = 1; defaultsky->horizfrac = 8192; } bool InitGame() { extern int MovesPerPacket; //void *ReserveMem=NULL; int i; engineInit(); InitAutoNet(); { auto pal = fileSystem.LoadFile("3drealms.pal", 0); if (pal.Size() >= 768) { for (auto& c : pal) c <<= 2; paletteSetColorTable(DREALMSPAL, pal.Data(), true, true); } } timerInit(120); InitPalette(); // sets numplayers, connecthead, connectpoint2, myconnectindex #if 0 if (!firstnet) initmultiplayers(0, NULL, 0, 0, 0); else if (initmultiplayersparms(argc - firstnet, &argv[firstnet])) { Printf("Waiting for players...\n"); while (initmultiplayerscycle()) { handleevents(); } } #else numplayers = 1; myconnectindex = 0; connecthead = 0; connectpoint2[0] = -1; #endif initsynccrc(); // code to duplicate packets if (numplayers > 4 && MovesPerPacket == 1) { MovesPerPacket = 2; } MultiSharewareCheck(); if (numplayers > 1) { CommPlayers = numplayers; OrigCommPlayers = CommPlayers; CommEnabled = TRUE; gNet.MultiGameType = MULTI_GAME_COMMBAT; } TileFiles.LoadArtSet("tiles%03d.art"); Connect(); SortBreakInfo(); parallaxtype = 1; SW_InitMultiPsky(); memset(Track, 0, sizeof(Track)); memset(Player, 0, sizeof(Player)); for (i = 0; i < MAX_SW_PLAYERS; i++) INITLIST(&Player[i].PanelSpriteList); LoadKVXFromScript("swvoxfil.txt"); // Load voxels from script file LoadPLockFromScript("swplock.txt"); // Get Parental Lock setup info LoadCustomInfoFromScript("engine/swcustom.txt"); // load the internal definitions. These also apply to the shareware version. if (!SW_SHAREWARE) { LoadCustomInfoFromScript("swcustom.txt"); // Load user customisation information } if (!loaddefinitionsfile(G_DefFile())) Printf("Definitions file loaded.\n"); userConfig.AddDefs.reset(); enginePostInit(); videoInit(); DemoModeMenuInit = TRUE; // precache as much stuff as you can if (UserMapName[0] == '\0') { if (!LoadLevel("$dozer.map")) return false; SetupPreCache(); DoTheCache(); } else { if (!LoadLevel(UserMapName)) return false; SetupPreCache(); DoTheCache(); } GraphicsMode = TRUE; InitFX(); // JBF: do it down here so we get a hold of the window handle return true; } short SongLevelNum; FString ThemeSongs[6]; int ThemeTrack[6]; void InitNewGame(void) { int i, ready_bak; int ver_bak; //waitforeverybody(); // since ready flag resets after this point, need to carefully sync for (i = 0; i < MAX_SW_PLAYERS; i++) { // don't jack with the playerreadyflag ready_bak = Player[i].playerreadyflag; ver_bak = Player[i].PlayerVersion; memset(&Player[i], 0, sizeof(Player[i])); Player[i].playerreadyflag = ready_bak; Player[i].PlayerVersion = ver_bak; INITLIST(&Player[i].PanelSpriteList); } memset(puser, 0, sizeof(puser)); } void FindLevelInfo(char *map_name, short *level) { short j; for (j = 1; j <= MAX_LEVELS; j++) { if (Bstrcasecmp(map_name, mapList[j].fileName.GetChars()) == 0) { *level = j; return; } } *level = 0; return; } int ChopTics; void InitLevelGlobals(void) { extern char PlayerGravity; extern short wait_active_check_offset; //extern short Zombies; extern int PlaxCeilGlobZadjust, PlaxFloorGlobZadjust; extern SWBOOL left_foot; extern SWBOOL serpwasseen; extern SWBOOL sumowasseen; extern SWBOOL zillawasseen; extern short BossSpriteNum[3]; ChopTics = 0; dimensionmode = 3; zoom = 768; PlayerGravity = 24; wait_active_check_offset = 0; PlaxCeilGlobZadjust = PlaxFloorGlobZadjust = Z(500); FinishedLevel = FALSE; AnimCnt = 0; left_foot = FALSE; screenpeek = myconnectindex; numinterpolations = short_numinterpolations = 0; gNet.TimeLimitClock = gNet.TimeLimit; serpwasseen = FALSE; sumowasseen = FALSE; zillawasseen = FALSE; memset(BossSpriteNum,-1,sizeof(BossSpriteNum)); PedanticMode = (DemoPlaying || DemoRecording || DemoEdit || DemoMode); } void InitLevelGlobals2(void) { extern short Bunny_Count; // GLOBAL RESETS NOT DONE for LOAD GAME // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! InitTimingVars(); TotalKillable = 0; Bunny_Count = 0; FinishAnim = 0; } void InitLevel(void) { if (LoadGameOutsideMoveLoop) { InitLevelGlobals(); return; } static int DemoNumber = 0; Terminate3DSounds(); // A few IMPORTANT GLOBAL RESETS InitLevelGlobals(); if (!DemoMode) Mus_Stop(); InitLevelGlobals2(); if (DemoMode) { Level = 0; NewGame = TRUE; DemoInitOnce = FALSE; strcpy(DemoFileName, DemoName[DemoNumber]); DemoNumber++; if (!DemoName[DemoNumber][0]) DemoNumber = 0; // read header and such DemoPlaySetup(); strcpy(LevelName, DemoLevelName); FindLevelInfo(LevelName, &Level); if (Level > 0) { strcpy(LevelName, mapList[Level].fileName); UserMapName[0] = '\0'; } else { strcpy(UserMapName, DemoLevelName); Level = 0; } } else { if (Level < 0) Level = 0; if (Level > MAX_LEVELS) Level = 1; // extra code in case something is resetting these values if (NewGame) { //Level = 1; //DemoPlaying = FALSE; DemoMode = FALSE; //DemoRecording = FALSE; //DemoEdit = FALSE; } if (UserMapName[0]) { strcpy(LevelName, UserMapName); Level = 0; FindLevelInfo(UserMapName, &Level); if (Level > 0) { // user map is part of game - treat it as such strcpy(LevelName, mapList[Level].fileName); UserMapName[0] = '\0'; } } else { strcpy(LevelName, mapList[Level].fileName); } } if (NewGame) InitNewGame(); //LoadingLevelScreen(); if (!DemoMode && !DemoInitOnce) DemoPlaySetup(); if (!LoadLevel(LevelName)) { NewGame = false; return; } STAT_NewLevel(LevelName); if (Bstrcasecmp(CacheLastLevel, LevelName) != 0) // clears gotpic and does some bit setting SetupPreCache(); else memset(gotpic,0,sizeof(gotpic)); if (sector[0].extra != -1) { NormalVisibility = g_visibility = sector[0].extra; sector[0].extra = 0; } else NormalVisibility = g_visibility; // // Do Player stuff first // InitAllPlayers(); QueueReset(); PreMapCombineFloors(); InitMultiPlayerInfo(); InitAllPlayerSprites(); // // Do setup for sprite, track, panel, sector, etc // // Set levels up InitTimingVars(); SpriteSetup(); SpriteSetupPost(); // post processing - already gone once through the loop InitLighting(); TrackSetup(); PlayerPanelSetup(); SectorSetup(); JS_InitMirrors(); JS_InitLockouts(); // Setup the lockout linked lists JS_ToggleLockouts(); // Init lockouts on/off PlaceSectorObjectsOnTracks(); PlaceActorsOnTracks(); PostSetupSectorObject(); SetupMirrorTiles(); initlava(); SongLevelNum = Level; if (DemoMode) { DisplayDemoText(); } // reset NewGame NewGame = FALSE; } void TerminateLevel(void) { int i, nexti, stat, pnum, ndx; SECT_USERp *sectu; //HEAP_CHECK(); DemoTerm(); // Free any track points for (ndx = 0; ndx < MAX_TRACKS; ndx++) { if (Track[ndx].TrackPoint) { FreeMem(Track[ndx].TrackPoint); // !JIM! I added null assigner Track[ndx].TrackPoint = NULL; } } // Clear the tracks memset(Track, 0, sizeof(Track)); StopFX(); // Clear all anims and any memory associated with them // Clear before killing sprites - save a little time //AnimClear(); for (stat = STAT_PLAYER0; stat < STAT_PLAYER0 + numplayers; stat++) { pnum = stat - STAT_PLAYER0; TRAVERSE_SPRITE_STAT(headspritestat[stat], i, nexti) { if (User[i]) memcpy(&puser[pnum], User[i], sizeof(USER)); } } // Kill User memory and delete sprites // for (stat = 0; stat < STAT_ALL; stat++) for (stat = 0; stat < MAXSTATUS; stat++) { TRAVERSE_SPRITE_STAT(headspritestat[stat], i, nexti) { KillSprite(i); } } // Free SectUser memory for (sectu = &SectUser[0]; sectu < &SectUser[MAXSECTORS]; sectu++) { if (*sectu) { FreeMem(*sectu); *sectu = NULL; } } //memset(&User[0], 0, sizeof(User)); memset(&SectUser[0], 0, sizeof(SectUser)); TRAVERSE_CONNECT(pnum) { PLAYERp pp = Player + pnum; // Free panel sprites for players pClearSpriteList(pp); pp->DoPlayerAction = NULL; pp->SpriteP = NULL; pp->PlayerSprite = -1; pp->UnderSpriteP = NULL; pp->PlayerUnderSprite = -1; memset(pp->HasKey, 0, sizeof(pp->HasKey)); //pp->WpnFlags = 0; pp->CurWpn = NULL; memset(pp->Wpn, 0, sizeof(pp->Wpn)); memset(pp->InventoryTics, 0, sizeof(pp->InventoryTics)); pp->Killer = -1; INITLIST(&pp->PanelSpriteList); } JS_UnInitLockouts(); //HEAP_CHECK(); } void NewLevel(void) { if (DemoPlaying) { InitLevel(); InitRunLevel(); DemoInitOnce = FALSE; if (DemoMode) { if (DemoModeMenuInit) { DemoModeMenuInit = FALSE; inputState.ClearKeyStatus(sc_Escape); } } DemoPlayBack(); if (DemoRecording && DemoEdit) { RunLevel(); } } else { do { InitLevel(); RunLevel(); } while (LoadGameOutsideMoveLoop); STAT_Update(false); if (!QuitFlag) { // for good measure do this ready2send = 0; waitforeverybody(); } StatScreen(&Player[myconnectindex]); } if (LoadGameFromDemo) LoadGameFromDemo = FALSE; else TerminateLevel(); InGame = FALSE; if (SW_SHAREWARE) { if (FinishAnim) { PlayTheme(); MenuLevel(); STAT_Update(true); } } else { if (FinishAnim == ANIM_ZILLA || FinishAnim == ANIM_SERP) { PlayTheme(); MenuLevel(); STAT_Update(true); } } } void PlayTheme() { // start music at logo PlaySong(nullptr, ThemeSongs[0], ThemeTrack[0]); } // CTW REMOVED END void DrawMenuLevelScreen(void) { const int TITLE_PIC = 2324; twod->ClearScreen(); rotatesprite(0, 0, RS_SCALE, 0, TITLE_PIC, 20, 0, TITLE_ROT_FLAGS, 0, 0, xdim - 1, ydim - 1); } short PlayerQuitMenuLevel = -1; void MenuLevel(void) { short w,h; M_StartControlPanel(false); M_SetMenu(NAME_Mainmenu); // do demos only if not playing multi play if (!CommEnabled && numplayers <= 1 && !FinishAnim && !NoDemoStartup) { // demos exist - do demo instead if (DemoName[0][0] != '\0') { DemoMode = TRUE; DemoPlaying = TRUE; return; } } DemoMode = FALSE; DemoPlaying = FALSE; twod->ClearScreen(); videoNextPage(); //FadeOut(0, 0); ready2send = 0; totalclock = 0; ototalclock = 0; ExitLevel = FALSE; InMenuLevel = TRUE; DrawMenuLevelScreen(); if (CommEnabled) { sprintf(ds,"Lo Wang is waiting for other players..."); MNU_MeasureString(ds, &w, &h); MNU_DrawString(TEXT_TEST_COL(w), 170, ds, 1, 16); sprintf(ds,"They are afraid!"); MNU_MeasureString(ds, &w, &h); MNU_DrawString(TEXT_TEST_COL(w), 180, ds, 1, 16); } videoNextPage(); //FadeIn(0, 3); waitforeverybody(); inputState.ClearAllInput(); if (SW_SHAREWARE) { // go to ordering menu only if shareware if (FinishAnim) { inputState.ClearKeyStatus(sc_Escape); M_StartControlPanel(false); M_SetMenu(NAME_CreditsMenu); FinishAnim = 0; } } else { FinishAnim = 0; } while (TRUE) { handleevents(); D_ProcessEvents(); C_RunDelayedCommands(); // limits checks to max of 40 times a second if (totalclock >= ototalclock + synctics) { ototalclock += synctics; if (CommEnabled) getpackets(); } if (CommEnabled) { if (MultiPlayQuitFlag) { uint8_t pbuf[1]; QuitFlag = TRUE; pbuf[0] = PACKET_TYPE_MENU_LEVEL_QUIT; netbroadcastpacket(pbuf, 1); // TENSW break; } if (PlayerQuitMenuLevel >= 0) { MenuCommPlayerQuit(PlayerQuitMenuLevel); PlayerQuitMenuLevel = -1; } } if (ExitLevel) { // Quiting Level ExitLevel = FALSE; break; } if (QuitFlag) { // Quiting Game break; } // must lock the clock for drawing so animations will happen totalclocklock = totalclock; //drawscreen as fast as you can DrawMenuLevelScreen(); DoUpdateSounds(); videoNextPage(); } inputState.ClearAllInput(); M_ClearMenus(); InMenuLevel = FALSE; twod->ClearScreen(); videoNextPage(); } extern SWBOOL FinishedLevel; void EndGameSequence(void) { StopSound(); //playanm(FinishAnim); //BonusScreen(); ExitLevel = FALSE; QuitFlag = FALSE; AutoNet = FALSE; //if (FinishAnim == ANIM_ZILLA) // CreditsLevel(); ExitLevel = FALSE; QuitFlag = FALSE; AutoNet = FALSE; if (SW_SHAREWARE) { Level = 0; } else { if (Level == 4 || Level == 20) { Level=0; } else Level++; } } void StatScreen(PLAYERp mpp) { extern SWBOOL FinishedLevel; short w,h; short rows,cols,i,j; PLAYERp pp = NULL; int x,y; short pal; //ResetPalette(mpp); COVER_SetReverb(0); // Reset reverb mpp->Reverb = 0; StopSound(); soundEngine->UpdateSounds((int)totalclock); if (FinishAnim) { EndGameSequence(); return; } if (gNet.MultiGameType != MULTI_GAME_COMMBAT) { if (!FinishedLevel) return; //BonusScreen(); return; } //MPBonusScreen(); } void GameIntro(void) { if (DemoPlaying || (!CommEnabled && UserMapName[0])) return; Level = 1; Logo([](bool) { gamestate = GS_LEVEL; }); SyncScreenJob(); MenuLevel(); } void Control() { InitGame(); InGame = TRUE; GameIntro(); while (!QuitFlag) { handleevents(); C_RunDelayedCommands(); NewLevel(); } CleanExit = TRUE; throw CExitEvent(0); } void _Assert(const char *expr, const char *strFile, unsigned uLine) { I_FatalError("Assertion failed: %s %s, line %u", expr, strFile, uLine); } void dsprintf(char *str, const char *format, ...) { va_list arglist; va_start(arglist, format); vsprintf(str, format, arglist); va_end(arglist); } void dsprintf_null(char *str, const char *format, ...) { va_list arglist; } void getinput(SW_PACKET *, SWBOOL); void MoveLoop(void) { int pnum; getpackets(); if (PredictionOn && CommEnabled) { while (predictmovefifoplc < Player[myconnectindex].movefifoend) { DoPrediction(ppp); } } //While you have new input packets to process... if (!CommEnabled) bufferjitter = 0; while (Player[myconnectindex].movefifoend - movefifoplc > bufferjitter) { //Make sure you have at least 1 packet from everyone else for (pnum=connecthead; pnum>=0; pnum=connectpoint2[pnum]) { if (movefifoplc == Player[pnum].movefifoend) { break; } } //Pnum is >= 0 only if last loop was broken, meaning a player wasn't caught up if (pnum >= 0) break; domovethings(); } } void InitPlayerGameSettings(void) { int pnum; // don't jack with auto aim settings if DemoMode is going // what the hell did I do this for????????? //if (DemoMode) // return; if (CommEnabled) { // everyone gets the same Auto Aim TRAVERSE_CONNECT(pnum) { if (gNet.AutoAim) SET(Player[pnum].Flags, PF_AUTO_AIM); else RESET(Player[pnum].Flags, PF_AUTO_AIM); } } else { if (cl_autoaim) SET(Player[myconnectindex].Flags, PF_AUTO_AIM); else RESET(Player[myconnectindex].Flags, PF_AUTO_AIM); } } void InitRunLevel(void) { if (DemoEdit) return; if (LoadGameOutsideMoveLoop) { int SavePlayClock; extern int PlayClock; LoadGameOutsideMoveLoop = FALSE; // contains what is needed from calls below if (snd_ambience) StartAmbientSound(); // crappy little hack to prevent play clock from being overwritten // for load games SavePlayClock = PlayClock; InitTimingVars(); PlayClock = SavePlayClock; return; } #if 0 // ensure we are through the initialization code before sending the game // version. Otherwise, it is possible to send this too early and have it // blown away on the other side. waitforeverybody(); #endif SendVersion(GameVersion); waitforeverybody(); Mus_Stop(); if (Bstrcasecmp(CacheLastLevel, LevelName) != 0) DoTheCache(); // auto aim / auto run / etc InitPlayerGameSettings(); // send packets with player info InitNetPlayerOptions(); // Initialize Game part of network code (When ready2send != 0) InitNetVars(); { if (Level == 0) { PlaySong(nullptr, currentLevel->music, 1 + RANDOM_RANGE(10)); } else { PlaySong(currentLevel->labelName, currentLevel->music, currentLevel->cdSongId); } } InitPrediction(&Player[myconnectindex]); if (!DemoInitOnce) DemoRecordSetup(); // everything has been inited at least once for RECORD DemoInitOnce = TRUE; waitforeverybody(); CheckVersion(GameVersion); // IMPORTANT - MUST be right before game loop AFTER waitforeverybody InitTimingVars(); if (snd_ambience) StartAmbientSound(); } void RunLevel(void) { InitRunLevel(); #if 0 waitforeverybody(); #endif ready2send = 1; while (TRUE) { handleevents(); C_RunDelayedCommands(); D_ProcessEvents(); if (LoadGameOutsideMoveLoop) { return; // Stop the game loop if a savegame was loaded from the menu. } updatePauseStatus(); if (paused) { ototalclock = (int)totalclock - (120 / synctics); buttonMap.ResetButtonStates(); } else { while (ready2send && (totalclock >= ototalclock + synctics)) { UpdateInputs(); MoveLoop(); } // Get input again to update q16ang/q16horiz. if (!PedanticMode) getinput(&loc, TRUE); } drawscreen(Player + screenpeek); if (QuitFlag) break; if (ExitLevel) { ExitLevel = FALSE; break; } timerUpdateClock(); } ready2send = 0; } #if 0 Map -> User Map Name Auto -> Auto Start Game Rules -> 0=WangBang 1=WangBang (No Respawn) 2=CoOperative Level -> 0 to 24(?) Enemy -> 0=None 1=Easy 2=Norm 3=Hard 4=Insane Markers -> 0=Off 1=On Team -> 0=Off 1=On HurtTeam -> 0=Off 1=On KillLimit -> 0=Infinite 1=10 2=20 3=30 4=40 5=50 6=60 7=70 8=80 9=90 10=100 TimeLimit -> 0=Infinite 1=3 2=5 3=10 4=20 5=30 6=45 7=60 Color -> 0=Brown 1=Purple 2=Red 3=Yellow 4=Olive 5=Green Nuke -> 0=Off 1=On Example Command Line : sw -map testmap.map -autonet 0,0,1,1,1,0,3,2,1,1 -f4 -name 1234567890 -net 12345678 commit -map grenade -autonet 0,0,1,1,1,0,3,2,1,1 -name frank #endif char isShareware = FALSE; int DetectShareware(void) { return (isShareware = !!(g_gameType & GAMEFLAG_SHAREWARE)); } void CommandLineHelp(char const * const * argv) { } static const char* actions[] = { "Move_Forward", "Move_Backward", "Turn_Left", "Turn_Right", "Strafe", "Fire", "Open", "Run", "Alt_Fire", // Duke3D", Blood "Jump", "Crouch", "Look_Up", "Look_Down", "Look_Left", "Look_Right", "Strafe_Left", "Strafe_Right", "Aim_Up", "Aim_Down", "Weapon_1", "Weapon_2", "Weapon_3", "Weapon_4", "Weapon_5", "Weapon_6", "Weapon_7", "Weapon_8", "Weapon_9", "Weapon_10", "Inventory", "Inventory_Left", "Inventory_Right", "NightVision", "MedKit", "TurnAround", "SendMessage", "Map", "Shrink_Screen", "Enlarge_Screen", "Center_View", "Holster_Weapon", "Show_Opponents_Weapon", "Map_Follow_Mode", "See_Coop_View", "Mouse_Aiming", "Toggle_Crosshair", "Next_Weapon", "Previous_Weapon", "Dpad_Select", "Dpad_Aiming", "Last_Weapon", "Alt_Weapon", "Third_Person_View", "Toggle_Crouch", // This is the last one used by EDuke32"", "Smoke_Bomb", "Gas_Bomb", "Flash_Bomb", "Caltrops", }; int32_t GameInterface::app_main() { int i; extern int MovesPerPacket; void DoSector(void); void gameinput(void); int cnt = 0; InitCheats(); buttonMap.SetButtons(actions, NUM_ACTIONS); automapping = 1; gs = gs_defaults; hud_size.Callback(); if (!DetectShareware()) { if (SW_SHAREWARE) Printf("Detected shareware GRP\n"); else Printf("Detected registered GRP\n"); } for (i = 0; i < MAX_SW_PLAYERS; i++) INITLIST(&Player[i].PanelSpriteList); DebugOperate = TRUE; enginecompatibility_mode = ENGINECOMPATIBILITY_19961112; if (SW_SHAREWARE) Printf("SHADOW WARRIOR(tm) Version 1.2 (Shareware Version)\n"); else Printf("SHADOW WARRIOR(tm) Version 1.2\n"); if (sw_snd_scratch == 0) // This is always 0 at this point - this check is only here to prevent whole program optimization from eliminating the variable. Printf("Copyright (c) 1997 3D Realms Entertainment\n"); UserMapName[0] = '\0'; registerosdcommands(); Control(); return 0; } void ManualPlayerInsert(PLAYERp pp) { PLAYERp npp = Player + numplayers; if (numplayers < MAX_SW_PLAYERS) { connectpoint2[numplayers - 1] = numplayers; connectpoint2[numplayers] = -1; npp->posx = pp->posx; npp->posy = pp->posy; npp->posz = pp->posz; npp->q16ang = pp->q16ang; npp->cursectnum = pp->cursectnum; myconnectindex = numplayers; screenpeek = numplayers; sprintf(Player[myconnectindex].PlayerName,"PLAYER %d",myconnectindex+1); Player[numplayers].movefifoend = Player[0].movefifoend; // If IsAI = TRUE, new player will be a bot Player[myconnectindex].IsAI = FALSE; numplayers++; } } void BotPlayerInsert(PLAYERp pp) { PLAYERp npp = Player + numplayers; if (numplayers < MAX_SW_PLAYERS) { connectpoint2[numplayers - 1] = numplayers; connectpoint2[numplayers] = -1; npp->posx = pp->posx; npp->posy = pp->posy; npp->posz = pp->posz-Z(100); npp->q16ang = pp->q16ang; npp->cursectnum = pp->cursectnum; //myconnectindex = numplayers; //screenpeek = numplayers; sprintf(Player[numplayers].PlayerName,"BOT %d",numplayers+1); Player[numplayers].movefifoend = Player[0].movefifoend; // If IsAI = TRUE, new player will be a bot Player[numplayers].IsAI = TRUE; numplayers++; } } void ManualPlayerDelete(void) { short i, nexti; USERp u; PLAYERp pp; if (numplayers > 1) { numplayers--; connectpoint2[numplayers - 1] = -1; pp = Player + numplayers; KillSprite(pp->PlayerSprite); pp->PlayerSprite = -1; // Make sure enemys "forget" about deleted player TRAVERSE_SPRITE_STAT(headspritestat[STAT_ENEMY], i, nexti) { u = User[i]; if (u->tgt_sp == pp->SpriteP) u->tgt_sp = Player[0].SpriteP; } if (myconnectindex >= numplayers) myconnectindex = 0; if (screenpeek >= numplayers) screenpeek = 0; } } char WangBangMacro[10][64]; void FunctionKeys(PLAYERp pp) { static int rts_delay = 0; int fn_key = 0; rts_delay++; if (inputState.GetKeyStatus(sc_F1)) { fn_key = 1; } if (inputState.GetKeyStatus(sc_F2)) { fn_key = 2; } if (inputState.GetKeyStatus(sc_F3)) { fn_key = 3; } if (inputState.GetKeyStatus(sc_F4)) { fn_key = 4; } if (inputState.GetKeyStatus(sc_F5)) { fn_key = 5; } if (inputState.GetKeyStatus(sc_F6)) { fn_key = 6; } if (inputState.GetKeyStatus(sc_F7)) { fn_key = 7; } if (inputState.GetKeyStatus(sc_F8)) { fn_key = 8; } if (inputState.GetKeyStatus(sc_F9)) { fn_key = 9; } if (inputState.GetKeyStatus(sc_F10)) { fn_key = 10; } if (inputState.AltPressed()) { if (rts_delay > 16 && fn_key && CommEnabled && !adult_lockout && !Global_PLock) { inputState.ClearKeyStatus(sc_F1 + fn_key - 1); rts_delay = 0; PlaySoundRTS(fn_key); if (CommEnabled) { PACKET_RTS p; p.PacketType = PACKET_TYPE_RTS; p.RTSnum = fn_key; netbroadcastpacket((uint8_t*)(&p), sizeof(p)); // TENSW } } return; } if (inputState.ShiftPressed()) { if (fn_key && CommEnabled) { inputState.ClearKeyStatus(sc_Escape); inputState.ClearKeyStatus(sc_F1 + fn_key - 1); if (CommEnabled) { short pnum; sprintf(ds,"SENT: %s",**CombatMacros[fn_key-1]); adduserquote(ds); TRAVERSE_CONNECT(pnum) { if (pnum != myconnectindex) { sprintf(ds,"%s: %s",pp->PlayerName, **CombatMacros[fn_key - 1]); SW_SendMessage(pnum, ds); } } } } return; } // F7 VIEW control if (buttonMap.ButtonDown(gamefunc_Third_Person_View)) { buttonMap.ClearButton(gamefunc_Third_Person_View); if (SHIFTS_IS_PRESSED) { if (TEST(pp->Flags, PF_VIEW_FROM_OUTSIDE)) pp->view_outside_dang = NORM_ANGLE(pp->view_outside_dang + 256); } else { if (TEST(pp->Flags, PF_VIEW_FROM_OUTSIDE)) { RESET(pp->Flags, PF_VIEW_FROM_OUTSIDE); } else { SET(pp->Flags, PF_VIEW_FROM_OUTSIDE); pp->camera_dist = 0; } } } } short MirrorDelay; double elapsedInputTicks; double scaleAdjustmentToInterval(double x) { return x * (120 / synctics) / (1000.0 / elapsedInputTicks); } void DoPlayerTurn(PLAYERp pp, fix16_t *pq16ang, fix16_t q16angvel); void DoPlayerHorizon(PLAYERp pp, fix16_t *pq16horiz, fix16_t q16aimvel); void getinput(SW_PACKET *loc, SWBOOL tied) { int i; PLAYERp pp = Player + myconnectindex; PLAYERp newpp = Player + myconnectindex; int inv_hotkey = 0; #define TURBOTURNTIME (120/8) #define NORMALTURN (12+6) #define RUNTURN (28) #define PREAMBLETURN 3 #define NORMALKEYMOVE 35 #define MAXVEL ((NORMALKEYMOVE*2)+10) #define MAXSVEL ((NORMALKEYMOVE*2)+10) #define MAXANGVEL 100 #define MAXHORIZVEL 128 #define SET_LOC_KEY(loc, sync_num, key_test) SET(loc, ((!!(key_test)) << (sync_num))) static int32_t turnheldtime; int32_t momx, momy; extern SWBOOL MenuButtonAutoAim; if (Prediction && CommEnabled) { newpp = ppp; } static double lastInputTicks; auto const currentHiTicks = timerGetHiTicks(); elapsedInputTicks = currentHiTicks - lastInputTicks; lastInputTicks = currentHiTicks; // MAKE SURE THIS WILL GET SET SET_LOC_KEY(loc->bits, SK_QUIT_GAME, MultiPlayQuitFlag); bool mouseaim = in_mousemode || buttonMap.ButtonDown(gamefunc_Mouse_Aiming); if (!CommEnabled) { // Go back to the source to set this - the old code here was catastrophically bad. // this needs to be fixed properly - as it is this can never be compatible with demo playback. if (mouseaim) SET(Player[myconnectindex].Flags, PF_MOUSE_AIMING_ON); else RESET(Player[myconnectindex].Flags, PF_MOUSE_AIMING_ON); if (cl_autoaim) SET(Player[myconnectindex].Flags, PF_AUTO_AIM); else RESET(Player[myconnectindex].Flags, PF_AUTO_AIM); } ControlInfo info; CONTROL_GetInput(&info); if (paused) return; // MAP KEY if (buttonMap.ButtonDown(gamefunc_Map)) { buttonMap.ClearButton(gamefunc_Map); // Init follow coords Follow_posx = pp->posx; Follow_posy = pp->posy; if (dimensionmode == 3) dimensionmode = 5; else if (dimensionmode == 5) dimensionmode = 6; else { MirrorDelay = 1; dimensionmode = 3; ScrollMode2D = FALSE; } } // Toggle follow map mode on/off if (dimensionmode == 5 || dimensionmode == 6) { if (buttonMap.ButtonDown(gamefunc_Map_Follow_Mode)) { buttonMap.ClearButton(gamefunc_Map_Follow_Mode); ScrollMode2D = !ScrollMode2D; Follow_posx = pp->posx; Follow_posy = pp->posy; } } // If in 2D follow mode, scroll around using glob vars // Tried calling this in domovethings, but key response it too poor, skips key presses // Note: ScrollMode2D = Follow mode, so this get called only during follow mode if (!tied && ScrollMode2D && pp == Player + myconnectindex && !Prediction) MoveScrollMode2D(Player + myconnectindex); // !JIM! Added M_Active() so that you don't move at all while using menus if (M_Active() || ScrollMode2D || InputMode) return; SET_LOC_KEY(loc->bits, SK_SPACE_BAR, buttonMap.ButtonDown(gamefunc_Open)); int const running = G_CheckAutorun(buttonMap.ButtonDown(gamefunc_Run)); int32_t turnamount; int32_t keymove; // The function DoPlayerTurn() scales the player's q16angvel by 1.40625, so store as constant // and use to scale back player's aim and ang values for a consistent feel between games. float const angvelScale = 1.40625f; float const aimvelScale = 1.203125f; // Shadow Warrior has a ticrate of 40, 25% more than the other games, so store below constant // for dividing controller input to match speed input speed of other games. float const ticrateScale = 0.75f; if (running) { if (pp->sop_control) turnamount = RUNTURN * 3; else turnamount = RUNTURN; keymove = NORMALKEYMOVE << 1; } else { if (pp->sop_control) turnamount = NORMALTURN * 3; else turnamount = NORMALTURN; keymove = NORMALKEYMOVE; } if (tied) keymove = 0; int32_t svel = 0, vel = 0; fix16_t q16aimvel = 0, q16angvel = 0; if (buttonMap.ButtonDown(gamefunc_Strafe) && !pp->sop) { svel -= (info.mousex * ticrateScale) * 4.f; svel -= info.dyaw * keymove; } else { q16angvel = fix16_sadd(q16angvel, fix16_from_float(info.mousex / angvelScale)); q16angvel = fix16_sadd(q16angvel, fix16_from_dbl(scaleAdjustmentToInterval((info.dyaw * ticrateScale) / angvelScale))); } if (mouseaim) q16aimvel = fix16_ssub(q16aimvel, fix16_from_float(info.mousey / aimvelScale)); else vel -= (info.mousey * ticrateScale) * 8.f; if (in_mouseflip) q16aimvel = -q16aimvel; q16aimvel -= fix16_from_dbl(scaleAdjustmentToInterval((info.dpitch * ticrateScale) / aimvelScale)); svel -= info.dx * keymove; vel -= info.dz * keymove; if (buttonMap.ButtonDown(gamefunc_Strafe) && !pp->sop) { if (buttonMap.ButtonDown(gamefunc_Turn_Left)) svel -= -keymove; if (buttonMap.ButtonDown(gamefunc_Turn_Right)) svel -= keymove; } else { if (buttonMap.ButtonDown(gamefunc_Turn_Left) || (buttonMap.ButtonDown(gamefunc_Strafe_Left) && pp->sop)) { turnheldtime += synctics; if (PedanticMode) { if (turnheldtime >= TURBOTURNTIME) q16angvel -= fix16_from_int(turnamount); else q16angvel -= fix16_from_int(PREAMBLETURN); } else q16angvel = fix16_ssub(q16angvel, fix16_from_float(scaleAdjustmentToInterval((turnheldtime >= TURBOTURNTIME) ? turnamount : PREAMBLETURN))); } else if (buttonMap.ButtonDown(gamefunc_Turn_Right) || (buttonMap.ButtonDown(gamefunc_Strafe_Right) && pp->sop)) { turnheldtime += synctics; if (PedanticMode) { if (turnheldtime >= TURBOTURNTIME) q16angvel += fix16_from_int(turnamount); else q16angvel += fix16_from_int(PREAMBLETURN); } else q16angvel = fix16_sadd(q16angvel, fix16_from_float(scaleAdjustmentToInterval((turnheldtime >= TURBOTURNTIME) ? turnamount : PREAMBLETURN))); } else { turnheldtime = 0; } } if (buttonMap.ButtonDown(gamefunc_Strafe_Left) && !pp->sop) svel += keymove; if (buttonMap.ButtonDown(gamefunc_Strafe_Right) && !pp->sop) svel += -keymove; if (buttonMap.ButtonDown(gamefunc_Move_Forward)) { vel += keymove; } if (buttonMap.ButtonDown(gamefunc_Move_Backward)) vel += -keymove; q16angvel = fix16_clamp(q16angvel, -fix16_from_int(MAXANGVEL), fix16_from_int(MAXANGVEL)); q16aimvel = fix16_clamp(q16aimvel, -fix16_from_int(MAXHORIZVEL), fix16_from_int(MAXHORIZVEL)); void DoPlayerTeleportPause(PLAYERp pp); if (PedanticMode) { q16angvel = fix16_floor(q16angvel); q16aimvel = fix16_floor(q16aimvel); } else { fix16_t prevcamq16ang = pp->camq16ang, prevcamq16horiz = pp->camq16horiz; if (TEST(pp->Flags2, PF2_INPUT_CAN_TURN)) DoPlayerTurn(pp, &pp->camq16ang, q16angvel); if (TEST(pp->Flags2, PF2_INPUT_CAN_AIM)) DoPlayerHorizon(pp, &pp->camq16horiz, q16aimvel); pp->oq16ang += pp->camq16ang - prevcamq16ang; pp->oq16horiz += pp->camq16horiz - prevcamq16horiz; } loc->vel += vel; loc->svel += svel; if (!tied) { vel = clamp(loc->vel, -MAXVEL, MAXVEL); svel = clamp(loc->svel, -MAXSVEL, MAXSVEL); momx = mulscale9(vel, sintable[NORM_ANGLE(fix16_to_int(newpp->q16ang) + 512)]); momy = mulscale9(vel, sintable[NORM_ANGLE(fix16_to_int(newpp->q16ang))]); momx += mulscale9(svel, sintable[NORM_ANGLE(fix16_to_int(newpp->q16ang))]); momy += mulscale9(svel, sintable[NORM_ANGLE(fix16_to_int(newpp->q16ang) + 1536)]); loc->vel = momx; loc->svel = momy; } loc->q16angvel += q16angvel; loc->q16aimvel += q16aimvel; if (!CommEnabled) { // What a mess...:? #if 0 if (MenuButtonAutoAim) { MenuButtonAutoAim = FALSE; if ((!!TEST(pp->Flags, PF_AUTO_AIM)) != !!cl_autoaim) SET_LOC_KEY(loc->bits, SK_AUTO_AIM, TRUE); } #endif } else if (inputState.GetKeyStatus(sc_Pause)) { SET_LOC_KEY(loc->bits, SK_PAUSE, true); inputState.ClearKeyStatus(sc_Pause); } SET_LOC_KEY(loc->bits, SK_CENTER_VIEW, buttonMap.ButtonDown(gamefunc_Center_View)); SET_LOC_KEY(loc->bits, SK_RUN, buttonMap.ButtonDown(gamefunc_Run)); SET_LOC_KEY(loc->bits, SK_SHOOT, buttonMap.ButtonDown(gamefunc_Fire)); // actually snap SET_LOC_KEY(loc->bits, SK_SNAP_UP, buttonMap.ButtonDown(gamefunc_Aim_Up)); SET_LOC_KEY(loc->bits, SK_SNAP_DOWN, buttonMap.ButtonDown(gamefunc_Aim_Down)); // actually just look SET_LOC_KEY(loc->bits, SK_LOOK_UP, buttonMap.ButtonDown(gamefunc_Look_Up)); SET_LOC_KEY(loc->bits, SK_LOOK_DOWN, buttonMap.ButtonDown(gamefunc_Look_Down)); for (i = 0; i < MAX_WEAPONS_KEYS; i++) { if (buttonMap.ButtonDown(gamefunc_Weapon_1 + i)) { SET(loc->bits, i + 1); break; } } if (buttonMap.ButtonPressed(gamefunc_Next_Weapon)) { USERp u = User[pp->PlayerSprite]; short next_weapon = u->WeaponNum + 1; short start_weapon; buttonMap.ClearButton(gamefunc_Next_Weapon); start_weapon = u->WeaponNum + 1; if (u->WeaponNum == WPN_SWORD) start_weapon = WPN_STAR; if (u->WeaponNum == WPN_FIST) { next_weapon = 14; } else { next_weapon = -1; for (i = start_weapon; TRUE; i++) { if (i >= MAX_WEAPONS_KEYS) { next_weapon = 13; break; } if (TEST(pp->WpnFlags, BIT(i)) && pp->WpnAmmo[i]) { next_weapon = i; break; } } } SET(loc->bits, next_weapon + 1); } if (buttonMap.ButtonPressed(gamefunc_Previous_Weapon)) { USERp u = User[pp->PlayerSprite]; short prev_weapon = u->WeaponNum - 1; short start_weapon; buttonMap.ClearButton(gamefunc_Previous_Weapon); start_weapon = u->WeaponNum - 1; if (u->WeaponNum == WPN_SWORD) { prev_weapon = 13; } else if (u->WeaponNum == WPN_STAR) { prev_weapon = 14; } else { prev_weapon = -1; for (i = start_weapon; TRUE; i--) { if (i <= -1) i = WPN_HEART; if (TEST(pp->WpnFlags, BIT(i)) && pp->WpnAmmo[i]) { prev_weapon = i; break; } } } SET(loc->bits, prev_weapon + 1); } if (buttonMap.ButtonDown(gamefunc_Alt_Weapon)) { buttonMap.ClearButton(gamefunc_Alt_Weapon); USERp u = User[pp->PlayerSprite]; short const which_weapon = u->WeaponNum + 1; SET(loc->bits, which_weapon); } inv_hotkey = 0; if (buttonMap.ButtonDown(gamefunc_MedKit)) inv_hotkey = INVENTORY_MEDKIT+1; if (buttonMap.ButtonDown(gamefunc_Smoke_Bomb)) inv_hotkey = INVENTORY_CLOAK+1; if (buttonMap.ButtonDown(gamefunc_NightVision)) inv_hotkey = INVENTORY_NIGHT_VISION+1; if (buttonMap.ButtonDown(gamefunc_Gas_Bomb)) inv_hotkey = INVENTORY_CHEMBOMB+1; if (buttonMap.ButtonDown(gamefunc_Flash_Bomb) && dimensionmode == 3) inv_hotkey = INVENTORY_FLASHBOMB+1; if (buttonMap.ButtonDown(gamefunc_Caltrops)) inv_hotkey = INVENTORY_CALTROPS+1; SET(loc->bits, inv_hotkey<bits, SK_INV_USE, buttonMap.ButtonDown(gamefunc_Inventory)); SET_LOC_KEY(loc->bits, SK_OPERATE, buttonMap.ButtonDown(gamefunc_Open)); SET_LOC_KEY(loc->bits, SK_JUMP, buttonMap.ButtonDown(gamefunc_Jump)); SET_LOC_KEY(loc->bits, SK_CRAWL, buttonMap.ButtonDown(gamefunc_Crouch)); SET_LOC_KEY(loc->bits, SK_TURN_180, buttonMap.ButtonDown(gamefunc_TurnAround)); SET_LOC_KEY(loc->bits, SK_INV_LEFT, buttonMap.ButtonDown(gamefunc_Inventory_Left)); SET_LOC_KEY(loc->bits, SK_INV_RIGHT, buttonMap.ButtonDown(gamefunc_Inventory_Right)); SET_LOC_KEY(loc->bits, SK_HIDE_WEAPON, buttonMap.ButtonDown(gamefunc_Holster_Weapon)); // need BUTTON SET_LOC_KEY(loc->bits, SK_CRAWL_LOCK, buttonMap.ButtonDown(gamefunc_Toggle_Crouch)); if (gNet.MultiGameType == MULTI_GAME_COOPERATIVE) { if (buttonMap.ButtonDown(gamefunc_See_Coop_View)) { buttonMap.ClearButton(gamefunc_See_Coop_View); screenpeek = connectpoint2[screenpeek]; if (screenpeek < 0) screenpeek = connecthead; if (dimensionmode != 2 && screenpeek == myconnectindex) { // JBF: figure out what's going on here DoPlayerDivePalette(pp); // Check Dive again DoPlayerNightVisionPalette(pp); // Check Night Vision again } else { PLAYERp tp = Player+screenpeek; DoPlayerDivePalette(tp); DoPlayerNightVisionPalette(tp); } } } if (!tied) FunctionKeys(pp); if (buttonMap.ButtonDown(gamefunc_Toggle_Crosshair)) { buttonMap.ClearButton(gamefunc_Toggle_Crosshair); pToggleCrosshair(); } } #define MAP_WHITE_SECTOR (LT_GREY + 2) #define MAP_RED_SECTOR (RED + 6) #define MAP_FLOOR_SPRITE (RED + 8) #define MAP_ENEMY (RED + 10) #define MAP_SPRITE (FIRE + 8) #define MAP_PLAYER (GREEN + 6) #define MAP_BLOCK_SPRITE (DK_BLUE + 6) void drawoverheadmap(int cposx, int cposy, int czoom, short cang) { int i, j, k, l, x1, y1, x2, y2, x3, y3, x4, y4, ox, oy, xoff, yoff; int dax, day, cosang, sinang, xspan, yspan, sprx, spry; int xrepeat, yrepeat, z1, z2, startwall, endwall, tilenum, daang; int xvect, yvect, xvect2, yvect2; char col; walltype *wal, *wal2; spritetype *spr; short p; static int pspr_ndx[8]= {0,0,0,0,0,0,0,0}; SWBOOL sprisplayer = FALSE; short txt_x, txt_y; int32_t tmpydim = (xdim * 5) / 8; renderSetAspect(65536, divscale16(tmpydim * 320, xdim * 200)); // draw location text if (gs.BorderNum <= BORDER_BAR-1) { txt_x = 7; txt_y = 168; } else { txt_x = 7; txt_y = 147; } if (ScrollMode2D) { minigametext(txt_x,txt_y-7,"Follow Mode",2+8); } sprintf(ds,"%s",currentLevel->DisplayName()); minigametext(txt_x,txt_y,ds,2+8); ////////////////////////////////// xvect = sintable[(2048 - cang) & 2047] * czoom; yvect = sintable[(1536 - cang) & 2047] * czoom; xvect2 = mulscale16(xvect, yxaspect); yvect2 = mulscale16(yvect, yxaspect); // Draw red lines for (i = 0; i < numsectors; i++) { startwall = sector[i].wallptr; endwall = sector[i].wallptr + sector[i].wallnum - 1; z1 = sector[i].ceilingz; z2 = sector[i].floorz; for (j = startwall, wal = &wall[startwall]; j <= endwall; j++, wal++) { k = wal->nextwall; if ((unsigned)k >= MAXWALLS) continue; if (!mapcheat) { if ((show2dwall[j >> 3] & (1 << (j & 7))) == 0) continue; if ((k > j) && ((show2dwall[k >> 3] & (1 << (k & 7))) > 0)) continue; } if (sector[wal->nextsector].ceilingz == z1) if (sector[wal->nextsector].floorz == z2) if (((wal->cstat | wall[wal->nextwall].cstat) & (16 + 32)) == 0) continue; col = 152; //if (dimensionmode == 2) if (dimensionmode == 6) { if (sector[i].floorz != sector[i].ceilingz) if (sector[wal->nextsector].floorz != sector[wal->nextsector].ceilingz) if (((wal->cstat | wall[wal->nextwall].cstat) & (16 + 32)) == 0) if (sector[i].floorz == sector[wal->nextsector].floorz) continue; if (sector[i].floorpicnum != sector[wal->nextsector].floorpicnum) continue; if (sector[i].floorshade != sector[wal->nextsector].floorshade) continue; col = 12; // 1=white / 31=black / 44=green / 56=pink / 128=yellow / 210=blue / 248=orange / 255=purple } ox = wal->x - cposx; oy = wal->y - cposy; x1 = mulscale16(ox, xvect) - mulscale16(oy, yvect); y1 = mulscale16(oy, xvect2) + mulscale16(ox, yvect2); wal2 = &wall[wal->point2]; ox = wal2->x - cposx; oy = wal2->y - cposy; x2 = mulscale16(ox, xvect) - mulscale16(oy, yvect); y2 = mulscale16(oy, xvect2) + mulscale16(ox, yvect2); renderDrawLine(x1 + (xdim << 11), y1 + (ydim << 11), x2 + (xdim << 11), y2 + (ydim << 11), col); } } // Draw sprites k = Player[screenpeek].PlayerSprite; for (i = 0; i < numsectors; i++) for (j = headspritesect[i]; j >= 0; j = nextspritesect[j]) { for (p=connecthead; p >= 0; p=connectpoint2[p]) { if (Player[p].PlayerSprite == j) { if (sprite[Player[p].PlayerSprite].xvel > 16) pspr_ndx[myconnectindex] = (((int32_t) totalclock>>4)&3); sprisplayer = TRUE; goto SHOWSPRITE; } } if (mapcheat || (show2dsprite[j >> 3] & (1 << (j & 7))) > 0) { SHOWSPRITE: spr = &sprite[j]; col = 56; // 1=white / 31=black / 44=green / 56=pink / 128=yellow / 210=blue / 248=orange / 255=purple if ((spr->cstat & 1) > 0) col = 248; if (j == k) col = 31; sprx = spr->x; spry = spr->y; k = spr->statnum; if ((k >= 1) && (k <= 8) && (k != 2)) // Interpolate moving { sprx = sprite[j].x; spry = sprite[j].y; } switch (spr->cstat & 48) { case 0: // Regular sprite if (Player[p].PlayerSprite == j) { ox = sprx - cposx; oy = spry - cposy; x1 = mulscale16(ox, xvect) - mulscale16(oy, yvect); y1 = mulscale16(oy, xvect2) + mulscale16(ox, yvect2); if (dimensionmode == 5 && (gNet.MultiGameType != MULTI_GAME_COMMBAT || j == Player[screenpeek].PlayerSprite)) { ox = (sintable[(spr->ang + 512) & 2047] >> 7); oy = (sintable[(spr->ang) & 2047] >> 7); x2 = mulscale16(ox, xvect) - mulscale16(oy, yvect); y2 = mulscale16(oy, xvect) + mulscale16(ox, yvect); if (j == Player[screenpeek].PlayerSprite) { x2 = 0L; y2 = -(czoom << 5); } x3 = mulscale16(x2, yxaspect); y3 = mulscale16(y2, yxaspect); renderDrawLine(x1 - x2 + (xdim << 11), y1 - y3 + (ydim << 11), x1 + x2 + (xdim << 11), y1 + y3 + (ydim << 11), col); renderDrawLine(x1 - y2 + (xdim << 11), y1 + x3 + (ydim << 11), x1 + x2 + (xdim << 11), y1 + y3 + (ydim << 11), col); renderDrawLine(x1 + y2 + (xdim << 11), y1 - x3 + (ydim << 11), x1 + x2 + (xdim << 11), y1 + y3 + (ydim << 11), col); } else { if (((gotsector[i >> 3] & (1 << (i & 7))) > 0) && (czoom > 192)) { daang = (spr->ang - cang) & 2047; if (j == Player[screenpeek].PlayerSprite) { x1 = 0; //y1 = (yxaspect << 2); y1 = 0; daang = 0; } // Special case tiles if (spr->picnum == 3123) break; if (sprisplayer) { if (gNet.MultiGameType != MULTI_GAME_COMMBAT || j == Player[screenpeek].PlayerSprite) rotatesprite((x1 << 4) + (xdim << 15), (y1 << 4) + (ydim << 15), mulscale16(czoom * (spr->yrepeat), yxaspect), daang, 1196+pspr_ndx[myconnectindex], spr->shade, spr->pal, (spr->cstat & 2) >> 1, windowxy1.x, windowxy1.y, windowxy2.x, windowxy2.y); } else rotatesprite((x1 << 4) + (xdim << 15), (y1 << 4) + (ydim << 15), mulscale16(czoom * (spr->yrepeat), yxaspect), daang, spr->picnum, spr->shade, spr->pal, (spr->cstat & 2) >> 1, windowxy1.x, windowxy1.y, windowxy2.x, windowxy2.y); } } } break; case 16: // Rotated sprite x1 = sprx; y1 = spry; tilenum = spr->picnum; xoff = (int)tileLeftOffset(tilenum) + (int)spr->xoffset; if ((spr->cstat & 4) > 0) xoff = -xoff; k = spr->ang; l = spr->xrepeat; dax = sintable[k & 2047] * l; day = sintable[(k + 1536) & 2047] * l; l = tilesiz[tilenum].x; k = (l >> 1) + xoff; x1 -= mulscale16(dax, k); x2 = x1 + mulscale16(dax, l); y1 -= mulscale16(day, k); y2 = y1 + mulscale16(day, l); ox = x1 - cposx; oy = y1 - cposy; x1 = mulscale16(ox, xvect) - mulscale16(oy, yvect); y1 = mulscale16(oy, xvect2) + mulscale16(ox, yvect2); ox = x2 - cposx; oy = y2 - cposy; x2 = mulscale16(ox, xvect) - mulscale16(oy, yvect); y2 = mulscale16(oy, xvect2) + mulscale16(ox, yvect2); renderDrawLine(x1 + (xdim << 11), y1 + (ydim << 11), x2 + (xdim << 11), y2 + (ydim << 11), col); break; case 32: // Floor sprite if (dimensionmode == 5) { tilenum = spr->picnum; xoff = (int)tileLeftOffset(tilenum) + (int)spr->xoffset; yoff = (int)tileTopOffset(tilenum) + (int)spr->yoffset; if ((spr->cstat & 4) > 0) xoff = -xoff; if ((spr->cstat & 8) > 0) yoff = -yoff; k = spr->ang; cosang = sintable[(k + 512) & 2047]; sinang = sintable[k]; xspan = tilesiz[tilenum].x; xrepeat = spr->xrepeat; yspan = tilesiz[tilenum].y; yrepeat = spr->yrepeat; dax = ((xspan >> 1) + xoff) * xrepeat; day = ((yspan >> 1) + yoff) * yrepeat; x1 = sprx + mulscale16(sinang, dax) + mulscale16(cosang, day); y1 = spry + mulscale16(sinang, day) - mulscale16(cosang, dax); l = xspan * xrepeat; x2 = x1 - mulscale16(sinang, l); y2 = y1 + mulscale16(cosang, l); l = yspan * yrepeat; k = -mulscale16(cosang, l); x3 = x2 + k; x4 = x1 + k; k = -mulscale16(sinang, l); y3 = y2 + k; y4 = y1 + k; ox = x1 - cposx; oy = y1 - cposy; x1 = mulscale16(ox, xvect) - mulscale16(oy, yvect); y1 = mulscale16(oy, xvect2) + mulscale16(ox, yvect2); ox = x2 - cposx; oy = y2 - cposy; x2 = mulscale16(ox, xvect) - mulscale16(oy, yvect); y2 = mulscale16(oy, xvect2) + mulscale16(ox, yvect2); ox = x3 - cposx; oy = y3 - cposy; x3 = mulscale16(ox, xvect) - mulscale16(oy, yvect); y3 = mulscale16(oy, xvect2) + mulscale16(ox, yvect2); ox = x4 - cposx; oy = y4 - cposy; x4 = mulscale16(ox, xvect) - mulscale16(oy, yvect); y4 = mulscale16(oy, xvect2) + mulscale16(ox, yvect2); renderDrawLine(x1 + (xdim << 11), y1 + (ydim << 11), x2 + (xdim << 11), y2 + (ydim << 11), col); renderDrawLine(x2 + (xdim << 11), y2 + (ydim << 11), x3 + (xdim << 11), y3 + (ydim << 11), col); renderDrawLine(x3 + (xdim << 11), y3 + (ydim << 11), x4 + (xdim << 11), y4 + (ydim << 11), col); renderDrawLine(x4 + (xdim << 11), y4 + (ydim << 11), x1 + (xdim << 11), y1 + (ydim << 11), col); } break; } } } // Draw white lines for (i = 0; i < numsectors; i++) { startwall = sector[i].wallptr; endwall = sector[i].wallptr + sector[i].wallnum - 1; for (j = startwall, wal = &wall[startwall]; j <= endwall; j++, wal++) { if ((uint16_t)wal->nextwall < MAXWALLS) continue; if (!mapcheat && (show2dwall[j >> 3] & (1 << (j & 7))) == 0) continue; if (!tileGetTexture(wal->picnum)->isValid()) continue; ox = wal->x - cposx; oy = wal->y - cposy; x1 = mulscale16(ox, xvect) - mulscale16(oy, yvect); y1 = mulscale16(oy, xvect2) + mulscale16(ox, yvect2); wal2 = &wall[wal->point2]; ox = wal2->x - cposx; oy = wal2->y - cposy; x2 = mulscale16(ox, xvect) - mulscale16(oy, yvect); y2 = mulscale16(oy, xvect2) + mulscale16(ox, yvect2); renderDrawLine(x1 + (xdim << 11), y1 + (ydim << 11), x2 + (xdim << 11), y2 + (ydim << 11), 24); } } videoSetCorrectedAspect(); } int RandomRange(int range) { uint32_t rand_num; uint32_t value; if (range <= 0) return 0; rand_num = RANDOM(); if (rand_num == 65535U) rand_num--; // shift values to give more precision value = (rand_num << 14) / ((65535UL << 14) / range); if (value >= (uint32_t)range) value = range - 1; return value; } int StdRandomRange(int range) { uint32_t rand_num; uint32_t value; if (range <= 0) return 0; rand_num = STD_RANDOM(); if (rand_num == RAND_MAX) rand_num--; // shift values to give more precision #if (RAND_MAX > 0x7fff) value = rand_num / (((int)RAND_MAX) / range); #else value = (rand_num << 14) / ((((int)RAND_MAX) << 14) / range); #endif if (value >= (uint32_t)range) value = range - 1; return value; } #include "saveable.h" saveable_module saveable_build{}; void Saveable_Init_Dynamic() { static saveable_data saveable_build_data[] = { {sector, MAXSECTORS*sizeof(sectortype)}, {sprite, MAXSPRITES*sizeof(spritetype)}, {wall, MAXWALLS*sizeof(walltype)}, }; saveable_build.data = saveable_build_data; saveable_build.numdata = NUM_SAVEABLE_ITEMS(saveable_build_data); } void GameInterface::set_hud_layout(int requested_size) { gs.BorderNum = 9 - requested_size; SetBorder(Player + myconnectindex); } ::GameInterface* CreateInterface() { return new GameInterface; } GameStats GameInterface::getStats() { PLAYERp pp = Player + myconnectindex; return { pp->Kills, TotalKillable, pp->SecretsFound, LevelSecrets, PlayClock / 120, 0 }; } void GameInterface::FreeGameData() { TerminateLevel(); } void GameInterface::UpdateScreenSize() { // Fixme. The underlying code here is too broken and must be refactored first. } END_SW_NS