/* * body_think */ static void body_think( edict_t *self ) { self->health = GIB_HEALTH - 1; //effect: small gibs, and only when it is still a body, not a gibbed head. if( self->s.type == ET_CORPSE ) ThrowSmallPileOfGibs( self, 25 ); //disallow interaction with the world. self->takedamage = DAMAGE_NO; self->r.solid = SOLID_NOT; self->s.sound = 0; self->flags |= FL_NO_KNOCKBACK; self->s.type = ET_GENERIC; self->r.svflags &= ~SVF_CORPSE; self->r.svflags |= SVF_NOCLIENT; self->s.modelindex = 0; self->s.modelindex2 = 0; VectorClear( self->velocity ); VectorClear( self->avelocity ); self->movetype = MOVETYPE_NONE; self->think = NULL; //memset( &self->snap, 0, sizeof(self->snap) ); GClip_UnlinkEntity( self ); }
/* * G_TeleportPlayer */ void G_TeleportPlayer( edict_t *player, edict_t *dest ) { int i; vec3_t velocity; mat3_t axis; float speed; gclient_t *client = player->r.client; if( !dest ) { return; } if( !client ) { return; } // draw the teleport entering effect G_TeleportEffect( player, false ); // // teleport the player // // from racesow - use old pmove velocity VectorCopy( client->old_pmove.velocity, velocity ); velocity[2] = 0; // ignore vertical velocity speed = VectorLengthFast( velocity ); AnglesToAxis( dest->s.angles, axis ); VectorScale( &axis[AXIS_FORWARD], speed, client->ps.pmove.velocity ); VectorCopy( dest->s.angles, client->ps.viewangles ); VectorCopy( dest->s.origin, client->ps.pmove.origin ); // set the delta angle for ( i = 0; i < 3; i++ ) client->ps.pmove.delta_angles[i] = ANGLE2SHORT( client->ps.viewangles[i] ) - client->ucmd.angles[i]; client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT; client->ps.pmove.pm_time = 1; // force the minimum no control delay player->s.teleported = true; // update the entity from the pmove VectorCopy( client->ps.viewangles, player->s.angles ); VectorCopy( client->ps.pmove.origin, player->s.origin ); VectorCopy( client->ps.pmove.origin, player->s.old_origin ); VectorCopy( client->ps.pmove.origin, player->olds.origin ); VectorCopy( client->ps.pmove.velocity, player->velocity ); // unlink to make sure it can't possibly interfere with KillBox GClip_UnlinkEntity( player ); // kill anything at the destination KillBox( player ); GClip_LinkEntity( player ); // add the teleport effect at the destination G_TeleportEffect( player, true ); }
/* * TVM_ClientDisconnect * * Called when a player drops from the server. * Will not be called between levels. */ void TVM_ClientDisconnect( tvm_relay_t *relay, edict_t *ent ) { assert( ent && ent->local && ent->r.client ); //TVM_Printf( "Disconnect: %s\n", ent->r.client->pers.netname ); ent->r.inuse = false; ent->r.svflags = SVF_NOCLIENT; memset( ent->r.client, 0, sizeof( *ent->r.client ) ); ent->r.client->ps.playerNum = PLAYERNUM( ent ); GClip_UnlinkEntity( ent->relay, ent ); }
static void use_target_spawner( edict_t *self, edict_t *other, edict_t *activator ) { edict_t *ent; ent = G_Spawn(); ent->classname = self->target; VectorCopy( self->s.origin, ent->s.origin ); VectorCopy( self->s.angles, ent->s.angles ); G_CallSpawn( ent ); GClip_UnlinkEntity( ent ); KillBox( ent ); GClip_LinkEntity( ent ); if( self->speed ) VectorCopy( self->moveinfo.movedir, ent->velocity ); }
/* * ClientDisconnect * Called when a player drops from the server. * Will not be called between levels. */ void ClientDisconnect( edict_t *ent, const char *reason ) { int team; if( !ent->r.client || !ent->r.inuse ) return; // always report in RACE mode if( GS_RaceGametype() || ( ent->r.client->team != TEAM_SPECTATOR && ( GS_MatchState() == MATCH_STATE_PLAYTIME || GS_MatchState() == MATCH_STATE_POSTMATCH ) ) ) G_AddPlayerReport( ent, GS_MatchState() == MATCH_STATE_POSTMATCH ); for( team = TEAM_PLAYERS; team < GS_MAX_TEAMS; team++ ) G_Teams_UnInvitePlayer( team, ent ); if( !level.gametype.disableObituaries || !(ent->r.svflags & SVF_FAKECLIENT ) ) { if( !reason ) G_PrintMsg( NULL, "%s" S_COLOR_WHITE " disconnected\n", ent->r.client->netname ); else G_PrintMsg( NULL, "%s" S_COLOR_WHITE " disconnected (%s" S_COLOR_WHITE ")\n", ent->r.client->netname, reason ); } // send effect if( ent->s.team > TEAM_SPECTATOR ) G_TeleportEffect( ent, false ); ent->r.client->team = TEAM_SPECTATOR; G_ClientRespawn( ent, true ); // respawn as ghost ent->movetype = MOVETYPE_NOCLIP; // allow freefly // let the gametype scripts know this client just disconnected G_Gametype_ScoreEvent( ent->r.client, "disconnect", NULL ); G_FreeAI( ent ); AI_EnemyRemoved( ent ); ent->r.inuse = false; ent->r.svflags = SVF_NOCLIENT; memset( ent->r.client, 0, sizeof( *ent->r.client ) ); ent->r.client->ps.playerNum = PLAYERNUM( ent ); trap_ConfigString( CS_PLAYERINFOS+PLAYERNUM( ent ), "" ); GClip_UnlinkEntity( ent ); G_Match_CheckReadys(); }
/* * GClip_SetBrushModel * * Also sets mins and maxs for inline bmodels */ void GClip_SetBrushModel( edict_t *ent, char *name ) { struct cmodel_s *cmodel; if( !name ) { //racesow G_Printf( "Warning: GClip_SetBrushModel: NULL model in '%s'", ent->classname ? ent->classname : "no classname\n" ); GClip_UnlinkEntity( ent ); G_FreeEdict( ent ); return; //!racesow } if( !name[0] ) { ent->s.modelindex = 0; return; } if( name[0] != '*' ) { ent->s.modelindex = trap_ModelIndex( name ); return; } // if it is an inline model, get the size information for it // world model is special if( !strcmp( name, "*0" ) ) { ent->s.modelindex = 0; cmodel = trap_CM_InlineModel( 0 ); trap_CM_InlineModelBounds( cmodel, ent->r.mins, ent->r.maxs ); return; } // racesow: THIS IS A VERY DIRTY "FIX": it assigns normally unreachable models (the ones over MAX_MODELS) to unrelated models.. // normally this only affects buggy maps that otherwise wouldn't load (like amt-freestyle3) // brush model //ent->s.modelindex = trap_ModelIndex( name ); // <- This is the unmodified version ent->s.modelindex = atoi( name+1 ); // !racesow assert( ent->s.modelindex == (unsigned int)atoi( name + 1 ) ); cmodel = trap_CM_InlineModel( ent->s.modelindex ); trap_CM_InlineModelBounds( cmodel, ent->r.mins, ent->r.maxs ); GClip_LinkEntity( ent ); }
/* * GClip_SetBrushModel * * Also sets mins and maxs for inline bmodels */ void GClip_SetBrushModel( edict_t *ent, const char *name ) { struct cmodel_s *cmodel; if( !name ) { G_Error( "GClip_SetBrushModel: NULL model in '%s'", ent->classname ? ent->classname : "no classname" ); // racesow GClip_UnlinkEntity( ent ); G_FreeEdict( ent ); return; // !racesow } if( !name[0] ) { ent->s.modelindex = 0; return; } if( name[0] != '*' ) { ent->s.modelindex = trap_ModelIndex( name ); return; } // if it is an inline model, get the size information for it // world model is special if( !strcmp( name, "*0" ) ) { ent->s.modelindex = 0; cmodel = trap_CM_InlineModel( 0 ); trap_CM_InlineModelBounds( cmodel, ent->r.mins, ent->r.maxs ); return; } // brush model ent->s.modelindex = trap_ModelIndex( name ); assert( ent->s.modelindex == (unsigned int)atoi( name + 1 ) ); cmodel = trap_CM_InlineModel( ent->s.modelindex ); trap_CM_InlineModelBounds( cmodel, ent->r.mins, ent->r.maxs ); GClip_LinkEntity( ent ); }
/* * G_Match_FreeBodyQueue */ void G_Match_FreeBodyQueue( void ) { edict_t *ent; int i; ent = &game.edicts[gs.maxclients + 1]; for( i = 0; i < BODY_QUEUE_SIZE; ent++, i++ ) { if( !ent->r.inuse ) continue; if( ent->classname && !Q_stricmp( ent->classname, "body" ) ) { GClip_UnlinkEntity( ent ); ent->deadflag = DEAD_NO; ent->movetype = MOVETYPE_NONE; ent->r.solid = SOLID_NOT; ent->r.svflags = SVF_NOCLIENT; ent->s.type = ET_GENERIC; ent->s.skinnum = 0; ent->s.frame = 0; ent->s.modelindex = 0; ent->s.sound = 0; ent->s.effects = 0; ent->takedamage = DAMAGE_NO; ent->flags |= FL_NO_KNOCKBACK; GClip_LinkEntity( ent ); } } level.body_que = 0; }
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 ) ); }
//target_give wait classname weapon_xxx static void target_give_use( edict_t *self, edict_t *other, edict_t *activator ) { edict_t *give; const gsitem_t *item; int i, numsounds; float attenuation; const char *pickup_sound; int prev_pickup = -1; gclient_t *aclient = activator && activator->r.client ? activator->r.client : NULL; const gsitem_t *sounds[MAX_GIVE_SOUNDS]; give = NULL; numsounds = 0; // more than one item can be given while( ( give = G_Find( give, FOFS( targetname ), self->target ) ) != NULL ) { // sanity item = give->item; if( !item ) continue; if( !( item->flags & ITFLAG_PICKABLE ) ) continue; if( aclient ) { prev_pickup = aclient->ps.stats[STAT_PICKUP_ITEM]; } pickup_sound = item->pickup_sound; // disable pickup sound, we'll play it later attenuation = give->attenuation; give->attenuation = 0; Touch_Item( give, activator, NULL, 0 ); if( give->r.inuse ) { give->nextThink = 0; give->think = 0; give->attenuation = attenuation; GClip_UnlinkEntity( give ); } // a hacky way to check for successful item pickup if( aclient && aclient->ps.stats[STAT_PICKUP_ITEM] == item->tag && prev_pickup != item->tag ) { prev_pickup = item->tag; // see if we don't know this pickup sound yet if( pickup_sound ) { for( i = 0; i < numsounds; i++ ) { if( !Q_stricmp( sounds[i]->pickup_sound, pickup_sound ) ) break; } if( i == numsounds && numsounds < MAX_GIVE_SOUNDS ) { sounds[numsounds++] = item; } } } } // play unique pickup sounds for( i = 0; i < numsounds; i++ ) { Touch_ItemSound( activator, sounds[i] ); } }
static void old_teleporter_touch( edict_t *self, edict_t *other, cplane_t *plane, int surfFlags ) { edict_t *dest; int i; vec3_t velocity, angles; mat3_t axis; float speed; vec3_t org; if( !other->r.client ) return; if( self->s.team && self->s.team != other->s.team ) return; if( other->r.client->ps.pmove.pm_type > PM_SPECTATOR ) return; if( self->spawnflags & 1 && other->r.client->ps.pmove.pm_type != PM_SPECTATOR ) return; // match countdown if( GS_MatchState() == MATCH_STATE_COUNTDOWN ) return; // wait delay if( self->timeStamp > level.time ) return; self->timeStamp = level.time + ( self->wait * 1000 ); dest = G_Find( NULL, FOFS( targetname ), self->target ); if( !dest ) { if( developer->integer ) G_Printf( "Couldn't find destination.\n" ); return; } if( self->s.modelindex ) { org[0] = self->s.origin[0] + 0.5 * ( self->r.mins[0] + self->r.maxs[0] ); org[1] = self->s.origin[1] + 0.5 * ( self->r.mins[1] + self->r.maxs[1] ); org[2] = self->s.origin[2] + 0.5 * ( self->r.mins[2] + self->r.maxs[2] ); } else VectorCopy( self->s.origin, org ); // play custom sound if any (played from the teleporter entrance) if( self->noise_index ) G_PositionedSound( org, CHAN_AUTO, self->noise_index, ATTN_NORM ); // draw the teleport entering effect G_TeleportEffect( other, false ); // // teleport the player // VectorCopy( other->r.client->ps.pmove.velocity, velocity ); velocity[2] = 0; // ignore vertical velocity speed = VectorLengthFast( velocity ); // if someone enters a portal backwards, inverse the destination YAW angle #if 0 VectorCopy( other->s.angles, angles ); angles[PITCH] = 0; AngleVectors( angles, axis[0], NULL, NULL ); VectorSubtract( org, other->s.origin, org ); VectorCopy( dest->s.angles, angles ); if( DotProduct( org, axis[0] ) < 0 ) angles[YAW] = anglemod( angles[YAW] - 180 ); #else VectorCopy( dest->s.angles, angles ); #endif AnglesToAxis( dest->s.angles, axis ); VectorScale( &axis[AXIS_FORWARD], speed, other->r.client->ps.pmove.velocity ); VectorCopy( angles, other->r.client->ps.viewangles ); VectorCopy( dest->s.origin, other->r.client->ps.pmove.origin ); // set the delta angle for( i = 0; i < 3; i++ ) other->r.client->ps.pmove.delta_angles[i] = ANGLE2SHORT( other->r.client->ps.viewangles[i] ) - other->r.client->ucmd.angles[i]; other->r.client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT; other->s.teleported = qtrue; other->r.client->ps.pmove.pm_time = 1; // force the minimum no control delay // update the entity from the pmove VectorCopy( other->r.client->ps.viewangles, other->s.angles ); VectorCopy( other->r.client->ps.pmove.origin, other->s.origin ); VectorCopy( other->r.client->ps.pmove.origin, other->s.old_origin ); VectorCopy( other->r.client->ps.pmove.origin, other->olds.origin ); VectorCopy( other->r.client->ps.pmove.velocity, other->velocity ); // unlink to make sure it can't possibly interfere with KillBox GClip_UnlinkEntity( other ); // kill anything at the destination if( !KillBox( other ) ) { } GClip_LinkEntity( other ); // add the teleport effect at the destination G_TeleportEffect( other, true ); }
/* * G_ClientRespawn */ void G_ClientRespawn( edict_t *self, bool ghost ) { int i; edict_t *spawnpoint; vec3_t hull_mins, hull_maxs; vec3_t spawn_origin, spawn_angles; gclient_t *client; int old_team; G_DeathAwards( self ); G_SpawnQueue_RemoveClient( self ); self->r.svflags &= ~SVF_NOCLIENT; //if invalid be spectator if( self->r.client->team < 0 || self->r.client->team >= GS_MAX_TEAMS ) self->r.client->team = TEAM_SPECTATOR; // force ghost always to true when in spectator team if( self->r.client->team == TEAM_SPECTATOR ) ghost = true; old_team = self->s.team; if( self->r.client->teamstate.is_coach ) ghost = true; GClip_UnlinkEntity( self ); client = self->r.client; memset( &client->resp, 0, sizeof( client->resp ) ); memset( &client->ps, 0, sizeof( client->ps ) ); client->resp.timeStamp = level.time; client->ps.playerNum = PLAYERNUM( self ); // clear entity values memset( &self->snap, 0, sizeof( self->snap ) ); memset( &self->s, 0, sizeof( self->s ) ); memset( &self->olds, 0, sizeof( self->olds ) ); memset( &self->invpak, 0, sizeof( self->invpak ) ); self->s.number = self->olds.number = ENTNUM( self ); // relink client struct self->r.client = &game.clients[PLAYERNUM( self )]; // update team self->s.team = client->team; self->deadflag = DEAD_NO; self->s.type = ET_PLAYER; self->groundentity = NULL; self->takedamage = DAMAGE_AIM; self->think = player_think; self->pain = player_pain; self->die = player_die; self->viewheight = playerbox_stand_viewheight; self->r.inuse = true; self->mass = PLAYER_MASS; self->air_finished = level.time + ( 12 * 1000 ); self->r.clipmask = MASK_PLAYERSOLID; self->waterlevel = 0; self->watertype = 0; self->flags &= ~FL_NO_KNOCKBACK; self->r.svflags &= ~SVF_CORPSE; self->enemy = NULL; self->r.owner = NULL; self->max_health = 100; self->health = self->max_health; if( AI_GetType( self->ai ) == AI_ISBOT ) { self->think = NULL; self->classname = "bot"; } else if( self->r.svflags & SVF_FAKECLIENT ) self->classname = "fakeclient"; else self->classname = "player"; VectorCopy( playerbox_stand_mins, self->r.mins ); VectorCopy( playerbox_stand_maxs, self->r.maxs ); VectorClear( self->velocity ); VectorClear( self->avelocity ); VectorCopy( self->r.mins, hull_mins ); VectorCopy( self->r.maxs, hull_maxs ); trap_CM_RoundUpToHullSize( hull_mins, hull_maxs, NULL ); if( self->r.maxs[2] > hull_maxs[2] ) self->viewheight -= (self->r.maxs[2] - hull_maxs[2]); client->ps.POVnum = ENTNUM( self ); // set movement info client->ps.pmove.stats[PM_STAT_MAXSPEED] = (short)DEFAULT_PLAYERSPEED; client->ps.pmove.stats[PM_STAT_JUMPSPEED] = (short)DEFAULT_JUMPSPEED; client->ps.pmove.stats[PM_STAT_DASHSPEED] = (short)DEFAULT_DASHSPEED; if( ghost ) { self->r.solid = SOLID_NOT; self->movetype = MOVETYPE_NOCLIP; if( self->s.team == TEAM_SPECTATOR ) self->r.svflags |= SVF_NOCLIENT; } else { self->r.client->resp.takeStun = true; self->r.solid = SOLID_YES; self->movetype = MOVETYPE_PLAYER; client->ps.pmove.stats[PM_STAT_FEATURES] = static_cast<unsigned short>(PMFEAT_DEFAULT); if( !g_allow_bunny->integer ) client->ps.pmove.stats[PM_STAT_FEATURES] &= ~( PMFEAT_AIRCONTROL|PMFEAT_FWDBUNNY ); } ClientUserinfoChanged( self, client->userinfo ); if( old_team != self->s.team ) G_Teams_UpdateMembersList(); SelectSpawnPoint( self, &spawnpoint, spawn_origin, spawn_angles ); VectorCopy( spawn_origin, client->ps.pmove.origin ); VectorCopy( spawn_origin, self->s.origin ); VectorCopy( self->s.origin, self->s.old_origin ); // set angles self->s.angles[PITCH] = 0; self->s.angles[YAW] = anglemod( spawn_angles[YAW] ); self->s.angles[ROLL] = 0; VectorCopy( self->s.angles, client->ps.viewangles ); // set the delta angle for( i = 0; i < 3; i++ ) client->ps.pmove.delta_angles[i] = ANGLE2SHORT( client->ps.viewangles[i] ) - client->ucmd.angles[i]; // don't put spectators in the game if( !ghost ) { if( KillBox( self ) ) { } } self->s.attenuation = ATTN_NORM; self->s.teleported = true; // hold in place briefly client->ps.pmove.pm_flags = PMF_TIME_TELEPORT; client->ps.pmove.pm_time = 14; client->ps.pmove.stats[PM_STAT_NOUSERCONTROL] = CLIENT_RESPAWN_FREEZE_DELAY; client->ps.pmove.stats[PM_STAT_NOAUTOATTACK] = 1000; // set race stats to invisible client->ps.stats[STAT_TIME_SELF] = STAT_NOTSET; client->ps.stats[STAT_TIME_BEST] = STAT_NOTSET; client->ps.stats[STAT_TIME_RECORD] = STAT_NOTSET; client->ps.stats[STAT_TIME_ALPHA] = STAT_NOTSET; client->ps.stats[STAT_TIME_BETA] = STAT_NOTSET; BOT_Respawn( self ); self->r.client->level.respawnCount++; G_UseTargets( spawnpoint, self ); GClip_LinkEntity( self ); // let the gametypes perform their changes if( game.asEngine != NULL ) GT_asCallPlayerRespawn( self, old_team, self->s.team ); else G_Gametype_GENERIC_ClientRespawn( self, old_team, self->s.team ); }
/* * CopyToBodyQue */ static edict_t *CopyToBodyQue( edict_t *ent, edict_t *attacker, int damage ) { edict_t *body; int contents; if( GS_RaceGametype() ) return NULL; contents = G_PointContents( ent->s.origin ); if( contents & CONTENTS_NODROP ) return NULL; G_Client_UnlinkBodies( ent ); // grab a body que and cycle to the next one body = &game.edicts[gs.maxclients + level.body_que + 1]; level.body_que = ( level.body_que + 1 ) % BODY_QUEUE_SIZE; // send an effect on the removed body if( body->s.modelindex && body->s.type == ET_CORPSE ) ThrowSmallPileOfGibs( body, 10 ); GClip_UnlinkEntity( body ); memset( body, 0, sizeof( edict_t ) ); //clean up garbage //init body edict G_InitEdict( body ); body->classname = "body"; body->health = ent->health; body->mass = ent->mass; body->r.owner = ent->r.owner; body->s.type = ent->s.type; body->s.team = ent->s.team; body->s.effects = 0; body->r.svflags = SVF_CORPSE; body->r.svflags &= ~SVF_NOCLIENT; body->activator = ent; if( g_deadbody_followkiller->integer ) body->enemy = attacker; //use flat yaw body->s.angles[PITCH] = 0; body->s.angles[ROLL] = 0; body->s.angles[YAW] = ent->s.angles[YAW]; body->s.modelindex2 = 0; // <- is bodyOwner when in ET_CORPSE, but not in ET_GENERIC or ET_PLAYER body->s.weapon = 0; //copy player position and box size VectorCopy( ent->s.old_origin, body->s.old_origin ); VectorCopy( ent->s.origin, body->s.origin ); VectorCopy( ent->s.origin, body->olds.origin ); VectorCopy( ent->r.mins, body->r.mins ); VectorCopy( ent->r.maxs, body->r.maxs ); VectorCopy( ent->r.absmin, body->r.absmin ); VectorCopy( ent->r.absmax, body->r.absmax ); VectorCopy( ent->r.size, body->r.size ); VectorCopy( ent->velocity, body->velocity ); body->r.maxs[2] = body->r.mins[2] + 8; body->r.solid = SOLID_YES; body->takedamage = DAMAGE_YES; body->r.clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP; body->movetype = MOVETYPE_TOSS; body->die = body_die; body->think = body_think; // body self destruction countdown if( ent->health < GIB_HEALTH || meansOfDeath == MOD_ELECTROBOLT_S /* electrobolt always gibs */ ) { ThrowSmallPileOfGibs( body, damage ); // reset gib impulse VectorClear( body->velocity ); ThrowClientHead( body, damage ); // sets ET_GIB body->s.frame = 0; body->nextThink = level.time + 3000 + random() * 3000; body->deadflag = DEAD_DEAD; } else if( ent->s.type == ET_PLAYER ) { // copy the model body->s.type = ET_CORPSE; body->s.modelindex = ent->s.modelindex; body->s.bodyOwner = ent->s.number; // bodyOwner is the same as modelindex2 body->s.skinnum = ent->s.skinnum; body->s.teleported = true; // launch the death animation on the body { static int i; i = ( i+1 )%3; G_AddEvent( body, EV_DIE, i, true ); switch( i ) { default: case 0: body->s.frame = ( ( BOTH_DEAD1&0x3F )|( BOTH_DEAD1&0x3F )<<6|( 0 &0xF )<<12 ); break; case 1: body->s.frame = ( ( BOTH_DEAD2&0x3F )|( BOTH_DEAD2&0x3F )<<6|( 0 &0xF )<<12 ); break; case 2: body->s.frame = ( ( BOTH_DEAD3&0x3F )|( BOTH_DEAD3&0x3F )<<6|( 0 &0xF )<<12 ); break; } } body->think = body_ready; body->takedamage = DAMAGE_NO; body->r.solid = SOLID_NOT; body->nextThink = level.time + 500; // make damageable in 0.5 seconds } else // wasn't a player, just copy it's model { VectorClear( body->velocity ); body->s.modelindex = ent->s.modelindex; body->s.frame = ent->s.frame; body->nextThink = level.time + 5000 + random()*10000; } GClip_LinkEntity( body ); return body; }
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++; }