//------------------------------------------------------------------------- /* 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 "misc.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); extern SWBOOL mapcheat; extern SWBOOL MultiPlayQuitFlag; extern int sw_snd_scratch; char DemoName[15][16]; int GameVersion = 20; char DemoText[3][64]; int DemoTextYstart = 0; int Follow_posx=0,Follow_posy=0; SWBOOL NoMeters = FALSE; SWBOOL GraphicsMode = FALSE; char PlayerNameArg[32] = ""; SWBOOL CleanExit = 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 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; SWBOOL HasAutoColor = FALSE; uint8_t AutoColor; const GAME_SET gs_defaults = { // Network game settings 0, // GameType 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; MapRecord* NextLevel = nullptr; 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 buffer[80], ch; 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) { if (CleanExit) { //SybexScreen(); } throw CExitEvent(3); } bool LoadLevel(MapRecord *maprec) { int16_t ang; if (engineLoadBoard(maprec->fileName, SW_SHAREWARE ? 1 : 0, (vec3_t *)&Player[0], &ang, &Player[0].cursectnum) == -1) { Printf("Map not found: %s", maprec->fileName.GetChars()); return false; } currentLevel = maprec; SECRET_SetMapName(currentLevel->DisplayName(), currentLevel->name); STAT_NewLevel(currentLevel->fileName); Player[0].q16ang = fix16_from_int(ang); return true; } 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; 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(); { 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 numplayers = 1; myconnectindex = 0; connecthead = 0; connectpoint2[0] = -1; // 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"); InitFonts(); //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(); GraphicsMode = TRUE; InitFX(); // JBF: do it down here so we get a hold of the window handle return true; } 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)); } 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 = false; } 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(); Mus_Stop(); auto maprec = NextLevel; NextLevel = nullptr; if (!maprec) { NewGame = false; return; } InitLevelGlobals2(); if (NewGame) InitNewGame(); if (!LoadLevel(maprec)) { NewGame = false; return; } SetupPreCache(); 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(); // reset NewGame NewGame = FALSE; } void TerminateLevel(void) { int i, nexti, stat, pnum, ndx; SECT_USERp *sectu; // 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) { 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(); DrawTexture(twod, tileGetTexture(TITLE_PIC), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_Color, shadeToLight(20), TAG_DONE); } short PlayerQuitMenuLevel = -1; void MenuLevel(void) { short w,h; M_StartControlPanel(false); M_SetMenu(NAME_Mainmenu); 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_DrawString(160, 170, ds, 1, 16, 0); sprintf(ds,"They are afraid!"); MNU_DrawString(160, 180, ds, 1, 16, 0); } 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 (ExitLevel) { 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; //if (FinishAnim == ANIM_ZILLA) // CreditsLevel(); ExitLevel = FALSE; QuitFlag = FALSE; if (currentLevel->levelNumber != 4 && currentLevel->levelNumber != 20) { NextLevel = FindMapByLevelNum(currentLevel->levelNumber + 1); } } 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) { 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; 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 (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; } //SendVersion(GameVersion); //waitforeverybody(); Mus_Stop(); DoTheCache(); // auto aim / auto run / etc InitPlayerGameSettings(); // send packets with player info InitNetPlayerOptions(); // Initialize Game part of network code (When ready2send != 0) InitNetVars(); if (currentLevel) { PlaySong(currentLevel->labelName, currentLevel->music, currentLevel->cdSongId); } InitPrediction(&Player[myconnectindex]); 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; } } 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; 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"); 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 && !adult_lockout) { inputState.ClearKeyStatus(sc_F1 + fn_key - 1); rts_delay = 0; PlaySoundRTS(fn_key); } return; } if (inputState.ShiftPressed()) { if (fn_key) { inputState.ClearKeyStatus(sc_F1 + fn_key - 1); } 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 (hud_size == Hud_Nothing) { txt_x = 7; txt_y = 168; } else { txt_x = 7; txt_y = 147; } if (ScrollMode2D) { MNU_DrawSmallString(txt_x, txt_y - 7, "Follow Mode", 0, 0); } sprintf(ds,"%s",currentLevel->DisplayName()); MNU_DrawSmallString(txt_x,txt_y,ds,0, 0); ////////////////////////////////// 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; int spnum = -1; if (sprisplayer) { if (gNet.MultiGameType != MULTI_GAME_COMMBAT || j == Player[screenpeek].PlayerSprite) spnum = 1196 + pspr_ndx[myconnectindex]; } else spnum = spr->picnum; double xd = ((x1 << 4) + (xdim << 15)) / 65536.; double yd = ((y1 << 4) + (ydim << 15)) / 65536.; double sc = mulscale16(czoom * (spr->yrepeat), yxaspect) / 65536.; if (spnum >= 0) { DrawTexture(twod, tileGetTexture(5407, true), xd, yd, DTA_FullscreenScale, FSMode_ScaleToFit43, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200, DTA_CenterOffsetRel, true, DTA_TranslationIndex, TRANSLATION(Translation_Remap, spr->pal), DTA_Color, shadeToLight(spr->shade), DTA_Alpha, (spr->cstat & 2) ? 0.33 : 1., TAG_DONE); } } } } 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); } ReservedSpace GameInterface::GetReservedScreenSpace(int viewsize) { return { 0, 48 }; } ::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(); } END_SW_NS