GameEdit.cpp (27693B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In 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 24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. 25 26 =========================================================================== 27 */ 28 29 #include "../idlib/precompiled.h" 30 #pragma hdrstop 31 32 #include "Game_local.h" 33 34 35 /* 36 =============================================================================== 37 38 Ingame cursor. 39 40 =============================================================================== 41 */ 42 43 CLASS_DECLARATION( idEntity, idCursor3D ) 44 END_CLASS 45 46 /* 47 =============== 48 idCursor3D::idCursor3D 49 =============== 50 */ 51 idCursor3D::idCursor3D() { 52 draggedPosition.Zero(); 53 } 54 55 /* 56 =============== 57 idCursor3D::~idCursor3D 58 =============== 59 */ 60 idCursor3D::~idCursor3D() { 61 } 62 63 /* 64 =============== 65 idCursor3D::Spawn 66 =============== 67 */ 68 void idCursor3D::Spawn() { 69 } 70 71 /* 72 =============== 73 idCursor3D::Present 74 =============== 75 */ 76 void idCursor3D::Present() { 77 // don't present to the renderer if the entity hasn't changed 78 if ( !( thinkFlags & TH_UPDATEVISUALS ) ) { 79 return; 80 } 81 BecomeInactive( TH_UPDATEVISUALS ); 82 83 const idVec3 &origin = GetPhysics()->GetOrigin(); 84 const idMat3 &axis = GetPhysics()->GetAxis(); 85 gameRenderWorld->DebugArrow( colorYellow, origin + axis[1] * -5.0f + axis[2] * 5.0f, origin, 2 ); 86 gameRenderWorld->DebugArrow( colorRed, origin, draggedPosition, 2 ); 87 } 88 89 /* 90 =============== 91 idCursor3D::Think 92 =============== 93 */ 94 void idCursor3D::Think() { 95 if ( thinkFlags & TH_THINK ) { 96 drag.Evaluate( gameLocal.time ); 97 } 98 Present(); 99 } 100 101 102 /* 103 =============================================================================== 104 105 Allows entities to be dragged through the world with physics. 106 107 =============================================================================== 108 */ 109 110 #define MAX_DRAG_TRACE_DISTANCE 2048.0f 111 112 /* 113 ============== 114 idDragEntity::idDragEntity 115 ============== 116 */ 117 idDragEntity::idDragEntity() { 118 cursor = NULL; 119 Clear(); 120 } 121 122 /* 123 ============== 124 idDragEntity::~idDragEntity 125 ============== 126 */ 127 idDragEntity::~idDragEntity() { 128 StopDrag(); 129 selected = NULL; 130 delete cursor; 131 cursor = NULL; 132 } 133 134 135 /* 136 ============== 137 idDragEntity::Clear 138 ============== 139 */ 140 void idDragEntity::Clear() { 141 dragEnt = NULL; 142 joint = INVALID_JOINT; 143 id = 0; 144 localEntityPoint.Zero(); 145 localPlayerPoint.Zero(); 146 bodyName.Clear(); 147 selected = NULL; 148 } 149 150 /* 151 ============== 152 idDragEntity::StopDrag 153 ============== 154 */ 155 void idDragEntity::StopDrag() { 156 dragEnt = NULL; 157 if ( cursor ) { 158 cursor->BecomeInactive( TH_THINK ); 159 } 160 } 161 162 /* 163 ============== 164 idDragEntity::Update 165 ============== 166 */ 167 void idDragEntity::Update( idPlayer *player ) { 168 idVec3 viewPoint, origin; 169 idMat3 viewAxis, axis; 170 trace_t trace; 171 idEntity *newEnt = NULL; 172 idAngles angles; 173 jointHandle_t newJoint = INVALID_JOINT; 174 idStr newBodyName; 175 176 player->GetViewPos( viewPoint, viewAxis ); 177 178 // if no entity selected for dragging 179 if ( !dragEnt.GetEntity() ) { 180 181 if ( player->usercmd.buttons & BUTTON_ATTACK ) { 182 183 gameLocal.clip.TracePoint( trace, viewPoint, viewPoint + viewAxis[0] * MAX_DRAG_TRACE_DISTANCE, (CONTENTS_SOLID|CONTENTS_RENDERMODEL|CONTENTS_BODY), player ); 184 if ( trace.fraction < 1.0f ) { 185 186 newEnt = gameLocal.entities[ trace.c.entityNum ]; 187 if ( newEnt ) { 188 189 if ( newEnt->GetBindMaster() ) { 190 if ( newEnt->GetBindJoint() ) { 191 trace.c.id = JOINT_HANDLE_TO_CLIPMODEL_ID( newEnt->GetBindJoint() ); 192 } else { 193 trace.c.id = newEnt->GetBindBody(); 194 } 195 newEnt = newEnt->GetBindMaster(); 196 } 197 198 if ( newEnt->IsType( idAFEntity_Base::Type ) && static_cast<idAFEntity_Base *>(newEnt)->IsActiveAF() ) { 199 idAFEntity_Base *af = static_cast<idAFEntity_Base *>(newEnt); 200 201 // joint being dragged 202 newJoint = CLIPMODEL_ID_TO_JOINT_HANDLE( trace.c.id ); 203 // get the body id from the trace model id which might be a joint handle 204 trace.c.id = af->BodyForClipModelId( trace.c.id ); 205 // get the name of the body being dragged 206 newBodyName = af->GetAFPhysics()->GetBody( trace.c.id )->GetName(); 207 208 } else if ( !newEnt->IsType( idWorldspawn::Type ) ) { 209 210 if ( trace.c.id < 0 ) { 211 newJoint = CLIPMODEL_ID_TO_JOINT_HANDLE( trace.c.id ); 212 } else { 213 newJoint = INVALID_JOINT; 214 } 215 newBodyName = ""; 216 217 } else { 218 219 newJoint = INVALID_JOINT; 220 newEnt = NULL; 221 } 222 } 223 if ( newEnt ) { 224 dragEnt = newEnt; 225 selected = newEnt; 226 joint = newJoint; 227 id = trace.c.id; 228 bodyName = newBodyName; 229 230 if ( !cursor ) { 231 cursor = ( idCursor3D * )gameLocal.SpawnEntityType( idCursor3D::Type ); 232 } 233 234 idPhysics *phys = dragEnt.GetEntity()->GetPhysics(); 235 localPlayerPoint = ( trace.c.point - viewPoint ) * viewAxis.Transpose(); 236 origin = phys->GetOrigin( id ); 237 axis = phys->GetAxis( id ); 238 localEntityPoint = ( trace.c.point - origin ) * axis.Transpose(); 239 240 cursor->drag.Init( g_dragDamping.GetFloat() ); 241 cursor->drag.SetPhysics( phys, id, localEntityPoint ); 242 cursor->Show(); 243 244 if ( phys->IsType( idPhysics_AF::Type ) || 245 phys->IsType( idPhysics_RigidBody::Type ) || 246 phys->IsType( idPhysics_Monster::Type ) ) { 247 cursor->BecomeActive( TH_THINK ); 248 } 249 } 250 } 251 } 252 } 253 254 // if there is an entity selected for dragging 255 idEntity *drag = dragEnt.GetEntity(); 256 if ( drag ) { 257 258 if ( !( player->usercmd.buttons & BUTTON_ATTACK ) ) { 259 StopDrag(); 260 return; 261 } 262 263 cursor->SetOrigin( viewPoint + localPlayerPoint * viewAxis ); 264 cursor->SetAxis( viewAxis ); 265 266 cursor->drag.SetDragPosition( cursor->GetPhysics()->GetOrigin() ); 267 268 renderEntity_t *renderEntity = drag->GetRenderEntity(); 269 idAnimator *dragAnimator = drag->GetAnimator(); 270 271 if ( joint != INVALID_JOINT && renderEntity != NULL && dragAnimator != NULL ) { 272 dragAnimator->GetJointTransform( joint, gameLocal.time, cursor->draggedPosition, axis ); 273 cursor->draggedPosition = renderEntity->origin + cursor->draggedPosition * renderEntity->axis; 274 gameRenderWorld->DrawText( va( "%s\n%s\n%s, %s", drag->GetName(), drag->GetType()->classname, dragAnimator->GetJointName( joint ), bodyName.c_str() ), cursor->GetPhysics()->GetOrigin(), 0.1f, colorWhite, viewAxis, 1 ); 275 } else { 276 cursor->draggedPosition = cursor->GetPhysics()->GetOrigin(); 277 gameRenderWorld->DrawText( va( "%s\n%s\n%s", drag->GetName(), drag->GetType()->classname, bodyName.c_str() ), cursor->GetPhysics()->GetOrigin(), 0.1f, colorWhite, viewAxis, 1 ); 278 } 279 } 280 281 // if there is a selected entity 282 if ( selected.GetEntity() && g_dragShowSelection.GetBool() ) { 283 // draw the bbox of the selected entity 284 renderEntity_t *renderEntity = selected.GetEntity()->GetRenderEntity(); 285 if ( renderEntity ) { 286 gameRenderWorld->DebugBox( colorYellow, idBox( renderEntity->bounds, renderEntity->origin, renderEntity->axis ) ); 287 } 288 } 289 } 290 291 /* 292 ============== 293 idDragEntity::SetSelected 294 ============== 295 */ 296 void idDragEntity::SetSelected( idEntity *ent ) { 297 selected = ent; 298 StopDrag(); 299 } 300 301 /* 302 ============== 303 idDragEntity::DeleteSelected 304 ============== 305 */ 306 void idDragEntity::DeleteSelected() { 307 delete selected.GetEntity(); 308 selected = NULL; 309 StopDrag(); 310 } 311 312 /* 313 ============== 314 idDragEntity::BindSelected 315 ============== 316 */ 317 void idDragEntity::BindSelected() { 318 int num, largestNum; 319 idLexer lexer; 320 idToken type, bodyName; 321 idStr key, value, bindBodyName; 322 const idKeyValue *kv; 323 idAFEntity_Base *af; 324 325 af = static_cast<idAFEntity_Base *>(dragEnt.GetEntity()); 326 327 if ( !af || !af->IsType( idAFEntity_Base::Type ) || !af->IsActiveAF() ) { 328 return; 329 } 330 331 bindBodyName = af->GetAFPhysics()->GetBody( id )->GetName(); 332 largestNum = 1; 333 334 // parse all the bind constraints 335 kv = af->spawnArgs.MatchPrefix( "bindConstraint ", NULL ); 336 while ( kv ) { 337 key = kv->GetKey(); 338 key.Strip( "bindConstraint " ); 339 if ( sscanf( key, "bind%d", &num ) ) { 340 if ( num >= largestNum ) { 341 largestNum = num + 1; 342 } 343 } 344 345 lexer.LoadMemory( kv->GetValue(), kv->GetValue().Length(), kv->GetKey() ); 346 lexer.ReadToken( &type ); 347 lexer.ReadToken( &bodyName ); 348 lexer.FreeSource(); 349 350 // if there already exists a bind constraint for this body 351 if ( bodyName.Icmp( bindBodyName ) == 0 ) { 352 // delete the bind constraint 353 af->spawnArgs.Delete( kv->GetKey() ); 354 kv = NULL; 355 } 356 357 kv = af->spawnArgs.MatchPrefix( "bindConstraint ", kv ); 358 } 359 360 sprintf( key, "bindConstraint bind%d", largestNum ); 361 sprintf( value, "ballAndSocket %s %s", bindBodyName.c_str(), af->GetAnimator()->GetJointName( joint ) ); 362 363 af->spawnArgs.Set( key, value ); 364 af->spawnArgs.Set( "bind", "worldspawn" ); 365 af->Bind( gameLocal.world, true ); 366 } 367 368 /* 369 ============== 370 idDragEntity::UnbindSelected 371 ============== 372 */ 373 void idDragEntity::UnbindSelected() { 374 const idKeyValue *kv; 375 idAFEntity_Base *af; 376 377 af = static_cast<idAFEntity_Base *>(selected.GetEntity()); 378 379 if ( !af || !af->IsType( idAFEntity_Base::Type ) || !af->IsActiveAF() ) { 380 return; 381 } 382 383 // unbind the selected entity 384 af->Unbind(); 385 386 // delete all the bind constraints 387 kv = selected.GetEntity()->spawnArgs.MatchPrefix( "bindConstraint ", NULL ); 388 while ( kv ) { 389 selected.GetEntity()->spawnArgs.Delete( kv->GetKey() ); 390 kv = selected.GetEntity()->spawnArgs.MatchPrefix( "bindConstraint ", NULL ); 391 } 392 393 // delete any bind information 394 af->spawnArgs.Delete( "bind" ); 395 af->spawnArgs.Delete( "bindToJoint" ); 396 af->spawnArgs.Delete( "bindToBody" ); 397 } 398 399 400 /* 401 =============================================================================== 402 403 Handles ingame entity editing. 404 405 =============================================================================== 406 */ 407 408 /* 409 ============== 410 idEditEntities::idEditEntities 411 ============== 412 */ 413 idEditEntities::idEditEntities() { 414 selectableEntityClasses.Clear(); 415 nextSelectTime = 0; 416 } 417 418 /* 419 ============= 420 idEditEntities::SelectEntity 421 ============= 422 */ 423 bool idEditEntities::SelectEntity( const idVec3 &origin, const idVec3 &dir, const idEntity *skip ) { 424 idVec3 end; 425 idEntity *ent; 426 427 if ( !g_editEntityMode.GetInteger() || selectableEntityClasses.Num() == 0 ) { 428 return false; 429 } 430 431 if ( gameLocal.time < nextSelectTime ) { 432 return true; 433 } 434 nextSelectTime = gameLocal.time + 300; 435 436 end = origin + dir * 4096.0f; 437 438 ent = NULL; 439 for ( int i = 0; i < selectableEntityClasses.Num(); i++ ) { 440 ent = gameLocal.FindTraceEntity( origin, end, *selectableEntityClasses[i].typeInfo, skip ); 441 if ( ent ) { 442 break; 443 } 444 } 445 if ( ent ) { 446 ClearSelectedEntities(); 447 if ( EntityIsSelectable( ent ) ) { 448 AddSelectedEntity( ent ); 449 gameLocal.Printf( "entity #%d: %s '%s'\n", ent->entityNumber, ent->GetClassname(), ent->name.c_str() ); 450 ent->ShowEditingDialog(); 451 return true; 452 } 453 } 454 return false; 455 } 456 457 /* 458 ============= 459 idEditEntities::AddSelectedEntity 460 ============= 461 */ 462 void idEditEntities::AddSelectedEntity(idEntity *ent) { 463 ent->fl.selected = true; 464 selectedEntities.AddUnique(ent); 465 } 466 467 /* 468 ============== 469 idEditEntities::RemoveSelectedEntity 470 ============== 471 */ 472 void idEditEntities::RemoveSelectedEntity( idEntity *ent ) { 473 if ( selectedEntities.Find( ent ) ) { 474 selectedEntities.Remove( ent ); 475 } 476 } 477 478 /* 479 ============= 480 idEditEntities::ClearSelectedEntities 481 ============= 482 */ 483 void idEditEntities::ClearSelectedEntities() { 484 int i, count; 485 486 count = selectedEntities.Num(); 487 for ( i = 0; i < count; i++ ) { 488 selectedEntities[i]->fl.selected = false; 489 } 490 selectedEntities.Clear(); 491 } 492 493 494 /* 495 ============= 496 idEditEntities::EntityIsSelectable 497 ============= 498 */ 499 bool idEditEntities::EntityIsSelectable( idEntity *ent, idVec4 *color, idStr *text ) { 500 for ( int i = 0; i < selectableEntityClasses.Num(); i++ ) { 501 if ( ent->GetType() == selectableEntityClasses[i].typeInfo ) { 502 if ( text ) { 503 *text = selectableEntityClasses[i].textKey; 504 } 505 if ( color ) { 506 if ( ent->fl.selected ) { 507 *color = colorRed; 508 } else { 509 switch( i ) { 510 case 1 : 511 *color = colorYellow; 512 break; 513 case 2 : 514 *color = colorBlue; 515 break; 516 default: 517 *color = colorGreen; 518 } 519 } 520 } 521 return true; 522 } 523 } 524 return false; 525 } 526 527 /* 528 ============= 529 idEditEntities::DisplayEntities 530 ============= 531 */ 532 void idEditEntities::DisplayEntities() { 533 idEntity *ent; 534 535 if ( !gameLocal.GetLocalPlayer() ) { 536 return; 537 } 538 539 selectableEntityClasses.Clear(); 540 selectedTypeInfo_t sit; 541 542 switch( g_editEntityMode.GetInteger() ) { 543 case 1: 544 sit.typeInfo = &idLight::Type; 545 sit.textKey = "texture"; 546 selectableEntityClasses.Append( sit ); 547 break; 548 case 2: 549 sit.typeInfo = &idSound::Type; 550 sit.textKey = "s_shader"; 551 selectableEntityClasses.Append( sit ); 552 sit.typeInfo = &idLight::Type; 553 sit.textKey = "texture"; 554 selectableEntityClasses.Append( sit ); 555 break; 556 case 3: 557 sit.typeInfo = &idAFEntity_Base::Type; 558 sit.textKey = "articulatedFigure"; 559 selectableEntityClasses.Append( sit ); 560 break; 561 case 4: 562 sit.typeInfo = &idFuncEmitter::Type; 563 sit.textKey = "model"; 564 selectableEntityClasses.Append( sit ); 565 break; 566 case 5: 567 sit.typeInfo = &idAI::Type; 568 sit.textKey = "name"; 569 selectableEntityClasses.Append( sit ); 570 break; 571 case 6: 572 sit.typeInfo = &idEntity::Type; 573 sit.textKey = "name"; 574 selectableEntityClasses.Append( sit ); 575 break; 576 case 7: 577 sit.typeInfo = &idEntity::Type; 578 sit.textKey = "model"; 579 selectableEntityClasses.Append( sit ); 580 break; 581 default: 582 return; 583 } 584 585 idBounds viewBounds( gameLocal.GetLocalPlayer()->GetPhysics()->GetOrigin() ); 586 idBounds viewTextBounds( gameLocal.GetLocalPlayer()->GetPhysics()->GetOrigin() ); 587 idMat3 axis = gameLocal.GetLocalPlayer()->viewAngles.ToMat3(); 588 589 viewBounds.ExpandSelf( 512 ); 590 viewTextBounds.ExpandSelf( 128 ); 591 592 idStr textKey; 593 594 for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) { 595 596 idVec4 color; 597 598 textKey = ""; 599 if ( !EntityIsSelectable( ent, &color, &textKey ) ) { 600 continue; 601 } 602 603 bool drawArrows = false; 604 if ( ent->GetType() == &idAFEntity_Base::Type ) { 605 if ( !static_cast<idAFEntity_Base *>(ent)->IsActiveAF() ) { 606 continue; 607 } 608 } else if ( ent->GetType() == &idSound::Type ) { 609 if ( ent->fl.selected ) { 610 drawArrows = true; 611 } 612 const idSoundShader * ss = declManager->FindSound( ent->spawnArgs.GetString( textKey ) ); 613 if ( ss->HasDefaultSound() || ss->base->GetState() == DS_DEFAULTED ) { 614 color.Set( 1.0f, 0.0f, 1.0f, 1.0f ); 615 } 616 } else if ( ent->GetType() == &idFuncEmitter::Type ) { 617 if ( ent->fl.selected ) { 618 drawArrows = true; 619 } 620 } 621 622 if ( !viewBounds.ContainsPoint( ent->GetPhysics()->GetOrigin() ) ) { 623 continue; 624 } 625 626 gameRenderWorld->DebugBounds( color, idBounds( ent->GetPhysics()->GetOrigin() ).Expand( 8 ) ); 627 if ( drawArrows ) { 628 idVec3 start = ent->GetPhysics()->GetOrigin(); 629 idVec3 end = start + idVec3( 1, 0, 0 ) * 20.0f; 630 gameRenderWorld->DebugArrow( colorWhite, start, end, 2 ); 631 gameRenderWorld->DrawText( "x+", end + idVec3( 4, 0, 0 ), 0.15f, colorWhite, axis ); 632 end = start + idVec3( 1, 0, 0 ) * -20.0f; 633 gameRenderWorld->DebugArrow( colorWhite, start, end, 2 ); 634 gameRenderWorld->DrawText( "x-", end + idVec3( -4, 0, 0 ), 0.15f, colorWhite, axis ); 635 end = start + idVec3( 0, 1, 0 ) * +20.0f; 636 gameRenderWorld->DebugArrow( colorGreen, start, end, 2 ); 637 gameRenderWorld->DrawText( "y+", end + idVec3( 0, 4, 0 ), 0.15f, colorWhite, axis ); 638 end = start + idVec3( 0, 1, 0 ) * -20.0f; 639 gameRenderWorld->DebugArrow( colorGreen, start, end, 2 ); 640 gameRenderWorld->DrawText( "y-", end + idVec3( 0, -4, 0 ), 0.15f, colorWhite, axis ); 641 end = start + idVec3( 0, 0, 1 ) * +20.0f; 642 gameRenderWorld->DebugArrow( colorBlue, start, end, 2 ); 643 gameRenderWorld->DrawText( "z+", end + idVec3( 0, 0, 4 ), 0.15f, colorWhite, axis ); 644 end = start + idVec3( 0, 0, 1 ) * -20.0f; 645 gameRenderWorld->DebugArrow( colorBlue, start, end, 2 ); 646 gameRenderWorld->DrawText( "z-", end + idVec3( 0, 0, -4 ), 0.15f, colorWhite, axis ); 647 } 648 649 if ( textKey.Length() ) { 650 const char *text = ent->spawnArgs.GetString( textKey ); 651 if ( viewTextBounds.ContainsPoint( ent->GetPhysics()->GetOrigin() ) ) { 652 gameRenderWorld->DrawText( text, ent->GetPhysics()->GetOrigin() + idVec3(0, 0, 12), 0.25, colorWhite, axis, 1 ); 653 } 654 } 655 } 656 } 657 658 659 /* 660 =============================================================================== 661 662 idGameEdit 663 664 =============================================================================== 665 */ 666 667 idGameEdit gameEditLocal; 668 idGameEdit * gameEdit = &gameEditLocal; 669 670 671 /* 672 ============= 673 idGameEdit::GetSelectedEntities 674 ============= 675 */ 676 int idGameEdit::GetSelectedEntities( idEntity *list[], int max ) { 677 int num = 0; 678 idEntity *ent; 679 680 for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) { 681 if ( ent->fl.selected ) { 682 list[num++] = ent; 683 if ( num >= max ) { 684 break; 685 } 686 } 687 } 688 return num; 689 } 690 691 /* 692 ============= 693 idGameEdit::TriggerSelected 694 ============= 695 */ 696 void idGameEdit::TriggerSelected() { 697 idEntity *ent; 698 for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) { 699 if ( ent->fl.selected ) { 700 ent->ProcessEvent( &EV_Activate, gameLocal.GetLocalPlayer() ); 701 } 702 } 703 } 704 705 /* 706 ================ 707 idGameEdit::ClearEntitySelection 708 ================ 709 */ 710 void idGameEdit::ClearEntitySelection() { 711 idEntity *ent; 712 713 for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) { 714 ent->fl.selected = false; 715 } 716 gameLocal.editEntities->ClearSelectedEntities(); 717 } 718 719 /* 720 ================ 721 idGameEdit::AddSelectedEntity 722 ================ 723 */ 724 void idGameEdit::AddSelectedEntity( idEntity *ent ) { 725 if ( ent ) { 726 gameLocal.editEntities->AddSelectedEntity( ent ); 727 } 728 } 729 730 /* 731 ================ 732 idGameEdit::FindEntityDefDict 733 ================ 734 */ 735 const idDict *idGameEdit::FindEntityDefDict( const char *name, bool makeDefault ) const { 736 return gameLocal.FindEntityDefDict( name, makeDefault ); 737 } 738 739 /* 740 ================ 741 idGameEdit::SpawnEntityDef 742 ================ 743 */ 744 void idGameEdit::SpawnEntityDef( const idDict &args, idEntity **ent ) { 745 gameLocal.SpawnEntityDef( args, ent ); 746 } 747 748 /* 749 ================ 750 idGameEdit::FindEntity 751 ================ 752 */ 753 idEntity *idGameEdit::FindEntity( const char *name ) const { 754 return gameLocal.FindEntity( name ); 755 } 756 757 /* 758 ============= 759 idGameEdit::GetUniqueEntityName 760 761 generates a unique name for a given classname 762 ============= 763 */ 764 const char *idGameEdit::GetUniqueEntityName( const char *classname ) const { 765 int id; 766 static char name[1024]; 767 768 // can only have MAX_GENTITIES, so if we have a spot available, we're guaranteed to find one 769 for( id = 0; id < MAX_GENTITIES; id++ ) { 770 idStr::snPrintf( name, sizeof( name ), "%s_%d", classname, id ); 771 if ( !gameLocal.FindEntity( name ) ) { 772 return name; 773 } 774 } 775 776 // id == MAX_GENTITIES + 1, which can't be in use if we get here 777 idStr::snPrintf( name, sizeof( name ), "%s_%d", classname, id ); 778 return name; 779 } 780 781 /* 782 ================ 783 idGameEdit::EntityGetOrigin 784 ================ 785 */ 786 void idGameEdit::EntityGetOrigin( idEntity *ent, idVec3 &org ) const { 787 if ( ent ) { 788 org = ent->GetPhysics()->GetOrigin(); 789 } 790 } 791 792 /* 793 ================ 794 idGameEdit::EntityGetAxis 795 ================ 796 */ 797 void idGameEdit::EntityGetAxis( idEntity *ent, idMat3 &axis ) const { 798 if ( ent ) { 799 axis = ent->GetPhysics()->GetAxis(); 800 } 801 } 802 803 /* 804 ================ 805 idGameEdit::EntitySetOrigin 806 ================ 807 */ 808 void idGameEdit::EntitySetOrigin( idEntity *ent, const idVec3 &org ) { 809 if ( ent ) { 810 ent->SetOrigin( org ); 811 } 812 } 813 814 /* 815 ================ 816 idGameEdit::EntitySetAxis 817 ================ 818 */ 819 void idGameEdit::EntitySetAxis( idEntity *ent, const idMat3 &axis ) { 820 if ( ent ) { 821 ent->SetAxis( axis ); 822 } 823 } 824 825 /* 826 ================ 827 idGameEdit::EntitySetColor 828 ================ 829 */ 830 void idGameEdit::EntitySetColor( idEntity *ent, const idVec3 color ) { 831 if ( ent ) { 832 ent->SetColor( color ); 833 } 834 } 835 836 /* 837 ================ 838 idGameEdit::EntityTranslate 839 ================ 840 */ 841 void idGameEdit::EntityTranslate( idEntity *ent, const idVec3 &org ) { 842 if ( ent ) { 843 ent->GetPhysics()->Translate( org ); 844 } 845 } 846 847 /* 848 ================ 849 idGameEdit::EntityGetSpawnArgs 850 ================ 851 */ 852 const idDict *idGameEdit::EntityGetSpawnArgs( idEntity *ent ) const { 853 if ( ent ) { 854 return &ent->spawnArgs; 855 } 856 return NULL; 857 } 858 859 /* 860 ================ 861 idGameEdit::EntityUpdateChangeableSpawnArgs 862 ================ 863 */ 864 void idGameEdit::EntityUpdateChangeableSpawnArgs( idEntity *ent, const idDict *dict ) { 865 if ( ent ) { 866 ent->UpdateChangeableSpawnArgs( dict ); 867 } 868 } 869 870 /* 871 ================ 872 idGameEdit::EntityChangeSpawnArgs 873 ================ 874 */ 875 void idGameEdit::EntityChangeSpawnArgs( idEntity *ent, const idDict *newArgs ) { 876 if ( ent ) { 877 for ( int i = 0 ; i < newArgs->GetNumKeyVals () ; i ++ ) { 878 const idKeyValue *kv = newArgs->GetKeyVal( i ); 879 880 if ( kv->GetValue().Length() > 0 ) { 881 ent->spawnArgs.Set ( kv->GetKey() ,kv->GetValue() ); 882 } else { 883 ent->spawnArgs.Delete ( kv->GetKey() ); 884 } 885 } 886 } 887 } 888 889 /* 890 ================ 891 idGameEdit::EntityUpdateVisuals 892 ================ 893 */ 894 void idGameEdit::EntityUpdateVisuals( idEntity *ent ) { 895 if ( ent ) { 896 ent->UpdateVisuals(); 897 } 898 } 899 900 /* 901 ================ 902 idGameEdit::EntitySetModel 903 ================ 904 */ 905 void idGameEdit::EntitySetModel( idEntity *ent, const char *val ) { 906 if ( ent ) { 907 ent->spawnArgs.Set( "model", val ); 908 ent->SetModel( val ); 909 } 910 } 911 912 /* 913 ================ 914 idGameEdit::EntityStopSound 915 ================ 916 */ 917 void idGameEdit::EntityStopSound( idEntity *ent ) { 918 if ( ent ) { 919 ent->StopSound( SND_CHANNEL_ANY, false ); 920 } 921 } 922 923 /* 924 ================ 925 idGameEdit::EntityDelete 926 ================ 927 */ 928 void idGameEdit::EntityDelete( idEntity *ent ) { 929 delete ent; 930 } 931 932 /* 933 ================ 934 idGameEdit::PlayerIsValid 935 ================ 936 */ 937 bool idGameEdit::PlayerIsValid() const { 938 return ( gameLocal.GetLocalPlayer() != NULL ); 939 } 940 941 /* 942 ================ 943 idGameEdit::PlayerGetOrigin 944 ================ 945 */ 946 void idGameEdit::PlayerGetOrigin( idVec3 &org ) const { 947 org = gameLocal.GetLocalPlayer()->GetPhysics()->GetOrigin(); 948 } 949 950 /* 951 ================ 952 idGameEdit::PlayerGetAxis 953 ================ 954 */ 955 void idGameEdit::PlayerGetAxis( idMat3 &axis ) const { 956 axis = gameLocal.GetLocalPlayer()->GetPhysics()->GetAxis(); 957 } 958 959 /* 960 ================ 961 idGameEdit::PlayerGetViewAngles 962 ================ 963 */ 964 void idGameEdit::PlayerGetViewAngles( idAngles &angles ) const { 965 angles = gameLocal.GetLocalPlayer()->viewAngles; 966 } 967 968 /* 969 ================ 970 idGameEdit::PlayerGetEyePosition 971 ================ 972 */ 973 void idGameEdit::PlayerGetEyePosition( idVec3 &org ) const { 974 org = gameLocal.GetLocalPlayer()->GetEyePosition(); 975 } 976 977 978 /* 979 ================ 980 idGameEdit::MapGetEntityDict 981 ================ 982 */ 983 const idDict *idGameEdit::MapGetEntityDict( const char *name ) const { 984 idMapFile *mapFile = gameLocal.GetLevelMap(); 985 if ( mapFile && name && *name ) { 986 idMapEntity *mapent = mapFile->FindEntity( name ); 987 if ( mapent ) { 988 return &mapent->epairs; 989 } 990 } 991 return NULL; 992 } 993 994 /* 995 ================ 996 idGameEdit::MapSave 997 ================ 998 */ 999 void idGameEdit::MapSave( const char *path ) const { 1000 idMapFile *mapFile = gameLocal.GetLevelMap(); 1001 if (mapFile) { 1002 mapFile->Write( (path) ? path : mapFile->GetName(), ".map"); 1003 } 1004 } 1005 1006 /* 1007 ================ 1008 idGameEdit::MapSetEntityKeyVal 1009 ================ 1010 */ 1011 void idGameEdit::MapSetEntityKeyVal( const char *name, const char *key, const char *val ) const { 1012 idMapFile *mapFile = gameLocal.GetLevelMap(); 1013 if ( mapFile && name && *name ) { 1014 idMapEntity *mapent = mapFile->FindEntity( name ); 1015 if ( mapent ) { 1016 mapent->epairs.Set( key, val ); 1017 } 1018 } 1019 } 1020 1021 /* 1022 ================ 1023 idGameEdit::MapCopyDictToEntity 1024 ================ 1025 */ 1026 void idGameEdit::MapCopyDictToEntity( const char *name, const idDict *dict ) const { 1027 idMapFile *mapFile = gameLocal.GetLevelMap(); 1028 if ( mapFile && name && *name ) { 1029 idMapEntity *mapent = mapFile->FindEntity( name ); 1030 if ( mapent ) { 1031 for ( int i = 0; i < dict->GetNumKeyVals(); i++ ) { 1032 const idKeyValue *kv = dict->GetKeyVal( i ); 1033 const char *key = kv->GetKey(); 1034 const char *val = kv->GetValue(); 1035 mapent->epairs.Set( key, val ); 1036 } 1037 } 1038 } 1039 } 1040 1041 1042 1043 /* 1044 ================ 1045 idGameEdit::MapGetUniqueMatchingKeyVals 1046 ================ 1047 */ 1048 int idGameEdit::MapGetUniqueMatchingKeyVals( const char *key, const char *list[], int max ) const { 1049 idMapFile *mapFile = gameLocal.GetLevelMap(); 1050 int count = 0; 1051 if ( mapFile ) { 1052 for ( int i = 0; i < mapFile->GetNumEntities(); i++ ) { 1053 idMapEntity *ent = mapFile->GetEntity( i ); 1054 if ( ent ) { 1055 const char *k = ent->epairs.GetString( key ); 1056 if ( k != NULL && *k != NULL && count < max ) { 1057 list[count++] = k; 1058 } 1059 } 1060 } 1061 } 1062 return count; 1063 } 1064 1065 /* 1066 ================ 1067 idGameEdit::MapAddEntity 1068 ================ 1069 */ 1070 void idGameEdit::MapAddEntity( const idDict *dict ) const { 1071 idMapFile *mapFile = gameLocal.GetLevelMap(); 1072 if ( mapFile ) { 1073 idMapEntity *ent = new (TAG_GAME) idMapEntity(); 1074 ent->epairs = *dict; 1075 mapFile->AddEntity( ent ); 1076 } 1077 } 1078 1079 /* 1080 ================ 1081 idGameEdit::MapRemoveEntity 1082 ================ 1083 */ 1084 void idGameEdit::MapRemoveEntity( const char *name ) const { 1085 idMapFile *mapFile = gameLocal.GetLevelMap(); 1086 if ( mapFile ) { 1087 idMapEntity *ent = mapFile->FindEntity( name ); 1088 if ( ent ) { 1089 mapFile->RemoveEntity( ent ); 1090 } 1091 } 1092 } 1093 1094 1095 /* 1096 ================ 1097 idGameEdit::MapGetEntitiesMatchignClassWithString 1098 ================ 1099 */ 1100 int idGameEdit::MapGetEntitiesMatchingClassWithString( const char *classname, const char *match, const char *list[], const int max ) const { 1101 idMapFile *mapFile = gameLocal.GetLevelMap(); 1102 int count = 0; 1103 if ( mapFile ) { 1104 int entCount = mapFile->GetNumEntities(); 1105 for ( int i = 0 ; i < entCount; i++ ) { 1106 idMapEntity *ent = mapFile->GetEntity(i); 1107 if (ent) { 1108 idStr work = ent->epairs.GetString("classname"); 1109 if ( work.Icmp( classname ) == 0 ) { 1110 if ( match && *match ) { 1111 work = ent->epairs.GetString( "soundgroup" ); 1112 if ( count < max && work.Icmp( match ) == 0 ) { 1113 list[count++] = ent->epairs.GetString( "name" ); 1114 } 1115 } else if ( count < max ) { 1116 list[count++] = ent->epairs.GetString( "name" ); 1117 } 1118 } 1119 } 1120 } 1121 } 1122 return count; 1123 } 1124 1125 1126 /* 1127 ================ 1128 idGameEdit::MapEntityTranslate 1129 ================ 1130 */ 1131 void idGameEdit::MapEntityTranslate( const char *name, const idVec3 &v ) const { 1132 idMapFile *mapFile = gameLocal.GetLevelMap(); 1133 if ( mapFile && name && *name ) { 1134 idMapEntity *mapent = mapFile->FindEntity( name ); 1135 if ( mapent ) { 1136 idVec3 origin; 1137 mapent->epairs.GetVector( "origin", "", origin ); 1138 origin += v; 1139 mapent->epairs.SetVector( "origin", origin ); 1140 } 1141 } 1142 }