From 53f5780ac088a1ea21e5b45060075d6fd1d13d98 Mon Sep 17 00:00:00 2001 From: Marco Hladik Date: Thu, 3 Mar 2022 14:16:02 -0800 Subject: [PATCH] Improvements to console output to make debugging easier, simplified entry.qc by pushing more code into external functions. Add support for mods to override networked events (after we added support for ent update overrides the other week). --- base/src/client/game_event.qc | 5 +- src/client/chat.qc | 2 +- src/client/cmd.h | 18 + src/client/cmd.qc | 268 +++++++++++ src/client/defs.h | 1 + src/client/detailtex.qc | 12 +- src/client/efx.qc | 3 + src/client/entities.qc | 118 +++++ src/client/entry.qc | 497 ++------------------ src/client/event.qc | 116 +++++ src/client/font.qc | 6 +- src/client/include.src | 2 + src/client/sky.qc | 1 + src/gs-entbase/shared/NSRenderableEntity.qc | 6 +- 14 files changed, 591 insertions(+), 464 deletions(-) create mode 100644 src/client/cmd.h create mode 100644 src/client/cmd.qc create mode 100644 src/client/event.qc diff --git a/base/src/client/game_event.qc b/base/src/client/game_event.qc index cdaed7cc..8d5e306b 100644 --- a/base/src/client/game_event.qc +++ b/base/src/client/game_event.qc @@ -14,7 +14,7 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -void +int ClientGame_EventParse(float fHeader) { switch (fHeader) { @@ -32,5 +32,8 @@ ClientGame_EventParse(float fHeader) CSQC_Parse_Print(sprintf("[TEAM] %s: %s", getplayerkeyvalue(fSender2, "name"), sMessage2), PRINT_CHAT); break; + default: + return (0); } + return (1); } diff --git a/src/client/chat.qc b/src/client/chat.qc index 7d31f94e..aa2f520a 100644 --- a/src/client/chat.qc +++ b/src/client/chat.qc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020 Marco Hladik + * Copyright (c) 2016-2022 Marco Hladik * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/src/client/cmd.h b/src/client/cmd.h new file mode 100644 index 00000000..2b640821 --- /dev/null +++ b/src/client/cmd.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2016-2022 Marco Hladik + * + * 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 Cmd_Init(void); +int Cmd_Parse(string); diff --git a/src/client/cmd.qc b/src/client/cmd.qc new file mode 100644 index 00000000..89268371 --- /dev/null +++ b/src/client/cmd.qc @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2016-2022 Marco Hladik + * + * 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. + */ + +/* +================= +Cmd_Parse + +Called from CSQC_ConsoleCommand to handle any builtin +commands from the Nuclide SDK. +Returns 0 if the command isn't handled and the engine is expected +to handle it from there. +================= +*/ +int +Cmd_Parse(string sCMD) +{ + 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_modeltest": + entity mt = spawn(); + mt.drawmask = MASK_ENGINE; + setmodel(mt, argv(1)); + setsize(mt, [0,0,0], [0,0,0]); + setorigin(mt, getproperty(VF_ORIGIN)); + mt.angles = getproperty(VF_ANGLES); + mt.angles[0] = mt.angles[2] = 0; + 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); +} + +/* +================= +Cmd_Init + +Register our commands for Nuclide +================= +*/ +void +Cmd_Init(void) +{ + print("--------- Initializing Cmds ----------\n"); + + /* 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"); + registercommand("dev_modeltest"); + + /* 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"); +} diff --git a/src/client/defs.h b/src/client/defs.h index 9c228c80..48358019 100644 --- a/src/client/defs.h +++ b/src/client/defs.h @@ -18,6 +18,7 @@ #include "efx.h" #include "font.h" #include "fade.h" +#include "cmd.h" /* flags for 2d drawing */ #define DRAWFLAG_NORMAL 0 diff --git a/src/client/detailtex.qc b/src/client/detailtex.qc index 719727db..ec6d7cda 100644 --- a/src/client/detailtex.qc +++ b/src/client/detailtex.qc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2021 Marco Hladik + * Copyright (c) 2016-2022 Marco Hladik * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -23,6 +23,8 @@ MAPTEXTURENAME PATH_TO_SOME_OVERLAY XSCALE YSCALE */ +static int g_detail_initialized; + const string g_detail_shader = \ "{\n" \ "{\n" \ @@ -65,13 +67,17 @@ DetailTex_Init(void) { filestream fh; string line; + int n = 0; if (!autocvar(r_detailtextures, 0, "High-res detail texture overlays for selected maps")) return; + print("--------- Initializing DetailTex ----------\n"); + fh = fopen(strcat("maps/", mapname, "_detail.txt"), FILE_READ); if (fh < 0) { + print(sprintf("DeailTex definition not found for %s.\n", mapname)); return; } @@ -82,7 +88,9 @@ DetailTex_Init(void) continue; DetailTex_Parse(strtolower(argv(0)), argv(1), stof(argv(2)), stof(argv(3))); + n++; } + print(sprintf("DeailTex initialized with %i entries.\n", n)); fclose(fh); -} \ No newline at end of file +} diff --git a/src/client/efx.qc b/src/client/efx.qc index e325e4ee..5a13beb0 100644 --- a/src/client/efx.qc +++ b/src/client/efx.qc @@ -210,6 +210,7 @@ EFX_Load(string efx_file) } } + print(sprintf("parsed EFXDef file %s\n", efx_file)); fclose(fh); return i; } @@ -337,6 +338,8 @@ EFX_Init(void) g_efx = (reverbinfo_t *)memalloc(sizeof(reverbinfo_t) * EFXDATA_MAX); g_efx_name = (string *)memalloc(sizeof(string) * EFXDATA_MAX); print(sprintf("allocated %d bytes for EFXDefs.\n", (sizeof(string) * EFXDATA_MAX) + (sizeof(reverbinfo_t) * EFXDATA_MAX))); +#else + print("dynamic allocation for EFXDefs enabled.\n"); #endif efx_default = EFX_Load("default"); diff --git a/src/client/entities.qc b/src/client/entities.qc index 4c653cd4..9efce01f 100644 --- a/src/client/entities.qc +++ b/src/client/entities.qc @@ -14,6 +14,118 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +void +Entity_EntityUpdate(float type, float new) +{ + switch (type) { + 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_ReadEntity(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: + //error(sprintf("Unknown entity type update received. (%d)\n", t)); + } +} + float Entities_ParseLump(void) { @@ -82,8 +194,14 @@ Entities_ParseLump(void) void Entities_RendererRestarted(void) { + int c = 0; + print("--------- Reloading Entity Resources ----------\n"); + for (entity b = world; (b = findfloat(b, ::isCSQC, TRUE));) { NSEntity pf = (NSEntity) b; pf.RendererRestarted(); + c++; } + + print(sprintf("resource reload called on %i entities\n", c)); } diff --git a/src/client/entry.qc b/src/client/entry.qc index 38ecde4b..9b877085 100644 --- a/src/client/entry.qc +++ b/src/client/entry.qc @@ -46,64 +46,7 @@ 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"); - registercommand("dev_modeltest"); - - /* 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"); + Cmd_Init(); /* Sound shaders */ Sound_Init(); @@ -175,7 +118,7 @@ CSQC_RendererRestarted(string rstr) DetailTex_Init(); /* end msg */ - print("Graphical resources reloaded.\n"); + print("Graphical resources reloaded\n"); } /* this is so that profile_csqc reports more accurate statistics as to @@ -550,14 +493,9 @@ CSQC_Input_Frame(void) CSQC_UpdateSeat(); me = pSeat->m_ePlayer; - if (me.classname == "player") { - player pl; - pl = (player)pSeat->m_ePlayer; + if (me.classname == "player" || me.classname == "spectator") { + base_client pl = (base_client)me; pl.ClientInputFrame(); - } else if (me.classname == "spectator") { - spectator spec; - spec = (spectator)pSeat->m_ePlayer; - spec.ClientInputFrame(); } } @@ -572,125 +510,28 @@ Whenever we call a SVC_CGAMEPACKET on the SSQC, this is being run void CSQC_Parse_Event(void) { - entity me; /* always 0, unless it was sent with a MULTICAST_ONE or MULTICAST_ONE_R to p2+ */ CSQC_UpdateSeat(); - me = pSeat->m_ePlayer; 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 (me.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); + int ret = ClientGame_EventParse(fHeader); + if (ret == 1) { + return; } + + Event_Parse(fHeader); } +/* +================= +CSQC_ConsoleCommand + +Commands not protected by the engine get passed here. +If we return 0 this means the engine needs to either handle +the command or throw a 'unrecognized command' message. +================= +*/ float CSQC_ConsoleCommand(string sCMD) { @@ -706,185 +547,36 @@ CSQC_ConsoleCommand(string sCMD) 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_modeltest": - entity mt = spawn(); - mt.drawmask = MASK_ENGINE; - setmodel(mt, argv(1)); - setsize(mt, [0,0,0], [0,0,0]); - setorigin(mt, getproperty(VF_ORIGIN)); - mt.angles = getproperty(VF_ANGLES); - mt.angles[0] = mt.angles[2] = 0; - 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); + return Cmd_Parse(sCMD); } +/* +================= +CSQC_ConsoleCommand + +Engine or server game will occasionally pass messages through here. +There are 4 different types currently: +PRINT_LOW = low on the screen. +PRINT_MEDIUM = medium level on the screen. +PRINT_HIGH = top level on the screen +PRINT_CHAT = chat message +Currently, everything but chat gets piped into a single printbuffer, +similar to NetQuake. +FIXME: We'd like to expose this further to modification. +================= +*/ void CSQC_Parse_Print(string sMessage, float fLevel) { CSQC_UpdateSeat(); - /* This gives messages other than chat an orange tint */ + /* chat goes through here */ if (fLevel == PRINT_CHAT) { Chat_Parse(sMessage); return; } + /* the rest goes into our print buffer */ if (pSeat->m_iPrintLines < 4) { pSeat->m_strPrintBuffer[pSeat->m_iPrintLines + 1] = sMessage; pSeat->m_iPrintLines++; @@ -897,7 +589,7 @@ CSQC_Parse_Print(string sMessage, float fLevel) pSeat->m_flPrintTime = time + CHAT_TIME; - // Log to console + /* log to console */ localcmd(sprintf("echo \"%s\"\n", sMessage)); } @@ -932,7 +624,10 @@ CSQC_Parse_CenterPrint(string sMessage) ================= CSQC_Ent_Update -Called whenever an entity is sent manually via .SendFlags and so on +Called when an entity is being networked from the server game. +ClientGame_EntityUpdate allows the project to do game specific +overrides. If that returns 0 Nuclide will attempt to handle it. +If neither handles it we'll get a protocol error. ================= */ void @@ -946,113 +641,7 @@ CSQC_Ent_Update(float new) return; } - 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_ReadEntity(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: - //error(sprintf("Unknown entity type update received. (%d)\n", t)); - } + Entity_EntityUpdate(t, new); } /* @@ -1066,7 +655,7 @@ void CSQC_WorldLoaded(void) { print("--------- Initializing Client World ----------\n"); - DetailTex_Init(); + //DetailTex_Init(); /* Primarily for the flashlight */ if (serverkeyfloat("*bspversion") != BSPVER_HL) { diff --git a/src/client/event.qc b/src/client/event.qc new file mode 100644 index 00000000..b8a3c5cd --- /dev/null +++ b/src/client/event.qc @@ -0,0 +1,116 @@ +void +Event_Parse(float type) +{ + entity me = pSeat->m_ePlayer; + + switch (type) { + 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 (me.classname == "spectator") + break; + pSeat->m_flShakeDuration = readfloat(); + pSeat->m_flShakeAmp = readfloat(); + pSeat->m_flShakeFreq = readfloat(); + pSeat->m_flShakeTime = pSeat->m_flShakeDuration; + break; + default: + error("event not recognized. abort immediately.\n"); + } +} diff --git a/src/client/font.qc b/src/client/font.qc index 80e58d26..c2326847 100644 --- a/src/client/font.qc +++ b/src/client/font.qc @@ -76,14 +76,14 @@ Font_Load(string strFile, font_s &fntNew) } fclose(fileFont); } else { - error(sprintf("[FONT] Cannot load font file %s!", strFile)); + error(sprintf("cannot load font file %s!", strFile)); } if (!fntNew.iScaleX || !fntNew.iScaleY) { - error(sprintf("[FONT] No valid size defined for %s!", strFile)); + error(sprintf("no valid size defined for %s!", strFile)); } - //print(sprintf("[FONT] %s: %s %s\n", strFile, strFontPath, strRenderSize)); + print(sprintf("loaded font definition for %s\n", strFile)); if (strRenderSize != "") fntNew.iID = (int)loadfont("", strFontPath, strRenderSize, -1, 0, 0); diff --git a/src/client/include.src b/src/client/include.src index b1552909..b47ecb3f 100644 --- a/src/client/include.src +++ b/src/client/include.src @@ -23,5 +23,7 @@ way.qc efx.qc detailtex.qc shake.qc +cmd.qc +event.qc entry.qc #endlist diff --git a/src/client/sky.qc b/src/client/sky.qc index d9c6ec03..1895365b 100644 --- a/src/client/sky.qc +++ b/src/client/sky.qc @@ -22,5 +22,6 @@ Sky_Update(int force) if (g_strSkyName != serverkey("skyname") || force == TRUE) { g_strSkyName = serverkey("skyname"); localcmd(sprintf("sky \"%s\"\n", g_strSkyName)); + print(sprintf("sky update applying %s.\n", g_strSkyName)); } } diff --git a/src/gs-entbase/shared/NSRenderableEntity.qc b/src/gs-entbase/shared/NSRenderableEntity.qc index ac23de46..9ee516ee 100644 --- a/src/gs-entbase/shared/NSRenderableEntity.qc +++ b/src/gs-entbase/shared/NSRenderableEntity.qc @@ -188,9 +188,9 @@ NSRenderableEntity::SendEntity(entity ePEnt, float fChanged) WriteCoord(MSG_ENTITY, origin[2]); } if (fChanged & BASEFL_CHANGED_ANGLES) { - angles[0] = Math_FixDelta(angles[0]); - angles[1] = Math_FixDelta(angles[1]); - angles[2] = Math_FixDelta(angles[2]); + /* fix angles and try to avoid a net update next frame */ + angles = Math_FixDeltaVector(angles); + angles_net = angles; WriteShort(MSG_ENTITY, angles[0] * 32767 / 360); WriteShort(MSG_ENTITY, angles[1] * 32767 / 360); WriteShort(MSG_ENTITY, angles[2] * 32767 / 360);