PageRenderTime 504ms CodeModel.GetById 88ms app.highlight 309ms RepoModel.GetById 33ms app.codeStats 1ms

/neo/game/Misc.cpp

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

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