cstrike/src/shared/weapons_cstrike.qc

237 lines
6.6 KiB
Plaintext

/*
* Copyright (c) 2016-2020 Marco Cawthorne <marco@icculus.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* How Counter-Strike's accuracy works (from my understanding)
* this was deducted from the decrypted CS:S script files:
* https://gamebanana.com/gamefiles/2293
*
* Each and every bullet fired contributes to the shotmultiplier value,
* which decreases back to 0 slowly over time.
*
* Meanwhile, accuracy gets calculated by taking said value and dividing it
* by the weapon-specific divider. Each gun has a different one.
* The higher the divider value, the more accurate the weapon is in contrast
* to other weapons.
*/
var float autocvar_fcs_guns_recoil_strength = 1.0f;
var bool autocvar_fcs_guns_random_recoil_direction = TRUE;
var float autocvar_fcs_guns_movement_inaccuracy = 1.0f;
var float autocvar_fcs_guns_firing_inaccuracy = 1.0f;
weapontype_t
csweapon_ranged_type(player pl)
{
return WPNTYPE_RANGED;
}
weapontype_t
csweapon_melee_type(player pl)
{
return WPNTYPE_CLOSE;
}
float
Cstrike_CalculateMovementInaccuracy(player pl) {
float m = 1.0f;
float maxspeed = 250;
float speedlimit_low = maxspeed/3;
float speedlimit_high = maxspeed/2;
float current_speed = vlen(pl.velocity);
if (current_speed > speedlimit_low) {
if (current_speed > speedlimit_high) {
m = 2.25f;
} else {
m = 1.0f + (current_speed - speedlimit_low)/(speedlimit_high - speedlimit_low);
}
}
if (!(pl.flags & FL_ONGROUND)) {
m = 2.5f;
}else if (pl.flags & FL_CROUCHING) {
m *= 0.6f;
}
return bound(0.75f,m,2.5f);
}
/* called whenever a cstrike gun fires a successful shot */
void
Cstrike_ShotMultiplierAdd(player pl, float shots, float strength, float inaccuracy)
{
int r;
inaccuracy = bound(.95,inaccuracy*50+0.1,1.6);
if (pl.cs_shotmultiplier < 1.6) {
pl.cs_rec_reverse_chance = 0.95f;
pl.cs_prev_hor_rec = 0;
pl.cs_hor_rec_sign = 1;
if (autocvar_fcs_guns_random_recoil_direction && pseudorandom() >= 0.5) {
pl.cs_hor_rec_sign = -1;
}
}
pl.cs_rec_reverse_chance -= 0.02f;
if (pl.cs_rec_reverse_chance < 0.1f) {
pl.cs_rec_reverse_chance = 0.1f;
}
if (autocvar_fcs_guns_random_recoil_direction == 1) {
if (pseudorandom() > pl.cs_rec_reverse_chance) {
pl.cs_hor_rec_sign = -pl.cs_hor_rec_sign;
pl.cs_rec_reverse_chance = 0.92f;
}
} else {
if ( pl.cs_shotmultiplier > 9) {
pl.cs_hor_rec_sign = -1;
}
}
float new_shotmultiplier
= pl.cs_shotmultiplier
+ 1.3
+ shots
+ pl.cs_shotmultiplier/12*(.85+inaccuracy/11)
- pl.cs_shotmultiplier*pl.cs_shotmultiplier/100*(.95+inaccuracy/10);
pl.cs_shotmultiplier = bound(0, new_shotmultiplier, 30);
float bound_shotmultiplier = bound(0, pl.cs_shotmultiplier, 12);
pl.cs_shottime = 0.2f;
float movement_inaccuracy = bound(0.92,Cstrike_CalculateMovementInaccuracy(pl),1.25);
pl.punchangle[0] = -(pl.cs_shotmultiplier)*0.35*(strength*0.9+inaccuracy/9)+0.5;
pl.punchangle[0] -= autocvar_fcs_guns_firing_inaccuracy * (1 + 3*inaccuracy * inaccuracy)/19;
pl.punchangle[0] *= autocvar_fcs_guns_recoil_strength;
if (pl.cs_shotmultiplier < 5) {
//here we add extra punchangle for low multiplier values,
//so that tapping has more weight to it.
float extrapunch = bound(0.6f,shots*strength,0.8f);
pl.punchangle[0] -= extrapunch;
}
float hor_recoil
= pl.cs_shotmultiplier/185*(1 + (pseudorandom() - 0.5)/3)
+ (pseudorandom() - 0.3)*inaccuracy*inaccuracy*0.5*autocvar_fcs_guns_firing_inaccuracy;
hor_recoil *= 1.2 * movement_inaccuracy * strength * autocvar_fcs_guns_recoil_strength;
if (pl.cs_hor_rec_sign > 0) {
pl.cs_prev_hor_rec += hor_recoil;
} else if (pl.cs_hor_rec_sign < 0) {
pl.cs_prev_hor_rec -= hor_recoil;
}
pl.punchangle[1] = pl.cs_prev_hor_rec;
r = (float)input_sequence % 5;
/*switch (r) {
case 1:
pl.punchangle[1] = -0.1;
break;
case 2:
pl.punchangle[1] = 0.25;
break;
case 3:
pl.punchangle[1] = -0.25;
break;
case 4:
pl.punchangle[1] = 0.5;
break;
case 5:
pl.punchangle[1] = 0.1;
break;
default:
pl.punchangle[1] = -0.5;
break;
}*/
}
/* generate an accuracy value that we'll pass onto TraceAttack */
float
Cstrike_CalculateAccuracy(player pl, float divisor, float movement_penalty=1)
{
float inacc = 0;
float m = Cstrike_CalculateMovementInaccuracy(pl);
if (m > 1) {
m *= movement_penalty;
}
if (divisor == -1) {
/* snipers shoot way less accurate overall. */
return (pl.viewzoom < 1.0f) ? (0.0f) : (0.05 * m);
} else {
inacc = pl.cs_shotmultiplier*(1.25 + pl.cs_shotmultiplier*pl.cs_shotmultiplier*0.3) * autocvar_fcs_guns_firing_inaccuracy;
inacc = inacc / divisor / 3100;
inacc = inacc * m;
if (m > 1) {
inacc += m * 0.0025*movement_penalty * autocvar_fcs_guns_movement_inaccuracy;
} else {
inacc += m * 0.0025;
}
return inacc;
}
}
void
Cstrike_BulletRecoil_ApplyPre(player pl, float strength) {
strength *= autocvar_fcs_guns_recoil_strength;
pl.v_angle += strength*pl.punchangle*(2 - pl.cs_shotmultiplier/100*0.2);
}
void
Cstrike_BulletRecoil_ApplyPost(player pl, float strength) {
strength *= autocvar_fcs_guns_recoil_strength;
pl.v_angle -= strength*pl.punchangle*(2 - pl.cs_shotmultiplier/100*0.2);
}
/* called whenever cstrike guns aren't firing */
void
Cstrike_ShotMultiplierUpdate(player pl)
{
if ((pl.cs_shotmultiplier > 0) && (pl.cs_shottime <= 0.0f)) {
pl.cs_shottime = pl.w_attack_next + 0.01;
pl.cs_shotmultiplier--;
}
pl.cs_shottime = max(0, pl.cs_shottime - input_timelength);
}
void
Cstrike_ShotReset(player pl)
{
pl.cs_shottime = 0.0f;
pl.cs_shotmultiplier = 0;
}
void
w_cstrike_weaponrelease(void)
{
player pl = (player)self;
pl.punchangle[1] *= 0.95;
Cstrike_ShotMultiplierUpdate(pl);
}
void
w_cstrke_switched(player pl)
{
Cstrike_ShotReset(pl);
}
#ifdef CLIENT
void
CStrikeView_UpdateGeomset(player pl)
{
if (getplayerkeyfloat(pl.entnum-1, "*team") == TEAM_CT) {
setcustomskin(pSeat->m_eViewModel, "", "geomset 0 2\ngeomset 1 1\n");
} else {
setcustomskin(pSeat->m_eViewModel, "", "geomset 0 1\ngeomset 1 2\n");
}
}
#endif