From 16edc07985e6177ba721107dad16824a543475cf Mon Sep 17 00:00:00 2001 From: g-cont Date: Mon, 3 Sep 2007 00:00:00 +0400 Subject: [PATCH] 03 Sep 2007 --- changelog.log | 8 +- debug.bat | 3 +- editor/editor.plg | 32 + engine/client/cl_cin.c | 8 +- engine/client/cl_ents.c | 26 +- engine/client/cl_fx.c | 10 +- engine/client/cl_input.c | 9 +- engine/client/cl_main.c | 73 +- engine/client/cl_newfx.c | 14 +- engine/client/cl_pred.c | 6 +- engine/client/cl_scrn.c | 25 +- engine/client/cl_view.c | 3 - engine/client/client.h | 58 +- engine/common.h | 109 +- engine/common/cmd.c | 15 +- engine/common/cmodel.c | 128 +- engine/common/common.c | 17 +- engine/common/console.c | 36 +- engine/common/cvar.c | 22 +- engine/common/cvar.h | 12 +- engine/common/keys.c | 8 +- engine/common/keys.h | 4 +- engine/common/menu.c | 190 +- engine/common/net_chan.c | 2 +- engine/common/net_msg.c | 30 +- engine/common/net_wins.c | 8 +- engine/common/pmove.c | 6 +- engine/common/qmenu.c | 14 +- engine/engine.dsp | 42 +- engine/engine.h | 8 + engine/engine.plg | 89 + engine/host.c | 51 +- engine/prvm/edict.h | 70 + engine/prvm/progdefs.h | 139 ++ engine/prvm/progsvm.h | 407 ++++ engine/prvm/vm_cmds.c | 3154 +++++++++++++++++++++++++++++++ engine/prvm/vm_cmds.h | 343 ++++ engine/prvm/vm_edict.c | 1984 ++++++++++++++++++++ engine/prvm/vm_exec.c | 588 ++++++ engine/prvm/vm_exec.h | 628 +++++++ engine/server/server.h | 130 +- engine/server/sv_ccmds.c | 61 +- engine/server/sv_ents.c | 281 ++- engine/server/sv_game.c | 1661 +++++++++++++--- engine/server/sv_game_old.c | 19 + engine/server/sv_init.c | 433 ++++- engine/server/sv_main.c | 115 +- engine/server/sv_phys.c | 1387 ++++++++++++++ engine/server/sv_save.c | 10 +- engine/server/sv_send.c | 46 +- engine/server/sv_studio.c | 3 +- engine/server/sv_user.c | 355 +++- engine/server/sv_world.c | 226 +-- engine/snd_dma.c | 62 +- engine/snd_loc.h | 48 +- engine/snd_mix.c | 37 +- engine/sound.h | 6 +- engine/system.c | 34 +- launcher/bsplib/makefile.nmake | 2 +- launcher/console.c | 4 +- launcher/launcher.c | 36 +- launcher/launcher.dsp | 4 +- launcher/launcher.h | 3 +- platform/basemem.c | 3 +- platform/bsplib/map.c | 2 +- platform/platform.dsp | 4 +- platform/platform.plg | 73 - platform/qcclib/pr_comp.c | 1 + platform/qcclib/pr_lex.c | 6 +- platform/qcclib/qcclib.h | 8 +- platform/qcclib/qccmain.c | 44 +- public/basemath.h | 116 +- public/basetypes.h | 5 +- public/bspmodel.h | 9 + public/const.h | 81 +- public/ref_system.h | 49 +- public/vprogs.h | 5 +- release.bat | 3 +- renderer/gl_draw.c | 12 +- renderer/gl_local.h | 2 +- renderer/gl_mesh.c | 11 +- renderer/gl_rmain.c | 4 +- renderer/gl_rsurf.c | 24 +- renderer/gl_warp.c | 9 +- renderer/r_sprite.c | 6 +- renderer/r_studio.c | 2 +- renderer/renderer.plg | 16 + resource/PROGS.SRC | 49 + resource/client.c | 291 +++ resource/damage.c | 163 ++ resource/defs.c | 414 ++++ resource/dummys.c | 132 ++ resource/impulses.c | 35 + resource/main.c | 137 ++ resource/player.c | 248 +++ resource/progdefs.h | 287 +++ resource/qcclib.exe | Bin 0 -> 39424 bytes server/baseentity.h | 6 - server/client.c | 89 - server/g_cmds.c | 2159 --------------------- server/g_items.c | 1607 ---------------- server/g_local.h | 1506 --------------- server/g_main.c | 485 ----- server/g_misc.c | 3237 -------------------------------- server/g_phys.c | 3158 ------------------------------- server/g_save.c | 927 --------- server/g_spawn.c | 752 -------- server/g_svcmds.c | 273 --- server/g_utils.c | 1399 -------------- server/m_player.h | 205 -- server/p_client.c | 2206 ---------------------- server/p_hud.c | 639 ------- server/p_menu.c | 189 -- server/p_menu.h | 27 - server/p_view.c | 1316 ------------- server/p_weapon.c | 1194 ------------ server/server.dsp | 205 -- 117 files changed, 13890 insertions(+), 23282 deletions(-) create mode 100644 engine/engine.plg create mode 100644 engine/prvm/edict.h create mode 100644 engine/prvm/progdefs.h create mode 100644 engine/prvm/progsvm.h create mode 100644 engine/prvm/vm_cmds.c create mode 100644 engine/prvm/vm_cmds.h create mode 100644 engine/prvm/vm_edict.c create mode 100644 engine/prvm/vm_exec.c create mode 100644 engine/prvm/vm_exec.h create mode 100644 engine/server/sv_game_old.c create mode 100644 engine/server/sv_phys.c delete mode 100644 platform/platform.plg create mode 100644 renderer/renderer.plg create mode 100644 resource/PROGS.SRC create mode 100644 resource/client.c create mode 100644 resource/damage.c create mode 100644 resource/defs.c create mode 100644 resource/dummys.c create mode 100644 resource/impulses.c create mode 100644 resource/main.c create mode 100644 resource/player.c create mode 100644 resource/progdefs.h create mode 100644 resource/qcclib.exe delete mode 100644 server/baseentity.h delete mode 100644 server/client.c delete mode 100644 server/g_cmds.c delete mode 100644 server/g_items.c delete mode 100644 server/g_local.h delete mode 100644 server/g_main.c delete mode 100644 server/g_misc.c delete mode 100644 server/g_phys.c delete mode 100644 server/g_save.c delete mode 100644 server/g_spawn.c delete mode 100644 server/g_svcmds.c delete mode 100644 server/g_utils.c delete mode 100644 server/m_player.h delete mode 100644 server/p_client.c delete mode 100644 server/p_hud.c delete mode 100644 server/p_menu.c delete mode 100644 server/p_menu.h delete mode 100644 server/p_view.c delete mode 100644 server/p_weapon.c delete mode 100644 server/server.dsp diff --git a/changelog.log b/changelog.log index ff2c1868..2a7d2aa2 100644 --- a/changelog.log +++ b/changelog.log @@ -1,13 +1,15 @@ +Разработать концепцию языка VirtualC\VirtualC++ (базируется на QuakeC) + //================================================== // FIXME //================================================== -1. Раз и навсегда переписать SC_ParseToken и SC_ParseWord OK -2. Подогнать новую VFS под QCC -3. Парсинг выходного имени файла исправить qccmsource3 +1. Переписать главный игровой цикл +2. Пофиксить физику //================================================== // то, что уже готово //================================================== ++исправлен баг с кодом возвращаемой ошибки +исправлен баг с выводом сообщений в консоль кварка\хаммера +исправлены баги со studiomdl +убраны лишние пункты в меню diff --git a/debug.bat b/debug.bat index ac98d543..dc1c236c 100644 --- a/debug.bat +++ b/debug.bat @@ -23,7 +23,7 @@ if errorlevel 1 set BUILD_ERROR=1 %MSDEV% renderer/renderer.dsp %CONFIG%"renderer - Win32 Debug" %build_target% if errorlevel 1 set BUILD_ERROR=1 -%MSDEV% server/server.dsp %CONFIG%"server - Win32 Debug" %build_target% +resource\qcclib.exe if errorlevel 1 set BUILD_ERROR=1 if "%BUILD_ERROR%"=="" goto build_ok @@ -50,6 +50,7 @@ if exist launcher\launcher.plg del /f /q launcher\launcher.plg if exist platform\platform.plg del /f /q platform\platform.plg if exist renderer\renderer.plg del /f /q renderer\renderer.plg if exist server\server.plg del /f /q server\server.plg +if exist resource\server.dat copy resource\server.dat D:\Xash3D\xash\server.dat echo Build succeeded! echo Please wait. Xash is now loading diff --git a/editor/editor.plg b/editor/editor.plg index 8c9fafb2..fff17474 100644 --- a/editor/editor.plg +++ b/editor/editor.plg @@ -6,6 +6,38 @@ --------------------Configuration: editor - Win32 Debug--------------------

Command Lines

+Creating temporary file "C:\Temp\RSP2FD7.tmp" with contents +[ +/nologo /MTd /W3 /Gm /Gi /GX /ZI /Od /I "../public" /I "../platform/formats" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR"..\temp\editor\!debug/" /Fo"..\temp\editor\!debug/" /Fd"..\temp\editor\!debug/" /FD /GZ /c +"D:\XASH3D\src_main\!source\editor\editor.c" +"D:\XASH3D\src_main\!source\editor\guiutils.c" +"D:\XASH3D\src_main\!source\editor\guiforms.c" +] +Creating command line "cl.exe @C:\Temp\RSP2FD7.tmp" +Creating temporary file "C:\Temp\RSP2FD8.tmp" with contents +[ +kernel32.lib user32.lib gdi32.lib comctl32.lib comdlg32.lib /nologo /dll /incremental:yes /pdb:"..\temp\editor\!debug/editor.pdb" /debug /machine:I386 /out:"..\temp\editor\!debug/editor.dll" /implib:"..\temp\editor\!debug/editor.lib" /pdbtype:sept +"\XASH3D\src_main\!source\temp\editor\!debug\editor.obj" +"\XASH3D\src_main\!source\temp\editor\!debug\guiutils.obj" +"\XASH3D\src_main\!source\temp\editor\!debug\guiforms.obj" +"\XASH3D\src_main\!source\temp\editor\!debug\editor.res" +] +Creating command line "link.exe @C:\Temp\RSP2FD8.tmp" +Creating temporary file "C:\Temp\RSP2FD9.bat" with contents +[ +@echo off +copy \XASH3D\src_main\!source\temp\editor\!debug\editor.dll "D:\Xash3D\bin\editor.dll" +] +Creating command line "C:\Temp\RSP2FD9.bat" +Compiling... +editor.c +guiutils.c +guiforms.c +Generating Code... +Linking... +

Output Window

+Performing Custom Build Step on \XASH3D\src_main\!source\temp\editor\!debug\editor.dll +‘Є®ЇЁа®ў ­® д ©«®ў: 1. diff --git a/engine/client/cl_cin.c b/engine/client/cl_cin.c index 3145637d..7dd84d45 100644 --- a/engine/client/cl_cin.c +++ b/engine/client/cl_cin.c @@ -402,17 +402,17 @@ void SCR_RunCinematic (void) if (cls.key_dest != key_game) { // pause if menu or console is up - cl.cinematictime = cls.realtime - cl.cinematicframe*1000/14; + cl.cinematictime = cls.realtime - cl.cinematicframe/14; return; } - frame = (cls.realtime - cl.cinematictime)*14.0/1000; + frame = (cls.realtime - cl.cinematictime)*14.0; if (frame <= cl.cinematicframe) return; if (frame > cl.cinematicframe+1) { Msg ("Dropped frame: %i > %i\n", frame, cl.cinematicframe+1); - cl.cinematictime = cls.realtime - cl.cinematicframe*1000/14; + cl.cinematictime = cls.realtime - cl.cinematicframe/14; } if (cin.pic) Z_Free (cin.pic); @@ -546,5 +546,5 @@ void SCR_PlayCinematic (char *arg) cl.cinematicframe = 0; cin.pic = SCR_ReadNextFrame (); - cl.cinematictime = Sys_Milliseconds (); + cl.cinematictime = cls.realtime; } diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index f0c22ed8..eaac98a9 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -656,7 +656,7 @@ void CL_ParseFrame (void) cl.frame.serverframe = MSG_ReadLong (&net_message); cl.frame.deltaframe = MSG_ReadLong (&net_message); - cl.frame.servertime = cl.frame.serverframe*100; + cl.frame.servertime = cl.frame.serverframe * 0.1; // BIG HACK to let old demos continue to work if (cls.serverProtocol != 26) @@ -699,8 +699,8 @@ void CL_ParseFrame (void) // clamp time if (cl.time > cl.frame.servertime) cl.time = cl.frame.servertime; - else if (cl.time < cl.frame.servertime - 100) - cl.time = cl.frame.servertime - 100; + else if (cl.time < cl.frame.servertime - 0.1) + cl.time = cl.frame.servertime - 0.1; // read areabits len = MSG_ReadByte (&net_message); @@ -829,7 +829,7 @@ void CL_AddPacketEntities (frame_t *frame) autorotate = anglemod(cl.time/10); // brush models can auto animate their frames - autoanim = 2*cl.time/1000; + autoanim = 2 * cl.time; memset (&ent, 0, sizeof(ent)); @@ -850,7 +850,7 @@ void CL_AddPacketEntities (frame_t *frame) else if (effects & EF_ANIM_ALL) ent.frame = autoanim; else if (effects & EF_ANIM_ALLFAST) - ent.frame = cl.time / 100; + ent.frame = cl.time * 0.1; else ent.frame = s1->frame; @@ -974,7 +974,7 @@ void CL_AddPacketEntities (frame_t *frame) vec3_t forward; vec3_t start; - AngleVectors (ent.angles, forward, NULL, NULL); + AngleVectorsRight(ent.angles, forward, NULL, NULL); VectorMA (ent.origin, 64, forward, start); V_AddLight (start, 100, 1, 0, 0); } @@ -1231,8 +1231,7 @@ void CL_CalcViewValues (void) // smooth out stair climbing delta = cls.realtime - cl.predicted_step_time; - if (delta < 100) - cl.refdef.vieworg[2] -= cl.predicted_step * (100 - delta) * 0.01; + if (delta < 0.1) cl.refdef.vieworg[2] -= cl.predicted_step * (0.1 - delta) * 100; } else { // just use interpolated values @@ -1257,7 +1256,7 @@ void CL_CalcViewValues (void) for (i=0 ; i<3 ; i++) cl.refdef.viewangles[i] += LerpAngle (ops->kick_angles[i], ps->kick_angles[i], lerp); - AngleVectors (cl.refdef.viewangles, cl.v_forward, cl.v_right, cl.v_up); + AngleVectorsRight(cl.refdef.viewangles, cl.v_forward, cl.v_right, cl.v_up); // interpolate field of view cl.refdef.fov_x = ops->fov + lerp * (ps->fov - ops->fov); @@ -1289,15 +1288,14 @@ void CL_AddEntities (void) cl.time = cl.frame.servertime; cl.lerpfrac = 1.0; } - else if (cl.time < cl.frame.servertime - 100) + else if (cl.time < cl.frame.servertime - 0.1) { if (cl_showclamp->value) - Msg ("low clamp %i\n", cl.frame.servertime-100 - cl.time); - cl.time = cl.frame.servertime - 100; + Msg ("low clamp %i\n", cl.frame.servertime - 0.1 - cl.time); + cl.time = cl.frame.servertime - 0.1; cl.lerpfrac = 0; } - else - cl.lerpfrac = 1.0 - (cl.frame.servertime - cl.time) * 0.01; + else cl.lerpfrac = 1.0 - (cl.frame.servertime - cl.time) * 100; if (cl_timedemo->value) cl.lerpfrac = 1.0; diff --git a/engine/client/cl_fx.c b/engine/client/cl_fx.c index 055749ce..e41650fa 100644 --- a/engine/client/cl_fx.c +++ b/engine/client/cl_fx.c @@ -69,7 +69,7 @@ void CL_RunLightStyles (void) int i; clightstyle_t *ls; - ofs = cl.time / 100; + ofs = cl.time * 0.1; if (ofs == lastofs) return; lastofs = ofs; @@ -257,7 +257,7 @@ void CL_ParseMuzzleFlash (void) dl = CL_AllocDlight (i); VectorCopy (pl->current.origin, dl->origin); - AngleVectors (pl->current.angles, fv, rv, NULL); + AngleVectorsRight(pl->current.angles, fv, rv, NULL); VectorMA (dl->origin, 18, fv, dl->origin); VectorMA (dl->origin, 16, rv, dl->origin); if (silenced) @@ -398,7 +398,7 @@ void CL_ParseMuzzleFlash2 (void) flash_number = MSG_ReadByte (&net_message); // locate the origin - AngleVectors (cl_entities[ent].current.angles, forward, right, NULL); + AngleVectorsRight(cl_entities[ent].current.angles, forward, right, NULL); origin[0] = cl_entities[ent].current.origin[0] + forward[0] + right[0]; origin[1] = cl_entities[ent].current.origin[1] + forward[1] + right[1]; origin[2] = cl_entities[ent].current.origin[2] + forward[2] + right[2]; @@ -1607,7 +1607,7 @@ void CL_FlyParticles (vec3_t origin, int count) } - ltime = (float)cl.time / 1000.0; + ltime = cl.time; for (i=0 ; idowntime = atoi(c); if (!b->downtime) - b->downtime = sys_frame_time - 100; + b->downtime = (uint)sys_frame_time - 0.1; b->state |= 1 + 2; // down + impulse down } @@ -354,8 +354,7 @@ void CL_FinishMove (usercmd_t *cmd) // send milliseconds of time to apply the move ms = cls.frametime * 1000; - if (ms > 250) - ms = 100; // time was unreasonable + if (ms > 250) ms = 100; // time was unreasonable cmd->msec = ms; CL_ClampPitch (); @@ -483,7 +482,7 @@ void CL_SendCmd (void) if ( cls.state == ca_connected) { - if (cls.netchan.message.cursize || curtime - cls.netchan.last_sent > 1000 ) + if (cls.netchan.message.cursize || curtime - cls.netchan.last_sent > 1.0f ) Netchan_Transmit (&cls.netchan, 0, buf.data); return; } @@ -498,7 +497,7 @@ void CL_SendCmd (void) } if (cmd->buttons && cl.cinematictime > 0 && !cl.attractloop - && cls.realtime - cl.cinematictime > 1000) + && cls.realtime - cl.cinematictime > 1.0f) { // skip the rest of the cinematic SCR_FinishCinematic (); } diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 2198302d..ca6f693b 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -618,12 +618,11 @@ void CL_Disconnect (void) if (cl_timedemo && cl_timedemo->value) { - int time; + float time; - time = Sys_Milliseconds () - cl.timedemo_start; + time = Sys_DoubleTime() - cl.timedemo_start; if (time > 0) - Msg ("%i frames, %3.1f seconds: %3.1f fps\n", cl.timedemo_frames, - time/1000.0, cl.timedemo_frames*1000.0 / time); + Msg ("%i frames, %3.1f seconds: %3.1f fps\n", cl.timedemo_frames, time, cl.timedemo_frames / time); } VectorClear (cl.refdef.blend); @@ -1021,8 +1020,7 @@ void CL_ReadPackets (void) // // check timeout // - if (cls.state >= ca_connected - && cls.realtime - cls.netchan.last_received > cl_timeout->value*1000) + if (cls.state >= ca_connected && cls.realtime - cls.netchan.last_received > cl_timeout->value) { if (++cl.timeoutcount > 5) // timeoutcount saves debugger { @@ -1413,7 +1411,7 @@ CL_InitLocal void CL_InitLocal (void) { cls.state = ca_disconnected; - cls.realtime = Sys_Milliseconds (); + cls.realtime = Sys_DoubleTime(); CL_InitInput (); adr0 = Cvar_Get( "adr0", "", CVAR_ARCHIVE ); @@ -1678,44 +1676,36 @@ CL_Frame ================== */ -void CL_Frame (int msec) +void CL_Frame (float time) { - static int extratime; - static int lasttimecalled; + static float extratime; + static float lasttimecalled; - if (dedicated->value) - return; + if (dedicated->value) return; - extratime += msec; + extratime += time; if (!cl_timedemo->value) { - if (cls.state == ca_connected && extratime < 100) - return; // don't flood packets out while connecting - if (extratime < 1000/cl_maxfps->value) - return; // framerate is too high + if (cls.state == ca_connected && extratime < 0.1f) + return; // don't flood packets out while connecting + if (extratime < 1.0f / cl_maxfps->value) + return; // framerate is too high } // let the mouse activate or deactivate IN_Frame (); // decide the simulation time - cls.frametime = extratime/1000.0; + cls.frametime = extratime; cl.time += extratime; - cls.realtime = curtime; + cls.realtime = Sys_DoubleTime(); extratime = 0; -#if 0 - if (cls.frametime > (1.0 / cl_minfps->value)) - cls.frametime = (1.0 / cl_minfps->value); -#else - if (cls.frametime > (1.0 / 5)) - cls.frametime = (1.0 / 5); -#endif + if (cls.frametime > (1.0 / 5)) cls.frametime = (1.0 / 5); // if in the debugger last frame, don't timeout - if (msec > 5000) - cls.netchan.last_received = Sys_Milliseconds (); + if (time > 1.0f) cls.netchan.last_received = Sys_DoubleTime(); // fetch results from server CL_ReadPackets (); @@ -1732,11 +1722,9 @@ void CL_Frame (int msec) CL_PrepRefresh (); // update the screen - if (host_speeds->value) - time_before_ref = Sys_Milliseconds (); + if (host_speeds->value) time_before_ref = Sys_DoubleTime(); SCR_UpdateScreen (); - if (host_speeds->value) - time_after_ref = Sys_Milliseconds (); + if (host_speeds->value) time_after_ref = Sys_DoubleTime(); // update audio S_Update (cl.refdef.vieworg, cl.v_forward, cl.v_right, cl.v_up); @@ -1748,27 +1736,6 @@ void CL_Frame (int msec) SCR_RunConsole (); cls.framecount++; - - if ( log_stats->value ) - { - if ( cls.state == ca_active ) - { - if ( !lasttimecalled ) - { - lasttimecalled = Sys_Milliseconds(); - if ( log_stats_file ) - FS_Printf( log_stats_file, "0\n" ); - } - else - { - int now = Sys_Milliseconds(); - - if ( log_stats_file ) - FS_Printf( log_stats_file, "%d\n", now - lasttimecalled ); - lasttimecalled = now; - } - } - } } diff --git a/engine/client/cl_newfx.c b/engine/client/cl_newfx.c index b3572dd4..b67c9b37 100644 --- a/engine/client/cl_newfx.c +++ b/engine/client/cl_newfx.c @@ -81,7 +81,7 @@ void CL_Flashlight (int ent, vec3_t pos) VectorCopy (pos, dl->origin); dl->radius = 400; dl->minlight = 250; - dl->die = cl.time + 100; + dl->die = cl.time + 0.1; dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 1; @@ -100,7 +100,7 @@ void CL_ColorFlash (vec3_t pos, int ent, int intensity, float r, float g, float VectorCopy (pos, dl->origin); dl->radius = intensity; dl->minlight = 250; - dl->die = cl.time + 100; + dl->die = cl.time + 0.1; dl->color[0] = r; dl->color[1] = g; dl->color[2] = b; @@ -453,7 +453,7 @@ void CL_Heatbeam (vec3_t start, vec3_t end) VectorMA (move, -1, up, move); VectorScale (vec, step, vec); - ltime = (float) cl.time/1000.0; + ltime = cl.time; // for (i=0 ; ifraction) { - trace.ent = (struct edict_s *)ent; + trace.ent = (prvm_edict_t *)ent; if (tr->startsolid) { *tr = trace; @@ -145,14 +145,14 @@ void CL_ClipMoveToEntities ( vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, CL_PMTrace ================ */ -trace_t CL_PMTrace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end) +trace_t CL_PMTrace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end) { trace_t t; // check against world t = CM_BoxTrace (start, end, mins, maxs, 0, MASK_PLAYERSOLID); if (t.fraction < 1.0) - t.ent = (struct edict_s *)1; + t.ent = (prvm_edict_t *)1; // check all other solid models CL_ClipMoveToEntities (start, mins, maxs, end, &t); diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index 5f94fb92..90499f4f 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -505,7 +505,7 @@ Scroll it up or down */ void SCR_RunConsole (void) { -// decide on the height of the console + // decide on the height of the console if (cls.key_dest == key_console) scr_conlines = 0.5; // half screen else @@ -513,14 +513,14 @@ void SCR_RunConsole (void) if (scr_conlines < scr_con_current) { - scr_con_current -= scr_conspeed->value*cls.frametime; + scr_con_current -= scr_conspeed->value * cls.frametime * 1000; if (scr_conlines > scr_con_current) scr_con_current = scr_conlines; } else if (scr_conlines > scr_con_current) { - scr_con_current += scr_conspeed->value*cls.frametime; + scr_con_current += scr_conspeed->value * cls.frametime * 1000; if (scr_conlines < scr_con_current) scr_con_current = scr_conlines; } @@ -583,7 +583,7 @@ void SCR_BeginLoadingPlaque (void) else scr_draw_loading = 1; SCR_UpdateScreen (); - cls.disable_screen = Sys_Milliseconds (); + cls.disable_screen = Sys_DoubleTime(); cls.disable_servercount = cl.servercount; } @@ -631,18 +631,19 @@ int entitycmpfnc( const entity_t *a, const entity_t *b ) void SCR_TimeRefresh_f (void) { int i; - int start, stop; - float time; + float start, stop; + float time; if ( cls.state != ca_active ) return; - start = Sys_Milliseconds (); + start = Sys_DoubleTime(); if (Cmd_Argc() == 2) - { // run without page flipping + { + // run without page flipping re->BeginFrame( 0 ); - for (i=0 ; i<128 ; i++) + for (i = 0; i < 128; i++) { cl.refdef.viewangles[1] = i/128.0*360.0; re->RenderFrame (&cl.refdef); @@ -661,8 +662,8 @@ void SCR_TimeRefresh_f (void) } } - stop = Sys_Milliseconds (); - time = (stop-start)/1000.0; + stop = Sys_DoubleTime(); + time = (stop-start); Msg ("%f seconds (%f fps)\n", time, 128/time); } @@ -1268,7 +1269,7 @@ void SCR_UpdateScreen (void) // do nothing at all if (cls.disable_screen) { - if (Sys_Milliseconds() - cls.disable_screen > 120000) + if (Sys_DoubleTime() - cls.disable_screen > 120.0f) { cls.disable_screen = 0; Msg ("Loading plaque timed out.\n"); diff --git a/engine/client/cl_view.c b/engine/client/cl_view.c index 52e6dc7a..02ec7506 100644 --- a/engine/client/cl_view.c +++ b/engine/client/cl_view.c @@ -533,9 +533,6 @@ void V_RenderView( float stereo_separation ) re->RenderFrame (&cl.refdef); if (cl_stats->value) Msg ("ent:%i lt:%i part:%i\n", r_numentities, r_numdlights, r_numparticles); - if ( log_stats->value && ( log_stats_file != 0 ) ) - FS_Printf( log_stats_file, "%i,%i,%i,",r_numentities, r_numdlights, r_numparticles); - SCR_AddDirtyPoint (scr_vrect.x, scr_vrect.y); SCR_AddDirtyPoint (scr_vrect.x+scr_vrect.width-1, diff --git a/engine/client/client.h b/engine/client/client.h index f3f3c406..a0c06f37 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -34,27 +34,27 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. typedef struct { bool valid; // cleared if delta parsing was invalid - int serverframe; - int servertime; // server time the message is valid for (in msec) - int deltaframe; - byte areabits[MAX_MAP_AREAS/8]; // portalarea visibility bits + int serverframe; + float servertime; // server time the message is valid for (in msec) + int deltaframe; + byte areabits[MAX_MAP_AREAS/8]; // portalarea visibility bits player_state_t playerstate; - int num_entities; - int parse_entities; // non-masked index into cl_parse_entities array + int num_entities; + int parse_entities; // non-masked index into cl_parse_entities array } frame_t; typedef struct { - entity_state_t baseline; // delta from this if not from a previous frame + entity_state_t baseline; // delta from this if not from a previous frame entity_state_t current; entity_state_t prev; // will always be valid, but might just be a copy of current int serverframe; // if not current, this ent isn't in the frame - int trailcount; // for diminishing grenade trails + int trailcount; // for diminishing grenade trails vec3_t lerp_origin; // for trails (variable hz) - int fly_stoptime; + float fly_stoptime; } centity_t; #define MAX_CLIENTWEAPONMODELS 20 // PGM -- upped from 16 to fit the chainfist vwep @@ -81,24 +81,24 @@ extern int num_cl_weaponmodels; // typedef struct { - int timeoutcount; + int timeoutcount; - int timedemo_frames; - int timedemo_start; + int timedemo_frames; + float timedemo_start; - bool refresh_prepped; // false if on new level or new ref dll - bool sound_prepped; // ambient sounds can start - bool force_refdef; // vid has changed, so we can't use a paused refdef + bool refresh_prepped; // false if on new level or new ref dll + bool sound_prepped; // ambient sounds can start + bool force_refdef; // vid has changed, so we can't use a paused refdef - int parse_entities; // index (not anded off) into cl_parse_entities[] + int parse_entities; // index (not anded off) into cl_parse_entities[] - usercmd_t cmd; - usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds - int cmd_time[CMD_BACKUP]; // time sent, for calculating pings + usercmd_t cmd; + usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds + int cmd_time[CMD_BACKUP]; // time sent, for calculating pings short predicted_origins[CMD_BACKUP][3]; // for debug comparing against server float predicted_step; // for stair up smoothing - unsigned predicted_step_time; + float predicted_step_time; vec3_t predicted_origin; // generated by CL_PredictMovement vec3_t predicted_angles; @@ -115,11 +115,11 @@ typedef struct // and teleport direction changes vec3_t viewangles; - int time; // this is the time value that the client - // is rendering at. always <= cls.realtime + float time; // this is the time value that the client + // is rendering at. always <= cls.realtime float lerpfrac; // between oldframe and frame - refdef_t refdef; + refdef_t refdef; vec3_t v_forward, v_right, v_up; // set when refdef.angles is set @@ -133,10 +133,10 @@ typedef struct // non-gameserver infornamtion // FIXME: move this cinematic stuff into the cin_t structure file_t *cinematic_file; - int cinematictime; // cls.realtime for first cinematic frame - int cinematicframe; + float cinematictime; // cls.realtime for first cinematic frame + int cinematicframe; char cinematicpalette[768]; - bool cinematicpalette_active; + bool cinematicpalette_active; // // server state information @@ -196,11 +196,11 @@ typedef struct keydest_t key_dest; int framecount; - int realtime; // always increasing, no clamping, etc - float frametime; // seconds since last frame + double realtime; // always increasing, no clamping, etc + float frametime; // seconds since last frame // screen rendering information - float disable_screen; // showing loading plaque between levels + float disable_screen; // showing loading plaque between levels // or changing rendering dlls // if time gets > 30 seconds ahead, break it int disable_servercount; // when we receive a frame and cl.servercount diff --git a/engine/common.h b/engine/common.h index 144ecbef..3d51e3cc 100644 --- a/engine/common.h +++ b/engine/common.h @@ -59,12 +59,17 @@ typedef struct sizebuf_s int errorcount; // cause by errors } sizebuf_t; -void SZ_Init (sizebuf_t *buf, byte *data, int length); -void SZ_Clear (sizebuf_t *buf); -void *SZ_GetSpace (sizebuf_t *buf, int length); -void SZ_Write (sizebuf_t *buf, void *data, int length); -void SZ_Print (sizebuf_t *buf, char *data); // strcats onto the sizebuf +void _SZ_Init (sizebuf_t *buf, byte *data, int length, const char *filename, int fileline); +void _SZ_Write (sizebuf_t *buf, void *data, int length, const char *filename, int fileline); +void *_SZ_GetSpace (sizebuf_t *buf, int length, const char *filename, int fileline); +void _SZ_Print (sizebuf_t *buf, char *data, const char *filename, int fileline); +void _SZ_Clear (sizebuf_t *buf, const char *filename, int fileline); +#define SZ_Init(buf, data, length) _SZ_Init( buf, data, length, __FILE__, __LINE__) +#define SZ_Write(buf, data, length) _SZ_Write( buf, data, length, __FILE__, __LINE__) +#define SZ_Print(buf, data ) _SZ_Print( buf, data, __FILE__, __LINE__) +#define SZ_GetSpace(buf, length) _SZ_GetSpace( buf, length, __FILE__, __LINE__) +#define SZ_Clear(buf) _SZ_Clear( buf, __FILE__, __LINE__) //============================================================================ struct usercmd_s; @@ -87,26 +92,26 @@ void _MSG_WriteDeltaUsercmd (sizebuf_t *sb, struct usercmd_s *from, struct userc void _MSG_WriteDeltaEntity (struct entity_state_s *from, struct entity_state_s *to, sizebuf_t *msg, bool force, bool newentity, const char *filename, int fileline); void _MSG_WriteDir (sizebuf_t *sb, vec3_t vector, const char *filename, int fileline); void _MSG_WriteVector (sizebuf_t *sb, float *v, const char *filename, int fileline); -void _MSG_Send (msgtype_t to, vec3_t origin, edict_t *ent, const char *filename, int fileline); +void _MSG_Send (msgtype_t to, vec3_t origin, prvm_edict_t *ent, const char *filename, int fileline); -#define MSG_Begin( x ) _MSG_Begin( x, __FILE__, __LINE__); -#define MSG_WriteChar(x,y) _MSG_WriteChar (x, y, __FILE__, __LINE__); -#define MSG_WriteByte(x,y) _MSG_WriteByte (x, y, __FILE__, __LINE__); -#define MSG_WriteShort(x,y) _MSG_WriteShort (x, y, __FILE__, __LINE__); -#define MSG_WriteWord(x,y) _MSG_WriteWord (x, y, __FILE__, __LINE__); -#define MSG_WriteLong(x,y) _MSG_WriteLong (x, y, __FILE__, __LINE__); -#define MSG_WriteFloat(x, y) _MSG_WriteFloat (x, y, __FILE__, __LINE__); -#define MSG_WriteString(x,y) _MSG_WriteString (x, y, __FILE__, __LINE__); -#define MSG_WriteCoord(x, y) _MSG_WriteCoord (x, y, __FILE__, __LINE__); -#define MSG_WritePos(x, y) _MSG_WritePos (x, y, __FILE__, __LINE__); -#define MSG_WriteAngle(x, y) _MSG_WriteAngle (x, y, __FILE__, __LINE__); -#define MSG_WriteAngle16(x, y) _MSG_WriteAngle16 (x, y, __FILE__, __LINE__); -#define MSG_WriteUnterminatedString(x, y) _MSG_WriteUnterminatedString (x, y, __FILE__, __LINE__); -#define MSG_WriteDeltaUsercmd(x, y, z) _MSG_WriteDeltaUsercmd (x, y, z, __FILE__, __LINE__); -#define MSG_WriteDeltaEntity(x, y, z, t, m) _MSG_WriteDeltaEntity (x, y, z, t, m, __FILE__, __LINE__); -#define MSG_WriteDir(x, y) _MSG_WriteDir (x, y, __FILE__, __LINE__); -#define MSG_WriteVector(x, y) _MSG_WriteVector (x, y, __FILE__, __LINE__); -#define MSG_Send(x, y, z) _MSG_Send(x, y, z, __FILE__, __LINE__); +#define MSG_Begin( x ) _MSG_Begin( x, __FILE__, __LINE__) +#define MSG_WriteChar(x,y) _MSG_WriteChar (x, y, __FILE__, __LINE__) +#define MSG_WriteByte(x,y) _MSG_WriteByte (x, y, __FILE__, __LINE__) +#define MSG_WriteShort(x,y) _MSG_WriteShort (x, y, __FILE__, __LINE__) +#define MSG_WriteWord(x,y) _MSG_WriteWord (x, y, __FILE__, __LINE__) +#define MSG_WriteLong(x,y) _MSG_WriteLong (x, y, __FILE__, __LINE__) +#define MSG_WriteFloat(x, y) _MSG_WriteFloat (x, y, __FILE__, __LINE__) +#define MSG_WriteString(x,y) _MSG_WriteString (x, y, __FILE__, __LINE__) +#define MSG_WriteCoord(x, y) _MSG_WriteCoord (x, y, __FILE__, __LINE__) +#define MSG_WritePos(x, y) _MSG_WritePos (x, y, __FILE__, __LINE__) +#define MSG_WriteAngle(x, y) _MSG_WriteAngle (x, y, __FILE__, __LINE__) +#define MSG_WriteAngle16(x, y) _MSG_WriteAngle16 (x, y, __FILE__, __LINE__) +#define MSG_WriteUnterminatedString(x, y) _MSG_WriteUnterminatedString (x, y, __FILE__, __LINE__) +#define MSG_WriteDeltaUsercmd(x, y, z) _MSG_WriteDeltaUsercmd (x, y, z, __FILE__, __LINE__) +#define MSG_WriteDeltaEntity(x, y, z, t, m) _MSG_WriteDeltaEntity (x, y, z, t, m, __FILE__, __LINE__) +#define MSG_WriteDir(x, y) _MSG_WriteDir (x, y, __FILE__, __LINE__) +#define MSG_WriteVector(x, y) _MSG_WriteVector (x, y, __FILE__, __LINE__) +#define MSG_Send(x, y, z) _MSG_Send(x, y, z, __FILE__, __LINE__) void MSG_BeginReading (sizebuf_t *sb); @@ -139,7 +144,7 @@ int COM_CheckParm (char *parm); void COM_Init (void); void COM_InitArgv (int argc, char **argv); -char *CopyString (char *in); +char *CopyString (const char *in); //============================================================================ @@ -414,7 +419,7 @@ void Cmd_AddCommand (char *cmd_name, xcommand_t function); // as a clc_stringcmd instead of executed locally void Cmd_RemoveCommand (char *cmd_name); -bool Cmd_Exists (char *cmd_name); +bool Cmd_Exists (const char *cmd_name); // used by the cvar code to check for cvar / command name overlap char *Cmd_CompleteCommand (char *partial); @@ -428,11 +433,11 @@ char *Cmd_Args (void); // functions. Cmd_Argv () will return an empty string, not a NULL // if arg > argc, so string operations are always safe. -void Cmd_TokenizeString (char *text, bool macroExpand); +void Cmd_TokenizeString (const char *text, bool macroExpand); // Takes a null terminated string. Does not need to be /n terminated. // breaks the string up into arg tokens. -void Cmd_ExecuteString (char *text); +void Cmd_ExecuteString (const char *text); // Parses a single line of text into arguments and tries to execute it // as if it was typed at the console @@ -457,33 +462,32 @@ NET #define PACKET_HEADER 10 // two ints and a short typedef enum {NA_LOOPBACK, NA_BROADCAST, NA_IP, NA_IPX, NA_BROADCAST_IPX} netadrtype_t; - typedef enum {NS_CLIENT, NS_SERVER} netsrc_t; typedef struct { netadrtype_t type; - byte ip[4]; - byte ipx[10]; + byte ip[4]; + byte ipx[10]; - unsigned short port; + word port; } netadr_t; -void NET_Init (void); -void NET_Shutdown (void); +void NET_Init (void); +void NET_Shutdown (void); -void NET_Config (bool multiplayer); +void NET_Config (bool multiplayer); bool NET_GetPacket (netsrc_t sock, netadr_t *net_from, sizebuf_t *net_message); -void NET_SendPacket (netsrc_t sock, int length, void *data, netadr_t to); +void NET_SendPacket (netsrc_t sock, int length, void *data, netadr_t to); bool NET_CompareAdr (netadr_t a, netadr_t b); bool NET_CompareBaseAdr (netadr_t a, netadr_t b); bool NET_IsLocalAddress (netadr_t adr); -char *NET_AdrToString (netadr_t a); +char *NET_AdrToString (netadr_t a); bool NET_StringToAdr (char *s, netadr_t *a); -void NET_Sleep(int msec); +void NET_Sleep(float time); //============================================================================ @@ -499,8 +503,8 @@ typedef struct int dropped; // between last packet and previous - int last_received; // for timeouts - int last_sent; // for retransmits + float last_received; // for timeouts + float last_sent; // for retransmits netadr_t remote_address; int qport; // qport value to write when transmitting @@ -517,16 +521,16 @@ typedef struct int last_reliable_sequence; // sequence number of last send // reliable staging and holding areas - sizebuf_t message; // writing buffer to send to server - byte message_buf[MAX_MSGLEN-16]; // leave space for header + sizebuf_t message; // writing buffer to send to server + byte message_buf[MAX_MSGLEN - 16]; // leave space for header // message is copied to this buffer when it is first transfered int reliable_length; - byte reliable_buf[MAX_MSGLEN-16]; // unacked reliable message + byte reliable_buf[MAX_MSGLEN - 16]; // unacked reliable message } netchan_t; -extern netadr_t net_from; -extern sizebuf_t net_message; +extern netadr_t net_from; +extern sizebuf_t net_message; extern byte net_message_buffer[MAX_MSGLEN]; @@ -648,15 +652,12 @@ float crand(void); // -1 to 1 extern cvar_t *developer; extern cvar_t *dedicated; extern cvar_t *host_speeds; -extern cvar_t *log_stats; - -extern file_t *log_stats_file; // host_speeds times -extern int time_before_game; -extern int time_after_game; -extern int time_before_ref; -extern int time_after_ref; +extern float time_before_game; +extern float time_after_game; +extern float time_before_ref; +extern float time_after_ref; #define NUMVERTEXNORMALS 162 extern vec3_t bytedirs[NUMVERTEXNORMALS]; @@ -697,12 +698,12 @@ CLIENT / SERVER SYSTEMS void CL_Init (void); void CL_Drop (void); void CL_Shutdown (void); -void CL_Frame (int msec); +void CL_Frame (float time); void Con_Print (char *text); void SCR_BeginLoadingPlaque (void); void SV_Init (void); void SV_Shutdown (char *finalmsg, bool reconnect); -void SV_Frame (int msec); +void SV_Frame (float time); #endif//COMMON_H \ No newline at end of file diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 41191e78..c7c91beb 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -538,7 +538,7 @@ char *Cmd_Args (void) Cmd_MacroExpandString ====================== */ -char *Cmd_MacroExpandString (char *text) +char *Cmd_MacroExpandString (const char *text) { int i, j, count, len; bool inquote; @@ -548,7 +548,7 @@ char *Cmd_MacroExpandString (char *text) char *token, *start; inquote = false; - scan = text; + scan = (char *)text; len = strlen (scan); if (len >= MAX_STRING_CHARS) @@ -613,10 +613,10 @@ Parses the given string into command line tokens. $Cvars will be expanded unless they are in a quoted token ============ */ -void Cmd_TokenizeString (char *text, bool macroExpand) +void Cmd_TokenizeString (const char *text, bool macroExpand) { int i; - char *token; + char *token; // clear the args from the last string for (i = 0; i < cmd_argc; i++) Z_Free (cmd_argv[i]); @@ -741,16 +741,15 @@ void Cmd_RemoveCommand (char *cmd_name) Cmd_Exists ============ */ -bool Cmd_Exists (char *cmd_name) +bool Cmd_Exists (const char *cmd_name) { cmd_function_t *cmd; - for (cmd=cmd_functions ; cmd ; cmd=cmd->next) + for (cmd = cmd_functions; cmd; cmd = cmd->next) { if (!strcmp (cmd_name,cmd->name)) return true; } - return false; } @@ -800,7 +799,7 @@ A complete command line has been parsed, so try to execute it FIXME: lookupnoadd the token to speed search? ============ */ -void Cmd_ExecuteString (char *text) +void Cmd_ExecuteString (const char *text) { cmd_function_t *cmd; cmdalias_t *a; diff --git a/engine/common/cmodel.c b/engine/common/cmodel.c index 083910f1..f48fcac6 100644 --- a/engine/common/cmodel.c +++ b/engine/common/cmodel.c @@ -36,19 +36,19 @@ typedef struct typedef struct { - int contents; - int cluster; - int area; - unsigned short firstleafbrush; - unsigned short numleafbrushes; + int contents; + int cluster; + int area; + word firstleafbrush; + word numleafbrushes; } cleaf_t; typedef struct { - int contents; - int numsides; - int firstbrushside; - int checkcount; // to avoid repeated testings + int contents; + int numsides; + int firstbrushside; + int checkcount; // to avoid repeated testings } cbrush_t; typedef struct @@ -926,7 +926,7 @@ Handles offseting and rotation of the end points for moving and rotating entities ================== */ -int CM_TransformedPointContents (vec3_t p, int headnode, vec3_t origin, vec3_t angles) +int CM_TransformedPointContents (vec3_t p, int headnode, vec3_t origin, vec3_t angles) { vec3_t p_l; vec3_t temp; @@ -940,7 +940,7 @@ int CM_TransformedPointContents (vec3_t p, int headnode, vec3_t origin, vec3_t a if (headnode != box_headnode && (angles[0] || angles[1] || angles[2]) ) { - AngleVectors (angles, forward, right, up); + AngleVectorsRight (angles, forward, right, up); VectorCopy (p_l, temp); p_l[0] = DotProduct (temp, forward); @@ -978,16 +978,15 @@ bool trace_ispoint; // optimized case CM_ClipBoxToBrush ================ */ -void CM_ClipBoxToBrush (vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2, - trace_t *trace, cbrush_t *brush) +void CM_ClipBoxToBrush (vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2, trace_t *trace, cbrush_t *brush) { - int i, j; - cplane_t *plane, *clipplane; + int i, j; + cplane_t *plane, *clipplane; float dist; float enterfrac, leavefrac; vec3_t ofs; float d1, d2; - bool getout, startout; + bool getout, startout; float f; cbrushside_t *side, *leadside; @@ -995,8 +994,7 @@ void CM_ClipBoxToBrush (vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2, leavefrac = 1; clipplane = NULL; - if (!brush->numsides) - return; + if (!brush->numsides) return; c_brush_traces++; @@ -1004,7 +1002,7 @@ void CM_ClipBoxToBrush (vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2, startout = false; leadside = NULL; - for (i=0 ; inumsides ; i++) + for (i = 0; i < brush->numsides; i++) { side = &map_brushsides[brush->firstbrushside+i]; plane = side->plane; @@ -1012,17 +1010,17 @@ void CM_ClipBoxToBrush (vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2, // FIXME: special case for axial if (!trace_ispoint) - { // general box case + { + // general box case // push the plane out apropriately for mins/maxs // FIXME: use signbits into 8 way lookup for each mins/maxs - for (j=0 ; j<3 ; j++) + for (j = 0; j < 3; j++) { if (plane->normal[j] < 0) ofs[j] = maxs[j]; - else - ofs[j] = mins[j]; + else ofs[j] = mins[j]; } dist = DotProduct (ofs, plane->normal); dist = plane->dist - dist; @@ -1035,17 +1033,13 @@ void CM_ClipBoxToBrush (vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2, d1 = DotProduct (p1, plane->normal) - dist; d2 = DotProduct (p2, plane->normal) - dist; - if (d2 > 0) - getout = true; // endpoint is not in solid - if (d1 > 0) - startout = true; + if (d2 > 0) getout = true; // endpoint is not in solid + if (d1 > 0) startout = true; // if completely in front of face, no intersection - if (d1 > 0 && d2 >= d1) - return; + if (d1 > 0 && d2 >= d1) return; - if (d1 <= 0 && d2 <= 0) - continue; + if (d1 <= 0 && d2 <= 0) continue; // crosses face if (d1 > d2) @@ -1060,25 +1054,23 @@ void CM_ClipBoxToBrush (vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2, } else { // leave - f = (d1+DIST_EPSILON) / (d1-d2); - if (f < leavefrac) - leavefrac = f; + f = (d1 + DIST_EPSILON) / (d1-d2); + if (f < leavefrac) leavefrac = f; } } if (!startout) - { // original point was inside brush + { + // original point was inside brush trace->startsolid = true; - if (!getout) - trace->allsolid = true; + if (!getout) trace->allsolid = true; return; } if (enterfrac < leavefrac) { if (enterfrac > -1 && enterfrac < trace->fraction) { - if (enterfrac < 0) - enterfrac = 0; + if (enterfrac < 0) enterfrac = 0; trace->fraction = enterfrac; trace->plane = *clipplane; trace->surface = &(leadside->surface->c); @@ -1192,7 +1184,7 @@ void CM_TestInLeaf (int leafnum) if ( !(leaf->contents & trace_contents)) return; // trace line against all brushes in the leaf - for (k=0 ; knumleafbrushes ; k++) + for (k = 0; k < leaf->numleafbrushes; k++) { brushnum = map_leafbrushes[leaf->firstleafbrush+k]; b = &map_brushes[brushnum]; @@ -1200,7 +1192,7 @@ void CM_TestInLeaf (int leafnum) continue; // already checked this brush in another leaf b->checkcount = checkcount; - if ( !(b->contents & trace_contents)) + if (!(b->contents & trace_contents)) continue; CM_TestBoxInBrush (trace_mins, trace_maxs, trace_start, &trace_trace, b); if (!trace_trace.fraction) @@ -1339,15 +1331,12 @@ return; CM_BoxTrace ================== */ -trace_t CM_BoxTrace (vec3_t start, vec3_t end, - vec3_t mins, vec3_t maxs, - int headnode, int brushmask) +trace_t CM_BoxTrace (vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, int headnode, int brushmask) { int i; - checkcount++; // for multi-check avoidance - - c_traces++; // for statistics, may be zeroed + checkcount++; // for multi-check avoidance + c_traces++; // for statistics, may be zeroed // fill in a default trace memset (&trace_trace, 0, sizeof(trace_trace)); @@ -1363,26 +1352,24 @@ trace_t CM_BoxTrace (vec3_t start, vec3_t end, VectorCopy (mins, trace_mins); VectorCopy (maxs, trace_maxs); - // // check for position test special case - // if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) { int leafs[1024]; int i, numleafs; - vec3_t c1, c2; + vec3_t c1, c2; int topnode; VectorAdd (start, mins, c1); VectorAdd (start, maxs, c2); - for (i=0 ; i<3 ; i++) + for (i = 0; i < 3; i++) { c1[i] -= 1; c2[i] += 1; } numleafs = CM_BoxLeafnums_headnode (c1, c2, leafs, 1024, headnode, &topnode); - for (i=0 ; i maxs[2] ? -mins[2] : maxs[2]; } - // // general sweeping through world - // CM_RecursiveHullCheck (headnode, 0, 1, start, end); if (trace_trace.fraction == 1) @@ -1420,7 +1403,7 @@ trace_t CM_BoxTrace (vec3_t start, vec3_t end, } else { - for (i=0 ; i<3 ; i++) + for (i = 0; i < 3; i++) trace_trace.endpos[i] = start[i] + trace_trace.fraction * (end[i] - start[i]); } return trace_trace; @@ -1440,10 +1423,7 @@ rotating entities #endif -trace_t CM_TransformedBoxTrace (vec3_t start, vec3_t end, - vec3_t mins, vec3_t maxs, - int headnode, int brushmask, - vec3_t origin, vec3_t angles) +trace_t CM_TransformedBoxTrace (vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, int headnode, int brushmask, vec3_t origin, vec3_t angles) { trace_t trace; vec3_t start_l, end_l; @@ -1457,15 +1437,14 @@ trace_t CM_TransformedBoxTrace (vec3_t start, vec3_t end, VectorSubtract (end, origin, end_l); // rotate start and end into the models frame of reference - if (headnode != box_headnode && - (angles[0] || angles[1] || angles[2]) ) + if (headnode != box_headnode && !VectorIsNull(angles)) rotated = true; else rotated = false; if (rotated) { - AngleVectors (angles, forward, right, up); + AngleVectorsRight (angles, forward, right, up); VectorCopy (start_l, temp); start_l[0] = DotProduct (temp, forward); @@ -1486,7 +1465,7 @@ trace_t CM_TransformedBoxTrace (vec3_t start, vec3_t end, // FIXME: figure out how to do this with existing angles VectorCopy( angles, a ); VectorNegate ( a, a ); - AngleVectors (a, forward, right, up); + AngleVectorsRight (a, forward, right, up); VectorCopy (trace.plane.normal, temp); trace.plane.normal[0] = DotProduct (temp, forward); @@ -1741,15 +1720,15 @@ STUDIO SHARED CMODELS =============================================================================== */ -#define NUM_HULL_ROUNDS 22 -#define HULL_PRECISION 4 +#define HULL_PRECISION 4 // precision factor -word hull_table[NUM_HULL_ROUNDS] = { 0, 4, 8, 16, 18, 24, 28, 30, 32, 40, 48, 54, 56, 60, 64, 72, 80, 112, 120, 128, 140, 176 }; +word hull_table[] = { 0, 4, 8, 16, 18, 24, 28, 30, 32, 40, 48, 54, 56, 60, 64, 72, 80, 112, 120, 128, 140, 176 }; +#define NUM_HULL_ROUNDS (sizeof(hull_table) / sizeof(word)) void CM_LookUpHullSize(vec3_t size, bool down) { int i, j; - + for(i = 0; i < 3; i++) { bool negative = false; @@ -1769,7 +1748,14 @@ void CM_LookUpHullSize(vec3_t size, bool down) break; } } - size[i] = result;//copy new value + + if(j == NUM_HULL_ROUNDS) + { + //catch an error + MsgWarn("CM_LookUpHullSize: hull size[%d] is too big, reset value to default\n", i ); + size[i] = 16; //default value + } + else size[i] = result;//copy new value } } @@ -1819,7 +1805,7 @@ cmodel_t *CM_StudioModel (char *name, byte *buffer) VectorSet(out->mins, -32, -32, -32 ); VectorSet(out->maxs, 32, 32, 32 ); } - + numsmodels++; return out; } diff --git a/engine/common/common.c b/engine/common/common.c index 8c8cb7e3..fa8a6005 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -28,22 +28,19 @@ jmp_buf abortframe; int com_argc; char *com_argv[MAX_NUM_ARGVS+1]; -int realtime; - -file_t *log_stats_file; +double realtime; cvar_t *host_speeds; -cvar_t *log_stats; cvar_t *developer; cvar_t *dedicated; int server_state; // host_speeds times -int time_before_game; -int time_after_game; -int time_before_ref; -int time_after_ref; +float time_before_game; +float time_after_game; +float time_before_ref; +float time_after_ref; /* @@ -317,9 +314,9 @@ void COM_InitArgv (int argc, char **argv) } } -char *CopyString (char *in) +char *CopyString (const char *in) { - char *out; + char *out; out = Z_Malloc (strlen(in)+1); strcpy (out, in); diff --git a/engine/common/console.c b/engine/common/console.c index b6a4dd8e..d9b6ff6c 100644 --- a/engine/common/console.c +++ b/engine/common/console.c @@ -206,7 +206,7 @@ void Con_ClearNotify (void) { int i; - for (i=0 ; i= 0) - con.times[con.current % NUM_CON_TIMES] = cls.realtime; + // mark time for transparent overlay + if (con.current >= 0) con.times[con.current % NUM_CON_TIMES] = cls.realtime; } switch (c) @@ -467,24 +466,24 @@ void Con_DrawInput (void) text = key_lines[edit_line]; -// add the cursor frame - text[key_linepos] = 10+((int)(cls.realtime>>8)&1); + // add the cursor frame + text[key_linepos] = 10 + ((int)(cls.realtime * 4.0f) & 1); -// fill out remainder with spaces - for (i=key_linepos+1 ; i< con.linewidth ; i++) + // fill out remainder with spaces + for (i = key_linepos + 1; i < con.linewidth; i++) text[i] = ' '; -// prestep if horizontally scrolling + // prestep if horizontally scrolling if (key_linepos >= con.linewidth) text += 1 + key_linepos - con.linewidth; -// draw it + // draw it y = con.vislines-16; for (i=0 ; iDrawChar ( (i+1)<<3, con.vislines - 22, text[i]); -// remove cursor + // remove cursor key_lines[edit_line][key_linepos] = 0; } @@ -501,22 +500,19 @@ void Con_DrawNotify (void) int x, v; char *text; int i; - int time; + float time; char *s; int skip; v = 0; for (i= con.current-NUM_CON_TIMES+1 ; i<=con.current ; i++) { - if (i < 0) - continue; + if (i < 0) continue; time = con.times[i % NUM_CON_TIMES]; - if (time == 0) - continue; + if (time == 0) continue; time = cls.realtime - time; - if (time > con_notifytime->value*1000) - continue; - text = con.text + (i % con.totallines)*con.linewidth; + if (time > con_notifytime->value) continue; + text = con.text + (i % con.totallines) * con.linewidth; for (x = 0 ; x < con.linewidth ; x++) re->DrawChar ( (x+1)<<3, v, text[x]); @@ -547,7 +543,7 @@ void Con_DrawNotify (void) re->DrawChar ( (x+skip)<<3, v, s[x]); x++; } - re->DrawChar ( (x+skip)<<3, v, 10+((cls.realtime>>8)&1)); + re->DrawChar ((x+skip)<<3, v, 10 + ((int)(cls.realtime * 4.0f) & 1)); v += 8; } diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 3c439cee..1974a8d6 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -28,7 +28,7 @@ cvar_t *cvar_vars; Cvar_InfoValidate ============ */ -static bool Cvar_InfoValidate (char *s) +static bool Cvar_InfoValidate (const char *s) { if (strstr (s, "\\")) return false; @@ -44,7 +44,7 @@ static bool Cvar_InfoValidate (char *s) Cvar_FindVar ============ */ -cvar_t *Cvar_FindVar (char *var_name) +cvar_t *Cvar_FindVar (const char *var_name) { cvar_t *var; @@ -60,13 +60,12 @@ cvar_t *Cvar_FindVar (char *var_name) Cvar_VariableValue ============ */ -float Cvar_VariableValue (char *var_name) +float Cvar_VariableValue (const char *var_name) { cvar_t *var; var = Cvar_FindVar (var_name); - if (!var) - return 0; + if (!var) return 0; return atof (var->string); } @@ -76,13 +75,12 @@ float Cvar_VariableValue (char *var_name) Cvar_VariableString ============ */ -char *Cvar_VariableString (char *var_name) +char *Cvar_VariableString (const char *var_name) { cvar_t *var; var = Cvar_FindVar (var_name); - if (!var) - return ""; + if (!var) return ""; return var->string; } @@ -124,7 +122,7 @@ If the variable already exists, the value will not be set The flags will be or'ed in if the variable exists. ============ */ -cvar_t *Cvar_Get (char *var_name, char *var_value, int flags) +cvar_t *Cvar_Get (const char *var_name, const char *var_value, int flags) { cvar_t *var; @@ -176,7 +174,7 @@ cvar_t *Cvar_Get (char *var_name, char *var_value, int flags) Cvar_Set2 ============ */ -cvar_t *Cvar_Set2 (char *var_name, char *value, bool force) +cvar_t *Cvar_Set2 (const char *var_name, const char *value, bool force) { cvar_t *var; @@ -260,7 +258,7 @@ cvar_t *Cvar_Set2 (char *var_name, char *value, bool force) Cvar_ForceSet ============ */ -cvar_t *Cvar_ForceSet (char *var_name, char *value) +cvar_t *Cvar_ForceSet (const char *var_name, const char *value) { return Cvar_Set2 (var_name, value, true); } @@ -270,7 +268,7 @@ cvar_t *Cvar_ForceSet (char *var_name, char *value) Cvar_Set ============ */ -cvar_t *Cvar_Set (char *var_name, char *value) +cvar_t *Cvar_Set (const char *var_name, const char *value) { return Cvar_Set2 (var_name, value, false); } diff --git a/engine/common/cvar.h b/engine/common/cvar.h index 1e4423a4..53255327 100644 --- a/engine/common/cvar.h +++ b/engine/common/cvar.h @@ -6,17 +6,17 @@ #define CVAR_H extern cvar_t *cvar_vars; -cvar_t *Cvar_FindVar (char *var_name); +cvar_t *Cvar_FindVar (const char *var_name); -cvar_t *Cvar_Get (char *var_name, char *value, int flags); +cvar_t *Cvar_Get (const char *var_name, const char *value, int flags); // creates the variable if it doesn't exist, or returns the existing one // if it exists, the value will not be changed, but flags will be ORed in // that allows variables to be unarchived without needing bitflags -cvar_t *Cvar_Set (char *var_name, char *value); +cvar_t *Cvar_Set (const char *var_name, const char *value); // will create the variable if it doesn't exist -cvar_t *Cvar_ForceSet (char *var_name, char *value); +cvar_t *Cvar_ForceSet (const char *var_name, const char *value); // will set the variable even if NOSET or LATCH cvar_t *Cvar_FullSet (char *var_name, char *value, int flags); @@ -24,10 +24,10 @@ cvar_t *Cvar_FullSet (char *var_name, char *value, int flags); void Cvar_SetValue (char *var_name, float value); // expands value to a string and calls Cvar_Set -float Cvar_VariableValue (char *var_name); +float Cvar_VariableValue (const char *var_name); // returns 0 if not defined or non numeric -char *Cvar_VariableString (char *var_name); +char *Cvar_VariableString (const char *var_name); // returns an empty string if not defined char *Cvar_CompleteVariable (char *partial); diff --git a/engine/common/keys.c b/engine/common/keys.c index d4aad1e3..2400f1e5 100644 --- a/engine/common/keys.c +++ b/engine/common/keys.c @@ -737,7 +737,7 @@ Called by the system between frames for both key up and key down events Should NOT be called during an interrupt! =================== */ -void Key_Event (int key, bool down, unsigned time) +void Key_Event (int key, bool down, uint msec) { char *kb; char cmd[1024]; @@ -835,7 +835,7 @@ void Key_Event (int key, bool down, unsigned time) kb = keybindings[key]; if (kb && kb[0] == '+') { - sprintf (cmd, "-%s %i %i\n", kb+1, key, time); + sprintf (cmd, "-%s %i %i\n", kb+1, key, msec); Cbuf_AddText (cmd); } if (keyshift[key] != key) @@ -843,7 +843,7 @@ void Key_Event (int key, bool down, unsigned time) kb = keybindings[keyshift[key]]; if (kb && kb[0] == '+') { - sprintf (cmd, "-%s %i %i\n", kb+1, key, time); + sprintf (cmd, "-%s %i %i\n", kb+1, key, msec); Cbuf_AddText (cmd); } } @@ -859,7 +859,7 @@ void Key_Event (int key, bool down, unsigned time) if (kb[0] == '+') { // button commands add keynum and time as a parm - sprintf (cmd, "%s %i %i\n", kb, key, time); + sprintf (cmd, "%s %i %i\n", kb, key, msec); Cbuf_AddText (cmd); } else diff --git a/engine/common/keys.h b/engine/common/keys.h index f81b22e3..e5a6eebe 100644 --- a/engine/common/keys.h +++ b/engine/common/keys.h @@ -137,10 +137,12 @@ extern char chat_buffer[]; extern int chat_bufferlen; extern bool chat_team; -void Key_Event (int key, bool down, unsigned time); +void Key_Event (int key, bool down, uint msec); void Key_Init (void); void Key_WriteBindings (file_t *f); void Key_SetBinding (int keynum, char *binding); void Key_ClearStates (void); +char *Key_KeynumToString (int keynum); +int Key_StringToKeynum (char *str); int Key_GetKey (void); diff --git a/engine/common/menu.c b/engine/common/menu.c index 9d489600..e34d7c12 100644 --- a/engine/common/menu.c +++ b/engine/common/menu.c @@ -321,7 +321,6 @@ void M_DrawCursor( int x, int y, int f ) for ( i = 0; i < NUM_CURSOR_FRAMES; i++ ) { sprintf( cursorname, "m_cursor%d", i ); - re->RegisterPic( cursorname ); } cached = true; @@ -425,7 +424,7 @@ void M_Main_Draw (void) strlcat( litname, "_sel", 80 ); re->DrawPic( xoffset, ystart + m_main_cursor * 40 + 13, litname ); - M_DrawCursor( xoffset - 25, ystart + m_main_cursor * 40 + 11, (int)(cls.realtime / 100)%NUM_CURSOR_FRAMES ); + M_DrawCursor( xoffset - 25, ystart + m_main_cursor * 40 + 11, (int)(cls.realtime * 8.0f) % NUM_CURSOR_FRAMES ); re->DrawGetPicSize( &w, &h, "m_main_plaque" ); re->DrawPic( xoffset - 30 - w, ystart, "m_main_plaque" ); @@ -686,10 +685,8 @@ static void M_FindKeysForCommand (char *command, int *twokeys) static void KeyCursorDrawFunc( menuframework_s *menu ) { - if ( bind_grab ) - re->DrawChar( menu->x, menu->y + menu->cursor * 9, '=' ); - else - re->DrawChar( menu->x, menu->y + menu->cursor * 9, 12 + ( ( int ) ( Sys_Milliseconds() / 250 ) & 1 ) ); + if ( bind_grab ) re->DrawChar( menu->x, menu->y + menu->cursor * 9, '=' ); + else re->DrawChar( menu->x, menu->y + menu->cursor * 9, 12 + ((int)(cls.realtime * 5.0f) & 1 )); } static void DrawKeyBindingFunc( void *self ) @@ -1363,7 +1360,7 @@ END GAME MENU ============================================================================= */ -static int credits_start_time; +static float credits_start_time; static const char **credits; static char *creditsIndex[256]; static char *creditsBuffer; @@ -1455,161 +1452,20 @@ static const char *idcredits[] = 0 }; -static const char *xatcredits[] = -{ - "+QUAKE II MISSION PACK: THE RECKONING", - "+BY", - "+XATRIX ENTERTAINMENT, INC.", - "", - "+DESIGN AND DIRECTION", - "Drew Markham", - "", - "+PRODUCED BY", - "Greg Goodrich", - "", - "+PROGRAMMING", - "Rafael Paiz", - "", - "+LEVEL DESIGN / ADDITIONAL GAME DESIGN", - "Alex Mayberry", - "", - "+LEVEL DESIGN", - "Mal Blackwell", - "Dan Koppel", - "", - "+ART DIRECTION", - "Michael \"Maxx\" Kaufman", - "", - "+COMPUTER GRAPHICS SUPERVISOR AND", - "+CHARACTER ANIMATION DIRECTION", - "Barry Dempsey", - "", - "+SENIOR ANIMATOR AND MODELER", - "Jason Hoover", - "", - "+CHARACTER ANIMATION AND", - "+MOTION CAPTURE SPECIALIST", - "Amit Doron", - "", - "+ART", - "Claire Praderie-Markham", - "Viktor Antonov", - "Corky Lehmkuhl", - "", - "+INTRODUCTION ANIMATION", - "Dominique Drozdz", - "", - "+ADDITIONAL LEVEL DESIGN", - "Aaron Barber", - "Rhett Baldwin", - "", - "+3D CHARACTER ANIMATION TOOLS", - "Gerry Tyra, SA Technology", - "", - "+ADDITIONAL EDITOR TOOL PROGRAMMING", - "Robert Duffy", - "", - "+ADDITIONAL PROGRAMMING", - "Ryan Feltrin", - "", - "+PRODUCTION COORDINATOR", - "Victoria Sylvester", - "", - "+SOUND DESIGN", - "Gary Bradfield", - "", - "+MUSIC BY", - "Sonic Mayhem", - "", - "", - "", - "+SPECIAL THANKS", - "+TO", - "+OUR FRIENDS AT ID SOFTWARE", - "", - "John Carmack", - "John Cash", - "Brian Hook", - "Adrian Carmack", - "Kevin Cloud", - "Paul Steed", - "Tim Willits", - "Christian Antkow", - "Paul Jaquays", - "Brandon James", - "Todd Hollenshead", - "Barrett (Bear) Alexander", - "Dave \"Zoid\" Kirsch", - "Donna Jackson", - "", - "", - "", - "+THANKS TO ACTIVISION", - "+IN PARTICULAR:", - "", - "Marty Stratton", - "Henk \"The Original Ripper\" Hartong", - "Kevin Kraff", - "Jamey Gottlieb", - "Chris Hepburn", - "", - "+AND THE GAME TESTERS", - "", - "Tim Vanlaw", - "Doug Jacobs", - "Steven Rosenthal", - "David Baker", - "Chris Campbell", - "Aaron Casillas", - "Steve Elwell", - "Derek Johnstone", - "Igor Krinitskiy", - "Samantha Lee", - "Michael Spann", - "Chris Toft", - "Juan Valdes", - "", - "+THANKS TO INTERGRAPH COMPUTER SYTEMS", - "+IN PARTICULAR:", - "", - "Michael T. Nicolaou", - "", - "", - "Quake II Mission Pack: The Reckoning", - "(tm) (C)1998 Id Software, Inc. All", - "Rights Reserved. Developed by Xatrix", - "Entertainment, Inc. for Id Software,", - "Inc. Distributed by Activision Inc.", - "under license. Quake(R) is a", - "registered trademark of Id Software,", - "Inc. Quake II Mission Pack: The", - "Reckoning(tm), Quake II(tm), the Id", - "Software name, the \"Q II\"(tm) logo", - "and id(tm) logo are trademarks of Id", - "Software, Inc. Activision(R) is a", - "registered trademark of Activision,", - "Inc. Xatrix(R) is a registered", - "trademark of Xatrix Entertainment,", - "Inc. All other trademarks and trade", - "names are properties of their", - "respective owners.", - 0 -}; - void M_Credits_MenuDraw( void ) { - int i, y; + int i; + float y; /* ** draw the credits */ - for ( i = 0, y = viddef.height - ( ( cls.realtime - credits_start_time ) / 40.0F ); credits[i] && y < viddef.height; y += 10, i++ ) + for ( i = 0, y = viddef.height - (( cls.realtime - credits_start_time ) * 40.0f ); credits[i] && y < viddef.height; y += 10, i++ ) { int j, stringoffset = 0; int bold = false; - if ( y <= -8 ) - continue; + if ( y <= -8 ) continue; if ( credits[i][0] == '+' ) { @@ -1628,15 +1484,13 @@ void M_Credits_MenuDraw( void ) x = ( viddef.width - strlen( credits[i] ) * 8 - stringoffset * 8 ) / 2 + ( j + stringoffset ) * 8; - if ( bold ) - re->DrawChar( x, y, credits[i][j+stringoffset] + 128 ); - else - re->DrawChar( x, y, credits[i][j+stringoffset] ); + if ( bold ) re->DrawChar( x, y, credits[i][j+stringoffset] + 128 ); + else re->DrawChar( x, y, credits[i][j+stringoffset] ); } } - if ( y < 0 ) - credits_start_time = cls.realtime; + // loop demonstration + if ( y < 0 ) credits_start_time = cls.realtime; } const char *M_Credits_Key( int key ) @@ -1656,12 +1510,12 @@ void M_Menu_Credits_f( void ) { int n; int count; - char *p; + char *p; int isdeveloper = 0; creditsBuffer = NULL; - creditsBuffer = FS_LoadFile ("credits", &count ); - if (count != -1) + creditsBuffer = FS_LoadFile ("scripts/credits.txt", &count ); + if (count) { p = creditsBuffer; for (n = 0; n < 255; n++) @@ -1670,18 +1524,15 @@ void M_Menu_Credits_f( void ) while (*p != '\r' && *p != '\n') { p++; - if (--count == 0) - break; + if (--count == 0) break; } if (*p == '\r') { *p++ = 0; - if (--count == 0) - break; + if (--count == 0) break; } *p++ = 0; - if (--count == 0) - break; + if (--count == 0) break; } creditsIndex[++n] = 0; credits = creditsIndex; @@ -3488,7 +3339,7 @@ void PlayerConfig_MenuDraw( void ) refdef.height = 168; refdef.fov_x = 40; refdef.fov_y = CalcFov( refdef.fov_x, refdef.width, refdef.height ); - refdef.time = cls.realtime*0.001; + refdef.time = cls.realtime; if ( s_pmi[s_player_model_box.curvalue].skindisplaynames ) { @@ -3677,8 +3528,7 @@ void M_Draw (void) SCR_DirtyScreen (); // dim everything behind it down - if (cl.cinematictime > 0) - re->DrawFill (0, 0, viddef.width, viddef.height, 0); + if (cl.cinematictime > 0) re->DrawFill (0, 0, viddef.width, viddef.height, 0); else re->DrawFadeScreen (); m_drawfunc (); diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 3c268980..0af0e874 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -93,7 +93,7 @@ void Netchan_Init (void) int port; // pick a port value that should be nice and random - port = Sys_Milliseconds() & 0xffff; + port = RANDOM_LONG(1, 65535); Msg("netchan port %d\n", port ); diff --git a/engine/common/net_msg.c b/engine/common/net_msg.c index 967b2304..a34d87fc 100644 --- a/engine/common/net_msg.c +++ b/engine/common/net_msg.c @@ -536,29 +536,37 @@ void MSG_ReadData (sizebuf_t *msg_read, void *data, int len) ======================= */ -void SZ_Init (sizebuf_t *buf, byte *data, int length) +void _SZ_Init (sizebuf_t *buf, byte *data, int length, const char *filename, int fileline) { memset (buf, 0, sizeof(*buf)); + + if(buf == NULL) Sys_Error("SZ_Clear: sizebuf == NULL (called at %s:%i)\n", filename, fileline ); + if(data == NULL) Sys_Error("SZ_Init: data == NULL (called at %s:%i)\n", filename, fileline ); + if(length <= 0) Sys_Error("SZ_Init: length <= 0 (called at %s:%i)\n", filename, fileline ); + buf->data = data; buf->maxsize = length; } -void SZ_Clear (sizebuf_t *buf) +void _SZ_Clear (sizebuf_t *buf, const char *filename, int fileline) { + if(buf == NULL) Sys_Error("SZ_Clear: sizebuf == NULL (called at %s:%i)\n", filename, fileline ); + if(buf->overflowed) MsgWarn("SZ_Clear: clearing buffer was overflowed\n"); + buf->cursize = 0; buf->overflowed = false; } -void *SZ_GetSpace (sizebuf_t *buf, int length) +void *_SZ_GetSpace (sizebuf_t *buf, int length, const char *filename, int fileline) { void *data; if (buf->cursize + length > buf->maxsize) { if (length > buf->maxsize) - Com_Error (ERR_DROP, "SZ_GetSpace: %i is > full buffer size", length); + Sys_Error("SZ_GetSpace: buf->cursize[%d] + length[%d] > buf->maxsize[%d](called at %s:%i)\n", buf->cursize, length, buf->maxsize, filename, fileline ); - Msg ("SZ_GetSpace: overflow [cursize %d maxsize %d]\n", buf->cursize + length, buf->maxsize ); + Msg ("SZ_GetSpace: overflow [cursize %d maxsize %d](called at %s:%i)\n", buf->cursize + length, buf->maxsize, filename, fileline ); SZ_Clear (buf); buf->overflowed = true; } @@ -568,20 +576,20 @@ void *SZ_GetSpace (sizebuf_t *buf, int length) return data; } -void SZ_Write (sizebuf_t *buf, void *data, int length) +void _SZ_Write (sizebuf_t *buf, void *data, int length, const char *filename, int fileline) { - Mem_Copy(SZ_GetSpace(buf, length), data, length); + pi->Mem.Copy(_SZ_GetSpace(buf, length, filename, fileline), data, length, filename, fileline); } -void SZ_Print (sizebuf_t *buf, char *data) +void _SZ_Print (sizebuf_t *buf, char *data, const char *filename, int fileline) { int len; len = strlen(data) + 1; if (buf->cursize) { - if (buf->data[buf->cursize - 1]) Mem_Copy ((byte *)SZ_GetSpace(buf, len),data,len); //no trailing 0 - else Mem_Copy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0 + if (buf->data[buf->cursize - 1]) pi->Mem.Copy((byte *)_SZ_GetSpace(buf, len, filename, fileline), data, len, filename, fileline); // no trailing 0 + else pi->Mem.Copy((byte *)_SZ_GetSpace(buf, len - 1, filename, fileline) - 1, data, len, filename, fileline); // write over trailing 0 } - else Mem_Copy ((byte *)SZ_GetSpace(buf, len),data,len); + else pi->Mem.Copy((byte *)_SZ_GetSpace(buf, len, filename, fileline), data, len, filename, fileline); } \ No newline at end of file diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 8d4fd6d5..ccc8dfb5 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -717,9 +717,9 @@ void NET_Config (bool multiplayer) } // sleeps msec or until net socket is ready -void NET_Sleep(int msec) +void NET_Sleep(float time) { - struct timeval timeout; + struct timeval timeout; fd_set fdset; extern cvar_t *dedicated; int i; @@ -738,8 +738,8 @@ void NET_Sleep(int msec) if (ipx_sockets[NS_SERVER] > i) i = ipx_sockets[NS_SERVER]; } - timeout.tv_sec = msec/1000; - timeout.tv_usec = (msec%1000)*1000; + timeout.tv_sec = time; + timeout.tv_usec = fmod(time, 1.0f) * 1.0f; select(i+1, &fdset, NULL, NULL, &timeout); } diff --git a/engine/common/pmove.c b/engine/common/pmove.c index 33a73f4a..d224d9a9 100644 --- a/engine/common/pmove.c +++ b/engine/common/pmove.c @@ -312,7 +312,7 @@ void PM_Friction (void) { friction = pm_friction; control = speed < pm_stopspeed ? pm_stopspeed : speed; - drop += control*friction*pml.frametime; + drop += control*friction * pml.frametime; } // apply water friction @@ -1083,7 +1083,7 @@ void PM_ClampAngles (void) else if (pm->viewangles[PITCH] < 271 && pm->viewangles[PITCH] >= 180) pm->viewangles[PITCH] = 271; } - AngleVectors (pm->viewangles, pml.forward, pml.right, pml.up); + AngleVectorsRight (pm->viewangles, pml.forward, pml.right, pml.up); } /* @@ -1198,7 +1198,7 @@ void Pmove (pmove_t *pmove) angles[PITCH] = angles[PITCH] - 360; angles[PITCH] /= 3; - AngleVectors (angles, pml.forward, pml.right, pml.up); + AngleVectorsRight (angles, pml.forward, pml.right, pml.up); PM_AirMove (); } } diff --git a/engine/common/qmenu.c b/engine/common/qmenu.c index 9f5a29a7..c1ccd1a0 100644 --- a/engine/common/qmenu.c +++ b/engine/common/qmenu.c @@ -116,17 +116,13 @@ void Field_Draw( menufield_s *f ) else offset = f->cursor; - if ( ( ( int ) ( Sys_Milliseconds() / 250 ) ) & 1 ) + if ((( int )( cls.realtime * 4.0f)) & 1 ) { - Draw_Char( f->generic.x + f->generic.parent->x + ( offset + 2 ) * 8 + 8, - f->generic.y + f->generic.parent->y, - 11 ); + Draw_Char( f->generic.x + f->generic.parent->x + ( offset + 2 ) * 8 + 8, f->generic.y + f->generic.parent->y, 11 ); } else { - Draw_Char( f->generic.x + f->generic.parent->x + ( offset + 2 ) * 8 + 8, - f->generic.y + f->generic.parent->y, - ' ' ); + Draw_Char( f->generic.x + f->generic.parent->x + ( offset + 2 ) * 8 + 8, f->generic.y + f->generic.parent->y, ' ' ); } } } @@ -389,11 +385,11 @@ void Menu_Draw( menuframework_s *menu ) { if ( item->flags & QMF_LEFT_JUSTIFY ) { - Draw_Char( menu->x + item->x - 24 + item->cursor_offset, menu->y + item->y, 12 + ( ( int ) ( Sys_Milliseconds()/250 ) & 1 ) ); + Draw_Char( menu->x + item->x - 24 + item->cursor_offset, menu->y + item->y, 12 + ((int)(cls.realtime * 5.0f) & 1 )); } else { - Draw_Char( menu->x + item->cursor_offset, menu->y + item->y, 12 + ( ( int ) ( Sys_Milliseconds()/250 ) & 1 ) ); + Draw_Char( menu->x + item->cursor_offset, menu->y + item->y, 12 + ((int)(cls.realtime * 5.0f) & 1 )); } } diff --git a/engine/engine.dsp b/engine/engine.dsp index f9c36faa..927bb526 100644 --- a/engine/engine.dsp +++ b/engine/engine.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 1 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c -# ADD CPP /nologo /MT /W3 /GX /O2 /Ob0 /I "./" /I "prvm" /I "common" /I "server" /I "client" /I "../public" /I "../platform/formats" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /Ob0 /I "./" /I "prvm" /I "common" /I "server" /I "client" /I "../public" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -80,7 +80,7 @@ SOURCE="$(InputPath)" # PROP Ignore_Export_Lib 1 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c -# ADD CPP /nologo /MTd /W3 /Gm /Gi /GX /ZI /Od /I "./" /I "prvm" /I "common" /I "server" /I "client" /I "../public" /I "../platform/formats" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /FD /c +# ADD CPP /nologo /MTd /W3 /Gm /Gi /GX /ZI /Od /I "./" /I "prvm" /I "common" /I "server" /I "client" /I "../public" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "_DEBUG" /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 @@ -91,7 +91,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 -# ADD LINK32 winmm.lib wsock32.lib kernel32.lib user32.lib gdi32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 gdi32.lib winmm.lib kernel32.lib user32.lib wsock32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept # SUBTRACT LINK32 /incremental:no /map /nodefaultlib # Begin Custom Build TargetDir=\XASH3D\src_main\!source\temp\engine\!debug @@ -266,6 +266,10 @@ SOURCE=.\server\sv_main.c # End Source File # Begin Source File +SOURCE=.\server\sv_phys.c +# End Source File +# Begin Source File + SOURCE=.\server\sv_save.c # End Source File # Begin Source File @@ -296,6 +300,18 @@ SOURCE=.\vid_dll.c SOURCE=.\vid_menu.c # End Source File +# Begin Source File + +SOURCE=.\prvm\vm_cmds.c +# End Source File +# Begin Source File + +SOURCE=.\prvm\vm_edict.c +# End Source File +# Begin Source File + +SOURCE=.\prvm\vm_exec.c +# End Source File # End Group # Begin Group "Header Files" @@ -334,6 +350,10 @@ SOURCE=.\common\cvar.h # End Source File # Begin Source File +SOURCE=.\prvm\edict.h +# End Source File +# Begin Source File + SOURCE=.\engine.h # End Source File # Begin Source File @@ -350,6 +370,14 @@ SOURCE=..\public\platform.h # End Source File # Begin Source File +SOURCE=.\prvm\progdefs.h +# End Source File +# Begin Source File + +SOURCE=.\prvm\progsvm.h +# End Source File +# Begin Source File + SOURCE=..\public\qfiles.h # End Source File # Begin Source File @@ -376,6 +404,14 @@ SOURCE=.\sound.h SOURCE=.\common\vid.h # End Source File +# Begin Source File + +SOURCE=.\prvm\vm_cmds.h +# End Source File +# Begin Source File + +SOURCE=.\prvm\vm_exec.h +# End Source File # End Group # End Target # End Project diff --git a/engine/engine.h b/engine/engine.h index 45425430..609d946b 100644 --- a/engine/engine.h +++ b/engine/engine.h @@ -21,6 +21,7 @@ #include "basemath.h" #include "qfiles.h" #include +#include "vprogs.h" #include "bspmodel.h" #include "const.h" #include "common.h" @@ -75,6 +76,7 @@ filesystem manager #define FS_FileBase( x, y ) pi->Fs.FileBase( x, y ) #define FS_Find( x ) pi->Fs.Search( x, false ) #define FS_Printf pi->Fs.Printf +#define FS_Print pi->Fs.Print #define FS_Seek pi->Fs.Seek #define FS_Tell pi->Fs.Tell #define FS_Gets pi->Fs.Gets @@ -127,4 +129,10 @@ void Host_Init ( char *funcname, int argc, char **argv ); void Host_Main ( void ); void Host_Free ( void ); +// +// in_win.c +// +extern int mouse_x, mouse_y, old_mouse_x, old_mouse_y, mx_accum, my_accum; + + #endif//ENGINE_H \ No newline at end of file diff --git a/engine/engine.plg b/engine/engine.plg new file mode 100644 index 00000000..8cf436f0 --- /dev/null +++ b/engine/engine.plg @@ -0,0 +1,89 @@ + + +
+

Build Log

+

+--------------------Configuration: engine - Win32 Debug-------------------- +

+

Command Lines

+Creating temporary file "C:\Temp\RSP31BD.tmp" with contents +[ +/nologo /MTd /W3 /Gm /Gi /GX /ZI /Od /I "./" /I "prvm" /I "common" /I "server" /I "client" /I "../public" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR"..\temp\engine\!debug/" /Fo"..\temp\engine\!debug/" /Fd"..\temp\engine\!debug/" /FD /c +"D:\XASH3D\src_main\!source\engine\system.c" +] +Creating command line "cl.exe @C:\Temp\RSP31BD.tmp" +Creating temporary file "C:\Temp\RSP31BE.tmp" with contents +[ +gdi32.lib winmm.lib kernel32.lib user32.lib wsock32.lib /nologo /subsystem:windows /dll /incremental:yes /pdb:"..\temp\engine\!debug/engine.pdb" /debug /machine:I386 /out:"..\temp\engine\!debug/engine.dll" /implib:"..\temp\engine\!debug/engine.lib" /pdbtype:sept +"\XASH3D\src_main\!source\temp\engine\!debug\cl_cin.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\cl_ents.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\cl_fx.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\cl_game.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\cl_input.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\cl_inv.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\cl_main.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\cl_newfx.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\cl_parse.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\cl_pred.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\cl_scrn.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\cl_tent.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\cl_view.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\cmd.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\cmodel.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\common.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\console.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\crc.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\cvar.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\host.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\in_win.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\keys.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\md4.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\menu.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\net_chan.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\net_msg.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\net_wins.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\pmove.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\qmenu.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\snd_dma.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\snd_mem.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\snd_mix.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\snd_win.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\sv_ccmds.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\sv_ents.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\sv_game.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\sv_init.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\sv_main.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\sv_phys.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\sv_save.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\sv_send.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\sv_studio.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\sv_user.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\sv_world.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\system.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\vid_dll.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\vid_menu.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\vm_cmds.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\vm_edict.obj" +"\XASH3D\src_main\!source\temp\engine\!debug\vm_exec.obj" +] +Creating command line "link.exe @C:\Temp\RSP31BE.tmp" +Creating temporary file "C:\Temp\RSP31BF.bat" with contents +[ +@echo off +copy \XASH3D\src_main\!source\temp\engine\!debug\engine.dll "D:\Xash3D\bin\engine.dll" +] +Creating command line "C:\Temp\RSP31BF.bat" +Compiling... +system.c +Linking... +

Output Window

+Performing Custom Build Step on \XASH3D\src_main\!source\temp\engine\!debug\engine.dll +‘Є®ЇЁа®ў ­® д ©«®ў: 1. + + + +

Results

+engine.dll - 0 error(s), 0 warning(s) +
+ + diff --git a/engine/host.c b/engine/host.c index e3f823ba..3c63d4cd 100644 --- a/engine/host.c +++ b/engine/host.c @@ -7,6 +7,7 @@ #include #include #include "engine.h" +#include "progsvm.h" platform_exp_t *pi; //fundamental callbacks @@ -16,6 +17,8 @@ bool Minimized; bool is_dedicated; extern uint sys_msg_time; +dword host_framecount = 0; + void Key_Init (void); void SCR_EndLoadingPlaque (void); @@ -96,6 +99,8 @@ void Host_Init (char *funcname, int argc, char **argv) Key_Init (); + PRVM_Init(); + // we need to add the early commands twice, because // a basedir or cddir needs to be set before execing // config files, but we want other parms to override @@ -112,7 +117,6 @@ void Host_Init (char *funcname, int argc, char **argv) Cmd_AddCommand ("error", Com_Error_f); host_speeds = Cvar_Get ("host_speeds", "0", 0); - log_stats = Cvar_Get ("log_stats", "0", 0); developer = Cvar_Get ("developer", "0", 0); timescale = Cvar_Get ("timescale", "1", 0); fixedtime = Cvar_Get ("fixedtime", "0", 0); @@ -160,30 +164,6 @@ void Host_Frame (double time) if (setjmp (abortframe) ) return; // an ERR_DROP was thrown - if ( log_stats->modified ) - { - log_stats->modified = false; - if ( log_stats->value ) - { - if ( log_stats_file ) - { - FS_Close( log_stats_file ); - log_stats_file = 0; - } - log_stats_file = FS_Open( "stats.log", "w" ); - if ( log_stats_file ) - FS_Printf( log_stats_file, "entities,dlights,parts,frame time\n" ); - } - else - { - if ( log_stats_file ) - { - FS_Close( log_stats_file ); - log_stats_file = 0; - } - } - } - if (showtrace->value) { extern int c_traces, c_brush_traces; @@ -198,10 +178,10 @@ void Host_Frame (double time) do { s = Sys_ConsoleInput (); - if (s) Cbuf_AddText (va("%s\n",s)); + if(s) Cbuf_AddText (va("%s\n",s)); } while (s); Cbuf_Execute (); - + if (host_speeds->value) time_before = Sys_DoubleTime(); SV_Frame (time); @@ -211,6 +191,7 @@ void Host_Frame (double time) CL_Frame (time); if (host_speeds->value) time_after = Sys_DoubleTime(); + if (host_speeds->value) { double all, sv, gm, cl, rf; @@ -234,9 +215,9 @@ Host_Main void Host_Main( void ) { MSG msg; - int time, oldtime, newtime; + float oldtime, newtime; - oldtime = Sys_Milliseconds (); + oldtime = Sys_DoubleTime(); //first call // main window message loop while (1) @@ -244,7 +225,7 @@ void Host_Main( void ) // if at a full screen console, don't update unless needed if (Minimized || (dedicated && dedicated->value) ) { - Sleep (1); + Sleep(1); } while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) @@ -254,15 +235,11 @@ void Host_Main( void ) TranslateMessage (&msg); DispatchMessage (&msg); } - do - { - newtime = Sys_Milliseconds (); - time = newtime - oldtime; - } while (time < 1); - _controlfp( _PC_24, _MCW_PC ); - Host_Frame (time); + newtime = Sys_DoubleTime(); + curtime = newtime - oldtime; + Host_Frame (curtime); oldtime = newtime; } } diff --git a/engine/prvm/edict.h b/engine/prvm/edict.h new file mode 100644 index 00000000..d7f99670 --- /dev/null +++ b/engine/prvm/edict.h @@ -0,0 +1,70 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +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 the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +#ifndef PROGS_H +#define PROGS_H + +#define MAX_ENT_CLUSTERS 16 + +#define SVF_NOCLIENT 0x00000001 // don't send entity to clients, even if it has effects +#define SVF_DEADMONSTER 0x00000002 // treat as CONTENTS_DEADMONSTER for collision +#define SVF_MONSTER 0x00000004 // treat as CONTENTS_MONSTER for collision + +typedef struct link_s +{ + struct link_s *prev, *next; + int entitynumber; +} link_t; + +typedef struct server_edict_s +{ + // these fields must match with edict_state_t pos. don't move it! + bool free; + float freetime; + + bool move; + + int linkcount; + + link_t area; // linked to a division node or leaf + int num_clusters; // if -1, use headnode instead + int clusternums[MAX_ENT_CLUSTERS]; + int headnode; // unused if num_clusters != -1 + int areanum, areanum2; + + int flags; // SVF_NOCLIENT, SVF_DEADMONSTER, SVF_MONSTER, etc + vec3_t mins, maxs; + vec3_t absmin, absmax, size; + + + + vec3_t moved_from; // used to keep track of where objects were before they were + vec3_t moved_fromangles; // moved, in case they need to be moved back + + // trace info + solid_t solid; + int clipmask; + struct server_edict_s *owner; + + entity_state_t state; + player_state_t *client; + +} server_edict_t; + +#endif \ No newline at end of file diff --git a/engine/prvm/progdefs.h b/engine/prvm/progdefs.h new file mode 100644 index 00000000..e205f993 --- /dev/null +++ b/engine/prvm/progdefs.h @@ -0,0 +1,139 @@ +#ifndef PROGDEFS_H +#define PROGDEFS_H + +typedef struct globalvars_s +{ + int pad[28]; + int self; + int other; + int world; + float time; + float frametime; + float force_retouch; + string_t mapname; + float deathmatch; + float coop; + float teamplay; + float serverflags; + float total_secrets; + float total_monsters; + float found_secrets; + float killed_monsters; + float parm[16]; + vec3_t v_forward; + vec3_t v_up; + vec3_t v_right; + float trace_allsolid; + float trace_startsolid; + float trace_fraction; + vec3_t trace_endpos; + vec3_t trace_plane_normal; + float trace_plane_dist; + int trace_ent; + float trace_inopen; + float trace_inwater; + int msg_entity; + + // game_export_s + func_t main; // Init + func_t StartFrame; // RunFrame + func_t EndFrame; // EndFrame + func_t PlayerPreThink; // ClientThink + func_t PlayerPostThink; // ClientThink + func_t ClientKill; // ??? + func_t ClientConnect; // ClientConnect + func_t PutClientInServer; // ClientBegin + func_t ClientDisconnect; // ClientDisconnect + func_t SetNewParms; // ??? + func_t SetChangeParms; // ??? + +} globalvars_t; + +typedef struct entvars_s +{ + float modelindex; + vec3_t absmin; + vec3_t absmax; + float ltime; + float movetype; + float solid; + vec3_t origin; + vec3_t oldorigin; + vec3_t velocity; + vec3_t angles; + vec3_t avelocity; + vec3_t punchangle; + string_t classname; + string_t model; + float frame; + float skin; + float body; + float effects; + float sequence; + float renderfx; + vec3_t mins; + vec3_t maxs; + vec3_t size; + func_t touch; + func_t use; + func_t think; + func_t blocked; + float nextthink; + int groundentity; + float health; + float frags; + float weapon; + string_t weaponmodel; + float weaponframe; + float currentammo; + float ammo_shells; + float ammo_nails; + float ammo_rockets; + float ammo_cells; + float items; + float takedamage; + int chain; + float deadflag; + vec3_t view_ofs; + float button0; + float button1; + float button2; + float impulse; + float fixangle; + vec3_t v_angle; + float idealpitch; + string_t netname; + int enemy; + float flags; + float colormap; + float team; + float max_health; + float teleport_time; + float armortype; + float armorvalue; + float waterlevel; + float watertype; + float ideal_yaw; + float yaw_speed; + int aiment; + int goalentity; + float spawnflags; + string_t target; + string_t targetname; + float dmg_take; + float dmg_save; + int dmg_inflictor; + int owner; + vec3_t movedir; + string_t message; + float sounds; + string_t noise; + string_t noise1; + string_t noise2; + string_t noise3; + +} entvars_t; + +#define PROG_CRC_SERVER 21645 + +#endif//PROGDEFS_H \ No newline at end of file diff --git a/engine/prvm/progsvm.h b/engine/prvm/progsvm.h new file mode 100644 index 00000000..f8eccde5 --- /dev/null +++ b/engine/prvm/progsvm.h @@ -0,0 +1,407 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +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 the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +/* +This is a try to make the vm more generic, it is mainly based on the progs.h file. +For the license refer to progs.h. + +Generic means, less as possible hard-coded links with the other parts of the engine. +This means no edict_engineprivate struct usage, etc. +The code uses void pointers instead. +*/ + +#ifndef PROGSVM_H +#define PROGSVM_H + +#include "vprogs.h" // defs shared with qcc +#include "progdefs.h" // generated by program cdefs +#include "edict.h" + +typedef struct prvm_stack_s +{ + int s; + mfunction_t *f; + +} prvm_stack_t; + +typedef union prvm_eval_s +{ + string_t string; + float _float; + float vector[3]; + func_t function; + int ivector[3]; + int _int; + int edict; + +} prvm_eval_t; + +//with this the crc isn't needed for fields. +typedef struct prvm_fieldvars_s +{ + int ofs; + int type; + const char *name; +} prvm_fieldvars_t; + +// AK: I dont call it engine private cause it doesnt really belongs to the engine +// it belongs to prvm. +typedef struct edict_state_s +{ + bool free; + float freetime; + +} edict_state_t; + +struct prvm_edict_s +{ + // engine-private fields (stored in dynamically resized array) + union + { + edict_state_t *ed; // vm edict state + void *vp; // generic edict + server_edict_t *sv; // server edict + } priv; + + // QuakeC fields (stored in dynamically resized array) + union + { + void *vp; // generic entvars + entvars_t *sv; // server entvars + } fields; + +}; + +#define PRVM_GETEDICTFIELDVALUE(ed, fieldoffset) (fieldoffset ? (prvm_eval_t *)((unsigned char *)ed->fields.vp + fieldoffset) : NULL) +#define PRVM_GETGLOBALFIELDVALUE(fieldoffset) (fieldoffset ? (prvm_eval_t *)((unsigned char *)prog->globals.generic + fieldoffset) : NULL) + +#define PRVM_FE_CLASSNAME 8 +#define PRVM_FE_CHAIN 4 +#define PRVM_OP_STATE 1 + +#define PRVM_MAX_STACK_DEPTH 1024 +#define PRVM_LOCALSTACK_SIZE 16384 + +#define PRVM_MAX_OPENFILES 256 +#define PRVM_MAX_OPENSEARCHES 128 + +typedef void (*prvm_builtin_t) (void); + +// [INIT] variables flagged with this token can be initialized by 'you' +// NOTE: external code has to create and free the mempools but everything else is done by prvm ! +typedef struct prvm_prog_s +{ + dprograms_t *progs; + mfunction_t *functions; + char *strings; + int stringssize; + ddef_t *fielddefs; + ddef_t *globaldefs; + dstatement_t *statements; + int edict_size; // in bytes + int edictareasize; // LordHavoc: in bytes (for bound checking) + + int *statement_linenums;// NULL if not available + + double *statement_profile; // only incremented if prvm_statementprofiling is on + + union + { + float *generic; + globalvars_t *server; + } globals; + + int maxknownstrings; + int numknownstrings; + // this is updated whenever a string is removed or added + // (simple optimization of the free string search) + int firstfreeknownstring; + const char **knownstrings; + unsigned char *knownstrings_freeable; + const char ***stringshash; + + // all memory allocations related to this vm_prog (code, edicts, strings) + byte *progs_mempool; // [INIT] + + prvm_builtin_t *builtins; // [INIT] + int numbuiltins; // [INIT] + + int argc; + + int trace; + mfunction_t *xfunction; + int xstatement; + + // stacktrace writes into stack[MAX_STACK_DEPTH] + // thus increase the array, so depth wont be overwritten + prvm_stack_t stack[PRVM_MAX_STACK_DEPTH+1]; + int depth; + + int localstack[PRVM_LOCALSTACK_SIZE]; + int localstack_used; + + word filecrc; + + //============================================================================ + // until this point everything also exists (with the pr_ prefix) in the old vm + + file_t *openfiles[PRVM_MAX_OPENFILES]; + search_t *opensearches[PRVM_MAX_OPENSEARCHES]; + + // copies of some vars that were former read from sv + int num_edicts; + // number of edicts for which space has been (should be) allocated + int max_edicts; // [INIT] + // used instead of the constant MAX_EDICTS + int limit_edicts; // [INIT] + + // number of reserved edicts (allocated from 1) + int reserved_edicts; // [INIT] + + prvm_edict_t *edicts; + void *edictsfields; + void *edictprivate; + + // size of the engine private struct + int edictprivate_size; // [INIT] + + // has to be updated every frame - so the vm time is up-to-date + // AK changed so time will point to the time field (if there is one) else it points to _time + // actually should be double, but qc doesnt support it + float *time; + float _time; + + // allow writing to world entity fields, this is set by server init and + // cleared before first server frame + bool allowworldwrites; + + // name of the prog, e.g. "Server", "Client" or "Menu" (used for text output) + char *name; // [INIT] + + // flag - used to store general flags like PRVM_GE_SELF, etc. + int flag; + + char *extensionstring; // [INIT] + + bool loadintoworld; // [INIT] + + // used to indicate whether a prog is loaded + bool loaded; + +// now passes as parameter of PRVM_LoadProgs +// char **required_func; +// int numrequiredfunc; + + //============================================================================ + + ddef_t *self; // if self != 0 then there is a global self + + //============================================================================ + // function pointers + + void (*begin_increase_edicts)(void); // [INIT] used by PRVM_MEM_Increase_Edicts + void (*end_increase_edicts)(void); // [INIT] + + void (*init_edict)(prvm_edict_t *edict); // [INIT] used by PRVM_ED_ClearEdict + void (*free_edict)(prvm_edict_t *ed); // [INIT] used by PRVM_ED_Free + + void (*count_edicts)(void); // [INIT] used by PRVM_ED_Count_f + + bool (*load_edict)(prvm_edict_t *ent); // [INIT] used by PRVM_ED_LoadFromFile + + void (*init_cmd)(void); // [INIT] used by PRVM_InitProg + void (*reset_cmd)(void); // [INIT] used by PRVM_ResetProg + + void (*error_cmd)(const char *format, ...); // [INIT] + +} prvm_prog_t; + +extern prvm_prog_t *prog; + +#define PRVM_MAXPROGS 3 +#define PRVM_SERVERPROG 0 // actually not used at the moment +#define PRVM_CLIENTPROG 1 +#define PRVM_MENUPROG 2 + +extern prvm_prog_t prvm_prog_list[PRVM_MAXPROGS]; + +//============================================================================ +// prvm_cmds part + +extern prvm_builtin_t vm_sv_builtins[]; +extern prvm_builtin_t vm_cl_builtins[]; +extern prvm_builtin_t vm_m_builtins[]; + +extern const int vm_sv_numbuiltins; +extern const int vm_cl_numbuiltins; +extern const int vm_m_numbuiltins; + +extern char * vm_sv_extensions; +extern char * vm_cl_extensions; +extern char * vm_m_extensions; + +void VM_SV_Cmd_Init(void); +void VM_SV_Cmd_Reset(void); + +void VM_CL_Cmd_Init(void); +void VM_CL_Cmd_Reset(void); + +void VM_M_Cmd_Init(void); +void VM_M_Cmd_Reset(void); + +void VM_Cmd_Init(void); +void VM_Cmd_Reset(void); +//============================================================================ + +void PRVM_Init (void); + +void PRVM_ExecuteProgram (func_t fnum, const char *errormessage); + +#define PRVM_Alloc(buffersize) _PRVM_Alloc(buffersize, __FILE__, __LINE__) +#define PRVM_Free(buffer) _PRVM_Free(buffer, __FILE__, __LINE__) +#define PRVM_FreeAll() _PRVM_FreeAll(__FILE__, __LINE__) +void *_PRVM_Alloc (size_t buffersize, const char *filename, int fileline); +void _PRVM_Free (void *buffer, const char *filename, int fileline); +void _PRVM_FreeAll (const char *filename, int fileline); + +void PRVM_Profile (int maxfunctions, int mininstructions); +void PRVM_Profile_f (void); +void PRVM_PrintFunction_f (void); + +void PRVM_PrintState(void); +void PRVM_CrashAll (void); +void PRVM_Crash (void); + +int PRVM_ED_FindFieldOffset(const char *field); +int PRVM_ED_FindGlobalOffset(const char *global); +ddef_t *PRVM_ED_FindField (const char *name); +mfunction_t *PRVM_ED_FindFunction (const char *name); + +void PRVM_MEM_IncreaseEdicts(void); + +prvm_edict_t *PRVM_ED_Alloc (void); +void PRVM_ED_Free (prvm_edict_t *ed); +void PRVM_ED_ClearEdict (prvm_edict_t *e); + +void PRVM_PrintFunctionStatements (const char *name); +void PRVM_ED_Print(prvm_edict_t *ed); +void PRVM_ED_Write (file_t *f, prvm_edict_t *ed); +const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent); + +void PRVM_ED_WriteGlobals (file_t *f); +void PRVM_ED_ParseGlobals (const char *data); + +void PRVM_ED_LoadFromFile (const char *data); + +prvm_edict_t *PRVM_EDICT_NUM_ERROR(int n, char *filename, int fileline); +#define PRVM_EDICT_NUM(n) (((n) >= 0 && (n) < prog->max_edicts) ? prog->edicts + (n) : PRVM_EDICT_NUM_ERROR(n, __FILE__, __LINE__)) +#define PRVM_EDICT_NUM_UNSIGNED(n) (((n) < prog->max_edicts) ? prog->edicts + (n) : PRVM_EDICT_NUM_ERROR(n, __FILE__, __LINE__)) +#define PRVM_EDICT_FROM_AREA(l) ((prvm_edict_t *)((byte *)l - (int)&(((prvm_edict_t *)0)->priv.sv->area))) + +//int NUM_FOR_EDICT_ERROR(prvm_edict_t *e); +#define PRVM_NUM_FOR_EDICT(e) ((int)((prvm_edict_t *)(e) - prog->edicts)) +//int PRVM_NUM_FOR_EDICT(prvm_edict_t *e); + +#define PRVM_NEXT_EDICT(e) ((e) + 1) + +#define PRVM_EDICT_TO_PROG(e) (PRVM_NUM_FOR_EDICT(e)) +//int PRVM_EDICT_TO_PROG(prvm_edict_t *e); +#define PRVM_PROG_TO_EDICT(n) (PRVM_EDICT_NUM(n)) +//prvm_edict_t *PRVM_PROG_TO_EDICT(int n); + +//============================================================================ + +#define PRVM_G_FLOAT(o) (prog->globals.generic[o]) +#define PRVM_G_INT(o) (*(int *)&prog->globals.generic[o]) +#define PRVM_G_EDICT(o) (PRVM_PROG_TO_EDICT(*(int *)&prog->globals.generic[o])) +#define PRVM_G_EDICTNUM(o) PRVM_NUM_FOR_EDICT(PRVM_G_EDICT(o)) +#define PRVM_G_VECTOR(o) (&prog->globals.generic[o]) +#define PRVM_G_STRING(o) (PRVM_GetString(*(string_t *)&prog->globals.generic[o])) +//#define PRVM_G_FUNCTION(o) (*(func_t *)&prog->globals.generic[o]) + +// FIXME: make these go away? +#define PRVM_E_FLOAT(e,o) (((float*)e->fields.vp)[o]) +#define PRVM_E_INT(e,o) (((int*)e->fields.vp)[o]) +//#define PRVM_E_VECTOR(e,o) (&((float*)e->fields.vp)[o]) +#define PRVM_E_STRING(e,o) (PRVM_GetString(*(string_t *)&((float*)e->fields.vp)[o])) + +extern int prvm_type_size[8]; // for consistency : I think a goal of this sub-project is to +// make the new vm mostly independent from the old one, thus if it's necessary, I copy everything + +void PRVM_Init_Exec(void); + +void PRVM_ED_PrintEdicts_f (void); +void PRVM_ED_PrintNum (int ent); + +const char *PRVM_GetString(int num); +int PRVM_SetEngineString(const char *s); +int PRVM_AllocString(size_t bufferlength, char **pointer); +void PRVM_FreeString(int num); + +//============================================================================ + +// used as replacement for a prog stack +//#define PRVM_DEBUGPRSTACK + +#ifdef PRVM_DEBUGPRSTACK +#define PRVM_Begin if(prog != 0) Con_Printf("prog not 0(prog = %i) in file: %s line: %i!\n", PRVM_GetProgNr(), __FILE__, __LINE__) +#define PRVM_End prog = 0 +#else +#define PRVM_Begin +#define PRVM_End prog = 0 +#endif + +//#define PRVM_SAFENAME +#ifndef PRVM_SAFENAME +# define PRVM_NAME (prog->name) +#else +# define PRVM_NAME (prog->name ? prog->name : "Unknown prog name") +#endif + +// helper macro to make function pointer calls easier +#define PRVM_GCALL(func) if(prog->func) prog->func + +#define PRVM_ERROR prog->error_cmd + +// other prog handling functions +bool PRVM_SetProgFromString(const char *str); +void PRVM_SetProg(int prognr); + +/* +Initializing a vm: +Call InitProg with the num +Set up the fields marked with [INIT] in the prog struct +Load a program with LoadProgs +*/ +void PRVM_InitProg(int prognr); +// LoadProgs expects to be called right after InitProg +void PRVM_LoadProgs (const char *filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_fieldvars_t *required_field); +void PRVM_ResetProg(void); + +bool PRVM_ProgLoaded(int prognr); + +int PRVM_GetProgNr(void); + +void VM_Warning(const char *fmt, ...); +void VM_Error(const char *fmt, ...); + +// TODO: fill in the params +//void PRVM_Create(); + +#endif diff --git a/engine/prvm/vm_cmds.c b/engine/prvm/vm_cmds.c new file mode 100644 index 00000000..0c37adb3 --- /dev/null +++ b/engine/prvm/vm_cmds.c @@ -0,0 +1,3154 @@ +// AK +// Basically every vm builtin cmd should be in here. +// All 3 builtin and extension lists can be found here +// cause large (I think they will) parts are from pr_cmds the same copyright like in pr_cmds +// also applies here + +#include "engine.h" +#include "vm_cmds.h" +#include "sound.h" +#include "screen.h" +#include "keys.h" + +//============================================================================ +// Common + +// temp string handling +// LordHavoc: added this to semi-fix the problem of using many ftos calls in a print +static char vm_string_temp[VM_STRINGTEMP_BUFFERS][VM_STRINGTEMP_LENGTH]; +static int vm_string_tempindex = 0; +extern renderer_exp_t *re; + +// TODO: (move vm_files and vm_fssearchlist to prvm_prog_t struct) +// TODO: move vm_files and vm_fssearchlist back [9/13/2006 Black] +char *VM_GetTempString(void) +{ + char *s; + s = vm_string_temp[vm_string_tempindex]; + vm_string_tempindex = (vm_string_tempindex + 1) % VM_STRINGTEMP_BUFFERS; + return s; +} + +void VM_CheckEmptyString (const char *s) +{ + if (s[0] <= ' ') + PRVM_ERROR ("%s: Bad string", PRVM_NAME); +} + +//============================================================================ +//BUILT-IN FUNCTIONS + +void VM_VarString(int first, char *out, int outlength) +{ + int i; + const char *s; + char *outend; + + outend = out + outlength - 1; + for (i = first;i < prog->argc && out < outend;i++) + { + s = PRVM_G_STRING((OFS_PARM0+i*3)); + while (out < outend && *s) + *out++ = *s++; + } + *out++ = 0; +} + +/* +================= +VM_checkextension + +returns true if the extension is supported by the server + +checkextension(extensionname) +================= +*/ + +// kind of helper function +static bool checkextension(const char *name) +{ + int len; + char *e, *start; + len = (int)strlen(name); + + for (e = prog->extensionstring;*e;e++) + { + while (*e == ' ') + e++; + if (!*e) + break; + start = e; + while (*e && *e != ' ') + e++; + if ((e - start) == len && !strncasecmp(start, name, len)) + return true; + } + return false; +} + +void VM_checkextension (void) +{ + VM_SAFEPARMCOUNT(1,VM_checkextension); + + PRVM_G_FLOAT(OFS_RETURN) = checkextension(PRVM_G_STRING(OFS_PARM0)); +} + +/* +================= +VM_error + +This is a TERMINAL error, which will kill off the entire prog. +Dumps self. + +error(value) +================= +*/ +void VM_error (void) +{ + prvm_edict_t *ed; + char string[VM_STRINGTEMP_LENGTH]; + + VM_VarString(0, string, sizeof(string)); + Msg("======%s ERROR in %s:\n%s\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string); + if(prog->self) + { + ed = PRVM_G_EDICT(prog->self->ofs); + PRVM_ED_Print(ed); + } + + PRVM_ERROR ("%s: Program error in function %s:\n%s\nTip: read above for entity information\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string); +} + +/* +================= +VM_objerror + +Dumps out self, then an error message. The program is aborted and self is +removed, but the level can continue. + +objerror(value) +================= +*/ +void VM_objerror (void) +{ + prvm_edict_t *ed; + char string[VM_STRINGTEMP_LENGTH]; + + VM_VarString(0, string, sizeof(string)); + Msg("======OBJECT ERROR======\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string); + if(prog->self) + { + ed = PRVM_G_EDICT (prog->self->ofs); + PRVM_ED_Print(ed); + + PRVM_ED_Free (ed); + } + else + // objerror has to display the object fields -> else call + PRVM_ERROR ("VM_objecterror: self not defined !"); + Msg("%s OBJECT ERROR in %s:\n%s\nTip: read above for entity information\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string); +} + +/* +================= +VM_print (actually used only by client and menu) + +print to console + +print(string) +================= +*/ +void VM_print (void) +{ + char string[VM_STRINGTEMP_LENGTH]; + + VM_VarString(0, string, sizeof(string)); + Con_Print(string); +} + +/* +================= +VM_bprint + +broadcast print to everyone on server + +bprint(...[string]) +================= +*/ +void VM_bprint (void) +{ + char string[VM_STRINGTEMP_LENGTH]; + + if(sv.state != ss_game) + { + VM_Warning("VM_bprint: game is not server(%s) !\n", PRVM_NAME); + return; + } + + VM_VarString(0, string, sizeof(string)); + //SV_BroadcastPrintf(PRINT_HIGH, string); + Msg(string); +} + +/* +================= +VM_sprint (menu & client but only if server.active == true) + +single print to a specific client + +sprint(float clientnum,...[string]) +================= +*/ +void VM_sprint( void ) +{ + int num; + char string[VM_STRINGTEMP_LENGTH]; + + //find client for this entity + num = (int)PRVM_G_FLOAT(OFS_PARM0); + if (sv.state != ss_game || num < 0 || num >= maxclients->value || !svs.clients[num].state != cs_spawned) + { + VM_Warning("VM_sprint: %s: invalid client or server is not active !\n", PRVM_NAME); + return; + } + + VM_VarString(1, string, sizeof(string)); + SV_ClientPrintf (svs.clients+(num - 1), PRINT_HIGH, "%s", string ); +} + +/* +================= +VM_centerprint + +single print to the screen + +centerprint(clientent, value) +================= +*/ +void VM_centerprint (void) +{ + char string[VM_STRINGTEMP_LENGTH]; + + VM_VarString(0, string, sizeof(string)); + + SCR_CenterPrint(string); +} + +/* +================= +VM_normalize + +vector normalize(vector) +================= +*/ +void VM_normalize (void) +{ + float *value1; + vec3_t newvalue; + double f; + + VM_SAFEPARMCOUNT(1,VM_normalize); + + value1 = PRVM_G_VECTOR(OFS_PARM0); + + f = DotProduct(value1, value1); + if (f) + { + f = 1.0 / sqrt(f); + VectorScale(value1, f, newvalue); + } + else VectorClear(newvalue); + + VectorCopy (newvalue, PRVM_G_VECTOR(OFS_RETURN)); +} + +/* +================= +VM_vlen + +scalar vlen(vector) +================= +*/ +void VM_vlen (void) +{ + VM_SAFEPARMCOUNT(1,VM_vlen); + PRVM_G_FLOAT(OFS_RETURN) = VectorLength(PRVM_G_VECTOR(OFS_PARM0)); +} + +/* +================= +VM_vectoyaw + +float vectoyaw(vector) +================= +*/ +void VM_vectoyaw (void) +{ + float *value1; + float yaw; + + VM_SAFEPARMCOUNT(1,VM_vectoyaw); + + value1 = PRVM_G_VECTOR(OFS_PARM0); + + if (value1[1] == 0 && value1[0] == 0) + yaw = 0; + else + { + yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI); + if (yaw < 0) + yaw += 360; + } + + PRVM_G_FLOAT(OFS_RETURN) = yaw; +} + + +/* +================= +VM_vectoangles + +vector vectoangles(vector) +================= +*/ +void VM_vectoangles (void) +{ + float *value1; + float forward; + float yaw, pitch; + + VM_SAFEPARMCOUNT(1,VM_vectoangles); + + value1 = PRVM_G_VECTOR(OFS_PARM0); + + if (value1[1] == 0 && value1[0] == 0) + { + yaw = 0; + if (value1[2] > 0) + pitch = 90; + else + pitch = 270; + } + else + { + // LordHavoc: optimized a bit + if (value1[0]) + { + yaw = (atan2(value1[1], value1[0]) * 180 / M_PI); + if (yaw < 0) + yaw += 360; + } + else if (value1[1] > 0) + yaw = 90; + else + yaw = 270; + + forward = sqrt(value1[0]*value1[0] + value1[1]*value1[1]); + pitch = (atan2(value1[2], forward) * 180 / M_PI); + if (pitch < 0) + pitch += 360; + } + + PRVM_G_FLOAT(OFS_RETURN+0) = pitch; + PRVM_G_FLOAT(OFS_RETURN+1) = yaw; + PRVM_G_FLOAT(OFS_RETURN+2) = 0; +} + +/* +================= +VM_random + +Returns a number from 0<= num < 1 + +float random() +================= +*/ +void VM_random (void) +{ + VM_SAFEPARMCOUNT(0,VM_random); + + PRVM_G_FLOAT(OFS_RETURN) = RANDOM_LONG(0, 1); +} + +/* +================= +PF_sound + +Each entity can have eight independant sound sources, like voice, +weapon, feet, etc. + +Channel 0 is an auto-allocate channel, the others override anything +already running on that entity/channel pair. + +An attenuation of 0 will play full volume everywhere in the level. +Larger attenuations will drop off. + +================= +*/ +/* +void PF_sound (void) +{ + char *sample; + int channel; + prvm_edict_t *entity; + int volume; + float attenuation; + + entity = PRVM_G_EDICT(OFS_PARM0); + channel = PRVM_G_FLOAT(OFS_PARM1); + sample = PRVM_G_STRING(OFS_PARM2); + volume = PRVM_G_FLOAT(OFS_PARM3) * 255; + attenuation = PRVM_G_FLOAT(OFS_PARM4); + + if (volume < 0 || volume > 255) + Host_Error ("SV_StartSound: volume = %i", volume); + + if (attenuation < 0 || attenuation > 4) + Host_Error ("SV_StartSound: attenuation = %f", attenuation); + + if (channel < 0 || channel > 7) + Host_Error ("SV_StartSound: channel = %i", channel); + + SV_StartSound (entity, channel, sample, volume, attenuation); +} +*/ + +/* +========= +VM_localsound + +localsound(string sample) +========= +*/ +void VM_localsound(void) +{ + const char *s; + + VM_SAFEPARMCOUNT(1, VM_localsound); + + s = PRVM_G_STRING(OFS_PARM0); + + if(!S_StartLocalSound(s)) + { + PRVM_G_FLOAT(OFS_RETURN) = -4; + VM_Warning("VM_localsound: Failed to play %s for %s !\n", s, PRVM_NAME); + return; + } + + PRVM_G_FLOAT(OFS_RETURN) = 1; +} + +/* +================= +VM_break + +break() +================= +*/ +void VM_break (void) +{ + PRVM_ERROR ("%s: break statement", PRVM_NAME); +} + +//============================================================================ + +/* +================= +VM_localcmd + +Sends text over to the client's execution buffer + +[localcmd (string, ...) or] +cmd (string, ...) +================= +*/ +void VM_localcmd (void) +{ + char string[VM_STRINGTEMP_LENGTH]; + VM_VarString(0, string, sizeof(string)); + Cbuf_AddText(string); +} + +/* +================= +VM_cvar + +float cvar (string) +================= +*/ +void VM_cvar (void) +{ + VM_SAFEPARMCOUNT(1,VM_cvar); + + PRVM_G_FLOAT(OFS_RETURN) = Cvar_VariableValue(PRVM_G_STRING(OFS_PARM0)); +} + +/* +================= +VM_cvar_string + +const string VM_cvar_string (string) +================= +*/ +void VM_cvar_string(void) +{ + char *out; + const char *name; + const char *cvar_string; + VM_SAFEPARMCOUNT(1,VM_cvar_string); + + name = PRVM_G_STRING(OFS_PARM0); + + if(!name) + PRVM_ERROR("VM_cvar_string: %s: null string", PRVM_NAME); + + VM_CheckEmptyString(name); + + out = VM_GetTempString(); + + cvar_string = Cvar_VariableString(name); + + strlcpy(out, cvar_string, VM_STRINGTEMP_LENGTH); + + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(out); +} + +/* +================= +VM_cvar_set + +void cvar_set (string,string) +================= +*/ +void VM_cvar_set (void) +{ + VM_SAFEPARMCOUNT(2,VM_cvar_set); + + Cvar_Set(PRVM_G_STRING(OFS_PARM0), PRVM_G_STRING(OFS_PARM1)); +} + +/* +========= +VM_dprint + +dprint(...[string]) +========= +*/ +void VM_dprint (void) +{ + char string[VM_STRINGTEMP_LENGTH]; + if (developer->value) + { + VM_VarString(0, string, sizeof(string)); + Msg("%s", string); + } +} + +/* +========= +VM_ftos + +string ftos(float) +========= +*/ + +void VM_ftos (void) +{ + float v; + char *s; + + VM_SAFEPARMCOUNT(1, VM_ftos); + + v = PRVM_G_FLOAT(OFS_PARM0); + + s = VM_GetTempString(); + if ((float)((int)v) == v) + sprintf(s, "%i", (int)v); + else + sprintf(s, "%f", v); + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(s); +} + +/* +========= +VM_fabs + +float fabs(float) +========= +*/ + +void VM_fabs (void) +{ + float v; + + VM_SAFEPARMCOUNT(1,VM_fabs); + + v = PRVM_G_FLOAT(OFS_PARM0); + PRVM_G_FLOAT(OFS_RETURN) = fabs(v); +} + +/* +========= +VM_vtos + +string vtos(vector) +========= +*/ + +void VM_vtos (void) +{ + char *s; + + VM_SAFEPARMCOUNT(1,VM_vtos); + + s = VM_GetTempString(); + sprintf (s, "'%5.1f %5.1f %5.1f'", PRVM_G_VECTOR(OFS_PARM0)[0], PRVM_G_VECTOR(OFS_PARM0)[1], PRVM_G_VECTOR(OFS_PARM0)[2]); + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(s); +} + +/* +========= +VM_etos + +string etos(entity) +========= +*/ + +void VM_etos (void) +{ + char *s; + + VM_SAFEPARMCOUNT(1, VM_etos); + + s = VM_GetTempString(); + sprintf (s, "entity %i", PRVM_G_EDICTNUM(OFS_PARM0)); + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(s); +} + +/* +========= +VM_stof + +float stof(...[string]) +========= +*/ +void VM_stof(void) +{ + char string[VM_STRINGTEMP_LENGTH]; + VM_VarString(0, string, sizeof(string)); + PRVM_G_FLOAT(OFS_RETURN) = atof(string); +} + +/* +======================== +VM_itof + +float itof(intt ent) +======================== +*/ +void VM_itof(void) +{ + VM_SAFEPARMCOUNT(1, VM_itof); + PRVM_G_FLOAT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0); +} + +/* +======================== +VM_ftoe + +entity ftoe(float num) +======================== +*/ +void VM_ftoe(void) +{ + int ent; + VM_SAFEPARMCOUNT(1, VM_ftoe); + + ent = (int)PRVM_G_FLOAT(OFS_PARM0); + if (ent < 0 || ent >= MAX_EDICTS || PRVM_PROG_TO_EDICT(ent)->priv.ed->free) + ent = 0; // return world instead of a free or invalid entity + + PRVM_G_INT(OFS_RETURN) = ent; +} + +/* +========= +VM_spawn + +entity spawn() +========= +*/ + +void VM_spawn (void) +{ + prvm_edict_t *ed; + prog->xfunction->builtinsprofile += 20; + ed = PRVM_ED_Alloc(); + VM_RETURN_EDICT(ed); +} + +/* +========= +VM_remove + +remove(entity e) +========= +*/ + +void VM_remove (void) +{ + prvm_edict_t *ed; + prog->xfunction->builtinsprofile += 20; + + VM_SAFEPARMCOUNT(1, VM_remove); + + ed = PRVM_G_EDICT(OFS_PARM0); + if( PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts ) + { + if (developer->value >= 1) + VM_Warning( "VM_remove: tried to remove the null entity or a reserved entity!\n" ); + } + else if( ed->priv.ed->free ) + { + if (developer->value >= 1) + VM_Warning( "VM_remove: tried to remove an already freed entity!\n" ); + } + else + PRVM_ED_Free (ed); +// if (ed == prog->edicts) +// PRVM_ERROR ("remove: tried to remove world"); +// if (PRVM_NUM_FOR_EDICT(ed) <= sv.maxclients) +// Host_Error("remove: tried to remove a client"); +} + +/* +========= +VM_find + +entity find(entity start, .string field, string match) +========= +*/ + +void VM_find (void) +{ + int e; + int f; + const char *s, *t; + prvm_edict_t *ed; + + VM_SAFEPARMCOUNT(3,VM_find); + + e = PRVM_G_EDICTNUM(OFS_PARM0); + f = PRVM_G_INT(OFS_PARM1); + s = PRVM_G_STRING(OFS_PARM2); + + // LordHavoc: apparently BloodMage does a find(world, weaponmodel, "") and + // expects it to find all the monsters, so we must be careful to support + // searching for "" + if (!s) + s = ""; + + for (e++ ; e < prog->num_edicts ; e++) + { + prog->xfunction->builtinsprofile++; + ed = PRVM_EDICT_NUM(e); + if (ed->priv.ed->free) + continue; + t = PRVM_E_STRING(ed,f); + if (!t) + t = ""; + if (!strcmp(t,s)) + { + VM_RETURN_EDICT(ed); + return; + } + } + + VM_RETURN_EDICT(prog->edicts); +} + +/* +========= +VM_findfloat + + entity findfloat(entity start, .float field, float match) + entity findentity(entity start, .entity field, entity match) +========= +*/ +// LordHavoc: added this for searching float, int, and entity reference fields +void VM_findfloat (void) +{ + int e; + int f; + float s; + prvm_edict_t *ed; + + VM_SAFEPARMCOUNT(3,VM_findfloat); + + e = PRVM_G_EDICTNUM(OFS_PARM0); + f = PRVM_G_INT(OFS_PARM1); + s = PRVM_G_FLOAT(OFS_PARM2); + + for (e++ ; e < prog->num_edicts ; e++) + { + prog->xfunction->builtinsprofile++; + ed = PRVM_EDICT_NUM(e); + if (ed->priv.ed->free) + continue; + if (PRVM_E_FLOAT(ed,f) == s) + { + VM_RETURN_EDICT(ed); + return; + } + } + + VM_RETURN_EDICT(prog->edicts); +} + +/* +========= +VM_findchain + +entity findchain(.string field, string match) +========= +*/ +// chained search for strings in entity fields +// entity(.string field, string match) findchain = #402; +void VM_findchain (void) +{ + int i; + int f; + int chain_of; + const char *s, *t; + prvm_edict_t *ent, *chain; + + VM_SAFEPARMCOUNT(2,VM_findchain); + + // is the same like !(prog->flag & PRVM_FE_CHAIN) - even if the operator precedence is another + if(!prog->flag & PRVM_FE_CHAIN) + PRVM_ERROR("VM_findchain: %s doesnt have a chain field !", PRVM_NAME); + + chain_of = PRVM_ED_FindField("chain")->ofs; + + chain = prog->edicts; + + f = PRVM_G_INT(OFS_PARM0); + s = PRVM_G_STRING(OFS_PARM1); + + // LordHavoc: apparently BloodMage does a find(world, weaponmodel, "") and + // expects it to find all the monsters, so we must be careful to support + // searching for "" + if (!s) + s = ""; + + ent = PRVM_NEXT_EDICT(prog->edicts); + for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent)) + { + prog->xfunction->builtinsprofile++; + if (ent->priv.ed->free) + continue; + t = PRVM_E_STRING(ent,f); + if (!t) + t = ""; + if (strcmp(t,s)) + continue; + + PRVM_E_INT(ent,chain_of) = PRVM_NUM_FOR_EDICT(chain); + chain = ent; + } + + VM_RETURN_EDICT(chain); +} + +/* +========= +VM_findchainfloat + +entity findchainfloat(.string field, float match) +entity findchainentity(.string field, entity match) +========= +*/ +// LordHavoc: chained search for float, int, and entity reference fields +// entity(.string field, float match) findchainfloat = #403; +void VM_findchainfloat (void) +{ + int i; + int f; + int chain_of; + float s; + prvm_edict_t *ent, *chain; + + VM_SAFEPARMCOUNT(2, VM_findchainfloat); + + if(!prog->flag & PRVM_FE_CHAIN) + PRVM_ERROR("VM_findchainfloat: %s doesnt have a chain field !", PRVM_NAME); + + chain_of = PRVM_ED_FindField("chain")->ofs; + + chain = (prvm_edict_t *)prog->edicts; + + f = PRVM_G_INT(OFS_PARM0); + s = PRVM_G_FLOAT(OFS_PARM1); + + ent = PRVM_NEXT_EDICT(prog->edicts); + for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent)) + { + prog->xfunction->builtinsprofile++; + if (ent->priv.ed->free) + continue; + if (PRVM_E_FLOAT(ent,f) != s) + continue; + + PRVM_E_INT(ent,chain_of) = PRVM_EDICT_TO_PROG(chain); + chain = ent; + } + + VM_RETURN_EDICT(chain); +} + +/* +======================== +VM_findflags + +entity findflags(entity start, .float field, float match) +======================== +*/ +// LordHavoc: search for flags in float fields +void VM_findflags (void) +{ + int e; + int f; + int s; + prvm_edict_t *ed; + + VM_SAFEPARMCOUNT(3, VM_findflags); + + + e = PRVM_G_EDICTNUM(OFS_PARM0); + f = PRVM_G_INT(OFS_PARM1); + s = (int)PRVM_G_FLOAT(OFS_PARM2); + + for (e++ ; e < prog->num_edicts ; e++) + { + prog->xfunction->builtinsprofile++; + ed = PRVM_EDICT_NUM(e); + if (ed->priv.ed->free) + continue; + if (!PRVM_E_FLOAT(ed,f)) + continue; + if ((int)PRVM_E_FLOAT(ed,f) & s) + { + VM_RETURN_EDICT(ed); + return; + } + } + + VM_RETURN_EDICT(prog->edicts); +} + +/* +======================== +VM_findchainflags + +entity findchainflags(.float field, float match) +======================== +*/ +// LordHavoc: chained search for flags in float fields +void VM_findchainflags (void) +{ + int i; + int f; + int s; + int chain_of; + prvm_edict_t *ent, *chain; + + VM_SAFEPARMCOUNT(2, VM_findchainflags); + + if(!prog->flag & PRVM_FE_CHAIN) + PRVM_ERROR("VM_findchainflags: %s doesnt have a chain field !", PRVM_NAME); + + chain_of = PRVM_ED_FindField("chain")->ofs; + + chain = (prvm_edict_t *)prog->edicts; + + f = PRVM_G_INT(OFS_PARM0); + s = (int)PRVM_G_FLOAT(OFS_PARM1); + + ent = PRVM_NEXT_EDICT(prog->edicts); + for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent)) + { + prog->xfunction->builtinsprofile++; + if (ent->priv.ed->free) + continue; + if (!PRVM_E_FLOAT(ent,f)) + continue; + if (!((int)PRVM_E_FLOAT(ent,f) & s)) + continue; + + PRVM_E_INT(ent,chain_of) = PRVM_EDICT_TO_PROG(chain); + chain = ent; + } + + VM_RETURN_EDICT(chain); +} + +/* +========= +VM_coredump + +coredump() +========= +*/ +void VM_coredump (void) +{ + VM_SAFEPARMCOUNT(0,VM_coredump); + + Cbuf_AddText("prvm_edicts "); + Cbuf_AddText(PRVM_NAME); + Cbuf_AddText("\n"); +} + +/* +========= +VM_stackdump + +stackdump() +========= +*/ +void PRVM_StackTrace(void); +void VM_stackdump (void) +{ + VM_SAFEPARMCOUNT(0, VM_stackdump); + + PRVM_StackTrace(); +} + +/* +========= +VM_crash + +crash() +========= +*/ + +void VM_crash(void) +{ + VM_SAFEPARMCOUNT(0, VM_crash); + + PRVM_ERROR("Crash called by %s",PRVM_NAME); +} + +/* +========= +VM_traceon + +traceon() +========= +*/ +void VM_traceon (void) +{ + VM_SAFEPARMCOUNT(0,VM_traceon); + + prog->trace = true; +} + +/* +========= +VM_traceoff + +traceoff() +========= +*/ +void VM_traceoff (void) +{ + VM_SAFEPARMCOUNT(0,VM_traceoff); + + prog->trace = false; +} + +/* +========= +VM_eprint + +eprint(entity e) +========= +*/ +void VM_eprint (void) +{ + VM_SAFEPARMCOUNT(1,VM_eprint); + + PRVM_ED_PrintNum (PRVM_G_EDICTNUM(OFS_PARM0)); +} + +/* +========= +VM_rint + +float rint(float) +========= +*/ +void VM_rint (void) +{ + float f; + VM_SAFEPARMCOUNT(1,VM_rint); + + f = PRVM_G_FLOAT(OFS_PARM0); + if (f > 0) + PRVM_G_FLOAT(OFS_RETURN) = floor(f + 0.5); + else + PRVM_G_FLOAT(OFS_RETURN) = ceil(f - 0.5); +} + +/* +========= +VM_floor + +float floor(float) +========= +*/ +void VM_floor (void) +{ + VM_SAFEPARMCOUNT(1,VM_floor); + + PRVM_G_FLOAT(OFS_RETURN) = floor(PRVM_G_FLOAT(OFS_PARM0)); +} + +/* +========= +VM_ceil + +float ceil(float) +========= +*/ +void VM_ceil (void) +{ + VM_SAFEPARMCOUNT(1,VM_ceil); + + PRVM_G_FLOAT(OFS_RETURN) = ceil(PRVM_G_FLOAT(OFS_PARM0)); +} + + +/* +============= +VM_nextent + +entity nextent(entity) +============= +*/ +void VM_nextent (void) +{ + int i; + prvm_edict_t *ent; + + i = PRVM_G_EDICTNUM(OFS_PARM0); + while (1) + { + prog->xfunction->builtinsprofile++; + i++; + if (i == prog->num_edicts) + { + VM_RETURN_EDICT(prog->edicts); + return; + } + ent = PRVM_EDICT_NUM(i); + if (!ent->priv.ed->free) + { + VM_RETURN_EDICT(ent); + return; + } + } +} + +//============================================================================= + +/* +============== +VM_changelevel +server and menu + +changelevel(string map) +============== +*/ +void VM_changelevel (void) +{ + const char *s; + + VM_SAFEPARMCOUNT(1, VM_changelevel); + + if(sv.state != ss_game) + { + VM_Warning("VM_changelevel: game is not server (%s)\n", PRVM_NAME); + return; + } + + s = PRVM_G_STRING(OFS_PARM0); + Cbuf_AddText (va("changelevel %s\n",s)); +} + +/* +========= +VM_sin + +float sin(float) +========= +*/ +void VM_sin (void) +{ + VM_SAFEPARMCOUNT(1,VM_sin); + PRVM_G_FLOAT(OFS_RETURN) = sin(PRVM_G_FLOAT(OFS_PARM0)); +} + +/* +========= +VM_cos +float cos(float) +========= +*/ +void VM_cos (void) +{ + VM_SAFEPARMCOUNT(1,VM_cos); + PRVM_G_FLOAT(OFS_RETURN) = cos(PRVM_G_FLOAT(OFS_PARM0)); +} + +/* +========= +VM_sqrt + +float sqrt(float) +========= +*/ +void VM_sqrt (void) +{ + VM_SAFEPARMCOUNT(1,VM_sqrt); + PRVM_G_FLOAT(OFS_RETURN) = sqrt(PRVM_G_FLOAT(OFS_PARM0)); +} + +/* +================= +VM_randomvec + +Returns a vector of length < 1 and > 0 + +vector randomvec() +================= +*/ +void VM_randomvec (void) +{ + vec3_t temp; + //float length; + + VM_SAFEPARMCOUNT(0, VM_randomvec); + + //// WTF ?? + do + { + temp[0] = (rand()&32767) * (2.0 / 32767.0) - 1.0; + temp[1] = (rand()&32767) * (2.0 / 32767.0) - 1.0; + temp[2] = (rand()&32767) * (2.0 / 32767.0) - 1.0; + } + while (DotProduct(temp, temp) >= 1); + VectorCopy (temp, PRVM_G_VECTOR(OFS_RETURN)); + + /* + temp[0] = (rand()&32767) * (2.0 / 32767.0) - 1.0; + temp[1] = (rand()&32767) * (2.0 / 32767.0) - 1.0; + temp[2] = (rand()&32767) * (2.0 / 32767.0) - 1.0; + // length returned always > 0 + length = (rand()&32766 + 1) * (1.0 / 32767.0) / VectorLength(temp); + VectorScale(temp,length, temp);*/ + //VectorCopy(temp, PRVM_G_VECTOR(OFS_RETURN)); +} + +//============================================================================= + +/* +========= +VM_registercvar + +float registercvar (string name, string value, float flags) +========= +*/ +void VM_registercvar (void) +{ + const char *name, *value; + int flags; + + VM_SAFEPARMCOUNT(3,VM_registercvar); + + name = PRVM_G_STRING(OFS_PARM0); + value = PRVM_G_STRING(OFS_PARM1); + flags = (int)PRVM_G_FLOAT(OFS_PARM2); + PRVM_G_FLOAT(OFS_RETURN) = 0; + + if(flags > CVAR_MAXFLAGSVAL) + return; + +// first check to see if it has already been defined + if (Cvar_FindVar (name)) + return; + +// check for overlap with a command + if (Cmd_Exists (name)) + { + VM_Warning("VM_registercvar: %s is a command\n", name); + return; + } + + Cvar_Get(name, value, flags); + + PRVM_G_FLOAT(OFS_RETURN) = 1; // success +} + +/* +================= +VM_min + +returns the minimum of two supplied floats + +float min(float a, float b, ...[float]) +================= +*/ +void VM_min (void) +{ + // LordHavoc: 3+ argument enhancement suggested by FrikaC + if (prog->argc == 2) + PRVM_G_FLOAT(OFS_RETURN) = min(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1)); + else if (prog->argc >= 3) + { + int i; + float f = PRVM_G_FLOAT(OFS_PARM0); + for (i = 1;i < prog->argc;i++) + if (PRVM_G_FLOAT((OFS_PARM0+i*3)) < f) + f = PRVM_G_FLOAT((OFS_PARM0+i*3)); + PRVM_G_FLOAT(OFS_RETURN) = f; + } + else + PRVM_ERROR("VM_min: %s must supply at least 2 floats", PRVM_NAME); +} + +/* +================= +VM_max + +returns the maximum of two supplied floats + +float max(float a, float b, ...[float]) +================= +*/ +void VM_max (void) +{ + // LordHavoc: 3+ argument enhancement suggested by FrikaC + if (prog->argc == 2) + PRVM_G_FLOAT(OFS_RETURN) = max(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1)); + else if (prog->argc >= 3) + { + int i; + float f = PRVM_G_FLOAT(OFS_PARM0); + for (i = 1;i < prog->argc;i++) + if (PRVM_G_FLOAT((OFS_PARM0+i*3)) > f) + f = PRVM_G_FLOAT((OFS_PARM0+i*3)); + PRVM_G_FLOAT(OFS_RETURN) = f; + } + else + PRVM_ERROR("VM_max: %s must supply at least 2 floats", PRVM_NAME); +} + +/* +================= +VM_bound + +returns number bounded by supplied range + +float bound(float min, float value, float max) +================= +*/ +void VM_bound (void) +{ + VM_SAFEPARMCOUNT(3,VM_bound); + PRVM_G_FLOAT(OFS_RETURN) = bound(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1), PRVM_G_FLOAT(OFS_PARM2)); +} + +/* +================= +VM_pow + +returns a raised to power b + +float pow(float a, float b) +================= +*/ +void VM_pow (void) +{ + VM_SAFEPARMCOUNT(2,VM_pow); + PRVM_G_FLOAT(OFS_RETURN) = pow(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1)); +} + +/* +================= +VM_copyentity + +copies data from one entity to another + +copyentity(entity src, entity dst) +================= +*/ +void VM_copyentity (void) +{ + prvm_edict_t *in, *out; + VM_SAFEPARMCOUNT(2,VM_copyentity); + in = PRVM_G_EDICT(OFS_PARM0); + out = PRVM_G_EDICT(OFS_PARM1); + memcpy(out->fields.vp, in->fields.vp, prog->progs->entityfields * 4); +} + +void VM_Files_Init(void) +{ + int i; + for (i = 0;i < PRVM_MAX_OPENFILES;i++) + prog->openfiles[i] = NULL; +} + +void VM_Files_CloseAll(void) +{ + int i; + for (i = 0;i < PRVM_MAX_OPENFILES;i++) + { + if (prog->openfiles[i]) + FS_Close(prog->openfiles[i]); + prog->openfiles[i] = NULL; + } +} + +file_t *VM_GetFileHandle( int index ) +{ + if (index < 0 || index >= PRVM_MAX_OPENFILES) + { + Msg("VM_GetFileHandle: invalid file handle %i used in %s\n", index, PRVM_NAME); + return NULL; + } + if (prog->openfiles[index] == NULL) + { + Msg("VM_GetFileHandle: no such file handle %i (or file has been closed) in %s\n", index, PRVM_NAME); + return NULL; + } + return prog->openfiles[index]; +} + +/* +========= +VM_fopen + +float fopen(string filename, float mode) +========= +*/ +// float(string filename, float mode) fopen = #110; +// opens a file inside quake/gamedir/data/ (mode is FILE_READ, FILE_APPEND, or FILE_WRITE), +// returns fhandle >= 0 if successful, or fhandle < 0 if unable to open file for any reason +void VM_fopen(void) +{ + int filenum, mode; + const char *modestring, *filename; + + VM_SAFEPARMCOUNT(2,VM_fopen); + + for (filenum = 0;filenum < PRVM_MAX_OPENFILES;filenum++) + if (prog->openfiles[filenum] == NULL) + break; + if (filenum >= PRVM_MAX_OPENFILES) + { + PRVM_G_FLOAT(OFS_RETURN) = -2; + VM_Warning("VM_fopen: %s ran out of file handles (%i)\n", PRVM_NAME, PRVM_MAX_OPENFILES); + return; + } + mode = (int)PRVM_G_FLOAT(OFS_PARM1); + switch(mode) + { + case 0: // FILE_READ + modestring = "rb"; + break; + case 1: // FILE_APPEND + modestring = "ab"; + break; + case 2: // FILE_WRITE + modestring = "wb"; + break; + default: + PRVM_G_FLOAT(OFS_RETURN) = -3; + VM_Warning("VM_fopen: %s: no such mode %i (valid: 0 = read, 1 = append, 2 = write)\n", PRVM_NAME, mode); + return; + } + filename = PRVM_G_STRING(OFS_PARM0); + + prog->openfiles[filenum] = FS_Open(va("data/%s", filename), modestring ); + if (prog->openfiles[filenum] == NULL && mode == 0) + prog->openfiles[filenum] = FS_Open(va("%s", filename), modestring ); + + if (prog->openfiles[filenum] == NULL) + { + PRVM_G_FLOAT(OFS_RETURN) = -1; + if (developer->value >= 100) + VM_Warning("VM_fopen: %s: %s mode %s failed\n", PRVM_NAME, filename, modestring); + } + else + { + PRVM_G_FLOAT(OFS_RETURN) = filenum; + if (developer->value >= 100) + Msg("VM_fopen: %s: %s mode %s opened as #%i\n", PRVM_NAME, filename, modestring, filenum); + } +} + +/* +========= +VM_fclose + +fclose(float fhandle) +========= +*/ +//void(float fhandle) fclose = #111; // closes a file +void VM_fclose(void) +{ + int filenum; + + VM_SAFEPARMCOUNT(1,VM_fclose); + + filenum = (int)PRVM_G_FLOAT(OFS_PARM0); + if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES) + { + VM_Warning("VM_fclose: invalid file handle %i used in %s\n", filenum, PRVM_NAME); + return; + } + if (prog->openfiles[filenum] == NULL) + { + VM_Warning("VM_fclose: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME); + return; + } + FS_Close(prog->openfiles[filenum]); + prog->openfiles[filenum] = NULL; + if (developer->value >= 100) + Msg("VM_fclose: %s: #%i closed\n", PRVM_NAME, filenum); +} + +/* +========= +VM_fgets + +string fgets(float fhandle) +========= +*/ +//string(float fhandle) fgets = #112; // reads a line of text from the file and returns as a tempstring +void VM_fgets(void) +{ + int c; + static char string[VM_STRINGTEMP_LENGTH]; + int filenum; + + VM_SAFEPARMCOUNT(1, VM_fgets); + + filenum = (int)PRVM_G_FLOAT(OFS_PARM0); + if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES) + { + VM_Warning("VM_fgets: invalid file handle %i used in %s\n", filenum, PRVM_NAME); + return; + } + if (prog->openfiles[filenum] == NULL) + { + VM_Warning("VM_fgets: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME); + return; + } + + c = FS_Gets (prog->openfiles[filenum], string, VM_STRINGTEMP_LENGTH ); + + if (developer->value >= 100) Msg("fgets: %s: %s\n", PRVM_NAME, string); + + if (c >= 0) PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(string); + else PRVM_G_INT(OFS_RETURN) = 0; +} + +/* +========= +VM_fputs + +fputs(float fhandle, string s) +========= +*/ +//void(float fhandle, string s) fputs = #113; // writes a line of text to the end of the file +void VM_fputs(void) +{ + int stringlength; + char string[VM_STRINGTEMP_LENGTH]; + int filenum; + + VM_SAFEPARMCOUNT(2,VM_fputs); + + filenum = (int)PRVM_G_FLOAT(OFS_PARM0); + if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES) + { + VM_Warning("VM_fputs: invalid file handle %i used in %s\n", filenum, PRVM_NAME); + return; + } + if (prog->openfiles[filenum] == NULL) + { + VM_Warning("VM_fputs: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME); + return; + } + VM_VarString(1, string, sizeof(string)); + if ((stringlength = (int)strlen(string))) + FS_Write(prog->openfiles[filenum], string, stringlength); + if (developer->value >= 100) + Msg("fputs: %s: %s\n", PRVM_NAME, string); +} + +/* +========= +VM_strlen + +float strlen(string s) +========= +*/ +//float(string s) strlen = #114; // returns how many characters are in a string +void VM_strlen(void) +{ + const char *s; + + VM_SAFEPARMCOUNT(1,VM_strlen); + + s = PRVM_G_STRING(OFS_PARM0); + if (s) + PRVM_G_FLOAT(OFS_RETURN) = strlen(s); + else + PRVM_G_FLOAT(OFS_RETURN) = 0; +} + +/* +========= +VM_strcat + +string strcat(string,string,...[string]) +========= +*/ +//string(string s1, string s2) strcat = #115; +// concatenates two strings (for example "abc", "def" would return "abcdef") +// and returns as a tempstring +void VM_strcat(void) +{ + char *s; + + if(prog->argc < 1) + PRVM_ERROR("VM_strcat wrong parameter count (min. 1 expected ) !"); + + s = VM_GetTempString(); + VM_VarString(0, s, VM_STRINGTEMP_LENGTH); + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(s); +} + +/* +========= +VM_substring + +string substring(string s, float start, float length) +========= +*/ +// string(string s, float start, float length) substring = #116; +// returns a section of a string as a tempstring +void VM_substring(void) +{ + int i, start, length; + const char *s; + char *string; + + VM_SAFEPARMCOUNT(3,VM_substring); + + string = VM_GetTempString(); + s = PRVM_G_STRING(OFS_PARM0); + start = (int)PRVM_G_FLOAT(OFS_PARM1); + length = (int)PRVM_G_FLOAT(OFS_PARM2); + if (!s) + s = ""; + for (i = 0;i < start && *s;i++, s++); + for (i = 0;i < VM_STRINGTEMP_LENGTH - 1 && *s && i < length;i++, s++) + string[i] = *s; + string[i] = 0; + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(string); +} + +/* +========= +VM_stov + +vector stov(string s) +========= +*/ +//vector(string s) stov = #117; // returns vector value from a string +void VM_stov(void) +{ + char string[VM_STRINGTEMP_LENGTH]; + vec3_t out; + const char *s; + int i; + + VM_SAFEPARMCOUNT(1,VM_stov); + + VM_VarString(0, string, sizeof(string)); + + s = string; + VectorClear( out ); + + if (*s == '\'') s++; + for (i = 0;i < 3;i++) + { + while (*s == ' ' || *s == '\t') s++; + out[i] = atof(s); + if (out[i] == 0 && *s != '-' && *s != '+' && (*s < '0' || *s > '9')) + break; // not a number + while (*s && *s != ' ' && *s !='\t' && *s != '\'') s++; + if (*s == '\'') break; + } + VectorCopy(out, PRVM_G_VECTOR(OFS_RETURN)); +} + +/* +========= +VM_strzone + +string strzone(string s) +========= +*/ +//string(string s, ...) strzone = #118; // makes a copy of a string into the string zone and returns it, this is often used to keep around a tempstring for longer periods of time (tempstrings are replaced often) +void VM_strzone(void) +{ + char *out; + char string[VM_STRINGTEMP_LENGTH]; + size_t alloclen; + + VM_SAFEPARMCOUNT(1,VM_strzone); + + VM_VarString(0, string, sizeof(string)); + alloclen = strlen(string) + 1; + PRVM_G_INT(OFS_RETURN) = PRVM_AllocString(alloclen, &out); + memcpy(out, string, alloclen); +} + +/* +========= +VM_strunzone + +strunzone(string s) +========= +*/ +//void(string s) strunzone = #119; // removes a copy of a string from the string zone (you can not use that string again or it may crash!!!) +void VM_strunzone(void) +{ + VM_SAFEPARMCOUNT(1,VM_strunzone); + PRVM_FreeString(PRVM_G_INT(OFS_PARM0)); +} + +/* +========= +VM_command (used by client and menu) + +clientcommand(float client, string s) (for client and menu) +========= +*/ +//void(entity e, string s) clientcommand = #440; // executes a command string as if it came from the specified client +//this function originally written by KrimZon, made shorter by LordHavoc +void VM_clcommand (void) +{ + client_t *temp_client; + int i; + + VM_SAFEPARMCOUNT(2, VM_clcommand); + + i = (int)PRVM_G_FLOAT(OFS_PARM0); + if (sv.state != ss_game || i < 0 || i >= maxclients->value || svs.clients[i].state != cs_spawned) + { + VM_Warning("VM_clientcommand: %s: invalid client/server is not active !\n", PRVM_NAME); + return; + } + + temp_client = sv_client; + sv_client = svs.clients + i; + Cmd_ExecuteString (PRVM_G_STRING(OFS_PARM1)); + sv_client = temp_client; +} + + +/* +========= +VM_tokenize + +float tokenize(string s) +========= +*/ +//float(string s) tokenize = #441; // takes apart a string into individal words (access them with argv), returns how many +//this function originally written by KrimZon, made shorter by LordHavoc +//20040203: rewritten by LordHavoc (no longer uses allocations) +int num_tokens = 0; +char *tokens[256], tokenbuf[MAX_INPUTLINE]; +void VM_tokenize (void) +{ + size_t pos; + const char *p; + + VM_SAFEPARMCOUNT(1, VM_tokenize); + + p = PRVM_G_STRING(OFS_PARM0); + + num_tokens = 0; + pos = 0; + + while(COM_Parse(&p)) + { + size_t tokenlen; + if (num_tokens >= (int)(sizeof(tokens)/sizeof(tokens[0]))) + break; + tokenlen = strlen(COM_Token()) + 1; + if (pos + tokenlen > sizeof(tokenbuf)) + break; + tokens[num_tokens++] = tokenbuf + pos; + Mem_Copy(tokenbuf + pos, COM_Token(), tokenlen); + pos += tokenlen; + } + + PRVM_G_FLOAT(OFS_RETURN) = num_tokens; +} + +//string(float n) argv = #442; // returns a word from the tokenized string (returns nothing for an invalid index) +//this function originally written by KrimZon, made shorter by LordHavoc +void VM_argv (void) +{ + int token_num; + + VM_SAFEPARMCOUNT(1,VM_argv); + + token_num = (int)PRVM_G_FLOAT(OFS_PARM0); + + if (token_num >= 0 && token_num < num_tokens) + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(tokens[token_num]); + else + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(NULL); +} + +/* +//void(entity e, entity tagentity, string tagname) setattachment = #443; // attachs e to a tag on tagentity (note: use "" to attach to entity origin/angles instead of a tag) +void PF_setattachment (void) +{ + prvm_edict_t *e = PRVM_G_EDICT(OFS_PARM0); + prvm_edict_t *tagentity = PRVM_G_EDICT(OFS_PARM1); + char *tagname = PRVM_G_STRING(OFS_PARM2); + prvm_eval_t *v; + int i, modelindex; + model_t *model; + + if (tagentity == NULL) + tagentity = prog->edicts; + + v = PRVM_GETEDICTFIELDVALUE(e, eval_tag_entity); + if (v) + fields.server->edict = PRVM_EDICT_TO_PROG(tagentity); + + v = PRVM_GETEDICTFIELDVALUE(e, eval_tag_index); + if (v) + fields.server->_float = 0; + if (tagentity != NULL && tagentity != prog->edicts && tagname && tagname[0]) + { + modelindex = (int)tagentity->fields.server->modelindex; + if (modelindex >= 0 && modelindex < MAX_MODELS) + { + model = sv.models[modelindex]; + if (model->data_overridetagnamesforskin && (unsigned int)tagentity->fields.server->skin < (unsigned int)model->numskins && model->data_overridetagnamesforskin[(unsigned int)tagentity->fields.server->skin].num_overridetagnames) + for (i = 0;i < model->data_overridetagnamesforskin[(unsigned int)tagentity->fields.server->skin].num_overridetagnames;i++) + if (!strcmp(tagname, model->data_overridetagnamesforskin[(unsigned int)tagentity->fields.server->skin].data_overridetagnames[i].name)) + fields.server->_float = i + 1; + // FIXME: use a model function to get tag info (need to handle skeletal) + if (fields.server->_float == 0 && model->num_tags) + for (i = 0;i < model->num_tags;i++) + if (!strcmp(tagname, model->data_tags[i].name)) + fields.server->_float = i + 1; + if (fields.server->_float == 0) + MsgDev("setattachment(edict %i, edict %i, string \"%s\"): tried to find tag named \"%s\" on entity %i (model \"%s\") but could not find it\n", PRVM_NUM_FOR_EDICT(e), PRVM_NUM_FOR_EDICT(tagentity), tagname, tagname, PRVM_NUM_FOR_EDICT(tagentity), model->name); + } + else + MsgDev("setattachment(edict %i, edict %i, string \"%s\"): tried to find tag named \"%s\" on entity %i but it has no model\n", PRVM_NUM_FOR_EDICT(e), PRVM_NUM_FOR_EDICT(tagentity), tagname, tagname, PRVM_NUM_FOR_EDICT(tagentity)); + } +}*/ + +/* +========= +VM_isserver + +float isserver() +========= +*/ +void VM_isserver(void) +{ + VM_SAFEPARMCOUNT(0,VM_serverstate); + + PRVM_G_FLOAT(OFS_RETURN) = (sv.state == ss_game) ? true : false; +} + +/* +========= +VM_clientcount + +float clientcount() +========= +*/ +void VM_clientcount(void) +{ + VM_SAFEPARMCOUNT(0,VM_clientcount); + + PRVM_G_FLOAT(OFS_RETURN) = maxclients->value; +} + +/* +========= +VM_getmousepos + +vector getmousepos() +========= +*/ +void VM_getmousepos(void) +{ + + VM_SAFEPARMCOUNT(0,VM_getmousepos); + + PRVM_G_VECTOR(OFS_RETURN)[0] = mouse_x; + PRVM_G_VECTOR(OFS_RETURN)[1] = mouse_y; + PRVM_G_VECTOR(OFS_RETURN)[2] = 0; +} + +/* +========= +VM_gettime + +float gettime(void) +========= +*/ +void VM_gettime(void) +{ + VM_SAFEPARMCOUNT(0,VM_gettime); + + PRVM_G_FLOAT(OFS_RETURN) = (float) *prog->time; +} + +/* +========= +VM_loadfromdata + +loadfromdata(string data) +========= +*/ +void VM_loadfromdata(void) +{ + VM_SAFEPARMCOUNT(1,VM_loadentsfromfile); + + PRVM_ED_LoadFromFile(PRVM_G_STRING(OFS_PARM0)); +} + +/* +======================== +VM_parseentitydata + +parseentitydata(entity ent, string data) +======================== +*/ +void VM_parseentitydata(void) +{ + prvm_edict_t *ent; + const char *data; + + VM_SAFEPARMCOUNT(2, VM_parseentitydata); + + // get edict and test it + ent = PRVM_G_EDICT(OFS_PARM0); + if (ent->priv.ed->free) + PRVM_ERROR ("VM_parseentitydata: %s: Can only set already spawned entities (entity %i is free)!", PRVM_NAME, PRVM_NUM_FOR_EDICT(ent)); + + data = PRVM_G_STRING(OFS_PARM1); + + // parse the opening brace + if (!COM_Parse(&data) || COM_Token()[0] != '{' ) + PRVM_ERROR ("VM_parseentitydata: %s: Couldn't parse entity data:\n%s", PRVM_NAME, data ); + + PRVM_ED_ParseEdict (data, ent); +} + +/* +========= +VM_loadfromfile + +loadfromfile(string file) +========= +*/ +void VM_loadfromfile(void) +{ + const char *filename; + char *data; + + VM_SAFEPARMCOUNT(1,VM_loadfromfile); + + filename = PRVM_G_STRING(OFS_PARM0); + // .. is parent directory on many platforms + // / is parent directory on Amiga + // : is root of drive on Amiga (also used as a directory separator on Mac, but / works there too, so that's a bad idea) + // \ is a windows-ism (so it's naughty to use it, / works on all platforms) + if ((filename[0] == '.' && filename[1] == '.') || filename[0] == '/' || strrchr(filename, ':') || strrchr(filename, '\\')) + { + PRVM_G_FLOAT(OFS_RETURN) = -4; + VM_Warning("VM_loadfromfile: %s dangerous or non-portable filename \"%s\" not allowed. (contains : or \\ or begins with .. or /)\n", PRVM_NAME, filename); + return; + } + + // not conform with VM_fopen + data = (char *)FS_LoadFile(filename, NULL); + if (data == NULL) PRVM_G_FLOAT(OFS_RETURN) = -1; + PRVM_ED_LoadFromFile(data); +} + + +/* +========= +VM_modulo + +float mod(float val, float m) +========= +*/ +void VM_modulo(void) +{ + int val, m; + VM_SAFEPARMCOUNT(2,VM_module); + + val = (int) PRVM_G_FLOAT(OFS_PARM0); + m = (int) PRVM_G_FLOAT(OFS_PARM1); + + PRVM_G_FLOAT(OFS_RETURN) = (float) (val % m); +} + +void VM_Search_Init(void) +{ + int i; + for (i = 0;i < PRVM_MAX_OPENSEARCHES;i++) + prog->opensearches[i] = NULL; +} + +void VM_Search_Reset(void) +{ + int i; + // reset the fssearch list + for(i = 0; i < PRVM_MAX_OPENSEARCHES; i++) + { + if(prog->opensearches[i]) + Mem_Free(prog->opensearches[i]); + prog->opensearches[i] = NULL; + } +} + +/* +========= +VM_search_begin + +float search_begin(string pattern, float caseinsensitive, float quiet) +========= +*/ +void VM_search_begin(void) +{ + int handle; + const char *pattern; + int caseinsens, quiet; + + VM_SAFEPARMCOUNT(3, VM_search_begin); + + pattern = PRVM_G_STRING(OFS_PARM0); + + VM_CheckEmptyString(pattern); + + caseinsens = (int)PRVM_G_FLOAT(OFS_PARM1); + quiet = (int)PRVM_G_FLOAT(OFS_PARM2); + + for(handle = 0; handle < PRVM_MAX_OPENSEARCHES; handle++) + if(!prog->opensearches[handle]) + break; + + if(handle >= PRVM_MAX_OPENSEARCHES) + { + PRVM_G_FLOAT(OFS_RETURN) = -2; + VM_Warning("VM_search_begin: %s ran out of search handles (%i)\n", PRVM_NAME, PRVM_MAX_OPENSEARCHES); + return; + } + + if(!(prog->opensearches[handle] = FS_Search(pattern))) + PRVM_G_FLOAT(OFS_RETURN) = -1; + else + PRVM_G_FLOAT(OFS_RETURN) = handle; +} + +/* +========= +VM_search_end + +void search_end(float handle) +========= +*/ +void VM_search_end(void) +{ + int handle; + VM_SAFEPARMCOUNT(1, VM_search_end); + + handle = (int)PRVM_G_FLOAT(OFS_PARM0); + + if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES) + { + VM_Warning("VM_search_end: invalid handle %i used in %s\n", handle, PRVM_NAME); + return; + } + if(prog->opensearches[handle] == NULL) + { + VM_Warning("VM_search_end: no such handle %i in %s\n", handle, PRVM_NAME); + return; + } + + Mem_Free(prog->opensearches[handle]); + prog->opensearches[handle] = NULL; +} + +/* +========= +VM_search_getsize + +float search_getsize(float handle) +========= +*/ +void VM_search_getsize(void) +{ + int handle; + VM_SAFEPARMCOUNT(1, VM_M_search_getsize); + + handle = (int)PRVM_G_FLOAT(OFS_PARM0); + + if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES) + { + VM_Warning("VM_search_getsize: invalid handle %i used in %s\n", handle, PRVM_NAME); + return; + } + if(prog->opensearches[handle] == NULL) + { + VM_Warning("VM_search_getsize: no such handle %i in %s\n", handle, PRVM_NAME); + return; + } + + PRVM_G_FLOAT(OFS_RETURN) = prog->opensearches[handle]->numfilenames; +} + +/* +========= +VM_search_getfilename + +string search_getfilename(float handle, float num) +========= +*/ +void VM_search_getfilename(void) +{ + int handle, filenum; + char *tmp; + VM_SAFEPARMCOUNT(2, VM_search_getfilename); + + handle = (int)PRVM_G_FLOAT(OFS_PARM0); + filenum = (int)PRVM_G_FLOAT(OFS_PARM1); + + if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES) + { + VM_Warning("VM_search_getfilename: invalid handle %i used in %s\n", handle, PRVM_NAME); + return; + } + if(prog->opensearches[handle] == NULL) + { + VM_Warning("VM_search_getfilename: no such handle %i in %s\n", handle, PRVM_NAME); + return; + } + if(filenum < 0 || filenum >= prog->opensearches[handle]->numfilenames) + { + VM_Warning("VM_search_getfilename: invalid filenum %i in %s\n", filenum, PRVM_NAME); + return; + } + + tmp = VM_GetTempString(); + strlcpy(tmp, prog->opensearches[handle]->filenames[filenum], VM_STRINGTEMP_LENGTH); + + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(tmp); +} + +/* +========= +VM_chr + +string chr(float ascii) +========= +*/ +void VM_chr(void) +{ + char *tmp; + VM_SAFEPARMCOUNT(1, VM_chr); + + tmp = VM_GetTempString(); + tmp[0] = (unsigned char) PRVM_G_FLOAT(OFS_PARM0); + tmp[1] = 0; + + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(tmp); +} + +//============================================================================= +// Draw builtins (client & menu) + +/* +========= +VM_iscachedpic + +float iscachedpic(string pic) +========= +*/ +void VM_iscachedpic(void) +{ + VM_SAFEPARMCOUNT(1,VM_iscachedpic); + + // drawq hasnt such a function, thus always return true + PRVM_G_FLOAT(OFS_RETURN) = false; +} + +/* +========= +VM_precache_pic + +string precache_pic(string pic) +========= +*/ +void VM_precache_pic(void) +{ + const char *s; + + VM_SAFEPARMCOUNT(1, VM_precache_pic); + + s = PRVM_G_STRING(OFS_PARM0); + PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0); + + if(!s) PRVM_ERROR ("VM_precache_pic: %s: NULL", PRVM_NAME); + + VM_CheckEmptyString (s); + + re->RegisterPic((char *)s); //may return empty frame +} + +/* +========= +VM_freepic + +freepic(string s) +========= +*/ +void VM_freepic(void) +{ + const char *s; + + VM_SAFEPARMCOUNT(1,VM_freepic); + + s = PRVM_G_STRING(OFS_PARM0); + + if(!s) + PRVM_ERROR ("VM_freepic: %s: NULL"); + + VM_CheckEmptyString (s); + + // this does nothing +} + +/* +========= +VM_drawcharacter + +float drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag) +========= +*/ +void VM_drawcharacter(void) +{ + float *pos, *scale, *rgb; + char character; + int flag; + VM_SAFEPARMCOUNT(6,VM_drawcharacter); + + character = (char) PRVM_G_FLOAT(OFS_PARM1); + if(character == 0) + { + PRVM_G_FLOAT(OFS_RETURN) = -1; + VM_Warning("VM_drawcharacter: %s passed null character !\n",PRVM_NAME); + return; + } + + pos = PRVM_G_VECTOR(OFS_PARM0); + scale = PRVM_G_VECTOR(OFS_PARM2); + rgb = PRVM_G_VECTOR(OFS_PARM3); + flag = (int)PRVM_G_FLOAT(OFS_PARM5); + + re->DrawChar( pos[0], pos[1], character ); + PRVM_G_FLOAT(OFS_RETURN) = 1; +} + +/* +========= +VM_drawstring + +float drawstring(vector position, string text, vector scale, vector rgb, float alpha, float flag) +========= +*/ +void VM_drawstring(void) +{ + float *pos,*scale,*rgb; + const char *string; + int flag; + VM_SAFEPARMCOUNT(6,VM_drawstring); + + string = PRVM_G_STRING(OFS_PARM1); + if(!string) + { + PRVM_G_FLOAT(OFS_RETURN) = -1; + VM_Warning("VM_drawstring: %s passed null string !\n",PRVM_NAME); + return; + } + + pos = PRVM_G_VECTOR(OFS_PARM0); + scale = PRVM_G_VECTOR(OFS_PARM2); + rgb = PRVM_G_VECTOR(OFS_PARM3); + flag = (int)PRVM_G_FLOAT(OFS_PARM5); + + re->DrawString( pos[0], pos[1], (char *)string ); + PRVM_G_FLOAT(OFS_RETURN) = 1; +} +/* +========= +VM_drawpic + +float drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag) +========= +*/ +void VM_drawpic(void) +{ + const char *picname; + float *size, *pos, *rgb; + int flag; + + VM_SAFEPARMCOUNT(6,VM_drawpic); + + picname = PRVM_G_STRING(OFS_PARM1); + + if(!picname) + { + PRVM_G_FLOAT(OFS_RETURN) = -1; + VM_Warning("VM_drawpic: %s passed null picture name !\n", PRVM_NAME); + return; + } + + VM_CheckEmptyString (picname); + + pos = PRVM_G_VECTOR(OFS_PARM0); + size = PRVM_G_VECTOR(OFS_PARM2); + rgb = PRVM_G_VECTOR(OFS_PARM3); + flag = (int) PRVM_G_FLOAT(OFS_PARM5); + + re->DrawPic( pos[0], pos[1], (char *)picname ); + PRVM_G_FLOAT(OFS_RETURN) = 1; +} + +/* +========= +VM_drawfill + +float drawfill(vector position, vector size, vector rgb, float alpha, float flag) +========= +*/ +void VM_drawfill(void) +{ + float *size, *pos, *rgb; + int flag, color; + + VM_SAFEPARMCOUNT(5,VM_drawfill); + + + pos = PRVM_G_VECTOR(OFS_PARM0); + size = PRVM_G_VECTOR(OFS_PARM1); + rgb = PRVM_G_VECTOR(OFS_PARM2); + flag = (int) PRVM_G_FLOAT(OFS_PARM4); + + color = VectorLength( rgb ); //stupid hack + + re->DrawFill( pos[0], pos[1], size[0], size[1], color ); + PRVM_G_FLOAT(OFS_RETURN) = 1; +} + +/* +========= +VM_keynumtostring + +string keynumtostring(float keynum) +========= +*/ +void VM_keynumtostring (void) +{ + int keynum; + char *tmp; + VM_SAFEPARMCOUNT(1, VM_keynumtostring); + + keynum = (int)PRVM_G_FLOAT(OFS_PARM0); + + tmp = VM_GetTempString(); + + strlcpy(tmp, Key_KeynumToString(keynum), VM_STRINGTEMP_LENGTH); + + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(tmp); +} + +/* +========= +VM_stringtokeynum + +float stringtokeynum(string key) +========= +*/ +void VM_stringtokeynum (void) +{ + const char *str; + VM_SAFEPARMCOUNT( 1, VM_keynumtostring ); + + str = PRVM_G_STRING( OFS_PARM0 ); + + PRVM_G_INT(OFS_RETURN) = Key_StringToKeynum( (char *)str ); +} + +/* +============== +VM_vectorvectors + +Writes new values for v_forward, v_up, and v_right based on the given forward vector +vectorvectors(vector, vector) +============== +*/ +void VM_vectorvectors (void) +{ + DotProduct(PRVM_G_VECTOR(OFS_PARM0), prog->globals.server->v_forward); + VectorVectors(prog->globals.server->v_forward, prog->globals.server->v_right, prog->globals.server->v_up); +} + +// float(float number, float quantity) bitshift (EXT_BITSHIFT) +void VM_bitshift (void) +{ + int n1, n2; + VM_SAFEPARMCOUNT(2, VM_bitshift); + + n1 = (int)fabs((int)PRVM_G_FLOAT(OFS_PARM0)); + n2 = (int)PRVM_G_FLOAT(OFS_PARM1); + if(!n1) + PRVM_G_FLOAT(OFS_RETURN) = n1; + else + if(n2 < 0) + PRVM_G_FLOAT(OFS_RETURN) = (n1 >> -n2); + else + PRVM_G_FLOAT(OFS_RETURN) = (n1 << n2); +} + +//////////////////////////////////////// +// AltString functions +//////////////////////////////////////// + +/* +======================== +VM_altstr_count + +float altstr_count(string) +======================== +*/ +void VM_altstr_count( void ) +{ + const char *altstr, *pos; + int count; + + VM_SAFEPARMCOUNT( 1, VM_altstr_count ); + + altstr = PRVM_G_STRING( OFS_PARM0 ); + //VM_CheckEmptyString( altstr ); + + for( count = 0, pos = altstr ; *pos ; pos++ ) { + if( *pos == '\\' ) { + if( !*++pos ) { + break; + } + } else if( *pos == '\'' ) { + count++; + } + } + + PRVM_G_FLOAT( OFS_RETURN ) = (float) (count / 2); +} + +/* +======================== +VM_altstr_prepare + +string altstr_prepare(string) +======================== +*/ +void VM_altstr_prepare( void ) +{ + char *outstr, *out; + const char *instr, *in; + int size; + + VM_SAFEPARMCOUNT( 1, VM_altstr_prepare ); + + instr = PRVM_G_STRING( OFS_PARM0 ); + //VM_CheckEmptyString( instr ); + outstr = VM_GetTempString(); + + for( out = outstr, in = instr, size = VM_STRINGTEMP_LENGTH - 1 ; size && *in ; size--, in++, out++ ) + if( *in == '\'' ) { + *out++ = '\\'; + *out = '\''; + size--; + } else + *out = *in; + *out = 0; + + PRVM_G_INT( OFS_RETURN ) = PRVM_SetEngineString( outstr ); +} + +/* +======================== +VM_altstr_get + +string altstr_get(string, float) +======================== +*/ +void VM_altstr_get( void ) +{ + const char *altstr, *pos; + char *outstr, *out; + int count, size; + + VM_SAFEPARMCOUNT( 2, VM_altstr_get ); + + altstr = PRVM_G_STRING( OFS_PARM0 ); + //VM_CheckEmptyString( altstr ); + + count = (int)PRVM_G_FLOAT( OFS_PARM1 ); + count = count * 2 + 1; + + for( pos = altstr ; *pos && count ; pos++ ) + if( *pos == '\\' ) { + if( !*++pos ) + break; + } else if( *pos == '\'' ) + count--; + + if( !*pos ) { + PRVM_G_INT( OFS_RETURN ) = PRVM_SetEngineString( NULL ); + return; + } + + outstr = VM_GetTempString(); + for( out = outstr, size = VM_STRINGTEMP_LENGTH - 1 ; size && *pos ; size--, pos++, out++ ) + if( *pos == '\\' ) { + if( !*++pos ) + break; + *out = *pos; + size--; + } else if( *pos == '\'' ) + break; + else + *out = *pos; + + *out = 0; + PRVM_G_INT( OFS_RETURN ) = PRVM_SetEngineString( outstr ); +} + +/* +======================== +VM_altstr_set + +string altstr_set(string altstr, float num, string set) +======================== +*/ +void VM_altstr_set( void ) +{ + int num; + const char *altstr, *str; + const char *in; + char *outstr, *out; + + VM_SAFEPARMCOUNT( 3, VM_altstr_set ); + + altstr = PRVM_G_STRING( OFS_PARM0 ); + //VM_CheckEmptyString( altstr ); + + num = (int)PRVM_G_FLOAT( OFS_PARM1 ); + + str = PRVM_G_STRING( OFS_PARM2 ); + //VM_CheckEmptyString( str ); + + outstr = out = VM_GetTempString(); + for( num = num * 2 + 1, in = altstr; *in && num; *out++ = *in++ ) + if( *in == '\\' ) { + if( !*++in ) { + break; + } + } else if( *in == '\'' ) { + num--; + } + + if( !in ) { + PRVM_G_INT( OFS_RETURN ) = PRVM_SetEngineString( altstr ); + return; + } + // copy set in + for( ; *str; *out++ = *str++ ); + // now jump over the old content + for( ; *in ; in++ ) + if( *in == '\'' || (*in == '\\' && !*++in) ) + break; + + if( !in ) { + PRVM_G_INT( OFS_RETURN ) = PRVM_SetEngineString( NULL ); + return; + } + + strlcpy(out, in, VM_STRINGTEMP_LENGTH); + PRVM_G_INT( OFS_RETURN ) = PRVM_SetEngineString( outstr ); +} + +/* +======================== +VM_altstr_ins +insert after num +string altstr_ins(string altstr, float num, string set) +======================== +*/ +void VM_altstr_ins(void) +{ + int num; + const char *setstr; + const char *set; + const char *instr; + const char *in; + char *outstr; + char *out; + + in = instr = PRVM_G_STRING( OFS_PARM0 ); + num = (int)PRVM_G_FLOAT( OFS_PARM1 ); + set = setstr = PRVM_G_STRING( OFS_PARM2 ); + + out = outstr = VM_GetTempString(); + for( num = num * 2 + 2 ; *in && num > 0 ; *out++ = *in++ ) + if( *in == '\\' ) { + if( !*++in ) { + break; + } + } else if( *in == '\'' ) { + num--; + } + + *out++ = '\''; + for( ; *set ; *out++ = *set++ ); + *out++ = '\''; + + strlcpy(out, in, VM_STRINGTEMP_LENGTH); + PRVM_G_INT( OFS_RETURN ) = PRVM_SetEngineString( outstr ); +} + + +//////////////////////////////////////// +// BufString functions +//////////////////////////////////////// +//[515]: string buffers support +#define MAX_QCSTR_BUFFERS 128 +#define MAX_QCSTR_STRINGS 1024 + +typedef struct +{ + int num_strings; + char *strings[MAX_QCSTR_STRINGS]; +}qcstrbuffer_t; + +static qcstrbuffer_t *qcstringbuffers[MAX_QCSTR_BUFFERS]; +static int num_qcstringbuffers; +static int buf_sortpower; + +#define BUFSTR_BUFFER(a) (a>=MAX_QCSTR_BUFFERS) ? NULL : (qcstringbuffers[a]) +#define BUFSTR_ISFREE(a) (anum_strings<=0) ? 1 : 0 + +static int BufStr_FindFreeBuffer (void) +{ + int i; + if(num_qcstringbuffers == MAX_QCSTR_BUFFERS) + return -1; + for(i=0;inum_strings > 0) + { + for(i=0;inum_strings;i++) + if(b->strings[i]) + Z_Free(b->strings[i]); + num_qcstringbuffers--; + } + Z_Free(qcstringbuffers[index]); + qcstringbuffers[index] = NULL; + } +} + +static int BufStr_FindFreeString (qcstrbuffer_t *b) +{ + int i; + for(i=0;inum_strings;i++) + if(!b->strings[i] || !b->strings[i][0]) + return i; + if(i == MAX_QCSTR_STRINGS) return -1; + else return i; +} + +static int BufStr_SortStringsUP (const void *in1, const void *in2) +{ + const char *a, *b; + a = *((const char **) in1); + b = *((const char **) in2); + if(!a[0]) return 1; + if(!b[0]) return -1; + return strncmp(a, b, buf_sortpower); +} + +static int BufStr_SortStringsDOWN (const void *in1, const void *in2) +{ + const char *a, *b; + a = *((const char **) in1); + b = *((const char **) in2); + if(!a[0]) return 1; + if(!b[0]) return -1; + return strncmp(b, a, buf_sortpower); +} + +#ifdef REMOVETHIS +static void VM_BufStr_Init (void) +{ + memset(qcstringbuffers, 0, sizeof(qcstringbuffers)); + num_qcstringbuffers = 0; +} + +static void VM_BufStr_ShutDown (void) +{ + int i; + for(i=0;i= 0) + num_qcstringbuffers++; + //else + //Msg("VM_buf_create: buffers overflow in %s\n", PRVM_NAME); + PRVM_G_FLOAT(OFS_RETURN) = i; +} + +/* +======================== +VM_buf_del +deletes buffer and all strings in it +void buf_del(float bufhandle) = #461; +======================== +*/ +void VM_buf_del (void) +{ + VM_SAFEPARMCOUNT(1, VM_buf_del); + if(BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0))) + BufStr_ClearBuffer((int)PRVM_G_FLOAT(OFS_PARM0)); + else + { + VM_Warning("VM_buf_del: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); + return; + } +} + +/* +======================== +VM_buf_getsize +how many strings are stored in buffer +float buf_getsize(float bufhandle) = #462; +======================== +*/ +void VM_buf_getsize (void) +{ + qcstrbuffer_t *b; + VM_SAFEPARMCOUNT(1, VM_buf_getsize); + + b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0)); + if(!b) + { + PRVM_G_FLOAT(OFS_RETURN) = -1; + VM_Warning("VM_buf_getsize: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); + return; + } + else + PRVM_G_FLOAT(OFS_RETURN) = b->num_strings; +} + +/* +======================== +VM_buf_copy +copy all content from one buffer to another, make sure it exists +void buf_copy(float bufhandle_from, float bufhandle_to) = #463; +======================== +*/ +void VM_buf_copy (void) +{ + qcstrbuffer_t *b1, *b2; + int i; + VM_SAFEPARMCOUNT(2, VM_buf_copy); + + b1 = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0)); + if(!b1) + { + VM_Warning("VM_buf_copy: invalid source buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); + return; + } + i = (int)PRVM_G_FLOAT(OFS_PARM1); + if(i == (int)PRVM_G_FLOAT(OFS_PARM0)) + { + VM_Warning("VM_buf_copy: source == destination (%i) in %s\n", i, PRVM_NAME); + return; + } + b2 = BUFSTR_BUFFER(i); + if(!b2) + { + VM_Warning("VM_buf_copy: invalid destination buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), PRVM_NAME); + return; + } + + BufStr_ClearBuffer(i); + qcstringbuffers[i] = (qcstrbuffer_t *)Z_Malloc(sizeof(qcstrbuffer_t)); + memset(qcstringbuffers[i], 0, sizeof(qcstrbuffer_t)); + b2->num_strings = b1->num_strings; + + for(i=0;inum_strings;i++) + if(b1->strings[i] && b1->strings[i][0]) + { + size_t stringlen; + stringlen = strlen(b1->strings[i]) + 1; + b2->strings[i] = (char *)Z_Malloc(stringlen); + if(!b2->strings[i]) + { + VM_Warning("VM_buf_copy: not enough memory for buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), PRVM_NAME); + break; + } + memcpy(b2->strings[i], b1->strings[i], stringlen); + } +} + +/* +======================== +VM_buf_sort +sort buffer by beginnings of strings (sortpower defaults it's lenght) +"backward == TRUE" means that sorting goes upside-down +void buf_sort(float bufhandle, float sortpower, float backward) = #464; +======================== +*/ +void VM_buf_sort (void) +{ + qcstrbuffer_t *b; + int i; + VM_SAFEPARMCOUNT(3, VM_buf_sort); + + b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0)); + if(!b) + { + VM_Warning("VM_buf_sort: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); + return; + } + if(b->num_strings <= 0) + { + VM_Warning("VM_buf_sort: tried to sort empty buffer %i in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); + return; + } + buf_sortpower = (int)PRVM_G_FLOAT(OFS_PARM1); + if(buf_sortpower <= 0) + buf_sortpower = 99999999; + + if(!PRVM_G_FLOAT(OFS_PARM2)) + qsort(b->strings, b->num_strings, sizeof(char*), BufStr_SortStringsUP); + else + qsort(b->strings, b->num_strings, sizeof(char*), BufStr_SortStringsDOWN); + + for(i=b->num_strings-1;i>=0;i--) //[515]: delete empty lines + if(b->strings) + { + if(b->strings[i][0]) + break; + else + { + Z_Free(b->strings[i]); + --b->num_strings; + b->strings[i] = NULL; + } + } + else + --b->num_strings; +} + +/* +======================== +VM_buf_implode +concantenates all buffer string into one with "glue" separator and returns it as tempstring +string buf_implode(float bufhandle, string glue) = #465; +======================== +*/ +void VM_buf_implode (void) +{ + qcstrbuffer_t *b; + char *k; + const char *sep; + int i; + size_t l; + VM_SAFEPARMCOUNT(2, VM_buf_implode); + + b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0)); + PRVM_G_INT(OFS_RETURN) = 0; + if(!b) + { + VM_Warning("VM_buf_implode: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); + return; + } + if(!b->num_strings) + return; + sep = PRVM_G_STRING(OFS_PARM1); + k = VM_GetTempString(); + k[0] = 0; + for(l=i=0;inum_strings;i++) + if(b->strings[i]) + { + l += strlen(b->strings[i]); + if(l>=4095) + break; + strlcat(k, b->strings[i], VM_STRINGTEMP_LENGTH); + if(sep && (i != b->num_strings-1)) + { + l += strlen(sep); + if(l>=4095) + break; + strlcat(k, sep, VM_STRINGTEMP_LENGTH); + } + } + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(k); +} + +/* +======================== +VM_bufstr_get +get a string from buffer, returns direct pointer, dont str_unzone it! +string bufstr_get(float bufhandle, float string_index) = #465; +======================== +*/ +void VM_bufstr_get (void) +{ + qcstrbuffer_t *b; + int strindex; + VM_SAFEPARMCOUNT(2, VM_bufstr_get); + + b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0)); + if(!b) + { + VM_Warning("VM_bufstr_get: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); + return; + } + strindex = (int)PRVM_G_FLOAT(OFS_PARM1); + if(strindex < 0 || strindex > MAX_QCSTR_STRINGS) + { + VM_Warning("VM_bufstr_get: invalid string index %i used in %s\n", strindex, PRVM_NAME); + return; + } + PRVM_G_INT(OFS_RETURN) = 0; + if(b->num_strings <= strindex) + return; + if(b->strings[strindex]) + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(b->strings[strindex]); +} + +/* +======================== +VM_bufstr_set +copies a string into selected slot of buffer +void bufstr_set(float bufhandle, float string_index, string str) = #466; +======================== +*/ +void VM_bufstr_set (void) +{ + int bufindex, strindex; + qcstrbuffer_t *b; + const char *news; + size_t alloclen; + + VM_SAFEPARMCOUNT(3, VM_bufstr_set); + + bufindex = (int)PRVM_G_FLOAT(OFS_PARM0); + b = BUFSTR_BUFFER(bufindex); + if(!b) + { + VM_Warning("VM_bufstr_set: invalid buffer %i used in %s\n", bufindex, PRVM_NAME); + return; + } + strindex = (int)PRVM_G_FLOAT(OFS_PARM1); + if(strindex < 0 || strindex > MAX_QCSTR_STRINGS) + { + VM_Warning("VM_bufstr_set: invalid string index %i used in %s\n", strindex, PRVM_NAME); + return; + } + news = PRVM_G_STRING(OFS_PARM2); + if(!news) + { + VM_Warning("VM_bufstr_set: null string used in %s\n", PRVM_NAME); + return; + } + if(b->strings[strindex]) + Z_Free(b->strings[strindex]); + alloclen = strlen(news) + 1; + b->strings[strindex] = (char *)Z_Malloc(alloclen); + memcpy(b->strings[strindex], news, alloclen); +} + +/* +======================== +VM_bufstr_add +adds string to buffer in nearest free slot and returns it +"order == TRUE" means that string will be added after last "full" slot +float bufstr_add(float bufhandle, string str, float order) = #467; +======================== +*/ +void VM_bufstr_add (void) +{ + int bufindex, order, strindex; + qcstrbuffer_t *b; + const char *string; + size_t alloclen; + + VM_SAFEPARMCOUNT(3, VM_bufstr_add); + + bufindex = (int)PRVM_G_FLOAT(OFS_PARM0); + b = BUFSTR_BUFFER(bufindex); + PRVM_G_FLOAT(OFS_RETURN) = -1; + if(!b) + { + VM_Warning("VM_bufstr_add: invalid buffer %i used in %s\n", bufindex, PRVM_NAME); + return; + } + string = PRVM_G_STRING(OFS_PARM1); + if(!string) + { + VM_Warning("VM_bufstr_add: null string used in %s\n", PRVM_NAME); + return; + } + + order = (int)PRVM_G_FLOAT(OFS_PARM2); + if(order) + strindex = b->num_strings; + else + { + strindex = BufStr_FindFreeString(b); + if(strindex < 0) + { + VM_Warning("VM_bufstr_add: buffer %i has no free string slots in %s\n", bufindex, PRVM_NAME); + return; + } + } + + while(b->num_strings <= strindex) + { + if(b->num_strings == MAX_QCSTR_STRINGS) + { + VM_Warning("VM_bufstr_add: buffer %i has no free string slots in %s\n", bufindex, PRVM_NAME); + return; + } + b->strings[b->num_strings] = NULL; + b->num_strings++; + } + if(b->strings[strindex]) + Z_Free(b->strings[strindex]); + alloclen = strlen(string) + 1; + b->strings[strindex] = (char *)Z_Malloc(alloclen); + memcpy(b->strings[strindex], string, alloclen); + PRVM_G_FLOAT(OFS_RETURN) = strindex; +} + +/* +======================== +VM_bufstr_free +delete string from buffer +void bufstr_free(float bufhandle, float string_index) = #468; +======================== +*/ +void VM_bufstr_free (void) +{ + int i; + qcstrbuffer_t *b; + VM_SAFEPARMCOUNT(2, VM_bufstr_free); + + b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0)); + if(!b) + { + VM_Warning("VM_bufstr_free: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); + return; + } + i = (int)PRVM_G_FLOAT(OFS_PARM1); + if(i < 0 || i > MAX_QCSTR_STRINGS) + { + VM_Warning("VM_bufstr_free: invalid string index %i used in %s\n", i, PRVM_NAME); + return; + } + if(b->strings[i]) + Z_Free(b->strings[i]); + b->strings[i] = NULL; + if(i+1 == b->num_strings) + --b->num_strings; +} + +//============= + +void VM_Cmd_Init(void) +{ + // only init the stuff for the current prog + VM_Files_Init(); + VM_Search_Init(); +} + +void VM_Cmd_Reset(void) +{ + VM_Search_Reset(); + VM_Files_CloseAll(); +} + diff --git a/engine/prvm/vm_cmds.h b/engine/prvm/vm_cmds.h new file mode 100644 index 00000000..1d74e23a --- /dev/null +++ b/engine/prvm/vm_cmds.h @@ -0,0 +1,343 @@ +// AK +// Basically every vm builtin cmd should be in here. +// All 3 builtin and extension lists can be found here +// cause large (I think they will) parts are from pr_cmds the same copyright like in pr_cmds +// also applies here + + +/* +============================================================================ +common cmd list: +================= + + checkextension(string) + error(...[string]) + objerror(...[string) + print(...[strings]) + bprint(...[string]) + sprint(float clientnum,...[string]) + centerprint(...[string]) +vector normalize(vector) +float vlen(vector) +float vectoyaw(vector) +vector vectoangles(vector) +float random() + cmd(string) + float cvar (string) + cvar_set (string,string) + dprint(...[string]) +string ftos(float) +float fabs(float) +string vtos(vector) +string etos(entity) +float stof(...[string]) +entity spawn() + remove(entity e) +entity find(entity start, .string field, string match) + +entity findfloat(entity start, .float field, float match) +entity findentity(entity start, .entity field, entity match) + +entity findchain(.string field, string match) + +entity findchainfloat(.string field, float match) +entity findchainentity(.string field, entity match) + +string precache_file(string) +string precache_sound (string sample) + coredump() + traceon() + traceoff() + eprint(entity e) +float rint(float) +float floor(float) +float ceil(float) +entity nextent(entity) +float sin(float) +float cos(float) +float sqrt(float) +vector randomvec() +float registercvar (string name, string value, float flags) +float min(float a, float b, ...[float]) +float max(float a, float b, ...[float]) +float bound(float min, float value, float max) +float pow(float a, float b) + copyentity(entity src, entity dst) +float fopen(string filename, float mode) + fclose(float fhandle) +string fgets(float fhandle) + fputs(float fhandle, string s) +float strlen(string s) +string strcat(string,string,...[string]) +string substring(string s, float start, float length) +vector stov(string s) +string strzone(string s) + strunzone(string s) +float tokenize(string s) +string argv(float n) +float isserver() +float clientcount() +float clientstate() + clientcommand(float client, string s) (for client and menu) + changelevel(string map) + localsound(string sample) +vector getmousepos() +float gettime() + loadfromdata(string data) + loadfromfile(string file) + parseentitydata(entity ent, string data) +float mod(float val, float m) +const string cvar_string (string) + crash() + stackdump() + +float search_begin(string pattern, float caseinsensitive, float quiet) +void search_end(float handle) +float search_getsize(float handle) +string search_getfilename(float handle, float num) + +string chr(float ascii) + +float itof(intt ent) +entity ftoe(float num) + +-------will be removed soon---------- +float altstr_count(string) +string altstr_prepare(string) +string altstr_get(string,float) +string altstr_set(string altstr, float num, string set) +string altstr_ins(string altstr, float num, string set) +-------------------------------------- + +entity findflags(entity start, .float field, float match) +entity findchainflags(.float field, float match) + +perhaps only : Menu : WriteMsg +=============================== + + WriteByte(float data, float dest, float desto) + WriteChar(float data, float dest, float desto) + WriteShort(float data, float dest, float desto) + WriteLong(float data, float dest, float desto) + WriteAngle(float data, float dest, float desto) + WriteCoord(float data, float dest, float desto) + WriteString(string data, float dest, float desto) + WriteEntity(entity data, float dest, float desto) + +Client & Menu : draw functions & video functions +=================================================== + +float iscachedpic(string pic) +string precache_pic(string pic) + freepic(string s) +float drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag) +float drawstring(vector position, string text, vector scale, vector rgb, float alpha, float flag) +float drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag) +float drawfill(vector position, vector size, vector rgb, float alpha, float flag) + +============================================================================== +menu cmd list: +=============== + + setkeydest(float dest) +float getkeydest() + setmousetarget(float target) +float getmousetarget() + + callfunction(...,string function_name) + writetofile(float fhandle, entity ent) +float isfunction(string function_name) +vector getresolution(float number) +string keynumtostring(float keynum) +string findkeysforcommand(string command) +float getserverliststat(float type) +string getserverliststring(float fld, float hostnr) + +float stringtokeynum(string key) + + resetserverlistmasks() + setserverlistmaskstring(float mask, float fld, string str) + setserverlistmasknumber(float mask, float fld, float num, float op) + resortserverlist() + setserverlistsort(float field, float descending) + refreshserverlist() +float getserverlistnumber(float fld, float hostnr) +float getserverlistindexforkey(string key) + addwantedserverlistkey(string key) +*/ + +#include "engine.h" +#include "server.h" +#include "progdefs.h" +#include "progsvm.h" + +//============================================================================ +// nice helper macros + +#ifndef VM_NOPARMCHECK +#define VM_SAFEPARMCOUNT(p,f) if(prog->argc != p) PRVM_ERROR(#f " wrong parameter count (" #p " expected ) !") +#else +#define VM_SAFEPARMCOUNT(p,f) +#endif + +#define VM_RETURN_EDICT(e) (((int *)prog->globals.generic)[OFS_RETURN] = PRVM_EDICT_TO_PROG(e)) + +#define e10 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL +#define e100 e10,e10,e10,e10,e10,e10,e10,e10,e10,e10 +#define e1000 e100,e100,e100,e100,e100,e100,e100,e100,e100,e100 + +#define VM_STRINGTEMP_BUFFERS 16 +#define VM_STRINGTEMP_LENGTH MAX_INPUTLINE + +// builtins and other general functions + +char *VM_GetTempString(void); +void VM_CheckEmptyString (const char *s); +void VM_VarString(int first, char *out, int outlength); + +void VM_checkextension (void); +void VM_error (void); +void VM_objerror (void); +void VM_print (void); +void VM_bprint (void); +void VM_sprint (void); +void VM_centerprint (void); +void VM_normalize (void); +void VM_vlen (void); +void VM_vectoyaw (void); +void VM_vectoangles (void); +void VM_random (void); +void VM_localsound(void); +void VM_break (void); +void VM_localcmd (void); +void VM_cvar (void); +void VM_cvar_string(void); +void VM_cvar_set (void); +void VM_dprint (void); +void VM_ftos (void); +void VM_fabs (void); +void VM_vtos (void); +void VM_etos (void); +void VM_stof(void); +void VM_itof(void); +void VM_ftoe(void); +void VM_spawn (void); +void VM_remove (void); +void VM_find (void); +void VM_findfloat (void); +void VM_findchain (void); +void VM_findchainfloat (void); +void VM_findflags (void); +void VM_findchainflags (void); +void VM_precache_file (void); +void VM_precache_error (void); +void VM_precache_sound (void); +void VM_coredump (void); + +void VM_stackdump (void); +void VM_crash(void); // REMOVE IT +void VM_traceon (void); +void VM_traceoff (void); +void VM_eprint (void); +void VM_rint (void); +void VM_floor (void); +void VM_ceil (void); +void VM_nextent (void); + +void VM_changelevel (void); +void VM_sin (void); +void VM_cos (void); +void VM_sqrt (void); +void VM_randomvec (void); +void VM_registercvar (void); +void VM_min (void); +void VM_max (void); +void VM_bound (void); +void VM_pow (void); +void VM_copyentity (void); + +void VM_Files_Init(void); +void VM_Files_CloseAll(void); + +void VM_fopen(void); +void VM_fclose(void); +void VM_fgets(void); +void VM_fputs(void); +// used by M_WriteToFile +// should be only called from a builtin +file_t *VM_GetFileHandle( int index ); + +void VM_strlen(void); +void VM_strcat(void); +void VM_substring(void); +void VM_stov(void); +void VM_strzone(void); +void VM_strunzone(void); + +void VM_clcommand (void); + +void VM_tokenize (void); +void VM_argv (void); + +void VM_isserver(void); +void VM_clientcount(void); +void VM_getmousepos(void); +void VM_gettime(void); +void VM_loadfromdata(void); +void VM_parseentitydata(void); +void VM_loadfromfile(void); +void VM_modulo(void); + +void VM_search_begin(void); +void VM_search_end(void); +void VM_search_getsize(void); +void VM_search_getfilename(void); +void VM_chr(void); +void VM_iscachedpic(void); +void VM_precache_pic(void); +void VM_freepic(void); +void VM_drawcharacter(void); +void VM_drawstring(void); +void VM_drawpic(void); +void VM_drawfill(void); +void VM_drawsetcliparea(void); +void VM_drawresetcliparea(void); +void VM_getimagesize(void); + +void VM_vectorvectors (void); + +void VM_keynumtostring (void); +void VM_stringtokeynum (void); + +void VM_cin_open( void ); +void VM_cin_close( void ); +void VM_cin_setstate( void ); +void VM_cin_getstate( void ); +void VM_cin_restart( void ); + +void VM_drawline (void); +void VM_R_PolygonBegin (void); +void VM_R_PolygonVertex (void); +void VM_R_PolygonEnd (void); + +void VM_bitshift (void); + +void VM_altstr_count( void ); +void VM_altstr_prepare( void ); +void VM_altstr_get( void ); +void VM_altstr_set( void ); +void VM_altstr_ins(void); + +void VM_buf_create(void); +void VM_buf_del (void); +void VM_buf_getsize (void); +void VM_buf_copy (void); +void VM_buf_sort (void); +void VM_buf_implode (void); +void VM_bufstr_get (void); +void VM_bufstr_set (void); +void VM_bufstr_add (void); +void VM_bufstr_free (void); + +void VM_Cmd_Init(void); +void VM_Cmd_Reset(void); diff --git a/engine/prvm/vm_edict.c b/engine/prvm/vm_edict.c new file mode 100644 index 00000000..1736e2b1 --- /dev/null +++ b/engine/prvm/vm_edict.c @@ -0,0 +1,1984 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +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 the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// AK new vm + +#include "engine.h" +#include "progsvm.h" + +prvm_prog_t *prog; + +static prvm_prog_t prog_list[PRVM_MAXPROGS]; + +int prvm_type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4}; + +ddef_t *PRVM_ED_FieldAtOfs(int ofs); +bool PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s); + +// LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others)) +cvar_t *prvm_boundscheck; +// LordHavoc: prints every opcode as it executes - warning: this is significant spew +cvar_t *prvm_traceqc; +// LordHavoc: counts usage of each QuakeC statement +cvar_t *prvm_statementprofiling; +//============================================================================ +// mempool handling + +/* +=============== +PRVM_MEM_Alloc +=============== +*/ +void PRVM_MEM_Alloc(void) +{ + int i; + + // reserve space for the null entity aka world + // check bound of max_edicts + prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts); + prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts); + + // edictprivate_size has to be min as big prvm_edict_private_t + prog->edictprivate_size = max(prog->edictprivate_size, (int)sizeof(edict_state_t)); + + // alloc edicts + prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool, prog->limit_edicts * sizeof(prvm_edict_t)); + + // alloc edict private space + prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size); + + // alloc edict fields + prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size); + + // set edict pointers + for(i = 0; i < prog->max_edicts; i++) + { + prog->edicts[i].priv.ed = (edict_state_t *)((byte *)prog->edictprivate + i * prog->edictprivate_size); + prog->edicts[i].fields.vp = (void*)((byte *)prog->edictsfields + i * prog->edict_size); + } +} + +/* +=============== +PRVM_MEM_IncreaseEdicts +=============== +*/ +void PRVM_MEM_IncreaseEdicts(void) +{ + int i; + int oldmaxedicts = prog->max_edicts; + void *oldedictsfields = prog->edictsfields; + void *oldedictprivate = prog->edictprivate; + + if(prog->max_edicts >= prog->limit_edicts) + return; + + PRVM_GCALL(begin_increase_edicts)(); + + // increase edicts + prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts); + + prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size); + prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size); + + memcpy(prog->edictsfields, oldedictsfields, oldmaxedicts * prog->edict_size); + memcpy(prog->edictprivate, oldedictprivate, oldmaxedicts * prog->edictprivate_size); + + //set e and v pointers + for(i = 0; i < prog->max_edicts; i++) + { + prog->edicts[i].priv.ed = (edict_state_t *)((byte *)prog->edictprivate + i * prog->edictprivate_size); + prog->edicts[i].fields.vp = (void*)((byte *)prog->edictsfields + i * prog->edict_size); + } + + PRVM_GCALL(end_increase_edicts)(); + + Mem_Free(oldedictsfields); + Mem_Free(oldedictprivate); +} + +//============================================================================ +// normal prvm + +int PRVM_ED_FindFieldOffset(const char *field) +{ + ddef_t *d; + d = PRVM_ED_FindField(field); + if (!d) + return 0; + return d->ofs*4; +} + +ddef_t* PRVM_ED_FindGlobal(const char *name); +int PRVM_ED_FindGlobalOffset(const char *global) +{ + ddef_t *d; + d = PRVM_ED_FindGlobal(global); + if (!d) + return 0; + return d->ofs*4; +} + +bool PRVM_ProgLoaded(int prognr) +{ + if(prognr < 0 || prognr >= PRVM_MAXPROGS) + return false; + + return (prog_list[prognr].loaded ? true : false); +} + +/* +================= +PRVM_SetProgFromString +================= +*/ +// perhaps add a return value when the str doesnt exist +bool PRVM_SetProgFromString(const char *str) +{ + int i = 0; + for(; i < PRVM_MAXPROGS ; i++) + if(prog_list[i].name && !strcmp(prog_list[i].name,str)) + { + if(prog_list[i].loaded) + { + prog = &prog_list[i]; + return true; + } + else + { + Msg("%s not loaded !\n", PRVM_NAME); + return false; + } + } + + Msg("Invalid program name %s !\n", str); + return false; +} + +/* +================= +PRVM_SetProg +================= +*/ +void PRVM_SetProg(int prognr) +{ + if(0 <= prognr && prognr < PRVM_MAXPROGS) + { + if(prog_list[prognr].loaded) + prog = &prog_list[prognr]; + else + PRVM_ERROR("%i not loaded !", prognr); + return; + } + PRVM_ERROR("Invalid program number %i", prognr); +} + +/* +================= +PRVM_ED_ClearEdict + +Sets everything to NULL +================= +*/ +void PRVM_ED_ClearEdict (prvm_edict_t *e) +{ + memset (e->fields.vp, 0, prog->progs->entityfields * 4); + e->priv.ed->free = false; + + // AK: Let the init_edict function determine if something needs to be initialized + PRVM_GCALL(init_edict)(e); +} + +/* +================= +PRVM_ED_Alloc + +Either finds a free edict, or allocates a new one. +Try to avoid reusing an entity that was recently freed, because it +can cause the client to think the entity morphed into something else +instead of being removed and recreated, which can cause interpolated +angles and bad trails. +================= +*/ +prvm_edict_t *PRVM_ED_Alloc (void) +{ + int i; + prvm_edict_t *e; + + // the client qc dont need maxclients + // thus it doesnt need to use svs.maxclients + // AK: changed i=svs.maxclients+1 + // AK: changed so the edict 0 wont spawn -> used as reserved/world entity + // although the menu/client has no world + for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++) + { + e = PRVM_EDICT_NUM(i); + // the first couple seconds of server time can involve a lot of + // freeing and allocating, so relax the replacement policy + if (e->priv.ed->free && ( e->priv.ed->freetime < 2 || (*prog->time - e->priv.ed->freetime) > 0.5 ) ) + { + PRVM_ED_ClearEdict (e); + return e; + } + } + + if (i == prog->limit_edicts) + PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME); + + prog->num_edicts++; + if (prog->num_edicts >= prog->max_edicts) + PRVM_MEM_IncreaseEdicts(); + + e = PRVM_EDICT_NUM(i); + PRVM_ED_ClearEdict (e); + + return e; +} + +/* +================= +PRVM_ED_Free + +Marks the edict as free +FIXME: walk all entities and NULL out references to this entity +================= +*/ +void PRVM_ED_Free (prvm_edict_t *ed) +{ + // dont delete the null entity (world) or reserved edicts + if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts ) + return; + + PRVM_GCALL(free_edict)(ed); + + ed->priv.ed->free = true; + ed->priv.ed->freetime = *prog->time; +} + +//=========================================================================== + +/* +============ +PRVM_ED_GlobalAtOfs +============ +*/ +ddef_t *PRVM_ED_GlobalAtOfs (int ofs) +{ + ddef_t *def; + int i; + + for (i=0 ; iprogs->numglobaldefs ; i++) + { + def = &prog->globaldefs[i]; + if (def->ofs == ofs) + return def; + } + return NULL; +} + +/* +============ +PRVM_ED_FieldAtOfs +============ +*/ +ddef_t *PRVM_ED_FieldAtOfs (int ofs) +{ + ddef_t *def; + int i; + + for (i=0 ; iprogs->numfielddefs ; i++) + { + def = &prog->fielddefs[i]; + if (def->ofs == ofs) + return def; + } + return NULL; +} + +/* +============ +PRVM_ED_FindField +============ +*/ +ddef_t *PRVM_ED_FindField (const char *name) +{ + ddef_t *def; + int i; + + for (i=0 ; iprogs->numfielddefs ; i++) + { + def = &prog->fielddefs[i]; + if (!strcmp(PRVM_GetString(def->s_name), name)) + return def; + } + return NULL; +} + +/* +============ +PRVM_ED_FindGlobal +============ +*/ +ddef_t *PRVM_ED_FindGlobal (const char *name) +{ + ddef_t *def; + int i; + + for (i=0 ; iprogs->numglobaldefs ; i++) + { + def = &prog->globaldefs[i]; + if (!strcmp(PRVM_GetString(def->s_name), name)) + return def; + } + return NULL; +} + + +/* +============ +PRVM_ED_FindFunction +============ +*/ +mfunction_t *PRVM_ED_FindFunction (const char *name) +{ + mfunction_t *func; + int i; + + for (i=0 ; iprogs->numfunctions ; i++) + { + func = &prog->functions[i]; + if (!strcmp(PRVM_GetString(func->s_name), name)) + return func; + } + return NULL; +} + + +/* +============ +PRVM_ValueString + +Returns a string describing *data in a type specific manner +============= +*/ +char *PRVM_ValueString (etype_t type, prvm_eval_t *val) +{ + static char line[MAX_INPUTLINE]; + ddef_t *def; + mfunction_t *f; + int n; + + type = (etype_t)((int) type & ~DEF_SAVEGLOBAL); + + switch (type) + { + case ev_string: + strlcpy (line, PRVM_GetString (val->string), sizeof (line)); + break; + case ev_entity: + n = val->edict; + if (n < 0 || n >= prog->limit_edicts) + sprintf (line, "entity %i (invalid!)", n); + else + sprintf (line, "entity %i", n); + break; + case ev_function: + f = prog->functions + val->function; + sprintf (line, "%s()", PRVM_GetString(f->s_name)); + break; + case ev_field: + def = PRVM_ED_FieldAtOfs ( val->_int ); + sprintf (line, ".%s", PRVM_GetString(def->s_name)); + break; + case ev_void: + sprintf (line, "void"); + break; + case ev_float: + // LordHavoc: changed from %5.1f to %10.4f + sprintf (line, "%10.4f", val->_float); + break; + case ev_vector: + // LordHavoc: changed from %5.1f to %10.4f + sprintf (line, "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]); + break; + case ev_pointer: + sprintf (line, "pointer"); + break; + default: + sprintf (line, "bad type %i", (int) type); + break; + } + + return line; +} + +/* +============ +PRVM_UglyValueString + +Returns a string describing *data in a type specific manner +Easier to parse than PR_ValueString +============= +*/ +char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val) +{ + static char line[MAX_INPUTLINE]; + int i; + const char *s; + ddef_t *def; + mfunction_t *f; + + type = (etype_t)((int)type & ~DEF_SAVEGLOBAL); + + switch (type) + { + case ev_string: + // Parse the string a bit to turn special characters + // (like newline, specifically) into escape codes, + // this fixes saving games from various mods + s = PRVM_GetString (val->string); + for (i = 0;i < (int)sizeof(line) - 2 && *s;) + { + if (*s == '\n') + { + line[i++] = '\\'; + line[i++] = 'n'; + } + else if (*s == '\r') + { + line[i++] = '\\'; + line[i++] = 'r'; + } + else + line[i++] = *s; + s++; + } + line[i] = '\0'; + break; + case ev_entity: + sprintf (line, "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict))); + break; + case ev_function: + f = prog->functions + val->function; + strlcpy (line, PRVM_GetString (f->s_name), sizeof (line)); + break; + case ev_field: + def = PRVM_ED_FieldAtOfs ( val->_int ); + sprintf (line, ".%s", PRVM_GetString(def->s_name)); + break; + case ev_void: + sprintf (line, "void"); + break; + case ev_float: + sprintf (line, "%f", val->_float); + break; + case ev_vector: + sprintf (line, "%f %f %f", val->vector[0], val->vector[1], val->vector[2]); + break; + case ev_pointer: + case ev_integer: + case ev_variant: + case ev_struct: + case ev_union: + sprintf (line, "skip new type %i", type); + break; + default: + sprintf (line, "bad type %i", type); + break; + } + + return line; +} + +/* +============ +PRVM_GlobalString + +Returns a string with a description and the contents of a global, +padded to 20 field width +============ +*/ +char *PRVM_GlobalString (int ofs) +{ + char *s; + //size_t i; + ddef_t *def; + void *val; + static char line[128]; + + val = (void *)&prog->globals.generic[ofs]; + def = PRVM_ED_GlobalAtOfs(ofs); + if (!def) + sprintf (line,"GLOBAL%i", ofs); + else + { + s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val); + sprintf (line,"%s (=%s)", PRVM_GetString(def->s_name), s); + } + + //i = strlen(line); + //for ( ; i<20 ; i++) + // strcat (line," "); + //strcat (line," "); + + return line; +} + +char *PRVM_GlobalStringNoContents (int ofs) +{ + //size_t i; + ddef_t *def; + static char line[128]; + + def = PRVM_ED_GlobalAtOfs(ofs); + if (!def) + sprintf (line,"GLOBAL%i", ofs); + else + sprintf (line,"%s", PRVM_GetString(def->s_name)); + + //i = strlen(line); + //for ( ; i<20 ; i++) + // strcat (line," "); + //strcat (line," "); + + return line; +} + + +/* +============= +PRVM_ED_Print + +For debugging +============= +*/ +// LordHavoc: optimized this to print out much more quickly (tempstring) +// LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print) +void PRVM_ED_Print(prvm_edict_t *ed) +{ + size_t l; + ddef_t *d; + int *v; + int i, j; + const char *name; + int type; + char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers + + if (ed->priv.ed->free) + { + Msg("%s: FREE\n",PRVM_NAME); + return; + } + + tempstring[0] = 0; + sprintf(tempstring, "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed)); + for (i=1 ; iprogs->numfielddefs ; i++) + { + d = &prog->fielddefs[i]; + name = PRVM_GetString(d->s_name); + if (name[strlen(name)-2] == '_') + continue; // skip _x, _y, _z vars + + v = (int *)((char *)ed->fields.vp + d->ofs*4); + + // if the value is still all 0, skip the field + type = d->type & ~DEF_SAVEGLOBAL; + + for (j=0 ; j sizeof(tempstring2)-4) + { + memcpy (tempstring2, name, sizeof(tempstring2)-4); + tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.'; + tempstring2[sizeof(tempstring2)-1] = 0; + name = tempstring2; + } + strlcat(tempstring, name, sizeof(tempstring)); + for (l = strlen(name);l < 14;l++) + strlcat(tempstring, " ", sizeof(tempstring)); + strlcat(tempstring, " ", sizeof(tempstring)); + + name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v); + if (strlen(name) > sizeof(tempstring2)-4) + { + memcpy (tempstring2, name, sizeof(tempstring2)-4); + tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.'; + tempstring2[sizeof(tempstring2)-1] = 0; + name = tempstring2; + } + strlcat(tempstring, name, sizeof(tempstring)); + strlcat(tempstring, "\n", sizeof(tempstring)); + if (strlen(tempstring) >= sizeof(tempstring)/2) + { + Con_Print(tempstring); + tempstring[0] = 0; + } + } + if (tempstring[0]) + Con_Print(tempstring); +} + +/* +============= +PRVM_ED_Write + +For savegames +============= +*/ +void PRVM_ED_Write (file_t *f, prvm_edict_t *ed) +{ + ddef_t *d; + int *v; + int i, j; + const char *name; + int type; + + FS_Print(f, "{\n"); + + if (ed->priv.ed->free) + { + FS_Print(f, "}\n"); + return; + } + + for (i=1 ; iprogs->numfielddefs ; i++) + { + d = &prog->fielddefs[i]; + name = PRVM_GetString(d->s_name); + if (name[strlen(name)-2] == '_') + continue; // skip _x, _y, _z vars + + v = (int *)((char *)ed->fields.vp + d->ofs*4); + + // if the value is still all 0, skip the field + type = d->type & ~DEF_SAVEGLOBAL; + for (j=0 ; jtype, (prvm_eval_t *)v)); + } + + FS_Print(f, "}\n"); +} + +void PRVM_ED_PrintNum (int ent) +{ + PRVM_ED_Print(PRVM_EDICT_NUM(ent)); +} + +/* +============= +PRVM_ED_PrintEdicts_f + +For debugging, prints all the entities in the current server +============= +*/ +void PRVM_ED_PrintEdicts_f (void) +{ + int i; + + if(Cmd_Argc() != 2) + { + Con_Print("prvm_edicts \n"); + return; + } + + PRVM_Begin; + if(!PRVM_SetProgFromString(Cmd_Argv(1))) + return; + + Msg("%s: %i entities\n", PRVM_NAME, prog->num_edicts); + for (i=0 ; inum_edicts ; i++) + PRVM_ED_PrintNum (i); + + PRVM_End; +} + +/* +============= +PRVM_ED_PrintEdict_f + +For debugging, prints a single edict +============= +*/ +void PRVM_ED_PrintEdict_f (void) +{ + int i; + + if(Cmd_Argc() != 3) + { + Con_Print("prvm_edict \n"); + return; + } + + PRVM_Begin; + if(!PRVM_SetProgFromString(Cmd_Argv(1))) + return; + + i = atoi (Cmd_Argv(2)); + if (i >= prog->num_edicts) + { + Con_Print("Bad edict number\n"); + PRVM_End; + return; + } + PRVM_ED_PrintNum (i); + + PRVM_End; +} + +/* +============= +PRVM_ED_Count + +For debugging +============= +*/ +// 2 possibilities : 1. just displaying the active edict count +// 2. making a function pointer [x] +void PRVM_ED_Count_f (void) +{ + int i; + prvm_edict_t *ent; + int active; + + if(Cmd_Argc() != 2) + { + Con_Print("prvm_count \n"); + return; + } + + PRVM_Begin; + if(!PRVM_SetProgFromString(Cmd_Argv(1))) + return; + + if(prog->count_edicts) + prog->count_edicts(); + else + { + active = 0; + for (i=0 ; inum_edicts ; i++) + { + ent = PRVM_EDICT_NUM(i); + if (ent->priv.ed->free) + continue; + active++; + } + + Msg("num_edicts:%3i\n", prog->num_edicts); + Msg("active :%3i\n", active); + } + + PRVM_End; +} + +/* +============================================================================== + + ARCHIVING GLOBALS + +FIXME: need to tag constants, doesn't really work +============================================================================== +*/ + +/* +============= +PRVM_ED_WriteGlobals +============= +*/ +void PRVM_ED_WriteGlobals (file_t *f) +{ + ddef_t *def; + int i; + const char *name; + int type; + + FS_Print(f,"{\n"); + for (i=0 ; iprogs->numglobaldefs ; i++) + { + def = &prog->globaldefs[i]; + type = def->type; + if ( !(def->type & DEF_SAVEGLOBAL) ) + continue; + type &= ~DEF_SAVEGLOBAL; + + if (type != ev_string && type != ev_float && type != ev_entity) + continue; + + name = PRVM_GetString(def->s_name); + FS_Printf(f,"\"%s\" ", name); + FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs])); + } + FS_Print(f,"}\n"); +} + +/* +============= +PRVM_ED_ParseGlobals +============= +*/ +void PRVM_ED_ParseGlobals (const char *data) +{ + char keyname[MAX_INPUTLINE]; + ddef_t *key; + + while (1) + { + // parse key + if (!COM_Parse(&data)) + PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace"); + if (COM_Token()[0] == '}') + break; + + strncpy (keyname, COM_Token(), sizeof(keyname)); + + // parse value + if (!COM_Parse(&data)) + PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace"); + + if (COM_Token()[0] == '}') + PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data"); + + key = PRVM_ED_FindGlobal (keyname); + if (!key) + { + MsgDev("'%s' is not a global on %s\n", keyname, PRVM_NAME); + continue; + } + + if (!PRVM_ED_ParseEpair(NULL, key, COM_Token())) + PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error"); + } +} + +//============================================================================ + + +/* +============= +PRVM_ED_ParseEval + +Can parse either fields or globals +returns false if error +============= +*/ +bool PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s) +{ + int i, l; + char *new_p; + ddef_t *def; + prvm_eval_t *val; + mfunction_t *func; + + if (ent) + val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs); + else + val = (prvm_eval_t *)((int *)prog->globals.generic + key->ofs); + switch (key->type & ~DEF_SAVEGLOBAL) + { + case ev_string: + l = (int)strlen(s) + 1; + val->string = PRVM_AllocString(l, &new_p); + for (i = 0;i < l;i++) + { + if (s[i] == '\\' && i < l-1) + { + i++; + if (s[i] == 'n') + *new_p++ = '\n'; + else if (s[i] == 'r') + *new_p++ = '\r'; + else + *new_p++ = s[i]; + } + else + *new_p++ = s[i]; + } + break; + + case ev_float: + while (*s && *s <= ' ') + s++; + val->_float = atof(s); + break; + + case ev_vector: + for (i = 0;i < 3;i++) + { + while (*s && *s <= ' ') + s++; + if (!*s) + break; + val->vector[i] = atof(s); + while (*s > ' ') + s++; + if (!*s) + break; + } + break; + + case ev_entity: + while (*s && *s <= ' ') + s++; + i = atoi(s); + if (i >= prog->limit_edicts) + Msg("PRVM_ED_ParseEpair: ev_entity reference too large (edict %u >= MAX_EDICTS %u) on %s\n", (unsigned int)i, (unsigned int)MAX_EDICTS, PRVM_NAME); + while (i >= prog->max_edicts) + PRVM_MEM_IncreaseEdicts(); + //SV_IncreaseEdicts(); + // if SV_IncreaseEdicts was called the base pointer needs to be updated + if (ent) + val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs); + val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i)); + break; + + case ev_field: + def = PRVM_ED_FindField(s); + if (!def) + { + MsgDev("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME); + return false; + } + val->_int = def->ofs; + break; + + case ev_function: + func = PRVM_ED_FindFunction(s); + if (!func) + { + Msg("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME); + return false; + } + val->function = func - prog->functions; + break; + + default: + Msg("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME); + return false; + } + return true; +} + +/* +============= +PRVM_ED_EdictSet_f + +Console command to set a field of a specified edict +============= +*/ +void PRVM_ED_EdictSet_f(void) +{ + prvm_edict_t *ed; + ddef_t *key; + + if(Cmd_Argc() != 5) + { + Con_Print("prvm_edictset \n"); + return; + } + + PRVM_Begin; + if(!PRVM_SetProgFromString(Cmd_Argv(1))) + { + Msg("Wrong program name %s !\n", Cmd_Argv(1)); + return; + } + + ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2))); + + if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0) + Msg("Key %s not found !\n", Cmd_Argv(3)); + else + PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4)); + + PRVM_End; +} + +/* +==================== +PRVM_ED_ParseEdict + +Parses an edict out of the given string, returning the new position +ed should be a properly initialized empty edict. +Used for initial level load and for savegames. +==================== +*/ +const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent) +{ + ddef_t *key; + bool init; + char keyname[256]; + size_t n; + + init = false; + +// go through all the dictionary pairs + while (1) + { + // parse key + if (!COM_Parse(&data)) + PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace"); + + MsgDev("Key: \"%s\"", COM_Token()); + if (COM_Token()[0] == '}') break; + + strncpy (keyname, COM_Token(), sizeof(keyname)); + + // another hack to fix keynames with trailing spaces + n = strlen(keyname); + while (n && keyname[n-1] == ' ') + { + keyname[n-1] = 0; + n--; + } + + // parse value + if (!COM_Parse(&data)) + PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace"); + MsgDev(" \"%s\"\n", COM_Token()); + + if (COM_Token()[0] == '}') + PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data"); + + init = true; + + // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp) + if (!keyname[0]) + continue; + + // keynames with a leading underscore are used for utility comments, + // and are immediately discarded by quake + if (keyname[0] == '_') continue; + + key = PRVM_ED_FindField (keyname); + if (!key) + { + MsgDev("%s: '%s' is not a field\n", PRVM_NAME, keyname); + continue; + } + + if (!PRVM_ED_ParseEpair(ent, key, COM_Token())) + PRVM_ERROR ("PRVM_ED_ParseEdict: parse error"); + } + + if (!init) ent->priv.ed->free = true; + + return data; +} + + +/* +================ +PRVM_ED_LoadFromFile + +The entities are directly placed in the array, rather than allocated with +PRVM_ED_Alloc, because otherwise an error loading the map would have entity +number references out of order. + +Creates a server's entity / program execution context by +parsing textual entity definitions out of an ent file. + +Used for both fresh maps and savegame loads. A fresh map would also need +to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves. +================ +*/ +void PRVM_ED_LoadFromFile (const char *data) +{ + prvm_edict_t *ent; + int parsed, inhibited, spawned, died; + mfunction_t *func; + + parsed = 0; + inhibited = 0; + spawned = 0; + died = 0; + + + // parse ents + while (1) + { + // parse the opening brace + if (!COM_Parse(&data)) + break; + if (COM_Token()[0] != '{') + PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, COM_Token()); + + // CHANGED: this is not conform to PR_LoadFromFile + if(prog->loadintoworld) + { + prog->loadintoworld = false; + ent = PRVM_EDICT_NUM(0); + } + else + ent = PRVM_ED_Alloc(); + + // clear it + if (ent != prog->edicts) // hack + memset (ent->fields.vp, 0, prog->progs->entityfields * 4); + + data = PRVM_ED_ParseEdict (data, ent); + parsed++; + + // remove the entity ? + if(prog->load_edict && !prog->load_edict(ent)) + { + PRVM_ED_Free(ent); + inhibited++; + continue; + } + + // immediately call spawn function, but only if there is a self global and a classname + if(prog->self && prog->flag & PRVM_FE_CLASSNAME) + { + string_t handle = *(string_t*)&((unsigned char*)ent->fields.vp)[PRVM_ED_FindFieldOffset("classname")]; + if (!handle) + { + Con_Print("No classname for:\n"); + PRVM_ED_Print(ent); + PRVM_ED_Free (ent); + continue; + } + + // look for the spawn function + func = PRVM_ED_FindFunction (PRVM_GetString(handle)); + + if (!func) + { + if (1) // don't confuse non-developers with errors + { + Msg("No spawn function for:\n"); + PRVM_ED_Print(ent); + } + PRVM_ED_Free (ent); + continue; + } + + // self = ent + PRVM_G_INT(prog->self->ofs) = PRVM_EDICT_TO_PROG(ent); + PRVM_ExecuteProgram (func - prog->functions, "" ); + } + + spawned++; + if (ent->priv.ed->free) + died++; + } + + MsgDev("%s: %i new entities parsed, %i new inhibited, %i (%i new) spawned (whereas %i removed self, %i stayed)\n", PRVM_NAME, parsed, inhibited, prog->num_edicts, spawned, died, spawned - died); +} + +/* +=============== +PRVM_ResetProg +=============== +*/ + +void PRVM_ResetProg() +{ + PRVM_GCALL(reset_cmd)(); + Mem_FreePool(&prog->progs_mempool); + memset(prog,0,sizeof(prvm_prog_t)); +} + +/* +=============== +PRVM_LoadLNO +=============== +*/ +void PRVM_LoadLNO( const char *progname ) +{ + fs_offset_t filesize; + unsigned char *lno; + unsigned int *header; + char filename[512]; + + strcpy( filename, progname ); + FS_StripExtension( filename ); + FS_DefaultExtension( filename, ".lno" ); + + lno = FS_LoadFile( filename, &filesize ); + if( !lno ) return; + +/* + FS_Write (h, &lnotype, sizeof(int)); + FS_Write (h, &version, sizeof(int)); + FS_Write (h, &numglobaldefs, sizeof(int)); + FS_Write (h, &numpr_globals, sizeof(int)); + FS_Write (h, &numfielddefs, sizeof(int)); + FS_Write (h, &numstatements, sizeof(int)); + FS_Write (h, statement_linenums, numstatements * sizeof(int)); +*/ + if( (uint) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) + { + Mem_Free(lno); + return; + } + + header = (uint *) lno; + if( header[ 0 ] == *(unsigned int *) "LNOF" && + LittleLong( header[ 1 ] ) == 1 && + (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs && + (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals && + (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs && + (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements ) + { + prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) ); + memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) ); + } + Mem_Free( lno ); +} + +/* +=============== +PRVM_LoadProgs +=============== +*/ +void PRVM_LoadProgs (const char *filename, int numedfunc, char **ed_func, int numedfields, prvm_fieldvars_t *ed_field) +{ + int i; + dstatement_t *st; + ddef_t *infielddefs; + dfunction_t *dfunctions; + fs_offset_t filesize; + + if( prog->loaded ) + { + PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME ); + } + + prog->progs = (dprograms_t *)FS_LoadFile (filename, &filesize); + if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t)) + PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME); + + MsgDev("%s programs occupy %iK.\n", PRVM_NAME, filesize/1024); + + prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize); + + // byte swap the header + for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++) + ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] ); + + if (prog->progs->version != VPROGS_VERSION) + PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, VPROGS_VERSION); + + //try to recognize progs.dat by crc + switch(prog->progs->crc) + { + case PROG_CRC_SERVER: + Msg("Loading ^3server.dat\n"); + break; + default: + PRVM_ERROR ("%s: %s system vars have been modified, progdefs.h is out of date", PRVM_NAME, filename); + break; + } + + //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions); + dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions); + + prog->strings = (char *)prog->progs + prog->progs->ofs_strings; + prog->stringssize = 0; + for (i = 0;i < prog->progs->numstrings;i++) + { + if (prog->progs->ofs_strings + prog->stringssize >= (int)filesize) + PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename); + prog->stringssize += (int)strlen (prog->strings + prog->stringssize) + 1; + } + prog->numknownstrings = 0; + prog->maxknownstrings = 0; + prog->knownstrings = NULL; + prog->knownstrings_freeable = NULL; + + prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs); + + // we need to expand the fielddefs list to include all the engine fields, + // so allocate a new place for it + infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs); + // ( + DPFIELDS ) + prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numedfields) * sizeof(ddef_t)); + + prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements); + + prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile)); + + // moved edict_size calculation down below field adding code + + //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals); + prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals); + + // byte swap the lumps + for (i=0 ; iprogs->numstatements ; i++) + { + prog->statements[i].op = LittleShort(prog->statements[i].op); + prog->statements[i].a = LittleShort(prog->statements[i].a); + prog->statements[i].b = LittleShort(prog->statements[i].b); + prog->statements[i].c = LittleShort(prog->statements[i].c); + } + + prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions); + for (i = 0; i < prog->progs->numfunctions; i++) + { + prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement); + prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start); + prog->functions[i].s_name = LittleLong (dfunctions[i].s_name); + prog->functions[i].s_file = LittleLong (dfunctions[i].s_file); + prog->functions[i].numparms = LittleLong (dfunctions[i].numparms); + prog->functions[i].locals = LittleLong (dfunctions[i].locals); + memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size)); + } + + for (i = 0; i < prog->progs->numglobaldefs; i++) + { + prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type); + prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs); + prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name); + } + + // copy the progs fields to the new fields list + for (i = 0; i < prog->progs->numfielddefs; i++) + { + prog->fielddefs[i].type = LittleShort (infielddefs[i].type); + if (prog->fielddefs[i].type & DEF_SAVEGLOBAL) + PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME); + prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs); + prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name); + } + + // append the ed fields + for (i = 0; i < (int)numedfields; i++) + { + prog->fielddefs[prog->progs->numfielddefs].type = ed_field[i].type; + prog->fielddefs[prog->progs->numfielddefs].ofs = ed_field[i].ofs; + prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(ed_field[i].name); + } + + // check ed functions + for(i=0 ; i < numedfunc ; i++) + if(PRVM_ED_FindFunction(ed_func[i]) == 0) + PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, ed_func[i], filename); + + for (i=0 ; iprogs->numglobals ; i++) + ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]); + + // moved edict_size calculation down here, below field adding code + // LordHavoc: this no longer includes the prvm_edict_t header + prog->edict_size = prog->progs->entityfields * 4; + prog->edictareasize = prog->edict_size * prog->limit_edicts; + + // LordHavoc: bounds check anything static + for (i = 0, st = prog->statements; i < prog->progs->numstatements; i++, st++) + { + switch (st->op) + { + case OP_IF: + case OP_IFNOT: + if ((word) st->a >= prog->progs->numglobals || (word)st->b + i < 0 || (word)st->b + i >= prog->progs->numstatements) + PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME); + break; + case OP_GOTO: + if (st->a + i < 0 || st->a + i >= prog->progs->numstatements) + PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME); + break; + // global global global + case OP_ADD_F: + case OP_ADD_V: + case OP_SUB_F: + case OP_SUB_V: + case OP_MUL_F: + case OP_MUL_V: + case OP_MUL_FV: + case OP_MUL_VF: + case OP_DIV_F: + case OP_BITAND: + case OP_BITOR: + case OP_GE: + case OP_LE: + case OP_GT: + case OP_LT: + case OP_AND: + case OP_OR: + case OP_EQ_F: + case OP_EQ_V: + case OP_EQ_S: + case OP_EQ_E: + case OP_EQ_FNC: + case OP_NE_F: + case OP_NE_V: + case OP_NE_S: + case OP_NE_E: + case OP_NE_FNC: + case OP_ADDRESS: + case OP_LOAD_F: + case OP_LOAD_FLD: + case OP_LOAD_ENT: + case OP_LOAD_S: + case OP_LOAD_FNC: + case OP_LOAD_V: + if ((word) st->a >= prog->progs->numglobals || (word) st->b >= prog->progs->numglobals || (word)st->c >= prog->progs->numglobals) + PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i); + break; + // global none global + case OP_NOT_F: + case OP_NOT_V: + case OP_NOT_S: + case OP_NOT_FNC: + case OP_NOT_ENT: + if ((word) st->a >= prog->progs->numglobals || (word) st->c >= prog->progs->numglobals) + PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME); + break; + // 2 globals + case OP_STOREP_F: + case OP_STOREP_ENT: + case OP_STOREP_FLD: + case OP_STOREP_S: + case OP_STOREP_FNC: + case OP_STORE_F: + case OP_STORE_ENT: + case OP_STORE_FLD: + case OP_STORE_S: + case OP_STORE_FNC: + case OP_STATE: + case OP_STOREP_V: + case OP_STORE_V: + if ((word) st->a >= prog->progs->numglobals || (word) st->b >= prog->progs->numglobals) + PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME); + break; + // 1 global + case OP_CALL0: + case OP_CALL1: + case OP_CALL2: + case OP_CALL3: + case OP_CALL4: + case OP_CALL5: + case OP_CALL6: + case OP_CALL7: + case OP_CALL8: + case OP_DONE: + case OP_RETURN: + if ((word) st->a >= prog->progs->numglobals) + PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME); + break; + default: + MsgDev("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME); + break; + } + } + + PRVM_LoadLNO(filename); + + PRVM_Init_Exec(); + + prog->loaded = true; + + // set flags & ddef_ts in prog + + prog->flag = 0; + + prog->self = PRVM_ED_FindGlobal("self"); + + if( PRVM_ED_FindGlobal("time") && PRVM_ED_FindGlobal("time")->type & ev_float ) + prog->time = &PRVM_G_FLOAT(PRVM_ED_FindGlobal("time")->ofs); + + if(PRVM_ED_FindField ("chain")) + prog->flag |= PRVM_FE_CHAIN; + + if(PRVM_ED_FindField ("classname")) + prog->flag |= PRVM_FE_CLASSNAME; + + if(PRVM_ED_FindField ("nextthink") && PRVM_ED_FindField ("frame") && PRVM_ED_FindField ("think") + && prog->flag && prog->self) + prog->flag |= PRVM_OP_STATE; + + PRVM_GCALL(init_cmd)(); + + // init mempools + PRVM_MEM_Alloc(); +} + + +void PRVM_Fields_f (void) +{ + int i, j, ednum, used, usedamount; + int *counts; + char tempstring[MAX_INPUTLINE], tempstring2[260]; + const char *name; + prvm_edict_t *ed; + ddef_t *d; + int *v; + + // TODO + /* + if (!sv.active) + { + Con_Print("no progs loaded\n"); + return; + } + */ + + if(Cmd_Argc() != 2) + { + Con_Print("prvm_fields \n"); + return; + } + + PRVM_Begin; + if(!PRVM_SetProgFromString(Cmd_Argv(1))) + return; + + counts = (int *)Z_Malloc(prog->progs->numfielddefs * sizeof(int)); + for (ednum = 0; ednum < prog->max_edicts; ednum++) + { + ed = PRVM_EDICT_NUM(ednum); + if (ed->priv.ed->free) + continue; + for (i = 1;i < prog->progs->numfielddefs;i++) + { + d = &prog->fielddefs[i]; + name = PRVM_GetString(d->s_name); + if (name[strlen(name)-2] == '_') + continue; // skip _x, _y, _z vars + v = (int *)((char *)ed->fields.vp + d->ofs*4); + // if the value is still all 0, skip the field + for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++) + { + if (v[j]) + { + counts[i]++; + break; + } + } + } + } + used = 0; + usedamount = 0; + tempstring[0] = 0; + for (i = 0;i < prog->progs->numfielddefs;i++) + { + d = &prog->fielddefs[i]; + name = PRVM_GetString(d->s_name); + if (name[strlen(name)-2] == '_') + continue; // skip _x, _y, _z vars + switch(d->type & ~DEF_SAVEGLOBAL) + { + case ev_string: + strlcat(tempstring, "string ", sizeof(tempstring)); + break; + case ev_entity: + strlcat(tempstring, "entity ", sizeof(tempstring)); + break; + case ev_function: + strlcat(tempstring, "function ", sizeof(tempstring)); + break; + case ev_field: + strlcat(tempstring, "field ", sizeof(tempstring)); + break; + case ev_void: + strlcat(tempstring, "void ", sizeof(tempstring)); + break; + case ev_float: + strlcat(tempstring, "float ", sizeof(tempstring)); + break; + case ev_vector: + strlcat(tempstring, "vector ", sizeof(tempstring)); + break; + case ev_pointer: + strlcat(tempstring, "pointer ", sizeof(tempstring)); + break; + default: + sprintf (tempstring2, "bad type %i ", d->type & ~DEF_SAVEGLOBAL); + strlcat(tempstring, tempstring2, sizeof(tempstring)); + break; + } + if (strlen(name) > sizeof(tempstring2)-4) + { + memcpy (tempstring2, name, sizeof(tempstring2)-4); + tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.'; + tempstring2[sizeof(tempstring2)-1] = 0; + name = tempstring2; + } + strlcat(tempstring, name, sizeof(tempstring)); + for (j = (int)strlen(name);j < 25;j++) + strlcat(tempstring, " ", sizeof(tempstring)); + sprintf(tempstring2, "%5d", counts[i]); + strlcat(tempstring, tempstring2, sizeof(tempstring)); + strlcat(tempstring, "\n", sizeof(tempstring)); + if (strlen(tempstring) >= sizeof(tempstring)/2) + { + Con_Print(tempstring); + tempstring[0] = 0; + } + if (counts[i]) + { + used++; + usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL]; + } + } + Mem_Free(counts); + Msg("%s: %i entity fields (%i in use), totalling %i bytes per edict (%i in use), %i edicts allocated, %i bytes total spent on edict fields (%i needed)\n", PRVM_NAME, prog->progs->entityfields, used, prog->progs->entityfields * 4, usedamount * 4, prog->max_edicts, prog->progs->entityfields * 4 * prog->max_edicts, usedamount * 4 * prog->max_edicts); + + PRVM_End; +} + +void PRVM_Globals_f (void) +{ + int i; + // TODO + /*if (!sv.active) + { + Con_Print("no progs loaded\n"); + return; + }*/ + if(Cmd_Argc () != 2) + { + Con_Print("prvm_globals \n"); + return; + } + + PRVM_Begin; + if(!PRVM_SetProgFromString (Cmd_Argv (1))) + return; + + Msg("%s :", PRVM_NAME); + + for (i = 0;i < prog->progs->numglobaldefs;i++) + Msg("%s\n", PRVM_GetString(prog->globaldefs[i].s_name)); + Msg("%i global variables, totalling %i bytes\n", prog->progs->numglobals, prog->progs->numglobals * 4); + + PRVM_End; +} + +/* +=============== +PRVM_Global +=============== +*/ +void PRVM_Global_f(void) +{ + ddef_t *global; + if( Cmd_Argc() != 3 ) { + Msg( "prvm_global \n" ); + return; + } + + PRVM_Begin; + if( !PRVM_SetProgFromString( Cmd_Argv(1) ) ) + return; + + global = PRVM_ED_FindGlobal( Cmd_Argv(2) ); + if( !global ) + Msg( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) ); + else + Msg( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) ); + PRVM_End; +} + +/* +=============== +PRVM_GlobalSet +=============== +*/ +void PRVM_GlobalSet_f(void) +{ + ddef_t *global; + if( Cmd_Argc() != 4 ) { + Msg( "prvm_globalset \n" ); + return; + } + + PRVM_Begin; + if( !PRVM_SetProgFromString( Cmd_Argv(1) ) ) + return; + + global = PRVM_ED_FindGlobal( Cmd_Argv(2) ); + if( !global ) + Msg( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) ); + else + PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3) ); + PRVM_End; +} + +/* +=============== +PRVM_Init +=============== +*/ +void PRVM_Init (void) +{ + Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f); + Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f); + Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f); + Cmd_AddCommand ("prvm_profile", PRVM_Profile_f); + Cmd_AddCommand ("prvm_fields", PRVM_Fields_f); + Cmd_AddCommand ("prvm_globals", PRVM_Globals_f); + Cmd_AddCommand ("prvm_global", PRVM_Global_f); + Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f); + Cmd_AddCommand ("prvm_edictset", PRVM_ED_EdictSet_f); + Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f); + + // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others)) + prvm_boundscheck = Cvar_Get ("prvm_boundscheck", "0", 0); + prvm_traceqc = Cvar_Get ("prvm_traceqc", "0", 0); + prvm_statementprofiling = Cvar_Get ("prvm_statementprofiling", "0", 0); + + //VM_Cmd_Init(); +} + +// LordHavoc: changed this to NOT use a return statement, so that it can be used in functions that must return a value +void VM_Warning(const char *fmt, ...) +{ + va_list argptr; + char msg[MAX_INPUTLINE]; + + va_start(argptr, fmt); + sprintf(msg, fmt, argptr); + va_end(argptr); + + Msg(msg); + // TODO: either add a cvar/cmd to control the state dumping or replace some of the calls with Con_Printf [9/13/2006 Black] + //PRVM_PrintState(); +} + +/* +=============== +VM_error + +Abort the server with a game error +=============== +*/ +void VM_Error (const char *fmt, ...) +{ + char msg[1024]; + va_list argptr; + + va_start (argptr,fmt); + vsprintf (msg, fmt, argptr); + va_end (argptr); + + Com_Error (ERR_DROP, "Prvm error: %s", msg); +} + +/* +=============== +PRVM_InitProg +=============== +*/ +void PRVM_InitProg(int prognr) +{ + if(prognr < 0 || prognr >= PRVM_MAXPROGS) + Sys_Error("PRVM_InitProg: Invalid program number %i",prognr); + + prog = &prog_list[prognr]; + + if(prog->loaded) PRVM_ResetProg(); + + memset(prog, 0, sizeof(prvm_prog_t)); + + prog->time = &prog->_time; + prog->error_cmd = VM_Error; +} + +int PRVM_GetProgNr() +{ + return prog - prog_list; +} + +void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline) +{ + return pi->Mem.Alloc(prog->progs_mempool, buffersize, filename, fileline); +} + +void _PRVM_Free(void *buffer, const char *filename, int fileline) +{ + pi->Mem.Free(buffer, filename, fileline); +} + +void _PRVM_FreeAll(const char *filename, int fileline) +{ + prog->progs = NULL; + prog->fielddefs = NULL; + prog->functions = NULL; + pi->Mem.EmptyPool(prog->progs_mempool, filename, fileline); +} + +// LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons +prvm_edict_t *PRVM_EDICT_NUM_ERROR(int n, char *filename, int fileline) +{ + PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline); + return NULL; +} + +/* +int NUM_FOR_EDICT_ERROR(prvm_edict_t *e) +{ + PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts); + return 0; +} + +int PRVM_NUM_FOR_EDICT(prvm_edict_t *e) +{ + int n; + n = e - prog->edicts; + if ((unsigned int)n >= prog->limit_edicts) + Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer"); + return n; +} + +//int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e) +//{ +// return e - prog->edicts; +//} + +//#define PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields)) +//#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4))) +int PRVM_EDICT_TO_PROG(prvm_edict_t *e) +{ + int n; + n = e - prog->edicts; + if ((unsigned int)n >= (unsigned int)prog->max_edicts) + Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts); + return n;// EXPERIMENTAL + //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields; +} +prvm_edict_t *PRVM_PROG_TO_EDICT(int n) +{ + if ((unsigned int)n >= (unsigned int)prog->max_edicts) + Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n); + return prog->edicts + n; // EXPERIMENTAL + //return prog->edicts + ((n) / (progs->entityfields * 4)); +} +*/ + + +const char *PRVM_GetString(int num) +{ + if (num >= 0 && num < prog->stringssize) + return prog->strings + num; + else if (num < 0 && num >= -prog->numknownstrings) + { + num = -1 - num; + if (!prog->knownstrings[num]) + PRVM_ERROR("PRVM_GetString: attempt to get string that is already freed"); + return prog->knownstrings[num]; + } + else + { + PRVM_ERROR("PRVM_GetString: invalid string offset %i", num); + return ""; + } +} + +int PRVM_SetEngineString(const char *s) +{ + int i; + if (!s) + return 0; + if (s >= prog->strings && s <= prog->strings + prog->stringssize) + PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area"); + for (i = 0;i < prog->numknownstrings;i++) + if (prog->knownstrings[i] == s) + return -1 - i; + // new unknown engine string + if (developer->value >= 100) + Msg("new engine string %p\n", s); + for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++) + if (!prog->knownstrings[i]) + break; + if (i >= prog->numknownstrings) + { + if (i >= prog->maxknownstrings) + { + const char **oldstrings = prog->knownstrings; + const unsigned char *oldstrings_freeable = prog->knownstrings_freeable; + prog->maxknownstrings += 128; + prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *)); + prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char)); + if (prog->numknownstrings) + { + memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *)); + memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char)); + } + } + prog->numknownstrings++; + } + prog->firstfreeknownstring = i + 1; + prog->knownstrings[i] = s; + return -1 - i; +} + +int PRVM_AllocString(size_t bufferlength, char **pointer) +{ + int i; + if (!bufferlength) + return 0; + for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++) + if (!prog->knownstrings[i]) + break; + if (i >= prog->numknownstrings) + { + if (i >= prog->maxknownstrings) + { + const char **oldstrings = prog->knownstrings; + const unsigned char *oldstrings_freeable = prog->knownstrings_freeable; + prog->maxknownstrings += 128; + prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *)); + prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char)); + if (prog->numknownstrings) + { + memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *)); + memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char)); + } + } + prog->numknownstrings++; + } + prog->firstfreeknownstring = i + 1; + prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength); + prog->knownstrings_freeable[i] = true; + if (pointer) + *pointer = (char *)(prog->knownstrings[i]); + return -1 - i; +} + +void PRVM_FreeString(int num) +{ + if (num == 0) + PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string"); + else if (num >= 0 && num < prog->stringssize) + PRVM_ERROR("PRVM_FreeString: attempt to free a constant string"); + else if (num < 0 && num >= -prog->numknownstrings) + { + num = -1 - num; + if (!prog->knownstrings[num]) + PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string"); + if (!prog->knownstrings[num]) + PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine"); + PRVM_Free((char *)prog->knownstrings[num]); + prog->knownstrings[num] = NULL; + prog->knownstrings_freeable[num] = false; + prog->firstfreeknownstring = min(prog->firstfreeknownstring, num); + } + else + PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num); +} + diff --git a/engine/prvm/vm_exec.c b/engine/prvm/vm_exec.c new file mode 100644 index 00000000..9fc1bdf8 --- /dev/null +++ b/engine/prvm/vm_exec.c @@ -0,0 +1,588 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +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 the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "engine.h" +#include "progsvm.h" + +char *prvm_opnames[] = +{ +"^5DONE", + +"MUL_F", +"MUL_V", +"MUL_FV", +"MUL_VF", + +"DIV", + +"ADD_F", +"ADD_V", + +"SUB_F", +"SUB_V", + +"^2EQ_F", +"^2EQ_V", +"^2EQ_S", +"^2EQ_E", +"^2EQ_FNC", + +"^2NE_F", +"^2NE_V", +"^2NE_S", +"^2NE_E", +"^2NE_FNC", + +"^2LE", +"^2GE", +"^2LT", +"^2GT", + +"^6FIELD_F", +"^6FIELD_V", +"^6FIELD_S", +"^6FIELD_ENT", +"^6FIELD_FLD", +"^6FIELD_FNC", + +"^1ADDRESS", + +"STORE_F", +"STORE_V", +"STORE_S", +"STORE_ENT", +"STORE_FLD", +"STORE_FNC", + +"^1STOREP_F", +"^1STOREP_V", +"^1STOREP_S", +"^1STOREP_ENT", +"^1STOREP_FLD", +"^1STOREP_FNC", + +"^5RETURN", + +"^2NOT_F", +"^2NOT_V", +"^2NOT_S", +"^2NOT_ENT", +"^2NOT_FNC", + +"^5IF", +"^5IFNOT", + +"^3CALL0", +"^3CALL1", +"^3CALL2", +"^3CALL3", +"^3CALL4", +"^3CALL5", +"^3CALL6", +"^3CALL7", +"^3CALL8", + +"^1STATE", + +"^5GOTO", + +"^2AND", +"^2OR", + +"BITAND", +"BITOR" +}; + +char *PRVM_GlobalString (int ofs); +char *PRVM_GlobalStringNoContents (int ofs); + + +//============================================================================= + +/* +================= +PRVM_PrintStatement +================= +*/ +extern cvar_t *prvm_statementprofiling; +void PRVM_PrintStatement (dstatement_t *s) +{ + size_t i; + int opnum = (int)(s - prog->statements); + + Msg("s%i: ", opnum); + if( prog->statement_linenums ) + Msg( "%s:%i: ", PRVM_GetString( prog->xfunction->s_file ), prog->statement_linenums[ opnum ] ); + + if (prvm_statementprofiling->value) + Msg("%7.0f ", prog->statement_profile[s - prog->statements]); + + if ( (unsigned)s->op < sizeof(prvm_opnames)/sizeof(prvm_opnames[0])) + { + Msg("%s ", prvm_opnames[s->op]); + i = strlen(prvm_opnames[s->op]); + // don't count a preceding color tag when padding the name + if (prvm_opnames[s->op][0] == STRING_COLOR_TAG) + i -= 2; + for ( ; i<10 ; i++) + Con_Print(" "); + } + if (s->op == OP_IF || s->op == OP_IFNOT) + Msg("%s, s%i",PRVM_GlobalString((unsigned short) s->a),(signed short)s->b + opnum); + else if (s->op == OP_GOTO) + Msg("s%i",(signed short)s->a + opnum); + else if ( (unsigned)(s->op - OP_STORE_F) < 6) + { + Con_Print(PRVM_GlobalString((unsigned short) s->a)); + Con_Print(", "); + Con_Print(PRVM_GlobalStringNoContents((unsigned short) s->b)); + } + else if (s->op == OP_ADDRESS || (unsigned)(s->op - OP_LOAD_F) < 6) + { + if (s->a) + Con_Print(PRVM_GlobalString((unsigned short) s->a)); + if (s->b) + { + Con_Print(", "); + Con_Print(PRVM_GlobalStringNoContents((unsigned short) s->b)); + } + if (s->c) + { + Con_Print(", "); + Con_Print(PRVM_GlobalStringNoContents((unsigned short) s->c)); + } + } + else + { + if (s->a) + Con_Print(PRVM_GlobalString((unsigned short) s->a)); + if (s->b) + { + Con_Print(", "); + Con_Print(PRVM_GlobalString((unsigned short) s->b)); + } + if (s->c) + { + Con_Print(", "); + Con_Print(PRVM_GlobalStringNoContents((unsigned short) s->c)); + } + } + Con_Print("\n"); +} + +void PRVM_PrintFunctionStatements (const char *name) +{ + int i, firststatement, endstatement; + mfunction_t *func; + func = PRVM_ED_FindFunction (name); + if (!func) + { + Msg("%s progs: no function named %s\n", PRVM_NAME, name); + return; + } + firststatement = func->first_statement; + if (firststatement < 0) + { + Msg("%s progs: function %s is builtin #%i\n", PRVM_NAME, name, -firststatement); + return; + } + + // find the end statement + endstatement = prog->progs->numstatements; + for (i = 0;i < prog->progs->numfunctions;i++) + if (endstatement > prog->functions[i].first_statement && firststatement < prog->functions[i].first_statement) + endstatement = prog->functions[i].first_statement; + + // now print the range of statements + Msg("%s progs: disassembly of function %s (statements %i-%i):\n", PRVM_NAME, name, firststatement, endstatement); + for (i = firststatement;i < endstatement;i++) + { + PRVM_PrintStatement(prog->statements + i); + prog->statement_profile[i] = 0; + } +} + +/* +============ +PRVM_PrintFunction_f + +============ +*/ +void PRVM_PrintFunction_f (void) +{ + if (Cmd_Argc() != 3) + { + Msg("usage: prvm_printfunction \n"); + return; + } + + PRVM_Begin; + if(!PRVM_SetProgFromString(Cmd_Argv(1))) + return; + + PRVM_PrintFunctionStatements(Cmd_Argv(2)); + + PRVM_End; +} + +/* +============ +PRVM_StackTrace +============ +*/ +void PRVM_StackTrace (void) +{ + mfunction_t *f; + int i; + + prog->stack[prog->depth].s = prog->xstatement; + prog->stack[prog->depth].f = prog->xfunction; + for (i = prog->depth;i > 0;i--) + { + f = prog->stack[i].f; + + if (!f) + Con_Print("\n"); + else + Msg("%12s : %s : statement %i\n", PRVM_GetString(f->s_file), PRVM_GetString(f->s_name), prog->stack[i].s - f->first_statement); + } +} + + +void PRVM_Profile (int maxfunctions, int mininstructions) +{ + mfunction_t *f, *best; + int i, num; + double max; + + Msg( "%s Profile:\n[CallCount] [Statements] [BuiltinCost]\n", PRVM_NAME ); + + num = 0; + do + { + max = 0; + best = NULL; + for (i=0 ; iprogs->numfunctions ; i++) + { + f = &prog->functions[i]; + if (max < f->profile + f->builtinsprofile + f->callcount) + { + max = f->profile + f->builtinsprofile + f->callcount; + best = f; + } + } + if (best) + { + if (num < maxfunctions && max >= mininstructions) + { + if (best->first_statement < 0) + Msg("%9.0f ----- builtin ----- %s\n", best->callcount, PRVM_GetString(best->s_name)); + else + Msg("%9.0f %9.0f %9.0f %s\n", best->callcount, best->profile, best->builtinsprofile, PRVM_GetString(best->s_name)); + } + num++; + best->profile = 0; + best->builtinsprofile = 0; + best->callcount = 0; + } + } while (best); +} + +/* +============ +PRVM_Profile_f + +============ +*/ +void PRVM_Profile_f (void) +{ + int howmany; + + howmany = 1<<30; + if (Cmd_Argc() == 3) + howmany = atoi(Cmd_Argv(2)); + else if (Cmd_Argc() != 2) + { + Con_Print("prvm_profile \n"); + return; + } + + PRVM_Begin; + if(!PRVM_SetProgFromString(Cmd_Argv(1))) + return; + + PRVM_Profile(howmany, 1); + + PRVM_End; +} + +void PRVM_CrashAll() +{ + int i; + prvm_prog_t *oldprog = prog; + + for(i = 0; i < PRVM_MAXPROGS; i++) + { + if(!PRVM_ProgLoaded(i)) + continue; + PRVM_SetProg(i); + PRVM_Crash(); + } + + prog = oldprog; +} + +void PRVM_PrintState(void) +{ + int i; + if (prog->xfunction) + { + for (i = -7; i <= 0;i++) + if (prog->xstatement + i >= prog->xfunction->first_statement) + PRVM_PrintStatement (prog->statements + prog->xstatement + i); + } + else + Con_Print("null function executing??\n"); + PRVM_StackTrace (); +} + +void PRVM_Crash() +{ + if (prog == NULL) + return; + + if( prog->depth > 0 ) + { + Msg("QuakeC crash report for %s:\n", PRVM_NAME); + PRVM_PrintState(); + } + + // dump the stack so host_error can shutdown functions + prog->depth = 0; + prog->localstack_used = 0; + + // reset the prog pointer + prog = NULL; +} + +/* +============================================================================ +PRVM_ExecuteProgram + +The interpretation main loop +============================================================================ +*/ + +/* +==================== +PRVM_EnterFunction + +Returns the new program statement counter +==================== +*/ +int PRVM_EnterFunction (mfunction_t *f) +{ + int i, j, c, o; + + if (!f) + PRVM_ERROR ("PRVM_EnterFunction: NULL function in %s", PRVM_NAME); + + prog->stack[prog->depth].s = prog->xstatement; + prog->stack[prog->depth].f = prog->xfunction; + prog->depth++; + if (prog->depth >=PRVM_MAX_STACK_DEPTH) + PRVM_ERROR ("stack overflow"); + +// save off any locals that the new function steps on + c = f->locals; + if (prog->localstack_used + c > PRVM_LOCALSTACK_SIZE) + PRVM_ERROR ("PRVM_ExecuteProgram: locals stack overflow in %s", PRVM_NAME); + + for (i=0 ; i < c ; i++) + prog->localstack[prog->localstack_used+i] = ((int *)prog->globals.generic)[f->parm_start + i]; + prog->localstack_used += c; + +// copy parameters + o = f->parm_start; + for (i=0 ; inumparms ; i++) + { + for (j=0 ; jparm_size[i] ; j++) + { + ((int *)prog->globals.generic)[o] = ((int *)prog->globals.generic)[OFS_PARM0+i*3+j]; + o++; + } + } + + prog->xfunction = f; + return f->first_statement - 1; // offset the s++ +} + +/* +==================== +PRVM_LeaveFunction +==================== +*/ +int PRVM_LeaveFunction (void) +{ + int i, c; + + if (prog->depth <= 0) + PRVM_ERROR ("prog stack underflow in %s", PRVM_NAME); + + if (!prog->xfunction) + PRVM_ERROR ("PR_LeaveFunction: NULL function in %s", PRVM_NAME); +// restore locals from the stack + c = prog->xfunction->locals; + prog->localstack_used -= c; + if (prog->localstack_used < 0) + PRVM_ERROR ("PRVM_ExecuteProgram: locals stack underflow in %s", PRVM_NAME); + + for (i=0 ; i < c ; i++) + ((int *)prog->globals.generic)[prog->xfunction->parm_start + i] = prog->localstack[prog->localstack_used+i]; + +// up stack + prog->depth--; + prog->xfunction = prog->stack[prog->depth].f; + return prog->stack[prog->depth].s; +} + +void PRVM_Init_Exec(void) +{ + // dump the stack + prog->depth = 0; + prog->localstack_used = 0; + // reset the string table + // nothing here yet +} + +/* +==================== +PRVM_ExecuteProgram +==================== +*/ +// LordHavoc: optimized +#define OPA ((prvm_eval_t *)&prog->globals.generic[(unsigned short) st->a]) +#define OPB ((prvm_eval_t *)&prog->globals.generic[(unsigned short) st->b]) +#define OPC ((prvm_eval_t *)&prog->globals.generic[(unsigned short) st->c]) +extern cvar_t *prvm_boundscheck; +extern cvar_t *prvm_traceqc; +extern cvar_t *prvm_statementprofiling; +extern int PRVM_ED_FindFieldOffset (const char *field); +extern ddef_t* PRVM_ED_FindGlobal(const char *name); +void PRVM_ExecuteProgram (func_t fnum, const char *errormessage) +{ + dstatement_t *st, *startst; + mfunction_t *f, *newf; + prvm_edict_t *ed; + prvm_eval_t *ptr; + int jumpcount, cachedpr_trace, exitdepth; + + if (!fnum || fnum >= (unsigned int)prog->progs->numfunctions) + { + if (prog->self && PRVM_G_INT(prog->self->ofs)) + PRVM_ED_Print(PRVM_PROG_TO_EDICT(PRVM_G_INT(prog->self->ofs))); + PRVM_ERROR ("PRVM_ExecuteProgram: %s", errormessage); + } + + f = &prog->functions[fnum]; + + prog->trace = prvm_traceqc->value; + + // we know we're done when pr_depth drops to this + exitdepth = prog->depth; + + // make a stack frame + st = &prog->statements[PRVM_EnterFunction (f)]; + // save the starting statement pointer for profiling + // (when the function exits or jumps, the (st - startst) integer value is + // added to the function's profile counter) + startst = st; + // instead of counting instructions, we count jumps + jumpcount = 0; + // add one to the callcount of this function because otherwise engine-called functions aren't counted + prog->xfunction->callcount++; + +chooseexecprogram: + cachedpr_trace = prog->trace; + if (prvm_statementprofiling->value) + { +#define PRVMSTATEMENTPROFILING 1 + if (prvm_boundscheck->value) + { +#define PRVMBOUNDSCHECK 1 + if (prog->trace) + { +#define PRVMTRACE 1 +#include "vm_exec.h" +#undef PRVMTRACE + } + else + { +#include "vm_exec.h" + } +#undef PRVMBOUNDSCHECK + } + else + { + if (prog->trace) + { +#define PRVMTRACE 1 +#include "vm_exec.h" +#undef PRVMTRACE + } + else + { +#include "vm_exec.h" + } + } +#undef PRVMSTATEMENTPROFILING + } + else + { + if (prvm_boundscheck->value) + { +#define PRVMBOUNDSCHECK 1 + if (prog->trace) + { +#define PRVMTRACE 1 +#include "vm_exec.h" +#undef PRVMTRACE + } + else + { +#include "vm_exec.h" + } +#undef PRVMBOUNDSCHECK + } + else + { + if (prog->trace) + { +#define PRVMTRACE 1 +#include "vm_exec.h" +#undef PRVMTRACE + } + else + { +#include "vm_exec.h" + } + } + } +} diff --git a/engine/prvm/vm_exec.h b/engine/prvm/vm_exec.h new file mode 100644 index 00000000..c232999b --- /dev/null +++ b/engine/prvm/vm_exec.h @@ -0,0 +1,628 @@ + +// This code isn't #ifdef/#define protectable, don't try. + + while (1) + { + st++; + +#if PRVMTRACE + PRVM_PrintStatement(st); +#endif +#if PRVMSTATEMENTPROFILING + prog->statement_profile[st - prog->statements]++; +#endif + + switch (st->op) + { + case OP_ADD_F: + OPC->_float = OPA->_float + OPB->_float; + break; + case OP_ADD_V: + OPC->vector[0] = OPA->vector[0] + OPB->vector[0]; + OPC->vector[1] = OPA->vector[1] + OPB->vector[1]; + OPC->vector[2] = OPA->vector[2] + OPB->vector[2]; + break; + case OP_SUB_F: + OPC->_float = OPA->_float - OPB->_float; + break; + case OP_SUB_V: + OPC->vector[0] = OPA->vector[0] - OPB->vector[0]; + OPC->vector[1] = OPA->vector[1] - OPB->vector[1]; + OPC->vector[2] = OPA->vector[2] - OPB->vector[2]; + break; + case OP_MUL_F: + OPC->_float = OPA->_float * OPB->_float; + break; + case OP_MUL_V: + OPC->_float = OPA->vector[0]*OPB->vector[0] + OPA->vector[1]*OPB->vector[1] + OPA->vector[2]*OPB->vector[2]; + break; + case OP_MUL_FV: + OPC->vector[0] = OPA->_float * OPB->vector[0]; + OPC->vector[1] = OPA->_float * OPB->vector[1]; + OPC->vector[2] = OPA->_float * OPB->vector[2]; + break; + case OP_MUL_VF: + OPC->vector[0] = OPB->_float * OPA->vector[0]; + OPC->vector[1] = OPB->_float * OPA->vector[1]; + OPC->vector[2] = OPB->_float * OPA->vector[2]; + break; + case OP_DIV_F: + if( OPB->_float != 0.0f ) + { + OPC->_float = OPA->_float / OPB->_float; + } + else + { + if( developer->value >= 1 ) + { + prog->xfunction->profile += (st - startst); + startst = st; + prog->xstatement = st - prog->statements; + VM_Warning( "Attempted division by zero in %s\n", PRVM_NAME ); + } + OPC->_float = 0.0f; + } + break; + case OP_BITAND: + OPC->_float = (int)OPA->_float & (int)OPB->_float; + break; + case OP_BITOR: + OPC->_float = (int)OPA->_float | (int)OPB->_float; + break; + case OP_GE: + OPC->_float = OPA->_float >= OPB->_float; + break; + case OP_LE: + OPC->_float = OPA->_float <= OPB->_float; + break; + case OP_GT: + OPC->_float = OPA->_float > OPB->_float; + break; + case OP_LT: + OPC->_float = OPA->_float < OPB->_float; + break; + case OP_AND: + OPC->_float = OPA->_float && OPB->_float; + break; + case OP_OR: + OPC->_float = OPA->_float || OPB->_float; + break; + case OP_NOT_F: + OPC->_float = !OPA->_float; + break; + case OP_NOT_V: + OPC->_float = !OPA->vector[0] && !OPA->vector[1] && !OPA->vector[2]; + break; + case OP_NOT_S: + OPC->_float = !OPA->string || !*PRVM_GetString(OPA->string); + break; + case OP_NOT_FNC: + OPC->_float = !OPA->function; + break; + case OP_NOT_ENT: + OPC->_float = (OPA->edict == 0); + break; + case OP_EQ_F: + OPC->_float = OPA->_float == OPB->_float; + break; + case OP_EQ_V: + OPC->_float = (OPA->vector[0] == OPB->vector[0]) && (OPA->vector[1] == OPB->vector[1]) && (OPA->vector[2] == OPB->vector[2]); + break; + case OP_EQ_S: + OPC->_float = !strcmp(PRVM_GetString(OPA->string),PRVM_GetString(OPB->string)); + break; + case OP_EQ_E: + OPC->_float = OPA->_int == OPB->_int; + break; + case OP_EQ_FNC: + OPC->_float = OPA->function == OPB->function; + break; + case OP_NE_F: + OPC->_float = OPA->_float != OPB->_float; + break; + case OP_NE_V: + OPC->_float = (OPA->vector[0] != OPB->vector[0]) || (OPA->vector[1] != OPB->vector[1]) || (OPA->vector[2] != OPB->vector[2]); + break; + case OP_NE_S: + OPC->_float = strcmp(PRVM_GetString(OPA->string),PRVM_GetString(OPB->string)); + break; + case OP_NE_E: + OPC->_float = OPA->_int != OPB->_int; + break; + case OP_NE_FNC: + OPC->_float = OPA->function != OPB->function; + break; + + //================== + case OP_STORE_F: + case OP_STORE_ENT: + case OP_STORE_FLD: // integers + case OP_STORE_S: + case OP_STORE_FNC: // pointers + OPB->_int = OPA->_int; + break; + case OP_STORE_V: + OPB->ivector[0] = OPA->ivector[0]; + OPB->ivector[1] = OPA->ivector[1]; + OPB->ivector[2] = OPA->ivector[2]; + break; + + case OP_STOREP_F: + case OP_STOREP_ENT: + case OP_STOREP_FLD: // integers + case OP_STOREP_S: + case OP_STOREP_FNC: // pointers +#if PRVMBOUNDSCHECK + if (OPB->_int < 0 || OPB->_int + 4 > prog->edictareasize) + { + prog->xfunction->profile += (st - startst); + prog->xstatement = st - prog->statements; + PRVM_ERROR("%s attempted to write to an out of bounds edict (%i)", PRVM_NAME, OPB->_int); + return; + } +#endif + ptr = (prvm_eval_t *)((unsigned char *)prog->edictsfields + OPB->_int); + ptr->_int = OPA->_int; + break; + case OP_STOREP_V: +#if PRVMBOUNDSCHECK + if (OPB->_int < 0 || OPB->_int + 12 > prog->edictareasize) + { + prog->xfunction->profile += (st - startst); + prog->xstatement = st - prog->statements; + PRVM_ERROR("%s attempted to write to an out of bounds edict (%i)", PRVM_NAME, OPB->_int); + return; + } +#endif + ptr = (prvm_eval_t *)((unsigned char *)prog->edictsfields + OPB->_int); + ptr->vector[0] = OPA->vector[0]; + ptr->vector[1] = OPA->vector[1]; + ptr->vector[2] = OPA->vector[2]; + break; + + case OP_ADDRESS: +#if PRVMBOUNDSCHECK + if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->progs->entityfields)) + { + prog->xfunction->profile += (st - startst); + prog->xstatement = st - prog->statements; + PRVM_ERROR("%s attempted to address an invalid field (%i) in an edict", PRVM_NAME, OPB->_int); + return; + } +#endif + if (OPA->edict == 0 && !prog->allowworldwrites) + { + prog->xfunction->profile += (st - startst); + prog->xstatement = st - prog->statements; + PRVM_ERROR("forbidden assignment to null/world entity in %s", PRVM_NAME); + return; + } + ed = PRVM_PROG_TO_EDICT(OPA->edict); + OPC->_int = (unsigned char *)((int *)ed->fields.vp + OPB->_int) - (unsigned char *)prog->edictsfields; + break; + + case OP_LOAD_F: + case OP_LOAD_FLD: + case OP_LOAD_ENT: + case OP_LOAD_S: + case OP_LOAD_FNC: +#if PRVMBOUNDSCHECK + if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->progs->entityfields)) + { + prog->xfunction->profile += (st - startst); + prog->xstatement = st - prog->statements; + PRVM_ERROR("%s attempted to read an invalid field in an edict (%i)", PRVM_NAME, OPB->_int); + return; + } +#endif + ed = PRVM_PROG_TO_EDICT(OPA->edict); + OPC->_int = ((prvm_eval_t *)((int *)ed->fields.vp + OPB->_int))->_int; + break; + + case OP_LOAD_V: +#if PRVMBOUNDSCHECK + if (OPB->_int < 0 || OPB->_int + 2 >= prog->progs->entityfields) + { + prog->xfunction->profile += (st - startst); + prog->xstatement = st - prog->statements; + PRVM_ERROR("%s attempted to read an invalid field in an edict (%i)", PRVM_NAME, OPB->_int); + return; + } +#endif + ed = PRVM_PROG_TO_EDICT(OPA->edict); + OPC->vector[0] = ((prvm_eval_t *)((int *)ed->fields.vp + OPB->_int))->vector[0]; + OPC->vector[1] = ((prvm_eval_t *)((int *)ed->fields.vp + OPB->_int))->vector[1]; + OPC->vector[2] = ((prvm_eval_t *)((int *)ed->fields.vp + OPB->_int))->vector[2]; + break; + + //================== + + case OP_IFNOT: + if (!OPA->_int) + { + prog->xfunction->profile += (st - startst); + st += st->b - 1; // offset the s++ + startst = st; + if (++jumpcount == 10000000) + { + prog->xstatement = st - prog->statements; + PRVM_Profile(1<<30, 1000000); + PRVM_ERROR("runaway loop counter hit limit of %d jumps\ntip: read above for list of most-executed functions", jumpcount, PRVM_NAME); + } + } + break; + + case OP_IF: + if (OPA->_int) + { + prog->xfunction->profile += (st - startst); + st += st->b - 1; // offset the s++ + startst = st; + if (++jumpcount == 10000000) + { + prog->xstatement = st - prog->statements; + PRVM_Profile(1<<30, 1000000); + PRVM_ERROR("runaway loop counter hit limit of %d jumps\ntip: read above for list of most-executed functions", jumpcount, PRVM_NAME); + } + } + break; + + case OP_GOTO: + prog->xfunction->profile += (st - startst); + st += st->a - 1; // offset the s++ + startst = st; + if (++jumpcount == 10000000) + { + prog->xstatement = st - prog->statements; + PRVM_Profile(1<<30, 1000000); + PRVM_ERROR("runaway loop counter hit limit of %d jumps\ntip: read above for list of most-executed functions", jumpcount, PRVM_NAME); + } + break; + case OP_CALL0: + case OP_CALL1: + case OP_CALL2: + case OP_CALL3: + case OP_CALL4: + case OP_CALL5: + case OP_CALL6: + case OP_CALL7: + case OP_CALL8: + prog->xfunction->profile += (st - startst); + startst = st; + prog->xstatement = st - prog->statements; + prog->argc = st->op - OP_CALL0; + if (!OPA->function) PRVM_ERROR("NULL function in %s", PRVM_NAME); + + newf = &prog->functions[OPA->function]; + newf->callcount++; + + if (newf->first_statement < 0) + { + // negative statements are built in functions + int builtinnumber = -newf->first_statement; + prog->xfunction->builtinsprofile++; + if (builtinnumber < prog->numbuiltins && prog->builtins[builtinnumber]) + prog->builtins[builtinnumber](); + else PRVM_ERROR("No such builtin #%i in %s", builtinnumber, PRVM_NAME); + } + else st = prog->statements + PRVM_EnterFunction(newf); + startst = st; + break; + case OP_DONE: + case OP_RETURN: + prog->xfunction->profile += (st - startst); + prog->xstatement = st - prog->statements; + + prog->globals.generic[OFS_RETURN+0] = prog->globals.generic[(word) st->a+0]; + prog->globals.generic[OFS_RETURN+1] = prog->globals.generic[(word) st->a+1]; + prog->globals.generic[OFS_RETURN+2] = prog->globals.generic[(word) st->a+2]; + + st = prog->statements + PRVM_LeaveFunction(); + startst = st; + if (prog->depth <= exitdepth) + return; // all done + if (prog->trace != cachedpr_trace) + goto chooseexecprogram; + break; + + case OP_STATE: + if(prog->flag & PRVM_OP_STATE) + { + ed = PRVM_PROG_TO_EDICT(PRVM_G_INT(prog->self->ofs)); + PRVM_E_FLOAT(ed, PRVM_ED_FindField ("nextthink")->ofs) = *prog->time + 0.1; + PRVM_E_FLOAT(ed, PRVM_ED_FindField ("frame")->ofs) = OPA->_float; + *(func_t *)((float*)ed->fields.vp + PRVM_ED_FindField ("think")->ofs) = OPB->function; + } + else + { + prog->xfunction->profile += (st - startst); + prog->xstatement = st - prog->statements; + PRVM_ERROR("OP_STATE not supported by %s", PRVM_NAME); + } + break; + +// LordHavoc: to be enabled when Progs version 7 (or whatever it will be numbered) is finalized +/* + case OP_ADD_I: + OPC->_int = OPA->_int + OPB->_int; + break; + case OP_ADD_IF: + OPC->_int = OPA->_int + (int) OPB->_float; + break; + case OP_ADD_FI: + OPC->_float = OPA->_float + (float) OPB->_int; + break; + case OP_SUB_I: + OPC->_int = OPA->_int - OPB->_int; + break; + case OP_SUB_IF: + OPC->_int = OPA->_int - (int) OPB->_float; + break; + case OP_SUB_FI: + OPC->_float = OPA->_float - (float) OPB->_int; + break; + case OP_MUL_I: + OPC->_int = OPA->_int * OPB->_int; + break; + case OP_MUL_IF: + OPC->_int = OPA->_int * (int) OPB->_float; + break; + case OP_MUL_FI: + OPC->_float = OPA->_float * (float) OPB->_int; + break; + case OP_MUL_VI: + OPC->vector[0] = (float) OPB->_int * OPA->vector[0]; + OPC->vector[1] = (float) OPB->_int * OPA->vector[1]; + OPC->vector[2] = (float) OPB->_int * OPA->vector[2]; + break; + case OP_DIV_VF: + { + float temp = 1.0f / OPB->_float; + OPC->vector[0] = temp * OPA->vector[0]; + OPC->vector[1] = temp * OPA->vector[1]; + OPC->vector[2] = temp * OPA->vector[2]; + } + break; + case OP_DIV_I: + OPC->_int = OPA->_int / OPB->_int; + break; + case OP_DIV_IF: + OPC->_int = OPA->_int / (int) OPB->_float; + break; + case OP_DIV_FI: + OPC->_float = OPA->_float / (float) OPB->_int; + break; + case OP_CONV_IF: + OPC->_float = OPA->_int; + break; + case OP_CONV_FI: + OPC->_int = OPA->_float; + break; + case OP_BITAND_I: + OPC->_int = OPA->_int & OPB->_int; + break; + case OP_BITOR_I: + OPC->_int = OPA->_int | OPB->_int; + break; + case OP_BITAND_IF: + OPC->_int = OPA->_int & (int)OPB->_float; + break; + case OP_BITOR_IF: + OPC->_int = OPA->_int | (int)OPB->_float; + break; + case OP_BITAND_FI: + OPC->_float = (int)OPA->_float & OPB->_int; + break; + case OP_BITOR_FI: + OPC->_float = (int)OPA->_float | OPB->_int; + break; + case OP_GE_I: + OPC->_float = OPA->_int >= OPB->_int; + break; + case OP_LE_I: + OPC->_float = OPA->_int <= OPB->_int; + break; + case OP_GT_I: + OPC->_float = OPA->_int > OPB->_int; + break; + case OP_LT_I: + OPC->_float = OPA->_int < OPB->_int; + break; + case OP_AND_I: + OPC->_float = OPA->_int && OPB->_int; + break; + case OP_OR_I: + OPC->_float = OPA->_int || OPB->_int; + break; + case OP_GE_IF: + OPC->_float = (float)OPA->_int >= OPB->_float; + break; + case OP_LE_IF: + OPC->_float = (float)OPA->_int <= OPB->_float; + break; + case OP_GT_IF: + OPC->_float = (float)OPA->_int > OPB->_float; + break; + case OP_LT_IF: + OPC->_float = (float)OPA->_int < OPB->_float; + break; + case OP_AND_IF: + OPC->_float = (float)OPA->_int && OPB->_float; + break; + case OP_OR_IF: + OPC->_float = (float)OPA->_int || OPB->_float; + break; + case OP_GE_FI: + OPC->_float = OPA->_float >= (float)OPB->_int; + break; + case OP_LE_FI: + OPC->_float = OPA->_float <= (float)OPB->_int; + break; + case OP_GT_FI: + OPC->_float = OPA->_float > (float)OPB->_int; + break; + case OP_LT_FI: + OPC->_float = OPA->_float < (float)OPB->_int; + break; + case OP_AND_FI: + OPC->_float = OPA->_float && (float)OPB->_int; + break; + case OP_OR_FI: + OPC->_float = OPA->_float || (float)OPB->_int; + break; + case OP_NOT_I: + OPC->_float = !OPA->_int; + break; + case OP_EQ_I: + OPC->_float = OPA->_int == OPB->_int; + break; + case OP_EQ_IF: + OPC->_float = (float)OPA->_int == OPB->_float; + break; + case OP_EQ_FI: + OPC->_float = OPA->_float == (float)OPB->_int; + break; + case OP_NE_I: + OPC->_float = OPA->_int != OPB->_int; + break; + case OP_NE_IF: + OPC->_float = (float)OPA->_int != OPB->_float; + break; + case OP_NE_FI: + OPC->_float = OPA->_float != (float)OPB->_int; + break; + case OP_STORE_I: + OPB->_int = OPA->_int; + break; + case OP_STOREP_I: +#if PRBOUNDSCHECK + if (OPB->_int < 0 || OPB->_int + 4 > pr_edictareasize) + { + prog->xfunction->profile += (st - startst); + prog->xstatement = st - prog->statements; + PRVM_ERROR ("%s Progs attempted to write to an out of bounds edict", PRVM_NAME); + return; + } +#endif + ptr = (prvm_eval_t *)((unsigned char *)prog->edictsfields + OPB->_int); + ptr->_int = OPA->_int; + break; + case OP_LOAD_I: +#if PRBOUNDSCHECK + if (OPA->edict < 0 || OPA->edict >= pr_edictareasize) + { + prog->xfunction->profile += (st - startst); + prog->xstatement = st - prog->statements; + PRVM_ERROR ("%s Progs attempted to read an out of bounds edict number", PRVM_NAME); + return; + } + if (OPB->_int < 0 || OPB->_int >= progs->entityfields) + { + prog->xfunction->profile += (st - startst); + prog->xstatement = st - prog->statements; + PRVM_ERROR ("%s Progs attempted to read an invalid field in an edict", PRVM_NAME); + return; + } +#endif + ed = PRVM_PROG_TO_EDICT(OPA->edict); + OPC->_int = ((prvm_eval_t *)((int *)ed->v + OPB->_int))->_int; + break; + + case OP_GSTOREP_I: + case OP_GSTOREP_F: + case OP_GSTOREP_ENT: + case OP_GSTOREP_FLD: // integers + case OP_GSTOREP_S: + case OP_GSTOREP_FNC: // pointers +#if PRBOUNDSCHECK + if (OPB->_int < 0 || OPB->_int >= pr_globaldefs) + { + prog->xfunction->profile += (st - startst); + prog->xstatement = st - prog->statements; + PRVM_ERROR ("%s Progs attempted to write to an invalid indexed global", PRVM_NAME); + return; + } +#endif + pr_globals[OPB->_int] = OPA->_float; + break; + case OP_GSTOREP_V: +#if PRBOUNDSCHECK + if (OPB->_int < 0 || OPB->_int + 2 >= pr_globaldefs) + { + prog->xfunction->profile += (st - startst); + prog->xstatement = st - prog->statements; + PRVM_ERROR ("%s Progs attempted to write to an invalid indexed global", PRVM_NAME); + return; + } +#endif + pr_globals[OPB->_int ] = OPA->vector[0]; + pr_globals[OPB->_int+1] = OPA->vector[1]; + pr_globals[OPB->_int+2] = OPA->vector[2]; + break; + + case OP_GADDRESS: + i = OPA->_int + (int) OPB->_float; +#if PRBOUNDSCHECK + if (i < 0 || i >= pr_globaldefs) + { + prog->xfunction->profile += (st - startst); + prog->xstatement = st - prog->statements; + PRVM_ERROR ("%s Progs attempted to address an out of bounds global", PRVM_NAME); + return; + } +#endif + OPC->_float = pr_globals[i]; + break; + + case OP_GLOAD_I: + case OP_GLOAD_F: + case OP_GLOAD_FLD: + case OP_GLOAD_ENT: + case OP_GLOAD_S: + case OP_GLOAD_FNC: +#if PRBOUNDSCHECK + if (OPA->_int < 0 || OPA->_int >= pr_globaldefs) + { + prog->xfunction->profile += (st - startst); + prog->xstatement = st - prog->statements; + PRVM_ERROR ("%s Progs attempted to read an invalid indexed global", PRVM_NAME); + return; + } +#endif + OPC->_float = pr_globals[OPA->_int]; + break; + + case OP_GLOAD_V: +#if PRBOUNDSCHECK + if (OPA->_int < 0 || OPA->_int + 2 >= pr_globaldefs) + { + prog->xfunction->profile += (st - startst); + prog->xstatement = st - prog->statements; + PRVM_ERROR ("%s Progs attempted to read an invalid indexed global", PRVM_NAME); + return; + } +#endif + OPC->vector[0] = pr_globals[OPA->_int ]; + OPC->vector[1] = pr_globals[OPA->_int+1]; + OPC->vector[2] = pr_globals[OPA->_int+2]; + break; + + case OP_BOUNDCHECK: + if (OPA->_int < 0 || OPA->_int >= st->b) + { + prog->xfunction->profile += (st - startst); + prog->xstatement = st - prog->statements; + PRVM_ERROR ("%s Progs boundcheck failed at line number %d, value is < 0 or >= %d", PRVM_NAME, st->b, st->c); + return; + } + break; + +*/ + + default: + prog->xfunction->profile += (st - startst); + prog->xstatement = st - prog->statements; + PRVM_ERROR ("Bad opcode %i in %s", st->op, PRVM_NAME); + } + } + diff --git a/engine/server/server.h b/engine/server/server.h index afbf7fae..e4935cc8 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -18,11 +18,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // server.h - +#ifndef SERVER_H +#define SERVER_H //define PARANOID // speed sapping error checking -#include "ref_server.h" +#include "progsvm.h" +#include "vm_cmds.h" //============================================================================= @@ -43,14 +45,18 @@ typedef struct { server_state_t state; // precache commands are only valid during load - bool attractloop; // running cinematics and demos for the local system only - bool loadgame; // client begins should reuse existing entity - - unsigned time; // always sv.framenum * 100 msec - int framenum; + bool attractloop; // running cinematics and demos for the local system only + bool loadgame; // client begins should reuse existing entity + float time; // always sv.framenum * 100 msec + float frametime; + float lastchecktime; + int framenum; + + int lastcheck; + char name[MAX_QPATH]; // map name, or cinematic name - struct cmodel_s *models[MAX_MODELS]; + cmodel_t *models[MAX_MODELS]; char configstrings[MAX_CONFIGSTRINGS][MAX_QPATH]; entity_state_t baselines[MAX_EDICTS]; @@ -60,6 +66,8 @@ typedef struct sizebuf_t multicast; byte multicast_buf[MAX_MSGLEN]; + prvm_edict_t **moved_edicts; + // demo server information file_t *demofile; bool timedemo; // don't time sync @@ -87,49 +95,52 @@ typedef struct int senttime; // for ping calculations } client_frame_t; +#define NUM_SPAWN_PARMS 16 #define LATENCY_COUNTS 16 #define RATE_MESSAGES 10 typedef struct client_s { - client_state_t state; + client_state_t state; char userinfo[MAX_INFO_STRING]; // name, etc - int lastframe; // for delta compression - usercmd_t lastcmd; // for filling in big drops + int lastframe; // for delta compression + usercmd_t lastcmd; // for filling in big drops - int commandMsec; // every seconds this is reset, if user - // commands exhaust it, assume time cheating + int commandMsec; // every seconds this is reset, if user + // commands exhaust it, assume time cheating - int frame_latency[LATENCY_COUNTS]; - int ping; + int frame_latency[LATENCY_COUNTS]; + int ping; - int message_size[RATE_MESSAGES]; // used to rate drop packets - int rate; - int surpressCount; // number of messages rate supressed + int message_size[RATE_MESSAGES]; // used to rate drop packets + int rate; + int surpressCount; // number of messages rate supressed - edict_t *edict; // EDICT_NUM(clientnum+1) + float spawn_parms[NUM_SPAWN_PARMS]; // quake 1 legacy + + prvm_edict_t *edict; // EDICT_NUM(clientnum+1) char name[32]; // extracted from userinfo, high bits masked - int messagelevel; // for filtering printed messages + int messagelevel; // for filtering printed messages // The datagram is written to by sound calls, prints, temp ents, etc. // It can be harmlessly overflowed. - sizebuf_t datagram; + sizebuf_t datagram; byte datagram_buf[MAX_MSGLEN]; - client_frame_t frames[UPDATE_BACKUP]; // updates can be delta'd from here + client_frame_t frames[UPDATE_BACKUP]; // updates can be delta'd from here byte *download; // file being downloaded - int downloadsize; // total bytes (can't use EOF because of paks) - int downloadcount; // bytes sent + int downloadsize; // total bytes (can't use EOF because of paks) + int downloadcount; // bytes sent - int lastmessage; // sv.framenum when packet was last received - int lastconnect; + int lastmessage; // sv.framenum when packet was last received + int lastconnect; - int challenge; // challenge of this user, randomly generated + int challenge; // challenge of this user, randomly generated - netchan_t netchan; + netchan_t netchan; } client_t; // a client can leave the server in one of four ways: @@ -156,7 +167,9 @@ typedef struct typedef struct { bool initialized; // sv_init has completed - int realtime; // always increasing, no clamping, etc + double realtime; // always increasing, no clamping, etc + + int serverflags; char mapcmd[MAX_TOKEN_CHARS]; // ie: *intro.cin+base char comment[MAX_TOKEN_CHARS]; // map name, e.t.c. @@ -169,7 +182,7 @@ typedef struct int next_client_entities; // next client_entity to use entity_state_t *client_entities; // [num_client_entities] - int last_heartbeat; + float last_heartbeat; challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting @@ -179,6 +192,17 @@ typedef struct byte demo_multicast_buf[MAX_MSGLEN]; } server_static_t; +typedef struct +{ + vec3_t boxmins, boxmaxs; // enclose the test object along entire move + float *mins, *maxs; // size of the moving object + vec3_t mins2, maxs2; // size when clipping against mosnters + float *start, *end; + trace_t trace; + prvm_edict_t *passedict; + int contentmask; +} moveclip_t; + //============================================================================= extern netadr_t net_from; @@ -197,7 +221,7 @@ extern cvar_t *sv_airaccelerate; // don't reload level state when reentering extern cvar_t *sv_enforcetime; extern client_t *sv_client; -extern edict_t *sv_player; +extern prvm_edict_t *sv_player; //=========================================================== @@ -207,9 +231,9 @@ extern edict_t *sv_player; void SV_FinalMessage (char *message, bool reconnect); void SV_DropClient (client_t *drop); -int SV_ModelIndex (char *name); -int SV_SoundIndex (char *name); -int SV_ImageIndex (char *name); +int SV_ModelIndex (const char *name); +int SV_SoundIndex (const char *name); +int SV_ImageIndex (const char *name); void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg); @@ -228,12 +252,16 @@ void Master_Packet (void); // void SV_InitGame (void); void SV_Map (bool attractloop, char *levelstring, char *savename, bool loadgame); - +void SV_SpawnServer (char *server, char *spawnpoint, char *savename, server_state_t serverstate, bool attractloop, bool loadgame); +void SV_VM_Setup(void); +void SV_VM_Begin(void); +void SV_VM_End(void); // // sv_phys.c // void SV_PrepWorldFrame (void); +void SV_Physics (void); // // sv_send.c @@ -247,7 +275,7 @@ void SV_FlushRedirect (int sv_redirected, char *outputbuf); void SV_DemoCompleted (void); void SV_SendClientMessages (void); -void SV_StartSound (vec3_t origin, edict_t *entity, int channel, +void SV_StartSound (vec3_t origin, prvm_edict_t *entity, int channel, int soundindex, float volume, float attenuation, float timeofs); void SV_ClientPrintf (client_t *cl, int level, char *fmt, ...); @@ -259,6 +287,8 @@ void SV_BroadcastCommand (char *fmt, ...); // void SV_Nextserver (void); void SV_ExecuteClientMessage (client_t *cl); +void SV_ApplyClientMove (void); +void SV_ClientThink (void); // // sv_ccmds.c @@ -271,24 +301,17 @@ void SV_Status_f (void); void SV_WriteFrameToClient (client_t *client, sizebuf_t *msg); void SV_RecordDemoMessage (void); void SV_BuildClientFrame (client_t *client); +void SV_FatPVS (vec3_t org); void SV_Error (char *error, ...); - -// -// sv_game.c -// -extern game_export_t *ge; - -void SV_InitGameProgs (void); -void SV_ShutdownGameProgs (void); -void SV_InitEdict (edict_t *e); +void SV_InitEdict (prvm_edict_t *e); // // sv_studio.c // -byte *SV_GetModelPtr(edict_t *ent); +byte *SV_GetModelPtr(prvm_edict_t *ent); int SV_StudioExtractBbox( studiohdr_t *phdr, int sequence, float *mins, float *maxs ); @@ -308,18 +331,18 @@ void SV_ReadLevelFile( char *name ); void SV_ClearWorld (void); // called after the world model has been loaded, before linking any entities -void SV_UnlinkEdict (edict_t *ent); +void SV_UnlinkEdict (prvm_edict_t *ent); // call before removing an entity, and before trying to move one, // so it doesn't clip against itself -void SV_LinkEdict (edict_t *ent); +void SV_LinkEdict (prvm_edict_t *ent); // Needs to be called any time an entity changes origin, mins, maxs, // or solid. Automatically unlinks if needed. // sets ent->v.absmin and ent->v.absmax // sets ent->leafnums[] for pvs determination even if the entity // is not solid -int SV_AreaEdicts (vec3_t mins, vec3_t maxs, edict_t **list, int maxcount, int areatype); +int SV_AreaEdicts (vec3_t mins, vec3_t maxs, prvm_edict_t **list, int maxcount, int areatype); // fills in a table of edict pointers with edicts that have bounding boxes // that intersect the given area. It is possible for a non-axial bmodel // to be returned that doesn't actually intersect the area on an exact @@ -337,7 +360,10 @@ int SV_PointContents (vec3_t p); // Quake 2 extends this to also check entities, to allow moving liquids -trace_t SV_Trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t *passedict, int contentmask); +trace_t SV_Trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, prvm_edict_t *passedict, int contentmask); +trace_t SV_TraceToss (prvm_edict_t *tossent, prvm_edict_t *ignore); +trace_t SV_ClipMoveToEntity(prvm_edict_t *ent, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int contentsmask); + // mins and maxs are relative // if the entire move stays in a solid volume, trace.allsolid will be set, @@ -346,4 +372,6 @@ trace_t SV_Trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t *p // if the starting point is in a solid, it will be allowed to move out // to an open area -// passedict is explicitly excluded from clipping checks (normally NULL) \ No newline at end of file +// passedict is explicitly excluded from clipping checks (normally NULL) + +#endif//SERVER_H \ No newline at end of file diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 1597d624..037691ad 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -195,15 +195,15 @@ void SV_GameMap_f (void) savedInuse = Z_Malloc(maxclients->value * sizeof(bool)); for (i = 0, cl = svs.clients; i < maxclients->value; i++, cl++) { - savedInuse[i] = cl->edict->inuse; - cl->edict->inuse = false; + savedInuse[i] = cl->edict->priv.sv->free; + cl->edict->priv.sv->free = false; } SV_WriteSaveFile( "save0" ); //autosave // we must restore these for clients to transfer over correctly for (i = 0, cl = svs.clients; i < maxclients->value; i++, cl++) - cl->edict->inuse = savedInuse[i]; + cl->edict->priv.sv->free = savedInuse[i]; Z_Free (savedInuse); } } @@ -225,23 +225,28 @@ For development work */ void SV_Map_f (void) { - char *map; - char expanded[MAX_QPATH]; + char level_path[MAX_QPATH]; - // if not a pcx, demo, or cinematic, check to make sure the level exists - map = Cmd_Argv(1); - if (!strstr (map, ".")) + sprintf(level_path, "maps/%s", Cmd_Argv(1)); + FS_DefaultExtension(level_path, ".bsp" ); + + if (FS_FileExists(level_path)) { - sprintf (expanded, "maps/%s.bsp", map); - if (!FS_LoadFile (expanded, NULL)) - { - Msg ("Can't find %s\n", expanded); - return; - } - } + sv.state = ss_dead; // don't save current level when changing - sv.state = ss_dead; // don't save current level when changing - SV_GameMap_f (); + SV_InitGame (); + Cvar_Set ("nextserver", ""); //reset demoloop + + SCR_BeginLoadingPlaque (); // for local system + SV_BroadcastCommand ("changing\n"); + SV_SendClientMessages (); + SV_SpawnServer (level_path, NULL, NULL, ss_game, false, false); + Cbuf_CopyToDefer (); + + SV_BroadcastCommand ("reconnect\n"); + strncpy (svs.mapcmd, Cmd_Argv(1), sizeof(svs.mapcmd) - 1); // archive server state + } + else Msg ("Can't loading %s\n", level_path); } /* @@ -309,7 +314,7 @@ void SV_Savegame_f (void) return; } - if (maxclients->value == 1 && svs.clients[0].edict->client->ps.stats[STAT_HEALTH] <= 0) + if (maxclients->value == 1 && svs.clients[0].edict->priv.sv->client->stats[STAT_HEALTH] <= 0) { Msg ("\nCan't savegame while dead!\n"); return; @@ -381,7 +386,7 @@ void SV_Status_f (void) { if (!cl->state) continue; Msg ("%3i ", i); - Msg ("%5i ", cl->edict->client->ps.stats[STAT_FRAGS]); + Msg ("%5i ", cl->edict->priv.sv->client->stats[STAT_FRAGS]); if (cl->state == cs_connected) Msg ("CNCT "); @@ -617,23 +622,6 @@ void SV_KillServer_f (void) NET_Config ( false ); // close network sockets } -/* -=============== -SV_ServerCommand_f - -Let the game dll handle a command -=============== -*/ -void SV_ServerCommand_f (void) -{ - if (!ge) - { - Msg ("No game loaded.\n"); - return; - } - ge->ServerCommand(); -} - //=========================================================== /* @@ -661,6 +649,5 @@ void SV_InitOperatorCommands (void) Cmd_AddCommand ("save", SV_Savegame_f); Cmd_AddCommand ("load", SV_Loadgame_f); Cmd_AddCommand ("killserver", SV_KillServer_f); - Cmd_AddCommand ("sv", SV_ServerCommand_f); } diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index 7780d0b3..dd8c32a2 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -34,11 +34,11 @@ Encode a client frame onto the network channel // because there can be a lot of projectiles, there is a special // network protocol for them #define MAX_PROJECTILES 64 -edict_t *projectiles[MAX_PROJECTILES]; +prvm_edict_t *projectiles[MAX_PROJECTILES]; int numprojs; cvar_t *sv_projectiles; -bool SV_AddProjectileUpdate (edict_t *ent) +bool SV_AddProjectileUpdate (prvm_edict_t *ent) { if (!sv_projectiles) sv_projectiles = Cvar_Get("sv_projectiles", "1", 0); @@ -59,7 +59,7 @@ void SV_EmitProjectileUpdate (sizebuf_t *msg) { byte bits[16]; // [modelindex] [48 bits] xyz p y 12 12 12 8 8 [entitynum] [e2] int n, i; - edict_t *ent; + prvm_edict_t *ent; int x, y, z, p, yaw; int len; @@ -157,7 +157,8 @@ void SV_EmitPacketEntities (client_frame_t *from, client_frame_t *to, sizebuf_t } if (newnum == oldnum) - { // delta update from old position + { + // delta update from old position // because the force parm is false, this will not result // in any bytes being emited if the entity has not changed at all // note that players are always 'newentities', this updates their oldorigin always @@ -169,17 +170,18 @@ void SV_EmitPacketEntities (client_frame_t *from, client_frame_t *to, sizebuf_t } if (newnum < oldnum) - { // this is a new entity, send it from the baseline + { + // this is a new entity, send it from the baseline MSG_WriteDeltaEntity (&sv.baselines[newnum], newent, msg, true, true); newindex++; continue; } if (newnum > oldnum) - { // the old entity isn't present in the new message + { + // the old entity isn't present in the new message bits = U_REMOVE; - if (oldnum >= 256) - bits |= U_NUMBER16 | U_MOREBITS1; + if (oldnum >= 256) bits |= U_NUMBER16 | U_MOREBITS1; MSG_WriteByte (msg, bits&255 ); if (bits & 0x0000ff00) @@ -199,11 +201,6 @@ void SV_EmitPacketEntities (client_frame_t *from, client_frame_t *to, sizebuf_t } MSG_WriteShort (msg, 0); // end of packetentities - -#if 0 - if (numprojs) - SV_EmitProjectileUpdate(msg); -#endif } @@ -231,86 +228,32 @@ void SV_WritePlayerstateToClient (client_frame_t *from, client_frame_t *to, size else ops = &from->ps; // determine what needs to be sent - if (ps->pmove.pm_type != ops->pmove.pm_type) - pflags |= PS_M_TYPE; - - if (ps->pmove.origin[0] != ops->pmove.origin[0] - || ps->pmove.origin[1] != ops->pmove.origin[1] - || ps->pmove.origin[2] != ops->pmove.origin[2] ) - pflags |= PS_M_ORIGIN; - - if (ps->pmove.velocity[0] != ops->pmove.velocity[0] - || ps->pmove.velocity[1] != ops->pmove.velocity[1] - || ps->pmove.velocity[2] != ops->pmove.velocity[2] ) - pflags |= PS_M_VELOCITY; - - if (ps->pmove.pm_time != ops->pmove.pm_time) - pflags |= PS_M_TIME; - - if (ps->pmove.pm_flags != ops->pmove.pm_flags) - pflags |= PS_M_FLAGS; - - if (ps->pmove.gravity != ops->pmove.gravity) - pflags |= PS_M_GRAVITY; - - if (ps->pmove.delta_angles[0] != ops->pmove.delta_angles[0] - || ps->pmove.delta_angles[1] != ops->pmove.delta_angles[1] - || ps->pmove.delta_angles[2] != ops->pmove.delta_angles[2] ) - pflags |= PS_M_DELTA_ANGLES; - - - if (ps->viewoffset[0] != ops->viewoffset[0] - || ps->viewoffset[1] != ops->viewoffset[1] - || ps->viewoffset[2] != ops->viewoffset[2] ) - pflags |= PS_VIEWOFFSET; - - if (ps->viewangles[0] != ops->viewangles[0] - || ps->viewangles[1] != ops->viewangles[1] - || ps->viewangles[2] != ops->viewangles[2] ) - pflags |= PS_VIEWANGLES; - - if (ps->kick_angles[0] != ops->kick_angles[0] - || ps->kick_angles[1] != ops->kick_angles[1] - || ps->kick_angles[2] != ops->kick_angles[2] ) - pflags |= PS_KICKANGLES; - - if (ps->blend[0] != ops->blend[0] - || ps->blend[1] != ops->blend[1] - || ps->blend[2] != ops->blend[2] - || ps->blend[3] != ops->blend[3] ) - pflags |= PS_BLEND; - - if (ps->fov != ops->fov) - pflags |= PS_FOV; - - if (ps->rdflags != ops->rdflags) - pflags |= PS_RDFLAGS; - - if (ps->gunframe != ops->gunframe) - pflags |= PS_WEAPONFRAME; - - if (ps->sequence != ops->sequence) - pflags |= PS_WEAPONSEQUENCE; - - if (ps->gunbody != ops->gunbody) - pflags |= PS_WEAPONBODY; - - if (ps->gunskin != ops->gunskin) - pflags |= PS_WEAPONSKIN; + if (ps->pmove.pm_type != ops->pmove.pm_type) pflags |= PS_M_TYPE; + if (!VectorICompare(ps->pmove.origin, ops->pmove.origin)) pflags |= PS_M_ORIGIN; + if (!VectorICompare(ps->pmove.velocity, ops->pmove.velocity)) pflags |= PS_M_VELOCITY; + if (ps->pmove.pm_time != ops->pmove.pm_time) pflags |= PS_M_TIME; + if (ps->pmove.pm_flags != ops->pmove.pm_flags) pflags |= PS_M_FLAGS; + if (ps->pmove.gravity != ops->pmove.gravity) pflags |= PS_M_GRAVITY; + if (!VectorICompare(ps->pmove.delta_angles, ops->pmove.delta_angles)) pflags |= PS_M_DELTA_ANGLES; + if (!VectorCompare(ps->viewoffset, ops->viewoffset)) pflags |= PS_VIEWOFFSET; + if (!VectorCompare(ps->viewangles, ops->viewangles)) pflags |= PS_VIEWANGLES; + if (!VectorCompare(ps->kick_angles, ops->kick_angles)) pflags |= PS_KICKANGLES; + if (!VectorCompare(ps->blend, ops->blend)) pflags |= PS_BLEND; + if (ps->fov != ops->fov) pflags |= PS_FOV; + if (ps->rdflags != ops->rdflags) pflags |= PS_RDFLAGS; + if (ps->gunframe != ops->gunframe) pflags |= PS_WEAPONFRAME; + if (ps->sequence != ops->sequence) pflags |= PS_WEAPONSEQUENCE; + if (ps->gunbody != ops->gunbody) pflags |= PS_WEAPONBODY; + if (ps->gunskin != ops->gunskin) pflags |= PS_WEAPONSKIN; pflags |= PS_WEAPONINDEX; - // // write it - // MSG_WriteByte (msg, svc_playerinfo); MSG_WriteLong (msg, pflags); - // // write the pmove_state_t - // - if (pflags & PS_M_TYPE) - MSG_WriteByte (msg, ps->pmove.pm_type); + if (pflags & PS_M_TYPE) MSG_WriteByte (msg, ps->pmove.pm_type); if (pflags & PS_M_ORIGIN) { @@ -326,14 +269,9 @@ void SV_WritePlayerstateToClient (client_frame_t *from, client_frame_t *to, size MSG_WriteShort (msg, ps->pmove.velocity[2]); } - if (pflags & PS_M_TIME) - MSG_WriteByte (msg, ps->pmove.pm_time); - - if (pflags & PS_M_FLAGS) - MSG_WriteByte (msg, ps->pmove.pm_flags); - - if (pflags & PS_M_GRAVITY) - MSG_WriteShort (msg, ps->pmove.gravity); + if (pflags & PS_M_TIME) MSG_WriteByte (msg, ps->pmove.pm_time); + if (pflags & PS_M_FLAGS) MSG_WriteByte (msg, ps->pmove.pm_flags); + if (pflags & PS_M_GRAVITY) MSG_WriteShort (msg, ps->pmove.gravity); if (pflags & PS_M_DELTA_ANGLES) { @@ -342,9 +280,7 @@ void SV_WritePlayerstateToClient (client_frame_t *from, client_frame_t *to, size MSG_WriteShort (msg, ps->pmove.delta_angles[2]); } - // // write the rest of the player_state_t - // if (pflags & PS_VIEWOFFSET) { MSG_WriteChar (msg, ps->viewoffset[0] * 4); @@ -366,11 +302,7 @@ void SV_WritePlayerstateToClient (client_frame_t *from, client_frame_t *to, size MSG_WriteChar (msg, ps->kick_angles[2] * 4); } - if (pflags & PS_WEAPONINDEX) - { - MSG_WriteByte (msg, ps->gunindex); - } - + if (pflags & PS_WEAPONINDEX) MSG_WriteByte (msg, ps->gunindex); if (pflags & PS_WEAPONFRAME) { MSG_WriteByte (msg, ps->gunframe); @@ -382,42 +314,35 @@ void SV_WritePlayerstateToClient (client_frame_t *from, client_frame_t *to, size MSG_WriteChar (msg, ps->gunangles[2]*4); } - if (pflags & PS_WEAPONSEQUENCE) - { - MSG_WriteByte (msg, ps->sequence); - } - - if (pflags & PS_WEAPONBODY) - { - MSG_WriteByte (msg, ps->gunbody); - } - - if (pflags & PS_WEAPONSKIN) - { - MSG_WriteByte (msg, ps->gunskin); - } + if (pflags & PS_WEAPONSEQUENCE) MSG_WriteByte (msg, ps->sequence); + if (pflags & PS_WEAPONBODY) MSG_WriteByte (msg, ps->gunbody); + if (pflags & PS_WEAPONSKIN) MSG_WriteByte (msg, ps->gunskin); if (pflags & PS_BLEND) { - MSG_WriteByte (msg, ps->blend[0]*255); - MSG_WriteByte (msg, ps->blend[1]*255); - MSG_WriteByte (msg, ps->blend[2]*255); - MSG_WriteByte (msg, ps->blend[3]*255); + MSG_WriteByte (msg, ps->blend[0] * 255); + MSG_WriteByte (msg, ps->blend[1] * 255); + MSG_WriteByte (msg, ps->blend[2] * 255); + MSG_WriteByte (msg, ps->blend[3] * 255); } - if (pflags & PS_FOV) - MSG_WriteByte (msg, ps->fov); - if (pflags & PS_RDFLAGS) - MSG_WriteByte (msg, ps->rdflags); + if (pflags & PS_FOV) MSG_WriteByte (msg, ps->fov); + if (pflags & PS_RDFLAGS) MSG_WriteByte (msg, ps->rdflags); // send stats statbits = 0; - for (i=0 ; istats[i] != ops->stats[i]) statbits |= 1<stats[i]); + } } @@ -436,18 +361,21 @@ void SV_WriteFrameToClient (client_t *client, sizebuf_t *msg) frame = &client->frames[sv.framenum & UPDATE_MASK]; if (client->lastframe <= 0) - { // client is asking for a retransmit + { + // client is asking for a retransmit oldframe = NULL; lastframe = -1; } else if (sv.framenum - client->lastframe >= (UPDATE_BACKUP - 3) ) - { // client hasn't gotten a good message through in a long time + { + // client hasn't gotten a good message through in a long time // Msg ("%s: Delta request from out-of-date packet.\n", client->name); oldframe = NULL; lastframe = -1; } else - { // we have a valid message to delta from + { + // we have a valid message to delta from oldframe = &client->frames[client->lastframe & UPDATE_MASK]; lastframe = client->lastframe; } @@ -537,29 +465,45 @@ copies off the playerstat and areabits. void SV_BuildClientFrame (client_t *client) { int e, i; - vec3_t org; - edict_t *ent; - edict_t *clent; + vec3_t org; + prvm_edict_t *ent; + prvm_edict_t *clent; client_frame_t *frame; entity_state_t *state; + player_state_t local_state; int l; int clientarea, clientcluster; int leafnum; int c_fullsend; - byte *clientphs; - byte *bitvector; + byte *clientphs; + byte *bitvector; clent = client->edict; - if (!clent->client) return;// not in game yet - + if (!clent->priv.sv->client) return;// not in game yet + + memset(&local_state, 0, sizeof(player_state_t)); + + // copy player state from prvm fields + VectorSet(local_state.viewoffset, 0, 0, -72 ); + local_state.fov = 90; + local_state.pmove.pm_type = PM_SPECTATOR; + local_state.pmove.pm_time = 14; + local_state.pmove.gravity = 800; + + for( i = 0; i < 3; i++) + local_state.pmove.origin[i] = (short)clent->fields.sv->origin[i]; + for( i = 0; i < 3; i++) + local_state.pmove.velocity[i] = (short)clent->fields.sv->velocity[i]; + + clent->priv.sv->client = &local_state; // this is the frame we are creating frame = &client->frames[sv.framenum & UPDATE_MASK]; frame->senttime = svs.realtime; // save it for ping calc later // find the client's PVS - for (i=0 ; i<3 ; i++) - org[i] = clent->client->ps.pmove.origin[i]*0.125 + clent->client->ps.viewoffset[i]; + for (i = 0; i < 3; i++) + org[i] = clent->priv.sv->client->pmove.origin[i]*0.125 + clent->priv.sv->client->viewoffset[i]; leafnum = CM_PointLeafnum (org); clientarea = CM_LeafArea (leafnum); @@ -569,7 +513,7 @@ void SV_BuildClientFrame (client_t *client) frame->areabytes = CM_WriteAreaBits (frame->areabits, clientarea); // grab the current player_state_t - frame->ps = clent->client->ps; + frame->ps = *clent->priv.sv->client; SV_FatPVS (org); @@ -581,34 +525,33 @@ void SV_BuildClientFrame (client_t *client) c_fullsend = 0; - for (e = 1; e < ge->num_edicts ; e++) + for (e = 1; e < prog->num_edicts ; e++) { - ent = EDICT_NUM(e); + ent = PRVM_EDICT_NUM(e); // ignore ents without visible models - if (ent->svflags & SVF_NOCLIENT) + if (ent->priv.sv->flags & SVF_NOCLIENT) continue; // ignore ents without visible models unless they have an effect - if (!ent->s.modelindex && !ent->s.effects && !ent->s.sound && !ent->s.event) + if (!ent->priv.sv->state.modelindex && !ent->priv.sv->state.effects && !ent->priv.sv->state.sound && !ent->priv.sv->state.event) continue; // ignore if not touching a PV leaf if (ent != clent) { // check area - if (!CM_AreasConnected (clientarea, ent->areanum)) + if (!CM_AreasConnected (clientarea, ent->priv.sv->areanum)) { // doors can legally straddle two areas, so // we may need to check another one - if (!ent->areanum2 - || !CM_AreasConnected (clientarea, ent->areanum2)) - continue; // blocked by a door + if (!ent->priv.sv->areanum2 || !CM_AreasConnected (clientarea, ent->priv.sv->areanum2)) + continue; // blocked by a door } // beams just check one point for PHS - if (ent->s.renderfx & RF_BEAM) + if (ent->priv.sv->state.renderfx & RF_BEAM) { - l = ent->clusternums[0]; + l = ent->priv.sv->clusternums[0]; if ( !(clientphs[l >> 3] & (1 << (l&7) )) ) continue; } @@ -616,37 +559,37 @@ void SV_BuildClientFrame (client_t *client) { // FIXME: if an ent has a model and a sound, but isn't // in the PVS, only the PHS, clear the model - if (ent->s.sound) + if (ent->priv.sv->state.sound) { bitvector = fatpvs; //clientphs; } else bitvector = fatpvs; - if (ent->num_clusters == -1) + if (ent->priv.sv->num_clusters == -1) { // too many leafs for individual check, go by headnode - if (!CM_HeadnodeVisible (ent->headnode, bitvector)) + if (!CM_HeadnodeVisible (ent->priv.sv->headnode, bitvector)) continue; c_fullsend++; } else { // check individual leafs - for (i=0 ; i < ent->num_clusters ; i++) + for (i=0 ; i < ent->priv.sv->num_clusters ; i++) { - l = ent->clusternums[i]; + l = ent->priv.sv->clusternums[i]; if (bitvector[l >> 3] & (1 << (l&7) )) break; } - if (i == ent->num_clusters) + if (i == ent->priv.sv->num_clusters) continue; // not visible } - if (!ent->s.modelindex) + if (!ent->priv.sv->state.modelindex) { // don't send sounds if they will be attenuated away vec3_t delta; float len; - VectorSubtract (org, ent->s.origin, delta); + VectorSubtract (org, ent->priv.sv->state.origin, delta); len = VectorLength (delta); if (len > 400) continue; @@ -656,15 +599,15 @@ void SV_BuildClientFrame (client_t *client) // add it to the circular client_entities array state = &svs.client_entities[svs.next_client_entities % svs.num_client_entities]; - if (ent->s.number != e) + if (ent->priv.sv->state.number != e) { - MsgWarn ("SV_BuildClientFrame: invalid ent->s.number %d\n", ent->s.number ); - ent->s.number = e; // ptr to current entity such as entnumber + MsgWarn ("SV_BuildClientFrame: invalid ent->priv.sv->state.number %d\n", ent->priv.sv->state.number ); + ent->priv.sv->state.number = e; // ptr to current entity such as entnumber } - *state = ent->s; + *state = ent->priv.sv->state; // don't mark players missiles as solid - if (ent->owner == client->edict) state->solid = 0; + if (ent->priv.sv->owner == client->edict->priv.sv) state->solid = 0; svs.next_client_entities++; frame->num_entities++; @@ -683,7 +626,7 @@ Used for recording footage for merged or assembled demos void SV_RecordDemoMessage (void) { int e; - edict_t *ent; + prvm_edict_t *ent; entity_state_t nostate; sizebuf_t buf; byte buf_data[32768]; @@ -702,18 +645,14 @@ void SV_RecordDemoMessage (void) MSG_WriteByte (&buf, svc_packetentities); e = 1; - ent = EDICT_NUM(e); - while (e < ge->num_edicts) + ent = PRVM_EDICT_NUM(e); + while (e < prog->num_edicts) { // ignore ents without visible models unless they have an effect - if (ent->inuse && - ent->s.number && - (ent->s.modelindex || ent->s.effects || ent->s.sound || ent->s.event) && - !(ent->svflags & SVF_NOCLIENT)) - MSG_WriteDeltaEntity (&nostate, &ent->s, &buf, false, true); - + if (ent->priv.sv->free && ent->priv.sv->state.number && (ent->priv.sv->state.modelindex || ent->priv.sv->state.effects || ent->priv.sv->state.sound || ent->priv.sv->state.event) && !(ent->priv.sv->flags & SVF_NOCLIENT)) + MSG_WriteDeltaEntity (&nostate, &ent->priv.sv->state, &buf, false, true); e++; - ent = EDICT_NUM(e); + ent = PRVM_EDICT_NUM(e); } MSG_WriteShort (&buf, 0); // end of packetentities diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index 29248dc2..b14e37bb 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -19,108 +19,103 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sv_game.c -- interface to the game dll -#include #include "engine.h" #include "server.h" -game_export_t *ge; -HINSTANCE sv_library; - /* -=============== -PF_dprintf +============== +PF_makevectors -Debug print to server console -=============== +Writes new values for v_forward, v_up, and v_right based on angles +makevectors(vector) +============== */ -void PF_dprintf (char *fmt, ...) +void PF_makevectors (void) { - char msg[1024]; - va_list argptr; - - va_start (argptr,fmt); - vsprintf (msg, fmt, argptr); - va_end (argptr); - - Msg ("%s", msg); + AngleVectorsRight(PRVM_G_VECTOR(OFS_PARM0), prog->globals.server->v_forward, prog->globals.server->v_right, prog->globals.server->v_up); } +void PF_makevectors2 (void) +{ + AngleVectorsLeft(PRVM_G_VECTOR(OFS_PARM0), prog->globals.server->v_forward, prog->globals.server->v_right, prog->globals.server->v_up); +} /* -=============== -PF_cprintf +================= +PF_setorigin -Print to a single client -=============== +This is the only valid way to move an object without using the physics of the world (setting velocity and waiting). Directly changing origin will not set internal links correctly, so clipping would be messed up. This should be called when an object is spawned, and then only if it is teleported. + +setorigin (entity, origin) +================= */ -void PF_cprintf (edict_t *ent, int level, char *fmt, ...) +void PF_setorigin (void) { - char msg[1024]; - va_list argptr; - int n; + prvm_edict_t *e; + float *org; - if (ent) + e = PRVM_G_EDICT(OFS_PARM0); + if (e == prog->edicts) { - n = NUM_FOR_EDICT(ent); - if (n < 1 || n > maxclients->value) - Com_Error (ERR_DROP, "cprintf to a non-client"); + VM_Warning("setorigin: can not modify world entity\n"); + return; } - - va_start (argptr,fmt); - vsprintf (msg, fmt, argptr); - va_end (argptr); - - if (ent) - SV_ClientPrintf (svs.clients+(n-1), level, "%s", msg); - else - Msg ("%s", msg); + if (e->priv.sv->free) + { + VM_Warning("setorigin: can not modify free entity\n"); + return; + } + org = PRVM_G_VECTOR(OFS_PARM1); + VectorCopy (org, e->fields.sv->origin); + SV_LinkEdict (e); } -/* -=============== -PF_centerprintf - -centerprint to a single client -=============== -*/ -void PF_centerprintf (edict_t *ent, char *fmt, ...) +void SetMinMaxSize (prvm_edict_t *e, float *min, float *max, bool rotate) { - char msg[1024]; - va_list argptr; - int n; - - n = NUM_FOR_EDICT(ent); - if (n < 1 || n > maxclients->value) - return; // Com_Error (ERR_DROP, "centerprintf to a non-client"); + int i; - va_start (argptr,fmt); - vsprintf (msg, fmt, argptr); - va_end (argptr); + for (i = 0; i < 3; i++) + if (min[i] > max[i]) + PRVM_ERROR("SetMinMaxSize: backwards mins/maxs"); - MSG_Begin( svc_centerprint ); - MSG_WriteString (&sv.multicast, msg); - MSG_Send(MSG_ONE_R, NULL, ent ); + // set derived values + VectorCopy (min, e->fields.sv->mins); + VectorCopy (max, e->fields.sv->maxs); + VectorSubtract (max, min, e->fields.sv->size); + + SV_LinkEdict (e); } - /* -=============== -PF_error +================= +PF_setsize -Abort the server with a game error -=============== +the size box is rotated by the current angle +LordHavoc: no it isn't... + +setsize (entity, minvector, maxvector) +================= */ -void PF_error (char *fmt, ...) +void PF_setsize (void) { - char msg[1024]; - va_list argptr; - - va_start (argptr,fmt); - vsprintf (msg, fmt, argptr); - va_end (argptr); + prvm_edict_t *e; + float *min, *max; - Com_Error (ERR_DROP, "Game Error: %s", msg); + e = PRVM_G_EDICT(OFS_PARM0); + if (e == prog->edicts) + { + VM_Warning("setsize: can not modify world entity\n"); + return; + } + if (e->priv.sv->free) + { + VM_Warning("setsize: can not modify free entity\n"); + return; + } + min = PRVM_G_VECTOR(OFS_PARM1); + max = PRVM_G_VECTOR(OFS_PARM2); + SetMinMaxSize (e, min, max, false); } @@ -128,103 +123,378 @@ void PF_error (char *fmt, ...) ================= PF_setmodel -Also sets mins and maxs for inline bmodels +setmodel(entity, model) ================= */ -void PF_setmodel (edict_t *ent, char *name) +void PF_setmodel( void ) +{ + prvm_edict_t *e; + cmodel_t *mod; + int i; + + e = PRVM_G_EDICT(OFS_PARM0); + if (e == prog->edicts) + { + VM_Warning("setmodel: can not modify world entity\n"); + return; + } + if (e->priv.sv->free) + { + VM_Warning("setmodel: can not modify free entity\n"); + return; + } + + i = SV_ModelIndex(PRVM_G_STRING(OFS_PARM1)); + e->fields.sv->model = PRVM_SetEngineString(sv.configstrings[CS_MODELS+i]); + e->fields.sv->modelindex = i; + + mod = CM_LoadModel(sv.configstrings[CS_MODELS+i]); + if(mod) SetMinMaxSize( e, mod->mins, mod->maxs, false ); +} + +/* +================= +PF_sprint + +single print to a specific client + +sprint(clientent, value) +================= +*/ +void PF_sprint (void) +{ + client_t *client; + int num; + char string[VM_STRINGTEMP_LENGTH]; + + num = PRVM_G_EDICTNUM(OFS_PARM0); + + if (num < 1 || num > maxclients->value || svs.clients[num - 1].state != cs_spawned) + { + VM_Warning("tried to centerprint to a non-client\n"); + return; + } + + client = svs.clients + num-1; + + VM_VarString(1, string, sizeof(string)); + SV_ClientPrintf (client, PRINT_HIGH, "%s", string ); +} + + +/* +================= +PF_centerprint + +single print to a specific client + +centerprint(clientent, value) +================= +*/ +void PF_centerprint (void) +{ + client_t *client; + int num; + char string[VM_STRINGTEMP_LENGTH]; + + num = PRVM_G_EDICTNUM(OFS_PARM0); + + if(num < 1 || num > maxclients->value || svs.clients[num-1].state != cs_spawned) + { + VM_Warning("tried to centerprint to a non-client\n"); + return; + } + + client = svs.clients + num - 1; + + VM_VarString(1, string, sizeof(string)); + MSG_Begin( svc_centerprint ); + MSG_WriteString (&sv.multicast, string ); + MSG_Send(MSG_ONE_R, NULL, client->edict ); +} + +/* +================= +PF_particle + +particle(origin, color, count) +================= +*/ +void PF_particle (void) +{ + float *org, *dir; + float color; + float count; + + org = PRVM_G_VECTOR(OFS_PARM0); + dir = PRVM_G_VECTOR(OFS_PARM1); + color = PRVM_G_FLOAT(OFS_PARM2); + count = PRVM_G_FLOAT(OFS_PARM3); + +//SV_StartParticle (org, dir, (int)color, (int)count); +} + + +/* +================= +PF_ambientsound + +================= +*/ +void PF_ambientsound (void) +{ + const char *samp; + float *pos; + float vol, attenuation; + int soundnum, large; + + pos = PRVM_G_VECTOR (OFS_PARM0); + samp = PRVM_G_STRING(OFS_PARM1); + vol = PRVM_G_FLOAT(OFS_PARM2); + attenuation = PRVM_G_FLOAT(OFS_PARM3); + + // check to see if samp was properly precached + soundnum = SV_SoundIndex(samp); + if (!soundnum) return; + + large = false; + if (soundnum >= 256) large = true; + + // unsupported +} + +/* +================= +PF_sound + +Each entity can have eight independant sound sources, like voice, +weapon, feet, etc. + +Channel 0 is an auto-allocate channel, the others override anything +already running on that entity/channel pair. + +An attenuation of 0 will play full volume everywhere in the level. +Larger attenuations will drop off. + +================= +*/ +void PF_sound (void) +{ + const char *sample; + int channel, sound_idx; + prvm_edict_t *entity; + int volume; + float attenuation; + + entity = PRVM_G_EDICT(OFS_PARM0); + channel = (int)PRVM_G_FLOAT(OFS_PARM1); + sample = PRVM_G_STRING(OFS_PARM2); + volume = (int)(PRVM_G_FLOAT(OFS_PARM3) * 255); + attenuation = PRVM_G_FLOAT(OFS_PARM4); + + if (volume < 0 || volume > 255) + { + VM_Warning("SV_StartSound: volume must be in range 0-1\n"); + return; + } + + if (attenuation < 0 || attenuation > 4) + { + VM_Warning("SV_StartSound: attenuation must be in range 0-4\n"); + return; + } + + if (channel < 0 || channel > 7) + { + VM_Warning("SV_StartSound: channel must be in range 0-7\n"); + return; + } + + sound_idx = SV_SoundIndex( sample ); + SV_StartSound (NULL, entity, channel, sound_idx, volume, attenuation, 0 ); +} + +/* +================= +PF_traceline + +Used for use tracing and shot targeting +Traces are blocked by bbox and exact bsp entityes, and also slide box entities +if the tryents flag is set. + +traceline (vector1, vector2, tryents) +================= +*/ +void PF_traceline (void) +{ + float *v1, *v2; + trace_t trace; + int move; + prvm_edict_t *ent; + + prog->xfunction->builtinsprofile += 30; + + v1 = PRVM_G_VECTOR(OFS_PARM0); + v2 = PRVM_G_VECTOR(OFS_PARM1); + move = (int)PRVM_G_FLOAT(OFS_PARM2); + ent = PRVM_G_EDICT(OFS_PARM3); + + if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v1[2]) || IS_NAN(v2[2])) + PRVM_ERROR("%s: NAN errors detected in traceline('%f %f %f', '%f %f %f', %i, entity %i)\n", PRVM_NAME, v1[0], v1[1], v1[2], v2[0], v2[1], v2[2], move, PRVM_EDICT_TO_PROG(ent)); + + trace = SV_Trace (v1, vec3_origin, vec3_origin, v2, ent, MASK_ALL ); + + prog->globals.server->trace_allsolid = trace.allsolid; + prog->globals.server->trace_startsolid = trace.startsolid; + prog->globals.server->trace_fraction = trace.fraction; + prog->globals.server->trace_inwater = (trace.contents & MASK_WATER) ? true : false; + prog->globals.server->trace_inopen = (trace.contents & MASK_SHOT) ? true : false; // this is correct? + + VectorCopy (trace.endpos, prog->globals.server->trace_endpos); + VectorCopy (trace.plane.normal, prog->globals.server->trace_plane_normal); + prog->globals.server->trace_plane_dist = trace.plane.dist; + if (trace.ent) prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace.ent); + else prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts); +} + + +/* +================= +PF_tracebox + +Used for use tracing and shot targeting +Traces are blocked by bbox and exact bsp entityes, and also slide box entities +if the tryents flag is set. + +tracebox (vector1, vector mins, vector maxs, vector2, tryents) +================= +*/ +// LordHavoc: added this for my own use, VERY useful, similar to traceline +void PF_tracebox (void) +{ + float *v1, *v2, *m1, *m2; + trace_t trace; + int move; + prvm_edict_t *ent; + + prog->xfunction->builtinsprofile += 30; + + v1 = PRVM_G_VECTOR(OFS_PARM0); + m1 = PRVM_G_VECTOR(OFS_PARM1); + m2 = PRVM_G_VECTOR(OFS_PARM2); + v2 = PRVM_G_VECTOR(OFS_PARM3); + move = (int)PRVM_G_FLOAT(OFS_PARM4); + ent = PRVM_G_EDICT(OFS_PARM5); + + if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v1[2]) || IS_NAN(v2[2])) + PRVM_ERROR("%s: NAN errors detected in tracebox('%f %f %f', '%f %f %f', '%f %f %f', '%f %f %f', %i, entity %i)\n", PRVM_NAME, v1[0], v1[1], v1[2], m1[0], m1[1], m1[2], m2[0], m2[1], m2[2], v2[0], v2[1], v2[2], move, PRVM_EDICT_TO_PROG(ent)); + + trace = SV_Trace (v1, m1, m2, v2, ent, MASK_ALL ); + + prog->globals.server->trace_allsolid = trace.allsolid; + prog->globals.server->trace_startsolid = trace.startsolid; + prog->globals.server->trace_fraction = trace.fraction; + prog->globals.server->trace_inwater = (trace.contents & MASK_WATER) ? true : false; + prog->globals.server->trace_inopen = (trace.contents & MASK_SHOT) ? true : false; // this is correct? + + VectorCopy (trace.endpos, prog->globals.server->trace_endpos); + VectorCopy (trace.plane.normal, prog->globals.server->trace_plane_normal); + prog->globals.server->trace_plane_dist = trace.plane.dist; + + if (trace.ent) prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace.ent); + else prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts); +} + +void PF_tracetoss (void) +{ + trace_t trace; + prvm_edict_t *ent; + prvm_edict_t *ignore; + + prog->xfunction->builtinsprofile += 600; + + ent = PRVM_G_EDICT(OFS_PARM0); + if (ent == prog->edicts) + { + VM_Warning("tracetoss: can not use world entity\n"); + return; + } + ignore = PRVM_G_EDICT(OFS_PARM1); + + trace = SV_TraceToss (ent, ignore); + + prog->globals.server->trace_allsolid = trace.allsolid; + prog->globals.server->trace_startsolid = trace.startsolid; + prog->globals.server->trace_fraction = trace.fraction; + prog->globals.server->trace_inwater = (trace.contents & MASK_WATER) ? true : false; + prog->globals.server->trace_inopen = (trace.contents & MASK_SHOT) ? true : false; // this is correct? + + VectorCopy (trace.endpos, prog->globals.server->trace_endpos); + VectorCopy (trace.plane.normal, prog->globals.server->trace_plane_normal); + prog->globals.server->trace_plane_dist = trace.plane.dist; + + if (trace.ent) prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace.ent); + else prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts); +} + + +/* +================= +PF_checkpos + +Returns true if the given entity can move to the given position from it's +current position by walking or rolling. +FIXME: make work... +scalar checkpos (entity, vector) +================= +*/ +void PF_checkpos (void) +{ +} + +//============================================================================ +int PF_newcheckclient (int check) { int i; - cmodel_t *mod; - - if (!name) Com_Error (ERR_DROP, "PF_setmodel: NULL"); + prvm_edict_t *ent; + vec3_t org; - i = SV_ModelIndex (name); - -// ent->model = name; - ent->s.modelindex = i; + // cycle to the next one + check = bound(1, check, maxclients->value); + if (check == maxclients->value) i = 1; + else i = check + 1; - mod = CM_LoadModel (name); - - if(mod) // hull setup + for (;; i++) { - VectorCopy (mod->mins, ent->mins); - VectorCopy (mod->maxs, ent->maxs); - SV_LinkEdict (ent); + // count the cost + prog->xfunction->builtinsprofile++; + // wrap around + if (i == maxclients->value + 1) i = 1; + // look up the client's edict + ent = PRVM_EDICT_NUM(i); + // check if it is to be ignored, but never ignore the one we started on (prevent infinite loop) + if (i != check && (ent->priv.sv->free || ent->fields.sv->health <= 0 || ((int)ent->fields.sv->flags & FL_NOTARGET))) + continue; + // found a valid client (possibly the same one again) + break; } -} -/* -=============== -PF_Configstring + // get the PVS for the entity + VectorAdd(ent->fields.sv->origin, ent->fields.sv->view_ofs, org); -=============== -*/ -void PF_Configstring (int index, char *val) -{ - if (index < 0 || index >= MAX_CONFIGSTRINGS) - Com_Error (ERR_DROP, "configstring: bad index %i value %s\n", index, val); + SV_FatPVS( org ); // fat pvs manually - if (!val) val = ""; - - // change the string in sv - strcpy (sv.configstrings[index], val); - - if (sv.state != ss_loading) - { - // send the update to everyone - SZ_Clear (&sv.multicast); - MSG_Begin(svc_configstring); - MSG_WriteShort (&sv.multicast, index); - MSG_WriteString (&sv.multicast, val); - MSG_Send(MSG_ALL_R, vec3_origin, NULL ); - } -} - - - -void PF_Begin( int dest ) { MSG_Begin( dest ); } -void PF_WriteChar (int c) { MSG_WriteChar (&sv.multicast, c); } -void PF_WriteByte (int c) { MSG_WriteByte (&sv.multicast, c); } -void PF_WriteShort (int c) { MSG_WriteShort (&sv.multicast, c); } -void PF_WriteWord (int c) { MSG_WriteWord (&sv.multicast, c); } -void PF_WriteLong (int c) { MSG_WriteLong (&sv.multicast, c); } -void PF_WriteFloat (float f) { MSG_WriteFloat (&sv.multicast, f); } -void PF_WriteString (char *s) { MSG_WriteString (&sv.multicast, s); } -void PF_WriteCoord(vec3_t pos){ MSG_WritePos (&sv.multicast, pos); } -void PF_WriteDir (vec3_t dir) { MSG_WriteDir (&sv.multicast, dir); } -void PF_WriteAngle (float f) { MSG_WriteAngle (&sv.multicast, f); } -void PF_Send( msgtype_t to, vec3_t org, edict_t *ent ) { MSG_Send( to, org, ent ); } - -message_write_t Msg_GetAPI( void ) -{ - static message_write_t Msg; - - Msg.api_size = sizeof(message_write_t); - - Msg.Begin = PF_Begin; - Msg.WriteChar = PF_WriteChar; - Msg.WriteByte = PF_WriteByte; - Msg.WriteWord = PF_WriteWord; - Msg.WriteShort = PF_WriteShort; - Msg.WriteLong = PF_WriteLong; - Msg.WriteFloat = PF_WriteFloat; - Msg.WriteString = PF_WriteString; - Msg.WriteCoord = PF_WriteCoord; - Msg.WriteDir = PF_WriteDir; - Msg.WriteAngle = PF_WriteAngle; - Msg.Send = PF_Send; - - return Msg; + return i; } /* ================= -PF_inPVS +PF_inphs Also checks portalareas so that doors block sight ================= */ -bool PF_inPVS (vec3_t p1, vec3_t p2) +bool PF_inpvs (vec3_t p1, vec3_t p2) { int leafnum; int cluster; @@ -246,15 +516,14 @@ bool PF_inPVS (vec3_t p1, vec3_t p2) return true; } - /* ================= -PF_inPHS +PF_inphs Also checks portalareas so that doors block sound ================= */ -bool PF_inPHS (vec3_t p1, vec3_t p2) +bool PF_inphs (vec3_t p1, vec3_t p2) { int leafnum; int cluster; @@ -277,102 +546,1044 @@ bool PF_inPHS (vec3_t p1, vec3_t p2) return true; } -void PF_StartSound (edict_t *entity, int channel, int sound_num, float volume, float attenuation, float timeofs) +/* +================= +PF_checkclient + +Returns a client (or object that has a client enemy) that would be a +valid target. + +If there is more than one valid option, they are cycled each frame + +If (self.origin + self.viewofs) is not in the PVS of the current target, +it is not returned at all. + +name checkclient () +================= +*/ +int c_invis, c_notvis; +void PF_checkclient (void) { - if (!entity) return; - SV_StartSound (NULL, entity, channel, sound_num, volume, attenuation, timeofs); + prvm_edict_t *ent, *self; + vec3_t view; + + // find a new check if on a new frame + if (sv.time - sv.lastchecktime >= 0.1) + { + sv.lastcheck = PF_newcheckclient (sv.lastcheck); + sv.lastchecktime = sv.time; + } + + // return check if it might be visible + ent = PRVM_EDICT_NUM(sv.lastcheck); + if (ent->priv.sv->free || ent->fields.sv->health <= 0) + { + VM_RETURN_EDICT(prog->edicts); + return; + } + + // if current entity can't possibly see the check entity, return 0 + self = PRVM_PROG_TO_EDICT(prog->globals.server->self); + VectorAdd(self->fields.sv->origin, self->fields.sv->view_ofs, view); + + if (!PF_inpvs(view, view)) + { + c_notvis++; + VM_RETURN_EDICT(prog->edicts); + return; + } + + // might be able to see it + c_invis++; + VM_RETURN_EDICT(ent); } -//============================================== - -/* -=============== -SV_ShutdownGameProgs - -Called when either the entire server is being killed, or -it is changing to a different game directory. -=============== -*/ -void SV_ShutdownGameProgs (void) +//============================================================================ +void SV_ConfigString (int index, const char *val) { - if (!ge) return; - ge->Shutdown (); - Sys_UnloadGame ( sv_library ); - ge = NULL; + if (index < 0 || index >= MAX_CONFIGSTRINGS) + Com_Error (ERR_DROP, "configstring: bad index %i value %s\n", index, val); + + if (!val) val = ""; + + // change the string in sv + strcpy (sv.configstrings[index], val); + + Msg("SV_ConfigString[%i]\"%s\"\n", index, val ); + if (sv.state != ss_loading) + { + // send the update to everyone + SZ_Clear (&sv.multicast); + MSG_Begin(svc_configstring); + MSG_WriteShort (&sv.multicast, index); + MSG_WriteString (&sv.multicast, (char *)val); + MSG_Send(MSG_ALL_R, vec3_origin, NULL ); + } } /* =============== -SV_InitGameProgs +PF_Configstring -Init the game subsystem for a new map =============== */ -void SCR_DebugGraph (float value, int color); - -void SV_InitGameProgs (void) +void PF_Configstring( void ) { - game_import_t import; + SV_ConfigString((int)PRVM_G_FLOAT(OFS_PARM0), PRVM_G_STRING(OFS_PARM1)); +} - // unload anything we have now - if (ge) SV_ShutdownGameProgs (); +/* +================= +PF_stuffcmd - // load a new game dll - import.Fs = pi->Fs; - import.VFs = pi->VFs; - import.Mem = pi->Mem; - import.Script = pi->Script; - import.Compile = pi->Compile; - import.Info = pi->Info; - import.Msg = Msg_GetAPI(); +Sends text over to the client's execution buffer - import.GameInfo = pi->GameInfo; +stuffcmd (clientent, value, ...) +================= +*/ +void PF_stuffcmd (void) +{ + int entnum; + client_t *old; + char string[VM_STRINGTEMP_LENGTH]; - import.bprintf = SV_BroadcastPrintf; - import.dprintf = PF_dprintf; - import.cprintf = PF_cprintf; - import.centerprintf = PF_centerprintf; - import.error = PF_error; + entnum = PRVM_G_EDICTNUM(OFS_PARM0); + if (entnum < 1 || entnum > maxclients->value || svs.clients[entnum-1].state != cs_spawned) + { + VM_Warning("Can't stuffcmd to a non-client\n"); + return; + } - import.linkentity = SV_LinkEdict; - import.unlinkentity = SV_UnlinkEdict; - import.BoxEdicts = SV_AreaEdicts; - import.trace = SV_Trace; - import.pointcontents = SV_PointContents; - import.setmodel = PF_setmodel; - import.inPVS = PF_inPVS; - import.inPHS = PF_inPHS; - import.Pmove = Pmove; + VM_VarString(1, string, sizeof(string)); - import.getmodelhdr = SV_GetModelPtr; + old = sv_client; + sv_client = svs.clients + entnum - 1; + SV_BroadcastCommand("%s", string); + sv_client = old; +} - import.modelindex = SV_ModelIndex; - import.soundindex = SV_SoundIndex; - import.imageindex = SV_ImageIndex; +/* +================= +PF_findradius - import.configstring = PF_Configstring; - import.sound = PF_StartSound; - import.positioned_sound = SV_StartSound; +Returns a chain of entities that have origins within a spherical area - import.cvar = Cvar_Get; - import.cvar_set = Cvar_Set; - import.cvar_forceset = Cvar_ForceSet; +findradius (origin, radius) +================= +*/ +void PF_findradius (void) +{ + prvm_edict_t *ent, *chain; + vec_t radius, radius2; + vec3_t org, eorg, mins, maxs; + int i; + int numtouchedicts; + prvm_edict_t *touchedicts[MAX_EDICTS]; - import.argc = Cmd_Argc; - import.argv = Cmd_Argv; - import.args = Cmd_Args; - import.AddCommandString = Cbuf_AddText; + chain = (prvm_edict_t *)prog->edicts; - import.DebugGraph = SCR_DebugGraph; - import.SetAreaPortalState = CM_SetAreaPortalState; - import.AreasConnected = CM_AreasConnected; + VectorCopy(PRVM_G_VECTOR(OFS_PARM0), org); + radius = PRVM_G_FLOAT(OFS_PARM1); + radius2 = radius * radius; - //find server.dll - ge = (game_export_t *)Sys_LoadGame("ServerAPI", sv_library, &import); + mins[0] = org[0] - (radius + 1); + mins[1] = org[1] - (radius + 1); + mins[2] = org[2] - (radius + 1); + maxs[0] = org[0] + (radius + 1); + maxs[1] = org[1] + (radius + 1); + maxs[2] = org[2] + (radius + 1); + + numtouchedicts = 0;//SV_EntitiesInBox(mins, maxs, MAX_EDICTS, touchedicts); + + if (numtouchedicts > MAX_EDICTS) + { + // this never happens + MsgWarn("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS); + numtouchedicts = MAX_EDICTS; + } + for (i = 0; i < numtouchedicts; i++) + { + ent = touchedicts[i]; + prog->xfunction->builtinsprofile++; + + if (ent->fields.sv->solid == SOLID_NOT) continue; + VectorSubtract(org, ent->fields.sv->origin, eorg); + VectorMAMAM(1, eorg, 0.5f, ent->fields.sv->mins, 0.5f, ent->fields.sv->maxs, eorg); + if (DotProduct(eorg, eorg) < radius2) + { + ent->fields.sv->chain = PRVM_EDICT_TO_PROG(chain); + chain = ent; + } + } + + VM_RETURN_EDICT(chain); +} + +void PF_precache_file (void) +{ + // precache_file is only used to copy files with qcc, it does nothing + PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0); +} + + +void PF_precache_sound (void) +{ + SV_SoundIndex(PRVM_G_STRING(OFS_PARM0)); + PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0); +} + +void PF_precache_model (void) +{ + SV_ModelIndex(PRVM_G_STRING(OFS_PARM0)); + PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0); +} + +/* +=============== +PF_walkmove + +float(float yaw, float dist) walkmove +=============== +*/ +void PF_walkmove (void) +{ + prvm_edict_t *ent; + float yaw, dist; + vec3_t move; + mfunction_t *oldf; + int oldself; + + // assume failure if it returns early + PRVM_G_FLOAT(OFS_RETURN) = 0; + + ent = PRVM_PROG_TO_EDICT(prog->globals.server->self); + if (ent == prog->edicts) + { + VM_Warning("walkmove: can not modify world entity\n"); + return; + } + if (ent->priv.sv->free) + { + VM_Warning("walkmove: can not modify free entity\n"); + return; + } + + yaw = PRVM_G_FLOAT(OFS_PARM0); + dist = PRVM_G_FLOAT(OFS_PARM1); + + if (!((int)ent->fields.sv->flags & (FL_ONGROUND|FL_FLY|FL_SWIM))) + return; + + yaw = yaw * M_PI * 2 / 360; + + move[0] = cos(yaw)*dist; + move[1] = sin(yaw)*dist; + move[2] = 0; + + // save program state, because SV_movestep may call other progs + oldf = prog->xfunction; + oldself = prog->globals.server->self; + + PRVM_G_FLOAT(OFS_RETURN) = 0;//SV_movestep(ent, move, true); + + // restore program state + prog->xfunction = oldf; + prog->globals.server->self = oldself; +} + +/* +=============== +PF_droptofloor + +void() droptofloor +=============== +*/ +void PF_droptofloor (void) +{ + prvm_edict_t *ent; + vec3_t end; + trace_t trace; + + // assume failure if it returns early + PRVM_G_FLOAT(OFS_RETURN) = 0; + + ent = PRVM_PROG_TO_EDICT(prog->globals.server->self); + if (ent == prog->edicts) + { + VM_Warning("droptofloor: can not modify world entity\n"); + return; + } + if (ent->priv.sv->free) + { + VM_Warning("droptofloor: can not modify free entity\n"); + return; + } + + VectorCopy (ent->fields.sv->origin, end); + end[2] -= 256; + + trace = SV_Trace(ent->fields.sv->origin, ent->fields.sv->mins, ent->fields.sv->maxs, end, ent, MASK_ALL ); + + if (trace.fraction != 1) + { + VectorCopy (trace.endpos, ent->fields.sv->origin); + SV_LinkEdict (ent); + ent->fields.sv->flags = (int)ent->fields.sv->flags | FL_ONGROUND; + ent->fields.sv->groundentity = PRVM_EDICT_TO_PROG(trace.ent); + PRVM_G_FLOAT(OFS_RETURN) = 1; + } +} + +/* +=============== +PF_lightstyle + +void(float style, string value) lightstyle +=============== +*/ +void PF_lightstyle (void) +{ + int style; + const char *val; + + style = (int)PRVM_G_FLOAT(OFS_PARM0); + val = PRVM_G_STRING(OFS_PARM1); + + if( (uint) style >= MAX_LIGHTSTYLES ) + { + PRVM_ERROR( "PF_lightstyle: style: %i >= 64", style ); + } + + SV_ConfigString (CS_LIGHTS + style, val ); +} + +/* +============= +PF_checkbottom +============= +*/ +void PF_checkbottom (void) +{ + PRVM_G_FLOAT(OFS_RETURN) = 0;//SV_CheckBottom (PRVM_G_EDICT(OFS_PARM0)); +} + +/* +============= +PF_pointcontents +============= +*/ +void PF_pointcontents (void) +{ + PRVM_G_FLOAT(OFS_RETURN) = SV_PointContents(PRVM_G_VECTOR(OFS_PARM0)); +} + +/* +============= +PF_aim + +Pick a vector for the player to shoot along +vector aim(entity, missilespeed) +============= +*/ +void PF_aim (void) +{ + prvm_edict_t *ent, *check, *bestent; + vec3_t start, dir, end, bestdir; + int i, j; + trace_t tr; + float dist, bestdist; + float speed; + int flags = Cvar_VariableValue( "dmflags" ); - if (!ge) Com_Error (ERR_DROP, "failed to load game DLL"); - if (ge->apiversion != GAME_API_VERSION) - Com_Error (ERR_DROP, "game is version %i, not %i", ge->apiversion, GAME_API_VERSION); - ge->Init (); + // assume failure if it returns early + VectorCopy(prog->globals.server->v_forward, PRVM_G_VECTOR(OFS_RETURN)); + + ent = PRVM_G_EDICT(OFS_PARM0); + if (ent == prog->edicts) + { + VM_Warning("aim: can not use world entity\n"); + return; + } + if (ent->priv.sv->free) + { + VM_Warning("aim: can not use free entity\n"); + return; + } + speed = PRVM_G_FLOAT(OFS_PARM1); + + VectorCopy (ent->fields.sv->origin, start); + start[2] += 20; + + // try sending a trace straight + VectorCopy (prog->globals.server->v_forward, dir); + VectorMA (start, 2048, dir, end); + tr = SV_Trace (start, vec3_origin, vec3_origin, end, ent, MASK_ALL ); + + if (tr.ent && ((prvm_edict_t *)tr.ent)->fields.sv->takedamage == DAMAGE_AIM && (flags & DF_NO_FRIENDLY_FIRE || ent->fields.sv->team <=0 || ent->fields.sv->team != ((prvm_edict_t *)tr.ent)->fields.sv->team)) + { + VectorCopy (prog->globals.server->v_forward, PRVM_G_VECTOR(OFS_RETURN)); + return; + } + + // try all possible entities + VectorCopy (dir, bestdir); + bestdist = 0.5f; + bestent = NULL; + + check = PRVM_NEXT_EDICT(prog->edicts); + for (i = 1; i < prog->num_edicts; i++, check = PRVM_NEXT_EDICT(check)) + { + prog->xfunction->builtinsprofile++; + if (check->fields.sv->takedamage != DAMAGE_AIM) + continue; + if (check == ent) + continue; + if (flags & DF_NO_FRIENDLY_FIRE && ent->fields.sv->team > 0 && ent->fields.sv->team == check->fields.sv->team) + continue; // don't aim at teammate + for (j = 0; j < 3; j++) + end[j] = check->fields.sv->origin[j] + 0.5 * (check->fields.sv->mins[j] + check->fields.sv->maxs[j]); + VectorSubtract (end, start, dir); + VectorNormalize (dir); + dist = DotProduct (dir, prog->globals.server->v_forward); + if (dist < bestdist) continue; // to far to turn + tr = SV_Trace (start, vec3_origin, vec3_origin, end, ent, MASK_ALL ); + if (tr.ent == check) + { + // can shoot at this one + bestdist = dist; + bestent = check; + } + } + + if (bestent) + { + VectorSubtract (bestent->fields.sv->origin, ent->fields.sv->origin, dir); + dist = DotProduct (dir, prog->globals.server->v_forward); + VectorScale (prog->globals.server->v_forward, dist, end); + end[2] = dir[2]; + VectorNormalize (end); + VectorCopy (end, PRVM_G_VECTOR(OFS_RETURN)); + } + else + { + VectorCopy (bestdir, PRVM_G_VECTOR(OFS_RETURN)); + } } +/* +============== +PF_changeyaw + +This was a major timewaster in progs, so it was converted to C +============== +*/ +void PF_changeyaw (void) +{ + prvm_edict_t *ent; + float ideal, current, move, speed; + + ent = PRVM_PROG_TO_EDICT(prog->globals.server->self); + if (ent == prog->edicts) + { + VM_Warning("changeyaw: can not modify world entity\n"); + return; + } + if (ent->priv.sv->free) + { + VM_Warning("changeyaw: can not modify free entity\n"); + return; + } + current = anglemod(ent->fields.sv->angles[1]); + ideal = ent->fields.sv->ideal_yaw; + speed = ent->fields.sv->yaw_speed; + + if (current == ideal) return; + move = ideal - current; + if (ideal > current) + { + if (move >= 180) + move = move - 360; + } + else + { + if (move <= -180) + move = move + 360; + } + if (move > 0) + { + if (move > speed) + move = speed; + } + else + { + if (move < -speed) + move = -speed; + } + + ent->fields.sv->angles[1] = anglemod(current + move); +} + +/* +============== +PF_changepitch +============== +*/ +void PF_changepitch (void) +{ + prvm_edict_t *ent; + float ideal = 30, current, move, speed = 30; + + ent = PRVM_G_EDICT(OFS_PARM0); + if (ent == prog->edicts) + { + VM_Warning("changepitch: can not modify world entity\n"); + return; + } + if (ent->priv.sv->free) + { + VM_Warning("changepitch: can not modify free entity\n"); + return; + } + current = anglemod( ent->fields.sv->angles[0] ); + + if (current == ideal) return; + move = ideal - current; + if (ideal > current) + { + if (move >= 180) + move = move - 360; + } + else + { + if (move <= -180) + move = move + 360; + } + if (move > 0) + { + if (move > speed) + move = speed; + } + else + { + if (move < -speed) + move = -speed; + } + + ent->fields.sv->angles[0] = anglemod(current + move); +} + +/* +=============================================================================== + +MESSAGE WRITING + +=============================================================================== +*/ +void PF_BeginMessage(void) +{ + MSG_Begin((int)PRVM_G_FLOAT(OFS_PARM0)); +} + +void PF_WriteByte (void) +{ + MSG_WriteByte (&sv.multicast, (int)PRVM_G_FLOAT(OFS_PARM0)); +} + +void PF_WriteChar (void) +{ + MSG_WriteChar (&sv.multicast, (int)PRVM_G_FLOAT(OFS_PARM0)); +} + +void PF_WriteShort (void) +{ + MSG_WriteShort (&sv.multicast, (int)PRVM_G_FLOAT(OFS_PARM0)); +} + +void PF_WriteWord (void) +{ + MSG_WriteWord(&sv.multicast, (int)PRVM_G_FLOAT(OFS_PARM0)); +} + +void PF_WriteLong (void) +{ + MSG_WriteLong(&sv.multicast, (int)PRVM_G_FLOAT(OFS_PARM0)); +} + +void PF_WriteFloat (void) +{ + MSG_WriteFloat(&sv.multicast, PRVM_G_FLOAT(OFS_PARM0)); +} + +void PF_WriteAngle (void) +{ + MSG_WriteAngle (&sv.multicast, PRVM_G_FLOAT(OFS_PARM0)); +} + +void PF_WriteCoord (void) +{ + MSG_WritePos (&sv.multicast, PRVM_G_VECTOR(OFS_PARM0)); +} + +void PF_WriteDir (void) +{ + MSG_WriteDir (&sv.multicast, PRVM_G_VECTOR(OFS_PARM0)); +} + +void PF_WriteString (void) +{ + MSG_WriteString (&sv.multicast, (char *)PRVM_G_STRING(OFS_PARM0)); +} + +void PF_WriteUnterminatedString (void) +{ + MSG_WriteUnterminatedString (&sv.multicast, PRVM_G_STRING(OFS_PARM0)); +} + +void PF_WriteEntity (void) +{ + MSG_WriteShort (&sv.multicast, PRVM_G_EDICTNUM(OFS_PARM1)); +} + +void PF_EndMessage (void) +{ + MSG_Send((int)PRVM_G_FLOAT(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_EDICT(OFS_PARM2)); +} + +////////////////////////////////////////////////////////// + +void PF_makestatic (void) +{ + prvm_edict_t *ent; + + ent = PRVM_G_EDICT(OFS_PARM0); + if (ent == prog->edicts) + { + VM_Warning("makestatic: can not modify world entity\n"); + return; + } + if (ent->priv.sv->free) + { + VM_Warning("makestatic: can not modify free entity\n"); + return; + } + + // just throw the entity away now + PRVM_ED_Free (ent); +} + +//============================================================================= + +/* +============== +PF_setspawnparms +============== +*/ +void PF_setspawnparms (void) +{ + prvm_edict_t *ent; + int i; + client_t *client; + + ent = PRVM_G_EDICT(OFS_PARM0); + i = PRVM_NUM_FOR_EDICT(ent); + if (i < 1 || i > maxclients->value || svs.clients[i-1].state != cs_spawned) + { + Msg("tried to setspawnparms on a non-client\n"); + return; + } + + // copy spawn parms out of the client_t + client = svs.clients + i - 1; + for (i = 0; i < NUM_SPAWN_PARMS; i++) + prog->globals.server->parm[i] = client->spawn_parms[i]; +} + +void PF_registercvar (void) +{ + const char *name, *value; + name = PRVM_G_STRING(OFS_PARM0); + value = PRVM_G_STRING(OFS_PARM1); + PRVM_G_FLOAT(OFS_RETURN) = 0; + + // first check to see if it has already been defined + if (Cvar_FindVar (name)) + return; + + // check for overlap with a command + if (Cmd_Exists (name)) + { + VM_Warning("PF_registercvar: %s is a command\n", name); + return; + } + + Cvar_Get(name, value, 0); + PRVM_G_FLOAT(OFS_RETURN) = 1; // success +} + +/* +================= +PF_copyentity + +copies data from one entity to another + +copyentity(src, dst) +================= +*/ +void PF_copyentity (void) +{ + prvm_edict_t *in, *out; + + in = PRVM_G_EDICT(OFS_PARM0); + if (in == prog->edicts) + { + VM_Warning("copyentity: can not read world entity\n"); + return; + } + if (in->priv.sv->free) + { + VM_Warning("copyentity: can not read free entity\n"); + return; + } + out = PRVM_G_EDICT(OFS_PARM1); + if (out == prog->edicts) + { + VM_Warning("copyentity: can not modify world entity\n"); + return; + } + if (out->priv.sv->free) + { + VM_Warning("copyentity: can not modify free entity\n"); + return; + } + Mem_Copy(out->fields.sv, in->fields.sv, prog->progs->entityfields * 4); +} + +static cmodel_t *getmodel(prvm_edict_t *ed) +{ + int modelindex; + if (!ed || ed->priv.sv->free) + return NULL; + + modelindex = (int)ed->fields.sv->modelindex; + if (modelindex < 1 || modelindex >= MAX_MODELS) + return NULL; + + return sv.models[modelindex]; +} + +//void(entity e, string s) clientcommand = #440; // executes a command string as if it came from the specified client +//this function originally written by KrimZon, made shorter by LordHavoc +void PF_clientcommand (void) +{ + client_t *temp_client; + int i; + + //find client for this entity + i = (PRVM_NUM_FOR_EDICT(PRVM_G_EDICT(OFS_PARM0)) - 1); + if (i < 0 || i >= maxclients->value || svs.clients[i].state != cs_spawned) + { + Msg("PF_clientcommand: entity is not a client\n"); + return; + } + + temp_client = sv_client; + sv_client = svs.clients + i; + Cmd_ExecuteString (PRVM_G_STRING(OFS_PARM1)); + sv_client = temp_client; +} + +void PF_dropclient (void) +{ + int clientnum = PRVM_G_EDICTNUM(OFS_PARM0) - 1; + + if (clientnum < 0 || clientnum >= maxclients->value) + { + VM_Warning("dropclient: not a client\n"); + return; + } + if (svs.clients[clientnum].state != cs_spawned) + { + VM_Warning("dropclient: that client slot is not connected\n"); + return; + } + + SV_DropClient(svs.clients + clientnum); +} + +void PF_spawnclient (void) +{ + int i; + prvm_edict_t *ed; + prog->xfunction->builtinsprofile += 2; + ed = prog->edicts; + + for (i = 0;i < maxclients->value; i++) + { + if (svs.clients[i].state != cs_spawned) + { + prog->xfunction->builtinsprofile += 100; +//SV_ConnectClient (i, NULL); + svs.clients[i].state = cs_connected; + ed = PRVM_EDICT_NUM(i + 1); + break; + } + } + VM_RETURN_EDICT(ed); +} + +//float(entity clent) clienttype (DP_SV_BOTCLIENT) +void PF_clienttype (void) +{ + int clientnum = PRVM_G_EDICTNUM(OFS_PARM0) - 1; + + if (clientnum < 0 || clientnum >= maxclients->value) + PRVM_G_FLOAT(OFS_RETURN) = 3; + else if (svs.clients[clientnum].state == cs_spawned) + PRVM_G_FLOAT(OFS_RETURN) = 0; + else if (svs.clients[clientnum].state == cs_connected) + PRVM_G_FLOAT(OFS_RETURN) = 1; + else + PRVM_G_FLOAT(OFS_RETURN) = 2; +} + +void PF_edict_num (void) +{ + VM_RETURN_EDICT(PRVM_EDICT_NUM((int)PRVM_G_FLOAT(OFS_PARM0))); +} + +prvm_builtin_t vm_sv_builtins[] = +{ +NULL, // #0 +PF_makevectors, // #1 void(vector ang) makevectors +PF_setorigin, // #2 void(entity e, vector o) setorigin +PF_setmodel, // #3 void(entity e, string m) setmodel +PF_setsize, // #4 void(entity e, vector min, vector max) setsize +NULL, // #5 void(entity e, vector min, vector max) setabssize +VM_break, // #6 void() break +VM_random, // #7 float() random +PF_sound, // #8 void(entity e, float chan, string samp) sound +VM_normalize, // #9 vector(vector v) normalize +VM_error, // #10 void(string e) error +VM_objerror, // #11 void(string e) objerror +VM_vlen, // #12 float(vector v) vlen +VM_vectoyaw, // #13 float(vector v) vectoyaw +VM_spawn, // #14 entity() spawn +VM_remove, // #15 void(entity e) remove +PF_traceline, // #16 float(vector v1, vector v2, float tryents) traceline +PF_checkclient, // #17 entity() clientlist +VM_find, // #18 entity(entity start, .string fld, string match) find +PF_precache_sound, // #19 void(string s) precache_sound +PF_precache_model, // #20 void(string s) precache_model +PF_stuffcmd, // #21 void(entity client, string s)stuffcmd +PF_findradius, // #22 entity(vector org, float rad) findradius +VM_bprint, // #23 void(string s) bprint +PF_sprint, // #24 void(entity client, string s) sprint +VM_dprint, // #25 void(string s) dprint +VM_ftos, // #26 void(string s) ftos +VM_vtos, // #27 void(string s) vtos +VM_coredump, // #28 void() coredump +VM_traceon, // #29 void() traceon +VM_traceoff, // #30 void() traceoff +VM_eprint, // #31 void(entity e) eprint +PF_walkmove, // #32 float(float yaw, float dist) walkmove +NULL, // #33 +PF_droptofloor, // #34 float() droptofloor +PF_lightstyle, // #35 void(float style, string value) lightstyle +VM_rint, // #36 float(float v) rint +VM_floor, // #37 float(float v) floor +VM_ceil, // #38 float(float v) ceil +NULL, // #39 +PF_checkbottom, // #40 float(entity e) checkbottom +PF_pointcontents, // #41 float(vector v) pointcontents +NULL, // #42 +VM_fabs, // #43 float(float f) fabs +PF_aim, // #44 vector(entity e, float speed) aim +VM_cvar, // #45 float(string s) cvar +VM_localcmd, // #46 void(string s) localcmd +VM_nextent, // #47 entity(entity e) nextent +PF_particle, // #48 void(vector o, vector d, float color, float count) particle +PF_changeyaw, // #49 void() ChangeYaw +NULL, // #50 +VM_vectoangles, // #51 vector(vector v) vectoangles +PF_WriteByte, // #52 void(float f) WriteByte +PF_WriteChar, // #53 void(float f) WriteChar +PF_WriteShort, // #54 void(float f) WriteShort +PF_WriteWord, // #55 void(float f) WriteWord +PF_WriteLong, // #56 void(float f) WriteLong +PF_WriteCoord, // #57 void(vector v) WriteCoord +PF_WriteAngle, // #58 void(float f) WriteAngle +PF_WriteString, // #59 void(string s) WriteString +PF_WriteEntity, // #60 void(entity e) WriteEntity +PF_WriteFloat, // #61 void(entity e) WriteFloat +PF_WriteDir, // #62 void(vector v) WriteDir +PF_BeginMessage, // #63 void(float to) MSG_Begin +PF_EndMessage, // #64 void(float to, vector v, entity e) MSG_End +PF_Configstring, // #65 void(float num, string s) configstring +NULL, // #66 +NULL, // #67 void(float step) movetogoal +PF_precache_file, // #68 string(string s) precache_file +PF_makestatic, // #69 void(entity e) makestatic +VM_changelevel, // #70 void(string s) changelevel +NULL, // #71 +VM_cvar_set, // #72 void(string var, string val) cvar_set +PF_centerprint, // #73 void(entity client, strings) centerprint +PF_ambientsound, // #74 void(vector pos, string samp, float vol, float atten) ambientsound +PF_precache_model, // #75 string(string s) precache_model2 +PF_precache_sound, // #76 string(string s) precache_sound2 +PF_precache_file, // #77 string(string s) precache_file2 +PF_setspawnparms, // #78 void(entity e) setspawnparms +PF_tracetoss, // #79 void(entity e, entity ignore) tracetoss (DP_QC_TRACETOSS) +PF_changepitch, // #80 void(entity ent) changepitch (DP_QC_CHANGEPITCH) +VM_stof, // #81 float(string s) stof (FRIK_FILE) +VM_sin, // #82 float(float f) sin (DP_QC_SINCOSSQRTPOW) +VM_cos, // #83 float(float f) cos (DP_QC_SINCOSSQRTPOW) +VM_sqrt, // #84 float(float f) sqrt (DP_QC_SINCOSSQRTPOW) +VM_etos, // #85 string(entity ent) etos (DP_QC_ETOS) +NULL, // #86 +NULL, // #87 +NULL, // #88 +NULL, // #89 +PF_tracebox, // #90 void(vector v1, vector min, vector max, vector v2, float nomonsters, entity forent) tracebox (DP_QC_TRACEBOX) +VM_randomvec, // #91 vector() randomvec (DP_QC_RANDOMVEC) +NULL, // #92 +PF_registercvar, // #93 float(string name, string value) registercvar (DP_REGISTERCVAR) +VM_min, // #94 float(float a, floats) min (DP_QC_MINMAXBOUND) +VM_max, // #95 float(float a, floats) max (DP_QC_MINMAXBOUND) +VM_bound, // #96 float(float minimum, float val, float maximum) bound (DP_QC_MINMAXBOUND) +VM_pow, // #97 float(float f, float f) pow (DP_QC_SINCOSSQRTPOW) +VM_findfloat, // #98 entity(entity start, .float fld, float match) findfloat (DP_QC_FINDFLOAT) +VM_checkextension, // #99 float(string s) checkextension (the basis of the extension system) +NULL, // #100 +NULL, // #101 +NULL, // #102 +NULL, // #103 +NULL, // #104 +NULL, // #105 +NULL, // #106 +NULL, // #107 +NULL, // #108 +NULL, // #109 +VM_fopen, // #110 float(string filename, float mode) fopen (FRIK_FILE) +VM_fclose, // #111 void(float fhandle) fclose (FRIK_FILE) +VM_fgets, // #112 string(float fhandle) fgets (FRIK_FILE) +VM_fputs, // #113 void(float fhandle, string s) fputs (FRIK_FILE) +VM_strlen, // #114 float(string s) strlen (FRIK_FILE) +VM_strcat, // #115 string(string s1, string s2) strcat (FRIK_FILE) +VM_substring, // #116 string(string s, float start, float length) substring (FRIK_FILE) +VM_stov, // #117 vector(string) stov (FRIK_FILE) +VM_strzone, // #118 string(string s) strzone (FRIK_FILE) +VM_strunzone, // #119 void(string s) strunzone (FRIK_FILE) +e10, e10, e10, e10, e10, e10, e10, e10, // #120-199 +NULL, // #200 FTEQW range #200-#299 +NULL, // #201 +NULL, // #202 +NULL, // #203 +NULL, // #204 +NULL, // #205 +NULL, // #206 +NULL, // #207 +NULL, // #208 +NULL, // #209 +NULL, // #210 +NULL, // #211 +NULL, // #212 +NULL, // #213 +NULL, // #214 +NULL, // #215 +NULL, // #216 +NULL, // #217 +VM_bitshift, // #218 float(float number, float quantity) bitshift (EXT_BITSHIFT) +NULL, // #219 +e10, // #220-#229 +e10, // #230-#239 +e10, // #240-#249 +e10, // #250-#259 +e10, // #260-#269 +e10, // #270-#279 +e10, // #280-#289 +e10, // #290-#299 +e10, e10, e10, e10, e10, e10, e10, e10, e10, e10, // #300-399 +VM_copyentity, // #400 void(entity from, entity to) copyentity (DP_QC_COPYENTITY) +NULL, // #401 +VM_findchain, // #402 entity(.string fld, string match) findchain (DP_QC_FINDCHAIN) +VM_findchainfloat, // #403 entity(.float fld, float match) findchainfloat (DP_QC_FINDCHAINFLOAT) +NULL, // #404 void(vector org, string modelname, float startframe, float endframe, float framerate) effect (DP_SV_EFFECT) +NULL, // #405 void(vector org, vector velocity, float howmany) te_blood (DP_TE_BLOOD) +NULL, // #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER) +NULL, // #407 void(vector org, vector color) te_explosionrgb (DP_TE_EXPLOSIONRGB) +NULL, // #408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE) +NULL, // #409 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain (DP_TE_PARTICLERAIN) +NULL, // #410 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow (DP_TE_PARTICLESNOW) +NULL, // #411 void(vector org, vector vel, float howmany) te_spark (DP_TE_SPARK) +NULL, // #412 void(vector org) te_gunshotquad (DP_QUADEFFECTS1) +NULL, // #413 void(vector org) te_spikequad (DP_QUADEFFECTS1) +NULL, // #414 void(vector org) te_superspikequad (DP_QUADEFFECTS1) +NULL, // #415 void(vector org) te_explosionquad (DP_QUADEFFECTS1) +NULL, // #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH) +NULL, // #417 void(vector org, float radius, float lifetime, vector color) te_customflash (DP_TE_CUSTOMFLASH) +NULL, // #418 void(vector org) te_gunshot (DP_TE_STANDARDEFFECTBUILTINS) +NULL, // #419 void(vector org) te_spike (DP_TE_STANDARDEFFECTBUILTINS) +NULL, // #420 void(vector org) te_superspike (DP_TE_STANDARDEFFECTBUILTINS) +NULL, // #421 void(vector org) te_explosion (DP_TE_STANDARDEFFECTBUILTINS) +NULL, // #422 void(vector org) te_tarexplosion (DP_TE_STANDARDEFFECTBUILTINS) +NULL, // #423 void(vector org) te_wizspike (DP_TE_STANDARDEFFECTBUILTINS) +NULL, // #424 void(vector org) te_knightspike (DP_TE_STANDARDEFFECTBUILTINS) +NULL, // #425 void(vector org) te_lavasplash (DP_TE_STANDARDEFFECTBUILTINS) +NULL, // #426 void(vector org) te_teleport (DP_TE_STANDARDEFFECTBUILTINS) +NULL, // #427 void(vector org, float colorstart, float colorlength) te_explosion2 (DP_TE_STANDARDEFFECTBUILTINS) +NULL, // #428 void(entity own, vector start, vector end) te_lightning1 (DP_TE_STANDARDEFFECTBUILTINS) +NULL, // #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS) +NULL, // #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS) +NULL, // #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS) +VM_vectorvectors, // #432 void(vector dir) vectorvectors (DP_QC_VECTORVECTORS) +NULL, // #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN) +NULL, // #434 float(entity e, float s) getsurfacenumpoints (DP_QC_GETSURFACE) +NULL, // #435 vector(entity e, float s, float n) getsurfacepoint (DP_QC_GETSURFACE) +NULL, // #436 vector(entity e, float s) getsurfacenormal (DP_QC_GETSURFACE) +NULL, // #437 string(entity e, float s) getsurfacetexture (DP_QC_GETSURFACE) +NULL, // #438 float(entity e, vector p) getsurfacenearpoint (DP_QC_GETSURFACE) +NULL, // #439 vector(entity e, float s, vector p) getsurfaceclippedpoint (DP_QC_GETSURFACE) +PF_clientcommand, // #440 void(entity e, string s) clientcommand (KRIMZON_SV_PARSECLIENTCOMMAND) +VM_tokenize, // #441 float(string s) tokenize (KRIMZON_SV_PARSECLIENTCOMMAND) +VM_argv, // #442 string(float n) argv (KRIMZON_SV_PARSECLIENTCOMMAND) +NULL, // #443 void(entity e, entity tagentity, string tagname) setattachment (DP_GFX_QUAKE3MODELTAGS) +VM_search_begin, // #444 float(string pattern, float caseinsensitive, float quiet) search_begin (DP_FS_SEARCH) +VM_search_end, // #445 void(float handle) search_end (DP_FS_SEARCH) +VM_search_getsize, // #446 float(float handle) search_getsize (DP_FS_SEARCH) +VM_search_getfilename, // #447 string(float handle, float num) search_getfilename (DP_FS_SEARCH) +VM_cvar_string, // #448 string(string s) cvar_string (DP_QC_CVAR_STRING) +VM_findflags, // #449 entity(entity start, .float fld, float match) findflags (DP_QC_FINDFLAGS) +VM_findchainflags, // #450 entity(.float fld, float match) findchainflags (DP_QC_FINDCHAINFLAGS) +NULL, // #451 float(entity ent, string tagname) gettagindex (DP_QC_GETTAGINFO) +NULL, // #452 vector(entity ent, float tagindex) gettaginfo (DP_QC_GETTAGINFO) +PF_dropclient, // #453 void(entity clent) dropclient (DP_SV_DROPCLIENT) +PF_spawnclient, // #454 entity() spawnclient (DP_SV_BOTCLIENT) +PF_clienttype, // #455 float(entity clent) clienttype (DP_SV_BOTCLIENT) +PF_WriteUnterminatedString, // #456 void(float to, string s) WriteUnterminatedString (DP_SV_WRITEUNTERMINATEDSTRING) +NULL, // #457 void(vector org, vector vel, float howmany) te_flamejet = #457 (DP_TE_FLAMEJET) +NULL, // #458 +PF_edict_num, // #459 entity(float num) (??) +VM_buf_create, // #460 float() buf_create (DP_QC_STRINGBUFFERS) +VM_buf_del, // #461 void(float bufhandle) buf_del (DP_QC_STRINGBUFFERS) +VM_buf_getsize, // #462 float(float bufhandle) buf_getsize (DP_QC_STRINGBUFFERS) +VM_buf_copy, // #463 void(float bufhandle_from, float bufhandle_to) buf_copy (DP_QC_STRINGBUFFERS) +VM_buf_sort, // #464 void(float bufhandle, float sortpower, float backward) buf_sort (DP_QC_STRINGBUFFERS) +VM_buf_implode, // #465 string(float bufhandle, string glue) buf_implode (DP_QC_STRINGBUFFERS) +VM_bufstr_get, // #466 string(float bufhandle, float string_index) bufstr_get (DP_QC_STRINGBUFFERS) +VM_bufstr_set, // #467 void(float bufhandle, float string_index, string str) bufstr_set (DP_QC_STRINGBUFFERS) +VM_bufstr_add, // #468 float(float bufhandle, string str, float order) bufstr_add (DP_QC_STRINGBUFFERS) +VM_bufstr_free, // #469 void(float bufhandle, float string_index) bufstr_free (DP_QC_STRINGBUFFERS) +NULL, // #470 +NULL, // #471 +NULL, // #472 +NULL, // #473 +NULL, // #474 +NULL, // #475 +NULL, // #476 +NULL, // #477 +NULL, // #478 +NULL, // #479 +e10, e10 // #480-499 (LordHavoc) +}; + +const int vm_sv_numbuiltins = sizeof(vm_sv_builtins) / sizeof(prvm_builtin_t); diff --git a/engine/server/sv_game_old.c b/engine/server/sv_game_old.c new file mode 100644 index 00000000..482d5072 --- /dev/null +++ b/engine/server/sv_game_old.c @@ -0,0 +1,19 @@ + +/* +=============== +PF_dprintf + +Debug print to server console +=============== +*/ +void PF_dprintf (char *fmt, ...) +{ + char msg[1024]; + va_list argptr; + + va_start (argptr,fmt); + vsprintf (msg, fmt, argptr); + va_end (argptr); + + Msg ("%s", msg); +} \ No newline at end of file diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 96322334..3975f401 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -24,30 +24,120 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. server_static_t svs; // persistant server info server_t sv; // local server +#define REQFIELDS (sizeof(reqfields) / sizeof(prvm_fieldvars_t)) + +prvm_fieldvars_t reqfields[] = +{ + {0, 2, "modelindex"}, + {1, 3, "absmin"}, + {1, 2, "absmin_x"}, + {2, 2, "absmin_y"}, + {3, 2, "absmin_z"}, + {4, 3, "absmax"}, + {7, 2, "ltime"}, + {8, 2, "movetype"}, + {9, 2, "solid"}, + {10, 3, "origin"}, + {13, 3, "oldorigin"}, + {16, 3, "velocity"}, + {19, 3, "angles"}, + {22, 3, "avelocity"}, + {25, 3, "punchangle"}, + {28, 1, "classname"}, + {29, 1, "model"}, + {30, 2, "frame"}, + {31, 2, "skin"}, + {32, 2, "body"}, + {33, 2, "effects"}, + {34, 2, "sequence"}, + {35, 2, "renderfx"}, + {36, 3, "mins"}, + {39, 3, "maxs"}, + {42, 3, "size"}, + {45, 6, "touch"}, + {46, 6, "use"}, + {47, 6, "think"}, + {48, 6, "blocked"}, + {49, 2, "nextthink"}, + {50, 4, "groundentity"}, + {51, 2, "health"}, + {52, 2, "frags"}, + {53, 2, "weapon"}, + {54, 1, "weaponmodel"}, + {55, 2, "weaponframe"}, + {56, 2, "currentammo"}, + {57, 2, "ammo_shells"}, + {58, 2, "ammo_nails"}, + {59, 2, "ammo_rockets"}, + {60, 2, "ammo_cells"}, + {61, 2, "items"}, + {62, 2, "takedamage"}, + {63, 4, "chain"}, + {64, 2, "deadflag"}, + {65, 3, "view_ofs"}, + {68, 2, "button0"}, + {69, 2, "button1"}, + {70, 2, "button2"}, + {71, 2, "impulse"}, + {72, 2, "fixangle"}, + {73, 3, "v_angle"}, + {76, 2, "idealpitch"}, + {77, 1, "netname"}, + {78, 4, "enemy"}, + {79, 2, "flags"}, + {80, 2, "colormap"}, + {81, 2, "team"}, + {82, 2, "max_health"}, + {83, 2, "teleport_time"}, + {84, 2, "armortype"}, + {85, 2, "armorvalue"}, + {86, 2, "waterlevel"}, + {87, 2, "watertype"}, + {88, 2, "ideal_yaw"}, + {89, 2, "yaw_speed"}, + {90, 4, "aiment"}, + {91, 4, "goalentity"}, + {92, 2, "spawnflags"}, + {93, 1, "target"}, + {94, 1, "targetname"}, + {95, 2, "dmg_take"}, + {96, 2, "dmg_save"}, + {97, 4, "dmg_inflictor"}, + {98, 4, "owner"}, + {99, 3, "movedir"}, + {102, 1, "message"}, + {103, 2, "sounds"}, + {104, 1, "noise"}, + {105, 1, "noise1"}, + {106, 1, "noise2"}, + {107, 1, "noise3"} +}; + /* ================ SV_FindIndex ================ */ -int SV_FindIndex (char *name, int start, int max, bool create) +int SV_FindIndex (const char *name, int start, int end, bool create) { int i; - if (!name || !name[0]) - return 0; + if (!name || !name[0]) return 0; - for (i=1 ; inum_edicts ; entnum++) + for (entnum = 1; entnum < prog->num_edicts; entnum++) { - svent = EDICT_NUM(entnum); - if (!svent->inuse) - continue; - if (!svent->s.modelindex && !svent->s.sound && !svent->s.effects) - continue; - svent->s.number = entnum; + svent = PRVM_EDICT_NUM(entnum); + + if (!svent->priv.sv->free) continue; + if (!svent->priv.sv->state.modelindex && !svent->priv.sv->state.sound && !svent->priv.sv->state.effects) + continue; + + svent->priv.sv->state.number = entnum; + + if (entnum > maxclients->value && !svent->fields.sv->modelindex) + continue; + + // create entity baseline + VectorCopy (svent->fields.sv->origin, svent->priv.sv->state.origin); + VectorCopy (svent->priv.sv->state.origin, svent->priv.sv->state.old_origin); + VectorCopy (svent->fields.sv->angles, svent->priv.sv->state.angles); + svent->priv.sv->state.frame = (int)svent->fields.sv->frame; + svent->priv.sv->state.skin = (int)svent->fields.sv->skin; + svent->priv.sv->state.body = (int)svent->fields.sv->body; + svent->priv.sv->state.sequence = (int)svent->fields.sv->sequence; + svent->priv.sv->state.effects = (int)svent->fields.sv->effects; + svent->priv.sv->state.renderfx = (int)svent->fields.sv->renderfx; + svent->priv.sv->state.solid = (int)svent->fields.sv->solid; + + if (entnum > 0 && entnum <= maxclients->value) + { + svent->priv.sv->state.modelindex = SV_ModelIndex("progs/player.mdl"); + } + else + { + svent->priv.sv->state.modelindex = (int)svent->fields.sv->modelindex; + } - // // take current state as baseline - // - VectorCopy (svent->s.origin, svent->s.old_origin); - sv.baselines[entnum] = svent->s; + sv.baselines[entnum] = svent->priv.sv->state; } } +/* +================ +SV_SaveSpawnparms + +Grabs the current state of each client for saving across the +transition to another level +================ +*/ +void SV_SaveSpawnparms (void) +{ + int i, j; + + svs.serverflags = (int)prog->globals.server->serverflags; + + for (i = 0, sv_client = svs.clients; i < maxclients->value; i++, sv_client++) + { + if (sv_client->state != cs_spawned) + continue; + + // call the progs to get default spawn parms for the new client + prog->globals.server->self = PRVM_EDICT_TO_PROG(sv_client->edict); + PRVM_ExecuteProgram (prog->globals.server->SetChangeParms, "QC function SetChangeParms is missing"); + for (j = 0; j < NUM_SPAWN_PARMS; j++) + sv_client->spawn_parms[j] = prog->globals.server->parm[j]; + } +} /* ================= @@ -118,7 +255,6 @@ SV_CheckForSavegame */ void SV_CheckForSavegame (char *savename ) { - int i; char name[MAX_SYSPATH]; if (sv_noreload->value) return; @@ -135,22 +271,6 @@ void SV_CheckForSavegame (char *savename ) // get configstrings and areaportals SV_ReadLevelFile ( savename ); - - if (!sv.loadgame) - { // coming back to a level after being in a different - // level, so run it for ten seconds - - // rlava2 was sending too many lightstyles, and overflowing the - // reliable data. temporarily changing the server state to loading - // prevents these from being passed down. - server_state_t previousState; // PGM - - previousState = sv.state; // PGM - sv.state = ss_loading; // PGM - for (i = 0; i < 100; i++) ge->RunFrame (); - - sv.state = previousState; // PGM - } } @@ -165,7 +285,8 @@ clients along with it. */ void SV_SpawnServer (char *server, char *spawnpoint, char *savename, server_state_t serverstate, bool attractloop, bool loadgame) { - uint i, checksum; + uint i, checksum; + prvm_edict_t *ent; if (attractloop) Cvar_Set ("paused", "0"); @@ -200,7 +321,7 @@ void SV_SpawnServer (char *server, char *spawnpoint, char *savename, server_stat strcpy (sv.name, server); // leave slots at start for clients only - for (i=0 ; ivalue ; i++) + for (i=0 ; i < maxclients->value ; i++) { // needs to reconnect if (svs.clients[i].state > cs_connected) @@ -208,7 +329,7 @@ void SV_SpawnServer (char *server, char *spawnpoint, char *savename, server_stat svs.clients[i].lastframe = -1; } - sv.time = 1000; + sv.time = 1; strcpy (sv.name, server); strcpy (sv.configstrings[CS_NAME], server); @@ -219,7 +340,7 @@ void SV_SpawnServer (char *server, char *spawnpoint, char *savename, server_stat } else { - sprintf (sv.configstrings[CS_MODELS+1], "maps/%s.bsp", server); + strcpy (sv.configstrings[CS_MODELS+1], server); sv.models[1] = CM_LoadMap (sv.configstrings[CS_MODELS+1], false, &checksum); } sprintf (sv.configstrings[CS_MAPCHECKSUM],"%i", checksum); @@ -233,6 +354,8 @@ void SV_SpawnServer (char *server, char *spawnpoint, char *savename, server_stat sv.models[i+1] = CM_InlineModel (sv.configstrings[CS_MODELS+1+i]); } + SV_VM_Setup(); + // // spawn the rest of the entities on the map // @@ -242,12 +365,46 @@ void SV_SpawnServer (char *server, char *spawnpoint, char *savename, server_stat sv.state = ss_loading; Com_SetServerState (sv.state); - // load and spawn all other entities - ge->SpawnEntities ( sv.name, CM_EntityString(), spawnpoint ); + SV_VM_Begin(); + + sv.moved_edicts = (prvm_edict_t **)PRVM_Alloc(prog->max_edicts * sizeof(prvm_edict_t *)); + *prog->time = sv.time; + + ent = PRVM_EDICT_NUM(0); + memset (ent->fields.sv, 0, prog->progs->entityfields * 4); + ent->priv.sv->free = false; + ent->fields.sv->model = PRVM_SetEngineString(sv.configstrings[CS_MODELS]); + ent->fields.sv->modelindex = 1; // world model + ent->fields.sv->solid = SOLID_BSP; + ent->fields.sv->movetype = MOVETYPE_PUSH; + + prog->globals.server->mapname = PRVM_SetEngineString(sv.name); + + // serverflags are for cross level information (sigils) + prog->globals.server->serverflags = svs.serverflags; + + // we need to reset the spawned flag on all connected clients here so that + // their thinks don't run during startup (before PutClientInServer) + // we also need to set up the client entities now + // and we need to set the ->edict pointers to point into the progs edicts + for (i = 0, sv_client = svs.clients; i < maxclients->value; i++, sv_client++) + { + sv_client->state = cs_connected; + sv_client->edict = PRVM_EDICT_NUM(i + 1); + sv_client->edict->priv.sv->client = Z_Malloc(sizeof(player_state_t)); + ent->priv.sv->state.number = i + 1; + memset (&sv_client->lastcmd, 0, sizeof(sv_client->lastcmd)); + PRVM_ED_ClearEdict(sv_client->edict); + } + + PRVM_ED_LoadFromFile (CM_EntityString()); // run two frames to allow everything to settle - ge->RunFrame (); - ge->RunFrame (); + for (i = 0; i < 2; i++) + { + sv.frametime = 0.1f; + SV_Physics(); + } // all precaches are complete sv.state = serverstate; @@ -256,8 +413,15 @@ void SV_SpawnServer (char *server, char *spawnpoint, char *savename, server_stat // create a baseline for more efficient communications SV_CreateBaseline (); + // call the progs to get default spawn parms for the new client + // set self to world to intentionally cause errors with broken SetNewParms code in some mods + prog->globals.server->self = 0; + PRVM_ExecuteProgram (prog->globals.server->SetNewParms, "QC function SetNewParms is missing"); + for (i = 0; i < NUM_SPAWN_PARMS; i++) + sv_client->spawn_parms[i] = prog->globals.server->parm[i]; + // check for a savegame - SV_CheckForSavegame ( savename ); +//SV_CheckForSavegame ( savename ); // set serverinfo variable Cvar_FullSet ("mapname", sv.name, CVAR_SERVERINFO | CVAR_NOSET); @@ -274,8 +438,8 @@ A brand new game has been started */ void SV_InitGame (void) { - int i; - edict_t *ent; + //int i; + //prvm_edict_t *ent; char idmaster[32]; if (svs.initialized) @@ -328,9 +492,9 @@ void SV_InitGame (void) } svs.spawncount = rand(); - svs.clients = Z_Malloc (sizeof(client_t)*maxclients->value); - svs.num_client_entities = maxclients->value*UPDATE_BACKUP*64; - svs.client_entities = Z_Malloc (sizeof(entity_state_t)*svs.num_client_entities); + svs.clients = Z_Malloc (sizeof(client_t) * maxclients->value); + svs.num_client_entities = maxclients->value * UPDATE_BACKUP * 64; + svs.client_entities = Z_Malloc(sizeof(entity_state_t) * svs.num_client_entities); // init network stuff NET_Config ( (maxclients->value > 1) ); @@ -339,17 +503,6 @@ void SV_InitGame (void) svs.last_heartbeat = -99999; // send immediately sprintf(idmaster, "192.246.40.37:%i", PORT_MASTER); NET_StringToAdr (idmaster, &master_adr[0]); - - // init game - SV_InitGameProgs (); - - for (i = 0; i < maxclients->value; i++) - { - ent = EDICT_NUM(i + 1); - ent->s.number = i + 1; - svs.clients[i].edict = ent; - memset (&svs.clients[i].lastcmd, 0, sizeof(svs.clients[i].lastcmd)); - } } @@ -438,3 +591,145 @@ void SV_Map (bool attractloop, char *levelstring, char *savename, bool loadgame) SV_BroadcastCommand ("reconnect\n"); } + +void SV_VM_BeginIncreaseEdicts(void) +{ + int i; + prvm_edict_t *ent; + + PRVM_Free( sv.moved_edicts ); + sv.moved_edicts = (prvm_edict_t **)PRVM_Alloc(prog->max_edicts * sizeof(prvm_edict_t *)); + + // links don't survive the transition, so unlink everything + for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++) + { + if (!ent->priv.sv->free) SV_UnlinkEdict(prog->edicts + i); + memset(&ent->priv.sv->clusternums, 0, sizeof(ent->priv.sv->clusternums)); + } + SV_ClearWorld(); +} + +void SV_VM_EndIncreaseEdicts(void) +{ + int i; + prvm_edict_t *ent; + + for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++) + { + // link every entity except world + if (!ent->priv.sv->free) SV_LinkEdict(ent); + } +} + +void SV_VM_InitEdict(prvm_edict_t *e) +{ + int num = PRVM_NUM_FOR_EDICT(e) - 1; + e->priv.sv->move = false; // don't move on first frame + + // set here additional player effects: model, skin, etc... +} + +void SV_VM_FreeEdict(prvm_edict_t *ed) +{ + SV_UnlinkEdict (ed); // unlink from world bsp + + ed->fields.sv->model = 0; + ed->fields.sv->takedamage = 0; + ed->fields.sv->modelindex = 0; + ed->fields.sv->colormap = 0; + ed->fields.sv->skin = 0; + ed->fields.sv->frame = 0; + VectorClear(ed->fields.sv->origin); + VectorClear(ed->fields.sv->angles); + ed->fields.sv->nextthink = -1; + ed->fields.sv->solid = 0; +} + +void SV_VM_CountEdicts( void ) +{ + int i; + prvm_edict_t *ent; + int active = 0, models = 0, solid = 0, step = 0; + + for (i = 0; i < prog->num_edicts; i++) + { + ent = PRVM_EDICT_NUM(i); + if (ent->priv.sv->free) + continue; + active++; + if (ent->fields.sv->solid) solid++; + if (ent->fields.sv->model) models++; + if (ent->fields.sv->movetype == MOVETYPE_STEP) step++; + } + + Msg("num_edicts:%3i\n", prog->num_edicts); + Msg("active :%3i\n", active); + Msg("view :%3i\n", models); + Msg("touch :%3i\n", solid); + Msg("step :%3i\n", step); +} + +bool SV_VM_LoadEdict(prvm_edict_t *ent) +{ + int current_skill = (int)Cvar_VariableValue ("skill"); + + // remove things from different skill levels or deathmatch + if(Cvar_VariableValue ("deathmatch")) + { + if (((int)ent->fields.sv->spawnflags & SPAWNFLAG_NOT_DEATHMATCH)) + { + return false; + } + } + else if ((current_skill <= 0 && ((int)ent->fields.sv->spawnflags & SPAWNFLAG_NOT_EASY )) || (current_skill == 1 && ((int)ent->fields.sv->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || (current_skill >= 2 && ((int)ent->fields.sv->spawnflags & SPAWNFLAG_NOT_HARD ))) + { + return false; + } + return true; +} + +void SV_VM_Setup( void ) +{ + PRVM_Begin; + PRVM_InitProg( PRVM_SERVERPROG ); + + // allocate the mempools + // TODO: move the magic numbers/constants into #defines [9/13/2006 Black] + prog->progs_mempool = Mem_AllocPool("Server Progs" ); + prog->builtins = vm_sv_builtins; + prog->numbuiltins = vm_sv_numbuiltins; + prog->max_edicts = 512; + prog->limit_edicts = MAX_EDICTS; + prog->reserved_edicts = maxclients->value; + prog->edictprivate_size = sizeof(server_edict_t); + prog->name = "server"; + prog->extensionstring = ""; + prog->loadintoworld = true; + + prog->begin_increase_edicts = SV_VM_BeginIncreaseEdicts; + prog->end_increase_edicts = SV_VM_EndIncreaseEdicts; + prog->init_edict = SV_VM_InitEdict; + prog->free_edict = SV_VM_FreeEdict; + prog->count_edicts = SV_VM_CountEdicts; + prog->load_edict = SV_VM_LoadEdict; + prog->init_cmd = VM_Cmd_Init; + prog->reset_cmd = VM_Cmd_Reset; + prog->error_cmd = VM_Error; + + // TODO: add a requiredfuncs list (ask LH if this is necessary at all) + PRVM_LoadProgs( "server.dat", 0, NULL, REQFIELDS, reqfields ); + PRVM_End; +} + +void SV_VM_Begin(void) +{ + PRVM_Begin; + PRVM_SetProg( PRVM_SERVERPROG ); + + *prog->time = (float)sv.time; +} + +void SV_VM_End(void) +{ + PRVM_End; +} \ No newline at end of file diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 12c6c148..73547307 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -41,10 +41,19 @@ cvar_t *allow_download_models; cvar_t *allow_download_sounds; cvar_t *allow_download_maps; -cvar_t *sv_airaccelerate; +cvar_t *sv_airaccelerate; +cvar_t *sv_wateraccelerate; +cvar_t *sv_accelerate; +cvar_t *sv_maxvelocity; +cvar_t *sv_maxspeed; +cvar_t *sv_friction; +cvar_t *sv_gravity; +cvar_t *sv_rollangle; +cvar_t *sv_rollspeed; cvar_t *sv_noreload; // don't reload level state when reentering + cvar_t *maxclients; // FIXME: rename sv_maxclients cvar_t *sv_showclamp; @@ -77,7 +86,7 @@ void SV_DropClient (client_t *drop) { // call the prog function for removing a client // this will remove the body, among other things - ge->ClientDisconnect (drop->edict); +//ge->ClientDisconnect (drop->edict); } if (drop->download) @@ -124,7 +133,7 @@ char *SV_StatusString (void) cl = &svs.clients[i]; if (cl->state == cs_connected || cl->state == cs_spawned ) { - sprintf (player, "%i %i \"%s\"\n", cl->edict->client->ps.stats[STAT_FRAGS], cl->ping, cl->name); + sprintf (player, "%i %i \"%s\"\n", cl->edict->priv.sv->client->stats[STAT_FRAGS], cl->ping, cl->name); playerLength = strlen(player); if (statusLength + playerLength >= sizeof(status) ) break; // can't hold any more @@ -265,7 +274,7 @@ void SVC_DirectConnect (void) int i; client_t *cl, *newcl; client_t temp; - edict_t *ent; + prvm_edict_t *ent; int edictnum; int version; int qport; @@ -335,7 +344,7 @@ void SVC_DirectConnect (void) && ( cl->netchan.qport == qport || adr.port == cl->netchan.remote_address.port ) ) { - if (!NET_IsLocalAddress (adr) && (svs.realtime - cl->lastconnect) < ((int)sv_reconnect_limit->value * 1000)) + if (!NET_IsLocalAddress (adr) && (svs.realtime - cl->lastconnect) < ((int)sv_reconnect_limit->value)) { MsgWarn("SVC_DirectConnect: %s:reconnect rejected : too soon\n", NET_AdrToString (adr)); return; @@ -371,19 +380,23 @@ gotnewcl: sv_client = newcl; edictnum = (newcl - svs.clients) + 1; - ent = EDICT_NUM(edictnum); + ent = PRVM_EDICT_NUM(edictnum); newcl->edict = ent; newcl->challenge = challenge; // save challenge for checksumming - // get the game a chance to reject this connection or modify the userinfo - if (!(ge->ClientConnect (ent, userinfo))) - { + prog->globals.server->time = sv.time; + prog->globals.server->self = PRVM_EDICT_TO_PROG(sv_client->edict); + PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing"); + PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing"); + + //if (!(ge->ClientConnect (ent, userinfo))) + /*{ if (*Info_ValueForKey (userinfo, "rejmsg")) Netchan_OutOfBandPrint (NS_SERVER, adr, "print\n%s\nConnection refused.\n", Info_ValueForKey (userinfo, "rejmsg")); else Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n" ); MsgWarn("SVC_DirectConnect: Game rejected a connection.\n"); return; - } + }*/ // parse some info from the info strings strncpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo)-1); @@ -535,7 +548,7 @@ void SV_CalcPings (void) #endif // let the game dll know about the ping - cl->edict->client->ping = cl->ping; +//cl->edict->client->ping = cl->ping; } } @@ -595,7 +608,7 @@ void SV_ReadPackets (void) qport = MSG_ReadShort (&net_message) & 0xffff; // check for packets from connected clients - for (i=0, cl=svs.clients ; ivalue ; i++,cl++) + for (i = 0, cl = svs.clients; i < maxclients->value; i++, cl++) { if (cl->state == cs_free) continue; @@ -610,7 +623,8 @@ void SV_ReadPackets (void) } if (Netchan_Process(&cl->netchan, &net_message)) - { // this is a valid, sequenced packet, so process it + { + // this is a valid, sequenced packet, so process it if (cl->state != cs_zombie) { cl->lastmessage = svs.realtime; // don't timeout @@ -645,17 +659,15 @@ void SV_CheckTimeouts (void) int droppoint; int zombiepoint; - droppoint = svs.realtime - 1000*timeout->value; - zombiepoint = svs.realtime - 1000*zombietime->value; + droppoint = svs.realtime - timeout->value; + zombiepoint = svs.realtime - zombietime->value; for (i=0,cl=svs.clients ; ivalue ; i++,cl++) { // message times may be wrong across a changelevel - if (cl->lastmessage > svs.realtime) - cl->lastmessage = svs.realtime; + if (cl->lastmessage > svs.realtime) cl->lastmessage = svs.realtime; - if (cl->state == cs_zombie - && cl->lastmessage < zombiepoint) + if (cl->state == cs_zombie && cl->lastmessage < zombiepoint) { cl->state = cs_free; // can now be reused continue; @@ -680,16 +692,15 @@ player processing happens outside RunWorldFrame */ void SV_PrepWorldFrame (void) { - edict_t *ent; + prvm_edict_t *ent; int i; - for (i=0 ; inum_edicts ; i++, ent++) + for (i = 0; i < prog->num_edicts ; i++, ent++) { - ent = EDICT_NUM(i); + ent = PRVM_EDICT_NUM(i); // events only last for a single message - ent->s.event = 0; + ent->priv.sv->state.event = 0; } - } @@ -700,20 +711,20 @@ SV_RunGameFrame */ void SV_RunGameFrame (void) { - if (host_speeds->value) - time_before_game = Sys_Milliseconds (); + if (host_speeds->value) time_before_game = Sys_DoubleTime(); // we always need to bump framenum, even if we // don't run the world, otherwise the delta // compression can get confused when a client // has the "current" frame + sv.framenum++; - sv.time = sv.framenum*100; + sv.time = sv.framenum * 0.1; // don't run if paused if (!sv_paused->value || maxclients->value > 1) { - ge->RunFrame (); + SV_Physics(); // never get more than one tic behind if (sv.time < svs.realtime) @@ -724,9 +735,7 @@ void SV_RunGameFrame (void) } } - if (host_speeds->value) - time_after_game = Sys_Milliseconds (); - + if (host_speeds->value) time_after_game = Sys_DoubleTime(); } /* @@ -735,22 +744,24 @@ SV_Frame ================== */ -void SV_Frame (int msec) +void SV_Frame (float time) { time_before_game = time_after_game = 0; // if server is not active, do nothing - if (!svs.initialized) - return; + if (!svs.initialized) return; - svs.realtime += msec; + svs.realtime += time; // keep the random time dependent rand (); + // setup the VM frame + SV_VM_Begin(); + // check timeouts SV_CheckTimeouts (); - + // get packets from clients SV_ReadPackets (); @@ -758,11 +769,10 @@ void SV_Frame (int msec) if (!sv_timedemo->value && svs.realtime < sv.time) { // never let the time get too far off - if (sv.time - svs.realtime > 100) + if (sv.time - svs.realtime > 0.1) { - if (sv_showclamp->value) - Msg ("sv lowclamp\n"); - svs.realtime = sv.time - 100; + if (sv_showclamp->value) Msg ("sv lowclamp\n"); + svs.realtime = sv.time - 0.1; } NET_Sleep(sv.time - svs.realtime); return; @@ -789,6 +799,8 @@ void SV_Frame (int msec) // clear teleport flags, etc for next frame SV_PrepWorldFrame (); + // end the server VM frame + SV_VM_End(); } //============================================================================ @@ -819,7 +831,7 @@ void Master_Heartbeat (void) if (svs.last_heartbeat > svs.realtime) svs.last_heartbeat = svs.realtime; - if (svs.realtime - svs.last_heartbeat < HEARTBEAT_SECONDS*1000) + if (svs.realtime - svs.last_heartbeat < HEARTBEAT_SECONDS) return; // not time to send yet svs.last_heartbeat = svs.realtime; @@ -828,12 +840,14 @@ void Master_Heartbeat (void) string = SV_StatusString(); // send to group master - for (i=0 ; ivalue) + if (!dedicated && !dedicated->value) return; // only dedicated servers send heartbeats // pgm post3.19 change, cvar pointer not validated before dereferencing @@ -882,7 +896,7 @@ void SV_UserinfoChanged (client_t *cl) int i; // call prog code to allow overrides - ge->ClientUserinfoChanged (cl->edict, cl->userinfo); +//ge->ClientUserinfoChanged (cl->edict, cl->userinfo); // name for C code strncpy (cl->name, Info_ValueForKey (cl->userinfo, "name"), sizeof(cl->name)-1); @@ -952,7 +966,16 @@ void SV_Init (void) sv_noreload = Cvar_Get ("sv_noreload", "0", 0); - sv_airaccelerate = Cvar_Get("sv_airaccelerate", "0", CVAR_LATCH); + sv_wateraccelerate = Cvar_Get("sv_wateraccelerate", "-1", CVAR_ARCHIVE); + sv_airaccelerate = Cvar_Get("sv_airaccelerate", "-1", CVAR_ARCHIVE); + sv_accelerate = Cvar_Get("sv_accelerate", "10", CVAR_ARCHIVE); + sv_maxvelocity = Cvar_Get("sv_maxvelocity", "2000", CVAR_ARCHIVE ); + sv_maxspeed = Cvar_Get("sv_maxspeed", "320", CVAR_ARCHIVE); + sv_friction = Cvar_Get("sv_friction", "4", CVAR_ARCHIVE); + sv_gravity = Cvar_Get("sv_gravity", "800", CVAR_ARCHIVE); + + sv_rollangle = Cvar_Get("sv_rollangle", "2", CVAR_ARCHIVE); + sv_rollspeed = Cvar_Get("sv_rollspeed", "200", CVAR_ARCHIVE); public_server = Cvar_Get ("public", "0", 0); @@ -1018,7 +1041,7 @@ void SV_Shutdown (char *finalmsg, bool reconnect) if (svs.clients) SV_FinalMessage (finalmsg, reconnect); Master_Shutdown (); - SV_ShutdownGameProgs (); +//SV_ShutdownGameProgs (); // free current level if (sv.demofile) FS_Close (sv.demofile); diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c new file mode 100644 index 00000000..30a61f91 --- /dev/null +++ b/engine/server/sv_phys.c @@ -0,0 +1,1387 @@ +//======================================================================= +// Copyright XashXT Group 2007 © +// sv_phys.c - internal physic engine +//======================================================================= + +#include "engine.h" +#include "server.h" + +// phys cvars +extern cvar_t *sv_maxvelocity; +extern cvar_t *sv_gravity; + +void SV_CheckVelocity (prvm_edict_t *ent) +{ + int i; + float wishspeed; + + // bound velocity + for (i = 0; i < 3; i++) + { + if (IS_NAN(ent->fields.sv->velocity[i])) + { + Msg("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.sv->classname)); + ent->fields.sv->velocity[i] = 0; + } + if (IS_NAN(ent->fields.sv->origin[i])) + { + Msg("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.sv->classname)); + ent->fields.sv->origin[i] = 0; + } + } + + // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster + wishspeed = DotProduct(ent->fields.sv->velocity, ent->fields.sv->velocity); + if (wishspeed > sv_maxvelocity->value * sv_maxvelocity->value) + { + wishspeed = sv_maxvelocity->value / sqrt(wishspeed); + ent->fields.sv->velocity[0] *= wishspeed; + ent->fields.sv->velocity[1] *= wishspeed; + ent->fields.sv->velocity[2] *= wishspeed; + } +} + +trace_t SV_TraceToss (prvm_edict_t *tossent, prvm_edict_t *ignore) +{ + int i; + float gravity = 1.0; + vec3_t move, end; + vec3_t original_origin; + vec3_t original_velocity; + vec3_t original_angles; + vec3_t original_avelocity; + trace_t trace; + + VectorCopy(tossent->fields.sv->origin, original_origin ); + VectorCopy(tossent->fields.sv->velocity, original_velocity ); + VectorCopy(tossent->fields.sv->angles, original_angles ); + VectorCopy(tossent->fields.sv->avelocity, original_avelocity); + + gravity *= sv_gravity->value * 0.05; + + for (i = 0; i < 200; i++) // LordHavoc: sanity check; never trace more than 10 seconds + { + SV_CheckVelocity (tossent); + tossent->fields.sv->velocity[2] -= gravity; + VectorMA (tossent->fields.sv->angles, 0.05, tossent->fields.sv->avelocity, tossent->fields.sv->angles); + VectorScale (tossent->fields.sv->velocity, 0.05, move); + VectorAdd (tossent->fields.sv->origin, move, end); + trace = SV_Trace(tossent->fields.sv->origin, tossent->fields.sv->mins, tossent->fields.sv->maxs, end, tossent, MASK_ALL ); + VectorCopy (trace.endpos, tossent->fields.sv->origin); + + if (trace.fraction < 1) break; + } + + VectorCopy(original_origin, tossent->fields.sv->origin ); + VectorCopy(original_velocity, tossent->fields.sv->velocity ); + VectorCopy(original_angles, tossent->fields.sv->angles ); + VectorCopy(original_avelocity, tossent->fields.sv->avelocity); + + return trace; +} + +/* +============ +SV_TestEntityPosition + +returns true if the entity is in solid currently +============ +*/ +int SV_TestEntityPosition (prvm_edict_t *ent) +{ + trace_t trace = SV_Trace(ent->fields.sv->origin, ent->fields.sv->mins, ent->fields.sv->maxs, ent->fields.sv->origin, ent, MASK_ALL); + + if (trace.contents & MASK_SOLID) + return true; + return false; +} + +/* +============= +SV_EntityThink + +Runs thinking code if time. There is some play in the exact time the think +function will be called, because it is called before any movement is done +in a frame. Not used for pushmove objects, because they must be exact. +Returns false if the entity removed itself. +============= +*/ +bool SV_EntityThink (prvm_edict_t *ent) +{ + float thinktime; + + thinktime = ent->fields.sv->nextthink; + if (thinktime <= 0 || thinktime > sv.time + sv.frametime) + return true; + + // don't let things stay in the past. + // it is possible to start that way by a trigger with a local time. + if (thinktime < sv.time) thinktime = sv.time; + + // reset nextthink, it will be restored in qc-code + ent->fields.sv->nextthink = 0; + prog->globals.server->time = thinktime; + prog->globals.server->self = PRVM_EDICT_TO_PROG(ent); + prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts); + PRVM_ExecuteProgram (ent->fields.sv->think, "QC function self.think is missing"); + return !ent->priv.sv->free; +} + +/* +================== +SV_Impact + +Two entities have touched, so run their touch functions +================== +*/ +void SV_Impact (prvm_edict_t *e1, trace_t *trace) +{ + int old_self, old_other; + prvm_edict_t *e2 = (prvm_edict_t *)trace->ent; + + old_self = prog->globals.server->self; + old_other = prog->globals.server->other; + + prog->globals.server->time = sv.time; + if (!e1->priv.sv->free && !e2->priv.sv->free && e1->fields.sv->touch && e1->fields.sv->solid != SOLID_NOT) + { + prog->globals.server->self = PRVM_EDICT_TO_PROG(e1); + prog->globals.server->other = PRVM_EDICT_TO_PROG(e2); + prog->globals.server->trace_allsolid = trace->allsolid; + prog->globals.server->trace_startsolid = trace->startsolid; + prog->globals.server->trace_fraction = trace->fraction; + prog->globals.server->trace_inwater = (trace->contents & MASK_WATER) ? true : false; + prog->globals.server->trace_inopen = (trace->contents & MASK_SHOT) ? true : false; + VectorCopy (trace->endpos, prog->globals.server->trace_endpos); + VectorCopy (trace->plane.normal, prog->globals.server->trace_plane_normal); + prog->globals.server->trace_plane_dist = trace->plane.dist; + if (trace->ent) prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace->ent); + else prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts); + PRVM_ExecuteProgram (e1->fields.sv->touch, "QC function self.touch is missing"); + } + if (!e1->priv.sv->free && !e2->priv.sv->free && e2->fields.sv->touch && e2->fields.sv->solid != SOLID_NOT) + { + prog->globals.server->self = PRVM_EDICT_TO_PROG(e2); + prog->globals.server->other = PRVM_EDICT_TO_PROG(e1); + prog->globals.server->trace_allsolid = false; + prog->globals.server->trace_startsolid = false; + prog->globals.server->trace_fraction = 1; + prog->globals.server->trace_inwater = false; + prog->globals.server->trace_inopen = true; + VectorCopy (e2->fields.sv->origin, prog->globals.server->trace_endpos); + VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1); + prog->globals.server->trace_plane_dist = 0; + prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1); + PRVM_ExecuteProgram (e2->fields.sv->touch, "QC function self.touch is missing"); + } + prog->globals.server->self = old_self; + prog->globals.server->other = old_other; +} + + +/* +================== +ClipVelocity + +Slide off of the impacting object +returns the blocked flags (1 = floor, 2 = step / wall) +================== +*/ +void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce) +{ + int i; + float backoff; + + backoff = -DotProduct (in, normal) * overbounce; + VectorMA(in, backoff, normal, out); + + for (i = 0;i < 3;i++) + if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) + out[i] = 0; +} + +/* +============ +SV_FlyMove + +The basic solid body movement clip that slides along multiple planes +Returns the clipflags if the velocity was modified (hit something solid) +1 = floor +2 = wall / step +4 = dead stop +If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored +============ +*/ +#define MAX_CLIP_PLANES 32 + +int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal) +{ + int blocked = 0, bumpcount; + int i, j, impact, numplanes; + float d, time_left; + vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity; + trace_t trace; + + VectorCopy(ent->fields.sv->velocity, original_velocity); + VectorCopy(ent->fields.sv->velocity, primal_velocity); + numplanes = 0; + time_left = time; + + for (bumpcount = 0; bumpcount < MAX_CLIP_PLANES; bumpcount++) + { + if (!ent->fields.sv->velocity[0] && !ent->fields.sv->velocity[1] && !ent->fields.sv->velocity[2]) + break; + + VectorMA(ent->fields.sv->origin, time_left, ent->fields.sv->velocity, end); + trace = SV_Trace(ent->fields.sv->origin, ent->fields.sv->mins, ent->fields.sv->maxs, end, ent, 0); + + // break if it moved the entire distance + if (trace.fraction == 1) + { + VectorCopy(trace.endpos, ent->fields.sv->origin); + break; + } + + if (!trace.ent) + { + MsgWarn("SV_FlyMove: !trace.ent"); + trace.ent = prog->edicts; + } + + if (((int) ent->fields.sv->flags & FL_ONGROUND) && ent->fields.sv->groundentity == PRVM_EDICT_TO_PROG(trace.ent)) + { + impact = false; + } + else + { + ent->fields.sv->flags = (int)ent->fields.sv->flags & ~FL_ONGROUND; + impact = true; + } + + if (trace.plane.normal[2]) + { + if (trace.plane.normal[2] > 0.7) + { + blocked |= 1; // floor + ent->fields.sv->flags = (int)ent->fields.sv->flags | FL_ONGROUND; + ent->fields.sv->groundentity = PRVM_EDICT_TO_PROG(trace.ent); + } + } + else + { + blocked |= 2; // step + // save the trace for player extrafriction + if (stepnormal) VectorCopy(trace.plane.normal, stepnormal); + } + + if (trace.fraction >= 0.001) + { + // actually covered some distance + VectorCopy(trace.endpos, ent->fields.sv->origin); + VectorCopy(ent->fields.sv->velocity, original_velocity); + numplanes = 0; + } + + // run the impact function + if (impact) + { + SV_Impact(ent, &trace); + + // break if removed by the impact function + if (ent->priv.sv->free) break; + } + + time_left *= 1 - trace.fraction; + + // clipped to another plane + if (numplanes >= MAX_CLIP_PLANES) + { + // this shouldn't really happen + VectorClear(ent->fields.sv->velocity); + blocked = 3; + break; + } + + VectorCopy(trace.plane.normal, planes[numplanes]); + numplanes++; + + // modify original_velocity so it parallels all of the clip planes + for (i = 0;i < numplanes;i++) + { + ClipVelocity(original_velocity, planes[i], new_velocity, 1); + for (j = 0; j < numplanes; j++) + { + if (j != i) // not ok + { + if (DotProduct(new_velocity, planes[j]) < 0) + break; + } + } + if (j == numplanes) break; + } + + if (i != numplanes) + { + // go along this plane + VectorCopy(new_velocity, ent->fields.sv->velocity); + } + else + { + // go along the crease + if (numplanes != 2) + { + VectorClear(ent->fields.sv->velocity); + blocked = 7; + break; + } + + CrossProduct(planes[0], planes[1], dir); + VectorNormalize(dir); + d = DotProduct(dir, ent->fields.sv->velocity); + VectorScale(dir, d, ent->fields.sv->velocity); + } + + // if current velocity is against the original velocity, + // stop dead to avoid tiny occilations in sloping corners + if (DotProduct(ent->fields.sv->velocity, primal_velocity) <= 0) + { + VectorClear(ent->fields.sv->velocity); + break; + } + } + + if ((int)ent->fields.sv->flags & FL_WATERJUMP) //from QuakeWorld + VectorCopy(primal_velocity, ent->fields.sv->velocity); + return blocked; +} + +/* +============ +SV_AddGravity + +============ +*/ +void SV_AddGravity (prvm_edict_t *ent) +{ + float ent_gravity = 1.0; + + ent->fields.sv->velocity[2] -= ent_gravity * sv_gravity->value * sv.frametime; +} + +/* +=============================================================================== + +PUSHMOVE + +=============================================================================== +*/ +/* +============ +SV_PushEntity + +Does not change the entities velocity at all +============ +*/ +trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, bool failonbmodelstartsolid) +{ + trace_t trace; + vec3_t end; + + VectorAdd (ent->fields.sv->origin, push, end); + + trace = SV_Trace(ent->fields.sv->origin, ent->fields.sv->mins, ent->fields.sv->maxs, end, ent, 0 ); + + if (trace.startstuck && failonbmodelstartsolid) return trace; + VectorCopy (trace.endpos, ent->fields.sv->origin); + SV_LinkEdict (ent); + + if (ent->fields.sv->solid >= SOLID_TRIGGER && trace.ent && (!((int)ent->fields.sv->flags & FL_ONGROUND) || ent->fields.sv->groundentity != PRVM_EDICT_TO_PROG(trace.ent))) + SV_Impact (ent, &trace); + return trace; +} + +/* +============ +SV_PushMove + +============ +*/ +void SV_PushMove (prvm_edict_t *pusher, float movetime) +{ + int i, e, index; + float savesolid, movetime2, pushltime; + vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org; + int num_moved; + int numcheckentities; + static prvm_edict_t *checkentities[MAX_EDICTS]; + cmodel_t *pushermodel; + trace_t trace; + + if (VectorIsNull(pusher->fields.sv->velocity) && VectorIsNull(pusher->fields.sv->avelocity)) + { + pusher->fields.sv->ltime += movetime; + return; + } + + switch ((int)pusher->fields.sv->solid) + { + // LordHavoc: valid pusher types + case SOLID_BSP: + case SOLID_BBOX: + break; + // LordHavoc: no collisions + case SOLID_NOT: + case SOLID_TRIGGER: + VectorMA (pusher->fields.sv->origin, movetime, pusher->fields.sv->velocity, pusher->fields.sv->origin); + VectorMA (pusher->fields.sv->angles, movetime, pusher->fields.sv->avelocity, pusher->fields.sv->angles); + pusher->fields.sv->angles[0] -= 360.0 * floor(pusher->fields.sv->angles[0] * (1.0 / 360.0)); + pusher->fields.sv->angles[1] -= 360.0 * floor(pusher->fields.sv->angles[1] * (1.0 / 360.0)); + pusher->fields.sv->angles[2] -= 360.0 * floor(pusher->fields.sv->angles[2] * (1.0 / 360.0)); + pusher->fields.sv->ltime += movetime; + SV_LinkEdict (pusher); + return; + default: + MsgWarn("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.sv->solid); + return; + } + + index = (int) pusher->fields.sv->modelindex; + if (index < 1 || index >= MAX_MODELS) + { + MsgWarn("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.sv->modelindex); + return; + } + pushermodel = sv.models[index]; + + movetime2 = movetime; + VectorScale(pusher->fields.sv->velocity, movetime2, move1); + VectorScale(pusher->fields.sv->avelocity, movetime2, moveangle); + if (moveangle[0] || moveangle[2]) + { + for (i = 0; i < 3; i++) + { + if (move1[i] > 0) + { + mins[i] = pushermodel->rotatedmins[i] + pusher->fields.sv->origin[i] - 1; + maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.sv->origin[i] + 1; + } + else + { + mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.sv->origin[i] - 1; + maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.sv->origin[i] + 1; + } + } + } + else if (moveangle[1]) + { + for (i = 0; i < 3; i++) + { + if (move1[i] > 0) + { + mins[i] = pushermodel->yawmins[i] + pusher->fields.sv->origin[i] - 1; + maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.sv->origin[i] + 1; + } + else + { + mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.sv->origin[i] - 1; + maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.sv->origin[i] + 1; + } + } + } + else + { + for (i = 0; i < 3; i++) + { + if (move1[i] > 0) + { + mins[i] = pushermodel->normalmins[i] + pusher->fields.sv->origin[i] - 1; + maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.sv->origin[i] + 1; + } + else + { + mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.sv->origin[i] - 1; + maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.sv->origin[i] + 1; + } + } + } + + VectorNegate (moveangle, a); + AngleVectorsLeft(a, forward, left, up); + + VectorCopy (pusher->fields.sv->origin, pushorig); + VectorCopy (pusher->fields.sv->angles, pushang); + pushltime = pusher->fields.sv->ltime; + + // move the pusher to its final position + VectorMA (pusher->fields.sv->origin, movetime, pusher->fields.sv->velocity, pusher->fields.sv->origin); + VectorMA (pusher->fields.sv->angles, movetime, pusher->fields.sv->avelocity, pusher->fields.sv->angles); + pusher->fields.sv->ltime += movetime; + SV_LinkEdict (pusher); + + savesolid = pusher->fields.sv->solid; + + // see if any solid entities are inside the final position + num_moved = 0; + + numcheckentities = 0;//SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities); + for (e = 0;e < numcheckentities; e++) + { + prvm_edict_t *check = checkentities[e]; + if (check->fields.sv->movetype == MOVETYPE_NONE || check->fields.sv->movetype == MOVETYPE_PUSH || check->fields.sv->movetype == MOVETYPE_FOLLOW || check->fields.sv->movetype == MOVETYPE_NOCLIP) + continue; + + // if the entity is standing on the pusher, it will definitely be moved + if (((int)check->fields.sv->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.sv->groundentity) == pusher) + { + // remove the onground flag for non-players + if (check->fields.sv->movetype != MOVETYPE_WALK) + check->fields.sv->flags = (int)check->fields.sv->flags & ~FL_ONGROUND; + } + else + { + // if the entity is not inside the pusher's final position, leave it alone + if (!SV_ClipMoveToEntity(pusher, check->fields.sv->origin, check->fields.sv->mins, check->fields.sv->maxs, check->fields.sv->origin, MASK_SOLID).startsolid) + continue; + } + + if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used + { + vec3_t org2; + VectorSubtract (check->fields.sv->origin, pusher->fields.sv->origin, org); + org2[0] = DotProduct (org, forward); + org2[1] = DotProduct (org, left); + org2[2] = DotProduct (org, up); + VectorSubtract (org2, org, move); + VectorAdd (move, move1, move); + } + else + VectorCopy (move1, move); + + VectorCopy (check->fields.sv->origin, check->priv.sv->moved_from); + VectorCopy (check->fields.sv->angles, check->priv.sv->moved_fromangles); + sv.moved_edicts[num_moved++] = check; + + // try moving the contacted entity + pusher->fields.sv->solid = SOLID_NOT; + trace = SV_PushEntity (check, move, true); + + // FIXME: turn players specially + check->fields.sv->angles[1] += trace.fraction * moveangle[1]; + pusher->fields.sv->solid = savesolid; // was SOLID_BSP + + // if it is still inside the pusher, block + if (SV_ClipMoveToEntity(pusher, check->fields.sv->origin, check->fields.sv->mins, check->fields.sv->maxs, check->fields.sv->origin, MASK_SOLID).startsolid) + { + // try moving the contacted entity a tiny bit further to account for precision errors + vec3_t move2; + pusher->fields.sv->solid = SOLID_NOT; + VectorScale(move, 1.1, move2); + VectorCopy (check->priv.sv->moved_from, check->fields.sv->origin); + VectorCopy (check->priv.sv->moved_fromangles, check->fields.sv->angles); + SV_PushEntity (check, move2, true); + pusher->fields.sv->solid = savesolid; + if (SV_ClipMoveToEntity(pusher, check->fields.sv->origin, check->fields.sv->mins, check->fields.sv->maxs, check->fields.sv->origin, MASK_SOLID).startsolid) + { + // try moving the contacted entity a tiny bit less to account for precision errors + pusher->fields.sv->solid = SOLID_NOT; + VectorScale(move, 0.9, move2); + VectorCopy (check->priv.sv->moved_from, check->fields.sv->origin); + VectorCopy (check->priv.sv->moved_fromangles, check->fields.sv->angles); + SV_PushEntity (check, move2, true); + pusher->fields.sv->solid = savesolid; + if (SV_ClipMoveToEntity(pusher, check->fields.sv->origin, check->fields.sv->mins, check->fields.sv->maxs, check->fields.sv->origin, MASK_SOLID).startsolid) + { + // still inside pusher, so it's really blocked + + // fail the move + if (check->fields.sv->mins[0] == check->fields.sv->maxs[0]) + continue; + if (check->fields.sv->solid == SOLID_NOT || check->fields.sv->solid == SOLID_TRIGGER) + { + // corpse + check->fields.sv->mins[0] = check->fields.sv->mins[1] = 0; + VectorCopy (check->fields.sv->mins, check->fields.sv->maxs); + continue; + } + + VectorCopy (pushorig, pusher->fields.sv->origin); + VectorCopy (pushang, pusher->fields.sv->angles); + pusher->fields.sv->ltime = pushltime; + SV_LinkEdict (pusher); + + // move back any entities we already moved + for (i = 0;i < num_moved;i++) + { + prvm_edict_t *ed = sv.moved_edicts[i]; + VectorCopy (ed->priv.sv->moved_from, ed->fields.sv->origin); + VectorCopy (ed->priv.sv->moved_fromangles, ed->fields.sv->angles); + SV_LinkEdict (ed); + } + + // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone + if (pusher->fields.sv->blocked) + { + prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher); + prog->globals.server->other = PRVM_EDICT_TO_PROG(check); + PRVM_ExecuteProgram (pusher->fields.sv->blocked, "QC function self.blocked is missing"); + } + break; + } + } + } + } + pusher->fields.sv->angles[0] -= 360.0 * floor(pusher->fields.sv->angles[0] * (1.0 / 360.0)); + pusher->fields.sv->angles[1] -= 360.0 * floor(pusher->fields.sv->angles[1] * (1.0 / 360.0)); + pusher->fields.sv->angles[2] -= 360.0 * floor(pusher->fields.sv->angles[2] * (1.0 / 360.0)); +} + +/* +================ +SV_PhysicsPusher + +================ +*/ +void SV_PhysicsPusher (prvm_edict_t *ent) +{ + float thinktime, oldltime, movetime; + + oldltime = ent->fields.sv->ltime; + + thinktime = ent->fields.sv->nextthink; + if (thinktime < ent->fields.sv->ltime + sv.frametime) + { + movetime = thinktime - ent->fields.sv->ltime; + if (movetime < 0) movetime = 0; + } + else movetime = sv.frametime; + + if (movetime) + { + // advances ent->fields.sv->ltime if not blocked + SV_PushMove(ent, movetime); + } + + if (thinktime > oldltime && thinktime <= ent->fields.sv->ltime) + { + // called QC think function + ent->fields.sv->nextthink = 0; + prog->globals.server->time = sv.time; + prog->globals.server->self = PRVM_EDICT_TO_PROG(ent); + prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts); + PRVM_ExecuteProgram (ent->fields.sv->think, "QC function self.think is missing"); + } +} + +/* +=============================================================================== + +CLIENT MOVEMENT + +=============================================================================== +*/ +/* +============= +SV_CheckStuck + +This is a big hack to try and fix the rare case of getting stuck in the world +clipping hull. +============= +*/ +void SV_CheckStuck (prvm_edict_t *ent) +{ + int x, y, z; + vec3_t org; + + if (!SV_TestEntityPosition(ent)) + { + VectorCopy (ent->fields.sv->origin, ent->fields.sv->oldorigin); + return; + } + + VectorCopy (ent->fields.sv->origin, org); + VectorCopy (ent->fields.sv->oldorigin, ent->fields.sv->origin); + + if(!SV_TestEntityPosition(ent)) + { + Msg("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.sv->classname)); + SV_LinkEdict (ent); + return; + } + + for (z = -1; z < 18; z++) + { + for (x = -1; x <= 1; x++) + { + for (y =-1; y <= 1; y++) + { + ent->fields.sv->origin[0] = org[0] + x; + ent->fields.sv->origin[1] = org[1] + y; + ent->fields.sv->origin[2] = org[2] + z; + if (!SV_TestEntityPosition(ent)) + { + Msg("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.sv->classname), (float)x, (float)y, (float)z); + SV_LinkEdict (ent); + return; + } + } + } + } + + VectorCopy (org, ent->fields.sv->origin); + Msg("Stuck player %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.sv->classname)); +} + +void SV_UnstickEntity (prvm_edict_t *ent) +{ + int x, y, z; + vec3_t org; + + // if not stuck in a bmodel, just return + if (!SV_TestEntityPosition(ent)) + return; + + VectorCopy (ent->fields.sv->origin, org); + + for (z = -1; z < 18; z += 6) + { + for (x = -1; x <= 1; x++) + { + for (y =-1; y <= 1; y++) + { + ent->fields.sv->origin[0] = org[0] + x; + ent->fields.sv->origin[1] = org[1] + y; + ent->fields.sv->origin[2] = org[2] + z; + if (!SV_TestEntityPosition(ent)) + { + Msg("Unstuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.sv->classname), (float)x, (float)y, (float)z); + SV_LinkEdict(ent); + return; + } + } + } + } + + VectorCopy (org, ent->fields.sv->origin); + Msg("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.sv->classname)); +} + +/* +============= +SV_CheckWater +============= +*/ +bool SV_CheckWater (prvm_edict_t *ent) +{ + int cont; + vec3_t point; + + point[0] = ent->fields.sv->origin[0]; + point[1] = ent->fields.sv->origin[1]; + point[2] = ent->fields.sv->origin[2] + ent->fields.sv->mins[2] + 1; + + ent->fields.sv->waterlevel = 0; + ent->fields.sv->watertype = CONTENTS_NONE; + cont = SV_PointContents(point); + + if (cont & MASK_WATER) + { + ent->fields.sv->watertype = cont; + ent->fields.sv->waterlevel = 1; + point[2] = ent->fields.sv->origin[2] + (ent->fields.sv->mins[2] + ent->fields.sv->maxs[2]) * 0.5; + + if(SV_PointContents(point) & MASK_WATER) + { + ent->fields.sv->waterlevel = 2; + point[2] = ent->fields.sv->origin[2] + ent->fields.sv->view_ofs[2]; + if (SV_PointContents(point) & MASK_WATER) ent->fields.sv->waterlevel = 3; + } + } + + return ent->fields.sv->waterlevel > 1; +} + +/* +============ +SV_WallFriction + +============ +*/ +void SV_WallFriction (prvm_edict_t *ent, float *stepnormal) +{ + float d, i; + vec3_t forward, into, side; + + AngleVectorsRight(ent->fields.sv->v_angle, forward, NULL, NULL); + + if ((d = DotProduct (stepnormal, forward) + 0.5) < 0) + { + // cut the tangential velocity + i = DotProduct (stepnormal, ent->fields.sv->velocity); + VectorScale (stepnormal, i, into); + VectorSubtract (ent->fields.sv->velocity, into, side); + ent->fields.sv->velocity[0] = side[0] * (1 + d); + ent->fields.sv->velocity[1] = side[1] * (1 + d); + } +} + +/* +===================== +SV_TryUnstick + +Player has come to a dead stop, possibly due to the problem with limited +float precision at some angle joins in the BSP hull. + +Try fixing by pushing one pixel in each direction. + +This is a hack, but in the interest of good gameplay... +====================== +*/ +int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel) +{ + int i, clip; + vec3_t oldorg, dir; + + VectorCopy (ent->fields.sv->origin, oldorg); + VectorClear (dir); + + for (i = 0; i < 8; i++) + { + // try pushing a little in an axial direction + switch (i) + { + case 0: dir[0] = 2; dir[1] = 0; break; + case 1: dir[0] = 0; dir[1] = 2; break; + case 2: dir[0] = -2; dir[1] = 0; break; + case 3: dir[0] = 0; dir[1] = -2; break; + case 4: dir[0] = 2; dir[1] = 2; break; + case 5: dir[0] = -2; dir[1] = 2; break; + case 6: dir[0] = 2; dir[1] = -2; break; + case 7: dir[0] = -2; dir[1] = -2; break; + } + + SV_PushEntity (ent, dir, false); + + // retry the original move + ent->fields.sv->velocity[0] = oldvel[0]; + ent->fields.sv->velocity[1] = oldvel[1]; + ent->fields.sv->velocity[2] = 0; + clip = SV_FlyMove (ent, 0.1, NULL); + + if (fabs(oldorg[1] - ent->fields.sv->origin[1]) > 4 || fabs(oldorg[0] - ent->fields.sv->origin[0]) > 4) + { + Msg("SV_TryUnstick: success.\n"); + return clip; + } + // go back to the original pos and try again + VectorCopy (oldorg, ent->fields.sv->origin); + } + + // still not moving + VectorClear (ent->fields.sv->velocity); + Msg("SV_TryUnstick: failure.\n"); + return 7; +} + +/* +===================== +SV_WalkMove + +Only used by players +====================== +*/ +void SV_WalkMove(prvm_edict_t *ent) +{ + int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity; + vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity; + trace_t downtrace; + + SV_CheckVelocity(ent); + + // do a regular slide move unless it looks like you ran into a step + oldonground = (int)ent->fields.sv->flags & FL_ONGROUND; + ent->fields.sv->flags = (int)ent->fields.sv->flags & ~FL_ONGROUND; + + VectorCopy (ent->fields.sv->origin, start_origin); + VectorCopy (ent->fields.sv->velocity, start_velocity); + + clip = SV_FlyMove (ent, sv.frametime, NULL); + + SV_CheckVelocity(ent); + + VectorCopy(ent->fields.sv->origin, originalmove_origin); + VectorCopy(ent->fields.sv->velocity, originalmove_velocity); + originalmove_clip = clip; + originalmove_flags = (int)ent->fields.sv->flags; + originalmove_groundentity = ent->fields.sv->groundentity; + + if ((int)ent->fields.sv->flags & FL_WATERJUMP) + return; + + // if move didn't block on a step, return + if (clip & 2) + { + // if move was not trying to move into the step, return + if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125) + return; + + if (ent->fields.sv->movetype != MOVETYPE_FLY) + { + // return if gibbed by a trigger + if (ent->fields.sv->movetype != MOVETYPE_WALK) + return; + } + + // try moving up and forward to go up a step + // back to start pos + VectorCopy (start_origin, ent->fields.sv->origin); + VectorCopy (start_velocity, ent->fields.sv->velocity); + + // move up + VectorClear (upmove); + upmove[2] = 18; + + // FIXME: don't link? + SV_PushEntity(ent, upmove, false); + + // move forward + ent->fields.sv->velocity[2] = 0; + clip = SV_FlyMove (ent, sv.frametime, stepnormal); + ent->fields.sv->velocity[2] += start_velocity[2]; + + SV_CheckVelocity(ent); + + // check for stuckness, possibly due to the limited precision of floats + // in the clipping hulls + if (clip && fabs(originalmove_origin[1] - ent->fields.sv->origin[1]) < 0.03125 && fabs(originalmove_origin[0] - ent->fields.sv->origin[0]) < 0.03125) + { + Msg("wall\n"); + // stepping up didn't make any progress, revert to original move + VectorCopy(originalmove_origin, ent->fields.sv->origin); + VectorCopy(originalmove_velocity, ent->fields.sv->velocity); + ent->fields.sv->flags = originalmove_flags; + ent->fields.sv->groundentity = originalmove_groundentity; + return; + } + + //Msg("step - "); + + // extra friction based on view angle + if (clip & 2) SV_WallFriction (ent, stepnormal); + } + // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground + else if (ent->fields.sv->waterlevel < 2 && start_velocity[2] < (1.0 / 32.0) && oldonground && !((int)ent->fields.sv->flags & FL_ONGROUND)) + return; + + // move down + VectorClear (downmove); + downmove[2] = -18 + start_velocity[2] * sv.frametime; + + // FIXME: don't link? + downtrace = SV_PushEntity (ent, downmove, false); + + if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7) + { + // this has been disabled so that you can't jump when you are stepping + // up while already jumping (also known as the Quake2 stair jump bug) + } + else + { + Msg("slope\n"); + + // if the push down didn't end up on good ground, use the move without + // the step up. This happens near wall / slope combinations, and can + // cause the player to hop up higher on a slope too steep to climb + VectorCopy(originalmove_origin, ent->fields.sv->origin); + VectorCopy(originalmove_velocity, ent->fields.sv->velocity); + ent->fields.sv->flags = originalmove_flags; + ent->fields.sv->groundentity = originalmove_groundentity; + } + + SV_CheckVelocity(ent); +} + +/* +============= +SV_Physics_Follow + +Entities that are "stuck" to another entity +============= +*/ +void SV_PhysicsFollow (prvm_edict_t *ent) +{ + vec3_t vf, vr, vu, angles, v; + prvm_edict_t *e; + + // regular thinking + if (!SV_EntityThink (ent)) return; + + e = PRVM_PROG_TO_EDICT(ent->fields.sv->aiment); + if (VectorCompare(e->fields.sv->angles, ent->fields.sv->punchangle )) + { + // quick case for no rotation + VectorAdd(e->fields.sv->origin, ent->fields.sv->view_ofs, ent->fields.sv->origin); + } + else + { + angles[0] = -ent->fields.sv->punchangle[0]; + angles[1] = ent->fields.sv->punchangle[1]; + angles[2] = ent->fields.sv->punchangle[2]; + + AngleVectorsRight(angles, vf, vr, vu); + + v[0] = ent->fields.sv->view_ofs[0] * vf[0] + ent->fields.sv->view_ofs[1] * vr[0] + ent->fields.sv->view_ofs[2] * vu[0]; + v[1] = ent->fields.sv->view_ofs[0] * vf[1] + ent->fields.sv->view_ofs[1] * vr[1] + ent->fields.sv->view_ofs[2] * vu[1]; + v[2] = ent->fields.sv->view_ofs[0] * vf[2] + ent->fields.sv->view_ofs[1] * vr[2] + ent->fields.sv->view_ofs[2] * vu[2]; + angles[0] = -e->fields.sv->angles[0]; // stupid quake legacy + angles[1] = e->fields.sv->angles[1]; + angles[2] = e->fields.sv->angles[2]; + + AngleVectorsRight(angles, vf, vr, vu); + + ent->fields.sv->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.sv->origin[0]; + ent->fields.sv->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.sv->origin[1]; + ent->fields.sv->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.sv->origin[2]; + } + VectorAdd (e->fields.sv->angles, ent->fields.sv->v_angle, ent->fields.sv->angles); + SV_LinkEdict (ent); +} + +/* +============================================================================== + +TOSS / BOUNCE + +============================================================================== +*/ +/* +============= +SV_CheckWaterTransition + +============= +*/ +void SV_CheckWaterTransition (prvm_edict_t *ent) +{ + int cont = SV_PointContents(ent->fields.sv->origin); + + if (!ent->fields.sv->watertype) + { + // just spawned here + ent->fields.sv->watertype = cont; + ent->fields.sv->waterlevel = 1; + return; + } + + if (cont & MASK_WATER) + { + ent->fields.sv->watertype = cont; + ent->fields.sv->waterlevel = 1; + } + else + { + ent->fields.sv->watertype = CONTENTS_NONE; + ent->fields.sv->waterlevel = 0; + } +} + + +/* +============= +SV_PhysicsToss + +Toss, bounce, and fly movement. When onground, do nothing. +============= +*/ +void SV_PhysicsToss (prvm_edict_t *ent) +{ + trace_t trace; + vec3_t move; + + // if onground, return without moving + if ((int)ent->fields.sv->flags & FL_ONGROUND) + { + if (ent->fields.sv->velocity[2] >= (1.0 / 32.0)) + { + // don't stick to ground if onground and moving upward + ent->fields.sv->flags -= FL_ONGROUND; + } + else if (!ent->fields.sv->groundentity) + { + // we can trust FL_ONGROUND if groundentity is world because it never moves + return; + } + } + SV_CheckVelocity (ent); + + // add gravity + if (ent->fields.sv->movetype == MOVETYPE_TOSS || ent->fields.sv->movetype == MOVETYPE_BOUNCE) + SV_AddGravity (ent); + + // move angles + VectorMA (ent->fields.sv->angles, sv.frametime, ent->fields.sv->avelocity, ent->fields.sv->angles); + + // move origin + VectorScale (ent->fields.sv->velocity, sv.frametime, move); + trace = SV_PushEntity (ent, move, true); + if (ent->priv.sv->free) return; + + if (trace.startstuck) + { + // try to unstick the entity + SV_UnstickEntity(ent); + trace = SV_PushEntity (ent, move, false); + if (ent->priv.sv->free) return; + } + + if (trace.fraction < 1) + { + if (ent->fields.sv->movetype == MOVETYPE_BOUNCE) + { + float d; + ClipVelocity (ent->fields.sv->velocity, trace.plane.normal, ent->fields.sv->velocity, 1.5); + + d = DotProduct(trace.plane.normal, ent->fields.sv->velocity); + if (trace.plane.normal[2] > 0.7 && fabs(d) < 60) + { + ent->fields.sv->flags = (int)ent->fields.sv->flags | FL_ONGROUND; + ent->fields.sv->groundentity = PRVM_EDICT_TO_PROG(trace.ent); + VectorClear (ent->fields.sv->velocity); + VectorClear (ent->fields.sv->avelocity); + } + else ent->fields.sv->flags = (int)ent->fields.sv->flags & ~FL_ONGROUND; + } + else + { + ClipVelocity (ent->fields.sv->velocity, trace.plane.normal, ent->fields.sv->velocity, 1.0); + if (trace.plane.normal[2] > 0.7) + { + ent->fields.sv->flags = (int)ent->fields.sv->flags | FL_ONGROUND; + ent->fields.sv->groundentity = PRVM_EDICT_TO_PROG(trace.ent); + VectorClear (ent->fields.sv->velocity); + VectorClear (ent->fields.sv->avelocity); + } + else ent->fields.sv->flags = (int)ent->fields.sv->flags & ~FL_ONGROUND; + } + } + + // check for in water + SV_CheckWaterTransition (ent); +} + +/* +=============================================================================== + +STEPPING MOVEMENT + +=============================================================================== +*/ +/* +============= +SV_Physics_Step + +Monsters freefall when they don't have a ground entity, otherwise +all movement is done with discrete steps. + +This is also used for objects that have become still on the ground, but +will fall if the floor is pulled out from under them. +============= +*/ +void SV_PhysicsStep (prvm_edict_t *ent) +{ + int flags = (int)ent->fields.sv->flags; + + // don't fall at all if fly/swim + if (!(flags & (FL_FLY | FL_SWIM))) + { + if (flags & FL_ONGROUND) + { + // freefall if onground and moving upward + // freefall if not standing on a world surface (it may be a lift or trap door) + if (ent->fields.sv->velocity[2] >= (1.0 / 32.0) || ent->fields.sv->groundentity) + { + ent->fields.sv->flags -= FL_ONGROUND; + SV_AddGravity(ent); + SV_CheckVelocity(ent); + SV_FlyMove(ent, sv.frametime, NULL); + SV_LinkEdict(ent); + } + } + else + { + // freefall if not onground + int hitsound = ent->fields.sv->velocity[2] < sv_gravity->value * -0.1; + + SV_AddGravity(ent); + SV_CheckVelocity(ent); + SV_FlyMove(ent, sv.frametime, NULL); + SV_LinkEdict(ent); + } + } + + // regular thinking + SV_EntityThink(ent); + SV_CheckWaterTransition(ent); +} + +void SV_PhysicsClient(prvm_edict_t *ent) +{ + SV_ApplyClientMove(); + SV_CheckVelocity(ent); // make sure the velocity is sane (not a NaN) + SV_ClientThink(); + SV_CheckVelocity(ent); // make sure the velocity is sane (not a NaN) + + // call standard client pre-think + prog->globals.server->time = sv.time; + prog->globals.server->self = PRVM_EDICT_TO_PROG(ent); + PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing"); + SV_CheckVelocity (ent); + + switch ((int)ent->fields.sv->movetype) + { + case MOVETYPE_PUSH: + SV_PhysicsPusher(ent); + break; + case MOVETYPE_NONE: + if (ent->fields.sv->nextthink > 0 && ent->fields.sv->nextthink <= sv.time + sv.frametime) + SV_EntityThink(ent); + break; + case MOVETYPE_FOLLOW: + SV_PhysicsFollow(ent); + break; + case MOVETYPE_NOCLIP: + if (SV_EntityThink(ent)) + { + SV_CheckWater(ent); + VectorMA(ent->fields.sv->origin, sv.frametime, ent->fields.sv->velocity, ent->fields.sv->origin); + VectorMA(ent->fields.sv->angles, sv.frametime, ent->fields.sv->avelocity, ent->fields.sv->angles); + } + break; + case MOVETYPE_STEP: + SV_PhysicsStep(ent); + break; + case MOVETYPE_WALK: + if (SV_EntityThink(ent)) + { + if(!SV_CheckWater(ent) && ! ((int)ent->fields.sv->flags & FL_WATERJUMP)) + SV_AddGravity (ent); + SV_CheckStuck (ent); + SV_WalkMove (ent); + } + break; + case MOVETYPE_TOSS: + case MOVETYPE_BOUNCE: + if (SV_EntityThink (ent)) + SV_PhysicsToss (ent); + break; + case MOVETYPE_FLY: + if(SV_EntityThink (ent)) + { + SV_CheckWater(ent); + SV_WalkMove(ent); + } + break; + default: + MsgWarn("SV_PhysicsClient: bad movetype %i\n", (int)ent->fields.sv->movetype); + break; + } + + SV_CheckVelocity (ent); + SV_LinkEdict(ent); + SV_CheckVelocity(ent); + + // call standard player post-think + prog->globals.server->time = sv.time; + prog->globals.server->self = PRVM_EDICT_TO_PROG(ent); + PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing"); +} + +void SV_PhysicsEntity(prvm_edict_t *ent) +{ + // don't run a move on newly spawned projectiles as it messes up movement + // interpolation and rocket trails + bool runmove = ent->priv.sv->move; + ent->priv.sv->move = true; + + switch ((int) ent->fields.sv->movetype) + { + case MOVETYPE_PUSH: + SV_PhysicsPusher (ent); + break; + case MOVETYPE_NONE: + if (ent->fields.sv->nextthink > 0 && ent->fields.sv->nextthink <= sv.time + sv.frametime) + SV_EntityThink (ent); + break; + case MOVETYPE_FOLLOW: + SV_PhysicsFollow (ent); + break; + case MOVETYPE_NOCLIP: + if (SV_EntityThink(ent)) + { + SV_CheckWater(ent); + VectorMA(ent->fields.sv->origin, sv.frametime, ent->fields.sv->velocity, ent->fields.sv->origin); + VectorMA(ent->fields.sv->angles, sv.frametime, ent->fields.sv->avelocity, ent->fields.sv->angles); + } + SV_LinkEdict(ent); + break; + case MOVETYPE_STEP: + SV_PhysicsStep(ent); + break; + case MOVETYPE_WALK: + if(SV_EntityThink(ent)) + { + if (!SV_CheckWater (ent) && !((int)ent->fields.sv->flags & FL_WATERJUMP)) + SV_AddGravity (ent); + SV_CheckStuck (ent); + SV_WalkMove (ent); + SV_LinkEdict (ent); + } + break; + case MOVETYPE_TOSS: + case MOVETYPE_BOUNCE: + case MOVETYPE_FLY: + if (SV_EntityThink (ent) && runmove) + SV_PhysicsToss (ent); + break; + default: + MsgWarn("SV_Physics: bad movetype %i\n", (int)ent->fields.sv->movetype); + break; + } +} + +void SV_Physics (void) +{ + int i; + prvm_edict_t *ent; + + // let the progs know that a new frame has started + prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts); + prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts); + prog->globals.server->time = sv.time; + prog->globals.server->frametime = sv.frametime; + PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing"); + + // run physics on the client entities + for (i = 1, ent = PRVM_EDICT_NUM(i), sv_client = svs.clients; i <= maxclients->value; i++, ent = PRVM_NEXT_EDICT(ent), sv_client++) + { + if (!ent->priv.sv->free) + { + // don't do physics on disconnected clients, FrikBot relies on this + if (sv_client->state != cs_spawned) + memset(&sv_client->lastcmd, 0, sizeof(sv_client->lastcmd)); + else SV_PhysicsClient(ent); + } + } + for (;i < prog->num_edicts; i++, ent = PRVM_NEXT_EDICT(ent)) + { + if (!ent->priv.sv->free) + SV_PhysicsEntity(ent); + } + prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts); + prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts); + prog->globals.server->time = sv.time; + PRVM_ExecuteProgram (prog->globals.server->EndFrame, "QC function EndFrame is missing"); + + // decrement prog->num_edicts if the highest number entities died + for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.sv->free;prog->num_edicts--); + + sv.time += sv.frametime; +} \ No newline at end of file diff --git a/engine/server/sv_save.c b/engine/server/sv_save.c index 00d49979..0a877ff7 100644 --- a/engine/server/sv_save.c +++ b/engine/server/sv_save.c @@ -65,7 +65,7 @@ size_t COM_UnpackString( byte *buffer, int pos, char *string ) if(!buffer || !string) return 0; in = buffer + pos; - do { in++, strsize++; } while(*in != '\0' && in != NULL ); + do { in++, strsize++; } while(in && *in != '\0'); strlcpy( string, in - (strsize - 1), strsize ); return pos + strsize; @@ -160,10 +160,10 @@ void SV_WriteSaveFile( char *name ) SV_AddSaveLump( header, savfile, LUMP_COMMENTS, comment, sizeof(comment)); SV_AddCStrLump( header, savfile ); SV_AddSaveLump( header, savfile, LUMP_AREASTATE, portalopen, sizeof(portalopen)); - ge->WriteLump ( header, savfile, LUMP_GAMELEVEL, autosave ); +//ge->WriteLump ( header, savfile, LUMP_GAMELEVEL, autosave ); SV_AddSaveLump( header, savfile, LUMP_MAPCMDS, svs.mapcmd, sizeof(svs.mapcmd)); SV_AddCvarLump( header, savfile ); - ge->WriteLump ( header, savfile, LUMP_GAMELOCAL, autosave ); +//ge->WriteLump ( header, savfile, LUMP_GAMELOCAL, autosave ); //merge header FS_Seek( savfile, 0, SEEK_SET ); @@ -278,7 +278,7 @@ void SV_ReadSaveFile( char *name ) SV_InitGame (); // start a new game fresh with new cvars Sav_LoadMapCmds(&header->lumps[LUMP_MAPCMDS]); - ge->ReadLump( sav_base, &header->lumps[LUMP_GAMELOCAL], LUMP_GAMELOCAL ); +//ge->ReadLump( sav_base, &header->lumps[LUMP_GAMELOCAL], LUMP_GAMELOCAL ); } /* @@ -313,7 +313,7 @@ void SV_ReadLevelFile( char *name ) Sav_LoadCfgString(&header->lumps[LUMP_CFGSTRING]); Sav_LoadAreaPortals(&header->lumps[LUMP_AREASTATE]); - ge->ReadLump( sav_base, &header->lumps[LUMP_GAMELEVEL], LUMP_GAMELEVEL ); +//ge->ReadLump( sav_base, &header->lumps[LUMP_GAMELEVEL], LUMP_GAMELEVEL ); } bool Menu_ReadComment( char *comment, int savenum ) diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index 87717666..e9869316 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -135,7 +135,7 @@ void SV_BroadcastCommand (char *fmt, ...) va_list argptr; char string[1024]; - if (!sv.state) return; + if (sv.state != ss_loading) return; va_start (argptr,fmt); vsprintf (string, fmt,argptr); va_end (argptr); @@ -159,7 +159,7 @@ MULTICAST_PVS send to clients potentially visible from org MULTICAST_PHS send to clients potentially hearable from org ================= */ -void _MSG_Send (msgtype_t to, vec3_t origin, edict_t *ent, const char *filename, int fileline) +void _MSG_Send (msgtype_t to, vec3_t origin, prvm_edict_t *ent, const char *filename, int fileline) { byte *mask = NULL; int leafnum = 0, cluster = 0; @@ -208,7 +208,7 @@ void _MSG_Send (msgtype_t to, vec3_t origin, edict_t *ent, const char *filename, reliable = true; // intentional fallthrough case MSG_ONE: if(ent == NULL) return; - j = NUM_FOR_EDICT(ent); + j = PRVM_NUM_FOR_EDICT(ent); if (j < 1 || j > numclients) return; current = svs.clients + (j - 1); numclients = 1; // send to one @@ -228,9 +228,9 @@ void _MSG_Send (msgtype_t to, vec3_t origin, edict_t *ent, const char *filename, { area2 = CM_LeafArea (leafnum); cluster = CM_LeafCluster (leafnum); - leafnum = CM_PointLeafnum (client->edict->s.origin); + leafnum = CM_PointLeafnum (client->edict->priv.sv->state.origin); if (!CM_AreasConnected (area1, area2)) continue; - if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7))))) continue; + if ( mask && (!(mask[cluster>>3] & (1<<(cluster & 7))))) continue; } if (reliable) SZ_Write (&client->netchan.message, sv.multicast.data, sv.multicast.cursize); @@ -278,7 +278,7 @@ If origin is NULL, the origin is determined from the entity origin or the midpoint of the entity box for bmodels. ================== */ -void SV_StartSound (vec3_t origin, edict_t *entity, int channel, int soundindex, float volume, float attenuation, float timeofs) +void SV_StartSound (vec3_t origin, prvm_edict_t *entity, int channel, int soundindex, float volume, float attenuation, float timeofs) { int i, ent, flags, sendchan; vec3_t origin_v; @@ -299,7 +299,7 @@ void SV_StartSound (vec3_t origin, edict_t *entity, int channel, int soundindex, MsgWarn("SV_StartSound: timeofs = %f\n", timeofs); timeofs = bound(0, timeofs, 0.255 ); } - ent = NUM_FOR_EDICT(entity); + ent = PRVM_NUM_FOR_EDICT(entity); if (channel & 8) // no PHS flag { @@ -315,7 +315,7 @@ void SV_StartSound (vec3_t origin, edict_t *entity, int channel, int soundindex, // the client doesn't know that bmodels have weird origins // the origin can also be explicitly set - if ( (entity->svflags & SVF_NOCLIENT) || (entity->solid == SOLID_BSP) || origin ) + if ( (entity->priv.sv->flags & SVF_NOCLIENT) || (entity->priv.sv->solid == SOLID_BSP) || origin ) flags |= SND_POS; // always send the entity number for channel overrides @@ -327,16 +327,16 @@ void SV_StartSound (vec3_t origin, edict_t *entity, int channel, int soundindex, if (!origin) { origin = origin_v; - if (entity->solid == SOLID_BSP) + if (entity->priv.sv->solid == SOLID_BSP) { for (i = 0; i < 3; i++) { - origin_v[i] = entity->s.origin[i]+0.5*(entity->mins[i]+entity->maxs[i]); + origin_v[i] = entity->priv.sv->state.origin[i]+0.5*(entity->priv.sv->mins[i]+entity->priv.sv->maxs[i]); } } else { - VectorCopy (entity->s.origin, origin_v); + VectorCopy (entity->priv.sv->state.origin, origin_v); } } @@ -490,9 +490,9 @@ SV_SendClientMessages */ void SV_SendClientMessages (void) { - int i; - client_t *c; - int msglen; + int i; + client_t *c; + int msglen; byte msgbuf[MAX_MSGLEN]; msglen = 0; @@ -500,8 +500,7 @@ void SV_SendClientMessages (void) // read the next demo message if needed if (sv.state == ss_demo && sv.demofile) { - if (sv_paused->value) - msglen = 0; + if (sv_paused->value) msglen = 0; else { // get the next message @@ -527,10 +526,9 @@ void SV_SendClientMessages (void) } // send a message to each connected client - for (i=0, c = svs.clients ; ivalue; i++, c++) + for (i = 0, c = svs.clients; i < maxclients->value; i++, c++) { - if (!c->state) - continue; + if (!c->state) continue; // if the reliable message overflowed, // drop the client if (c->netchan.message.overflowed) @@ -541,23 +539,19 @@ void SV_SendClientMessages (void) SV_DropClient (c); } - if (sv.state == ss_cinematic - || sv.state == ss_demo - || sv.state == ss_pic - ) + if (sv.state == ss_cinematic || sv.state == ss_demo || sv.state == ss_pic) Netchan_Transmit (&c->netchan, msglen, msgbuf); else if (c->state == cs_spawned) { // don't overrun bandwidth if (SV_RateDrop (c)) continue; - SV_SendClientDatagram (c); } else { - // just update reliable if needed - if (c->netchan.message.cursize || curtime - c->netchan.last_sent > 1000 ) + // just update reliable if needed + if (c->netchan.message.cursize || curtime - c->netchan.last_sent > 1.0f) Netchan_Transmit (&c->netchan, 0, NULL); } } diff --git a/engine/server/sv_studio.c b/engine/server/sv_studio.c index 1ace6b5b..2c923d0a 100644 --- a/engine/server/sv_studio.c +++ b/engine/server/sv_studio.c @@ -19,8 +19,7 @@ int SV_StudioExtractBbox( studiohdr_t *phdr, int sequence, float *mins, float *m return 1; } -byte *SV_GetModelPtr(edict_t *ent) +byte *SV_GetModelPtr(prvm_edict_t *ent) { - if(!ent) return NULL; return NULL; } \ No newline at end of file diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index b5041ba5..91cdbea8 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -22,7 +22,18 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "engine.h" #include "server.h" -edict_t *sv_player; +extern cvar_t *sv_maxspeed; +extern cvar_t *sv_accelerate; +extern cvar_t *sv_wateraccelerate; +extern cvar_t *sv_friction; +extern cvar_t *sv_rollangle; +extern cvar_t *sv_rollspeed; + +prvm_edict_t *sv_player; +static bool onground; +static usercmd_t cmd; +static vec3_t wishdir, forward, right, up; +static float wishspeed; /* ============================================================ @@ -60,7 +71,7 @@ void SV_New_f (void) { char *gamedir; int playernum; - edict_t *ent; + prvm_edict_t *ent; if (sv_client->state != cs_connected) { @@ -98,8 +109,8 @@ void SV_New_f (void) if (sv.state == ss_game) { // set up the entity for the client - ent = EDICT_NUM(playernum+1); - ent->s.number = playernum+1; + ent = PRVM_EDICT_NUM(playernum+1); + ent->priv.sv->state.number = playernum+1; sv_client->edict = ent; memset (&sv_client->lastcmd, 0, sizeof(sv_client->lastcmd)); @@ -238,7 +249,7 @@ void SV_Begin_f (void) sv_client->state = cs_spawned; // call the game begin function - ge->ClientBegin (sv_player); +//ge->ClientBegin (sv_player); Cbuf_InsertFromDefer (); } @@ -460,8 +471,8 @@ void SV_ExecuteUserCommand (char *s) } } - if (!u->name && sv.state == ss_game) - ge->ClientCommand (sv_player); +//if (!u->name && sv.state == ss_game) +//ge->ClientCommand (sv_player); } /* @@ -471,17 +482,324 @@ USER CMD EXECUTION =========================================================================== */ - -void SV_ClientThink (client_t *cl, usercmd_t *cmd) +void SV_ClientRun (client_t *cl, usercmd_t *curcmd) { - cl->commandMsec -= cmd->msec; + sv_client = cl; + cmd = *curcmd; + + cl->commandMsec -= curcmd->msec; if (cl->commandMsec < 0 && sv_enforcetime->value ) { MsgWarn("SV_ClientThink: commandMsec underflow from %s\n", cl->name); return; } - ge->ClientThink (cl->edict, cmd); + //SV_ClientThink(); +} + +/* +=============== +SV_CalcRoll + +=============== +*/ +float SV_CalcRoll (vec3_t angles, vec3_t velocity) +{ + float sign; + float side; + float value; + + side = DotProduct (velocity, right); + sign = side < 0 ? -1 : 1; + side = fabs(side); + + value = sv_rollangle->value; + + if (side < sv_rollspeed->value) + side = side * value / sv_rollspeed->value; + else side = value; + + return side*sign; + +} + + +/* +================== +SV_UserFriction + +================== +*/ +void SV_UserFriction (void) +{ + float speed, newspeed, control, friction; + vec3_t start, stop; + trace_t trace; + + speed = sqrt(sv_client->edict->fields.sv->velocity[0]*sv_client->edict->fields.sv->velocity[0]+sv_client->edict->fields.sv->velocity[1]*sv_client->edict->fields.sv->velocity[1]); + if (!speed) return; + + // if the leading edge is over a dropoff, increase friction + start[0] = stop[0] = sv_client->edict->fields.sv->origin[0] + sv_client->edict->fields.sv->velocity[0]/speed*16; + start[1] = stop[1] = sv_client->edict->fields.sv->origin[1] + sv_client->edict->fields.sv->velocity[1]/speed*16; + start[2] = sv_client->edict->fields.sv->origin[2] + sv_client->edict->fields.sv->mins[2]; + stop[2] = start[2] - 34; + + trace = SV_Trace (start, vec3_origin, vec3_origin, stop, sv_client->edict, MASK_SOLID ); + + if (trace.fraction == 1.0) friction = sv_friction->value * 2; + else friction = sv_friction->value; + + // apply friction + + control = max(speed, 100); + newspeed = speed - sv.frametime * control * friction; + + if (newspeed < 0) newspeed = 0; + else newspeed /= speed; + + VectorScale(sv_client->edict->fields.sv->velocity, newspeed, sv_client->edict->fields.sv->velocity); +} + +/* +============== +SV_Accelerate +============== +*/ +void SV_Accelerate (void) +{ + int i; + float addspeed, accelspeed, currentspeed; + + currentspeed = DotProduct (sv_client->edict->fields.sv->velocity, wishdir); + addspeed = wishspeed - currentspeed; + if (addspeed <= 0) return; + accelspeed = sv_accelerate->value * sv.frametime * wishspeed; + + if (accelspeed > addspeed) accelspeed = addspeed; + + for (i = 0; i < 3; i++) sv_client->edict->fields.sv->velocity[i] += accelspeed * wishdir[i]; +} + +void SV_AirAccelerate (vec3_t wishveloc) +{ + int i; + float addspeed, wishspd, accelspeed, currentspeed; + + wishspd = VectorNormalize (wishveloc); + if (wishspd > sv_maxspeed->value / 10) wishspd = sv_maxspeed->value / 10; + currentspeed = DotProduct (sv_client->edict->fields.sv->velocity, wishveloc); + addspeed = wishspd - currentspeed; + if (addspeed <= 0) return; + accelspeed = (sv_airaccelerate->value < 0 ? sv_accelerate->value : sv_airaccelerate->value) * wishspeed * sv.frametime; + if (accelspeed > addspeed) accelspeed = addspeed; + + for (i = 0; i < 3; i++) sv_client->edict->fields.sv->velocity[i] += accelspeed*wishveloc[i]; +} + +void DropPunchAngle (void) +{ + float len; + + len = VectorNormalize(sv_client->edict->fields.sv->punchangle); + + len -= 10 * sv.frametime; + if (len < 0) len = 0; + VectorScale (sv_client->edict->fields.sv->punchangle, len, sv_client->edict->fields.sv->punchangle); +} + +/* +=================== +SV_AirMove + +=================== +*/ +void SV_AirMove (void) +{ + int i; + vec3_t wishvel; + float fmove, smove, temp; + + wishvel[0] = wishvel[2] = 0; + wishvel[1] = sv_client->edict->fields.sv->angles[1]; + AngleVectorsRight (wishvel, forward, right, up); + + fmove = cmd.forwardmove; + smove = cmd.sidemove; + + // hack to not let you back into teleporter + if (sv.time < sv_client->edict->fields.sv->teleport_time && fmove < 0) + fmove = 0; + + for (i=0 ; i<3 ; i++) + wishvel[i] = forward[i]*fmove + right[i]*smove; + + if ((int)sv_client->edict->fields.sv->movetype != MOVETYPE_WALK) + wishvel[2] += cmd.upmove; + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + if (wishspeed > sv_maxspeed->value) + { + temp = sv_maxspeed->value/wishspeed; + VectorScale (wishvel, temp, wishvel); + wishspeed = sv_maxspeed->value; + } + + if (sv_client->edict->fields.sv->movetype == MOVETYPE_NOCLIP) + { + // noclip + VectorCopy (wishvel, sv_client->edict->fields.sv->velocity); + } + else if (onground && (!(sv_client->edict->fields.sv->button2))) + { + SV_UserFriction (); + SV_Accelerate (); + } + else + { + // not on ground, so little effect on velocity + SV_AirAccelerate (wishvel); + } +} + +/* +=================== +SV_WaterMove + +=================== +*/ +void SV_WaterMove (void) +{ + int i; + vec3_t wishvel; + float speed, newspeed, wishspeed, addspeed, accelspeed, temp; + + // user intentions + AngleVectorsRight (sv_client->edict->fields.sv->v_angle, forward, right, up); + + for (i = 0; i < 3; i++) + wishvel[i] = forward[i]*cmd.forwardmove + right[i]*cmd.sidemove; + + if (!cmd.forwardmove && !cmd.sidemove && !cmd.upmove) + wishvel[2] -= 60; // drift towards bottom + else + wishvel[2] += cmd.upmove; + + wishspeed = VectorLength(wishvel); + if (wishspeed > sv_maxspeed->value) + { + temp = sv_maxspeed->value/wishspeed; + VectorScale (wishvel, temp, wishvel); + wishspeed = sv_maxspeed->value; + } + wishspeed *= 0.7; + + // water friction + speed = VectorLength(sv_client->edict->fields.sv->velocity); + if (speed) + { + newspeed = speed - sv.frametime * speed * -1; + if (newspeed < 0) + newspeed = 0; + temp = newspeed/speed; + VectorScale(sv_client->edict->fields.sv->velocity, temp, sv_client->edict->fields.sv->velocity); + } + else + newspeed = 0; + + // water acceleration + if (!wishspeed) + return; + + addspeed = wishspeed - newspeed; + if (addspeed <= 0) + return; + + VectorNormalize (wishvel); + accelspeed = (sv_wateraccelerate->value < 0 ? sv_accelerate->value : sv_wateraccelerate->value) * wishspeed * sv.frametime; + if (accelspeed > addspeed) accelspeed = addspeed; + + for (i = 0; i < 3; i++) sv_client->edict->fields.sv->velocity[i] += accelspeed * wishvel[i]; +} + +void SV_WaterJump (void) +{ + if (sv.time > sv_client->edict->fields.sv->teleport_time || !sv_client->edict->fields.sv->waterlevel) + { + sv_client->edict->fields.sv->flags = (int)sv_client->edict->fields.sv->flags & ~FL_WATERJUMP; + sv_client->edict->fields.sv->teleport_time = 0; + } + sv_client->edict->fields.sv->velocity[0] = sv_client->edict->fields.sv->movedir[0]; + sv_client->edict->fields.sv->velocity[1] = sv_client->edict->fields.sv->movedir[1]; +} + + +/* +=================== +SV_ClientThink + +the move fields specify an intended velocity in pix/sec +the angle fields specify an exact angular motion in degrees +=================== +*/ +void SV_ClientThink (void) +{ + vec3_t v_angle; + + if (sv_client->edict->fields.sv->movetype == MOVETYPE_NONE) + return; + + onground = (int)sv_client->edict->fields.sv->flags & FL_ONGROUND; + + DropPunchAngle (); + + // if dead, behave differently + if (sv_client->edict->fields.sv->health <= 0) return; + + cmd = sv_client->lastcmd; + sv_client->commandMsec -= cmd.msec; + + // angles + // show 1/3 the pitch angle and all the roll angle + VectorAdd (sv_client->edict->fields.sv->v_angle, sv_client->edict->fields.sv->punchangle, v_angle); + sv_client->edict->fields.sv->angles[ROLL] = SV_CalcRoll (sv_client->edict->fields.sv->angles, sv_client->edict->fields.sv->velocity)*4; + if (!sv_client->edict->fields.sv->fixangle) + { + sv_client->edict->fields.sv->angles[PITCH] = -v_angle[PITCH]/3; + sv_client->edict->fields.sv->angles[YAW] = v_angle[YAW]; + } + + if ( (int)sv_client->edict->fields.sv->flags & FL_WATERJUMP ) + { + SV_WaterJump (); + return; + } + + // walk + if ((sv_client->edict->fields.sv->waterlevel >= 2) && (sv_client->edict->fields.sv->movetype != MOVETYPE_NOCLIP)) + { + SV_WaterMove(); + return; + } + + SV_AirMove(); +} + +void SV_ApplyClientMove (void) +{ + usercmd_t *move = &sv_client->lastcmd; + + if (!move->msec) return; + + // set the edict fields + sv_client->edict->fields.sv->button0 = move->buttons & 1; + sv_client->edict->fields.sv->button2 = (move->buttons & 2)>>1; + if (move->impulse) sv_client->edict->fields.sv->impulse = move->impulse; + + // only send the impulse to qc once + move->impulse = 0; + VectorCopy(sv_client->edict->priv.sv->client->viewangles, sv_client->edict->fields.sv->v_angle); } /* @@ -498,8 +816,8 @@ void SV_ExecuteClientMessage (client_t *cl) int c; char *s; - usercmd_t nullcmd; - usercmd_t oldest, oldcmd, newcmd; + usercmd_t nullcmd; + usercmd_t oldest, oldcmd, newcmd; int net_drop; int stringCmdCount; int checksum, calculatedChecksum; @@ -584,13 +902,13 @@ void SV_ExecuteClientMessage (client_t *cl) { while (net_drop > 2) { - SV_ClientThink (cl, &cl->lastcmd); + SV_ClientRun (cl, &cl->lastcmd); net_drop--; } - if (net_drop > 1) SV_ClientThink (cl, &oldest); - if (net_drop > 0) SV_ClientThink (cl, &oldcmd); + if (net_drop > 1) SV_ClientRun (cl, &oldest); + if (net_drop > 0) SV_ClientRun (cl, &oldcmd); } - SV_ClientThink (cl, &newcmd); + SV_ClientRun (cl, &newcmd); } cl->lastcmd = newcmd; break; @@ -604,5 +922,4 @@ void SV_ExecuteClientMessage (client_t *cl) break; } } -} - +} \ No newline at end of file diff --git a/engine/server/sv_world.c b/engine/server/sv_world.c index dac9e951..b7f66f44 100644 --- a/engine/server/sv_world.c +++ b/engine/server/sv_world.c @@ -30,14 +30,6 @@ ENTITY AREA CHECKING FIXME: this use of "area" is different from the bsp file use =============================================================================== */ - -// (type *)STRUCT_FROM_LINK(link_t *link, type, member) -// ent = STRUCT_FROM_LINK(link,entity_t,order) -// FIXME: remove this mess! -#define STRUCT_FROM_LINK(l,t,m) ((t *)((byte *)l - (int)&(((t *)0)->m))) - -#define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area) - typedef struct areanode_s { int axis; // -1 = leaf node @@ -53,12 +45,12 @@ typedef struct areanode_s areanode_t sv_areanodes[AREA_NODES]; int sv_numareanodes; -float *area_mins, *area_maxs; -edict_t **area_list; +float *area_mins, *area_maxs; +prvm_edict_t **area_list; int area_count, area_maxcount; int area_type; -int SV_HullForEntity (edict_t *ent); +int SV_HullForEntity (prvm_edict_t *ent); // ClearLink is used for new headnodes @@ -147,12 +139,11 @@ SV_UnlinkEdict =============== */ -void SV_UnlinkEdict (edict_t *ent) +void SV_UnlinkEdict (prvm_edict_t *ent) { - if (!ent->area.prev) - return; // not linked in anywhere - RemoveLink (&ent->area); - ent->area.prev = ent->area.next = NULL; + if (!ent->priv.sv->area.prev) return; // not linked in anywhere + RemoveLink (&ent->priv.sv->area); + ent->priv.sv->area.prev = ent->priv.sv->area.next = NULL; } @@ -163,7 +154,7 @@ SV_LinkEdict =============== */ #define MAX_TOTAL_ENT_LEAFS 128 -void SV_LinkEdict (edict_t *ent) +void SV_LinkEdict (prvm_edict_t *ent) { areanode_t *node; int leafs[MAX_TOTAL_ENT_LEAFS]; @@ -173,80 +164,80 @@ void SV_LinkEdict (edict_t *ent) int area; int topnode; - if (ent->area.prev) SV_UnlinkEdict (ent); // unlink from old position - if (ent == ge->edicts) return; // don't add the world - if (!ent->inuse) return; + if (ent->priv.sv->area.prev) SV_UnlinkEdict (ent); // unlink from old position + if (ent == prog->edicts) return; // don't add the world + if (!ent->priv.sv->free) return; // set the size - VectorSubtract (ent->maxs, ent->mins, ent->size); + VectorSubtract (ent->priv.sv->maxs, ent->priv.sv->mins, ent->priv.sv->size); // encode the size into the entity_state for client prediction - if (ent->solid == SOLID_BBOX && !(ent->svflags & SVF_DEADMONSTER)) + if (ent->priv.sv->solid == SOLID_BBOX && !(ent->priv.sv->flags & SVF_DEADMONSTER)) { // assume that x/y are equal and symetric - i = ent->maxs[0]/8; + i = ent->priv.sv->maxs[0]/8; if (i<1) i = 1; if (i>31) i = 31; // z is not symetric - j = (-ent->mins[2])/8; + j = (-ent->priv.sv->mins[2])/8; if (j < 1) j = 1; if (j > 31) j = 31; // and z maxs can be negative... - k = (ent->maxs[2]+32)/8; + k = (ent->priv.sv->maxs[2]+32)/8; if (k<1) k = 1; if (k>63) k = 63; - ent->s.solid = (k<<10) | (j<<5) | i; + ent->priv.sv->state.solid = (k<<10) | (j<<5) | i; } - else if (ent->solid == SOLID_BSP) + else if (ent->priv.sv->solid == SOLID_BSP) { - ent->s.solid = 31; // a solid_bbox will never create this value + ent->priv.sv->state.solid = 31; // a solid_bbox will never create this value } - else ent->s.solid = 0; + else ent->priv.sv->state.solid = 0; // set the abs box - if (ent->solid == SOLID_BSP && (ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2]) ) + if (ent->priv.sv->solid == SOLID_BSP && (ent->priv.sv->state.angles[0] || ent->priv.sv->state.angles[1] || ent->priv.sv->state.angles[2]) ) { // expand for rotation float max = 0, v; int i; - for (i=0 ; i<3 ; i++) + for (i = 0; i < 3; i++) { - v =fabs( ent->mins[i]); + v =fabs( ent->priv.sv->mins[i]); if (v > max) max = v; - v =fabs( ent->maxs[i]); + v =fabs( ent->priv.sv->maxs[i]); if (v > max) max = v; } - for (i=0 ; i<3 ; i++) + for (i = 0; i < 3; i++) { - ent->absmin[i] = ent->s.origin[i] - max; - ent->absmax[i] = ent->s.origin[i] + max; + ent->priv.sv->absmin[i] = ent->priv.sv->state.origin[i] - max; + ent->priv.sv->absmax[i] = ent->priv.sv->state.origin[i] + max; } } else { // normal - VectorAdd (ent->s.origin, ent->mins, ent->absmin); - VectorAdd (ent->s.origin, ent->maxs, ent->absmax); + VectorAdd (ent->priv.sv->state.origin, ent->priv.sv->mins, ent->priv.sv->absmin); + VectorAdd (ent->priv.sv->state.origin, ent->priv.sv->maxs, ent->priv.sv->absmax); } // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch - ent->absmin[0] -= 1; - ent->absmin[1] -= 1; - ent->absmin[2] -= 1; - ent->absmax[0] += 1; - ent->absmax[1] += 1; - ent->absmax[2] += 1; + ent->priv.sv->absmin[0] -= 1; + ent->priv.sv->absmin[1] -= 1; + ent->priv.sv->absmin[2] -= 1; + ent->priv.sv->absmax[0] += 1; + ent->priv.sv->absmax[1] += 1; + ent->priv.sv->absmax[2] += 1; // link to PVS leafs - ent->num_clusters = 0; - ent->areanum = 0; - ent->areanum2 = 0; + ent->priv.sv->num_clusters = 0; + ent->priv.sv->areanum = 0; + ent->priv.sv->areanum2 = 0; //get all leafs, including solids - num_leafs = CM_BoxLeafnums (ent->absmin, ent->absmax, leafs, MAX_TOTAL_ENT_LEAFS, &topnode); + num_leafs = CM_BoxLeafnums (ent->priv.sv->absmin, ent->priv.sv->absmax, leafs, MAX_TOTAL_ENT_LEAFS, &topnode); // set areas for (i = 0; i < num_leafs; i++) @@ -256,25 +247,25 @@ void SV_LinkEdict (edict_t *ent) if (area) { // doors may legally straggle two areas, // but nothing should evern need more than that - if (ent->areanum && ent->areanum != area) + if (ent->priv.sv->areanum && ent->priv.sv->areanum != area) { - if (ent->areanum2 && ent->areanum2 != area && sv.state == ss_loading) - MsgWarn("SV_LinkEdict: object touching 3 areas at %f %f %f\n", ent->absmin[0], ent->absmin[1], ent->absmin[2]); - ent->areanum2 = area; + if (ent->priv.sv->areanum2 && ent->priv.sv->areanum2 != area && sv.state == ss_loading) + MsgWarn("SV_LinkEdict: object touching 3 areas at %f %f %f\n", ent->priv.sv->absmin[0], ent->priv.sv->absmin[1], ent->priv.sv->absmin[2]); + ent->priv.sv->areanum2 = area; } - else ent->areanum = area; + else ent->priv.sv->areanum = area; } } if (num_leafs >= MAX_TOTAL_ENT_LEAFS) { // assume we missed some leafs, and mark by headnode - ent->num_clusters = -1; - ent->headnode = topnode; + ent->priv.sv->num_clusters = -1; + ent->priv.sv->headnode = topnode; } else { - ent->num_clusters = 0; + ent->priv.sv->num_clusters = 0; for (i = 0; i < num_leafs; i++) { if (clusters[i] == -1) continue; // not a visible leaf @@ -284,42 +275,42 @@ void SV_LinkEdict (edict_t *ent) } if (j == i) { - if (ent->num_clusters == MAX_ENT_CLUSTERS) + if (ent->priv.sv->num_clusters == MAX_ENT_CLUSTERS) { // assume we missed some leafs, and mark by headnode - ent->num_clusters = -1; - ent->headnode = topnode; + ent->priv.sv->num_clusters = -1; + ent->priv.sv->headnode = topnode; break; } - ent->clusternums[ent->num_clusters++] = clusters[i]; + ent->priv.sv->clusternums[ent->priv.sv->num_clusters++] = clusters[i]; } } } // if first time, make sure old_origin is valid - if (!ent->linkcount) + if (!ent->priv.sv->linkcount) { - VectorCopy (ent->s.origin, ent->s.old_origin); + VectorCopy (ent->priv.sv->state.origin, ent->priv.sv->state.old_origin); } - ent->linkcount++; + ent->priv.sv->linkcount++; - if (ent->solid == SOLID_NOT) return; + if (ent->priv.sv->solid == SOLID_NOT) return; // find the first node that the ent's box crosses node = sv_areanodes; while (1) { if (node->axis == -1) break; - if (ent->absmin[node->axis] > node->dist) + if (ent->priv.sv->absmin[node->axis] > node->dist) node = node->children[0]; - else if (ent->absmax[node->axis] < node->dist) + else if (ent->priv.sv->absmax[node->axis] < node->dist) node = node->children[1]; else break; // crosses the node } // link it in - if (ent->solid == SOLID_TRIGGER) InsertLinkBefore (&ent->area, &node->trigger_edicts); - else InsertLinkBefore (&ent->area, &node->solid_edicts); + if (ent->priv.sv->solid == SOLID_TRIGGER) InsertLinkBefore (&ent->priv.sv->area, &node->trigger_edicts); + else InsertLinkBefore (&ent->priv.sv->area, &node->solid_edicts); } @@ -333,7 +324,7 @@ SV_AreaEdicts_r void SV_AreaEdicts_r (areanode_t *node) { link_t *l, *next, *start; - edict_t *check; + prvm_edict_t *check; int count = 0; // touch linked edicts @@ -344,11 +335,11 @@ void SV_AreaEdicts_r (areanode_t *node) for (l = start->next; l != start; l = next) { next = l->next; - check = EDICT_FROM_AREA(l); + check = PRVM_EDICT_FROM_AREA(l); - if (check->solid == SOLID_NOT) continue; // deactivated - if (check->absmin[0] > area_maxs[0] || check->absmin[1] > area_maxs[1] || check->absmin[2] > area_maxs[2] - || check->absmax[0] < area_mins[0] || check->absmax[1] < area_mins[1] || check->absmax[2] < area_mins[2]) + if (check->priv.sv->solid == SOLID_NOT) continue; // deactivated + if (check->priv.sv->absmin[0] > area_maxs[0] || check->priv.sv->absmin[1] > area_maxs[1] || check->priv.sv->absmin[2] > area_maxs[2] + || check->priv.sv->absmax[0] < area_mins[0] || check->priv.sv->absmax[1] < area_mins[1] || check->priv.sv->absmax[2] < area_mins[2]) continue; // not touching if (area_count == area_maxcount) @@ -375,7 +366,7 @@ void SV_AreaEdicts_r (areanode_t *node) SV_AreaEdicts ================ */ -int SV_AreaEdicts (vec3_t mins, vec3_t maxs, edict_t **list, int maxcount, int areatype) +int SV_AreaEdicts (vec3_t mins, vec3_t maxs, prvm_edict_t **list, int maxcount, int areatype) { area_mins = mins; area_maxs = maxs; @@ -399,7 +390,7 @@ SV_PointContents */ int SV_PointContents (vec3_t p) { - edict_t *touch[MAX_EDICTS], *hit; + prvm_edict_t *touch[MAX_EDICTS], *hit; int i, num; int contents, c2; int headnode; @@ -417,30 +408,14 @@ int SV_PointContents (vec3_t p) // might intersect, so do an exact clip headnode = SV_HullForEntity (hit); - angles = hit->s.angles; - if (hit->solid != SOLID_BSP) angles = vec3_origin; // boxes don't rotate - c2 = CM_TransformedPointContents (p, headnode, hit->s.origin, hit->s.angles); + angles = hit->priv.sv->state.angles; + if (hit->priv.sv->solid != SOLID_BSP) angles = vec3_origin; // boxes don't rotate + c2 = CM_TransformedPointContents (p, headnode, hit->priv.sv->state.origin, hit->priv.sv->state.angles); contents |= c2; } return contents; } - - -typedef struct -{ - vec3_t boxmins, boxmaxs;// enclose the test object along entire move - float *mins, *maxs; // size of the moving object - vec3_t mins2, maxs2; // size when clipping against mosnters - float *start, *end; - trace_t trace; - edict_t *passedict; - int contentmask; - -} moveclip_t; - - - /* ================ SV_HullForEntity @@ -451,15 +426,15 @@ Offset is filled in to contain the adjustment that must be added to the testing object's origin to get a point to use with the returned hull. ================ */ -int SV_HullForEntity (edict_t *ent) +int SV_HullForEntity (prvm_edict_t *ent) { cmodel_t *model; // decide which clipping hull to use, based on the size - if (ent->solid == SOLID_BSP) + if (ent->priv.sv->solid == SOLID_BSP) { // explicit hulls in the BSP model - model = sv.models[ ent->s.modelindex ]; + model = sv.models[ ent->priv.sv->state.modelindex ]; if (!model) { @@ -470,7 +445,7 @@ int SV_HullForEntity (edict_t *ent) } // create a temp hull from bounding box sizes - return CM_HeadnodeForBox (ent->mins, ent->maxs); + return CM_HeadnodeForBox (ent->priv.sv->mins, ent->priv.sv->maxs); } /* @@ -482,7 +457,7 @@ SV_ClipMoveToEntities void SV_ClipMoveToEntities ( moveclip_t *clip ) { int i, num; - edict_t *touchlist[MAX_EDICTS], *touch; + prvm_edict_t *touchlist[MAX_EDICTS], *touch; trace_t trace; int headnode; float *angles; @@ -494,30 +469,30 @@ void SV_ClipMoveToEntities ( moveclip_t *clip ) for (i = 0; i < num; i++) { touch = touchlist[i]; - if (touch->solid == SOLID_NOT) continue; + if (touch->priv.sv->solid == SOLID_NOT) continue; if (touch == clip->passedict) continue; if (clip->trace.allsolid) return; if (clip->passedict) { - if (touch->owner == clip->passedict) continue; // don't clip against own missiles - if (clip->passedict->owner == touch) continue; // don't clip against owner + if (touch->priv.sv->owner == clip->passedict->priv.sv) continue; // don't clip against own missiles + if (clip->passedict->priv.sv->owner == touch->priv.sv) continue; // don't clip against owner } - if ( !(clip->contentmask & CONTENTS_DEADMONSTER) && (touch->svflags & SVF_DEADMONSTER) ) + if ( !(clip->contentmask & CONTENTS_DEADMONSTER) && (touch->priv.sv->flags & SVF_DEADMONSTER) ) continue; // might intersect, so do an exact clip headnode = SV_HullForEntity (touch); - angles = touch->s.angles; - if (touch->solid != SOLID_BSP) angles = vec3_origin; // boxes don't rotate + angles = touch->priv.sv->state.angles; + if (touch->priv.sv->solid != SOLID_BSP) angles = vec3_origin; // boxes don't rotate - if (touch->svflags & SVF_MONSTER) + if (touch->priv.sv->flags & SVF_MONSTER) { - trace = CM_TransformedBoxTrace (clip->start, clip->end, clip->mins2, clip->maxs2, headnode, clip->contentmask, touch->s.origin, angles); + trace = CM_TransformedBoxTrace (clip->start, clip->end, clip->mins2, clip->maxs2, headnode, clip->contentmask, touch->priv.sv->state.origin, angles); } else { - trace = CM_TransformedBoxTrace (clip->start, clip->end, clip->mins, clip->maxs, headnode, clip->contentmask, touch->s.origin, angles); + trace = CM_TransformedBoxTrace (clip->start, clip->end, clip->mins, clip->maxs, headnode, clip->contentmask, touch->priv.sv->state.origin, angles); } if (trace.allsolid || trace.startsolid || trace.fraction < clip->trace.fraction) { @@ -529,7 +504,11 @@ void SV_ClipMoveToEntities ( moveclip_t *clip ) } else clip->trace = trace; } - else if (trace.startsolid) clip->trace.startsolid = true; + else if (trace.startsolid) + { + clip->trace.startsolid = true; + //clip->trace.startstuck = true; + } } } @@ -568,7 +547,7 @@ Passedict and edicts owned by passedict are explicitly not checked. ================== */ -trace_t SV_Trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t *passedict, int contentmask) +trace_t SV_Trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, prvm_edict_t *passedict, int contentmask) { moveclip_t clip; @@ -579,7 +558,7 @@ trace_t SV_Trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t *p // clip to world clip.trace = CM_BoxTrace (start, end, mins, maxs, 0, contentmask); - clip.trace.ent = ge->edicts; + clip.trace.ent = prog->edicts; if (clip.trace.fraction == 0) return clip.trace; // blocked by the world clip.contentmask = contentmask; @@ -601,3 +580,28 @@ trace_t SV_Trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t *p return clip.trace; } +trace_t SV_ClipMoveToEntity(prvm_edict_t *ent, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int contentsmask) +{ + moveclip_t clip; + + memset( &clip, 0, sizeof(moveclip_t)); + + clip.passedict = ent; + clip.contentmask = contentsmask; + + VectorCopy(start, clip.start); + VectorCopy(end, clip.end); + + VectorCopy(mins, clip.mins); + VectorCopy(maxs, clip.maxs); + VectorCopy(mins, clip.mins2); + VectorCopy(maxs, clip.maxs2); + + // create the bounding box of the entire move + SV_TraceBounds ( clip.start, clip.mins2, clip.maxs2, clip.end, clip.boxmins, clip.boxmaxs ); + + // all prepares finished + SV_ClipMoveToEntities( &clip ); + + return clip.trace; +} \ No newline at end of file diff --git a/engine/snd_dma.c b/engine/snd_dma.c index 51d6ca02..ec711d4d 100644 --- a/engine/snd_dma.c +++ b/engine/snd_dma.c @@ -53,8 +53,8 @@ vec3_t listener_up; bool s_registering; -int soundtime; // sample PAIRS -int paintedtime; // sample PAIRS +float soundtime; // sample PAIRS +float paintedtime; // sample PAIRS // during registration it is possible to have more sounds // than could actually be referenced during gameplay, @@ -80,7 +80,7 @@ cvar_t *s_mixahead; cvar_t *s_primary; -int s_rawend; +float s_rawend; portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES]; @@ -120,8 +120,7 @@ void S_Init (void) Msg("\n------- sound initialization -------\n"); cv = Cvar_Get ("s_initsound", "1", 0); - if (!cv->value) - Msg ("not initializing.\n"); + if (!cv->value) Msg ("not initializing.\n"); else { s_volume = Cvar_Get ("s_volume", "0.7", CVAR_ARCHIVE); @@ -137,8 +136,7 @@ void S_Init (void) Cmd_AddCommand("soundlist", S_SoundList); Cmd_AddCommand("soundinfo", S_SoundInfo_f); - if (!SNDDMA_Init()) - return; + if (!SNDDMA_Init()) return; S_InitScaletable (); @@ -149,7 +147,6 @@ void S_Init (void) paintedtime = 0; Msg ("sound sampling rate: %i\n", dma.speed); - S_StopAllSounds (); } @@ -166,8 +163,7 @@ void S_Shutdown(void) int i; sfx_t *sfx; - if (!sound_started) - return; + if (!sound_started) return; SNDDMA_Shutdown(); @@ -181,13 +177,10 @@ void S_Shutdown(void) // free all sounds for (i=0, sfx=known_sfx ; i < num_sfx ; i++,sfx++) { - if (!sfx->name[0]) - continue; - if (sfx->cache) - Z_Free (sfx->cache); + if (!sfx->name[0]) continue; + if (sfx->cache) Z_Free (sfx->cache); memset (sfx, 0, sizeof(*sfx)); } - num_sfx = 0; } @@ -202,7 +195,7 @@ S_FindName ================== */ -sfx_t *S_FindName (char *name, bool create) +sfx_t *S_FindName (const char *name, bool create) { int i; sfx_t *sfx; @@ -302,7 +295,7 @@ S_RegisterSound ================== */ -sfx_t *S_RegisterSound (char *name) +sfx_t *S_RegisterSound (const char *name) { sfx_t *sfx; @@ -371,18 +364,19 @@ S_PickChannel */ channel_t *S_PickChannel(int entnum, int entchannel) { - int ch_idx; - int first_to_die; - int life_left; + int ch_idx; + int first_to_die; + float life_left; channel_t *ch; if (entchannel<0) Com_Error (ERR_DROP, "S_PickChannel: entchannel<0"); -// Check for replacement sound, or find the best one to replace - first_to_die = -1; - life_left = 0x7fffffff; - for (ch_idx=0 ; ch_idx < MAX_CHANNELS ; ch_idx++) + // Check for replacement sound, or find the best one to replace + first_to_die = -1; + life_left = 32768.0; + + for (ch_idx = 0; ch_idx < MAX_CHANNELS; ch_idx++) { if (entchannel != 0 // channel 0 never overrides && channels[ch_idx].entnum == entnum @@ -692,16 +686,16 @@ void S_StartSound(vec3_t origin, int entnum, int entchannel, sfx_t *sfx, float f ps->sfx = sfx; // drift s_beginofs - start = cl.frame.servertime * 0.001 * dma.speed + s_beginofs; + start = cl.frame.servertime * dma.speed + s_beginofs; if (start < paintedtime) { start = paintedtime; - s_beginofs = start - (cl.frame.servertime * 0.001 * dma.speed); + s_beginofs = start - (cl.frame.servertime * dma.speed); } else if (start > paintedtime + 0.3 * dma.speed) { start = paintedtime + 0.1 * dma.speed; - s_beginofs = start - (cl.frame.servertime * 0.001 * dma.speed); + s_beginofs = start - (cl.frame.servertime * dma.speed); } else { @@ -732,7 +726,7 @@ void S_StartSound(vec3_t origin, int entnum, int entchannel, sfx_t *sfx, float f S_StartLocalSound ================== */ -int S_StartLocalSound (char *sound) +int S_StartLocalSound (const char *sound) { sfx_t *sfx; @@ -892,7 +886,7 @@ void S_AddLoopSounds (void) ch->rightvol = right_total; ch->autosound = true; // remove next frame ch->sfx = sfx; - ch->pos = paintedtime % sc->length; + ch->pos = fmod(paintedtime, sc->length); ch->end = paintedtime + sc->length - ch->pos; } } @@ -926,7 +920,7 @@ void S_RawSamples (int samples, int rate, int width, int channels, byte *data) { // optimized case for (i=0 ; i= samples) break; - dst = s_rawend&(MAX_RAW_SAMPLES-1); + dst = (int)s_rawend & (MAX_RAW_SAMPLES-1); s_rawend++; s_rawsamples[dst].left = LittleShort(((short *)data)[src*2]) << 8; @@ -957,7 +951,7 @@ void S_RawSamples (int samples, int rate, int width, int channels, byte *data) src = i*scale; if (src >= samples) break; - dst = s_rawend&(MAX_RAW_SAMPLES-1); + dst = (int)s_rawend & (MAX_RAW_SAMPLES-1); s_rawend++; s_rawsamples[dst].left = LittleShort(((short *)data)[src]) << 8; @@ -972,7 +966,7 @@ void S_RawSamples (int samples, int rate, int width, int channels, byte *data) src = i*scale; if (src >= samples) break; - dst = s_rawend&(MAX_RAW_SAMPLES-1); + dst = (int)s_rawend & (MAX_RAW_SAMPLES-1); s_rawend++; s_rawsamples[dst].left = ((char *)data)[src*2] << 16; @@ -987,7 +981,7 @@ void S_RawSamples (int samples, int rate, int width, int channels, byte *data) src = i*scale; if (src >= samples) break; - dst = s_rawend&(MAX_RAW_SAMPLES-1); + dst = (int)s_rawend & (MAX_RAW_SAMPLES-1); s_rawend++; s_rawsamples[dst].left = (((byte *)data)[src]-128) << 16; diff --git a/engine/snd_loc.h b/engine/snd_loc.h index 8fba57d1..d8e8bf65 100644 --- a/engine/snd_loc.h +++ b/engine/snd_loc.h @@ -28,7 +28,7 @@ typedef struct typedef struct { - int length; + float length; int loopstart; int speed; // not needed, because converted on load? int width; @@ -39,7 +39,7 @@ typedef struct typedef struct sfx_s { char name[MAX_QPATH]; - int registration_sequence; + int registration_sequence; sfxcache_t *cache; char *truename; } sfx_t; @@ -53,21 +53,21 @@ typedef struct playsound_s sfx_t *sfx; float volume; float attenuation; - int entnum; - int entchannel; - bool fixed_origin; // use origin field instead of entnum's origin + int entnum; + int entchannel; + bool fixed_origin; // use origin field instead of entnum's origin vec3_t origin; - unsigned begin; // begin on this sample + float begin; // begin on this sample } playsound_t; typedef struct { - int channels; - int samples; // mono samples in buffer - int submission_chunk; // don't mix less than this # - int samplepos; // in mono samples - int samplebits; - int speed; + int channels; + int samples; // mono samples in buffer + int submission_chunk; // don't mix less than this # + int samplepos; // in mono samples + int samplebits; + int speed; byte *buffer; } dma_t; @@ -75,18 +75,18 @@ typedef struct typedef struct { sfx_t *sfx; // sfx number - int leftvol; // 0-255 volume - int rightvol; // 0-255 volume - int end; // end time in global paintsamples + int leftvol; // 0-255 volume + int rightvol; // 0-255 volume + float end; // end time in global paintsamples int pos; // sample position in sfx - int looping; // where to loop, -1 = no looping OBSOLETE? - int entnum; // to allow overriding a specific sound - int entchannel; // + int looping; // where to loop, -1 = no looping OBSOLETE? + int entnum; // to allow overriding a specific sound + int entchannel; // vec3_t origin; // only use if fixed_origin is set vec_t dist_mult; // distance multiplier (attenuation/clipK) - int master_vol; // 0-255 master volume - bool fixed_origin; // use origin instead of fetching entnum's origin - bool autosound; // from an entity->sound, cleared each frame + int master_vol; // 0-255 master volume + bool fixed_origin; // use origin instead of fetching entnum's origin + bool autosound; // from an entity->sound, cleared each frame } channel_t; typedef struct @@ -126,8 +126,8 @@ void SNDDMA_Submit(void); #define MAX_CHANNELS 32 extern channel_t channels[MAX_CHANNELS]; -extern int paintedtime; -extern int s_rawend; +extern float paintedtime; +extern float s_rawend; extern vec3_t listener_origin; extern vec3_t listener_forward; extern vec3_t listener_right; @@ -155,7 +155,7 @@ sfxcache_t *S_LoadSound (sfx_t *s); void S_IssuePlaysound (playsound_t *ps); -void S_PaintChannels(int endtime); +void S_PaintChannels(float endtime); // picks a channel based on priorities, empty slots, number of channels channel_t *S_PickChannel(int entnum, int entchannel); diff --git a/engine/snd_mix.c b/engine/snd_mix.c index 97fc94c0..2a306d7b 100644 --- a/engine/snd_mix.c +++ b/engine/snd_mix.c @@ -109,17 +109,17 @@ S_TransferPaintBuffer =================== */ -void S_TransferPaintBuffer(int endtime) +void S_TransferPaintBuffer(float endtime) { int out_idx; - int count; + float count; int out_mask; int *p; int step; - int val; - unsigned long *pbuf; + int val; + dword *pbuf; - pbuf = (unsigned long *)dma.buffer; + pbuf = (dword*)dma.buffer; if (s_testsound->value) { @@ -129,7 +129,7 @@ void S_TransferPaintBuffer(int endtime) // write a fixed sine wave count = (endtime - paintedtime); for (i=0 ; ivalue*256; -//Msg ("%i to %i\n", paintedtime, endtime); + //Msg ("%i to %i\n", paintedtime, endtime); while (paintedtime < endtime) { // if paintbuffer is smaller than DMA buffer @@ -221,12 +221,11 @@ void S_PaintChannels(int endtime) continue; } - if (ps->begin < end) - end = ps->begin; // stop here + if (ps->begin < end) end = ps->begin;// stop here break; } - // clear the paint buffer + // clear the paint buffer if (s_rawend < paintedtime) { // Msg ("clear\n"); @@ -242,7 +241,7 @@ void S_PaintChannels(int endtime) for (i=paintedtime ; i nul @copy $(MAINTARGET).exe D:\Xash3D\bin\$(MAINTARGET).exe @del $(MAINTARGET).exe diff --git a/launcher/console.c b/launcher/console.c index c90509d3..e66b1eb5 100644 --- a/launcher/console.c +++ b/launcher/console.c @@ -260,7 +260,7 @@ print into cmd32 console */ void Sys_PrintA(const char *pMsg) { - printf( pMsg ); + fprintf (stdout, pMsg ); fflush (stdout); //refresh message } @@ -509,7 +509,7 @@ void Sys_ErrorW(char *error, ...) SetFocus( s_wcd.hWnd ); // wait for the user to quit - while(msg.message != WM_QUIT) + while(!hooked_out && msg.message != WM_QUIT) { if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { diff --git a/launcher/launcher.c b/launcher/launcher.c index 5a1bc99e..4b8969d5 100644 --- a/launcher/launcher.c +++ b/launcher/launcher.c @@ -11,6 +11,7 @@ bool hooked_out = false; bool log_active = false; bool show_always = true; bool about_mode = false; +bool sys_error = false; char dllname[64]; const char *show_credits = "\n\n\n\n\tCopyright XashXT Group 2007 ©\n\t All Rights Reserved\n\n\t Visit www.xash.ru\n"; @@ -138,30 +139,29 @@ void PlatformInit ( char *funcname, int argc, char **argv ) pi->Init( argc, argv ); - if(!GetParmFromCmdLine("-game", gamedir )) - strncpy(gamedir, "xash", sizeof(gamedir)); - if(!GetParmFromCmdLine("+map", source )) - strncpy(source, "newmap", sizeof(source)); - if(!GetParmFromCmdLine("+dat", source )) - strncpy(source, "progs", sizeof(source)); - - if(CheckParm("-vis")) bspflags |= BSP_ONLYVIS; - if(CheckParm("-rad")) bspflags |= BSP_ONLYRAD; - if(CheckParm("-full")) bspflags |= BSP_FULLCOMPILE; - if(CheckParm("-onlyents")) bspflags |= BSP_ONLYENTS; - - if(CheckParm("-progdefs")) qccflags |= QCC_PROGDEFS; - if(CheckParm("/O0")) qccflags |= QCC_OPT_LEVEL_0; - if(CheckParm("/O1")) qccflags |= QCC_OPT_LEVEL_1; - if(CheckParm("/O2")) qccflags |= QCC_OPT_LEVEL_2; - if(CheckParm("/O2")) qccflags |= QCC_OPT_LEVEL_3; - switch(app_name) { case BSPLIB: + if(!GetParmFromCmdLine("-game", gamedir )) + strncpy(gamedir, "xash", sizeof(gamedir)); + if(!GetParmFromCmdLine("+map", source )) + strncpy(source, "newmap", sizeof(source)); + if(CheckParm("-vis")) bspflags |= BSP_ONLYVIS; + if(CheckParm("-rad")) bspflags |= BSP_ONLYRAD; + if(CheckParm("-full")) bspflags |= BSP_FULLCOMPILE; + if(CheckParm("-onlyents")) bspflags |= BSP_ONLYENTS; + pi->Compile.PrepareBSP( gamedir, source, bspflags ); break; case QCCLIB: + if(!GetParmFromCmdLine("+dat", source )) + strncpy(source, "progs", sizeof(source)); + if(CheckParm("-progdefs")) qccflags |= QCC_PROGDEFS; + if(CheckParm("/O0")) qccflags |= QCC_OPT_LEVEL_0; + if(CheckParm("/O1")) qccflags |= QCC_OPT_LEVEL_1; + if(CheckParm("/O2")) qccflags |= QCC_OPT_LEVEL_2; + if(CheckParm("/O2")) qccflags |= QCC_OPT_LEVEL_3; + pi->Compile.PrepareDAT( gamedir, source, qccflags ); break; case SPRITE: diff --git a/launcher/launcher.dsp b/launcher/launcher.dsp index 0a9b2f54..b653caed 100644 --- a/launcher/launcher.dsp +++ b/launcher/launcher.dsp @@ -53,8 +53,8 @@ BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib /nologo /dll /pdb:none /machine:I386 +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /opt:nowin98 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib /nologo /dll /pdb:none /machine:I386 /opt:nowin98 # Begin Custom Build TargetDir=\Xash3D\src_main\!source\temp\launcher\!release InputPath=\Xash3D\src_main\!source\temp\launcher\!release\launcher.dll diff --git a/launcher/launcher.h b/launcher/launcher.h index be41fc36..a464bdef 100644 --- a/launcher/launcher.h +++ b/launcher/launcher.h @@ -36,6 +36,7 @@ extern HINSTANCE base_hInstance; extern HINSTANCE linked_dll; extern bool debug_mode; extern bool log_active; +extern bool hooked_out; extern int com_argc; extern char *com_argv[MAX_NUM_ARGVS]; extern char sys_rootdir[ MAX_SYSPATH ]; @@ -43,8 +44,8 @@ extern char log_path[256]; extern bool console_read_only; extern bool show_always; extern bool about_mode; +extern bool sys_error; char *va(const char *format, ...); -static int sys_error = false; const char* Log_Timestamp( void ); int CheckParm (const char *parm); diff --git a/platform/basemem.c b/platform/basemem.c index 2da546e5..7a936952 100644 --- a/platform/basemem.c +++ b/platform/basemem.c @@ -103,7 +103,7 @@ void *_Mem_Realloc(byte *poolptr, void *memptr, size_t size, const char *filenam char *nb; memheader_t *hdr; - if (size <= 0) return memptr;//no need to reallocate + if (size <= 0) return memptr; //no need to reallocate nb = _Mem_Alloc(poolptr, size, filename, fileline); if (memptr) //first allocate? @@ -113,7 +113,6 @@ void *_Mem_Realloc(byte *poolptr, void *memptr, size_t size, const char *filenam _Mem_Copy( nb, memptr, hdr->size, filename, fileline ); _Mem_Free( memptr, filename, fileline);//free unused old block } - else MsgWarn("Mem_Realloc: memptr == NULL (called at %s:%i)\n", filename, fileline); return (void *)nb; } diff --git a/platform/bsplib/map.c b/platform/bsplib/map.c index 983e5c5f..1376f520 100644 --- a/platform/bsplib/map.c +++ b/platform/bsplib/map.c @@ -999,7 +999,7 @@ void LoadMapFile (void) nummapbrushsides = 0; num_entities = 0; - if(!load) Sys_Error("can't loading map file\n"); + if(!load) Sys_Error("can't loading map file %s\n", path ); Msg ("reading %s\n", path); while (ParseMapEntity ()){} diff --git a/platform/platform.dsp b/platform/platform.dsp index 912f8bd9..8e492211 100644 --- a/platform/platform.dsp +++ b/platform/platform.dsp @@ -53,8 +53,8 @@ BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 -# ADD LINK32 kernel32.lib winmm.lib user32.lib /nologo /dll /pdb:none /machine:I386 +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /opt:nowin98 +# ADD LINK32 kernel32.lib winmm.lib user32.lib /nologo /dll /pdb:none /machine:I386 /opt:nowin98 # Begin Custom Build TargetDir=\XASH3D\src_main\!source\temp\platform\!release InputPath=\XASH3D\src_main\!source\temp\platform\!release\platform.dll diff --git a/platform/platform.plg b/platform/platform.plg deleted file mode 100644 index 5d398aca..00000000 --- a/platform/platform.plg +++ /dev/null @@ -1,73 +0,0 @@ - - -
-

Build Log

-

---------------------Configuration: platform - Win32 Release-------------------- -

-

Command Lines

-Creating temporary file "C:\Temp\RSPF6D.tmp" with contents -[ -/nologo /ML /W3 /GX /O2 /Ob0 /I "./" /I "../public" /I "./bsplib/" /I "./qcclib" /I "./mdllib" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Fo"..\temp\platform\!release/" /Fd"..\temp\platform\!release/" /FD /c -"D:\XASH3D\src_main\!source\platform\qcclib\qccmain.c" -] -Creating command line "cl.exe @C:\Temp\RSPF6D.tmp" -Creating temporary file "C:\Temp\RSPF6E.tmp" with contents -[ -kernel32.lib winmm.lib user32.lib /nologo /dll /pdb:none /machine:I386 /out:"..\temp\platform\!release/platform.dll" /implib:"..\temp\platform\!release/platform.lib" -"\XASH3D\src_main\!source\temp\platform\!release\basemem.obj" -"\XASH3D\src_main\!source\temp\platform\!release\baseutils.obj" -"\XASH3D\src_main\!source\temp\platform\!release\brushbsp.obj" -"\XASH3D\src_main\!source\temp\platform\!release\bspfile.obj" -"\XASH3D\src_main\!source\temp\platform\!release\csg.obj" -"\XASH3D\src_main\!source\temp\platform\!release\faces.obj" -"\XASH3D\src_main\!source\temp\platform\!release\filesystem.obj" -"\XASH3D\src_main\!source\temp\platform\!release\flow.obj" -"\XASH3D\src_main\!source\temp\platform\!release\imglib.obj" -"\XASH3D\src_main\!source\temp\platform\!release\leakfile.obj" -"\XASH3D\src_main\!source\temp\platform\!release\lightmap.obj" -"\XASH3D\src_main\!source\temp\platform\!release\map.obj" -"\XASH3D\src_main\!source\temp\platform\!release\patches.obj" -"\XASH3D\src_main\!source\temp\platform\!release\platform.obj" -"\XASH3D\src_main\!source\temp\platform\!release\portals.obj" -"\XASH3D\src_main\!source\temp\platform\!release\pr_comp.obj" -"\XASH3D\src_main\!source\temp\platform\!release\pr_lex.obj" -"\XASH3D\src_main\!source\temp\platform\!release\prtfile.obj" -"\XASH3D\src_main\!source\temp\platform\!release\qbsp3.obj" -"\XASH3D\src_main\!source\temp\platform\!release\qcc_utils.obj" -"\XASH3D\src_main\!source\temp\platform\!release\qccmain.obj" -"\XASH3D\src_main\!source\temp\platform\!release\qrad3.obj" -"\XASH3D\src_main\!source\temp\platform\!release\qvis3.obj" -"\XASH3D\src_main\!source\temp\platform\!release\shaders.obj" -"\XASH3D\src_main\!source\temp\platform\!release\spritegen.obj" -"\XASH3D\src_main\!source\temp\platform\!release\studio.obj" -"\XASH3D\src_main\!source\temp\platform\!release\studio_utils.obj" -"\XASH3D\src_main\!source\temp\platform\!release\textures.obj" -"\XASH3D\src_main\!source\temp\platform\!release\trace.obj" -"\XASH3D\src_main\!source\temp\platform\!release\tree.obj" -"\XASH3D\src_main\!source\temp\platform\!release\winding.obj" -"\XASH3D\src_main\!source\temp\platform\!release\writebsp.obj" -"\XASH3D\src_main\!source\temp\platform\!release\ziplib.obj" -] -Creating command line "link.exe @C:\Temp\RSPF6E.tmp" -Creating temporary file "C:\Temp\RSPF6F.bat" with contents -[ -@echo off -copy \XASH3D\src_main\!source\temp\platform\!release\platform.dll "D:\Xash3D\bin\platform.dll" -] -Creating command line "C:\Temp\RSPF6F.bat" -Compiling... -qccmain.c -Linking... - Creating library ..\temp\platform\!release/platform.lib and object ..\temp\platform\!release/platform.exp -

Output Window

-Performing Custom Build Step on \XASH3D\src_main\!source\temp\platform\!release\platform.dll -‘Є®ЇЁа®ў ­® д ©«®ў: 1. - - - -

Results

-platform.dll - 0 error(s), 0 warning(s) -
- - diff --git a/platform/qcclib/pr_comp.c b/platform/qcclib/pr_comp.c index 3f641d89..75ab873f 100644 --- a/platform/qcclib/pr_comp.c +++ b/platform/qcclib/pr_comp.c @@ -7063,6 +7063,7 @@ def_t *PR_GetDef (type_t *type, char *name, def_t *scope, bool allocate, int arr { def->nextlocal = pr.localvars; pr.localvars = def; + def->local = true; } return def; diff --git a/platform/qcclib/pr_lex.c b/platform/qcclib/pr_lex.c index 9cbd3c43..240b0e97 100644 --- a/platform/qcclib/pr_lex.c +++ b/platform/qcclib/pr_lex.c @@ -2197,10 +2197,8 @@ void PR_Warning (int type, char *file, int line, char *error, ...) va_end (argptr); PR_PrintScope(); - if (file) - Msg ("%s:%i: warning: %s\n", file, line, string); - else - Msg ("warning: %s\n", string); + if (file) Msg ("%s(%i) : warning C%i: %s\n", file, line, type, string); + else Msg ("warning C%i: %s\n", type, string); pr_warning_count++; } diff --git a/platform/qcclib/qcclib.h b/platform/qcclib/qcclib.h index 37ddd9df..c00bd8f1 100644 --- a/platform/qcclib/qcclib.h +++ b/platform/qcclib/qcclib.h @@ -207,14 +207,15 @@ typedef struct def_s type_t *type; char *name; struct def_s *next; - struct def_s *nextlocal; //provides a chain of local variables - gofs_t ofs; //for the opt_locals_marshalling optimisation. + struct def_s *nextlocal; // provides a chain of local variables + gofs_t ofs; // for the opt_locals_marshalling optimisation. struct def_s *scope; // function the var was defined in, or NULL int initialized; // 1 when a declaration included "= immediate" int constant; // 1 says we can use the value over and over again + bool local; // 1 indices local variable int references; - int timescalled; //part of the opt_stripfunctions optimisation. + int timescalled; // part of the opt_stripfunctions optimisation. int s_file; int s_line; @@ -390,6 +391,7 @@ typedef enum { ERR_INVALIDSTRINGIMMEDIATE, ERR_BADCHARACTURECODE, ERR_BADPARMS, + ERR_EXCEEDERRCOUNT, WARN_MAX, }; diff --git a/platform/qcclib/qccmain.c b/platform/qcclib/qccmain.c index bdb79edb..aaf41372 100644 --- a/platform/qcclib/qccmain.c +++ b/platform/qcclib/qccmain.c @@ -11,7 +11,8 @@ extern int optres_test1; extern int optres_test2; int writeasm; - +int level; + cachedsourcefile_t *sourcefile; bool PR_SimpleGetToken (void); void PR_LexWhitespace (void); @@ -553,14 +554,14 @@ void PR_WriteData (int crc) } //compression of blocks? - if (compressoutput) progs.blockscompressed |=1; //statements - if (compressoutput) progs.blockscompressed |=2; //defs - if (compressoutput) progs.blockscompressed |=4; //fields - if (compressoutput) progs.blockscompressed |=8; //functions - if (compressoutput) progs.blockscompressed |=16; //strings - if (compressoutput) progs.blockscompressed |=32; //globals - if (compressoutput) progs.blockscompressed |=64; //line numbers - if (compressoutput) progs.blockscompressed |=128; //types + if (compressoutput) progs.blockscompressed |=1; //statements + if (compressoutput) progs.blockscompressed |=2; //defs + if (compressoutput) progs.blockscompressed |=4; //fields + if (compressoutput) progs.blockscompressed |=8; //functions + if (compressoutput) progs.blockscompressed |=16; //strings + if (compressoutput) progs.blockscompressed |=32; //globals + if (compressoutput) progs.blockscompressed |=64; //line numbers + if (compressoutput) progs.blockscompressed |=128; //types //include a type block? types = debugtarget;//!!PR_CheckCompConstDefined("TYPES"); //useful for debugging and saving (maybe, anyway...). @@ -621,9 +622,7 @@ void PR_WriteData (int crc) } if (def->references <= 0) { - if (def->constant) PR_Warning(WARN_NOTREFERENCEDCONST, strings + def->s_file, def->s_line, "%s no references", def->name); - else PR_Warning(WARN_NOTREFERENCED, strings + def->s_file, def->s_line, "%s no references", def->name); - + if(def->local) PR_Warning(WARN_NOTREFERENCED, strings + def->s_file, def->s_line, "'%s' : unreferenced local variable", def->name); if (opt_unreferenced && def->type->type != ev_field) { optres_unreferenced++; @@ -2060,7 +2059,6 @@ void PR_FinishCompile(void); void PR_SetDefaultProperties (void) { extern int ForcedCRC; - int level; int i; Hash_InitTable(&compconstantstable, MAX_CONSTANTS, Qalloc(BytesForBuckets(MAX_CONSTANTS))); @@ -2279,13 +2277,10 @@ void PR_main ( void ) //as part of the quake engine } else *qccmsourcedir = '\0'; - //autoprototype = true; - PR_InitData (); PR_BeginCompilation (Qalloc (0x100000), 0x100000); sprintf (qccmprogsdat, "%sprogs.src", qccmsourcedir); - Msg ("Source file: %s\n", qccmprogsdat); qccmsrc = QCC_LoadFile (qccmprogsdat); if (writeasm) @@ -2299,7 +2294,10 @@ void PR_main ( void ) //as part of the quake engine pr_file_p = SC_ParseToken(&qccmsrc); strcpy (destfile, token); - Msg ("outputfile: %s\n", destfile); + FS_StripExtension( token ); + + // msvc6.0 style message + Msg("--------------------Configuration: %s - Vm16 %s--------------------\n", token, level <= 0 ? "Debug" : "Release" ); pr_dumpasm = false; currentchunk = NULL; @@ -2322,6 +2320,7 @@ void PR_ContinueCompile(void) qccmsrc = originalqccmsrc; PR_SetDefaultProperties(); autoprototype = false; + Msg("Compiling...\n"); return; } PR_FinishCompile(); @@ -2356,13 +2355,12 @@ void PR_ContinueCompile(void) break; } strcat (qccmfilename, s); - if (autoprototype) Msg ("prototyping %s\n", qccmfilename); - else Msg ("compiling %s\n", qccmfilename); + Msg ("%s\n", qccmfilename); qccmsrc2 = QCC_LoadFile (qccmfilename); if(!PR_CompileFile (qccmsrc2, qccmfilename)) { - Msg("Compile errors limit exceeded %i, stop compilation\n", MAX_ERRORS); + Msg("error count exceeds %i; stopping compilation\n", MAX_ERRORS); Sys_Error("%s - %i error(s), %i warning(s)\n", destfile, pr_error_count, pr_warning_count); } } @@ -2384,8 +2382,6 @@ void PR_FinishCompile(void) // report / copy the data files PR_CopyFiles (); - Msg ("Compile Complete\n\n"); - if (optres_shortenifnots) Msg("optres_shortenifnots %i\n", optres_shortenifnots); if (optres_overlaptemps) Msg("optres_overlaptemps %i\n", optres_overlaptemps); if (optres_noduplicatestrings) Msg("optres_noduplicatestrings %i\n", optres_noduplicatestrings); @@ -2411,7 +2407,7 @@ void PR_FinishCompile(void) if (optres_test1) Msg("optres_test1 %i\n", optres_test1); if (optres_test2) Msg("optres_test2 %i\n", optres_test2); - Msg("numtemps %i\n", numtemps); + Msg ("‘Є®ЇЁа®ў ­® д ©«®ў: 1.\n\n");// enigma from M$ :) Msg("%s - %i error(s), %i warning(s)\n", destfile, pr_error_count, pr_warning_count); qcc_compileactive = false; @@ -2431,6 +2427,8 @@ bool CompileDATProgs ( void ) { PR_main(); + if (autoprototype) Msg ("Prototyping...\n"); + else Msg("Compiling...\n"); while(qcc_compileactive) PR_ContinueCompile(); diff --git a/public/basemath.h b/public/basemath.h index 02205c7f..9a75233b 100644 --- a/public/basemath.h +++ b/public/basemath.h @@ -13,6 +13,7 @@ #define SIDE_ON 2 #define EQUAL_EPSILON 0.001 +#define STOP_EPSILON 0.1 #define DEG2RAD( a ) ( a * M_PI ) / 180.0F #ifndef M_PI @@ -34,6 +35,12 @@ _inline void VectorScale(const vec3_t a, const float b, vec3_t c){c[0]=b*a[0];c[ #define VectorNegate(x, y) {y[0] =-x[0]; y[1]=-x[1]; y[2]=-x[2];} _inline float anglemod(const float a){return(360.0/65536) * ((int)(a*(65536/360.0)) & 65535);} +#define VectorM(scale1, b1, c) ((c)[0] = (scale1) * (b1)[0],(c)[1] = (scale1) * (b1)[1],(c)[2] = (scale1) * (b1)[2]) +#define VectorMAM(scale1, b1, scale2, b2, c) ((c)[0] = (scale1) * (b1)[0] + (scale2) * (b2)[0],(c)[1] = (scale1) * (b1)[1] + (scale2) * (b2)[1],(c)[2] = (scale1) * (b1)[2] + (scale2) * (b2)[2]) +#define VectorMAMAM(scale1, b1, scale2, b2, scale3, b3, c) ((c)[0] = (scale1) * (b1)[0] + (scale2) * (b2)[0] + (scale3) * (b3)[0],(c)[1] = (scale1) * (b1)[1] + (scale2) * (b2)[1] + (scale3) * (b3)[1],(c)[2] = (scale1) * (b1)[2] + (scale2) * (b2)[2] + (scale3) * (b3)[2]) +#define VectorMAMAMAM(scale1, b1, scale2, b2, scale3, b3, scale4, b4, c) ((c)[0] = (scale1) * (b1)[0] + (scale2) * (b2)[0] + (scale3) * (b3)[0] + (scale4) * (b4)[0],(c)[1] = (scale1) * (b1)[1] + (scale2) * (b2)[1] + (scale3) * (b3)[1] + (scale4) * (b4)[1],(c)[2] = (scale1) * (b1)[2] + (scale2) * (b2)[2] + (scale3) * (b3)[2] + (scale4) * (b4)[2]) + + _inline void VectorBound(const float min, vec3_t v, const float max) { v[0] = bound(min, v[0], max); @@ -79,6 +86,16 @@ _inline bool VectorCompare (const vec3_t v1, const vec3_t v2) return true; } +_inline bool VectorICompare (const short* v1, const short* v2) +{ + int i; + + for (i = 0; i < 3; i++ ) + if (abs(v1[i] - v2[i]) > 0) + return false; + return true; +} + _inline vec_t VectorNormalize (vec3_t v) { float length, ilength; @@ -160,7 +177,7 @@ _inline void VectorVectors(vec3_t forward, vec3_t right, vec3_t up) CrossProduct(right, forward, up); } -_inline void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) +_inline void AngleVectorsRight(vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) { float angle; static float sr, sp, sy, cr, cp, cy; @@ -182,17 +199,98 @@ _inline void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t u forward[1] = cp*sy; forward[2] = -sp; } - if (right) + if (right || up) { - right[0] = (-1*sr*sp*cy+-1*cr*-sy); - right[1] = (-1*sr*sp*sy+-1*cr*cy); - right[2] = -1*sr*cp; + if (angles[ROLL]) + { + angle = angles[ROLL] * (M_PI*2 / 360); + sr = sin(angle); + cr = cos(angle); + if (right) + { + right[0] = -1*(sr*sp*cy+cr*-sy); + right[1] = -1*(sr*sp*sy+cr*cy); + right[2] = -1*(sr*cp); + } + if (up) + { + up[0] = (cr*sp*cy+-sr*-sy); + up[1] = (cr*sp*sy+-sr*cy); + up[2] = cr*cp; + } + } + else + { + if (right) + { + right[0] = sy; + right[1] = -cy; + right[2] = 0; + } + if (up) + { + up[0] = (sp*cy); + up[1] = (sp*sy); + up[2] = cp; + } + } } - if (up) +} + +_inline void AngleVectorsLeft(const vec3_t angles, vec3_t forward, vec3_t left, vec3_t up) +{ + float angle; + static float sr, sp, sy, cr, cp, cy; + // static to help MS compiler fp bugs + + angle = angles[YAW] * (M_PI*2 / 360); + sy = sin(angle); + cy = cos(angle); + angle = angles[PITCH] * (M_PI*2 / 360); + sp = sin(angle); + cp = cos(angle); + + if (forward) { - up[0] = (cr*sp*cy+-sr*-sy); - up[1] = (cr*sp*sy+-sr*cy); - up[2] = cr*cp; + forward[0] = cp*cy; + forward[1] = cp*sy; + forward[2] = -sp; + } + if (left || up) + { + if (angles[ROLL]) + { + angle = angles[ROLL] * (M_PI*2 / 360); + sr = sin(angle); + cr = cos(angle); + if (left) + { + left[0] = sr*sp*cy+cr*-sy; + left[1] = sr*sp*sy+cr*cy; + left[2] = sr*cp; + } + if (up) + { + up[0] = cr*sp*cy+-sr*-sy; + up[1] = cr*sp*sy+-sr*cy; + up[2] = cr*cp; + } + } + else + { + if (left) + { + left[0] = -sy; + left[1] = cy; + left[2] = 0; + } + if (up) + { + up[0] = sp*cy; + up[1] = sp*sy; + up[2] = cp; + } + } } } diff --git a/public/basetypes.h b/public/basetypes.h index 4ecb8aa7..cd2d82b5 100644 --- a/public/basetypes.h +++ b/public/basetypes.h @@ -41,12 +41,10 @@ typedef unsigned int uint; typedef signed __int64 int64; typedef struct file_s file_t; typedef struct vfile_s vfile_t; -typedef struct edict_s edict_t; -typedef int func_t; typedef struct image_s image_t; typedef struct model_s model_t; +typedef int func_t; typedef int string_t; -typedef struct gclient_s gclient_t; typedef int progsnum_t; typedef struct progfuncs_s progfuncs_t; typedef float vec_t; @@ -54,6 +52,7 @@ typedef vec_t vec3_t[3]; typedef vec_t vec4_t[4]; typedef long fs_offset_t; typedef vec_t matrix3x4[3][4]; +typedef struct prvm_edict_s prvm_edict_t; typedef struct { int fileofs; int filelen; }lump_t; typedef struct { byte r; byte g; byte b; } color24; typedef struct { uint b:5; uint g:6; uint r:5; } color16; diff --git a/public/bspmodel.h b/public/bspmodel.h index d4083dd7..b109d664 100644 --- a/public/bspmodel.h +++ b/public/bspmodel.h @@ -207,6 +207,15 @@ typedef struct cmodel_s vec3_t mins, maxs; // boundbox vec3_t origin; // for sounds or lights int headnode; // bsp info + + vec3_t normalmins; // bounding box at angles '0 0 0' + vec3_t normalmaxs; + + vec3_t yawmins; // bounding box if yaw angle is not 0, but pitch and roll are used + vec3_t yawmaxs; + + vec3_t rotatedmins; // bounding box if pitch or roll are used + vec3_t rotatedmaxs; int numframes; //sprite framecount void *extradata; //for studio models diff --git a/public/const.h b/public/const.h index a1a0f943..a41997be 100644 --- a/public/const.h +++ b/public/const.h @@ -79,10 +79,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define PRINT_HIGH 2 // critical messages #define PRINT_CHAT 3 // chat messages -#define SPAWNFLAG_NOT_EASY 0x00000100 -#define SPAWNFLAG_NOT_MEDIUM 0x00000200 -#define SPAWNFLAG_NOT_HARD 0x00000400 -#define SPAWNFLAG_NOT_DEATHMATCH 0x00000800 +#define SPAWNFLAG_NOT_EASY 256 +#define SPAWNFLAG_NOT_MEDIUM 512 +#define SPAWNFLAG_NOT_HARD 1024 +#define SPAWNFLAG_NOT_DEATHMATCH 2048 // entity_state_t->renderfx flags #define RF_MINLIGHT 1 // allways have some light (viewmodel) @@ -148,23 +148,29 @@ typedef enum WEAPON_FIRING } weaponstate_t; -#define MOVETYPE_NONE 0 // never moves -#define MOVETYPE_NOCLIP 1 // origin and angles change with no interaction -#define MOVETYPE_PUSH 2 // no clip to world, push on box contact -#define MOVETYPE_STOP 3 // no clip to world, stops on box contact -#define MOVETYPE_WALK 4 // gravity -#define MOVETYPE_STEP 5 // gravity, special edge handling -#define MOVETYPE_FLY 6 -#define MOVETYPE_TOSS 7 // gravity -#define MOVETYPE_FLYMISSILE 8 // extra size to monsters -#define MOVETYPE_BOUNCE 9 -#define MOVETYPE_FOLLOW 10 // attached models -#define MOVETYPE_VEHICLE 11 -#define MOVETYPE_PUSHABLE 12 -#define MOVETYPE_DEBRIS 13 // non-solid debris that can still hurt you -#define MOVETYPE_RAIN 14 // identical to MOVETYPE_FLYMISSILE, but doesn't cause splash noises when touching water. -#define MOVETYPE_PENDULUM 15 // same as MOVETYPE_PUSH, but used only for pendulums to grab special-case problems -#define MOVETYPE_CONVEYOR 16 +typedef enum +{ + SOLID_NOT, // no interaction with other objects + SOLID_TRIGGER, // only touch when inside, after moving + SOLID_BBOX, // touch on edge + SOLID_BSP // bsp clip, touch on edge +} solid_t; + +typedef enum +{ + MOVETYPE_NONE = 0, // never moves, but can collide + MOVETYPE_NOCLIP, // origin and angles change with no interaction + MOVETYPE_PUSH, // no clip to world, push on box contact + MOVETYPE_WALK, // player case + MOVETYPE_STEP, // monster case (get rid of this) + MOVETYPE_FLY, // ignore gravity + MOVETYPE_TOSS, // gravity + MOVETYPE_BOUNCE, + MOVETYPE_FOLLOW, // attached models + MOVETYPE_COMPLEX, // complex moving ents (parent system) + MOVETYPE_RAGDOLL, // npc ragdoll (not implemented yet) + +} movetype_t; /* ============================================================== @@ -233,7 +239,7 @@ SYSTEM SPECIFIC ============================================================== */ -extern int curtime; // time returned by last Sys_Milliseconds +extern double curtime; void Sys_Error (char *error, ...); void Com_Printf (char *msg, ...); @@ -267,14 +273,16 @@ COLLISION DETECTION // a trace is returned when a box is swept through the world typedef struct { - bool allsolid; // if true, plane is not valid - bool startsolid; // if true, the initial point was in a solid area - float fraction; // time completed, 1.0 = didn't hit anything + bool allsolid; // if true, plane is not valid + bool startsolid; // if true, the initial point was in a solid area + bool startstuck; // if true, the initial point was stuck into SOLID_BSP model + + float fraction; // time completed, 1.0 = didn't hit anything vec3_t endpos; // final position - cplane_t plane; // surface normal at impact - csurface_t *surface; // surface hit - int contents; // contents on other side of surface hit - struct edict_s *ent; // not set by CM_*() functions + cplane_t plane; // surface normal at impact + csurface_t *surface; // surface hit + int contents; // contents on other side of surface hit + prvm_edict_t *ent; // not set by CM_*() functions } trace_t; @@ -316,7 +324,7 @@ typedef struct byte pm_time; // each unit = 8 ms short gravity; short delta_angles[3]; // add to command angles to get view direction - // changed by spawns, rotating objects, and teleporters + // changed by spawns, rotating objects, and teleporters } pmove_state_t; @@ -342,6 +350,7 @@ typedef struct usercmd_s short forwardmove, sidemove, upmove; byte impulse; // remove? byte lightlevel; // light level the player is standing on + } usercmd_t; @@ -357,14 +366,14 @@ typedef struct // results (out) int numtouch; - struct edict_s *touchents[MAXTOUCH]; + prvm_edict_t *touchents[MAXTOUCH]; vec3_t viewangles; // clamped float viewheight; vec3_t mins, maxs; // bounding box size - struct edict_s *groundentity; + prvm_edict_t *groundentity; int watertype; int waterlevel; @@ -807,16 +816,16 @@ typedef struct // these fields do not need to be communicated bit-precise - vec3_t viewangles; // for fixed views - vec3_t viewoffset; // add to pmovestate->origin + vec3_t viewangles; // for fixed views + vec3_t viewoffset; // add to pmovestate->origin vec3_t kick_angles; // add to view direction to get render angles - // set by weapon kicks, pain effects, etc + // set by weapon kicks, pain effects, etc vec3_t gunangles; vec3_t gunoffset; int gunindex; int gunframe; // studio frame - int sequence; // stuido animation sequence + int sequence; // studio animation sequence int gunbody; int gunskin; diff --git a/public/ref_system.h b/public/ref_system.h index 1a0b5af2..2351f3bb 100644 --- a/public/ref_system.h +++ b/public/ref_system.h @@ -286,7 +286,7 @@ typedef struct float vieworg[3]; float viewangles[3]; float blend[4]; // rgba 0-1 full screen blend - float time; // time is used to auto animate + double time; // time is used to auto animate int rdflags; // RDF_UNDERWATER, etc byte *areabits; // if not NULL, only areas with set bits will be drawn @@ -439,51 +439,6 @@ typedef struct scriptsystem_api_s /* ============================================================================== -NETWORK MESSAGES INTERFACE -============================================================================== -*/ - -typedef struct message_write_s -{ - //interface validator - size_t api_size; // must matched with sizeof(message_write_t) - - void (*Begin)( int dest ); // marker of start message - void (*WriteChar) (int c); - void (*WriteByte) (int c); - void (*WriteWord) (int c); - void (*WriteShort) (int c); - void (*WriteLong) (int c); - void (*WriteFloat) (float f); - void (*WriteString) (char *s); - void (*WriteCoord) (vec3_t pos); // some fractional bits - void (*WriteDir) (vec3_t pos); // single byte encoded, very coarse - void (*WriteAngle) (float f); - void (*Send)( msgtype_t type, vec3_t origin, edict_t *ent );// end of message -} message_write_t; - -typedef struct message_read_s -{ - //interface validator - size_t api_size; // must matched with sizeof(message_read_t) - - void (*Begin)( void ); // begin reading - int (*ReadChar) ( void ); - int (*ReadByte) ( void ); - int (*ReadLong) ( void ); - int (*ReadShort) ( void ); - float *(*ReadDir) ( void ); // return value from anorms.h - float (*ReadFloat) ( void ); - float (*ReadAngle) ( void ); - void *(*ReadData) (int len ); - float *(*ReadCoord) ( void ); // x, y, z coords - char *(*ReadString) ( bool line ); // get line once only - void (*End)( void ); // message received -} message_read_t; - -/* -============================================================================== - INTERNAL COMPILERS INTERFACE ============================================================================== */ @@ -623,7 +578,7 @@ typedef struct renderer_exp_s void (*DrawGetPicSize) (int *w, int *h, char *name); // will return 0 0 if not found void (*DrawPic) (int x, int y, char *name); void (*DrawStretchPic) (int x, int y, int w, int h, char *name); - void (*DrawChar) (int x, int y, int c); + void (*DrawChar) (float x, float y, int c); void (*DrawString) (int x, int y, char *str); void (*DrawTileClear) (int x, int y, int w, int h, char *name); void (*DrawFill) (int x, int y, int w, int h, int c); diff --git a/public/vprogs.h b/public/vprogs.h index 75e06064..a1ae27d4 100644 --- a/public/vprogs.h +++ b/public/vprogs.h @@ -41,6 +41,7 @@ a internal virtual machine like as QuakeC, but it has more extensions #define MAX_PARMS 8 +// 16-bit mode #define dstatement_t dstatement16_t #define ddef_t ddef16_t //these should be the same except the string type @@ -320,14 +321,14 @@ enum { typedef struct statement16_s { word op; - word a,b,c; + short a,b,c; } dstatement16_t; typedef struct statement32_s { dword op; - dword a,b,c; + long a,b,c; } dstatement32_t; diff --git a/release.bat b/release.bat index 310e3fcb..7883d88d 100644 --- a/release.bat +++ b/release.bat @@ -24,7 +24,8 @@ if errorlevel 1 set BUILD_ERROR=1 %MSDEV% renderer/renderer.dsp %CONFIG%"renderer - Win32 Release" %build_target% if errorlevel 1 set BUILD_ERROR=1 -%MSDEV% server/server.dsp %CONFIG%"server - Win32 Release" %build_target% +resource\qcclib.exe -O3 +if errorlevel 0 copy resource\server.dat D:\Xash3D\xash\server.dat if errorlevel 1 set BUILD_ERROR=1 if "%BUILD_ERROR%"=="" goto build_ok diff --git a/renderer/gl_draw.c b/renderer/gl_draw.c index e60dbf77..dd78f2c6 100644 --- a/renderer/gl_draw.c +++ b/renderer/gl_draw.c @@ -60,18 +60,16 @@ void Draw_String (int x, int y, char *str) } } -void Draw_Char (int x, int y, int num) +void Draw_Char (float x, float y, int num) { - int row, col; - float frow, fcol, size; + int row, col; + float frow, fcol, size; num &= 255; - if ( (num&127) == 32 ) - return; // space + if ( (num & 127) == 32 ) return;// space - if (y <= -8) - return; // totally off screen + if (y <= -8) return; // totally off screen row = num>>4; col = num&15; diff --git a/renderer/gl_local.h b/renderer/gl_local.h index 8a8443cc..64ac10a2 100644 --- a/renderer/gl_local.h +++ b/renderer/gl_local.h @@ -414,7 +414,7 @@ void COM_FileBase (char *in, char *out); void Draw_GetPicSize (int *w, int *h, char *name); void Draw_Pic (int x, int y, char *name); void Draw_StretchPic (int x, int y, int w, int h, char *name); -void Draw_Char (int x, int y, int c); +void Draw_Char (float x, float y, int c); void Draw_String (int x, int y, char *str); void Draw_TileClear (int x, int y, int w, int h, char *name); void Draw_Fill (int x, int y, int w, int h, int c); diff --git a/renderer/gl_mesh.c b/renderer/gl_mesh.c index 46e86f41..435f0b7e 100644 --- a/renderer/gl_mesh.c +++ b/renderer/gl_mesh.c @@ -125,7 +125,7 @@ void GL_DrawAliasFrameLerp (dmdl_t *paliashdr, float backlerp) // move should be the delta back to the previous frame * backlerp VectorSubtract (currententity->oldorigin, currententity->origin, delta); - AngleVectors (currententity->angles, vectors[0], vectors[1], vectors[2]); + AngleVectorsRight(currententity->angles, vectors[0], vectors[1], vectors[2]); move[0] = DotProduct (delta, vectors[0]); // forward move[1] = -DotProduct (delta, vectors[1]); // left @@ -454,7 +454,7 @@ static bool R_CullAliasModel( vec3_t bbox[8], entity_t *e ) */ VectorCopy( e->angles, angles ); angles[YAW] = -angles[YAW]; - AngleVectors( angles, vectors[0], vectors[1], vectors[2] ); + AngleVectorsRight( angles, vectors[0], vectors[1], vectors[2] ); for ( i = 0; i < 8; i++ ) { @@ -604,12 +604,13 @@ void R_DrawAliasModel ( int passnum ) } if ( currententity->flags & RF_GLOW ) - { // bonus items will pulse with time + { + // bonus items will pulse with time float scale; float min; - scale = 0.1 * sin(r_newrefdef.time*7); - for (i=0 ; i<3 ; i++) + scale = 0.1 * sin(r_newrefdef.time * 0.7); + for (i = 0; i < 3; i++) { min = shadelight[i] * 0.8; shadelight[i] += scale; diff --git a/renderer/gl_rmain.c b/renderer/gl_rmain.c index 21fe9cca..66e37db2 100644 --- a/renderer/gl_rmain.c +++ b/renderer/gl_rmain.c @@ -499,7 +499,7 @@ void R_SetupFrame (void) // build the transformation matrix for the given view angles VectorCopy (r_newrefdef.vieworg, r_origin); - AngleVectors (r_newrefdef.viewangles, vforward, vright, vup); + AngleVectorsRight(r_newrefdef.viewangles, vforward, vright, vup); // current viewcluster if ( !( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) ) @@ -1511,7 +1511,7 @@ void R_RenderFrame (refdef_t *fd); image_t *Draw_FindPic (char *name); void Draw_Pic (int x, int y, char *name); -void Draw_Char (int x, int y, int c); +void Draw_Char (float x, float y, int c); void Draw_TileClear (int x, int y, int w, int h, char *name); void Draw_Fill (int x, int y, int w, int h, int c); void Draw_FadeScreen (void); diff --git a/renderer/gl_rsurf.c b/renderer/gl_rsurf.c index bd8ff586..aa8460c8 100644 --- a/renderer/gl_rsurf.c +++ b/renderer/gl_rsurf.c @@ -117,10 +117,10 @@ glpoly_t *WaterWarpPolyVerts (glpoly_t *p) out->numverts = p->numverts; v = p->verts[0]; nv = out->verts[0]; - for (i=0 ; inumverts ; i++, v+= VERTEXSIZE, nv+=VERTEXSIZE) + for (i = 0; i < p->numverts; i++, v+= VERTEXSIZE, nv+=VERTEXSIZE) { - nv[0] = v[0] + 4*sin(v[1]*0.05+r_newrefdef.time)*sin(v[2]*0.05+r_newrefdef.time); - nv[1] = v[1] + 4*sin(v[0]*0.05+r_newrefdef.time)*sin(v[2]*0.05+r_newrefdef.time); + nv[0] = v[0] + 4*sin(v[1] * 0.05 + r_newrefdef.time) * sin(v[2] * 0.05 + r_newrefdef.time); + nv[1] = v[1] + 4*sin(v[0] * 0.05 + r_newrefdef.time) * sin(v[2] * 0.05 + r_newrefdef.time); nv[2] = v[2]; nv[3] = v[3]; @@ -207,13 +207,12 @@ void DrawGLFlowingPoly (msurface_t *fa) p = fa->polys; - scroll = -64 * ( (r_newrefdef.time / 40.0) - (int)(r_newrefdef.time / 40.0) ); - if(scroll == 0.0) - scroll = -64.0; + scroll = -64 * ((r_newrefdef.time * 0.4) - (int)(r_newrefdef.time * 0.4) ); + if(scroll == 0.0) scroll = -64.0; qglBegin (GL_POLYGON); v = p->verts[0]; - for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) + for (i = 0; i < p->numverts; i++, v+= VERTEXSIZE) { qglTexCoord2f ((v[3] + scroll), v[4]); qglVertex3fv (v); @@ -762,9 +761,8 @@ dynamic: { float scroll; - scroll = -64 * ( (r_newrefdef.time / 40.0) - (int)(r_newrefdef.time / 40.0) ); - if(scroll == 0.0) - scroll = -64.0; + scroll = -64 * ( (r_newrefdef.time * 0.4) - (int)(r_newrefdef.time * 0.4) ); + if(scroll == 0.0) scroll = -64.0; for ( p = surf->polys; p; p = p->chain ) { @@ -810,7 +808,7 @@ dynamic: { float scroll; - scroll = -64 * ( (r_newrefdef.time / 40.0) - (int)(r_newrefdef.time / 40.0) ); + scroll = -64 * ((r_newrefdef.time * 0.4) - (int)(r_newrefdef.time * 0.4)); if(scroll == 0.0) scroll = -64.0; @@ -978,7 +976,7 @@ void R_DrawBrushModel ( int passnum ) vec3_t forward, right, up; VectorCopy (modelorg, temp); - AngleVectors (e->angles, forward, right, up); + AngleVectorsRight(e->angles, forward, right, up); modelorg[0] = DotProduct (temp, forward); modelorg[1] = -DotProduct (temp, right); modelorg[2] = DotProduct (temp, up); @@ -1193,7 +1191,7 @@ void R_DrawWorld (void) // auto cycle the world frame for texture animation memset (&ent, 0, sizeof(ent)); - ent.frame = (int)(r_newrefdef.time*2); + ent.frame = (int)(r_newrefdef.time * 0.5); currententity = &ent; gl_state.currenttextures[0] = gl_state.currenttextures[1] = -1; diff --git a/renderer/gl_warp.c b/renderer/gl_warp.c index d9c73804..5362f0eb 100644 --- a/renderer/gl_warp.c +++ b/renderer/gl_warp.c @@ -213,13 +213,12 @@ void EmitWaterPolys (msurface_t *fa) int i; float s, t, os, ot; float scroll; - float rdt = r_newrefdef.time; if (fa->texinfo->flags & SURF_FLOWING) - scroll = -64 * ( (r_newrefdef.time*0.5) - (int)(r_newrefdef.time*0.5) ); + scroll = -64 * ( (r_newrefdef.time * 0.5) - (int)(r_newrefdef.time * 0.5)); else scroll = 0; - for (bp=fa->polys ; bp ; bp=bp->next) + for (bp = fa->polys; bp; bp = bp->next) { p = bp; @@ -229,11 +228,11 @@ void EmitWaterPolys (msurface_t *fa) os = v[3]; ot = v[4]; - s = os + r_turbsin[(int)((ot*0.125+r_newrefdef.time) * TURBSCALE) & 255]; + s = os + r_turbsin[(int)((ot * 0.125 + r_newrefdef.time) * TURBSCALE) & 255]; s += scroll; s *= (1.0/64); - t = ot + r_turbsin[(int)((os*0.125+rdt) * TURBSCALE) & 255]; + t = ot + r_turbsin[(int)((os * 0.125 + r_newrefdef.time) * TURBSCALE) & 255]; t *= (1.0/64); qglTexCoord2f (s, t); diff --git a/renderer/r_sprite.c b/renderer/r_sprite.c index 59deb1ff..d7b4abde 100644 --- a/renderer/r_sprite.c +++ b/renderer/r_sprite.c @@ -244,13 +244,13 @@ mspriteframe_t *R_GetSpriteFrame (entity_t *currententity) void R_DrawSpriteModel( int passnum ) { - float alpha = 1.0F; + float alpha= 1.0F; mspriteframe_t *frame; vec3_t point, forward, right, up; msprite_t *psprite; entity_t *e = currententity; model_t *mod = currentmodel; - float realtime = r_newrefdef.time / 1000.000f; + float realtime = r_newrefdef.time; if ( (e->flags & RF_TRANSLUCENT) && (passnum == RENDERPASS_SOLID)) return;// solid if (!(e->flags & RF_TRANSLUCENT) && (passnum == RENDERPASS_ALPHA)) return;// solid @@ -281,7 +281,7 @@ void R_DrawSpriteModel( int passnum ) switch( psprite->type ) { case SPR_ORIENTED: - AngleVectors (e->angles, forward, right, up); + AngleVectorsRight(e->angles, forward, right, up); VectorScale(forward, 0.01, forward );//offset for decals VectorSubtract(e->origin, forward, e->origin ); break; diff --git a/renderer/r_studio.c b/renderer/r_studio.c index 4aef08a6..3c6c4c76 100644 --- a/renderer/r_studio.c +++ b/renderer/r_studio.c @@ -1012,7 +1012,7 @@ static bool R_StudioComputeBBox( vec3_t *bbox ) //rotate the bounding box VectorCopy( e->angles, angles ); angles[PITCH] = -angles[PITCH]; - AngleVectors( angles, vectors[0], vectors[1], vectors[2] ); + AngleVectorsRight( angles, vectors[0], vectors[1], vectors[2] ); for ( i = 0; i < 8; i++ ) { diff --git a/renderer/renderer.plg b/renderer/renderer.plg new file mode 100644 index 00000000..8c2d58ff --- /dev/null +++ b/renderer/renderer.plg @@ -0,0 +1,16 @@ + + +
+

Build Log

+

+--------------------Configuration: renderer - Win32 Debug-------------------- +

+

Command Lines

+ + + +

Results

+renderer.dll - 0 error(s), 0 warning(s) +
+ + diff --git a/resource/PROGS.SRC b/resource/PROGS.SRC new file mode 100644 index 00000000..de585e1d --- /dev/null +++ b/resource/PROGS.SRC @@ -0,0 +1,49 @@ +// +-----+ +// |Progs| +// +-----+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +// | Scratch Http://www.admdev.com/scratch | +// +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-+ +// | This file contains all a list of QuakeC files to compile into the final | +// | output file (progs.dat by default) | +// +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-+ + +server.dat // Output File + +defs.c // Definitions +main.c // Main subroutines +damage.c +player.c +client.c // Client subroutines +dummys.c // Null spawn functions +ents/internal.c // Entity Management +ents/lights.c // Light Entities +ents/ambient.c // Ambient Sounds +ents/ccam.c // Chasecam Effect + +//TRIGGERS +ents/triggers/triggers.c // Ambient Sounds +ents/triggers/trigger_generic.c +ents/triggers/trigger_once.c +ents/triggers/trigger_sequence.c +ents/triggers/trigger_message.c +ents/triggers/trigger_counter.c +ents/triggers/trigger_setviewpoint.c +ents/triggers/trigger_teleport.c +ents/triggers/trigger_hurt.c +ents/triggers/trigger_push.c +ents/triggers/trigger_changelevel.c +ents/triggers/trigger_setskill.c +ents/triggers/trigger_secret.c + +//FUNCS +ents/funcs/funcs.c +ents/funcs/func_mover.c +ents/funcs/func_door.c +ents/funcs/func_button.c +ents/funcs/func_path_corner.c +ents/funcs/func_train.c + +//ITEMS +ents/items/items.c + +impulses.c // Should ALWAYS remain at bottom. \ No newline at end of file diff --git a/resource/client.c b/resource/client.c new file mode 100644 index 00000000..b72106e0 --- /dev/null +++ b/resource/client.c @@ -0,0 +1,291 @@ +/* ++------+ +|Client| ++------+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +| Scratch Http://www.admdev.com/scratch | ++=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +| Handle's "clients" (eg, Players) connecting, disconnecting, etc. | ++=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +*/ + +//DEFS; + +void() CCamChasePlayer; // From Ccam.qc +void() CheckImpulses; // From Impulses.QC +void() PutClientInServer; //From Client.QC + +//END DEFS; + +/* ++=========+ +|PPRINT():| ++=========+==============================================================================+ +|Description: | +|This function prints a server wide(bprint) 'entity' 'did' 'what" message... v useful for all those| +|client joining, leaving msgs etc.. saves a fair amount of code i hope.. [8 lines i think..] | ++========================================================================================+ +*/ + +void(entity dude, string did, string what)pprint = +{ + bprint("\n"); + bprint(dude.netname); + bprint(did); + bprint(what); + bprint("\n"); +}; + +/* +CLIENTRESPAWN(); +*/ + +void() ClientRespawn = +{ + if (coop) + { + // get the spawn parms as they were at level start + GetLevelParms(); + // respawn + PutClientInServer(); + } + else if (deathmatch) + { + // set default spawn parms + SetNewParms(); + // respawn + PutClientInServer(); + } + else + { // restart the entire server + localcmd ("restart\n"); + } +}; + +/* +CLIENTOBITURARY() + +Description; +Describes the entity 'who_died' in relation to enitity 'who_killed'. +Called when a player gets 'killed' by KILLED(); [DAMAGE.QC] +*/ + +void(entity who_died, entity who_killed) ClientObiturary = +{ + local string deathstring; + local string who; + local float rnum, msgdt, fragnum; + + rnum = random(); + + if(who_died.flags & FL_CLIENT) + { + if(who_killed == world) + { + deathstring = "was killed"; + + if(who_died.watertype == CONTENT_WATER) + deathstring = " drowned"; + else if(who_died.watertype == CONTENT_SLIME) + deathstring = " melted"; + else if(who_died.watertype == CONTENT_LAVA) + deathstring = " got incinerated"; + + msgdt = TRUE; + } + + if(who_killed.classname == "door") + { + if(rnum < 0.25) + { + deathstring = " got crushed"; + } + else + deathstring = " angered the "; + } + + if(who_killed.classname == "button") + { + if(rnum < 0.25) + { + deathstring = " pushed it the wrong way"; + msgdt = TRUE; + } + else + deathstring = " angered the "; + } + + if(who_killed.classname == "train") + { + deathstring = " jumped infront the "; + } + + if(who_killed.classname == "teledeath") + { + deathstring = " was telefragged by "; + } + + if(who_killed.classname == "t_hurt") + { + deathstring = " got hurt too much..."; + } + + if(who_killed.classname == "t_push") + { + deathstring = " got pushed too far..."; + } + + if(who_killed == who_died) + { + deathstring = " killed themselves..."; + msgdt = TRUE; + } + + bprint(who_died.netname); + bprint(deathstring); + + if(msgdt != TRUE) + { + if(who_killed.flags & FL_CLIENT) + bprint(who_killed.netname); + else + bprint(who_killed.classname); + } + + bprint("\n"); + } +}; + +/* +=============== +|CLIENTKILL():| +================================================================================= +Description: +This function is called when the player enters the 'kill' command in the console. +================================================================================= +*/ + +void() ClientKill = +{ + //pprint(self, " has", " killed themselves."); + T_Damage(self, self, self, self.health); + ClientRespawn(); +}; + +/* +================== +|CLIENTCONNECT():| +================================================================================= +Description: +This function is called when the player connects to the server. +================================================================================= +*/ + +void() ClientConnect = +{ + pprint(self, " has", " joined the game."); + configstring (2, "sky"); //CS_SKY + configstring (30, "4" ); //CS_MAXCLIENTS +}; + +/* +================== +|CLIENTDISCONNECT():| +================================================================================= +Description: +This function is called when the player disconnects from the server. +================================================================================= +*/ + +void() ClientDisconnect = +{ + pprint(self, " has", " left the game."); +}; + +/* +==================== +|PLAYERPRETHINK():| +=========================================================== +Description: +This function is called every frame *BEFORE* world physics. +=========================================================== +*/ + + +void() PlayerPreThink = +{ + WaterMove (); + SetClientFrame (); + CheckImpulses(); +}; + +/* +==================== +|PLAYERPOSTTHINK():| +=========================================================== +Description: +This function is called every frame *AFTER* world physics. +=========================================================== +*/ + +void() PlayerPostThink = {}; + +/* +====================== +|PUTCLIENTINSERVER():| +=========================================================== +Description: +This function is called whenever a client enters the world. +It sets up the player entity. +=========================================================== +*/ + +entity() find_spawnspot = +{ + local entity spot; + local string a; + + if(deathmatch == 1) + a = "info_player_deathmatch"; + else if(coop == 1) + a = "info_player_coop"; + + else if(!deathmatch || !coop) + a = "info_player_start"; + + spot = find (world, classname, a); + + return spot; +}; + +void() PutClientInServer = +{ + local entity spawn_spot; // This holds where we want to spawn + spawn_spot = find_spawnspot(); //find (world, classname, "info_player_start"); // Find it :) + + self.classname = "player"; // I'm a player! + self.health = self.max_health = 100; // My health (and my max) is 100 + self.takedamage = DAMAGE_AIM; // I can be fired at + self.solid = SOLID_SLIDEBOX; // Things sort of 'slide' past me + self.movetype = MOVETYPE_WALK; // Yep, I want to walk. + self.flags = FL_CLIENT; // Yes, I'm a client. + + self.origin = spawn_spot.origin + '0 0 1'; // Move to the spawnspot location + self.angles = spawn_spot.angles; // Face the angle the spawnspot indicates + self.fixangle = TRUE; // Turn this way immediately + + setmodel (self, "progs/player.mdl"); // Set my player to the player model + setsize (self, VEC_HULL_MIN, VEC_HULL_MAX); // Set my size + + self.view_ofs = '0 0 22'; // Center my view + + if (self.aflag) + CCamChasePlayer (); + + self.velocity = '0 0 0'; // Stop any old movement + + self.th_pain = PlayerPain; + self.th_die = PlayerDie; + + GetLevelParms(); +}; + diff --git a/resource/damage.c b/resource/damage.c new file mode 100644 index 00000000..d60bb248 --- /dev/null +++ b/resource/damage.c @@ -0,0 +1,163 @@ +/* ++------+ +|Damage| ++------+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +| Scratch http://www.inside3d.com/qctut/scratch.shtml | ++=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +| T_Damage and other like functions | ++=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +*/ +//DEFS +void(entity who_died, entity who_killed) ClientObiturary; +//END DEFS +/* +=-=-=-=-= + Killed +=-=-=-=-= +*/ + +void(entity targ, entity attacker) Killed = +{ + local entity oself; + + if (targ.health < -99) + targ.health = -99; // don't let sbar look bad if a player + + targ.takedamage = DAMAGE_NO; + targ.touch = SUB_Null; + + oself = self; + self = targ; // self must be targ for th_die + self.th_die (); + self = oself; + + ClientObiturary(targ, attacker); +}; + +/* ++=======+ +|T_Heal| ++=======+ +|Heal entity e for healamount possibly ignoring max health.| ++======================================================+ +*/ + +void(entity e, float healamount, float ignore) T_Heal = +{ + if (e.health <= 0) + return; + + if ((!ignore) && (e.health >= other.max_health)) + return; + + healamount = ceil(healamount); + + e.health = e.health + healamount; + + if ((!ignore) && (e.health >= other.max_health)) + e.health = other.max_health; +}; + +/* +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +T_Damage + +The damage is coming from inflictor, but get mad at attacker +This should be the only function that ever reduces health. +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ +void(entity targ, entity inflictor, entity attacker, float damage) T_Damage= +{ + local vector dir; + local entity oldself; + + if (!targ.takedamage) + return; + +// used by buttons and triggers to set activator for target firing + damage_attacker = attacker; + +// figure momentum add + if ( (inflictor != world) && (targ.movetype == MOVETYPE_WALK) ) + { + dir = targ.origin - (inflictor.absmin + inflictor.absmax) * 0.5; + dir = normalize(dir); + targ.velocity = targ.velocity + dir*damage*8; + } + +// check for godmode + if (targ.flags & FL_GODMODE) + return; + +// add to the damage total for clients, which will be sent as a single +// message at the end of the frame + if (targ.flags & FL_CLIENT) + { + targ.dmg_take = targ.dmg_take + damage; + targ.dmg_save = targ.dmg_save + damage; + targ.dmg_inflictor = inflictor; + } + +// team play damage avoidance + if ( (teamplay == 1) && (targ.team > 0)&&(targ.team == attacker.team) ) + return; + +// do the damage + targ.health = targ.health - damage; + + if (targ.health <= 0) + { + Killed (targ, attacker); + return; + } + +// react to the damage + oldself = self; + self = targ; + + if (self.th_pain) + self.th_pain (attacker, damage); + + self = oldself; +}; + + +/* +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +WaterMove + +Can be used for clients or monsters +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ +void() WaterMove = +{ + if (self.movetype == MOVETYPE_NOCLIP) + return; + if (self.health < 0) + return; + + if (self.waterlevel != 3) + { + self.air_finished = time + 12; + self.dmg = 2; + } + else if (self.air_finished < time && self.pain_finished < time) + { // drown! + self.dmg = self.dmg + 2; + if (self.dmg > 15) + self.dmg = 10; + T_Damage (self, world, world, self.dmg); + self.pain_finished = time + 1; + } + + if (self.watertype == CONTENT_LAVA && self.dmgtime < time) + { // do damage + self.dmgtime = time + 0.2; + T_Damage (self, world, world, 6*self.waterlevel); + } + else if (self.watertype == CONTENT_SLIME && self.dmgtime < time) + { // do damage + self.dmgtime = time + 1; + T_Damage (self, world, world, 4*self.waterlevel); + } +}; \ No newline at end of file diff --git a/resource/defs.c b/resource/defs.c new file mode 100644 index 00000000..5ed5232e --- /dev/null +++ b/resource/defs.c @@ -0,0 +1,414 @@ +/* ++----+ +|Defs| ++----+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +| Scratch Http://www.admdev.com/scratch | ++=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +| This contains necessary definitions from the original V1.06 defs.qc file. | +| This includes some basic constants, the built in function definitions, and | +| some variable's used by the Quake Engine internally. | +| Certain lines in this file are hardcoded into Quake engine, and -must- be | +| present and unchanged, in the order they are shown. Otherwise Quake will | +| refuse to run. | ++=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +*/ + +// These lines CANNOT be altered/moved +entity self; +entity other; +entity world; +float time; +float frametime; +float force_retouch; // force all entities to touch triggers +string mapname; +float deathmatch; +float coop; +float teamplay; +float serverflags; // propagated from level to level, used to +float total_secrets; +float total_monsters; +float found_secrets; // number of secrets found +float killed_monsters; // number of monsters killed +float parm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16; +vector v_forward, v_up, v_right; // set by makevectors() +float trace_allsolid; +float trace_startsolid; +float trace_fraction; +vector trace_endpos; +vector trace_plane_normal; +float trace_plane_dist; +entity trace_ent; +float trace_inopen; +float trace_inwater; + +entity msg_entity; // destination of single entity writes +void() main; // only for testing +void() StartFrame; +void() EndFrame; +void() PlayerPreThink; +void() PlayerPostThink; +void() ClientKill; +void() ClientConnect; +void() PutClientInServer; // call after setting the parm1... parms +void() ClientDisconnect; +void() SetNewParms; // called when a client first connects to +void() SetChangeParms; // call to set parms for self so they can +void end_sys_globals; // flag for structure dumping + +.float modelindex; // *** model index in the precached list +.vector absmin, absmax; // *** origin + mins / maxs +.float ltime; // local time for entity +.float movetype; +.float solid; +.vector origin; // *** +.vector oldorigin; // *** +.vector velocity; +.vector angles; +.vector avelocity; +.vector punchangle; // temp angle adjust from damage or recoil +.string classname; // spawn function +.string model; +.float frame; +.float skin; +.float body; +.float effects; +.float sequence; +.float renderfx; +.vector mins, maxs; // bounding box extents reletive to origin +.vector size; // maxs - mins +.void() touch; +.void() use; +.void() think; +.void() blocked; // for doors or plats, called when can't push other +.float nextthink; +.entity groundentity; +.float health; +.float frags; +.float weapon; // one of the IT_SHOTGUN, etc flags +.string weaponmodel; +.float weaponframe; +.float currentammo; +.float ammo_shells, ammo_nails, ammo_rockets, ammo_cells; +.float items; // bit flags +.float takedamage; +.entity chain; +.float deadflag; +.vector view_ofs; // add to origin to get eye point +.float button0; // fire +.float button1; // use +.float button2; // jump +.float impulse; // weapon changes +.float fixangle; +.vector v_angle; // view / targeting angle for players +.float idealpitch; // calculated pitch angle for lookup up slopes +.string netname; +.entity enemy; +.float flags; +.float colormap; +.float team; +.float max_health; // players maximum health is stored here +.float teleport_time; // don't back up +.float armortype; // save this fraction of incoming damage +.float armorvalue; +.float waterlevel; // 0 = not in, 1 = feet, 2 = wast, 3 = eyes +.float watertype; // a contents value +.float ideal_yaw; +.float yaw_speed; +.entity aiment; +.entity goalentity; // a movetarget or an enemy +.float spawnflags; +.string target; +.string targetname; +.float dmg_take; +.float dmg_save; +.entity dmg_inflictor; +.entity owner; // who launched a missile +.vector movedir; // mostly for doors, but also used for waterjump +.string message; // trigger messages +.float sounds; // either a cd track number or sound number +.string noise, noise1, noise2, noise3; // contains names of wavs to play + +void end_sys_fields; // flag for structure dumping +// End. Lines below this MAY be altered, to some extent + +// Built In functions +void(vector ang) makevectors = #1; // sets v_forward, etc globals +void(entity e, vector o) setorigin = #2; +void(entity e, string m) setmodel = #3; // set movetype and solid first +void(entity e, vector min, vector max) setsize = #4; +void() break = #6; +float() random = #7; // returns 0 - 1 +void(entity e, float chan, string samp, float vol, float atten) sound = #8; +vector(vector v) normalize = #9; +void(string e) error = #10; +void(string e) objerror = #11; +float(vector v) vlen = #12; +float(vector v) vectoyaw = #13; +entity() spawn = #14; +void(entity e) remove = #15; +void(vector v1, vector v2, float nomonsters, entity forent) traceline = #16; +entity() checkclient = #17; // returns a client to look for +entity(entity start, .string fld, string match) find = #18; +string(string s) precache_sound = #19; +string(string s) precache_model = #20; +void(entity client, string s)stuffcmd = #21; +entity(vector org, float rad) findradius = #22; +void(string s) dprint = #25; +string(float f) ftos = #26; +string(vector v) vtos = #27; +void() coredump = #28; // prints all edicts +void() traceon = #29; // turns statment trace on +void() traceoff = #30; +void(entity e) eprint = #31; // prints an entire edict +float(float yaw, float dist) walkmove = #32; // returns TRUE or FALSE +float() droptofloor = #34; // TRUE if landed on floor +void(float style, string value) lightstyle = #35; +float(float v) rint = #36; // round to nearest int +float(float v) floor = #37; // largest integer <= v +float(float v) ceil = #38; // smallest integer >= v +float(entity e) checkbottom = #40; // true if self is on ground +float(vector v) pointcontents = #41; // returns a CONTENT_* +float(float f) fabs = #43; +vector(entity e, float speed) aim = #44; // returns the shooting vector +float(string s) cvar = #45; // return cvar.value +void(string s) localcmd = #46; // put string into local que +entity(entity e) nextent = #47; // for looping through all ents +void() ChangeYaw = #49; // turn towards self.ideal_yaw +vector(vector v) vectoangles = #51; +void(float f) WriteByte = #52; +void(float f) WriteChar = #53; +void(float f) WriteShort = #54; +void(float f) WriteWord = #55; +void(float f) WriteLong = #56; +void(vector v) WriteCoord = #57; +void(float f) WriteAngle = #58; +void(string s) WriteString = #59; +void(entity s) WriteEntity = #60; +void(entity s) WriteFloat = #61; +void(vector v) WriteDir = #62; +void(float dest) MsgBegin = #63; +void(float to, vector v, entity e) MsgEnd = #64; +void(float num, string s) configstring = #65; +string(string s) precache_file = #68; // no effect except for -copy +void(entity e) makestatic = #69; +void(string s) changelevel = #70; +void(string var, string val) cvar_set = #72; // sets cvar.value +void(entity client, string s) centerprint = #73; // sprint, but in middle +void(entity client, string s, string s) centerprint2 = #73; +void(entity client, string s, string s, string s) centerprint3 = #73; +void(entity client, string s, string s, string s, string s) centerprint4 = #73; +void(entity client, string s, string s, string s, string s, string s) centerprint5 = #73; +void(entity client, string s, string s, string s, string s, string s, string s) centerprint6 = #73; +void(entity client, string s, string s, string s, string s, string s, string s, string s) centerprint7 = #73; +void(vector pos, string samp, float vol, float atten) ambientsound = #74; +string(string s) precache_model2 = #75; // registered version only +string(string s) precache_sound2 = #76; // registered version only +string(string s) precache_file2 = #77; // registered version only +void(entity e) setspawnparms = #78; // set parm1... to the + +// +// constants +// + +float FALSE = 0; +float TRUE = 1; + +// edict.flags +float FL_FLY = 1; +float FL_SWIM = 2; +float FL_CLIENT = 8; // set for all client edicts +float FL_INWATER = 16; // for enter / leave water splash +float FL_MONSTER = 32; +float FL_GODMODE = 64; // player cheat +float FL_NOTARGET = 128; // player cheat +float FL_ITEM = 256; // extra wide size for bonus items +float FL_ONGROUND = 512; // standing on something +float FL_PARTIALGROUND = 1024; // not all corners are valid +float FL_WATERJUMP = 2048; // player jumping out of water +float FL_JUMPRELEASED = 4096; // for jump debouncing + +// edict.movetype values +float MOVETYPE_NONE = 0; // never moves +float MOVETYPE_ANGLENOCLIP = 1; +float MOVETYPE_ANGLECLIP = 2; +float MOVETYPE_WALK = 3; // players only +float MOVETYPE_STEP = 4; // discrete, not real time unless fall +float MOVETYPE_FLY = 5; +float MOVETYPE_TOSS = 6; // gravity +float MOVETYPE_PUSH = 7; // no clip to world, push and crush +float MOVETYPE_NOCLIP = 8; +float MOVETYPE_FLYMISSILE = 9; // fly with extra size against monsters +float MOVETYPE_BOUNCE = 10; +float MOVETYPE_BOUNCEMISSILE = 11; // bounce with extra size + +// edict.solid values +float SOLID_NOT = 0; // no interaction with other objects +float SOLID_TRIGGER = 1; // touch on edge, but not blocking +float SOLID_BBOX = 2; // touch on edge, block +float SOLID_SLIDEBOX = 3; // touch on edge, but not an onground +float SOLID_BSP = 4; // bsp clip, touch on edge, block + +// range values +float RANGE_MELEE = 0; +float RANGE_NEAR = 1; +float RANGE_MID = 2; +float RANGE_FAR = 3; + +// deadflag values + +float DEAD_NO = 0; +float DEAD_DYING = 1; +float DEAD_DEAD = 2; +float DEAD_RESPAWNABLE = 3; + +// takedamage values + +float DAMAGE_NO = 0; +float DAMAGE_YES = 1; +float DAMAGE_AIM = 2; + +.void() th_stand; +.void() th_walk; +.void() th_run; +.void(entity attacker, float damage) th_pain; +.void() th_die; +.void() th_missile; +.void() th_melee; + +// point content values + +float CONTENT_EMPTY = -1; +float CONTENT_SOLID = -2; +float CONTENT_WATER = -3; +float CONTENT_SLIME = -4; +float CONTENT_LAVA = -5; +float CONTENT_SKY = -6; + +float STATE_RAISED = 0; +float STATE_LOWERED = 1; +float STATE_UP = 2; +float STATE_DOWN = 3; + +vector VEC_ORIGIN = '0 0 0'; +vector VEC_HULL_MIN = '-16 -16 -24'; +vector VEC_HULL_MAX = '16 16 32'; + +vector VEC_HULL2_MIN = '-32 -32 -24'; +vector VEC_HULL2_MAX = '32 32 64'; + +// protocol bytes +float SVC_BAD = 0; +float SVC_NOP = 1; +float SVC_DISCONNECT = 2; +float SVC_UPDATESTAT = 3; +float SVC_VERSION = 4; +float SVC_SETVIEW = 5; +float SVC_SOUND = 6; +float SVC_TIME = 7; +float SVC_PRINT = 8; +float SVC_STUFFTEXT = 9; +float SVC_SETANGLE = 10; +float SVC_SERVERINFO = 11; +float SVC_LIGHTSTYLE = 12; +float SVC_UPDATENAME = 13; +float SVC_UPDATEFRAGS = 14; +float SVC_CLIENTDATA = 15; +float SVC_STOPSOUND = 16; +float SVC_UPDATECOLORS = 17; +float SVC_PARTICLE = 18; +float SVC_DAMAGE = 19; +float SVC_SPAWNSTATIC = 20; +float SVC_SPAWNBINARY = 21; +float SVC_SPAWNBASELINE = 22; +float SVC_TEMPENTITY = 23; +float SVC_SETPAUSE = 24; +float SVC_SIGNONNUM = 25; +float SVC_CENTERPRINT = 26; +float SVC_KILLEDMONSTER = 27; +float SVC_FOUNDSECRET = 28; +float SVC_SPAWNSTATICSOUND = 29; // 1998-08-08 Complete SVC list by Zhenga +float SVC_INTERMISSION = 30; +float SVC_FINALE = 31; +float SVC_CDTRACK = 32; +float SVC_SELLSCREEN = 33; +float SVC_CUTSCENE = 34; // 1998-08-08 Complete SVC list by Zhenga + +float TE_SPIKE = 0; +float TE_SUPERSPIKE = 1; +float TE_GUNSHOT = 2; +float TE_EXPLOSION = 3; +float TE_TAREXPLOSION = 4; +float TE_LIGHTNING1 = 5; +float TE_LIGHTNING2 = 6; +float TE_WIZSPIKE = 7; +float TE_KNIGHTSPIKE = 8; +float TE_LIGHTNING3 = 9; +float TE_LAVASPLASH = 10; +float TE_TELEPORT = 11; + +// sound channels +// channel 0 never willingly overrides +// other channels (1-7) allways override a playing sound on that channel +float CHAN_AUTO = 0; +float CHAN_WEAPON = 1; +float CHAN_VOICE = 2; +float CHAN_ITEM = 3; +float CHAN_BODY = 4; + +float ATTN_NONE = 0; +float ATTN_NORM = 1; +float ATTN_IDLE = 2; +float ATTN_STATIC = 3; + +// update types + +float UPDATE_GENERAL = 0; +float UPDATE_STATIC = 1; +float UPDATE_BINARY = 2; +float UPDATE_TEMP = 3; + +// entity effects + +float EF_BRIGHTFIELD = 1; +float EF_MUZZLEFLASH = 2; +float EF_BRIGHTLIGHT = 4; +float EF_DIMLIGHT = 8; + + +// messages +float MSG_BROADCAST = 0; // unreliable to all +float MSG_ONE = 1; // reliable to one (msg_entity) +float MSG_ALL = 2; // reliable to all +float MSG_INIT = 3; // write to the init string + +float AS_STRAIGHT = 1; +float AS_SLIDING = 2; +float AS_MELEE = 3; +float AS_MISSILE = 4; + +void() SUB_Null = {}; +void() SUB_Null2 = {}; + +// Quake assumes these are defined. +entity activator; +string string_null; // null string, nothing should be held here + +.string wad, map; +.float worldtype, delay, wait, lip, light_lev, speed, style, skill; +.string killtarget; +.vector pos1, pos2, mangle; + +void(vector o, vector d, float color, float count) particle = #48;// start a particle effect +void(string s) bprint = #23; +void(entity client, string s) sprint = #24; +void() SUB_Remove = {remove(self);}; +// End + +// Damage.qc +entity damage_attacker; +.float pain_finished, air_finished, dmg, dmgtime; + +//ChaseCAm +.vector camview; +.float aflag; +.entity trigger_field; diff --git a/resource/dummys.c b/resource/dummys.c new file mode 100644 index 00000000..3c40ad34 --- /dev/null +++ b/resource/dummys.c @@ -0,0 +1,132 @@ +/* ++------+ +|Dummys| ++------+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +| Scratch Http://www.admdev.com/scratch | ++=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +| This file contains remove(self); statements for entities not yet coded. | +| This avoids Quake spewing out pages and pages of error messages when | +| loading maps. | ++=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +*/ +// General Junk +void() event_lightning = {remove(self);}; +void() misc_fireball = {remove(self);}; +void() misc_explobox2 = {remove(self);}; +void() trap_spikeshooter = {remove(self);}; +void() trap_shooter = {remove(self);}; +void() func_bossgate = {remove(self);}; +void() func_episodegate = {remove(self);}; +//void() func_illusionary = {remove(self);}; +//void() func_train = {remove(self);}; +//void() func_button = {remove(self);}; +//void() func_door = {remove(self);}; +void() func_door_secret = {remove(self);}; +void() func_plat = {remove(self);}; +void() func_wall = {remove(self);}; +void() info_intermission = {remove(self);}; +void() info_null = {remove(self);}; +//void() info_teleport_destination= {remove(self);}; +//void() path_corner = {remove(self);}; + +// Triggers +//void() trigger_relay = {remove(self);}; +//void() trigger_multiple = {remove(self);}; +//void() trigger_once = {remove(self);}; +//void() trigger_changelevel = {remove(self);}; +//void() trigger_counter = {remove(self);}; +//void() trigger_teleport = {remove(self);}; +//void() trigger_secret = {remove(self);}; +//void() trigger_setskill = {remove(self);}; +void() trigger_monsterjump = {remove(self);}; +void() trigger_onlyregistered = {remove(self);}; +//void() trigger_push = {remove(self);}; +//void() trigger_hurt = {remove(self);}; + +// Player Starts +void() info_player_start = {}; +//void() info_player_start2 = {}; +void() info_player_deathmatch = {}; +void() info_player_coop = {}; + +// Weapons +void() weapon_supershotgun = {remove(self);}; +void() weapon_nailgun = {remove(self);}; +void() weapon_supernailgun = {remove(self);}; +void() weapon_grenadelauncher = {remove(self);}; +void() weapon_rocketlauncher = {remove(self);}; +void() weapon_lightning = {remove(self);}; + +// Monsters +void() monster_enforcer = {remove(self);}; +void() monster_ogre = {remove(self);}; +void() monster_demon1 = {remove(self);}; +void() monster_shambler = {remove(self);}; +void() monster_knight = {remove(self);}; +void() monster_army = {remove(self);}; +void() monster_wizard = {remove(self);}; +void() monster_dog = {remove(self);}; +void() monster_zombie = {remove(self);}; +void() monster_boss = {remove(self);}; +void() monster_tarbaby = {remove(self);}; +void() monster_hell_knight = {remove(self);}; +void() monster_fish = {remove(self);}; +void() monster_shalrath = {remove(self);}; +void() monster_oldone = {remove(self);}; + +void() item_health = {remove(self);}; +void() item_megahealth_rot = {remove(self);}; +void() item_armor1 = {remove(self);}; +void() item_armor2 = {remove(self);}; +void() item_armorInv = {remove(self);}; +void() item_shells = {remove(self);}; +void() item_spikes = {remove(self);}; +void() item_rockets = {remove(self);}; +void() item_cells = {remove(self);}; +void() item_key1 = {remove(self);}; +void() item_key2 = {remove(self);}; +void() item_artifact_invulnerability = {remove(self);}; +void() item_artifact_envirosuit = {remove(self);}; +void() item_artifact_invisibility = {remove(self);}; +void() item_artifact_super_damage = {remove(self);}; + +void barrel_spawn(string netname1, string model1, string deathmessage, float damage) +{ + local float oldz; + + precache_model (model1); + precache_sound ("weapons/r_exp3.wav"); + + if (!self.dmg) self.dmg = damage; + self.netname = netname1; + + self.owner = self; + self.solid = SOLID_BBOX; + self.movetype = MOVETYPE_NONE; + setmodel (self, model1); + self.health = 20; + self.th_die = SUB_Null; + self.takedamage = DAMAGE_AIM; + self.think = SUB_Null; + self.nextthink = -1; + self.flags = 0; + + self.origin_z = self.origin_z + 2; + oldz = self.origin_z; + + droptofloor(); + + if (oldz - self.origin_z > 250) + { + dprint ("explosive box fell out of level at "); + dprint (vtos(self.origin)); + dprint ("\n"); + remove(self); + } +} + +void() misc_explobox = +{ + float f, g; + barrel_spawn("Large exploding box", "models/barrel.mdl", " was blown up by an explosive box", 750); +}; \ No newline at end of file diff --git a/resource/impulses.c b/resource/impulses.c new file mode 100644 index 00000000..efbfb67d --- /dev/null +++ b/resource/impulses.c @@ -0,0 +1,35 @@ +/* ++--------+ +|Impulses| ++--------+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +| Scratch Http://www.admdev.com/scratch | ++=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +| Handle and execute "Impulse" commands - as entered from console. | ++=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +*/ + +void() CheckImpulses = +{ + if (self.impulse == 15) + CCam (); + + if(self.impulse == 10) + { + local string a; + a = ftos(self.items); + sprint(self, a); + sprint(self, "Items Printed\n"); + } + if(self.impulse == 11) + { + self.items = self.items + 8; + sprint(self, "Items added to\{136}\n"); + } + + if(self.impulse == 150) + { + IEM_effects(TE_TELEPORT, self.origin); + } + + self.impulse = 0; // Clear impulse list. +}; \ No newline at end of file diff --git a/resource/main.c b/resource/main.c new file mode 100644 index 00000000..3ecc6cdd --- /dev/null +++ b/resource/main.c @@ -0,0 +1,137 @@ +/* ++----+ +|Main| ++----+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +| Scratch Http://www.admdev.com/scratch | ++=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +| Contains some 'base' subroutines. As a general rule nothing in this file | +| does much, except to setup basic variables and entities. | ++=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +*/ + +//INCLUDES; + +void() LightStyles_setup; // Entities/Lights.QC +void() precaches; + +//END INCLUDES; + +void() main = {}; + +/* +================ +|SETNEWPARMS():| +================================================================================= +Description: +This function is called to set up new player info. +================================================================================= +*/ + +void() SetNewParms = //Sets up start game parms +{ + parm1 = 10; + parm2 = 100; +}; + +/* +================== +|SETCHANGEPARMS():| +================================================================================= +Description: + +This function is called when the player hits a change level. Stores player info for +loading upon next level. +================================================================================= +*/ + +void() SetChangeParms = //called on changelevel; command +{ + if (self.health <= 0) + { + SetNewParms(); + return; + } + + parm1 = self.items; + parm2 = self.health; +}; + +/* +================== +|GETLEVELPARMS():| +================================================================================= +Description: + +This function is called by 'PUTCLIENTINSERVER(); in (CLIENT.QC)' and carries over +information in between level loads, or sets new parms at start map. + +Information stored in .parms which are the ONLY floats to be stored in memory in +between level loads. +================================================================================= +*/ + +void() GetLevelParms = +{ + if (world.model == "maps/start.bsp") + SetNewParms (); // take away all stuff on starting new episode + + self.items = parm1; + self.health = parm2; +}; + + +void() StartFrame = {}; +void() EndFrame = {}; + +/* +=============== +|WORLDSPAWN():| +================================================================================= +Description: +This function is called when the world spawns. +================================================================================= +*/ + +void() worldspawn = +{ + precaches(); + LightStyles_setup(); +}; + +/* +============== +|PRECACHES():| +================================================================================= +Description: +Precaches for the game. +================================================================================= +*/ + +void() precaches = +{ + precache_model ("progs/player.mdl"); + precache_model("progs/s_bubble.spr"); + precache_model("progs/eyes.mdl"); + + precache_model ("progs/enforcer.mdl"); //testing only + +// pain sounds + precache_sound ("player/drown1.wav"); // drowning pain + precache_sound ("player/drown2.wav"); // drowning pain + precache_sound ("player/lburn1.wav"); // slime/lava burn + precache_sound ("player/lburn2.wav"); // slime/lava burn + precache_sound ("player/pain1.wav"); + precache_sound ("player/pain2.wav"); + precache_sound ("player/pain3.wav"); + precache_sound ("player/pain4.wav"); + precache_sound ("player/pain5.wav"); + precache_sound ("player/pain6.wav"); + +// death sounds + precache_sound ("player/h2odeath.wav"); // drowning death + precache_sound ("player/death1.wav"); + precache_sound ("player/death2.wav"); + precache_sound ("player/death3.wav"); + precache_sound ("player/death4.wav"); + precache_sound ("player/death5.wav"); +}; diff --git a/resource/player.c b/resource/player.c new file mode 100644 index 00000000..392ec7e4 --- /dev/null +++ b/resource/player.c @@ -0,0 +1,248 @@ +/* ++------+ +|Player| ++------+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +| Scratch http://www.inside3d.com/qctut/scratch.shtml | ++=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +| Handle player animations and other misc player functions | ++=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +*/ +.float anim_time; // used for animation timing +.float anim_end; // end frame for current scene +.float anim_priority; // prioritize animations +.float anim_run; // running or not + +// client_t->anim_priority +float ANIM_BASIC = 0; // stand / run +float ANIM_PAIN = 1; +float ANIM_ATTACK = 2; +float ANIM_DEATH = 3; + + +// running + +$frame axrun1 axrun2 axrun3 axrun4 axrun5 axrun6 +$frame rockrun1 rockrun2 rockrun3 rockrun4 rockrun5 rockrun6 + +// standing +$frame stand1 stand2 stand3 stand4 stand5 +$frame axstnd1 axstnd2 axstnd3 axstnd4 axstnd5 axstnd6 +$frame axstnd7 axstnd8 axstnd9 axstnd10 axstnd11 axstnd12 + +// pain +$frame axpain1 axpain2 axpain3 axpain4 axpain5 axpain6 +$frame pain1 pain2 pain3 pain4 pain5 pain6 + +// death +$frame axdeth1 axdeth2 axdeth3 axdeth4 axdeth5 axdeth6 +$frame axdeth7 axdeth8 axdeth9 +$frame deatha1 deatha2 deatha3 deatha4 deatha5 deatha6 deatha7 deatha8 +$frame deatha9 deatha10 deatha11 +$frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8 +$frame deathb9 +$frame deathc1 deathc2 deathc3 deathc4 deathc5 deathc6 deathc7 deathc8 +$frame deathc9 deathc10 deathc11 deathc12 deathc13 deathc14 deathc15 +$frame deathd1 deathd2 deathd3 deathd4 deathd5 deathd6 deathd7 +$frame deathd8 deathd9 +$frame deathe1 deathe2 deathe3 deathe4 deathe5 deathe6 deathe7 +$frame deathe8 deathe9 + +// attacks +$frame nailatt1 nailatt2 +$frame light1 light2 +$frame rockatt1 rockatt2 rockatt3 rockatt4 rockatt5 rockatt6 +$frame shotatt1 shotatt2 shotatt3 shotatt4 shotatt5 shotatt6 +$frame axatt1 axatt2 axatt3 axatt4 axatt5 axatt6 +$frame axattb1 axattb2 axattb3 axattb4 axattb5 axattb6 +$frame axattc1 axattc2 axattc3 axattc4 axattc5 axattc6 +$frame axattd1 axattd2 axattd3 axattd4 axattd5 axattd6 + + +void () SetClientFrame = +{ +// note: call whenever weapon frames are called! + if (self.anim_time > time) + return; //don't call every frame, if it is the animations will play too fast + self.anim_time = time + 0.1; + + local float anim_change, run; + + if (self.velocity_x || self.velocity_y) + run = TRUE; + else + run = FALSE; + + anim_change = FALSE; + + // check for stop/go and animation transitions + if (run != self.anim_run && self.anim_priority == ANIM_BASIC) + anim_change = TRUE; + + if (anim_change != TRUE) + { + if (self.frame < self.anim_end) + { // continue an animation + self.frame = self.frame + 1; + return; + } + if (self.anim_priority == ANIM_DEATH) + { + if (self.deadflag == DEAD_DYING) + { + self.nextthink = -1; + self.deadflag = DEAD_DEAD; + } + return; // stay there + } + } + + // return to either a running or standing frame + self.anim_priority = ANIM_BASIC; + self.anim_run = run; + + if (self.velocity_x || self.velocity_y) + { // running + self.frame = $rockrun1; + self.anim_end = $rockrun6; + } + else + { // standing + self.frame = $stand1; + self.anim_end = $stand5; + } +}; + +/* +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + Pain sound, and Pain animation function +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ +void() PainSound = +{ + if (self.health < 0) + return; + self.noise = ""; + + if (self.watertype == CONTENT_WATER && self.waterlevel == 3) + { // water pain sounds + if (random() <= 0.5) + self.noise = "player/drown1.wav"; + else + self.noise = "player/drown2.wav"; + } + else if (self.watertype == CONTENT_SLIME || self.watertype == CONTENT_LAVA) + { // slime/lava pain sounds + if (random() <= 0.5) + self.noise = "player/lburn1.wav"; + else + self.noise = "player/lburn2.wav"; + } + + if (self.noise) + { + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); + return; + } +//don't make multiple pain sounds right after each other + if (self.pain_finished > time) + return; + self.pain_finished = time + 0.5; + + local float rs; + rs = rint((random() * 5) + 1); // rs = 1-6 + + if (rs == 1) + self.noise = "player/pain1.wav"; + else if (rs == 2) + self.noise = "player/pain2.wav"; + else if (rs == 3) + self.noise = "player/pain3.wav"; + else if (rs == 4) + self.noise = "player/pain4.wav"; + else if (rs == 5) + self.noise = "player/pain5.wav"; + else + self.noise = "player/pain6.wav"; + + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); +}; + +void () PlayerPain = +{ + if (self.anim_priority < ANIM_PAIN) // call only if not attacking and not already in pain + { + self.anim_priority = ANIM_PAIN; + self.frame = $pain1; + self.anim_end = $pain6; + } + PainSound (); +}; + +/* +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + Death sound, and Death function +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ +void() DeathSound = +{ + local float rs; + rs = rint ((random() * 4) + 1); // rs = 1-5 + + if (self.waterlevel == 3) // water death sound + self.noise = "player/h2odeath.wav"; + else if (rs == 1) + self.noise = "player/death1.wav"; + else if (rs == 2) + self.noise = "player/death2.wav"; + else if (rs == 3) + self.noise = "player/death3.wav"; + else if (rs == 4) + self.noise = "player/death4.wav"; + else if (rs == 5) + self.noise = "player/death5.wav"; + + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NONE); +}; + +void () PlayerDie = +{ + self.view_ofs = '0 0 -8'; + self.angles_x = self.angles_z = 0; + self.deadflag = DEAD_DYING; + self.solid = SOLID_NOT; + self.movetype = MOVETYPE_TOSS; + self.flags = self.flags - (self.flags & FL_ONGROUND); + if (self.velocity_z < 10) + self.velocity_z = self.velocity_z + random()*300; + + local float rand; + rand = rint ((random() * 4) + 1); // rand = 1-5 + + self.anim_priority = ANIM_DEATH; + if (rand == 1) + { + self.frame = $deatha1; + self.anim_end = $deatha11; + } + else if (rand == 2) + { + self.frame = $deathb1; + self.anim_end = $deathb9; + } + else if (rand == 3) + { + self.frame = $deathc1; + self.anim_end = $deathc15; + } + else if (rand == 4) + { + self.frame = $deathd1; + self.anim_end = $deathd9; + } + else + { + self.frame = $deathe1; + self.anim_end = $deathe9; + } + DeathSound(); +}; diff --git a/resource/progdefs.h b/resource/progdefs.h new file mode 100644 index 00000000..180e98bc --- /dev/null +++ b/resource/progdefs.h @@ -0,0 +1,287 @@ + +/* File generated by FTEQCC, relevent for engine modding only, the generated crc must be the same as your engine expects. */ + +typedef struct globalvars_s +{ int pad; + int ofs_return[3]; + int ofs_parm0[3]; + int ofs_parm1[3]; + int ofs_parm2[3]; + int ofs_parm3[3]; + int ofs_parm4[3]; + int ofs_parm5[3]; + int ofs_parm6[3]; + int ofs_parm7[3]; + int self; + int other; + int world; + float time; + float frametime; + float force_retouch; + string_t mapname; + float deathmatch; + float coop; + float teamplay; + float serverflags; + float total_secrets; + float total_monsters; + float found_secrets; + float killed_monsters; + float parm1; + float parm2; + float parm3; + float parm4; + float parm5; + float parm6; + float parm7; + float parm8; + float parm9; + float parm10; + float parm11; + float parm12; + float parm13; + float parm14; + float parm15; + float parm16; + vec3_t v_forward; + vec3_t v_up; + vec3_t v_right; + float trace_allsolid; + float trace_startsolid; + float trace_fraction; + vec3_t trace_endpos; + vec3_t trace_plane_normal; + float trace_plane_dist; + int trace_ent; + float trace_inopen; + float trace_inwater; + int msg_entity; + func_t main; + func_t StartFrame; + func_t EndFrame; + func_t PlayerPreThink; + func_t PlayerPostThink; + func_t ClientKill; + func_t ClientConnect; + func_t PutClientInServer; + func_t ClientDisconnect; + func_t SetNewParms; + func_t SetChangeParms; +} globalvars_t; + +typedef struct entvars_s +{ + float modelindex; + vec3_t absmin; + vec3_t absmax; + float ltime; + float movetype; + float solid; + vec3_t origin; + vec3_t oldorigin; + vec3_t velocity; + vec3_t angles; + vec3_t avelocity; + vec3_t punchangle; + string_t classname; + string_t model; + float frame; + float skin; + float body; + float effects; + float sequence; + float renderfx; + vec3_t mins; + vec3_t maxs; + vec3_t size; + func_t touch; + func_t use; + func_t think; + func_t blocked; + float nextthink; + int groundentity; + float health; + float frags; + float weapon; + string_t weaponmodel; + float weaponframe; + float currentammo; + float ammo_shells; + float ammo_nails; + float ammo_rockets; + float ammo_cells; + float items; + float takedamage; + int chain; + float deadflag; + vec3_t view_ofs; + float button0; + float button1; + float button2; + float impulse; + float fixangle; + vec3_t v_angle; + float idealpitch; + string_t netname; + int enemy; + float flags; + float colormap; + float team; + float max_health; + float teleport_time; + float armortype; + float armorvalue; + float waterlevel; + float watertype; + float ideal_yaw; + float yaw_speed; + int aiment; + int goalentity; + float spawnflags; + string_t target; + string_t targetname; + float dmg_take; + float dmg_save; + int dmg_inflictor; + int owner; + vec3_t movedir; + string_t message; + float sounds; + string_t noise; + string_t noise1; + string_t noise2; + string_t noise3; +} entvars_t; + +//with this the crc isn't needed for fields. +struct fieldvars_s +{ + int ofs; + int type; + char *name; +} fieldvars[] = { + {0, 2, "modelindex"}, + {1, 3, "absmin"}, + {1, 2, "absmin_x"}, + {2, 2, "absmin_y"}, + {3, 2, "absmin_z"}, + {4, 3, "absmax"}, + {4, 2, "absmax_x"}, + {5, 2, "absmax_y"}, + {6, 2, "absmax_z"}, + {7, 2, "ltime"}, + {8, 2, "movetype"}, + {9, 2, "solid"}, + {10, 3, "origin"}, + {10, 2, "origin_x"}, + {11, 2, "origin_y"}, + {12, 2, "origin_z"}, + {13, 3, "oldorigin"}, + {13, 2, "oldorigin_x"}, + {14, 2, "oldorigin_y"}, + {15, 2, "oldorigin_z"}, + {16, 3, "velocity"}, + {16, 2, "velocity_x"}, + {17, 2, "velocity_y"}, + {18, 2, "velocity_z"}, + {19, 3, "angles"}, + {19, 2, "angles_x"}, + {20, 2, "angles_y"}, + {21, 2, "angles_z"}, + {22, 3, "avelocity"}, + {22, 2, "avelocity_x"}, + {23, 2, "avelocity_y"}, + {24, 2, "avelocity_z"}, + {25, 3, "punchangle"}, + {25, 2, "punchangle_x"}, + {26, 2, "punchangle_y"}, + {27, 2, "punchangle_z"}, + {28, 1, "classname"}, + {29, 1, "model"}, + {30, 2, "frame"}, + {31, 2, "skin"}, + {32, 2, "body"}, + {33, 2, "effects"}, + {34, 2, "sequence"}, + {35, 2, "renderfx"}, + {36, 3, "mins"}, + {36, 2, "mins_x"}, + {37, 2, "mins_y"}, + {38, 2, "mins_z"}, + {39, 3, "maxs"}, + {39, 2, "maxs_x"}, + {40, 2, "maxs_y"}, + {41, 2, "maxs_z"}, + {42, 3, "size"}, + {42, 2, "size_x"}, + {43, 2, "size_y"}, + {44, 2, "size_z"}, + {45, 6, "touch"}, + {46, 6, "use"}, + {47, 6, "think"}, + {48, 6, "blocked"}, + {49, 2, "nextthink"}, + {50, 4, "groundentity"}, + {51, 2, "health"}, + {52, 2, "frags"}, + {53, 2, "weapon"}, + {54, 1, "weaponmodel"}, + {55, 2, "weaponframe"}, + {56, 2, "currentammo"}, + {57, 2, "ammo_shells"}, + {58, 2, "ammo_nails"}, + {59, 2, "ammo_rockets"}, + {60, 2, "ammo_cells"}, + {61, 2, "items"}, + {62, 2, "takedamage"}, + {63, 4, "chain"}, + {64, 2, "deadflag"}, + {65, 3, "view_ofs"}, + {65, 2, "view_ofs_x"}, + {66, 2, "view_ofs_y"}, + {67, 2, "view_ofs_z"}, + {68, 2, "button0"}, + {69, 2, "button1"}, + {70, 2, "button2"}, + {71, 2, "impulse"}, + {72, 2, "fixangle"}, + {73, 3, "v_angle"}, + {73, 2, "v_angle_x"}, + {74, 2, "v_angle_y"}, + {75, 2, "v_angle_z"}, + {76, 2, "idealpitch"}, + {77, 1, "netname"}, + {78, 4, "enemy"}, + {79, 2, "flags"}, + {80, 2, "colormap"}, + {81, 2, "team"}, + {82, 2, "max_health"}, + {83, 2, "teleport_time"}, + {84, 2, "armortype"}, + {85, 2, "armorvalue"}, + {86, 2, "waterlevel"}, + {87, 2, "watertype"}, + {88, 2, "ideal_yaw"}, + {89, 2, "yaw_speed"}, + {90, 4, "aiment"}, + {91, 4, "goalentity"}, + {92, 2, "spawnflags"}, + {93, 1, "target"}, + {94, 1, "targetname"}, + {95, 2, "dmg_take"}, + {96, 2, "dmg_save"}, + {97, 4, "dmg_inflictor"}, + {98, 4, "owner"}, + {99, 3, "movedir"}, + {99, 2, "movedir_x"}, + {100, 2, "movedir_y"}, + {101, 2, "movedir_z"}, + {102, 1, "message"}, + {103, 2, "sounds"}, + {104, 1, "noise"}, + {105, 1, "noise1"}, + {106, 1, "noise2"}, + {107, 1, "noise3"} +}; + +#define PROGHEADER_CRC 21645 diff --git a/resource/qcclib.exe b/resource/qcclib.exe new file mode 100644 index 0000000000000000000000000000000000000000..75676ca2c23d9d668925283b2d2be734248ad740 GIT binary patch literal 39424 zcmeIb4|o(sx;NUB=_H*nVFpMbK#)O#ib0G7G;s({NG1d&Ffn9?CR7_tbMbo0lP z;KWWeowk)dyWf89UDuVx-DTH3=lJa(WOrRn2%7+Mls~SE!m~zY-HweK1(QKW`u^VP z84~=nXP@uxeeS*I(s^pC>aDlls(P#Ht*W=)?v&oOgR^rS$HSQ>$L)nrkC*-a{ht#^ z9zFGUqq$!v{pYm3wk7{LZF$|gM)#(MjrTTG-{)Ray5fMXj!@Y zzKv^ZW~ZbiXGTfOchCAs@2v|zjk(=37Ju4wY?mTP#)C|h?~)&l5(v+e>p9L(fdP;fDDY2uQ08lYhtzDbW~+$w z<<lQ2K7hno*~J6si91Kh)KPr&^gZZF&` zaPPx$8<7WYF5D8hTDYxn?Qq|Pdk(G>?iILGaPPwn!o_WZ41vpp%Z6JBw;Zktt{$!x zZU@}caKD7x2X_Rn4{iW14s}h1V-M9M`j~i5O!9JP;4tG8I80v`XHVb~;#|DX=ZmLj zUbZzy-Ph&%S0oDDE!o*Ye5BoenC ze=}Lh*KLs2uSan8>P1VIEi72Fy2MxDUtR87;wxP4DV||TV z+z6?=PFz>Le%%8#?z)=lO^ggjc()K9iRYRP8>M^e+>M*6*VMRcQTFyTKcOuZm8M1 zPQ0-i5^kO77O9Ds>UC2d z({SIq4VUvdo3-aF`=@3DIJj}ceKi|I&_i9bS$n<`x4Pk8i4YrO{LP+2e5zf)5yfq| z*S%@ux(y;527s)sS!?mLblsYUjg1>?MR#CbBXw0_R+jtD=m1@^?i&r&4clnUCSy>> zz&;wc(~^_@*5ZFFhA=mK_H1r`td$$8F}kG<-`ucq%Z3G%+j?l8-Z9R>R(wt`HZ92caD}@%@_99P2}RZdaf=PwCaEyGShgM zmy41UiCnH$dajI-t0&B@z*H;3cbaM37Z9B)2%2dEz?T;&^!g%Hf8B@1wRMWuGHP`p zqU4Ko^(DtY{d+Ix*AJos@_vU}8j-tfN@+wdweYXA@V~=Kj&!|~+_H}{+^Ihi&49*t z+E8cx&5Nvn>2;g;zpDgaif6%l!#O{s|J|8C)4UH1avj;sgmSqL>& zk6vGxs&6Er-7Iq$E%o>!%`?=}@Nf}kS~{A(>*-g$oSaX;oET@s8Hb{bFVjCliLsPh z>kgyXorDISWel@YnhYkNKw4cNe(E_h?JyGSZTj_b997ex^a!Q%EtjQJUq_2leJ+1p zCX)458c#>im{t2+Tu^Pw6pVW9nM`VqYN|^z`QT6&Mi3`DJ8T85oubEzN*}AX-wSpcZ8ctGjEhK+mE$?owoRcY8Ed&cE(mFP5T!IxqY)FBcSR+6JY`dGolRdpWUE z-;IX1bxM9KwQW!=R##@a+D?iK%Jnfwt5m~k0*332emxFRPjBVs32H)Uer~?x43_)# ze*?oYT!9fU_wdGXH9;%NwHa|aovW+vrYS0)ajQNdp!jsf*Qfbhy}mOG(E`8b>oXLE;WP! zAmB%T1r+OeigFZv2~l~!tDEMkDP}tKGzM@Gz{UV5jf(3I*4tL+<)?!e(ikKNX4-Wa zXc&x{@Z_dU2UiY$q$>HTKxv=iBEOFfx;vl(jm6X$n zb-gXv4=E{q6x3b2PQlL^4CpIUQALafp*}-@4#<^?6w$98gYlL&7u0rTqEY%^ogB9- z1B}ogOW`>Ehyy>hnAD1NMDslZ)@1`#uLfB0Jvrz*ViOab&--38?Dfej{HF9|AYi7= z0*YS`Mv%+*v|nG2?5gjKDyT_H=@b4k{fAhnDl5BHv_pDAFJ>Ym6Ghsjc>QnaOz?}> zCyeK~I%*S&Z1EDloF7Bl7eo34fyyWrah46qW*1u|R&|^6PQT^lOmu+cV7~&!$2|TW zG{7-|Mo0#@k+_$J=&EkH^Wv)R!z3@F(>c`=7v7k-h(}$xzb`Z9KxSlGGGpAZ%qU<| zD66YmE4WDXS?KdiArc`3D=V+SET)MY%rVsjPp`OCoiZPd3gW3Z{TR&VaaH$xBrd4X zCrxFuRVm7Ji8ls=75;kDyqRHO_FNSN2UGRuGEm=Q9uv0`6I#SXQ~wfiAw>sd5!x7XQbkZ%*m%e6eW+vRLMNzGm=WL{K8D?l!GP=Z|J;4(jQ z#MS=HVFOgUy5-^%GDMEha&a$wX;iuX>uDr=Dy77LUV~s|?NG5pum#1nYO$a$$O>yE zHf6NBG<|WKsVwEUyMq35{c$u46Iby$j2!ubDUMgW)t9@%@$%bKRld#rj+fK;14jJb zBN!2bUfwOH)W_9x<@z74V);P1qf}@YjMRFY<`Y&E$@!N^<~2K3iwW{lhugq9F3h08 zWe5&8>JOGw-Oct6EoVyNNS+4OW=GCo$J9lwCl%?OBArp19nRKsIJ>B&13AYGES&?Y zbR_3kUd0*bqo)85U2r#z3tiay;ArQI-5sI*=sCI1NwU)UVr$Q7M7zSg98OexN1TuR zAIM;4w*4>@NTma&QgK+RIH6SZDHW%&a4Qw3`nLiDgk;?-p9}r}ljV)L`X93b?*2APy6t8uHT0lut~7el zBH)F<_x|-rP%4h7DXxdHajQ7x+|xZ+RA38<6E)u%NF4!;^6L)*t79sL&m!a*nNUjw z?059ffKMz9-|Kul1HCMVYn~v18-PEe+49rATdWR;oA<1vj%;i3*rRlOWaW%ZB%^c zqRs684RN#P6tx_w{kss|=_O8J8{PjHh)a&pg?pt$w2F$trp}ZO%bkKkfdRE7td#Cm zy%ELt0`+jTMVfCf)}e5lms6#Pyg#+&y%6$>sdDoGCykCGVUv%sQc2XuVoNj(jv!xU zh_UqsDFq#A)Bf;VFbW(F>YtxC&C31=>LTmwoUO!US&w(NK7|0-)JmRMMia30bL1%j z?0+#F%*cbt)$4ohTEva{SzSlN{l5mp>V4Mc(r~ofMIn^SiFU8?aaLFV<1E{NfNaQ} z2yf6A34!-GzHs8I4&Q4%zW1~FGVGzB#sL8Q4W#iUxb z^h~WH81e8GoK9G;_fMz(z_0>&tiizjJS1YOKq!2GluDz3a*NaO>t#d9p&uZ)^QF%3 z88h@3Sorx)yx2}s4Jl^~k$eTXbmI_=lzSkS?4 zOrODSengIJ{jVU09CGt1PArg{&vDzS?dqzqw$-dv?S-Ioz+b4(nhd^UGH#!vm7XaB zVFuUfG61>N-eqXp5hE>H6;nEAN@4wXRw_D+7f06e{%Eddhqdxy&1)NnIf3mPHv9*> zr%d&7XQz0%(N}r7yWqYB_dU2*;Hr^^U4>feFc;!BG)7Uy^!6dl zu1Yx!axN)e7EEl>aMH(G+mvb5bo(@HjMD?yzNE7)%r78YA-UxRrkKc@^5DCxtGa2^ z?ohk$2n5?Yw_k(I8OY^wzL*d!My?FME=9B5M%lXWz~Fg@&MP=j@iND&97IYqB4Afq z5LU}#aU|AKDs7Lb%Ja}cV6L;(jexpBXgld_O`}e`5HC68djy;~R`jPZfX&%D2==Kd z*I^5%lwyP6P*@m3Qa&#@+YYeQPKZA>Z9jhPM+2Ti&1u@)#aTzSxxeuPBwxeHJ&xO@ zA=Gi+VF2MS0>-BN9V0+31X`8%|fg zuOAx2ao8Kv0i`2h{?*cqj$&K!{Hw(@oYTi(eEIclIBwC3^j@*?ZfL>PQcAIrJm0-J zp*Xa_z2&ox1R(N=_v4h=U7m9?ZwiqP1VMWPXsh$+#FJxoB;499j-8(`+7<=$kI!PX zYGQ)T&^7)KSn+VC*)AE#qJD^rM^a@Fdp-gr9T+fX_? zZoTtl)OLPa~3re^o^fT!=w06$7uY+{yo@Ia85{f zerKOjk$%)rkKfUDOdMTE73oP>S!~+wkj{3vP|w@t=5$UpOR2I9mK zO-!)(WAVm0f`N8$&9r&@S3v%;R)S%st$zYVP0=o2iw;bMBqXU@R<<+ySua;!q290@ zLnaW+Ihiw}=fak0TsY4jlTH`u1%?~U!?784-c@RkINIy2%}bZgTiz#GJ!(FkY>$S^%f030{y=#-PO@{&KjV07 zh;u4*4~8{O89LrhFE6j~L#}56%ltD|v(bpS^n-NtSD?)zjTV#+OQ?F?M!6i0la_cc zkSL1PSe&0bRqYj%y}9Yq1^OAx>h-F6h5j0p9c(&7sXSY~IbMu)Z9MOYy#1Z{_R#t2+h5E_*e45M3Joh_ZFvk6^mvf`(+QWqD8EBjoq7(CI zv!^&dc_-n>JwiOC8SiLlVN|J73x(7|jK)7AD^{wawoYg3Zz-Iu)$x0A!39!U#_Xmz zbs%pZmC=0XOPL^M)h7w{Apw{xTjeW>T01Mgt1&%v4~iGe9&dcI>csB4rHd7Q8dK7u z)bx8JL1i>^FmARNt9C5X;>{hU++deYoIThjNTX28d%zJ=FQ~oht6w~ntD_X0y}9-h zevwb^<~0MH)S~4HUl@xndN6Do2(H-8&?Zq~xa@#v=Ec*0S7$0bDb(k&X{$A#P z2mCkF>a2E8lK-$3X>rmR`48(6h;M*CX+?h=I#O$U?E}-S+ks*0uiG@ni%e@-CTHsr zkjOcyX@rpb1Ns%mb=c-tHwp6}c#xmvZnqWWPLw{dDAbSl*R4QyokvwL3^CdTE5%r> zwXKDwjLCU_RL1>6_YG10|ww2Ofju!3FD%)Jrm*q_~#kGmpBG`baFy1N3>VOWn61hO}P4tb@M@BtvL@+E~;EbiW@sTb&~_fdT}0GO(P`qUuPX$RXwalU3>BuV+ zq*f5p;D=3` zRu=(r9b=Q*ITy=xqHn04<+Usm|7q@vF71L?x|>Mwe5xqe6yp zQTRQnUhD+F{Z~l*N(&V9Yk#0D`iuRh>BRwO{^prhieG;gAnd*vNm=*Q+wI}Hm7oyR zZ<RfJH6i}DS@NU13 zD9>7LTmvPj#xm67*5hXVDpaNtf%eo)0ZVc15JL8^ymrX8t{rIOoQpoe zwTk2o?roydpkswJ+QE2)eO^AL8`Fb(w+<&h$`YNexQE5^NC?!As+8(EyB$lnDlwKSb&5Ne3;e|mg$}T2M{bTrv}yI0EOaPQM}?i?df*5zKS|4t0Hz1 zu$dA4MqJ?F@<-jT_)bBKV;bX0GU6H~=cKZVLBLbG@>+Gu+4^ndg^t0LP7&=ONUQUR zwD~$F)Hd`PW3g4{>%{p!u&P>V#&-W^tGG^_Td25*(tLsyH}+C-IVJ(1?C@L=ZwP=m zbrem_&q{e=|>%7TA{rCcSM|f3QX+4J^P%G5XVF9>=fcPJ{%@__E~N2o#@7{_xPV{ zPd)LFmkW6~^<{BZN14q!0YOLdnWuM4DsJvO{*@${u!G2WJ-wShEhq@(anfrhPF~#t z3?TT1E-V4P@sYQzt+{{KX@vA16t3-g6nS47K>Wu)uG~d+|FKa40^3YT!4eEKbf8s3 z!JAJ4wc+D{{wRD4d6=k?piH3xayl8cXPW!x`Sj(9+o?xv0{Bq!T7%(C^*-RZqXNs8{p0Pu z9UFnYJdWYrd&jb0UZC(83J2@Gm8dH9A9^iXX~vgb)C$H4v|+Wia-?FZhB_?ZkKPXs z4~6kGXgXh{YPYw>pMgmCdU_hpb@)E%X``4MYJ^+LgIdK|=bmKf9XwrIreqz^USOyN zd);`{H@f=c&OWQYEWd3(`}2hhI~JO)P{6IeyB;$7%xCBWb^&P>jg1+R4ftXEP;?E3 zA>lx;Y^4S+B?qFnmXYaE99*q_5vPuXY83A^;K}<}Vy^C4HMh)E8={(VrX+(l`gUTHaQ z{KScQ`OFw%jQ;(1nB~lZ*pT(SH+}ujFP) zf|^eSKvY7q3Oa4@A%)roE1L@C+ci0+SoULATJZ%*n z&&g|_<2hDgx!J*q#Z;9AZ_~2k*J&?fRY{x;ctHQ_DP-%Z%<>79c3(K;=Db5EC!ZWT zKK%q%p0p=16$3h5IARv{_tA)qnv*c&gd0db8>Az6s{UjjQ^?Z(NlobBbD=A{3X@`z zbqLZg56h#EZ$Qt3d9A_?HIDD>Poi zl;#3{{pMIJ)DM~F?9Xputf-0VTUcGj`Ekh9*I$bjDnYfA8qMg(X;1wl4uGq1@!tgD zg9-~eH&42>wLWY-c)VAMj8X`W>x24 z(M`Nf1ZTdF+Tw&>Ns8ek;B^vOWGq8N(wRj}2KIHkK^B)H7tsA?+B1mI(u>L_`Sm79 zno7g1CWh8#^Pg))**x?nsq&21KneS^7(r{AL`5dL1jDY}lR}DzK+q{~!?eJ4*2km= zKrJ)aG-*G`r0&R9?So*PSHuAiTGC0**8jvjw929ZtK?H*#`rAXz}NQG!EQ6&i-FG$ z;QA1WRyq^6bt(mdm?AHB2)zaAbT~K0Ai`?}sYn2b?ZyiPxtiw`8ykw&NNk`rS3My2 zO~qJL9c&>^4w8x^Vhl2Qga?1F3dDiVPXI! zFePn@)mPV}uP}39O2i)6*c4@^kLr}t(G#f%yK;^VeXO29$N~SjQGlrq6ExCN5bS9G z(UV8tKYC)+LFwGkS=^hh%}fVIQD*AUd-kIZqdmaeA~Ym=fVkFZaQdK z@<_5V?G7#butp-){9R1^lH?s9(0`qXA=K6x5O4PD$0=NC+)ARC4z}z%BcPwbp$KK7 zAI4x!#kFcg&$e{9hI53N-AZDXopXcC?5WhHLNVW^9<}#)E^Zb)Vdq11C>-jg1w%^3 zCQg3Gj%&MynP-1&X8w=e3o-V3e@BUOKi}Yx`|S1uFyQGdz(B~<_9A%){Q2Y`0W1RN zMr8WYj)e9^TvxR(vUL>4fo%JtIJw6z_cG##FA>)k592Z{o7@v8_r_5besT$GhefX# z7>g+7(Mu`F|3j4bC|oOC6PyUQ9&Rn%N;tLXvC~S?4@OAiS;Gcm%0Dl1fIr!(` zhvA3eN8sZ?sBw7g1NLx?6DBpd6Yelv9~`Ds)#`)YJE*lI!&b|q5Y+K9Zh#drTur(}(-$!#MdP#{EY}VD1=!huWic(Z2f7E`LNl>U`)f ztZE~2fc80*AA#dhDF<9CTn1bw+%0h6a|ZaF0Y4pnI{Z}lkQR@*;A0v;=75h0`=%nae$ZP1R|nTbv}65JY)9)y^aG2dwIlifZ3B)4yR6^X?52GZbJ1Du zB1sBM`*OJ^j2!`HQ^!_ zL6ZzWtjat0oZargilwiZ#wtH*Cz+mg@SEg5EHxqw&R&-LvyC~iwvNKp(G4R}YgK;J z^j@Ie&TP|sZjixJ+J6geVQyBXFj^~kq;n0aup@}p!E>uN_gB=O& zb<+FSvW6Q}78$og{iL{}ea;)S7oBtmgl zk-RYX4e@B}L9xsNNgs?l5mdXo-gN~=oluisBFlV;=b@aJA$QKpADd*P|7uQ;ypL~r zo9y(Zj2X;a|2TOzNoaYGS}N_YT75U|S{6ofPlz|;X!23+De+p^<^Bz(&;XIiz$t(d z0D8^%Zq;h5#tSbZU$?gKbIXg6M=c`lb+A0pF^0}SP}Z#LxZehQ{blM9t>+zy#kofq z$x$5%6imih|I@0wSEB|57?2EYyV+44hr+JwNPtAo?UVL*6xdOT^p0BWRsay+GX7Ui zCo*;H>pzY?pSH+Eq+9K!U(B&!pd8A^cdt?3pcQK0kUWE1T*`IV2n>hz> zU1V52_8JUY(U98GPvINrr_ILTjc2kBsQcS&>f11b@$&xe zIxo$v%g!ckoxAp8KZb>x_LB09=MdrK8NVj4-nQ!(@RbxgA@#;TgI^HmR&?*AWX%!U zNm(^VJFGfri^FI69W9^Hn|}?7a%ZZcgHKF~xae@f*5v}&4qTLbB3auUT^~8sSJxfn z<8TIe)K*0E6E?S_7h`zi%^7Fgdq_}IZ_+YuQYy}=0a(q3)rxb_bSSGF()O;;_}k^r zZrg0HzwIvd4&HMIw(jz0?#8P~g%?r}9XGTkas1HnX)E^@T}+a$Lk*@)G^S~{_c4S^ z$xrUxj|{*_AdE^Lrw`nr73Bnb%^7x?v?D+dyE30^ti^NZY zQk0ElV8Dd=)DY5l(&(d$@l#a8l--D_^F(azy54r$OB}!FLL5#%UKntfnXg;vEK8Jj zl3!raXil$7ex{vz*P^R>T^CCVErb$RXj>sCCCT^j7a2s}imm0ka?FiQ4)b*eGB>$~ zrwZ*a*{E03*qG--Q1Yk;7$Q`JOU9fj(@Ijy{CdY^)Z8av%W9_3Eb+usn>O?EeL^am zE^JDXV9%%K?V?nsVPz8~bwnBE0gVp@wfPSbGW7N(6XdrCFh#n~^NZ#4?&i_*E9UVJ zNx;K`-EhfWeYDUF9@ojB-51_TQ&&CVc}rO(G)}}6Q9aX3!_zO0dQ}=Fcc*$*MUc?& zH~7*v^-a$SLQA_0Egopbd!7?gC6F4&$p0c>`~U__;{(cfO)OsuOJn#S0)MogOLO-` z8}2OxF|}NO6VXcb4wqt63te)q$&>|70F^1MmW3|WC4UAp`G}aRKDvYYOl{gx6szwo zss-9qG`%7`T{;IlQcf!=8uhv=os%Z0-)aRumQf_)K&f7wo;T{xVn3Aa>Y*I-3j*ex zx&(AzfqKg-rrwQ0R<|8&ia+}HB4bD`J*O@|&#TEwTvq3pWIFO@IewjE-o$}{9_ zC0juCcHVQlAXUqzyE)660Iwc5ATYG1Tu2;c)tTul+lv&qi%uyQGY>Yp(?%ILY0F}= zCyJ{*%7!O|)WnNUWRpfkk?-Kyj8$(J)CYJOi@dUpN2%%qg0c>4N*Q4&WkR%eYde-C@bX2>-%-soFLs5RM{(_dNTXgt=8*cz@sE-`mx8EIQd4`8 zapMTY6m4lALjdN#kXY1SV6_D}Dut46fjun@E=DQI%91e=l9^#x&D)P^OKk+26G8u| zquUN@+ic1LX_99O4R&cPXnEM&mJ*}u4Iql3E#%omu%iMp}IqlmVOsj)8 z;J8>_pu-R_qx3kVz<64+wUPmBK6@c@wPlX@Fvc*h5rGE%kaoL6DG`(cUS9iD5vRQH6ivR4;=+yt z7rRdVL9A38S8FR0zaLJP0EVh8D>_BT7!KWm#NWWgMwzy(EU z*ZWTO(3hs*vdCf&CKpV*^4O^mmuJq$R-;z1vU9dAL%{w9F8Ln!A%vk}3B21d8#gD^ z-&P4=r0pMQo-uK zj8`9|Nro+1)+@GB_DZtzv7dt{YI6juYM03`)0W&6kxjPsSgo!)r)6AFR=KcKp!};` zN+m2kc(sy;y$60hm4dXgV@V4rCQOBB8$tf8dz+B z;2J41*gD$EY7TI0$#Y?6LX)#-4yKT@#75Jw!l4!0l!Z9m&k6T00u!N{ErjRPZY7Sc za?Au}%+Wg{+R7YEQWthqBv`6>ZdRDH0WeECC$yEEA+f5G}F2)+^Xd zi3u^zj~?D@Esb$rgXDpf`x4t>?gGwl!M1~OehYRX*si$jQbdtlmz6p!?f!)rORT;F z3>TtXKX5oI?bvQ5fpM62D~kr0r6@gu z>s2=D<^1e+=po2jP)dB!jj%DL&h7r<)><8C8+5ihh~+nsa2b|22y?Ka47Zui)<1#A z^*n~C%~I@^2hBgx7U(|!-%GST1JYG;K80W_y^DOjDam3PyT~`vl!ToWP>OkF9tCh) zqhxE3a2z)0kC9=7Qk<$4rz=Yx%66AhEaZIfTQY>nIVOMZa{gf7?bDCm?o(Wl2V^|> zImdHc)4SLT;e8oP(*{{*@AiZnCx;%V^Cpfkf|A&~h#x?trI!SZaSEoC*D~pad}bQ$ z*;=spKuh&SEuHn6qGiGt%>Z$0Wyzc8-HjDOKm&GBC`!VOxH=lzK1EOyXqQ^TkCe{1 zu1R4XVu@mUS(>;IlwoLMEtYJ<@S@AjalhvVZsS^Rqnb;3YB$?V+KXLG_~KfH>W#zi zRM}~oDMio%<9$fPdEC_7mF*5}GR#uw+FJ~2wEPj?7ij(^eYHS+McvGcv*c~*rgRNn zPAqWWMcW+5LgMIWjH4e}zLZ#|(U5^pg+-0hTZQU`vAvBI^yXF|MtK51~{D$9XaEup_r57grHJdo9;jLzCu zh4&G)B0+$;nsot1huy3aSJ!!mR&;MF*a9d8rPCb4C(dqG%TbEW;``r&Zl2D z?&e(jGdQ?1!z{YJRaq|u)d20;tT*Y@?Sl4LI{2&hX7$K=dRN%q)#tG9^PGqNqVZPM ziH(it<)%q_tNG1o<=7_w7A;_ET}h&Yz-nIZjZ$C>7R%Rgz}f}jebS{aZF@R4wH~}N z7HP~;6FuEc6Ny-AIlE~k!w`$yHK5hvi?IIb6+F%1hGQ0$n?0=BVp@M8c&)I=%&E$D zq5nE$W-|>ost-a3mLNaqz-_a78??sEdd$$()eag%>RGirYe@buz3ZaA>ph2ki%a&H zuorEPxSGj64dVmdE`Ee2VOj69)^tDKWeP{P7d_6lyca-NA7 z`MU(;6VHLkozuINMPyFcUMwi9g_-u5#XRJ&V3{o|qm8-xTwJGNbk9^vTcs{{7MeU_?#Wand z&_2P_-I!<_GqVgTFS>ow3IxRl;nDvJknKaE1^B##+M~^NdCoQ#cBHqSl(I2*R@kaw zKqK*m#m125lg6d^OobGW6~e%><;bgWI3M~RveRb4ytDZIf9d;o00pq7y#&|2=KIAKi<)a3*kJpNYZyt1Z_2D(|%XtI_VYLmwnuHXn zYy@>@Epl~*anqTqHT9-%9t(^8tOM}pDb|zKF$%u zk>UX;#omBKOC9PVg(7L^q(tcArH#fkXx`cr7cCZNK+87MN|y{~mm`(Q@7l!_3jwJ& z;=K#VQx{Zb-PX;6IF_m`w1?EAOfb@0V2XliJHZ%@5Q;*G&pF0u=5#I=lhH|J zE3z1B;&<&*Tda;aNYKY2z|ya@(#*gP8wPv0bk7&TH>nuk4Y1u^UPpGC%%}-wb7_06 z6--d35RP$mFj!tkwwjdoYnJA0y&C=B8&BytnBew1l`Jw-ajC7Boq(LjtK+FVDV46` zrdY;*R(j0Vj|lbu1$mi;-El?)3q{N*GYIoNBB%N?*-HR@f)`RcYWt`{({LKLIjv6; zW5^zHvSp8$Z9puKG=Cfyvq!|syO#Oms?&MRr^H$qH^S_YO1Ru6u@sFRVUb9zAj`)S zo*{A0pf81oZBcWEv-OuK7^b2RUS&1yi&?PwYI__gWXgEp3#N?GxpT>evgv)R&Zm@B zeV5x%nmH#e8%k(k#9!mht;>uhqrLPb>Jtn~d@yJvSd|HsTUXG;ExLckn>x@s+)uXy zJ^l1nf;C14zHW?=?If7Eb1S+JmXh7l0?l_C_GeWAHcVg*)CsDzrNM@63Z(_C;epBc z0!wkWl5~a@?JlB0Lm*T2Wq?xrPP$DWIS{a6qCEXSLlhj$g$;Pj`>$Hu`L1=qudnMMJ=$#4pqdHsP=puOK=jEW5gyv9U~g*EF; zQ|gOa3ez}+k$z-kO@E5w0PVKb!0>kjik>t>SLPiSxAxsHqOzE9+ zvffw-ldH=9bpXcht(|T3MhGlqFZMYfp`lM`hK>$8QAw|lejlKyP0e|;ck zRdsVfPdx+Fa3x;Yx`DJkg^?22&0!hzj7FOj)Ga*gTJQYtJryB zX9xoX%YSTh6}#Si{(s^M~MDSajRF8Lur9!g!Vg#RM#awP0h#2wS!J zmu%G%ndO;HyoEO9xXZQPK$)7;nRP&PEf$5v(l~$pIYaQ*e`?tM^@C>Gei|f+vLoBX zj^t}>=0;dhHD-Lld}06%61MQ3cxIzG+poX=x9CKWmKZR|je}OzGD#d8HA!57w|m)4k!}m%rS1_{h-_+S z#zx%afS!x7OC2}SP}xuq>M03K*s8sF(XKmSoB{3~zQqFviEA}uNO)n7r*kF^A0{XYm zpBPsGeL4bEqj=~&tGWk^Q@j{pn_c>J6fkt6vTAZC8+CNj_$kCcSf zYBzSGFx93_Q6;@hh8Jc}Vz*_rymUm%bJY@D(#v8t44+&!$q5!<;xiUZDKsV#l#|8{ zcEyRK{rb3Qk`y;89Am++SVpxQ=O6F_rN-$eXm&AOa@1_CT$+Nehtak3QPKs$@Y?zK zPbi?f`=5f3)uZJCoy(f9ibb=cH4%B9FEH*@4?vMX)#-v+ z&7Px!MFCr=ifVI_YcGwo`#!g7^Ev89iZ`t^nbn4)+C<2r{*yuKSo+3S+(8I4U-ERH zWB6v=x*^AG``@w$+gk@VeT6;Pk$bTN%G3@Zne4%qklzQG1A%O~f5;w;h-?3)Js4qq zwLMrg|9=4CNx1L8Jqp(f*90fRt%rjh(Lcc+jB&!G1|NYt1vfynV|GXXx7maF>rskj z55^$~e#QD@q&?V*b*w)Kc}E?>?QqY*#q7AgWDiD|pcPC1JN96V<_Nt1+8!)Qn@152 zxKy|dxJUX9TrBs>wGOBeMlQ_x>H+l#nb^NGHEP*BSacND^)vi>K6aOQOEN7LYdzhuU1mQ& zmKl9p+l(YC`GDC?dVXjiQa$-(9q*JMCgIzw4t?1+o=m)L;<}*z20n$umV^1SX+!hy zR{40y_1bx5Z!Ltt?L2Er=7%&4Rw=HM}a+QtKGHfS2K5dI7ayF<;ev*qqUX zk`Es~vmWh=KA+r&094$2$&WsxH*nlxz#=2?ith&+U|;q8cl_4*-L^Yx?k!xuv9ack znr+;_P^?U6?CrR=jU z!8f6!pL+(K1vY$t8J~AvQ?Pbz1HQbBWuEilOWXva=u>gaqaU)aiGjEw`et*X)X;!$ zP{)AR5S`MEYo+xyCDj|&uCD=YKf%hyYJ3@d)8Y-a8wW1YTOX=I(3%7|i+`q+DY^Y;I*ZQ`uso6x|7cXA79$(nz zxXj^l0jgPw?@^=VUvk9_H8sBBZ)%r|4fry4V*&T8uSp4V2}J43SvcXV;X#hOFRr{s zv|3xZ5g*dVce4q%TwH4bIWCRjRIlaRHq~%RU!WAIZdga(JI8mfDJ9AU>dhb6O7LCx zqMAmrVdFOL`z%(7uatus+)Hr*>s#y8MDB+yjRm|r4;@K z@n8hkhTHT#RRS?__N?ohJOxx$bf$s{#N)ez~2V{7w{i||0Mhf z{3qbkB}6-XTO7Ss&2i75A@@AA?V)KD!Sj>1EPv;ZEx+Z5eUz2+ZVxekH}Jc5y}8S6 zC4cF+<)Q`hM*ZLXgZKqe{5`;b^+%_FG>QA`kMtki{q^*>?_@d3FaXNlta{VUt$wrq z%{gDjKYrpbedi$&w_7#qgVKT?@@+O2w0>M8!qFi0*+`TxVo*A{E6)`U{G%L5uf<&c z3P*3tk%RR3(%*IXZ#UN8m(#usDS<5cdw;Fmm;WxuZ{ddjY}`NgHItuOQppxqz;H*fou=3U{y6%JhCz!eT$;lLFRT;aeK4qV~D z6%JhCz!eT$;lLFRT;aeK4qV~D6%JhCz!eT$;lLFRT;aeK4qV~D6%JhCz!eT$;lTf2 zI6$@meItyMC`fO^#cYybM#aKBe6k&bnG}}-hxezL?HSpwIpD|$jC`_3BR?6A(#WPR zmPT|bFZuY=2%}5pQUoJ=HL_JBM`cfgBlx^%ydWAcjK=-&DbJlzczM)^`6r`a37^Vd zABEos-wn43j%?f-;Ha+D4ysQGj_TYJ^&f&y=`tLpx580eCw!{!GjLSi zPvHpfc{r-yD{z$lXE;hv2UCbYHn`bQAKpLdv2)a4*P%J1;d|i|e^*$|Ln!u;Ee_*F zC(6G6cX&Bj78uN48Tqi zG`{#e%FhKTz?E~W@#{nXmvf7`W!zH47vr}WVS4_+e+0We8@^s@Ju#p9n~EjUBZ_B8 z?}K?Z&=WB_)^QuSdqHg-Mo}qtpl0TqC~6w1QjCxw%CsVIwE8(TM*L4m&H`rLu~Uqmf&~>aYoQSqIDw zj7AxtYq%wVd;>I=fTDzyHK1C<#`E=O(kTsEU<>?H!1+9fi)4WPDu zM2+ufWvyprQQ`A3YHcX923#RbYUw7lkvMfPY5#@t0{8-qd>i`3!X?fU zg&1!sW^rj`nX^&SRycads@2B%k$)q%7HwXG(nj+3zv9bm#+S?Ue~~*QdGqBy_JMC# z`uM+79~YrNZIB?<7*(;6I6TYowh5CE%aKZRxe?>F`m(v0#N)r|;DM~gTx7Bq^r8am zi-HB|))Ru@LnsuoJp3Zab#`{brvzA4lVLSXz?p|%`s@qL^#G)2>3+`k28s0OGaPSc z&(i%u%Ns5PLyv5sXVIC&`-gTQ5PIZsvidt$WSV29n&wb6KCsC&AHkQ&{;@c!v&o77 z3>L)?w{z`V(Sl3yE$v&jY;7O76dy8C@`H(&;$}OlFt8;QE3e(8N)N}WhGzJS@u5rj zR(kv8aR!XlpGENVzY%Bri~T$jvalue && *value && strcmp(value, "0")) - { - int i, numspec; - - if (*spectator_password->string && strcmp(spectator_password->string, "none") && strcmp(spectator_password->string, value)) - { - Info_SetValueForKey(userinfo, "rejmsg", "Spectator password required or incorrect."); - return false; - } - - // count spectators - for (i = numspec = 0; i < maxclients->value; i++) - { - if (g_edicts[i+1].inuse && g_edicts[i+1].client->pers.spectator) - numspec++; - } - if (numspec >= maxspectators->value) - { - Info_SetValueForKey(userinfo, "rejmsg", "Server spectator limit is full."); - return false; - } - } - else - { - // check for a password - value = Info_ValueForKey (userinfo, "password"); - if (*password->string && strcmp(password->string, "none") && strcmp(password->string, value)) - { - Info_SetValueForKey(userinfo, "rejmsg", "Password required or incorrect."); - return false; - } - } - - - // they can connect - ent->client = game.clients + (ent - g_edicts - 1); - - // if there is already a body waiting for us (a loadgame), just - // take it, otherwise spawn one from scratch - if (ent->inuse == false) - { - // clear the respawning variables - InitClientResp (ent->client); - if (!game.autosaved || !ent->client->pers.weapon) - { - InitClientPersistant (ent->client,world->style); - } - } - - ClientUserinfoChanged (ent, userinfo); - - if (game.maxclients > 1) gi.dprintf ("%s connected\n", ent->client->pers.netname); - - ent->svflags = 0; // make sure we start with known default - ent->client->pers.connected = true; - - return true; -} diff --git a/server/g_cmds.c b/server/g_cmds.c deleted file mode 100644 index 4a3800bc..00000000 --- a/server/g_cmds.c +++ /dev/null @@ -1,2159 +0,0 @@ -#include "baseentity.h" -#include "m_player.h" - -int nostatus = 0; -int xray = 0; - -void RotateAngles(vec3_t in, vec3_t delta, vec3_t out) -{ - // Rotates input angles (in) by delta angles around - // the local coordinate system, returns new angles - // in out. - vec3_t X={1,0,0}; - vec3_t Y={0,1,0}; - float angle, c, s; - float xtemp, ytemp, ztemp; - - if(delta[ROLL] != 0) - { - // Rotate about the X axis by delta roll - angle = DEG2RAD(delta[ROLL]); - c = cos(angle); - s = sin(angle); - ytemp = c*X[1] - s*X[2]; - ztemp = c*X[2] + s*X[1]; - X[1] = ytemp; X[2] = ztemp; - ytemp = c*Y[1] - s*Y[2]; - ztemp = c*Y[2] + s*Y[1]; - Y[1] = ytemp; Y[2] = ztemp; - } - if(delta[PITCH] != 0) - { - // Rotate about the Y axis by delta yaw - angle = -DEG2RAD(delta[PITCH]); - c = cos(angle); - s = sin(angle); - ztemp = c*X[2] - s*X[0]; - xtemp = c*X[0] + s*X[2]; - X[0] = xtemp; X[2] = ztemp; - ztemp = c*Y[2] - s*Y[0]; - xtemp = c*Y[0] + s*Y[2]; - Y[0] = xtemp; Y[2] = ztemp; - } - if(delta[YAW] != 0) - { - // Rotate about the Z axis by delta yaw - angle = DEG2RAD(delta[YAW]); - c = cos(angle); - s = sin(angle); - xtemp = c*X[0] - s*X[1]; - ytemp = c*X[1] + s*X[0]; - X[0] = xtemp; X[1] = ytemp; - xtemp = c*Y[0] - s*Y[1]; - ytemp = c*Y[1] + s*Y[0]; - Y[0] = xtemp; Y[1] = ytemp; - } - if(in[ROLL] != 0) - { - // Rotate about X axis by input roll - angle = DEG2RAD(in[ROLL]); - c = cos(angle); - s = sin(angle); - ytemp = c*X[1] - s*X[2]; - ztemp = c*X[2] + s*X[1]; - X[1] = ytemp; X[2] = ztemp; - ytemp = c*Y[1] - s*Y[2]; - ztemp = c*Y[2] + s*Y[1]; - Y[1] = ytemp; Y[2] = ztemp; - } - if(in[PITCH] != 0) - { - // Rotate about Y axis by input pitch - angle = -DEG2RAD(in[PITCH]); - c = cos(angle); - s = sin(angle); - ztemp = c*X[2] - s*X[0]; - xtemp = c*X[0] + s*X[2]; - X[0] = xtemp; X[2] = ztemp; - ztemp = c*Y[2] - s*Y[0]; - xtemp = c*Y[0] + s*Y[2]; - Y[0] = xtemp; Y[2] = ztemp; - } - if(in[YAW] != 0) - { - // Rotate about Z axis by input yaw - angle = DEG2RAD(in[YAW]); - c = cos(angle); - s = sin(angle); - xtemp = c*X[0] - s*X[1]; - ytemp = c*X[1] + s*X[0]; - X[0] = xtemp; X[1] = ytemp; - xtemp = c*Y[0] - s*Y[1]; - ytemp = c*Y[1] + s*Y[0]; - Y[0] = xtemp; Y[1] = ytemp; - } - - out[YAW] = (180./M_PI) * atan2(X[1],X[0]); - if(out[YAW] != 0) - { - angle = -DEG2RAD(out[YAW]); - c = cos(angle); - s = sin(angle); - xtemp = c*X[0] - s*X[1]; - ytemp = c*X[1] + s*X[0]; - X[0] = xtemp; X[1] = ytemp; - xtemp = c*Y[0] - s*Y[1]; - ytemp = c*Y[1] + s*Y[0]; - Y[0] = xtemp; Y[1] = ytemp; - } - - out[PITCH] = (180./M_PI) * atan2(X[2],X[0]); - if(out[PITCH] != 0) - { - angle = DEG2RAD(out[PITCH]); - c = cos(angle); - s = sin(angle); - ztemp = c*Y[2] - s*Y[0]; - xtemp = c*Y[0] + s*Y[2]; - Y[0] = xtemp; Y[2] = ztemp; - } - out[ROLL] = (180./M_PI) * atan2(Y[2],Y[1]); - -} - -void laser_sight_think(edict_t *laser) -{ - edict_t *player; - vec3_t end, forward, right, offset; - trace_t tr; - - - if(!laser->activator) - return; - player = laser->activator; - - AngleVectors (player->client->v_angle, forward, right, NULL); - VectorSet(offset, 16, 8, player->viewheight-8); - - if (player->client->pers.hand == LEFT_HANDED) - offset[1] *= -1; - else if (player->client->pers.hand == CENTER_HANDED) - offset[1] = 0; - G_ProjectSource (player->s.origin, offset, forward, right, laser->s.origin); - VectorMA (laser->s.origin, 2048, forward, end); - tr = gi.trace (laser->s.origin, laser->mins, laser->maxs, end, player, MASK_SHOT); - VectorCopy (tr.endpos, laser->s.origin); - gi.linkentity(laser); - laser->nextthink = level.time + FRAMETIME; -} - -void SaveEntProps(edict_t *e, file_t *f) -{ - gi.Fs.Printf(f, - "================================\n" - "entity_state_t\n" - " number = %d\n" - " origin = %s\n" - " angles = %s\n" - " old_origin = %s\n" - " modelindex = %d\n" - " weaponmodel = %d\n" - " body = %d\n" - " sequence = %d\n" - " frame = %d\n" - " skin = %d\n" - " effects = 0x%08x\n" - " solid = 0x%08x\n" - " sound = %d\n" - " event = %d\n", - e->s.number,vtos(e->s.origin),vtos(e->s.angles), - vtos(e->s.old_origin),e->s.modelindex,e->s.weaponmodel, - e->s.body,e->s.sequence,e->s.frame, - e->s.skin,e->s.effects,e->s.solid,e->s.sound, - e->s.event); - gi.Fs.Printf(f,"inuse = %d\n" - "linkcount = %d\n" - "svflags = 0x%08x\n" - "mins = %s\n" - "maxs = %s\n" - "absmin = %s\n" - "absmax = %s\n" - "size = %s\n" - "solid = 0x%08x\n" - "clipmask = 0x%08x\n", - e->inuse,e->linkcount,e->svflags,vtos(e->mins), - vtos(e->maxs),vtos(e->absmin),vtos(e->absmax), - vtos(e->size),e->solid,e->clipmask); - gi.Fs.Printf(f,"movetype = 0x%08x\n" - "flags = 0x%08x\n" - "freetime = %g\n" - "message = %s\n" - "key_message = %s\n" - "classname = %s\n" - "spawnflags = 0x%08x\n" - "timestamp = %g\n" - "angle = %g\n" - "target = %s\n" - "targetname = %s\n" - "killtarget = %s\n" - "team = %s\n" - "pathtarget = %s\n" - "deathtarget = %s\n" - "combattarget= %s\n" - "dmgteam = %s\n", - e->movetype,e->flags,e->freetime,e->message,e->key_message, - e->classname,e->spawnflags,e->timestamp,e->angle,e->target, - e->targetname,e->killtarget,e->team,e->pathtarget,e->deathtarget, - e->combattarget,e->dmgteam); - gi.Fs.Printf(f,"speed = %g\n" - "accel = %g\n" - "decel = %g\n" - "movedir = %s\n" - "pos1 = %s\n" - "pos2 = %s\n" - "velocity = %s\n" - "avelocity = %s\n" - "mass = %d\n" - "air_finished= %g\n" - "gravity = %g\n" - "yaw_speed = %g\n" - "ideal_yaw = %g\n" - "pitch_speed = %g\n" - "ideal_pitch = %g\n" - "ideal_roll = %g\n" - "roll = %g\n" - "groundentity= %s\n", - e->speed,e->accel,e->decel,vtos(e->movedir),vtos(e->pos1), - vtos(e->pos2),vtos(e->velocity),vtos(e->avelocity), - e->mass,e->air_finished,e->gravity,e->yaw_speed,e->ideal_yaw, - e->pitch_speed,e->ideal_pitch,e->ideal_roll,e->roll, - (e->groundentity ? e->groundentity->classname : "None") ); - gi.Fs.Printf(f,"touch_debounce_time = %g\n" - "pain_debounce_time = %g\n" - "damage_debounce_time = %g\n" - "gravity_debounce_time= %g\n" - "fly_debounce_time = %g\n" - "last_move_time = %g\n", - e->touch_debounce_time,e->pain_debounce_time, - e->damage_debounce_time,e->gravity_debounce_time, - e->fly_sound_debounce_time,e->last_move_time); - gi.Fs.Printf(f,"health = %d\n" - "max_health = %d\n" - "gib_health = %d\n" - "deadflag = %d\n" - "show_hostile= %d\n" - "health2 = %d\n" - "mass2 = %d\n" - "powerarmor_time=%g\n", - e->health,e->max_health,e->gib_health,e->deadflag,e->show_hostile, - e->health2,e->mass2,e->powerarmor_time); - gi.Fs.Printf(f,"viewheight = %d\n" - "takedamage = %d\n" - "dmg = %d\n" - "radius_dmg = %d\n" - "dmg_radius = %g\n" - "sounds = %d\n" - "count = %d\n", - e->viewheight,e->takedamage,e->dmg,e->radius_dmg,e->dmg_radius, - e->sounds,e->count); - gi.Fs.Printf(f,"noise_index = %d\n" - "noise_index2= %d\n" - "volume = %d\n" - "attenuation = %g\n" - "wait = %g\n" - "delay = %g\n" - "random = %g\n" - "starttime = %g\n" - "endtime = %g\n" - "teleport_time=%g\n" - "watertype = %d\n" - "waterlevel = %d\n" - "move_origin = %s\n" - "move_angles = %s\n", - e->noise_index,e->noise_index2,e->volume,e->attenuation, - e->wait,e->delay,e->random,e->starttime,e->endtime,e->teleport_time, - e->watertype,e->waterlevel,vtos(e->move_origin),vtos(e->move_angles)); - gi.Fs.Printf(f,"light_level = %d\n" - "style = %d\n", - e->light_level,e->style); - gi.Fs.Printf(f,"enemy = %s\n",(e->enemy ? e->enemy->classname : "NULL")); - gi.Fs.Printf(f,"enemy->inuse? %s\n",(e->enemy && e->enemy->inuse ? "Y" : "N")); - gi.Fs.Printf(f,"moveinfo_t\n" - " start_origin = %s\n" - " start_angles = %s\n" - " end_origin = %s\n" - " end_angles = %s\n" - " sound_start = %d\n" - " sound_middle = %d\n" - " sound_end = %d\n" - " accel = %g\n" - " speed = %g\n" - " decel = %g\n" - " distance = %g\n" - " wait = %g\n" - " state = %d\n" - " dir = %s\n" - " current_speed = %g\n" - " move_speed = %g\n" - " next_speed = %g\n" - " remaining_dist = %g\n" - " decel_distance = %g\n", - vtos(e->moveinfo.start_origin), - vtos(e->moveinfo.start_angles), - vtos(e->moveinfo.end_origin), - vtos(e->moveinfo.end_angles), - e->moveinfo.sound_start,e->moveinfo.sound_middle, - e->moveinfo.sound_end,e->moveinfo.accel,e->moveinfo.speed, - e->moveinfo.decel,e->moveinfo.distance,e->moveinfo.wait, - e->moveinfo.state,vtos(e->moveinfo.dir),e->moveinfo.current_speed, - e->moveinfo.move_speed,e->moveinfo.next_speed, - e->moveinfo.remaining_distance,e->moveinfo.decel_distance); - gi.Fs.Printf(f,"monsterinfo\n" - " aiflags = 0x%08x\n" - " nextframe = %d\n" - " scale = %g\n" - " pausetime = %g\n" - " attack_finished = %g\n" - " saved_goal = %s\n" - " search_time = %g\n" - " trail_time = %g\n" - " last_sighting = %s\n" - " attack_state = %d\n" - " lefty = %d\n" - " idle_time = %g\n" - " linkcount = %d\n" - " power_armor_type = %d\n" - " power_armor_power= %d\n" - " min_range = %g\n", - e->monsterinfo.aiflags,e->monsterinfo.nextframe, - e->monsterinfo.scale,e->monsterinfo.pausetime, - e->monsterinfo.attack_finished,vtos(e->monsterinfo.saved_goal), - e->monsterinfo.search_time,e->monsterinfo.trail_time, - vtos(e->monsterinfo.last_sighting),e->monsterinfo.attack_state, - e->monsterinfo.lefty,e->monsterinfo.idle_time, - e->monsterinfo.linkcount,e->monsterinfo.power_armor_type, - e->monsterinfo.power_armor_power,e->monsterinfo.min_range); -} - -char *ClientTeam (edict_t *ent) -{ - char *p; - static char value[512]; - - value[0] = 0; - - if (!ent->client) - return value; - - strcpy(value, Info_ValueForKey (ent->client->pers.userinfo, "skin")); - p = strchr(value, '/'); - if (!p) - return value; - - if ((int)(dmflags->value) & DF_MODELTEAMS) - { - *p = 0; - return value; - } - - // if ((int)(dmflags->value) & DF_SKINTEAMS) - return ++p; -} - -bool OnSameTeam (edict_t *ent1, edict_t *ent2) -{ - char ent1Team [512]; - char ent2Team [512]; - - if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) - return false; - - strcpy (ent1Team, ClientTeam (ent1)); - strcpy (ent2Team, ClientTeam (ent2)); - - if (strcmp(ent1Team, ent2Team) == 0) - return true; - return false; -} - - -void SelectNextItem (edict_t *ent, int itflags) -{ - gclient_t *cl; - int i, index; - gitem_t *it; - - cl = ent->client; - - if (cl->menu) - { - PMenu_Next(ent); - return; - } - - // scan for the next valid one - for (i=1 ; i<=MAX_ITEMS ; i++) - { - index = (cl->pers.selected_item + i)%MAX_ITEMS; - if (!cl->pers.inventory[index]) - continue; - it = &itemlist[index]; - if (!it->use) - continue; - if (!(it->flags & itflags)) - continue; - - cl->pers.selected_item = index; - return; - } - - cl->pers.selected_item = -1; -} - -void SelectPrevItem (edict_t *ent, int itflags) -{ - gclient_t *cl; - int i, index; - gitem_t *it; - - cl = ent->client; - - if (cl->menu) - { - PMenu_Prev(ent); - return; - } - - // scan for the next valid one - for (i=1 ; i<=MAX_ITEMS ; i++) - { - index = (cl->pers.selected_item + MAX_ITEMS - i)%MAX_ITEMS; - if (!cl->pers.inventory[index]) - continue; - it = &itemlist[index]; - if (!it->use) - continue; - if (!(it->flags & itflags)) - continue; - - cl->pers.selected_item = index; - return; - } - - cl->pers.selected_item = -1; -} - -void ValidateSelectedItem (edict_t *ent) -{ - gclient_t *cl; - - cl = ent->client; - - if (cl->pers.inventory[cl->pers.selected_item]) - return; // valid - - SelectNextItem (ent, -1); -} - - -//================================================================================= - -/* -================== -Cmd_Give_f - -Give items to a client -================== -*/ -void Cmd_Give_f (edict_t *ent) -{ - char *name; - gitem_t *it; - int index; - int i; - bool give_all; - edict_t *it_ent; - - if (deathmatch->value && !sv_cheats->value) - { - gi.cprintf (ent, PRINT_HIGH, "You must run the server with '+set cheats 1' to enable this command.\n"); - return; - } - - name = gi.args(); - - if (strcasecmp(name, "all") == 0) - give_all = true; - else - give_all = false; - - if (give_all || strcasecmp(gi.argv(1), "health") == 0) - { - if (gi.argc() == 3) - ent->health = atoi(gi.argv(2)); - else - ent->health = ent->max_health; - if (!give_all) - return; - } - - if (give_all || strcasecmp(name, "weapons") == 0) - { - for (i=0 ; ipickup) - continue; - if (!(it->flags & IT_WEAPON)) - continue; - ent->client->pers.inventory[i] += 1; - } - if (!give_all) - return; - } - - if (give_all || strcasecmp(name, "ammo") == 0) - { - for (i=0 ; ipickup) - continue; - if (!(it->flags & IT_AMMO)) - continue; - if (it->classname && !strcasecmp(it->classname,"ammo_fuel") && !developer->value) - continue; - if (it->classname && !strcasecmp(it->classname,"ammo_homing_missiles") && !developer->value) - continue; - Add_Ammo (ent, it, 1000); - } - if (!give_all) - return; - } - - if (give_all || strcasecmp(name, "armor") == 0) - { - gitem_armor_t *info; - - it = FindItem("Jacket Armor"); - ent->client->pers.inventory[ITEM_INDEX(it)] = 0; - - it = FindItem("Combat Armor"); - ent->client->pers.inventory[ITEM_INDEX(it)] = 0; - - it = FindItem("Body Armor"); - info = (gitem_armor_t *)it->info; - ent->client->pers.inventory[ITEM_INDEX(it)] = info->max_count; - - if (!give_all) - return; - } - - if (give_all || strcasecmp(name, "Power Shield") == 0) - { - it = FindItem("Power Shield"); - it_ent = G_Spawn(); - it_ent->classname = it->classname; - SpawnItem (it_ent, it); - Touch_Item (it_ent, ent, NULL, NULL); - if (it_ent->inuse) - G_FreeEdict(it_ent); - - if (!give_all) - return; - } - - if (give_all) - { - for (i=0 ; ipickup) - continue; - if (it->flags & (IT_ARMOR|IT_WEAPON|IT_AMMO)) - continue; - if (it->classname && !strcasecmp(it->classname,"item_jetpack") && !developer->value) - continue; - if (it->classname && !strcasecmp(it->classname,"item_flashlight") && !developer->value) - continue; - if (it->classname && !strcasecmp(it->classname,"item_freeze") && !developer->value) - continue; - ent->client->pers.inventory[i] = 1; - } - return; - } - - it = FindItem (name); - if (!it) - { - name = gi.argv(1); - it = FindItem (name); - if (!it) - { - gi.cprintf (ent, PRINT_HIGH, "unknown item\n"); - return; - } - } - - if (!it->pickup) - { - gi.cprintf (ent, PRINT_HIGH, "non-pickup item\n"); - return; - } - - index = ITEM_INDEX(it); - - if (it->flags & IT_AMMO) - { - // Lazarus: Bleah - special case for "homing missiles" - if (it->tag == AMMO_HOMING_MISSILES) - { - if (gi.argc() == 4) - ent->client->pers.inventory[index] = atoi(gi.argv(3)); - else - ent->client->pers.inventory[index] += it->quantity; - } - else - { - if (gi.argc() == 3) - ent->client->pers.inventory[index] = atoi(gi.argv(2)); - else - ent->client->pers.inventory[index] += it->quantity; - } - } - else - { - it_ent = G_Spawn(); - it_ent->classname = it->classname; - SpawnItem (it_ent, it); - Touch_Item (it_ent, ent, NULL, NULL); - if (it_ent->inuse) - G_FreeEdict(it_ent); - } -} - - -/* -================== -Cmd_God_f - -Sets client to godmode - -argv(0) god -================== -*/ -void Cmd_God_f (edict_t *ent) -{ - char *msg; - - if (deathmatch->value && !sv_cheats->value) - { - gi.cprintf (ent, PRINT_HIGH, "You must run the server with '+set cheats 1' to enable this command.\n"); - return; - } - - ent->flags ^= FL_GODMODE; - if (!(ent->flags & FL_GODMODE) ) - msg = "godmode OFF\n"; - else - msg = "godmode ON\n"; - - gi.cprintf (ent, PRINT_HIGH, msg); -} - - -/* -================== -Cmd_Notarget_f - -Sets client to notarget - -argv(0) notarget -================== -*/ -void Cmd_Notarget_f (edict_t *ent) -{ - char *msg; - - if (deathmatch->value && !sv_cheats->value) - { - gi.cprintf (ent, PRINT_HIGH, "You must run the server with '+set cheats 1' to enable this command.\n"); - return; - } - - ent->flags ^= FL_NOTARGET; - if (!(ent->flags & FL_NOTARGET) ) - msg = "notarget OFF\n"; - else - msg = "notarget ON\n"; - - gi.cprintf (ent, PRINT_HIGH, msg); -} - - -/* -================== -Cmd_Noclip_f - -argv(0) noclip -================== -*/ -void Cmd_Noclip_f (edict_t *ent) -{ - char *msg; - - if (deathmatch->value && !sv_cheats->value) - { - gi.cprintf (ent, PRINT_HIGH, "You must run the server with '+set cheats 1' to enable this command.\n"); - return; - } - - if (ent->movetype == MOVETYPE_NOCLIP) - { - ent->movetype = MOVETYPE_WALK; - ent->solid = SOLID_BBOX; // Lazarus - msg = "noclip OFF\n"; - } - else - { - ent->movetype = MOVETYPE_NOCLIP; - ent->solid = SOLID_NOT; // Lazarus - msg = "noclip ON\n"; - } - - gi.cprintf (ent, PRINT_HIGH, msg); -} - - -/* -================== -Cmd_Use_f - -Use an inventory item -================== -*/ -void Cmd_Use_f (edict_t *ent) -{ - int index; - gitem_t *it; - char *s; - - s = gi.args(); - it = FindItem (s); - if (!it) - { - gi.cprintf (ent, PRINT_HIGH, "unknown item: %s\n", s); - return; - } - if (!it->use) - { - gi.cprintf (ent, PRINT_HIGH, "Item is not usable.\n"); - return; - } - index = ITEM_INDEX(it); - if (!stricmp(s,"stasis generator")) - { - // Special case - turn freeze off if already on - if(level.freeze) - { - level.freeze = false; - level.freezeframes = 0; - return; - } - } - if (!ent->client->pers.inventory[index]) - { - gi.cprintf (ent, PRINT_HIGH, "Out of item: %s\n", s); - return; - } - - it->use (ent, it); -} - - -/* -================== -Cmd_Drop_f - -Drop an inventory item -================== -*/ -void Cmd_Drop_f (edict_t *ent) -{ - int index; - gitem_t *it; - char *s; - - s = gi.args(); - it = FindItem (s); - if (!it) - { - gi.cprintf (ent, PRINT_HIGH, "unknown item: %s\n", s); - return; - } - if (!it->drop) - { - gi.cprintf (ent, PRINT_HIGH, "Item is not dropable.\n"); - return; - } - index = ITEM_INDEX(it); - if (!ent->client->pers.inventory[index]) - { - gi.cprintf (ent, PRINT_HIGH, "Out of item: %s\n", s); - return; - } - - it->drop (ent, it); -} - - -/* -================= -Cmd_Inven_f -================= -*/ -void Cmd_Inven_f (edict_t *ent) -{ - int i; - gclient_t *cl; - - cl = ent->client; - - cl->showscores = false; - cl->showhelp = false; - - if (cl->menu) - { - PMenu_Close(ent); - return; - } - - if (cl->showinventory) - { - cl->showinventory = false; - return; - } - - cl->showinventory = true; - - MESSAGE_BEGIN (svc_inventory); - for (i = 0; i < MAX_ITEMS; i++) - { - // Don't show "No Weapon" or "Homing Missile Launcher" in inventory - if((i == noweapon_index) || (i == hml_index)) - WRITE_SHORT (0); - else WRITE_SHORT (cl->pers.inventory[i]); - } - MESSAGE_SEND(MSG_ONE_R, NULL, ent ); -} - -/* -================= -Cmd_InvUse_f -================= -*/ -void Cmd_InvUse_f (edict_t *ent) -{ - gitem_t *it; - - if (ent->client->menu) { - PMenu_Select(ent); - return; - } - - ValidateSelectedItem (ent); - - if (ent->client->pers.selected_item == -1) - { - gi.cprintf (ent, PRINT_HIGH, "No item to use.\n"); - return; - } - - it = &itemlist[ent->client->pers.selected_item]; - if (!it->use) - { - gi.cprintf (ent, PRINT_HIGH, "Item is not usable.\n"); - return; - } - it->use (ent, it); -} - -/* -================= -Cmd_WeapPrev_f -================= -*/ -void Cmd_WeapPrev_f (edict_t *ent) -{ - gclient_t *cl; - int i, index; - gitem_t *it; - int selected_weapon; - - cl = ent->client; - - if (!cl->pers.weapon) - return; - - selected_weapon = ITEM_INDEX(cl->pers.weapon); - - // scan for the next valid one - for (i=1 ; i<=MAX_ITEMS ; i++) - { - index = (selected_weapon + i)%MAX_ITEMS; - if (!cl->pers.inventory[index]) - continue; - it = &itemlist[index]; - if (!it->use) - continue; - if (! (it->flags & IT_WEAPON) ) - continue; - it->use (ent, it); - if (cl->pers.weapon == it) - return; // successful - } -} - -/* -================= -Cmd_WeapNext_f -================= -*/ -void Cmd_WeapNext_f (edict_t *ent) -{ - gclient_t *cl; - int i, index; - gitem_t *it; - int selected_weapon; - - cl = ent->client; - - if (!cl->pers.weapon) - return; - - selected_weapon = ITEM_INDEX(cl->pers.weapon); - - // scan for the next valid one - for (i=1 ; i<=MAX_ITEMS ; i++) - { - index = (selected_weapon + MAX_ITEMS - i)%MAX_ITEMS; - if (!cl->pers.inventory[index]) - continue; - it = &itemlist[index]; - if (!it->use) - continue; - if (! (it->flags & IT_WEAPON) ) - continue; - it->use (ent, it); - if (cl->pers.weapon == it) - return; // successful - } -} - -/* -================= -Cmd_WeapLast_f -================= -*/ -void Cmd_WeapLast_f (edict_t *ent) -{ - gclient_t *cl; - int index; - gitem_t *it; - - cl = ent->client; - - if (!cl->pers.weapon || !cl->pers.lastweapon) - return; - - index = ITEM_INDEX(cl->pers.lastweapon); - if (!cl->pers.inventory[index]) - return; - it = &itemlist[index]; - if (!it->use) - return; - if (! (it->flags & IT_WEAPON) ) - return; - it->use (ent, it); -} - -/* -================= -Cmd_InvDrop_f -================= -*/ -void Cmd_InvDrop_f (edict_t *ent) -{ - gitem_t *it; - - ValidateSelectedItem (ent); - - if (ent->client->pers.selected_item == -1) - { - gi.cprintf (ent, PRINT_HIGH, "No item to drop.\n"); - return; - } - - it = &itemlist[ent->client->pers.selected_item]; - if (!it->drop) - { - gi.cprintf (ent, PRINT_HIGH, "Item is not dropable.\n"); - return; - } - it->drop (ent, it); -} - -/* -================= -Cmd_Kill_f -================= -*/ -void Cmd_Kill_f (edict_t *ent) -{ - if((level.time - ent->client->respawn_time) < 5) - return; - ent->flags &= ~FL_GODMODE; - ent->health = 0; - meansOfDeath = MOD_SUICIDE; - player_die (ent, ent, ent, 100000, vec3_origin); -} - -/* -================= -Cmd_PutAway_f -================= -*/ -void Cmd_PutAway_f (edict_t *ent) -{ - ent->client->showscores = false; - ent->client->showhelp = false; - ent->client->showinventory = false; - - if (ent->client->menu) - PMenu_Close(ent); -} - - -int PlayerSort (void const *a, void const *b) -{ - int anum, bnum; - - anum = *(int *)a; - bnum = *(int *)b; - - anum = game.clients[anum].ps.stats[STAT_FRAGS]; - bnum = game.clients[bnum].ps.stats[STAT_FRAGS]; - - if (anum < bnum) - return -1; - if (anum > bnum) - return 1; - return 0; -} - -/* -================= -Cmd_Players_f -================= -*/ -void Cmd_Players_f (edict_t *ent) -{ - int i; - int count; - char small[64]; - char large[1280]; - int index[256]; - - count = 0; - for (i = 0 ; i < maxclients->value ; i++) - if (game.clients[i].pers.connected) - { - index[count] = i; - count++; - } - - // sort by frags - qsort (index, count, sizeof(index[0]), PlayerSort); - - // print information - large[0] = 0; - - for (i = 0 ; i < count ; i++) - { - sprintf (small, "%3i %s\n", - game.clients[index[i]].ps.stats[STAT_FRAGS], - game.clients[index[i]].pers.netname); - if (strlen (small) + strlen(large) > sizeof(large) - 100 ) - { // can't print all of them in one packet - strcat (large, "...\n"); - break; - } - strcat (large, small); - } - - gi.cprintf (ent, PRINT_HIGH, "%s\n%i players\n", large, count); -} - -/* -================= -Cmd_Wave_f -================= -*/ -void Cmd_Wave_f (edict_t *ent) -{ - int i; - - i = atoi (gi.argv(1)); - - // can't wave when ducked - if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) - return; - - if (ent->client->anim_priority > ANIM_WAVE) - return; - - ent->client->anim_priority = ANIM_WAVE; - - switch (i) - { - case 0: - gi.cprintf (ent, PRINT_HIGH, "flipoff\n"); - ent->s.frame = FRAME_flip01-1; - ent->client->anim_end = FRAME_flip12; - break; - case 1: - gi.cprintf (ent, PRINT_HIGH, "salute\n"); - ent->s.frame = FRAME_salute01-1; - ent->client->anim_end = FRAME_salute11; - break; - case 2: - gi.cprintf (ent, PRINT_HIGH, "taunt\n"); - ent->s.frame = FRAME_taunt01-1; - ent->client->anim_end = FRAME_taunt17; - break; - case 3: - gi.cprintf (ent, PRINT_HIGH, "wave\n"); - ent->s.frame = FRAME_wave01-1; - ent->client->anim_end = FRAME_wave11; - break; - case 4: - default: - gi.cprintf (ent, PRINT_HIGH, "point\n"); - ent->s.frame = FRAME_point01-1; - ent->client->anim_end = FRAME_point12; - break; - } -} - -/* -================== -Cmd_Say_f -================== -*/ -void Cmd_Say_f (edict_t *ent, bool team, bool arg0) -{ - int i, j; - edict_t *other; - char *p; - char text[2048]; - gclient_t *cl; - - if (gi.argc () < 2 && !arg0) - return; - - if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) - team = false; - - if (team) - sprintf (text, "(%s): ", ent->client->pers.netname); - else - sprintf (text, "%s: ", ent->client->pers.netname); - - if (arg0) - { - strcat (text, gi.argv(0)); - strcat (text, " "); - strcat (text, gi.args()); - } - else - { - p = gi.args(); - - if (*p == '"') - { - p++; - p[strlen(p)-1] = 0; - } - strcat(text, p); - } - - // don't let text be too long for malicious reasons - if (strlen(text) > 150) - text[150] = 0; - - strcat(text, "\n"); - - if (flood_msgs->value) { - cl = ent->client; - - if (level.time < cl->flood_locktill) { - gi.cprintf(ent, PRINT_HIGH, "You can't talk for %d more seconds\n", - (int)(cl->flood_locktill - level.time)); - return; - } - i = cl->flood_whenhead - flood_msgs->value + 1; - if (i < 0) - i = (sizeof(cl->flood_when)/sizeof(cl->flood_when[0])) + i; - if (cl->flood_when[i] && - level.time - cl->flood_when[i] < flood_persecond->value) { - cl->flood_locktill = level.time + flood_waitdelay->value; - gi.cprintf(ent, PRINT_CHAT, "Flood protection: You can't talk for %d seconds.\n", - (int)flood_waitdelay->value); - return; - } - cl->flood_whenhead = (cl->flood_whenhead + 1) % - (sizeof(cl->flood_when)/sizeof(cl->flood_when[0])); - cl->flood_when[cl->flood_whenhead] = level.time; - } - - if (dedicated->value) - gi.cprintf(NULL, PRINT_CHAT, "%s", text); - - for (j = 1; j <= game.maxclients; j++) - { - other = &g_edicts[j]; - if (!other->inuse) - continue; - if (!other->client) - continue; - if (team) - { - if (!OnSameTeam(ent, other)) - continue; - } - gi.cprintf(other, PRINT_CHAT, "%s", text); - } -} - -void Cmd_PlayerList_f(edict_t *ent) -{ - int i; - char st[80]; - char text[1400]; - edict_t *e2; - - // connect time, ping, score, name - *text = 0; - for (i = 0, e2 = g_edicts + 1; i < maxclients->value; i++, e2++) { - if (!e2->inuse) - continue; - - sprintf(st, "%02d:%02d %4d %3d %s%s\n", - (level.framenum - e2->client->resp.enterframe) / 600, - ((level.framenum - e2->client->resp.enterframe) % 600)/10, - e2->client->ping, - e2->client->resp.score, - e2->client->pers.netname, - e2->client->resp.spectator ? " (spectator)" : ""); - if (strlen(text) + strlen(st) > sizeof(text) - 50) - { - sprintf(text+strlen(text), "And more...\n"); - gi.cprintf(ent, PRINT_HIGH, "%s", text); - return; - } - strcat(text, st); - } - gi.cprintf(ent, PRINT_HIGH, "%s", text); -} - -void DrawBBox(edict_t *ent) -{ - vec3_t p1, p2; - vec3_t origin; - - VectorCopy(ent->s.origin,origin); - VectorSet(p1,origin[0]+ent->mins[0],origin[1]+ent->mins[1],origin[2]+ent->mins[2]); - VectorSet(p2,origin[0]+ent->mins[0],origin[1]+ent->mins[1],origin[2]+ent->maxs[2]); - - MESSAGE_BEGIN(svc_temp_entity); - WRITE_BYTE (TE_DEBUGTRAIL); - WRITE_COORD (p1); - WRITE_COORD (p2); - MESSAGE_SEND (MSG_ALL, p1, NULL); - - VectorSet(p2,origin[0]+ent->mins[0],origin[1]+ent->maxs[1],origin[2]+ent->mins[2]); - - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_DEBUGTRAIL); - WRITE_COORD (p1); - WRITE_COORD (p2); - MESSAGE_SEND (MSG_ALL, p1, NULL); - - VectorSet(p2,origin[0]+ent->maxs[0],origin[1]+ent->mins[1],origin[2]+ent->mins[2]); - - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_DEBUGTRAIL); - WRITE_COORD (p1); - WRITE_COORD (p2); - MESSAGE_SEND(MSG_ALL, p1, NULL); - - VectorSet(p1,origin[0]+ent->maxs[0],origin[1]+ent->maxs[1],origin[2]+ent->mins[2]); - VectorSet(p2,origin[0]+ent->maxs[0],origin[1]+ent->maxs[1],origin[2]+ent->maxs[2]); - - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_DEBUGTRAIL); - WRITE_COORD (p1); - WRITE_COORD (p2); - MESSAGE_SEND (MSG_ALL, p1, NULL); - - VectorSet(p2,origin[0]+ent->maxs[0],origin[1]+ent->mins[1],origin[2]+ent->mins[2]); - - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_DEBUGTRAIL); - WRITE_COORD (p1); - WRITE_COORD (p2); - MESSAGE_SEND (MSG_ALL, p1, NULL); - - VectorSet(p2,origin[0]+ent->mins[0],origin[1]+ent->maxs[1],origin[2]+ent->mins[2]); - - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_DEBUGTRAIL); - WRITE_COORD (p1); - WRITE_COORD (p2); - MESSAGE_SEND (MSG_ALL, p1, NULL); - - VectorSet(p1,origin[0]+ent->maxs[0],origin[1]+ent->mins[1],origin[2]+ent->maxs[2]); - VectorSet(p2,origin[0]+ent->maxs[0],origin[1]+ent->mins[1],origin[2]+ent->mins[2]); - - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_DEBUGTRAIL); - WRITE_COORD (p1); - WRITE_COORD (p2); - MESSAGE_SEND (MSG_ALL, p1, NULL); - - VectorSet(p2,origin[0]+ent->maxs[0],origin[1]+ent->maxs[1],origin[2]+ent->maxs[2]); - - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_DEBUGTRAIL); - WRITE_COORD (p1); - WRITE_COORD (p2); - MESSAGE_SEND(MSG_ALL, p1, NULL); - - VectorSet(p2,origin[0]+ent->mins[0],origin[1]+ent->mins[1],origin[2]+ent->maxs[2]); - - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_DEBUGTRAIL); - WRITE_COORD (p1); - WRITE_COORD (p2); - MESSAGE_SEND (MSG_ALL, p1, NULL); - - VectorSet(p1,origin[0]+ent->mins[0],origin[1]+ent->maxs[1],origin[2]+ent->maxs[2]); - VectorSet(p2,origin[0]+ent->mins[0],origin[1]+ent->maxs[1],origin[2]+ent->mins[2]); - - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_DEBUGTRAIL); - WRITE_COORD (p1); - WRITE_COORD (p2); - MESSAGE_SEND (MSG_ALL, p1, NULL); - - VectorSet(p2,origin[0]+ent->mins[0],origin[1]+ent->mins[1],origin[2]+ent->maxs[2]); - - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_DEBUGTRAIL); - WRITE_COORD (p1); - WRITE_COORD (p2); - MESSAGE_SEND (MSG_ALL, p1, NULL); - - VectorSet(p2,origin[0]+ent->maxs[0],origin[1]+ent->maxs[1],origin[2]+ent->maxs[2]); - - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_DEBUGTRAIL); - WRITE_COORD (p1); - WRITE_COORD (p2); - MESSAGE_SEND (MSG_ALL, p1, NULL); -} -void Cmd_Bbox_f (edict_t *ent) -{ - edict_t *viewing; - - viewing = LookingAt(ent, 0, NULL, NULL); - if(!viewing) return; - DrawBBox(viewing); -} - -void SetSensitivities(edict_t *ent,bool reset) -{ - char string[512]; - - if(deathmatch->value || coop->value) return; - if(!ent->inuse) return; - if(!ent->client) return; - if(reset) { - m_pitch->value = lazarus_pitch->value; - m_yaw->value = lazarus_yaw->value; - joy_pitchsensitivity->value = lazarus_joyp->value; - joy_yawsensitivity->value = lazarus_joyy->value; - if(cl_gun->value != lazarus_cl_gun->value) { - sprintf(string,"cl_gun %0d\n",(int)(lazarus_cl_gun->value)); - stuffcmd(ent,string); - } - if(crosshair->value != lazarus_crosshair->value) { - sprintf(string,"crosshair %0d\n",(int)(lazarus_crosshair->value)); - stuffcmd(ent,string); - } - ent->client->pers.hand = hand->value; - } - else { - float ratio; - if(!ent->client->sensitivities_init) { - ent->client->m_pitch = m_pitch->value; - ent->client->m_yaw = m_yaw->value; - ent->client->joy_pitchsensitivity = joy_pitchsensitivity->value; - ent->client->joy_yawsensitivity = joy_yawsensitivity->value; - ent->client->sensitivities_init = true; - } - if(ent->client->ps.fov >= ent->client->original_fov) { - ratio = 1.; - if(cl_gun->value != lazarus_cl_gun->value) { - sprintf(string,"cl_gun %0d\n",(int)(lazarus_cl_gun->value)); - stuffcmd(ent,string); - } - if(crosshair->value != lazarus_crosshair->value) { - sprintf(string,"crosshair %0d\n",(int)(lazarus_crosshair->value)); - stuffcmd(ent,string); - } - ent->client->pers.hand = hand->value; - } else { - ratio = ent->client->ps.fov / ent->client->original_fov; - } - m_pitch->value = ent->client->m_pitch * ratio; - m_yaw->value = ent->client->m_yaw * ratio; - joy_pitchsensitivity->value = ent->client->joy_pitchsensitivity * ratio; - joy_yawsensitivity->value = ent->client->joy_yawsensitivity * ratio; - } - sprintf(string,"m_pitch %g;m_yaw %g;joy_pitchsensitivity %g;joy_yawsensitivity %g\n", - m_pitch->value,m_yaw->value,joy_pitchsensitivity->value,joy_yawsensitivity->value); - stuffcmd(ent,string); -} - -/* -===================== -Cmd_attack2_f -Alternate firing mode -===================== -*/ -void Cmd_attack2_f(edict_t *ent, bool bOn) -{ - if(!ent->client) return; - if(ent->health <= 0) return; - - if(bOn) - { - ent->client->pers.fire_mode=1; - ent->client->nNewLatch |= BUTTON_ATTACK2; - } - else - { - ent->client->pers.fire_mode=0; - ent->client->nNewLatch &= ~BUTTON_ATTACK2; - } -} - -void forcewall_think(edict_t *self) -{ - self->nextthink = level.time + FRAMETIME; -} - -void SpawnForcewall(edict_t *player) -{ - edict_t *wall; - vec3_t forward, point, start; - trace_t tr; - - wall = G_Spawn(); - VectorCopy(player->s.origin,start); - start[2] += player->viewheight; - AngleVectors(player->client->v_angle,forward,NULL,NULL); - VectorMA(start,8192,forward,point); - tr = gi.trace(start,NULL,NULL,point,player,MASK_SOLID); - VectorCopy(tr.endpos,wall->s.origin); - - if(fabs(forward[0]) > fabs(forward[1])) - { - wall->pos1[0] = wall->pos2[0] = wall->s.origin[0]; - wall->mins[0] = -1; - wall->maxs[0] = 1; - - VectorCopy(wall->s.origin,point); - point[1] -= 8192; - tr = gi.trace(wall->s.origin,NULL,NULL,point,NULL,MASK_SOLID); - wall->pos1[1] = tr.endpos[1]; - wall->mins[1] = wall->pos1[1] - wall->s.origin[1]; - - point[1] = wall->s.origin[1] + 8192; - tr = gi.trace(wall->s.origin,NULL,NULL,point,NULL,MASK_SOLID); - wall->pos2[1] = tr.endpos[1]; - wall->maxs[1] = wall->pos2[1] - wall->s.origin[1]; - } - else - { - VectorCopy(wall->s.origin,point); - point[0] -= 8192; - tr = gi.trace(wall->s.origin,NULL,NULL,point,NULL,MASK_SOLID); - wall->pos1[0] = tr.endpos[0]; - wall->mins[0] = wall->pos1[0] - wall->s.origin[0]; - - point[0] = wall->s.origin[0] + 8192; - tr = gi.trace(wall->s.origin,NULL,NULL,point,NULL,MASK_SOLID); - wall->pos2[0] = tr.endpos[0]; - wall->maxs[0] = wall->pos2[0] - wall->s.origin[0]; - - wall->pos1[1] = wall->pos2[1] = wall->s.origin[1]; - wall->mins[1] = -1; - wall->maxs[1] = 1; - } - wall->mins[2] = 0; - - VectorCopy(wall->s.origin,point); - point[2] = wall->s.origin[2] + 8192; - tr = gi.trace(wall->s.origin,NULL,NULL,point,NULL,MASK_SOLID); - wall->maxs[2] = tr.endpos[2] - wall->s.origin[2]; - wall->pos1[2] = wall->pos2[2] = tr.endpos[2]; - - wall->style = 208; // Color from Q2 palette - wall->movetype = MOVETYPE_NONE; - wall->solid = SOLID_BBOX; - wall->clipmask = MASK_PLAYERSOLID | MASK_MONSTERSOLID; - wall->think = forcewall_think; - wall->nextthink = level.time + FRAMETIME; - wall->svflags = SVF_NOCLIENT; - wall->classname = "forcewall"; - wall->activator = player; - wall->owner = player; - gi.linkentity(wall); -} - -void ForcewallOff(edict_t *player) -{ - vec3_t forward, point, start; - trace_t tr; - - VectorCopy(player->s.origin,start); - start[2] += player->viewheight; - AngleVectors(player->client->v_angle,forward,NULL,NULL); - VectorMA(start,8192,forward,point); - tr = gi.trace(start,NULL,NULL,point,player,MASK_SHOT); - if(strcasecmp(tr.ent->classname,"forcewall")) - { - gi.cprintf(player,PRINT_HIGH,"Not a forcewall!\n"); - return; - } - if(tr.ent->activator != player) - { - gi.cprintf(player,PRINT_HIGH,"You don't own this forcewall, bub!\n"); - return; - } - G_FreeEdict(tr.ent); -} - -/* -================= -ClientCommand -================= -*/ -void ClientCommand (edict_t *ent) -{ - char *cmd; - char *parm; - - if (!ent->client) - return; // not fully in game yet - - cmd = gi.argv(0); - if(gi.argc() < 2) - parm = NULL; - else - parm = gi.argv(1); - - if (strcasecmp (cmd, "players") == 0) - { - Cmd_Players_f (ent); - return; - } - if (strcasecmp (cmd, "say") == 0) - { - Cmd_Say_f (ent, false, false); - return; - } - if (strcasecmp (cmd, "say_team") == 0) - { - Cmd_Say_f (ent, true, false); - return; - } - if (strcasecmp (cmd, "score") == 0) - { - Cmd_Score_f (ent); - return; - } - if (strcasecmp (cmd, "help") == 0) - { - Cmd_Help_f (ent); - return; - } - - if (level.intermissiontime) - return; - - if (strcasecmp (cmd, "use") == 0) - Cmd_Use_f (ent); - else if (strcasecmp (cmd, "drop") == 0) - Cmd_Drop_f (ent); - else if (strcasecmp (cmd, "give") == 0) - Cmd_Give_f (ent); - else if (strcasecmp (cmd, "god") == 0) - Cmd_God_f (ent); - else if (strcasecmp (cmd, "notarget") == 0) - Cmd_Notarget_f (ent); - else if (strcasecmp (cmd, "noclip") == 0) - Cmd_Noclip_f (ent); - else if (strcasecmp (cmd, "inven") == 0) - Cmd_Inven_f (ent); - else if (strcasecmp (cmd, "invnext") == 0) - SelectNextItem (ent, -1); - else if (strcasecmp (cmd, "invprev") == 0) - SelectPrevItem (ent, -1); - else if (strcasecmp (cmd, "invnextw") == 0) - SelectNextItem (ent, IT_WEAPON); - else if (strcasecmp (cmd, "invprevw") == 0) - SelectPrevItem (ent, IT_WEAPON); - else if (strcasecmp (cmd, "invnextp") == 0) - SelectNextItem (ent, IT_POWERUP); - else if (strcasecmp (cmd, "invprevp") == 0) - SelectPrevItem (ent, IT_POWERUP); - else if (strcasecmp (cmd, "invuse") == 0) - Cmd_InvUse_f (ent); - else if (strcasecmp (cmd, "invdrop") == 0) - Cmd_InvDrop_f (ent); - else if (strcasecmp (cmd, "weapprev") == 0) - Cmd_WeapPrev_f (ent); - else if (strcasecmp (cmd, "weapnext") == 0) - Cmd_WeapNext_f (ent); - else if (strcasecmp (cmd, "weaplast") == 0) - Cmd_WeapLast_f (ent); - else if (strcasecmp (cmd, "kill") == 0) - Cmd_Kill_f (ent); - else if (strcasecmp (cmd, "putaway") == 0) - Cmd_PutAway_f (ent); - else if (strcasecmp (cmd, "wave") == 0) - Cmd_Wave_f (ent); - else if (strcasecmp(cmd, "playerlist") == 0) - Cmd_PlayerList_f(ent); - -#ifdef FLASHLIGHT_MOD -#if FLASHLIGHT_USE==POWERUP_USE_ITEM - else if (strcasecmp (cmd, "flashlight") == 0) - Use_Flashlight_f (ent, (gitem_t *)NULL); -#endif -#endif - - // alternate attack mode - else if (!strcasecmp(cmd,"attack2_off")) - Cmd_attack2_f(ent,false); - else if (!strcasecmp(cmd,"attack2_on")) - Cmd_attack2_f(ent,true); - - // zoom - else if (!strcasecmp(cmd, "zoomin")) { - if(!deathmatch->value && !coop->value) { - if(ent->client->ps.fov > 5) { - if(cl_gun->value) - stuffcmd(ent,"cl_gun 0\n"); - ent->client->frame_zoomrate = zoomrate->value * ent->client->secs_per_frame; - ent->client->zooming = 1; - ent->client->zoomed = true; - } - } - } - else if (!strcasecmp(cmd, "zoomout")) { - if(!deathmatch->value && !coop->value) { - if(ent->client->ps.fov < ent->client->original_fov) { - if(cl_gun->value) - stuffcmd(ent,"cl_gun 0\n"); - ent->client->frame_zoomrate = zoomrate->value * ent->client->secs_per_frame; - ent->client->zooming = -1; - ent->client->zoomed = true; - } - } - } - else if (!strcasecmp(cmd, "zoom")) { - if(!deathmatch->value && !coop->value) { - if(!parm) { - gi.dprintf("syntax: zoom [0/1] (0=off, 1=on)\n"); - } else if(!atoi(parm)) { - ent->client->ps.fov = ent->client->original_fov; - ent->client->zooming = 0; - ent->client->zoomed = false; - SetSensitivities(ent,true); - } else { - ent->client->ps.fov = zoomsnap->value; - ent->client->pers.hand = 2; - if(cl_gun->value) - stuffcmd(ent,"cl_gun 0\n"); - ent->client->zooming = 0; - ent->client->zoomed = true; - SetSensitivities(ent,false); - } - } - } - else if (!strcasecmp(cmd, "zoomoff")) { - if(!deathmatch->value && !coop->value) { - if(ent->client->zoomed && !ent->client->zooming) { - ent->client->ps.fov = ent->client->original_fov; - ent->client->zooming = 0; - ent->client->zoomed = false; - SetSensitivities(ent,true); - } - } - } - else if (!strcasecmp(cmd, "zoomon")) { - if(!deathmatch->value && !coop->value) { - if(!ent->client->zoomed && !ent->client->zooming) { - ent->client->ps.fov = zoomsnap->value; - ent->client->pers.hand = 2; - if(cl_gun->value) - stuffcmd(ent,"cl_gun 0\n"); - ent->client->zooming = 0; - ent->client->zoomed = true; - SetSensitivities(ent,false); - } - } - } - else if (!strcasecmp(cmd, "zoominstop")) { - if(!deathmatch->value && !coop->value) { - if(ent->client->zooming > 0) { - ent->client->zooming = 0; - if(ent->client->ps.fov == ent->client->original_fov) { - ent->client->zoomed = false; - SetSensitivities(ent,true); - } else { - gi.cvar_forceset("zoomsnap",va("%f",ent->client->ps.fov)); - SetSensitivities(ent,false); - } - } - } - } - else if (!strcasecmp(cmd, "zoomoutstop")) { - if(!deathmatch->value && !coop->value) { - if(ent->client->zooming < 0) { - ent->client->zooming = 0; - if(ent->client->ps.fov == ent->client->original_fov) { - ent->client->zoomed = false; - SetSensitivities(ent,true); - } else { - gi.cvar_forceset("zoomsnap",va("%f",ent->client->ps.fov)); - SetSensitivities(ent,false); - } - } - } - } - - else if(!strcasecmp(cmd, "entlist")) { - if(parm) { - edict_t *e; - file_t *f; - int i; - vec3_t origin; - int count; - - f = gi.Fs.Open(parm,"w"); - if( f ) - { - gi.Fs.Printf(f,"Movetype codes\n" - " 0 MOVETYPE_NONE\n" - " 1 MOVETYPE_NOCLIP\n" - " 2 MOVETYPE_PUSH (most moving brush models)\n" - " 3 MOVETYPE_STOP (buttons)\n" - " 4 MOVETYPE_WALK (players only)\n" - " 5 MOVETYPE_STEP (monsters)\n" - " 6 MOVETYPE_FLY (never used)\n" - " 7 MOVETYPE_TOSS (gibs, normal debris)\n" - " 8 MOVETYPE_FLYMISSILE (rockets)\n" - " 9 MOVETYPE_BOUNCE (grenades)\n" - "10 MOVETYPE_VEHICLE (Lazarus func_vehicle)\n" - "11 MOVETYPE_PUSHABLE (Lazarus func_pushable)\n" - "12 MOVETYPE_DEBRIS (Lazarus target_rocks)\n" - "13 MOVETYPE_RAIN (Lazarus precipitation)\n\n"); - gi.Fs.Printf(f,"Solid codes\n" - " 0 SOLID_NOT no interaction with other objects\n" - " 1 SOLID_TRIGGER trigger fields, pickups\n" - " 2 SOLID_BBOX solid point entities\n" - " 3 SOLID_BSP brush models\n\n"); - gi.Fs.Printf(f,"CONTENT_ codes (clipmask)\n" - " 0x00000001 SOLID\n" - " 0x00000002 WINDOW\n" - " 0x00000004 AUX\n" - " 0x00000008 LAVA\n" - " 0x00000010 SLIME\n" - " 0x00000020 WATER\n" - " 0x00000040 MIST\n" - " 0x00008000 AREAPORTAL\n" - " 0x00010000 PLAYERCLIP\n" - " 0x00020000 MONSTERCLIP\n" - " 0x00040000 CURRENT_0\n" - " 0x00080000 CURRENT_90\n" - " 0x00100000 CURRENT_180\n" - " 0x00200000 CURRENT_270\n" - " 0x00400000 CURRENT_UP\n" - " 0x00800000 CURRENT_DOWN\n" - " 0x01000000 ORIGIN\n" - " 0x02000000 MONSTER\n" - " 0x04000000 DEADMONSTER\n" - " 0x08000000 DETAIL\n" - " 0x10000000 TRANSLUCENT\n" - " 0x20000000 LADDER\n\n"); - gi.Fs.Printf(f,"NOTE: \"freed\" indicates an empty slot in the edicts array.\n\n"); - - gi.Fs.Printf(f,"============================================================\n"); - count = 0; - for(i = 0, e = &g_edicts[0]; i < globals.num_edicts; i++, e++) - { - VectorAdd(e->s.origin,e->origin_offset,origin); - gi.Fs.Printf(f,"entity #%d, classname = %s at %s, velocity = %s\n",i,e->classname,vtos(origin),vtos(e->velocity)); - gi.Fs.Printf(f,"health=%d, mass=%d, dmg=%d, wait=%g, angles=%s\n",e->health, e->mass, e->dmg, e->wait, vtos(e->s.angles)); - gi.Fs.Printf(f,"targetname=%s, target=%s, spawnflags=0x%04x\n",e->targetname,e->target,e->spawnflags); - gi.Fs.Printf(f,"absmin,absmax,size=%s, %s, %s\n",vtos(e->absmin),vtos(e->absmax),vtos(e->size)); - gi.Fs.Printf(f,"groundentity=%s\n",(e->groundentity ? e->groundentity->classname : "NULL")); - if(e->classname) - { - // class-specific output - if(e->class_id == ENTITY_TARGET_CHANGELEVEL) - gi.Fs.Printf(f,"map=%s\n",e->map); - } - gi.Fs.Printf(f,"movetype=%d, solid=%d, clipmask=0x%08x\n",e->movetype,e->solid,e->clipmask); - gi.Fs.Printf(f,"================================================================================\n"); - if(e->inuse) count++; - } - gi.Fs.Printf(f,"Total number of entities = %d\n",count); - gi.Fs.Close(f); - gi.dprintf("done!\n"); - } - else - { - gi.dprintf("Error opening %s\n",parm); - } - } - else - { - gi.dprintf("syntax: entlist \n"); - } - } - else if(!strcasecmp(cmd, "properties")) - { - if(parm) { - char filename[_MAX_PATH]; - edict_t *e; - file_t *f; -// int i; - - e = LookingAt(ent,0,NULL,NULL); - if(!e) return; - - GameDirRelativePath(parm,filename); - strcat(filename,".txt"); - f = gi.Fs.Open(filename,"w"); - SaveEntProps(e, f); - gi.Fs.Close(f); - } - else - { - gi.dprintf("syntax: properties \n"); - } - } - else if(!strcasecmp(cmd,"hud")) - { - if(parm) - { - int state = atoi(parm); - - if(state) - Hud_On(); - else - Hud_Off(); - } - else - Cmd_ToggleHud(); - } - else if(!strcasecmp(cmd,"whatsit")) - { - if(parm) - { - int state = atoi(parm); - if(state) - world->effects |= FX_WORLDSPAWN_WHATSIT; - else - world->effects &= ~FX_WORLDSPAWN_WHATSIT; - } - else - world->effects ^= FX_WORLDSPAWN_WHATSIT; - } - -/* else if(!strcasecmp(cmd,"lsight")) - { - if(ent->client->laser_sight) - { - G_FreeEdict(ent->client->laser_sight); - ent->client->laser_sight = NULL; - } - else - { - edict_t *laser; - - laser = G_Spawn(); - ent->client->laser_sight = laser; - laser->movetype = MOVETYPE_NOCLIP; - laser->solid = SOLID_NOT; - laser->s.effects = EF_SPHERETRANS; - laser->s.modelindex = gi.modelindex("sprites/laserdot.spr"); - laser->dmg = 0; - VectorSet (laser->mins, -1, -1, -1); - VectorSet (laser->maxs, 1, 1, 1); - laser->activator = ent; - laser->think = laser_sight_think; - laser->think(laser); - } - } */ - else if(!strcasecmp(cmd,"whereis")) - { - if(parm) - { - edict_t *e; - int i; - int count=0; - - for(i=1; iclassname && !strcasecmp(parm,e->classname)) - { - count++; - gi.dprintf("%d. %s\n",count,vtos(e->s.origin)); - } - } - if(!count) - gi.dprintf("none found\n"); - } - else - gi.dprintf("syntax: whereis \n"); - } - - // debugging/developer stuff - else if(developer->value) - { - if (!strcasecmp(cmd,"bbox")) - Cmd_Bbox_f (ent); - else if(!strcasecmp(cmd,"forcewall")) - { - SpawnForcewall(ent); - } - else if(!strcasecmp(cmd,"forcewall_off")) - { - ForcewallOff(ent); - } - else if (!strcasecmp(cmd,"freeze")) - { - if(level.freeze) - level.freeze = false; - else - level.freeze = true; - } - else if (strcasecmp (cmd, "goto") == 0) - { - if(!parm) - gi.dprintf("syntax: goto \ntype \"destinations\" for a list of targets.\n"); - else - { - edict_t *dest; - int i; - - dest = G_Find (NULL, FOFS(targetname), parm); - if (!dest) - { - gi.dprintf ("Couldn't find destination\n"); - return; - } - - // unlink to make sure it can't possibly interfere with KillBox - gi.unlinkentity (ent); - VectorCopy (dest->s.origin, ent->s.origin); - VectorCopy (dest->s.origin, ent->s.old_origin); - ent->s.origin[2] += 10; - // clear the velocity and hold them in place briefly - VectorClear (ent->velocity); - ent->client->ps.pmove.pm_time = 160>>3; // hold time - ent->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT; - // draw the teleport splash at source and on the player - ent->s.event = EV_PLAYER_TELEPORT; - // set angles - for (i=0 ; i<3 ; i++) - { - ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - ent->client->resp.cmd_angles[i]); - } - VectorClear (ent->s.angles); - VectorClear (ent->client->ps.viewangles); - VectorClear (ent->client->v_angle); - // kill anything at the destination - KillBox (ent); - gi.linkentity (ent); - } - } - else if (strcasecmp (cmd, "destinations") == 0 ) - { - // Display a list of all named info_player_starts and info_notnulls with - // their locations. These entities might be used as targets of - // "goto" console commands (note that "goto" is not limited to these - // entity types, but these are the usual suspects. - edict_t *dest; - int count=0; - - dest = G_Find(NULL, FOFS(classname), "info_player_start"); - while(dest) - { - if(dest->targetname) - { - gi.dprintf("\"%s\" at %s\n",dest->targetname,vtos(dest->s.origin)); - count++; - } - dest = G_Find(dest, FOFS(classname), "info_player_start"); - } - dest = G_Find(NULL, FOFS(classname), "info_notnull"); - while(dest) - { - if(dest->targetname && (dest->spawnflags & 1)) - { - gi.dprintf("\"%s\" at %s\n",dest->targetname,vtos(dest->s.origin)); - count++; - } - dest = G_Find(dest, FOFS(classname), "info_notnull"); - } - if(!count) - gi.dprintf("None found\n"); - } - else if (!strcasecmp(cmd,"id")) { - edict_t *viewing; - vec3_t origin; - float range; - viewing = LookingAt(ent,0,NULL,&range); - if(!viewing) - return; - VectorAdd(viewing->s.origin,viewing->origin_offset,origin); - gi.dprintf("classname = %s at %s, velocity = %s\n",viewing->classname,vtos(origin),vtos(viewing->velocity)); - gi.dprintf("health=%d, mass=%d, dmg=%d, wait=%g, sounds=%d, angles=%s, movetype=%d\n",viewing->health, viewing->mass, viewing->dmg, viewing->wait, viewing->sounds, vtos(viewing->s.angles), viewing->movetype); - gi.dprintf("targetname=%s, target=%s, spawnflags=0x%04x\n",viewing->targetname,viewing->target,viewing->spawnflags); - gi.dprintf("absmin,absmax,size=%s, %s, %s, range=%g\n",vtos(viewing->absmin),vtos(viewing->absmax),vtos(viewing->size),range); - gi.dprintf("groundentity=%s\n",(viewing->groundentity ? viewing->groundentity->classname : "NULL")); - gi.dprintf("movedir=%g %g %g",viewing->movedir[0],viewing->movedir[1],viewing->movedir[2]); - } - else if(!strcasecmp(cmd,"range")) - { - vec3_t forward, point, start; - trace_t tr; - VectorCopy(ent->s.origin,start); - - start[2] += ent->viewheight; - AngleVectors(ent->client->v_angle,forward,NULL,NULL); - VectorMA(start,8192,forward,point); - tr = gi.trace(start,NULL,NULL,point,ent,MASK_SOLID); - VectorSubtract(tr.endpos,start,point); - gi.dprintf("range=%g\n",VectorLength(point)); - } - else if (!strcasecmp(cmd,"setskill")) { - if(gi.argc() < 2) - gi.dprintf("Syntax: setskill X\n"); - else - { - int s = atoi(gi.argv(1)); - gi.cvar_forceset("skill", va("%i", s)); - } - } - else if (!strcasecmp(cmd,"sk")) { - edict_t *viewing; - int skin; - - viewing = LookingAt(ent,0,NULL,NULL); - if(!viewing) - return; - - if(parm) { - skin = atoi(parm); - viewing->s.skin = skin; - gi.linkentity(viewing); - } - else - gi.dprintf("Currently using skin #%i\n",viewing->s.skin); - - } - else if(!strcasecmp(cmd,"spawn")) - { - edict_t *e; - vec3_t forward; - if(!parm) - { - gi.dprintf("syntax: spawn \n"); - return; - } - e = G_Spawn(); - e->classname = (char *)TagMalloc(strlen(parm)+1,TAG_LEVEL); - strcpy(e->classname,parm); - AngleVectors(ent->client->v_angle,forward,NULL,NULL); - VectorMA(ent->s.origin,128,forward,e->s.origin); - e->s.angles[YAW] = ent->s.angles[YAW]; - ED_CallSpawn(e); - } - else if (!strcasecmp(cmd,"stepleft")) { - vec3_t left; - AngleVectors(ent->s.angles,NULL,left,NULL); - VectorNegate(left,left); - VectorMA(ent->s.origin,4,left,ent->s.origin); - gi.linkentity(ent); - } - else if(!strcasecmp(cmd,"texture")) { - trace_t tr; - vec3_t forward, start, end; - - VectorCopy(ent->s.origin, start); - start[2] += ent->viewheight; - AngleVectors(ent->client->v_angle, forward, NULL, NULL); - VectorMA(start, 8192, forward, end); - tr = gi.trace(start,NULL,NULL,end,ent,MASK_ALL); - if(!tr.ent) - gi.dprintf("Nothing hit?\n"); - else { - if(!tr.surface) - gi.dprintf("Not a brush\n"); - else - gi.dprintf("Texture=%s, surface=0x%08x, value=%d\n",tr.surface->name,tr.surface->flags,tr.surface->value); - } - } - else if(!strcasecmp(cmd,"xray")) { - int i; - edict_t *e; - - xray = 1-xray; - for(i=game.maxclients+1; isvflags & SVF_MONSTER) - { - if(xray) - e->s.renderfx |= RF_DEPTHHACK; - else - e->s.renderfx &= ~RF_DEPTHHACK; - gi.linkentity(e); - } - } - } - else - Cmd_Say_f (ent, false, true); - } - // end debugging stuff -} diff --git a/server/g_items.c b/server/g_items.c deleted file mode 100644 index f0b0dae3..00000000 --- a/server/g_items.c +++ /dev/null @@ -1,1607 +0,0 @@ -#include "baseentity.h" - -void Use_Weapon (edict_t *ent, gitem_t *inv); -void Drop_Weapon (edict_t *ent, gitem_t *inv); - -void Weapon_Blaster (edict_t *ent); -void Weapon_HyperBlaster (edict_t *ent); -void Weapon_Null(edict_t *ent); -bool Pickup_Weapon (edict_t *ent, edict_t *other); - -gitem_armor_t jacketarmor_info = { 25, 50, .30, .00, ARMOR_JACKET}; -gitem_armor_t combatarmor_info = { 50, 100, .60, .30, ARMOR_COMBAT}; -gitem_armor_t bodyarmor_info = {100, 200, .80, .60, ARMOR_BODY}; - -int noweapon_index; -static int jacket_armor_index; -static int combat_armor_index; -static int body_armor_index; -static int power_screen_index; -static int power_shield_index; -int shells_index; -int bullets_index; -int grenades_index; -int rockets_index; -int cells_index; -int slugs_index; -int fuel_index; -int homing_index; -int rl_index; -int hml_index; - -#define HEALTH_IGNORE_MAX 1 -#define HEALTH_TIMED 2 - -#define NO_STUPID_SPINNING 4 -#define NO_DROPTOFLOOR 8 -#define SHOOTABLE 16 - -void Use_Quad (edict_t *ent, gitem_t *item); -void Use_Stasis (edict_t *ent, gitem_t *item); -static int quad_drop_timeout_hack; - -//====================================================================== - -// Lazarus: damageable pickups -void item_die(edict_t *self,edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) -{ - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_EXPLOSION1); - WRITE_COORD (self->s.origin); - MESSAGE_SEND (MSG_PVS, self->s.origin, NULL); - - if (!(self->spawnflags & DROPPED_ITEM) && (deathmatch->value)) - SetRespawn (self, 30); - else - G_FreeEdict (self); -} - -/* -=============== -GetItemByIndex -=============== -*/ -gitem_t *GetItemByIndex (int index) -{ - if (index == 0 || index >= game.num_items) - return NULL; - - return &itemlist[index]; -} - - -/* -=============== -FindItemByClassname - -=============== -*/ -gitem_t *FindItemByClassname (char *classname) -{ - int i; - gitem_t *it; - - it = itemlist; - for (i=0 ; iclassname) - continue; - if (!strcasecmp(it->classname, classname)) - return it; - } - - return NULL; -} - -/* -=============== -FindItem - -=============== -*/ -gitem_t *FindItem (char *pickup_name) -{ - int i; - gitem_t *it; - - it = itemlist; - for (i=0 ; ipickup_name) - continue; - if (!strcasecmp(it->pickup_name, pickup_name)) - return it; - } - - return NULL; -} - -//====================================================================== - -void DoRespawn (edict_t *ent) -{ - if (ent->team) - { - edict_t *master; - int count; - int choice; - - master = ent->teammaster; - - for (count = 0, ent = master; ent; ent = ent->chain, count++) - ; - - choice = rand() % count; - - for (count = 0, ent = master; count < choice; ent = ent->chain, count++) - ; - } - - ent->svflags &= ~SVF_NOCLIENT; - if(ent->spawnflags & SHOOTABLE) { - ent->solid = SOLID_BBOX; - ent->clipmask |= MASK_MONSTERSOLID; - if(!ent->health) - ent->health = 20; - ent->takedamage = DAMAGE_YES; - ent->die = item_die; - } else - ent->solid = SOLID_TRIGGER; - gi.linkentity (ent); - - // send an effect - ent->s.event = EV_ITEM_RESPAWN; -} - -void SetRespawn (edict_t *ent, float delay) -{ - ent->flags |= FL_RESPAWN; - ent->svflags |= SVF_NOCLIENT; - ent->solid = SOLID_NOT; - ent->nextthink = level.time + delay; - ent->think = DoRespawn; - gi.linkentity (ent); -} - - -//====================================================================== - -bool Pickup_Powerup (edict_t *ent, edict_t *other) -{ - int quantity; - - quantity = other->client->pers.inventory[ITEM_INDEX(ent->item)]; - if ((skill->value == 1 && quantity >= 2) || (skill->value >= 2 && quantity >= 1)) - return false; - - if ((coop->value) && (ent->item->flags & IT_STAY_COOP) && (quantity > 0)) - return false; - - // Lazarus: Don't allow more than one of some items -#ifdef FLASHLIGHT_MOD - if( !strcasecmp(ent->classname,"item_flashlight") && quantity >= 1 ) return false; -#endif -#ifdef JETPACK_MOD - if( !strcasecmp(ent->classname,"item_jetpack") ) - { - gitem_t *fuel; - - if( quantity >= 1 ) - return false; - - fuel = FindItem("fuel"); - if(ent->count < 0) - { - other->client->jetpack_infinite = true; - Add_Ammo(other,fuel,10000); - } - else - { - other->client->jetpack_infinite = false; - Add_Ammo(other,fuel,ent->count); - } - } -#endif - - other->client->pers.inventory[ITEM_INDEX(ent->item)]++; - -#ifdef FLASHLIGHT_MOD - // DON'T Instant-use flashlight - if (ent->item->use == Use_Flashlight) return true; -#endif - - if (deathmatch->value) - { - if (!(ent->spawnflags & DROPPED_ITEM) ) - SetRespawn (ent, ent->item->quantity); - -#ifdef JETPACK_MOD - // DON'T Instant-use Jetpack - if(ent->item->use == Use_Jet) return true; -#endif - - if (((int)dmflags->value & DF_INSTANT_ITEMS) || ((ent->item->use == Use_Quad) && (ent->spawnflags & DROPPED_PLAYER_ITEM))) - { - if ((ent->item->use == Use_Quad) && (ent->spawnflags & DROPPED_PLAYER_ITEM)) - quad_drop_timeout_hack = (ent->nextthink - level.time) / FRAMETIME; - ent->item->use (other, ent->item); - } - } - - return true; -} - -void Drop_General (edict_t *ent, gitem_t *item) -{ - Drop_Item (ent, item); - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem (ent); -} - -#ifdef JETPACK_MOD -void Drop_Jetpack (edict_t *ent, gitem_t *item) -{ - if(ent->client->jetpack) - gi.cprintf(ent,PRINT_HIGH,"Cannot drop jetpack in use\n"); - else - { - edict_t *dropped; - - dropped = Drop_Item (ent, item); - if(ent->client->jetpack_infinite) - { - dropped->count = -1; - ent->client->pers.inventory[fuel_index] = 0; - ent->client->jetpack_infinite = false; - } - else - { - dropped->count = ent->client->pers.inventory[fuel_index]; - if(dropped->count > 500) - dropped->count = 500; - ent->client->pers.inventory[fuel_index] -= dropped->count; - } - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem (ent); - } -} -#endif - -//====================================================================== - -bool Pickup_Adrenaline (edict_t *ent, edict_t *other) -{ - if (!deathmatch->value) - other->max_health += 1; - - if (other->health < other->max_health) - other->health = other->max_health; - - if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) - SetRespawn (ent, ent->item->quantity); - - return true; -} - -bool Pickup_AncientHead (edict_t *ent, edict_t *other) -{ - other->max_health += 2; - - if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) - SetRespawn (ent, ent->item->quantity); - - return true; -} - -bool Pickup_Bandolier (edict_t *ent, edict_t *other) -{ - gitem_t *item; - int index; - - if (other->client->pers.max_bullets < 250) - other->client->pers.max_bullets = 250; - if (other->client->pers.max_shells < 150) - other->client->pers.max_shells = 150; - if (other->client->pers.max_cells < 250) - other->client->pers.max_cells = 250; - if (other->client->pers.max_slugs < 75) - other->client->pers.max_slugs = 75; - if (other->client->pers.max_fuel < 1500) - other->client->pers.max_fuel = 1500; - - item = FindItem("Bullets"); - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - if (other->client->pers.inventory[index] > other->client->pers.max_bullets) - other->client->pers.inventory[index] = other->client->pers.max_bullets; - } - - item = FindItem("Shells"); - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - if (other->client->pers.inventory[index] > other->client->pers.max_shells) - other->client->pers.inventory[index] = other->client->pers.max_shells; - } - - if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) - SetRespawn (ent, ent->item->quantity); - - return true; -} - -bool Pickup_Pack (edict_t *ent, edict_t *other) -{ - gitem_t *item; - int index; - - if (other->client->pers.max_bullets < 300) - other->client->pers.max_bullets = 300; - if (other->client->pers.max_shells < 200) - other->client->pers.max_shells = 200; - if (other->client->pers.max_rockets < 100) - other->client->pers.max_rockets = 100; - if (other->client->pers.max_grenades < 100) - other->client->pers.max_grenades = 100; - if (other->client->pers.max_cells < 300) - other->client->pers.max_cells = 300; - if (other->client->pers.max_slugs < 100) - other->client->pers.max_slugs = 100; - if (other->client->pers.max_fuel < 2000) - other->client->pers.max_fuel = 2000; - - item = FindItem("Bullets"); - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - if (other->client->pers.inventory[index] > other->client->pers.max_bullets) - other->client->pers.inventory[index] = other->client->pers.max_bullets; - } - - item = FindItem("Shells"); - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - if (other->client->pers.inventory[index] > other->client->pers.max_shells) - other->client->pers.inventory[index] = other->client->pers.max_shells; - } - - item = FindItem("Cells"); - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - if (other->client->pers.inventory[index] > other->client->pers.max_cells) - other->client->pers.inventory[index] = other->client->pers.max_cells; - } - - item = FindItem("Grenades"); - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - if (other->client->pers.inventory[index] > other->client->pers.max_grenades) - other->client->pers.inventory[index] = other->client->pers.max_grenades; - } - - item = FindItem("Rockets"); - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - if (other->client->pers.inventory[index] > other->client->pers.max_rockets) - other->client->pers.inventory[index] = other->client->pers.max_rockets; - } - - item = FindItem("Slugs"); - if (item) - { - index = ITEM_INDEX(item); - other->client->pers.inventory[index] += item->quantity; - if (other->client->pers.inventory[index] > other->client->pers.max_slugs) - other->client->pers.inventory[index] = other->client->pers.max_slugs; - } - - if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) - SetRespawn (ent, ent->item->quantity); - - return true; -} - -//====================================================================== - -void Use_Quad (edict_t *ent, gitem_t *item) -{ - int timeout; - - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem (ent); - - if (quad_drop_timeout_hack) - { - timeout = quad_drop_timeout_hack; - quad_drop_timeout_hack = 0; - } - else - { - timeout = 300; - } - - if (ent->client->quad_framenum > level.framenum) - ent->client->quad_framenum += timeout; - else - ent->client->quad_framenum = level.framenum + timeout; - - gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage.wav"), 1, ATTN_NORM, 0); -} - -//====================================================================== - -void Use_Breather (edict_t *ent, gitem_t *item) -{ - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem (ent); - - if (ent->client->breather_framenum > level.framenum) - ent->client->breather_framenum += 300; - else - ent->client->breather_framenum = level.framenum + 300; - -// gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage.wav"), 1, ATTN_NORM, 0); -} - -//====================================================================== - -void Use_Envirosuit (edict_t *ent, gitem_t *item) -{ - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem (ent); - - if (ent->client->enviro_framenum > level.framenum) - ent->client->enviro_framenum += 300; - else - ent->client->enviro_framenum = level.framenum + 300; - -// gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage.wav"), 1, ATTN_NORM, 0); -} - -//====================================================================== - -void Use_Invulnerability (edict_t *ent, gitem_t *item) -{ - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem (ent); - - if (ent->client->invincible_framenum > level.framenum) - ent->client->invincible_framenum += 300; - else - ent->client->invincible_framenum = level.framenum + 300; - - gi.sound(ent, CHAN_ITEM, gi.soundindex("items/protect.wav"), 1, ATTN_NORM, 0); -} - -//====================================================================== - -void Use_Silencer (edict_t *ent, gitem_t *item) -{ - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem (ent); - ent->client->silencer_shots += 30; - -// gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage.wav"), 1, ATTN_NORM, 0); -} - -//====================================================================== - -bool Pickup_Key (edict_t *ent, edict_t *other) -{ - if (coop->value) - { - if (strcmp(ent->classname, "key_power_cube") == 0) - { - if (other->client->pers.power_cubes & ((ent->spawnflags & 0x0000ff00)>> 8)) - return false; - other->client->pers.inventory[ITEM_INDEX(ent->item)]++; - other->client->pers.power_cubes |= ((ent->spawnflags & 0x0000ff00) >> 8); - } - else - { - if (other->client->pers.inventory[ITEM_INDEX(ent->item)]) - return false; - other->client->pers.inventory[ITEM_INDEX(ent->item)] = 1; - } - return true; - } - other->client->pers.inventory[ITEM_INDEX(ent->item)]++; - return true; -} - -//====================================================================== - -bool Add_Ammo (edict_t *ent, gitem_t *item, int count) -{ - int index; - int max; - - if (!ent->client) - return false; - - if (item->tag == AMMO_BULLETS) - max = ent->client->pers.max_bullets; - else if (item->tag == AMMO_SHELLS) - max = ent->client->pers.max_shells; - else if (item->tag == AMMO_ROCKETS) - max = ent->client->pers.max_rockets; - else if (item->tag == AMMO_GRENADES) - max = ent->client->pers.max_grenades; - else if (item->tag == AMMO_CELLS) - max = ent->client->pers.max_cells; - else if (item->tag == AMMO_SLUGS) - max = ent->client->pers.max_slugs; - else if (item->tag == AMMO_FUEL) - max = ent->client->pers.max_fuel; - else if (item->tag == AMMO_HOMING_MISSILES) - max = ent->client->pers.max_homing_missiles; - else - return false; - - index = ITEM_INDEX(item); - - if (ent->client->pers.inventory[index] == max) - return false; - - ent->client->pers.inventory[index] += count; - - if (ent->client->pers.inventory[index] > max) - ent->client->pers.inventory[index] = max; - - return true; -} - -bool Pickup_Ammo (edict_t *ent, edict_t *other) -{ - int oldcount; - int count; - bool weapon; - - weapon = (ent->item->flags & IT_WEAPON); - if ( (weapon) && ( (int)dmflags->value & DF_INFINITE_AMMO ) ) - count = 1000; - else if (ent->count) - count = ent->count; - else - count = ent->item->quantity; - - oldcount = other->client->pers.inventory[ITEM_INDEX(ent->item)]; - - if (!Add_Ammo (other, ent->item, count)) - return false; - - if (weapon && !oldcount) - { - if (other->client->pers.weapon != ent->item && ( !deathmatch->value || other->client->pers.weapon == FindItem("blaster") || other->client->pers.weapon == FindItem("No weapon") ) ) - other->client->newweapon = ent->item; - } - - if (!(ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM)) && (deathmatch->value)) - SetRespawn (ent, 30); - return true; -} - -void Drop_Ammo (edict_t *ent, gitem_t *item) -{ - edict_t *dropped; - int index; - - index = ITEM_INDEX(item); - dropped = Drop_Item (ent, item); - if (ent->client->pers.inventory[index] >= item->quantity) - dropped->count = item->quantity; - else - dropped->count = ent->client->pers.inventory[index]; - - if (ent->client->pers.weapon && - ent->client->pers.weapon->tag == AMMO_GRENADES && - item->tag == AMMO_GRENADES && - ent->client->pers.inventory[index] - dropped->count <= 0) { - gi.cprintf (ent, PRINT_HIGH, "Can't drop current weapon\n"); - G_FreeEdict(dropped); - return; - } - - ent->client->pers.inventory[index] -= dropped->count; - ValidateSelectedItem (ent); -} - - -//====================================================================== - -void MegaHealth_think (edict_t *self) -{ - if (self->owner->health > self->owner->max_health) - { - self->nextthink = level.time + 1; - self->owner->health -= 1; - return; - } - - if (!(self->spawnflags & DROPPED_ITEM) && (deathmatch->value)) - SetRespawn (self, 20); - else - G_FreeEdict (self); -} - -bool Pickup_Health (edict_t *ent, edict_t *other) -{ - if (!(ent->style & HEALTH_IGNORE_MAX)) - if (other->health >= other->max_health) - return false; - - other->health += ent->count; - - if (!(ent->style & HEALTH_IGNORE_MAX)) - { - if (other->health > other->max_health) - other->health = other->max_health; - } - - if (ent->style & HEALTH_TIMED) - { - ent->think = MegaHealth_think; - ent->nextthink = level.time + 5; - ent->owner = other; - ent->flags |= FL_RESPAWN; - ent->svflags |= SVF_NOCLIENT; - ent->solid = SOLID_NOT; - } - else - { - if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) - SetRespawn (ent, 30); - } - - return true; -} - -//====================================================================== - -int ArmorIndex (edict_t *ent) -{ - return 0; -} - -bool Pickup_Armor (edict_t *ent, edict_t *other) -{ - int old_armor_index; - gitem_armor_t *oldinfo; - gitem_armor_t *newinfo; - int newcount; - float salvage; - int salvagecount; - - // get info on new armor - newinfo = (gitem_armor_t *)ent->item->info; - - old_armor_index = ArmorIndex (other); - - // handle armor shards specially - if (ent->item->tag == ARMOR_SHARD) - { - if (!old_armor_index) - other->client->pers.inventory[jacket_armor_index] = 2; - else - other->client->pers.inventory[old_armor_index] += 2; - } - - // if player has no armor, just use it - else if (!old_armor_index) - { - other->client->pers.inventory[ITEM_INDEX(ent->item)] = newinfo->base_count; - } - - // use the better armor - else - { - // get info on old armor - if (old_armor_index == jacket_armor_index) - oldinfo = &jacketarmor_info; - else if (old_armor_index == combat_armor_index) - oldinfo = &combatarmor_info; - else // (old_armor_index == body_armor_index) - oldinfo = &bodyarmor_info; - - if (newinfo->normal_protection > oldinfo->normal_protection) - { - // calc new armor values - salvage = oldinfo->normal_protection / newinfo->normal_protection; - salvagecount = salvage * other->client->pers.inventory[old_armor_index]; - newcount = newinfo->base_count + salvagecount; - if (newcount > newinfo->max_count) - newcount = newinfo->max_count; - - // zero count of old armor so it goes away - other->client->pers.inventory[old_armor_index] = 0; - - // change armor to new item with computed value - other->client->pers.inventory[ITEM_INDEX(ent->item)] = newcount; - } - else - { - // calc new armor values - salvage = newinfo->normal_protection / oldinfo->normal_protection; - salvagecount = salvage * newinfo->base_count; - newcount = other->client->pers.inventory[old_armor_index] + salvagecount; - if (newcount > oldinfo->max_count) - newcount = oldinfo->max_count; - - // if we're already maxed out then we don't need the new armor - if (other->client->pers.inventory[old_armor_index] >= newcount) - return false; - - // update current armor value - other->client->pers.inventory[old_armor_index] = newcount; - } - } - - if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) - SetRespawn (ent, 20); - - return true; -} - -//====================================================================== - -int PowerArmorType (edict_t *ent) -{ - if (!ent->client) - return POWER_ARMOR_NONE; - - if (!(ent->flags & FL_POWER_ARMOR)) - return POWER_ARMOR_NONE; - - if (ent->client->pers.inventory[power_shield_index] > 0) - return POWER_ARMOR_SHIELD; - - if (ent->client->pers.inventory[power_screen_index] > 0) - return POWER_ARMOR_SCREEN; - - return POWER_ARMOR_NONE; -} - -void Use_PowerArmor (edict_t *ent, gitem_t *item) -{ - int index; - - if (ent->flags & FL_POWER_ARMOR) - { - ent->flags &= ~FL_POWER_ARMOR; - gi.sound(ent, CHAN_AUTO, gi.soundindex("misc/power2.wav"), 1, ATTN_NORM, 0); - } - else - { - index = cells_index; - if (!ent->client->pers.inventory[index]) - { - gi.cprintf (ent, PRINT_HIGH, "No cells for power armor.\n"); - return; - } - ent->flags |= FL_POWER_ARMOR; - gi.sound(ent, CHAN_AUTO, gi.soundindex("misc/power1.wav"), 1, ATTN_NORM, 0); - } -} - -bool Pickup_PowerArmor (edict_t *ent, edict_t *other) -{ - int quantity; - - quantity = other->client->pers.inventory[ITEM_INDEX(ent->item)]; - - other->client->pers.inventory[ITEM_INDEX(ent->item)]++; - - if (deathmatch->value) - { - if (!(ent->spawnflags & DROPPED_ITEM) ) - SetRespawn (ent, ent->item->quantity); - // auto-use for DM only if we didn't already have one - if (!quantity) - ent->item->use (other, ent->item); - } - - return true; -} - -void Drop_PowerArmor (edict_t *ent, gitem_t *item) -{ - if ((ent->flags & FL_POWER_ARMOR) && (ent->client->pers.inventory[ITEM_INDEX(item)] == 1)) - Use_PowerArmor (ent, item); - Drop_General (ent, item); -} - -//====================================================================== - -/* -=============== -Touch_Item -=============== -*/ -void Touch_Item (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - bool taken; - - if (!other->client) - return; - if (other->health < 1) - return; // dead people can't pickup - if (!ent->item->pickup) - return; // not a grabbable item? - - taken = ent->item->pickup(ent, other); - - if (taken) - { - // flash the screen - other->client->bonus_alpha = 0.25; - - // show icon and name on status bar - other->client->ps.stats[STAT_PICKUP_ICON] = gi.imageindex(ent->item->icon); - other->client->ps.stats[STAT_PICKUP_STRING] = CS_ITEMS+ITEM_INDEX(ent->item); - other->client->pickup_msg_time = level.time + 3.0; - - // change selected item - if (ent->item->use) - other->client->pers.selected_item = other->client->ps.stats[STAT_SELECTED_ITEM] = ITEM_INDEX(ent->item); - - if (ent->item->pickup == Pickup_Health) - { - if (ent->count == 2) - gi.sound(other, CHAN_ITEM, gi.soundindex("items/s_health.wav"), 1, ATTN_NORM, 0); - else if (ent->count == 10) - gi.sound(other, CHAN_ITEM, gi.soundindex("items/n_health.wav"), 1, ATTN_NORM, 0); - else if (ent->count == 25) - gi.sound(other, CHAN_ITEM, gi.soundindex("items/l_health.wav"), 1, ATTN_NORM, 0); - else // (ent->count == 100) - gi.sound(other, CHAN_ITEM, gi.soundindex("items/m_health.wav"), 1, ATTN_NORM, 0); - } - else if (ent->item->pickup_sound) - { - gi.sound(other, CHAN_ITEM, gi.soundindex(ent->item->pickup_sound), 1, ATTN_NORM, 0); - } - } - - if (!(ent->spawnflags & ITEM_TARGETS_USED)) - { - G_UseTargets (ent, other); - ent->spawnflags |= ITEM_TARGETS_USED; - } - - if (!taken) - return; - - if (!((coop->value) && (ent->item->flags & IT_STAY_COOP)) || (ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM))) - { - if (ent->flags & FL_RESPAWN) - ent->flags &= ~FL_RESPAWN; - else - G_FreeEdict (ent); - } -} - -//====================================================================== - -static void drop_temp_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - if (other == ent->owner) - return; - - Touch_Item (ent, other, plane, surf); -} - -static void drop_make_touchable (edict_t *ent) -{ - ent->touch = Touch_Item; - if (deathmatch->value) - { - ent->nextthink = level.time + 29; - ent->think = G_FreeEdict; - } -} - -edict_t *Drop_Item (edict_t *ent, gitem_t *item) -{ - edict_t *dropped; - vec3_t forward, right; - vec3_t offset; - - dropped = G_Spawn(); - - dropped->classname = item->classname; - dropped->item = item; - dropped->spawnflags = DROPPED_ITEM; - dropped->s.effects = item->world_model_flags; - dropped->s.renderfx = RF_GLOW; -// VectorSet (dropped->mins, -15, -15, -15); -// VectorSet (dropped->maxs, 15, 15, 15); - VectorSet (dropped->mins, -16, -16, -16); - VectorSet (dropped->maxs, 16, 16, 16); - gi.setmodel (dropped, dropped->item->world_model); - dropped->solid = SOLID_TRIGGER; - dropped->movetype = MOVETYPE_TOSS; - dropped->touch = drop_temp_touch; - dropped->owner = ent; - - // Lazarus: for monster-dropped health - dropped->count = item->quantity; - if(item->pickup == Pickup_Health) - { - if(item->quantity == 2) - dropped->style |= HEALTH_IGNORE_MAX; - if(item->quantity == 100) - dropped->style |= HEALTH_IGNORE_MAX | HEALTH_TIMED; - } - - if (ent->client) - { - trace_t trace; - - AngleVectors (ent->client->v_angle, forward, right, NULL); - VectorSet(offset, 24, 0, -16); - G_ProjectSource (ent->s.origin, offset, forward, right, dropped->s.origin); - trace = gi.trace (ent->s.origin, dropped->mins, dropped->maxs, - dropped->s.origin, ent, CONTENTS_SOLID); - VectorCopy (trace.endpos, dropped->s.origin); - } - else - { -// Lazarus: throw the dropped item a bit farther than the default - trace_t trace; - - AngleVectors (ent->s.angles, forward, right, NULL); -// VectorCopy (ent->s.origin, dropped->s.origin); - VectorSet(offset, 24, 0, -16); - G_ProjectSource (ent->s.origin, offset, forward, right, dropped->s.origin); - trace = gi.trace (ent->s.origin, dropped->mins, dropped->maxs, - dropped->s.origin, ent, CONTENTS_SOLID); - VectorCopy (trace.endpos, dropped->s.origin); - } - - VectorScale (forward, 100, dropped->velocity); - dropped->velocity[2] = 300; - - dropped->think = drop_make_touchable; - dropped->nextthink = level.time + 1; - - gi.linkentity (dropped); - - return dropped; -} - -void Use_Item (edict_t *ent, edict_t *other, edict_t *activator) -{ - ent->svflags &= ~SVF_NOCLIENT; - ent->use = NULL; - - if (ent->spawnflags & ITEM_NO_TOUCH) - { - ent->solid = SOLID_BBOX; - ent->touch = NULL; - } - else - { - // Lazarus: - if(ent->spawnflags & SHOOTABLE) { - ent->solid = SOLID_BBOX; - ent->clipmask |= MASK_MONSTERSOLID; - if(!ent->health) - ent->health = 20; - ent->takedamage = DAMAGE_YES; - ent->die = item_die; - } else - ent->solid = SOLID_TRIGGER; - ent->touch = Touch_Item; - } - - gi.linkentity (ent); -} - -//====================================================================== - -/* -================ -droptofloor -================ -*/ -void droptofloor (edict_t *ent) -{ - trace_t tr; - vec3_t dest; - float *v; - - v = tv(-15,-15,-15); - VectorCopy (v, ent->mins); - v = tv(15,15,15); - VectorCopy (v, ent->maxs); - - if (ent->model) - gi.setmodel (ent, ent->model); - else - gi.setmodel (ent, ent->item->world_model); - - // Lazarus: - // origin_offset is wrong - absmin and absmax weren't set soon enough. - // Fortunately we KNOW what the "offset" is - nada. - VectorClear(ent->origin_offset); - - if(ent->spawnflags & SHOOTABLE) { - ent->solid = SOLID_BBOX; - ent->clipmask |= MASK_MONSTERSOLID; - if(!ent->health) - ent->health = 20; - ent->takedamage = DAMAGE_YES; - ent->die = item_die; - } else - ent->solid = SOLID_TRIGGER; - - if(ent->spawnflags & NO_DROPTOFLOOR) - ent->movetype = MOVETYPE_NONE; - else - ent->movetype = MOVETYPE_TOSS; - ent->touch = Touch_Item; - - // Lazarus: - if(!(ent->spawnflags & NO_DROPTOFLOOR)) { - v = tv(0,0,-128); - VectorAdd (ent->s.origin, v, dest); - - tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, dest, ent, MASK_SOLID); - if (tr.startsolid) - { - gi.dprintf ("droptofloor: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin)); - G_FreeEdict (ent); - return; - } - tr.endpos[2] += 1; - ent->mins[2] -= 1; - VectorCopy (tr.endpos, ent->s.origin); - } - - if (ent->team) - { - ent->flags &= ~FL_TEAMSLAVE; - ent->chain = ent->teamchain; - ent->teamchain = NULL; - - ent->svflags |= SVF_NOCLIENT; - ent->solid = SOLID_NOT; - if (ent == ent->teammaster) - { - ent->nextthink = level.time + FRAMETIME; - ent->think = DoRespawn; - } - } - - if (ent->spawnflags & ITEM_NO_TOUCH) - { - ent->solid = SOLID_BBOX; - ent->touch = NULL; - ent->s.effects &= ~EF_ROTATE; - ent->s.renderfx &= ~RF_GLOW; - } - - if (ent->spawnflags & ITEM_TRIGGER_SPAWN) - { - ent->svflags |= SVF_NOCLIENT; - ent->solid = SOLID_NOT; - ent->use = Use_Item; - } - - gi.linkentity (ent); -} - - -/* -=============== -PrecacheItem - -Precaches all data needed for a given item. -This will be called for each item spawned in a level, -and for each item in each client's inventory. -=============== -*/ -void PrecacheItem (gitem_t *it) -{ - char *s, *start; - char data[MAX_QPATH]; - int len; - gitem_t *ammo; - - if (!it) - return; - - if (it->pickup_sound) - gi.soundindex (it->pickup_sound); - if (it->world_model) - gi.modelindex (it->world_model); - if (it->view_model) - gi.modelindex (it->view_model); - if (it->icon) - gi.imageindex (it->icon); - - // parse everything for its ammo - if (it->ammo && it->ammo[0]) - { - ammo = FindItem (it->ammo); - if (ammo != it) - PrecacheItem (ammo); - } - - // parse the space seperated precache string for other items - s = it->precaches; - if (!s || !s[0]) - return; - - while (*s) - { - start = s; - while (*s && *s != ' ') - s++; - - len = s-start; - if (len >= MAX_QPATH || len < 5) - gi.error ("PrecacheItem: %s has bad precache string", it->classname); - memcpy (data, start, len); - data[len] = 0; - if (*s) - s++; - - // determine type based on extension - if (!strcmp(data+len-3, "md2")) - gi.modelindex (data); - else if (!strcmp(data+len-3, "sp2")) - gi.modelindex (data); - else if (!strcmp(data+len-3, "wav")) - gi.soundindex (data); - if (!strcmp(data+len-3, "pcx")) - gi.imageindex (data); - } -} - -/* -============ -SpawnItem - -Sets the clipping size and plants the object on the floor. - -Items can't be immediately dropped to floor, because they might -be on an entity that hasn't spawned yet. -============ -*/ -void SpawnItem (edict_t *ent, gitem_t *item) -{ - PrecacheItem (item); - - // Lazarus: added several spawnflags, plus gave ALL keys trigger_spawn and no_touch - // capabilities - if ( ( (item->flags & IT_KEY) && (ent->spawnflags & ~31) ) || - (!(item->flags & IT_KEY) && (ent->spawnflags & ~28) ) ) -// if (ent->spawnflags) - { -// if (strcmp(ent->classname, "key_power_cube") != 0) - { - gi.dprintf("%s at %s has invalid spawnflags set (%d)\n", ent->classname, vtos(ent->s.origin), ent->spawnflags); - if (item->flags & IT_KEY) - ent->spawnflags &= 31; - else - ent->spawnflags &= 28; - } - } - - // some items will be prevented in deathmatch - if (deathmatch->value) - { - if ( (int)dmflags->value & DF_NO_ARMOR ) - { - if (item->pickup == Pickup_Armor || item->pickup == Pickup_PowerArmor) - { - G_FreeEdict (ent); - return; - } - } - if ( (int)dmflags->value & DF_NO_ITEMS ) - { - if (item->pickup == Pickup_Powerup) - { - G_FreeEdict (ent); - return; - } - } - if ( (int)dmflags->value & DF_NO_HEALTH ) - { - if (item->pickup == Pickup_Health || item->pickup == Pickup_Adrenaline || item->pickup == Pickup_AncientHead) - { - G_FreeEdict (ent); - return; - } - } - if ( (int)dmflags->value & DF_INFINITE_AMMO ) - { - if ( (item->flags == IT_AMMO) || (strcmp(ent->classname, "weapon_bfg") == 0) ) - { - G_FreeEdict (ent); - return; - } - } - } - - if (coop->value && (strcmp(ent->classname, "key_power_cube") == 0)) - { - ent->spawnflags |= (1 << (8 + level.power_cubes)); - level.power_cubes++; - } - - // don't let them drop items that stay in a coop game - if ((coop->value) && (item->flags & IT_STAY_COOP)) - { - item->drop = NULL; - } - - // Lazarus: flashlight - get level-wide cost for use - if(strcmp(ent->classname, "item_flashlight") == 0) - level.flashlight_cost = ent->count; - - ent->item = item; - ent->nextthink = level.time + 2 * FRAMETIME; // items start after other solids - ent->think = droptofloor; - ent->s.effects = item->world_model_flags; - ent->s.renderfx = RF_GLOW; - - // Lazarus: - if(item->pickup == Pickup_Health) - { - ent->count = item->quantity; - ent->style = item->tag; - } - if(ent->spawnflags & NO_STUPID_SPINNING) - { - ent->s.effects &= ~EF_ROTATE; - ent->s.renderfx &= ~RF_GLOW; - } - - if (ent->model) gi.modelindex (ent->model); -} - -//====================================================================== - -gitem_t itemlist[] = -{ - { - NULL - }, // leave index 0 alone - - { - "item_flashlight", - Pickup_Powerup, - Use_Flashlight, - Drop_General, - NULL, - "items/pkup.wav", - "models/items/f_light/tris.md2", EF_ROTATE, - NULL, - "p_flash", - "Flashlight", - 2, - 60, - NULL, - IT_POWERUP, - 0, - NULL, - 0, - "" - }, - // - // WEAPONS - // - -/*QUAKED weapon_blaster (.3 .3 1) (-16 -16 -16) (16 16 16) -*/ - { - "weapon_blaster", - Pickup_Weapon, - Use_Weapon, - Drop_Weapon, - Weapon_Blaster, - "misc/w_pkup.wav", - "models/weapons/w_glock.mdl", EF_ROTATE, - "models/weapons/v_glock.mdl", - "w_blaster", - "Blaster", - 0, - 0, - NULL, - IT_WEAPON|IT_STAY_COOP, - WEAP_BLASTER, - NULL, - 0, - "weapons/blastf1a.wav misc/lasfly.wav" - }, - // Lazarus: No weapon - we HAVE to have a weapon - { - "weapon_null", - NULL, - Use_Weapon, - NULL, - Weapon_Null, - "misc/w_pkup.wav", - NULL, 0, - NULL, - NULL, - "No Weapon", - 0, - 0, - NULL, - IT_WEAPON|IT_STAY_COOP, - WEAP_NONE, - NULL, - 0, - "" - }, - { - "item_freeze", - Pickup_Powerup, - Use_Stasis, - Drop_General, - NULL, - "items/pkup.wav", - "models/items/stasis/tris.md2", EF_ROTATE, - NULL, - "p_freeze", - "Stasis Generator", - 2, - 30, - NULL, - IT_POWERUP, - 0, - NULL, - 0, - "items/stasis_start.wav items/stasis.wav items/stasis_stop.wav" - }, - { - "item_health_small", - Pickup_Health, - NULL, - NULL, - NULL, - "items/s_health.wav", - "models/w_medkit.mdl", 0, - NULL, - "i_health", - "Health", - 3, - 2, - NULL, - 0, - 0, - NULL, - HEALTH_IGNORE_MAX, - "items/s_health.wav" - }, - { - "item_health", - Pickup_Health, - NULL, - NULL, - NULL, - "items/n_health.wav", - "models/items/healing/medium/tris.md2", 0, - NULL, - "i_health", - "Health", - 3, - 10, - NULL, - 0, - 0, - NULL, - 0, - "items/n_health.wav" - }, - {NULL} -}; - - -// QUAKED item_health (.3 .3 1) (-16 -16 -16) (16 16 16) - -void SP_item_health (edict_t *self) -{ - if ( deathmatch->value && ((int)dmflags->value & DF_NO_HEALTH) ) - { - G_FreeEdict (self); - return; - } - - self->class_id = ENTITY_ITEM_HEALTH; - self->model = "models/items/healing/medium/tris.md2"; - self->count = 10; - SpawnItem (self, FindItemByClassname ("item_health")); - gi.soundindex ("items/n_health.wav"); -} - -// QUAKED item_health_small (.3 .3 1) (-16 -16 -16) (16 16 16) - -void SP_item_health_small (edict_t *self) -{ - if ( deathmatch->value && ((int)dmflags->value & DF_NO_HEALTH) ) - { - G_FreeEdict (self); - return; - } - - self->class_id = ENTITY_ITEM_HEALTH_SMALL; - self->model = "models/w_medkit.mdl"; - self->count = 2; - SpawnItem (self, FindItemByClassname ("item_health_small")); - self->style = HEALTH_IGNORE_MAX; - gi.soundindex ("items/s_health.wav"); -} - -// QUAKED item_health_large (.3 .3 1) (-16 -16 -16) (16 16 16) - -void SP_item_health_large (edict_t *self) -{ - if ( deathmatch->value && ((int)dmflags->value & DF_NO_HEALTH) ) - { - G_FreeEdict (self); - return; - } - - self->class_id = ENTITY_ITEM_HEALTH_LARGE; - self->model = "models/items/healing/large/tris.md2"; - self->count = 25; -// SpawnItem (self, FindItem ("Health")); - SpawnItem (self, FindItemByClassname ("item_health_large")); - gi.soundindex ("items/l_health.wav"); -} - -// QUAKED item_health_mega (.3 .3 1) (-16 -16 -16) (16 16 16) - -void SP_item_health_mega (edict_t *self) -{ - if ( deathmatch->value && ((int)dmflags->value & DF_NO_HEALTH) ) - { - G_FreeEdict (self); - return; - } - - self->class_id = ENTITY_ITEM_HEALTH_MEGA; - self->model = "models/items/mega_h/tris.md2"; - self->count = 100; -// SpawnItem (self, FindItem ("Health")); - SpawnItem (self, FindItemByClassname ("item_health_mega")); - gi.soundindex ("items/m_health.wav"); - self->style = HEALTH_IGNORE_MAX|HEALTH_TIMED; -} - - -void InitItems (void) -{ - game.num_items = sizeof(itemlist)/sizeof(itemlist[0]) - 1; -} - - - -/* -=============== -SetItemNames - -Called by worldspawn -=============== -*/ -void SetItemNames (void) -{ - int i; - gitem_t *it; - - for (i=0 ; ipickup_name); - } - - noweapon_index = ITEM_INDEX(FindItem("No Weapon")); - jacket_armor_index = ITEM_INDEX(FindItem("Jacket Armor")); - combat_armor_index = ITEM_INDEX(FindItem("Combat Armor")); - body_armor_index = ITEM_INDEX(FindItem("Body Armor")); - power_screen_index = ITEM_INDEX(FindItem("Power Screen")); - power_shield_index = ITEM_INDEX(FindItem("Power Shield")); - shells_index = ITEM_INDEX(FindItem("shells")); - bullets_index = ITEM_INDEX(FindItem("bullets")); - grenades_index = ITEM_INDEX(FindItem("Grenades")); - rockets_index = ITEM_INDEX(FindItem("rockets")); - cells_index = ITEM_INDEX(FindItem("cells")); - slugs_index = ITEM_INDEX(FindItem("slugs")); - fuel_index = ITEM_INDEX(FindItem("fuel")); - homing_index = ITEM_INDEX(FindItem("homing missiles")); - rl_index = ITEM_INDEX(FindItem("rocket launcher")); - hml_index = ITEM_INDEX(FindItem("Homing Missile Launcher")); -} - -/* -================== -Use_Flashlight -================== -*/ -void Use_Flashlight ( edict_t *ent, gitem_t *item ) -{ - if(!ent->client->flashlight) - { - if(ent->client->pers.inventory[ITEM_INDEX(FindItem(FLASHLIGHT_ITEM))] < level.flashlight_cost) - { - gi.cprintf(ent,PRINT_HIGH,"Flashlight requires %s\n",FLASHLIGHT_ITEM); - return; - } -#if FLASHLIGHT_USE != POWERUP_USE_ITEM - /* Lazarus: We never "use up" the flashlight - ent->client->pers.inventory[ITEM_INDEX(item)]--; */ - ValidateSelectedItem (ent); -#endif - } - if(ent->client->flashlight ^= 1) - ent->client->flashlight_time = level.time + FLASHLIGHT_DRAIN; -} - -#ifdef JETPACK_MOD -//============================================================================== -void Use_Jet ( edict_t *ent, gitem_t *item ) -{ - if(ent->client->jetpack) - { - // Currently on... turn it off and store remaining time - ent->client->jetpack = false; - ent->client->jetpack_framenum = 0; - // Force frame. While using the jetpack ClientThink forces the frame to - // stand20 when it really SHOULD be jump2. This is fine, but if we leave - // it at that then the player cycles through the wrong frames to complete - // his "jump" when the jetpack is turned off. The same thing is done in - // ClientThink when jetpack timer expires. - ent->s.frame = 67; - gi.sound(ent,CHAN_GIZMO,gi.soundindex("jetpack/shutdown.wav"), 1, ATTN_NORM, 0); - } - else - { - // Currently off. Turn it on, and add time, if any, remaining - // from last jetpack. - if( ent->client->pers.inventory[ITEM_INDEX(item)] ) - { - ent->client->jetpack = true; - // Lazarus: Never remove jetpack from inventory (unless dropped) - // ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem (ent); - ent->client->jetpack_framenum = level.framenum; - ent->client->jetpack_activation = level.framenum; - } - else if(ent->client->pers.inventory[fuel_index] > 0) - { - ent->client->jetpack = true; - ent->client->jetpack_framenum = level.framenum; - ent->client->jetpack_activation = level.framenum; - } - else - return; // Shouldn't have been able to get here, but I'm a pessimist - gi.sound( ent, CHAN_GIZMO, gi.soundindex("jetpack/activate.wav"), 1, ATTN_NORM, 0); - } -} -#endif - -// Lazarus: Stasis field generator -void Use_Stasis ( edict_t *ent, gitem_t *item ) -{ - ent->client->pers.inventory[ITEM_INDEX(item)]--; - ValidateSelectedItem (ent); - level.freeze = true; - level.freezeframes = 0; - gi.sound(ent, CHAN_ITEM, gi.soundindex("items/stasis_start.wav"), 1, ATTN_NORM, 0); -} diff --git a/server/g_local.h b/server/g_local.h deleted file mode 100644 index ce127ec3..00000000 --- a/server/g_local.h +++ /dev/null @@ -1,1506 +0,0 @@ -// g_local.h -- local definitions for game module - - -// define GAME_INCLUDE so that game.h does not define the -// short, server-visible gclient_t and edict_t structures, -// because we define the full size ones in this file -#define GAME_INCLUDE -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "basetypes.h" -#include "basemath.h" -#include "qfiles.h" -#include "const.h" -#include "ref_system.h" -#include "ref_server.h" -#include "p_menu.h" - -// the "gameversion" client command will print this plus compile date -#define GAMEVERSION "xash" - -extern byte *zone_level; -extern byte *zone_game; - -#define FreeTag(data) gi.Mem.Free(data) - -//malloc-free -#define Mem_Alloc(pool,size) gi.Mem.Alloc(pool, size, __FILE__, __LINE__) -#define Mem_Free(mem) gi.Mem.Free(mem, __FILE__, __LINE__) - -//Hunk_AllocName -#define Mem_AllocPool(name) gi.Mem.AllocPool(name, __FILE__, __LINE__) -#define Mem_FreePool(pool) gi.Mem.FreePool(pool, __FILE__, __LINE__) -#define Mem_EmptyPool(pool) gi.Mem.EmptyPool(pool, __FILE__, __LINE__) - -#define COM_Parse(data) gi.Script.ParseToken(data) - -#define GameTitle gi.GameInfo().title - -/* -=========================================== -infostring manager -=========================================== -*/ -#define Info_Print(x) gi.Info.Print -#define Info_Validate(x) gi.Info.Validate(x) -#define Info_RemoveKey(x, y) gi.Info.RemoveKey(x,y) -#define Info_ValueForKey(x,y) gi.Info.ValueForKey(x,y) -#define Info_SetValueForKey(x,y,z) gi.Info.SetValueForKey(x,y,z) - -void *TagMalloc (int size, int tag); -void FreeTags (int tag); - -// protocol bytes that can be directly added to messages -#define svc_muzzleflash 1 -#define svc_muzzleflash2 2 -#define svc_temp_entity 3 -#define svc_layout 4 -#define svc_inventory 5 -#define svc_stufftext 11 - -/* -=========================================== -network messaging manager -=========================================== -*/ - -#define MESSAGE_BEGIN(x) gi.Msg.Begin(x) -#define WRITE_CHAR(x) gi.Msg.WriteChar(x) -#define WRITE_BYTE(x) gi.Msg.WriteByte(x) -#define WRITE_WORD(x) gi.Msg.WriteWord(x) -#define WRITE_SHORT(x) gi.Msg.WriteShort(x) -#define WRITE_LONG(x) gi.Msg.WriteLong(x) -#define WRITE_FLOAT(x) gi.Msg.WriteFloat(x) -#define WRITE_STRING(x) gi.Msg.WriteString(x) -#define WRITE_COORD(x) gi.Msg.WriteCoord(x) -#define WRITE_DIR(x) gi.Msg.WriteDir(x) -#define WRITE_ANGLE(x) gi.Msg.WriteAngler(x) -#define MESSAGE_SEND(x,y,z) gi.Msg.Send(x,y,z) - -//================================================================== - -// view pitching times -#define DAMAGE_TIME 0.5 -#define FALL_TIME 0.3 - - -// edict->spawnflags -// these are set with checkboxes on each entity in the map editor -#define SPAWNFLAG_NOT_EASY 0x00000100 -#define SPAWNFLAG_NOT_MEDIUM 0x00000200 -#define SPAWNFLAG_NOT_HARD 0x00000400 -#define SPAWNFLAG_NOT_DEATHMATCH 0x00000800 - -// edict->flags -#define FL_FLY 0x00000001 -#define FL_SWIM 0x00000002 // implied immunity to drowining -#define FL_IMMUNE_LASER 0x00000004 -#define FL_INWATER 0x00000008 -#define FL_GODMODE 0x00000010 -#define FL_NOTARGET 0x00000020 -#define FL_IMMUNE_SLIME 0x00000040 -#define FL_IMMUNE_LAVA 0x00000080 -#define FL_PARTIALGROUND 0x00000100 // not all corners are valid -#define FL_WATERJUMP 0x00000200 // player jumping out of water -#define FL_TEAMSLAVE 0x00000400 // not the first on the team -#define FL_NO_KNOCKBACK 0x00000800 -#define FL_POWER_ARMOR 0x00001000 // power armor (if any) is active -#define FL_BOB 0x00002000 // Lazarus: Used for bobbing water - -#define FL_TRACKTRAIN 0x00008000 -#define FL_DISGUISED 0x00020000 // entity is in disguise, monsters will not recognize. -#define FL_NOGIB 0x00040000 // player has been vaporized by a nuke, drop no gibs - -#define FL_REVERSIBLE 0x00080000 // Lazarus: used for reversible func_door_rotating -#define FL_REVOLVING 0x00100000 // Lazarus revolving door -#define FL_ROBOT 0x00200000 // Player-controlled robot or monster. Relax yaw constraints - -#define FL_RESPAWN 0x80000000 // used for item respawning - - -#define FRAMETIME 0.1 - -// memory tags to allow dynamic memory to be cleaned up -#define TAG_GAME 765 // clear when unloading the dll -#define TAG_LEVEL 766 // clear when loading a new level - - -#define MELEE_DISTANCE 80 - -#define BODY_QUEUE_SIZE 8 - -typedef enum -{ - AMMO_BULLETS, - AMMO_SHELLS, - AMMO_ROCKETS, - AMMO_GRENADES, - AMMO_CELLS, - AMMO_SLUGS, - AMMO_FUEL, - AMMO_HOMING_MISSILES -} ammo_t; - - -//deadflag -#define DEAD_NO 0 -#define DEAD_DYING 1 -#define DEAD_DEAD 2 -#define DEAD_RESPAWNABLE 3 -#define DEAD_FROZEN 4 // Lazarus: Don't shift angles, just freeze him - -//range -#define RANGE_MELEE 0 -#define RANGE_NEAR 1 -#define RANGE_MID 2 -#define RANGE_FAR 3 - -//gib types -#define GIB_ORGANIC 0 -#define GIB_METALLIC 1 - -//monster ai flags -#define AI_STAND_GROUND 0x00000001 -#define AI_TEMP_STAND_GROUND 0x00000002 -#define AI_SOUND_TARGET 0x00000004 -#define AI_LOST_SIGHT 0x00000008 -#define AI_PURSUIT_LAST_SEEN 0x00000010 -#define AI_PURSUE_NEXT 0x00000020 -#define AI_PURSUE_TEMP 0x00000040 -#define AI_HOLD_FRAME 0x00000080 -#define AI_GOOD_GUY 0x00000100 -#define AI_BRUTAL 0x00000200 -#define AI_NOSTEP 0x00000400 -#define AI_DUCKED 0x00000800 -#define AI_COMBAT_POINT 0x00001000 -#define AI_MEDIC 0x00002000 -#define AI_RESURRECTING 0x00004000 -#define AI_ACTOR 0x00040000 // Is this a misc_actor? -#define AI_FOLLOW_LEADER 0x00080000 // misc_actor only -#define AI_TWO_GUNS 0x00100000 // misc_actor only - nothing to do with AI really, - // but we're out of spawnflags -#define AI_RESPAWN_FINDPLAYER 0x00200000 // used for monsters that change maps with - // a trigger_transition... tells 'em to find SP - // player right away -#define AI_FREEFORALL 0x00400000 // Set by target_monsterbattle, lets dmgteam monsters - // attack monsters on opposion dmgteam -#define AI_RANGE_PAUSE 0x00800000 -#define AI_SEEK_COVER 0x02000000 -#define AI_CHICKEN 0x04000000 -#define AI_MEDIC_PATROL 0x08000000 -#define AI_HINT_TEST 0x10000000 -#define AI_CROUCH 0x20000000 -#define AI_EVADE_GRENADE 0x40000000 -#define AI_SEEK_ENEMY 0x80000000 - -//monster attack state -#define AS_STRAIGHT 1 -#define AS_SLIDING 2 -#define AS_MELEE 3 -#define AS_MISSILE 4 -#define AS_BLIND 5 // PMM - used by boss code to do nasty things even if it can't see you - -// armor types -#define ARMOR_NONE 0 -#define ARMOR_JACKET 1 -#define ARMOR_COMBAT 2 -#define ARMOR_BODY 3 -#define ARMOR_SHARD 4 - -// power armor types -#define POWER_ARMOR_NONE 0 -#define POWER_ARMOR_SCREEN 1 -#define POWER_ARMOR_SHIELD 2 - -// handedness values -#define RIGHT_HANDED 0 -#define LEFT_HANDED 1 -#define CENTER_HANDED 2 - - -// game.serverflags values -#define SFL_CROSS_TRIGGER_1 0x00000001 -#define SFL_CROSS_TRIGGER_2 0x00000002 -#define SFL_CROSS_TRIGGER_3 0x00000004 -#define SFL_CROSS_TRIGGER_4 0x00000008 -#define SFL_CROSS_TRIGGER_5 0x00000010 -#define SFL_CROSS_TRIGGER_6 0x00000020 -#define SFL_CROSS_TRIGGER_7 0x00000040 -#define SFL_CROSS_TRIGGER_8 0x00000080 -#define SFL_CROSS_TRIGGER_MASK 0x000000ff - - -// noise types for PlayerNoise -#define PNOISE_SELF 0 -#define PNOISE_WEAPON 1 -#define PNOISE_IMPACT 2 - -// actor follow parms -#define ACTOR_FOLLOW_RUN_RANGE 256 // AI_FOLLOW_LEADER actors run if farther away than this -#define ACTOR_FOLLOW_STAND_RANGE 128 // .. .. stand if closer than this - -typedef struct -{ - int base_count; - int max_count; - float normal_protection; - float energy_protection; - int armor; -} gitem_armor_t; - - -// gitem_t->flags -#define IT_WEAPON 1 // use makes active weapon -#define IT_AMMO 2 -#define IT_ARMOR 4 -#define IT_STAY_COOP 8 -#define IT_KEY 16 -#define IT_POWERUP 32 - -// gitem_t->weapmodel for weapons indicates model index -#define WEAP_BLASTER 1 -#define WEAP_SHOTGUN 2 -#define WEAP_SUPERSHOTGUN 3 -#define WEAP_MACHINEGUN 4 -#define WEAP_CHAINGUN 5 -#define WEAP_GRENADES 6 -#define WEAP_GRENADELAUNCHER 7 -#define WEAP_ROCKETLAUNCHER 8 -#define WEAP_HYPERBLASTER 9 -#define WEAP_RAILGUN 10 -#define WEAP_BFG 11 -#define WEAP_NONE 12 - -typedef struct gitem_s -{ - char *classname; // spawning name - bool (*pickup)(struct edict_s *ent, struct edict_s *other); - void (*use)(struct edict_s *ent, struct gitem_s *item); - void (*drop)(struct edict_s *ent, struct gitem_s *item); - void (*weaponthink)(struct edict_s *ent); - char *pickup_sound; - char *world_model; - int world_model_flags; - char *view_model; - - // client side info - char *icon; - char *pickup_name; // for printing on pickup - int count_width; // number of digits to display by icon - - int quantity; // for ammo how much, for weapons how much is used per shot - char *ammo; // for weapons - int flags; // IT_* flags - - int weapmodel; // weapon model index (for weapons) - - void *info; - int tag; - - char *precaches; // string of all models, sounds, and images this item will use -} gitem_t; - - - -// -// this structure is left intact through an entire game -// it should be initialized at dll load time, and read/written to -// the server.ssv file for savegames -// -typedef struct -{ - char helpmessage1[512]; - char helpmessage2[512]; - int helpchanged; // flash F1 icon if non 0, play sound - // and increment only if 1, 2, or 3 - - gclient_t *clients; // [maxclients] - - // can't store spawnpoint in level, because - // it would get overwritten by the savegame restore - char spawnpoint[512]; // needed for coop respawns - - // store latched cvars here that we want to get at often - int maxclients; - int maxentities; - - // cross level triggers - int serverflags; - - // Lazarus: target_lock combination - char lock_code[9]; - int lock_revealed; - bool lock_hud; - // Lazarus: number of entities moved between maps (not counting players) - int transition_ents; - - // items - int num_items; - - bool autosaved; -} game_locals_t; - -// -// this structure is cleared as each map is entered -// it is read/written to the level.sav file for savegames -// -typedef struct -{ - int framenum; - float time; - - char level_name[MAX_QPATH]; // the descriptive name (Outer Base, etc) - char mapname[MAX_QPATH]; // the server name (base1, etc) - char nextmap[MAX_QPATH]; // go here when fraglimit is hit - - // intermission state - float intermissiontime; // time the intermission was started - char *changemap; - int exitintermission; - vec3_t intermission_origin; - vec3_t intermission_angle; - - edict_t *sight_client; // changed once each frame for coop games - - edict_t *sight_entity; - int sight_entity_framenum; - edict_t *sound_entity; - int sound_entity_framenum; - edict_t *sound2_entity; - int sound2_entity_framenum; - - int pic_health; - - int total_secrets; - int found_secrets; - - int total_goals; - int found_goals; - - int total_monsters; - int killed_monsters; - - edict_t *current_entity; // entity running from G_RunFrame - int body_que; // dead bodies - - int power_cubes; // ugly necessity for coop - - int flashlight_cost; // cost/10 seconds for flashlight - int mud_puddles; - int num_3D_sounds; - bool restart_for_actor_models; - bool freeze; - int freezeframes; - int next_skill; - -} level_locals_t; - -extern level_locals_t level; - -// spawn_temp_t is only used to hold entity field values that -// can be set from the editor, but aren't actualy present -// in edict_t during gameplay -typedef struct -{ - // world vars - char *sky; - float skyrotate; - vec3_t skyaxis; - char *nextmap; - - int lip; - int distance; - int height; - char *noise; - float pausetime; - char *item; - char *gravity; - - float minyaw; - float maxyaw; - float minpitch; - float maxpitch; - float phase; - - float shift; -} spawn_temp_t; - - -typedef struct -{ - // fixed data - vec3_t start_origin; - vec3_t start_angles; - vec3_t end_origin; - vec3_t end_angles; - - int sound_start; - int sound_middle; - int sound_end; - - float accel; - float speed; - float decel; - float distance; - - float wait; - - // state data - int state; - int prevstate; - vec3_t dir; - float current_speed; - float move_speed; - float next_speed; - float remaining_distance; - float decel_distance; - float ratio; - void (*endfunc)(edict_t *); - bool is_blocked; -} moveinfo_t; - - -typedef struct -{ - void (*aifunc)(edict_t *self, float dist); - float dist; - void (*thinkfunc)(edict_t *self); -} mframe_t; - -typedef struct -{ - int firstframe; - int lastframe; - mframe_t *frame; - void (*endfunc)(edict_t *self); -} mmove_t; - -typedef struct -{ - mmove_t *currentmove; - mmove_t *savemove; - int aiflags; - int nextframe; - float scale; - - void (*stand)(edict_t *self); - void (*idle)(edict_t *self); - void (*search)(edict_t *self); - void (*walk)(edict_t *self); - void (*run)(edict_t *self); - void (*dodge)(edict_t *self, edict_t *other, float eta); - void (*attack)(edict_t *self); - void (*melee)(edict_t *self); - void (*sight)(edict_t *self, edict_t *other); - bool (*checkattack)(edict_t *self); - void (*jump)(edict_t *self); - - float pausetime; - float attack_finished; - - vec3_t saved_goal; - float search_time; - float trail_time; - vec3_t last_sighting; - int attack_state; - int lefty; - float idle_time; - int linkcount; - - int power_armor_type; - int power_armor_power; - - float min_range; // Monsters stop chasing enemy at this distance - float max_range; // Monsters won't notice or attack targets farther than this - float ideal_range[2]; // Ideal low and high range from target, weapon-specific - float flies; // Probability of dead monster generating flies - float jumpup; - float jumpdn; - float rangetime; - int chicken_framenum; - int pathdir; // Up/down a hint_path chain flag for medic - float visibility; // Ratio of visibility - -} monsterinfo_t; - -extern game_locals_t game; -extern level_locals_t level; -extern game_import_t gi; -extern game_export_t globals; -extern spawn_temp_t st; - -extern int sm_meat_index; -extern int snd_fry; - -extern int noweapon_index; -extern int jacket_armor_index; -extern int combat_armor_index; -extern int body_armor_index; -extern int shells_index; -extern int bullets_index; -extern int grenades_index; -extern int rockets_index; -extern int cells_index; -extern int slugs_index; -extern int fuel_index; -extern int homing_index; -extern int rl_index; -extern int hml_index; - -// means of death -#define MOD_UNKNOWN 0 -#define MOD_BLASTER 1 -#define MOD_SHOTGUN 2 -#define MOD_SSHOTGUN 3 -#define MOD_MACHINEGUN 4 -#define MOD_CHAINGUN 5 -#define MOD_GRENADE 6 -#define MOD_G_SPLASH 7 -#define MOD_ROCKET 8 -#define MOD_R_SPLASH 9 -#define MOD_HYPERBLASTER 10 -#define MOD_RAILGUN 11 -#define MOD_BFG_LASER 12 -#define MOD_BFG_BLAST 13 -#define MOD_BFG_EFFECT 14 -#define MOD_HANDGRENADE 15 -#define MOD_HG_SPLASH 16 -#define MOD_WATER 17 -#define MOD_SLIME 18 -#define MOD_LAVA 19 -#define MOD_CRUSH 20 -#define MOD_TELEFRAG 21 -#define MOD_FALLING 22 -#define MOD_SUICIDE 23 -#define MOD_HELD_GRENADE 24 -#define MOD_EXPLOSIVE 25 -#define MOD_BARREL 26 -#define MOD_BOMB 27 -#define MOD_EXIT 28 -#define MOD_SPLASH 29 -#define MOD_TARGET_LASER 30 -#define MOD_TRIGGER_HURT 31 -#define MOD_HIT 32 -#define MOD_TARGET_BLASTER 33 -#define MOD_VEHICLE 34 -#define MOD_KICK 35 -#define MOD_FRIENDLY_FIRE 0x8000000 - -extern int meansOfDeath; - -extern edict_t *g_edicts; - -#define FOFS(x) (int)&(((edict_t *)0)->x) -#define STOFS(x) (int)&(((spawn_temp_t *)0)->x) -#define LLOFS(x) (int)&(((level_locals_t *)0)->x) -#define CLOFS(x) (int)&(((gclient_t *)0)->x) - -#define random() ((rand () & 0x7fff) / ((float)0x7fff)) -#define crandom() (2.0 * (random() - 0.5)) - -extern cvar_t *maxentities; -extern cvar_t *deathmatch; -extern cvar_t *coop; -extern cvar_t *dmflags; -extern cvar_t *skill; -extern cvar_t *fraglimit; -extern cvar_t *timelimit; -extern cvar_t *password; -extern cvar_t *spectator_password; -extern cvar_t *needpass; -extern cvar_t *g_select_empty; -extern cvar_t *dedicated; - -extern cvar_t *filterban; - -extern cvar_t *sv_gravity; -extern cvar_t *sv_maxvelocity; - -extern cvar_t *gun_x, *gun_y, *gun_z; -extern cvar_t *sv_rollspeed; -extern cvar_t *sv_rollangle; - -extern cvar_t *run_pitch; -extern cvar_t *run_roll; -extern cvar_t *bob_up; -extern cvar_t *bob_pitch; -extern cvar_t *bob_roll; - -extern cvar_t *sv_cheats; -extern cvar_t *maxclients; -extern cvar_t *maxspectators; - -extern cvar_t *flood_msgs; -extern cvar_t *flood_persecond; -extern cvar_t *flood_waitdelay; - -extern cvar_t *sv_maplist; -extern cvar_t *r_motionblur; - -extern cvar_t *actorchicken; -extern cvar_t *actorjump; -extern cvar_t *actorscram; -extern cvar_t *alert_sounds; -extern cvar_t *allow_download; -extern cvar_t *bounce_bounce; -extern cvar_t *bounce_minv; -extern cvar_t *cd_loopcount; -extern cvar_t *cl_gun; -extern cvar_t *corpse_fade; -extern cvar_t *corpse_fadetime; -extern cvar_t *crosshair; -extern cvar_t *developer; -extern cvar_t *fov; -extern cvar_t *gl_clear; -extern cvar_t *gl_driver; -extern cvar_t *hand; -extern cvar_t *jetpack_weenie; -extern cvar_t *joy_pitchsensitivity; -extern cvar_t *joy_yawsensitivity; -extern cvar_t *jump_kick; -extern cvar_t *lazarus_cd_loop; -extern cvar_t *lazarus_cl_gun; -extern cvar_t *lazarus_crosshair; -extern cvar_t *lazarus_gl_clear; -extern cvar_t *lazarus_joyp; -extern cvar_t *lazarus_joyy; -extern cvar_t *lazarus_pitch; -extern cvar_t *lazarus_yaw; -extern cvar_t *m_pitch; -extern cvar_t *m_yaw; -extern cvar_t *monsterjump; -extern cvar_t *player_vampire; -extern cvar_t *readout; -extern cvar_t *rocket_strafe; -extern cvar_t *rotate_distance; -extern cvar_t *s_primary; -extern cvar_t *shift_distance; -extern cvar_t *sv_maxgibs; -extern cvar_t *turn_rider; -extern cvar_t *zoomrate; -extern cvar_t *zoomsnap; - -extern int max_modelindex; -extern int max_soundindex; - - -#define world (&g_edicts[0]) - -// item spawnflags -#define ITEM_TRIGGER_SPAWN 0x00000001 -#define ITEM_NO_TOUCH 0x00000002 -// 6 bits reserved for editor flags -// 8 bits used as power cube id bits for coop games -#define DROPPED_ITEM 0x00010000 -#define DROPPED_PLAYER_ITEM 0x00020000 -#define ITEM_TARGETS_USED 0x00040000 - -// -// fields are needed for spawning from the entity string -// and saving / loading games -// -#define FFL_SPAWNTEMP 1 -#define FFL_NOSPAWN 2 - -typedef enum { - F_INT, - F_FLOAT, - F_LSTRING, // string on disk, pointer in memory, TAG_LEVEL - F_GSTRING, // string on disk, pointer in memory, TAG_GAME - F_VECTOR, - F_ANGLEHACK, - F_EDICT, // index on disk, pointer in memory - F_ITEM, // index on disk, pointer in memory - F_CLIENT, // index on disk, pointer in memory - F_FUNCTION, - F_MMOVE, - F_IGNORE -} fieldtype_t; - -typedef struct -{ - char *name; - int ofs; - fieldtype_t type; - int flags; -} field_t; - -typedef struct -{ - char *name; - void (*spawn)(edict_t *ent); -} spawn_t; - -// Lazarus: worldspawn effects -#define FX_WORLDSPAWN_NOHELP 1 -#define FX_WORLDSPAWN_STEPSOUNDS 2 -#define FX_WORLDSPAWN_WHATSIT 4 -#define FX_WORLDSPAWN_ALERTSOUNDS 8 -#define FX_WORLDSPAWN_CORPSEFADE 16 -#define FX_WORLDSPAWN_JUMPKICK 32 - - -extern field_t fields[]; -extern gitem_t itemlist[]; -extern spawn_t spawns[]; - -// -// g_ai.c -// -void AI_SetSightClient (void); -void ai_stand (edict_t *self, float dist); -void ai_move (edict_t *self, float dist); -void ai_walk (edict_t *self, float dist); -void ai_turn (edict_t *self, float dist); -void ai_run (edict_t *self, float dist); -void ai_charge (edict_t *self, float dist); -bool canReach (edict_t *ent, edict_t *other); -bool FacingIdeal(edict_t *self); -bool FindTarget (edict_t *self); -void FoundTarget (edict_t *self); -void HuntTarget (edict_t *self); -bool infront (edict_t *self, edict_t *other); -int range (edict_t *self, edict_t *other); -bool visible (edict_t *self, edict_t *other); -bool ai_chicken (edict_t *ent, edict_t *badguy); -// -// g_combat.c -// -bool OnSameTeam (edict_t *ent1, edict_t *ent2); -bool CanDamage (edict_t *targ, edict_t *inflictor); -void T_Damage (edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod); -void T_RadiusDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod, double dmg_slope); -void Killed (edict_t *targ, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point); - -// damage flags -#define DAMAGE_RADIUS 0x00000001 // damage was indirect -#define DAMAGE_NO_ARMOR 0x00000002 // armour does not protect from this damage -#define DAMAGE_ENERGY 0x00000004 // damage is from an energy based weapon -#define DAMAGE_NO_KNOCKBACK 0x00000008 // do not affect velocity, just view angles -#define DAMAGE_BULLET 0x00000010 // damage is from a bullet (used for ricochets) -#define DAMAGE_NO_PROTECTION 0x00000020 // armor, shields, invulnerability, and godmode have no effect - -#define DEFAULT_BULLET_HSPREAD 300 -#define DEFAULT_BULLET_VSPREAD 500 -#define DEFAULT_SHOTGUN_HSPREAD 1000 -#define DEFAULT_SHOTGUN_VSPREAD 500 -#define DEFAULT_DEATHMATCH_SHOTGUN_COUNT 12 -#define DEFAULT_SHOTGUN_COUNT 12 -#define DEFAULT_SSHOTGUN_COUNT 20 -// -// g_cmds.c -// -void Cmd_Help_f (edict_t *ent); -void Cmd_Score_f (edict_t *ent); -void Use_Flashlight(edict_t *ent,gitem_t *item); -void ClientCommand (edict_t *ent); -// -// g_items.c -// -void PrecacheItem (gitem_t *it); -void InitItems (void); -void SetItemNames (void); -gitem_t *FindItem (char *pickup_name); -gitem_t *FindItemByClassname (char *classname); -#define ITEM_INDEX(x) ((x)-itemlist) -edict_t *Drop_Item (edict_t *ent, gitem_t *item); -void SetRespawn (edict_t *ent, float delay); -void ChangeWeapon (edict_t *ent); -void SpawnItem (edict_t *ent, gitem_t *item); -void Think_Weapon (edict_t *ent); -int ArmorIndex (edict_t *ent); -int PowerArmorType (edict_t *ent); -gitem_t *GetItemByIndex (int index); -bool Add_Ammo (edict_t *ent, gitem_t *item, int count); -void Touch_Item (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf); -// -// g_main.c -// -void SaveClientData (void); -void FetchClientEntData (edict_t *ent); -// -// g_misc.c -// -void ThrowHead (edict_t *self, char *gibname, int damage, int type); -void ThrowClientHead (edict_t *self, int damage); -void ThrowGib (edict_t *self, char *gibname, int damage, int type); -void BecomeExplosion1(edict_t *self); -void barrel_delay (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point); -void barrel_explode (edict_t *self); -void func_explosive_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point); -void PrecacheDebris (int style); -// -// g_monster.c -// -#define SF_MONSTER_AMBUSH 1 -#define SF_MONSTER_TRIGGER_SPAWN 2 -#define SF_MONSTER_SIGHT 4 -#define SF_MONSTER_GOODGUY 8 -#define SF_MONSTER_NOGIB 16 -#define SF_MONSTER_SPECIAL 32 -#define SF_ACTOR_BAD_GUY 64 -#define SF_MONSTER_FLIES 128 // only used for monster_commander_body -#define SF_MONSTER_IGNORESHOTS 128 -#define SF_MONSTER_KNOWS_MIRRORS 0x00010000 - -void FadeSink (edict_t *ent); -void FadeDieSink (edict_t *ent); -void monster_fire_bullet (edict_t *self, vec3_t start, vec3_t dir, int damage, int kick, int hspread, int vspread, int flashtype); -void monster_fire_shotgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int flashtype); -void monster_fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect); -void monster_fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int flashtype); -void monster_fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, edict_t *homing_target); -void monster_fire_railgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int flashtype); -void monster_fire_bfg (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int kick, float damage_radius, int flashtype); -void HintTestNext (edict_t *self, edict_t *hint); -int HintTestStart (edict_t *self); -void M_droptofloor (edict_t *ent); -void monster_think (edict_t *self); -void walkmonster_start (edict_t *self); -void swimmonster_start (edict_t *self); -void flymonster_start (edict_t *self); -void AttackFinished (edict_t *self, float time); -void monster_death_use (edict_t *self); -void M_CatagorizePosition (edict_t *ent); -bool M_CheckAttack (edict_t *self); -void M_FlyCheck (edict_t *self); -void M_FliesOff (edict_t *self); -void M_FliesOn (edict_t *self); -void M_CheckGround (edict_t *ent); -bool M_walkmove (edict_t *ent, float yaw, float dist); -bool M_SetDeath (edict_t *ent,mmove_t **moves); -int PatchMonsterModel (char *model); -// -// g_patchplayermodels.c -// -int PatchPlayerModels (char *modelname); -// -// g_phys.c -// -void SV_AddGravity (edict_t *ent); -void G_RunEntity (edict_t *ent); -// -// g_spawn.c -// -void ED_CallSpawn (edict_t *ent); -void G_FindTeams(); -void Cmd_ToggleHud (); -void Hud_On(); -void Hud_Off(); -// -// g_svcmds.c -// -void ServerCommand (void); -bool SV_FilterPacket (char *from); -// -// g_thing.c -// -edict_t *SpawnThing(); -// -// g_trigger.c -// -typedef struct -{ - char *name; -} entlist_t; -bool HasSpawnFunction(edict_t *ent); -int trigger_transition_ents (edict_t *changelevel, edict_t *self); -// -// g_utils.c -// -bool KillBox (edict_t *ent); -void G_ProjectSource (vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result); -edict_t *G_Find (edict_t *from, int fieldofs, char *match); -edict_t *findradius (edict_t *from, vec3_t org, float rad); -edict_t *G_PickTarget (char *targetname); -void G_UseTargets (edict_t *ent, edict_t *activator); -void G_SetMovedir (vec3_t angles, vec3_t movedir); -void G_InitEdict (edict_t *e); -edict_t *G_Spawn (void); -void G_FreeEdict (edict_t *e); -void G_TouchTriggers (edict_t *ent); -void G_TouchSolids (edict_t *ent); -char *G_CopyString (char *in); -void stuffcmd(edict_t *ent,char *command); -float *tv (float x, float y, float z); -char *vtos (vec3_t v); -float vectoyaw (vec3_t vec); -void vectoangles (vec3_t vec, vec3_t angles); -bool point_infront (edict_t *self, vec3_t point); -void AnglesNormalize(vec3_t vec); -float SnapToEights(float x); -// Lazarus -float AtLeast(float x, float dx); -edict_t *LookingAt(edict_t *ent, int filter, vec3_t endpos, float *range); -void GameDirRelativePath(char *filename, char *output); -void G_UseTarget (edict_t *ent, edict_t *activator, edict_t *target); -// -// p_client.c -// -void player_pain (edict_t *self, edict_t *other, float kick, int damage); -void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point); -void respawn (edict_t *ent); -void BeginIntermission (edict_t *targ); -void PutClientInServer (edict_t *ent); -void InitClientPersistant (gclient_t *client,int style); -void InitClientResp (gclient_t *client); -void InitBodyQue (void); -void ClientBeginServerFrame (edict_t *ent); -// -// p_hud.c -// -void MoveClientToIntermission (edict_t *client); -void G_SetStats (edict_t *ent); -void G_SetSpectatorStats (edict_t *ent); -void ValidateSelectedItem (edict_t *ent); -void DeathmatchScoreboardMessage (edict_t *client, edict_t *killer); -// -// p_view.c -// -void ClientEndServerFrame (edict_t *ent); -// -// p_weapon.c -// -void PlayerNoise(edict_t *who, vec3_t where, int type); -void P_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result); -void kick_attack (edict_t *ent); - -//============================================================================ - -// client_t->anim_priority -#define ANIM_BASIC 0 // stand / run -#define ANIM_WAVE 1 -#define ANIM_JUMP 2 -#define ANIM_PAIN 3 -#define ANIM_ATTACK 4 -#define ANIM_DEATH 5 -#define ANIM_REVERSE 6 - - -// client data that stays across multiple level loads -typedef struct -{ - char userinfo[MAX_INFO_STRING]; - char netname[16]; - int hand; - - bool connected; // a loadgame will leave valid entities that - // just don't have a connection yet - - // values saved and restored from edicts when changing levels - int health; - int max_health; - int savedFlags; - - int selected_item; - int inventory[MAX_ITEMS]; - - // ammo capacities - int max_bullets; - int max_shells; - int max_rockets; - int max_grenades; - int max_cells; - int max_slugs; - int max_fuel; - int max_homing_missiles; - - gitem_t *weapon; - gitem_t *lastweapon; - - bool fire_mode; // Lazarus - alternate firing mode - - int power_cubes; // used for tracking the cubes in coop games - int score; // for calculating total unit score in coop games - - int game_helpchanged; - int helpchanged; - - bool spectator; // client is a spectator - bool spawn_landmark; - bool spawn_levelchange; - vec3_t spawn_offset; - vec3_t spawn_velocity; - vec3_t spawn_angles; - vec3_t spawn_viewangles; - int spawn_pm_flags; - int spawn_gunframe; - int spawn_modelframe; - int spawn_anim_end; - gitem_t *newweapon; -} client_persistant_t; - -// client data that stays across deathmatch respawns -typedef struct -{ - client_persistant_t coop_respawn; // what to set client->pers to on a respawn - int enterframe; // level.framenum the client entered the game - int score; // frags, etc - vec3_t cmd_angles; // angles sent over in the last command - - bool spectator; // client is a spectator -} client_respawn_t; - -// this structure is cleared on each PutClientInServer(), -// except for 'client->pers' -struct gclient_s -{ - // known to server - player_state_t ps; // communicated by server to clients - int ping; - - // private to game - client_persistant_t pers; - client_respawn_t resp; - pmove_state_t old_pmove; // for detecting out-of-pmove changes - - bool showscores; // set layout stat - bool showinventory; // set layout stat - bool showhelp; - bool showhelpicon; - - int ammo_index; - - int buttons; - int oldbuttons; - int latched_buttons; - - int nNewLatch; - - bool weapon_thunk; - - gitem_t *newweapon; - - // sum up damage over an entire frame, so - // shotgun blasts give a single big kick - int damage_armor; // damage absorbed by armor - int damage_parmor; // damage absorbed by power armor - int damage_blood; // damage taken out of health - int damage_knockback; // impact damage - vec3_t damage_from; // origin for vector calculation - - float killer_yaw; // when dead, look at killer - - weaponstate_t weaponstate; - vec3_t kick_angles; // weapon kicks - vec3_t kick_origin; - float v_dmg_roll, v_dmg_pitch, v_dmg_time; // damage kicks - float fall_time, fall_value; // for view drop on fall - float damage_alpha; - float bonus_alpha; - vec3_t damage_blend; - vec3_t v_angle; // aiming direction - float bobtime; // so off-ground doesn't change it - vec3_t oldviewangles; - vec3_t oldvelocity; - - float next_drown_time; - int old_waterlevel; - int breather_sound; - - int machinegun_shots; // for weapon raising - - // animation vars - int anim_end; - int anim_priority; - bool anim_duck; - bool anim_run; - - // powerup timers - float quad_framenum; - float invincible_framenum; - float breather_framenum; - float enviro_framenum; - - bool grenade_blew_up; - float grenade_time; - int silencer_shots; - int weapon_sound; - - float pickup_msg_time; - - float flood_locktill; // locked from talking - float flood_when[10]; // when messages were said - int flood_whenhead; // head pointer for when said - - float respawn_time; // can respawn when time > this - - usercmd_t ucmd; // Lazarus: Copied for convenience in ClientThink - - // TREMOR func_pushable stuff - float maxvelocity; // Used when pushing func_pushable - edict_t *push; - - // flashlight - bool flashlight; - float flashlight_time; - - // menu stuff ala CTF - bool inmenu; // in menu - int menutimer; - pmenuhnd_t *menu; // current menu - char *whatsit; - - // security camera - edict_t *spycam; - edict_t *monitor; - edict_t *camplayer; - vec3_t org_viewangles; - short old_owner_angles[2]; - - // laser sight - edict_t *laser_sight; - - int vehicle_framenum; // last time player engaged or disengaged vehicle - int zooming; - float joy_pitchsensitivity; - float joy_yawsensitivity; - float m_pitch; - float m_yaw; - bool sensitivities_init; - bool zoomed; - float original_fov; - float fps_time_start; - int fps_frames; - float secs_per_frame; - float frame_zoomrate; - - int shift_dir; // direction code for debugging/moving an item - int startframe; // time at which ClientBegin is called - - float fadestart; - float fadein; // for fading screen to black at mission failure - float fadehold; - float fadeout; - vec3_t fadecolor; - float fadealpha; - - int leftfoot; // 0 or 1, used for footstep sounds - int jumping; // 0 or 1, used for jumpkick - - edict_t *homing_rocket; // used to limit firing frequency -}; - -#define NUM_ACTOR_SOUNDS 13 - -struct edict_s -{ - entity_state_t s; - struct gclient_s *client; // NULL if not a player - // the server expects the first part - // of gclient_s to be a player_state_t - // but the rest of it is opaque - - bool inuse; - int linkcount; - - // FIXME: move these fields to a server private sv_entity_t - link_t area; // linked to a division node or leaf - - int num_clusters; // if -1, use headnode instead - int clusternums[MAX_ENT_CLUSTERS]; - int headnode; // unused if num_clusters != -1 - int areanum, areanum2; - - //================================ - - int svflags; - vec3_t mins, maxs; - vec3_t absmin, absmax, size; - solid_t solid; - int clipmask; - edict_t *owner; - - - // DO NOT MODIFY ANYTHING ABOVE THIS, THE SERVER - // EXPECTS THE FIELDS IN THAT ORDER! - - //================================ - entity_id class_id; // Lazarus: Added in lieu of doing string comparisons - // on classnames. - - int movetype; - int flags; - - char *model; - float freetime; // sv.time when the object was freed - - // - // only used locally in game, not by server - // - char *message; - char *key_message; // Lazarus: used from tremor_trigger_key - char *classname; - int spawnflags; - - float timestamp; - - float angle; // set in qe3, -1 = up, -2 = down - char *target; - char *targetname; - char *killtarget; - char *team; - char *pathtarget; - char *deathtarget; - char *combattarget; - edict_t *target_ent; - - float speed, accel, decel; - vec3_t movedir; - vec3_t pos1, pos2; - - vec3_t velocity; - vec3_t avelocity; - int mass; - float air_finished; - float gravity; // per entity gravity multiplier (1.0 is normal) - // use for lowgrav artifact, flares - - edict_t *goalentity; - edict_t *movetarget; - float yaw_speed; - float ideal_yaw; - - char *common_name; - - // Lazarus: for rotating brush models: - float pitch_speed; - float roll_speed; - float ideal_pitch; - float ideal_roll; - float roll; - - float nextthink; - void (*prethink) (edict_t *ent); - void (*think)(edict_t *self); - void (*blocked)(edict_t *self, edict_t *other); //move to moveinfo? - void (*touch)(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf); - void (*use)(edict_t *self, edict_t *other, edict_t *activator); - void (*pain)(edict_t *self, edict_t *other, float kick, int damage); - void (*die)(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point); - - void (*play)(edict_t *self, edict_t *activator); - - float touch_debounce_time; // are all these legit? do we need more/less of them? - float pain_debounce_time; - float damage_debounce_time; - float gravity_debounce_time; // used by item_ movement commands to prevent - // monsters from dropping to floor - float fly_sound_debounce_time; //move to clientinfo - float last_move_time; - - int health; - int max_health; - int gib_health; - int deadflag; - bool show_hostile; - - // Lazarus: health2 and mass2 are passed from jorg to makron health and mass - int health2; - int mass2; - - float powerarmor_time; - - char *map; // target_changelevel - - int viewheight; // height above origin where eyesight is determined - int takedamage; - int dmg; - int radius_dmg; - float dmg_radius; - int sounds; //make this a spawntemp var? - int count; - - edict_t *chain; - edict_t *enemy; - edict_t *oldenemy; - edict_t *activator; - edict_t *groundentity; - int groundentity_linkcount; - edict_t *teamchain; - edict_t *teammaster; - - edict_t *mynoise; // can go in client only - edict_t *mynoise2; - - int noise_index; - int noise_index2; - float volume; - float attenuation; - - // timing variables - float wait; - float delay; // before firing targets - float random; - // Lazarus: laser timing - float starttime; - float endtime; - - float teleport_time; - - int watertype; - int waterlevel; - int old_watertype; - - vec3_t move_origin; - vec3_t move_angles; - - // move this to clientinfo? - int light_level; - - int style; // also used as areaportal number - - gitem_t *item; // for bonus items - - // common data blocks - moveinfo_t moveinfo; - monsterinfo_t monsterinfo; - - float goal_frame; - - // various Lazarus additions follow: - - //ed - for the sprite/model spawner - char *usermodel; - int startframe; - int framenumbers; - int solidstate; - // Lazarus: changed from rendereffect to renderfx and effects, and now uses - // real constants which can be combined. -// int rendereffect; - int renderfx; - int effects; - vec3_t bleft; - vec3_t tright; - - // item identification - char *datafile; - - // func_pushable - vec3_t oldvelocity; // Added for TREMOR to figure falling damage - vec3_t offset; // Added for TREMOR - offset from func_pushable to pusher - float density; - float bob; // bobbing in water amplitude - float duration; - int bobframe; - int bounce_me; // 0 for no bounce, 1 to bounce, 2 if velocity should not be clipped - // this is solely used by func_pushable for now - vec3_t origin_offset; // used to help locate brush models w/o origin brush - vec3_t org_mins,org_maxs; - vec3_t org_angles; - int org_movetype; - int axis; - - // crane - bool busy; - bool attracted; - int crane_increment; - int crane_dir; - edict_t *crane_control; - edict_t *crane_onboard_control; - edict_t *crane_beam; - edict_t *crane_hoist; - edict_t *crane_hook; - edict_t *crane_cargo; - edict_t *crane_cable; - edict_t *crane_light; - vec_t crane_bonk; - - edict_t *speaker; // moving speaker that eliminates the need - // for origin brushes with brush models - edict_t *vehicle; // generic drivable vehicle - char *idle_noise; - float radius; - vec3_t org_size; // Initial size of the vehicle bounding box, - - // monster AI - char *dmgteam; - - // spycam - edict_t *viewer; - - // monster power armor - int powerarmor; - - // MOVETYPE_PUSH rider angles - int turn_rider; - - // selected brush models will move their origin to - // the origin of this entity: - char *move_to; - - // newtargetname used ONLY by target_change and target_bmodel_spawner. - char *newtargetname; - - // source of target_clone's model - char *source; - - // if true, brush models will move directly to Move_Done - // at Move_Final rather than slowing down. - bool smooth_movement; - - int in_mud; - - int actor_sound_index[NUM_ACTOR_SOUNDS]; - int actor_gunframe; - int actor_current_weapon; // Index into weapon[] - int actor_weapon[2]; - int actor_model_index[2]; - float actor_crouch_time; - bool actor_id_model; - vec3_t muzzle; // Offset from origin to gun muzzle - vec3_t muzzle2; // Offset to left weapon (must have SF | 128) - - vec3_t color; // target_fade - float alpha; - float fadein; - float holdtime; - float fadeout; - - int owner_id; // These are used ONLY for ents that - int id; // change maps via trigger_transition - int last_attacked_framenum; // Used to turn off chicken mode - - // tracktrain - char *target2; - edict_t *prevpath; - - // spline train - edict_t *from; - edict_t *to; - - edict_t *next_grenade; // Used to build a list of active grenades - edict_t *prev_grenade; - - // gib type - specifies folder where gib models are found. - int gib_type; - int blood_type; - - int moreflags; - - // actor muzzle flash - edict_t *flash; -}; - -#define LOOKAT_NOBRUSHMODELS 1 -#define LOOKAT_NOWORLD 2 -#define LOOKAT_MD2 (LOOKAT_NOBRUSHMODELS | LOOKAT_NOWORLD) - -#define BeepBeep(ent) (gi.sound (ent, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0)) - -#define POWERUP_REPLACE_ENT 0 -#define POWERUP_NEW_ENT 1 -#define POWERUP_USE_ITEM 2 - -#define FLASHLIGHT_MOD -#define FLASHLIGHT_USE POWERUP_NEW_ENT -#define FLASHLIGHT_DRAIN 60 -#define FLASHLIGHT_ITEM "Cells" \ No newline at end of file diff --git a/server/g_main.c b/server/g_main.c deleted file mode 100644 index 8344e4d5..00000000 --- a/server/g_main.c +++ /dev/null @@ -1,485 +0,0 @@ - -#include "baseentity.h" - -game_locals_t game; -level_locals_t level; -game_import_t gi; -game_export_t globals; -spawn_temp_t st; - -byte *zone_level; -byte *zone_game; - -int sm_meat_index; -int snd_fry; -int meansOfDeath; - -edict_t *g_edicts; - -cvar_t *deathmatch; -cvar_t *coop; -cvar_t *dmflags; -cvar_t *skill; -cvar_t *fraglimit; -cvar_t *timelimit; -cvar_t *password; -cvar_t *spectator_password; -cvar_t *needpass; -cvar_t *maxclients; -cvar_t *maxspectators; -cvar_t *maxentities; -cvar_t *g_select_empty; -cvar_t *dedicated; - -cvar_t *filterban; - -cvar_t *sv_maxvelocity; -cvar_t *sv_gravity; - -cvar_t *sv_rollspeed; -cvar_t *sv_rollangle; -cvar_t *gun_x; -cvar_t *gun_y; -cvar_t *gun_z; - -cvar_t *run_pitch; -cvar_t *run_roll; -cvar_t *bob_up; -cvar_t *bob_pitch; -cvar_t *bob_roll; - -cvar_t *sv_cheats; - -cvar_t *flood_msgs; -cvar_t *flood_persecond; -cvar_t *flood_waitdelay; -cvar_t *r_motionblur; - -cvar_t *sv_maplist; - -cvar_t *actorchicken; -cvar_t *actorjump; -cvar_t *actorscram; -cvar_t *alert_sounds; -cvar_t *allow_download; -cvar_t *bounce_bounce; -cvar_t *bounce_minv; -cvar_t *cd_loopcount; -cvar_t *cl_gun; -cvar_t *corpse_fade; -cvar_t *corpse_fadetime; -cvar_t *crosshair; -cvar_t *developer; -cvar_t *footstep_sounds; -cvar_t *fov; -cvar_t *gl_clear; -cvar_t *gl_driver; -cvar_t *hand; -cvar_t *jetpack_weenie; -cvar_t *joy_pitchsensitivity; -cvar_t *joy_yawsensitivity; -cvar_t *jump_kick; -cvar_t *lazarus_cd_loop; -cvar_t *lazarus_cl_gun; -cvar_t *lazarus_crosshair; -cvar_t *lazarus_gl_clear; -cvar_t *lazarus_joyp; -cvar_t *lazarus_joyy; -cvar_t *lazarus_pitch; -cvar_t *lazarus_yaw; -cvar_t *m_pitch; -cvar_t *m_yaw; -cvar_t *monsterjump; -cvar_t *player_vampire; -cvar_t *readout; -cvar_t *rocket_strafe; -cvar_t *rotate_distance; -cvar_t *s_primary; -cvar_t *shift_distance; -cvar_t *sv_maxgibs; -cvar_t *turn_rider; -cvar_t *zoomrate; -cvar_t *zoomsnap; - -void SpawnEntities (char *mapname, char *entities, char *spawnpoint); -void ClientThink (edict_t *ent, usercmd_t *cmd); -bool ClientConnect (edict_t *ent, char *userinfo); -void ClientUserinfoChanged (edict_t *ent, char *userinfo); -void ClientDisconnect (edict_t *ent); -void ClientBegin (edict_t *ent); -void RunEntity (edict_t *ent); -void WriteLump (dsavehdr_t *hdr, file_t *f, int lumpnum, bool autosave); -void ReadLump (byte *base, lump_t *l, int lumpnum); -void InitGame (void); -void G_RunFrame (void); - -//=================================================================== - -void ShutdownGame (void) -{ - gi.dprintf ("==== ShutdownGame ====\n"); - if(!deathmatch->value && !coop->value) - { - gi.cvar_forceset("m_pitch", va("%f",lazarus_pitch->value)); - gi.cvar_forceset("cd_loopcount", va("%d",lazarus_cd_loop->value)); - gi.cvar_forceset("gl_clear", va("%d", lazarus_gl_clear->value)); - } - - //free main memory pools - Mem_FreePool(&zone_level); - Mem_FreePool(&zone_game); -} - - -game_import_t RealFunc; -int max_modelindex; -int max_soundindex; - - -int Debug_Modelindex (char *name) -{ - int modelnum; - modelnum = RealFunc.modelindex(name); - if(modelnum > max_modelindex) - { - gi.dprintf("Model %03d %s\n",modelnum,name); - max_modelindex = modelnum; - } - return modelnum; -} - -int Debug_Soundindex (char *name) -{ - int soundnum; - soundnum = RealFunc.soundindex(name); - if(soundnum > max_soundindex) - { - gi.dprintf("Sound %03d %s\n",soundnum,name); - max_soundindex = soundnum; - } - return soundnum; -} - -/* -================= -GetGameAPI - -Returns a pointer to the structure with all entry points -and global variables -================= -*/ -game_export_t DLLEXPORT *ServerAPI (game_import_t *import) -{ - gi = *import; - - globals.apiversion = GAME_API_VERSION; - globals.Init = InitGame; - globals.Shutdown = ShutdownGame; - globals.SpawnEntities = SpawnEntities; - - globals.WriteLump = WriteLump; - globals.ReadLump = ReadLump; - - globals.ClientThink = ClientThink; - globals.ClientConnect = ClientConnect; - globals.ClientUserinfoChanged = ClientUserinfoChanged; - globals.ClientDisconnect = ClientDisconnect; - globals.ClientBegin = ClientBegin; - globals.ClientCommand = ClientCommand; - - globals.RunFrame = G_RunFrame; - - globals.ServerCommand = ServerCommand; - - globals.edict_size = sizeof(edict_t); - - gl_driver = gi.cvar ("gl_driver", "", 0); - - developer = gi.cvar("developer", "0", CVAR_SERVERINFO); - readout = gi.cvar("readout", "0", CVAR_SERVERINFO); - if(readout->value) - { - max_modelindex = 0; - max_soundindex = 0; - RealFunc.modelindex = gi.modelindex; - gi.modelindex = Debug_Modelindex; - RealFunc.soundindex = gi.soundindex; - gi.soundindex = Debug_Soundindex; - } - - return &globals; -} - -/* -================= -ClientEndServerFrames -================= -*/ -void ClientEndServerFrames (void) -{ - int i; - edict_t *ent; - - // calc the player views now that all pushing - // and damage has been added - for (i=0 ; ivalue ; i++) - { - ent = g_edicts + 1 + i; - if (!ent->inuse || !ent->client) - continue; - ClientEndServerFrame (ent); - } -} - -/* -================= -CreateTargetChangeLevel - -Returns the created target changelevel -================= -*/ -edict_t *CreateTargetChangeLevel(char *map) -{ - edict_t *ent; - - ent = G_Spawn (); - ent->classname = "target_changelevel"; - sprintf(level.nextmap, "%s", map); - ent->map = level.nextmap; - return ent; -} - -/* -================= -EndDMLevel - -The timelimit or fraglimit has been exceeded -================= -*/ -void EndDMLevel (void) -{ - edict_t *ent; - char *s, *t, *f; - static const char *seps = " ,\n\r"; - - // stay on same level flag - if ((int)dmflags->value & DF_SAME_LEVEL) - { - BeginIntermission (CreateTargetChangeLevel (level.mapname) ); - return; - } - - // see if it's in the map list - if (*sv_maplist->string) { - s = strdup(sv_maplist->string); - f = NULL; - t = strtok(s, seps); - while (t != NULL) { - if (strcasecmp(t, level.mapname) == 0) { - // it's in the list, go to the next one - t = strtok(NULL, seps); - if (t == NULL) { // end of list, go to first one - if (f == NULL) // there isn't a first one, same level - BeginIntermission (CreateTargetChangeLevel (level.mapname) ); - else - BeginIntermission (CreateTargetChangeLevel (f) ); - } else - BeginIntermission (CreateTargetChangeLevel (t) ); - free(s); - return; - } - if (!f) - f = t; - t = strtok(NULL, seps); - } - free(s); - } - - if (level.nextmap[0]) // go to a specific map - BeginIntermission (CreateTargetChangeLevel (level.nextmap) ); - else { // search for a changelevel - ent = G_Find (NULL, FOFS(classname), "target_changelevel"); - if (!ent) - { // the map designer didn't include a changelevel, - // so create a fake ent that goes back to the same level - BeginIntermission (CreateTargetChangeLevel (level.mapname) ); - return; - } - BeginIntermission (ent); - } -} - - -/* -================= -CheckNeedPass -================= -*/ -void CheckNeedPass (void) -{ - int need; - - // if password or spectator_password has changed, update needpass - // as needed - if (password->modified || spectator_password->modified) - { - password->modified = spectator_password->modified = false; - - need = 0; - - if (*password->string && strcasecmp(password->string, "none")) - need |= 1; - if (*spectator_password->string && strcasecmp(spectator_password->string, "none")) - need |= 2; - - gi.cvar_set("needpass", va("%d", need)); - } -} - -/* -================= -CheckDMRules -================= -*/ -void CheckDMRules (void) -{ - int i; - gclient_t *cl; - - if (level.intermissiontime) - return; - - if (!deathmatch->value) - return; - - if (timelimit->value) - { - if (level.time >= timelimit->value*60) - { - gi.bprintf (PRINT_HIGH, "Timelimit hit.\n"); - EndDMLevel (); - return; - } - } - - if (fraglimit->value) - { - for (i=0 ; ivalue ; i++) - { - cl = game.clients + i; - if (!g_edicts[i+1].inuse) - continue; - - if (cl->resp.score >= fraglimit->value) - { - gi.bprintf (PRINT_HIGH, "Fraglimit hit.\n"); - EndDMLevel (); - return; - } - } - } -} - - -/* -============= -ExitLevel -============= -*/ -void ExitLevel (void) -{ - int i; - edict_t *ent; - char command [256]; - - sprintf (command, "gamemap \"%s\"\n", level.changemap); - gi.AddCommandString (command); - level.changemap = NULL; - level.exitintermission = 0; - level.intermissiontime = 0; - ClientEndServerFrames (); - - // clear some things before going to next level - for (i=0 ; ivalue ; i++) - { - ent = g_edicts + 1 + i; - if (!ent->inuse) - continue; - if (ent->health > ent->client->pers.max_health) - ent->health = ent->client->pers.max_health; - } - -} - -/* -================ -G_RunFrame - -Advances the world by 0.1 seconds -================ -*/ -void G_RunFrame (void) -{ - int i; - edict_t *ent; - - if(level.freeze) - { - level.freezeframes++; - if(level.freezeframes >= 300) - level.freeze = false; - } else - level.framenum++; - - level.time = level.framenum*FRAMETIME; - - // exit intermissions - if (level.exitintermission) - { - ExitLevel (); - return; - } - - // - // treat each object in turn - // even the world gets a chance to think - // - ent = &g_edicts[0]; - for (i=0 ; iinuse) - continue; - - level.current_entity = ent; - - VectorCopy (ent->s.origin, ent->s.old_origin); - - // if the ground entity moved, make sure we are still on it - if ((ent->groundentity) && (ent->groundentity->linkcount != ent->groundentity_linkcount)) - { - ent->groundentity = NULL; - if ( !(ent->flags & (FL_SWIM|FL_FLY)) && (ent->svflags & SVF_MONSTER) ) - { - M_CheckGround (ent); - } - } - - if (i > 0 && i <= maxclients->value) - { - ClientBeginServerFrame (ent); - continue; - } - - G_RunEntity (ent); - } - - // see if it is time to end a deathmatch - CheckDMRules (); - - // see if needpass needs updated - CheckNeedPass (); - - // build the playerstate_t structures for all players - ClientEndServerFrames (); -} - diff --git a/server/g_misc.c b/server/g_misc.c deleted file mode 100644 index 1885fe8c..00000000 --- a/server/g_misc.c +++ /dev/null @@ -1,3237 +0,0 @@ -// g_misc.c - -#include "baseentity.h" - -int gibsthisframe=0; -int lastgibframe=0; - -#define GIB_METAL 1 -#define GIB_GLASS 2 -#define GIB_BARREL 3 -#define GIB_CRATE 4 -#define GIB_ROCK 5 -#define GIB_MECH 6 -#define GIB_WOOD 7 -#define GIB_TECH 8 - -void FadeThink(edict_t *ent) -{ - ent->count++; - if (ent->count == 2) - { - G_FreeEdict(ent); - return; - } - ent->nextthink=level.time + 0.5; - gi.linkentity(ent); -} -void FadeDieThink (edict_t *ent) -{ - ent->s.renderfx = RF_TRANSLUCENT; - ent->think = FadeThink; - ent->nextthink = level.time+0.5; - ent->count = 0; - gi.linkentity(ent); -} - -/*QUAKED func_group (0 0 0) ? -Used to group brushes together just for editor convenience. -*/ - -//===================================================== - -void Use_Areaportal (edict_t *ent, edict_t *other, edict_t *activator) -{ - ent->count ^= 1; // toggle state -// gi.dprintf ("portalstate: %i = %i\n", ent->style, ent->count); - gi.SetAreaPortalState (ent->style, ent->count); -} - -/*QUAKED func_areaportal (0 0 0) ? - -This is a non-visible object that divides the world into -areas that are seperated when this portal is not activated. -Usually enclosed in the middle of a door. -*/ -void SP_func_areaportal (edict_t *ent) -{ - ent->class_id = ENTITY_FUNC_AREAPORTAL; - ent->use = Use_Areaportal; - ent->count = 0; // always start closed; -} - -//===================================================== - - -/* -================= -Misc functions -================= -*/ -void VelocityForDamage (int damage, vec3_t v) -{ - v[0] = 100.0 * crandom(); - v[1] = 100.0 * crandom(); - v[2] = 200.0 + 100.0 * random(); - - if (damage < 50) - VectorScale (v, 0.7, v); - else - VectorScale (v, 1.2, v); -} - -void ClipGibVelocity (edict_t *ent) -{ - if (ent->velocity[0] < -300) - ent->velocity[0] = -300; - else if (ent->velocity[0] > 300) - ent->velocity[0] = 300; - if (ent->velocity[1] < -300) - ent->velocity[1] = -300; - else if (ent->velocity[1] > 300) - ent->velocity[1] = 300; - if (ent->velocity[2] < 200) - ent->velocity[2] = 200; // always some upwards - else if (ent->velocity[2] > 500) - ent->velocity[2] = 500; -} - - -/* -================= -gibs -================= -*/ -void gib_think (edict_t *self) -{ - self->s.frame++; - self->nextthink = level.time + FRAMETIME; - - if (self->s.frame == 10) - { - self->think = G_FreeEdict; - self->nextthink = level.time + 8 + random()*10; - } -} - -void gib_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - vec3_t normal_angles, right; - - if (!self->groundentity) - return; - - self->touch = NULL; - - if (plane) - { - gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/fhit3.wav"), 1, ATTN_NORM, 0); - - vectoangles (plane->normal, normal_angles); - AngleVectors (normal_angles, NULL, right, NULL); - vectoangles (right, self->s.angles); - - if (self->s.modelindex == sm_meat_index) - { - self->s.frame++; - self->think = gib_think; - self->nextthink = level.time + FRAMETIME; - } - } -} - -void gib_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) -{ - G_FreeEdict (self); -} - -void ThrowGib (edict_t *self, char *gibname, int damage, int type) -{ - edict_t *gib; - vec3_t vd; - vec3_t origin; - vec3_t size; - float vscale; - char modelname[256]; - char *p; - - // Lazarus: Prevent gib showers (generally due to firing BFG in a crowd) from - // causing SZ_GetSpace: overflow - if(level.framenum > lastgibframe) - { - gibsthisframe = 0; - lastgibframe = level.framenum; - } - gibsthisframe++; - if(gibsthisframe > sv_maxgibs->value) - return; - - gib = G_Spawn(); - - gib->classname = (char *)TagMalloc (4,TAG_LEVEL); - strcpy(gib->classname,"gib"); - - // Lazarus: mapper-definable gib class - strcpy(modelname,gibname); - p = strstr(modelname,"models/objects/gibs/"); - if(p && self->gib_type) - { - p += 18; - p[0] = (char)((int)'0' + (self->gib_type % 10)); - } - - // Save gibname and type for level transition gibs - gib->key_message = (char *)TagMalloc (strlen(modelname)+1,TAG_LEVEL); - strcpy(gib->key_message, modelname); - gib->style = type; - - VectorScale (self->size, 0.5, size); - VectorAdd (self->absmin, size, origin); - gib->s.origin[0] = origin[0] + crandom() * size[0]; - gib->s.origin[1] = origin[1] + crandom() * size[1]; - gib->s.origin[2] = origin[2] + crandom() * size[2]; - - gi.setmodel (gib, modelname); - gib->solid = SOLID_NOT; - if(self->blood_type == 1) - gib->s.effects |= EF_GREENGIB; - else if(self->blood_type == 2) - gib->s.effects |= EF_GRENADE; - else - gib->s.effects |= EF_GIB; - gib->flags |= FL_NO_KNOCKBACK; - gib->takedamage = DAMAGE_YES; - gib->die = gib_die; - - if (type == GIB_ORGANIC) - { - gib->movetype = MOVETYPE_TOSS; - gib->touch = gib_touch; - vscale = 0.5; - } - else - { - gib->movetype = MOVETYPE_BOUNCE; - vscale = 1.0; - } - - VelocityForDamage (damage, vd); - VectorMA (self->velocity, vscale, vd, gib->velocity); - ClipGibVelocity (gib); - gib->avelocity[0] = random()*600; - gib->avelocity[1] = random()*600; - gib->avelocity[2] = random()*600; - - gib->think = FadeDieThink; - gib->nextthink = level.time + 8 + random()*10; - - gi.linkentity (gib); -} - -// NOTE: SP_gib is ONLY intended to be used for gibs that change maps -// via trigger_transition. It should NOT be used for map entities - -void gib_delayed_start (edict_t *gib) -{ - if(g_edicts[1].linkcount) - { - if(gib->count > 0) - { - gib->think = FadeThink; - gib->nextthink = level.time + FRAMETIME; - } - else - { - gib->think = FadeDieThink; - gib->nextthink = level.time + 8 + random()*10; - } - } - else - { - gib->nextthink = level.time + FRAMETIME; - } -} - -void SP_gib (edict_t *gib) -{ - gib->class_id = ENTITY_GIB; - if(gib->key_message) - gi.setmodel (gib, gib->key_message); - else - gi.setmodel (gib, "models/objects/gibs/sm_meat/tris.md2"); - gib->die = gib_die; - if (gib->style == GIB_ORGANIC) - gib->touch = gib_touch; - gib->think = gib_delayed_start; - gib->nextthink = level.time + FRAMETIME; - gi.linkentity (gib); -} - -void ThrowHead (edict_t *self, char *gibname, int damage, int type) -{ - vec3_t vd; - float vscale; - char modelname[256]; - char *p; - - self->s.skin = 0; - self->s.frame = 0; - VectorClear (self->mins); - VectorClear (self->maxs); - - strcpy(modelname,gibname); - p = strstr(modelname,"models/objects/gibs/"); - if(p && self->gib_type) - { - p += 18; - p[0] = (char)((int)'0' + (self->gib_type % 10)); - } - - // Save gibname and type for level transition gibs - self->key_message = (char *)TagMalloc (strlen(modelname)+1,TAG_LEVEL); - strcpy(self->key_message, modelname); - - self->style = type; - self->s.weaponmodel = 0; - gi.setmodel (self, modelname); - self->solid = SOLID_NOT; - if(self->blood_type == 1) - self->s.effects |= EF_GREENGIB; - else if(self->blood_type == 2) - self->s.effects |= EF_GRENADE; - else - self->s.effects |= EF_GIB; - self->s.effects &= ~EF_FLIES; - self->s.sound = 0; - self->flags |= FL_NO_KNOCKBACK; - self->svflags &= ~SVF_MONSTER; - self->takedamage = DAMAGE_YES; - // Lazarus: Disassociate this head with its monster - self->targetname = NULL; - self->die = gib_die; - self->dmgteam = NULL; // Prevent gibs from becoming angry if their buddies are hurt - - if (type == GIB_ORGANIC) - { - self->movetype = MOVETYPE_TOSS; - self->touch = gib_touch; - vscale = 0.5; - } - else - { - self->movetype = MOVETYPE_BOUNCE; - vscale = 1.0; - } - - VelocityForDamage (damage, vd); - VectorMA (self->velocity, vscale, vd, self->velocity); - ClipGibVelocity (self); - - self->avelocity[YAW] = crandom()*600; - - self->think = FadeDieThink; - self->nextthink = level.time + 8 + random()*10; - - gi.linkentity (self); -} - -void SP_gibhead (edict_t *gib) -{ - gib->class_id = ENTITY_GIBHEAD; - if(gib->key_message) - gi.setmodel (gib, gib->key_message); - else - gi.setmodel (gib, "models/objects/gibs/head2/tris.md2"); - - if (gib->style == GIB_ORGANIC) - gib->touch = gib_touch; - - gib->think = gib_delayed_start; - gib->nextthink = level.time + FRAMETIME; - gi.linkentity (gib); - -} - -void ThrowClientHead (edict_t *self, int damage) -{ - vec3_t vd; - char *gibname; - - if (rand()&1) - { - gibname = "models/objects/gibs/head2/tris.md2"; - self->s.skin = 1; // second skin is player - } - else - { - gibname = "models/objects/gibs/skull/tris.md2"; - self->s.skin = 0; - } - - self->s.origin[2] += 32; - self->s.frame = 0; - gi.setmodel (self, gibname); - VectorSet (self->mins, -16, -16, 0); - VectorSet (self->maxs, 16, 16, 16); - - self->takedamage = DAMAGE_NO; - self->solid = SOLID_NOT; - self->s.effects = EF_GIB; - self->s.sound = 0; - self->flags |= FL_NO_KNOCKBACK; - - self->movetype = MOVETYPE_BOUNCE; - VelocityForDamage (damage, vd); - VectorAdd (self->velocity, vd, self->velocity); - - if (self->client) // bodies in the queue don't have a client anymore - { - self->client->anim_priority = ANIM_DEATH; - self->client->anim_end = self->s.frame; - } - else - { - self->think = NULL; - self->nextthink = 0; - } - - gi.linkentity (self); -} - - -/* -================= -debris -================= -*/ -void debris_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) -{ - G_FreeEdict (self); -} - -void ThrowDebris (edict_t *self, char *modelname, float speed, vec3_t origin, int body, int effects) -{ - edict_t *chunk; - vec3_t v; - - chunk = G_Spawn(); - VectorCopy (origin, chunk->s.origin); - gi.setmodel (chunk, modelname); - v[0] = 100 * crandom(); - v[1] = 100 * crandom(); - v[2] = 100 + 100 * crandom(); - VectorMA (self->velocity, speed, v, chunk->velocity); - chunk->movetype = MOVETYPE_BOUNCE; - chunk->solid = SOLID_NOT; - chunk->avelocity[0] = random()*600; - chunk->avelocity[1] = random()*600; - chunk->avelocity[2] = random()*600; - chunk->think = G_FreeEdict; - chunk->nextthink = level.time + 5 + random()*5; - chunk->s.frame = 0; - chunk->flags = 0; - chunk->classname = "debris"; - chunk->takedamage = DAMAGE_YES; - chunk->die = debris_die; - - // Lazarus: Preserve model name for level changes: - chunk->message = (char *)TagMalloc (strlen(modelname) + 1,TAG_LEVEL); - strcpy(chunk->message, modelname); - - // Lazarus: skin number and effects - chunk->s.body = body; - chunk->s.effects |= effects; - - gi.linkentity (chunk); -} - -// NOTE: SP_debris is ONLY intended to be used for debris chunks that change maps -// via trigger_transition. It should NOT be used for map entities - -void debris_delayed_start (edict_t *debris) -{ - if(g_edicts[1].linkcount) - { - debris->think = G_FreeEdict; - debris->nextthink = level.time + 5 + random()*5; - } - else - { - debris->nextthink = level.time + FRAMETIME; - } -} - -void SP_debris (edict_t *debris) -{ - debris->class_id = ENTITY_DEBRIS; - if(debris->message) - gi.setmodel (debris, debris->message); - else - gi.setmodel (debris, "models/objects/debris2/tris.md2"); - debris->think = debris_delayed_start; - debris->nextthink = level.time + FRAMETIME; - debris->die = debris_die; - gi.linkentity (debris); -} - -void BecomeExplosion1 (edict_t *self) -{ - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_EXPLOSION1); - WRITE_COORD (self->s.origin); - MESSAGE_SEND (MSG_PVS, self->s.origin, NULL); - - G_FreeEdict (self); -} - - -void BecomeExplosion2 (edict_t *self) -{ - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_EXPLOSION2); - WRITE_COORD (self->s.origin); - MESSAGE_SEND (MSG_PVS, self->s.origin, NULL); - - G_FreeEdict (self); -} - - -/*QUAKED path_corner (.5 .3 0) (-8 -8 -8) (8 8 8) TELEPORT -Target: next path corner -Pathtarget: gets used when an entity that has - this path_corner targeted touches it -*/ - -void path_corner_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - vec3_t v; - edict_t *next; - - if (other->movetarget != self) - return; - - if (other->enemy) - return; - - if (self->pathtarget) - { - char *savetarget; - - savetarget = self->target; - self->target = self->pathtarget; - G_UseTargets (self, other); - self->target = savetarget; - } - - if (self->target) - next = G_PickTarget(self->target); - else - next = NULL; - - // Lazarus: Distinguish between path_corner and target_actor, or target_actor - // with JUMP spawnflag will result in teleport. Ack! - if ((next) && (next->spawnflags & 1) && !strcasecmp(next->classname,"path_corner")) - { - VectorCopy (next->s.origin, v); - v[2] += next->mins[2]; - v[2] -= other->mins[2]; - VectorCopy (v, other->s.origin); - next = G_PickTarget(next->target); - other->s.event = EV_OTHER_TELEPORT; - } - - other->goalentity = other->movetarget = next; - - if (self->wait > 0) - { - other->monsterinfo.pausetime = level.time + self->wait; - other->monsterinfo.stand (other); - } - else if (self->wait < 0) - { - other->monsterinfo.pausetime = level.time + 100000000; - other->monsterinfo.stand (other); - } - else - { - - if (!other->movetarget) - { - other->monsterinfo.pausetime = level.time + 100000000; - other->monsterinfo.stand (other); - } - else - { - VectorSubtract (other->goalentity->s.origin, other->s.origin, v); - other->ideal_yaw = vectoyaw (v); - } - } - - self->count--; - if(!self->count) { - self->think = G_FreeEdict; - self->nextthink = level.time + 1; - } - -} - -void SP_path_corner (edict_t *self) -{ - if (!self->targetname) - { - gi.dprintf ("path_corner with no targetname at %s\n", vtos(self->s.origin)); - G_FreeEdict (self); - return; - } - self->class_id = ENTITY_PATH_CORNER; - self->solid = SOLID_TRIGGER; - self->touch = path_corner_touch; - VectorSet (self->mins, -8, -8, -8); - VectorSet (self->maxs, 8, 8, 8); - self->svflags |= SVF_NOCLIENT; - if(!self->count) self->count = -1; - gi.linkentity (self); -} - - -/*QUAKED point_combat (0.5 0.3 0) (-8 -8 -8) (8 8 8) Hold -Makes this the target of a monster and it will head here -when first activated before going after the activator. If -hold is selected, it will stay here. -*/ -void point_combat_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - edict_t *activator; - - if (other->movetarget != self) - return; - - self->count--; - if(!self->count) { - self->think = G_FreeEdict; - self->nextthink = level.time + self->delay + 1; - } - - if (self->target) - { - other->target = self->target; - other->goalentity = other->movetarget = G_PickTarget(other->target); - if (!other->goalentity) - { - gi.dprintf("%s at %s target %s does not exist\n", self->classname, vtos(self->s.origin), self->target); - other->movetarget = self; - } - // Lazarus: NO NO NO! This makes the pc's target accessible once and only once - //self->target = NULL; - } - else if ((self->spawnflags & 1) && !(other->flags & (FL_SWIM|FL_FLY))) - { - other->monsterinfo.pausetime = level.time + 100000000; - other->monsterinfo.aiflags |= AI_STAND_GROUND; - other->monsterinfo.stand (other); - } - - if (other->movetarget == self) - { - other->target = NULL; - other->movetarget = NULL; - other->goalentity = other->enemy; - other->monsterinfo.aiflags &= ~AI_COMBAT_POINT; - } - - if (self->pathtarget) - { - char *savetarget; - - savetarget = self->target; - self->target = self->pathtarget; - if (other->enemy && other->enemy->client) - activator = other->enemy; - else if (other->oldenemy && other->oldenemy->client) - activator = other->oldenemy; - else if (other->activator && other->activator->client) - activator = other->activator; - else - activator = other; - G_UseTargets (self, activator); - self->target = savetarget; - } -} - -void SP_point_combat (edict_t *self) -{ - if (deathmatch->value) - { - G_FreeEdict (self); - return; - } - self->class_id = ENTITY_POINT_COMBAT; - self->solid = SOLID_TRIGGER; - self->touch = point_combat_touch; - VectorSet (self->mins, -8, -8, -16); - VectorSet (self->maxs, 8, 8, 16); - self->svflags = SVF_NOCLIENT; - - // Point_combat usage count is handled a bit differently than other - // entities. Since the standard Q2 point_combat can only be used once, - // the default behavior should be to delete the point_combat after - // one use. - if(!self->count) self->count = 1; - gi.linkentity (self); -}; - - -/*QUAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8) -Just for the debugging level. Don't use -*/ -void TH_viewthing(edict_t *ent) -{ - ent->s.frame = (ent->s.frame + 1) % 7; - ent->nextthink = level.time + FRAMETIME; -} - -void SP_viewthing(edict_t *ent) -{ - ent->class_id = ENTITY_VIEWTHING; - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_BBOX; - ent->s.renderfx = RF_FRAMELERP; - VectorSet (ent->mins, -16, -16, -24); - VectorSet (ent->maxs, 16, 16, 32); - ent->s.modelindex = gi.modelindex ("models/objects/banner/tris.md2"); - gi.linkentity (ent); - ent->nextthink = level.time + 0.5; - ent->think = TH_viewthing; - return; -} - - -/*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4) -Used as a positional target for spotlights, etc. -*/ -void SP_info_null (edict_t *self) -{ - G_FreeEdict (self); -}; - - -/*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4) -Used as a positional target for lightning. -*/ -void SP_info_notnull (edict_t *self) -{ - self->class_id = ENTITY_INFO_NOTNULL; - VectorCopy (self->s.origin, self->absmin); - VectorCopy (self->s.origin, self->absmax); -}; - - -/*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF -Non-displayed light. -Default light value is 300. -Default style is 0. -If targeted, will toggle between on and off. -Default _cone value is 10 (used to set size of light for spotlights) -*/ - -#define START_OFF 1 - -static void light_use (edict_t *self, edict_t *other, edict_t *activator) -{ - if (self->spawnflags & START_OFF) - { - gi.configstring (CS_LIGHTS+self->style, "m"); - self->spawnflags &= ~START_OFF; - } - else - { - gi.configstring (CS_LIGHTS+self->style, "a"); - self->spawnflags |= START_OFF; - - self->count--; - if(!self->count) { - self->think = G_FreeEdict; - self->nextthink = level.time + 1; - } - } - -} - -void SP_light (edict_t *self) -{ - // no targeted lights in deathmatch, because they cause global messages - if (!self->targetname || deathmatch->value) - { - G_FreeEdict (self); - return; - } - self->class_id = ENTITY_LIGHT; - if (self->style >= 32) - { - self->use = light_use; - if (self->spawnflags & START_OFF) - gi.configstring (CS_LIGHTS+self->style, "a"); - else - gi.configstring (CS_LIGHTS+self->style, "m"); - } -} - - -/*QUAKED func_wall (0 .5 .8) ? TRIGGER_SPAWN TOGGLE START_ON ANIMATED ANIMATED_FAST -This is just a solid wall if not inhibited - -TRIGGER_SPAWN the wall will not be present until triggered - it will then blink in to existance; it will - kill anything that was in it's way - -TOGGLE only valid for TRIGGER_SPAWN walls - this allows the wall to be turned on and off - -START_ON only valid for TRIGGER_SPAWN walls - the wall will initially be present -*/ - -void func_wall_use (edict_t *self, edict_t *other, edict_t *activator) -{ - if (self->solid == SOLID_NOT) - { - self->solid = SOLID_BSP; - self->svflags &= ~SVF_NOCLIENT; - KillBox (self); - } - else - { - self->solid = SOLID_NOT; - self->svflags |= SVF_NOCLIENT; - - self->count--; - if(!self->count) { - self->think = G_FreeEdict; - self->nextthink = level.time + 1; - return; - } - } - gi.linkentity (self); - - if (!(self->spawnflags & 2)) - self->use = NULL; -} - -void SP_func_wall (edict_t *self) -{ - self->class_id = ENTITY_FUNC_WALL; - self->movetype = MOVETYPE_PUSH; - gi.setmodel (self, self->model); - - if (self->spawnflags & 8) - self->s.effects |= EF_ANIM_ALL; - if (self->spawnflags & 16) - self->s.effects |= EF_ANIM_ALLFAST; - - // just a wall - if ((self->spawnflags & 7) == 0) - { - self->solid = SOLID_BSP; - gi.linkentity (self); - return; - } - - // it must be TRIGGER_SPAWN - if (!(self->spawnflags & 1)) - { -// gi.dprintf("func_wall missing TRIGGER_SPAWN\n"); - self->spawnflags |= 1; - } - - // yell if the spawnflags are odd - if (self->spawnflags & 4) - { - if (!(self->spawnflags & 2)) - { -// Lazarus: no point to this: -// gi.dprintf("func_wall START_ON without TOGGLE\n"); - self->spawnflags |= 2; - } - } - - self->use = func_wall_use; - if (self->spawnflags & 4) - { - self->solid = SOLID_BSP; - } - else - { - self->solid = SOLID_NOT; - self->svflags |= SVF_NOCLIENT; - } - gi.linkentity (self); -} - - -/*QUAKED func_object (0 .5 .8) ? TRIGGER_SPAWN ANIMATED ANIMATED_FAST -This is solid bmodel that will fall if it's support it removed. -*/ - -void func_object_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - // only squash thing we fall on top of - if (!plane) - return; - if (plane->normal[2] < 1.0) - return; - if (other->takedamage == DAMAGE_NO) - return; - T_Damage (other, self, self, vec3_origin, self->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH); -} - -void func_object_release (edict_t *self) -{ - self->movetype = MOVETYPE_TOSS; - self->touch = func_object_touch; -} - -void func_object_use (edict_t *self, edict_t *other, edict_t *activator) -{ - self->solid = SOLID_BSP; - self->svflags &= ~SVF_NOCLIENT; - self->use = NULL; - KillBox (self); - func_object_release (self); -} - -void SP_func_object (edict_t *self) -{ - self->class_id = ENTITY_FUNC_OBJECT; - gi.setmodel (self, self->model); - - self->mins[0] += 1; - self->mins[1] += 1; - self->mins[2] += 1; - self->maxs[0] -= 1; - self->maxs[1] -= 1; - self->maxs[2] -= 1; - - if (!self->dmg) - self->dmg = 100; - - if (self->spawnflags == 0) - { - self->solid = SOLID_BSP; - self->movetype = MOVETYPE_PUSH; - self->think = func_object_release; - self->nextthink = level.time + 2 * FRAMETIME; - } - else - { - self->solid = SOLID_NOT; - self->movetype = MOVETYPE_PUSH; - self->use = func_object_use; - self->svflags |= SVF_NOCLIENT; - } - - if (self->spawnflags & 2) - self->s.effects |= EF_ANIM_ALL; - if (self->spawnflags & 4) - self->s.effects |= EF_ANIM_ALLFAST; - - self->clipmask = MASK_MONSTERSOLID; - - gi.linkentity (self); -} - - -/*QUAKED func_explosive (0 .5 .8) ? Trigger_Spawn ANIMATED ANIMATED_FAST -Any brush that you want to explode or break apart. If you want an -ex0plosion, set dmg and it will do a radius explosion of that amount -at the center of the bursh. - -If targeted it will not be shootable. - -health defaults to 100. - -mass defaults to 75. This determines how much debris is emitted when -it explodes. You get one large chunk per 100 of mass (up to 8) and -one small chunk per 25 of mass (up to 16). So 800 gives the most. - -Lazarus: -SF 8 EXPLOSIVES_ONLY Func_explosive may ONLY be damaged by explosions - -*/ -void func_explosive_use(edict_t *self, edict_t *other, edict_t *activator); -void func_explosive_respawn(edict_t *self) -{ - KillBox(self); - self->solid = SOLID_BSP; - self->svflags &= ~SVF_NOCLIENT; - self->health = self->max_health; - self->takedamage = DAMAGE_YES; - self->use = func_explosive_use; - gi.linkentity(self); -} - -void func_explosive_explode (edict_t *self) -{ - vec3_t origin; - vec3_t chunkorigin; - vec3_t size; - int count; - int mass; - - // bmodel origins are (0 0 0), we need to adjust that here - VectorScale (self->size, 0.5, size); - VectorAdd (self->absmin, size, origin); - VectorCopy (origin, self->s.origin); - - self->takedamage = DAMAGE_NO; - - if (self->dmg) - T_RadiusDamage (self, self->activator, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE, -0.5); - - VectorSubtract (self->s.origin, self->enemy->s.origin, self->velocity); - VectorNormalize (self->velocity); - VectorScale (self->velocity, 150, self->velocity); - - // start chunks towards the center - VectorScale (size, 0.5, size); - - mass = self->mass; - if (!mass) mass = 75; - - // Lazarus: Use traditional debris for gib_type=0, but non-zero gib_type gives equal - // weight to all models. - - if( (self->gib_type > 0) && (self->gib_type < 10)) - { - int r; - - count = mass/25; - if(count > 16) count = 16; - while(count--) - { - r = (rand() % 5) + 1; - chunkorigin[0] = origin[0] + crandom() * size[0]; - chunkorigin[1] = origin[1] + crandom() * size[1]; - chunkorigin[2] = origin[2] + crandom() * size[2]; - switch(self->gib_type) - { - case GIB_METAL: ThrowDebris (self, "models/gibs/metal.mdl", 2, chunkorigin, r, 0); break; - case GIB_GLASS: ThrowDebris (self, "models/gibs/glass.mdl", 2, chunkorigin, r, 0); break; - case GIB_BARREL: ThrowDebris (self, "models/gibs/barrelgib.mdl", 2, chunkorigin, r, 0); break; - case GIB_CRATE: ThrowDebris (self, "models/gibs/wood.mdl", 2, chunkorigin, r, 0); break; - case GIB_ROCK: ThrowDebris (self, "models/gibs/rock.mdl", 2, chunkorigin, r, 0); break; - case GIB_MECH: ThrowDebris (self, "models/gibs/flesh.mdl", 2, chunkorigin, r, 0); break; - case GIB_WOOD: ThrowDebris (self, "models/gibs/wood.mdl", 2, chunkorigin, r, 0); break; - case GIB_TECH: ThrowDebris (self, "models/gibs/computer.mdl", 2, chunkorigin, r, 0); break; - } - } - } - else - { - // big chunks - if (mass >= 100) - { - count = mass / 100; - if (count > 8) - count = 8; - while(count--) - { - chunkorigin[0] = origin[0] + crandom() * size[0]; - chunkorigin[1] = origin[1] + crandom() * size[1]; - chunkorigin[2] = origin[2] + crandom() * size[2]; - ThrowDebris (self, "models/objects/debris1/tris.md2", 1, chunkorigin, 0, 0); - } - } - - // small chunks - count = mass / 25; - if (count > 16) - count = 16; - while(count--) - { - chunkorigin[0] = origin[0] + crandom() * size[0]; - chunkorigin[1] = origin[1] + crandom() * size[1]; - chunkorigin[2] = origin[2] + crandom() * size[2]; - ThrowDebris (self, "models/objects/debris2/tris.md2", 2, chunkorigin, 0, 0); - } - } - - G_UseTargets (self, self->activator); - -// Respawnable func_explosive. If desired, replace these lines.... - - if (self->dmg) - BecomeExplosion1 (self); - else - G_FreeEdict (self); - -/* with these: - - if (self->dmg) - { - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_EXPLOSION1); - WRITE_COORD (self->s.origin); - MESSAGE_SEND (MSG_PVS, self->s.origin, NULL); - } - VectorClear(self->s.origin); - VectorClear(self->velocity); - self->solid = SOLID_NOT; - self->svflags |= SVF_NOCLIENT; - self->think = func_explosive_respawn; - self->nextthink = level.time + 10; // substitute whatever value you want here - self->use = NULL; - gi.linkentity(self); -*/ - -} - -void func_explosive_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) -{ - self->enemy = inflictor; - self->activator = attacker; - if(self->delay > 0) - { - self->think = func_explosive_explode; - self->nextthink = level.time + self->delay; - } - else - func_explosive_explode(self); -} - -void func_explosive_use(edict_t *self, edict_t *other, edict_t *activator) -{ - func_explosive_die (self, self, other, self->health, vec3_origin); -} - -void func_explosive_spawn (edict_t *self, edict_t *other, edict_t *activator) -{ - self->solid = SOLID_BSP; - self->svflags &= ~SVF_NOCLIENT; - self->use = NULL; - KillBox (self); - gi.linkentity (self); -} - -// Lazarus func_explosive can be damaged by impact -void func_explosive_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - int damage; - float delta; - float mass; - vec3_t dir, impact_v; - - if(!self->health) return; - if(other->mass <= 200) return; - if(VectorLength(other->velocity)==0) return; - // Check for impact damage - if(self->health > 0) - { - VectorSubtract(other->velocity,self->velocity,impact_v); - delta = VectorLength(impact_v); - delta = delta*delta*0.0001; - mass = self->mass; - if(!mass) mass = 200; - delta *= (float)(other->mass)/mass; - if (delta > 30) - { - damage = (delta-30)/2; - if (damage > 0) - { - VectorSubtract(self->s.origin,other->s.origin,dir); - VectorNormalize(dir); - T_Damage (self, other, other, dir, self->s.origin, vec3_origin, damage, 0, 0, MOD_FALLING); - } - } - } -} - -void SP_func_explosive (edict_t *self) -{ - if (deathmatch->value) - { // auto-remove for deathmatch - G_FreeEdict (self); - return; - } - self->class_id = ENTITY_FUNC_EXPLOSIVE; - self->movetype = MOVETYPE_PUSH; - - PrecacheDebris(self->gib_type); - - gi.setmodel (self, self->model); - - if (self->spawnflags & 1) - { - self->svflags |= SVF_NOCLIENT; - self->solid = SOLID_NOT; - self->use = func_explosive_spawn; - } - else - { - self->solid = SOLID_BSP; - if (self->targetname) - self->use = func_explosive_use; - } - - if (self->spawnflags & 2) - self->s.effects |= EF_ANIM_ALL; - if (self->spawnflags & 4) - self->s.effects |= EF_ANIM_ALLFAST; - - if (self->use != func_explosive_use) - { - if (!self->health) - self->health = 100; - self->die = func_explosive_die; - self->takedamage = DAMAGE_YES; - } - - // Lazarus: added touch function for impact damage - self->touch = func_explosive_touch; - - // Lazarus: for respawning func_explosives - self->max_health = self->health; - - gi.linkentity (self); -} - - -void barrel_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) - -{ - float ratio; - vec3_t v; - - if ((!other->groundentity) || (other->groundentity == self)) - return; - - ratio = (float)other->mass / (float)self->mass; - VectorSubtract (self->s.origin, other->s.origin, v); - M_walkmove (self, vectoyaw(v), 20 * ratio * FRAMETIME); -} - -void barrel_explode (edict_t *self) -{ - vec3_t org; - float spd; - vec3_t save; - vec3_t size; - - if( (self->gib_type > 0) && (self->gib_type < 10)) - { - func_explosive_explode(self); - return; - } - VectorCopy (self->s.origin, save); - VectorMA (self->absmin, 0.5, self->size, self->s.origin); - - T_RadiusDamage (self, self->activator, self->dmg, NULL, self->dmg+40, MOD_BARREL, -0.5); - - // Lazarus: Nice catch by Argh! For years now, explobox chunks have - // gone through the floor. This is very noticeable if the floor - // is transparent. Like func_explosive, shrink size box so that - // debris has an initial standoff from the floor. - VectorScale(self->size,0.5,size); - - // a few big chunks - spd = 1.5 * (float)self->dmg / 200.0; - org[0] = self->s.origin[0] + crandom() * size[0]; - org[1] = self->s.origin[1] + crandom() * size[1]; - org[2] = self->s.origin[2] + crandom() * size[2]; - ThrowDebris (self, "models/gibs/cinder.mdl", spd, org, 6, 0); - org[0] = self->s.origin[0] + crandom() * size[0]; - org[1] = self->s.origin[1] + crandom() * size[1]; - org[2] = self->s.origin[2] + crandom() * size[2]; - ThrowDebris (self, "models/gibs/cinder.mdl", spd, org, 7, 0); - - // bottom corners - spd = 1.75 * (float)self->dmg / 200.0; - VectorCopy (self->absmin, org); - org[2] += 2; - ThrowDebris (self, "models/gibs/cinder.mdl", spd, org, 8, 0); - VectorCopy (self->absmin, org); - org[0] += self->size[0]; - org[2] += 2; - ThrowDebris (self, "models/gibs/cinder.mdl", spd, org, 5, 0); - VectorCopy (self->absmin, org); - org[1] += self->size[1]; - org[2] += 2; - ThrowDebris (self, "models/gibs/cinder.mdl", spd, org, 8, 0); - VectorCopy (self->absmin, org); - org[0] += self->size[0]; - org[1] += self->size[1]; - org[2] += 2; - ThrowDebris (self, "models/gibs/cinder.mdl", spd, org, 5, 0); - - // a bunch of little chunks - spd = 2 * self->dmg / 200; - org[0] = self->s.origin[0] + crandom() * size[0]; - org[1] = self->s.origin[1] + crandom() * size[1]; - org[2] = self->s.origin[2] + crandom() * size[2]; - ThrowDebris (self, "models/gibs/cinder.mdl", spd, org, 0, 0); - org[0] = self->s.origin[0] + crandom() * size[0]; - org[1] = self->s.origin[1] + crandom() * size[1]; - org[2] = self->s.origin[2] + crandom() * size[2]; - ThrowDebris (self, "models/gibs/cinder.mdl", spd, org, 1, 0); - org[0] = self->s.origin[0] + crandom() * size[0]; - org[1] = self->s.origin[1] + crandom() * size[1]; - org[2] = self->s.origin[2] + crandom() * size[2]; - ThrowDebris (self, "models/gibs/cinder.mdl", spd, org, 2, 0); - org[0] = self->s.origin[0] + crandom() * size[0]; - org[1] = self->s.origin[1] + crandom() * size[1]; - org[2] = self->s.origin[2] + crandom() * size[2]; - ThrowDebris (self, "models/gibs/cinder.mdl", spd, org, 3, 0); - org[0] = self->s.origin[0] + crandom() * size[0]; - org[1] = self->s.origin[1] + crandom() * size[1]; - org[2] = self->s.origin[2] + crandom() * size[2]; - ThrowDebris (self, "models/gibs/cinder.mdl", spd, org, 2, 0); - org[0] = self->s.origin[0] + crandom() * size[0]; - org[1] = self->s.origin[1] + crandom() * size[1]; - org[2] = self->s.origin[2] + crandom() * size[2]; - ThrowDebris (self, "models/gibs/cinder.mdl", spd, org, 1, 0); - org[0] = self->s.origin[0] + crandom() * size[0]; - org[1] = self->s.origin[1] + crandom() * size[1]; - org[2] = self->s.origin[2] + crandom() * size[2]; - ThrowDebris (self, "models/gibs/cinder.mdl", spd, org, 3, 0); - org[0] = self->s.origin[0] + crandom() * size[0]; - org[1] = self->s.origin[1] + crandom() * size[1]; - org[2] = self->s.origin[2] + crandom() * size[2]; - ThrowDebris (self, "models/gibs/cinder.mdl", spd, org, 0, 0); - - // Lazarus: use targets - G_UseTargets (self, self->activator); - - // Lazarus: added case for no damage - if(self->dmg) - { - if (self->groundentity) - { - self->s.origin[2] = self->absmin[2] + 2; - BecomeExplosion2 (self); - } - else BecomeExplosion1 (self); - } - else - { - gi.sound (self, CHAN_VOICE, gi.soundindex ("tank/thud.wav"), 1, ATTN_NORM, 0); - G_FreeEdict (self); - } -} - -void barrel_delay (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) -{ - self->takedamage = DAMAGE_NO; - self->nextthink = level.time + 2 * FRAMETIME; - self->think = barrel_explode; - self->activator = attacker; -} - -void SP_misc_explobox (edict_t *self) -{ - if (deathmatch->value) - { // auto-remove for deathmatch - G_FreeEdict (self); - return; - } - - self->class_id = ENTITY_MISC_EXPLOBOX; - // Lazarus: can use actual barrel parts for debris: - - self->gib_type = GIB_BARREL; - - PrecacheDebris(self->gib_type); - self->solid = SOLID_BBOX; - self->clipmask = MASK_MONSTERSOLID | MASK_PLAYERSOLID; - self->movetype = MOVETYPE_STEP; - - self->model = "models/barrel.mdl"; - self->s.modelindex = gi.modelindex (self->model); - VectorSet (self->mins, -16, -16, 0); - VectorSet (self->maxs, 16, 16, 40); - - if (!self->mass) self->mass = 400; - if (!self->health) self->health = 10; - if (!self->dmg) self->dmg = 150; - - self->die = barrel_delay; - self->takedamage = DAMAGE_YES; - self->monsterinfo.aiflags = AI_NOSTEP; - - self->touch = barrel_touch; - - // Lazarus: this isn't necessary, as MOVETYPE_STEP is already - // effected by gravity anyway. Adding this in has the effect - // of doubling gravity, which is OK for normally placed - // misc_explobox but sucks if it is spawned from above the floor - // by a target_spawner or target_clone. - //self->think = M_droptofloor; - //self->nextthink = level.time + 2 * FRAMETIME; - - self->common_name = "Exploding Box"; - gi.setmodel (self, self->model); -} - - -// -// miscellaneous specialty items -// - -/*QUAKED misc_blackhole (1 .5 0) (-8 -8 -8) (8 8 8) -*/ - -void misc_blackhole_use (edict_t *ent, edict_t *other, edict_t *activator) -{ - /* - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_BOSSTPORT); - WRITE_COORD (ent->s.origin); - MESSAGE_SEND (MSG_PVS, ent->s.origin, NULL); - */ - G_FreeEdict (ent); -} - -void misc_blackhole_think (edict_t *self) -{ - if (++self->s.frame < 19) - self->nextthink = level.time + FRAMETIME; - else - { - self->s.frame = 0; - self->nextthink = level.time + FRAMETIME; - } -} - -void misc_blackhole_transparent (edict_t *ent) -{ - // Lazarus: This avoids the problem mentioned below. - ent->s.renderfx = RF_TRANSLUCENT; - ent->prethink = NULL; - gi.linkentity(ent); -} - -void SP_misc_blackhole (edict_t *ent) -{ - ent->class_id = ENTITY_MISC_BLACKHOLE; - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_NOT; - VectorSet (ent->mins, -64, -64, 0); - VectorSet (ent->maxs, 64, 64, 8); - ent->s.modelindex = gi.modelindex ("models/objects/black/tris.md2"); -// ent->s.renderfx = RF_TRANSLUCENT; // Lazarus: For some oddball reason if this is set - // here, blackhole generator will be - // invisible. Rogue MP has the same problem. - ent->use = misc_blackhole_use; - ent->think = misc_blackhole_think; - ent->prethink = misc_blackhole_transparent; - ent->nextthink = level.time + 2 * FRAMETIME; - gi.linkentity (ent); -} - -/*QUAKED misc_eastertank (1 .5 0) (-32 -32 -16) (32 32 32) -*/ - -void misc_eastertank_think (edict_t *self) -{ - if (++self->s.frame < 293) - self->nextthink = level.time + FRAMETIME; - else - { - self->s.frame = 254; - self->nextthink = level.time + FRAMETIME; - } -} - -void SP_misc_eastertank (edict_t *ent) -{ - ent->class_id = ENTITY_MISC_EASTERTANK; - ent->movetype = MOVETYPE_NONE; - // Lazarus - nonsolid - if(ent->spawnflags & 1) - ent->solid = SOLID_NOT; - else - { - ent->solid = SOLID_BBOX; - VectorSet (ent->mins, -32, -32, -16); - VectorSet (ent->maxs, 32, 32, 32); - } - ent->s.modelindex = gi.modelindex ("models/monsters/tank/tris.md2"); - ent->s.frame = 254; - ent->think = misc_eastertank_think; - ent->nextthink = level.time + 2 * FRAMETIME; - gi.linkentity (ent); -} - -/*QUAKED misc_easterchick (1 .5 0) (-32 -32 0) (32 32 32) -*/ - - -void misc_easterchick_think (edict_t *self) -{ - if (++self->s.frame < 247) - self->nextthink = level.time + FRAMETIME; - else - { - self->s.frame = 208; - self->nextthink = level.time + FRAMETIME; - } -} - -void SP_misc_easterchick (edict_t *ent) -{ - ent->class_id = ENTITY_MISC_EASTERCHICK; - ent->movetype = MOVETYPE_NONE; - // Lazarus - nonsolid - if(ent->spawnflags & 1) - ent->solid = SOLID_NOT; - else - { - ent->solid = SOLID_BBOX; - VectorSet (ent->mins, -32, -32, 0); - VectorSet (ent->maxs, 32, 32, 32); - } - ent->s.modelindex = gi.modelindex ("models/monsters/bitch/tris.md2"); - ent->s.frame = 208; - ent->think = misc_easterchick_think; - ent->nextthink = level.time + 2 * FRAMETIME; - gi.linkentity (ent); -} - -/*QUAKED misc_easterchick2 (1 .5 0) (-32 -32 0) (32 32 32) -*/ - - -void misc_easterchick2_think (edict_t *self) -{ - if (++self->s.frame < 287) - self->nextthink = level.time + FRAMETIME; - else - { - self->s.frame = 248; - self->nextthink = level.time + FRAMETIME; - } -} - -void SP_misc_easterchick2 (edict_t *ent) -{ - ent->class_id = ENTITY_MISC_EASTERCHICK2; - ent->movetype = MOVETYPE_NONE; - // Lazarus - nonsolid - if(ent->spawnflags & 1) - ent->solid = SOLID_NOT; - else - { - ent->solid = SOLID_BBOX; - VectorSet (ent->mins, -32, -32, 0); - VectorSet (ent->maxs, 32, 32, 32); - } - ent->s.modelindex = gi.modelindex ("models/monsters/bitch/tris.md2"); - ent->s.frame = 248; - ent->think = misc_easterchick2_think; - ent->nextthink = level.time + 2 * FRAMETIME; - gi.linkentity (ent); -} - - -/*QUAKED monster_commander_body (1 .5 0) (-32 -32 0) (32 32 48) -Not really a monster, this is the Tank Commander's decapitated body. -There should be a item_commander_head that has this as it's target. -*/ - -void commander_body_think (edict_t *self) -{ - if (++self->s.frame < 24) - self->nextthink = level.time + FRAMETIME; - else - self->nextthink = 0; - - if (self->s.frame == 22) - gi.sound (self, CHAN_BODY, gi.soundindex ("tank/thud.wav"), 1, ATTN_NORM, 0); -} - -void commander_body_use (edict_t *self, edict_t *other, edict_t *activator) -{ - self->think = commander_body_think; - self->nextthink = level.time + FRAMETIME; - gi.sound (self, CHAN_BODY, gi.soundindex ("tank/pain.wav"), 1, ATTN_NORM, 0); -} - -void commander_body_drop (edict_t *self) -{ - self->movetype = MOVETYPE_TOSS; - self->s.origin[2] += 2; - - if (!(self->spawnflags & SF_MONSTER_FLIES)) - return; - if (self->waterlevel) - return; - self->s.effects |= EF_FLIES; - self->s.sound = gi.soundindex ("infantry/inflies1.wav"); -} - -void SP_monster_commander_body (edict_t *self) -{ - self->class_id = ENTITY_MONSTER_COMMANDER_BODY; - self->movetype = MOVETYPE_NONE; - self->solid = SOLID_BBOX; - self->model = "models/monsters/commandr/tris.md2"; - self->s.modelindex = gi.modelindex (self->model); - VectorSet (self->mins, -32, -32, 0); - VectorSet (self->maxs, 32, 32, 48); - self->use = commander_body_use; - self->takedamage = DAMAGE_YES; - self->flags = FL_GODMODE; - self->s.renderfx |= RF_FRAMELERP; - gi.linkentity (self); - - gi.soundindex ("tank/thud.wav"); - gi.soundindex ("tank/pain.wav"); - - self->think = commander_body_drop; - self->nextthink = level.time + 5 * FRAMETIME; -} - - -/*QUAKED misc_banner (1 .5 0) (-4 -4 -4) (4 4 4) -The origin is the bottom of the banner. -The banner is 128 tall. -*/ -void misc_banner_think (edict_t *ent) -{ - ent->s.frame = (ent->s.frame + 1) % 16; - ent->nextthink = level.time + FRAMETIME; -} - -void SP_misc_banner (edict_t *ent) -{ - ent->class_id = ENTITY_MISC_VIPER; - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_NOT; - ent->s.modelindex = gi.modelindex ("models/objects/banner/tris.md2"); - ent->s.frame = rand() % 16; - gi.linkentity (ent); - - ent->think = misc_banner_think; - ent->nextthink = level.time + FRAMETIME; -} - -/*QUAKED misc_deadsoldier (1 .5 0) (-16 -16 0) (16 16 16) ON_BACK ON_STOMACH BACK_DECAP FETAL_POS SIT_DECAP IMPALED -This is the dead player model. Comes in 6 exciting different poses! -*/ -void misc_deadsoldier_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) -{ - int n; - - if (self->health > -80) - return; - - // Lazarus - self->s.effects &= ~EF_FLIES; - self->s.sound = 0; - - gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0); - for (n= 0; n < 4; n++) - ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); - ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); -} - -void misc_deadsoldier_flieson(edict_t *self) -{ - if (!(self->spawnflags & 64)) - return; - if (self->waterlevel) - return; - self->s.effects |= EF_FLIES; - self->s.sound = gi.soundindex ("infantry/inflies1.wav"); -} - -void SP_misc_deadsoldier (edict_t *ent) -{ - if (deathmatch->value) - { // auto-remove for deathmatch - G_FreeEdict (ent); - return; - } - - ent->class_id = ENTITY_MISC_DEADSOLDIER; - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_BBOX; - ent->s.modelindex=gi.modelindex ("models/deadbods/dude/tris.md2"); - - // Defaults to frame 0 - if (ent->spawnflags & 2) - ent->s.frame = 1; - else if (ent->spawnflags & 4) - ent->s.frame = 2; - else if (ent->spawnflags & 8) - ent->s.frame = 3; - else if (ent->spawnflags & 16) - ent->s.frame = 4; - else if (ent->spawnflags & 32) - ent->s.frame = 5; - else - ent->s.frame = 0; - - // Lazarus - if (ent->spawnflags & 64) { - // postpone turning on flies till we figure out whether dude is submerged - ent->think = misc_deadsoldier_flieson; - ent->nextthink = level.time + FRAMETIME; - } - VectorSet (ent->mins, -16, -16, 0); - VectorSet (ent->maxs, 16, 16, 16); - ent->deadflag = DEAD_DEAD; - ent->takedamage = DAMAGE_YES; - ent->svflags |= SVF_MONSTER|SVF_DEADMONSTER; - ent->die = misc_deadsoldier_die; - ent->monsterinfo.aiflags |= AI_GOOD_GUY; - - ent->common_name = "Dead Marine"; - gi.linkentity (ent); -} - -/*QUAKED misc_bigviper (1 .5 0) (-176 -120 -24) (176 120 72) -This is a large stationary viper as seen in Paul's intro -*/ -void SP_misc_bigviper (edict_t *ent) -{ - ent->class_id = ENTITY_MISC_BIGVIPER; - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_BBOX; - VectorSet (ent->mins, -176, -120, -24); - VectorSet (ent->maxs, 176, 120, 72); - ent->s.modelindex = gi.modelindex ("models/ships/bigviper/tris.md2"); - ent->common_name = "Viper"; - gi.linkentity (ent); -} - - -/*QUAKED misc_viper_bomb (1 0 0) (-8 -8 -8) (8 8 8) -"dmg" how much boom should the bomb make? -*/ -void misc_viper_bomb_use (edict_t *, edict_t *, edict_t *); -void misc_viper_bomb_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - G_UseTargets (self, self->activator); - - self->s.origin[2] = self->absmin[2] + 1; - T_RadiusDamage (self, self, self->dmg, NULL, self->dmg+40, MOD_BOMB, -0.5); - - self->count--; - if(!self->count) - self->spawnflags &= ~1; - - if(self->spawnflags & 1) - { - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_EXPLOSION2); - WRITE_COORD (self->s.origin); - MESSAGE_SEND (MSG_PVS, self->s.origin, NULL); - - self->svflags |= SVF_NOCLIENT; - self->solid = SOLID_NOT; - self->use = misc_viper_bomb_use; - self->s.effects = 0; - self->movetype = MOVETYPE_NONE; - self->nextthink = 0; - self->think = NULL; - VectorCopy(self->pos1,self->s.origin); - VectorCopy(self->pos2,self->s.angles); - VectorClear(self->velocity); - gi.linkentity(self); - } - else - BecomeExplosion2 (self); -} - -void misc_viper_bomb_prethink (edict_t *self) -{ - vec3_t v; - float diff; - - self->groundentity = NULL; - - diff = self->timestamp - level.time; - if (diff < -1.0) - diff = -1.0; - - VectorScale (self->moveinfo.dir, 1.0 + diff, v); - v[2] = diff; - - diff = self->s.angles[2]; - vectoangles (v, self->s.angles); - self->s.angles[2] = diff + 10; -} - -void viper_bomb_think(edict_t *self) -{ - if(readout->value) { - gi.dprintf("bomb velocity=%g,%g,%g\n",self->velocity[0],self->velocity[1],self->velocity[2]); - gi.dprintf("bomb position=%g,%g,%g\n",self->s.origin[0],self->s.origin[1],self->s.origin[2]); - } - self->nextthink = level.time + FRAMETIME; -} -void misc_viper_bomb_use (edict_t *self, edict_t *other, edict_t *activator) -{ - edict_t *viper; - - if(self->solid != SOLID_NOT) // Already in use - return; - - self->solid = SOLID_BBOX; - self->svflags &= ~SVF_NOCLIENT; - self->s.effects |= EF_ROCKET; - self->use = NULL; - self->movetype = MOVETYPE_TOSS; - self->prethink = misc_viper_bomb_prethink; - self->touch = misc_viper_bomb_touch; - self->activator = activator; - - VectorCopy(self->pos1,self->s.origin); - VectorCopy(self->pos2,self->s.angles); - - if(self->pathtarget) - { - if(!strcasecmp(self->pathtarget,self->targetname)) - { - VectorScale(self->movedir,self->speed,self->velocity); - VectorCopy(self->movedir,self->moveinfo.dir); - } - else - { - viper = G_Find(NULL, FOFS(targetname), self->pathtarget); - if(!viper) - return; - VectorScale (viper->moveinfo.dir, viper->moveinfo.speed, self->velocity); - VectorCopy (viper->moveinfo.dir, self->moveinfo.dir); - } - } - else - { - viper = G_Find (NULL, FOFS(classname), "misc_viper"); - if(!viper) - return; - VectorScale (viper->moveinfo.dir, viper->moveinfo.speed, self->velocity); - VectorCopy (viper->moveinfo.dir, self->moveinfo.dir); - } - - self->timestamp = level.time; - - self->think = viper_bomb_think; - self->nextthink = level.time + FRAMETIME; -} - -void SP_misc_viper_bomb (edict_t *self) -{ - vec3_t angles; - - self->class_id = ENTITY_MISC_VIPER_BOMB; - self->movetype = MOVETYPE_NONE; - self->solid = SOLID_NOT; - VectorSet (self->mins, -8, -8, -8); - VectorSet (self->maxs, 8, 8, 8); - - self->s.modelindex = gi.modelindex ("models/objects/bomb/tris.md2"); - - // Lazarus: Preserve angles in movedir and initial placement - // for case of targeting self - VectorCopy(self->s.angles,angles); - G_SetMovedir (angles,self->movedir); - VectorCopy(self->s.origin,self->pos1); - VectorCopy(self->s.angles,self->pos2); - - if (!self->dmg) - self->dmg = 1000; - - self->use = misc_viper_bomb_use; - self->svflags |= SVF_NOCLIENT; - self->common_name = "Bomb"; - gi.linkentity (self); -} - -/*QUAKED misc_satellite_dish (1 .5 0) (-64 -64 0) (64 64 128) -*/ - -// Lazarus: Changed to toggle properly with each use. - -void misc_satellite_dish_think (edict_t *self) -{ - if(self->spawnflags & 1) - { - self->s.frame++; - if (self->s.frame < 38) - self->nextthink = level.time + FRAMETIME; - } - else if (self->s.frame > 0) - { - self->s.frame--; - self->nextthink = level.time + FRAMETIME; - } -} - -void misc_satellite_dish_use (edict_t *self, edict_t *other, edict_t *activator) -{ - self->think = misc_satellite_dish_think; - self->nextthink = level.time + FRAMETIME; - self->spawnflags ^= 1; -} - -void SP_misc_satellite_dish (edict_t *ent) -{ - ent->class_id = ENTITY_MISC_SATELLITE_DISH; - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_BBOX; - ent->s.frame = 0; - VectorSet (ent->mins, -64, -64, 0); - VectorSet (ent->maxs, 64, 64, 128); - ent->s.modelindex = gi.modelindex ("models/objects/satellite/tris.md2"); - ent->use = misc_satellite_dish_use; - ent->common_name = "Satellite Dish"; - gi.linkentity (ent); -} - - -/*QUAKED light_mine1 (0 1 0) (-2 -2 -12) (2 2 12) -*/ -void SP_light_mine1 (edict_t *ent) -{ - ent->class_id = ENTITY_LIGHT_MINE1; - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_BBOX; - ent->s.modelindex = gi.modelindex ("models/objects/minelite/light1/tris.md2"); - gi.linkentity (ent); -} - - -/*QUAKED light_mine2 (0 1 0) (-2 -2 -12) (2 2 12) -*/ -void SP_light_mine2 (edict_t *ent) -{ - ent->class_id = ENTITY_LIGHT_MINE2; - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_BBOX; - ent->s.modelindex = gi.modelindex ("models/objects/minelite/light2/tris.md2"); - gi.linkentity (ent); -} - - -/*QUAKED misc_gib_arm (1 0 0) (-8 -8 -8) (8 8 8) -Intended for use with the target_spawner -*/ -void SP_misc_gib_arm (edict_t *ent) -{ - ent->class_id = ENTITY_MISC_GIB_ARM; - gi.setmodel (ent, "models/objects/gibs/arm/tris.md2"); - ent->solid = SOLID_NOT; - ent->s.effects |= EF_GIB; - ent->takedamage = DAMAGE_YES; - ent->die = gib_die; - ent->movetype = MOVETYPE_TOSS; - ent->svflags |= SVF_MONSTER; - ent->deadflag = DEAD_DEAD; - ent->avelocity[0] = random()*200; - ent->avelocity[1] = random()*200; - ent->avelocity[2] = random()*200; - ent->think = G_FreeEdict; - ent->nextthink = level.time + 30; - gi.linkentity (ent); -} - -/*QUAKED misc_gib_leg (1 0 0) (-8 -8 -8) (8 8 8) -Intended for use with the target_spawner -*/ -void SP_misc_gib_leg (edict_t *ent) -{ - ent->class_id = ENTITY_MISC_GIB_LEG; - gi.setmodel (ent, "models/objects/gibs/leg/tris.md2"); - ent->solid = SOLID_NOT; - ent->s.effects |= EF_GIB; - ent->takedamage = DAMAGE_YES; - ent->die = gib_die; - ent->movetype = MOVETYPE_TOSS; - ent->svflags |= SVF_MONSTER; - ent->deadflag = DEAD_DEAD; - ent->avelocity[0] = random()*200; - ent->avelocity[1] = random()*200; - ent->avelocity[2] = random()*200; - ent->think = G_FreeEdict; - ent->nextthink = level.time + 30; - gi.linkentity (ent); -} - -/*QUAKED misc_gib_head (1 0 0) (-8 -8 -8) (8 8 8) -Intended for use with the target_spawner -*/ -void SP_misc_gib_head (edict_t *ent) -{ - ent->class_id = ENTITY_MISC_GIB_HEAD; - gi.setmodel (ent, "models/objects/gibs/head/tris.md2"); - ent->solid = SOLID_NOT; - ent->s.effects |= EF_GIB; - ent->takedamage = DAMAGE_YES; - ent->die = gib_die; - ent->movetype = MOVETYPE_TOSS; - ent->svflags |= SVF_MONSTER; - ent->deadflag = DEAD_DEAD; - ent->avelocity[0] = random()*200; - ent->avelocity[1] = random()*200; - ent->avelocity[2] = random()*200; - ent->think = G_FreeEdict; - ent->nextthink = level.time + 30; - gi.linkentity (ent); -} - -//===================================================== - -/*QUAKED target_character (0 0 1) ? -used with target_string (must be on same "team") -"count" is position in the string (starts at 1) -*/ - -void SP_target_character (edict_t *self) -{ - self->class_id = ENTITY_TARGET_CHARACTER; - self->movetype = MOVETYPE_PUSH; - gi.setmodel (self, self->model); - self->solid = SOLID_BSP; - self->s.frame = 12; - gi.linkentity (self); - return; -} - - -/*QUAKED target_string (0 0 1) (-8 -8 -8) (8 8 8) -*/ - -void target_string_use (edict_t *self, edict_t *other, edict_t *activator) -{ - edict_t *e; - int n, l; - char c; - - l = strlen(self->message); - for (e = self->teammaster; e; e = e->teamchain) - { - if (!e->count) - continue; - n = e->count - 1; - if (n > l) - { - e->s.frame = 12; - continue; - } - - c = self->message[n]; - if (c >= '0' && c <= '9') - e->s.frame = c - '0'; - else if (c == '-') - e->s.frame = 10; - else if (c == ':') - e->s.frame = 11; - else - e->s.frame = 12; - } -} - -void SP_target_string (edict_t *self) -{ - self->class_id = ENTITY_TARGET_STRING; - if (!self->message) - self->message = ""; - self->use = target_string_use; -} - - -/*QUAKED func_clock (0 0 1) (-8 -8 -8) (8 8 8) TIMER_UP TIMER_DOWN START_OFF MULTI_USE -target a target_string with this - -The default is to be a time of day clock - -TIMER_UP and TIMER_DOWN run for "count" seconds and the fire "pathtarget" -If START_OFF, this entity must be used before it starts - -"style" 0 "xx" - 1 "xx:xx" - 2 "xx:xx:xx" -*/ - -#define CLOCK_MESSAGE_SIZE 16 - -// don't let field width of any clock messages change, or it -// could cause an overwrite after a game load - -static void func_clock_reset (edict_t *self) -{ - self->activator = NULL; - if (self->spawnflags & 1) - { - self->health = 0; - self->wait = self->count; - } - else if (self->spawnflags & 2) - { - self->health = self->count; - self->wait = 0; - } -} - -static void func_clock_format_countdown (edict_t *self) -{ - if (self->style == 0) - { - sprintf (self->message, "%2i", self->health); - return; - } - - if (self->style == 1) - { - sprintf(self->message, "%2i:%2i", self->health / 60, self->health % 60); - if (self->message[3] == ' ') - self->message[3] = '0'; - return; - } - - if (self->style == 2) - { - sprintf(self->message, "%2i:%2i:%2i", self->health / 3600, (self->health - (self->health / 3600) * 3600) / 60, self->health % 60); - if (self->message[3] == ' ') - self->message[3] = '0'; - if (self->message[6] == ' ') - self->message[6] = '0'; - return; - } -} - -void func_clock_think (edict_t *self) -{ - if (!self->enemy) - { - self->enemy = G_Find (NULL, FOFS(targetname), self->target); - if (!self->enemy) - return; - } - - if (self->spawnflags & 1) - { - func_clock_format_countdown (self); - self->health++; - } - else if (self->spawnflags & 2) - { - func_clock_format_countdown (self); - self->health--; - } - else - { - struct tm *ltime; - time_t gmtime; - - time(&gmtime); - ltime = localtime(&gmtime); - sprintf (self->message, "%2i:%2i:%2i", ltime->tm_hour, ltime->tm_min, ltime->tm_sec); - if (self->message[3] == ' ') - self->message[3] = '0'; - if (self->message[6] == ' ') - self->message[6] = '0'; - } - - self->enemy->message = self->message; - self->enemy->use (self->enemy, self, self); - - if (((self->spawnflags & 1) && (self->health > self->wait)) || - ((self->spawnflags & 2) && (self->health < self->wait))) - { - if (self->pathtarget) - { - char *savetarget; - char *savemessage; - - savetarget = self->target; - savemessage = self->message; - self->target = self->pathtarget; - self->message = NULL; - G_UseTargets (self, self->activator); - self->target = savetarget; - self->message = savemessage; - } - - if (!(self->spawnflags & 8)) { - // Lazarus: Can't be used again, so get rid of it - self->think = G_FreeEdict; - self->nextthink = level.time + 1; - return; - } - - func_clock_reset (self); - - if (self->spawnflags & 4) - return; - } - - self->nextthink = level.time + 1; -} - -void func_clock_use (edict_t *self, edict_t *other, edict_t *activator) -{ - if (!(self->spawnflags & 8)) - self->use = NULL; - if (self->activator) - return; - self->activator = activator; - self->think (self); -} - -void SP_func_clock (edict_t *self) -{ - if (!self->target) - { - gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin)); - G_FreeEdict (self); - return; - } - - if ((self->spawnflags & 2) && (!self->count)) - { - gi.dprintf("%s with no count at %s\n", self->classname, vtos(self->s.origin)); - G_FreeEdict (self); - return; - } - self->class_id = ENTITY_FUNC_CLOCK; - - if ((self->spawnflags & 1) && (!self->count)) - self->count = 60*60;; - - func_clock_reset (self); - - self->message = (char *)TagMalloc (CLOCK_MESSAGE_SIZE, TAG_LEVEL); - - self->think = func_clock_think; - - if (self->spawnflags & 4) - self->use = func_clock_use; - else - self->nextthink = level.time + 1; -} -/*================================================================== - Lazarus changes to misc_teleporter: - - START_OFF (=1) If set, misc_teleporter must be targeted to work - TOGGLE (=2) If set, misc_teleporter is toggled on/off each time - it is targeted - NO_MODEL (=4) If set, teleporter does not use the normal teleporter - base or splash effect - LANDMARK (=64) If set, player angles and speed are preserved - - misc_teleporter selects a random pick from up to 8 targets for a - destination - - trigger_transition with same name as teleporter can be used to - teleport multiple non-player entities -================================================================== */ -bool HasSpawnFunction(edict_t *ent) -{ - spawn_t *s; - gitem_t *item; - int i; - - if(!ent->classname) - return false; - - // check item spawn functions - for (i=0,item=itemlist ; iclassname) - continue; - if (!strcmp(item->classname, ent->classname)) - return true; - } - // check normal spawn functions - for (s=spawns ; s->name ; s++) - { - if (!strcmp(s->name, ent->classname)) - return true; - } - return false; -} - -void teleport_transition_ents (edict_t *transition, edict_t *teleporter, edict_t *destination) -{ - entlist_t DoNotMove; - int i, j; - int total=0; - bool nogo=false; - edict_t *ent; - entlist_t *p; - vec3_t angles, forward, right, v; - vec3_t start; - - angles[YAW] = destination->s.angles[YAW] - teleporter->s.angles[YAW]; - angles[PITCH] = angles[ROLL] = 0; - AngleVectors(angles,forward,right,NULL); - VectorNegate(right,right); - if(teleporter->solid == SOLID_TRIGGER) - VectorMA(teleporter->absmin,0.5,teleporter->size,start); - else - VectorCopy(teleporter->s.origin,start); - - for(i=game.maxclients+1; iid = 0; - if(!ent->inuse) continue; - // Pass up owned entities not owned by the player on this pass... - // get 'em next pass so we'll know whether owner is in our list - if(ent->owner && !ent->owner->client) continue; - if(ent->solid == SOLID_BSP) continue; - if((ent->solid == SOLID_TRIGGER) && !FindItemByClassname(ent->classname)) continue; - // Do not under any circumstances move these entities: - for(p=&DoNotMove, nogo=false; p->name && !nogo; p++) - if(!strcasecmp(ent->classname,p->name)) - nogo = true; - if(nogo) continue; - if(!HasSpawnFunction(ent)) continue; - if(ent->s.origin[0] > transition->maxs[0]) continue; - if(ent->s.origin[1] > transition->maxs[1]) continue; - if(ent->s.origin[2] > transition->maxs[2]) continue; - if(ent->s.origin[0] < transition->mins[0]) continue; - if(ent->s.origin[1] < transition->mins[1]) continue; - if(ent->s.origin[2] < transition->mins[2]) continue; - total++; - ent->id = total; - if(ent->owner) - ent->owner_id = -(ent->owner - g_edicts); - else - ent->owner_id = 0; - if(angles[YAW]) - { - vec3_t spawn_offset; - - VectorSubtract(ent->s.origin,start,spawn_offset); - VectorCopy(spawn_offset,v); - G_ProjectSource (vec3_origin, v, forward, right, spawn_offset); - VectorCopy(spawn_offset,ent->s.origin); - VectorCopy(ent->velocity,v); - G_ProjectSource (vec3_origin, v, forward, right, ent->velocity); - ent->s.angles[YAW] += angles[YAW]; - } - else - { - VectorSubtract(ent->s.origin,teleporter->s.origin,ent->s.origin); - } - VectorAdd(ent->s.origin,destination->s.origin,ent->s.origin); - gi.linkentity(ent); - } - // Repeat, ONLY for ents owned by non-players - for(i=game.maxclients+1; iinuse) continue; - if(!ent->owner) continue; - if(ent->owner->client) continue; - if(ent->solid == SOLID_BSP) continue; - if((ent->solid == SOLID_TRIGGER) && !FindItemByClassname(ent->classname)) continue; - // Do not under any circumstances move these entities: - for(p=&DoNotMove, nogo=false; p->name && !nogo; p++) - if(!strcasecmp(ent->classname,p->name)) - nogo = true; - if(nogo) continue; - if(!HasSpawnFunction(ent)) continue; - if(ent->s.origin[0] > transition->maxs[0]) continue; - if(ent->s.origin[1] > transition->maxs[1]) continue; - if(ent->s.origin[2] > transition->maxs[2]) continue; - if(ent->s.origin[0] < transition->mins[0]) continue; - if(ent->s.origin[1] < transition->mins[1]) continue; - if(ent->s.origin[2] < transition->mins[2]) continue; - ent->owner_id = 0; - for(j=game.maxclients+1; jowner_id; j++) - { - if(ent->owner == &g_edicts[j]) - ent->owner_id = g_edicts[j].id; - } - if(!ent->owner_id) continue; - total++; - ent->id = total; - if(angles[YAW]) - { - vec3_t spawn_offset; - - VectorSubtract(ent->s.origin,start,spawn_offset); - VectorCopy(spawn_offset,v); - G_ProjectSource (vec3_origin, v, forward, right, spawn_offset); - VectorCopy(spawn_offset,ent->s.origin); - VectorCopy(ent->velocity,v); - G_ProjectSource (vec3_origin, v, forward, right, ent->velocity); - ent->s.angles[YAW] += angles[YAW]; - } - else - { - VectorSubtract(ent->s.origin,teleporter->s.origin,ent->s.origin); - } - VectorAdd(ent->s.origin,destination->s.origin,ent->s.origin); - gi.linkentity(ent); - } -} - -// G_PickDestination is identical to G_PickTarget, but w/o the -// obnoxious error message. - -#define MAXCHOICES 8 -static edict_t *G_PickDestination (char *targetname) -{ - edict_t *ent = NULL; - int num_choices = 0; - edict_t *choice[MAXCHOICES]; - - if (!targetname) - { - gi.dprintf("G_PickDestination called with NULL targetname\n"); - return NULL; - } - - while(1) - { - ent = G_Find (ent, FOFS(targetname), targetname); - if (!ent) - break; - choice[num_choices++] = ent; - if (num_choices == MAXCHOICES) - break; - } - - if (!num_choices) - return NULL; - - return choice[rand() % num_choices]; -} -//================================================================================= - -void teleporter_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - edict_t *dest; - edict_t *teleporter; - bool landmark; - int i; - - // Lazarus: teleporters with MONSTER spawnflag can be used by monsters - if (!other->client && (!(self->spawnflags & 8) || !(other->svflags & SVF_MONSTER))) - return; - // Lazarus: Use G_PickTarget instead so we can have multiple destinations - //dest = G_Find (NULL, FOFS(targetname), self->target); - dest = G_PickDestination(self->target); - if (!dest) - { - gi.dprintf ("Couldn't find destination\n"); - return; - } - - if(self->owner) - teleporter = self->owner; - else - teleporter = self; - - // unlink to make sure it can't possibly interfere with KillBox - gi.unlinkentity (other); - - VectorCopy (dest->s.origin, other->s.origin); - VectorCopy (dest->s.origin, other->s.old_origin); - other->s.origin[2] += 10; - - // clear the velocity and hold them in place briefly - landmark = (teleporter->spawnflags & 64 ? true : false); - - if(landmark) - { - vec3_t angles, forward, right, v; - - VectorSubtract(dest->s.angles,teleporter->s.angles,angles); - AngleVectors(angles,forward,right,NULL); - VectorNegate(right,right); - VectorCopy(other->velocity,v); - G_ProjectSource (vec3_origin, - v, forward, right, - other->velocity); - } - else - VectorClear (other->velocity); - - if(other->client) { - other->client->ps.pmove.pm_time = 160>>3; // hold time - other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT; - } - - if(self->owner) - self->owner->s.event = EV_PLAYER_TELEPORT; - else - { - // This must be a trigger_teleporter - vec3_t origin; - VectorMA(self->mins,0.5,self->size,origin); - - gi.positioned_sound(origin,self,CHAN_AUTO,self->noise_index,1,1,0); - } - other->s.event = EV_PLAYER_TELEPORT; - - // set angles - if(landmark) - { - edict_t *transition; - - // LANDMARK - if(other->client) - { - other->s.angles[ROLL] = 0; - for (i=0 ; i<3 ; i++) - other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - teleporter->s.angles[i] + other->s.angles[i] - other->client->resp.cmd_angles[i]); - VectorClear (other->client->ps.viewangles); - VectorClear (other->client->v_angle); - VectorClear (other->s.angles); - } - else - { - VectorSubtract(other->s.angles,teleporter->s.angles,other->s.angles); - VectorAdd(other->s.angles,dest->s.angles,other->s.angles); - } - - transition = G_Find(NULL,FOFS(classname),"trigger_transition"); - while(transition) - { - if(!strcasecmp(transition->targetname,teleporter->targetname)) - teleport_transition_ents(transition,teleporter,dest); - transition = G_Find(transition,FOFS(classname),"trigger_transition"); - } - - } - else - { - if(other->client) - { - for (i=0 ; i<3 ; i++) - other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]); - VectorClear (other->client->ps.viewangles); - VectorClear (other->client->v_angle); - VectorClear (other->s.angles); - } else - VectorCopy (dest->s.angles,other->s.angles); - } - - - // kill anything at the destination - KillBox (other); - - if(!self->owner) - { - // this is a trigger_teleporter - self->count--; - if(!self->count) - G_FreeEdict(self); - } - gi.linkentity (other); -} - -void use_teleporter (edict_t *self, edict_t *other, edict_t *activator) { - - if(!(self->spawnflags & 1)) { - if(!(self->spawnflags & 2)) - return; - self->spawnflags |= 1; - self->s.effects &= ~EF_TELEPORTER; - self->target_ent->solid = SOLID_NOT; - self->s.sound = 0; - } else { - self->spawnflags &= ~1; - if(!(self->spawnflags & 4)) - self->s.effects |= EF_TELEPORTER; - self->target_ent->solid = SOLID_TRIGGER; - self->s.sound = gi.soundindex ("world/amb10.wav"); - } - gi.linkentity(self); - gi.linkentity(self->target_ent); -} - -/*QUAKED misc_teleporter (1 0 0) (-32 -32 -24) (32 32 -16) -Stepping onto this disc will teleport players to the targeted misc_teleporter_dest object. -*/ -void SP_misc_teleporter (edict_t *ent) -{ - edict_t *trig; - - if (!ent->target) - { - gi.dprintf ("teleporter without a target.\n"); - G_FreeEdict (ent); - return; - } - ent->class_id = ENTITY_MISC_TELEPORTER; - - if(!(ent->spawnflags & 4)) { - gi.setmodel (ent, "models/objects/dmspot/tris.md2"); - ent->s.skin = 1; - if(!(ent->spawnflags & 1)) { - ent->s.effects = EF_TELEPORTER; - ent->s.sound = gi.soundindex ("world/amb10.wav"); - } - } - - if(ent->spawnflags & 3) - ent->use = use_teleporter; - - if(ent->spawnflags & 4) { - ent->solid = SOLID_NOT; - } else { - ent->solid = SOLID_BBOX; - VectorSet (ent->mins, -32, -32, -24); - VectorSet (ent->maxs, 32, 32, -16); - } - ent->common_name = "Teleporter"; - gi.linkentity (ent); - - trig = G_Spawn (); - ent->target_ent = trig; - trig->touch = teleporter_touch; - trig->spawnflags = ent->spawnflags & 8; // Lazarus: Monster-usable - if(ent->spawnflags & 1) - trig->solid = SOLID_NOT; - else - trig->solid = SOLID_TRIGGER; - trig->target = ent->target; - trig->owner = ent; - VectorCopy (ent->s.origin, trig->s.origin); - VectorSet (trig->mins, -8, -8, 8); - VectorSet (trig->maxs, 8, 8, 24); - gi.linkentity (trig); - -} - -void trigger_teleporter_use (edict_t *self, edict_t *other, edict_t *activator) -{ - if(self->solid == SOLID_NOT) - self->solid = SOLID_TRIGGER; - else - self->solid = SOLID_NOT; - gi.linkentity(self); -} - -// trigger_teleporter -// spawnflags -// 2 = TRIGGERED -// 8 = MONSTER -// 16 = NO EFFECT -// 32 = CUSTOM -void SP_trigger_teleporter (edict_t *self) -{ - if (!self->target) - { - gi.dprintf ("trigger_teleporter without a target.\n"); - G_FreeEdict (self); - return; - } - self->class_id = ENTITY_TRIGGER_TELEPORTER; - if (!(self->spawnflags & 48)) - self->noise_index = gi.soundindex("misc/tele1.wav"); - else if(self->spawnflags & 32) - { - if(st.noise) - self->noise_index = gi.soundindex(st.noise); - else - self->noise_index = 0; - } - else - self->noise_index = 0; - - gi.setmodel (self, self->model); - self->touch = teleporter_touch; - self->svflags = SVF_NOCLIENT; - if(self->spawnflags & 2) - self->solid = SOLID_NOT; - else - self->solid = SOLID_TRIGGER; - gi.linkentity (self); -} - -/*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16) -Point teleporters at these. -*/ -void SP_misc_teleporter_dest (edict_t *ent) -{ - ent->class_id = ENTITY_MISC_TELEPORTER_DEST; - gi.setmodel (ent, "models/objects/dmspot/tris.md2"); - ent->s.skin = 0; - ent->solid = SOLID_BBOX; - VectorSet (ent->mins, -32, -32, -24); - VectorSet (ent->maxs, 32, 32, -16); - ent->common_name = "Teleporter Destination"; - gi.linkentity (ent); -} - -// Lazarus new entities - -void misc_light_think (edict_t *self) -{ - if(self->spawnflags & START_OFF) return; - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_FLASHLIGHT); - WRITE_COORD (self->s.origin); - WRITE_SHORT (self - g_edicts); - MESSAGE_SEND (MSG_PVS, self->s.origin, NULL); - self->nextthink = level.time + FRAMETIME; -} - -void misc_light_use (edict_t *self, edict_t *other, edict_t *activator) -{ - if (self->spawnflags & START_OFF) { - self->spawnflags &= ~START_OFF; - self->nextthink = level.time + FRAMETIME; - } else - self->spawnflags |= START_OFF; -} - -void SP_misc_light (edict_t *self) -{ - self->class_id = ENTITY_MISC_LIGHT; - self->use = misc_light_use; - self->movetype = MOVETYPE_NONE; - self->think = misc_light_think; - if (!(self->spawnflags & START_OFF)) - self->nextthink = level.time + 2*FRAMETIME; -} - -/*============================================================= - -TARGET_PRECIPITATION - -==============================================================*/ - -#define SF_WEATHER_STARTON 1 -#define SF_WEATHER_SPLASH 2 -#define SF_WEATHER_GRAVITY_BOUNCE 4 -#define SF_WEATHER_FIRE_ONCE 8 -#define SF_WEATHER_START_FADE 16 -#define STYLE_WEATHER_RAIN 0 -#define STYLE_WEATHER_BIGRAIN 1 -#define STYLE_WEATHER_SNOW 2 -#define STYLE_WEATHER_LEAF 3 -#define STYLE_WEATHER_USER 4 - -void drop_add_to_chain(edict_t *drop) -{ - edict_t *owner = drop->owner; - - if(!owner || !owner->inuse || !(owner->spawnflags & SF_WEATHER_STARTON)) - { - G_FreeEdict(drop); - return; - } - drop->svflags |= SVF_NOCLIENT; - drop->s.renderfx &= ~RF_TRANSLUCENT; - VectorClear(drop->velocity); - VectorClear(drop->avelocity); - gi.linkentity(drop); -} - -void drop_splash(edict_t *drop) -{ - vec3_t up = {0,0,1}; - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_LASER_SPARKS); - WRITE_BYTE (drop->owner->mass2); - WRITE_COORD (drop->s.origin); - WRITE_DIR (up); - WRITE_BYTE (drop->owner->sounds); - MESSAGE_SEND (MSG_PVS, drop->s.origin, NULL); - drop_add_to_chain(drop); -} - -void leaf_fade2(edict_t *ent) -{ - ent->count++; - if (ent->count == 1) - { - ent->nextthink=level.time+0.5; - gi.linkentity(ent); - } - else - drop_add_to_chain(ent); -} - -void leaf_fade (edict_t *ent) -{ - ent->s.renderfx = RF_TRANSLUCENT; - ent->think = leaf_fade2; - ent->nextthink = level.time+0.5; - ent->count = 0; - gi.linkentity(ent); -} - -void drop_touch(edict_t *drop, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - if(drop->owner->spawnflags & SF_WEATHER_START_FADE) - return; - else if(drop->fadeout > 0) - { - if( (drop->spawnflags & SF_WEATHER_GRAVITY_BOUNCE) && (drop->owner->gravity > 0)) - { - drop->movetype = MOVETYPE_DEBRIS; - drop->gravity = drop->owner->gravity; - } - drop->think = leaf_fade; - drop->nextthink = level.time + drop->fadeout; - } - else if(drop->spawnflags & SF_WEATHER_SPLASH) - drop_splash(drop); - else - drop_add_to_chain(drop); -} - -void spawn_precipitation(edict_t *self, vec3_t org, vec3_t dir, float speed) -{ - edict_t *drop; - - drop = G_Spawn(); - if(self->style == STYLE_WEATHER_BIGRAIN) - drop->s.modelindex = gi.modelindex ("models/objects/drop/heavy.md2"); - else if(self->style == STYLE_WEATHER_SNOW) - drop->s.modelindex = gi.modelindex ("models/objects/snow/tris.md2"); - else if(self->style == STYLE_WEATHER_LEAF) - { - float r=random(); - if(r < 0.33) - drop->s.modelindex = gi.modelindex ("models/objects/leaf1/tris.md2"); - else if(r < 0.66) - drop->s.modelindex = gi.modelindex ("models/objects/leaf2/tris.md2"); - else - drop->s.modelindex = gi.modelindex ("models/objects/leaf3/tris.md2"); - VectorSet(drop->mins,-1,-1,-1); - VectorSet(drop->maxs, 1, 1, 1); - } - else if(self->style == STYLE_WEATHER_USER) - drop->s.modelindex = gi.modelindex(self->usermodel); - else - drop->s.modelindex = gi.modelindex ("models/objects/drop/tris.md2"); - drop->classname = "rain drop"; - - if(self->gravity > 0. || self->attenuation > 0 ) - drop->movetype = MOVETYPE_DEBRIS; - else - drop->movetype = MOVETYPE_RAIN; - - drop->touch = drop_touch; - if(self->style == STYLE_WEATHER_USER) - drop->clipmask = MASK_MONSTERSOLID; - else if((self->fadeout > 0) && (self->gravity == 0.)) - drop->clipmask = MASK_SOLID | CONTENTS_WATER; - else - drop->clipmask = MASK_MONSTERSOLID | CONTENTS_WATER; - drop->solid = SOLID_BBOX; - drop->svflags = SVF_DEADMONSTER; - VectorSet(drop->mins,-1,-1,-1); - VectorSet(drop->maxs, 1, 1, 1); - - if(self->spawnflags & SF_WEATHER_GRAVITY_BOUNCE) - drop->gravity = self->gravity; - else - drop->gravity = 0.; - drop->attenuation = self->attenuation; - drop->mass = self->mass; - drop->spawnflags = self->spawnflags; - drop->fadeout = self->fadeout; - drop->owner = self; - - VectorCopy (org, drop->s.origin); - vectoangles(dir, drop->s.angles); - drop->s.angles[PITCH] -= 90; - VectorScale (dir, speed, drop->velocity); - if(self->style == STYLE_WEATHER_LEAF) - { - drop->avelocity[PITCH] = crandom() * 360; - drop->avelocity[YAW] = crandom() * 360; - drop->avelocity[ROLL] = crandom() * 360; - } - else if(self->style == STYLE_WEATHER_USER) - { - drop->s.effects = self->effects; - drop->s.renderfx = self->renderfx; - drop->avelocity[PITCH] = crandom() * self->pitch_speed; - drop->avelocity[YAW] = crandom() * self->yaw_speed; - drop->avelocity[ROLL] = crandom() * self->roll_speed; - } - else - { - drop->avelocity[YAW] = self->yaw_speed; - } - if(self->spawnflags & SF_WEATHER_START_FADE) - { - drop->think = leaf_fade; - drop->nextthink = level.time + self->fadeout; - } - gi.linkentity(drop); -} - -void target_precipitation_think (edict_t *self) -{ - vec3_t center; - vec3_t org; - int r, i; - float u, v, z; - float temp; - bool can_see_me; - - self->nextthink = level.time + FRAMETIME; - - // Don't start raining until player is in the game. The following - // takes care of both initial map load conditions and restored saved games. - // This is a gross abuse of groundentity_linkcount. Sue me. - if(g_edicts[1].linkcount == self->groundentity_linkcount) - return; - else - self->groundentity_linkcount = g_edicts[1].linkcount; - // Don't spawn drops if player can't see us. This SEEMS like an obvious - // thing to do, but can cause visual problems if mapper isn't careful. - // For example, placing target_precipitation where it isn't in the PVS - // of the player's current position, but the result (rain) IS in the - // PVS. In any case, this step is necessary to prevent overflows when - // player suddenly encounters rain. - can_see_me = false; - for(i=1; i<=game.maxclients && !can_see_me; i++) - { - if(!g_edicts[i].inuse) continue; - if(gi.inPVS(g_edicts[i].s.origin,self->s.origin)) - can_see_me = true; - } - if(!can_see_me) return; - - // Count is models/second. We accumulate a probability of a model - // falling this frame in ->density. Yeah its a misnomer but density isn't - // used for anything else so it works fine. - - temp = 0.1*(self->density + crandom()*self->random); - r = (int)(temp); - if(r > 0) - self->density = self->count + (temp-(float)r)*10; - else - self->density += (temp*10); - if(r < 1) return; - - VectorAdd(self->bleft,self->tright,center); - VectorMA(self->s.origin,0.5,center,center); - - for(i=0; itright[0] - self->bleft[0])/2; - v = crandom() * (self->tright[1] - self->bleft[1])/2; - z = crandom() * (self->tright[2] - self->bleft[2])/2; - - VectorCopy(center, org); - - org[0] += u; - org[1] += v; - org[2] += z; - - spawn_precipitation(self, org, self->movedir, self->speed); - } -} - -void target_precipitation_use (edict_t *ent, edict_t *other, edict_t *activator) -{ - if(ent->spawnflags & SF_WEATHER_STARTON) - { - // already on; turn it off - ent->nextthink = 0; - ent->spawnflags &= ~SF_WEATHER_STARTON; - } - else - { - ent->density = ent->count; - ent->think = target_precipitation_think; - ent->spawnflags |= SF_WEATHER_STARTON; - ent->think(ent); - } -} - -void target_precipitation_delayed_use (edict_t *self) -{ - // Since target_precipitation tends to be a processor hog, - // for START_ON we wait until the player has spawned into the - // game to ease the startup burden somewhat - if(g_edicts[1].linkcount) - { - self->think = target_precipitation_think; - self->think(self); - } - else - self->nextthink = level.time + FRAMETIME; -} -void SP_target_precipitation (edict_t *ent) -{ - if(deathmatch->value || coop->value) - { - G_FreeEdict(ent); - return; - } - - ent->class_id = ENTITY_TARGET_PRECIPITATION; - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_NOT; - - if(ent->spawnflags & SF_WEATHER_STARTON) - { - ent->think = target_precipitation_delayed_use; - ent->nextthink = level.time + 1; - } - - if(ent->style == STYLE_WEATHER_USER) - { - char *buffer; - if(!ent->usermodel) - { - gi.dprintf("target_precipitation style=user\nwith no usermodel.\n"); - G_FreeEdict(ent); - return; - } - buffer = (char *)TagMalloc(strlen(ent->usermodel)+10,TAG_LEVEL); - if(strstr(ent->usermodel,".spr")) - sprintf(buffer, "sprites/%s", ent->usermodel); - else - sprintf(buffer, "models/%s", ent->usermodel); - ent->usermodel = buffer; - - if(st.gravity) - ent->gravity = atof(st.gravity); - else - ent->gravity = 0.; - } - else - { - ent->gravity = 0.; - ent->attenuation = 0.; - } - - // If not rain or "user", turn off splash. Yeah I know goofy mapper - // might WANT splash, but we're enforcing good taste here :) - if(ent->style > STYLE_WEATHER_BIGRAIN && ent->style != STYLE_WEATHER_USER) - ent->spawnflags &= ~SF_WEATHER_SPLASH; - - ent->use = target_precipitation_use; - - if(!ent->count) - ent->count = 1; - - if(!ent->sounds) - ent->sounds = 2; // blue splash - - if(!ent->mass2) - ent->mass2 = 8; // 8 particles in splash - - if((ent->style < STYLE_WEATHER_RAIN) || (ent->style > STYLE_WEATHER_USER)) - ent->style = STYLE_WEATHER_RAIN; // single rain drop model - - if(ent->speed <= 0) - { - switch(ent->style) - { - case STYLE_WEATHER_SNOW: ent->speed = 50; break; - case STYLE_WEATHER_LEAF: ent->speed = 50; break; - default: ent->speed = 300; - } - } - - if((VectorLength(ent->bleft) == 0.) && (VectorLength(ent->tright) == 0.)) - { - // Default distribution places raindrops vertically for - // full coverage, to help avoid "lumps" - VectorSet(ent->bleft,-512,-512, -ent->speed*0.05); - VectorSet(ent->tright,512, 512, ent->speed*0.05); - } - - if(VectorLength(ent->s.angles) > 0) - G_SetMovedir(ent->s.angles,ent->movedir); - else - VectorSet(ent->movedir,0,0,-1); - - ent->density = ent->count; - - gi.linkentity (ent); -} - -//============================================================================= -// TARGET_FOUNTAIN is identical to TARGET_PRECIPITATION, with these exceptions: -// ALL styles are "user-defined" (no predefined rain, snow, etc.) -// Models are spawned from a point source, and bleft/tright form a box within -// which the target point is found. -//============================================================================= -void target_fountain_think (edict_t *self) -{ - vec3_t center; - vec3_t org; - vec3_t dir; - int r, i; - float u, v, z; - float temp; - bool can_see_me; - - if(!(self->spawnflags & SF_WEATHER_FIRE_ONCE)) - self->nextthink = level.time + FRAMETIME; - - // Don't start raining until player is in the game. The following - // takes care of both initial map load conditions and restored saved games. - // This is a gross abuse of groundentity_linkcount. Sue me. - if(g_edicts[1].linkcount == self->groundentity_linkcount) - return; - else - self->groundentity_linkcount = g_edicts[1].linkcount; - // Don't spawn drops if player can't see us. This SEEMS like an obvious - // thing to do, but can cause visual problems if mapper isn't careful. - // For example, placing target_precipitation where it isn't in the PVS - // of the player's current position, but the result (rain) IS in the - // PVS. In any case, this step is necessary to prevent overflows when - // player suddenly encounters rain. - can_see_me = false; - for(i=1; i<=game.maxclients && !can_see_me; i++) - { - if(!g_edicts[i].inuse) continue; - if(gi.inPVS(g_edicts[i].s.origin,self->s.origin)) - can_see_me = true; - } - if(!can_see_me) return; - - // Count is models/second. We accumulate a probability of a model - // falling this frame in ->density. Yeah its a misnomer but density isn't - // used for anything else so it works fine. - - temp = 0.1*(self->density + crandom()*self->random); - r = (int)(temp); - if(r > 0) - self->density = self->count; - else - self->density += (temp*10); - if(r < 1) return; - - VectorAdd(self->bleft,self->tright,center); - VectorMA(self->s.origin,0.5,center,center); - - for(i=0; itright[0] - self->bleft[0])/2; - v = crandom() * (self->tright[1] - self->bleft[1])/2; - z = crandom() * (self->tright[2] - self->bleft[2])/2; - - VectorCopy(center, org); - - org[0] += u; - org[1] += v; - org[2] += z; - VectorSubtract(org,self->s.origin,dir); - VectorNormalize(dir); - - spawn_precipitation(self, self->s.origin, dir, self->speed); - } -} - -void target_fountain_use (edict_t *ent, edict_t *other, edict_t *activator) -{ - if((ent->spawnflags & SF_WEATHER_STARTON) && !(ent->spawnflags & SF_WEATHER_FIRE_ONCE)) - { - // already on; turn it off - ent->nextthink = 0; - ent->spawnflags &= ~SF_WEATHER_STARTON; - } - else - { - ent->density = ent->count; - ent->think = target_fountain_think; - ent->spawnflags |= SF_WEATHER_STARTON; - ent->think(ent); - } -} - -void target_fountain_delayed_use (edict_t *self) -{ - // Since target_fountain tends to be a processor hog, - // for START_ON we wait until the player has spawned into the - // game to ease the startup burden somewhat - if(g_edicts[1].linkcount) - { - self->think = target_fountain_think; - self->think(self); - } - else - self->nextthink = level.time + FRAMETIME; -} -void SP_target_fountain (edict_t *ent) -{ - char *buffer; - - if(deathmatch->value || coop->value) - { - G_FreeEdict(ent); - return; - } - - ent->class_id = ENTITY_TARGET_FOUNTAIN; - ent->movetype = MOVETYPE_NONE; - ent->solid = SOLID_NOT; - - if(ent->spawnflags & SF_WEATHER_STARTON) - { - ent->think = target_fountain_delayed_use; - ent->nextthink = level.time + 1; - } - - ent->style = STYLE_WEATHER_USER; - if(!ent->usermodel) - { - gi.dprintf("target_fountain with no usermodel.\n"); - G_FreeEdict(ent); - return; - } - buffer = (char *)TagMalloc(strlen(ent->usermodel)+10,TAG_LEVEL); - if(strstr(ent->usermodel,".spr")) - sprintf(buffer, "sprites/%s", ent->usermodel); - else - sprintf(buffer, "models/%s", ent->usermodel); - ent->usermodel = buffer; - - if(st.gravity) - ent->gravity = atof(st.gravity); - else - ent->gravity = 0.; - - ent->use = target_fountain_use; - - if(!ent->count) - ent->count = 1; - - if(!ent->sounds) - ent->sounds = 2; // blue splash - - if(!ent->mass2) - ent->mass2 = 8; // 8 particles in splash - - if(ent->speed <= 0) - ent->speed = 300; - - if((VectorLength(ent->bleft) == 0.) && (VectorLength(ent->tright) == 0.)) - { - // Default distribution places raindrops vertically for - // full coverage, to help avoid "lumps" - VectorSet(ent->bleft,-32, -32, 64); - VectorSet(ent->tright,32, 32,128); - } - - ent->density = ent->count; - - gi.linkentity (ent); -} - -void PrecacheDebris(int type) -{ - switch(type) - { - case GIB_ROCK: gi.modelindex("models/gibs/rock.mdl"); break; - case GIB_CRATE: gi.modelindex("models/gibs/wood.mdl"); break; - case GIB_GLASS: gi.modelindex("models/gibs/glass.mdl"); break; - case GIB_METAL: gi.modelindex("models/gibs/metal.mdl"); break; - case GIB_BARREL: gi.modelindex("models/gibs/barrelgib.mdl"); break; - case GIB_MECH: gi.modelindex("models/gibs/flesh.mdl"); break; - case GIB_WOOD: gi.modelindex("models/gibs/wood.mdl"); break; - case GIB_TECH: gi.modelindex("models/gibs/computer.mdl"); break; - default: gi.modelindex("models/gibs/cinder.mdl"); break; - } -} diff --git a/server/g_phys.c b/server/g_phys.c deleted file mode 100644 index fb4fe97d..00000000 --- a/server/g_phys.c +++ /dev/null @@ -1,3158 +0,0 @@ -// g_phys.c - -#include "baseentity.h" - -bool wasonground; -bool onconveyor; -edict_t *blocker; -/* - - -pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move. - -onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects - -doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH -bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS -corpses are SOLID_NOT and MOVETYPE_TOSS -crates are SOLID_BBOX and MOVETYPE_TOSS -walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP -flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY - -solid_edge items only clip against bsp models. - -*/ - -#define STEPSIZE 18 - -/* -============= -M_CheckBottom - -Returns false if any part of the bottom of the entity is off an edge that -is not a staircase. - -============= -*/ -int c_yes, c_no; - -bool M_CheckBottom (edict_t *ent) -{ - vec3_t mins, maxs, start, stop; - trace_t trace; - int x, y; - float mid, bottom; - - VectorAdd (ent->s.origin, ent->mins, mins); - VectorAdd (ent->s.origin, ent->maxs, maxs); - -// if all of the points under the corners are solid world, don't bother -// with the tougher checks -// the corners must be within 16 of the midpoint - start[2] = mins[2] - 1; - for (x=0 ; x<=1 ; x++) - for (y=0 ; y<=1 ; y++) - { - start[0] = x ? maxs[0] : mins[0]; - start[1] = y ? maxs[1] : mins[1]; - if (gi.pointcontents (start) != CONTENTS_SOLID) - goto realcheck; - } - - c_yes++; - return true; // we got out easy - -realcheck: - c_no++; -// -// check it for real... -// - start[2] = mins[2]; - -// the midpoint must be within 16 of the bottom - start[0] = stop[0] = (mins[0] + maxs[0])*0.5; - start[1] = stop[1] = (mins[1] + maxs[1])*0.5; - stop[2] = start[2] - 2*STEPSIZE; - trace = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID); - - if (trace.fraction == 1.0) - return false; - mid = bottom = trace.endpos[2]; - -// the corners must be within 16 of the midpoint - for (x=0 ; x<=1 ; x++) - for (y=0 ; y<=1 ; y++) - { - start[0] = stop[0] = x ? maxs[0] : mins[0]; - start[1] = stop[1] = y ? maxs[1] : mins[1]; - - trace = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID); - - if (trace.fraction != 1.0 && trace.endpos[2] > bottom) - bottom = trace.endpos[2]; - if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE) - return false; - } - - c_yes++; - return true; -} - -/* -============= -SV_movestep - -Called by monster program code. -The move will be adjusted for slopes and stairs, but if the move isn't -possible, no move is done, false is returned, and -pr_global_struct->trace_normal is set to the normal of the blocking wall -============= -*/ -//FIXME since we need to test end position contents here, can we avoid doing -//it again later in catagorize position? -bool SV_movestep (edict_t *ent, vec3_t move, bool relink) -{ - float dz; - vec3_t oldorg, neworg, end; - trace_t trace; - int i; - float stepsize; - float jumpheight; - vec3_t test; - int contents; - - bool canjump; - float d1, d2; - int jump; // 1=jump up, -1=jump down - vec3_t forward, up; - vec3_t dir; - vec_t dist; - edict_t *target; - - // try the move - VectorCopy (ent->s.origin, oldorg); - VectorAdd (ent->s.origin, move, neworg); - - AngleVectors(ent->s.angles,forward,NULL,up); - if(ent->enemy) - target = ent->enemy; - else if(ent->movetarget) - target = ent->movetarget; - else - target = NULL; - - // flying monsters don't step up - if ( ent->flags & (FL_SWIM | FL_FLY) ) - { - // try one move with vertical motion, then one without - for (i=0 ; i<2 ; i++) - { - VectorAdd (ent->s.origin, move, neworg); - if (i == 0 && ent->enemy) - { - if (!ent->goalentity) - ent->goalentity = ent->enemy; - dz = ent->s.origin[2] - ent->goalentity->s.origin[2]; - if (ent->goalentity->client) - { - if (dz > 40) - neworg[2] -= 8; - if (!((ent->flags & FL_SWIM) && (ent->waterlevel < 2))) - if (dz < 30) - neworg[2] += 8; - } - else - { - if (dz > 8) - neworg[2] -= 8; - else if (dz > 0) - neworg[2] -= dz; - else if (dz < -8) - neworg[2] += 8; - else - neworg[2] += dz; - } - } - trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, neworg, ent, MASK_MONSTERSOLID); - - // fly monsters don't enter water voluntarily - if (ent->flags & FL_FLY) - { - if (!ent->waterlevel) - { - test[0] = trace.endpos[0]; - test[1] = trace.endpos[1]; - test[2] = trace.endpos[2] + ent->mins[2] + 1; - contents = gi.pointcontents(test); - if (contents & MASK_WATER) - return false; - } - } - - // swim monsters don't exit water voluntarily - if (ent->flags & FL_SWIM) - { - if (ent->waterlevel < 2) - { - test[0] = trace.endpos[0]; - test[1] = trace.endpos[1]; - test[2] = trace.endpos[2] + ent->mins[2] + 1; - contents = gi.pointcontents(test); - if (!(contents & MASK_WATER)) - return false; - } - } - - if (trace.fraction == 1) - { - VectorCopy (trace.endpos, ent->s.origin); - if (relink) - { - gi.linkentity (ent); - G_TouchTriggers (ent); - } - return true; - } - - if (!ent->enemy) - break; - } - - return false; - } - - // push down from a step height above the wished position - if (!(ent->monsterinfo.aiflags & AI_NOSTEP)) - stepsize = STEPSIZE; - else - stepsize = 1; - - neworg[2] += stepsize; - VectorCopy (neworg, end); - end[2] -= stepsize*2; - - trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID); - - // Determine whether monster is capable of and/or should jump - jump = 0; - if((ent->monsterinfo.jump) && !(ent->monsterinfo.aiflags & AI_DUCKED)) - { - // Don't jump if path is blocked by monster or player. Otherwise, - // monster might attempt to jump OVER the monster/player, which - // ends up looking a bit goofy. Also don't jump if the monster's - // movement isn't deliberate (target=NULL) - if(trace.ent && (trace.ent->client || (trace.ent->svflags & SVF_MONSTER))) - canjump = false; - else if(target) - { - // Never jump unless it places monster closer to his goal - vec3_t dir; - VectorSubtract(target->s.origin, oldorg, dir); - d1 = VectorLength(dir); - VectorSubtract(target->s.origin, trace.endpos, dir); - d2 = VectorLength(dir); - if(d2 < d1) - canjump = true; - else - canjump = false; - } - else - canjump = false; - } - else - canjump = false; - - if (trace.allsolid) - { - if(canjump && (ent->monsterinfo.jumpup > 0)) - { - neworg[2] += ent->monsterinfo.jumpup - stepsize; - trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID); - if (!trace.allsolid && !trace.startsolid && trace.fraction > 0 && (trace.plane.normal[2] > 0.9)) - { - if(!trace.ent || (!trace.ent->client && !(trace.ent->svflags & SVF_MONSTER) && !(trace.ent->svflags & SVF_DEADMONSTER))) - { - // Good plane to jump on. Make sure monster is more or less facing - // the obstacle to avoid cutting-corners jumps - trace_t tr; - vec3_t p2; - - VectorMA(ent->s.origin,1024,forward,p2); - tr = gi.trace(ent->s.origin,ent->mins,ent->maxs,p2,ent,MASK_MONSTERSOLID); - if(DotProduct(tr.plane.normal,forward) < -0.95) - { - jump = 1; - jumpheight = trace.endpos[2] - ent->s.origin[2]; - } - else - return false; - } - } - else - return false; - } - else - return false; - } - - if (trace.startsolid) - { - neworg[2] -= stepsize; - trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID); - if (trace.allsolid || trace.startsolid) - return false; - } - - - // don't go in to water - // Lazarus: misc_actors don't go swimming, but wading is fine - if (ent->monsterinfo.aiflags & AI_ACTOR) - { - // First check for lava/slime under feet - but only if we're not already in - // a liquid - test[0] = trace.endpos[0]; - test[1] = trace.endpos[1]; - if (ent->waterlevel == 0) - { - test[2] = trace.endpos[2] + ent->mins[2] + 1; - contents = gi.pointcontents(test); - if (contents & (CONTENTS_LAVA | CONTENTS_SLIME)) - return false; - } - test[2] = trace.endpos[2] + ent->viewheight - 1; - contents = gi.pointcontents(test); - if (contents & MASK_WATER) - return false; - } - else if (ent->waterlevel == 0) - { - test[0] = trace.endpos[0]; - test[1] = trace.endpos[1]; - test[2] = trace.endpos[2] + ent->mins[2] + 1; - contents = gi.pointcontents(test); - - if (contents & MASK_WATER) - return false; - } - - // Lazarus: Don't intentionally walk into lasers. - dist = VectorLength(move); - if(dist > 0.) - { - edict_t *e; - trace_t laser_trace; - vec_t delta; - vec3_t laser_mins, laser_maxs; - vec3_t laser_start, laser_end; - vec3_t monster_mins, monster_maxs; - - for(i=game.maxclients+1; iinuse) - continue; - if(!e->classname) - continue; - if(e->class_id != ENTITY_TARGET_LASER) - continue; - if(e->svflags & SVF_NOCLIENT) - continue; - if( (e->style == 2) || (e->style == 3)) - continue; - if(!gi.inPVS(ent->s.origin,e->s.origin)) - continue; - // Check to see if monster is ALREADY in the path of this laser. - // If so, allow the move so he can get out. - VectorMA(e->s.origin,2048,e->movedir,laser_end); - laser_trace = gi.trace(e->s.origin,NULL,NULL,laser_end,NULL,CONTENTS_SOLID|CONTENTS_MONSTER); - if(laser_trace.ent == ent) - continue; - VectorCopy(laser_trace.endpos,laser_end); - laser_mins[0] = min(e->s.origin[0],laser_end[0]); - laser_mins[1] = min(e->s.origin[1],laser_end[1]); - laser_mins[2] = min(e->s.origin[2],laser_end[2]); - laser_maxs[0] = max(e->s.origin[0],laser_end[0]); - laser_maxs[1] = max(e->s.origin[1],laser_end[1]); - laser_maxs[2] = max(e->s.origin[2],laser_end[2]); - monster_mins[0] = min(oldorg[0],trace.endpos[0]) + ent->mins[0]; - monster_mins[1] = min(oldorg[1],trace.endpos[1]) + ent->mins[1]; - monster_mins[2] = min(oldorg[2],trace.endpos[2]) + ent->mins[2]; - monster_maxs[0] = max(oldorg[0],trace.endpos[0]) + ent->maxs[0]; - monster_maxs[1] = max(oldorg[1],trace.endpos[1]) + ent->maxs[1]; - monster_maxs[2] = max(oldorg[2],trace.endpos[2]) + ent->maxs[2]; - if( monster_maxs[0] < laser_mins[0] ) continue; - if( monster_maxs[1] < laser_mins[1] ) continue; - if( monster_maxs[2] < laser_mins[2] ) continue; - if( monster_mins[0] > laser_maxs[0] ) continue; - if( monster_mins[1] > laser_maxs[1] ) continue; - if( monster_mins[2] > laser_maxs[2] ) continue; - // If we arrive here, some part of the bounding box surrounding - // monster's total movement intersects laser bounding box. - // If laser is parallel to x, y, or z, we definitely - // know this move will put monster in path of laser - if ( (e->movedir[0] == 1.) || (e->movedir[1] == 1.) || (e->movedir[2] == 1.)) - return false; - // Shift psuedo laser towards monster's current position up to - // the total distance he's proposing moving. - delta = min(16,dist); - VectorCopy(move,dir); - VectorNormalize(dir); - while(delta < dist+15.875) - { - if(delta > dist) delta = dist; - VectorMA(e->s.origin, -delta,dir,laser_start); - VectorMA(e->s.old_origin,-delta,dir,laser_end); - laser_trace = gi.trace(laser_start,NULL,NULL,laser_end,world,CONTENTS_SOLID|CONTENTS_MONSTER); - if(laser_trace.ent == ent) - return false; - delta += 16; - } - } - } - if ((trace.fraction == 1) && !jump && canjump && (ent->monsterinfo.jumpdn > 0)) - { - end[2] = oldorg[2] + move[2] - ent->monsterinfo.jumpdn; - trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID | MASK_WATER); - if(trace.fraction < 1 && (trace.plane.normal[2] > 0.9) && (trace.contents & MASK_SOLID) && (neworg[2] - 16 > trace.endpos[2])) - { - if(!trace.ent || (!trace.ent->client && !(trace.ent->svflags & SVF_MONSTER) && !(trace.ent->svflags & SVF_DEADMONSTER))) - jump = -1; - } - } - - - if ((trace.fraction == 1) && !jump) - { - // if monster had the ground pulled out, go ahead and fall - if ( ent->flags & FL_PARTIALGROUND ) - { - VectorAdd (ent->s.origin, move, ent->s.origin); - if (relink) - { - gi.linkentity (ent); - G_TouchTriggers (ent); - } - ent->groundentity = NULL; - return true; - } - return false; // walked off an edge - } - - // check point traces down for dangling corners - VectorCopy (trace.endpos, ent->s.origin); - - if(!jump) - { - bool skip = false; - // if monster CAN jump down, and a position just a bit forward would be - // a good jump-down spot, allow (briefly) !M_CheckBottom - if (canjump && target && (target->s.origin[2] < ent->s.origin[2]) && (ent->monsterinfo.jumpdn > 0)) - { - vec3_t p1, p2; - trace_t tr; - - VectorMA(oldorg,48,forward,p1); - tr = gi.trace(ent->s.origin, ent->mins, ent->maxs, p1, ent, MASK_MONSTERSOLID); - if(tr.fraction == 1) - { - p2[0] = p1[0]; - p2[1] = p1[1]; - p2[2] = p1[2] - ent->monsterinfo.jumpdn; - tr = gi.trace(p1,ent->mins,ent->maxs,p2,ent,MASK_MONSTERSOLID | MASK_WATER); - if(tr.fraction < 1 && (tr.plane.normal[2] > 0.9) && (tr.contents & MASK_SOLID) && (p1[2] - 16 > tr.endpos[2])) - { - if(!tr.ent || (!tr.ent->client && !(tr.ent->svflags & SVF_MONSTER) && !(tr.ent->svflags & SVF_DEADMONSTER))) - { - VectorSubtract(target->s.origin, tr.endpos, dir); - d2 = VectorLength(dir); - if(d2 < d1) - skip = true; - } - } - } - - } - if (!skip) - { - if (!M_CheckBottom (ent)) - { - if ( ent->flags & FL_PARTIALGROUND ) - { // entity had floor mostly pulled out from underneath it - // and is trying to correct - if (relink) - { - gi.linkentity (ent); - G_TouchTriggers (ent); - } - return true; - } - VectorCopy (oldorg, ent->s.origin); - return false; - } - } - } - - if ( ent->flags & FL_PARTIALGROUND ) - { - ent->flags &= ~FL_PARTIALGROUND; - } - ent->groundentity = trace.ent; - if(trace.ent) - ent->groundentity_linkcount = trace.ent->linkcount; - -// the move is ok - if(jump) - { - VectorScale(move, 10, ent->velocity); - if(jump > 0) - { - ent->monsterinfo.jump(ent); - ent->velocity[2] = 2.5*jumpheight + 80; - } - else - { - ent->velocity[2] = max(ent->velocity[2],100); - if(oldorg[2] - ent->s.origin[2] > 48) - ent->s.origin[2] = oldorg[2] + ent->velocity[2]*FRAMETIME; - } - if(relink) - { - gi.linkentity (ent); - G_TouchTriggers (ent); - } - } - else if (relink) - { - gi.linkentity (ent); - G_TouchTriggers (ent); - } - return true; -} - - -/* -=============== -M_walkmove -=============== -*/ -bool M_walkmove (edict_t *ent, float yaw, float dist) -{ - vec3_t move; - - if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM))) - return false; - - yaw = yaw*M_PI*2 / 360; - - move[0] = cos(yaw)*dist; - move[1] = sin(yaw)*dist; - move[2] = 0; - - return SV_movestep(ent, move, true); -} - -// -//================= -// other_FallingDamage -// Identical to player's P_FallingDamage... except of course ent doesn't have to be a player -//================= -// -void other_FallingDamage (edict_t *ent) -{ - float delta; - float fall_time, fall_value; - int damage; - vec3_t dir; - - if (ent->movetype == MOVETYPE_NOCLIP) - return; - - if ((ent->oldvelocity[2] < 0) && (ent->velocity[2] > ent->oldvelocity[2]) && (!ent->groundentity)) - { - delta = ent->oldvelocity[2]; - } - else - { - if (!ent->groundentity) - return; - delta = ent->velocity[2] - ent->oldvelocity[2]; - } - delta = delta*delta * 0.0001; - - // never take falling damage if completely underwater - if (ent->waterlevel == 3) - return; - if (ent->waterlevel == 2) - delta *= 0.25; - if (ent->waterlevel == 1) - delta *= 0.5; - - if (delta < 1) - return; - - if (delta < 15) - { - ent->s.event = EV_FOOTSTEP; - return; - } - - fall_value = delta*0.5; - if (fall_value > 40) fall_value = 40; - fall_time = level.time + FALL_TIME; - - if (delta > 30) - { - ent->pain_debounce_time = level.time; // no normal pain sound - damage = (delta-30)/2; - if (damage < 1) - damage = 1; - VectorSet (dir, 0, 0, 1); - - if (!deathmatch->value || !((int)dmflags->value & DF_NO_FALLING) ) - T_Damage (ent, world, world, dir, ent->s.origin, vec3_origin, damage, 0, 0, MOD_FALLING); - } - else - { - // ent->s.event = EV_FALLSHORT; - return; - } -} - -/* -============ -SV_TestEntityPosition - -============ -*/ -edict_t *SV_TestEntityPosition (edict_t *ent) -{ - trace_t trace; - int mask; - - if (ent->clipmask) - mask = ent->clipmask; - else - mask = MASK_SOLID; - if(ent->solid == SOLID_BSP) - { - vec3_t org, mins, maxs; - VectorAdd(ent->s.origin,ent->origin_offset,org); - VectorSubtract(ent->mins,ent->origin_offset,mins); - VectorSubtract(ent->maxs,ent->origin_offset,maxs); - trace = gi.trace (org, mins, maxs, org, ent, mask); - } - else - trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, ent, mask); - - if (trace.startsolid) - { - // Lazarus - work around for players/monsters standing on dead monsters causing - // those monsters to gib when rotating brush models are in the vicinity - if ( (ent->svflags & SVF_DEADMONSTER) && (trace.ent->client || (trace.ent->svflags & SVF_MONSTER))) - return NULL; - - // Lazarus - return a bit more useful info than simply "g_edicts" - if(trace.ent) - return trace.ent; - else - return world; - } - - return NULL; -} - - -/* -================ -SV_CheckVelocity -================ -*/ -void SV_CheckVelocity (edict_t *ent) -{ - // Lazarus: This is a pretty goofy way to bound velocity. This has the effect - // of changing directions, which makes no sense at all. Maybe they - // were just avoiding a sqrt? -/* - int i; - -// -// bound velocity -// - for (i=0 ; i<3 ; i++) - { - if (ent->velocity[i] > sv_maxvelocity->value) - ent->velocity[i] = sv_maxvelocity->value; - else if (ent->velocity[i] < -sv_maxvelocity->value) - ent->velocity[i] = -sv_maxvelocity->value; - } */ - if (VectorLength(ent->velocity) > sv_maxvelocity->value) - { - VectorNormalize(ent->velocity); - VectorScale(ent->velocity, sv_maxvelocity->value, ent->velocity); - } -} - -/* -============= -SV_RunThink - -Runs thinking code for this frame if necessary -============= -*/ -bool SV_RunThink (edict_t *ent) -{ - float thinktime; - - thinktime = ent->nextthink; - if (thinktime <= 0) - return true; - if (thinktime > level.time+0.001) - return true; - - ent->nextthink = 0; - if (!ent->think) - gi.error ("NULL ent->think for %s",ent->classname); - ent->think (ent); - - return false; -} - -/* -================== -SV_Impact - -Two entities have touched, so run their touch functions -================== -*/ -void SV_Impact (edict_t *e1, trace_t *trace) -{ - edict_t *e2; -// cplane_t backplane; - - e2 = trace->ent; - - if (e1->touch && e1->solid != SOLID_NOT) - e1->touch (e1, e2, &trace->plane, trace->surface); - - if (e2->touch && e2->solid != SOLID_NOT) - e2->touch (e2, e1, NULL, NULL); -} - - -/* -================== -ClipVelocity - -Slide off of the impacting object -returns the blocked flags (1 = floor, 2 = step / wall) -================== -*/ -#define STOP_EPSILON 0.1 - -int ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce) -{ - float backoff; - float change; - int i, blocked; - - blocked = 0; - if (normal[2] > 0) - blocked |= 1; // floor - if (!normal[2]) - blocked |= 2; // step - - backoff = DotProduct (in, normal) * overbounce; - - for (i=0 ; i<3 ; i++) - { - change = normal[i]*backoff; - out[i] = in[i] - change; - if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) - out[i] = 0; - } - - return blocked; -} - -/* -============ -SV_FlyMove - -The basic solid body movement clip that slides along multiple planes -Returns the clipflags if the velocity was modified (hit something solid) -1 = floor -2 = wall / step -4 = dead stop -============ -*/ -#define MAX_CLIP_PLANES 5 -int SV_FlyMove (edict_t *ent, float time, int mask) -{ - edict_t *hit; - int bumpcount, numbumps; - vec3_t dir; - float d; - int numplanes; - vec3_t planes[MAX_CLIP_PLANES]; - vec3_t primal_velocity, original_velocity, new_velocity; - int i, j; - trace_t trace; - vec3_t end; - float time_left; - int blocked; - int num_retries = 0; - -retry: - numbumps = 4; - - blocked = 0; - VectorCopy (ent->velocity, original_velocity); - VectorCopy (ent->velocity, primal_velocity); - numplanes = 0; - - time_left = time; - - ent->groundentity = NULL; - for (bumpcount=0 ; bumpcounts.origin[i] + time_left * ent->velocity[i]; - - trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, end, ent, mask); - - if (trace.allsolid) - { // entity is trapped in another solid - VectorCopy (vec3_origin, ent->velocity); - return 3; - } - - if (trace.fraction > 0) - { // actually covered some distance - VectorCopy (trace.endpos, ent->s.origin); - VectorCopy (ent->velocity, original_velocity); - numplanes = 0; - } - - if (trace.fraction == 1) - break; // moved the entire distance - - blocker = hit = trace.ent; - - // Lazarus: If the pushed entity is a conveyor, raise us up and - // try again - if (!num_retries && wasonground) - { - if ((hit->movetype == MOVETYPE_CONVEYOR) && (trace.plane.normal[2] > 0.7)) - { - vec3_t above; - - VectorCopy(end,above); - above[2] += 32; - trace = gi.trace (above, ent->mins, ent->maxs, end, ent, mask); - VectorCopy (trace.endpos,end); - end[2] += 1; - VectorSubtract (end,ent->s.origin,ent->velocity); - VectorScale (ent->velocity,1.0/time_left,ent->velocity); - num_retries++; - goto retry; - } - } - - // if blocked by player AND on a conveyor - if (hit && hit->client && onconveyor) - { - vec3_t player_dest; - trace_t ptrace; - - if(ent->mass > hit->mass) - { - VectorMA (hit->s.origin,time_left,ent->velocity,player_dest); - ptrace = gi.trace(hit->s.origin,hit->mins,hit->maxs,player_dest,hit,hit->clipmask); - if(ptrace.fraction == 1.0) - { - VectorCopy(player_dest,hit->s.origin); - gi.linkentity(hit); - goto retry; - } - } - blocked |= 8; - } - - if (trace.plane.normal[2] > 0.7) - { - blocked |= 1; // floor - if ( hit->solid == SOLID_BSP) - { - ent->groundentity = hit; - ent->groundentity_linkcount = hit->linkcount; - } - } - if (!trace.plane.normal[2]) - { - blocked |= 2; // step - } - -// -// run the impact function -// - SV_Impact (ent, &trace); - if (!ent->inuse) - break; // removed by the impact function - - - time_left -= time_left * trace.fraction; - - // cliped to another plane - if (numplanes >= MAX_CLIP_PLANES) - { // this shouldn't really happen - VectorCopy (vec3_origin, ent->velocity); - blocked |= 3; - return blocked; - } - - VectorCopy (trace.plane.normal, planes[numplanes]); - numplanes++; - -// -// modify original_velocity so it parallels all of the clip planes -// - for (i=0 ; ivelocity); - } - else - { // go along the crease - if (numplanes != 2) - { -// gi.dprintf ("clip velocity, numplanes == %i\n",numplanes); - VectorCopy (vec3_origin, ent->velocity); - blocked |= 7; - return blocked; - } - CrossProduct (planes[0], planes[1], dir); - d = DotProduct (dir, ent->velocity); - VectorScale (dir, d, ent->velocity); - } - -// -// if original velocity is against the original velocity, stop dead -// to avoid tiny occilations in sloping corners -// - if (DotProduct (ent->velocity, primal_velocity) <= 0) - { - VectorCopy (vec3_origin, ent->velocity); - return blocked; - } - } - - return blocked; -} - -/* -============ -SV_PushableMove - -The basic solid body movement clip that slides along multiple planes -Returns the clipflags if the velocity was modified (hit something solid) -1 = floor -2 = wall / step -4 = dead stop -============ -*/ -#define MAX_CLIP_PLANES 5 -int SV_PushableMove (edict_t *ent, float time, int mask) -{ - edict_t *hit; - int bumpcount, numbumps; - vec3_t dir; - float d; - int numplanes; - vec3_t planes[MAX_CLIP_PLANES]; - vec3_t primal_velocity, original_velocity, new_velocity; - int i, j; - trace_t trace; - vec3_t end; - float time_left; - int blocked; - int num_retries=0; - - // Corrective stuff added for bmodels with no origin brush - vec3_t mins, maxs; - vec3_t origin; - -retry: - - numbumps = 4; - ent->bounce_me = 0; - - blocked = 0; - VectorCopy (ent->velocity, original_velocity); - VectorCopy (ent->velocity, primal_velocity); - numplanes = 0; - - time_left = time; - - VectorAdd(ent->s.origin,ent->origin_offset,origin); - VectorCopy(ent->size,maxs); - VectorScale(maxs,0.5,maxs); - VectorNegate(maxs,mins); - - ent->groundentity = NULL; - - for (bumpcount=0 ; bumpcountvelocity[i]; - - trace = gi.trace (origin, mins, maxs, end, ent, mask); - - if (trace.allsolid) - { // entity is trapped in another solid - VectorCopy (vec3_origin, ent->velocity); - return 3; - } - - if (trace.fraction > 0) - { // actually covered some distance - VectorCopy (trace.endpos, origin); - VectorSubtract (origin, ent->origin_offset, ent->s.origin); - VectorCopy (ent->velocity, original_velocity); - numplanes = 0; - } - - if (trace.fraction == 1) - break; // moved the entire distance - - blocker = hit = trace.ent; - - // Lazarus: If the pushed entity is a conveyor, raise us up and - // try again - if (!num_retries && wasonground) - { - if ((hit->movetype == MOVETYPE_CONVEYOR) && (trace.plane.normal[2] > 0.7)) - { - vec3_t above; - - VectorCopy(end,above); - above[2] += 32; - trace = gi.trace (above, mins, maxs, end, ent, mask); - VectorCopy (trace.endpos,end); - VectorSubtract (end,origin,ent->velocity); - VectorScale (ent->velocity,1.0/time_left,ent->velocity); - num_retries++; - goto retry; - } - } - - // if blocked by player AND on a conveyor - if (hit->client && onconveyor) - { - vec3_t player_dest; - trace_t ptrace; - - if(ent->mass > hit->mass) - { - VectorMA (hit->s.origin,time_left,ent->velocity,player_dest); - ptrace = gi.trace(hit->s.origin,hit->mins,hit->maxs,player_dest,hit,hit->clipmask); - if(ptrace.fraction == 1.0) - { - VectorCopy(player_dest,hit->s.origin); - gi.linkentity(hit); - goto retry; - } - } - blocked |= 8; - } - - if (trace.plane.normal[2] > 0.7) - { - // Lazarus: special case - if this ent or the impact ent is - // in water, motion is NOT blocked. - if((hit->movetype != MOVETYPE_PUSHABLE) || ((ent->waterlevel==0) && (hit->waterlevel==0))) - { - blocked |= 1; // floor - if ( hit->solid == SOLID_BSP) - { - ent->groundentity = hit; - ent->groundentity_linkcount = hit->linkcount; - } - } - } - if (!trace.plane.normal[2]) - { - blocked |= 2; // step - } -// -// run the impact function -// - SV_Impact (ent, &trace); - if (!ent->inuse) - break; // removed by the impact function - - time_left -= time_left * trace.fraction; - - // clipped to another plane - if (numplanes >= MAX_CLIP_PLANES) - { // this shouldn't really happen - VectorCopy (vec3_origin, ent->velocity); - blocked |= 3; - return blocked; - } - - VectorCopy (trace.plane.normal, planes[numplanes]); - numplanes++; - -// -// modify original_velocity so it parallels all of the clip planes -// - for (i=0 ; ibounce_me == 1) { - ClipVelocity (original_velocity, planes[i], new_velocity, 1.4); - // stop small oscillations - if (new_velocity[2] < 60) - { - ent->groundentity = trace.ent; - ent->groundentity_linkcount = trace.ent->linkcount; - VectorCopy (vec3_origin, new_velocity); - } else { - // add a bit of random horizontal motion - if(!new_velocity[0]) new_velocity[0] = crandom() * new_velocity[2]/4; - if(!new_velocity[1]) new_velocity[1] = crandom() * new_velocity[2]/4; - } - } - else if(ent->bounce_me == 2) - { - VectorCopy(ent->velocity,new_velocity); - } - else - { - ClipVelocity (original_velocity, planes[i], new_velocity, 1); - } - for (j=0 ; jvelocity); - } - else - { // go along the crease - if (numplanes != 2) - { - VectorCopy (vec3_origin, ent->velocity); - blocked |= 7; - return blocked; - } - CrossProduct (planes[0], planes[1], dir); - d = DotProduct (dir, ent->velocity); - VectorScale (dir, d, ent->velocity); - } - -// -// if velocity is against the original velocity, stop dead -// to avoid tiny occilations in sloping corners -// - if( !ent->bounce_me ) { - if (DotProduct (ent->velocity, primal_velocity) <= 0) - { - VectorCopy (vec3_origin, ent->velocity); - return blocked; - } - } - } - return blocked; -} - -/* -============ -SV_AddGravity - -============ -*/ -void SV_AddGravity (edict_t *ent) -{ - if(level.time > ent->gravity_debounce_time) - ent->velocity[2] -= ent->gravity * sv_gravity->value * FRAMETIME; -} - -/* -=============================================================================== - -PUSHMOVE - -=============================================================================== -*/ - -/* -================================================= -SV_PushEntity - -Does not change the entities velocity at all - -called for MOVETYPE_TOSS - MOVETYPE_BOUNCE - MOVETYPE_FLY - MOVETYPE_FLYMISSILE - MOVETYPE_RAIN -================================================= -*/ -trace_t SV_PushEntity (edict_t *ent, vec3_t push) -{ - trace_t trace; - vec3_t start; - vec3_t end; - int mask; - int num_retries=0; - - VectorCopy (ent->s.origin, start); - VectorAdd (start, push, end); - - if (ent->clipmask) - mask = ent->clipmask; - else - mask = MASK_SOLID; - -retry: - trace = gi.trace (start, ent->mins, ent->maxs, end, ent, mask); - - VectorCopy (trace.endpos, ent->s.origin); - gi.linkentity (ent); - - if (trace.fraction != 1.0) - { - SV_Impact (ent, &trace); - - // if the pushed entity went away and the pusher is still there - if (!trace.ent->inuse && ent->inuse) - { - // move the pusher back and try again - VectorCopy (start, ent->s.origin); - gi.linkentity (ent); - goto retry; - } - - // Lazarus: If the pushed entity is a conveyor, raise us up and - // try again - if (!num_retries && wasonground) - { - if ((trace.ent->movetype == MOVETYPE_CONVEYOR) && (trace.plane.normal[2] > 0.7) && !trace.startsolid) - { - vec3_t above; - VectorCopy(end,above); - above[2] += 32; - trace = gi.trace (above, ent->mins, ent->maxs, end, ent, mask); - VectorCopy (trace.endpos, end); - VectorCopy (start, ent->s.origin); - gi.linkentity(ent); - num_retries++; - goto retry; - } - } - if(onconveyor && !trace.ent->client) - { - // If blocker can be damaged, destroy it. Otherwise destroy blockee. - if(trace.ent->takedamage == DAMAGE_YES) - T_Damage(trace.ent, ent, ent, vec3_origin, trace.ent->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH); - else - T_Damage(ent, trace.ent, trace.ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH); - } - - } - if (ent->inuse) - G_TouchTriggers (ent); - - return trace; -} - - -typedef struct -{ - edict_t *ent; - vec3_t origin; - vec3_t angles; - float deltayaw; -} pushed_t; -pushed_t pushed[MAX_EDICTS], *pushed_p; - -edict_t *obstacle; - -void MoveRiders(edict_t *platform, edict_t *ignore, vec3_t move, vec3_t amove, bool turn) -{ - int i; - edict_t *rider; - - for(i=1, rider=g_edicts+i; i<=globals.num_edicts; i++, rider++) { - if((rider->groundentity == platform) && (rider != ignore)) { - VectorAdd(rider->s.origin,move,rider->s.origin); - if (turn && (amove[YAW] != 0.)) { - if(!rider->client) - rider->s.angles[YAW] += amove[YAW]; - else - { - rider->s.angles[YAW] += amove[YAW]; - rider->client->ps.pmove.delta_angles[YAW] += ANGLE2SHORT(amove[YAW]); - rider->client->ps.pmove.pm_type = PM_FREEZE; - rider->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION; - } - } - gi.linkentity(rider); - if(SV_TestEntityPosition(rider)) { - // Move is blocked. Since this is for riders, not pushees, - // it should be ok to just back the move for this rider off - VectorSubtract(rider->s.origin,move,rider->s.origin); - if(turn && (amove[YAW] != 0.)) { - rider->s.angles[YAW] -= amove[YAW]; - if(rider->client) - { - rider->client->ps.pmove.delta_angles[YAW] -= ANGLE2SHORT(amove[YAW]); - rider->client->ps.viewangles[YAW] -= amove[YAW]; - } - } - gi.linkentity(rider); - } else { - // move this rider's riders - MoveRiders(rider,ignore,move,amove,turn); - } - } - } -} -/* -============ -RealBoundingBox - -Returns the actual bounding box of a bmodel. This is a big improvement over -what q2 normally does with rotating bmodels - q2 sets absmin, absmax to a cube -that will completely contain the bmodel at *any* rotation on *any* axis, whether -the bmodel can actually rotate to that angle or not. This leads to a lot of -false block tests in SV_Push if another bmodel is in the vicinity. -============ -*/ - -void RealBoundingBox(edict_t *ent, vec3_t mins, vec3_t maxs) -{ - vec3_t forward, left, up, f1, l1, u1; - vec3_t p[8]; - int i, j, k, j2, k4; - - for(k=0; k<2; k++) - { - k4 = k*4; - if(k) - p[k4][2] = ent->maxs[2]; - else - p[k4][2] = ent->mins[2]; - p[k4+1][2] = p[k4][2]; - p[k4+2][2] = p[k4][2]; - p[k4+3][2] = p[k4][2]; - for(j=0; j<2; j++) - { - j2 = j*2; - if(j) - p[j2+k4][1] = ent->maxs[1]; - else - p[j2+k4][1] = ent->mins[1]; - p[j2+k4+1][1] = p[j2+k4][1]; - for(i=0; i<2; i++) - { - if(i) - p[i+j2+k4][0] = ent->maxs[0]; - else - p[i+j2+k4][0] = ent->mins[0]; - } - } - } - AngleVectors(ent->s.angles,forward,left,up); - for(i=0; i<8; i++) - { - VectorScale(forward,p[i][0],f1); - VectorScale(left,-p[i][1],l1); - VectorScale(up,p[i][2],u1); - VectorAdd(ent->s.origin,f1,p[i]); - VectorAdd(p[i],l1,p[i]); - VectorAdd(p[i],u1,p[i]); - } - VectorCopy(p[0],mins); - VectorCopy(p[0],maxs); - for(i=1; i<8; i++) - { - mins[0] = min(mins[0],p[i][0]); - mins[1] = min(mins[1],p[i][1]); - mins[2] = min(mins[2],p[i][2]); - maxs[0] = max(maxs[0],p[i][0]); - maxs[1] = max(maxs[1],p[i][1]); - maxs[2] = max(maxs[2],p[i][2]); - } -} - -/* -============ -SV_Push - -Objects need to be moved back on a failed push, -otherwise riders would continue to slide. -============ -*/ -bool SV_Push (edict_t *pusher, vec3_t move, vec3_t amove) -{ - int i, e; - edict_t *check, *block; - vec3_t mins, maxs; - pushed_t *p; - vec3_t org, org2, org_check, forward, right, up; - vec3_t move2={0,0,0}; - vec3_t move3={0,0,0}; - vec3_t realmins, realmaxs; - bool turn; - trace_t tr; - - // clamp the move to 1/8 units, so the position will - // be accurate for client side prediction - for (i=0 ; i<3 ; i++) - { - float temp; - temp = move[i]*8.0; - if (temp > 0.0) - temp += 0.5; - else - temp -= 0.5; - move[i] = 0.125 * (int)temp; - } - - // find the bounding box - for (i=0 ; i<3 ; i++) - { - mins[i] = pusher->absmin[i] + move[i]; - maxs[i] = pusher->absmax[i] + move[i]; - } - - // Lazarus: temp turn indicates whether riders - // should rotate with the pusher - if(pusher->turn_rider && turn_rider->value) - turn = true; - else - turn = false; - -// we need this for pushing things later - VectorSubtract (vec3_origin, amove, org); - AngleVectors (org, forward, right, up); - -// save the pusher's original position - pushed_p->ent = pusher; - VectorCopy (pusher->s.origin, pushed_p->origin); - VectorCopy (pusher->s.angles, pushed_p->angles); - if (pusher->client) - pushed_p->deltayaw = pusher->client->ps.pmove.delta_angles[YAW]; - pushed_p++; - -// move the pusher to it's final position - VectorAdd (pusher->s.origin, move, pusher->s.origin); - VectorAdd (pusher->s.angles, amove, pusher->s.angles); - gi.linkentity (pusher); - - // Lazarus: Standard Q2 takes a horrible shortcut - // with rotating brush models, setting - // absmin and absmax to a cube that would - // contain the brush model if it could - // rotate around ANY axis. The result is - // a lot of false hits on intersections, - // particularly when you have multiple - // rotating brush models in the same area. - // RealBoundingBox gives us the actual - // bounding box at the current angles. - RealBoundingBox(pusher,realmins,realmaxs); - -// see if any solid entities are inside the final position - check = g_edicts+1; - for (e = 1; e < globals.num_edicts; e++, check++) - { - if (!check->inuse) - continue; - if (check == pusher->owner) // Lazarus: owner can't block us - continue; - if (check->movetype == MOVETYPE_PUSH - || check->movetype == MOVETYPE_STOP - || check->movetype == MOVETYPE_NONE - || check->movetype == MOVETYPE_NOCLIP - || check->movetype == MOVETYPE_PENDULUM) - continue; - - if (!check->area.prev) - continue; // not linked in anywhere - - // if the entity is standing on the pusher, it will definitely be moved - if (check->groundentity != pusher) - { - // see if the ent needs to be tested -/* if ( check->absmin[0] >= maxs[0] - || check->absmin[1] >= maxs[1] - || check->absmin[2] >= maxs[2] - || check->absmax[0] <= mins[0] - || check->absmax[1] <= mins[1] - || check->absmax[2] <= mins[2] ) - continue; */ - - if ( check->absmin[0] >= realmaxs[0] - || check->absmin[1] >= realmaxs[1] - || check->absmin[2] >= realmaxs[2] - || check->absmax[0] <= realmins[0] - || check->absmax[1] <= realmins[1] - || check->absmax[2] <= realmins[2] ) - continue; - - // see if the ent's bbox is inside the pusher's final position - if (!SV_TestEntityPosition (check)) - continue; - } - - // Lazarus: func_tracktrain-specific stuff - // If train is *driven*, then hurt monsters/players it touches NOW - // rather than waiting to be blocked. - if ((pusher->flags & FL_TRACKTRAIN) && pusher->owner && ((check->svflags & SVF_MONSTER) || check->client) && (check->groundentity != pusher)) - { - vec3_t dir; - int knockback; - VectorSubtract(check->s.origin,pusher->s.origin,dir); - dir[2] += 16; - VectorNormalize(dir); - knockback = (int)(fabs(pusher->moveinfo.current_speed) * check->mass / 300.); - T_Damage (check, pusher, pusher, dir, check->s.origin, vec3_origin, pusher->dmg, knockback, 0, MOD_CRUSH); - } - - if ((pusher->movetype == MOVETYPE_PUSH) || (pusher->movetype == MOVETYPE_PENDULUM) || (check->groundentity == pusher)) - { - // move this entity - pushed_p->ent = check; - VectorCopy (check->s.origin, pushed_p->origin); - VectorCopy (check->s.angles, pushed_p->angles); - pushed_p++; - - // try moving the contacted entity - VectorAdd (check->s.origin, move, check->s.origin); - // Lazarus: if turn_rider is set, do it. We don't do this by default - // 'cause it can be a fairly drastic change in gameplay - if (turn && (check->groundentity == pusher)) { - if(!check->client) - { - check->s.angles[YAW] += amove[YAW]; - } - else - { - if(amove[YAW] != 0.) - { - check->client->ps.pmove.delta_angles[YAW] += ANGLE2SHORT(amove[YAW]); - check->client->ps.viewangles[YAW] += amove[YAW]; - - // PM_FREEZE makes the turn smooth, even though it will - // be turned off by ClientThink in the very next video frame - check->client->ps.pmove.pm_type = PM_FREEZE; - // PMF_NO_PREDICTION overrides .exe's client physics, which - // really doesn't like for us to change player angles. Note - // that this isn't strictly necessary, since Lazarus 1.7 and - // later automatically turn prediction off (in ClientThink) when - // player is riding a MOVETYPE_PUSH - check->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION; - } - if(amove[PITCH] != 0.) - { - float delta_yaw; - float pitch = amove[PITCH]; - - delta_yaw = check->s.angles[YAW] - pusher->s.angles[YAW]; - delta_yaw *= M_PI / 180.; - pitch *= cos(delta_yaw); - check->client->ps.pmove.delta_angles[PITCH] += ANGLE2SHORT(pitch); - check->client->ps.viewangles[PITCH] += pitch; - check->client->ps.pmove.pm_type = PM_FREEZE; - check->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION; - } - } - } - - // Lazarus: This is where we attempt to move check due to a rotation, WITHOUT embedding - // check in pusher (or anything else) - if(check->groundentity == pusher) - { - if((amove[PITCH] != 0) || (amove[YAW] != 0) || (amove[ROLL] != 0)) - { - // figure movement due to the pusher's amove - VectorAdd(check->s.origin,check->origin_offset,org_check); - VectorSubtract (org_check, pusher->s.origin, org); - org2[0] = DotProduct (org, forward); - org2[1] = -DotProduct (org, right); - org2[2] = DotProduct (org, up); - VectorSubtract (org2, org, move2); - VectorAdd (check->s.origin, move2, check->s.origin); - if((amove[PITCH] != 0) || (amove[ROLL] != 0)) - { - VectorCopy(check->s.origin,org); - org[2] += 2*check->mins[2]; - tr = gi.trace(check->s.origin,vec3_origin,vec3_origin,org,check,MASK_SOLID); - if(!tr.startsolid && tr.fraction < 1) - check->s.origin[2] = tr.endpos[2] - check->mins[2] + fabs(tr.plane.normal[0])*check->size[0]/2 + fabs(tr.plane.normal[1])*check->size[1]/2; - - // Lazarus: func_tracktrain is a special case. Since we KNOW (if the map was - // constructed properly) that "move_origin" is a safe position, we - // can infer that there should be a safe (not embedded) position - // somewhere between move_origin and the proposed new location. - if((pusher->flags & FL_TRACKTRAIN) && (check->client || (check->svflags & SVF_MONSTER))) - { - vec3_t f,l,u; - - AngleVectors(pusher->s.angles, f, l, u); - VectorScale(f,pusher->move_origin[0],f); - VectorScale(l,-pusher->move_origin[1],l); - VectorAdd(pusher->s.origin,f,org); - VectorAdd(org,l,org); - org[2] += pusher->move_origin[2] + 1; - org[2] += 16 * ( fabs(u[0]) + fabs(u[1]) ); - tr = gi.trace(org,check->mins,check->maxs,check->s.origin,check,MASK_SOLID); - if(!tr.startsolid) - { - VectorCopy(tr.endpos,check->s.origin); - VectorCopy(check->s.origin,org); - org[2] -= 128; - tr = gi.trace(check->s.origin,check->mins,check->maxs,org,check,MASK_SOLID); - if(tr.fraction > 0) - VectorCopy(tr.endpos,check->s.origin); - } - } - } - } - } - - // may have pushed them off an edge - if (check->groundentity != pusher) - check->groundentity = NULL; - - block = SV_TestEntityPosition (check); - - if (block && (pusher->flags & FL_TRACKTRAIN) && (check->client || (check->svflags & SVF_MONSTER)) && (check->groundentity == pusher) ) - { - // Lazarus: Last hope. If this doesn't get rider out of the way he's - // gonna be stuck. - vec3_t f,l,u; - - AngleVectors(pusher->s.angles, f, l, u); - VectorScale(f,pusher->move_origin[0],f); - VectorScale(l,-pusher->move_origin[1],l); - VectorAdd(pusher->s.origin,f,org); - VectorAdd(org,l,org); - org[2] += pusher->move_origin[2] + 1; - org[2] += 16 * ( fabs(u[0]) + fabs(u[1]) ); - tr = gi.trace(org,check->mins,check->maxs,check->s.origin,check,MASK_SOLID); - if(!tr.startsolid) - { - VectorCopy(tr.endpos,check->s.origin); - VectorCopy(check->s.origin,org); - org[2] -= 128; - tr = gi.trace(check->s.origin,check->mins,check->maxs,org,check,MASK_SOLID); - if(tr.fraction > 0) - VectorCopy(tr.endpos,check->s.origin); - block = SV_TestEntityPosition (check); - } - } - - if (!block) - { // pushed ok - gi.linkentity (check); - // Lazarus: Move check riders, and riders of riders, and... well, you get the pic - VectorAdd(move,move2,move3); - MoveRiders(check,NULL,move3,amove,turn); - // impact? - continue; - } - - // if it is ok to leave in the old position, do it - // this is only relevent for riding entities, not pushed - VectorSubtract (check->s.origin, move, check->s.origin); - VectorSubtract (check->s.origin, move2, check->s.origin); - if(turn) - { - // Argh! - angle - check->s.angles[YAW] -= amove[YAW]; - if(check->client) - { - check->client->ps.pmove.delta_angles[YAW] -= ANGLE2SHORT(amove[YAW]); - check->client->ps.viewangles[YAW] -= amove[YAW]; - } - } - - block = SV_TestEntityPosition (check); - if (!block) - { - pushed_p--; - continue; - } - } - - // save off the obstacle so we can call the block function - obstacle = check; - - // move back any entities we already moved - // go backwards, so if the same entity was pushed - // twice, it goes back to the original position - for (p=pushed_p-1 ; p>=pushed ; p--) - { - VectorCopy (p->origin, p->ent->s.origin); - VectorCopy (p->angles, p->ent->s.angles); - if (p->ent->client) - { - p->ent->client->ps.pmove.delta_angles[YAW] = p->deltayaw; - } - gi.linkentity (p->ent); - } - return false; - } - -//FIXME: is there a better way to handle this? - // see if anything we moved has touched a trigger - for (p=pushed_p-1 ; p>=pushed ; p--) - G_TouchTriggers (p->ent); - - return true; -} - -/* -================ -SV_Physics_Pusher - -Bmodel objects don't interact with each other, but -push all box objects -================ -*/ -void SV_Physics_Pusher (edict_t *ent) -{ - vec3_t move, amove; - edict_t *part, *mv; - - // if not a team captain, movement will be handled elsewhere - if ( ent->flags & FL_TEAMSLAVE) - return; - - // make sure all team slaves can move before commiting - // any moves or calling any think functions - // if the move is blocked, all moved objects will be backed out -//retry: - pushed_p = pushed; - for (part = ent ; part ; part=part->teamchain) - { - if(part->attracted) - part->velocity[0] = part->velocity[1] = 0; - if (part->velocity[0] || part->velocity[1] || part->velocity[2] || - part->avelocity[0] || part->avelocity[1] || part->avelocity[2] - ) - { // object is moving - VectorScale (part->velocity, FRAMETIME, move); - VectorScale (part->avelocity, FRAMETIME, amove); - - if (!SV_Push (part, move, amove)) - break; // move was blocked - - if(part->moveinfo.is_blocked) - { - part->moveinfo.is_blocked = false; - if(part->moveinfo.sound_middle) - part->s.sound = part->moveinfo.sound_middle; - } - } - } - if (pushed_p > &pushed[MAX_EDICTS]) - gi.error ("pushed_p > &pushed[MAX_EDICTS], memory corrupted"); - - if (part && !part->attracted) - { - // the move failed, bump all nextthink times and back out moves - for (mv = ent ; mv ; mv=mv->teamchain) - { - if (mv->nextthink > 0) - mv->nextthink += FRAMETIME; - } - - // if the pusher has a "blocked" function, call it - // otherwise, just stay in place until the obstacle is gone - if (part->blocked) - { - // Lazarus: Func_pushables with health < 0 & vehicles ALWAYS block pushers - if( ( (obstacle->movetype == MOVETYPE_PUSHABLE) && (obstacle->health < 0)) || - (obstacle->movetype == MOVETYPE_VEHICLE) ) - { - part->moveinfo.is_blocked = true; - if(part->s.sound) - { - if (part->moveinfo.sound_end) - gi.sound (part, CHAN_NO_PHS_ADD+CHAN_VOICE, part->moveinfo.sound_end, 1, ATTN_STATIC, 0); - part->s.sound = 0; - } - // Lazarus: More special-case stuff. Man I hate doing this - if(part->movetype == MOVETYPE_PENDULUM) - { - if(fabs(part->s.angles[ROLL]) > 2) - { -// gi.dprintf("pendulum continue in g_phys, avelocity=%g\n",part->avelocity[ROLL]); - part->moveinfo.start_angles[ROLL] = part->s.angles[ROLL]; - VectorClear(part->avelocity); - part->startframe = 0; - } - else - { -// gi.dprintf("pendulum stop in g_phys\n"); - part->spawnflags &= ~1; - part->moveinfo.start_angles[ROLL] = 0; - VectorClear(part->s.angles); - VectorClear(part->avelocity); - } - } - } - else - { - part->blocked (part, obstacle); - part->moveinfo.is_blocked = true; - } - } -#if 0 - // if the pushed entity went away and the pusher is still there - if (!obstacle->inuse && part->inuse) - goto retry; -#endif - } - else - { - // the move succeeded, so call all think functions - for (part = ent ; part ; part=part->teamchain) - { - SV_RunThink (part); - } - } -} - -//================================================================== - -/* -============= -SV_Physics_None - -Non moving objects can only think -============= -*/ -void SV_Physics_None (edict_t *ent) -{ -// regular thinking - SV_RunThink (ent); -} - -/* -============= -SV_Physics_Noclip - -A moving object that doesn't obey physics -============= -*/ -void SV_Physics_Noclip (edict_t *ent) -{ -// regular thinking - if (!SV_RunThink (ent)) - return; - - VectorMA (ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles); - VectorMA (ent->s.origin, FRAMETIME, ent->velocity, ent->s.origin); - - gi.linkentity (ent); -} - -/* -============================================================================== - -TOSS / BOUNCE - -============================================================================== -*/ - -/* -============= -SV_Physics_Toss - -Toss, bounce, and fly movement. When onground, do nothing. -============= -*/ -void SV_Physics_Toss (edict_t *ent) -{ - trace_t trace; - vec3_t move; - float backoff; - edict_t *slave; - bool wasinwater; - bool isinwater; - vec3_t old_origin; - -// regular thinking - SV_RunThink (ent); - - // if not a team captain, so movement will be handled elsewhere - if ( ent->flags & FL_TEAMSLAVE) - return; - - if (ent->groundentity) - wasonground = true; - - if (ent->velocity[2] > 0) - ent->groundentity = NULL; - -// check for the groundentity going away - if (ent->groundentity) - if (!ent->groundentity->inuse) - ent->groundentity = NULL; - -// Lazarus: conveyor - if (ent->groundentity && (ent->groundentity->movetype == MOVETYPE_CONVEYOR)) - { - vec3_t point, end; - trace_t tr; - edict_t *ground = ent->groundentity; - - VectorCopy(ent->s.origin,point); - point[2] += 1; - VectorCopy(point,end); - end[2] -= 256; - tr = gi.trace (point, ent->mins, ent->maxs, end, ent, MASK_SOLID); - // tr.ent HAS to be ground, but just in case we screwed something up: - if(tr.ent == ground) - { - onconveyor = true; - ent->velocity[0] = ground->movedir[0] * ground->speed; - ent->velocity[1] = ground->movedir[1] * ground->speed; - if(tr.plane.normal[2] > 0) { - ent->velocity[2] = ground->speed * - sqrt(1.0 - tr.plane.normal[2]*tr.plane.normal[2]) / - tr.plane.normal[2]; - if(DotProduct(ground->movedir,tr.plane.normal) > 0) { - // then we're moving down - ent->velocity[2] = -ent->velocity[2]; - } - } - VectorScale (ent->velocity, FRAMETIME, move); - trace = SV_PushEntity (ent, move); - if (!ent->inuse) - return; - M_CheckGround(ent); - } - } - -// if onground, return without moving - if ( ent->groundentity ) - return; - - VectorCopy (ent->s.origin, old_origin); - - SV_CheckVelocity (ent); - -// add gravity - if (ent->movetype != MOVETYPE_FLY - && ent->movetype != MOVETYPE_FLYMISSILE - && ent->movetype != MOVETYPE_VEHICLE - && ent->movetype != MOVETYPE_RAIN) - SV_AddGravity (ent); - -// move angles - VectorMA (ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles); - -// move origin - VectorScale (ent->velocity, FRAMETIME, move); - trace = SV_PushEntity (ent, move); - if (!ent->inuse) - return; - - if (trace.fraction < 1 ) - { - if (ent->movetype == MOVETYPE_BOUNCE) -// backoff = 1.5; - backoff = 1.0 + bounce_bounce->value; - else if((ent->movetype == MOVETYPE_RAIN) && (trace.plane.normal[2] <= 0.7)) - backoff = 2; - else if(trace.plane.normal[2] <= 0.7) // Lazarus - don't stop on steep incline - backoff = 1.5; - else - backoff = 1; - - ClipVelocity (ent->velocity, trace.plane.normal, ent->velocity, backoff); - - // stop if on ground - if (trace.plane.normal[2] > 0.7) - { -// if (ent->velocity[2] < 60 || ent->movetype != MOVETYPE_BOUNCE ) - if (ent->velocity[2] < bounce_minv->value || (ent->movetype != MOVETYPE_BOUNCE) ) - { - ent->groundentity = trace.ent; - ent->groundentity_linkcount = trace.ent->linkcount; - VectorCopy (vec3_origin, ent->velocity); - VectorCopy (vec3_origin, ent->avelocity); - } - } - -// if (ent->touch) -// ent->touch (ent, trace.ent, &trace.plane, trace.surface); - } - - // Lazarus: MOVETYPE_RAIN doesn't cause splash noises when touching water - if(ent->movetype != MOVETYPE_RAIN) - { - // check for water transition - wasinwater = (ent->watertype & MASK_WATER); - ent->watertype = gi.pointcontents (ent->s.origin); - isinwater = ent->watertype & MASK_WATER; - - if (isinwater) - ent->waterlevel = 1; - else - ent->waterlevel = 0; - - // tpp... don't do sounds for the camera - if(ent->class_id != ENTITY_CHASECAM) - { - if (!wasinwater && isinwater) - gi.positioned_sound (old_origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0); - else if (wasinwater && !isinwater) - gi.positioned_sound (ent->s.origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0); - } - } - -// move teamslaves - for (slave = ent->teamchain; slave; slave = slave->teamchain) - { - VectorCopy (ent->s.origin, slave->s.origin); - gi.linkentity (slave); - } - -} - -/* -=============================================================================== - -STEPPING MOVEMENT - -=============================================================================== -*/ - -/* -============= -SV_Physics_Step - -Monsters freefall when they don't have a ground entity, otherwise -all movement is done with discrete steps. - -This is also used for objects that have become still on the ground, but -will fall if the floor is pulled out from under them. -FIXME: is this true? -============= -*/ - -//FIXME: hacked in for E3 demo -#define sv_stopspeed 100 -#define sv_friction 6 -#define sv_waterfriction 1 - -void SV_AddRotationalFriction (edict_t *ent) -{ - int n; - float adjustment; - - VectorMA (ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles); - adjustment = FRAMETIME * sv_stopspeed * sv_friction; - for (n = 0; n < 3; n++) - { - if (ent->avelocity[n] > 0) - { - ent->avelocity[n] -= adjustment; - if (ent->avelocity[n] < 0) - ent->avelocity[n] = 0; - } - else - { - ent->avelocity[n] += adjustment; - if (ent->avelocity[n] > 0) - ent->avelocity[n] = 0; - } - } -} - -#define WATER_DENSITY 0.00190735 - -float RiderMass(edict_t *platform) -{ - float mass = 0; - int i; - edict_t *rider; - trace_t trace; - vec3_t point; - - for(i=1, rider=g_edicts+i; i<=globals.num_edicts; i++, rider++) { - if(rider == platform) continue; - if(!rider->inuse) continue; - if(rider->groundentity == platform) { - mass += rider->mass; - mass += RiderMass(rider); - } else if (rider->movetype == MOVETYPE_PUSHABLE ) { - // Bah - special case for func_pushable riders. Swimming - // func_pushables don't really have a groundentity, even - // though they may be sitting on another swimming - // func_pushable, which is what we need to know. - VectorCopy(rider->s.origin,point); - point[2] -= 0.25; - trace = gi.trace (rider->s.origin, rider->mins, rider->maxs, point, rider, MASK_MONSTERSOLID); - if ( trace.plane.normal[2] < 0.7 && !trace.startsolid) - continue; - if (!trace.startsolid && !trace.allsolid) { - if(trace.ent == platform) { - mass += rider->mass; - mass += RiderMass(rider); - } - } - } - } - return mass; -} - -void SV_Physics_Step (edict_t *ent) -{ - bool hitsound = false; - float *vel; - float speed, newspeed, control; - float friction; - edict_t *ground; - edict_t *e; - int cont; - int mask; - int i; - int oldwaterlevel; - vec3_t point, end; - vec3_t old_origin, move; - - // airborne monsters should always check for ground - if (!ent->groundentity) - M_CheckGround (ent); - - oldwaterlevel = ent->waterlevel; - - VectorCopy(ent->s.origin,old_origin); - - // Lazarus: If density hasn't been calculated yet, do so now - if (ent->mass > 0 && ent->density == 0.) { - ent->volume = ent->size[0] * ent->size[1] * ent->size[2]; - ent->density = ent->mass/ent->volume; - - if(ent->movetype == MOVETYPE_PUSHABLE) { - // This stuff doesn't apply to anything else, and... heh... - // caused monster_flipper to sink - - ent->bob = min(2.0,300.0/ent->mass); - ent->duration = max(2.0,1.0 + ent->mass/100); - - // Figure out neutral bouyancy line for this entity - // This isn't entirely realistic, but helps gameplay: - // Arbitrary mass limit for func_pushable that can be pushed on - // land is 500. So make a mass=500+, 64x64x64 crate sink. - // (Otherwise, player might cause a 501 crate to leave - // water and expect to be able to push it.) - // Max floating density is then 0.0019073486328125 - if(ent->density > WATER_DENSITY) - ent->flags &= ~FL_SWIM; // sinks like a rock - } - } - // If not a monster, then determine whether we're in water. - // (monsters take care of this in g_monster.c) - if (!(ent->svflags & SVF_MONSTER) && (ent->flags && FL_SWIM) ) { - point[0] = (ent->absmax[0] + ent->absmin[0])/2; - point[1] = (ent->absmax[1] + ent->absmin[1])/2; - point[2] = ent->absmin[2] + 1; - cont = gi.pointcontents (point); - if (!(cont & MASK_WATER)) { - ent->waterlevel = 0; - ent->watertype = 0; - } - else { - ent->watertype = cont; - ent->waterlevel = 1; - point[2] = ent->absmin[2] + ent->size[2]/2; - cont = gi.pointcontents (point); - if (cont & MASK_WATER) - { - ent->waterlevel = 2; - point[2] = ent->absmax[2]; - cont = gi.pointcontents (point); - if (cont & MASK_WATER) - ent->waterlevel = 3; - } - } - } - - ground = ent->groundentity; - - SV_CheckVelocity (ent); - - if (ground) - wasonground = true; - - if (ent->avelocity[0] || ent->avelocity[1] || ent->avelocity[2]) - SV_AddRotationalFriction (ent); - - // add gravity except: - // flying monsters - // swimming monsters who are in the water - if (! wasonground) - if (!(ent->flags & FL_FLY)) - if (!((ent->flags & FL_SWIM) && (ent->waterlevel > 2))) { - if (ent->velocity[2] < sv_gravity->value*-0.1) - hitsound = true; - if (ent->waterlevel == 0) - SV_AddGravity (ent); - } - - // friction for flying monsters that have been given vertical velocity - if ((ent->flags & FL_FLY) && (ent->velocity[2] != 0)) { - speed = fabs(ent->velocity[2]); - control = speed < sv_stopspeed ? sv_stopspeed : speed; - friction = sv_friction/3; - newspeed = speed - (FRAMETIME * control * friction); - if (newspeed < 0) - newspeed = 0; - newspeed /= speed; - ent->velocity[2] *= newspeed; - } - - // friction for swimming monsters that have been given vertical velocity - if (ent->movetype != MOVETYPE_PUSHABLE) { - // Lazarus: This is id's swag at drag. It works mostly, but for submerged - // crates we can do better. - if ((ent->flags & FL_SWIM) && (ent->velocity[2] != 0)) { - speed = fabs(ent->velocity[2]); - control = speed < sv_stopspeed ? sv_stopspeed : speed; - newspeed = speed - (FRAMETIME * control * sv_waterfriction * ent->waterlevel); - if (newspeed < 0) - newspeed = 0; - newspeed /= speed; - ent->velocity[2] *= newspeed; - } - } - - - // Lazarus: Floating stuff - if ((ent->movetype == MOVETYPE_PUSHABLE) && (ent->flags && FL_SWIM) && (ent->waterlevel)) { - float waterlevel; - float rider_mass, total_mass; - trace_t tr; - float Accel, Area, Drag, Force; - - VectorCopy(point,end); - if(ent->waterlevel < 3) { - point[2] = ent->absmax[2]; - end[2] = ent->absmin[2]; - tr = gi.trace(point,NULL,NULL,end,ent,MASK_WATER); - waterlevel = tr.endpos[2]; - } - else { - // Not right, but really all we need to know - waterlevel = ent->absmax[2] + 1; - } - rider_mass = RiderMass(ent); - total_mass = rider_mass + ent->mass; - Area = ent->size[0] * ent->size[1]; - if(waterlevel < ent->absmax[2]) { - // A portion of crate is above water - int time; - float t0, t1, z0, z1; - - // For partially submerged crates, use same psuedo-friction thing used - // on other entities. This isn't really correct, but then neither is - // our drag calculation used for fully submerged crates good for this - // situation - if (ent->velocity[2] != 0) { - speed = fabs(ent->velocity[2]); - control = speed < sv_stopspeed ? sv_stopspeed : speed; - newspeed = speed - (FRAMETIME * control * sv_waterfriction * ent->waterlevel); - if (newspeed < 0) - newspeed = 0; - newspeed /= speed; - ent->velocity[2] *= newspeed; - } - - // Apply physics and bob AFTER friction, or the damn thing will never move. - Force = -total_mass + ((waterlevel-ent->absmin[2]) * Area * WATER_DENSITY); - Accel = Force * sv_gravity->value/total_mass; - ent->velocity[2] += Accel*FRAMETIME; - - time = ent->duration*10; - t0 = ent->bobframe%time; - t1 = (ent->bobframe+1)%time; - z0 = sin(2*M_PI*t0/time); - z1 = sin(2*M_PI*t1/time); - ent->velocity[2] += ent->bob * (z1-z0) * 10; - ent->bobframe = (ent->bobframe+1)%time; - } else { - // Crate is fully submerged - Force = -total_mass + ent->volume * WATER_DENSITY; - if(sv_gravity->value) { - Drag = 0.00190735 * 1.05 * Area * (ent->velocity[2]*ent->velocity[2])/sv_gravity->value; - if(Drag > fabs(Force)) { - // Drag actually CAN be > total weight, but if we do this we tend to - // get crates flying back out of the water after being dropped from some - // height - Drag = fabs(Force); - } - if(ent->velocity[2] > 0) - Drag = -Drag; - Force += Drag; - } - Accel = Force * sv_gravity->value/total_mass; - ent->velocity[2] += Accel*FRAMETIME; - } - - if(ent->watertype & MASK_CURRENT) { - // Move with current, relative to mass. Mass=400 or less - // will move at 50 units/sec. - float v; - int current; - - if(ent->mass > 400) - v = 0.125 * ent->mass; - else - v = 50.; - current = ent->watertype & MASK_CURRENT; - switch (current) - { - case CONTENTS_CURRENT_0: ent->velocity[0] = v; break; - case CONTENTS_CURRENT_90: ent->velocity[1] = v; break; - case CONTENTS_CURRENT_180: ent->velocity[0] = -v; break; - case CONTENTS_CURRENT_270: ent->velocity[1] = -v; break; - case CONTENTS_CURRENT_UP : ent->velocity[2] = max(v, ent->velocity[2]); - case CONTENTS_CURRENT_DOWN: ent->velocity[2] = min(-v, ent->velocity[2]); - } - } - } - - // Conveyor - if (wasonground && (ground->movetype == MOVETYPE_CONVEYOR)) - { - trace_t tr; - - VectorCopy(ent->s.origin,point); - point[2] += 1; - VectorCopy(point,end); - end[2] -= 256; - tr = gi.trace (point, ent->mins, ent->maxs, end, ent, MASK_SOLID); - // tr.ent HAS to be ground, but just in case we screwed something up: - if(tr.ent == ground) - { - onconveyor = true; - ent->velocity[0] = ground->movedir[0] * ground->speed; - ent->velocity[1] = ground->movedir[1] * ground->speed; - if(tr.plane.normal[2] > 0) { - ent->velocity[2] = ground->speed * - sqrt(1.0 - tr.plane.normal[2]*tr.plane.normal[2]) / - tr.plane.normal[2]; - if(DotProduct(ground->movedir,tr.plane.normal) > 0) { - // Then we're moving down. - ent->velocity[2] = -ent->velocity[2] + 2; - } - } - } - } - - if (ent->velocity[2] || ent->velocity[1] || ent->velocity[0]) { - int block; - // apply friction - // let dead monsters who aren't completely onground slide - - if ((wasonground) || (ent->flags & (FL_SWIM|FL_FLY))) - - if (!onconveyor) - { - if (!(ent->health <= 0.0 && !M_CheckBottom(ent))) { - vel = ent->velocity; - speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]); - if (speed) - { - friction = sv_friction; - - control = speed < sv_stopspeed ? sv_stopspeed : speed; - newspeed = speed - FRAMETIME*control*friction; - - if (newspeed < 0) - newspeed = 0; - newspeed /= speed; - - vel[0] *= newspeed; - vel[1] *= newspeed; - } - } - } - - if (ent->svflags & SVF_MONSTER) - mask = MASK_MONSTERSOLID; - else if(ent->movetype == MOVETYPE_PUSHABLE) - mask = MASK_MONSTERSOLID | MASK_PLAYERSOLID; - else if(ent->clipmask) - mask = ent->clipmask; // Lazarus edition - else - mask = MASK_SOLID; - - if (ent->movetype == MOVETYPE_PUSHABLE) - { - block = SV_PushableMove (ent, FRAMETIME, mask); - if(block && !(block & 8) && onconveyor) - { - if(blocker && (blocker->takedamage == DAMAGE_YES)) - T_Damage(blocker,world,world,vec3_origin,ent->s.origin,vec3_origin,100000,1,0,MOD_CRUSH); - else - T_Damage(ent,world,world,vec3_origin,ent->s.origin,vec3_origin,100000,1,0,MOD_CRUSH); - if(!ent->inuse) - return; - } - } - else - { - block = SV_FlyMove (ent, FRAMETIME, mask); - if(block && !(block & 8) && onconveyor) - { - if(blocker && (blocker->takedamage == DAMAGE_YES)) - T_Damage(blocker,world,world,vec3_origin,ent->s.origin,vec3_origin,100000,1,0,MOD_CRUSH); - else - T_Damage (ent,world,world,vec3_origin,ent->s.origin,vec3_origin,100000,1,0,MOD_CRUSH); - if(!ent->inuse) - return; - } - } - gi.linkentity (ent); - G_TouchTriggers (ent); - if (!ent->inuse) - return; - - if (ent->groundentity) - if (!wasonground) - if (hitsound) - gi.sound (ent, 0, gi.soundindex("world/land.wav"), 1, 1, 0); - - // Move func_pushable riders - if(ent->movetype == MOVETYPE_PUSHABLE) { - trace_t tr; - if(ent->bounce_me == 2) - VectorMA(old_origin,FRAMETIME,ent->velocity,ent->s.origin); - VectorSubtract(ent->s.origin,old_origin,move); - for(i=1, e=g_edicts+i; igroundentity == ent) { - VectorAdd(e->s.origin,move,end); - tr = gi.trace(e->s.origin,e->mins,e->maxs,end,ent,MASK_SOLID); - VectorCopy(tr.endpos,e->s.origin); - gi.linkentity(e); - } - } - } - } - else if(ent->movetype == MOVETYPE_PUSHABLE) { - // We run touch function for non-moving func_pushables every frame - // to see if they are touching, for example, a trigger_mass - G_TouchTriggers(ent); - if(!ent->inuse) return; - } - - - // Lazarus: Add falling damage for entities that can be damaged - if( ent->takedamage ) { - other_FallingDamage(ent); - VectorCopy(ent->velocity,ent->oldvelocity); - } - - if ((!oldwaterlevel && ent->waterlevel) && !ent->groundentity) { - if( (ent->watertype & CONTENTS_SLIME) || (ent->watertype & CONTENTS_WATER) ) - gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); - else if(ent->watertype & CONTENTS_MUD) - gi.sound (ent, CHAN_BODY, gi.soundindex("mud/mud_in2.wav"), 1, ATTN_NORM, 0); - } - -// regular thinking - SV_RunThink (ent); - VectorCopy(ent->velocity,ent->oldvelocity); -} - - -// -//============ -//SV_VehicleMove -//============ -// -#define MAX_CLIP_PLANES 5 -int SV_VehicleMove (edict_t *ent, float time, int mask) -{ - edict_t *hit; - edict_t *ignore; - trace_t trace; - vec3_t dir; - vec3_t end; - vec3_t planes[MAX_CLIP_PLANES]; - vec3_t primal_velocity, original_velocity, new_velocity; - vec3_t start; - vec3_t move, amove; - vec3_t xy_velocity; - vec_t xy_speed; - float d; - float e, m, v11, v12, v21, v22; - float time_left; - int bumpcount, numbumps; - int numplanes; - int i, j; - int blocked; - - // Corrective stuff added for bmodels with no origin brush - vec3_t mins, maxs; - vec3_t origin; - - numbumps = 4; - - blocked = 0; - VectorCopy (ent->velocity, original_velocity); - VectorCopy (ent->velocity, primal_velocity); - numplanes = 0; - - VectorCopy(ent->velocity,xy_velocity); - xy_velocity[2] = 0; - xy_speed = VectorLength(xy_velocity); - - time_left = time; - - VectorAdd(ent->s.origin,ent->origin_offset,origin); - VectorCopy(ent->size,maxs); - VectorScale(maxs,0.5,maxs); - VectorNegate(maxs,mins); - mins[2] += 1; - - ent->groundentity = NULL; - - ignore = ent; - VectorCopy(origin,start); - - for (bumpcount=0 ; bumpcountvelocity[i]; - - trace = gi.trace (start, mins, maxs, end, ignore, mask); - - if (trace.allsolid) - { - // entity is trapped in another solid - if(trace.ent && (trace.ent->svflags & SVF_MONSTER)) { - // Monster stuck in vehicle. No matter how screwed up this is, - // we've gotta get him out of there. - // Give him a light-speed nudge and a velocity - trace_t tr; - vec3_t new_origin, new_velocity; - - VectorSubtract(trace.ent->s.origin,ent->s.origin,dir); - dir[2] = 0; - VectorNormalize(dir); - dir[2] = 0.2; - VectorMA(trace.ent->velocity,32,dir,new_velocity); - VectorMA(trace.ent->s.origin,FRAMETIME,new_velocity,new_origin); - tr = gi.trace(trace.ent->s.origin,trace.ent->mins,trace.ent->maxs,new_origin,trace.ent,MASK_MONSTERSOLID); - if(tr.fraction == 1) { - VectorCopy(new_origin,trace.ent->s.origin); - VectorCopy(new_velocity,trace.ent->velocity); - gi.linkentity(trace.ent); - } - } - else if(trace.ent->client && xy_speed > 0 ) - { - // If player is relatively close to the vehicle move_origin, AND the - // vehicle is still moving, then most likely the player just disengaged - // the vehicle and isn't really trapped. Move player along with - // vehicle - vec3_t forward, left, f1, l1, drive, offset; - - AngleVectors(ent->s.angles, forward, left, NULL); - VectorScale(forward,ent->move_origin[0],f1); - VectorScale(left,ent->move_origin[1],l1); - VectorAdd(ent->s.origin,f1,drive); - VectorAdd(drive,l1,drive); - VectorSubtract(drive,trace.ent->s.origin,offset); - if (fabs(offset[2]) < 64) - offset[2] = 0; - if (VectorLength(offset) < 16) - { - VectorAdd(trace.ent->s.origin,end,trace.ent->s.origin); - VectorSubtract(trace.ent->s.origin,origin,trace.ent->s.origin); - gi.linkentity(trace.ent); - goto not_allsolid; - } - } - VectorCopy (vec3_origin, ent->velocity); - VectorCopy (vec3_origin, ent->avelocity); - return 3; - } - -not_allsolid: - - if (trace.fraction > 0) - { // actually covered some distance - VectorCopy (trace.endpos, origin); - VectorSubtract (origin, ent->origin_offset, ent->s.origin); - VectorCopy (ent->velocity, original_velocity); - numplanes = 0; - } - - if (trace.fraction == 1) - break; // moved the entire distance - - hit = trace.ent; - - if (trace.plane.normal[2] > 0.7) - { - blocked |= 1; // floor - if ( hit->solid == SOLID_BSP) - { - ent->groundentity = hit; - ent->groundentity_linkcount = hit->linkcount; - } - } - if (trace.plane.normal[0] > 0 || trace.plane.normal[1] > 0) - blocked |= 1; - if (!trace.plane.normal[2]) - blocked |= 2; // step - -// -// run the impact function -// - SV_Impact (ent, &trace); - if (!ent->inuse) - break; // vehicle destroyed - if (!trace.ent->inuse) - { - blocked = 0; - break; - } - - if(trace.ent->classname) - { - if(ent->owner && (trace.ent->svflags & (SVF_MONSTER | SVF_DEADMONSTER))) - { - continue; // handled in vehicle_touch - } - else if(trace.ent->movetype != MOVETYPE_PUSHABLE) - { - // if not a func_pushable, match speeds... - VectorCopy(trace.ent->velocity,ent->velocity); - } - else if(ent->mass && VectorLength(ent->velocity)) - { - // otherwise push func_pushable (if vehicle has mass & is moving) - e = 0.0; // coefficient of restitution - m = (float)(ent->mass)/(float)(trace.ent->mass); - for(i=0; i<2; i++) { - v11 = ent->velocity[i]; - v21 = trace.ent->velocity[i]; - v22 = ( e*m*(v11-v21) + m*v11 + v21 ) / (1.0 + m); - v12 = v22 - e*(v11-v21); - ent->velocity[i] = v12; - trace.ent->velocity[i] = v22; - trace.ent->oldvelocity[i] = v22; - } - gi.linkentity(trace.ent); - } - } - - time_left -= time_left * trace.fraction; - - // cliped to another plane - if (numplanes >= MAX_CLIP_PLANES) - { - VectorCopy (vec3_origin, ent->velocity); - VectorCopy (vec3_origin, ent->avelocity); - return 3; - } - - // players, monsters and func_pushables don't block us - if(trace.ent->client) { - blocked = 0; - continue; - } - if(trace.ent->svflags & SVF_MONSTER) { - blocked = 0; - continue; - } - if(trace.ent->movetype == MOVETYPE_PUSHABLE) - { - blocked = 0; - continue; - } - - VectorCopy (trace.plane.normal, planes[numplanes]); - numplanes++; - -// -// modify original_velocity so it parallels all of the clip planes -// - for (i=0 ; ivelocity); - VectorCopy (new_velocity, ent->oldvelocity); - } - else - { // go along the crease - // DWH: What the hell does this do? - if (numplanes != 2) - { - ent->moveinfo.state = 0; - ent->moveinfo.next_speed = 0; - VectorCopy (vec3_origin, ent->velocity); - VectorCopy (vec3_origin, ent->oldvelocity); - VectorCopy (vec3_origin, ent->avelocity); - return 7; - } - CrossProduct (planes[0], planes[1], dir); - d = DotProduct (dir, ent->velocity); - VectorScale (dir, d, ent->velocity); - } -// -// if original velocity is against the original velocity, stop dead -// to avoid tiny occilations in sloping corners -// - if (DotProduct (ent->velocity, primal_velocity) <= 0) - { - ent->moveinfo.state = 0; - ent->moveinfo.next_speed = 0; - VectorCopy (vec3_origin, ent->velocity); - VectorCopy (vec3_origin, ent->oldvelocity); - VectorCopy (vec3_origin, ent->avelocity); - return blocked; - } - } - VectorScale(ent->velocity,FRAMETIME,move); - VectorScale(ent->avelocity,FRAMETIME,amove); - return blocked; -} - - - -void SV_Physics_Vehicle (edict_t *ent) -{ - edict_t *ground; - int mask; - -// see if we're on the ground - if (!ent->groundentity) - M_CheckGround (ent); - - ground = ent->groundentity; - SV_CheckVelocity (ent); - if (ground) - wasonground = true; - -// move angles - VectorMA (ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles); - - if (ent->velocity[2] || ent->velocity[1] || ent->velocity[0]) - { - if(ent->org_size[0]) - { - float ca, sa, yaw; - vec3_t p[2][2]; - vec3_t mins, maxs; - vec3_t s2; - // Adjust bounding box for yaw - yaw = ent->s.angles[YAW] * M_PI / 180.; - ca = cos(yaw); - sa = sin(yaw); - VectorCopy(ent->org_size,s2); - VectorScale(s2,0.5,s2); - p[0][0][0] = -s2[0]*ca + s2[1]*sa; - p[0][0][1] = -s2[1]*ca - s2[0]*sa; - p[0][1][0] = s2[0]*ca + s2[1]*sa; - p[0][1][1] = -s2[1]*ca + s2[0]*sa; - p[1][0][0] = -s2[0]*ca - s2[1]*sa; - p[1][0][1] = s2[1]*ca - s2[0]*sa; - p[1][1][0] = s2[0]*ca - s2[1]*sa; - p[1][1][1] = s2[1]*ca + s2[0]*sa; - mins[0] = min(p[0][0][0],p[0][1][0]); - mins[0] = min(mins[0],p[1][0][0]); - mins[0] = min(mins[0],p[1][1][0]); - mins[1] = min(p[0][0][1],p[0][1][1]); - mins[1] = min(mins[1],p[1][0][1]); - mins[1] = min(mins[1],p[1][1][1]); - maxs[0] = max(p[0][0][0],p[0][1][0]); - maxs[0] = max(maxs[0],p[1][0][0]); - maxs[0] = max(maxs[0],p[1][1][0]); - maxs[1] = max(p[0][0][1],p[0][1][1]); - maxs[1] = max(maxs[1],p[1][0][1]); - maxs[1] = max(maxs[1],p[1][1][1]); - ent->size[0] = maxs[0] - mins[0]; - ent->size[1] = maxs[1] - mins[1]; - ent->mins[0] = -ent->size[0]/2; - ent->mins[1] = -ent->size[1]/2; - ent->maxs[0] = ent->size[0]/2; - ent->maxs[1] = ent->size[1]/2; - gi.linkentity(ent); - } - mask = MASK_ALL; - SV_VehicleMove (ent, FRAMETIME, mask); - gi.linkentity (ent); - G_TouchTriggers (ent); - if (!ent->inuse) - return; - } -// regular thinking - SV_RunThink (ent); - VectorCopy(ent->velocity,ent->oldvelocity); -} - -//============================================================================ -/* -============ -SV_DebrisEntity - -Does not change the entities velocity at all -============ -*/ -trace_t SV_DebrisEntity (edict_t *ent, vec3_t push) -{ - trace_t trace; - vec3_t start; - vec3_t end; - vec3_t v1, v2; - vec_t dot, speed1, speed2; - float scale; - int damage; - int mask; - - VectorCopy (ent->s.origin, start); - VectorAdd (start, push, end); - if(ent->clipmask) - mask = ent->clipmask; - else - mask = MASK_SHOT; - trace = gi.trace (start, ent->mins, ent->maxs, end, ent, mask); - VectorCopy (trace.endpos, ent->s.origin); - gi.linkentity (ent); - if (trace.fraction != 1.0) { - if( (trace.surface) && (trace.surface->flags & SURF_SKY) ) { - G_FreeEdict(ent); - return trace; - } - if(trace.ent->client || (trace.ent->svflags & SVF_MONSTER) ) { - // touching a player or monster - // if rock has no mass we really don't care who it hits - if(!ent->mass) return trace; - speed1 = VectorLength(ent->velocity); - if(!speed1) return trace; - speed2 = VectorLength(trace.ent->velocity); - VectorCopy(ent->velocity,v1); - VectorNormalize(v1); - VectorCopy(trace.ent->velocity,v2); - VectorNormalize(v2); - dot = -DotProduct(v1,v2); - speed1 += speed2 * dot; - if(speed1 <= 0) return trace; - scale = (float)ent->mass/200.*speed1; - VectorMA(trace.ent->velocity,scale,v1,trace.ent->velocity); - // Take a swag at it... - - if(speed1 > 100) { - damage = (int)(ent->mass * speed1 / 5000.); - if(damage) - T_Damage(trace.ent, world, world, v1, trace.ent->s.origin, vec3_origin, - damage, 0, DAMAGE_NO_KNOCKBACK, MOD_CRUSH); - } - if(ent->touch) - ent->touch (ent, trace.ent, &trace.plane, trace.surface); - - gi.linkentity(trace.ent); - } - else - { - SV_Impact (ent, &trace); - } - } - return trace; -} - -/* -============= -SV_Physics_Debris - -Toss, bounce, and fly movement. When onground, do nothing. -============= -*/ -void SV_Physics_Debris (edict_t *ent) -{ - trace_t trace; - vec3_t move; - float backoff; - bool wasinwater; - bool isinwater; - vec3_t old_origin; - -// regular thinking - SV_RunThink (ent); - - if (ent->velocity[2] > 0) - ent->groundentity = NULL; - -// check for the groundentity going away - if (ent->groundentity) - if (!ent->groundentity->inuse) - ent->groundentity = NULL; - -// if onground, return without moving - if ( ent->groundentity ) - return; - - VectorCopy (ent->s.origin, old_origin); - SV_CheckVelocity (ent); - SV_AddGravity (ent); - -// move angles - VectorMA (ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles); - -// move origin - VectorScale (ent->velocity, FRAMETIME, move); - trace = SV_DebrisEntity (ent, move); - if (!ent->inuse) - return; - - if (trace.fraction < 1) - { - backoff = 1.0 + ent->attenuation; - ClipVelocity (ent->velocity, trace.plane.normal, ent->velocity, backoff); - - // stop if on ground -// if (trace.plane.normal[2] > 0.7) Lazarus: This is too strict... rocks get hung -// up on sides of cliffs and spin in place - if (trace.plane.normal[2] > 0.3) - { - if (ent->velocity[2] < 60) - { - ent->groundentity = trace.ent; - ent->groundentity_linkcount = trace.ent->linkcount; - VectorCopy (vec3_origin, ent->velocity); - VectorCopy (vec3_origin, ent->avelocity); - } - } - } - -// check for water transition - wasinwater = (ent->watertype & MASK_WATER); - ent->watertype = gi.pointcontents (ent->s.origin); - isinwater = ent->watertype & MASK_WATER; - - if (isinwater) - ent->waterlevel = 1; - else - ent->waterlevel = 0; - - if (!wasinwater && isinwater) - gi.positioned_sound (old_origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0); - else if (wasinwater && !isinwater) - gi.positioned_sound (ent->s.origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0); - -} - -/* -==================== -SV_Physics_Conveyor - -REAL simple - all we do is check for player riders and adjust their position. -Only gotcha here is we have to make sure we don't end up embedding player in -*another* object that's being moved by the conveyor. - -==================== -*/ -void SV_Physics_Conveyor(edict_t *ent) -{ - edict_t *player; - int i; - trace_t tr; - vec3_t v, move; - vec3_t point, end; - - VectorScale(ent->movedir,ent->speed,v); - VectorScale(v,FRAMETIME,move); - for(i=0; iinuse) - continue; - if(!player->groundentity) - continue; - if(player->groundentity != ent) - continue; - // Look below player; make sure he's on a conveyor - VectorCopy(player->s.origin,point); - point[2] += 1; - VectorCopy(point,end); - end[2] -= 256; - tr = gi.trace (point, player->mins, player->maxs, end, player, MASK_SOLID); - // tr.ent HAS to be conveyor, but just in case we screwed something up: - if(tr.ent == ent) - { - if(tr.plane.normal[2] > 0) { - v[2] = ent->speed * sqrt(1.0 - tr.plane.normal[2]*tr.plane.normal[2]) / - tr.plane.normal[2]; - if(DotProduct(ent->movedir,tr.plane.normal) > 0) { - // then we're moving down - v[2] = -v[2]; - } - move[2] = v[2]*FRAMETIME; - } - VectorAdd(player->s.origin,move,end); - tr = gi.trace(player->s.origin,player->mins,player->maxs,end,player,player->clipmask); - VectorCopy(tr.endpos,player->s.origin); - gi.linkentity(player); - } - } -} - -void M_CheckGround (edict_t *ent) -{ - vec3_t point; - trace_t trace; - - if (level.time < ent->gravity_debounce_time) - return; - - if (ent->flags & (FL_SWIM|FL_FLY)) - return; - - if (ent->velocity[2] > 100) - { - ent->groundentity = NULL; - return; - } - -// if the hull point one-quarter unit down is solid the entity is on ground - point[0] = ent->s.origin[0]; - point[1] = ent->s.origin[1]; - point[2] = ent->s.origin[2] - 0.25; - - trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, point, ent, MASK_MONSTERSOLID); - - // check steepness - if ( trace.plane.normal[2] < 0.7 && !trace.startsolid) - { - ent->groundentity = NULL; - return; - } - - // Lazarus: The following 2 lines were in the original code and commented out - // by id. However, the effect of this is that a player walking over - // a dead monster who is laying on a brush model will cause the - // dead monster to drop through the brush model. This change *may* - // have other consequences, though, so watch out for this. - - ent->groundentity = trace.ent; - ent->groundentity_linkcount = trace.ent->linkcount; -// if (!trace.startsolid && !trace.allsolid) -// VectorCopy (trace.endpos, ent->s.origin); - if (!trace.startsolid && !trace.allsolid) - { - VectorCopy (trace.endpos, ent->s.origin); - ent->groundentity = trace.ent; - ent->groundentity_linkcount = trace.ent->linkcount; -// ent->velocity[2] = 0; Lazarus: what if the groundentity is moving? - ent->velocity[2] = trace.ent->velocity[2]; - } -} - -/* -================ -G_RunEntity - -================ -*/ -void G_RunEntity (edict_t *ent) -{ - if(level.freeze && (ent->class_id != ENTITY_CHASECAM)) - return; - - if (ent->prethink) - ent->prethink (ent); - - onconveyor = false; - wasonground = false; - blocker = NULL; - - switch ( (int)ent->movetype) - { - case MOVETYPE_PUSH: - case MOVETYPE_STOP: - case MOVETYPE_PENDULUM: - SV_Physics_Pusher (ent); - break; - case MOVETYPE_NONE: - SV_Physics_None (ent); - break; - case MOVETYPE_NOCLIP: - SV_Physics_Noclip (ent); - break; - case MOVETYPE_STEP: - case MOVETYPE_PUSHABLE: - SV_Physics_Step (ent); - break; - case MOVETYPE_TOSS: - case MOVETYPE_BOUNCE: - case MOVETYPE_FLY: - case MOVETYPE_FLYMISSILE: - case MOVETYPE_RAIN: - SV_Physics_Toss (ent); - break; - case MOVETYPE_DEBRIS: - SV_Physics_Debris (ent); - break; - case MOVETYPE_VEHICLE: - SV_Physics_Vehicle (ent); - break; - // Lazarus - case MOVETYPE_WALK: - SV_Physics_None(ent); - break; - case MOVETYPE_CONVEYOR: - SV_Physics_Conveyor(ent); - break; - default: - gi.error ("SV_Physics: bad movetype %i", (int)ent->movetype); - } -} diff --git a/server/g_save.c b/server/g_save.c deleted file mode 100644 index a3f5a6ea..00000000 --- a/server/g_save.c +++ /dev/null @@ -1,927 +0,0 @@ - -#include "baseentity.h" - -#define Function(f) {#f, f} - -mmove_t mmove_reloc; - -field_t fields[] = -{ - {"classname", FOFS(classname), F_LSTRING}, - {"model", FOFS(model), F_LSTRING}, - {"spawnflags", FOFS(spawnflags), F_INT}, - {"speed", FOFS(speed), F_FLOAT}, - {"accel", FOFS(accel), F_FLOAT}, - {"decel", FOFS(decel), F_FLOAT}, - {"target", FOFS(target), F_LSTRING}, - {"targetname", FOFS(targetname), F_LSTRING}, - {"pathtarget", FOFS(pathtarget), F_LSTRING}, - {"deathtarget", FOFS(deathtarget), F_LSTRING}, - {"killtarget", FOFS(killtarget), F_LSTRING}, - {"combattarget", FOFS(combattarget), F_LSTRING}, - {"message", FOFS(message), F_LSTRING}, - {"key_message", FOFS(key_message), F_LSTRING}, - {"team", FOFS(team), F_LSTRING}, - {"wait", FOFS(wait), F_FLOAT}, - {"delay", FOFS(delay), F_FLOAT}, - {"random", FOFS(random), F_FLOAT}, - {"move_origin", FOFS(move_origin), F_VECTOR}, - {"move_angles", FOFS(move_angles), F_VECTOR}, - {"style", FOFS(style), F_INT}, - {"count", FOFS(count), F_INT}, - {"health", FOFS(health), F_INT}, - {"sounds", FOFS(sounds), F_INT}, - {"light", 0, F_IGNORE}, - {"dmg", FOFS(dmg), F_INT}, - {"mass", FOFS(mass), F_INT}, - {"volume", FOFS(volume), F_FLOAT}, - {"attenuation", FOFS(attenuation), F_FLOAT}, - {"map", FOFS(map), F_LSTRING}, - {"origin", FOFS(s.origin), F_VECTOR}, - {"angles", FOFS(s.angles), F_VECTOR}, - {"angle", FOFS(s.angles), F_ANGLEHACK}, - - {"goalentity", FOFS(goalentity), F_EDICT, FFL_NOSPAWN}, - {"movetarget", FOFS(movetarget), F_EDICT, FFL_NOSPAWN}, - {"enemy", FOFS(enemy), F_EDICT, FFL_NOSPAWN}, - {"oldenemy", FOFS(oldenemy), F_EDICT, FFL_NOSPAWN}, - {"activator", FOFS(activator), F_EDICT, FFL_NOSPAWN}, - {"groundentity", FOFS(groundentity), F_EDICT, FFL_NOSPAWN}, - {"teamchain", FOFS(teamchain), F_EDICT, FFL_NOSPAWN}, - {"teammaster", FOFS(teammaster), F_EDICT, FFL_NOSPAWN}, - {"owner", FOFS(owner), F_EDICT, FFL_NOSPAWN}, - {"mynoise", FOFS(mynoise), F_EDICT, FFL_NOSPAWN}, - {"mynoise2", FOFS(mynoise2), F_EDICT, FFL_NOSPAWN}, - {"target_ent", FOFS(target_ent), F_EDICT, FFL_NOSPAWN}, - {"chain", FOFS(chain), F_EDICT, FFL_NOSPAWN}, - - {"prethink", FOFS(prethink), F_FUNCTION, FFL_NOSPAWN}, - {"think", FOFS(think), F_FUNCTION, FFL_NOSPAWN}, - {"blocked", FOFS(blocked), F_FUNCTION, FFL_NOSPAWN}, - {"touch", FOFS(touch), F_FUNCTION, FFL_NOSPAWN}, - {"use", FOFS(use), F_FUNCTION, FFL_NOSPAWN}, - {"pain", FOFS(pain), F_FUNCTION, FFL_NOSPAWN}, - {"die", FOFS(die), F_FUNCTION, FFL_NOSPAWN}, - - {"stand", FOFS(monsterinfo.stand), F_FUNCTION, FFL_NOSPAWN}, - {"idle", FOFS(monsterinfo.idle), F_FUNCTION, FFL_NOSPAWN}, - {"search", FOFS(monsterinfo.search), F_FUNCTION, FFL_NOSPAWN}, - {"walk", FOFS(monsterinfo.walk), F_FUNCTION, FFL_NOSPAWN}, - {"run", FOFS(monsterinfo.run), F_FUNCTION, FFL_NOSPAWN}, - {"dodge", FOFS(monsterinfo.dodge), F_FUNCTION, FFL_NOSPAWN}, - {"attack", FOFS(monsterinfo.attack), F_FUNCTION, FFL_NOSPAWN}, - {"melee", FOFS(monsterinfo.melee), F_FUNCTION, FFL_NOSPAWN}, - {"sight", FOFS(monsterinfo.sight), F_FUNCTION, FFL_NOSPAWN}, - {"jump", FOFS(monsterinfo.jump), F_FUNCTION, FFL_NOSPAWN}, - {"checkattack", FOFS(monsterinfo.checkattack), F_FUNCTION, FFL_NOSPAWN}, - {"currentmove", FOFS(monsterinfo.currentmove), F_MMOVE, FFL_NOSPAWN}, - - {"endfunc", FOFS(moveinfo.endfunc), F_FUNCTION, FFL_NOSPAWN}, - - // temp spawn vars -- only valid when the spawn function is called - {"lip", STOFS(lip), F_INT, FFL_SPAWNTEMP}, - {"distance", STOFS(distance), F_INT, FFL_SPAWNTEMP}, - {"height", STOFS(height), F_INT, FFL_SPAWNTEMP}, - {"noise", STOFS(noise), F_LSTRING, FFL_SPAWNTEMP}, - {"pausetime", STOFS(pausetime), F_FLOAT, FFL_SPAWNTEMP}, - {"phase", STOFS(phase), F_FLOAT, FFL_SPAWNTEMP}, - {"item", STOFS(item), F_LSTRING, FFL_SPAWNTEMP}, - {"shift", STOFS(shift), F_FLOAT, FFL_SPAWNTEMP}, - - //need for item field in edict struct, FFL_SPAWNTEMP item will be skipped on saves - {"item", FOFS(item), F_ITEM}, - - {"gravity", STOFS(gravity), F_LSTRING, FFL_SPAWNTEMP}, - {"skyname", STOFS(sky), F_LSTRING, FFL_SPAWNTEMP}, - {"skyrotate", STOFS(skyrotate), F_FLOAT, FFL_SPAWNTEMP}, - {"skyaxis", STOFS(skyaxis), F_VECTOR, FFL_SPAWNTEMP}, - {"minyaw", STOFS(minyaw), F_FLOAT, FFL_SPAWNTEMP}, - {"maxyaw", STOFS(maxyaw), F_FLOAT, FFL_SPAWNTEMP}, - {"minpitch", STOFS(minpitch), F_FLOAT, FFL_SPAWNTEMP}, - {"maxpitch", STOFS(maxpitch), F_FLOAT, FFL_SPAWNTEMP}, - {"nextmap", STOFS(nextmap), F_LSTRING, FFL_SPAWNTEMP}, - - {"aiflags", FOFS(monsterinfo.aiflags), F_INT}, - {"alpha", FOFS(alpha), F_FLOAT}, - {"axis", FOFS(axis), F_INT}, - {"bleft", FOFS(bleft), F_VECTOR}, - {"blood_type", FOFS(blood_type), F_INT}, - {"bob", FOFS(bob), F_FLOAT}, - {"bobframe", FOFS(bobframe), F_INT}, - {"busy", FOFS(busy), F_INT}, - {"class_id", FOFS(class_id), F_INT}, - {"color", FOFS(color), F_VECTOR}, - {"crane_beam", FOFS(crane_beam), F_EDICT, FFL_NOSPAWN}, - {"crane_bonk", FOFS(crane_bonk), F_VECTOR}, - {"crane_cable", FOFS(crane_cable), F_EDICT, FFL_NOSPAWN}, - {"crane_cargo", FOFS(crane_cargo), F_EDICT, FFL_NOSPAWN}, - {"crane_control", FOFS(crane_control), F_EDICT, FFL_NOSPAWN}, - {"crane_dir", FOFS(crane_dir), F_INT}, - {"crane_hoist", FOFS(crane_hoist), F_EDICT, FFL_NOSPAWN}, - {"crane_hook", FOFS(crane_hook), F_EDICT, FFL_NOSPAWN}, - {"crane_increment", FOFS(crane_increment), F_INT}, - {"crane_light", FOFS(crane_light), F_EDICT, FFL_NOSPAWN}, - {"crane_onboard_control", FOFS(crane_onboard_control), F_EDICT, FFL_NOSPAWN}, - {"datafile", FOFS(datafile), F_LSTRING}, - {"deadflag", FOFS(deadflag), F_INT}, - {"density", FOFS(density), F_FLOAT}, - {"dmgteam", FOFS(dmgteam), F_LSTRING}, - {"duration", FOFS(duration), F_FLOAT}, - {"effects", FOFS(effects), F_INT}, - {"fadein", FOFS(fadein), F_FLOAT}, - {"fadeout", FOFS(fadeout), F_FLOAT}, - {"flies", FOFS(monsterinfo.flies), F_FLOAT}, - {"frame", FOFS(s.frame), F_INT}, - {"framenumbers", FOFS(framenumbers), F_INT}, - {"gib_health", FOFS(gib_health), F_INT}, - {"gib_type", FOFS(gib_type), F_INT}, - {"health2", FOFS(health2), F_INT}, - {"holdtime", FOFS(holdtime), F_FLOAT}, - {"id", FOFS(id), F_INT}, - {"idle_noise", FOFS(idle_noise), F_LSTRING}, - {"jumpdn", FOFS(monsterinfo.jumpdn), F_FLOAT}, - {"jumpup", FOFS(monsterinfo.jumpup), F_FLOAT}, - {"mass2", FOFS(mass2), F_INT}, - {"max_health", FOFS(max_health), F_INT}, - {"max_range", FOFS(monsterinfo.max_range), F_FLOAT}, - {"moreflags", FOFS(moreflags), F_INT}, - {"move_to", FOFS(move_to), F_LSTRING}, - {"muzzle", FOFS(muzzle), F_VECTOR}, - {"muzzle2", FOFS(muzzle2), F_VECTOR}, - {"newtargetname", FOFS(newtargetname), F_LSTRING}, - {"next_grenade", FOFS(next_grenade), F_EDICT, FFL_NOSPAWN}, - {"origin_offset", FOFS(origin_offset), F_VECTOR}, - {"offset", FOFS(offset), F_VECTOR}, - {"org_maxs", FOFS(org_maxs), F_VECTOR}, - {"org_mins", FOFS(org_mins), F_VECTOR}, - {"org_size", FOFS(org_size), F_VECTOR}, - {"owner_id", FOFS(owner_id), F_INT}, - {"pitch_speed", FOFS(pitch_speed), F_FLOAT}, - {"powerarmor", FOFS(powerarmor), F_INT}, - {"prev_grenade", FOFS(prev_grenade), F_EDICT, FFL_NOSPAWN}, - {"prevpath", FOFS(prevpath), F_EDICT}, - {"radius", FOFS(radius), F_FLOAT}, - {"renderfx", FOFS(renderfx), F_INT}, - {"roll", FOFS(roll), F_FLOAT}, - {"roll_speed", FOFS(roll_speed), F_FLOAT}, - {"skin", FOFS(s.skin), F_INT}, - {"body", FOFS(s.body), F_INT}, - {"speaker", FOFS(speaker), F_EDICT, FFL_NOSPAWN}, - {"smooth_movement", FOFS(smooth_movement), F_INT}, - {"solidstate", FOFS(solidstate), F_INT}, - {"source", FOFS(source), F_LSTRING}, - {"startframe", FOFS(startframe), F_INT}, - {"target2", FOFS(target2), F_LSTRING}, - {"tright", FOFS(tright), F_VECTOR}, - {"turn_rider", FOFS(turn_rider), F_INT}, - {"usermodel", FOFS(usermodel), F_LSTRING}, - {"vehicle", FOFS(vehicle), F_EDICT, FFL_NOSPAWN}, - {"viewer", FOFS(viewer), F_EDICT}, - {"viewheight", FOFS(viewheight), F_INT}, - {"yaw_speed", FOFS(yaw_speed), F_FLOAT}, - - {0, 0, F_INT, 0} - -}; - -field_t levelfields[] = -{ - {"changemap", LLOFS(changemap), F_LSTRING}, - - {"sight_client", LLOFS(sight_client), F_EDICT}, - {"sight_entity", LLOFS(sight_entity), F_EDICT}, - {"sound_entity", LLOFS(sound_entity), F_EDICT}, - {"sound2_entity", LLOFS(sound2_entity), F_EDICT}, - - {NULL, 0, F_INT} -}; - -field_t clientfields[] = -{ - {"pers.weapon", CLOFS(pers.weapon), F_ITEM}, - {"pers.lastweapon", CLOFS(pers.lastweapon), F_ITEM}, - {"newweapon", CLOFS(newweapon), F_ITEM}, - - {NULL, 0, F_INT} -}; - -/* -============ -InitGame - -This will be called when the dll is first loaded, which -only happens when a new game is started or a save game -is loaded. -============ -*/ -void InitGame (void) -{ - gi.dprintf ("====== Init %s ======\n\n", GameTitle ); - - //init main memory pools - zone_level = Mem_AllocPool("Zone Level"); - zone_game = Mem_AllocPool("Zone Game"); - - gun_x = gi.cvar ("gun_x", "0", 0); - gun_y = gi.cvar ("gun_y", "0", 0); - gun_z = gi.cvar ("gun_z", "0", 0); - - //FIXME: sv_ prefix is wrong for these - sv_rollspeed = gi.cvar ("sv_rollspeed", "200", 0); - sv_rollangle = gi.cvar ("sv_rollangle", "2", 0); - sv_maxvelocity = gi.cvar ("sv_maxvelocity", "2000", 0); - sv_gravity = gi.cvar ("sv_gravity", "800", 0); - r_motionblur = gi.cvar ("r_motionblur", "0", CVAR_ARCHIVE); - - // noset vars - dedicated = gi.cvar ("dedicated", "0", CVAR_NOSET); - - // latched vars - sv_cheats = gi.cvar ("cheats", "0", CVAR_SERVERINFO|CVAR_LATCH); - gi.cvar ("gamename", GAMEVERSION , CVAR_SERVERINFO | CVAR_LATCH); - gi.cvar ("gamedate", __DATE__ , CVAR_SERVERINFO | CVAR_LATCH); - - maxclients = gi.cvar ("maxclients", "4", CVAR_SERVERINFO | CVAR_LATCH); - maxspectators = gi.cvar ("maxspectators", "4", CVAR_SERVERINFO); - deathmatch = gi.cvar ("deathmatch", "0", CVAR_LATCH); - coop = gi.cvar ("coop", "0", CVAR_LATCH); - skill = gi.cvar ("skill", "1", CVAR_LATCH); - maxentities = gi.cvar ("maxentities", "1024", CVAR_LATCH); - - // change anytime vars - dmflags = gi.cvar ("dmflags", "0", CVAR_SERVERINFO); - fraglimit = gi.cvar ("fraglimit", "0", CVAR_SERVERINFO); - timelimit = gi.cvar ("timelimit", "0", CVAR_SERVERINFO); - password = gi.cvar ("password", "", CVAR_USERINFO); - spectator_password = gi.cvar ("spectator_password", "", CVAR_USERINFO); - needpass = gi.cvar ("needpass", "0", CVAR_SERVERINFO); - filterban = gi.cvar ("filterban", "1", 0); - - g_select_empty = gi.cvar ("g_select_empty", "0", CVAR_ARCHIVE); - - run_pitch = gi.cvar ("run_pitch", "0.002", 0); - run_roll = gi.cvar ("run_roll", "0.005", 0); - bob_up = gi.cvar ("bob_up", "0.005", 0); - bob_pitch = gi.cvar ("bob_pitch", "0.002", 0); - bob_roll = gi.cvar ("bob_roll", "0.002", 0); - - // flood control - flood_msgs = gi.cvar ("flood_msgs", "4", 0); - flood_persecond = gi.cvar ("flood_persecond", "4", 0); - flood_waitdelay = gi.cvar ("flood_waitdelay", "10", 0); - - // dm map list - sv_maplist = gi.cvar ("sv_maplist", "", 0); - - alert_sounds = gi.cvar("alert_sounds", "0", CVAR_SERVERINFO|CVAR_LATCH); - cd_loopcount = gi.cvar("cd_loopcount","4",0); - cl_gun = gi.cvar("cl_gun", "1", 0); - corpse_fade = gi.cvar("corpse_fade", "0", CVAR_SERVERINFO|CVAR_LATCH); - corpse_fadetime = gi.cvar("corpse_fadetime", "20", 0); - crosshair = gi.cvar("crosshair", "1", 0); - fov = gi.cvar("fov", "90", 0); - hand = gi.cvar("hand", "0", 0); - jetpack_weenie = gi.cvar("jetpack_weenie", "0", CVAR_SERVERINFO); - joy_pitchsensitivity = gi.cvar("joy_pitchsensitivity", "1", 0); - joy_yawsensitivity = gi.cvar("joy_yawsensitivity", "-1", 0); - jump_kick = gi.cvar("jump_kick", "0", CVAR_SERVERINFO|CVAR_LATCH); - m_pitch = gi.cvar("m_pitch", "0.022", 0); - m_yaw = gi.cvar("m_yaw", "0.022", 0); - monsterjump = gi.cvar("monsterjump", "1", CVAR_SERVERINFO|CVAR_LATCH); - player_vampire = gi.cvar("player_vampire", "0", CVAR_SERVERINFO|CVAR_LATCH); - rocket_strafe = gi.cvar("rocket_strafe", "0", 0); - s_primary = gi.cvar("s_primary", "0", 0); - sv_maxgibs = gi.cvar("sv_maxgibs", "20", CVAR_SERVERINFO); - turn_rider = gi.cvar("turn_rider", "1", CVAR_SERVERINFO); - zoomrate = gi.cvar("zoomrate", "80", CVAR_ARCHIVE); - zoomsnap = gi.cvar("zoomsnap", "20", CVAR_ARCHIVE); - - // shift_ and rotate_distance only used for debugging stuff - this is the distance - // an entity will be moved by "item_left", "item_right", etc. - shift_distance = gi.cvar("shift_distance", "1", CVAR_SERVERINFO); - rotate_distance = gi.cvar("rotate_distance", "1", CVAR_SERVERINFO); - - // GL stuff - gl_clear = gi.cvar("gl_clear", "0", 0); - - // Lazarus saved cvars that we may or may not manipulate, but need to - // restore to original values upon map exit. - lazarus_cd_loop = gi.cvar("lazarus_cd_loop", "0", 0); - lazarus_gl_clear= gi.cvar("lazarus_gl_clear","0", 0); - lazarus_pitch = gi.cvar("lazarus_pitch", "0", 0); - lazarus_yaw = gi.cvar("lazarus_yaw", "0", 0); - lazarus_joyp = gi.cvar("lazarus_joyp", "0", 0); - lazarus_joyy = gi.cvar("lazarus_joyy", "0", 0); - lazarus_cl_gun = gi.cvar("lazarus_cl_gun", "0", 0); - lazarus_crosshair = gi.cvar("lazarus_crosshair", "0", 0); - - if(lazarus_gl_clear->value) - gi.cvar_forceset("gl_clear", va("%d",lazarus_gl_clear->value)); - else gi.cvar_forceset("lazarus_gl_clear", va("%d",gl_clear->value)); - - if(!deathmatch->value && !coop->value) - { - if(lazarus_pitch->value) - { - gi.cvar_forceset("cd_loopcount", va("%d",(int)(lazarus_cd_loop->value))); - gi.cvar_forceset("m_pitch", va("%f",lazarus_pitch->value)); - gi.cvar_forceset("m_yaw", va("%f",lazarus_yaw->value)); - gi.cvar_forceset("cl_gun", va("%d",(int)(lazarus_cl_gun->value))); - gi.cvar_forceset("crosshair", va("%d",(int)(lazarus_crosshair->value))); - } - else - { - gi.cvar_forceset("lazarus_cd_loop", va("%d",(int)(cd_loopcount->value))); - gi.cvar_forceset("lazarus_pitch", va("%f",m_pitch->value)); - gi.cvar_forceset("lazarus_yaw", va("%f",m_yaw->value)); - gi.cvar_forceset("lazarus_joyp", va("%f",joy_pitchsensitivity->value)); - gi.cvar_forceset("lazarus_joyy", va("%f",joy_yawsensitivity->value)); - gi.cvar_forceset("lazarus_cl_gun", va("%d",(int)(cl_gun->value))); - gi.cvar_forceset("lazarus_crosshair", va("%d",(int)(crosshair->value))); - } - } - - allow_download = gi.cvar("allow_download", "0", 0); - - // If this is an SP game and "readout" is not set, force allow_download off - // so we don't get the annoying "Refusing to download path with .." messages - // due to misc_actor sounds. - if(allow_download->value && !readout->value && !deathmatch->value) - gi.cvar_forceset("allow_download", "0"); - - bounce_bounce = gi.cvar("bounce_bounce", "0.5", 0); - bounce_minv = gi.cvar("bounce_minv", "60", 0); - - // items - InitItems (); - - sprintf (game.helpmessage1, ""); - - sprintf (game.helpmessage2, ""); - - // initialize all entities for this game - game.maxentities = maxentities->value; - g_edicts = (edict_t *)TagMalloc (game.maxentities * sizeof(g_edicts[0]), TAG_GAME); - globals.edicts = g_edicts; - globals.max_edicts = game.maxentities; - - // initialize all clients for this game - game.maxclients = maxclients->value; - game.clients = (gclient_t *)TagMalloc (game.maxclients * sizeof(game.clients[0]), TAG_GAME); - globals.num_edicts = game.maxclients+1; -} - -//========================================================= - -void WriteField1 (vfile_t *f, field_t *field, byte *base) -{ - void *p; - int len; - int index; - - if (field->flags & FFL_SPAWNTEMP) - return; - - p = (void *)(base + field->ofs); - switch (field->type) - { - case F_INT: - case F_FLOAT: - case F_ANGLEHACK: - case F_VECTOR: - case F_IGNORE: - break; - - case F_LSTRING: - case F_GSTRING: - if ( *(char **)p ) - len = strlen(*(char **)p) + 1; - else len = 0; - *(int *)p = len; - break; - case F_EDICT: - if ( *(edict_t **)p == NULL) - index = -1; - else index = *(edict_t **)p - g_edicts; - *(int *)p = index; - break; - case F_CLIENT: - if ( *(gclient_t **)p == NULL) - index = -1; - else index = *(gclient_t **)p - game.clients; - *(int *)p = index; - break; - case F_ITEM: - if ( *(edict_t **)p == NULL) - index = -1; - else index = *(gitem_t **)p - itemlist; - *(int *)p = index; - break; - //relative to code segment - case F_FUNCTION: - if (*(byte **)p == NULL) - index = 0; - else index = *(byte **)p - ((byte *)InitGame); - *(int *)p = index; - break; - - //relative to data segment - case F_MMOVE: - if (*(byte **)p == NULL) - index = 0; - else - index = *(byte **)p - (byte *)&mmove_reloc; - *(int *)p = index; - break; - - default: - gi.error ("WriteEdict: unknown field type"); - break; - } -} - - -void WriteField2 (vfile_t *f, field_t *field, byte *base) -{ - int len; - void *p; - - if (field->flags & FFL_SPAWNTEMP) - return; - - p = (void *)(base + field->ofs); - switch (field->type) - { - case F_LSTRING: - if ( *(char **)p ) - { - len = strlen(*(char **)p) + 1; - gi.VFs.Write (f, *(char **)p, len); - } - break; - } -} - -void ReadField (vfile_t *f, field_t *field, byte *base) -{ - void *p; - int len; - int index; - - if (field->flags & FFL_SPAWNTEMP) - return; - - p = (void *)(base + field->ofs); - switch (field->type) - { - case F_INT: - case F_FLOAT: - case F_ANGLEHACK: - case F_VECTOR: - case F_IGNORE: - break; - - case F_LSTRING: - len = *(int *)p; - if (!len) *(char **)p = NULL; - else - { - *(char **)p = (char *)TagMalloc (len, TAG_LEVEL); - gi.VFs.Read (f, *(char **)p, len); - } - break; - case F_EDICT: - index = *(int *)p; - if ( index == -1 ) - *(edict_t **)p = NULL; - else *(edict_t **)p = &g_edicts[index]; - break; - case F_CLIENT: - index = *(int *)p; - if ( index == -1 ) - *(gclient_t **)p = NULL; - else *(gclient_t **)p = &game.clients[index]; - break; - case F_ITEM: - index = *(int *)p; - if ( index == -1 ) - *(gitem_t **)p = NULL; - else *(gitem_t **)p = &itemlist[index]; - break; - - //relative to code segment - case F_FUNCTION: - index = *(int *)p; - if ( index == 0 ) - *(byte **)p = NULL; - else *(byte **)p = ((byte *)InitGame) + index; - break; - - //relative to data segment - case F_MMOVE: - index = *(int *)p; - if (index == 0) - *(byte **)p = NULL; - else *(byte **)p = (byte *)&mmove_reloc + index; - break; - - default: - gi.error ("ReadEdict: unknown field type"); - break; - } -} - -//========================================================= - -/* -============== -WriteClient - -All pointer variables (except function pointers) must be handled specially. -============== -*/ -void WriteClient (vfile_t *f, gclient_t *client) -{ - field_t *field; - gclient_t temp; - - // all of the ints, floats, and vectors stay as they are - temp = *client; - - // change the pointers to lengths or indexes - for (field = clientfields; field->name; field++) - { - WriteField1 (f, field, (byte *)&temp); - } - - // write the block - gi.VFs.Write (f, &temp, sizeof(temp)); - - // now write any allocated data following the edict - for (field = clientfields; field->name; field++) - { - WriteField2 (f, field, (byte *)client); - } -} - -/* -============== -ReadClient - -All pointer variables (except function pointers) must be handled specially. -============== -*/ -void ReadClient (vfile_t *f, gclient_t *client) -{ - field_t *field; - - gi.VFs.Read (f, client, sizeof(*client)); - - client->pers.spawn_landmark = false; - client->pers.spawn_levelchange = false; - for (field = clientfields; field->name; field++) - { - ReadField (f, field, (byte *)client); - } -} - -/* -============ -WriteGame - -This will be called whenever the game goes to a new level, -and when the user explicitly saves the game. - -Game information include cross level data, like multi level -triggers, help computer info, and all client states. - -A single player death will automatically restore from the -last save position. -============ -*/ -void WriteGame (vfile_t *f, bool autosave) -{ - int i; - char str[16]; - - if(developer->value) gi.dprintf ("==== WriteGame ====\n"); - - if (!autosave) game.transition_ents = 0; - if (!autosave) SaveClientData (); - - memset (str, 0, sizeof(str)); - strcpy (str, __DATE__ ); - gi.VFs.Write (f, str, sizeof(str)); - - game.autosaved = autosave; - gi.VFs.Write (f, &game, sizeof(game)); - game.autosaved = false; - - for (i = 0; i < game.maxclients; i++) - WriteClient (f, &game.clients[i]); -} - -void ReadGame (vfile_t *f) -{ - int i; - char str[16]; - - if(developer->value) gi.dprintf ("==== ReadGame ====\n"); - - FreeTags (TAG_GAME); - - gi.VFs.Read (f, str, sizeof(str)); - if (strcmp (str, __DATE__)) - { - gi.VFs.Close (f); - gi.error ("Savegame from an older version.\n"); - } - - g_edicts = (edict_t *)TagMalloc (game.maxentities * sizeof(g_edicts[0]), TAG_GAME); - globals.edicts = g_edicts; - - gi.VFs.Read (f, &game, sizeof(game)); - game.clients = (gclient_t *)TagMalloc (game.maxclients * sizeof(game.clients[0]), TAG_GAME); - for (i = 0; i < game.maxclients; i++) ReadClient (f, &game.clients[i]); -} - -//========================================================== - - -/* -============== -WriteEdict - -All pointer variables (except function pointers) must be handled specially. -============== -*/ -void WriteEdict (vfile_t *f, edict_t *ent) -{ - field_t *field; - edict_t temp; - - // all of the ints, floats, and vectors stay as they are - temp = *ent; - - // change the pointers to lengths or indexes - for (field = fields; field->name; field++) - { - WriteField1 (f, field, (byte *)&temp); - } - - // write the block - gi.VFs.Write (f, &temp, sizeof(temp)); - - // now write any allocated data following the edict - for (field = fields; field->name; field++) - { - WriteField2 (f, field, (byte *)ent); - } - -} - -/* -============== -WriteLevelLocals - -All pointer variables (except function pointers) must be handled specially. -============== -*/ -void WriteLevelLocals (vfile_t *f) -{ - field_t *field; - level_locals_t temp; - - // all of the ints, floats, and vectors stay as they are - temp = level; - - // change the pointers to lengths or indexes - for (field = levelfields; field->name; field++) - { - WriteField1 (f, field, (byte *)&temp); - } - - // write the block - gi.VFs.Write (f, &temp, sizeof(temp)); - - // now write any allocated data following the edict - for (field = levelfields; field->name; field++) - { - WriteField2 (f, field, (byte *)&level); - } -} - - -/* -============== -ReadEdict - -All pointer variables (except function pointers) must be handled specially. -============== -*/ -void ReadEdict (vfile_t *f, edict_t *ent) -{ - field_t *field; - - gi.VFs.Read (f, ent, sizeof(*ent)); - - for (field = fields; field->name; field++) - { - ReadField (f, field, (byte *)ent); - } -} - -/* -============== -ReadLevelLocals - -All pointer variables (except function pointers) must be handled specially. -============== -*/ -void ReadLevelLocals (vfile_t *f) -{ - field_t *field; - - gi.VFs.Read (f, &level, sizeof(level)); - - for (field = levelfields; field->name; field++) - { - ReadField (f, field, (byte *)&level); - } -} - -/* -================= -WriteLevel - -================= -*/ -void WriteLevel (vfile_t *f) -{ - int i; - edict_t *ent; - void *base; - - if(developer->value) gi.dprintf ("==== WriteLevel ====\n"); - - // write out edict size for checking - i = sizeof(edict_t); - gi.VFs.Write (f, &i, sizeof(i)); - - // write out a function pointer for checking - base = (void *)InitGame; - gi.VFs.Write (f, &base, sizeof(base)); - - // write out level_locals_t - WriteLevelLocals (f); - - // write out all the entities - for (i = 0; i < globals.num_edicts; i++) - { - ent = &g_edicts[i]; - if (!ent->inuse) continue; - gi.VFs.Write (f, &i, sizeof(i)); - WriteEdict (f, ent); - } - i = -1; - gi.VFs.Write (f, &i, sizeof(i)); -} - - -/* -================= -ReadLevel - -SpawnEntities will allready have been called on the -level the same way it was when the level was saved. - -That is necessary to get the baselines -set up identically. - -The server will have cleared all of the world links before -calling ReadLevel. - -No clients are connected yet. -================= -*/ -void ReadLevel (vfile_t *f) -{ - int entnum; - int i; - void *base; - edict_t *ent; - - if(developer->value) gi.dprintf ("==== ReadLevel ====\n"); - - // free any dynamic memory allocated by loading the level - // base state - FreeTags (TAG_LEVEL); - - // wipe all the entities - memset (g_edicts, 0, game.maxentities * sizeof(g_edicts[0])); - globals.num_edicts = maxclients->value+1; - - // check edict size - gi.VFs.Read (f, &i, sizeof(i)); - if (i != sizeof(edict_t)) - { - gi.VFs.Close (f); - gi.error ("ReadLevel: mismatched edict size"); - } - - // check function pointer base address - gi.VFs.Read (f, &base, sizeof(base)); - - // load the level locals - ReadLevelLocals (f); - - // load all the entities - while (1) - { - if (!gi.VFs.Read (f, &entnum, sizeof(entnum))) - { - gi.VFs.Close (f); - gi.error ("ReadLevel: failed to read entnum"); - } - if (entnum == -1) break; - if (entnum >= globals.num_edicts) - globals.num_edicts = entnum+1; - - ent = &g_edicts[entnum]; - ReadEdict (f, ent); - - // let the server rebuild world links for this ent - memset (&ent->area, 0, sizeof(ent->area)); - gi.linkentity (ent); - } - - gi.VFs.Close (f); - - // mark all clients as unconnected - for (i = 0; i < maxclients->value; i++) - { - ent = &g_edicts[i+1]; - ent->client = game.clients + i; - ent->client->pers.connected = false; - } - - // do any load time things at this point - for (i = 0; i < globals.num_edicts; i++) - { - ent = &g_edicts[i]; - if (!ent->inuse) continue; - - // fire any cross-level triggers - if (ent->class_id == ENTITY_TARGET_CROSSLEVEL_TARGET) - ent->nextthink = level.time + ent->delay; - } -} - -void WriteLump (dsavehdr_t *hdr, file_t *f, int lumpnum, bool autosave) -{ - lump_t *lump; - vfile_t *vf; - - vf = gi.VFs.Open( f, "w" ); - - switch( lumpnum ) - { - case LUMP_GAMELEVEL: WriteLevel( vf ); break; - case LUMP_GAMELOCAL: WriteGame( vf, false ); break;//FIXME - default: - gi.dprintf("unsupported lump num\n"); - return; - } - - lump = &hdr->lumps[lumpnum]; - lump->fileofs = LittleLong( gi.Fs.Tell(f)); - lump->filelen = LittleLong( gi.VFs.Tell(vf)); - gi.VFs.Close( vf ); //write into disk -} - -void ReadLump (byte *base, lump_t *l, int lumpnum) -{ - vfile_t *vf; - byte *in; - - in = (byte *)(base + l->fileofs); - if (l->filelen % sizeof(*in)) gi.error("Sav_LoadGame: funny lump size\n" ); - - vf = gi.VFs.Create(in, l->filelen ); - - switch( lumpnum ) - { - case LUMP_GAMELEVEL: - ReadLevel( vf ); //no need to closed - break; - case LUMP_GAMELOCAL: - ReadGame( vf ); - gi.VFs.Close( vf ); - break; - default: - gi.dprintf("unsupported lump num\n"); - return; - } - - -} \ No newline at end of file diff --git a/server/g_spawn.c b/server/g_spawn.c deleted file mode 100644 index d4a026f8..00000000 --- a/server/g_spawn.c +++ /dev/null @@ -1,752 +0,0 @@ -#include "baseentity.h" - -void SP_info_player_start (edict_t *ent); -void SP_info_player_deathmatch (edict_t *ent); -void SP_info_player_coop (edict_t *ent); -void SP_info_player_intermission (edict_t *ent); -void SP_worldspawn (edict_t *ent); -void SP_misc_explobox (edict_t *self); - -spawn_t spawns[] = -{ - {"info_player_start", SP_info_player_start}, - {"info_player_deathmatch", SP_info_player_deathmatch}, - {"info_player_coop", SP_info_player_coop}, - {"info_player_intermission", SP_info_player_intermission}, - - - {"misc_explobox", SP_misc_explobox}, - {"worldspawn", SP_worldspawn}, - {NULL, NULL} -}; - -/* -=============== -ED_CallSpawn - -Finds the spawn function for the entity and calls it -=============== -*/ -void ED_CallSpawn (edict_t *ent) -{ - spawn_t *s; - gitem_t *item; - int i; - - // Lazarus: if this fails, edict is freed. - - if (!ent->classname) - { - gi.dprintf ("ED_CallSpawn: NULL classname\n"); - G_FreeEdict(ent); - return; - } - - // check item spawn functions - for (i=0,item=itemlist ; iclassname) - continue; - if (!strcmp(item->classname, ent->classname)) - { // found it - SpawnItem (ent, item); - return; - } - } - - // check normal spawn functions - for (s=spawns ; s->name ; s++) - { - if (!strcmp(s->name, ent->classname)) - { // found it - s->spawn (ent); - return; - } - } - gi.dprintf ("%s doesn't have a spawn function\n", ent->classname); - G_FreeEdict(ent); -} - -/* -============= -ED_NewString -============= -*/ -char *ED_NewString (const char *string) -{ - char *newb, *new_p; - int i,l; - - l = strlen(string) + 1; - - newb = (char *)TagMalloc (l, TAG_LEVEL); - - new_p = newb; - - for (i=0 ; i< l ; i++) - { - if (string[i] == '\\' && i < l-1) - { - i++; - if (string[i] == 'n') - *new_p++ = '\n'; - else - *new_p++ = '\\'; - } - else - *new_p++ = string[i]; - } - - return newb; -} - - - - -/* -=============== -ED_ParseField - -Takes a key/value pair and sets the binary values -in an edict -=============== -*/ -void ED_ParseField (char *key, const char *value, edict_t *ent) -{ - field_t *f; - byte *b; - float v; - vec3_t vec; - - for (f=fields ; f->name ; f++) - { - if (!(f->flags & FFL_NOSPAWN) && !strcasecmp(f->name, key)) - { // found it - if (f->flags & FFL_SPAWNTEMP) - b = (byte *)&st; - else - b = (byte *)ent; - - switch (f->type) - { - case F_LSTRING: - *(char **)(b+f->ofs) = ED_NewString (value); - break; - case F_VECTOR: - sscanf (value, "%f %f %f", &vec[0], &vec[1], &vec[2]); - ((float *)(b+f->ofs))[0] = vec[0]; - ((float *)(b+f->ofs))[1] = vec[1]; - ((float *)(b+f->ofs))[2] = vec[2]; - break; - case F_INT: - *(int *)(b+f->ofs) = atoi(value); - break; - case F_FLOAT: - *(float *)(b+f->ofs) = atof(value); - break; - case F_ANGLEHACK: - v = atof(value); - ((float *)(b+f->ofs))[0] = 0; - ((float *)(b+f->ofs))[1] = v; - ((float *)(b+f->ofs))[2] = 0; - break; - case F_IGNORE: - break; - } - return; - } - } - gi.dprintf ("%s is not a field\n", key); -} - -/* -==================== -ED_ParseEdict - -Parses an edict out of the given string, returning the new position -ed should be a properly initialized empty edict. -==================== -*/ -char *ED_ParseEdict (char *data, edict_t *ent) -{ - bool init; - char keyname[256]; - char *com_token; - - init = false; - memset (&st, 0, sizeof(st)); - - // go through all the dictionary pairs - while (1) - { - // parse key - com_token = COM_Parse (&data); - if (com_token[0] == '}') break; - if (!data) gi.error ("ED_ParseEntity: EOF without closing brace"); - - strncpy (keyname, com_token, sizeof(keyname)-1); - - // parse value - com_token = COM_Parse (&data); - if (!data) gi.error ("ED_ParseEntity: EOF without closing brace"); - - if (com_token[0] == '}') - gi.error ("ED_ParseEntity: closing brace without data"); - - init = true; - - // keynames with a leading underscore are used for utility comments, - // and are immediately discarded by quake - if (keyname[0] == '_') continue; - - ED_ParseField (keyname, com_token, ent); - } - - if (!init) memset (ent, 0, sizeof(*ent)); - - return (char *)data; -} - - -/* -================ -G_FindTeams - -Chain together all entities with a matching team field. - -All but the first will have the FL_TEAMSLAVE flag set. -All but the last will have the teamchain field set to the next one -================ -*/ -void G_FindTeams (void) -{ - edict_t *e, *e2, *chain; - int i, j; - int c, c2; - - c = 0; - c2 = 0; - for (i=1, e=g_edicts+i ; i < globals.num_edicts ; i++,e++) - { - if (!e->inuse) - continue; - if (!e->team) - continue; - if (e->flags & FL_TEAMSLAVE) - continue; - // Lazarus: some entities may have psuedo-teams that shouldn't be handled here - if (e->class_id == ENTITY_TARGET_CHANGE) - continue; - if (e->class_id == ENTITY_TARGET_CLONE) - continue; - chain = e; - e->teammaster = e; - c++; - c2++; - for (j=i+1, e2=e+1 ; j < globals.num_edicts ; j++,e2++) - { - if (!e2->inuse) - continue; - if (!e2->team) - continue; - if (e2->flags & FL_TEAMSLAVE) - continue; - if (!strcmp(e->team, e2->team)) - { - c2++; - chain->teamchain = e2; - e2->teammaster = e; - chain = e2; - e2->flags |= FL_TEAMSLAVE; - } - } - } - - if(level.time < 2) - gi.dprintf ("%i teams with %i entities\n", c, c2); -} -/* -============== -SpawnEntities - -Creates a server's entity / program execution context by -parsing textual entity definitions out of an ent file. -============== -*/ -void SpawnEntities (char *mapname, char *entities, char *spawnpoint) -{ - edict_t *ent; - int inhibit; - char *com_token; - int i; - float skill_level; - extern int max_modelindex; - extern int max_soundindex; - - if(developer->value) - gi.dprintf("====== SpawnEntities ========\n"); - skill_level = floor (skill->value); - if (skill_level < 0) - skill_level = 0; - if (skill_level > 3) - skill_level = 3; - if (skill->value != skill_level) - gi.cvar_forceset("skill", va("%f", skill_level)); - - SaveClientData (); - - FreeTags (TAG_LEVEL); - - memset (&level, 0, sizeof(level)); - memset (g_edicts, 0, game.maxentities * sizeof (g_edicts[0])); - // Lazarus: these are used to track model and sound indices - // in g_main.c: - max_modelindex = 0; - max_soundindex = 0; - - strncpy (level.mapname, mapname, sizeof(level.mapname)-1); - strncpy (game.spawnpoint, spawnpoint, sizeof(game.spawnpoint)-1); - - // set client fields on player ents - for (i=0 ; iclassname, "trigger_once") && !strcasecmp(ent->model, "*27")) - ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; - - // remove things (except the world) from different skill levels or deathmatch - if (ent != g_edicts) - { - if (deathmatch->value) - { - if ( ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH ) - { - G_FreeEdict (ent); - inhibit++; - continue; - } - } - else - { - if (((skill->value == 0) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) || - ((skill->value == 1) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || - (((skill->value == 2) || (skill->value == 3)) && (ent->spawnflags & SPAWNFLAG_NOT_HARD)) - ) - { - G_FreeEdict (ent); - inhibit++; - continue; - } - } - - ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY|SPAWNFLAG_NOT_MEDIUM|SPAWNFLAG_NOT_HARD|SPAWNFLAG_NOT_DEATHMATCH); - } - - ED_CallSpawn (ent); - ent->s.renderfx |= RF_IR_VISIBLE; //PGM - } - - gi.dprintf ("%i entities inhibited\n", inhibit); - - G_FindTeams (); - - // Get origin offsets (mainly for brush models w/o origin brushes) - for (i=1, ent=g_edicts+i ; i < globals.num_edicts ; i++,ent++) - { - VectorAdd(ent->absmin,ent->absmax,ent->origin_offset); - VectorScale(ent->origin_offset,0.5,ent->origin_offset); - VectorSubtract(ent->origin_offset,ent->s.origin,ent->origin_offset); - } -} - - -//=================================================================== - -#if 0 - // cursor positioning - xl - xr - yb - yt - xv - yv - - // drawing - statpic - pic - num - string - - // control - if - ifeq - ifbit - endif - -#endif - -char *single_statusbar = -"yb -24 " - -// health -"xv 0 " -"hnum " -"xv 50 " -"pic 0 " - -// ammo -"if 2 " -"{ xv 100 " -"anum " -"xv 150 " -"pic 2 " -"} " - -// armor -"if 4 " -"{ xv 200 " -"rnum " -"xv 250 " -"pic 4 " -"} " - -// selected item -"if 6 " -"{ xv 296 " -"pic 6 " -"} " - -"yb -50 " - -// picked up item -"if 7 " -"{ xv 0 " -"pic 7 " -"xv 26 " -"yb -42 " -"stat_string 8 " -"yb -50 " -"} " - -// timer (was xv 262) -"if 9 " -"{ xv 230 " -"num 4 10 " -"xv 296 " -"pic 9 " -"} " - -// help / weapon icon -"if 11 " -"{ xv 148 " -"pic 11 " -"} " - -// vehicle speed -"if 22 " -"{ yb -90 " -"xv 128 " -"pic 22 " -"} " - -// zoom -"if 23 " -"{ yv 0 " -"xv 0 " -"pic 23 " -"} " -; - - -char *dm_statusbar = -"yb -24 " - -// health -"xv 0 " -"hnum " -"xv 50 " -"pic 0 " - -// ammo -"if 2 " -"{ xv 100 " -"anum " -"xv 150 " -"pic 2 " -"} " - -// armor -"if 4 " -"{ xv 200 " -"rnum " -"xv 250 " -"pic 4 " -"} " - -// selected item -"if 6 " -"{ xv 296 " -"pic 6 " -"} " - -"yb -50 " - -// picked up item -"if 7 " -"{ xv 0 " -"pic 7 " -"xv 26 " -"yb -42 " -"stat_string 8 " -"yb -50 " -"} " - -// timer -"if 9 " -"{ xv 230 " -"num 4 10 " -"xv 296 " -"pic 9 " -"} " - -// help / weapon icon -"if 11 " -"{ xv 148 " -"pic 11 " -"} " - -// frags -"xr -50 " -"yt 2 " -"num 3 14 " - -// spectator -"if 17 " -"{ xv 0 " -"yb -58 " -"string2 \"SPECTATOR MODE\" " -"} " - -// chase camera -"if 16 " -"{ xv 0 " -"yb -68 " -"string \"Chasing\" " -"xv 64 " -"stat_string 16 " -"} " - -// vehicle speed -"if 22 " -"{ yb -90 " -"xv 128 " -"pic 22 " -"} " -; - -/*QUAKED worldspawn (0 0 0) ? - -Only used for the world. -"skyname" environment map name -"skyaxis" vector axis for rotating sky -"skyrotate" speed of rotation in degrees/second -"sounds" music cd track number -"gravity" 800 is default gravity -"message" text to print at user logon -*/ -//void SetChromakey(); -void SP_worldspawn (edict_t *ent) -{ - ent->class_id = ENTITY_WORLDSPAWN; - ent->movetype = MOVETYPE_PUSH; - ent->solid = SOLID_BSP; - ent->inuse = true; // since the world doesn't use G_Spawn() - ent->s.modelindex = 1; // world model is always index 1 - - //--------------- - - // reserve some spots for dead player bodies for coop / deathmatch - InitBodyQue (); - - // set configstrings for items - SetItemNames (); - - if (st.nextmap) - strcpy (level.nextmap, st.nextmap); - - // make some data visible to the server - - if (ent->message && ent->message[0]) - { - gi.configstring (CS_NAME, ent->message); - strncpy (level.level_name, ent->message, sizeof(level.level_name)); - } - else - strncpy (level.level_name, level.mapname, sizeof(level.level_name)); - - if (st.sky && st.sky[0]) - gi.configstring (CS_SKY, st.sky); - else - gi.configstring (CS_SKY, "sky"); - - gi.configstring (CS_SKYROTATE, va("%f", st.skyrotate) ); - - gi.configstring (CS_SKYAXIS, va("%f %f %f", - st.skyaxis[0], st.skyaxis[1], st.skyaxis[2]) ); - - gi.configstring (CS_CDTRACK, va("%i", ent->sounds) ); - - gi.configstring (CS_MAXCLIENTS, va("%i", (int)(maxclients->value) ) ); - - // status bar program - if (deathmatch->value) - gi.configstring (CS_STATUSBAR, dm_statusbar); - else - gi.configstring (CS_STATUSBAR, single_statusbar); - - //--------------- - - // help icon for statusbar - gi.imageindex ("i_help"); - level.pic_health = gi.imageindex ("i_health"); - gi.imageindex ("help"); - gi.imageindex ("field_3"); - - if (!st.gravity) - gi.cvar_set("sv_gravity", "800"); - else - gi.cvar_set("sv_gravity", st.gravity); - - snd_fry = gi.soundindex ("player/fry.wav"); // standing in lava / slime - - PrecacheItem (FindItem ("Blaster")); - - gi.soundindex ("player/lava1.wav"); - gi.soundindex ("player/lava2.wav"); - - gi.soundindex ("misc/pc_up.wav"); - gi.soundindex ("misc/talk1.wav"); - - gi.soundindex ("misc/udeath.wav"); - - // gibs - gi.soundindex ("items/respawn1.wav"); - - // sexed sounds - gi.soundindex ("*death1.wav"); - gi.soundindex ("*death2.wav"); - gi.soundindex ("*death3.wav"); - gi.soundindex ("*death4.wav"); - gi.soundindex ("*fall1.wav"); - gi.soundindex ("*fall2.wav"); - gi.soundindex ("*gurp1.wav"); // drowning damage - gi.soundindex ("*gurp2.wav"); - gi.soundindex ("*jump1.wav"); // player jump - gi.soundindex ("*pain25_1.wav"); - gi.soundindex ("*pain25_2.wav"); - gi.soundindex ("*pain50_1.wav"); - gi.soundindex ("*pain50_2.wav"); - gi.soundindex ("*pain75_1.wav"); - gi.soundindex ("*pain75_2.wav"); - gi.soundindex ("*pain100_1.wav"); - gi.soundindex ("*pain100_2.wav"); - - gi.modelindex ("#w_blaster.md2"); - - gi.soundindex ("player/gasp1.wav"); // gasping for air - gi.soundindex ("player/gasp2.wav"); // head breaking surface, not gasping - - gi.soundindex ("player/watr_in.wav"); // feet hitting water - gi.soundindex ("player/watr_out.wav"); // feet leaving water - - gi.soundindex ("player/watr_un.wav"); // head going underwater - - gi.soundindex ("player/u_breath1.wav"); - gi.soundindex ("player/u_breath2.wav"); - - gi.soundindex ("items/pkup.wav"); // bonus item pickup - gi.soundindex ("world/land.wav"); // landing thud - gi.soundindex ("misc/h2ohit1.wav"); // landing splash - - gi.soundindex ("items/damage.wav"); - gi.soundindex ("items/protect.wav"); - gi.soundindex ("items/protect4.wav"); - gi.soundindex ("weapons/noammo.wav"); - - gi.soundindex ("infantry/inflies1.wav"); - - sm_meat_index = gi.modelindex ("models/objects/gibs/sm_meat/tris.md2"); - gi.modelindex ("models/objects/gibs/arm/tris.md2"); - gi.modelindex ("models/objects/gibs/bone/tris.md2"); - gi.modelindex ("models/objects/gibs/bone2/tris.md2"); - gi.modelindex ("models/objects/gibs/chest/tris.md2"); - gi.modelindex ("models/objects/gibs/skull/tris.md2"); - gi.modelindex ("models/objects/gibs/head2/tris.md2"); - - gi.soundindex ("mud/mud_in2.wav"); - gi.soundindex ("mud/mud_out1.wav"); - gi.soundindex ("mud/mud_un1.wav"); - gi.soundindex ("mud/wade_mud1.wav"); - gi.soundindex ("mud/wade_mud2.wav"); - - // FMOD 3D sound attenuation: - if(ent->attenuation <= 0.) - ent->attenuation = 1.0; - - // FMOD 3D sound Doppler shift: - if(st.shift > 0) - ent->moveinfo.distance = st.shift; - else if(st.shift < 0) - ent->moveinfo.distance = 0.0; - else - ent->moveinfo.distance = 1.0; - - // cvar overrides for effects flags: - if(alert_sounds->value) - world->effects |= FX_WORLDSPAWN_ALERTSOUNDS; - if(corpse_fade->value) - world->effects |= FX_WORLDSPAWN_CORPSEFADE; - if(jump_kick->value) - world->effects |= FX_WORLDSPAWN_JUMPKICK; -} - -int nohud = 0; - -void Hud_On() -{ - if (deathmatch->value) gi.configstring (CS_STATUSBAR, dm_statusbar); - else gi.configstring (CS_STATUSBAR, single_statusbar); - nohud = 0; -} - -void Hud_Off() -{ - gi.configstring (CS_STATUSBAR, NULL); - nohud = 1; -} - -void Cmd_ToggleHud () -{ - if (deathmatch->value) return; - if (nohud) Hud_On(); - else Hud_Off(); -} diff --git a/server/g_svcmds.c b/server/g_svcmds.c deleted file mode 100644 index 7215d332..00000000 --- a/server/g_svcmds.c +++ /dev/null @@ -1,273 +0,0 @@ - -#include "baseentity.h" - - -void Svcmd_Test_f (void) -{ - gi.cprintf (NULL, PRINT_HIGH, "Svcmd_Test_f()\n"); -} - -/* -============================================================================== - -PACKET FILTERING - - -You can add or remove addresses from the filter list with: - -addip -removeip - -The ip address is specified in dot format, and any unspecified digits will match any value, so you can specify an entire class C network with "addip 192.246.40". - -Removeip will only remove an address specified exactly the same way. You cannot addip a subnet, then removeip a single host. - -listip -Prints the current list of filters. - -writeip -Dumps "addip " commands to listip.cfg so it can be execed at a later date. The filter lists are not saved and restored by default, because I beleive it would cause too much confusion. - -filterban <0 or 1> - -If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game. This is the default setting. - -If 0, then only addresses matching the list will be allowed. This lets you easily set up a private game, or a game that only allows players from your local network. - - -============================================================================== -*/ - -typedef struct -{ - unsigned mask; - unsigned compare; -} ipfilter_t; - -#define MAX_IPFILTERS 1024 - -ipfilter_t ipfilters[MAX_IPFILTERS]; -int numipfilters; - -/* -================= -StringToFilter -================= -*/ -static bool StringToFilter (char *s, ipfilter_t *f) -{ - char num[128]; - int i, j; - byte b[4]; - byte m[4]; - - for (i=0 ; i<4 ; i++) - { - b[i] = 0; - m[i] = 0; - } - - for (i=0 ; i<4 ; i++) - { - if (*s < '0' || *s > '9') - { - gi.cprintf(NULL, PRINT_HIGH, "Bad filter address: %s\n", s); - return false; - } - - j = 0; - while (*s >= '0' && *s <= '9') - { - num[j++] = *s++; - } - num[j] = 0; - b[i] = atoi(num); - if (b[i] != 0) - m[i] = 255; - - if (!*s) - break; - s++; - } - - f->mask = *(unsigned *)m; - f->compare = *(unsigned *)b; - - return true; -} - -/* -================= -SV_FilterPacket -================= -*/ -bool SV_FilterPacket (char *from) -{ - int i; - unsigned in; - byte m[4]; - char *p; - - i = 0; - p = from; - while (*p && i < 4) { - m[i] = 0; - while (*p >= '0' && *p <= '9') { - m[i] = m[i]*10 + (*p - '0'); - p++; - } - if (!*p || *p == ':') - break; - i++, p++; - } - - in = *(unsigned *)m; - - for (i=0 ; ivalue; - - return (int)!filterban->value; -} - - -/* -================= -SV_AddIP_f -================= -*/ -void SVCmd_AddIP_f (void) -{ - int i; - - if (gi.argc() < 3) { - gi.cprintf(NULL, PRINT_HIGH, "Usage: addip \n"); - return; - } - - for (i=0 ; i\n"); - return; - } - - if (!StringToFilter (gi.argv(2), &f)) - return; - - for (i=0 ; ivalue); - - for (i = 0; i < numipfilters; i++) - { - *(unsigned *)b = ipfilters[i].compare; - gi.Fs.Printf (f, "sv addip %i.%i.%i.%i\n", b[0], b[1], b[2], b[3]); - } - - gi.Fs.Close (f); -} - -/* -================= -ServerCommand - -ServerCommand will be called when an "sv" command is issued. -The game can issue gi.argc() / gi.argv() commands to get the rest -of the parameters -================= -*/ -void ServerCommand (void) -{ - char *cmd; - - cmd = gi.argv(1); - if (strcasecmp (cmd, "test") == 0) - Svcmd_Test_f (); - else if (strcasecmp (cmd, "addip") == 0) - SVCmd_AddIP_f (); - else if (strcasecmp (cmd, "removeip") == 0) - SVCmd_RemoveIP_f (); - else if (strcasecmp (cmd, "listip") == 0) - SVCmd_ListIP_f (); - else if (strcasecmp (cmd, "writeip") == 0) - SVCmd_WriteIP_f (); - else - gi.cprintf (NULL, PRINT_HIGH, "Unknown server command \"%s\"\n", cmd); -} - diff --git a/server/g_utils.c b/server/g_utils.c deleted file mode 100644 index 1ad3de49..00000000 --- a/server/g_utils.c +++ /dev/null @@ -1,1399 +0,0 @@ -// g_utils.c -- misc utility functions for game module - -#include "baseentity.h" - -void *TagMalloc (int size, int tag) -{ - if(tag == TAG_LEVEL) return Mem_Alloc(zone_level, size); - else if(tag == TAG_GAME) return Mem_Alloc(zone_game, size); - - gi.dprintf("Warning: try to alloc unknown tag\n"); - return NULL; -} - -void FreeTags (int tag) -{ - if(tag == TAG_LEVEL) Mem_EmptyPool(zone_level); - else if(tag == TAG_GAME) Mem_EmptyPool(zone_game); - else gi.dprintf("Warning: try to free unknown tag\n"); -} - -void G_ProjectSource (vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result) -{ - result[0] = point[0] + forward[0] * distance[0] + right[0] * distance[1]; - result[1] = point[1] + forward[1] * distance[0] + right[1] * distance[1]; - result[2] = point[2] + forward[2] * distance[0] + right[2] * distance[1] + distance[2]; -} - -void G_ProjectSource2 (vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t up, vec3_t result) -{ - result[0] = point[0] + forward[0] * distance[0] + right[0] * distance[1] + up[0] * distance[2]; - result[1] = point[1] + forward[1] * distance[0] + right[1] * distance[1] + up[1] * distance[2]; - result[2] = point[2] + forward[2] * distance[0] + right[2] * distance[1] + up[2] * distance[2]; -} - -/* -============= -G_Find - -Searches all active entities for the next one that holds -the matching string at fieldofs (use the FOFS() macro) in the structure. - -Searches beginning at the edict after from, or the beginning if NULL -NULL will be returned if the end of the list is reached. - -============= -*/ -edict_t *G_Find (edict_t *from, int fieldofs, char *match) -{ - char *s; - - if (!from) - from = g_edicts; - else - from++; - - for ( ; from < &g_edicts[globals.num_edicts] ; from++) - { - if (!from->inuse) - continue; - s = *(char **) ((byte *)from + fieldofs); - if (!s) - continue; - if (!strcasecmp (s, match)) - return from; - } - - return NULL; -} - - -/* -================= -findradius - -Returns entities that have origins within a spherical area - -findradius (origin, radius) -================= -*/ -edict_t *findradius (edict_t *from, vec3_t org, float rad) -{ - vec3_t eorg; - int j; - - if (!from) - from = g_edicts; - else - from++; - for ( ; from < &g_edicts[globals.num_edicts]; from++) - { - if (!from->inuse) - continue; - if (from->solid == SOLID_NOT) - continue; - for (j=0 ; j<3 ; j++) - eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5); - if (VectorLength(eorg) > rad) - continue; - return from; - } - - return NULL; -} - -/* -================= -findradius2 - -Returns entities that have origins within a spherical area - -only returns entities that can be damaged -only returns entities that are SVF_DAMAGEABLE - -findradius2 (origin, radius) -================= -*/ -edict_t *findradius2 (edict_t *from, vec3_t org, float rad) -{ - // rad must be positive - vec3_t eorg; - int j; - - if (!from) - from = g_edicts; - else - from++; - for ( ; from < &g_edicts[globals.num_edicts]; from++) - { - if (!from->inuse) - continue; - if (from->solid == SOLID_NOT) - continue; - if (!from->takedamage) - continue; - for (j=0 ; j<3 ; j++) - eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5); - if (VectorLength(eorg) > rad) - continue; - return from; - } - - return NULL; -} - -/* -============= -G_PickTarget - -Searches all active entities for the next one that holds -the matching string at fieldofs (use the FOFS() macro) in the structure. - -Searches beginning at the edict after from, or the beginning if NULL -NULL will be returned if the end of the list is reached. - -============= -*/ -#define MAXCHOICES 8 - -edict_t *G_PickTarget (char *targetname) -{ - edict_t *ent = NULL; - int num_choices = 0; - edict_t *choice[MAXCHOICES]; - - if (!targetname) - { - gi.dprintf("G_PickTarget called with NULL targetname\n"); - return NULL; - } - - while(1) - { - ent = G_Find (ent, FOFS(targetname), targetname); - if (!ent) - break; - choice[num_choices++] = ent; - if (num_choices == MAXCHOICES) - break; - } - - if (!num_choices) - { - gi.dprintf("G_PickTarget: target %s not found\n", targetname); - return NULL; - } - - return choice[rand() % num_choices]; -} - - - -void Think_Delay (edict_t *ent) -{ - G_UseTargets (ent, ent->activator); - G_FreeEdict (ent); -} - -/* -============================== -G_UseTargets - -the global "activator" should be set to the entity that initiated the firing. - -If self.delay is set, a DelayedUse entity will be created that will actually -do the SUB_UseTargets after that many seconds have passed. - -Centerprints any self.message to the activator. - -Search for (string)targetname in all entities that -match (string)self.target and call their .use function - -============================== -*/ -void G_UseTargets (edict_t *ent, edict_t *activator) -{ - edict_t *t; - -// -// check for a delay -// - if (ent->delay) - { - // create a temp object to fire at a later time - t = G_Spawn(); - t->classname = "DelayedUse"; - t->nextthink = level.time + ent->delay; - t->think = Think_Delay; - t->activator = activator; - if (!activator) - gi.dprintf ("Think_Delay with no activator\n"); - t->message = ent->message; - t->target = ent->target; - t->killtarget = ent->killtarget; - t->noise_index = ent->noise_index; - return; - } - - -// -// print the message -// - if ((ent->message) && !(activator->svflags & SVF_MONSTER)) - { -// Lazarus - change so that noise_index < 0 means no sound - gi.centerprintf (activator, "%s", ent->message); - if (ent->noise_index > 0) - gi.sound (activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0); - else if (ent->noise_index == 0) - gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0); - } - -// -// kill killtargets -// - if (ent->killtarget) - { - t = NULL; - while ((t = G_Find (t, FOFS(targetname), ent->killtarget))) - { - // Lazarus: remove LIVE killtargeted monsters from total_monsters - if((t->svflags & SVF_MONSTER) && (t->deadflag == DEAD_NO)) - { - if(!t->dmgteam || strcmp(t->dmgteam,"player")) - if(!(t->monsterinfo.aiflags & AI_GOOD_GUY)) - level.total_monsters--; - } - // and decrement secret count if target_secret is removed - else if(t->class_id == ENTITY_TARGET_SECRET) - level.total_secrets--; - // same deal with target_goal, but also turn off CD music if applicable - else if(t->class_id == ENTITY_TARGET_GOAL) - { - level.total_goals--; - if (level.found_goals >= level.total_goals) - gi.configstring (CS_CDTRACK, "0"); - } - G_FreeEdict (t); - if (!ent->inuse) - { - gi.dprintf("entity was removed while using killtargets\n"); - return; - } - } - } - -// -// fire targets -// - if (ent->target) - { - t = NULL; - while ((t = G_Find (t, FOFS(targetname), ent->target))) - { - // doors fire area portals in a specific way - if ( (t->class_id == ENTITY_FUNC_AREAPORTAL) && - ( (ent->class_id == ENTITY_FUNC_DOOR) || - (ent->class_id == ENTITY_FUNC_DOOR_ROTATING)) ) - continue; - - if (t == ent) - { - gi.dprintf ("WARNING: Entity used itself.\n"); - } - else - { - if (t->use) - t->use (t, ent, activator); - } - if (!ent->inuse) - { - gi.dprintf("entity was removed while using targets\n"); - return; - } - } - } -} - - -/* -============= -TempVector - -This is just a convenience function -for making temporary vectors for function calls -============= -*/ -float *tv (float x, float y, float z) -{ - static int index; - static vec3_t vecs[8]; - float *v; - - // use an array so that multiple tempvectors won't collide - // for a while - v = vecs[index]; - index = (index + 1)&7; - - v[0] = x; - v[1] = y; - v[2] = z; - - return v; -} - - -/* -============= -VectorToString - -This is just a convenience function -for printing vectors -============= -*/ -char *vtos (vec3_t v) -{ - static int index; - static char str[8][32]; - char *s; - - // use an array so that multiple vtos won't collide - s = str[index]; - index = (index + 1)&7; - - sprintf (s, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]); - - return s; -} - - -vec3_t VEC_UP = {0, -1, 0}; -vec3_t MOVEDIR_UP = {0, 0, 1}; -vec3_t VEC_DOWN = {0, -2, 0}; -vec3_t MOVEDIR_DOWN = {0, 0, -1}; - -void G_SetMovedir (vec3_t angles, vec3_t movedir) -{ - if (VectorCompare (angles, VEC_UP)) - { - VectorCopy (MOVEDIR_UP, movedir); - } - else if (VectorCompare (angles, VEC_DOWN)) - { - VectorCopy (MOVEDIR_DOWN, movedir); - } - else - { - AngleVectors (angles, movedir, NULL, NULL); - } - - VectorClear (angles); -} - - -float vectoyaw (vec3_t vec) -{ - float yaw; - - if (vec[PITCH] == 0) { - if (vec[YAW] == 0) - yaw = 0; - else if (vec[YAW] > 0) - yaw = 90; - else - yaw = 270; - } else { - yaw = (int) (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI); - if (yaw < 0) - yaw += 360; - } - - return yaw; -} - -float vectoyaw2 (vec3_t vec) -{ - float yaw; - - if (vec[PITCH] == 0) { - if (vec[YAW] == 0) - yaw = 0; - else if (vec[YAW] > 0) - yaw = 90; - else - yaw = 270; - } else { - yaw = (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI); - if (yaw < 0) - yaw += 360; - } - - return yaw; -} - - -void vectoangles (vec3_t value1, vec3_t angles) -{ - float forward; - float yaw, pitch; - - if (value1[1] == 0 && value1[0] == 0) - { - yaw = 0; - if (value1[2] > 0) - pitch = 90; - else - pitch = 270; - } - else - { - // PMM - fixed to correct for pitch of 0 - if (value1[0]) - yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI); - else if (value1[1] > 0) - yaw = 90; - else - yaw = 270; - if (yaw < 0) - yaw += 360; - - forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]); - pitch = (int) (atan2(value1[2], forward) * 180 / M_PI); - if (pitch < 0) - pitch += 360; - } - - angles[PITCH] = -pitch; - angles[YAW] = yaw; - angles[ROLL] = 0; -} - -void vectoangles2 (vec3_t value1, vec3_t angles) -{ - float forward; - float yaw, pitch; - - if (value1[1] == 0 && value1[0] == 0) - { - yaw = 0; - if (value1[2] > 0) - pitch = 90; - else - pitch = 270; - } - else - { - // PMM - fixed to correct for pitch of 0 - if (value1[0]) - yaw = (atan2(value1[1], value1[0]) * 180 / M_PI); - else if (value1[1] > 0) - yaw = 90; - else - yaw = 270; - - if (yaw < 0) - yaw += 360; - - forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]); - pitch = (atan2(value1[2], forward) * 180 / M_PI); - if (pitch < 0) - pitch += 360; - } - - angles[PITCH] = -pitch; - angles[YAW] = yaw; - angles[ROLL] = 0; -} - -char *G_CopyString (char *in) -{ - char *out; - - out = (char *)TagMalloc (strlen(in)+1, TAG_LEVEL); - strcpy (out, in); - return out; -} - - -void G_InitEdict (edict_t *e) -{ - e->inuse = true; - e->classname = "noclass"; - e->gravity = 1.0; - e->s.number = e - g_edicts; - e->org_movetype = -1; -} - -/* -================= -G_Spawn - -Either finds a free edict, or allocates a new one. -Try to avoid reusing an entity that was recently freed, because it -can cause the client to think the entity morphed into something else -instead of being removed and recreated, which can cause interpolated -angles and bad trails. -================= -*/ -edict_t *G_Spawn (void) -{ - int i; - edict_t *e; - - e = &g_edicts[(int)maxclients->value+1]; - for ( i=maxclients->value+1 ; iinuse && ( e->freetime < 2 || level.time - e->freetime > 0.5 ) ) - { - G_InitEdict (e); - return e; - } - } - - if (i == game.maxentities) - gi.error ("ED_Alloc: no free edicts"); - - globals.num_edicts++; - - if(developer->value && readout->value) - gi.dprintf("num_edicts = %d\n",globals.num_edicts); - - G_InitEdict (e); - return e; -} - -/* -================= -G_FreeEdict - -Marks the edict as free -================= -*/ -void G_FreeEdict (edict_t *ed) -{ - // Lazarus - if part of a movewith chain, remove from - // the chain and repair broken links - gi.unlinkentity (ed); // unlink from world - - // Lazarus: In SP we no longer reserve slots for bodyque's - if (deathmatch->value || coop->value) { - if ((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE)) - { -// gi.dprintf("tried to free special edict\n"); - return; - } - } else { - if ((ed - g_edicts) <= maxclients->value) - { - return; - } - } - - // Lazarus: actor muzzle flash - if (ed->flash) - { - memset (ed->flash, 0, sizeof(*ed)); - ed->flash->classname = "freed"; - ed->flash->freetime = level.time; - ed->flash->inuse = false; - } - - memset (ed, 0, sizeof(*ed)); - ed->classname = "freed"; - ed->freetime = level.time; - ed->inuse = false; -} - -/* -============ -G_TouchTriggers - -============ -*/ -void G_TouchTriggers (edict_t *ent) -{ - int i, num; - edict_t *touch[MAX_EDICTS], *hit; - - // Lazarus: nothing touches anything if game is frozen - if (level.freeze) - return; - - // dead things don't activate triggers! - if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0)) - return; - - num = gi.BoxEdicts (ent->absmin, ent->absmax, touch - , MAX_EDICTS, AREA_TRIGGERS); - - // be careful, it is possible to have an entity in this - // list removed before we get to it (killtriggered) - for (i=0 ; iinuse) - continue; - if (!hit->touch) - continue; - hit->touch (hit, ent, NULL, NULL); - } -} - -/* -============ -G_TouchSolids - -Call after linking a new trigger in during gameplay -to force all entities it covers to immediately touch it -============ -*/ -void G_TouchSolids (edict_t *ent) -{ - int i, num; - edict_t *touch[MAX_EDICTS], *hit; - - num = gi.BoxEdicts (ent->absmin, ent->absmax, touch - , MAX_EDICTS, AREA_SOLID); - - // be careful, it is possible to have an entity in this - // list removed before we get to it (killtriggered) - for (i=0 ; iinuse) - continue; - if (ent->touch) - ent->touch (hit, ent, NULL, NULL); - if (!ent->inuse) - break; - } -} - - - - -/* -============================================================================== - -Kill box - -============================================================================== -*/ - -/* -================= -KillBox - -Kills all entities that would touch the proposed new positioning -of ent. Ent should be unlinked before calling this! -================= -*/ -bool KillBox (edict_t *ent) -{ - trace_t tr; - - while (1) - { - tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID); - if (!tr.ent) - break; - // nail it - T_Damage (tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG); - - // if we didn't kill it, fail - if (tr.ent->solid) - return false; - } - - return true; // all clear -} - -void AnglesNormalize(vec3_t vec) -{ - while(vec[0] > 180) vec[0] -= 360; - while(vec[0] < -180) vec[0] += 360; - while(vec[1] > 360) vec[1] -= 360; - while(vec[1] < 0) vec[1] += 360; -} - -float SnapToEights(float x) -{ - x *= 8.0; - if (x > 0.0) x += 0.5; - else x -= 0.5; - return 0.125 * (int)x; -} - - -/* Lazarus - added functions */ - -void stuffcmd(edict_t *pent, char *pszCommand) -{ - MESSAGE_BEGIN(svc_stufftext); - WRITE_STRING(pszCommand); - MESSAGE_SEND(MSG_ONE_R, NULL, pent); -} - -bool point_infront (edict_t *self, vec3_t point) -{ - vec3_t vec; - float dot; - vec3_t forward; - - AngleVectors (self->s.angles, forward, NULL, NULL); - VectorSubtract (point, self->s.origin, vec); - VectorNormalize (vec); - dot = DotProduct (vec, forward); - - if (dot > 0.3) return true; - return false; -} - -float AtLeast(float x, float dx) -{ - float xx; - - xx = (float)(floor(x/dx - 0.5)+1.)*dx; - if(xx < x) xx += dx; - return xx; -} - -edict_t *LookingAt(edict_t *ent, int filter, vec3_t endpos, float *range) -{ - edict_t *who; - edict_t *trigger[MAX_EDICTS]; - edict_t *ignore; - trace_t tr; - vec_t r; - vec3_t end, forward, start; - vec3_t dir, entp, mins, maxs; - int i, num; - - if(!ent->client) { - if(endpos) VectorClear(endpos); - if(range) *range = 0; - return NULL; - } - VectorClear(end); - if(ent->client->spycam) - { - AngleVectors(ent->client->ps.viewangles, forward, NULL, NULL); - VectorCopy(ent->s.origin,start); - ignore = ent->client->spycam; - } - else - { - AngleVectors(ent->client->v_angle, forward, NULL, NULL); - VectorCopy(ent->s.origin, start); - start[2] += ent->viewheight; - ignore = ent; - } - - VectorMA(start, 8192, forward, end); - - /* First check for looking directly at a pickup item */ - VectorSet(mins,-4096,-4096,-4096); - VectorSet(maxs, 4096, 4096, 4096); - num = gi.BoxEdicts (mins, maxs, trigger, MAX_EDICTS, AREA_TRIGGERS); - for (i=0 ; iinuse) - continue; - if (!who->item) - continue; - if (!visible(ent,who)) - continue; - if (!infront(ent,who)) - continue; - VectorSubtract(who->s.origin,start,dir); - r = VectorLength(dir); - VectorMA(start, r, forward, entp); - if(entp[0] < who->s.origin[0] - 17) continue; - if(entp[1] < who->s.origin[1] - 17) continue; - if(entp[2] < who->s.origin[2] - 17) continue; - if(entp[0] > who->s.origin[0] + 17) continue; - if(entp[1] > who->s.origin[1] + 17) continue; - if(entp[2] > who->s.origin[2] + 17) continue; - if(endpos) - VectorCopy(who->s.origin,endpos); - if(range) - *range = r; - return who; - } - - tr = gi.trace (start, NULL, NULL, end, ignore, MASK_SHOT); - if (tr.fraction == 1.0) - { - // too far away - gi.sound (ent, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0); - return NULL; - } - if(!tr.ent) - { - // no hit - gi.sound (ent, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0); - return NULL; - } - if(!tr.ent->classname) - { - // should never happen - gi.sound (ent, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0); - return NULL; - } - - if((strstr(tr.ent->classname,"func_") != NULL) && (filter & LOOKAT_NOBRUSHMODELS)) - { - // don't hit on brush models - gi.sound (ent, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0); - return NULL; - } - if( (tr.ent == world) && (filter & LOOKAT_NOWORLD)) - { - // world brush - gi.sound (ent, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0); - return NULL; - } - if(endpos) { - endpos[0] = tr.endpos[0]; - endpos[1] = tr.endpos[1]; - endpos[2] = tr.endpos[2]; - } - if(range) { - VectorSubtract(tr.endpos,start,start); - *range = VectorLength(start); - } - return tr.ent; -} - -void GameDirRelativePath(char *filename, char *output) -{ - strcpy(output, filename ); -} - -/* -Lazarus: G_UseTarget is similar to G_UseTargets, but only triggers -a single target rather than all entities matching target -criteria. It *does*, however, kill all killtargets -*/ - -void Think_Delay_Single (edict_t *ent) -{ - G_UseTarget (ent, ent->activator, ent->target_ent); - G_FreeEdict (ent); -} - -void G_UseTarget (edict_t *ent, edict_t *activator, edict_t *target) -{ - edict_t *t; - -// -// check for a delay -// - if (ent->delay) - { - // create a temp object to fire at a later time - t = G_Spawn(); - t->classname = "DelayedUse"; - t->nextthink = level.time + ent->delay; - t->think = Think_Delay_Single; - t->activator = activator; - t->target_ent = target; - if (!activator) - gi.dprintf ("Think_Delay_Single with no activator\n"); - t->message = ent->message; - t->target = ent->target; - t->killtarget = ent->killtarget; - t->noise_index = ent->noise_index; - return; - } - - -// -// print the message -// - if ((ent->message) && !(activator->svflags & SVF_MONSTER)) - { - gi.centerprintf (activator, "%s", ent->message); - if (ent->noise_index > 0) - gi.sound (activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0); - else if (ent->noise_index == 0) - gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0); - } - -// -// kill killtargets -// - if (ent->killtarget) - { - t = NULL; - while ((t = G_Find (t, FOFS(targetname), ent->killtarget))) - { - // Lazarus: remove killtargeted monsters from total_monsters - if(t->svflags & SVF_MONSTER) { - if(!t->dmgteam || strcmp(t->dmgteam,"player")) - if(!(t->monsterinfo.aiflags & AI_GOOD_GUY)) - level.total_monsters--; - } - G_FreeEdict (t); - if (!ent->inuse) - { - gi.dprintf("entity was removed while using killtargets\n"); - return; - } - } - } - -// -// fire target -// - if (target) - { - // doors fire area portals in a specific way - if ( (target->class_id == ENTITY_FUNC_AREAPORTAL) && - ((ent->class_id == ENTITY_FUNC_DOOR) || - (ent->class_id == ENTITY_FUNC_DOOR_ROTATING))) - return; - - if (target == ent) - { - gi.dprintf ("WARNING: Entity used itself.\n"); - } - else - { - if (target->use) - target->use (target, ent, activator); - } - if (!ent->inuse) - { - gi.dprintf("entity was removed while using target\n"); - return; - } - } -} - -/* -============= -visible - -returns 1 if the entity is visible to self, even if not infront () -============= -*/ -bool visible (edict_t *self, edict_t *other) -{ - vec3_t spot1; - vec3_t spot2; - trace_t trace; - - VectorCopy (self->s.origin, spot1); - spot1[2] += self->viewheight; - VectorCopy (other->s.origin, spot2); - spot2[2] += other->viewheight; - trace = gi.trace (spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE); - - // Lazarus: Take fog into account for monsters - - if ( (trace.fraction == 1.0) || (trace.ent == other)) - { - self->monsterinfo.visibility = 1.0; - return true; - } - return false; -} - - -/* -============= -infront - -returns 1 if the entity is in front (in sight) of self -============= -*/ -bool infront (edict_t *self, edict_t *other) -{ - vec3_t vec; - float dot; - vec3_t forward; - - AngleVectors (self->s.angles, forward, NULL, NULL); - VectorSubtract (other->s.origin, self->s.origin, vec); - VectorNormalize (vec); - dot = DotProduct (vec, forward); - - if (dot > 0.3) - return true; - return false; -} - -void T_Damage (edict_t *in_targ, edict_t *inflictor, edict_t *in_attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod) -{ - gclient_t *client; - int take; - int save; - int te_sparks; - edict_t *attacker; - edict_t *targ; - - targ = in_targ; - - if (!targ || !targ->inuse) - return; - - if (!targ->takedamage) - return; - - if (!in_attacker) - attacker = world; - else - attacker = in_attacker; - - - // If targ is a fake player for the real player viewing camera, get that player - // out of the camera and do the damage to him - if (targ->class_id == ENTITY_CAMPLAYER) { - if(targ->target_ent && targ->target_ent->client && targ->target_ent->client->spycam) - { - if(attacker->enemy == targ) - { - attacker->enemy = targ->target_ent; - attacker->goalentity = NULL; - attacker->movetarget = NULL; - } - targ = targ->target_ent; - if(attacker->svflags & SVF_MONSTER) - { - if(attacker->spawnflags & SF_MONSTER_GOODGUY) - { - if(attacker->enemy == targ) - { - attacker->enemy = NULL; - attacker->monsterinfo.aiflags &= ~AI_FOLLOW_LEADER; - attacker->monsterinfo.attack_finished = 0; - attacker->monsterinfo.pausetime = level.time + 100000000; - if(attacker->monsterinfo.stand) - attacker->monsterinfo.stand(attacker); - } - } - } - } - else - return; // don't damage target_monitor camplayer - } - // friendly fire avoidance - // if enabled you can't hurt teammates (but you can hurt yourself) - // knockback still occurs - if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value)) - { - if (OnSameTeam (targ, attacker)) - { - if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) - damage = 0; - else - mod |= MOD_FRIENDLY_FIRE; - } - } - meansOfDeath = mod; - - // easy mode takes half damage - if (skill->value == 0 && deathmatch->value == 0 && targ->client) - { - damage *= 0.5; - if (!damage) - damage = 1; - } - - client = targ->client; - - if (dflags & DAMAGE_BULLET) - te_sparks = TE_BULLET_SPARKS; - else - te_sparks = TE_SPARKS; - - VectorNormalize(dir); - -// bonus damage for suprising a monster - if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0)) - damage *= 2; - - if (targ->flags & FL_NO_KNOCKBACK) - knockback = 0; - -// Lazarus: If monster is currently chasing a "thing" and mod is a laser, -// ignore knockback to give poor guy a chance to vamoose. - if (targ->movetarget && (targ->movetarget->class_id == ENTITY_THING) && (mod == MOD_TARGET_LASER)) - knockback = 0; - -// figure momentum add - if (!(dflags & DAMAGE_NO_KNOCKBACK)) - { - // Lazarus: Added MOVETYPE_TOSS to no knockback (pickup items and dead bodies) - if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP) && (targ->movetype != MOVETYPE_TOSS)) - { - vec3_t kvel; - float mass; - - if (targ->mass < 50) - mass = 50; - else - mass = targ->mass; - - if (targ->client && attacker == targ) - { - VectorScale (dir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack... - } - else - { - VectorScale (dir, 500.0 * (float)knockback / mass, kvel); - } - VectorAdd (targ->velocity, kvel, targ->velocity); - } - } - - take = damage; - save = 0; - - // check for invincibility - if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION)) - { - if (targ->pain_debounce_time < level.time) - { - gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0); - targ->pain_debounce_time = level.time + 2; - } - take = 0; - save = damage; - } - - // do the damage - if (take) - { - if(targ->client) - { - if(in_targ != targ) - { - // Then player has taken the place of whatever was originally - // damaged, as in switching from func_monitor usage. Limit - // damage so that player isn't killed, and make him temporarily - // invincible - targ->health = max(2, targ->health - take); - targ->client->invincible_framenum = level.framenum+2; - targ->pain_debounce_time = max(targ->pain_debounce_time,level.time+0.3); - } - else if(level.framenum - targ->client->startframe > 30) - targ->health = targ->health - take; - else if(targ->health > 10) - targ->health = max(10,targ->health - take); - } - else - { - // Lazarus: For func_explosive target, check spawnflags and, if needed, - // damage type - if(targ->class_id == ENTITY_FUNC_EXPLOSIVE) - { - bool good_damage = true; - - if(targ->spawnflags & 8) // explosion only - { - good_damage = false; - if(mod == MOD_GRENADE) good_damage = true; - if(mod == MOD_G_SPLASH) good_damage = true; - if(mod == MOD_ROCKET) good_damage = true; - if(mod == MOD_R_SPLASH) good_damage = true; - if(mod == MOD_BFG_BLAST) good_damage = true; - if(mod == MOD_HANDGRENADE) good_damage = true; - if(mod == MOD_HG_SPLASH) good_damage = true; - if(mod == MOD_EXPLOSIVE) good_damage = true; - if(mod == MOD_BARREL) good_damage = true; - if(mod == MOD_BOMB) good_damage = true; - } - if(!good_damage) return; - } - - targ->health = targ->health - take; - } - - if (targ->health <= 0) - { - if ((targ->svflags & SVF_MONSTER) || (client)) - targ->flags |= FL_NO_KNOCKBACK; - Killed (targ, inflictor, attacker, take, point); - return; - } - } - - if (client) - { - if (!(targ->flags & FL_GODMODE) && (take)) - targ->pain (targ, attacker, knockback, take); - } - else if (take) - { - if (targ->pain) - targ->pain (targ, attacker, knockback, take); - } - - // add to the damage inflicted on a player this frame - // the total will be turned into screen blends and view angle kicks - // at the end of the frame - if (client) - { - client->damage_parmor += 10; - client->damage_armor += 10; - client->damage_blood += take; - client->damage_knockback += knockback; - VectorCopy (point, client->damage_from); - } -} - - -/* -============ -T_RadiusDamage - -Lazarus adds dmg_slope to alter the radius damage equation. (Standard Q2 = -0.5) -============ -*/ -void T_RadiusDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod, double dmg_slope) -{ - float points; - edict_t *ent = NULL; - vec3_t v; - vec3_t dir; - - while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL) - { - if (ent == ignore) - continue; - if (!ent->takedamage) - continue; - - VectorAdd (ent->mins, ent->maxs, v); - VectorMA (ent->s.origin, 0.5, v, v); - VectorSubtract (inflictor->s.origin, v, v); - points = damage + dmg_slope * VectorLength (v); - if (ent == attacker) - points = points * 0.5; - if (points > 0) - { - if (CanDamage (ent, inflictor)) - { - VectorSubtract (ent->s.origin, inflictor->s.origin, dir); - T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); - } - } - } -} - -/* -============ -Killed -============ -*/ -void misc_deadsoldier_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point); -void Killed (edict_t *targ, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) -{ - if (targ->health < -999) targ->health = -999; - - targ->enemy = attacker; - - if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD)) - { - if (!(targ->monsterinfo.aiflags & AI_GOOD_GUY) ) - { - level.killed_monsters++; - if (coop->value && attacker->client) - attacker->client->resp.score++; - // medics won't heal monsters that they kill themselves - if (attacker->class_id == ENTITY_MONSTER_MEDIC) - targ->owner = attacker; - } - } - - if (targ->movetype == MOVETYPE_PUSH || targ->movetype == MOVETYPE_STOP || targ->movetype == MOVETYPE_NONE) - { // doors, triggers, etc - targ->die (targ, inflictor, attacker, damage, point); - return; - } - - if (inflictor->movetype == MOVETYPE_PUSH) - { - // Lazarus - Die function won't gib NO_GIB monsters... blow 'em up - if((targ->die != misc_deadsoldier_die) && (targ->spawnflags & SF_MONSTER_NOGIB)) - { - BecomeExplosion1(targ); - return; - } - } - targ->die (targ, inflictor, attacker, damage, point); -} - -/* -============ -CanDamage - -Returns true if the inflictor can directly damage the target. Used for -explosions and melee attacks. -============ -*/ -bool CanDamage (edict_t *targ, edict_t *inflictor) -{ - vec3_t dest; - trace_t trace; - -// bmodels need special checking because their origin is 0,0,0 - if (targ->movetype == MOVETYPE_PUSH) - { - VectorAdd (targ->absmin, targ->absmax, dest); - VectorScale (dest, 0.5, dest); - trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); - if (trace.fraction == 1.0) - return true; - if (trace.ent == targ) - return true; - return false; - } - - trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, targ->s.origin, inflictor, MASK_SOLID); - if (trace.fraction == 1.0 || trace.ent == targ) - return true; - - // Lazarus: This is kinda cheesy, but avoids doing goofy things in a map to make this work. If a LOS - // from inflictor to targ is blocked by a func_tracktrain, AND the targ is riding/driving - // the tracktrain, go ahead and hurt him. - - if(trace.ent && (trace.ent->flags & FL_TRACKTRAIN) && ((trace.ent->owner == targ) || (targ->groundentity == trace.ent)) ) - return true; - - VectorCopy (targ->s.origin, dest); - dest[0] += 15.0; - dest[1] += 15.0; - trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); - if (trace.fraction == 1.0 || trace.ent == targ) - return true; - - VectorCopy (targ->s.origin, dest); - dest[0] += 15.0; - dest[1] -= 15.0; - trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); - if (trace.fraction == 1.0 || trace.ent == targ) - return true; - - VectorCopy (targ->s.origin, dest); - dest[0] -= 15.0; - dest[1] += 15.0; - trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); - if (trace.fraction == 1.0 || trace.ent == targ) - return true; - - VectorCopy (targ->s.origin, dest); - dest[0] -= 15.0; - dest[1] -= 15.0; - trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); - if (trace.fraction == 1.0 || trace.ent == targ) - return true; - - - return false; -} \ No newline at end of file diff --git a/server/m_player.h b/server/m_player.h deleted file mode 100644 index e8d2bebb..00000000 --- a/server/m_player.h +++ /dev/null @@ -1,205 +0,0 @@ -// G:\quake2\baseq2\models/player_x/frames - -// This file generated by qdata - Do NOT Modify - -#define FRAME_stand01 0 -#define FRAME_stand02 1 -#define FRAME_stand03 2 -#define FRAME_stand04 3 -#define FRAME_stand05 4 -#define FRAME_stand06 5 -#define FRAME_stand07 6 -#define FRAME_stand08 7 -#define FRAME_stand09 8 -#define FRAME_stand10 9 -#define FRAME_stand11 10 -#define FRAME_stand12 11 -#define FRAME_stand13 12 -#define FRAME_stand14 13 -#define FRAME_stand15 14 -#define FRAME_stand16 15 -#define FRAME_stand17 16 -#define FRAME_stand18 17 -#define FRAME_stand19 18 -#define FRAME_stand20 19 -#define FRAME_stand21 20 -#define FRAME_stand22 21 -#define FRAME_stand23 22 -#define FRAME_stand24 23 -#define FRAME_stand25 24 -#define FRAME_stand26 25 -#define FRAME_stand27 26 -#define FRAME_stand28 27 -#define FRAME_stand29 28 -#define FRAME_stand30 29 -#define FRAME_stand31 30 -#define FRAME_stand32 31 -#define FRAME_stand33 32 -#define FRAME_stand34 33 -#define FRAME_stand35 34 -#define FRAME_stand36 35 -#define FRAME_stand37 36 -#define FRAME_stand38 37 -#define FRAME_stand39 38 -#define FRAME_stand40 39 -#define FRAME_run1 40 -#define FRAME_run2 41 -#define FRAME_run3 42 -#define FRAME_run4 43 -#define FRAME_run5 44 -#define FRAME_run6 45 -#define FRAME_attack1 46 -#define FRAME_attack2 47 -#define FRAME_attack3 48 -#define FRAME_attack4 49 -#define FRAME_attack5 50 -#define FRAME_attack6 51 -#define FRAME_attack7 52 -#define FRAME_attack8 53 -#define FRAME_pain101 54 -#define FRAME_pain102 55 -#define FRAME_pain103 56 -#define FRAME_pain104 57 -#define FRAME_pain201 58 -#define FRAME_pain202 59 -#define FRAME_pain203 60 -#define FRAME_pain204 61 -#define FRAME_pain301 62 -#define FRAME_pain302 63 -#define FRAME_pain303 64 -#define FRAME_pain304 65 -#define FRAME_jump1 66 -#define FRAME_jump2 67 -#define FRAME_jump3 68 -#define FRAME_jump4 69 -#define FRAME_jump5 70 -#define FRAME_jump6 71 -#define FRAME_flip01 72 -#define FRAME_flip02 73 -#define FRAME_flip03 74 -#define FRAME_flip04 75 -#define FRAME_flip05 76 -#define FRAME_flip06 77 -#define FRAME_flip07 78 -#define FRAME_flip08 79 -#define FRAME_flip09 80 -#define FRAME_flip10 81 -#define FRAME_flip11 82 -#define FRAME_flip12 83 -#define FRAME_salute01 84 -#define FRAME_salute02 85 -#define FRAME_salute03 86 -#define FRAME_salute04 87 -#define FRAME_salute05 88 -#define FRAME_salute06 89 -#define FRAME_salute07 90 -#define FRAME_salute08 91 -#define FRAME_salute09 92 -#define FRAME_salute10 93 -#define FRAME_salute11 94 -#define FRAME_taunt01 95 -#define FRAME_taunt02 96 -#define FRAME_taunt03 97 -#define FRAME_taunt04 98 -#define FRAME_taunt05 99 -#define FRAME_taunt06 100 -#define FRAME_taunt07 101 -#define FRAME_taunt08 102 -#define FRAME_taunt09 103 -#define FRAME_taunt10 104 -#define FRAME_taunt11 105 -#define FRAME_taunt12 106 -#define FRAME_taunt13 107 -#define FRAME_taunt14 108 -#define FRAME_taunt15 109 -#define FRAME_taunt16 110 -#define FRAME_taunt17 111 -#define FRAME_wave01 112 -#define FRAME_wave02 113 -#define FRAME_wave03 114 -#define FRAME_wave04 115 -#define FRAME_wave05 116 -#define FRAME_wave06 117 -#define FRAME_wave07 118 -#define FRAME_wave08 119 -#define FRAME_wave09 120 -#define FRAME_wave10 121 -#define FRAME_wave11 122 -#define FRAME_point01 123 -#define FRAME_point02 124 -#define FRAME_point03 125 -#define FRAME_point04 126 -#define FRAME_point05 127 -#define FRAME_point06 128 -#define FRAME_point07 129 -#define FRAME_point08 130 -#define FRAME_point09 131 -#define FRAME_point10 132 -#define FRAME_point11 133 -#define FRAME_point12 134 -#define FRAME_crstnd01 135 -#define FRAME_crstnd02 136 -#define FRAME_crstnd03 137 -#define FRAME_crstnd04 138 -#define FRAME_crstnd05 139 -#define FRAME_crstnd06 140 -#define FRAME_crstnd07 141 -#define FRAME_crstnd08 142 -#define FRAME_crstnd09 143 -#define FRAME_crstnd10 144 -#define FRAME_crstnd11 145 -#define FRAME_crstnd12 146 -#define FRAME_crstnd13 147 -#define FRAME_crstnd14 148 -#define FRAME_crstnd15 149 -#define FRAME_crstnd16 150 -#define FRAME_crstnd17 151 -#define FRAME_crstnd18 152 -#define FRAME_crstnd19 153 -#define FRAME_crwalk1 154 -#define FRAME_crwalk2 155 -#define FRAME_crwalk3 156 -#define FRAME_crwalk4 157 -#define FRAME_crwalk5 158 -#define FRAME_crwalk6 159 -#define FRAME_crattak1 160 -#define FRAME_crattak2 161 -#define FRAME_crattak3 162 -#define FRAME_crattak4 163 -#define FRAME_crattak5 164 -#define FRAME_crattak6 165 -#define FRAME_crattak7 166 -#define FRAME_crattak8 167 -#define FRAME_crattak9 168 -#define FRAME_crpain1 169 -#define FRAME_crpain2 170 -#define FRAME_crpain3 171 -#define FRAME_crpain4 172 -#define FRAME_crdeath1 173 -#define FRAME_crdeath2 174 -#define FRAME_crdeath3 175 -#define FRAME_crdeath4 176 -#define FRAME_crdeath5 177 -#define FRAME_death101 178 -#define FRAME_death102 179 -#define FRAME_death103 180 -#define FRAME_death104 181 -#define FRAME_death105 182 -#define FRAME_death106 183 -#define FRAME_death201 184 -#define FRAME_death202 185 -#define FRAME_death203 186 -#define FRAME_death204 187 -#define FRAME_death205 188 -#define FRAME_death206 189 -#define FRAME_death301 190 -#define FRAME_death302 191 -#define FRAME_death303 192 -#define FRAME_death304 193 -#define FRAME_death305 194 -#define FRAME_death306 195 -#define FRAME_death307 196 -#define FRAME_death308 197 - -#define MODEL_SCALE 1.000000 - diff --git a/server/p_client.c b/server/p_client.c deleted file mode 100644 index bf595c1e..00000000 --- a/server/p_client.c +++ /dev/null @@ -1,2206 +0,0 @@ -#include "baseentity.h" -#include "m_player.h" - -#define MUD1BASE 0.20 -#define MUD1AMP 0.08 -#define MUD3 0.08 - -void ClientUserinfoChanged (edict_t *ent, char *userinfo); - -// -// Gross, ugly, disgustuing hack section -// - -// this function is an ugly as hell hack to fix some map flaws -// -// the coop spawn spots on some maps are SNAFU. There are coop spots -// with the wrong targetname as well as spots with no name at all -// -// we use carnal knowledge of the maps to fix the coop spot targetnames to match -// that of the nearest named single player spot - -static void SP_FixCoopSpots (edict_t *self) -{ - edict_t *spot; - vec3_t d; - - spot = NULL; - - while(1) - { - spot = G_Find(spot, FOFS(classname), "info_player_start"); - if (!spot) - return; - if (!spot->targetname) - continue; - VectorSubtract(self->s.origin, spot->s.origin, d); - if (VectorLength(d) < 384) - { - if ((!self->targetname) || strcasecmp(self->targetname, spot->targetname) != 0) - { -// gi.dprintf("FixCoopSpots changed %s at %s targetname from %s to %s\n", self->classname, vtos(self->s.origin), self->targetname, spot->targetname); - self->targetname = spot->targetname; - } - return; - } - } -} - -// now if that one wasn't ugly enough for you then try this one on for size -// some maps don't have any coop spots at all, so we need to create them -// where they should have been - -static void SP_CreateCoopSpots (edict_t *self) -{ - edict_t *spot; - - if(strcasecmp(level.mapname, "security") == 0) - { - spot = G_Spawn(); - spot->classname = "info_player_coop"; - spot->s.origin[0] = 188 - 64; - spot->s.origin[1] = -164; - spot->s.origin[2] = 80; - spot->targetname = "jail3"; - spot->s.angles[1] = 90; - - spot = G_Spawn(); - spot->classname = "info_player_coop"; - spot->s.origin[0] = 188 + 64; - spot->s.origin[1] = -164; - spot->s.origin[2] = 80; - spot->targetname = "jail3"; - spot->s.angles[1] = 90; - - spot = G_Spawn(); - spot->classname = "info_player_coop"; - spot->s.origin[0] = 188 + 128; - spot->s.origin[1] = -164; - spot->s.origin[2] = 80; - spot->targetname = "jail3"; - spot->s.angles[1] = 90; - - return; - } -} - - -/*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32) -The normal starting point for a level. -*/ -void SP_info_player_start(edict_t *self) -{ - self->class_id = ENTITY_INFO_PLAYER_START; - if (!coop->value) - return; - if(strcasecmp(level.mapname, "security") == 0) - { - // invoke one of our gross, ugly, disgusting hacks - self->think = SP_CreateCoopSpots; - self->nextthink = level.time + FRAMETIME; - } -} - -/*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) -potential spawning position for deathmatch games -*/ -void SP_info_player_deathmatch(edict_t *self) -{ - if (!deathmatch->value) - { - G_FreeEdict (self); - return; - } - self->class_id = ENTITY_INFO_PLAYER_DEATHMATCH; -} - -/*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 32) -potential spawning position for coop games -*/ - -void SP_info_player_coop(edict_t *self) -{ - if (!coop->value) - { - G_FreeEdict (self); - return; - } - self->class_id = ENTITY_INFO_PLAYER_COOP; - if((strcasecmp(level.mapname, "jail2") == 0) || - (strcasecmp(level.mapname, "jail4") == 0) || - (strcasecmp(level.mapname, "mine1") == 0) || - (strcasecmp(level.mapname, "mine2") == 0) || - (strcasecmp(level.mapname, "mine3") == 0) || - (strcasecmp(level.mapname, "mine4") == 0) || - (strcasecmp(level.mapname, "lab") == 0) || - (strcasecmp(level.mapname, "boss1") == 0) || - (strcasecmp(level.mapname, "fact3") == 0) || - (strcasecmp(level.mapname, "biggun") == 0) || - (strcasecmp(level.mapname, "space") == 0) || - (strcasecmp(level.mapname, "command") == 0) || - (strcasecmp(level.mapname, "power2") == 0) || - (strcasecmp(level.mapname, "strike") == 0)) - { - // invoke one of our gross, ugly, disgusting hacks - self->think = SP_FixCoopSpots; - self->nextthink = level.time + FRAMETIME; - } -} - - -/*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32) -The deathmatch intermission point will be at one of these -Use 'angles' instead of 'angle', so you can set pitch or roll as well as yaw. 'pitch yaw roll' -*/ -void SP_info_player_intermission(edict_t *self) -{ - self->class_id = ENTITY_INFO_PLAYER_INTERMISSION; -} - - -//======================================================================= - - -void player_pain (edict_t *self, edict_t *other, float kick, int damage) -{ - // player pain is handled at the end of the frame in P_DamageFeedback -} - - -bool IsFemale (edict_t *ent) -{ - char *info; - - if (!ent->client) - return false; - - info = Info_ValueForKey (ent->client->pers.userinfo, "gender"); - if (info[0] == 'f' || info[0] == 'F') - return true; - return false; -} - -bool IsNeutral (edict_t *ent) -{ - char *info; - - if (!ent->client) - return false; - - info = Info_ValueForKey (ent->client->pers.userinfo, "gender"); - if (info[0] != 'f' && info[0] != 'F' && info[0] != 'm' && info[0] != 'M') - return true; - return false; -} - -void ClientObituary (edict_t *self, edict_t *inflictor, edict_t *attacker) -{ - int mod; - char *message; - char *message2; - bool ff; - - if (coop->value && attacker->client) - meansOfDeath |= MOD_FRIENDLY_FIRE; - - if (deathmatch->value || coop->value) - { - ff = meansOfDeath & MOD_FRIENDLY_FIRE; - mod = meansOfDeath & ~MOD_FRIENDLY_FIRE; - message = NULL; - message2 = ""; - - switch (mod) - { - case MOD_SUICIDE: - message = "suicides"; - break; - case MOD_FALLING: - message = "cratered"; - break; - case MOD_CRUSH: - message = "was squished"; - break; - case MOD_WATER: - message = "sank like a rock"; - break; - case MOD_SLIME: - message = "melted"; - break; - case MOD_LAVA: - message = "does a back flip into the lava"; - break; - case MOD_EXPLOSIVE: - case MOD_BARREL: - message = "blew up"; - break; - case MOD_EXIT: - message = "found a way out"; - break; - case MOD_TARGET_LASER: - message = "saw the light"; - break; - case MOD_TARGET_BLASTER: - message = "got blasted"; - break; - case MOD_BOMB: - case MOD_SPLASH: - case MOD_TRIGGER_HURT: - case MOD_VEHICLE: - message = "was in the wrong place"; - break; - } - if (attacker == self) - { - switch (mod) - { - case MOD_HELD_GRENADE: - message = "tried to put the pin back in"; - break; - case MOD_HG_SPLASH: - case MOD_G_SPLASH: - if (IsNeutral(self)) - message = "tripped on its own grenade"; - else if (IsFemale(self)) - message = "tripped on her own grenade"; - else - message = "tripped on his own grenade"; - break; - case MOD_R_SPLASH: - if (IsNeutral(self)) - message = "blew itself up"; - else if (IsFemale(self)) - message = "blew herself up"; - else - message = "blew himself up"; - break; - case MOD_BFG_BLAST: - message = "should have used a smaller gun"; - break; - default: - if (IsNeutral(self)) - message = "killed itself"; - else if (IsFemale(self)) - message = "killed herself"; - else - message = "killed himself"; - break; - } - } - if (message) - { - gi.bprintf (PRINT_MEDIUM, "%s %s.\n", self->client->pers.netname, message); - if (deathmatch->value) - self->client->resp.score--; - self->enemy = NULL; - return; - } - - self->enemy = attacker; - if (attacker && attacker->client) - { - switch (mod) - { - case MOD_BLASTER: - message = "was blasted by"; - break; - case MOD_SHOTGUN: - message = "was gunned down by"; - break; - case MOD_SSHOTGUN: - message = "was blown away by"; - message2 = "'s super shotgun"; - break; - case MOD_MACHINEGUN: - message = "was machinegunned by"; - break; - case MOD_CHAINGUN: - message = "was cut in half by"; - message2 = "'s chaingun"; - break; - case MOD_GRENADE: - message = "was popped by"; - message2 = "'s grenade"; - break; - case MOD_G_SPLASH: - message = "was shredded by"; - message2 = "'s shrapnel"; - break; - case MOD_ROCKET: - message = "ate"; - message2 = "'s rocket"; - break; - case MOD_R_SPLASH: - message = "almost dodged"; - message2 = "'s rocket"; - break; - case MOD_HYPERBLASTER: - message = "was melted by"; - message2 = "'s hyperblaster"; - break; - case MOD_RAILGUN: - message = "was railed by"; - break; - case MOD_BFG_LASER: - message = "saw the pretty lights from"; - message2 = "'s BFG"; - break; - case MOD_BFG_BLAST: - message = "was disintegrated by"; - message2 = "'s BFG blast"; - break; - case MOD_BFG_EFFECT: - message = "couldn't hide from"; - message2 = "'s BFG"; - break; - case MOD_HANDGRENADE: - message = "caught"; - message2 = "'s handgrenade"; - break; - case MOD_HG_SPLASH: - message = "didn't see"; - message2 = "'s handgrenade"; - break; - case MOD_HELD_GRENADE: - message = "feels"; - message2 = "'s pain"; - break; - case MOD_TELEFRAG: - message = "tried to invade"; - message2 = "'s personal space"; - break; - case MOD_VEHICLE: - message = "was splattered by"; - message2 = "'s vehicle"; - break; - case MOD_KICK: - message = "was booted by"; - message2 = "'s foot"; - break; - } - if (message) - { - gi.bprintf (PRINT_MEDIUM,"%s %s %s%s\n", self->client->pers.netname, message, attacker->client->pers.netname, message2); - if (deathmatch->value) - { - if (ff) - attacker->client->resp.score--; - else - attacker->client->resp.score++; - } - return; - } - } - } - - gi.bprintf (PRINT_MEDIUM,"%s died.\n", self->client->pers.netname); - if (deathmatch->value) - self->client->resp.score--; -} - - -void Touch_Item (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf); - -void TossClientWeapon (edict_t *self) -{ - gitem_t *item; - edict_t *drop; - bool quad; - float spread; - - if (!deathmatch->value) - return; - - item = self->client->pers.weapon; - if (! self->client->pers.inventory[self->client->ammo_index] ) - item = NULL; - if (item && (strcmp (item->pickup_name, "Blaster") == 0)) - item = NULL; - if (item && (strcmp (item->pickup_name, "No Weapon") == 0)) - item = NULL; - - if (!((int)(dmflags->value) & DF_QUAD_DROP)) - quad = false; - else - quad = (self->client->quad_framenum > (level.framenum + 10)); - - if (item && quad) - spread = 22.5; - else - spread = 0.0; - - if (item) - { - self->client->v_angle[YAW] -= spread; - drop = Drop_Item (self, item); - self->client->v_angle[YAW] += spread; - drop->spawnflags = DROPPED_PLAYER_ITEM; - } - - if (quad) - { - self->client->v_angle[YAW] += spread; - drop = Drop_Item (self, FindItemByClassname ("item_quad")); - self->client->v_angle[YAW] -= spread; - drop->spawnflags |= DROPPED_PLAYER_ITEM; - - drop->touch = Touch_Item; - drop->nextthink = level.time + (self->client->quad_framenum - level.framenum) * FRAMETIME; - drop->think = G_FreeEdict; - } -} - - -/* -================== -LookAtKiller -================== -*/ -void LookAtKiller (edict_t *self, edict_t *inflictor, edict_t *attacker) -{ - vec3_t dir; - - if (attacker && attacker != world && attacker != self) - { - VectorSubtract (attacker->s.origin, self->s.origin, dir); - } - else if (inflictor && inflictor != world && inflictor != self) - { - VectorSubtract (inflictor->s.origin, self->s.origin, dir); - } - else - { - self->client->killer_yaw = self->s.angles[YAW]; - return; - } - - if (dir[0]) - self->client->killer_yaw = 180/M_PI*atan2(dir[1], dir[0]); - else { - self->client->killer_yaw = 0; - if (dir[1] > 0) - self->client->killer_yaw = 90; - else if (dir[1] < 0) - self->client->killer_yaw = -90; - } - if (self->client->killer_yaw < 0) - self->client->killer_yaw += 360; - - -} - -/* -================== -player_die -================== -*/ -void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) -{ - int n; - - self->client->pers.spawn_landmark = false; // paranoia check - self->client->pers.spawn_levelchange = false; - self->client->zooming = 0; - self->client->zoomed = false; - - VectorClear (self->avelocity); - - self->takedamage = DAMAGE_YES; - self->movetype = MOVETYPE_TOSS; - - self->s.weaponmodel = 0; // remove linked weapon model - - self->s.angles[0] = 0; - self->s.angles[2] = 0; - - self->s.sound = 0; - self->client->weapon_sound = 0; - - self->maxs[2] = -8; - -// self->solid = SOLID_NOT; - self->svflags |= SVF_DEADMONSTER; - - if (!self->deadflag) - { - self->client->respawn_time = level.time + 1.0; - LookAtKiller (self, inflictor, attacker); - self->client->ps.pmove.pm_type = PM_DEAD; - ClientObituary (self, inflictor, attacker); - TossClientWeapon (self); - if (deathmatch->value) - Cmd_Help_f (self); // show scores - - // clear inventory - // this is kind of ugly, but it's how we want to handle keys in coop - for (n = 0; n < game.num_items; n++) - { - if (coop->value && itemlist[n].flags & IT_KEY) - self->client->resp.coop_respawn.inventory[n] = self->client->pers.inventory[n]; - self->client->pers.inventory[n] = 0; - } - } - - // remove powerups - self->client->quad_framenum = 0; - self->client->invincible_framenum = 0; - self->client->breather_framenum = 0; - self->client->enviro_framenum = 0; - self->flags &= ~FL_POWER_ARMOR; - self->client->flashlight = false; - - // turn off alt-fire mode if on - self->client->pers.fire_mode=0; - self->client->nNewLatch &= ~BUTTON_ATTACK2; - - if (self->health < self->gib_health) - { // gib - gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0); - for (n= 0; n < 4; n++) - ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); - ThrowClientHead (self, damage); - - self->takedamage = DAMAGE_NO; - } - else - { // normal death - if (!self->deadflag) - { - static int i; - - i = (i+1)%3; - // start a death animation - self->client->anim_priority = ANIM_DEATH; - if (self->client->ps.pmove.pm_flags & PMF_DUCKED) - { - self->s.frame = FRAME_crdeath1-1; - self->client->anim_end = FRAME_crdeath5; - } - else switch (i) - { - case 0: - self->s.frame = FRAME_death101-1; - self->client->anim_end = FRAME_death106; - break; - case 1: - self->s.frame = FRAME_death201-1; - self->client->anim_end = FRAME_death206; - break; - case 2: - self->s.frame = FRAME_death301-1; - self->client->anim_end = FRAME_death308; - break; - } - gi.sound (self, CHAN_VOICE, gi.soundindex(va("*death%i.wav", (rand()%4)+1)), 1, ATTN_NORM, 0); - } - } - self->deadflag = DEAD_DEAD; - gi.linkentity (self); - -} - -//======================================================================= - -void SelectStartWeapon (gclient_t *client, int style) -{ - gitem_t *item; - int n; - - // Lazarus: We allow choice of weapons (or no weapon) at startup - // If style is non-zero, first clear player inventory of all - // weapons and ammo that might have been passed over through - // target_changelevel or acquired when previously called by - // InitClientPersistant - if(style) - { - for (n = 0; n < MAX_ITEMS; n++) - { - if (itemlist[n].flags & IT_WEAPON) - client->pers.inventory[n] = 0; - } - client->pers.inventory[shells_index] = 0; - client->pers.inventory[bullets_index] = 0; - client->pers.inventory[grenades_index] = 0; - client->pers.inventory[rockets_index] = 0; - client->pers.inventory[cells_index] = 0; - client->pers.inventory[slugs_index] = 0; - client->pers.inventory[homing_index] = 0; - } - - switch(style) - { - case -1: - item = FindItem("No Weapon"); - break; - case -2: - case 2: - item = FindItem("Shotgun"); - break; - case -3: - case 3: - item = FindItem("Super Shotgun"); - break; - case -4: - case 4: - item = FindItem("Machinegun"); - break; - case -5: - case 5: - item = FindItem("Chaingun"); - break; - case -6: - case 6: - item = FindItem("Grenade Launcher"); - break; - case -7: - case 7: - item = FindItem("Rocket Launcher"); - break; - case -8: - case 8: - item = FindItem("HyperBlaster"); - break; - case -9: - case 9: - item = FindItem("Railgun"); - break; - case -10: - case 10: - item = FindItem("BFG10K"); - break; - default: - item = FindItem("Blaster"); - break; - } - client->pers.selected_item = ITEM_INDEX(item); - client->pers.inventory[client->pers.selected_item] = 1; - client->pers.weapon = item; - - // Lazarus: If default weapon is NOT "No Weapon", then give player - // a blaster - if(style > 1) - client->pers.inventory[ITEM_INDEX(FindItem("Blaster"))] = 1; - - // and give him standard ammo - if (item->ammo) - { - gitem_t *ammo; - - ammo = FindItem (item->ammo); - if ( deathmatch->value && ((int)dmflags->value & DF_INFINITE_AMMO) ) - client->pers.inventory[ITEM_INDEX(ammo)] += 1000; - else - client->pers.inventory[ITEM_INDEX(ammo)] += ammo->quantity; - } -} - -/* -============== -InitClientPersistant - -This is only called when the game first initializes in single player, -but is called after each death and level change in deathmatch -============== -*/ -void InitClientPersistant (gclient_t *client, int style) -{ - memset (&client->pers, 0, sizeof(client->pers)); - - client->homing_rocket = NULL; - SelectStartWeapon (client, style); - - client->pers.health = 100; - client->pers.max_health = 100; - - client->pers.max_bullets = 200; - client->pers.max_shells = 100; - client->pers.max_rockets = 50; - client->pers.max_grenades = 50; - client->pers.max_cells = 200; - client->pers.max_slugs = 50; - client->pers.max_fuel = 1000; - client->pers.max_homing_missiles = 50; - client->pers.fire_mode = 0; // Lazarus alternate fire mode - - client->pers.connected = true; - - // Lazarus - client->zooming = 0; - client->zoomed = false; - client->spycam = NULL; - client->pers.spawn_landmark = false; - client->pers.spawn_levelchange = false; -} - - -void InitClientResp (gclient_t *client) -{ - memset (&client->resp, 0, sizeof(client->resp)); - client->resp.enterframe = level.framenum; - client->resp.coop_respawn = client->pers; -} - -/* -================== -SaveClientData - -Some information that should be persistant, like health, -is still stored in the edict structure, so it needs to -be mirrored out to the client structure before all the -edicts are wiped. -================== -*/ -void SaveClientData (void) -{ - int i; - edict_t *ent; - - for (i=0 ; iinuse) - continue; - game.clients[i].pers.newweapon = ent->client->newweapon; - game.clients[i].pers.health = ent->health; - game.clients[i].pers.max_health = ent->max_health; - game.clients[i].pers.savedFlags = (ent->flags & (FL_GODMODE|FL_NOTARGET|FL_POWER_ARMOR)); - if (coop->value) - game.clients[i].pers.score = ent->client->resp.score; - } -} - -void FetchClientEntData (edict_t *ent) -{ - ent->health = ent->client->pers.health; - ent->gib_health = -40; - ent->max_health = ent->client->pers.max_health; - ent->flags |= ent->client->pers.savedFlags; - if (coop->value) - ent->client->resp.score = ent->client->pers.score; -} - - - -/* -======================================================================= - - SelectSpawnPoint - -======================================================================= -*/ - -/* -================ -PlayersRangeFromSpot - -Returns the distance to the nearest player from the given spot -================ -*/ -float PlayersRangeFromSpot (edict_t *spot) -{ - edict_t *player; - float bestplayerdistance; - vec3_t v; - int n; - float playerdistance; - - - bestplayerdistance = 9999999; - - for (n = 1; n <= maxclients->value; n++) - { - player = &g_edicts[n]; - - if (!player->inuse) - continue; - - if (player->health <= 0) - continue; - - VectorSubtract (spot->s.origin, player->s.origin, v); - playerdistance = VectorLength (v); - - if (playerdistance < bestplayerdistance) - bestplayerdistance = playerdistance; - } - - return bestplayerdistance; -} - -/* -================ -SelectRandomDeathmatchSpawnPoint - -go to a random point, but NOT the two points closest -to other players -================ -*/ -edict_t *SelectRandomDeathmatchSpawnPoint (void) -{ - edict_t *spot, *spot1, *spot2; - int count = 0; - int selection; - float range, range1, range2; - - spot = NULL; - range1 = range2 = 99999; - spot1 = spot2 = NULL; - - while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) - { - count++; - range = PlayersRangeFromSpot(spot); - if (range < range1) - { - range1 = range; - spot1 = spot; - } - else if (range < range2) - { - range2 = range; - spot2 = spot; - } - } - - if (!count) - return NULL; - - if (count <= 2) - { - spot1 = spot2 = NULL; - } - // Lazarus: This is wrong. If there is no spot1 or spot2, all spots should - // be valid. -// else -// count -= 2; - else - { - if(spot1) count--; - if(spot2) count--; - } - - selection = rand() % count; - - spot = NULL; - do - { - spot = G_Find (spot, FOFS(classname), "info_player_deathmatch"); - if (spot == spot1 || spot == spot2) - selection++; - } while(selection--); - - return spot; -} - -/* -================ -SelectFarthestDeathmatchSpawnPoint - -================ -*/ -edict_t *SelectFarthestDeathmatchSpawnPoint (void) -{ - edict_t *bestspot; - float bestdistance, bestplayerdistance; - edict_t *spot; - - - spot = NULL; - bestspot = NULL; - bestdistance = 0; - while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) - { - bestplayerdistance = PlayersRangeFromSpot (spot); - - if (bestplayerdistance > bestdistance) - { - bestspot = spot; - bestdistance = bestplayerdistance; - } - } - - if (bestspot) - { - return bestspot; - } - - // if there is a player just spawned on each and every start spot - // we have no choice to turn one into a telefrag meltdown - spot = G_Find (NULL, FOFS(classname), "info_player_deathmatch"); - - return spot; -} - -edict_t *SelectDeathmatchSpawnPoint (void) -{ - if ( (int)(dmflags->value) & DF_SPAWN_FARTHEST) - return SelectFarthestDeathmatchSpawnPoint (); - else - return SelectRandomDeathmatchSpawnPoint (); -} - - -edict_t *SelectCoopSpawnPoint (edict_t *ent) -{ - int index; - edict_t *spot = NULL; - char *target; - - index = ent->client - game.clients; - - // player 0 starts in normal player spawn point - if (!index) - return NULL; - - spot = NULL; - - // assume there are four coop spots at each spawnpoint - while (1) - { - spot = G_Find (spot, FOFS(classname), "info_player_coop"); - if (!spot) - return NULL; // we didn't have enough... - - target = spot->targetname; - if (!target) - target = ""; - if ( strcasecmp(game.spawnpoint, target) == 0 ) - { // this is a coop spawn point for one of the clients here - index--; - if (!index) - return spot; // this is it - } - } - return spot; -} - - -/* -=========== -SelectSpawnPoint - -Chooses a player start, deathmatch start, coop start, etc -============ -*/ -void SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles, int *style, int *health) -{ - edict_t *spot = NULL; - - if (deathmatch->value) - spot = SelectDeathmatchSpawnPoint (); - else if (coop->value) - spot = SelectCoopSpawnPoint (ent); - - // find a single player start spot - if (!spot) - { - while ((spot = G_Find (spot, FOFS(classname), "info_player_start")) != NULL) - { - if (!game.spawnpoint[0] && !spot->targetname) - break; - - if (!game.spawnpoint[0] || !spot->targetname) - continue; - - if (strcasecmp(game.spawnpoint, spot->targetname) == 0) - break; - } - - if (!spot) - { - if (!game.spawnpoint[0]) - { // there wasn't a spawnpoint without a target, so use any - spot = G_Find (spot, FOFS(classname), "info_player_start"); - } - if (!spot) - gi.error ("Couldn't find spawn point %s\n", game.spawnpoint); - } - } - - *style = spot->style; - *health = spot->health; - VectorCopy (spot->s.origin, origin); - origin[2] += 9; - VectorCopy (spot->s.angles, angles); - - if(!deathmatch->value && !coop->value) { - - spot->count--; - if(!spot->count) { - spot->think = G_FreeEdict; - spot->nextthink = level.time + 1; - } - } -} - -//====================================================================== - - -void InitBodyQue (void) -{ - // DWH: bodyque isn't used in SP, so why reserve space for it? - if (deathmatch->value || coop->value) { - - int i; - edict_t *ent; - - level.body_que = 0; - for (i=0; iclassname = "bodyque"; - } - } -} - -void body_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) -{ - int n; - - if (self->health < self->gib_health) - { - gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0); - for (n= 0; n < 4; n++) - ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); - self->s.origin[2] -= 48; - ThrowClientHead (self, damage); - self->takedamage = DAMAGE_NO; - } -} - -void CopyToBodyQue (edict_t *ent) -{ - edict_t *body; - - // grab a body que and cycle to the next one - body = &g_edicts[(int)maxclients->value + level.body_que + 1]; - level.body_que = (level.body_que + 1) % BODY_QUEUE_SIZE; - - // FIXME: send an effect on the removed body - - gi.unlinkentity (ent); - - gi.unlinkentity (body); - body->s = ent->s; - body->s.number = body - g_edicts; - - body->svflags = ent->svflags; - VectorCopy (ent->mins, body->mins); - VectorCopy (ent->maxs, body->maxs); - VectorCopy (ent->absmin, body->absmin); - VectorCopy (ent->absmax, body->absmax); - VectorCopy (ent->size, body->size); - body->solid = ent->solid; - body->clipmask = ent->clipmask; - body->owner = ent->owner; - body->movetype = ent->movetype; - - body->die = body_die; - body->takedamage = DAMAGE_YES; - - gi.linkentity (body); -} - - -void respawn (edict_t *self) -{ - if (deathmatch->value || coop->value) - { - // spectator's don't leave bodies - if (self->movetype != MOVETYPE_NOCLIP) - CopyToBodyQue (self); - self->svflags &= ~SVF_NOCLIENT; - PutClientInServer (self); - - // add a teleportation effect - self->s.event = EV_PLAYER_TELEPORT; - - // hold in place briefly - self->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT; - self->client->ps.pmove.pm_time = 14; - - self->client->respawn_time = level.time; - - return; - } - // restart the entire server - gi.AddCommandString ("menu_loadgame\n"); -} - -/* - * only called when pers.spectator changes - * note that resp.spectator should be the opposite of pers.spectator here - */ -void spectator_respawn (edict_t *ent) -{ - int i, numspec; - - // if the user wants to become a spectator, make sure he doesn't - // exceed max_spectators - - if (ent->client->pers.spectator) { - char *value = Info_ValueForKey (ent->client->pers.userinfo, "spectator"); - if (*spectator_password->string && strcmp(spectator_password->string, "none") && strcmp(spectator_password->string, value)) - { - gi.cprintf(ent, PRINT_HIGH, "Spectator password incorrect.\n"); - ent->client->pers.spectator = false; - MESSAGE_BEGIN (svc_stufftext); - WRITE_STRING ("spectator 0\n"); - MESSAGE_SEND(MSG_ONE_R, NULL, ent); - return; - } - - // count spectators - for (i = 1, numspec = 0; i <= maxclients->value; i++) - if (g_edicts[i].inuse && g_edicts[i].client->pers.spectator) - numspec++; - - if (numspec >= maxspectators->value) - { - gi.cprintf(ent, PRINT_HIGH, "Server spectator limit is full."); - ent->client->pers.spectator = false; - // reset his spectator var - MESSAGE_BEGIN (svc_stufftext); - WRITE_STRING ("spectator 0\n"); - MESSAGE_SEND(MSG_ONE_R, NULL, ent); - return; - } - } - else - { - // he was a spectator and wants to join the game - // he must have the right password - char *value = Info_ValueForKey (ent->client->pers.userinfo, "password"); - if (*password->string && strcmp(password->string, "none") && - strcmp(password->string, value)) { - gi.cprintf(ent, PRINT_HIGH, "Password incorrect.\n"); - ent->client->pers.spectator = true; - MESSAGE_BEGIN (svc_stufftext); - WRITE_STRING ("spectator 1\n"); - MESSAGE_SEND(MSG_ONE_R, NULL, ent ); - return; - } - } - - // clear client on respawn - ent->client->resp.score = ent->client->pers.score = 0; - - ent->svflags &= ~SVF_NOCLIENT; - PutClientInServer (ent); - - // add a teleportation effect - if (!ent->client->pers.spectator) { - // send effect - MESSAGE_BEGIN (svc_muzzleflash); - WRITE_SHORT (ent-g_edicts); - WRITE_BYTE (MZ_LOGIN); - MESSAGE_SEND (MSG_PVS, ent->s.origin, NULL); - - // hold in place briefly - ent->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT; - ent->client->ps.pmove.pm_time = 14; - } - - ent->client->respawn_time = level.time; - - if (ent->client->pers.spectator) - gi.bprintf (PRINT_HIGH, "%s has moved to the sidelines\n", ent->client->pers.netname); - else - gi.bprintf (PRINT_HIGH, "%s joined the game\n", ent->client->pers.netname); -} - -//============================================================== - - -/* -=========== -PutClientInServer - -Called when a player connects to a server or respawns in -a deathmatch. -============ -*/ -void PutClientInServer (edict_t *ent) -{ - gitem_t *newweapon; - extern int nostatus; - vec3_t mins = {-16, -16, -24}; - vec3_t maxs = {16, 16, 32}; - int index; - vec3_t spawn_origin, spawn_angles, spawn_viewangles; - gclient_t *client; - int i; - bool spawn_landmark; - bool spawn_levelchange; - int spawn_gunframe; - int spawn_modelframe; - int spawn_anim_end; - int spawn_pm_flags; - int spawn_style; - int spawn_health; - client_persistant_t saved; - client_respawn_t resp; - - // find a spawn point - // do it before setting health back up, so farthest - // ranging doesn't count this client - SelectSpawnPoint (ent, spawn_origin, spawn_angles, &spawn_style, &spawn_health); - - index = ent-g_edicts-1; - client = ent->client; - newweapon = client->pers.newweapon; - spawn_landmark = client->pers.spawn_landmark; - spawn_levelchange= client->pers.spawn_levelchange; - spawn_gunframe = client->pers.spawn_gunframe; - spawn_modelframe = client->pers.spawn_modelframe; - spawn_anim_end = client->pers.spawn_anim_end; - client->pers.spawn_landmark = false; - client->pers.spawn_levelchange = false; - - if(spawn_landmark) - { - spawn_origin[2] -= 9; - VectorAdd(spawn_origin,client->pers.spawn_offset,spawn_origin); - VectorCopy(client->pers.spawn_angles,spawn_angles); - VectorCopy(client->pers.spawn_viewangles,spawn_viewangles); - VectorCopy(client->pers.spawn_velocity,ent->velocity); - spawn_pm_flags = client->pers.spawn_pm_flags; - } - - // deathmatch wipes most client data every spawn - if (deathmatch->value) - { - char userinfo[MAX_INFO_STRING]; - - resp = client->resp; - memcpy (userinfo, client->pers.userinfo, sizeof(userinfo)); - InitClientPersistant (client,spawn_style); - ClientUserinfoChanged (ent, userinfo); - } - else if (coop->value) - { -// int n; - char userinfo[MAX_INFO_STRING]; - - resp = client->resp; - memcpy (userinfo, client->pers.userinfo, sizeof(userinfo)); - resp.coop_respawn.game_helpchanged = client->pers.game_helpchanged; - resp.coop_respawn.helpchanged = client->pers.helpchanged; - client->pers = resp.coop_respawn; - ClientUserinfoChanged (ent, userinfo); - if (resp.score > client->pers.score) - client->pers.score = resp.score; - } - else - { - memset (&resp, 0, sizeof(resp)); - } - - // clear everything but the persistant data - saved = client->pers; - memset (client, 0, sizeof(*client)); - client->pers = saved; - if (client->pers.health <= 0) - InitClientPersistant(client,spawn_style); - else if(spawn_style) - SelectStartWeapon(client,spawn_style); - - client->resp = resp; - client->pers.newweapon = newweapon; - - // copy some data from the client to the entity - FetchClientEntData (ent); - - // Lazarus: Starting health < max. Presumably player was hurt in a crash - if( (spawn_health > 0) && !deathmatch->value && !coop->value) - ent->health = min(ent->health, spawn_health); - - // clear entity values - ent->groundentity = NULL; - ent->client = &game.clients[index]; - ent->takedamage = DAMAGE_AIM; - ent->movetype = MOVETYPE_WALK; - ent->viewheight = 22; - ent->inuse = true; - ent->classname = "player"; - ent->mass = 200; - ent->solid = SOLID_BBOX; - ent->deadflag = DEAD_NO; - ent->air_finished = level.time + 12; - ent->clipmask = MASK_PLAYERSOLID; - ent->model = "players/male/tris.md2"; - ent->pain = player_pain; - ent->die = player_die; - ent->waterlevel = 0; - ent->watertype = 0; - ent->flags &= ~FL_NO_KNOCKBACK; - ent->svflags &= ~SVF_DEADMONSTER; - ent->client->spycam = NULL; - ent->client->camplayer = NULL; - - VectorCopy (mins, ent->mins); - VectorCopy (maxs, ent->maxs); - - if(!spawn_landmark) - VectorClear (ent->velocity); - - // clear playerstate values - memset (&ent->client->ps, 0, sizeof(client->ps)); - - if(spawn_landmark) - client->ps.pmove.pm_flags = spawn_pm_flags; - - client->ps.pmove.origin[0] = spawn_origin[0]*8; - client->ps.pmove.origin[1] = spawn_origin[1]*8; - client->ps.pmove.origin[2] = spawn_origin[2]*8; - - if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV)) - { - client->ps.fov = 90; - } - else - { - client->ps.fov = atoi(Info_ValueForKey(client->pers.userinfo, "fov")); - if (client->ps.fov < 1) - client->ps.fov = 90; - else if (client->ps.fov > 160) - client->ps.fov = 160; - } - // DWH - client->original_fov = client->ps.fov; - // end DWH - - client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model); - - // clear entity state values - ent->s.effects = 0; - ent->s.modelindex = MAX_MODELS-1; // will use the skin specified model - - if(ITEM_INDEX(client->pers.weapon) == noweapon_index) - ent->s.weaponmodel = 0; - else ent->s.weaponmodel = MAX_MODELS - 1; // custom gun model - - // sknum is player num and weapon number - // weapon number will be added in changeweapon - ent->s.skin = ent - g_edicts - 1; - - ent->s.frame = 0; - VectorCopy (spawn_origin, ent->s.origin); - ent->s.origin[2] += 1; // make sure off ground - VectorCopy (ent->s.origin, ent->s.old_origin); - - // set the delta angle - for (i=0 ; i<3 ; i++) - { - client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]); - } - - ent->s.angles[PITCH] = ent->s.angles[ROLL] = 0; - ent->s.angles[YAW] = spawn_angles[YAW]; - if(spawn_landmark) - { - VectorCopy(spawn_viewangles, client->ps.viewangles); -// client->ps.pmove.pm_flags |= PMF_NO_PREDICTION; - } - else - VectorCopy(ent->s.angles, client->ps.viewangles); - VectorCopy (client->ps.viewangles, client->v_angle); - - client->resp.spectator = false; - - // DWH: - client->flashlight = false; - client->secs_per_frame = 0.025; // assumed 40 fps until we know better - client->fps_time_start = level.time; - - if (!KillBox (ent)) - { // could't spawn in? - } - - gi.linkentity (ent); - - if(spawn_levelchange && !client->pers.newweapon) - { - // we already had a weapon when the level changed... no need to bring it up - int i; - - client->pers.lastweapon = client->pers.weapon; - client->newweapon = NULL; - client->machinegun_shots = 0; - i = ((client->pers.weapon->weapmodel & 0xff) << 8); - ent->s.skin = (ent - g_edicts - 1) | i; - if (client->pers.weapon->ammo) - client->ammo_index = ITEM_INDEX(FindItem(client->pers.weapon->ammo)); - else - client->ammo_index = 0; - client->weaponstate = WEAPON_READY; - client->ps.gunframe = 0; - client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model); - client->ps.gunframe = spawn_gunframe; - ent->s.frame = spawn_modelframe; - client->anim_end = spawn_anim_end; - } - else - { - // force the current weapon up - client->newweapon = client->pers.weapon; - ChangeWeapon (ent); - } -} - -/* -===================== -ClientBeginDeathmatch - -A client has just connected to the server in -deathmatch mode, so clear everything out before starting them. -===================== -*/ -void ClientBeginDeathmatch (edict_t *ent) -{ - G_InitEdict (ent); - - InitClientResp (ent->client); - - // locate ent at a spawn point - PutClientInServer (ent); - - if (level.intermissiontime) - { - MoveClientToIntermission (ent); - } - else - { - // send effect - MESSAGE_BEGIN (svc_muzzleflash); - WRITE_SHORT (ent-g_edicts); - WRITE_BYTE (MZ_LOGIN); - MESSAGE_SEND (MSG_PVS, ent->s.origin, NULL); - } - - gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname); - - // make sure all view stuff is valid - ClientEndServerFrame (ent); -} - - -/* -=========== -ClientBegin - -called when a client has finished connecting, and is ready -to be placed into the game. This will happen every level load. -============ -*/ -void ClientBegin (edict_t *ent) -{ - int i; - - ent->client = game.clients + (ent - g_edicts - 1); - - // Lazarus: Set the alias for our alternate attack - stuffcmd(ent, "alias +attack2 attack2_on; alias -attack2 attack2_off\n"); - - if (deathmatch->value) - { - ClientBeginDeathmatch (ent); - return; - } - - stuffcmd(ent,"alias +zoomin zoomin;alias -zoomin zoominstop\n"); - stuffcmd(ent,"alias +zoomout zoomout;alias -zoomout zoomoutstop\n"); - stuffcmd(ent,"alias +zoom zoomon;alias -zoom zoomoff\n"); - - // if there is already a body waiting for us (a loadgame), just - // take it, otherwise spawn one from scratch - if (ent->inuse == true) - { - // the client has cleared the client side viewangles upon - // connecting to the server, which is different than the - // state when the game is saved, so we need to compensate - // with deltaangles - for (i=0 ; i<3 ; i++) - ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(ent->client->ps.viewangles[i]); - } - else - { - // a spawn point will completely reinitialize the entity - // except for the persistant data that was initialized at - // ClientConnect() time - G_InitEdict (ent); - ent->classname = "player"; - InitClientResp (ent->client); - PutClientInServer (ent); - } - - if (level.intermissiontime) - { - MoveClientToIntermission (ent); - } - else - { - // send effect if in a multiplayer game - if (game.maxclients > 1) - { - MESSAGE_BEGIN (svc_muzzleflash); - WRITE_SHORT (ent-g_edicts); - WRITE_BYTE (MZ_LOGIN); - MESSAGE_SEND (MSG_PVS, ent->s.origin, NULL); - - gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname); - } - } - - // make sure all view stuff is valid - ClientEndServerFrame (ent); -} - -/* -=========== -ClientUserInfoChanged - -called whenever the player updates a userinfo variable. - -The game can override any of the settings in place -(forcing skins or names, etc) before copying it off. -============ -*/ -void ClientUserinfoChanged (edict_t *ent, char *userinfo) -{ - char *s; - int playernum; - - // check for malformed or illegal info strings - if (!Info_Validate(userinfo)) - { - strcpy (userinfo, "\\name\\badinfo\\skin\\male/grunt"); - } - - // set name - s = Info_ValueForKey (userinfo, "name"); - strncpy (ent->client->pers.netname, s, sizeof(ent->client->pers.netname)-1); - - // set spectator - s = Info_ValueForKey (userinfo, "spectator"); - // spectators are only supported in deathmatch - if (deathmatch->value && *s && strcmp(s, "0")) - ent->client->pers.spectator = true; - else - ent->client->pers.spectator = false; - - // set skin - s = Info_ValueForKey (userinfo, "skin"); - - playernum = ent - g_edicts - 1; - - // combine name and skin into a configstring - gi.configstring (CS_PLAYERSKINS + playernum, va("%s\\%s", ent->client->pers.netname, s) ); - - // fov - if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV)) - { - ent->client->ps.fov = 90; - ent->client->original_fov = ent->client->ps.fov; - } - else - { - float new_fov; - - new_fov = atoi(Info_ValueForKey(userinfo, "fov")); - if (new_fov < 1) - new_fov = 90; - else if (new_fov > 160) - new_fov = 160; - if(new_fov != ent->client->original_fov) { - ent->client->ps.fov = new_fov; - ent->client->original_fov = new_fov; - } - } - - // handedness - s = Info_ValueForKey (userinfo, "hand"); - if (strlen(s)) - ent->client->pers.hand = atoi(s); - - // save off the userinfo in case we want to check something later - strncpy (ent->client->pers.userinfo, userinfo, sizeof(ent->client->pers.userinfo)-1); -} - -/* -=========== -ClientDisconnect - -Called when a player drops from the server. -Will not be called between levels. -============ -*/ -void ClientDisconnect (edict_t *ent) -{ - int playernum; - - if (!ent->client) - return; - - // DWH - ent->client->zooming = 0; - ent->client->zoomed = false; - // end DWH - - gi.bprintf (PRINT_HIGH, "%s disconnected\n", ent->client->pers.netname); - - // send effect - MESSAGE_BEGIN (svc_muzzleflash); - WRITE_SHORT (ent-g_edicts); - WRITE_BYTE (MZ_LOGOUT); - MESSAGE_SEND (MSG_PVS, ent->s.origin, NULL); - - gi.unlinkentity (ent); - ent->s.modelindex = 0; - ent->solid = SOLID_NOT; - ent->inuse = false; - ent->classname = "disconnected"; - ent->client->pers.connected = false; - - playernum = ent-g_edicts-1; - gi.configstring (CS_PLAYERSKINS+playernum, ""); - -} - - -//============================================================== - - -edict_t *pm_passent; - -// pmove doesn't need to know about passent and contentmask -trace_t PM_trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end) -{ - if (pm_passent->health > 0) - return gi.trace (start, mins, maxs, end, pm_passent, MASK_PLAYERSOLID); - else - return gi.trace (start, mins, maxs, end, pm_passent, MASK_DEADSOLID); -} - -unsigned CheckBlock (void *b, int c) -{ - int v,i; - v = 0; - for (i=0 ; is, sizeof(pm->s)); - c2 = CheckBlock (&pm->cmd, sizeof(pm->cmd)); - gi.dprintf("sv %3i:%i %i\n", pm->cmd.impulse, c1, c2); -} - -// DWH -//========================================================================== -// DWH: PM_CmdScale was ripped from Q3 source -//========================================================================== -float PM_CmdScale( usercmd_t *cmd ) { - int max; - float total; - float scale; - - max = abs( cmd->forwardmove ); - if ( abs( cmd->sidemove ) > max ) { - max = abs( cmd->sidemove ); - } - if ( abs( cmd->upmove ) > max ) { - max = abs( cmd->upmove ); - } - if ( !max ) { - return 0; - } - - total = sqrt( cmd->forwardmove * cmd->forwardmove - + cmd->sidemove * cmd->sidemove + cmd->upmove * cmd->upmove ); - scale = max / total; - - return scale; -} -void RemovePush(edict_t *ent) -{ - ent->client->push->s.sound = 0; - ent->client->push->activator = NULL; - ent->client->push = NULL; - ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; -} - -void ClientPushPushable(edict_t *ent) -{ - edict_t *box = ent->client->push; - vec_t dist; - vec3_t new_origin, v, vbox; - - VectorAdd (box->absmax,box->absmin,vbox); - VectorScale(vbox,0.5,vbox); - if (point_infront(ent,vbox)) - { - VectorSubtract(ent->s.origin,box->offset,new_origin); - VectorSubtract(new_origin,box->s.origin,v); - v[2] = 0; - dist = VectorLength(v); - if(dist > 8) - { - // func_pushable got hung up somehow. Break off contact - RemovePush(ent); - } - else if(dist > 0) - { - if(!box->speaker) box->s.sound = box->noise_index; - //box_walkmove( box, vectoyaw(v), dist ); - } - else - box->s.sound = 0; - } - else - RemovePush(ent); -} -/* -============== -ClientThink - -This will be called once for each client frame, which will -usually be a couple times for each server frame. -============== -*/ -void ClientThink (edict_t *ent, usercmd_t *ucmd) -{ - gclient_t *client; - edict_t *other; - edict_t *ground; - pmove_t pm; - vec_t t; - vec3_t view; - vec3_t oldorigin, oldvelocity; - int i, j; - float ground_speed; -// short save_forwardmove; - - level.current_entity = ent; - client = ent->client; - // Lazarus: Copy latest usercmd stuff for use in other routines - client->ucmd = *ucmd; - - VectorCopy(ent->s.origin,oldorigin); - VectorCopy(ent->velocity,oldvelocity); - ground = ent->groundentity; - - if(ground && (ground->movetype == MOVETYPE_PUSH) && (ground != world) && ground->turn_rider) - ground_speed = VectorLength(ground->velocity); - else - ground_speed = 0; - - if( (ent->in_mud) || - (ent->client->push) || - (ent->vehicle) || - (ent->client->spycam) || - (ground_speed > 0) ) - ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION; - else - ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; - - if(client->startframe == 0) - client->startframe = level.framenum; - - client->fps_frames++; - if(client->fps_frames >= 100) { - client->secs_per_frame = (level.time-client->fps_time_start)/100; - client->fps_frames = 0; - client->fps_time_start = level.time; - client->frame_zoomrate = zoomrate->value * client->secs_per_frame; - } - VectorCopy(ent->s.origin,view); - view[2] += ent->viewheight; - - if( client->push ) - { - // currently pushing or pulling a func_pushable - if( (!ent->groundentity) && (ent->waterlevel==0 || client->push->waterlevel == 0 ) ) - { - // oops, we fall down - RemovePush(ent); - } - else - { - // Scale client velocity by mass of func_pushable - t = VectorLength(ent->velocity); - if( t > client->maxvelocity ) - VectorScale(ent->velocity, client->maxvelocity/t, ent->velocity); - client->ps.pmove.pm_flags |= PMF_NO_PREDICTION; - t = 200./client->push->mass; - ucmd->forwardmove *= t; - ucmd->sidemove *= t; - } - } - - // INTERMISSION - if (level.intermissiontime) - { - client->ps.pmove.pm_type = PM_FREEZE; - // can exit intermission after five seconds - if (level.time > level.intermissiontime + 5.0 - && (ucmd->buttons & BUTTON_ANY) ) - level.exitintermission = true; - - return; - } - - if (ent->target_ent && (ent->target_ent->class_id == ENTITY_TARGET_MONITOR)) - { - edict_t *monitor = ent->target_ent; - if(monitor->target_ent && monitor->target_ent->inuse) - { - if(monitor->spawnflags & 2) - { - VectorCopy(monitor->target_ent->s.angles,client->ps.viewangles); - } - else - { - vec3_t dir; - VectorSubtract(monitor->target_ent->s.origin,monitor->s.origin,dir); - vectoangles(dir,client->ps.viewangles); - } - } - else - VectorCopy (monitor->s.angles, client->ps.viewangles); - VectorCopy(monitor->s.origin,ent->s.origin); - client->ps.pmove.pm_type = PM_FREEZE; - return; - } - -// ZOOM - if (client->zooming) { - client->pers.hand = 2; - if(client->zooming > 0) { - if(client->ps.fov > 5) { - client->ps.fov -= client->frame_zoomrate; - if(client->ps.fov < 5) - client->ps.fov = 5; - } else { - client->ps.fov = 5; - } - client->zoomed = true; - } else { - if(client->ps.fov < client->original_fov) { - client->ps.fov += client->frame_zoomrate; - if(client->ps.fov > client->original_fov) { - client->ps.fov = client->original_fov; - client->zoomed = false; - } else - client->zoomed = true; - } else { - client->ps.fov = client->original_fov; - client->zoomed = false; - } - } - } - - pm_passent = ent; - - // set up for pmove - memset (&pm, 0, sizeof(pm)); - - if (ent->movetype == MOVETYPE_NOCLIP) - client->ps.pmove.pm_type = PM_SPECTATOR; - else if (ent->s.modelindex != MAX_MODELS-1) - client->ps.pmove.pm_type = PM_GIB; - else if (ent->deadflag) - client->ps.pmove.pm_type = PM_DEAD; - else - client->ps.pmove.pm_type = PM_NORMAL; - if(level.time > ent->gravity_debounce_time) - client->ps.pmove.gravity = sv_gravity->value; - else - client->ps.pmove.gravity = 0; - - pm.s = client->ps.pmove; - - for (i=0 ; i<3 ; i++) - { - pm.s.origin[i] = ent->s.origin[i]*8; - pm.s.velocity[i] = ent->velocity[i]*8; - } - - if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s))) - { - pm.snapinitial = true; - // gi.dprintf ("pmove changed!\n"); - } - - pm.cmd = *ucmd; - - pm.trace = PM_trace; // adds default parms - pm.pointcontents = gi.pointcontents; - - if(ent->vehicle) - pm.s.pm_flags |= PMF_ON_GROUND; - - // perform a pmove - gi.Pmove (&pm); - - // save results of pmove - client->ps.pmove = pm.s; - client->old_pmove = pm.s; - - for (i=0 ; i<3 ; i++) - { - ent->s.origin[i] = pm.s.origin[i]*0.125; - ent->velocity[i] = pm.s.velocity[i]*0.125; - } - VectorCopy (pm.mins, ent->mins); - VectorCopy (pm.maxs, ent->maxs); - - client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]); - client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]); - client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]); - -// MUD - "correct" Pmove physics - if(pm.waterlevel && ent->in_mud) - { - vec3_t point; - vec3_t end; - - vec3_t deltapos, deltavel; - float frac; - - pm.watertype |= CONTENTS_MUD; - ent->in_mud = pm.waterlevel; - VectorSubtract(ent->s.origin,oldorigin,deltapos); - VectorSubtract(ent->velocity,oldvelocity,deltavel); - if(pm.waterlevel == 1) - { - frac = MUD1BASE + MUD1AMP*sin( (float)(level.framenum%10)/10.*2*M_PI); - ent->s.origin[0] = oldorigin[0] + frac*deltapos[0]; - ent->s.origin[1] = oldorigin[1] + frac*deltapos[1]; - ent->s.origin[2] = oldorigin[2] + 0.75*deltapos[2]; - ent->velocity[0] = oldvelocity[0] + frac*deltavel[0]; - ent->velocity[1] = oldvelocity[1] + frac*deltavel[1]; - ent->velocity[2] = oldvelocity[2] + 0.75*deltavel[2]; - } - else if(pm.waterlevel == 2) - { - trace_t tr; - float dist; - - VectorCopy(oldorigin,point); - point[2] += ent->maxs[2]; - end[0] = point[0]; end[1] = point[1]; end[2] = oldorigin[2] + ent->mins[2]; - tr = gi.trace(point,NULL,NULL,end,ent,CONTENTS_WATER); - dist = point[2] - tr.endpos[2]; - // frac = waterlevel 1 frac at dist=32 or more, - // = waterlevel 3 frac at dist=10 or less - if(dist <= 10) - frac = MUD3; - else - frac = MUD3 + (dist-10)/22.*(MUD1BASE-MUD3); - ent->s.origin[0] = oldorigin[0] + frac*deltapos[0]; - ent->s.origin[1] = oldorigin[1] + frac*deltapos[1]; - ent->s.origin[2] = oldorigin[2] + frac*deltapos[2]; - ent->velocity[0] = oldvelocity[0] + frac*deltavel[0]; - ent->velocity[1] = oldvelocity[1] + frac*deltavel[1]; - ent->velocity[2] = oldvelocity[2] + frac*deltavel[2]; - if(!ent->groundentity) - { - // Player can't possibly move up - ent->s.origin[2] = min(oldorigin[2], ent->s.origin[2]); - ent->velocity[2] = min(oldvelocity[2],ent->velocity[2]); - ent->velocity[2] = min(-10,ent->velocity[2]); - } - } - else - { - ent->s.origin[0] = oldorigin[0] + MUD3*deltapos[0]; - ent->s.origin[1] = oldorigin[1] + MUD3*deltapos[1]; - ent->velocity[0] = oldvelocity[0] + MUD3*deltavel[0]; - ent->velocity[1] = oldvelocity[1] + MUD3*deltavel[1]; - if(ent->groundentity) - { - ent->s.origin[2] = oldorigin[2] + MUD3*deltapos[2]; - ent->velocity[2] = oldvelocity[2] + MUD3*deltavel[2]; - } - else - { - ent->s.origin[2] = min(oldorigin[2],ent->s.origin[2]); - ent->velocity[2] = min(oldvelocity[2], 0); - } - } - } - else - ent->in_mud = 0; -// end MUD - - if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0)) - { - gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, ATTN_NORM, 0); - PlayerNoise(ent, ent->s.origin, PNOISE_SELF); - // Lazarus: temporarily match velocities with entity we just - // jumped from - VectorAdd(ent->groundentity->velocity,ent->velocity,ent->velocity); - } - - if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0)) - ent->client->jumping = 1; - - if (ent->deadflag != DEAD_FROZEN) - ent->viewheight = pm.viewheight; - ent->waterlevel = pm.waterlevel; - ent->watertype = pm.watertype; - ent->groundentity = pm.groundentity; - if (pm.groundentity) - ent->groundentity_linkcount = pm.groundentity->linkcount; - - // Lazarus - lie about ground when driving a vehicle. - // Pmove apparently doesn't think the ground - // can be "owned" - if (ent->vehicle && !ent->groundentity) - { - ent->groundentity = ent->vehicle; - ent->groundentity_linkcount = ent->vehicle->linkcount; - } - - - if (ent->deadflag) - { - if (ent->deadflag != DEAD_FROZEN) - { - client->ps.viewangles[ROLL] = 40; - client->ps.viewangles[PITCH] = -15; - client->ps.viewangles[YAW] = client->killer_yaw; - } - } - else - { - VectorCopy (pm.viewangles, client->v_angle); - VectorCopy (pm.viewangles, client->ps.viewangles); - } - - gi.linkentity (ent); - - if (ent->movetype != MOVETYPE_NOCLIP) - G_TouchTriggers (ent); - - if( (world->effects & FX_WORLDSPAWN_JUMPKICK) && (ent->client->jumping) && (ent->solid != SOLID_NOT)) - kick_attack(ent); - - // touch other objects - // Lazarus: but NOT if game is frozen - if(!level.freeze) - { - for (i=0 ; itouch) - continue; - other->touch (other, ent, NULL, NULL); - } - } - - client->oldbuttons = client->buttons; - client->buttons = ucmd->buttons; - client->latched_buttons |= client->buttons & ~client->oldbuttons; - - // save light level the player is standing on for - // monster sighting AI - ent->light_level = ucmd->lightlevel; - - // fire weapon from final position if needed - if (client->latched_buttons & BUTTONS_ATTACK) - { - if (client->resp.spectator) { - - client->latched_buttons = 0; - - } else if (!client->weapon_thunk) { - client->weapon_thunk = true; - Think_Weapon (ent); - } - } - - if (client->resp.spectator) { - if (ucmd->upmove >= 10) { - if (!(client->ps.pmove.pm_flags & PMF_JUMP_HELD)) { - client->ps.pmove.pm_flags |= PMF_JUMP_HELD; - } - } else - client->ps.pmove.pm_flags &= ~PMF_JUMP_HELD; - } - - if(client->push != NULL) - { - client->push->s.sound = 0; - } - -} - - -/* -============== -ClientBeginServerFrame - -This will be called once for each server frame, before running -any other entities in the world. -============== -*/ -void ClientBeginServerFrame (edict_t *ent) -{ - gclient_t *client; - int buttonMask; - - if (level.intermissiontime) - return; - - client = ent->client; - - // DWH - if(client->spycam) - client = client->camplayer->client; - - if (deathmatch->value && - client->pers.spectator != client->resp.spectator && - (level.time - client->respawn_time) >= 5) { - spectator_respawn(ent); - return; - } - - // run weapon animations if it hasn't been done by a ucmd_t - if (!client->weapon_thunk && !client->resp.spectator) - Think_Weapon (ent); - else - client->weapon_thunk = false; - - if (ent->deadflag) - { - // wait for any button just going down - if ( level.time > client->respawn_time) - { - // in deathmatch, only wait for attack button - if (deathmatch->value) - buttonMask = BUTTONS_ATTACK; - else - buttonMask = -1; - - if ( ( client->latched_buttons & buttonMask ) || - (deathmatch->value && ((int)dmflags->value & DF_FORCE_RESPAWN) ) ) - { - respawn(ent); - client->latched_buttons = 0; - } - } - return; - } - client->latched_buttons = 0; -} - diff --git a/server/p_hud.c b/server/p_hud.c deleted file mode 100644 index 52d266a9..00000000 --- a/server/p_hud.c +++ /dev/null @@ -1,639 +0,0 @@ -#include "baseentity.h" - -/* -====================================================================== - -INTERMISSION - -====================================================================== -*/ - -void MoveClientToIntermission (edict_t *ent) -{ - if (deathmatch->value || coop->value) - ent->client->showscores = true; - VectorCopy (level.intermission_origin, ent->s.origin); - ent->client->ps.pmove.origin[0] = level.intermission_origin[0]*8; - ent->client->ps.pmove.origin[1] = level.intermission_origin[1]*8; - ent->client->ps.pmove.origin[2] = level.intermission_origin[2]*8; - VectorCopy (level.intermission_angle, ent->client->ps.viewangles); - ent->client->ps.pmove.pm_type = PM_FREEZE; - ent->client->ps.gunindex = 0; - ent->client->ps.blend[3] = 0; - ent->client->ps.rdflags &= ~RDF_UNDERWATER; - - // clean up powerup info - ent->client->quad_framenum = 0; - ent->client->invincible_framenum = 0; - ent->client->breather_framenum = 0; - ent->client->enviro_framenum = 0; - ent->client->grenade_blew_up = false; - ent->client->grenade_time = 0; - - ent->viewheight = 0; - ent->s.modelindex = 0; - ent->s.weaponmodel = 0; - ent->s.effects = 0; - ent->s.sound = 0; - ent->solid = SOLID_NOT; - - // add the layout - - if (deathmatch->value || coop->value) - { - DeathmatchScoreboardMessage (ent, NULL); - MESSAGE_SEND (MSG_ONE_R, NULL, ent ); - } - -} - -void BeginIntermission (edict_t *targ) -{ - int i, n; - edict_t *ent, *client; - - if (level.intermissiontime) - return; // already activated - - game.autosaved = false; - - // respawn any dead clients - for (i=0 ; ivalue ; i++) - { - client = g_edicts + 1 + i; - if (!client->inuse) - continue; - if (client->health <= 0) - respawn(client); - } - - level.intermissiontime = level.time; - level.changemap = targ->map; - - if (strstr(level.changemap, "*")) - { - if (coop->value) - { - for (i=0 ; ivalue ; i++) - { - client = g_edicts + 1 + i; - if (!client->inuse) - continue; - // strip players of all keys between units - for (n = 0; n < MAX_ITEMS; n++) - { - if (itemlist[n].flags & IT_KEY) - client->client->pers.inventory[n] = 0; - } - } - } - } - else - { - if (!deathmatch->value) - { - level.exitintermission = 1; // go immediately to the next level - return; - } - } - - level.exitintermission = 0; - - // find an intermission spot - ent = G_Find (NULL, FOFS(classname), "info_player_intermission"); - if (!ent) - { // the map creator forgot to put in an intermission point... - ent = G_Find (NULL, FOFS(classname), "info_player_start"); - if (!ent) - ent = G_Find (NULL, FOFS(classname), "info_player_deathmatch"); - } - else - { // chose one of four spots - i = rand() & 3; - while (i--) - { - ent = G_Find (ent, FOFS(classname), "info_player_intermission"); - if (!ent) // wrap around the list - ent = G_Find (ent, FOFS(classname), "info_player_intermission"); - } - } - - VectorCopy (ent->s.origin, level.intermission_origin); - VectorCopy (ent->s.angles, level.intermission_angle); - - // move all clients to the intermission point - for (i=0 ; ivalue ; i++) - { - client = g_edicts + 1 + i; - if (!client->inuse) - continue; - MoveClientToIntermission (client); - } -} - - -/* -================== -DeathmatchScoreboardMessage - -================== -*/ -void DeathmatchScoreboardMessage (edict_t *ent, edict_t *killer) -{ - char entry[1024]; - char string[1400]; - int stringlength; - int i, j, k; - int sorted[MAX_CLIENTS]; - int sortedscores[MAX_CLIENTS]; - int score, total; - int picnum; - int x, y; - gclient_t *cl; - edict_t *cl_ent; - char *tag; - - // sort the clients by score - total = 0; - for (i=0 ; iinuse || game.clients[i].resp.spectator) - continue; - score = game.clients[i].resp.score; - for (j=0 ; j sortedscores[j]) - break; - } - for (k=total ; k>j ; k--) - { - sorted[k] = sorted[k-1]; - sortedscores[k] = sortedscores[k-1]; - } - sorted[j] = i; - sortedscores[j] = score; - total++; - } - - // print level name and exit rules - string[0] = 0; - - stringlength = strlen(string); - - // add the clients in sorted order - if (total > 12) - total = 12; - - for (i=0 ; i=6) ? 160 : 0; - y = 32 + 32 * (i%6); - - // add a dogtag - if (cl_ent == ent) - tag = "tag1"; - else if (cl_ent == killer) - tag = "tag2"; - else - tag = NULL; - if (tag) - { - sprintf (entry, "xv %i yv %i picn %s ",x+32, y, tag); - j = strlen(entry); - if (stringlength + j > 1024) - break; - strcpy (string + stringlength, entry); - stringlength += j; - } - - // send the layout - sprintf (entry, "client %i %i %i %i %i %i ", x, y, sorted[i], cl->resp.score, cl->ping, (level.framenum - cl->resp.enterframe)/600); - j = strlen(entry); - if (stringlength + j > 1024) - break; - strcpy (string + stringlength, entry); - stringlength += j; - } - - MESSAGE_BEGIN (svc_layout); - WRITE_STRING (string); -} - - -/* -================== -DeathmatchScoreboard - -Draw instead of help message. -Note that it isn't that hard to overflow the 1400 byte message limit! -================== -*/ -void DeathmatchScoreboard (edict_t *ent) -{ - DeathmatchScoreboardMessage (ent, ent->enemy); - MESSAGE_SEND (MSG_ONE_R, NULL, ent ); -} - - -/* -================== -Cmd_Score_f - -Display the scoreboard -================== -*/ -void Cmd_Score_f (edict_t *ent) -{ - ent->client->showinventory = false; - ent->client->showhelp = false; - - if (ent->client->menu) - PMenu_Close(ent); - - if (!deathmatch->value && !coop->value) - return; - - if (ent->client->showscores) - { - ent->client->showscores = false; - return; - } - - ent->client->showscores = true; - DeathmatchScoreboard (ent); -} - - -/* -================== -HelpComputer - -Draw help computer. -================== -*/ -void HelpComputer (edict_t *ent) -{ - char string[1024]; - char *sk; - - if (skill->value == 0) - sk = "easy"; - else if (skill->value == 1) - sk = "medium"; - else if (skill->value == 2) - sk = "hard"; - else - sk = "hard+"; - - // send the layout - if(world->effects & FX_WORLDSPAWN_NOHELP) - { - sprintf (string, "xv %d yv %d picn help ",(int)(world->bleft[0]),(int)(world->bleft[1])); - } - else - { - sprintf (string, "xv 32 yv 8 picn help " // background - "xv 202 yv 12 string2 \"%s\" " // skill - "xv 0 yv 24 cstring2 \"%s\" " // level name - "xv 0 yv 54 cstring2 \"%s\" " // help 1 - "xv 0 yv 110 cstring2 \"%s\" " // help 2 - "xv 50 yv 164 string2 \" kills goals secrets\" " - "xv 50 yv 172 string2 \"%3i/%3i %i/%i %i/%i\" ", - sk, - level.level_name, - game.helpmessage1, - game.helpmessage2, - level.killed_monsters, level.total_monsters, - level.found_goals, level.total_goals, - level.found_secrets, level.total_secrets); - } - - MESSAGE_BEGIN (svc_layout); - WRITE_STRING (string); - MESSAGE_SEND (MSG_ONE_R, NULL, ent ); -} - - -/* -================== -Cmd_Help_f - -Display the current help message -================== -*/ -void Cmd_Help_f (edict_t *ent) -{ - // this is for backwards compatability - if (deathmatch->value) - { - Cmd_Score_f (ent); - return; - } - - ent->client->showinventory = false; - ent->client->showscores = false; - - if (ent->client->showhelp && (ent->client->pers.game_helpchanged == game.helpchanged)) - { - ent->client->showhelp = false; - return; - } - - ent->client->showhelp = true; - ent->client->pers.helpchanged = 0; - HelpComputer (ent); -} - - -//======================================================================= -void WhatIsIt (edict_t *ent) -{ - float range; - int i, num; - edict_t *touch[MAX_EDICTS]; - edict_t *who, *best; - trace_t tr; - vec3_t dir, end, entp, forward, mins, maxs, start, viewp; - - /* Check for looking directly at a player or other non-trigger entity */ - VectorCopy(ent->s.origin, start); - start[2] += ent->viewheight; - AngleVectors(ent->client->v_angle, forward, NULL, NULL); - VectorMA(start, 8192, forward, end); - tr = gi.trace(start, NULL, NULL, end, ent, MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA); - if (tr.ent > world) - { - if(tr.ent->common_name) - ent->client->whatsit = tr.ent->common_name; -// else -// ent->client->whatsit = tr.ent->classname; - return; - } - - /* Check for looking directly at a pickup item */ - VectorCopy(ent->s.origin,viewp); - viewp[2] += ent->viewheight; - AngleVectors(ent->client->v_angle, forward, NULL, NULL); - VectorSet(mins,-4096,-4096,-4096); - VectorSet(maxs, 4096, 4096, 4096); - num = gi.BoxEdicts (mins, maxs, touch, MAX_EDICTS, AREA_TRIGGERS); - best = NULL; - for (i=0 ; iinuse) - continue; - if (!who->item) - continue; - if (!visible(ent,who)) - continue; - if (!infront(ent,who)) - continue; - VectorSubtract(who->s.origin,viewp,dir); - range = VectorLength(dir); - VectorMA(viewp, range, forward, entp); - if(entp[0] < who->s.origin[0] - 17) continue; - if(entp[1] < who->s.origin[1] - 17) continue; - if(entp[2] < who->s.origin[2] - 17) continue; - if(entp[0] > who->s.origin[0] + 17) continue; - if(entp[1] > who->s.origin[1] + 17) continue; - if(entp[2] > who->s.origin[2] + 17) continue; - best = who; - break; - } - if(best) - { - ent->client->whatsit = best->item->pickup_name; - return; - } -} - -/* -=============== -G_SetStats -=============== -*/ -extern void WhatsIt(edict_t *ent); -void G_SetStats (edict_t *ent) -{ - gitem_t *item; - int index, cells; - int power_armor_type; - - // - // health - // - ent->client->ps.stats[STAT_HEALTH_ICON] = level.pic_health; - ent->client->ps.stats[STAT_HEALTH] = ent->health; - - // - // ammo - // - if (!ent->client->ammo_index ) - { - ent->client->ps.stats[STAT_AMMO_ICON] = 0; - ent->client->ps.stats[STAT_AMMO] = 0; - } - else - { - item = &itemlist[ent->client->ammo_index]; - ent->client->ps.stats[STAT_AMMO_ICON] = gi.imageindex (item->icon); - ent->client->ps.stats[STAT_AMMO] = ent->client->pers.inventory[ent->client->ammo_index]; - } - - // - // armor - // - power_armor_type = PowerArmorType (ent); - if (power_armor_type) - { - cells = ent->client->pers.inventory[ITEM_INDEX(FindItem ("cells"))]; - if (cells == 0) - { // ran out of cells for power armor - ent->flags &= ~FL_POWER_ARMOR; - gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/power2.wav"), 1, ATTN_NORM, 0); - power_armor_type = 0;; - } - } - - index = ArmorIndex (ent); - if (power_armor_type && (!index || (level.framenum & 8) ) ) - { // flash between power armor and other armor icon - ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex ("i_powershield"); - ent->client->ps.stats[STAT_ARMOR] = cells; - } - else if (index) - { - item = GetItemByIndex (index); - ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex (item->icon); - ent->client->ps.stats[STAT_ARMOR] = ent->client->pers.inventory[index]; - } - else - { - ent->client->ps.stats[STAT_ARMOR_ICON] = 0; - ent->client->ps.stats[STAT_ARMOR] = 0; - } - - // - // pickup message - // - if (level.time > ent->client->pickup_msg_time) - { - ent->client->ps.stats[STAT_PICKUP_ICON] = 0; - ent->client->ps.stats[STAT_PICKUP_STRING] = 0; - } - - // - // timers - // - if (ent->client->quad_framenum > level.framenum) - { - ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_quad"); - ent->client->ps.stats[STAT_TIMER] = (ent->client->quad_framenum - level.framenum)/10; - } - else if (ent->client->invincible_framenum > level.framenum) - { - ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_invulnerability"); - ent->client->ps.stats[STAT_TIMER] = (ent->client->invincible_framenum - level.framenum)/10; - } - else if (ent->client->enviro_framenum > level.framenum) - { - ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_envirosuit"); - ent->client->ps.stats[STAT_TIMER] = (ent->client->enviro_framenum - level.framenum)/10; - } - else if (ent->client->breather_framenum > level.framenum) - { - ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_rebreather"); - ent->client->ps.stats[STAT_TIMER] = (ent->client->breather_framenum - level.framenum)/10; - } - else if (level.freeze) - { - ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_freeze"); - ent->client->ps.stats[STAT_TIMER] = 30 - level.freezeframes/10; - } - else - { - ent->client->ps.stats[STAT_TIMER_ICON] = 0; - ent->client->ps.stats[STAT_TIMER] = 0; - } - - // - // selected item - // - if (ent->client->pers.selected_item == -1) - ent->client->ps.stats[STAT_SELECTED_ICON] = 0; - else - ent->client->ps.stats[STAT_SELECTED_ICON] = gi.imageindex (itemlist[ent->client->pers.selected_item].icon); - - ent->client->ps.stats[STAT_SELECTED_ITEM] = ent->client->pers.selected_item; - - // Lazarus vehicle/tracktrain - if(ent->vehicle && !(ent->vehicle->spawnflags & 16)) - { - switch(ent->vehicle->moveinfo.state) - { - case -3: ent->client->ps.stats[STAT_SPEED] = gi.imageindex("speedr3"); break; - case -2: ent->client->ps.stats[STAT_SPEED] = gi.imageindex("speedr2"); break; - case -1: ent->client->ps.stats[STAT_SPEED] = gi.imageindex("speedr1"); break; - case 1: ent->client->ps.stats[STAT_SPEED] = gi.imageindex("speed1"); break; - case 2: ent->client->ps.stats[STAT_SPEED] = gi.imageindex("speed2"); break; - case 3: ent->client->ps.stats[STAT_SPEED] = gi.imageindex("speed3"); break; - default: ent->client->ps.stats[STAT_SPEED] = gi.imageindex("speed0"); break; - } - } - else - ent->client->ps.stats[STAT_SPEED] = 0; - - // "whatsit" - if (world->effects & FX_WORLDSPAWN_WHATSIT) - { - if (ent->client->showscores || ent->client->showhelp || ent->client->showinventory) - ent->client->whatsit = NULL; - else if(!(level.framenum % 5)) // only update every 1/2 second - { - char *temp = ent->client->whatsit; - - ent->client->whatsit = NULL; - WhatIsIt(ent); - if(ent->client->whatsit && !temp) - WhatsIt(ent); - } - } - else - ent->client->whatsit = NULL; - - // - // layouts - // - ent->client->ps.stats[STAT_LAYOUTS] = 0; - - if (deathmatch->value) - { - if (ent->client->pers.health <= 0 || level.intermissiontime - || ent->client->showscores) - ent->client->ps.stats[STAT_LAYOUTS] |= 1; - if (ent->client->showinventory && ent->client->pers.health > 0) - ent->client->ps.stats[STAT_LAYOUTS] |= 2; - } - else - { - if (ent->client->showscores || ent->client->showhelp) - ent->client->ps.stats[STAT_LAYOUTS] |= 1; - if (ent->client->showinventory && ent->client->pers.health > 0) - ent->client->ps.stats[STAT_LAYOUTS] |= 2; - } - if(!ent->client->ps.stats[STAT_LAYOUTS] && ent->client->whatsit) - ent->client->ps.stats[STAT_LAYOUTS] |= 1; - - // - // frags - // - ent->client->ps.stats[STAT_FRAGS] = ent->client->resp.score; - - // - // help icon / current weapon if not shown - // - if (ent->client->pers.helpchanged && (level.framenum&8) ) - ent->client->ps.stats[STAT_HELPICON] = gi.imageindex ("i_help"); - else if ( (ent->client->pers.hand == CENTER_HANDED || ent->client->ps.fov > 91) - && ent->client->pers.weapon) - ent->client->ps.stats[STAT_HELPICON] = gi.imageindex (ent->client->pers.weapon->icon); - else - ent->client->ps.stats[STAT_HELPICON] = 0; - - ent->client->ps.stats[STAT_SPECTATOR] = 0; - - if(ent->client->zoomed) - ent->client->ps.stats[STAT_ZOOM] = gi.imageindex("zoom"); - else - ent->client->ps.stats[STAT_ZOOM] = 0; -} - -/* -=============== -G_SetSpectatorStats -=============== -*/ -void G_SetSpectatorStats (edict_t *ent) -{ - gclient_t *cl = ent->client; - - G_SetStats (ent); - - cl->ps.stats[STAT_SPECTATOR] = 1; - - // layouts are independant in spectator - cl->ps.stats[STAT_LAYOUTS] = 0; - if (cl->pers.health <= 0 || level.intermissiontime || cl->showscores) - cl->ps.stats[STAT_LAYOUTS] |= 1; - if (cl->showinventory && cl->pers.health > 0) - cl->ps.stats[STAT_LAYOUTS] |= 2; - - cl->ps.stats[STAT_CHASE] = 0; -} - diff --git a/server/p_menu.c b/server/p_menu.c deleted file mode 100644 index a93c6342..00000000 --- a/server/p_menu.c +++ /dev/null @@ -1,189 +0,0 @@ -#include "baseentity.h" - -void PMenu_Open(edict_t *ent, pmenu_t *entries, int cur, int num) -{ - pmenuhnd_t *hnd; - pmenu_t *p; - int i; - - if (!ent->client) - return; - - if (ent->client->menu) - { - gi.dprintf("warning, ent already has a menu\n"); - PMenu_Close(ent); - } - - hnd = (pmenuhnd_t *)malloc(sizeof(*hnd)); - - hnd->entries = entries; - hnd->num = num; - - if (cur < 0 || !entries[cur].SelectFunc) - { - for (i = 0, p = entries; i < num; i++, p++) - if (p->SelectFunc) - break; - } - else i = cur; - - if (i >= num) hnd->cur = -1; - else hnd->cur = i; - - ent->client->showscores = true; - ent->client->inmenu = true; - ent->client->menu = hnd; - - PMenu_Update(ent); - MESSAGE_SEND (MSG_ONE_R, NULL, ent ); -} - -void PMenu_Close(edict_t *ent) -{ - if (!ent->client->menu) - return; - - free(ent->client->menu); - ent->client->menu = NULL; - ent->client->showscores = false; -} - -void PMenu_Update(edict_t *ent) -{ - char string[1400]; - int i; - pmenu_t *p; - int x; - pmenuhnd_t *hnd; - char *t; - bool alt = false; - - if (!ent->client->menu) { - gi.dprintf("warning: ent has no menu\n"); - return; - } - - hnd = ent->client->menu; - - strcpy(string, "xv 32 yv 8 picn inventory "); - - for (i = 0, p = hnd->entries; i < hnd->num; i++, p++) { - if (!p->text || !*(p->text)) - continue; // blank line - t = p->text; - if (*t == '*') { - alt = true; - t++; - } - sprintf(string + strlen(string), "yv %d ", 32 + i * 8); - if (p->align == PMENU_ALIGN_CENTER) - x = 196/2 - strlen(t)*4 + 64; - else if (p->align == PMENU_ALIGN_RIGHT) - x = 64 + (196 - strlen(t)*8); - else - x = 64; - - sprintf(string + strlen(string), "xv %d ", - x - ((hnd->cur == i) ? 8 : 0)); - - if (hnd->cur == i) - sprintf(string + strlen(string), "string2 \"\x0d%s\" ", t); - else if (alt) - sprintf(string + strlen(string), "string2 \"%s\" ", t); - else - sprintf(string + strlen(string), "string \"%s\" ", t); - alt = false; - } - - MESSAGE_BEGIN (svc_layout); - WRITE_STRING (string); -} - -void PMenu_Next(edict_t *ent) -{ - pmenuhnd_t *hnd; - int i; - pmenu_t *p; - - if (!ent->client->menu) { - gi.dprintf("warning: ent has no menu\n"); - return; - } - - hnd = ent->client->menu; - - if (hnd->cur < 0) - return; // no selectable entries - - i = hnd->cur; - p = hnd->entries + hnd->cur; - do { - i++, p++; - if (i == hnd->num) - i = 0, p = hnd->entries; - if (p->SelectFunc) - break; - } while (i != hnd->cur); - - hnd->cur = i; - - PMenu_Update(ent); - MESSAGE_SEND (MSG_ONE_R, NULL, ent ); -} - -void PMenu_Prev(edict_t *ent) -{ - pmenuhnd_t *hnd; - int i; - pmenu_t *p; - - if (!ent->client->menu) { - gi.dprintf("warning: ent has no menu\n"); - return; - } - - hnd = ent->client->menu; - - if (hnd->cur < 0) - return; // no selectable entries - - i = hnd->cur; - p = hnd->entries + hnd->cur; - do { - if (i == 0) { - i = hnd->num - 1; - p = hnd->entries + i; - } else - i--, p--; - if (p->SelectFunc) - break; - } while (i != hnd->cur); - - hnd->cur = i; - - PMenu_Update(ent); - MESSAGE_SEND (MSG_ONE_R, NULL, ent); -} - -void PMenu_Select(edict_t *ent) -{ - pmenuhnd_t *hnd; - pmenu_t *p; - - if (!ent->client->menu) - { - gi.dprintf("warning: ent has no menu\n"); - return; - } - - hnd = ent->client->menu; - - if (hnd->cur < 0) return; // no selectable entries - - p = hnd->entries + hnd->cur; - - if (p->SelectFunc) - p->SelectFunc(ent, p); -} - diff --git a/server/p_menu.h b/server/p_menu.h deleted file mode 100644 index 0470fd9c..00000000 --- a/server/p_menu.h +++ /dev/null @@ -1,27 +0,0 @@ - -enum { - PMENU_ALIGN_LEFT, - PMENU_ALIGN_CENTER, - PMENU_ALIGN_RIGHT -}; - -typedef struct pmenuhnd_s { - struct pmenu_s *entries; - int cur; - int num; -} pmenuhnd_t; - -typedef struct pmenu_s { - char *text; - int align; - void *arg; - void (*SelectFunc)(edict_t *ent, struct pmenu_s *entry); -} pmenu_t; - -void PMenu_Open(edict_t *ent, pmenu_t *entries, int cur, int num); -void PMenu_Close(edict_t *ent); -void PMenu_Update(edict_t *ent); -void PMenu_Next(edict_t *ent); -void PMenu_Prev(edict_t *ent); -void PMenu_Select(edict_t *ent); - diff --git a/server/p_view.c b/server/p_view.c deleted file mode 100644 index da31e4f1..00000000 --- a/server/p_view.c +++ /dev/null @@ -1,1316 +0,0 @@ - -#include "baseentity.h" -#include "m_player.h" - -static edict_t *current_player; -static gclient_t *current_client; - -static vec3_t forward, right, up; -float xyspeed; - -float bobmove; -int bobcycle; // odd cycles are right foot going forward -float bobfracsin; // sin(bobfrac*M_PI) - -/* -=============== -SV_CalcRoll - -=============== -*/ -float SV_CalcRoll (vec3_t angles, vec3_t velocity) -{ - float sign; - float side; - float value; - - side = DotProduct (velocity, right); - sign = side < 0 ? -1 : 1; - side = fabs(side); - - value = sv_rollangle->value; - - if (side < sv_rollspeed->value) - side = side * value / sv_rollspeed->value; - else - side = value; - - return side*sign; - -} - - -/* -=============== -P_DamageFeedback - -Handles color blends and view kicks -=============== -*/ -void P_DamageFeedback (edict_t *player) -{ - gclient_t *client; - float side; - float realcount, count, kick; - vec3_t v; - int r, l; - static vec3_t power_color = {0.0, 1.0, 0.0}; - static vec3_t acolor = {1.0, 1.0, 1.0}; - static vec3_t bcolor = {1.0, 0.0, 0.0}; - - client = player->client; - - // flash the backgrounds behind the status numbers - client->ps.stats[STAT_FLASHES] = 0; - if (client->damage_blood) - client->ps.stats[STAT_FLASHES] |= 1; - if (client->damage_armor && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum)) - client->ps.stats[STAT_FLASHES] |= 2; - - // total points of damage shot at the player this frame - count = (client->damage_blood + client->damage_armor + client->damage_parmor); - if (count == 0) - return; // didn't take any damage - - // start a pain animation if still in the player model - if (client->anim_priority < ANIM_PAIN && player->s.modelindex == MAX_MODELS-1) - { - static int i; - - client->anim_priority = ANIM_PAIN; - if (client->ps.pmove.pm_flags & PMF_DUCKED) - { - player->s.frame = FRAME_crpain1-1; - client->anim_end = FRAME_crpain4; - } - else - { - i = (i+1)%3; - switch (i) - { - case 0: - player->s.frame = FRAME_pain101-1; - client->anim_end = FRAME_pain104; - break; - case 1: - player->s.frame = FRAME_pain201-1; - client->anim_end = FRAME_pain204; - break; - case 2: - player->s.frame = FRAME_pain301-1; - client->anim_end = FRAME_pain304; - break; - } - } - } - - realcount = count; - if (count < 10) - count = 10; // always make a visible effect - - // play an apropriate pain sound - if ((level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum)) - { - r = 1 + (rand()&1); - player->pain_debounce_time = level.time + 0.7; - if (player->health < 25) - l = 25; - else if (player->health < 50) - l = 50; - else if (player->health < 75) - l = 75; - else - l = 100; - gi.sound (player, CHAN_VOICE, gi.soundindex(va("*pain%i_%i.wav", l, r)), 1, ATTN_NORM, 0); - } - - // the total alpha of the blend is always proportional to count - if (client->damage_alpha < 0) - client->damage_alpha = 0; - client->damage_alpha += count*0.01; - if (client->damage_alpha < 0.2) - client->damage_alpha = 0.2; - if (client->damage_alpha > 0.6) - client->damage_alpha = 0.6; // don't go too saturated - - // the color of the blend will vary based on how much was absorbed - // by different armors - VectorClear (v); - if (client->damage_parmor) - VectorMA (v, (float)client->damage_parmor/realcount, power_color, v); - if (client->damage_armor) - VectorMA (v, (float)client->damage_armor/realcount, acolor, v); - if (client->damage_blood) - VectorMA (v, (float)client->damage_blood/realcount, bcolor, v); - VectorCopy (v, client->damage_blend); - - - // - // calculate view angle kicks - // - kick = abs(client->damage_knockback); - if (kick && player->health > 0) // kick of 0 means no view adjust at all - { - kick = kick * 100 / player->health; - - if (kick < count*0.5) - kick = count*0.5; - if (kick > 50) - kick = 50; - - VectorSubtract (client->damage_from, player->s.origin, v); - VectorNormalize (v); - - side = DotProduct (v, right); - client->v_dmg_roll = kick*side*0.3; - - side = -DotProduct (v, forward); - client->v_dmg_pitch = kick*side*0.3; - - client->v_dmg_time = level.time + DAMAGE_TIME; - } - - // - // clear totals - // - client->damage_blood = 0; - client->damage_armor = 0; - client->damage_parmor = 0; - client->damage_knockback = 0; -} - - - - -/* -=============== -SV_CalcViewOffset - -Auto pitching on slopes? - - fall from 128: 400 = 160000 - fall from 256: 580 = 336400 - fall from 384: 720 = 518400 - fall from 512: 800 = 640000 - fall from 640: 960 = - - damage = deltavelocity*deltavelocity * 0.0001 - -=============== -*/ -void SV_CalcViewOffset (edict_t *ent) -{ - float *angles; - float bob; - float ratio; - float delta; - vec3_t v; - - -//=================================== - - // base angles - angles = ent->client->ps.kick_angles; - - // if dead, fix the angle and don't add any kick - if (ent->deadflag) - { - if(ent->deadflag != DEAD_FROZEN) - { - VectorClear (angles); - - ent->client->ps.viewangles[ROLL] = 40; - ent->client->ps.viewangles[PITCH] = -15; - ent->client->ps.viewangles[YAW] = ent->client->killer_yaw; - } - } - else - { - // add angles based on weapon kick - - VectorCopy (ent->client->kick_angles, angles); - - // add angles based on damage kick - - ratio = (ent->client->v_dmg_time - level.time) / DAMAGE_TIME; - if (ratio < 0) - { - ratio = 0; - ent->client->v_dmg_pitch = 0; - ent->client->v_dmg_roll = 0; - } - angles[PITCH] += ratio * ent->client->v_dmg_pitch; - angles[ROLL] += ratio * ent->client->v_dmg_roll; - - // add pitch based on fall kick - - ratio = (ent->client->fall_time - level.time) / FALL_TIME; - if (ratio < 0) - ratio = 0; - angles[PITCH] += ratio * ent->client->fall_value; - - // add angles based on velocity - - delta = DotProduct (ent->velocity, forward); - angles[PITCH] += delta*run_pitch->value; - - delta = DotProduct (ent->velocity, right); - angles[ROLL] += delta*run_roll->value; - - // add angles based on bob - - delta = bobfracsin * bob_pitch->value * xyspeed; - if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) - delta *= 6; // crouching - angles[PITCH] += delta; - delta = bobfracsin * bob_roll->value * xyspeed; - if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) - delta *= 6; // crouching - if (bobcycle & 1) - delta = -delta; - angles[ROLL] += delta; - } - -//=================================== - - // base origin - - VectorClear (v); - - // add view height - - v[2] += ent->viewheight; - - // add fall height - - ratio = (ent->client->fall_time - level.time) / FALL_TIME; - if (ratio < 0) - ratio = 0; - v[2] -= ratio * ent->client->fall_value * 0.4; - - // add bob height - - bob = bobfracsin * xyspeed * bob_up->value; - if (bob > 6) - bob = 6; -// gi.DebugGraph (bob *2, 255); - v[2] += bob; - - // add kick offset - - VectorAdd (v, ent->client->kick_origin, v); - - // absolutely bound offsets - // so the view can never be outside the player box - - if(ent->client->spycam) { - VectorSet (v, 0, 0, 0); - VectorCopy (ent->client->spycam->s.angles, ent->client->ps.viewangles); - if(ent->client->spycam->svflags & SVF_MONSTER) - ent->client->ps.viewangles[PITCH] = ent->client->spycam->move_angles[PITCH]; - } else { - if (v[0] < -14) - v[0] = -14; - else if (v[0] > 14) - v[0] = 14; - if (v[1] < -14) - v[1] = -14; - else if (v[1] > 14) - v[1] = 14; - if (v[2] < -22) - v[2] = -22; - else if (v[2] > 30) - v[2] = 30; - } - - VectorCopy (v, ent->client->ps.viewoffset); -} - -/* -============== -SV_CalcGunOffset -============== -*/ -void SV_CalcGunOffset (edict_t *ent) -{ - int i; - float delta; - - // gun angles from bobbing - ent->client->ps.gunangles[ROLL] = xyspeed * bobfracsin * 0.005; - ent->client->ps.gunangles[YAW] = xyspeed * bobfracsin * 0.01; - if (bobcycle & 1) - { - ent->client->ps.gunangles[ROLL] = -ent->client->ps.gunangles[ROLL]; - ent->client->ps.gunangles[YAW] = -ent->client->ps.gunangles[YAW]; - } - - ent->client->ps.gunangles[PITCH] = xyspeed * bobfracsin * 0.005; - - // gun angles from delta movement - for (i=0 ; i<3 ; i++) - { - delta = ent->client->oldviewangles[i] - ent->client->ps.viewangles[i]; - if (delta > 180) - delta -= 360; - if (delta < -180) - delta += 360; - if (delta > 45) - delta = 45; - if (delta < -45) - delta = -45; - if (i == YAW) - ent->client->ps.gunangles[ROLL] += 0.1*delta; - ent->client->ps.gunangles[i] += 0.2 * delta; - } - - // gun height - VectorClear (ent->client->ps.gunoffset); -// ent->ps->gunorigin[2] += bob; - - // gun_x / gun_y / gun_z are development tools - for (i=0 ; i<3 ; i++) - { - ent->client->ps.gunoffset[i] += forward[i]*(gun_y->value); - ent->client->ps.gunoffset[i] += right[i]*gun_x->value; - ent->client->ps.gunoffset[i] += up[i]* (-gun_z->value); - } -} - - -/* -============= -SV_AddBlend -============= -*/ -void SV_AddBlend (float r, float g, float b, float a, float *v_blend) -{ - float a2, a3; - - if (a <= 0) - return; - a2 = v_blend[3] + (1-v_blend[3])*a; // new total alpha - a3 = v_blend[3]/a2; // fraction of color from old - - v_blend[0] = v_blend[0]*a3 + r*(1-a3); - v_blend[1] = v_blend[1]*a3 + g*(1-a3); - v_blend[2] = v_blend[2]*a3 + b*(1-a3); - v_blend[3] = a2; -} - - -/* -============= -SV_CalcBlend -============= -*/ -void SV_CalcBlend (edict_t *ent) -{ - int contents; - vec3_t vieworg; - int remaining; - - ent->client->ps.blend[0] = ent->client->ps.blend[1] = - ent->client->ps.blend[2] = ent->client->ps.blend[3] = 0; - - // add for contents - VectorAdd (ent->s.origin, ent->client->ps.viewoffset, vieworg); - contents = gi.pointcontents (vieworg); - if (contents & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER) ) - ent->client->ps.rdflags |= RDF_UNDERWATER; - else - { - ent->client->ps.rdflags &= ~RDF_UNDERWATER; - ent->client->ps.rdflags &= ~RDF_WATER; - ent->client->ps.rdflags &= ~RDF_SLIME; - ent->client->ps.rdflags &= ~RDF_LAVA; - } - - if (contents & (/*CONTENTS_SOLID|*/CONTENTS_LAVA)) - { - if(!r_motionblur->value) - SV_AddBlend (1.0, 0.3, 0.0, 0.6, ent->client->ps.blend); - else ent->client->ps.rdflags |= RDF_LAVA; - } - else if (contents & CONTENTS_SLIME) - { - if(!r_motionblur->value) - SV_AddBlend (0.0, 0.1, 0.05, 0.6, ent->client->ps.blend); - else ent->client->ps.rdflags |= RDF_SLIME; - } - else if (contents & CONTENTS_WATER) - { - if(!r_motionblur->value) - SV_AddBlend (0.5, 0.3, 0.2, 0.4, ent->client->ps.blend); - else ent->client->ps.rdflags |= RDF_WATER; - } - - // add for powerups - if (ent->client->quad_framenum > level.framenum) - { - remaining = ent->client->quad_framenum - level.framenum; - if (remaining == 30) // beginning to fade - gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage2.wav"), 1, ATTN_NORM, 0); - if (remaining > 30 || (remaining & 4) ) - SV_AddBlend (0, 0, 1, 0.08, ent->client->ps.blend); - } - else if (ent->client->invincible_framenum > level.framenum) - { - remaining = ent->client->invincible_framenum - level.framenum; - if (remaining == 30) // beginning to fade - gi.sound(ent, CHAN_ITEM, gi.soundindex("items/protect2.wav"), 1, ATTN_NORM, 0); - if (remaining > 30 || (remaining & 4) ) - SV_AddBlend (1, 1, 0, 0.08, ent->client->ps.blend); - } - else if (ent->client->enviro_framenum > level.framenum) - { - remaining = ent->client->enviro_framenum - level.framenum; - if (remaining == 30) // beginning to fade - gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0); - if (remaining > 30 || (remaining & 4) ) - SV_AddBlend (0, 1, 0, 0.08, ent->client->ps.blend); - } - else if (ent->client->breather_framenum > level.framenum) - { - remaining = ent->client->breather_framenum - level.framenum; - if (remaining == 30) // beginning to fade - gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0); - if (remaining > 30 || (remaining & 4) ) - SV_AddBlend (0.4, 1, 0.4, 0.04, ent->client->ps.blend); - } - - if (level.freeze && (level.freezeframes % 30 == 0)) - { - if(level.freezeframes == 270) - gi.sound(ent,CHAN_ITEM,gi.soundindex("items/stasis_stop.wav"), 1, ATTN_NORM, 0); - else - gi.sound(ent,CHAN_ITEM,gi.soundindex("items/stasis.wav"), 1, ATTN_NORM, 0); - } - - // add for damage - if (ent->client->damage_alpha > 0) - { - if(!r_motionblur->value) - { - // SV_AddBlend (ent->client->damage_blend[0],ent->client->damage_blend[1], ent->client->damage_blend[2], - // ent->client->damage_alpha, ent->client->ps.blend); //shit! - SV_AddBlend (1.0, 0, 0, 0.2, ent->client->ps.blend); - } else - ent->client->ps.rdflags |= RDF_PAIN; - - } - if (ent->client->bonus_alpha > 0) - - SV_AddBlend (0.85, 0.7, 0.3, ent->client->bonus_alpha, ent->client->ps.blend); - - // drop the damage value - ent->client->damage_alpha -= 0.06; - if (ent->client->damage_alpha < 0) - { - ent->client->damage_alpha = 0; - ent->client->ps.rdflags &= ~RDF_PAIN; - } - - - // drop the bonus value - ent->client->bonus_alpha -= 0.1; - if (ent->client->bonus_alpha < 0) - ent->client->bonus_alpha = 0; - - // DWH: Screen fade from target_failure or target_fade - if (ent->client->fadein > 0) - { - float alpha; - - // Turn off fade for dead software players or they won't see menu - if(ent->health <= 0) ent->client->fadein = 0; - - if(ent->client->fadein > level.framenum) - { - alpha = ent->client->fadealpha*(1.0 - (ent->client->fadein-level.framenum)/(ent->client->fadein-ent->client->fadestart)); - SV_AddBlend (ent->client->fadecolor[0], - ent->client->fadecolor[1], - ent->client->fadecolor[2], - alpha, ent->client->ps.blend); - } - else if(ent->client->fadehold > level.framenum) - { - SV_AddBlend (ent->client->fadecolor[0], - ent->client->fadecolor[1], - ent->client->fadecolor[2], - ent->client->fadealpha, ent->client->ps.blend); - } - else if(ent->client->fadeout > level.framenum) - { - alpha = ent->client->fadealpha*((ent->client->fadeout-level.framenum)/(ent->client->fadeout-ent->client->fadehold)); - SV_AddBlend (ent->client->fadecolor[0], - ent->client->fadecolor[1], - ent->client->fadecolor[2], - alpha, ent->client->ps.blend); - } - else ent->client->fadein = 0; - } -} - -/* -================= -P_SlamDamage -Serves same purpose as P_FallingDamage, but detects wall impacts -================= -*/ -void P_SlamDamage (edict_t *ent) -{ - float delta; - int damage; - vec3_t dir; - vec3_t deltav; - - if (ent->s.modelindex != MAX_MODELS-1) - return; // not in the player model - - if (ent->movetype == MOVETYPE_NOCLIP) - return; - - deltav[0] = ent->velocity[0] - ent->client->oldvelocity[0]; - deltav[1] = ent->velocity[1] - ent->client->oldvelocity[1]; - deltav[2] = 0; - delta = VectorLength(deltav); - delta = delta*delta * 0.0001; - if (delta > 40) - { - if (ent->health > 0) - { - if(delta > 65) - ent->s.event = EV_FALLFAR; - else - ent->s.event = EV_FALL; - } - ent->pain_debounce_time = level.time; // no normal pain sound - damage = (delta-40)/2; - if (damage < 1) - damage = 1; - VectorCopy(deltav,dir); - VectorNegate(dir,dir); - VectorNormalize(dir); - if (!deathmatch->value || !((int)dmflags->value & DF_NO_FALLING) ) - T_Damage (ent, world, world, dir, ent->s.origin, vec3_origin, damage, 0, 0, MOD_FALLING); - } -} - - -/* -================= -P_FallingDamage -================= -*/ -void P_FallingDamage (edict_t *ent) -{ - float delta; - int damage; - vec3_t dir; - - if (ent->s.modelindex != MAX_MODELS-1) - return; // not in the player model - - if (ent->movetype == MOVETYPE_NOCLIP) - return; - - if ((ent->client->oldvelocity[2] < 0) && (ent->velocity[2] > ent->client->oldvelocity[2]) && (!ent->groundentity)) - { - delta = ent->client->oldvelocity[2]; - } - else - { - if (!ent->groundentity) - return; - delta = ent->velocity[2] - ent->client->oldvelocity[2]; - ent->client->jumping = 0; - } - delta = delta*delta * 0.0001; - - // never take falling damage if completely underwater - if (ent->waterlevel == 3) - return; - if (ent->waterlevel == 2) - delta *= 0.25; - if (ent->waterlevel == 1) - delta *= 0.5; - - if (delta < 1) return; - - ent->client->fall_value = delta*0.5; - if (ent->client->fall_value > 40) - ent->client->fall_value = 40; - ent->client->fall_time = level.time + FALL_TIME; - - if (delta > 30) - { - if (ent->health > 0) - { - if (delta >= 55) - gi.sound(ent,CHAN_VOICE,gi.soundindex("*fall1.wav"),1.0,ATTN_NORM,0); - else - gi.sound(ent,CHAN_VOICE,gi.soundindex("*fall2.wav"),1.0,ATTN_NORM,0); - - if(world->effects & FX_WORLDSPAWN_ALERTSOUNDS) - PlayerNoise(ent,ent->s.origin,PNOISE_SELF); - - } - - ent->pain_debounce_time = level.time; // no normal pain sound - damage = (delta-30)/2; - if (damage < 1) - damage = 1; - VectorSet (dir, 0, 0, 1); - - if (!deathmatch->value || !((int)dmflags->value & DF_NO_FALLING) ) - T_Damage (ent, world, world, dir, ent->s.origin, vec3_origin, damage, 0, 0, MOD_FALLING); - } - else - { - ent->s.event = EV_FALLSHORT; - if(world->effects & FX_WORLDSPAWN_ALERTSOUNDS) - PlayerNoise(ent,ent->s.origin,PNOISE_SELF); - return; - } -} - - - -/* -============= -P_WorldEffects -============= -*/ -void P_WorldEffects (void) -{ - bool breather; - bool envirosuit; - int waterlevel, old_waterlevel, old_watertype; - - if (current_player->movetype == MOVETYPE_NOCLIP) - { - current_player->air_finished = level.time + 12; // don't need air - return; - } - - waterlevel = current_player->waterlevel; - old_waterlevel = current_client->old_waterlevel; - old_watertype = current_player->old_watertype; - current_client->old_waterlevel = waterlevel; - current_player->old_watertype = current_player->watertype; - - breather = current_client->breather_framenum > level.framenum; - envirosuit = current_client->enviro_framenum > level.framenum; - - // - // if just entered a water volume, play a sound - // - if (!old_waterlevel && waterlevel) - { - PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); - if (current_player->watertype & CONTENTS_LAVA) - gi.sound (current_player, CHAN_BODY, gi.soundindex("player/lava_in.wav"), 1, ATTN_NORM, 0); - else if (current_player->watertype & CONTENTS_SLIME) - gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); - else if (current_player->watertype & CONTENTS_MUD) - gi.sound (current_player, CHAN_BODY, gi.soundindex("mud/mud_in2.wav"), 1, ATTN_NORM, 0); - else if (current_player->watertype & CONTENTS_WATER) - gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); - current_player->flags |= FL_INWATER; - - // clear damage_debounce, so the pain sound will play immediately - current_player->damage_debounce_time = level.time - 1; - } - - // - // if just completely exited a water volume, play a sound - // - if (old_waterlevel && ! waterlevel) - { - PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); - if(old_watertype & CONTENTS_MUD) - gi.sound (current_player, CHAN_BODY, gi.soundindex("mud/mud_out1.wav"), 1, ATTN_NORM, 0); - else - gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0); - current_player->flags &= ~FL_INWATER; - } - - // - // check for head just going under water - // - if (old_waterlevel != 3 && waterlevel == 3) - { - if(current_player->in_mud) - gi.sound (current_player, CHAN_BODY, gi.soundindex("mud/mud_un1.wav"), 1, ATTN_NORM, 0); - else - gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_un.wav"), 1, ATTN_NORM, 0); - } - - // - // check for head just coming out of water - // - if (old_waterlevel == 3 && waterlevel != 3) - { - if (current_player->air_finished < level.time) - { // gasp for air - gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp1.wav"), 1, ATTN_NORM, 0); - PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); - } - else if (current_player->air_finished < level.time + 11) - { // just break surface - gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp2.wav"), 1, ATTN_NORM, 0); - } - } - - // - // check for drowning - // - if (waterlevel == 3) - { - // breather or envirosuit give air - if (breather || envirosuit) - { - current_player->air_finished = level.time + 10; - - if (((int)(current_client->breather_framenum - level.framenum) % 25) == 0) - { - if (!current_client->breather_sound) - gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath1.wav"), 1, ATTN_NORM, 0); - else - gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath2.wav"), 1, ATTN_NORM, 0); - current_client->breather_sound ^= 1; - PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); - //FIXME: release a bubble? - } - } - - // if out of air, start drowning - if (current_player->air_finished < level.time) - { // drown! - if (current_player->client->next_drown_time < level.time - && current_player->health > 0) - { - current_player->client->next_drown_time = level.time + 1; - - // take more damage the longer underwater - current_player->dmg += 2; - if (current_player->dmg > 15) - current_player->dmg = 15; - - // play a gurp sound instead of a normal pain sound - if (current_player->health <= current_player->dmg) - gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/drown1.wav"), 1, ATTN_NORM, 0); - else if (rand()&1) - gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp1.wav"), 1, ATTN_NORM, 0); - else - gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp2.wav"), 1, ATTN_NORM, 0); - - current_player->pain_debounce_time = level.time; - - T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, current_player->dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER); - } - } - } - else - { - current_player->air_finished = level.time + 12; - current_player->dmg = 2; - } - - // - // check for sizzle damage - // - if (waterlevel && (current_player->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) - { - if (current_player->watertype & CONTENTS_LAVA) - { - if (current_player->health > 0 - && current_player->pain_debounce_time <= level.time - && current_client->invincible_framenum < level.framenum) - { - if (rand()&1) - gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn1.wav"), 1, ATTN_NORM, 0); - else - gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn2.wav"), 1, ATTN_NORM, 0); - current_player->pain_debounce_time = level.time + 1; - } - - if (envirosuit) // take 1/3 damage with envirosuit - T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_LAVA); - else - T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 3*waterlevel, 0, 0, MOD_LAVA); - } - - if (current_player->watertype & CONTENTS_SLIME) - { - if (!envirosuit) - { // no damage from slime with envirosuit - T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_SLIME); - } - } - } -} - - -/* -=============== -G_SetClientEffects -=============== -*/ -void G_SetClientEffects (edict_t *ent) -{ - int pa_type; - int remaining; - - ent->s.effects = 0; -// ent->s.renderfx = 0; - // PGM - player is always ir visible, even dead. - ent->s.renderfx = RF_IR_VISIBLE; - - if (ent->health <= 0 || level.intermissiontime) - return; - - if(ent->flags & FL_DISGUISED) - ent->s.renderfx |= RF_USE_DISGUISE; - - if (ent->powerarmor_time > level.time) - { - pa_type = PowerArmorType (ent); - if (pa_type == POWER_ARMOR_SCREEN) - { - ent->s.effects |= EF_POWERSCREEN; - } - else if (pa_type == POWER_ARMOR_SHIELD) - { - ent->s.effects |= EF_COLOR_SHELL; - ent->s.renderfx |= RF_SHELL_GREEN; - } - } - - if (ent->client->quad_framenum > level.framenum) - { - remaining = ent->client->quad_framenum - level.framenum; - if (remaining > 30 || (remaining & 4) ) - ent->s.effects |= EF_QUAD; - } - - if (ent->client->invincible_framenum > level.framenum) - { - remaining = ent->client->invincible_framenum - level.framenum; - if (remaining > 30 || (remaining & 4) ) - ent->s.effects |= EF_PENT; - } - - // show cheaters!!! - if (ent->flags & FL_GODMODE) - { - ent->s.effects |= EF_COLOR_SHELL; - ent->s.renderfx |= (RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE); - } - - if (ent->client->flashlight) - { - vec3_t end, forward, offset, right, start, up; - trace_t tr; - - if(level.flashlight_cost > 0) { - if(!strcasecmp(FLASHLIGHT_ITEM,"health") || - (ent->client->pers.inventory[ITEM_INDEX(FindItem(FLASHLIGHT_ITEM))]>=level.flashlight_cost) ) { - // Player has items remaining - if(ent->client->flashlight_time <= level.time) { - ent->client->pers.inventory[ITEM_INDEX(FindItem(FLASHLIGHT_ITEM))]-=level.flashlight_cost; - ent->client->flashlight_time = level.time + FLASHLIGHT_DRAIN; - } - } else { - // Out of item - ent->client->flashlight = false; - } - } - if(ent->client->flashlight) - { - AngleVectors (ent->s.angles, forward, right, up); - VectorSet(offset, 0, 0, ent->viewheight-8); - G_ProjectSource (ent->s.origin, offset, forward, right, start); - VectorMA(start,384,forward,end); // was 128 - tr = gi.trace (start,NULL,NULL, end, ent, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER); - if (tr.fraction != 1) - VectorMA(tr.endpos,-4,forward,end); - VectorCopy(tr.endpos,end); - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_FLASHLIGHT); - WRITE_COORD (end); - WRITE_SHORT (ent - g_edicts); - MESSAGE_SEND (MSG_PVS, end, NULL); - - } - } -} - - -/* -=============== -G_SetClientEvent -=============== -*/ -void G_SetClientEvent (edict_t *ent) -{ - if (ent->s.event) - return; - - if ( ent->groundentity ) - { - if( ent->in_mud && (ent->waterlevel == 1) && (xyspeed > 40)) - { - if ( (level.framenum % 10) == 0 ) - { - if( rand() & 1 ) - gi.sound(ent, CHAN_BODY, gi.soundindex("mud/wade_mud1.wav"), 1, ATTN_NORM, 0); - else - gi.sound(ent, CHAN_BODY, gi.soundindex("mud/wade_mud2.wav"), 1, ATTN_NORM, 0); - } - } - } - else - { - if(world->effects & FX_WORLDSPAWN_STEPSOUNDS) - { - if( (level.framenum % 5) == 0) - { - if(!ent->waterlevel && (ent->movetype != MOVETYPE_NOCLIP) && (fabs(ent->velocity[2]) > 50)) - { - vec3_t end, forward; - trace_t tr; - AngleVectors(ent->s.angles,forward,NULL,NULL); - VectorMA(ent->s.origin,2,forward,end); - tr = gi.trace(ent->s.origin,ent->mins,ent->maxs,end,ent,CONTENTS_LADDER); - if(tr.fraction < 1.0) - { - int r; - r = rand() & 1 + ent->client->leftfoot*2; - ent->client->leftfoot = 1 - ent->client->leftfoot; - switch (r) - { - case 0: gi.sound(ent,CHAN_VOICE,gi.soundindex("player/pl_ladder1.wav"),1.0,ATTN_NORM,0); break; - case 1: gi.sound(ent,CHAN_VOICE,gi.soundindex("player/pl_ladder3.wav"),1.0,ATTN_NORM,0); break; - case 2: gi.sound(ent,CHAN_VOICE,gi.soundindex("player/pl_ladder2.wav"),1.0,ATTN_NORM,0); break; - case 3: gi.sound(ent,CHAN_VOICE,gi.soundindex("player/pl_ladder4.wav"),1.0,ATTN_NORM,0); break; - } - } - } - } - } - } -} - -/* -=============== -G_SetClientSound -=============== -*/ -void G_SetClientSound (edict_t *ent) -{ - char *weap; - - if (ent->client->pers.game_helpchanged != game.helpchanged) - { - ent->client->pers.game_helpchanged = game.helpchanged; - ent->client->pers.helpchanged = 1; - } - - // help beep (no more than three times) - if (ent->client->pers.helpchanged && ent->client->pers.helpchanged <= 3 && !(level.framenum&63) ) - { - ent->client->pers.helpchanged++; - gi.sound (ent, CHAN_VOICE, gi.soundindex ("misc/pc_up.wav"), 1, ATTN_STATIC, 0); - } - - - if (ent->client->pers.weapon) - weap = ent->client->pers.weapon->classname; - else - weap = ""; - - if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) - ent->s.sound = snd_fry; - else if (strcmp(weap, "weapon_railgun") == 0) - ent->s.sound = gi.soundindex("weapons/rg_hum.wav"); - else if (strcmp(weap, "weapon_bfg") == 0) - ent->s.sound = gi.soundindex("weapons/bfg_hum.wav"); - else if (ent->client->weapon_sound) - ent->s.sound = ent->client->weapon_sound; - else - ent->s.sound = 0; -} - -/* -=============== -G_SetClientFrame -=============== -*/ -void G_SetClientFrame (edict_t *ent) -{ - gclient_t *client; - bool duck, run; - - if (ent->s.modelindex != MAX_MODELS-1) - return; // not in the player model - - client = ent->client; - - if (client->ps.pmove.pm_flags & PMF_DUCKED) - duck = true; - else - duck = false; - if (xyspeed) - run = true; - else - run = false; - - // Lazarus: override run animations for vehicle drivers - if (ent->vehicle) - run = false; - - // check for stand/duck and stop/go transitions - if (duck != client->anim_duck && client->anim_priority < ANIM_DEATH) - goto newanim; - if (run != client->anim_run && client->anim_priority == ANIM_BASIC) - goto newanim; - if (!ent->groundentity && client->anim_priority <= ANIM_WAVE) - goto newanim; - - if(client->anim_priority == ANIM_REVERSE) - { - if(ent->s.frame > client->anim_end) - { - ent->s.frame--; - return; - } - } - else if (ent->s.frame < client->anim_end) - { // continue an animation - ent->s.frame++; - return; - } - - if (client->anim_priority == ANIM_DEATH) - return; // stay there - if (client->anim_priority == ANIM_JUMP) - { - if (!ent->groundentity) - return; // stay there - ent->client->anim_priority = ANIM_WAVE; - ent->s.frame = FRAME_jump3; - ent->client->anim_end = FRAME_jump6; - return; - } - -newanim: - // return to either a running or standing frame - client->anim_priority = ANIM_BASIC; - client->anim_duck = duck; - client->anim_run = run; - - if (!ent->groundentity) - { - client->anim_priority = ANIM_JUMP; - if (ent->s.frame != FRAME_jump2) - ent->s.frame = FRAME_jump1; - client->anim_end = FRAME_jump2; - } - else if (run) - { // running - if (duck) - { - ent->s.frame = FRAME_crwalk1; - client->anim_end = FRAME_crwalk6; - } - else - { - ent->s.frame = FRAME_run1; - client->anim_end = FRAME_run6; - } - } - else - { // standing - if (duck) - { - ent->s.frame = FRAME_crstnd01; - client->anim_end = FRAME_crstnd19; - } - else - { - ent->s.frame = FRAME_stand01; - client->anim_end = FRAME_stand40; - } - } -} - - -/* -================= -ClientEndServerFrame - -Called for each player at the end of the server frame -and right after spawning -================= -*/ -// Lazarus: "whatsit" display - -void WhatsIt(edict_t *ent) -{ - char string[128]; - - if(!ent->client->whatsit) - return; - - sprintf(string, "xv 0 yb -68 cstring2 \"%s\" ", ent->client->whatsit); - MESSAGE_BEGIN (svc_layout); - WRITE_STRING (string); - MESSAGE_SEND(MSG_ONE_R, NULL, ent ); -} - -void ClientEndServerFrame (edict_t *ent) -{ - float bobtime; - int i; - - current_player = ent; - current_client = ent->client; - - // - // If the origin or velocity have changed since ClientThink(), - // update the pmove values. This will happen when the client - // is pushed by a bmodel or kicked by an explosion. - // - // If it wasn't updated here, the view position would lag a frame - // behind the body position when pushed -- "sinking into plats" - // - for (i=0 ; i<3 ; i++) - { - current_client->ps.pmove.origin[i] = ent->s.origin[i]*8.0; - current_client->ps.pmove.velocity[i] = ent->velocity[i]*8.0; - } - - // - // If the end of unit layout is displayed, don't give - // the player any normal movement attributes - // - if (level.intermissiontime) - { - // FIXME: add view drifting here? - current_client->ps.blend[3] = 0; - current_client->ps.fov = 90; - G_SetStats (ent); - return; - } - - AngleVectors (ent->client->v_angle, forward, right, up); - - // burn from lava, etc - P_WorldEffects (); - - // - // set model angles from view angles so other things in - // the world can tell which direction you are looking - // - if (ent->client->v_angle[PITCH] > 180) - ent->s.angles[PITCH] = (-360 + ent->client->v_angle[PITCH])/3; - else - ent->s.angles[PITCH] = ent->client->v_angle[PITCH]/3; - ent->s.angles[YAW] = ent->client->v_angle[YAW]; - ent->s.angles[ROLL] = 0; - ent->s.angles[ROLL] = SV_CalcRoll (ent->s.angles, ent->velocity)*4; - - // - // calculate speed and cycle to be used for - // all cyclic walking effects - // - xyspeed = sqrt(ent->velocity[0]*ent->velocity[0] + ent->velocity[1]*ent->velocity[1]); - - if (xyspeed < 5) - { - bobmove = 0; - current_client->bobtime = 0; // start at beginning of cycle again - } - else if (ent->groundentity) - { // so bobbing only cycles when on ground - if (xyspeed > 210) - bobmove = 0.25; - else if (xyspeed > 100) - bobmove = 0.125; - else - bobmove = 0.0625; - } - - bobtime = (current_client->bobtime += bobmove); - - if (current_client->ps.pmove.pm_flags & PMF_DUCKED) - bobtime *= 4; - - bobcycle = (int)bobtime; - - // Lazarus: vehicle drivers don't bob - if(ent->vehicle) - bobfracsin = 0.; - else - bobfracsin = fabs(sin(bobtime*M_PI)); - - // detect hitting the floor - P_FallingDamage (ent); - - // Lazarus: detect hitting walls - P_SlamDamage (ent); - - // apply all the damage taken this frame - P_DamageFeedback (ent); - - // determine the view offsets - SV_CalcViewOffset (ent); - - // determine the gun offsets - SV_CalcGunOffset (ent); - - // determine the full screen color blend - // must be after viewoffset, so eye contents can be - // accurately determined - // FIXME: with client prediction, the contents - // should be determined by the client - SV_CalcBlend (ent); - - // chase cam stuff - if (ent->client->resp.spectator) - G_SetSpectatorStats(ent); - else - G_SetStats (ent); - G_SetClientEvent (ent); - - G_SetClientEffects (ent); - - G_SetClientSound (ent); - - G_SetClientFrame (ent); - - VectorCopy (ent->velocity, ent->client->oldvelocity); - VectorCopy (ent->client->ps.viewangles, ent->client->oldviewangles); - - // clear weapon kicks - VectorClear (ent->client->kick_origin); - VectorClear (ent->client->kick_angles); - - // if the scoreboard is up, update it - if (!(level.framenum & 31)) - { - if (ent->client->showscores) - { - if (ent->client->menu) - PMenu_Update(ent); - else - DeathmatchScoreboardMessage (ent, ent->enemy); - MESSAGE_SEND (MSG_ONE, NULL, ent ); - } - else if(ent->client->whatsit) - WhatsIt(ent); - } -} - diff --git a/server/p_weapon.c b/server/p_weapon.c deleted file mode 100644 index aae7a336..00000000 --- a/server/p_weapon.c +++ /dev/null @@ -1,1194 +0,0 @@ -// g_weapon.c - -#include "baseentity.h" -#include "m_player.h" - - -static bool is_quad; -static byte is_silenced; - - -void weapon_grenade_fire (edict_t *ent, bool held); - - -void P_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result) -{ - vec3_t _distance; - - VectorCopy (distance, _distance); - // DWH - if (client->zoomed) - _distance[2] += 8; - // end DWH - if (client->pers.hand == LEFT_HANDED) - _distance[1] *= -1; - else if (client->pers.hand == CENTER_HANDED) - _distance[1] = 0; - G_ProjectSource (point, _distance, forward, right, result); -} - -/* -=============== -PlayerNoise - -Each player can have two noise objects associated with it: -a personal noise (jumping, pain, weapon firing), and a weapon -target noise (bullet wall impacts) - -Monsters that don't directly see the player can move -to a noise in hopes of seeing the player from there. -=============== -*/ -void PlayerNoise(edict_t *who, vec3_t where, int type) -{ - edict_t *noise; - - if (type == PNOISE_WEAPON) - { - if (who->client->silencer_shots) - { - who->client->silencer_shots--; - return; - } - } - - if (deathmatch->value) - return; - - if (who->flags & FL_NOTARGET) - return; - - if (!who->mynoise) - { - noise = G_Spawn(); - noise->classname = "player_noise"; - noise->class_id = ENTITY_PLAYER_NOISE; - VectorSet (noise->mins, -8, -8, -8); - VectorSet (noise->maxs, 8, 8, 8); - noise->owner = who; - noise->svflags = SVF_NOCLIENT; - who->mynoise = noise; - - noise = G_Spawn(); - noise->classname = "player_noise"; - noise->class_id = ENTITY_PLAYER_NOISE; - VectorSet (noise->mins, -8, -8, -8); - VectorSet (noise->maxs, 8, 8, 8); - noise->owner = who; - noise->svflags = SVF_NOCLIENT; - who->mynoise2 = noise; - } - - if (type == PNOISE_SELF || type == PNOISE_WEAPON) - { - noise = who->mynoise; - level.sound_entity = noise; - level.sound_entity_framenum = level.framenum; - } - else // type == PNOISE_IMPACT - { - noise = who->mynoise2; - level.sound2_entity = noise; - level.sound2_entity_framenum = level.framenum; - } - - VectorCopy (where, noise->s.origin); - VectorSubtract (where, noise->maxs, noise->absmin); - VectorAdd (where, noise->maxs, noise->absmax); - noise->teleport_time = level.time; - gi.linkentity (noise); -} - - -bool Pickup_Weapon (edict_t *ent, edict_t *other) -{ - int index; - gitem_t *ammo; - - index = ITEM_INDEX(ent->item); - - if ( ( ((int)(dmflags->value) & DF_WEAPONS_STAY) || coop->value) - && other->client->pers.inventory[index]) - { - if (!(ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM) ) ) - return false; // leave the weapon for others to pickup - } - - other->client->pers.inventory[index]++; - - if (!(ent->spawnflags & DROPPED_ITEM) ) - { - // give them some ammo with it - - // Lazarus: blaster doesn't use ammo - if (ent->item->ammo) - { - ammo = FindItem (ent->item->ammo); - if ( (int)dmflags->value & DF_INFINITE_AMMO ) - Add_Ammo (other, ammo, 1000); - else - Add_Ammo (other, ammo, ammo->quantity); - } - - if (! (ent->spawnflags & DROPPED_PLAYER_ITEM) ) - { - if (deathmatch->value) - { - if ((int)(dmflags->value) & DF_WEAPONS_STAY) - ent->flags |= FL_RESPAWN; - else - SetRespawn (ent, 30); - } - if (coop->value) - ent->flags |= FL_RESPAWN; - } - } - - if (other->client->pers.weapon != ent->item && - (other->client->pers.inventory[index] == 1) && - ( !deathmatch->value || other->client->pers.weapon == FindItem("blaster") || - other->client->pers.weapon == FindItem("No weapon") ) ) - other->client->newweapon = ent->item; - - // If rocket launcher, give the HML (but no ammo). - if (index == rl_index) - other->client->pers.inventory[hml_index] = other->client->pers.inventory[index]; - - return true; -} - - -/* -=============== -ChangeWeapon - -The old weapon has been dropped all the way, so make the new one -current -=============== -*/ -void ChangeWeapon (edict_t *ent) -{ - int i; - - if (ent->client->grenade_time) - { - ent->client->grenade_time = level.time; - ent->client->weapon_sound = 0; - weapon_grenade_fire (ent, false); - ent->client->grenade_time = 0; - } - - ent->client->pers.lastweapon = ent->client->pers.weapon; - ent->client->pers.weapon = ent->client->newweapon; - ent->client->newweapon = NULL; - ent->client->machinegun_shots = 0; - - // set visible model - if (ent->s.modelindex == MAX_MODELS-1) { - if (ent->client->pers.weapon) - i = ((ent->client->pers.weapon->weapmodel & 0xff) << 8); - else - i = 0; - ent->s.skin = (ent - g_edicts - 1) | i; - } - - if (ent->client->pers.weapon && ent->client->pers.weapon->ammo) - ent->client->ammo_index = ITEM_INDEX(FindItem(ent->client->pers.weapon->ammo)); - else - ent->client->ammo_index = 0; - - if (!ent->client->pers.weapon) - { // dead - ent->client->ps.gunindex = 0; - return; - } - - ent->client->weaponstate = WEAPON_ACTIVATING; - ent->client->ps.gunframe = 0; - - ent->client->ps.gunindex = gi.modelindex(ent->client->pers.weapon->view_model); - - // DWH: change weapon model index if necessary - if(ITEM_INDEX(ent->client->pers.weapon) == noweapon_index) - ent->s.weaponmodel = 0; - else ent->s.weaponmodel = MAX_MODELS-1; - - ent->client->anim_priority = ANIM_PAIN; - if(ent->client->ps.pmove.pm_flags & PMF_DUCKED) - { - ent->s.frame = FRAME_crpain1; - ent->client->anim_end = FRAME_crpain4; - } - else - { - ent->s.frame = FRAME_pain301; - ent->client->anim_end = FRAME_pain304; - - } -} - -/* -================= -NoAmmoWeaponChange -================= -*/ -void NoAmmoWeaponChange (edict_t *ent) -{ - if ( ent->client->pers.inventory[slugs_index] - && ent->client->pers.inventory[ITEM_INDEX(FindItem("railgun"))] ) - { - ent->client->newweapon = FindItem ("railgun"); - return; - } - if ( ent->client->pers.inventory[cells_index] - && ent->client->pers.inventory[ITEM_INDEX(FindItem("hyperblaster"))] ) - { - ent->client->newweapon = FindItem ("hyperblaster"); - return; - } - if ( ent->client->pers.inventory[bullets_index] - && ent->client->pers.inventory[ITEM_INDEX(FindItem("chaingun"))] ) - { - ent->client->newweapon = FindItem ("chaingun"); - return; - } - if ( ent->client->pers.inventory[bullets_index] - && ent->client->pers.inventory[ITEM_INDEX(FindItem("machinegun"))] ) - { - ent->client->newweapon = FindItem ("machinegun"); - return; - } - if ( ent->client->pers.inventory[shells_index] > 1 - && ent->client->pers.inventory[ITEM_INDEX(FindItem("super shotgun"))] ) - { - ent->client->newweapon = FindItem ("super shotgun"); - return; - } - if ( ent->client->pers.inventory[shells_index] - && ent->client->pers.inventory[ITEM_INDEX(FindItem("shotgun"))] ) - { - ent->client->newweapon = FindItem ("shotgun"); - return; - } - // DWH: Dude may not HAVE a blaster - //ent->client->newweapon = FindItem ("blaster"); - if ( ent->client->pers.inventory[ITEM_INDEX(FindItem("blaster"))] ) - ent->client->newweapon = FindItem ("blaster"); - else - ent->client->newweapon = FindItem ("No Weapon"); -} - -/* -================= -Think_Weapon - -Called by ClientBeginServerFrame and ClientThink -================= -*/ -void Think_Weapon (edict_t *ent) -{ - // if just died, put the weapon away - if (ent->health < 1) - { - ent->client->newweapon = NULL; - ChangeWeapon (ent); - } - - // Lazarus: Don't fire if game is frozen - if (level.freeze) - return; - - // call active weapon think routine - if (ent->client->pers.weapon && ent->client->pers.weapon->weaponthink) - { - is_quad = (ent->client->quad_framenum > level.framenum); - if (ent->client->silencer_shots) - is_silenced = MZ_SILENCED; - else - is_silenced = 0; - ent->client->pers.weapon->weaponthink (ent); - } -} - - -/* -================ -Use_Weapon - -Make the weapon ready if there is ammo -================ -*/ -void Use_Weapon (edict_t *ent, gitem_t *in_item) -{ - int ammo_index; - gitem_t *ammo_item; - int index; - gitem_t *item; - int current_weapon_index; - - item = in_item; - index = ITEM_INDEX(item); - current_weapon_index = ITEM_INDEX(ent->client->pers.weapon); - - // see if we're already using it - if ( (index == current_weapon_index) || - ( (index == rl_index) && (current_weapon_index == hml_index) ) || - ( (index == hml_index) && (current_weapon_index == rl_index) ) ) - { - if(current_weapon_index == rl_index) - { - if(ent->client->pers.inventory[homing_index] > 0) - { - item = FindItem("homing missile launcher"); - index = hml_index; - } - else - return; - } - else if(current_weapon_index == hml_index) - { - if(ent->client->pers.inventory[rockets_index] > 0) - { - item = FindItem("rocket launcher"); - index = rl_index; - } - else - return; - } - else - return; - } - - if (item->ammo && !g_select_empty->value && !(item->flags & IT_AMMO)) - { - ammo_item = FindItem(item->ammo); - ammo_index = ITEM_INDEX(ammo_item); - - if (!ent->client->pers.inventory[ammo_index]) - { - // Lazarus: If player is attempting to switch to RL and doesn't have rockets, - // but DOES have homing missiles, switch to HML - if(index == rl_index) - { - if( (ent->client->pers.inventory[homing_index] > 0) && - (ent->client->pers.inventory[hml_index] > 0) ) - { - ent->client->newweapon = FindItem("homing missile launcher"); - return; - } - } - gi.cprintf (ent, PRINT_HIGH, "No %s for %s.\n", ammo_item->pickup_name, item->pickup_name); - return; - } - - if (ent->client->pers.inventory[ammo_index] < item->quantity) - { - gi.cprintf (ent, PRINT_HIGH, "Not enough %s for %s.\n", ammo_item->pickup_name, item->pickup_name); - return; - } - } - - // change to this weapon when down - ent->client->newweapon = item; -} - - - -/* -================ -Drop_Weapon -================ -*/ -void Drop_Weapon (edict_t *ent, gitem_t *item) -{ - int index; - - if ((int)(dmflags->value) & DF_WEAPONS_STAY) - return; - - index = ITEM_INDEX(item); - // see if we're already using it - if ( ((item == ent->client->pers.weapon) || (item == ent->client->newweapon))&& (ent->client->pers.inventory[index] == 1) ) - { - gi.cprintf (ent, PRINT_HIGH, "Can't drop current weapon\n"); - return; - } - - // Lazarus: Don't drop rocket launcher if current weapon is homing missile launcher - if (index == rl_index) - { - int current_weapon_index; - current_weapon_index = ITEM_INDEX(ent->client->pers.weapon); - if(current_weapon_index == hml_index) - { - gi.cprintf (ent, PRINT_HIGH, "Can't drop current weapon\n"); - return; - } - } - - Drop_Item (ent, item); - ent->client->pers.inventory[index]--; - - // Lazarus: if dropped weapon is RL, decrement HML inventory also - if (item->weapmodel == WEAP_ROCKETLAUNCHER) - ent->client->pers.inventory[hml_index] = ent->client->pers.inventory[index]; -} - - -/* -================ -Weapon_Generic - -A generic function to handle the basics of weapon thinking -================ -*/ -#define FRAME_FIRE_FIRST (FRAME_ACTIVATE_LAST + 1) -#define FRAME_IDLE_FIRST (FRAME_FIRE_LAST + 1) -#define FRAME_DEACTIVATE_FIRST (FRAME_IDLE_LAST + 1) - -void Weapon_Generic (edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, int FRAME_IDLE_LAST, int FRAME_DEACTIVATE_LAST, int *pause_frames, int *fire_frames, void (*fire)(edict_t *ent)) -{ - int n; - - if(ent->deadflag || ent->s.modelindex != MAX_MODELS-1) // VWep animations screw up corpses - { - return; - } - - if (ent->client->weaponstate == WEAPON_DROPPING) - { - if (ent->client->ps.gunframe == FRAME_DEACTIVATE_LAST) - { - ChangeWeapon (ent); - return; - } - else if ((FRAME_DEACTIVATE_LAST - ent->client->ps.gunframe) == 4) - { - ent->client->anim_priority = ANIM_REVERSE; - if(ent->client->ps.pmove.pm_flags & PMF_DUCKED) - { - ent->s.frame = FRAME_crpain4+1; - ent->client->anim_end = FRAME_crpain1; - } - else - { - ent->s.frame = FRAME_pain304+1; - ent->client->anim_end = FRAME_pain301; - - } - } - - ent->client->ps.gunframe++; - return; - } - - if (ent->client->weaponstate == WEAPON_ACTIVATING) - { - if (ent->client->ps.gunframe == FRAME_ACTIVATE_LAST) - { - ent->client->weaponstate = WEAPON_READY; - ent->client->ps.gunframe = FRAME_IDLE_FIRST; - return; - } - - ent->client->ps.gunframe++; - return; - } - - if ((ent->client->newweapon) && (ent->client->weaponstate != WEAPON_FIRING)) - { - ent->client->weaponstate = WEAPON_DROPPING; - ent->client->ps.gunframe = FRAME_DEACTIVATE_FIRST; - - if ((FRAME_DEACTIVATE_LAST - FRAME_DEACTIVATE_FIRST) < 4) - { - ent->client->anim_priority = ANIM_REVERSE; - if(ent->client->ps.pmove.pm_flags & PMF_DUCKED) - { - ent->s.frame = FRAME_crpain4+1; - ent->client->anim_end = FRAME_crpain1; - } - else - { - ent->s.frame = FRAME_pain304+1; - ent->client->anim_end = FRAME_pain301; - - } - } - return; - } - - if (ent->client->weaponstate == WEAPON_READY) - { - // Lazarus: Head off firing 2nd homer NOW, so firing animations aren't played - if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTONS_ATTACK) ) - { - if(ent->client->ammo_index == homing_index) - { - if(ent->client->homing_rocket && ent->client->homing_rocket->inuse) - { - ent->client->latched_buttons &= ~BUTTONS_ATTACK; - ent->client->buttons &= ~BUTTONS_ATTACK; - } - } - } - - if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTONS_ATTACK) ) - { - if ((!ent->client->ammo_index) || ( ent->client->pers.inventory[ent->client->ammo_index] >= ent->client->pers.weapon->quantity)) - { - ent->client->ps.gunframe = FRAME_FIRE_FIRST; - ent->client->weaponstate = WEAPON_FIRING; - - // start the animation - ent->client->anim_priority = ANIM_ATTACK; - if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) - { - ent->s.frame = FRAME_crattak1-1; - ent->client->anim_end = FRAME_crattak9; - } - else - { - ent->s.frame = FRAME_attack1-1; - ent->client->anim_end = FRAME_attack8; - } - } - else - { - if (level.time >= ent->pain_debounce_time) - { - gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0); - ent->pain_debounce_time = level.time + 1; - } - NoAmmoWeaponChange (ent); - } - ent->client->latched_buttons &= ~BUTTONS_ATTACK; - } - else - { - if (ent->client->ps.gunframe == FRAME_IDLE_LAST) - { - ent->client->ps.gunframe = FRAME_IDLE_FIRST; - return; - } - - if (pause_frames) - { - for (n = 0; pause_frames[n]; n++) - { - if (ent->client->ps.gunframe == pause_frames[n]) - { - if (rand()&15) - return; - } - } - } - ent->client->ps.gunframe++; - return; - } - } - - if (ent->client->weaponstate == WEAPON_FIRING) - { - for (n = 0; fire_frames[n]; n++) - { - if (ent->client->ps.gunframe == fire_frames[n]) - { - if (ent->client->quad_framenum > level.framenum) - gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0); - - fire (ent); - break; - } - } - - if (!fire_frames[n]) - ent->client->ps.gunframe++; - - if (ent->client->ps.gunframe == FRAME_IDLE_FIRST+1) - ent->client->weaponstate = WEAPON_READY; - } -} - - -/* -====================================================================== - -GRENADE - -====================================================================== -*/ - -#define GRENADE_TIMER 3.0 -#define GRENADE_MINSPEED 400 -#define GRENADE_MAXSPEED 800 - -void weapon_grenade_fire (edict_t *ent, bool held) -{ - vec3_t offset; - vec3_t forward, right; - vec3_t start; - int damage = 125; - float timer; - int speed; - float radius; - - radius = damage+40; - if (is_quad) - damage *= 4; - - VectorSet(offset, 8, 8, ent->viewheight-8); - AngleVectors (ent->client->v_angle, forward, right, NULL); - P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start); - - timer = ent->client->grenade_time - level.time; - speed = GRENADE_MINSPEED + (GRENADE_TIMER - timer) * ((GRENADE_MAXSPEED - GRENADE_MINSPEED) / GRENADE_TIMER); - -//fire_grenade2 (ent, start, forward, damage, speed, timer, radius, held); - - if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) ) - ent->client->pers.inventory[ent->client->ammo_index]--; - - ent->client->grenade_time = level.time + 1.0; - - if(ent->deadflag || ent->s.modelindex != MAX_MODELS-1) // VWep animations screw up corpses - { - return; - } - - if (ent->health <= 0) - return; - - if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) - { - ent->client->anim_priority = ANIM_ATTACK; - ent->s.frame = FRAME_crattak1-1; - ent->client->anim_end = FRAME_crattak3; - } - else - { - ent->client->anim_priority = ANIM_REVERSE; - ent->s.frame = FRAME_wave08; - ent->client->anim_end = FRAME_wave01; - } -} - -void Weapon_Grenade (edict_t *ent) -{ - if ((ent->client->newweapon) && (ent->client->weaponstate == WEAPON_READY)) - { - ChangeWeapon (ent); - return; - } - - if (ent->client->weaponstate == WEAPON_ACTIVATING) - { - ent->client->weaponstate = WEAPON_READY; - ent->client->ps.gunframe = 16; - return; - } - - if (ent->client->weaponstate == WEAPON_READY) - { - if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTONS_ATTACK) ) - { - ent->client->latched_buttons &= ~BUTTONS_ATTACK; - if (ent->client->pers.inventory[ent->client->ammo_index]) - { - ent->client->ps.gunframe = 1; - ent->client->weaponstate = WEAPON_FIRING; - ent->client->grenade_time = 0; - } - else - { - if (level.time >= ent->pain_debounce_time) - { - gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0); - ent->pain_debounce_time = level.time + 1; - } - NoAmmoWeaponChange (ent); - } - return; - } - - if ((ent->client->ps.gunframe == 29) || (ent->client->ps.gunframe == 34) || (ent->client->ps.gunframe == 39) || (ent->client->ps.gunframe == 48)) - { - if (rand()&15) - return; - } - - if (++ent->client->ps.gunframe > 48) - ent->client->ps.gunframe = 16; - return; - } - - if (ent->client->weaponstate == WEAPON_FIRING) - { - if (ent->client->ps.gunframe == 5) - gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/hgrena1b.wav"), 1, ATTN_NORM, 0); - - if (ent->client->ps.gunframe == 11) - { - if (!ent->client->grenade_time) - { - ent->client->grenade_time = level.time + GRENADE_TIMER + 0.2; - ent->client->weapon_sound = gi.soundindex("weapons/hgrenc1b.wav"); - } - - // they waited too long, detonate it in their hand - if (!ent->client->grenade_blew_up && level.time >= ent->client->grenade_time) - { - ent->client->weapon_sound = 0; - weapon_grenade_fire (ent, true); - ent->client->grenade_blew_up = true; - } - - if (ent->client->buttons & BUTTONS_ATTACK) - return; - - if (ent->client->grenade_blew_up) - { - if (level.time >= ent->client->grenade_time) - { - ent->client->ps.gunframe = 15; - ent->client->grenade_blew_up = false; - } - else - { - return; - } - } - } - - if (ent->client->ps.gunframe == 12) - { - ent->client->weapon_sound = 0; - weapon_grenade_fire (ent, false); - } - - if ((ent->client->ps.gunframe == 15) && (level.time < ent->client->grenade_time)) - return; - - ent->client->ps.gunframe++; - - if (ent->client->ps.gunframe == 16) - { - ent->client->grenade_time = 0; - ent->client->weaponstate = WEAPON_READY; - } - } -} - -/* -====================================================================== - -GRENADE LAUNCHER - -====================================================================== -*/ - -void weapon_grenadelauncher_fire (edict_t *ent) -{ - vec3_t offset; - vec3_t forward, right; - vec3_t start; - int damage = 120; - float radius; - - radius = damage+40; - if (is_quad) - damage *= 4; - - VectorSet(offset, 8, 8, ent->viewheight-8); - AngleVectors (ent->client->v_angle, forward, right, NULL); - P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start); - - VectorScale (forward, -2, ent->client->kick_origin); - ent->client->kick_angles[0] = -1; - -//fire_grenade (ent, start, forward, damage, 600, 2.5, radius); - - MESSAGE_BEGIN (svc_muzzleflash); - WRITE_SHORT (ent-g_edicts); - WRITE_BYTE (MZ_GRENADE | is_silenced); - MESSAGE_SEND (MSG_PVS, ent->s.origin, NULL); - - ent->client->ps.gunframe++; - - PlayerNoise(ent, start, PNOISE_WEAPON); - - if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) ) - ent->client->pers.inventory[ent->client->ammo_index]--; -} - -void Weapon_GrenadeLauncher (edict_t *ent) -{ - static int pause_frames[] = {34, 51, 59, 0}; - static int fire_frames[] = {6, 0}; - - Weapon_Generic (ent, 5, 16, 59, 64, pause_frames, fire_frames, weapon_grenadelauncher_fire); -} - -/* -====================================================================== - -ROCKET - -====================================================================== -*/ -edict_t *rocket_target(edict_t *self, vec3_t start, vec3_t forward) -{ - float bd, d; - int i; - edict_t *who, *best; - trace_t tr; - vec3_t dir, end; - - VectorMA(start, 8192, forward, end); - - /* Check for aiming directly at a damageable entity */ - tr = gi.trace(start, NULL, NULL, end, self, MASK_SHOT); - if ((tr.ent->takedamage != DAMAGE_NO) && (tr.ent->solid != SOLID_NOT)) - return tr.ent; - - /* Check for damageable entity within a tolerance of view angle */ - bd = 0; - best = NULL; - for (i=1, who=g_edicts+1; iinuse) - continue; - if (who == self) - continue; - if (who->takedamage == DAMAGE_NO) - continue; - if (who->solid == SOLID_NOT) - continue; - VectorMA(who->absmin,0.5,who->size,end); - tr = gi.trace (start, vec3_origin, vec3_origin, end, self, MASK_OPAQUE); - if(tr.fraction < 1.0) - continue; - VectorSubtract(end, self->s.origin, dir); - VectorNormalize(dir); - d = DotProduct(forward, dir); - if (d > bd) { - bd = d; - best = who; - } - } - if (bd > 0.90) - return best; - - return NULL; -} - -void Weapon_RocketLauncher_Fire (edict_t *ent) -{ - vec3_t offset, start; - vec3_t forward, right; - int damage; - float damage_radius; - int radius_damage; - - damage = 100 + (int)(random() * 20.0); - radius_damage = 120; - damage_radius = 120; - if (is_quad) - { - damage *= 4; - radius_damage *= 4; - } - - AngleVectors (ent->client->v_angle, forward, right, NULL); - - VectorScale (forward, -2, ent->client->kick_origin); - ent->client->kick_angles[0] = -1; - - VectorSet(offset, 8, 8, ent->viewheight-8); - P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start); - if(ent->client->pers.fire_mode) - { - edict_t *target; - - if(ent->client->homing_rocket && ent->client->homing_rocket->inuse) - { - ent->client->ps.gunframe++; - return; - } - - target = rocket_target(ent, start, forward); -//fire_rocket (ent, start, forward, damage, 650, damage_radius, radius_damage, target); - } -//else fire_rocket (ent, start, forward, damage, 650, damage_radius, radius_damage, NULL); - - // send muzzle flash - MESSAGE_BEGIN (svc_muzzleflash); - WRITE_SHORT (ent-g_edicts); - WRITE_BYTE (MZ_ROCKET | is_silenced); - MESSAGE_SEND (MSG_PVS, ent->s.origin, NULL); - - ent->client->ps.gunframe++; - - PlayerNoise(ent, start, PNOISE_WEAPON); - - if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) ) - ent->client->pers.inventory[ent->client->ammo_index]--; -} - -void Weapon_RocketLauncher (edict_t *ent) -{ - static int pause_frames[] = {25, 33, 42, 50, 0}; - static int fire_frames[] = {5, 0}; - - Weapon_Generic (ent, 4, 12, 50, 54, pause_frames, fire_frames, Weapon_RocketLauncher_Fire); -} - -void Weapon_HomingMissileLauncher_Fire (edict_t *ent) -{ - ent->client->pers.fire_mode = 1; - Weapon_RocketLauncher_Fire (ent); - ent->client->pers.fire_mode = 0; -} - -void Weapon_HomingMissileLauncher (edict_t *ent) -{ - static int pause_frames[] = {25, 33, 42, 50, 0}; - static int fire_frames[] = {5, 0}; - - Weapon_Generic (ent, 4, 12, 50, 54, pause_frames, fire_frames, Weapon_HomingMissileLauncher_Fire); -} -/* -====================================================================== - -BLASTER / HYPERBLASTER - -====================================================================== -*/ - -/* -================= -fire_blaster - -Fires a single blaster bolt. Used by the blaster and hyper blaster. -================= -*/ -void blaster_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) -{ - int mod; - - if (other == self->owner) - return; - - if (surf && (surf->flags & SURF_SKY)) - { - G_FreeEdict (self); - return; - } - - if (self->owner->client) - PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); - - if (other->takedamage) - { - if (self->spawnflags & 1) - mod = MOD_HYPERBLASTER; - else - mod = MOD_BLASTER; - T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod); - } - else - { - MESSAGE_BEGIN (svc_temp_entity); - WRITE_BYTE (TE_BLASTER); - WRITE_COORD (self->s.origin); - if (!plane) WRITE_DIR (vec3_origin); - else WRITE_DIR (plane->normal); - MESSAGE_SEND (MSG_PVS, self->s.origin, NULL); - } - - G_FreeEdict (self); -} - -/* -================= -check_dodge - -This is a support routine used when a client is firing -a non-instant attack weapon. It checks to see if a -monster's dodge function should be called. -================= -*/ -static void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed) -{ - vec3_t end; - vec3_t v; - trace_t tr; - float eta; - - // easy mode only ducks one quarter the time - if (skill->value == 0) - { - if (random() > 0.25) - return; - } - VectorMA (start, 8192, dir, end); - tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT); - if ((tr.ent) && (tr.ent->svflags & SVF_MONSTER) && (tr.ent->health > 0) && (tr.ent->monsterinfo.dodge) && infront(tr.ent, self)) - { - VectorSubtract (tr.endpos, start, v); - eta = (VectorLength(v) - tr.ent->maxs[0]) / speed; - tr.ent->monsterinfo.dodge (tr.ent, self, eta); - } -} - -void fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, bool hyper) -{ - edict_t *bolt; - trace_t tr; - - VectorNormalize (dir); - - bolt = G_Spawn(); - bolt->svflags = SVF_DEADMONSTER; - // yes, I know it looks weird that projectiles are deadmonsters - // what this means is that when prediction is used against the object - // (blaster/hyperblaster shots), the player won't be solid clipped against - // the object. Right now trying to run into a firing hyperblaster - // is very jerky since you are predicted 'against' the shots. - VectorCopy (start, bolt->s.origin); - VectorCopy (start, bolt->s.old_origin); - vectoangles (dir, bolt->s.angles); - VectorScale (dir, speed, bolt->velocity); - bolt->movetype = MOVETYPE_FLYMISSILE; - bolt->clipmask = MASK_SHOT; - bolt->solid = SOLID_BBOX; - bolt->s.effects |= effect; - VectorClear (bolt->mins); - VectorClear (bolt->maxs); - bolt->s.modelindex = gi.modelindex ("models/shell9mm.mdl"); - bolt->s.sound = gi.soundindex ("misc/lasfly.wav"); - bolt->owner = self; - bolt->touch = blaster_touch; - bolt->nextthink = level.time + 2; - bolt->think = G_FreeEdict; - bolt->dmg = damage; - bolt->classname = "bolt"; - - gi.linkentity (bolt); - - if (self->client) - check_dodge (self, bolt->s.origin, dir, speed); - - tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT); - if (tr.fraction < 1.0) - { - VectorMA (bolt->s.origin, -10, dir, bolt->s.origin); - bolt->touch (bolt, tr.ent, NULL, NULL); - } - -} - -void Blaster_Fire (edict_t *ent, vec3_t g_offset, int damage, bool hyper, int effect) -{ - vec3_t forward, right; - vec3_t start; - vec3_t offset; - - if (is_quad) - damage *= 4; - AngleVectors (ent->client->v_angle, forward, right, NULL); - VectorSet(offset, 24, 8, ent->viewheight-8); - VectorAdd (offset, g_offset, offset); - P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start); - - VectorScale (forward, -2, ent->client->kick_origin); - ent->client->kick_angles[0] = -1; - - fire_blaster (ent, start, forward, damage, 1000, effect, hyper); - - // send muzzle flash - MESSAGE_BEGIN (svc_muzzleflash); - WRITE_SHORT (ent-g_edicts); - WRITE_BYTE (MZ_HYPERBLASTER | is_silenced); - MESSAGE_SEND (MSG_PVS, ent->s.origin, NULL); - - PlayerNoise(ent, start, PNOISE_WEAPON); -} - - -void Weapon_Blaster_Fire (edict_t *ent) -{ - int damage; - - if (deathmatch->value) - damage = 15; - else - damage = 10; - Blaster_Fire (ent, vec3_origin, damage, false, EF_BLASTER); - ent->client->ps.gunframe++; -} - -void Weapon_Blaster (edict_t *ent) -{ - static int pause_frames[] = {19, 32, 0}; - static int fire_frames[] = {5, 0}; - - Weapon_Generic (ent, 4, 8, 52, 55, pause_frames, fire_frames, Weapon_Blaster_Fire); -} - -//====================================================================== -void Weapon_Null(edict_t *ent) -{ - if (ent->client->newweapon) ChangeWeapon(ent); -} -//====================================================================== -void kick_attack (edict_t * ent ) -{ - vec3_t start; - vec3_t forward, right; - vec3_t offset; - int damage = 10; - int kick = 300; - trace_t tr; - vec3_t end; - - if(ent->client->quad_framenum > level.framenum) - { - damage *= 4; - kick *= 4; - } - - AngleVectors (ent->client->v_angle, forward, right, NULL); - - VectorScale (forward, 0, ent->client->kick_origin); - - VectorSet(offset, 0, 0, ent->viewheight-20); - P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start); - - VectorMA( start, 25, forward, end ); - - tr = gi.trace (ent->s.origin, NULL, NULL, end, ent, MASK_SHOT); - - // don't need to check for water - if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) - { - if (tr.fraction < 1.0) - { - if (tr.ent->takedamage) - { - if ( tr.ent->health <= 0 ) - return; - - if (((tr.ent != ent) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value) && OnSameTeam (tr.ent, ent))) - return; - // zucc stop powerful upwards kicking - //forward[2] = 0; - - // glass fx - T_Damage (tr.ent, ent, ent, forward, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_KICK ); - gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/kick.wav"), 1, ATTN_NORM, 0); - PlayerNoise(ent, ent->s.origin, PNOISE_SELF); - ent->client->jumping = 0; // only 1 jumpkick per jump - } - } - } -} diff --git a/server/server.dsp b/server/server.dsp deleted file mode 100644 index 4b3c6209..00000000 --- a/server/server.dsp +++ /dev/null @@ -1,205 +0,0 @@ -# Microsoft Developer Studio Project File - Name="server" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=server - Win32 Release -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "server.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "server.mak" CFG="server - Win32 Release" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "server - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "server - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "server - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir ".\Release" -# PROP BASE Intermediate_Dir ".\Release" -# PROP BASE Target_Dir "." -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "..\temp\server\!release" -# PROP Intermediate_Dir "..\temp\server\!release" -# PROP Ignore_Export_Lib 1 -# PROP Target_Dir "." -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c -# ADD CPP /nologo /W3 /GX /O2 /I ".\\" /I ".\gamerules" /I ".\ents" /I ".\global" /I "../public" /I "../public/libs" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "NDEBUG" /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 -# ADD LINK32 kernel32.lib user32.lib winmm.lib /nologo /subsystem:windows /dll /pdb:none /machine:I386 -# SUBTRACT LINK32 /debug -# Begin Custom Build -TargetDir=\XASH3D\src_main\!source\temp\server\!release -InputPath=\XASH3D\src_main\!source\temp\server\!release\server.dll -SOURCE="$(InputPath)" - -"D:\Xash3D\xash\bin\server.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - copy $(TargetDir)\server.dll "D:\Xash3D\xash\bin\server.dll" - -# End Custom Build - -!ELSEIF "$(CFG)" == "server - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir ".\Debug" -# PROP BASE Intermediate_Dir ".\Debug" -# PROP BASE Target_Dir "." -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "..\temp\server\!debug" -# PROP Intermediate_Dir "..\temp\server\!debug" -# PROP Ignore_Export_Lib 1 -# PROP Target_Dir "." -# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c -# ADD CPP /nologo /MTd /W3 /Gm /Gi /GX /ZI /Od /I ".\\" /I ".\gamerules" /I ".\ents" /I ".\global" /I "../public" /I "../public/libs" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /FD /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "_DEBUG" /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 -# ADD LINK32 kernel32.lib user32.lib winmm.lib /nologo /subsystem:windows /dll /debug /machine:I386 -# SUBTRACT LINK32 /incremental:no /map -# Begin Custom Build -TargetDir=\XASH3D\src_main\!source\temp\server\!debug -InputPath=\XASH3D\src_main\!source\temp\server\!debug\server.dll -SOURCE="$(InputPath)" - -"D:\Xash3D\xash\bin\server.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - copy $(TargetDir)\server.dll "D:\Xash3D\xash\bin\server.dll" - -# End Custom Build - -!ENDIF - -# Begin Target - -# Name "server - Win32 Release" -# Name "server - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" -# Begin Source File - -SOURCE=.\client.c -# End Source File -# Begin Source File - -SOURCE=.\g_cmds.c -# End Source File -# Begin Source File - -SOURCE=.\g_items.c -# End Source File -# Begin Source File - -SOURCE=.\g_main.c -# End Source File -# Begin Source File - -SOURCE=.\g_misc.c -# End Source File -# Begin Source File - -SOURCE=.\g_phys.c -# End Source File -# Begin Source File - -SOURCE=.\g_save.c -# End Source File -# Begin Source File - -SOURCE=.\g_spawn.c -# End Source File -# Begin Source File - -SOURCE=.\g_svcmds.c -# End Source File -# Begin Source File - -SOURCE=.\g_utils.c -# End Source File -# Begin Source File - -SOURCE=.\p_client.c -# End Source File -# Begin Source File - -SOURCE=.\p_hud.c -# End Source File -# Begin Source File - -SOURCE=.\p_menu.c -# End Source File -# Begin Source File - -SOURCE=.\p_view.c -# End Source File -# Begin Source File - -SOURCE=.\p_weapon.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" -# Begin Source File - -SOURCE=.\baseentity.h -# End Source File -# Begin Source File - -SOURCE=..\common\const.h -# End Source File -# Begin Source File - -SOURCE=.\g_local.h -# End Source File -# Begin Source File - -SOURCE=.\m_player.h -# End Source File -# Begin Source File - -SOURCE=.\p_menu.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project