mirror of
https://github.com/dhewm/dhewm3-sdk.git
synced 2025-04-19 07:41:41 +00:00
Import Classic Doom 3
this is basically the diff between the cdoom source and the Doom3 SDK applied to the dhewm3 SDK + README + minimal change in CMakeLists.txt (change game name, disable d3xp because this is based on the base game)
This commit is contained in:
parent
4dd278d6fb
commit
739c6c86b5
20 changed files with 736 additions and 29 deletions
|
@ -2,10 +2,10 @@ cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
|
|||
project(dhewm3sdk)
|
||||
|
||||
option(BASE "Build the base (game/) game code" ON)
|
||||
set(BASE_NAME "base" CACHE STRING "Name of the mod built from game/ (will result in \${BASE_NAME}.dll)")
|
||||
set(BASE_NAME "cdoom" CACHE STRING "Name of the mod built from game/ (will result in \${BASE_NAME}.dll)")
|
||||
set(BASE_DEFS "GAME_DLL" CACHE STRING "Compiler definitions for the mod built from game/")
|
||||
|
||||
option(D3XP "Build the d3xp/ game code" ON)
|
||||
option(D3XP "Build the d3xp/ game code" OFF)
|
||||
set(D3XP_NAME "d3xp" CACHE STRING "Name of the mod built from d3xp/ (will result in \${D3XP_NAME}.dll)")
|
||||
set(D3XP_DEFS "GAME_DLL;_D3XP;CTF" CACHE STRING "Compiler definitions for the mod built from d3xp/")
|
||||
|
||||
|
|
54
README.txt
Normal file
54
README.txt
Normal file
|
@ -0,0 +1,54 @@
|
|||
NOTE from Daniel:
|
||||
The following is (mostly) the original README of the mod and still refers to the SDK etc.
|
||||
|
||||
You can get the game data from http://www.gamefront.com/files/files/9003559/classic_doom_3_1.3.1.zip
|
||||
Just extract that zip to your Doom3/dhewm3 installation.
|
||||
|
||||
After compiling the source, you can start the game with
|
||||
./dhewm3 +set fs_game cdoom
|
||||
|
||||
A big THANK YOU to "SnoopJeDi" (and the rest of the Classic Doom 3 Team) for releasing this mod under GPL!
|
||||
|
||||
The original ReadMe:
|
||||
======================================================================
|
||||
CLASSIC DOOM III - VERSION 1.3.1 Sourcecode
|
||||
======================================================================
|
||||
|
||||
DEVELOPER - Flaming Sheep Software
|
||||
DATE OF RELEASE - October 2007
|
||||
HOMEPAGE - http://cdoom.d3files.com
|
||||
FORUMS - http://cdoom.d3files.com/forum/index.php
|
||||
|
||||
|
||||
======================================================================
|
||||
Description
|
||||
======================================================================
|
||||
|
||||
This is a package of all the files from the Doom 3 Software Development Kit (SDK) that have been modified in the creation of Classic Doom 3. The more predominant features of this code are the following:
|
||||
|
||||
-The intermission/statistics screen displayed between levels (there is an unused key/val combination that would allow mappers to update the GUI graphics depending the sequential progress of the player. This was originally designed to facilitate a map on the intermission screen, but was dropped).
|
||||
|
||||
-Autorun. A very small modification that simply checks the state of ui_autoRun and accordingly either flips input from the run key or leaves it untouched.
|
||||
|
||||
|
||||
For a complete picture of what has been changed, cross-reference this code and the untouched 1.3.1 code using diff or a similar program for your operating system.
|
||||
|
||||
|
||||
|
||||
======================================================================
|
||||
Legal
|
||||
======================================================================
|
||||
|
||||
This modification is only for use with a legitimate version of the game "Doom 3", created by id Software. This modification is not affiliated with id Software in any way.
|
||||
|
||||
If there is any technical issues directly related to this modification that is not a known issue with the game "Doom 3", please contact Flaming Sheep Software either through email or the website at http://cdoom.d3files.com
|
||||
|
||||
Activision and id Software do not hold responsibility for any technical issues that you may encouter through the use of this modification.
|
||||
|
||||
Distribution of this modification can be by any means necessary, aslong as it is kept as a complete package.
|
||||
|
||||
[Any and all assets or code presented in this modification are not allowed to be distributed as part of any other package, in whole or in part.] - UPDATE: got permission by the Coder (SnoopJeDi) to release this source under GPLv3
|
||||
|
||||
If you have any questions about the uses that are allowed with the modification, please contact Flaming Sheep Software at http://cdoom.d3files.com
|
||||
|
||||
======================================================================
|
|
@ -4100,6 +4100,17 @@ void idEntity::Event_FadeSound( int channel, float to, float over ) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idEntity::FadeMusic
|
||||
================
|
||||
*/
|
||||
void idEntity::FadeMusic( int channel, float to, float over ) { //SnoopJeDi
|
||||
if ( spawnArgs.GetBool( "s_music" ) ) {
|
||||
Event_FadeSound( channel, to, over );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idEntity::Event_GetWorldOrigin
|
||||
|
|
|
@ -360,6 +360,7 @@ public:
|
|||
|
||||
void ServerSendEvent( int eventId, const idBitMsg *msg, bool saveEvent, int excludeClient ) const;
|
||||
void ClientSendEvent( int eventId, const idBitMsg *msg ) const;
|
||||
void FadeMusic( int channel, float to, float over ); // SnoopJeDi
|
||||
|
||||
protected:
|
||||
renderEntity_t renderEntity; // used to present a model to the renderer
|
||||
|
|
|
@ -187,6 +187,7 @@ void idGameLocal::Clear( void ) {
|
|||
num_entities = 0;
|
||||
spawnedEntities.Clear();
|
||||
activeEntities.Clear();
|
||||
musicSpeakers.Clear(); // SnoopJeDi - Housekeeping!
|
||||
numEntitiesToDeactivate = 0;
|
||||
sortPushers = false;
|
||||
sortTeamMasters = false;
|
||||
|
@ -307,6 +308,15 @@ void idGameLocal::Init( void ) {
|
|||
|
||||
InitConsoleCommands();
|
||||
|
||||
if ( !g_lms_bind_run_once.GetBool() ) { // SnoopJeDi - LMS addition
|
||||
//The default config file contains proper value for mod_validSkins cvar.
|
||||
//We want to run this once after the base doom config file has run so we can
|
||||
//have the correct xp binds
|
||||
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "exec default.cfg\n" );
|
||||
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "seta g_lms_bind_run_once 1\n" );
|
||||
cmdSystem->ExecuteCommandBuffer();
|
||||
}
|
||||
|
||||
// load default scripts
|
||||
program.Startup( SCRIPT_DEFAULT );
|
||||
|
||||
|
@ -522,6 +532,14 @@ void idGameLocal::SaveGame( idFile *f ) {
|
|||
savegame.WriteInt( previousTime );
|
||||
savegame.WriteInt( time );
|
||||
|
||||
savegame.WriteInt( monsters ); // SnoopJeDi - Added by deadite4
|
||||
savegame.WriteInt( items ); // SnoopJeDi
|
||||
int numsecrets = secretAreas.Num();
|
||||
savegame.WriteInt( numsecrets );
|
||||
for ( i = 0; i != numsecrets; i++ ) {
|
||||
savegame.WriteInt( secretAreas[ i ] );
|
||||
}
|
||||
|
||||
savegame.WriteInt( vacuumAreaNum );
|
||||
|
||||
savegame.WriteInt( entityDefBits );
|
||||
|
@ -871,6 +889,13 @@ void idGameLocal::LoadMap( const char *mapName, int randseed ) {
|
|||
memset( spawnIds, -1, sizeof( spawnIds ) );
|
||||
spawnCount = INITIAL_SPAWN_COUNT;
|
||||
|
||||
//musicSpeakers.Clear(); // SnoopJeDi: This doesn't feel right, might cause some weird bugs if more than one music speaker is used.
|
||||
s_music_vol.ClearModified();
|
||||
s_music_vol.SetModified(); // SnoopJeDi: we want to fade on level start
|
||||
secretAreas.Clear(); // SnoopJeDi
|
||||
monsters = 0;
|
||||
items = 0;
|
||||
|
||||
spawnedEntities.Clear();
|
||||
activeEntities.Clear();
|
||||
numEntitiesToDeactivate = 0;
|
||||
|
@ -1163,6 +1188,9 @@ idGameLocal::InitFromNewMap
|
|||
*/
|
||||
void idGameLocal::InitFromNewMap( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, bool isServer, bool isClient, int randseed ) {
|
||||
|
||||
musicSpeakers.Clear(); // SnoopJeDi: new map, not reload, so clear the list
|
||||
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "heartbeat\n" ); // SnoopJeDi - iddevnet fix to keep server from dropping off master list
|
||||
|
||||
this->isServer = isServer;
|
||||
this->isClient = isClient;
|
||||
this->isMultiplayer = isServer || isClient;
|
||||
|
@ -1182,6 +1210,9 @@ void idGameLocal::InitFromNewMap( const char *mapName, idRenderWorld *renderWorl
|
|||
|
||||
InitScriptForMap();
|
||||
|
||||
monsters = 0; // SnoopJeDi
|
||||
items = 0; // SnoopJeDi
|
||||
|
||||
MapPopulate();
|
||||
|
||||
mpGame.Reset();
|
||||
|
@ -1339,6 +1370,15 @@ bool idGameLocal::InitFromSaveGame( const char *mapName, idRenderWorld *renderWo
|
|||
savegame.ReadInt( previousTime );
|
||||
savegame.ReadInt( time );
|
||||
|
||||
savegame.ReadInt( monsters ); // SnoopJeDi
|
||||
savegame.ReadInt( items ); // SnoopJeDi
|
||||
int numsecrets;
|
||||
savegame.ReadInt( numsecrets );
|
||||
secretAreas.SetNum( numsecrets, true ); // Resize the list so we don't violate memory rules
|
||||
for ( i = 0; i != numsecrets; i++ ) {
|
||||
savegame.ReadInt( secretAreas[ i ] );
|
||||
}
|
||||
|
||||
savegame.ReadInt( vacuumAreaNum );
|
||||
|
||||
savegame.ReadInt( entityDefBits );
|
||||
|
@ -2187,6 +2227,8 @@ gameReturn_t idGameLocal::RunFrame( const usercmd_t *clientCmds ) {
|
|||
previousTime = time;
|
||||
time += msec;
|
||||
realClientTime = time;
|
||||
if ( !isMultiplayer )
|
||||
GetLocalPlayer()->inventory.time = time; // SnoopJeDi - source of dedserver bug 5/21/06
|
||||
|
||||
#ifdef GAME_DLL
|
||||
// allow changing SIMD usage on the fly
|
||||
|
@ -2447,6 +2489,16 @@ makes rendering and sound system calls
|
|||
================
|
||||
*/
|
||||
bool idGameLocal::Draw( int clientNum ) {
|
||||
|
||||
if ( s_music_vol.IsModified() ) { //SnoopJeDi, fade that sound!
|
||||
for ( int i = 0; i < musicSpeakers.Num(); i++ ) {
|
||||
idSound* ent = static_cast<idSound *>(entities[ musicSpeakers[ i ] ]);
|
||||
if (ent)
|
||||
ent->FadeMusic( 0, s_music_vol.GetFloat(), 0 );
|
||||
}
|
||||
s_music_vol.ClearModified();
|
||||
}
|
||||
|
||||
if ( isMultiplayer ) {
|
||||
return mpGame.Draw( clientNum );
|
||||
}
|
||||
|
@ -3121,6 +3173,8 @@ bool idGameLocal::InhibitEntitySpawn( idDict &spawnArgs ) {
|
|||
|
||||
if ( isMultiplayer ) {
|
||||
spawnArgs.GetBool( "not_multiplayer", "0", result );
|
||||
// } else if ( !isMultiplayer ) {
|
||||
// spawnArgs.GetBool( "not_sp", "0", result ); // SnoopJeDi - MP stuff
|
||||
} else if ( g_skill.GetInteger() == 0 ) {
|
||||
spawnArgs.GetBool( "not_easy", "0", result );
|
||||
} else if ( g_skill.GetInteger() == 1 ) {
|
||||
|
@ -4217,7 +4271,7 @@ idGameLocal::UpdateServerInfoFlags
|
|||
*/
|
||||
void idGameLocal::UpdateServerInfoFlags() {
|
||||
gameType = GAME_SP;
|
||||
if ( ( idStr::Icmp( serverInfo.GetString( "si_gameType" ), "deathmatch" ) == 0 ) ) {
|
||||
if ( ( ( idStr::Icmp( serverInfo.GetString( "si_gameType" ), "deathmatch" ) == 0 ) ) || ( ( idStr::Icmp( serverInfo.GetString( "si_gameType" ), "cdoomDM" ) == 0 ) ) ) {
|
||||
gameType = GAME_DM;
|
||||
} else if ( ( idStr::Icmp( serverInfo.GetString( "si_gameType" ), "Tourney" ) == 0 ) ) {
|
||||
gameType = GAME_TOURNEY;
|
||||
|
@ -4368,3 +4422,21 @@ idGameLocal::GetMapLoadingGUI
|
|||
===============
|
||||
*/
|
||||
void idGameLocal::GetMapLoadingGUI( char gui[ MAX_STRING_CHARS ] ) { }
|
||||
|
||||
/*
|
||||
====================
|
||||
idGameLocal::DeactivateSecretAreas - SnoopJeDi
|
||||
Multiple func_secret entities can be used for one secret (ie. E1M1 hallway).
|
||||
Once triggered, all entities with the same SecretNum will be deactivated.
|
||||
====================
|
||||
*/
|
||||
void idGameLocal::DeactivateSecretAreas( int areanum ) {
|
||||
for ( idEntity * ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
|
||||
if ( ent->IsType( idSecret::Type ) ) {
|
||||
idSecret *secret = static_cast<idSecret *>(ent);
|
||||
if ( secret->GetNum() == areanum ) {
|
||||
secret->Deactivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -448,6 +448,14 @@ public:
|
|||
|
||||
bool NeedRestart();
|
||||
|
||||
// void UpdateMusicVol( idSoundEmitter *ent );
|
||||
idList<int> musicSpeakers; //SnoopJeDi - holds entitynum values for speakers with s_music set
|
||||
void DeactivateSecretAreas( int areanum );
|
||||
int monsters;
|
||||
int items;
|
||||
idList<int> secretAreas;
|
||||
|
||||
|
||||
private:
|
||||
const static int INITIAL_SPAWN_COUNT = 1;
|
||||
|
||||
|
|
|
@ -275,6 +275,13 @@ void idItem::Spawn( void ) {
|
|||
idStr giveTo;
|
||||
idEntity * ent;
|
||||
float tsize;
|
||||
const char *kv;
|
||||
|
||||
kv = spawnArgs.GetString( "inv_name", "" );
|
||||
if ( kv != "" ) { // SnoopJeDi - Only if it's something good to eat!
|
||||
gameLocal.items++;
|
||||
}
|
||||
|
||||
|
||||
if ( spawnArgs.GetBool( "dropToFloor" ) ) {
|
||||
PostEventMS( &EV_DropToFloor, 0 );
|
||||
|
@ -364,6 +371,8 @@ bool idItem::Pickup( idPlayer *player ) {
|
|||
return false;
|
||||
}
|
||||
|
||||
player->inventory.itemspickedup++; // SnoopJeDi
|
||||
|
||||
if ( gameLocal.isServer ) {
|
||||
ServerSendEvent( EVENT_PICKUP, NULL, false, -1 );
|
||||
}
|
||||
|
@ -636,6 +645,17 @@ bool idItemPowerup::GiveToPlayer( idPlayer *player ) {
|
|||
if ( player->spectating ) {
|
||||
return false;
|
||||
}
|
||||
if ( !idStr::Icmp( spawnArgs.GetString( "type" ), "4" ) ) {
|
||||
if (player->health >= player->spawnArgs.GetInt( "maxbonushealth" )) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ( !idStr::Icmp( spawnArgs.GetString( "type" ), "5" ) || !idStr::Icmp( spawnArgs.GetString( "type" ), "6" ) ) {
|
||||
if (player->inventory.armor >= player->spawnArgs.GetInt( "maxbonusarmor" )) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
player->GivePowerUp( type, time * 1000 );
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -3156,3 +3156,89 @@ void idPhantomObjects::Think( void ) {
|
|||
BecomeInactive( TH_THINK );
|
||||
}
|
||||
}
|
||||
|
||||
// SnoopJeDi -- Begin
|
||||
|
||||
CLASS_DECLARATION( idEntity, idSecret )
|
||||
EVENT( EV_Touch, idSecret::Event_Touch )
|
||||
END_CLASS
|
||||
|
||||
/*
|
||||
===========================
|
||||
idSecret::idSecret()
|
||||
===========================
|
||||
*/
|
||||
idSecret::idSecret( void ) {
|
||||
}
|
||||
|
||||
/*
|
||||
===========================
|
||||
idSecret::Think()
|
||||
===========================
|
||||
*/
|
||||
void idSecret::Think() {
|
||||
}
|
||||
|
||||
/*
|
||||
===========================
|
||||
idSecret::Spawn()
|
||||
===========================
|
||||
*/
|
||||
void idSecret::Spawn( void ) {
|
||||
SecretNum = spawnArgs.GetInt( "SecretNum", "0" ); //Todo: Increment secret areas automatically for newb mappers?
|
||||
if ( gameLocal.secretAreas.FindIndex( SecretNum ) < 0 ) {
|
||||
gameLocal.secretAreas.Append( SecretNum );
|
||||
}
|
||||
activated = false;
|
||||
GetPhysics()->SetContents( CONTENTS_TRIGGER ); // Can be Touch()-ed, but not solid.
|
||||
}
|
||||
|
||||
/*
|
||||
===========================
|
||||
idSecret::Event_Activate()
|
||||
===========================
|
||||
*/
|
||||
void idSecret::Event_Activate( idEntity *activator ) {
|
||||
Deactivate();
|
||||
}
|
||||
|
||||
/*
|
||||
===========================
|
||||
idSecret::Save()
|
||||
===========================
|
||||
*/
|
||||
void idSecret::Save( idSaveGame *savefile ) const {
|
||||
savefile->WriteInt( SecretNum );
|
||||
}
|
||||
|
||||
/*
|
||||
===========================
|
||||
idSecret::Restore()
|
||||
===========================
|
||||
*/
|
||||
void idSecret::Restore( idRestoreGame *savefile ) {
|
||||
savefile->ReadInt( SecretNum );
|
||||
}
|
||||
|
||||
/*
|
||||
===========================
|
||||
idSecret::Event_Touch()
|
||||
===========================
|
||||
*/
|
||||
void idSecret::Event_Touch( idEntity *other, trace_t *trace ) {
|
||||
if ( !gameLocal.isMultiplayer && !activated && other->IsType(
|
||||
idPlayer::Type) ) {
|
||||
gameLocal.GetLocalPlayer()->SecretArea( SecretNum );
|
||||
Deactivate(); // Don't touch me again!
|
||||
}
|
||||
}
|
||||
|
||||
void idSecret::Deactivate() {
|
||||
activated = true;
|
||||
}
|
||||
|
||||
int idSecret::GetNum() {
|
||||
return SecretNum;
|
||||
}
|
||||
|
||||
// SnoopJeDi -- End
|
||||
|
|
32
game/Misc.h
32
game/Misc.h
|
@ -765,4 +765,36 @@ private:
|
|||
idList<idVec3> lastTargetPos;
|
||||
};
|
||||
|
||||
// SnoopJeDi -- Begin
|
||||
/*
|
||||
==================================
|
||||
|
||||
idSecret - Simple entity for activating secret areas
|
||||
|
||||
==================================
|
||||
*/
|
||||
class idSecret : public idEntity {
|
||||
public:
|
||||
CLASS_PROTOTYPE( idSecret );
|
||||
|
||||
idSecret();
|
||||
void Spawn ( void );
|
||||
void Save( idSaveGame *savefile ) const;
|
||||
void Restore( idRestoreGame *savefile );
|
||||
void Think( void );
|
||||
void Deactivate();
|
||||
int GetNum();
|
||||
|
||||
private:
|
||||
void Event_Touch( idEntity *other, trace_t *trace );
|
||||
int SecretNum;
|
||||
bool activated;
|
||||
|
||||
void Event_Activate( idEntity *activator ); // SnoopJeDi - added 2/13/07, allows mappers to trigger secrets (guis, etc)
|
||||
};
|
||||
// SnoopJeDi -- End
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* !__GAME_MISC_H__ */
|
||||
|
|
|
@ -2767,7 +2767,7 @@ void idMultiplayerGame::ServerCallVote( int clientNum, const idBitMsg &msg ) {
|
|||
assert( vote_gameTypeIndex >= 0 && vote_gameTypeIndex <= 3 );
|
||||
switch ( vote_gameTypeIndex ) {
|
||||
case 0:
|
||||
strcpy( value, "Deathmatch" );
|
||||
strcpy( value, "cdoomDM" );//deadite4
|
||||
break;
|
||||
case 1:
|
||||
strcpy( value, "Tourney" );
|
||||
|
|
304
game/Player.cpp
304
game/Player.cpp
|
@ -143,6 +143,10 @@ void idInventory::Clear( void ) {
|
|||
deplete_rate = 0.0f;
|
||||
deplete_ammount = 0;
|
||||
nextArmorDepleteTime = 0;
|
||||
Secrets = 0; // SnoopJeDi
|
||||
kills = 0; // SnoopJeDi
|
||||
itemspickedup = 0; // SnoopJeDi
|
||||
|
||||
|
||||
memset( ammo, 0, sizeof( ammo ) );
|
||||
|
||||
|
@ -202,6 +206,10 @@ void idInventory::GivePowerUp( idPlayer *player, int powerup, int msec ) {
|
|||
case ADRENALINE:
|
||||
def = gameLocal.FindEntityDef( "powerup_adrenaline", false );
|
||||
break;
|
||||
case HEALTHVIAL:
|
||||
def = gameLocal.FindEntityDef( "item_health_vial", false );
|
||||
break; // SnoopJeDi
|
||||
|
||||
}
|
||||
assert( def );
|
||||
msec = def->dict.GetInt( "time" ) * 1000;
|
||||
|
@ -278,6 +286,10 @@ void idInventory::GetPersistantData( idDict &dict ) {
|
|||
dict.SetInt( "selAudio", selAudio );
|
||||
dict.SetInt( "pdaOpened", pdaOpened );
|
||||
dict.SetInt( "turkeyScore", turkeyScore );
|
||||
dict.SetInt( "Secrets", Secrets ); //SnoopJeDi
|
||||
dict.SetInt( "kills", kills ); // SnoopJeDi
|
||||
dict.SetInt( "itemspickedup", itemspickedup ); // SnoopJeDi
|
||||
|
||||
|
||||
// pdas
|
||||
for ( i = 0; i < pdas.Num(); i++ ) {
|
||||
|
@ -335,6 +347,10 @@ void idInventory::RestoreInventory( idPlayer *owner, const idDict &dict ) {
|
|||
deplete_armor = dict.GetInt( "deplete_armor", "0" );
|
||||
deplete_rate = dict.GetFloat( "deplete_rate", "2.0" );
|
||||
deplete_ammount = dict.GetInt( "deplete_ammount", "1" );
|
||||
Secrets = dict.GetInt( "Secrets", "0" ); //SnoopJeDi
|
||||
kills = dict.GetInt( "kills", "0" ); // SnoopJeDi
|
||||
itemspickedup = dict.GetInt( "itemspickedup", "0" ); // SnoopJeDi
|
||||
|
||||
|
||||
// the clip and powerups aren't restored
|
||||
|
||||
|
@ -437,6 +453,11 @@ void idInventory::Save( idSaveGame *savefile ) const {
|
|||
savefile->WriteFloat( deplete_rate );
|
||||
savefile->WriteInt( deplete_ammount );
|
||||
savefile->WriteInt( nextArmorDepleteTime );
|
||||
savefile->WriteInt( Secrets ); // SnoopJeDi
|
||||
savefile->WriteInt( kills ); // SnoopJeDi
|
||||
savefile->WriteInt( itemspickedup ); // SnoopJeDi
|
||||
savefile->WriteInt( time ); //SnoopJeDi
|
||||
|
||||
|
||||
for( i = 0; i < AMMO_NUMTYPES; i++ ) {
|
||||
savefile->WriteInt( ammo[ i ] );
|
||||
|
@ -533,6 +554,11 @@ void idInventory::Restore( idRestoreGame *savefile ) {
|
|||
savefile->ReadFloat( deplete_rate );
|
||||
savefile->ReadInt( deplete_ammount );
|
||||
savefile->ReadInt( nextArmorDepleteTime );
|
||||
savefile->ReadInt( Secrets ); // SnoopJeDi
|
||||
savefile->ReadInt( kills ); // SnoopJeDi
|
||||
savefile->ReadInt( itemspickedup ); // SnoopJeDi
|
||||
savefile->ReadInt( time ); // SnoopJeDi
|
||||
|
||||
|
||||
for( i = 0; i < AMMO_NUMTYPES; i++ ) {
|
||||
savefile->ReadInt( ammo[ i ] );
|
||||
|
@ -641,7 +667,7 @@ idInventory::AmmoIndexForAmmoClass
|
|||
==============
|
||||
*/
|
||||
ammo_t idInventory::AmmoIndexForAmmoClass( const char *ammo_classname ) const {
|
||||
return idWeapon::GetAmmoNumForName( ammo_classname );
|
||||
return idWeapon::GetAmmoNumForName( ammo_classname );
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -650,7 +676,11 @@ idInventory::AmmoIndexForAmmoClass
|
|||
==============
|
||||
*/
|
||||
int idInventory::MaxAmmoForAmmoClass( idPlayer *owner, const char *ammo_classname ) const {
|
||||
return owner->spawnArgs.GetInt( va( "max_%s", ammo_classname ), "0" );
|
||||
if ( owner->extraammo ) { //SnoopJedi - Moved by deadite4 in 1.3.1
|
||||
return owner->spawnArgs.GetInt( va( "backpack_max_%s", ammo_classname ), "0" );
|
||||
} else {
|
||||
return owner->spawnArgs.GetInt( va( "max_%s", ammo_classname ), "0" );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -785,6 +815,8 @@ bool idInventory::Give( idPlayer *owner, const idDict &spawnArgs, const char *st
|
|||
GivePowerUp( owner, BERSERK, SEC2MS( atof( value ) ) );
|
||||
} else if ( !idStr::Icmp( statname, "mega" ) ) {
|
||||
GivePowerUp( owner, MEGAHEALTH, SEC2MS( atof( value ) ) );
|
||||
} else if ( !idStr::Icmp( statname, "item_health_vial" ) ) { // don't need this, it's not an inventory item.
|
||||
GivePowerUp( owner, HEALTHVIAL, SEC2MS( atof( value ) ) );
|
||||
} else if ( !idStr::Icmp( statname, "weapon" ) ) {
|
||||
tookWeapon = false;
|
||||
for( pos = value; pos != NULL; pos = end ) {
|
||||
|
@ -840,6 +872,8 @@ bool idInventory::Give( idPlayer *owner, const idDict &spawnArgs, const char *st
|
|||
} else if ( !idStr::Icmp( statname, "item" ) || !idStr::Icmp( statname, "icon" ) || !idStr::Icmp( statname, "name" ) ) {
|
||||
// ignore these as they're handled elsewhere
|
||||
return false;
|
||||
} else if ( !idStr::Icmp( statname, "extraammo" ) ) { // SnoopJeDi
|
||||
owner->extraammo = true;
|
||||
} else {
|
||||
// unknown item
|
||||
gameLocal.Warning( "Unknown stat '%s' added to player's inventory", statname );
|
||||
|
@ -967,6 +1001,10 @@ idPlayer::idPlayer() {
|
|||
buttonMask = 0;
|
||||
oldFlags = 0;
|
||||
|
||||
statsUI = NULL; // SnoopJeDi
|
||||
statsUIopen = false; // SnoopJeDi
|
||||
statsDebug = false; // SnoopJeDi
|
||||
|
||||
lastHitTime = 0;
|
||||
lastSndHitTime = 0;
|
||||
lastSavingThrowTime = 0;
|
||||
|
@ -990,6 +1028,8 @@ idPlayer::idPlayer() {
|
|||
healthPulse = false;
|
||||
nextHealthTake = 0;
|
||||
healthTake = false;
|
||||
extraammo = false; // SnoopJeDi
|
||||
|
||||
|
||||
scoreBoardOpen = false;
|
||||
forceScoreBoard = false;
|
||||
|
@ -1409,6 +1449,8 @@ void idPlayer::Init( void ) {
|
|||
}
|
||||
|
||||
cvarSystem->SetCVarBool( "ui_chat", false );
|
||||
ClearStats(); // SnoopJeDi
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1426,6 +1468,8 @@ void idPlayer::Spawn( void ) {
|
|||
gameLocal.Error( "entityNum > MAX_CLIENTS for player. Player may only be spawned with a client." );
|
||||
}
|
||||
|
||||
s_music_vol.SetModified(); // SnoopJeDi - To make sure we get music faded properly
|
||||
|
||||
// allow thinking during cinematics
|
||||
cinematic = true;
|
||||
|
||||
|
@ -1469,6 +1513,10 @@ void idPlayer::Spawn( void ) {
|
|||
|
||||
objectiveSystem = uiManager->FindGui( "guis/pda.gui", true, false, true );
|
||||
objectiveSystemOpen = false;
|
||||
statsUI = uiManager->FindGui( "guis/stats.gui", true, false, true ); //SnoopJeDi - Set 'er up!
|
||||
statsUIopen = false;
|
||||
statsDebug = false;
|
||||
|
||||
}
|
||||
|
||||
SetLastHitTime( 0 );
|
||||
|
@ -1633,6 +1681,9 @@ void idPlayer::Save( idSaveGame *savefile ) const {
|
|||
savefile->WriteInt( lastSndHitTime );
|
||||
savefile->WriteInt( lastSavingThrowTime );
|
||||
|
||||
savefile->WriteBool( extraammo ); // SnoopJeDi
|
||||
|
||||
|
||||
// idBoolFields don't need to be saved, just re-linked in Restore
|
||||
|
||||
inventory.Save( savefile );
|
||||
|
@ -1641,6 +1692,10 @@ void idPlayer::Save( idSaveGame *savefile ) const {
|
|||
savefile->WriteUserInterface( hud, false );
|
||||
savefile->WriteUserInterface( objectiveSystem, false );
|
||||
savefile->WriteBool( objectiveSystemOpen );
|
||||
savefile->WriteUserInterface( statsUI, false ); //SnoopJeDi
|
||||
savefile->WriteBool( statsUIopen ); //SnoopJeDi
|
||||
savefile->WriteBool( statsDebug ); //SnoopJeDi
|
||||
|
||||
|
||||
savefile->WriteInt( weapon_soulcube );
|
||||
savefile->WriteInt( weapon_pda );
|
||||
|
@ -1851,6 +1906,8 @@ void idPlayer::Restore( idRestoreGame *savefile ) {
|
|||
savefile->ReadInt( lastHitTime );
|
||||
savefile->ReadInt( lastSndHitTime );
|
||||
savefile->ReadInt( lastSavingThrowTime );
|
||||
savefile->ReadBool( extraammo ); //SnoopJeDi
|
||||
|
||||
|
||||
// Re-link idBoolFields to the scriptObject, values will be restored in scriptObject's restore
|
||||
LinkScriptVariables();
|
||||
|
@ -1865,6 +1922,10 @@ void idPlayer::Restore( idRestoreGame *savefile ) {
|
|||
savefile->ReadUserInterface( hud );
|
||||
savefile->ReadUserInterface( objectiveSystem );
|
||||
savefile->ReadBool( objectiveSystemOpen );
|
||||
savefile->ReadUserInterface( statsUI ); //SnoopJeDi
|
||||
savefile->ReadBool( statsUIopen ); //SnoopJeDi
|
||||
savefile->ReadBool( statsDebug ); //SnoopJeDi
|
||||
|
||||
|
||||
savefile->ReadInt( weapon_soulcube );
|
||||
savefile->ReadInt( weapon_pda );
|
||||
|
@ -2293,6 +2354,8 @@ void idPlayer::SavePersistantInfo( void ) {
|
|||
inventory.GetPersistantData( playerInfo );
|
||||
playerInfo.SetInt( "health", health );
|
||||
playerInfo.SetInt( "current_weapon", currentWeapon );
|
||||
playerInfo.SetBool( "extraammo", extraammo ); // SnoopJeDi
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2311,6 +2374,7 @@ void idPlayer::RestorePersistantInfo( void ) {
|
|||
|
||||
inventory.RestoreInventory( this, spawnArgs );
|
||||
health = spawnArgs.GetInt( "health", "100" );
|
||||
extraammo = spawnArgs.GetBool( "extraammo", 0 );
|
||||
if ( !gameLocal.isClient ) {
|
||||
idealWeapon = spawnArgs.GetInt( "current_weapon", "1" );
|
||||
}
|
||||
|
@ -2493,6 +2557,17 @@ void idPlayer::UpdateHudAmmo( idUserInterface *_hud ) {
|
|||
_hud->SetStateString( "player_clips", weapon.GetEntity()->ClipSize() ? va( "%i", ammoamount / weapon.GetEntity()->ClipSize() ) : "--" );
|
||||
_hud->SetStateString( "player_allammo", va( "%i/%i", inclip, ammoamount - inclip ) );
|
||||
}
|
||||
const idDict *ammoDef = gameLocal.FindEntityDefDict("ammo_types", false);
|
||||
|
||||
if (ammoDef) {
|
||||
for (int i = 0; i < AMMO_NUMTYPES; i++) {
|
||||
const idKeyValue *ammoType = ammoDef->GetKeyVal(i);
|
||||
|
||||
if (ammoType) {
|
||||
_hud->SetStateInt( va("player_%s", ammoType->GetKey().c_str()), inventory.ammo[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_hud->SetStateBool( "player_ammo_empty", ( ammoamount == 0 ) );
|
||||
_hud->SetStateBool( "player_clip_empty", ( weapon.GetEntity()->ClipSize() ? inclip == 0 : false ) );
|
||||
|
@ -2526,6 +2601,32 @@ void idPlayer::UpdateHudStats( idUserInterface *_hud ) {
|
|||
_hud->SetStateInt( "player_hr", heartRate );
|
||||
_hud->SetStateInt( "player_nostamina", ( max_stamina == 0 ) ? 1 : 0 );
|
||||
|
||||
if ( statsDebug ) { // SnoopJeDi
|
||||
int secrets = inventory.Secrets;
|
||||
int totalsecrets = gameLocal.secretAreas.Num();
|
||||
int kills = inventory.kills;
|
||||
int monsters = gameLocal.monsters;
|
||||
float time = inventory.time;
|
||||
int min = idMath::Ftoi( ( float )time/60000 );
|
||||
int sec = (time - 60000 * min)/1000;
|
||||
int pickedup = inventory.itemspickedup;
|
||||
int totalitems = gameLocal.items;
|
||||
idStr timestr;
|
||||
if ( sec > 9 ) {
|
||||
timestr = va( "Time: %i:%i", min, sec );
|
||||
} else {
|
||||
timestr = va( "Time: %i:0%i", min, sec );
|
||||
}
|
||||
_hud->SetStateBool( "statsDebug", true );
|
||||
_hud->SetStateString( "secrets", va( "Secrets: %i/%i (%i%%)", secrets, totalsecrets, totalsecrets != 0 ? idMath::FtoiFast( 100.0f * ( float )secrets/( float )totalsecrets ) : 0 ) );
|
||||
_hud->SetStateString( "kills", va( "Kills: %i/%i (%i%%)", kills, monsters, monsters != 0 ? idMath::Ftoi( 100.0f * ( float )kills/( float )monsters ) : 0 ) );
|
||||
_hud->SetStateString( "time", timestr );
|
||||
_hud->SetStateString( "items", va( "Items: %i/%i (%i%%)", pickedup, totalitems, totalitems != 0 ? idMath::Ftoi( 100.0f * ( float )pickedup/( float )totalitems ) : 0 ) );
|
||||
} else {
|
||||
_hud->SetStateBool( "statsDebug", false );
|
||||
}
|
||||
|
||||
|
||||
_hud->HandleNamedEvent( "updateArmorHealthAir" );
|
||||
|
||||
if ( healthPulse ) {
|
||||
|
@ -2627,7 +2728,7 @@ void idPlayer::DrawHUD( idUserInterface *_hud ) {
|
|||
|
||||
// weapon targeting crosshair
|
||||
if ( !GuiActive() ) {
|
||||
if ( cursor && weapon.GetEntity()->ShowCrosshair() ) {
|
||||
if ( cursor && weapon.GetEntity()->ShowCrosshair() && !statsUIopen ) { // SnoopJeDi
|
||||
cursor->Redraw( gameLocal.realClientTime );
|
||||
}
|
||||
}
|
||||
|
@ -3026,7 +3127,9 @@ bool idPlayer::GivePowerUp( int powerup, int time ) {
|
|||
|
||||
if ( powerup >= 0 && powerup < MAX_POWERUPS ) {
|
||||
|
||||
if ( gameLocal.isServer ) {
|
||||
if ( gameLocal.isServer && powerup != MEGAHEALTH && powerup != HEALTHVIAL && powerup != ARMORBONUS && powerup != MEGAARMOR ) { // SnoopJeDi - LMS fix
|
||||
// CDoom powerups now work for clients
|
||||
|
||||
idBitMsg msg;
|
||||
byte msgBuf[MAX_EVENT_PARAM_SIZE];
|
||||
|
||||
|
@ -3036,7 +3139,8 @@ bool idPlayer::GivePowerUp( int powerup, int time ) {
|
|||
ServerSendEvent( EVENT_POWERUP, &msg, false, -1 );
|
||||
}
|
||||
|
||||
if ( powerup != MEGAHEALTH ) {
|
||||
if ( powerup != MEGAHEALTH && powerup != HEALTHVIAL && powerup != ARMORBONUS && powerup != MEGAARMOR ) { //SnoopJeDi - LMS fix
|
||||
|
||||
inventory.GivePowerUp( this, powerup, time );
|
||||
}
|
||||
|
||||
|
@ -3075,12 +3179,59 @@ bool idPlayer::GivePowerUp( int powerup, int time ) {
|
|||
break;
|
||||
}
|
||||
case MEGAHEALTH: {
|
||||
inventory.AddPickupName("Mega Health", "");
|
||||
if ( spawnArgs.GetString( "snd_megahealth", "", &sound ) ) {
|
||||
StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_ANY, 0, false, NULL );
|
||||
}
|
||||
def = gameLocal.FindEntityDef( "powerup_megahealth", false );
|
||||
if ( def ) {
|
||||
health = def->dict.GetInt( "inv_health" );
|
||||
health += def->dict.GetInt( "inv_health" );
|
||||
}
|
||||
if ( health > 200 ) {
|
||||
health = 200;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HEALTHVIAL: { //SnoopJeDi
|
||||
inventory.AddPickupName("Health Vial", "");
|
||||
|
||||
def = gameLocal.FindEntityDef( "item_health_vial", false );
|
||||
if ( def && def->dict.GetString( "snd_acquire", "", &sound ) ) {
|
||||
StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_ANY, 0, false, NULL );
|
||||
}
|
||||
if ( def ) {
|
||||
// gameLocal.Printf ( "Gave player 1 health: GivePowerUp()\n" );
|
||||
health += def->dict.GetInt( "inv_health" );
|
||||
//health += 1;
|
||||
//} else {
|
||||
// gameLocal.Printf ( "Didn't give player health: GivePowerUp()\n" );
|
||||
if ( health > spawnArgs.GetInt( "maxbonushealth" ) ) {
|
||||
health = spawnArgs.GetInt( "maxbonushealth" );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ARMORBONUS: { //SnoopJeDi
|
||||
inventory.AddPickupName("Armor Bonus", "");
|
||||
def = gameLocal.FindEntityDef( "item_armor_shard", false );
|
||||
if ( def && inventory.armor < spawnArgs.GetInt ( "maxbonusarmor" ) ) {
|
||||
inventory.armor += def->dict.GetInt( "inv_armor" );
|
||||
if ( inventory.armor > spawnArgs.GetInt ( "maxbonusarmor" ) ) {
|
||||
inventory.armor = spawnArgs.GetInt ( "maxbonusarmor" );
|
||||
}
|
||||
inventory.nextArmorDepleteTime = 0;
|
||||
inventory.armorPulse = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MEGAARMOR: { //SnoopJeDi
|
||||
inventory.AddPickupName("Mega Armor", "");
|
||||
def = gameLocal.FindEntityDef( "item_mega_armor", false );
|
||||
if ( def && inventory.armor < spawnArgs.GetInt ( "maxbonusarmor" ) ) {
|
||||
inventory.armor += def->dict.GetInt( "inv_armor" );
|
||||
if ( inventory.armor > spawnArgs.GetInt ( "maxbonusarmor" ) ) {
|
||||
inventory.armor = spawnArgs.GetInt ( "maxbonusarmor" );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -3525,7 +3676,7 @@ void idPlayer::NextWeapon( void ) {
|
|||
const char *weap;
|
||||
int w;
|
||||
|
||||
if ( !weaponEnabled || spectating || hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || health < 0 ) {
|
||||
if ( !weaponEnabled || spectating || hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || health < 0 || statsUIopen ) { //SnoopJeDi
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3575,7 +3726,7 @@ void idPlayer::PrevWeapon( void ) {
|
|||
const char *weap;
|
||||
int w;
|
||||
|
||||
if ( !weaponEnabled || spectating || hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || health < 0 ) {
|
||||
if ( !weaponEnabled || spectating || hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || health < 0 || statsUIopen ) { //SnoopJeDi
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3624,7 +3775,7 @@ idPlayer::SelectWeapon
|
|||
void idPlayer::SelectWeapon( int num, bool force ) {
|
||||
const char *weap;
|
||||
|
||||
if ( !weaponEnabled || spectating || gameLocal.inCinematic || health < 0 ) {
|
||||
if ( !weaponEnabled || spectating || gameLocal.inCinematic || health < 0 || statsUIopen ) { // SnoopJeDi
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3800,6 +3951,9 @@ idUserInterface *idPlayer::ActiveGui( void ) {
|
|||
if ( objectiveSystemOpen ) {
|
||||
return objectiveSystem;
|
||||
}
|
||||
if ( statsUIopen ) { //SnoopJeDi
|
||||
return statsUI;
|
||||
}
|
||||
|
||||
return focusUI;
|
||||
}
|
||||
|
@ -3970,7 +4124,7 @@ idPlayer::Weapon_GUI
|
|||
*/
|
||||
void idPlayer::Weapon_GUI( void ) {
|
||||
|
||||
if ( !objectiveSystemOpen ) {
|
||||
if ( !objectiveSystemOpen && !statsUIopen ) { // SnoopJeDi
|
||||
if ( idealWeapon != currentWeapon ) {
|
||||
Weapon_Combat();
|
||||
}
|
||||
|
@ -4169,6 +4323,11 @@ bool idPlayer::HandleSingleGuiCommand( idEntity *entityGui, idLexer *src ) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if ( token.Icmp( "statsOver" ) == 0 ) { // SnoopJeDi
|
||||
StatsProceed();
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( token.Icmp( "addhealth" ) == 0 ) {
|
||||
if ( entityGui && health < 100 ) {
|
||||
int _health = entityGui->spawnArgs.GetInt( "gui_parm1" );
|
||||
|
@ -4861,7 +5020,7 @@ void idPlayer::UpdateViewAngles( void ) {
|
|||
int i;
|
||||
idAngles delta;
|
||||
|
||||
if ( !noclip && ( gameLocal.inCinematic || privateCameraView || gameLocal.GetCamera() || influenceActive == INFLUENCE_LEVEL2 || objectiveSystemOpen ) ) {
|
||||
if ( !noclip && ( gameLocal.inCinematic || privateCameraView || gameLocal.GetCamera() || influenceActive == INFLUENCE_LEVEL2 || objectiveSystemOpen || statsUIopen ) ) { // SnoopJeDi
|
||||
// no view changes at all, but we still want to update the deltas or else when
|
||||
// we get out of this mode, our view will snap to a kind of random angle
|
||||
UpdateDeltaViewAngles( viewAngles );
|
||||
|
@ -5356,7 +5515,7 @@ void idPlayer::TogglePDA( void ) {
|
|||
}
|
||||
|
||||
if ( inventory.pdas.Num() == 0 ) {
|
||||
ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_noPDA" ), true );
|
||||
//ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_noPDA" ), true ); // SnoopJeDi - Removed "no PDA" text
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -5580,7 +5739,7 @@ void idPlayer::PerformImpulse( int impulse ) {
|
|||
case IMPULSE_19: {
|
||||
// when we're not in single player, IMPULSE_19 is used for showScores
|
||||
// otherwise it opens the pda
|
||||
if ( !gameLocal.isMultiplayer ) {
|
||||
if ( !gameLocal.isMultiplayer && !statsUIopen ) { // SnoopJeDi
|
||||
if ( objectiveSystemOpen ) {
|
||||
TogglePDA();
|
||||
} else if ( weapon_pda >= 0 ) {
|
||||
|
@ -5613,6 +5772,10 @@ void idPlayer::PerformImpulse( int impulse ) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case IMPULSE_30: { // SnoopJeDi - At the request of many.
|
||||
ToggleStatsDebug();
|
||||
break;
|
||||
}
|
||||
case IMPULSE_40: {
|
||||
UseVehicle();
|
||||
break;
|
||||
|
@ -5682,6 +5845,9 @@ void idPlayer::AdjustSpeed( void ) {
|
|||
float speed;
|
||||
float rate;
|
||||
|
||||
if ( GetUserInfo()->GetBool( "ui_autoRun" ) ) {
|
||||
usercmd.buttons ^= BUTTON_RUN; //SnoopJeDi: cusTom3 is briliant!
|
||||
}
|
||||
if ( spectating ) {
|
||||
speed = pm_spectatespeed.GetFloat();
|
||||
bobFrac = 0.0f;
|
||||
|
@ -6209,7 +6375,7 @@ void idPlayer::Think( void ) {
|
|||
oldFlags = usercmd.flags;
|
||||
}
|
||||
|
||||
if ( objectiveSystemOpen || gameLocal.inCinematic || influenceActive ) {
|
||||
if ( objectiveSystemOpen || gameLocal.inCinematic || influenceActive || statsUIopen ) { // SnoopJeDi
|
||||
if ( objectiveSystemOpen && AI_PAIN ) {
|
||||
TogglePDA();
|
||||
}
|
||||
|
@ -6241,7 +6407,7 @@ void idPlayer::Think( void ) {
|
|||
}
|
||||
|
||||
// zooming
|
||||
if ( ( usercmd.buttons ^ oldCmd.buttons ) & BUTTON_ZOOM ) {
|
||||
if ( ( ( usercmd.buttons ^ oldCmd.buttons ) & BUTTON_ZOOM ) && !statsUIopen ) { //SnoopJeDi
|
||||
if ( ( usercmd.buttons & BUTTON_ZOOM ) && weapon.GetEntity() ) {
|
||||
zoomFov.Init( gameLocal.time, 200.0f, CalcFov( false ), weapon.GetEntity()->GetZoomFov() );
|
||||
} else {
|
||||
|
@ -7375,6 +7541,8 @@ void idPlayer::AddAIKill( void ) {
|
|||
int max_souls;
|
||||
int ammo_souls;
|
||||
|
||||
inventory.kills++; // SnoopJeDi
|
||||
|
||||
if ( ( weapon_soulcube < 0 ) || ( inventory.weapons & ( 1 << weapon_soulcube ) ) == 0 ) {
|
||||
return;
|
||||
}
|
||||
|
@ -7707,7 +7875,7 @@ idPlayer::Event_OpenPDA
|
|||
==================
|
||||
*/
|
||||
void idPlayer::Event_OpenPDA( void ) {
|
||||
if ( !gameLocal.isMultiplayer ) {
|
||||
if ( !gameLocal.isMultiplayer && !statsUIopen ) { // SnoopJeDi
|
||||
TogglePDA();
|
||||
}
|
||||
}
|
||||
|
@ -7800,7 +7968,7 @@ void idPlayer::ClientPredictionThink( void ) {
|
|||
buttonMask &= usercmd.buttons;
|
||||
usercmd.buttons &= ~buttonMask;
|
||||
|
||||
if ( objectiveSystemOpen ) {
|
||||
if ( objectiveSystemOpen || statsUIopen ) { // SnoopJeDi
|
||||
usercmd.forwardmove = 0;
|
||||
usercmd.rightmove = 0;
|
||||
usercmd.upmove = 0;
|
||||
|
@ -8120,7 +8288,7 @@ void idPlayer::ReadFromSnapshot( const idBitMsgDelta &msg ) {
|
|||
common->Warning( "NET: no damage def for damage feedback '%d'\n", lastDamageDef );
|
||||
}
|
||||
}
|
||||
} else if ( health > oldHealth && PowerUpActive( MEGAHEALTH ) && !stateHitch ) {
|
||||
} else if ( health > oldHealth && PowerUpActive( MEGAHEALTH ) && PowerUpActive( HEALTHVIAL ) && !stateHitch ) {
|
||||
// just pulse, for any health raise
|
||||
healthPulse = true;
|
||||
}
|
||||
|
@ -8520,3 +8688,103 @@ bool idPlayer::NeedsIcon( void ) {
|
|||
// local clients don't render their own icons... they're only info for other clients
|
||||
return entityNumber != gameLocal.localClientNum && ( isLagged || isChatting );
|
||||
}
|
||||
|
||||
//SnoopJeDi -- BEGIN
|
||||
|
||||
/*
|
||||
================
|
||||
idPlayer::SecretArea
|
||||
================
|
||||
*/
|
||||
void idPlayer::SecretArea( int areanum ) {
|
||||
inventory.Secrets++;
|
||||
//common->Printf( "^1Secrets Incremented\n" );
|
||||
gameLocal.DeactivateSecretAreas( areanum );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idPlayer::OpenStats
|
||||
================
|
||||
*/
|
||||
|
||||
void idPlayer::OpenStats( idStr nextMap, int level_no ) {
|
||||
StopFiring();
|
||||
int secrets = inventory.Secrets;
|
||||
int totalsecrets = gameLocal.secretAreas.Num();
|
||||
int kills = inventory.kills;
|
||||
int monsters = gameLocal.monsters;
|
||||
float time = inventory.time;
|
||||
int min = idMath::Ftoi( ( float )time/60000 );
|
||||
int sec = (time - 60000 * min)/1000;
|
||||
int pickedup = inventory.itemspickedup;
|
||||
int totalitems = gameLocal.items;
|
||||
float psecrets = ( float )secrets/( float )totalsecrets;
|
||||
float pkills = ( float )kills/( float )monsters;
|
||||
float pitems = ( float )pickedup/( float )totalitems;
|
||||
idStr timestr;
|
||||
if ( sec > 9 ) {
|
||||
timestr = va( "Time: %i:%i", min, sec );
|
||||
} else {
|
||||
timestr = va( "Time: %i:0%i", min, sec );
|
||||
}
|
||||
statsUI->Activate( true, gameLocal.time );
|
||||
statsUIopen = true;
|
||||
statsUI->SetStateString( "secrets_found", va( "Secrets: %i/%i (%i%%)", secrets, totalsecrets, totalsecrets != 0 ? idMath::FtoiFast( 100.0f * psecrets ) : 0 ) );
|
||||
statsUI->SetStateString( "kills", va( "Kills: %i/%i (%i%%)", kills, monsters, monsters != 0 ? idMath::Ftoi( 100.0f * pkills ) : 0 ) );
|
||||
statsUI->SetStateString( "time", timestr );
|
||||
statsUI->SetStateString( "items", va( "Items: %i/%i (%i%%)", pickedup, totalitems, totalitems != 0 ? idMath::Ftoi( 100.0f * pitems ) : 0 ) );
|
||||
statsUI->SetStateFloat( "psecrets", psecrets );
|
||||
statsUI->SetStateFloat( "pkills", pkills );
|
||||
statsUI->SetStateFloat( "pitems", pitems );
|
||||
statsUI->SetStateInt( "health", health );
|
||||
statsUI->SetStateInt( "level_no", level_no );
|
||||
//statsUI->SetStateString( "entering", va( "Now Entering: %s", nextMapName ) );
|
||||
statsUI->SetStateString( "nextMap", va( nextMap ) );
|
||||
gameSoundWorld->PlayShaderDirectly( "music_stats", SND_CHANNEL_ANY );
|
||||
//common->Printf( "^1Nextmap is: %s", va( nextMap ) );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idPlayer::StatsProceed
|
||||
================
|
||||
*/
|
||||
|
||||
void idPlayer::StatsProceed() { //Ripped un-ceremoniously from idTarget_EndLevel
|
||||
idStr nextMap;
|
||||
|
||||
nextMap = statsUI->GetStateString( "nextMap", "" );
|
||||
if ( !nextMap ) {
|
||||
gameLocal.Printf( "idPlayer::StatsProceed: no nextMap key\n" );
|
||||
return;
|
||||
}
|
||||
gameLocal.sessionCommand = "map ";
|
||||
gameLocal.sessionCommand += nextMap;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idPlayer::ClearStats
|
||||
==================
|
||||
*/
|
||||
|
||||
void idPlayer::ClearStats() {
|
||||
inventory.kills = 0;
|
||||
inventory.Secrets = 0;
|
||||
inventory.itemspickedup = 0;
|
||||
//common->Printf( "^1ClearStats reset integers\n" );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
idPlayer::ToggleStatsDebug
|
||||
==================
|
||||
*/
|
||||
|
||||
void idPlayer::ToggleStatsDebug( void ) { // SnoopJeDi
|
||||
statsDebug = !statsDebug;
|
||||
}
|
||||
|
||||
//SnoopJeDi -- END
|
||||
|
|
|
@ -103,6 +103,9 @@ enum {
|
|||
INVISIBILITY,
|
||||
MEGAHEALTH,
|
||||
ADRENALINE,
|
||||
HEALTHVIAL, // SnoopJeDi
|
||||
ARMORBONUS, // SnoopJeDi
|
||||
MEGAARMOR, // SnoopJeDi
|
||||
MAX_POWERUPS
|
||||
};
|
||||
|
||||
|
@ -132,6 +135,10 @@ public:
|
|||
int ammo[ AMMO_NUMTYPES ];
|
||||
int clip[ MAX_WEAPONS ];
|
||||
int powerupEndTime[ MAX_POWERUPS ];
|
||||
int Secrets; //SnoopJeDi
|
||||
int kills; //SnoopJeDi
|
||||
int itemspickedup; // SnoopJeDi
|
||||
int time; // SnoopJeDi - For saved games
|
||||
|
||||
// mp
|
||||
int ammoPredictTime;
|
||||
|
@ -265,6 +272,15 @@ public:
|
|||
idUserInterface * objectiveSystem;
|
||||
bool objectiveSystemOpen;
|
||||
|
||||
idUserInterface * statsUI; // SnoopJeDi
|
||||
bool statsUIopen; // SnoopJeDi
|
||||
bool statsDebug; // SnoopJeDi
|
||||
void OpenStats( idStr nextMap, int level_no ); // SnoopJeDi
|
||||
void StatsProceed(); // SnoopJeDi
|
||||
void ClearStats(); // SnoopJeDi
|
||||
void ToggleStatsDebug(); // SnoopJeDi
|
||||
|
||||
|
||||
int weapon_soulcube;
|
||||
int weapon_pda;
|
||||
int weapon_fists;
|
||||
|
@ -283,6 +299,8 @@ public:
|
|||
bool healthPulse;
|
||||
bool healthTake;
|
||||
int nextHealthTake;
|
||||
bool extraammo; // SnoopJeDi
|
||||
|
||||
|
||||
|
||||
bool hiddenWeapon; // if the weapon is hidden ( in noWeapons maps )
|
||||
|
@ -521,6 +539,9 @@ public:
|
|||
virtual void HidePlayerIcons( void );
|
||||
bool NeedsIcon( void );
|
||||
|
||||
void SecretArea( int areanum ); //SnoopJeDi
|
||||
|
||||
|
||||
bool SelfSmooth( void );
|
||||
void SetSelfSmooth( bool b );
|
||||
|
||||
|
|
|
@ -536,6 +536,10 @@ void idPlayerView::SingleView( idUserInterface *hud, const renderView_t *view )
|
|||
renderSystem->DrawStretchPic( 0.0f, 0.0f, 640.0f, 480.0f, 0.0f, 0.0f, 1.0f, 1.0f, mtr );
|
||||
}
|
||||
}
|
||||
if ( player->statsUIopen ) { //SnoopJeDi - Draw over world.
|
||||
player->statsUI->Redraw( gameLocal.time );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -128,6 +128,10 @@ void idSound::Spawn( void ) {
|
|||
} else {
|
||||
timerOn = false;
|
||||
}
|
||||
if ( spawnArgs.GetBool( "s_music" ) ) { //SnoopJeDi
|
||||
gameLocal.musicSpeakers.Append( entityNumber );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -216,6 +216,75 @@ void idTarget_EndLevel::Event_Activate( idEntity *activator ) {
|
|||
gameLocal.sessionCommand += nextMap;
|
||||
}
|
||||
|
||||
//SnoopJeDi -- BEGIN
|
||||
//Zombie class!
|
||||
|
||||
CLASS_DECLARATION( idEntity, idTarget_EndLevelCdoom )
|
||||
EVENT( EV_Activate, idTarget_EndLevelCdoom::Event_Trigger )
|
||||
END_CLASS
|
||||
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
idTarget_EndLevelCdoom::Spawn
|
||||
================
|
||||
*/
|
||||
void idTarget_EndLevelCdoom::Spawn( void ) {
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idTarget_EndLevelCdoom::~idTarget_EndLevelCdoom()
|
||||
================
|
||||
*/
|
||||
idTarget_EndLevelCdoom::~idTarget_EndLevelCdoom() {
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idTarget_EndLevelCdoom::Event_Trigger
|
||||
================
|
||||
*/
|
||||
void idTarget_EndLevelCdoom::Event_Trigger( idEntity *activator ) {
|
||||
|
||||
if ( !activator->IsType( idPlayer::Type ) )
|
||||
return;
|
||||
gameSoundWorld->StopAllSounds();
|
||||
renderView_t renderView;
|
||||
|
||||
memset( &renderView, 0, sizeof( renderView ) );
|
||||
|
||||
renderView.width = SCREEN_WIDTH;
|
||||
renderView.height = SCREEN_HEIGHT;
|
||||
renderView.x = 0;
|
||||
renderView.y = 0;
|
||||
|
||||
renderView.fov_x = 90;
|
||||
renderView.fov_y = 90; // FIXME!
|
||||
renderView.time = gameLocal.time;
|
||||
|
||||
#if 0
|
||||
renderView.vieworg = initialViewOrg;
|
||||
renderView.viewaxis = idAngles(initialViewAngles).toMat3();
|
||||
#else
|
||||
renderView.vieworg = renderEntity.origin;
|
||||
renderView.viewaxis = renderEntity.axis;
|
||||
#endif
|
||||
|
||||
gameRenderWorld->RenderScene( &renderView );
|
||||
|
||||
// draw the gui on top of the 3D view
|
||||
|
||||
idPlayer * player = gameLocal.GetLocalPlayer();
|
||||
player->OpenStats( idStr(spawnArgs.GetString("nextMap", "")), spawnArgs.GetInt( "level_no", "-1" ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// SnoopJeDi -- END
|
||||
|
||||
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
|
|
@ -566,5 +566,34 @@ private:
|
|||
void Event_RestoreVolume();
|
||||
};
|
||||
|
||||
// SnoopJeDi -- BEGIN
|
||||
/*
|
||||
================================
|
||||
|
||||
idTarget_EndLevelCdoom
|
||||
|
||||
This is the illegitimate child of idTarget_EndLevel and my desire for a stats screen.
|
||||
It's a zombie entity, which only really serves to stop game sounds and bring up the stats screen.
|
||||
Everything else is handled in Player.cpp
|
||||
|
||||
================================
|
||||
*/
|
||||
|
||||
|
||||
class idTarget_EndLevelCdoom : public idEntity {
|
||||
public:
|
||||
CLASS_PROTOTYPE( idTarget_EndLevelCdoom );
|
||||
|
||||
void Spawn( void );
|
||||
~idTarget_EndLevelCdoom();
|
||||
idUserInterface * gui;
|
||||
|
||||
private:
|
||||
void Event_Trigger( idEntity *activator );
|
||||
};
|
||||
|
||||
// SnoopJeDi -- END
|
||||
|
||||
|
||||
|
||||
#endif /* !__GAME_TARGET_H__ */
|
||||
|
|
|
@ -719,6 +719,8 @@ void idAI::Spawn( void ) {
|
|||
return;
|
||||
}
|
||||
|
||||
gameLocal.monsters++; // SnoopJeDi
|
||||
|
||||
spawnArgs.GetInt( "team", "1", team );
|
||||
spawnArgs.GetInt( "rank", "0", rank );
|
||||
spawnArgs.GetInt( "fly_offset", "0", fly_offset );
|
||||
|
@ -1053,6 +1055,9 @@ void idAI::Think( void ) {
|
|||
if ( CheckDormant() ) {
|
||||
return;
|
||||
}
|
||||
if ( gameLocal.GetLocalPlayer()->statsUIopen ) //SnoopJeDi - Don't Think() so!
|
||||
return;
|
||||
|
||||
|
||||
if ( thinkFlags & TH_THINK ) {
|
||||
// clear out the enemy when he dies or is hidden
|
||||
|
@ -3395,9 +3400,15 @@ void idAI::Killed( idEntity *inflictor, idEntity *attacker, int damage, const id
|
|||
kv = spawnArgs.MatchPrefix( "def_drops", kv );
|
||||
}
|
||||
|
||||
if ( ( attacker && attacker->IsType( idPlayer::Type ) ) && ( inflictor && !inflictor->IsType( idSoulCubeMissile::Type ) ) ) {
|
||||
/*if ( ( attacker && attacker->IsType( idPlayer::Type ) ) && ( inflictor && !inflictor->IsType( idSoulCubeMissile::Type ) ) ) {
|
||||
static_cast< idPlayer* >( attacker )->AddAIKill();
|
||||
}
|
||||
}*/
|
||||
// SnoopJeDi - If stats makes it to coop, this needs to be unborked, and
|
||||
// original attacker info needs to be passed along the barrel chain.
|
||||
|
||||
if ( !gameLocal.isMultiplayer )
|
||||
gameLocal.GetLocalPlayer()->AddAIKill();
|
||||
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
|
|
|
@ -317,7 +317,9 @@ void Cmd_Give_f( const idCmdArgs &args ) {
|
|||
player->GiveItem( name );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( give_all ) {
|
||||
player->Give( "extraammo", "1" ); // SnoopJeDi
|
||||
}
|
||||
if ( give_all || idStr::Icmp( name, "health" ) == 0 ) {
|
||||
player->health = player->inventory.maxHealth;
|
||||
if ( !give_all ) {
|
||||
|
|
|
@ -47,11 +47,14 @@ All game cvars should be defined here.
|
|||
|
||||
*/
|
||||
|
||||
const char *si_gameTypeArgs[] = { "singleplayer", "deathmatch", "Tourney", "Team DM", "Last Man", NULL };
|
||||
const char *si_gameTypeArgs[] = { "singleplayer", "cdoomDM", "Tourney", "Team DM", "Last Man", NULL }; // SnoopJeDi
|
||||
const char *si_readyArgs[] = { "Not Ready", "Ready", NULL };
|
||||
|
||||
const char *si_spectateArgs[] = { "Play", "Spectate", NULL };
|
||||
|
||||
const char *ui_skinArgs[] = { "skins/characters/player/marine_mp", "skins/characters/player/marine_mp_red", "skins/characters/player/marine_mp_blue", "skins/characters/player/marine_mp_green", "skins/characters/player/marine_mp_yellow", NULL };
|
||||
const char *ui_skinArgs[] = { "skins/characters/player/doomguy_green", "skins/characters/player/doomguy_red", "skins/characters/player/doomguy_blue", "skins/characters/player/doomguy_yellow", "skins/characters/player/doomguy_indigo", "skins/characters/player/doomguy_black", "skins/characters/player/doomguy_tan", "skins/characters/player/doomguy_white", NULL };
|
||||
//SnoopJeDi - Modified to allow cdoom skins 5/24/06
|
||||
|
||||
const char *ui_teamArgs[] = { "Red", "Blue", NULL };
|
||||
|
||||
struct gameVersion_s {
|
||||
|
@ -66,8 +69,8 @@ idCVar gamename( "gamename", GAME_VERSION, CVAR_GAME | CVAR_SERVERINFO |
|
|||
idCVar gamedate( "gamedate", __DATE__, CVAR_GAME | CVAR_ROM, "" );
|
||||
|
||||
// server info
|
||||
idCVar si_name( "si_name", "dhewm server", CVAR_GAME | CVAR_SERVERINFO | CVAR_ARCHIVE, "name of the server" );
|
||||
idCVar si_gameType( "si_gameType", si_gameTypeArgs[ 0 ], CVAR_GAME | CVAR_SERVERINFO | CVAR_ARCHIVE, "game type - singleplayer, deathmatch, Tourney, Team DM or Last Man", si_gameTypeArgs, idCmdSystem::ArgCompletion_String<si_gameTypeArgs> );
|
||||
idCVar si_name( "si_name", "DOOM Server", CVAR_GAME | CVAR_SERVERINFO | CVAR_ARCHIVE, "name of the server" );
|
||||
idCVar si_gameType( "si_gameType", si_gameTypeArgs[ 0 ], CVAR_GAME | CVAR_SERVERINFO | CVAR_ARCHIVE, "game type - singleplayer, cdoomDM, Tourney, Team DM or Last Man", si_gameTypeArgs, idCmdSystem::ArgCompletion_String<si_gameTypeArgs> ); // SnoopJeDi
|
||||
idCVar si_map( "si_map", "game/mp/d3dm1",CVAR_GAME | CVAR_SERVERINFO | CVAR_ARCHIVE, "map to be played next on server", idCmdSystem::ArgCompletion_MapName );
|
||||
idCVar si_maxPlayers( "si_maxPlayers", "4", CVAR_GAME | CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_INTEGER, "max number of players allowed on the server", 1, 4 );
|
||||
idCVar si_fragLimit( "si_fragLimit", "10", CVAR_GAME | CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_INTEGER, "frag limit", 1, MP_PLAYER_MAXFRAGS );
|
||||
|
@ -89,8 +92,14 @@ idCVar ui_showGun( "ui_showGun", "1", CVAR_GAME | CVAR_USERINFO | CVAR_
|
|||
idCVar ui_ready( "ui_ready", si_readyArgs[ 0 ], CVAR_GAME | CVAR_USERINFO, "player is ready to start playing", idCmdSystem::ArgCompletion_String<si_readyArgs> );
|
||||
idCVar ui_spectate( "ui_spectate", si_spectateArgs[ 0 ], CVAR_GAME | CVAR_USERINFO, "play or spectate", idCmdSystem::ArgCompletion_String<si_spectateArgs> );
|
||||
idCVar ui_chat( "ui_chat", "0", CVAR_GAME | CVAR_USERINFO | CVAR_BOOL | CVAR_ROM | CVAR_CHEAT, "player is chatting" );
|
||||
// SnoopJeDi: autoRun
|
||||
idCVar ui_autoRun( "ui_autoRun", "0", CVAR_GAME | CVAR_USERINFO | CVAR_ARCHIVE | CVAR_BOOL, "whether or not the player should run while not holding the run button" );
|
||||
|
||||
|
||||
// change anytime vars
|
||||
// SnoopJeDi: music volume
|
||||
idCVar s_music_vol( "s_music_vol", "0", CVAR_GAME | CVAR_FLOAT | CVAR_ARCHIVE , "the volume in dB of all speakers with the s_music key set" );
|
||||
|
||||
idCVar developer( "developer", "0", CVAR_GAME | CVAR_BOOL, "" );
|
||||
|
||||
idCVar r_aspectRatio( "r_aspectRatio", "-1", CVAR_RENDERER | CVAR_INTEGER | CVAR_ARCHIVE, "aspect ratio of view:\n0 = 4:3\n1 = 16:9\n2 = 16:10\n-1 = auto (guess from resolution)", -1, 2 );
|
||||
|
@ -110,6 +119,9 @@ idCVar g_nightmare( "g_nightmare", "0", CVAR_GAME | CVAR_ARCHIVE | CVAR
|
|||
idCVar g_gravity( "g_gravity", DEFAULT_GRAVITY_STRING, CVAR_GAME | CVAR_FLOAT, "" );
|
||||
idCVar g_skipFX( "g_skipFX", "0", CVAR_GAME | CVAR_BOOL, "" );
|
||||
idCVar g_skipParticles( "g_skipParticles", "0", CVAR_GAME | CVAR_BOOL, "" );
|
||||
// SnoopJeDi - just so grepping can pick up my name
|
||||
idCVar g_lms_bind_run_once( "g_lms_bind_run_once", "0", CVAR_GAME | CVAR_BOOL | CVAR_ARCHIVE, "Rebind all controls once for LMS." );//deadite4, thanks to Hobbes
|
||||
|
||||
|
||||
idCVar g_disasm( "g_disasm", "0", CVAR_GAME | CVAR_BOOL, "disassemble script into base/script/disasm.txt on the local drive when script is compiled" );
|
||||
idCVar g_debugBounds( "g_debugBounds", "0", CVAR_GAME | CVAR_BOOL, "checks for models with bounds > 2048" );
|
||||
|
|
|
@ -118,6 +118,9 @@ extern idCVar g_vehicleSuspensionDown;
|
|||
extern idCVar g_vehicleSuspensionKCompress;
|
||||
extern idCVar g_vehicleSuspensionDamping;
|
||||
extern idCVar g_vehicleTireFriction;
|
||||
extern idCVar g_lms_bind_run_once; // SnoopJeDi
|
||||
extern idCVar s_music_vol; // SnoopJeDi
|
||||
|
||||
|
||||
extern idCVar ik_enable;
|
||||
extern idCVar ik_debug;
|
||||
|
|
Loading…
Reference in a new issue