raze/source/games/sw/src/game.cpp
Christoph Oelckers 454816299e - reorganized loading of textures.
Due to dependencies on initializing some data in app_init it was not possible to cleanly set up the fonts.
This adds a game-side function for loading the entire palettes before starting with the texture data and another one for loading game-side texture data.
This now allows fully setting up the palettes before starting with the textures and to fully set up the textures before reading the .def files.

All this is needed because to properly initialize, the fonts need to be able to access the fully initialized texture state, including replacements and hires substitutions from the .def files.
2021-06-01 11:05:26 +02:00

762 lines
17 KiB
C++

//-------------------------------------------------------------------------
/*
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 "names2.h"
#include "panel.h"
#include "game.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 "automap.h"
#include "statusbar.h"
#include "texturemanager.h"
#include "st_start.h"
#include "i_interface.h"
#include "mytypes.h"
#include "menus.h"
#include "gamecontrol.h"
#include "misc.h"
#include "misc.h"
#include "break.h"
#include "light.h"
#include "misc.h"
#include "jsector.h"
#include "gameconfigfile.h"
#include "printf.h"
#include "m_argv.h"
#include "debugbreak.h"
#include "razemenu.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 "screenjob_.h"
#include "inputstate.h"
#include "gamestate.h"
#include "d_net.h"
#include "v_draw.h"
#include "interpolate.h"
//#include "crc32.h"
CVAR(Bool, sw_ninjahack, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_SERVERINFO);
CVAR(Bool, sw_darts, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
CVAR(Bool, sw_bunnyrockets, false, CVAR_SERVERINFO | CVAR_CHEAT); // This is a cheat, so don't save.
BEGIN_SW_NS
void pClearSpriteList(PLAYERp pp);
extern int sw_snd_scratch;
int GameVersion = 20;
bool NoMeters = false;
int FinishAnim = 0;
bool ReloadPrompt = false;
bool NewGame = false;
//Miscellaneous variables
bool FinishedLevel = false;
short screenpeek = 0;
int GodMode = false;
short Skill = 2;
short TotalKillable;
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;
bool PlayerTrackingMode = false;
bool SlowMode = false;
bool DebugOperate = false;
void LoadingLevelScreen(void);
uint8_t FakeMultiNumPlayers;
int totalsynctics;
int OrigCommPlayers=0;
extern uint8_t CommPlayers;
extern bool CommEnabled;
bool CameraTestMode = false;
char ds[645]; // debug string
extern short NormalVisibility;
bool CommandSetup = false;
char buffer[80], ch;
uint8_t DebugPrintColor = 255;
FString ThemeSongs[6];
int ThemeTrack[6];
/// L O C A L P R O T O T Y P E S /////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
#define x(a, b) registerName(#a, b);
static void SetTileNames()
{
auto registerName = [](const char* name, int index)
{
TexMan.AddAlias(name, tileGetTexture(index));
};
#include "namelist.h"
}
#undef x
void GameInterface::LoadGameTextures()
{
LoadKVXFromScript("swvoxfil.txt"); // Load voxels from script file
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void GameInterface::app_init()
{
GameTicRate = 40;
InitCheats();
automapping = 1;
gs = gs_defaults;
for (int 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();
numplayers = 1; myconnectindex = 0;
connecthead = 0; connectpoint2[0] = -1;
if (SW_SHAREWARE && 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.");
}
//Connect();
SortBreakInfo();
parallaxtype = 1;
SW_InitMultiPsky();
memset(Track, 0, sizeof(Track));
memset(Player, 0, sizeof(Player));
for (int i = 0; i < MAX_SW_PLAYERS; i++)
INITLIST(&Player[i].PanelSpriteList);
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
SetTileNames();
userConfig.AddDefs.reset();
InitFX();
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void GameInterface::DrawBackground(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);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void InitLevelGlobals(void)
{
ChopTics = 0;
automapMode = am_off;
PlayerGravity = 24;
wait_active_check_offset = 0;
PlaxCeilGlobZadjust = PlaxFloorGlobZadjust = Z(500);
FinishedLevel = false;
AnimCnt = 0;
left_foot = false;
screenpeek = myconnectindex;
ClearInterpolations();
gNet.TimeLimitClock = gNet.TimeLimit;
for (auto& b : bosswasseen) b = false;
memset(BossSpriteNum,-1,sizeof(BossSpriteNum));
}
//---------------------------------------------------------------------------
//
// GLOBAL RESETS NOT DONE for LOAD GAME
//
//---------------------------------------------------------------------------
void InitLevelGlobals2(void)
{
InitTimingVars();
TotalKillable = 0;
Bunny_Count = 0;
FinishAnim = false;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void InitLevel(MapRecord *maprec)
{
Terminate3DSounds();
// A few IMPORTANT GLOBAL RESETS
InitLevelGlobals();
Mus_Stop();
if (!maprec)
{
I_Error("Attempt to start game without level");
return;
}
InitLevelGlobals2();
if (NewGame)
{
for (int i = 0; i < MAX_SW_PLAYERS; i++)
{
// don't jack with the playerreadyflag
int ready_bak = Player[i].playerreadyflag;
int 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));
}
int16_t ang;
engineLoadBoard(maprec->fileName, SW_SHAREWARE ? 1 : 0, &Player[0].pos, &ang, &Player[0].cursectnum);
currentLevel = maprec;
SECRET_SetMapName(currentLevel->DisplayName(), currentLevel->name);
STAT_NewLevel(currentLevel->fileName);
Player[0].angle.ang = buildang(ang);
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();
PlaceSectorObjectsOnTracks();
PlaceActorsOnTracks();
PostSetupSectorObject();
SetupMirrorTiles();
initlava();
CollectPortals();
// reset NewGame
NewGame = false;
setLevelStarted(maprec);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void InitRunLevel(void)
{
Mus_Stop();
DoTheCache();
// send packets with player info
InitNetPlayerOptions();
// Initialize Game part of network code
InitNetVars();
if (currentLevel)
{
PlaySong(currentLevel->music, currentLevel->cdSongId);
}
InitPrediction(&Player[myconnectindex]);
InitTimingVars();
if (snd_ambience)
StartAmbientSound();
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void TerminateLevel(void)
{
videoFadePalette(0, 0, 0, 0);
if (!currentLevel) return;
int i, stat, pnum, ndx;
SECT_USERp* sectu;
// Free any track points
for (ndx = 0; ndx < MAX_TRACKS; ndx++)
{
Track[ndx].FreeTrackPoints();
}
// 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;
StatIterator it(stat);
if ((i = it.NextIndex()) >= 0)
{
if (User[i].Data()) puser[pnum].CopyFromUser(User[i].Data());
}
}
// Kill User memory and delete sprites
for (stat = 0; stat < MAXSTATUS; stat++)
{
StatIterator it(stat);
while ((i = it.NextIndex()) >= 0)
{
KillSprite(i);
}
}
// Free SectUser memory
for (auto& su : SectUser) su.Clear();
TRAVERSE_CONNECT(pnum)
{
PLAYERp pp = Player + pnum;
// Free panel sprites for players
pClearSpriteList(pp);
pp->cookieTime = 0;
memset(pp->cookieQuote, 0, sizeof(pp->cookieQuote));
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);
}
}
using namespace ShadowWarrior;
static bool DidOrderSound;
static int zero = 0;
static void PlayOrderSound()
{
if (!DidOrderSound)
{
DidOrderSound = true;
int choose_snd = STD_RANDOM_RANGE(1000);
if (choose_snd > 500)
PlaySound(DIGI_WANGORDER1, v3df_dontpan, CHAN_BODY, CHANF_UI);
else
PlaySound(DIGI_WANGORDER2, v3df_dontpan, CHAN_BODY, CHANF_UI);
}
}
void GameInterface::LevelCompleted(MapRecord* map, int skill)
{
//ResetPalette(mpp);
COVER_SetReverb(0); // Reset reverb
Player[myconnectindex].Reverb = 0;
StopSound();
STAT_Update(map == nullptr);
SummaryInfo info{};
info.kills = Player[screenpeek].Kills;
info.maxkills = TotalKillable;
info.secrets = Player[screenpeek].SecretsFound;
info.maxsecrets = LevelSecrets;
info.time = PlayClock / 120;
ShowIntermission(currentLevel, map, &info, [=](bool)
{
if (map == nullptr)
{
FinishAnim = false;
PlaySong(ThemeSongs[0], ThemeTrack[0]);
if (isShareware())
{
PlayOrderSound();
gameaction = ga_creditsmenu;
}
else gameaction = ga_mainmenu;
}
else gameaction = ga_nextlevel;
});
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void GameInterface::NextLevel(MapRecord *map, int skill)
{
if (skill != -1) Skill = skill;
ShadowWarrior::NewGame = false;
InitLevel(map);
InitRunLevel();
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void GameInterface::NewGame(MapRecord *map, int skill, bool)
{
if (skill != -1) Skill = skill;
ShadowWarrior::NewGame = true;
InitLevel(map);
InitRunLevel();
gameaction = ga_level;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void GameInterface::Ticker(void)
{
int i;
TRAVERSE_CONNECT(i)
{
auto pp = Player + i;
pp->lastinput = pp->input;
pp->input = playercmds[i].ucmd;
if (pp->lastinput.actions & SB_CENTERVIEW) pp->input.actions |= SB_CENTERVIEW;
}
domovethings();
r_NoInterpolate = paused;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void GameInterface::Render()
{
if (paused)
{
smoothratio = MaxSmoothRatio;
}
else
{
smoothratio = I_GetTimeFrac() * MaxSmoothRatio;
}
drawtime.Reset();
drawtime.Clock();
drawscreen(Player + screenpeek, smoothratio);
drawtime.Unclock();
}
void GameInterface::Startup()
{
PlayLogos(ga_mainmenunostopsound, ga_mainmenu, false);
}
void GameInterface::ErrorCleanup()
{
// Make sure we do not leave the game in an unstable state
TerminateLevel();
FinishAnim = false;
}
void GameInterface::ExitFromMenu()
{
endoomName = !isShareware() ? "swreg.bin" : "shadsw.bin";
ST_Endoom();
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
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();
}
void GameInterface::FreeLevelData()
{
TerminateLevel();
::GameInterface::FreeLevelData();
}
int GameInterface::Voxelize(int sprnum)
{
return (aVoxelArray[sprnum].Voxel);
}
END_SW_NS