1131 lines
26 KiB
Plaintext
1131 lines
26 KiB
Plaintext
/*
|
|
* Copyright (c) 2016-2021 Marco Hladik <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.
|
|
*/
|
|
|
|
void
|
|
CSQC_UpdateSeat(void)
|
|
{
|
|
int s = (float)getproperty(VF_ACTIVESEAT);
|
|
pSeat = &g_seats[s];
|
|
pSeatLocal = &g_seatslocal[s];
|
|
}
|
|
|
|
/* This file houses all of the callbacks and entry points the engine
|
|
calls by itself */
|
|
void
|
|
CSQC_Init(float apilevel, string enginename, float engineversion)
|
|
{
|
|
pSeat = &g_seats[0];
|
|
pSeatLocal = &g_seatslocal[0];
|
|
|
|
/* developer/debug commands */
|
|
registercommand("testLight");
|
|
registercommand("testPointLight");
|
|
registercommand("getpos");
|
|
registercommand("setpos");
|
|
registercommand("setang");
|
|
registercommand("dev_sentence");
|
|
registercommand("titles_test");
|
|
registercommand("buildcubemaps");
|
|
registercommand("dev_sunpos");
|
|
registercommand("dev_measure");
|
|
registercommand("view_geomtest");
|
|
registercommand("player_geomtest");
|
|
registercommand("way_menu");
|
|
registercommand("dev_explode");
|
|
|
|
/* basic actions */
|
|
registercommand("+attack");
|
|
registercommand("-attack");
|
|
registercommand("+attack2");
|
|
registercommand("-attack2");
|
|
registercommand("+reload");
|
|
registercommand("-reload");
|
|
registercommand("+use");
|
|
registercommand("-use");
|
|
registercommand("+duck");
|
|
registercommand("-duck");
|
|
|
|
/* voting */
|
|
registercommand("vote");
|
|
registercommand("callvote");
|
|
|
|
/* hud weapon selection system */
|
|
registercommand("slot1");
|
|
registercommand("slot2");
|
|
registercommand("slot3");
|
|
registercommand("slot4");
|
|
registercommand("slot5");
|
|
registercommand("slot6");
|
|
registercommand("slot7");
|
|
registercommand("slot8");
|
|
registercommand("slot9");
|
|
registercommand("slot10");
|
|
registercommand("lastinv");
|
|
registercommand("invnext");
|
|
registercommand("invprev");
|
|
|
|
/* scoreboard */
|
|
registercommand("+showscores");
|
|
registercommand("-showscores");
|
|
|
|
/* meant to be hidden */
|
|
registercommand("_fnchat_msg");
|
|
|
|
/* Requested by Slacer */
|
|
registercommand("+zoomin");
|
|
registercommand("-zoomin");
|
|
|
|
/* Sound shaders */
|
|
Sound_Init();
|
|
PropData_Init();
|
|
precache_sound("common/wpn_hudon.wav");
|
|
precache_sound("common/wpn_hudoff.wav");
|
|
precache_sound("common/wpn_moveselect.wav");
|
|
precache_sound("common/wpn_select.wav");
|
|
|
|
/* VGUI */
|
|
VGUI_Init();
|
|
|
|
/* Game specific inits */
|
|
ClientGame_Init(apilevel, enginename, engineversion);
|
|
EFX_Init();
|
|
Titles_Init();
|
|
Sentences_Init();
|
|
Decals_Init();
|
|
Way_Init();
|
|
|
|
/* let the menu know we're a multi or a singleplayer game */
|
|
if (serverkeyfloat("sv_playerslots") == 1)
|
|
cvar_set("_menu_singleplayer", "1");
|
|
else
|
|
cvar_set("_menu_singleplayer", "0");
|
|
}
|
|
|
|
/* Rendering Caches */
|
|
void
|
|
CSQC_RendererRestarted(string rstr)
|
|
{
|
|
/* Fonts */
|
|
Font_Load("fonts/font16.font", FONT_16);
|
|
Font_Load("fonts/font20.font", FONT_20);
|
|
Font_Load("fonts/fontcon.font", FONT_CON);
|
|
|
|
/* Particles */
|
|
PART_DUSTMOTE = particleeffectnum("volume.dustmote");
|
|
PART_BURNING = particleeffectnum("burn.burning");
|
|
|
|
/* 2D Pics */
|
|
precache_pic("gfx/vgui/icntlk_sv");
|
|
precache_pic("gfx/vgui/icntlk_pl");
|
|
|
|
/* View */
|
|
Chat_Init();
|
|
Weapons_Init();
|
|
Scores_Init();
|
|
View_Init();
|
|
ClientGame_RendererRestart(rstr);
|
|
HUD_Init();
|
|
|
|
/* GS-Entbase */
|
|
Fade_Init();
|
|
Decal_Reload();
|
|
Sky_Update(TRUE);
|
|
Entities_RendererRestarted();
|
|
DetailTex_Init();
|
|
}
|
|
|
|
/* this is so that profile_csqc reports more accurate statistics as to
|
|
what causes computation time */
|
|
void
|
|
CSQC_RenderScene(void)
|
|
{
|
|
renderscene();
|
|
}
|
|
|
|
void
|
|
CSQC_UpdateView(float w, float h, float focus)
|
|
{
|
|
player pl = __NULL__;
|
|
spectator spec;
|
|
int s;
|
|
|
|
if (w == 0 || h == 0) {
|
|
return;
|
|
} else {
|
|
/* First time we can effectively call VGUI
|
|
* because until now we don't know the video res.
|
|
*/
|
|
if (!video_res[0] && !video_res[1]) {
|
|
video_res[0] = w;
|
|
video_res[1] = h;
|
|
ClientGame_InitDone();
|
|
}
|
|
}
|
|
|
|
/* While the init above may have already happened,
|
|
people are able to resize windows dynamically too. */
|
|
video_res[0] = w;
|
|
video_res[1] = h;
|
|
|
|
Fog_Update();
|
|
Sky_Update(FALSE);
|
|
cvar_set("_background", serverkey("background"));
|
|
|
|
if (serverkeyfloat("background") == 1) {
|
|
setpause(FALSE);
|
|
}
|
|
|
|
if (g_iCubeProcess == TRUE) {
|
|
clearscene();
|
|
setproperty(VF_DRAWWORLD, TRUE);
|
|
setproperty(VF_DRAWENGINESBAR, FALSE);
|
|
setproperty(VF_DRAWCROSSHAIR, FALSE);
|
|
setproperty(VF_ENVMAP, "$whiteimage");
|
|
setproperty(VF_ORIGIN, g_vecCubePos);
|
|
setproperty(VF_AFOV, 90);
|
|
//setproperty(VF_CL_VIEWANGLES, [0,0,0]);
|
|
//setproperty(VF_ANGLES, [0,0,0]);
|
|
SkyCamera_Setup(g_vecCubePos);
|
|
CSQC_RenderScene();
|
|
return;
|
|
}
|
|
|
|
clearscene();
|
|
setproperty(VF_DRAWENGINESBAR, 0);
|
|
setproperty(VF_DRAWCROSSHAIR, 0);
|
|
|
|
//just in case...
|
|
if (numclientseats > g_seats.length) {
|
|
numclientseats = g_seats.length;
|
|
}
|
|
|
|
for (s = g_seats.length; s-- > numclientseats;) {
|
|
pSeat = &g_seats[s];
|
|
pSeatLocal = &g_seatslocal[s];
|
|
pSeat->m_ePlayer = world;
|
|
}
|
|
|
|
for (s = numclientseats; s-- > 0;) {
|
|
pSeat = &g_seats[s];
|
|
pSeatLocal = &g_seatslocal[s];
|
|
|
|
View_CalcViewport(s, w, h);
|
|
setproperty(VF_ACTIVESEAT, (float)s);
|
|
|
|
setproperty(VF_MIN, video_mins);
|
|
setproperty(VF_SIZE, video_res);
|
|
|
|
pSeat->m_ePlayer = self = findfloat(world, entnum, player_localentnum);
|
|
pl = (player)self;
|
|
|
|
if (!self) {
|
|
continue;
|
|
}
|
|
|
|
if (self.classname == "player") {
|
|
Predict_PlayerPreFrame(pl);
|
|
|
|
pSeat->m_vecPredictedOrigin = pl.origin;
|
|
pSeat->m_vecPredictedVelocity = pl.velocity;
|
|
pSeat->m_flPredictedFlags = pl.flags;
|
|
|
|
/* Don't hide the player entity */
|
|
if (autocvar_cl_thirdperson == TRUE && pl.health) {
|
|
setproperty(VF_VIEWENTITY, (float)0);
|
|
} else {
|
|
setproperty(VF_VIEWENTITY, (float)player_localentnum);
|
|
}
|
|
|
|
float oldzoom = pl.viewzoom;
|
|
if (pl.viewzoom == 1.0f) {
|
|
pl.viewzoom = 1.0 - (0.5 * pSeat->m_flZoomTime);
|
|
|
|
/* +zoomin requested by Slacer */
|
|
if (pSeat->m_iZoomed) {
|
|
pSeat->m_flZoomTime += clframetime * 15;
|
|
} else {
|
|
pSeat->m_flZoomTime -= clframetime * 15;
|
|
}
|
|
pSeat->m_flZoomTime = bound(0, pSeat->m_flZoomTime, 1);
|
|
}
|
|
|
|
setproperty(VF_AFOV, cvar("fov") * pl.viewzoom);
|
|
|
|
if (autocvar_zoom_sensitivity && pl.viewzoom < 1.0f) {
|
|
setsensitivityscaler(pl.viewzoom * autocvar_zoom_sensitivity);
|
|
} else {
|
|
setsensitivityscaler(pl.viewzoom);
|
|
}
|
|
|
|
if (pl.viewzoom <= 0.0f) {
|
|
setsensitivityscaler(1.0f);
|
|
}
|
|
|
|
pl.viewzoom = oldzoom;
|
|
|
|
View_PreDraw();
|
|
} else if (self.classname == "spectator") {
|
|
spec = (spectator)self;
|
|
Predict_SpectatorPreFrame(spec);
|
|
|
|
pSeat->m_vecPredictedOrigin = spec.origin;
|
|
pSeat->m_vecPredictedVelocity = spec.velocity;
|
|
pSeat->m_flPredictedFlags = spec.flags;
|
|
}
|
|
|
|
addentities(MASK_ENGINE);
|
|
|
|
if (pSeat->m_flCameraTime > time || pSeat->m_flCameraTime == -1) {
|
|
view_angles = pSeat->m_vecCameraAngle;
|
|
setproperty(VF_ORIGIN, pSeat->m_vecCameraOrigin);
|
|
setproperty(VF_CL_VIEWANGLES, view_angles);
|
|
setproperty(VF_ANGLES, view_angles);
|
|
} else {
|
|
if (getplayerkeyvalue(pl.entnum-1, "*spec") == "0") {
|
|
setproperty(VF_ORIGIN, pSeat->m_vecPredictedOrigin + pl.view_ofs);
|
|
|
|
if (pl.flags & FL_INVEHICLE) {
|
|
NSVehicle veh = (NSVehicle)pl.vehicle;
|
|
|
|
if (veh.UpdateView)
|
|
veh.UpdateView();
|
|
} else if (pl.health) {
|
|
if (autocvar_cl_thirdperson == TRUE) {
|
|
makevectors(view_angles);
|
|
vector vStart = [pSeat->m_vecPredictedOrigin[0], pSeat->m_vecPredictedOrigin[1], pSeat->m_vecPredictedOrigin[2] + 16] + (v_right * 4);
|
|
vector vEnd = vStart + (v_forward * -48) + [0,0,16] + (v_right * 4);
|
|
traceline(vStart, vEnd, FALSE, self);
|
|
setproperty(VF_ORIGIN, trace_endpos + (v_forward * 5));
|
|
}
|
|
}
|
|
|
|
if (pSeat->m_flShakeDuration > 0.0) {
|
|
vector vecShake = [0,0,0];
|
|
vecShake[0] += random() * 3;
|
|
vecShake[1] += random() * 3;
|
|
vecShake[2] += random() * 3;
|
|
pl.punchangle += (vecShake * pSeat->m_flShakeAmp) * (pSeat->m_flShakeDuration / pSeat->m_flShakeTime);
|
|
pSeat->m_flShakeDuration -= clframetime;
|
|
}
|
|
setproperty(VF_ANGLES, view_angles + pl.punchangle);
|
|
} else if (getplayerkeyvalue(pl.entnum-1, "*spec") == "1") {
|
|
spec = (spectator)self;
|
|
switch (spec.spec_mode) {
|
|
case SPECMODE_THIRDPERSON:
|
|
makevectors(view_angles);
|
|
vector vecStart;
|
|
vecStart[0] = pSeat->m_vecPredictedOrigin[0];
|
|
vecStart[1] = pSeat->m_vecPredictedOrigin[1];
|
|
vecStart[2] = pSeat->m_vecPredictedOrigin[2] + 16;
|
|
vecStart += (v_right * 4);
|
|
|
|
vector vecEnd = vecStart + (v_forward * -48) + [0,0,16] + (v_right * 4);
|
|
traceline(vecStart, vecEnd, FALSE, self);
|
|
setproperty(VF_ORIGIN, trace_endpos + (v_forward * 5));
|
|
break;
|
|
case SPECMODE_FIRSTPERSON:
|
|
entity c;
|
|
c = findfloat(world, ::entnum, spec.spec_ent);
|
|
|
|
if (c.classname == "player") {
|
|
player bp = (player)c;
|
|
removeentity(c);
|
|
setproperty(VF_ORIGIN, pSeat->m_vecPredictedOrigin + bp.view_ofs);
|
|
setproperty(VF_ANGLES, bp.v_angle);
|
|
setproperty(VF_CL_VIEWANGLES, bp.v_angle);
|
|
}
|
|
break;
|
|
default:
|
|
setproperty(VF_ORIGIN, pSeat->m_vecPredictedOrigin);
|
|
}
|
|
} else if (getplayerkeyvalue(pl.entnum-1, "*spec") == "2") {
|
|
setproperty(VF_ORIGIN, pSeat->m_vecPredictedOrigin);
|
|
|
|
if (pSeat->m_flShakeDuration > 0.0) {
|
|
vector vecShake = [0,0,0];
|
|
vecShake[0] += random() * 3;
|
|
vecShake[1] += random() * 3;
|
|
vecShake[2] += random() * 3;
|
|
pl.punchangle += (vecShake * pSeat->m_flShakeAmp) * (pSeat->m_flShakeDuration / pSeat->m_flShakeTime);
|
|
pSeat->m_flShakeDuration -= clframetime;
|
|
}
|
|
setproperty(VF_ANGLES, view_angles + pl.punchangle);
|
|
}
|
|
|
|
if (g_iIntermission) {
|
|
view_angles = pSeat->m_vecCameraAngle;
|
|
view_angles += [sin(time), sin(time * 2)];
|
|
setproperty(VF_ORIGIN, pSeat->m_vecCameraOrigin);
|
|
setproperty(VF_CL_VIEWANGLES, view_angles);
|
|
}
|
|
}
|
|
|
|
setproperty(VF_DRAWWORLD, 1);
|
|
SkyCamera_Setup(getproperty(VF_ORIGIN));
|
|
|
|
/* draw the viewmodel in a second pass if desired */
|
|
if (autocvar_r_viewmodelpass && pl.health > 0) {
|
|
CSQC_RenderScene();
|
|
clearscene();
|
|
setproperty(VF_MIN, video_mins);
|
|
setproperty(VF_SIZE, video_res);
|
|
setproperty(VF_ANGLES, view_angles + pl.punchangle);
|
|
setproperty(VF_DRAWWORLD, 0);
|
|
setproperty(VF_AFOV, autocvar_r_viewmodelfov);
|
|
setproperty(VF_ORIGIN, pSeat->m_vecPredictedOrigin + pl.view_ofs);
|
|
View_DrawViewModel();
|
|
} else if (pl.health > 0) {
|
|
View_DrawViewModel();
|
|
}
|
|
|
|
CSQC_RenderScene();
|
|
RenderTarget_Monitor_Update();
|
|
|
|
for (entity b = world; (b = findfloat(b, ::isCSQC, 1));) {
|
|
NSEntity pf = (NSEntity) b;
|
|
pf.postdraw();
|
|
}
|
|
|
|
Fade_Update((int)video_mins[0],(int)video_mins[1], (int)w, (int)h);
|
|
View_PostDraw();
|
|
|
|
if (g_iIntermission) {
|
|
Scores_Draw();
|
|
} else if (focus == TRUE) {
|
|
GameText_Draw();
|
|
PointMessage_Draw();
|
|
|
|
if (getplayerkeyvalue(pl.entnum-1, "*spec") == "0") {
|
|
HUD_Draw();
|
|
} else if (self.classname == "player") {
|
|
HUD_DrawSpectator();
|
|
}
|
|
|
|
Voice_DrawHUD();
|
|
Chat_Draw();
|
|
Print_Draw();
|
|
|
|
/* no prints overlapping scoreboards */
|
|
if (pSeat->m_iScoresVisible == TRUE) {
|
|
Scores_Draw();
|
|
} else {
|
|
VGUI_Draw();
|
|
Print_DrawCenterprint();
|
|
}
|
|
}
|
|
|
|
if (self.classname == "player")
|
|
Predict_PlayerPostFrame((player)self);
|
|
else if (self.classname == "spectator")
|
|
Predict_SpectatorPostFrame((spectator)self);
|
|
}
|
|
|
|
EFX_UpdateListener();
|
|
DSP_UpdateSoundscape();
|
|
|
|
if (autocvar_s_al_debug)
|
|
EFX_DebugInfo();
|
|
|
|
pSeat = __NULL__;
|
|
pSeatLocal = __NULL__;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CSQC_InputEvent
|
|
|
|
Updates all our input related globals for use in other functions
|
|
=================
|
|
*/
|
|
float
|
|
CSQC_InputEvent(float fEventType, float fKey, float fCharacter, float fDeviceID)
|
|
{
|
|
CSQC_UpdateSeat();
|
|
|
|
switch (fEventType) {
|
|
case IE_KEYDOWN:
|
|
break;
|
|
case IE_KEYUP:
|
|
break;
|
|
case IE_MOUSEABS:
|
|
mouse_pos[0] = fKey;
|
|
mouse_pos[1] = fCharacter;
|
|
break;
|
|
case IE_MOUSEDELTA:
|
|
mouse_pos[0] += fKey;
|
|
mouse_pos[1] += fCharacter;
|
|
|
|
if (mouse_pos[0] < 0) {
|
|
mouse_pos[0] = 0;
|
|
} else if (mouse_pos[0] > video_res[0]) {
|
|
mouse_pos[0] = video_res[0];
|
|
}
|
|
|
|
if (mouse_pos[1] < 0) {
|
|
mouse_pos[1] = 0;
|
|
} else if (mouse_pos[1] > video_res[1]) {
|
|
mouse_pos[1] = video_res[1];
|
|
}
|
|
break;
|
|
default:
|
|
return (1);
|
|
}
|
|
|
|
VGUI_Input(fEventType, fKey, fCharacter, fDeviceID);
|
|
|
|
if (g_vguiWidgetCount) {
|
|
setcursormode(TRUE, "gfx/cursor", [0,0,0], 1.0f);
|
|
} else {
|
|
setcursormode(FALSE, "gfx/cursor", [0,0,0], 1.0f);
|
|
}
|
|
|
|
if (VGUI_Active())
|
|
return (1);
|
|
else
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CSQC_Input_Frame
|
|
|
|
Hijacks and controls what input globals are being sent to the server
|
|
=================
|
|
*/
|
|
void
|
|
CSQC_Input_Frame(void)
|
|
{
|
|
CSQC_UpdateSeat();
|
|
|
|
/* If we are inside a VGUI, don't let the client do stuff outside */
|
|
if (VGUI_Active()) {
|
|
input_impulse = 0;
|
|
input_buttons = 0;
|
|
return;
|
|
}
|
|
|
|
/* background maps have no input */
|
|
if (serverkeyfloat("background") == 1)
|
|
return;
|
|
|
|
if (pSeat->m_iInputAttack2 == TRUE) {
|
|
input_buttons |= INPUT_BUTTON3;
|
|
}
|
|
|
|
if (pSeat->m_iInputReload == TRUE) {
|
|
input_buttons |= INPUT_BUTTON4;
|
|
}
|
|
|
|
if (pSeat->m_iInputUse == TRUE) {
|
|
input_buttons |= INPUT_BUTTON5;
|
|
}
|
|
|
|
if (pSeat->m_iInputDuck == TRUE) {
|
|
input_buttons |= INPUT_BUTTON8;
|
|
}
|
|
|
|
/* The HUD needs more time */
|
|
if (pSeat->m_iHUDWeaponSelected) {
|
|
if ((input_buttons & INPUT_BUTTON0))
|
|
HUD_DrawWeaponSelect_Trigger();
|
|
else if ((input_buttons & INPUT_BUTTON3))
|
|
pSeat->m_iHUDWeaponSelected = pSeat->m_flHUDWeaponSelectTime = 0;
|
|
|
|
pSeat->m_flInputBlockTime = time + 0.2;
|
|
}
|
|
|
|
/* prevent accidental input packets */
|
|
if (pSeat->m_flInputBlockTime > time) {
|
|
input_buttons &= ~INPUT_BUTTON0;
|
|
input_buttons &= ~INPUT_BUTTON3;
|
|
pSeat->m_iInputAttack2 = FALSE;
|
|
return;
|
|
}
|
|
|
|
/* compat*/
|
|
if (input_impulse == 201) {
|
|
sendevent("Spraylogo", "");
|
|
}
|
|
|
|
if (pSeat->m_flCameraTime > time) {
|
|
/* TODO: Supress the changing of view_angles/input_angles. */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
CSQC_Parse_Event
|
|
|
|
Whenever we call a SVC_CGAMEPACKET on the SSQC, this is being run
|
|
=================
|
|
*/
|
|
void
|
|
CSQC_Parse_Event(void)
|
|
{
|
|
/* always 0, unless it was sent with a MULTICAST_ONE or MULTICAST_ONE_R to p2+ */
|
|
CSQC_UpdateSeat();
|
|
|
|
float fHeader = readbyte();
|
|
|
|
switch (fHeader) {
|
|
case EV_DAMAGE:
|
|
vector vecDmgPos;
|
|
int iDmgTake;
|
|
int iDmgFlags;
|
|
vecDmgPos[0] = readcoord();
|
|
vecDmgPos[1] = readcoord();
|
|
vecDmgPos[2] = readcoord();
|
|
iDmgTake = readint();
|
|
iDmgFlags = readint();
|
|
CSQC_Parse_Damage_New(vecDmgPos, iDmgTake, iDmgFlags);
|
|
break;
|
|
case EV_INTERMISSION:
|
|
int cam;
|
|
vector pos, ang;
|
|
|
|
cam = (int)readbyte();
|
|
|
|
if (cam) {
|
|
ang[0] = readfloat();
|
|
ang[1] = readfloat();
|
|
ang[2] = readfloat();
|
|
|
|
pos[0] = readcoord();
|
|
pos[1] = readcoord();
|
|
pos[2] = readcoord();
|
|
} else {
|
|
pos = getproperty(VF_ORIGIN);
|
|
ang = getproperty(VF_ANGLES);
|
|
}
|
|
|
|
pSeat->m_vecCameraOrigin = pos;
|
|
pSeat->m_vecCameraAngle = ang;
|
|
g_iIntermission = TRUE;
|
|
break;
|
|
case EV_MUSICTRACK:
|
|
Music_ParseTrack();
|
|
break;
|
|
case EV_MUSICLOOP:
|
|
Music_ParseLoop();
|
|
break;
|
|
case EV_SPEAK:
|
|
string msg;
|
|
float pit;
|
|
entity t = findfloat(world, entnum, readentitynum());
|
|
msg = readstring();
|
|
pit = readfloat();
|
|
sound(t, CHAN_VOICE, msg, 1.0, ATTN_NORM, pit);
|
|
break;
|
|
case EV_SENTENCE:
|
|
NSTalkMonster_ParseSentence();
|
|
break;
|
|
case EV_HUDHINT:
|
|
string hint;
|
|
hint = readstring();
|
|
/* TODO: Handle the event properly */
|
|
Chat_Parse(sprintf("Hint: %s", hint));
|
|
break;
|
|
case EV_FADE:
|
|
Fade_Parse();
|
|
break;
|
|
case EV_SPRITE:
|
|
EnvSprite_ParseEvent();
|
|
break;
|
|
case EV_TEXT:
|
|
GameText_Parse();
|
|
break;
|
|
case EV_MESSAGE:
|
|
GameMessage_Parse();
|
|
break;
|
|
case EV_CAMERATRIGGER:
|
|
vector cam_newpos;
|
|
|
|
cam_newpos[0] = readcoord();
|
|
cam_newpos[1] = readcoord();
|
|
cam_newpos[2] = readcoord();
|
|
|
|
pSeat->m_vecCameraAngle[0] = readcoord();
|
|
pSeat->m_vecCameraAngle[1] = readcoord();
|
|
pSeat->m_vecCameraAngle[2] = readcoord();
|
|
|
|
pSeat->m_flCameraTime = time + readfloat();
|
|
|
|
/* if the same camera as last-time (hack) is still active,
|
|
then make sure it becomes inactive... */
|
|
if (pSeat->m_vecCameraOrigin == cam_newpos) {
|
|
pSeat->m_flCameraTime = 0.0f;
|
|
} else {
|
|
pSeat->m_vecCameraOrigin = cam_newpos;
|
|
}
|
|
break;
|
|
case EV_ANGLE:
|
|
vector a;
|
|
a[0] = readfloat();
|
|
a[1] = readfloat();
|
|
a[2] = readfloat();
|
|
setproperty(VF_CL_VIEWANGLES, a);
|
|
setproperty(VF_ANGLES, a);
|
|
break;
|
|
case EV_SHAKE:
|
|
if (self.classname == "spectator")
|
|
break;
|
|
pSeat->m_flShakeDuration = readfloat();
|
|
pSeat->m_flShakeAmp = readfloat();
|
|
pSeat->m_flShakeFreq = readfloat();
|
|
pSeat->m_flShakeTime = pSeat->m_flShakeDuration;
|
|
break;
|
|
default:
|
|
ClientGame_EventParse(fHeader);
|
|
}
|
|
}
|
|
|
|
float
|
|
CSQC_ConsoleCommand(string sCMD)
|
|
{
|
|
/* the engine will hide the p1 etc commands... which is fun... */
|
|
CSQC_UpdateSeat();
|
|
|
|
tokenize(sCMD);
|
|
|
|
/* give us a chance to override commands */
|
|
int ret = ClientGame_ConsoleCommand();
|
|
|
|
/* successful override */
|
|
if (ret == (1))
|
|
return (1);
|
|
|
|
switch (argv(0)) {
|
|
case "testPointLight":
|
|
makevectors(getproperty(VF_ANGLES));
|
|
traceline(getproperty(VF_ORIGIN), getproperty(VF_ORIGIN) + v_forward * 4096, FALSE, pSeat->m_ePlayer);
|
|
dynamiclight_spawnstatic(trace_endpos + (v_forward * -16), 1024, [1,1,1]);
|
|
break;
|
|
case "dev_explode":
|
|
makevectors(getproperty(VF_ANGLES));
|
|
traceline(getproperty(VF_ORIGIN), getproperty(VF_ORIGIN) + v_forward * 4096, FALSE, pSeat->m_ePlayer);
|
|
FX_Explosion(trace_endpos);
|
|
break;
|
|
case "dev_sunpos":
|
|
vector sunpos, sunang;
|
|
vector lepos, leang;
|
|
|
|
makevectors(getproperty(VF_ANGLES));
|
|
sunpos = v_forward * -1;
|
|
sunang = vectoangles(sunpos);
|
|
|
|
makevectors(getproperty(VF_ANGLES));
|
|
lepos = v_forward * -1;
|
|
leang = vectoangles(lepos);
|
|
leang[1] -= 180;
|
|
leang[0] *= -1;
|
|
|
|
localcmd(sprintf("r_shadows_throwdirection %v\n", sunpos));
|
|
print(sprintf("env_sun: pitch: %d; angle: %d\n", -sunang[0], sunang[1]));
|
|
print(sprintf("light_environment: sunangle: %d; pitch: %d\n", leang[1], leang[0]));
|
|
break;
|
|
case "dev_measure":
|
|
static vector measurepos;
|
|
if (!vlen(measurepos)) {
|
|
measurepos = getproperty(VF_ORIGIN);
|
|
CSQC_Parse_CenterPrint(sprintf( "First marker set at\n%v", measurepos));
|
|
} else {
|
|
CSQC_Parse_CenterPrint(sprintf("Distance: %d\n", vlen(measurepos - getproperty(VF_ORIGIN))));
|
|
measurepos = [0,0,0];
|
|
}
|
|
break;
|
|
case "vote":
|
|
if (argv(1) == "yes") {
|
|
sendevent("VoteY", "");
|
|
} else if (argv(1) == "no") {
|
|
sendevent("VoteN", "");
|
|
}
|
|
break;
|
|
case "getpos":
|
|
print(sprintf("setpos %v;setang -%v\n", getproperty(VF_ORIGIN), getproperty(VF_ANGLES)));
|
|
break;
|
|
case "setpos":
|
|
localcmd(sprintf("cmd setpos \"%s\"\n", argv(1)));
|
|
break;
|
|
case "setang":
|
|
setproperty(VF_CL_VIEWANGLES, stov(argv(1)));
|
|
setproperty(VF_ANGLES, stov(argv(1)));
|
|
break;
|
|
case "callvote":
|
|
sendevent("CallVote", "s", substring(sCMD, 9, strlen(sCMD)-9));
|
|
break;
|
|
case "+zoomin":
|
|
pSeat->m_iZoomed = TRUE;
|
|
break;
|
|
case "-zoomin":
|
|
pSeat->m_iZoomed = FALSE;
|
|
break;
|
|
case "buildcubemaps":
|
|
CMap_Build();
|
|
break;
|
|
case "titles_test":
|
|
GameMessage_Setup(argv(1), 0);
|
|
break;
|
|
case "+attack2":
|
|
pSeat->m_iInputAttack2 = TRUE;
|
|
break;
|
|
case "-attack2":
|
|
pSeat->m_iInputAttack2 = FALSE;
|
|
break;
|
|
case "+reload":
|
|
pSeat->m_iInputReload = TRUE;
|
|
break;
|
|
case "-reload":
|
|
pSeat->m_iInputReload = FALSE;
|
|
break;
|
|
case "+use":
|
|
pSeat->m_iInputUse = TRUE;
|
|
break;
|
|
case "-use":
|
|
pSeat->m_iInputUse = FALSE;
|
|
break;
|
|
case "+duck":
|
|
pSeat->m_iInputDuck = TRUE;
|
|
break;
|
|
case "-duck":
|
|
pSeat->m_iInputDuck = FALSE;
|
|
break;
|
|
case "invnext":
|
|
HUD_DrawWeaponSelect_Back();
|
|
break;
|
|
case "invprev":
|
|
HUD_DrawWeaponSelect_Forward();
|
|
break;
|
|
case "lastinv":
|
|
HUD_DrawWeaponSelect_Last();
|
|
break;
|
|
case "+showscores":
|
|
pSeat->m_iScoresVisible = TRUE;
|
|
break;
|
|
case "-showscores":
|
|
pSeat->m_iScoresVisible = FALSE;
|
|
break;
|
|
case "slot1":
|
|
HUD_SlotSelect(0);
|
|
break;
|
|
case "slot2":
|
|
HUD_SlotSelect(1);
|
|
break;
|
|
case "slot3":
|
|
HUD_SlotSelect(2);
|
|
break;
|
|
case "slot4":
|
|
HUD_SlotSelect(3);
|
|
break;
|
|
case "slot5":
|
|
HUD_SlotSelect(4);
|
|
break;
|
|
case "slot6":
|
|
HUD_SlotSelect(5);
|
|
break;
|
|
case "slot7":
|
|
HUD_SlotSelect(6);
|
|
break;
|
|
case "slot8":
|
|
HUD_SlotSelect(7);
|
|
break;
|
|
case "slot9":
|
|
HUD_SlotSelect(8);
|
|
break;
|
|
case "slot10":
|
|
HUD_SlotSelect(9);
|
|
break;
|
|
case "way_menu":
|
|
Way_Autoload();
|
|
Textmenu_Call("WAY_MENU");
|
|
break;
|
|
case "_fnchat_msg":
|
|
CSQC_Parse_Print(argv(1), PRINT_CHAT);
|
|
break;
|
|
case "view_geomtest":
|
|
Weapons_SetGeomset(sprintf("geomset %s %s\n", argv(1), argv(2)));
|
|
break;
|
|
case "player_geomtest":
|
|
setcustomskin(pSeat->m_ePlayer, "", sprintf("geomset %s %s\n", argv(1), argv(2)));
|
|
break;
|
|
default:
|
|
return (0);
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
void
|
|
CSQC_Parse_Print(string sMessage, float fLevel)
|
|
{
|
|
CSQC_UpdateSeat();
|
|
|
|
/* This gives messages other than chat an orange tint */
|
|
if (fLevel == PRINT_CHAT) {
|
|
Chat_Parse(sMessage);
|
|
return;
|
|
}
|
|
|
|
if (pSeat->m_iPrintLines < 4) {
|
|
pSeat->m_strPrintBuffer[pSeat->m_iPrintLines + 1] = sMessage;
|
|
pSeat->m_iPrintLines++;
|
|
} else {
|
|
for (int i = 0; i < 4; i++) {
|
|
pSeat->m_strPrintBuffer[i] = pSeat->m_strPrintBuffer[i + 1];
|
|
}
|
|
pSeat->m_strPrintBuffer[4] = sMessage;
|
|
}
|
|
|
|
pSeat->m_flPrintTime = time + CHAT_TIME;
|
|
|
|
// Log to console
|
|
localcmd(sprintf("echo \"%s\"\n", sMessage));
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
CSQC_Parse_CenterPrint
|
|
|
|
Catches every centerprint call and allows us to tinker with it.
|
|
That's how we are able to add color, alpha and whatnot.
|
|
Keep in mind that newlines need to be tokenized
|
|
=================
|
|
*/
|
|
float
|
|
CSQC_Parse_CenterPrint(string sMessage)
|
|
{
|
|
CSQC_UpdateSeat();
|
|
|
|
pSeat->m_iCenterprintLines = tokenizebyseparator(sMessage, "\n");
|
|
|
|
for (int i = 0; i < (pSeat->m_iCenterprintLines); i++) {
|
|
pSeat->m_strCenterprintBuffer[i] = sprintf("^xF80%s", argv(i));
|
|
}
|
|
|
|
pSeat->m_flCenterprintAlpha = 1;
|
|
pSeat->m_flCenterprintTime = time + 3;
|
|
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CSQC_Ent_Update
|
|
|
|
Called whenever an entity is sent manually via .SendFlags and so on
|
|
=================
|
|
*/
|
|
void
|
|
CSQC_Ent_Update(float new)
|
|
{
|
|
float t;
|
|
t = readbyte();
|
|
|
|
switch (t) {
|
|
case ENT_ENTITY:
|
|
NSEntity me = (NSEntity)self;
|
|
if (new) {
|
|
spawnfunc_NSEntity();
|
|
}
|
|
me.ReceiveEntity(new, readfloat());
|
|
break;
|
|
case ENT_ENTITYRENDERABLE:
|
|
NSRenderableEntity rend = (NSRenderableEntity)self;
|
|
if (new) {
|
|
spawnfunc_NSRenderableEntity();
|
|
}
|
|
rend.ReceiveEntity(new, readfloat());
|
|
break;
|
|
case ENT_MONSTER:
|
|
NSMonster_ReadEntity(new);
|
|
break;
|
|
case ENT_TALKMONSTER:
|
|
NSTalkMonster_ReadEntity(new);
|
|
break;
|
|
case ENT_VEHICLE:
|
|
basevehicle_readentity(new);
|
|
break;
|
|
case ENT_VEH_TANKMORTAR:
|
|
func_tankmortar_readentity(new);
|
|
break;
|
|
case ENT_VEH_4WHEEL:
|
|
prop_vehicle_driveable_readentity(new);
|
|
break;
|
|
case ENT_PLAYER:
|
|
player pl = (player)self;
|
|
|
|
/* splitscreen */
|
|
CSQC_UpdateSeat();
|
|
|
|
Predict_EntityUpdate(pl, new);
|
|
|
|
/* any differences in things that are read below are now
|
|
officially from prediction misses. */
|
|
float a = readfloat();
|
|
pl.ReceiveEntity(new, a);
|
|
break;
|
|
case ENT_SPECTATOR:
|
|
spectator spec = (spectator)self;
|
|
if (new || self.classname != "spectator") {
|
|
spawnfunc_spectator();
|
|
spec.classname = "spectator";
|
|
spec.solid = SOLID_SLIDEBOX;
|
|
spec.drawmask = MASK_ENGINE;
|
|
spec.customphysics = Empty;
|
|
setsize(spec, [0,0,0], [0,0,0]);
|
|
}
|
|
spec.ReceiveEntity(new);
|
|
break;
|
|
case ENT_SPRITE:
|
|
env_sprite spr = (env_sprite)self;
|
|
if (new) {
|
|
spawnfunc_env_sprite();
|
|
}
|
|
spr.ReceiveEntity(new, readfloat());
|
|
break;
|
|
case ENT_SPRAY:
|
|
Spray_Parse();
|
|
break;
|
|
case ENT_DECAL:
|
|
Decal_Parse();
|
|
break;
|
|
case ENT_AMBIENTSOUND:
|
|
Sound_ParseLoopingEntity(self, new);
|
|
break;
|
|
case ENT_OLDCAMERA:
|
|
trigger_camera tc = (trigger_camera)self;
|
|
if (new) {
|
|
spawnfunc_trigger_camera();
|
|
}
|
|
tc.ReceiveEntity(new, readfloat());
|
|
break;
|
|
case ENT_MONITOR:
|
|
func_monitor fc = (func_monitor)self;
|
|
if (new) {
|
|
spawnfunc_func_monitor();
|
|
}
|
|
fc.ReceiveEntity(new, readfloat());
|
|
break;
|
|
case ENT_DLIGHT:
|
|
light_dynamic dl = (light_dynamic)self;
|
|
if (new) {
|
|
spawnfunc_light_dynamic();
|
|
}
|
|
dl.ReceiveEntity(new, readfloat());
|
|
break;
|
|
case ENT_PROJECTEDTEXTURE:
|
|
env_projectedtexture ept = (env_projectedtexture)self;
|
|
if (new) {
|
|
spawnfunc_env_projectedtexture();
|
|
}
|
|
ept.ReceiveEntity(new, readfloat());
|
|
break;
|
|
case ENT_ENVLASER:
|
|
env_laser l = (env_laser)self;
|
|
if (new) {
|
|
spawnfunc_env_laser();
|
|
}
|
|
l.ReceiveEntity(new, readfloat());
|
|
break;
|
|
case ENT_PARTSYSTEM:
|
|
info_particle_system ips = (info_particle_system)self;
|
|
if (new) {
|
|
spawnfunc_info_particle_system();
|
|
}
|
|
ips.ReceiveEntity(new, readfloat());
|
|
break;
|
|
default:
|
|
if (ClientGame_EntityUpdate(t, new) == FALSE) {
|
|
//error(sprintf("Unknown entity type update received. (%d)\n", t));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CSQC_WorldLoaded
|
|
|
|
Whenever the world is fully initialized...
|
|
=================
|
|
*/
|
|
void
|
|
CSQC_WorldLoaded(void)
|
|
{
|
|
DetailTex_Init();
|
|
|
|
/* Primarily for the flashlight */
|
|
if (serverkeyfloat("*bspversion") != BSPVER_HL) {
|
|
localcmd("r_shadow_realtime_dlight 1\n");
|
|
} else {
|
|
localcmd("r_shadow_realtime_dlight 0\n");
|
|
}
|
|
|
|
string strTokenized;
|
|
getentitytoken(0);
|
|
|
|
while (1) {
|
|
strTokenized = getentitytoken();
|
|
if (strTokenized == "") {
|
|
break;
|
|
}
|
|
if (strTokenized != "{") {
|
|
print("^1[WARNING] ^7Bad entity data\n");
|
|
return;
|
|
}
|
|
if (!Entities_ParseLump()) {
|
|
print("^1[WARNING] ^7Bad entity data\n");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CSQC_Ent_Remove
|
|
|
|
Whenever an entity gets removed from the server and will no longer
|
|
receive entity updates.
|
|
=================
|
|
*/
|
|
void
|
|
CSQC_Ent_Remove(void)
|
|
{
|
|
/* avoid spawning dupes */
|
|
if (self.classname == "CCSAmbientSound")
|
|
sound(self, CHAN_VOICE, "", 0.0f, ATTN_NONE);
|
|
|
|
ClientGame_EntityRemove();
|
|
remove(self);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CSQC_Shutdown
|
|
|
|
Incase you need to free something
|
|
=================
|
|
*/
|
|
void
|
|
CSQC_Shutdown(void)
|
|
{
|
|
Decal_Shutdown();
|
|
Sentences_Shutdown();
|
|
Titles_Shutdown();
|
|
Sound_Shutdown();
|
|
PropData_Shutdown();
|
|
EFX_Shutdown();
|
|
}
|