/* * G_OffsetSpawnPoint - use a grid of player boxes to offset the spawn point */ bool G_OffsetSpawnPoint( vec3_t origin, vec3_t box_mins, vec3_t box_maxs, float radius, bool checkground ) { trace_t trace; vec3_t virtualorigin; vec3_t absmins, absmaxs; float playerbox_rowwidth; float playerbox_columnwidth; int rows, columns; int i, j, row = 0, column = 0; int rowseed = rand() & 255; int columnseed = rand() & 255; int mask_spawn = MASK_PLAYERSOLID | ( CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_TELEPORTER|CONTENTS_JUMPPAD|CONTENTS_BODY|CONTENTS_NODROP ); int playersFound = 0, worldfound = 0, nofloorfound = 0, badclusterfound = 0; // check box clusters int cluster; int num_leafs; int leafs[8]; if( radius <= box_maxs[0] - box_mins[0] ) return true; if( checkground ) // drop the point to ground (scripts can accept any entity now) { VectorCopy( origin, virtualorigin ); virtualorigin[2] -= 1024; G_Trace( &trace, origin, box_mins, box_maxs, virtualorigin, NULL, MASK_PLAYERSOLID ); if( trace.fraction == 1.0f ) checkground = false; else if( trace.endpos[2] + 8.0f < origin[2] ) { VectorCopy( trace.endpos, origin ); origin[2] += 8.0f; } } playerbox_rowwidth = 2 + box_maxs[0] - box_mins[0]; playerbox_columnwidth = 2 + box_maxs[1] - box_mins[1]; rows = radius / playerbox_rowwidth; columns = radius / playerbox_columnwidth; // no, we won't just do a while, let's go safe and just check as many times as // positions in the grid. If we didn't found a spawnpoint by then, we let it telefrag. for( i = 0; i < ( rows * columns ); i++ ) { row = Q_brandom( &rowseed, -rows, rows ); column = Q_brandom( &columnseed, -columns, columns ); VectorSet( virtualorigin, origin[0] + ( row * playerbox_rowwidth ), origin[1] + ( column * playerbox_columnwidth ), origin[2] ); VectorAdd( virtualorigin, box_mins, absmins ); VectorAdd( virtualorigin, box_maxs, absmaxs ); for( j = 0; j < 2; j++ ) { absmaxs[j] += 1; absmins[j] -= 1; } //check if position is inside world // check if valid cluster cluster = -1; // fix a warning num_leafs = trap_CM_BoxLeafnums( absmins, absmaxs, leafs, 8, NULL ); for( j = 0; j < num_leafs; j++ ) { cluster = trap_CM_LeafCluster( leafs[j] ); if( cluster == -1 ) break; } if( cluster == -1 ) { badclusterfound++; continue; } // one more trace is needed, only checking if some part of the world is on the // way from spawnpoint to the virtual position trap_CM_TransformedBoxTrace( &trace, origin, virtualorigin, box_mins, box_maxs, NULL, MASK_PLAYERSOLID, NULL, NULL ); if( trace.fraction != 1.0f ) continue; // check if anything solid is on player's way G_Trace( &trace, vec3_origin, absmins, absmaxs, vec3_origin, world, mask_spawn ); if( trace.startsolid || trace.allsolid || trace.ent != -1 ) { if( trace.ent == 0 ) worldfound++; else if( trace.ent < gs.maxclients ) playersFound++; continue; } // one more check before accepting this spawn: there's ground at our feet? if( checkground ) // if floating item flag is not set { vec3_t origin_from, origin_to; VectorCopy( virtualorigin, origin_from ); origin_from[2] += box_mins[2] + 1; VectorCopy( origin_from, origin_to ); origin_to[2] -= 32; // use point trace instead of box trace to avoid small glitches that can't support the player but will stop the trace G_Trace( &trace, origin_from, vec3_origin, vec3_origin, origin_to, NULL, MASK_PLAYERSOLID ); if( trace.startsolid || trace.allsolid || trace.fraction == 1.0f ) { // full run means no ground nofloorfound++; continue; } } VectorCopy( virtualorigin, origin ); return true; } //G_Printf( "Warning: couldn't find a safe spawnpoint (blocked by players:%i world:%i nofloor:%i badcluster:%i)\n", playersFound, worldfound, nofloorfound, badclusterfound ); return false; }
void GClip_LinkEntity( edict_t *ent ) { areanode_t *node; int leafs[MAX_TOTAL_ENT_LEAFS]; int clusters[MAX_TOTAL_ENT_LEAFS]; int num_leafs; int i, j, k; int area; int topnode; if( ent->r.area.prev ) GClip_UnlinkEntity( ent ); // unlink from old position if( ent == game.edicts ) return; // don't add the world if( !ent->r.inuse ) return; // set the size VectorSubtract( ent->r.maxs, ent->r.mins, ent->r.size ); if( ent->r.solid == SOLID_NOT || ( ent->r.svflags & SVF_PROJECTILE ) ) { ent->s.solid = 0; } else if( ISBRUSHMODEL( ent->s.modelindex ) ) { // the only predicted SOLID_TRIGGER entity is ET_PUSH_TRIGGER if( ent->r.solid != SOLID_TRIGGER || ent->s.type == ET_PUSH_TRIGGER ) ent->s.solid = SOLID_BMODEL; else ent->s.solid = 0; } else // encode the size into the entity_state for client prediction { if( ent->r.solid == SOLID_TRIGGER ) { ent->s.solid = 0; } else { // assume that x/y are equal and symetric i = ent->r.maxs[0]/8; clamp( i, 1, 31 ); // z is not symetric j = ( -ent->r.mins[2] )/8; clamp( j, 1, 31 ); // and z maxs can be negative... k = ( ent->r.maxs[2]+32 )/8; clamp( k, 1, 63 ); ent->s.solid = ( k<<10 ) | ( j<<5 ) | i; } } // set the abs box if( ISBRUSHMODEL( ent->s.modelindex ) && ( ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2] ) ) { // expand for rotation float radius; radius = RadiusFromBounds( ent->r.mins, ent->r.maxs ); for( i = 0; i < 3; i++ ) { ent->r.absmin[i] = ent->s.origin[i] - radius; ent->r.absmax[i] = ent->s.origin[i] + radius; } } else // axis aligned { VectorAdd( ent->s.origin, ent->r.mins, ent->r.absmin ); VectorAdd( ent->s.origin, ent->r.maxs, ent->r.absmax ); } // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch ent->r.absmin[0] -= 1; ent->r.absmin[1] -= 1; ent->r.absmin[2] -= 1; ent->r.absmax[0] += 1; ent->r.absmax[1] += 1; ent->r.absmax[2] += 1; // link to PVS leafs ent->r.num_clusters = 0; ent->r.areanum = ent->r.areanum2 = -1; // get all leafs, including solids num_leafs = trap_CM_BoxLeafnums( ent->r.absmin, ent->r.absmax, leafs, MAX_TOTAL_ENT_LEAFS, &topnode ); // set areas for( i = 0; i < num_leafs; i++ ) { clusters[i] = trap_CM_LeafCluster( leafs[i] ); area = trap_CM_LeafArea( leafs[i] ); if( area > -1 ) { // doors may legally straggle two areas, // but nothing should ever need more than that if( ent->r.areanum > -1 && ent->r.areanum != area ) { if( ent->r.areanum2 > -1 && ent->r.areanum2 != area ) { if( developer->integer ) G_Printf( "Object %s touching 3 areas at %f %f %f\n", ( ent->classname ? ent->classname : "" ), ent->r.absmin[0], ent->r.absmin[1], ent->r.absmin[2] ); } ent->r.areanum2 = area; } else ent->r.areanum = area; } } if( num_leafs >= MAX_TOTAL_ENT_LEAFS ) { // assume we missed some leafs, and mark by headnode ent->r.num_clusters = -1; ent->r.headnode = topnode; } else { ent->r.num_clusters = 0; for( i = 0; i < num_leafs; i++ ) { if( clusters[i] == -1 ) continue; // not a visible leaf for( j = 0; j < i; j++ ) if( clusters[j] == clusters[i] ) break; if( j == i ) { if( ent->r.num_clusters == MAX_ENT_CLUSTERS ) { // assume we missed some leafs, and mark by headnode ent->r.num_clusters = -1; ent->r.headnode = topnode; break; } ent->r.clusternums[ent->r.num_clusters++] = clusters[i]; } } } // if first time, make sure old_origin is valid if( !ent->r.linkcount && !( ent->r.svflags & SVF_TRANSMITORIGIN2 ) ) { VectorCopy( ent->s.origin, ent->s.old_origin ); ent->olds = ent->s; } ent->r.linkcount++; ent->linked = qtrue; if( ent->r.solid == SOLID_NOT ) return; // find the first node that the ent's box crosses node = sv_areanodes; while( 1 ) { if( node->axis == -1 ) break; if( ent->r.absmin[node->axis] > node->dist ) node = node->children[0]; else if( ent->r.absmax[node->axis] < node->dist ) node = node->children[1]; else break; // crosses the node } // link it in if( ent->r.solid == SOLID_TRIGGER ) GClip_InsertLinkBefore( &ent->r.area, &node->trigger_edicts, NUM_FOR_EDICT( ent ) ); else GClip_InsertLinkBefore( &ent->r.area, &node->solid_edicts, NUM_FOR_EDICT( ent ) ); }
void GClip_LinkEntity( tvm_relay_t *relay, edict_t *ent ) { int leafs[MAX_TOTAL_ENT_LEAFS]; int clusters[MAX_TOTAL_ENT_LEAFS]; int num_leafs; int i, j; int area; int topnode; if( ent->r.area.prev ) GClip_UnlinkEntity( relay, ent ); // unlink from old position if( ent == ent->relay->edicts ) return; // don't add the world if( !ent->r.inuse ) return; // set the size VectorSubtract( ent->r.maxs, ent->r.mins, ent->r.size ); // set the abs box if( ent->s.solid == SOLID_BMODEL && ( ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2] ) ) { // expand for rotation float radius; radius = RadiusFromBounds( ent->r.mins, ent->r.maxs ); for( i = 0; i < 3; i++ ) { ent->r.absmin[i] = ent->s.origin[i] - radius; ent->r.absmax[i] = ent->s.origin[i] + radius; } } else { // normal VectorAdd( ent->s.origin, ent->r.mins, ent->r.absmin ); VectorAdd( ent->s.origin, ent->r.maxs, ent->r.absmax ); } // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch ent->r.absmin[0] -= 1; ent->r.absmin[1] -= 1; ent->r.absmin[2] -= 1; ent->r.absmax[0] += 1; ent->r.absmax[1] += 1; ent->r.absmax[2] += 1; // link to PVS leafs ent->r.num_clusters = 0; ent->r.areanum = ent->r.areanum2 = -1; // get all leafs, including solids num_leafs = trap_CM_BoxLeafnums( relay, ent->r.absmin, ent->r.absmax, leafs, MAX_TOTAL_ENT_LEAFS, &topnode ); // set areas for( i = 0; i < num_leafs; i++ ) { clusters[i] = trap_CM_LeafCluster( relay, leafs[i] ); area = trap_CM_LeafArea( relay, leafs[i] ); if( area > -1 ) { // doors may legally straggle two areas, // but nothing should ever need more than that if( ent->r.areanum > -1 && ent->r.areanum != area ) { if( ent->r.areanum2 > -1 && ent->r.areanum2 != area ) { TVM_Printf( "Object touching 3 areas at %f %f %f\n", ent->r.absmin[0], ent->r.absmin[1], ent->r.absmin[2] ); } ent->r.areanum2 = area; } else ent->r.areanum = area; } } if( num_leafs >= MAX_TOTAL_ENT_LEAFS ) { // assume we missed some leafs, and mark by headnode ent->r.num_clusters = -1; ent->r.headnode = topnode; } else { ent->r.num_clusters = 0; for( i = 0; i < num_leafs; i++ ) { if( clusters[i] == -1 ) continue; // not a visible leaf for( j = 0; j < i; j++ ) if( clusters[j] == clusters[i] ) break; if( j == i ) { if( ent->r.num_clusters == MAX_ENT_CLUSTERS ) { // assume we missed some leafs, and mark by headnode ent->r.num_clusters = -1; ent->r.headnode = topnode; break; } ent->r.clusternums[ent->r.num_clusters++] = clusters[i]; } } } // if first time, make sure old_origin is valid if( !ent->r.linkcount && !( ent->r.svflags & SVF_TRANSMITORIGIN2 ) ) { VectorCopy( ent->s.origin, ent->s.old_origin ); //ent->olds = ent->s; } ent->r.linkcount++; }