// Reference tag utility functions // leave this line at the top for all g_xxxx.cpp files... #include "g_headers.h" #include "g_local.h" #include "g_functions.h" #include "g_nav.h" extern int delayedShutDown; #define TAG_GENERIC_NAME "__WORLD__" //If a designer chooses this name, cut a finger off as an example to the others typedef vector < reference_tag_t * > refTag_v; typedef map < string, reference_tag_t * > refTag_m; typedef struct tagOwner_s { refTag_v tags; refTag_m tagMap; } tagOwner_t; typedef map < string, tagOwner_t * > refTagOwner_m; refTagOwner_m refTagOwnerMap; /* ------------------------- TAG_ShowTags ------------------------- */ void TAG_ShowTags( int flags ) { refTagOwner_m::iterator rtoi; STL_ITERATE( rtoi, refTagOwnerMap ) { refTag_v::iterator rti; STL_ITERATE( rti, (((*rtoi).second)->tags) ) { if ( (*rti)->flags & RTF_NAVGOAL ) { if ( gi.inPVS( g_entities[0].currentOrigin, (*rti)->origin ) ) CG_DrawNode( (*rti)->origin, NODE_NAVGOAL ); } } } } /* ------------------------- TAG_Init ------------------------- */ void TAG_Init( void ) { refTagOwner_m::iterator rtoi; //Delete all owners for ( rtoi = refTagOwnerMap.begin(); rtoi != refTagOwnerMap.end(); rtoi++ ) { if ( (*rtoi).second == NULL ) { assert( 0 ); //FIXME: This is not good continue; } refTag_v::iterator rti; //Delete all tags within the owner's scope for ( rti = ((*rtoi).second)->tags.begin(); rti != ((*rtoi).second)->tags.end(); rti++ ) { if ( (*rti) == NULL ) { assert( 0 ); //FIXME: Bad bad continue; } //Free it delete (*rti); } //Clear the containers ((*rtoi).second)->tags.clear(); ((*rtoi).second)->tagMap.clear(); //Delete the owner delete ((*rtoi).second); } //Clear the container refTagOwnerMap.clear(); } /* ------------------------- TAG_FindOwner ------------------------- */ tagOwner_t *TAG_FindOwner( const char *owner ) { refTagOwner_m::iterator rtoi; rtoi = refTagOwnerMap.find( owner ); if ( rtoi == refTagOwnerMap.end() ) return NULL; return (*rtoi).second; } /* ------------------------- TAG_Find ------------------------- */ reference_tag_t *TAG_Find( const char *owner, const char *name ) { tagOwner_t *tagOwner; tagOwner = VALIDSTRING( owner ) ? TAG_FindOwner( owner ) : TAG_FindOwner( TAG_GENERIC_NAME ); //Not found... if ( tagOwner == NULL ) { tagOwner = TAG_FindOwner( TAG_GENERIC_NAME ); if ( tagOwner == NULL ) return NULL; } refTag_m::iterator rti; rti = tagOwner->tagMap.find( name ); if ( rti == tagOwner->tagMap.end() ) { //Try the generic owner instead tagOwner = TAG_FindOwner( TAG_GENERIC_NAME ); if ( tagOwner == NULL ) return NULL; char tempName[ MAX_REFNAME ]; Q_strncpyz( (char *) tempName, name, MAX_REFNAME ); Q_strlwr( (char *) tempName ); //NOTENOTE: For case insensitive searches on a map rti = tagOwner->tagMap.find( tempName ); if ( rti == tagOwner->tagMap.end() ) return NULL; } return (*rti).second; } /* ------------------------- TAG_Add ------------------------- */ reference_tag_t *TAG_Add( const char *name, const char *owner, vec3_t origin, vec3_t angles, int radius, int flags ) { reference_tag_t *tag = new reference_tag_t; VALIDATEP( tag ); //Copy the information VectorCopy( origin, tag->origin ); VectorCopy( angles, tag->angles ); tag->radius = radius; tag->flags = flags; if ( VALIDSTRING( name ) == false ) { //gi.Error("Nameless ref_tag found at (%i %i %i)", (int)origin[0], (int)origin[1], (int)origin[2]); gi.Printf(S_COLOR_RED"ERROR: Nameless ref_tag found at (%i %i %i)\n", (int)origin[0], (int)origin[1], (int)origin[2]); delayedShutDown = level.time + 100; return NULL; } //Copy the name Q_strncpyz( (char *) tag->name, name, MAX_REFNAME ); Q_strlwr( (char *) tag->name ); //NOTENOTE: For case insensitive searches on a map //Make sure this tag's name isn't alread in use if ( TAG_Find( owner, name ) ) { delayedShutDown = level.time + 100; gi.Printf(S_COLOR_RED"ERROR: Duplicate tag name \"%s\"\n", name ); return NULL; } //Attempt to add this to the owner's list if ( VALIDSTRING( owner ) == false ) { //If the owner isn't found, use the generic world name owner = TAG_GENERIC_NAME; } tagOwner_t *tagOwner = TAG_FindOwner( owner ); //If the owner is valid, add this tag to it if VALID( tagOwner ) { tagOwner->tags.insert( tagOwner->tags.end(), tag ); tagOwner->tagMap[ (char*) &tag->name ] = tag; } else { //Create a new owner list tagOwner_t *tagOwner = new tagOwner_t; VALIDATEP( tagOwner ); //Insert the information tagOwner->tags.insert( tagOwner->tags.end(), tag ); tagOwner->tagMap[ (char *) tag->name ] = tag; //Map it refTagOwnerMap[ owner ] = tagOwner; } return tag; } /* ------------------------- TAG_GetOrigin ------------------------- */ int TAG_GetOrigin( const char *owner, const char *name, vec3_t origin ) { reference_tag_t *tag = TAG_Find( owner, name ); if (!tag) { VectorClear(origin); return false; } VALIDATEB( tag ); VectorCopy( tag->origin, origin ); return true; } /* ------------------------- TAG_GetOrigin2 Had to get rid of that damn assert for dev ------------------------- */ int TAG_GetOrigin2( const char *owner, const char *name, vec3_t origin ) { reference_tag_t *tag = TAG_Find( owner, name ); if( tag == NULL ) { return qfalse; } VectorCopy( tag->origin, origin ); return qtrue; } /* ------------------------- TAG_GetAngles ------------------------- */ int TAG_GetAngles( const char *owner, const char *name, vec3_t angles ) { reference_tag_t *tag = TAG_Find( owner, name ); VALIDATEB( tag ); VectorCopy( tag->angles, angles ); return true; } /* ------------------------- TAG_GetRadius ------------------------- */ int TAG_GetRadius( const char *owner, const char *name ) { reference_tag_t *tag = TAG_Find( owner, name ); VALIDATEB( tag ); return tag->radius; } /* ------------------------- TAG_GetFlags ------------------------- */ int TAG_GetFlags( const char *owner, const char *name ) { reference_tag_t *tag = TAG_Find( owner, name ); VALIDATEB( tag ); return tag->flags; } /* ============================================================================== Spawn functions ============================================================================== */ /*QUAKED ref_tag (0.5 0.5 1) (-8 -8 -8) (8 8 8) Reference tags which can be positioned throughout the level. These tags can later be refered to by the scripting system so that their origins and angles can be referred to. If you set angles on the tag, these will be retained. If you target a ref_tag at an entity, that will set the ref_tag's angles toward that entity. If you set the ref_tag's ownername to the ownername of an entity, it makes that entity is the owner of the ref_tag. This means that the owner, and only the owner, may refer to that tag. Tags may not have the same name as another tag with the same owner. However, tags with different owners may have the same name as one another. In this way, scripts can generically refer to tags by name, and their owners will automatically specifiy which tag is being referred to. targetname - the name of this tag ownername - the owner of this tag target - use to point the tag at something for angles */ void ref_link ( gentity_t *ent ) { reference_tag_t *tag; if ( ent->target ) { //TODO: Find the target and set our angles to that direction gentity_t *target = G_Find( NULL, FOFS(targetname), ent->target ); vec3_t dir; if ( target ) { //Find the direction to the target VectorSubtract( target->s.origin, ent->s.origin, dir ); VectorNormalize( dir ); vectoangles( dir, ent->s.angles ); //FIXME: Does pitch get flipped? } else { gi.Printf( S_COLOR_RED"ERROR: ref_tag (%s) has invalid target (%s)", ent->targetname, ent->target ); } } //Add the tag tag = TAG_Add( ent->targetname, ent->ownername, ent->s.origin, ent->s.angles, 16, 0 ); //Delete immediately, cannot be refered to as an entity again //NOTE: this means if you wanted to link them in a chain for, say, a path, you can't G_FreeEntity( ent ); } void SP_reference_tag ( gentity_t *ent ) { if ( ent->target ) { //Init cannot occur until all entities have been spawned ent->e_ThinkFunc = thinkF_ref_link; ent->nextthink = level.time + START_TIME_LINK_ENTS; } else { ref_link( ent ); } }