mirror of
https://github.com/dhewm/dhewm3-sdk.git
synced 2024-11-25 05:51:15 +00:00
6399 lines
158 KiB
C++
6399 lines
158 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.
|
||
|
||
===========================================================================
|
||
*/
|
||
|
||
#include "sys/platform.h"
|
||
#include "idlib/geometry/JointTransform.h"
|
||
#include "idlib/LangDict.h"
|
||
#include "framework/async/NetworkSystem.h"
|
||
#include "framework/DeclEntityDef.h"
|
||
#include "renderer/ModelManager.h"
|
||
|
||
#include "gamesys/SysCvar.h"
|
||
#include "physics/Physics_Parametric.h"
|
||
#include "physics/Physics_Actor.h"
|
||
#include "script/Script_Thread.h"
|
||
#include "Fx.h"
|
||
#include "AFEntity.h"
|
||
#include "Player.h"
|
||
#include "Mover.h"
|
||
#include "WorldSpawn.h"
|
||
#include "SmokeParticles.h"
|
||
|
||
#include "Entity.h"
|
||
|
||
/*
|
||
===============================================================================
|
||
|
||
idEntity
|
||
|
||
===============================================================================
|
||
*/
|
||
|
||
// overridable events
|
||
const idEventDef EV_PostSpawn( "<postspawn>", NULL );
|
||
const idEventDef EV_FindTargets( "<findTargets>", NULL );
|
||
const idEventDef EV_Touch( "<touch>", "et" );
|
||
const idEventDef EV_GetName( "getName", NULL, 's' );
|
||
const idEventDef EV_SetName( "setName", "s" );
|
||
const idEventDef EV_Activate( "activate", "e" );
|
||
const idEventDef EV_ActivateTargets( "activateTargets", "e" );
|
||
const idEventDef EV_NumTargets( "numTargets", NULL, 'f' );
|
||
const idEventDef EV_GetTarget( "getTarget", "f", 'e' );
|
||
const idEventDef EV_RandomTarget( "randomTarget", "s", 'e' );
|
||
const idEventDef EV_Bind( "bind", "e" );
|
||
const idEventDef EV_BindPosition( "bindPosition", "e" );
|
||
const idEventDef EV_BindToJoint( "bindToJoint", "esf" );
|
||
const idEventDef EV_Unbind( "unbind", NULL );
|
||
const idEventDef EV_RemoveBinds( "removeBinds" );
|
||
const idEventDef EV_SpawnBind( "<spawnbind>", NULL );
|
||
const idEventDef EV_SetOwner( "setOwner", "e" );
|
||
const idEventDef EV_SetModel( "setModel", "s" );
|
||
const idEventDef EV_SetSkin( "setSkin", "s" );
|
||
const idEventDef EV_GetWorldOrigin( "getWorldOrigin", NULL, 'v' );
|
||
const idEventDef EV_SetWorldOrigin( "setWorldOrigin", "v" );
|
||
const idEventDef EV_GetOrigin( "getOrigin", NULL, 'v' );
|
||
const idEventDef EV_SetOrigin( "setOrigin", "v" );
|
||
const idEventDef EV_GetAngles( "getAngles", NULL, 'v' );
|
||
const idEventDef EV_SetAngles( "setAngles", "v" );
|
||
const idEventDef EV_GetLinearVelocity( "getLinearVelocity", NULL, 'v' );
|
||
const idEventDef EV_SetLinearVelocity( "setLinearVelocity", "v" );
|
||
const idEventDef EV_GetAngularVelocity( "getAngularVelocity", NULL, 'v' );
|
||
const idEventDef EV_SetAngularVelocity( "setAngularVelocity", "v" );
|
||
const idEventDef EV_GetSize( "getSize", NULL, 'v' );
|
||
const idEventDef EV_SetSize( "setSize", "vv" );
|
||
const idEventDef EV_GetMins( "getMins", NULL, 'v' );
|
||
const idEventDef EV_GetMaxs( "getMaxs", NULL, 'v' );
|
||
const idEventDef EV_IsHidden( "isHidden", NULL, 'd' );
|
||
const idEventDef EV_Hide( "hide", NULL );
|
||
const idEventDef EV_Show( "show", NULL );
|
||
const idEventDef EV_Touches( "touches", "E", 'd' );
|
||
const idEventDef EV_ClearSignal( "clearSignal", "d" );
|
||
const idEventDef EV_GetShaderParm( "getShaderParm", "d", 'f' );
|
||
const idEventDef EV_SetShaderParm( "setShaderParm", "df" );
|
||
const idEventDef EV_SetShaderParms( "setShaderParms", "ffff" );
|
||
const idEventDef EV_SetColor( "setColor", "fff" );
|
||
const idEventDef EV_GetColor( "getColor", NULL, 'v' );
|
||
const idEventDef EV_CacheSoundShader( "cacheSoundShader", "s" );
|
||
const idEventDef EV_StartSoundShader( "startSoundShader", "sd", 'f' );
|
||
const idEventDef EV_StartSound( "startSound", "sdd", 'f' );
|
||
const idEventDef EV_StopSound( "stopSound", "dd" );
|
||
const idEventDef EV_FadeSound( "fadeSound", "dff" );
|
||
const idEventDef EV_SetGuiParm( "setGuiParm", "ss" );
|
||
const idEventDef EV_SetGuiFloat( "setGuiFloat", "sf" );
|
||
const idEventDef EV_GetNextKey( "getNextKey", "ss", 's' );
|
||
const idEventDef EV_SetKey( "setKey", "ss" );
|
||
const idEventDef EV_GetKey( "getKey", "s", 's' );
|
||
const idEventDef EV_GetIntKey( "getIntKey", "s", 'f' );
|
||
const idEventDef EV_GetFloatKey( "getFloatKey", "s", 'f' );
|
||
const idEventDef EV_GetVectorKey( "getVectorKey", "s", 'v' );
|
||
const idEventDef EV_GetEntityKey( "getEntityKey", "s", 'e' );
|
||
const idEventDef EV_RestorePosition( "restorePosition" );
|
||
const idEventDef EV_UpdateCameraTarget( "<updateCameraTarget>", NULL );
|
||
const idEventDef EV_DistanceTo( "distanceTo", "E", 'f' );
|
||
const idEventDef EV_DistanceToPoint( "distanceToPoint", "v", 'f' );
|
||
const idEventDef EV_StartFx( "startFx", "s" );
|
||
const idEventDef EV_HasFunction( "hasFunction", "s", 'd' );
|
||
const idEventDef EV_CallFunction( "callFunction", "s" );
|
||
const idEventDef EV_SetNeverDormant( "setNeverDormant", "d" );
|
||
//Ivan start
|
||
const idEventDef EV_GetGuiParm ( "getGuiParm", "ds", 's' );
|
||
const idEventDef EV_GetGuiParmFloat ( "getGuiParmFloat", "ds", 'f' );
|
||
const idEventDef EV_GuiNamedEvent ( "guiNamedEvent", "ds" );
|
||
const idEventDef EV_GetEntityHealth( "getEntityHealth", NULL, 'f' );
|
||
const idEventDef EV_SetEntityHealth( "setEntityHealth", "f" );
|
||
const idEventDef EV_Interact( "interact", "ed" );
|
||
//Ivan end
|
||
|
||
//ivan start
|
||
const idEventDef EV_GetClosestTargetTypePrefix( "getClosestTargetTypePrefix", "ss", 'e' );
|
||
const idEventDef EV_GetRandomTargetTypePrefix( "getRandomTargetTypePrefix", "ss", 'e' );
|
||
const idEventDef EV_FireProjectile( "fireProjectile", "svv", 'e' );
|
||
const idEventDef EV_FireProjAtTarget( "fireProjAtTarget", "svE", 'e' ); //E = NULL ok too
|
||
//ivan end
|
||
|
||
ABSTRACT_DECLARATION( idClass, idEntity )
|
||
EVENT( EV_GetName, idEntity::Event_GetName )
|
||
EVENT( EV_SetName, idEntity::Event_SetName )
|
||
EVENT( EV_FindTargets, idEntity::Event_FindTargets )
|
||
EVENT( EV_ActivateTargets, idEntity::Event_ActivateTargets )
|
||
EVENT( EV_NumTargets, idEntity::Event_NumTargets )
|
||
EVENT( EV_GetTarget, idEntity::Event_GetTarget )
|
||
EVENT( EV_RandomTarget, idEntity::Event_RandomTarget )
|
||
EVENT( EV_BindToJoint, idEntity::Event_BindToJoint )
|
||
EVENT( EV_RemoveBinds, idEntity::Event_RemoveBinds )
|
||
EVENT( EV_Bind, idEntity::Event_Bind )
|
||
EVENT( EV_BindPosition, idEntity::Event_BindPosition )
|
||
EVENT( EV_Unbind, idEntity::Event_Unbind )
|
||
EVENT( EV_SpawnBind, idEntity::Event_SpawnBind )
|
||
EVENT( EV_SetOwner, idEntity::Event_SetOwner )
|
||
EVENT( EV_SetModel, idEntity::Event_SetModel )
|
||
EVENT( EV_SetSkin, idEntity::Event_SetSkin )
|
||
EVENT( EV_GetShaderParm, idEntity::Event_GetShaderParm )
|
||
EVENT( EV_SetShaderParm, idEntity::Event_SetShaderParm )
|
||
EVENT( EV_SetShaderParms, idEntity::Event_SetShaderParms )
|
||
EVENT( EV_SetColor, idEntity::Event_SetColor )
|
||
EVENT( EV_GetColor, idEntity::Event_GetColor )
|
||
EVENT( EV_IsHidden, idEntity::Event_IsHidden )
|
||
EVENT( EV_Hide, idEntity::Event_Hide )
|
||
EVENT( EV_Show, idEntity::Event_Show )
|
||
EVENT( EV_CacheSoundShader, idEntity::Event_CacheSoundShader )
|
||
EVENT( EV_StartSoundShader, idEntity::Event_StartSoundShader )
|
||
EVENT( EV_StartSound, idEntity::Event_StartSound )
|
||
EVENT( EV_StopSound, idEntity::Event_StopSound )
|
||
EVENT( EV_FadeSound, idEntity::Event_FadeSound )
|
||
EVENT( EV_GetWorldOrigin, idEntity::Event_GetWorldOrigin )
|
||
EVENT( EV_SetWorldOrigin, idEntity::Event_SetWorldOrigin )
|
||
EVENT( EV_GetOrigin, idEntity::Event_GetOrigin )
|
||
EVENT( EV_SetOrigin, idEntity::Event_SetOrigin )
|
||
EVENT( EV_GetAngles, idEntity::Event_GetAngles )
|
||
EVENT( EV_SetAngles, idEntity::Event_SetAngles )
|
||
EVENT( EV_GetLinearVelocity, idEntity::Event_GetLinearVelocity )
|
||
EVENT( EV_SetLinearVelocity, idEntity::Event_SetLinearVelocity )
|
||
EVENT( EV_GetAngularVelocity, idEntity::Event_GetAngularVelocity )
|
||
EVENT( EV_SetAngularVelocity, idEntity::Event_SetAngularVelocity )
|
||
EVENT( EV_GetSize, idEntity::Event_GetSize )
|
||
EVENT( EV_SetSize, idEntity::Event_SetSize )
|
||
EVENT( EV_GetMins, idEntity::Event_GetMins)
|
||
EVENT( EV_GetMaxs, idEntity::Event_GetMaxs )
|
||
EVENT( EV_Touches, idEntity::Event_Touches )
|
||
EVENT( EV_SetGuiParm, idEntity::Event_SetGuiParm )
|
||
EVENT( EV_SetGuiFloat, idEntity::Event_SetGuiFloat )
|
||
EVENT( EV_GetNextKey, idEntity::Event_GetNextKey )
|
||
EVENT( EV_SetKey, idEntity::Event_SetKey )
|
||
EVENT( EV_GetKey, idEntity::Event_GetKey )
|
||
EVENT( EV_GetIntKey, idEntity::Event_GetIntKey )
|
||
EVENT( EV_GetFloatKey, idEntity::Event_GetFloatKey )
|
||
EVENT( EV_GetVectorKey, idEntity::Event_GetVectorKey )
|
||
EVENT( EV_GetEntityKey, idEntity::Event_GetEntityKey )
|
||
EVENT( EV_RestorePosition, idEntity::Event_RestorePosition )
|
||
EVENT( EV_UpdateCameraTarget, idEntity::Event_UpdateCameraTarget )
|
||
EVENT( EV_DistanceTo, idEntity::Event_DistanceTo )
|
||
EVENT( EV_DistanceToPoint, idEntity::Event_DistanceToPoint )
|
||
EVENT( EV_StartFx, idEntity::Event_StartFx )
|
||
EVENT( EV_Thread_WaitFrame, idEntity::Event_WaitFrame )
|
||
EVENT( EV_Thread_Wait, idEntity::Event_Wait )
|
||
EVENT( EV_HasFunction, idEntity::Event_HasFunction )
|
||
EVENT( EV_CallFunction, idEntity::Event_CallFunction )
|
||
EVENT( EV_SetNeverDormant, idEntity::Event_SetNeverDormant )
|
||
|
||
//ivan start
|
||
EVENT( EV_GetGuiParm, idEntity::Event_GetGuiParm )
|
||
EVENT( EV_GetGuiParmFloat, idEntity::Event_GetGuiParmFloat )
|
||
EVENT( EV_GuiNamedEvent, idEntity::Event_GuiNamedEvent )
|
||
EVENT( EV_GetEntityHealth, idEntity::Event_GetEntityHealth )
|
||
EVENT( EV_SetEntityHealth, idEntity::Event_SetEntityHealth )
|
||
EVENT( EV_GetClosestTargetTypePrefix, idEntity::Event_GetClosestTargetTypePrefix )
|
||
EVENT( EV_GetRandomTargetTypePrefix, idEntity::Event_GetRandomTargetTypePrefix )
|
||
EVENT( EV_FireProjectile, idEntity::Event_FireProjectile ) //ff 1.1
|
||
EVENT( EV_FireProjAtTarget, idEntity::Event_FireProjAtTarget ) //ff 1.1
|
||
//ivan end
|
||
|
||
END_CLASS
|
||
|
||
/*
|
||
================
|
||
UpdateGuiParms
|
||
================
|
||
*/
|
||
void UpdateGuiParms( idUserInterface *gui, const idDict *args ) {
|
||
if ( gui == NULL || args == NULL ) {
|
||
return;
|
||
}
|
||
const idKeyValue *kv = args->MatchPrefix( "gui_parm", NULL );
|
||
while( kv ) {
|
||
gui->SetStateString( kv->GetKey(), kv->GetValue() );
|
||
kv = args->MatchPrefix( "gui_parm", kv );
|
||
}
|
||
gui->SetStateBool( "noninteractive", args->GetBool( "gui_noninteractive" ) ) ;
|
||
gui->StateChanged( gameLocal.time );
|
||
}
|
||
|
||
/*
|
||
================
|
||
AddRenderGui
|
||
================
|
||
*/
|
||
void AddRenderGui( const char *name, idUserInterface **gui, const idDict *args ) {
|
||
const idKeyValue *kv = args->MatchPrefix( "gui_parm", NULL );
|
||
*gui = uiManager->FindGui( name, true, ( kv != NULL ) );
|
||
UpdateGuiParms( *gui, args );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idGameEdit::ParseSpawnArgsToRenderEntity
|
||
|
||
parse the static model parameters
|
||
this is the canonical renderEntity parm parsing,
|
||
which should be used by dmap and the editor
|
||
================
|
||
*/
|
||
void idGameEdit::ParseSpawnArgsToRenderEntity( const idDict *args, renderEntity_t *renderEntity ) {
|
||
int i;
|
||
const char *temp;
|
||
idVec3 color;
|
||
float angle;
|
||
const idDeclModelDef *modelDef;
|
||
|
||
memset( renderEntity, 0, sizeof( *renderEntity ) );
|
||
|
||
temp = args->GetString( "model" );
|
||
|
||
modelDef = NULL;
|
||
if ( temp[0] != '\0' ) {
|
||
modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, temp, false ) );
|
||
if ( modelDef ) {
|
||
renderEntity->hModel = modelDef->ModelHandle();
|
||
}
|
||
if ( !renderEntity->hModel ) {
|
||
renderEntity->hModel = renderModelManager->FindModel( temp );
|
||
}
|
||
}
|
||
if ( renderEntity->hModel ) {
|
||
renderEntity->bounds = renderEntity->hModel->Bounds( renderEntity );
|
||
} else {
|
||
renderEntity->bounds.Zero();
|
||
}
|
||
|
||
temp = args->GetString( "skin" );
|
||
if ( temp[0] != '\0' ) {
|
||
renderEntity->customSkin = declManager->FindSkin( temp );
|
||
} else if ( modelDef ) {
|
||
renderEntity->customSkin = modelDef->GetDefaultSkin();
|
||
}
|
||
|
||
temp = args->GetString( "shader" );
|
||
if ( temp[0] != '\0' ) {
|
||
renderEntity->customShader = declManager->FindMaterial( temp );
|
||
}
|
||
|
||
args->GetVector( "origin", "0 0 0", renderEntity->origin );
|
||
|
||
// get the rotation matrix in either full form, or single angle form
|
||
if ( !args->GetMatrix( "rotation", "1 0 0 0 1 0 0 0 1", renderEntity->axis ) ) {
|
||
angle = args->GetFloat( "angle" );
|
||
if ( angle != 0.0f ) {
|
||
renderEntity->axis = idAngles( 0.0f, angle, 0.0f ).ToMat3();
|
||
} else {
|
||
renderEntity->axis.Identity();
|
||
}
|
||
}
|
||
|
||
renderEntity->referenceSound = NULL;
|
||
|
||
// get shader parms
|
||
args->GetVector( "_color", "1 1 1", color );
|
||
renderEntity->shaderParms[ SHADERPARM_RED ] = color[0];
|
||
renderEntity->shaderParms[ SHADERPARM_GREEN ] = color[1];
|
||
renderEntity->shaderParms[ SHADERPARM_BLUE ] = color[2];
|
||
renderEntity->shaderParms[ 3 ] = args->GetFloat( "shaderParm3", "1" );
|
||
renderEntity->shaderParms[ 4 ] = args->GetFloat( "shaderParm4", "0" );
|
||
renderEntity->shaderParms[ 5 ] = args->GetFloat( "shaderParm5", "0" );
|
||
renderEntity->shaderParms[ 6 ] = args->GetFloat( "shaderParm6", "0" );
|
||
renderEntity->shaderParms[ 7 ] = args->GetFloat( "shaderParm7", "0" );
|
||
renderEntity->shaderParms[ 8 ] = args->GetFloat( "shaderParm8", "0" );
|
||
renderEntity->shaderParms[ 9 ] = args->GetFloat( "shaderParm9", "0" );
|
||
renderEntity->shaderParms[ 10 ] = args->GetFloat( "shaderParm10", "0" );
|
||
renderEntity->shaderParms[ 11 ] = args->GetFloat( "shaderParm11", "0" );
|
||
|
||
// check noDynamicInteractions flag
|
||
renderEntity->noDynamicInteractions = args->GetBool( "noDynamicInteractions" );
|
||
|
||
// check noshadows flag
|
||
renderEntity->noShadow = args->GetBool( "noshadows" );
|
||
|
||
// check noselfshadows flag
|
||
renderEntity->noSelfShadow = args->GetBool( "noselfshadows" );
|
||
|
||
// init any guis, including entity-specific states
|
||
for( i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
|
||
temp = args->GetString( i == 0 ? "gui" : va( "gui%d", i + 1 ) );
|
||
if ( temp[ 0 ] != '\0' ) {
|
||
AddRenderGui( temp, &renderEntity->gui[ i ], args );
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idGameEdit::ParseSpawnArgsToRefSound
|
||
|
||
parse the sound parameters
|
||
this is the canonical refSound parm parsing,
|
||
which should be used by dmap and the editor
|
||
================
|
||
*/
|
||
void idGameEdit::ParseSpawnArgsToRefSound( const idDict *args, refSound_t *refSound ) {
|
||
const char *temp;
|
||
|
||
memset( refSound, 0, sizeof( *refSound ) );
|
||
|
||
refSound->parms.minDistance = args->GetFloat( "s_mindistance" );
|
||
refSound->parms.maxDistance = args->GetFloat( "s_maxdistance" );
|
||
refSound->parms.volume = args->GetFloat( "s_volume" );
|
||
refSound->parms.shakes = args->GetFloat( "s_shakes" );
|
||
|
||
args->GetVector( "origin", "0 0 0", refSound->origin );
|
||
|
||
refSound->referenceSound = NULL;
|
||
|
||
// if a diversity is not specified, every sound start will make
|
||
// a random one. Specifying diversity is usefull to make multiple
|
||
// lights all share the same buzz sound offset, for instance.
|
||
refSound->diversity = args->GetFloat( "s_diversity", "-1" );
|
||
refSound->waitfortrigger = args->GetBool( "s_waitfortrigger" );
|
||
|
||
if ( args->GetBool( "s_omni" ) ) {
|
||
refSound->parms.soundShaderFlags |= SSF_OMNIDIRECTIONAL;
|
||
}
|
||
if ( args->GetBool( "s_looping" ) ) {
|
||
refSound->parms.soundShaderFlags |= SSF_LOOPING;
|
||
}
|
||
if ( args->GetBool( "s_occlusion" ) ) {
|
||
refSound->parms.soundShaderFlags |= SSF_NO_OCCLUSION;
|
||
}
|
||
if ( args->GetBool( "s_global" ) ) {
|
||
refSound->parms.soundShaderFlags |= SSF_GLOBAL;
|
||
}
|
||
if ( args->GetBool( "s_unclamped" ) ) {
|
||
refSound->parms.soundShaderFlags |= SSF_UNCLAMPED;
|
||
}
|
||
refSound->parms.soundClass = args->GetInt( "s_soundClass" );
|
||
|
||
temp = args->GetString( "s_shader" );
|
||
if ( temp[0] != '\0' ) {
|
||
refSound->shader = declManager->FindSound( temp );
|
||
}
|
||
}
|
||
|
||
/*
|
||
===============
|
||
idEntity::UpdateChangeableSpawnArgs
|
||
|
||
Any key val pair that might change during the course of the game ( via a gui or whatever )
|
||
should be initialize here so a gui or other trigger can change something and have it updated
|
||
properly. An optional source may be provided if the values reside in an outside dictionary and
|
||
first need copied over to spawnArgs
|
||
===============
|
||
*/
|
||
void idEntity::UpdateChangeableSpawnArgs( const idDict *source ) {
|
||
int i;
|
||
const char *target;
|
||
|
||
if ( !source ) {
|
||
source = &spawnArgs;
|
||
}
|
||
cameraTarget = NULL;
|
||
target = source->GetString( "cameraTarget" );
|
||
if ( target && target[0] ) {
|
||
// update the camera taget
|
||
PostEventMS( &EV_UpdateCameraTarget, 0 );
|
||
}
|
||
|
||
for ( i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
|
||
UpdateGuiParms( renderEntity.gui[ i ], source );
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::idEntity
|
||
================
|
||
*/
|
||
idEntity::idEntity() {
|
||
|
||
entityNumber = ENTITYNUM_NONE;
|
||
entityDefNumber = -1;
|
||
|
||
spawnNode.SetOwner( this );
|
||
activeNode.SetOwner( this );
|
||
|
||
snapshotNode.SetOwner( this );
|
||
snapshotSequence = -1;
|
||
snapshotBits = 0;
|
||
|
||
thinkFlags = 0;
|
||
dormantStart = 0;
|
||
cinematic = false;
|
||
renderView = NULL;
|
||
cameraTarget = NULL;
|
||
health = 0;
|
||
|
||
physics = NULL;
|
||
bindMaster = NULL;
|
||
bindJoint = INVALID_JOINT;
|
||
bindBody = -1;
|
||
teamMaster = NULL;
|
||
teamChain = NULL;
|
||
signals = NULL;
|
||
|
||
#ifdef _DENTONMOD_ENTITY_CPP
|
||
entDamageEffects = NULL;
|
||
#endif
|
||
|
||
memset( PVSAreas, 0, sizeof( PVSAreas ) );
|
||
numPVSAreas = -1;
|
||
|
||
memset( &fl, 0, sizeof( fl ) );
|
||
fl.neverDormant = true; // most entities never go dormant
|
||
|
||
memset( &renderEntity, 0, sizeof( renderEntity ) );
|
||
modelDefHandle = -1;
|
||
memset( &refSound, 0, sizeof( refSound ) );
|
||
|
||
mpGUIState = -1;
|
||
//rev grab
|
||
noGrab = false;
|
||
//rev grab
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::FixupLocalizedStrings
|
||
================
|
||
*/
|
||
void idEntity::FixupLocalizedStrings() {
|
||
for ( int i = 0; i < spawnArgs.GetNumKeyVals(); i++ ) {
|
||
const idKeyValue *kv = spawnArgs.GetKeyVal( i );
|
||
if ( idStr::Cmpn( kv->GetValue(), STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ){
|
||
spawnArgs.Set( kv->GetKey(), common->GetLanguageDict()->GetString( kv->GetValue() ) );
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Spawn
|
||
================
|
||
*/
|
||
void idEntity::Spawn( void ) {
|
||
int i;
|
||
const char *temp;
|
||
idVec3 origin;
|
||
idMat3 axis;
|
||
const idKeyValue *networkSync;
|
||
const char *classname;
|
||
const char *scriptObjectName;
|
||
|
||
gameLocal.RegisterEntity( this );
|
||
|
||
spawnArgs.GetString( "classname", NULL, &classname );
|
||
const idDeclEntityDef *def = gameLocal.FindEntityDef( classname, false );
|
||
if ( def ) {
|
||
entityDefNumber = def->Index();
|
||
}
|
||
|
||
FixupLocalizedStrings();
|
||
|
||
// parse static models the same way the editor display does
|
||
gameEdit->ParseSpawnArgsToRenderEntity( &spawnArgs, &renderEntity );
|
||
|
||
renderEntity.entityNum = entityNumber;
|
||
|
||
//rev grab
|
||
noGrab = spawnArgs.GetBool( "noGrab", "0" );
|
||
//rev grab
|
||
// go dormant within 5 frames so that when the map starts most monsters are dormant
|
||
dormantStart = gameLocal.time - DELAY_DORMANT_TIME + gameLocal.msec * 5;
|
||
|
||
origin = renderEntity.origin;
|
||
axis = renderEntity.axis;
|
||
|
||
// do the audio parsing the same way dmap and the editor do
|
||
gameEdit->ParseSpawnArgsToRefSound( &spawnArgs, &refSound );
|
||
|
||
// only play SCHANNEL_PRIVATE when sndworld->PlaceListener() is called with this listenerId
|
||
// don't spatialize sounds from the same entity
|
||
refSound.listenerId = entityNumber + 1;
|
||
|
||
cameraTarget = NULL;
|
||
temp = spawnArgs.GetString( "cameraTarget" );
|
||
if ( temp && temp[0] ) {
|
||
// update the camera taget
|
||
PostEventMS( &EV_UpdateCameraTarget, 0 );
|
||
}
|
||
|
||
for ( i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
|
||
UpdateGuiParms( renderEntity.gui[ i ], &spawnArgs );
|
||
}
|
||
|
||
fl.solidForTeam = spawnArgs.GetBool( "solidForTeam", "0" );
|
||
fl.neverDormant = spawnArgs.GetBool( "neverDormant", "0" );
|
||
fl.hidden = spawnArgs.GetBool( "hide", "0" );
|
||
if ( fl.hidden ) {
|
||
// make sure we're hidden, since a spawn function might not set it up right
|
||
PostEventMS( &EV_Hide, 0 );
|
||
}
|
||
cinematic = spawnArgs.GetBool( "cinematic", "0" );
|
||
|
||
networkSync = spawnArgs.FindKey( "networkSync" );
|
||
if ( networkSync ) {
|
||
fl.networkSync = ( atoi( networkSync->GetValue() ) != 0 );
|
||
}
|
||
|
||
#if 0
|
||
if ( !gameLocal.isClient ) {
|
||
// common->DPrintf( "NET: DBG %s - %s is synced: %s\n", spawnArgs.GetString( "classname", "" ), GetType()->classname, fl.networkSync ? "true" : "false" );
|
||
if ( spawnArgs.GetString( "classname", "" )[ 0 ] == '\0' && !fl.networkSync ) {
|
||
common->DPrintf( "NET: WRN %s entity, no classname, and no networkSync?\n", GetType()->classname );
|
||
}
|
||
}
|
||
#endif
|
||
|
||
// every object will have a unique name
|
||
temp = spawnArgs.GetString( "name", va( "%s_%s_%d", GetClassname(), spawnArgs.GetString( "classname" ), entityNumber ) );
|
||
SetName( temp );
|
||
|
||
// if we have targets, wait until all entities are spawned to get them
|
||
if ( spawnArgs.MatchPrefix( "target" ) || spawnArgs.MatchPrefix( "guiTarget" ) ) {
|
||
if ( gameLocal.GameState() == GAMESTATE_STARTUP ) {
|
||
PostEventMS( &EV_FindTargets, 0 );
|
||
} else {
|
||
// not during spawn, so it's ok to get the targets
|
||
FindTargets();
|
||
}
|
||
}
|
||
|
||
health = spawnArgs.GetInt( "health" );
|
||
|
||
InitDefaultPhysics( origin, axis );
|
||
|
||
SetOrigin( origin );
|
||
SetAxis( axis );
|
||
|
||
temp = spawnArgs.GetString( "model" );
|
||
if ( temp && *temp ) {
|
||
SetModel( temp );
|
||
}
|
||
|
||
if ( spawnArgs.GetString( "bind", "", &temp ) ) {
|
||
PostEventMS( &EV_SpawnBind, 0 );
|
||
}
|
||
|
||
// auto-start a sound on the entity
|
||
if ( refSound.shader && !refSound.waitfortrigger ) {
|
||
StartSoundShader( refSound.shader, SND_CHANNEL_ANY, 0, false, NULL );
|
||
}
|
||
|
||
// setup script object
|
||
if ( ShouldConstructScriptObjectAtSpawn() && spawnArgs.GetString( "scriptobject", NULL, &scriptObjectName ) ) {
|
||
if ( !scriptObject.SetType( scriptObjectName ) ) {
|
||
gameLocal.Error( "Script object '%s' not found on entity '%s'.", scriptObjectName, name.c_str() );
|
||
}
|
||
|
||
ConstructScriptObject();
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::~idEntity
|
||
================
|
||
*/
|
||
idEntity::~idEntity( void ) {
|
||
|
||
if ( gameLocal.GameState() != GAMESTATE_SHUTDOWN && !gameLocal.isClient && fl.networkSync && entityNumber >= MAX_CLIENTS ) {
|
||
idBitMsg msg;
|
||
byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
|
||
|
||
msg.Init( msgBuf, sizeof( msgBuf ) );
|
||
msg.WriteByte( GAME_RELIABLE_MESSAGE_DELETE_ENT );
|
||
msg.WriteBits( gameLocal.GetSpawnId( this ), 32 );
|
||
networkSystem->ServerSendReliableMessage( -1, msg );
|
||
}
|
||
|
||
DeconstructScriptObject();
|
||
scriptObject.Free();
|
||
|
||
if ( thinkFlags ) {
|
||
BecomeInactive( thinkFlags );
|
||
}
|
||
activeNode.Remove();
|
||
|
||
Signal( SIG_REMOVED );
|
||
|
||
// we have to set back the default physics object before unbinding because the entity
|
||
// specific physics object might be an entity variable and as such could already be destroyed.
|
||
SetPhysics( NULL );
|
||
|
||
#ifdef _DENTONMOD_ENTITY_CPP
|
||
|
||
for ( entDamageEffect_t *de = entDamageEffects; de; de = entDamageEffects ) {
|
||
entDamageEffects = de->next; // Clear damage effects if any
|
||
delete de;
|
||
}
|
||
|
||
#endif
|
||
|
||
// remove any entities that are bound to me
|
||
RemoveBinds();
|
||
|
||
// unbind from master
|
||
Unbind();
|
||
QuitTeam();
|
||
|
||
gameLocal.RemoveEntityFromHash( name.c_str(), this );
|
||
|
||
delete renderView;
|
||
renderView = NULL;
|
||
|
||
delete signals;
|
||
signals = NULL;
|
||
|
||
FreeModelDef();
|
||
FreeSoundEmitter( false );
|
||
|
||
gameLocal.UnregisterEntity( this );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Save
|
||
================
|
||
*/
|
||
void idEntity::Save( idSaveGame *savefile ) const {
|
||
int i, j;
|
||
|
||
#ifdef _DENTONMOD_ENTITY_CPP
|
||
// Wounds are very temporary, ignored at this time
|
||
#endif
|
||
savefile->WriteInt( entityNumber );
|
||
savefile->WriteInt( entityDefNumber );
|
||
|
||
// spawnNode and activeNode are restored by gameLocal
|
||
|
||
savefile->WriteInt( snapshotSequence );
|
||
savefile->WriteInt( snapshotBits );
|
||
|
||
savefile->WriteDict( &spawnArgs );
|
||
savefile->WriteString( name );
|
||
scriptObject.Save( savefile );
|
||
|
||
savefile->WriteInt( thinkFlags );
|
||
savefile->WriteInt( dormantStart );
|
||
savefile->WriteBool( cinematic );
|
||
|
||
savefile->WriteObject( cameraTarget );
|
||
|
||
savefile->WriteInt( health );
|
||
|
||
savefile->WriteInt( targets.Num() );
|
||
for( i = 0; i < targets.Num(); i++ ) {
|
||
targets[ i ].Save( savefile );
|
||
}
|
||
|
||
entityFlags_s flags = fl;
|
||
LittleBitField( &flags, sizeof( flags ) );
|
||
savefile->Write( &flags, sizeof( flags ) );
|
||
//rev grab
|
||
savefile->WriteBool( noGrab );
|
||
//rev grab
|
||
savefile->WriteRenderEntity( renderEntity );
|
||
savefile->WriteInt( modelDefHandle );
|
||
savefile->WriteRefSound( refSound );
|
||
|
||
savefile->WriteObject( bindMaster );
|
||
savefile->WriteJoint( bindJoint );
|
||
savefile->WriteInt( bindBody );
|
||
savefile->WriteObject( teamMaster );
|
||
savefile->WriteObject( teamChain );
|
||
|
||
savefile->WriteStaticObject( defaultPhysicsObj );
|
||
|
||
savefile->WriteInt( numPVSAreas );
|
||
for( i = 0; i < MAX_PVS_AREAS; i++ ) {
|
||
savefile->WriteInt( PVSAreas[ i ] );
|
||
}
|
||
|
||
if ( !signals ) {
|
||
savefile->WriteBool( false );
|
||
} else {
|
||
savefile->WriteBool( true );
|
||
for( i = 0; i < NUM_SIGNALS; i++ ) {
|
||
savefile->WriteInt( signals->signal[ i ].Num() );
|
||
for( j = 0; j < signals->signal[ i ].Num(); j++ ) {
|
||
savefile->WriteInt( signals->signal[ i ][ j ].threadnum );
|
||
savefile->WriteString( signals->signal[ i ][ j ].function->Name() );
|
||
}
|
||
}
|
||
}
|
||
|
||
savefile->WriteInt( mpGUIState );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Restore
|
||
================
|
||
*/
|
||
void idEntity::Restore( idRestoreGame *savefile ) {
|
||
int i, j;
|
||
int num;
|
||
idStr funcname;
|
||
|
||
savefile->ReadInt( entityNumber );
|
||
savefile->ReadInt( entityDefNumber );
|
||
|
||
// spawnNode and activeNode are restored by gameLocal
|
||
|
||
savefile->ReadInt( snapshotSequence );
|
||
savefile->ReadInt( snapshotBits );
|
||
|
||
savefile->ReadDict( &spawnArgs );
|
||
savefile->ReadString( name );
|
||
SetName( name );
|
||
|
||
scriptObject.Restore( savefile );
|
||
|
||
savefile->ReadInt( thinkFlags );
|
||
savefile->ReadInt( dormantStart );
|
||
savefile->ReadBool( cinematic );
|
||
|
||
savefile->ReadObject( reinterpret_cast<idClass *&>( cameraTarget ) );
|
||
|
||
savefile->ReadInt( health );
|
||
|
||
targets.Clear();
|
||
savefile->ReadInt( num );
|
||
targets.SetNum( num );
|
||
for( i = 0; i < num; i++ ) {
|
||
targets[ i ].Restore( savefile );
|
||
}
|
||
|
||
savefile->Read( &fl, sizeof( fl ) );
|
||
LittleBitField( &fl, sizeof( fl ) );
|
||
//rev grab
|
||
savefile->ReadBool( noGrab );
|
||
//rev grab
|
||
savefile->ReadRenderEntity( renderEntity );
|
||
savefile->ReadInt( modelDefHandle );
|
||
savefile->ReadRefSound( refSound );
|
||
|
||
savefile->ReadObject( reinterpret_cast<idClass *&>( bindMaster ) );
|
||
savefile->ReadJoint( bindJoint );
|
||
savefile->ReadInt( bindBody );
|
||
savefile->ReadObject( reinterpret_cast<idClass *&>( teamMaster ) );
|
||
savefile->ReadObject( reinterpret_cast<idClass *&>( teamChain ) );
|
||
|
||
savefile->ReadStaticObject( defaultPhysicsObj );
|
||
RestorePhysics( &defaultPhysicsObj );
|
||
|
||
savefile->ReadInt( numPVSAreas );
|
||
for( i = 0; i < MAX_PVS_AREAS; i++ ) {
|
||
savefile->ReadInt( PVSAreas[ i ] );
|
||
}
|
||
|
||
bool readsignals;
|
||
savefile->ReadBool( readsignals );
|
||
if ( readsignals ) {
|
||
signals = new signalList_t;
|
||
for( i = 0; i < NUM_SIGNALS; i++ ) {
|
||
savefile->ReadInt( num );
|
||
signals->signal[ i ].SetNum( num );
|
||
for( j = 0; j < num; j++ ) {
|
||
savefile->ReadInt( signals->signal[ i ][ j ].threadnum );
|
||
savefile->ReadString( funcname );
|
||
signals->signal[ i ][ j ].function = gameLocal.program.FindFunction( funcname );
|
||
if ( !signals->signal[ i ][ j ].function ) {
|
||
savefile->Error( "Function '%s' not found", funcname.c_str() );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
savefile->ReadInt( mpGUIState );
|
||
|
||
// restore must retrieve modelDefHandle from the renderer
|
||
if ( modelDefHandle != -1 ) {
|
||
modelDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetEntityDefName
|
||
================
|
||
*/
|
||
const char * idEntity::GetEntityDefName( void ) const {
|
||
if ( entityDefNumber < 0 ) {
|
||
return "*unknown*";
|
||
}
|
||
return declManager->DeclByIndex( DECL_ENTITYDEF, entityDefNumber, false )->GetName();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::SetName
|
||
================
|
||
*/
|
||
void idEntity::SetName( const char *newname ) {
|
||
if ( name.Length() ) {
|
||
gameLocal.RemoveEntityFromHash( name.c_str(), this );
|
||
gameLocal.program.SetEntity( name, NULL );
|
||
}
|
||
|
||
name = newname;
|
||
if ( name.Length() ) {
|
||
if ( ( name == "NULL" ) || ( name == "null_entity" ) ) {
|
||
gameLocal.Error( "Cannot name entity '%s'. '%s' is reserved for script.", name.c_str(), name.c_str() );
|
||
}
|
||
gameLocal.AddEntityToHash( name.c_str(), this );
|
||
gameLocal.program.SetEntity( name, this );
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetName
|
||
================
|
||
*/
|
||
const char * idEntity::GetName( void ) const {
|
||
return name.c_str();
|
||
}
|
||
|
||
|
||
/***********************************************************************
|
||
|
||
Thinking
|
||
|
||
***********************************************************************/
|
||
|
||
/*
|
||
================
|
||
idEntity::Think
|
||
================
|
||
*/
|
||
void idEntity::Think( void ) {
|
||
RunPhysics();
|
||
Present();
|
||
|
||
#ifdef _DENTONMOD_ENTITY_CPP
|
||
if ( thinkFlags & TH_UPDATEWOUNDPARTICLES )
|
||
UpdateParticles();
|
||
#endif
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::DoDormantTests
|
||
|
||
Monsters and other expensive entities that are completely closed
|
||
off from the player can skip all of their work
|
||
================
|
||
*/
|
||
bool idEntity::DoDormantTests( void ) {
|
||
|
||
if ( fl.neverDormant ) {
|
||
return false;
|
||
}
|
||
|
||
// if the monster area is not topologically connected to a player
|
||
if ( !gameLocal.InPlayerConnectedArea( this ) ) {
|
||
if ( dormantStart == 0 ) {
|
||
dormantStart = gameLocal.time;
|
||
}
|
||
if ( gameLocal.time - dormantStart < DELAY_DORMANT_TIME ) {
|
||
// just got closed off, don't go dormant yet
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// the monster area is topologically connected to a player, but if
|
||
// the monster hasn't been woken up before, do the more precise PVS check
|
||
if ( !fl.hasAwakened ) {
|
||
if ( !gameLocal.InPlayerPVS( this ) ) {
|
||
return true; // stay dormant
|
||
}
|
||
}
|
||
|
||
// wake up
|
||
dormantStart = 0;
|
||
fl.hasAwakened = true; // only go dormant when area closed off now, not just out of PVS
|
||
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::CheckDormant
|
||
|
||
Monsters and other expensive entities that are completely closed
|
||
off from the player can skip all of their work
|
||
================
|
||
*/
|
||
bool idEntity::CheckDormant( void ) {
|
||
bool dormant;
|
||
|
||
dormant = DoDormantTests();
|
||
if ( dormant && !fl.isDormant ) {
|
||
fl.isDormant = true;
|
||
DormantBegin();
|
||
} else if ( !dormant && fl.isDormant ) {
|
||
fl.isDormant = false;
|
||
DormantEnd();
|
||
}
|
||
|
||
return dormant;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::DormantBegin
|
||
|
||
called when entity becomes dormant
|
||
================
|
||
*/
|
||
void idEntity::DormantBegin( void ) {
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::DormantEnd
|
||
|
||
called when entity wakes from being dormant
|
||
================
|
||
*/
|
||
void idEntity::DormantEnd( void ) {
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::IsActive
|
||
================
|
||
*/
|
||
bool idEntity::IsActive( void ) const {
|
||
return activeNode.InList();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::BecomeActive
|
||
================
|
||
*/
|
||
void idEntity::BecomeActive( int flags ) {
|
||
if ( ( flags & TH_PHYSICS ) ) {
|
||
// enable the team master if this entity is part of a physics team
|
||
if ( teamMaster && teamMaster != this ) {
|
||
teamMaster->BecomeActive( TH_PHYSICS );
|
||
} else if ( !( thinkFlags & TH_PHYSICS ) ) {
|
||
// if this is a pusher
|
||
if ( physics->IsType( idPhysics_Parametric::Type ) || physics->IsType( idPhysics_Actor::Type ) ) {
|
||
gameLocal.sortPushers = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
int oldFlags = thinkFlags;
|
||
thinkFlags |= flags;
|
||
if ( thinkFlags ) {
|
||
if ( !IsActive() ) {
|
||
activeNode.AddToEnd( gameLocal.activeEntities );
|
||
} else if ( !oldFlags ) {
|
||
// we became inactive this frame, so we have to decrease the count of entities to deactivate
|
||
gameLocal.numEntitiesToDeactivate--;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::BecomeInactive
|
||
================
|
||
*/
|
||
void idEntity::BecomeInactive( int flags ) {
|
||
if ( ( flags & TH_PHYSICS ) ) {
|
||
// may only disable physics on a team master if no team members are running physics or bound to a joints
|
||
if ( teamMaster == this ) {
|
||
for ( idEntity *ent = teamMaster->teamChain; ent; ent = ent->teamChain ) {
|
||
if ( ( ent->thinkFlags & TH_PHYSICS ) || ( ( ent->bindMaster == this ) && ( ent->bindJoint != INVALID_JOINT ) ) ) {
|
||
flags &= ~TH_PHYSICS;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( thinkFlags ) {
|
||
thinkFlags &= ~flags;
|
||
if ( !thinkFlags && IsActive() ) {
|
||
gameLocal.numEntitiesToDeactivate++;
|
||
}
|
||
}
|
||
|
||
if ( ( flags & TH_PHYSICS ) ) {
|
||
// if this entity has a team master
|
||
if ( teamMaster && teamMaster != this ) {
|
||
// if the team master is at rest
|
||
if ( teamMaster->IsAtRest() ) {
|
||
teamMaster->BecomeInactive( TH_PHYSICS );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/***********************************************************************
|
||
|
||
Visuals
|
||
|
||
***********************************************************************/
|
||
|
||
/*
|
||
================
|
||
idEntity::SetShaderParm
|
||
================
|
||
*/
|
||
void idEntity::SetShaderParm( int parmnum, float value ) {
|
||
if ( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) {
|
||
gameLocal.Warning( "shader parm index (%d) out of range", parmnum );
|
||
return;
|
||
}
|
||
|
||
renderEntity.shaderParms[ parmnum ] = value;
|
||
UpdateVisuals();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::SetColor
|
||
================
|
||
*/
|
||
void idEntity::SetColor( float red, float green, float blue ) {
|
||
renderEntity.shaderParms[ SHADERPARM_RED ] = red;
|
||
renderEntity.shaderParms[ SHADERPARM_GREEN ] = green;
|
||
renderEntity.shaderParms[ SHADERPARM_BLUE ] = blue;
|
||
UpdateVisuals();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::SetColor
|
||
================
|
||
*/
|
||
void idEntity::SetColor( const idVec3 &color ) {
|
||
SetColor( color[ 0 ], color[ 1 ], color[ 2 ] );
|
||
UpdateVisuals();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetColor
|
||
================
|
||
*/
|
||
void idEntity::GetColor( idVec3 &out ) const {
|
||
out[ 0 ] = renderEntity.shaderParms[ SHADERPARM_RED ];
|
||
out[ 1 ] = renderEntity.shaderParms[ SHADERPARM_GREEN ];
|
||
out[ 2 ] = renderEntity.shaderParms[ SHADERPARM_BLUE ];
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::SetColor
|
||
================
|
||
*/
|
||
void idEntity::SetColor( const idVec4 &color ) {
|
||
renderEntity.shaderParms[ SHADERPARM_RED ] = color[ 0 ];
|
||
renderEntity.shaderParms[ SHADERPARM_GREEN ] = color[ 1 ];
|
||
renderEntity.shaderParms[ SHADERPARM_BLUE ] = color[ 2 ];
|
||
renderEntity.shaderParms[ SHADERPARM_ALPHA ] = color[ 3 ];
|
||
UpdateVisuals();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetColor
|
||
================
|
||
*/
|
||
void idEntity::GetColor( idVec4 &out ) const {
|
||
out[ 0 ] = renderEntity.shaderParms[ SHADERPARM_RED ];
|
||
out[ 1 ] = renderEntity.shaderParms[ SHADERPARM_GREEN ];
|
||
out[ 2 ] = renderEntity.shaderParms[ SHADERPARM_BLUE ];
|
||
out[ 3 ] = renderEntity.shaderParms[ SHADERPARM_ALPHA ];
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::UpdateAnimationControllers
|
||
================
|
||
*/
|
||
bool idEntity::UpdateAnimationControllers( void ) {
|
||
// any ragdoll and IK animation controllers should be updated here
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::SetModel
|
||
================
|
||
*/
|
||
void idEntity::SetModel( const char *modelname ) {
|
||
assert( modelname );
|
||
|
||
FreeModelDef();
|
||
|
||
renderEntity.hModel = renderModelManager->FindModel( modelname );
|
||
|
||
if ( renderEntity.hModel ) {
|
||
renderEntity.hModel->Reset();
|
||
}
|
||
|
||
renderEntity.callback = NULL;
|
||
renderEntity.numJoints = 0;
|
||
renderEntity.joints = NULL;
|
||
if ( renderEntity.hModel ) {
|
||
renderEntity.bounds = renderEntity.hModel->Bounds( &renderEntity );
|
||
} else {
|
||
renderEntity.bounds.Zero();
|
||
}
|
||
|
||
UpdateVisuals();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::SetSkin
|
||
================
|
||
*/
|
||
void idEntity::SetSkin( const idDeclSkin *skin ) {
|
||
renderEntity.customSkin = skin;
|
||
UpdateVisuals();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetSkin
|
||
================
|
||
*/
|
||
const idDeclSkin *idEntity::GetSkin( void ) const {
|
||
return renderEntity.customSkin;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::FreeModelDef
|
||
================
|
||
*/
|
||
void idEntity::FreeModelDef( void ) {
|
||
if ( modelDefHandle != -1 ) {
|
||
gameRenderWorld->FreeEntityDef( modelDefHandle );
|
||
modelDefHandle = -1;
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::FreeLightDef
|
||
================
|
||
*/
|
||
void idEntity::FreeLightDef( void ) {
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::IsHidden
|
||
================
|
||
*/
|
||
bool idEntity::IsHidden( void ) const {
|
||
return fl.hidden;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Hide
|
||
================
|
||
*/
|
||
void idEntity::Hide( void ) {
|
||
if ( !IsHidden() ) {
|
||
fl.hidden = true;
|
||
FreeModelDef();
|
||
UpdateVisuals();
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Show
|
||
================
|
||
*/
|
||
void idEntity::Show( void ) {
|
||
if ( IsHidden() ) {
|
||
fl.hidden = false;
|
||
UpdateVisuals();
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::UpdateModelTransform
|
||
================
|
||
*/
|
||
void idEntity::UpdateModelTransform( void ) {
|
||
idVec3 origin;
|
||
idMat3 axis;
|
||
|
||
if ( GetPhysicsToVisualTransform( origin, axis ) ) {
|
||
renderEntity.axis = axis * GetPhysics()->GetAxis();
|
||
renderEntity.origin = GetPhysics()->GetOrigin() + origin * renderEntity.axis;
|
||
} else {
|
||
renderEntity.axis = GetPhysics()->GetAxis();
|
||
renderEntity.origin = GetPhysics()->GetOrigin();
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::UpdateModel
|
||
================
|
||
*/
|
||
void idEntity::UpdateModel( void ) {
|
||
UpdateModelTransform();
|
||
|
||
// check if the entity has an MD5 model
|
||
idAnimator *animator = GetAnimator();
|
||
if ( animator && animator->ModelHandle() ) {
|
||
// set the callback to update the joints
|
||
renderEntity.callback = idEntity::ModelCallback;
|
||
}
|
||
|
||
// set to invalid number to force an update the next time the PVS areas are retrieved
|
||
ClearPVSAreas();
|
||
|
||
// ensure that we call Present this frame
|
||
BecomeActive( TH_UPDATEVISUALS );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::UpdateVisuals
|
||
================
|
||
*/
|
||
void idEntity::UpdateVisuals( void ) {
|
||
UpdateModel();
|
||
UpdateSound();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::UpdatePVSAreas
|
||
================
|
||
*/
|
||
void idEntity::UpdatePVSAreas( void ) {
|
||
int localNumPVSAreas, localPVSAreas[32];
|
||
idBounds modelAbsBounds;
|
||
int i;
|
||
|
||
modelAbsBounds.FromTransformedBounds( renderEntity.bounds, renderEntity.origin, renderEntity.axis );
|
||
localNumPVSAreas = gameLocal.pvs.GetPVSAreas( modelAbsBounds, localPVSAreas, sizeof( localPVSAreas ) / sizeof( localPVSAreas[0] ) );
|
||
|
||
// FIXME: some particle systems may have huge bounds and end up in many PVS areas
|
||
// the first MAX_PVS_AREAS may not be visible to a network client and as a result the particle system may not show up when it should
|
||
if ( localNumPVSAreas > MAX_PVS_AREAS ) {
|
||
localNumPVSAreas = gameLocal.pvs.GetPVSAreas( idBounds( modelAbsBounds.GetCenter() ).Expand( 64.0f ), localPVSAreas, sizeof( localPVSAreas ) / sizeof( localPVSAreas[0] ) );
|
||
}
|
||
|
||
for ( numPVSAreas = 0; numPVSAreas < MAX_PVS_AREAS && numPVSAreas < localNumPVSAreas; numPVSAreas++ ) {
|
||
PVSAreas[numPVSAreas] = localPVSAreas[numPVSAreas];
|
||
}
|
||
|
||
for( i = numPVSAreas; i < MAX_PVS_AREAS; i++ ) {
|
||
PVSAreas[ i ] = 0;
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::UpdatePVSAreas
|
||
================
|
||
*/
|
||
void idEntity::UpdatePVSAreas( const idVec3 &pos ) {
|
||
int i;
|
||
|
||
numPVSAreas = gameLocal.pvs.GetPVSAreas( idBounds( pos ), PVSAreas, MAX_PVS_AREAS );
|
||
i = numPVSAreas;
|
||
while ( i < MAX_PVS_AREAS ) {
|
||
PVSAreas[ i++ ] = 0;
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetNumPVSAreas
|
||
================
|
||
*/
|
||
int idEntity::GetNumPVSAreas( void ) {
|
||
if ( numPVSAreas < 0 ) {
|
||
UpdatePVSAreas();
|
||
}
|
||
return numPVSAreas;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetPVSAreas
|
||
================
|
||
*/
|
||
const int *idEntity::GetPVSAreas( void ) {
|
||
if ( numPVSAreas < 0 ) {
|
||
UpdatePVSAreas();
|
||
}
|
||
return PVSAreas;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::ClearPVSAreas
|
||
================
|
||
*/
|
||
void idEntity::ClearPVSAreas( void ) {
|
||
numPVSAreas = -1;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::PhysicsTeamInPVS
|
||
|
||
FIXME: for networking also return true if any of the entity shadows is in the PVS
|
||
================
|
||
*/
|
||
bool idEntity::PhysicsTeamInPVS( pvsHandle_t pvsHandle ) {
|
||
idEntity *part;
|
||
|
||
if ( teamMaster ) {
|
||
for ( part = teamMaster; part; part = part->teamChain ) {
|
||
if ( gameLocal.pvs.InCurrentPVS( pvsHandle, part->GetPVSAreas(), part->GetNumPVSAreas() ) ) {
|
||
return true;
|
||
}
|
||
}
|
||
} else {
|
||
return gameLocal.pvs.InCurrentPVS( pvsHandle, GetPVSAreas(), GetNumPVSAreas() );
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
==============
|
||
idEntity::ProjectOverlay
|
||
==============
|
||
*/
|
||
void idEntity::ProjectOverlay( const idVec3 &origin, const idVec3 &dir, float size, const char *material ) {
|
||
float s, c;
|
||
idMat3 axis, axistemp;
|
||
idVec3 localOrigin, localAxis[2];
|
||
idPlane localPlane[2];
|
||
|
||
// make sure the entity has a valid model handle
|
||
if ( modelDefHandle < 0 ) {
|
||
return;
|
||
}
|
||
|
||
// only do this on dynamic md5 models
|
||
if ( renderEntity.hModel->IsDynamicModel() != DM_CACHED ) {
|
||
return;
|
||
}
|
||
|
||
idMath::SinCos16( gameLocal.random.RandomFloat() * idMath::TWO_PI, s, c );
|
||
|
||
axis[2] = -dir;
|
||
axis[2].NormalVectors( axistemp[0], axistemp[1] );
|
||
axis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * -s;
|
||
axis[1] = axistemp[ 0 ] * -s + axistemp[ 1 ] * -c;
|
||
|
||
renderEntity.axis.ProjectVector( origin - renderEntity.origin, localOrigin );
|
||
renderEntity.axis.ProjectVector( axis[0], localAxis[0] );
|
||
renderEntity.axis.ProjectVector( axis[1], localAxis[1] );
|
||
|
||
size = 1.0f / size;
|
||
localAxis[0] *= size;
|
||
localAxis[1] *= size;
|
||
|
||
localPlane[0] = localAxis[0];
|
||
localPlane[0][3] = -( localOrigin * localAxis[0] ) + 0.5f;
|
||
|
||
localPlane[1] = localAxis[1];
|
||
localPlane[1][3] = -( localOrigin * localAxis[1] ) + 0.5f;
|
||
|
||
const idMaterial *mtr = declManager->FindMaterial( material );
|
||
|
||
// project an overlay onto the model
|
||
gameRenderWorld->ProjectOverlay( modelDefHandle, localPlane, mtr );
|
||
|
||
// make sure non-animating models update their overlay
|
||
UpdateVisuals();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Present
|
||
|
||
Present is called to allow entities to generate refEntities, lights, etc for the renderer.
|
||
================
|
||
*/
|
||
void idEntity::Present( void ) {
|
||
|
||
if ( !gameLocal.isNewFrame ) {
|
||
return;
|
||
}
|
||
|
||
// don't present to the renderer if the entity hasn't changed
|
||
if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
|
||
return;
|
||
}
|
||
BecomeInactive( TH_UPDATEVISUALS );
|
||
|
||
// camera target for remote render views
|
||
if ( cameraTarget && gameLocal.InPlayerPVS( this ) ) {
|
||
renderEntity.remoteRenderView = cameraTarget->GetRenderView();
|
||
}
|
||
|
||
// if set to invisible, skip
|
||
if ( !renderEntity.hModel || IsHidden() ) {
|
||
return;
|
||
}
|
||
|
||
// add to refresh list
|
||
if ( modelDefHandle == -1 ) {
|
||
modelDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
|
||
} else {
|
||
gameRenderWorld->UpdateEntityDef( modelDefHandle, &renderEntity );
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetRenderEntity
|
||
================
|
||
*/
|
||
renderEntity_t *idEntity::GetRenderEntity( void ) {
|
||
return &renderEntity;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetModelDefHandle
|
||
================
|
||
*/
|
||
int idEntity::GetModelDefHandle( void ) {
|
||
return modelDefHandle;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::UpdateRenderEntity
|
||
================
|
||
*/
|
||
bool idEntity::UpdateRenderEntity( renderEntity_s *renderEntity, const renderView_t *renderView ) {
|
||
if ( gameLocal.inCinematic && gameLocal.skipCinematic ) {
|
||
return false;
|
||
}
|
||
|
||
idAnimator *animator = GetAnimator();
|
||
if ( animator ) {
|
||
return animator->CreateFrame( gameLocal.time, false );
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::ModelCallback
|
||
|
||
NOTE: may not change the game state whatsoever!
|
||
================
|
||
*/
|
||
bool idEntity::ModelCallback( renderEntity_s *renderEntity, const renderView_t *renderView ) {
|
||
idEntity *ent;
|
||
|
||
ent = gameLocal.entities[ renderEntity->entityNum ];
|
||
if ( !ent ) {
|
||
gameLocal.Error( "idEntity::ModelCallback: callback with NULL game entity" );
|
||
}
|
||
|
||
return ent->UpdateRenderEntity( renderEntity, renderView );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetAnimator
|
||
|
||
Subclasses will be responsible for allocating animator.
|
||
================
|
||
*/
|
||
idAnimator *idEntity::GetAnimator( void ) {
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
=============
|
||
idEntity::GetRenderView
|
||
|
||
This is used by remote camera views to look from an entity
|
||
=============
|
||
*/
|
||
renderView_t *idEntity::GetRenderView( void ) {
|
||
if ( !renderView ) {
|
||
renderView = new renderView_t;
|
||
}
|
||
memset( renderView, 0, sizeof( *renderView ) );
|
||
|
||
renderView->vieworg = GetPhysics()->GetOrigin();
|
||
renderView->fov_x = 120;
|
||
renderView->fov_y = 120;
|
||
renderView->viewaxis = GetPhysics()->GetAxis();
|
||
|
||
// copy global shader parms
|
||
for( int i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
|
||
renderView->shaderParms[ i ] = gameLocal.globalShaderParms[ i ];
|
||
}
|
||
|
||
renderView->globalMaterial = gameLocal.GetGlobalMaterial();
|
||
|
||
renderView->time = gameLocal.time;
|
||
|
||
return renderView;
|
||
}
|
||
|
||
/***********************************************************************
|
||
|
||
Sound
|
||
|
||
***********************************************************************/
|
||
|
||
/*
|
||
================
|
||
idEntity::CanPlayChatterSounds
|
||
|
||
Used for playing chatter sounds on monsters.
|
||
================
|
||
*/
|
||
bool idEntity::CanPlayChatterSounds( void ) const {
|
||
return true;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::StartSound
|
||
================
|
||
*/
|
||
bool idEntity::StartSound( const char *soundName, const s_channelType channel, int soundShaderFlags, bool broadcast, int *length ) {
|
||
const idSoundShader *shader;
|
||
const char *sound;
|
||
|
||
if ( length ) {
|
||
*length = 0;
|
||
}
|
||
|
||
// we should ALWAYS be playing sounds from the def.
|
||
// hardcoded sounds MUST be avoided at all times because they won't get precached.
|
||
assert( idStr::Icmpn( soundName, "snd_", 4 ) == 0 );
|
||
|
||
if ( !spawnArgs.GetString( soundName, "", &sound ) ) {
|
||
return false;
|
||
}
|
||
|
||
if ( sound[0] == '\0' ) {
|
||
return false;
|
||
}
|
||
|
||
if ( !gameLocal.isNewFrame ) {
|
||
// don't play the sound, but don't report an error
|
||
return true;
|
||
}
|
||
|
||
shader = declManager->FindSound( sound );
|
||
return StartSoundShader( shader, channel, soundShaderFlags, broadcast, length );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::StartSoundShader
|
||
================
|
||
*/
|
||
bool idEntity::StartSoundShader( const idSoundShader *shader, const s_channelType channel, int soundShaderFlags, bool broadcast, int *length ) {
|
||
float diversity;
|
||
int len;
|
||
|
||
if ( length ) {
|
||
*length = 0;
|
||
}
|
||
|
||
if ( !shader ) {
|
||
return false;
|
||
}
|
||
|
||
if ( !gameLocal.isNewFrame ) {
|
||
return true;
|
||
}
|
||
|
||
if ( gameLocal.isServer && broadcast ) {
|
||
idBitMsg msg;
|
||
byte msgBuf[MAX_EVENT_PARAM_SIZE];
|
||
|
||
msg.Init( msgBuf, sizeof( msgBuf ) );
|
||
msg.BeginWriting();
|
||
msg.WriteInt( gameLocal.ServerRemapDecl( -1, DECL_SOUND, shader->Index() ) );
|
||
msg.WriteByte( channel );
|
||
ServerSendEvent( EVENT_STARTSOUNDSHADER, &msg, false, -1 );
|
||
}
|
||
|
||
// set a random value for diversity unless one was parsed from the entity
|
||
if ( refSound.diversity < 0.0f ) {
|
||
diversity = gameLocal.random.RandomFloat();
|
||
} else {
|
||
diversity = refSound.diversity;
|
||
}
|
||
|
||
// if we don't have a soundEmitter allocated yet, get one now
|
||
if ( !refSound.referenceSound ) {
|
||
refSound.referenceSound = gameSoundWorld->AllocSoundEmitter();
|
||
}
|
||
|
||
UpdateSound();
|
||
|
||
len = refSound.referenceSound->StartSound( shader, channel, diversity, soundShaderFlags );
|
||
if ( length ) {
|
||
*length = len;
|
||
}
|
||
|
||
// set reference to the sound for shader synced effects
|
||
renderEntity.referenceSound = refSound.referenceSound;
|
||
|
||
return true;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::StopSound
|
||
================
|
||
*/
|
||
void idEntity::StopSound( const s_channelType channel, bool broadcast ) {
|
||
if ( !gameLocal.isNewFrame ) {
|
||
return;
|
||
}
|
||
|
||
if ( gameLocal.isServer && broadcast ) {
|
||
idBitMsg msg;
|
||
byte msgBuf[MAX_EVENT_PARAM_SIZE];
|
||
|
||
msg.Init( msgBuf, sizeof( msgBuf ) );
|
||
msg.BeginWriting();
|
||
msg.WriteByte( channel );
|
||
ServerSendEvent( EVENT_STOPSOUNDSHADER, &msg, false, -1 );
|
||
}
|
||
|
||
if ( refSound.referenceSound ) {
|
||
refSound.referenceSound->StopSound( channel );
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::SetSoundVolume
|
||
|
||
Must be called before starting a new sound.
|
||
================
|
||
*/
|
||
void idEntity::SetSoundVolume( float volume ) {
|
||
refSound.parms.volume = volume;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::UpdateSound
|
||
================
|
||
*/
|
||
void idEntity::UpdateSound( void ) {
|
||
if ( refSound.referenceSound ) {
|
||
idVec3 origin;
|
||
idMat3 axis;
|
||
|
||
if ( GetPhysicsToSoundTransform( origin, axis ) ) {
|
||
refSound.origin = GetPhysics()->GetOrigin() + origin * axis;
|
||
} else {
|
||
refSound.origin = GetPhysics()->GetOrigin();
|
||
}
|
||
|
||
refSound.referenceSound->UpdateEmitter( refSound.origin, refSound.listenerId, &refSound.parms );
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetListenerId
|
||
================
|
||
*/
|
||
int idEntity::GetListenerId( void ) const {
|
||
return refSound.listenerId;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetSoundEmitter
|
||
================
|
||
*/
|
||
idSoundEmitter *idEntity::GetSoundEmitter( void ) const {
|
||
return refSound.referenceSound;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::FreeSoundEmitter
|
||
================
|
||
*/
|
||
void idEntity::FreeSoundEmitter( bool immediate ) {
|
||
if ( refSound.referenceSound ) {
|
||
refSound.referenceSound->Free( immediate );
|
||
refSound.referenceSound = NULL;
|
||
}
|
||
}
|
||
|
||
/***********************************************************************
|
||
|
||
entity binding
|
||
|
||
***********************************************************************/
|
||
|
||
/*
|
||
================
|
||
idEntity::PreBind
|
||
================
|
||
*/
|
||
void idEntity::PreBind( void ) {
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::PostBind
|
||
================
|
||
*/
|
||
void idEntity::PostBind( void ) {
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::PreUnbind
|
||
================
|
||
*/
|
||
void idEntity::PreUnbind( void ) {
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::PostUnbind
|
||
================
|
||
*/
|
||
void idEntity::PostUnbind( void ) {
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::InitBind
|
||
================
|
||
*/
|
||
bool idEntity::InitBind( idEntity *master ) {
|
||
|
||
if ( master == this ) {
|
||
gameLocal.Error( "Tried to bind an object to itself." );
|
||
return false;
|
||
}
|
||
|
||
if ( this == gameLocal.world ) {
|
||
gameLocal.Error( "Tried to bind world to another entity" );
|
||
return false;
|
||
}
|
||
|
||
// unbind myself from my master
|
||
Unbind();
|
||
|
||
// add any bind constraints to an articulated figure
|
||
if ( master && IsType( idAFEntity_Base::Type ) ) {
|
||
static_cast<idAFEntity_Base *>(this)->AddBindConstraints();
|
||
}
|
||
|
||
if ( !master || master == gameLocal.world ) {
|
||
// this can happen in scripts, so safely exit out.
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::FinishBind
|
||
================
|
||
*/
|
||
void idEntity::FinishBind( void ) {
|
||
|
||
// set the master on the physics object
|
||
physics->SetMaster( bindMaster, fl.bindOrientated );
|
||
|
||
// We are now separated from our previous team and are either
|
||
// an individual, or have a team of our own. Now we can join
|
||
// the new bindMaster's team. Bindmaster must be set before
|
||
// joining the team, or we will be placed in the wrong position
|
||
// on the team.
|
||
JoinTeam( bindMaster );
|
||
|
||
// if our bindMaster is enabled during a cinematic, we must be, too
|
||
cinematic = bindMaster->cinematic;
|
||
|
||
// make sure the team master is active so that physics get run
|
||
teamMaster->BecomeActive( TH_PHYSICS );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Bind
|
||
|
||
bind relative to the visual position of the master
|
||
================
|
||
*/
|
||
void idEntity::Bind( idEntity *master, bool orientated ) {
|
||
|
||
if ( !InitBind( master ) ) {
|
||
return;
|
||
}
|
||
|
||
PreBind();
|
||
|
||
bindJoint = INVALID_JOINT;
|
||
bindBody = -1;
|
||
bindMaster = master;
|
||
fl.bindOrientated = orientated;
|
||
|
||
FinishBind();
|
||
|
||
PostBind( );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::BindToJoint
|
||
|
||
bind relative to a joint of the md5 model used by the master
|
||
================
|
||
*/
|
||
void idEntity::BindToJoint( idEntity *master, const char *jointname, bool orientated ) {
|
||
jointHandle_t jointnum;
|
||
idAnimator *masterAnimator;
|
||
|
||
if ( !InitBind( master ) ) {
|
||
return;
|
||
}
|
||
|
||
masterAnimator = master->GetAnimator();
|
||
if ( !masterAnimator ) {
|
||
gameLocal.Warning( "idEntity::BindToJoint: entity '%s' cannot support skeletal models.", master->GetName() );
|
||
return;
|
||
}
|
||
|
||
jointnum = masterAnimator->GetJointHandle( jointname );
|
||
if ( jointnum == INVALID_JOINT ) {
|
||
gameLocal.Warning( "idEntity::BindToJoint: joint '%s' not found on entity '%s'.", jointname, master->GetName() );
|
||
}
|
||
|
||
PreBind();
|
||
|
||
bindJoint = jointnum;
|
||
bindBody = -1;
|
||
bindMaster = master;
|
||
fl.bindOrientated = orientated;
|
||
|
||
FinishBind();
|
||
|
||
PostBind();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::BindToJoint
|
||
|
||
bind relative to a joint of the md5 model used by the master
|
||
================
|
||
*/
|
||
void idEntity::BindToJoint( idEntity *master, jointHandle_t jointnum, bool orientated ) {
|
||
|
||
if ( !InitBind( master ) ) {
|
||
return;
|
||
}
|
||
|
||
PreBind();
|
||
|
||
bindJoint = jointnum;
|
||
bindBody = -1;
|
||
bindMaster = master;
|
||
fl.bindOrientated = orientated;
|
||
|
||
FinishBind();
|
||
|
||
PostBind();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::BindToBody
|
||
|
||
bind relative to a collision model used by the physics of the master
|
||
================
|
||
*/
|
||
void idEntity::BindToBody( idEntity *master, int bodyId, bool orientated ) {
|
||
|
||
if ( !InitBind( master ) ) {
|
||
return;
|
||
}
|
||
|
||
if ( bodyId < 0 ) {
|
||
gameLocal.Warning( "idEntity::BindToBody: body '%d' not found.", bodyId );
|
||
}
|
||
|
||
PreBind();
|
||
|
||
bindJoint = INVALID_JOINT;
|
||
bindBody = bodyId;
|
||
bindMaster = master;
|
||
fl.bindOrientated = orientated;
|
||
|
||
FinishBind();
|
||
|
||
PostBind();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Unbind
|
||
================
|
||
*/
|
||
void idEntity::Unbind( void ) {
|
||
idEntity * prev;
|
||
idEntity * next;
|
||
idEntity * last;
|
||
idEntity * ent;
|
||
|
||
// remove any bind constraints from an articulated figure
|
||
if ( IsType( idAFEntity_Base::Type ) ) {
|
||
static_cast<idAFEntity_Base *>(this)->RemoveBindConstraints();
|
||
}
|
||
|
||
if ( !bindMaster ) {
|
||
return;
|
||
}
|
||
|
||
if ( !teamMaster ) {
|
||
// Teammaster already has been freed
|
||
bindMaster = NULL;
|
||
return;
|
||
}
|
||
|
||
PreUnbind();
|
||
|
||
if ( physics ) {
|
||
physics->SetMaster( NULL, fl.bindOrientated );
|
||
}
|
||
|
||
// We're still part of a team, so that means I have to extricate myself
|
||
// and any entities that are bound to me from the old team.
|
||
// Find the node previous to me in the team
|
||
prev = teamMaster;
|
||
for( ent = teamMaster->teamChain; ent && ( ent != this ); ent = ent->teamChain ) {
|
||
prev = ent;
|
||
}
|
||
|
||
assert( ent == this ); // If ent is not pointing to this, then something is very wrong.
|
||
|
||
// Find the last node in my team that is bound to me.
|
||
// Also find the first node not bound to me, if one exists.
|
||
last = this;
|
||
for( next = teamChain; next != NULL; next = next->teamChain ) {
|
||
if ( !next->IsBoundTo( this ) ) {
|
||
break;
|
||
}
|
||
|
||
// Tell them I'm now the teamMaster
|
||
next->teamMaster = this;
|
||
last = next;
|
||
}
|
||
|
||
// disconnect the last member of our team from the old team
|
||
last->teamChain = NULL;
|
||
|
||
// connect up the previous member of the old team to the node that
|
||
// follow the last node bound to me (if one exists).
|
||
if ( teamMaster != this ) {
|
||
prev->teamChain = next;
|
||
if ( !next && ( teamMaster == prev ) ) {
|
||
prev->teamMaster = NULL;
|
||
}
|
||
} else if ( next ) {
|
||
// If we were the teamMaster, then the nodes that were not bound to me are now
|
||
// a disconnected chain. Make them into their own team.
|
||
for( ent = next; ent->teamChain != NULL; ent = ent->teamChain ) {
|
||
ent->teamMaster = next;
|
||
}
|
||
next->teamMaster = next;
|
||
}
|
||
|
||
// If we don't have anyone on our team, then clear the team variables.
|
||
if ( teamChain ) {
|
||
// make myself my own team
|
||
teamMaster = this;
|
||
} else {
|
||
// no longer a team
|
||
teamMaster = NULL;
|
||
}
|
||
|
||
bindJoint = INVALID_JOINT;
|
||
bindBody = -1;
|
||
bindMaster = NULL;
|
||
|
||
PostUnbind();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::RemoveBinds
|
||
================
|
||
*/
|
||
void idEntity::RemoveBinds( void ) {
|
||
idEntity *ent;
|
||
idEntity *next;
|
||
|
||
for( ent = teamChain; ent != NULL; ent = next ) {
|
||
next = ent->teamChain;
|
||
if ( ent->bindMaster == this ) {
|
||
ent->Unbind();
|
||
ent->PostEventMS( &EV_Remove, 0 );
|
||
next = teamChain;
|
||
}
|
||
}
|
||
}
|
||
|
||
//ivan start
|
||
/*
|
||
================
|
||
idEntity::UnbindBinds
|
||
================
|
||
|
||
void idEntity::UnbindBinds( void ) {
|
||
idEntity *ent;
|
||
idEntity *next;
|
||
|
||
for( ent = teamChain; ent != NULL; ent = next ) {
|
||
next = ent->teamChain;
|
||
if ( ent->bindMaster == this ) {
|
||
ent->Unbind();
|
||
next = teamChain;
|
||
}
|
||
}
|
||
}
|
||
*/
|
||
//ivan end
|
||
|
||
/*
|
||
================
|
||
idEntity::IsBound
|
||
================
|
||
*/
|
||
bool idEntity::IsBound( void ) const {
|
||
if ( bindMaster ) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::IsBoundTo
|
||
================
|
||
*/
|
||
bool idEntity::IsBoundTo( idEntity *master ) const {
|
||
idEntity *ent;
|
||
|
||
if ( !bindMaster ) {
|
||
return false;
|
||
}
|
||
|
||
for ( ent = bindMaster; ent != NULL; ent = ent->bindMaster ) {
|
||
if ( ent == master ) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetBindMaster
|
||
================
|
||
*/
|
||
idEntity *idEntity::GetBindMaster( void ) const {
|
||
return bindMaster;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetBindJoint
|
||
================
|
||
*/
|
||
jointHandle_t idEntity::GetBindJoint( void ) const {
|
||
return bindJoint;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetBindBody
|
||
================
|
||
*/
|
||
int idEntity::GetBindBody( void ) const {
|
||
return bindBody;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetTeamMaster
|
||
================
|
||
*/
|
||
idEntity *idEntity::GetTeamMaster( void ) const {
|
||
return teamMaster;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetNextTeamEntity
|
||
================
|
||
*/
|
||
idEntity *idEntity::GetNextTeamEntity( void ) const {
|
||
return teamChain;
|
||
}
|
||
|
||
/*
|
||
=====================
|
||
idEntity::ConvertLocalToWorldTransform
|
||
=====================
|
||
*/
|
||
void idEntity::ConvertLocalToWorldTransform( idVec3 &offset, idMat3 &axis ) {
|
||
UpdateModelTransform();
|
||
|
||
offset = renderEntity.origin + offset * renderEntity.axis;
|
||
axis *= renderEntity.axis;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetLocalVector
|
||
|
||
Takes a vector in worldspace and transforms it into the parent
|
||
object's localspace.
|
||
|
||
Note: Does not take origin into acount. Use getLocalCoordinate to
|
||
convert coordinates.
|
||
================
|
||
*/
|
||
idVec3 idEntity::GetLocalVector( const idVec3 &vec ) const {
|
||
idVec3 pos;
|
||
|
||
if ( !bindMaster ) {
|
||
return vec;
|
||
}
|
||
|
||
idVec3 masterOrigin;
|
||
idMat3 masterAxis;
|
||
|
||
GetMasterPosition( masterOrigin, masterAxis );
|
||
masterAxis.ProjectVector( vec, pos );
|
||
|
||
return pos;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetLocalCoordinates
|
||
|
||
Takes a vector in world coordinates and transforms it into the parent
|
||
object's local coordinates.
|
||
================
|
||
*/
|
||
idVec3 idEntity::GetLocalCoordinates( const idVec3 &vec ) const {
|
||
idVec3 pos;
|
||
|
||
if ( !bindMaster ) {
|
||
return vec;
|
||
}
|
||
|
||
idVec3 masterOrigin;
|
||
idMat3 masterAxis;
|
||
|
||
GetMasterPosition( masterOrigin, masterAxis );
|
||
masterAxis.ProjectVector( vec - masterOrigin, pos );
|
||
|
||
return pos;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetWorldVector
|
||
|
||
Takes a vector in the parent object's local coordinates and transforms
|
||
it into world coordinates.
|
||
|
||
Note: Does not take origin into acount. Use getWorldCoordinate to
|
||
convert coordinates.
|
||
================
|
||
*/
|
||
idVec3 idEntity::GetWorldVector( const idVec3 &vec ) const {
|
||
idVec3 pos;
|
||
|
||
if ( !bindMaster ) {
|
||
return vec;
|
||
}
|
||
|
||
idVec3 masterOrigin;
|
||
idMat3 masterAxis;
|
||
|
||
GetMasterPosition( masterOrigin, masterAxis );
|
||
masterAxis.UnprojectVector( vec, pos );
|
||
|
||
return pos;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetWorldCoordinates
|
||
|
||
Takes a vector in the parent object's local coordinates and transforms
|
||
it into world coordinates.
|
||
================
|
||
*/
|
||
idVec3 idEntity::GetWorldCoordinates( const idVec3 &vec ) const {
|
||
idVec3 pos;
|
||
|
||
if ( !bindMaster ) {
|
||
return vec;
|
||
}
|
||
|
||
idVec3 masterOrigin;
|
||
idMat3 masterAxis;
|
||
|
||
GetMasterPosition( masterOrigin, masterAxis );
|
||
masterAxis.UnprojectVector( vec, pos );
|
||
pos += masterOrigin;
|
||
|
||
return pos;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetMasterPosition
|
||
================
|
||
*/
|
||
bool idEntity::GetMasterPosition( idVec3 &masterOrigin, idMat3 &masterAxis ) const {
|
||
idVec3 localOrigin;
|
||
idMat3 localAxis;
|
||
idAnimator *masterAnimator;
|
||
|
||
if ( bindMaster ) {
|
||
// if bound to a joint of an animated model
|
||
if ( bindJoint != INVALID_JOINT ) {
|
||
masterAnimator = bindMaster->GetAnimator();
|
||
if ( !masterAnimator ) {
|
||
masterOrigin = vec3_origin;
|
||
masterAxis = mat3_identity;
|
||
return false;
|
||
} else {
|
||
masterAnimator->GetJointTransform( bindJoint, gameLocal.time, masterOrigin, masterAxis );
|
||
masterAxis *= bindMaster->renderEntity.axis;
|
||
masterOrigin = bindMaster->renderEntity.origin + masterOrigin * bindMaster->renderEntity.axis;
|
||
}
|
||
} else if ( bindBody >= 0 && bindMaster->GetPhysics() ) {
|
||
masterOrigin = bindMaster->GetPhysics()->GetOrigin( bindBody );
|
||
masterAxis = bindMaster->GetPhysics()->GetAxis( bindBody );
|
||
} else {
|
||
masterOrigin = bindMaster->renderEntity.origin;
|
||
masterAxis = bindMaster->renderEntity.axis;
|
||
}
|
||
return true;
|
||
} else {
|
||
masterOrigin = vec3_origin;
|
||
masterAxis = mat3_identity;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetWorldVelocities
|
||
================
|
||
*/
|
||
void idEntity::GetWorldVelocities( idVec3 &linearVelocity, idVec3 &angularVelocity ) const {
|
||
|
||
linearVelocity = physics->GetLinearVelocity();
|
||
angularVelocity = physics->GetAngularVelocity();
|
||
|
||
if ( bindMaster ) {
|
||
idVec3 masterOrigin, masterLinearVelocity, masterAngularVelocity;
|
||
idMat3 masterAxis;
|
||
|
||
// get position of master
|
||
GetMasterPosition( masterOrigin, masterAxis );
|
||
|
||
// get master velocities
|
||
bindMaster->GetWorldVelocities( masterLinearVelocity, masterAngularVelocity );
|
||
|
||
// linear velocity relative to master plus master linear and angular velocity
|
||
linearVelocity = linearVelocity * masterAxis + masterLinearVelocity +
|
||
masterAngularVelocity.Cross( GetPhysics()->GetOrigin() - masterOrigin );
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::JoinTeam
|
||
================
|
||
*/
|
||
void idEntity::JoinTeam( idEntity *teammember ) {
|
||
idEntity *ent;
|
||
idEntity *master;
|
||
idEntity *prev;
|
||
idEntity *next;
|
||
|
||
// if we're already on a team, quit it so we can join this one
|
||
if ( teamMaster && ( teamMaster != this ) ) {
|
||
QuitTeam();
|
||
}
|
||
|
||
assert( teammember );
|
||
|
||
if ( teammember == this ) {
|
||
teamMaster = this;
|
||
return;
|
||
}
|
||
|
||
// check if our new team mate is already on a team
|
||
master = teammember->teamMaster;
|
||
if ( !master ) {
|
||
// he's not on a team, so he's the new teamMaster
|
||
master = teammember;
|
||
teammember->teamMaster = teammember;
|
||
teammember->teamChain = this;
|
||
|
||
// make anyone who's bound to me part of the new team
|
||
for( ent = teamChain; ent != NULL; ent = ent->teamChain ) {
|
||
ent->teamMaster = master;
|
||
}
|
||
} else {
|
||
// skip past the chain members bound to the entity we're teaming up with
|
||
prev = teammember;
|
||
next = teammember->teamChain;
|
||
if ( bindMaster ) {
|
||
// if we have a bindMaster, join after any entities bound to the entity
|
||
// we're joining
|
||
while( next && next->IsBoundTo( teammember ) ) {
|
||
prev = next;
|
||
next = next->teamChain;
|
||
}
|
||
} else {
|
||
// if we're not bound to someone, then put us at the end of the team
|
||
while( next ) {
|
||
prev = next;
|
||
next = next->teamChain;
|
||
}
|
||
}
|
||
|
||
// make anyone who's bound to me part of the new team and
|
||
// also find the last member of my team
|
||
for( ent = this; ent->teamChain != NULL; ent = ent->teamChain ) {
|
||
ent->teamChain->teamMaster = master;
|
||
}
|
||
|
||
prev->teamChain = this;
|
||
ent->teamChain = next;
|
||
}
|
||
|
||
teamMaster = master;
|
||
|
||
// reorder the active entity list
|
||
gameLocal.sortTeamMasters = true;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::QuitTeam
|
||
================
|
||
*/
|
||
void idEntity::QuitTeam( void ) {
|
||
idEntity *ent;
|
||
|
||
if ( !teamMaster ) {
|
||
return;
|
||
}
|
||
|
||
// check if I'm the teamMaster
|
||
if ( teamMaster == this ) {
|
||
// do we have more than one teammate?
|
||
if ( !teamChain->teamChain ) {
|
||
// no, break up the team
|
||
teamChain->teamMaster = NULL;
|
||
} else {
|
||
// yes, so make the first teammate the teamMaster
|
||
for( ent = teamChain; ent; ent = ent->teamChain ) {
|
||
ent->teamMaster = teamChain;
|
||
}
|
||
}
|
||
} else {
|
||
assert( teamMaster );
|
||
assert( teamMaster->teamChain );
|
||
|
||
// find the previous member of the teamChain
|
||
ent = teamMaster;
|
||
while( ent->teamChain != this ) {
|
||
assert( ent->teamChain ); // this should never happen
|
||
ent = ent->teamChain;
|
||
}
|
||
|
||
// remove this from the teamChain
|
||
ent->teamChain = teamChain;
|
||
|
||
// if no one is left on the team, break it up
|
||
if ( !teamMaster->teamChain ) {
|
||
teamMaster->teamMaster = NULL;
|
||
}
|
||
}
|
||
|
||
teamMaster = NULL;
|
||
teamChain = NULL;
|
||
}
|
||
|
||
/***********************************************************************
|
||
|
||
Physics.
|
||
|
||
***********************************************************************/
|
||
|
||
/*
|
||
================
|
||
idEntity::InitDefaultPhysics
|
||
================
|
||
*/
|
||
void idEntity::InitDefaultPhysics( const idVec3 &origin, const idMat3 &axis ) {
|
||
const char *temp;
|
||
idClipModel *clipModel = NULL;
|
||
|
||
// check if a clipmodel key/value pair is set
|
||
if ( spawnArgs.GetString( "clipmodel", "", &temp ) ) {
|
||
if ( idClipModel::CheckModel( temp ) ) {
|
||
clipModel = new idClipModel( temp );
|
||
}
|
||
}
|
||
|
||
if ( !spawnArgs.GetBool( "noclipmodel", "0" ) ) {
|
||
|
||
// check if mins/maxs or size key/value pairs are set
|
||
if ( !clipModel ) {
|
||
idVec3 size;
|
||
idBounds bounds;
|
||
bool setClipModel = false;
|
||
|
||
if ( spawnArgs.GetVector( "mins", NULL, bounds[0] ) &&
|
||
spawnArgs.GetVector( "maxs", NULL, bounds[1] ) ) {
|
||
setClipModel = true;
|
||
if ( bounds[0][0] > bounds[1][0] || bounds[0][1] > bounds[1][1] || bounds[0][2] > bounds[1][2] ) {
|
||
gameLocal.Error( "Invalid bounds '%s'-'%s' on entity '%s'", bounds[0].ToString(), bounds[1].ToString(), name.c_str() );
|
||
}
|
||
} else if ( spawnArgs.GetVector( "size", NULL, size ) ) {
|
||
if ( ( size.x < 0.0f ) || ( size.y < 0.0f ) || ( size.z < 0.0f ) ) {
|
||
gameLocal.Error( "Invalid size '%s' on entity '%s'", size.ToString(), name.c_str() );
|
||
}
|
||
bounds[0].Set( size.x * -0.5f, size.y * -0.5f, 0.0f );
|
||
bounds[1].Set( size.x * 0.5f, size.y * 0.5f, size.z );
|
||
setClipModel = true;
|
||
}
|
||
|
||
if ( setClipModel ) {
|
||
int numSides;
|
||
idTraceModel trm;
|
||
|
||
if ( spawnArgs.GetInt( "cylinder", "0", numSides ) && numSides > 0 ) {
|
||
trm.SetupCylinder( bounds, numSides < 3 ? 3 : numSides );
|
||
} else if ( spawnArgs.GetInt( "cone", "0", numSides ) && numSides > 0 ) {
|
||
trm.SetupCone( bounds, numSides < 3 ? 3 : numSides );
|
||
} else {
|
||
trm.SetupBox( bounds );
|
||
}
|
||
clipModel = new idClipModel( trm );
|
||
}
|
||
}
|
||
|
||
// check if the visual model can be used as collision model
|
||
if ( !clipModel ) {
|
||
temp = spawnArgs.GetString( "model" );
|
||
if ( ( temp != NULL ) && ( *temp != 0 ) ) {
|
||
if ( idClipModel::CheckModel( temp ) ) {
|
||
clipModel = new idClipModel( temp );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
defaultPhysicsObj.SetSelf( this );
|
||
defaultPhysicsObj.SetClipModel( clipModel, 1.0f );
|
||
defaultPhysicsObj.SetOrigin( origin );
|
||
defaultPhysicsObj.SetAxis( axis );
|
||
|
||
physics = &defaultPhysicsObj;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::SetPhysics
|
||
================
|
||
*/
|
||
void idEntity::SetPhysics( idPhysics *phys ) {
|
||
// clear any contacts the current physics object has
|
||
if ( physics ) {
|
||
physics->ClearContacts();
|
||
}
|
||
// set new physics object or set the default physics if NULL
|
||
if ( phys != NULL ) {
|
||
defaultPhysicsObj.SetClipModel( NULL, 1.0f );
|
||
physics = phys;
|
||
physics->Activate();
|
||
} else {
|
||
physics = &defaultPhysicsObj;
|
||
}
|
||
physics->UpdateTime( gameLocal.time );
|
||
physics->SetMaster( bindMaster, fl.bindOrientated );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::RestorePhysics
|
||
================
|
||
*/
|
||
void idEntity::RestorePhysics( idPhysics *phys ) {
|
||
assert( phys != NULL );
|
||
// restore physics pointer
|
||
physics = phys;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetPhysics
|
||
================
|
||
*/
|
||
idPhysics *idEntity::GetPhysics( void ) const {
|
||
return physics;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::RunPhysics
|
||
================
|
||
*/
|
||
bool idEntity::RunPhysics( void ) {
|
||
int i, reachedTime, startTime, endTime;
|
||
idEntity * part, *blockedPart, *blockingEntity;
|
||
bool moved;
|
||
|
||
// don't run physics if not enabled
|
||
if ( !( thinkFlags & TH_PHYSICS ) ) {
|
||
// however do update any animation controllers
|
||
if ( UpdateAnimationControllers() ) {
|
||
BecomeActive( TH_ANIMATE );
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// if this entity is a team slave don't do anything because the team master will handle everything
|
||
if ( teamMaster && teamMaster != this ) {
|
||
return false;
|
||
}
|
||
|
||
startTime = gameLocal.previousTime;
|
||
endTime = gameLocal.time;
|
||
|
||
gameLocal.push.InitSavingPushedEntityPositions();
|
||
blockedPart = NULL;
|
||
|
||
// save the physics state of the whole team and disable the team for collision detection
|
||
for ( part = this; part != NULL; part = part->teamChain ) {
|
||
if ( part->physics ) {
|
||
if ( !part->fl.solidForTeam ) {
|
||
part->physics->DisableClip();
|
||
}
|
||
part->physics->SaveState();
|
||
}
|
||
}
|
||
|
||
// move the whole team
|
||
for ( part = this; part != NULL; part = part->teamChain ) {
|
||
|
||
if ( part->physics ) {
|
||
|
||
// run physics
|
||
moved = part->physics->Evaluate( endTime - startTime, endTime );
|
||
|
||
// check if the object is blocked
|
||
blockingEntity = part->physics->GetBlockingEntity();
|
||
if ( blockingEntity ) {
|
||
blockedPart = part;
|
||
break;
|
||
}
|
||
|
||
// if moved or forced to update the visual position and orientation from the physics
|
||
if ( moved || part->fl.forcePhysicsUpdate ) {
|
||
part->UpdateFromPhysics( false );
|
||
}
|
||
|
||
// update any animation controllers here so an entity bound
|
||
// to a joint of this entity gets the correct position
|
||
if ( part->UpdateAnimationControllers() ) {
|
||
part->BecomeActive( TH_ANIMATE );
|
||
}
|
||
}
|
||
}
|
||
|
||
// enable the whole team for collision detection
|
||
for ( part = this; part != NULL; part = part->teamChain ) {
|
||
if ( part->physics ) {
|
||
if ( !part->fl.solidForTeam ) {
|
||
part->physics->EnableClip();
|
||
}
|
||
}
|
||
}
|
||
|
||
// if one of the team entities is a pusher and blocked
|
||
if ( blockedPart ) {
|
||
// move the parts back to the previous position
|
||
for ( part = this; part != blockedPart; part = part->teamChain ) {
|
||
|
||
if ( part->physics ) {
|
||
|
||
// restore the physics state
|
||
part->physics->RestoreState();
|
||
|
||
// move back the visual position and orientation
|
||
part->UpdateFromPhysics( true );
|
||
}
|
||
}
|
||
for ( part = this; part != NULL; part = part->teamChain ) {
|
||
if ( part->physics ) {
|
||
// update the physics time without moving
|
||
part->physics->UpdateTime( endTime );
|
||
}
|
||
}
|
||
|
||
// restore the positions of any pushed entities
|
||
gameLocal.push.RestorePushedEntityPositions();
|
||
|
||
if ( gameLocal.isClient ) {
|
||
return false;
|
||
}
|
||
|
||
// if the master pusher has a "blocked" function, call it
|
||
Signal( SIG_BLOCKED );
|
||
ProcessEvent( &EV_TeamBlocked, blockedPart, blockingEntity );
|
||
// call the blocked function on the blocked part
|
||
blockedPart->ProcessEvent( &EV_PartBlocked, blockingEntity );
|
||
return false;
|
||
}
|
||
|
||
// set pushed
|
||
for ( i = 0; i < gameLocal.push.GetNumPushedEntities(); i++ ) {
|
||
idEntity *ent = gameLocal.push.GetPushedEntity( i );
|
||
ent->physics->SetPushed( endTime - startTime );
|
||
}
|
||
|
||
if ( gameLocal.isClient ) {
|
||
return true;
|
||
}
|
||
|
||
// post reached event if the current time is at or past the end point of the motion
|
||
for ( part = this; part != NULL; part = part->teamChain ) {
|
||
|
||
if ( part->physics ) {
|
||
|
||
reachedTime = part->physics->GetLinearEndTime();
|
||
if ( startTime < reachedTime && endTime >= reachedTime ) {
|
||
part->ProcessEvent( &EV_ReachedPos );
|
||
}
|
||
reachedTime = part->physics->GetAngularEndTime();
|
||
if ( startTime < reachedTime && endTime >= reachedTime ) {
|
||
part->ProcessEvent( &EV_ReachedAng );
|
||
}
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
#ifdef _DENTONMOD_ENTITY_CPP
|
||
|
||
/*
|
||
==============
|
||
idEntity::UpdateParticles
|
||
==============
|
||
*/
|
||
void idEntity::UpdateParticles( void ) {
|
||
entDamageEffect_t *de, **prev;
|
||
|
||
// free any that have timed out
|
||
prev = &this->entDamageEffects;
|
||
while ( *prev ) {
|
||
de = *prev;
|
||
if ( de->time == 0 ) { // FIXME:SMOKE
|
||
*prev = de->next;
|
||
delete de;
|
||
} else {
|
||
prev = &de->next;
|
||
}
|
||
}
|
||
|
||
if ( !g_bloodEffects.GetBool() ) {
|
||
return;
|
||
}
|
||
|
||
if ( !this->entDamageEffects ){ // If no more particles left...
|
||
if ( thinkFlags & TH_UPDATEWOUNDPARTICLES )
|
||
BecomeInactive( TH_UPDATEWOUNDPARTICLES );
|
||
return;
|
||
}
|
||
|
||
// emit a particle for each bleeding wound
|
||
for ( de = this->entDamageEffects; de; de = de->next ) {
|
||
idVec3 origin;
|
||
idVec3 dir;
|
||
|
||
dir = de->dir * renderEntity.axis;
|
||
origin = renderEntity.origin + de->origin * renderEntity.axis;
|
||
|
||
if (de->time == -1){ // This key fixes an issue where some particles are not at all emitted
|
||
de->time = gameLocal.time;
|
||
}
|
||
if ( !gameLocal.smokeParticles->EmitSmoke( de->type, de->time, gameLocal.random.CRandomFloat(), origin, dir.ToMat3() ) ) {
|
||
de->time = 0;
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
/*
|
||
================
|
||
idEntity::UpdateFromPhysics
|
||
================
|
||
*/
|
||
void idEntity::UpdateFromPhysics( bool moveBack ) {
|
||
|
||
if ( IsType( idActor::Type ) ) {
|
||
idActor *actor = static_cast<idActor *>( this );
|
||
|
||
// set master delta angles for actors
|
||
if ( GetBindMaster() ) {
|
||
idAngles delta = actor->GetDeltaViewAngles();
|
||
if ( moveBack ) {
|
||
delta.yaw -= static_cast<idPhysics_Actor *>(physics)->GetMasterDeltaYaw();
|
||
} else {
|
||
delta.yaw += static_cast<idPhysics_Actor *>(physics)->GetMasterDeltaYaw();
|
||
}
|
||
actor->SetDeltaViewAngles( delta );
|
||
}
|
||
}
|
||
|
||
UpdateVisuals();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::SetOrigin
|
||
================
|
||
*/
|
||
void idEntity::SetOrigin( const idVec3 &org ) {
|
||
|
||
GetPhysics()->SetOrigin( org );
|
||
|
||
UpdateVisuals();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::SetAxis
|
||
================
|
||
*/
|
||
void idEntity::SetAxis( const idMat3 &axis ) {
|
||
|
||
if ( GetPhysics()->IsType( idPhysics_Actor::Type ) ) {
|
||
static_cast<idActor *>(this)->viewAxis = axis;
|
||
} else {
|
||
GetPhysics()->SetAxis( axis );
|
||
}
|
||
|
||
UpdateVisuals();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::SetAngles
|
||
================
|
||
*/
|
||
void idEntity::SetAngles( const idAngles &ang ) {
|
||
SetAxis( ang.ToMat3() );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetFloorPos
|
||
================
|
||
*/
|
||
bool idEntity::GetFloorPos( float max_dist, idVec3 &floorpos ) const {
|
||
trace_t result;
|
||
|
||
if ( !GetPhysics()->HasGroundContacts() ) {
|
||
GetPhysics()->ClipTranslation( result, GetPhysics()->GetGravityNormal() * max_dist, NULL );
|
||
if ( result.fraction < 1.0f ) {
|
||
floorpos = result.endpos;
|
||
return true;
|
||
} else {
|
||
floorpos = GetPhysics()->GetOrigin();
|
||
return false;
|
||
}
|
||
} else {
|
||
floorpos = GetPhysics()->GetOrigin();
|
||
return true;
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetPhysicsToVisualTransform
|
||
================
|
||
*/
|
||
bool idEntity::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetPhysicsToSoundTransform
|
||
================
|
||
*/
|
||
bool idEntity::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
|
||
// by default play the sound at the center of the bounding box of the first clip model
|
||
if ( GetPhysics()->GetNumClipModels() > 0 ) {
|
||
origin = GetPhysics()->GetBounds().GetCenter();
|
||
axis.Identity();
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Collide
|
||
================
|
||
*/
|
||
bool idEntity::Collide( const trace_t &collision, const idVec3 &velocity ) {
|
||
// this entity collides with collision.c.entityNum
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetImpactInfo
|
||
================
|
||
*/
|
||
void idEntity::GetImpactInfo( idEntity *ent, int id, const idVec3 &point, impactInfo_t *info ) {
|
||
GetPhysics()->GetImpactInfo( id, point, info );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::ApplyImpulse
|
||
================
|
||
*/
|
||
void idEntity::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse ) {
|
||
GetPhysics()->ApplyImpulse( id, point, impulse );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::AddForce
|
||
================
|
||
*/
|
||
void idEntity::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) {
|
||
GetPhysics()->AddForce( id, point, force );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::ActivatePhysics
|
||
================
|
||
*/
|
||
void idEntity::ActivatePhysics( idEntity *ent ) {
|
||
GetPhysics()->Activate();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::IsAtRest
|
||
================
|
||
*/
|
||
bool idEntity::IsAtRest( void ) const {
|
||
return GetPhysics()->IsAtRest();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::GetRestStartTime
|
||
================
|
||
*/
|
||
int idEntity::GetRestStartTime( void ) const {
|
||
return GetPhysics()->GetRestStartTime();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::AddContactEntity
|
||
================
|
||
*/
|
||
void idEntity::AddContactEntity( idEntity *ent ) {
|
||
GetPhysics()->AddContactEntity( ent );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::RemoveContactEntity
|
||
================
|
||
*/
|
||
void idEntity::RemoveContactEntity( idEntity *ent ) {
|
||
GetPhysics()->RemoveContactEntity( ent );
|
||
}
|
||
|
||
|
||
|
||
/***********************************************************************
|
||
|
||
Damage
|
||
|
||
***********************************************************************/
|
||
|
||
/*
|
||
============
|
||
idEntity::CanDamage
|
||
|
||
Returns true if the inflictor can directly damage the target. Used for
|
||
explosions and melee attacks.
|
||
============
|
||
*/
|
||
bool idEntity::CanDamage( const idVec3 &origin, idVec3 &damagePoint ) const {
|
||
idVec3 dest;
|
||
trace_t tr;
|
||
idVec3 midpoint;
|
||
|
||
// use the midpoint of the bounds instead of the origin, because
|
||
// bmodels may have their origin at 0,0,0
|
||
midpoint = ( GetPhysics()->GetAbsBounds()[0] + GetPhysics()->GetAbsBounds()[1] ) * 0.5;
|
||
|
||
dest = midpoint;
|
||
gameLocal.clip.TracePoint( tr, origin, dest, MASK_SOLID, NULL );
|
||
if ( tr.fraction == 1.0 || ( gameLocal.GetTraceEntity( tr ) == this ) ) {
|
||
damagePoint = tr.endpos;
|
||
return true;
|
||
}
|
||
|
||
// this should probably check in the plane of projection, rather than in world coordinate
|
||
dest = midpoint;
|
||
dest[0] += 15.0;
|
||
dest[1] += 15.0;
|
||
gameLocal.clip.TracePoint( tr, origin, dest, MASK_SOLID, NULL );
|
||
if ( tr.fraction == 1.0 || ( gameLocal.GetTraceEntity( tr ) == this ) ) {
|
||
damagePoint = tr.endpos;
|
||
return true;
|
||
}
|
||
|
||
dest = midpoint;
|
||
dest[0] += 15.0;
|
||
dest[1] -= 15.0;
|
||
gameLocal.clip.TracePoint( tr, origin, dest, MASK_SOLID, NULL );
|
||
if ( tr.fraction == 1.0 || ( gameLocal.GetTraceEntity( tr ) == this ) ) {
|
||
damagePoint = tr.endpos;
|
||
return true;
|
||
}
|
||
|
||
dest = midpoint;
|
||
dest[0] -= 15.0;
|
||
dest[1] += 15.0;
|
||
gameLocal.clip.TracePoint( tr, origin, dest, MASK_SOLID, NULL );
|
||
if ( tr.fraction == 1.0 || ( gameLocal.GetTraceEntity( tr ) == this ) ) {
|
||
damagePoint = tr.endpos;
|
||
return true;
|
||
}
|
||
|
||
dest = midpoint;
|
||
dest[0] -= 15.0;
|
||
dest[1] -= 15.0;
|
||
gameLocal.clip.TracePoint( tr, origin, dest, MASK_SOLID, NULL );
|
||
if ( tr.fraction == 1.0 || ( gameLocal.GetTraceEntity( tr ) == this ) ) {
|
||
damagePoint = tr.endpos;
|
||
return true;
|
||
}
|
||
|
||
dest = midpoint;
|
||
dest[2] += 15.0;
|
||
gameLocal.clip.TracePoint( tr, origin, dest, MASK_SOLID, NULL );
|
||
if ( tr.fraction == 1.0 || ( gameLocal.GetTraceEntity( tr ) == this ) ) {
|
||
damagePoint = tr.endpos;
|
||
return true;
|
||
}
|
||
|
||
dest = midpoint;
|
||
dest[2] -= 15.0;
|
||
gameLocal.clip.TracePoint( tr, origin, dest, MASK_SOLID, NULL );
|
||
if ( tr.fraction == 1.0 || ( gameLocal.GetTraceEntity( tr ) == this ) ) {
|
||
damagePoint = tr.endpos;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::DamageFeedback
|
||
|
||
callback function for when another entity received damage from this entity. damage can be adjusted and returned to the caller.
|
||
================
|
||
*/
|
||
void idEntity::DamageFeedback( idEntity *victim, idEntity *inflictor, int &damage ) {
|
||
// implemented in subclasses
|
||
}
|
||
|
||
/*
|
||
============
|
||
Damage
|
||
|
||
this entity that is being damaged
|
||
inflictor entity that is causing the damage
|
||
attacker entity that caused the inflictor to damage targ
|
||
example: this=monster, inflictor=rocket, attacker=player
|
||
|
||
dir direction of the attack for knockback in global space
|
||
point point at which the damage is being inflicted, used for headshots
|
||
damage amount of damage being inflicted
|
||
|
||
inflictor, attacker, dir, and point can be NULL for environmental effects
|
||
|
||
============
|
||
*/
|
||
/*
|
||
Ivan note: idPlayer and idActor are the only classes that override this method without calling idEntity::Damage
|
||
*/
|
||
void idEntity::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir,
|
||
const char *damageDefName, const float damageScale, const int location ) {
|
||
if ( !fl.takedamage ) {
|
||
return;
|
||
}
|
||
|
||
if ( !inflictor ) {
|
||
inflictor = gameLocal.world;
|
||
}
|
||
|
||
if ( !attacker ) {
|
||
attacker = gameLocal.world;
|
||
}
|
||
|
||
const idDict *damageDef = gameLocal.FindEntityDefDict( damageDefName );
|
||
if ( !damageDef ) {
|
||
gameLocal.Error( "Unknown damageDef '%s'\n", damageDefName );
|
||
}
|
||
|
||
//ivan start - damaging fx
|
||
//CheckDamageFx( damageDef );
|
||
//ivan end
|
||
|
||
int damage = damageDef->GetInt( "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 );
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::AddDamageEffect
|
||
================
|
||
*/
|
||
#ifdef _DENTONMOD
|
||
void idEntity::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName, idEntity *soundEnt ) {
|
||
#else
|
||
void idEntity::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName ) {
|
||
#endif
|
||
const char *sound, *decal, *key;
|
||
|
||
const idDeclEntityDef *def = gameLocal.FindEntityDef( damageDefName, false );
|
||
if ( def == NULL ) {
|
||
return;
|
||
}
|
||
|
||
const char *materialType = gameLocal.sufaceTypeNames[ collision.c.material->GetSurfaceType() ];
|
||
|
||
// start impact sound based on material type
|
||
key = va( "snd_%s", materialType );
|
||
sound = spawnArgs.GetString( key );
|
||
if ( *sound == '\0' ) {
|
||
sound = def->dict.GetString( key );
|
||
}
|
||
if ( *sound == '\0' ) {
|
||
sound = def->dict.GetString( "snd_metal" ); // default sound 1
|
||
}
|
||
if ( *sound == '\0' ) {
|
||
sound = def->dict.GetString( "snd_impact" ); // default sound 2
|
||
}
|
||
|
||
if ( *sound != '\0' ) {
|
||
#ifdef _DENTONMOD
|
||
if (soundEnt == NULL) {
|
||
StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_BODY, 0, false, NULL );
|
||
}
|
||
else {
|
||
soundEnt->StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_BODY, 0, false, NULL );
|
||
}
|
||
#else
|
||
StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_BODY, 0, false, NULL );
|
||
#endif
|
||
}
|
||
|
||
if ( g_decals.GetBool() ) {
|
||
// place a wound overlay on the model
|
||
|
||
if( g_debugDamage.GetBool() ) {
|
||
gameLocal.Printf("\n Collision Material Type- %s", materialType);
|
||
gameLocal.Printf("\n file :%s", collision.c.material->GetFileName());
|
||
gameLocal.Printf("\n Collision material:%s", collision.c.material->ImageName());
|
||
}
|
||
|
||
key = va( "mtr_wound_%s", materialType );
|
||
decal = spawnArgs.RandomPrefix( key, gameLocal.random );
|
||
if ( *decal == '\0' ) {
|
||
decal = def->dict.RandomPrefix( key, gameLocal.random );
|
||
}
|
||
if ( *decal == '\0' ) {
|
||
decal = def->dict.GetString( "mtr_wound" ); // Default decal
|
||
}
|
||
|
||
if ( *decal != '\0' ) {
|
||
float size;
|
||
idVec3 dir = velocity;
|
||
dir.Normalize();
|
||
|
||
if ( !def->dict.GetFloat( va( "size_wound_%s", materialType ), "6.0", size ) ) { // If Material Specific decal size not found, look for default size
|
||
size = def->dict.GetFloat( "size_wound", "6.0" );
|
||
}
|
||
|
||
if(size > 0.0f) {
|
||
|
||
#ifdef _DENTONMOD_ENTITY_CPP
|
||
gameLocal.ProjectDecal( collision.c.point, -collision.c.normal, 8.0f, true, size, decal );
|
||
#else
|
||
ProjectOverlay( collision.c.point, dir, size, decal ); // added by Clone JCD
|
||
#endif // Modified for custom wound size
|
||
}
|
||
}
|
||
}
|
||
|
||
#ifdef _DENTONMOD_ENTITY_CPP
|
||
// a blood spurting wound is added -By Clone JC Denton
|
||
|
||
key = va( "smoke_wound_%s", materialType );
|
||
const char *bleed = spawnArgs.GetString( key );
|
||
|
||
if ( *bleed == '\0' ) {
|
||
bleed = def->dict.GetString( key );
|
||
|
||
if( *bleed == '\0' )
|
||
bleed = def->dict.GetString( "smoke_wound_metal" ); // play default smoke
|
||
|
||
if( *bleed == '\0' )
|
||
bleed = def->dict.GetString( "smoke_wound" ); // play default smoke
|
||
}
|
||
|
||
if ( *bleed != '\0' ) {
|
||
entDamageEffect_t *de = new entDamageEffect_t;
|
||
de->next = this->entDamageEffects;
|
||
this->entDamageEffects = de;
|
||
|
||
de->origin = (collision.c.point - renderEntity.origin) * renderEntity.axis.Transpose();
|
||
de->dir = collision.c.normal * renderEntity.axis.Transpose();
|
||
de->type = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, bleed ) );
|
||
|
||
//de->isTimeInitialized = false;
|
||
de->time = -1; // -1 means this effect has just started, see idEntity::UpdateParticles()
|
||
|
||
if (!(thinkFlags & TH_UPDATEWOUNDPARTICLES)) {// if flag was not set before set it now
|
||
BecomeActive( TH_UPDATEWOUNDPARTICLES );
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
/*
|
||
============
|
||
idEntity::Pain
|
||
|
||
Called whenever an entity recieves damage. Returns whether the entity responds to the pain.
|
||
This is a virtual function that subclasses are expected to implement.
|
||
============
|
||
*/
|
||
bool idEntity::Pain( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
============
|
||
idEntity::Killed
|
||
|
||
Called whenever an entity's health is reduced to 0 or less.
|
||
This is a virtual function that subclasses are expected to implement.
|
||
============
|
||
*/
|
||
void idEntity::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
|
||
}
|
||
|
||
|
||
/***********************************************************************
|
||
|
||
Script functions
|
||
|
||
***********************************************************************/
|
||
|
||
/*
|
||
================
|
||
idEntity::ShouldConstructScriptObjectAtSpawn
|
||
|
||
Called during idEntity::Spawn to see if it should construct the script object or not.
|
||
Overridden by subclasses that need to spawn the script object themselves.
|
||
================
|
||
*/
|
||
bool idEntity::ShouldConstructScriptObjectAtSpawn( void ) const {
|
||
return true;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::ConstructScriptObject
|
||
|
||
Called during idEntity::Spawn. Calls the constructor on the script object.
|
||
Can be overridden by subclasses when a thread doesn't need to be allocated.
|
||
================
|
||
*/
|
||
idThread *idEntity::ConstructScriptObject( void ) {
|
||
idThread *thread;
|
||
const function_t *constructor;
|
||
|
||
// init the script object's data
|
||
scriptObject.ClearObject();
|
||
|
||
// call script object's constructor
|
||
constructor = scriptObject.GetConstructor();
|
||
if ( constructor ) {
|
||
// start a thread that will initialize after Spawn is done being called
|
||
thread = new idThread();
|
||
thread->SetThreadName( name.c_str() );
|
||
thread->CallFunction( this, constructor, true );
|
||
thread->DelayedStart( 0 );
|
||
} else {
|
||
thread = NULL;
|
||
}
|
||
|
||
// clear out the object's memory
|
||
scriptObject.ClearObject();
|
||
|
||
return thread;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::DeconstructScriptObject
|
||
|
||
Called during idEntity::~idEntity. Calls the destructor on the script object.
|
||
Can be overridden by subclasses when a thread doesn't need to be allocated.
|
||
Not called during idGameLocal::MapShutdown.
|
||
================
|
||
*/
|
||
void idEntity::DeconstructScriptObject( void ) {
|
||
idThread *thread;
|
||
const function_t *destructor;
|
||
|
||
// don't bother calling the script object's destructor on map shutdown
|
||
if ( gameLocal.GameState() == GAMESTATE_SHUTDOWN ) {
|
||
return;
|
||
}
|
||
|
||
// call script object's destructor
|
||
destructor = scriptObject.GetDestructor();
|
||
if ( destructor ) {
|
||
// start a thread that will run immediately and be destroyed
|
||
thread = new idThread();
|
||
thread->SetThreadName( name.c_str() );
|
||
thread->CallFunction( this, destructor, true );
|
||
thread->Execute();
|
||
delete thread;
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::HasSignal
|
||
================
|
||
*/
|
||
bool idEntity::HasSignal( signalNum_t signalnum ) const {
|
||
if ( !signals ) {
|
||
return false;
|
||
}
|
||
assert( ( signalnum >= 0 ) && ( signalnum < NUM_SIGNALS ) );
|
||
return ( signals->signal[ signalnum ].Num() > 0 );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::SetSignal
|
||
================
|
||
*/
|
||
void idEntity::SetSignal( signalNum_t signalnum, idThread *thread, const function_t *function ) {
|
||
int i;
|
||
int num;
|
||
signal_t sig;
|
||
int threadnum;
|
||
|
||
assert( ( signalnum >= 0 ) && ( signalnum < NUM_SIGNALS ) );
|
||
|
||
if ( !signals ) {
|
||
signals = new signalList_t;
|
||
}
|
||
|
||
assert( thread );
|
||
threadnum = thread->GetThreadNum();
|
||
|
||
num = signals->signal[ signalnum ].Num();
|
||
for( i = 0; i < num; i++ ) {
|
||
if ( signals->signal[ signalnum ][ i ].threadnum == threadnum ) {
|
||
signals->signal[ signalnum ][ i ].function = function;
|
||
return;
|
||
}
|
||
}
|
||
|
||
if ( num >= MAX_SIGNAL_THREADS ) {
|
||
thread->Error( "Exceeded maximum number of signals per object" );
|
||
}
|
||
|
||
sig.threadnum = threadnum;
|
||
sig.function = function;
|
||
signals->signal[ signalnum ].Append( sig );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::ClearSignal
|
||
================
|
||
*/
|
||
void idEntity::ClearSignal( idThread *thread, signalNum_t signalnum ) {
|
||
assert( thread );
|
||
if ( ( signalnum < 0 ) || ( signalnum >= NUM_SIGNALS ) ) {
|
||
gameLocal.Error( "Signal out of range" );
|
||
}
|
||
|
||
if ( !signals ) {
|
||
return;
|
||
}
|
||
|
||
signals->signal[ signalnum ].Clear();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::ClearSignalThread
|
||
================
|
||
*/
|
||
void idEntity::ClearSignalThread( signalNum_t signalnum, idThread *thread ) {
|
||
int i;
|
||
int num;
|
||
int threadnum;
|
||
|
||
assert( thread );
|
||
|
||
if ( ( signalnum < 0 ) || ( signalnum >= NUM_SIGNALS ) ) {
|
||
gameLocal.Error( "Signal out of range" );
|
||
}
|
||
|
||
if ( !signals ) {
|
||
return;
|
||
}
|
||
|
||
threadnum = thread->GetThreadNum();
|
||
|
||
num = signals->signal[ signalnum ].Num();
|
||
for( i = 0; i < num; i++ ) {
|
||
if ( signals->signal[ signalnum ][ i ].threadnum == threadnum ) {
|
||
signals->signal[ signalnum ].RemoveIndex( i );
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Signal
|
||
================
|
||
*/
|
||
void idEntity::Signal( signalNum_t signalnum ) {
|
||
int i;
|
||
int num;
|
||
signal_t sigs[ MAX_SIGNAL_THREADS ];
|
||
idThread *thread;
|
||
|
||
assert( ( signalnum >= 0 ) && ( signalnum < NUM_SIGNALS ) );
|
||
|
||
if ( !signals ) {
|
||
return;
|
||
}
|
||
|
||
// we copy the signal list since each thread has the potential
|
||
// to end any of the threads in the list. By copying the list
|
||
// we don't have to worry about the list changing as we're
|
||
// processing it.
|
||
num = signals->signal[ signalnum ].Num();
|
||
for( i = 0; i < num; i++ ) {
|
||
sigs[ i ] = signals->signal[ signalnum ][ i ];
|
||
}
|
||
|
||
// clear out the signal list so that we don't get into an infinite loop
|
||
signals->signal[ signalnum ].Clear();
|
||
|
||
for( i = 0; i < num; i++ ) {
|
||
thread = idThread::GetThread( sigs[ i ].threadnum );
|
||
if ( thread ) {
|
||
thread->CallFunction( this, sigs[ i ].function, true );
|
||
thread->Execute();
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::SignalEvent
|
||
================
|
||
*/
|
||
void idEntity::SignalEvent( idThread *thread, signalNum_t signalnum ) {
|
||
if ( ( signalnum < 0 ) || ( signalnum >= NUM_SIGNALS ) ) {
|
||
gameLocal.Error( "Signal out of range" );
|
||
}
|
||
|
||
if ( !signals ) {
|
||
return;
|
||
}
|
||
|
||
Signal( signalnum );
|
||
}
|
||
|
||
/***********************************************************************
|
||
|
||
Guis.
|
||
|
||
***********************************************************************/
|
||
|
||
|
||
/*
|
||
================
|
||
idEntity::TriggerGuis
|
||
================
|
||
*/
|
||
void idEntity::TriggerGuis( void ) {
|
||
int i;
|
||
for ( i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
|
||
if ( renderEntity.gui[ i ] ) {
|
||
renderEntity.gui[ i ]->Trigger( gameLocal.time );
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::HandleGuiCommands
|
||
================
|
||
*/
|
||
bool idEntity::HandleGuiCommands( idEntity *entityGui, const char *cmds ) {
|
||
idEntity *targetEnt;
|
||
bool ret = false;
|
||
if ( entityGui && cmds && *cmds ) {
|
||
idLexer src;
|
||
idToken token, token2, token3, token4;
|
||
src.LoadMemory( cmds, strlen( cmds ), "guiCommands" );
|
||
while( 1 ) {
|
||
|
||
if ( !src.ReadToken( &token ) ) {
|
||
return ret;
|
||
}
|
||
|
||
if ( token == ";" ) {
|
||
continue;
|
||
}
|
||
|
||
if ( token.Icmp( "activate" ) == 0 ) {
|
||
bool targets = true;
|
||
if ( src.ReadToken( &token2 ) ) {
|
||
if ( token2 == ";" ) {
|
||
src.UnreadToken( &token2 );
|
||
} else {
|
||
targets = false;
|
||
}
|
||
}
|
||
|
||
if ( targets ) {
|
||
entityGui->ActivateTargets( this );
|
||
} else {
|
||
idEntity *ent = gameLocal.FindEntity( token2 );
|
||
if ( ent ) {
|
||
ent->Signal( SIG_TRIGGER );
|
||
ent->PostEventMS( &EV_Activate, 0, this );
|
||
}
|
||
}
|
||
|
||
entityGui->renderEntity.shaderParms[ SHADERPARM_MODE ] = 1.0f;
|
||
continue;
|
||
}
|
||
|
||
|
||
if ( token.Icmp( "runScript" ) == 0 ) {
|
||
if ( src.ReadToken( &token2 ) ) {
|
||
while( src.CheckTokenString( "::" ) ) {
|
||
idToken token3;
|
||
if ( !src.ReadToken( &token3 ) ) {
|
||
gameLocal.Error( "Expecting function name following '::' in gui for entity '%s'", entityGui->name.c_str() );
|
||
}
|
||
token2 += "::" + token3;
|
||
}
|
||
const function_t *func = gameLocal.program.FindFunction( token2 );
|
||
if ( !func ) {
|
||
gameLocal.Error( "Can't find function '%s' for gui in entity '%s'", token2.c_str(), entityGui->name.c_str() );
|
||
} else {
|
||
idThread *thread = new idThread( func );
|
||
thread->DelayedStart( 0 );
|
||
}
|
||
}
|
||
continue;
|
||
}
|
||
|
||
if ( token.Icmp("play") == 0 ) {
|
||
if ( src.ReadToken( &token2 ) ) {
|
||
const idSoundShader *shader = declManager->FindSound(token2);
|
||
entityGui->StartSoundShader( shader, SND_CHANNEL_ANY, 0, false, NULL );
|
||
}
|
||
continue;
|
||
}
|
||
|
||
if ( token.Icmp( "setkeyval" ) == 0 ) {
|
||
if ( src.ReadToken( &token2 ) && src.ReadToken(&token3) && src.ReadToken( &token4 ) ) {
|
||
idEntity *ent = gameLocal.FindEntity( token2 );
|
||
if ( ent ) {
|
||
ent->spawnArgs.Set( token3, token4 );
|
||
ent->UpdateChangeableSpawnArgs( NULL );
|
||
ent->UpdateVisuals();
|
||
}
|
||
}
|
||
continue;
|
||
}
|
||
|
||
if ( token.Icmp( "setshaderparm" ) == 0 ) {
|
||
if ( src.ReadToken( &token2 ) && src.ReadToken(&token3) ) {
|
||
entityGui->SetShaderParm( atoi( token2 ), atof( token3 ) );
|
||
entityGui->UpdateVisuals();
|
||
}
|
||
continue;
|
||
}
|
||
|
||
if ( token.Icmp("close") == 0 ) {
|
||
ret = true;
|
||
continue;
|
||
}
|
||
|
||
if ( !token.Icmp( "turkeyscore" ) ) {
|
||
if ( src.ReadToken( &token2 ) && entityGui->renderEntity.gui[0] ) {
|
||
int score = entityGui->renderEntity.gui[0]->State().GetInt( "score" );
|
||
score += atoi( token2 );
|
||
entityGui->renderEntity.gui[0]->SetStateInt( "score", score );
|
||
if ( gameLocal.GetLocalPlayer() && score >= 25000 && !gameLocal.GetLocalPlayer()->inventory.turkeyScore ) {
|
||
gameLocal.GetLocalPlayer()->GiveEmail( "highScore" );
|
||
gameLocal.GetLocalPlayer()->inventory.turkeyScore = true;
|
||
}
|
||
}
|
||
continue;
|
||
}
|
||
|
||
|
||
// handy for debugging GUI stuff
|
||
if ( !token.Icmp( "print" ) ) {
|
||
idStr msg;
|
||
while ( src.ReadToken( &token2 ) ) {
|
||
if ( token2 == ";" ) {
|
||
src.UnreadToken( &token2 );
|
||
break;
|
||
}
|
||
msg += token2.c_str();
|
||
}
|
||
common->Printf( "ent gui 0x%x '%s': %s\n", entityNumber, name.c_str(), msg.c_str() );
|
||
continue;
|
||
}
|
||
|
||
// if we get to this point we don't know how to handle it
|
||
src.UnreadToken(&token);
|
||
if ( !HandleSingleGuiCommand( entityGui, &src ) ) {
|
||
// not handled there see if entity or any of its targets can handle it
|
||
// this will only work for one target atm
|
||
if ( entityGui->HandleSingleGuiCommand( entityGui, &src ) ) {
|
||
continue;
|
||
}
|
||
|
||
int c = entityGui->targets.Num();
|
||
int i;
|
||
for ( i = 0; i < c; i++) {
|
||
targetEnt = entityGui->targets[ i ].GetEntity();
|
||
if ( targetEnt && targetEnt->HandleSingleGuiCommand( entityGui, &src ) ) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ( i == c ) {
|
||
// not handled
|
||
common->DPrintf( "idEntity::HandleGuiCommands: '%s' not handled\n", token.c_str() );
|
||
src.ReadToken( &token );
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::HandleSingleGuiCommand
|
||
================
|
||
*/
|
||
bool idEntity::HandleSingleGuiCommand( idEntity *entityGui, idLexer *src ) {
|
||
return false;
|
||
}
|
||
|
||
/***********************************************************************
|
||
|
||
Targets
|
||
|
||
***********************************************************************/
|
||
|
||
/*
|
||
===============
|
||
idEntity::FindTargets
|
||
|
||
We have to wait until all entities are spawned
|
||
Used to build lists of targets after the entity is spawned. Since not all entities
|
||
have been spawned when the entity is created at map load time, we have to wait
|
||
===============
|
||
*/
|
||
void idEntity::FindTargets( void ) {
|
||
int i;
|
||
|
||
// targets can be a list of multiple names
|
||
gameLocal.GetTargets( spawnArgs, targets, "target" );
|
||
|
||
// ensure that we don't target ourselves since that could cause an infinite loop when activating entities
|
||
for( i = 0; i < targets.Num(); i++ ) {
|
||
if ( targets[ i ].GetEntity() == this ) {
|
||
gameLocal.Error( "Entity '%s' is targeting itself", name.c_str() );
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::RemoveNullTargets
|
||
================
|
||
*/
|
||
void idEntity::RemoveNullTargets( void ) {
|
||
int i;
|
||
|
||
for( i = targets.Num() - 1; i >= 0; i-- ) {
|
||
if ( !targets[ i ].GetEntity() ) {
|
||
targets.RemoveIndex( i );
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
==============================
|
||
idEntity::ActivateTargets
|
||
|
||
"activator" should be set to the entity that initiated the firing.
|
||
==============================
|
||
*/
|
||
void idEntity::ActivateTargets( idEntity *activator ) const {
|
||
idEntity *ent;
|
||
int i, j;
|
||
|
||
for( i = 0; i < targets.Num(); i++ ) {
|
||
ent = targets[ i ].GetEntity();
|
||
if ( !ent ) {
|
||
continue;
|
||
}
|
||
if ( ent->RespondsTo( EV_Activate ) || ent->HasSignal( SIG_TRIGGER ) ) {
|
||
ent->Signal( SIG_TRIGGER );
|
||
ent->ProcessEvent( &EV_Activate, activator );
|
||
}
|
||
for ( j = 0; j < MAX_RENDERENTITY_GUI; j++ ) {
|
||
if ( ent->renderEntity.gui[ j ] ) {
|
||
ent->renderEntity.gui[ j ]->Trigger( gameLocal.time );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/***********************************************************************
|
||
|
||
Misc.
|
||
|
||
***********************************************************************/
|
||
|
||
/*
|
||
================
|
||
idEntity::Teleport
|
||
================
|
||
*/
|
||
void idEntity::Teleport( const idVec3 &origin, const idAngles &angles, idEntity *destination ) {
|
||
GetPhysics()->SetOrigin( origin );
|
||
GetPhysics()->SetAxis( angles.ToMat3() );
|
||
|
||
UpdateVisuals();
|
||
}
|
||
|
||
/*
|
||
============
|
||
idEntity::TouchTriggers
|
||
|
||
Activate all trigger entities touched at the current position.
|
||
============
|
||
*/
|
||
bool idEntity::TouchTriggers( void ) const {
|
||
int i, numClipModels, numEntities;
|
||
idClipModel * cm;
|
||
idClipModel * clipModels[ MAX_GENTITIES ];
|
||
idEntity * ent;
|
||
trace_t trace;
|
||
|
||
memset( &trace, 0, sizeof( trace ) );
|
||
trace.endpos = GetPhysics()->GetOrigin();
|
||
trace.endAxis = GetPhysics()->GetAxis();
|
||
|
||
numClipModels = gameLocal.clip.ClipModelsTouchingBounds( GetPhysics()->GetAbsBounds(), CONTENTS_TRIGGER, clipModels, MAX_GENTITIES );
|
||
numEntities = 0;
|
||
|
||
for ( i = 0; i < numClipModels; i++ ) {
|
||
cm = clipModels[ i ];
|
||
|
||
// don't touch it if we're the owner
|
||
if ( cm->GetOwner() == this ) {
|
||
continue;
|
||
}
|
||
|
||
ent = cm->GetEntity();
|
||
|
||
if ( !ent->RespondsTo( EV_Touch ) && !ent->HasSignal( SIG_TOUCH ) ) {
|
||
continue;
|
||
}
|
||
|
||
if ( !GetPhysics()->ClipContents( cm ) ) {
|
||
continue;
|
||
}
|
||
|
||
numEntities++;
|
||
|
||
trace.c.contents = cm->GetContents();
|
||
trace.c.entityNum = cm->GetEntity()->entityNumber;
|
||
trace.c.id = cm->GetId();
|
||
|
||
ent->Signal( SIG_TOUCH );
|
||
ent->ProcessEvent( &EV_Touch, this, &trace );
|
||
|
||
if ( !gameLocal.entities[ entityNumber ] ) {
|
||
gameLocal.Printf( "entity was removed while touching triggers\n" );
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return ( numEntities != 0 );
|
||
}
|
||
|
||
//ivan start
|
||
|
||
/*
|
||
============
|
||
idEntity::CanInteract
|
||
|
||
If it returns false, prevents EV_Interact to be raised.
|
||
Overridden by subclasses.
|
||
============
|
||
*/
|
||
bool idEntity::CanInteract( int flags ) const {
|
||
return false; //by default
|
||
}
|
||
|
||
/*
|
||
============
|
||
idEntity::InteractTouchingTriggers
|
||
|
||
Interact with all trigger entities touched at the current position.
|
||
============
|
||
*/
|
||
bool idEntity::InteractTouchingTriggers( int flags ) const {
|
||
int i, numClipModels, numEntities;
|
||
idClipModel * cm;
|
||
idClipModel * clipModels[ MAX_GENTITIES ];
|
||
idEntity * ent;
|
||
trace_t trace;
|
||
|
||
memset( &trace, 0, sizeof( trace ) );
|
||
trace.endpos = GetPhysics()->GetOrigin();
|
||
trace.endAxis = GetPhysics()->GetAxis();
|
||
|
||
numClipModels = gameLocal.clip.ClipModelsTouchingBounds( GetPhysics()->GetAbsBounds(), CONTENTS_TRIGGER, clipModels, MAX_GENTITIES );
|
||
numEntities = 0;
|
||
|
||
for ( i = 0; i < numClipModels; i++ ) {
|
||
cm = clipModels[ i ];
|
||
|
||
// don't touch it if we're the owner
|
||
if ( cm->GetOwner() == this ) {
|
||
continue;
|
||
}
|
||
|
||
ent = cm->GetEntity();
|
||
|
||
if ( !ent->RespondsTo( EV_Interact ) ) { //if ( !ent->RespondsTo( EV_Touch ) && !ent->HasSignal( SIG_TOUCH ) ) {
|
||
continue;
|
||
}
|
||
|
||
if ( !GetPhysics()->ClipContents( cm ) ) {
|
||
continue;
|
||
}
|
||
|
||
if ( !ent->CanInteract( flags ) ){ //we need to know if it can interact, otherwise we'll return true even if nothing is done.
|
||
continue;
|
||
}
|
||
|
||
numEntities++;
|
||
|
||
/*
|
||
trace.c.contents = cm->GetContents();
|
||
trace.c.entityNum = cm->GetEntity()->entityNumber;
|
||
trace.c.id = cm->GetId();
|
||
*/
|
||
|
||
ent->ProcessEvent( &EV_Interact, this, flags ); //sono arrivato qui - ora chiamo sta funz in player ::think se <20> il caso e vedo se posso semplificare il mio trigger...
|
||
|
||
/* was
|
||
ent->Signal( SIG_TOUCH );
|
||
ent->ProcessEvent( &EV_Touch, this, &trace );
|
||
*/
|
||
|
||
if ( !gameLocal.entities[ entityNumber ] ) {
|
||
gameLocal.Printf( "entity was removed while interacting with triggers\n" );
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return ( numEntities != 0 );
|
||
}
|
||
|
||
|
||
/*
|
||
===============
|
||
idEntity::GetFirstValidTarget
|
||
===============
|
||
*/
|
||
idEntity * idEntity::GetFirstValidTarget( void ) const {
|
||
idEntity *ent = NULL;
|
||
|
||
for( int i = 0; i < targets.Num(); i++){
|
||
ent = targets[ i ].GetEntity();
|
||
if( ent ) break;
|
||
}
|
||
return ent;
|
||
}
|
||
|
||
|
||
/*
|
||
=====================
|
||
idEntity::CheckDamageFx
|
||
=====================
|
||
|
||
void idEntity::CheckDamageFx( const idDict *damageDef ){ //TODO: move to actors?
|
||
if( !damageDef ){ return; }
|
||
if( !spawnArgs.GetBool( "allowDmgfxs", "0" ) ){
|
||
return;
|
||
}
|
||
int dmgFxType = damageDef->GetInt( "dmgFxType", "0" ); //default is "0" -> no fx
|
||
if( dmgFxType > DMGFX_NONE && dmgFxType < NUM_DMGFX_TYPES ){
|
||
StartDamageFx( dmgFxType );
|
||
}
|
||
}
|
||
*/
|
||
|
||
/*
|
||
=====================
|
||
idEntity::StartDamageFx
|
||
=====================
|
||
|
||
void idEntity::StartDamageFx( int type ){ //TODO: move to actors?
|
||
int i;
|
||
idDamagingFx* tempFx;
|
||
|
||
if( type <= DMGFX_NONE || type >= NUM_DMGFX_TYPES ){
|
||
gameLocal.Warning("StartDamageFx: invalid dmgFxType");
|
||
return;
|
||
}
|
||
|
||
//remove invalid ones
|
||
for( i = dmgFxEntities.Num() - 1; i >= 0; i-- ) {
|
||
if ( !dmgFxEntities[ i ].GetEntity() ) {
|
||
dmgFxEntities.RemoveIndex( i );
|
||
}
|
||
}
|
||
|
||
//check if the effect is already active --> restart it
|
||
for( i = dmgFxEntities.Num() - 1; i >= 0; i-- ) {
|
||
tempFx = dmgFxEntities[ i ].GetEntity();
|
||
if( tempFx->GetDmgFxType() == type ){
|
||
tempFx->Restart();
|
||
return;
|
||
}
|
||
}
|
||
|
||
//start a new one
|
||
tempFx = idDamagingFx::StartDamagingFx( type, this );
|
||
if( tempFx ){
|
||
idEntityPtr<idDamagingFx> &newFxPtr = dmgFxEntities.Alloc();
|
||
newFxPtr = tempFx;
|
||
}
|
||
|
||
}
|
||
*/
|
||
//ivan end
|
||
|
||
/*
|
||
================
|
||
idEntity::GetSpline
|
||
================
|
||
*/
|
||
idCurve_Spline<idVec3> *idEntity::GetSpline( void ) const {
|
||
int i, numPoints, t;
|
||
const idKeyValue *kv;
|
||
idLexer lex;
|
||
idVec3 v;
|
||
idCurve_Spline<idVec3> *spline;
|
||
const char *curveTag = "curve_";
|
||
|
||
kv = spawnArgs.MatchPrefix( curveTag );
|
||
if ( !kv ) {
|
||
return NULL;
|
||
}
|
||
|
||
idStr str = kv->GetKey().Right( kv->GetKey().Length() - strlen( curveTag ) );
|
||
if ( str.Icmp( "CatmullRomSpline" ) == 0 ) {
|
||
spline = new idCurve_CatmullRomSpline<idVec3>();
|
||
} else if ( str.Icmp( "nubs" ) == 0 ) {
|
||
spline = new idCurve_NonUniformBSpline<idVec3>();
|
||
} else if ( str.Icmp( "nurbs" ) == 0 ) {
|
||
spline = new idCurve_NURBS<idVec3>();
|
||
} else {
|
||
spline = new idCurve_BSpline<idVec3>();
|
||
}
|
||
|
||
spline->SetBoundaryType( idCurve_Spline<idVec3>::BT_CLAMPED );
|
||
|
||
lex.LoadMemory( kv->GetValue(), kv->GetValue().Length(), curveTag );
|
||
numPoints = lex.ParseInt();
|
||
lex.ExpectTokenString( "(" );
|
||
for ( t = i = 0; i < numPoints; i++, t += 100 ) {
|
||
v.x = lex.ParseFloat();
|
||
v.y = lex.ParseFloat();
|
||
v.z = lex.ParseFloat();
|
||
spline->AddValue( t, v );
|
||
}
|
||
lex.ExpectTokenString( ")" );
|
||
|
||
return spline;
|
||
}
|
||
|
||
/*
|
||
===============
|
||
idEntity::ShowEditingDialog
|
||
===============
|
||
*/
|
||
void idEntity::ShowEditingDialog( void ) {
|
||
}
|
||
|
||
/***********************************************************************
|
||
|
||
Events
|
||
|
||
***********************************************************************/
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_GetName
|
||
================
|
||
*/
|
||
void idEntity::Event_GetName( void ) {
|
||
idThread::ReturnString( name.c_str() );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_SetName
|
||
================
|
||
*/
|
||
void idEntity::Event_SetName( const char *newname ) {
|
||
SetName( newname );
|
||
}
|
||
|
||
/*
|
||
===============
|
||
idEntity::Event_FindTargets
|
||
===============
|
||
*/
|
||
void idEntity::Event_FindTargets( void ) {
|
||
FindTargets();
|
||
}
|
||
|
||
/*
|
||
============
|
||
idEntity::Event_ActivateTargets
|
||
|
||
Activates any entities targeted by this entity. Mainly used as an
|
||
event to delay activating targets.
|
||
============
|
||
*/
|
||
void idEntity::Event_ActivateTargets( idEntity *activator ) {
|
||
ActivateTargets( activator );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_NumTargets
|
||
================
|
||
*/
|
||
void idEntity::Event_NumTargets( void ) {
|
||
idThread::ReturnFloat( targets.Num() );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_GetTarget
|
||
================
|
||
*/
|
||
void idEntity::Event_GetTarget( float index ) {
|
||
int i;
|
||
|
||
i = ( int )index;
|
||
if ( ( i < 0 ) || i >= targets.Num() ) {
|
||
idThread::ReturnEntity( NULL );
|
||
} else {
|
||
idThread::ReturnEntity( targets[ i ].GetEntity() );
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_RandomTarget
|
||
================
|
||
*/
|
||
void idEntity::Event_RandomTarget( const char *ignore ) {
|
||
int num;
|
||
idEntity *ent;
|
||
int i;
|
||
int ignoreNum;
|
||
|
||
RemoveNullTargets();
|
||
if ( !targets.Num() ) {
|
||
idThread::ReturnEntity( NULL );
|
||
return;
|
||
}
|
||
|
||
ignoreNum = -1;
|
||
if ( ignore && ( ignore[ 0 ] != 0 ) && ( targets.Num() > 1 ) ) {
|
||
for( i = 0; i < targets.Num(); i++ ) {
|
||
ent = targets[ i ].GetEntity();
|
||
if ( ent && ( ent->name == ignore ) ) {
|
||
ignoreNum = i;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( ignoreNum >= 0 ) {
|
||
num = gameLocal.random.RandomInt( targets.Num() - 1 );
|
||
if ( num >= ignoreNum ) {
|
||
num++;
|
||
}
|
||
} else {
|
||
num = gameLocal.random.RandomInt( targets.Num() );
|
||
}
|
||
|
||
ent = targets[ num ].GetEntity();
|
||
idThread::ReturnEntity( ent );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_BindToJoint
|
||
================
|
||
*/
|
||
void idEntity::Event_BindToJoint( idEntity *master, const char *jointname, float orientated ) {
|
||
BindToJoint( master, jointname, ( orientated != 0.0f ) );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_RemoveBinds
|
||
================
|
||
*/
|
||
void idEntity::Event_RemoveBinds( void ) {
|
||
RemoveBinds();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_Bind
|
||
================
|
||
*/
|
||
void idEntity::Event_Bind( idEntity *master ) {
|
||
Bind( master, true );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_BindPosition
|
||
================
|
||
*/
|
||
void idEntity::Event_BindPosition( idEntity *master ) {
|
||
Bind( master, false );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_Unbind
|
||
================
|
||
*/
|
||
void idEntity::Event_Unbind( void ) {
|
||
Unbind();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_SpawnBind
|
||
================
|
||
*/
|
||
void idEntity::Event_SpawnBind( void ) {
|
||
idEntity *parent;
|
||
const char *bind, *joint, *bindanim;
|
||
jointHandle_t bindJoint;
|
||
bool bindOrientated;
|
||
int id;
|
||
const idAnim *anim;
|
||
int animNum;
|
||
idAnimator *parentAnimator;
|
||
|
||
if ( spawnArgs.GetString( "bind", "", &bind ) ) {
|
||
if ( idStr::Icmp( bind, "worldspawn" ) == 0 ) {
|
||
//FIXME: Completely unneccessary since the worldspawn is called "world"
|
||
parent = gameLocal.world;
|
||
} else {
|
||
parent = gameLocal.FindEntity( bind );
|
||
}
|
||
bindOrientated = spawnArgs.GetBool( "bindOrientated", "1" );
|
||
if ( parent ) {
|
||
// bind to a joint of the skeletal model of the parent
|
||
if ( spawnArgs.GetString( "bindToJoint", "", &joint ) && *joint ) {
|
||
parentAnimator = parent->GetAnimator();
|
||
if ( !parentAnimator ) {
|
||
gameLocal.Error( "Cannot bind to joint '%s' on '%s'. Entity does not support skeletal models.", joint, name.c_str() );
|
||
}
|
||
bindJoint = parentAnimator->GetJointHandle( joint );
|
||
if ( bindJoint == INVALID_JOINT ) {
|
||
gameLocal.Error( "Joint '%s' not found for bind on '%s'", joint, name.c_str() );
|
||
}
|
||
|
||
// bind it relative to a specific anim
|
||
if ( ( parent->spawnArgs.GetString( "bindanim", "", &bindanim ) || parent->spawnArgs.GetString( "anim", "", &bindanim ) ) && *bindanim ) {
|
||
animNum = parentAnimator->GetAnim( bindanim );
|
||
if ( !animNum ) {
|
||
gameLocal.Error( "Anim '%s' not found for bind on '%s'", bindanim, name.c_str() );
|
||
}
|
||
anim = parentAnimator->GetAnim( animNum );
|
||
if ( !anim ) {
|
||
gameLocal.Error( "Anim '%s' not found for bind on '%s'", bindanim, name.c_str() );
|
||
}
|
||
|
||
// make sure parent's render origin has been set
|
||
parent->UpdateModelTransform();
|
||
|
||
//FIXME: need a BindToJoint that accepts a joint position
|
||
parentAnimator->CreateFrame( gameLocal.time, true );
|
||
idJointMat *frame = parent->renderEntity.joints;
|
||
gameEdit->ANIM_CreateAnimFrame( parentAnimator->ModelHandle(), anim->MD5Anim( 0 ), parent->renderEntity.numJoints, frame, 0, parentAnimator->ModelDef()->GetVisualOffset(), parentAnimator->RemoveOrigin() );
|
||
BindToJoint( parent, joint, bindOrientated );
|
||
parentAnimator->ForceUpdate();
|
||
} else {
|
||
BindToJoint( parent, joint, bindOrientated );
|
||
}
|
||
}
|
||
// bind to a body of the physics object of the parent
|
||
else if ( spawnArgs.GetInt( "bindToBody", "0", id ) ) {
|
||
BindToBody( parent, id, bindOrientated );
|
||
}
|
||
// bind to the parent
|
||
else {
|
||
Bind( parent, bindOrientated );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_SetOwner
|
||
================
|
||
*/
|
||
void idEntity::Event_SetOwner( idEntity *owner ) {
|
||
int i;
|
||
|
||
for ( i = 0; i < GetPhysics()->GetNumClipModels(); i++ ) {
|
||
GetPhysics()->GetClipModel( i )->SetOwner( owner );
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_SetModel
|
||
================
|
||
*/
|
||
void idEntity::Event_SetModel( const char *modelname ) {
|
||
SetModel( modelname );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_SetSkin
|
||
================
|
||
*/
|
||
void idEntity::Event_SetSkin( const char *skinname ) {
|
||
renderEntity.customSkin = declManager->FindSkin( skinname );
|
||
UpdateVisuals();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_GetShaderParm
|
||
================
|
||
*/
|
||
void idEntity::Event_GetShaderParm( int parmnum ) {
|
||
if ( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) {
|
||
gameLocal.Error( "shader parm index (%d) out of range", parmnum );
|
||
}
|
||
|
||
idThread::ReturnFloat( renderEntity.shaderParms[ parmnum ] );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_SetShaderParm
|
||
================
|
||
*/
|
||
void idEntity::Event_SetShaderParm( int parmnum, float value ) {
|
||
SetShaderParm( parmnum, value );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_SetShaderParms
|
||
================
|
||
*/
|
||
void idEntity::Event_SetShaderParms( float parm0, float parm1, float parm2, float parm3 ) {
|
||
renderEntity.shaderParms[ SHADERPARM_RED ] = parm0;
|
||
renderEntity.shaderParms[ SHADERPARM_GREEN ] = parm1;
|
||
renderEntity.shaderParms[ SHADERPARM_BLUE ] = parm2;
|
||
renderEntity.shaderParms[ SHADERPARM_ALPHA ] = parm3;
|
||
UpdateVisuals();
|
||
}
|
||
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_SetColor
|
||
================
|
||
*/
|
||
void idEntity::Event_SetColor( float red, float green, float blue ) {
|
||
SetColor( red, green, blue );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_GetColor
|
||
================
|
||
*/
|
||
void idEntity::Event_GetColor( void ) {
|
||
idVec3 out;
|
||
|
||
GetColor( out );
|
||
idThread::ReturnVector( out );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_IsHidden
|
||
================
|
||
*/
|
||
void idEntity::Event_IsHidden( void ) {
|
||
idThread::ReturnInt( fl.hidden );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_Hide
|
||
================
|
||
*/
|
||
void idEntity::Event_Hide( void ) {
|
||
Hide();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_Show
|
||
================
|
||
*/
|
||
void idEntity::Event_Show( void ) {
|
||
Show();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_CacheSoundShader
|
||
================
|
||
*/
|
||
void idEntity::Event_CacheSoundShader( const char *soundName ) {
|
||
declManager->FindSound( soundName );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_StartSoundShader
|
||
================
|
||
*/
|
||
void idEntity::Event_StartSoundShader( const char *soundName, int channel ) {
|
||
int length;
|
||
|
||
StartSoundShader( declManager->FindSound( soundName ), (s_channelType)channel, 0, false, &length );
|
||
idThread::ReturnFloat( MS2SEC( length ) );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_StopSound
|
||
================
|
||
*/
|
||
void idEntity::Event_StopSound( int channel, int netSync ) {
|
||
StopSound( channel, ( netSync != 0 ) );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_StartSound
|
||
================
|
||
*/
|
||
void idEntity::Event_StartSound( const char *soundName, int channel, int netSync ) {
|
||
int time;
|
||
|
||
StartSound( soundName, ( s_channelType )channel, 0, ( netSync != 0 ), &time );
|
||
idThread::ReturnFloat( MS2SEC( time ) );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_FadeSound
|
||
================
|
||
*/
|
||
void idEntity::Event_FadeSound( int channel, float to, float over ) {
|
||
if ( refSound.referenceSound ) {
|
||
refSound.referenceSound->FadeSound( channel, to, over );
|
||
}
|
||
}
|
||
|
||
//ivan start
|
||
/*
|
||
================
|
||
idEntity::FadeSound
|
||
================
|
||
*/
|
||
void idEntity::FadeSound( int channel, float to, float over ) {
|
||
if ( refSound.referenceSound ) {
|
||
refSound.referenceSound->FadeSound( channel, to, over );
|
||
}
|
||
}
|
||
|
||
//ivan end
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_GetWorldOrigin
|
||
================
|
||
*/
|
||
void idEntity::Event_GetWorldOrigin( void ) {
|
||
idThread::ReturnVector( GetPhysics()->GetOrigin() );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_SetWorldOrigin
|
||
================
|
||
*/
|
||
void idEntity::Event_SetWorldOrigin( idVec3 const &org ) {
|
||
idVec3 neworg = GetLocalCoordinates( org );
|
||
SetOrigin( neworg );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_SetOrigin
|
||
================
|
||
*/
|
||
void idEntity::Event_SetOrigin( idVec3 const &org ) {
|
||
SetOrigin( org );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_GetOrigin
|
||
================
|
||
*/
|
||
void idEntity::Event_GetOrigin( void ) {
|
||
idThread::ReturnVector( GetLocalCoordinates( GetPhysics()->GetOrigin() ) );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_SetAngles
|
||
================
|
||
*/
|
||
void idEntity::Event_SetAngles( idAngles const &ang ) {
|
||
SetAngles( ang );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_GetAngles
|
||
================
|
||
*/
|
||
void idEntity::Event_GetAngles( void ) {
|
||
idAngles ang = GetPhysics()->GetAxis().ToAngles();
|
||
idThread::ReturnVector( idVec3( ang[0], ang[1], ang[2] ) );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_SetLinearVelocity
|
||
================
|
||
*/
|
||
void idEntity::Event_SetLinearVelocity( const idVec3 &velocity ) {
|
||
GetPhysics()->SetLinearVelocity( velocity );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_GetLinearVelocity
|
||
================
|
||
*/
|
||
void idEntity::Event_GetLinearVelocity( void ) {
|
||
idThread::ReturnVector( GetPhysics()->GetLinearVelocity() );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_SetAngularVelocity
|
||
================
|
||
*/
|
||
void idEntity::Event_SetAngularVelocity( const idVec3 &velocity ) {
|
||
GetPhysics()->SetAngularVelocity( velocity );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_GetAngularVelocity
|
||
================
|
||
*/
|
||
void idEntity::Event_GetAngularVelocity( void ) {
|
||
idThread::ReturnVector( GetPhysics()->GetAngularVelocity() );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_SetSize
|
||
================
|
||
*/
|
||
void idEntity::Event_SetSize( idVec3 const &mins, idVec3 const &maxs ) {
|
||
GetPhysics()->SetClipBox( idBounds( mins, maxs ), 1.0f );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_GetSize
|
||
================
|
||
*/
|
||
void idEntity::Event_GetSize( void ) {
|
||
idBounds bounds;
|
||
|
||
bounds = GetPhysics()->GetBounds();
|
||
idThread::ReturnVector( bounds[1] - bounds[0] );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_GetMins
|
||
================
|
||
*/
|
||
void idEntity::Event_GetMins( void ) {
|
||
idThread::ReturnVector( GetPhysics()->GetBounds()[0] );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_GetMaxs
|
||
================
|
||
*/
|
||
void idEntity::Event_GetMaxs( void ) {
|
||
idThread::ReturnVector( GetPhysics()->GetBounds()[1] );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_Touches
|
||
================
|
||
*/
|
||
void idEntity::Event_Touches( idEntity *ent ) {
|
||
if ( !ent ) {
|
||
idThread::ReturnInt( false );
|
||
return;
|
||
}
|
||
|
||
const idBounds &myBounds = GetPhysics()->GetAbsBounds();
|
||
const idBounds &entBounds = ent->GetPhysics()->GetAbsBounds();
|
||
|
||
idThread::ReturnInt( myBounds.IntersectsBounds( entBounds ) );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_SetGuiParm
|
||
================
|
||
*/
|
||
void idEntity::Event_SetGuiParm( const char *key, const char *val ) {
|
||
for ( int i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
|
||
if ( renderEntity.gui[ i ] ) {
|
||
if ( idStr::Icmpn( key, "gui_", 4 ) == 0 ) {
|
||
spawnArgs.Set( key, val );
|
||
}
|
||
renderEntity.gui[ i ]->SetStateString( key, val );
|
||
renderEntity.gui[ i ]->StateChanged( gameLocal.time );
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_SetGuiParm
|
||
================
|
||
*/
|
||
void idEntity::Event_SetGuiFloat( const char *key, float f ) {
|
||
for ( int i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
|
||
if ( renderEntity.gui[ i ] ) {
|
||
renderEntity.gui[ i ]->SetStateString( key, va( "%f", f ) );
|
||
renderEntity.gui[ i ]->StateChanged( gameLocal.time );
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_GetNextKey
|
||
================
|
||
*/
|
||
void idEntity::Event_GetNextKey( const char *prefix, const char *lastMatch ) {
|
||
const idKeyValue *kv;
|
||
const idKeyValue *previous;
|
||
|
||
if ( *lastMatch ) {
|
||
previous = spawnArgs.FindKey( lastMatch );
|
||
} else {
|
||
previous = NULL;
|
||
}
|
||
|
||
kv = spawnArgs.MatchPrefix( prefix, previous );
|
||
if ( !kv ) {
|
||
idThread::ReturnString( "" );
|
||
} else {
|
||
idThread::ReturnString( kv->GetKey() );
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_SetKey
|
||
================
|
||
*/
|
||
void idEntity::Event_SetKey( const char *key, const char *value ) {
|
||
spawnArgs.Set( key, value );
|
||
//rev grab
|
||
UpdateChangeableSpawnArgs( NULL );
|
||
//rev grab
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_GetKey
|
||
================
|
||
*/
|
||
void idEntity::Event_GetKey( const char *key ) {
|
||
const char *value;
|
||
|
||
spawnArgs.GetString( key, "", &value );
|
||
idThread::ReturnString( value );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_GetIntKey
|
||
================
|
||
*/
|
||
void idEntity::Event_GetIntKey( const char *key ) {
|
||
int value;
|
||
|
||
spawnArgs.GetInt( key, "0", value );
|
||
|
||
// scripts only support floats
|
||
idThread::ReturnFloat( value );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_GetFloatKey
|
||
================
|
||
*/
|
||
void idEntity::Event_GetFloatKey( const char *key ) {
|
||
float value;
|
||
|
||
spawnArgs.GetFloat( key, "0", value );
|
||
idThread::ReturnFloat( value );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_GetVectorKey
|
||
================
|
||
*/
|
||
void idEntity::Event_GetVectorKey( const char *key ) {
|
||
idVec3 value;
|
||
|
||
spawnArgs.GetVector( key, "0 0 0", value );
|
||
idThread::ReturnVector( value );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_GetEntityKey
|
||
================
|
||
*/
|
||
void idEntity::Event_GetEntityKey( const char *key ) {
|
||
idEntity *ent;
|
||
const char *entname;
|
||
|
||
if ( !spawnArgs.GetString( key, NULL, &entname ) ) {
|
||
idThread::ReturnEntity( NULL );
|
||
return;
|
||
}
|
||
|
||
ent = gameLocal.FindEntity( entname );
|
||
if ( !ent ) {
|
||
gameLocal.Warning( "Couldn't find entity '%s' specified in '%s' key in entity '%s'", entname, key, name.c_str() );
|
||
}
|
||
|
||
idThread::ReturnEntity( ent );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_RestorePosition
|
||
================
|
||
*/
|
||
void idEntity::Event_RestorePosition( void ) {
|
||
idVec3 org;
|
||
idAngles angles;
|
||
idMat3 axis;
|
||
idEntity * part;
|
||
|
||
spawnArgs.GetVector( "origin", "0 0 0", org );
|
||
|
||
// get the rotation matrix in either full form, or single angle form
|
||
if ( spawnArgs.GetMatrix( "rotation", "1 0 0 0 1 0 0 0 1", axis ) ) {
|
||
angles = axis.ToAngles();
|
||
} else {
|
||
angles[ 0 ] = 0;
|
||
angles[ 1 ] = spawnArgs.GetFloat( "angle" );
|
||
angles[ 2 ] = 0;
|
||
}
|
||
|
||
Teleport( org, angles, NULL );
|
||
|
||
for ( part = teamChain; part != NULL; part = part->teamChain ) {
|
||
if ( part->bindMaster != this ) {
|
||
continue;
|
||
}
|
||
if ( part->GetPhysics()->IsType( idPhysics_Parametric::Type ) ) {
|
||
if ( static_cast<idPhysics_Parametric *>(part->GetPhysics())->IsPusher() ) {
|
||
gameLocal.Warning( "teleported '%s' which has the pushing mover '%s' bound to it\n", GetName(), part->GetName() );
|
||
}
|
||
} else if ( part->GetPhysics()->IsType( idPhysics_AF::Type ) ) {
|
||
gameLocal.Warning( "teleported '%s' which has the articulated figure '%s' bound to it\n", GetName(), part->GetName() );
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_UpdateCameraTarget
|
||
================
|
||
*/
|
||
void idEntity::Event_UpdateCameraTarget( void ) {
|
||
const char *target;
|
||
const idKeyValue *kv;
|
||
idVec3 dir;
|
||
|
||
target = spawnArgs.GetString( "cameraTarget" );
|
||
|
||
cameraTarget = gameLocal.FindEntity( target );
|
||
|
||
if ( cameraTarget ) {
|
||
kv = cameraTarget->spawnArgs.MatchPrefix( "target", NULL );
|
||
while( kv ) {
|
||
idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
|
||
if ( ent && idStr::Icmp( ent->GetEntityDefName(), "target_null" ) == 0) {
|
||
dir = ent->GetPhysics()->GetOrigin() - cameraTarget->GetPhysics()->GetOrigin();
|
||
dir.Normalize();
|
||
cameraTarget->SetAxis( dir.ToMat3() );
|
||
SetAxis(dir.ToMat3());
|
||
break;
|
||
}
|
||
kv = cameraTarget->spawnArgs.MatchPrefix( "target", kv );
|
||
}
|
||
}
|
||
UpdateVisuals();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_DistanceTo
|
||
================
|
||
*/
|
||
void idEntity::Event_DistanceTo( idEntity *ent ) {
|
||
if ( !ent ) {
|
||
// just say it's really far away
|
||
idThread::ReturnFloat( MAX_WORLD_SIZE );
|
||
} else {
|
||
float dist = ( GetPhysics()->GetOrigin() - ent->GetPhysics()->GetOrigin() ).LengthFast();
|
||
idThread::ReturnFloat( dist );
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_DistanceToPoint
|
||
================
|
||
*/
|
||
void idEntity::Event_DistanceToPoint( const idVec3 &point ) {
|
||
float dist = ( GetPhysics()->GetOrigin() - point ).LengthFast();
|
||
idThread::ReturnFloat( dist );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_StartFx
|
||
================
|
||
*/
|
||
void idEntity::Event_StartFx( const char *fx ) {
|
||
idEntityFx::StartFx( fx, NULL, NULL, this, true );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_WaitFrame
|
||
================
|
||
*/
|
||
void idEntity::Event_WaitFrame( void ) {
|
||
idThread *thread;
|
||
|
||
thread = idThread::CurrentThread();
|
||
if ( thread ) {
|
||
thread->WaitFrame();
|
||
}
|
||
}
|
||
|
||
/*
|
||
=====================
|
||
idEntity::Event_Wait
|
||
=====================
|
||
*/
|
||
void idEntity::Event_Wait( float time ) {
|
||
idThread *thread = idThread::CurrentThread();
|
||
|
||
if ( !thread ) {
|
||
gameLocal.Error( "Event 'wait' called from outside thread" );
|
||
}
|
||
|
||
thread->WaitSec( time );
|
||
}
|
||
|
||
/*
|
||
=====================
|
||
idEntity::Event_HasFunction
|
||
=====================
|
||
*/
|
||
void idEntity::Event_HasFunction( const char *name ) {
|
||
const function_t *func;
|
||
|
||
func = scriptObject.GetFunction( name );
|
||
if ( func ) {
|
||
idThread::ReturnInt( true );
|
||
} else {
|
||
idThread::ReturnInt( false );
|
||
}
|
||
}
|
||
|
||
/*
|
||
=====================
|
||
idEntity::Event_CallFunction
|
||
=====================
|
||
*/
|
||
void idEntity::Event_CallFunction( const char *funcname ) {
|
||
const function_t *func;
|
||
idThread *thread;
|
||
|
||
thread = idThread::CurrentThread();
|
||
if ( !thread ) {
|
||
gameLocal.Error( "Event 'callFunction' called from outside thread" );
|
||
}
|
||
|
||
func = scriptObject.GetFunction( funcname );
|
||
if ( !func ) {
|
||
gameLocal.Error( "Unknown function '%s' in '%s'", funcname, scriptObject.GetTypeName() );
|
||
}
|
||
|
||
if ( func->type->NumParameters() != 1 ) {
|
||
gameLocal.Error( "Function '%s' has the wrong number of parameters for 'callFunction'", funcname );
|
||
}
|
||
if ( !scriptObject.GetTypeDef()->Inherits( func->type->GetParmType( 0 ) ) ) {
|
||
gameLocal.Error( "Function '%s' is the wrong type for 'callFunction'", funcname );
|
||
}
|
||
|
||
// function args will be invalid after this call
|
||
thread->CallFunction( this, func, false );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_SetNeverDormant
|
||
================
|
||
*/
|
||
void idEntity::Event_SetNeverDormant( int enable ) {
|
||
fl.neverDormant = ( enable != 0 );
|
||
dormantStart = 0;
|
||
}
|
||
|
||
|
||
//Ivan start
|
||
void idEntity::Event_GetGuiParm(int guiNum, const char *key) {
|
||
if(renderEntity.gui[guiNum-1]) {
|
||
idThread::ReturnString(renderEntity.gui[guiNum-1]->GetStateString(key));
|
||
return;
|
||
}
|
||
idThread::ReturnString("");
|
||
}
|
||
|
||
void idEntity::Event_GetGuiParmFloat(int guiNum, const char *key) {
|
||
if(renderEntity.gui[guiNum-1]) {
|
||
idThread::ReturnFloat(renderEntity.gui[guiNum-1]->GetStateFloat(key));
|
||
return;
|
||
}
|
||
idThread::ReturnFloat(0.0f);
|
||
}
|
||
|
||
void idEntity::Event_GuiNamedEvent(int guiNum, const char *event) {
|
||
if(renderEntity.gui[guiNum-1]) {
|
||
renderEntity.gui[guiNum-1]->HandleNamedEvent(event);
|
||
}
|
||
}
|
||
|
||
void idEntity::Event_SetEntityHealth( float newHealth ) {
|
||
health = newHealth;
|
||
}
|
||
|
||
|
||
void idEntity::Event_GetEntityHealth( void ) {
|
||
idThread::ReturnFloat( health );
|
||
}
|
||
//Ivan end
|
||
|
||
/***********************************************************************
|
||
|
||
Network
|
||
|
||
***********************************************************************/
|
||
|
||
/*
|
||
================
|
||
idEntity::ClientPredictionThink
|
||
================
|
||
*/
|
||
void idEntity::ClientPredictionThink( void ) {
|
||
RunPhysics();
|
||
Present();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::WriteBindToSnapshot
|
||
================
|
||
*/
|
||
void idEntity::WriteBindToSnapshot( idBitMsgDelta &msg ) const {
|
||
int bindInfo;
|
||
|
||
if ( bindMaster ) {
|
||
bindInfo = bindMaster->entityNumber;
|
||
bindInfo |= ( fl.bindOrientated & 1 ) << GENTITYNUM_BITS;
|
||
if ( bindJoint != INVALID_JOINT ) {
|
||
bindInfo |= 1 << ( GENTITYNUM_BITS + 1 );
|
||
bindInfo |= bindJoint << ( 3 + GENTITYNUM_BITS );
|
||
} else if ( bindBody != -1 ) {
|
||
bindInfo |= 2 << ( GENTITYNUM_BITS + 1 );
|
||
bindInfo |= bindBody << ( 3 + GENTITYNUM_BITS );
|
||
}
|
||
} else {
|
||
bindInfo = ENTITYNUM_NONE;
|
||
}
|
||
msg.WriteBits( bindInfo, GENTITYNUM_BITS + 3 + 9 );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::ReadBindFromSnapshot
|
||
================
|
||
*/
|
||
void idEntity::ReadBindFromSnapshot( const idBitMsgDelta &msg ) {
|
||
int bindInfo, bindEntityNum, bindPos;
|
||
bool bindOrientated;
|
||
idEntity *master;
|
||
|
||
bindInfo = msg.ReadBits( GENTITYNUM_BITS + 3 + 9 );
|
||
bindEntityNum = bindInfo & ( ( 1 << GENTITYNUM_BITS ) - 1 );
|
||
|
||
if ( bindEntityNum != ENTITYNUM_NONE ) {
|
||
master = gameLocal.entities[ bindEntityNum ];
|
||
|
||
bindOrientated = ( bindInfo >> GENTITYNUM_BITS ) & 1;
|
||
bindPos = ( bindInfo >> ( GENTITYNUM_BITS + 3 ) );
|
||
switch( ( bindInfo >> ( GENTITYNUM_BITS + 1 ) ) & 3 ) {
|
||
case 1: {
|
||
BindToJoint( master, (jointHandle_t) bindPos, bindOrientated );
|
||
break;
|
||
}
|
||
case 2: {
|
||
BindToBody( master, bindPos, bindOrientated );
|
||
break;
|
||
}
|
||
default: {
|
||
Bind( master, bindOrientated );
|
||
break;
|
||
}
|
||
}
|
||
} else if ( bindMaster ) {
|
||
Unbind();
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::WriteColorToSnapshot
|
||
================
|
||
*/
|
||
void idEntity::WriteColorToSnapshot( idBitMsgDelta &msg ) const {
|
||
idVec4 color;
|
||
|
||
color[0] = renderEntity.shaderParms[ SHADERPARM_RED ];
|
||
color[1] = renderEntity.shaderParms[ SHADERPARM_GREEN ];
|
||
color[2] = renderEntity.shaderParms[ SHADERPARM_BLUE ];
|
||
color[3] = renderEntity.shaderParms[ SHADERPARM_ALPHA ];
|
||
msg.WriteInt( PackColor( color ) );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::ReadColorFromSnapshot
|
||
================
|
||
*/
|
||
void idEntity::ReadColorFromSnapshot( const idBitMsgDelta &msg ) {
|
||
idVec4 color;
|
||
|
||
UnpackColor( msg.ReadInt(), color );
|
||
renderEntity.shaderParms[ SHADERPARM_RED ] = color[0];
|
||
renderEntity.shaderParms[ SHADERPARM_GREEN ] = color[1];
|
||
renderEntity.shaderParms[ SHADERPARM_BLUE ] = color[2];
|
||
renderEntity.shaderParms[ SHADERPARM_ALPHA ] = color[3];
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::WriteGUIToSnapshot
|
||
================
|
||
*/
|
||
void idEntity::WriteGUIToSnapshot( idBitMsgDelta &msg ) const {
|
||
// no need to loop over MAX_RENDERENTITY_GUI at this time
|
||
if ( renderEntity.gui[ 0 ] ) {
|
||
msg.WriteByte( renderEntity.gui[ 0 ]->State().GetInt( "networkState" ) );
|
||
} else {
|
||
msg.WriteByte( 0 );
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::ReadGUIFromSnapshot
|
||
================
|
||
*/
|
||
void idEntity::ReadGUIFromSnapshot( const idBitMsgDelta &msg ) {
|
||
int state;
|
||
idUserInterface *gui;
|
||
state = msg.ReadByte( );
|
||
gui = renderEntity.gui[ 0 ];
|
||
if ( gui && state != mpGUIState ) {
|
||
mpGUIState = state;
|
||
gui->SetStateInt( "networkState", state );
|
||
gui->HandleNamedEvent( "networkState" );
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::WriteToSnapshot
|
||
================
|
||
*/
|
||
void idEntity::WriteToSnapshot( idBitMsgDelta &msg ) const {
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::ReadFromSnapshot
|
||
================
|
||
*/
|
||
void idEntity::ReadFromSnapshot( const idBitMsgDelta &msg ) {
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::ServerSendEvent
|
||
|
||
Saved events are also sent to any client that connects late so all clients
|
||
always receive the events nomatter what time they join the game.
|
||
================
|
||
*/
|
||
void idEntity::ServerSendEvent( int eventId, const idBitMsg *msg, bool saveEvent, int excludeClient ) const {
|
||
idBitMsg outMsg;
|
||
byte msgBuf[MAX_GAME_MESSAGE_SIZE];
|
||
|
||
if ( !gameLocal.isServer ) {
|
||
return;
|
||
}
|
||
|
||
// prevent dupe events caused by frame re-runs
|
||
if ( !gameLocal.isNewFrame ) {
|
||
return;
|
||
}
|
||
|
||
outMsg.Init( msgBuf, sizeof( msgBuf ) );
|
||
outMsg.BeginWriting();
|
||
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_EVENT );
|
||
outMsg.WriteBits( gameLocal.GetSpawnId( this ), 32 );
|
||
outMsg.WriteByte( eventId );
|
||
outMsg.WriteInt( gameLocal.time );
|
||
if ( msg ) {
|
||
outMsg.WriteBits( msg->GetSize(), idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
|
||
outMsg.WriteData( msg->GetData(), msg->GetSize() );
|
||
} else {
|
||
outMsg.WriteBits( 0, idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
|
||
}
|
||
|
||
if ( excludeClient != -1 ) {
|
||
networkSystem->ServerSendReliableMessageExcluding( excludeClient, outMsg );
|
||
} else {
|
||
networkSystem->ServerSendReliableMessage( -1, outMsg );
|
||
}
|
||
|
||
if ( saveEvent ) {
|
||
gameLocal.SaveEntityNetworkEvent( this, eventId, msg );
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::ClientSendEvent
|
||
================
|
||
*/
|
||
void idEntity::ClientSendEvent( int eventId, const idBitMsg *msg ) const {
|
||
idBitMsg outMsg;
|
||
byte msgBuf[MAX_GAME_MESSAGE_SIZE];
|
||
|
||
if ( !gameLocal.isClient ) {
|
||
return;
|
||
}
|
||
|
||
// prevent dupe events caused by frame re-runs
|
||
if ( !gameLocal.isNewFrame ) {
|
||
return;
|
||
}
|
||
|
||
outMsg.Init( msgBuf, sizeof( msgBuf ) );
|
||
outMsg.BeginWriting();
|
||
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_EVENT );
|
||
outMsg.WriteBits( gameLocal.GetSpawnId( this ), 32 );
|
||
outMsg.WriteByte( eventId );
|
||
outMsg.WriteInt( gameLocal.time );
|
||
if ( msg ) {
|
||
outMsg.WriteBits( msg->GetSize(), idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
|
||
outMsg.WriteData( msg->GetData(), msg->GetSize() );
|
||
} else {
|
||
outMsg.WriteBits( 0, idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
|
||
}
|
||
|
||
networkSystem->ClientSendReliableMessage( outMsg );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::ServerReceiveEvent
|
||
================
|
||
*/
|
||
bool idEntity::ServerReceiveEvent( int event, int time, const idBitMsg &msg ) {
|
||
switch( event ) {
|
||
case 0: {
|
||
}
|
||
default: {
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::ClientReceiveEvent
|
||
================
|
||
*/
|
||
bool idEntity::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
|
||
int index;
|
||
const idSoundShader *shader;
|
||
s_channelType channel;
|
||
|
||
switch( event ) {
|
||
case EVENT_STARTSOUNDSHADER: {
|
||
// the sound stuff would early out
|
||
assert( gameLocal.isNewFrame );
|
||
if ( time < gameLocal.realClientTime - 1000 ) {
|
||
// too old, skip it ( reliable messages don't need to be parsed in full )
|
||
common->DPrintf( "ent 0x%x: start sound shader too old (%d ms)\n", entityNumber, gameLocal.realClientTime - time );
|
||
return true;
|
||
}
|
||
index = gameLocal.ClientRemapDecl( DECL_SOUND, msg.ReadInt() );
|
||
if ( index >= 0 && index < declManager->GetNumDecls( DECL_SOUND ) ) {
|
||
shader = declManager->SoundByIndex( index, false );
|
||
channel = (s_channelType)msg.ReadByte();
|
||
StartSoundShader( shader, channel, 0, false, NULL );
|
||
}
|
||
return true;
|
||
}
|
||
case EVENT_STOPSOUNDSHADER: {
|
||
// the sound stuff would early out
|
||
assert( gameLocal.isNewFrame );
|
||
channel = (s_channelType)msg.ReadByte();
|
||
StopSound( channel, false );
|
||
return true;
|
||
}
|
||
default:
|
||
break;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
//rev grab
|
||
/*
|
||
================
|
||
idEntity::SetGrabbedState
|
||
================
|
||
*/
|
||
void idEntity::SetGrabbedState( bool grabbed ) {
|
||
fl.grabbed = grabbed;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::IsGrabbed
|
||
================
|
||
*/
|
||
bool idEntity::IsGrabbed() {
|
||
return fl.grabbed;
|
||
}
|
||
//rev grab
|
||
|
||
//ivan start - just copied from ff1.1
|
||
|
||
/*
|
||
================
|
||
idEntity::CommonFireProjectile
|
||
================
|
||
*/
|
||
idProjectile* idEntity::CommonFireProjectile( const char *projDefName , const idVec3 &firePos, const idVec3 &dir ) {
|
||
idProjectile *proj;
|
||
idEntity *ent;
|
||
idDict projectileDict;
|
||
|
||
if ( gameLocal.isClient ) { return NULL; }
|
||
|
||
const idDeclEntityDef *projectileDef = gameLocal.FindEntityDef( projDefName, false );
|
||
if ( !projectileDef ) {
|
||
gameLocal.Warning( "No def '%s' found", projDefName );
|
||
return NULL;
|
||
}
|
||
|
||
projectileDict = projectileDef->dict;
|
||
if ( !projectileDict.GetNumKeyVals() ) {
|
||
gameLocal.Warning( "No projectile defined '%s'", projDefName );
|
||
return NULL;
|
||
}
|
||
|
||
if ( IsType( idPlayer::Type ) ) {
|
||
static_cast<idPlayer *>( this )->AddProjectilesFired(1);
|
||
gameLocal.AlertAI( this );
|
||
}
|
||
|
||
gameLocal.SpawnEntityDef( projectileDict, &ent, false );
|
||
|
||
if ( !ent || !ent->IsType( idProjectile::Type ) ) {
|
||
gameLocal.Error( "'%s' is not an idProjectile", projDefName );
|
||
}
|
||
|
||
if ( projectileDict.GetBool( "net_instanthit" ) ) {
|
||
ent->fl.networkSync = false;
|
||
}
|
||
|
||
proj = static_cast<idProjectile *>(ent);
|
||
proj->Create( this, firePos, dir );
|
||
proj->Launch( firePos, dir, vec3_origin );
|
||
return proj;
|
||
}
|
||
|
||
|
||
/*
|
||
=====================
|
||
idEntity::CommonGetAimDir
|
||
=====================
|
||
*/
|
||
void idEntity::CommonGetAimDir( const idVec3 &firePos, idEntity *aimAtEnt, idVec3 &aimDir ) {
|
||
idVec3 mainTargetPos;
|
||
idVec3 secTargetPos; //not used, but necessary
|
||
|
||
if ( aimAtEnt ) { //target entity ok!
|
||
if ( aimAtEnt->IsType( idPlayer::Type ) ) { //player - head
|
||
static_cast<idPlayer *>( aimAtEnt )->GetAIAimTargets( aimAtEnt->GetPhysics()->GetOrigin(), mainTargetPos, secTargetPos );
|
||
} else if ( aimAtEnt->IsType( idActor::Type ) ) { //AI - chest
|
||
static_cast<idActor *>( aimAtEnt )->GetAIAimTargets( aimAtEnt->GetPhysics()->GetOrigin(), secTargetPos, mainTargetPos );
|
||
} else { //center
|
||
mainTargetPos = aimAtEnt->GetPhysics()->GetAbsBounds().GetCenter();
|
||
}
|
||
|
||
aimDir = mainTargetPos - firePos;
|
||
aimDir.Normalize();
|
||
|
||
} else { //no valid aimAtEnt entity!
|
||
if ( IsType( idPlayer::Type ) ) { //player - view
|
||
static_cast<idPlayer *>( this )->viewAngles.ToVectors( &aimDir, NULL, NULL );
|
||
} else if ( IsType( idActor::Type ) ) { //AI - axis
|
||
aimDir = static_cast<idActor *>( this )->viewAxis[ 0 ];
|
||
} else {
|
||
aimDir = GetPhysics()->GetAxis()[ 0 ];
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_FireProjectile
|
||
================
|
||
*/
|
||
void idEntity::Event_FireProjectile( const char* projDefName , const idVec3 &firePos, const idAngles &fireAng ) {
|
||
idProjectile *proj;
|
||
idVec3 dir;
|
||
|
||
dir = fireAng.ToForward();
|
||
proj = CommonFireProjectile( projDefName , firePos, dir );
|
||
|
||
idThread::ReturnEntity( proj );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idEntity::Event_FireProjAtTarget
|
||
================
|
||
*/
|
||
void idEntity::Event_FireProjAtTarget( const char* projDefName , const idVec3 &firePos, idEntity* aimAtEnt) {
|
||
idProjectile *proj;
|
||
idVec3 dir;
|
||
|
||
CommonGetAimDir( firePos, aimAtEnt, dir );
|
||
proj = CommonFireProjectile( projDefName , firePos, dir );
|
||
|
||
idThread::ReturnEntity( proj );
|
||
}
|
||
|
||
//ivan end
|
||
|
||
//ivan start
|
||
/*
|
||
=====================
|
||
idEntity::Event_GetClosestTargetTypePrefix
|
||
=====================
|
||
*/
|
||
void idEntity::Event_GetClosestTargetTypePrefix( const char *typePrefix, const char *ignoreType ) {
|
||
int i;
|
||
float dist;
|
||
float bestDist;
|
||
int prefixLenght;
|
||
int ignoreLenght;
|
||
idEntity *ent;
|
||
idEntity *bestEnt;
|
||
|
||
prefixLenght = idStr::Length( typePrefix );
|
||
ignoreLenght = idStr::Length( ignoreType );
|
||
bestDist = idMath::INFINITY;
|
||
bestEnt = NULL;
|
||
|
||
for( i = 0; i < targets.Num(); i++ ) {
|
||
ent = targets[ i ].GetEntity();
|
||
if( ent ){
|
||
if ( (ignoreLenght > 0) && (idStr::Cmp( ent->GetEntityDefName(), ignoreType ) == 0) ) {
|
||
continue;
|
||
}
|
||
if ( idStr::Cmpn( ent->GetEntityDefName(), typePrefix, prefixLenght ) == 0 ) {
|
||
dist = ( GetPhysics()->GetOrigin() - ent->GetPhysics()->GetOrigin() ).LengthFast();
|
||
if( dist < bestDist ){
|
||
bestDist = dist;
|
||
bestEnt = ent;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
idThread::ReturnEntity( bestEnt );
|
||
}
|
||
|
||
/*
|
||
=====================
|
||
idEntity::Event_GetRandomTargetTypePrefix
|
||
=====================
|
||
*/
|
||
void idEntity::Event_GetRandomTargetTypePrefix( const char *typePrefix, const char *ignoreType ) {
|
||
int i;
|
||
int num;
|
||
int which;
|
||
int prefixLenght;
|
||
int ignoreLenght;
|
||
idEntity *ent;
|
||
idEntity *ents[ MAX_GENTITIES ];
|
||
|
||
prefixLenght = idStr::Length( typePrefix );
|
||
ignoreLenght = idStr::Length( ignoreType );
|
||
|
||
num = 0;
|
||
for( i = 0; i < targets.Num(); i++ ) {
|
||
ent = targets[ i ].GetEntity();
|
||
if( ent ){
|
||
if ( (ignoreLenght > 0) && (idStr::Cmp( ent->GetEntityDefName(), ignoreType ) == 0) ) {
|
||
continue;
|
||
}
|
||
if ( idStr::Cmpn( ent->GetEntityDefName(), typePrefix, prefixLenght ) == 0 ) {
|
||
ents[ num++ ] = ent;
|
||
if ( num >= MAX_GENTITIES ) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( !num ) {
|
||
idThread::ReturnEntity( NULL );
|
||
return;
|
||
}
|
||
|
||
which = gameLocal.random.RandomInt( num );
|
||
idThread::ReturnEntity( ents[ which ] );
|
||
}
|
||
//ivan end
|
||
|
||
/*
|
||
===============================================================================
|
||
|
||
idAnimatedEntity
|
||
|
||
===============================================================================
|
||
*/
|
||
|
||
const idEventDef EV_GetJointHandle( "getJointHandle", "s", 'd' );
|
||
const idEventDef EV_ClearAllJoints( "clearAllJoints" );
|
||
const idEventDef EV_ClearJoint( "clearJoint", "d" );
|
||
const idEventDef EV_SetJointPos( "setJointPos", "ddv" );
|
||
const idEventDef EV_SetJointAngle( "setJointAngle", "ddv" );
|
||
const idEventDef EV_GetJointPos( "getJointPos", "d", 'v' );
|
||
const idEventDef EV_GetJointAngle( "getJointAngle", "d", 'v' );
|
||
//ivan start
|
||
const idEventDef EV_FireProjectileFromJoint( "fireProjectileFromJoint", "sdv", 'e' );
|
||
const idEventDef EV_FireProjAtTargetFromJoint( "fireProjAtTargetFromJoint", "sdE", 'e' ); //E = NULL ok too
|
||
//ivan end
|
||
|
||
CLASS_DECLARATION( idEntity, idAnimatedEntity )
|
||
EVENT( EV_GetJointHandle, idAnimatedEntity::Event_GetJointHandle )
|
||
EVENT( EV_ClearAllJoints, idAnimatedEntity::Event_ClearAllJoints )
|
||
EVENT( EV_ClearJoint, idAnimatedEntity::Event_ClearJoint )
|
||
EVENT( EV_SetJointPos, idAnimatedEntity::Event_SetJointPos )
|
||
EVENT( EV_SetJointAngle, idAnimatedEntity::Event_SetJointAngle )
|
||
EVENT( EV_GetJointPos, idAnimatedEntity::Event_GetJointPos )
|
||
EVENT( EV_GetJointAngle, idAnimatedEntity::Event_GetJointAngle )
|
||
//ivan start
|
||
EVENT( EV_FireProjectileFromJoint, idAnimatedEntity::Event_FireProjectileFromJoint )
|
||
EVENT( EV_FireProjAtTargetFromJoint, idAnimatedEntity::Event_FireProjAtTargetFromJoint )
|
||
//ivan end
|
||
END_CLASS
|
||
|
||
/*
|
||
================
|
||
idAnimatedEntity::idAnimatedEntity
|
||
================
|
||
*/
|
||
idAnimatedEntity::idAnimatedEntity() {
|
||
animator.SetEntity( this );
|
||
damageEffects = NULL;
|
||
nextBloodPoolTime = 0;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idAnimatedEntity::~idAnimatedEntity
|
||
================
|
||
*/
|
||
idAnimatedEntity::~idAnimatedEntity() {
|
||
damageEffect_t *de;
|
||
|
||
for ( de = damageEffects; de; de = damageEffects ) {
|
||
damageEffects = de->next;
|
||
delete de;
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idAnimatedEntity::Save
|
||
|
||
archives object for save game file
|
||
================
|
||
*/
|
||
void idAnimatedEntity::Save( idSaveGame *savefile ) const {
|
||
animator.Save( savefile );
|
||
|
||
// Wounds are very temporary, ignored at this time
|
||
//damageEffect_t *damageEffects;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idAnimatedEntity::Restore
|
||
|
||
unarchives object from save game file
|
||
================
|
||
*/
|
||
void idAnimatedEntity::Restore( idRestoreGame *savefile ) {
|
||
animator.Restore( savefile );
|
||
|
||
// check if the entity has an MD5 model
|
||
if ( animator.ModelHandle() ) {
|
||
// set the callback to update the joints
|
||
renderEntity.callback = idEntity::ModelCallback;
|
||
animator.GetJoints( &renderEntity.numJoints, &renderEntity.joints );
|
||
animator.GetBounds( gameLocal.time, renderEntity.bounds );
|
||
if ( modelDefHandle != -1 ) {
|
||
gameRenderWorld->UpdateEntityDef( modelDefHandle, &renderEntity );
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idAnimatedEntity::ClientPredictionThink
|
||
================
|
||
*/
|
||
void idAnimatedEntity::ClientPredictionThink( void ) {
|
||
RunPhysics();
|
||
UpdateAnimation();
|
||
Present();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idAnimatedEntity::Think
|
||
================
|
||
*/
|
||
void idAnimatedEntity::Think( void ) {
|
||
RunPhysics();
|
||
UpdateAnimation();
|
||
Present();
|
||
|
||
#ifdef _DENTONMOD_ENTITY_CPP
|
||
if (thinkFlags & TH_UPDATEWOUNDPARTICLES)
|
||
#endif
|
||
UpdateDamageEffects();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idAnimatedEntity::UpdateAnimation
|
||
================
|
||
*/
|
||
void idAnimatedEntity::UpdateAnimation( void ) {
|
||
// don't do animations if they're not enabled
|
||
if ( !( thinkFlags & TH_ANIMATE ) ) {
|
||
return;
|
||
}
|
||
|
||
// is the model an MD5?
|
||
if ( !animator.ModelHandle() ) {
|
||
// no, so nothing to do
|
||
return;
|
||
}
|
||
|
||
// call any frame commands that have happened in the past frame
|
||
if ( !fl.hidden ) {
|
||
animator.ServiceAnims( gameLocal.previousTime, gameLocal.time );
|
||
}
|
||
|
||
// if the model is animating then we have to update it
|
||
if ( !animator.FrameHasChanged( gameLocal.time ) ) {
|
||
// still fine the way it was
|
||
return;
|
||
}
|
||
|
||
// get the latest frame bounds
|
||
animator.GetBounds( gameLocal.time, renderEntity.bounds );
|
||
if ( renderEntity.bounds.IsCleared() && !fl.hidden ) {
|
||
gameLocal.DPrintf( "%d: inside out bounds\n", gameLocal.time );
|
||
}
|
||
|
||
// update the renderEntity
|
||
UpdateVisuals();
|
||
|
||
// the animation is updated
|
||
animator.ClearForceUpdate();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idAnimatedEntity::GetAnimator
|
||
================
|
||
*/
|
||
idAnimator *idAnimatedEntity::GetAnimator( void ) {
|
||
return &animator;
|
||
}
|
||
|
||
/*
|
||
================
|
||
idAnimatedEntity::SetModel
|
||
================
|
||
*/
|
||
void idAnimatedEntity::SetModel( const char *modelname ) {
|
||
FreeModelDef();
|
||
|
||
renderEntity.hModel = animator.SetModel( modelname );
|
||
if ( !renderEntity.hModel ) {
|
||
idEntity::SetModel( modelname );
|
||
return;
|
||
}
|
||
|
||
if ( !renderEntity.customSkin ) {
|
||
renderEntity.customSkin = animator.ModelDef()->GetDefaultSkin();
|
||
}
|
||
|
||
// set the callback to update the joints
|
||
renderEntity.callback = idEntity::ModelCallback;
|
||
animator.GetJoints( &renderEntity.numJoints, &renderEntity.joints );
|
||
animator.GetBounds( gameLocal.time, renderEntity.bounds );
|
||
|
||
UpdateVisuals();
|
||
}
|
||
|
||
/*
|
||
=====================
|
||
idAnimatedEntity::GetJointWorldTransform
|
||
=====================
|
||
*/
|
||
bool idAnimatedEntity::GetJointWorldTransform( jointHandle_t jointHandle, int currentTime, idVec3 &offset, idMat3 &axis ) {
|
||
if ( !animator.GetJointTransform( jointHandle, currentTime, offset, axis ) ) {
|
||
return false;
|
||
}
|
||
|
||
ConvertLocalToWorldTransform( offset, axis );
|
||
return true;
|
||
}
|
||
|
||
/*
|
||
==============
|
||
idAnimatedEntity::GetJointTransformForAnim
|
||
==============
|
||
*/
|
||
bool idAnimatedEntity::GetJointTransformForAnim( jointHandle_t jointHandle, int animNum, int frameTime, idVec3 &offset, idMat3 &axis ) const {
|
||
const idAnim *anim;
|
||
int numJoints;
|
||
idJointMat *frame;
|
||
|
||
anim = animator.GetAnim( animNum );
|
||
if ( !anim ) {
|
||
assert( 0 );
|
||
return false;
|
||
}
|
||
|
||
numJoints = animator.NumJoints();
|
||
if ( ( jointHandle < 0 ) || ( jointHandle >= numJoints ) ) {
|
||
assert( 0 );
|
||
return false;
|
||
}
|
||
|
||
frame = ( idJointMat * )_alloca16( numJoints * sizeof( idJointMat ) );
|
||
gameEdit->ANIM_CreateAnimFrame( animator.ModelHandle(), anim->MD5Anim( 0 ), renderEntity.numJoints, frame, frameTime, animator.ModelDef()->GetVisualOffset(), animator.RemoveOrigin() );
|
||
|
||
offset = frame[ jointHandle ].ToVec3();
|
||
axis = frame[ jointHandle ].ToMat3();
|
||
|
||
return true;
|
||
}
|
||
|
||
/*
|
||
==============
|
||
idAnimatedEntity::AddDamageEffect
|
||
|
||
Dammage effects track the animating impact position, spitting out particles.
|
||
==============
|
||
*/
|
||
#ifdef _DENTONMOD
|
||
void idAnimatedEntity::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName, idEntity *soundEnt = NULL ) {
|
||
#else
|
||
void idAnimatedEntity::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName ) {
|
||
#endif
|
||
jointHandle_t jointNum;
|
||
idVec3 dir;
|
||
|
||
if ( !g_bloodEffects.GetBool() || renderEntity.joints == NULL ) {
|
||
return;
|
||
}
|
||
|
||
const idDeclEntityDef *def = gameLocal.FindEntityDef( damageDefName, false );
|
||
if ( def == NULL ) {
|
||
return;
|
||
}
|
||
|
||
jointNum = CLIPMODEL_ID_TO_JOINT_HANDLE( collision.c.id );
|
||
if ( jointNum == INVALID_JOINT ) {
|
||
return;
|
||
}
|
||
|
||
dir = velocity;
|
||
dir.Normalize();
|
||
|
||
#ifdef _DENTONMOD_ENTITY_CPP
|
||
AddLocalDamageEffect( jointNum, collision.c.point, collision.c.normal, dir, def, collision.c.material, soundEnt );
|
||
#else
|
||
idVec3 origin, localDir, localOrigin, localNormal;
|
||
idMat3 axis;
|
||
|
||
axis = renderEntity.joints[jointNum].ToMat3() * renderEntity.axis;
|
||
origin = renderEntity.origin + renderEntity.joints[jointNum].ToVec3() * renderEntity.axis;
|
||
|
||
localOrigin = ( collision.c.point - origin ) * axis.Transpose();
|
||
localNormal = collision.c.normal * axis.Transpose();
|
||
localDir = dir * axis.Transpose();
|
||
|
||
AddLocalDamageEffect( jointNum, localOrigin, localNormal, localDir, def, collision.c.material );
|
||
#endif
|
||
|
||
|
||
if ( gameLocal.isServer ) {
|
||
idBitMsg msg;
|
||
byte msgBuf[MAX_EVENT_PARAM_SIZE];
|
||
|
||
msg.Init( msgBuf, sizeof( msgBuf ) );
|
||
msg.BeginWriting();
|
||
msg.WriteShort( (int)jointNum );
|
||
|
||
#ifdef _DENTONMOD_ENTITY_CPP
|
||
msg.WriteFloat( collision.c.point[0] );
|
||
msg.WriteFloat( collision.c.point[1] );
|
||
msg.WriteFloat( collision.c.point[2] );
|
||
msg.WriteDir( collision.c.normal, 24 );
|
||
msg.WriteDir( dir, 24 );
|
||
#else
|
||
msg.WriteFloat( localOrigin[0] );
|
||
msg.WriteFloat( localOrigin[1] );
|
||
msg.WriteFloat( localOrigin[2] );
|
||
msg.WriteDir( localNormal, 24 );
|
||
msg.WriteDir( localDir, 24 );
|
||
#endif
|
||
msg.WriteInt( gameLocal.ServerRemapDecl( -1, DECL_ENTITYDEF, def->Index() ) );
|
||
msg.WriteInt( gameLocal.ServerRemapDecl( -1, DECL_MATERIAL, collision.c.material->Index() ) );
|
||
ServerSendEvent( EVENT_ADD_DAMAGE_EFFECT, &msg, false, -1 );
|
||
}
|
||
}
|
||
|
||
/*
|
||
==============
|
||
idAnimatedEntity::GetDefaultSurfaceType
|
||
==============
|
||
*/
|
||
int idAnimatedEntity::GetDefaultSurfaceType( void ) const {
|
||
return SURFTYPE_METAL;
|
||
}
|
||
|
||
/*
|
||
==============
|
||
idAnimatedEntity::AddLocalDamageEffect
|
||
==============
|
||
*/
|
||
#ifdef _DENTONMOD_ENTITY_CPP
|
||
void idAnimatedEntity::AddLocalDamageEffect( jointHandle_t jointNum, const idVec3 &origin, const idVec3 &normal, const idVec3 &dir, const idDeclEntityDef *def, const idMaterial *collisionMaterial, idEntity *soundEnt ) {
|
||
#else
|
||
void idAnimatedEntity::AddLocalDamageEffect( jointHandle_t jointNum, const idVec3 &localOrigin, const idVec3 &localNormal, const idVec3 &localDir, const idDeclEntityDef *def, const idMaterial *collisionMaterial ) {
|
||
#endif
|
||
const char *sound, *splat, *decal, *bleed, *key;
|
||
damageEffect_t *de;
|
||
idVec3 gravDir;
|
||
idPhysics *phys;
|
||
|
||
#ifdef _DENTONMOD_ENTITY_CPP
|
||
#else
|
||
idMat3 axis;
|
||
idVec3 origin, dir;
|
||
axis = renderEntity.joints[jointNum].ToMat3() * renderEntity.axis;
|
||
origin = renderEntity.origin + renderEntity.joints[jointNum].ToVec3() * renderEntity.axis;
|
||
|
||
origin = origin + localOrigin * axis;
|
||
dir = localDir * axis;
|
||
#endif
|
||
|
||
int type = collisionMaterial->GetSurfaceType();
|
||
if ( type == SURFTYPE_NONE ) {
|
||
type = GetDefaultSurfaceType();
|
||
}
|
||
|
||
const char *materialType = gameLocal.sufaceTypeNames[ type ];
|
||
|
||
// start impact sound based on material type
|
||
key = va( "snd_%s", materialType );
|
||
sound = spawnArgs.GetString( key );
|
||
if ( *sound == '\0' ) {
|
||
sound = def->dict.GetString( key );
|
||
}
|
||
if ( *sound == '\0' ) {
|
||
sound = def->dict.GetString( "snd_metal" ); // default sound 1
|
||
}
|
||
if ( *sound == '\0' ) {
|
||
sound = def->dict.GetString( "snd_impact" ); // default sound 2
|
||
}
|
||
if ( *sound != '\0' ) {
|
||
#ifdef _DENTONMOD
|
||
if( soundEnt == NULL ) {
|
||
StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_BODY, 0, false, NULL );
|
||
}
|
||
else {
|
||
soundEnt->StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_BODY, 0, false, NULL );
|
||
}
|
||
#else
|
||
StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_BODY, 0, false, NULL );
|
||
#endif
|
||
}
|
||
|
||
// blood splats are thrown onto nearby surfaces
|
||
key = va( "mtr_splat_%s", materialType );
|
||
splat = spawnArgs.RandomPrefix( key, gameLocal.random );
|
||
if ( *splat == '\0' ) {
|
||
splat = def->dict.RandomPrefix( key, gameLocal.random );
|
||
}
|
||
if ( *splat != '\0' ) {
|
||
gameLocal.BloodSplat( origin, dir, 64.0f, splat );
|
||
}
|
||
|
||
|
||
// Create blood pools at feet, Only when alive - By Clone JCD
|
||
if (health > 0){
|
||
int bloodpoolTime;
|
||
|
||
if( gameLocal.time > nextBloodPoolTime ) { // You can use this condition instead :- if (gameLocal.isNewFrame)
|
||
key = va( "mtr_bloodPool_%s", materialType );
|
||
splat = spawnArgs.RandomPrefix( key, gameLocal.random );
|
||
if ( *splat == '\0' ) {
|
||
splat = def->dict.RandomPrefix( key, gameLocal.random );
|
||
}
|
||
if ( *splat != '\0' ) {
|
||
phys = GetPhysics();
|
||
gravDir = phys->GetGravity();
|
||
gravDir.Normalize();
|
||
if( spawnArgs.GetBool("bloodPool_below_origin") ) {
|
||
gameLocal.BloodSplat( phys->GetOrigin(), gravDir, def->dict.GetFloat ( va ("size_bloodPool_%s", materialType), "64.0f"), splat );
|
||
}
|
||
else {
|
||
gameLocal.BloodSplat( origin, gravDir, def->dict.GetFloat ( va ("size_bloodPool_%s", materialType), "64.0f"), splat );
|
||
}
|
||
}
|
||
// This condition makes sure that we dont spawn overlapping bloodpools in a single frame.
|
||
if(!spawnArgs.GetInt( "next_bloodpool_time", "050", bloodpoolTime) ){
|
||
bloodpoolTime = def->dict.GetInt( "next_bloodpool_time", "050");
|
||
}
|
||
nextBloodPoolTime = gameLocal.time + bloodpoolTime; // This avoids excessive bloodpool overlapping
|
||
}
|
||
}
|
||
|
||
// can't see wounds on the player model in single player mode
|
||
if ( !( IsType( idPlayer::Type ) && !gameLocal.isMultiplayer ) ) {
|
||
|
||
|
||
// blood splats can be thrown on the body itself two - By Clone JC Denton
|
||
key = va( "mtr_splatSelf_%s", materialType );
|
||
splat = spawnArgs.RandomPrefix( key, gameLocal.random );
|
||
if ( *splat == '\0' ) {
|
||
splat = def->dict.RandomPrefix( key, gameLocal.random );
|
||
}
|
||
if ( *splat != '\0' ) {
|
||
ProjectOverlay( origin, dir, def->dict.GetFloat ( va ("size_splatSelf_%s", materialType), "15.0f"), splat );
|
||
}
|
||
|
||
// place a wound overlay on the model
|
||
if( g_debugDamage.GetBool() ) {
|
||
gameLocal.Printf("\nCollision Material Type: %s", materialType);
|
||
gameLocal.Printf("\n File: %s", collisionMaterial->GetFileName());
|
||
gameLocal.Printf("\n material: %s", collisionMaterial->ImageName());
|
||
}
|
||
|
||
key = va( "mtr_wound_%s", materialType );
|
||
decal = spawnArgs.RandomPrefix( key, gameLocal.random );
|
||
if ( *decal == '\0' ) {
|
||
decal = def->dict.RandomPrefix( key, gameLocal.random );
|
||
}
|
||
if ( *decal == '\0' ) {
|
||
decal = def->dict.GetString( "mtr_wound" ); // Default decal
|
||
}
|
||
if ( *decal != '\0' ) {
|
||
float size;
|
||
if ( !def->dict.GetFloat( va( "size_wound_%s", materialType ), "6.0", size ) ) { // If Material Specific decal size not found, look for default size
|
||
size = def->dict.GetFloat( "size_wound", "6.0" );
|
||
}
|
||
ProjectOverlay( origin, dir, size, decal );
|
||
}
|
||
}
|
||
|
||
// a blood spurting wound is added
|
||
key = va( "smoke_wound_%s", materialType );
|
||
bleed = spawnArgs.GetString( key );
|
||
if ( *bleed == '\0' ) {
|
||
bleed = def->dict.GetString( key );
|
||
if( *bleed == '\0' )
|
||
bleed = def->dict.GetString( "smoke_wound" ); // play default smoke
|
||
}
|
||
|
||
if ( *bleed != '\0' ) {
|
||
|
||
de = new damageEffect_t;
|
||
de->next = this->damageEffects;
|
||
this->damageEffects = de;
|
||
|
||
de->jointNum = jointNum;
|
||
de->type = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, bleed ) );
|
||
|
||
#ifdef _DENTONMOD_ENTITY_CPP
|
||
idVec3 boneOrigin;
|
||
idMat3 boneAxis;
|
||
|
||
boneAxis = renderEntity.joints[jointNum].ToMat3() * renderEntity.axis;
|
||
boneOrigin = renderEntity.origin + renderEntity.joints[jointNum].ToVec3() * renderEntity.axis;
|
||
|
||
de->localOrigin = ( origin - boneOrigin ) * boneAxis.Transpose();
|
||
de->localNormal = normal * boneAxis.Transpose();
|
||
de->time = -1; // used as flag, notifies UpdateDamageEffects that this effect is just started
|
||
|
||
if (!(thinkFlags & TH_UPDATEWOUNDPARTICLES)) // if flag was not set before set it now
|
||
BecomeActive( TH_UPDATEWOUNDPARTICLES );
|
||
#else
|
||
de->localOrigin = localOrigin;
|
||
de->localNormal = localNormal;
|
||
de->time = gameLocal.time;
|
||
#endif
|
||
|
||
}
|
||
}
|
||
|
||
/*
|
||
==============
|
||
idAnimatedEntity::UpdateDamageEffects
|
||
==============
|
||
*/
|
||
void idAnimatedEntity::UpdateDamageEffects( void ) {
|
||
damageEffect_t *de, **prev;
|
||
|
||
|
||
// free any that have timed out
|
||
prev = &this->damageEffects;
|
||
while ( *prev ) {
|
||
de = *prev;
|
||
if ( de->time == 0 ) { // FIXME:SMOKE
|
||
*prev = de->next;
|
||
delete de;
|
||
} else {
|
||
prev = &de->next;
|
||
}
|
||
}
|
||
|
||
if ( !g_bloodEffects.GetBool() ) {
|
||
return;
|
||
}
|
||
|
||
#ifdef _DENTONMOD_ENTITY_CPP
|
||
if ( !this->damageEffects ){ // If no more particles left...
|
||
if ( thinkFlags & TH_UPDATEWOUNDPARTICLES )
|
||
BecomeInactive( TH_UPDATEWOUNDPARTICLES );
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
// emit a particle for each bleeding wound
|
||
for ( de = this->damageEffects; de; de = de->next ) {
|
||
|
||
#ifdef _DENTONMOD_ENTITY_CPP
|
||
idVec3 origin, dir;
|
||
idMat3 axis;
|
||
|
||
// Sometimes it happens that the original animated model is replace by a particle effect, e.g. on flying lost soul's death.
|
||
// In that case we wont be able to find any joints. So a proper check should be made.
|
||
if( renderEntity.numJoints <= 0 ) {
|
||
axis = renderEntity.axis;
|
||
origin = renderEntity.origin;
|
||
}
|
||
else {
|
||
axis = renderEntity.joints[de->jointNum].ToMat3() * renderEntity.axis;
|
||
origin = renderEntity.origin + renderEntity.joints[de->jointNum].ToVec3() * renderEntity.axis;
|
||
}
|
||
|
||
origin = origin + de->localOrigin * axis;
|
||
dir = de->localNormal * axis;
|
||
|
||
if ( de->time == -1 ) { // initialize start time just before passing it to emitSmoke
|
||
de->time = gameLocal.time;
|
||
}
|
||
if ( !gameLocal.smokeParticles->EmitSmoke( de->type, de->time, gameLocal.random.CRandomFloat(), origin, dir.ToMat3() ) ) {
|
||
#else
|
||
idVec3 origin, start;
|
||
idMat3 axis;
|
||
|
||
animator.GetJointTransform( de->jointNum, gameLocal.time, origin, axis );
|
||
axis *= renderEntity.axis;
|
||
origin = renderEntity.origin + origin * renderEntity.axis;
|
||
start = origin + de->localOrigin * axis;
|
||
|
||
if ( !gameLocal.smokeParticles->EmitSmoke( de->type, de->time, gameLocal.random.CRandomFloat(), start, axis ) ) {
|
||
#endif
|
||
de->time = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
================
|
||
idAnimatedEntity::ClientReceiveEvent
|
||
================
|
||
*/
|
||
bool idAnimatedEntity::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
|
||
int damageDefIndex;
|
||
int materialIndex;
|
||
jointHandle_t jointNum;
|
||
idVec3 localOrigin, localNormal, localDir;
|
||
|
||
switch( event ) {
|
||
case EVENT_ADD_DAMAGE_EFFECT: {
|
||
jointNum = (jointHandle_t) msg.ReadShort();
|
||
localOrigin[0] = msg.ReadFloat();
|
||
localOrigin[1] = msg.ReadFloat();
|
||
localOrigin[2] = msg.ReadFloat();
|
||
localNormal = msg.ReadDir( 24 );
|
||
localDir = msg.ReadDir( 24 );
|
||
damageDefIndex = gameLocal.ClientRemapDecl( DECL_ENTITYDEF, msg.ReadInt() );
|
||
materialIndex = gameLocal.ClientRemapDecl( DECL_MATERIAL, msg.ReadInt() );
|
||
const idDeclEntityDef *damageDef = static_cast<const idDeclEntityDef *>( declManager->DeclByIndex( DECL_ENTITYDEF, damageDefIndex ) );
|
||
const idMaterial *collisionMaterial = static_cast<const idMaterial *>( declManager->DeclByIndex( DECL_MATERIAL, materialIndex ) );
|
||
AddLocalDamageEffect( jointNum, localOrigin, localNormal, localDir, damageDef, collisionMaterial );
|
||
return true;
|
||
}
|
||
default:
|
||
break;
|
||
}
|
||
|
||
return idEntity::ClientReceiveEvent( event, time, msg );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idAnimatedEntity::Event_GetJointHandle
|
||
|
||
looks up the number of the specified joint. returns INVALID_JOINT if the joint is not found.
|
||
================
|
||
*/
|
||
void idAnimatedEntity::Event_GetJointHandle( const char *jointname ) {
|
||
jointHandle_t joint;
|
||
|
||
joint = animator.GetJointHandle( jointname );
|
||
idThread::ReturnInt( joint );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idAnimatedEntity::Event_ClearAllJoints
|
||
|
||
removes any custom transforms on all joints
|
||
================
|
||
*/
|
||
void idAnimatedEntity::Event_ClearAllJoints( void ) {
|
||
animator.ClearAllJoints();
|
||
}
|
||
|
||
/*
|
||
================
|
||
idAnimatedEntity::Event_ClearJoint
|
||
|
||
removes any custom transforms on the specified joint
|
||
================
|
||
*/
|
||
void idAnimatedEntity::Event_ClearJoint( jointHandle_t jointnum ) {
|
||
animator.ClearJoint( jointnum );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idAnimatedEntity::Event_SetJointPos
|
||
|
||
modifies the position of the joint based on the transform type
|
||
================
|
||
*/
|
||
void idAnimatedEntity::Event_SetJointPos( jointHandle_t jointnum, jointModTransform_t transform_type, const idVec3 &pos ) {
|
||
animator.SetJointPos( jointnum, transform_type, pos );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idAnimatedEntity::Event_SetJointAngle
|
||
|
||
modifies the orientation of the joint based on the transform type
|
||
================
|
||
*/
|
||
void idAnimatedEntity::Event_SetJointAngle( jointHandle_t jointnum, jointModTransform_t transform_type, const idAngles &angles ) {
|
||
idMat3 mat;
|
||
|
||
mat = angles.ToMat3();
|
||
animator.SetJointAxis( jointnum, transform_type, mat );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idAnimatedEntity::Event_GetJointPos
|
||
|
||
returns the position of the joint in worldspace
|
||
================
|
||
*/
|
||
void idAnimatedEntity::Event_GetJointPos( jointHandle_t jointnum ) {
|
||
idVec3 offset;
|
||
idMat3 axis;
|
||
|
||
if ( !GetJointWorldTransform( jointnum, gameLocal.time, offset, axis ) ) {
|
||
gameLocal.Warning( "Joint # %d out of range on entity '%s'", jointnum, name.c_str() );
|
||
}
|
||
|
||
idThread::ReturnVector( offset );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idAnimatedEntity::Event_GetJointAngle
|
||
|
||
returns the orientation of the joint in worldspace
|
||
================
|
||
*/
|
||
void idAnimatedEntity::Event_GetJointAngle( jointHandle_t jointnum ) {
|
||
idVec3 offset;
|
||
idMat3 axis;
|
||
|
||
if ( !GetJointWorldTransform( jointnum, gameLocal.time, offset, axis ) ) {
|
||
gameLocal.Warning( "Joint # %d out of range on entity '%s'", jointnum, name.c_str() );
|
||
}
|
||
|
||
idAngles ang = axis.ToAngles();
|
||
idVec3 vec( ang[ 0 ], ang[ 1 ], ang[ 2 ] );
|
||
idThread::ReturnVector( vec );
|
||
}
|
||
|
||
//ivan start
|
||
/*
|
||
================
|
||
idAnimatedEntity::Event_FireProjectileFromJoint
|
||
================
|
||
*/
|
||
void idAnimatedEntity::Event_FireProjectileFromJoint( const char *projDefName, jointHandle_t jointnum, const idAngles &fireAng ) {
|
||
idProjectile *proj;
|
||
idVec3 dir;
|
||
idVec3 firePos;
|
||
idMat3 axis; //useless but needed
|
||
|
||
if ( !GetJointWorldTransform( jointnum, gameLocal.time, firePos, axis ) ) {
|
||
gameLocal.Warning( "Joint # %d out of range on entity '%s'", jointnum, name.c_str() );
|
||
}
|
||
|
||
dir = fireAng.ToForward();
|
||
proj = CommonFireProjectile( projDefName , firePos, dir );
|
||
|
||
idThread::ReturnEntity( proj );
|
||
}
|
||
|
||
/*
|
||
================
|
||
idAnimatedEntity::Event_FireProjAtTargetFromJoint
|
||
================
|
||
*/
|
||
void idAnimatedEntity::Event_FireProjAtTargetFromJoint( const char *projDefName, jointHandle_t jointnum, idEntity *aimAtEnt ) {
|
||
idProjectile *proj;
|
||
idVec3 dir;
|
||
idVec3 firePos;
|
||
idMat3 axis; //useless but needed
|
||
|
||
if ( !GetJointWorldTransform( jointnum, gameLocal.time, firePos, axis ) ) {
|
||
gameLocal.Warning( "Joint # %d out of range on entity '%s'", jointnum, name.c_str() );
|
||
}
|
||
|
||
CommonGetAimDir( firePos, aimAtEnt, dir );
|
||
proj = CommonFireProjectile( projDefName , firePos, dir );
|
||
|
||
idThread::ReturnEntity( proj );
|
||
}
|
||
//ivan end
|
||
|
||
//test:
|
||
/*
|
||
================
|
||
idWeapon::ModelCallback
|
||
================
|
||
|
||
bool idAnimatedEntity::ModelCallback( renderEntity_s *renderEntity, const renderView_t *renderView ) {
|
||
const idWeapon *ent;
|
||
|
||
//gameLocal.Printf("idWeapon::ModelCallback\n");
|
||
ent = static_cast<idWeapon *>(gameLocal.entities[ renderEntity->entityNum ]);
|
||
if ( !ent ) {
|
||
gameLocal.Error( "idBrittleFracture::ModelCallback: callback with NULL game entity" );
|
||
}
|
||
|
||
return ent->UpdateRenderEntity( renderEntity, renderView );
|
||
}
|
||
|
||
bool idAnimatedEntity::UpdateRenderEntity( renderEntity_s *renderEntity, const renderView_t *renderView ) const {
|
||
int i, j, k, n, msec, numTris;
|
||
float fade;
|
||
dword packedColor;
|
||
srfTriangles_t *tris, *decalTris;
|
||
modelSurface_t surface;
|
||
idDrawVert *v;
|
||
idPlane plane;
|
||
idMat3 tangents;
|
||
|
||
//ivan
|
||
const idMaterial * material = declManager->FindMaterial( "textures/decals/irblend" );
|
||
|
||
// this may be triggered by a model trace or other non-view related source,
|
||
// to which we should look like an empty model
|
||
if ( !renderView ) {
|
||
return false;
|
||
}
|
||
|
||
gameLocal.Printf("idAnimatedEntity::UpdateRenderEntity...\n %s\n%s\n%s\n%s\n",
|
||
spawnArgs.GetString( "pointL_old", "0 0 0" ),
|
||
spawnArgs.GetString( "pointH_old", "0 0 0" ),
|
||
spawnArgs.GetString( "pointL_new", "0 0 0" ),
|
||
spawnArgs.GetString( "pointH_new", "0 0 0" )
|
||
);
|
||
|
||
numTris = 2; //1 quadrato = 2 tris
|
||
|
||
// FIXME: re-use model surfaces
|
||
//renderEntity->hModel->InitEmpty( brittleFracture_SnapshotName );
|
||
|
||
// allocate triangle surfaces for the fractures and decals
|
||
tris = renderEntity->hModel->AllocSurfaceTriangles( numTris * 3, material->ShouldCreateBackSides() ? numTris * 6 : numTris * 3 );
|
||
|
||
for ( i = 0; i < 1; i++ ) { //shards.Num()
|
||
//const idVec3 &origin = GetOrigin();
|
||
//const idMat3 &axis = GetAxis();
|
||
|
||
|
||
fade = 1.0f;
|
||
if ( shards[i]->droppedTime >= 0 ) {
|
||
msec = gameLocal.time - shards[i]->droppedTime - SHARD_FADE_START;
|
||
if ( msec > 0 ) {
|
||
fade = 1.0f - (float) msec / ( SHARD_ALIVE_TIME - SHARD_FADE_START );
|
||
}
|
||
}
|
||
packedColor = PackColor( idVec4( renderEntity->shaderParms[ SHADERPARM_RED ] * fade,
|
||
renderEntity->shaderParms[ SHADERPARM_GREEN ] * fade,
|
||
renderEntity->shaderParms[ SHADERPARM_BLUE ] * fade,
|
||
fade ) );
|
||
|
||
packedColor = PackColor( idVec4( 1,1,1,1 ) ); //ivan
|
||
|
||
|
||
//was: const idWinding &winding = shards[i]->winding;
|
||
|
||
//ivan
|
||
idFixedWinding winding;
|
||
winding.Clear();
|
||
winding.AddPoint( spawnArgs.GetVector( "pointL_old", "0 0 0" ));
|
||
winding.AddPoint( spawnArgs.GetVector( "pointH_old", "0 0 0" ));
|
||
winding.AddPoint( spawnArgs.GetVector( "pointL_new", "0 0 0" ));
|
||
winding.AddPoint( spawnArgs.GetVector( "pointH_new", "0 0 0" ));
|
||
//ivan end
|
||
|
||
winding.GetPlane( plane );
|
||
tangents = ( plane.Normal() ).ToMat3(); //was: ( plane.Normal() * axis ).ToMat3();
|
||
|
||
for ( j = 2; j < winding.GetNumPoints(); j++ ) {
|
||
|
||
v = &tris->verts[tris->numVerts++];
|
||
v->Clear();
|
||
v->xyz = winding[0].ToVec3(); //was: origin + winding[0].ToVec3() * axis;
|
||
v->st[0] = winding[0].s;
|
||
v->st[1] = winding[0].t;
|
||
v->normal = tangents[0];
|
||
v->tangents[0] = tangents[1];
|
||
v->tangents[1] = tangents[2];
|
||
v->SetColor( packedColor );
|
||
|
||
v = &tris->verts[tris->numVerts++];
|
||
v->Clear();
|
||
v->xyz = winding[j-1].ToVec3(); //was: origin + winding[j-1].ToVec3() * axis;
|
||
v->st[0] = winding[j-1].s;
|
||
v->st[1] = winding[j-1].t;
|
||
v->normal = tangents[0];
|
||
v->tangents[0] = tangents[1];
|
||
v->tangents[1] = tangents[2];
|
||
v->SetColor( packedColor );
|
||
|
||
v = &tris->verts[tris->numVerts++];
|
||
v->Clear();
|
||
v->xyz = winding[j].ToVec3(); //was: origin + winding[j].ToVec3() * axis;
|
||
v->st[0] = winding[j].s;
|
||
v->st[1] = winding[j].t;
|
||
v->normal = tangents[0];
|
||
v->tangents[0] = tangents[1];
|
||
v->tangents[1] = tangents[2];
|
||
v->SetColor( packedColor );
|
||
|
||
tris->indexes[tris->numIndexes++] = tris->numVerts - 3;
|
||
tris->indexes[tris->numIndexes++] = tris->numVerts - 2;
|
||
tris->indexes[tris->numIndexes++] = tris->numVerts - 1;
|
||
|
||
if ( material->ShouldCreateBackSides() ) {
|
||
|
||
tris->indexes[tris->numIndexes++] = tris->numVerts - 2;
|
||
tris->indexes[tris->numIndexes++] = tris->numVerts - 3;
|
||
tris->indexes[tris->numIndexes++] = tris->numVerts - 1;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
tris->tangentsCalculated = true;
|
||
|
||
SIMDProcessor->MinMax( tris->bounds[0], tris->bounds[1], tris->verts, tris->numVerts );
|
||
|
||
memset( &surface, 0, sizeof( surface ) );
|
||
surface.shader = material;
|
||
surface.id = 0;
|
||
surface.geometry = tris;
|
||
renderEntity->hModel->AddSurface( surface );
|
||
|
||
return true;
|
||
}
|
||
*/
|
||
|
||
//test:
|