mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-29 23:51:49 +00:00
736ec20d4d
Don't include the lazy precompiled.h everywhere, only what's required for the compilation unit. platform.h needs to be included instead to provide all essential defines and types. All includes use the relative path to the neo or the game specific root. Move all idlib related includes from idlib/Lib.h to precompiled.h. precompiled.h still exists for the MFC stuff in tools/. Add some missing header guards.
740 lines
20 KiB
C++
740 lines
20 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 GPL Source Code
|
|
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
|
|
|
|
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#ifdef _D3XP
|
|
|
|
#include "sys/platform.h"
|
|
|
|
#include "gamesys/SysCvar.h"
|
|
#include "ai/AI.h"
|
|
#include "Player.h"
|
|
#include "Moveable.h"
|
|
#include "Misc.h"
|
|
|
|
#include "Grabber.h"
|
|
|
|
#define MAX_DRAG_TRACE_DISTANCE 384.0f
|
|
#define TRACE_BOUNDS_SIZE 3.f
|
|
#define HOLD_DISTANCE 72.f
|
|
#define FIRING_DELAY 1000.0f
|
|
#define DRAG_FAIL_LEN 64.f
|
|
#define THROW_SCALE 1000
|
|
#define MAX_PICKUP_VELOCITY 1500 * 1500
|
|
#define MAX_PICKUP_SIZE 96
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
Allows entities to be dragged through the world with physics.
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION( idEntity, idGrabber )
|
|
END_CLASS
|
|
|
|
/*
|
|
==============
|
|
idGrabber::idGrabber
|
|
==============
|
|
*/
|
|
idGrabber::idGrabber( void ) {
|
|
dragEnt = NULL;
|
|
owner = NULL;
|
|
beam = NULL;
|
|
beamTarget = NULL;
|
|
oldUcmdFlags = 0;
|
|
shakeForceFlip = false;
|
|
holdingAF = false;
|
|
endTime = 0;
|
|
lastFiredTime = -FIRING_DELAY;
|
|
dragFailTime = 0;
|
|
startDragTime = 0;
|
|
warpId = -1;
|
|
dragTraceDist = MAX_DRAG_TRACE_DISTANCE;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idGrabber::~idGrabber
|
|
==============
|
|
*/
|
|
idGrabber::~idGrabber( void ) {
|
|
StopDrag( true );
|
|
if ( beam ) {
|
|
delete beam;
|
|
}
|
|
if ( beamTarget ) {
|
|
delete beamTarget;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idGrabber::Save
|
|
==============
|
|
*/
|
|
void idGrabber::Save( idSaveGame *savefile ) const {
|
|
|
|
dragEnt.Save( savefile );
|
|
savefile->WriteStaticObject( drag );
|
|
|
|
savefile->WriteVec3( saveGravity );
|
|
savefile->WriteInt( id );
|
|
|
|
savefile->WriteVec3( localPlayerPoint );
|
|
|
|
owner.Save( savefile );
|
|
|
|
savefile->WriteBool( holdingAF );
|
|
savefile->WriteBool( shakeForceFlip );
|
|
|
|
savefile->WriteInt( endTime );
|
|
savefile->WriteInt( lastFiredTime );
|
|
savefile->WriteInt( dragFailTime );
|
|
savefile->WriteInt( startDragTime );
|
|
savefile->WriteFloat( dragTraceDist );
|
|
savefile->WriteInt( savedContents );
|
|
savefile->WriteInt( savedClipmask );
|
|
|
|
savefile->WriteObject( beam );
|
|
savefile->WriteObject( beamTarget );
|
|
|
|
savefile->WriteInt( warpId );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idGrabber::Restore
|
|
==============
|
|
*/
|
|
void idGrabber::Restore( idRestoreGame *savefile ) {
|
|
//Spawn the beams
|
|
Initialize();
|
|
|
|
dragEnt.Restore( savefile );
|
|
savefile->ReadStaticObject( drag );
|
|
|
|
savefile->ReadVec3( saveGravity );
|
|
savefile->ReadInt( id );
|
|
|
|
// Restore the drag force's physics object
|
|
if ( dragEnt.IsValid() ) {
|
|
drag.SetPhysics( dragEnt.GetEntity()->GetPhysics(), id, dragEnt.GetEntity()->GetPhysics()->GetOrigin() );
|
|
}
|
|
|
|
savefile->ReadVec3( localPlayerPoint );
|
|
|
|
owner.Restore( savefile );
|
|
|
|
savefile->ReadBool( holdingAF );
|
|
savefile->ReadBool( shakeForceFlip );
|
|
|
|
savefile->ReadInt( endTime );
|
|
savefile->ReadInt( lastFiredTime );
|
|
savefile->ReadInt( dragFailTime );
|
|
savefile->ReadInt( startDragTime );
|
|
savefile->ReadFloat( dragTraceDist );
|
|
savefile->ReadInt( savedContents );
|
|
savefile->ReadInt( savedClipmask );
|
|
|
|
savefile->ReadObject( reinterpret_cast<idClass *&>(beam) );
|
|
savefile->ReadObject( reinterpret_cast<idClass *&>(beamTarget) );
|
|
|
|
savefile->ReadInt( warpId );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idGrabber::Initialize
|
|
==============
|
|
*/
|
|
void idGrabber::Initialize( void ) {
|
|
if ( !gameLocal.isMultiplayer ) {
|
|
idDict args;
|
|
|
|
if ( !beamTarget ) {
|
|
args.SetVector( "origin", vec3_origin );
|
|
args.SetBool( "start_off", true );
|
|
beamTarget = ( idBeam * )gameLocal.SpawnEntityType( idBeam::Type, &args );
|
|
}
|
|
|
|
if ( !beam ) {
|
|
args.Clear();
|
|
args.Set( "target", beamTarget->name.c_str() );
|
|
args.SetVector( "origin", vec3_origin );
|
|
args.SetBool( "start_off", true );
|
|
args.Set( "width", "6" );
|
|
args.Set( "skin", "textures/smf/flareSizeable" );
|
|
args.Set( "_color", "0.0235 0.843 0.969 0.2" );
|
|
beam = ( idBeam * )gameLocal.SpawnEntityType( idBeam::Type, &args );
|
|
beam->SetShaderParm( 6, 1.0f );
|
|
}
|
|
|
|
endTime = 0;
|
|
dragTraceDist = MAX_DRAG_TRACE_DISTANCE;
|
|
}
|
|
else {
|
|
beam = NULL;
|
|
beamTarget = NULL;
|
|
endTime = 0;
|
|
dragTraceDist = MAX_DRAG_TRACE_DISTANCE;
|
|
};
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idGrabber::SetDragDistance
|
|
==============
|
|
*/
|
|
void idGrabber::SetDragDistance( float dist ) {
|
|
dragTraceDist = dist;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idGrabber::StartDrag
|
|
==============
|
|
*/
|
|
void idGrabber::StartDrag( idEntity *grabEnt, int id ) {
|
|
int clipModelId = id;
|
|
idPlayer *thePlayer = owner.GetEntity();
|
|
|
|
holdingAF = false;
|
|
dragFailTime = gameLocal.slow.time;
|
|
startDragTime = gameLocal.slow.time;
|
|
|
|
oldUcmdFlags = thePlayer->usercmd.flags;
|
|
|
|
// set grabbed state for networking
|
|
grabEnt->SetGrabbedState( true );
|
|
|
|
// This is the new object to drag around
|
|
dragEnt = grabEnt;
|
|
|
|
// Show the beams!
|
|
UpdateBeams();
|
|
if ( beam ) {
|
|
beam->Show();
|
|
}
|
|
if ( beamTarget ) {
|
|
beamTarget->Show();
|
|
}
|
|
|
|
// Move the object to the fast group (helltime)
|
|
grabEnt->timeGroup = TIME_GROUP2;
|
|
|
|
// Handle specific class types
|
|
if ( grabEnt->IsType( idProjectile::Type ) ) {
|
|
idProjectile* p = (idProjectile*)grabEnt;
|
|
|
|
p->CatchProjectile( thePlayer, "_catch" );
|
|
|
|
// Make the projectile non-solid to other projectiles/enemies (special hack for helltime hunter)
|
|
if ( !idStr::Cmp( grabEnt->GetEntityDefName(), "projectile_helltime_killer" ) ) {
|
|
savedContents = CONTENTS_PROJECTILE;
|
|
savedClipmask = MASK_SHOT_RENDERMODEL|CONTENTS_PROJECTILE;
|
|
} else {
|
|
savedContents = grabEnt->GetPhysics()->GetContents();
|
|
savedClipmask = grabEnt->GetPhysics()->GetClipMask();
|
|
}
|
|
grabEnt->GetPhysics()->SetContents( 0 );
|
|
grabEnt->GetPhysics()->SetClipMask( CONTENTS_SOLID|CONTENTS_BODY );
|
|
|
|
} else if ( grabEnt->IsType( idExplodingBarrel::Type ) ) {
|
|
idExplodingBarrel *ebarrel = static_cast<idExplodingBarrel*>(grabEnt);
|
|
|
|
ebarrel->StartBurning();
|
|
|
|
} else if ( grabEnt->IsType( idAFEntity_Gibbable::Type ) ) {
|
|
holdingAF = true;
|
|
clipModelId = 0;
|
|
|
|
if ( grabbableAI( grabEnt->spawnArgs.GetString( "classname" ) ) ) {
|
|
idAI *aiEnt = static_cast<idAI*>(grabEnt);
|
|
|
|
aiEnt->StartRagdoll();
|
|
}
|
|
} else if ( grabEnt->IsType( idMoveableItem::Type ) ) {
|
|
grabEnt->PostEventMS( &EV_Touch, 250, thePlayer, 0 );
|
|
}
|
|
|
|
// Get the current physics object to manipulate
|
|
idPhysics *phys = grabEnt->GetPhysics();
|
|
|
|
// Turn off gravity on object
|
|
saveGravity = phys->GetGravity();
|
|
phys->SetGravity( vec3_origin );
|
|
|
|
// hold it directly in front of player
|
|
localPlayerPoint = ( thePlayer->firstPersonViewAxis[0] * HOLD_DISTANCE ) * thePlayer->firstPersonViewAxis.Transpose();
|
|
|
|
// Set the ending time for the hold
|
|
endTime = gameLocal.time + g_grabberHoldSeconds.GetFloat() * 1000;
|
|
|
|
// Start up the Force_Drag to bring it in
|
|
drag.Init( g_grabberDamping.GetFloat() );
|
|
drag.SetPhysics( phys, clipModelId, thePlayer->firstPersonViewOrigin + localPlayerPoint * thePlayer->firstPersonViewAxis);
|
|
|
|
// start the screen warp
|
|
warpId = thePlayer->playerView.AddWarp( phys->GetOrigin(), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 160, 2000 );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idGrabber::StopDrag
|
|
==============
|
|
*/
|
|
void idGrabber::StopDrag( bool dropOnly ) {
|
|
idPlayer *thePlayer = owner.GetEntity();
|
|
|
|
if ( beam ) {
|
|
beam->Hide();
|
|
}
|
|
if ( beamTarget ) {
|
|
beamTarget->Hide();
|
|
}
|
|
|
|
if ( dragEnt.IsValid() ) {
|
|
idEntity *ent = dragEnt.GetEntity();
|
|
|
|
// set grabbed state for networking
|
|
ent->SetGrabbedState( false );
|
|
|
|
// If a cinematic has started, allow dropped object to think in cinematics
|
|
if ( gameLocal.inCinematic ) {
|
|
ent->cinematic = true;
|
|
}
|
|
|
|
// Restore Gravity
|
|
ent->GetPhysics()->SetGravity( saveGravity );
|
|
|
|
// Move the object back to the slow group (helltime)
|
|
ent->timeGroup = TIME_GROUP1;
|
|
|
|
if ( holdingAF ) {
|
|
idAFEntity_Gibbable *af = static_cast<idAFEntity_Gibbable *>(ent);
|
|
idPhysics_AF *af_Phys = static_cast<idPhysics_AF*>(af->GetPhysics());
|
|
|
|
if ( grabbableAI( ent->spawnArgs.GetString( "classname" ) ) ) {
|
|
idAI *aiEnt = static_cast<idAI*>(ent);
|
|
|
|
aiEnt->Damage( thePlayer, thePlayer, vec3_origin, "damage_suicide", 1.0f, INVALID_JOINT );
|
|
}
|
|
|
|
af->SetThrown( !dropOnly );
|
|
|
|
// Reset timers so that it isn't forcibly put to rest in mid-air
|
|
af_Phys->PutToRest();
|
|
af_Phys->Activate();
|
|
|
|
af_Phys->SetTimeScaleRamp( MS2SEC(gameLocal.slow.time) - 1.5f, MS2SEC(gameLocal.slow.time) + 1.0f );
|
|
}
|
|
|
|
// If the object isn't near its goal, just drop it in place.
|
|
if ( !ent->IsType( idProjectile::Type ) && ( dropOnly || drag.GetDistanceToGoal() > DRAG_FAIL_LEN ) ) {
|
|
ent->GetPhysics()->SetLinearVelocity( vec3_origin );
|
|
thePlayer->StartSoundShader( declManager->FindSound( "grabber_maindrop" ), SND_CHANNEL_WEAPON, 0, false, NULL );
|
|
|
|
if ( ent->IsType( idExplodingBarrel::Type ) ) {
|
|
idExplodingBarrel *ebarrel = static_cast<idExplodingBarrel*>(ent);
|
|
|
|
ebarrel->SetStability( true );
|
|
ebarrel->StopBurning();
|
|
}
|
|
} else {
|
|
// Shoot the object forward
|
|
ent->ApplyImpulse( thePlayer, 0, ent->GetPhysics()->GetOrigin(), thePlayer->firstPersonViewAxis[0] * THROW_SCALE * ent->GetPhysics()->GetMass() );
|
|
thePlayer->StartSoundShader( declManager->FindSound( "grabber_release" ), SND_CHANNEL_WEAPON, 0, false, NULL );
|
|
|
|
// Orient projectiles away from the player
|
|
if ( ent->IsType( idProjectile::Type ) ) {
|
|
idPlayer *player = owner.GetEntity();
|
|
idAngles ang = player->firstPersonViewAxis[0].ToAngles();
|
|
|
|
ang.pitch += 90.f;
|
|
ent->GetPhysics()->SetAxis( ang.ToMat3() );
|
|
ent->GetPhysics()->SetAngularVelocity( vec3_origin );
|
|
|
|
// Restore projectile contents
|
|
ent->GetPhysics()->SetContents( savedContents );
|
|
ent->GetPhysics()->SetClipMask( savedClipmask );
|
|
|
|
} else if ( ent->IsType( idMoveable::Type ) ) {
|
|
// Turn on damage for this object
|
|
idMoveable *obj = static_cast<idMoveable*>(ent);
|
|
obj->EnableDamage( true, 2.5f );
|
|
obj->SetAttacker( thePlayer );
|
|
|
|
if ( ent->IsType( idExplodingBarrel::Type ) ) {
|
|
idExplodingBarrel *ebarrel = static_cast<idExplodingBarrel*>(ent);
|
|
ebarrel->SetStability( false );
|
|
}
|
|
|
|
} else if ( ent->IsType( idMoveableItem::Type ) ) {
|
|
ent->GetPhysics()->SetClipMask( MASK_MONSTERSOLID );
|
|
}
|
|
}
|
|
|
|
// Remove the Force_Drag's control of the entity
|
|
drag.RemovePhysics( ent->GetPhysics() );
|
|
}
|
|
|
|
if ( warpId != -1 ) {
|
|
thePlayer->playerView.FreeWarp( warpId );
|
|
warpId = -1;
|
|
}
|
|
|
|
lastFiredTime = gameLocal.time;
|
|
dragEnt = NULL;
|
|
endTime = 0;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idGrabber::Update
|
|
==============
|
|
*/
|
|
int idGrabber::Update( idPlayer *player, bool hide ) {
|
|
trace_t trace;
|
|
idEntity *newEnt;
|
|
|
|
// pause before allowing refire
|
|
if ( lastFiredTime + FIRING_DELAY > gameLocal.time ) {
|
|
return 3;
|
|
}
|
|
|
|
// Dead players release the trigger
|
|
if ( hide || player->health <= 0 ) {
|
|
StopDrag( true );
|
|
if ( hide ) {
|
|
lastFiredTime = gameLocal.time - FIRING_DELAY + 250;
|
|
}
|
|
return 3;
|
|
}
|
|
|
|
// Check if object being held has been removed (dead demon, projectile, etc.)
|
|
if ( endTime > gameLocal.time ) {
|
|
bool abort = !dragEnt.IsValid();
|
|
|
|
if ( !abort && dragEnt.GetEntity()->IsType( idProjectile::Type ) ) {
|
|
idProjectile *proj = (idProjectile *)dragEnt.GetEntity();
|
|
|
|
if ( proj->GetProjectileState() >= 3 ) {
|
|
abort = true;
|
|
}
|
|
}
|
|
if ( !abort && dragEnt.GetEntity()->IsHidden() ) {
|
|
abort = true;
|
|
}
|
|
// Not in multiplayer :: Pressing "reload" lets you carefully drop an item
|
|
if ( !gameLocal.isMultiplayer && !abort && (( player->usercmd.flags & UCF_IMPULSE_SEQUENCE ) != ( oldUcmdFlags & UCF_IMPULSE_SEQUENCE )) && (player->usercmd.impulse == IMPULSE_13) ) {
|
|
abort = true;
|
|
}
|
|
|
|
if ( abort ) {
|
|
StopDrag( true );
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
owner = player;
|
|
|
|
// if no entity selected for dragging
|
|
if ( !dragEnt.GetEntity() ) {
|
|
idBounds bounds;
|
|
idVec3 end = player->firstPersonViewOrigin + player->firstPersonViewAxis[0] * dragTraceDist;
|
|
|
|
bounds.Zero();
|
|
bounds.ExpandSelf( TRACE_BOUNDS_SIZE );
|
|
|
|
gameLocal.clip.TraceBounds( trace, player->firstPersonViewOrigin, end, bounds, MASK_SHOT_RENDERMODEL|CONTENTS_PROJECTILE|CONTENTS_MOVEABLECLIP, player );
|
|
// If the trace hit something
|
|
if ( trace.fraction < 1.0f ) {
|
|
newEnt = gameLocal.entities[ trace.c.entityNum ];
|
|
|
|
// if entity is already being grabbed then bypass
|
|
if ( gameLocal.isMultiplayer && newEnt->IsGrabbed() ) {
|
|
return 0;
|
|
}
|
|
|
|
// Check if this is a valid entity to hold
|
|
if ( newEnt && ( newEnt->IsType( idMoveable::Type ) ||
|
|
newEnt->IsType( idMoveableItem::Type ) ||
|
|
newEnt->IsType( idProjectile::Type ) ||
|
|
newEnt->IsType( idAFEntity_Gibbable::Type ) ) &&
|
|
newEnt->noGrab == false &&
|
|
newEnt->GetPhysics()->GetBounds().GetRadius() < MAX_PICKUP_SIZE &&
|
|
newEnt->GetPhysics()->GetLinearVelocity().LengthSqr() < MAX_PICKUP_VELOCITY ) {
|
|
|
|
bool validAF = true;
|
|
|
|
if ( newEnt->IsType( idAFEntity_Gibbable::Type ) ) {
|
|
idAFEntity_Gibbable *afEnt = static_cast<idAFEntity_Gibbable*>(newEnt);
|
|
|
|
if ( grabbableAI( newEnt->spawnArgs.GetString( "classname" ) ) ) {
|
|
// Make sure it's also active
|
|
if ( !afEnt->IsActive() ) {
|
|
validAF = false;
|
|
}
|
|
} else if ( !afEnt->IsActiveAF() ) {
|
|
validAF = false;
|
|
}
|
|
}
|
|
|
|
if ( validAF && player->usercmd.buttons & BUTTON_ATTACK ) {
|
|
// Grab this entity and start dragging it around
|
|
StartDrag( newEnt, trace.c.id );
|
|
} else if ( validAF ) {
|
|
// A holdable object is ready to be grabbed
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// check backwards server time in multiplayer
|
|
bool allow = true;
|
|
|
|
if ( gameLocal.isMultiplayer ) {
|
|
|
|
// if we've marched backwards
|
|
if ( gameLocal.slow.time < startDragTime ) {
|
|
allow = false;
|
|
}
|
|
}
|
|
|
|
|
|
// if there is an entity selected for dragging
|
|
if ( dragEnt.GetEntity() && allow ) {
|
|
idPhysics *entPhys = dragEnt.GetEntity()->GetPhysics();
|
|
idVec3 goalPos;
|
|
|
|
// If the player lets go of attack, or time is up
|
|
if ( !( player->usercmd.buttons & BUTTON_ATTACK ) ) {
|
|
StopDrag( false );
|
|
return 3;
|
|
}
|
|
if ( gameLocal.time > endTime ) {
|
|
StopDrag( true );
|
|
return 3;
|
|
}
|
|
|
|
// Check if the player is standing on the object
|
|
if ( !holdingAF ) {
|
|
idBounds playerBounds;
|
|
idBounds objectBounds = entPhys->GetAbsBounds();
|
|
idVec3 newPoint = player->GetPhysics()->GetOrigin();
|
|
|
|
// create a bounds at the players feet
|
|
playerBounds.Clear();
|
|
playerBounds.AddPoint( newPoint );
|
|
newPoint.z -= 1.f;
|
|
playerBounds.AddPoint( newPoint );
|
|
playerBounds.ExpandSelf( 8.f );
|
|
|
|
// If it intersects the object bounds, then drop it
|
|
if ( playerBounds.IntersectsBounds( objectBounds ) ) {
|
|
StopDrag( true );
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
// Shake the object at the end of the hold
|
|
if ( g_grabberEnableShake.GetBool() && !gameLocal.isMultiplayer ) {
|
|
ApplyShake();
|
|
}
|
|
|
|
// Set and evaluate drag force
|
|
goalPos = player->firstPersonViewOrigin + localPlayerPoint * player->firstPersonViewAxis;
|
|
|
|
drag.SetGoalPosition( goalPos );
|
|
drag.Evaluate( gameLocal.time );
|
|
|
|
// If an object is flying too fast toward the player, stop it hard
|
|
if ( g_grabberHardStop.GetBool() ) {
|
|
idPlane theWall;
|
|
idVec3 toPlayerVelocity, objectCenter;
|
|
float toPlayerSpeed;
|
|
|
|
toPlayerVelocity = -player->firstPersonViewAxis[0];
|
|
toPlayerSpeed = entPhys->GetLinearVelocity() * toPlayerVelocity;
|
|
|
|
if ( toPlayerSpeed > 64.f ) {
|
|
objectCenter = entPhys->GetAbsBounds().GetCenter();
|
|
|
|
theWall.SetNormal( player->firstPersonViewAxis[0] );
|
|
theWall.FitThroughPoint( goalPos );
|
|
|
|
if ( theWall.Side( objectCenter, 0.1f ) == PLANESIDE_BACK ) {
|
|
int i, num;
|
|
|
|
num = entPhys->GetNumClipModels();
|
|
for ( i=0; i<num; i++ ) {
|
|
entPhys->SetLinearVelocity( vec3_origin, i );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make sure the object isn't spinning too fast
|
|
const float MAX_ROTATION_SPEED = 12.f;
|
|
|
|
idVec3 angVel = entPhys->GetAngularVelocity();
|
|
float rotationSpeed = angVel.LengthFast();
|
|
|
|
if ( rotationSpeed > MAX_ROTATION_SPEED ) {
|
|
angVel.NormalizeFast();
|
|
angVel *= MAX_ROTATION_SPEED;
|
|
entPhys->SetAngularVelocity( angVel );
|
|
}
|
|
}
|
|
|
|
// Orient projectiles away from the player
|
|
if ( dragEnt.GetEntity()->IsType( idProjectile::Type ) ) {
|
|
idAngles ang = player->firstPersonViewAxis[0].ToAngles();
|
|
ang.pitch += 90.f;
|
|
entPhys->SetAxis( ang.ToMat3() );
|
|
}
|
|
|
|
// Some kind of effect from gun to object?
|
|
UpdateBeams();
|
|
|
|
// If the object is stuck away from its intended position for more than 500ms, let it go.
|
|
if ( drag.GetDistanceToGoal() > DRAG_FAIL_LEN ) {
|
|
if ( dragFailTime < (gameLocal.slow.time - 500) ) {
|
|
StopDrag( true );
|
|
return 3;
|
|
}
|
|
} else {
|
|
dragFailTime = gameLocal.slow.time;
|
|
}
|
|
|
|
// Currently holding an object
|
|
return 2;
|
|
}
|
|
|
|
// Not holding, nothing to hold
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
======================
|
|
idGrabber::UpdateBeams
|
|
======================
|
|
*/
|
|
void idGrabber::UpdateBeams( void ) {
|
|
jointHandle_t muzzle_joint;
|
|
idVec3 muzzle_origin;
|
|
idMat3 muzzle_axis;
|
|
renderEntity_t *re;
|
|
|
|
if ( !beam ) {
|
|
return;
|
|
}
|
|
|
|
if ( dragEnt.IsValid() ) {
|
|
idPlayer *thePlayer = owner.GetEntity();
|
|
|
|
if ( beamTarget ) {
|
|
beamTarget->SetOrigin( dragEnt.GetEntity()->GetPhysics()->GetAbsBounds().GetCenter() );
|
|
}
|
|
|
|
muzzle_joint = thePlayer->weapon.GetEntity()->GetAnimator()->GetJointHandle( "particle_upper" );
|
|
if ( muzzle_joint != INVALID_JOINT ) {
|
|
thePlayer->weapon.GetEntity()->GetJointWorldTransform( muzzle_joint, gameLocal.time, muzzle_origin, muzzle_axis );
|
|
} else {
|
|
muzzle_origin = thePlayer->GetPhysics()->GetOrigin();
|
|
}
|
|
|
|
beam->SetOrigin( muzzle_origin );
|
|
re = beam->GetRenderEntity();
|
|
re->origin = muzzle_origin;
|
|
|
|
beam->UpdateVisuals();
|
|
beam->Present();
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idGrabber::ApplyShake
|
|
==============
|
|
*/
|
|
void idGrabber::ApplyShake( void ) {
|
|
float u = 1 - (float)( endTime - gameLocal.time ) / ( g_grabberHoldSeconds.GetFloat() * 1000 );
|
|
|
|
if ( u >= 0.8f ) {
|
|
idVec3 point, impulse;
|
|
float shakeForceMagnitude = 450.f;
|
|
float mass = dragEnt.GetEntity()->GetPhysics()->GetMass();
|
|
|
|
shakeForceFlip = !shakeForceFlip;
|
|
|
|
// get point to rotate around
|
|
point = dragEnt.GetEntity()->GetPhysics()->GetOrigin();
|
|
point.y += 1;
|
|
|
|
// Articulated figures get less violent shake
|
|
if ( holdingAF ) {
|
|
shakeForceMagnitude = 120.f;
|
|
}
|
|
|
|
// calc impulse
|
|
if ( shakeForceFlip ) {
|
|
impulse.Set( 0, 0, shakeForceMagnitude * u * mass );
|
|
}
|
|
else {
|
|
impulse.Set( 0, 0, -shakeForceMagnitude * u * mass );
|
|
}
|
|
|
|
dragEnt.GetEntity()->ApplyImpulse( NULL, 0, point, impulse );
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idGrabber::grabbableAI
|
|
==============
|
|
*/
|
|
bool idGrabber::grabbableAI( const char *aiName ) {
|
|
// skip "monster_"
|
|
aiName += 8;
|
|
|
|
if (!idStr::Cmpn( aiName, "flying_lostsoul", 15 ) ||
|
|
!idStr::Cmpn( aiName, "demon_trite", 11 ) ||
|
|
!idStr::Cmp( aiName, "flying_forgotten" ) ||
|
|
!idStr::Cmp( aiName, "demon_cherub" ) ||
|
|
!idStr::Cmp( aiName, "demon_tick" )) {
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#endif
|