PageRenderTime 295ms CodeModel.GetById 51ms app.highlight 182ms RepoModel.GetById 39ms app.codeStats 2ms

/neo/d3xp/Misc.cpp

https://bitbucket.org/peanut64/iodoom3-copy-original
C++ | 3753 lines | 1981 code | 541 blank | 1231 comment | 321 complexity | 83cfe221af36628eda5dd7bb0df35ee0 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2===========================================================================
   3
   4Doom 3 GPL Source Code
   5Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
   6
   7This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
   8
   9Doom 3 Source Code is free software: you can redistribute it and/or modify
  10it under the terms of the GNU General Public License as published by
  11the Free Software Foundation, either version 3 of the License, or
  12(at your option) any later version.
  13
  14Doom 3 Source Code is distributed in the hope that it will be useful,
  15but WITHOUT ANY WARRANTY; without even the implied warranty of
  16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17GNU General Public License for more details.
  18
  19You should have received a copy of the GNU General Public License
  20along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
  21
  22In 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.
  23
  24If 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.
  25
  26===========================================================================
  27*/
  28/*
  29
  30Various utility objects and functions.
  31
  32*/
  33
  34#include "../idlib/precompiled.h"
  35#pragma hdrstop
  36
  37#include "Game_local.h"
  38
  39/*
  40===============================================================================
  41
  42idSpawnableEntity
  43
  44A simple, spawnable entity with a model and no functionable ability of it's own.
  45For example, it can be used as a placeholder during development, for marking
  46locations on maps for script, or for simple placed models without any behavior
  47that can be bound to other entities.  Should not be subclassed.
  48===============================================================================
  49*/
  50
  51CLASS_DECLARATION( idEntity, idSpawnableEntity )
  52END_CLASS
  53
  54/*
  55======================
  56idSpawnableEntity::Spawn
  57======================
  58*/
  59void idSpawnableEntity::Spawn() {
  60	// this just holds dict information
  61}
  62
  63/*
  64===============================================================================
  65
  66	idPlayerStart
  67
  68===============================================================================
  69*/
  70
  71const idEventDef EV_TeleportStage( "<TeleportStage>", "e" );
  72
  73CLASS_DECLARATION( idEntity, idPlayerStart )
  74	EVENT( EV_Activate,			idPlayerStart::Event_TeleportPlayer )
  75	EVENT( EV_TeleportStage,	idPlayerStart::Event_TeleportStage )
  76END_CLASS
  77
  78/*
  79===============
  80idPlayerStart::idPlayerStart
  81================
  82*/
  83idPlayerStart::idPlayerStart( void ) {
  84	teleportStage = 0;
  85}
  86
  87/*
  88===============
  89idPlayerStart::Spawn
  90================
  91*/
  92void idPlayerStart::Spawn( void ) {
  93	teleportStage = 0;
  94}
  95
  96/*
  97================
  98idPlayerStart::Save
  99================
 100*/
 101void idPlayerStart::Save( idSaveGame *savefile ) const {
 102	savefile->WriteInt( teleportStage );
 103}
 104
 105/*
 106================
 107idPlayerStart::Restore
 108================
 109*/
 110void idPlayerStart::Restore( idRestoreGame *savefile ) {
 111	savefile->ReadInt( teleportStage );
 112}
 113
 114/*
 115================
 116idPlayerStart::ClientReceiveEvent
 117================
 118*/
 119bool idPlayerStart::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
 120	int entityNumber;
 121
 122	switch( event ) {
 123		case EVENT_TELEPORTPLAYER: {
 124			entityNumber = msg.ReadBits( GENTITYNUM_BITS );
 125			idPlayer *player = static_cast<idPlayer *>( gameLocal.entities[entityNumber] );
 126			if ( player != NULL && player->IsType( idPlayer::Type ) ) {
 127				Event_TeleportPlayer( player );
 128			}
 129			return true;
 130		}
 131		default: {
 132			return idEntity::ClientReceiveEvent( event, time, msg );
 133		}
 134	}
 135	return false;
 136}
 137
 138/*
 139===============
 140idPlayerStart::Event_TeleportStage
 141
 142FIXME: add functionality to fx system ( could be done with player scripting too )
 143================
 144*/
 145void idPlayerStart::Event_TeleportStage( idEntity *_player ) {
 146	idPlayer *player;
 147	if ( !_player->IsType( idPlayer::Type ) ) {
 148		common->Warning( "idPlayerStart::Event_TeleportStage: entity is not an idPlayer\n" );
 149		return;
 150	}
 151	player = static_cast<idPlayer*>(_player);
 152	float teleportDelay = spawnArgs.GetFloat( "teleportDelay" );
 153	switch ( teleportStage ) {
 154		case 0:
 155			player->playerView.Flash( colorWhite, 125 );
 156			player->SetInfluenceLevel( INFLUENCE_LEVEL3 );
 157			player->SetInfluenceView( spawnArgs.GetString( "mtr_teleportFx" ), NULL, 0.0f, NULL );
 158			gameSoundWorld->FadeSoundClasses( 0, -20.0f, teleportDelay );
 159			player->StartSound( "snd_teleport_start", SND_CHANNEL_BODY2, 0, false, NULL );
 160			teleportStage++;
 161			PostEventSec( &EV_TeleportStage, teleportDelay, player );
 162			break;
 163		case 1:
 164			gameSoundWorld->FadeSoundClasses( 0, 0.0f, 0.25f );
 165			teleportStage++;
 166			PostEventSec( &EV_TeleportStage, 0.25f, player );
 167			break;
 168		case 2:
 169			player->SetInfluenceView( NULL, NULL, 0.0f, NULL );
 170			TeleportPlayer( player );
 171			player->StopSound( SND_CHANNEL_BODY2, false );
 172			player->SetInfluenceLevel( INFLUENCE_NONE );
 173			teleportStage = 0;
 174			break;
 175		default:
 176			break;
 177	}
 178}
 179
 180/*
 181===============
 182idPlayerStart::TeleportPlayer
 183================
 184*/
 185void idPlayerStart::TeleportPlayer( idPlayer *player ) {
 186	float pushVel = spawnArgs.GetFloat( "push", "300" );
 187	float f = spawnArgs.GetFloat( "visualEffect", "0" );
 188	const char *viewName = spawnArgs.GetString( "visualView", "" );
 189	idEntity *ent = viewName ? gameLocal.FindEntity( viewName ) : NULL;
 190
 191#ifdef _D3XP
 192	SetTimeState ts( player->timeGroup );
 193#endif
 194
 195	if ( f && ent ) {
 196		// place in private camera view for some time
 197		// the entity needs to teleport to where the camera view is to have the PVS right
 198		player->Teleport( ent->GetPhysics()->GetOrigin(), ang_zero, this );
 199		player->StartSound( "snd_teleport_enter", SND_CHANNEL_ANY, 0, false, NULL );
 200		player->SetPrivateCameraView( static_cast<idCamera*>(ent) );
 201		// the player entity knows where to spawn from the previous Teleport call
 202		if ( !gameLocal.isClient ) {
 203			player->PostEventSec( &EV_Player_ExitTeleporter, f );
 204		}
 205	} else {
 206		// direct to exit, Teleport will take care of the killbox
 207		player->Teleport( GetPhysics()->GetOrigin(), GetPhysics()->GetAxis().ToAngles(), NULL );
 208
 209		// multiplayer hijacked this entity, so only push the player in multiplayer
 210		if ( gameLocal.isMultiplayer ) {
 211			player->GetPhysics()->SetLinearVelocity( GetPhysics()->GetAxis()[0] * pushVel );
 212		}
 213	}
 214}
 215
 216/*
 217===============
 218idPlayerStart::Event_TeleportPlayer
 219================
 220*/
 221void idPlayerStart::Event_TeleportPlayer( idEntity *activator ) {
 222	idPlayer *player;
 223
 224	if ( activator->IsType( idPlayer::Type ) ) {
 225		player = static_cast<idPlayer*>( activator );
 226	} else {
 227		player = gameLocal.GetLocalPlayer();
 228	}
 229	if ( player ) {
 230		if ( spawnArgs.GetBool( "visualFx" ) ) {
 231
 232			teleportStage = 0;
 233			Event_TeleportStage( player );
 234
 235		} else {
 236
 237			if ( gameLocal.isServer ) {
 238				idBitMsg	msg;
 239				byte		msgBuf[MAX_EVENT_PARAM_SIZE];
 240
 241				msg.Init( msgBuf, sizeof( msgBuf ) );
 242				msg.BeginWriting();
 243				msg.WriteBits( player->entityNumber, GENTITYNUM_BITS );
 244				ServerSendEvent( EVENT_TELEPORTPLAYER, &msg, false, -1 );
 245			}
 246
 247			TeleportPlayer( player );
 248		}
 249	}
 250}
 251
 252/*
 253===============================================================================
 254
 255	idActivator
 256
 257===============================================================================
 258*/
 259
 260CLASS_DECLARATION( idEntity, idActivator )
 261	EVENT( EV_Activate,		idActivator::Event_Activate )
 262END_CLASS
 263
 264/*
 265===============
 266idActivator::Save
 267================
 268*/
 269void idActivator::Save( idSaveGame *savefile ) const {
 270	savefile->WriteBool( stay_on );
 271}
 272
 273/*
 274===============
 275idActivator::Restore
 276================
 277*/
 278void idActivator::Restore( idRestoreGame *savefile ) {
 279	savefile->ReadBool( stay_on );
 280
 281	if ( stay_on ) {
 282		BecomeActive( TH_THINK );
 283	}
 284}
 285
 286/*
 287===============
 288idActivator::Spawn
 289================
 290*/
 291void idActivator::Spawn( void ) {
 292	bool start_off;
 293
 294	spawnArgs.GetBool( "stay_on", "0", stay_on );
 295	spawnArgs.GetBool( "start_off", "0", start_off );
 296
 297	GetPhysics()->SetClipBox( idBounds( vec3_origin ).Expand( 4 ), 1.0f );
 298	GetPhysics()->SetContents( 0 );
 299
 300	if ( !start_off ) {
 301		BecomeActive( TH_THINK );
 302	}
 303}
 304
 305/*
 306===============
 307idActivator::Think
 308================
 309*/
 310void idActivator::Think( void ) {
 311	RunPhysics();
 312	if ( thinkFlags & TH_THINK ) {
 313		if ( TouchTriggers() ) {
 314			if ( !stay_on ) {
 315				BecomeInactive( TH_THINK );
 316			}
 317		}
 318	}
 319	Present();
 320}
 321
 322/*
 323===============
 324idActivator::Activate
 325================
 326*/
 327void idActivator::Event_Activate( idEntity *activator ) {
 328	if ( thinkFlags & TH_THINK ) {
 329		BecomeInactive( TH_THINK );
 330	} else {
 331		BecomeActive( TH_THINK );
 332	}
 333}
 334
 335
 336/*
 337===============================================================================
 338
 339idPathCorner
 340
 341===============================================================================
 342*/
 343
 344CLASS_DECLARATION( idEntity, idPathCorner )
 345	EVENT( AI_RandomPath,		idPathCorner::Event_RandomPath )
 346END_CLASS
 347
 348/*
 349=====================
 350idPathCorner::Spawn
 351=====================
 352*/
 353void idPathCorner::Spawn( void ) {
 354}
 355
 356/*
 357=====================
 358idPathCorner::DrawDebugInfo
 359=====================
 360*/
 361void idPathCorner::DrawDebugInfo( void ) {
 362	idEntity *ent;
 363	idBounds bnds( idVec3( -4.0, -4.0f, -8.0f ), idVec3( 4.0, 4.0f, 64.0f ) );
 364
 365	for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
 366		if ( !ent->IsType( idPathCorner::Type ) ) {
 367			continue;
 368		}
 369
 370		idVec3 org = ent->GetPhysics()->GetOrigin();
 371		gameRenderWorld->DebugBounds( colorRed, bnds, org, 0 );
 372	}
 373}
 374
 375/*
 376============
 377idPathCorner::RandomPath
 378============
 379*/
 380idPathCorner *idPathCorner::RandomPath( const idEntity *source, const idEntity *ignore ) {
 381	int	i;
 382	int	num;
 383	int which;
 384	idEntity *ent;
 385	idPathCorner *path[ MAX_GENTITIES ];
 386
 387	num = 0;
 388	for( i = 0; i < source->targets.Num(); i++ ) {
 389		ent = source->targets[ i ].GetEntity();
 390		if ( ent && ( ent != ignore ) && ent->IsType( idPathCorner::Type ) ) {
 391			path[ num++ ] = static_cast<idPathCorner *>( ent );
 392			if ( num >= MAX_GENTITIES ) {
 393				break;
 394			}
 395		}
 396	}
 397
 398	if ( !num ) {
 399		return NULL;
 400	}
 401
 402	which = gameLocal.random.RandomInt( num );
 403	return path[ which ];
 404}
 405
 406/*
 407=====================
 408idPathCorner::Event_RandomPath
 409=====================
 410*/
 411void idPathCorner::Event_RandomPath( void ) {
 412	idPathCorner *path;
 413
 414	path = RandomPath( this, NULL );
 415	idThread::ReturnEntity( path );
 416}
 417
 418/*
 419===============================================================================
 420
 421  idDamagable
 422	
 423===============================================================================
 424*/
 425
 426const idEventDef EV_RestoreDamagable( "<RestoreDamagable>" );
 427
 428CLASS_DECLARATION( idEntity, idDamagable )
 429	EVENT( EV_Activate,			idDamagable::Event_BecomeBroken )
 430	EVENT( EV_RestoreDamagable,	idDamagable::Event_RestoreDamagable )
 431END_CLASS
 432
 433/*
 434================
 435idDamagable::idDamagable
 436================
 437*/
 438idDamagable::idDamagable( void ) {
 439	count = 0;
 440	nextTriggerTime = 0;
 441}
 442
 443/*
 444================
 445idDamagable::Save
 446================
 447*/
 448void idDamagable::Save( idSaveGame *savefile ) const {
 449	savefile->WriteInt( count );
 450	savefile->WriteInt( nextTriggerTime );
 451}
 452
 453/*
 454================
 455idDamagable::Restore
 456================
 457*/
 458void idDamagable::Restore( idRestoreGame *savefile ) {
 459	savefile->ReadInt( count );
 460	savefile->ReadInt( nextTriggerTime );
 461}
 462
 463/*
 464================
 465idDamagable::Spawn
 466================
 467*/
 468void idDamagable::Spawn( void ) {
 469	idStr broken;
 470
 471	health = spawnArgs.GetInt( "health", "5" );
 472	spawnArgs.GetInt( "count", "1", count );	
 473	nextTriggerTime = 0;
 474	
 475	// make sure the model gets cached
 476	spawnArgs.GetString( "broken", "", broken );
 477	if ( broken.Length() && !renderModelManager->CheckModel( broken ) ) {
 478		gameLocal.Error( "idDamagable '%s' at (%s): cannot load broken model '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), broken.c_str() );
 479	}
 480
 481	fl.takedamage = true;
 482	GetPhysics()->SetContents( CONTENTS_SOLID );
 483}
 484
 485/*
 486================
 487idDamagable::BecomeBroken
 488================
 489*/
 490void idDamagable::BecomeBroken( idEntity *activator ) {
 491	float	forceState;
 492	int		numStates;
 493	int		cycle;
 494	float	wait;
 495	
 496	if ( gameLocal.time < nextTriggerTime ) {
 497		return;
 498	}
 499
 500	spawnArgs.GetFloat( "wait", "0.1", wait );
 501	nextTriggerTime = gameLocal.time + SEC2MS( wait );
 502	if ( count > 0 ) {
 503		count--;
 504		if ( !count ) {
 505			fl.takedamage = false;
 506		} else {
 507			health = spawnArgs.GetInt( "health", "5" );
 508		}
 509	}
 510
 511	idStr	broken;
 512
 513	spawnArgs.GetString( "broken", "", broken );
 514	if ( broken.Length() ) {
 515		SetModel( broken );
 516	}
 517
 518	// offset the start time of the shader to sync it to the gameLocal time
 519	renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
 520
 521	spawnArgs.GetInt( "numstates", "1", numStates );
 522	spawnArgs.GetInt( "cycle", "0", cycle );
 523	spawnArgs.GetFloat( "forcestate", "0", forceState );
 524
 525	// set the state parm
 526	if ( cycle ) {
 527		renderEntity.shaderParms[ SHADERPARM_MODE ]++;
 528		if ( renderEntity.shaderParms[ SHADERPARM_MODE ] > numStates ) {
 529			renderEntity.shaderParms[ SHADERPARM_MODE ] = 0;
 530		}
 531	} else if ( forceState ) {
 532		renderEntity.shaderParms[ SHADERPARM_MODE ] = forceState;
 533	} else {
 534		renderEntity.shaderParms[ SHADERPARM_MODE ] = gameLocal.random.RandomInt( numStates ) + 1;
 535	}
 536
 537	renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
 538
 539	ActivateTargets( activator );
 540
 541	if ( spawnArgs.GetBool( "hideWhenBroken" ) ) {
 542		Hide();
 543		PostEventMS( &EV_RestoreDamagable, nextTriggerTime - gameLocal.time );
 544		BecomeActive( TH_THINK );
 545	}
 546}
 547
 548/*
 549================
 550idDamagable::Killed
 551================
 552*/
 553void idDamagable::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
 554	if ( gameLocal.time < nextTriggerTime ) {
 555		health += damage;
 556		return;
 557	}
 558
 559	BecomeBroken( attacker );
 560}
 561
 562#ifdef _D3XP
 563/*
 564================
 565idDamagable::Hide
 566================
 567*/
 568void idDamagable::Hide( void ) {
 569	idEntity::Hide();
 570	GetPhysics()->SetContents( 0 );
 571}
 572
 573/*
 574================
 575idDamagable::Show
 576================
 577*/
 578void idDamagable::Show( void ) {
 579	idEntity::Show();
 580	GetPhysics()->SetContents( CONTENTS_SOLID );
 581}
 582#endif
 583
 584/*
 585================
 586idDamagable::Event_BecomeBroken
 587================
 588*/
 589void idDamagable::Event_BecomeBroken( idEntity *activator ) {
 590	BecomeBroken( activator );
 591}
 592
 593/*
 594================
 595idDamagable::Event_RestoreDamagable
 596================
 597*/
 598void idDamagable::Event_RestoreDamagable( void ) {
 599	health = spawnArgs.GetInt( "health", "5" );
 600	Show();
 601}
 602
 603
 604/*
 605===============================================================================
 606
 607  idExplodable
 608	
 609===============================================================================
 610*/
 611
 612CLASS_DECLARATION( idEntity, idExplodable )
 613	EVENT( EV_Activate,	idExplodable::Event_Explode )
 614END_CLASS
 615
 616/*
 617================
 618idExplodable::Spawn
 619================
 620*/
 621void idExplodable::Spawn( void ) {
 622	Hide();
 623}
 624
 625/*
 626================
 627idExplodable::Event_Explode
 628================
 629*/
 630void idExplodable::Event_Explode( idEntity *activator ) {
 631	const char *temp;
 632
 633	if ( spawnArgs.GetString( "def_damage", "damage_explosion", &temp ) ) {
 634		gameLocal.RadiusDamage( GetPhysics()->GetOrigin(), activator, activator, this, this, temp );
 635	}
 636
 637	StartSound( "snd_explode", SND_CHANNEL_ANY, 0, false, NULL );
 638
 639	// Show() calls UpdateVisuals, so we don't need to call it ourselves after setting the shaderParms
 640	renderEntity.shaderParms[SHADERPARM_RED]		= 1.0f;
 641	renderEntity.shaderParms[SHADERPARM_GREEN]		= 1.0f;
 642	renderEntity.shaderParms[SHADERPARM_BLUE]		= 1.0f;
 643	renderEntity.shaderParms[SHADERPARM_ALPHA]		= 1.0f;
 644	renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC( gameLocal.time );
 645	renderEntity.shaderParms[SHADERPARM_DIVERSITY]	= 0.0f;
 646	Show();
 647
 648	PostEventMS( &EV_Remove, 2000 );
 649
 650	ActivateTargets( activator );
 651}
 652
 653
 654/*
 655===============================================================================
 656
 657  idSpring
 658	
 659===============================================================================
 660*/
 661
 662CLASS_DECLARATION( idEntity, idSpring )
 663	EVENT( EV_PostSpawn,	idSpring::Event_LinkSpring )
 664END_CLASS
 665
 666/*
 667================
 668idSpring::Think
 669================
 670*/
 671void idSpring::Think( void ) {
 672	idVec3 start, end, origin;
 673	idMat3 axis;
 674
 675	// run physics
 676	RunPhysics();
 677
 678	if ( thinkFlags & TH_THINK ) {
 679		// evaluate force
 680		spring.Evaluate( gameLocal.time );
 681
 682		start = p1;
 683		if ( ent1->GetPhysics() ) {
 684			axis = ent1->GetPhysics()->GetAxis();
 685			origin = ent1->GetPhysics()->GetOrigin();
 686			start = origin + start * axis;
 687		}
 688
 689		end = p2;
 690		if ( ent2->GetPhysics() ) {
 691			axis = ent2->GetPhysics()->GetAxis();
 692			origin = ent2->GetPhysics()->GetOrigin();
 693			end = origin + p2 * axis;
 694		}
 695		
 696		gameRenderWorld->DebugLine( idVec4(1, 1, 0, 1), start, end, 0, true );
 697	}
 698
 699	Present();
 700}
 701
 702/*
 703================
 704idSpring::Event_LinkSpring
 705================
 706*/
 707void idSpring::Event_LinkSpring( void ) {
 708	idStr name1, name2;
 709
 710	spawnArgs.GetString( "ent1", "", name1 );
 711	spawnArgs.GetString( "ent2", "", name2 );
 712
 713	if ( name1.Length() ) {
 714		ent1 = gameLocal.FindEntity( name1 );
 715		if ( !ent1 ) {
 716			gameLocal.Error( "idSpring '%s' at (%s): cannot find first entity '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), name1.c_str() );
 717		}
 718	}
 719	else {
 720		ent1 = gameLocal.entities[ENTITYNUM_WORLD];
 721	}
 722
 723	if ( name2.Length() ) {
 724		ent2 = gameLocal.FindEntity( name2 );
 725		if ( !ent2 ) {
 726			gameLocal.Error( "idSpring '%s' at (%s): cannot find second entity '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), name2.c_str() );
 727		}
 728	}
 729	else {
 730		ent2 = gameLocal.entities[ENTITYNUM_WORLD];
 731	}
 732	spring.SetPosition( ent1->GetPhysics(), id1, p1, ent2->GetPhysics(), id2, p2 );
 733	BecomeActive( TH_THINK );
 734}
 735
 736/*
 737================
 738idSpring::Spawn
 739================
 740*/
 741void idSpring::Spawn( void ) {
 742	float Kstretch, damping, restLength;
 743
 744	spawnArgs.GetInt( "id1", "0", id1 );
 745	spawnArgs.GetInt( "id2", "0", id2 );
 746	spawnArgs.GetVector( "point1", "0 0 0", p1 );
 747	spawnArgs.GetVector( "point2", "0 0 0", p2 );
 748	spawnArgs.GetFloat( "constant", "100.0f", Kstretch );
 749	spawnArgs.GetFloat( "damping", "10.0f", damping );
 750	spawnArgs.GetFloat( "restlength", "0.0f", restLength );
 751
 752	spring.InitSpring( Kstretch, 0.0f, damping, restLength );
 753
 754	ent1 = ent2 = NULL;
 755
 756	PostEventMS( &EV_PostSpawn, 0 );
 757}
 758
 759/*
 760===============================================================================
 761
 762  idForceField
 763	
 764===============================================================================
 765*/
 766
 767const idEventDef EV_Toggle( "Toggle", NULL );
 768
 769CLASS_DECLARATION( idEntity, idForceField )
 770	EVENT( EV_Activate,		idForceField::Event_Activate )
 771	EVENT( EV_Toggle,		idForceField::Event_Toggle )
 772	EVENT( EV_FindTargets,	idForceField::Event_FindTargets )
 773END_CLASS
 774
 775/*
 776===============
 777idForceField::Toggle
 778================
 779*/
 780void idForceField::Toggle( void ) {
 781	if ( thinkFlags & TH_THINK ) {
 782		BecomeInactive( TH_THINK );
 783	} else {
 784		BecomeActive( TH_THINK );
 785	}
 786}
 787
 788/*
 789================
 790idForceField::Think
 791================
 792*/
 793void idForceField::Think( void ) {
 794	if ( thinkFlags & TH_THINK ) {
 795		// evaluate force
 796		forceField.Evaluate( gameLocal.time );
 797	}
 798	Present();
 799}
 800
 801/*
 802================
 803idForceField::Save
 804================
 805*/
 806void idForceField::Save( idSaveGame *savefile ) const {
 807	savefile->WriteStaticObject( forceField );
 808}
 809
 810/*
 811================
 812idForceField::Restore
 813================
 814*/
 815void idForceField::Restore( idRestoreGame *savefile ) {
 816	savefile->ReadStaticObject( forceField );
 817}
 818
 819/*
 820================
 821idForceField::Spawn
 822================
 823*/
 824void idForceField::Spawn( void ) {
 825	idVec3 uniform;
 826	float explosion, implosion, randomTorque;
 827
 828	if ( spawnArgs.GetVector( "uniform", "0 0 0", uniform ) ) {
 829		forceField.Uniform( uniform );
 830	} else if ( spawnArgs.GetFloat( "explosion", "0", explosion ) ) {
 831		forceField.Explosion( explosion );
 832	} else if ( spawnArgs.GetFloat( "implosion", "0", implosion ) ) {
 833		forceField.Implosion( implosion );
 834	}
 835
 836	if ( spawnArgs.GetFloat( "randomTorque", "0", randomTorque ) ) {
 837		forceField.RandomTorque( randomTorque );
 838	}
 839
 840	if ( spawnArgs.GetBool( "applyForce", "0" ) ) {
 841		forceField.SetApplyType( FORCEFIELD_APPLY_FORCE );
 842	} else if ( spawnArgs.GetBool( "applyImpulse", "0" ) ) {
 843		forceField.SetApplyType( FORCEFIELD_APPLY_IMPULSE );
 844	} else {
 845		forceField.SetApplyType( FORCEFIELD_APPLY_VELOCITY );
 846	}
 847
 848	forceField.SetPlayerOnly( spawnArgs.GetBool( "playerOnly", "0" ) );
 849	forceField.SetMonsterOnly( spawnArgs.GetBool( "monsterOnly", "0" ) );
 850
 851	// set the collision model on the force field
 852	forceField.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ) );
 853
 854	// remove the collision model from the physics object
 855	GetPhysics()->SetClipModel( NULL, 1.0f );
 856
 857	if ( spawnArgs.GetBool( "start_on" ) ) {
 858		BecomeActive( TH_THINK );
 859	}
 860}
 861
 862/*
 863===============
 864idForceField::Event_Toggle
 865================
 866*/
 867void idForceField::Event_Toggle( void ) {
 868	Toggle();
 869}
 870
 871/*
 872================
 873idForceField::Event_Activate
 874================
 875*/
 876void idForceField::Event_Activate( idEntity *activator ) {
 877	float wait;
 878
 879	Toggle();
 880	if ( spawnArgs.GetFloat( "wait", "0.01", wait ) ) {
 881		PostEventSec( &EV_Toggle, wait );
 882	}
 883}
 884
 885/*
 886================
 887idForceField::Event_FindTargets
 888================
 889*/
 890void idForceField::Event_FindTargets( void ) {
 891	FindTargets();
 892	RemoveNullTargets();
 893	if ( targets.Num() ) {
 894		forceField.Uniform( targets[0].GetEntity()->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin() );
 895	}
 896}
 897
 898
 899/*
 900===============================================================================
 901
 902	idAnimated
 903
 904===============================================================================
 905*/
 906
 907const idEventDef EV_Animated_Start( "<start>" );
 908const idEventDef EV_LaunchMissiles( "launchMissiles", "ssssdf" );
 909const idEventDef EV_LaunchMissilesUpdate( "<launchMissiles>", "dddd" );
 910const idEventDef EV_AnimDone( "<AnimDone>", "d" );
 911const idEventDef EV_StartRagdoll( "startRagdoll" );
 912#ifdef _D3XP
 913const idEventDef EV_SetAnimation( "setAnimation", "s" );
 914const idEventDef EV_GetAnimationLength( "getAnimationLength", NULL, 'f' );
 915#endif
 916
 917CLASS_DECLARATION( idAFEntity_Gibbable, idAnimated )
 918	EVENT( EV_Activate,				idAnimated::Event_Activate )
 919	EVENT( EV_Animated_Start,		idAnimated::Event_Start )
 920	EVENT( EV_StartRagdoll,			idAnimated::Event_StartRagdoll )
 921	EVENT( EV_AnimDone,				idAnimated::Event_AnimDone )
 922	EVENT( EV_Footstep,				idAnimated::Event_Footstep )
 923	EVENT( EV_FootstepLeft,			idAnimated::Event_Footstep )
 924	EVENT( EV_FootstepRight,		idAnimated::Event_Footstep )
 925	EVENT( EV_LaunchMissiles,		idAnimated::Event_LaunchMissiles )
 926	EVENT( EV_LaunchMissilesUpdate,	idAnimated::Event_LaunchMissilesUpdate )
 927#ifdef _D3XP
 928	EVENT( EV_SetAnimation,			idAnimated::Event_SetAnimation )
 929	EVENT( EV_GetAnimationLength,	idAnimated::Event_GetAnimationLength )
 930#endif
 931END_CLASS
 932
 933/*
 934===============
 935idAnimated::idAnimated
 936================
 937*/
 938idAnimated::idAnimated() {
 939	anim = 0;
 940	blendFrames = 0;
 941	soundJoint = INVALID_JOINT;
 942	activated = false;
 943	combatModel = NULL;
 944	activator = NULL;
 945	current_anim_index = 0;
 946	num_anims = 0;
 947
 948}
 949
 950/*
 951===============
 952idAnimated::idAnimated
 953================
 954*/
 955idAnimated::~idAnimated() {
 956	delete combatModel;
 957	combatModel = NULL;
 958}
 959
 960/*
 961===============
 962idAnimated::Save
 963================
 964*/
 965void idAnimated::Save( idSaveGame *savefile ) const {
 966	savefile->WriteInt( current_anim_index );
 967	savefile->WriteInt( num_anims );
 968	savefile->WriteInt( anim );
 969	savefile->WriteInt( blendFrames );
 970	savefile->WriteJoint( soundJoint );
 971	activator.Save( savefile );
 972	savefile->WriteBool( activated );
 973}
 974
 975/*
 976===============
 977idAnimated::Restore
 978================
 979*/
 980void idAnimated::Restore( idRestoreGame *savefile ) {
 981	savefile->ReadInt( current_anim_index );
 982	savefile->ReadInt( num_anims );
 983	savefile->ReadInt( anim );
 984	savefile->ReadInt( blendFrames );
 985	savefile->ReadJoint( soundJoint );
 986	activator.Restore( savefile );
 987	savefile->ReadBool( activated );
 988}
 989
 990/*
 991===============
 992idAnimated::Spawn
 993================
 994*/
 995void idAnimated::Spawn( void ) {
 996	idStr		animname;
 997	int			anim2;
 998	float		wait;
 999	const char	*joint;
1000
1001	joint = spawnArgs.GetString( "sound_bone", "origin" ); 
1002	soundJoint = animator.GetJointHandle( joint );
1003	if ( soundJoint == INVALID_JOINT ) {
1004		gameLocal.Warning( "idAnimated '%s' at (%s): cannot find joint '%s' for sound playback", name.c_str(), GetPhysics()->GetOrigin().ToString(0), joint );
1005	}
1006
1007	LoadAF();
1008
1009	// allow bullets to collide with a combat model
1010	if ( spawnArgs.GetBool( "combatModel", "0" ) ) {
1011		combatModel = new idClipModel( modelDefHandle );
1012	}
1013
1014	// allow the entity to take damage
1015	if ( spawnArgs.GetBool( "takeDamage", "0" ) ) {
1016		fl.takedamage = true;
1017	}
1018
1019	blendFrames = 0;
1020
1021	current_anim_index = 0;
1022	spawnArgs.GetInt( "num_anims", "0", num_anims );
1023
1024	blendFrames = spawnArgs.GetInt( "blend_in" );
1025
1026	animname = spawnArgs.GetString( num_anims ? "anim1" : "anim" );
1027	if ( !animname.Length() ) {
1028		anim = 0;
1029	} else {
1030		anim = animator.GetAnim( animname );
1031		if ( !anim ) {
1032			gameLocal.Error( "idAnimated '%s' at (%s): cannot find anim '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), animname.c_str() );
1033		}
1034	}
1035
1036	if ( spawnArgs.GetBool( "hide" ) ) {
1037		Hide();
1038
1039		if ( !num_anims ) {
1040			blendFrames = 0;
1041		}
1042	} else if ( spawnArgs.GetString( "start_anim", "", animname ) ) {
1043		anim2 = animator.GetAnim( animname );
1044		if ( !anim2 ) {
1045			gameLocal.Error( "idAnimated '%s' at (%s): cannot find anim '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), animname.c_str() );
1046		}
1047		animator.CycleAnim( ANIMCHANNEL_ALL, anim2, gameLocal.time, 0 );
1048	} else if ( anim ) {
1049		// init joints to the first frame of the animation
1050		animator.SetFrame( ANIMCHANNEL_ALL, anim, 1, gameLocal.time, 0 );		
1051
1052		if ( !num_anims ) {
1053			blendFrames = 0;
1054		}
1055	}
1056
1057	spawnArgs.GetFloat( "wait", "-1", wait );
1058
1059	if ( wait >= 0 ) {
1060		PostEventSec( &EV_Activate, wait, this );
1061	}
1062}
1063
1064/*
1065===============
1066idAnimated::LoadAF
1067===============
1068*/
1069bool idAnimated::LoadAF( void ) {
1070	idStr fileName;
1071
1072	if ( !spawnArgs.GetString( "ragdoll", "*unknown*", fileName ) ) {
1073		return false;
1074	}
1075	af.SetAnimator( GetAnimator() );
1076	return af.Load( this, fileName );
1077}
1078
1079/*
1080===============
1081idAnimated::GetPhysicsToSoundTransform
1082===============
1083*/
1084bool idAnimated::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
1085	animator.GetJointTransform( soundJoint, gameLocal.time, origin, axis );
1086	axis = renderEntity.axis;
1087	return true;
1088}
1089
1090/*
1091================
1092idAnimated::StartRagdoll
1093================
1094*/
1095bool idAnimated::StartRagdoll( void ) {
1096	// if no AF loaded
1097	if ( !af.IsLoaded() ) {
1098		return false;
1099	}
1100
1101	// if the AF is already active
1102	if ( af.IsActive() ) {
1103		return true;
1104	}
1105
1106	// disable any collision model used
1107	GetPhysics()->DisableClip();
1108
1109	// start using the AF
1110	af.StartFromCurrentPose( spawnArgs.GetInt( "velocityTime", "0" ) );
1111	
1112	return true;
1113}
1114
1115/*
1116=====================
1117idAnimated::PlayNextAnim
1118=====================
1119*/
1120void idAnimated::PlayNextAnim( void ) {
1121	const char *animname;
1122	int len;
1123	int cycle;
1124
1125	if ( current_anim_index >= num_anims ) {
1126		Hide();
1127		if ( spawnArgs.GetBool( "remove" ) ) {
1128			PostEventMS( &EV_Remove, 0 );
1129		} else {
1130			current_anim_index = 0;
1131		}
1132		return;
1133	}
1134
1135	Show();
1136	current_anim_index++;
1137
1138	spawnArgs.GetString( va( "anim%d", current_anim_index ), NULL, &animname );
1139	if ( !animname ) {
1140		anim = 0;
1141		animator.Clear( ANIMCHANNEL_ALL, gameLocal.time, FRAME2MS( blendFrames ) );
1142		return;
1143	}
1144
1145	anim = animator.GetAnim( animname );
1146	if ( !anim ) {
1147		gameLocal.Warning( "missing anim '%s' on %s", animname, name.c_str() );
1148		return;
1149	}
1150
1151	if ( g_debugCinematic.GetBool() ) {
1152		gameLocal.Printf( "%d: '%s' start anim '%s'\n", gameLocal.framenum, GetName(), animname );
1153	}
1154		
1155	spawnArgs.GetInt( "cycle", "1", cycle );
1156	if ( ( current_anim_index == num_anims ) && spawnArgs.GetBool( "loop_last_anim" ) ) {
1157		cycle = -1;
1158	}
1159
1160	animator.CycleAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, FRAME2MS( blendFrames ) );
1161	animator.CurrentAnim( ANIMCHANNEL_ALL )->SetCycleCount( cycle );
1162
1163	len = animator.CurrentAnim( ANIMCHANNEL_ALL )->PlayLength();
1164	if ( len >= 0 ) {
1165		PostEventMS( &EV_AnimDone, len, current_anim_index );
1166	}
1167
1168	// offset the start time of the shader to sync it to the game time
1169	renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
1170
1171	animator.ForceUpdate();
1172	UpdateAnimation();
1173	UpdateVisuals();
1174	Present();
1175}
1176
1177/*
1178===============
1179idAnimated::Event_StartRagdoll
1180================
1181*/
1182void idAnimated::Event_StartRagdoll( void ) {
1183	StartRagdoll();
1184}
1185
1186/*
1187===============
1188idAnimated::Event_AnimDone
1189================
1190*/
1191void idAnimated::Event_AnimDone( int animindex ) {
1192	if ( g_debugCinematic.GetBool() ) {
1193		const idAnim *animPtr = animator.GetAnim( anim );
1194		gameLocal.Printf( "%d: '%s' end anim '%s'\n", gameLocal.framenum, GetName(), animPtr ? animPtr->Name() : "" );
1195	}
1196
1197	if ( ( animindex >= num_anims ) && spawnArgs.GetBool( "remove" ) ) {
1198		Hide();
1199		PostEventMS( &EV_Remove, 0 );
1200	} else if ( spawnArgs.GetBool( "auto_advance" ) ) {
1201		PlayNextAnim();
1202	} else {
1203		activated = false;
1204	}
1205
1206	ActivateTargets( activator.GetEntity() );
1207}
1208
1209/*
1210===============
1211idAnimated::Event_Activate
1212================
1213*/
1214void idAnimated::Event_Activate( idEntity *_activator ) {
1215	if ( num_anims ) {
1216		PlayNextAnim();
1217		activator = _activator;
1218		return;
1219	}
1220
1221	if ( activated ) {
1222		// already activated
1223		return;
1224	}
1225
1226	activated = true;
1227	activator = _activator;
1228	ProcessEvent( &EV_Animated_Start );
1229}
1230
1231/*
1232===============
1233idAnimated::Event_Start
1234================
1235*/
1236void idAnimated::Event_Start( void ) {
1237	int cycle;
1238	int len;
1239
1240	Show();
1241
1242	if ( num_anims ) {
1243		PlayNextAnim();
1244		return;
1245	}
1246
1247	if ( anim ) {
1248		if ( g_debugCinematic.GetBool() ) {
1249			const idAnim *animPtr = animator.GetAnim( anim );
1250			gameLocal.Printf( "%d: '%s' start anim '%s'\n", gameLocal.framenum, GetName(), animPtr ? animPtr->Name() : "" );
1251		}
1252		spawnArgs.GetInt( "cycle", "1", cycle );
1253		animator.CycleAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, FRAME2MS( blendFrames ) );
1254		animator.CurrentAnim( ANIMCHANNEL_ALL )->SetCycleCount( cycle );
1255
1256		len = animator.CurrentAnim( ANIMCHANNEL_ALL )->PlayLength();
1257		if ( len >= 0 ) {
1258			PostEventMS( &EV_AnimDone, len, 1 );
1259		}
1260	}
1261
1262	// offset the start time of the shader to sync it to the game time
1263	renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
1264
1265	animator.ForceUpdate();
1266	UpdateAnimation();
1267	UpdateVisuals();
1268	Present();
1269}
1270
1271/*
1272===============
1273idAnimated::Event_Footstep
1274===============
1275*/
1276void idAnimated::Event_Footstep( void ) {
1277	StartSound( "snd_footstep", SND_CHANNEL_BODY, 0, false, NULL );
1278}
1279
1280/*
1281=====================
1282idAnimated::Event_LaunchMissilesUpdate
1283=====================
1284*/
1285void idAnimated::Event_LaunchMissilesUpdate( int launchjoint, int targetjoint, int numshots, int framedelay ) {
1286	idVec3			launchPos;
1287	idVec3			targetPos;
1288	idMat3			axis;
1289	idVec3			dir;
1290	idEntity *		ent;
1291	idProjectile *	projectile;
1292	const idDict *	projectileDef;
1293	const char *	projectilename;
1294
1295	projectilename = spawnArgs.GetString( "projectilename" );
1296	projectileDef = gameLocal.FindEntityDefDict( projectilename, false );
1297	if ( !projectileDef ) {
1298		gameLocal.Warning( "idAnimated '%s' at (%s): 'launchMissiles' called with unknown projectile '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), projectilename );
1299		return;
1300	}
1301
1302	StartSound( "snd_missile", SND_CHANNEL_WEAPON, 0, false, NULL );
1303
1304	animator.GetJointTransform( ( jointHandle_t )launchjoint, gameLocal.time, launchPos, axis );
1305	launchPos = renderEntity.origin + launchPos * renderEntity.axis;
1306	
1307	animator.GetJointTransform( ( jointHandle_t )targetjoint, gameLocal.time, targetPos, axis );
1308	targetPos = renderEntity.origin + targetPos * renderEntity.axis;
1309
1310	dir = targetPos - launchPos;
1311	dir.Normalize();
1312
1313	gameLocal.SpawnEntityDef( *projectileDef, &ent, false );
1314	if ( !ent || !ent->IsType( idProjectile::Type ) ) {
1315		gameLocal.Error( "idAnimated '%s' at (%s): in 'launchMissiles' call '%s' is not an idProjectile", name.c_str(), GetPhysics()->GetOrigin().ToString(0), projectilename );
1316	}
1317	projectile = ( idProjectile * )ent;
1318	projectile->Create( this, launchPos, dir );
1319	projectile->Launch( launchPos, dir, vec3_origin );
1320
1321	if ( numshots > 0 ) {
1322		PostEventMS( &EV_LaunchMissilesUpdate, FRAME2MS( framedelay ), launchjoint, targetjoint, numshots - 1, framedelay );
1323	}
1324}
1325
1326/*
1327=====================
1328idAnimated::Event_LaunchMissiles
1329=====================
1330*/
1331void idAnimated::Event_LaunchMissiles( const char *projectilename, const char *sound, const char *launchjoint, const char *targetjoint, int numshots, int framedelay ) {
1332	const idDict *	projectileDef;
1333	jointHandle_t	launch;
1334	jointHandle_t	target;
1335
1336	projectileDef = gameLocal.FindEntityDefDict( projectilename, false );
1337	if ( !projectileDef ) {
1338		gameLocal.Warning( "idAnimated '%s' at (%s): unknown projectile '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), projectilename );
1339		return;
1340	}
1341
1342	launch = animator.GetJointHandle( launchjoint );
1343	if ( launch == INVALID_JOINT ) {
1344		gameLocal.Warning( "idAnimated '%s' at (%s): unknown launch joint '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), launchjoint );
1345		gameLocal.Error( "Unknown joint '%s'", launchjoint );
1346	}
1347
1348	target = animator.GetJointHandle( targetjoint );
1349	if ( target == INVALID_JOINT ) {
1350		gameLocal.Warning( "idAnimated '%s' at (%s): unknown target joint '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), targetjoint );
1351	}
1352
1353	spawnArgs.Set( "projectilename", projectilename );
1354	spawnArgs.Set( "missilesound", sound );
1355
1356	CancelEvents( &EV_LaunchMissilesUpdate );
1357	ProcessEvent( &EV_LaunchMissilesUpdate, launch, target, numshots - 1, framedelay );
1358}
1359
1360#ifdef _D3XP
1361/*
1362=====================
1363idAnimated::Event_SetAnimation
1364=====================
1365*/
1366void idAnimated::Event_SetAnimation( const char *animName ) {
1367
1368	//BSM Nerve: Need to add some error checking so we don't change the animation
1369	//in the middle of the existing animation
1370	anim = animator.GetAnim( animName );
1371	if ( !anim ) {
1372		gameLocal.Error( "idAnimated '%s' at (%s): cannot find anim '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), animName );
1373	}
1374
1375}
1376
1377/*
1378=====================
1379idAnimated::Event_GetAnimationLength
1380=====================
1381*/
1382void idAnimated::Event_GetAnimationLength() {
1383	float length = 0;
1384
1385	if(anim) {
1386		length = (float)(animator.AnimLength( anim )) / 1000.f;
1387	}
1388
1389	idThread::ReturnFloat(length);
1390}
1391#endif
1392
1393/*
1394===============================================================================
1395
1396	idStaticEntity
1397
1398	Some static entities may be optimized into inline geometry by dmap
1399
1400===============================================================================
1401*/
1402
1403CLASS_DECLARATION( idEntity, idStaticEntity )
1404	EVENT( EV_Activate,				idStaticEntity::Event_Activate )
1405END_CLASS
1406
1407/*
1408===============
1409idStaticEntity::idStaticEntity
1410===============
1411*/
1412idStaticEntity::idStaticEntity( void ) {
1413	spawnTime = 0;
1414	active = false;
1415	fadeFrom.Set( 1, 1, 1, 1 );
1416	fadeTo.Set( 1, 1, 1, 1 );
1417	fadeStart = 0;
1418	fadeEnd	= 0;
1419	runGui = false;
1420}
1421
1422/*
1423===============
1424idStaticEntity::Save
1425===============
1426*/
1427void idStaticEntity::Save( idSaveGame *savefile ) const {
1428	savefile->WriteInt( spawnTime );
1429	savefile->WriteBool( active );
1430	savefile->WriteVec4( fadeFrom );
1431	savefile->WriteVec4( fadeTo );
1432	savefile->WriteInt( fadeStart );
1433	savefile->WriteInt( fadeEnd );
1434	savefile->WriteBool( runGui );
1435}
1436
1437/*
1438===============
1439idStaticEntity::Restore
1440===============
1441*/
1442void idStaticEntity::Restore( idRestoreGame *savefile ) {
1443	savefile->ReadInt( spawnTime );
1444	savefile->ReadBool( active );
1445	savefile->ReadVec4( fadeFrom );
1446	savefile->ReadVec4( fadeTo );
1447	savefile->ReadInt( fadeStart );
1448	savefile->ReadInt( fadeEnd );
1449	savefile->ReadBool( runGui );
1450}
1451
1452/*
1453===============
1454idStaticEntity::Spawn
1455===============
1456*/
1457void idStaticEntity::Spawn( void ) {
1458	bool solid;
1459	bool hidden;
1460
1461	// an inline static model will not do anything at all
1462	if ( spawnArgs.GetBool( "inline" ) || gameLocal.world->spawnArgs.GetBool( "inlineAllStatics" ) ) {
1463		Hide();
1464		return;
1465	}
1466
1467	solid = spawnArgs.GetBool( "solid" );
1468	hidden = spawnArgs.GetBool( "hide" );
1469
1470	if ( solid && !hidden ) {
1471		GetPhysics()->SetContents( CONTENTS_SOLID );
1472	} else {
1473		GetPhysics()->SetContents( 0 );
1474	}
1475
1476	spawnTime = gameLocal.time;
1477	active = false;
1478
1479	idStr model = spawnArgs.GetString( "model" );
1480	if ( model.Find( ".prt" ) >= 0 ) {
1481		// we want the parametric particles out of sync with each other
1482		renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = gameLocal.random.RandomInt( 32767 );
1483	}
1484
1485	fadeFrom.Set( 1, 1, 1, 1 );
1486	fadeTo.Set( 1, 1, 1, 1 );
1487	fadeStart = 0;
1488	fadeEnd	= 0;
1489
1490	// NOTE: this should be used very rarely because it is expensive
1491	runGui = spawnArgs.GetBool( "runGui" );
1492	if ( runGui ) {
1493		BecomeActive( TH_THINK );
1494	}
1495}
1496
1497/*
1498================
1499idStaticEntity::ShowEditingDialog
1500================
1501*/
1502void idStaticEntity::ShowEditingDialog( void ) {
1503	common->InitTool( EDITOR_PARTICLE, &spawnArgs );
1504}
1505/*
1506================
1507idStaticEntity::Think
1508================
1509*/
1510void idStaticEntity::Think( void ) {
1511	idEntity::Think();
1512	if ( thinkFlags & TH_THINK ) {
1513		if ( runGui && renderEntity.gui[0] ) {
1514			idPlayer *player = gameLocal.GetLocalPlayer();
1515			if ( player ) {
1516				if ( !player->objectiveSystemOpen ) {
1517					renderEntity.gui[0]->StateChanged( gameLocal.time, true );
1518					if ( renderEntity.gui[1] ) {
1519						renderEntity.gui[1]->StateChanged( gameLocal.time, true );
1520					}
1521					if ( renderEntity.gui[2] ) {
1522						renderEntity.gui[2]->StateChanged( gameLocal.time, true );
1523					}
1524				}
1525			}
1526		}
1527		if ( fadeEnd > 0 ) {
1528			idVec4 color;
1529			if ( gameLocal.time < fadeEnd ) {
1530				color.Lerp( fadeFrom, fadeTo, ( float )( gameLocal.time - fadeStart ) / ( float )( fadeEnd - fadeStart ) );
1531			} else {
1532				color = fadeTo;
1533				fadeEnd = 0;
1534				BecomeInactive( TH_THINK );
1535			}
1536			SetColor( color );
1537		}
1538	}
1539}
1540
1541/*
1542================
1543idStaticEntity::Fade
1544================
1545*/
1546void idStaticEntity::Fade( const idVec4 &to, float fadeTime ) {
1547	GetColor( fadeFrom );
1548	fadeTo = to;
1549	fadeStart = gameLocal.time;
1550	fadeEnd = gameLocal.time + SEC2MS( fadeTime );
1551	BecomeActive( TH_THINK );
1552}
1553
1554/*
1555================
1556idStaticEntity::Hide
1557================
1558*/
1559void idStaticEntity::Hide( void ) {
1560	idEntity::Hide();
1561	GetPhysics()->SetContents( 0 );
1562}
1563
1564/*
1565================
1566idStaticEntity::Show
1567================
1568*/
1569void idStaticEntity::Show( void ) {
1570	idEntity::Show();
1571	if ( spawnArgs.GetBool( "solid" ) ) {
1572		GetPhysics()->SetContents( CONTENTS_SOLID );
1573	}
1574}
1575
1576/*
1577================
1578idStaticEntity::Event_Activate
1579================
1580*/
1581void idStaticEntity::Event_Activate( idEntity *activator ) {
1582	idStr activateGui;
1583
1584	spawnTime = gameLocal.time;
1585	active = !active;
1586
1587	const idKeyValue *kv = spawnArgs.FindKey( "hide" );
1588	if ( kv ) {
1589		if ( IsHidden() ) {
1590			Show();
1591		} else {
1592			Hide();
1593		}
1594	}
1595
1596	renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( spawnTime );
1597	renderEntity.shaderParms[5] = active;
1598	// this change should be a good thing, it will automatically turn on 
1599	// lights etc.. when triggered so that does not have to be specifically done
1600	// with trigger parms.. it MIGHT break things so need to keep an eye on it
1601	renderEntity.shaderParms[ SHADERPARM_MODE ] = ( renderEntity.shaderParms[ SHADERPARM_MODE ] ) ?  0.0f : 1.0f;
1602	BecomeActive( TH_UPDATEVISUALS );
1603}
1604
1605/*
1606================
1607idStaticEntity::WriteToSnapshot
1608================
1609*/
1610void idStaticEntity::WriteToSnapshot( idBitMsgDelta &msg ) const {
1611	GetPhysics()->WriteToSnapshot( msg );
1612	WriteBindToSnapshot( msg );
1613	WriteColorToSnapshot( msg );
1614	WriteGUIToSnapshot( msg );
1615	msg.WriteBits( IsHidden()?1:0, 1 );
1616}
1617
1618/*
1619================
1620idStaticEntity::ReadFromSnapshot
1621================
1622*/
1623void idStaticEntity::ReadFromSnapshot( const idBitMsgDelta &msg ) {
1624	bool hidden;
1625
1626	GetPhysics()->ReadFromSnapshot( msg );
1627	ReadBindFromSnapshot( msg );
1628	ReadColorFromSnapshot( msg );
1629	ReadGUIFromSnapshot( msg );
1630	hidden = msg.ReadBits( 1 ) == 1;
1631	if ( hidden != IsHidden() ) {
1632		if ( hidden ) {
1633			Hide();
1634		} else {
1635			Show();
1636		}
1637	}
1638	if ( msg.HasChanged() ) {
1639		UpdateVisuals();
1640	}
1641}
1642
1643
1644/*
1645===============================================================================
1646
1647idFuncEmitter
1648
1649===============================================================================
1650*/
1651
1652
1653CLASS_DECLARATION( idStaticEntity, idFuncEmitter )
1654EVENT( EV_Activate,				idFuncEmitter::Event_Activate )
1655END_CLASS
1656
1657/*
1658===============
1659idFuncEmitter::idFuncEmitter
1660===============
1661*/
1662idFuncEmitter::idFuncEmitter( void ) {
1663	hidden = false;
1664}
1665
1666/*
1667===============
1668idFuncEmitter::Spawn
1669===============
1670*/
1671void idFuncEmitter::Spawn( void ) {
1672	if ( spawnArgs.GetBool( "start_off" ) ) {
1673		hidden = true;
1674		renderEntity.shaderParms[SHADERPARM_PARTICLE_STOPTIME] = MS2SEC( 1 );
1675		UpdateVisuals();
1676	} else {
1677		hidden = false;
1678	}
1679}
1680
1681/*
1682===============
1683idFuncEmitter::Save
1684===============
1685*/
1686void idFuncEmitter::Save( idSaveGame *savefile ) const {
1687	savefile->WriteBool( hidden );
1688}
1689
1690/*
1691===============
1692idFuncEmitter::Restore
1693===============
1694*/
1695void idFuncEmitter::Restore( idRestoreGame *savefile ) {
1696	savefile->ReadBool( hidden );
1697}
1698
1699/*
1700================
1701idFuncEmitter::Event_Activate
1702================
1703*/
1704void idFuncEmitter::Event_Activate( idEntity *activator ) {
1705	if ( hidden || spawnArgs.GetBool( "cycleTrigger" ) ) {
1706		renderEntity.shaderParms[SHADERPARM_PARTICLE_STOPTIME] = 0;
1707		renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC( gameLocal.time );
1708		hidden = false;
1709	} else {
1710		renderEntity.shaderParms[SHADERPARM_PARTICLE_STOPTIME] = MS2SEC( gameLocal.time );
1711		hidden = true;
1712	}
1713	UpdateVisuals();
1714}
1715
1716/*
1717================
1718idFuncEmitter::WriteToSnapshot
1719================
1720*/
1721void idFuncEmitter::WriteToSnapshot( idBitMsgDelta &msg ) const {
1722	msg.WriteBits( hidden ? 1 : 0, 1 );
1723	msg.WriteFloat( renderEntity.shaderParms[ SHADERPARM_PARTICLE_STOPTIME ] );
1724	msg.WriteFloat( renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] );
1725}
1726
1727/*
1728================
1729idFuncEmitter::ReadFromSnapshot
1730================
1731*/
1732void idFuncEmitter::ReadFromSnapshot( const idBitMsgDelta &msg ) {
1733	hidden = msg.ReadBits( 1 ) != 0;
1734	renderEntity.shaderParms[ SHADERPARM_PARTICLE_STOPTIME ] = msg.ReadFloat();
1735	renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = msg.ReadFloat();
1736	if ( msg.HasChanged() ) {
1737		UpdateVisuals();
1738	}
1739}
1740
1741
1742/*
1743===============================================================================
1744
1745idFuncSplat
1746
1747===============================================================================
1748*/
1749
1750
1751const idEventDef EV_Splat( "<Splat>" );
1752CLASS_DECLARATION( idFuncEmitter, idFuncSplat )
1753EVENT( EV_Activate,		idFuncSplat::Event_Activate )
1754EVENT( EV_Splat,		idFuncSplat::Event_Splat )
1755END_CLASS
1756
1757/*
1758===============
1759idFuncSplat::idFuncSplat
1760===============
1761*/
1762idFuncSplat::idFuncSplat( void ) {
1763}
1764
1765/*
1766===============
1767idFuncSplat::Spawn
1768===============
1769*/
1770void idFuncSplat::Spawn( void ) {
1771}
1772
1773/*
1774================
1775idFuncSplat::Event_Splat
1776================
1777*/
1778void idFuncSplat::Event_Splat( void ) {
1779	const char *splat = NULL;
1780	int count = spawnArgs.GetInt( "splatCount", "1" );
1781	for ( int i = 0; i < count; i++ ) {
1782		splat = spawnArgs.RandomPrefix( "mtr_splat", gameLocal.random );
1783		if ( splat && *splat ) {
1784			float size = spawnArgs.GetFloat( "splatSize", "128" );
1785			float dist = spawnArgs.GetFloat( "splatDistance", "128" );
1786			float angle = spawnArgs.GetFloat( "splatAngle", "0" );
1787			gameLocal.ProjectDecal( GetPhysics()->GetOrigin(), GetPhysics()->GetAxis()[2], dist, true, size, splat, angle );
1788		}
1789	}
1790	StartSound( "snd_splat", SND_CHANNEL_ANY, 0, false, NULL );
1791}
1792
1793/*
1794================
1795idFuncSplat::Event_Activate
1796================
1797*/
1798void idFuncSplat::Event_Activate( idEntity *activator ) {
1799	idFuncEmitter::Event_Activate( activator );
1800	PostEventSec( &EV_Splat, spawnArgs.GetFloat( "splatDelay", "0.25" ) );
1801	StartSound( "snd_spurt", SND_CHANNEL_ANY, 0, false, NULL );
1802}
1803
1804/*
1805===============================================================================
1806
1807idFuncSmoke
1808
1809===============================================================================
1810*/
1811
1812CLASS_DECLARATION( idEntity, idFuncSmoke )
1813EVENT( EV_Activate,				idFuncSmoke::Event_Activate )
1814END_CLASS
1815
1816/*
1817===============
1818idFuncSmoke::idFuncSmoke
1819===============
1820*/
1821idFuncSmoke::idFuncSmoke() {
1822	smokeTime = 0;
1823	smoke = NULL;
1824	restart = false;
1825}
1826
1827/*
1828===============
1829idFuncSmoke::Save
1830===============
1831*/
1832void idFuncSmoke::Save(	idSaveGame *savefile ) const {
1833	savefile->WriteInt( smokeTime );
1834	savefile->WriteParticle( smoke );
1835	savefile->WriteBool( restart );
1836}
1837
1838/*
1839===============
1840idFuncSmoke::Restore
1841===============
1842*/
1843void idFuncSmoke::Restore( idRestoreGame *savefile ) {
1844	savefile->ReadInt( smokeTime );
1845	savefile->ReadParticle( smoke );
1846	savefile->ReadBool( restart );
1847}
1848
1849/*
1850===============
1851idFuncSmoke::Spawn
1852===============
1853*/
1854void idFuncSmoke::Spawn( void ) {
1855	const char *smokeName = spawnArgs.GetString( "smoke" );
1856	if ( *smokeName != '\0' ) {
1857		smoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
1858	} else {
1859		smoke = NULL;
1860	}
1861	if ( spawnArgs.GetBool( "start_off" ) ) {
1862		smokeTime = 0;
1863		restart = false;
1864	} else if ( smoke ) {
1865		smokeTime = gameLocal.time;
1866		BecomeActive( TH_UPDATEPARTICLES );
1867		restart = true;
1868	}
1869	GetPhysics()->SetContents( 0 );
1870}
1871
1872/*
1873================
1874idFuncSmoke::Event_Activate
1875================
1876*/
1877void idFuncSmoke::Event_Activate( idEntity *activator ) {
1878	if ( thinkFlags & TH_UPDATEPARTICLES ) {
1879		restart = false;
1880		return;
1881	} else {
1882		BecomeActive( TH_UPDATEPARTICLES );
1883		restart = true;
1884		smokeTime = gameLocal.time;
1885	}
1886}
1887
1888/*
1889===============
1890idFuncSmoke::Think
1891================
1892*/
1893void idFuncSmoke::Think( void ) {
1894
1895	// if we are completely closed off from the player, don't do anything at all
1896	if ( CheckDormant() || smoke == NULL || smokeTime == -1 ) {
1897		return;
1898	}
1899
1900	if ( ( thinkFlags & TH_UPDATEPARTICLES) && !IsHidden() ) {
1901		if ( !gameLocal.smokeParticles->EmitSmoke( smoke, smokeTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis(), timeGroup /*_D3XP*/ ) ) {
1902			if ( restart ) {
1903				smokeTime = gameLocal.time;
1904			} else {
1905				smokeTime = 0;
1906				BecomeInactive( TH_UPDATEPARTICLES );
1907			}
1908		}
1909	}
1910
1911}
1912
1913
1914/*
1915===============================================================================
1916
1917	idTextEntity
1918
1919===============================================================================
1920*/
1921
1922CLASS_DECLARATION( idEntity, idTextEntity )
1923END_CLASS
1924
1925/*
1926================
1927idTextEntity::Spawn
1928================
1929*/
1930void idTextEntity::Spawn( void ) {
1931	// these are cached as the are used each frame
1932	text = spawnArgs.GetString( "text" );
1933	playerOriented = spawnArgs.GetBool( "playerOriented" );
1934	bool force = spawnArgs.GetBool( "force" );
1935	if ( developer.GetBool() || force ) {
1936		BecomeActive(TH_THINK);
1937	}
1938}
1939
1940/*
1941================
1942idTextEntity::Save
1943================
1944*/
1945void idTextEntity::Save( idSaveGame *savefile ) const {
1946	savefile->WriteString( text );
1947	savefile->WriteBool( playerOriented );
1948}
1949
1950/*
1951================
1952idTextEntity::Restore
1953================
1954*/
1955void idTextEntity::Restore( idRestoreGame *savefile ) {
1956	savefile->ReadString( text );
1957	savefile->ReadBool( playerOriented );
1958}
1959
1960/*
1961================
1962idTextEntity::Think
1963================
1964*/
1965void idTextEntity::Think( void ) {
1966	if ( thinkFlags & TH_THINK ) {
1967		gameRenderWorld->DrawText( text, GetPhysics()->GetOrigin(), 0.25, colorWhite, playerOriented ? gameLocal.GetLocalPlayer()->viewAngles.ToMat3() : GetPhysics()->GetAxis().Transpose(), 1 );
1968		for ( int i = 0; i < targets.Num(); i++ ) {
1969			if ( targets[i].GetEntity() ) {
1970				gameRenderWorld->DebugArrow( colorBlue, GetPhysics()->GetOrigin(), targets[i].GetEntity()->GetPhysics()->GetOrigin(), 1 );
1971			}
1972		}
1973	} else {
1974		BecomeInactive( TH_ALL );
1975	}
1976}
1977
1978
1979/*
1980===============================================================================
1981
1982	idVacuumSeperatorEntity
1983
1984	Can be triggered to let vacuum through a portal (blown out window)
1985
1986===============================================================================
1987*/
1988
1989CLASS_DECLARATION( idEntity, idVacuumSeparatorEntity )
1990	EVENT( EV_Activate,		idVacuumSeparatorEntity::Event_Activate )
1991END_CLASS
1992
1993
1994/*
1995================
1996idVacuumSeparatorEntity::idVacuumSeparatorEntity
1997================
1998*/
1999idVacuumSeparatorEntity::idVacuumSeparatorEntity( void ) {
2000	portal = 0;
2001}
2002
2003/*
2004================
2005idVacuumSeparatorEntity::Save
2006================
2007*/
2008void idVacuumSeparatorEntity::Save( idSaveGame *savefile ) const {
2009	savefile->WriteInt( (int)portal );
2010	savefile->WriteInt( gameRenderWorld->GetPortalState( portal ) );
2011}
2012
2013/*
2014================
2015idVacuumSeparatorEntity::Restore
2016================
2017*/
2018void idVacuumSeparatorEntity::Restore( idRestoreGame *savefile ) {
2019	int state;
2020
2021	savefile->ReadInt( (int &)portal );
2022	savefile->ReadInt( state );
2023
2024	gameLocal.SetPortalState( portal, state );
2025}
2026
2027/*
2028================
2029idVacuumSeparatorEntity::Spawn
2030================
2031*/
2032void idVacuumSeparatorEntity::Spawn() {
2033	idBounds b;
2034
2035	b = idBounds( spawnArgs.GetVector( "origin" ) ).Expand( 16 );
2036	portal = gameRenderWorld->FindPortal( b );
2037	if ( !portal ) {
2038		gameLocal.Warning( "VacuumSeparator '%s' didn't contact a portal", spawnArgs.GetString( "name" ) );
2039		return;
2040	}
2041	gameLocal.SetPortalState( portal, PS_BLOCK_AIR | PS_BLOCK_LOCATION );
2042}
2043
2044/*
2045================
2046idVacuumSeparatorEntity::Event_Activate
2047================
2048*/
2049void idVacuumSeparatorEntity::Event_Activate( idEntity *activator ) {
2050	if ( !portal ) {
2051		return;
2052	}
2053	gameLocal.SetPortalState( portal, PS_BLOCK_NONE );
2054}
2055
2056
2057/*
2058===============================================================================
2059
2060idLocationSeparatorEntity
2061
2062===============================================================================
2063*/
2064
2065CLASS_DECLARATION( idEntity, idLocationSeparatorEntity )
2066END_CLASS
2067
2068/*
2069================
2070idLocationSeparatorEntity::Spawn
2071================
2072*/
2073void idLocationSeparatorEntity::Spawn() {
2074	idBounds b;
2075
2076	b = idBounds( spawnArgs.GetVector( "origin" ) ).Expand( 16 );
2077	qhandle_t portal = gameRenderWorld->FindPortal( b );
2078	if ( !portal ) {
2079		gameLocal.Warning( "LocationSeparator '%s' didn't contac…

Large files files are truncated, but you can click here to view the full file