/* =============== RespawnItem =============== */ void RespawnItem( gentity_t *ent ) { // randomly select from teamed entities if (ent->team) { gentity_t *master; int count; int choice; if ( !ent->teammaster ) { G_Error( "RespawnItem: bad teammaster"); } master = ent->teammaster; for (count = 0, ent = master; ent; ent = ent->teamchain, count++) ; choice = Q_rand() % count; for (count = 0, ent = master; count < choice; ent = ent->teamchain, count++) ; } ent->r.contents = CONTENTS_TRIGGER; ent->s.eFlags &= ~EF_NODRAW; ent->r.svFlags &= ~SVF_NOCLIENT; trap_LinkEntity (ent); // play the normal respawn sound only to nearby clients G_AddEvent( ent, EV_ITEM_RESPAWN, 0 ); ent->nextthink = 0; }
void chick_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) { int n; // check for gib if (self->health <= self->gib_health) { gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0); for (n = 0; n < 2; n++) ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); for (n = 0; n < 4; n++) ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); self->deadflag = DEAD_DEAD; return; } if (self->deadflag == DEAD_DEAD) return; // regular death self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_YES; n = Q_rand() % 2; if (n == 0) { self->monsterinfo.currentmove = &chick_move_death1; gi.sound(self, CHAN_VOICE, sound_death1, 1, ATTN_NORM, 0); } else { self->monsterinfo.currentmove = &chick_move_death2; gi.sound(self, CHAN_VOICE, sound_death2, 1, ATTN_NORM, 0); } }
void ChickSlash(edict_t *self) { vec3_t aim; VectorSet(aim, MELEE_DISTANCE, self->mins[0], 10); gi.sound(self, CHAN_WEAPON, sound_melee_swing, 1, ATTN_NORM, 0); fire_hit(self, aim, (10 + (Q_rand() % 6)), 100); }
void GaldiatorMelee(edict_t *self) { vec3_t aim; VectorSet(aim, MELEE_DISTANCE, self->mins[0], -4); if (fire_hit(self, aim, (20 + (Q_rand() % 5)), 300)) gi.sound(self, CHAN_AUTO, sound_cleaver_hit, 1, ATTN_NORM, 0); else gi.sound(self, CHAN_AUTO, sound_cleaver_miss, 1, ATTN_NORM, 0); }
void SP_misc_banner(edict_t *ent) { ent->movetype = MOVETYPE_NONE; ent->solid = SOLID_NOT; ent->s.modelindex = gi.modelindex("models/objects/banner/tris.md2"); ent->s.frame = Q_rand() % 16; gi.linkentity(ent); ent->think = misc_banner_think; ent->nextthink = level.time + FRAMETIME; }
/* ====================== M_MoveToGoal ====================== */ void M_MoveToGoal(edict_t *ent, float dist) { edict_t *goal; goal = ent->goalentity; if (!ent->groundentity && !(ent->flags & (FL_FLY | FL_SWIM))) return; // if the next step hits the enemy, return immediately if (ent->enemy && SV_CloseEnough(ent, ent->enemy, dist)) return; // bump around... if ((Q_rand() & 3) == 1 || !SV_StepDirection(ent, ent->ideal_yaw, dist)) { if (ent->inuse) SV_NewChaseDir(ent, goal, dist); } }
static explosion_t *CL_PlainExplosion(void) { explosion_t *ex; ex = CL_AllocExplosion(); VectorCopy(te.pos1, ex->ent.origin); ex->type = ex_poly; ex->ent.flags = RF_FULLBRIGHT; ex->start = cl.servertime - CL_FRAMETIME; ex->light = 350; VectorSet(ex->lightcolor, 1.0f, 0.5f, 0.5f); ex->ent.angles[1] = Q_rand() % 360; ex->ent.model = cl_mod_explo4; if (frand() < 0.5f) ex->baseframe = 15; ex->frames = 15; return ex; }
gentity_t *SelectRandomTeamSpawnPoint( int teamstate, team_t team ) { gentity_t *spot; int count; int selection; gentity_t *spots[MAX_TEAM_SPAWN_POINTS]; char *classname; if (teamstate == TEAM_BEGIN) { if (team == TEAM_RED) classname = "team_CTF_redplayer"; else if (team == TEAM_BLUE) classname = "team_CTF_blueplayer"; else return NULL; } else { if (team == TEAM_RED) classname = "team_CTF_redspawn"; else if (team == TEAM_BLUE) classname = "team_CTF_bluespawn"; else return NULL; } count = 0; spot = NULL; while ((spot = G_Find (spot, FOFS(classname), classname)) != NULL) { if ( SpotWouldTelefrag( spot->s.origin ) ) { continue; } spots[ count ] = spot; if (++count == MAX_TEAM_SPAWN_POINTS) break; } if ( !count ) { // no spots that won't telefrag return G_Find( NULL, FOFS(classname), classname); } selection = Q_rand() % count; return spots[ selection ]; }
void ThrowClientHead(edict_t *self, int damage) { vec3_t vd; char *gibname; if (Q_rand() & 1) { gibname = "models/objects/gibs/head2/tris.md2"; self->s.skinnum = 1; // second skin is player } else { gibname = "models/objects/gibs/skull/tris.md2"; self->s.skinnum = 0; } self->s.origin[2] += 32; self->s.frame = 0; gi.setmodel(self, gibname); VectorSet(self->mins, -16, -16, 0); VectorSet(self->maxs, 16, 16, 16); self->takedamage = DAMAGE_NO; self->solid = SOLID_NOT; self->s.effects = EF_GIB; self->s.sound = 0; self->flags |= FL_NO_KNOCKBACK; self->movetype = MOVETYPE_BOUNCE; VelocityForDamage(damage, vd); VectorAdd(self->velocity, vd, self->velocity); if (self->client) { // bodies in the queue don't have a client anymore self->client->anim_priority = ANIM_DEATH; self->client->anim_end = self->s.frame; } else { self->think = NULL; self->nextthink = 0; } gi.linkentity(self); }
// an entity has just been parsed that has an event value static void entity_event(int number) { centity_t *cent = &cl_entities[number]; // EF_TELEPORTER acts like an event, but is not cleared each frame if ((cent->current.effects & EF_TELEPORTER) && CL_FRAMESYNC) { CL_TeleporterParticles(cent->current.origin); } #if USE_FPS if (cent->event_frame != cl.frame.number) return; #endif switch (cent->current.event) { case EV_ITEM_RESPAWN: S_StartSound(NULL, number, CHAN_WEAPON, S_RegisterSound("items/respawn1.wav"), 1, ATTN_IDLE, 0); CL_ItemRespawnParticles(cent->current.origin); break; case EV_PLAYER_TELEPORT: S_StartSound(NULL, number, CHAN_WEAPON, S_RegisterSound("misc/tele1.wav"), 1, ATTN_IDLE, 0); CL_TeleportParticles(cent->current.origin); break; case EV_FOOTSTEP: if (cl_footsteps->integer) S_StartSound(NULL, number, CHAN_BODY, cl_sfx_footsteps[Q_rand() & 3], 1, ATTN_NORM, 0); break; case EV_FALLSHORT: S_StartSound(NULL, number, CHAN_AUTO, S_RegisterSound("player/land1.wav"), 1, ATTN_NORM, 0); break; case EV_FALL: S_StartSound(NULL, number, CHAN_AUTO, S_RegisterSound("*fall2.wav"), 1, ATTN_NORM, 0); break; case EV_FALLFAR: S_StartSound(NULL, number, CHAN_AUTO, S_RegisterSound("*fall1.wav"), 1, ATTN_NORM, 0); break; } }
void SV_NewChaseDir(edict_t *actor, edict_t *enemy, float dist) { float deltax, deltay; float d[3]; float tdir, olddir, turnaround; //FIXME: how did we get here with no enemy if (!enemy) return; olddir = anglemod((int)(actor->ideal_yaw / 45) * 45); turnaround = anglemod(olddir - 180); deltax = enemy->s.origin[0] - actor->s.origin[0]; deltay = enemy->s.origin[1] - actor->s.origin[1]; if (deltax > 10) d[1] = 0; else if (deltax < -10) d[1] = 180; else d[1] = DI_NODIR; if (deltay < -10) d[2] = 270; else if (deltay > 10) d[2] = 90; else d[2] = DI_NODIR; // try direct route if (d[1] != DI_NODIR && d[2] != DI_NODIR) { if (d[1] == 0) tdir = d[2] == 90 ? 45 : 315; else tdir = d[2] == 90 ? 135 : 215; if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) return; } // try other directions if (((Q_rand() & 3) & 1) || fabsf(deltay) > fabsf(deltax)) { tdir = d[1]; d[1] = d[2]; d[2] = tdir; } if (d[1] != DI_NODIR && d[1] != turnaround && SV_StepDirection(actor, d[1], dist)) return; if (d[2] != DI_NODIR && d[2] != turnaround && SV_StepDirection(actor, d[2], dist)) return; /* there is no direct path to the player, so pick another direction */ if (olddir != DI_NODIR && SV_StepDirection(actor, olddir, dist)) return; if (Q_rand() & 1) { /*randomly determine direction of search*/ for (tdir = 0 ; tdir <= 315 ; tdir += 45) if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) return; } else { for (tdir = 315 ; tdir >= 0 ; tdir -= 45) if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) return; } if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist)) return; actor->ideal_yaw = olddir; // can't move // if a bridge was pulled out from underneath a monster, it may not have // a valid standing position at all if (!M_CheckBottom(actor)) SV_FixCheckBottom(actor); }
float Q_random( int *seed ) { return (Q_rand( seed ) & 0xffff) / (float)0x10000; }
/* =============== CL_AddPacketEntities =============== */ static void CL_AddPacketEntities(void) { entity_t ent; entity_state_t *s1; float autorotate; int i; int pnum; centity_t *cent; int autoanim; clientinfo_t *ci; unsigned int effects, renderfx; // bonus items rotate at a fixed rate autorotate = anglemod(cl.time * 0.1f); // brush models can auto animate their frames autoanim = 2 * cl.time / 1000; memset(&ent, 0, sizeof(ent)); for (pnum = 0; pnum < cl.frame.numEntities; pnum++) { i = (cl.frame.firstEntity + pnum) & PARSE_ENTITIES_MASK; s1 = &cl.entityStates[i]; cent = &cl_entities[s1->number]; effects = s1->effects; renderfx = s1->renderfx; // set frame if (effects & EF_ANIM01) ent.frame = autoanim & 1; else if (effects & EF_ANIM23) ent.frame = 2 + (autoanim & 1); else if (effects & EF_ANIM_ALL) ent.frame = autoanim; else if (effects & EF_ANIM_ALLFAST) ent.frame = cl.time / 100; else ent.frame = s1->frame; // quad and pent can do different things on client if (effects & EF_PENT) { effects &= ~EF_PENT; effects |= EF_COLOR_SHELL; renderfx |= RF_SHELL_RED; } if (effects & EF_QUAD) { effects &= ~EF_QUAD; effects |= EF_COLOR_SHELL; renderfx |= RF_SHELL_BLUE; } if (effects & EF_DOUBLE) { effects &= ~EF_DOUBLE; effects |= EF_COLOR_SHELL; renderfx |= RF_SHELL_DOUBLE; } if (effects & EF_HALF_DAMAGE) { effects &= ~EF_HALF_DAMAGE; effects |= EF_COLOR_SHELL; renderfx |= RF_SHELL_HALF_DAM; } // optionally remove the glowing effect if (cl_noglow->integer) renderfx &= ~RF_GLOW; ent.oldframe = cent->prev.frame; ent.backlerp = 1.0f - cl.lerpfrac; if (renderfx & RF_FRAMELERP) { // step origin discretely, because the frames // do the animation properly VectorCopy(cent->current.origin, ent.origin); VectorCopy(cent->current.old_origin, ent.oldorigin); // FIXME } else if (renderfx & RF_BEAM) { // interpolate start and end points for beams LerpVector(cent->prev.origin, cent->current.origin, cl.lerpfrac, ent.origin); LerpVector(cent->prev.old_origin, cent->current.old_origin, cl.lerpfrac, ent.oldorigin); } else { if (s1->number == cl.frame.clientNum + 1) { // use predicted origin VectorCopy(cl.playerEntityOrigin, ent.origin); VectorCopy(cl.playerEntityOrigin, ent.oldorigin); } else { // interpolate origin LerpVector(cent->prev.origin, cent->current.origin, cl.lerpfrac, ent.origin); VectorCopy(ent.origin, ent.oldorigin); } #if USE_FPS // run alias model animation if (cent->prev_frame != s1->frame) { int delta = cl.time - cent->anim_start; float frac; if (delta > BASE_FRAMETIME) { Com_DDDDPrintf("[%d] anim end %d: %d --> %d\n", cl.time, s1->number, cent->prev_frame, s1->frame); cent->prev_frame = s1->frame; frac = 1; } else if (delta > 0) { frac = delta * BASE_1_FRAMETIME; Com_DDDDPrintf("[%d] anim run %d: %d --> %d [%f]\n", cl.time, s1->number, cent->prev_frame, s1->frame, frac); } else { frac = 0; } ent.oldframe = cent->prev_frame; ent.backlerp = 1.0f - frac; } #endif } if ((effects & EF_GIB) && !cl_gibs->integer) { goto skip; } // create a new entity // tweak the color of beams if (renderfx & RF_BEAM) { // the four beam colors are encoded in 32 bits of skinnum (hack) ent.alpha = 0.30f; ent.skinnum = (s1->skinnum >> ((Q_rand() % 4) * 8)) & 0xff; ent.model = 0; } else { // set skin if (s1->modelindex == 255) {
/* * UI_Init */ void UI_Init( int vidWidth, int vidHeight, int protocol, int sharedSeed ) { m_active = NULL; m_cursoritem = NULL; m_drawfunc = NULL; m_keyfunc = NULL; m_entersound = qfalse; m_keypressed = 0; memset( &uis, 0, sizeof( uis ) ); uis.vidWidth = vidWidth; uis.vidHeight = vidHeight; uis.gameProtocol = protocol; #if 0 uis.scaleX = UI_WIDTHSCALE; uis.scaleY = UI_HEIGHTSCALE; #else uis.scaleX = 1; uis.scaleY = 1; #endif uis.cursorX = uis.vidWidth / 2; uis.cursorY = uis.vidHeight / 2; uis.initialSharedSeed = sharedSeed; uis.sharedSeed = uis.initialSharedSeed; uis.backgroundNum = Q_rand( &uis.sharedSeed ) % UI_SHADER_MAX_BACKGROUNDS; developer = trap_Cvar_Get( "developer", "0", CVAR_CHEAT ); // wsw/Mokshu : test svn addin for VS 2005 and what about a "help/news" menu (latest news from website for example) trap_Cmd_AddCommand( "menu_main", M_Menu_Main_f ); trap_Cmd_AddCommand( "menu_main_sbar", M_Menu_Main_Statusbar_f ); trap_Cmd_AddCommand( "menu_setup", M_Menu_Setup_f ); trap_Cmd_AddCommand( "menu_joinserver", M_Menu_JoinServer_f ); trap_Cmd_AddCommand( "menu_matchmaker", M_Menu_MatchMaker_f ); #ifdef AUTH_CODE trap_Cmd_AddCommand( "menu_login", M_Menu_Login_f ); trap_Cmd_AddCommand( "menu_register", M_Menu_Register_f ); #endif trap_Cmd_AddCommand( "menu_playerconfig", M_Menu_PlayerConfig_f ); trap_Cmd_AddCommand( "menu_startserver", M_Menu_StartServer_f ); trap_Cmd_AddCommand( "menu_sound", M_Menu_Sound_f ); trap_Cmd_AddCommand( "menu_options", M_Menu_Options_f ); trap_Cmd_AddCommand( "menu_performance", M_Menu_Performance_f ); trap_Cmd_AddCommand( "menu_performanceadv", M_Menu_PerformanceAdv_f ); trap_Cmd_AddCommand( "menu_keys", M_Menu_Keys_f ); trap_Cmd_AddCommand( "menu_vsays", M_Menu_Vsays_f ); trap_Cmd_AddCommand( "menu_quit", M_Menu_Quit_f ); trap_Cmd_AddCommand( "menu_reset", M_Menu_Reset_f ); trap_Cmd_AddCommand( "menu_demos", M_Menu_Demos_f ); trap_Cmd_AddCommand( "menu_mods", M_Menu_Mods_f ); trap_Cmd_AddCommand( "menu_game", M_Menu_Game_f ); trap_Cmd_AddCommand( "menu_tv", M_Menu_TV_f ); trap_Cmd_AddCommand( "menu_tv_channel_add", M_Menu_TV_ChannelAdd_f ); trap_Cmd_AddCommand( "menu_tv_channel_remove", M_Menu_TV_ChannelRemove_f ); trap_Cmd_AddCommand( "menu_failed", M_Menu_Failed_f ); trap_Cmd_AddCommand( "menu_msgbox", M_Menu_MsgBox_f ); trap_Cmd_AddCommand( "menu_custom", M_Menu_Custom_f ); trap_Cmd_AddCommand( "menu_chasecam", M_Menu_Chasecam_f ); trap_Cmd_AddCommand( "menu_teamconfig", M_Menu_TeamConfig_f ); trap_Cmd_AddCommand( "menu_force", UI_Force_f ); trap_Cmd_AddCommand( "menu_tutorials", M_Menu_Tutorials_f ); trap_Cmd_AddCommand( "menu_demoplay", M_Menu_Demoplay_f ); trap_Cmd_AddCommand( "menu_pop", Cmd_PopMenu_f ); M_Cache(); UI_Playermodel_Init(); // create a list with the available player models UI_InitTemporaryBoneposesCache(); uis.backGroundTrackStarted = qfalse; // jal: this is a small trick to assign userinfo flag to cg_oldMovement before cgame is loaded trap_Cvar_Get( "cg_oldMovement", "0", CVAR_USERINFO | CVAR_ARCHIVE ); trap_Cvar_Get( "cg_noAutohop", "0", CVAR_USERINFO | CVAR_ARCHIVE ); }
/* =============== P_DamageFeedback Handles color blends and view kicks =============== */ void P_DamageFeedback(edict_t *player) { gclient_t *client; float side; float realcount, count, kick; vec3_t v; int r, l; static vec3_t power_color = {0.0, 1.0, 0.0}; static vec3_t acolor = {1.0, 1.0, 1.0}; static vec3_t bcolor = {1.0, 0.0, 0.0}; client = player->client; // flash the backgrounds behind the status numbers client->ps.stats[STAT_FLASHES] = 0; if (client->damage_blood) client->ps.stats[STAT_FLASHES] |= 1; if (client->damage_armor && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum)) client->ps.stats[STAT_FLASHES] |= 2; // total points of damage shot at the player this frame count = (client->damage_blood + client->damage_armor + client->damage_parmor); if (count == 0) return; // didn't take any damage // start a pain animation if still in the player model if (client->anim_priority < ANIM_PAIN && player->s.modelindex == 255) { static int i; client->anim_priority = ANIM_PAIN; if (client->ps.pmove.pm_flags & PMF_DUCKED) { player->s.frame = FRAME_crpain1 - 1; client->anim_end = FRAME_crpain4; } else { i = (i + 1) % 3; switch (i) { case 0: player->s.frame = FRAME_pain101 - 1; client->anim_end = FRAME_pain104; break; case 1: player->s.frame = FRAME_pain201 - 1; client->anim_end = FRAME_pain204; break; case 2: player->s.frame = FRAME_pain301 - 1; client->anim_end = FRAME_pain304; break; } } } realcount = count; if (count < 10) count = 10; // always make a visible effect // play an apropriate pain sound if ((level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum)) { r = 1 + (Q_rand() & 1); player->pain_debounce_time = level.time + 0.7f; if (player->health < 25) l = 25; else if (player->health < 50) l = 50; else if (player->health < 75) l = 75; else l = 100; gi.sound(player, CHAN_VOICE, gi.soundindex(va("*pain%i_%i.wav", l, r)), 1, ATTN_NORM, 0); } // the total alpha of the blend is always proportional to count if (client->damage_alpha < 0) client->damage_alpha = 0; client->damage_alpha += count * 0.01f; if (client->damage_alpha < 0.2f) client->damage_alpha = 0.2f; if (client->damage_alpha > 0.6f) client->damage_alpha = 0.6f; // don't go too saturated // the color of the blend will vary based on how much was absorbed // by different armors VectorClear(v); if (client->damage_parmor) VectorMA(v, (float)client->damage_parmor / realcount, power_color, v); if (client->damage_armor) VectorMA(v, (float)client->damage_armor / realcount, acolor, v); if (client->damage_blood) VectorMA(v, (float)client->damage_blood / realcount, bcolor, v); VectorCopy(v, client->damage_blend); // // calculate view angle kicks // kick = abs(client->damage_knockback); if (kick && player->health > 0) { // kick of 0 means no view adjust at all kick = kick * 100 / player->health; if (kick < count * 0.5f) kick = count * 0.5f; if (kick > 50) kick = 50; VectorSubtract(client->damage_from, player->s.origin, v); VectorNormalize(v); side = DotProduct(v, right); client->v_dmg_roll = kick * side * 0.3f; side = -DotProduct(v, forward); client->v_dmg_pitch = kick * side * 0.3f; client->v_dmg_time = level.time + DAMAGE_TIME; } // // clear totals // client->damage_blood = 0; client->damage_armor = 0; client->damage_parmor = 0; client->damage_knockback = 0; }
/* ============= P_WorldEffects ============= */ void P_WorldEffects(void) { bool breather; bool envirosuit; int waterlevel, old_waterlevel; if (current_player->movetype == MOVETYPE_NOCLIP) { current_player->air_finished = level.time + 12; // don't need air return; } waterlevel = current_player->waterlevel; old_waterlevel = current_client->old_waterlevel; current_client->old_waterlevel = waterlevel; breather = current_client->breather_framenum > level.framenum; envirosuit = current_client->enviro_framenum > level.framenum; // // if just entered a water volume, play a sound // if (!old_waterlevel && waterlevel) { PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); if (current_player->watertype & CONTENTS_LAVA) gi.sound(current_player, CHAN_BODY, gi.soundindex("player/lava_in.wav"), 1, ATTN_NORM, 0); else if (current_player->watertype & CONTENTS_SLIME) gi.sound(current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); else if (current_player->watertype & CONTENTS_WATER) gi.sound(current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); current_player->flags |= FL_INWATER; // clear damage_debounce, so the pain sound will play immediately current_player->damage_debounce_time = level.time - 1; } // // if just completely exited a water volume, play a sound // if (old_waterlevel && ! waterlevel) { PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); gi.sound(current_player, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0); current_player->flags &= ~FL_INWATER; } // // check for head just going under water // if (old_waterlevel != 3 && waterlevel == 3) { gi.sound(current_player, CHAN_BODY, gi.soundindex("player/watr_un.wav"), 1, ATTN_NORM, 0); } // // check for head just coming out of water // if (old_waterlevel == 3 && waterlevel != 3) { if (current_player->air_finished < level.time) { // gasp for air gi.sound(current_player, CHAN_VOICE, gi.soundindex("player/gasp1.wav"), 1, ATTN_NORM, 0); PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); } else if (current_player->air_finished < level.time + 11) { // just break surface gi.sound(current_player, CHAN_VOICE, gi.soundindex("player/gasp2.wav"), 1, ATTN_NORM, 0); } } // // check for drowning // if (waterlevel == 3) { // breather or envirosuit give air if (breather || envirosuit) { current_player->air_finished = level.time + 10; if (((int)(current_client->breather_framenum - level.framenum) % 25) == 0) { if (!current_client->breather_sound) gi.sound(current_player, CHAN_AUTO, gi.soundindex("player/u_breath1.wav"), 1, ATTN_NORM, 0); else gi.sound(current_player, CHAN_AUTO, gi.soundindex("player/u_breath2.wav"), 1, ATTN_NORM, 0); current_client->breather_sound ^= 1; PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); //FIXME: release a bubble? } } // if out of air, start drowning if (current_player->air_finished < level.time) { // drown! if (current_player->client->next_drown_time < level.time && current_player->health > 0) { current_player->client->next_drown_time = level.time + 1; // take more damage the longer underwater current_player->dmg += 2; if (current_player->dmg > 15) current_player->dmg = 15; // play a gurp sound instead of a normal pain sound if (current_player->health <= current_player->dmg) gi.sound(current_player, CHAN_VOICE, gi.soundindex("player/drown1.wav"), 1, ATTN_NORM, 0); else if (Q_rand() & 1) gi.sound(current_player, CHAN_VOICE, gi.soundindex("*gurp1.wav"), 1, ATTN_NORM, 0); else gi.sound(current_player, CHAN_VOICE, gi.soundindex("*gurp2.wav"), 1, ATTN_NORM, 0); current_player->pain_debounce_time = level.time; T_Damage(current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, current_player->dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER); } } } else { current_player->air_finished = level.time + 12; current_player->dmg = 2; } // // check for sizzle damage // if (waterlevel && (current_player->watertype & (CONTENTS_LAVA | CONTENTS_SLIME))) { if (current_player->watertype & CONTENTS_LAVA) { if (current_player->health > 0 && current_player->pain_debounce_time <= level.time && current_client->invincible_framenum < level.framenum) { if (Q_rand() & 1) gi.sound(current_player, CHAN_VOICE, gi.soundindex("player/burn1.wav"), 1, ATTN_NORM, 0); else gi.sound(current_player, CHAN_VOICE, gi.soundindex("player/burn2.wav"), 1, ATTN_NORM, 0); current_player->pain_debounce_time = level.time + 1; } if (envirosuit) // take 1/3 damage with envirosuit T_Damage(current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1 * waterlevel, 0, 0, MOD_LAVA); else T_Damage(current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 3 * waterlevel, 0, 0, MOD_LAVA); } if (current_player->watertype & CONTENTS_SLIME) { if (!envirosuit) { // no damage from slime with envirosuit T_Damage(current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1 * waterlevel, 0, 0, MOD_SLIME); } } } }