static bool Pickup_Health( edict_t *other, const gsitem_t *item, int flags ) { if( !(flags & ITEM_IGNORE_MAX) ) if( HEALTH_TO_INT( other->health ) >= other->max_health ) return false; // start from at least 0.5, so the player sees his health increase the correct amount if( other->health < 0.5 ) other->health = 0.5; other->health += item->quantity; if( other->r.client ) { other->r.client->level.stats.health_taken += item->quantity; teamlist[other->s.team].stats.health_taken += item->quantity; } if( !(flags & ITEM_IGNORE_MAX) ) { if( other->health > other->max_health ) other->health = other->max_health; } else { if( other->health > 200 ) other->health = 200; } return true; }
static qboolean Pickup_Health( edict_t *ent, edict_t *other ) { if( !( ent->style & HEALTH_IGNORE_MAX ) ) if( HEALTH_TO_INT( other->health ) >= other->max_health ) return qfalse; // start from at least 0.5, so the player sees his health increase the correct amount if( other->health < 0.5 ) other->health = 0.5; other->health += ent->item->quantity; if( other->r.client ) { other->r.client->level.stats.health_taken += ent->item->quantity; teamlist[other->s.team].stats.health_taken += ent->item->quantity; } if( !( ent->style & HEALTH_IGNORE_MAX ) ) { if( other->health > other->max_health ) other->health = other->max_health; } else { if( other->health > 200 ) other->health = 200; } if( ent->style & HEALTH_TIMED ) ent->r.owner = other; return qtrue; }
static void Say_Team_Health( edict_t *who, char *buf, int buflen, const char *current_color ) { int health = HEALTH_TO_INT( who->health ); if( health <= 0 ) Q_snprintfz( buf, buflen, "%s0%s", S_COLOR_RED, current_color ); else if( health <= 50 ) Q_snprintfz( buf, buflen, "%s%i%s", S_COLOR_YELLOW, health, current_color ); else if( health <= 100 ) Q_snprintfz( buf, buflen, "%s%i%s", S_COLOR_WHITE, health, current_color ); else Q_snprintfz( buf, buflen, "%s%i%s", S_COLOR_GREEN, health, current_color ); }
void MegaHealth_think( edict_t *self ) { self->nextThink = level.time + 1; if( self->r.owner ) { if( self->r.owner->r.inuse && self->r.owner->s.team != TEAM_SPECTATOR && HEALTH_TO_INT( self->r.owner->health ) > self->r.owner->max_health ) { return; } // disable the link to the owner self->r.owner = NULL; } // player is back under max health so we can set respawn time for next MH if( !( self->spawnflags & ( DROPPED_ITEM | DROPPED_PLAYER_ITEM ) ) && G_Gametype_CanRespawnItem( self->item ) ) SetRespawn( self, G_Gametype_RespawnTimeForItem( self->item ) ); else G_FreeEdict( self ); }
/* * G_Teams_TDM_UpdateTeamInfoMessages */ void G_Teams_UpdateTeamInfoMessages( void ) { static int nexttime = 0; static char teammessage[MAX_STRING_CHARS]; edict_t *ent, *e; size_t len; int i, j, team; char entry[MAX_TOKEN_CHARS]; int locationTag; nexttime -= game.snapFrameTime; if( nexttime > 0 ) return; while( nexttime <= 0 ) nexttime += 2000; // time for a new update for( team = TEAM_ALPHA; team < GS_MAX_TEAMS; team++ ) { *teammessage = 0; Q_snprintfz( teammessage, sizeof( teammessage ), "ti \"" ); len = strlen( teammessage ); // add our team info to the string for( i = 0; i < teamlist[team].numplayers; i++ ) { ent = game.edicts + teamlist[team].playerIndices[i]; if( G_IsDead( ent ) ) // don't show dead players continue; if( ent->r.client->teamstate.is_coach ) // don't show coachs continue; // get location name locationTag = G_MapLocationTAGForOrigin( ent->s.origin ); if( locationTag == -1 ) continue; *entry = 0; Q_snprintfz( entry, sizeof( entry ), "%i %i %i %i ", PLAYERNUM( ent ), locationTag, HEALTH_TO_INT( ent->health ), ARMOR_TO_INT( ent->r.client->resp.armor ) ); if( MAX_STRING_CHARS - len > strlen( entry ) ) { Q_strncatz( teammessage, entry, sizeof( teammessage ) ); len = strlen( teammessage ); } } // add closing quote *entry = 0; Q_snprintfz( entry, sizeof( entry ), "\"" ); if( MAX_STRING_CHARS - len > strlen( entry ) ) { Q_strncatz( teammessage, entry, sizeof( teammessage ) ); len = strlen( teammessage ); } for( i = 0; i < teamlist[team].numplayers; i++ ) { ent = game.edicts + teamlist[team].playerIndices[i]; if( !ent->r.inuse || !ent->r.client ) continue; trap_GameCmd( ent, teammessage ); // see if there are spectators chasing this player and send them the layout too for( j = 0; j < teamlist[TEAM_SPECTATOR].numplayers; j++ ) { e = game.edicts + teamlist[TEAM_SPECTATOR].playerIndices[j]; if( !e->r.inuse || !e->r.client ) continue; if( e->r.client->resp.chase.active && e->r.client->resp.chase.target == ENTNUM( ent ) ) trap_GameCmd( e, teammessage ); } } } }
/* * G_EdictsAddSnapEffects * add effects based on accumulated info along the server frame */ static void G_SnapEntities( void ) { edict_t *ent; int i; vec3_t dir, origin; for( i = 0, ent = &game.edicts[0]; i < game.numentities; i++, ent++ ) { if( !ent->r.inuse || ( ent->r.svflags & SVF_NOCLIENT ) ) continue; if( ent->s.type == ET_PARTICLES ) // particles use a special configuration { ent->s.frame = ent->particlesInfo.speed; ent->s.modelindex = ent->particlesInfo.shaderIndex; ent->s.modelindex2 = ent->particlesInfo.spread; ent->s.counterNum = ent->particlesInfo.time; ent->s.weapon = ent->particlesInfo.frequency; ent->s.effects = ent->particlesInfo.size & 0xFF; if( ent->particlesInfo.spherical ) ent->s.effects |= ( 1<<8 ); if( ent->particlesInfo.bounce ) ent->s.effects |= ( 1<<9 ); if( ent->particlesInfo.gravity ) ent->s.effects |= ( 1<<10 ); if( ent->particlesInfo.expandEffect ) ent->s.effects |= ( 1<<11 ); if( ent->particlesInfo.shrinkEffect ) ent->s.effects |= ( 1<<11 ); GClip_LinkEntity( ent ); continue; } if( ent->s.type == ET_PLAYER || ent->s.type == ET_CORPSE ) { // this is pretty hackish. We exploit the fact that 0.5 servers *do not* transmit // origin2/old_origin for ET_PLAYER/ET_CORPSE entities, and we use it for sending the player velocity if( !G_ISGHOSTING( ent ) ) { ent->r.svflags |= SVF_TRANSMITORIGIN2; VectorCopy( ent->velocity, ent->s.origin2 ); } else ent->r.svflags &= ~SVF_TRANSMITORIGIN2; } if( ISEVENTENTITY( ent ) || G_ISGHOSTING( ent ) || !ent->takedamage ) continue; // types which can have accumulated damage effects if( ( ent->s.type == ET_GENERIC || ent->s.type == ET_PLAYER || ent->s.type == ET_CORPSE ) ) // doors don't bleed { // Until we get a proper damage saved effect, we accumulate both into the blood fx // so, at least, we don't send 2 entities where we can send one ent->snap.damage_taken += ent->snap.damage_saved; //ent->snap.damage_saved = 0; //spawn accumulated damage if( ent->snap.damage_taken && !( ent->flags & FL_GODMODE ) && HEALTH_TO_INT( ent->health ) > 0 ) { edict_t *event; float damage = ent->snap.damage_taken; if( damage > 120 ) damage = 120; VectorCopy( ent->snap.damage_dir, dir ); VectorNormalize( dir ); VectorAdd( ent->s.origin, ent->snap.damage_at, origin ); if( ent->s.type == ET_PLAYER || ent->s.type == ET_CORPSE ) { event = G_SpawnEvent( EV_BLOOD, DirToByte( dir ), origin ); event->s.damage = HEALTH_TO_INT( damage ); event->s.ownerNum = i; // set owner // ET_PLAYERS can also spawn sound events if( ent->s.type == ET_PLAYER ) { // play an apropriate pain sound if( level.time >= ent->pain_debounce_time ) { // see if it should pain for a FALL or for damage received if( ent->snap.damage_fall ) { ent->pain_debounce_time = level.time + 200; } else if( !G_IsDead( ent ) ) { if( ent->r.client->ps.inventory[POWERUP_SHELL] > 0 ) G_AddEvent( ent, EV_PAIN, PAIN_WARSHELL, true ); else if( ent->health <= 20 ) G_AddEvent( ent, EV_PAIN, PAIN_20, true ); else if( ent->health <= 35 ) G_AddEvent( ent, EV_PAIN, PAIN_30, true ); else if( ent->health <= 60 ) G_AddEvent( ent, EV_PAIN, PAIN_60, true ); else G_AddEvent( ent, EV_PAIN, PAIN_100, true ); ent->pain_debounce_time = level.time + 400; } } } } else { event = G_SpawnEvent( EV_SPARKS, DirToByte( dir ), origin ); event->s.damage = HEALTH_TO_INT( damage ); } } } } }
/* * G_SetClientStats */ void G_SetClientStats( edict_t *ent ) { gclient_t *client = ent->r.client; int team, i; if( ent->r.client->resp.chase.active ) // in chasecam it copies the other player stats return; // // layouts // client->ps.stats[STAT_LAYOUTS] = 0; // don't force scoreboard when dead during timeout if( ent->r.client->level.showscores || GS_MatchState() >= MATCH_STATE_POSTMATCH ) client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_SCOREBOARD; if( GS_TeamBasedGametype() && !GS_InvidualGameType() ) client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_TEAMTAB; if( GS_HasChallengers() && ent->r.client->queueTimeStamp ) client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_CHALLENGER; if( GS_MatchState() <= MATCH_STATE_WARMUP && level.ready[PLAYERNUM( ent )] ) client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_READY; if( G_SpawnQueue_GetSystem( ent->s.team ) == SPAWNSYSTEM_INSTANT ) client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_INSTANTRESPAWN; // // team // client->ps.stats[STAT_TEAM] = client->ps.stats[STAT_REALTEAM] = ent->s.team; // // health // if( ent->s.team == TEAM_SPECTATOR ) client->ps.stats[STAT_HEALTH] = STAT_NOTSET; // no health for spectator else client->ps.stats[STAT_HEALTH] = HEALTH_TO_INT( ent->health ); client->r.frags = client->ps.stats[STAT_SCORE]; // // armor // if( GS_Instagib() ) { if( g_instashield->integer ) client->ps.stats[STAT_ARMOR] = ARMOR_TO_INT( 100.0f * ( client->resp.instashieldCharge / INSTA_SHIELD_MAX ) ); else client->ps.stats[STAT_ARMOR] = 0; } else client->ps.stats[STAT_ARMOR] = ARMOR_TO_INT( client->resp.armor ); // // pickup message // if( level.time > client->resp.pickup_msg_time ) { client->ps.stats[STAT_PICKUP_ITEM] = 0; } // // frags // if( ent->s.team == TEAM_SPECTATOR ) { client->ps.stats[STAT_SCORE] = STAT_NOTSET; // no frags for spectators } else { client->ps.stats[STAT_SCORE] = ent->r.client->level.stats.score; } // // Team scores // if( GS_TeamBasedGametype() ) { // team based i = 0; for( team = TEAM_ALPHA; team < GS_MAX_TEAMS; team++ ) { client->ps.stats[STAT_TEAM_ALPHA_SCORE+i] = teamlist[team].stats.score; i++; } // mark the rest as not set for(; team < GS_MAX_TEAMS; team++ ) { client->ps.stats[STAT_TEAM_ALPHA_SCORE+i] = STAT_NOTSET; i++; } } else { // not team based i = 0; for( team = TEAM_ALPHA; team < GS_MAX_TEAMS; team++ ) { client->ps.stats[STAT_TEAM_ALPHA_SCORE+i] = STAT_NOTSET; i++; } } // spawn system client->ps.stats[STAT_NEXT_RESPAWN] = ceil( G_SpawnQueue_NextRespawnTime( client->team ) * 0.001f ); // pointed player client->ps.stats[STAT_POINTED_TEAMPLAYER] = 0; client->ps.stats[STAT_POINTED_PLAYER] = G_FindPointedPlayer( ent ); if( client->ps.stats[STAT_POINTED_PLAYER] && GS_TeamBasedGametype() ) { edict_t *e = &game.edicts[client->ps.stats[STAT_POINTED_PLAYER]]; if( e->s.team == ent->s.team ) { int pointedhealth = HEALTH_TO_INT( e->health ); int pointedarmor = 0; int available_bits = 0; bool mega = false; if( pointedhealth < 0 ) pointedhealth = 0; if( pointedhealth > 100 ) { pointedhealth -= 100; mega = true; if( pointedhealth > 100 ) pointedhealth = 100; } pointedhealth /= 3.2; if( GS_Armor_TagForCount( e->r.client->resp.armor ) ) { pointedarmor = ARMOR_TO_INT( e->r.client->resp.armor ); } if( pointedarmor > 150 ) { pointedarmor = 150; } pointedarmor /= 5; client->ps.stats[STAT_POINTED_TEAMPLAYER] = ( ( pointedhealth &0x1F )|( pointedarmor&0x3F )<<6|( available_bits&0xF )<<12 ); if( mega ) { client->ps.stats[STAT_POINTED_TEAMPLAYER] |= 0x20; } } } // last killer. ignore world and team kills if( client->teamstate.last_killer ) { edict_t *targ = ent, *attacker = client->teamstate.last_killer; client->ps.stats[STAT_LAST_KILLER] = (attacker->r.client && !GS_IsTeamDamage( &targ->s, &attacker->s ) ? ENTNUM( attacker ) : 0); } else { client->ps.stats[STAT_LAST_KILLER] = 0; } }
/* * G_Damage * targ entity that is being damaged * inflictor entity that is causing the damage * attacker entity that caused the inflictor to damage targ * example: targ=enemy, inflictor=rocket, attacker=player * * dir direction of the attack * point point at which the damage is being inflicted * normal normal vector from that point * damage amount of damage being inflicted * knockback force to be applied against targ as a result of the damage * * dflags these flags are used to control how T_Damage works */ void G_Damage( edict_t *targ, edict_t *inflictor, edict_t *attacker, const vec3_t pushdir, const vec3_t dmgdir, const vec3_t point, float damage, float knockback, float stun, int dflags, int mod ) { gclient_t *client; float take; float save; float asave; qboolean statDmg; if( !targ || !targ->takedamage ) return; if( !attacker ) { attacker = world; mod = MOD_TRIGGER_HURT; } meansOfDeath = mod; client = targ->r.client; // Cgg - race mode: players don't interact with one another if( GS_RaceGametype() ) { if( attacker->r.client && targ->r.client && attacker != targ ) return; } // push if( !( dflags & DAMAGE_NO_KNOCKBACK ) ) G_KnockBackPush( targ, attacker, pushdir, knockback, dflags ); // stun if( g_allow_stun->integer && !( dflags & (DAMAGE_NO_STUN|FL_GODMODE) ) && (int)stun > 0 && targ->r.client && targ->r.client->resp.takeStun && !GS_IsTeamDamage( &targ->s, &attacker->s ) && ( targ != attacker ) ) { if( dflags & DAMAGE_STUN_CLAMP ) { if( targ->r.client->ps.pmove.stats[PM_STAT_STUN] < (int)stun ) targ->r.client->ps.pmove.stats[PM_STAT_STUN] = (int)stun; } else targ->r.client->ps.pmove.stats[PM_STAT_STUN] += (int)stun; clamp( targ->r.client->ps.pmove.stats[PM_STAT_STUN], 0, MAX_STUN_TIME ); } // dont count self-damage cause it just adds the same to both stats statDmg = ( attacker != targ ) && ( mod != MOD_TELEFRAG ); // apply handicap on the damage given if( statDmg && attacker->r.client && !GS_Instagib() ) { // handicap is a percentage value if( attacker->r.client->handicap != 0 ) damage *= 1.0 - (attacker->r.client->handicap * 0.01f); } take = damage; save = 0; // check for cases where damage is protected if( !( dflags & DAMAGE_NO_PROTECTION ) ) { // check for godmode if( targ->flags & FL_GODMODE ) { take = 0; save = damage; } // never damage in timeout else if( GS_MatchPaused() ) { take = save = 0; } // ca has self splash damage disabled else if( ( dflags & DAMAGE_RADIUS ) && attacker == targ && !GS_SelfDamage() ) { take = save = 0; } // don't get damage from players in race else if( ( GS_RaceGametype() ) && attacker->r.client && targ->r.client && ( attacker->r.client != targ->r.client ) ) { take = save = 0; } // team damage avoidance else if( GS_IsTeamDamage( &targ->s, &attacker->s ) && !G_Gametype_CanTeamDamage( dflags ) ) { take = save = 0; } // apply warShell powerup protection else if( targ->r.client && targ->r.client->ps.inventory[POWERUP_SHELL] > 0 ) { // warshell offers full protection in instagib if( GS_Instagib() ) { take = 0; save = damage; } else { take = ( damage * 0.25f ); save = damage - take; } // todo : add protection sound } } asave = G_CheckArmor( targ, take, dflags ); take -= asave; //treat cheat/powerup savings the same as armor asave += save; // APPLY THE DAMAGES if( !take && !asave ) return; // do the damage if( take <= 0 ) return; // adding damage given/received to stats if( statDmg && attacker->r.client && !targ->deadflag && targ->movetype != MOVETYPE_PUSH && targ->s.type != ET_CORPSE ) { attacker->r.client->level.stats.total_damage_given += take + asave; teamlist[attacker->s.team].stats.total_damage_given += take + asave; if( GS_IsTeamDamage( &targ->s, &attacker->s ) ) { attacker->r.client->level.stats.total_teamdamage_given += take + asave; teamlist[attacker->s.team].stats.total_teamdamage_given += take + asave; } } G_Gametype_ScoreEvent( attacker->r.client, "dmg", va( "%i %f %i", targ->s.number, damage, attacker->s.number ) ); if( statDmg && client ) { client->level.stats.total_damage_received += take + asave; teamlist[targ->s.team].stats.total_damage_received += take + asave; if( GS_IsTeamDamage( &targ->s, &attacker->s ) ) { client->level.stats.total_teamdamage_received += take + asave; teamlist[targ->s.team].stats.total_teamdamage_received += take + asave; } } // accumulate received damage for snapshot effects { vec3_t dorigin; if( inflictor == world && mod == MOD_FALLING ) // it's fall damage targ->snap.damage_fall += take + save; if( point[0] != 0.0f || point[1] != 0.0f || point[2] != 0.0f ) VectorCopy( point, dorigin ); else VectorSet( dorigin, targ->s.origin[0], targ->s.origin[1], targ->s.origin[2] + targ->viewheight ); G_BlendFrameDamage( targ, take, &targ->snap.damage_taken, dorigin, dmgdir, targ->snap.damage_at, targ->snap.damage_dir ); G_BlendFrameDamage( targ, save, &targ->snap.damage_saved, dorigin, dmgdir, targ->snap.damage_at, targ->snap.damage_dir ); if( targ->r.client ) { if( mod != MOD_FALLING && mod != MOD_TELEFRAG && mod != MOD_SUICIDE ) { if( inflictor == world || attacker == world ) { // for world inflicted damage use always 'frontal' G_ClientAddDamageIndicatorImpact( targ->r.client, take + save, NULL ); } else if( dflags & DAMAGE_RADIUS ) { // for splash hits the direction is from the inflictor origin G_ClientAddDamageIndicatorImpact( targ->r.client, take + save, pushdir ); } else { // for direct hits the direction is the projectile direction G_ClientAddDamageIndicatorImpact( targ->r.client, take + save, dmgdir ); } } } } targ->health = targ->health - take; // add damage done to stats if( !GS_IsTeamDamage( &targ->s, &attacker->s ) && statDmg && G_ModToAmmo( mod ) != AMMO_NONE && client && attacker->r.client ) { attacker->r.client->level.stats.accuracy_hits[G_ModToAmmo( mod )-AMMO_GUNBLADE]++; attacker->r.client->level.stats.accuracy_damage[G_ModToAmmo( mod )-AMMO_GUNBLADE] += damage; teamlist[attacker->s.team].stats.accuracy_hits[G_ModToAmmo( mod )-AMMO_GUNBLADE]++; teamlist[attacker->s.team].stats.accuracy_damage[G_ModToAmmo( mod )-AMMO_GUNBLADE] += damage; G_AwardPlayerHit( targ, attacker, mod ); } // accumulate given damage for hit sounds if( ( take || asave ) && targ != attacker && client && !targ->deadflag ) { if( attacker ) { if( GS_IsTeamDamage( &targ->s, &attacker->s ) ) attacker->snap.damageteam_given += take + asave; // we want to know how good our hit was, so saved also matters else attacker->snap.damage_given += take + asave; } } if( G_IsDead( targ ) ) { if( client ) targ->flags |= FL_NO_KNOCKBACK; G_Killed( targ, inflictor, attacker, HEALTH_TO_INT( take ), point, mod ); } else { G_CallPain( targ, attacker, knockback, take ); } }
/* * G_PlayerWorldEffects */ static void G_PlayerWorldEffects( edict_t *ent ) { int waterlevel, old_waterlevel; int watertype, old_watertype; if( ent->movetype == MOVETYPE_NOCLIP ) { ent->air_finished = level.time + ( 12*1000 ); // don't need air return; } waterlevel = ent->waterlevel; watertype = ent->watertype; old_waterlevel = ent->r.client->resp.old_waterlevel; old_watertype = ent->r.client->resp.old_watertype; ent->r.client->resp.old_waterlevel = waterlevel; ent->r.client->resp.old_watertype = watertype; // // if just entered a water volume, play a sound // if( !old_waterlevel && waterlevel ) { if( ent->watertype & CONTENTS_LAVA ) G_Sound( ent, CHAN_AUTO, trap_SoundIndex( S_WORLD_LAVA_IN ), ATTN_NORM ); else if( ent->watertype & CONTENTS_SLIME ) G_Sound( ent, CHAN_AUTO, trap_SoundIndex( S_WORLD_SLIME_IN ), ATTN_NORM ); else if( ent->watertype & CONTENTS_WATER ) G_Sound( ent, CHAN_AUTO, trap_SoundIndex( S_WORLD_WATER_IN ), ATTN_NORM ); ent->flags |= FL_INWATER; } // // if just completely exited a water volume, play a sound // if( old_waterlevel && !waterlevel ) { if( old_watertype & CONTENTS_LAVA ) G_Sound( ent, CHAN_AUTO, trap_SoundIndex( S_WORLD_LAVA_OUT ), ATTN_NORM ); else if( old_watertype & CONTENTS_SLIME ) G_Sound( ent, CHAN_AUTO, trap_SoundIndex( S_WORLD_SLIME_OUT ), ATTN_NORM ); else if( old_watertype & CONTENTS_WATER ) G_Sound( ent, CHAN_AUTO, trap_SoundIndex( S_WORLD_WATER_OUT ), ATTN_NORM ); ent->flags &= ~FL_INWATER; } // // check for head just coming out of water // if( old_waterlevel == 3 && waterlevel != 3 ) { if( ent->air_finished < level.time ) { // gasp for air // wsw : jal : todo : better variations of gasp sounds G_AddEvent( ent, EV_SEXEDSOUND, 1, true ); } else if( ent->air_finished < level.time + 11000 ) { // just break surface // wsw : jal : todo : better variations of gasp sounds G_AddEvent( ent, EV_SEXEDSOUND, 2, true ); } } // // check for drowning // if( waterlevel == 3 ) { // if out of air, start drowning if( ent->air_finished < level.time ) { // drown! if( ent->r.client->resp.next_drown_time < level.time && !G_IsDead( ent ) ) { ent->r.client->resp.next_drown_time = level.time + 1000; // take more damage the longer underwater ent->r.client->resp.drowningDamage += 2; if( ent->r.client->resp.drowningDamage > 15 ) ent->r.client->resp.drowningDamage = 15; // wsw : jal : todo : better variations of gasp sounds // play a gurp sound instead of a normal pain sound if( HEALTH_TO_INT( ent->health ) - ent->r.client->resp.drowningDamage <= 0 ) G_AddEvent( ent, EV_SEXEDSOUND, 2, true ); else G_AddEvent( ent, EV_SEXEDSOUND, 1, true ); ent->pain_debounce_time = level.time; G_Damage( ent, world, world, vec3_origin, vec3_origin, ent->s.origin, ent->r.client->resp.drowningDamage, 0, 0, DAMAGE_NO_ARMOR, MOD_WATER ); } } } else { ent->air_finished = level.time + 12000; ent->r.client->resp.drowningDamage = 2; } // // check for sizzle damage // if( waterlevel && ( ent->watertype & ( CONTENTS_LAVA|CONTENTS_SLIME ) ) ) { if( ent->watertype & CONTENTS_LAVA ) { // wsw: Medar: We don't have the sounds yet and this seems to overwrite the normal pain sounds //if( !G_IsDead(ent) && ent->pain_debounce_time <= level.time ) //{ // G_Sound( ent, CHAN_BODY, trap_SoundIndex(va(S_PLAYER_BURN_1_to_2, (rand()&1)+1)), 1, ATTN_NORM ); // ent->pain_debounce_time = level.time + 1000; //} G_Damage( ent, world, world, vec3_origin, vec3_origin, ent->s.origin, ( 30 * waterlevel ) * game.snapFrameTime / 1000.0f, 0, 0, 0, MOD_LAVA ); } if( ent->watertype & CONTENTS_SLIME ) { G_Damage( ent, world, world, vec3_origin, vec3_origin, ent->s.origin, ( 10 * waterlevel ) * game.snapFrameTime / 1000.0f, 0, 0, 0, MOD_SLIME ); } } }