/*QUAKED target_autosave (1 1 0) (-8 -8 -8) (8 8 8) saves game to 'autosave.sav' when triggered then dies. */ void SP_target_autosave(gentity_t *ent) { // ent->use = Use_Target_Autosave; G_FreeEntity(ent); }
/* ================== Cmd_Give_f Give items to a client ================== */ void Cmd_Give_f (gentity_t *ent) { char *name; gitem_t *it; int i; qboolean give_all; gentity_t *it_ent; trace_t trace; if ( !CheatsOk( ent ) ) { return; } name = ConcatArgs( 1 ); if (Q_stricmp(name, "all") == 0) give_all = qtrue; else give_all = qfalse; if (give_all || Q_stricmp( name, "health") == 0) { ent->health = ent->client->ps.stats[STAT_MAX_HEALTH]; if (!give_all) return; } if (give_all || Q_stricmp(name, "weapons") == 0) { ent->client->ps.stats[STAT_WEAPONS] = (1 << WP_NUM_WEAPONS) - 1 - ( 1 << WP_GRAPPLING_HOOK ) - ( 1 << WP_NONE ); if (!give_all) return; } if (give_all || Q_stricmp(name, "ammo") == 0) { for ( i = 0 ; i < MAX_WEAPONS ; i++ ) { ent->client->ps.ammo[i] = 999; } if (!give_all) return; } if (give_all || Q_stricmp(name, "armor") == 0) { ent->client->ps.stats[STAT_ARMOR] = 200; if (!give_all) return; } if (Q_stricmp(name, "excellent") == 0) { ent->client->ps.persistant[PERS_EXCELLENT_COUNT]++; return; } if (Q_stricmp(name, "impressive") == 0) { ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++; return; } if (Q_stricmp(name, "gauntletaward") == 0) { ent->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++; return; } if (Q_stricmp(name, "defend") == 0) { ent->client->ps.persistant[PERS_DEFEND_COUNT]++; return; } if (Q_stricmp(name, "assist") == 0) { ent->client->ps.persistant[PERS_ASSIST_COUNT]++; return; } // spawn a specific item right on the player if ( !give_all ) { it = BG_FindItem (name); if (!it) { return; } it_ent = G_Spawn(); VectorCopy( ent->r.currentOrigin, it_ent->s.origin ); it_ent->classname = it->classname; G_SpawnItem (it_ent, it); FinishSpawningItem(it_ent ); memset( &trace, 0, sizeof( trace ) ); Touch_Item (it_ent, ent, &trace); if (it_ent->inuse) { G_FreeEntity( it_ent ); } } }
/** * @brief Run item. */ void G_RunItem(gentity_t *ent) { vec3_t origin; trace_t tr; int contents; int mask; // if groundentity has been set to -1, it may have been pushed off an edge if (ent->s.groundEntityNum == -1) { if (ent->s.pos.trType != TR_GRAVITY) { ent->s.pos.trType = TR_GRAVITY; ent->s.pos.trTime = level.time; } } if (ent->s.pos.trType == TR_STATIONARY || ent->s.pos.trType == TR_GRAVITY_PAUSED) // check think function { G_RunThink(ent); return; } if (ent->s.pos.trType == TR_LINEAR && (!ent->clipmask && !ent->r.contents)) { // check think function G_RunThink(ent); return; } // get current position BG_EvaluateTrajectory(&ent->s.pos, level.time, origin, qfalse, ent->s.effect2Time); // trace a line from the previous position to the current position if (ent->clipmask) { mask = ent->clipmask; } else { mask = MASK_SOLID; } trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->r.ownerNum, mask); if (ent->isProp && ent->takedamage) { G_RunItemProp(ent, origin); } VectorCopy(tr.endpos, ent->r.currentOrigin); if (tr.startsolid) { tr.fraction = 0; } trap_LinkEntity(ent); // FIXME: avoid this for stationary? // check think function G_RunThink(ent); if (tr.fraction == 1) { return; } // if it is in a nodrop volume, remove it contents = trap_PointContents(ent->r.currentOrigin, -1); if (contents & CONTENTS_NODROP) { if (ent->item && ent->item->giType == IT_TEAM) { Team_ReturnFlag(ent); } else { G_FreeEntity(ent); } return; } G_BounceItem(ent, &tr); }
void trigger_always_think(gentity_t *ent) { G_UseTargets(ent, ent); G_FreeEntity(ent); }
/* ================== Cmd_Give_f Give items to a client ================== */ void Cmd_Give_f (gentity_t *ent) { char *name; gitem_t *it; int i; qboolean give_all; if ( !CheatsOk( ent ) ) { return; } name = ConcatArgs( 1 ); if (Q_stricmp(name, "all") == 0) give_all = qtrue; else give_all = qfalse; if (give_all || Q_stricmp(name, "force") == 0) { if ( ent->client ) { ent->client->ps.forcePower = FORCE_POWER_MAX; } if (!give_all) return; } if (give_all || Q_stricmp(gi.argv(1), "health") == 0) { if (gi.argc() == 3) { ent->health = atoi(gi.argv(2)); if (ent->health > ent->client->ps.stats[STAT_MAX_HEALTH]) { ent->health = ent->client->ps.stats[STAT_MAX_HEALTH]; } } else { ent->health = ent->client->ps.stats[STAT_MAX_HEALTH]; } if (!give_all) return; } /* if (give_all || Q_stricmp(name, "inventory") == 0) { // Huh? Was doing a INV_MAX+1 which was wrong because then you'd actually have every inventory item including INV_MAX ent->client->ps.stats[STAT_ITEMS] = (1 << (INV_MAX)) - ( 1 << INV_ELECTROBINOCULARS ); ent->client->ps.inventory[INV_ELECTROBINOCULARS] = 1; //ent->client->ps.inventory[INV_BACTA_CANISTER] = 5; //ent->client->ps.inventory[INV_SEEKER] = 5; ent->client->ps.inventory[INV_LIGHTAMP_GOGGLES] = 1; //ent->client->ps.inventory[INV_SENTRY] = 5; //ent->client->ps.inventory[INV_GOODIE_KEY] = 5; //ent->client->ps.inventory[INV_SECURITY_KEY] = 5; if (!give_all) { return; } } */ if (give_all || Q_stricmp(name, "weapons") == 0) { ent->client->ps.stats[STAT_WEAPONS] = (1 << (WP_MELEE)) - ( 1 << WP_NONE ); if (!give_all) return; } if ( !give_all && Q_stricmp(gi.argv(1), "weaponnum") == 0 ) { ent->client->ps.stats[STAT_WEAPONS] |= (1 << atoi(gi.argv(2))); return; } if ( Q_stricmp(name, "eweaps") == 0) //for developing, gives you all the weapons, including enemy { ent->client->ps.stats[STAT_WEAPONS] = (unsigned)(1 << WP_NUM_WEAPONS) - ( 1 << WP_NONE ); // NOTE: this wasn't giving the last weapon in the list if (!give_all) return; } if (give_all || Q_stricmp(name, "ammo") == 0) { for ( i = 0 ; i < AMMO_MAX ; i++ ) { ent->client->ps.ammo[i] = ammoData[i].max; } if (!give_all) return; } if (give_all || Q_stricmp(gi.argv(1), "batteries") == 0) { if (gi.argc() == 3) ent->client->ps.batteryCharge = atoi(gi.argv(2)); else ent->client->ps.batteryCharge = MAX_BATTERIES; if (!give_all) return; } if (give_all || Q_stricmp(gi.argv(1), "armor") == 0) { if (gi.argc() == 3) ent->client->ps.stats[STAT_ARMOR] = atoi(gi.argv(2)); else ent->client->ps.stats[STAT_ARMOR] = ent->client->ps.stats[STAT_MAX_HEALTH]; if ( ent->client->ps.stats[STAT_ARMOR] > 0 ) { ent->client->ps.powerups[PW_BATTLESUIT] = Q3_INFINITE; } else { ent->client->ps.powerups[PW_BATTLESUIT] = 0; } if (!give_all) return; } // spawn a specific item right on the player if ( !give_all ) { gentity_t *it_ent; trace_t trace; it = FindItem (name); if (!it) { name = gi.argv(1); it = FindItem (name); if (!it) { gi.SendServerCommand( ent-g_entities, "print \"unknown item\n\""); return; } } it_ent = G_Spawn(); VectorCopy( ent->currentOrigin, it_ent->s.origin ); it_ent->classname = G_NewString(it->classname); G_SpawnItem (it_ent, it); FinishSpawningItem(it_ent ); memset( &trace, 0, sizeof( trace ) ); Touch_Item (it_ent, ent, &trace); if (it_ent->inuse) { G_FreeEntity( it_ent ); } } }
qboolean IsLegShot( gentity_t *targ, vec3_t dir, vec3_t point, int mod ) { float height; float theight; gentity_t *leg; if (!(targ->client)) return qfalse; if (targ->health <= 0) return qfalse; if(!point) { return qfalse; } if(!IsHeadShotWeapon(mod)) { return qfalse; } leg = G_BuildLeg( targ ); if( leg ) { gentity_t *traceEnt; vec3_t start, end; trace_t tr; // trace another shot see if we hit the legs VectorCopy( point, start ); VectorMA( start, 64, dir, end ); trap_Trace( &tr, start, NULL, NULL, end, targ->s.number, MASK_SHOT ); traceEnt = &g_entities[ tr.entityNum ]; if( g_debugBullets.integer >= 3 ) { // show hit player head bb gentity_t *tent; vec3_t b1, b2; VectorCopy( leg->r.currentOrigin, b1 ); VectorCopy( leg->r.currentOrigin, b2 ); VectorAdd( b1, leg->r.mins, b1 ); VectorAdd( b2, leg->r.maxs, b2 ); tent = G_TempEntity( b1, EV_RAILTRAIL ); VectorCopy( b2, tent->s.origin2 ); tent->s.dmgFlags = 1; // show headshot trace // end the headshot trace at the head box if it hits if( tr.fraction != 1 ) { VectorMA( start, (tr.fraction * 64), dir, end ); } tent = G_TempEntity( start, EV_RAILTRAIL ); VectorCopy( end, tent->s.origin2 ); tent->s.dmgFlags = 0; } G_FreeEntity( leg ); if( traceEnt == leg ) { return qtrue; } } else { height = point[2] - targ->r.absmin[2]; theight = targ->r.absmax[2] - targ->r.absmin[2]; if(height < (theight * 0.4f)) { return qtrue; } } return qfalse; }
void misc_model_breakable_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int dFlags,int hitLoc ) { int numChunks; float size = 0, scale; vec3_t dir, up, dis; //NOTE: Stop any scripts that are currently running (FLUSH)... ? //Turn off animation self->s.frame = self->startFrame = self->endFrame = 0; self->svFlags &= ~SVF_ANIMATING; self->health = 0; //Throw some chunks AngleVectors( self->s.apos.trBase, dir, NULL, NULL ); VectorNormalize( dir ); numChunks = random() * 6 + 20; VectorSubtract( self->absmax, self->absmin, dis ); // This formula really has no logical basis other than the fact that it seemed to be the closest to yielding the results that I wanted. // Volume is length * width * height...then break that volume down based on how many chunks we have scale = sqrt( sqrt( dis[0] * dis[1] * dis[2] )) * 1.75f; if ( scale > 48 ) { size = 2; } else if ( scale > 24 ) { size = 1; } scale = scale / numChunks; if ( self->radius > 0.0f ) { // designer wants to scale number of chunks, helpful because the above scale code is far from perfect // I do this after the scale calculation because it seems that the chunk size generally seems to be very close, it's just the number of chunks is a bit weak numChunks *= self->radius; } VectorAdd( self->absmax, self->absmin, dis ); VectorScale( dis, 0.5f, dis ); CG_Chunks( self->s.number, dis, dir, self->absmin, self->absmax, 300, numChunks, self->material, self->s.modelindex3, scale ); self->e_PainFunc = painF_NULL; self->e_DieFunc = dieF_NULL; // self->e_UseFunc = useF_NULL; self->takedamage = qfalse; if ( !(self->spawnflags & 4) ) {//We don't want to stay solid self->s.solid = 0; self->contents = 0; self->clipmask = 0; gi.linkentity(self); } VectorSet(up, 0, 0, 1); if(self->target) { G_UseTargets(self, attacker); } if(inflictor->client) { VectorSubtract( self->currentOrigin, inflictor->currentOrigin, dir ); VectorNormalize( dir ); } else { VectorCopy(up, dir); } if ( !(self->spawnflags & 2048) ) // NO_EXPLOSION { // Ok, we are allowed to explode, so do it now! if(self->splashDamage > 0 && self->splashRadius > 0) {//explode vec3_t org; AddSightEvent( attacker, self->currentOrigin, 256, AEL_DISCOVERED, 100 ); AddSoundEvent( attacker, self->currentOrigin, 128, AEL_DISCOVERED ); //FIXME: specify type of explosion? (barrel, electrical, etc.) Also, maybe just use the explosion effect below since it's // a bit better? // up the origin a little for the damage check, because several models have their origin on the ground, so they don't alwasy do damage, not the optimal solution... VectorCopy( self->currentOrigin, org ); if ( self->mins[2] > -4 ) {//origin is going to be below it or very very low in the model //center the origin org[2] = self->currentOrigin[2] + self->mins[2] + (self->maxs[2] - self->mins[2])/2.0f; } G_RadiusDamage( org, self, self->splashDamage, self->splashRadius, self, MOD_UNKNOWN ); if ( self->model && Q_stricmp( "models/map_objects/ships/tie_fighter.md3", self->model ) == 0 ) {//TEMP HACK for Tie Fighters- they're HUGE G_PlayEffect( "fighter_explosion2", self->currentOrigin ); G_Sound( self, G_SoundIndex( "sound/weapons/tie_fighter/TIEexplode.wav" ) ); } else { CG_MiscModelExplosion( self->absmin, self->absmax, size, self->material ); G_Sound( self, G_SoundIndex("sound/weapons/explosions/cargoexplode.wav") ); } } else {//just break AddSightEvent( attacker, self->currentOrigin, 128, AEL_DISCOVERED ); AddSoundEvent( attacker, self->currentOrigin, 64, AEL_SUSPICIOUS ); // This is the default explosion CG_MiscModelExplosion( self->absmin, self->absmax, size, self->material ); G_Sound(self, G_SoundIndex("sound/weapons/explosions/cargoexplode.wav")); } } self->e_ThinkFunc = thinkF_NULL; self->nextthink = -1; if(self->s.modelindex2 != -1 && !(self->spawnflags & 8)) {//FIXME: modelindex doesn't get set to -1 if the damage model doesn't exist self->svFlags |= SVF_BROKEN; self->s.modelindex = self->s.modelindex2; G_ActivateBehavior( self, BSET_DEATH ); } else { G_FreeEntity( self ); } }
/* ================ G_MissileImpact ================ */ void G_MissileImpact( gentity_t *ent, trace_t *trace ) { gentity_t *other, *attacker; qboolean returnAfterDamage = qfalse; vec3_t dir; other = &g_entities[ trace->entityNum ]; attacker = &g_entities[ ent->r.ownerNum ]; // check for bounce if( !other->takedamage && ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); //only play a sound if requested if( !( ent->s.eFlags & EF_NO_BOUNCE_SOUND ) ) G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } if( !strcmp( ent->classname, "grenade" ) ) { //grenade doesn't explode on impact G_BounceMissile( ent, trace ); //only play a sound if requested if( !( ent->s.eFlags & EF_NO_BOUNCE_SOUND ) ) G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } else if( !strcmp( ent->classname, "lockblob" ) ) { if( other->client && other->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) { other->client->ps.stats[ STAT_STATE ] |= SS_BLOBLOCKED; other->client->lastLockTime = level.time; AngleVectors( other->client->ps.viewangles, dir, NULL, NULL ); other->client->ps.stats[ STAT_VIEWLOCK ] = DirToByte( dir ); } } else if( !strcmp( ent->classname, "slowblob" ) ) { if( other->client && other->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) { other->client->ps.stats[ STAT_STATE ] |= SS_SLOWLOCKED; other->client->lastSlowTime = level.time; AngleVectors( other->client->ps.viewangles, dir, NULL, NULL ); other->client->ps.stats[ STAT_VIEWLOCK ] = DirToByte( dir ); } } else if( !strcmp( ent->classname, "hive" ) ) { if( other->s.eType == ET_BUILDABLE && other->s.modelindex == BA_A_HIVE ) { if( !ent->parent ) G_Printf( S_COLOR_YELLOW "WARNING: hive entity has no parent in G_MissileImpact\n" ); else ent->parent->active = qfalse; G_FreeEntity( ent ); return; } else { //prevent collision with the client when returning ent->r.ownerNum = other->s.number; ent->think = G_ExplodeMissile; ent->nextthink = level.time + FRAMETIME; //only damage humans if( other->client && other->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) returnAfterDamage = qtrue; else return; } } // impact damage if( other->takedamage ) { // FIXME: wrong damage direction? if( ent->damage ) { vec3_t velocity; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if( VectorLength( velocity ) == 0 ) velocity[ 2 ] = 1; // stepped on a grenade G_Damage( other, ent, attacker, velocity, ent->s.origin, ent->damage, DAMAGE_NO_LOCDAMAGE, ent->methodOfDeath ); } } if( returnAfterDamage ) return; // is it cheaper in bandwidth to just remove this ent and create a new // one, rather than changing the missile into the explosion? if( other->takedamage && ( other->s.eType == ET_PLAYER || other->s.eType == ET_BUILDABLE ) ) { G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); ent->s.otherEntityNum = other->s.number; } else if( trace->surfaceFlags & SURF_METALSTEPS ) G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) ); else G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact ent->s.eType = ET_GENERAL; SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); // save net bandwidth G_SetOrigin( ent, trace->endpos ); // splash damage (doesn't apply to person directly hit) if( ent->splashDamage ) G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ); trap_LinkEntity( ent ); }
/* ================ G_RunMissile ================ */ void G_RunMissile( gentity_t *ent ) { vec3_t origin; trace_t tr; int passent; qboolean impact = qfalse; // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); // ignore interactions with the missile owner passent = ent->r.ownerNum; // general trace to see if we hit anything at all trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask ); if( tr.startsolid || tr.allsolid ) { tr.fraction = 0.0f; VectorCopy( ent->r.currentOrigin, tr.endpos ); } if( tr.fraction < 1.0f ) { if( !ent->pointAgainstWorld || tr.contents & CONTENTS_BODY ) { // We hit an entity or we don't care impact = qtrue; } else { trap_Trace( &tr, ent->r.currentOrigin, NULL, NULL, origin, passent, ent->clipmask ); if( tr.fraction < 1.0f ) { // Hit the world with point trace impact = qtrue; } else { if( tr.contents & CONTENTS_BODY ) { // Hit an entity impact = qtrue; } else { trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, CONTENTS_BODY ); if( tr.fraction < 1.0f ) impact = qtrue; } } } } VectorCopy( tr.endpos, ent->r.currentOrigin ); if( impact ) { if( tr.surfaceFlags & SURF_NOIMPACT ) { // Never explode or bounce on sky G_FreeEntity( ent ); return; } G_MissileImpact( ent, &tr ); if( ent->s.eType != ET_MISSILE ) return; // exploded } ent->r.contents = CONTENTS_SOLID; //trick trap_LinkEntity into... trap_LinkEntity( ent ); ent->r.contents = 0; //...encoding bbox information // check think function after bouncing G_RunThink( ent ); }
// Set up snapshot merge based on this portal qboolean G_smvRunCamera( gentity_t *ent ) { int id = ent->TargetFlag; int chargeTime, sprintTime, hintTime, weapHeat; playerState_t *tps, *ps; // Opt out if not a real MV portal if ( ent->tagParent == NULL || ent->tagParent->client == NULL ) { return( qfalse ); } if ( ( ps = &ent->tagParent->client->ps ) == NULL ) { return( qfalse ); } // If viewing client is no longer connected, delete this camera if ( ent->tagParent->client->pers.connected != CON_CONNECTED ) { G_FreeEntity( ent ); return( qtrue ); } // Also remove if the target player is no longer in the game playing if ( ent->target_ent->client->pers.connected != CON_CONNECTED || ent->target_ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { G_smvLocateEntityInMVList( ent->tagParent, ent->target_ent - g_entities, qtrue ); return( qtrue ); } // Seems that depending on player's state, we pull from either r.currentOrigin, or s.origin // if(!spec) then use: r.currentOrigin // if(spec) then use: s.origin // // This is true for both the portal origin and its target (origin2) // VectorCopy( ent->tagParent->s.origin, ent->s.origin ); G_SetOrigin( ent, ent->s.origin ); VectorCopy( ent->target_ent->r.currentOrigin, ent->s.origin2 ); trap_LinkEntity( ent ); // Only allow client ids 0 to (MAX_MVCLIENTS-1) to be updated with extra info if ( id >= MAX_MVCLIENTS ) { return( qtrue ); } tps = &ent->target_ent->client->ps; if ( tps->stats[STAT_PLAYER_CLASS] == PC_ENGINEER ) { chargeTime = g_engineerChargeTime.value; } else if ( tps->stats[STAT_PLAYER_CLASS] == PC_MEDIC ) { chargeTime = g_medicChargeTime.value; } else if ( tps->stats[STAT_PLAYER_CLASS] == PC_FIELDOPS ) { chargeTime = g_LTChargeTime.value; } else if ( tps->stats[STAT_PLAYER_CLASS] == PC_COVERTOPS ) { chargeTime = g_covertopsChargeTime.value; } else { chargeTime = g_soldierChargeTime.value;} chargeTime = ( level.time - tps->classWeaponTime >= (int)chargeTime ) ? 0 : ( 1 + floor( 15.0f * (float)( level.time - tps->classWeaponTime ) / chargeTime ) ); sprintTime = ( ent->target_ent->client->pmext.sprintTime >= 20000 ) ? 0.0f : ( 1 + floor( 7.0f * (float)ent->target_ent->client->pmext.sprintTime / 20000.0f ) ); weapHeat = floor( (float)tps->curWeapHeat * 15.0f / 255.0f ); hintTime = ( tps->serverCursorHint != HINT_BUILD && ( tps->serverCursorHintVal >= 255 || tps->serverCursorHintVal == 0 ) ) ? 0 : ( 1 + floor( 15.0f * (float)tps->serverCursorHintVal / 255.0f ) ); // (Remaining bits) // ammo : 0 // ammo-1 : 0 // ammiclip : 0 // ammoclip-1: 16 id = MAX_WEAPONS - 1 - ( id * 2 ); if ( tps->pm_flags & PMF_LIMBO ) { ps->ammo[id] = 0; ps->ammo[id - 1] = 0; ps->ammoclip[id - 1] = 0; } else { ps->ammo[id] = ( ( ( ent->target_ent->health > 0 ) ? ent->target_ent->health : 0 ) & 0xFF ); // Meds up to 140 :( ps->ammo[id] |= ( hintTime & 0x0F ) << 8; // 4 bits for work on current item (dynamite, weapon repair, etc.) ps->ammo[id] |= ( weapHeat & 0x0F ) << 12; // 4 bits for weapon heat info ps->ammo[id - 1] = tps->ammo[BG_FindAmmoForWeapon( tps->weapon )] & 0x3FF; // 11 bits needed to cover 1500 Venom ammo ps->ammo[id - 1] |= ( BG_simpleWeaponState( tps->weaponstate ) & 0x03 ) << 11; // 2 bits for current weapon state ps->ammo[id - 1] |= ( ( tps->persistant[PERS_HWEAPON_USE] ) ? 1 : 0 ) << 13; // 1 bit for mg42 use ps->ammo[id - 1] |= ( BG_simpleHintsCollapse( tps->serverCursorHint, hintTime ) & 0x03 ) << 14; // 2 bits for cursor hints // G_Printf("tps->hint: %d, dr: %d, collapse: %d\n", tps->serverCursorHint, HINT_DOOR_ROTATING, G_simpleHintsCollapse(tps->serverCursorHint, hintTime)); ps->ammoclip[id - 1] = tps->ammoclip[BG_FindClipForWeapon( tps->weapon )] & 0x1FF; // 9 bits to cover 500 Venom ammo clip ps->ammoclip[id - 1] |= ( chargeTime & 0x0F ) << 9; // 4 bits for weapon charge time ps->ammoclip[id - 1] |= ( sprintTime & 0x07 ) << 13; // 3 bits for fatigue } return( qtrue ); }
/* QUAKED trigger_concussive_dust (.5 .5 .5) ? Allows client side prediction of teleportation events. Must point at a target_position, which will be the teleport destination. */ void SP_trigger_concussive_dust(gentity_t *self) { G_Printf("trigger_concussive_dust is obsolete, please delete it.\n"); G_FreeEntity(self); }
void FinishSpawningItem( gentity_t *ent ) { trace_t tr; vec3_t dest; gitem_t *item; int itemNum; itemNum=1; for ( item = bg_itemlist + 1 ; item->classname ; item++,itemNum++) { if (!strcmp(item->classname,ent->classname)) { break; } } // Set bounding box for item VectorSet( ent->mins, item->mins[0],item->mins[1] ,item->mins[2]); VectorSet( ent->maxs, item->maxs[0],item->maxs[1] ,item->maxs[2]); if ((!ent->mins[0] && !ent->mins[1] && !ent->mins[2]) && (!ent->maxs[0] && !ent->maxs[1] && !ent->maxs[2])) { VectorSet (ent->mins, -ITEM_RADIUS, -ITEM_RADIUS, -2);//to match the comments in the items.dat file! VectorSet (ent->maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS); } if ((item->quantity) && (item->giType == IT_AMMO)) { ent->count = item->quantity; } if ((item->quantity) && (item->giType == IT_BATTERY)) { ent->count = item->quantity; } // if ( item->giType == IT_WEAPON ) // NOTE: james thought it was ok to just always do this? { ent->s.radius = 20; VectorSet( ent->s.modelScale, 1.0f, 1.0f, 1.0f ); gi.G2API_InitGhoul2Model( ent->ghoul2, ent->item->world_model, G_ModelIndex( ent->item->world_model ), NULL, NULL, 0, 0); } // Set crystal ammo amount based on skill level /* if ((itemNum == ITM_AMMO_CRYSTAL_BORG) || (itemNum == ITM_AMMO_CRYSTAL_DN) || (itemNum == ITM_AMMO_CRYSTAL_FORGE) || (itemNum == ITM_AMMO_CRYSTAL_SCAVENGER) || (itemNum == ITM_AMMO_CRYSTAL_STASIS)) { CrystalAmmoSettings(ent); } */ ent->s.eType = ET_ITEM; ent->s.modelindex = ent->item - bg_itemlist; // store item number in modelindex ent->s.modelindex2 = 0; // zero indicates this isn't a dropped item ent->contents = CONTENTS_TRIGGER|CONTENTS_ITEM;//CONTENTS_BODY;//CONTENTS_TRIGGER| ent->e_TouchFunc = touchF_Touch_Item; // useing an item causes it to respawn ent->e_UseFunc = useF_Use_Item; ent->svFlags |= SVF_PLAYER_USABLE;//so player can pick it up // Hang in air? ent->s.origin[2] += 1;//just to get it off the damn ground because coplanar = insolid if ( ent->spawnflags & ITMSF_SUSPEND) { // suspended G_SetOrigin( ent, ent->s.origin ); } else { // drop to floor VectorSet( dest, ent->s.origin[0], ent->s.origin[1], MIN_WORLD_COORD ); gi.trace( &tr, ent->s.origin, ent->mins, ent->maxs, dest, ent->s.number, MASK_SOLID|CONTENTS_PLAYERCLIP, G2_NOCOLLIDE, 0 ); if ( tr.startsolid ) { if ( &g_entities[tr.entityNum] != NULL ) { gi.Printf (S_COLOR_RED"FinishSpawningItem: removing %s startsolid at %s (in a %s)\n", ent->classname, vtos(ent->s.origin), g_entities[tr.entityNum].classname ); } else { gi.Printf (S_COLOR_RED"FinishSpawningItem: removing %s startsolid at %s (in a %s)\n", ent->classname, vtos(ent->s.origin) ); } assert( 0 && "item starting in solid"); #ifndef FINAL_BUILD if (!g_entities[ENTITYNUM_WORLD].s.radius){ //not a region delayedShutDown = level.time + 100; } #endif G_FreeEntity( ent ); return; } // allow to ride movers ent->s.groundEntityNum = tr.entityNum; G_SetOrigin( ent, tr.endpos ); } /* ? don't need this // team slaves and targeted items aren't present at start if ( ( ent->flags & FL_TEAMSLAVE ) || ent->targetname ) { ent->s.eFlags |= EF_NODRAW; ent->contents = 0; return; } */ if ( ent->spawnflags & ITMSF_INVISIBLE ) // invisible { ent->s.eFlags |= EF_NODRAW; ent->contents = 0; } if ( ent->spawnflags & ITMSF_NOTSOLID ) // not solid { ent->contents = 0; } gi.linkentity (ent); }
void Touch_Item (gentity_t *ent, gentity_t *other, trace_t *trace) { int respawn = 0; if (!other->client) return; if (other->health < 1) return; // dead people can't pickup if ( other->client->ps.pm_time > 0 ) {//cant pick up when out of control return; } // Only monsters can pick it up if ((ent->spawnflags & ITMSF_MONSTER) && (other->client->playerTeam == TEAM_PLAYER)) { return; } // Only starfleet can pick it up if ((ent->spawnflags & ITMSF_TEAM) && (other->client->playerTeam != TEAM_PLAYER)) { return; } if ( other->client->NPC_class == CLASS_ATST || other->client->NPC_class == CLASS_GONK || other->client->NPC_class == CLASS_MARK1 || other->client->NPC_class == CLASS_MARK2 || other->client->NPC_class == CLASS_MOUSE || other->client->NPC_class == CLASS_PROBE || other->client->NPC_class == CLASS_PROTOCOL || other->client->NPC_class == CLASS_R2D2 || other->client->NPC_class == CLASS_R5D2 || other->client->NPC_class == CLASS_SEEKER || other->client->NPC_class == CLASS_REMOTE || other->client->NPC_class == CLASS_SENTRY ) {//FIXME: some flag would be better //droids can't pick up items/weapons! return; } //FIXME: need to make them run toward a dropped weapon when fleeing without one? //FIXME: need to make them come out of flee mode when pick up their old weapon? if ( CheckItemCanBePickedUpByNPC( ent, other ) ) { if ( other->NPC && other->NPC->goalEntity && other->NPC->goalEntity->enemy == ent ) {//they were running to pick me up, they did, so clear goal other->NPC->goalEntity = NULL; other->NPC->squadState = SQUAD_STAND_AND_SHOOT; } } else if (!(ent->spawnflags & ITMSF_TEAM) && !(ent->spawnflags & ITMSF_MONSTER)) {// Only player can pick it up if ( other->s.number != 0 ) // Not the player? { return; } } // the same pickup rules are used for client side and server side if ( !BG_CanItemBeGrabbed( &ent->s, &other->client->ps ) ) { return; } if ( other->client ) { if ( other->client->ps.eFlags&EF_FORCE_GRIPPED ) {//can't pick up anything while being gripped return; } if ( PM_InKnockDown( &other->client->ps ) && !PM_InGetUp( &other->client->ps ) ) {//can't pick up while in a knockdown return; } } if (!ent->item) { //not an item! gi.Printf( "Touch_Item: %s is not an item!\n", ent->classname); return; } qboolean bHadWeapon = qfalse; // call the item-specific pickup function switch( ent->item->giType ) { case IT_WEAPON: if ( other->NPC && other->s.weapon == WP_NONE ) {//Make them duck and sit here for a few seconds int pickUpTime = Q_irand( 1000, 3000 ); TIMER_Set( other, "duck", pickUpTime ); TIMER_Set( other, "roamTime", pickUpTime ); TIMER_Set( other, "stick", pickUpTime ); TIMER_Set( other, "verifyCP", pickUpTime ); TIMER_Set( other, "attackDelay", 600 ); respawn = 0; } if ( other->client->ps.stats[STAT_WEAPONS] & ( 1 << ent->item->giTag ) ) { bHadWeapon = qtrue; } respawn = Pickup_Weapon(ent, other); break; case IT_AMMO: respawn = Pickup_Ammo(ent, other); break; case IT_ARMOR: respawn = Pickup_Armor(ent, other); break; case IT_HEALTH: respawn = Pickup_Health(ent, other); break; case IT_HOLDABLE: respawn = Pickup_Holdable(ent, other); break; case IT_BATTERY: respawn = Pickup_Battery( ent, other ); break; case IT_HOLOCRON: respawn = Pickup_Holocron( ent, other ); break; default: return; } if ( !respawn ) { return; } // play the normal pickup sound if ( !other->s.number && g_timescale->value < 1.0f ) {//SIGH... with timescale on, you lose events left and right extern void CG_ItemPickup( int itemNum, qboolean bHadItem ); // but we're SP so we'll cheat cgi_S_StartSound( NULL, other->s.number, CHAN_AUTO, cgi_S_RegisterSound( ent->item->pickup_sound ) ); // show icon and name on status bar CG_ItemPickup( ent->s.modelindex, bHadWeapon ); } else { if ( bHadWeapon ) { G_AddEvent( other, EV_ITEM_PICKUP, -ent->s.modelindex ); } else { G_AddEvent( other, EV_ITEM_PICKUP, ent->s.modelindex ); } } // fire item targets G_UseTargets (ent, other); // wait of -1 will not respawn // if ( ent->wait == -1 ) { //why not just remove me? G_FreeEntity( ent ); /* //NOTE: used to do this: (for respawning?) ent->svFlags |= SVF_NOCLIENT; ent->s.eFlags |= EF_NODRAW; ent->contents = 0; ent->unlinkAfterEvent = qtrue; */ return; } }
/* ================ G_RunItem ================ */ void G_RunItem( gentity_t *ent ) { vec3_t origin; trace_t tr; int contents; int mask; // if groundentity has been set to -1, it may have been pushed off an edge if ( ent->s.groundEntityNum == ENTITYNUM_NONE ) { if ( ent->s.pos.trType != TR_GRAVITY ) { ent->s.pos.trType = TR_GRAVITY; ent->s.pos.trTime = level.time; } } if ( ent->s.pos.trType == TR_STATIONARY ) { // check think function G_RunThink( ent ); if ( !g_gravity->value ) { ent->s.pos.trType = TR_GRAVITY; ent->s.pos.trTime = level.time; ent->s.pos.trDelta[0] += crandom() * 40.0f; // I dunno, just do this?? ent->s.pos.trDelta[1] += crandom() * 40.0f; ent->s.pos.trDelta[2] += random() * 20.0f; } return; } // get current position EvaluateTrajectory( &ent->s.pos, level.time, origin ); // trace a line from the previous position to the current position if ( ent->clipmask ) { mask = ent->clipmask; } else { mask = MASK_SOLID|CONTENTS_PLAYERCLIP;//shouldn't be able to get anywhere player can't } int ignore = ENTITYNUM_NONE; if ( ent->owner ) { ignore = ent->owner->s.number; } else if ( ent->activator ) { ignore = ent->activator->s.number; } gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, ignore, mask, G2_NOCOLLIDE, 0 ); VectorCopy( tr.endpos, ent->currentOrigin ); if ( tr.startsolid ) { tr.fraction = 0; } gi.linkentity( ent ); // FIXME: avoid this for stationary? // check think function G_RunThink( ent ); if ( tr.fraction == 1 ) { if ( g_gravity->value <= 0 ) { if ( ent->s.apos.trType != TR_LINEAR ) { VectorCopy( ent->currentAngles, ent->s.apos.trBase ); ent->s.apos.trType = TR_LINEAR; ent->s.apos.trDelta[1] = Q_flrand( -300, 300 ); ent->s.apos.trDelta[0] = Q_flrand( -10, 10 ); ent->s.apos.trDelta[2] = Q_flrand( -10, 10 ); ent->s.apos.trTime = level.time; } } //friction in zero-G if ( !g_gravity->value ) { float friction = 0.975f; /*friction -= ent->mass/1000.0f; if ( friction < 0.1 ) { friction = 0.1f; } */ VectorScale( ent->s.pos.trDelta, friction, ent->s.pos.trDelta ); VectorCopy( ent->currentOrigin, ent->s.pos.trBase ); ent->s.pos.trTime = level.time; } return; } // if it is in a nodrop volume, remove it contents = gi.pointcontents( ent->currentOrigin, -1 ); if ( contents & CONTENTS_NODROP ) { G_FreeEntity( ent ); return; } if ( !tr.startsolid ) { G_BounceItem( ent, &tr ); } }
/* =================== G_SpawnGEntityFromSpawnVars Spawn an entity and fill in all of the level fields from level.spawnVars[], then call the class specfic spawn function =================== */ void G_SpawnGEntityFromSpawnVars( void ) { int i; gentity_t *ent; char *s, *value, *gametypeName; static char *gametypeNames[] = {"ffa", "tournament", "single", "team", "ctf", "oneflag", "obelisk", "harvester"}; int spawnVarsOffset[MAX_SPAWN_VARS][2]; // key / value pairs offsets // Convert to offsets. for (i = 0; i < level.numSpawnVars; ++i) { spawnVarsOffset[i][0] = level.spawnVars[i][0] - level.spawnVarChars; spawnVarsOffset[i][1] = level.spawnVars[i][1] - level.spawnVarChars; } // Early out if spawn is not required. if (!dmlab_update_spawn_vars( level.spawnVarChars, &level.numSpawnVarChars, spawnVarsOffset, &level.numSpawnVars)) { return; } // Convert from offsets. for (i = 0; i < level.numSpawnVars; ++i) { level.spawnVars[i][0] = level.spawnVarChars + spawnVarsOffset[i][0]; level.spawnVars[i][1] = level.spawnVarChars + spawnVarsOffset[i][1]; } // get the next free entity ent = G_Spawn(); for ( i = 0 ; i < level.numSpawnVars ; i++ ) { G_ParseField( level.spawnVars[i][0], level.spawnVars[i][1], ent ); } // check for "notsingle" flag if ( g_gametype.integer == GT_SINGLE_PLAYER ) { G_SpawnInt( "notsingle", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } // check for "notteam" flag (GT_FFA, GT_TOURNAMENT, GT_SINGLE_PLAYER) if ( g_gametype.integer >= GT_TEAM ) { G_SpawnInt( "notteam", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } else { G_SpawnInt( "notfree", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } #ifdef MISSIONPACK G_SpawnInt( "notta", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } #else G_SpawnInt( "notq3a", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } #endif if( G_SpawnString( "gametype", NULL, &value ) ) { if( g_gametype.integer >= GT_FFA && g_gametype.integer < GT_MAX_GAME_TYPE ) { gametypeName = gametypeNames[g_gametype.integer]; s = strstr( value, gametypeName ); if( !s ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } } // move editor origin to pos VectorCopy( ent->s.origin, ent->s.pos.trBase ); VectorCopy( ent->s.origin, ent->r.currentOrigin ); // if we didn't get a classname, don't bother spawning anything if ( !G_CallSpawn( ent ) ) { G_FreeEntity( ent ); } }
void G_delayPrint( gentity_t *dpent ) { int think_next = 0; bool fFree = true; switch ( dpent->spawnflags ) { case DP_PAUSEINFO: { if ( level.match_pause > PAUSE_UNPAUSING ) { int cSeconds = match_timeoutlength.integer * 1000 - ( level.time - dpent->timestamp ); if ( cSeconds > 1000 ) { AP( va( "cp \"^3Match resuming in ^1%d^3 seconds!\n\"", cSeconds / 1000 ) ); think_next = level.time + 15000; fFree = false; } else { level.match_pause = PAUSE_UNPAUSING; AP( "print \"^3Match resuming in 10 seconds!\n\"" ); G_globalSound( "sound/osp/prepare.wav" ); G_spawnPrintf( DP_UNPAUSING, level.time + 10, NULL ); } } break; } case DP_UNPAUSING: { if ( level.match_pause == PAUSE_UNPAUSING ) { int cSeconds = 11 * 1000 - ( level.time - dpent->timestamp ); if ( cSeconds > 1000 ) { AP( va( "cp \"^3Match resuming in ^1%d^3 seconds!\n\"", cSeconds / 1000 ) ); think_next = level.time + 1000; fFree = false; } else { level.match_pause = PAUSE_NONE; G_globalSound( "sound/osp/fight.wav" ); G_printFull( "^1FIGHT!", NULL ); trap_SetConfigstring( CS_LEVEL_START_TIME, va( "%i", level.startTime + level.timeDelta ) ); level.server_settings &= ~CV_SVS_PAUSE; trap_SetConfigstring( CS_SERVERTOGGLES, va( "%d", level.server_settings ) ); } } break; } case DP_MVSPAWN: { int i; gentity_t *ent; for ( i = 0; i < level.numConnectedClients; i++ ) { ent = g_entities + level.sortedClients[i]; if ( ent->client->pers.mvReferenceList == 0 ) { continue; } if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { continue; } G_smvRegenerateClients( ent, ent->client->pers.mvReferenceList ); } break; } default: break; } dpent->nextthink = think_next; if ( fFree ) { dpent->think = 0; G_FreeEntity( dpent ); } }
/* ================ G_RunFrame Advances the non-player objects in the world ================ */ void G_RunFrame( int levelTime ) { int i; gentity_t *ent; int msec; int start, end; // if we are waiting for the level to restart, do nothing if ( level.restarted ) { return; } level.framenum++; level.previousTime = level.time; level.time = levelTime; msec = level.time - level.previousTime; // get any cvar changes G_UpdateCvars(); // // go through all allocated objects // start = trap_Milliseconds(); ent = &g_entities[0]; for (i=0 ; i<level.num_entities ; i++, ent++) { if ( !ent->inuse ) { continue; } // clear events that are too old if ( level.time - ent->eventTime > EVENT_VALID_MSEC ) { if ( ent->s.event ) { ent->s.event = 0; // &= EV_EVENT_BITS; if ( ent->client ) { ent->client->ps.externalEvent = 0; // predicted events should never be set to zero //ent->client->ps.events[0] = 0; //ent->client->ps.events[1] = 0; } } if ( ent->freeAfterEvent ) { // tempEntities or dropped items completely go away after their event G_FreeEntity( ent ); continue; } else if ( ent->unlinkAfterEvent ) { // items that will respawn will hide themselves after their pickup event ent->unlinkAfterEvent = qfalse; trap_UnlinkEntity( ent ); } } // temporary entities don't think if ( ent->freeAfterEvent ) { continue; } if ( !ent->r.linked && ent->neverFree ) { continue; } if ( ent->s.eType == ET_MISSILE ) { G_RunMissile( ent ); continue; } if ( ent->s.eType == ET_ITEM || ent->physicsObject ) { G_RunItem( ent ); continue; } if ( ent->s.eType == ET_MOVER ) { G_RunMover( ent ); continue; } if ( i < MAX_CLIENTS ) { G_RunClient( ent ); continue; } G_RunThink( ent ); } end = trap_Milliseconds(); start = trap_Milliseconds(); // perform final fixups on the players ent = &g_entities[0]; for (i=0 ; i < level.maxclients ; i++, ent++ ) { if ( ent->inuse ) { ClientEndFrame( ent ); } } end = trap_Milliseconds(); // see if it is time to do a tournement restart CheckTournament(); // see if it is time to end the level CheckExitRules(); // update to team status? CheckTeamStatus(); // cancel vote if timed out CheckVote(); // check team votes CheckTeamVote( TEAM_RED ); CheckTeamVote( TEAM_BLUE ); // for tracking changes CheckCvars(); if (g_listEntity.integer) { for (i = 0; i < MAX_GENTITIES; i++) { G_Printf("%4i: %s\n", i, g_entities[i].classname); } trap_Cvar_Set("g_listEntity", "0"); } }
/*QUAKED bot_jump_dest (0.5 0.8 0) (-18 -18 -24) (18 18 40) NODROP Bots will use this spot to jump to Must recompile .AAS file after adding/changing these entities NOTE: MUST HAVE A MATCHING BOT_JUMP_SOURCE NODROP means dont drop it to the ground */ void SP_bot_jump_dest(gentity_t *ent) { // only used for bspc process G_FreeEntity(ent); }
/** * @brief SP_target_location * @details QUAKED target_location (0 0.5 0) (-8 -8 -8) (8 8 8) * Set "message" to the name of this location. * Set "count" to 0-7 for color. * 0:white 1:red 2:green 3:yellow 4:blue 5:cyan 6:magenta 7:white * * Closest target_location in sight used for the location, if none * in site, closest in distance * @param self */ void SP_target_location(gentity_t *self) { G_Printf(S_COLOR_YELLOW "WARNING: target_location entities are now obsolete. Please remove ASAP\n"); G_FreeEntity(self); }
static void PortalDie(gentity_t * self, gentity_t * inflictor, gentity_t * attacker, int damage, int mod) { G_FreeEntity(self); //FIXME do something more interesting }
/*QUAKED target_autosave (1 1 0) (-8 -8 -8) (8 8 8) saves game to 'autosave.svg' when triggered then dies. */ void SP_target_autosave( gentity_t *ent ) { G_FreeEntity(ent); }
/*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4) Used as a positional target for calculations in the utilities (spotlights, etc), but removed during gameplay. */ void SP_info_null(gentity_t * self) { G_FreeEntity(self); }
/* ================ FinishSpawningItem Traces down to find where an item should rest, instead of letting them free fall from their spawn points ================ */ void FinishSpawningItem( gentity_t *ent ) { trace_t tr; vec3_t dest; VectorSet( ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS ); VectorSet( ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS ); ent->s.eType = ET_ITEM; ent->s.modelindex = ent->item - bg_itemlist; // store item number in modelindex ent->s.modelindex2 = 0; // zero indicates this isn't a dropped item ent->r.contents = CONTENTS_TRIGGER; ent->touch = Touch_Item; // useing an item causes it to respawn ent->use = Use_Item; if ( ent->spawnflags & 1 ) { // suspended G_SetOrigin( ent, ent->s.origin ); } else { // drop to floor VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 ); trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID ); if ( tr.startsolid ) { G_Printf ("FinishSpawningItem: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin)); G_FreeEntity( ent ); return; } // allow to ride movers ent->s.groundEntityNum = tr.entityNum; G_SetOrigin( ent, tr.endpos ); } // team slaves and targeted items aren't present at start if ( ( ent->flags & FL_TEAMSLAVE ) || ent->targetname ) { ent->s.eFlags |= EF_NODRAW; ent->r.contents = 0; return; } // powerups don't spawn in for a while if ( ent->item->giType == IT_POWERUP ) { float respawn; respawn = 45 + crandom() * 15; ent->s.eFlags |= EF_NODRAW; ent->r.contents = 0; ent->nextthink = level.time + respawn * 1000; ent->think = RespawnItem; return; } if ( ent->item->giType == IT_HOLDABLE ) { if ( ( ent->item->giTag == HI_BAMBAM ) && ( g_gametype.integer != GT_CTF ) ) { return; } else if ( ( ent->item->giTag == HI_BOOMIES ) && ( ( g_gametype.integer != GT_CTF ) && ( g_gametype.integer != GT_BALLOON ) ) ) { return; } } trap_LinkEntity (ent); }
/*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) linear Non-displayed light. "light" overrides the default 300 intensity. Linear checbox gives linear falloff instead of inverse square Lights pointed at a target will be spotlights. "radius" overrides the default 64 unit radius of a spotlight at the target point. */ void SP_light(gentity_t * self) { G_FreeEntity(self); }
//------------------ void Cmd_Fx( gentity_t *ent ) { vec3_t dir; gentity_t *fx_ent = NULL; if ( Q_stricmp( gi.argv(1), "play" ) == 0 ) { if ( gi.argc() == 3 ) { // I guess, only allow one active at a time while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL ) { G_FreeEntity( fx_ent ); } fx_ent = G_Spawn(); fx_ent->fxFile = gi.argv( 2 ); // Move out in front of the person spawning the effect AngleVectors( ent->currentAngles, dir, NULL, NULL ); VectorMA( ent->currentOrigin, 32, dir, fx_ent->s.origin ); extern void SP_fx_runner( gentity_t *ent ); SP_fx_runner( fx_ent ); fx_ent->delay = 2000; // adjusting delay fx_ent->classname = "cmd_fx"; // and classname return; } } else if ( Q_stricmp( gi.argv(1), "stop" ) == 0 ) { while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL ) { G_FreeEntity( fx_ent ); } return; } else if ( Q_stricmp( gi.argv(1), "delay" ) == 0 ) { while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL ) { if ( gi.argc() == 3 ) { fx_ent->delay = atoi( gi.argv( 2 )); } else { gi.Printf( S_COLOR_GREEN"FX: current delay is: %i\n", fx_ent->delay ); } return; } } else if ( Q_stricmp( gi.argv(1), "random" ) == 0 ) { while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL ) { if ( gi.argc() == 3 ) { fx_ent->random = atoi( gi.argv( 2 )); } else { gi.Printf( S_COLOR_GREEN"FX: current random is: %6.2f\n", fx_ent->random ); } return; } } else if ( Q_stricmp( gi.argv(1), "origin" ) == 0 ) { while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL ) { if ( gi.argc() == 5 ) { fx_ent->s.origin[0] = atof( gi.argv( 2 )); fx_ent->s.origin[1] = atof( gi.argv( 3 )); fx_ent->s.origin[2] = atof( gi.argv( 4 )); G_SetOrigin( fx_ent, fx_ent->s.origin ); } else { gi.Printf( S_COLOR_GREEN"FX: current origin is: <%6.2f %6.2f %6.2f>\n", fx_ent->currentOrigin[0], fx_ent->currentOrigin[1], fx_ent->currentOrigin[2] ); } return; } } else if ( Q_stricmp( gi.argv(1), "dir" ) == 0 ) { while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL ) { if ( gi.argc() == 5 ) { fx_ent->s.angles[0] = atof( gi.argv( 2 )); fx_ent->s.angles[1] = atof( gi.argv( 3 )); fx_ent->s.angles[2] = atof( gi.argv( 4 )); if ( !VectorNormalize( fx_ent->s.angles )) { // must have been zero length fx_ent->s.angles[2] = 1; } } else { gi.Printf( S_COLOR_GREEN"FX: current dir is: <%6.2f %6.2f %6.2f>\n", fx_ent->s.angles[0], fx_ent->s.angles[1], fx_ent->s.angles[2] ); } return; } } gi.Printf( S_COLOR_CYAN"Fx--------------------------------------------------------\n" ); gi.Printf( S_COLOR_CYAN"commands: sample usage:\n" ); gi.Printf( S_COLOR_CYAN"----------------------------------------------------------\n" ); gi.Printf( S_COLOR_CYAN"fx play <filename> fx play sparks, fx play env/fire\n" ); gi.Printf( S_COLOR_CYAN"fx stop fx stop\n" ); gi.Printf( S_COLOR_CYAN"fx delay <#> fx delay 1000\n" ); gi.Printf( S_COLOR_CYAN"fx random <#> fx random 200\n" ); gi.Printf( S_COLOR_CYAN"fx origin <#><#><#> fx origin 10 20 30\n" ); gi.Printf( S_COLOR_CYAN"fx dir <#><#><#> fx dir 0 0 -1\n\n" ); }
/* ================= AimAtTarget Calculate origin2 so the target apogee will be hit ================= */ void AimAtTarget( gentity_t *self ) { gentity_t *ent; vec3_t origin; float height, gravity, time, forward; float dist; VectorAdd( self->absmin, self->absmax, origin ); VectorScale ( origin, 0.5, origin ); ent = G_PickTarget( self->target ); if ( !ent ) { G_FreeEntity( self ); return; } if ( self->classname && !Q_stricmp( "trigger_push", self->classname ) ) { if ( (self->spawnflags&2) ) {//check once a second to see if we should activate or deactivate ourselves self->e_ThinkFunc = thinkF_trigger_push_checkclear; self->nextthink = level.time + FRAMETIME; } if ( (self->spawnflags&16) ) {//relative, not an arc or linear VectorCopy( ent->currentOrigin, self->s.origin2 ); return; } else if ( (self->spawnflags&4) ) {//linear, not an arc VectorSubtract( ent->currentOrigin, origin, self->s.origin2 ); VectorNormalize( self->s.origin2 ); return; } } if ( self->classname && !Q_stricmp( "target_push", self->classname ) ) { if( self->spawnflags & PUSH_CONSTANT ) { VectorSubtract ( ent->s.origin, self->s.origin, self->s.origin2 ); VectorNormalize( self->s.origin2); VectorScale (self->s.origin2, self->speed, self->s.origin2); return; } } height = ent->s.origin[2] - origin[2]; if ( height < 0 ) {//sqrt of negative is bad! height = 0; } gravity = g_gravity->value; if ( gravity < 0 ) { gravity = 0; } time = sqrt( height / ( .5 * gravity ) ); if ( !time ) { G_FreeEntity( self ); return; } // set s.origin2 to the push velocity VectorSubtract ( ent->s.origin, origin, self->s.origin2 ); self->s.origin2[2] = 0; dist = VectorNormalize( self->s.origin2); forward = dist / time; VectorScale( self->s.origin2, forward, self->s.origin2 ); self->s.origin2[2] = time * gravity; }
/* =================== G_SpawnGEntityFromSpawnVars Spawn an entity and fill in all of the level fields from level.spawnVars[], then call the class specfic spawn function =================== */ void G_SpawnGEntityFromSpawnVars( void ) { int i; gentity_t *ent; char *s, *value, *gametypeName; static char *gametypeNames[] = {"ffa", "tournament", "single", "team", "ctf", "oneflag", "obelisk", "harvester"}; // get the next free entity ent = G_Spawn(); for ( i = 0 ; i < level.numSpawnVars ; i++ ) { G_ParseField( level.spawnVars[i][0], level.spawnVars[i][1], ent ); } // check for "notsingle" flag if ( g_gametype.integer == GT_SINGLE_PLAYER ) { G_SpawnInt( "notsingle", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } // check for "notteam" flag (GT_FFA, GT_TOURNAMENT, GT_SINGLE_PLAYER) if ( g_gametype.integer >= GT_TEAM ) { G_SpawnInt( "notteam", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } else { G_SpawnInt( "notfree", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } #ifdef MISSIONPACK G_SpawnInt( "notta", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } #else G_SpawnInt( "notq3a", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } #endif if( G_SpawnString( "gametype", NULL, &value ) ) { if( g_gametype.integer >= GT_FFA && g_gametype.integer < GT_MAX_GAME_TYPE ) { gametypeName = gametypeNames[g_gametype.integer]; s = strstr( value, gametypeName ); if( !s ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } } // move editor origin to pos VectorCopy( ent->s.origin, ent->s.pos.trBase ); VectorCopy( ent->s.origin, ent->r.currentOrigin ); // if we didn't get a classname, don't bother spawning anything if ( !G_CallSpawn( ent ) ) { G_FreeEntity( ent ); } }
/* ================ G_SetEntState sets the entstate of an entity. ================ */ void G_SetEntState(gentity_t *ent, entState_t state) { if (ent->entstate == state) { G_DPrintf("G_SetEntState: entity %i already in desired state [%i]\n", ent->s.number, state); return; } switch (state) { case STATE_DEFAULT: if (ent->entstate == STATE_UNDERCONSTRUCTION) { ent->clipmask = ent->realClipmask; ent->r.contents = ent->realContents; if (!ent->realNonSolidBModel) { ent->s.eFlags &= ~EF_NONSOLID_BMODEL; } } ent->entstate = STATE_DEFAULT; ent->s.powerups = STATE_DEFAULT; if (ent->s.eType == ET_WOLF_OBJECTIVE) { char cs[MAX_STRING_CHARS]; trap_GetConfigstring(ent->count, cs, sizeof(cs)); ent->count2 &= ~256; Info_SetValueForKey(cs, "t", va("%i", ent->count2)); trap_SetConfigstring(ent->count, cs); } if (ent->s.eType != ET_COMMANDMAP_MARKER) { trap_LinkEntity(ent); } // deal with any entities in the solid { int listedEntities, e; int entityList[MAX_GENTITIES]; gentity_t *check, *block; listedEntities = trap_EntitiesInBox(ent->r.absmin, ent->r.absmax, entityList, MAX_GENTITIES); for (e = 0; e < listedEntities; e++) { check = &g_entities[entityList[e]]; // ignore everything but items, players and missiles (grenades too) if (check->s.eType != ET_MISSILE && check->s.eType != ET_ITEM && check->s.eType != ET_PLAYER && !check->physicsObject) { continue; } if ((block = G_TestEntityPosition(check)) == NULL) { continue; } if (block != ent) { // the entity is blocked by another entity - that block this should take care of this itself continue; } if (check->client || check->s.eType == ET_CORPSE) { // gibs anything player like G_Damage(check, ent, ent, NULL, NULL, 9999, DAMAGE_NO_PROTECTION, MOD_CRUSH_CONSTRUCTIONDEATH_NOATTACKER); } else if (check->s.eType == ET_ITEM && check->item->giType == IT_TEAM) { // see if it's a critical entity, one that we can't just simply kill (basically flags) Team_DroppedFlagThink(check); } else { // remove the landmine from both teamlists if (check->s.eType == ET_MISSILE && check->methodOfDeath == MOD_LANDMINE) { mapEntityData_t *mEnt; if ((mEnt = G_FindMapEntityData(&mapEntityData[0], check - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[0], mEnt); } if ((mEnt = G_FindMapEntityData(&mapEntityData[1], check - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[1], mEnt); } } // just get rid of it G_FreeEntity(check); } } } break; case STATE_UNDERCONSTRUCTION: ent->entstate = STATE_UNDERCONSTRUCTION; ent->s.powerups = STATE_UNDERCONSTRUCTION; ent->realClipmask = ent->clipmask; if (ent->s.eType != ET_CONSTRUCTIBLE) // don't make nonsolid as we want to make them partially solid for staged construction { ent->clipmask = 0; } ent->realContents = ent->r.contents; if (ent->s.eType != ET_CONSTRUCTIBLE) { ent->r.contents = 0; } if (ent->s.eFlags & EF_NONSOLID_BMODEL) { ent->realNonSolidBModel = qtrue; } else if (ent->s.eType != ET_CONSTRUCTIBLE) { ent->s.eFlags |= EF_NONSOLID_BMODEL; } if (!Q_stricmp(ent->classname, "misc_mg42")) { // stop using the mg42 mg42_stopusing(ent); } if (ent->s.eType == ET_COMMANDMAP_MARKER) { mapEntityData_t *mEnt; if ((mEnt = G_FindMapEntityData(&mapEntityData[0], ent - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[0], mEnt); } if ((mEnt = G_FindMapEntityData(&mapEntityData[1], ent - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[1], mEnt); } } trap_LinkEntity(ent); break; case STATE_INVISIBLE: if (ent->entstate == STATE_UNDERCONSTRUCTION) { ent->clipmask = ent->realClipmask; ent->r.contents = ent->realContents; if (!ent->realNonSolidBModel) { ent->s.eFlags &= ~EF_NONSOLID_BMODEL; } } ent->entstate = STATE_INVISIBLE; ent->s.powerups = STATE_INVISIBLE; if (!Q_stricmp(ent->classname, "misc_mg42")) { mg42_stopusing(ent); } else if (ent->s.eType == ET_WOLF_OBJECTIVE) { char cs[MAX_STRING_CHARS]; trap_GetConfigstring(ent->count, cs, sizeof(cs)); ent->count2 |= 256; Info_SetValueForKey(cs, "t", va("%i", ent->count2)); trap_SetConfigstring(ent->count, cs); } if (ent->s.eType == ET_COMMANDMAP_MARKER) { mapEntityData_t *mEnt; if ((mEnt = G_FindMapEntityData(&mapEntityData[0], ent - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[0], mEnt); } if ((mEnt = G_FindMapEntityData(&mapEntityData[1], ent - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[1], mEnt); } } trap_UnlinkEntity(ent); break; } }
/** * @brief Traces down to find where an item should rest, instead of letting them free fall from their spawn points. */ void FinishSpawningItem(gentity_t *ent) { trace_t tr; vec3_t dest; vec3_t maxs; if (ent->spawnflags & 1) // suspended { VectorSet(ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS); VectorSet(ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS); VectorCopy(ent->r.maxs, maxs); } else { // had to modify this so that items would spawn in shelves VectorSet(ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, 0); VectorSet(ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS); VectorCopy(ent->r.maxs, maxs); maxs[2] /= 2; } ent->r.contents = CONTENTS_TRIGGER | CONTENTS_ITEM; ent->touch = Touch_Item_Auto; ent->s.eType = ET_ITEM; ent->s.modelindex = ent->item - bg_itemlist; // store item number in modelindex ent->s.otherEntityNum2 = 0; // takes modelindex2's place in signaling a dropped item // we don't use this (yet, anyway) so I'm taking it so you can specify a model for treasure items and clipboards //ent->s.modelindex2 = 0; // zero indicates this isn't a dropped item if (ent->model) { ent->s.modelindex2 = G_ModelIndex(ent->model); } // using an item causes it to respawn ent->use = Use_Item; // moved this up so it happens for suspended items too (and made it a function) G_SetAngle(ent, ent->s.angles); if (ent->spawnflags & 1) // suspended { G_SetOrigin(ent, ent->s.origin); } else { VectorSet(dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096); trap_Trace(&tr, ent->s.origin, ent->r.mins, maxs, dest, ent->s.number, MASK_SOLID); if (tr.startsolid) { vec3_t temp; VectorCopy(ent->s.origin, temp); temp[2] -= ITEM_RADIUS; VectorSet(dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096); trap_Trace(&tr, temp, ent->r.mins, maxs, dest, ent->s.number, MASK_SOLID); } if (tr.startsolid) { G_Printf("FinishSpawningItem: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin)); G_FreeEntity(ent); return; } // allow to ride movers ent->s.groundEntityNum = tr.entityNum; G_SetOrigin(ent, tr.endpos); } if (ent->spawnflags & 2) // spin { ent->s.eFlags |= EF_SPINNING; } // team slaves and targeted items aren't present at start if ((ent->flags & FL_TEAMSLAVE) || ent->targetname) { ent->flags |= FL_NODRAW; //ent->s.eFlags |= EF_NODRAW; ent->r.contents = 0; return; } // health/ammo can potentially be multi-stage (multiple use) if (ent->item->giType == IT_HEALTH || ent->item->giType == IT_AMMO) { int i; // having alternate models defined in bg_misc.c for a health or ammo item specify it as "multi-stage" // - left-hand operand of comma expression has no effect // initial line: for(i=0;i<4,ent->item->world_model[i];i++) {} for (i = 0; i < 4 && ent->item->world_model[i] ; i++) { } ent->s.density = i - 1; // store number of stages in 'density' for client (most will have '1') } trap_LinkEntity(ent); }
/* ================ G_RunMissile ================ */ void G_RunMissile( gentity_t *ent ) { vec3_t origin, groundSpot; trace_t tr; int passent; qboolean isKnockedSaber = qfalse; if (ent->neverFree && ent->s.weapon == WP_SABER && (ent->flags & FL_BOUNCE_HALF)) { isKnockedSaber = qtrue; ent->s.pos.trType = TR_GRAVITY; } // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); // if this missile bounced off an invulnerability sphere if ( ent->target_ent ) { passent = ent->target_ent->s.number; } else { // ignore interactions with the missile owner if ( (ent->r.svFlags&SVF_OWNERNOTSHARED) && (ent->s.eFlags&EF_JETPACK_ACTIVE) ) {//A vehicle missile that should be solid to its owner //I don't care about hitting my owner passent = ent->s.number; } else { passent = ent->r.ownerNum; } } // trace a line from the previous position to the current position if (d_projectileGhoul2Collision.integer) { trap_G2Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask, G2TRFLAG_DOGHOULTRACE|G2TRFLAG_GETSURFINDEX|G2TRFLAG_THICK|G2TRFLAG_HITCORPSES, g_g2TraceLod.integer ); if (tr.fraction != 1.0 && tr.entityNum < ENTITYNUM_WORLD) { gentity_t *g2Hit = &g_entities[tr.entityNum]; if (g2Hit->inuse && g2Hit->client && g2Hit->ghoul2) { //since we used G2TRFLAG_GETSURFINDEX, tr.surfaceFlags will actually contain the index of the surface on the ghoul2 model we collided with. g2Hit->client->g2LastSurfaceHit = tr.surfaceFlags; g2Hit->client->g2LastSurfaceTime = level.time; } if (g2Hit->ghoul2) { tr.surfaceFlags = 0; //clear the surface flags after, since we actually care about them in here. } } } else { trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask ); } if ( tr.startsolid || tr.allsolid ) { // make sure the tr.entityNum is set to the entity we're stuck in trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask ); tr.fraction = 0; } else { VectorCopy( tr.endpos, ent->r.currentOrigin ); } if (ent->passThroughNum && tr.entityNum == (ent->passThroughNum-1)) { VectorCopy( origin, ent->r.currentOrigin ); trap_LinkEntity( ent ); goto passthrough; } trap_LinkEntity( ent ); if (ent->s.weapon == G2_MODEL_PART && !ent->bounceCount) { vec3_t lowerOrg; trace_t trG; VectorCopy(ent->r.currentOrigin, lowerOrg); lowerOrg[2] -= 1; trap_Trace( &trG, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, lowerOrg, passent, ent->clipmask ); VectorCopy(trG.endpos, groundSpot); if (!trG.startsolid && !trG.allsolid && trG.entityNum == ENTITYNUM_WORLD) { ent->s.groundEntityNum = trG.entityNum; } else { ent->s.groundEntityNum = ENTITYNUM_NONE; } } if ( tr.fraction != 1) { // never explode or bounce on sky if ( tr.surfaceFlags & SURF_NOIMPACT ) { // If grapple, reset owner if (ent->parent && ent->parent->client && ent->parent->client->hook == ent) { ent->parent->client->hook = NULL; } if ((ent->s.weapon == WP_SABER && ent->isSaberEntity) || isKnockedSaber) { G_RunThink( ent ); return; } else if (ent->s.weapon != G2_MODEL_PART) { G_FreeEntity( ent ); return; } } #if 0 //will get stomped with missile impact event... if (ent->s.weapon > WP_NONE && ent->s.weapon < WP_NUM_WEAPONS && (tr.entityNum < MAX_CLIENTS || g_entities[tr.entityNum].s.eType == ET_NPC)) { //player or NPC, try making a mark on him /* gentity_t *evEnt = G_TempEntity(ent->r.currentOrigin, EV_GHOUL2_MARK); evEnt->s.owner = tr.entityNum; //the entity the mark should be placed on evEnt->s.weapon = ent->s.weapon; //the weapon used (to determine mark type) VectorCopy(ent->r.currentOrigin, evEnt->s.origin); //the point of impact //origin2 gets the predicted trajectory-based position. BG_EvaluateTrajectory( &ent->s.pos, level.time, evEnt->s.origin2 ); //If they are the same, there will be problems. if (VectorCompare(evEnt->s.origin, evEnt->s.origin2)) { evEnt->s.origin2[2] += 2; //whatever, at least it won't mess up. } */ //ok, let's try adding it to the missile ent instead (tempents bad!) G_AddEvent(ent, EV_GHOUL2_MARK, 0); //copy current pos to s.origin, and current projected traj to origin2 VectorCopy(ent->r.currentOrigin, ent->s.origin); BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->s.origin2 ); //the index for whoever we are hitting ent->s.otherEntityNum = tr.entityNum; if (VectorCompare(ent->s.origin, ent->s.origin2)) { ent->s.origin2[2] += 2.0f; //whatever, at least it won't mess up. } } #else if (ent->s.weapon > WP_NONE && ent->s.weapon < WP_NUM_WEAPONS && (tr.entityNum < MAX_CLIENTS || g_entities[tr.entityNum].s.eType == ET_NPC)) { //player or NPC, try making a mark on him //copy current pos to s.origin, and current projected traj to origin2 VectorCopy(ent->r.currentOrigin, ent->s.origin); BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->s.origin2 ); if (VectorCompare(ent->s.origin, ent->s.origin2)) { ent->s.origin2[2] += 2.0f; //whatever, at least it won't mess up. } } #endif G_MissileImpact( ent, &tr ); if (tr.entityNum == ent->s.otherEntityNum) { //if the impact event other and the trace ent match then it's ok to do the g2 mark ent->s.trickedentindex = 1; } if ( ent->s.eType != ET_MISSILE && ent->s.weapon != G2_MODEL_PART ) { return; // exploded } } passthrough: if ( ent->s.pos.trType == TR_STATIONARY && (ent->s.eFlags&EF_MISSILE_STICK) ) {//stuck missiles should check some special stuff G_RunStuckMissile( ent ); return; } if (ent->s.weapon == G2_MODEL_PART) { if (ent->s.groundEntityNum == ENTITYNUM_WORLD) { ent->s.pos.trType = TR_LINEAR; VectorClear(ent->s.pos.trDelta); ent->s.pos.trTime = level.time; VectorCopy(groundSpot, ent->s.pos.trBase); VectorCopy(groundSpot, ent->r.currentOrigin); if (ent->s.apos.trType != TR_STATIONARY) { ent->s.apos.trType = TR_STATIONARY; ent->s.apos.trTime = level.time; ent->s.apos.trBase[ROLL] = 0; ent->s.apos.trBase[PITCH] = 0; } } } // check think function after bouncing G_RunThink( ent ); }