/* ================ G_MissileImpact ================ */ void G_MissileImpact( gentity_t *ent, trace_t *trace ) { gentity_t *other; qboolean hitClient = qfalse; #ifdef MISSIONPACK vec3_t forward, impactpoint, bouncedir; int eFlags; #endif other = &g_entities[trace->entityNum]; // 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) { // 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 } G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath); } } #ifdef MISSIONPACK 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; } #endif 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; } // 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->client ) { 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 ) { if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ) ) { if( !hitClient ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; } } } trap_LinkEntity( ent ); }
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; }
/* ================ G_MissileImpact ================ */ void G_MissileImpact( gentity_t *ent, trace_t *trace ) { gentity_t *other; qboolean hitPlayer = qfalse; #if defined MISSIONPACK && !defined TURTLEARENA // POWERS vec3_t forward, impactpoint, bouncedir; int eFlags; #endif #ifdef TA_WEAPSYS qboolean damagedOther = qfalse; #endif other = &g_entities[trace->entityNum]; #if defined MISSIONPACK && !defined TURTLEARENA // POWERS if ( other->takedamage ) { #ifdef TA_WEAPSYS if ( !bg_projectileinfo[ent->s.weapon].stickOnImpact ) #else if ( ent->s.weapon != WP_PROX_LAUNCHER ) #endif { if ( other->player && other->player->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 #ifdef TA_WEAPSYS // stickOnImpact only damages once && !(ent->count & 2) #endif ) { // FIXME: wrong damage direction? if ( ent->damage ) { vec3_t velocity; if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) { g_entities[ent->r.ownerNum].player->accuracy_hits++; hitPlayer = qtrue; } BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if ( VectorLength( velocity ) == 0 ) { #ifdef IOQ3ZTM VectorCopy(trace->plane.normal, velocity); #else velocity[2] = 1; // stepped on a grenade #endif } #ifdef TA_WEAPSYS damagedOther = G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath); #else G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath); #endif } } // check for bounce if ( #ifdef TA_WEAPSYS !damagedOther && #else !other->takedamage && #endif ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); #ifdef TA_WEAPSYS // Bounce missiles // Die on Nth bounce if (ent->s.modelindex2 > 0) { ent->s.modelindex2--; if (ent->s.modelindex2 == 0) { // Kill missile G_ExplodeMissile( ent ); return; } } G_AddEvent( ent, EV_PROJECTILE_BOUNCE, DirToByte( trace->plane.normal ) ); ent->s.time2 = trace->surfaceFlags; // surface #else G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); #endif return; } #ifdef TA_WEAPSYS if (bg_projectileinfo[ent->s.weapon].stickOnImpact != PSOI_NONE) { vec3_t dir; #ifndef TURTLEARENA // if it's a player, stick it on to them (flag them and remove this entity) if( bg_projectileinfo[ent->s.weapon].explosionType == PE_PROX && other->s.eType == ET_PLAYER && other->health > 0 ) { ProximityMine_Player( ent, other ); return; } #endif if (ent->count & 2) { // Already stuck to wall return; } ent->count |= 2; // Don't stick to players or obelisk if (other->s.eType == ET_PLAYER #ifdef MISSIONPACK || (other->pain == ObeliskPain) #endif ) { goto missileExplode; } // Don't stick to the entity that this missile just killed or other missiles if ((damagedOther && other->health <= 0) || other->s.eType == ET_MISSILE) { // Don't remove projectile if it doesn't explode. if (bg_projectileinfo[ent->s.weapon].explosionType == PE_NONE) { ent->s.pos.trType = TR_GRAVITY; ent->count &= ~2; return; } else { goto missileExplode; } } if (bg_projectileinfo[ent->s.weapon].shootable) VectorMA(trace->endpos, -8, trace->plane.normal, trace->endpos); SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); G_SetOrigin( ent, trace->endpos ); if (bg_projectileinfo[ent->s.weapon].stickOnImpact == PSOI_KEEP_ANGLES) { #if 0 // convert direction of travel into axis if ( VectorNormalize2( ent->s.pos.trDelta, dir ) == 0 ) { dir[2] = 1; } // Set the angles vectoangles( dir, ent->s.angles ); #else VectorCopy(trace->plane.normal, dir); #endif } else { VectorCopy(trace->plane.normal, dir); vectoangles( dir, ent->s.angles ); switch (bg_projectileinfo[ent->s.weapon].stickOnImpact) { case PSOI_ANGLE_270: ent->s.angles[0] += 270; break; case PSOI_ANGLE_180: ent->s.angles[0] += 180; break; case PSOI_ANGLE_90: // Maybe this is good for prox mines, but doesn't look good on my // rocket or shuirkens... ent->s.angles[0] += 90; break; case PSOI_ANGLE_0: break; } } // Save direction VectorCopy(dir, ent->s.angles2); ent->s.pos.trType = TR_STATIONARY; VectorClear( ent->s.pos.trDelta ); G_AddEvent( ent, EV_PROJECTILE_STICK, DirToByte(trace->plane.normal) ); ent->s.time2 = trace->surfaceFlags; // surface if (bg_projectileinfo[ent->s.weapon].explosionType == PE_PROX) { // When a BREAKABLE ET_MOVER is killed it drops the projectiles stuck to it, // so don't setup the prox mine when it impact a surface if it already hit been setup. if (ent->die != ProximityMine_Die) { ent->think = ProximityMine_Activate; ent->nextthink = level.time + 2000; ent->die = ProximityMine_Die; } } else { ent->die = G_Missile_Die; } // link the prox mine to the other entity ent->enemy = other; VectorCopy(dir, ent->movedir); VectorSet(ent->s.mins, -4, -4, -4); VectorSet(ent->s.maxs, 4, 4, 4); trap_LinkEntity(ent); return; } #elif defined MISSIONPACK 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->s.mins, -4, -4, -4); VectorSet(ent->s.maxs, 4, 4, 4); trap_LinkEntity(ent); return; } #endif #ifdef TA_WEAPSYS if (bg_projectileinfo[ent->s.weapon].grappling) #else if (!strcmp(ent->classname, "hook")) #endif { gentity_t *nent; vec3_t v; nent = G_Spawn(); if ( other->takedamage && other->player ) { G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); nent->s.otherEntityNum = other->s.number; v[0] = other->r.currentOrigin[0] + (other->s.mins[0] + other->s.maxs[0]) * 0.5; v[1] = other->r.currentOrigin[1] + (other->s.mins[1] + other->s.maxs[1]) * 0.5; v[2] = other->r.currentOrigin[2] + (other->s.mins[2] + other->s.maxs[2]) * 0.5; } else { VectorCopy(trace->endpos, v); G_AddEvent( nent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); } #ifdef IOQ3ZTM // IOQ3BUGFIX: Fix grapple wallmark/death-effect/debris (only tested with TA_WEAPSYS...) nent->s.weapon = ent->s.weapon; #endif #ifdef TA_WEAPSYS if (ent->parent) nent->s.playerNum = ent->parent->s.number; else nent->s.playerNum = ENTITYNUM_NONE; #endif nent->s.weapon = ent->s.weapon; ent->enemy = other; ent->s.groundEntityNum = other->s.number; 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->player->ps.pm_flags |= PMF_GRAPPLE_PULL; VectorCopy( ent->r.currentOrigin, ent->parent->player->ps.grapplePoint); trap_LinkEntity( ent ); trap_LinkEntity( nent ); #ifdef TA_WEAPSYS // Don't grapple to the entity if you just killed it. if (damagedOther && other->health <= 0) { Weapon_HookFree(ent); } #endif return; } #ifdef TA_WEAPSYS missileExplode: #endif // 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->player ) { 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 ) ); } #ifdef TA_WEAPSYS if (ent->parent) ent->s.playerNum = ent->parent->s.number; else ent->s.playerNum = ENTITYNUM_NONE; #endif ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact #ifndef TA_WEAPSYS // Must be after G_RadiusDamage ent->s.eType = ET_GENERAL; #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 ) { #ifdef TA_WEAPSYS if( G_RadiusDamage( trace->endpos, ent, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ) ) #else if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ) ) #endif { if( !hitPlayer ) { g_entities[ent->r.ownerNum].player->accuracy_hits++; } } } #ifdef TA_WEAPSYS ent->s.eType = ET_GENERAL; #endif trap_LinkEntity( ent ); }