/* server/clientfuncs.qc Used to communicate between server and client Copyright (C) 2021-2024 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() NotifyGameEnd = { #ifdef FTE WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); WriteByte(MSG_MULTICAST, EVENT_ENDGAME); multicast('0 0 0', MULTICAST_ALL); #endif // FTE } 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(float mode, entity to) ReportMapMode = { WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); WriteByte(MSG_MULTICAST, EVENT_MAPTYPE); WriteByte(MSG_MULTICAST, mode); msg_entity = to; multicast('0 0 0', MULTICAST_ONE); } 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); } void(string track_name) songegg = { WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); WriteByte(MSG_MULTICAST, EVENT_SONGPLAY); WriteString(MSG_MULTICAST, track_name); multicast('0 0 0', MULTICAST_ALL); }; #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 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 } void(entity who, float weapon) CL_SendWeaponFire = { float recoil_return_time = getWeaponRecoilReturn(weapon); vector weapon_recoil = GetWeaponRecoil(weapon); msg_entity = who; #ifdef FTE WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); WriteByte(MSG_MULTICAST, EVENT_WEAPONRECOIL); WriteCoord (MSG_MULTICAST, weapon_recoil_x); WriteCoord (MSG_MULTICAST, weapon_recoil_y); WriteCoord (MSG_MULTICAST, weapon_recoil_z); multicast('0 0 0', MULTICAST_ONE); #else WriteByte(MSG_ONE, SVC_WEAPONFIRE); WriteLong(MSG_ONE, recoil_return_time); // FIXME: A long for this is overkill. WriteCoord(MSG_ONE, weapon_recoil_x); WriteCoord(MSG_ONE, weapon_recoil_y); WriteCoord(MSG_ONE, weapon_recoil_z); #endif // FTE self.recoil_delay = 60/recoil_return_time + time; } 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 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 } #ifdef FTE void(entity who) grenade_pulse = { WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); WriteByte(MSG_MULTICAST, EVENT_GRENADEPULSE); msg_entity = who; multicast('0 0 0', MULTICAST_ONE); } #endif // FTE #ifdef FTE void(entity who) nzp_bettyprompt = { WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); WriteByte(MSG_MULTICAST, EVENT_BETTYPROMPT); msg_entity = who; multicast('0 0 0', MULTICAST_ONE); } #endif // FTE #ifdef FTE // // FTE_InterpolatePunchAngle(person) // Punch Angle interpolation from our native sourceports, // handled in SSQC for FTE. // void(entity person) FTE_InterpolatePunchAngle = { vector last_punchangle = person.punchangle; float lerp_factor = 0.9*frametime; // It's fine that this is server-dependent, since it's server-executed.. // Early-out: Let's not spam fabs calls if we have no punchangle. if (person.punchangle_x == 0 && person.punchangle_y == 0) return; // Begin interpolation. for(int i = 0; i < 2; i++) { float difference = last_punchangle[i] * lerp_factor; if (fabs(person.punchangle[i]) > 0.01) { if (person.punchangle[i] >= difference) person.punchangle[i] -= difference; else if (person.punchangle[i] <= -difference) person.punchangle[i] -= difference; else person.punchangle[i] = 0; } else { person.punchangle[i] = 0; } } }; // // FTE_UpdateDynamicFOV(person) // Context-sensitive adjustment of client's viewzoom // factor. // void(entity person) FTE_UpdateDynamicFOV = { // Early-out if client is not zoomed in at all // and viewzoom is correctly applied if (!person.zoom && person.viewzoom == 1) return; // Calculate viewzoom FOV differential float viewzoom_differential = 1 - (0.018*GetWeaponZoomAmount(person.weapon)); float has_scoped_weapon = WepDef_HasSniperScore(person.weapon); // Sniper scope-in: Force-set as soon as the Scope draws if (person.zoom == 2) { person.viewzoom = viewzoom_differential; return; } // Force-set viewzoom back to one if client is holding a scoped // weapon and need their viewzoom adjusted. else if (person.viewzoom != 1 && has_scoped_weapon) { person.viewzoom = 1; return; } else if (has_scoped_weapon) { return; } // Utilize a sigmoid curve to minimize motion sickness. float sigmoid_input, sigmoid_output; float viewzoom_adjustment_rate; // If we're Aiming down the Sight and viewzoom has not reached it's end if (person.zoom == 1 && person.viewzoom != viewzoom_differential) { sigmoid_input = (person.viewzoom - viewzoom_differential) * 10; sigmoid_output = 1 / (1 + exp(-sigmoid_input)); viewzoom_adjustment_rate = 0.06 * (sigmoid_output + 0.5) * (frametime*20); if (person.viewzoom > viewzoom_differential) { person.viewzoom -= viewzoom_adjustment_rate; if (person.viewzoom < viewzoom_differential) person.viewzoom = viewzoom_differential; } else { person.viewzoom = viewzoom_differential; } } // If we're not Aiming down the Sight and viewzoom is not reset else if (person.zoom != 1 && person.viewzoom != 1) { // We can zoom out at a constant rate without consequence. person.viewzoom += 0.85 * frametime; if (person.viewzoom > 1) person.viewzoom = 1; } }; #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 } } #ifdef FTE void CSQC_SendChatMessage(float player_id, string message) { WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); WriteByte(MSG_MULTICAST, EVENT_CHATMESSAGE); WriteByte(MSG_MULTICAST, num_for_edict(self) - 1); WriteByte(MSG_MULTICAST, player_id); WriteString(MSG_MULTICAST, message); multicast('0 0 0', MULTICAST_ALL); } /* ================= Player_SendEntity Networks the player info ================= */ float Player_SendEntity( entity ePVEnt, float flChanged ) { WriteByte( MSG_ENTITY, 1 ); WriteCoord( MSG_ENTITY, self.origin_x ); // Position X WriteCoord( MSG_ENTITY, self.origin_y ); // Position Y WriteCoord( MSG_ENTITY, self.origin_z ); // Position Z WriteAngle( MSG_ENTITY, self.angles_x ); // Angle X WriteAngle( MSG_ENTITY, self.angles_y ); // Angle Y WriteAngle( MSG_ENTITY, self.angles_z ); // Angle Z WriteShort( MSG_ENTITY, self.velocity_x ); // Velocity X WriteShort( MSG_ENTITY, self.velocity_y ); // Velocity X WriteShort( MSG_ENTITY, self.velocity_z ); // Velocity X WriteByte( MSG_ENTITY, self.playernum ); // Player ID WriteShort( MSG_ENTITY, self.modelindex ); // Player Model WriteByte( MSG_ENTITY, self.frame ); // Player's Frame WriteShort( MSG_ENTITY, self.movetype ); // Player Movetype WriteShort( MSG_ENTITY, self.flags ); // Flags, important for physics WriteByte( MSG_ENTITY, self.stance ); // Player Stance WriteFloat( MSG_ENTITY, self.points ); // Player Score WriteShort( MSG_ENTITY, self.kills ); // Player Kills WriteByte( MSG_ENTITY, self.is_in_menu ); // Player is in a Menu State return TRUE; } #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 } } #ifdef FTE void(entity who, float version) nzp_setdoubletapver = { WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); WriteByte(MSG_MULTICAST, EVENT_DOUBLETAPUPDATE); WriteByte(MSG_MULTICAST, version); msg_entity = who; multicast('0 0 0', MULTICAST_ONE); }; #endif // FTE void(vector org) Effect_Fire = { #ifndef FTE particle (self.origin, v_up*8, 111, 0); #else WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); WriteByte(MSG_MULTICAST, EVENT_FLAME); WriteCoord(MSG_MULTICAST, org_x); WriteCoord(MSG_MULTICAST, org_y); WriteCoord(MSG_MULTICAST, org_z); multicast('0 0 0', MULTICAST_ALL); //te_flamejet(self.origin, v_up*8, 10); #endif // FTE }; // ***************************************** // Unrelated to engine, but custom functions // ***************************************** // "Removes" an entity that is placed back on restart. void(entity ent) Ent_FakeRemove = { ent.entity_removed = true; ent.oldmodel = ent.model; ent.oldorigin = ent.origin; ent.bbmins = ent.mins; ent.bbmaxs = ent.maxs; ent.sprintflag = ent.solid; ent.solid = SOLID_NOT; setsize(ent, '0 0 0', '0 0 0'); setmodel(ent, ""); } void(string modelname) Precache_Set = // Precache model, and set myself to it { modelname = Compat_ConvertOldAssetPath(modelname); precache_model(modelname); setmodel(self, modelname); }; float() crandom = { return 2*(random() - 0.5); } 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; }; float(entity them, entity me) PlayerIsLooking = { float ret = false; float old_solid = me.solid; me.solid = SOLID_BBOX; setorigin(me, me.origin); vector source; makevectors (them.v_angle); source = them.origin + them.view_ofs; // Standard 'are we facing' test.. traceline(source, source + v_forward*50, 0, them); // We're inside of an object.. is it the target? if (trace_startsolid) { if (v_forward*normalize(me.origin - them.origin) > 0.7) ret = true; } else if (trace_ent == me) { ret = true; } me.solid = old_solid; setorigin(me, me.origin); return ret; };