qboolean G_CanEntityBeActivated(gentity_t *player, gentity_t *target, qboolean exact){ trace_t tr; vec3_t muzzle, forward, end; if(!exact && Distance(player->client->ps.origin, target->r.currentOrigin) > ACTIVATE_RANGE) return qfalse; VectorCopy( player->client->ps.origin, muzzle ); muzzle[2] += player->client->ps.viewheight; // modify the muzzle point G_ModifyEyeAngles(muzzle, player->client->ps.viewangles, qfalse); //calculate muzzle and end point AngleVectors(player->client->ps.viewangles, forward, NULL, NULL); VectorMA (muzzle, 8192, forward, end); //find the nearest obstacle in the line trap_Trace_New (&tr, muzzle, NULL, NULL, end, player->client->ps.clientNum, MASK_SHOT); if(tr.entityNum != target->s.number){ return qfalse; } if(exact && Distance(player->client->ps.origin, tr.endpos) > ACTIVATE_RANGE) return qfalse; return qtrue; }
/*QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON When triggered, fires a laser. You can either set a target or a direction. */ void target_laser_think (gentity_t *self) { vec3_t end; trace_t tr; vec3_t point; // if pointed at another entity, set movedir to point at it if ( self->enemy ) { VectorMA (self->enemy->s.origin, 0.5, self->enemy->r.mins, point); VectorMA (point, 0.5, self->enemy->r.maxs, point); VectorSubtract (point, self->s.origin, self->movedir); VectorNormalize (self->movedir); } // fire forward and see what we hit VectorMA (self->s.origin, 2048, self->movedir, end); trap_Trace_New( &tr, self->s.origin, NULL, NULL, end, self->s.number, CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_CORPSE); if ( tr.entityNum ) { // hurt it if we can G_Damage(&g_entities[tr.entityNum], self, self->activator, self->movedir, tr.endpos, self->damage, 0, MOD_TARGET_LASER); } VectorCopy (tr.endpos, self->s.origin2); trap_LinkEntity( self ); self->nextthink = level.time + FRAMETIME; }
void G_MissileImpact( gentity_t *ent, trace_t *trace ) { #else void G_MissileImpact( gentity_t *ent, trace_t *trace, int shaderNum ) { #endif gentity_t *other; qboolean hitClient = qfalse; #ifndef SMOKINGUNS #ifdef MISSIONPACK vec3_t forward, impactpoint, bouncedir; int eFlags; #endif #else qboolean hitKnife = qfalse; vec3_t bottledirs[ALC_COUNT]; #endif other = &g_entities[trace->entityNum]; #ifndef SMOKINGUNS // check for bounce if ( !other->takedamage && ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } #ifdef MISSIONPACK if ( other->takedamage ) { if ( ent->s.weapon != WP_PROX_LAUNCHER ) { if ( other->client && other->client->invulnerabilityTime > level.time ) { // VectorCopy( ent->s.pos.trDelta, forward ); VectorNormalize( forward ); if (G_InvulnerabilityEffect( other, forward, ent->s.pos.trBase, impactpoint, bouncedir )) { VectorCopy( bouncedir, trace->plane.normal ); eFlags = ent->s.eFlags & EF_BOUNCE_HALF; ent->s.eFlags &= ~EF_BOUNCE_HALF; G_BounceMissile( ent, trace ); ent->s.eFlags |= eFlags; } ent->target_ent = other; return; } } } #endif // impact damage if (other->takedamage) { #else if(other->takedamage) hitKnife = qtrue; // check for bounce if ( ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); return; } if (other->takedamage && ent->s.weapon != WP_DYNAMITE) { #endif // FIXME: wrong damage direction? if ( ent->damage ) { vec3_t velocity; if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; hitClient = qtrue; } BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if ( VectorLength( velocity ) == 0 ) { velocity[2] = 1; // stepped on a grenade } #ifndef SMOKINGUNS G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath); #else // you can't make dynamite exploding by using a knife if(!(ent->s.weapon == WP_KNIFE && other->s.weapon == WP_DYNAMITE && other->s.eType == ET_ITEM)){ // prepare breakable, if not already initialized if(!(other->flags & FL_BREAKABLE_INIT)) G_BreakablePrepare(other, shaderNum); G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath); } #endif } } #ifndef SMOKINGUNS if( ent->s.weapon == WP_PROX_LAUNCHER ) { if( ent->s.pos.trType != TR_GRAVITY ) { return; } // if it's a player, stick it on to them (flag them and remove this entity) if( other->s.eType == ET_PLAYER && other->health > 0 ) { ProximityMine_Player( ent, other ); return; } SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); G_SetOrigin( ent, trace->endpos ); ent->s.pos.trType = TR_STATIONARY; VectorClear( ent->s.pos.trDelta ); G_AddEvent( ent, EV_PROXIMITY_MINE_STICK, trace->surfaceFlags ); ent->think = ProximityMine_Activate; ent->nextthink = level.time + 2000; vectoangles( trace->plane.normal, ent->s.angles ); ent->s.angles[0] += 90; // link the prox mine to the other entity ent->enemy = other; ent->die = ProximityMine_Die; VectorCopy(trace->plane.normal, ent->movedir); VectorSet(ent->r.mins, -4, -4, -4); VectorSet(ent->r.maxs, 4, 4, 4); trap_LinkEntity(ent); return; } if (!strcmp(ent->classname, "hook")) { gentity_t *nent; vec3_t v; nent = G_Spawn(); if ( other->takedamage && other->client ) { G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); nent->s.otherEntityNum = other->s.number; ent->enemy = other; v[0] = other->r.currentOrigin[0] + (other->r.mins[0] + other->r.maxs[0]) * 0.5; v[1] = other->r.currentOrigin[1] + (other->r.mins[1] + other->r.maxs[1]) * 0.5; v[2] = other->r.currentOrigin[2] + (other->r.mins[2] + other->r.maxs[2]) * 0.5; SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth } else { VectorCopy(trace->endpos, v); G_AddEvent( nent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); ent->enemy = NULL; } SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth nent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact nent->s.eType = ET_GENERAL; ent->s.eType = ET_GRAPPLE; G_SetOrigin( ent, v ); G_SetOrigin( nent, v ); ent->think = Weapon_HookThink; ent->nextthink = level.time + FRAMETIME; ent->parent->client->ps.pm_flags |= PMF_GRAPPLE_PULL; VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.grapplePoint); trap_LinkEntity( ent ); trap_LinkEntity( nent ); return; } #endif // is it cheaper in bandwidth to just remove this ent and create a new // one, rather than changing the missile into the explosion? #ifndef SMOKINGUNS if ( other->takedamage && other->client ) { #else // alcoohol impact if( !Q_stricmp(ent->classname, "alcohol")){ // no event //G_AddEvent( ent, EV_MISSILE_ALCOHOL, DirToByte( trace->plane.normal)); } else if( !Q_stricmp(ent->classname, "molotov")){ // we have to launch the whiskey drops int i; // set the directions for(i = 0; i < ALC_COUNT; i++){ int temp; VectorSet(bottledirs[i], (rand()%10)-5, (rand()%10)-5, (rand()%10)-3); // direction has to be exactly the same (client and server) temp = DirToByte(bottledirs[i]); ByteToDir(temp, bottledirs[i]); } // dirs BG_DirsToEntityState(&ent->s, bottledirs); // burning if(ent->s.apos.trDelta[0]) G_AddEvent( ent, EV_MISSILE_FIRE, DirToByte( trace->plane.normal)); // not burning else G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal)); } else if ( other->takedamage && other->client ) { #endif G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); ent->s.otherEntityNum = other->s.number; #ifndef SMOKINGUNS } else if( trace->surfaceFlags & SURF_METALSTEPS ) { #else } else if( trace->surfaceFlags & SURF_METAL ) { #endif G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) ); } else { G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); } #ifndef SMOKINGUNS ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact ent->s.eType = ET_GENERAL; #else if(Q_stricmp(ent->classname, "knife")){ ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact ent->s.eType = ET_GENERAL; } else { vec3_t dir; gitem_t *item; item = BG_FindItemForWeapon(WP_KNIFE); ent->s.modelindex = item-bg_itemlist; ent->s.modelindex2 = 1; ent->item = item; ent->s.eType = ET_ITEM; ent->s.pos.trType = TR_GRAVITY; ent->physicsBounce = 0.01f; ent->r.contents = CONTENTS_TRIGGER; ent->touch = Touch_Item; ent->nextthink = level.time + 100; ent->think = G_KnifeThink; ent->wait = level.time + 30000; ent->flags |= FL_THROWN_ITEM; vectoangles(ent->s.pos.trDelta, dir); VectorCopy(dir, ent->s.apos.trBase); VectorCopy(dir, ent->r.currentAngles); } //modified by Spoon END #endif 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 ) { if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ) ) { if( !hitClient ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; } } } // spawn alcohol missiles #ifdef SMOKINGUNS if(!Q_stricmp(ent->classname, "molotov")){ BottleBreak( ent, trace->endpos, trace->plane.normal, bottledirs); } #endif trap_LinkEntity( ent ); } /* ================ G_RunMissile ================ */ void G_RunMissile( gentity_t *ent ) { vec3_t origin; trace_t tr; int passent; #ifdef SMOKINGUNS int shaderNum; gentity_t *traceEnt; #endif // 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; } #ifndef SMOKINGUNS // prox mines that left the owner bbox will attach to anything, even the owner else if (ent->s.weapon == WP_PROX_LAUNCHER && ent->count) { passent = ENTITYNUM_NONE; } #endif else { // ignore interactions with the missile owner passent = ent->r.ownerNum; } // trace a line from the previous position to the current position #ifndef SMOKINGUNS trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask ); #else shaderNum = trap_Trace_New2( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask ); traceEnt = &g_entities[tr.entityNum]; #endif if ( tr.startsolid || tr.allsolid ) { // make sure the tr.entityNum is set to the entity we're stuck in #ifndef SMOKINGUNS trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask ); #else trap_Trace_New( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask ); #endif tr.fraction = 0; } else { VectorCopy( tr.endpos, ent->r.currentOrigin ); } trap_LinkEntity( ent ); if ( tr.fraction != 1 ) { #ifdef SMOKINGUNS VectorCopy(origin, ent->s.origin2); #endif // 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 its a dynamite or molotov let it move 10 seconds before deleting it #ifdef SMOKINGUNS if(ent->s.weapon == WP_DYNAMITE || ent->s.weapon == WP_MOLOTOV || ent->s.weapon == WP_KNIFE){ if(ent->mappart >= level.time && ent->mappart){ goto think; } else if(ent->mappart){ ent->mappart = 0; } else { ent->mappart = level.time + 5000; goto think; } } #endif G_FreeEntity( ent ); return; } #ifndef SMOKINGUNS G_MissileImpact( ent, &tr ); if ( ent->s.eType != ET_MISSILE ) { #else G_MissileImpact( ent, &tr, shaderNum ); if ( ent->s.eType != ET_MISSILE && ent->s.eType != ET_ITEM) { #endif return; // exploded } } #ifndef SMOKINGUNS // if the prox mine wasn't yet outside the player body if (ent->s.weapon == WP_PROX_LAUNCHER && !ent->count) { // check if the prox mine is outside the owner bbox trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, ENTITYNUM_NONE, ent->clipmask ); if (!tr.startsolid || tr.entityNum != ent->r.ownerNum) { ent->count = 1; } } #endif think: // check think function after bouncing G_RunThink( ent ); } //============================================================================= #ifndef SMOKINGUNS /* ================= fire_plasma ================= */ gentity_t *fire_plasma (gentity_t *self, vec3_t start, vec3_t dir) { gentity_t *bolt; VectorNormalize (dir); bolt = G_Spawn(); bolt->classname = "plasma"; bolt->nextthink = level.time + 10000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_PLASMAGUN; bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = 20; bolt->splashDamage = 15; bolt->splashRadius = 20; bolt->methodOfDeath = MOD_PLASMA; bolt->splashMethodOfDeath = MOD_PLASMA_SPLASH; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, 2000, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->r.currentOrigin); return bolt; } //============================================================================= /* ================= fire_grenade ================= */ gentity_t *fire_grenade (gentity_t *self, vec3_t start, vec3_t dir) { gentity_t *bolt; VectorNormalize (dir); bolt = G_Spawn(); bolt->classname = "grenade"; bolt->nextthink = level.time + 2500; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_GRENADE_LAUNCHER; bolt->s.eFlags = EF_BOUNCE_HALF; bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = 100; bolt->splashDamage = 100; bolt->splashRadius = 150; bolt->methodOfDeath = MOD_GRENADE; bolt->splashMethodOfDeath = MOD_GRENADE_SPLASH; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->s.pos.trType = TR_GRAVITY; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, 700, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->r.currentOrigin); return bolt; }
/* ======================== Gatling_Think by Spoon ======================== */ static void Gatling_Think( gentity_t *self) { gclient_t *client; vec3_t end; trace_t trace; // Tequila: Fixed gcc warning about maybe uninitialized use of client client = self->client; if(self->s.eventParm != -1){ client = g_entities[self->s.eventParm].client; // store oldbuttons self->mapparttime = self->mappart; self->mappart = client->buttons; } else { self->mappart = 0; } // if deployed on a breakable surface that breaks, the gatling falls down if (!self->s.time && self->s.pos.trType == TR_STATIONARY) { VectorCopy(self->r.currentOrigin, end); end[2] -= 40; trap_Trace(&trace, self->r.currentOrigin, NULL, NULL, end, self->s.eventParm, MASK_SHOT); if (trace.fraction == 1.0) { // the ground is gone gitem_t *item; vec3_t angles, velocity, dir; gentity_t *ent; if (self->s.eventParm != -1) { ent = &g_entities[self->s.eventParm]; LetGoOfGatling(client, self); } else { ent = self; } VectorCopy(ent->s.apos.trBase, angles); angles[PITCH] = 0; // always forward AngleVectors(angles, velocity, NULL, NULL); VectorScale(velocity, 50, velocity); velocity[2] += 10; VectorNormalize(velocity); if (ent != self) { // drop away from client, so he won't just pick it up in the fall VectorClear(dir); AngleVectors(angles, dir, NULL, NULL); VectorScale(dir, 50, dir); VectorAdd(ent->s.pos.trBase, dir, dir); // snap to integer coordinates for more efficient network bandwidth usage SnapVector(dir); } else { VectorCopy(self->s.pos.trBase, dir); } item = BG_FindItemForWeapon(WP_GATLING); LaunchItem(item, dir, velocity, FL_DROPPED_ITEM); G_FreeEntity(self); return; } } // gatling is being built up if(client && self->s.time2 > 0){ if(client->ps.stats[STAT_HEALTH] <= 0){ gitem_t *item = BG_FindItemForWeapon(WP_GATLING); G_dropWeapon( self, item, 0, FL_DROPPED_ITEM | FL_THROWN_ITEM ); G_FreeEntity(self); return; } if(self->s.time2 < level.time){ self->s.time2 = 0; VectorCopy(gatling_mins, self->r.mins); VectorCopy(gatling_maxs, self->r.maxs); // Set mins/maxs for clientside CG_ScanForCrosshairEntity VectorCopy(self->r.mins, self->s.origin); VectorCopy(self->r.maxs, self->s.origin2); trap_LinkEntity(self); } self->nextthink = level.time; return; } // gatling is being dismantled if(client && self->s.time2 < 0){ if(client->ps.stats[STAT_HEALTH] <= 0){ //abort self->s.time2 = 0; self->s.eventParm = -1; self->nextthink = level.time; client->ps.stats[STAT_FLAGS] &= ~SF_GAT_CHANGE; return; } // start WP_ANIM_CHANGE if(self->s.time2 - TRIPOD_TIME - 4*STAGE_TIME + G_AnimLength(WP_ANIM_CHANGE, WP_GATLING) > -level.time && !(client->ps.stats[STAT_FLAGS] & SF_GAT_CHANGE)) { client->ps.weaponAnim = WP_ANIM_CHANGE; client->ps.weaponTime += G_AnimLength(WP_ANIM_CHANGE, WP_GATLING); client->ps.stats[STAT_FLAGS] |= SF_GAT_CHANGE; } if(self->s.time2 - TRIPOD_TIME - 4*STAGE_TIME > -level.time){ client->ps.stats[STAT_GATLING_MODE] = 0; client->ps.stats[STAT_OLDWEAPON] = 0; client->ps.stats[STAT_FLAGS] &= ~SF_GAT_CHANGE; self->s.eventParm = -1; G_FreeEntity(self); return; } else { self->nextthink = level.time; } return; } // player controls the gatling if(self->s.eventParm != -1){ //change the contents self->r.contents = CONTENTS_CORPSE; //pick gatling up if(client->buttons & BUTTON_ALT_ATTACK && !(self->mapparttime & BUTTON_ALT_ATTACK)){ // don't dismantle if player already has a special weapon if(BG_FindPlayerWeapon(WP_WINCHESTER66, WP_GATLING, &client->ps) || (client->ps.stats[STAT_FLAGS] & SF_GAT_CARRY)){ trap_SendServerCommand( client->ps.clientNum, va("print \"You already have a secondary weapon.\n\"")); } else { int userNum = self->s.eventParm; // save eventparm, because its // getting lost when sending the event client->ps.weaponTime = TRIPOD_TIME + 4*STAGE_TIME; G_AddEvent( self, EV_DISM_TURRET, 0 ); self->s.time2 = -level.time; self->s.eventParm = userNum; } } //get away from gatling is dead or moving, but not when crouching else if(client->ps.stats[STAT_HEALTH] <= 0 || fabs(client->pers.cmd.forwardmove) > 40 || fabs(client->pers.cmd.rightmove) > 40 || client->pers.cmd.upmove){ LetGoOfGatling(client, self); } } else { trace_t trace; trap_Trace_New(&trace, self->r.currentOrigin, gatling_mins, gatling_maxs, self->r.currentOrigin, self->s.number, CONTENTS_BODY); if((trace.allsolid || trace.startsolid) && trace.entityNum < MAX_CLIENTS){ // Tequila comment: Don't stuck a client playing with another gatling // too next from this one self->r.contents = CONTENTS_CORPSE; } else { self->r.contents = MASK_SHOT; } } self->nextthink = level.time; trap_LinkEntity(self); }