//---------------------------------------------------------- void fx_runner_think( gentity_t *ent ) { vec3_t temp; EvaluateTrajectory( &ent->s.pos, level.time, ent->currentOrigin ); EvaluateTrajectory( &ent->s.apos, level.time, ent->currentAngles ); // call the effect with the desired position and orientation G_AddEvent( ent, EV_PLAY_EFFECT, ent->fxID ); // Assume angles, we'll do a cross product on the other end to finish up AngleVectors( ent->currentAngles, ent->pos3, NULL, NULL ); MakeNormalVectors( ent->pos3, ent->pos4, temp ); // there IS a reason this is done...it's so that it doesn't break every effect in the game... ent->nextthink = level.time + ent->delay + random() * ent->random; if ( ent->spawnflags & 4 ) // damage { G_RadiusDamage( ent->currentOrigin, ent, ent->splashDamage, ent->splashRadius, ent, MOD_UNKNOWN ); } if ( ent->target2 ) { // let our target know that we have spawned an effect G_UseTargets2( ent, ent, ent->target2 ); } if ( !(ent->spawnflags & 2 ) && !ent->s.loopSound ) // NOT ONESHOT...this is an assy thing to do { if ( VALIDSTRING( ent->soundSet ) == true ) { ent->s.loopSound = CAS_GetBModelSound( ent->soundSet, BMS_MID ); if ( ent->s.loopSound < 0 ) { ent->s.loopSound = 0; } } } }
/* ------------------------- NPC_MineMonster_Pain ------------------------- */ void NPC_MineMonster_Pain(gentity_t *self, gentity_t *attacker, int damage) { G_AddEvent( self, EV_PAIN, floor((float)self->health/self->client->pers.maxHealth*100.0f) ); if ( damage >= 10 ) { TIMER_Remove( self, "attacking" ); TIMER_Remove( self, "attacking1_dmg" ); TIMER_Remove( self, "attacking2_dmg" ); TIMER_Set( self, "takingPain", 1350 ); VectorCopy( &self->NPC->lastPathAngles, &self->s.angles ); NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); if ( self->NPC ) { self->NPC->localState = LSTATE_WAITING; } } }
void __cdecl Slay(void) { int argc = Cmd_Argc(); if (argc < 2) { Com_Printf("Usage: %s <client_id>\n", Cmd_Argv(0)); return; } int i = atoi(Cmd_Argv(1)); if (i < 0 || i > sv_maxclients->integer) { Com_Printf("client_id must be a number between 0 and %d\n.", sv_maxclients->integer); return; } else if (g_entities[i].inuse && g_entities[i].health > 0) { Com_Printf("Slaying player...\n"); SV_SendServerCommand(NULL, "print \"%s^7 was slain!\n\"\n", svs->clients[i].name); DebugPrint("Slaying '%s'!\n", svs->clients[i].name); g_entities[i].health = -40; G_AddEvent(&g_entities[i], EV_GIB_PLAYER, g_entities[i].s.number); } else Com_Printf("The player is currently not active.\n"); }
/* ======================================================================================================================================= AIFunc_Helga_MeleeStart ======================================================================================================================================= */ char *AIFunc_Helga_MeleeStart(cast_state_t *cs) { gentity_t *ent; ent = &g_entities[cs->entityNum]; ent->s.effect1Time = level.time; cs->ideal_viewangles[YAW] = cs->viewangles[YAW]; cs->weaponFireTimes[cs->weaponNum] = level.time; cs->animHitCount = 0; cs->aiFlags |= AIFL_SPECIAL_FUNC; // face them AICast_AimAtEnemy(cs); // play an anim BG_UpdateConditionValue(cs->entityNum, ANIM_COND_WEAPON, cs->weaponNum, qtrue); BG_AnimScriptEvent(&ent->client->ps, ANIM_ET_FIREWEAPON, qfalse, qtrue); // play a sound G_AddEvent(ent, EV_GENERAL_SOUND, G_SoundIndex(aiDefaults[ent->aiCharacter].soundScripts[ATTACKSOUNDSCRIPT])); cs->aifunc = AIFunc_Helga_Melee; cs->aifunc(cs); // think once now, to prevent a delay return "AIFunc_Helga_Melee"; }
/* ================ G_ScriptAction_PlaySound syntax: playsound <soundname OR scriptname> [LOOPING] Currently only allows playing on the VOICE channel, unless you use a sound script. Use the optional LOOPING paramater to attach the sound to the entities looping channel. ================ */ qboolean G_ScriptAction_PlaySound( gentity_t *ent, char *params ) { char *pString, *token; char sound[MAX_QPATH]; if ( !params ) { G_Error( "G_Scripting: syntax error\n\nplaysound <soundname OR scriptname>\n" ); } pString = params; token = COM_ParseExt( &pString, qfalse ); Q_strncpyz( sound, token, sizeof( sound ) ); token = COM_ParseExt( &pString, qfalse ); if ( !token[0] || Q_strcasecmp( token, "looping" ) ) { G_AddEvent( ent, EV_GENERAL_SOUND, G_SoundIndex( sound ) ); } else { // looping channel ent->s.loopSound = G_SoundIndex( sound ); } return qtrue; }
/* ------------------------- NPC_MineMonster_Pain ------------------------- */ void NPC_MineMonster_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const vec3_t point, int damage, int mod,int hitLoc ) { G_AddEvent( self, EV_PAIN, floor((float)self->health/self->max_health*100.0f) ); if ( damage >= 10 ) { TIMER_Remove( self, "attacking" ); TIMER_Remove( self, "attacking1_dmg" ); TIMER_Remove( self, "attacking2_dmg" ); TIMER_Set( self, "takingPain", 1350 ); VectorCopy( self->NPC->lastPathAngles, self->s.angles ); NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); if ( self->NPC ) { self->NPC->localState = LSTATE_WAITING; } } }
/* ================= G_GiveClientMaxAmmo ================= */ void G_GiveClientMaxAmmo( gentity_t *ent, qboolean buyingEnergyAmmo ) { int i; int maxAmmo, maxClips; qboolean weaponType, restoredAmmo = qfalse; for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ ) { if( buyingEnergyAmmo ) weaponType = BG_FindUsesEnergyForWeapon( i ); else weaponType = !BG_FindUsesEnergyForWeapon( i ); if( BG_InventoryContainsWeapon( i, ent->client->ps.stats ) && weaponType && !BG_FindInfinteAmmoForWeapon( i ) && !BG_WeaponIsFull( i, ent->client->ps.stats, ent->client->ps.ammo, ent->client->ps.powerups ) ) { BG_FindAmmoForWeapon( i, &maxAmmo, &maxClips ); if( buyingEnergyAmmo ) { G_AddEvent( ent, EV_RPTUSE_SOUND, 0 ); if( BG_InventoryContainsUpgrade( UP_BATTPACK, ent->client->ps.stats ) ) maxAmmo = (int)( (float)maxAmmo * BATTPACK_MODIFIER ); } else if ( BG_InventoryContainsUpgrade( UP_BATTPACK, ent->client->ps.stats ) ) maxClips = (int)( (float)maxAmmo * BATTPACK_MODIFIER ); BG_PackAmmoArray( i, ent->client->ps.ammo, ent->client->ps.powerups, maxAmmo, maxClips ); restoredAmmo = qtrue; } } if( restoredAmmo ) G_ForceWeaponChange( ent, ent->client->ps.weapon ); }
/* ======================== LetGoOfGatling Make a player let go of the deployed gatling he's using. ======================== */ static void LetGoOfGatling(gclient_t *client, gentity_t *gatling) { // add the ammo into the gatling gatling->count = client->ps.ammo[WP_GATLING]; client->ps.weaponTime = 0; client->ps.eFlags &= ~EF_RELOAD; client->ps.stats[STAT_GATLING_MODE] = 0; // only do that if player doesn't carry another gatling if(!(client->ps.stats[STAT_FLAGS] & SF_GAT_CARRY)) { client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_GATLING); client->ps.ammo[WP_GATLING] = 0; } else { client->ps.ammo[WP_GATLING] = client->carriedGatlingAmmo; } if(!client->ps.stats[STAT_OLDWEAPON] || (client->ps.stats[STAT_OLDWEAPON] == WP_GATLING && !(client->ps.stats[STAT_FLAGS] & SF_GAT_CARRY))) { int i; for ( i = WP_GATLING ; i > 0 ; i-- ) { if ( client->ps.stats[STAT_WEAPONS] & ( 1 << i ) ) { client->ps.stats[STAT_OLDWEAPON] = i; break; } } //G_Printf("away %i\n", client->ps.stats[STAT_OLDWEAPON]); } client->pers.cmd.weapon = client->ps.stats[STAT_OLDWEAPON]; G_AddEvent(&g_entities[gatling->s.eventParm], EV_CHANGE_TO_WEAPON, client->ps.stats[STAT_OLDWEAPON]); gatling->s.eventParm = -1; // Tequila comment: Gatling is now an object in the world gatling->r.contents = MASK_SHOT; }
void Use_Shooter( gentity_t *ent, gentity_t *other, gentity_t *activator ) { vec3_t dir; float deg; vec3_t up, right; // see if we have a target if ( ent->enemy ) { VectorSubtract( ent->enemy->r.currentOrigin, ent->s.origin, dir ); VectorNormalize( dir ); } else { VectorCopy( ent->movedir, dir ); } // randomize a bit PerpendicularVector( up, dir ); CrossProduct( up, dir, right ); deg = crandom() * ent->random; VectorMA( dir, deg, up, dir ); deg = crandom() * ent->random; VectorMA( dir, deg, right, dir ); VectorNormalize( dir ); switch ( ent->s.weapon ) { case WP_GRENADE_LAUNCHER: fire_grenade( ent, ent->s.origin, dir ); break; case WP_ROCKET_LAUNCHER: fire_rocket( ent, ent->s.origin, dir ); break; case WP_PLASMAGUN: fire_plasma( ent, ent->s.origin, dir ); break; } G_AddEvent( ent, EV_FIRE_WEAPON, 0 ); }
/* =============== buildFire =============== */ void buildFire( gentity_t *ent, dynMenu_t menu ) { if( ( ent->client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) > BA_NONE ) { if( ent->client->ps.stats[ STAT_MISC ] > 0 ) { G_AddEvent( ent, EV_BUILD_DELAY, ent->client->ps.clientNum ); return; } if( G_ValidateBuild( ent, ent->client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) ) { if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS && !G_isOvermind( ) ) { ent->client->ps.stats[ STAT_MISC ] += BG_FindBuildDelayForWeapon( ent->s.weapon ) * 2; } else if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS && !G_isPower( muzzle ) && ( ent->client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) != BA_H_REPEATER ) //hack { ent->client->ps.stats[ STAT_MISC ] += BG_FindBuildDelayForWeapon( ent->s.weapon ) * 2; } else ent->client->ps.stats[ STAT_MISC ] += BG_FindBuildDelayForWeapon( ent->s.weapon ); ent->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE; // don't want it bigger than 32k if( ent->client->ps.stats[ STAT_MISC ] > 30000 ) ent->client->ps.stats[ STAT_MISC ] = 30000; } return; } G_TriggerMenu( ent->client->ps.clientNum, menu ); }
void Cmd_UseSentry_f(gentity_t *ent) { if ( ent->health < 1 || in_camera ) { return; } if ( ent->client->ps.inventory[INV_SENTRY] <= 0 ) { // have none to place...play sound? return; } if ( place_portable_assault_sentry( ent, ent->currentOrigin, ent->client->ps.viewangles )) { ent->client->ps.inventory[INV_SENTRY]--; G_AddEvent( ent, EV_USE_INV_SENTRY, 0 ); } else { // couldn't be placed....play a notification sound!! } }
/* ======================================================================================================================================= ObeliskPain ======================================================================================================================================= */ void ObeliskPain(gentity_t *self, gentity_t *attacker, int damage) { int actualDamage; actualDamage = damage / 10; if (actualDamage <= 0) { actualDamage = 1; } self->activator->s.modelindex2 = self->health * 0xff / g_obeliskHealth.integer; if (!self->activator->s.frame) { G_AddEvent(self, EV_OBELISKPAIN, 0); } self->activator->s.frame = 1; if (self->spawnflags == attacker->client->sess.sessionTeam) { AddScore(attacker, self->r.currentOrigin, -actualDamage); } else { AddScore(attacker, self->r.currentOrigin, actualDamage); } }
/** * @brief alarmbox_use * @param[in,out] ent * @param[in] other * @param foo - unused */ void alarmbox_use(gentity_t *ent, gentity_t *other, gentity_t *foo) { if (!(ent->active)) { return; } if (ent->s.frame) { ent->s.frame = 0; } else { ent->s.frame = 1; } alarmbox_updateparts(ent, qtrue); if (other->client) { G_AddEvent(ent, EV_GENERAL_SOUND, ent->soundPos3); } // G_Printf("touched alarmbox\n"); }
void G_minethink(gentity_t *ent) { trace_t tr; vec3_t end, origin, dir; gentity_t *traceEnt; ent->nextthink = level.time + 100; BG_EvaluateTrajectory(&ent->s.pos, level.time, origin); SnapVector(origin); G_SetOrigin(ent, origin); // set aiming directions VectorCopy(origin,end); end[2] += 10;//aim up trap_Trace(&tr, origin, NULL, NULL, end, ent->s.number, MASK_SHOT); if (tr.surfaceFlags & SURF_NOIMPACT) return; traceEnt = &g_entities[tr.entityNum]; dir[0] = dir[1] = 0; dir[2] = 1; if (traceEnt->client && (traceEnt->r.svFlags & SVF_BOT) && traceEnt->health > 0 && traceEnt->client->ps.stats[STAT_PTEAM] == PTE_ALIENS)//FIRE IN ZE HOLE! {//Might want to check team too ent->s.eType = ET_GENERAL; G_AddEvent(ent, EV_MISSILE_MISS, DirToByte(dir)); ent->freeAfterEvent = qtrue; G_RadiusDamage(ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent, ent->splashMethodOfDeath); ent->parent->numMines -= 1; trap_LinkEntity(ent); } }
/* ================ ProximityMine_Player ================ */ static void ProximityMine_Player(gentity_t * mine, gentity_t * player) { if(mine->s.eFlags & EF_NODRAW) { return; } G_AddEvent(mine, EV_PROXIMITY_MINE_STICK, 0); if(player->s.eFlags & EF_TICKING) { player->activator->splashDamage += mine->splashDamage; player->activator->splashRadius *= 1.50; mine->think = G_FreeEntity; mine->nextthink = level.time; return; } player->client->ps.eFlags |= EF_TICKING; player->activator = mine; mine->s.eFlags |= EF_NODRAW; mine->r.svFlags |= SVF_NOCLIENT; mine->s.pos.trType = TR_LINEAR; VectorClear(mine->s.pos.trDelta); mine->enemy = player; mine->think = ProximityMine_ExplodeOnPlayer; if(player->client->invulnerabilityTime > level.time) { mine->nextthink = level.time + 2 * 1000; } else { mine->nextthink = level.time + 10 * 1000; } }
void laserTrapExplode( gentity_t *self ) { vec3_t v; self->takedamage = qfalse; if (self->activator) { G_RadiusDamage( self->r.currentOrigin, self->activator, self->splashDamage, self->splashRadius, self, self, MOD_TRIP_MINE_SPLASH/*MOD_LT_SPLASH*/ ); } if (self->s.weapon != WP_FLECHETTE) { G_AddEvent( self, EV_MISSILE_MISS, 0); } VectorCopy(self->s.pos.trDelta, v); //Explode outward from the surface if (self->s.time == -2) { v[0] = 0; v[1] = 0; v[2] = 0; } if (self->s.weapon == WP_FLECHETTE) { G_PlayEffect(EFFECT_EXPLOSION_FLECHETTE, self->r.currentOrigin, v); } else { G_PlayEffect(EFFECT_EXPLOSION_TRIPMINE, self->r.currentOrigin, v); } self->think = G_FreeEntity; self->nextthink = level.time; }
/* ================== GibEntity ================== */ void GibEntity( gentity_t *self, int killer ) { gentity_t *ent; int i; //if this entity still has kamikaze if (self->s.eFlags & EF_KAMIKAZE) { // check if there is a kamikaze timer around for this owner for (i = 0; i < MAX_GENTITIES; i++) { ent = &g_entities[i]; if (!ent->inuse) continue; if (ent->activator != self) continue; if (strcmp(ent->classname, "kamikaze timer")) continue; G_FreeEntity(ent); break; } } G_AddEvent( self, EV_GIB_PLAYER, killer ); self->takedamage = qfalse; self->s.eType = ET_INVISIBLE; self->r.contents = 0; }
void DoRespawn( edict_t *ent ) { if( ent->team ) { edict_t *master; int count; int choice; master = ent->teammaster; for( count = 0, ent = master; ent; ent = ent->chain, count++ ); choice = rand() % count; for( count = 0, ent = master; count < choice; ent = ent->chain, count++ ); } ent->r.solid = SOLID_TRIGGER; ent->r.svflags &= ~SVF_NOCLIENT; GClip_LinkEntity( ent ); // send an effect G_AddEvent( ent, EV_ITEM_RESPAWN, ent->item ? ent->item->tag : 0, qtrue ); // powerups announce their presence with a global sound if( ent->item && ( ent->item->type & IT_POWERUP ) ) { if( ent->item->tag == POWERUP_QUAD ) G_GlobalSound( CHAN_AUTO, trap_SoundIndex( S_ITEM_QUAD_RESPAWN ) ); if( ent->item->tag == POWERUP_SHELL ) G_GlobalSound( CHAN_AUTO, trap_SoundIndex( S_ITEM_WARSHELL_RESPAWN ) ); if( ent->item->tag == POWERUP_REGEN ) G_GlobalSound( CHAN_AUTO, trap_SoundIndex( S_ITEM_REGEN_RESPAWN ) ); } }
/* ================ G_ExplodeMissile Explode a missile without an impact ================ */ void G_ExplodeMissile( gentity_t *ent ) { vec3_t dir; vec3_t origin; BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); SnapVector( origin ); G_SetOrigin( ent, origin ); // we don't have a valid direction, so just point straight up dir[0] = dir[1] = 0; dir[2] = 1; ent->s.eType = ET_GENERAL; G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) ); ent->freeAfterEvent = qtrue; ent->takedamage = qfalse; // splash damage if ( ent->splashDamage ) { if( G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent, ent, ent->splashMethodOfDeath ) ) { if (ent->parent) { g_entities[ent->parent->s.number].client->accuracy_hits++; } else if (ent->activator) { g_entities[ent->activator->s.number].client->accuracy_hits++; } } } trap->LinkEntity( (sharedEntity_t *)ent ); }
//------------------------------------------ void fx_target_beam_fire( gentity_t *ent ) { trace_t trace; vec3_t dir, org, end; int ignore; qboolean open; if ( !ent->enemy || !ent->enemy->inuse ) {//info_null most likely ignore = ent->s.number; ent->enemy = NULL; VectorCopy( ent->s.origin2, org ); } else { ignore = ent->enemy->s.number; VectorCopy( ent->enemy->currentOrigin, org ); } VectorCopy( org, ent->s.origin2 ); VectorSubtract( org, ent->s.origin, dir ); VectorNormalize( dir ); gi.trace( &trace, ent->s.origin, NULL, NULL, org, ENTITYNUM_NONE, MASK_SHOT );//ignore if ( ent->spawnflags & 2 ) { open = qtrue; VectorCopy( org, end ); } else { open = qfalse; VectorCopy( trace.endpos, end ); } if ( trace.fraction < 1.0 ) { if ( trace.entityNum < ENTITYNUM_WORLD ) { gentity_t *victim = &g_entities[trace.entityNum]; if ( victim && victim->takedamage ) { if ( ent->spawnflags & 4 ) // NO_KNOCKBACK { G_Damage( victim, ent, ent->activator, dir, trace.endpos, ent->damage, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN ); } else { G_Damage( victim, ent, ent->activator, dir, trace.endpos, ent->damage, 0, MOD_UNKNOWN ); } } } } G_AddEvent( ent, EV_TARGET_BEAM_DRAW, ent->fxID ); VectorCopy( end, ent->s.origin2 ); if ( open ) { VectorScale( dir, -1, ent->pos1 ); } else { VectorCopy( trace.plane.normal, ent->pos1 ); } ent->e_ThinkFunc = thinkF_fx_target_beam_think; ent->nextthink = level.time + FRAMETIME; }
/* ================== player_die ================== */ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { gentity_t *ent; int anim; int contents; int killer; int i; char *killerName, *obit; if ( self->client->ps.pm_type == PM_DEAD ) { return; } if ( level.intermissiontime ) { return; } // check for an almost capture CheckAlmostCapture( self, attacker ); // check for a player that almost brought in cubes CheckAlmostScored( self, attacker ); if (self->client && self->client->hook) { Weapon_HookFree(self->client->hook); } #ifdef MISSIONPACK if ((self->client->ps.eFlags & EF_TICKING) && self->activator) { self->client->ps.eFlags &= ~EF_TICKING; self->activator->think = G_FreeEntity; self->activator->nextthink = level.time; } #endif self->client->ps.pm_type = PM_DEAD; if ( attacker ) { killer = attacker->s.number; if ( attacker->client ) { killerName = attacker->client->pers.netname; } else { killerName = "<non-client>"; } } else { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( killer < 0 || killer >= MAX_CLIENTS ) { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( meansOfDeath < 0 || meansOfDeath >= ARRAY_LEN( modNames ) ) { obit = "<bad obituary>"; } else { obit = modNames[meansOfDeath]; } G_LogPrintf("Kill: %i %i %i: %s killed %s by %s\n", killer, self->s.number, meansOfDeath, killerName, self->client->pers.netname, obit ); // broadcast the death event to everyone ent = G_TempEntity( 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 self->enemy = attacker; self->client->ps.persistant[PERS_KILLED]++; if (attacker && attacker->client) { attacker->client->lastkilled_client = self->s.number; if ( attacker == self || OnSameTeam (self, attacker ) ) { AddScore( attacker, self->r.currentOrigin, -1 ); } else { AddScore( attacker, self->r.currentOrigin, 1 ); if( meansOfDeath == MOD_GAUNTLET ) { // play humiliation on player attacker->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++; // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_GAUNTLET; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; // also play humiliation on target self->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_GAUNTLETREWARD; } // check for two kills in a short amount of time // if this is close enough to the last kill, give a reward sound if ( level.time - attacker->client->lastKillTime < CARNAGE_REWARD_TIME ) { // play excellent on player attacker->client->ps.persistant[PERS_EXCELLENT_COUNT]++; // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_EXCELLENT; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; } attacker->client->lastKillTime = level.time; } } else { AddScore( self, self->r.currentOrigin, -1 ); } // Add team bonuses Team_FragBonuses(self, inflictor, attacker); // if I committed suicide, the flag does not fall, it returns. if (meansOfDeath == MOD_SUICIDE) { if ( self->client->ps.powerups[PW_NEUTRALFLAG] ) { // only happens in One Flag CTF Team_ReturnFlag( TEAM_FREE ); self->client->ps.powerups[PW_NEUTRALFLAG] = 0; } else if ( self->client->ps.powerups[PW_REDFLAG] ) { // only happens in standard CTF Team_ReturnFlag( TEAM_RED ); self->client->ps.powerups[PW_REDFLAG] = 0; } else if ( self->client->ps.powerups[PW_BLUEFLAG] ) { // only happens in standard CTF Team_ReturnFlag( TEAM_BLUE ); self->client->ps.powerups[PW_BLUEFLAG] = 0; } } // if client is in a nodrop area, don't drop anything (but return CTF flags!) contents = trap_PointContents( self->r.currentOrigin, -1 ); if ( !( contents & CONTENTS_NODROP )) { TossClientItems( self ); } else { if ( self->client->ps.powerups[PW_NEUTRALFLAG] ) { // only happens in One Flag CTF Team_ReturnFlag( TEAM_FREE ); } else if ( self->client->ps.powerups[PW_REDFLAG] ) { // only happens in standard CTF Team_ReturnFlag( TEAM_RED ); } else if ( self->client->ps.powerups[PW_BLUEFLAG] ) { // only happens in standard CTF Team_ReturnFlag( TEAM_BLUE ); } } #ifdef MISSIONPACK TossClientPersistantPowerups( self ); if( g_gametype.integer == GT_HARVESTER ) { TossClientCubes( self ); } #endif Cmd_Score_f( 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.sessionTeam != TEAM_SPECTATOR ) { continue; } if ( client->sess.spectatorClient == self->s.number ) { Cmd_Score_f( g_entities + i ); } } self->takedamage = qtrue; // can still be gibbed self->s.weapon = WP_NONE; self->s.powerups = 0; self->r.contents = CONTENTS_CORPSE; self->s.angles[0] = 0; self->s.angles[2] = 0; 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; // remove powerups memset( self->client->ps.powerups, 0, sizeof(self->client->ps.powerups) ); // never gib in a nodrop if ( (self->health <= GIB_HEALTH && !(contents & CONTENTS_NODROP) && g_blood.integer) || meansOfDeath == MOD_SUICIDE) { // gib death GibEntity( self, killer ); } else { // normal death static int i; switch ( i ) { case 0: anim = BOTH_DEATH1; break; case 1: anim = BOTH_DEATH2; break; case 2: default: anim = BOTH_DEATH3; break; } // for the no-blood option, we need to prevent the health // from going to gib level if ( self->health <= GIB_HEALTH ) { self->health = GIB_HEALTH+1; } self->client->ps.legsAnim = ( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; self->client->ps.torsoAnim = ( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; G_AddEvent( self, EV_DEATH1 + i, killer ); // the body can still be gibbed self->die = body_die; // globally cycle through the different death animations i = ( i + 1 ) % 3; #ifdef MISSIONPACK if (self->s.eFlags & EF_KAMIKAZE) { Kamikaze_DeathTimer( self ); } #endif } trap_LinkEntity (self); }
void Rancor_Attack( float distance, qboolean doCharge ) { if ( !TIMER_Exists( NPCS.NPC, "attacking" ) ) { if ( NPCS.NPC->count == 2 && NPCS.NPC->activator ) { } else if ( NPCS.NPC->count == 1 && NPCS.NPC->activator ) {//holding enemy if ( NPCS.NPC->activator->health > 0 && Q_irand( 0, 1 ) ) {//quick bite NPC_SetAnim( NPCS.NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); TIMER_Set( NPCS.NPC, "attack_dmg", 450 ); } else {//full eat NPC_SetAnim( NPCS.NPC, SETANIM_BOTH, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); TIMER_Set( NPCS.NPC, "attack_dmg", 900 ); //Make victim scream in fright if ( NPCS.NPC->activator->health > 0 && NPCS.NPC->activator->client ) { G_AddEvent( NPCS.NPC->activator, Q_irand(EV_DEATH1, EV_DEATH3), 0 ); NPC_SetAnim( NPCS.NPC->activator, SETANIM_TORSO, BOTH_FALLDEATH1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); if ( NPCS.NPC->activator->NPC ) {//no more thinking for you TossClientItems( NPCS.NPC ); NPCS.NPC->activator->NPC->nextBStateThink = Q3_INFINITE; } } } } else if ( NPCS.NPC->enemy->health > 0 && doCharge ) {//charge vec3_t fwd, yawAng; VectorSet( yawAng, 0, NPCS.NPC->client->ps.viewangles[YAW], 0 ); AngleVectors( yawAng, fwd, NULL, NULL ); VectorScale( fwd, distance*1.5f, NPCS.NPC->client->ps.velocity ); NPCS.NPC->client->ps.velocity[2] = 150; NPCS.NPC->client->ps.groundEntityNum = ENTITYNUM_NONE; NPC_SetAnim( NPCS.NPC, SETANIM_BOTH, BOTH_MELEE2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); TIMER_Set( NPCS.NPC, "attack_dmg", 1250 ); } else if ( !Q_irand(0, 1) ) {//smash NPC_SetAnim( NPCS.NPC, SETANIM_BOTH, BOTH_MELEE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); TIMER_Set( NPCS.NPC, "attack_dmg", 1000 ); } else {//try to grab NPC_SetAnim( NPCS.NPC, SETANIM_BOTH, BOTH_ATTACK2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); TIMER_Set( NPCS.NPC, "attack_dmg", 1000 ); } TIMER_Set( NPCS.NPC, "attacking", NPCS.NPC->client->ps.legsTimer + random() * 200 ); } // Need to do delayed damage since the attack animations encapsulate multiple mini-attacks if ( TIMER_Done2( NPCS.NPC, "attack_dmg", qtrue ) ) { vec3_t shakePos; switch ( NPCS.NPC->client->ps.legsAnim ) { case BOTH_MELEE1: Rancor_Smash(); G_GetBoltPosition( NPCS.NPC, NPCS.NPC->client->renderInfo.handLBolt, shakePos, 0 ); G_ScreenShake( shakePos, NULL, 4.0f, 1000, qfalse ); //CGCam_Shake( 1.0f*playerDist/128.0f, 1000 ); break; case BOTH_MELEE2: Rancor_Bite(); TIMER_Set( NPCS.NPC, "attack_dmg2", 450 ); break; case BOTH_ATTACK1: if ( NPCS.NPC->count == 1 && NPCS.NPC->activator ) { G_Damage( NPCS.NPC->activator, NPCS.NPC, NPCS.NPC, vec3_origin, NPCS.NPC->activator->r.currentOrigin, Q_irand( 25, 40 ), DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK, MOD_MELEE ); if ( NPCS.NPC->activator->health <= 0 ) {//killed him //make it look like we bit his head off //NPC->activator->client->dismembered = qfalse; G_Dismember( NPCS.NPC->activator, NPCS.NPC, NPCS.NPC->activator->r.currentOrigin, G2_MODELPART_HEAD, 90, 0, NPCS.NPC->activator->client->ps.torsoAnim, qtrue); //G_DoDismemberment( NPC->activator, NPC->activator->r.currentOrigin, MOD_SABER, 1000, HL_HEAD, qtrue ); NPCS.NPC->activator->client->ps.forceHandExtend = HANDEXTEND_NONE; NPCS.NPC->activator->client->ps.forceHandExtendTime = 0; NPC_SetAnim( NPCS.NPC->activator, SETANIM_BOTH, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); } G_Sound( NPCS.NPC->activator, CHAN_AUTO, G_SoundIndex( "sound/chars/rancor/chomp.wav" ) ); } break; case BOTH_ATTACK2: //try to grab Rancor_Swing( qtrue ); break; case BOTH_ATTACK3: if ( NPCS.NPC->count == 1 && NPCS.NPC->activator ) { //cut in half if ( NPCS.NPC->activator->client ) { //NPC->activator->client->dismembered = qfalse; G_Dismember( NPCS.NPC->activator, NPCS.NPC, NPCS.NPC->activator->r.currentOrigin, G2_MODELPART_WAIST, 90, 0, NPCS.NPC->activator->client->ps.torsoAnim, qtrue); //G_DoDismemberment( NPC->activator, NPC->enemy->r.currentOrigin, MOD_SABER, 1000, HL_WAIST, qtrue ); } //KILL G_Damage( NPCS.NPC->activator, NPCS.NPC, NPCS.NPC, vec3_origin, NPCS.NPC->activator->r.currentOrigin, NPCS.NPC->enemy->health+10, DAMAGE_NO_PROTECTION|DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_MELEE );//, HL_NONE );// if ( NPCS.NPC->activator->client ) { NPCS.NPC->activator->client->ps.forceHandExtend = HANDEXTEND_NONE; NPCS.NPC->activator->client->ps.forceHandExtendTime = 0; NPC_SetAnim( NPCS.NPC->activator, SETANIM_BOTH, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); } TIMER_Set( NPCS.NPC, "attack_dmg2", 1350 ); G_Sound( NPCS.NPC->activator, CHAN_AUTO, G_SoundIndex( "sound/chars/rancor/swipehit.wav" ) ); G_AddEvent( NPCS.NPC->activator, EV_JUMP, NPCS.NPC->activator->health ); } break; } } else if ( TIMER_Done2( NPCS.NPC, "attack_dmg2", qtrue ) ) { switch ( NPCS.NPC->client->ps.legsAnim ) { case BOTH_MELEE1: break; case BOTH_MELEE2: Rancor_Bite(); break; case BOTH_ATTACK1: break; case BOTH_ATTACK2: break; case BOTH_ATTACK3: if ( NPCS.NPC->count == 1 && NPCS.NPC->activator ) {//swallow victim G_Sound( NPCS.NPC->activator, CHAN_AUTO, G_SoundIndex( "sound/chars/rancor/chomp.wav" ) ); //FIXME: sometimes end up with a live one in our mouths? //just make sure they're dead if ( NPCS.NPC->activator->health > 0 ) { //cut in half //NPC->activator->client->dismembered = qfalse; G_Dismember( NPCS.NPC->activator, NPCS.NPC, NPCS.NPC->activator->r.currentOrigin, G2_MODELPART_WAIST, 90, 0, NPCS.NPC->activator->client->ps.torsoAnim, qtrue); //G_DoDismemberment( NPC->activator, NPC->enemy->r.currentOrigin, MOD_SABER, 1000, HL_WAIST, qtrue ); //KILL G_Damage( NPCS.NPC->activator, NPCS.NPC, NPCS.NPC, vec3_origin, NPCS.NPC->activator->r.currentOrigin, NPCS.NPC->enemy->health+10, DAMAGE_NO_PROTECTION|DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_MELEE );//, HL_NONE ); NPCS.NPC->activator->client->ps.forceHandExtend = HANDEXTEND_NONE; NPCS.NPC->activator->client->ps.forceHandExtendTime = 0; NPC_SetAnim( NPCS.NPC->activator, SETANIM_BOTH, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); G_AddEvent( NPCS.NPC->activator, EV_JUMP, NPCS.NPC->activator->health ); } if ( NPCS.NPC->activator->client ) {//*sigh*, can't get tags right, just remove them? NPCS.NPC->activator->client->ps.eFlags |= EF_NODRAW; } NPCS.NPC->count = 2; TIMER_Set( NPCS.NPC, "clearGrabbed", 2600 ); } break; } } else if ( NPCS.NPC->client->ps.legsAnim == BOTH_ATTACK2 ) { if ( NPCS.NPC->client->ps.legsTimer >= 1200 && NPCS.NPC->client->ps.legsTimer <= 1350 ) { if ( Q_irand( 0, 2 ) ) { Rancor_Swing( qfalse ); } else { Rancor_Swing( qtrue ); } } else if ( NPCS.NPC->client->ps.legsTimer >= 1100 && NPCS.NPC->client->ps.legsTimer <= 1550 ) { Rancor_Swing( qtrue ); } } // Just using this to remove the attacking flag at the right time TIMER_Done2( NPCS.NPC, "attacking", qtrue ); }
void G_MissileImpact( gentity_t *ent, trace_t *trace ) { gentity_t *other; qboolean hitClient = qfalse; qboolean isKnockedSaber = qfalse; other = &g_entities[trace->entityNum]; // check for bounce if ( !other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && (ent->flags & (FL_BOUNCE | FL_BOUNCE_HALF)) ) { G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } else if ( ent->neverFree && ent->s.weapon == WP_SABER && (ent->flags & FL_BOUNCE_HALF) ) { //this is a knocked-away saber if ( ent->bounceCount > 0 || ent->bounceCount == -5 ) { G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } isKnockedSaber = qtrue; } // I would glom onto the FL_BOUNCE code section above, but don't feel like risking breaking something else if ( (!other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && (ent->flags&(FL_BOUNCE_SHRAPNEL))) || ((trace->surfaceFlags&SURF_FORCEFIELD) && !ent->splashDamage&&!ent->splashRadius && (ent->bounceCount > 0 || ent->bounceCount == -5)) ) { G_BounceMissile( ent, trace ); if ( ent->bounceCount < 1 ) { ent->flags &= ~FL_BOUNCE_SHRAPNEL; } return; } /* if ( !other->takedamage && ent->s.weapon == WP_THERMAL && !ent->alt_fire ) {//rolling thermal det - FIXME: make this an eFlag like bounce & stick!!! //G_BounceRollMissile( ent, trace ); if ( ent->owner && ent->owner->s.number == 0 ) { G_MissileAddAlerts( ent ); } //gi.linkentity( ent ); return; } */ if ( (other->r.contents & CONTENTS_LIGHTSABER) && !isKnockedSaber ) { //hit this person's saber, so.. gentity_t *otherOwner = &g_entities[other->r.ownerNum]; if ( otherOwner->takedamage && otherOwner->client && otherOwner->client->ps.duelInProgress && otherOwner->client->ps.duelIndex != ent->r.ownerNum ) { goto killProj; } } else if ( !isKnockedSaber ) { if ( other->takedamage && other->client && other->client->ps.duelInProgress && other->client->ps.duelIndex != ent->r.ownerNum ) { goto killProj; } } if ( other->flags & FL_DMG_BY_HEAVY_WEAP_ONLY ) { if ( ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_ROCKET && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_ROCKET_HOMING && ent->methodOfDeath != MOD_THERMAL && ent->methodOfDeath != MOD_THERMAL_SPLASH && ent->methodOfDeath != MOD_TRIP_MINE_SPLASH && ent->methodOfDeath != MOD_TIMED_MINE_SPLASH && ent->methodOfDeath != MOD_DET_PACK_SPLASH && ent->methodOfDeath != MOD_VEHICLE && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT && ent->methodOfDeath != MOD_SABER && ent->methodOfDeath != MOD_TURBLAST ) { vector3 fwd; if ( trace ) { VectorCopy( &trace->plane.normal, &fwd ); } else { //oh well AngleVectors( &other->r.currentAngles, &fwd, NULL, NULL ); } G_DeflectMissile( other, ent, &fwd ); G_MissileBounceEffect( ent, &ent->r.currentOrigin, &fwd ); return; } } if ( (other->flags & FL_SHIELDED) && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_TRIP_MINE && ent->s.weapon != WP_DET_PACK && ent->s.weapon != WP_DEMP2 && ent->s.weapon != WP_EMPLACED_GUN && ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_TURBLAST && ent->methodOfDeath != MOD_VEHICLE && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT && !(ent->dflags&DAMAGE_HEAVY_WEAP_CLASS) ) { vector3 fwd; if ( other->client ) { AngleVectors( &other->client->ps.viewangles, &fwd, NULL, NULL ); } else { AngleVectors( &other->r.currentAngles, &fwd, NULL, NULL ); } G_DeflectMissile( other, ent, &fwd ); G_MissileBounceEffect( ent, &ent->r.currentOrigin, &fwd ); return; } if ( other->takedamage && other->client && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_TRIP_MINE && ent->s.weapon != WP_DET_PACK && ent->s.weapon != WP_DEMP2 && ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT && other->client->ps.saberBlockTime < level.time && !isKnockedSaber && WP_SaberCanBlock( other, &ent->r.currentOrigin, 0, 0, qtrue, 0 ) ) { //only block one projectile per 200ms (to prevent giant swarms of projectiles being blocked) vector3 fwd; gentity_t *te; int otherDefLevel = other->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE]; te = G_TempEntity( &ent->r.currentOrigin, EV_SABER_BLOCK ); VectorCopy( &ent->r.currentOrigin, &te->s.origin ); VectorCopy( &trace->plane.normal, &te->s.angles ); te->s.eventParm = 0; te->s.weapon = 0;//saberNum te->s.legsAnim = 0;//bladeNum /*if (other->client->ps.velocity[2] > 0 || other->client->pers.cmd.forwardmove || other->client->pers.cmd.rightmove) */ if ( other->client->ps.velocity.z > 0 || other->client->pers.cmd.forwardmove < 0 ) //now we only do it if jumping or running backward. Should be able to full-on charge. { otherDefLevel -= 1; if ( otherDefLevel < 0 ) { otherDefLevel = 0; } } AngleVectors( &other->client->ps.viewangles, &fwd, NULL, NULL ); if ( otherDefLevel == FORCE_LEVEL_1 ) { //if def is only level 1, instead of deflecting the shot it should just die here } else if ( otherDefLevel == FORCE_LEVEL_2 ) G_DeflectMissile( other, ent, &fwd ); else G_ReflectMissile( other, ent, &fwd ); other->client->ps.saberBlockTime = level.time + (350 - (otherDefLevel * 100)); //200; //For jedi AI other->client->ps.saberEventFlags |= SEF_DEFLECTED; if ( otherDefLevel == FORCE_LEVEL_3 ) other->client->ps.saberBlockTime = 0; //^_^ if ( otherDefLevel == FORCE_LEVEL_1 ) goto killProj; return; } else if ( (other->r.contents & CONTENTS_LIGHTSABER) && !isKnockedSaber ) { //hit this person's saber, so.. gentity_t *otherOwner = &g_entities[other->r.ownerNum]; if ( otherOwner->takedamage && otherOwner->client && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_TRIP_MINE && ent->s.weapon != WP_DET_PACK && ent->s.weapon != WP_DEMP2 && ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT /*&& otherOwner->client->ps.saberBlockTime < level.time*/ ) { //for now still deflect even if saberBlockTime >= level.time because it hit the actual saber vector3 fwd; gentity_t *te; int otherDefLevel = otherOwner->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE]; //in this case, deflect it even if we can't actually block it because it hit our saber //WP_SaberCanBlock(otherOwner, ent->r.currentOrigin, 0, 0, qtrue, 0); if ( otherOwner->client && otherOwner->client->ps.weaponTime <= 0 ) { WP_SaberBlockNonRandom( otherOwner, &ent->r.currentOrigin, qtrue ); } te = G_TempEntity( &ent->r.currentOrigin, EV_SABER_BLOCK ); VectorCopy( &ent->r.currentOrigin, &te->s.origin ); VectorCopy( &trace->plane.normal, &te->s.angles ); te->s.eventParm = 0; te->s.weapon = 0;//saberNum te->s.legsAnim = 0;//bladeNum /*if (otherOwner->client->ps.velocity[2] > 0 || otherOwner->client->pers.cmd.forwardmove || otherOwner->client->pers.cmd.rightmove)*/ if ( otherOwner->client->ps.velocity.z > 0 || otherOwner->client->pers.cmd.forwardmove < 0 ) //now we only do it if jumping or running backward. Should be able to full-on charge. { otherDefLevel -= 1; if ( otherDefLevel < 0 ) { otherDefLevel = 0; } } AngleVectors( &otherOwner->client->ps.viewangles, &fwd, NULL, NULL ); if ( otherDefLevel == FORCE_LEVEL_1 ) { //if def is only level 1, instead of deflecting the shot it should just die here } else if ( otherDefLevel == FORCE_LEVEL_2 ) { G_DeflectMissile( otherOwner, ent, &fwd ); } else { G_ReflectMissile( otherOwner, ent, &fwd ); } otherOwner->client->ps.saberBlockTime = level.time + (350 - (otherDefLevel * 100));//200; //For jedi AI otherOwner->client->ps.saberEventFlags |= SEF_DEFLECTED; if ( otherDefLevel == FORCE_LEVEL_3 ) { otherOwner->client->ps.saberBlockTime = 0; //^_^ } if ( otherDefLevel == FORCE_LEVEL_1 ) { goto killProj; } return; } } // check for sticking if ( !other->takedamage && (ent->s.eFlags & EF_MISSILE_STICK) ) { laserTrapStick( ent, &trace->endpos, &trace->plane.normal ); G_AddEvent( ent, EV_MISSILE_STICK, 0 ); return; } // impact damage if ( other->takedamage && !isKnockedSaber ) { // FIXME: wrong damage direction? if ( ent->damage ) { vector3 velocity; qboolean didDmg = qfalse; 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.z = 1; // stepped on a grenade } if ( ent->s.weapon == WP_BOWCASTER || ent->s.weapon == WP_FLECHETTE || ent->s.weapon == WP_ROCKET_LAUNCHER ) { if ( ent->s.weapon == WP_FLECHETTE && (ent->s.eFlags & EF_ALT_FIRING) ) { ent->think( ent ); JPLua::Entity_CallFunction( ent, JPLua::JPLUA_ENTITY_THINK ); } else { G_Damage( other, ent, &g_entities[ent->r.ownerNum], &velocity, /*ent->s.origin*/&ent->r.currentOrigin, ent->damage, DAMAGE_HALF_ABSORB, ent->methodOfDeath ); didDmg = qtrue; } } else { G_Damage( other, ent, &g_entities[ent->r.ownerNum], &velocity, /*ent->s.origin*/&ent->r.currentOrigin, ent->damage, 0, ent->methodOfDeath ); didDmg = qtrue; } //Raz: air shots if ( (other->client && other->client->ps.groundEntityNum == ENTITYNUM_NONE) && (ent->methodOfDeath == MOD_CONC || ent->methodOfDeath == MOD_REPEATER_ALT || ent->methodOfDeath == MOD_ROCKET || ent->methodOfDeath == MOD_ROCKET_HOMING || ent->methodOfDeath == MOD_THERMAL) ) { g_entities[ent->r.ownerNum].client->ps.persistant[PERS_IMPRESSIVE_COUNT]++; } if ( didDmg && other && other->client ) { //What I'm wondering is why this isn't in the NPC pain funcs. But this is what SP does, so whatever. class_t npc_class = other->client->NPC_class; // If we are a robot and we aren't currently doing the full body electricity... if ( npc_class == CLASS_SEEKER || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE || npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 || npc_class == CLASS_REMOTE || npc_class == CLASS_MARK1 || npc_class == CLASS_MARK2 || //npc_class == CLASS_PROTOCOL ||//no protocol, looks odd npc_class == CLASS_INTERROGATOR || npc_class == CLASS_ATST || npc_class == CLASS_SENTRY ) { // special droid only behaviors if ( other->client->ps.electrifyTime < level.time + 100 ) { // ... do the effect for a split second for some more feedback other->client->ps.electrifyTime = level.time + 450; } //FIXME: throw some sparks off droids,too } } } if ( ent->s.weapon == WP_DEMP2 ) {//a hit with demp2 decloaks people, disables ships if ( other && other->client && other->client->NPC_class == CLASS_VEHICLE ) {//hit a vehicle if ( other->m_pVehicle //valid vehicle ent && other->m_pVehicle->m_pVehicleInfo//valid stats && (other->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER//always affect speeders || (other->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER && ent->classname && Q_stricmp( "vehicle_proj", ent->classname ) == 0))//only vehicle ion weapons affect a fighter in this manner && !FighterIsLanded( other->m_pVehicle, &other->client->ps )//not landed && !(other->spawnflags & 2) )//and not suspended {//vehicles hit by "ion cannons" lose control if ( other->client->ps.electrifyTime > level.time ) {//add onto it //FIXME: extern the length of the "out of control" time? other->client->ps.electrifyTime += Q_irand( 200, 500 ); if ( other->client->ps.electrifyTime > level.time + 4000 ) {//cap it other->client->ps.electrifyTime = level.time + 4000; } } else {//start it //FIXME: extern the length of the "out of control" time? other->client->ps.electrifyTime = level.time + Q_irand( 200, 500 ); } } } else if ( other && other->client && other->client->ps.powerups[PW_CLOAKED] ) { Jedi_Decloak( other ); if ( ent->methodOfDeath == MOD_DEMP2_ALT ) {//direct hit with alt disables cloak forever //permanently disable the saboteur's cloak other->client->cloakToggleTime = Q3_INFINITE; } else {//temp disable other->client->cloakToggleTime = level.time + Q_irand( 3000, 10000 ); } } } } killProj: if ( !strcmp( ent->classname, "hook" ) ) { // gentity_t *nent = G_Spawn(); vector3 v; int i; if ( other->takedamage && other->client ) { // G_AddEvent( nent, EV_DISRUPTOR_HIT, DirToByte( trace->plane.normal ) ); // nent->s.otherEntityNum = other->s.number; ent->enemy = other; for ( i = 0; i < 3; i++ ) v.raw[i] = other->r.currentOrigin.raw[i] + (other->r.mins.raw[i] + other->r.maxs.raw[i]) * 0.5f; SnapVectorTowards( &v, &ent->s.pos.trBase ); // Save net bandwidth } else { VectorCopy( &trace->endpos, &v ); // G_AddEvent( nent, EV_DISRUPTOR_HIT, DirToByte( trace->plane.normal ) ); ent->enemy = NULL; } SnapVectorTowards( &v, &ent->s.pos.trBase ); // Save net bandwidth // nent->freeAfterEvent = true; //Change over to a normal entity right at the point of impact // nent->s.eType = ET_GENERAL; // ent->s.eType = ET_GENERAL; 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; // ent->parent->client->ps.eFlags |= EF_GRAPPLE_SWING; //ent->genericValue10 = 1; ent->parent->client->fireHeld = qfalse; VectorCopy( &ent->r.currentOrigin, &ent->parent->client->ps.lastHitLoc ); trap->LinkEntity( (sharedEntity_t *)ent ); // trap->LinkEntity( (sharedEntity_t *)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 && !isKnockedSaber ) { 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 if ( ent->s.weapon != G2_MODEL_PART && !isKnockedSaber ) { G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( &trace->plane.normal ) ); } if ( !isKnockedSaber ) { 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 ); ent->takedamage = qfalse; // 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, ent->splashMethodOfDeath ) ) { if ( !hitClient && g_entities[ent->r.ownerNum].client ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; } } } if ( ent->s.weapon == G2_MODEL_PART ) { ent->freeAfterEvent = qfalse; //it will free itself } trap->LinkEntity( (sharedEntity_t *)ent ); }
/* ================ Use_BinaryMover ================ */ void Use_BinaryMover( gentity_t *ent, gentity_t *other, gentity_t *activator ) { int total; int partial; // only the master should be used if ( ent->flags & FL_TEAMSLAVE ) { Use_BinaryMover( ent->teammaster, other, activator ); return; } ent->activator = activator; if ( ent->moverState == MOVER_POS1 ) { // start moving 50 msec later, becase if this was player // triggered, level.time hasn't been advanced yet MatchTeam( ent, MOVER_1TO2, level.time + 50 ); // starting sound if ( ent->sound1to2 ) { G_AddEvent( ent, EV_GENERAL_SOUND, ent->sound1to2 ); } // looping sound ent->s.loopSound = ent->soundLoop; // open areaportal if ( ent->teammaster == ent || !ent->teammaster ) { trap_AdjustAreaPortalState( ent, qtrue ); } return; } // if all the way up, just delay before coming down if ( ent->moverState == MOVER_POS2 ) { ent->nextthink = level.time + ent->wait; return; } // only partway down before reversing if ( ent->moverState == MOVER_2TO1 ) { total = ent->s.pos.trDuration; partial = level.time - ent->s.pos.trTime; if ( partial > total ) { partial = total; } MatchTeam( ent, MOVER_1TO2, level.time - ( total - partial ) ); if ( ent->sound1to2 ) { G_AddEvent( ent, EV_GENERAL_SOUND, ent->sound1to2 ); } return; } // only partway up before reversing if ( ent->moverState == MOVER_1TO2 ) { total = ent->s.pos.trDuration; partial = level.time - ent->s.pos.trTime; if ( partial > total ) { partial = total; } MatchTeam( ent, MOVER_2TO1, level.time - ( total - partial ) ); if ( ent->sound2to1 ) { G_AddEvent( ent, EV_GENERAL_SOUND, ent->sound2to1 ); } return; } }
/* * W_Touch_Grenade */ static void W_Touch_Grenade( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags ) { int hitType; vec3_t dir; if( surfFlags & SURF_NOIMPACT ) { G_FreeEdict( ent ); return; } hitType = G_Projectile_HitStyle( ent, other ); if( hitType == PROJECTILE_TOUCH_NOT ) return; // don't explode on doors and plats that take damage if( !other->takedamage || ISBRUSHMODEL( other->s.modelindex ) ) { // kill some velocity on each bounce float fric; static cvar_t *g_grenade_friction = NULL; if( !g_grenade_friction ) g_grenade_friction = trap_Cvar_Get( "g_grenade_friction", "0.85", CVAR_DEVELOPER ); fric = bound( 0, g_grenade_friction->value, 1 ); VectorScale( ent->velocity, fric, ent->velocity ); G_AddEvent( ent, EV_GRENADE_BOUNCE, ( ent->s.effects & EF_STRONG_WEAPON ) ? FIRE_MODE_STRONG : FIRE_MODE_WEAK, qtrue ); return; } if( other->takedamage ) { int directHitDamage = ent->projectileInfo.maxDamage; VectorNormalize2( ent->velocity, dir ); if( hitType == PROJECTILE_TOUCH_DIRECTSPLASH ) // use hybrid direction from splash and projectile { G_SplashFrac4D( ENTNUM( other ), ent->s.origin, ent->projectileInfo.radius, dir, NULL, NULL, ent->timeDelta ); } else { VectorNormalize2( ent->velocity, dir ); // no direct hit bonuses for grenades /* if( hitType == PROJECTILE_TOUCH_DIRECTAIRHIT ) directHitDamage += DIRECAIRTHIT_DAMAGE_BONUS; else if( hitType == PROJECTILE_TOUCH_DIRECTHIT ) directHitDamage += DIRECTHIT_DAMAGE_BONUS; */ } G_Damage( other, ent, ent->r.owner, dir, ent->velocity, ent->s.origin, directHitDamage, ent->projectileInfo.maxKnockback, ent->projectileInfo.stun, 0, ent->style ); } ent->enemy = other; W_Grenade_ExplodeDir( ent, plane ? plane->normal : NULL ); }
/* ============ G_MoverPush Objects need to be moved back on a failed push, otherwise riders would continue to slide. If qfalse is returned, *obstacle will be the blocking entity ============ */ qboolean G_MoverPush( gentity_t *pusher, vec3_t move, vec3_t amove, gentity_t **obstacle ) { int i, e; gentity_t *check; vec3_t mins, maxs; pushed_t *p; int entityList[MAX_GENTITIES]; int listedEntities; vec3_t totalMins, totalMaxs; *obstacle = NULL; // mins/maxs are the bounds at the destination // totalMins / totalMaxs are the bounds for the entire move if ( pusher->r.currentAngles[0] || pusher->r.currentAngles[1] || pusher->r.currentAngles[2] || amove[0] || amove[1] || amove[2] ) { float radius; radius = RadiusFromBounds( pusher->r.mins, pusher->r.maxs ); for ( i = 0 ; i < 3 ; i++ ) { mins[i] = pusher->r.currentOrigin[i] + move[i] - radius; maxs[i] = pusher->r.currentOrigin[i] + move[i] + radius; totalMins[i] = mins[i] - move[i]; totalMaxs[i] = maxs[i] - move[i]; } } else { for (i=0 ; i<3 ; i++) { mins[i] = pusher->r.absmin[i] + move[i]; maxs[i] = pusher->r.absmax[i] + move[i]; } VectorCopy( pusher->r.absmin, totalMins ); VectorCopy( pusher->r.absmax, totalMaxs ); for (i=0 ; i<3 ; i++) { if ( move[i] > 0 ) { totalMaxs[i] += move[i]; } else { totalMins[i] += move[i]; } } } // unlink the pusher so we don't get it in the entityList trap_UnlinkEntity( pusher ); listedEntities = trap_EntitiesInBox( totalMins, totalMaxs, entityList, MAX_GENTITIES ); // move the pusher to it's final position VectorAdd( pusher->r.currentOrigin, move, pusher->r.currentOrigin ); VectorAdd( pusher->r.currentAngles, amove, pusher->r.currentAngles ); trap_LinkEntity( pusher ); // see if any solid entities are inside the final position for ( e = 0 ; e < listedEntities ; e++ ) { check = &g_entities[ entityList[ e ] ]; #ifdef MISSIONPACK if ( check->s.eType == ET_MISSILE ) { // if it is a prox mine if ( !strcmp(check->classname, "prox mine") ) { // if this prox mine is attached to this mover try to move it with the pusher if ( check->enemy == pusher ) { if (!G_TryPushingProxMine( check, pusher, move, amove )) { //explode check->s.loopSound = 0; G_AddEvent( check, EV_PROXIMITY_MINE_TRIGGER, 0 ); G_ExplodeMissile(check); if (check->activator) { G_FreeEntity(check->activator); check->activator = NULL; } //G_Printf("prox mine explodes\n"); } } else { //check if the prox mine is crushed by the mover if (!G_CheckProxMinePosition( check )) { //explode check->s.loopSound = 0; G_AddEvent( check, EV_PROXIMITY_MINE_TRIGGER, 0 ); G_ExplodeMissile(check); if (check->activator) { G_FreeEntity(check->activator); check->activator = NULL; } //G_Printf("prox mine explodes\n"); } } continue; } } #endif // only push items and players if ( check->s.eType != ET_ITEM && check->s.eType != ET_PLAYER && !check->physicsObject ) { continue; } // if the entity is standing on the pusher, it will definitely be moved if ( check->s.groundEntityNum != pusher->s.number ) { // see if the ent needs to be tested if ( check->r.absmin[0] >= maxs[0] || check->r.absmin[1] >= maxs[1] || check->r.absmin[2] >= maxs[2] || check->r.absmax[0] <= mins[0] || check->r.absmax[1] <= mins[1] || check->r.absmax[2] <= mins[2] ) { continue; } // see if the ent's bbox is inside the pusher's final position // this does allow a fast moving object to pass through a thin entity... if (!G_TestEntityPosition (check)) { continue; } } // the entity needs to be pushed if ( G_TryPushingEntity( check, pusher, move, amove ) ) { continue; } // the move was blocked an entity // bobbing entities are instant-kill and never get blocked if ( pusher->s.pos.trType == TR_SINE || pusher->s.apos.trType == TR_SINE ) { G_Damage( check, pusher, pusher, NULL, NULL, 99999, 0, MOD_CRUSH ); continue; } // save off the obstacle so we can call the block function (crush, etc) *obstacle = check; // move back any entities we already moved // go backwards, so if the same entity was pushed // twice, it goes back to the original position for ( p=pushed_p-1 ; p>=pushed ; p-- ) { VectorCopy (p->origin, p->ent->s.pos.trBase); VectorCopy (p->angles, p->ent->s.apos.trBase); if ( p->ent->client ) { p->ent->client->ps.delta_angles[YAW] = p->deltayaw; VectorCopy (p->origin, p->ent->client->ps.origin); } trap_LinkEntity (p->ent); } return qfalse; } return qtrue; }
/* ================== 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 target_rumble_think (gentity_t * ent) { gentity_t *tent; float ratio; float time, time2; float dapitch, dayaw; qboolean validrumble = qtrue; if (!(ent->count)) { ent->timestamp = level.time; ent->count ++; // start sound here if (ent->soundPos1) G_AddEvent( ent, EV_GENERAL_SOUND, ent->soundPos1); } else { // looping sound ent->s.loopSound = ent->soundLoop; } dapitch = ent->delay; dayaw = ent->random; ratio = 1.0f; if (ent->start_size) { if (level.time < (ent->timestamp + ent->start_size)) { time = level.time - ent->timestamp; time2 = (ent->timestamp + ent->start_size) - ent->timestamp; ratio = time / time2; } else if (level.time < (ent->timestamp + ent->end_size + ent->start_size)) { time = level.time - ent->timestamp; time2 = (ent->timestamp + ent->start_size + ent->end_size) - ent->timestamp; ratio = time2 / time; } else validrumble = qfalse; } if (validrumble) { tent = G_TempEntity (ent->r.currentOrigin, EV_RUMBLE_EFX); tent->s.angles[0] = dapitch * ratio; tent->s.angles[1] = dayaw * ratio; } // end sound if (level.time > ent->duration + ent->timestamp) { if (ent->soundPos2) { G_AddEvent( ent, EV_GENERAL_SOUND, ent->soundPos2 ); ent->s.loopSound = 0; } ent->nextthink = 0; } else ent->nextthink = level.time + 50; }
void NPC_Touch(gentity_t *self, gentity_t *other, trace_t *trace) { if(!self->NPC) return; SaveNPCGlobals(); SetNPCGlobals( self ); if ( self->message && self->health <= 0 ) {//I am dead and carrying a key if ( other && player && player->health > 0 && other == player ) {//player touched me char *text; qboolean keyTaken; //give him my key if ( Q_stricmp( "goodie", self->message ) == 0 ) {//a goodie key if ( (keyTaken = INV_GoodieKeyGive( other )) == qtrue ) { text = "cp @SP_INGAME_TOOK_IMPERIAL_GOODIE_KEY"; G_AddEvent( other, EV_ITEM_PICKUP, (FindItemForInventory( INV_GOODIE_KEY )-bg_itemlist) ); } else { text = "cp @SP_INGAME_CANT_CARRY_GOODIE_KEY"; } } else {//a named security key if ( (keyTaken = INV_SecurityKeyGive( player, self->message )) == qtrue ) { text = "cp @SP_INGAME_TOOK_IMPERIAL_SECURITY_KEY"; G_AddEvent( other, EV_ITEM_PICKUP, (FindItemForInventory( INV_SECURITY_KEY )-bg_itemlist) ); } else { text = "cp @SP_INGAME_CANT_CARRY_SECURITY_KEY"; } } if ( keyTaken ) {//remove my key gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "l_arm_key", 0x00000002 ); self->message = NULL; self->client->ps.eFlags &= ~EF_FORCE_VISIBLE; //remove sight flag G_Sound( player, G_SoundIndex( "sound/weapons/key_pkup.wav" ) ); } gi.SendServerCommand( 0, text ); } } if ( other->client ) {//FIXME: if pushing against another bot, both ucmd.rightmove = 127??? //Except if not facing one another... if ( other->health > 0 ) { NPCInfo->touchedByPlayer = other; } if ( other == NPCInfo->goalEntity ) { NPCInfo->aiFlags |= NPCAI_TOUCHED_GOAL; } if( !(self->svFlags&SVF_LOCKEDENEMY) && !(self->svFlags&SVF_IGNORE_ENEMIES) && !(other->flags & FL_NOTARGET) ) { if ( self->client->enemyTeam ) {//See if we bumped into an enemy if ( other->client->playerTeam == self->client->enemyTeam ) {//bumped into an enemy if( NPCInfo->behaviorState != BS_HUNT_AND_KILL && !NPCInfo->tempBehavior ) {//MCG - Begin: checking specific BS mode here, this is bad, a HACK //FIXME: not medics? if ( NPC->enemy != other ) {//not already mad at them G_SetEnemy( NPC, other ); } // NPCInfo->tempBehavior = BS_HUNT_AND_KILL; } } } } //FIXME: do this if player is moving toward me and with a certain dist? /* if ( other->s.number == 0 && self->client->playerTeam == other->client->playerTeam ) { VectorAdd( self->client->pushVec, other->client->ps.velocity, self->client->pushVec ); } */ } else {//FIXME: check for SVF_NONNPC_ENEMY flag here? if ( other->health > 0 ) { if ( NPC->enemy == other && (other->svFlags&SVF_NONNPC_ENEMY) ) { NPCInfo->touchedByPlayer = other; } } if ( other == NPCInfo->goalEntity ) { NPCInfo->aiFlags |= NPCAI_TOUCHED_GOAL; } } if ( NPC->client->NPC_class == CLASS_RANCOR ) {//rancor if ( NPCInfo->blockedEntity != other && TIMER_Done(NPC, "blockedEntityIgnore")) {//blocked //if ( G_EntIsBreakable( other->s.number, NPC ) ) {//bumped into another breakable, so take that one instead? NPCInfo->blockedEntity = other;//??? } } } RestoreNPCGlobals(); }
void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_t dir, vec3_t point, int damage, int dflags, int mod ) { gclient_t *client; int take; int save; int asave; int knockback; int max; #ifdef MISSIONPACK vec3_t bouncedir, impactpoint; #endif if (!targ->takedamage) { return; } // the intermission has allready been qualified for, so don't // allow any extra scoring if ( level.intermissionQueued ) { return; } #ifdef MISSIONPACK if ( targ->client && mod != MOD_JUICED) { if ( targ->client->invulnerabilityTime > level.time) { if ( dir && point ) { G_InvulnerabilityEffect( targ, dir, point, impactpoint, bouncedir ); } return; } } #endif if ( !inflictor ) { inflictor = &g_entities[ENTITYNUM_WORLD]; } if ( !attacker ) { attacker = &g_entities[ENTITYNUM_WORLD]; } // shootable doors / buttons don't actually have any health if ( targ->s.eType == ET_MOVER ) { if ( targ->use && targ->moverState == MOVER_POS1 ) { targ->use( targ, inflictor, attacker ); } return; } #ifdef MISSIONPACK if( g_gametype.integer == GT_OBELISK && CheckObeliskAttack( targ, attacker ) ) { return; } #endif // reduce damage by the attacker's handicap value // unless they are rocket jumping if ( attacker->client && attacker != targ ) { max = attacker->client->ps.stats[STAT_MAX_HEALTH]; #ifdef MISSIONPACK if( bg_itemlist[attacker->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) { max /= 2; } #endif damage = damage * max / 100; } client = targ->client; if ( client ) { if ( client->noclip ) { return; } } if ( !dir ) { dflags |= DAMAGE_NO_KNOCKBACK; } else { VectorNormalize(dir); } knockback = damage; if ( knockback > 200 ) { knockback = 200; } if ( targ->flags & FL_NO_KNOCKBACK ) { knockback = 0; } if ( dflags & DAMAGE_NO_KNOCKBACK ) { knockback = 0; } // figure momentum add, even if the damage won't be taken if ( knockback && targ->client ) { vec3_t kvel; float mass; mass = 200; VectorScale (dir, g_knockback.value * (float)knockback / mass, kvel); VectorAdd (targ->client->ps.velocity, kvel, targ->client->ps.velocity); // set the timer so that the other client can't cancel // out the movement immediately if ( !targ->client->ps.pm_time ) { int t; t = knockback * 2; if ( t < 50 ) { t = 50; } if ( t > 200 ) { t = 200; } targ->client->ps.pm_time = t; targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; } } // check for completely getting out of the damage if ( !(dflags & DAMAGE_NO_PROTECTION) ) { // if TF_NO_FRIENDLY_FIRE is set, don't do damage to the target // if the attacker was on the same team #ifdef MISSIONPACK if ( mod != MOD_JUICED && targ != attacker && !(dflags & DAMAGE_NO_TEAM_PROTECTION) && OnSameTeam (targ, attacker) ) { #else if ( targ != attacker && OnSameTeam (targ, attacker) ) { #endif if ( !g_friendlyFire.integer ) { return; } } #ifdef MISSIONPACK if (mod == MOD_PROXIMITY_MINE) { if (inflictor && inflictor->parent && OnSameTeam(targ, inflictor->parent)) { return; } if (targ == attacker) { return; } } #endif // check for godmode if ( targ->flags & FL_GODMODE ) { return; } } // battlesuit protects from all radius damage (but takes knockback) // and protects 50% against all damage if ( client && client->ps.powerups[PW_BATTLESUIT] ) { G_AddEvent( targ, EV_POWERUP_BATTLESUIT, 0 ); if ( ( dflags & DAMAGE_RADIUS ) || ( mod == MOD_FALLING ) ) { return; } damage *= 0.5; } // add to the attacker's hit counter (if the target isn't a general entity like a prox mine) if ( attacker->client && client && targ != attacker && targ->health > 0 && targ->s.eType != ET_MISSILE && targ->s.eType != ET_GENERAL) { if ( OnSameTeam( targ, attacker ) ) { attacker->client->ps.persistant[PERS_HITS]--; } else { attacker->client->ps.persistant[PERS_HITS]++; } attacker->client->ps.persistant[PERS_ATTACKEE_ARMOR] = (targ->health<<8)|(client->ps.stats[STAT_ARMOR]); } // always give half damage if hurting self // calculated after knockback, so rocket jumping works if ( targ == attacker) { damage *= 0.5; } if ( damage < 1 ) { damage = 1; } take = damage; save = 0; // save some from armor asave = CheckArmor (targ, take, dflags); take -= asave; if ( g_debugDamage.integer ) { G_Printf( "%i: client:%i health:%i damage:%i armor:%i\n", level.time, targ->s.number, targ->health, take, asave ); } // add to the damage inflicted on a player this frame // the total will be turned into screen blends and view angle kicks // at the end of the frame if ( client ) { if ( attacker ) { client->ps.persistant[PERS_ATTACKER] = attacker->s.number; } else { client->ps.persistant[PERS_ATTACKER] = ENTITYNUM_WORLD; } client->damage_armor += asave; client->damage_blood += take; client->damage_knockback += knockback; if ( dir ) { VectorCopy ( dir, client->damage_from ); client->damage_fromWorld = qfalse; } else { VectorCopy ( targ->r.currentOrigin, client->damage_from ); client->damage_fromWorld = qtrue; } } // See if it's the player hurting the emeny flag carrier #ifdef MISSIONPACK if( g_gametype.integer == GT_CTF || g_gametype.integer == GT_1FCTF ) { #else if( g_gametype.integer == GT_CTF) { #endif Team_CheckHurtCarrier(targ, attacker); } if (targ->client) { // set the last client who damaged the target targ->client->lasthurt_client = attacker->s.number; targ->client->lasthurt_mod = mod; } // do the damage if (take) { targ->health = targ->health - take; if ( targ->client ) { targ->client->ps.stats[STAT_HEALTH] = targ->health; } if ( targ->health <= 0 ) { if ( client ) targ->flags |= FL_NO_KNOCKBACK; if (targ->health < -999) targ->health = -999; targ->enemy = attacker; targ->die (targ, inflictor, attacker, take, mod); return; } else if ( targ->pain ) { targ->pain (targ, attacker, take); } } } /* ============ CanDamage Returns qtrue if the inflictor can directly damage the target. Used for explosions and melee attacks. ============ */ qboolean CanDamage (gentity_t *targ, vec3_t origin) { vec3_t dest; trace_t tr; vec3_t midpoint; // use the midpoint of the bounds instead of the origin, because // bmodels may have their origin is 0,0,0 VectorAdd (targ->r.absmin, targ->r.absmax, midpoint); VectorScale (midpoint, 0.5, midpoint); VectorCopy (midpoint, dest); trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0 || tr.entityNum == targ->s.number) return qtrue; // this should probably check in the plane of projection, // rather than in world coordinate, and also include Z VectorCopy (midpoint, dest); dest[0] += 15.0; dest[1] += 15.0; trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) return qtrue; VectorCopy (midpoint, dest); dest[0] += 15.0; dest[1] -= 15.0; trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) return qtrue; VectorCopy (midpoint, dest); dest[0] -= 15.0; dest[1] += 15.0; trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) return qtrue; VectorCopy (midpoint, dest); dest[0] -= 15.0; dest[1] -= 15.0; trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) return qtrue; return qfalse; } /* ============ G_RadiusDamage ============ */ qboolean G_RadiusDamage ( vec3_t origin, gentity_t *attacker, float damage, float radius, gentity_t *ignore, int mod) { float points, dist; gentity_t *ent; int entityList[MAX_GENTITIES]; int numListedEntities; vec3_t mins, maxs; vec3_t v; vec3_t dir; int i, e; qboolean hitClient = qfalse; if ( radius < 1 ) { radius = 1; } for ( i = 0 ; i < 3 ; i++ ) { mins[i] = origin[i] - radius; maxs[i] = origin[i] + radius; } numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for ( e = 0 ; e < numListedEntities ; e++ ) { ent = &g_entities[entityList[ e ]]; if (ent == ignore) continue; if (!ent->takedamage) continue; // find the distance from the edge of the bounding box for ( i = 0 ; i < 3 ; i++ ) { if ( origin[i] < ent->r.absmin[i] ) { v[i] = ent->r.absmin[i] - origin[i]; } else if ( origin[i] > ent->r.absmax[i] ) { v[i] = origin[i] - ent->r.absmax[i]; } else { v[i] = 0; } } dist = VectorLength( v ); if ( dist >= radius ) { continue; } points = damage * ( 1.0 - dist / radius ); if( CanDamage (ent, origin) ) { if( LogAccuracyHit( ent, attacker ) ) { hitClient = qtrue; } VectorSubtract (ent->r.currentOrigin, origin, dir); // push the center of mass higher than the origin so players // get knocked into the air more dir[2] += 24; G_Damage (ent, NULL, attacker, dir, origin, (int)points, DAMAGE_RADIUS, mod); } } return hitClient; }