#include "../idlib/precompiled.h" #pragma hdrstop #include "Game_local.h" // RAVEN BEGIN // bdube: added #include "Effect.h" // nmckenzie: //#include "rvAI/AI.h" #include "ai/AI.h" #include "client/ClientEffect.h" // RAVEN END /* =============================================================================== Ingame cursor. =============================================================================== */ CLASS_DECLARATION( idEntity, idCursor3D ) END_CLASS /* =============== idCursor3D::idCursor3D =============== */ idCursor3D::idCursor3D( void ) { draggedPosition.Zero(); } /* =============== idCursor3D::~idCursor3D =============== */ idCursor3D::~idCursor3D( void ) { } /* =============== idCursor3D::Spawn =============== */ void idCursor3D::Spawn( void ) { } /* =============== idCursor3D::Present =============== */ void idCursor3D::Present( void ) { // don't present to the renderer if the entity hasn't changed if ( !( thinkFlags & TH_UPDATEVISUALS ) ) { return; } BecomeInactive( TH_UPDATEVISUALS ); const idVec3 &origin = GetPhysics()->GetOrigin(); const idMat3 &axis = GetPhysics()->GetAxis(); gameRenderWorld->DebugArrow( colorYellow, origin + axis[1] * -5.0f + axis[2] * 5.0f, origin, 2 ); gameRenderWorld->DebugArrow( colorRed, origin, draggedPosition, 2 ); } /* =============== idCursor3D::Think =============== */ void idCursor3D::Think( void ) { if ( thinkFlags & TH_THINK ) { drag.Evaluate( gameLocal.time ); } Present(); } /* =============================================================================== Allows entities to be dragged through the world with physics. =============================================================================== */ #define MAX_DRAG_TRACE_DISTANCE 2048.0f /* ============== idDragEntity::idDragEntity ============== */ idDragEntity::idDragEntity( void ) { cursor = NULL; Clear(); } /* ============== idDragEntity::~idDragEntity ============== */ idDragEntity::~idDragEntity( void ) { StopDrag(); selected = NULL; delete cursor; cursor = NULL; } /* ============== idDragEntity::Clear ============== */ void idDragEntity::Clear() { dragEnt = NULL; joint = INVALID_JOINT; id = 0; localEntityPoint.Zero(); localPlayerPoint.Zero(); bodyName.Clear(); selected = NULL; } /* ============== idDragEntity::StopDrag ============== */ void idDragEntity::StopDrag( void ) { dragEnt = NULL; if ( cursor ) { cursor->BecomeInactive( TH_THINK ); } } /* ============== idDragEntity::Update ============== */ void idDragEntity::Update( idPlayer *player ) { idVec3 viewPoint, origin; idMat3 viewAxis, axis; trace_t trace; idEntity *newEnt; idAngles angles; jointHandle_t newJoint = INVALID_JOINT; idStr newBodyName; player->GetViewPos( viewPoint, viewAxis ); // if no entity selected for dragging if ( !dragEnt.GetEntity() ) { if ( player->usercmd.buttons & BUTTON_ATTACK ) { // RAVEN BEGIN // ddynerman: multiple clip worlds gameLocal.TracePoint( player, trace, viewPoint, viewPoint + viewAxis[0] * MAX_DRAG_TRACE_DISTANCE, (CONTENTS_SOLID|CONTENTS_RENDERMODEL|CONTENTS_BODY), player ); // RAVEN END if ( trace.fraction < 1.0f ) { newEnt = gameLocal.entities[ trace.c.entityNum ]; if ( newEnt ) { if ( newEnt->GetBindMaster() ) { if ( newEnt->GetBindJoint() ) { trace.c.id = JOINT_HANDLE_TO_CLIPMODEL_ID( newEnt->GetBindJoint() ); } else { trace.c.id = newEnt->GetBindBody(); } newEnt = newEnt->GetBindMaster(); } // RAVEN BEGIN // jnewquist: Use accessor for static class type if ( newEnt->IsType( idAFEntity_Base::GetClassType() ) && static_cast(newEnt)->IsActiveAF() ) { // RAVEN END idAFEntity_Base *af = static_cast(newEnt); // joint being dragged newJoint = CLIPMODEL_ID_TO_JOINT_HANDLE( trace.c.id ); // get the body id from the trace model id which might be a joint handle trace.c.id = af->BodyForClipModelId( trace.c.id ); // get the name of the body being dragged newBodyName = af->GetAFPhysics()->GetBody( trace.c.id )->GetName(); // RAVEN BEGIN // jnewquist: Use accessor for static class type } else if ( !newEnt->IsType( idWorldspawn::GetClassType() ) ) { // RAVEN END if ( trace.c.id < 0 ) { newJoint = CLIPMODEL_ID_TO_JOINT_HANDLE( trace.c.id ); } else { newJoint = INVALID_JOINT; } newBodyName = ""; } else { newJoint = INVALID_JOINT; newEnt = NULL; } } if ( newEnt ) { dragEnt = newEnt; selected = newEnt; joint = newJoint; id = trace.c.id; bodyName = newBodyName; if ( !cursor ) { // RAVEN BEGIN // jnewquist: Use accessor for static class type cursor = ( idCursor3D * )gameLocal.SpawnEntityType( idCursor3D::GetClassType() ); // RAVEN END } idPhysics *phys = dragEnt.GetEntity()->GetPhysics(); localPlayerPoint = ( trace.c.point - viewPoint ) * viewAxis.Transpose(); origin = phys->GetOrigin( id ); axis = phys->GetAxis( id ); localEntityPoint = ( trace.c.point - origin ) * axis.Transpose(); cursor->drag.Init( g_dragDamping.GetFloat() ); cursor->drag.SetPhysics( phys, id, localEntityPoint ); cursor->Show(); // RAVEN BEGIN // jnewquist: Use accessor for static class type if ( phys->IsType( idPhysics_AF::GetClassType() ) || phys->IsType( idPhysics_RigidBody::GetClassType() ) || phys->IsType( idPhysics_Monster::GetClassType() ) ) { // RAVEN END cursor->BecomeActive( TH_THINK ); } } } } } // if there is an entity selected for dragging idEntity *drag = dragEnt.GetEntity(); if ( drag ) { if ( !( player->usercmd.buttons & BUTTON_ATTACK ) ) { StopDrag(); return; } cursor->SetOrigin( viewPoint + localPlayerPoint * viewAxis ); cursor->SetAxis( viewAxis ); cursor->drag.SetDragPosition( cursor->GetPhysics()->GetOrigin() ); renderEntity_t *renderEntity = drag->GetRenderEntity(); idAnimator *dragAnimator = drag->GetAnimator(); if ( joint != INVALID_JOINT && renderEntity && dragAnimator ) { dragAnimator->GetJointTransform( joint, gameLocal.time, cursor->draggedPosition, axis ); cursor->draggedPosition = renderEntity->origin + cursor->draggedPosition * renderEntity->axis; 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 ); } else { cursor->draggedPosition = cursor->GetPhysics()->GetOrigin(); gameRenderWorld->DrawText( va( "%s\n%s\n%s", drag->GetName(), drag->GetType()->classname, bodyName.c_str() ), cursor->GetPhysics()->GetOrigin(), 0.1f, colorWhite, viewAxis, 1 ); } } // if there is a selected entity if ( selected.GetEntity() && g_dragShowSelection.GetBool() ) { // draw the bbox of the selected entity renderEntity_t *renderEntity = selected.GetEntity()->GetRenderEntity(); if ( renderEntity ) { gameRenderWorld->DebugBox( colorYellow, idBox( renderEntity->bounds, renderEntity->origin, renderEntity->axis ) ); } } } /* ============== idDragEntity::SetSelected ============== */ void idDragEntity::SetSelected( idEntity *ent ) { selected = ent; StopDrag(); } /* ============== idDragEntity::DeleteSelected ============== */ void idDragEntity::DeleteSelected( void ) { delete selected.GetEntity(); selected = NULL; StopDrag(); } /* ============== idDragEntity::BindSelected ============== */ void idDragEntity::BindSelected( void ) { int num, largestNum; idLexer lexer; idToken type, bodyName; idStr key, value, bindBodyName; const idKeyValue *kv; idAFEntity_Base *af; af = static_cast(dragEnt.GetEntity()); // RAVEN BEGIN // jnewquist: Use accessor for static class type if ( !af || !af->IsType( idAFEntity_Base::GetClassType() ) || !af->IsActiveAF() ) { // RAVEN END return; } bindBodyName = af->GetAFPhysics()->GetBody( id )->GetName(); largestNum = 1; // parse all the bind constraints kv = af->spawnArgs.MatchPrefix( "bindConstraint ", NULL ); while ( kv ) { key = kv->GetKey(); key.Strip( "bindConstraint " ); if ( sscanf( key, "bind%d", &num ) ) { if ( num >= largestNum ) { largestNum = num + 1; } } lexer.LoadMemory( kv->GetValue(), kv->GetValue().Length(), kv->GetKey() ); lexer.ReadToken( &type ); lexer.ReadToken( &bodyName ); lexer.FreeSource(); // if there already exists a bind constraint for this body if ( bodyName.Icmp( bindBodyName ) == 0 ) { // delete the bind constraint af->spawnArgs.Delete( kv->GetKey() ); kv = NULL; } kv = af->spawnArgs.MatchPrefix( "bindConstraint ", kv ); } sprintf( key, "bindConstraint bind%d", largestNum ); sprintf( value, "ballAndSocket %s %s", bindBodyName.c_str(), af->GetAnimator()->GetJointName( joint ) ); af->spawnArgs.Set( key, value ); af->spawnArgs.Set( "bind", "worldspawn" ); af->Bind( gameLocal.world, true ); } /* ============== idDragEntity::UnbindSelected ============== */ void idDragEntity::UnbindSelected( void ) { const idKeyValue *kv; idAFEntity_Base *af; af = static_cast(selected.GetEntity()); // RAVEN BEGIN // jnewquist: Use accessor for static class type if ( !af || !af->IsType( idAFEntity_Base::GetClassType() ) || !af->IsActiveAF() ) { // RAVEN END return; } // unbind the selected entity af->Unbind(); // delete all the bind constraints kv = selected.GetEntity()->spawnArgs.MatchPrefix( "bindConstraint ", NULL ); while ( kv ) { selected.GetEntity()->spawnArgs.Delete( kv->GetKey() ); kv = selected.GetEntity()->spawnArgs.MatchPrefix( "bindConstraint ", NULL ); } // delete any bind information af->spawnArgs.Delete( "bind" ); af->spawnArgs.Delete( "bindToJoint" ); af->spawnArgs.Delete( "bindToBody" ); } /* =============================================================================== Handles ingame entity editing. =============================================================================== */ /* ============== idEditEntities::idEditEntities ============== */ idEditEntities::idEditEntities( void ) { selectableEntityClasses.Clear(); nextSelectTime = 0; } // RAVEN BEGIN // bdube: made this special to edit entities /* ============= idEditEntities::FindTraceEntity ============= */ idEntity* idEditEntities::FindTraceEntity( idVec3 start, idVec3 end, const idEntity *skip ) { idEntity *ent; idEntity *bestEnt; float scale; float bestScale; idBounds b; bestEnt = NULL; bestScale = 1.0f; for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) { if ( ent != skip && EntityIsSelectable ( ent ) ) { b = ent->GetPhysics()->GetAbsBounds().Expand( 16 ); if ( b.RayIntersection( start, end-start, scale ) ) { if ( scale >= 0.0f && scale < bestScale ) { bestEnt = ent; bestScale = scale; } } } } return bestEnt; } // RAVEN END /* ============= idEditEntities::SelectEntity ============= */ bool idEditEntities::SelectEntity( const idVec3 &origin, const idVec3 &dir, const idEntity *skip ) { idVec3 end; idEntity *ent; if ( !g_editEntityMode.GetInteger() || selectableEntityClasses.Num() == 0 ) { return false; } if ( gameLocal.time < nextSelectTime ) { return true; } nextSelectTime = gameLocal.time + 300; end = origin + dir * 4096.0f; // RAVEN BEGIN // bdube: more generic ent = NULL; for ( int i = 0; i < selectableEntityClasses.Num(); i++ ) { ent = FindTraceEntity( origin, end, skip ); if ( ent ) { break; } } if ( !ent ) { return false; } ClearSelectedEntities(); AddSelectedEntity( ent ); gameLocal.Printf( "entity #%d: %s '%s'\n", ent->entityNumber, ent->GetClassname(), ent->name.c_str() ); if ( gameLocal.editors & EDITOR_ENTVIEW ) { common->InitTool ( EDITOR_ENTVIEW, &ent->spawnArgs ); } else { ent->ShowEditingDialog(); } return true; // RAVEN END } /* ============= idEditEntities::AddSelectedEntity ============= */ void idEditEntities::AddSelectedEntity(idEntity *ent) { ent->fl.selected = true; selectedEntities.AddUnique(ent); } /* ============== idEditEntities::RemoveSelectedEntity ============== */ void idEditEntities::RemoveSelectedEntity( idEntity *ent ) { if ( selectedEntities.Find( ent ) ) { selectedEntities.Remove( ent ); } } /* ============= idEditEntities::ClearSelectedEntities ============= */ void idEditEntities::ClearSelectedEntities() { int i, count; count = selectedEntities.Num(); for ( i = 0; i < count; i++ ) { selectedEntities[i]->fl.selected = false; } selectedEntities.Clear(); } /* ============= idEditEntities::EntityIsSelectable ============= */ bool idEditEntities::EntityIsSelectable( idEntity *ent, idVec4 *color, idStr *text ) { // RAVEN BEGIN // bdube: no matter what dont let the player or its entities be selectable idPlayer* player; if ( 0 != (player = gameLocal.GetLocalPlayer() )) { if ( ent == player || ent == player->GetWeaponViewModel ( ) || ent == player->GetWeaponWorldModel ( ) ) { return false; } } for ( int i = 0; i < selectableEntityClasses.Num(); i++ ) { if ( ent->IsType ( *selectableEntityClasses[i].typeInfo ) ) { // RAVEN END if ( text ) { *text = selectableEntityClasses[i].textKey; } if ( color ) { if ( ent->fl.selected ) { *color = colorRed; } else { switch( i ) { case 1 : *color = colorYellow; break; case 2 : *color = colorBlue; break; default: *color = colorGreen; } } } return true; } } return false; } /* ============= idEditEntities::DisplayEntities ============= */ void idEditEntities::DisplayEntities( void ) { idEntity *ent; if ( !gameLocal.GetLocalPlayer() ) { return; } selectableEntityClasses.Clear(); selectedTypeInfo_t sit; switch( g_editEntityMode.GetInteger() ) { case 1: // RAVEN BEGIN // jnewquist: Use accessor for static class type sit.typeInfo = &idLight::GetClassType(); // RAVEN END sit.textKey = "texture"; selectableEntityClasses.Append( sit ); break; case 2: // RAVEN BEGIN // jnewquist: Use accessor for static class type sit.typeInfo = &idSound::GetClassType(); // scork: added secondary "name" field as well (Zack request) sit.textKey = "s_shader|name"; selectableEntityClasses.Append( sit ); // scork: Zack (reasonably enough) doesn't want the lights displayed when editing sounds // sit.typeInfo = &idLight::GetClassType(); // sit.textKey = "texture"; // selectableEntityClasses.Append( sit ); // RAVEN END break; case 3: // RAVEN BEGIN // jnewquist: Use accessor for static class type sit.typeInfo = &idAFEntity_Base::GetClassType(); // RAVEN END sit.textKey = "articulatedFigure"; selectableEntityClasses.Append( sit ); break; case 4: // RAVEN BEGIN // jnewquist: Use accessor for static class type sit.typeInfo = &idFuncEmitter::GetClassType(); // RAVEN END sit.textKey = "model"; selectableEntityClasses.Append( sit ); break; case 5: // RAVEN BEGIN // jnewquist: Use accessor for static class type sit.typeInfo = &idAI::GetClassType(); // RAVEN END sit.textKey = "name"; selectableEntityClasses.Append( sit ); break; case 6: // RAVEN BEGIN // jnewquist: Use accessor for static class type sit.typeInfo = &idEntity::GetClassType(); // RAVEN END sit.textKey = "name"; selectableEntityClasses.Append( sit ); break; case 7: // RAVEN BEGIN // jnewquist: Use accessor for static class type sit.typeInfo = &idEntity::GetClassType(); // RAVEN END sit.textKey = "model"; selectableEntityClasses.Append( sit ); break; // RAVEN BEGIN // bdube: added fx entities case 8: // RAVEN BEGIN // jnewquist: Use accessor for static class type sit.typeInfo = &rvEffect::GetClassType(); // RAVEN END sit.textKey = "fx"; selectableEntityClasses.Append ( sit ); break; // RAVEN END default: return; } idBounds viewBounds( gameLocal.GetLocalPlayer()->GetPhysics()->GetOrigin() ); idBounds viewTextBounds( gameLocal.GetLocalPlayer()->GetPhysics()->GetOrigin() ); idMat3 axis = gameLocal.GetLocalPlayer()->viewAngles.ToMat3(); viewBounds.ExpandSelf( g_editEntityDistance.GetFloat() ); // RAVEN BEGIN // scork: changed from 128 to 256 so we can see speaker ent descriptions before getting right up to them // rhummer: Added cvar to adjust the distance for the text too. viewTextBounds.ExpandSelf( g_editEntityTextDistance.GetFloat() ); // RAVEN END idStr textKey; // RAVEN BEGIN // scork: secondary field idStr textKey2; idStr strOutput; // RAVEN END for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) { idVec4 color; textKey = ""; if ( !EntityIsSelectable( ent, &color, &textKey ) ) { continue; } // RAVEN BEGIN // scork: handle optional secondary field textKey2 = ""; int iIndex = textKey.Find('|'); if (iIndex >= 0) { textKey2 = textKey.Mid ( iIndex+1, textKey.Length()-(iIndex+1) ); // hmmm, they emulate 99% of MS CString but don't have a single-param Mid() func? textKey = textKey.Left( iIndex ); } // RAVEN END bool drawArrows = false; // RAVEN BEGIN // bdube: added bool drawDirection = false; // RAVEN END // RAVEN BEGIN // jnewquist: Use accessor for static class type if ( ent->GetType() == &idAFEntity_Base::GetClassType() ) { // RAVEN END if ( !static_cast(ent)->IsActiveAF() ) { continue; } // RAVEN BEGIN // jnewquist: Use accessor for static class type } else if ( ent->GetType() == &idSound::GetClassType() ) { // RAVEN END if ( ent->fl.selected ) { drawArrows = true; int iFlag = ent->GetRefSoundShaderFlags(); iFlag |= SSF_HILITE; ent->SetRefSoundShaderFlags( iFlag ); } else { int iFlag = ent->GetRefSoundShaderFlags(); iFlag &= ~SSF_HILITE; ent->SetRefSoundShaderFlags( iFlag ); } ent->UpdateSound(); const idSoundShader * ss = declManager->FindSound( ent->spawnArgs.GetString( textKey ) ); if ( ss->HasDefaultSound() || ss->base->GetState() == DS_DEFAULTED ) { color.Set( 1.0f, 0.0f, 1.0f, 1.0f ); } // RAVEN BEGIN // bdube: added // jnewquist: Use accessor for static class type } else if ( ent->GetType() == &rvEffect::GetClassType() ) { drawDirection = true; if ( ent->fl.selected ) { drawArrows = true; } } else if ( ent->GetType() == &idFuncEmitter::GetClassType() ) { // RAVEN END if ( ent->fl.selected ) { drawArrows = true; } } if ( !viewBounds.ContainsPoint( ent->GetPhysics()->GetOrigin() ) ) { continue; } gameRenderWorld->DebugBounds( color, idBounds( ent->GetPhysics()->GetOrigin() ).Expand( 8 ) ); if ( drawArrows ) { idVec3 start = ent->GetPhysics()->GetOrigin(); idVec3 end = start + idVec3( 1, 0, 0 ) * 20.0f; gameRenderWorld->DebugArrow( colorWhite, start, end, 2 ); gameRenderWorld->DrawText( "x+", end + idVec3( 4, 0, 0 ), 0.15f, colorWhite, axis ); end = start + idVec3( 1, 0, 0 ) * -20.0f; gameRenderWorld->DebugArrow( colorWhite, start, end, 2 ); gameRenderWorld->DrawText( "x-", end + idVec3( -4, 0, 0 ), 0.15f, colorWhite, axis ); end = start + idVec3( 0, 1, 0 ) * +20.0f; gameRenderWorld->DebugArrow( colorGreen, start, end, 2 ); gameRenderWorld->DrawText( "y+", end + idVec3( 0, 4, 0 ), 0.15f, colorWhite, axis ); end = start + idVec3( 0, 1, 0 ) * -20.0f; gameRenderWorld->DebugArrow( colorGreen, start, end, 2 ); gameRenderWorld->DrawText( "y-", end + idVec3( 0, -4, 0 ), 0.15f, colorWhite, axis ); end = start + idVec3( 0, 0, 1 ) * +20.0f; gameRenderWorld->DebugArrow( colorBlue, start, end, 2 ); gameRenderWorld->DrawText( "z+", end + idVec3( 0, 0, 4 ), 0.15f, colorWhite, axis ); end = start + idVec3( 0, 0, 1 ) * -20.0f; gameRenderWorld->DebugArrow( colorBlue, start, end, 2 ); gameRenderWorld->DrawText( "z-", end + idVec3( 0, 0, -4 ), 0.15f, colorWhite, axis ); } // RAVEN BEGIN // bdube: added if ( drawDirection ) { idVec3 start = ent->GetPhysics()->GetOrigin ( ); idVec3 end = start + ent->GetPhysics()->GetAxis()[0] * 35.0f; gameRenderWorld->DebugArrow ( colorYellow, start, end, 6 ); } // RAVEN END if ( textKey.Length() ) { // RAVEN BEGIN // scork: handle optional secondary field, plus only call GetString when bounds are within view if ( viewTextBounds.ContainsPoint( ent->GetPhysics()->GetOrigin() ) ) { strOutput = ent->spawnArgs.GetString( textKey ); if (!textKey2.IsEmpty()) { strOutput += " ( "; strOutput += ent->spawnArgs.GetString( textKey2 ); strOutput += " )"; } gameRenderWorld->DrawText( strOutput.c_str(), ent->GetPhysics()->GetOrigin() + idVec3(0, 0, 12), 0.25, colorWhite, axis, 1 ); } // RAVEN END } } } /* =============================================================================== idGameEdit =============================================================================== */ idGameEdit gameEditLocal; idGameEdit * gameEdit = &gameEditLocal; /* ============= idGameEdit::GetSelectedEntities ============= */ int idGameEdit::GetSelectedEntities( idEntity *list[], int max ) { int num = 0; idEntity *ent; for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) { if ( ent->fl.selected ) { list[num++] = ent; if ( num >= max ) { break; } } } return num; } /* ============= idGameEdit::TriggerSelected ============= */ void idGameEdit::TriggerSelected() { idEntity *ent; for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) { if ( ent->fl.selected ) { ent->ProcessEvent( &EV_Activate, gameLocal.GetLocalPlayer() ); } } } /* ================ idGameEdit::ClearEntitySelection ================ */ void idGameEdit::ClearEntitySelection() { idEntity *ent; for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) { ent->fl.selected = false; } // RAVEN BEGIN // bdube: fixed potential crash if ( gameLocal.editEntities ) { gameLocal.editEntities->ClearSelectedEntities(); } // RAVEN END } /* ================ idGameEdit::AddSelectedEntity ================ */ void idGameEdit::AddSelectedEntity( idEntity *ent ) { // RAVEN BEGIN // mekberg: fixed crash if ( ent && gameLocal.editEntities ) { gameLocal.editEntities->AddSelectedEntity( ent ); } // RAVEN END } /* ================ idGameEdit::FindEntityDefDict ================ */ const idDict *idGameEdit::FindEntityDefDict( const char *name, bool makeDefault ) const { return gameLocal.FindEntityDefDict( name, makeDefault ); } /* ================ idGameEdit::SpawnEntityDef ================ */ void idGameEdit::SpawnEntityDef( const idDict &args, idEntity **ent ) { gameLocal.SpawnEntityDef( args, ent ); } /* ================ idGameEdit::FindEntity ================ */ idEntity *idGameEdit::FindEntity( const char *name ) const { return gameLocal.FindEntity( name ); } /* ============= idGameEdit::GetUniqueEntityName generates a unique name for a given classname ============= */ const char *idGameEdit::GetUniqueEntityName( const char *classname ) const { int id; static char name[1024]; // can only have MAX_GENTITIES, so if we have a spot available, we're guaranteed to find one for( id = 0; id < MAX_GENTITIES; id++ ) { idStr::snPrintf( name, sizeof( name ), "%s_%d", classname, id ); if ( !gameLocal.FindEntity( name ) ) { return name; } } // id == MAX_GENTITIES + 1, which can't be in use if we get here idStr::snPrintf( name, sizeof( name ), "%s_%d", classname, id ); return name; } /* ================ idGameEdit::EntityGetOrigin ================ */ void idGameEdit::EntityGetOrigin( idEntity *ent, idVec3 &org ) const { if ( ent ) { org = ent->GetPhysics()->GetOrigin(); } } /* ================ idGameEdit::EntityGetAxis ================ */ void idGameEdit::EntityGetAxis( idEntity *ent, idMat3 &axis ) const { if ( ent ) { axis = ent->GetPhysics()->GetAxis(); } } /* ================ idGameEdit::EntitySetOrigin ================ */ void idGameEdit::EntitySetOrigin( idEntity *ent, const idVec3 &org ) { if ( ent ) { ent->SetOrigin( org ); } } /* ================ idGameEdit::EntitySetAxis ================ */ void idGameEdit::EntitySetAxis( idEntity *ent, const idMat3 &axis ) { if ( ent ) { ent->SetAxis( axis ); } } /* ================ idGameEdit::EntitySetColor ================ */ void idGameEdit::EntitySetColor( idEntity *ent, const idVec3 color ) { if ( ent ) { ent->SetColor( color ); } } /* ================ idGameEdit::EntityTranslate ================ */ void idGameEdit::EntityTranslate( idEntity *ent, const idVec3 &org ) { if ( ent ) { ent->GetPhysics()->Translate( org ); } } /* ================ idGameEdit::EntityGetSpawnArgs ================ */ // RAVEN BEGIN // scork: const-qualified 'ent' so other things would compile const idDict *idGameEdit::EntityGetSpawnArgs( const idEntity *ent ) const { // RAVEN END if ( ent ) { return &ent->spawnArgs; } return NULL; } /* ================ idGameEdit::EntityUpdateChangeableSpawnArgs ================ */ void idGameEdit::EntityUpdateChangeableSpawnArgs( idEntity *ent, const idDict *dict ) { if ( ent ) { ent->UpdateChangeableSpawnArgs( dict ); } } /* ================ idGameEdit::EntityChangeSpawnArgs ================ */ void idGameEdit::EntityChangeSpawnArgs( idEntity *ent, const idDict *newArgs ) { if ( ent ) { for ( int i = 0 ; i < newArgs->GetNumKeyVals () ; i ++ ) { const idKeyValue *kv = newArgs->GetKeyVal( i ); if ( kv->GetValue().Length() > 0 ) { ent->spawnArgs.Set ( kv->GetKey() ,kv->GetValue() ); } else { ent->spawnArgs.Delete ( kv->GetKey() ); } } } } /* ================ idGameEdit::EntityUpdateVisuals ================ */ void idGameEdit::EntityUpdateVisuals( idEntity *ent ) { if ( ent ) { ent->UpdateVisuals(); } } /* ================ idGameEdit::EntitySetModel ================ */ void idGameEdit::EntitySetModel( idEntity *ent, const char *val ) { if ( ent ) { ent->spawnArgs.Set( "model", val ); ent->SetModel( val ); } } /* ================ idGameEdit::EntityStopSound ================ */ void idGameEdit::EntityStopSound( idEntity *ent ) { if ( ent ) { ent->StopSound( SND_CHANNEL_ANY, false ); } } /* ================ idGameEdit::EntityDelete ================ */ void idGameEdit::EntityDelete( idEntity *ent ) { delete ent; } // RAVEN BEGIN // bdube: added /* ================ idGameEdit::EntityGetRenderEntity ================ */ renderEntity_t* idGameEdit::EntityGetRenderEntity ( idEntity* ent ) { return ent->GetRenderEntity(); } /* ================ idGameEdit::EntityGetName ================ */ const char* idGameEdit::EntityGetName ( idEntity* ent ) const { if ( !ent ) { return ""; } return ent->GetName(); } /* ================ idGameEdit::EntityGetClassname ================ */ const char* idGameEdit::EntityGetClassname ( idEntity* ent ) const { return ent->GetType()->classname; } /* ================ idGameEdit::EntityIsDerivedFrom ================ */ bool idGameEdit::EntityIsDerivedFrom ( idEntity* ent, const char* classname ) const { idTypeInfo* type; type = idClass::GetClass ( classname ); if ( !type ) { return false; } return ent->IsType ( *type ); } /* ================ idGameEdit::EntityIsValid ================ */ int idGameEdit::EntityToSafeId ( idEntity* ent ) const { if ( !ent ) { return 0; } return ( gameLocal.spawnIds[ent->entityNumber] << GENTITYNUM_BITS ) | ent->entityNumber; } /* ================ idGameEdit::EntityFromSafeId ================ */ idEntity *idGameEdit::EntityFromSafeId( int safeID ) const { int entityNum = safeID & ( ( 1 << GENTITYNUM_BITS ) - 1 ); if ( ( gameLocal.spawnIds[ entityNum ] == ( safeID >> GENTITYNUM_BITS ) ) ) { return gameLocal.entities[ entityNum ]; } return NULL; } /* ================ idGameEdit::EntitySetSkin ================ */ void idGameEdit::EntitySetSkin ( idEntity* ent, const char* temp ) const { ent->SetSkin ( declManager->FindSkin ( temp ) ); } /* ================ idGameEdit::EntityClearSkin ================ */ void idGameEdit::EntityClearSkin ( idEntity* ent ) const { ent->ClearSkin ( ); } /* ================ idGameEdit::EntityClearSkin ================ */ void idGameEdit::EntityShow ( idEntity* ent ) const { ent->Show ( ); } /* ================ idGameEdit::EntityClearSkin ================ */ void idGameEdit::EntityHide ( idEntity* ent ) const { ent->Hide ( ); } /* ================ idGameEdit::EntityGetBounds ================ */ void idGameEdit::EntityGetBounds ( idEntity* ent, idBounds &bounds ) const { bounds = ent->GetRenderEntity()->bounds; } /* ================ idGameEdit::EntityPlayAnim ================ */ int idGameEdit::EntityPlayAnim ( idEntity* ent, int animNum, int time, int blendtime ) { if ( !ent->GetAnimator ( ) ) { return 0; } ent->GetAnimator()->PlayAnim ( ANIMCHANNEL_ALL, animNum, time, blendtime ); ent->GetAnimator()->ServiceAnims ( time, time ); return ent->GetAnimator()->CurrentAnim( ANIMCHANNEL_ALL )->GetEndTime ( ); } /* ================ idGameEdit::EntitySetFrame ================ */ void idGameEdit::EntitySetFrame ( idEntity* ent, int animNum, int frame, int time, int blendtime ) { idAnimator* animator; animator = ent->GetAnimator ( ); if ( !animator ) { return; } animator->ClearAllAnims ( time, time ); // Move to the first frame of the animation // RAVEN BEGIN frameBlend_t frameBlend = { 0, frame, frame, 1.0f, 0 }; animator->SetFrame ( ANIMCHANNEL_ALL, animNum, frameBlend ); // RAVEN END animator->ForceUpdate ( ); } /* ================ idGameEdit::EntityGetDelta ================ */ void idGameEdit::EntityGetDelta ( idEntity* ent, int fromTime, int toTime, idVec3& delta ) { ent->GetAnimator()->GetDelta ( fromTime, toTime, delta ); } /* ================ idGameEdit::EntityRemoveOriginOffset ================ */ void idGameEdit::EntityRemoveOriginOffset ( idEntity* ent, bool remove ) { ent->GetAnimator()->RemoveOriginOffset ( remove ); } /* ================ idGameEdit::EntityStopAllEffects ================ */ void idGameEdit::EntityStopAllEffects ( idEntity* ent ) { ent->StopAllEffects ( ); ent->StopSound ( SND_CHANNEL_ANY, false ); } // RAVEN BEGIN // scork: some accessor functions for various utils idEntity *idGameEdit::EntityGetNextTeamEntity( idEntity *pEnt ) const { return pEnt->GetNextTeamEntity(); } void idGameEdit::GetPlayerInfo( idVec3 &v3Origin, idMat3 &mat3Axis, int PlayerNum, idAngles *deltaViewAngles ) const { game->GetPlayerInfo( v3Origin, mat3Axis, PlayerNum, deltaViewAngles ); } void idGameEdit::SetPlayerInfo( idVec3 &v3Origin, idMat3 &mat3Axis, int PlayerNum ) const { game->SetPlayerInfo( v3Origin, mat3Axis, PlayerNum ); } void idGameEdit::EntitySetName( idEntity* pEnt, const char *psName ) { pEnt->SetName( psName ); } // RAVEN END /* ================ idGameEdit::LightSetParms ================ */ void idGameEdit::LightSetParms ( idEntity* ent, int maxLevel, int currentLevel, float radius ) { int data; idLight* light; // Switch to a light entity light = dynamic_cast(ent); if ( !light ) { return; } light->ProcessEvent ( &EV_Light_SetMaxLightLevel, maxLevel ); light->ProcessEvent ( &EV_Light_SetCurrentLightLevel, (int)currentLevel ); (*(float*)&data) = radius; light->ProcessEventArgPtr ( &EV_Light_SetRadius, &data ); light->SetLightLevel(); } // RAVEN END /* ================ idGameEdit::PlayerIsValid ================ */ bool idGameEdit::PlayerIsValid() const { return ( gameLocal.GetLocalPlayer() != NULL ); } /* ================ idGameEdit::PlayerGetOrigin ================ */ void idGameEdit::PlayerGetOrigin( idVec3 &org ) const { org = gameLocal.GetLocalPlayer()->GetPhysics()->GetOrigin(); } /* ================ idGameEdit::PlayerGetAxis ================ */ void idGameEdit::PlayerGetAxis( idMat3 &axis ) const { axis = gameLocal.GetLocalPlayer()->GetPhysics()->GetAxis(); } /* ================ idGameEdit::PlayerGetViewAngles ================ */ void idGameEdit::PlayerGetViewAngles( idAngles &angles ) const { angles = gameLocal.GetLocalPlayer()->viewAngles; } /* ================ idGameEdit::PlayerGetEyePosition ================ */ void idGameEdit::PlayerGetEyePosition( idVec3 &org ) const { org = gameLocal.GetLocalPlayer()->GetEyePosition(); } /* ================ idGameEdit::MapGetEntityDict ================ */ const idDict *idGameEdit::MapGetEntityDict( const char *name ) const { idMapFile *mapFile = gameLocal.GetLevelMap(); if ( mapFile && name && *name ) { idMapEntity *mapent = mapFile->FindEntity( name ); if ( mapent ) { return &mapent->epairs; } } return NULL; } /* ================ idGameEdit::MapSave ================ */ void idGameEdit::MapSave( const char *path ) const { idMapFile *mapFile = gameLocal.GetLevelMap(); if (mapFile) { // RAVEN BEGIN // rjohnson: added entity export if (mapFile->HasExportEntities()) { mapFile->WriteExport( (path) ? path : mapFile->GetName() ); } else { if ( path ) { mapFile->Write( path, ".map"); } else { idStr osPath; osPath = mapFile->GetName ( ); osPath.DefaultFileExtension ( ".map" ); idFile* file = fileSystem->OpenFileRead ( osPath ); if ( file ) { osPath = file->GetFullPath ( ); fileSystem->CloseFile ( file ); mapFile->Write ( osPath, ".map", false ); } else { mapFile->Write ( file->GetName(), ".map" ); } } } // RAVEN END } } // RAVEN BEGIN // rjohnson: added entity export bool idGameEdit::MapHasExportEntities( void ) const { idMapFile *mapFile = gameLocal.GetLevelMap(); if (mapFile) { return mapFile->HasExportEntities(); } return false; } // scork: simple query function for the sound editor // cdr: changed to also return the full string name of the map file (still compatable as a bool test) const char* idGameEdit::MapLoaded( void ) const { const char *psMapName = gameLocal.GetMapName(); if (psMapName && psMapName[0]) { return psMapName; } return 0; } // cdr: AASTactical idAASFile* idGameEdit::GetAASFile( int i ) { if (gameLocal.GetAAS( i )) { return gameLocal.GetAAS( i )->GetFile(); } return 0; } // RAVEN END /* ================ idGameEdit::MapSetEntityKeyVal ================ */ void idGameEdit::MapSetEntityKeyVal( const char *name, const char *key, const char *val ) const { idMapFile *mapFile = gameLocal.GetLevelMap(); if ( mapFile && name && *name ) { idMapEntity *mapent = mapFile->FindEntity( name ); if ( mapent ) { mapent->epairs.Set( key, val ); } } } /* ================ idGameEdit::MapCopyDictToEntity ================ */ void idGameEdit::MapCopyDictToEntity( const char *name, const idDict *dict ) const { idMapFile *mapFile = gameLocal.GetLevelMap(); if ( mapFile && name && *name ) { idMapEntity *mapent = mapFile->FindEntity( name ); if ( mapent ) { for ( int i = 0; i < dict->GetNumKeyVals(); i++ ) { const idKeyValue *kv = dict->GetKeyVal( i ); const char *key = kv->GetKey(); const char *val = kv->GetValue(); mapent->epairs.Set( key, val ); } } } } /* ================ idGameEdit::MapGetUniqueMatchingKeyVals ================ */ int idGameEdit::MapGetUniqueMatchingKeyVals( const char *key, const char *list[], int max ) const { idMapFile *mapFile = gameLocal.GetLevelMap(); int count = 0; if ( mapFile ) { for ( int i = 0; i < mapFile->GetNumEntities(); i++ ) { idMapEntity *ent = mapFile->GetEntity( i ); if ( ent ) { const char *k = ent->epairs.GetString( key ); if ( k && *k && count < max ) { list[count++] = k; } } } } return count; } /* ================ idGameEdit::MapAddEntity ================ */ void idGameEdit::MapAddEntity( const idDict *dict ) const { idMapFile *mapFile = gameLocal.GetLevelMap(); if ( mapFile ) { idMapEntity *ent = new idMapEntity(); ent->epairs = *dict; mapFile->AddEntity( ent ); } } /* ================ idGameEdit::MapRemoveEntity ================ */ void idGameEdit::MapRemoveEntity( const char *name ) const { idMapFile *mapFile = gameLocal.GetLevelMap(); if ( mapFile ) { idMapEntity *ent = mapFile->FindEntity( name ); if ( ent ) { mapFile->RemoveEntity( ent ); } } } /* ================ idGameEdit::MapGetEntitiesMatchignClassWithString ================ */ int idGameEdit::MapGetEntitiesMatchingClassWithString( const char *classname, const char *match, const char *list[], const int max ) const { idMapFile *mapFile = gameLocal.GetLevelMap(); int count = 0; if ( mapFile ) { int entCount = mapFile->GetNumEntities(); for ( int i = 0 ; i < entCount; i++ ) { idMapEntity *ent = mapFile->GetEntity(i); if (ent) { idStr work = ent->epairs.GetString("classname"); if ( work.Icmp( classname ) == 0 ) { if ( match && *match ) { work = ent->epairs.GetString( "soundgroup" ); if ( count < max && work.Icmp( match ) == 0 ) { list[count++] = ent->epairs.GetString( "name" ); } } else if ( count < max ) { list[count++] = ent->epairs.GetString( "name" ); } } } } } return count; } /* ================ idGameEdit::MapEntityTranslate ================ */ void idGameEdit::MapEntityTranslate( const char *name, const idVec3 &v ) const { idMapFile *mapFile = gameLocal.GetLevelMap(); if ( mapFile && name && *name ) { idMapEntity *mapent = mapFile->FindEntity( name ); if ( mapent ) { idVec3 origin; mapent->epairs.GetVector( "origin", "", origin ); origin += v; mapent->epairs.SetVector( "origin", origin ); } } } // RAVEN BEGIN // bdube: new game edit stuff /* ================ idGameEdit::PlayerTraceFromEye ================ */ bool idGameEdit::PlayerTraceFromEye ( trace_t &results, float length, int contentMask ) { idVec3 start; idVec3 end; idAngles angles; PlayerGetEyePosition( start ); PlayerGetEyePosition( end ); PlayerGetViewAngles ( angles ); end += angles.ToForward() * length; // RAVEN BEGIN // ddynerman: multiple clip worlds return gameLocal.TracePoint ( gameLocal.GetLocalPlayer(), results, start, end, contentMask, gameLocal.GetLocalPlayer() ); // RAVEN END } /* ================ idGameEdit::EffectRefreshTemplate ================ */ void idGameEdit::EffectRefreshTemplate ( const idDecl *effect ) const { rvClientEntity* cent; // Restart all effects for ( cent = gameLocal.clientSpawnedEntities.Next(); cent; cent = cent->spawnNode.Next() ) { if ( cent->IsType ( rvClientEffect::GetClassType() ) ) { rvClientEffect* clientEffect; clientEffect = static_cast( cent ); if ( clientEffect->GetEffectIndex ( ) == effect->Index() ) { clientEffect->Restart ( ); } } } } /* ================ idGameEdit::GetGameTime ================ */ int idGameEdit::GetGameTime ( int *previous ) const { if ( previous ) { *previous = gameLocal.previousTime; } return gameLocal.time; } /* ================ idGameEdit::SetGameTime ================ */ void idGameEdit::SetGameTime ( int time ) const { gameLocal.time = time; gameLocal.previousTime = time; } /* ================ idGameEdit::TracePoint ================ */ bool idGameEdit::TracePoint ( trace_t &results, const idVec3 &start, const idVec3 &end, int contentMask ) const { // RAVEN BEGIN // ddynerman: multiple clip worlds return gameLocal.TracePoint( gameLocal.GetLocalPlayer(), results, start, end, contentMask, NULL ); // RAVEN END } /* ================ idGameEdit::CacheDictionaryMedia ================ */ void idGameEdit::CacheDictionaryMedia ( const idDict* dict ) const { gameLocal.CacheDictionaryMedia ( dict ); } /* ================ idGameEdit::SetCamera ================ */ void idGameEdit::SetCamera ( idEntity* camera ) const { gameLocal.SetCamera ( dynamic_cast(camera) ); } /* ================ idGameEdit::ScriptGetStatementLineNumber ================ */ int idGameEdit::ScriptGetStatementLineNumber ( idProgram* program, int instructionPointer ) const { return program->GetStatement ( instructionPointer ).linenumber; } /* ================ idGameEdit::ScriptGetStatementFileName ================ */ const char* idGameEdit::ScriptGetStatementFileName ( idProgram* program, int instructionPointer ) const { return program->GetFilename ( program->GetStatement ( instructionPointer ).file ); } /* ================ idGameEdit::ScriptGetStatementOperator ================ */ int idGameEdit::ScriptGetStatementOperator ( idProgram* program, int instructionPointer ) const { return program->GetStatement ( instructionPointer ).op; } /* ================ idGameEdit::ScriptGetCurrentFunction ================ */ void* idGameEdit::ScriptGetCurrentFunction ( idInterpreter* interpreter ) const { return (void*)interpreter->GetCurrentFunction ( ); } /* ================ idGameEdit::ScriptGetCurrentFunctionName ================ */ const char* idGameEdit::ScriptGetCurrentFunctionName ( idInterpreter* interpreter ) const { if ( interpreter->GetCurrentFunction ( ) ) { return interpreter->GetCurrentFunction ( )->Name(); } return ""; } /* ================ idGameEdit::ScriptGetStatementOperator ================ */ int idGameEdit::ScriptGetCallstackDepth ( idInterpreter* interpreter ) const { return interpreter->GetCallstackDepth ( ); } /* ================ idGameEdit::ScriptGetCallstackFunction ================ */ void* idGameEdit::ScriptGetCallstackFunction ( idInterpreter* interpreter, int depth ) const { return (void*)interpreter->GetCallstack ( )[depth].f; } /* ================ idGameEdit::ScriptGetCallstackFunctionName ================ */ const char* idGameEdit::ScriptGetCallstackFunctionName ( idInterpreter* interpreter, int depth ) const { return interpreter->GetCallstack()[depth].f->Name(); } /* ================ idGameEdit::ScriptGetCallstackStatement ================ */ int idGameEdit::ScriptGetCallstackStatement ( idInterpreter* interpreter, int depth ) const { return interpreter->GetCallstack()[depth].s; } /* ================ idGameEdit::ScriptIsReturnOperator ================ */ bool idGameEdit::ScriptIsReturnOperator ( int op ) const { return op == OP_RETURN; } /* ================ idGameEdit::ScriptGetRegisterValue ================ */ const char* idGameEdit::ScriptGetRegisterValue ( idInterpreter* interpreter, const char* varname, int callstackDepth ) const { static char value[4096]; idStr out; value[0] = '\0'; if ( interpreter->GetRegisterValue ( varname, out, callstackDepth ) ) { idStr::snPrintf ( value, 4095, out.c_str() ); } return value; } /* ================ idGameEdit::ScriptGetThread ================ */ idThread* idGameEdit::ScriptGetThread ( idInterpreter* interpreter ) const { return interpreter->GetThread(); } /* ================ idGameEdit::ThreadGetCount ================ */ int idGameEdit::ThreadGetCount ( void ) { return idThread::GetThreads().Num(); } /* ================ idGameEdit::ThreadGetThread ================ */ idThread* idGameEdit::ThreadGetThread ( int index ) { return idThread::GetThreads()[index]; } /* ================ idGameEdit::ThreadGetName ================ */ const char* idGameEdit::ThreadGetName ( idThread* thread ) { return thread->GetThreadName ( ); } /* ================ idGameEdit::ThreadGetNumber ================ */ int idGameEdit::ThreadGetNumber ( idThread* thread ) { return thread->GetThreadNum ( ); } /* ================ idGameEdit::ThreadGetState ================ */ const char* idGameEdit::ThreadGetState ( idThread* thread ) { if ( thread->IsDying() ) { return "Dying"; } else if ( thread->IsWaiting() ) { return "Waiting"; } else if ( thread->IsDoneProcessing() ) { return "Stopped"; } return "Running"; } /* ================ idGameEdit::GetClassDebugInfo ================ */ void idGameEdit::GetClassDebugInfo ( const idEntity* entity, debugInfoProc_t proc, void* userdata ) { const_cast( entity )->GetDebugInfo ( proc, userdata ); } /* ================ idGameEdit::GetGameEntityRegisterTime ================ */ int idGameEdit::GetGameEntityRegisterTime ( void ) const { return gameLocal.entityRegisterTime; } /* ================ idGameEdit::GetFirstSpawnedEntity ================ */ idEntity* idGameEdit::GetFirstSpawnedEntity ( void ) const { return gameLocal.spawnedEntities.Next(); } /* ================ idGameEdit::GetNextSpawnedEntity ================ */ idEntity* idGameEdit::GetNextSpawnedEntity ( idEntity* from ) const { if ( !from ) { return NULL; } return from->spawnNode.Next(); } // RAVEN END // RAVEN BEGIN // mekberg: access to animationlib functions for radiant void idGameEdit::FlushUnusedAnims ( void ) { // RAVEN BEGIN // jsinger: animationLib changed to a pointer animationLib->FlushUnusedAnims(); // RAVEN END } /* =============================================================================== rvModviewModel Actor model for modview =============================================================================== */ class rvModviewModel : public idActor { public: CLASS_PROTOTYPE( rvModviewModel ); rvModviewModel ( void ); private: void Event_Speak ( const char* lipsync ); }; CLASS_DECLARATION( idActor, rvModviewModel ) EVENT( AI_Speak, rvModviewModel::Event_Speak ) END_CLASS /* ===================== rvModviewModel::rvModviewModel ===================== */ rvModviewModel::rvModviewModel ( void ) { } /* ===================== rvModviewModel::Event_Speak ===================== */ void rvModviewModel::Event_Speak ( const char* lipsync ) { assert( idStr::Icmpn( lipsync, "lipsync_", 7 ) == 0 ); lipsync = spawnArgs.GetString ( lipsync ); if ( !lipsync || !*lipsync ) { return; } if ( head ) { head->StartLipSyncing( lipsync ); } else { StartSoundShader (declManager->FindSound ( lipsync ), SND_CHANNEL_VOICE, 0, false, NULL ); } } // RAVEN END