//----------------------------------------------------------------------------- // // $Logfile:: /EF2/Code/DLLs/game/viewthing.cpp $ // $Revision:: 46 $ // $Author:: Singlis $ // $Date:: 9/26/03 2:36p $ // // Copyright (C) 1997 by Ritual Entertainment, Inc. // All rights reserved. // // This source is may not be distributed and/or modified without // expressly written permission by Ritual Entertainment, Inc. // // // DESCRIPTION: // Actor code for the Viewthing. // #include "_pch_cpp.h" #include "entity.h" #include "viewthing.h" #include "player.h" #include "q_shared.h" #include // These command vars are used with the viewset and viewlist commands cvar_t *g_vt_droptoggle; // treated as a boolean. If true, we drop newly spawned things cvar_t *g_vt_setFilename ; // contains the filename of the current .lst file (if any) cvar_t *g_vt_modelIndex ; // Index specifying which model in the .lst file we are viewing cvar_t *g_vt_numModels ; // Number of total models in the .lst file cvar_t *g_vt_height ; // Z Height off 0 that we position spawned things cvar_t *g_vt_scale ; // Scale of spawned things cvar_t *g_vt_yaw ; // Current yaw value for spawned things cvar_t *g_vt_roll ; // Current roll value for spawned things cvar_t *g_vt_pitch ; // Current pitch value for spawned things cvar_t *g_vt_xtrans ; // Current x translation for current viewthing cvar_t *g_vt_ytrans ; // Current y translation for current viewthing cvar_t *g_vt_ztrans ; // Current z translation for current viewthing cvar_t *g_vt_makestatic ; // Toggle for controlling if spawned objects are "static" (static lighting) or not. cvar_t *g_vt_usebox ; // When shooting objects, objects pull away from wall by their bounding box. Defaults true. cvar_t *g_vt_viewcount ; // Holds number of spawned viewthings cvar_t *g_vt_viewnumber ; // Holds position of current_viewthing amongst all spawned ones cvar_t *g_vt_viewrandomize ; // If true (default is false) spawned things are affected by randomization settings (below) cvar_t *g_vt_viewsrandomize; // If true randomize scale (default true) cvar_t *g_vt_viewzrandomize; // If true randomize rotations about z axis (yaw) (default false) cvar_t *g_vt_viewyrandomize; // If true randomize rotations about y axis (pitch) (default false) cvar_t *g_vt_viewxrandomize; // If true randomize rotations about x axis (roll) (default false) cvar_t *g_vt_viewminrandomize;// Min value for randomizing scale (default is 50%) cvar_t *g_vt_viewmaxrandomize;// Max value for randomizing scale (default is 150%) cvar_t *g_vt_viewbellrandomize;// Set to true if you want scale randomizatins to be on a bell curve (default true) Event EV_ViewThing_Think ( "viewthing_think", EV_CODEONLY, NULL, NULL, "Called every frame to process the view thing." ); Event EV_ViewThing_ToggleAnimate ( "viewanimate", EV_CHEAT, NULL, NULL, "Cycle through the animations modes of the current viewthing\n" "No Animation\n" "Animation with no motion\n" "Animation with looping motion\n" "Animation with motion\n" ); Event EV_ViewThing_SetModel ( "viewmodel", EV_CHEAT, "s", "viewthingModel", "Set the model of the current viewthing" ); Event EV_ViewThing_NextFrame ( "viewnext", EV_CHEAT, NULL, NULL, "Advance to the next frame of animation of the current viewthing" ); Event EV_ViewThing_PrevFrame ( "viewprev", EV_CHEAT, NULL, NULL, "Advance to the previous frame of animation of the current viewthing" ); Event EV_ViewThing_NextAnim ( "viewnextanim", EV_CHEAT, NULL, NULL, "Advance to the next animation of the current viewthing" ); Event EV_ViewThing_PrevAnim ( "viewprevanim", EV_CHEAT, NULL, NULL, "Advance to the previous animation of the current viewthing" ); Event EV_ViewThing_SetFrame ( "viewsetframe", EV_CHEAT, NULL, NULL, "Set the animation to a specific frame number." ); Event EV_ViewThing_ScaleUp ( "viewscaleup", EV_CHEAT, NULL, NULL, "Increase the scale of the current viewthing" ); Event EV_ViewThing_ScaleDown ( "viewscaledown", EV_CHEAT, NULL, NULL, "Decrease the scale of the current viewthing" ); Event EV_ViewThing_SetScale ( "viewscale", EV_CHEAT, "f", "scale", "Set the scale of the current viewthing" ); Event EV_ViewThing_SetYaw ( "viewyaw", EV_CHEAT, "f", "yaw", "Set the yaw of the current viewthing" ); Event EV_ViewThing_SetPitch ( "viewpitch", EV_CHEAT, "f", "pitch", "Set the pitch of the current viewthing" ); Event EV_ViewThing_SetRoll ( "viewroll", EV_CHEAT, "f", "roll", "Set the roll of the current viewthing" ); Event EV_ViewThing_SetAngles ( "viewangles", EV_CHEAT, "f[0,360]f[0,360]f[0,360]", "pitch yaw roll", "Set the angles of the current viewthing" ); Event EV_ViewThing_TranslateX ( "viewxtranslate", EV_CHEAT, "f", "xtrans", "Translate current viewthing in x direction" ); Event EV_ViewThing_TranslateY ( "viewytranslate", EV_CHEAT, "f", "ytrans", "Translate current viewthing in y direction" ); Event EV_ViewThing_TranslateZ ( "viewztranslate", EV_CHEAT, "f", "ztrans", "Translate current viewthing in z direction" ); Event EV_ViewThing_Pull ( "viewthingpull", EV_CHEAT, NULL, NULL, "Pulls the current viewthing to the camera" ); Event EV_ViewThing_Spawn ( "viewspawn", EV_CHEAT, "s", "model", "Create a viewthing with the specified model" ); Event EV_ViewThing_SpawnFromTS ( "viewspawnfromts", EV_CHEAT, "sS", "reloadmodel spawnmodel", "Create a viewthing from the model specified by the ToolServer" ); Event EV_ViewThing_Next ( "viewthingnext", EV_CHEAT, NULL, NULL, "Change the active viewthing to the next viewthing" ); Event EV_ViewThing_Prev ( "viewthingprev", EV_CHEAT, NULL, NULL, "Change the active viewthing to the previous viewthing" ); Event EV_ViewThing_Attach ( "viewattach", EV_CHEAT, "ss", "tagname model", "Attach a model the the specified tagname" ); Event EV_ViewThing_Detach ( "viewdetach", EV_CHEAT, NULL, NULL, "Detach the current viewthing from its parent" ); Event EV_ViewThing_DetachAll ( "viewdetachall", EV_CHEAT, NULL, NULL, "Detach all the models attached to the current viewthing" ); Event EV_ViewThing_Delete ( "viewdelete", EV_CHEAT, NULL, NULL, "Delete the current viewthing" ); Event EV_ViewThing_SetStatic ( "viewsetstatic", EV_CHEAT, "i", "boolean", "Makes the current viewthing a static (not dynamically lit) object" ); Event EV_ViewThing_SetOrigin ( "vieworigin", EV_CHEAT, "fff", "x y z", "Set the origin of the current viewthing" ); Event EV_ViewThing_DeleteAll ( "viewdeleteall", EV_CHEAT, NULL, NULL, "Delete all viewthings" ); Event EV_ViewThing_LastFrame ( "viewlastframe", EV_CODEONLY, NULL, NULL, "Called when the view things last animation frame is displayed." ); Event EV_ViewThing_SaveOffSurfaces ( "viewsavesurfaces", EV_CODEONLY, NULL, NULL, "Called after the model is spawned to save off the models original surfaces." ); Event EV_ViewThing_PrintTime ( "_viewthing_printtime", EV_CODEONLY, NULL, NULL, "Prints out the current level.time." ); Event EV_ViewThing_SetAnim ( "viewsetanim", EV_CHEAT, "f", "animNum", "Set the animation absolutely based off a floating point value" ); Event EV_ViewThing_NextMorph ( "viewnextmorph", EV_CHEAT, NULL, NULL, "Goes to the next morph for this model" ); Event EV_ViewThing_PrevMorph ( "viewprevmorph", EV_CHEAT, NULL, NULL, "Goes to the previous morph for this model" ); Event EV_ViewThing_Morph ( "viewmorph", EV_CHEAT, NULL, NULL, "Morphs the current morph" ); Event EV_ViewThing_Unmorph ( "viewunmorph", EV_CHEAT, NULL, NULL, "Unmorphs the current morph" ); Event EV_ViewSet_Load ( "viewsetload", EV_CHEAT, "s", "filename", "Loads the specified viewset" ); Event EV_ViewSet_Save ( "viewsetsave", EV_CHEAT, NULL, NULL, "Saves names and positions of currently spawned things data to disk" ); Event EV_ViewThing_Copy ( "viewcopy", EV_CHEAT, NULL, NULL, "Copies the current viewthing" ); Event EV_ViewThing_Shoot ( "viewshoot", EV_CHEAT, NULL, NULL, "Shoots current viewthing ahead and sticks it on the wall" ); Event EV_ViewList_List ( "viewlistlist", EV_CHEAT, NULL, NULL, "List all TIKIs in the current .lst set" ); Event EV_ViewList_NextInSet ( "viewlistnext", EV_CHEAT, NULL, NULL, "Delete current viewthing and load next view thing in view list" ); Event EV_ViewList_PrevInSet ( "viewlistprev", EV_CHEAT, NULL, NULL, "Delete current viewthing and load prev view thing in view list" ); Event EV_ViewList_LiftModel ( "viewlistliftmodel", EV_CHEAT, "f", "z", "Raise the origin (z) by specified amount" ); Event EV_ViewList_ToggleDrop ( "viewlisttoggledrop", EV_CHEAT, NULL, NULL, "Toggle whether objects are dropped upon spawning" ); Event EV_ViewList_Jump ( "viewlistjump", EV_CHEAT, "i", "modelindex", "Jumps to a specific model in a view set (.lst file) of models" ); Event EV_ViewThing_ResetOrigin ( "viewresetorigin", EV_CHEAT, NULL, NULL, "Sets the origin of current viewspawn to its current origin" ); Event EV_ViewThing_XTrans ( "viewtransx", EV_CHEAT, "i", "offsetamount", "translate current viewspawn in x direction by specified amount" ); Event EV_ViewThing_YTrans ( "viewtransy", EV_CHEAT, "i", "offsetamount", "translate current viewspawn in y direction by specified amount" ); Event EV_ViewThing_ZTrans ( "viewtransz", EV_CHEAT, "i", "offsetamount", "translate current viewspawn in z direction by specified amount" ); Event EV_ViewThing_Offset ( "viewoffset", EV_CHEAT, "fff", "x y z", "Offsets the origin of the current viewthing" ); Event EV_ViewThing_ChangeRoll ( "viewchangeroll", EV_CHEAT, "i", "offsetamount", "rotate current viewspawn around x axis by specified amount" ); Event EV_ViewThing_ChangePitch ( "viewchangepitch", EV_CHEAT, "i", "offsetamount", "rotate current viewspawn around y axis by specified amount" ); Event EV_ViewThing_ChangeYaw ( "viewchangeyaw", EV_CHEAT, "i", "offsetamount", "rotate current viewspawn around z axis by specified amount" ); Event EV_ViewThing_Flash ( "viewflash", EV_CHEAT, NULL, NULL, "Flashes the currently select viewthing" ); CLASS_DECLARATION( Listener, ViewMaster, NULL ) { { &EV_ViewThing_Spawn, &ViewMaster::Spawn }, { &EV_ViewThing_SpawnFromTS, &ViewMaster::SpawnFromTS }, { &EV_ViewThing_Next, &ViewMaster::Next }, { &EV_ViewThing_Prev, &ViewMaster::Prev }, { &EV_ViewThing_SetModel, &ViewMaster::SetModelEvent }, { &EV_ViewThing_DeleteAll, &ViewMaster::DeleteAll }, { &EV_ViewThing_ToggleAnimate, &ViewMaster::PassEvent }, { &EV_ViewThing_NextFrame, &ViewMaster::PassEvent }, { &EV_ViewThing_PrevFrame, &ViewMaster::PassEvent }, { &EV_ViewThing_NextAnim, &ViewMaster::PassEvent }, { &EV_ViewThing_PrevAnim, &ViewMaster::PassEvent }, { &EV_ViewThing_ScaleUp, &ViewMaster::PassEvent }, { &EV_ViewThing_ScaleDown, &ViewMaster::PassEvent }, { &EV_ViewThing_SetScale, &ViewMaster::PassEvent }, { &EV_ViewThing_SetYaw, &ViewMaster::PassEvent }, { &EV_ViewThing_SetPitch, &ViewMaster::PassEvent }, { &EV_ViewThing_SetRoll, &ViewMaster::PassEvent }, { &EV_ViewThing_SetAngles, &ViewMaster::PassEvent }, { &EV_ViewThing_Attach, &ViewMaster::PassEvent }, { &EV_ViewThing_Detach, &ViewMaster::PassEvent }, { &EV_ViewThing_DetachAll, &ViewMaster::PassEvent }, { &EV_ViewThing_SetStatic, &ViewMaster::PassEvent }, { &EV_ViewThing_SetOrigin, &ViewMaster::PassEvent }, { &EV_ViewThing_SetAnim, &ViewMaster::PassEvent }, { &EV_ViewThing_NextMorph, &ViewMaster::PassEvent }, { &EV_ViewThing_PrevMorph, &ViewMaster::PassEvent }, { &EV_ViewThing_Morph, &ViewMaster::PassEvent }, { &EV_ViewThing_Unmorph, &ViewMaster::PassEvent }, { &EV_ViewThing_ChangeRoll, &ViewMaster::PassEvent }, { &EV_ViewThing_ChangePitch, &ViewMaster::PassEvent }, { &EV_ViewThing_ChangeYaw, &ViewMaster::PassEvent }, { &EV_ViewThing_Flash, &ViewMaster::PassEvent }, { &EV_ViewThing_SetFrame, &ViewMaster::PassEvent }, { &EV_ViewSet_Load, &ViewMaster::LoadSet }, { &EV_ViewSet_Save, &ViewMaster::Save }, { &EV_ViewThing_Copy, &ViewMaster::Copy }, { &EV_ViewThing_Shoot, &ViewMaster::Shoot }, { &EV_ViewThing_Delete, &ViewMaster::Delete }, { &EV_ViewThing_TranslateX, &ViewMaster::SetXTranslation }, { &EV_ViewThing_TranslateY, &ViewMaster::SetYTranslation }, { &EV_ViewThing_TranslateZ, &ViewMaster::SetZTranslation }, { &EV_ViewList_PrevInSet, &ViewMaster::PrevModelInSet }, { &EV_ViewList_NextInSet, &ViewMaster::NextModelInSet }, { &EV_ViewList_LiftModel, &ViewMaster::SetZTranslation }, { &EV_ViewList_ToggleDrop, &ViewMaster::ToggleDrop }, { &EV_ViewList_Jump, &ViewMaster::JumpToModel }, { &EV_ViewThing_Pull, &ViewMaster::PullToCamera }, { &EV_ViewThing_ResetOrigin, &ViewMaster::ResetOrigin }, { &EV_ViewThing_XTrans, &ViewMaster::XTranslate }, { &EV_ViewThing_YTrans, &ViewMaster::YTranslate }, { &EV_ViewThing_ZTrans, &ViewMaster::ZTranslate }, { &EV_ViewThing_Offset, &ViewMaster::Offset }, { NULL, NULL } }; ViewMaster Viewmodel; ViewMaster::ViewMaster() { current_viewthing = NULL; _spawnAtLastItemsOrigin = false ; _currentSetIdx = 0 ; _numberOfSets = 0 ; _numberOfModelsInSet = 0 ; } ViewMaster::~ViewMaster() { _setNamesArray.FreeObjectList(); _modelNamesArray.FreeObjectList(); } void ViewMaster::Init( void ) { gi.AddCommand( "viewanimate" ); gi.AddCommand( "viewmodel" ); gi.AddCommand( "viewnext" ); gi.AddCommand( "viewprev" ); gi.AddCommand( "viewnextanim" ); gi.AddCommand( "viewprevanim" ); gi.AddCommand( "viewscaleup" ); gi.AddCommand( "viewscaledown" ); gi.AddCommand( "viewscale" ); gi.AddCommand( "viewyaw" ); gi.AddCommand( "viewpitch" ); gi.AddCommand( "viewroll" ); gi.AddCommand( "viewangles" ); gi.AddCommand( "viewxtranslate" ); gi.AddCommand( "viewytranslate" ); gi.AddCommand( "viewztranslate" ); gi.AddCommand( "viewoffset" ); gi.AddCommand( "viewspawn" ); gi.AddCommand( "viewspawnfromts"); gi.AddCommand( "viewthingnext" ); gi.AddCommand( "viewthingprev" ); gi.AddCommand( "viewthingpull" ); gi.AddCommand( "viewattach" ); gi.AddCommand( "viewdetach" ); gi.AddCommand( "viewdetachall" ); gi.AddCommand( "viewdelete" ); gi.AddCommand( "viewsetstatic" ); gi.AddCommand( "vieworiginlift" ); gi.AddCommand( "vieworigin" ); gi.AddCommand( "viewdeleteall" ); gi.AddCommand( "viewsetanim" ); gi.AddCommand( "viewsetload" ); gi.AddCommand( "viewsetnext" ); gi.AddCommand( "viewsetprev" ); gi.AddCommand( "viewsetsave" ); gi.AddCommand( "viewcopy" ); gi.AddCommand( "viewlistlist" ); gi.AddCommand( "viewlistnext" ); gi.AddCommand( "viewlistprev" ); gi.AddCommand( "viewlistliftmodel" ); gi.AddCommand( "viewlisttoggledrop" ); gi.AddCommand( "viewlistjump" ); gi.AddCommand( "viewresetorigin" ); gi.AddCommand( "viewtransx" ); gi.AddCommand( "viewtransy" ); gi.AddCommand( "viewtransz" ); gi.AddCommand( "viewflash" ); gi.AddCommand( "viewsetframe" ); g_vt_droptoggle = gi.cvar( "g_vt_droptoggle", "1", 0 ); g_vt_setFilename = gi.cvar( "g_vt_setFilename", "", 0 ); g_vt_modelIndex = gi.cvar( "g_vt_modelIndex", "0", 0 ); g_vt_height = gi.cvar( "g_vt_height", "0", 0 ); g_vt_makestatic = gi.cvar( "g_vt_makestatic", "1", 0 ); g_vt_scale = gi.cvar( "g_vt_scale", "1.0", 0 ); g_vt_numModels = gi.cvar( "g_vt_numModels", "0", 0 ); g_vt_yaw = gi.cvar( "g_vt_yaw", "0.0", 0 ); g_vt_pitch = gi.cvar( "g_vt_pitch", "0.0", 0 ); g_vt_roll = gi.cvar( "g_vt_roll", "0.0", 0 ); g_vt_xtrans = gi.cvar( "g_vt_xtrans", "0.0", 0 ); g_vt_ytrans = gi.cvar( "g_vt_ytrans", "0.0", 0 ); g_vt_ztrans = gi.cvar( "g_vt_ztrans", "0.0", 0 ); g_vt_usebox = gi.cvar( "g_vt_usebox", "1", 0 ); g_vt_viewnumber = gi.cvar( "g_vt_viewnumber", "0", 0 ); g_vt_viewcount = gi.cvar( "g_vt_viewcount", "0", 0 ); g_vt_viewrandomize = gi.cvar( "g_vt_viewrandomize", "0", 0 ); g_vt_viewsrandomize = gi.cvar( "g_vt_viewsrandomize", "1", 0 ); g_vt_viewzrandomize = gi.cvar( "g_vt_viewzrandomize", "1", 0 ); g_vt_viewyrandomize = gi.cvar( "g_vt_viewyrandomize", "0", 0 ); g_vt_viewxrandomize = gi.cvar( "g_vt_viewxrandomize", "0", 0 ); g_vt_viewminrandomize = gi.cvar( "g_vt_viewminrandomize", "25", 0 ); g_vt_viewmaxrandomize = gi.cvar( "g_vt_viewmaxrandomize", "175", 0 ); g_vt_viewbellrandomize = gi.cvar( "g_vt_viewminrandomize", "1", 0 ); } void ViewMaster::Save( Event *ev ) { // saves currently spawned things to disk str buf; str filename; int count = 1 ; // get the name of the sound file from the world filename = "maps/"; filename += level.mapname; for(int i = filename.length() - 1; i >= 0; i-- ) { if ( filename[ i ] == '.' ) { filename[ i ] = 0; break; } } filename += "_ents.map"; gi.cvar_set("g_vt_savefile", filename.c_str()); gi.Printf( "Saving spawned things to '%s'...\n", filename.c_str() ); buf = ""; buf += va( "//\n" ); buf += va( "// Viewspawned entities file \"%s\".\n", filename.c_str()); buf += va( "//\n" ); Viewthing *viewthingPtr = 0 ; Entity *entityPtr = 0 ; entityPtr = G_FindClass( entityPtr, "viewthing" ); while (entityPtr != 0) { viewthingPtr = (Viewthing*)entityPtr; str modelName = viewthingPtr->model ; char* strPtr = (char*)modelName.c_str(); char* endPtr = strPtr + (strlen(strPtr) - 1) ; bool found = false ; for (int idx=strlen(strPtr); idx > 0; idx++) { if (*endPtr == '/') { endPtr++ ; found = true ; break ; } endPtr-- ; } if (found) { strPtr = endPtr ; } buf += va( "// Entity %d\n", count); buf += va( "{\n"); buf += va( "\"make_static\" \"%d\"\n", viewthingPtr->_static); buf += va( "\"classname\" \"FROM_TIKI\"\n" ); buf += va( "\"model\" \"%s\"\n", strPtr); buf += va( "\"scale\" \"%f\"\n", viewthingPtr->edict->s.scale); buf += va( "\"origin\" \"%.2f %.2f %.2f\"\n", viewthingPtr->origin.x, viewthingPtr->origin.y, viewthingPtr->origin.z ); buf += va( "\"angles\" \"%.2f %.2f %.2f\"\n", viewthingPtr->angles.x, viewthingPtr->angles.y, viewthingPtr->angles.z ); buf += va( "\"spawnflags\" \"1\"\n"); buf += va( "}\n" ); entityPtr = G_FindClass( entityPtr, "viewthing"); count++ ; } gi.FS_WriteFile( filename.c_str(), buf.c_str(), buf.length() + 1 ); gi.Printf( "Done.\n" ); } void ViewMaster::LoadSet( Event *ev ) { // If we have stuff loaded, delete it all if (_numberOfModelsInSet || _numberOfSets) { gi.cvar_set( "viewsetname", va( " ") ); gi.cvar_set( "viewlistmodelname", va( " ") ); gi.cvar_set( "g_vt_modelIndex", "0"); gi.cvar_set( "g_vt_setFilename", ""); _currentSetIdx = 0 ; _numberOfSets = 0 ; _numberOfModelsInSet = 0 ; _setNamesArray.FreeObjectList(); _modelNamesArray.FreeObjectList(); } str filename = ev->GetString(1); _setNamesArray.AddObject(filename); _numberOfSets++ ; // If we loaded any tiki filenames, display the first one if (_numberOfSets) { _currentSetIdx = 1 ; //str setFilename = _setNamesArray.ObjectAt(_currentSetIdx); LoadModelsInSet(_setNamesArray.ObjectAt(_currentSetIdx)); } } void ViewMaster::LoadModelsInSet( const str &setFileName ) { char *buffer; char *buf; char com_token[MAX_STRING_CHARS]; if (_numberOfModelsInSet) { gi.cvar_set( "viewlistmodelname", va( " ") ); gi.cvar_set( "g_vt_modelIndex", "0"); _modelNamesArray.FreeObjectList(); _numberOfModelsInSet = 0 ; } // Read the viewlist.txt file. This file contains newline seperated list of .tik models if ( gi.FS_ReadFile( setFileName, ( void ** )&buf, true ) != -1 ) { buffer = buf; while ( 1 ) { // Pull out a token strcpy( com_token, COM_ParseExt( &buffer, true ) ); // Quit when no more tokens if (!com_token[0]) break; // Check for single comment lines if ((com_token[0] != '#')) { // # is a single line comment const char *relative_pathname = NULL; str modelName = "models/" ; relative_pathname = strstr( com_token, "models/" ); if ( !relative_pathname ) relative_pathname = strstr( com_token, "models\\" ); if ( relative_pathname ) { relative_pathname += strlen( "models/" ); modelName += relative_pathname; } else { modelName += com_token; } _modelNamesArray.AddObject(modelName); _numberOfModelsInSet++ ; } // Read in the rest of the line while( 1 ) { strcpy( com_token, COM_ParseExt( &buffer, false ) ); if (!com_token[0]) break; } } gi.FS_FreeFile( buf ); // If we loaded any tiki filenames, display the first one if (_numberOfModelsInSet) { gi.cvar_set( "g_vt_modelIndex", "1"); DisplayCurrentModelInSet(false) ; } str setName = _setNamesArray.ObjectAt(_currentSetIdx); gi.cvar_set( "viewsetname", va( "%s", setName.c_str()) ); gi.cvar_set( "g_vt_numModels", va( "%d", _numberOfModelsInSet) ); } } void ViewMaster::DisplayCurrentModelInSet( bool spawnAtLastOrigin ) { if ( (g_vt_modelIndex->integer <= 0) || (g_vt_modelIndex->integer > _numberOfModelsInSet) ) { gi.cvar_set( "viewlistmodelname", " " ); gi.Printf( " No models in set or position out of range\n"); return ; } // Get the string name of the view item str modelName = _modelNamesArray.ObjectAt(g_vt_modelIndex->integer); // Create a new spawn event with item name to send to Spawn function Event* spawnEvent = new Event( EV_ViewThing_Spawn); spawnEvent->AddString(modelName); _spawnAtLastItemsOrigin = spawnAtLastOrigin ; Spawn(spawnEvent); _spawnAtLastItemsOrigin = false ; delete spawnEvent ; spawnEvent = 0 ; gi.cvar_set( "viewlistnumitems", va( "%d", _numberOfModelsInSet ) ); gi.cvar_set( "viewlistmodelname", modelName.c_str() ); gi.cvar_set( "g_vt_scale", "1.0"); gi.cvar_set( "g_vt_height", "0" ); } //================================================================ // Name: Copy // Class: ViewMaster // // Description: Event function to spawn a duplicate of the current // viewthing at the spot occupied by the player. // // The object should be a true copy, including rotation // and scaling. But currently isn't. FIXME // // Parameters: Event* -- Not used // // Returns: None // //================================================================ void ViewMaster::Copy( Event * ) { DisplayCurrentModelInSet(false); _updateViewthingCounts(); } //================================================================ // Name: Shoot // Class: ViewMaster // // Description: Event function to spawn a duplicate of the current // viewthing at the spot aimed by the player. The new // viewthing is not copied in scale or rotation. // // Parameters: Event* -- Not used // // Returns: None // //================================================================ void ViewMaster::Shoot( Event *ev ) { int idx = g_vt_modelIndex->integer ; if ( (idx <= 0) || (idx > _modelNamesArray.NumObjects()) ) return ; Vector forward ; Vector right ; Vector up ; Vector view ; Vector start; Vector end; Vector mins = vec_zero ; Vector maxs = vec_zero ; trace_t trace; // Check if we have a client Player* player = (Player*)g_entities[ 0 ].entity; if ( !player ) return; Viewthing* viewthing = 0 ; if (current_viewthing && g_vt_usebox->integer) { viewthing = (Viewthing*) ( (Entity*)current_viewthing); mins = viewthing->mins ; maxs = viewthing->maxs ; } view = player->GetVAngles(); AngleVectors( view, forward, right, up ); start = player->origin ; start.z += player->viewheight ; end = start + ( forward * 10000.0f ) ; trace = G_FullTrace( start, mins, maxs, end, player, MASK_SHOT, false, "ViewMaster::shoot" ); if ( ( trace.fraction == 1.0f ) || trace.allsolid || !trace.ent ) { return ; } if ( g_showbullettrace->integer ) { G_DebugLine( start, end, 1.0f, 1.0f, 1.0f, 1.0f ); } Vector pos = Vector( trace.endpos ) + ( Vector( trace.plane.normal ) * 0.2f ); str modelName(_modelNamesArray.ObjectAt(g_vt_modelIndex->integer)); spawnAtPosition(modelName, pos); if (current_viewthing) { Event* yawEvent = new Event(EV_ViewThing_SetYaw); viewthing = (Viewthing*) ( (Entity*)current_viewthing); Vector n(trace.plane.normal); float yaw = n.toYaw(); yawEvent->AddFloat(yaw); viewthing->SetYawEvent(yawEvent); delete yawEvent ; yawEvent = 0 ; if (g_vt_viewrandomize->integer) { _randomizeViewthing(viewthing); } } } //================================================================ // Name: NextModelInSet // Class: ViewMaster // // Description: Event function to switch current_viewthing to the // next model in the set (if any). This will switch // the current tiki model to be the next tiki model in // the loaded .lst file. It does not change the number // of total viewthings in the world. Wraps when at // end of list. // // Parameters: Event* -- Not used // // Returns: None // //================================================================ void ViewMaster::NextModelInSet( Event * ) { if (!_numberOfModelsInSet) { gi.Printf(" No current set\n"); return ; } // Increment on to next model int nextIndex = ( g_vt_modelIndex->integer >= _numberOfModelsInSet) ? 1 : (g_vt_modelIndex->integer + 1 ); gi.cvar_set( "g_vt_modelIndex", va( "%d", nextIndex ) ); DeleteCurrentViewthing(); DisplayCurrentModelInSet(); } //================================================================ // Name: PrevModelInSet // Class: ViewMaster // // Description: Event function to switch current_viewthing to the // prev model in the set (if any). This will switch // the current tiki model to be the prev tiki model in // the loaded .lst file. It does not change the number // of total viewthings in the world. Wraps when at // start of list. // // Parameters: Event* -- Not used // // Returns: None // //================================================================ void ViewMaster::PrevModelInSet( Event *ev ) { if (!_numberOfModelsInSet) { gi.Printf(" No models in current set\n"); return ; } // Increment on to next model int nextIndex = ( g_vt_modelIndex->integer <= 1) ? _numberOfModelsInSet : ( g_vt_modelIndex->integer - 1 ); gi.cvar_set( "g_vt_modelIndex", va( "%d", nextIndex ) ); DeleteCurrentViewthing(); DisplayCurrentModelInSet() ; } //================================================================ // Name: JumpToModel // Class: ViewMaster // // Description: Explicity function to jump to the tiki specified // by number out of the total list of tiki's in the // load .lst file. This enables random access thru // the .lst instead of one at a time. // // Parameters: Event* -- first argument is position to jump to // // Returns: None // //================================================================ void ViewMaster::JumpToModel( Event *ev ) { int nextIndex = ev->GetInteger(1); if ((nextIndex < 1) || (nextIndex > _numberOfModelsInSet)) { gi.Printf(" Illegal index for model in viewset list\n"); return ; } gi.cvar_set( "g_vt_modelIndex", va( "%d", nextIndex ) ); DeleteCurrentViewthing(); DisplayCurrentModelInSet(); } void ViewMaster::SetXTranslation( Event *ev ) { if (!current_viewthing) return ; Viewthing *viewthing = ( Viewthing * )( ( Entity * )current_viewthing ); Event *originEvent = new Event(EV_ViewThing_SetOrigin); float y = viewthing->origin.y ; float z = viewthing->origin.z ; originEvent->AddFloat(_lastBaseOrigin.x + ev->GetFloat(1)); originEvent->AddFloat(y); originEvent->AddFloat(z); viewthing->ChangeOrigin(originEvent); delete originEvent ; originEvent = 0 ; gi.cvar_set( "g_vt_xtrans", va("%0.2f", ev->GetFloat(1)) ); } void ViewMaster::XTranslate( Event *ev ) { if (!current_viewthing) return ; Viewthing *viewthing = ( Viewthing * )( ( Entity * )current_viewthing ); Event *originEvent = new Event(EV_ViewThing_SetOrigin); originEvent->AddFloat(viewthing->origin.x + ev->GetFloat(1)); originEvent->AddFloat(viewthing->origin.y); originEvent->AddFloat(viewthing->origin.z); viewthing->ChangeOrigin(originEvent); delete originEvent ; originEvent = 0 ; _resetOrigin(); } void ViewMaster::SetYTranslation( Event *ev ) { if (!current_viewthing) return ; Viewthing *viewthing = ( Viewthing * )( ( Entity * )current_viewthing ); Event *originEvent = new Event(EV_ViewThing_SetOrigin); float x = viewthing->origin.x ; float z = viewthing->origin.z ; originEvent->AddFloat(x); originEvent->AddFloat(_lastBaseOrigin.y + ev->GetFloat(1)); originEvent->AddFloat(z); viewthing->ChangeOrigin(originEvent); delete originEvent ; originEvent = 0 ; gi.cvar_set( "g_vt_ytrans", va("%0.2f", ev->GetFloat(1)) ); } void ViewMaster::YTranslate( Event *ev ) { if (!current_viewthing) return ; Viewthing *viewthing = ( Viewthing * )( ( Entity * )current_viewthing ); Event *originEvent = new Event(EV_ViewThing_SetOrigin); originEvent->AddFloat(viewthing->origin.x); originEvent->AddFloat(viewthing->origin.y + ev->GetFloat(1)); originEvent->AddFloat(viewthing->origin.z); viewthing->ChangeOrigin(originEvent); delete originEvent ; originEvent = 0 ; _resetOrigin(); } void ViewMaster::SetZTranslation( Event *ev ) { if (!current_viewthing) return ; Viewthing *viewthing = ( Viewthing * )( ( Entity * )current_viewthing ); Event *originEvent = new Event(EV_ViewThing_SetOrigin); float x = viewthing->origin.x ; float y = viewthing->origin.y ; originEvent->AddFloat(x); originEvent->AddFloat(y); originEvent->AddFloat(_lastBaseOrigin.z + ev->GetFloat(1)); viewthing->ChangeOrigin(originEvent); delete originEvent ; originEvent = 0 ; gi.cvar_set( "g_vt_ztrans", va("%0.2f", ev->GetFloat(1)) ); } void ViewMaster::ZTranslate( Event *ev ) { if (!current_viewthing) return ; Viewthing *viewthing = ( Viewthing * )( ( Entity * )current_viewthing ); Event *originEvent = new Event(EV_ViewThing_SetOrigin); originEvent->AddFloat(viewthing->origin.x); originEvent->AddFloat(viewthing->origin.y); originEvent->AddFloat(viewthing->origin.z + ev->GetFloat(1)); viewthing->ChangeOrigin(originEvent); delete originEvent ; originEvent = 0 ; _resetOrigin(); } void ViewMaster::Offset( Event *ev ) { Viewthing *viewthing; Event *new_event; if ( !current_viewthing ) return; viewthing = ( Viewthing * )( Entity * )current_viewthing; new_event = new Event( EV_ViewThing_SetOrigin ); new_event->AddFloat( viewthing->origin.x + ev->GetFloat( 1 ) ); new_event->AddFloat( viewthing->origin.y + ev->GetFloat( 2 ) ); new_event->AddFloat( viewthing->origin.z + ev->GetFloat( 3 ) ); viewthing->ProcessEvent( new_event ); _resetOrigin(); } //================================================================ // Name: ResetOrigin // Class: ViewMaster // // Description: Forces the _lastBaseOrigin to be the origin of the // current_viewthing (if any). Calls _resetOrigin to // do the work. // // This is useful because models can be translated off // of _lastBaseOrigin and reset to it. This enables // the _lastBaseOrigin to move to where ever the viewthing // has been translated too so further relative movements are // relative to this new position. // // Parameters: Event* -- Not used // // Returns: None // //================================================================ void ViewMaster::ResetOrigin( Event* ) { _resetOrigin(); } //================================================================ // Name: PullToCamera // Class: ViewMaster // // Description: Moves the current_viewthing (if any) to the current // player's (client[0]) position. Does not drop the // object but instead explicitly calls _setToPosition // to force the origin of the current_viewthing to be // at player's positionn. // // Parameters: Event* -- Not used // // Returns: None // //================================================================ void ViewMaster::PullToCamera( Event* ) { if (!current_viewthing) return ; // Check if we have a client Entity* clientEntity = g_entities[ 0 ].entity; if (!clientEntity) return ; // Store this new camera position as being where items should spawn here on out _lastBaseOrigin = clientEntity->origin ; _setToPosition(clientEntity->origin); } //================================================================ // Name: ToggleDrop // Class: ViewMaster // // Description: Toggles the g_vt_droptoggle cvar. When the // cvar is true, objects are dropped upon spawning. // // Parameters: Event* -- Not used // // Returns: None // //================================================================ void ViewMaster::ToggleDrop( Event * ) { if (g_vt_droptoggle->integer) { gi.cvar_set("g_vt_droptoggle", "0"); } else { gi.cvar_set("g_vt_droptoggle", "1"); } } //================================================================ // Name: Next // Class: ViewMaster // // Description: Takes an event and moves to next spawned viewthing // if there is one. Does not change current_viewthing // if there isn't a next viewthing. // // Parameters: Event* -- Not used // // Returns: None // //================================================================ void ViewMaster::Next( Event * ) { _selectNext(); } //================================================================ // Name: Prev // Class: ViewMaster // // Description: Responds to an event and moves to previous spawned // viewthing if there is one. Does not change current_viewthing // if there isn't a previous viewthing. // // Parameters: Event* -- Not used // // Returns: None // //================================================================ void ViewMaster::Prev( Event * ) { _selectPrevious(); } //================================================================ // Name: DeleteCurrentViewthing // Class: ViewMaster // // Description: Support function that simply sends a EV_Remove event // to the current viewthing if there is one. This will // delete the current viewthing. // // Does not modify current_viewthing nor the count of // viewthings because it is expected that the caller // handles this. // // Parameters: None // // Returns: None // //================================================================ void ViewMaster::DeleteCurrentViewthing() { if (current_viewthing) { // delete all existing models Viewthing *viewthing = (Viewthing*)( (Entity*) current_viewthing); viewthing->PostEvent( EV_Remove, 0.0f ); } } //================================================================ // Name: Delete // Class: ViewMaster // // Description: Deletes the current_viewthing if there is one. Attempts // to set the current_viewthing to the previous one in the // list of viewthings. If it cannot do that, it sets it // to the next one. If it cannot do that, it sets current_viewthing // to 0 (NULL). // // Parameters: Event* -- Not used // // Returns: None // //================================================================ void ViewMaster::Delete( Event * ) { if (!current_viewthing) return ; // Save off the one to delete Viewthing *viewthing = (Viewthing*)( (Entity*) current_viewthing); _selectPrevious(); // Try and select previous if (current_viewthing == viewthing) // If same as one we are deleting, there wasn't a previous { _selectNext(); // Try and select next } if (current_viewthing == viewthing) // If same as one we are deleting, there wasn't a next { current_viewthing = 0 ; // set it to 0 } // Delete the one we saved off viewthing->PostEvent( EV_Remove, 0.0f ); _updateViewthingCounts(-1); // we pass -1 here because there will be one less viewthing but it hasn't actually been deleted yet } //================================================================ // Name: DeleteAll // Class: ViewMaster // // Description: Deletes all viewspawn things currently in memory. // // Parameters: None // // Returns: None // //================================================================ void ViewMaster::DeleteAll( Event * ) { Entity *next; for( next = G_FindClass( NULL, "viewthing" ); next != NULL; next = G_FindClass( next, "viewthing" ) ) { next->PostEvent( EV_Remove, 0.0f ); } current_viewthing = NULL; } //================================================================ // Name: spawnAtPosition // Class: ViewMaster // // Description: Spawns the specified tiki model at the specified // position. The current_viewthing will point to this // new viewthing if the spawn succeeds, 0 if it doesn't. // // Parameters: const str& -- name of tiki model ("models/eliza.tik") // const Vector& -- Position to spawn model at // // Returns: None // //================================================================ void ViewMaster::spawnAtPosition( const str& modelName, const Vector& pos ) { // create a new viewthing Viewthing *viewthing = new Viewthing; current_viewthing = viewthing; viewthing->baseorigin = pos ; viewthing->setOrigin( viewthing->baseorigin ); _lastBaseOrigin = viewthing->baseorigin ; if (g_vt_droptoggle->integer) { viewthing->droptofloor( 10000.0f ); } viewthing->setAngles( vec_zero ); Event* event = new Event( EV_ViewThing_SetModel ); event->AddString( modelName ); viewthing->ProcessEvent( event ); if ( !gi.IsModel( viewthing->edict->s.modelindex ) ) { delete viewthing; current_viewthing = 0 ; return; } _updateViewthingCounts(); } //================================================================ // Name: SpawnFromTS // Class: ViewMaster // // Description: Called by the ToolServer to viewspawn a model // the ToolServer sends over dynamically. // // Parameters: Event *ev -- Point to event whose first string argument // is name of tiki model to reload, the second string // is the tiki to respawn. If the second string isn't // specified, it respawns the first string // // Returns: None // //================================================================ void ViewMaster::SpawnFromTS( Event *ev ) { char *data = (char*)gi.ToolServerGetData(); if (!data) // No data? Just abort. return; DeleteAll(NULL); // Overkill? char spawnmodel[256], reloadmodel[256]; strcpy(reloadmodel, "models/"); strcat(reloadmodel, ev->GetString(1)); if ( ev->NumArgs() > 1 ) { // Already has models/ on it strcpy(spawnmodel, ev->GetString(2)); } else { strcpy(spawnmodel,"models/"); strcat(spawnmodel, ev->GetString(1)); } gi.DPrintf("Reloading Model: %s...\n",reloadmodel); gi.TikiLoadFromTS(reloadmodel, data); // Setting this config string causes the client to reload the surfaces for this tiki gi.setConfigstring( CS_DEBUGINFO, va( "reload %s %d", spawnmodel, gi.modelindex(reloadmodel) ) ); // Create a new spawn event with item name to send to Spawn function Event* spawnEvent = new Event( EV_ViewThing_Spawn); spawnEvent->AddString(spawnmodel); Spawn(spawnEvent); delete spawnEvent; spawnEvent = 0 ; gi.setConfigstring( CS_DEBUGINFO, "" ); } //================================================================ // Name: Spawn // Class: ViewMaster // // Description: Spawns the tiki model specified in the Event's // first string argument. The current_viewthing points // to the spawned object if the spawn succeeds, 0 // if it doesn't. // // The spawned object is dropped straight down if // the cvar g_vt_droptoggle is set to true (default). // This will cause objects to spawn on the ground. // // Parameters: Event* -- Point to event whose first string argument // is name of tiki model to spawn ("model/eliza.tik") // // Returns: None // //================================================================ void ViewMaster::Spawn( Event *ev ) { const char *mdl; Viewthing *viewthing; Entity *ent; Event *event; Vector forward; Vector up; Vector delta; mdl = ev->GetString( 1 ); if ( !mdl || !mdl[ 0 ] ) { ev->Error( "Must specify a model name" ); return; } // Check if we have a client ent = g_entities[ 0 ].entity; assert( ent ); //----------------------------------------- // This if block attempts to keep the current // model index of a set up to date when an explicit // viewspawn command is issued. If the model that's // being spawn is within the set, the index in the // set will be updated accordingly. // // If it isn't found in the set, we simply continue. //----------------------------------------- if (_numberOfModelsInSet) { str modelName(mdl); int pos = g_vt_modelIndex->integer ; if ((pos > 1) && (pos < _modelNamesArray.NumObjects()) && (_modelNamesArray.ObjectAt(pos) != modelName) ) { int pos = _modelNamesArray.IndexOfObject(modelName); if (pos) { gi.cvar_set( "g_vt_modelIndex", va("%d", pos) ); DeleteCurrentViewthing(); DisplayCurrentModelInSet(); return ; } } } // create a new viewthin and set current_viewthing to it. viewthing = new Viewthing; current_viewthing = viewthing; //FIXME FIXME ent->angles.AngleVectors( &forward, NULL, &up ); // If _spawnAtLastItemsOrigin is set, we will spawn on if (_spawnAtLastItemsOrigin) { float upOffset = (g_vt_droptoggle->integer) ? 48 : 0 ; viewthing->baseorigin = _lastBaseOrigin ; viewthing->baseorigin += forward ; viewthing->baseorigin += (up * upOffset); } else { viewthing->baseorigin = ent->origin; viewthing->baseorigin += ( forward * 48.0f ); viewthing->baseorigin += ( up * 48.0f ); } viewthing->setOrigin( viewthing->baseorigin ); // Added this so that guns and such can be prevented from falling into the floor. // Controlled by viewmodeltoggledrop command. Default is true, so same as old functionality if (g_vt_droptoggle->integer) { viewthing->droptofloor( 10000.0f ); } viewthing->baseorigin = viewthing->origin; _lastBaseOrigin = viewthing->baseorigin ; delta = ent->origin - viewthing->origin; viewthing->setAngles( vec_zero ); event = new Event( EV_ViewThing_SetModel ); event->AddString( mdl ); viewthing->ProcessEvent( event ); if ( !gi.IsModel( viewthing->edict->s.modelindex ) ) { ev->Error( "model %s not found, viewmodel not spawned.", mdl ); delete viewthing; current_viewthing = NULL; return; } _updateViewthingCounts(); } //================================================================ // Name: SetModelEvent // Class: ViewMaster // // Description: Sets the model for the current viewthing. Passes // a SetModelEvent down to the current viewthing. The // Event must contain the model name as its first string // argument. // // Parameters: Event* -- First string argument contains model name // // Returns: None // //================================================================ void ViewMaster::SetModelEvent( Event *ev ) { const char *mdl; char str[ 128 ]; Event *event; Viewthing *viewthing; mdl = ev->GetString( 1 ); if ( !mdl || !mdl[ 0 ] ) { ev->Error( "Must specify a model name" ); return; } if ( !current_viewthing ) { // try to find one on the map current_viewthing = G_FindClass( NULL, "viewthing" ); if ( !current_viewthing ) { ev->Error( "No viewmodel" ); return; } } viewthing = ( Viewthing * )( ( Entity * )current_viewthing ); // Prepend 'models/' to make things easier str[ 0 ] = 0; if ( ( mdl[ 1 ] != ':' ) && strnicmp( mdl, "models", 6 ) ) { strcpy( str, "models/" ); } strcat( str, mdl ); event = new Event( EV_ViewThing_SetModel ); event->AddString( str ); viewthing->ProcessEvent( event ); viewthing->UpdateCvars(); } //================================================================ // Name: PassEvent // Class: ViewMaster // // Description: Passes an event down to the current_viewthing if // it is set. // // Parameters: Event* -- Event to pass // // Returns: None // //================================================================ void ViewMaster::PassEvent( Event *ev ) { Viewthing *viewthing; Event *event; if ( !current_viewthing ) { ev->Error( "No viewmodel" ); return; } viewthing = ( Viewthing * )( ( Entity * )current_viewthing ); if ( viewthing ) { event = new Event( ev ); viewthing->ProcessEvent( event ); } } //--------------------------------------------------------------------------- // P R O T E C T E D M E T H O D S //--------------------------------------------------------------------------- //================================================================ // Name: _resetOrigin // Class: ViewMaster // // Description: Sets the origin for the current viewthing to be wherever // it is at. This also sets the _lastBaseOrigin, which // means further relative translations are based from here. // // Parameters: None // // Returns: None // //================================================================ void ViewMaster::_resetOrigin() { if (!current_viewthing) return ; Viewthing *viewthing = ( Viewthing*) ( (Entity*) current_viewthing); _lastBaseOrigin = viewthing->origin ; _setToPosition(_lastBaseOrigin); // just ensure we update cvars } //================================================================ // Name: _setToPosition // Class: ViewMaster // // Description: Sets the Position of the current viewthing. // // Parameters: Vector& pos -- vector of position to set origin to // // Returns: None // //================================================================ void ViewMaster::_setToPosition( const Vector& pos ) { // Create a new event to pass to viewthing with new origin Event* originEvent = new Event(EV_ViewThing_SetOrigin); originEvent->AddFloat(pos.x); originEvent->AddFloat(pos.y); originEvent->AddFloat(pos.z); // Pass to the viewthing and clean up Viewthing *viewthing = (Viewthing*)( (Entity*)current_viewthing ); viewthing->ChangeOrigin(originEvent); delete originEvent ; originEvent = 0 ; gi.cvar_set( "g_vt_xtrans", "0" ); gi.cvar_set( "g_vt_ytrans", "0" ); gi.cvar_set( "g_vt_ztrans", "0" ); } //================================================================ // Name: _selectNext // Class: ViewMaster // // Description: Selects the next viewthing in the set of viewthings // Selects none if there are no more viewthings. // // Parameters: None // // Returns: None // //================================================================ void ViewMaster::_selectNext() { Viewthing *viewthing; Entity * ent; ent = G_FindClass( current_viewthing, "viewthing" ); if ( ent ) { if (current_viewthing) { viewthing = ( Viewthing * )( ( Entity * )current_viewthing ); viewthing->SetSelected( false ); } current_viewthing = ent; viewthing = ( Viewthing * )( ( Entity * )current_viewthing ); _lastBaseOrigin = viewthing->origin ; viewthing->UpdateCvars(); viewthing->SetSelected( true ); gi.Printf( "current viewthing model %s.\n", viewthing->model.c_str() ); _updateViewthingCounts(); } } //================================================================ // Name: _selectPrev // Class: ViewMaster // // Description: Selects the previous viewthing in the set of viewthings. // Selects none if there are no previous viewthings. // // Parameters: None // // Returns: None // //================================================================ void ViewMaster::_selectPrevious() { Viewthing *viewthing; Entity *prev; Entity *next; next = NULL; do { prev = next; next = G_FindClass( prev, "viewthing" ); } while( next != current_viewthing ); if ( prev ) { if (current_viewthing) { viewthing = ( Viewthing * )( ( Entity * )current_viewthing ); viewthing->SetSelected( false ); } current_viewthing = prev; viewthing = ( Viewthing * )( ( Entity * )current_viewthing ); _lastBaseOrigin = viewthing->origin ; viewthing->UpdateCvars(); viewthing->SetSelected(true); gi.Printf( "current viewthing model %s.\n", viewthing->model.c_str() ); _updateViewthingCounts(); } } //================================================================ // Name: _updateViewthingCounts // Class: ViewMaster // // Description: Sets the cvars g_vt_viewnumber and g_vt_viewcount // appropriately after any change to the number of // spawned viewthings in the world. When the loop // finishes, g_vt_viewcount is the total number of // spawned viewthings and g_vt_viewnumber is position // in this list of the current_viewthing. // // The passed in parameter can be used for manipulating // the final count. Because deletion of viewthings is // event driven, when we know there is going to be a // deletion the deletion hasn't actually occured yet. // So it would show up in the count. Hence, we can pass // in a -1 to offset the final count appropriately. // Hack. // // Parameters: int countAdjustment -- offset final count (default is 0). // // Returns: None // //================================================================ void ViewMaster::_updateViewthingCounts(int countAdjustment) { Entity* next = G_FindClass( NULL, "viewthing"); int numberOfViewthings = 0 ; int currentViewthingIndex = 0 ; while (next) { numberOfViewthings++ ; if (next == current_viewthing) { currentViewthingIndex = numberOfViewthings ; } next = G_FindClass( next, "viewthing"); } gi.cvar_set( "g_vt_viewnumber", va("%d", currentViewthingIndex) ); gi.cvar_set( "g_vt_viewcount", va("%d", numberOfViewthings + countAdjustment) ); } //================================================================ // Name: _randomizeViewthing // Class: ViewMaster // // Description: Randomizes the scaling and rotation of the specified // viewthing, based on various cvar settings. These // randomizations can be turned on and off individually, // but they are only applied if g_vt_viewrandomize is true. // // The bell curve randomization is done in the scaling section. // It works like this: // o Add two random values between 0 and 100 and divide total by 100. (Bell curve value--try with two dice) // o Get half distance between desired low and high ((high-low) divided by 2) // o Scale this half-distance by the bell curve value computed in step 1 (yields # from 0 to 2*half, but tends towards halfway) // o Add scaled half to low value, giving bell curve between desired low and high // // Parameters: Viewthing* -- viewthing to randomize // // Returns: None // //================================================================ void ViewMaster::_randomizeViewthing( Viewthing* viewthingPtr ) { if (!viewthingPtr) { gi.Printf( "No viewthing to randomize\n"); return ; } if (g_vt_viewsrandomize->integer) { // Randomly scale float die1 = G_Random( 100.0f ); float die2 = G_Random( 100.0f ); float scaleValue = (die1 + die2) / 100.0f ; float midpoint = ( (float)g_vt_viewmaxrandomize->integer - (float)g_vt_viewminrandomize->integer) / 2.0f ; float adjustment = (float)g_vt_viewminrandomize->integer + (midpoint * scaleValue); float scaleFactor = (adjustment / 100.0f); Event* scaleEvent = new Event(EV_ViewThing_SetScale); scaleEvent->AddFloat(scaleFactor); viewthingPtr->SetScaleEvent(scaleEvent); delete scaleEvent ; scaleEvent = 0 ; } if (g_vt_viewzrandomize->integer) { // Randomly rotate around z-axis Event* rotateEvent = new Event(EV_ViewThing_SetYaw); rotateEvent->AddFloat(G_Random(360.0f)); viewthingPtr->SetYawEvent(rotateEvent); delete rotateEvent ; rotateEvent = 0 ; } if (g_vt_viewyrandomize->integer) { // Randomly rotate around z-axis Event* rotateEvent = new Event(EV_ViewThing_SetPitch); rotateEvent->AddFloat(G_Random(360.0f)); viewthingPtr->SetPitchEvent(rotateEvent); delete rotateEvent ; rotateEvent = 0 ; } if (g_vt_viewxrandomize->integer) { // Randomly rotate around z-axis Event* rotateEvent = new Event(EV_ViewThing_SetRoll); rotateEvent->AddFloat(G_Random(360.0f)); viewthingPtr->SetRollEvent(rotateEvent); delete rotateEvent ; rotateEvent = 0 ; } } //------------------------------------------------------------------------ // // VIEWTHING // //------------------------------------------------------------------------ CLASS_DECLARATION( Entity, Viewthing, "viewthing" ) { { &EV_ViewThing_Think, &Viewthing::ThinkEvent }, { &EV_ViewThing_LastFrame, &Viewthing::LastFrameEvent }, { &EV_ViewThing_ToggleAnimate, &Viewthing::ToggleAnimateEvent }, { &EV_ViewThing_SetModel, &Viewthing::SetModelEvent }, { &EV_ViewThing_NextFrame, &Viewthing::NextFrameEvent }, { &EV_ViewThing_PrevFrame, &Viewthing::PrevFrameEvent }, { &EV_ViewThing_NextAnim, &Viewthing::NextAnimEvent }, { &EV_ViewThing_PrevAnim, &Viewthing::PrevAnimEvent }, { &EV_ViewThing_ScaleUp, &Viewthing::ScaleUpEvent }, { &EV_ViewThing_ScaleDown, &Viewthing::ScaleDownEvent }, { &EV_ViewThing_SetScale, &Viewthing::SetScaleEvent }, { &EV_ViewThing_SetYaw, &Viewthing::SetYawEvent }, { &EV_ViewThing_SetPitch, &Viewthing::SetPitchEvent }, { &EV_ViewThing_SetRoll, &Viewthing::SetRollEvent }, { &EV_ViewThing_SetAngles, &Viewthing::SetAnglesEvent }, { &EV_ViewThing_SetStatic, &Viewthing::SetStatic }, { &EV_ViewThing_Attach, &Viewthing::AttachModel }, { &EV_ViewThing_Detach, &Viewthing::Delete }, { &EV_ViewThing_DetachAll, &Viewthing::DetachAll }, { &EV_ViewThing_Delete, &Viewthing::Delete }, { &EV_ViewThing_SetOrigin, &Viewthing::ChangeOrigin }, { &EV_ViewThing_SaveOffSurfaces, &Viewthing::SaveSurfaces }, { &EV_ViewThing_PrintTime, &Viewthing::PrintTime }, { &EV_ViewThing_SetAnim, &Viewthing::SetAnim }, { &EV_ViewThing_NextMorph, &Viewthing::NextMorph }, { &EV_ViewThing_PrevMorph, &Viewthing::PrevMorph }, { &EV_ViewThing_Morph, &Viewthing::Morph }, { &EV_ViewThing_Unmorph, &Viewthing::Unmorph }, { &EV_ViewThing_ChangeRoll, &Viewthing::ChangeRollEvent }, { &EV_ViewThing_ChangePitch, &Viewthing::ChangePitchEvent }, { &EV_ViewThing_ChangeYaw, &Viewthing::ChangeYawEvent }, { &EV_ViewThing_Flash, &Viewthing::Flash }, { &EV_ViewThing_SetFrame, &Viewthing::SetFrame }, { NULL, NULL } }; Viewthing::Viewthing( void ) { animate = new Animate( this ); frame = 0; animstate = 0; _static = (g_vt_makestatic->integer != 0) ? true : false ; _selected = false ; _pulseCount = 0 ; setSolidType( SOLID_NOT ); baseorigin = origin; Viewmodel.current_viewthing = this; edict->s.eType = ET_MODELANIM; edict->s.renderfx |= RF_SHADOW; edict->s.renderfx |= RF_SHADOW_PRECISE; edict->s.renderfx |= RF_EXTRALIGHT; current_morph = 0; gi.cvar_set( "g_vt_scale", "1.0" ); gi.cvar_set( "g_vt_roll", "0.0" ); gi.cvar_set( "g_vt_pitch", "0.0" ); gi.cvar_set( "g_vt_yaw", "0.0" ); gi.cvar_set( "g_vt_xtrans", "0.0" ); gi.cvar_set( "g_vt_ytrans", "0.0" ); gi.cvar_set( "g_vt_ztrans", "0.0" ); // save off surfaces once the model is spawned PostEvent( EV_ViewThing_SaveOffSurfaces, FRAMETIME ); PostEvent( EV_ViewThing_Think, FRAMETIME ); } void Viewthing::SetSelected( qboolean state ) { _selected = state ; _pulseCount = 0 ; if (this->hidden()) { this->showModel(); } } void Viewthing::Flash( Event *ev ) { SetSelected(true); } void Viewthing::PrintTime( Event *ev ) { gi.Printf( "ev current frame %d leveltime %.2f\n", ev->GetInteger( 1 ), level.time ); } void Viewthing::UpdateCvars( qboolean quiet ) { gi.cvar_set( "viewmodelanim", animate->AnimName() ); gi.cvar_set( "viewmodeltotaltime", va( "%.3f", animate->AnimTime()) ); gi.cvar_set( "viewmodelanimframes",va( "%d", animate->NumFrames()) ); gi.cvar_set( "viewmodelframe", va( "%d", CurrentFrame() ) ); gi.cvar_set( "viewmodelname", model.c_str() ); gi.cvar_set( "viewmodelscale", va( "%0.2f", edict->s.scale ) ); gi.cvar_set( "currentviewthing", model.c_str() ); gi.cvar_set( "viewthingorigin", va( "%0.2f,%0.2f,%0.2f", edict->s.origin[0],edict->s.origin[1],edict->s.origin[2] ) ); gi.cvar_set( "viewmodelanimnum", va( "%.3f", ( float )CurrentAnim() / ( float )animate->NumAnims() ) ); gi.cvar_set( "g_vt_scale", va( "%.2f", edict->s.scale) ); gi.cvar_set( "g_vt_roll", va( "%.2f", (angles.z <= 180.0f) ? angles.z : (angles.z - 360.0f)) ); gi.cvar_set( "g_vt_pitch", va( "%.2f", (angles.x <= 180.0f) ? angles.x : (angles.x - 360.0f)) ); gi.cvar_set( "g_vt_yaw", va( "%.2f", (angles.y <= 180.0f) ? angles.y : (angles.y - 360.0f)) ); gi.cvar_set( "g_vt_xtrans", va( "%.2f", (Viewmodel._lastBaseOrigin.x - baseorigin.x) ) ); gi.cvar_set( "g_vt_ytrans", va( "%.2f", (Viewmodel._lastBaseOrigin.y - baseorigin.y) ) ); gi.cvar_set( "g_vt_ztrans", va( "%.2f", (Viewmodel._lastBaseOrigin.z - baseorigin.z) ) ); if ( gi.Morph_NameForNum( edict->s.modelindex, current_morph ) ) gi.cvar_set( "viewmorphname", gi.Morph_NameForNum( edict->s.modelindex, current_morph ) ); else gi.cvar_set( "viewmorphname", "" ); if ( !quiet ) { gi.Printf( "%s, frame %3d, scale %4.2f\n", animate->AnimName(), CurrentFrame(), edict->s.scale ); } } void Viewthing::ThinkEvent( Event *ev ) { static float startTime = 0 ; if (_selected && (_pulseCount < 20)) { if (startTime == 0.0f) { startTime = level.time ; } if ( (level.time - startTime) > 0.15f) { if (this->hidden()) { this->showModel(); } else { this->hideModel(); } startTime = 0.0f ; _pulseCount++ ; } } int f; if (animstate >= 2) { Vector forward; Vector left; Vector up; Vector realmove; angles.AngleVectors( &forward, &left, &up ); realmove = ( left * total_delta[1] ) + ( up * total_delta[2] ) + ( forward * total_delta[0] ); setOrigin( baseorigin + realmove ); gi.cvar_set( "viewthingorigin", va( "%0.2f,%0.2f,%0.2f", edict->s.origin[0],edict->s.origin[1],edict->s.origin[2] ) ); } PostEvent( EV_ViewThing_Think, FRAMETIME ); if ( ( animstate > 0 ) && ( Viewmodel.current_viewthing == this ) ) { f = CurrentFrame(); if ( f != lastframe ) { float time; lastframe = f; time = f * animate->AnimTime() / (float)animate->NumFrames(); gi.Printf( "current frame %d time %.2f\n", f, time ); gi.cvar_set( "viewmodeltime", va( "%.2f", time ) ); gi.cvar_set( "viewmodelframe", va( "%d", f ) ); gi.cvar_set( "viewmodelanim", animate->AnimName() ); } } } void Viewthing::LastFrameEvent( Event *ev ) { if ( animstate != 3 ) { total_delta = Vector(0, 0, 0); } } void Viewthing::ToggleAnimateEvent( Event *ev ) { animstate = ( animstate + 1 ) % 4; total_delta = Vector(0, 0, 0); setOrigin( baseorigin ); // reset to a known state switch( animstate ) { case 0: animate->SetFrame( frame ); gi.Printf( "Animation stopped.\n" ); gi.cvar_set( "viewmodelanimmode", "Stopped" ); break; case 1: animate->NewAnim( CurrentAnim() ); gi.Printf( "Animation no motion.\n" ); gi.cvar_set( "viewmodelanimmode", "No Motion" ); break; case 2: animate->NewAnim( CurrentAnim(), EV_ViewThing_LastFrame ); gi.Printf( "Animation with motion and looping.\n" ); gi.cvar_set( "viewmodelanimmode", "Motion and Looping" ); break; case 3: animate->NewAnim( CurrentAnim(), EV_ViewThing_LastFrame ); gi.Printf( "Animation with motion no looping.\n" ); gi.cvar_set( "viewmodelanimmode", "Motion and No Looping" ); break; } UpdateCvars( true ); } void Viewthing::SetStatic( Event *ev ) { if (ev->NumArgs() == 1) { _static = (ev->GetInteger(1)) ? true : false ; } else { _static = (g_vt_makestatic->integer != 0) ? true : false ; } } void Viewthing::SetModelEvent( Event *ev ) { str modelname; modelname = ev->GetString( 1 ); setModel( modelname ); if ( gi.IsModel( edict->s.modelindex ) ) { // Default the viewthing to an idle animation if the model has one if ( animate->HasAnim( "idle" ) ) animate->RandomAnimate( "idle" ); else animate->NewAnim( 0 ); frame = 0; animate->SetFrame( 0 ); } UpdateCvars(); } void Viewthing::NextFrameEvent( Event *ev ) { int numframes; numframes = animate->NumFrames(); if ( numframes ) { frame = (frame+1)%numframes; animate->SetFrame( frame ); animstate = 0; UpdateCvars(); } } void Viewthing::PrevFrameEvent( Event *ev ) { int numframes; numframes = animate->NumFrames(); if ( numframes ) { frame = (frame-1)%numframes; animate->SetFrame( frame ); animstate = 0; UpdateCvars(); } } void Viewthing::SetAnim( Event *ev ) { int numanims, anim; numanims = animate->NumAnims(); if ( numanims ) { // restore original surfaces memcpy( edict->s.surfaces, origSurfaces, sizeof( origSurfaces ) ); anim = ev->GetFloat( 1 ) * numanims; if ( anim >= numanims ) anim = numanims - 1; animate->NewAnim( anim % numanims ); frame = 0; animate->SetFrame( frame ); animstate = 0; UpdateCvars(); } } void Viewthing::SetAnim( int num ) { int numanims; numanims = animate->NumAnims(); if ( numanims ) { // restore original surfaces memcpy( edict->s.surfaces, origSurfaces, sizeof( origSurfaces ) ); // if ( num >= numanims ) // num = numanims - 1; animate->NewAnim( num ); animate->SetFrame( frame ); //animstate = 0; UpdateCvars(); } } void Viewthing::NextAnimEvent( Event *ev ) { int numanims; numanims = animate->NumAnims(); if ( numanims ) { // restore original surfaces memcpy( edict->s.surfaces, origSurfaces, sizeof( origSurfaces ) ); animate->NewAnim( ( CurrentAnim() + 1 ) % numanims ); frame = 0; animate->SetFrame( frame ); animstate = 0; gi.Printf( "Animation stopped.\n" ); gi.cvar_set( "viewmodelanimmode", "Stopped" ); UpdateCvars(); } } void Viewthing::PrevAnimEvent( Event *ev ) { int anim; int numanims; numanims = animate->NumAnims(); if ( numanims ) { // restore original surfaces memcpy( edict->s.surfaces, origSurfaces, sizeof( origSurfaces ) ); anim = CurrentAnim() - 1; while( anim < 0 ) { anim += numanims; } animate->NewAnim( anim ); frame = 0; animate->SetFrame( frame ); animstate = 0; gi.Printf( "Animation stopped.\n" ); gi.cvar_set( "viewmodelanimmode", "Stopped" ); UpdateCvars(); } } void Viewthing::ScaleUpEvent( Event *ev ) { edict->s.scale += 0.05f; UpdateCvars(); } void Viewthing::ScaleDownEvent( Event *ev ) { edict->s.scale -= 0.05f; UpdateCvars(); } void Viewthing::SetScaleEvent( Event *ev ) { if ( ev->NumArgs() ) { float s = ev->GetFloat( 1 ); edict->s.scale = s; UpdateCvars(); } } void Viewthing::SetYawEvent( Event *ev ) { if ( ev->NumArgs() > 0 ) { angles.setYaw( ev->GetFloat( 1 ) ); setAngles( angles ); UpdateCvars(); } } void Viewthing::SetPitchEvent( Event *ev ) { if ( ev->NumArgs() > 0 ) { angles.setPitch( ev->GetFloat( 1 ) ); setAngles( angles ); UpdateCvars(); } } void Viewthing::SetRollEvent( Event *ev ) { if ( ev->NumArgs() > 0 ) { angles.setRoll( ev->GetFloat( 1 ) ); setAngles( angles ); UpdateCvars(); } } void Viewthing::ChangeRollEvent( Event *ev ) { if ( ev->NumArgs() > 0) { angles.setRoll(angles.roll() + ev->GetFloat( 1 ) ); setAngles(angles); UpdateCvars(); } } void Viewthing::ChangePitchEvent( Event *ev ) { if ( ev->NumArgs() > 0) { angles.setPitch(angles.pitch() + ev->GetFloat( 1 ) ); setAngles(angles); UpdateCvars(); } } void Viewthing::ChangeYawEvent( Event *ev ) { if ( ev->NumArgs() > 0) { angles.setYaw(angles.yaw() + ev->GetFloat( 1 ) ); setAngles(angles); UpdateCvars(); } } void Viewthing::SetAnglesEvent( Event *ev ) { if ( ev->NumArgs() > 2 ) { angles.x = ev->GetFloat( 1 ); angles.y = ev->GetFloat( 2 ); angles.z = ev->GetFloat( 3 ); setAngles( angles ); } gi.cvar_set( "g_vt_roll", va ("%0.2f", angles.x) ); gi.cvar_set( "g_vt_pitch", va ("%0.2f", angles.y) ); gi.cvar_set( "g_vt_yaw", va ("%0.2f", angles.z) ); gi.Printf( "angles = %0.4f, %0.4f, %0.4f\n", angles.x, angles.y, angles.z ); } //-------------------------------------------------------------- // // Name: AttachModel // Class: Viewthing // // Description: Attaches a model to a viewspawned model // // Parameters: Event *ev // string -- model name // // Returns: None // //-------------------------------------------------------------- void Viewthing::AttachModel(Event *ev) { Event *event = 0; Viewthing *child = 0; str mdl = ev->GetString(2); child = new Viewthing; child->setModel( mdl.c_str() ); // Check to make sure it exists. if ( !gi.IsModel( child->edict->s.modelindex ) ) { ev->Error( "attachmodel %s not found, attachmodel not spawned.", mdl.c_str() ); delete child; Viewmodel.ProcessEvent(EV_ViewThing_Prev); return; } // Attach the child event = new Event( EV_Attach ); event->AddEntity( this ); event->AddString( ev->GetString( 1 ) ); child->ProcessEvent( event ); } void Viewthing::Delete( Event *ev ) { Viewmodel.current_viewthing = NULL; PostEvent( EV_Remove, 0.0f ); Viewmodel.PostEvent( EV_ViewThing_Next, 0.0f ); } void Viewthing::Delete() { PostEvent( EV_Remove, 0.0f ); } void Viewthing::DetachAll( Event *ev ) { int i; int num; Entity * ent; if ( bind_info ) { num = bind_info->numchildren; for (i=0;ichildren[i]) continue; ent = ( Entity * )G_GetEntity( bind_info->children[i] ); ent->PostEvent( EV_Remove, 0.0f ); num--; if (!num) break; } } } void Viewthing::ChangeOrigin( Event *ev ) { if ( ev->NumArgs() ) { origin.x = ev->GetFloat( 1 ); origin.y = ev->GetFloat( 2 ); origin.z = ev->GetFloat( 3 ); setOrigin( origin ); baseorigin = origin; gi.cvar_set( "g_vt_height", va ("%f", origin.z) ); UpdateCvars(); } gi.Printf( "vieworigin = x%0.4f y%0.4f z%0.4f\n", origin.x, origin.y, origin.z ); } void Viewthing::SaveSurfaces( Event *ev ) { memcpy( origSurfaces, edict->s.surfaces, sizeof( origSurfaces ) ); } void Viewthing::NextMorph( Event *ev ) { current_morph++; if ( current_morph >= gi.NumMorphs( edict->s.modelindex ) ) current_morph = 0; if ( gi.Morph_NameForNum( edict->s.modelindex, current_morph ) ) gi.Printf( "morph %s\n", gi.Morph_NameForNum( edict->s.modelindex, current_morph ) ); UpdateCvars( true ); } void Viewthing::PrevMorph( Event *ev ) { current_morph--; if ( current_morph < 0 ) current_morph = gi.NumMorphs( edict->s.modelindex ) - 1; if ( gi.Morph_NameForNum( edict->s.modelindex, current_morph ) ) gi.Printf( "morph %s\n", gi.Morph_NameForNum( edict->s.modelindex, current_morph ) ); UpdateCvars( true ); } void Viewthing::Morph( Event *ev ) { const char *morph_name; morph_name = gi.Morph_NameForNum( edict->s.modelindex, current_morph ); if ( morph_name ) { Event *new_event; new_event = new Event ( EV_Morph ); new_event->AddString( morph_name ); //new_event->AddFloat( 50 ); ProcessEvent( new_event ); } } void Viewthing::Unmorph( Event *ev ) { const char *morph_name; morph_name = gi.Morph_NameForNum( edict->s.modelindex, current_morph ); if ( morph_name ) { Event *new_event; new_event = new Event ( EV_Unmorph ); new_event->AddString( morph_name ); ProcessEvent( new_event ); } } void Viewthing::SetFrame( Event *ev ) { int newframe; newframe = ev->GetInteger(1); if ( newframe < animate->NumFrames() ) { animate->SetFrame( newframe ); animstate = 0; UpdateCvars(); } }