]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/game/GameEdit.cpp
hello world
[icculus/iodoom3.git] / neo / game / GameEdit.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 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 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 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
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( void ) {
52         draggedPosition.Zero();
53 }
54
55 /*
56 ===============
57 idCursor3D::~idCursor3D
58 ===============
59 */
60 idCursor3D::~idCursor3D( void ) {
61 }
62
63 /*
64 ===============
65 idCursor3D::Spawn
66 ===============
67 */
68 void idCursor3D::Spawn( void ) {
69 }
70
71 /*
72 ===============
73 idCursor3D::Present
74 ===============
75 */
76 void idCursor3D::Present( void ) {
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( void ) {
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( void ) {
118         cursor = NULL;
119         Clear();
120 }
121
122 /*
123 ==============
124 idDragEntity::~idDragEntity
125 ==============
126 */
127 idDragEntity::~idDragEntity( void ) {
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( void ) {
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;
172         idAngles angles;
173         jointHandle_t newJoint;
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 && dragAnimator ) {
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( void ) {
307         delete selected.GetEntity();
308         selected = NULL;
309         StopDrag();
310 }
311
312 /*
313 ==============
314 idDragEntity::BindSelected
315 ==============
316 */
317 void idDragEntity::BindSelected( void ) {
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( void ) {
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( void ) {
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( void ) {
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 && *k && 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 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 }