quakec/source/server/clientfuncs.qc
Steam Deck User 12a2633738 CLIENT/SERVER: Misc. Weapon Spread improvements
Adds proper gun recoil to FTE, as well as moves spread calculation to be
relative to crosshairs. CSQC's crosshair values are now accurate to
World at War as well. Weapons are also much more precise ADS, so the Kar
is more viable. Shotguns also no longer reduce spread when ADS, in
parity with World at War :^)
2023-03-02 22:06:26 -05:00

711 lines
17 KiB
C++

/*
server/clientfuncs.qc
Used to communicate between server and client
Copyright (C) 2021-2022 NZ:P Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
void SetUpdate(entity client, float type, float val1, float val2, float val3)
{
#ifdef FTE
if (type != 2)
{
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_UPDATE);
WriteByte(MSG_MULTICAST, type); // UT_HUD
WriteByte(MSG_MULTICAST, val1);
WriteByte(MSG_MULTICAST, val2);
WriteByte(MSG_MULTICAST, val3); // misc flags/vars for later if needed
msg_entity = client;
multicast('0 0 0', MULTICAST_ONE);
}
else
{
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_UPDATE);
WriteByte(MSG_MULTICAST, type); // UT_ROUNDS_CHANGE
WriteByte(MSG_MULTICAST, val1);
WriteByte(MSG_MULTICAST, val2);
WriteByte(MSG_MULTICAST, val3); // misc flags/vars for later if needed
multicast('0 0 0', MULTICAST_ALL);
}
#endif // FTE
}
#ifdef FTE
void(entity to, float type, float cost, float weapon) useprint = {
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_USEPRINT);
WriteByte(MSG_MULTICAST, type);
WriteShort(MSG_MULTICAST, cost);
WriteByte(MSG_MULTICAST, weapon);
msg_entity = to;
multicast('0 0 0', MULTICAST_ONE);
}
#endif // FTE
void(vector org) CallExplosion = {
#ifndef FTE
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
WriteByte (MSG_BROADCAST, TE_EXPLOSION);
WriteCoord (MSG_BROADCAST, org_x);
WriteCoord (MSG_BROADCAST, org_y);
WriteCoord (MSG_BROADCAST, org_z);
#else
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_EXPLOSION);
WriteCoord(MSG_MULTICAST, org_x);
WriteCoord(MSG_MULTICAST, org_y);
WriteCoord(MSG_MULTICAST, org_z);
multicast('0 0 0', MULTICAST_ALL);
#endif // FTE
}
void NotifyNewRound(float to) {
#ifdef FTE
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_NEWROUND);
WriteByte(MSG_MULTICAST, to);
multicast('0 0 0', MULTICAST_ALL);
#endif // FTE
}
void SetRound(entity client, float to) {
#ifdef FTE
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_SETROUND);
WriteByte(MSG_MULTICAST, to);
msg_entity = client;
multicast('0 0 0', MULTICAST_ONE);
#endif // FTE
}
void SetPerk(entity client, float to)
{
#ifdef FTE
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_PERK);
WriteLong(MSG_MULTICAST, to);
msg_entity = client;
multicast('0 0 0', MULTICAST_ONE);
#endif // FTE
}
void(float to) SwitchWeapon =
{
#ifdef FTE
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_WEAPONCHANGE);
WriteByte(MSG_MULTICAST, to);
msg_entity = self;
multicast('0 0 0', MULTICAST_ONE);
// hotfix for weapon2models not reseting
self.weapon2model = GetWeapon2Model(to);
#endif // FTE
}
void(string to, float skin) UpdateVmodel =
{
#ifdef FTE
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_UPDATEVMODEL);
WriteString(MSG_MULTICAST, to);
WriteByte(MSG_MULTICAST, skin);
msg_entity = self;
multicast('0 0 0', MULTICAST_ONE);
#endif // FTE
}
void(string to, float skin) UpdateV2model =
{
#ifdef FTE
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_UPDATEV2MODEL);
WriteString(MSG_MULTICAST, to);
WriteByte(MSG_MULTICAST, skin);
msg_entity = self;
multicast('0 0 0', MULTICAST_ONE);
#endif // FTE
}
void(float index, float state) ChangeReviveIconState =
{
#ifdef FTE
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_REVIVECHANGE);
WriteByte(MSG_MULTICAST, index);
WriteByte(MSG_MULTICAST, state);
multicast('0 0 0', MULTICAST_ALL);
#endif // FTE
}
void(float index, vector org) EnableReviveIcon =
{
#ifdef FTE
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_REVIVEON);
WriteByte(MSG_MULTICAST, index);
WriteCoord(MSG_MULTICAST, org_x);
WriteCoord(MSG_MULTICAST, org_y);
WriteCoord(MSG_MULTICAST, org_z);
multicast('0 0 0', MULTICAST_ALL);
#endif // FTE
}
void(float index) DisableReviveIcon =
{
#ifdef FTE
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_REVIVEOFF);
WriteByte(MSG_MULTICAST, index);
multicast('0 0 0', MULTICAST_ALL);
#endif // FTE
}
void(entity who, float broadcast_time, float type, string str) BroadcastMessageToClient =
{
#ifdef FTE
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_BROADCAST);
WriteByte(MSG_MULTICAST, broadcast_time);
WriteByte(MSG_MULTICAST, type);
WriteString(MSG_MULTICAST, str);
msg_entity = who;
multicast('0 0 0', MULTICAST_ONE);
#endif // FTE
}
// FIXME: basically a copy of CL_SendWeaponFire but for FTE
void(entity who, float weapon) SendWeaponRecoil =
{
#ifdef FTE
vector Wep_Recoil;
Wep_Recoil = GetWeaponRecoil(weapon);
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_WEAPONRECOIL);
WriteCoord (MSG_MULTICAST, Wep_Recoil_x);
WriteCoord (MSG_MULTICAST, Wep_Recoil_y);
WriteCoord (MSG_MULTICAST, Wep_Recoil_z);
msg_entity = who;
multicast('0 0 0', MULTICAST_ONE);
#endif // FTE
}
void(float broadcast_time, float type, string str) BroadcastMessage =
{
#ifdef FTE
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_BROADCAST);
WriteByte(MSG_MULTICAST, broadcast_time);
WriteByte(MSG_MULTICAST, type);
WriteString(MSG_MULTICAST, str);
multicast('0 0 0', MULTICAST_ALL);
#endif // FTE
}
void(float playernum, float points, float am, float kills, string name, entity person) UpdatePlayerPoints =
{
#ifdef FTE
if (player_count == 0) {
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_POINTUPDATE);
WriteByte(MSG_MULTICAST, playernum);
WriteLong(MSG_MULTICAST, points);
WriteLong(MSG_MULTICAST, am);
WriteLong(MSG_MULTICAST, kills);
WriteString(MSG_MULTICAST, name);
msg_entity = person;
multicast('0 0 0', MULTICAST_ONE);
}
else {
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_POINTUPDATE);
WriteByte(MSG_MULTICAST, playernum);
WriteLong(MSG_MULTICAST, points);
WriteLong(MSG_MULTICAST, am);
WriteLong(MSG_MULTICAST, kills);
WriteString(MSG_MULTICAST, name);
multicast('0 0 0', MULTICAST_ALL);
}
#endif // FTE
}
void(float count) UpdatePlayerCount = {
#ifdef FTE
if (count == 0)
return;
else {
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_PLAYERUPDATE);
WriteByte(MSG_MULTICAST, count);
multicast('0 0 0', MULTICAST_ALL);
}
#endif // FTE
}
void(float newtime, float newtype, entity change) PromptLevelChange =
{
#ifdef FTE
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_BLACKOUT);
WriteByte(MSG_MULTICAST, newtime);
WriteByte(MSG_MULTICAST, newtype);
msg_entity = change;
multicast('0 0 0', MULTICAST_ONE);
#endif // FTE
}
void(entity who) UpdatePunchangle =
{
#ifdef FTE
// naievil -- shit logic lol...but result looks clean as fuck...
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_PUNCHANGLE);
WriteCoord(MSG_MULTICAST, who.punchangle_x);
WriteCoord(MSG_MULTICAST, who.punchangle_y);
WriteCoord(MSG_MULTICAST, who.punchangle_z);
msg_entity = who;
multicast('0 0 0', MULTICAST_ONE);
vector tempv = who.punchangle;
if (fabs(who.punchangle_x) > 0.01) {
if (who.punchangle_x >= 0.05*tempv_x)
who.punchangle_x -= 0.05*tempv_x;
else if (who.punchangle_x <= -0.05*tempv_x)
who.punchangle_x -= 0.05*tempv_x;
else
who.punchangle_x = 0;
} else
who.punchangle_x = 0;
if (fabs(who.punchangle_y) > 0.01) {
if (who.punchangle_y >= 0.05*tempv_y)
who.punchangle_y -= 0.05*tempv_y;
else if (who.punchangle_y <= -0.05*tempv_y)
who.punchangle_y -= 0.05*tempv_y;
else
who.punchangle_y = 0;
} else
who.punchangle_y = 0;
if (fabs(who.punchangle_z) > 0.01) {
if (who.punchangle_z >= 0.05*tempv_z)
who.punchangle_z -= 0.05*tempv_z;
else if (who.punchangle_z <= -0.05*tempv_z)
who.punchangle_z -= 0.05*tempv_z;
else
who.punchangle_z = 0;
} else
who.punchangle_z = 0;
#endif // FTE
}
#ifdef FTE
void(string h, float h2, entity who) pushHUD = {
if (player_count == 0) {
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_HUDUPDATE);
WriteString(MSG_MULTICAST, h);
WriteByte(MSG_MULTICAST, h2);
msg_entity = who;
multicast('0 0 0', MULTICAST_ONE);
} else {
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_WEAPONUPDATE);
WriteString(MSG_MULTICAST, h);
WriteByte(MSG_MULTICAST, h2);
multicast('0 0 0', MULTICAST_ALL);
}
}
void (float wepnum, string wepname, string wvmodel, float mag, float reserve, string ads, float min, float max, string flash, float flashsize, string v2, float isd, entity who) sendCustomWeapon = {
if (player_count == 0) {
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_WEAPONUPDATE);
WriteByte(MSG_MULTICAST, wepnum);
WriteString(MSG_MULTICAST, wepname);
WriteString(MSG_MULTICAST, wvmodel);
WriteByte(MSG_MULTICAST, mag);
WriteByte(MSG_MULTICAST, reserve);
WriteString(MSG_MULTICAST, ads);
WriteByte(MSG_MULTICAST, min);
WriteByte(MSG_MULTICAST, max);
WriteString(MSG_MULTICAST, flash);
WriteByte(MSG_MULTICAST, flashsize);
WriteString(MSG_MULTICAST, v2);
WriteByte(MSG_MULTICAST, isd);
msg_entity = who;
multicast('0 0 0', MULTICAST_ONE);
} else {
WriteByte(MSG_ALL, SVC_CGAMEPACKET);
WriteByte(MSG_ALL, EVENT_WEAPONUPDATE);
WriteByte(MSG_ALL, wepnum);
WriteString(MSG_ALL, wepname);
WriteString(MSG_ALL, wvmodel);
WriteByte(MSG_ALL, mag);
WriteByte(MSG_ALL, reserve);
WriteString(MSG_ALL, ads);
WriteByte(MSG_ALL, min);
WriteByte(MSG_ALL, max);
WriteString(MSG_ALL, flash);
WriteByte(MSG_ALL, flashsize);
WriteString(MSG_ALL, v2);
WriteByte(MSG_ALL, isd);
}
}
void(string msg, entity who) ScrollText = {
if (player_count == 0) {
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_SCROLLTEXT);
WriteString(MSG_MULTICAST, msg);
msg_entity = who;
multicast('0 0 0', MULTICAST_ONE);
} else {
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_SCROLLTEXT);
WriteString(MSG_MULTICAST, msg);
multicast('0 0 0', MULTICAST_ALL);
}
}
void(string chaptertitle, string location, string date, string person, entity who) WorldText = {
if (player_count == 0) {
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_WORLDDATA);
WriteString(MSG_MULTICAST, chaptertitle);
WriteString(MSG_MULTICAST, location);
WriteString(MSG_MULTICAST, date);
WriteString(MSG_MULTICAST, person);
msg_entity = who;
multicast('0 0 0', MULTICAST_ONE);
} else {
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_WORLDDATA);
WriteString(MSG_MULTICAST, chaptertitle);
WriteString(MSG_MULTICAST, location);
WriteString(MSG_MULTICAST, date);
WriteString(MSG_MULTICAST, person);
multicast('0 0 0', MULTICAST_ALL);
}
}
#endif // FTE
void (float achievement_id, optional entity who) GiveAchievement =
{
#ifndef FTE
// temp
if (achievement_id > 4)
return;
#endif // FTE
// this is an achievement specific to an individual
if ((who && who != world) || player_count == 0) {
if (player_count == 0) who = find(world, classname, "player");
#ifndef FTE
achievement(who, achievement_id);
#else
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_ACHIEVEMENT);
WriteByte(MSG_MULTICAST, achievement_id);
msg_entity = who;
multicast('0 0 0', MULTICAST_ONE);
#endif // FTE
} else {
#ifndef FTE
entity players;
players = find(world, classname, "player");
while(players != world) {
achievement(players, achievement_id);
players = find(players, classname, "player");
}
#else
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_ACHIEVEMENT);
WriteByte(MSG_MULTICAST, achievement_id);
multicast('0 0 0', MULTICAST_ALL);
#endif // FTE
}
}
void (float achievement_id, float progress_value, optional entity who) UpdateAchievementProgress =
{
#ifndef FTE
// temp
if (achievement_id > 4)
return;
#endif // FTE
// this is a progress update specific to an individual
if ((who && who != world) || player_count == 0) {
if (player_count == 0) who = find(world, classname, "player");
#ifndef FTE
//achievement_progress(who, achievement_id, progress_value);
#else
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_ACHIEVEMENTPROGRESS);
WriteByte(MSG_MULTICAST, achievement_id);
WriteFloat(MSG_MULTICAST, progress_value);
msg_entity = who;
multicast('0 0 0', MULTICAST_ONE);
#endif // FTE
} else {
#ifndef FTE
entity players;
players = find(world, classname, "player");
while(players != world) {
//achievement_progress(players, achievement_id, progress_value);
players = find(players, classname, "player");
}
#else
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_ACHIEVEMENTPROGRESS);
WriteByte(MSG_MULTICAST, achievement_id);
WriteFloat(MSG_MULTICAST, progress_value);
multicast('0 0 0', MULTICAST_ALL);
#endif // FTE
}
}
// *****************************************
// Unrelated to engine, but custom functions
// *****************************************
void(string modelname) Precache_Set = // Precache model, and set myself to it
{
precache_model(modelname);
setmodel(self, modelname);
};
float() crandom =
{
return 2*(random() - 0.5);
}
void WeaponSwitch(entity player) {
float wep, cmag, cmag2, cammo;
wep = other.weapon;
other.weapon = other.secondaryweapon;
other.secondaryweapon = wep;
cmag = other.currentmag;
other.currentmag = other.secondarymag;
other.secondarymag = cmag;
cmag2 = other.currentmag2;
other.currentmag2 = other.secondarymag2;
other.secondarymag2 = cmag2;
cammo = other.currentammo;
other.currentammo = other.secondaryammo;
other.secondaryammo = cammo;
entity tempe = self;
self = player;
SwitchWeapon(other.weapon);
self = tempe;
}
void(entity person, float expamt, float doublepoint) addmoney =
{
if (person.classname != "player" || person.downed)
return;
if (expamt > 0 && doublepoint == TRUE && x2_finished > time) {
expamt *= 2;
person.score += expamt;
}
// Combine the positive score with the powerup score tally
if (expamt > 0)
total_powerup_points += expamt;
person.points += expamt;
UpdatePlayerPoints(person.playernum, person.points, expamt, person.kills, person.netname, person);
};
//
// Util_GetPlayerAmmoInSlot(person, slot)
// Returns the reserve ammo the player has in a weapon slot.
//
float(entity person, float slot) Util_GetPlayerAmmoInSlot =
{
switch(slot) {
case 1: return person.currentammo; break;
case 2: return person.secondaryammo; break;
case 3: return person.thirdammo; break;
default: return 0; break;
}
}
//
// Util_SetPlayerAmmoInSlot(person, slot, ammo)
// Sets the player's reserve ammo in a slot to param3.
//
void(entity person, float slot, float ammo) Util_SetPlayerAmmoInSlot =
{
switch(slot) {
case 1: person.currentammo = ammo; break;
case 2: person.secondaryammo = ammo; break;
case 3: person.thirdammo = ammo; break;
default: return; break;
}
};
//
// Util_PlayerHasWeapon(peron, comparison, include_pap)
// Checks to see if the Player is holding a weapon in any
// of their three slots. `include_pap` dictates whether to
// consider Pack-A-Punch'd varients. Returns 1, 2, 3 depending
// on the slot the weapon is contained in.
//
float(entity person, float comparison, float include_pap) Util_PlayerHasWeapon =
{
// Storage.
float first, second, third;
// If we're including pap'd weapons, just convert the weapon set to the base
// ones to save on comparison checks.
if (include_pap == true) {
first = EqualNonPapWeapon(person.weapon);
second = EqualNonPapWeapon(person.secondaryweapon);
third = EqualNonPapWeapon(person.thirdweapon);
} else {
first = person.weapon;
second = person.secondaryweapon;
third = person.thirdweapon;
}
// Now do the comparisons
if (first == comparison)
return 1;
if (second == comparison)
return 2;
if (third == comparison)
return 3;
return 0;
};
//
// Util_WeaponIsSemiAutomatic(float weapon)
// Checks weapon firetypes and returns true if intended
// to be semi-automatic.
//
float(float weapon) Util_WeaponIsSemiAutomatic =
{
float firetype = GetFiretype(weapon);
// Valid firetypes
if (firetype == FIRETYPE_SEMIAUTO || firetype == FIRETYPE_GRENADE ||
firetype == FIRETYPE_TESLA)
return true;
return false;
}
//
// Util_WeaponFiresTraceshot(weapon)
// Simply returns true if given weapon's firetype requires a
// firetrace to be used properly.
//
float(float weapon) Util_WeaponFiresTraceshot =
{
float firetype = GetFiretype(weapon);
// Valid firetypes
if (firetype == FIRETYPE_FULLAUTO || firetype == FIRETYPE_SEMIAUTO)
return true;
return false;
}