From 3f720956db631a368c1e3f6527daefd55e8d82c6 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Fri, 27 Sep 2024 03:15:03 +0200 Subject: [PATCH] Merge code of Desolated - The Crying Fate mod --- framework/UsercmdGen.h | 7 + game/AF.cpp | 10 + game/Entity.cpp | 66 +++ game/Entity.h | 6 + game/Game_local.h | 15 + game/Liquid.cpp | 226 ++++++++ game/Liquid.h | 43 ++ game/Misc.cpp | 83 +-- game/Misc.h | 27 - game/Player.cpp | 669 +++++++++++++++++++++- game/Player.h | 67 +++ game/PlayerView.cpp | 176 +++++- game/PlayerView.h | 19 + game/Trigger.cpp | 89 +++ game/Trigger.h | 7 + game/ai/AI.cpp | 22 + game/gamesys/SysCmds.cpp | 62 +- game/gamesys/SysCvar.cpp | 34 ++ game/gamesys/SysCvar.h | 34 ++ game/physics/Physics.h | 7 + game/physics/Physics_AF.cpp | 360 +++++++++++- game/physics/Physics_AF.h | 21 + game/physics/Physics_Actor.cpp | 77 +++ game/physics/Physics_Actor.h | 16 + game/physics/Physics_Base.cpp | 74 +++ game/physics/Physics_Base.h | 8 + game/physics/Physics_Liquid.cpp | 194 +++++++ game/physics/Physics_Liquid.h | 60 ++ game/physics/Physics_Monster.cpp | 6 + game/physics/Physics_Player.cpp | 116 ++-- game/physics/Physics_Player.h | 14 - game/physics/Physics_RigidBody.cpp | 274 ++++++++- game/physics/Physics_RigidBody.h | 12 +- game/script/Script_Thread.cpp | 878 +++++++++++++++++++++++++++++ game/script/Script_Thread.h | 49 ++ 35 files changed, 3595 insertions(+), 233 deletions(-) create mode 100644 game/Liquid.cpp create mode 100644 game/Liquid.h create mode 100644 game/physics/Physics_Liquid.cpp create mode 100644 game/physics/Physics_Liquid.h diff --git a/framework/UsercmdGen.h b/framework/UsercmdGen.h index 17e88fe..cb0be37 100644 --- a/framework/UsercmdGen.h +++ b/framework/UsercmdGen.h @@ -81,6 +81,13 @@ const int IMPULSE_26 = 26; // const int IMPULSE_27 = 27; // const int IMPULSE_28 = 28; // vote yes const int IMPULSE_29 = 29; // vote no +//###// by MacX +const int IMPULSE_35 = 35; // slowmotion - by Cameron +const int IMPULSE_36 = 36; // toggle xp - by Cameron +const int IMPULSE_37 = 37; // toggle diary +const int IMPULSE_38 = 38; // toggle questlog +const int IMPULSE_39 = 39; // toggle thirdperson +//###// const int IMPULSE_40 = 40; // use vehicle // usercmd_t->flags diff --git a/game/AF.cpp b/game/AF.cpp index 1a97edc..ef9b496 100644 --- a/game/AF.cpp +++ b/game/AF.cpp @@ -928,6 +928,16 @@ bool idAF::Load( idEntity *ent, const char *fileName ) { } } + // load how the body will be floated in liquid + bool isFixedDensity; + if( ent->spawnArgs.GetBool( "fixedDensityBuoyancy", "1", isFixedDensity ) ) + physicsObj.SetFixedDensityBuoyancy( isFixedDensity ); + + // load liquid density from file + float liquidDensity; + if( ent->spawnArgs.GetFloat( "liquidDensity", "", liquidDensity ) ) + physicsObj.SetLiquidDensity( liquidDensity ); + physicsObj.SetMass( file->totalMass ); physicsObj.SetChanged(); diff --git a/game/Entity.cpp b/game/Entity.cpp index 1983a4c..4c49432 100644 --- a/game/Entity.cpp +++ b/game/Entity.cpp @@ -120,6 +120,8 @@ const idEventDef EV_StartFx( "startFx", "s" ); const idEventDef EV_HasFunction( "hasFunction", "s", 'd' ); const idEventDef EV_CallFunction( "callFunction", "s" ); const idEventDef EV_SetNeverDormant( "setNeverDormant", "d" ); +const idEventDef EV_GetMass("getMass","d",'f'); +const idEventDef EV_IsInLiquid("isInLiquid",NULL,'d'); ABSTRACT_DECLARATION( idClass, idEntity ) EVENT( EV_GetName, idEntity::Event_GetName ) @@ -185,6 +187,8 @@ ABSTRACT_DECLARATION( idClass, idEntity ) EVENT( EV_HasFunction, idEntity::Event_HasFunction ) EVENT( EV_CallFunction, idEntity::Event_CallFunction ) EVENT( EV_SetNeverDormant, idEntity::Event_SetNeverDormant ) + EVENT( EV_GetMass, idEntity::Event_GetMass ) + EVENT( EV_IsInLiquid, idEntity::Event_IsInLiquid ) END_CLASS /* @@ -432,6 +436,8 @@ idEntity::idEntity() { memset( &renderEntity, 0, sizeof( renderEntity ) ); modelDefHandle = -1; + modelDefHandlePost = -1; // new 6th venom + shaderPost = NULL; // new memset( &refSound, 0, sizeof( refSound ) ); mpGUIState = -1; @@ -478,6 +484,13 @@ void idEntity::Spawn( void ) { // parse static models the same way the editor display does gameEdit->ParseSpawnArgsToRenderEntity( &spawnArgs, &renderEntity ); + //new 6th venom + temp = spawnArgs.GetString( "shader_post" ); + + if( temp[0] != '\0' ) + shaderPost = (idMaterial *)declManager->FindMaterial( temp ); + + renderEntity.entityNum = entityNumber; // go dormant within 5 frames so that when the map starts most monsters are dormant @@ -663,6 +676,8 @@ void idEntity::Save( idSaveGame *savefile ) const { savefile->WriteRenderEntity( renderEntity ); savefile->WriteInt( modelDefHandle ); + savefile->WriteInt( modelDefHandlePost ); // new 6th venom + savefile->WriteMaterial( shaderPost ); // new savefile->WriteRefSound( refSound ); savefile->WriteObject( bindMaster ); @@ -738,6 +753,8 @@ void idEntity::Restore( idRestoreGame *savefile ) { savefile->ReadRenderEntity( renderEntity ); savefile->ReadInt( modelDefHandle ); + savefile->ReadInt( modelDefHandlePost ); // new 6th venom + savefile->ReadMaterial( shaderPost ); // new savefile->ReadRefSound( refSound ); savefile->ReadObject( reinterpret_cast( bindMaster ) ); @@ -778,6 +795,17 @@ void idEntity::Restore( idRestoreGame *savefile ) { if ( modelDefHandle != -1 ) { modelDefHandle = gameRenderWorld->AddEntityDef( &renderEntity ); } + + // new 6th venom + if( shaderPost ) + { + renderEntity_t post = renderEntity; + post.customShader = shaderPost; + + if( modelDefHandlePost != -1 ) + modelDefHandlePost = gameRenderWorld->AddEntityDef( &post ); + } + } /* @@ -1144,6 +1172,14 @@ void idEntity::FreeModelDef( void ) { gameRenderWorld->FreeEntityDef( modelDefHandle ); modelDefHandle = -1; } + + //new 6th venom + if( modelDefHandlePost != -1 ) + { + gameRenderWorld->FreeEntityDef( modelDefHandlePost ); + modelDefHandlePost = -1; + } + } /* @@ -1422,6 +1458,18 @@ void idEntity::Present( void ) { } else { gameRenderWorld->UpdateEntityDef( modelDefHandle, &renderEntity ); } + + //new 6th venom + if( shaderPost ) + { + renderEntity_t post = renderEntity; + post.customShader = shaderPost; + + if( modelDefHandlePost == -1 ) + modelDefHandlePost = gameRenderWorld->AddEntityDef( &post ); + else + gameRenderWorld->UpdateEntityDef( modelDefHandlePost, &post ); + } } /* @@ -4569,6 +4617,24 @@ void idEntity::Event_CallFunction( const char *funcname ) { thread->CallFunction( this, func, false ); } +/* +================ +idEntity::Event_GetMass +================ +*/ +void idEntity::Event_GetMass( int id ) { + idThread::ReturnFloat(physics->GetMass(id)); +} + +/* +================ +idEntity::Event_IsInLiquid +================ +*/ +void idEntity::Event_IsInLiquid( void ) { + idThread::ReturnInt(physics->GetWater() != NULL); +} + /* ================ idEntity::Event_SetNeverDormant diff --git a/game/Entity.h b/game/Entity.h index 709543b..65d8491 100644 --- a/game/Entity.h +++ b/game/Entity.h @@ -68,6 +68,8 @@ extern const idEventDef EV_SetSkin; extern const idEventDef EV_StartSoundShader; extern const idEventDef EV_StopSound; extern const idEventDef EV_CacheSoundShader; +extern const idEventDef EV_GetMass; +extern const idEventDef EV_IsInLiquid; // Think flags enum { @@ -364,6 +366,8 @@ public: protected: renderEntity_t renderEntity; // used to present a model to the renderer int modelDefHandle; // handle to static renderer model + int modelDefHandlePost; // new 6th venom + const idMaterial *shaderPost; // new refSound_t refSound; // used to present sound to the audio engine private: @@ -465,6 +469,8 @@ private: void Event_HasFunction( const char *name ); void Event_CallFunction( const char *name ); void Event_SetNeverDormant( int enable ); + void Event_GetMass( int body ); + void Event_IsInLiquid( void ); }; /* diff --git a/game/Game_local.h b/game/Game_local.h index df7456d..d5bf25e 100644 --- a/game/Game_local.h +++ b/game/Game_local.h @@ -656,4 +656,19 @@ extern const float DEFAULT_GRAVITY; extern const idVec3 DEFAULT_GRAVITY_VEC3; extern const int CINEMATIC_SKIP_DELAY; +#include "physics/Physics_Parametric.h" +#include "physics/Physics_RigidBody.h" +#include "physics/Physics_AF.h" +#include "physics/Physics_Liquid.h" + +#include "SmokeParticles.h" + +#include "AF.h" +#include "IK.h" +#include "AFEntity.h" +#include "Liquid.h" +#include "Misc.h" +#include "Actor.h" +#include "Projectile.h" + #endif /* !__GAME_LOCAL_H__ */ diff --git a/game/Liquid.cpp b/game/Liquid.cpp new file mode 100644 index 0000000..459c1fd --- /dev/null +++ b/game/Liquid.cpp @@ -0,0 +1,226 @@ +#include "../idlib/precompiled.h" +#pragma hdrstop + +#include "Game_local.h" + +// We do these splashes if the mass of the colliding object is less than these values. +// Anything large than MEDIUM_SPLASH does a large splash. (get it?) +const int SMALL_SPLASH = 25; +const int MEDIUM_SPLASH = 1000; + +/* +=============================================================================== + + idLiquid + +=============================================================================== +*/ + +CLASS_DECLARATION( idEntity, idLiquid ) + EVENT( EV_Touch, idLiquid::Event_Touch ) +END_CLASS + +/* +================ +idLiquid::Save +================ +*/ +void idLiquid::Save( idSaveGame *savefile ) const { + + int i; + + savefile->WriteStaticObject( this->physicsObj ); + + savefile->WriteString(smokeName.c_str()); + savefile->WriteString(soundName.c_str()); + + for( i = 0; i < 3; i++ ) + savefile->WriteParticle(this->splash[i]); + savefile->WriteParticle(this->waves); +} + +/* +================ +idLiquid::Restore +================ +*/ +void idLiquid::Restore( idRestoreGame *savefile ) { + + int i; + + savefile->ReadStaticObject( this->physicsObj ); + RestorePhysics( &this->physicsObj ); + + savefile->ReadString(this->smokeName); + savefile->ReadString(this->soundName); + + for( i = 0; i < 3; i++ ) + savefile->ReadParticle(this->splash[i]); + savefile->ReadParticle(this->waves); +} + +/* +================ +idLiquid::Spawn +================ +*/ +void idLiquid::Spawn() { +/* + model = dynamic_cast( renderEntity.hModel ); + if ( !model ) { + gameLocal.Error( "Entity '%s' must have liquid model", name.c_str() ); + } + model->Reset(); +*/ + float liquidDensity; + float liquidViscosity; + float liquidFriction; + idVec3 minSplash; + idVec3 minWave; + idStr temp; + const char *splashName; + + // getters + spawnArgs.GetFloat("density","0.01043f",liquidDensity); + spawnArgs.GetFloat("viscosity","3.0f",liquidViscosity); + spawnArgs.GetFloat("friction","3.0f",liquidFriction); + spawnArgs.GetString("liquid_name","water",temp); + spawnArgs.GetVector("minSplashVelocity","100 100 100",minSplash); + spawnArgs.GetVector("minWaveVelocity","60 60 60",minWave); + + // setters + this->smokeName = "smoke_"; + this->smokeName.Append(temp); + + this->soundName = "snd_"; + this->soundName.Append(temp); + + splashName = spawnArgs.GetString("smoke_small","water_splash_tiny"); + this->splash[0] = static_cast(declManager->FindType(DECL_PARTICLE,splashName)); + + splashName = spawnArgs.GetString("smoke_medium","water_splash"); + this->splash[1] = static_cast(declManager->FindType(DECL_PARTICLE,splashName)); + + splashName = spawnArgs.GetString("smoke_large","water_splash_large"); + this->splash[2] = static_cast(declManager->FindType(DECL_PARTICLE,splashName)); + + splashName = spawnArgs.GetString("smoke_waves","water_waves"); + this->waves = static_cast(declManager->FindType(DECL_PARTICLE,splashName)); + + // setup physics + this->physicsObj.SetSelf(this); + this->physicsObj.SetClipModel( new idClipModel(this->GetPhysics()->GetClipModel()), liquidDensity ); + this->physicsObj.SetOrigin(this->GetPhysics()->GetOrigin()); + this->physicsObj.SetAxis(this->GetPhysics()->GetAxis()); + this->physicsObj.SetGravity( gameLocal.GetGravity() ); + this->physicsObj.SetContents( CONTENTS_WATER | CONTENTS_TRIGGER ); + + this->physicsObj.SetDensity(liquidDensity); + this->physicsObj.SetViscosity(liquidViscosity); + this->physicsObj.SetMinSplashVelocity(minSplash); + this->physicsObj.SetMinWaveVelocity(minWave); + + this->SetPhysics( &this->physicsObj ); + + BecomeActive( TH_THINK ); +} + +/* +================ +idLiquid::Event_Touch + + This is mainly used for actors who touch the liquid, it spawns a splash + near they're feet if they're moving fast enough. +================ +*/ +void idLiquid::Event_Touch( idEntity *other, trace_t *trace ) { +// FIXME: for QuakeCon +/* + idVec3 pos; + + pos = other->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin(); + model->IntersectBounds( other->GetPhysics()->GetBounds().Translate( pos ), -10.0f ); +*/ + + idPhysics_Liquid *liquid; + idPhysics_Actor *phys; + + if( !other->GetPhysics()->IsType(idPhysics_Actor::Type) ) + return; + + phys = static_cast(other->GetPhysics()); + if( phys->GetWaterLevel() != WATERLEVEL_FEET ) + return; + + impactInfo_t info; + other->GetImpactInfo(this,trace->c.id,trace->c.point,&info); + liquid = &this->physicsObj; + + trace->c.point = info.position + other->GetPhysics()->GetOrigin(); + trace->c.entityNum = other->entityNumber; + + // stop actors from constantly splashing when they're in the water + // (this is such a bad thing to do!!!) + // TODO: Fixme... + // 1) Probably the best way to fix this is put a wait timer inside the actor and have this + // function set/reset that timer for when the actor should spawn particles at it's feet. + // 2) Actors don't spawn particles at their feet, it's usually at the origin, for some + // reason info.position is (null), needs a fix so that splash position is correct + if( gameLocal.random.RandomFloat() > 0.5f ) + return; + + this->Collide(*trace,info.velocity); +} + +/* +================ +idLiquid::Collide + Spawns a splash particle and attaches a sound to the colliding entity. +================ +*/ +bool idLiquid::Collide( const trace_t &collision, const idVec3 &velocity ) { + idEntity *e = gameLocal.entities[collision.c.entityNum]; + idPhysics_Liquid *phys = static_cast( this->GetPhysics() ); + const idDeclParticle *splash; + const char *sName; + float eMass; + idVec3 splashSpot; + float velSquare = velocity.LengthSqr(); + + eMass = e->GetPhysics()->GetMass(); + splashSpot = collision.c.point; + + if( velSquare > phys->GetMinSplashVelocity().LengthSqr() ) { + // pick which splash particle to spawn + // first we check the entity, if it's not defined we use + // one defined for this liquid. + sName = e->spawnArgs.GetString(this->smokeName.c_str()); + if( *sName != '\0' ) { + // load entity particle + splash = static_cast(declManager->FindType(DECL_PARTICLE,sName)); + } + else { + // load a liquid particle based on the mass of the splashing entity + if( eMass < SMALL_SPLASH ) + splash = this->splash[0]; + else if( eMass < MEDIUM_SPLASH ) + splash = this->splash[1]; + else + splash = this->splash[2]; + } + + // only play the sound for a splash + e->StartSound( this->soundName.c_str(), SND_CHANNEL_ANY, 0, false, NULL); + } + else if( velSquare > phys->GetMinWaveVelocity().LengthSqr() ) { + splash = this->waves; + } + else { + // the object is moving to slow so we abort + return true; + } + + // spawn the particle + gameLocal.smokeParticles->EmitSmoke(splash,gameLocal.time,gameLocal.random.RandomFloat(),splashSpot,mat3_identity); + return true; +} \ No newline at end of file diff --git a/game/Liquid.h b/game/Liquid.h new file mode 100644 index 0000000..14f1305 --- /dev/null +++ b/game/Liquid.h @@ -0,0 +1,43 @@ +#ifndef __LIQUID_H__ +#define __LIQUID_H__ + +/* +=============================================================================== + + idLiquid + + Base class for all liquid object. The entity part of the liquid is + responsible for spawning splashes and sounds to match. + + The physics portion is as usual, responsible for the physics. +=============================================================================== +*/ + +class idRenderModelLiquid; + +class idLiquid : public idEntity { +public: + CLASS_PROTOTYPE( idLiquid ); + + void Spawn( void ); + + void Save( idSaveGame *savefile ) const; + void Restore( idRestoreGame *savefile ); + + virtual bool Collide( const trace_t &collision, const idVec3 &velocity ); + +private: + void Event_Touch( idEntity *other, trace_t *trace ); + + idPhysics_Liquid physicsObj; + + idRenderModelLiquid *model; + + const idDeclParticle *splash[3]; + const idDeclParticle *waves; + + idStr smokeName; + idStr soundName; +}; + +#endif // __LIQUID_H__ \ No newline at end of file diff --git a/game/Misc.cpp b/game/Misc.cpp index bfde719..74658f9 100644 --- a/game/Misc.cpp +++ b/game/Misc.cpp @@ -1465,6 +1465,26 @@ void idStaticEntity::Think( void ) { renderEntity.gui[2]->StateChanged( gameLocal.time, true ); } } + //###// by MacX - using code by Cameron + if( !player->diaryUIOpen ) { + renderEntity.gui[0]->StateChanged( gameLocal.time, true ); + if ( renderEntity.gui[1] ) { + renderEntity.gui[1]->StateChanged( gameLocal.time, true ); + } + if ( renderEntity.gui[2] ) { + renderEntity.gui[2]->StateChanged( gameLocal.time, true ); + } + } + if( !player->questlogUIOpen ) { + renderEntity.gui[0]->StateChanged( gameLocal.time, true ); + if ( renderEntity.gui[1] ) { + renderEntity.gui[1]->StateChanged( gameLocal.time, true ); + } + if ( renderEntity.gui[2] ) { + renderEntity.gui[2]->StateChanged( gameLocal.time, true ); + } + } + //###// } } if ( fadeEnd > 0 ) { @@ -2293,69 +2313,6 @@ void idBeam::ReadFromSnapshot( const idBitMsgDelta &msg ) { } -/* -=============================================================================== - - idLiquid - -=============================================================================== -*/ - -CLASS_DECLARATION( idEntity, idLiquid ) - EVENT( EV_Touch, idLiquid::Event_Touch ) -END_CLASS - -/* -================ -idLiquid::Save -================ -*/ -void idLiquid::Save( idSaveGame *savefile ) const { - // Nothing to save -} - -/* -================ -idLiquid::Restore -================ -*/ -void idLiquid::Restore( idRestoreGame *savefile ) { - //FIXME: NO! - Spawn(); -} - -/* -================ -idLiquid::Spawn -================ -*/ -void idLiquid::Spawn() { -/* - model = dynamic_cast( renderEntity.hModel ); - if ( !model ) { - gameLocal.Error( "Entity '%s' must have liquid model", name.c_str() ); - } - model->Reset(); - GetPhysics()->SetContents( CONTENTS_TRIGGER ); -*/ -} - -/* -================ -idLiquid::Event_Touch -================ -*/ -void idLiquid::Event_Touch( idEntity *other, trace_t *trace ) { - // FIXME: for QuakeCon -/* - idVec3 pos; - - pos = other->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin(); - model->IntersectBounds( other->GetPhysics()->GetBounds().Translate( pos ), -10.0f ); -*/ -} - - /* =============================================================================== diff --git a/game/Misc.h b/game/Misc.h index 89a4235..0e9ce28 100644 --- a/game/Misc.h +++ b/game/Misc.h @@ -534,33 +534,6 @@ private: }; -/* -=============================================================================== - - idLiquid - -=============================================================================== -*/ - -class idRenderModelLiquid; - -class idLiquid : public idEntity { -public: - CLASS_PROTOTYPE( idLiquid ); - - void Spawn( void ); - - void Save( idSaveGame *savefile ) const; - void Restore( idRestoreGame *savefile ); - -private: - void Event_Touch( idEntity *other, trace_t *trace ); - - - idRenderModelLiquid *model; -}; - - /* =============================================================================== diff --git a/game/Player.cpp b/game/Player.cpp index 8308c96..4fb8311 100644 --- a/game/Player.cpp +++ b/game/Player.cpp @@ -128,6 +128,7 @@ idVec3 idPlayer::colorBarTable[ 5 ] = { idVec3( 1.00f, 0.80f, 0.10f ) }; + /* ============== idInventory::Clear @@ -139,6 +140,34 @@ void idInventory::Clear( void ) { powerups = 0; armor = 0; maxarmor = 0; + +//###// by MacX + + money = 0; + diary.Clear(); + quest.Clear(); + questState.Clear(); + + enemiesKilled = 0; + + testVarGui1 = 0; + testVarGui2 = 0; + testVarGui3 = 0; + testVarGui4 = 0; + testVarGui5 = 0; + testVar1 = 0; + testVar2 = 0; + testVar3 = 0; + testVar4 = 0; + testVar5 = 0; + testVar6 = 0; + testVar7 = 0; + testVar8 = 0; + testVar9 = 0; + testVar10 = 0; + +//###// + deplete_armor = 0; deplete_rate = 0.0f; deplete_ammount = 0; @@ -239,6 +268,46 @@ void idInventory::GetPersistantData( idDict &dict ) { // armor dict.SetInt( "armor", armor ); + //###// by MacX + dict.SetInt( "money", money ); + + for ( i = 0; i < diary.Num(); i++ ) { + sprintf( key, "diary_%i", i ); + dict.Set( key, diary[ i ].c_str() ); + } + dict.SetInt( "diary", diary.Num() ); + + for ( i = 0; i < quest.Num(); i++ ) { + sprintf( key, "quest_%i", i ); + dict.Set( key, quest[ i ].c_str() ); + } + dict.SetInt( "quest", quest.Num() ); + + for ( i = 0; i < questState.Num(); i++ ) { + sprintf( key, "questState_%i", i ); + dict.Set( key, questState[ i ].c_str() ); + } + dict.SetInt( "questState", questState.Num() ); + + dict.SetInt( "enemiesKilled", enemiesKilled ); + + dict.SetInt( "testVarGui1", testVarGui1 ); + dict.SetInt( "testVarGui2", testVarGui2 ); + dict.SetInt( "testVarGui3", testVarGui3 ); + dict.SetInt( "testVarGui4", testVarGui4 ); + dict.SetInt( "testVarGui5", testVarGui5 ); + dict.SetInt( "testVar1", testVar1 ); + dict.SetInt( "testVar2", testVar2 ); + dict.SetInt( "testVar3", testVar3 ); + dict.SetInt( "testVar4", testVar4 ); + dict.SetInt( "testVar5", testVar5 ); + dict.SetInt( "testVar6", testVar6 ); + dict.SetInt( "testVar7", testVar7 ); + dict.SetInt( "testVar8", testVar8 ); + dict.SetInt( "testVar9", testVar9 ); + dict.SetInt( "testVar10", testVar10 ); + //###// + // don't bother with powerups, maxhealth, maxarmor, or the clip // ammo @@ -332,6 +401,52 @@ void idInventory::RestoreInventory( idPlayer *owner, const idDict &dict ) { maxHealth = dict.GetInt( "maxhealth", "100" ); armor = dict.GetInt( "armor", "50" ); maxarmor = dict.GetInt( "maxarmor", "100" ); + + //###// by MacX + money = dict.GetInt( "money", "0" ); + + // diary + num = dict.GetInt( "diary" ); + diary.SetNum( num ); + for ( i = 0; i < num; i++ ) { + sprintf( itemname, "diary_%i", i ); + diary[i] = dict.GetString( itemname, "default" ); + } + + //quest + num = dict.GetInt( "quest" ); + quest.SetNum( num ); + for ( i = 0; i < num; i++ ) { + sprintf( itemname, "quest_%i", i ); + quest[i] = dict.GetString( itemname, "default" ); + } + + // questState + num = dict.GetInt( "questState" ); + questState.SetNum( num ); + for ( i = 0; i < num; i++ ) { + sprintf( itemname, "questState_%i", i ); + questState[i] = dict.GetString( itemname, "default" ); + } + + enemiesKilled = dict.GetInt( "enemiesKilled", "0" ); + + testVarGui1 = dict.GetInt( "testVarGui1", "0" ); + testVarGui2 = dict.GetInt( "testVarGui2", "0" ); + testVarGui3 = dict.GetInt( "testVarGui3", "0" ); + testVarGui4 = dict.GetInt( "testVarGui4", "0" ); + testVarGui5 = dict.GetInt( "testVarGui5", "0" ); + testVar1 = dict.GetInt( "testVar1", "0" ); + testVar2 = dict.GetInt( "testVar2", "0" ); + testVar3 = dict.GetInt( "testVar3", "0" ); + testVar4 = dict.GetInt( "testVar4", "0" ); + testVar5 = dict.GetInt( "testVar5", "0" ); + testVar6 = dict.GetInt( "testVar6", "0" ); + testVar7 = dict.GetInt( "testVar7", "0" ); + testVar8 = dict.GetInt( "testVar8", "0" ); + testVar9 = dict.GetInt( "testVar9", "0" ); + //###// + deplete_armor = dict.GetInt( "deplete_armor", "0" ); deplete_rate = dict.GetFloat( "deplete_rate", "2.0" ); deplete_ammount = dict.GetInt( "deplete_ammount", "1" ); @@ -432,6 +547,44 @@ void idInventory::Save( idSaveGame *savefile ) const { savefile->WriteInt( powerups ); savefile->WriteInt( armor ); savefile->WriteInt( maxarmor ); + + //###// by MacX + savefile->WriteInt( money ); + + savefile->WriteInt( diary.Num() ); + for( i = 0; i < diary.Num(); i++ ) { + savefile->WriteString( diary[ i ] ); + } + + savefile->WriteInt( quest.Num() ); + for( i = 0; i < quest.Num(); i++ ) { + savefile->WriteString( quest[ i ] ); + } + + savefile->WriteInt( questState.Num() ); + for( i = 0; i < questState.Num(); i++ ) { + savefile->WriteString( questState[ i ] ); + } + + savefile->WriteInt( enemiesKilled ); + + savefile->WriteInt( testVarGui1 ); + savefile->WriteInt( testVarGui2 ); + savefile->WriteInt( testVarGui3 ); + savefile->WriteInt( testVarGui4 ); + savefile->WriteInt( testVarGui5 ); + savefile->WriteInt( testVar1 ); + savefile->WriteInt( testVar2 ); + savefile->WriteInt( testVar3 ); + savefile->WriteInt( testVar4 ); + savefile->WriteInt( testVar5 ); + savefile->WriteInt( testVar6 ); + savefile->WriteInt( testVar7 ); + savefile->WriteInt( testVar8 ); + savefile->WriteInt( testVar9 ); + savefile->WriteInt( testVar10 ); + //###// + savefile->WriteInt( ammoPredictTime ); savefile->WriteInt( deplete_armor ); savefile->WriteFloat( deplete_rate ); @@ -528,6 +681,50 @@ void idInventory::Restore( idRestoreGame *savefile ) { savefile->ReadInt( powerups ); savefile->ReadInt( armor ); savefile->ReadInt( maxarmor ); + + //###// by MacX + savefile->ReadInt( money ); + + savefile->ReadInt( num ); + for( i = 0; i < num; i++ ) { + idStr strDiary; + savefile->ReadString( strDiary ); + diary.Append( strDiary ); + } + + savefile->ReadInt( num ); + for( i = 0; i < num; i++ ) { + idStr strQuest; + savefile->ReadString( strQuest ); + quest.Append( strQuest ); + } + + savefile->ReadInt( num ); + for( i = 0; i < num; i++ ) { + idStr strQuest; + savefile->ReadString( strQuest ); + questState.Append( strQuest ); + } + + savefile->ReadInt( enemiesKilled ); + + savefile->ReadInt( testVarGui1 ); + savefile->ReadInt( testVarGui2 ); + savefile->ReadInt( testVarGui3 ); + savefile->ReadInt( testVarGui4 ); + savefile->ReadInt( testVarGui5 ); + savefile->ReadInt( testVar1 ); + savefile->ReadInt( testVar2 ); + savefile->ReadInt( testVar3 ); + savefile->ReadInt( testVar4 ); + savefile->ReadInt( testVar5 ); + savefile->ReadInt( testVar6 ); + savefile->ReadInt( testVar7 ); + savefile->ReadInt( testVar8 ); + savefile->ReadInt( testVar9 ); + savefile->ReadInt( testVar10 ); + //###// + savefile->ReadInt( ammoPredictTime ); savefile->ReadInt( deplete_armor ); savefile->ReadFloat( deplete_rate ); @@ -762,7 +959,22 @@ bool idInventory::Give( idPlayer *owner, const idDict &spawnArgs, const char *st AddPickupName( name, "" ); } } - } else if ( !idStr::Icmp( statname, "armor" ) ) { + } +//###// by MacX + + else if ( !idStr::Icmp( statname, "money" ) ) { + amount = atoi( value ); + if ( amount ) { + money += amount; + if ( money > 999999 ) { + money = 999999; + } + } + } + +//###// + + else if ( !idStr::Icmp( statname, "armor" ) ) { if ( armor >= maxarmor ) { return false; // can't hold any more, so leave the item } @@ -977,6 +1189,14 @@ idPlayer::idPlayer() { objectiveSystem = NULL; objectiveSystemOpen = false; + //###//by MacX + diaryUI = NULL; + diaryUIOpen = false; + + questlogUI = NULL; + questlogUIOpen = false; + //###// + heartRate = BASE_HEARTRATE; heartInfo.Init( 0, 0, 0, 0 ); lastHeartAdjust = 0; @@ -1133,6 +1353,8 @@ idPlayer::idPlayer() { isChatting = false; selfSmooth = false; + + bIsZoomed = false; // sikk - Zoom DoF Fullscreen PostProcess Effect } /* @@ -1286,6 +1508,11 @@ void idPlayer::Init( void ) { } } + //###// by MacX + slowmotion = pm_slowmotion.GetFloat(); + startslow = 0; + //###// + // disable stamina on hell levels if ( gameLocal.world && gameLocal.world->spawnArgs.GetBool( "no_stamina" ) ) { pm_stamina.SetFloat( 0.0f ); @@ -1469,6 +1696,14 @@ void idPlayer::Spawn( void ) { objectiveSystem = uiManager->FindGui( "guis/pda.gui", true, false, true ); objectiveSystemOpen = false; + + //###// by MacX + diaryUI = uiManager->FindGui( "guis/diary.gui", true, false, true ); + diaryUIOpen = false; + + questlogUI = uiManager->FindGui( "guis/questlog.gui", true, false, true ); + questlogUIOpen = false; + //###// } SetLastHitTime( 0 ); @@ -1642,6 +1877,18 @@ void idPlayer::Save( idSaveGame *savefile ) const { savefile->WriteUserInterface( objectiveSystem, false ); savefile->WriteBool( objectiveSystemOpen ); + //Cameron Law START - edited by MacX + savefile->WriteFloat( slowmotion ); + savefile->WriteInt( startslow ); + //Cameron Law END + + //###// by MacX + savefile->WriteUserInterface( diaryUI, false ); + savefile->WriteBool( diaryUIOpen ); + savefile->WriteUserInterface( questlogUI, false ); + savefile->WriteBool( questlogUIOpen ); + //###// + savefile->WriteInt( weapon_soulcube ); savefile->WriteInt( weapon_pda ); savefile->WriteInt( weapon_fists ); @@ -1811,6 +2058,11 @@ void idPlayer::Save( idSaveGame *savefile ) const { savefile->WriteFloat( pm_stamina.GetFloat() ); + //###// by MacX + savefile->WriteFloat( pm_slowmotion.GetFloat() ); + savefile->WriteFloat( pm_slowmotionrate.GetFloat() ); + //###// + if ( hud ) { hud->SetStateString( "message", common->GetLanguageDict()->GetString( "#str_02916" ) ); hud->HandleNamedEvent( "Message" ); @@ -1865,6 +2117,16 @@ void idPlayer::Restore( idRestoreGame *savefile ) { savefile->ReadUserInterface( hud ); savefile->ReadUserInterface( objectiveSystem ); savefile->ReadBool( objectiveSystemOpen ); + //Cameron Law START - edited by MacX + savefile->ReadFloat( slowmotion ); + savefile->ReadInt( startslow ); + //Cameron Law END + //###// by MacX + savefile->ReadUserInterface( diaryUI ); + savefile->ReadBool( diaryUIOpen ); + savefile->ReadUserInterface( questlogUI ); + savefile->ReadBool( questlogUIOpen ); + //###// savefile->ReadInt( weapon_soulcube ); savefile->ReadInt( weapon_pda ); @@ -2061,6 +2323,13 @@ void idPlayer::Restore( idRestoreGame *savefile ) { savefile->ReadFloat( set ); pm_stamina.SetFloat( set ); + //###// by MacX + savefile->ReadFloat( set ); + pm_slowmotion.SetFloat( set ); + savefile->ReadFloat( set ); + pm_slowmotionrate.SetFloat( set ); + //###// + // create combat collision hull for exact collision detection SetCombatModel(); @@ -2533,6 +2802,29 @@ void idPlayer::UpdateHudStats( idUserInterface *_hud ) { _hud->SetStateInt( "player_hr", heartRate ); _hud->SetStateInt( "player_nostamina", ( max_stamina == 0 ) ? 1 : 0 ); +//###// by MacX + + _hud->SetStateInt( "player_money", inventory.money ); + + if( pm_thirdPerson.GetBool() == true ) { + _hud->SetStateInt( "thirdperson", 1 ); + } else { + _hud->SetStateInt( "thirdperson", 0 ); + } + + int slowmopercentage = idMath::FtoiFast( 100.0f * slowmotion / pm_slowmotion.GetFloat() ); + _hud->SetStateInt( "player_slowmotion", slowmopercentage ); + + _hud->SetStateInt( "player_enemiesKilled", inventory.enemiesKilled ); + + _hud->SetStateInt( "player_testVarGui1", inventory.testVarGui1 ); + _hud->SetStateInt( "player_testVarGui2", inventory.testVarGui2 ); + _hud->SetStateInt( "player_testVarGui3", inventory.testVarGui3 ); + _hud->SetStateInt( "player_testVarGui4", inventory.testVarGui4 ); + _hud->SetStateInt( "player_testVarGui5", inventory.testVarGui5 ); + +//###// + _hud->HandleNamedEvent( "updateArmorHealthAir" ); if ( healthPulse ) { @@ -3804,9 +4096,17 @@ idPlayer::ActiveGui =============== */ idUserInterface *idPlayer::ActiveGui( void ) { - if ( objectiveSystemOpen ) { + //###// by MacX + if ( objectiveSystemOpen || diaryUIOpen || questlogUIOpen ) { + if ( diaryUIOpen ) { + return diaryUI; + } + if ( questlogUIOpen ) { + return questlogUI; + } return objectiveSystem; } + //###// return focusUI; } @@ -3977,7 +4277,9 @@ idPlayer::Weapon_GUI */ void idPlayer::Weapon_GUI( void ) { - if ( !objectiveSystemOpen ) { +//###// by MacX + if ( !objectiveSystemOpen && !diaryUIOpen && !questlogUIOpen ) { +//###// if ( idealWeapon != currentWeapon ) { Weapon_Combat(); } @@ -4488,6 +4790,12 @@ void idPlayer::UpdateFocus( void ) { focusGUIent = ent; focusUI = ui; +//###// by MacX + + focusUI->SetStateString( "player_money", va( "%i%", inventory.money ) ); + +//###// + if ( oldFocus != ent ) { // new activation // going to see if we have anything in inventory a gui might be interested in @@ -4873,7 +5181,10 @@ void idPlayer::UpdateViewAngles( void ) { int i; idAngles delta; - if ( !noclip && ( gameLocal.inCinematic || privateCameraView || gameLocal.GetCamera() || influenceActive == INFLUENCE_LEVEL2 || objectiveSystemOpen ) ) { +//###// by MacX + if ( !noclip && ( gameLocal.inCinematic || privateCameraView || gameLocal.GetCamera() || influenceActive == INFLUENCE_LEVEL2 || objectiveSystemOpen || + diaryUIOpen || questlogUIOpen ) ) { +//###// // 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 ); @@ -5053,6 +5364,10 @@ void idPlayer::UpdateAir( void ) { return; } + //###// by MacX - using code of water physics Mod + idPhysics_Player *phys = dynamic_cast(this->GetPhysics()); + //###// + // see if the player is connected to the info_vacuum bool newAirless = false; @@ -5073,6 +5388,14 @@ void idPlayer::UpdateAir( void ) { } } + //###// by MacX - using code of water physics Mod + + // check if the player is in water + if( phys != NULL && phys->GetWaterLevel() >= WATERLEVEL_HEAD ) + newAirless = true; + + //###// + if ( newAirless ) { if ( !airless ) { StartSound( "snd_decompress", SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL ); @@ -5430,6 +5753,222 @@ void idPlayer::TogglePDA( void ) { objectiveSystemOpen ^= 1; } +//###// by MacX + +/* +============== +idPlayer::ToggleDiary +============== +*/ +void idPlayer::ToggleDiary( void ) { + if ( diaryUI == NULL ) { + return; + } + assert( hud ); + + if( !diaryUIOpen ) + { + idStrList diary = inventory.diary; + + inventory.diaryInfo.currentPage = 0; + inventory.diaryInfo.diaryText = ""; + inventory.diaryInfo.diaryText2 = ""; + inventory.diaryInfo.diaryText3 = ""; + inventory.diaryInfo.diaryText4 = ""; + inventory.diaryInfo.pageLeft = ""; + inventory.diaryInfo.pageRight = ""; + + int i = 0; + + if( diary.Num() > 0 && diary.Num() < 1000 /* something goes wrong */ ) { + + i = diary.Num(); + if( i == 1 ) { + inventory.diaryInfo.diaryText = diary[i-1]; + inventory.diaryInfo.pageLeft = va( "-%i-", i ); + inventory.diaryInfo.pageRight = va( "-%i-", i+1 ); + inventory.diaryInfo.currentPage = (( i+1 ) / 2 ); + } + else { + if( ( i % 2 ) == 1 ) { + inventory.diaryInfo.diaryText = diary[i-1]; + inventory.diaryInfo.pageLeft = va( "-%i-", i ); + inventory.diaryInfo.pageRight = va( "-%i-", i+1 ); + inventory.diaryInfo.currentPage = (( i+1 ) / 2 ); + } + else { + inventory.diaryInfo.diaryText = diary[i-2]; + inventory.diaryInfo.diaryText2 = diary[i-1]; + inventory.diaryInfo.pageLeft = va( "-%i-", i-1 ); + inventory.diaryInfo.pageRight = va( "-%i-", i ); + inventory.diaryInfo.currentPage = ( i / 2 ); + } + } + + if( inventory.diaryInfo.currentPage > 1 ) { + diaryUI->HandleNamedEvent( "prevPageOn" ); + diaryUI->HandleNamedEvent( "nextPageOff" ); + } + + diaryUI->SetStateString( "pageLeft", inventory.diaryInfo.pageLeft ); + diaryUI->SetStateString( "pageRight", inventory.diaryInfo.pageRight ); + diaryUI->SetStateString( "diaryText", inventory.diaryInfo.diaryText ); + diaryUI->SetStateString( "diaryText2", inventory.diaryInfo.diaryText2 ); + } + else { + diaryUI->SetStateString( "pageLeft", "-1-" ); + diaryUI->SetStateString( "pageRight", "-2-" ); + } + + diaryUI->Activate( true, gameLocal.time ); + } + else { + diaryUI->Activate( false, gameLocal.time ); + } + + diaryUIOpen ^= 1; +} + +/* +============== +idPlayer::ToggleQuestlog +============== +*/ +void idPlayer::ToggleQuestlog( void ) { + if ( questlogUI == NULL ) { + return; + } + assert( hud ); + + if( !questlogUIOpen ) + { + idStrList quest = inventory.quest; + + inventory.diaryInfo.currentPage = 0; + inventory.diaryInfo.diaryText = ""; + inventory.diaryInfo.diaryText2 = ""; + inventory.diaryInfo.diaryText3 = ""; + inventory.diaryInfo.diaryText4 = ""; + inventory.diaryInfo.pageLeft = ""; + inventory.diaryInfo.pageRight = ""; + + int i = 0; + + questlogUI->HandleNamedEvent( "StateLeftTopBoxOff" ); + questlogUI->HandleNamedEvent( "StateLeftBottomBoxOff" ); + questlogUI->HandleNamedEvent( "StateRightTopBoxOff" ); + questlogUI->HandleNamedEvent( "StateRightBottomBoxOff" ); + questlogUI->HandleNamedEvent( "StateLeftTopDoneOff" ); + questlogUI->HandleNamedEvent( "StateLeftBottomDoneOff" ); + questlogUI->HandleNamedEvent( "StateRightTopDoneOff" ); + questlogUI->HandleNamedEvent( "StateRightBottomDoneOff" ); + + if( quest.Num() > 0 && quest.Num() < 1000 /* something goes wrong */ ) { + i = quest.Num(); + + if( (i % 4) == 1 ) { + inventory.diaryInfo.diaryText = quest[i-1]; + inventory.diaryInfo.pageLeft = va( "-%i-", (i / 2) + 1 ); + inventory.diaryInfo.pageRight = va( "-%i-", (i / 2) + 2 ); + inventory.diaryInfo.currentPage = ((i + 3) / 4 ); + questlogUI->HandleNamedEvent( "StateLeftTopBoxOn" ); + if( inventory.questState[i-1] == "solved" ) { + questlogUI->HandleNamedEvent( "StateLeftTopDoneOn" ); + } + } + else if( (i % 4) == 2 ) { + inventory.diaryInfo.diaryText = quest[i-2]; + inventory.diaryInfo.diaryText2 = quest[i-1]; + inventory.diaryInfo.pageLeft = va( "-%i-", (i / 2) ); + inventory.diaryInfo.pageRight = va( "-%i-", (i / 2) + 1 ); + inventory.diaryInfo.currentPage = ((i + 2) / 4 ); + questlogUI->HandleNamedEvent( "StateLeftTopBoxOn" ); + questlogUI->HandleNamedEvent( "StateLeftBottomBoxOn" ); + if( inventory.questState[i-2] == "solved" ) { + questlogUI->HandleNamedEvent( "StateLeftTopDoneOn" ); + } + if( inventory.questState[i-1] == "solved" ) { + questlogUI->HandleNamedEvent( "StateLeftBottomDoneOn" ); + } + } + else if( (i % 4) == 3 ) { + inventory.diaryInfo.diaryText = quest[i-3]; + inventory.diaryInfo.diaryText2 = quest[i-2]; + inventory.diaryInfo.diaryText3 = quest[i-1]; + inventory.diaryInfo.pageLeft = va( "-%i-", (i / 2) ); + inventory.diaryInfo.pageRight = va( "-%i-", (i / 2) + 1 ); + inventory.diaryInfo.currentPage = ((i + 1) / 4 ); + + questlogUI->HandleNamedEvent( "StateLeftTopBoxOn" ); + questlogUI->HandleNamedEvent( "StateLeftBottomBoxOn" ); + questlogUI->HandleNamedEvent( "StateRightTopBoxOn" ); + + if( inventory.questState[i-3] == "solved" ) { + questlogUI->HandleNamedEvent( "StateLeftTopDoneOn" ); + } + if( inventory.questState[i-2] == "solved" ) { + questlogUI->HandleNamedEvent( "StateLeftBottomDoneOn" ); + } + if( inventory.questState[i-1] == "solved" ) { + questlogUI->HandleNamedEvent( "StateRightTopDoneOn" ); + } + } + else if( (i % 4) == 0 ) { + inventory.diaryInfo.diaryText = quest[i-4]; + inventory.diaryInfo.diaryText2 = quest[i-3]; + inventory.diaryInfo.diaryText3 = quest[i-2]; + inventory.diaryInfo.diaryText4 = quest[i-1]; + inventory.diaryInfo.pageLeft = va( "-%i-", (i / 2) - 1 ); + inventory.diaryInfo.pageRight = va( "-%i-", (i / 2) ); + inventory.diaryInfo.currentPage = ( i / 4 ); + + questlogUI->HandleNamedEvent( "StateLeftTopBoxOn" ); + questlogUI->HandleNamedEvent( "StateLeftBottomBoxOn" ); + questlogUI->HandleNamedEvent( "StateRightTopBoxOn" ); + questlogUI->HandleNamedEvent( "StateRightBottomBoxOn" ); + + if( inventory.questState[i-4] == "solved" ) { + questlogUI->HandleNamedEvent( "StateLeftTopDoneOn" ); + } + if( inventory.questState[i-3] == "solved" ) { + questlogUI->HandleNamedEvent( "StateLeftBottomDoneOn" ); + } + if( inventory.questState[i-2] == "solved" ) { + questlogUI->HandleNamedEvent( "StateRightTopDoneOn" ); + } + if( inventory.questState[i-1] == "solved" ) { + questlogUI->HandleNamedEvent( "StateRightBottomDoneOn" ); + } + } + + if( inventory.diaryInfo.currentPage > 1 ) { + questlogUI->HandleNamedEvent( "prevPageOn" ); + questlogUI->HandleNamedEvent( "nextPageOff" ); + } + + questlogUI->SetStateString( "pageLeft", inventory.diaryInfo.pageLeft ); + questlogUI->SetStateString( "pageRight", inventory.diaryInfo.pageRight ); + questlogUI->SetStateString( "diaryText", inventory.diaryInfo.diaryText ); + questlogUI->SetStateString( "diaryText2", inventory.diaryInfo.diaryText2 ); + questlogUI->SetStateString( "diaryText3", inventory.diaryInfo.diaryText3 ); + questlogUI->SetStateString( "diaryText4", inventory.diaryInfo.diaryText4 ); + } + else { + questlogUI->SetStateString( "pageLeft", "-1-" ); + questlogUI->SetStateString( "pageRight", "-2-" ); + } + + questlogUI->Activate( true, gameLocal.time ); + } + else { + questlogUI->Activate( false, gameLocal.time ); + } + + questlogUIOpen ^= 1; +} + +//###// + /* ============== idPlayer::ToggleScoreboard @@ -5565,7 +6104,9 @@ void idPlayer::PerformImpulse( int impulse ) { SelectWeapon( impulse, false ); return; } - + //Cameron Law START + idPlayer *player=gameLocal.GetLocalPlayer(); + //Cameron Law END switch( impulse ) { case IMPULSE_13: { Reload(); @@ -5625,6 +6166,47 @@ void idPlayer::PerformImpulse( int impulse ) { } break; } + //Cameron Law START + case IMPULSE_35: { + if ( cvarSystem->GetCVarFloat( "timeScale" ) == 1 ) { + idLib::common->DPrintf( "pm_slowmotion (20%): %f\n", pm_slowmotion.GetFloat() ); + idLib::common->DPrintf( "Slowmotion: %f\n", player->slowmotion ); + if ( player->slowmotion > ( pm_slowmotion.GetFloat() * 0.2f ) ) { + cvarSystem->SetCVarFloat( "timeScale", 0.5 ); + player->startslow = gameLocal.time; + } + } else { + cvarSystem->SetCVarFloat( "timeScale", 1 ); + player->startslow = gameLocal.time; + } + break; + } + //Cameron Law End + //###// by MacX + case IMPULSE_37: { + ToggleDiary(); + break; + } + case IMPULSE_38: { + ToggleQuestlog(); + break; + } + //###// + //Cameron Law START (Toggle thirdperson) + //###// by MacX + case IMPULSE_39: { // 39 -> IMPULSE_39 ( defined in framework/usercmdgen.h ) + //###// + if ( pm_thirdPerson.GetBool() == 1 ) + { + pm_thirdPerson.SetBool(0); + } + else + { + pm_thirdPerson.SetBool(1); + } + break; + } + //Cameron Law END case IMPULSE_40: { UseVehicle(); break; @@ -5641,6 +6223,16 @@ bool idPlayer::HandleESC( void ) { TogglePDA(); return true; } + //###// by MacX + if( diaryUIOpen ) { + ToggleDiary(); + return true; + } + if( questlogUIOpen ) { + ToggleQuestlog(); + return true; + } + //###// return false; } @@ -5737,7 +6329,14 @@ void idPlayer::AdjustSpeed( void ) { speed *= 0.33f; } - physicsObj.SetSpeed( speed, pm_crouchspeed.GetFloat() ); + //###// by MacX + if ( cvarSystem->GetCVarFloat( "timeScale" ) == 1 ) { + physicsObj.SetSpeed( speed, pm_crouchspeed.GetFloat() ); + } + else { + physicsObj.SetSpeed( speed + 100, pm_crouchspeed.GetFloat() + 80 ); + } + //###// } /* @@ -6197,6 +6796,33 @@ void idPlayer::Think( void ) { UpdatePlayerIcons(); + //Cameron Law END - edited by MacX + if ( cvarSystem->GetCVarFloat( "timeScale" ) != 1 ) { + if ( slowmotion <= 0 ) { + slowmotion = 0.0f; + cvarSystem->SetCVarFloat( "timeScale", 1 ); + startslow = gameLocal.time; + } else { + if ( ( startslow + 250 ) < gameLocal.time ) { + slowmotion -= 2.0f; + startslow = gameLocal.time; + } + } + } + //Cameron Law END + //###// by MacX + else { + if ( slowmotion >= pm_slowmotion.GetFloat() ) { + slowmotion = pm_slowmotion.GetFloat(); + } else { + if ( ( startslow + 1000 ) < gameLocal.time ) { + slowmotion += pm_slowmotionrate.GetFloat(); + startslow = gameLocal.time; + } + } + } + //###// + // latch button actions oldButtons = usercmd.buttons; @@ -6221,10 +6847,20 @@ void idPlayer::Think( void ) { oldFlags = usercmd.flags; } - if ( objectiveSystemOpen || gameLocal.inCinematic || influenceActive ) { +//###// by MacX - using code by Cameron + if ( objectiveSystemOpen || gameLocal.inCinematic || influenceActive || + diaryUIOpen || questlogUIOpen ) { + if ( objectiveSystemOpen && AI_PAIN ) { TogglePDA(); } + if( diaryUIOpen && AI_PAIN ) { + ToggleDiary(); + } + if( questlogUIOpen && AI_PAIN ) { + ToggleQuestlog(); + } +//###// usercmd.forwardmove = 0; usercmd.rightmove = 0; usercmd.upmove = 0; @@ -6256,8 +6892,10 @@ void idPlayer::Think( void ) { if ( ( usercmd.buttons ^ oldCmd.buttons ) & BUTTON_ZOOM ) { if ( ( usercmd.buttons & BUTTON_ZOOM ) && weapon.GetEntity() ) { zoomFov.Init( gameLocal.time, 200.0f, CalcFov( false ), weapon.GetEntity()->GetZoomFov() ); + bIsZoomed = true; // sikk - Zoom DoF Fullscreen PostProcess Effect } else { zoomFov.Init( gameLocal.time, 200.0f, zoomFov.GetCurrentValue( gameLocal.time ), DefaultFov() ); + bIsZoomed = false; // sikk - Zoom DoF Fullscreen PostProcess Effect } } @@ -7167,6 +7805,19 @@ void idPlayer::OffsetThirdPersonView( float angle, float range, float height, bo angles = viewAngles; GetViewPos( origin, axis ); + //Cameron Law START (Zoom camera in and out) + if( usercmd.buttons & BUTTON_5 && range < MAX_PLAYER_CAM ) + { + range++; + } + else if( usercmd.buttons & BUTTON_6 && range > MIN_PLAYER_CAM ) + { + range--; + } + //save range + pm_thirdPersonRange.SetFloat(range); + //Cameron Law END + if ( angle ) { angles.pitch = 0.0f; } @@ -7812,11 +8463,13 @@ void idPlayer::ClientPredictionThink( void ) { buttonMask &= usercmd.buttons; usercmd.buttons &= ~buttonMask; - if ( objectiveSystemOpen ) { +//###// by MacX - using code by Cameron + if ( objectiveSystemOpen || diaryUIOpen || questlogUIOpen ) { usercmd.forwardmove = 0; usercmd.rightmove = 0; usercmd.upmove = 0; } +//###// // clear the ik before we do anything else so the skeleton doesn't get updated twice walkIK.ClearJointMods(); diff --git a/game/Player.h b/game/Player.h index b411a0c..f069c48 100644 --- a/game/Player.h +++ b/game/Player.h @@ -31,6 +31,11 @@ If you have questions concerning this license or the applicable additional terms #include "idlib/math/Interpolate.h" +//Cameron Law START Max and Min camera settings +const float MIN_PLAYER_CAM=20; +const float MAX_PLAYER_CAM=300; +//Cameron Law END + #include "physics/Physics_Player.h" #include "Item.h" #include "Actor.h" @@ -122,6 +127,20 @@ enum { INFLUENCE_LEVEL3, // slow player movement }; +//###// by MacX + +struct idDiaryInfo{ + int currentPage; + idStr diaryText; + idStr diaryText2; + idStr diaryText3; + idStr diaryText4; + idStr pageLeft; // page number, i.e. "-1-" + idStr pageRight; // page number, i.e. "-2-" +}; + +//###// + class idInventory { public: int maxHealth; @@ -129,6 +148,33 @@ public: int powerups; int armor; int maxarmor; + +//###// by MacX + int money; + idStrList diary; + idStrList quest; + idStrList questState; + idDiaryInfo diaryInfo; + + int enemiesKilled; + + int testVarGui1; + int testVarGui2; + int testVarGui3; + int testVarGui4; + int testVarGui5; + int testVar1; + int testVar2; + int testVar3; + int testVar4; + int testVar5; + int testVar6; + int testVar7; + int testVar8; + int testVar9; + int testVar10; +//###// + int ammo[ AMMO_NUMTYPES ]; int clip[ MAX_WEAPONS ]; int powerupEndTime[ MAX_POWERUPS ]; @@ -265,6 +311,15 @@ public: idUserInterface * objectiveSystem; bool objectiveSystemOpen; + //###// by MacX + + idUserInterface* diaryUI; + bool diaryUIOpen; + idUserInterface* questlogUI; + bool questlogUIOpen; + + //###// + int weapon_soulcube; int weapon_pda; int weapon_fists; @@ -320,6 +375,11 @@ public: int minRespawnTime; // can respawn when time > this, force after g_forcerespawn int maxRespawnTime; // force respawn after this time + //Cameron Law Start - edited by MacX + float slowmotion; + int startslow; + //Cameron Law End + // the first person view values are always calculated, even // if a third person view is used idVec3 firstPersonViewOrigin; @@ -327,6 +387,7 @@ public: idDragEntity dragEntity; + bool bIsZoomed; // sikk - Zoom DoF Fullscreen PostProcess Effect public: CLASS_PROTOTYPE( idPlayer ); @@ -460,6 +521,12 @@ public: void PerformImpulse( int impulse ); void Spectate( bool spectate ); void TogglePDA( void ); + + //###// by MacX + void ToggleDiary( void ); + void ToggleQuestlog( void ); + //###// + void ToggleScoreboard( void ); void RouteGuiMouse( idUserInterface *gui ); void UpdateHud( void ); diff --git a/game/PlayerView.cpp b/game/PlayerView.cpp index 149b829..7187174 100644 --- a/game/PlayerView.cpp +++ b/game/PlayerView.cpp @@ -54,6 +54,16 @@ idPlayerView::idPlayerView() { bloodSprayMaterial = declManager->FindMaterial( "textures/decals/bloodspray" ); bfgMaterial = declManager->FindMaterial( "textures/decals/bfgvision" ); lagoMaterial = declManager->FindMaterial( LAGO_MATERIAL, false ); + +// sikk---> Brilliant Bloom/Motion Blur/DoF/Scene Effect PostProcess + bloomAddMaterial = declManager->FindMaterial( "textures/AFX/AFXadd" ); + bloomBlurMaterial = declManager->FindMaterial( "textures/AFX/AFXblurB" ); + bloomWeightMaterial = declManager->FindMaterial( "textures/AFX/AFXweight" ); + motionblurMaterial = declManager->FindMaterial( "textures/sfx/motionblur" ); + dofMaterial = declManager->FindMaterial( "textures/sfx/zoomDoF" ); + celMaterial = declManager->FindMaterial( "textures/sfx/cel" ); +// <---sikk + bfgVision = false; dvFinishTime = 0; kickFinishTime = 0; @@ -448,6 +458,17 @@ void idPlayerView::SingleView( idUserInterface *hud, const renderView_t *view ) return; } + //###// by MacX - using code by Cameron + if( player->diaryUIOpen ) { + player->diaryUI->Redraw( gameLocal.time ); + return; + } + if( player->questlogUIOpen ) { + player->questlogUI->Redraw( gameLocal.time ); + return; + } + //###// + // hack the shake in at the very last moment, so it can't cause any consistency problems renderView_t hackedView = *view; hackedView.viewaxis = hackedView.viewaxis * ShakeAxis(); @@ -458,8 +479,13 @@ void idPlayerView::SingleView( idUserInterface *hud, const renderView_t *view ) return; } +//###// by MacX + // draw screen blobs - if ( !pm_thirdPerson.GetBool() && !g_skipViewEffects.GetBool() ) { + //if ( !pm_thirdPerson.GetBool() && !g_skipViewEffects.GetBool() ) { + if ( !g_skipViewEffects.GetBool() ) { + +//###// for ( int i = 0 ; i < MAX_SCREEN_BLOBS ; i++ ) { screenBlob_t *blob = &screenBlobs[i]; if ( blob->finishTime <= gameLocal.time ) { @@ -477,7 +503,8 @@ void idPlayerView::SingleView( idUserInterface *hud, const renderView_t *view ) renderSystem->DrawStretchPic( blob->x, blob->y, blob->w, blob->h,blob->s1, blob->t1, blob->s2, blob->t2, blob->material ); } } - player->DrawHUD( hud ); + +// player->DrawHUD( hud ); // sikk - Draw the hud after postprocessing effects // armor impulse feedback float armorPulse = ( gameLocal.time - player->lastArmorPulse ) / 250.0f; @@ -487,7 +514,6 @@ void idPlayerView::SingleView( idUserInterface *hud, const renderView_t *view ) renderSystem->DrawStretchPic( 0, 0, 640, 480, 0, 0, 1, 1, armorMaterial ); } - // tunnel vision float health = 0.0f; if ( g_testHealthVision.GetFloat() != 0.0f ) { @@ -523,6 +549,27 @@ 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, bfgMaterial ); } +// sikk---> Brilliant Bloom/Filmgrain/Motion Blur/DoF/Cel Shading PostProcessing Effects + if( z_bloom.GetBool() && + !player->PowerUpActive( BERSERK ) && + //###// by MacX + ( cvarSystem->GetCVarFloat( "timeScale" ) == 1 ) ) { + //###// + + PostprocessBloom(); + } + if ( r_useCelShading.GetBool() ) { + PostprocessCelShading(); + } + if ( r_useMotionBlur.GetBool() ) { + PostprocessMotionBlur(); + } + if ( r_useZoomDoF.GetBool() ) { + PostprocessZoomDoF(); + } +// <---sikk + + player->DrawHUD( hud ); // sikk - Draw the hud after postprocessing effects } // test a single material drawn over everything @@ -711,7 +758,10 @@ void idPlayerView::RenderPlayerView( idUserInterface *hud ) { InfluenceVision( hud, view ); } else if ( gameLocal.time < dvFinishTime ) { DoubleVision( hud, view, dvFinishTime - gameLocal.time ); - } else if ( player->PowerUpActive( BERSERK ) ) { + //CAMERON LAW START +// } else if ( player->PowerUpActive( BERSERK ) ) { + } else if ( player->PowerUpActive( BERSERK ) || cvarSystem->GetCVarFloat("timeScale") !=1) { + //CAMERON LAW END BerserkVision( hud, view ); } else { SingleView( hud, view ); @@ -724,3 +774,121 @@ void idPlayerView::RenderPlayerView( idUserInterface *hud ) { renderSystem->DrawStretchPic( 10.0f, 380.0f, 64.0f, 64.0f, 0.0f, 0.0f, 1.0f, 1.0f, lagoMaterial ); } } + + +// sikk---> Brilliant Bloom Fullscreen PostProcess Effect +/* +=================== +idPlayerView::PostprocessBloom + +- original code by mahaX +=================== +*/ +void idPlayerView::PostprocessBloom() { + int bW, bH, rbW, rbH; // buffer and renderBuffer (currentRender) + float rbMx, rbMy; // renderBuffer margin + + // notes: outside the source code I might have mixed the bloom buffer as "render buffer"... + + // determine AFX buffer size + switch ( z_bloomBufferSize.GetInteger() ) { + case 0: + bW = 64; bH = 32; break; + case 1: + bW = 128; bH = 64; break; + case 2: + bW = 256; bH = 128; break; + case 3: + bW = 512; bH = 256; break; + case 4: + bW = 1024; bH = 512; break; + default: + bW = 256; bH = 128; break; + } + + // determine currentRender buffer size + if ( renderSystem->GetScreenWidth() > 1024 ) + rbW = 2048; + else + rbW = 1024; + + if ( renderSystem->GetScreenHeight() > 1024 ) + rbH = 2048; + else if ( renderSystem->GetScreenHeight() < 512 ) + rbH = 512; + else + rbH = 1024; + + rbMx = renderSystem->GetScreenWidth() / (float)rbW; + rbMy = renderSystem->GetScreenHeight() / (float)rbH; + + // capture original + renderSystem->CaptureRenderToImage( "_currentRender" ); + + // create weight map + renderSystem->CropRenderSize( 2, 2, true, true ); + renderSystem->DrawStretchPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 1, 1, 0, bloomWeightMaterial ); + renderSystem->CaptureRenderToImage( "_zweight" ); + renderSystem->UnCrop(); + + // create lower res map + renderSystem->CropRenderSize( bW, bH, true, true ); + renderSystem->DrawStretchPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, rbMy, rbMx, 0, declManager->FindMaterial( "_currentRender" ) ); + renderSystem->CaptureRenderToImage( "_zbloom" ); + + // loop iterations + for ( int i = 0; i < z_bloomIterations.GetInteger(); i++ ) { + renderSystem->DrawStretchPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 1, 1, 0, bloomBlurMaterial ); + renderSystem->CaptureRenderToImage( "_zbloom" ); + } + renderSystem->UnCrop(); + + // blend original and bloom + renderSystem->DrawStretchPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 1, 1, 0, bloomAddMaterial ); +} +// <---sikk + +// sikk---> Motion Blur Fullscreen PostProcess Effect +/* +=================== +idPlayerView::PostprocessMotionBlur +=================== +*/ +void idPlayerView::PostprocessMotionBlur() { + if ( ( player->viewAngles.pitch >= mbPrevAngles.pitch + 5 ) || + ( player->viewAngles.pitch <= mbPrevAngles.pitch - 5 ) || + ( player->viewAngles.yaw >= mbPrevAngles.yaw + 5 ) || + ( player->viewAngles.yaw <= mbPrevAngles.yaw - 5 ) ) { + + renderSystem->CaptureRenderToImage( "_currentRender" ); + renderSystem->DrawStretchPic( 0, 0, 640, 480, 0, 0, 1, 1, motionblurMaterial ); + } + + mbPrevAngles = player->viewAngles; +} +// <---sikk + +// sikk---> Zoom DoF Fullscreen PostProcess Effect +/* +=================== +idPlayerView::PostprocessZoomDoF +=================== +*/ +void idPlayerView::PostprocessZoomDoF() { + if ( player->bIsZoomed ) { + renderSystem->CaptureRenderToImage( "_currentRender" ); + renderSystem->DrawStretchPic( 0, 0, 640, 480, 0, 0, 1, 1, dofMaterial ); + } +} + +// sikk---> Cel-Shading Fullscreen PostProcess Effect +/* +=================== +idPlayerView::PostprocessCelShading +=================== +*/ +void idPlayerView::PostprocessCelShading() { + renderSystem->CaptureRenderToImage( "_currentRender" ); + renderSystem->DrawStretchPic( 0, 0, 640, 480, 0, 0, 1, 1, celMaterial); +} +// <---sikk diff --git a/game/PlayerView.h b/game/PlayerView.h index bc90458..a5b3550 100644 --- a/game/PlayerView.h +++ b/game/PlayerView.h @@ -98,6 +98,13 @@ private: void InfluenceVision( idUserInterface *hud, const renderView_t *view ); void ScreenFade(); +// sikk---> Brilliant Bloom/Motion Blur/DoF PostProcessing Effects + void PostprocessBloom(); + void PostprocessMotionBlur(); + void PostprocessZoomDoF(); + void PostprocessCelShading(); +// <---sikk + screenBlob_t * GetScreenBlob(); screenBlob_t screenBlobs[MAX_SCREEN_BLOBS]; @@ -117,6 +124,18 @@ private: const idMaterial * bloodSprayMaterial; // blood spray const idMaterial * bfgMaterial; // when targeted with BFG const idMaterial * lagoMaterial; // lagometer drawing + +// sikk---> Brilliant Bloom/Filmgrain/Motion Blur/DoF/Scene Effect PostProcess + const idMaterial * bloomAddMaterial; // Bloom Add material + const idMaterial * bloomBlurMaterial; // Bloom Blur material + const idMaterial * bloomWeightMaterial; // Bloom Weight material + const idMaterial * dofMaterial; // DoF material + const idMaterial * motionblurMaterial; // Motion Blur material + const idMaterial * celMaterial; // Cel Shading + + idAngles mbPrevAngles; // sikk - Holds previous frame's player view angle for motion blur +// <---sikk + float lastDamageTime; // accentuate the tunnel effect for a while idVec4 fadeColor; // fade color diff --git a/game/Trigger.cpp b/game/Trigger.cpp index 212b2b0..15a8fc7 100644 --- a/game/Trigger.cpp +++ b/game/Trigger.cpp @@ -287,6 +287,14 @@ void idTrigger_Multi::Save( idSaveGame *savefile ) const { savefile->WriteBool( touchOther ); savefile->WriteBool( triggerFirst ); savefile->WriteBool( triggerWithSelf ); + +//###// by MacX + savefile->WriteString( diaryTextKey ); + savefile->WriteString( questlogTextKey ); + savefile->WriteString( questDone ); + savefile->WriteString( subtitle ); +//###// + } /* @@ -306,6 +314,14 @@ void idTrigger_Multi::Restore( idRestoreGame *savefile ) { savefile->ReadBool( touchOther ); savefile->ReadBool( triggerFirst ); savefile->ReadBool( triggerWithSelf ); + +//###// by MacX + savefile->ReadString( diaryTextKey ); + savefile->ReadString( questlogTextKey ); + savefile->ReadString( questDone ); + savefile->ReadString( subtitle ); +//###// + } /* @@ -341,6 +357,13 @@ void idTrigger_Multi::Spawn( void ) { spawnArgs.GetBool( "triggerFirst", "0", triggerFirst ); spawnArgs.GetBool( "triggerWithSelf", "0", triggerWithSelf ); +//###// by MacX + spawnArgs.GetString( "diaryTextKey", "", diaryTextKey ); + spawnArgs.GetString( "questlogTextKey", "", questlogTextKey ); + spawnArgs.GetString( "questDone", "", questDone ); + spawnArgs.GetString( "subtitle", "", subtitle ); +//###// + if ( spawnArgs.GetBool( "anyTouch" ) ) { touchClient = true; touchOther = true; @@ -402,6 +425,72 @@ void idTrigger_Multi::TriggerAction( idEntity *activator ) { nextTriggerTime = gameLocal.time + 1; PostEventMS( &EV_Remove, 0 ); } + +//###// by MacX + + idStr str; + idPlayer* player = gameLocal.GetLocalPlayer(); + + idStr diaryString = ""; + if( !diaryTextKey.IsEmpty() ) { + diaryString = "#str_"; + diaryString += diaryTextKey; + } + + if ( idStr::Cmpn( diaryString, STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) { + str = common->GetLanguageDict()->GetString( diaryString.c_str() ); + player->inventory.diary.Append( str ); + } + + idStr questlogString = ""; + if( !questlogTextKey.IsEmpty() ) { + questlogString = "#str_"; + questlogString += questlogTextKey; + } + + if ( idStr::Cmpn( questlogString, STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) { + str = common->GetLanguageDict()->GetString( questlogString.c_str() ); + player->inventory.quest.Append( str ); + player->hud->HandleNamedEvent( "InfoNewQuest" ); + player->inventory.questState.Append( "unsolved" ); + } + + idStr strDone = ""; + if( !questDone.IsEmpty() ) { + strDone = "#str_"; + strDone += questDone; + } + + if ( idStr::Cmpn( strDone, STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) { + str = common->GetLanguageDict()->GetString( strDone.c_str() ); + questDone = str; + + if( !questDone.IsEmpty() ) { + for( int i = 0; i < player->inventory.quest.Num(); i++ ) { + if( questDone == player->inventory.quest[i] ) { + player->inventory.questState[i] = "solved"; + player->hud->HandleNamedEvent( "InfoQuestDone" ); + } + } + } + } + + if( g_showSubtitle.GetBool() ) { + idStr strSubtitle = ""; + if( !subtitle.IsEmpty() ) { + strSubtitle = "#str_"; + strSubtitle += subtitle; + } + + if( idStr::Cmpn( strSubtitle, STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) { + str = common->GetLanguageDict()->GetString( strSubtitle.c_str() ); + player->hud->SetStateString( "player_subtitle", str.c_str() ); + player->hud->HandleNamedEvent( "ShowSubtitle" ); + } + } + +//###// + } /* diff --git a/game/Trigger.h b/game/Trigger.h index 74d503e..e6e5e71 100644 --- a/game/Trigger.h +++ b/game/Trigger.h @@ -102,6 +102,13 @@ private: bool triggerFirst; bool triggerWithSelf; +//###// by MacX + idStr diaryTextKey; + idStr questlogTextKey; + idStr questDone; + idStr subtitle; +//###// + bool CheckFacing( idEntity *activator ); void TriggerAction( idEntity *activator ); void Event_TriggerAction( idEntity *activator ); diff --git a/game/ai/AI.cpp b/game/ai/AI.cpp index 2ac9948..e74674b 100644 --- a/game/ai/AI.cpp +++ b/game/ai/AI.cpp @@ -3398,6 +3398,28 @@ void idAI::Killed( idEntity *inflictor, idEntity *attacker, int damage, const id if ( ( attacker && attacker->IsType( idPlayer::Type ) ) && ( inflictor && !inflictor->IsType( idSoulCubeMissile::Type ) ) ) { static_cast< idPlayer* >( attacker )->AddAIKill(); } + +//###// by MacX + srand( (unsigned)time( NULL ) ); + + int randMoney = 50 + ( rand() % ( 100 - 50 + 1 ) ); // random value between 50 and 100 + + idPlayer* player = gameLocal.GetLocalPlayer(); + if( player->inventory.money < 999999 ) { + player->inventory.money += randMoney; + if( player->inventory.money > 999999 ) { + player->inventory.money = 999999; + } + } + + if( player->inventory.enemiesKilled < INT_MAX ) { + player->inventory.enemiesKilled += 1; + } + if( player->inventory.enemiesKilled < 0 ) { + player->inventory.enemiesKilled = 0; + } + +//###// } /*********************************************************************** diff --git a/game/gamesys/SysCmds.cpp b/game/gamesys/SysCmds.cpp index 105ff57..576e122 100644 --- a/game/gamesys/SysCmds.cpp +++ b/game/gamesys/SysCmds.cpp @@ -302,18 +302,26 @@ void Cmd_Give_f( const idCmdArgs &args ) { give_all = false; } - if ( give_all || ( idStr::Cmpn( name, "weapon", 6 ) == 0 ) ) { - if ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) { - gameLocal.world->spawnArgs.SetBool( "no_Weapons", false ); - for( i = 0; i < gameLocal.numClients; i++ ) { - if ( gameLocal.entities[ i ] ) { - gameLocal.entities[ i ]->PostEventSec( &EV_Player_SelectWeapon, 0.5f, gameLocal.entities[ i ]->spawnArgs.GetString( "def_weapon1" ) ); - } - } - } - } +//###// by MacX + //if ( give_all || ( idStr::Cmpn( name, "weapon", 6 ) == 0 ) ) { + // if ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) { + // gameLocal.world->spawnArgs.SetBool( "no_Weapons", false ); + // for( i = 0; i < gameLocal.numClients; i++ ) { + // if ( gameLocal.entities[ i ] ) { + // gameLocal.entities[ i ]->PostEventSec( &EV_Player_SelectWeapon, 0.5f, gameLocal.entities[ i ]->spawnArgs.GetString( "def_weapon1" ) ); + // } + // } + // } + //} +//###// - if ( ( idStr::Cmpn( name, "weapon_", 7 ) == 0 ) || ( idStr::Cmpn( name, "item_", 5 ) == 0 ) || ( idStr::Cmpn( name, "ammo_", 5 ) == 0 ) ) { + if ( ( ( idStr::Cmpn( name, "weapon_", 7 ) == 0 ) && +//###// by MacX + ( idStr::Cmpn( name, "weapon_soulcube", 15 ) != 0 ) && + ( idStr::Cmpn( name, "weapon_bfg", 10 ) != 0 ) && + ( idStr::Cmpn( name, "weapon_handgrenade", 18 ) != 0 ) ) || +//###// + ( idStr::Cmpn( name, "item_", 5 ) == 0 ) || ( idStr::Cmpn( name, "ammo_", 5 ) == 0 ) ) { player->GiveItem( name ); return; } @@ -325,14 +333,16 @@ void Cmd_Give_f( const idCmdArgs &args ) { } } - if ( give_all || idStr::Icmp( name, "weapons" ) == 0 ) { - player->inventory.weapons = BIT( MAX_WEAPONS ) - 1; - player->CacheWeapons(); +//###// by MacX + //if ( give_all || idStr::Icmp( name, "weapons" ) == 0 ) { + // player->inventory.weapons = BIT( MAX_WEAPONS ) - 1; + // player->CacheWeapons(); - if ( !give_all ) { - return; - } - } + // if ( !give_all ) { + // return; + // } + //} +//###// if ( give_all || idStr::Icmp( name, "ammo" ) == 0 ) { for ( i = 0 ; i < AMMO_NUMTYPES; i++ ) { @@ -343,12 +353,16 @@ void Cmd_Give_f( const idCmdArgs &args ) { } } - if ( give_all || idStr::Icmp( name, "armor" ) == 0 ) { - player->inventory.armor = player->inventory.maxarmor; - if ( !give_all ) { - return; - } - } +//###// by MacX + + //if ( give_all || idStr::Icmp( name, "armor" ) == 0 ) { + // player->inventory.armor = player->inventory.maxarmor; + // if ( !give_all ) { + // return; + // } + //} + +//###// if ( idStr::Icmp( name, "berserk" ) == 0 ) { player->GivePowerUp( BERSERK, SEC2MS( 30.0f ) ); diff --git a/game/gamesys/SysCvar.cpp b/game/gamesys/SysCvar.cpp index e2d64e7..f4d2ea3 100644 --- a/game/gamesys/SysCvar.cpp +++ b/game/gamesys/SysCvar.cpp @@ -146,6 +146,10 @@ idCVar g_showTestModelFrame( "g_showTestModelFrame", "0", CVAR_GAME | CVAR_B idCVar g_showActiveEntities( "g_showActiveEntities", "0", CVAR_GAME | CVAR_BOOL, "draws boxes around thinking entities. dormant entities (outside of pvs) are drawn yellow. non-dormant are green." ); idCVar g_showEnemies( "g_showEnemies", "0", CVAR_GAME | CVAR_BOOL, "draws boxes around monsters that have targeted the the player" ); +//###// by MacX +idCVar g_showSubtitle( "g_showSubtitle", "0", CVAR_GAME | CVAR_BOOL, "displays subtitle 1=on/0=off" ); +//###// + idCVar g_frametime( "g_frametime", "0", CVAR_GAME | CVAR_BOOL, "displays timing information for each game frame" ); idCVar g_timeentities( "g_timeEntities", "0", CVAR_GAME | CVAR_FLOAT, "when non-zero, shows entities whose think functions exceeded the # of milliseconds specified" ); @@ -196,6 +200,12 @@ idCVar af_useLinearTime( "af_useLinearTime", "1", CVAR_GAME | CVAR_BOOL, " idCVar af_useImpulseFriction( "af_useImpulseFriction", "0", CVAR_GAME | CVAR_BOOL, "use impulse based contact friction" ); idCVar af_useJointImpulseFriction( "af_useJointImpulseFriction","0", CVAR_GAME | CVAR_BOOL, "use impulse based joint friction" ); idCVar af_useSymmetry( "af_useSymmetry", "1", CVAR_GAME | CVAR_BOOL, "use constraint matrix symmetry" ); + +//###// by MacX - using code of water physics mod +idCVar af_useBodyDensityBuoyancy( "af_useBodyDensityBuoyancy","0", CVAR_GAME | CVAR_BOOL, "uses density of each body to calculate buoyancy"); +idCVar af_useFixedDensityBuoyancy( "af_useFixedDensityBuoyancy","1", CVAR_GAME | CVAR_BOOL, "if set, use liquidDensity as a fixed density for each body when calculating buoyancy. If clear, bodies are floated uniformly by a scalar liquidDensity as defined in the decls." ); +//###// + idCVar af_skipSelfCollision( "af_skipSelfCollision", "0", CVAR_GAME | CVAR_BOOL, "skip self collision detection" ); idCVar af_skipLimits( "af_skipLimits", "0", CVAR_GAME | CVAR_BOOL, "skip joint limits" ); idCVar af_skipFriction( "af_skipFriction", "0", CVAR_GAME | CVAR_BOOL, "skip friction" ); @@ -230,6 +240,10 @@ idCVar rb_showInertia( "rb_showInertia", "0", CVAR_GAME | CVAR_BOOL, "sho idCVar rb_showVelocity( "rb_showVelocity", "0", CVAR_GAME | CVAR_BOOL, "show the velocity of each rigid body" ); idCVar rb_showActive( "rb_showActive", "0", CVAR_GAME | CVAR_BOOL, "show rigid bodies that are not at rest" ); +//###// by MacX - using code of water physics mod +idCVar rb_showBuoyancy( "rb_showBuoyancy", "0", CVAR_GAME | CVAR_BOOL, "show rigid body buoyancy information" ); +//###// + // The default values for player movement cvars are set in def/player.def idCVar pm_jumpheight( "pm_jumpheight", "48", CVAR_GAME | CVAR_NETWORKSYNC | CVAR_FLOAT, "approximate height the player can jump" ); idCVar pm_stepsize( "pm_stepsize", "16", CVAR_GAME | CVAR_NETWORKSYNC | CVAR_FLOAT, "maximum height the player can step up without jumping" ); @@ -245,6 +259,12 @@ idCVar pm_maxviewpitch( "pm_maxviewpitch", "89", CVAR_GAME | CVAR_NETWORK idCVar pm_stamina( "pm_stamina", "24", CVAR_GAME | CVAR_NETWORKSYNC | CVAR_FLOAT, "length of time player can run" ); idCVar pm_staminathreshold( "pm_staminathreshold", "45", CVAR_GAME | CVAR_NETWORKSYNC | CVAR_FLOAT, "when stamina drops below this value, player gradually slows to a walk" ); idCVar pm_staminarate( "pm_staminarate", "0.75", CVAR_GAME | CVAR_NETWORKSYNC | CVAR_FLOAT, "rate that player regains stamina. divide pm_stamina by this value to determine how long it takes to fully recharge." ); + +//###// by MacX +idCVar pm_slowmotion( "pm_slowmotion", "60", CVAR_GAME | CVAR_NETWORKSYNC | CVAR_FLOAT, "length of time in slow motion" ); +idCVar pm_slowmotionrate( "pm_slowmotionrate", "1", CVAR_GAME | CVAR_NETWORKSYNC | CVAR_FLOAT, "rate to regain slow motion time" ); +//###// + idCVar pm_crouchheight( "pm_crouchheight", "38", CVAR_GAME | CVAR_NETWORKSYNC | CVAR_FLOAT, "height of player's bounding box while crouched" ); idCVar pm_crouchviewheight( "pm_crouchviewheight", "32", CVAR_GAME | CVAR_NETWORKSYNC | CVAR_FLOAT, "height of player's view while crouched" ); idCVar pm_normalheight( "pm_normalheight", "74", CVAR_GAME | CVAR_NETWORKSYNC | CVAR_FLOAT, "height of player's bounding box while standing" ); @@ -335,3 +355,17 @@ idCVar mod_validSkins( "mod_validSkins", "skins/characters/player/marine_mp idCVar net_serverDownload( "net_serverDownload", "0", CVAR_GAME | CVAR_INTEGER | CVAR_ARCHIVE, "enable server download redirects. 0: off 1: redirect to si_serverURL 2: use builtin download. see net_serverDl cvars for configuration" ); idCVar net_serverDlBaseURL( "net_serverDlBaseURL", "", CVAR_GAME | CVAR_ARCHIVE, "base URL for the download redirection" ); idCVar net_serverDlTable( "net_serverDlTable", "", CVAR_GAME | CVAR_ARCHIVE, "pak names for which download is provided, separated by ;" ); + +// ### z_mod ### + +idCVar z_bloom("z_bloom", "1", CVAR_GAME | CVAR_BOOL, "Enable bloom" ); +idCVar z_bloomBufferSize("z_bloomBufferSize", "2", CVAR_GAME | CVAR_INTEGER | CVAR_ARCHIVE, "bloom render to texture size: \n0 = 64x32\n1 = 128x64\n2 = 256x128\n3 = 512x256\n4 = 1024x512" ); +idCVar z_bloomIterations("z_bloomIterations", "12", CVAR_GAME | CVAR_INTEGER | CVAR_ARCHIVE, "Number of times the blur filter is applied" ); + +//idCVar z_decalPP("z_decalPP", "1", CVAR_GAME | CVAR_BOOL, "Enable decal post processing effects" ); + +// sikk---> Motion Blur/DoF post processing effects +idCVar r_useMotionBlur( "r_useMotionBlur", "0", CVAR_GAME | CVAR_NOCHEAT | CVAR_BOOL | CVAR_ARCHIVE, "Enable motion blur postprocessing effect." ); +idCVar r_useZoomDoF( "r_useZoomDoF", "0", CVAR_GAME | CVAR_NOCHEAT | CVAR_BOOL | CVAR_ARCHIVE, "Enable fake depth of field postprocessing effect when zoomed." ); +idCVar r_useCelShading( "r_useCelShading", "1", CVAR_GAME | CVAR_NOCHEAT | CVAR_BOOL | CVAR_ARCHIVE, "Enable cel shading." ); +// <---sikk diff --git a/game/gamesys/SysCvar.h b/game/gamesys/SysCvar.h index da8e26e..27b122d 100644 --- a/game/gamesys/SysCvar.h +++ b/game/gamesys/SysCvar.h @@ -83,6 +83,10 @@ extern idCVar g_showTestModelFrame; extern idCVar g_showActiveEntities; extern idCVar g_showEnemies; +//###// by MacX +extern idCVar g_showSubtitle; +//###// + extern idCVar g_frametime; extern idCVar g_timeentities; @@ -126,6 +130,12 @@ extern idCVar af_useLinearTime; extern idCVar af_useImpulseFriction; extern idCVar af_useJointImpulseFriction; extern idCVar af_useSymmetry; + +//###// by MacX - using code of water physics mod +extern idCVar af_useBodyDensityBuoyancy; +extern idCVar af_useFixedDensityBuoyancy; +//###// + extern idCVar af_skipSelfCollision; extern idCVar af_skipLimits; extern idCVar af_skipFriction; @@ -160,6 +170,10 @@ extern idCVar rb_showInertia; extern idCVar rb_showVelocity; extern idCVar rb_showActive; +//###// by MacX - using code of water physics mod +extern idCVar rb_showBuoyancy; +//###// + extern idCVar pm_jumpheight; extern idCVar pm_stepsize; extern idCVar pm_crouchspeed; @@ -174,6 +188,12 @@ extern idCVar pm_maxviewpitch; extern idCVar pm_stamina; extern idCVar pm_staminathreshold; extern idCVar pm_staminarate; + +//###// by MacX +extern idCVar pm_slowmotion; +extern idCVar pm_slowmotionrate; +//###// + extern idCVar pm_crouchheight; extern idCVar pm_crouchviewheight; extern idCVar pm_normalheight; @@ -254,4 +274,18 @@ extern const char *si_gameTypeArgs[]; extern const char *ui_skinArgs[]; +// ### z_mod ### +extern idCVar z_bloom; +extern idCVar z_bloomBufferSize; +extern idCVar z_bloomIterations; + +//extern idCVar z_decalPP; // decal post process + +// sikk---> Motion Blur/DoF/Cel Shading PostProcessing Effects +extern idCVar r_useMotionBlur; +extern idCVar r_useZoomDoF; +extern idCVar r_useCelShading; +// <---sikk + + #endif /* !__SYS_CVAR_H__ */ diff --git a/game/physics/Physics.h b/game/physics/Physics.h index 545c6ee..a99e210 100644 --- a/game/physics/Physics.h +++ b/game/physics/Physics.h @@ -70,6 +70,7 @@ If you have questions concerning this license or the applicable additional terms #define CONTACT_EPSILON 0.25f // maximum contact seperation distance class idEntity; +class idPhysics_Liquid; typedef struct impactInfo_s { float invMass; // inverse mass @@ -184,6 +185,12 @@ public: // common physics interface // networking virtual void WriteToSnapshot( idBitMsgDelta &msg ) const = 0; virtual void ReadFromSnapshot( const idBitMsgDelta &msg ) = 0; + + // gets/sets the water + // these should be pure virtual but I would've had to change 10 or so other classes + // so this was a better solution + virtual idPhysics_Liquid *GetWater() { return NULL; } + virtual void SetWater( idPhysics_Liquid *e ) {} }; #endif /* !__PHYSICS_H__ */ diff --git a/game/physics/Physics_AF.cpp b/game/physics/Physics_AF.cpp index 3730480..b851efd 100644 --- a/game/physics/Physics_AF.cpp +++ b/game/physics/Physics_AF.cpp @@ -47,16 +47,22 @@ const float LCP_EPSILON = 1e-7f; const float LIMIT_LCP_EPSILON = 1e-4f; const float CONTACT_LCP_EPSILON = 1e-6f; const float CENTER_OF_MASS_EPSILON = 1e-4f; -const float NO_MOVE_TIME = 1.0f; +const float NO_MOVE_TIME = 2.0f; const float NO_MOVE_TRANSLATION_TOLERANCE = 10.0f; const float NO_MOVE_ROTATION_TOLERANCE = 10.0f; const float MIN_MOVE_TIME = -1.0f; const float MAX_MOVE_TIME = -1.0f; -const float IMPULSE_THRESHOLD = 500.0f; +const float IMPULSE_THRESHOLD = 1500.0f; const float SUSPEND_LINEAR_VELOCITY = 10.0f; const float SUSPEND_ANGULAR_VELOCITY = 15.0f; const float SUSPEND_LINEAR_ACCELERATION = 20.0f; const float SUSPEND_ANGULAR_ACCELERATION = 30.0f; + +const float WATER_FRICTION = 0.0f; // we need AF friction to be a little bigger than RB water friction, we add this value +const float DEFAULT_LIQUID_SCALAR = -0.28f; +const float DEFAULT_LIQUID_DENSITY = 0.005f; +const float LIQUID_MASS_MUL = 3.0f; // I'm not sure how to explain this, without it body bob way too quickly + const idVec6 vec6_lcp_epsilon = idVec6( LCP_EPSILON, LCP_EPSILON, LCP_EPSILON, LCP_EPSILON, LCP_EPSILON, LCP_EPSILON ); @@ -4205,6 +4211,10 @@ void idAFBody::Init( void ) { centerOfMass = vec3_zero; inertiaTensor = mat3_identity; inverseInertiaTensor = mat3_identity; + this->volume = 1.0f; + this->liquidMass = 1.0f; + this->invLiquidMass = 1.0f; + this->waterLevel = 0.0f; current = &state[0]; next = &state[1]; @@ -4317,6 +4327,11 @@ void idAFBody::SetDensity( float density, const idMat3 &inertiaScale ) { else { inverseInertiaTensor = inertiaTensor.Inverse(); } + + // stuff for water + this->volume = mass / density; + this->liquidMass = this->mass; + this->invLiquidMass = this->invMass; } /* @@ -4425,6 +4440,9 @@ void idAFBody::Save( idSaveGame *saveFile ) { saveFile->WriteFloat( contactMotorVelocity ); saveFile->WriteFloat( contactMotorForce ); + saveFile->WriteFloat( volume ); + saveFile->WriteFloat( liquidMass ); + saveFile->WriteFloat( invLiquidMass ); saveFile->WriteFloat( mass ); saveFile->WriteFloat( invMass ); saveFile->WriteVec3( centerOfMass ); @@ -4455,6 +4473,9 @@ void idAFBody::Restore( idRestoreGame *saveFile ) { saveFile->ReadFloat( contactMotorVelocity ); saveFile->ReadFloat( contactMotorForce ); + saveFile->ReadFloat( volume ); + saveFile->ReadFloat( liquidMass ); + saveFile->ReadFloat( invLiquidMass ); saveFile->ReadFloat( mass ); saveFile->ReadFloat( invMass ); saveFile->ReadVec3( centerOfMass ); @@ -4470,6 +4491,89 @@ void idAFBody::Restore( idRestoreGame *saveFile ) { } +/* +================ +idAFBody::GetWaterLevel + returns the percent of the body in water (set by SetWaterLevel) +================ +*/ +float idAFBody::GetWaterLevel() const { + return this->waterLevel; +} + +/* +================ +idAFBody::SetWaterLevel + returns the percent of the body in water + 0.0f if out of water + + Note we use the liquid's gravity normal for + floating because the idPhysics_AF gravity normal + is really hard to get a hold of! +================ +*/ +float idAFBody::SetWaterLevel( idPhysics_Liquid *l, const idVec3 &gravityNormal, bool fixedDensityBuoyancy ) { + if( l == NULL ) { + this->waterLevel = 0.0f; + return 0.0f; + } + + if( !fixedDensityBuoyancy ) { + const idBounds &bounds = this->clipModel->GetBounds(); + idVec3 depth,point; + float height, d; + + // + // check if physics object is under water + // and return the percentage of the object under water + // + point = this->GetWorldOrigin(); + + depth = l->GetDepth(point); + // height = abs( (bounds[0] - bounds[1]) * gravityNormal ) * 0.5f; + // d = abs( depth * gravityNormal ); + height = abs( bounds[0].z - bounds[1].z ) * 0.5f; + d = depth.z; + + if( d < 0 ) + this->waterLevel = 0.0f; + else if( d > height ) + this->waterLevel = 1.0f; + else + this->waterLevel = d / height; + } + else { + idVec3 depth,bottom(this->current->worldOrigin); + idBounds bounds = this->clipModel->GetBounds(); + float height,d; + + // offset and rotate the bounding box + bounds += -centerOfMass; + bounds *= this->current->worldAxis.Transpose(); + + // gets the position of the object relative to the surface of the water + height = abs(bounds[1] * gravityNormal * 2); + + // calculates the depth of the bottom of the object + bottom += (height * 0.5f) * gravityNormal; + depth = l->GetDepth(bottom); + d = abs(depth * gravityNormal); + + if( d > height ) { + // the body is totally submerged + this->waterLevel = 1.0f; + } + else if( depth.x == -1 && depth.y == -1 && depth.z == -1 ) { + this->waterLevel = 0.0f; + } + else { + // the body is partly submerged + this->waterLevel = d / height; + } + } + return this->waterLevel; +} + //=============================================================== // M @@ -4905,11 +5009,21 @@ idPhysics_AF::EvaluateBodies void idPhysics_AF::EvaluateBodies( float timeStep ) { int i; idAFBody *body; + float bMass, invbMass; idMat3 axis; for ( i = 0; i < bodies.Num(); i++ ) { body = bodies[i]; + if( this->water != NULL && body->GetWaterLevel() > 0.0f ) { + bMass = body->liquidMass; + invbMass = body->invLiquidMass; + } + else { + bMass = body->mass; + invbMass = body->invMass; + } + // we transpose the axis before using it because idMat3 is column-major axis = body->current->worldAxis.Transpose(); @@ -4917,20 +5031,20 @@ void idPhysics_AF::EvaluateBodies( float timeStep ) { if ( body->centerOfMass.Compare( vec3_origin, CENTER_OF_MASS_EPSILON ) ) { // spatial inertia in world space - body->I.Set( body->mass * mat3_identity, mat3_zero, + body->I.Set( bMass * mat3_identity, mat3_zero, mat3_zero, axis * body->inertiaTensor * axis.Transpose() ); // inverse spatial inertia in world space - body->inverseWorldSpatialInertia.Set( body->invMass * mat3_identity, mat3_zero, + body->inverseWorldSpatialInertia.Set( invbMass * mat3_identity, mat3_zero, mat3_zero, axis * body->inverseInertiaTensor * axis.Transpose() ); body->fl.spatialInertiaSparse = true; } else { - idMat3 massMoment = body->mass * SkewSymmetric( body->centerOfMass ); + idMat3 massMoment = bMass * SkewSymmetric( body->centerOfMass ); // spatial inertia in world space - body->I.Set( body->mass * mat3_identity, massMoment, + body->I.Set( bMass * mat3_identity, massMoment, massMoment.Transpose(), axis * body->inertiaTensor * axis.Transpose() ); // inverse spatial inertia in world space @@ -5337,7 +5451,7 @@ idPhysics_AF::Evolve */ void idPhysics_AF::Evolve( float timeStep ) { int i; - float angle; + float angle,waterLevel; idVec3 vec; idAFBody *body; idVec6 force; @@ -5392,7 +5506,15 @@ void idPhysics_AF::Evolve( float timeStep ) { body->next->worldAxis.OrthoNormalizeSelf(); // linear and angular friction - body->next->spatialVelocity.SubVec3(0) -= body->linearFriction * body->next->spatialVelocity.SubVec3(0); + // apply a higher friction value if the AF is underwater + waterLevel = body->GetWaterLevel(); + if( waterLevel == 0.0f || this->water == NULL ) { + body->next->spatialVelocity.SubVec3(0) -= body->linearFriction * body->next->spatialVelocity.SubVec3(0); + } + else { + body->next->spatialVelocity.SubVec3(0) -= (body->linearFriction * (this->water->GetViscosity()+WATER_FRICTION) * waterLevel) * body->next->spatialVelocity.SubVec3(0); + } + body->next->spatialVelocity.SubVec3(1) -= body->angularFriction * body->next->spatialVelocity.SubVec3(1); } } @@ -5410,6 +5532,7 @@ bool idPhysics_AF::CollisionImpulse( float timeStep, idAFBody *body, trace_t &co idVec3 r, velocity, impulse; idMat3 inverseWorldInertiaTensor; float impulseNumerator, impulseDenominator; + float invMass; impactInfo_t info; idEntity *ent; @@ -5418,6 +5541,13 @@ bool idPhysics_AF::CollisionImpulse( float timeStep, idAFBody *body, trace_t &co return false; } + if( this->water != NULL ) { + invMass = body->invLiquidMass; + } + else { + invMass = body->invMass; + } + // get info from other entity involved ent->GetImpactInfo( self, collision.c.id, collision.c.point, &info ); // collision point relative to the body center of mass @@ -5432,7 +5562,7 @@ bool idPhysics_AF::CollisionImpulse( float timeStep, idAFBody *body, trace_t &co } inverseWorldInertiaTensor = body->current->worldAxis.Transpose() * body->inverseInertiaTensor * body->current->worldAxis; impulseNumerator = -( 1.0f + body->bouncyness ) * ( velocity * collision.c.normal ); - impulseDenominator = body->invMass + ( ( inverseWorldInertiaTensor * r.Cross( collision.c.normal ) ).Cross( r ) * collision.c.normal ); + impulseDenominator = invMass + ( ( inverseWorldInertiaTensor * r.Cross( collision.c.normal ) ).Cross( r ) * collision.c.normal ); if ( info.invMass ) { impulseDenominator += info.invMass + ( ( info.invInertiaTensor * info.position.Cross( collision.c.normal ) ).Cross( info.position ) * collision.c.normal ); } @@ -5549,6 +5679,7 @@ void idPhysics_AF::CheckForCollisions( float timeStep ) { idRotation rotation; trace_t collision; idEntity *passEntity; + impactInfo_t info; // clear list with collisions collisions.SetNum( 0, false ); @@ -5591,6 +5722,25 @@ void idPhysics_AF::CheckForCollisions( float timeStep ) { collisions[index].body = body; } + // Check for water collision + // ideally we could do this check in one step but if a body moves quickly in shallow water + // they will occasionally clip through a solid entity (ie. fall through the floor) + if ( gameLocal.clip.Motion( collision, body->current->worldOrigin, body->next->worldOrigin, rotation, + body->clipModel, body->current->worldAxis, MASK_WATER, passEntity ) ) { + idEntity *ent = gameLocal.entities[collision.c.entityNum]; + + // if the object collides with something with a physics_liquid + if( ent->GetPhysics()->IsType( idPhysics_Liquid::Type ) ) { + idPhysics_Liquid *liquid = static_cast(ent->GetPhysics()); + impactInfo_t info; + + this->self->GetImpactInfo(ent,collision.c.id,collision.c.point,&info); + + this->SetWater(liquid); + this->water->Splash(this->self,body->GetVolume(),info,collision); + } + } + #ifdef TEST_COLLISION_DETECTION if ( gameLocal.clip.Contents( body->next->worldOrigin, body->clipModel, body->next->worldAxis, body->clipMask, passEntity ) ) { @@ -5764,14 +5914,53 @@ idPhysics_AF::AddGravity ================ */ void idPhysics_AF::AddGravity( void ) { - int i; idAFBody *body; + idVec3 grav( this->liquidDensity * this->gravityVector ); + float waterLevel,wDensity; + bool inWater,bodyBuoyancy; + int i; + if( this->SetWaterLevelf() == 1.0f ) { + wDensity = this->water->GetDensity(); + bodyBuoyancy = af_useBodyDensityBuoyancy.GetBool(); + } + + inWater = false; for ( i = 0; i < bodies.Num(); i++ ) { body = bodies[i]; + // add gravitational force - body->current->externalForce.SubVec3( 0 ) += body->mass * gravityVector; + waterLevel = body->SetWaterLevel(this->water,this->gravityNormal,this->fixedDensityBuoyancy); + if( waterLevel > 0.0f ) { + if( !this->fixedDensityBuoyancy && !bodyBuoyancy ) + { + body->liquidMass = body->mass; + body->invLiquidMass = body->invMass; + } + else { + body->liquidMass = body->volume * this->liquidDensity * LIQUID_MASS_MUL; + body->invLiquidMass = 1 / body->liquidMass; + } + + // we float the body in water + if( bodyBuoyancy ) + body->current->externalForce.SubVec3( 0 ) += (body->mass - (body->volume * wDensity * waterLevel)) * gravityVector; + else if( this->fixedDensityBuoyancy ) + body->current->externalForce.SubVec3( 0 ) += body->volume * ( this->liquidDensity - (wDensity * waterLevel) ) * gravityVector; + else + body->current->externalForce.SubVec3( 0 ) += body->mass * grav * waterLevel; + + inWater = true; + } + else { + body->current->externalForce.SubVec3( 0 ) += body->mass * gravityVector; + } } + + // if all AFBodies are not in the water, we assume the whole entity is not in water so + // we clear the water flag + if( !inWater ) + this->water = NULL; } /* @@ -5919,6 +6108,11 @@ bool idPhysics_AF::TestIfAtRest( float timeStep ) { return true; } + // prevent bodies from going in active after floating. You don't really want bodies to + // go inactive if they're in water (sometimes they just have a long way to go before surfacing) + if( this->water != NULL ) + current.activateTime = 0.0f; + current.activateTime += timeStep; // if the simulation should never be suspended before a certaint amount of time passed @@ -5966,20 +6160,41 @@ bool idPhysics_AF::TestIfAtRest( float timeStep ) { } // test if the velocity or acceleration of any body is still too large to come to rest - for ( i = 0; i < bodies.Num(); i++ ) { - body = bodies[i]; + // we do seperates tests for if we're in water or not + if( this->water == NULL ) { + for ( i = 0; i < bodies.Num(); i++ ) { + body = bodies[i]; - if ( body->current->spatialVelocity.SubVec3(0).LengthSqr() > Square( suspendVelocity[0] ) ) { - return false; + if ( body->current->spatialVelocity.SubVec3(0).LengthSqr() > ( suspendVelocity[0] ) ) { + return false; + } + if ( body->current->spatialVelocity.SubVec3(1).LengthSqr() > ( suspendVelocity[1] ) ) { + return false; + } + if ( body->acceleration.SubVec3(0).LengthSqr() > Square( suspendAcceleration[0] ) ) { + return false; + } + if ( body->acceleration.SubVec3(1).LengthSqr() > Square( suspendAcceleration[1] ) ) { + return false; + } } - if ( body->current->spatialVelocity.SubVec3(1).LengthSqr() > Square( suspendVelocity[1] ) ) { - return false; - } - if ( body->acceleration.SubVec3(0).LengthSqr() > Square( suspendAcceleration[0] ) ) { - return false; - } - if ( body->acceleration.SubVec3(1).LengthSqr() > Square( suspendAcceleration[1] ) ) { - return false; + } + else { + for ( i = 0; i < bodies.Num(); i++ ) { + body = bodies[i]; + + if ( body->current->spatialVelocity.SubVec3(0).LengthSqr() > Square( suspendVelocity[0] ) ) { + return false; + } + if ( body->current->spatialVelocity.SubVec3(1).LengthSqr() > Square( suspendVelocity[1] ) ) { + return false; + } + if ( body->acceleration.SubVec3(0).LengthSqr() > Square( suspendAcceleration[0] ) ) { + return false; + } + if ( body->acceleration.SubVec3(1).LengthSqr() > Square( suspendAcceleration[1] ) ) { + return false; + } } } @@ -6118,8 +6333,24 @@ idPhysics_AF::GetMass */ float idPhysics_AF::GetMass( int id ) const { if ( id >= 0 && id < bodies.Num() ) { - return bodies[id]->mass; + if( bodies[id]->GetWaterLevel() > 0.0f ) + return bodies[id]->liquidMass; + else + return bodies[id]->mass; } + + // if body in water, we have to recompute the total mass + if( this->water != NULL ) { + int i; + float waterMass = 0.0f; + + for( i = 0; i < this->bodies.Num(); i++ ) { + waterMass += this->bodies[i]->liquidMass; + } + + return waterMass; + } + return totalMass; } @@ -6524,13 +6755,16 @@ void idPhysics_AF::DebugDraw( void ) { if ( af_showMass.GetBool() ) { for ( i = 0; i < bodies.Num(); i++ ) { body = bodies[i]; - gameRenderWorld->DrawText( va( "\n%1.2f", 1.0f / body->GetInverseMass() ), body->GetWorldOrigin(), 0.08f, colorCyan, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1 ); + if( body->GetWaterLevel() > 0.0f ) + gameRenderWorld->DrawText( va( "\n%1.2f", body->liquidMass ), body->GetWorldOrigin(), 0.08f, colorCyan, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1 ); + else + gameRenderWorld->DrawText( va( "\n%1.2f", body->mass ), body->GetWorldOrigin(), 0.08f, colorCyan, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1 ); } } if ( af_showTotalMass.GetBool() ) { axis = gameLocal.GetLocalPlayer()->viewAngles.ToMat3(); - gameRenderWorld->DrawText( va( "\n%1.2f", totalMass ), bodies[0]->GetWorldOrigin() + axis[2] * 8.0f, 0.15f, colorCyan, axis, 1 ); + gameRenderWorld->DrawText( va( "\n%1.2f", this->GetMass() ), bodies[0]->GetWorldOrigin() + axis[2] * 8.0f, 0.15f, colorCyan, axis, 1 ); } if ( af_showInertia.GetBool() ) { @@ -6617,6 +6851,17 @@ idPhysics_AF::idPhysics_AF( void ) { totalMass = 0.0f; forceTotalMass = -1.0f; + // sets default buoyancy property based on CVar + if( af_useFixedDensityBuoyancy.GetBool() ) { + this->fixedDensityBuoyancy = true; + this->liquidDensity = DEFAULT_LIQUID_DENSITY; + } + else { + this->fixedDensityBuoyancy = false; + this->liquidDensity = DEFAULT_LIQUID_SCALAR; + } + this->water = NULL; + suspendVelocity.Set( SUSPEND_LINEAR_VELOCITY, SUSPEND_ANGULAR_VELOCITY ); suspendAcceleration.Set( SUSPEND_LINEAR_ACCELERATION, SUSPEND_LINEAR_ACCELERATION ); noMoveTime = NO_MOVE_TIME; @@ -6749,6 +6994,9 @@ void idPhysics_AF::Save( idSaveGame *saveFile ) const { saveFile->WriteFloat( totalMass ); saveFile->WriteFloat( forceTotalMass ); + saveFile->WriteBool( this->fixedDensityBuoyancy ); + saveFile->WriteFloat( this->liquidDensity ); + saveFile->WriteVec2( suspendVelocity ); saveFile->WriteVec2( suspendAcceleration ); saveFile->WriteFloat( noMoveTime ); @@ -6823,6 +7071,9 @@ void idPhysics_AF::Restore( idRestoreGame *saveFile ) { saveFile->ReadFloat( totalMass ); saveFile->ReadFloat( forceTotalMass ); + saveFile->ReadBool( this->fixedDensityBuoyancy ); + saveFile->ReadFloat( this->liquidDensity ); + saveFile->ReadVec2( suspendVelocity ); saveFile->ReadVec2( suspendAcceleration ); saveFile->ReadFloat( noMoveTime ); @@ -7399,7 +7650,11 @@ void idPhysics_AF::GetImpactInfo( const int id, const idVec3 &point, impactInfo_ memset( info, 0, sizeof( *info ) ); return; } - info->invMass = 1.0f / bodies[id]->mass; + if( this->water != NULL ) + info->invMass = bodies[id]->invLiquidMass; + else + info->invMass = bodies[id]->invMass; + info->invInertiaTensor = bodies[id]->current->worldAxis.Transpose() * bodies[id]->inverseInertiaTensor * bodies[id]->current->worldAxis; info->position = point - bodies[id]->current->worldOrigin; info->velocity = bodies[id]->current->spatialVelocity.SubVec3(0) + bodies[id]->current->spatialVelocity.SubVec3(1).Cross( info->position ); @@ -7418,7 +7673,11 @@ void idPhysics_AF::ApplyImpulse( const int id, const idVec3 &point, const idVec3 return; } idMat3 invWorldInertiaTensor = bodies[id]->current->worldAxis.Transpose() * bodies[id]->inverseInertiaTensor * bodies[id]->current->worldAxis; - bodies[id]->current->spatialVelocity.SubVec3(0) += bodies[id]->invMass * impulse; + if( this->water != NULL ) + bodies[id]->current->spatialVelocity.SubVec3(0) += bodies[id]->invLiquidMass * impulse; + else + bodies[id]->current->spatialVelocity.SubVec3(0) += bodies[id]->invMass * impulse; + bodies[id]->current->spatialVelocity.SubVec3(1) += invWorldInertiaTensor * (point - bodies[id]->current->worldOrigin).Cross( impulse ); Activate(); } @@ -8011,3 +8270,48 @@ void idPhysics_AF::ReadFromSnapshot( const idBitMsgDelta &msg ) { UpdateClipModels(); } + +/* +================ +idPhysics_AF::SetLiquidDensity +================ +*/ +void idPhysics_AF::SetLiquidDensity( float density ) +{ + this->liquidDensity = density; +} + +/* +================ +idPhysics_AF::GetLiquidDensity +================ +*/ +float idPhysics_AF::GetLiquidDensity() const +{ + return this->liquidDensity; +} + +/* +================ +idPhysics_AF::SetFixedDensityBuoyancy + This will reset the liquid density to the default value depending on the mode. +================ +*/ +void idPhysics_AF::SetFixedDensityBuoyancy( bool fixed ) +{ + this->fixedDensityBuoyancy = fixed; + if( this->fixedDensityBuoyancy ) + this->liquidDensity = DEFAULT_LIQUID_DENSITY; + else + this->liquidDensity = DEFAULT_LIQUID_SCALAR; +} + +/* +================ +idPhysics_AF::GetFixedDensityBuoyancy +================ +*/ +bool idPhysics_AF::GetFixedDensityBuoyancy() const +{ + return this->fixedDensityBuoyancy; +} \ No newline at end of file diff --git a/game/physics/Physics_AF.h b/game/physics/Physics_AF.h index 90b57c9..cf01d73 100644 --- a/game/physics/Physics_AF.h +++ b/game/physics/Physics_AF.h @@ -689,6 +689,7 @@ public: float GetContactFriction( void ) const { return contactFriction; } void SetBouncyness( float bounce ); float GetBouncyness( void ) const { return bouncyness; } + float GetVolume( void ) const { return volume; } void SetDensity( float density, const idMat3 &inertiaScale = mat3_identity ); float GetInverseMass( void ) const { return invMass; } idMat3 GetInverseWorldInertia( void ) const { return current->worldAxis.Transpose() * inverseInertiaTensor * current->worldAxis; } @@ -696,6 +697,11 @@ public: void SetFrictionDirection( const idVec3 &dir ); bool GetFrictionDirection( idVec3 &dir ) const; + // returns the depth of the object in the water + // 0.0f if out of water + float GetWaterLevel() const; + float SetWaterLevel( idPhysics_Liquid *l, const idVec3 &gravityNormal, bool fixedDensityBuoyancy ); + void SetContactMotorDirection( const idVec3 &dir ); bool GetContactMotorDirection( idVec3 &dir ) const; void SetContactMotorVelocity( float vel ) { contactMotorVelocity = vel; } @@ -723,6 +729,7 @@ private: float angularFriction; // rotational friction float contactFriction; // friction with contact surfaces float bouncyness; // bounce + float volume; // volume of body int clipMask; // contents this body collides with idVec3 frictionDir; // specifies a single direction of friction in body space idVec3 contactMotorDir; // contact motor direction @@ -732,6 +739,9 @@ private: // derived properties float mass; // mass of body float invMass; // inverse mass + float liquidMass; // mass of object in a liquid + float invLiquidMass; // inverse liquid mass + float waterLevel; // percent of body in water idVec3 centerOfMass; // center of mass of body idMat3 inertiaTensor; // inertia tensor idMat3 inverseInertiaTensor; // inverse inertia tensor @@ -893,6 +903,14 @@ public: // update the clip model positions void UpdateClipModels( void ); + // buoyancy stuff + void SetLiquidDensity( float density ); + float GetLiquidDensity() const; + + // this will reset liquidDensity so be careful when using it + void SetFixedDensityBuoyancy( bool fixed ); + bool GetFixedDensityBuoyancy() const; + public: // common physics interface void SetClipModel( idClipModel *model, float density, int id = 0, bool freeOld = true ); idClipModel * GetClipModel( int id = 0 ) const; @@ -1016,6 +1034,9 @@ private: // physics state AFPState_t current; AFPState_t saved; + bool fixedDensityBuoyancy; // treats liquid Density as THE density for each body when the AF is in liquid. + // otherwise liquidDensity is just a gravity scalar for the AF in any liquid. + float liquidDensity; // explained above. idAFBody * masterBody; // master body idLCP * lcp; // linear complementarity problem solver diff --git a/game/physics/Physics_Actor.cpp b/game/physics/Physics_Actor.cpp index 85a45a7..c0e2a9e 100644 --- a/game/physics/Physics_Actor.cpp +++ b/game/physics/Physics_Actor.cpp @@ -48,6 +48,9 @@ idPhysics_Actor::idPhysics_Actor( void ) { masterYaw = 0.0f; masterDeltaYaw = 0.0f; groundEntityPtr = NULL; + + waterLevel = WATERLEVEL_NONE; + waterType = 0; } /* @@ -79,6 +82,9 @@ void idPhysics_Actor::Save( idSaveGame *savefile ) const { savefile->WriteFloat( masterYaw ); savefile->WriteFloat( masterDeltaYaw ); + savefile->WriteInt( (int)waterLevel ); + savefile->WriteInt( waterType ); + groundEntityPtr.Save( savefile ); } @@ -99,6 +105,9 @@ void idPhysics_Actor::Restore( idRestoreGame *savefile ) { savefile->ReadFloat( masterYaw ); savefile->ReadFloat( masterDeltaYaw ); + savefile->ReadInt( (int &)waterLevel ); + savefile->ReadInt( waterType ); + groundEntityPtr.Restore( savefile ); } @@ -380,3 +389,71 @@ bool idPhysics_Actor::EvaluateContacts( void ) { return ( contacts.Num() != 0 ); } + + +/* +============= +idPhysics_Actor::SetWaterLevel +============= +*/ +void idPhysics_Actor::SetWaterLevel( void ) { + idVec3 point; + idVec3 origin; + idBounds bounds; + int contents; + + // + // get waterlevel, accounting for ducking + // + waterLevel = WATERLEVEL_NONE; + waterType = 0; + + origin = this->GetOrigin(); + bounds = clipModel->GetBounds(); + + // check at feet level + point = origin - ( bounds[0][2] + 1.0f ) * gravityNormal; + contents = gameLocal.clip.Contents( point, NULL, mat3_identity, -1, self ); + if ( contents & MASK_WATER ) { + // sets water entity + this->SetWaterLevelf(); + + waterType = contents; + waterLevel = WATERLEVEL_FEET; + + // check at waist level + point = origin - ( bounds[1][2] - bounds[0][2] ) * 0.5f * gravityNormal; + contents = gameLocal.clip.Contents( point, NULL, mat3_identity, -1, self ); + if ( contents & MASK_WATER ) { + + waterLevel = WATERLEVEL_WAIST; + + // check at head level + point = origin - ( bounds[1][2] - 1.0f ) * gravityNormal; + contents = gameLocal.clip.Contents( point, NULL, mat3_identity, -1, self ); + if ( contents & MASK_WATER ) { + waterLevel = WATERLEVEL_HEAD; + } + } + } + else + this->SetWater(NULL); +} + +/* +================ +idPhysics_Actor::GetWaterLevel +================ +*/ +waterLevel_t idPhysics_Actor::GetWaterLevel( void ) const { + return waterLevel; +} + +/* +================ +idPhysics_Actor::GetWaterType +================ +*/ +int idPhysics_Actor::GetWaterType( void ) const { + return waterType; +} \ No newline at end of file diff --git a/game/physics/Physics_Actor.h b/game/physics/Physics_Actor.h index e50ae17..f863adc 100644 --- a/game/physics/Physics_Actor.h +++ b/game/physics/Physics_Actor.h @@ -43,6 +43,14 @@ If you have questions concerning this license or the applicable additional terms =================================================================================== */ +typedef enum { + WATERLEVEL_NONE, + WATERLEVEL_FEET, + WATERLEVEL_WAIST, + WATERLEVEL_HEAD +} waterLevel_t; + + class idPhysics_Actor : public idPhysics_Base { public: @@ -61,6 +69,10 @@ public: // align the clip model with the gravity direction void SetClipModelAxis( void ); + // water stuff + virtual waterLevel_t GetWaterLevel( void ) const; + virtual int GetWaterType( void ) const; + public: // common physics interface void SetClipModel( idClipModel *model, float density, int id = 0, bool freeOld = true ); idClipModel * GetClipModel( int id = 0 ) const; @@ -96,6 +108,8 @@ public: // common physics interface bool EvaluateContacts( void ); protected: + virtual void SetWaterLevel( void ); + idClipModel * clipModel; // clip model used for collision detection idMat3 clipModelAxis; // axis of clip model aligned with gravity direction @@ -109,6 +123,8 @@ protected: float masterDeltaYaw; // results of last evaluate + waterLevel_t waterLevel; + int waterType; idEntityPtr groundEntityPtr; }; diff --git a/game/physics/Physics_Base.cpp b/game/physics/Physics_Base.cpp index 41b455b..8d80745 100644 --- a/game/physics/Physics_Base.cpp +++ b/game/physics/Physics_Base.cpp @@ -42,6 +42,7 @@ idPhysics_Base::idPhysics_Base */ idPhysics_Base::idPhysics_Base( void ) { self = NULL; + water = NULL; clipMask = 0; SetGravity( gameLocal.GetGravity() ); ClearContacts(); @@ -836,3 +837,76 @@ idPhysics_Base::ReadFromSnapshot */ void idPhysics_Base::ReadFromSnapshot( const idBitMsgDelta &msg ) { } + + +/* +================ +idPhysics_Base::SetWater +================ +*/ +void idPhysics_Base::SetWater( idPhysics_Liquid *e ) { + this->water = e; +} + +/* +================ +idPhysics_Base::GetWater +================ +*/ +idPhysics_Liquid *idPhysics_Base::GetWater() +{ + return this->water; +} + +/* +================ +idPhysics_Base::SetWaterLevelf + + Returns 1.0f if the object is in a liquid, 0.0f otherwise. + + If the object's not in a liquid it double checks to make sure it's really not. + Normally we only set this->water when an object collides with a water material but + what happens if an object spawns inside a liquid or something? Nothing, it'll just sit + there. This function sets the water level for an object that's already inside the water. + + This was most noticeable when I had monsters walking into the water and of course, they'd + sink to the bottom. After I'd kill them they'd die normally and not float. After adding + this function they float after they're killed. + +================ +*/ +float idPhysics_Base::SetWaterLevelf() { + if( this->water == NULL ) { + idEntity *e[2]; + trace_t result; + idBounds bounds = this->GetBounds(); + + bounds += this->GetOrigin(); + + // trace for a water contact + if( gameLocal.clip.EntitiesTouchingBounds(bounds,MASK_WATER,e,2) ) { + if( e[0]->GetPhysics()->IsType(idPhysics_Liquid::Type) ) { + this->water = static_cast(e[0]->GetPhysics()); + return 1.0f; + } + } + + return 0.0f; + } + else + return 1.0f; +} + +/* +================ +idPhysics_Base::GetWaterLevelf + + The water level for this object, 0.0f if not in the water, 1.0f if in water +================ +*/ +float idPhysics_Base::GetWaterLevelf() const { + if( this->water == NULL ) + return 0.0f; + else + return 1.0f; +} \ No newline at end of file diff --git a/game/physics/Physics_Base.h b/game/physics/Physics_Base.h index 0d73934..4230337 100644 --- a/game/physics/Physics_Base.h +++ b/game/physics/Physics_Base.h @@ -56,6 +56,7 @@ public: public: // common physics interface void SetSelf( idEntity *e ); + inline const idEntity *GetSelf() { return this->self; } void SetClipModel( idClipModel *model, float density, int id = 0, bool freeOld = true ); idClipModel * GetClipModel( int id = 0 ) const; @@ -144,6 +145,12 @@ public: // common physics interface void WriteToSnapshot( idBitMsgDelta &msg ) const; void ReadFromSnapshot( const idBitMsgDelta &msg ); + // water level stuff + idPhysics_Liquid * GetWater(); + void SetWater( idPhysics_Liquid *e ); + float SetWaterLevelf(); + float GetWaterLevelf() const; + protected: idEntity * self; // entity using this physics object int clipMask; // contents the physics object collides with @@ -151,6 +158,7 @@ protected: idVec3 gravityNormal; // normalized direction of gravity idList contacts; // contacts with other physics objects idList contactEntities; // entities touching this physics object + idPhysics_Liquid *water; // the water object the object is in, we use this to check density/viscosity protected: // add ground contacts for the clip model diff --git a/game/physics/Physics_Liquid.cpp b/game/physics/Physics_Liquid.cpp new file mode 100644 index 0000000..c009c79 --- /dev/null +++ b/game/physics/Physics_Liquid.cpp @@ -0,0 +1,194 @@ + +#include "../../idlib/precompiled.h" +#pragma hdrstop + +#include "../Game_local.h" + + +CLASS_DECLARATION( idPhysics_Static, idPhysics_Liquid ) +END_CLASS + +/* +=============================================================================== + + idPhysics_Liquid + +=============================================================================== +*/ + +/* +================ +idPhysics_Liquid::idPhysics_Liquid +================ +*/ +idPhysics_Liquid::idPhysics_Liquid() { + + // initializes to a water-like liquid + this->density = 0.001043f; + this->viscosity = 3.0f; +} + +/* +================ +idPhysics_Liquid::~idPhysics_Liquid +================ +*/ +idPhysics_Liquid::~idPhysics_Liquid() { +} + +/* +================ +idPhysics_Liquid::Save +================ +*/ +void idPhysics_Liquid::Save( idSaveGame *savefile ) const +{ + savefile->WriteFloat(this->density); + savefile->WriteFloat(this->viscosity); + savefile->WriteVec3(this->minSplashVelocity); + savefile->WriteVec3(this->minWaveVelocity); +} + +/* +================ +idPhysics_Liquid::Restore +================ +*/ +void idPhysics_Liquid::Restore( idRestoreGame *savefile ) +{ + savefile->ReadFloat(this->density); + savefile->ReadFloat(this->viscosity); + savefile->ReadVec3(this->minSplashVelocity); + savefile->ReadVec3(this->minWaveVelocity); +} + +/* +================ +idPhysics_Liquid::GetDepth + Gets the depth of a point in the liquid. Returns -1 -1 -1 if the object is not in the liquid +================ +*/ +idVec3 idPhysics_Liquid::GetDepth( const idVec3 &point ) const { + const idBounds &bounds = this->GetBounds(); + idVec3 gravityNormal = this->GetGravityNormal(); + idVec3 depth(-1.0f,-1.0f,-1.0f); + + if( !this->isInLiquid(point) ) + return depth; + depth = (((bounds[1] + this->GetOrigin()) - point) * gravityNormal) * gravityNormal; + + return depth; +} + +/* +================ +idPhysics_Liquid::Splash + Causes the liquid to splash but only if the velocity is greater than minSplashVelocity +================ +*/ +void idPhysics_Liquid::Splash( idEntity *other, float volume, impactInfo_t &info, trace_t &collision ) { + collision.c.entityNum = other->entityNumber; + self->Collide(collision,info.velocity); +} + +/* +================ +idPhysics_Liquid::isInLiquid + Returns true if a point is in the liquid +================ +*/ +bool idPhysics_Liquid::isInLiquid( const idVec3 &point ) const { + bool result; + + result = (gameLocal.clip.Contents(point,NULL,mat3_identity,MASK_WATER,NULL) != 0); + return result; +} + +/* +================ +idPhysics_Liquid::GetPressure + Returns the amount of pressure the liquid applies to the given point +================ +*/ +idVec3 idPhysics_Liquid::GetPressure( const idVec3 &point ) const { + idVec3 pressure; + idVec3 &depth = this->GetDepth(point); + + pressure = depth * this->density; + + return pressure; +} + +/* +================ +idPhysics_Liquid::GetDensity +================ +*/ +float idPhysics_Liquid::GetDensity() const { + return this->density; +} + +/* +================ +idPhysics_Liquid::SetDensity +================ +*/ +void idPhysics_Liquid::SetDensity( float density ) { + if( density > 0.0f ) + this->density = density; +} + +/* +================ +idPhysics_Liquid::GetViscosity +================ +*/ +float idPhysics_Liquid::GetViscosity() const { + return this->viscosity; +} + +/* +================ +idPhysics_Liquid::SetViscosity +================ +*/ +void idPhysics_Liquid::SetViscosity( float viscosity ) { + if( viscosity >= 0.0f ) + this->viscosity = viscosity; +} + +/* +================ +idPhysics_Liquid::GetMinSplashVelocity +================ +*/ +const idVec3 &idPhysics_Liquid::GetMinSplashVelocity() const { + return this->minSplashVelocity; +} + +/* +================ +idPhysics_Liquid::SetMinSplashVelocity +================ +*/ +void idPhysics_Liquid::SetMinSplashVelocity( const idVec3 &m ) { + this->minSplashVelocity = m; +} + +/* +================ +idPhysics_Liquid::GetMinWaveVelocity +================ +*/ +const idVec3 &idPhysics_Liquid::GetMinWaveVelocity() const { + return this->minWaveVelocity; +} + +/* +================ +idPhysics_Liquid::SetMinWaveVelocity +================ +*/ +void idPhysics_Liquid::SetMinWaveVelocity( const idVec3 &w ) { + this->minWaveVelocity = w; +} \ No newline at end of file diff --git a/game/physics/Physics_Liquid.h b/game/physics/Physics_Liquid.h new file mode 100644 index 0000000..9c2b1fc --- /dev/null +++ b/game/physics/Physics_Liquid.h @@ -0,0 +1,60 @@ + +/* +=============================================================================== + + Physics object for a liquid. This class contains physics properties for a + given liquid. Note: Liquid does not necessarily imply water. + + This class does very little functionally as it relies on the other physics + classes to do the bouoyancy calculations. It simply holds information and + allows the other object to deal with that information however they please. + + As a side note, the difference between minSplashVelocity and + minWaveVelocity is that min splash is the minimum amount of velocity + before the liquid spawns a splash particle. minWaveVelocity is to generate + a wave on the surface, not a splash. It should be lower than min splash + velocity. It's the reason some things won't splash but will still cause + ripples in the water (especially when surfacing) +=============================================================================== +*/ + +class idPhysics_Liquid : public idPhysics_Static { +public: + CLASS_PROTOTYPE( idPhysics_Liquid ); + + idPhysics_Liquid( void ); + ~idPhysics_Liquid( void ); + + void Save( idSaveGame *savefile ) const; + void Restore( idRestoreGame *savefile ); + +public: + // Creates a splash on the liquid + virtual void Splash( idEntity *other, float volume, impactInfo_t &info, trace_t &collision ); + + // Derived information + virtual bool isInLiquid( const idVec3 &point ) const; + virtual idVec3 GetDepth( const idVec3 &point ) const; + virtual idVec3 GetPressure( const idVec3 &point ) const; + + // Physical properties + virtual float GetDensity() const; + virtual void SetDensity( float density ); + + virtual float GetViscosity() const; + virtual void SetViscosity( float viscosity ); + + virtual const idVec3 &GetMinSplashVelocity() const; + virtual void SetMinSplashVelocity( const idVec3 &m ); + + virtual const idVec3 &GetMinWaveVelocity() const; + virtual void SetMinWaveVelocity( const idVec3 &w ); + +private: + // STATE + float density; + float viscosity; + + idVec3 minWaveVelocity; + idVec3 minSplashVelocity; +}; \ No newline at end of file diff --git a/game/physics/Physics_Monster.cpp b/game/physics/Physics_Monster.cpp index 8b74d3a..328ec65 100644 --- a/game/physics/Physics_Monster.cpp +++ b/game/physics/Physics_Monster.cpp @@ -451,6 +451,9 @@ bool idPhysics_Monster::Evaluate( int timeStepMSec, int endTimeMSec ) { idMat3 masterAxis; float timeStep; + waterLevel = WATERLEVEL_NONE; + waterType = 0; + timeStep = MS2SEC( timeStepMSec ); moveResult = MM_OK; @@ -481,6 +484,9 @@ bool idPhysics_Monster::Evaluate( int timeStepMSec, int endTimeMSec ) { clipModel->Unlink(); + // check water level / type + idPhysics_Monster::SetWaterLevel(); + // check if on the ground idPhysics_Monster::CheckGround( current ); diff --git a/game/physics/Physics_Player.cpp b/game/physics/Physics_Player.cpp index 82ac812..96cab4e 100644 --- a/game/physics/Physics_Player.cpp +++ b/game/physics/Physics_Player.cpp @@ -1216,6 +1216,8 @@ bool idPhysics_Player::CheckWaterJump( void ) { idVec3 spot; int cont; idVec3 flatforward; + const idBounds &bounds = this->GetBounds(); + idVec3 offset = ( ((bounds[0] + bounds[1]) * 0.5f) * gravityNormal) * gravityNormal; if ( current.movementTime ) { return false; @@ -1229,25 +1231,80 @@ bool idPhysics_Player::CheckWaterJump( void ) { flatforward = viewForward - (viewForward * gravityNormal) * gravityNormal; flatforward.Normalize(); - spot = current.origin + 30.0f * flatforward; - spot -= 4.0f * gravityNormal; + spot = current.origin + ((bounds[1].x + 1.0f) * flatforward); + spot += offset; cont = gameLocal.clip.Contents( spot, NULL, mat3_identity, -1, self ); if ( !(cont & CONTENTS_SOLID) ) { return false; } - spot -= 16.0f * gravityNormal; + spot += 0.75f * offset; cont = gameLocal.clip.Contents( spot, NULL, mat3_identity, -1, self ); if ( cont ) { return false; } // jump out of water - current.velocity = 200.0f * viewForward - 350.0f * gravityNormal; + current.velocity = (300.0f * viewForward) - (300.0f * gravityNormal); current.movementFlags |= PMF_TIME_WATERJUMP; current.movementTime = 2000; return true; +/* idVec3 spot; + int cont; + idVec3 flatforward; + // inolen + idBounds bounds; + + if ( current.movementTime ) { + return false; + } + + // check for water jump + if ( waterLevel != WATERLEVEL_WAIST ) { + return false; + } + + // inolen + bounds = clipModel->GetBounds(); + + flatforward = viewForward - (viewForward * gravityNormal) * gravityNormal; + flatforward.Normalize(); + + // inolen + spot = current.origin + (bounds[1][0]+1.0f) * flatforward; + spot -= 4.0f * gravityNormal; + + // debugging + //gameRenderWorld->DebugBox( colorGreen, idBox( idBounds( idVec3( -2.0f, -2.0f, -2.0f ), idVec3( 2.0f, 2.0f, 2.0f ) ), spot, mat3_identity ) ); + + cont = gameLocal.clip.Contents( spot, NULL, mat3_identity, -1, self ); + + if ( !(cont & CONTENTS_SOLID) ) { + return false; + } + + // inolen + spot -= (bounds[1][2] + 4.0f) * gravityNormal; + cont = gameLocal.clip.Contents( spot, NULL, mat3_identity, -1, self ); + + // debugging + //gameRenderWorld->DebugArrow( colorRed, current.origin, spot, 2 ); + + if( cont ) { + return false; + } + + // jump out of water + // inolen + current.velocity = ((flatforward + -gravityNormal)/2.0f); + current.velocity.Normalize(); + current.velocity *= 650.0f; + + current.movementFlags |= PMF_TIME_WATERJUMP; + current.movementTime = 2000; + + return true;*/ } /* @@ -1255,43 +1312,6 @@ bool idPhysics_Player::CheckWaterJump( void ) { idPhysics_Player::SetWaterLevel ============= */ -void idPhysics_Player::SetWaterLevel( void ) { - idVec3 point; - idBounds bounds; - int contents; - - // - // get waterlevel, accounting for ducking - // - waterLevel = WATERLEVEL_NONE; - waterType = 0; - - bounds = clipModel->GetBounds(); - - // check at feet level - point = current.origin - ( bounds[0][2] + 1.0f ) * gravityNormal; - contents = gameLocal.clip.Contents( point, NULL, mat3_identity, -1, self ); - if ( contents & MASK_WATER ) { - - waterType = contents; - waterLevel = WATERLEVEL_FEET; - - // check at waist level - point = current.origin - ( bounds[1][2] - bounds[0][2] ) * 0.5f * gravityNormal; - contents = gameLocal.clip.Contents( point, NULL, mat3_identity, -1, self ); - if ( contents & MASK_WATER ) { - - waterLevel = WATERLEVEL_WAIST; - - // check at head level - point = current.origin - ( bounds[1][2] - 1.0f ) * gravityNormal; - contents = gameLocal.clip.Contents( point, NULL, mat3_identity, -1, self ); - if ( contents & MASK_WATER ) { - waterLevel = WATERLEVEL_HEAD; - } - } - } -} /* ================ @@ -1432,18 +1452,12 @@ void idPhysics_Player::MovePlayer( int msec ) { idPhysics_Player::GetWaterLevel ================ */ -waterLevel_t idPhysics_Player::GetWaterLevel( void ) const { - return waterLevel; -} /* ================ idPhysics_Player::GetWaterType ================ */ -int idPhysics_Player::GetWaterType( void ) const { - return waterType; -} /* ================ @@ -1518,8 +1532,6 @@ idPhysics_Player::idPhysics_Player( void ) { groundMaterial = NULL; ladder = false; ladderNormal.Zero(); - waterLevel = WATERLEVEL_NONE; - waterType = 0; } /* @@ -1586,9 +1598,6 @@ void idPhysics_Player::Save( idSaveGame *savefile ) const { savefile->WriteBool( ladder ); savefile->WriteVec3( ladderNormal ); - - savefile->WriteInt( (int)waterLevel ); - savefile->WriteInt( waterType ); } /* @@ -1624,9 +1633,6 @@ void idPhysics_Player::Restore( idRestoreGame *savefile ) { savefile->ReadBool( ladder ); savefile->ReadVec3( ladderNormal ); - savefile->ReadInt( (int &)waterLevel ); - savefile->ReadInt( waterType ); - /* DG: It can apparently happen that the player saves while the clipModel's axis are * modified by idPush::TryRotatePushEntity() -> idPhysics_Player::Rotate() -> idClipModel::Link() * Normally idPush seems to reset them to the identity matrix in the next frame, diff --git a/game/physics/Physics_Player.h b/game/physics/Physics_Player.h index 40d52e7..51f3a04 100644 --- a/game/physics/Physics_Player.h +++ b/game/physics/Physics_Player.h @@ -51,13 +51,6 @@ typedef enum { PM_NOCLIP // flying without collision detection nor gravity } pmtype_t; -typedef enum { - WATERLEVEL_NONE, - WATERLEVEL_FEET, - WATERLEVEL_WAIST, - WATERLEVEL_HEAD -} waterLevel_t; - #define MAXTOUCH 32 typedef struct playerPState_s { @@ -91,8 +84,6 @@ public: void SetKnockBack( const int knockBackTime ); void SetDebugLevel( bool set ); // feed back from last physics frame - waterLevel_t GetWaterLevel( void ) const; - int GetWaterType( void ) const; bool HasJumped( void ) const; bool HasSteppedUp( void ) const; float GetStepUp( void ) const; @@ -165,10 +156,6 @@ private: bool ladder; idVec3 ladderNormal; - // results of last evaluate - waterLevel_t waterLevel; - int waterType; - private: float CmdScale( const usercmd_t &cmd ) const; void Accelerate( const idVec3 &wishdir, const float wishspeed, const float accel ); @@ -189,7 +176,6 @@ private: void CheckLadder( void ); bool CheckJump( void ); bool CheckWaterJump( void ); - void SetWaterLevel( void ); void DropTimers( void ); void MovePlayer( int msec ); }; diff --git a/game/physics/Physics_RigidBody.cpp b/game/physics/Physics_RigidBody.cpp index 8380656..5ec8191 100644 --- a/game/physics/Physics_RigidBody.cpp +++ b/game/physics/Physics_RigidBody.cpp @@ -38,8 +38,13 @@ If you have questions concerning this license or the applicable additional terms CLASS_DECLARATION( idPhysics_Base, idPhysics_RigidBody ) END_CLASS -const float STOP_SPEED = 10.0f; +const int STOP_SPEED = 10.0f; +// if linearVelocity < WATER_STOP_LINEAR && angularVelocity < WATER_STOP_ANGULAR then set the RB to rest +// and we need this->noMoveTime + NO_MOVE_TIME < gameLocal.getTime() +const idVec3 WATER_STOP_LINEAR(10.0f,10.0f,10.0f); +const idVec3 WATER_STOP_ANGULAR(500000.0f,500000.0f,500000.0f); +const int NO_MOVE_TIME = 200; #undef RB_TIMINGS @@ -73,10 +78,128 @@ void RigidBodyDerivatives( const float t, const void *clientData, const float *s // derivatives d->linearVelocity = p->inverseMass * s->linearMomentum; d->angularMatrix = SkewSymmetric( angularVelocity ) * s->orientation; - d->force = - p->linearFriction * s->linearMomentum + p->current.externalForce; - d->torque = - p->angularFriction * s->angularMomentum + p->current.externalTorque; + + // underwater we have a higher friction + if( p->GetWaterLevelf() == 0.0f ) { + + d->force = - p->linearFriction * s->linearMomentum + p->current.externalForce; + d->torque = - p->angularFriction * s->angularMomentum + p->current.externalTorque; + } + else { + // don't let water friction go less than 25% of the water viscosity + float percent = Max(0.25f,p->GetSubmergedPercent(s->position,s->orientation.Transpose())); + + d->force = (-p->linearFriction * p->water->GetViscosity() * percent) * s->linearMomentum + p->current.externalForce; + d->torque = (-p->angularFriction * p->water->GetViscosity()) * s->angularMomentum + p->current.externalTorque; + } } + +/* +================ +idPhysics_RigidBody::GetSubmergedPercent + + Approximates the percentage of the body that is submerged +================ +*/ +float idPhysics_RigidBody::GetSubmergedPercent( const idVec3 &pos, const idMat3 &rotation ) const +{ + idVec3 depth,bottom(pos); + idBounds bounds = this->GetBounds(); + float height,d; + + if( this->water == NULL ) + return 0.0f; + + // offset and rotate the bounding box + bounds += -centerOfMass; + bounds *= rotation; + + // gets the position of the object relative to the surface of the water + height = abs(bounds[1] * gravityNormal * 2); + + // calculates the depth of the bottom of the object + bottom += (height * 0.5f) * gravityNormal; + depth = this->water->GetDepth(bottom); + d = abs(depth * gravityNormal); + + if( d > height ) { + // the body is totally submerged + return 1.0f; + } + else if( d <= 0 ) { + return 0.0f; + } + else { + // the body is partly submerged + return d / height; + } +} + +/* +================ +idPhysics_RigidBody::GetBuoyancy + + Gets buoyancy information for this RB +================ +*/ +bool idPhysics_RigidBody::GetBuoyancy( const idVec3 &pos, const idMat3 &rotation, idVec3 &bCenter, float &percent ) const +{ + // pos - position of the RB + // rotation - axis for the RB + // bCenter - after the function is called this is an approximation for the center of buoyancy + // percent - rough percentage of the body that is under water + // used to calculate the volume of the submersed object (volume * percent) to give + // the body somewhat realistic bobbing. + // + // return true if the body is in water, false otherwise + + idVec3 tbCenter(pos); + idBounds bounds = this->GetBounds(); + idTraceModel tm = *this->GetClipModel()->GetTraceModel(); + int i,count; + + percent = this->GetSubmergedPercent(pos,rotation); + bCenter = pos; + + if( percent == 1.0f ) { + // the body is totally submerged + return true; + } + else { + // the body is partly submerged (or not in the water) + // + // We do a rough approximation for center of buoyancy. + // Normally this is done by calculating the volume of the submersed part of the body + // so the center of buoyancy is the center of mass of the submersed volume. This is + // probably a slow computation so what I do is take an average of the submersed + // vertices of the trace model. + // + // I was suprized when this first worked but you can use rb_showBuoyancy to see + // what the approximation looks like. + + // set up clip model for approximation of center of buoyancy + tm.Translate(-centerOfMass); + tm.Rotate(rotation); + tm.Translate(pos); + + // calculate which vertices are under water + for( i = 0, count = 1; i < tm.numVerts; i++ ) { + if( gameLocal.clip.Contents(tm.verts[i],NULL,this->GetAxis(),MASK_WATER,NULL) ) { + tbCenter += tm.verts[i]; + count += 1; + } + } + + if( count == 1 ) + bCenter = pos; + else + bCenter = tbCenter / count; + return (count != 1); + } +} + + /* ================ idPhysics_RigidBody::Integrate @@ -95,8 +218,39 @@ void idPhysics_RigidBody::Integrate( float deltaTime, rigidBodyPState_t &next ) integrator->Evaluate( (float *) ¤t.i, (float *) &next.i, 0, deltaTime ); next.i.orientation.OrthoNormalizeSelf(); - // apply gravity - next.i.linearMomentum += deltaTime * gravityVector * mass; + // apply a water gravity if the body is out of water + if( this->SetWaterLevelf() != 0.0f ) { + idVec3 bCenter; + idVec3 bForce(gravityVector),rForce(-gravityVector); + float bMass,fraction,liquidMass; + bool inWater; + + inWater = this->GetBuoyancy(next.i.position,next.i.orientation.Transpose(),bCenter,fraction); + + // calculate water mass + liquidMass = this->volume * this->water->GetDensity() * fraction; + // don't let liquid mass get too high + liquidMass = Min( liquidMass, 3 * this->mass ); + + bMass = this->mass - liquidMass; + + // calculate buoyancy force + bForce *= deltaTime * bMass; + rForce *= deltaTime * liquidMass; + + // apply water force + // basically here we do a ::ApplyImpulse() but we apply it to next, not current + next.i.linearMomentum += bForce; + next.i.angularMomentum += (bCenter - next.i.position).Cross(rForce); + + // take the body out of water if it's not in water. + if( !inWater ) + this->SetWater(NULL); + } + else { + // apply normal gravity + next.i.linearMomentum += deltaTime * gravityVector * mass; + } current.i.orientation.TransposeSelf(); next.i.orientation.TransposeSelf(); @@ -177,6 +331,8 @@ bool idPhysics_RigidBody::CheckForCollisions( const float deltaTime, rigidBodyPS //#define TEST_COLLISION_DETECTION idMat3 axis; idRotation rotation; + idVec3 pos; + trace_t waterCollision; bool collided = false; #ifdef TEST_COLLISION_DETECTION @@ -189,9 +345,10 @@ bool idPhysics_RigidBody::CheckForCollisions( const float deltaTime, rigidBodyPS TransposeMultiply( current.i.orientation, next.i.orientation, axis ); rotation = axis.ToRotation(); rotation.SetOrigin( current.i.position ); + pos = next.i.position; // if there was a collision - if ( gameLocal.clip.Motion( collision, current.i.position, next.i.position, rotation, clipModel, current.i.orientation, clipMask, self ) ) { + if ( collided || gameLocal.clip.Motion( collision, current.i.position, next.i.position, rotation, clipModel, current.i.orientation, clipMask, self ) ) { // set the next state to the state at the moment of impact next.i.position = collision.endpos; next.i.orientation = collision.endAxis; @@ -200,6 +357,43 @@ bool idPhysics_RigidBody::CheckForCollisions( const float deltaTime, rigidBodyPS collided = true; } + // Check for water collision + // ideally we could do this check in one step but if a body moves quickly in shallow water + // they will occasionally clip through a solid entity (ie. fall through the floor) + if ( gameLocal.clip.Motion( waterCollision, current.i.position, pos, rotation, clipModel, current.i.orientation, MASK_WATER, self ) ) { + idEntity *ent = gameLocal.entities[waterCollision.c.entityNum]; + + // make sure the object didn't collide with something before hitting the water (we don't splash for that case) + if( !collided || waterCollision.fraction < collision.fraction ) { + + // if the object collides with something with a physics_liquid + if( ent->GetPhysics()->IsType( idPhysics_Liquid::Type ) ) { + idPhysics_Liquid *liquid = static_cast(ent->GetPhysics()); + impactInfo_t info; + + self->GetImpactInfo(ent,waterCollision.c.id,waterCollision.c.point,&info); + + // apply water splash friction + if( this->water == NULL ) { + idVec3 impulse = -info.velocity * this->volume * liquid->GetDensity() * 0.25f; + impulse = (impulse * gravityNormal) * gravityNormal; + + if( next.i.linearMomentum.LengthSqr() < impulse.LengthSqr() ) { + // cancel falling, maintain sideways movement (lateral?) + next.i.linearMomentum -= (next.i.linearMomentum * gravityNormal) * gravityNormal; + } + else { + next.i.angularMomentum += ( waterCollision.c.point - ( next.i.position + centerOfMass * next.i.orientation ) ).Cross( impulse ); + next.i.linearMomentum += impulse * 0.5f; + } + } + + this->SetWater(liquid); + this->water->Splash(this->self,this->volume,info,waterCollision); + } + } + } + #ifdef TEST_COLLISION_DETECTION if ( gameLocal.clip.Contents( next.i.position, clipModel, next.i.orientation, clipMask, self ) ) { if ( !startsolid ) { @@ -275,7 +469,7 @@ idPhysics_RigidBody::TestIfAtRest Does not catch all cases where the body is at rest but is generally good enough. ================ */ -bool idPhysics_RigidBody::TestIfAtRest( void ) const { +bool idPhysics_RigidBody::TestIfAtRest( void ) { int i; float gv; idVec3 v, av, normal, point; @@ -286,6 +480,25 @@ bool idPhysics_RigidBody::TestIfAtRest( void ) const { return true; } + // do some special checks if the body is in water + if( this->water != NULL ) + { + if( this->current.i.linearMomentum.LengthSqr() < WATER_STOP_LINEAR.LengthSqr() && + this->current.i.angularMomentum.LengthSqr() < WATER_STOP_ANGULAR.LengthSqr() ) { + + if( this->noMoveTime == 0 ) { + this->noMoveTime = gameLocal.GetTime(); + } + else if( this->noMoveTime+NO_MOVE_TIME < gameLocal.GetTime() ) { + this->noMoveTime = 0; + return true; + } + } + else { + this->noMoveTime = 0; + } + } + // need at least 3 contact points to come to rest if ( contacts.Num() < 3 ) { return false; @@ -414,7 +627,19 @@ void idPhysics_RigidBody::DebugDraw( void ) { } if ( rb_showMass.GetBool() ) { - gameRenderWorld->DrawText( va( "\n%1.2f", mass ), current.i.position, 0.08f, colorCyan, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1 ); + if( this->water != NULL ) { + idVec3 pos; + float percent, liquidMass; + + pos = this->current.i.position + this->centerOfMass*this->current.i.orientation; + percent = this->GetSubmergedPercent(pos,this->current.i.orientation.Transpose()); + + liquidMass = this->mass - ( this->volume * this->water->GetDensity() * percent ); + + gameRenderWorld->DrawText( va( "\n%1.2f", liquidMass), current.i.position, 0.08f, colorCyan, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1 ); + } + else + gameRenderWorld->DrawText( va( "\n%1.2f", mass ), current.i.position, 0.08f, colorCyan, gameLocal.GetLocalPlayer()->viewAngles.ToMat3(), 1 ); } if ( rb_showInertia.GetBool() ) { @@ -429,6 +654,18 @@ void idPhysics_RigidBody::DebugDraw( void ) { if ( rb_showVelocity.GetBool() ) { DrawVelocity( clipModel->GetId(), 0.1f, 4.0f ); } + + if( rb_showBuoyancy.GetBool() && this->water != NULL ) { + idVec3 pos; + idVec3 bCenter; + float percent; + + pos = this->current.i.position + this->centerOfMass*this->current.i.orientation; + this->GetBuoyancy(pos,this->current.i.orientation.Transpose(),bCenter,percent); + + gameRenderWorld->DebugArrow(colorGreen,pos,bCenter,1); + gameRenderWorld->DrawText( va( "%1.2f",percent), pos, 0.08f, colorCyan, gameLocal.GetLocalPlayer()->viewAngles.ToMat3()); + } } /* @@ -463,6 +700,8 @@ idPhysics_RigidBody::idPhysics_RigidBody( void ) { inertiaTensor.Identity(); inverseInertiaTensor.Identity(); + this->water = NULL; + // use the least expensive euler integrator integrator = new idODE_Euler( sizeof(rigidBodyIState_t) / sizeof(float), RigidBodyDerivatives, this ); @@ -473,6 +712,8 @@ idPhysics_RigidBody::idPhysics_RigidBody( void ) { hasMaster = false; isOrientated = false; + this->noMoveTime = 0.0f; + #ifdef RB_TIMINGS lastTimerReset = 0; #endif @@ -548,6 +789,7 @@ void idPhysics_RigidBody::Save( idSaveGame *savefile ) const { savefile->WriteClipModel( clipModel ); savefile->WriteFloat( mass ); + savefile->WriteFloat( volume ); savefile->WriteFloat( inverseMass ); savefile->WriteVec3( centerOfMass ); savefile->WriteMat3( inertiaTensor ); @@ -579,6 +821,7 @@ void idPhysics_RigidBody::Restore( idRestoreGame *savefile ) { savefile->ReadClipModel( clipModel ); savefile->ReadFloat( mass ); + savefile->ReadFloat( volume ); savefile->ReadFloat( inverseMass ); savefile->ReadVec3( centerOfMass ); savefile->ReadMat3( inertiaTensor ); @@ -627,6 +870,8 @@ void idPhysics_RigidBody::SetClipModel( idClipModel *model, const float density, inertiaTensor.Identity(); } + this->volume = mass / density; + // check whether or not the inertia tensor is balanced minIndex = Min3Index( inertiaTensor[0][0], inertiaTensor[1][1], inertiaTensor[2][2] ); inertiaScale.Identity(); @@ -687,7 +932,18 @@ idPhysics_RigidBody::GetMass ================ */ float idPhysics_RigidBody::GetMass( int id ) const { - return mass; + if( this->water != NULL ) { + idVec3 pos; + float percent,bMass; + + pos = this->current.i.position + this->centerOfMass*this->current.i.orientation; + percent = this->GetSubmergedPercent(pos,this->current.i.orientation); + bMass = mass - (this->volume * this->water->GetDensity() * percent); + + return bMass; + } + else + return mass; } /* diff --git a/game/physics/Physics_RigidBody.h b/game/physics/Physics_RigidBody.h index d211ebe..39313b8 100644 --- a/game/physics/Physics_RigidBody.h +++ b/game/physics/Physics_RigidBody.h @@ -166,6 +166,7 @@ private: float angularFriction; // rotational friction float contactFriction; // friction with contact surfaces float bouncyness; // bouncyness + float volume; // object volume idClipModel * clipModel; // clip model used for collision detection // derived properties @@ -185,6 +186,9 @@ private: bool hasMaster; bool isOrientated; + // buoyancy + int noMoveTime; // suspend simulation if hardly any movement for this many seconds + private: friend void RigidBodyDerivatives( const float t, const void *clientData, const float *state, float *derivatives ); void Integrate( const float deltaTime, rigidBodyPState_t &next ); @@ -192,9 +196,15 @@ private: bool CollisionImpulse( const trace_t &collision, idVec3 &impulse ); void ContactFriction( float deltaTime ); void DropToFloorAndRest( void ); - bool TestIfAtRest( void ) const; + bool TestIfAtRest( void ); void Rest( void ); void DebugDraw( void ); + + // Buoyancy stuff + // Approximates the center of mass of the submerged portion of the rigid body. + virtual bool GetBuoyancy( const idVec3 &pos, const idMat3 &rotation, idVec3 &bCenter, float &percent ) const; + // Returns rough a percentage of which percent of the body is in water. + virtual float GetSubmergedPercent( const idVec3 &pos, const idMat3 &rotation ) const; }; #endif /* !__PHYSICS_RIGIDBODY_H__ */ diff --git a/game/script/Script_Thread.cpp b/game/script/Script_Thread.cpp index 3aa8054..ef84fc1 100644 --- a/game/script/Script_Thread.cpp +++ b/game/script/Script_Thread.cpp @@ -102,6 +102,55 @@ const idEventDef EV_Thread_StrRight( "strRight", "sd", 's' ); const idEventDef EV_Thread_StrSkip( "strSkip", "sd", 's' ); const idEventDef EV_Thread_StrMid( "strMid", "sdd", 's' ); const idEventDef EV_Thread_StrToFloat( "strToFloat", "s", 'f' ); + +//###// by MacX + +const idEventDef EV_Thread_GetMoney( "getMoney", NULL, 'f' ); +const idEventDef EV_Thread_DecreaseMoney( "decreaseMoney", "f" ); +const idEventDef EV_Thread_LooseMoney( "looseMoney", NULL ); +const idEventDef EV_Thread_SetPlayerHealth( "setPlayerHealth", NULL ); +const idEventDef EV_Thread_DecreasePlayerHealth( "decreasePlayerHealth", "f" ); +const idEventDef EV_Thread_PreviousPageDiary( "previousPageDiary", NULL ); +const idEventDef EV_Thread_NextPageDiary( "nextPageDiary", NULL ); +const idEventDef EV_Thread_PreviousPageQuestlog( "previousPageQuestlog", NULL ); +const idEventDef EV_Thread_NextPageQuestlog( "nextPageQuestlog", NULL ); + +const idEventDef EV_Thread_GetEnemiesKilled( "getEnemiesKilled", NULL, 'f' ); + +const idEventDef EV_Thread_GetTestVarGui1( "getTestVarGui1", NULL, 'f' ); +const idEventDef EV_Thread_GetTestVarGui2( "getTestVarGui2", NULL, 'f' ); +const idEventDef EV_Thread_GetTestVarGui3( "getTestVarGui3", NULL, 'f' ); +const idEventDef EV_Thread_GetTestVarGui4( "getTestVarGui4", NULL, 'f' ); +const idEventDef EV_Thread_GetTestVarGui5( "getTestVarGui5", NULL, 'f' ); +const idEventDef EV_Thread_GetTestVar1( "getTestVar1", NULL, 'f' ); +const idEventDef EV_Thread_GetTestVar2( "getTestVar2", NULL, 'f' ); +const idEventDef EV_Thread_GetTestVar3( "getTestVar3", NULL, 'f' ); +const idEventDef EV_Thread_GetTestVar4( "getTestVar4", NULL, 'f' ); +const idEventDef EV_Thread_GetTestVar5( "getTestVar5", NULL, 'f' ); +const idEventDef EV_Thread_GetTestVar6( "getTestVar6", NULL, 'f' ); +const idEventDef EV_Thread_GetTestVar7( "getTestVar7", NULL, 'f' ); +const idEventDef EV_Thread_GetTestVar8( "getTestVar8", NULL, 'f' ); +const idEventDef EV_Thread_GetTestVar9( "getTestVar9", NULL, 'f' ); +const idEventDef EV_Thread_GetTestVar10( "getTestVar10", NULL, 'f' ); + +const idEventDef EV_Thread_SetTestVarGui1( "setTestVarGui1", "f" ); +const idEventDef EV_Thread_SetTestVarGui2( "setTestVarGui2", "f" ); +const idEventDef EV_Thread_SetTestVarGui3( "setTestVarGui3", "f" ); +const idEventDef EV_Thread_SetTestVarGui4( "setTestVarGui4", "f" ); +const idEventDef EV_Thread_SetTestVarGui5( "setTestVarGui5", "f" ); +const idEventDef EV_Thread_SetTestVar1( "setTestVar1", "f" ); +const idEventDef EV_Thread_SetTestVar2( "setTestVar2", "f" ); +const idEventDef EV_Thread_SetTestVar3( "setTestVar3", "f" ); +const idEventDef EV_Thread_SetTestVar4( "setTestVar4", "f" ); +const idEventDef EV_Thread_SetTestVar5( "setTestVar5", "f" ); +const idEventDef EV_Thread_SetTestVar6( "setTestVar6", "f" ); +const idEventDef EV_Thread_SetTestVar7( "setTestVar7", "f" ); +const idEventDef EV_Thread_SetTestVar8( "setTestVar8", "f" ); +const idEventDef EV_Thread_SetTestVar9( "setTestVar9", "f" ); +const idEventDef EV_Thread_SetTestVar10( "setTestVar10", "f" ); + +//###// + const idEventDef EV_Thread_RadiusDamage( "radiusDamage", "vEEEsf" ); const idEventDef EV_Thread_IsClient( "isClient", NULL, 'f' ); const idEventDef EV_Thread_IsMultiplayer( "isMultiplayer", NULL, 'f' ); @@ -181,6 +230,55 @@ CLASS_DECLARATION( idClass, idThread ) EVENT( EV_Thread_StrSkip, idThread::Event_StrSkip ) EVENT( EV_Thread_StrMid, idThread::Event_StrMid ) EVENT( EV_Thread_StrToFloat, idThread::Event_StrToFloat ) + +//###// by MacX + + EVENT( EV_Thread_GetMoney, idThread::Event_GetMoney ) + EVENT( EV_Thread_DecreaseMoney, idThread::Event_DecreaseMoney ) + EVENT( EV_Thread_LooseMoney, idThread::Event_LooseMoney ) + EVENT( EV_Thread_SetPlayerHealth, idThread::Event_SetPlayerHealth ) + EVENT( EV_Thread_DecreasePlayerHealth, idThread::Event_DecreasePlayerHealth ) + EVENT( EV_Thread_PreviousPageDiary, idThread::Event_PreviousPageDiary ) + EVENT( EV_Thread_NextPageDiary, idThread::Event_NextPageDiary ) + EVENT( EV_Thread_PreviousPageQuestlog, idThread::Event_PreviousPageQuestlog ) + EVENT( EV_Thread_NextPageQuestlog, idThread::Event_NextPageQuestlog ) + + EVENT( EV_Thread_GetEnemiesKilled, idThread::Event_GetEnemiesKilled ) + + EVENT( EV_Thread_GetTestVarGui1, idThread::Event_GetTestVarGui1 ) + EVENT( EV_Thread_GetTestVarGui2, idThread::Event_GetTestVarGui2 ) + EVENT( EV_Thread_GetTestVarGui3, idThread::Event_GetTestVarGui3 ) + EVENT( EV_Thread_GetTestVarGui4, idThread::Event_GetTestVarGui4 ) + EVENT( EV_Thread_GetTestVarGui5, idThread::Event_GetTestVarGui5 ) + EVENT( EV_Thread_GetTestVar1, idThread::Event_GetTestVar1 ) + EVENT( EV_Thread_GetTestVar2, idThread::Event_GetTestVar2 ) + EVENT( EV_Thread_GetTestVar3, idThread::Event_GetTestVar3 ) + EVENT( EV_Thread_GetTestVar4, idThread::Event_GetTestVar4 ) + EVENT( EV_Thread_GetTestVar5, idThread::Event_GetTestVar5 ) + EVENT( EV_Thread_GetTestVar6, idThread::Event_GetTestVar6 ) + EVENT( EV_Thread_GetTestVar7, idThread::Event_GetTestVar7 ) + EVENT( EV_Thread_GetTestVar8, idThread::Event_GetTestVar8 ) + EVENT( EV_Thread_GetTestVar9, idThread::Event_GetTestVar9 ) + EVENT( EV_Thread_GetTestVar10, idThread::Event_GetTestVar10 ) + + EVENT( EV_Thread_SetTestVarGui1, idThread::Event_SetTestVarGui1 ) + EVENT( EV_Thread_SetTestVarGui2, idThread::Event_SetTestVarGui2 ) + EVENT( EV_Thread_SetTestVarGui3, idThread::Event_SetTestVarGui3 ) + EVENT( EV_Thread_SetTestVarGui4, idThread::Event_SetTestVarGui4 ) + EVENT( EV_Thread_SetTestVarGui5, idThread::Event_SetTestVarGui5 ) + EVENT( EV_Thread_SetTestVar1, idThread::Event_SetTestVar1 ) + EVENT( EV_Thread_SetTestVar2, idThread::Event_SetTestVar2 ) + EVENT( EV_Thread_SetTestVar3, idThread::Event_SetTestVar3 ) + EVENT( EV_Thread_SetTestVar4, idThread::Event_SetTestVar4 ) + EVENT( EV_Thread_SetTestVar5, idThread::Event_SetTestVar5 ) + EVENT( EV_Thread_SetTestVar6, idThread::Event_SetTestVar6 ) + EVENT( EV_Thread_SetTestVar7, idThread::Event_SetTestVar7 ) + EVENT( EV_Thread_SetTestVar8, idThread::Event_SetTestVar8 ) + EVENT( EV_Thread_SetTestVar9, idThread::Event_SetTestVar9 ) + EVENT( EV_Thread_SetTestVar10, idThread::Event_SetTestVar10 ) + +//###// + EVENT( EV_Thread_RadiusDamage, idThread::Event_RadiusDamage ) EVENT( EV_Thread_IsClient, idThread::Event_IsClient ) EVENT( EV_Thread_IsMultiplayer, idThread::Event_IsMultiplayer ) @@ -1727,6 +1825,786 @@ void idThread::Event_StrToFloat( const char *string ) { idThread::ReturnFloat( result ); } + +//###// by MacX + +/* +================ +idThread::Event_GetMoney( void ) +================ +*/ +void idThread::Event_GetMoney( void ) { + + idPlayer* player = gameLocal.GetLocalPlayer(); + + idThread::ReturnFloat( player->inventory.money ); +} + +/* +================ +idThread::Event_DecreaseMoney( float value ) +================ +*/ +void idThread::Event_DecreaseMoney( float value ) { + + idPlayer* player = gameLocal.GetLocalPlayer(); + + if( player->inventory.money > 0 ) { + player->inventory.money -= value; + if( player->inventory.money < 0 ) { + player->inventory.money = 0; + } + } +} + +/* +================ +idThread::Event_LooseMoney() +================ +*/ +void idThread::Event_LooseMoney( void ) { + + idPlayer* player = gameLocal.GetLocalPlayer(); + + player->inventory.money = 0; +} + +/* +================ +idThread::Event_SetPlayerHealth() +================ +*/ +void idThread::Event_SetPlayerHealth( void ) { + + idPlayer* player = gameLocal.GetLocalPlayer(); + player->hud->HandleNamedEvent( "hideHealth" ); + + int newPlayerHealth; + + if ( g_skill.GetInteger() == 0 ) { + newPlayerHealth = 4; + } else if ( g_skill.GetInteger() == 1 ) { + newPlayerHealth = 2; + } else { + newPlayerHealth = 1; + } + + if( !(player->health > player->inventory.maxHealth) && + !( player->health <= 0 ) ) { + + player->health += newPlayerHealth; + + if( player->health > player->inventory.maxHealth ) { + player->health = player->inventory.maxHealth; + } + } + if( player->inventory.armor > 0 ) { + player->inventory.armor -= 1; + } +} + +/* +================ +idThread::Event_DecreasePlayerHealth() +================ +*/ +void idThread::Event_DecreasePlayerHealth( float hp ) { + + idPlayer* player = gameLocal.GetLocalPlayer(); + + if( player->health > 0 ) { + player->health -= hp; + + if( player->health <= 0 ) { + player->health = 0; + player->Damage( player, player, vec3_origin, "damage_suicide", 1.0f, INVALID_JOINT ); + } + } +} + +/* +================ +idThread::Event_GetEnemiesKilled +================ +*/ +void idThread::Event_GetEnemiesKilled( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + idThread::ReturnFloat( player->inventory.enemiesKilled ); +} + +/* +================ +idThread::Event_GetTestVarGui1() +================ +*/ +void idThread::Event_GetTestVarGui1( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + idThread::ReturnFloat( player->inventory.testVarGui1 ); +} + +/* +================ +idThread::Event_GetTestVarGui2() +================ +*/ +void idThread::Event_GetTestVarGui2( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + idThread::ReturnFloat( player->inventory.testVarGui2 ); +} + +/* +================ +idThread::Event_GetTestVarGui3() +================ +*/ +void idThread::Event_GetTestVarGui3( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + idThread::ReturnFloat( player->inventory.testVarGui3 ); +} + +/* +================ +idThread::Event_GetTestVarGui4() +================ +*/ +void idThread::Event_GetTestVarGui4( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + idThread::ReturnFloat( player->inventory.testVarGui4 ); +} + +/* +================ +idThread::Event_GetTestVarGui5() +================ +*/ +void idThread::Event_GetTestVarGui5( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + idThread::ReturnFloat( player->inventory.testVarGui5 ); +} + +/* +================ +idThread::Event_GetTestVar1() +================ +*/ +void idThread::Event_GetTestVar1( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + idThread::ReturnFloat( player->inventory.testVar1 ); +} + +/* +================ +idThread::Event_GetTestVar2() +================ +*/ +void idThread::Event_GetTestVar2( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + idThread::ReturnFloat( player->inventory.testVar2 ); +} + +/* +================ +idThread::Event_GetTestVar3() +================ +*/ +void idThread::Event_GetTestVar3( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + idThread::ReturnFloat( player->inventory.testVar3 ); +} + +/* +================ +idThread::Event_GetTestVar4() +================ +*/ +void idThread::Event_GetTestVar4( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + idThread::ReturnFloat( player->inventory.testVar4 ); +} + +/* +================ +idThread::Event_GetTestVar5() +================ +*/ +void idThread::Event_GetTestVar5( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + idThread::ReturnFloat( player->inventory.testVar5 ); +} + +/* +================ +idThread::Event_GetTestVar6() +================ +*/ +void idThread::Event_GetTestVar6( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + idThread::ReturnFloat( player->inventory.testVar6 ); +} + +/* +================ +idThread::Event_GetTestVar7() +================ +*/ +void idThread::Event_GetTestVar7( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + idThread::ReturnFloat( player->inventory.testVar7 ); +} + +/* +================ +idThread::Event_GetTestVar8() +================ +*/ +void idThread::Event_GetTestVar8( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + idThread::ReturnFloat( player->inventory.testVar8 ); +} + +/* +================ +idThread::Event_GetTestVar9() +================ +*/ +void idThread::Event_GetTestVar9( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + idThread::ReturnFloat( player->inventory.testVar9 ); +} + +/* +================ +idThread::Event_GetTestVar10() +================ +*/ +void idThread::Event_GetTestVar10( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + idThread::ReturnFloat( player->inventory.testVar10 ); +} + +/* +================ +idThread::Event_SetTestVarGui1() +================ +*/ +void idThread::Event_SetTestVarGui1( float value ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + player->inventory.testVarGui1 = value; + + if( player->inventory.testVarGui1 < 0 || + player->inventory.testVarGui1 > INT_MAX ) { + + player->inventory.testVarGui1 = 0; + } +} + +/* +================ +idThread::Event_SetTestVarGui2() +================ +*/ +void idThread::Event_SetTestVarGui2( float value ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + player->inventory.testVarGui2 = value; + + if( player->inventory.testVarGui2 < 0 || + player->inventory.testVarGui2 > INT_MAX ) { + + player->inventory.testVarGui2 = 0; + } +} + +/* +================ +idThread::Event_SetTestVarGui3() +================ +*/ +void idThread::Event_SetTestVarGui3( float value ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + player->inventory.testVarGui3 = value; + + if( player->inventory.testVarGui3 < 0 || + player->inventory.testVarGui3 > INT_MAX ) { + + player->inventory.testVarGui3 = 0; + } +} + +/* +================ +idThread::Event_SetTestVarGui4() +================ +*/ +void idThread::Event_SetTestVarGui4( float value ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + player->inventory.testVarGui4 = value; + + if( player->inventory.testVarGui4 < 0 || + player->inventory.testVarGui4 > INT_MAX ) { + + player->inventory.testVarGui4 = 0; + } +} + +/* +================ +idThread::Event_SetTestVarGui5() +================ +*/ +void idThread::Event_SetTestVarGui5( float value ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + player->inventory.testVarGui5 = value; + + if( player->inventory.testVarGui5 < 0 || + player->inventory.testVarGui5 > INT_MAX ) { + + player->inventory.testVarGui5 = 0; + } +} + +/* +================ +idThread::Event_SetTestVar1() +================ +*/ +void idThread::Event_SetTestVar1( float value ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + player->inventory.testVar1 = value; + + if( player->inventory.testVar1 < 0 || + player->inventory.testVar1 > INT_MAX ) { + + player->inventory.testVar1 = 0; + } +} + +/* +================ +idThread::Event_SetTestVar2() +================ +*/ +void idThread::Event_SetTestVar2( float value ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + player->inventory.testVar2 = value; + + if( player->inventory.testVar2 < 0 || + player->inventory.testVar2 > INT_MAX ) { + + player->inventory.testVar2 = 0; + } +} + +/* +================ +idThread::Event_SetTestVar3() +================ +*/ +void idThread::Event_SetTestVar3( float value ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + player->inventory.testVar3 = value; + + if( player->inventory.testVar3 < 0 || + player->inventory.testVar3 > INT_MAX ) { + + player->inventory.testVar3 = 0; + } +} + +/* +================ +idThread::Event_SetTestVar4() +================ +*/ +void idThread::Event_SetTestVar4( float value ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + player->inventory.testVar4 = value; + + if( player->inventory.testVar4 < 0 || + player->inventory.testVar4 > INT_MAX ) { + + player->inventory.testVar4 = 0; + } +} + +/* +================ +idThread::Event_SetTestVar5() +================ +*/ +void idThread::Event_SetTestVar5( float value ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + player->inventory.testVar5 = value; + + if( player->inventory.testVar5 < 0 || + player->inventory.testVar5 > INT_MAX ) { + + player->inventory.testVar5 = 0; + } +} + +/* +================ +idThread::Event_SetTestVar6() +================ +*/ +void idThread::Event_SetTestVar6( float value ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + player->inventory.testVar6 = value; + + if( player->inventory.testVar6 < 0 || + player->inventory.testVar6 > INT_MAX ) { + + player->inventory.testVar6 = 0; + } +} + +/* +================ +idThread::Event_SetTestVar7() +================ +*/ +void idThread::Event_SetTestVar7( float value ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + player->inventory.testVar7 = value; + + if( player->inventory.testVar7 < 0 || + player->inventory.testVar7 > INT_MAX ) { + + player->inventory.testVar7 = 0; + } +} + +/* +================ +idThread::Event_SetTestVar8() +================ +*/ +void idThread::Event_SetTestVar8( float value ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + player->inventory.testVar8 = value; + + if( player->inventory.testVar8 < 0 || + player->inventory.testVar8 > INT_MAX ) { + + player->inventory.testVar8 = 0; + } +} + +/* +================ +idThread::Event_SetTestVar9() +================ +*/ +void idThread::Event_SetTestVar9( float value ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + player->inventory.testVar9 = value; + + if( player->inventory.testVar9 < 0 || + player->inventory.testVar9 > INT_MAX ) { + + player->inventory.testVar9 = 0; + } +} + +/* +================ +idThread::Event_SetTestVar10() +================ +*/ +void idThread::Event_SetTestVar10( float value ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + + player->inventory.testVar10 = value; + + if( player->inventory.testVar10 < 0 || + player->inventory.testVar10 > INT_MAX ) { + + player->inventory.testVar10 = 0; + } +} + +/* +================ +idThread::Event_PreviousPageDiary() +================ +*/ +void idThread::Event_PreviousPageDiary( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + idStrList diary = player->inventory.diary; + + player->inventory.diaryInfo.currentPage--; + + if( player->inventory.diaryInfo.currentPage == 1 ) { + player->diaryUI->HandleNamedEvent( "prevPageOff" ); + } + + player->diaryUI->HandleNamedEvent( "nextPageOn" ); + + int i = ( player->inventory.diaryInfo.currentPage * 2 ); + + player->inventory.diaryInfo.diaryText = diary[i-2]; + player->inventory.diaryInfo.diaryText2 = diary[i-1]; + player->inventory.diaryInfo.pageLeft = va( "-%i-", i-1 ); + player->inventory.diaryInfo.pageRight = va( "-%i-", i ); + + player->diaryUI->SetStateString( + "pageLeft", player->inventory.diaryInfo.pageLeft ); + player->diaryUI->SetStateString( + "pageRight", player->inventory.diaryInfo.pageRight ); + player->diaryUI->SetStateString( + "diaryText", player->inventory.diaryInfo.diaryText ); + player->diaryUI->SetStateString( + "diaryText2", player->inventory.diaryInfo.diaryText2 ); +} + +/* +================ +idThread::Event_NextPageDiary() +================ +*/ +void idThread::Event_NextPageDiary( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + idStrList diary = player->inventory.diary; + + player->inventory.diaryInfo.currentPage++; + int i = ( player->inventory.diaryInfo.currentPage * 2 ); + + if( i >= diary.Num() ) { + player->diaryUI->HandleNamedEvent( "nextPageOff" ); + } + player->diaryUI->HandleNamedEvent( "prevPageOn" ); + + player->inventory.diaryInfo.diaryText = diary[i-2]; + player->inventory.diaryInfo.pageLeft = va( "-%i-", i-1 ); + player->inventory.diaryInfo.pageRight = va( "-%i-", i ); + + if( (i-1) == diary.Num() && i > diary.Num() ) { + player->inventory.diaryInfo.diaryText2 = ""; + } + else { + player->inventory.diaryInfo.diaryText2 = diary[i-1]; + } + + player->diaryUI->SetStateString( + "pageLeft", player->inventory.diaryInfo.pageLeft ); + player->diaryUI->SetStateString( + "pageRight", player->inventory.diaryInfo.pageRight ); + player->diaryUI->SetStateString( + "diaryText", player->inventory.diaryInfo.diaryText ); + player->diaryUI->SetStateString( + "diaryText2", player->inventory.diaryInfo.diaryText2 ); +} + +/* +================ +idThread::Event_PreviousPageQuestlog() +================ +*/ +void idThread::Event_PreviousPageQuestlog( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + idStrList quest = player->inventory.quest; + + player->inventory.diaryInfo.currentPage--; + + player->inventory.diaryInfo.diaryText = ""; + player->inventory.diaryInfo.diaryText2 = ""; + player->inventory.diaryInfo.diaryText3 = ""; + player->inventory.diaryInfo.diaryText4 = ""; + + if( player->inventory.diaryInfo.currentPage == 1 ) { + player->questlogUI->HandleNamedEvent( "prevPageOff" ); + } + + player->questlogUI->HandleNamedEvent( "nextPageOn" ); + + player->questlogUI->HandleNamedEvent( "StateLeftTopBoxOn" ); + player->questlogUI->HandleNamedEvent( "StateLeftBottomBoxOn" ); + player->questlogUI->HandleNamedEvent( "StateRightTopBoxOn" ); + player->questlogUI->HandleNamedEvent( "StateRightBottomBoxOn" ); + + player->questlogUI->HandleNamedEvent( "StateLeftTopDoneOff" ); + player->questlogUI->HandleNamedEvent( "StateLeftBottomDoneOff" ); + player->questlogUI->HandleNamedEvent( "StateRightTopDoneOff" ); + player->questlogUI->HandleNamedEvent( "StateRightBottomDoneOff" ); + + int i = ( player->inventory.diaryInfo.currentPage * 4 ); + + player->inventory.diaryInfo.diaryText = quest[i-4]; + player->inventory.diaryInfo.diaryText2 = quest[i-3]; + player->inventory.diaryInfo.diaryText3 = quest[i-2]; + player->inventory.diaryInfo.diaryText4 = quest[i-1]; + player->inventory.diaryInfo.pageLeft = va( "-%i-", (i / 2) - 1 ); + player->inventory.diaryInfo.pageRight = va( "-%i-", (i / 2) ); + + if( player->inventory.questState[i-4] == "solved" ) { + player->questlogUI->HandleNamedEvent( "StateLeftTopDoneOn" ); + } + if( player->inventory.questState[i-3] == "solved" ) { + player->questlogUI->HandleNamedEvent( "StateLeftBottomDoneOn" ); + } + if( player->inventory.questState[i-2] == "solved" ) { + player->questlogUI->HandleNamedEvent( "StateRightTopDoneOn" ); + } + if( player->inventory.questState[i-1] == "solved" ) { + player->questlogUI->HandleNamedEvent( "StateRightBottomDoneOn" ); + } + + player->questlogUI->SetStateString( + "pageLeft", player->inventory.diaryInfo.pageLeft ); + player->questlogUI->SetStateString( + "pageRight", player->inventory.diaryInfo.pageRight ); + player->questlogUI->SetStateString( + "diaryText", player->inventory.diaryInfo.diaryText ); + player->questlogUI->SetStateString( + "diaryText2", player->inventory.diaryInfo.diaryText2 ); + player->questlogUI->SetStateString( + "diaryText3", player->inventory.diaryInfo.diaryText3 ); + player->questlogUI->SetStateString( + "diaryText4", player->inventory.diaryInfo.diaryText4 ); +} + +/* +================ +idThread::Event_NextPageQuestlog() +================ +*/ +void idThread::Event_NextPageQuestlog( void ) { + idPlayer* player = gameLocal.GetLocalPlayer(); + idStrList quest = player->inventory.quest; + + player->inventory.diaryInfo.currentPage++; + + player->inventory.diaryInfo.diaryText = ""; + player->inventory.diaryInfo.diaryText2 = ""; + player->inventory.diaryInfo.diaryText3 = ""; + player->inventory.diaryInfo.diaryText4 = ""; + + int i = ( player->inventory.diaryInfo.currentPage * 4 ); + + if( i >= quest.Num() ) { + player->questlogUI->HandleNamedEvent( "nextPageOff" ); + } + player->questlogUI->HandleNamedEvent( "prevPageOn" ); + + player->questlogUI->HandleNamedEvent( "StateLeftTopBoxOff" ); + player->questlogUI->HandleNamedEvent( "StateLeftBottomBoxOff" ); + player->questlogUI->HandleNamedEvent( "StateRightTopBoxOff" ); + player->questlogUI->HandleNamedEvent( "StateRightBottomBoxOff" ); + + player->questlogUI->HandleNamedEvent( "StateLeftTopDoneOff" ); + player->questlogUI->HandleNamedEvent( "StateLeftBottomDoneOff" ); + player->questlogUI->HandleNamedEvent( "StateRightTopDoneOff" ); + player->questlogUI->HandleNamedEvent( "StateRightBottomDoneOff" ); + + int diff = 0; + + if( i <= quest.Num() ) { + diff = i; + } else { + diff = quest.Num(); + } + + player->inventory.diaryInfo.diaryText = quest[i-4]; + player->inventory.diaryInfo.pageLeft = va( "-%i-", (i / 2) - 1 ); + player->inventory.diaryInfo.pageRight = va( "-%i-", (i / 2) ); + + player->questlogUI->HandleNamedEvent( "StateLeftTopBoxOn" ); + + if( player->inventory.questState[i-4] == "solved" ) { + player->questlogUI->HandleNamedEvent( "StateLeftTopDoneOn" ); + } + + if( (diff % 4) == 1 ) { + player->inventory.diaryInfo.diaryText2 = ""; + player->inventory.diaryInfo.diaryText3 = ""; + player->inventory.diaryInfo.diaryText4 = ""; + } + else if( (diff % 4) == 2 ) { + player->inventory.diaryInfo.diaryText2 = quest[i-3]; + player->questlogUI->HandleNamedEvent( "StateLeftBottomBoxOn" ); + if( player->inventory.questState[i-3] == "solved" ) { + player->questlogUI->HandleNamedEvent( "StateLeftBottomDoneOn" ); + } + player->inventory.diaryInfo.diaryText3 = ""; + player->inventory.diaryInfo.diaryText4 = ""; + } + else if( (diff % 4) == 3 ) { + player->inventory.diaryInfo.diaryText2 = quest[i-3]; + player->inventory.diaryInfo.diaryText3 = quest[i-2]; + player->questlogUI->HandleNamedEvent( "StateLeftBottomBoxOn" ); + player->questlogUI->HandleNamedEvent( "StateRightTopBoxOn" ); + if( player->inventory.questState[i-3] == "solved" ) { + player->questlogUI->HandleNamedEvent( "StateLeftBottomDoneOn" ); + } + if( player->inventory.questState[i-2] == "solved" ) { + player->questlogUI->HandleNamedEvent( "StateRightTopDoneOn" ); + } + player->inventory.diaryInfo.diaryText4 = ""; + } + else { + player->inventory.diaryInfo.diaryText2 = quest[i-3]; + player->inventory.diaryInfo.diaryText3 = quest[i-2]; + player->inventory.diaryInfo.diaryText4 = quest[i-1]; + player->questlogUI->HandleNamedEvent( "StateLeftBottomBoxOn" ); + player->questlogUI->HandleNamedEvent( "StateRightTopBoxOn" ); + player->questlogUI->HandleNamedEvent( "StateRightBottomBoxOn" ); + if( player->inventory.questState[i-3] == "solved" ) { + player->questlogUI->HandleNamedEvent( "StateLeftBottomDoneOn" ); + } + if( player->inventory.questState[i-2] == "solved" ) { + player->questlogUI->HandleNamedEvent( "StateRightTopDoneOn" ); + } + if( player->inventory.questState[i-1] == "solved" ) { + player->questlogUI->HandleNamedEvent( "StateRightBottomDoneOn" ); + } + } + + player->questlogUI->SetStateString( + "pageLeft", player->inventory.diaryInfo.pageLeft ); + player->questlogUI->SetStateString( + "pageRight", player->inventory.diaryInfo.pageRight ); + player->questlogUI->SetStateString( + "diaryText", player->inventory.diaryInfo.diaryText ); + player->questlogUI->SetStateString( + "diaryText2", player->inventory.diaryInfo.diaryText2 ); + player->questlogUI->SetStateString( + "diaryText3", player->inventory.diaryInfo.diaryText3 ); + player->questlogUI->SetStateString( + "diaryText4", player->inventory.diaryInfo.diaryText4 ); +} + +//###// + + /* ================ idThread::Event_RadiusDamage diff --git a/game/script/Script_Thread.h b/game/script/Script_Thread.h index fe24943..6f884f3 100644 --- a/game/script/Script_Thread.h +++ b/game/script/Script_Thread.h @@ -175,6 +175,55 @@ private: void Event_StrSkip( const char *string, int num ); void Event_StrMid( const char *string, int start, int num ); void Event_StrToFloat( const char *string ); + +//###// by MacX + + void Event_GetMoney( void ); + void Event_DecreaseMoney( float value ); + void Event_LooseMoney( void ); + void Event_SetPlayerHealth( void ); + void Event_DecreasePlayerHealth( float hp ); + void Event_PreviousPageDiary( void ); + void Event_NextPageDiary( void ); + void Event_PreviousPageQuestlog( void ); + void Event_NextPageQuestlog( void ); + + void Event_GetEnemiesKilled( void ); + + void Event_GetTestVarGui1( void ); + void Event_GetTestVarGui2( void ); + void Event_GetTestVarGui3( void ); + void Event_GetTestVarGui4( void ); + void Event_GetTestVarGui5( void ); + void Event_GetTestVar1( void ); + void Event_GetTestVar2( void ); + void Event_GetTestVar3( void ); + void Event_GetTestVar4( void ); + void Event_GetTestVar5( void ); + void Event_GetTestVar6( void ); + void Event_GetTestVar7( void ); + void Event_GetTestVar8( void ); + void Event_GetTestVar9( void ); + void Event_GetTestVar10( void ); + + void Event_SetTestVarGui1( float value ); + void Event_SetTestVarGui2( float value ); + void Event_SetTestVarGui3( float value ); + void Event_SetTestVarGui4( float value ); + void Event_SetTestVarGui5( float value ); + void Event_SetTestVar1( float value ); + void Event_SetTestVar2( float value ); + void Event_SetTestVar3( float value ); + void Event_SetTestVar4( float value ); + void Event_SetTestVar5( float value ); + void Event_SetTestVar6( float value ); + void Event_SetTestVar7( float value ); + void Event_SetTestVar8( float value ); + void Event_SetTestVar9( float value ); + void Event_SetTestVar10( float value ); + +//###// + void Event_RadiusDamage( const idVec3 &origin, idEntity *inflictor, idEntity *attacker, idEntity *ignore, const char *damageDefName, float dmgPower ); void Event_IsClient( void ); void Event_IsMultiplayer( void );