mirror of
https://github.com/nzp-team/quakec.git
synced 2024-12-11 21:22:03 +00:00
637 lines
No EOL
16 KiB
C++
637 lines
No EOL
16 KiB
C++
/*
|
|
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
|
|
|
|
*/
|
|
|
|
#ifdef FTE
|
|
|
|
//
|
|
// FTE_RunParticleEffect(target, particle_type, position, optional_field, optional_entity)
|
|
// Fires a CSQC_EVENT_PARTICLE to clients specified (or
|
|
// world for global).
|
|
//
|
|
void(entity target, float particle_type, vector position, float optional_field, entity optional_entity) FTE_RunParticleEffect =
|
|
{
|
|
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
|
WriteByte(MSG_MULTICAST, CSQC_EVENT_PARTICLE);
|
|
WriteByte(MSG_MULTICAST, particle_type);
|
|
WriteCoord(MSG_MULTICAST, position_x);
|
|
WriteCoord(MSG_MULTICAST, position_y);
|
|
WriteCoord(MSG_MULTICAST, position_z);
|
|
WriteByte(MSG_MULTICAST, optional_field);
|
|
WriteEntity(MSG_MULTICAST, optional_entity);
|
|
|
|
if (target != world) {
|
|
msg_entity = target;
|
|
multicast(position, MULTICAST_ONE);
|
|
} else {
|
|
multicast(position, MULTICAST_ALL);
|
|
}
|
|
};
|
|
|
|
#endif // FTE
|
|
|
|
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, CSQC_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, CSQC_EVENT_MUSICSTREAM);
|
|
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
|
|
|
|
FTE_RunParticleEffect(world, CSQC_PART_EXPLOSION, org, 0, world);
|
|
|
|
#endif // FTE
|
|
}
|
|
|
|
#ifdef FTE
|
|
|
|
//
|
|
// FTE_IncrementRound()
|
|
// Alerts all clients of new value for Rounds.
|
|
// -- RELIABLE --
|
|
//
|
|
void(float round) FTE_IncrementRound =
|
|
{
|
|
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
|
WriteByte(MSG_MULTICAST, CSQC_EVENT_ROUNDCHANGE);
|
|
WriteByte(MSG_MULTICAST, round);
|
|
multicast('0 0 0', MULTICAST_ALL_R);
|
|
};
|
|
|
|
|
|
#endif // FTE
|
|
|
|
void(float player_index, float state) ChangeReviveIconState =
|
|
{
|
|
#ifdef FTE
|
|
|
|
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
|
WriteByte(MSG_MULTICAST, EVENT_REVIVECHANGE);
|
|
WriteByte(MSG_MULTICAST, player_index);
|
|
WriteByte(MSG_MULTICAST, state);
|
|
multicast('0 0 0', MULTICAST_ALL);
|
|
|
|
#endif // FTE
|
|
}
|
|
|
|
void(float player_index) EnableReviveIcon =
|
|
{
|
|
#ifdef FTE
|
|
|
|
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
|
WriteByte(MSG_MULTICAST, EVENT_REVIVEON);
|
|
WriteByte(MSG_MULTICAST, player_index);
|
|
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 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;
|
|
}
|
|
|
|
#ifdef FTE
|
|
|
|
//
|
|
// FTE_BroadcastMessage(target, broadcast_type, broadcast_type, player_id)
|
|
// Sends a CSQC Event to display text on the screen for the
|
|
// desired clients. Use 'world' to send to everyone.
|
|
// -- RELIABLE --
|
|
//
|
|
void(entity target, float broadcast_type, float broadcast_time, float player_id) FTE_BroadcastMessage =
|
|
{
|
|
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
|
WriteByte(MSG_MULTICAST, CSQC_EVENT_BROADCASTMESSAGE);
|
|
WriteByte(MSG_MULTICAST, broadcast_type);
|
|
WriteByte(MSG_MULTICAST, broadcast_time);
|
|
WriteByte(MSG_MULTICAST, player_id);
|
|
|
|
if (target != world) {
|
|
msg_entity = target;
|
|
multicast('0 0 0', MULTICAST_ONE_R);
|
|
} else {
|
|
multicast('0 0 0', MULTICAST_ALL_R);
|
|
}
|
|
};
|
|
|
|
//
|
|
// nzp_screenflash(target, color, duration, type)
|
|
// FTE equivalent of nzp_screenflash builtin.
|
|
//
|
|
void(entity target, float color, float duration, float type) nzp_screenflash =
|
|
{
|
|
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
|
WriteByte(MSG_MULTICAST, CSQC_EVENT_SCREENFLASH);
|
|
WriteByte(MSG_MULTICAST, color);
|
|
WriteByte(MSG_MULTICAST, duration);
|
|
WriteByte(MSG_MULTICAST, type);
|
|
|
|
if (target != world) {
|
|
msg_entity = target;
|
|
multicast('0 0 0', MULTICAST_ONE);
|
|
} else {
|
|
multicast('0 0 0', MULTICAST_ALL);
|
|
}
|
|
};
|
|
|
|
//
|
|
// nzp_rumble(target, low_frequency, high_frequency, duration)
|
|
// FTE equivalent of the nzp_rumble builtin.
|
|
//
|
|
void(entity target, float low_frequency, float high_frequency, float duration) nzp_rumble =
|
|
{
|
|
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
|
WriteByte(MSG_MULTICAST, CSQC_EVENT_RUMBLE);
|
|
WriteShort(MSG_MULTICAST, low_frequency);
|
|
WriteShort(MSG_MULTICAST, high_frequency);
|
|
WriteShort(MSG_MULTICAST, duration);
|
|
|
|
msg_entity = target;
|
|
multicast('0 0 0', MULTICAST_ONE);
|
|
};
|
|
|
|
#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
|
|
}
|
|
|
|
#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
|
|
|
|
//
|
|
// nzp_maxammo()
|
|
// FTE equivalent of nzp_maxammo builtin.
|
|
//
|
|
void() nzp_maxammo =
|
|
{
|
|
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
|
WriteByte(MSG_MULTICAST, CSQC_EVENT_MAXAMMOTEXT);
|
|
multicast('0 0 0', MULTICAST_ALL);
|
|
};
|
|
|
|
//
|
|
// nzp_setplayername(target, player_name)
|
|
// FTE equivalent of nzp_setplayername builtin.
|
|
//
|
|
void(entity target, string player_name) nzp_setplayername =
|
|
{
|
|
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
|
WriteByte(MSG_MULTICAST, CSQC_EVENT_PLAYERNAME);
|
|
WriteString(MSG_MULTICAST, player_name);
|
|
msg_entity = target;
|
|
multicast('0 0 0', MULTICAST_ONE);
|
|
};
|
|
|
|
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, CSQC_EVENT_GIVEACHIEVEMENT);
|
|
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, CSQC_EVENT_GIVEACHIEVEMENT);
|
|
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;
|
|
}
|
|
|
|
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
|
|
|
|
FTE_RunParticleEffect(world, CSQC_PART_FIRE, org, 0, world);
|
|
|
|
#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);
|
|
}
|
|
|
|
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;
|
|
}; |