2020-06-12 21:06:25 +00:00
|
|
|
/*
|
|
|
|
===========================================================================
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
===========================================================================
|
|
|
|
*/
|
|
|
|
#include "../idlib/precompiled.h"
|
|
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
#ifdef _D3XP
|
|
|
|
|
|
|
|
#include "Game_local.h"
|
|
|
|
#include "Misc.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 ) ) {
|
2020-10-22 17:12:18 +00:00
|
|
|
// RB: 64 bit fixes, changed NULL to 0
|
|
|
|
grabEnt->PostEventMS( &EV_Touch, 250, thePlayer, 0 );
|
|
|
|
// RB end
|
2020-06-12 21:06:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|