X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fcl_player.qc;h=416fb1db7dc98dd70604cd61ec94add8b1961d99;hb=ae451c4613d70de122929142380b47a3dcf54280;hp=c11e92051074b7fcbf15b208b774f14d96c00d88;hpb=90efb5467fe733b53d89c0b32da3dca8252e7f19;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/cl_player.qc b/qcsrc/server/cl_player.qc index c11e92051..416fb1db7 100644 --- a/qcsrc/server/cl_player.qc +++ b/qcsrc/server/cl_player.qc @@ -1,132 +1,10 @@ -.entity accuracy; -.float accuracy_frags[WEP_MAXCOUNT]; +#include "cl_player.qh" +#include "g_triggers.qh" +#include "g_violence.qh" +#include "miscfunctions.qh" -float weaponstats_buffer; +#include "weapons/weaponstats.qh" -void WeaponStats_Init() -{ - if(autocvar_sv_weaponstats_file != "") - weaponstats_buffer = buf_create(); - else - weaponstats_buffer = -1; -} - -#define WEAPONSTATS_GETINDEX(awep,abot,vwep,vbot) (((vwep) + (awep) * (WEP_LAST - WEP_FIRST + 1) - (WEP_FIRST + WEP_FIRST * (WEP_LAST - WEP_FIRST + 1))) * 4 + (abot) * 2 + (vbot)) - -void WeaponStats_ready(entity fh, entity pass, float status) -{ - float i, j, n, ibot, jbot, idx; - vector v; - string prefix, s; - switch(status) - { - case URL_READY_CANWRITE: - // we can write - prefix = strcat(autocvar_hostname, "\t", GetGametype(), "_", GetMapname(), "\t"); - url_fputs(fh, "#begin statsfile\n"); - url_fputs(fh, strcat("#date ", strftime(TRUE, "%a %b %e %H:%M:%S %Z %Y"), "\n")); -#ifdef WATERMARK - url_fputs(fh, strcat("#version ", WATERMARK, "\n")); -#endif - url_fputs(fh, strcat("#config ", ftos(crc16(FALSE, cvar_purechanges)), "\n")); - url_fputs(fh, strcat("#cvar_purechanges ", ftos(cvar_purechanges_count), "\n")); - n = tokenizebyseparator(cvar_purechanges, "\n"); - for(i = 0; i < n; ++i) - url_fputs(fh, strcat("#cvar_purechange ", argv(i), "\n")); - for(i = WEP_FIRST; i <= WEP_LAST; ++i) for(ibot = 0; ibot <= 1; ++ibot) - for(j = WEP_FIRST; j <= WEP_LAST; ++j) for(jbot = 0; jbot <= 1; ++jbot) - { - idx = WEAPONSTATS_GETINDEX(i, ibot, j, jbot); - v = stov(bufstr_get(weaponstats_buffer, idx)); - if(v != '0 0 0') - { - //vector is: kills hits damage - url_fputs(fh, sprintf("%s%d %d\t%d %d\t", prefix, i, ibot, j, jbot)); - url_fputs(fh, sprintf("%d %d %g\n", v_x, v_y, v_z)); - } - } - url_fputs(fh, "#end\n\n"); - url_fclose(fh); - break; - case URL_READY_CANREAD: - // url_fclose is processing, we got a response for writing the data - // this must come from HTTP - print("Got response from weapon stats server:\n"); - while((s = url_fgets(fh))) - print(" ", s, "\n"); - print("End of response.\n"); - url_fclose(fh); - break; - case URL_READY_CLOSED: - // url_fclose has finished - print("Weapon stats written\n"); - buf_del(weaponstats_buffer); - weaponstats_buffer = -1; - break; - case URL_READY_ERROR: - default: - print("Weapon stats writing failed: ", ftos(status), "\n"); - buf_del(weaponstats_buffer); - weaponstats_buffer = -1; - break; - } -} - -void WeaponStats_Shutdown() -{ - if(weaponstats_buffer < 0) - return; - if(autocvar_sv_weaponstats_file != "") - { - url_multi_fopen(autocvar_sv_weaponstats_file, FILE_APPEND, WeaponStats_ready, world); - } - else - { - buf_del(weaponstats_buffer); - weaponstats_buffer = -1; - } -} - -void WeaponStats_LogItem(float awep, float abot, float vwep, float vbot, vector item) -{ - float idx; - if(weaponstats_buffer < 0) - return; - if(awep < WEP_FIRST || vwep < WEP_FIRST) - return; - if(awep > WEP_LAST || vwep > WEP_LAST) - return; - idx = WEAPONSTATS_GETINDEX(awep,abot,vwep,vbot); - bufstr_set(weaponstats_buffer, idx, vtos(stov(bufstr_get(weaponstats_buffer, idx)) + item)); -} -void WeaponStats_LogDamage(float awep, float abot, float vwep, float vbot, float damage) -{ - if(damage < 0) - error("negative damage?"); - WeaponStats_LogItem(awep, abot, vwep, vbot, '0 0 1' * damage + '0 1 0'); -} -void WeaponStats_LogKill(float awep, float abot, float vwep, float vbot) -{ - WeaponStats_LogItem(awep, abot, vwep, vbot, '1 0 0'); -} - -// changes by LordHavoc on 03/29/04 and 03/30/04 at Vermeulen's request -// merged player_run and player_stand to player_anim -// added death animations to player_anim -// can now spawn thrown weapons from anywhere, not just from players -// thrown weapons now fade out after 20 seconds -// created PlayerGib function -// PlayerDie no longer uses hitloc or damage -// PlayerDie now supports dying animations as well as gibbing -// cleaned up PlayerDie a lot -// added CopyBody - -.entity pusher; -.float pushltime; -.float istypefrag; - -.float CopyBody_nextthink; -.void(void) CopyBody_think; void CopyBody_Think(void) { if(self.CopyBody_nextthink && time > self.CopyBody_nextthink) @@ -214,7 +92,8 @@ void CopyBody(float keepvelocity) self.nextthink = time; self.think = CopyBody_Think; // "bake" the current animation frame for clones (they don't get clientside animation) - animdecide_setframes(self, FALSE, frame, frame1time, frame2, frame2time); + animdecide_load_if_needed(self); + animdecide_setframes(self, false, frame, frame1time, frame2, frame2time); self = oldself; } @@ -233,42 +112,42 @@ float player_getspecies() void player_setupanimsformodel() { // load animation info - animdecide_init(self); - animdecide_setstate(self, 0, FALSE); + animdecide_load_if_needed(self); + animdecide_setstate(self, 0, false); } void player_anim (void) { - float deadbits = (self.anim_state & (ANIMSTATE_DEAD1 | ANIMSTATE_DEAD2)); - if(self.deadflag && !deadbits) - if(random() < 0.5) - deadbits = ANIMSTATE_DEAD1; - else - deadbits = ANIMSTATE_DEAD2; - float animbits = deadbits; + int deadbits = (self.anim_state & (ANIMSTATE_DEAD1 | ANIMSTATE_DEAD2)); + if(self.deadflag) { + if (!deadbits) { + // Decide on which death animation to use. + if(random() < 0.5) + deadbits = ANIMSTATE_DEAD1; + else + deadbits = ANIMSTATE_DEAD2; + } + } else { + // Clear a previous death animation. + deadbits = 0; + } + int animbits = deadbits; if(self.frozen) animbits |= ANIMSTATE_FROZEN; if(self.crouch) animbits |= ANIMSTATE_DUCK; - animdecide_setstate(self, animbits, FALSE); + animdecide_setstate(self, animbits, false); animdecide_setimplicitstate(self, (self.flags & FL_ONGROUND)); if (self.weaponentity) { updateanim(self.weaponentity); if (!self.weaponentity.animstate_override) - setanim(self.weaponentity, self.weaponentity.anim_idle, TRUE, FALSE, FALSE); + setanim(self.weaponentity, self.weaponentity.anim_idle, true, false, false); } } -void SpawnThrownWeapon (vector org, float w) -{ - if(self.weapons & WepSet_FromWeapon(self.weapon)) - if(W_IsWeaponThrowable(self.weapon)) - W_ThrowNewWeapon(self, self.weapon, FALSE, org, randomvec() * 125 + '0 0 200'); -} - -void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) { float take, save; vector v; @@ -278,8 +157,8 @@ void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, float damage = max(damage - 5, 1); v = healtharmor_applydamage(self.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage); - take = v_x; - save = v_y; + take = v.x; + save = v.y; if(sound_allowed(MSG_BROADCAST, attacker)) { @@ -296,13 +175,11 @@ void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, float if (take > 100) Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, self, attacker); - if (!(self.flags & FL_GODMODE)) - { - self.armorvalue = self.armorvalue - save; - self.health = self.health - take; - // pause regeneration for 5 seconds - self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_health_regen); - } + self.armorvalue = self.armorvalue - save; + self.health = self.health - take; + // pause regeneration for 5 seconds + self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_health_regen); + self.dmg_save = self.dmg_save + save;//max(save - 10, 0); self.dmg_take = self.dmg_take + take;//max(take - 10, 0); self.dmg_inflictor = inflictor; @@ -318,24 +195,15 @@ void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, float self.alpha = -1; self.solid = SOLID_NOT; // restore later self.takedamage = DAMAGE_NO; // restore later - self.damagedbycontents = FALSE; + self.damagedbycontents = false; } } -// g__str: -// If 0, default is used. -// If <0, 0 is used. -// Otherwise, g_str (default value) is used. -// For consistency, negative values there are mapped to zero too. -#define GAMETYPE_DEFAULTED_SETTING(str) \ - ((gametype_setting_tmp = cvar(strcat("g_", GetGametype(), "_" #str))), \ - (gametype_setting_tmp < 0) ? 0 : \ - (gametype_setting_tmp == 0) ? max(0, autocvar_g_##str) : \ - gametype_setting_tmp) - - void calculate_player_respawn_time() { + if(g_ca) + return; + float gametype_setting_tmp; float sdelay_max = GAMETYPE_DEFAULTED_SETTING(respawn_delay_max); float sdelay_small = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small); @@ -418,11 +286,10 @@ void calculate_player_respawn_time() self.respawn_flags = self.respawn_flags | RESPAWN_FORCE; } -void ClientKill_Now_TeamChange(); - -void PlayerDamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +void PlayerDamage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) { - float take, save, dh, da, j; + float take, save, dh, da; + int j; vector v; float valid_damage_for_weaponstats; float excess; @@ -444,7 +311,7 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht vector d; float f; ear1 = self.origin; - ear1_z += 0.125 * self.view_ofs_z + 0.875 * self.maxs_z; // 7/8 + ear1_z += 0.125 * self.view_ofs.z + 0.875 * self.maxs.z; // 7/8 ear2 = ear1; makevectors(self.angles); ear1 += v_right * -10; @@ -473,8 +340,8 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht v = healtharmor_applydamage(self.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage); - take = v_x; - save = v_y; + take = v.x; + save = v.y; if(attacker == self) { @@ -535,7 +402,7 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht self.health = self.health - take; // pause regeneration for 5 seconds if(take) - self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_health_regen); + self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_health_regen); if (time > self.pain_finished) //Don't switch pain sequences like crazy { @@ -547,14 +414,14 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht if (!self.animstate_override) { if (random() > 0.5) - animdecide_setaction(self, ANIMACTION_PAIN1, TRUE); + animdecide_setaction(self, ANIMACTION_PAIN1, true); else - animdecide_setaction(self, ANIMACTION_PAIN2, TRUE); + animdecide_setaction(self, ANIMACTION_PAIN2, true); } } if(sound_allowed(MSG_BROADCAST, attacker)) - if(!DEATH_ISWEAPON(deathtype, WEP_LASER) || attacker != self || self.health < 2 * autocvar_g_balance_laser_primary_damage * autocvar_g_balance_selfdamagepercent + 1) + if((self.health < 2 * WEP_CVAR_PRI(blaster, damage) * autocvar_g_balance_selfdamagepercent + 1) || !((get_weaponinfo(DEATH_WEAPONOF(deathtype))).spawnflags & WEP_FLAG_CANCLIMB) || attacker != self) // WEAPONTODO: create separate limit for pain notification with laser if(self.health > 1) // exclude pain sounds for laserjumps as long as you aren't REALLY low on health and would die of the next two { @@ -570,12 +437,16 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht PlayerSound(playersound_pain25, CH_PAIN, VOICETYPE_PLAYERSOUND); } } + } - // throw off bot aim temporarily - float shake; + // throw off bot aim temporarily + float shake; + if(IS_BOT_CLIENT(self) && self.health >= 1) + { shake = damage * 5 / (bound(0,skill,100) + 1); - self.v_angle_x = self.v_angle_x + (random() * 2 - 1) * shake; - self.v_angle_y = self.v_angle_y + (random() * 2 - 1) * shake; + self.v_angle_x = self.v_angle.x + (random() * 2 - 1) * shake; + self.v_angle_y = self.v_angle.y + (random() * 2 - 1) * shake; + self.v_angle_x = bound(-90, self.v_angle.x, 90); } } else @@ -614,11 +485,11 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht if (self.health < 1) { float defer_ClientKill_Now_TeamChange; - defer_ClientKill_Now_TeamChange = FALSE; + defer_ClientKill_Now_TeamChange = false; if(self.alivetime) { - PlayerStats_Event(self, PLAYERSTATS_ALIVETIME, time - self.alivetime); + PS_GR_P_ADDVAL(self, PLAYERSTATS_ALIVETIME, time - self.alivetime); self.alivetime = 0; } @@ -640,7 +511,7 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht remove(self.killindicator); self.killindicator = world; if(self.killindicator_teamchange) - defer_ClientKill_Now_TeamChange = TRUE; + defer_ClientKill_Now_TeamChange = true; if(self.classname == "body") if(deathtype == DEATH_KILL) @@ -651,11 +522,11 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht } // print an obituary message - Obituary (attacker, inflictor, self, deathtype); + if(self.classname != "body") + Obituary (attacker, inflictor, self, deathtype); // increment frag counter for used weapon type - float w; - w = DEATH_WEAPONOF(deathtype); + int w = DEATH_WEAPONOF(deathtype); if(WEP_VALID(w)) if(accuracy_isgooddamage(attacker, self)) attacker.accuracy.(accuracy_frags[w-1]) += 1; @@ -666,20 +537,20 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht frag_deathtype = deathtype; MUTATOR_CALLHOOK(PlayerDies); - weapon_action(self.weapon, WR_PLAYERDEATH); + WEP_ACTION(self.weapon, WR_PLAYERDEATH); RemoveGrapplingHook(self); Portal_ClearAllLater(self); - self.fixangle = TRUE; + self.fixangle = true; if(defer_ClientKill_Now_TeamChange) ClientKill_Now_TeamChange(); // can turn player into spectator // player could have been miraculously resuscitated ;) // e.g. players in freezetag get frozen, they don't really die - if(self.health >= 1 || !IS_PLAYER(self)) + if(self.health >= 1 || !(IS_PLAYER(self) || self.classname == "body")) return; // when we get here, player actually dies @@ -710,15 +581,16 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht self.flags &= ~FL_ONGROUND; // dying animation self.deadflag = DEAD_DYING; + // when to allow respawn calculate_player_respawn_time(); self.death_time = time; if (random() < 0.5) - animdecide_setstate(self, self.anim_state | ANIMSTATE_DEAD1, TRUE); + animdecide_setstate(self, self.anim_state | ANIMSTATE_DEAD1, true); else - animdecide_setstate(self, self.anim_state | ANIMSTATE_DEAD2, TRUE); - if (self.maxs_z > 5) + animdecide_setstate(self, self.anim_state | ANIMSTATE_DEAD2, true); + if (self.maxs.z > 5) { self.maxs_z = 5; setsize(self, self.mins, self.maxs); @@ -727,24 +599,32 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht self.event_damage = PlayerCorpseDamage; // call the corpse damage function just in case it wants to gib self.event_damage(inflictor, attacker, excess, deathtype, hitloc, force); + // set up to fade out later SUB_SetFade (self, time + 6 + random (), 1); + // reset body think wrapper broken by SUB_SetFade + if(self.classname == "body" && self.think != CopyBody_Think) { + self.CopyBody_think = self.think; + self.CopyBody_nextthink = self.nextthink; + self.think = CopyBody_Think; + self.nextthink = time; + } - if(autocvar_sv_gentle > 0 || autocvar_ekg) { + if(autocvar_sv_gentle > 0 || autocvar_ekg || self.classname == "body") { // remove corpse + // clones don't run any animation code any more, so we must gib them when they die :( PlayerCorpseDamage (inflictor, attacker, autocvar_sv_gibhealth+1.0, deathtype, hitloc, force); } // reset fields the weapons may use just in case for (j = WEP_FIRST; j <= WEP_LAST; ++j) { - weapon_action(j, WR_RESETPLAYER); + WEP_ACTION(j, WR_RESETPLAYER); ATTACK_FINISHED_FOR(self, j) = 0; } } } -.float muted; // to be used by prvm_edictset server playernumber muted 1 float Say(entity source, float teamsay, entity privatesay, string msgin, float floodcontrol) // message "": do not say, just test flood control // return value: @@ -772,11 +652,11 @@ float Say(entity source, float teamsay, entity privatesay, string msgin, float f else { colorstr = ""; - teamsay = FALSE; + teamsay = false; } if(intermission_running) - teamsay = FALSE; + teamsay = false; if(msgin != "") msgin = trigger_magicear_processmessage_forallears(source, teamsay, privatesay, msgin); @@ -887,9 +767,9 @@ float Say(entity source, float teamsay, entity privatesay, string msgin, float f flood = 2; } - if(time >= source.flood_field) + if (time >= source.(flood_field)) { - source.flood_field = max(time - flood_burst * flood_spl, source.flood_field) + lines * flood_spl; + source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + lines * flood_spl; } else { @@ -899,14 +779,14 @@ float Say(entity source, float teamsay, entity privatesay, string msgin, float f } else { - if(time >= source.flood_field) - source.flood_field = max(time - flood_burst * flood_spl, source.flood_field) + flood_spl; + if (time >= source.(flood_field)) + source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + flood_spl; else flood = 1; } if (timeout_status == TIMEOUT_ACTIVE) // when game is paused, no flood protection - source.flood_field = flood = 0; + source.(flood_field) = flood = 0; } if(flood == 2) // cannot happen for empty msgstr @@ -951,9 +831,9 @@ float Say(entity source, float teamsay, entity privatesay, string msgin, float f } else if(flood == 1) { - if(autocvar_g_chat_flood_notify_flooder) + if (autocvar_g_chat_flood_notify_flooder) { - sprint(source, strcat("^3FLOOD CONTROL: ^7wait ^1", ftos(source.flood_field - time), "^3 seconds\n")); + sprint(source, strcat("^3FLOOD CONTROL: ^7wait ^1", ftos(source.(flood_field) - time), "^3 seconds\n")); ret = 0; } else @@ -1026,7 +906,6 @@ float GetVoiceMessageVoiceType(string type) return VOICETYPE_TEAMRADIO; } -string allvoicesamples; .string GetVoiceMessageSampleField(string type) { GetPlayerSoundSampleField_notFound = 0; @@ -1124,16 +1003,14 @@ float LoadPlayerSounds(string f, float first) field = GetVoiceMessageSampleField(argv(0)); if(GetPlayerSoundSampleField_notFound) continue; - if(self.field) - strunzone(self.field); - self.field = strzone(strcat(argv(1), " ", argv(2))); + if (self.(field)) + strunzone(self.(field)); + self.(field) = strzone(strcat(argv(1), " ", argv(2))); } fclose(fh); return 1; } -.float modelindex_for_playersound; -.float skin_for_playersound; void UpdatePlayerSounds() { if(self.modelindex == self.modelindex_for_playersound) @@ -1202,7 +1079,7 @@ void FakeGlobalSound(string sample, float chan, float voicetype) case VOICETYPE_TAUNT: if(IS_PLAYER(self)) if(self.deadflag == DEAD_NO) - animdecide_setaction(self, ANIMACTION_TAUNT, TRUE); + animdecide_setaction(self, ANIMACTION_TAUNT, true); if(!sv_taunt) break; if(autocvar_sv_gentle) @@ -1299,7 +1176,7 @@ void GlobalSound(string sample, float chan, float voicetype) case VOICETYPE_TAUNT: if(IS_PLAYER(self)) if(self.deadflag == DEAD_NO) - animdecide_setaction(self, ANIMACTION_TAUNT, TRUE); + animdecide_setaction(self, ANIMACTION_TAUNT, true); if(!sv_taunt) break; if(autocvar_sv_gentle) @@ -1323,7 +1200,7 @@ void GlobalSound(string sample, float chan, float voicetype) void PlayerSound(.string samplefield, float chan, float voicetype) { - GlobalSound(self.samplefield, chan, voicetype); + GlobalSound(self.(samplefield), chan, voicetype); } void VoiceMessage(string type, string msg) @@ -1344,10 +1221,10 @@ void VoiceMessage(string type, string msg) flood = Say(self, ownteam, world, msg, 1); - if (flood > 0) - GlobalSound(self.sample, CH_VOICE, voicetype); - else if (flood < 0) - FakeGlobalSound(self.sample, CH_VOICE, voicetype); + if (IS_SPEC(self) || IS_OBSERVER(self) || flood < 0) + FakeGlobalSound(self.(sample), CH_VOICE, voicetype); + else if (flood > 0) + GlobalSound(self.(sample), CH_VOICE, voicetype); } void MoveToTeam(entity client, float team_colour, float type)