PageRenderTime 1364ms CodeModel.GetById 224ms app.highlight 1087ms RepoModel.GetById 13ms app.codeStats 1ms

/neo/d3xp/Misc.cpp

https://bitbucket.org/stephenabritton/doom-3-bfg
C++ | 3934 lines | 2068 code | 563 blank | 1303 comment | 370 complexity | f2b31e0b5decb927552da7af45c0e9d3 MD5 | raw file

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

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

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