void bulletFire( gentity_t *ent, float spread, int damage, int mod ) { trace_t tr; vec3_t end; float r; float u; gentity_t *tent; gentity_t *traceEnt; r = random() * M_PI * 2.0f; u = sin( r ) * crandom() * spread * 16; r = cos( r ) * crandom() * spread * 16; VectorMA( muzzle, 8192 * 16, forward, end ); VectorMA( end, r, right, end ); VectorMA( end, u, up, end ); // don't use unlagged if this is not a client (e.g. turret) if ( ent->client ) { G_UnlaggedOn( ent, muzzle, 8192 * 16 ); trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT ); G_UnlaggedOff(); } else { trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT ); } if ( tr.surfaceFlags & SURF_NOIMPACT ) { return; } traceEnt = &g_entities[ tr.entityNum ]; // snap the endpos to integers, but nudged towards the line SnapVectorTowards( tr.endpos, muzzle ); // send bullet impact if ( traceEnt->takedamage && ( traceEnt->s.eType == ET_PLAYER || traceEnt->s.eType == ET_BUILDABLE ) ) { tent = G_NewTempEntity( tr.endpos, EV_BULLET_HIT_FLESH ); tent->s.eventParm = traceEnt->s.number; } else { tent = G_NewTempEntity( tr.endpos, EV_BULLET_HIT_WALL ); tent->s.eventParm = DirToByte( tr.plane.normal ); } tent->s.otherEntityNum = ent->s.number; if ( traceEnt->takedamage ) { G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, mod ); } }
static void SendRangedHitEvent( gentity_t *attacker, gentity_t *target, trace_t *tr ) { gentity_t *event; // snap the endpos to integers, but nudged towards the line G_SnapVectorTowards( tr->endpos, muzzle ); if ( target->takedamage && ( target->s.eType == ET_BUILDABLE || target->s.eType == ET_PLAYER ) ) { event = G_NewTempEntity( tr->endpos, EV_WEAPON_HIT_ENTITY ); } else { event = G_NewTempEntity( tr->endpos, EV_WEAPON_HIT_ENVIRONMENT ); } // normal event->s.eventParm = DirToByte( tr->plane.normal ); // victim event->s.otherEntityNum = target->s.number; // attacker event->s.otherEntityNum2 = attacker->s.number; // weapon event->s.weapon = attacker->s.weapon; // weapon mode event->s.generic1 = attacker->s.generic1; }
/* ============= G_Sound ============= */ void G_Sound( gentity_t *ent, int channel, int soundIndex ) { gentity_t *te; te = G_NewTempEntity( ent->r.currentOrigin, EV_GENERAL_SOUND ); te->s.eventParm = soundIndex; }
void painSawFire( gentity_t *ent ) { trace_t tr; vec3_t temp; gentity_t *tent, *traceEnt; G_WideTrace( &tr, ent, PAINSAW_RANGE, PAINSAW_WIDTH, PAINSAW_HEIGHT, &traceEnt ); if ( !traceEnt || !traceEnt->takedamage ) { return; } // hack to line up particle system with weapon model tr.endpos[ 2 ] -= 5.0f; // send blood impact if ( traceEnt->s.eType == ET_PLAYER || traceEnt->s.eType == ET_BUILDABLE ) { BloodSpurt( ent, traceEnt, &tr ); } else { VectorCopy( tr.endpos, temp ); tent = G_NewTempEntity( temp, EV_MISSILE_MISS ); tent->s.eventParm = DirToByte( tr.plane.normal ); tent->s.weapon = ent->s.weapon; tent->s.generic1 = ent->s.generic1; //weaponMode } G_Damage( traceEnt, ent, ent, forward, tr.endpos, PAINSAW_DAMAGE, DAMAGE_NO_KNOCKBACK, MOD_PAINSAW ); }
/* =============== WideBloodSpurt Calculates the position of a blood spurt for wide traces and generates an event =============== */ static void WideBloodSpurt( gentity_t *attacker, gentity_t *victim, trace_t *tr ) { gentity_t *tent; vec3_t normal, origin; float mag, radius; if ( !attacker->client ) { return; } if ( victim->health <= 0 ) { return; } if ( tr ) { VectorSubtract( tr->endpos, victim->s.origin, normal ); } else { VectorSubtract( attacker->client->ps.origin, victim->s.origin, normal ); } // Normalize the horizontal components of the vector difference to the // "radius" of the bounding box mag = sqrt( normal[ 0 ] * normal[ 0 ] + normal[ 1 ] * normal[ 1 ] ); radius = victim->r.maxs[ 0 ] * 1.21f; if ( mag > radius ) { normal[ 0 ] = normal[ 0 ] / mag * radius; normal[ 1 ] = normal[ 1 ] / mag * radius; } // Clamp origin to be within bounding box vertically if ( normal[ 2 ] > victim->r.maxs[ 2 ] ) { normal[ 2 ] = victim->r.maxs[ 2 ]; } if ( normal[ 2 ] < victim->r.mins[ 2 ] ) { normal[ 2 ] = victim->r.mins[ 2 ]; } VectorAdd( victim->s.origin, normal, origin ); VectorNegate( normal, normal ); VectorNormalize( normal ); // Create the blood spurt effect entity tent = G_NewTempEntity( origin, EV_MISSILE_HIT ); tent->s.eventParm = DirToByte( normal ); tent->s.otherEntityNum = victim->s.number; tent->s.weapon = attacker->s.weapon; tent->s.generic1 = attacker->s.generic1; // weaponMode }
/* =============== G_BroadcastEvent Sends an event to every client =============== */ void G_BroadcastEvent( int event, int eventParm ) { gentity_t *ent; ent = G_NewTempEntity( vec3_origin, event ); ent->s.eventParm = eventParm; ent->r.svFlags = SVF_BROADCAST; // send to everyone }
static void NotifyClientOfHit( gentity_t *attacker ) { gentity_t *event; if ( !attacker->client ) { return; } event = G_NewTempEntity( attacker->s.origin, EV_HIT ); event->r.svFlags = SVF_SINGLECLIENT; event->r.singleClient = attacker->client->ps.clientNum; }
void shotgunFire( gentity_t *ent ) { gentity_t *tent; // send shotgun blast tent = G_NewTempEntity( muzzle, EV_SHOTGUN ); VectorScale( forward, 4096, tent->s.origin2 ); SnapVector( tent->s.origin2 ); tent->s.eventParm = rand() / ( RAND_MAX / 0x100 + 1 ); // seed for spread pattern tent->s.otherEntityNum = ent->s.number; G_UnlaggedOn( ent, muzzle, SHOTGUN_RANGE ); ShotgunPattern( tent->s.pos.trBase, tent->s.origin2, tent->s.eventParm, ent ); G_UnlaggedOff(); }
/* =========== ClientDisconnect Called when a player drops from the server. Will not be called between levels. This should NOT be called directly by any game logic, call trap_DropClient(), which will call this and do server system housekeeping. ============ */ void ClientDisconnect( int clientNum ) { gentity_t *ent; gentity_t *tent; int i; ent = g_entities + clientNum; if ( !ent->client || ent->client->pers.connected == CON_DISCONNECTED ) { return; } G_LeaveTeam( ent ); G_namelog_disconnect( ent->client ); G_Vote( ent, TEAM_NONE, false ); // stop any following clients for ( i = 0; i < level.maxclients; i++ ) { // remove any /ignore settings for this clientNum Com_ClientListRemove( &level.clients[ i ].sess.ignoreList, clientNum ); } // send effect if they were completely connected if ( ent->client->pers.connected == CON_CONNECTED && ent->client->sess.spectatorState == SPECTATOR_NOT ) { tent = G_NewTempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_OUT ); tent->s.clientNum = ent->s.clientNum; } G_LogPrintf( "ClientDisconnect: %i [%s] (%s) \"%s^7\"", clientNum, ent->client->pers.ip.str, ent->client->pers.guid, ent->client->pers.netname ); ent->client->pers.connected = CON_DISCONNECTED; ent->client->sess.spectatorState = SPECTATOR_NOT; ent->client->ps.persistant[ PERS_SPECSTATE ] = SPECTATOR_NOT; G_FreeEntity(ent); ent->classname = "disconnected"; ent->client = level.clients + clientNum; trap_SetConfigstring( CS_PLAYERS + clientNum, "" ); CalculateRanks(); Beacon::PropagateAll(); }
static void FireShotgun( gentity_t *self ) { gentity_t *tent; // instead of an EV_WEAPON_HIT_* event, send this so client can generate the same spread pattern tent = G_NewTempEntity( muzzle, EV_SHOTGUN ); VectorScale( forward, 4096, tent->s.origin2 ); SnapVector( tent->s.origin2 ); tent->s.eventParm = rand() / ( RAND_MAX / 0x100 + 1 ); // seed for spread pattern tent->s.otherEntityNum = self->s.number; // caclulate the pattern and do the damage G_UnlaggedOn( self, muzzle, SHOTGUN_RANGE ); ShotgunPattern( tent->s.pos.trBase, tent->s.origin2, tent->s.eventParm, self ); G_UnlaggedOff(); }
/* =============== G_BroadcastEvent Sends an event to every client =============== */ void G_BroadcastEvent( int event, int eventParm, team_t team ) { gentity_t *ent; ent = G_NewTempEntity( vec3_origin, event ); ent->s.eventParm = eventParm; if ( team ) { G_TeamToClientmask( team, &ent->r.loMask, &ent->r.hiMask ); ent->r.svFlags = SVF_BROADCAST | SVF_CLIENTMASK; } else { ent->r.svFlags = SVF_BROADCAST; } }
static void FireTesla( gentity_t *self ) { trace_t tr; vec3_t origin, target; gentity_t *tent; if ( !self->target ) { return; } // Move the muzzle from the entity origin up a bit to fire over turrets VectorMA( muzzle, self->r.maxs[ 2 ], self->s.origin2, origin ); // Don't aim for the center, aim at the top of the bounding box VectorCopy( self->target->s.origin, target ); target[ 2 ] += self->target->r.maxs[ 2 ]; // Trace to the target entity trap_Trace( &tr, origin, NULL, NULL, target, self->s.number, MASK_SHOT ); if ( tr.entityNum != self->target->s.number ) { return; } // Client side firing effect self->s.eFlags |= EF_FIRING; // Deal damage if ( self->target->takedamage ) { vec3_t dir; VectorSubtract( target, origin, dir ); VectorNormalize( dir ); G_Damage( self->target, self, self, dir, tr.endpos, TESLAGEN_DMG, 0, MOD_TESLAGEN ); } // Send tesla zap trail tent = G_NewTempEntity( tr.endpos, EV_TESLATRAIL ); tent->s.generic1 = self->s.number; // src tent->s.clientNum = self->target->s.number; // dest }
void massDriverFire( gentity_t *ent ) { trace_t tr; vec3_t end; gentity_t *tent; gentity_t *traceEnt; VectorMA( muzzle, 8192.0f * 16.0f, forward, end ); G_UnlaggedOn( ent, muzzle, 8192.0f * 16.0f ); trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT ); G_UnlaggedOff(); if ( tr.surfaceFlags & SURF_NOIMPACT ) { return; } traceEnt = &g_entities[ tr.entityNum ]; // snap the endpos to integers, but nudged towards the line SnapVectorTowards( tr.endpos, muzzle ); // send impact if ( traceEnt->takedamage && ( traceEnt->s.eType == ET_BUILDABLE || traceEnt->s.eType == ET_PLAYER ) ) { BloodSpurt( ent, traceEnt, &tr ); } else { tent = G_NewTempEntity( tr.endpos, EV_MISSILE_MISS ); tent->s.eventParm = DirToByte( tr.plane.normal ); tent->s.weapon = ent->s.weapon; tent->s.generic1 = ent->s.generic1; //weaponMode } if ( traceEnt->takedamage ) { G_Damage( traceEnt, ent, ent, forward, tr.endpos, MDRIVER_DMG, 0, MOD_MDRIVER ); } }
/* =============== BloodSpurt Generates a blood spurt event for traces with accurate end points =============== */ static void BloodSpurt( gentity_t *attacker, gentity_t *victim, trace_t *tr ) { gentity_t *tent; if ( !attacker->client ) { return; } if ( victim->health <= 0 ) { return; } tent = G_NewTempEntity( tr->endpos, EV_MISSILE_HIT ); tent->s.otherEntityNum = victim->s.number; tent->s.eventParm = DirToByte( tr->plane.normal ); tent->s.weapon = attacker->s.weapon; tent->s.generic1 = attacker->s.generic1; // weaponMode }
static void SendMeleeHitEvent( gentity_t *attacker, gentity_t *target, trace_t *tr ) { gentity_t *event; vec3_t normal, origin; float mag, radius; if ( !attacker->client ) { return; } if ( target->health <= 0 ) { return; } if ( tr ) { VectorSubtract( tr->endpos, target->s.origin, normal ); } else { VectorSubtract( attacker->client->ps.origin, target->s.origin, normal ); } // Normalize the horizontal components of the vector difference to the "radius" of the bounding box mag = sqrt( normal[ 0 ] * normal[ 0 ] + normal[ 1 ] * normal[ 1 ] ); radius = target->r.maxs[ 0 ] * 1.21f; if ( mag > radius ) { normal[ 0 ] = normal[ 0 ] / mag * radius; normal[ 1 ] = normal[ 1 ] / mag * radius; } // Clamp origin to be within bounding box vertically if ( normal[ 2 ] > target->r.maxs[ 2 ] ) { normal[ 2 ] = target->r.maxs[ 2 ]; } if ( normal[ 2 ] < target->r.mins[ 2 ] ) { normal[ 2 ] = target->r.mins[ 2 ]; } VectorAdd( target->s.origin, normal, origin ); VectorNegate( normal, normal ); VectorNormalize( normal ); event = G_NewTempEntity( origin, EV_WEAPON_HIT_ENTITY ); // normal event->s.eventParm = DirToByte( normal ); // victim event->s.otherEntityNum = target->s.number; // attacker event->s.otherEntityNum2 = attacker->s.number; // weapon event->s.weapon = attacker->s.weapon; // weapon mode event->s.generic1 = attacker->s.generic1; }
/** * Awards momentum to a team. * * Will notify the client hwo earned it if given, otherwise the whole team, with *an event. */ static float AddMomentum(momentum_t type, team_t team, float amount, gentity_t* source, bool skipChangeHook) { gentity_t* event = nullptr; gclient_t* client; char* clientName; if (team <= TEAM_NONE || team >= NUM_TEAMS) { return 0.0f; } // apply modifier amount *= MomentumMod(type); // limit a team's total if (level.team[team].momentum + amount > MOMENTUM_MAX) { amount = MOMENTUM_MAX - level.team[team].momentum; } if (amount != 0.0f) { // add momentum to team level.team[team].momentum += amount; // run change hook if requested if (!skipChangeHook) { MomentumChanged(); } // notify source if (source) { client = source->client; if (client && client->pers.team == team) { event = G_NewTempEntity(client->ps.origin, EV_MOMENTUM); event->r.svFlags = SVF_SINGLECLIENT; event->r.singleClient = client->ps.clientNum; } } else { event = G_NewTempEntity(vec3_origin, EV_MOMENTUM); event->r.svFlags = (SVF_BROADCAST | SVF_CLIENTMASK); G_TeamToClientmask(team, &(event->r.loMask), &(event->r.hiMask)); } if (event) { // TODO: Use more bits for momentum value event->s.eventParm = 0; event->s.otherEntityNum = 0; event->s.otherEntityNum2 = (int) (fabs(amount) * 10.0f + 0.5f); event->s.groundEntityNum = amount < 0.0f ? true : false; } // notify legacy stage sensors NotifyLegacyStageSensors(team, amount); } if (g_debugMomentum.integer > 0) { if (source && source->client) { clientName = source->client->pers.netname; } else { clientName = "no source"; } Com_Printf("Momentum: %.2f to %s (%s by %s for %s)\n", amount, BG_TeamNamePlural(team), amount < 0.0f ? "lost" : "earned", clientName, MomentumTypeToReason(type)); } return amount; }
/* ================== player_die ================== */ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int meansOfDeath ) { gentity_t *ent; int anim; int killer; int i; const char *killerName, *obit; if ( self->client->ps.pm_type == PM_DEAD ) { return; } if ( level.intermissiontime ) { return; } self->client->ps.pm_type = PM_DEAD; self->suicideTime = 0; if ( attacker ) { killer = attacker->s.number; if ( attacker->client ) { killerName = attacker->client->pers.netname; } else { killerName = "<world>"; } } else { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( meansOfDeath < 0 || meansOfDeath >= ARRAY_LEN( modNames ) ) { // fall back on the number obit = va( "%d", meansOfDeath ); } else { obit = modNames[ meansOfDeath ]; } G_LogPrintf( "Die: %d %d %s: %s" S_COLOR_WHITE " killed %s\n", killer, ( int )( self - g_entities ), obit, killerName, self->client->pers.netname ); // deactivate all upgrades for ( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ ) { BG_DeactivateUpgrade( i, self->client->ps.stats ); } // broadcast the death event to everyone ent = G_NewTempEntity( self->r.currentOrigin, EV_OBITUARY ); ent->s.eventParm = meansOfDeath; ent->s.otherEntityNum = self->s.number; ent->s.otherEntityNum2 = killer; ent->r.svFlags = SVF_BROADCAST; // send to everyone if ( attacker && attacker->client ) { if ( ( attacker == self || OnSameTeam( self, attacker ) ) ) { //punish team kills and suicides if ( attacker->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) { G_AddCreditToClient( attacker->client, -ALIEN_TK_SUICIDE_PENALTY, qtrue ); G_AddCreditsToScore( attacker, -ALIEN_TK_SUICIDE_PENALTY ); } else if ( attacker->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) { G_AddCreditToClient( attacker->client, -HUMAN_TK_SUICIDE_PENALTY, qtrue ); G_AddCreditsToScore( attacker, -HUMAN_TK_SUICIDE_PENALTY ); } } else if ( g_showKillerHP.integer ) { trap_SendServerCommand( self - g_entities, va( "print_tr %s %s %3i", QQ( N_("Your killer, $1$^7, had $2$ HP.\n") ), Quote( killerName ), attacker->health ) ); } } else if ( attacker->s.eType != ET_BUILDABLE ) { if ( self->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) { G_AddCreditsToScore( self, -ALIEN_TK_SUICIDE_PENALTY ); } else if ( self->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) { G_AddCreditsToScore( self, -HUMAN_TK_SUICIDE_PENALTY ); } } // give credits for killing this player G_RewardAttackers( self ); ScoreboardMessage( self ); // show scores // send updated scores to any clients that are following this one, // or they would get stale scoreboards for ( i = 0; i < level.maxclients; i++ ) { gclient_t *client; client = &level.clients[ i ]; if ( client->pers.connected != CON_CONNECTED ) { continue; } if ( client->sess.spectatorState == SPECTATOR_NOT ) { continue; } if ( client->sess.spectatorClient == self->s.number ) { ScoreboardMessage( g_entities + i ); } } VectorCopy( self->s.origin, self->client->pers.lastDeathLocation ); self->takedamage = qfalse; // can still be gibbed self->s.weapon = WP_NONE; if ( self->client->noclip ) { self->client->cliprcontents = CONTENTS_CORPSE; } else { self->r.contents = CONTENTS_CORPSE; } self->s.angles[ PITCH ] = 0; self->s.angles[ ROLL ] = 0; self->s.angles[ YAW ] = self->s.apos.trBase[ YAW ]; LookAtKiller( self, inflictor, attacker ); VectorCopy( self->s.angles, self->client->ps.viewangles ); self->s.loopSound = 0; self->r.maxs[ 2 ] = -8; // don't allow respawn until the death anim is done // g_forcerespawn may force spawning at some later time self->client->respawnTime = level.time + 1700; // clear misc memset( self->client->ps.misc, 0, sizeof( self->client->ps.misc ) ); { static int i; if ( !( self->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL ) ) { switch ( i ) { case 0: anim = BOTH_DEATH1; break; case 1: anim = BOTH_DEATH2; break; case 2: default: anim = BOTH_DEATH3; break; } } else { switch ( i ) { case 0: anim = NSPA_DEATH1; break; case 1: anim = NSPA_DEATH2; break; case 2: default: anim = NSPA_DEATH3; break; } } self->client->ps.legsAnim = ( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; if ( !( self->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL ) ) { self->client->ps.torsoAnim = ( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; } // use own entityid if killed by non-client to prevent uint8_t overflow G_AddEvent( self, EV_DEATH1 + i, ( killer < MAX_CLIENTS ) ? killer : self - g_entities ); // globally cycle through the different death animations i = ( i + 1 ) % 3; } trap_LinkEntity( self ); self->client->pers.infoChangeTime = level.time; }
void ReactorComponent::CreateTeslaTrail(Entity& target) { gentity_t* trail = G_NewTempEntity(entity.oldEnt->s.origin, EV_TESLATRAIL); trail->s.generic1 = entity.oldEnt->s.number; // Source. trail->s.clientNum = target.oldEnt->s.number; // Destination. }