123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557 |
- // Copyright (c) ZeniMax Media Inc.
- // Licensed under the GNU General Public License 2.0.
- #include "g_local.h"
- #include "m_player.h"
- #include "bots/bot_includes.h"
- static edict_t *current_player;
- static gclient_t *current_client;
- static vec3_t forward, right, up;
- float xyspeed;
- float bobmove;
- int bobcycle, bobcycle_run; // odd cycles are right foot going forward
- float bobfracsin; // sinf(bobfrac*M_PI)
- /*
- ===============
- SkipViewModifiers
- ===============
- */
- inline bool SkipViewModifiers() {
- if ( g_skipViewModifiers->integer && sv_cheats->integer ) {
- return true;
- }
- // don't do bobbing, etc on grapple
- if (current_client->ctf_grapple &&
- current_client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY) {
- return true;
- }
- // spectator mode
- if (current_client->resp.spectator || (G_TeamplayEnabled() && current_client->resp.ctf_team == CTF_NOTEAM)) {
- return true;
- }
- return false;
- }
- /*
- ===============
- SV_CalcRoll
- ===============
- */
- float SV_CalcRoll(const vec3_t &angles, const vec3_t &velocity)
- {
- if ( SkipViewModifiers() ) {
- return 0.0f;
- }
- float sign;
- float side;
- float value;
- side = velocity.dot(right);
- sign = side < 0 ? -1.f : 1.f;
- side = fabsf(side);
- value = sv_rollangle->value;
- if (side < sv_rollspeed->value)
- side = side * value / sv_rollspeed->value;
- else
- side = value;
- return side * sign;
- }
- /*
- ===============
- 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 l;
- constexpr vec3_t armor_color = { 1.0, 1.0, 1.0 };
- constexpr vec3_t power_color = { 0.0, 1.0, 0.0 };
- constexpr vec3_t bcolor = { 1.0, 0.0, 0.0 };
- client = player->client;
- // flash the backgrounds behind the status numbers
- int16_t want_flashes = 0;
- if (client->damage_blood)
- want_flashes |= 1;
- if (client->damage_armor && !(player->flags & FL_GODMODE) && (client->invincible_time <= level.time))
- want_flashes |= 2;
- if (want_flashes)
- {
- client->flash_time = level.time + 100_ms;
- client->ps.stats[STAT_FLASHES] = want_flashes;
- }
- else if (client->flash_time < level.time)
- client->ps.stats[STAT_FLASHES] = 0;
- // total points of damage shot at the player this frame
- count = (float) (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 == MODELINDEX_PLAYER)
- {
- 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;
- }
- }
- client->anim_time = 0_ms;
- }
- realcount = count;
- // if we took health damage, do a minimum clamp
- if (client->damage_blood)
- {
- if (count < 10)
- count = 10; // always make a visible effect
- }
- else
- {
- if (count > 2)
- count = 2; // don't go too deep
- }
- // play an appropriate pain sound
- if ((level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) && (client->invincible_time <= level.time))
- {
- player->pain_debounce_time = level.time + 700_ms;
- constexpr const char *pain_sounds[] = {
- "*pain25_1.wav",
- "*pain25_2.wav",
- "*pain50_1.wav",
- "*pain50_2.wav",
- "*pain75_1.wav",
- "*pain75_2.wav",
- "*pain100_1.wav",
- "*pain100_2.wav",
- };
- if (player->health < 25)
- l = 0;
- else if (player->health < 50)
- l = 2;
- else if (player->health < 75)
- l = 4;
- else
- l = 6;
- if (brandom())
- l |= 1;
- gi.sound(player, CHAN_VOICE, gi.soundindex(pain_sounds[l]), 1, ATTN_NORM, 0);
- // Paril: pain noises alert monsters
- PlayerNoise(player, player->s.origin, PNOISE_SELF);
- }
- // the total alpha of the blend is always proportional to count
- if (client->damage_alpha < 0)
- client->damage_alpha = 0;
- // [Paril-KEX] tweak the values to rely less on this
- // and more on damage indicators
- if (client->damage_blood || (client->damage_alpha + count * 0.06f) < 0.15f)
- {
- client->damage_alpha += count * 0.06f;
- if (client->damage_alpha < 0.06f)
- client->damage_alpha = 0.06f;
- if (client->damage_alpha > 0.4f)
- client->damage_alpha = 0.4f; // don't go too saturated
- }
- // mix in colors
- v = {};
- if (client->damage_parmor)
- v += power_color * (client->damage_parmor / realcount);
- if (client->damage_blood)
- v += bcolor * max(15.0f, (client->damage_blood / realcount));
- if (client->damage_armor)
- v += armor_color * (client->damage_armor / realcount);
- client->damage_blend = v.normalized();
- //
- // calculate view angle kicks
- //
- kick = (float) 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;
- v = client->damage_from - player->s.origin;
- v.normalize();
- side = v.dot(right);
- client->v_dmg_roll = kick * side * 0.3f;
- side = -v.dot(forward);
- client->v_dmg_pitch = kick * side * 0.3f;
- client->v_dmg_time = level.time + DAMAGE_TIME();
- }
- // [Paril-KEX] send view indicators
- if (client->num_damage_indicators)
- {
- gi.WriteByte(svc_damage);
- gi.WriteByte(client->num_damage_indicators);
- for (uint8_t i = 0; i < client->num_damage_indicators; i++)
- {
- auto &indicator = client->damage_indicators[i];
- // encode total damage into 5 bits
- uint8_t encoded = std::clamp((indicator.health + indicator.power + indicator.armor) / 3, 1, 0x1F);
- // encode types in the latter 3 bits
- if (indicator.health)
- encoded |= 0x20;
- if (indicator.armor)
- encoded |= 0x40;
- if (indicator.power)
- encoded |= 0x80;
- gi.WriteByte(encoded);
- gi.WriteDir((player->s.origin - indicator.from).normalized());
- }
- gi.unicast(player, false);
- }
- //
- // clear totals
- //
- client->damage_blood = 0;
- client->damage_armor = 0;
- client->damage_parmor = 0;
- client->damage_knockback = 0;
- client->num_damage_indicators = 0;
- }
- /*
- ===============
- SV_CalcViewOffset
- Auto pitching on slopes?
- fall from 128: 400 = 160000
- fall from 256: 580 = 336400
- fall from 384: 720 = 518400
- fall from 512: 800 = 640000
- fall from 640: 960 =
- damage = deltavelocity*deltavelocity * 0.0001
- ===============
- */
- void SV_CalcViewOffset(edict_t *ent)
- {
- float bob;
- float ratio;
- float delta;
- vec3_t v;
- //===================================
- // base angles
- vec3_t &angles = ent->client->ps.kick_angles;
- // if dead, fix the angle and don't add any kick
- if (ent->deadflag && !ent->client->resp.spectator)
- {
- angles = {};
- if (ent->flags & FL_SAM_RAIMI)
- {
- ent->client->ps.viewangles[ROLL] = 0;
- ent->client->ps.viewangles[PITCH] = 0;
- }
- else
- {
- ent->client->ps.viewangles[ROLL] = 40;
- ent->client->ps.viewangles[PITCH] = -15;
- }
- ent->client->ps.viewangles[YAW] = ent->client->killer_yaw;
- }
- else if (!ent->client->pers.bob_skip && !SkipViewModifiers())
- {
- // add angles based on weapon kick
- angles = P_CurrentKickAngles(ent);
- // add angles based on damage kick
- if (ent->client->v_dmg_time > level.time)
- {
- // [Paril-KEX] 100ms of slack is added to account for
- // visual difference in higher tickrates
- gtime_t diff = ent->client->v_dmg_time - level.time;
- // slack time remaining
- if (DAMAGE_TIME_SLACK())
- {
- if (diff > DAMAGE_TIME() - DAMAGE_TIME_SLACK())
- ratio = (DAMAGE_TIME() - diff).seconds() / DAMAGE_TIME_SLACK().seconds();
- else
- ratio = diff.seconds() / (DAMAGE_TIME() - DAMAGE_TIME_SLACK()).seconds();
- }
- else
- ratio = diff.seconds() / (DAMAGE_TIME() - DAMAGE_TIME_SLACK()).seconds();
- angles[PITCH] += ratio * ent->client->v_dmg_pitch;
- angles[ROLL] += ratio * ent->client->v_dmg_roll;
- }
- // add pitch based on fall kick
- if (ent->client->fall_time > level.time)
- {
- // [Paril-KEX] 100ms of slack is added to account for
- // visual difference in higher tickrates
- gtime_t diff = ent->client->fall_time - level.time;
- // slack time remaining
- if (DAMAGE_TIME_SLACK())
- {
- if (diff > FALL_TIME() - DAMAGE_TIME_SLACK())
- ratio = (FALL_TIME() - diff).seconds() / DAMAGE_TIME_SLACK().seconds();
- else
- ratio = diff.seconds() / (FALL_TIME() - DAMAGE_TIME_SLACK()).seconds();
- }
- else
- ratio = diff.seconds() / (FALL_TIME() - DAMAGE_TIME_SLACK()).seconds();
- angles[PITCH] += ratio * ent->client->fall_value;
- }
- // add angles based on velocity
- if (!ent->client->pers.bob_skip && !SkipViewModifiers())
- {
- delta = ent->velocity.dot(forward);
- angles[PITCH] += delta * run_pitch->value;
- delta = ent->velocity.dot(right);
- angles[ROLL] += delta * run_roll->value;
- // add angles based on bob
- delta = bobfracsin * bob_pitch->value * xyspeed;
- if ((ent->client->ps.pmove.pm_flags & PMF_DUCKED) && ent->groundentity)
- delta *= 6; // crouching
- delta = min(delta, 1.2f);
- angles[PITCH] += delta;
- delta = bobfracsin * bob_roll->value * xyspeed;
- if ((ent->client->ps.pmove.pm_flags & PMF_DUCKED) && ent->groundentity)
- delta *= 6; // crouching
- delta = min(delta, 1.2f);
- if (bobcycle & 1)
- delta = -delta;
- angles[ROLL] += delta;
- }
- // add earthquake angles
- if (ent->client->quake_time > level.time)
- {
- float factor = min(1.0f, (ent->client->quake_time.seconds() / level.time.seconds()) * 0.25f);
- angles.x += crandom_open() * factor;
- angles.z += crandom_open() * factor;
- angles.y += crandom_open() * factor;
- }
- }
- // [Paril-KEX] clamp angles
- for (int i = 0; i < 3; i++)
- angles[i] = clamp(angles[i], -31.f, 31.f);
- //===================================
- // base origin
- v = {};
- // add fall height
- if (!ent->client->pers.bob_skip && !SkipViewModifiers())
- {
- if (ent->client->fall_time > level.time)
- {
- // [Paril-KEX] 100ms of slack is added to account for
- // visual difference in higher tickrates
- gtime_t diff = ent->client->fall_time - level.time;
- // slack time remaining
- if (DAMAGE_TIME_SLACK())
- {
- if (diff > FALL_TIME() - DAMAGE_TIME_SLACK())
- ratio = (FALL_TIME() - diff).seconds() / DAMAGE_TIME_SLACK().seconds();
- else
- ratio = diff.seconds() / (FALL_TIME() - DAMAGE_TIME_SLACK()).seconds();
- }
- else
- ratio = diff.seconds() / (FALL_TIME() - DAMAGE_TIME_SLACK()).seconds();
- v[2] -= ratio * ent->client->fall_value * 0.4f;
- }
- // add bob height
- bob = bobfracsin * xyspeed * bob_up->value;
- if (bob > 6)
- bob = 6;
- // gi.DebugGraph (bob *2, 255);
- v[2] += bob;
- }
- // add kick offset
- if (!ent->client->pers.bob_skip && !SkipViewModifiers())
- v += P_CurrentKickOrigin(ent);
- // absolutely bound offsets
- // so the view can never be outside the player box
- if (v[0] < -14)
- v[0] = -14;
- else if (v[0] > 14)
- v[0] = 14;
- if (v[1] < -14)
- v[1] = -14;
- else if (v[1] > 14)
- v[1] = 14;
- if (v[2] < -22)
- v[2] = -22;
- else if (v[2] > 30)
- v[2] = 30;
- ent->client->ps.viewoffset = v;
- }
- /*
- ==============
- SV_CalcGunOffset
- ==============
- */
- void SV_CalcGunOffset(edict_t *ent)
- {
- int i;
- // ROGUE
- // ROGUE - heatbeam shouldn't bob so the beam looks right
- if (ent->client->pers.weapon &&
- !((ent->client->pers.weapon->id == IT_WEAPON_PLASMABEAM || ent->client->pers.weapon->id == IT_WEAPON_GRAPPLE) && ent->client->weaponstate == WEAPON_FIRING)
- && !SkipViewModifiers())
- {
- // ROGUE
- // gun angles from bobbing
- ent->client->ps.gunangles[ROLL] = xyspeed * bobfracsin * 0.005f;
- ent->client->ps.gunangles[YAW] = xyspeed * bobfracsin * 0.01f;
- if (bobcycle & 1)
- {
- ent->client->ps.gunangles[ROLL] = -ent->client->ps.gunangles[ROLL];
- ent->client->ps.gunangles[YAW] = -ent->client->ps.gunangles[YAW];
- }
- ent->client->ps.gunangles[PITCH] = xyspeed * bobfracsin * 0.005f;
- vec3_t viewangles_delta = ent->client->oldviewangles - ent->client->ps.viewangles;
-
- for (i = 0; i < 3; i++)
- ent->client->slow_view_angles[i] += viewangles_delta[i];
- // gun angles from delta movement
- for (i = 0; i < 3; i++)
- {
- float &d = ent->client->slow_view_angles[i];
- if (!d)
- continue;
- if (d > 180)
- d -= 360;
- if (d < -180)
- d += 360;
- if (d > 45)
- d = 45;
- if (d < -45)
- d = -45;
- // [Sam-KEX] Apply only half-delta. Makes the weapons look less detatched from the player.
- if (i == ROLL)
- ent->client->ps.gunangles[i] += (0.1f * d) * 0.5f;
- else
- ent->client->ps.gunangles[i] += (0.2f * d) * 0.5f;
- float reduction_factor = viewangles_delta[i] ? 0.05f : 0.15f;
- if (d > 0)
- d = max(0.f, d - gi.frame_time_ms * reduction_factor);
- else if (d < 0)
- d = min(0.f, d + gi.frame_time_ms * reduction_factor);
- }
- // [Paril-KEX] cl_rollhack
- ent->client->ps.gunangles[ROLL] = -ent->client->ps.gunangles[ROLL];
- }
- // ROGUE
- else
- {
- for (i = 0; i < 3; i++)
- ent->client->ps.gunangles[i] = 0;
- }
- // ROGUE
- // gun height
- ent->client->ps.gunoffset = {};
- // gun_x / gun_y / gun_z are development tools
- for (i = 0; i < 3; i++)
- {
- ent->client->ps.gunoffset[i] += forward[i] * (gun_y->value);
- ent->client->ps.gunoffset[i] += right[i] * gun_x->value;
- ent->client->ps.gunoffset[i] += up[i] * (-gun_z->value);
- }
- }
- /*
- =============
- SV_CalcBlend
- =============
- */
- void SV_CalcBlend(edict_t *ent)
- {
- gtime_t remaining;
- ent->client->ps.damage_blend = ent->client->ps.screen_blend = {};
- // add for powerups
- if (ent->client->quad_time > level.time)
- {
- remaining = ent->client->quad_time - level.time;
- if (remaining.milliseconds() == 3000) // beginning to fade
- gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage2.wav"), 1, ATTN_NORM, 0);
- if (G_PowerUpExpiringRelative(remaining))
- G_AddBlend(0, 0, 1, 0.08f, ent->client->ps.screen_blend);
- }
- // RAFAEL
- else if (ent->client->quadfire_time > level.time)
- {
- remaining = ent->client->quadfire_time - level.time;
- if (remaining.milliseconds() == 3000) // beginning to fade
- gi.sound(ent, CHAN_ITEM, gi.soundindex("items/quadfire2.wav"), 1, ATTN_NORM, 0);
- if (G_PowerUpExpiringRelative(remaining))
- G_AddBlend(1, 0.2f, 0.5f, 0.08f, ent->client->ps.screen_blend);
- }
- // RAFAEL
- // PMM - double damage
- else if (ent->client->double_time > level.time)
- {
- remaining = ent->client->double_time - level.time;
- if (remaining.milliseconds() == 3000) // beginning to fade
- gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/ddamage2.wav"), 1, ATTN_NORM, 0);
- if (G_PowerUpExpiringRelative(remaining))
- G_AddBlend(0.9f, 0.7f, 0, 0.08f, ent->client->ps.screen_blend);
- }
- // PMM
- else if (ent->client->invincible_time > level.time)
- {
- remaining = ent->client->invincible_time - level.time;
- if (remaining.milliseconds() == 3000) // beginning to fade
- gi.sound(ent, CHAN_ITEM, gi.soundindex("items/protect2.wav"), 1, ATTN_NORM, 0);
- if (G_PowerUpExpiringRelative(remaining))
- G_AddBlend(1, 1, 0, 0.08f, ent->client->ps.screen_blend);
- }
- else if (ent->client->invisible_time > level.time)
- {
- remaining = ent->client->invisible_time - level.time;
- if (remaining.milliseconds() == 3000) // beginning to fade
- gi.sound(ent, CHAN_ITEM, gi.soundindex("items/protect2.wav"), 1, ATTN_NORM, 0);
- if (G_PowerUpExpiringRelative(remaining))
- G_AddBlend(0.8f, 0.8f, 0.8f, 0.08f, ent->client->ps.screen_blend);
- }
- else if (ent->client->enviro_time > level.time)
- {
- remaining = ent->client->enviro_time - level.time;
- if (remaining.milliseconds() == 3000) // beginning to fade
- gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0);
- if (G_PowerUpExpiringRelative(remaining))
- G_AddBlend(0, 1, 0, 0.08f, ent->client->ps.screen_blend);
- }
- else if (ent->client->breather_time > level.time)
- {
- remaining = ent->client->breather_time - level.time;
- if (remaining.milliseconds() == 3000) // beginning to fade
- gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0);
- if (G_PowerUpExpiringRelative(remaining))
- G_AddBlend(0.4f, 1, 0.4f, 0.04f, ent->client->ps.screen_blend);
- }
- // PGM
- if (ent->client->nuke_time > level.time)
- {
- float brightness = (ent->client->nuke_time - level.time).seconds() / 2.0f;
- G_AddBlend(1, 1, 1, brightness, ent->client->ps.screen_blend);
- }
- if (ent->client->ir_time > level.time)
- {
- remaining = ent->client->ir_time - level.time;
- if (G_PowerUpExpiringRelative(remaining))
- {
- ent->client->ps.rdflags |= RDF_IRGOGGLES;
- G_AddBlend(1, 0, 0, 0.2f, ent->client->ps.screen_blend);
- }
- else
- ent->client->ps.rdflags &= ~RDF_IRGOGGLES;
- }
- else
- {
- ent->client->ps.rdflags &= ~RDF_IRGOGGLES;
- }
- // PGM
- // add for damage
- if (ent->client->damage_alpha > 0)
- G_AddBlend(ent->client->damage_blend[0], ent->client->damage_blend[1], ent->client->damage_blend[2], ent->client->damage_alpha, ent->client->ps.damage_blend);
- // [Paril-KEX] drowning visual indicator
- if (ent->air_finished < level.time + 9_sec)
- {
- constexpr vec3_t drown_color = { 0.1f, 0.1f, 0.2f };
- constexpr float max_drown_alpha = 0.75f;
- float alpha = (ent->air_finished < level.time) ? 1 : (1.f - ((ent->air_finished - level.time).seconds() / 9.0f));
- G_AddBlend(drown_color[0], drown_color[1], drown_color[2], min(alpha, max_drown_alpha), ent->client->ps.damage_blend);
- }
- #if 0
- if (ent->client->bonus_alpha > 0)
- G_AddBlend(0.85f, 0.7f, 0.3f, ent->client->bonus_alpha, ent->client->ps.damage_blend);
- #endif
- // drop the damage value
- ent->client->damage_alpha -= gi.frame_time_s * 0.6f;
- if (ent->client->damage_alpha < 0)
- ent->client->damage_alpha = 0;
- // drop the bonus value
- ent->client->bonus_alpha -= gi.frame_time_s;
- if (ent->client->bonus_alpha < 0)
- ent->client->bonus_alpha = 0;
- }
- /*
- =============
- P_WorldEffects
- =============
- */
- void P_WorldEffects()
- {
- bool breather;
- bool envirosuit;
- water_level_t waterlevel, old_waterlevel;
- if (current_player->movetype == MOVETYPE_NOCLIP)
- {
- current_player->air_finished = level.time + 12_sec; // don't need air
- return;
- }
- waterlevel = current_player->waterlevel;
- old_waterlevel = current_client->old_waterlevel;
- current_client->old_waterlevel = waterlevel;
- breather = current_client->breather_time > level.time;
- envirosuit = current_client->enviro_time > level.time;
- //
- // 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_sec;
- }
- //
- // 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 != WATER_UNDER && waterlevel == WATER_UNDER)
- {
- 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 (current_player->health > 0 && old_waterlevel == WATER_UNDER && waterlevel != WATER_UNDER)
- {
- 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_sec)
- { // just break surface
- gi.sound(current_player, CHAN_VOICE, gi.soundindex("player/gasp2.wav"), 1, ATTN_NORM, 0);
- }
- }
- //
- // check for drowning
- //
- if (waterlevel == WATER_UNDER)
- {
- // breather or envirosuit give air
- if (breather || envirosuit)
- {
- current_player->air_finished = level.time + 10_sec;
- if (((current_client->breather_time - level.time).milliseconds() % 2500) == 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_sec;
- // 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("*drown1.wav"), 1, ATTN_NORM, 0); // [Paril-KEX]
- else if (brandom())
- 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);
- }
- }
- // Paril: almost-drowning sounds
- else if (current_player->air_finished <= level.time + 3_sec)
- {
- if (current_player->client->next_drown_time < level.time)
- {
- gi.sound(current_player, CHAN_VOICE, gi.soundindex(fmt::format("player/wade{}.wav", 1 + ((int32_t) level.time.seconds() % 3)).c_str()), 1, ATTN_NORM, 0);
- current_player->client->next_drown_time = level.time + 1_sec;
- }
- }
- }
- else
- {
- current_player->air_finished = level.time + 12_sec;
- current_player->dmg = 2;
- }
- //
- // check for sizzle damage
- //
- if (waterlevel && (current_player->watertype & (CONTENTS_LAVA | CONTENTS_SLIME)) && current_player->slime_debounce_time <= level.time)
- {
- if (current_player->watertype & CONTENTS_LAVA)
- {
- if (current_player->health > 0 && current_player->pain_debounce_time <= level.time && current_client->invincible_time < level.time)
- {
- if (brandom())
- 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_sec;
- }
- int dmg = (envirosuit ? 1 : 3) * waterlevel; // take 1/3 damage with envirosuit
- T_Damage(current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, dmg, 0, DAMAGE_NONE, MOD_LAVA);
- current_player->slime_debounce_time = level.time + 10_hz;
- }
- 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, DAMAGE_NONE, MOD_SLIME);
- current_player->slime_debounce_time = level.time + 10_hz;
- }
- }
- }
- }
- /*
- ===============
- G_SetClientEffects
- ===============
- */
- void G_SetClientEffects(edict_t *ent)
- {
- int pa_type;
- ent->s.effects = EF_NONE;
- ent->s.renderfx &= RF_STAIR_STEP;
- ent->s.renderfx |= RF_IR_VISIBLE;
- ent->s.alpha = 1.0;
- if (ent->health <= 0 || level.intermissiontime)
- return;
- if (ent->flags & FL_FLASHLIGHT)
- ent->s.effects |= EF_FLASHLIGHT;
- //=========
- // PGM
- if (ent->flags & FL_DISGUISED)
- ent->s.renderfx |= RF_USE_DISGUISE;
- if (gamerules->integer)
- {
- if (DMGame.PlayerEffects)
- DMGame.PlayerEffects(ent);
- }
- // PGM
- //=========
- if (ent->powerarmor_time > level.time)
- {
- pa_type = PowerArmorType(ent);
- if (pa_type == IT_ITEM_POWER_SCREEN)
- {
- ent->s.effects |= EF_POWERSCREEN;
- }
- else if (pa_type == IT_ITEM_POWER_SHIELD)
- {
- ent->s.effects |= EF_COLOR_SHELL;
- ent->s.renderfx |= RF_SHELL_GREEN;
- }
- }
- // ZOID
- CTFEffects(ent);
- // ZOID
- if (ent->client->quad_time > level.time)
- {
- if (G_PowerUpExpiring(ent->client->quad_time))
- CTFSetPowerUpEffect(ent, EF_QUAD);
- }
- // RAFAEL
- if (ent->client->quadfire_time > level.time)
- {;
- if (G_PowerUpExpiring(ent->client->quadfire_time))
- CTFSetPowerUpEffect(ent, EF_DUALFIRE);
- }
- // RAFAEL
- //=======
- // ROGUE
- if (ent->client->double_time > level.time)
- {
- if (G_PowerUpExpiring(ent->client->double_time))
- CTFSetPowerUpEffect(ent, EF_DOUBLE);
- }
- if ((ent->client->owned_sphere) && (ent->client->owned_sphere->spawnflags == SPHERE_DEFENDER))
- {
- CTFSetPowerUpEffect(ent, EF_HALF_DAMAGE);
- }
- if (ent->client->tracker_pain_time > level.time)
- {
- ent->s.effects |= EF_TRACKERTRAIL;
- }
- if (ent->client->invisible_time > level.time)
- {
- if (ent->client->invisibility_fade_time <= level.time)
- ent->s.alpha = 0.1f;
- else
- {
- float x = (ent->client->invisibility_fade_time - level.time).seconds() / INVISIBILITY_TIME.seconds();
- ent->s.alpha = std::clamp(x, 0.1f, 1.0f);
- }
- }
- // ROGUE
- //=======
- if (ent->client->invincible_time > level.time)
- {
- if (G_PowerUpExpiring(ent->client->invincible_time))
- CTFSetPowerUpEffect(ent, EF_PENT);
- }
- // show cheaters!!!
- if (ent->flags & FL_GODMODE)
- {
- ent->s.effects |= EF_COLOR_SHELL;
- ent->s.renderfx |= (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE);
- }
- #if 0
- // disintegrator stuff
- if (ent->disintegrator_time)
- {
- if (ent->disintegrator_time > 100_sec)
- {
- gi.WriteByte(svc_temp_entity);
- gi.WriteByte(TE_BOSSTPORT);
- gi.WritePosition(ent->s.origin);
- gi.multicast(ent->s.origin, MULTICAST_PHS, false);
- Killed(ent, ent, ent, 999999, vec3_origin, MOD_NUKE);
- ent->disintegrator_time = 0_ms;
- ThrowClientHead(ent, 9999);
- ent->s.modelindex = 0;
- ent->solid = SOLID_NOT;
- }
- else
- {
- ent->disintegrator_time = max(0_ms, ent->disintegrator_time - 1500_ms);
- if (ent->disintegrator_time)
- ent->s.alpha = max(1 / 255.f, 1.f - (ent->disintegrator_time.seconds() / 100));
- else
- ent->s.alpha = 1;
- }
- }
- #endif
- }
- /*
- ===============
- G_SetClientEvent
- ===============
- */
- void G_SetClientEvent(edict_t *ent)
- {
- if (ent->s.event)
- return;
- if (ent->client->ps.pmove.pm_flags & PMF_ON_LADDER)
- {
- if (!deathmatch->integer &&
- current_client->last_ladder_sound < level.time &&
- (current_client->last_ladder_pos - ent->s.origin).length() > 48.f)
- {
- ent->s.event = EV_LADDER_STEP;
- current_client->last_ladder_pos = ent->s.origin;
- current_client->last_ladder_sound = level.time + LADDER_SOUND_TIME;
- }
- }
- else if (ent->groundentity && xyspeed > 225)
- {
- if ((int) (current_client->bobtime + bobmove) != bobcycle_run)
- ent->s.event = EV_FOOTSTEP;
- }
- }
- /*
- ===============
- G_SetClientSound
- ===============
- */
- void G_SetClientSound(edict_t *ent)
- {
- // help beep (no more than three times)
- if (ent->client->pers.helpchanged && ent->client->pers.helpchanged <= 3 && ent->client->pers.help_time < level.time)
- {
- if (ent->client->pers.helpchanged == 1) // [KEX] haleyjd: once only
- gi.sound(ent, CHAN_AUTO, gi.soundindex("misc/pc_up.wav"), 1, ATTN_STATIC, 0);
- ent->client->pers.helpchanged++;
- ent->client->pers.help_time = level.time + 5_sec;
- }
- // reset defaults
- ent->s.sound = 0;
- ent->s.loop_attenuation = 0;
- ent->s.loop_volume = 0;
- if (ent->waterlevel && (ent->watertype & (CONTENTS_LAVA | CONTENTS_SLIME)))
- {
- ent->s.sound = snd_fry;
- return;
- }
- if (ent->deadflag || ent->client->resp.spectator)
- return;
- if (ent->client->weapon_sound)
- ent->s.sound = ent->client->weapon_sound;
- else if (ent->client->pers.weapon)
- {
- if (ent->client->pers.weapon->id == IT_WEAPON_RAILGUN)
- ent->s.sound = gi.soundindex("weapons/rg_hum.wav");
- else if (ent->client->pers.weapon->id == IT_WEAPON_BFG)
- ent->s.sound = gi.soundindex("weapons/bfg_hum.wav");
- // RAFAEL
- else if (ent->client->pers.weapon->id == IT_WEAPON_PHALANX)
- ent->s.sound = gi.soundindex("weapons/phaloop.wav");
- // RAFAEL
- }
- // [Paril-KEX] if no other sound is playing, play appropriate grapple sounds
- if (!ent->s.sound && ent->client->ctf_grapple)
- {
- if (ent->client->ctf_grapplestate == CTF_GRAPPLE_STATE_PULL)
- ent->s.sound = gi.soundindex("weapons/grapple/grpull.wav");
- else if (ent->client->ctf_grapplestate == CTF_GRAPPLE_STATE_FLY)
- ent->s.sound = gi.soundindex("weapons/grapple/grfly.wav");
- else if (ent->client->ctf_grapplestate == CTF_GRAPPLE_STATE_HANG)
- ent->s.sound = gi.soundindex("weapons/grapple/grhang.wav");
- }
- // weapon sounds play at a higher attn
- ent->s.loop_attenuation = ATTN_NORM;
- }
- /*
- ===============
- G_SetClientFrame
- ===============
- */
- void G_SetClientFrame(edict_t *ent)
- {
- gclient_t *client;
- bool duck, run;
- if (ent->s.modelindex != MODELINDEX_PLAYER)
- return; // not in the player model
- client = ent->client;
- if (client->ps.pmove.pm_flags & PMF_DUCKED)
- duck = true;
- else
- duck = false;
- if (xyspeed)
- run = true;
- else
- run = false;
- // check for stand/duck and stop/go transitions
- if (duck != client->anim_duck && client->anim_priority < ANIM_DEATH)
- goto newanim;
- if (run != client->anim_run && client->anim_priority == ANIM_BASIC)
- goto newanim;
- if (!ent->groundentity && client->anim_priority <= ANIM_WAVE)
- goto newanim;
-
- if (client->anim_time > level.time)
- return;
- else if ((client->anim_priority & ANIM_REVERSED) && (ent->s.frame > client->anim_end))
- {
- if (client->anim_time <= level.time)
- {
- ent->s.frame--;
- client->anim_time = level.time + 10_hz;
- }
- return;
- }
- else if (!(client->anim_priority & ANIM_REVERSED) && (ent->s.frame < client->anim_end))
- {
- // continue an animation
- if (client->anim_time <= level.time)
- {
- ent->s.frame++;
- client->anim_time = level.time + 10_hz;
- }
- return;
- }
- if (client->anim_priority == ANIM_DEATH)
- return; // stay there
- if (client->anim_priority == ANIM_JUMP)
- {
- if (!ent->groundentity)
- return; // stay there
- ent->client->anim_priority = ANIM_WAVE;
- if (duck)
- {
- ent->s.frame = FRAME_jump6;
- ent->client->anim_end = FRAME_jump4;
- ent->client->anim_priority |= ANIM_REVERSED;
- }
- else
- {
- ent->s.frame = FRAME_jump3;
- ent->client->anim_end = FRAME_jump6;
- }
- ent->client->anim_time = level.time + 10_hz;
- return;
- }
- newanim:
- // return to either a running or standing frame
- client->anim_priority = ANIM_BASIC;
- client->anim_duck = duck;
- client->anim_run = run;
- client->anim_time = level.time + 10_hz;
- if (!ent->groundentity)
- {
- // ZOID: if on grapple, don't go into jump frame, go into standing
- // frame
- if (client->ctf_grapple)
- {
- if (duck)
- {
- ent->s.frame = FRAME_crstnd01;
- client->anim_end = FRAME_crstnd19;
- }
- else
- {
- ent->s.frame = FRAME_stand01;
- client->anim_end = FRAME_stand40;
- }
- }
- else
- {
- // ZOID
- client->anim_priority = ANIM_JUMP;
- if (duck)
- {
- if (ent->s.frame != FRAME_crwalk2)
- ent->s.frame = FRAME_crwalk1;
- client->anim_end = FRAME_crwalk2;
- }
- else
- {
- if (ent->s.frame != FRAME_jump2)
- ent->s.frame = FRAME_jump1;
- client->anim_end = FRAME_jump2;
- }
- }
- }
- else if (run)
- { // running
- if (duck)
- {
- ent->s.frame = FRAME_crwalk1;
- client->anim_end = FRAME_crwalk6;
- }
- else
- {
- ent->s.frame = FRAME_run1;
- client->anim_end = FRAME_run6;
- }
- }
- else
- { // standing
- if (duck)
- {
- ent->s.frame = FRAME_crstnd01;
- client->anim_end = FRAME_crstnd19;
- }
- else
- {
- ent->s.frame = FRAME_stand01;
- client->anim_end = FRAME_stand40;
- }
- }
- }
- // [Paril-KEX]
- static void P_RunMegaHealth(edict_t *ent)
- {
- if (!ent->client->pers.megahealth_time)
- return;
- else if (ent->health <= ent->max_health)
- {
- ent->client->pers.megahealth_time = 0_ms;
- return;
- }
- ent->client->pers.megahealth_time -= FRAME_TIME_S;
- if (ent->client->pers.megahealth_time <= 0_ms)
- {
- ent->health--;
- if (ent->health > ent->max_health)
- ent->client->pers.megahealth_time = 1000_ms;
- else
- ent->client->pers.megahealth_time = 0_ms;
- }
- }
- // [Paril-KEX] push all players' origins back to match their lag compensation
- void G_LagCompensate(edict_t *from_player, const vec3_t &start, const vec3_t &dir)
- {
- uint32_t current_frame = gi.ServerFrame();
- // if you need this to fight monsters, you need help
- if (!deathmatch->integer)
- return;
- else if (!g_lag_compensation->integer)
- return;
- // don't need this
- else if (from_player->client->cmd.server_frame >= current_frame ||
- (from_player->svflags & SVF_BOT))
- return;
- int32_t frame_delta = (current_frame - from_player->client->cmd.server_frame) + 1;
- for (auto player : active_players())
- {
- // we aren't gonna hit ourselves
- if (player == from_player)
- continue;
- // not enough data, spare them
- if (player->client->num_lag_origins < frame_delta)
- continue;
- // if they're way outside of cone of vision, they won't be captured in this
- if ((player->s.origin - start).normalized().dot(dir) < 0.75f)
- continue;
- int32_t lag_id = (player->client->next_lag_origin - 1) - (frame_delta - 1);
- if (lag_id < 0)
- lag_id = game.max_lag_origins + lag_id;
- if (lag_id < 0 || lag_id >= player->client->num_lag_origins)
- {
- gi.Com_Print("lag compensation error\n");
- G_UnLagCompensate();
- return;
- }
- const vec3_t &lag_origin = (game.lag_origins + ((player->s.number - 1) * game.max_lag_origins))[lag_id];
- // no way they'd be hit if they aren't in the PVS
- if (!gi.inPVS(lag_origin, start, false))
- continue;
- // only back up once
- if (!player->client->is_lag_compensated)
- {
- player->client->is_lag_compensated = true;
- player->client->lag_restore_origin = player->s.origin;
- }
-
- player->s.origin = lag_origin;
- gi.linkentity(player);
- }
- }
- // [Paril-KEX] pop everybody's lag compensation values
- void G_UnLagCompensate()
- {
- for (auto player : active_players())
- {
- if (player->client->is_lag_compensated)
- {
- player->client->is_lag_compensated = false;
- player->s.origin = player->client->lag_restore_origin;
- gi.linkentity(player);
- }
- }
- }
- // [Paril-KEX] save the current lag compensation value
- static void G_SaveLagCompensation(edict_t *ent)
- {
- (game.lag_origins + ((ent->s.number - 1) * game.max_lag_origins))[ent->client->next_lag_origin] = ent->s.origin;
- ent->client->next_lag_origin = (ent->client->next_lag_origin + 1) % game.max_lag_origins;
- if (ent->client->num_lag_origins < game.max_lag_origins)
- ent->client->num_lag_origins++;
- }
- /*
- =================
- ClientEndServerFrame
- Called for each player at the end of the server frame
- and right after spawning
- =================
- */
- void ClientEndServerFrame(edict_t *ent)
- {
- // no player exists yet (load game)
- if (!ent->client->pers.spawned)
- return;
- float bobtime, bobtime_run;
- current_player = ent;
- current_client = ent->client;
- // check fog changes
- P_ForceFogTransition(ent, false);
- // check goals
- G_PlayerNotifyGoal(ent);
- // mega health
- P_RunMegaHealth(ent);
- //
- // If the origin or velocity have changed since ClientThink(),
- // update the pmove values. This will happen when the client
- // is pushed by a bmodel or kicked by an explosion.
- //
- // If it wasn't updated here, the view position would lag a frame
- // behind the body position when pushed -- "sinking into plats"
- //
- current_client->ps.pmove.origin = ent->s.origin;
- current_client->ps.pmove.velocity = ent->velocity;
- //
- // If the end of unit layout is displayed, don't give
- // the player any normal movement attributes
- //
- if (level.intermissiontime || ent->client->awaiting_respawn)
- {
- if (ent->client->awaiting_respawn || (level.intermission_eou || level.is_n64 || (deathmatch->integer && level.intermissiontime)))
- {
- current_client->ps.screen_blend[3] = current_client->ps.damage_blend[3] = 0;
- current_client->ps.fov = 90;
- current_client->ps.gunindex = 0;
- }
- G_SetStats(ent);
- G_SetCoopStats(ent);
- // if the scoreboard is up, update it if a client leaves
- if (deathmatch->integer && ent->client->showscores && ent->client->menutime)
- {
- DeathmatchScoreboardMessage(ent, ent->enemy);
- gi.unicast(ent, false);
- ent->client->menutime = 0_ms;
- }
- return;
- }
- // ZOID
- // regen tech
- CTFApplyRegeneration(ent);
- // ZOID
- AngleVectors(ent->client->v_angle, forward, right, up);
- // burn from lava, etc
- P_WorldEffects();
- //
- // set model angles from view angles so other things in
- // the world can tell which direction you are looking
- //
- if (ent->client->v_angle[PITCH] > 180)
- ent->s.angles[PITCH] = (-360 + ent->client->v_angle[PITCH]) / 3;
- else
- ent->s.angles[PITCH] = ent->client->v_angle[PITCH] / 3;
-
- ent->s.angles[YAW] = ent->client->v_angle[YAW];
- ent->s.angles[ROLL] = 0;
- // [Paril-KEX] cl_rollhack
- ent->s.angles[ROLL] = -SV_CalcRoll(ent->s.angles, ent->velocity) * 4;
- //
- // calculate speed and cycle to be used for
- // all cyclic walking effects
- //
- xyspeed = sqrt(ent->velocity[0] * ent->velocity[0] + ent->velocity[1] * ent->velocity[1]);
- if (xyspeed < 5)
- {
- bobmove = 0;
- current_client->bobtime = 0; // start at beginning of cycle again
- }
- else if (ent->groundentity)
- { // so bobbing only cycles when on ground
- if (xyspeed > 210)
- bobmove = gi.frame_time_ms / 400.f;
- else if (xyspeed > 100)
- bobmove = gi.frame_time_ms / 800.f;
- else
- bobmove = gi.frame_time_ms / 1600.f;
- }
- bobtime = (current_client->bobtime += bobmove);
- bobtime_run = bobtime;
- if ((current_client->ps.pmove.pm_flags & PMF_DUCKED) && ent->groundentity)
- bobtime *= 4;
- bobcycle = (int) bobtime;
- bobcycle_run = (int) bobtime_run;
- bobfracsin = fabsf(sinf(bobtime * PIf));
- // apply all the damage taken this frame
- P_DamageFeedback(ent);
- // determine the view offsets
- SV_CalcViewOffset(ent);
- // determine the gun offsets
- SV_CalcGunOffset(ent);
- // determine the full screen color blend
- // must be after viewoffset, so eye contents can be
- // accurately determined
- SV_CalcBlend(ent);
- // chase cam stuff
- if (ent->client->resp.spectator)
- G_SetSpectatorStats(ent);
- else
- G_SetStats(ent);
- G_CheckChaseStats(ent);
- G_SetCoopStats(ent);
- G_SetClientEvent(ent);
- G_SetClientEffects(ent);
- G_SetClientSound(ent);
- G_SetClientFrame(ent);
- ent->client->oldvelocity = ent->velocity;
- ent->client->oldviewangles = ent->client->ps.viewangles;
- ent->client->oldgroundentity = ent->groundentity;
- // ZOID
- if (ent->client->menudirty && ent->client->menutime <= level.time)
- {
- if (ent->client->menu)
- {
- PMenu_Do_Update(ent);
- gi.unicast(ent, true);
- }
- ent->client->menutime = level.time;
- ent->client->menudirty = false;
- }
- // ZOID
- // if the scoreboard is up, update it
- if (ent->client->showscores && ent->client->menutime <= level.time)
- {
- // ZOID
- if (ent->client->menu)
- {
- PMenu_Do_Update(ent);
- ent->client->menudirty = false;
- }
- else
- // ZOID
- DeathmatchScoreboardMessage(ent, ent->enemy);
- gi.unicast(ent, false);
- ent->client->menutime = level.time + 3_sec;
- }
- if ( ( ent->svflags & SVF_BOT ) != 0 ) {
- Bot_EndFrame( ent );
- }
- P_AssignClientSkinnum(ent);
- if (deathmatch->integer)
- G_SaveLagCompensation(ent);
- Compass_Update(ent, false);
- // [Paril-KEX] in coop, if player collision is enabled and
- // we are currently in no-player-collision mode, check if
- // it's safe.
- if (coop->integer && G_ShouldPlayersCollide(false) && !(ent->clipmask & CONTENTS_PLAYER) && ent->takedamage)
- {
- bool clipped_player = false;
- for (auto player : active_players())
- {
- if (player == ent)
- continue;
- trace_t clip = gi.clip(player, ent->s.origin, ent->mins, ent->maxs, ent->s.origin, CONTENTS_MONSTER | CONTENTS_PLAYER);
- if (clip.startsolid || clip.allsolid)
- {
- clipped_player = true;
- break;
- }
- }
- // safe!
- if (!clipped_player)
- ent->clipmask |= CONTENTS_PLAYER;
- }
- }
|