From cdc61a1a53475a4038df5dbd871105554c50e380 Mon Sep 17 00:00:00 2001 From: LegendGuard <49716252+LegendaryGuard@users.noreply.github.com> Date: Mon, 24 Jul 2023 18:43:41 +0200 Subject: [PATCH] Add modified source code in framework and game directory (part 1) --- framework/UsercmdGen.h | 7 + game/AFEntity.cpp | 8 +- game/AFEntity.h | 4 +- game/Actor.cpp | 107 +++- game/Actor.h | 11 +- game/BrittleFracture.cpp | 63 +- game/Entity.h | 81 ++- game/GameBase.h | 7 +- game/Game_local.cpp | 780 +++++++++++++++++++++++- game/Game_local.h | 57 +- game/Game_network.cpp | 26 + game/Item.cpp | 507 ++++++++++++++- game/Item.h | 51 ++ game/Light.cpp | 3 + game/Misc.cpp | 383 +++++++++++- game/Misc.h | 53 +- game/Moveable.cpp | 1253 +++++++++++++++++++++++++++++++++++++- game/Moveable.h | 83 ++- game/Mover.cpp | 189 +++++- game/Mover.h | 20 + 20 files changed, 3624 insertions(+), 69 deletions(-) diff --git a/framework/UsercmdGen.h b/framework/UsercmdGen.h index 17e88fe..616cf67 100644 --- a/framework/UsercmdGen.h +++ b/framework/UsercmdGen.h @@ -46,6 +46,7 @@ const int BUTTON_RUN = BIT(1); const int BUTTON_ZOOM = BIT(2); const int BUTTON_SCORES = BIT(3); const int BUTTON_MLOOK = BIT(4); +const int BUTTON_ATTACK2 = BIT(5); // Zeroth404 const int BUTTON_5 = BIT(5); const int BUTTON_6 = BIT(6); const int BUTTON_7 = BIT(7); @@ -82,6 +83,12 @@ const int IMPULSE_27 = 27; // const int IMPULSE_28 = 28; // vote yes const int IMPULSE_29 = 29; // vote no const int IMPULSE_40 = 40; // use vehicle +// Zeroth +const int IMPULSE_41 = 41; // hec Inventory Scroll Right +const int IMPULSE_42 = 42; // hec Inventory Scroll Left +const int IMPULSE_43 = 43; // hec use selected item +const int IMPULSE_44 = 44; // hec drop selected item +const int IMPULSE_45 = 45; // toggle automap // usercmd_t->flags const int UCF_IMPULSE_SEQUENCE = 0x0001; // toggled every time an impulse command is sent diff --git a/game/AFEntity.cpp b/game/AFEntity.cpp index 686263f..22a4e71 100644 --- a/game/AFEntity.cpp +++ b/game/AFEntity.cpp @@ -377,10 +377,10 @@ Pass damage to body at the bindjoint ============ */ void idAFAttachment::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, - const char *damageDefName, const float damageScale, const int location ) { + const char *damageDefName, const float damageScale, const int location, const idVec3 &iPoint ) { if ( body ) { - body->Damage( inflictor, attacker, dir, damageDefName, damageScale, attachJoint ); + body->Damage( inflictor, attacker, dir, damageDefName, damageScale, attachJoint, iPoint ); } } @@ -1087,11 +1087,11 @@ void idAFEntity_Gibbable::Present( void ) { idAFEntity_Gibbable::Damage ================ */ -void idAFEntity_Gibbable::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ) { +void idAFEntity_Gibbable::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location, idVec3 &iPoint ) { if ( !fl.takedamage ) { return; } - idAFEntity_Base::Damage( inflictor, attacker, dir, damageDefName, damageScale, location ); + idAFEntity_Base::Damage( inflictor, attacker, dir, damageDefName, damageScale, location, iPoint ); if ( health < -20 && spawnArgs.GetBool( "gib" ) ) { Gib( dir, damageDefName ); } diff --git a/game/AFEntity.h b/game/AFEntity.h index bc7dee4..1132e05 100644 --- a/game/AFEntity.h +++ b/game/AFEntity.h @@ -123,7 +123,7 @@ public: virtual void ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ); virtual void AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ); - virtual void Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ); + virtual void Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location, const idVec3 &iPoint ); virtual void AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName ); void SetCombatModel( void ); @@ -225,7 +225,7 @@ public: void Save( idSaveGame *savefile ) const; void Restore( idRestoreGame *savefile ); virtual void Present( void ); - virtual void Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ); + virtual void Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location, idVec3 &iPoint ); virtual void SpawnGibs( const idVec3 &dir, const char *damageDefName ); protected: diff --git a/game/Actor.cpp b/game/Actor.cpp index 15061a2..e303cc5 100644 --- a/game/Actor.cpp +++ b/game/Actor.cpp @@ -1825,6 +1825,27 @@ idActor *idActor::ClosestEnemyToPoint( const idVec3 &pos ) { return bestEnt; } +// HEXEN : Zeroth +/* +================ +idActor::IsEnemy +================ +*/ +bool idActor::IsEnemy( idEntity *test ) { + idActor *ent; + for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) { + if ( ent->fl.hidden ) { + continue; + } + gameLocal.Printf(" [%s] - [%s]", ent->name.c_str(), test->name.c_str()); + if ( ent->name.c_str() == test->name.c_str() ) { + return true; + } + } + + return false; +} + /* ================ idActor::EnemyWithMostHealth @@ -2158,7 +2179,7 @@ calls Damage() ============ */ void idActor::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, - const char *damageDefName, const float damageScale, const int location ) { + const char *damageDefName, const float damageScale, const int location, const idVec3 &iPoint ) { if ( !fl.takedamage ) { return; } @@ -2258,10 +2279,25 @@ bool idActor::Pain( idEntity *inflictor, idEntity *attacker, int damage, const i return false; } - // set the pain anim idStr damageGroup = GetDamageGroup( location ); painAnim = ""; + if ( g_debugDamage.GetBool() ) { + gameLocal.Printf( "Damage: joint: '%s', zone '%s', anim '%s'\n", animator.GetJointName( ( jointHandle_t )location ), + damageGroup.c_str(), painAnim.c_str() ); + } + + // HEXEN : Zeroth: don't do pain anims for trigger types + if ( inflictor ) { + if ( inflictor->IsType( idTrigger_Hurt::Type ) ) { + return false; + } + if ( inflictor->IsType( idTrigger_Touch::Type ) ) { + return false; + } + } + + // set the pain anim if ( animPrefix.Length() ) { if ( damageGroup.Length() && ( damageGroup != "legs" ) ) { sprintf( painAnim, "%s_pain_%s", animPrefix.c_str(), damageGroup.c_str() ); @@ -2293,11 +2329,6 @@ bool idActor::Pain( idEntity *inflictor, idEntity *attacker, int damage, const i painAnim = "pain"; } - if ( g_debugDamage.GetBool() ) { - gameLocal.Printf( "Damage: joint: '%s', zone '%s', anim '%s'\n", animator.GetJointName( ( jointHandle_t )location ), - damageGroup.c_str(), painAnim.c_str() ); - } - return true; } @@ -2403,11 +2434,49 @@ idActor::Event_EnableEyeFocus void idActor::PlayFootStepSound( void ) { const char *sound = NULL; const idMaterial *material; + idPlayer *player; if ( !GetPhysics()->HasGroundContacts() ) { return; } + // HEXEN : Zeroth + if ( this->IsType( idPlayer::Type ) ) { + player = static_cast< idPlayer* >( this ); + + // no footstep sounds when flying (wings of wrath) + if ( player->FreeMove ) { + return; + } + + if ( player->inWater ) { + waterLevel_t level = static_cast< idPhysics_Player* >( player->GetPlayerPhysics() )->GetWaterLevel(); + + if ( level == WATERLEVEL_FEET ) { + sound = spawnArgs.GetString( "snd_footstep_water_feet" ); + } else if ( level == WATERLEVEL_WAIST ) { + sound = spawnArgs.GetString( "snd_footstep_water_waist" ); + } else if ( level == WATERLEVEL_HEAD ) { + sound = spawnArgs.GetString( "snd_footstep_water_head" ); + } else { + sound = spawnArgs.GetString( "snd_footstep_water_feet" ); + } + + if ( *sound != '\0' ) { + StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_BODY, SSF_NO_DUPS, false, NULL ); + } + + return; + } else if ( player->leftWater != 0 && gameLocal.time < player->leftWater + 3000 ) { + sound = spawnArgs.GetString( "snd_footstep_wet" ); + + if ( *sound != '\0' ) { + StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_BODY, SSF_NO_DUPS, false, NULL ); + } + return; + } + } + // start footstep sound based on material type material = GetPhysics()->GetContact( 0 ).material; if ( material != NULL ) { @@ -2417,7 +2486,7 @@ void idActor::PlayFootStepSound( void ) { sound = spawnArgs.GetString( "snd_footstep" ); } if ( *sound != '\0' ) { - StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_BODY, 0, false, NULL ); + StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_BODY, SSF_NO_DUPS, false, NULL ); } } @@ -3120,22 +3189,30 @@ idActor::Event_AnimLength ================ */ void idActor::Event_AnimLength( int channel, const char *animname ) { + idThread::ReturnFloat( GetAnimLength(channel, animname) ); +} + +// HEXEN : Zeroth +/* +================ +idActor::GetAnimLength +================ +*/ +float idActor::GetAnimLength( int channel, const char *animname ) { int anim; anim = GetAnim( channel, animname ); if ( anim ) { if ( channel == ANIMCHANNEL_HEAD ) { if ( head.GetEntity() ) { - idThread::ReturnFloat( MS2SEC( head.GetEntity()->GetAnimator()->AnimLength( anim ) ) ); - return; + return MS2SEC( head.GetEntity()->GetAnimator()->AnimLength( anim ) ); } } else { - idThread::ReturnFloat( MS2SEC( animator.AnimLength( anim ) ) ); - return; - } + return MS2SEC( animator.AnimLength( anim ) ); + } } - - idThread::ReturnFloat( 0.0f ); + + return 0.0f; } /* diff --git a/game/Actor.h b/game/Actor.h index c52fb27..eb26d58 100644 --- a/game/Actor.h +++ b/game/Actor.h @@ -165,7 +165,7 @@ public: // damage void SetupDamageGroups( void ); - virtual void Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ); + virtual void Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location, const idVec3 &iPoint ); int GetDamageForLocation( int damage, int location ); const char * GetDamageGroup( int location ); void ClearPain( void ); @@ -318,6 +318,15 @@ private: void Event_SetState( const char *name ); void Event_GetState( void ); void Event_GetHead( void ); + +// HEXEN : Zeroth +protected: + void Event_VecForward( float spread ); // return a forward vector for the direction we're facing, with some random spread (like projectiles) + +// HEXEN : Zeroth +public: + bool IsEnemy( idEntity *test ); + float GetAnimLength( int channel, const char *animname ); }; #endif /* !__GAME_ACTOR_H__ */ diff --git a/game/BrittleFracture.cpp b/game/BrittleFracture.cpp index 58557a0..16565e1 100644 --- a/game/BrittleFracture.cpp +++ b/game/BrittleFracture.cpp @@ -39,8 +39,8 @@ CLASS_DECLARATION( idEntity, idBrittleFracture ) EVENT( EV_Touch, idBrittleFracture::Event_Touch ) END_CLASS -const int SHARD_ALIVE_TIME = 5000; -const int SHARD_FADE_START = 2000; +const int SHARD_ALIVE_TIME = 5000; // HEXEN : Zeroth - changed from 5000 +const int SHARD_FADE_START = 3500; // HEXEN : Zeroth - changed from 2000 static const char *brittleFracture_SnapshotName = "_BrittleFracture_Snapshot_"; @@ -772,13 +772,39 @@ void idBrittleFracture::ProjectDecal( const idVec3 &point, const idVec3 &dir, co idBrittleFracture::DropShard ================ */ -void idBrittleFracture::DropShard( shard_t *shard, const idVec3 &point, const idVec3 &dir, const float impulse, const int time ) { +void idBrittleFracture::DropShard( shard_t *shard, const idVec3 &point, const idVec3 &odir, const float oimpulse, const int time ) { int i, j, clipModelId; float dist, f; idVec3 dir2, origin; idMat3 axis; shard_t *neighbour; + idVec3 dir = odir; // HEXEN : Zeroth + float impulse = oimpulse; // HEXEN : Zeroth + + // HEXEN : Zeroth + if ( dir.Length() < 5 ) { // 5 is just an (whats that word?) + dir = idVec3(0,0,-1) * spawnArgs.GetFloat( "forceIfZero", "0" ); + impulse = dir.Normalize(); + } + + // HEXEN : Zeroth + if ( spawnArgs.GetBool( "blastBothWays", "0" ) ) { + dir = -dir; + //m = dir.Normalize(); + } + + // HEXEN : Zeroth + if ( spawnArgs.GetBool( "randomDir", "0" ) ) { + float len = dir.Length(); + dir.x = gameLocal.random.RandomFloat() - 0.5; + dir.y = gameLocal.random.RandomFloat() - 0.5; + dir.z = gameLocal.random.RandomFloat() - 0.5; + dir.Normalize(); + dir *= len; + //m = dir.Normalize(); + } + // don't display decals on dropped shards shard->decals.DeleteContents( true ); @@ -818,10 +844,26 @@ void idBrittleFracture::DropShard( shard_t *shard, const idVec3 &point, const id shard->physicsObj.SetAxis( axis ); shard->physicsObj.SetBouncyness( bouncyness ); shard->physicsObj.SetFriction( 0.6f, 0.6f, friction ); - shard->physicsObj.SetGravity( gameLocal.GetGravity() ); + + // HEXEN : Zeroth + float grv = spawnArgs.GetFloat( "shardGravity", "0" ); + if ( grv ) { + shard->physicsObj.SetGravity( idVec3(0,0,-1) * grv ); + } else { + shard->physicsObj.SetGravity( gameLocal.GetGravity() ); + } + + // HEXEN : Zeroth + float velScale = 1; + if ( spawnArgs.GetBool( "shardRandomVelocity", "0" ) ) { + velScale = gameLocal.random.RandomFloat(); + } else { + velScale = 1; + } + shard->physicsObj.SetContents( CONTENTS_RENDERMODEL ); shard->physicsObj.SetClipMask( MASK_SOLID | CONTENTS_MOVEABLECLIP ); - shard->physicsObj.ApplyImpulse( 0, origin, impulse * linearVelocityScale * dir ); + shard->physicsObj.ApplyImpulse( 0, origin, impulse * linearVelocityScale * dir * velScale ); // HEXEN : Zeroth: added velScale shard->physicsObj.SetAngularVelocity( dir.Cross( dir2 ) * ( f * angularVelocityScale ) ); shard->clipModel->SetId( clipModelId ); @@ -884,7 +926,10 @@ void idBrittleFracture::Shatter( const idVec3 &point, const idVec3 &impulse, con DropShard( shard, point, dir, m, time ); } - DropFloatingIslands( point, impulse, time ); + // HEXEN : Zeroth + if ( spawnArgs.GetBool( "dropFloatingIsland", "1" ) ) { + DropFloatingIslands( point, dir, time ); // impulse changed to dir + } } /* @@ -895,12 +940,13 @@ idBrittleFracture::DropFloatingIslands void idBrittleFracture::DropFloatingIslands( const idVec3 &point, const idVec3 &impulse, const int time ) { int i, j, numIslands; int queueStart, queueEnd; + float m; // HEXEN : Zeroth shard_t *curShard, *nextShard, **queue; bool touchesEdge; idVec3 dir; dir = impulse; - dir.Normalize(); + m = dir.Normalize(); // HEXEN : Zeroth - added 'm = ' numIslands = 0; queue = (shard_t **) _alloca16( shards.Num() * sizeof(shard_t **) ); @@ -955,7 +1001,7 @@ void idBrittleFracture::DropFloatingIslands( const idVec3 &point, const idVec3 & // if the island is not connected to the world at any edges if ( !touchesEdge ) { for ( j = 0; j < queueEnd; j++ ) { - DropShard( queue[j], point, dir, 0.0f, time ); + DropShard( queue[j], point, dir, m, time ); // HEXEN : Zeroth - 0.0f to m } } } @@ -969,6 +1015,7 @@ idBrittleFracture::Break void idBrittleFracture::Break( void ) { fl.takedamage = false; physicsObj.SetContents( CONTENTS_RENDERMODEL | CONTENTS_TRIGGER ); + gameLocal.SetPersistentRemove( name.c_str() ); } /* diff --git a/game/Entity.h b/game/Entity.h index 709543b..852a835 100644 --- a/game/Entity.h +++ b/game/Entity.h @@ -141,7 +141,14 @@ public: idList< idEntityPtr > targets; // when this entity is activated these entities entity are activated - int health; // FIXME: do all objects really need health? + // HEXEN : Zeroth +protected: + int nextFlame; + int fireJoint; +public: + int health; + int onFire; + struct entityFlags_s { bool notarget :1; // if true never attack or target this entity @@ -158,6 +165,16 @@ public: bool networkSync :1; // if true the entity is synchronized over the network } fl; +// HEXEN : Zeroth +public: + bool gravityMod; + bool inWater; + +// HEXEN : Zeroth +public: + idVec3 curGrav( void ); + idVec3 curNorm( void ); + public: ABSTRACT_PROTOTYPE( idEntity ); @@ -177,6 +194,16 @@ public: // clients generate views based on all the player specific options, // cameras have custom code, and everything else just uses the axis orientation virtual renderView_t * GetRenderView(); + void ShutdownThreads( void ); + void UpdateScript( void ); + void SetState( const char *statename ); + void SetState( const function_t *newState ); + const function_t *GetScriptFunction( const char *funcname ); + const char *WaitState( void ) const; + void SetWaitState( const char *_waitstate ); + //idThread *ConstructScriptObject( void ); + //void idEntity::FinishSetup( void ); + //bool IsInUse( void ); // HEXEN : Zeroth // thinking virtual void Think( void ); @@ -302,7 +329,7 @@ public: // returns true if this entity can be damaged from the given origin virtual bool CanDamage( const idVec3 &origin, idVec3 &damagePoint ) const; // applies damage to this entity - virtual void Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ); + virtual void Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location, const idVec3 &iPoint ); // adds a damage effect like overlays, blood, sparks, debris etc. virtual void AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName ); // callback function for when another entity received damage from this entity. damage can be adjusted and returned to the caller. @@ -361,11 +388,24 @@ public: void ServerSendEvent( int eventId, const idBitMsg *msg, bool saveEvent, int excludeClient ) const; void ClientSendEvent( int eventId, const idBitMsg *msg ) const; +// HEXEN : Zeroth +// ****** thanks SnoopJeDi ( http://www.doom3world.org/phpbb2/viewtopic.php?f=56&t=12469&p=214427#p214427 ) + void FadeMusic( int channel, float to, float over ); +// ****** + protected: renderEntity_t renderEntity; // used to present a model to the renderer int modelDefHandle; // handle to static renderer model refSound_t refSound; // used to present sound to the audio engine +// HEXEN : Zeroth + // state variables + const function_t *state; + const function_t *idealState; + // script variables + idThread * scriptThread; + idStr waitState; + private: idPhysics_Static defaultPhysicsObj; // default physics object idPhysics * physics; // physics used for this entity @@ -381,6 +421,7 @@ private: signalList_t * signals; int mpGUIState; // local cache to avoid systematic SetStateInt + idThread * thread; private: void FixupLocalizedStrings(); @@ -465,6 +506,32 @@ private: void Event_HasFunction( const char *name ); void Event_CallFunction( const char *name ); void Event_SetNeverDormant( int enable ); + +// HEXEN : Zeroth +private: + idDict projectileDict; + idEntity *projectileEnt; + void Event_GetState( void ); + void Event_SetState( const char *name ); + void Event_SetNextState( const char *name ); + + void Event_SetGravity( const idVec3 &grav ); + void Event_GetGravity( void ); + void Event_GetGravityNormal( void ); + void Event_GetSelfEntity( void ); + void Event_SetHealth( float newHealth ); + void Event_GetHealth( void ); + void Event_GetType( void ); + void Event_SpawnProjectiles( int num_projectiles, float spread, float fuseOffset, float launchPower, float dmgPower ); + void Event_CreateProjectile( void ); + void Event_GetMaster( void ); + void Event_GetModelDims( void ); + void Event_ReplaceMaterial( const char * replacee, const char * replacer ); + void Event_ResetGravity( void ); + void Event_HudMessage( const char *message ); +public: + idAngles GetAngles( void ); + idVec3 GetModelDims( void ); }; /* @@ -509,6 +576,7 @@ public: virtual void AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName ); void AddLocalDamageEffect( jointHandle_t jointNum, const idVec3 &localPoint, const idVec3 &localNormal, const idVec3 &localDir, const idDeclEntityDef *def, const idMaterial *collisionMaterial ); void UpdateDamageEffects( void ); + void EmitFlames( void ); virtual bool ClientReceiveEvent( int event, int time, const idBitMsg &msg ); @@ -529,6 +597,15 @@ private: void Event_SetJointAngle( jointHandle_t jointnum, jointModTransform_t transform_type, const idAngles &angles ); void Event_GetJointPos( jointHandle_t jointnum ); void Event_GetJointAngle( jointHandle_t jointnum ); + +// HEXEN : Zeroth +private: + void Event_TransitionJointAngle( jointHandle_t jointnum, jointModTransform_t transform_type, const idAngles &to, const idAngles &from, float seconds, float transitions ); +public: + idVec3 idAnimatedEntity::GetJointPos( jointHandle_t jointnum ); + void TransitionJointAngle( jointHandle_t jointnum, jointModTransform_t transform_type, const idAngles &to, const idAngles &from, float seconds, float transitions ); + void SetJointAngle( jointHandle_t jointnum, jointModTransform_t transform_type, const idAngles &angles ); + idAngles GetJointAngle( jointHandle_t jointnum ); }; #endif /* !__GAME_ENTITY_H__ */ diff --git a/game/GameBase.h b/game/GameBase.h index 5269b67..d8616ff 100644 --- a/game/GameBase.h +++ b/game/GameBase.h @@ -34,6 +34,11 @@ If you have questions concerning this license or the applicable additional terms #define SCRIPT_DEFAULT "script/doom_main.script" #define SCRIPT_DEFAULTFUNC "doom_main" +// HEXEN : Zeroth +#define NUM_UNIQUE_ARTIFACTS (16) +#define EOC_NUM_VMODES (15) +#define EOC_RELEASE (1) + #define LAGO_IMG_WIDTH 64 #define LAGO_IMG_HEIGHT 64 #define LAGO_WIDTH 64 @@ -50,7 +55,7 @@ If you have questions concerning this license or the applicable additional terms #define GAME_VERSION "baseDOOM-1" #define MAX_CLIENTS 32 -#define GENTITYNUM_BITS 12 +#define GENTITYNUM_BITS 13 #define MAX_GENTITIES (1<BufferCommandText( CMD_EXEC_APPEND, "heartbeat\n" ); // SnoopJeDi - iddevnet fix to keep server from dropping off master list + // ** + this->isServer = isServer; this->isClient = isClient; this->isMultiplayer = isServer || isClient; @@ -1188,6 +1294,7 @@ void idGameLocal::InitFromNewMap( const char *mapName, idRenderWorld *renderWorl gameRenderWorld = renderWorld; gameSoundWorld = soundWorld; + eoc_MapPath.Clear(); LoadMap( mapName, randseed ); @@ -1203,6 +1310,10 @@ void idGameLocal::InitFromNewMap( const char *mapName, idRenderWorld *renderWorl animationLib.FlushUnusedAnims(); gamestate = GAMESTATE_ACTIVE; + + // HEXEN : Zeroth + InitHub(); + UpdateFog(); } /* @@ -1222,6 +1333,8 @@ bool idGameLocal::InitFromSaveGame( const char *mapName, idRenderWorld *renderWo Printf( "----- Game Map Init SaveGame -----\n" ); + eoc_MapPath.Clear(); + gamestate = GAMESTATE_STARTUP; gameRenderWorld = renderWorld; @@ -1231,6 +1344,11 @@ bool idGameLocal::InitFromSaveGame( const char *mapName, idRenderWorld *renderWo savegame.ReadBuildNumber(); + // a number to signify which release the save file belons to. this + // way we can make save games compatible with newer releases of + int eocnum; + savegame.ReadInt( eocnum ); + // DG: I enhanced the information in savegames a bit for dhewm3 1.5.1 // for which I bumped th BUILD_NUMBER to 1305 if( savegame.GetBuildNumber() >= 1305 ) @@ -1394,6 +1512,10 @@ bool idGameLocal::InitFromSaveGame( const char *mapName, idRenderWorld *renderWo savegame.ReadBool( isNewFrame ); savegame.ReadFloat( clientSmoothing ); + // HEXEN : Zeroth404 + portalSkyEnt.Restore( &savegame ); + savegame.ReadBool( portalSkyActive ); + savegame.ReadBool( mapCycleLoaded ); savegame.ReadInt( spawnCount ); @@ -1442,6 +1564,9 @@ bool idGameLocal::InitFromSaveGame( const char *mapName, idRenderWorld *renderWo savegame.RestoreObjects(); + // HEXEN : Zeroth + InitHub(); + mpGame.Reset(); mpGame.Precache(); @@ -1462,6 +1587,8 @@ idGameLocal::MapClear void idGameLocal::MapClear( bool clearClients ) { int i; + SavePersistentMoveables(); + for( i = ( clearClients ? 0 : MAX_CLIENTS ); i < MAX_GENTITIES; i++ ) { delete entities[ i ]; // ~idEntity is in charge of setting the pointer to NULL @@ -1471,6 +1598,7 @@ void idGameLocal::MapClear( bool clearClients ) { } entityHash.Clear( 1024, MAX_GENTITIES ); + entypeHash.Clear( 1024, MAX_GENTITIES ); if ( !clearClients ) { // add back the hashes of the clients @@ -1479,6 +1607,7 @@ void idGameLocal::MapClear( bool clearClients ) { continue; } entityHash.Add( entityHash.GenerateKey( entities[ i ]->name.c_str(), true ), i ); + entypeHash.Add( entypeHash.GenerateKey( entities[ i ]->GetClassname(), true ), i ); } } @@ -1492,6 +1621,8 @@ void idGameLocal::MapClear( bool clearClients ) { delete[] locationEntities; locationEntities = NULL; + + BanishLocationList.DeleteContents( true ); } /* @@ -1533,6 +1664,7 @@ void idGameLocal::MapShutdown( void ) { ShutdownAsyncNetwork(); mapFileName.Clear(); + BanishLocationList.Clear(); gameRenderWorld = NULL; gameSoundWorld = NULL; @@ -2040,6 +2172,24 @@ void idGameLocal::SetupPlayerPVS( void ) { pvs.FreeCurrentPVS( otherPVS ); playerConnectedAreas = newPVS; } + + // HEXEN : Zeroth + // if portalSky is preset, then merge into pvs so we get rotating brushes, etc + if ( portalSkyEnt.GetEntity() ) { + idEntity *skyEnt = portalSkyEnt.GetEntity(); + + otherPVS = pvs.SetupCurrentPVS( skyEnt->GetPVSAreas(), skyEnt->GetNumPVSAreas() ); + newPVS = pvs.MergeCurrentPVS( playerPVS, otherPVS ); + pvs.FreeCurrentPVS( playerPVS ); + pvs.FreeCurrentPVS( otherPVS ); + playerPVS = newPVS; + + otherPVS = pvs.SetupCurrentPVS( skyEnt->GetPVSAreas(), skyEnt->GetNumPVSAreas() ); + newPVS = pvs.MergeCurrentPVS( playerConnectedAreas, otherPVS ); + pvs.FreeCurrentPVS( playerConnectedAreas ); + pvs.FreeCurrentPVS( otherPVS ); + playerConnectedAreas = newPVS; + } } } @@ -2204,6 +2354,14 @@ gameReturn_t idGameLocal::RunFrame( const usercmd_t *clientCmds ) { idPlayer *player; const renderView_t *view; +// HEXEN : Zeroth - for foliage rendering +/* + idEntity *ent; + float isFoliage=0; + idVec3 dist; + float maxFoliageDist=2000; +*/ + #ifdef _DEBUG if ( isMultiplayer ) { assert( !isClient ); @@ -2212,6 +2370,26 @@ gameReturn_t idGameLocal::RunFrame( const usercmd_t *clientCmds ) { player = GetLocalPlayer(); +// HEXEN : Zeroth - foliage rendering +/* + for (int i = 0; i < MAX_GENTITIES; i++ ) { + ent = entities[i]; + if (ent) { + ent = entities[i]; + ent->spawnArgs.GetFloat( "foliage", "0", isFoliage ); + if ( isFoliage ) { + dist = ent->GetPhysics()->GetOrigin() - player->GetPhysics()->GetOrigin(); + + if (sqrt(dist.x * dist.x + dist.y * dist.y + dist.z * dist.z) > maxFoliageDist) { + ent->Hide(); + }else{ + ent->Show(); + } + } + } + } +*/ + if ( !isMultiplayer && g_stopTime.GetBool() ) { // clear any debug lines from a previous frame gameRenderWorld->DebugClearLines( time + 1 ); @@ -2223,11 +2401,14 @@ gameReturn_t idGameLocal::RunFrame( const usercmd_t *clientCmds ) { player->Think(); } } else do { - // update the game time - framenum++; - previousTime = time; - time += msec; - realClientTime = time; + + if ( !paused ) { + // update the game time + framenum++; + previousTime = time; + time += msec; + realClientTime = time; + } #ifdef GAME_DLL // allow changing SIMD usage on the fly @@ -2358,8 +2539,6 @@ gameReturn_t idGameLocal::RunFrame( const usercmd_t *clientCmds ) { if ( !isMultiplayer && player ) { ret.health = player->health; - ret.heartRate = player->heartRate; - ret.stamina = idMath::FtoiFast( player->stamina ); // combat is a 0-100 value based on lastHitTime and lastDmgTime // each make up 50% of the time spread over 10 seconds ret.combat = 0; @@ -2488,6 +2667,16 @@ makes rendering and sound system calls ================ */ bool idGameLocal::Draw( int clientNum ) { + + if ( s_music_vol.IsModified() ) { //SnoopJeDi, fade that sound! + for ( int i = 0; i < musicSpeakers.Num(); i++ ) { + idSound* ent = static_cast(entities[ musicSpeakers[ i ] ]); + if (ent) + ent->FadeMusic( 0, s_music_vol.GetFloat(), 0 ); + } + s_music_vol.ClearModified(); + } + if ( isMultiplayer ) { return mpGame.Draw( clientNum ); } @@ -2550,12 +2739,86 @@ const char* idGameLocal::HandleGuiCommands( const char *menuCommand ) { return mpGame.HandleGuiCommands( menuCommand ); } +#if 0 +#ifdef EOC_WIN32 +void idGameLocal::GetMainWindowHandle(void) { + extern HWND eoc_hwnd; + if (eoc_hwnd != 0) + return; + GUITHREADINFO winfo; + winfo.cbSize = sizeof(GUITHREADINFO); + GetGUIThreadInfo(GetCurrentThreadId(), &winfo); + eoc_hwnd = winfo.hwndActive; +} +#endif +void idGameLocal::GetDesktopResolution(int *X, int *Y) { + gameLocal.Printf("Entered"); + #ifdef EOC_WIN32 + RECT rect; + GetWindowRect(GetDesktopWindow(), &rect); + *X = (int) (rect.right - rect.left); + *Y = (int) (rect.bottom - rect.top); + #endif +} +void idGameLocal::GetMouseCoord(int *X, int *Y) { + #ifdef EOC_WIN32 + int width, height; + float posX, posY; + POINT pt; + RECT rect; + extern HWND eoc_hwnd; + GetDesktopResolution(&width, &height); + GetMainWindowHandle(); + GetWindowRect(eoc_hwnd, &rect); + GetCursorPos(&pt); + gameLocal.Printf("\n\n------------------------------"); + gameLocal.Printf("Doom Res: %d, %d\n", (rect.right - rect.left), (rect.bottom - rect.top) ); + gameLocal.Printf("GetCursorPos: %d, %d\n", pt.x, pt.y); + posX = (float) pt.x; // convert to float so we don't lose precision + posY = (float) pt.y; + gameLocal.Printf("Subracting Left/Top: %d, %d\n", rect.left, rect.top); + posX -= rect.left; + posY -= rect.top; + if (posX < 0) posX=0; + if (posY < 0) posY=0; + if (posX > rect.right) posX=rect.right; + if (posY > rect.bottom) posY=rect.bottom; + gameLocal.Printf("Subracted: %f, %f\n", posX, posY); + *X = (int) posX; + *Y = (int) posY; + gameLocal.Printf("Converted to Int: %d, %d\n", *X, *Y ); + gameLocal.Printf("------------------------------\n\n"); + // scale down to dooms actual resolution +#endif +} +#endif + /* ================ idGameLocal::HandleMainMenuCommands ================ */ -void idGameLocal::HandleMainMenuCommands( const char *menuCommand, idUserInterface *gui ) { } +void idGameLocal::HandleMainMenuCommands( const char *menuCommand, idUserInterface *gui ) { + idUserInterface *mainMenuGui = uiManager->FindGui( "guis/mainmenu.gui", true, false, true ); + if ( mainMenuGui ) { + mainMenuGui->SetStateFloat("inGame", gamestate == GAMESTATE_ACTIVE); + } +// HEXEN : Zeroth +#if 0 + #ifdef EOC_WIN32 + int posX, posY; + GetMouseCoord(&posX, &posY); + idUserInterface *mainMenuGui = uiManager->FindGui( "guis/mainmenu.gui", true, false, true ); + mainMenuGui->SetStateFloat( "eoc_MouseX", posX ); + mainMenuGui->SetStateFloat( "eoc_MouseY", posY ); + #endif +#endif + +// HEXEN : Zeroth +// idUserInterface *mainMenuGui = uiManager->FindGui( "guis/mainmenu.gui", true, false, true ); +// if (mainMenuGui) mainMenuGui->SetStateFloat( "eoc_MouseX", mainMenuGui->CursorX() ); +// if (mainMenuGui) mainMenuGui->SetStateFloat( "eoc_MouseY", mainMenuGui->CursorY() ); +} /* ================ @@ -3233,7 +3496,7 @@ void idGameLocal::SpawnMapEntities( void ) { int numEntities; idDict args; - Printf( "Spawning entities\n" ); + // Printf( "Spawning entities\n" ); if ( mapFile == NULL ) { Printf("No mapfile present\n"); @@ -3259,6 +3522,10 @@ void idGameLocal::SpawnMapEntities( void ) { num = 1; inhibit = 0; + // if ( loadGui != NULL ) { + // tickShader = loadGui->GetStateString( "tickshader" ); + // } + for ( i = 1 ; i < numEntities ; i++ ) { mapEnt = mapFile->GetEntity( i ); args = mapEnt->epairs; @@ -3287,6 +3554,7 @@ void idGameLocal::AddEntityToHash( const char *name, idEntity *ent ) { Error( "Multiple entities named '%s'", name ); } entityHash.Add( entityHash.GenerateKey( name, true ), ent->entityNumber ); + entypeHash.Add( entypeHash.GenerateKey( ent->spawnArgs.GetString("spawnclass"), true ), ent->entityNumber ); } /* @@ -3297,6 +3565,15 @@ idGameLocal::RemoveEntityFromHash bool idGameLocal::RemoveEntityFromHash( const char *name, idEntity *ent ) { int hash, i; + // HEXEN : Zeroth + hash = entypeHash.GenerateKey( ent->spawnArgs.GetString("spawnclass"), true ); + for ( i = entypeHash.First( hash ); i != -1; i = entypeHash.Next( i ) ) { + if ( entities[i] && entities[i] == ent && entities[i]->name.Icmp( name ) == 0 && !strcmp(entities[i]->GetClassname(), ent->GetClassname() ) ) { + entypeHash.Remove( hash, i ); + break; + } + } + hash = entityHash.GenerateKey( name, true ); for ( i = entityHash.First( hash ); i != -1; i = entityHash.Next( i ) ) { if ( entities[i] && entities[i] == ent && entities[i]->name.Icmp( name ) == 0 ) { @@ -3394,6 +3671,26 @@ idEntity *idGameLocal::FindEntity( const char *name ) const { return NULL; } +/* +============= +Zeroth +idGameLocal::FindEntityType +============= +*/ +idEntity *idGameLocal::FindEntityType( const idTypeInfo &type ) const { + int hash, i; + + // HEXEN : Zeroth + hash = entypeHash.GenerateKey( type.classname, true ); + for ( i = entypeHash.First( hash ); i != -1; i = entypeHash.Next( i ) ) { + if ( entities[i] && !strcmp(entities[i]->GetClassname(), type.classname ) ) { + return entities[i]; + } + } + + return NULL; +} + /* ============= idGameLocal::FindEntityUsingDef @@ -3519,7 +3816,7 @@ void idGameLocal::KillBox( idEntity *ent, bool catch_teleport ) { if ( hit->IsType( idPlayer::Type ) && static_cast< idPlayer * >( hit )->IsInTeleport() ) { static_cast< idPlayer * >( hit )->TeleportDeath( ent->entityNumber ); } else if ( !catch_teleport ) { - hit->Damage( ent, ent, vec3_origin, "damage_telefrag", 1.0f, INVALID_JOINT ); + hit->Damage( ent, ent, vec3_origin, "damage_telefrag", 1.0f, INVALID_JOINT, idVec3( 0, 0, 0 ) ); } if ( !gameLocal.isMultiplayer ) { @@ -3592,6 +3889,7 @@ void idGameLocal::RadiusDamage( const idVec3 &origin, idEntity *inflictor, idEnt idBounds bounds; idVec3 v, damagePoint, dir; int i, e, damage, radius, push; + bool notlocalplayer; const idDict *damageDef = FindEntityDefDict( damageDefName, false ); if ( !damageDef ) { @@ -3604,6 +3902,7 @@ void idGameLocal::RadiusDamage( const idVec3 &origin, idEntity *inflictor, idEnt damageDef->GetInt( "push", va( "%d", damage * 100 ), push ); damageDef->GetFloat( "attackerDamageScale", "0.5", attackerDamageScale ); damageDef->GetFloat( "attackerPushScale", "0", attackerPushScale ); + damageDef->GetBool( "notlocalplayer", "1", notlocalplayer ); if ( radius < 1 ) { radius = 1; @@ -3629,6 +3928,10 @@ void idGameLocal::RadiusDamage( const idVec3 &origin, idEntity *inflictor, idEnt ent = entityList[ e ]; assert( ent ); + if ( notlocalplayer && ent == GetLocalPlayer() ) { + continue; + } + if ( !ent->fl.takedamage ) { continue; } @@ -3674,13 +3977,13 @@ void idGameLocal::RadiusDamage( const idVec3 &origin, idEntity *inflictor, idEnt damageScale *= attackerDamageScale; } - ent->Damage( inflictor, attacker, dir, damageDefName, damageScale, INVALID_JOINT ); + ent->Damage( inflictor, attacker, dir, damageDefName, damageScale, INVALID_JOINT, idVec3( 0, 0, 0 ) ); } } // push physics objects if ( push ) { - RadiusPush( origin, radius, push * dmgPower, attacker, ignorePush, attackerPushScale, false ); + RadiusPush( origin, radius, push * dmgPower, attacker, ignorePush, attackerPushScale, false, notlocalplayer, damageDef->GetBool( "notprojectiles", "1" ) ); } } @@ -3689,7 +3992,7 @@ void idGameLocal::RadiusDamage( const idVec3 &origin, idEntity *inflictor, idEnt idGameLocal::RadiusPush ============== */ -void idGameLocal::RadiusPush( const idVec3 &origin, const float radius, const float push, const idEntity *inflictor, const idEntity *ignore, float inflictorScale, const bool quake ) { +void idGameLocal::RadiusPush( const idVec3 &origin, const float radius, const float push, const idEntity *inflictor, const idEntity *ignore, float inflictorScale, const bool quake, const bool notlocalplayer, const bool notprojectiles ) { int i, numListedClipModels; idClipModel *clipModel; idClipModel *clipModelList[ MAX_GENTITIES ]; @@ -3725,8 +4028,12 @@ void idGameLocal::RadiusPush( const idVec3 &origin, const float radius, const fl ent = clipModel->GetEntity(); + if ( notlocalplayer && ent == GetLocalPlayer() ) { + continue; + } + // never push projectiles - if ( ent->IsType( idProjectile::Type ) ) { + if ( notprojectiles && ent->IsType( idProjectile::Type ) ) { continue; } @@ -3979,6 +4286,16 @@ void idGameLocal::SetCamera( idCamera *cam ) { } } } + // HEXEN : Zeroth + // if ( camera ) { + // renderView_t *rv = camera->GetRenderView(); + // Printf( "= camera\n" ); + // if ( rv ) { + // Printf( "==============[ %f, %f ]==============", rv->fov_x, rv->fov_y ); + // } + // } else { + // Printf( "= no camera\n" ); + // } } /* @@ -4181,6 +4498,16 @@ void idGameLocal::RandomizeInitialSpawns( void ) { currentInitialSpot = 0; } +/* +=========== +Zeroth +idGameLocal::SetLocalPlayerSpawnPoint +=========== +*/ +void idGameLocal::SetLocalPlayerSpawnPoint(idStr point) { + eoc_LocalPlayerSpawnPoint = point; +} + /* =========== idGameLocal::SelectInitialSpawnPoint @@ -4198,10 +4525,19 @@ idEntity *idGameLocal::SelectInitialSpawnPoint( idPlayer *player ) { bool alone; if ( !isMultiplayer || !spawnSpots.Num() ) { - spot.ent = FindEntityUsingDef( NULL, "info_player_start" ); + // HEXEN : Zeroth + idStr point = "info_player_start_"; + if ( eoc_LocalPlayerSpawnPoint == "" ) eoc_LocalPlayerSpawnPoint = "1"; + point += eoc_LocalPlayerSpawnPoint; + + spot.ent = FindEntity( point ); + if ( !spot.ent ) { - Error( "No info_player_start on map.\n" ); + Error( "Did not find Player Spawn Point in map: %s", point.c_str() ); } + + eoc_LocalPlayerSpawnPoint = "1"; // should always be 1 by default + return spot.ent; } if ( player->spectating ) { @@ -4316,6 +4652,26 @@ void idGameLocal::ThrottleUserInfo( void ) { mpGame.ThrottleUserInfo(); } +/* +================= +Zeroth +idPlayer::SetPortalSkyEnt +================= +*/ +void idGameLocal::SetPortalSkyEnt( idEntity *ent ) { + portalSkyEnt = ent; +} + +/* +================= +Zeroth +idPlayer::IsPortalSkyAcive +================= +*/ +bool idGameLocal::IsPortalSkyAcive() { + return portalSkyActive; +} + /* =========== idGameLocal::SelectTimeGroup @@ -4409,3 +4765,395 @@ idGameLocal::GetMapLoadingGUI =============== */ void idGameLocal::GetMapLoadingGUI( char gui[ MAX_STRING_CHARS ] ) { } + +/* +=============== +Zeroth +idGameLocal::InitHub +=============== +*/ +void idGameLocal::InitHub( void ) { + idEntity* ent; + idStr name,tmp,name_str,st; + int nt = 0; + idVec3 vc; + idAngles ag; + idMat3 ax; + + for (int i = 0; i < MAX_GENTITIES; i++ ) { + ent = gameLocal.entities[i]; + if ( ent ) { + + name = ent->GetEntityDefName(); + + // find banish locations + if ( name == "speaker" || name == "light" || name.Left(8) == "trigger_" || + name.Left(5) == "ammo_" || name.Left(5) == "path_" ) { + BanishLocationList.AddUnique( new idVec3( ent->GetPhysics()->GetOrigin() ) ); + } + + // remove leaves //z.todo: this is a temporary fix for a clipping issue that + // causes the game to freeze after a savegame load (not crash) + if ( ent->IsType( idEntity_Leaf::Type ) ) { + delete ent; + continue; + } + + // entities which need to be removed + tmp = GetMapName(); + tmp += "_"; + tmp += ent->GetName(); + name_str = tmp; + name_str += "_remove"; + + if ( persistentLevelInfo.GetInt( name_str ) ) { + delete ent; + gameLocal.entities[i]=NULL; + continue; + } + + // idTrigger enable/disable + name_str = tmp; + name_str += "_trig"; + st = persistentLevelInfo.GetString( name_str ); + + if ( st == "on" ) { + static_cast(gameLocal.entities[i])->Enable(); + continue; + } else if ( st == "off" ) { + static_cast(gameLocal.entities[i])->Disable(); + continue; + } + + // idTrigger_Timer enable/disable + name_str = tmp; + name_str += "_timer"; + st = persistentLevelInfo.GetString( name_str ); + + if ( st == "on" ) { + static_cast(gameLocal.entities[i])->Enable(); + continue; + } else if ( st == "off" ) { + static_cast(gameLocal.entities[i])->Disable(); + continue; + } + + // idTrigger_Touch enable/disable + name_str = tmp; + name_str += "_touch"; + st = persistentLevelInfo.GetString( name_str ); + + if ( st == "on" ) { + static_cast(gameLocal.entities[i])->Enable(); + continue; + } else if ( st == "off" ) { + static_cast(gameLocal.entities[i])->Disable(); + continue; + } + + // idTrigger_Count 'count' variable + name_str = tmp; + name_str += "_count_count"; + st = persistentLevelInfo.GetString( name_str ); + + if ( st != "" ) { + nt = persistentLevelInfo.GetInt( name_str ); + static_cast(gameLocal.entities[i])->SetCount( nt ); + continue; + } + + // idTrigger_Count 'goal' variable + name_str = tmp; + name_str += "_count_goal"; + st = persistentLevelInfo.GetString( name_str ); + + if ( st != "" ) { + nt = persistentLevelInfo.GetInt( name_str ); + static_cast(gameLocal.entities[i])->SetGoal( nt ); + continue; + } + + // light broken + name_str = tmp; + name_str += "_light_broken"; + st = persistentLevelInfo.GetString( name_str ); + + if ( st == "1" ) { + static_cast(gameLocal.entities[i])->BecomeBroken( NULL ); + continue; + } + + // light on + name_str = tmp; + name_str += "_light_on"; + st = persistentLevelInfo.GetString( name_str ); + + if ( st == "on" ) { + static_cast(gameLocal.entities[i])->On(); + continue; + } else if ( st == "off" ) { + static_cast(gameLocal.entities[i])->Off(); + continue; + } + + // mover pos + name_str = tmp; + name_str += "_mover_pos"; + st = persistentLevelInfo.GetString( name_str ); + + if ( st != "" ) { + vc = persistentLevelInfo.GetVector( name_str ); + static_cast(gameLocal.entities[i])->GetPhysics()->SetOrigin( vc ); + static_cast(gameLocal.entities[i])->SetDestPos( vc ); + continue; + } + + // mover ang + name_str = tmp; + name_str += "_mover_pos"; + st = persistentLevelInfo.GetString( name_str ); + + if ( st != "" ) { + ag = persistentLevelInfo.GetAngles( name_str ); + ag.Normalize360(); + static_cast(static_cast(gameLocal.entities[i])->GetPhysics())->SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, ag, ang_zero, ang_zero ); + continue; + } + + // moveable pos/ang/vel + name_str = tmp; + name_str += "_moveable_pos"; + st = persistentLevelInfo.GetString( name_str ); + + if ( st != "" ) { + static_cast(static_cast(gameLocal.entities[i])->GetPhysics())->SetOrigin( persistentLevelInfo.GetVector( name_str ) ); + name_str = tmp; + name_str += "_moveable_ang"; + static_cast(gameLocal.entities[i])->SetAngles( persistentLevelInfo.GetAngles( name_str ) ); + name_str = tmp; + name_str += "_moveable_vel"; + static_cast(static_cast(gameLocal.entities[i])->GetPhysics())->SetLinearVelocity( persistentLevelInfo.GetVector( name_str ) ); + continue; + } + } + } + + if ( BanishLocationList.Num() > 0 ) { + BanishLocationList.Shuffle(); + } +} + +/* +=============== +Zeroth +idGameLocal::SendLocalUserHudMessage +=============== +*/ +void idGameLocal::SendLocalUserHudMessage( const char *message ) { + GetLocalPlayer()->ShowHudMessage( message ); +} + +void idGameLocal::SendLocalUserHudMessage( idStr message ) { + GetLocalPlayer()->ShowHudMessage( message.c_str() ); +} + +/* +=============== +Zeroth +idGameLocal::UpdateFog + +Shows or hides fog based on r_fog. +=============== +*/ +void idGameLocal::UpdateFog( void ) { + idStr nam; + idStr fogprefix="fog_"; + bool eoc_fog = r_fog.GetBool(); + + int hash = gameLocal.entypeHash.GenerateKey( idLight::Type.classname, true ); + idLight *light; + + for ( int i = gameLocal.entypeHash.First( hash ); i != -1; i = gameLocal.entypeHash.Next( i ) ) { + if ( gameLocal.entities[i] && !strcmp(gameLocal.entities[i]->GetClassname(), idLight::Type.classname ) ) { + if ( !gameLocal.entities[i]->IsType( idLight::Type ) ) { + continue; + } + + light = static_cast< idLight* >( gameLocal.entities[i] ); + + if ( !light ) { + continue; + } + + nam = light->GetName(); + + if ( fogprefix == nam.Left(4) ) { + if ( eoc_fog ) { + light->On(); + } else { + light->Off(); + } + } + } + } +} + +/* +=============== +Zeroth +idGameLocal::SetPersistentRemove +=============== +*/ +void idGameLocal::SetPersistentRemove( const char *name ) { + // for removing items/creatures/etc after returning to a level (hubs) + idStr name_str; + + name_str = gameLocal.GetMapName(); + name_str += "_"; + name_str += name; + name_str += "_remove"; + + persistentLevelInfo.Set( name_str, "1" ); +} + +/* +=============== +Zeroth +idGameLocal::SetPersistentLightBroken +=============== +*/ +void idGameLocal::SetPersistentLightBroken( const char *name ) { + // for removing items/creatures/etc after returning to a level (hubs) + idStr name_str; + + name_str = gameLocal.GetMapName(); + name_str += "_"; + name_str += name; + name_str += "_light_broken"; + + persistentLevelInfo.Set( name_str, "1" ); +} + +/* +=============== +Zeroth +idGameLocal::SetPersistentLightOn +=============== +*/ +void idGameLocal::SetPersistentLightOn( const char *name, bool state ) { + // for removing items/creatures/etc after returning to a level (hubs) + idStr name_str, st; + + name_str = gameLocal.GetMapName(); + name_str += "_"; + name_str += name; + name_str += "_light_on"; + + if ( state ) { + st = "on"; + } else { + st = "off"; + } + + persistentLevelInfo.Set( name_str, st ); +} + +/* +=============== +Zeroth +idGameLocal::SetPersistentTrigger +=============== +*/ +void idGameLocal::SetPersistentTrigger( const char *type, const char *name, const bool state ) { + // for removing items/creatures/etc after returning to a level (hubs) + idStr name_str, st; + + name_str = gameLocal.GetMapName(); + name_str += "_"; + name_str += name; + name_str += "_"; + name_str += type; + + if ( state ) { + st = "on"; + } else { + st = "off"; + } + + persistentLevelInfo.Set( name_str, st ); +} + +/* +=============== +Zeroth +idGameLocal::SetPersistentTriggerInt +=============== +*/ +void idGameLocal::SetPersistentTriggerInt( const char *type, const char *var, const char *name, int val ) { + // for removing items/creatures/etc after returning to a level (hubs) + idStr name_str, st; + + name_str = gameLocal.GetMapName(); + name_str += "_"; + name_str += name; + name_str += "_"; + name_str += type; + name_str += "_"; + name_str += var; + + persistentLevelInfo.SetInt( name_str, val ); +} + +/* +=============== +Zeroth +idGameLocal::SavePersistentMoveables +=============== +*/ +void idGameLocal::SavePersistentMoveables(void) { + idEntity* ent; + idStr tmp, name_str, st, name; + int nt = 0; + // idVec3 vc; + idAngles ag; + + for (int i = 0; i < MAX_GENTITIES; i++ ) { + ent = gameLocal.entities[i]; + if (ent) { + + name = ent->GetEntityDefName(); + + // find moveables locations + if ( name.Left(9) == "moveable_" ) { + /* + vc = ent->GetPhysics()->GetOrigin(); + if ( vc == ent->spawnArgs.GetVector("origin") ) { + continue; + } + */ + + if ( static_cast( ent )->savePersistentInfo == false ) { + continue; + } + + tmp = GetMapName(); + tmp += "_"; + tmp += ent->GetName(); + name_str = tmp; + name_str += "_moveable_pos"; + + persistentLevelInfo.SetVector( name_str, ent->GetPhysics()->GetOrigin() ); + + name_str = tmp; + name_str += "_moveable_ang"; + + persistentLevelInfo.SetAngles( name_str, ent->GetAngles() ); + + name_str = tmp; + name_str += "_moveable_vel"; + + persistentLevelInfo.SetVector( name_str, ent->GetPhysics()->GetLinearVelocity() ); + } + } + } +} \ No newline at end of file diff --git a/game/Game_local.h b/game/Game_local.h index df7456d..dc68662 100644 --- a/game/Game_local.h +++ b/game/Game_local.h @@ -80,6 +80,10 @@ class idThread; class idEditEntities; class idLocationEntity; +//============================================================================ +// HEXEN : Zeroth - Forward Declarations + + //============================================================================ extern const int NUM_RENDER_PORTAL_BITS; @@ -96,6 +100,13 @@ extern const int NUM_RENDER_PORTAL_BITS; =============================================================================== */ +// HEXEN : Zeroth +struct r_vmodes_type { + int width; + int height; + int ratio; +}; + typedef struct entityState_s { int entityNumber; idBitMsg state; @@ -232,6 +243,7 @@ public: int firstFreeIndex; // first free index in the entities array int num_entities; // current number <= MAX_GENTITIES idHashIndex entityHash; // hash table to quickly find entities by name + idHashIndex entypeHash; // hash table to quickly find entities by type (works in paralel with entityHash) idWorldspawn * world; // world entity idLinkList spawnedEntities; // all spawned entities idLinkList activeEntities; // all thinking entities (idEntity::thinkFlags != 0) @@ -273,6 +285,7 @@ public: int previousTime; // time in msec of last frame int time; // in msec static const int msec = USERCMD_MSEC; // time since last update in milliseconds + bool paused; int vacuumAreaNum; // -1 if level doesn't have any outside areas @@ -294,6 +307,15 @@ public: idEntityPtr lastGUIEnt; // last entity with a GUI, used by Cmd_NextGUI_f int lastGUI; // last GUI on the lastGUIEnt +// HEXEN : Zeroth +public: + idEntityPtr portalSkyEnt; + bool portalSkyActive; + void SetPortalSkyEnt( idEntity *ent ); + bool IsPortalSkyAcive(); + r_vmodes_type r_vmodes[EOC_NUM_VMODES]; + int r_vmode; + // ---------------------- Public idGame Interface ------------------- idGameLocal(); @@ -387,6 +409,9 @@ public: bool InPlayerPVS( idEntity *ent ) const; bool InPlayerConnectedArea( idEntity *ent ) const; +public: + pvsHandle_t GetPlayerPVS() { return playerPVS; }; + void SetCamera( idCamera *cam ); idCamera * GetCamera( void ) const; bool SkipCinematic( void ); @@ -402,12 +427,16 @@ public: static void ArgCompletion_EntityName( const idCmdArgs &args, void(*callback)( const char *s ) ); idEntity * FindTraceEntity( idVec3 start, idVec3 end, const idTypeInfo &c, const idEntity *skip ) const; idEntity * FindEntity( const char *name ) const; + +// HEXEN : Zeroth + idEntity * FindEntityType( const idTypeInfo &type ) const; + idEntity * FindEntityUsingDef( idEntity *from, const char *match ) const; int EntitiesWithinRadius( const idVec3 org, float radius, idEntity **entityList, int maxCount ) const; void KillBox( idEntity *ent, bool catch_teleport = false ); void RadiusDamage( const idVec3 &origin, idEntity *inflictor, idEntity *attacker, idEntity *ignoreDamage, idEntity *ignorePush, const char *damageDefName, float dmgPower = 1.0f ); - void RadiusPush( const idVec3 &origin, const float radius, const float push, const idEntity *inflictor, const idEntity *ignore, float inflictorScale, const bool quake ); + void RadiusPush( const idVec3 &origin, const float radius, const float push, const idEntity *inflictor, const idEntity *ignore, float inflictorScale, const bool quake, const bool notlocalplayer=false, const bool notprojectiles=true ); void RadiusPushClipModel( const idVec3 &origin, const float push, const idClipModel *clipModel ); void ProjectDecal( const idVec3 &origin, const idVec3 &dir, float depth, bool parallel, float size, const char *material, float angle = 0 ); @@ -448,6 +477,31 @@ public: bool NeedRestart(); +// HEXEN : Zeroth +// ****** thanks SnoopJeDi ( http://www.doom3world.org/phpbb2/viewtopic.php?f=56&t=12469&p=214427#p214427 ) + idList musicSpeakers; //SnoopJeDi - holds entitynum values for speakers with s_music set +// ****** + void SetLocalPlayerSpawnPoint(idStr point); +// void FoliageRendering( void ); + idStr eoc_MapPath; + void InitHub(void); + void SendLocalUserHudMessage( const char *message ); + void SendLocalUserHudMessage( idStr message ); + void UpdateFog( void ); + void SetPersistentRemove( const char *name ); + void SetPersistentLightOn( const char *name, bool state ); + void SetPersistentLightBroken( const char *name ); + void SetPersistentTrigger( const char *type, const char *name, const bool state ); + void SetPersistentTriggerInt( const char *type, const char *var, const char *name, int val ); + void SavePersistentMoveables(void); +// HEXEN : Zeroth +public: + idStr eoc_LocalPlayerSpawnPoint; + float eoc_MapLoading; + float eoc_MapLoadingPrev; + idStr mapNameForCheat; + idList BanishLocationList; + private: const static int INITIAL_SPAWN_COUNT = 1; const static int INTERNAL_SAVEGAME_VERSION = 1; // DG: added this for >= 1305 savegames @@ -653,6 +707,7 @@ typedef enum { } gameSoundChannel_t; extern const float DEFAULT_GRAVITY; +const idVec3 DEFAULT_GRAVITY_NORMAL = idVec3( 0, 0, -1 ); // HEXEN : Zeroth extern const idVec3 DEFAULT_GRAVITY_VEC3; extern const int CINEMATIC_SKIP_DELAY; diff --git a/game/Game_network.cpp b/game/Game_network.cpp index d7e2268..47c56b6 100644 --- a/game/Game_network.cpp +++ b/game/Game_network.cpp @@ -589,6 +589,19 @@ void idGameLocal::ServerWriteSnapshot( int clientNum, int sequence, idBitMsg &ms numSourceAreas = gameRenderWorld->BoundsInAreas( spectated->GetPlayerPhysics()->GetAbsBounds(), sourceAreas, idEntity::MAX_PVS_AREAS ); pvsHandle = gameLocal.pvs.SetupCurrentPVS( sourceAreas, numSourceAreas, PVS_NORMAL ); + // HEXEN : Zeroth + // Add portalSky areas to PVS + if ( portalSkyEnt.GetEntity() ) { + pvsHandle_t otherPVS, newPVS; + idEntity *skyEnt = portalSkyEnt.GetEntity(); + + otherPVS = gameLocal.pvs.SetupCurrentPVS( skyEnt->GetPVSAreas(), skyEnt->GetNumPVSAreas() ); + newPVS = gameLocal.pvs.MergeCurrentPVS( pvsHandle, otherPVS ); + pvs.FreeCurrentPVS( pvsHandle ); + pvs.FreeCurrentPVS( otherPVS ); + pvsHandle = newPVS; + } + #if ASYNC_WRITE_TAGS idRandom tagRandom; tagRandom.SetSeed( random.RandomInt() ); @@ -1120,6 +1133,19 @@ void idGameLocal::ClientReadSnapshot( int clientNum, int sequence, const int gam numSourceAreas = gameRenderWorld->BoundsInAreas( spectated->GetPlayerPhysics()->GetAbsBounds(), sourceAreas, idEntity::MAX_PVS_AREAS ); pvsHandle = gameLocal.pvs.SetupCurrentPVS( sourceAreas, numSourceAreas, PVS_NORMAL ); + // HEXEN : Zeroth + // Add portalSky areas to PVS + if ( portalSkyEnt.GetEntity() ) { + pvsHandle_t otherPVS, newPVS; + idEntity *skyEnt = portalSkyEnt.GetEntity(); + + otherPVS = gameLocal.pvs.SetupCurrentPVS( skyEnt->GetPVSAreas(), skyEnt->GetNumPVSAreas() ); + newPVS = gameLocal.pvs.MergeCurrentPVS( pvsHandle, otherPVS ); + pvs.FreeCurrentPVS( pvsHandle ); + pvs.FreeCurrentPVS( otherPVS ); + pvsHandle = newPVS; + } + // read the PVS from the snapshot #if ASYNC_WRITE_PVS int serverPVS[idEntity::MAX_PVS_AREAS]; diff --git a/game/Item.cpp b/game/Item.cpp index 97ae952..2c1337d 100644 --- a/game/Item.cpp +++ b/game/Item.cpp @@ -50,6 +50,18 @@ const idEventDef EV_RespawnFx( "" ); const idEventDef EV_GetPlayerPos( "" ); const idEventDef EV_HideObjective( "", "e" ); const idEventDef EV_CamShot( "" ); +const idEventDef EV_SetNextState( "setNextState", "s" ); +const idEventDef EV_SetState( "setState", "s" ); +const idEventDef EV_GetState( "getState", NULL, 's' ); +// HEXEN : Zeroth +const idEventDef EV_ArtifactStart( "ArtifactStart" ); +const idEventDef EV_ArtifactCoolDown( "ArtifactCoolDown" ); +const idEventDef EV_ArtifactDone( "ArtifactDone" ); +const idEventDef EV_SetArtifactActive( "ArtifactActive", "f" ); +const idEventDef EV_OwnerLaunchProjectiles( "OwnerLaunchProjectiles", "dffff" ); +const idEventDef EV_OwnerCreateProjectile( "OwnerCreateProjectile", NULL, 'e' ); +const idEventDef EV_GetOwner( "GetOwner", NULL, 'e' ); +const idEventDef EV_HideMultiModel( "HideMultiModel" ); CLASS_DECLARATION( idEntity, idItem ) EVENT( EV_DropToFloor, idItem::Event_DropToFloor ) @@ -57,6 +69,18 @@ CLASS_DECLARATION( idEntity, idItem ) EVENT( EV_Activate, idItem::Event_Trigger ) EVENT( EV_RespawnItem, idItem::Event_Respawn ) EVENT( EV_RespawnFx, idItem::Event_RespawnFx ) +// EVENT( EV_SetNextState, idItem::Event_SetNextState ) +// EVENT( EV_SetState, idItem::Event_SetState ) +// EVENT( EV_GetState, idItem::Event_GetState ) +// HEXEN : Zeroth + EVENT( EV_ArtifactStart, idItem::Event_ArtifactStart ) + EVENT( EV_ArtifactDone, idItem::Event_ArtifactDone ) + EVENT( EV_ArtifactCoolDown, idItem::Event_ArtifactCoolDown ) + EVENT( EV_SetArtifactActive, idItem::Event_SetArtifactActive ) + EVENT( EV_OwnerLaunchProjectiles, idItem::Event_OwnerLaunchProjectiles ) + EVENT( EV_OwnerCreateProjectile, idItem::Event_OwnerCreateProjectile ) + EVENT( EV_GetOwner, idItem::Event_GetOwner ) + EVENT( EV_HideMultiModel, idItem::Event_HideMultiModel ) END_CLASS @@ -76,6 +100,12 @@ idItem::idItem() { orgOrigin.Zero(); canPickUp = true; fl.networkSync = true; + + owner = NULL; + DeleteMe = false; + Cooling = false; + ArtifactActive = false; + Processing = false; } /* @@ -88,6 +118,99 @@ idItem::~idItem() { if ( itemShellHandle != -1 ) { gameRenderWorld->FreeEntityDef( itemShellHandle ); } + + if ( multimodel ) { + delete multimodel; + } + + //z.todo: necessary??? + // delete scriptThread; + // DeconstructScriptObject(); + // scriptObject.Free(); +} + +/* +================ +HEXEN +idItem::Hide +================ +*/ +void idItem::Hide( void ) { + if ( !idEntity::IsHidden() ) { + idEntity::Hide(); + if ( multimodel ) { + multimodel->Hide(); + } + } +} + +/* +================ +HEXEN +idItem::Event_ArtifactStart +================ +*/ +void idItem::Event_ArtifactStart( void ) { + if ( owner != NULL ) { + Processing = true; + + owner->RemoveInventoryItem(spawnArgs.GetString("inv_name")); + owner->UpdateHudArtifacts(); + } +} + +/* +================ +HEXEN +idItem::Event_ArtifactDone +================ +*/ +void idItem::Event_ArtifactDone( void ) { + if ( owner != NULL ) { + Cooling = false; + DeleteMe = true; + } +} + +/* +================ +HEXEN +idItem::Event_ArtifactCoolDown +================ +*/ +void idItem::Event_ArtifactCoolDown( void ) { + if ( owner != NULL ) { + Cooling = true; + owner->UpdateHudActiveArtifacts(); + } +} + +/* +================ +HEXEN +idItem::Event_SetArtifactActive +================ +*/ +void idItem::Event_SetArtifactActive( const float yesorno ) { + if ( owner != NULL ) { + if ( yesorno ) { + ArtifactActive = true; + owner->UpdateHudActiveArtifacts(); + } else { + ArtifactActive = false; + owner->UpdateHudActiveArtifacts(); + } + } +} + +/* +================ +HEXEN +idItem::Event_GetOwner +================ +*/ +void idItem::Event_GetOwner( void ) { + idThread::ReturnEntity( owner ); } /* @@ -108,6 +231,42 @@ void idItem::Save( idSaveGame *savefile ) const { savefile->WriteInt( inViewTime ); savefile->WriteInt( lastCycle ); savefile->WriteInt( lastRenderViewTime ); + + // HEXEN : Zeroth + savefile->WriteObject( scriptThread ); + savefile->WriteString( waitState ); + + //FIXME: this is unneccesary + idToken token; + // HEXEN : Zeroth + if ( state ) { + idLexer src( state->Name(), idStr::Length( state->Name() ), "idItem::Save" ); + + src.ReadTokenOnLine( &token ); + src.ExpectTokenString( "::" ); + src.ReadTokenOnLine( &token ); + + savefile->WriteString( token ); + } else { + savefile->WriteString( "" ); + } + + // HEXEN : Zeroth + if ( idealState ) { + idLexer src( idealState->Name(), idStr::Length( idealState->Name() ), "idItem::Save" ); + + src.ReadTokenOnLine( &token ); + src.ExpectTokenString( "::" ); + src.ReadTokenOnLine( &token ); + + savefile->WriteString( token ); + } else { + savefile->WriteString( "" ); + } + + // HEXEN : Zeroth + savefile->WriteObject( projectileEnt ); + savefile->WriteObject( multimodel ); } /* @@ -130,6 +289,35 @@ void idItem::Restore( idRestoreGame *savefile ) { savefile->ReadInt( lastRenderViewTime ); itemShellHandle = -1; + + // HEXEN : Zeroth + savefile->ReadObject( reinterpret_cast( scriptThread ) ); + savefile->ReadString( waitState ); + idStr statename; + + // HEXEN : Zeroth + savefile->ReadString( statename ); + if ( statename.Length() > 0 ) { + state = GetScriptFunction( statename ); + } + + // HEXEN : Zeroth + savefile->ReadString( statename ); + if ( statename.Length() > 0 ) { + idealState = GetScriptFunction( statename ); + } + + // HEXEN : Zeroth + const idDeclEntityDef *projectileDef = gameLocal.FindEntityDef( spawnArgs.GetString( "def_projectile" ), false ); + if ( projectileDef ) { + projectileDict = projectileDef->dict; + } else { + projectileDict.Clear(); + } + + // HEXEN : Zeroth + savefile->ReadObject( reinterpret_cast( projectileEnt ) ); + savefile->ReadObject( reinterpret_cast( multimodel ) ); } /* @@ -227,7 +415,7 @@ void idItem::Think( void ) { ang.yaw = ( gameLocal.time & 4095 ) * 360.0f / -4096.0f; SetAngles( ang ); - float scale = 0.005f + entityNumber * 0.00001f; + float scale = 0.005f; org = orgOrigin; org.z += 4.0f + cos( ( gameLocal.time + 2000 ) * scale ) * 4.0f; @@ -236,6 +424,11 @@ void idItem::Think( void ) { } Present(); + + if ( !IsHidden() && multimodel ) { + multimodel->GetPhysics()->SetOrigin( GetPhysics()->GetOrigin() ); // bob up and down in unison + multimodel->SetAngles( -GetAngles() ); // make it spin the opposite direction + } } /* @@ -275,6 +468,30 @@ void idItem::Spawn( void ) { idStr giveTo; idEntity * ent; float tsize; + const char *projectileName; + + state = NULL; + idealState = NULL; + + // HEXEN : Zeroth + // get the projectile + projectileDict.Clear(); + + projectileName = spawnArgs.GetString( "def_projectile" ); + if ( projectileName[0] != '\0' ) { + const idDeclEntityDef *projectileDef = gameLocal.FindEntityDef( projectileName, false ); + if ( !projectileDef ) { + gameLocal.Warning( "Unknown projectile '%s' in item '%s'", projectileName, spawnArgs.GetString("inv_name") ); + } else { + const char *spawnclass = projectileDef->dict.GetString( "spawnclass" ); + idTypeInfo *cls = idClass::GetClass( spawnclass ); + if ( !cls || !cls->IsType( idProjectile::Type ) ) { + gameLocal.Warning( "Invalid spawnclass '%s' on projectile '%s' (used by item '%s')", spawnclass, projectileName, spawnArgs.GetString("inv_name") ); + } else { + projectileDict = projectileDef->dict; + } + } + } if ( spawnArgs.GetBool( "dropToFloor" ) ) { PostEventMS( &EV_DropToFloor, 0 ); @@ -317,6 +534,18 @@ void idItem::Spawn( void ) { lastCycle = -1; itemShellHandle = -1; shellMaterial = declManager->FindMaterial( "itemHighlightShell" ); + + multimodel=NULL; + idStr mstr=spawnArgs.GetString("multimodel"); + + if ( mstr != "" ) { + const idDict *multimodeldef = gameLocal.FindEntityDefDict( mstr.c_str() ); + if ( gameLocal.SpawnEntityDef( *multimodeldef, &multimodel ) && multimodel ) { + multimodel->GetPhysics()->SetOrigin( GetPhysics()->GetOrigin() ); // bob up and down in unison + multimodel->SetAngles( -GetAngles() ); // make it spin the opposite direction + spawnArgs.Set( "multimodel_name", multimodel->GetName() ); + } + } } /* @@ -346,11 +575,26 @@ bool idItem::GiveToPlayer( idPlayer *player ) { return false; } + bool val = false; + if ( spawnArgs.GetBool( "inv_carry" ) ) { - return player->GiveInventoryItem( &spawnArgs ); + val = player->GiveInventoryItem( this ); + } else { + val = player->GiveItem( this ); } - return player->GiveItem( this ); + if ( val ) { + if ( spawnArgs.GetString("scriptobject") != "" ) { + if ( !g_noPickupNotification.GetBool() && !spawnArgs.GetBool("dontNotifyOnPickup") ) { + CallFunc( "pickup_message" ); + } + CallFunc( "pickup_effect" ); + } + + gameLocal.SetPersistentRemove( name.c_str() ); + } + + return val; } /* @@ -369,7 +613,9 @@ bool idItem::Pickup( idPlayer *player ) { } // play pickup sound - StartSound( "snd_acquire", SND_CHANNEL_ITEM, 0, false, NULL ); + if ( !g_noPickupNotification.GetBool() ) { + StartSound( "snd_acquire", SND_CHANNEL_ITEM, 0, false, NULL ); + } // trigger our targets ActivateTargets( player ); @@ -508,15 +754,46 @@ idItem::Event_Touch ================ */ void idItem::Event_Touch( idEntity *other, trace_t *trace ) { + idPlayer *player; + if ( !other->IsType( idPlayer::Type ) ) { return; } + player = static_cast( other ); + if ( !canPickUp ) { return; } - Pickup( static_cast(other) ); + + player->CleanupArtifactItems(); + + // pickup delay for this player, to prevent instant pickup after drop. + + if ( player == lastOwner && spawnArgs.GetBool( "eoc_dropped" ) ) { + if ( PickupDelayTime < MS2SEC( gameLocal.realClientTime ) ) { + return; + } + } + + // I don't know why this happens, but it does. seems the player can hit an item multiple times and trigger this pickupevent before he has a chance to disappear after picking it up the first time. + if ( player == owner ) { + return; + } + + if ( spawnArgs.GetBool( "instantEffect" ) && player->ActiveArtifact( spawnArgs.GetString( "inv_name" ) ) ) { + return; + } + + // don't pickup if we're full on 'em + if ( spawnArgs.FindKey("artifact") ) { + if ( spawnArgs.GetInt( "max_inventory" ) > 0 && player->InventoryItemQty(spawnArgs.GetString( "inv_name" )) >= spawnArgs.GetInt( "max_inventory" ) ) { + return; + } + } + + Pickup( static_cast( other ) ); } /* @@ -637,9 +914,141 @@ bool idItemPowerup::GiveToPlayer( idPlayer *player ) { return false; } player->GivePowerUp( type, time * 1000 ); + gameLocal.SetPersistentRemove( name.c_str() ); return true; } +/* +================ +HEXEN +idItem::Event_OwnerLaunchProjectiles +================ +*/ +void idItem::Event_OwnerLaunchProjectiles( int num_projectiles, float spread, float fuseOffset, float launchPower, float dmgPower ) { + idProjectile *proj; + idEntity *ent; + int i; + idVec3 dir; + float ang; + float spin; + float distance; + trace_t tr; + idVec3 start; + idVec3 muzzle_pos; + idBounds ownerBounds, projBounds; + + idVec3 playerViewOrigin; + idMat3 playerViewAxis; + idVec3 zzero; + + zzero.Zero(); + + playerViewOrigin.Zero(); + playerViewAxis.Zero(); + + playerViewOrigin = owner->firstPersonViewOrigin; + playerViewAxis = owner->firstPersonViewAxis; + + if ( !projectileDict.GetNumKeyVals() ) { + const char *classname = this->spawnArgs.GetString("inv_name"); + gameLocal.Warning( "No projectile defined on '%s'", classname ); + return; + } + + if ( gameLocal.isClient ) { + // predict instant hit projectiles + if ( projectileDict.GetBool( "net_instanthit" ) ) { + float spreadRad = DEG2RAD( spread ); + muzzle_pos = playerViewOrigin + playerViewAxis[ 0 ] * 2.0f; + for( i = 0; i < num_projectiles; i++ ) { + ang = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() ); + spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat(); + dir = playerViewAxis[ 0 ] + playerViewAxis[ 2 ] * ( ang * idMath::Sin( spin ) ) - playerViewAxis[ 1 ] * ( ang * idMath::Cos( spin ) ); + dir.Normalize(); + gameLocal.clip.Translation( tr, muzzle_pos, muzzle_pos + dir * 4096.0f, NULL, mat3_identity, MASK_SHOT_RENDERMODEL, owner ); + if ( tr.fraction < 1.0f ) { + idProjectile::ClientPredictionCollide( this, projectileDict, tr, vec3_origin, true ); + } + } + } + + } else { + + ownerBounds = owner->GetPhysics()->GetAbsBounds(); + + owner->AddProjectilesFired( num_projectiles ); + + float spreadRad = DEG2RAD( spread ); + for( i = 0; i < num_projectiles; i++ ) { + ang = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() ); + spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat(); + dir = playerViewAxis[ 0 ] + playerViewAxis[ 2 ] * ( ang * idMath::Sin( spin ) ) - playerViewAxis[ 1 ] * ( ang * idMath::Cos( spin ) ); + dir.Normalize(); + + if ( projectileEnt ) { + ent = projectileEnt; + ent->Show(); + ent->Unbind(); + projectileEnt = NULL; + } else { + gameLocal.SpawnEntityDef( projectileDict, &ent, false ); + } + + if ( !ent || !ent->IsType( idProjectile::Type ) ) { + const char *projectileName = this->spawnArgs.GetString( "def_projectile" ); + gameLocal.Error( "'%s' is not an idProjectile", projectileName ); + } + + if ( projectileDict.GetBool( "net_instanthit" ) ) { + // don't synchronize this on top of the already predicted effect + ent->fl.networkSync = false; + } + + proj = static_cast(ent); + proj->Create( owner, playerViewOrigin, dir ); + + projBounds = proj->GetPhysics()->GetBounds().Rotate( proj->GetPhysics()->GetAxis() ); + + // make sure the projectile starts inside the bounding box of the owner + if ( i == 0 ) { + muzzle_pos = playerViewOrigin + playerViewAxis[ 0 ] * 2.0f; + if ( ( ownerBounds - projBounds).RayIntersection( muzzle_pos, playerViewAxis[0], distance ) ) { + start = muzzle_pos + distance * playerViewAxis[0]; + } else { + start = ownerBounds.GetCenter(); + } + gameLocal.clip.Translation( tr, start, muzzle_pos, proj->GetPhysics()->GetClipModel(), proj->GetPhysics()->GetClipModel()->GetAxis(), MASK_SHOT_RENDERMODEL, owner ); + muzzle_pos = tr.endpos; + } + + proj->Launch( muzzle_pos, dir, zzero, fuseOffset, launchPower, dmgPower ); + } + + } +} + +/* +================ +HEXEN +idItem::Event_OwnerCreateProjectile +================ +*/ +void idItem::Event_OwnerCreateProjectile( void ) { + if ( !gameLocal.isClient ) { + projectileEnt = NULL; + gameLocal.SpawnEntityDef( projectileDict, &projectileEnt, false ); + if ( projectileEnt ) { + projectileEnt->SetOrigin( GetPhysics()->GetOrigin() ); + projectileEnt->Bind( owner, false ); + projectileEnt->Hide(); + } + idThread::ReturnEntity( projectileEnt ); + } else { + idThread::ReturnEntity( NULL ); + } +} + + /* =============================================================================== @@ -712,6 +1121,43 @@ void idObjective::Event_CamShot( ) { renderView_t fullView = *view; fullView.width = SCREEN_WIDTH; fullView.height = SCREEN_HEIGHT; + + // HEXEN : Zeroth + // HACK : always draw sky-portal view if there is one in the map, this isn't real-time + if ( gameLocal.portalSkyEnt.GetEntity() && g_enablePortalSky.GetBool() ) { + renderView_t portalView = fullView; + portalView.vieworg = gameLocal.portalSkyEnt.GetEntity()->GetPhysics()->GetOrigin(); + + // setup global fixup projection vars + #if 1 + int vidWidth, vidHeight; + idVec2 shiftScale; + + renderSystem->GetGLSettings( vidWidth, vidHeight ); + + float pot; + int temp; + + int w = vidWidth; + for (temp = 1 ; temp < w ; temp<<=1) { + } + pot = (float)temp; + shiftScale.x = (float)w / pot; + + int h = vidHeight; + for (temp = 1 ; temp < h ; temp<<=1) { + } + pot = (float)temp; + shiftScale.y = (float)h / pot; + + fullView.shaderParms[4] = shiftScale.x; + fullView.shaderParms[5] = shiftScale.y; + #endif + + gameRenderWorld->RenderScene( &portalView ); + renderSystem->CaptureRenderToImage( "_currentRender" ); + } + // draw a view to a texture renderSystem->CropRenderSize( 256, 256, true ); gameRenderWorld->RenderScene( &fullView ); @@ -821,6 +1267,7 @@ bool idVideoCDItem::GiveToPlayer( idPlayer *player ) { if ( player && str.Length() ) { player->GiveVideo( str, &spawnArgs ); } + gameLocal.SetPersistentRemove( name.c_str() ); return true; } @@ -845,6 +1292,7 @@ bool idPDAItem::GiveToPlayer(idPlayer *player) { if ( player ) { player->GivePDA( str, &spawnArgs ); } + gameLocal.SetPersistentRemove( name.c_str() ); return true; } @@ -1203,6 +1651,7 @@ bool idMoveablePDAItem::GiveToPlayer(idPlayer *player) { if ( player ) { player->GivePDA( str, &spawnArgs ); } + gameLocal.SetPersistentRemove( name.c_str() ); return true; } @@ -1356,3 +1805,51 @@ void idObjectiveComplete::Event_HideObjective( idEntity *e ) { } } } + +/* +================ +HEXEN +idItem::SetOwner +================ +*/ +void idItem::SetOwner( idPlayer *_owner ) { + assert( !owner ); + owner = _owner; + lastOwner = _owner; + // SetName( va( "%s_weapon", owner->name.c_str() ) ); + + // if ( worldModel.GetEntity() ) { + // worldModel.GetEntity()->SetName( va( "%s_weapon_worldmodel", owner->name.c_str() ) ); + // } +} + +/* +================ +HEXEN +idItem::CallFunc +================ +*/ +bool idItem::CallFunc( char *funcName ) { + const function_t *func = GetScriptFunction( (const char*) funcName ); + if ( !func ) { + assert( 0 ); + gameLocal.Error( "Can't find function use' in object '%s'", scriptObject.GetTypeName() ); + return false; + } + + SetState( func ); + UpdateScript(); + return true; +} + +/* +================ +HEXEN +idItem::Event_HideMultiModel +================ +*/ +void idItem::Event_HideMultiModel() { + if ( multimodel ) { + multimodel->Hide(); + } +} diff --git a/game/Item.h b/game/Item.h index acf7c0c..d19051f 100644 --- a/game/Item.h +++ b/game/Item.h @@ -56,6 +56,14 @@ public: virtual bool Pickup( idPlayer *player ); virtual void Think( void ); virtual void Present(); + void Hide(); + +// HEXEN : Zeroth +public: + void SetOwner( idPlayer *owner ); + idPlayer* GetOwner( void ); + idPlayer* GetLastOwner( void ); + bool CallFunc( char *funcName ); enum { EVENT_PICKUP = idEntity::EVENT_MAXEVENTS, @@ -71,6 +79,20 @@ public: virtual void WriteToSnapshot( idBitMsgDelta &msg ) const; virtual void ReadFromSnapshot( const idBitMsgDelta &msg ); +// HEXEN : Zeroth +public: + bool DeleteMe; // whether this artifact should be deleted in the next artifact cleanup (in player.cpp) + bool ArtifactActive; // whether this artifact is active (valid for time-based effects) + bool Processing; // whether this artifacts script is busy + bool Cooling; // whether artifact is in cooldown mode + int PickupDelayTime; // time in seconds for how long we should wait before letting a player pick up the item he dropped (necessary to prevent instant pickup afte drop) + +// HEXEN : Zeroth +private: + idPlayer* owner; + idPlayer* lastOwner; + + private: idVec3 orgOrigin; bool spin; @@ -95,6 +117,27 @@ private: void Event_Trigger( idEntity *activator ); void Event_Respawn( void ); void Event_RespawnFx( void ); + +// HEXEN : Zeroth +private: + idDict projectileDict; + idEntity *projectileEnt; +// void Event_GetState( void ); +// void Event_SetState( const char *name ); +// void Event_SetNextState( const char *name ); + void Event_ArtifactStart( void ); + void Event_Artifact( void ); + void Event_ArtifactDone( void ); + void Event_ArtifactCoolDown( void ); + void Event_SetArtifactActive( const float yesorno ); + void Event_OwnerLaunchProjectiles( int num_projectiles, float spread, float fuseOffset, float launchPower, float dmgPower ); + void Event_OwnerCreateProjectile( void ); + void Event_GetOwner( void ); + +// MultiModel + void Event_HideMultiModel( void ); +public: + idEntity *multimodel; }; class idItemPowerup : public idItem { @@ -226,4 +269,12 @@ private: void Event_GetPlayerPos(); }; +ID_INLINE idPlayer* idItem::GetOwner( void ) { + return owner; +} + +ID_INLINE idPlayer* idItem::GetLastOwner( void ) { + return lastOwner; +} + #endif /* !__GAME_ITEM_H__ */ diff --git a/game/Light.cpp b/game/Light.cpp index cc24a42..c74aaad 100644 --- a/game/Light.cpp +++ b/game/Light.cpp @@ -567,6 +567,7 @@ idLight::On ================ */ void idLight::On( void ) { + gameLocal.SetPersistentLightOn( name.c_str(), true ); currentLevel = levels; // offset the start time of the shader to sync it to the game time renderLight.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time ); @@ -584,6 +585,7 @@ idLight::Off ================ */ void idLight::Off( void ) { + gameLocal.SetPersistentLightOn( name.c_str(), false ); currentLevel = 0; // kill any sound it was making if ( refSound.referenceSound && refSound.referenceSound->CurrentlyPlaying() ) { @@ -646,6 +648,7 @@ idLight::BecomeBroken ================ */ void idLight::BecomeBroken( idEntity *activator ) { + gameLocal.SetPersistentLightBroken( name.c_str() ); const char *damageDefName; fl.takedamage = false; diff --git a/game/Misc.cpp b/game/Misc.cpp index bfde719..fecf2f8 100644 --- a/game/Misc.cpp +++ b/game/Misc.cpp @@ -2305,13 +2305,60 @@ CLASS_DECLARATION( idEntity, idLiquid ) EVENT( EV_Touch, idLiquid::Event_Touch ) END_CLASS +/* +================ +Zeroth +idLiquid::idLiquid +================ +*/ +idLiquid::idLiquid( void ) { + clipModel = NULL; + touchingEntities.Clear(); + flagEntities.Clear(); + //resistTimeEntities.Clear(); + + // HEXEN : Zeroth - for limit entity types + dontTripby_LocalPlayer = false; + limitEntityType = false; + tripby_idPlayer = false; + tripby_idAI = false; + tripby_idMoveable = false; + tripby_idItem = false; + tripby_idActor = false; + tripby_idProjectile = false; + + particleOnExit = false; // whether to spawn a particle when something leaves the water + moveResistance = 0; + moveDir.Zero(); + moveAmt = 0; +} + /* ================ idLiquid::Save ================ */ void idLiquid::Save( idSaveGame *savefile ) const { - // Nothing to save + // HEXEN : Zeroth + savefile->WriteClipModel( clipModel ); + savefile->WriteBool( limitEntityType ); + savefile->WriteBool( dontTripby_LocalPlayer ); + savefile->WriteBool( tripby_idPlayer ); + savefile->WriteBool( tripby_idAI ); + savefile->WriteBool( tripby_idMoveable ); + savefile->WriteBool( tripby_idItem ); + savefile->WriteBool( tripby_idActor ); + savefile->WriteBool( tripby_idProjectile ); + savefile->WriteBool( particleOnExit ); + savefile->WriteVec3( moveDir ); + savefile->WriteFloat( moveAmt ); + savefile->WriteFloat( moveResistance ); + + savefile->WriteInt( touchingEntities.Num() ); + for ( int i = 0; i < touchingEntities.Num(); i++ ) { + savefile->WriteString( touchingEntities[i] ); + savefile->WriteBool( flagEntities[i] ); + } } /* @@ -2322,6 +2369,32 @@ idLiquid::Restore void idLiquid::Restore( idRestoreGame *savefile ) { //FIXME: NO! Spawn(); + + // HEXEN : Zeroth + savefile->ReadClipModel( clipModel ); + savefile->ReadBool( limitEntityType ); + savefile->ReadBool( dontTripby_LocalPlayer ); + savefile->ReadBool( tripby_idPlayer ); + savefile->ReadBool( tripby_idAI ); + savefile->ReadBool( tripby_idMoveable ); + savefile->ReadBool( tripby_idItem ); + savefile->ReadBool( tripby_idActor ); + savefile->ReadBool( tripby_idProjectile ); + savefile->ReadBool( particleOnExit ); + savefile->ReadVec3( moveDir ); + savefile->ReadFloat( moveAmt ); + savefile->ReadFloat( moveResistance ); + + bool bol; + int num; + idStr str; + savefile->ReadInt( num ); + for ( int i = 0; i < num; i++ ) { + savefile->ReadString( str ); + touchingEntities.Append( str ); + savefile->ReadBool( bol ); + flagEntities.Append( bol ); + } } /* @@ -2338,6 +2411,56 @@ void idLiquid::Spawn() { model->Reset(); GetPhysics()->SetContents( CONTENTS_TRIGGER ); */ + // HEXEN : Zeroth - create clip model. copied from idTrigger_Touch + clipModel = new idClipModel( GetPhysics()->GetClipModel() ); // get the clip model + //GetPhysics()->SetClipModel( NULL, 1.0f ); // remove the collision model from the physics object + BecomeActive( TH_THINK ); + + // HEXEN : Zeroth - for limit entity types + spawnArgs.GetBool( "dontTripby_LocalPlayer", "0", dontTripby_LocalPlayer ); + spawnArgs.GetBool( "limitEntityType", "0", limitEntityType ); + spawnArgs.GetBool( "tripby_idPlayer", "0", tripby_idPlayer ); + spawnArgs.GetBool( "tripby_idAI", "0", tripby_idAI ); + spawnArgs.GetBool( "tripby_idMoveable", "0", tripby_idMoveable ); + spawnArgs.GetBool( "tripby_idItem", "0", tripby_idItem ); + spawnArgs.GetBool( "tripby_idActor", "0", tripby_idActor ); + spawnArgs.GetBool( "tripby_idProjectile", "0", tripby_idProjectile ); + spawnArgs.GetBool( "doParticleOnExit", "0", particleOnExit ); + + splashParticleRipple = NULL; + splashParticleTiny = NULL; + splashParticleSmall = NULL; + splashParticleBig = NULL; + splashParticleHuge = NULL; + + const char *splashName = spawnArgs.GetString( "smoke_ripple" ); + if ( *splashName != '\0' ) { + splashParticleRipple = static_cast( declManager->FindType( DECL_PARTICLE, splashName ) ); + } + + splashName = spawnArgs.GetString( "smoke_splashTiny" ); + if ( *splashName != '\0' ) { + splashParticleTiny = static_cast( declManager->FindType( DECL_PARTICLE, splashName ) ); + } + + splashName = spawnArgs.GetString( "smoke_splashSmall" ); + if ( *splashName != '\0' ) { + splashParticleSmall = static_cast( declManager->FindType( DECL_PARTICLE, splashName ) ); + } + + splashName = spawnArgs.GetString( "smoke_splashBig" ); + if ( *splashName != '\0' ) { + splashParticleBig = static_cast( declManager->FindType( DECL_PARTICLE, splashName ) ); + } + + splashName = spawnArgs.GetString( "smoke_splashHuge" ); + if ( *splashName != '\0' ) { + splashParticleHuge = static_cast( declManager->FindType( DECL_PARTICLE, splashName ) ); + } + + spawnArgs.GetVector( "moveDir", "0 0 0", moveDir ); + spawnArgs.GetFloat( "moveAmt", "0", moveAmt ); + spawnArgs.GetFloat( "moveResistance", "0", moveResistance ); } /* @@ -2355,6 +2478,201 @@ void idLiquid::Event_Touch( idEntity *other, trace_t *trace ) { */ } +/* +================ +Zeroth +idLiquid::Event_Touch +================ +*/ +void idLiquid::TouchEntities( void ) { // Z.TODO: this is getting messy, maybe split it into separate entities? + int numClipModels, i, c; + idBounds bounds; + idClipModel *cm, *clipModelList[ MAX_GENTITIES ]; + + // HEXEN : Zeroth - for limit entity type + idEntity *entity = NULL; + bool dontSplash; + idVec3 cmOrigin; + idMat3 cmAxis; + cmHandle_t cmHandle; + idVec3 myOrigin; + idMat3 myAxis; + + if ( clipModel == NULL ) { + return; + } + + for ( c = 0; c < touchingEntities.Num(); c++ ) { + entity = gameLocal.FindEntity( touchingEntities[c].c_str() ); + // remove entities from list which are no longer touching or no longer exist + if ( !flagEntities[c] || !entity ) { + if ( entity ) { + if ( !gameLocal.isClient ) { + // entity->StartSound( "snd_splashexit", SND_CHANNEL_ANY, 0, false, NULL ); + const idSoundShader *shader = declManager->FindSound( spawnArgs.GetString( "snd_splashexit" ) ); + entity->StartSoundShader( shader, SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL ); + + + // spawn the exit splash + if ( particleOnExit ) { + // gameLocal.smokeParticles->EmitSmoke( splashParticle, gameLocal.time, gameLocal.random.CRandomFloat(), entity->GetPhysics()->GetOrigin(), idAngles( 0, gameLocal.random.CRandomFloat() * 360, 0 ).ToMat3() ); + } + + entity->inWater = false; + if ( entity->IsType( idPlayer::Type ) ) { + static_cast< idPlayer* >( entity )->leftWater = gameLocal.time; + } + } + } + + touchingEntities.RemoveIndex( c ); + touchingEntities.Condense(); + flagEntities.RemoveIndex( c ); + flagEntities.Condense(); + c--; + } else { + // flag all entities as NOT touching, test if they are during the clip tests + flagEntities[c] = false; + } + } + + bounds.FromTransformedBounds( clipModel->GetBounds(), clipModel->GetOrigin(), clipModel->GetAxis() ); + numClipModels = gameLocal.clip.ClipModelsTouchingBounds( bounds, -1, clipModelList, MAX_GENTITIES ); + + for ( i = 0; i < numClipModels; i++ ) { + cm = clipModelList[ i ]; + if ( !cm->IsTraceModel() ) { + continue; + } + + entity = clipModelList[ i ]->GetEntity(); + if ( !entity ) { + continue; + } + + if ( entity->spawnArgs.GetBool("nosplash") ) { + continue; + } + + cmOrigin = cm->GetOrigin(); + cmAxis = cm->GetAxis(); + cmHandle = clipModel->Handle(); + myOrigin = clipModel->GetOrigin(); + myAxis = clipModel->GetAxis(); + + if ( !gameLocal.clip.ContentsModel( cmOrigin, cm, cmAxis, -1, cmHandle, myOrigin, myAxis ) ) { + continue; + } + + // get the intersection point so we know where to spawn the effect + // gameLocal.clip.Translation( trace, cmOrigin, cmOrigin, cm, cmAxis, -1, this ); + + if (limitEntityType) { + if ( ( !tripby_idPlayer && entity->IsType( idPlayer::Type ) ) || + ( !tripby_idAI && entity->IsType( idAI::Type ) ) || + ( !tripby_idActor && entity->IsType( idActor::Type ) ) || + ( !tripby_idProjectile && entity->IsType( idProjectile::Type ) ) || + ( !tripby_idItem && entity->IsType( idItem::Type ) ) || + ( !tripby_idMoveable && entity->IsType( idMoveable::Type ) ) ) { + continue; + } + } + + if ( dontTripby_LocalPlayer && entity->IsType( idPlayer::Type ) && gameLocal.GetLocalPlayer() == ( static_cast( entity ) ) ) { + continue; + } + + // if the entity is still touching, dont splash + dontSplash = false; + for ( c = 0; c < touchingEntities.Num(); c++ ) { + if ( touchingEntities[c] == entity->GetName() ) { + flagEntities[c] = true; // flag it as touching + dontSplash = true; + entity->inWater = true; // if the entity leaves another body of water, it will be set to false. it's still in this one, so keep it true. + break; + } + } + + // if the entity was not in our list, add it. else don't trigger. + if ( !dontSplash ) { + touchingEntities.Append( idStr( entity->GetName() ) ); + flagEntities.Append( true ); + } else { + continue; + } + + //if the entity is already in water, dont splash + if ( entity->inWater ) { + continue; + } + + entity->inWater = true; + + if ( !gameLocal.isClient ) { + const idDeclParticle * prt = NULL; + idVec3 v = entity->GetPhysics()->GetLinearVelocity(); + float vel = v.Length(); + + int whichPrt, maxPrt; + float m = entity->GetPhysics()->GetMass(); + // static const unsigned long double const * foo = NULL; // hahaha! + float a = spawnArgs.GetFloat( "rippleObjectMass" ); + float b = spawnArgs.GetFloat( "tinyObjectMass" ); + float c = spawnArgs.GetFloat( "smallObjectMass" ); + float d = spawnArgs.GetFloat( "bigObjectMass" ); + float e = spawnArgs.GetFloat( "largeObjectMass" ); + + if ( m <= a ) { + maxPrt = 0; + } else if ( m <= b ) { + maxPrt = 1; + } else if ( m <= c ) { + maxPrt = 2; + } else if ( m <= d ) { + maxPrt = 3; + } else { + maxPrt = 4; + } + + whichPrt = ( vel / spawnArgs.GetFloat( "velocityForMaxiumSplash" ) ) * maxPrt; + + if ( maxPrt == 0) { + prt = splashParticleRipple; + } else if ( whichPrt == 1 || maxPrt == 1) { + prt = splashParticleTiny; + } else if ( whichPrt == 2 || maxPrt == 2 ) { + prt = splashParticleSmall; + } else if ( whichPrt == 3 || maxPrt == 3 ) { + prt = splashParticleBig; + } else if ( whichPrt == 4 || maxPrt == 4 ) { + prt = splashParticleHuge; + } + + if ( maxPrt != 0 ) { + // entity->StartSound( "snd_splash", SND_CHANNEL_ANY, 0, false, NULL ); + const idSoundShader *shader = declManager->FindSound( spawnArgs.GetString( "snd_splash" ) ); + entity->StartSoundShader( shader, SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL ); + } + + if ( prt ) { + gameLocal.smokeParticles->EmitSmoke( prt, gameLocal.time, gameLocal.random.CRandomFloat(), entity->GetPhysics()->GetOrigin(), idAngles( 0, gameLocal.random.CRandomFloat() * 360, 0 ).ToMat3() ); + } + } + } +} + +/* +=============== +Zeroth +idLiquid::Think +=============== +*/ +void idLiquid::Think( void ) { + if ( thinkFlags & TH_THINK ) { + TouchEntities(); + } + idEntity::Think(); +} /* =============================================================================== @@ -3156,3 +3474,66 @@ void idPhantomObjects::Think( void ) { BecomeInactive( TH_THINK ); } } + + +/* +=============================================================================== + +Zeroth +idPortalSky + +=============================================================================== +*/ + +CLASS_DECLARATION( idEntity, idPortalSky ) + EVENT( EV_PostSpawn, idPortalSky::Event_PostSpawn ) + EVENT( EV_Activate, idPortalSky::Event_Activate ) +END_CLASS + +/* +=============== +Zeroth +idPortalSky::idPortalSky +=============== +*/ +idPortalSky::idPortalSky( void ) { } + +/* +=============== +Zeroth +idPortalSky::~idPortalSky +=============== +*/ +idPortalSky::~idPortalSky( void ) { } + +/* +=============== +Zeroth +idPortalSky::Spawn +=============== +*/ +void idPortalSky::Spawn( void ) { + if ( !spawnArgs.GetBool( "triggered" ) ) { + PostEventMS( &EV_PostSpawn, 1 ); + } +} + +/* +================ +Zeroth +idPortalSky::Event_PostSpawn +================ +*/ +void idPortalSky::Event_PostSpawn() { + gameLocal.SetPortalSkyEnt( this ); +} + +/* +================ +Zeroth +idPortalSky::Event_Activate +================ +*/ +void idPortalSky::Event_Activate( idEntity *activator ) { + gameLocal.SetPortalSkyEnt( this ); +} diff --git a/game/Misc.h b/game/Misc.h index 89a4235..53f584a 100644 --- a/game/Misc.h +++ b/game/Misc.h @@ -548,13 +548,44 @@ class idLiquid : public idEntity { public: CLASS_PROTOTYPE( idLiquid ); + idLiquid( void ); // HEXEN : Zeroth + void Spawn( void ); + void Think( void ); // HEXEN : Zeroth void Save( idSaveGame *savefile ) const; void Restore( idRestoreGame *savefile ); private: - void Event_Touch( idEntity *other, trace_t *trace ); + void Event_Touch( idEntity *other, trace_t *trace ); // HEXEN : Zeroth - was commented out + +// Zeroth + idClipModel * clipModel; + void TouchEntities( void ); + bool particleOnExit; // whether to spawn a particle when something leaves the water + const idDeclParticle * splashParticleRipple; + const idDeclParticle * splashParticleTiny; + const idDeclParticle * splashParticleSmall; + const idDeclParticle * splashParticleBig; + const idDeclParticle * splashParticleHuge; + float moveResistance; + idVec3 moveDir; + float moveAmt; + idList touchingEntities; // list of entities touching the trigger + idList flagEntities; // used to test if touchingEntities[x] is still touching, if not to be removed from list + // idList resistTimeEntities; + + +// HEXEN : Zeroth - for limit entity types +private: + bool dontTripby_LocalPlayer; + bool limitEntityType; + bool tripby_idPlayer; + bool tripby_idAI; + bool tripby_idMoveable; + bool tripby_idItem; + bool tripby_idActor; + bool tripby_idProjectile; idRenderModelLiquid *model; @@ -765,4 +796,24 @@ private: idList lastTargetPos; }; +/* +=============================================================================== + +Zeroth +idPortalSky + +=============================================================================== +*/ +class idPortalSky : public idEntity { +public: + CLASS_PROTOTYPE( idPortalSky ); + + idPortalSky(); + ~idPortalSky(); + + void Spawn( void ); + void Event_PostSpawn(); + void Event_Activate( idEntity *activator ); +}; + #endif /* !__GAME_MISC_H__ */ diff --git a/game/Moveable.cpp b/game/Moveable.cpp index e13ecd6..742424f 100644 --- a/game/Moveable.cpp +++ b/game/Moveable.cpp @@ -45,13 +45,19 @@ const idEventDef EV_BecomeNonSolid( "becomeNonSolid" ); const idEventDef EV_SetOwnerFromSpawnArgs( "" ); const idEventDef EV_IsAtRest( "isAtRest", NULL, 'd' ); const idEventDef EV_EnableDamage( "enableDamage", "f" ); +// HEXEN : Zeroth +const idEventDef EV_BecomeSolid( "MovableBecomeSolid" ); +const idEventDef EV_DealDirectDamage( "directDamage", "es" ); CLASS_DECLARATION( idEntity, idMoveable ) EVENT( EV_Activate, idMoveable::Event_Activate ) - EVENT( EV_BecomeNonSolid, idMoveable::Event_BecomeNonSolid ) + EVENT( EV_BecomeSolid, idMoveable::Event_BecomeSolid ) EVENT( EV_SetOwnerFromSpawnArgs, idMoveable::Event_SetOwnerFromSpawnArgs ) EVENT( EV_IsAtRest, idMoveable::Event_IsAtRest ) EVENT( EV_EnableDamage, idMoveable::Event_EnableDamage ) +// HEXEN : Zeroth + EVENT( EV_BecomeNonSolid, idMoveable::Event_BecomeNonSolid ) + EVENT( EV_DealDirectDamage, idMoveable::Event_DirectDamage ) END_CLASS @@ -97,6 +103,7 @@ void idMoveable::Spawn( void ) { float density, friction, bouncyness, mass; int clipShrink; idStr clipModelName; + savePersistentInfo = false; // check if a clip model is set spawnArgs.GetString( "clipmodel", "", clipModelName ); @@ -104,6 +111,12 @@ void idMoveable::Spawn( void ) { clipModelName = spawnArgs.GetString( "model" ); // use the visual model } + // HEXEN : Zeroth +/** if ( !clipModelName[0] && IsType( idWood::Type ) ) { + gameLocal.Error("idWood '%s': Please open map editor and re-save map.", name.c_str() ); + } +**/ + if ( !collisionModelManager->TrmFromModel( clipModelName, trm ) ) { gameLocal.Error( "idMoveable '%s': cannot load collision model %s", name.c_str(), clipModelName.c_str() ); return; @@ -138,6 +151,8 @@ void idMoveable::Spawn( void ) { health = spawnArgs.GetInt( "health", "0" ); spawnArgs.GetString( "broken", "", brokenModel ); + spawnArgs.GetBool( "removeWhenBroken", "", removeWhenBroken ); + spawnArgs.GetBool( "call_script_on_broken", "", brokenScript ); // HEXEN : Zeroth if ( health ) { if ( brokenModel != "" && !renderModelManager->CheckModel( brokenModel ) ) { @@ -162,13 +177,13 @@ void idMoveable::Spawn( void ) { physicsObj.SetMass( mass ); } - if ( spawnArgs.GetBool( "nodrop" ) ) { + if ( spawnArgs.GetBool( "nodrop" ) /**|| IsType( idWood::Type ) **/ ) { // HEXEN : Zeroth, added idWood physicsObj.PutToRest(); } else { physicsObj.DropToFloor(); } - if ( spawnArgs.GetBool( "noimpact" ) || spawnArgs.GetBool( "notPushable" ) ) { + if ( spawnArgs.GetBool( "noimpact" ) || spawnArgs.GetBool( "notPushable" ) /**|| IsType( idWood::Type )**/ ) { // HEXEN : Zeroth, added idWood physicsObj.DisableImpact(); } @@ -189,6 +204,8 @@ idMoveable::Save void idMoveable::Save( idSaveGame *savefile ) const { savefile->WriteString( brokenModel ); + savefile->WriteBool( brokenScript ); + savefile->WriteBool( removeWhenBroken ); savefile->WriteString( damage ); savefile->WriteString( fxCollide ); savefile->WriteInt( nextCollideFxTime ); @@ -215,6 +232,8 @@ void idMoveable::Restore( idRestoreGame *savefile ) { int initialSplineTime; savefile->ReadString( brokenModel ); + savefile->ReadBool( brokenScript ); + savefile->ReadBool( removeWhenBroken ); savefile->ReadString( damage ); savefile->ReadString( fxCollide ); savefile->ReadInt( nextCollideFxTime ); @@ -237,6 +256,7 @@ void idMoveable::Restore( idRestoreGame *savefile ) { savefile->ReadStaticObject( physicsObj ); RestorePhysics( &physicsObj ); + gameLocal.Printf( "restore %s\n", name.c_str() ); } /* @@ -271,6 +291,7 @@ bool idMoveable::Collide( const trace_t &collision, const idVec3 &velocity ) { idVec3 dir; idEntity *ent; + savePersistentInfo = true; v = -( velocity * collision.c.normal ); if ( v > BOUNCE_SOUND_MIN_VELOCITY && gameLocal.time > nextSoundTime ) { f = v > BOUNCE_SOUND_MAX_VELOCITY ? 1.0f : idMath::Sqrt( v - BOUNCE_SOUND_MIN_VELOCITY ) * ( 1.0f / idMath::Sqrt( BOUNCE_SOUND_MAX_VELOCITY - BOUNCE_SOUND_MIN_VELOCITY ) ); @@ -288,7 +309,7 @@ bool idMoveable::Collide( const trace_t &collision, const idVec3 &velocity ) { f = v > maxDamageVelocity ? 1.0f : idMath::Sqrt( v - minDamageVelocity ) * ( 1.0f / idMath::Sqrt( maxDamageVelocity - minDamageVelocity ) ); dir = velocity; dir.NormalizeFast(); - ent->Damage( this, GetPhysics()->GetClipModel()->GetOwner(), dir, damage, f, INVALID_JOINT ); + ent->Damage( this, GetPhysics()->GetClipModel()->GetOwner(), dir, damage, f, INVALID_JOINT, idVec3( collision.c.point ) ); nextDamageTime = gameLocal.time + 1000; } } @@ -321,6 +342,91 @@ void idMoveable::Killed( idEntity *inflictor, idEntity *attacker, int damage, co } } + // HEXEN : Zeroth + const idSoundShader *shader = declManager->FindSound( spawnArgs.GetString( "snd_break" ) ); + this->StartSoundShader( shader, SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL ); + + // HEXEN : Zeroth + const idDeclParticle * smokeBreak = NULL; + int smokeBreakTime = 0; + const char *smokeName = spawnArgs.GetString( "smoke_break" ); + if ( *smokeName != '\0' ) { + smokeBreak = static_cast( declManager->FindType( DECL_PARTICLE, smokeName ) ); + smokeBreakTime = gameLocal.time; + + // get center of bounds on moveable + idVec3 newDir; + newDir = GetPhysics()->GetBounds()[1] + GetPhysics()->GetBounds()[0]; + newDir *= GetPhysics()->GetClipModel()->GetAxis(); + newDir += GetPhysics()->GetOrigin(); + + gameLocal.smokeParticles->EmitSmoke( smokeBreak, smokeBreakTime, gameLocal.random.CRandomFloat(), newDir, GetPhysics()->GetAxis() ); + } + + // HEXEN : Zeroth + if ( removeWhenBroken ) { + Hide(); + gameLocal.SetPersistentRemove( name.c_str() ); + + physicsObj.PutToRest(); + CancelEvents( &EV_Explode ); + CancelEvents( &EV_Activate ); + + if ( spawnArgs.GetBool( "triggerTargets" ) ) { + ActivateTargets( this ); + } + + PostEventMS( &EV_Remove, spawnArgs.GetFloat( "fuse" ) ); + } + + // HEXEN : Zeroth + if ( scriptThread != NULL && brokenScript ) { + const function_t *func = GetScriptFunction( "broken" ); + if ( !func ) { + assert( 0 ); + gameLocal.Error( "Can't find function use' in object '%s'", scriptObject.GetTypeName() ); + return; + } + + SetState( func ); + UpdateScript(); + } + + const idKeyValue *kv = spawnArgs.MatchPrefix( "def_debris" ); + // bool first = true; + while ( kv ) { + const idDict *debris_args = gameLocal.FindEntityDefDict( kv->GetValue(), false ); + if ( debris_args ) { + idEntity *ent; + idVec3 dir; + idDebris *debris; + // if ( first ) { + dir = physicsObj.GetAxis()[1]; + // first = false; + // } else { + dir.x += gameLocal.random.CRandomFloat() * 4.0f; + dir.y += gameLocal.random.CRandomFloat() * 4.0f; + // dir.z = gameLocal.random.RandomFloat() * 8.0f; + // } + dir.Normalize(); + + gameLocal.SpawnEntityDef( *debris_args, &ent, false ); + if ( !ent || !ent->IsType( idDebris::Type ) ) { + gameLocal.Error( "'projectile_debris' is not an idDebris" ); + } + + debris = static_cast(ent); + debris->randomPosInBounds = true; + debris->randomPosEnt = this; + debris->Create( this, physicsObj.GetOrigin(), dir.ToMat3() ); + debris->Launch(); + debris->GetRenderEntity()->shaderParms[ SHADERPARM_TIME_OF_DEATH ] = ( gameLocal.time + 1500 ) * 0.001f; + debris->UpdateVisuals(); + + } + kv = spawnArgs.MatchPrefix( "def_debris", kv ); + } + if ( renderEntity.gui[ 0 ] ) { renderEntity.gui[ 0 ] = NULL; } @@ -350,6 +456,18 @@ void idMoveable::BecomeNonSolid( void ) { physicsObj.SetClipMask( MASK_SOLID | CONTENTS_CORPSE | CONTENTS_MOVEABLECLIP ); } +/* +================ +Zeroth +idMoveable::BecomeSolid +================ +*/ +void idMoveable::BecomeSolid( void ) { + // set CONTENTS_RENDERMODEL so bullets still collide with the moveable + physicsObj.SetContents( CONTENTS_SOLID ); + physicsObj.SetClipMask( MASK_SOLID | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_MOVEABLECLIP ); +} + /* ================ idMoveable::EnableDamage @@ -469,6 +587,16 @@ void idMoveable::Event_BecomeNonSolid( void ) { BecomeNonSolid(); } +/* +================ +Zeroth +idMoveable::Event_BecomeSolid +================ +*/ +void idMoveable::Event_BecomeSolid( void ) { + BecomeSolid(); +} + /* ================ idMoveable::Event_Activate @@ -537,6 +665,53 @@ void idMoveable::Event_EnableDamage( float enable ) { canDamage = ( enable != 0.0f ); } +/* +================ +idMoveable::Event_DirectDamage +================ +*/ +void idMoveable::Event_DirectDamage( idEntity *damageTarget, const char *damageDefName ) { + DirectDamage( damageDefName, damageTarget ); +} + +/* +================ +idMoveable::DirectDamage +================ +*/ +void idMoveable::DirectDamage( const char *meleeDefName, idEntity *ent ) { + const idDict *meleeDef; + const char *p; + const idSoundShader *shader; + + meleeDef = gameLocal.FindEntityDefDict( meleeDefName, false ); + if ( !meleeDef ) { + gameLocal.Error( "Unknown damage def '%s' on '%s'", meleeDefName, name.c_str() ); + } + + if ( !ent->fl.takedamage ) { + const idSoundShader *shader = declManager->FindSound(meleeDef->GetString( "snd_miss" )); + StartSoundShader( shader, SND_CHANNEL_DAMAGE, 0, false, NULL ); + return; + } + + // + // do the damage + // + p = meleeDef->GetString( "snd_hit" ); + if ( p && *p ) { + shader = declManager->FindSound( p ); + StartSoundShader( shader, SND_CHANNEL_DAMAGE, 0, false, NULL ); + } + + idVec3 kickDir; + meleeDef->GetVector( "kickDir", "0 0 0", kickDir ); + + idVec3 globalKickDir; + globalKickDir = kickDir; // z.todo: not proper + + ent->Damage( this, this, globalKickDir, meleeDefName, 1.0f, INVALID_JOINT, idVec3( 0, 0, 0 ) ); +} /* =============================================================================== @@ -1065,7 +1240,7 @@ idExplodingBarrel::Damage ================ */ void idExplodingBarrel::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, - const char *damageDefName, const float damageScale, const int location ) { + const char *damageDefName, const float damageScale, const int location, const idVec3 &iPoint ) { const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName ); if ( !damageDef ) { @@ -1074,7 +1249,7 @@ void idExplodingBarrel::Damage( idEntity *inflictor, idEntity *attacker, const i if ( damageDef->FindKey( "radius" ) && GetPhysics()->GetContents() != 0 && GetBindMaster() == NULL ) { PostEventMS( &EV_Explode, 400 ); } else { - idEntity::Damage( inflictor, attacker, dir, damageDefName, damageScale, location ); + idEntity::Damage( inflictor, attacker, dir, damageDefName, damageScale, location, iPoint ); } } @@ -1192,3 +1367,1069 @@ bool idExplodingBarrel::ClientReceiveEvent( int event, int time, const idBitMsg return idBarrel::ClientReceiveEvent( event, time, msg ); } + + +#if 0 +/********************************************************* +Zeroth's idWood. +for breaking boards, rocks, etc. +*********************************************************/ + +CLASS_DECLARATION( idMoveable, idWood ) +// EVENT( EV_Touch, idLiquid::Event_Touch ) +END_CLASS +// HEXEN : Zeroth +idWood::idWood( void ) { + geoConstructed = false; +} +// HEXEN : Zeroth +void idWood::Spawn( void ) { + if (!geoConstructed) { + ConstructGeo(); + } + + BecomeActive( TH_THINK ); +} +// HEXEN : Zeroth +// SDK does not contain information that allows us to access faces of models +// like (*faces[face])[vertex], so we'll have to build that information here. +void idWood::ConstructGeo( void ) { + int i, v, f, v2, f2; + srfTriangles_t *surf = renderEntity.hModel->Surface(0)->geometry; + // assign vertices to geo.faces + for (i=0; i < (surf->numIndexes / 3); i++) { + f = geo.faces.New(); + geo.faces[f].plane = &surf->facePlanes[i]; //z.todo: delete geta/b/c/d funcs. + for ( v=0; v < surf->numVerts; v++ ) { + if ( surf->facePlanes[i].Side(surf->verts[v].xyz, ON_EPSILON ) == PLANESIDE_ON ) { + geo.faces[f].verts.Append( surf->verts[v].xyz ); //origin + winding[0].ToVec3() * axis; + } + } + } + geo.faces.Condense(); +//************ +//merge perpendicular faces that share 2+ verts +//************ + int same; + for (f=0; f < geo.faces.Num(); f++) { + int verts = geo.faces[f].verts.Num(); + for ( f2=0; f2 < geo.faces.Num(); f2++ ) { + if ( f == f2 ) { + continue; + } + // perpendicular test + if ( geo.faces[f].plane->Normal() != geo.faces[f2].plane->Normal() ) { + continue; + } + // test if all vertices on plane + for ( v2=0; v2Side( geo.faces[f2].verts[v2], ON_EPSILON ) != PLANESIDE_ON ) { + continue; + } + } + // test if share 2 vertices + same=0; + for ( v2=0; v2 < geo.faces[f2].verts.Num() && same < 2; v2++ ) { + if ( geo.faces[f].verts.Find( geo.faces[f2].verts[v2] ) ) { + same++; + } + } +// same=0; +// for ( v=0; v < verts && same < 2; v++ ) { +// for ( v2=0; v2 < geo.faces[f2].verts.Num() && same < 2; v2++ ) { +// if ( geo.faces[f].verts.Find( geo.faces[f2].verts[v2] ) ) { +// same++; +// } +// } +// } +// + if ( same >= 2 ){ + for ( v2=0; v2 < geo.faces[f2].verts.Num(); v2++ ) { + geo.faces[f].verts.Append( geo.faces[f2].verts[v2] ); + } + geo.faces[f2].verts.Clear(); + geo.faces.RemoveIndex(f2); + geo.faces.Condense(); + f2--; + } + } + } +//************ +//delete duplicate vertices +//************ + for (f=0; f < geo.faces.Num(); f++) { + for ( v=0; v < geo.faces[f].verts.Num(); v++ ) { + for ( i=v+1; i < geo.faces[f].verts.Num(); i++ ) { + if ( idVec3( geo.faces[f].verts[v] - geo.faces[f].verts[i] ).Length() < 0.00001 ) { + geo.faces[f].verts.RemoveIndex(i); + geo.faces[f].verts.Condense(); + i--; + } + } + } + } + // debuging +// for ( f=0; f < geo.faces.Num(); f++ ) { +// gameLocal.Printf("face %i:\n", f ); +// for ( v=0; v < geo.faces[f].verts.Num(); v++ ) { +// gameLocal.Printf("%i:%i : %f,%f,%f\n", f, v, geo.faces[f].verts[v].x, geo.faces[f].verts[v].y, geo.faces[f].verts[v].z ); +// +// } +// } +//************ +//sort vertices clockwise +//************ + for ( f=0; f < geo.faces.Num(); f++ ) { + bool DoneSorting; + idVec3 h,j; + for (i=0; i < geo.faces[f].verts.Num(); i++) + { + DoneSorting = true; + for (v=0; v < geo.faces[f].verts.Num()-2; v++) { + h = geo.faces[f].verts[v+1] - geo.faces[f].verts[0]; + j = geo.faces[f].verts[v+2] - geo.faces[f].verts[0]; + j = h.Cross(j); + j.Normalize(); + if ( j * geo.faces[f].plane->Normal() < 0.0f ) { + DoneSorting = false; + j = geo.faces[f].verts[v+2]; + geo.faces[f].verts[v+2] = geo.faces[f].verts[v+1]; + geo.faces[f].verts[v+1] = j; + } + } + if (DoneSorting) { + break; + } + } + } +//************ +//get center of model +//*********** + geo.center.Zero(); + int verts=0; + for ( f=0; f < geo.faces.Num(); f++ ) { + verts += geo.faces[f].verts.Num(); + for ( v=0; v < geo.faces[f].verts.Num(); v++ ) { + geo.center += geo.faces[f].verts[v]; + } + } + geo.center /= verts; +//************ +//get average surface area per side +//************ + geo.area = 0; + float a,b,c,p; + for ( f=0; f < geo.faces.Num(); f++ ) { + // get center of the face + geo.faces[f].center.Zero(); + for ( v=0; v < geo.faces[f].verts.Num(); v++ ) { + geo.faces[f].center += geo.faces[f].verts[v]; + } + geo.faces[f].center /= geo.faces[f].verts.Num(); + // get area of each portion of face + geo.faces[f].area = 0; + for ( v=0; v < geo.faces[f].verts.Num() - 1; v++ ) { + a = ( geo.faces[f].center - geo.faces[f].verts[v] ).Length(); + b = ( geo.faces[f].verts[v] - geo.faces[f].verts[v+1] ).Length(); + c = ( geo.faces[f].verts[v+1] - geo.faces[f].center ).Length(); + p = (a+b+c)/2; + geo.faces[f].area += idMath::Sqrt( p * ( p - a ) * ( p - b ) * ( p - c ) ); + } + gameLocal.Printf("face %i area %f\n", f, geo.faces[f].area); + geo.area += geo.faces[f].area; + } + geo.avgArea = geo.area / geo.faces.Num(); + gameLocal.Printf("tot area %f\n", geo.area); + gameLocal.Printf("areaavg %f\n", geo.avgArea); +//*********** +//store adjacent sides +//*********** + // faces sharing at least one vertice with one another are adjacent. + for ( f=0; fGetInt( "damage" ); + // inform the attacker that they hit someone + attacker->DamageFeedback( this, inflictor, damage ); + if ( damage ) { + // do the damage + health -= damage; + if ( health <= 0 ) { + if ( health < -999 ) { + health = -999; + } + Killed( inflictor, attacker, damage, dir, location ); + } else { + Pain( inflictor, attacker, damage, dir, location ); + } + } +} +bool idWood::CheckFloating( idWood *caller, idWood *chain[], int c ) { + // make sure we aren't caught in a recursive loop with the other touching entities + int numClipModels, t, i; + idBounds bounds; + idClipModel *cm, *clipModelList[ MAX_GENTITIES ]; + idEntity* entity; + for ( i=0; iGetPhysics()->GetClipModel() ) { + return false; + } + // add self to chain + chain[c] = this; + c++; + bounds.FromTransformedBounds( this->GetPhysics()->GetClipModel()->GetBounds(), this->GetPhysics()->GetClipModel()->GetOrigin(), this->GetPhysics()->GetClipModel()->GetAxis() ); + numClipModels = gameLocal.clip.ClipModelsTouchingBounds( bounds, -1, clipModelList, MAX_GENTITIES ); + // check all touchers to see if they're NOT floating + for ( i = 0; i < numClipModels; i++ ) { + cm = clipModelList[ i ]; + if ( !cm->IsTraceModel() ) { + continue; + } + entity = clipModelList[ i ]->GetEntity(); + if ( !entity ) { + continue; + } + if ( !gameLocal.clip.ContentsModel( cm->GetOrigin(), cm, cm->GetAxis(), -1, GetPhysics()->GetClipModel()->Handle(), GetPhysics()->GetClipModel()->GetOrigin(), GetPhysics()->GetClipModel()->GetAxis() ) ) { + continue; + } + if ( !entity->IsType( idWood::Type ) ) { + return false; + } + if ( static_cast< idWood* >( entity ) == caller ) { + continue; + } + if ( static_cast< idWood* >( entity )->spawnArgs.GetBool("stopper") ) { + return false; + } + if ( static_cast< idWood* >( entity )->health <= 0 ) { + return true; + } + if ( ! static_cast< idWood* >( entity )->CheckFloating( this, chain, c ) ) { + return false; + } + } + // if we haven't found a non-floater, we're floating + physicsObj.EnableImpact(); + physicsObj.SetLinearVelocity( idVec3(0,0,-20) ); + BecomeNonSolid(); + return true; +} + + +bool idWood::CheckFloating( idWood &caller, idWood *chain, int c ) { + // make sure we aren't caught in a recursive loop with the other touching entities + for ( i=0; ibounds, -1, entityList, MAX_GENTITIES ); + // if theres only one toucher, and it's the caller, we're floating + if ( n == 1 && entityList[0] == caller ) { + physicsObj.EnableImpact(); + physicsObj.SetLinearVelocity( idVec3(0,0,1) ); + return true; + } + // if no touchers.. we're floating + if ( n == 0 ) { + physicsObj.EnableImpact(); + physicsObj.SetLinearVelocity( idVec3(0,0,1) ); + return true; + } + // add self to chain + chain[n] = this; + c++; + // check all touchers to see if they're NOT floating + for ( i=0; i < n; i++ ) { + if ( !entityList[i]->IsType( idWood::Type ) ) { + return false; + } + if ( entityList[i] == caller ) { + continue; + } + if ( ! static_cast< idWood* >( entityList[i] )->CheckFloating( this, chain, c ) ) { + return false; + } + } + // if we haven't found a non-floater, we're floating + physicsObj.EnableImpact(); + physicsObj.SetLinearVelocity( idVec3(0,0,1) ); + return true; +} + +// HEXEN : Zeroth +void idWood::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) { + idEntity *impactor = NULL; + int f, v, r, end, op; + idVec3 ray; + float dist; + float oldDist = 0; + physicsObj.EnableImpact(); + physicsObj.SetLinearVelocity( idVec3(0,0,-20) ); + BecomeNonSolid(); + idWood * chain[ MAX_GENTITIES ]; + CheckFloating( this, chain, 0 ); + if ( unbindOnDeath ) { + Unbind(); + } + if ( renderEntity.gui[ 0 ] ) { + renderEntity.gui[ 0 ] = NULL; + } + ActivateTargets( this ); + fl.takedamage = false; + + // spawn new log (for one half) + +/**************************************************** +for trying to create a new one on teh fly, or using the default model +*****************************************************/ + idDict newDebris; + //newDebris.Copy(spawnArgs); +// newDebris.Set("material", spawnArgs.GetString("material") ); +// idClipModel *nclip=new idClipModel( GetPhysics()->GetClipModel() ); +// newDebris.Set( "clipmodel", nclip->GetEntity()->GetName() ); +// renderEntity_t *nrend=new renderEntity_t( renderEntity ); +// newDebris.Set( "model", nrend->hModel->Name() ); + //newDebris.Delete("name"); + //newDebris.Delete("clipmodel"); + idWood *deb = static_cast< idWood * >( gameLocal.SpawnEntityType(idMoveable::Type, &newDebris) ); + idVec3 org( physicsObj.GetOrigin() ); + deb->GetPhysics()->SetOrigin( org ); + deb->GetPhysics()->SetAxis( physicsObj.GetAxis() ); + deb->GetPhysics()->SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), spawnArgs.GetFloat( "density", "0.5" ) ); + //renderEntity_t nrend( renderEntity ); + //deb->SetModel( nrend.hModel->Name() ); + //physicsObj.GetClipModel()->LoadModel( modelDefHandle ); + // err: TraceModelForClipModel: clip model 0 on wood_2 is not a trace model +// idRenderModel *mod; +// mod = renderModelManager->AllocModel(); +// mod->AllocSurfaceTriangles( renderEntity.hModel->Surface(0)->geometry->numVerts, renderEntity.hModel->Surface(0)->geometry->numIndexes ); +// mod->AddSurface( *renderEntity.hModel->Surface(0) ); +// mod->FinishSurfaces(); +// mod->FreeVertexCache(); + idRenderModel *mod; + mod = renderModelManager->AllocModel(); + mod->LoadModel(); + deb->SetModel( mod->Name() ); + deb->UpdateModel(); + deb->UpdateModelTransform(); + deb->UpdateVisuals(); + srfTriangles_t *debsgeo = deb->renderEntity.hModel->Surface(0)->geometry; + srfTriangles_t *sgeo = renderEntity.hModel->Surface(0)->geometry; +/**************************************************** + using woodbreak entity +****************************************************/ + idWood *deb = static_cast< idWood* >( gameLocal.FindEntity("woodbreak") ); + if ( !deb ) { + return; + } + //srfTriangles_t *debsgeo = deb->renderEntity.hModel->Surface(0)->geometry; + srfTriangles_t *sgeo = renderEntity.hModel->Surface(0)->geometry; +/********************************************** + add splinters to wood +***********************************************/ + // find the smallest face (short end of the wood) + for ( end=0, f=1; fNormal() * 10 ; + idFixedWinding w; + w.Clear(); + vv = sgeo->verts[ sgeo->indexes[ 0 ] ]; + w.AddPoint( newFace[0] ); + w[0].s = vv.st[0]; + w[0].t = vv.st[1]; + w.AddPoint( newFace[1] ); + w[1].s = vv.st[0]; + w[1].t = vv.st[1]; + w.AddPoint( newFace[2] ); + w[2].s = vv.st[0]; + w[2].t = vv.st[1]; +*/ + /** this DEFINITELY doesnt owrk + sgeo->numVerts += 3; + realloc( sgeo->verts, sgeo->numVerts * sizeof( sgeo->verts ) ); + realloc( sgeo->facePlanes, ( sgeo->numVerts / 3 ) * sizeof( sgeo->facePlanes ) ); + sgeo->verts[ sgeo->numVerts - 3 ].xyz = newFace[0]; + sgeo->verts[ sgeo->numVerts - 2 ].xyz = newFace[1]; + sgeo->verts[ sgeo->numVerts - 1 ].xyz = newFace[2]; + sgeo->facePlanes[ ( sgeo->numVerts / 3 ) - 1 ] = sgeo->facePlanes[ ( sgeo->numVerts / 3 ) - 2 ]; + **/ +/** // using shards... need to figure out how to use them properly. + shard_t *shard = new shard_t; + shard->clipModel = GetPhysics()->GetClipModel(); + shard->droppedTime = -1; + shard->winding = w; + shard->decals.Clear(); + shard->edgeHasNeighbour.AssureSize( w.GetNumPoints(), false ); + shard->neighbours.Clear(); + shard->atEdge = false; + // setup the physics + shard->physicsObj.SetSelf( this ); + shard->physicsObj.SetClipModel( shard->clipModel, 1 ); + shard->physicsObj.SetMass( GetPhysics()->GetMass() ); + shard->physicsObj.SetOrigin( GetPhysics()->GetOrigin() ); + shard->physicsObj.SetAxis( GetPhysics()->GetAxis() ); + shard->physicsObj.SetBouncyness( 1 ); + shard->physicsObj.SetFriction( 0.6f, 0.6f, 1 ); + shard->droppedTime = gameLocal.time; + shard->physicsObj.SetContents( CONTENTS_RENDERMODEL ); + shard->physicsObj.SetClipMask( MASK_SOLID | CONTENTS_MOVEABLECLIP ); + //shard->clipModel->SetId( shard->clipModel->GetId(); ); + BecomeActive( TH_PHYSICS ); +*/ +/********************************************** + match up geo +***********************************************/ + idList oneClock; + idList twoClock; + idVec3 h,j; + // store vertices in the same direction. doesnt seem to accomplish anything + for ( f=0, r=0; r < sgeo->numVerts; r+=3, f++ ) { + oneClock.Append(true); + // check if side is stored clockwise + h = sgeo->verts[r+1].xyz - sgeo->verts[r].xyz; + j = sgeo->verts[r+2].xyz - sgeo->verts[r].xyz; + j = h.Cross(j); + j.Normalize(); + if ( j * sgeo->facePlanes[f].Normal() < 0.0f ) { + oneClock[f] = false; + } + } + for ( f=0, r=0; r < debsgeo->numVerts; r+=3, f++ ) { + twoClock.Append(true); + // check if side is stored clockwise + h = debsgeo->verts[r+1].xyz - debsgeo->verts[r].xyz; + j = debsgeo->verts[r+2].xyz - debsgeo->verts[r].xyz; + j = h.Cross(j); + j.Normalize(); + if ( j * debsgeo->facePlanes[f].Normal() < 0.0f ) { + twoClock[f] = false; + } + } + for ( f=0, r=0; r < sgeo->numVerts; r+=3, f++ ) { + if ( oneClock[f] == twoClock[f] ) { + debsgeo->verts[r].xyz = sgeo->verts[r].xyz; + debsgeo->verts[r+1].xyz = sgeo->verts[r+1].xyz; + debsgeo->verts[r+2].xyz = sgeo->verts[r+2].xyz; + } else { + debsgeo->verts[r].xyz = sgeo->verts[r+2].xyz; + debsgeo->verts[r+1].xyz = sgeo->verts[r+1].xyz; + debsgeo->verts[r+2].xyz = sgeo->verts[r].xyz; + } + } + // match vertices + for ( r=0; r < sgeo->numVerts; r++ ) { + debsgeo->verts[r].xyz = sgeo->verts[r].xyz; + } + // also seems to not help + // match faces + for ( r=0; r < sgeo->numVerts; r+=3 ) { + debsgeo->facePlanes[r].SetDist( sgeo->facePlanes[r].Dist() ); + debsgeo->facePlanes[r].SetNormal( sgeo->facePlanes[r].Normal() ); + } + Hide(); + // match indexed + for ( r=0; r numIndexes; r+=3 ) { + for ( v = 0; v < 3; v++ ) { + debsgeo->verts[ sgeo->indexes[ r + 2 - v ] ].st[0] = sgeo->verts[ sgeo->indexes[ r + 2 - v ] ].st[0]; + debsgeo->verts[ sgeo->indexes[ r + 2 - v ] ].st[1] = sgeo->verts[ sgeo->indexes[ r + 2 - v ] ].st[1]; + } + } + // match tangents and normal + for ( r=0; r numIndexes; r+=3 ) { + debsgeo->verts[r].tangents[0] = sgeo->verts[r].tangents[0]; + debsgeo->verts[r].tangents[1] = sgeo->verts[r].tangents[1]; + debsgeo->verts[r].normal = sgeo->verts[r].normal; + } + // deb->renderEntity.customShader + // deb->renderEntity.hModel->Surface(0)->shader + deb->renderEntity.customShader = renderEntity.hModel->Surface(0)->shader; + deb->GetPhysics()->SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), spawnArgs.GetFloat( "density", "0.5" ), 0, true ); + deb->GetPhysics()->SetOrigin( physicsObj.GetOrigin() ); + deb->GetPhysics()->SetAxis( physicsObj.GetAxis() ); + deb->renderEntity.hModel->FreeVertexCache(); + deb->UpdateModel(); + deb->UpdateModelTransform(); + deb->UpdateVisuals(); + deb->Show(); + +/********************************************** + Split in half +***********************************************/ + // find the smallest face (short end of the wood) + for ( end=0, f=1; f oldDist ) { + op = f; + } + } + // move shortest face half way to opposing face + for ( v=0; vnumVerts; r++ ) { + //sgeo->verts[r].xyz /= 2; + if ( sgeo->verts[r].xyz != geo.faces[end].verts[v] ) { + continue; + } + sgeo->verts[r].xyz += ( geo.faces[op].center - geo.faces[end].center ) / 2; + } + geo.faces[end].verts[v] += ( geo.faces[op].center - geo.faces[end].center ) / 2; + } +// // other half: move shortest face half way to opposing face +// for ( v=0; vgeo.faces[op].verts.Num(); v++ ) { +// // update verts in the render entity +// for ( r=0; r < debsgeo->numVerts; r++ ) { +// if ( debsgeo->verts[r].xyz == deb->geo.faces[op].verts[v] ) { +// debsgeo->verts[r].xyz += ( deb->geo.faces[end].center - deb->geo.faces[op].center ) / 2; +// } +// } +// deb->geo.faces[op].verts[v] += ( deb->geo.faces[end].center - deb->geo.faces[op].center ) / 2; +// } + // store distance + dist = idVec3(geo.faces[end].center - geo.faces[op].center).Length(); + // update center now + geo.faces[end].center += ( geo.faces[op].center - geo.faces[end].center ) / 2; + deb->geo.faces[op].center += ( deb->geo.faces[end].center - deb->geo.faces[op].center ) / 2; +// currently not functional +// // resize short face and update area +// float newArea = ( ( geo.faces[op].area - geo.faces[end].area ) / dist ) * ( dist / 2 ) + geo.faces[end].area; +// float vertMoveDist = ( newArea / geo.faces[end].area ) / 4; // 4 sides +// geo.faces[end].area = newArea; +// for ( v=0; v < geo.faces[end].verts.Num(); v++ ) { +// idVec3 vertAdd = ( geo.faces[end].verts[v] - geo.faces[end].center ); +// +// vertAdd.Normalize(); +// vertAdd *= vertMoveDist; +// +// // update verts in the render entity +// for ( int r=0; r < sgeo->numVerts; r++ ) { +// if ( sgeo->verts[r].xyz == geo.faces[end].verts[v] ) { +// sgeo->verts[r].xyz += vertAdd; +// } +// } +// +// geo.faces[end].verts[v] += vertAdd; +// } +// +// // other half: resize short face and update area +// newArea = ( ( deb->geo.faces[end].area - deb->geo.faces[op].area ) / dist ) * ( dist / 2 ) + deb->geo.faces[op].area; +// vertMoveDist = ( newArea / deb->geo.faces[op].area ) / 4; // 4 sides +// deb->geo.faces[op].area = newArea; +// for ( v=0; v < deb->geo.faces[op].verts.Num(); v++ ) { +// idVec3 vertAdd = ( deb->geo.faces[op].verts[v] - deb->geo.faces[op].center ); +// +// vertAdd.Normalize(); +// vertAdd *= vertMoveDist; +// +// // update verts in the render entity +// for ( int r=0; r < debsgeo->numVerts; r++ ) { +// if ( debsgeo->verts[r].xyz == deb->geo.faces[op].verts[v] ) { +// debsgeo->verts[r].xyz += vertAdd; +// } +// } +// +// deb->geo.faces[op].verts[v] += vertAdd; +// } + renderEntity.hModel->FreeVertexCache(); + UpdateModel(); + UpdateModelTransform(); + UpdateVisuals(); + deb->renderEntity.hModel->FreeVertexCache(); + deb->UpdateModel(); + deb->UpdateModelTransform(); + deb->UpdateVisuals(); + // other side: update render model's bounds + srfTriangles_t *suu = sgeo; + int mins=0, maxs=0; + idVec3 min, max; + max.x = min.x = suu->verts[0].xyz.x; + max.y = min.y = suu->verts[0].xyz.y; + max.z = min.z = suu->verts[0].xyz.z; + for ( v=1; v < suu->numVerts; v++ ) { + if ( suu->verts[v].xyz.x < min.x ) { min.x = suu->verts[v].xyz.x; } + if ( suu->verts[v].xyz.y < min.y ) { min.y = suu->verts[v].xyz.y; } + if ( suu->verts[v].xyz.z < min.z ) { min.z = suu->verts[v].xyz.z; } + if ( suu->verts[v].xyz.x > max.x ) { max.x = suu->verts[v].xyz.x; } + if ( suu->verts[v].xyz.y > max.y ) { max.y = suu->verts[v].xyz.y; } + if ( suu->verts[v].xyz.z > max.z ) { max.z = suu->verts[v].xyz.z; } + } + suu->bounds = idBounds(min, max); + // create a new clip model based on the new bounds + float density = spawnArgs.GetFloat( "density", "0.5" ); + physicsObj.SetClipModel( new idClipModel( idTraceModel( suu->bounds ) ), density ); + UpdateModelTransform(); + UpdateVisuals(); +// // other side: update render model's bounds +// suu = debsgeo; +// +// mins=0, maxs=0; +// max.x = min.x = suu->verts[0].xyz.x; +// max.y = min.y = suu->verts[0].xyz.y; +// max.z = min.z = suu->verts[0].xyz.z; +// +// for ( v=1; v < suu->numVerts; v++ ) { +// if ( suu->verts[v].xyz.x < min.x ) { min.x = suu->verts[v].xyz.x; } +// if ( suu->verts[v].xyz.y < min.y ) { min.y = suu->verts[v].xyz.y; } +// if ( suu->verts[v].xyz.z < min.z ) { min.z = suu->verts[v].xyz.z; } +// if ( suu->verts[v].xyz.x > max.x ) { max.x = suu->verts[v].xyz.x; } +// if ( suu->verts[v].xyz.y > max.y ) { max.y = suu->verts[v].xyz.y; } +// if ( suu->verts[v].xyz.z > max.z ) { max.z = suu->verts[v].xyz.z; } +// } +// suu->bounds = idBounds(min, max); +// +// // other side: create a new clip model based on the new bounds +// density = spawnArgs.GetFloat( "density", "0.5" ); +// deb->physicsObj.SetClipModel( new idClipModel( idTraceModel( suu->bounds ) ), density ); +// +// deb->UpdateModelTransform(); +// deb->UpdateVisuals(); + +} + +/************************************************************** +test code for trying to create new logs +///////////////////////////////////////////////////////////////*/ + + + /******************************** + remove old model and spawn two halves + ********************************/ + int f,f2,v,v2,num,lst,end; + // find the smallest face (short end of the wood) + for ( end=0, f=1; f adjs; + idList< idVec3 > ver; + idList< idVec3 > ver2; + idList< idVec3 > ver3; + // find all adjacent sides to end + for ( f=0; f( gameLocal.SpawnEntityType(idWood::Type, &newDebris) ); + idVec3 org( physicsObj.GetOrigin() ); + org.z += 50; + deb->GetPhysics()->SetOrigin( org ); + deb->GetPhysics()->SetAxis( physicsObj.GetAxis() ); + srfTriangles_t *surf = deb->GetRenderEntity()->hModel->Surface(0)->geometry; + int idx; + idVec3 nw; + // shorten the new log + // z.todo: we're only ASSUMING that ver and ver2 indexes aren't in mismatched order + for ( v=0; v < surf->numVerts; v++ ) { + idx = ver.FindIndex( surf->verts[v].xyz ); + if ( idx != -1 ) { +/* + // get direction fro upper ring vert to lower ring vert + nw = ver2[idx] - ver[idx]; + // make the vector about half the length of the log + nw.Normalize(); + nw *= idMath::Sqrt( geo.area / 2 ); // sqrt( area / 2 ) is close enough to half the length of the log + surf->verts[v].xyz += nw; +*/ + } + } + // remove the old log + // ... + //gameLocal.Printf("adjs: %i+ 1 = %i\n", adjs.Num(),adjs.Num()+1); + //gameLocal.Printf("faces: %i / 2 = %i\n", geo.faces.Num(), geo.faces.Num() / 2); + + + /**************************************** + CRAP + *****************************************/ +/* + idList< int > hits; + int bestHit; + // find surface which is closest to impact point + // z.todo: fuck it. deal with this later. + + gameLocal.Printf("math : %f,%f,%f - ", lastiPoint.x,lastiPoint.y,lastiPoint.z); + gameLocal.Printf("%f,%f,%f * ", physicsObj.GetOrigin().x,physicsObj.GetOrigin().y,physicsObj.GetOrigin().z); + gameLocal.Printf("%f,%f,%f\n", -physicsObj.GetAxis().ToAngles().yaw,-physicsObj.GetAxis().ToAngles().pitch,-physicsObj.GetAxis().ToAngles().roll); + gameLocal.Printf("before: %f,%f,%f\n", lastiPoint.x,lastiPoint.y,lastiPoint.z); + lastiPoint = lastiPoint - physicsObj.GetOrigin() * -physicsObj.GetAxis(); + gameLocal.Printf(" after: %f,%f,%f\n", lastiPoint.x,lastiPoint.y,lastiPoint.z); +*/ + +/* + for ( i=0; iNormal() * physicsObj.GetAxis(); +// rightNorm.Normalize(); +// ray = lastiPoint - rightCenter; +// ray.Normalize(); +/* + gameLocal.Printf("\nTesting Face: %i\n", i ); +// gameLocal.Printf(" lastiPoint: %f,%f,%f\n", lastiPoint.x,lastiPoint.y,lastiPoint.z); +// gameLocal.Printf(" geo.center: %f,%f,%f\n", geo.center.x,geo.center.y,geo.center.z ); +// gameLocal.Printf(" WoodCenter: %f,%f,%f\n", rightCenter.x,rightCenter.y,rightCenter.z ); // v->xyz = origin + winding[0].ToVec3() * axis; +// gameLocal.Printf(" face i norm: %f,%f,%f\n", geo.faces[i].plane->Normal().z,geo.faces[i].plane->Normal().y,geo.faces[i].plane->Normal().z ); +// gameLocal.Printf("WFace i Norm: %f,%f,%f\n", rightNorm.z,rightNorm.y,rightNorm.z ); +// gameLocal.Printf(" Ray: %f,%f,%f\n",ray.x,ray.y,ray.z ); +// gameLocal.Printf(" Angle: %f\n", RAD2DEG( idMath::ACos( rightNorm * ray ) ) ); +// gameLocal.Printf(" physaxis: %f,%f,%f\n",physicsObj.GetAxis().ToAngles().yaw,physicsObj.GetAxis().ToAngles().pitch,physicsObj.GetAxis().ToAngles().roll ); +// gameLocal.Printf(" Origin: %f,%f,%f\n",physicsObj.GetOrigin().x,physicsObj.GetOrigin().y,physicsObj.GetOrigin().z ); + // plane(normal, distance) + /* + float dist = -(geo.faces[i].plane->Normal() * geo.faces[i].verts[0]); + dist = dist + physicsObj.GetOrigin() * physicsObj.GetAxis(); + idPlane plane( geo.faces[i].plane->Normal(), dist) + */ + + //pa = physicsObj.GetOrigin() * physicsObj.GetAxis() + + + // make a plane that is where the face is in the world + +/* + gameLocal.Printf("origin: %f,%f,%f\n", physicsObj.GetOrigin().x, physicsObj.GetOrigin().y, physicsObj.GetOrigin().z); + idVec3 or = physicsObj.GetOrigin() * physicsObj.GetAxis(); + gameLocal.Printf("toaxis: %f,%f,%f\n", or.x, or.y, or.z); + gameLocal.Printf("apoint: %f,%f,%f\n", geo.faces[i].verts[0].x, geo.faces[i].verts[0].y, geo.faces[i].verts[0].z); + idVec3 pa = geo.faces[i].verts[0] * physicsObj.GetAxis(); + gameLocal.Printf("toaxis: %f,%f,%f\n", pa.x, pa.y, pa.z); + pa = (physicsObj.GetOrigin() + geo.faces[i].verts[0]); + gameLocal.Printf("added: %f,%f,%f\n\n", pa.x, pa.y, pa.z); + idVec3 no = geo.faces[i].plane->Normal(); + gameLocal.Printf("normal: %f,%f,%f\n\n", geo.faces[i].plane->Normal().x, geo.faces[i].plane->Normal().y, geo.faces[i].plane->Normal().z); + no *= physicsObj.GetAxis(); + gameLocal.Printf("toaxis: %f,%f,%f\n\n", no.x, no.y, no.z); + float dist = -( geo.faces[i].plane->Normal() * pa ); + gameLocal.Printf("dist: %f", dist); + idPlane plane( no, dist); + if (plane.Side( lastiPoint, ON_EPSILON ) == PLANESIDE_FRONT ) { +// hits.Append(i); // mark it as a hit +// } + if (plane.Side( lastiPoint, ON_EPSILON ) == PLANESIDE_ON ) { +// hits.Append(i); // mark it as a hit + } +*/ +/* + if (idMath::ACos( rightNorm * ray ) < DEG2RAD(90) && // if angle between face normal and angle of impact < 90 + geo.faces[i].plane->RayIntersection( lastiPoint, rightCenter - lastiPoint, tmpf ) // and it passes through the face + ) { + gameLocal.Printf("hit\n"); + hits.Append(i); // mark it as a hit + } */ + //} + +// if ( hits.Num() < 1 ) { +// return; // this should never happen +// } + +// gameLocal.Printf("hits: %i\n", hits.Num() ); + +/* + // find the closest face-center to impact + bestHit = hits[0]; + for ( i=1; i geo.avgArea * (2/3) ) { +// gameLocal.Printf("Break, side %i, area %f\n", i, geo.faces[i].area); + +// const idDeclParticle *splashParticleTiny = static_cast( declManager->FindType( DECL_PARTICLE, "water_body_splash_tiny" ) ); +// gameLocal.smokeParticles->EmitSmoke( splashParticleTiny, gameLocal.time, gameLocal.random.CRandomFloat(), lastiPoint, idAngles( 0, gameLocal.random.CRandomFloat() * 360, 0 ).ToMat3() ); + + /// spawn two halves, touching where hit occured. same texture. do some math to figure out shape. + /// spawn some shards with somewhat random vertices so they're shaped differently + /// spawn some shards attached to board halves where hit occured (for splints) + /// spawn some brittle fracture + /// remove board +/* } else { + // just spawn some chipped debris + gameLocal.Printf("Cip, side %i, area %f\n", i, geo.faces[i].area); + /// if hit on the short side less than half the length of an adjacent side) + /// spawn largest portion. same texture. do some math to figure out shape. + /// spawn some brittle fracture + } +*/ + + + // * amount of debris dependant upon size + // * health dependant upon size + + + +/************************************************************************* +Test code for trying to create clip models on the fly +*************************************************************************/ + +/**/ // remove clip model +//physicsObj.DisableClip(); +//delete physicsObj.clipModel; +//physicsObj.clipModel = NULL; +//physicsObj.DisableImpact(); + + +/** // create a new clip model + //FreeModelDef(); + //UpdateVisuals(); + physicsObj.UnlinkClip(); + idTraceModel trm; + float density, friction, bouncyness, mass; + int clipShrink; + idStr renderModelName = spawnArgs.GetString( "model" ); + // create clipModel from renderModel + if ( !collisionModelManager->TrmFromModel( renderModelName, trm ) ) { + gameLocal.Error( "idMoveable '%s': cannot load collision model %s", name.c_str(), renderModelName.c_str() ); + return; + } + //trm.bounds = suu->bounds = idBounds(min, max); + // get rigid body properties + spawnArgs.GetFloat( "density", "0.5", density ); + density = idMath::ClampFloat( 0.001f, 1000.0f, density ); +// spawnArgs.GetFloat( "friction", "0.05", friction ); +// friction = idMath::ClampFloat( 0.0f, 1.0f, friction ); +// spawnArgs.GetFloat( "bouncyness", "0.6", bouncyness ); +// bouncyness = idMath::ClampFloat( 0.0f, 1.0f, bouncyness ); +// damage = spawnArgs.GetString( "def_damage", "" ); +// canDamage +// minDamageVelocity = spawnArgs.GetFloat( "minDamageVelocity", "300" ); // _D3XP +// maxDamageVelocity = spawnArgs.GetFloat( "maxDamageVelocity", "700" ); // _D3XP + // setup the physics + //physicsObj.clipModel +// physicsObj.SetSelf( this ); + // ??? physicsObj.GetClipModel()->CheckModel( const char *name ); + //physicsObj.GetClipModel()->ClearTraceModelCache(); + //physicsObj.GetClipModel()->TraceModelCacheSize(); + //nooope physicsObj.SetClipModel( new idClipModel( trm ), density, 0, true ); + //physicsObj.SetClipModel( new idClipModel( modelDefHandle ), density, 0, true ); //nooope + // noooope + //int hnd = physicsObj.GetClipModel()->GetRenderModelHandle(); + // physicsObj.SetClipModel( new idClipModel( hnd ), density, 0, true ); + /* nope + idRenderModel *ass( renderEntity.hModel ); + renderEntity_t re(renderEntity); + re.hModel = ass; + physicsObj.SetClipModel( new idClipModel( gameRenderWorld->AddEntityDef( &re ) ), density, 0, true ); + */ +/* + physicsObj.SetClipModel( new idClipModel( gameRenderWorld->AddEntityDef( &re ) ), density, 0, true ); + physicsObj.GetClipModel()->SetMaterial( GetRenderModelMaterial() ); + physicsObj.LinkClip(); +*/ + +// physicsObj.SetOrigin( GetPhysics()->GetOrigin() ); +// physicsObj.SetAxis( GetPhysics()->GetAxis() ); +// physicsObj.SetBouncyness( bouncyness ); +// physicsObj.SetFriction( 0.6f, 0.6f, friction ); +// physicsObj.SetGravity( gameLocal.GetGravity() ); +// physicsObj.SetContents( CONTENTS_SOLID ); +// physicsObj.SetClipMask( MASK_SOLID | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_MOVEABLECLIP ); +// SetPhysics( &physicsObj ); + +// physicsObj.SetMass( mass ); +// physicsObj.DropToFloor(); +// physicsObj.DisableImpact(); +// BecomeNonSolid(); +// allowStep = spawnArgs.GetBool( "allowStep", "1" ); + +/** // update things again + //UpdateModel(); + UpdateModelTransform(); + UpdateVisuals(); + //renderEntity.forceUpdate; // ? +**/ + + +/*/ // method 2: remove surfaces, add new ones, + asds + // allocate new surface ( contains all faces of model, init with previous model ) + modelSurface_t sur( *renderEntity.hModel->Surface(0) ); + // update the bounds + // ... + // resize the new surfaces + for ( v=0; v < sur.geometry->numVerts; v++ ) { + sur.geometry->verts[v].xyz /= 2; + } + // disappear the render model +// renderEntity.hModel->PurgeModel(); +// srfTriangles_t tri; +// for ( int i=0, vrt=0; i < sur.geometry->numVerts / 3; i++, vrt++ ) { + //tri.vert + // sur.geometry->nums + // sur.geometry->facePlanes[ + // renderEntity.hModel->FreeSurfaceTriangles( &tri ); +///// renderEntity.hModel->FreeSurfaceTriangles( renderEntity.hModel->Surface(0)->geometry ); +// } +// assign new surfaces +/// AllocSurfaceTriangles +// renderEntity.hModel->AddSurface( sur ); +/// FinishSurfaces +// renderEntity.hModel->AddSurface( sur ); // crashed the game... + //renderEntity.hModel->FinishSurfaces(); + // create the new clip model + //idTraceModel nt( sur.geometry->bounds ); + physicsObj.GetClipModel()->LoadModel( sur.geometry->bounds; +/**/ + + +/*************/ +// this is how to create a renderEntity/renderModel +// cant create renderModel with static models +/* + idRenderModel *newmodel; + renderEntity_t ent; + memset( &ent, 0, sizeof( ent ) ); + ent.bounds.Clear(); + ent.suppressSurfaceInViewID = 0; + ent.numJoints = renderEntity.hModel->NumJoints(); + ent.joints = ( idJointMat * )Mem_Alloc16( ent.numJoints * sizeof( *ent.joints ) ); + newmodel = renderEntity.hModel->InstantiateDynamicModel( &ent, NULL, NULL ); + Mem_Free16( ent.joints ); + ent.joints = NULL; + return; + // newmodel; +/**************/ + + +/* + modelSurface_t sur( *renderEntity.hModel->Surface(0) ); + for ( v=0; v < sur.geometry->numVerts; v++ ) { + sur.geometry->verts[v].xyz /= 2; + } + renderEntity.hModel->FreeSurfaceTriangles(); + //PurgeModel(); + renderEntity.hModel->AddSurface( sur ); + //renderEntity.hModel->FinishSurfaces(); + idTraceModel nt( renderEntity.hModel->Bounds() ); + physicsObj.GetClipModel()->LoadModel( nt ); + //renderEntity.hModel->InitEmpty( "something" ); + UpdateModel(); + UpdateModelTransform(); + UpdateVisuals(); + //renderEntity.forceUpdate; + + + //physicsObj.GetClipModel()->LoadModel( modelDefHandle ); + // err: TraceModelForClipModel: clip model 0 on wood_2 is not a trace model + + //renderEntity.hModel = renderModelManager->AllocModel(); + //renderEntity.hModel->InitEmpty( brittleFracture_SnapshotName ); +*/ +} +#endif diff --git a/game/Moveable.h b/game/Moveable.h index 21f0092..51d686c 100644 --- a/game/Moveable.h +++ b/game/Moveable.h @@ -88,8 +88,13 @@ protected: int nextDamageTime; // next time the movable can hurt the player int nextSoundTime; // next time the moveable can make a sound +// HEXEN : Zeroth +protected: + bool brokenScript; // call ::Broken on killed. + bool removeWhenBroken; // removes entity after it's "fuse" delay + + const idMaterial * GetRenderModelMaterial( void ) const; - void BecomeNonSolid( void ); void InitInitialSpline( int startTime ); bool FollowInitialSplinePath( void ); @@ -98,6 +103,19 @@ protected: void Event_SetOwnerFromSpawnArgs( void ); void Event_IsAtRest( void ); void Event_EnableDamage( float enable ); + void Event_DirectDamage( idEntity *damageTarget, const char *damageDefName ); + + void DirectDamage( const char *meleeDefName, idEntity *ent ); +// HEXEN : Zeroth +protected: + void Event_BecomeSolid( void ); + +// HEXEN : Zeroth +public: + void BecomeNonSolid( void ); + void BecomeSolid( void ); +public: + bool savePersistentInfo; }; @@ -160,7 +178,7 @@ public: virtual void Think( void ); virtual void Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, - const char *damageDefName, const float damageScale, const int location ); + const char *damageDefName, const float damageScale, const int location, const idVec3 &iPoint ); virtual void Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ); virtual void WriteToSnapshot( idBitMsgDelta &msg ) const; @@ -201,4 +219,65 @@ private: void Event_TriggerTargets(); }; +#if 0 +// HEXEN : Zeroth +// for idWood and other entities with renderModels that need more vertex/face info +class Destructible_FaceData_t { +public: + // this could be a bit more effiecient with idVec3 pointers that point to surface geometry... + idList< idVec3 > verts; + idVec3 center; // center of face + float area; + idPlane * plane; + idList< int > adjacent; // stores indexes of adjacent faces +// idVec3 worldCenter(void) const; +// idVec3 worldNotm(void) const; +}; + + +//ID_INLINE idVec3 faceData_t::worldCenter(void) const { +// return center + physicsObj.GetOrigin() * physicsObj.GetAxis(); +//} + +//ID_INLINE idVec3 faceData_t::worldNorm(void) const { +// return norm + physicsObj.GetOrigin() * physicsObj.GetAxis(); +//} + +class Destructible_ModelData_t { +public: + idList faces; + idVec3 center; + float area; + float avgArea; +// idVec3 worldCenter(void) const; +}; + +//ID_INLINE idVec3 geoData_t::worldCenter(void) const { +// return geo.center + physicsObj.GetOrigin() * physicsObj.GetAxis(); +//} + +// HEXEN : Zeroth +class idWood : public idMoveable { +public: + CLASS_PROTOTYPE( idWood ); + + idWood::idWood( void ); + + void Spawn( void ); + virtual void Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ); + void Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, + const char *damageDefName, const float damageScale, const int location, idVec3 &iPoint ); + bool CheckFloating( idWood *caller, idWood *chain[], int c ); + + bool geoConstructed; + Destructible_ModelData_t geo; + + idVec3 lastiPoint; // last damage position + +private: + void ConstructGeo( void ); + +}; +#endif + #endif /* !__GAME_MOVEABLE_H__ */ diff --git a/game/Mover.cpp b/game/Mover.cpp index 9f1a819..ffadc66 100644 --- a/game/Mover.cpp +++ b/game/Mover.cpp @@ -90,6 +90,11 @@ const idEventDef EV_StartSpline( "startSpline", "e" ); const idEventDef EV_StopSpline( "stopSpline", NULL ); const idEventDef EV_IsMoving( "isMoving", NULL, 'd' ); const idEventDef EV_IsRotating( "isRotating", NULL, 'd' ); +const idEventDef EV_EnableClip( "enableClip" ); +const idEventDef EV_DisableClip( "disableClip" ); +const idEventDef EV_CanBecomeSolid( "canBecomeSolid", NULL, 'f' ); +const idEventDef EV_MoverBecomeSolid( "becomeSolid" ); +const idEventDef EV_MoverBecomeNonSolid( "becomeNonSolid" ); CLASS_DECLARATION( idEntity, idMover ) EVENT( EV_FindGuiTargets, idMover::Event_FindGuiTargets ) @@ -131,6 +136,11 @@ CLASS_DECLARATION( idEntity, idMover ) EVENT( EV_Activate, idMover::Event_Activate ) EVENT( EV_IsMoving, idMover::Event_IsMoving ) EVENT( EV_IsRotating, idMover::Event_IsRotating ) + EVENT( EV_EnableClip, idMover::Event_EnableClip ) + EVENT( EV_DisableClip, idMover::Event_DisableClip ) + EVENT( EV_CanBecomeSolid, idMover::Event_CanBecomeSolid ) + EVENT( EV_MoverBecomeSolid, idMover::Event_BecomeSolid ) + EVENT( EV_MoverBecomeNonSolid, idMover::Event_BecomeNonSolid ) END_CLASS /* @@ -345,7 +355,9 @@ void idMover::Spawn( void ) { spawnArgs.GetFloat( "damage" , "0", damage ); dest_position = GetPhysics()->GetOrigin(); + SetPersistentPos(dest_position); dest_angles = GetPhysics()->GetAxis().ToAngles(); + SetPersistentAng(dest_angles); physicsObj.SetSelf( this ); physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f ); @@ -999,7 +1011,7 @@ idMover::Event_PartBlocked */ void idMover::Event_PartBlocked( idEntity *blockingEntity ) { if ( damage > 0.0f ) { - blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT ); + blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT, idVec3( 0, 0, 0 ) ); } if ( g_debugMover.GetBool() ) { gameLocal.Printf( "%d: '%s' blocked by '%s'\n", gameLocal.time, name.c_str(), blockingEntity->name.c_str() ); @@ -1034,6 +1046,18 @@ void idMover::Event_SetMoveTime( float time ) { move_time = SEC2MS( time ); } +#if 0 +/* +================ +Zeroth +idMover::eoc_SetMoveTime +================ +*/ +void idMover::eoc_SetMoveTime( float time ) { + Event_SetMoveTime( time ); +} +#endif + /* ================ idMover::Event_SetAccellerationTime @@ -1071,9 +1095,54 @@ void idMover::Event_MoveTo( idEntity *ent ) { } dest_position = GetLocalCoordinates( ent->GetPhysics()->GetOrigin() ); + SetPersistentPos( dest_position ); BeginMove( idThread::CurrentThread() ); } +/* +================ +Zeroth +idMover::SetPersistentPos +================ +*/ +void idMover::SetPersistentPos( idVec3 &pos ) { + // for removing items/creatures/etc after returning to a level (hubs) + idStr name_str, st; + + name_str = gameLocal.GetMapName(); + name_str += "_mover_pos"; + + gameLocal.persistentLevelInfo.SetVector( name_str, pos ); +} + +/* +================ +Zeroth +idMover::SetPersistentAng +================ +*/ +void idMover::SetPersistentAng( idAngles &ang ) { + // for removing items/creatures/etc after returning to a level (hubs) + idStr name_str, st; + + name_str = gameLocal.GetMapName(); + name_str += "_mover_ang"; + + gameLocal.persistentLevelInfo.SetAngles( name_str, ang ); +} + +#if 0 +/* +================ +Zeroth +idMover::eoc_MoveToPos +================ +*/ +void idMover::eoc_MoveToPos( const idVec3 &pos ) { + MoveToPos( pos ); +} +#endif + /* ================ idMover::MoveToPos @@ -1081,6 +1150,7 @@ idMover::MoveToPos */ void idMover::MoveToPos( const idVec3 &pos ) { dest_position = GetLocalCoordinates( pos ); + SetPersistentPos( dest_position ); BeginMove( NULL ); } @@ -1105,6 +1175,7 @@ void idMover::Event_MoveDir( float angle, float distance ) { physicsObj.GetLocalOrigin( org ); VectorForDir( angle, dir ); dest_position = org + dir * distance; + SetPersistentPos( dest_position ); BeginMove( idThread::CurrentThread() ); } @@ -1456,6 +1527,10 @@ idMover::Event_Activate ================ */ void idMover::Event_Activate( idEntity *activator ) { + if ( spawnArgs.GetBool( "NoActivateWhenTriggered", "0" ) ) { + return; + } + Show(); Event_StartSpline( this ); } @@ -1535,6 +1610,112 @@ void idMover::SetPortalState( bool open ) { gameLocal.SetPortalState( areaPortal, open ? PS_BLOCK_NONE : PS_BLOCK_ALL ); } +/* +================ +HEXEN +idMover::Event_EnableClip +================ +*/ +void idMover::Event_EnableClip( void ) { + physicsObj.SetClipMask( MASK_SOLID ); +} + +/* +================ +HEXEN +idMover::Event_DisableClip +================ +*/ +void idMover::Event_DisableClip( void ) { + physicsObj.SetClipMask( 0 ); +} + +/* +================ +HEXEN +idMover::Event_CanBecomeSolid +================ +*/ +void idMover::Event_CanBecomeSolid( void ) { + idThread::ReturnFloat( CanBecomeSolid() ); +} + +/* +================ +HEXEN +idMover::CanBecomeSolid +================ +*/ +bool idMover::CanBecomeSolid( void ) { + int i; + int num; + idEntity * hit; + idClipModel *cm; + idClipModel *clipModels[ MAX_GENTITIES ]; + + num = gameLocal.clip.ClipModelsTouchingBounds( physicsObj.GetAbsBounds(), MASK_MONSTERSOLID, clipModels, MAX_GENTITIES ); + for ( i = 0; i < num; i++ ) { + cm = clipModels[ i ]; + + // don't check render entities + if ( cm->IsRenderModel() ) { + continue; + } + + hit = cm->GetEntity(); + if ( ( hit == this ) || !hit->fl.takedamage ) { + continue; + } + + if ( physicsObj.ClipContents( cm ) ) { + return false; + } + } + + return true; +} + +/* +================ +HEXEN +idMover::Event_BecomeSolid +================ +*/ +void idMover::Event_BecomeSolid( void ) { + BecomeSolid( ); +} + +/* +================ +HEXEN +idMover::BecomeSolid +================ +*/ +void idMover::BecomeSolid( void ) { + physicsObj.SetContents( MASK_SOLID ); +} + +/* +================ +HEXEN +idMover::Event_BecomeNonSolid +================ +*/ +void idMover::Event_BecomeNonSolid( void ) { + BecomeNonSolid(); +} + +/* +================ +HEXEN +idMover::BecomeSolid +================ +*/ +void idMover::BecomeNonSolid( void ) { + physicsObj.SetContents( 0 ); +} + + /* =============================================================================== @@ -3788,7 +3969,7 @@ idDoor::Event_PartBlocked */ void idDoor::Event_PartBlocked( idEntity *blockingEntity ) { if ( damage > 0.0f ) { - blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT ); + blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT, idVec3( 0, 0, 0 ) ); } } @@ -4225,7 +4406,7 @@ idPlat::Event_PartBlocked */ void idPlat::Event_PartBlocked( idEntity *blockingEntity ) { if ( damage > 0.0f ) { - blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT ); + blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT, idVec3( 0, 0, 0 ) ); } } @@ -4316,7 +4497,7 @@ idMover_Periodic::Event_PartBlocked */ void idMover_Periodic::Event_PartBlocked( idEntity *blockingEntity ) { if ( damage > 0.0f ) { - blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT ); + blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT, idVec3( 0, 0, 0 ) ); } } diff --git a/game/Mover.h b/game/Mover.h index 7b95598..d0741c7 100644 --- a/game/Mover.h +++ b/game/Mover.h @@ -66,6 +66,26 @@ public: void SetPortalState( bool open ); + void Event_EnableClip( void ); + void Event_DisableClip( void ); + void Event_BecomeSolid( void ); + void Event_CanBecomeSolid( void ); + void Event_BecomeNonSolid( void ); + bool CanBecomeSolid( void ); + void BecomeSolid( void ); + void BecomeNonSolid( void ); + + // only for use with hub-style map initialization + void SetDestPos( const idVec3 &pos ) { dest_position=pos; } + void SetDestAng( const idAngles &ang ) { dest_angles=ang; } + +// HEXEN : Zeroth +// void eoc_SetMoveTime( float time ); +// void eoc_MoveToPos( const idVec3 &pos ); + + void SetPersistentPos( idVec3 &pos ); + void SetPersistentAng( idAngles &ang ); + protected: typedef enum { ACCELERATION_STAGE,