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