aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTravis Bradshaw <travis.bradshaw@idsoftware.com>2012-01-31 13:57:11 -0600
committerTravis Bradshaw <travis.bradshaw@idsoftware.com>2012-01-31 13:57:11 -0600
commit372afde46e7defc9dd2d719a1732b8ace1fa096e (patch)
tree6b1e834e265b4a026e1274971234e78dcd13ad51
downloadquake2-master.tar.gz
quake2-master.tar.bz2
quake2-master.zip
The original Quake 2 sources as originally released under the GPL license on December 21, 2001.HEADmaster
-rw-r--r--3.15_Changes.txt107
-rw-r--r--3.16_Changes.txt127
-rw-r--r--3.17_Changes.txt158
-rw-r--r--3.18_changes.txt53
-rw-r--r--baseq2/config.cfg150
-rw-r--r--baseq2/save/save0/game.ssvbin0 -> 32012 bytes
-rw-r--r--baseq2/save/save0/server.ssvbin0 -> 2720 bytes
-rw-r--r--changes.txt166
-rw-r--r--client/adivtab.h1058
-rw-r--r--client/anorms.h181
-rw-r--r--client/asm_i386.h81
-rw-r--r--client/block16.h123
-rw-r--r--client/block8.h124
-rw-r--r--client/cdaudio.h26
-rw-r--r--client/cl_cin.c650
-rw-r--r--client/cl_ents.c1500
-rw-r--r--client/cl_fx.c2298
-rw-r--r--client/cl_input.c542
-rw-r--r--client/cl_inv.c142
-rw-r--r--client/cl_main.c1844
-rw-r--r--client/cl_newfx.c1323
-rw-r--r--client/cl_parse.c806
-rw-r--r--client/cl_pred.c278
-rw-r--r--client/cl_scrn.c1401
-rw-r--r--client/cl_tent.c1745
-rw-r--r--client/cl_view.c584
-rw-r--r--client/client.h584
-rw-r--r--client/console.c682
-rw-r--r--client/console.h62
-rw-r--r--client/input.h34
-rw-r--r--client/keys.c943
-rw-r--r--client/keys.h146
-rw-r--r--client/menu.c4016
-rw-r--r--client/qmenu.c674
-rw-r--r--client/qmenu.h140
-rw-r--r--client/ref.h224
-rw-r--r--client/screen.h62
-rw-r--r--client/snd_dma.c1214
-rw-r--r--client/snd_loc.h164
-rw-r--r--client/snd_mem.c359
-rw-r--r--client/snd_mix.c497
-rw-r--r--client/sound.h45
-rw-r--r--client/vid.h42
-rw-r--r--client/x86.c95
-rw-r--r--ctf/2do.txt16
-rw-r--r--ctf/Makefile.Linux.i386159
-rw-r--r--ctf/ctf.0011009
-rw-r--r--ctf/ctf.def2
-rw-r--r--ctf/ctf.dsp1007
-rw-r--r--ctf/ctf.plg17
-rw-r--r--ctf/docs/admin.gifbin0 -> 11206 bytes
-rw-r--r--ctf/docs/adminset.gifbin0 -> 12303 bytes
-rw-r--r--ctf/docs/automac.gifbin0 -> 16924 bytes
-rw-r--r--ctf/docs/ghost.jpgbin0 -> 11088 bytes
-rw-r--r--ctf/docs/grapple.jpgbin0 -> 17631 bytes
-rw-r--r--ctf/docs/layout.jpgbin0 -> 36882 bytes
-rw-r--r--ctf/docs/mainctf_back.jpgbin0 -> 15335 bytes
-rw-r--r--ctf/docs/menu.gifbin0 -> 12559 bytes
-rw-r--r--ctf/docs/q2ctf.html1243
-rw-r--r--ctf/docs/say_team.gifbin0 -> 6717 bytes
-rw-r--r--ctf/docs/stats.jpgbin0 -> 5558 bytes
-rw-r--r--ctf/docs/tech1.gifbin0 -> 1375 bytes
-rw-r--r--ctf/docs/tech2.gifbin0 -> 1344 bytes
-rw-r--r--ctf/docs/tech3.gifbin0 -> 1595 bytes
-rw-r--r--ctf/docs/tech4.gifbin0 -> 1434 bytes
-rw-r--r--ctf/g_ai.c1117
-rw-r--r--ctf/g_chase.c157
-rw-r--r--ctf/g_cmds.c1066
-rw-r--r--ctf/g_combat.c596
-rw-r--r--ctf/g_ctf.c4016
-rw-r--r--ctf/g_ctf.h185
-rw-r--r--ctf/g_func.c2047
-rw-r--r--ctf/g_items.c2446
-rw-r--r--ctf/g_local.h1145
-rw-r--r--ctf/g_main.c427
-rw-r--r--ctf/g_misc.c1909
-rw-r--r--ctf/g_monster.c740
-rw-r--r--ctf/g_phys.c959
-rw-r--r--ctf/g_save.c743
-rw-r--r--ctf/g_spawn.c998
-rw-r--r--ctf/g_svcmds.c48
-rw-r--r--ctf/g_target.c809
-rw-r--r--ctf/g_trigger.c598
-rw-r--r--ctf/g_utils.c570
-rw-r--r--ctf/g_weapon.c919
-rw-r--r--ctf/game.h242
-rw-r--r--ctf/layout.txt12
-rw-r--r--ctf/m_move.c556
-rw-r--r--ctf/m_player.h225
-rw-r--r--ctf/p_client.c1726
-rw-r--r--ctf/p_hud.c544
-rw-r--r--ctf/p_menu.c256
-rw-r--r--ctf/p_menu.h49
-rw-r--r--ctf/p_trail.c146
-rw-r--r--ctf/p_view.c1135
-rw-r--r--ctf/p_weapon.c1469
-rw-r--r--ctf/q_shared.c1419
-rw-r--r--ctf/q_shared.h1200
-rw-r--r--game/g_ai.c1117
-rw-r--r--game/g_chase.c175
-rw-r--r--game/g_cmds.c992
-rw-r--r--game/g_combat.c576
-rw-r--r--game/g_func.c2048
-rw-r--r--game/g_items.c2216
-rw-r--r--game/g_local.h1113
-rw-r--r--game/g_main.c411
-rw-r--r--game/g_misc.c1876
-rw-r--r--game/g_monster.c740
-rw-r--r--game/g_phys.c961
-rw-r--r--game/g_save.c769
-rw-r--r--game/g_spawn.c984
-rw-r--r--game/g_svcmds.c300
-rw-r--r--game/g_target.c809
-rw-r--r--game/g_trigger.c598
-rw-r--r--game/g_turret.c432
-rw-r--r--game/g_utils.c568
-rw-r--r--game/g_weapon.c916
-rw-r--r--game/game.0011619
-rw-r--r--game/game.def2
-rw-r--r--game/game.dsp1618
-rw-r--r--game/game.h254
-rw-r--r--game/game.plg75
-rw-r--r--game/m_actor.c609
-rw-r--r--game/m_actor.h506
-rw-r--r--game/m_berserk.c457
-rw-r--r--game/m_berserk.h269
-rw-r--r--game/m_boss2.c679
-rw-r--r--game/m_boss2.h206
-rw-r--r--game/m_boss3.c76
-rw-r--r--game/m_boss31.c749
-rw-r--r--game/m_boss31.h213
-rw-r--r--game/m_boss32.c913
-rw-r--r--game/m_boss32.h516
-rw-r--r--game/m_brain.c676
-rw-r--r--game/m_brain.h247
-rw-r--r--game/m_chick.c677
-rw-r--r--game/m_chick.h313
-rw-r--r--game/m_flash.c488
-rw-r--r--game/m_flipper.c403
-rw-r--r--game/m_flipper.h185
-rw-r--r--game/m_float.c663
-rw-r--r--game/m_float.h273
-rw-r--r--game/m_flyer.c626
-rw-r--r--game/m_flyer.h182
-rw-r--r--game/m_gladiator.c387
-rw-r--r--game/m_gladiator.h115
-rw-r--r--game/m_gunner.c628
-rw-r--r--game/m_gunner.h234
-rw-r--r--game/m_hover.c620
-rw-r--r--game/m_hover.h230
-rw-r--r--game/m_infantry.c607
-rw-r--r--game/m_infantry.h232
-rw-r--r--game/m_insane.c693
-rw-r--r--game/m_insane.h307
-rw-r--r--game/m_medic.c769
-rw-r--r--game/m_medic.h262
-rw-r--r--game/m_move.c556
-rw-r--r--game/m_mutant.c663
-rw-r--r--game/m_mutant.h174
-rw-r--r--game/m_parasite.c552
-rw-r--r--game/m_parasite.h143
-rw-r--r--game/m_player.h224
-rw-r--r--game/m_rider.h66
-rw-r--r--game/m_soldier.c1299
-rw-r--r--game/m_soldier.h500
-rw-r--r--game/m_supertank.c717
-rw-r--r--game/m_supertank.h279
-rw-r--r--game/m_tank.c856
-rw-r--r--game/m_tank.h319
-rw-r--r--game/p_client.c1805
-rw-r--r--game/p_hud.c571
-rw-r--r--game/p_trail.c146
-rw-r--r--game/p_view.c1087
-rw-r--r--game/p_weapon.c1434
-rw-r--r--game/q_shared.c1418
-rw-r--r--game/q_shared.h1200
-rw-r--r--gnu.txt87
-rw-r--r--irix/cd_irix.c40
-rw-r--r--irix/glw_imp.c927
-rw-r--r--irix/q_shirix.c200
-rw-r--r--irix/qgl_irix.c3997
-rw-r--r--irix/snd_irix.c222
-rw-r--r--irix/sys_irix.c383
-rw-r--r--irix/vid_menu.c426
-rw-r--r--irix/vid_so.c492
-rw-r--r--joystick.txt226
-rw-r--r--linux/Makefile.AXP716
-rw-r--r--linux/Makefile.i3861085
-rw-r--r--linux/block16.h123
-rw-r--r--linux/block8.h124
-rw-r--r--linux/cd_linux.c420
-rw-r--r--linux/d_copy.s147
-rw-r--r--linux/d_ifacea.h79
-rw-r--r--linux/d_polysa.s1250
-rw-r--r--linux/gl_fxmesa.c206
-rw-r--r--linux/glob.c164
-rw-r--r--linux/glob.h1
-rw-r--r--linux/in_linux.c29
-rw-r--r--linux/math.s403
-rw-r--r--linux/net_udp.c537
-rw-r--r--linux/q_shlinux.c205
-rw-r--r--linux/qasm.h459
-rw-r--r--linux/qgl_linux.c3994
-rw-r--r--linux/r_aclipa.s195
-rw-r--r--linux/r_draw16.s1227
-rw-r--r--linux/r_drawa.s817
-rw-r--r--linux/r_edgea.s729
-rw-r--r--linux/r_scana.s68
-rw-r--r--linux/r_spr8.s879
-rw-r--r--linux/r_surf8.s762
-rw-r--r--linux/r_varsa.s223
-rw-r--r--linux/rw_in_svgalib.c374
-rw-r--r--linux/rw_linux.h16
-rw-r--r--linux/rw_svgalib.c311
-rw-r--r--linux/rw_x11.c1093
-rw-r--r--linux/snd_linux.c266
-rw-r--r--linux/snd_mixa.s193
-rw-r--r--linux/sys_dosa.s94
-rw-r--r--linux/sys_linux.c379
-rw-r--r--linux/vid_menu.c437
-rw-r--r--linux/vid_so.c489
-rw-r--r--makefile259
-rw-r--r--makezip2
-rw-r--r--makezip.bat1
-rw-r--r--null/cd_null.c31
-rw-r--r--null/cl_null.c55
-rw-r--r--null/glimp_null.c34
-rw-r--r--null/in_null.c36
-rw-r--r--null/snddma_null.c28
-rw-r--r--null/swimp_null.c30
-rw-r--r--null/sys_null.c127
-rw-r--r--null/vid_null.c145
-rw-r--r--qcommon/cmd.c892
-rw-r--r--qcommon/cmodel.c1770
-rw-r--r--qcommon/common.c1588
-rw-r--r--qcommon/crc.c92
-rw-r--r--qcommon/crc.h6
-rw-r--r--qcommon/cvar.c527
-rw-r--r--qcommon/files.c877
-rw-r--r--qcommon/md4.c278
-rw-r--r--qcommon/net_chan.c387
-rw-r--r--qcommon/pmove.c1360
-rw-r--r--qcommon/qcommon.h826
-rw-r--r--qcommon/qfiles.h482
-rw-r--r--quake2.0012061
-rw-r--r--quake2.bcebin0 -> 92816 bytes
-rw-r--r--quake2.bcpbin0 -> 1161 bytes
-rw-r--r--quake2.dsp2050
-rw-r--r--quake2.dsw77
-rw-r--r--quake2.mak4593
-rw-r--r--quake2.optbin0 -> 130560 bytes
-rw-r--r--quake2.plg715
-rw-r--r--readme.txt29
-rw-r--r--ref_gl/anorms.h181
-rw-r--r--ref_gl/anormtab.h37
-rw-r--r--ref_gl/gl_draw.c415
-rw-r--r--ref_gl/gl_image.c1571
-rw-r--r--ref_gl/gl_light.c729
-rw-r--r--ref_gl/gl_local.h460
-rw-r--r--ref_gl/gl_mesh.c839
-rw-r--r--ref_gl/gl_model.c1223
-rw-r--r--ref_gl/gl_model.h261
-rw-r--r--ref_gl/gl_rmain.c1692
-rw-r--r--ref_gl/gl_rmisc.c246
-rw-r--r--ref_gl/gl_rsurf.c1660
-rw-r--r--ref_gl/gl_warp.c662
-rw-r--r--ref_gl/qgl.h442
-rw-r--r--ref_gl/ref_gl.001752
-rw-r--r--ref_gl/ref_gl.def2
-rw-r--r--ref_gl/ref_gl.dsp773
-rw-r--r--ref_gl/ref_gl.plg17
-rw-r--r--ref_gl/warpsin.h51
-rw-r--r--ref_soft/adivtab.h1077
-rw-r--r--ref_soft/anorms.h181
-rw-r--r--ref_soft/asm_draw.h121
-rw-r--r--ref_soft/block16.inc116
-rw-r--r--ref_soft/block8.inc116
-rw-r--r--ref_soft/d_if.inc81
-rw-r--r--ref_soft/d_ifacea.h76
-rw-r--r--ref_soft/qasm.inc435
-rw-r--r--ref_soft/r_aclip.c323
-rw-r--r--ref_soft/r_aclipa.asm200
-rw-r--r--ref_soft/r_alias.c1198
-rw-r--r--ref_soft/r_bsp.c637
-rw-r--r--ref_soft/r_draw.c445
-rw-r--r--ref_soft/r_draw16.asm1234
-rw-r--r--ref_soft/r_drawa.asm822
-rw-r--r--ref_soft/r_edge.c1125
-rw-r--r--ref_soft/r_edgea.asm733
-rw-r--r--ref_soft/r_image.c617
-rw-r--r--ref_soft/r_light.c442
-rw-r--r--ref_soft/r_local.h849
-rw-r--r--ref_soft/r_main.c1422
-rw-r--r--ref_soft/r_misc.c670
-rw-r--r--ref_soft/r_model.c1241
-rw-r--r--ref_soft/r_model.h256
-rw-r--r--ref_soft/r_part.c638
-rw-r--r--ref_soft/r_poly.c1244
-rw-r--r--ref_soft/r_polysa.asm812
-rw-r--r--ref_soft/r_polyse.c1539
-rw-r--r--ref_soft/r_rast.c852
-rw-r--r--ref_soft/r_scan.c591
-rw-r--r--ref_soft/r_scana.asm73
-rw-r--r--ref_soft/r_spr8.asm884
-rw-r--r--ref_soft/r_sprite.c123
-rw-r--r--ref_soft/r_surf.c651
-rw-r--r--ref_soft/r_surf8.asm771
-rw-r--r--ref_soft/r_varsa.asm220
-rw-r--r--ref_soft/rand1k.h123
-rw-r--r--ref_soft/ref_soft.0011498
-rw-r--r--ref_soft/ref_soft.def2
-rw-r--r--ref_soft/ref_soft.dsp1496
-rw-r--r--ref_soft/ref_soft.plg17
-rw-r--r--rhapsody/in_next.m332
-rw-r--r--rhapsody/makefile.bak63
-rw-r--r--rhapsody/notes.txt34
-rw-r--r--rhapsody/pb.project17
-rw-r--r--rhapsody/quake2.iconheader2
-rw-r--r--rhapsody/quake2.tiffbin0 -> 9410 bytes
-rw-r--r--rhapsody/r_next.m735
-rw-r--r--rhapsody/rhapqw.txt36
-rw-r--r--rhapsody/snd_next.m2151
-rw-r--r--rhapsody/swimp_rhap.m580
-rw-r--r--rhapsody/sys_rhap.m338
-rw-r--r--rhapsody/vid_next.m1789
-rw-r--r--server/server.h341
-rw-r--r--server/sv_ccmds.c1050
-rw-r--r--server/sv_ents.c727
-rw-r--r--server/sv_game.c396
-rw-r--r--server/sv_init.c465
-rw-r--r--server/sv_main.c1055
-rw-r--r--server/sv_null.c15
-rw-r--r--server/sv_send.c567
-rw-r--r--server/sv_user.c664
-rw-r--r--server/sv_world.c659
-rw-r--r--solaris/Makefile.OLD478
-rw-r--r--solaris/Makefile.Solaris719
-rw-r--r--solaris/g_so.c3
-rw-r--r--solaris/glob.c164
-rw-r--r--solaris/glob.h1
-rw-r--r--solaris/net_udp.c537
-rw-r--r--solaris/q_shsolaris.c196
-rw-r--r--solaris/sys_solaris.c337
-rw-r--r--unix/makefile308
-rw-r--r--unix/makefile_old317
-rw-r--r--unix/next/sv_ccmds.obin0 -> 60812 bytes
-rw-r--r--win32/cd_win.c510
-rw-r--r--win32/conproc.c431
-rw-r--r--win32/conproc.h24
-rw-r--r--win32/glw_imp.c616
-rw-r--r--win32/glw_win.h47
-rw-r--r--win32/in_win.c889
-rw-r--r--win32/net_wins.c842
-rw-r--r--win32/q2.apsbin0 -> 31508 bytes
-rw-r--r--win32/q2.icobin0 -> 766 bytes
-rw-r--r--win32/q2.rc72
-rw-r--r--win32/q_shwin.c215
-rw-r--r--win32/qe3.icobin0 -> 766 bytes
-rw-r--r--win32/qgl_win.c4133
-rw-r--r--win32/resource.h16
-rw-r--r--win32/rw_ddraw.c556
-rw-r--r--win32/rw_dib.c375
-rw-r--r--win32/rw_imp.c471
-rw-r--r--win32/rw_win.h68
-rw-r--r--win32/snd_win.c861
-rw-r--r--win32/sys_win.c663
-rw-r--r--win32/vid_dll.c760
-rw-r--r--win32/vid_menu.c473
-rw-r--r--win32/winquake.apsbin0 -> 32116 bytes
-rw-r--r--win32/winquake.h44
-rw-r--r--win32/winquake.rc98
371 files changed, 219621 insertions, 0 deletions
diff --git a/3.15_Changes.txt b/3.15_Changes.txt
new file mode 100644
index 0000000..df36789
--- /dev/null
+++ b/3.15_Changes.txt
@@ -0,0 +1,107 @@
+
+Quake2 3.15 Upgrade
+-------------------
+
+This upgrade addresses several features, including security, playability, and
+enhancements.
+
+A new map is also included (in baseq2\pak3.pak) called match1, Reckless
+Abandon. This map is designed for one on one deathmatch play. It was built
+by American McGee and Dave "Zoid" Kirsch.
+
+This patch replaces the following files:
+
+ quake2.exe
+ 3dfxgl.dll
+ pvrgl.dll
+ ref_gl.dll
+ ref_soft.dll
+ baseq2\gamex86.dll
+
+Changes
+-------
+
+- Added visible weapons support. This is precached with a special symbol, i.e.
+ gi.modelindex("#w_shotgun.md2") which causes the client to autobind it to
+ the players current weapon model. Plug in player models can optionally
+ support the visible weapons. Any that do not support it will use their
+ default weapon.md2 files automatically.
+ Visible weapons files for plug in player models are not downloaded
+ automatically--only the default weapon.md2 (and skin) is.
+ The Visible weapon models themselves are not included. They can be
+ downloaded from http://www.telefragged.com/vwep/
+- Rewrote the some of the net code to use optimized network packets for
+ projectiles. This is transparent to the game code, but improves netplay
+ substancially. The hyperblaster doesn't flood modem players anymore.
+- Rewrote the packet checksum code to be more portable and defeat proxy bots
+ yet again.
+- Autodownload support is in. The following items will be automatcally
+ downloaded as needed:
+ - world map (and textures)
+ - models
+ - sounds (precached ones)
+ - plug in player model, skin, skin_i and weapon.md2
+ downloads go to a temp file (maps/blah.tmp for example) and get renamed
+ when done. autoresume is supported (if you lose connect during the
+ download, just reconnect and resume). Server has fine control over
+ the downloads with the following new cvars:
+ allow_download - global download on/off
+ allow_download_players - players download on/off
+ allow_download_models - models download on/off
+ allow_download_sounds - sounds download on/off
+ allow_download_maps - maps download on/off
+ maps that are in pak files will _not_ autodownload from the server, this
+ is for copyright considerations.
+ The QuakeWorld bug of the server map changing while download a map has
+ been fixed.
+- New option in the Multiplayer/Player Setup menu for setting your connection
+ speed. This sets a default rate for the player and can improve net
+ performance for modem connections.
+- Rewrote some of the save game code to make it more portable. I wanted to
+ completely rewrite the entire save game system and make it portable across
+ versions and operating systems, but this would require an enormous amount
+ of work.
+- Added another 512 configure strings for general usage for mod makers.
+ This gives lots of room for general string displays on the HUD and in other
+ data.
+- Player movement code re-written to be similiar to that of NetQuake and
+ later versions of QuakeWorld. Player has more control in the air and
+ gets a boost in vertical speed when jumping off the top of ramps.
+- Fixed up serverrecord so that it works correctly with the later versions.
+ serverrecord lets the server do a recording of the current game that
+ demo editors can use to make demos from any PVS in the level. Server
+ recorded demos are BIG. Will look at using delta compression in them
+ to cut down the size.
+- Copy protection CD check has been removed.
+- Quake2 3.15 has changed the protocol (so old servers will not run) but
+ all existing game dlls can run on the new version (albiet without the
+ new features such as visible weapons).
+- Added flood protection. Controlled from the following cvars:
+ flood_msgs - maximum number of messages allowed in the time period
+ specified by flood_persecond
+ flood_persecond - time period that a maximum of flood_msgs messages are
+ permitted
+ flood_waitdelay - amount of time a client gets muzzled for flooding
+- fixed it so blaster/hyperblaster shots aren't treated as solid when
+ predicting--you aren't clipped against them now.
+- gender support is now in. The userinfo cvar "gender" can be set to
+ male/female/none (none for neutral messages). This doesn't affect sounds
+ but does affect death messages in the game. The models male and cyborg
+ default to gender male, and female and crackhor default to female.
+ Everything else defaults to none, but you can set it by typing
+ "gender male" or "gender female" as appropriate.
+- IP banning support ala QW. It's built into the game dll as 'sv' console
+ commands. This list is:
+ sv addip <ip-mask> - adds an ip to the ban list
+ sv listip <ip-mask> - removes an ip from the ban list
+ sv writeip - writes the ban list to <gamedir>/listip.cfg. You can
+ exec this on a server load to load the list on subsequent server runs.
+ like so: quake2 +set dedicated 1 +exec listip.cfg
+ sv removeip <ip-mask> - remove an ip from the list
+ the ip list is a simple mask system. Adding 192.168 to the list
+ would block out everyone in the 192.168.*.* net block. You get 1024 bans,
+ if you need more, recompile the game dll. :)
+ A new cvar is also supported called 'filterban'. It defaults to one which
+ means "allow everyone to connect _except_ those matching in the ban list."
+ If you set it to zero, the meaning reverses like so, "don't allow anyone
+ to connect unless they are in the list."
diff --git a/3.16_Changes.txt b/3.16_Changes.txt
new file mode 100644
index 0000000..e99ebdd
--- /dev/null
+++ b/3.16_Changes.txt
@@ -0,0 +1,127 @@
+
+Quake2 3.16 Upgrade
+-------------------
+
+This upgrade addresses several features, including security, playability, and
+enhancements.
+
+A new map is also included (in baseq2\pak3.pak) called match1, Reckless
+Abandon. This map is designed for one on one deathmatch play. It was built
+by American McGee and Dave "Zoid" Kirsch.
+
+Changes for 3.16
+----------------
+
+- Fixed infinite grenade bug
+- Fixed autodownloading to actually download sounds and console pics
+- Fixed autodownload to not create empty directories for files not on
+ the server.
+- Added customized client downloading. cvars are the same as the server side:
+ allow_download - global download on/off
+ allow_download_players - players download on/off
+ allow_download_models - models download on/off
+ allow_download_sounds - sounds download on/off
+ allow_download_maps - maps download on/off
+ They can also be (more easily) set with a new Download Options menu
+ accessible in Multiplayer/Player Setup/Download Options
+- Changed checksumming code to be more portable and faster.
+ The checksum in 3.15 was seriously broken.
+ This change makes 3.16 incompatible with previous servers.
+- Fixed it so sounds played for PPMs that default to male are only checked
+ on disk once.
+- Fixed player 'warping' present in 3.15 (this was an artifact of the
+ hyperblaster optimizations).
+- Fixed the autodownload in 3.15 so that stuff like skins for models are
+ downloaded as well as pics.
+
+Changes for 3.15
+----------------
+
+- Added visible weapons support. This is precached with a special symbol, i.e.
+ gi.modelindex("#w_shotgun.md2") which causes the client to autobind it to
+ the players current weapon model. Plug in player models can optionally
+ support the visible weapons. Any that do not support it will use their
+ default weapon.md2 files automatically.
+ Visible weapons files for plug in player models are not downloaded
+ automatically--only the default weapon.md2 (and skin) is.
+ The Visible weapon models themselves are not included. They can be
+ downloaded from http://www.telefragged.com/vwep/
+- Rewrote the some of the net code to use optimized network packets for
+ projectiles. This is transparent to the game code, but improves netplay
+ substancially. The hyperblaster doesn't flood modem players anymore.
+- Rewrote the packet checksum code to be more portable and defeat proxy bots
+ yet again.
+- Autodownload support is in. The following items will be automatcally
+ downloaded as needed:
+ - world map (and textures)
+ - models
+ - sounds (precached ones)
+ - plug in player model, skin, skin_i and weapon.md2
+ downloads go to a temp file (maps/blah.tmp for example) and get renamed
+ when done. autoresume is supported (if you lose connect during the
+ download, just reconnect and resume). Server has fine control over
+ the downloads with the following new cvars:
+ allow_download - global download on/off
+ allow_download_players - players download on/off
+ allow_download_models - models download on/off
+ allow_download_sounds - sounds download on/off
+ allow_download_maps - maps download on/off
+ maps that are in pak files will _not_ autodownload from the server, this
+ is for copyright considerations.
+ The QuakeWorld bug of the server map changing while download a map has
+ been fixed.
+- New option in the Multiplayer/Player Setup menu for setting your connection
+ speed. This sets a default rate for the player and can improve net
+ performance for modem connections.
+- Rewrote some of the save game code to make it more portable. I wanted to
+ completely rewrite the entire save game system and make it portable across
+ versions and operating systems, but this would require an enormous amount
+ of work.
+- Added another 512 configure strings for general usage for mod makers.
+ This gives lots of room for general string displays on the HUD and in other
+ data.
+- Player movement code re-written to be similiar to that of NetQuake and
+ later versions of QuakeWorld. Player has more control in the air and
+ gets a boost in vertical speed when jumping off the top of ramps.
+- Fixed up serverrecord so that it works correctly with the later versions.
+ serverrecord lets the server do a recording of the current game that
+ demo editors can use to make demos from any PVS in the level. Server
+ recorded demos are BIG. Will look at using delta compression in them
+ to cut down the size.
+- Copy protection CD check has been removed.
+- Quake2 3.15 has changed the protocol (so old servers will not run) but
+ all existing game dlls can run on the new version (albiet without the
+ new features such as visible weapons).
+- Added flood protection. Controlled from the following cvars:
+ flood_msgs - maximum number of messages allowed in the time period
+ specified by flood_persecond
+ flood_persecond - time period that a maximum of flood_msgs messages are
+ permitted
+ flood_waitdelay - amount of time a client gets muzzled for flooding
+ (gamex86 DLL specific)
+- fixed it so blaster/hyperblaster shots aren't treated as solid when
+ predicting--you aren't clipped against them now.
+ (gamex86 DLL specific, the SVF_DEADMONSTER flag is set on projectiles)
+- gender support is now in. The userinfo cvar "gender" can be set to
+ male/female/none (none for neutral messages). This doesn't affect sounds
+ but does affect death messages in the game. The models male and cyborg
+ default to gender male, and female and crackhor default to female.
+ Everything else defaults to none, but you can set it by typing
+ "gender male" or "gender female" as appropriate.
+- IP banning support ala QW. It's built into the game dll as 'sv' console
+ commands. This list is:
+ sv addip <ip-mask> - adds an ip to the ban list
+ sv listip <ip-mask> - removes an ip from the ban list
+ sv writeip - writes the ban list to <gamedir>/listip.cfg. You can
+ exec this on a server load to load the list on subsequent server runs.
+ like so: quake2 +set dedicated 1 +exec listip.cfg
+ sv removeip <ip-mask> - remove an ip from the list
+ the ip list is a simple mask system. Adding 192.168 to the list
+ would block out everyone in the 192.168.*.* net block. You get 1024 bans,
+ if you need more, recompile the game dll. :)
+ A new cvar is also supported called 'filterban'. It defaults to one which
+ means "allow everyone to connect _except_ those matching in the ban list."
+ If you set it to zero, the meaning reverses like so, "don't allow anyone
+ to connect unless they are in the list."
+ (gamex86 DLL specific)
+
diff --git a/3.17_Changes.txt b/3.17_Changes.txt
new file mode 100644
index 0000000..dd0e8a4
--- /dev/null
+++ b/3.17_Changes.txt
@@ -0,0 +1,158 @@
+
+Quake2 3.17 Upgrade
+-------------------
+
+This upgrade addresses several features, including security, playability, and
+enhancements.
+
+Changes for 3.17
+----------------
+
+- Fixed possible NAN resulting from handing zero to second arg of atan2
+- Autodownloading is now DISABLED by DEFAULT. It must be enabled by typing
+ 'allow_download 1' at the console, or using the download options menu
+ in Multiplayer/PlayerSetup/Download Options
+- Server demos now include a svc_serverdata block at the beginning with the
+ attractloop byte set to '2' to indicate server demo (byte before gamedir
+ in the svc_serverdata block). This allows easy identification of
+ serverrecorded demos (serverrecord demos are only for demo editors, they
+ can not be played back in Quake2 without being first edited).
+- New options for setting texture formats in ref_gl:
+ gl_texturealphamode: default, GL_RGBA, GL_RGBA8, GL_RGB5_A1, GL_RGBA4,
+ GL_RGBA2
+ gl_texturesolidmode: default, GL_RGB, GL_RGB8, GL_RGB5, GL_RGB4,
+ GL_R3_G3_B2, GL_RGB2 (SGI only)
+- Player movement during Air acceleration changed to reflect more real-world
+ physics while airborne.
+- Fixed a bug when riding trains that caused drift in a southwest direction
+ (Thanks to Jim Dose at Ritual for pointing this one out).
+- Linux: Now correctly reports out of memory rather than segfaulting (mmap
+ returns (void *)-1 and not NULL on error).
+- Fixed autodownloading to not create paths for files that can't be downloaded
+ (this was creating many empty directories in baseq2/players).
+- When downloading a file from a server that doesn't have it, the message is
+ now "Server does not have this file" rather than "File not found."
+- Fixed some coop keys in 3.15 weren't being handled correctly (pyramid key).
+- Highbits are now stripped from console when using condump
+- Restored support for gl_modulate in multiplayer play
+- Fixed it so that players with a model/skin you don't have aren't checked for
+ on disk more than once.
+- Fixed it so sounds played for PPMs that default to male are only checked
+ on disk once.
+- Byte ordering/portability fixes in cinematics, PCX and other file handling.
+- Client state during static image cinematic (PCX image) so that client can
+ continue to next unit.
+- Fixed it so that dedicated coop servers no longer get stuck at victory.pcx,
+ if a server is in coop mode, hitting a button at the victory.pcx screen
+ while cause the server to restart at base1
+- Fixed infinite grenade bug
+- Fixed autodownloading to actually download sounds and console pics
+- Fixed autodownload to not create empty directories for files not on
+ the server.
+- Added customized client downloading. cvars are the same as the server side:
+ allow_download - global download on/off
+ allow_download_players - players download on/off
+ allow_download_models - models download on/off
+ allow_download_sounds - sounds download on/off
+ allow_download_maps - maps download on/off
+ They can also be (more easily) set with a new Download Options menu
+ accessible in Multiplayer/Player Setup/Download Options
+- Changed checksumming code to be more portable and faster.
+ The checksum in 3.15 was seriously broken.
+ This change makes 3.17 incompatible with previous servers.
+- Fixed player 'warping' present in 3.15 (this was an artifact of the
+ hyperblaster optimizations).
+- Fixed the autodownload in 3.15 so that stuff like skins for models are
+ downloaded as well as pics.
+
+Changes for 3.15
+----------------
+
+- Added visible weapons support. This is precached with a special symbol, i.e.
+ gi.modelindex("#w_shotgun.md2") which causes the client to autobind it to
+ the players current weapon model. Plug in player models can optionally
+ support the visible weapons. Any that do not support it will use their
+ default weapon.md2 files automatically.
+ Visible weapons files for plug in player models are not downloaded
+ automatically--only the default weapon.md2 (and skin) is.
+ The Visible weapon models themselves are not included. They can be
+ downloaded from http://www.telefragged.com/vwep/
+- Rewrote the some of the net code to use optimized network packets for
+ projectiles. This is transparent to the game code, but improves netplay
+ substancially. The hyperblaster doesn't flood modem players anymore.
+- Rewrote the packet checksum code to be more portable and defeat proxy bots
+ yet again.
+- Autodownload support is in. The following items will be automatcally
+ downloaded as needed:
+ - world map (and textures)
+ - models
+ - sounds (precached ones)
+ - plug in player model, skin, skin_i and weapon.md2
+ downloads go to a temp file (maps/blah.tmp for example) and get renamed
+ when done. autoresume is supported (if you lose connect during the
+ download, just reconnect and resume). Server has fine control over
+ the downloads with the following new cvars:
+ allow_download - global download on/off
+ allow_download_players - players download on/off
+ allow_download_models - models download on/off
+ allow_download_sounds - sounds download on/off
+ allow_download_maps - maps download on/off
+ maps that are in pak files will _not_ autodownload from the server, this
+ is for copyright considerations.
+ The QuakeWorld bug of the server map changing while download a map has
+ been fixed.
+- New option in the Multiplayer/Player Setup menu for setting your connection
+ speed. This sets a default rate for the player and can improve net
+ performance for modem connections.
+- Rewrote some of the save game code to make it more portable. I wanted to
+ completely rewrite the entire save game system and make it portable across
+ versions and operating systems, but this would require an enormous amount
+ of work.
+- Added another 512 configure strings for general usage for mod makers.
+ This gives lots of room for general string displays on the HUD and in other
+ data.
+- Player movement code re-written to be similiar to that of NetQuake and
+ later versions of QuakeWorld. Player has more control in the air and
+ gets a boost in vertical speed when jumping off the top of ramps.
+- Fixed up serverrecord so that it works correctly with the later versions.
+ serverrecord lets the server do a recording of the current game that
+ demo editors can use to make demos from any PVS in the level. Server
+ recorded demos are BIG. Will look at using delta compression in them
+ to cut down the size.
+- Copy protection CD check has been removed.
+- Quake2 3.15 has changed the protocol (so old servers will not run) but
+ all existing game dlls can run on the new version (albiet without the
+ new features such as visible weapons).
+- Added flood protection. Controlled from the following cvars:
+ flood_msgs - maximum number of messages allowed in the time period
+ specified by flood_persecond
+ flood_persecond - time period that a maximum of flood_msgs messages are
+ permitted
+ flood_waitdelay - amount of time a client gets muzzled for flooding
+ (gamex86 DLL specific)
+- fixed it so blaster/hyperblaster shots aren't treated as solid when
+ predicting--you aren't clipped against them now.
+ (gamex86 DLL specific, the SVF_DEADMONSTER flag is set on projectiles)
+- gender support is now in. The userinfo cvar "gender" can be set to
+ male/female/none (none for neutral messages). This doesn't affect sounds
+ but does affect death messages in the game. The models male and cyborg
+ default to gender male, and female and crackhor default to female.
+ Everything else defaults to none, but you can set it by typing
+ "gender male" or "gender female" as appropriate.
+- IP banning support ala QW. It's built into the game dll as 'sv' console
+ commands. This list is:
+ sv addip <ip-mask> - adds an ip to the ban list
+ sv listip <ip-mask> - removes an ip from the ban list
+ sv writeip - writes the ban list to <gamedir>/listip.cfg. You can
+ exec this on a server load to load the list on subsequent server runs.
+ like so: quake2 +set dedicated 1 +exec listip.cfg
+ sv removeip <ip-mask> - remove an ip from the list
+ the ip list is a simple mask system. Adding 192.168 to the list
+ would block out everyone in the 192.168.*.* net block. You get 1024 bans,
+ if you need more, recompile the game dll. :)
+ A new cvar is also supported called 'filterban'. It defaults to one which
+ means "allow everyone to connect _except_ those matching in the ban list."
+ If you set it to zero, the meaning reverses like so, "don't allow anyone
+ to connect unless they are in the list."
+ (gamex86 DLL specific)
+
diff --git a/3.18_changes.txt b/3.18_changes.txt
new file mode 100644
index 0000000..a7b014e
--- /dev/null
+++ b/3.18_changes.txt
@@ -0,0 +1,53 @@
+3.18 Changes
+
+- "Water surfing" that was present in 3.17 has been fixed (holding jump while
+ on the surface of water let you swim at full speed).
+- Environment maps (env) are now autodownloaded (if allow_download_maps is set).
+- Spectator support added. A new cvar is built into the client, "spectator"
+ Setting it to value other than "0" will allow you join a game as a spectator.
+ While in spectator mode, you can press the attack button to enter a chasecam
+ mode and follow other players. Using the inventory keys (by default the
+ left and right square brackets) you can switch between players in the game
+ while using the chasecam.
+ You may enter and leave spectator mode while connected. Doing so resets
+ your score to zero.
+ ***The new spectator support requires a new game.dll and may not work for
+ user mods until they update their code. The default game.dll that comes
+ with 3.18 supports chasecam as well as the new included Xatrix game.dll.
+- Fixed it so that when a model defaults to male/grunt (don't have the
+ necessary model or skin for the player), VWep support is still enabled.
+- New console command for players, "playerlist". This will cause the server
+ to give you a text list of the players on the server, including their
+ connect time, score, ping and spectator status. This is handy if not
+ everyone fits on the scoreboard on busy servers.
+- New cvar for the game.dll: spectator_password. If set to a value (other
+ than "none"), users must set their spectator variable to this value in order
+ to join the server as a spectator. This password is independant of the
+ normal user password.
+- New cvar for the game.dll: maxspectators (defaults to 4). This value is
+ not seperate from maxclients (a spectator is still a client).
+- New cvar for the game.dll: sv_maplist. This can be set to a list of map
+ names that the server should autorotate through, rather than using the
+ nextmap set in the actual map files themselves.
+ For example: set sv_maplist "base1 q2dm1 q2dm3 fact3" will cause the server
+ to rotate through those maps.
+ ***This requires a game.dll update and will not work with user mods until
+ they update their code.
+- A new facility has been added to ClientConnect() in the game.dll to allow
+ the game.dll to pass a message back to the user for the reason of disallowing
+ a connection. It is done by setting a key of "rejmsg" in the passed userinfo.
+ For example:
+ Info_SetValueforKey(userinfo, "rejmsg", "Password required or incorrect.");
+- The server cvar, password, may be set to "none" to clear the password. This
+ is needed because rcon can not set a blank password.
+- New server cvar: sv_airaccelerate. This controls the optional air
+ acceleration facility. The default value is 0, which disables air control.
+ The usual value to replicate the air control seen in the original Quake and
+ later versions of Quakeworld is 10. 10 allows for much more
+ air control (as was seen in 3.15). This value is ignored in single player
+ and coop.
+- Fixed NoSuchFrame/BAD_MODELTYPE errors when doing a vid_restart while
+ connected.
+- NoSuchFrame errors now include model name to assist in debugging user mods.
+- Fixed the remote status query response (ServerInfo) to not include error
+ messages and be more consistent.
diff --git a/baseq2/config.cfg b/baseq2/config.cfg
new file mode 100644
index 0000000..3e718de
--- /dev/null
+++ b/baseq2/config.cfg
@@ -0,0 +1,150 @@
+// generated by quake, do not modify
+bind TAB "inven"
+bind ENTER "invuse"
+bind ESCAPE "togglemenu"
+bind SPACE "+moveup"
+bind ' "inven_drop"
+bind + "sizeup"
+bind , "+moveleft"
+bind - "sizedown"
+bind . "+moveright"
+bind / "weapnext"
+bind 0 "use BFG10K"
+bind 1 "use Blaster"
+bind 2 "use Shotgun"
+bind 3 "use Super Shotgun"
+bind 4 "use Machinegun"
+bind 5 "use Chaingun"
+bind 6 "use Grenade Launcher"
+bind 7 "use Rocket Launcher"
+bind 8 "use HyperBlaster"
+bind 9 "use Railgun"
+bind = "sizeup"
+bind [ "invprev"
+bind \ "+mlook"
+bind ] "invnext"
+bind ` "toggleconsole"
+bind a "+moveleft"
+bind b "use rebreather"
+bind c "+movedown"
+bind d "+moveright"
+bind e "weapnext"
+bind g "use grenades"
+bind h "wave 0"
+bind i "use invulnerability"
+bind j "wave 1"
+bind k "wave 2"
+bind l "wave 3"
+bind p "use shield"
+bind q "invprev"
+bind r "invuse"
+bind s "+back"
+bind t "messagemode"
+bind u "wave 4"
+bind w "+forward"
+bind x "centerview"
+bind z "+movedown"
+bind ~ "toggleconsole"
+bind BACKSPACE "invdrop"
+bind UPARROW "+forward"
+bind DOWNARROW "+back"
+bind LEFTARROW "+left"
+bind RIGHTARROW "+right"
+bind ALT "+strafe"
+bind CTRL "+attack"
+bind SHIFT "+speed"
+bind F1 "cmd help"
+bind F2 "menu_savegame"
+bind F3 "menu_loadgame"
+bind F4 "give ammo"
+bind F5 "give weapons"
+bind F6 "r_speeds 0"
+bind F7 "r_speeds 1"
+bind F8 "notarget"
+bind F9 "noclip"
+bind F10 "god"
+bind F11 "screenshot"
+bind F12 "quit"
+bind INS "+klook"
+bind DEL "+lookdown"
+bind PGDN "+lookup"
+bind PGUP "+lookup"
+bind END "centerview"
+bind MOUSE1 "+attack"
+bind MOUSE2 "+strafe"
+bind MOUSE3 "+mlook"
+bind PAUSE "pause"
+set gl_3dlabs_broken "1"
+set gl_swapinterval "1"
+set gl_ext_compiled_vertex_array "1"
+set gl_ext_pointparameters "1"
+set gl_ext_multitexture "1"
+set gl_ext_palettedtexture "1"
+set gl_ext_swapinterval "1"
+set gl_vertex_arrays "0"
+set gl_texturesolidmode "default"
+set gl_texturealphamode "default"
+set gl_texturemode "GL_LINEAR_MIPMAP_NEAREST"
+set gl_driver "opengl32"
+set gl_finish "0"
+set gl_shadows "0"
+set gl_mode "3"
+set gl_modulate "1"
+set gl_particle_att_c "0.01"
+set gl_particle_att_b "0.0"
+set gl_particle_att_a "0.01"
+set gl_particle_size "40"
+set gl_particle_max_size "40"
+set gl_particle_min_size "2"
+set g_select_empty "0"
+set in_joystick "0"
+set in_mouse "1"
+set cl_vwep "1"
+set gender_auto "1"
+set gender "male"
+set fov "90"
+set msg "1"
+set rate "25000"
+set freelook "0"
+set cl_stereo_separation "0.4"
+set adr8 ""
+set adr7 ""
+set adr6 ""
+set adr5 ""
+set adr4 ""
+set adr3 ""
+set adr2 ""
+set adr1 ""
+set adr0 ""
+set cd_nocd "0"
+set s_primary "0"
+set s_mixahead "0.2"
+set s_loadas8bit "1"
+set s_khz "11"
+set s_volume "0.7"
+set sw_mode "0"
+set sw_stipplealpha "0"
+set sw_allow_modex "1"
+set vid_gamma "1"
+set vid_ypos "32"
+set vid_xpos "115"
+set vid_ref "gl"
+set sv_reconnect_limit "3"
+set allow_download_maps "1"
+set allow_download_sounds "1"
+set allow_download_models "1"
+set allow_download_players "0"
+set allow_download "0"
+set hostname "noname"
+set skin "male/grunt"
+set name "hook"
+set lookstrafe "0"
+set lookspring "1"
+set m_pitch "-0.022000"
+set hand "2"
+set cl_run "0"
+set crosshair "1"
+set sensitivity "9.000000"
+set win_noalttab "0"
+set vid_fullscreen "0"
+set viewsize "100"
diff --git a/baseq2/save/save0/game.ssv b/baseq2/save/save0/game.ssv
new file mode 100644
index 0000000..f1569a3
--- /dev/null
+++ b/baseq2/save/save0/game.ssv
Binary files differ
diff --git a/baseq2/save/save0/server.ssv b/baseq2/save/save0/server.ssv
new file mode 100644
index 0000000..ba337c7
--- /dev/null
+++ b/baseq2/save/save0/server.ssv
Binary files differ
diff --git a/changes.txt b/changes.txt
new file mode 100644
index 0000000..a0940b8
--- /dev/null
+++ b/changes.txt
@@ -0,0 +1,166 @@
+
+Quake2 3.16 changes:
+
+- Fixed infinite grenade bug
+- Fixed autodownloading to actually download sounds and console pics
+- Fixed autodownload to not create empty directories for files not on
+ the server.
+- Added customized client downloading. cvars are the same as the server side:
+ allow_download - global download on/off
+ allow_download_players - players download on/off
+ allow_download_models - models download on/off
+ allow_download_sounds - sounds download on/off
+ allow_download_maps - maps download on/off
+ They can also be (more easily) set with a new Download Options menu
+ accessible in Multiplayer/Player Setup/Download Options
+- Changed checksumming code to be more portable and faster.
+
+
+Quake2 3.15 changes:
+
+- Added visible weapons support. This is precached with a special symbol, i.e.
+ gi.modelindex("#w_shotgun.md2") which causes the client to autobind it to
+ the players current weapon model. Plug in player models can optionally
+ support the visible weapons. Any that do not support it will use their
+ default weapon.md2 files automatically.
+ Visible weapons files for plug in player models are not downloaded
+ automatically--only the default weapon.md2 (and skin) is.
+- New cvar cl_vwep controls whether visible weapons is enabled on the client.
+ If you turn it off, the visible weapons models are not loaded. This can offer
+ a speed up on slow or memory starved machines.
+- Rewrote the some of the net code to use optimized network packets for
+ projectiles. This is transparent to the game code, but improves netplay
+ substancially. The hyperblaster doesn't flood modem players anymore.
+- Rewrote the packet checksum code to be more portable and defeat proxy bots
+ yet again.
+- Autodownload support is in. The following items will be automatcally
+ downloaded as needed:
+ - world map (and textures)
+ - models
+ - sounds (precached ones)
+ - plug in player model, skin, skin_i and weapon.md2
+ downloads go to a temp file (maps/blah.tmp for example) and get renamed
+ when done. autoresume is supported (if you lose connect during the
+ download, just reconnect and resume). Server has fine control over
+ the downloads with the following new cvars:
+ allow_download - global download on/off
+ allow_download_players - players download on/off
+ allow_download_models - models download on/off
+ allow_download_sounds - sounds download on/off
+ allow_download_maps - maps download on/off
+ maps that are in pak files will _not_ autodownload from the server, this
+ is for copyright considerations.
+ The QuakeWorld bug of the server map changing while download a map has
+ been fixed.
+- New option in the Multiplayer/Player Setup menu for setting your connection
+ speed. This sets a default rate for the player and can improve net
+ performance for modem connections.
+- Rewrote some of the save game code to make it more portable. I wanted to
+ completely rewrite the entire save game system and make it portable across
+ versions and operating systems, but this would require an enormous amount
+ of work.
+- Added another 512 configure strings for general usage for mod makers.
+ This gives lots of room for general string displays on the HUD and in other
+ data.
+- Player movement code re-written to be similiar to that of NetQuake and
+ later versions of QuakeWorld. Player has more control in the air and
+ gets a boost in vertical speed when jumping off the top of ramps.
+- Fixed up serverrecord so that it works correctly with the later versions.
+ serverrecord lets the server do a recording of the current game that
+ demo editors can use to make demos from any PVS in the level. Server
+ recorded demos are BIG. Will look at using delta compression in them
+ to cut down the size.
+- Copy protection CD check has been removed.
+- Quake2 3.15 has changed the protocol (so old servers will not run) but
+ all existing game dlls can run on the new version (albiet without the
+ new features such as visible weapons).
+- Added flood protection. Controlled from the following cvars:
+ flood_msgs - maximum number of messages allowed in the time period
+ specified by flood_persecond
+ flood_persecond - time period that a maximum of flood_msgs messages are
+ permitted
+ flood_waitdelay - amount of time a client gets muzzled for flooding
+- fixed it so blaster/hyperblaster shots aren't treated as solid when
+ predicting--you aren't clipped against them now.
+- gender support is now in. The userinfo cvar "gender" can be set to
+ male/female/none (none for neutral messages). This doesn't affect sounds
+ but does affect death messages in the game. The models male and cyborg
+ default to gender male, and female and crackhor default to female.
+ Everything else defaults to none, but you can set it by typing
+ "gender male" or "gender female" as appropriate.
+- IP banning support ala QW. It's built into the game dll as 'sv' console
+ commands. This list is:
+ sv addip <ip-mask> - adds an ip to the ban list
+ sv listip <ip-mask> - removes an ip from the ban list
+ sv writeip - writes the ban list to <gamedir>/listip.cfg. You can
+ exec this on a server load to load the list on subsequent server runs.
+ like so: quake2 +set dedicated 1 +exec listip.cfg
+ sv removeip <ip-mask> - remove an ip from the list
+ the ip list is a simple mask system. Adding 192.168 to the list
+ would block out everyone in the 192.168.*.* net block. You get 1024 bans,
+ if you need more, recompile the game dll. :)
+ A new cvar is also supported called 'filterban'. It defaults to one which
+ means "allow everyone to connect _except_ those matching in the ban list."
+ If you set it to zero, the meaning reverses like so, "don't allow anyone
+ to connect unless they are in the list."
+
+Quake2 CTF 1.09a Changes:
+
+- Q2CTF 1.09 requires 3.15 now.
+- Competition Match mode added. Server can be reset into a timed match mode.
+ Includes a pregame setup time, countdown until game start, timed match,
+ statistics on players, admin functions and a post game time.
+- The server command 'gamemap' now works correctly. On a server, you can
+ change maps with two commands: map and gamemap. Map will cause all teams
+ to reset, gamemap will change maps with the teams intact.
+- New console commands:
+ yes - vote yes on an election
+ no - vote no on an election
+ ready - ready oneself for a match
+ notready - remove oneself from the ready list (stop the clock)
+ ghost - ghost back into a match if connection was lost
+ admin - become an admin or access the admin menu
+ stats - show statistics on players in a match
+ warp - warp to a new level
+ boot - kick a player of the server (you must be an admin)
+ playerlist - show player list and connect times
+- New cvars:
+ competition - set to 1 to allow the server to be voted by players into
+ competition mode. Set to 3 for a dedicated competition server.
+ The default, 0, disables competition features.
+ matchlock - controls whether players are allowed into a match in progress
+ in competition mode. Defaults to on (1).
+ electpercentage - the precentage of yes votes needed to win an election.
+ Defaults to 66%.
+ matchtime - length of a match, defaulting to 20 minutes. Can be changed
+ by admins.
+ matchsetuptime - length of time allowed to setup a match (after which
+ the server will reset itself back into normal pickup play). Defaults
+ to 10 mins.
+ matchstarttime - The countdown after match setup has been completed
+ until the match begins. Defaults to 20 seconds.
+ admin_password - Password for admin access (allowing access to the admin
+ menu without needing to be elected).
+- Minor bug fixes in team selection to help balance the teams better (the
+ default option on the menu is now the team with the fewer players).
+- Don't get base defenses for telefragging your teammates in base.
+- Telefrags at start of game no longer count (to help with game spawning).
+- Instant weapon changing is now a server option (and can be changed by
+ admin menu).
+- New admin menu that allows remote changes of the following items:
+ Match length (time)
+ Match setup length (time)
+ Match start length (time)
+ Weapons Stay
+ Instant Items
+ Quad Drop
+ Instant Weapons
+- As part of the match code, a new 'ghost' option is included. When a match
+ begins, all players are printed a randomly generated five digit ghost code
+ in their consoles. If the player loses connection for some reason during
+ the match, they can reconnect and reenter the game keeping their score
+ intact at the time of disconnection.
+- Visible weapon support (as with the 3.15 release).
+- Some minor changes to the pmenu code to allow more flexability
+
+
diff --git a/client/adivtab.h b/client/adivtab.h
new file mode 100644
index 0000000..812a8e1
--- /dev/null
+++ b/client/adivtab.h
@@ -0,0 +1,1058 @@
+// table of quotients and remainders for [-15...16] / [-15...16]
+
+// numerator = -15
+{1, 0},
+{1, -1},
+{1, -2},
+{1, -3},
+{1, -4},
+{1, -5},
+{1, -6},
+{1, -7},
+{2, -1},
+{2, -3},
+{3, 0},
+{3, -3},
+{5, 0},
+{7, -1},
+{15, 0},
+{0, 0},
+{-15, 0},
+{-8, 1},
+{-5, 0},
+{-4, 1},
+{-3, 0},
+{-3, 3},
+{-3, 6},
+{-2, 1},
+{-2, 3},
+{-2, 5},
+{-2, 7},
+{-2, 9},
+{-2, 11},
+{-2, 13},
+{-1, 0},
+{-1, 1},
+// numerator = -14
+{0, -14},
+{1, 0},
+{1, -1},
+{1, -2},
+{1, -3},
+{1, -4},
+{1, -5},
+{1, -6},
+{2, 0},
+{2, -2},
+{2, -4},
+{3, -2},
+{4, -2},
+{7, 0},
+{14, 0},
+{0, 0},
+{-14, 0},
+{-7, 0},
+{-5, 1},
+{-4, 2},
+{-3, 1},
+{-3, 4},
+{-2, 0},
+{-2, 2},
+{-2, 4},
+{-2, 6},
+{-2, 8},
+{-2, 10},
+{-2, 12},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+// numerator = -13
+{0, -13},
+{0, -13},
+{1, 0},
+{1, -1},
+{1, -2},
+{1, -3},
+{1, -4},
+{1, -5},
+{1, -6},
+{2, -1},
+{2, -3},
+{3, -1},
+{4, -1},
+{6, -1},
+{13, 0},
+{0, 0},
+{-13, 0},
+{-7, 1},
+{-5, 2},
+{-4, 3},
+{-3, 2},
+{-3, 5},
+{-2, 1},
+{-2, 3},
+{-2, 5},
+{-2, 7},
+{-2, 9},
+{-2, 11},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+// numerator = -12
+{0, -12},
+{0, -12},
+{0, -12},
+{1, 0},
+{1, -1},
+{1, -2},
+{1, -3},
+{1, -4},
+{1, -5},
+{2, 0},
+{2, -2},
+{3, 0},
+{4, 0},
+{6, 0},
+{12, 0},
+{0, 0},
+{-12, 0},
+{-6, 0},
+{-4, 0},
+{-3, 0},
+{-3, 3},
+{-2, 0},
+{-2, 2},
+{-2, 4},
+{-2, 6},
+{-2, 8},
+{-2, 10},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+// numerator = -11
+{0, -11},
+{0, -11},
+{0, -11},
+{0, -11},
+{1, 0},
+{1, -1},
+{1, -2},
+{1, -3},
+{1, -4},
+{1, -5},
+{2, -1},
+{2, -3},
+{3, -2},
+{5, -1},
+{11, 0},
+{0, 0},
+{-11, 0},
+{-6, 1},
+{-4, 1},
+{-3, 1},
+{-3, 4},
+{-2, 1},
+{-2, 3},
+{-2, 5},
+{-2, 7},
+{-2, 9},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+// numerator = -10
+{0, -10},
+{0, -10},
+{0, -10},
+{0, -10},
+{0, -10},
+{1, 0},
+{1, -1},
+{1, -2},
+{1, -3},
+{1, -4},
+{2, 0},
+{2, -2},
+{3, -1},
+{5, 0},
+{10, 0},
+{0, 0},
+{-10, 0},
+{-5, 0},
+{-4, 2},
+{-3, 2},
+{-2, 0},
+{-2, 2},
+{-2, 4},
+{-2, 6},
+{-2, 8},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+// numerator = -9
+{0, -9},
+{0, -9},
+{0, -9},
+{0, -9},
+{0, -9},
+{0, -9},
+{1, 0},
+{1, -1},
+{1, -2},
+{1, -3},
+{1, -4},
+{2, -1},
+{3, 0},
+{4, -1},
+{9, 0},
+{0, 0},
+{-9, 0},
+{-5, 1},
+{-3, 0},
+{-3, 3},
+{-2, 1},
+{-2, 3},
+{-2, 5},
+{-2, 7},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+{-1, 7},
+// numerator = -8
+{0, -8},
+{0, -8},
+{0, -8},
+{0, -8},
+{0, -8},
+{0, -8},
+{0, -8},
+{1, 0},
+{1, -1},
+{1, -2},
+{1, -3},
+{2, 0},
+{2, -2},
+{4, 0},
+{8, 0},
+{0, 0},
+{-8, 0},
+{-4, 0},
+{-3, 1},
+{-2, 0},
+{-2, 2},
+{-2, 4},
+{-2, 6},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+{-1, 7},
+{-1, 8},
+// numerator = -7
+{0, -7},
+{0, -7},
+{0, -7},
+{0, -7},
+{0, -7},
+{0, -7},
+{0, -7},
+{0, -7},
+{1, 0},
+{1, -1},
+{1, -2},
+{1, -3},
+{2, -1},
+{3, -1},
+{7, 0},
+{0, 0},
+{-7, 0},
+{-4, 1},
+{-3, 2},
+{-2, 1},
+{-2, 3},
+{-2, 5},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+{-1, 7},
+{-1, 8},
+{-1, 9},
+// numerator = -6
+{0, -6},
+{0, -6},
+{0, -6},
+{0, -6},
+{0, -6},
+{0, -6},
+{0, -6},
+{0, -6},
+{0, -6},
+{1, 0},
+{1, -1},
+{1, -2},
+{2, 0},
+{3, 0},
+{6, 0},
+{0, 0},
+{-6, 0},
+{-3, 0},
+{-2, 0},
+{-2, 2},
+{-2, 4},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+{-1, 7},
+{-1, 8},
+{-1, 9},
+{-1, 10},
+// numerator = -5
+{0, -5},
+{0, -5},
+{0, -5},
+{0, -5},
+{0, -5},
+{0, -5},
+{0, -5},
+{0, -5},
+{0, -5},
+{0, -5},
+{1, 0},
+{1, -1},
+{1, -2},
+{2, -1},
+{5, 0},
+{0, 0},
+{-5, 0},
+{-3, 1},
+{-2, 1},
+{-2, 3},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+{-1, 7},
+{-1, 8},
+{-1, 9},
+{-1, 10},
+{-1, 11},
+// numerator = -4
+{0, -4},
+{0, -4},
+{0, -4},
+{0, -4},
+{0, -4},
+{0, -4},
+{0, -4},
+{0, -4},
+{0, -4},
+{0, -4},
+{0, -4},
+{1, 0},
+{1, -1},
+{2, 0},
+{4, 0},
+{0, 0},
+{-4, 0},
+{-2, 0},
+{-2, 2},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+{-1, 7},
+{-1, 8},
+{-1, 9},
+{-1, 10},
+{-1, 11},
+{-1, 12},
+// numerator = -3
+{0, -3},
+{0, -3},
+{0, -3},
+{0, -3},
+{0, -3},
+{0, -3},
+{0, -3},
+{0, -3},
+{0, -3},
+{0, -3},
+{0, -3},
+{0, -3},
+{1, 0},
+{1, -1},
+{3, 0},
+{0, 0},
+{-3, 0},
+{-2, 1},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+{-1, 7},
+{-1, 8},
+{-1, 9},
+{-1, 10},
+{-1, 11},
+{-1, 12},
+{-1, 13},
+// numerator = -2
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{0, -2},
+{1, 0},
+{2, 0},
+{0, 0},
+{-2, 0},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+{-1, 7},
+{-1, 8},
+{-1, 9},
+{-1, 10},
+{-1, 11},
+{-1, 12},
+{-1, 13},
+{-1, 14},
+// numerator = -1
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{0, -1},
+{1, 0},
+{0, 0},
+{-1, 0},
+{-1, 1},
+{-1, 2},
+{-1, 3},
+{-1, 4},
+{-1, 5},
+{-1, 6},
+{-1, 7},
+{-1, 8},
+{-1, 9},
+{-1, 10},
+{-1, 11},
+{-1, 12},
+{-1, 13},
+{-1, 14},
+{-1, 15},
+// numerator = 0
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+{0, 0},
+// numerator = 1
+{-1, -14},
+{-1, -13},
+{-1, -12},
+{-1, -11},
+{-1, -10},
+{-1, -9},
+{-1, -8},
+{-1, -7},
+{-1, -6},
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{0, 0},
+{1, 0},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+{0, 1},
+// numerator = 2
+{-1, -13},
+{-1, -12},
+{-1, -11},
+{-1, -10},
+{-1, -9},
+{-1, -8},
+{-1, -7},
+{-1, -6},
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, 0},
+{0, 0},
+{2, 0},
+{1, 0},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+{0, 2},
+// numerator = 3
+{-1, -12},
+{-1, -11},
+{-1, -10},
+{-1, -9},
+{-1, -8},
+{-1, -7},
+{-1, -6},
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -1},
+{-3, 0},
+{0, 0},
+{3, 0},
+{1, 1},
+{1, 0},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+{0, 3},
+// numerator = 4
+{-1, -11},
+{-1, -10},
+{-1, -9},
+{-1, -8},
+{-1, -7},
+{-1, -6},
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -2},
+{-2, 0},
+{-4, 0},
+{0, 0},
+{4, 0},
+{2, 0},
+{1, 1},
+{1, 0},
+{0, 4},
+{0, 4},
+{0, 4},
+{0, 4},
+{0, 4},
+{0, 4},
+{0, 4},
+{0, 4},
+{0, 4},
+{0, 4},
+{0, 4},
+{0, 4},
+// numerator = 5
+{-1, -10},
+{-1, -9},
+{-1, -8},
+{-1, -7},
+{-1, -6},
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -3},
+{-2, -1},
+{-3, -1},
+{-5, 0},
+{0, 0},
+{5, 0},
+{2, 1},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 5},
+{0, 5},
+{0, 5},
+{0, 5},
+{0, 5},
+{0, 5},
+{0, 5},
+{0, 5},
+{0, 5},
+{0, 5},
+{0, 5},
+// numerator = 6
+{-1, -9},
+{-1, -8},
+{-1, -7},
+{-1, -6},
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -4},
+{-2, -2},
+{-2, 0},
+{-3, 0},
+{-6, 0},
+{0, 0},
+{6, 0},
+{3, 0},
+{2, 0},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 6},
+{0, 6},
+{0, 6},
+{0, 6},
+{0, 6},
+{0, 6},
+{0, 6},
+{0, 6},
+{0, 6},
+{0, 6},
+// numerator = 7
+{-1, -8},
+{-1, -7},
+{-1, -6},
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -5},
+{-2, -3},
+{-2, -1},
+{-3, -2},
+{-4, -1},
+{-7, 0},
+{0, 0},
+{7, 0},
+{3, 1},
+{2, 1},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 7},
+{0, 7},
+{0, 7},
+{0, 7},
+{0, 7},
+{0, 7},
+{0, 7},
+{0, 7},
+{0, 7},
+// numerator = 8
+{-1, -7},
+{-1, -6},
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -6},
+{-2, -4},
+{-2, -2},
+{-2, 0},
+{-3, -1},
+{-4, 0},
+{-8, 0},
+{0, 0},
+{8, 0},
+{4, 0},
+{2, 2},
+{2, 0},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 8},
+{0, 8},
+{0, 8},
+{0, 8},
+{0, 8},
+{0, 8},
+{0, 8},
+{0, 8},
+// numerator = 9
+{-1, -6},
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -7},
+{-2, -5},
+{-2, -3},
+{-2, -1},
+{-3, -3},
+{-3, 0},
+{-5, -1},
+{-9, 0},
+{0, 0},
+{9, 0},
+{4, 1},
+{3, 0},
+{2, 1},
+{1, 4},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 9},
+{0, 9},
+{0, 9},
+{0, 9},
+{0, 9},
+{0, 9},
+{0, 9},
+// numerator = 10
+{-1, -5},
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -8},
+{-2, -6},
+{-2, -4},
+{-2, -2},
+{-2, 0},
+{-3, -2},
+{-4, -2},
+{-5, 0},
+{-10, 0},
+{0, 0},
+{10, 0},
+{5, 0},
+{3, 1},
+{2, 2},
+{2, 0},
+{1, 4},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 10},
+{0, 10},
+{0, 10},
+{0, 10},
+{0, 10},
+{0, 10},
+// numerator = 11
+{-1, -4},
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -9},
+{-2, -7},
+{-2, -5},
+{-2, -3},
+{-2, -1},
+{-3, -4},
+{-3, -1},
+{-4, -1},
+{-6, -1},
+{-11, 0},
+{0, 0},
+{11, 0},
+{5, 1},
+{3, 2},
+{2, 3},
+{2, 1},
+{1, 5},
+{1, 4},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 11},
+{0, 11},
+{0, 11},
+{0, 11},
+{0, 11},
+// numerator = 12
+{-1, -3},
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -10},
+{-2, -8},
+{-2, -6},
+{-2, -4},
+{-2, -2},
+{-2, 0},
+{-3, -3},
+{-3, 0},
+{-4, 0},
+{-6, 0},
+{-12, 0},
+{0, 0},
+{12, 0},
+{6, 0},
+{4, 0},
+{3, 0},
+{2, 2},
+{2, 0},
+{1, 5},
+{1, 4},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 12},
+{0, 12},
+{0, 12},
+{0, 12},
+// numerator = 13
+{-1, -2},
+{-1, -1},
+{-1, 0},
+{-2, -11},
+{-2, -9},
+{-2, -7},
+{-2, -5},
+{-2, -3},
+{-2, -1},
+{-3, -5},
+{-3, -2},
+{-4, -3},
+{-5, -2},
+{-7, -1},
+{-13, 0},
+{0, 0},
+{13, 0},
+{6, 1},
+{4, 1},
+{3, 1},
+{2, 3},
+{2, 1},
+{1, 6},
+{1, 5},
+{1, 4},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 13},
+{0, 13},
+{0, 13},
+// numerator = 14
+{-1, -1},
+{-1, 0},
+{-2, -12},
+{-2, -10},
+{-2, -8},
+{-2, -6},
+{-2, -4},
+{-2, -2},
+{-2, 0},
+{-3, -4},
+{-3, -1},
+{-4, -2},
+{-5, -1},
+{-7, 0},
+{-14, 0},
+{0, 0},
+{14, 0},
+{7, 0},
+{4, 2},
+{3, 2},
+{2, 4},
+{2, 2},
+{2, 0},
+{1, 6},
+{1, 5},
+{1, 4},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 14},
+{0, 14},
+// numerator = 15
+{-1, 0},
+{-2, -13},
+{-2, -11},
+{-2, -9},
+{-2, -7},
+{-2, -5},
+{-2, -3},
+{-2, -1},
+{-3, -6},
+{-3, -3},
+{-3, 0},
+{-4, -1},
+{-5, 0},
+{-8, -1},
+{-15, 0},
+{0, 0},
+{15, 0},
+{7, 1},
+{5, 0},
+{3, 3},
+{3, 0},
+{2, 3},
+{2, 1},
+{1, 7},
+{1, 6},
+{1, 5},
+{1, 4},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
+{0, 15},
+// numerator = 16
+{-2, -14},
+{-2, -12},
+{-2, -10},
+{-2, -8},
+{-2, -6},
+{-2, -4},
+{-2, -2},
+{-2, 0},
+{-3, -5},
+{-3, -2},
+{-4, -4},
+{-4, 0},
+{-6, -2},
+{-8, 0},
+{-16, 0},
+{0, 0},
+{16, 0},
+{8, 0},
+{5, 1},
+{4, 0},
+{3, 1},
+{2, 4},
+{2, 2},
+{2, 0},
+{1, 7},
+{1, 6},
+{1, 5},
+{1, 4},
+{1, 3},
+{1, 2},
+{1, 1},
+{1, 0},
diff --git a/client/anorms.h b/client/anorms.h
new file mode 100644
index 0000000..011582e
--- /dev/null
+++ b/client/anorms.h
@@ -0,0 +1,181 @@
+/*
+Copyright (C) 1997-2001 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.
+
+*/
+{-0.525731, 0.000000, 0.850651},
+{-0.442863, 0.238856, 0.864188},
+{-0.295242, 0.000000, 0.955423},
+{-0.309017, 0.500000, 0.809017},
+{-0.162460, 0.262866, 0.951056},
+{0.000000, 0.000000, 1.000000},
+{0.000000, 0.850651, 0.525731},
+{-0.147621, 0.716567, 0.681718},
+{0.147621, 0.716567, 0.681718},
+{0.000000, 0.525731, 0.850651},
+{0.309017, 0.500000, 0.809017},
+{0.525731, 0.000000, 0.850651},
+{0.295242, 0.000000, 0.955423},
+{0.442863, 0.238856, 0.864188},
+{0.162460, 0.262866, 0.951056},
+{-0.681718, 0.147621, 0.716567},
+{-0.809017, 0.309017, 0.500000},
+{-0.587785, 0.425325, 0.688191},
+{-0.850651, 0.525731, 0.000000},
+{-0.864188, 0.442863, 0.238856},
+{-0.716567, 0.681718, 0.147621},
+{-0.688191, 0.587785, 0.425325},
+{-0.500000, 0.809017, 0.309017},
+{-0.238856, 0.864188, 0.442863},
+{-0.425325, 0.688191, 0.587785},
+{-0.716567, 0.681718, -0.147621},
+{-0.500000, 0.809017, -0.309017},
+{-0.525731, 0.850651, 0.000000},
+{0.000000, 0.850651, -0.525731},
+{-0.238856, 0.864188, -0.442863},
+{0.000000, 0.955423, -0.295242},
+{-0.262866, 0.951056, -0.162460},
+{0.000000, 1.000000, 0.000000},
+{0.000000, 0.955423, 0.295242},
+{-0.262866, 0.951056, 0.162460},
+{0.238856, 0.864188, 0.442863},
+{0.262866, 0.951056, 0.162460},
+{0.500000, 0.809017, 0.309017},
+{0.238856, 0.864188, -0.442863},
+{0.262866, 0.951056, -0.162460},
+{0.500000, 0.809017, -0.309017},
+{0.850651, 0.525731, 0.000000},
+{0.716567, 0.681718, 0.147621},
+{0.716567, 0.681718, -0.147621},
+{0.525731, 0.850651, 0.000000},
+{0.425325, 0.688191, 0.587785},
+{0.864188, 0.442863, 0.238856},
+{0.688191, 0.587785, 0.425325},
+{0.809017, 0.309017, 0.500000},
+{0.681718, 0.147621, 0.716567},
+{0.587785, 0.425325, 0.688191},
+{0.955423, 0.295242, 0.000000},
+{1.000000, 0.000000, 0.000000},
+{0.951056, 0.162460, 0.262866},
+{0.850651, -0.525731, 0.000000},
+{0.955423, -0.295242, 0.000000},
+{0.864188, -0.442863, 0.238856},
+{0.951056, -0.162460, 0.262866},
+{0.809017, -0.309017, 0.500000},
+{0.681718, -0.147621, 0.716567},
+{0.850651, 0.000000, 0.525731},
+{0.864188, 0.442863, -0.238856},
+{0.809017, 0.309017, -0.500000},
+{0.951056, 0.162460, -0.262866},
+{0.525731, 0.000000, -0.850651},
+{0.681718, 0.147621, -0.716567},
+{0.681718, -0.147621, -0.716567},
+{0.850651, 0.000000, -0.525731},
+{0.809017, -0.309017, -0.500000},
+{0.864188, -0.442863, -0.238856},
+{0.951056, -0.162460, -0.262866},
+{0.147621, 0.716567, -0.681718},
+{0.309017, 0.500000, -0.809017},
+{0.425325, 0.688191, -0.587785},
+{0.442863, 0.238856, -0.864188},
+{0.587785, 0.425325, -0.688191},
+{0.688191, 0.587785, -0.425325},
+{-0.147621, 0.716567, -0.681718},
+{-0.309017, 0.500000, -0.809017},
+{0.000000, 0.525731, -0.850651},
+{-0.525731, 0.000000, -0.850651},
+{-0.442863, 0.238856, -0.864188},
+{-0.295242, 0.000000, -0.955423},
+{-0.162460, 0.262866, -0.951056},
+{0.000000, 0.000000, -1.000000},
+{0.295242, 0.000000, -0.955423},
+{0.162460, 0.262866, -0.951056},
+{-0.442863, -0.238856, -0.864188},
+{-0.309017, -0.500000, -0.809017},
+{-0.162460, -0.262866, -0.951056},
+{0.000000, -0.850651, -0.525731},
+{-0.147621, -0.716567, -0.681718},
+{0.147621, -0.716567, -0.681718},
+{0.000000, -0.525731, -0.850651},
+{0.309017, -0.500000, -0.809017},
+{0.442863, -0.238856, -0.864188},
+{0.162460, -0.262866, -0.951056},
+{0.238856, -0.864188, -0.442863},
+{0.500000, -0.809017, -0.309017},
+{0.425325, -0.688191, -0.587785},
+{0.716567, -0.681718, -0.147621},
+{0.688191, -0.587785, -0.425325},
+{0.587785, -0.425325, -0.688191},
+{0.000000, -0.955423, -0.295242},
+{0.000000, -1.000000, 0.000000},
+{0.262866, -0.951056, -0.162460},
+{0.000000, -0.850651, 0.525731},
+{0.000000, -0.955423, 0.295242},
+{0.238856, -0.864188, 0.442863},
+{0.262866, -0.951056, 0.162460},
+{0.500000, -0.809017, 0.309017},
+{0.716567, -0.681718, 0.147621},
+{0.525731, -0.850651, 0.000000},
+{-0.238856, -0.864188, -0.442863},
+{-0.500000, -0.809017, -0.309017},
+{-0.262866, -0.951056, -0.162460},
+{-0.850651, -0.525731, 0.000000},
+{-0.716567, -0.681718, -0.147621},
+{-0.716567, -0.681718, 0.147621},
+{-0.525731, -0.850651, 0.000000},
+{-0.500000, -0.809017, 0.309017},
+{-0.238856, -0.864188, 0.442863},
+{-0.262866, -0.951056, 0.162460},
+{-0.864188, -0.442863, 0.238856},
+{-0.809017, -0.309017, 0.500000},
+{-0.688191, -0.587785, 0.425325},
+{-0.681718, -0.147621, 0.716567},
+{-0.442863, -0.238856, 0.864188},
+{-0.587785, -0.425325, 0.688191},
+{-0.309017, -0.500000, 0.809017},
+{-0.147621, -0.716567, 0.681718},
+{-0.425325, -0.688191, 0.587785},
+{-0.162460, -0.262866, 0.951056},
+{0.442863, -0.238856, 0.864188},
+{0.162460, -0.262866, 0.951056},
+{0.309017, -0.500000, 0.809017},
+{0.147621, -0.716567, 0.681718},
+{0.000000, -0.525731, 0.850651},
+{0.425325, -0.688191, 0.587785},
+{0.587785, -0.425325, 0.688191},
+{0.688191, -0.587785, 0.425325},
+{-0.955423, 0.295242, 0.000000},
+{-0.951056, 0.162460, 0.262866},
+{-1.000000, 0.000000, 0.000000},
+{-0.850651, 0.000000, 0.525731},
+{-0.955423, -0.295242, 0.000000},
+{-0.951056, -0.162460, 0.262866},
+{-0.864188, 0.442863, -0.238856},
+{-0.951056, 0.162460, -0.262866},
+{-0.809017, 0.309017, -0.500000},
+{-0.864188, -0.442863, -0.238856},
+{-0.951056, -0.162460, -0.262866},
+{-0.809017, -0.309017, -0.500000},
+{-0.681718, 0.147621, -0.716567},
+{-0.681718, -0.147621, -0.716567},
+{-0.850651, 0.000000, -0.525731},
+{-0.688191, 0.587785, -0.425325},
+{-0.587785, 0.425325, -0.688191},
+{-0.425325, 0.688191, -0.587785},
+{-0.425325, -0.688191, -0.587785},
+{-0.587785, -0.425325, -0.688191},
+{-0.688191, -0.587785, -0.425325},
diff --git a/client/asm_i386.h b/client/asm_i386.h
new file mode 100644
index 0000000..087a320
--- /dev/null
+++ b/client/asm_i386.h
@@ -0,0 +1,81 @@
+
+#ifndef __ASM_I386__
+#define __ASM_I386__
+
+#ifdef ELF
+#define C(label) label
+#else
+#define C(label) _##label
+#endif
+
+//
+// !!! note that this file must match the corresponding C structures at all
+// times !!!
+//
+
+// plane_t structure
+// !!! if this is changed, it must be changed in model.h too !!!
+// !!! if the size of this is changed, the array lookup in SV_HullPointContents
+// must be changed too !!!
+#define pl_normal 0
+#define pl_dist 12
+#define pl_type 16
+#define pl_signbits 17
+#define pl_pad 18
+#define pl_size 20
+
+// hull_t structure
+// !!! if this is changed, it must be changed in model.h too !!!
+#define hu_clipnodes 0
+#define hu_planes 4
+#define hu_firstclipnode 8
+#define hu_lastclipnode 12
+#define hu_clip_mins 16
+#define hu_clip_maxs 28
+#define hu_size 40
+
+// dnode_t structure
+// !!! if this is changed, it must be changed in bspfile.h too !!!
+#define nd_planenum 0
+#define nd_children 4
+#define nd_mins 8
+#define nd_maxs 20
+#define nd_firstface 32
+#define nd_numfaces 36
+#define nd_size 40
+
+// sfxcache_t structure
+// !!! if this is changed, it much be changed in sound.h too !!!
+#define sfxc_length 0
+#define sfxc_loopstart 4
+#define sfxc_speed 8
+#define sfxc_width 12
+#define sfxc_stereo 16
+#define sfxc_data 20
+
+// channel_t structure
+// !!! if this is changed, it much be changed in sound.h too !!!
+#define ch_sfx 0
+#define ch_leftvol 4
+#define ch_rightvol 8
+#define ch_end 12
+#define ch_pos 16
+#define ch_looping 20
+#define ch_entnum 24
+#define ch_entchannel 28
+#define ch_origin 32
+#define ch_dist_mult 44
+#define ch_master_vol 48
+#define ch_size 52
+
+// portable_samplepair_t structure
+// !!! if this is changed, it much be changed in sound.h too !!!
+#define psp_left 0
+#define psp_right 4
+#define psp_size 8
+
+// !!! must be kept the same as in d_iface.h !!!
+#define TRANSPARENT_COLOR 255
+
+#endif
+
diff --git a/client/block16.h b/client/block16.h
new file mode 100644
index 0000000..98a1cf7
--- /dev/null
+++ b/client/block16.h
@@ -0,0 +1,123 @@
+LEnter16_16:
+ movb (%esi),%al
+ movb (%esi,%ebx,),%cl
+ movb %dh,%ah
+ addl %ebp,%edx
+ movb %dh,%ch
+ leal (%esi,%ebx,2),%esi
+ movw 0x12345678(,%eax,2),%ax
+LBPatch0:
+ addl %ebp,%edx
+ movw %ax,(%edi)
+ movw 0x12345678(,%ecx,2),%cx
+LBPatch1:
+ movw %cx,2(%edi)
+ addl $0x4,%edi
+
+ movb (%esi),%al
+ movb (%esi,%ebx,),%cl
+ movb %dh,%ah
+ addl %ebp,%edx
+ movb %dh,%ch
+ leal (%esi,%ebx,2),%esi
+ movw 0x12345678(,%eax,2),%ax
+LBPatch2:
+ addl %ebp,%edx
+ movw %ax,(%edi)
+ movw 0x12345678(,%ecx,2),%cx
+LBPatch3:
+ movw %cx,2(%edi)
+ addl $0x4,%edi
+
+ movb (%esi),%al
+ movb (%esi,%ebx,),%cl
+ movb %dh,%ah
+ addl %ebp,%edx
+ movb %dh,%ch
+ leal (%esi,%ebx,2),%esi
+ movw 0x12345678(,%eax,2),%ax
+LBPatch4:
+ addl %ebp,%edx
+ movw %ax,(%edi)
+ movw 0x12345678(,%ecx,2),%cx
+LBPatch5:
+ movw %cx,2(%edi)
+ addl $0x4,%edi
+
+ movb (%esi),%al
+ movb (%esi,%ebx,),%cl
+ movb %dh,%ah
+ addl %ebp,%edx
+ movb %dh,%ch
+ leal (%esi,%ebx,2),%esi
+ movw 0x12345678(,%eax,2),%ax
+LBPatch6:
+ addl %ebp,%edx
+ movw %ax,(%edi)
+ movw 0x12345678(,%ecx,2),%cx
+LBPatch7:
+ movw %cx,2(%edi)
+ addl $0x4,%edi
+
+LEnter8_16:
+ movb (%esi),%al
+ movb (%esi,%ebx,),%cl
+ movb %dh,%ah
+ addl %ebp,%edx
+ movb %dh,%ch
+ leal (%esi,%ebx,2),%esi
+ movw 0x12345678(,%eax,2),%ax
+LBPatch8:
+ addl %ebp,%edx
+ movw %ax,(%edi)
+ movw 0x12345678(,%ecx,2),%cx
+LBPatch9:
+ movw %cx,2(%edi)
+ addl $0x4,%edi
+
+ movb (%esi),%al
+ movb (%esi,%ebx,),%cl
+ movb %dh,%ah
+ addl %ebp,%edx
+ movb %dh,%ch
+ leal (%esi,%ebx,2),%esi
+ movw 0x12345678(,%eax,2),%ax
+LBPatch10:
+ addl %ebp,%edx
+ movw %ax,(%edi)
+ movw 0x12345678(,%ecx,2),%cx
+LBPatch11:
+ movw %cx,2(%edi)
+ addl $0x4,%edi
+
+LEnter4_16:
+ movb (%esi),%al
+ movb (%esi,%ebx,),%cl
+ movb %dh,%ah
+ addl %ebp,%edx
+ movb %dh,%ch
+ leal (%esi,%ebx,2),%esi
+ movw 0x12345678(,%eax,2),%ax
+LBPatch12:
+ addl %ebp,%edx
+ movw %ax,(%edi)
+ movw 0x12345678(,%ecx,2),%cx
+LBPatch13:
+ movw %cx,2(%edi)
+ addl $0x4,%edi
+
+LEnter2_16:
+ movb (%esi),%al
+ movb (%esi,%ebx,),%cl
+ movb %dh,%ah
+ addl %ebp,%edx
+ movb %dh,%ch
+ leal (%esi,%ebx,2),%esi
+ movw 0x12345678(,%eax,2),%ax
+LBPatch14:
+ addl %ebp,%edx
+ movw %ax,(%edi)
+ movw 0x12345678(,%ecx,2),%cx
+LBPatch15:
+ movw %cx,2(%edi)
+ addl $0x4,%edi
diff --git a/client/block8.h b/client/block8.h
new file mode 100644
index 0000000..d501379
--- /dev/null
+++ b/client/block8.h
@@ -0,0 +1,124 @@
+LEnter16_8:
+ movb (%esi),%al
+ movb (%esi,%ebx,),%cl
+ movb %dh,%ah
+ addl %ebp,%edx
+ movb %dh,%ch
+ leal (%esi,%ebx,2),%esi
+ movb 0x12345678(%eax),%al
+LBPatch0:
+ addl %ebp,%edx
+ movb %al,(%edi)
+ movb 0x12345678(%ecx),%cl
+LBPatch1:
+ movb %cl,1(%edi)
+ addl $0x2,%edi
+
+ movb (%esi),%al
+ movb (%esi,%ebx,),%cl
+ movb %dh,%ah
+ addl %ebp,%edx
+ movb %dh,%ch
+ leal (%esi,%ebx,2),%esi
+ movb 0x12345678(%eax),%al
+LBPatch2:
+ addl %ebp,%edx
+ movb %al,(%edi)
+ movb 0x12345678(%ecx),%cl
+LBPatch3:
+ movb %cl,1(%edi)
+ addl $0x2,%edi
+
+ movb (%esi),%al
+ movb (%esi,%ebx,),%cl
+ movb %dh,%ah
+ addl %ebp,%edx
+ movb %dh,%ch
+ leal (%esi,%ebx,2),%esi
+ movb 0x12345678(%eax),%al
+LBPatch4:
+ addl %ebp,%edx
+ movb %al,(%edi)
+ movb 0x12345678(%ecx),%cl
+LBPatch5:
+ movb %cl,1(%edi)
+ addl $0x2,%edi
+
+ movb (%esi),%al
+ movb (%esi,%ebx,),%cl
+ movb %dh,%ah
+ addl %ebp,%edx
+ movb %dh,%ch
+ leal (%esi,%ebx,2),%esi
+ movb 0x12345678(%eax),%al
+LBPatch6:
+ addl %ebp,%edx
+ movb %al,(%edi)
+ movb 0x12345678(%ecx),%cl
+LBPatch7:
+ movb %cl,1(%edi)
+ addl $0x2,%edi
+
+LEnter8_8:
+ movb (%esi),%al
+ movb (%esi,%ebx,),%cl
+ movb %dh,%ah
+ addl %ebp,%edx
+ movb %dh,%ch
+ leal (%esi,%ebx,2),%esi
+ movb 0x12345678(%eax),%al
+LBPatch8:
+ addl %ebp,%edx
+ movb %al,(%edi)
+ movb 0x12345678(%ecx),%cl
+LBPatch9:
+ movb %cl,1(%edi)
+ addl $0x2,%edi
+
+ movb (%esi),%al
+ movb (%esi,%ebx,),%cl
+ movb %dh,%ah
+ addl %ebp,%edx
+ movb %dh,%ch
+ leal (%esi,%ebx,2),%esi
+ movb 0x12345678(%eax),%al
+LBPatch10:
+ addl %ebp,%edx
+ movb %al,(%edi)
+ movb 0x12345678(%ecx),%cl
+LBPatch11:
+ movb %cl,1(%edi)
+ addl $0x2,%edi
+
+LEnter4_8:
+ movb (%esi),%al
+ movb (%esi,%ebx,),%cl
+ movb %dh,%ah
+ addl %ebp,%edx
+ movb %dh,%ch
+ leal (%esi,%ebx,2),%esi
+ movb 0x12345678(%eax),%al
+LBPatch12:
+ addl %ebp,%edx
+ movb %al,(%edi)
+ movb 0x12345678(%ecx),%cl
+LBPatch13:
+ movb %cl,1(%edi)
+ addl $0x2,%edi
+
+LEnter2_8:
+ movb (%esi),%al
+ movb (%esi,%ebx,),%cl
+ movb %dh,%ah
+ addl %ebp,%edx
+ movb %dh,%ch
+ leal (%esi,%ebx,2),%esi
+ movb 0x12345678(%eax),%al
+LBPatch14:
+ addl %ebp,%edx
+ movb %al,(%edi)
+ movb 0x12345678(%ecx),%cl
+LBPatch15:
+ movb %cl,1(%edi)
+ addl $0x2,%edi
+
diff --git a/client/cdaudio.h b/client/cdaudio.h
new file mode 100644
index 0000000..ad656b8
--- /dev/null
+++ b/client/cdaudio.h
@@ -0,0 +1,26 @@
+/*
+Copyright (C) 1997-2001 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.
+
+*/
+
+int CDAudio_Init(void);
+void CDAudio_Shutdown(void);
+void CDAudio_Play(int track, qboolean looping);
+void CDAudio_Stop(void);
+void CDAudio_Update(void);
+void CDAudio_Activate (qboolean active);
diff --git a/client/cl_cin.c b/client/cl_cin.c
new file mode 100644
index 0000000..18ea81a
--- /dev/null
+++ b/client/cl_cin.c
@@ -0,0 +1,650 @@
+/*
+Copyright (C) 1997-2001 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 "client.h"
+
+typedef struct
+{
+ byte *data;
+ int count;
+} cblock_t;
+
+typedef struct
+{
+ qboolean restart_sound;
+ int s_rate;
+ int s_width;
+ int s_channels;
+
+ int width;
+ int height;
+ byte *pic;
+ byte *pic_pending;
+
+ // order 1 huffman stuff
+ int *hnodes1; // [256][256][2];
+ int numhnodes1[256];
+
+ int h_used[512];
+ int h_count[512];
+} cinematics_t;
+
+cinematics_t cin;
+
+/*
+=================================================================
+
+PCX LOADING
+
+=================================================================
+*/
+
+
+/*
+==============
+SCR_LoadPCX
+==============
+*/
+void SCR_LoadPCX (char *filename, byte **pic, byte **palette, int *width, int *height)
+{
+ byte *raw;
+ pcx_t *pcx;
+ int x, y;
+ int len;
+ int dataByte, runLength;
+ byte *out, *pix;
+
+ *pic = NULL;
+
+ //
+ // load the file
+ //
+ len = FS_LoadFile (filename, (void **)&raw);
+ if (!raw)
+ return; // Com_Printf ("Bad pcx file %s\n", filename);
+
+ //
+ // parse the PCX file
+ //
+ pcx = (pcx_t *)raw;
+ raw = &pcx->data;
+
+ if (pcx->manufacturer != 0x0a
+ || pcx->version != 5
+ || pcx->encoding != 1
+ || pcx->bits_per_pixel != 8
+ || pcx->xmax >= 640
+ || pcx->ymax >= 480)
+ {
+ Com_Printf ("Bad pcx file %s\n", filename);
+ return;
+ }
+
+ out = Z_Malloc ( (pcx->ymax+1) * (pcx->xmax+1) );
+
+ *pic = out;
+
+ pix = out;
+
+ if (palette)
+ {
+ *palette = Z_Malloc(768);
+ memcpy (*palette, (byte *)pcx + len - 768, 768);
+ }
+
+ if (width)
+ *width = pcx->xmax+1;
+ if (height)
+ *height = pcx->ymax+1;
+
+ for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1)
+ {
+ for (x=0 ; x<=pcx->xmax ; )
+ {
+ dataByte = *raw++;
+
+ if((dataByte & 0xC0) == 0xC0)
+ {
+ runLength = dataByte & 0x3F;
+ dataByte = *raw++;
+ }
+ else
+ runLength = 1;
+
+ while(runLength-- > 0)
+ pix[x++] = dataByte;
+ }
+
+ }
+
+ if ( raw - (byte *)pcx > len)
+ {
+ Com_Printf ("PCX file %s was malformed", filename);
+ Z_Free (*pic);
+ *pic = NULL;
+ }
+
+ FS_FreeFile (pcx);
+}
+
+//=============================================================
+
+/*
+==================
+SCR_StopCinematic
+==================
+*/
+void SCR_StopCinematic (void)
+{
+ cl.cinematictime = 0; // done
+ if (cin.pic)
+ {
+ Z_Free (cin.pic);
+ cin.pic = NULL;
+ }
+ if (cin.pic_pending)
+ {
+ Z_Free (cin.pic_pending);
+ cin.pic_pending = NULL;
+ }
+ if (cl.cinematicpalette_active)
+ {
+ re.CinematicSetPalette(NULL);
+ cl.cinematicpalette_active = false;
+ }
+ if (cl.cinematic_file)
+ {
+ fclose (cl.cinematic_file);
+ cl.cinematic_file = NULL;
+ }
+ if (cin.hnodes1)
+ {
+ Z_Free (cin.hnodes1);
+ cin.hnodes1 = NULL;
+ }
+
+ // switch back down to 11 khz sound if necessary
+ if (cin.restart_sound)
+ {
+ cin.restart_sound = false;
+ CL_Snd_Restart_f ();
+ }
+
+}
+
+/*
+====================
+SCR_FinishCinematic
+
+Called when either the cinematic completes, or it is aborted
+====================
+*/
+void SCR_FinishCinematic (void)
+{
+ // tell the server to advance to the next map / cinematic
+ MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
+ SZ_Print (&cls.netchan.message, va("nextserver %i\n", cl.servercount));
+}
+
+//==========================================================================
+
+/*
+==================
+SmallestNode1
+==================
+*/
+int SmallestNode1 (int numhnodes)
+{
+ int i;
+ int best, bestnode;
+
+ best = 99999999;
+ bestnode = -1;
+ for (i=0 ; i<numhnodes ; i++)
+ {
+ if (cin.h_used[i])
+ continue;
+ if (!cin.h_count[i])
+ continue;
+ if (cin.h_count[i] < best)
+ {
+ best = cin.h_count[i];
+ bestnode = i;
+ }
+ }
+
+ if (bestnode == -1)
+ return -1;
+
+ cin.h_used[bestnode] = true;
+ return bestnode;
+}
+
+
+/*
+==================
+Huff1TableInit
+
+Reads the 64k counts table and initializes the node trees
+==================
+*/
+void Huff1TableInit (void)
+{
+ int prev;
+ int j;
+ int *node, *nodebase;
+ byte counts[256];
+ int numhnodes;
+
+ cin.hnodes1 = Z_Malloc (256*256*2*4);
+ memset (cin.hnodes1, 0, 256*256*2*4);
+
+ for (prev=0 ; prev<256 ; prev++)
+ {
+ memset (cin.h_count,0,sizeof(cin.h_count));
+ memset (cin.h_used,0,sizeof(cin.h_used));
+
+ // read a row of counts
+ FS_Read (counts, sizeof(counts), cl.cinematic_file);
+ for (j=0 ; j<256 ; j++)
+ cin.h_count[j] = counts[j];
+
+ // build the nodes
+ numhnodes = 256;
+ nodebase = cin.hnodes1 + prev*256*2;
+
+ while (numhnodes != 511)
+ {
+ node = nodebase + (numhnodes-256)*2;
+
+ // pick two lowest counts
+ node[0] = SmallestNode1 (numhnodes);
+ if (node[0] == -1)
+ break; // no more
+
+ node[1] = SmallestNode1 (numhnodes);
+ if (node[1] == -1)
+ break;
+
+ cin.h_count[numhnodes] = cin.h_count[node[0]] + cin.h_count[node[1]];
+ numhnodes++;
+ }
+
+ cin.numhnodes1[prev] = numhnodes-1;
+ }
+}
+
+/*
+==================
+Huff1Decompress
+==================
+*/
+cblock_t Huff1Decompress (cblock_t in)
+{
+ byte *input;
+ byte *out_p;
+ int nodenum;
+ int count;
+ cblock_t out;
+ int inbyte;
+ int *hnodes, *hnodesbase;
+//int i;
+
+ // get decompressed count
+ count = in.data[0] + (in.data[1]<<8) + (in.data[2]<<16) + (in.data[3]<<24);
+ input = in.data + 4;
+ out_p = out.data = Z_Malloc (count);
+
+ // read bits
+
+ hnodesbase = cin.hnodes1 - 256*2; // nodes 0-255 aren't stored
+
+ hnodes = hnodesbase;
+ nodenum = cin.numhnodes1[0];
+ while (count)
+ {
+ inbyte = *input++;
+ //-----------
+ if (nodenum < 256)
+ {
+ hnodes = hnodesbase + (nodenum<<9);
+ *out_p++ = nodenum;
+ if (!--count)
+ break;
+ nodenum = cin.numhnodes1[nodenum];
+ }
+ nodenum = hnodes[nodenum*2 + (inbyte&1)];
+ inbyte >>=1;
+ //-----------
+ if (nodenum < 256)
+ {
+ hnodes = hnodesbase + (nodenum<<9);
+ *out_p++ = nodenum;
+ if (!--count)
+ break;
+ nodenum = cin.numhnodes1[nodenum];
+ }
+ nodenum = hnodes[nodenum*2 + (inbyte&1)];
+ inbyte >>=1;
+ //-----------
+ if (nodenum < 256)
+ {
+ hnodes = hnodesbase + (nodenum<<9);
+ *out_p++ = nodenum;
+ if (!--count)
+ break;
+ nodenum = cin.numhnodes1[nodenum];
+ }
+ nodenum = hnodes[nodenum*2 + (inbyte&1)];
+ inbyte >>=1;
+ //-----------
+ if (nodenum < 256)
+ {
+ hnodes = hnodesbase + (nodenum<<9);
+ *out_p++ = nodenum;
+ if (!--count)
+ break;
+ nodenum = cin.numhnodes1[nodenum];
+ }
+ nodenum = hnodes[nodenum*2 + (inbyte&1)];
+ inbyte >>=1;
+ //-----------
+ if (nodenum < 256)
+ {
+ hnodes = hnodesbase + (nodenum<<9);
+ *out_p++ = nodenum;
+ if (!--count)
+ break;
+ nodenum = cin.numhnodes1[nodenum];
+ }
+ nodenum = hnodes[nodenum*2 + (inbyte&1)];
+ inbyte >>=1;
+ //-----------
+ if (nodenum < 256)
+ {
+ hnodes = hnodesbase + (nodenum<<9);
+ *out_p++ = nodenum;
+ if (!--count)
+ break;
+ nodenum = cin.numhnodes1[nodenum];
+ }
+ nodenum = hnodes[nodenum*2 + (inbyte&1)];
+ inbyte >>=1;
+ //-----------
+ if (nodenum < 256)
+ {
+ hnodes = hnodesbase + (nodenum<<9);
+ *out_p++ = nodenum;
+ if (!--count)
+ break;
+ nodenum = cin.numhnodes1[nodenum];
+ }
+ nodenum = hnodes[nodenum*2 + (inbyte&1)];
+ inbyte >>=1;
+ //-----------
+ if (nodenum < 256)
+ {
+ hnodes = hnodesbase + (nodenum<<9);
+ *out_p++ = nodenum;
+ if (!--count)
+ break;
+ nodenum = cin.numhnodes1[nodenum];
+ }
+ nodenum = hnodes[nodenum*2 + (inbyte&1)];
+ inbyte >>=1;
+ }
+
+ if (input - in.data != in.count && input - in.data != in.count+1)
+ {
+ Com_Printf ("Decompression overread by %i", (input - in.data) - in.count);
+ }
+ out.count = out_p - out.data;
+
+ return out;
+}
+
+/*
+==================
+SCR_ReadNextFrame
+==================
+*/
+byte *SCR_ReadNextFrame (void)
+{
+ int r;
+ int command;
+ byte samples[22050/14*4];
+ byte compressed[0x20000];
+ int size;
+ byte *pic;
+ cblock_t in, huf1;
+ int start, end, count;
+
+ // read the next frame
+ r = fread (&command, 4, 1, cl.cinematic_file);
+ if (r == 0) // we'll give it one more chance
+ r = fread (&command, 4, 1, cl.cinematic_file);
+
+ if (r != 1)
+ return NULL;
+ command = LittleLong(command);
+ if (command == 2)
+ return NULL; // last frame marker
+
+ if (command == 1)
+ { // read palette
+ FS_Read (cl.cinematicpalette, sizeof(cl.cinematicpalette), cl.cinematic_file);
+ cl.cinematicpalette_active=0; // dubious.... exposes an edge case
+ }
+
+ // decompress the next frame
+ FS_Read (&size, 4, cl.cinematic_file);
+ size = LittleLong(size);
+ if (size > sizeof(compressed) || size < 1)
+ Com_Error (ERR_DROP, "Bad compressed frame size");
+ FS_Read (compressed, size, cl.cinematic_file);
+
+ // read sound
+ start = cl.cinematicframe*cin.s_rate/14;
+ end = (cl.cinematicframe+1)*cin.s_rate/14;
+ count = end - start;
+
+ FS_Read (samples, count*cin.s_width*cin.s_channels, cl.cinematic_file);
+
+ S_RawSamples (count, cin.s_rate, cin.s_width, cin.s_channels, samples);
+
+ in.data = compressed;
+ in.count = size;
+
+ huf1 = Huff1Decompress (in);
+
+ pic = huf1.data;
+
+ cl.cinematicframe++;
+
+ return pic;
+}
+
+
+/*
+==================
+SCR_RunCinematic
+
+==================
+*/
+void SCR_RunCinematic (void)
+{
+ int frame;
+
+ if (cl.cinematictime <= 0)
+ {
+ SCR_StopCinematic ();
+ return;
+ }
+
+ if (cl.cinematicframe == -1)
+ return; // static image
+
+ if (cls.key_dest != key_game)
+ { // pause if menu or console is up
+ cl.cinematictime = cls.realtime - cl.cinematicframe*1000/14;
+ return;
+ }
+
+ frame = (cls.realtime - cl.cinematictime)*14.0/1000;
+ if (frame <= cl.cinematicframe)
+ return;
+ if (frame > cl.cinematicframe+1)
+ {
+ Com_Printf ("Dropped frame: %i > %i\n", frame, cl.cinematicframe+1);
+ cl.cinematictime = cls.realtime - cl.cinematicframe*1000/14;
+ }
+ if (cin.pic)
+ Z_Free (cin.pic);
+ cin.pic = cin.pic_pending;
+ cin.pic_pending = NULL;
+ cin.pic_pending = SCR_ReadNextFrame ();
+ if (!cin.pic_pending)
+ {
+ SCR_StopCinematic ();
+ SCR_FinishCinematic ();
+ cl.cinematictime = 1; // hack to get the black screen behind loading
+ SCR_BeginLoadingPlaque ();
+ cl.cinematictime = 0;
+ return;
+ }
+}
+
+/*
+==================
+SCR_DrawCinematic
+
+Returns true if a cinematic is active, meaning the view rendering
+should be skipped
+==================
+*/
+qboolean SCR_DrawCinematic (void)
+{
+ if (cl.cinematictime <= 0)
+ {
+ return false;
+ }
+
+ if (cls.key_dest == key_menu)
+ { // blank screen and pause if menu is up
+ re.CinematicSetPalette(NULL);
+ cl.cinematicpalette_active = false;
+ return true;
+ }
+
+ if (!cl.cinematicpalette_active)
+ {
+ re.CinematicSetPalette(cl.cinematicpalette);
+ cl.cinematicpalette_active = true;
+ }
+
+ if (!cin.pic)
+ return true;
+
+ re.DrawStretchRaw (0, 0, viddef.width, viddef.height,
+ cin.width, cin.height, cin.pic);
+
+ return true;
+}
+
+/*
+==================
+SCR_PlayCinematic
+
+==================
+*/
+void SCR_PlayCinematic (char *arg)
+{
+ int width, height;
+ byte *palette;
+ char name[MAX_OSPATH], *dot;
+ int old_khz;
+
+ // make sure CD isn't playing music
+ CDAudio_Stop();
+
+ cl.cinematicframe = 0;
+ dot = strstr (arg, ".");
+ if (dot && !strcmp (dot, ".pcx"))
+ { // static pcx image
+ Com_sprintf (name, sizeof(name), "pics/%s", arg);
+ SCR_LoadPCX (name, &cin.pic, &palette, &cin.width, &cin.height);
+ cl.cinematicframe = -1;
+ cl.cinematictime = 1;
+ SCR_EndLoadingPlaque ();
+ cls.state = ca_active;
+ if (!cin.pic)
+ {
+ Com_Printf ("%s not found.\n", name);
+ cl.cinematictime = 0;
+ }
+ else
+ {
+ memcpy (cl.cinematicpalette, palette, sizeof(cl.cinematicpalette));
+ Z_Free (palette);
+ }
+ return;
+ }
+
+ Com_sprintf (name, sizeof(name), "video/%s", arg);
+ FS_FOpenFile (name, &cl.cinematic_file);
+ if (!cl.cinematic_file)
+ {
+// Com_Error (ERR_DROP, "Cinematic %s not found.\n", name);
+ SCR_FinishCinematic ();
+ cl.cinematictime = 0; // done
+ return;
+ }
+
+ SCR_EndLoadingPlaque ();
+
+ cls.state = ca_active;
+
+ FS_Read (&width, 4, cl.cinematic_file);
+ FS_Read (&height, 4, cl.cinematic_file);
+ cin.width = LittleLong(width);
+ cin.height = LittleLong(height);
+
+ FS_Read (&cin.s_rate, 4, cl.cinematic_file);
+ cin.s_rate = LittleLong(cin.s_rate);
+ FS_Read (&cin.s_width, 4, cl.cinematic_file);
+ cin.s_width = LittleLong(cin.s_width);
+ FS_Read (&cin.s_channels, 4, cl.cinematic_file);
+ cin.s_channels = LittleLong(cin.s_channels);
+
+ Huff1TableInit ();
+
+ // switch up to 22 khz sound if necessary
+ old_khz = Cvar_VariableValue ("s_khz");
+ if (old_khz != cin.s_rate/1000)
+ {
+ cin.restart_sound = true;
+ Cvar_SetValue ("s_khz", cin.s_rate/1000);
+ CL_Snd_Restart_f ();
+ Cvar_SetValue ("s_khz", old_khz);
+ }
+
+ cl.cinematicframe = 0;
+ cin.pic = SCR_ReadNextFrame ();
+ cl.cinematictime = Sys_Milliseconds ();
+}
diff --git a/client/cl_ents.c b/client/cl_ents.c
new file mode 100644
index 0000000..5f2f16b
--- /dev/null
+++ b/client/cl_ents.c
@@ -0,0 +1,1500 @@
+/*
+Copyright (C) 1997-2001 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.
+
+*/
+// cl_ents.c -- entity parsing and management
+
+#include "client.h"
+
+
+extern struct model_s *cl_mod_powerscreen;
+
+//PGM
+int vidref_val;
+//PGM
+
+/*
+=========================================================================
+
+FRAME PARSING
+
+=========================================================================
+*/
+
+#if 0
+
+typedef struct
+{
+ int modelindex;
+ int num; // entity number
+ int effects;
+ vec3_t origin;
+ vec3_t oldorigin;
+ vec3_t angles;
+ qboolean present;
+} projectile_t;
+
+#define MAX_PROJECTILES 64
+projectile_t cl_projectiles[MAX_PROJECTILES];
+
+void CL_ClearProjectiles (void)
+{
+ int i;
+
+ for (i = 0; i < MAX_PROJECTILES; i++) {
+// if (cl_projectiles[i].present)
+// Com_DPrintf("PROJ: %d CLEARED\n", cl_projectiles[i].num);
+ cl_projectiles[i].present = false;
+ }
+}
+
+/*
+=====================
+CL_ParseProjectiles
+
+Flechettes are passed as efficient temporary entities
+=====================
+*/
+void CL_ParseProjectiles (void)
+{
+ int i, c, j;
+ byte bits[8];
+ byte b;
+ projectile_t pr;
+ int lastempty = -1;
+ qboolean old = false;
+
+ c = MSG_ReadByte (&net_message);
+ for (i=0 ; i<c ; i++)
+ {
+ bits[0] = MSG_ReadByte (&net_message);
+ bits[1] = MSG_ReadByte (&net_message);
+ bits[2] = MSG_ReadByte (&net_message);
+ bits[3] = MSG_ReadByte (&net_message);
+ bits[4] = MSG_ReadByte (&net_message);
+ pr.origin[0] = ( ( bits[0] + ((bits[1]&15)<<8) ) <<1) - 4096;
+ pr.origin[1] = ( ( (bits[1]>>4) + (bits[2]<<4) ) <<1) - 4096;
+ pr.origin[2] = ( ( bits[3] + ((bits[4]&15)<<8) ) <<1) - 4096;
+ VectorCopy(pr.origin, pr.oldorigin);
+
+ if (bits[4] & 64)
+ pr.effects = EF_BLASTER;
+ else
+ pr.effects = 0;
+
+ if (bits[4] & 128) {
+ old = true;
+ bits[0] = MSG_ReadByte (&net_message);
+ bits[1] = MSG_ReadByte (&net_message);
+ bits[2] = MSG_ReadByte (&net_message);
+ bits[3] = MSG_ReadByte (&net_message);
+ bits[4] = MSG_ReadByte (&net_message);
+ pr.oldorigin[0] = ( ( bits[0] + ((bits[1]&15)<<8) ) <<1) - 4096;
+ pr.oldorigin[1] = ( ( (bits[1]>>4) + (bits[2]<<4) ) <<1) - 4096;
+ pr.oldorigin[2] = ( ( bits[3] + ((bits[4]&15)<<8) ) <<1) - 4096;
+ }
+
+ bits[0] = MSG_ReadByte (&net_message);
+ bits[1] = MSG_ReadByte (&net_message);
+ bits[2] = MSG_ReadByte (&net_message);
+
+ pr.angles[0] = 360*bits[0]/256;
+ pr.angles[1] = 360*bits[1]/256;
+ pr.modelindex = bits[2];
+
+ b = MSG_ReadByte (&net_message);
+ pr.num = (b & 0x7f);
+ if (b & 128) // extra entity number byte
+ pr.num |= (MSG_ReadByte (&net_message) << 7);
+
+ pr.present = true;
+
+ // find if this projectile already exists from previous frame
+ for (j = 0; j < MAX_PROJECTILES; j++) {
+ if (cl_projectiles[j].modelindex) {
+ if (cl_projectiles[j].num == pr.num) {
+ // already present, set up oldorigin for interpolation
+ if (!old)
+ VectorCopy(cl_projectiles[j].origin, pr.oldorigin);
+ cl_projectiles[j] = pr;
+ break;
+ }
+ } else
+ lastempty = j;
+ }
+
+ // not present previous frame, add it
+ if (j == MAX_PROJECTILES) {
+ if (lastempty != -1) {
+ cl_projectiles[lastempty] = pr;
+ }
+ }
+ }
+}
+
+/*
+=============
+CL_LinkProjectiles
+
+=============
+*/
+void CL_AddProjectiles (void)
+{
+ int i, j;
+ projectile_t *pr;
+ entity_t ent;
+
+ memset (&ent, 0, sizeof(ent));
+
+ for (i=0, pr=cl_projectiles ; i < MAX_PROJECTILES ; i++, pr++)
+ {
+ // grab an entity to fill in
+ if (pr->modelindex < 1)
+ continue;
+ if (!pr->present) {
+ pr->modelindex = 0;
+ continue; // not present this frame (it was in the previous frame)
+ }
+
+ ent.model = cl.model_draw[pr->modelindex];
+
+ // interpolate origin
+ for (j=0 ; j<3 ; j++)
+ {
+ ent.origin[j] = ent.oldorigin[j] = pr->oldorigin[j] + cl.lerpfrac *
+ (pr->origin[j] - pr->oldorigin[j]);
+
+ }
+
+ if (pr->effects & EF_BLASTER)
+ CL_BlasterTrail (pr->oldorigin, ent.origin);
+ V_AddLight (pr->origin, 200, 1, 1, 0);
+
+ VectorCopy (pr->angles, ent.angles);
+ V_AddEntity (&ent);
+ }
+}
+#endif
+
+/*
+=================
+CL_ParseEntityBits
+
+Returns the entity number and the header bits
+=================
+*/
+int bitcounts[32]; /// just for protocol profiling
+int CL_ParseEntityBits (unsigned *bits)
+{
+ unsigned b, total;
+ int i;
+ int number;
+
+ total = MSG_ReadByte (&net_message);
+ if (total & U_MOREBITS1)
+ {
+ b = MSG_ReadByte (&net_message);
+ total |= b<<8;
+ }
+ if (total & U_MOREBITS2)
+ {
+ b = MSG_ReadByte (&net_message);
+ total |= b<<16;
+ }
+ if (total & U_MOREBITS3)
+ {
+ b = MSG_ReadByte (&net_message);
+ total |= b<<24;
+ }
+
+ // count the bits for net profiling
+ for (i=0 ; i<32 ; i++)
+ if (total&(1<<i))
+ bitcounts[i]++;
+
+ if (total & U_NUMBER16)
+ number = MSG_ReadShort (&net_message);
+ else
+ number = MSG_ReadByte (&net_message);
+
+ *bits = total;
+
+ return number;
+}
+
+/*
+==================
+CL_ParseDelta
+
+Can go from either a baseline or a previous packet_entity
+==================
+*/
+void CL_ParseDelta (entity_state_t *from, entity_state_t *to, int number, int bits)
+{
+ // set everything to the state we are delta'ing from
+ *to = *from;
+
+ VectorCopy (from->origin, to->old_origin);
+ to->number = number;
+
+ if (bits & U_MODEL)
+ to->modelindex = MSG_ReadByte (&net_message);
+ if (bits & U_MODEL2)
+ to->modelindex2 = MSG_ReadByte (&net_message);
+ if (bits & U_MODEL3)
+ to->modelindex3 = MSG_ReadByte (&net_message);
+ if (bits & U_MODEL4)
+ to->modelindex4 = MSG_ReadByte (&net_message);
+
+ if (bits & U_FRAME8)
+ to->frame = MSG_ReadByte (&net_message);
+ if (bits & U_FRAME16)
+ to->frame = MSG_ReadShort (&net_message);
+
+ if ((bits & U_SKIN8) && (bits & U_SKIN16)) //used for laser colors
+ to->skinnum = MSG_ReadLong(&net_message);
+ else if (bits & U_SKIN8)
+ to->skinnum = MSG_ReadByte(&net_message);
+ else if (bits & U_SKIN16)
+ to->skinnum = MSG_ReadShort(&net_message);
+
+ if ( (bits & (U_EFFECTS8|U_EFFECTS16)) == (U_EFFECTS8|U_EFFECTS16) )
+ to->effects = MSG_ReadLong(&net_message);
+ else if (bits & U_EFFECTS8)
+ to->effects = MSG_ReadByte(&net_message);
+ else if (bits & U_EFFECTS16)
+ to->effects = MSG_ReadShort(&net_message);
+
+ if ( (bits & (U_RENDERFX8|U_RENDERFX16)) == (U_RENDERFX8|U_RENDERFX16) )
+ to->renderfx = MSG_ReadLong(&net_message);
+ else if (bits & U_RENDERFX8)
+ to->renderfx = MSG_ReadByte(&net_message);
+ else if (bits & U_RENDERFX16)
+ to->renderfx = MSG_ReadShort(&net_message);
+
+ if (bits & U_ORIGIN1)
+ to->origin[0] = MSG_ReadCoord (&net_message);
+ if (bits & U_ORIGIN2)
+ to->origin[1] = MSG_ReadCoord (&net_message);
+ if (bits & U_ORIGIN3)
+ to->origin[2] = MSG_ReadCoord (&net_message);
+
+ if (bits & U_ANGLE1)
+ to->angles[0] = MSG_ReadAngle(&net_message);
+ if (bits & U_ANGLE2)
+ to->angles[1] = MSG_ReadAngle(&net_message);
+ if (bits & U_ANGLE3)
+ to->angles[2] = MSG_ReadAngle(&net_message);
+
+ if (bits & U_OLDORIGIN)
+ MSG_ReadPos (&net_message, to->old_origin);
+
+ if (bits & U_SOUND)
+ to->sound = MSG_ReadByte (&net_message);
+
+ if (bits & U_EVENT)
+ to->event = MSG_ReadByte (&net_message);
+ else
+ to->event = 0;
+
+ if (bits & U_SOLID)
+ to->solid = MSG_ReadShort (&net_message);
+}
+
+/*
+==================
+CL_DeltaEntity
+
+Parses deltas from the given base and adds the resulting entity
+to the current frame
+==================
+*/
+void CL_DeltaEntity (frame_t *frame, int newnum, entity_state_t *old, int bits)
+{
+ centity_t *ent;
+ entity_state_t *state;
+
+ ent = &cl_entities[newnum];
+
+ state = &cl_parse_entities[cl.parse_entities & (MAX_PARSE_ENTITIES-1)];
+ cl.parse_entities++;
+ frame->num_entities++;
+
+ CL_ParseDelta (old, state, newnum, bits);
+
+ // some data changes will force no lerping
+ if (state->modelindex != ent->current.modelindex
+ || state->modelindex2 != ent->current.modelindex2
+ || state->modelindex3 != ent->current.modelindex3
+ || state->modelindex4 != ent->current.modelindex4
+ || abs(state->origin[0] - ent->current.origin[0]) > 512
+ || abs(state->origin[1] - ent->current.origin[1]) > 512
+ || abs(state->origin[2] - ent->current.origin[2]) > 512
+ || state->event == EV_PLAYER_TELEPORT
+ || state->event == EV_OTHER_TELEPORT
+ )
+ {
+ ent->serverframe = -99;
+ }
+
+ if (ent->serverframe != cl.frame.serverframe - 1)
+ { // wasn't in last update, so initialize some things
+ ent->trailcount = 1024; // for diminishing rocket / grenade trails
+ // duplicate the current state so lerping doesn't hurt anything
+ ent->prev = *state;
+ if (state->event == EV_OTHER_TELEPORT)
+ {
+ VectorCopy (state->origin, ent->prev.origin);
+ VectorCopy (state->origin, ent->lerp_origin);
+ }
+ else
+ {
+ VectorCopy (state->old_origin, ent->prev.origin);
+ VectorCopy (state->old_origin, ent->lerp_origin);
+ }
+ }
+ else
+ { // shuffle the last state to previous
+ ent->prev = ent->current;
+ }
+
+ ent->serverframe = cl.frame.serverframe;
+ ent->current = *state;
+}
+
+/*
+==================
+CL_ParsePacketEntities
+
+An svc_packetentities has just been parsed, deal with the
+rest of the data stream.
+==================
+*/
+void CL_ParsePacketEntities (frame_t *oldframe, frame_t *newframe)
+{
+ int newnum;
+ int bits;
+ entity_state_t *oldstate;
+ int oldindex, oldnum;
+
+ newframe->parse_entities = cl.parse_entities;
+ newframe->num_entities = 0;
+
+ // delta from the entities present in oldframe
+ oldindex = 0;
+ if (!oldframe)
+ oldnum = 99999;
+ else
+ {
+ if (oldindex >= oldframe->num_entities)
+ oldnum = 99999;
+ else
+ {
+ oldstate = &cl_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];
+ oldnum = oldstate->number;
+ }
+ }
+
+ while (1)
+ {
+ newnum = CL_ParseEntityBits (&bits);
+ if (newnum >= MAX_EDICTS)
+ Com_Error (ERR_DROP,"CL_ParsePacketEntities: bad number:%i", newnum);
+
+ if (net_message.readcount > net_message.cursize)
+ Com_Error (ERR_DROP,"CL_ParsePacketEntities: end of message");
+
+ if (!newnum)
+ break;
+
+ while (oldnum < newnum)
+ { // one or more entities from the old packet are unchanged
+ if (cl_shownet->value == 3)
+ Com_Printf (" unchanged: %i\n", oldnum);
+ CL_DeltaEntity (newframe, oldnum, oldstate, 0);
+
+ oldindex++;
+
+ if (oldindex >= oldframe->num_entities)
+ oldnum = 99999;
+ else
+ {
+ oldstate = &cl_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];
+ oldnum = oldstate->number;
+ }
+ }
+
+ if (bits & U_REMOVE)
+ { // the entity present in oldframe is not in the current frame
+ if (cl_shownet->value == 3)
+ Com_Printf (" remove: %i\n", newnum);
+ if (oldnum != newnum)
+ Com_Printf ("U_REMOVE: oldnum != newnum\n");
+
+ oldindex++;
+
+ if (oldindex >= oldframe->num_entities)
+ oldnum = 99999;
+ else
+ {
+ oldstate = &cl_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];
+ oldnum = oldstate->number;
+ }
+ continue;
+ }
+
+ if (oldnum == newnum)
+ { // delta from previous state
+ if (cl_shownet->value == 3)
+ Com_Printf (" delta: %i\n", newnum);
+ CL_DeltaEntity (newframe, newnum, oldstate, bits);
+
+ oldindex++;
+
+ if (oldindex >= oldframe->num_entities)
+ oldnum = 99999;
+ else
+ {
+ oldstate = &cl_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];
+ oldnum = oldstate->number;
+ }
+ continue;
+ }
+
+ if (oldnum > newnum)
+ { // delta from baseline
+ if (cl_shownet->value == 3)
+ Com_Printf (" baseline: %i\n", newnum);
+ CL_DeltaEntity (newframe, newnum, &cl_entities[newnum].baseline, bits);
+ continue;
+ }
+
+ }
+
+ // any remaining entities in the old frame are copied over
+ while (oldnum != 99999)
+ { // one or more entities from the old packet are unchanged
+ if (cl_shownet->value == 3)
+ Com_Printf (" unchanged: %i\n", oldnum);
+ CL_DeltaEntity (newframe, oldnum, oldstate, 0);
+
+ oldindex++;
+
+ if (oldindex >= oldframe->num_entities)
+ oldnum = 99999;
+ else
+ {
+ oldstate = &cl_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];
+ oldnum = oldstate->number;
+ }
+ }
+}
+
+
+
+/*
+===================
+CL_ParsePlayerstate
+===================
+*/
+void CL_ParsePlayerstate (frame_t *oldframe, frame_t *newframe)
+{
+ int flags;
+ player_state_t *state;
+ int i;
+ int statbits;
+
+ state = &newframe->playerstate;
+
+ // clear to old value before delta parsing
+ if (oldframe)
+ *state = oldframe->playerstate;
+ else
+ memset (state, 0, sizeof(*state));
+
+ flags = MSG_ReadShort (&net_message);
+
+ //
+ // parse the pmove_state_t
+ //
+ if (flags & PS_M_TYPE)
+ state->pmove.pm_type = MSG_ReadByte (&net_message);
+
+ if (flags & PS_M_ORIGIN)
+ {
+ state->pmove.origin[0] = MSG_ReadShort (&net_message);
+ state->pmove.origin[1] = MSG_ReadShort (&net_message);
+ state->pmove.origin[2] = MSG_ReadShort (&net_message);
+ }
+
+ if (flags & PS_M_VELOCITY)
+ {
+ state->pmove.velocity[0] = MSG_ReadShort (&net_message);
+ state->pmove.velocity[1] = MSG_ReadShort (&net_message);
+ state->pmove.velocity[2] = MSG_ReadShort (&net_message);
+ }
+
+ if (flags & PS_M_TIME)
+ state->pmove.pm_time = MSG_ReadByte (&net_message);
+
+ if (flags & PS_M_FLAGS)
+ state->pmove.pm_flags = MSG_ReadByte (&net_message);
+
+ if (flags & PS_M_GRAVITY)
+ state->pmove.gravity = MSG_ReadShort (&net_message);
+
+ if (flags & PS_M_DELTA_ANGLES)
+ {
+ state->pmove.delta_angles[0] = MSG_ReadShort (&net_message);
+ state->pmove.delta_angles[1] = MSG_ReadShort (&net_message);
+ state->pmove.delta_angles[2] = MSG_ReadShort (&net_message);
+ }
+
+ if (cl.attractloop)
+ state->pmove.pm_type = PM_FREEZE; // demo playback
+
+ //
+ // parse the rest of the player_state_t
+ //
+ if (flags & PS_VIEWOFFSET)
+ {
+ state->viewoffset[0] = MSG_ReadChar (&net_message) * 0.25;
+ state->viewoffset[1] = MSG_ReadChar (&net_message) * 0.25;
+ state->viewoffset[2] = MSG_ReadChar (&net_message) * 0.25;
+ }
+
+ if (flags & PS_VIEWANGLES)
+ {
+ state->viewangles[0] = MSG_ReadAngle16 (&net_message);
+ state->viewangles[1] = MSG_ReadAngle16 (&net_message);
+ state->viewangles[2] = MSG_ReadAngle16 (&net_message);
+ }
+
+ if (flags & PS_KICKANGLES)
+ {
+ state->kick_angles[0] = MSG_ReadChar (&net_message) * 0.25;
+ state->kick_angles[1] = MSG_ReadChar (&net_message) * 0.25;
+ state->kick_angles[2] = MSG_ReadChar (&net_message) * 0.25;
+ }
+
+ if (flags & PS_WEAPONINDEX)
+ {
+ state->gunindex = MSG_ReadByte (&net_message);
+ }
+
+ if (flags & PS_WEAPONFRAME)
+ {
+ state->gunframe = MSG_ReadByte (&net_message);
+ state->gunoffset[0] = MSG_ReadChar (&net_message)*0.25;
+ state->gunoffset[1] = MSG_ReadChar (&net_message)*0.25;
+ state->gunoffset[2] = MSG_ReadChar (&net_message)*0.25;
+ state->gunangles[0] = MSG_ReadChar (&net_message)*0.25;
+ state->gunangles[1] = MSG_ReadChar (&net_message)*0.25;
+ state->gunangles[2] = MSG_ReadChar (&net_message)*0.25;
+ }
+
+ if (flags & PS_BLEND)
+ {
+ state->blend[0] = MSG_ReadByte (&net_message)/255.0;
+ state->blend[1] = MSG_ReadByte (&net_message)/255.0;
+ state->blend[2] = MSG_ReadByte (&net_message)/255.0;
+ state->blend[3] = MSG_ReadByte (&net_message)/255.0;
+ }
+
+ if (flags & PS_FOV)
+ state->fov = MSG_ReadByte (&net_message);
+
+ if (flags & PS_RDFLAGS)
+ state->rdflags = MSG_ReadByte (&net_message);
+
+ // parse stats
+ statbits = MSG_ReadLong (&net_message);
+ for (i=0 ; i<MAX_STATS ; i++)
+ if (statbits & (1<<i) )
+ state->stats[i] = MSG_ReadShort(&net_message);
+}
+
+
+/*
+==================
+CL_FireEntityEvents
+
+==================
+*/
+void CL_FireEntityEvents (frame_t *frame)
+{
+ entity_state_t *s1;
+ int pnum, num;
+
+ for (pnum = 0 ; pnum<frame->num_entities ; pnum++)
+ {
+ num = (frame->parse_entities + pnum)&(MAX_PARSE_ENTITIES-1);
+ s1 = &cl_parse_entities[num];
+ if (s1->event)
+ CL_EntityEvent (s1);
+
+ // EF_TELEPORTER acts like an event, but is not cleared each frame
+ if (s1->effects & EF_TELEPORTER)
+ CL_TeleporterParticles (s1);
+ }
+}
+
+
+/*
+================
+CL_ParseFrame
+================
+*/
+void CL_ParseFrame (void)
+{
+ int cmd;
+ int len;
+ frame_t *old;
+
+ memset (&cl.frame, 0, sizeof(cl.frame));
+
+#if 0
+ CL_ClearProjectiles(); // clear projectiles for new frame
+#endif
+
+ cl.frame.serverframe = MSG_ReadLong (&net_message);
+ cl.frame.deltaframe = MSG_ReadLong (&net_message);
+ cl.frame.servertime = cl.frame.serverframe*100;
+
+ // BIG HACK to let old demos continue to work
+ if (cls.serverProtocol != 26)
+ cl.surpressCount = MSG_ReadByte (&net_message);
+
+ if (cl_shownet->value == 3)
+ Com_Printf (" frame:%i delta:%i\n", cl.frame.serverframe,
+ cl.frame.deltaframe);
+
+ // If the frame is delta compressed from data that we
+ // no longer have available, we must suck up the rest of
+ // the frame, but not use it, then ask for a non-compressed
+ // message
+ if (cl.frame.deltaframe <= 0)
+ {
+ cl.frame.valid = true; // uncompressed frame
+ old = NULL;
+ cls.demowaiting = false; // we can start recording now
+ }
+ else
+ {
+ old = &cl.frames[cl.frame.deltaframe & UPDATE_MASK];
+ if (!old->valid)
+ { // should never happen
+ Com_Printf ("Delta from invalid frame (not supposed to happen!).\n");
+ }
+ if (old->serverframe != cl.frame.deltaframe)
+ { // The frame that the server did the delta from
+ // is too old, so we can't reconstruct it properly.
+ Com_Printf ("Delta frame too old.\n");
+ }
+ else if (cl.parse_entities - old->parse_entities > MAX_PARSE_ENTITIES-128)
+ {
+ Com_Printf ("Delta parse_entities too old.\n");
+ }
+ else
+ cl.frame.valid = true; // valid delta parse
+ }
+
+ // 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;
+
+ // read areabits
+ len = MSG_ReadByte (&net_message);
+ MSG_ReadData (&net_message, &cl.frame.areabits, len);
+
+ // read playerinfo
+ cmd = MSG_ReadByte (&net_message);
+ SHOWNET(svc_strings[cmd]);
+ if (cmd != svc_playerinfo)
+ Com_Error (ERR_DROP, "CL_ParseFrame: not playerinfo");
+ CL_ParsePlayerstate (old, &cl.frame);
+
+ // read packet entities
+ cmd = MSG_ReadByte (&net_message);
+ SHOWNET(svc_strings[cmd]);
+ if (cmd != svc_packetentities)
+ Com_Error (ERR_DROP, "CL_ParseFrame: not packetentities");
+ CL_ParsePacketEntities (old, &cl.frame);
+
+#if 0
+ if (cmd == svc_packetentities2)
+ CL_ParseProjectiles();
+#endif
+
+ // save the frame off in the backup array for later delta comparisons
+ cl.frames[cl.frame.serverframe & UPDATE_MASK] = cl.frame;
+
+ if (cl.frame.valid)
+ {
+ // getting a valid frame message ends the connection process
+ if (cls.state != ca_active)
+ {
+ cls.state = ca_active;
+ cl.force_refdef = true;
+ cl.predicted_origin[0] = cl.frame.playerstate.pmove.origin[0]*0.125;
+ cl.predicted_origin[1] = cl.frame.playerstate.pmove.origin[1]*0.125;
+ cl.predicted_origin[2] = cl.frame.playerstate.pmove.origin[2]*0.125;
+ VectorCopy (cl.frame.playerstate.viewangles, cl.predicted_angles);
+ if (cls.disable_servercount != cl.servercount
+ && cl.refresh_prepped)
+ SCR_EndLoadingPlaque (); // get rid of loading plaque
+ }
+ cl.sound_prepped = true; // can start mixing ambient sounds
+
+ // fire entity events
+ CL_FireEntityEvents (&cl.frame);
+ CL_CheckPredictionError ();
+ }
+}
+
+/*
+==========================================================================
+
+INTERPOLATE BETWEEN FRAMES TO GET RENDERING PARMS
+
+==========================================================================
+*/
+
+struct model_s *S_RegisterSexedModel (entity_state_t *ent, char *base)
+{
+ int n;
+ char *p;
+ struct model_s *mdl;
+ char model[MAX_QPATH];
+ char buffer[MAX_QPATH];
+
+ // determine what model the client is using
+ model[0] = 0;
+ n = CS_PLAYERSKINS + ent->number - 1;
+ if (cl.configstrings[n][0])
+ {
+ p = strchr(cl.configstrings[n], '\\');
+ if (p)
+ {
+ p += 1;
+ strcpy(model, p);
+ p = strchr(model, '/');
+ if (p)
+ *p = 0;
+ }
+ }
+ // if we can't figure it out, they're male
+ if (!model[0])
+ strcpy(model, "male");
+
+ Com_sprintf (buffer, sizeof(buffer), "players/%s/%s", model, base+1);
+ mdl = re.RegisterModel(buffer);
+ if (!mdl) {
+ // not found, try default weapon model
+ Com_sprintf (buffer, sizeof(buffer), "players/%s/weapon.md2", model);
+ mdl = re.RegisterModel(buffer);
+ if (!mdl) {
+ // no, revert to the male model
+ Com_sprintf (buffer, sizeof(buffer), "players/%s/%s", "male", base+1);
+ mdl = re.RegisterModel(buffer);
+ if (!mdl) {
+ // last try, default male weapon.md2
+ Com_sprintf (buffer, sizeof(buffer), "players/male/weapon.md2");
+ mdl = re.RegisterModel(buffer);
+ }
+ }
+ }
+
+ return mdl;
+}
+
+/*
+===============
+CL_AddPacketEntities
+
+===============
+*/
+void CL_AddPacketEntities (frame_t *frame)
+{
+ entity_t ent;
+ entity_state_t *s1;
+ float autorotate;
+ int i;
+ int pnum;
+ centity_t *cent;
+ int autoanim;
+ clientinfo_t *ci;
+ unsigned int effects, renderfx;
+
+ // bonus items rotate at a fixed rate
+ autorotate = anglemod(cl.time/10);
+
+ // brush models can auto animate their frames
+ autoanim = 2*cl.time/1000;
+
+ memset (&ent, 0, sizeof(ent));
+
+ for (pnum = 0 ; pnum<frame->num_entities ; pnum++)
+ {
+ s1 = &cl_parse_entities[(frame->parse_entities+pnum)&(MAX_PARSE_ENTITIES-1)];
+
+ cent = &cl_entities[s1->number];
+
+ effects = s1->effects;
+ renderfx = s1->renderfx;
+
+ // set frame
+ if (effects & EF_ANIM01)
+ ent.frame = autoanim & 1;
+ else if (effects & EF_ANIM23)
+ ent.frame = 2 + (autoanim & 1);
+ else if (effects & EF_ANIM_ALL)
+ ent.frame = autoanim;
+ else if (effects & EF_ANIM_ALLFAST)
+ ent.frame = cl.time / 100;
+ else
+ ent.frame = s1->frame;
+
+ // quad and pent can do different things on client
+ if (effects & EF_PENT)
+ {
+ effects &= ~EF_PENT;
+ effects |= EF_COLOR_SHELL;
+ renderfx |= RF_SHELL_RED;
+ }
+
+ if (effects & EF_QUAD)
+ {
+ effects &= ~EF_QUAD;
+ effects |= EF_COLOR_SHELL;
+ renderfx |= RF_SHELL_BLUE;
+ }
+//======
+// PMM
+ if (effects & EF_DOUBLE)
+ {
+ effects &= ~EF_DOUBLE;
+ effects |= EF_COLOR_SHELL;
+ renderfx |= RF_SHELL_DOUBLE;
+ }
+
+ if (effects & EF_HALF_DAMAGE)
+ {
+ effects &= ~EF_HALF_DAMAGE;
+ effects |= EF_COLOR_SHELL;
+ renderfx |= RF_SHELL_HALF_DAM;
+ }
+// pmm
+//======
+ ent.oldframe = cent->prev.frame;
+ ent.backlerp = 1.0 - cl.lerpfrac;
+
+ if (renderfx & (RF_FRAMELERP|RF_BEAM))
+ { // step origin discretely, because the frames
+ // do the animation properly
+ VectorCopy (cent->current.origin, ent.origin);
+ VectorCopy (cent->current.old_origin, ent.oldorigin);
+ }
+ else
+ { // interpolate origin
+ for (i=0 ; i<3 ; i++)
+ {
+ ent.origin[i] = ent.oldorigin[i] = cent->prev.origin[i] + cl.lerpfrac *
+ (cent->current.origin[i] - cent->prev.origin[i]);
+ }
+ }
+
+ // create a new entity
+
+ // tweak the color of beams
+ if ( renderfx & RF_BEAM )
+ { // the four beam colors are encoded in 32 bits of skinnum (hack)
+ ent.alpha = 0.30;
+ ent.skinnum = (s1->skinnum >> ((rand() % 4)*8)) & 0xff;
+ ent.model = NULL;
+ }
+ else
+ {
+ // set skin
+ if (s1->modelindex == 255)
+ { // use custom player skin
+ ent.skinnum = 0;
+ ci = &cl.clientinfo[s1->skinnum & 0xff];
+ ent.skin = ci->skin;
+ ent.model = ci->model;
+ if (!ent.skin || !ent.model)
+ {
+ ent.skin = cl.baseclientinfo.skin;
+ ent.model = cl.baseclientinfo.model;
+ }
+
+//============
+//PGM
+ if (renderfx & RF_USE_DISGUISE)
+ {
+ if(!strncmp((char *)ent.skin, "players/male", 12))
+ {
+ ent.skin = re.RegisterSkin ("players/male/disguise.pcx");
+ ent.model = re.RegisterModel ("players/male/tris.md2");
+ }
+ else if(!strncmp((char *)ent.skin, "players/female", 14))
+ {
+ ent.skin = re.RegisterSkin ("players/female/disguise.pcx");
+ ent.model = re.RegisterModel ("players/female/tris.md2");
+ }
+ else if(!strncmp((char *)ent.skin, "players/cyborg", 14))
+ {
+ ent.skin = re.RegisterSkin ("players/cyborg/disguise.pcx");
+ ent.model = re.RegisterModel ("players/cyborg/tris.md2");
+ }
+ }
+//PGM
+//============
+ }
+ else
+ {
+ ent.skinnum = s1->skinnum;
+ ent.skin = NULL;
+ ent.model = cl.model_draw[s1->modelindex];
+ }
+ }
+
+ // only used for black hole model right now, FIXME: do better
+ if (renderfx == RF_TRANSLUCENT)
+ ent.alpha = 0.70;
+
+ // render effects (fullbright, translucent, etc)
+ if ((effects & EF_COLOR_SHELL))
+ ent.flags = 0; // renderfx go on color shell entity
+ else
+ ent.flags = renderfx;
+
+ // calculate angles
+ if (effects & EF_ROTATE)
+ { // some bonus items auto-rotate
+ ent.angles[0] = 0;
+ ent.angles[1] = autorotate;
+ ent.angles[2] = 0;
+ }
+ // RAFAEL
+ else if (effects & EF_SPINNINGLIGHTS)
+ {
+ ent.angles[0] = 0;
+ ent.angles[1] = anglemod(cl.time/2) + s1->angles[1];
+ ent.angles[2] = 180;
+ {
+ vec3_t forward;
+ vec3_t start;
+
+ AngleVectors (ent.angles, forward, NULL, NULL);
+ VectorMA (ent.origin, 64, forward, start);
+ V_AddLight (start, 100, 1, 0, 0);
+ }
+ }
+ else
+ { // interpolate angles
+ float a1, a2;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ a1 = cent->current.angles[i];
+ a2 = cent->prev.angles[i];
+ ent.angles[i] = LerpAngle (a2, a1, cl.lerpfrac);
+ }
+ }
+
+ if (s1->number == cl.playernum+1)
+ {
+ ent.flags |= RF_VIEWERMODEL; // only draw from mirrors
+ // FIXME: still pass to refresh
+
+ if (effects & EF_FLAG1)
+ V_AddLight (ent.origin, 225, 1.0, 0.1, 0.1);
+ else if (effects & EF_FLAG2)
+ V_AddLight (ent.origin, 225, 0.1, 0.1, 1.0);
+ else if (effects & EF_TAGTRAIL) //PGM
+ V_AddLight (ent.origin, 225, 1.0, 1.0, 0.0); //PGM
+ else if (effects & EF_TRACKERTRAIL) //PGM
+ V_AddLight (ent.origin, 225, -1.0, -1.0, -1.0); //PGM
+
+ continue;
+ }
+
+ // if set to invisible, skip
+ if (!s1->modelindex)
+ continue;
+
+ if (effects & EF_BFG)
+ {
+ ent.flags |= RF_TRANSLUCENT;
+ ent.alpha = 0.30;
+ }
+
+ // RAFAEL
+ if (effects & EF_PLASMA)
+ {
+ ent.flags |= RF_TRANSLUCENT;
+ ent.alpha = 0.6;
+ }
+
+ if (effects & EF_SPHERETRANS)
+ {
+ ent.flags |= RF_TRANSLUCENT;
+ // PMM - *sigh* yet more EF overloading
+ if (effects & EF_TRACKERTRAIL)
+ ent.alpha = 0.6;
+ else
+ ent.alpha = 0.3;
+ }
+//pmm
+
+ // add to refresh list
+ V_AddEntity (&ent);
+
+ // color shells generate a seperate entity for the main model
+ if (effects & EF_COLOR_SHELL)
+ {
+ ent.flags = renderfx | RF_TRANSLUCENT;
+ ent.alpha = 0.30;
+ V_AddEntity (&ent);
+ }
+
+ ent.skin = NULL; // never use a custom skin on others
+ ent.skinnum = 0;
+ ent.flags = 0;
+ ent.alpha = 0;
+
+ // duplicate for linked models
+ if (s1->modelindex2)
+ {
+ if (s1->modelindex2 == 255)
+ { // custom weapon
+ ci = &cl.clientinfo[s1->skinnum & 0xff];
+ i = (s1->skinnum >> 8); // 0 is default weapon model
+ if (!cl_vwep->value || i > MAX_CLIENTWEAPONMODELS - 1)
+ i = 0;
+ ent.model = ci->weaponmodel[i];
+ if (!ent.model) {
+ if (i != 0)
+ ent.model = ci->weaponmodel[0];
+ if (!ent.model)
+ ent.model = cl.baseclientinfo.weaponmodel[0];
+ }
+ }
+ //PGM - hack to allow translucent linked models (defender sphere's shell)
+ // set the high bit 0x80 on modelindex2 to enable translucency
+ else if(s1->modelindex2 & 0x80)
+ {
+ ent.model = cl.model_draw[s1->modelindex2 & 0x7F];
+ ent.alpha = 0.32;
+ ent.flags = RF_TRANSLUCENT;
+ }
+ //PGM
+ else
+ ent.model = cl.model_draw[s1->modelindex2];
+ V_AddEntity (&ent);
+
+ //PGM - make sure these get reset.
+ ent.flags = 0;
+ ent.alpha = 0;
+ //PGM
+ }
+ if (s1->modelindex3)
+ {
+ ent.model = cl.model_draw[s1->modelindex3];
+ V_AddEntity (&ent);
+ }
+ if (s1->modelindex4)
+ {
+ ent.model = cl.model_draw[s1->modelindex4];
+ V_AddEntity (&ent);
+ }
+
+ if ( effects & EF_POWERSCREEN )
+ {
+ ent.model = cl_mod_powerscreen;
+ ent.oldframe = 0;
+ ent.frame = 0;
+ ent.flags |= (RF_TRANSLUCENT | RF_SHELL_GREEN);
+ ent.alpha = 0.30;
+ V_AddEntity (&ent);
+ }
+
+ // add automatic particle trails
+ if ( (effects&~EF_ROTATE) )
+ {
+ if (effects & EF_ROCKET)
+ {
+ CL_RocketTrail (cent->lerp_origin, ent.origin, cent);
+ V_AddLight (ent.origin, 200, 1, 1, 0);
+ }
+ // PGM - Do not reorder EF_BLASTER and EF_HYPERBLASTER.
+ // EF_BLASTER | EF_TRACKER is a special case for EF_BLASTER2... Cheese!
+ else if (effects & EF_BLASTER)
+ {
+// CL_BlasterTrail (cent->lerp_origin, ent.origin);
+//PGM
+ if (effects & EF_TRACKER) // lame... problematic?
+ {
+ CL_BlasterTrail2 (cent->lerp_origin, ent.origin);
+ V_AddLight (ent.origin, 200, 0, 1, 0);
+ }
+ else
+ {
+ CL_BlasterTrail (cent->lerp_origin, ent.origin);
+ V_AddLight (ent.origin, 200, 1, 1, 0);
+ }
+//PGM
+ }
+ else if (effects & EF_HYPERBLASTER)
+ {
+ if (effects & EF_TRACKER) // PGM overloaded for blaster2.
+ V_AddLight (ent.origin, 200, 0, 1, 0); // PGM
+ else // PGM
+ V_AddLight (ent.origin, 200, 1, 1, 0);
+ }
+ else if (effects & EF_GIB)
+ {
+ CL_DiminishingTrail (cent->lerp_origin, ent.origin, cent, effects);
+ }
+ else if (effects & EF_GRENADE)
+ {
+ CL_DiminishingTrail (cent->lerp_origin, ent.origin, cent, effects);
+ }
+ else if (effects & EF_FLIES)
+ {
+ CL_FlyEffect (cent, ent.origin);
+ }
+ else if (effects & EF_BFG)
+ {
+ static int bfg_lightramp[6] = {300, 400, 600, 300, 150, 75};
+
+ if (effects & EF_ANIM_ALLFAST)
+ {
+ CL_BfgParticles (&ent);
+ i = 200;
+ }
+ else
+ {
+ i = bfg_lightramp[s1->frame];
+ }
+ V_AddLight (ent.origin, i, 0, 1, 0);
+ }
+ // RAFAEL
+ else if (effects & EF_TRAP)
+ {
+ ent.origin[2] += 32;
+ CL_TrapParticles (&ent);
+ i = (rand()%100) + 100;
+ V_AddLight (ent.origin, i, 1, 0.8, 0.1);
+ }
+ else if (effects & EF_FLAG1)
+ {
+ CL_FlagTrail (cent->lerp_origin, ent.origin, 242);
+ V_AddLight (ent.origin, 225, 1, 0.1, 0.1);
+ }
+ else if (effects & EF_FLAG2)
+ {
+ CL_FlagTrail (cent->lerp_origin, ent.origin, 115);
+ V_AddLight (ent.origin, 225, 0.1, 0.1, 1);
+ }
+//======
+//ROGUE
+ else if (effects & EF_TAGTRAIL)
+ {
+ CL_TagTrail (cent->lerp_origin, ent.origin, 220);
+ V_AddLight (ent.origin, 225, 1.0, 1.0, 0.0);
+ }
+ else if (effects & EF_TRACKERTRAIL)
+ {
+ if (effects & EF_TRACKER)
+ {
+ float intensity;
+
+ intensity = 50 + (500 * (sin(cl.time/500.0) + 1.0));
+ // FIXME - check out this effect in rendition
+ if(vidref_val == VIDREF_GL)
+ V_AddLight (ent.origin, intensity, -1.0, -1.0, -1.0);
+ else
+ V_AddLight (ent.origin, -1.0 * intensity, 1.0, 1.0, 1.0);
+ }
+ else
+ {
+ CL_Tracker_Shell (cent->lerp_origin);
+ V_AddLight (ent.origin, 155, -1.0, -1.0, -1.0);
+ }
+ }
+ else if (effects & EF_TRACKER)
+ {
+ CL_TrackerTrail (cent->lerp_origin, ent.origin, 0);
+ // FIXME - check out this effect in rendition
+ if(vidref_val == VIDREF_GL)
+ V_AddLight (ent.origin, 200, -1, -1, -1);
+ else
+ V_AddLight (ent.origin, -200, 1, 1, 1);
+ }
+//ROGUE
+//======
+ // RAFAEL
+ else if (effects & EF_GREENGIB)
+ {
+ CL_DiminishingTrail (cent->lerp_origin, ent.origin, cent, effects);
+ }
+ // RAFAEL
+ else if (effects & EF_IONRIPPER)
+ {
+ CL_IonripperTrail (cent->lerp_origin, ent.origin);
+ V_AddLight (ent.origin, 100, 1, 0.5, 0.5);
+ }
+ // RAFAEL
+ else if (effects & EF_BLUEHYPERBLASTER)
+ {
+ V_AddLight (ent.origin, 200, 0, 0, 1);
+ }
+ // RAFAEL
+ else if (effects & EF_PLASMA)
+ {
+ if (effects & EF_ANIM_ALLFAST)
+ {
+ CL_BlasterTrail (cent->lerp_origin, ent.origin);
+ }
+ V_AddLight (ent.origin, 130, 1, 0.5, 0.5);
+ }
+ }
+
+ VectorCopy (ent.origin, cent->lerp_origin);
+ }
+}
+
+
+
+/*
+==============
+CL_AddViewWeapon
+==============
+*/
+void CL_AddViewWeapon (player_state_t *ps, player_state_t *ops)
+{
+ entity_t gun; // view model
+ int i;
+
+ // allow the gun to be completely removed
+ if (!cl_gun->value)
+ return;
+
+ // don't draw gun if in wide angle view
+ if (ps->fov > 90)
+ return;
+
+ memset (&gun, 0, sizeof(gun));
+
+ if (gun_model)
+ gun.model = gun_model; // development tool
+ else
+ gun.model = cl.model_draw[ps->gunindex];
+ if (!gun.model)
+ return;
+
+ // set up gun position
+ for (i=0 ; i<3 ; i++)
+ {
+ gun.origin[i] = cl.refdef.vieworg[i] + ops->gunoffset[i]
+ + cl.lerpfrac * (ps->gunoffset[i] - ops->gunoffset[i]);
+ gun.angles[i] = cl.refdef.viewangles[i] + LerpAngle (ops->gunangles[i],
+ ps->gunangles[i], cl.lerpfrac);
+ }
+
+ if (gun_frame)
+ {
+ gun.frame = gun_frame; // development tool
+ gun.oldframe = gun_frame; // development tool
+ }
+ else
+ {
+ gun.frame = ps->gunframe;
+ if (gun.frame == 0)
+ gun.oldframe = 0; // just changed weapons, don't lerp from old
+ else
+ gun.oldframe = ops->gunframe;
+ }
+
+ gun.flags = RF_MINLIGHT | RF_DEPTHHACK | RF_WEAPONMODEL;
+ gun.backlerp = 1.0 - cl.lerpfrac;
+ VectorCopy (gun.origin, gun.oldorigin); // don't lerp at all
+ V_AddEntity (&gun);
+}
+
+
+/*
+===============
+CL_CalcViewValues
+
+Sets cl.refdef view values
+===============
+*/
+void CL_CalcViewValues (void)
+{
+ int i;
+ float lerp, backlerp;
+ centity_t *ent;
+ frame_t *oldframe;
+ player_state_t *ps, *ops;
+
+ // find the previous frame to interpolate from
+ ps = &cl.frame.playerstate;
+ i = (cl.frame.serverframe - 1) & UPDATE_MASK;
+ oldframe = &cl.frames[i];
+ if (oldframe->serverframe != cl.frame.serverframe-1 || !oldframe->valid)
+ oldframe = &cl.frame; // previous frame was dropped or involid
+ ops = &oldframe->playerstate;
+
+ // see if the player entity was teleported this frame
+ if ( fabs(ops->pmove.origin[0] - ps->pmove.origin[0]) > 256*8
+ || abs(ops->pmove.origin[1] - ps->pmove.origin[1]) > 256*8
+ || abs(ops->pmove.origin[2] - ps->pmove.origin[2]) > 256*8)
+ ops = ps; // don't interpolate
+
+ ent = &cl_entities[cl.playernum+1];
+ lerp = cl.lerpfrac;
+
+ // calculate the origin
+ if ((cl_predict->value) && !(cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION))
+ { // use predicted values
+ unsigned delta;
+
+ backlerp = 1.0 - lerp;
+ for (i=0 ; i<3 ; i++)
+ {
+ cl.refdef.vieworg[i] = cl.predicted_origin[i] + ops->viewoffset[i]
+ + cl.lerpfrac * (ps->viewoffset[i] - ops->viewoffset[i])
+ - backlerp * cl.prediction_error[i];
+ }
+
+ // 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;
+ }
+ else
+ { // just use interpolated values
+ for (i=0 ; i<3 ; i++)
+ cl.refdef.vieworg[i] = ops->pmove.origin[i]*0.125 + ops->viewoffset[i]
+ + lerp * (ps->pmove.origin[i]*0.125 + ps->viewoffset[i]
+ - (ops->pmove.origin[i]*0.125 + ops->viewoffset[i]) );
+ }
+
+ // if not running a demo or on a locked frame, add the local angle movement
+ if ( cl.frame.playerstate.pmove.pm_type < PM_DEAD )
+ { // use predicted values
+ for (i=0 ; i<3 ; i++)
+ cl.refdef.viewangles[i] = cl.predicted_angles[i];
+ }
+ else
+ { // just use interpolated values
+ for (i=0 ; i<3 ; i++)
+ cl.refdef.viewangles[i] = LerpAngle (ops->viewangles[i], ps->viewangles[i], lerp);
+ }
+
+ 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);
+
+ // interpolate field of view
+ cl.refdef.fov_x = ops->fov + lerp * (ps->fov - ops->fov);
+
+ // don't interpolate blend color
+ for (i=0 ; i<4 ; i++)
+ cl.refdef.blend[i] = ps->blend[i];
+
+ // add the weapon
+ CL_AddViewWeapon (ps, ops);
+}
+
+/*
+===============
+CL_AddEntities
+
+Emits all entities, particles, and lights to the refresh
+===============
+*/
+void CL_AddEntities (void)
+{
+ if (cls.state != ca_active)
+ return;
+
+ if (cl.time > cl.frame.servertime)
+ {
+ if (cl_showclamp->value)
+ Com_Printf ("high clamp %i\n", cl.time - cl.frame.servertime);
+ cl.time = cl.frame.servertime;
+ cl.lerpfrac = 1.0;
+ }
+ else if (cl.time < cl.frame.servertime - 100)
+ {
+ if (cl_showclamp->value)
+ Com_Printf ("low clamp %i\n", cl.frame.servertime-100 - cl.time);
+ cl.time = cl.frame.servertime - 100;
+ cl.lerpfrac = 0;
+ }
+ else
+ cl.lerpfrac = 1.0 - (cl.frame.servertime - cl.time) * 0.01;
+
+ if (cl_timedemo->value)
+ cl.lerpfrac = 1.0;
+
+// CL_AddPacketEntities (&cl.frame);
+// CL_AddTEnts ();
+// CL_AddParticles ();
+// CL_AddDLights ();
+// CL_AddLightStyles ();
+
+ CL_CalcViewValues ();
+ // PMM - moved this here so the heat beam has the right values for the vieworg, and can lock the beam to the gun
+ CL_AddPacketEntities (&cl.frame);
+#if 0
+ CL_AddProjectiles ();
+#endif
+ CL_AddTEnts ();
+ CL_AddParticles ();
+ CL_AddDLights ();
+ CL_AddLightStyles ();
+}
+
+
+
+/*
+===============
+CL_GetEntitySoundOrigin
+
+Called to get the sound spatialization origin
+===============
+*/
+void CL_GetEntitySoundOrigin (int ent, vec3_t org)
+{
+ centity_t *old;
+
+ if (ent < 0 || ent >= MAX_EDICTS)
+ Com_Error (ERR_DROP, "CL_GetEntitySoundOrigin: bad ent");
+ old = &cl_entities[ent];
+ VectorCopy (old->lerp_origin, org);
+
+ // FIXME: bmodel issues...
+}
diff --git a/client/cl_fx.c b/client/cl_fx.c
new file mode 100644
index 0000000..32c7dec
--- /dev/null
+++ b/client/cl_fx.c
@@ -0,0 +1,2298 @@
+/*
+Copyright (C) 1997-2001 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.
+
+*/
+// cl_fx.c -- entity effects parsing and management
+
+#include "client.h"
+
+void CL_LogoutEffect (vec3_t org, int type);
+void CL_ItemRespawnParticles (vec3_t org);
+
+static vec3_t avelocities [NUMVERTEXNORMALS];
+
+extern struct model_s *cl_mod_smoke;
+extern struct model_s *cl_mod_flash;
+
+/*
+==============================================================
+
+LIGHT STYLE MANAGEMENT
+
+==============================================================
+*/
+
+typedef struct
+{
+ int length;
+ float value[3];
+ float map[MAX_QPATH];
+} clightstyle_t;
+
+clightstyle_t cl_lightstyle[MAX_LIGHTSTYLES];
+int lastofs;
+
+/*
+================
+CL_ClearLightStyles
+================
+*/
+void CL_ClearLightStyles (void)
+{
+ memset (cl_lightstyle, 0, sizeof(cl_lightstyle));
+ lastofs = -1;
+}
+
+/*
+================
+CL_RunLightStyles
+================
+*/
+void CL_RunLightStyles (void)
+{
+ int ofs;
+ int i;
+ clightstyle_t *ls;
+
+ ofs = cl.time / 100;
+ if (ofs == lastofs)
+ return;
+ lastofs = ofs;
+
+ for (i=0,ls=cl_lightstyle ; i<MAX_LIGHTSTYLES ; i++, ls++)
+ {
+ if (!ls->length)
+ {
+ ls->value[0] = ls->value[1] = ls->value[2] = 1.0;
+ continue;
+ }
+ if (ls->length == 1)
+ ls->value[0] = ls->value[1] = ls->value[2] = ls->map[0];
+ else
+ ls->value[0] = ls->value[1] = ls->value[2] = ls->map[ofs%ls->length];
+ }
+}
+
+
+void CL_SetLightstyle (int i)
+{
+ char *s;
+ int j, k;
+
+ s = cl.configstrings[i+CS_LIGHTS];
+
+ j = strlen (s);
+ if (j >= MAX_QPATH)
+ Com_Error (ERR_DROP, "svc_lightstyle length=%i", j);
+
+ cl_lightstyle[i].length = j;
+
+ for (k=0 ; k<j ; k++)
+ cl_lightstyle[i].map[k] = (float)(s[k]-'a')/(float)('m'-'a');
+}
+
+/*
+================
+CL_AddLightStyles
+================
+*/
+void CL_AddLightStyles (void)
+{
+ int i;
+ clightstyle_t *ls;
+
+ for (i=0,ls=cl_lightstyle ; i<MAX_LIGHTSTYLES ; i++, ls++)
+ V_AddLightStyle (i, ls->value[0], ls->value[1], ls->value[2]);
+}
+
+/*
+==============================================================
+
+DLIGHT MANAGEMENT
+
+==============================================================
+*/
+
+cdlight_t cl_dlights[MAX_DLIGHTS];
+
+/*
+================
+CL_ClearDlights
+================
+*/
+void CL_ClearDlights (void)
+{
+ memset (cl_dlights, 0, sizeof(cl_dlights));
+}
+
+/*
+===============
+CL_AllocDlight
+
+===============
+*/
+cdlight_t *CL_AllocDlight (int key)
+{
+ int i;
+ cdlight_t *dl;
+
+// first look for an exact key match
+ if (key)
+ {
+ dl = cl_dlights;
+ for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
+ {
+ if (dl->key == key)
+ {
+ memset (dl, 0, sizeof(*dl));
+ dl->key = key;
+ return dl;
+ }
+ }
+ }
+
+// then look for anything else
+ dl = cl_dlights;
+ for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
+ {
+ if (dl->die < cl.time)
+ {
+ memset (dl, 0, sizeof(*dl));
+ dl->key = key;
+ return dl;
+ }
+ }
+
+ dl = &cl_dlights[0];
+ memset (dl, 0, sizeof(*dl));
+ dl->key = key;
+ return dl;
+}
+
+/*
+===============
+CL_NewDlight
+===============
+*/
+void CL_NewDlight (int key, float x, float y, float z, float radius, float time)
+{
+ cdlight_t *dl;
+
+ dl = CL_AllocDlight (key);
+ dl->origin[0] = x;
+ dl->origin[1] = y;
+ dl->origin[2] = z;
+ dl->radius = radius;
+ dl->die = cl.time + time;
+}
+
+
+/*
+===============
+CL_RunDLights
+
+===============
+*/
+void CL_RunDLights (void)
+{
+ int i;
+ cdlight_t *dl;
+
+ dl = cl_dlights;
+ for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
+ {
+ if (!dl->radius)
+ continue;
+
+ if (dl->die < cl.time)
+ {
+ dl->radius = 0;
+ return;
+ }
+ dl->radius -= cls.frametime*dl->decay;
+ if (dl->radius < 0)
+ dl->radius = 0;
+ }
+}
+
+/*
+==============
+CL_ParseMuzzleFlash
+==============
+*/
+void CL_ParseMuzzleFlash (void)
+{
+ vec3_t fv, rv;
+ cdlight_t *dl;
+ int i, weapon;
+ centity_t *pl;
+ int silenced;
+ float volume;
+ char soundname[64];
+
+ i = MSG_ReadShort (&net_message);
+ if (i < 1 || i >= MAX_EDICTS)
+ Com_Error (ERR_DROP, "CL_ParseMuzzleFlash: bad entity");
+
+ weapon = MSG_ReadByte (&net_message);
+ silenced = weapon & MZ_SILENCED;
+ weapon &= ~MZ_SILENCED;
+
+ pl = &cl_entities[i];
+
+ dl = CL_AllocDlight (i);
+ VectorCopy (pl->current.origin, dl->origin);
+ AngleVectors (pl->current.angles, fv, rv, NULL);
+ VectorMA (dl->origin, 18, fv, dl->origin);
+ VectorMA (dl->origin, 16, rv, dl->origin);
+ if (silenced)
+ dl->radius = 100 + (rand()&31);
+ else
+ dl->radius = 200 + (rand()&31);
+ dl->minlight = 32;
+ dl->die = cl.time; // + 0.1;
+
+ if (silenced)
+ volume = 0.2;
+ else
+ volume = 1;
+
+ switch (weapon)
+ {
+ case MZ_BLASTER:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/blastf1a.wav"), volume, ATTN_NORM, 0);
+ break;
+ case MZ_BLUEHYPERBLASTER:
+ dl->color[0] = 0;dl->color[1] = 0;dl->color[2] = 1;
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/hyprbf1a.wav"), volume, ATTN_NORM, 0);
+ break;
+ case MZ_HYPERBLASTER:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/hyprbf1a.wav"), volume, ATTN_NORM, 0);
+ break;
+ case MZ_MACHINEGUN:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound(soundname), volume, ATTN_NORM, 0);
+ break;
+ case MZ_SHOTGUN:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/shotgf1b.wav"), volume, ATTN_NORM, 0);
+ S_StartSound (NULL, i, CHAN_AUTO, S_RegisterSound("weapons/shotgr1b.wav"), volume, ATTN_NORM, 0.1);
+ break;
+ case MZ_SSHOTGUN:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/sshotf1b.wav"), volume, ATTN_NORM, 0);
+ break;
+ case MZ_CHAINGUN1:
+ dl->radius = 200 + (rand()&31);
+ dl->color[0] = 1;dl->color[1] = 0.25;dl->color[2] = 0;
+ Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound(soundname), volume, ATTN_NORM, 0);
+ break;
+ case MZ_CHAINGUN2:
+ dl->radius = 225 + (rand()&31);
+ dl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0;
+ dl->die = cl.time + 0.1; // long delay
+ Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound(soundname), volume, ATTN_NORM, 0);
+ Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound(soundname), volume, ATTN_NORM, 0.05);
+ break;
+ case MZ_CHAINGUN3:
+ dl->radius = 250 + (rand()&31);
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ dl->die = cl.time + 0.1; // long delay
+ Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound(soundname), volume, ATTN_NORM, 0);
+ Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound(soundname), volume, ATTN_NORM, 0.033);
+ Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound(soundname), volume, ATTN_NORM, 0.066);
+ break;
+ case MZ_RAILGUN:
+ dl->color[0] = 0.5;dl->color[1] = 0.5;dl->color[2] = 1.0;
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/railgf1a.wav"), volume, ATTN_NORM, 0);
+ break;
+ case MZ_ROCKET:
+ dl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0.2;
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/rocklf1a.wav"), volume, ATTN_NORM, 0);
+ S_StartSound (NULL, i, CHAN_AUTO, S_RegisterSound("weapons/rocklr1b.wav"), volume, ATTN_NORM, 0.1);
+ break;
+ case MZ_GRENADE:
+ dl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0;
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/grenlf1a.wav"), volume, ATTN_NORM, 0);
+ S_StartSound (NULL, i, CHAN_AUTO, S_RegisterSound("weapons/grenlr1b.wav"), volume, ATTN_NORM, 0.1);
+ break;
+ case MZ_BFG:
+ dl->color[0] = 0;dl->color[1] = 1;dl->color[2] = 0;
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/bfg__f1y.wav"), volume, ATTN_NORM, 0);
+ break;
+
+ case MZ_LOGIN:
+ dl->color[0] = 0;dl->color[1] = 1; dl->color[2] = 0;
+ dl->die = cl.time + 1.0;
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0);
+ CL_LogoutEffect (pl->current.origin, weapon);
+ break;
+ case MZ_LOGOUT:
+ dl->color[0] = 1;dl->color[1] = 0; dl->color[2] = 0;
+ dl->die = cl.time + 1.0;
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0);
+ CL_LogoutEffect (pl->current.origin, weapon);
+ break;
+ case MZ_RESPAWN:
+ dl->color[0] = 1;dl->color[1] = 1; dl->color[2] = 0;
+ dl->die = cl.time + 1.0;
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0);
+ CL_LogoutEffect (pl->current.origin, weapon);
+ break;
+ // RAFAEL
+ case MZ_PHALANX:
+ dl->color[0] = 1;dl->color[1] = 0.5; dl->color[2] = 0.5;
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/plasshot.wav"), volume, ATTN_NORM, 0);
+ break;
+ // RAFAEL
+ case MZ_IONRIPPER:
+ dl->color[0] = 1;dl->color[1] = 0.5; dl->color[2] = 0.5;
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/rippfire.wav"), volume, ATTN_NORM, 0);
+ break;
+
+// ======================
+// PGM
+ case MZ_ETF_RIFLE:
+ dl->color[0] = 0.9;dl->color[1] = 0.7;dl->color[2] = 0;
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/nail1.wav"), volume, ATTN_NORM, 0);
+ break;
+ case MZ_SHOTGUN2:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/shotg2.wav"), volume, ATTN_NORM, 0);
+ break;
+ case MZ_HEATBEAM:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ dl->die = cl.time + 100;
+// S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/bfg__l1a.wav"), volume, ATTN_NORM, 0);
+ break;
+ case MZ_BLASTER2:
+ dl->color[0] = 0;dl->color[1] = 1;dl->color[2] = 0;
+ // FIXME - different sound for blaster2 ??
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/blastf1a.wav"), volume, ATTN_NORM, 0);
+ break;
+ case MZ_TRACKER:
+ // negative flashes handled the same in gl/soft until CL_AddDLights
+ dl->color[0] = -1;dl->color[1] = -1;dl->color[2] = -1;
+ S_StartSound (NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/disint2.wav"), volume, ATTN_NORM, 0);
+ break;
+ case MZ_NUKE1:
+ dl->color[0] = 1;dl->color[1] = 0;dl->color[2] = 0;
+ dl->die = cl.time + 100;
+ break;
+ case MZ_NUKE2:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ dl->die = cl.time + 100;
+ break;
+ case MZ_NUKE4:
+ dl->color[0] = 0;dl->color[1] = 0;dl->color[2] = 1;
+ dl->die = cl.time + 100;
+ break;
+ case MZ_NUKE8:
+ dl->color[0] = 0;dl->color[1] = 1;dl->color[2] = 1;
+ dl->die = cl.time + 100;
+ break;
+// PGM
+// ======================
+ }
+}
+
+
+/*
+==============
+CL_ParseMuzzleFlash2
+==============
+*/
+void CL_ParseMuzzleFlash2 (void)
+{
+ int ent;
+ vec3_t origin;
+ int flash_number;
+ cdlight_t *dl;
+ vec3_t forward, right;
+ char soundname[64];
+
+ ent = MSG_ReadShort (&net_message);
+ if (ent < 1 || ent >= MAX_EDICTS)
+ Com_Error (ERR_DROP, "CL_ParseMuzzleFlash2: bad entity");
+
+ flash_number = MSG_ReadByte (&net_message);
+
+ // locate the origin
+ AngleVectors (cl_entities[ent].current.angles, forward, right, NULL);
+ origin[0] = cl_entities[ent].current.origin[0] + forward[0] * monster_flash_offset[flash_number][0] + right[0] * monster_flash_offset[flash_number][1];
+ origin[1] = cl_entities[ent].current.origin[1] + forward[1] * monster_flash_offset[flash_number][0] + right[1] * monster_flash_offset[flash_number][1];
+ origin[2] = cl_entities[ent].current.origin[2] + forward[2] * monster_flash_offset[flash_number][0] + right[2] * monster_flash_offset[flash_number][1] + monster_flash_offset[flash_number][2];
+
+ dl = CL_AllocDlight (ent);
+ VectorCopy (origin, dl->origin);
+ dl->radius = 200 + (rand()&31);
+ dl->minlight = 32;
+ dl->die = cl.time; // + 0.1;
+
+ switch (flash_number)
+ {
+ case MZ2_INFANTRY_MACHINEGUN_1:
+ case MZ2_INFANTRY_MACHINEGUN_2:
+ case MZ2_INFANTRY_MACHINEGUN_3:
+ case MZ2_INFANTRY_MACHINEGUN_4:
+ case MZ2_INFANTRY_MACHINEGUN_5:
+ case MZ2_INFANTRY_MACHINEGUN_6:
+ case MZ2_INFANTRY_MACHINEGUN_7:
+ case MZ2_INFANTRY_MACHINEGUN_8:
+ case MZ2_INFANTRY_MACHINEGUN_9:
+ case MZ2_INFANTRY_MACHINEGUN_10:
+ case MZ2_INFANTRY_MACHINEGUN_11:
+ case MZ2_INFANTRY_MACHINEGUN_12:
+ case MZ2_INFANTRY_MACHINEGUN_13:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ CL_ParticleEffect (origin, vec3_origin, 0, 40);
+ CL_SmokeAndFlash(origin);
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("infantry/infatck1.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_SOLDIER_MACHINEGUN_1:
+ case MZ2_SOLDIER_MACHINEGUN_2:
+ case MZ2_SOLDIER_MACHINEGUN_3:
+ case MZ2_SOLDIER_MACHINEGUN_4:
+ case MZ2_SOLDIER_MACHINEGUN_5:
+ case MZ2_SOLDIER_MACHINEGUN_6:
+ case MZ2_SOLDIER_MACHINEGUN_7:
+ case MZ2_SOLDIER_MACHINEGUN_8:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ CL_ParticleEffect (origin, vec3_origin, 0, 40);
+ CL_SmokeAndFlash(origin);
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("soldier/solatck3.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_GUNNER_MACHINEGUN_1:
+ case MZ2_GUNNER_MACHINEGUN_2:
+ case MZ2_GUNNER_MACHINEGUN_3:
+ case MZ2_GUNNER_MACHINEGUN_4:
+ case MZ2_GUNNER_MACHINEGUN_5:
+ case MZ2_GUNNER_MACHINEGUN_6:
+ case MZ2_GUNNER_MACHINEGUN_7:
+ case MZ2_GUNNER_MACHINEGUN_8:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ CL_ParticleEffect (origin, vec3_origin, 0, 40);
+ CL_SmokeAndFlash(origin);
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("gunner/gunatck2.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_ACTOR_MACHINEGUN_1:
+ case MZ2_SUPERTANK_MACHINEGUN_1:
+ case MZ2_SUPERTANK_MACHINEGUN_2:
+ case MZ2_SUPERTANK_MACHINEGUN_3:
+ case MZ2_SUPERTANK_MACHINEGUN_4:
+ case MZ2_SUPERTANK_MACHINEGUN_5:
+ case MZ2_SUPERTANK_MACHINEGUN_6:
+ case MZ2_TURRET_MACHINEGUN: // PGM
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+
+ CL_ParticleEffect (origin, vec3_origin, 0, 40);
+ CL_SmokeAndFlash(origin);
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("infantry/infatck1.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_BOSS2_MACHINEGUN_L1:
+ case MZ2_BOSS2_MACHINEGUN_L2:
+ case MZ2_BOSS2_MACHINEGUN_L3:
+ case MZ2_BOSS2_MACHINEGUN_L4:
+ case MZ2_BOSS2_MACHINEGUN_L5:
+ case MZ2_CARRIER_MACHINEGUN_L1: // PMM
+ case MZ2_CARRIER_MACHINEGUN_L2: // PMM
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+
+ CL_ParticleEffect (origin, vec3_origin, 0, 40);
+ CL_SmokeAndFlash(origin);
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("infantry/infatck1.wav"), 1, ATTN_NONE, 0);
+ break;
+
+ case MZ2_SOLDIER_BLASTER_1:
+ case MZ2_SOLDIER_BLASTER_2:
+ case MZ2_SOLDIER_BLASTER_3:
+ case MZ2_SOLDIER_BLASTER_4:
+ case MZ2_SOLDIER_BLASTER_5:
+ case MZ2_SOLDIER_BLASTER_6:
+ case MZ2_SOLDIER_BLASTER_7:
+ case MZ2_SOLDIER_BLASTER_8:
+ case MZ2_TURRET_BLASTER: // PGM
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("soldier/solatck2.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_FLYER_BLASTER_1:
+ case MZ2_FLYER_BLASTER_2:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("flyer/flyatck3.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_MEDIC_BLASTER_1:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("medic/medatck1.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_HOVER_BLASTER_1:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("hover/hovatck1.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_FLOAT_BLASTER_1:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("floater/fltatck1.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_SOLDIER_SHOTGUN_1:
+ case MZ2_SOLDIER_SHOTGUN_2:
+ case MZ2_SOLDIER_SHOTGUN_3:
+ case MZ2_SOLDIER_SHOTGUN_4:
+ case MZ2_SOLDIER_SHOTGUN_5:
+ case MZ2_SOLDIER_SHOTGUN_6:
+ case MZ2_SOLDIER_SHOTGUN_7:
+ case MZ2_SOLDIER_SHOTGUN_8:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ CL_SmokeAndFlash(origin);
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("soldier/solatck1.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_TANK_BLASTER_1:
+ case MZ2_TANK_BLASTER_2:
+ case MZ2_TANK_BLASTER_3:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/tnkatck3.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_TANK_MACHINEGUN_1:
+ case MZ2_TANK_MACHINEGUN_2:
+ case MZ2_TANK_MACHINEGUN_3:
+ case MZ2_TANK_MACHINEGUN_4:
+ case MZ2_TANK_MACHINEGUN_5:
+ case MZ2_TANK_MACHINEGUN_6:
+ case MZ2_TANK_MACHINEGUN_7:
+ case MZ2_TANK_MACHINEGUN_8:
+ case MZ2_TANK_MACHINEGUN_9:
+ case MZ2_TANK_MACHINEGUN_10:
+ case MZ2_TANK_MACHINEGUN_11:
+ case MZ2_TANK_MACHINEGUN_12:
+ case MZ2_TANK_MACHINEGUN_13:
+ case MZ2_TANK_MACHINEGUN_14:
+ case MZ2_TANK_MACHINEGUN_15:
+ case MZ2_TANK_MACHINEGUN_16:
+ case MZ2_TANK_MACHINEGUN_17:
+ case MZ2_TANK_MACHINEGUN_18:
+ case MZ2_TANK_MACHINEGUN_19:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ CL_ParticleEffect (origin, vec3_origin, 0, 40);
+ CL_SmokeAndFlash(origin);
+ Com_sprintf(soundname, sizeof(soundname), "tank/tnkatk2%c.wav", 'a' + rand() % 5);
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound(soundname), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_CHICK_ROCKET_1:
+ case MZ2_TURRET_ROCKET: // PGM
+ dl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0.2;
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("chick/chkatck2.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_TANK_ROCKET_1:
+ case MZ2_TANK_ROCKET_2:
+ case MZ2_TANK_ROCKET_3:
+ dl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0.2;
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/tnkatck1.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_SUPERTANK_ROCKET_1:
+ case MZ2_SUPERTANK_ROCKET_2:
+ case MZ2_SUPERTANK_ROCKET_3:
+ case MZ2_BOSS2_ROCKET_1:
+ case MZ2_BOSS2_ROCKET_2:
+ case MZ2_BOSS2_ROCKET_3:
+ case MZ2_BOSS2_ROCKET_4:
+ case MZ2_CARRIER_ROCKET_1:
+// case MZ2_CARRIER_ROCKET_2:
+// case MZ2_CARRIER_ROCKET_3:
+// case MZ2_CARRIER_ROCKET_4:
+ dl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0.2;
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/rocket.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_GUNNER_GRENADE_1:
+ case MZ2_GUNNER_GRENADE_2:
+ case MZ2_GUNNER_GRENADE_3:
+ case MZ2_GUNNER_GRENADE_4:
+ dl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0;
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("gunner/gunatck3.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_GLADIATOR_RAILGUN_1:
+ // PMM
+ case MZ2_CARRIER_RAILGUN:
+ case MZ2_WIDOW_RAIL:
+ // pmm
+ dl->color[0] = 0.5;dl->color[1] = 0.5;dl->color[2] = 1.0;
+ break;
+
+// --- Xian's shit starts ---
+ case MZ2_MAKRON_BFG:
+ dl->color[0] = 0.5;dl->color[1] = 1 ;dl->color[2] = 0.5;
+ //S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("makron/bfg_fire.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_MAKRON_BLASTER_1:
+ case MZ2_MAKRON_BLASTER_2:
+ case MZ2_MAKRON_BLASTER_3:
+ case MZ2_MAKRON_BLASTER_4:
+ case MZ2_MAKRON_BLASTER_5:
+ case MZ2_MAKRON_BLASTER_6:
+ case MZ2_MAKRON_BLASTER_7:
+ case MZ2_MAKRON_BLASTER_8:
+ case MZ2_MAKRON_BLASTER_9:
+ case MZ2_MAKRON_BLASTER_10:
+ case MZ2_MAKRON_BLASTER_11:
+ case MZ2_MAKRON_BLASTER_12:
+ case MZ2_MAKRON_BLASTER_13:
+ case MZ2_MAKRON_BLASTER_14:
+ case MZ2_MAKRON_BLASTER_15:
+ case MZ2_MAKRON_BLASTER_16:
+ case MZ2_MAKRON_BLASTER_17:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("makron/blaster.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_JORG_MACHINEGUN_L1:
+ case MZ2_JORG_MACHINEGUN_L2:
+ case MZ2_JORG_MACHINEGUN_L3:
+ case MZ2_JORG_MACHINEGUN_L4:
+ case MZ2_JORG_MACHINEGUN_L5:
+ case MZ2_JORG_MACHINEGUN_L6:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ CL_ParticleEffect (origin, vec3_origin, 0, 40);
+ CL_SmokeAndFlash(origin);
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("boss3/xfire.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_JORG_MACHINEGUN_R1:
+ case MZ2_JORG_MACHINEGUN_R2:
+ case MZ2_JORG_MACHINEGUN_R3:
+ case MZ2_JORG_MACHINEGUN_R4:
+ case MZ2_JORG_MACHINEGUN_R5:
+ case MZ2_JORG_MACHINEGUN_R6:
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ CL_ParticleEffect (origin, vec3_origin, 0, 40);
+ CL_SmokeAndFlash(origin);
+ break;
+
+ case MZ2_JORG_BFG_1:
+ dl->color[0] = 0.5;dl->color[1] = 1 ;dl->color[2] = 0.5;
+ break;
+
+ case MZ2_BOSS2_MACHINEGUN_R1:
+ case MZ2_BOSS2_MACHINEGUN_R2:
+ case MZ2_BOSS2_MACHINEGUN_R3:
+ case MZ2_BOSS2_MACHINEGUN_R4:
+ case MZ2_BOSS2_MACHINEGUN_R5:
+ case MZ2_CARRIER_MACHINEGUN_R1: // PMM
+ case MZ2_CARRIER_MACHINEGUN_R2: // PMM
+
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+
+ CL_ParticleEffect (origin, vec3_origin, 0, 40);
+ CL_SmokeAndFlash(origin);
+ break;
+
+// ======
+// ROGUE
+ case MZ2_STALKER_BLASTER:
+ case MZ2_DAEDALUS_BLASTER:
+ case MZ2_MEDIC_BLASTER_2:
+ case MZ2_WIDOW_BLASTER:
+ case MZ2_WIDOW_BLASTER_SWEEP1:
+ case MZ2_WIDOW_BLASTER_SWEEP2:
+ case MZ2_WIDOW_BLASTER_SWEEP3:
+ case MZ2_WIDOW_BLASTER_SWEEP4:
+ case MZ2_WIDOW_BLASTER_SWEEP5:
+ case MZ2_WIDOW_BLASTER_SWEEP6:
+ case MZ2_WIDOW_BLASTER_SWEEP7:
+ case MZ2_WIDOW_BLASTER_SWEEP8:
+ case MZ2_WIDOW_BLASTER_SWEEP9:
+ case MZ2_WIDOW_BLASTER_100:
+ case MZ2_WIDOW_BLASTER_90:
+ case MZ2_WIDOW_BLASTER_80:
+ case MZ2_WIDOW_BLASTER_70:
+ case MZ2_WIDOW_BLASTER_60:
+ case MZ2_WIDOW_BLASTER_50:
+ case MZ2_WIDOW_BLASTER_40:
+ case MZ2_WIDOW_BLASTER_30:
+ case MZ2_WIDOW_BLASTER_20:
+ case MZ2_WIDOW_BLASTER_10:
+ case MZ2_WIDOW_BLASTER_0:
+ case MZ2_WIDOW_BLASTER_10L:
+ case MZ2_WIDOW_BLASTER_20L:
+ case MZ2_WIDOW_BLASTER_30L:
+ case MZ2_WIDOW_BLASTER_40L:
+ case MZ2_WIDOW_BLASTER_50L:
+ case MZ2_WIDOW_BLASTER_60L:
+ case MZ2_WIDOW_BLASTER_70L:
+ case MZ2_WIDOW_RUN_1:
+ case MZ2_WIDOW_RUN_2:
+ case MZ2_WIDOW_RUN_3:
+ case MZ2_WIDOW_RUN_4:
+ case MZ2_WIDOW_RUN_5:
+ case MZ2_WIDOW_RUN_6:
+ case MZ2_WIDOW_RUN_7:
+ case MZ2_WIDOW_RUN_8:
+ dl->color[0] = 0;dl->color[1] = 1;dl->color[2] = 0;
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/tnkatck3.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_WIDOW_DISRUPTOR:
+ dl->color[0] = -1;dl->color[1] = -1;dl->color[2] = -1;
+ S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/disint2.wav"), 1, ATTN_NORM, 0);
+ break;
+
+ case MZ2_WIDOW_PLASMABEAM:
+ case MZ2_WIDOW2_BEAMER_1:
+ case MZ2_WIDOW2_BEAMER_2:
+ case MZ2_WIDOW2_BEAMER_3:
+ case MZ2_WIDOW2_BEAMER_4:
+ case MZ2_WIDOW2_BEAMER_5:
+ case MZ2_WIDOW2_BEAM_SWEEP_1:
+ case MZ2_WIDOW2_BEAM_SWEEP_2:
+ case MZ2_WIDOW2_BEAM_SWEEP_3:
+ case MZ2_WIDOW2_BEAM_SWEEP_4:
+ case MZ2_WIDOW2_BEAM_SWEEP_5:
+ case MZ2_WIDOW2_BEAM_SWEEP_6:
+ case MZ2_WIDOW2_BEAM_SWEEP_7:
+ case MZ2_WIDOW2_BEAM_SWEEP_8:
+ case MZ2_WIDOW2_BEAM_SWEEP_9:
+ case MZ2_WIDOW2_BEAM_SWEEP_10:
+ case MZ2_WIDOW2_BEAM_SWEEP_11:
+ dl->radius = 300 + (rand()&100);
+ dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
+ dl->die = cl.time + 200;
+ break;
+// ROGUE
+// ======
+
+// --- Xian's shit ends ---
+
+ }
+}
+
+
+/*
+===============
+CL_AddDLights
+
+===============
+*/
+void CL_AddDLights (void)
+{
+ int i;
+ cdlight_t *dl;
+
+ dl = cl_dlights;
+
+//=====
+//PGM
+ if(vidref_val == VIDREF_GL)
+ {
+ for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
+ {
+ if (!dl->radius)
+ continue;
+ V_AddLight (dl->origin, dl->radius,
+ dl->color[0], dl->color[1], dl->color[2]);
+ }
+ }
+ else
+ {
+ for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
+ {
+ if (!dl->radius)
+ continue;
+
+ // negative light in software. only black allowed
+ if ((dl->color[0] < 0) || (dl->color[1] < 0) || (dl->color[2] < 0))
+ {
+ dl->radius = -(dl->radius);
+ dl->color[0] = 1;
+ dl->color[1] = 1;
+ dl->color[2] = 1;
+ }
+ V_AddLight (dl->origin, dl->radius,
+ dl->color[0], dl->color[1], dl->color[2]);
+ }
+ }
+//PGM
+//=====
+}
+
+
+
+/*
+==============================================================
+
+PARTICLE MANAGEMENT
+
+==============================================================
+*/
+
+/*
+// THIS HAS BEEN RELOCATED TO CLIENT.H
+typedef struct particle_s
+{
+ struct particle_s *next;
+
+ float time;
+
+ vec3_t org;
+ vec3_t vel;
+ vec3_t accel;
+ float color;
+ float colorvel;
+ float alpha;
+ float alphavel;
+} cparticle_t;
+
+
+#define PARTICLE_GRAVITY 40
+*/
+
+cparticle_t *active_particles, *free_particles;
+
+cparticle_t particles[MAX_PARTICLES];
+int cl_numparticles = MAX_PARTICLES;
+
+
+/*
+===============
+CL_ClearParticles
+===============
+*/
+void CL_ClearParticles (void)
+{
+ int i;
+
+ free_particles = &particles[0];
+ active_particles = NULL;
+
+ for (i=0 ;i<cl_numparticles ; i++)
+ particles[i].next = &particles[i+1];
+ particles[cl_numparticles-1].next = NULL;
+}
+
+
+/*
+===============
+CL_ParticleEffect
+
+Wall impact puffs
+===============
+*/
+void CL_ParticleEffect (vec3_t org, vec3_t dir, int color, int count)
+{
+ int i, j;
+ cparticle_t *p;
+ float d;
+
+ for (i=0 ; i<count ; i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ p->color = color + (rand()&7);
+
+ d = rand()&31;
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = org[j] + ((rand()&7)-4) + d*dir[j];
+ p->vel[j] = crand()*20;
+ }
+
+ p->accel[0] = p->accel[1] = 0;
+ p->accel[2] = -PARTICLE_GRAVITY;
+ p->alpha = 1.0;
+
+ p->alphavel = -1.0 / (0.5 + frand()*0.3);
+ }
+}
+
+
+/*
+===============
+CL_ParticleEffect2
+===============
+*/
+void CL_ParticleEffect2 (vec3_t org, vec3_t dir, int color, int count)
+{
+ int i, j;
+ cparticle_t *p;
+ float d;
+
+ for (i=0 ; i<count ; i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ p->color = color;
+
+ d = rand()&7;
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = org[j] + ((rand()&7)-4) + d*dir[j];
+ p->vel[j] = crand()*20;
+ }
+
+ p->accel[0] = p->accel[1] = 0;
+ p->accel[2] = -PARTICLE_GRAVITY;
+ p->alpha = 1.0;
+
+ p->alphavel = -1.0 / (0.5 + frand()*0.3);
+ }
+}
+
+
+// RAFAEL
+/*
+===============
+CL_ParticleEffect3
+===============
+*/
+void CL_ParticleEffect3 (vec3_t org, vec3_t dir, int color, int count)
+{
+ int i, j;
+ cparticle_t *p;
+ float d;
+
+ for (i=0 ; i<count ; i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ p->color = color;
+
+ d = rand()&7;
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = org[j] + ((rand()&7)-4) + d*dir[j];
+ p->vel[j] = crand()*20;
+ }
+
+ p->accel[0] = p->accel[1] = 0;
+ p->accel[2] = PARTICLE_GRAVITY;
+ p->alpha = 1.0;
+
+ p->alphavel = -1.0 / (0.5 + frand()*0.3);
+ }
+}
+
+/*
+===============
+CL_TeleporterParticles
+===============
+*/
+void CL_TeleporterParticles (entity_state_t *ent)
+{
+ int i, j;
+ cparticle_t *p;
+
+ for (i=0 ; i<8 ; i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ p->color = 0xdb;
+
+ for (j=0 ; j<2 ; j++)
+ {
+ p->org[j] = ent->origin[j] - 16 + (rand()&31);
+ p->vel[j] = crand()*14;
+ }
+
+ p->org[2] = ent->origin[2] - 8 + (rand()&7);
+ p->vel[2] = 80 + (rand()&7);
+
+ p->accel[0] = p->accel[1] = 0;
+ p->accel[2] = -PARTICLE_GRAVITY;
+ p->alpha = 1.0;
+
+ p->alphavel = -0.5;
+ }
+}
+
+
+/*
+===============
+CL_LogoutEffect
+
+===============
+*/
+void CL_LogoutEffect (vec3_t org, int type)
+{
+ int i, j;
+ cparticle_t *p;
+
+ for (i=0 ; i<500 ; i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+
+ if (type == MZ_LOGIN)
+ p->color = 0xd0 + (rand()&7); // green
+ else if (type == MZ_LOGOUT)
+ p->color = 0x40 + (rand()&7); // red
+ else
+ p->color = 0xe0 + (rand()&7); // yellow
+
+ p->org[0] = org[0] - 16 + frand()*32;
+ p->org[1] = org[1] - 16 + frand()*32;
+ p->org[2] = org[2] - 24 + frand()*56;
+
+ for (j=0 ; j<3 ; j++)
+ p->vel[j] = crand()*20;
+
+ p->accel[0] = p->accel[1] = 0;
+ p->accel[2] = -PARTICLE_GRAVITY;
+ p->alpha = 1.0;
+
+ p->alphavel = -1.0 / (1.0 + frand()*0.3);
+ }
+}
+
+
+/*
+===============
+CL_ItemRespawnParticles
+
+===============
+*/
+void CL_ItemRespawnParticles (vec3_t org)
+{
+ int i, j;
+ cparticle_t *p;
+
+ for (i=0 ; i<64 ; i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+
+ p->color = 0xd4 + (rand()&3); // green
+
+ p->org[0] = org[0] + crand()*8;
+ p->org[1] = org[1] + crand()*8;
+ p->org[2] = org[2] + crand()*8;
+
+ for (j=0 ; j<3 ; j++)
+ p->vel[j] = crand()*8;
+
+ p->accel[0] = p->accel[1] = 0;
+ p->accel[2] = -PARTICLE_GRAVITY*0.2;
+ p->alpha = 1.0;
+
+ p->alphavel = -1.0 / (1.0 + frand()*0.3);
+ }
+}
+
+
+/*
+===============
+CL_ExplosionParticles
+===============
+*/
+void CL_ExplosionParticles (vec3_t org)
+{
+ int i, j;
+ cparticle_t *p;
+
+ for (i=0 ; i<256 ; i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ p->color = 0xe0 + (rand()&7);
+
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = org[j] + ((rand()%32)-16);
+ p->vel[j] = (rand()%384)-192;
+ }
+
+ p->accel[0] = p->accel[1] = 0;
+ p->accel[2] = -PARTICLE_GRAVITY;
+ p->alpha = 1.0;
+
+ p->alphavel = -0.8 / (0.5 + frand()*0.3);
+ }
+}
+
+
+/*
+===============
+CL_BigTeleportParticles
+===============
+*/
+void CL_BigTeleportParticles (vec3_t org)
+{
+ int i;
+ cparticle_t *p;
+ float angle, dist;
+ static int colortable[4] = {2*8,13*8,21*8,18*8};
+
+ for (i=0 ; i<4096 ; i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+
+ p->color = colortable[rand()&3];
+
+ angle = M_PI*2*(rand()&1023)/1023.0;
+ dist = rand()&31;
+ p->org[0] = org[0] + cos(angle)*dist;
+ p->vel[0] = cos(angle)*(70+(rand()&63));
+ p->accel[0] = -cos(angle)*100;
+
+ p->org[1] = org[1] + sin(angle)*dist;
+ p->vel[1] = sin(angle)*(70+(rand()&63));
+ p->accel[1] = -sin(angle)*100;
+
+ p->org[2] = org[2] + 8 + (rand()%90);
+ p->vel[2] = -100 + (rand()&31);
+ p->accel[2] = PARTICLE_GRAVITY*4;
+ p->alpha = 1.0;
+
+ p->alphavel = -0.3 / (0.5 + frand()*0.3);
+ }
+}
+
+
+/*
+===============
+CL_BlasterParticles
+
+Wall impact puffs
+===============
+*/
+void CL_BlasterParticles (vec3_t org, vec3_t dir)
+{
+ int i, j;
+ cparticle_t *p;
+ float d;
+ int count;
+
+ count = 40;
+ for (i=0 ; i<count ; i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ p->color = 0xe0 + (rand()&7);
+
+ d = rand()&15;
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = org[j] + ((rand()&7)-4) + d*dir[j];
+ p->vel[j] = dir[j] * 30 + crand()*40;
+ }
+
+ p->accel[0] = p->accel[1] = 0;
+ p->accel[2] = -PARTICLE_GRAVITY;
+ p->alpha = 1.0;
+
+ p->alphavel = -1.0 / (0.5 + frand()*0.3);
+ }
+}
+
+
+/*
+===============
+CL_BlasterTrail
+
+===============
+*/
+void CL_BlasterTrail (vec3_t start, vec3_t end)
+{
+ vec3_t move;
+ vec3_t vec;
+ float len;
+ int j;
+ cparticle_t *p;
+ int dec;
+
+ VectorCopy (start, move);
+ VectorSubtract (end, start, vec);
+ len = VectorNormalize (vec);
+
+ dec = 5;
+ VectorScale (vec, 5, vec);
+
+ // FIXME: this is a really silly way to have a loop
+ while (len > 0)
+ {
+ len -= dec;
+
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+ VectorClear (p->accel);
+
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (0.3+frand()*0.2);
+ p->color = 0xe0;
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + crand();
+ p->vel[j] = crand()*5;
+ p->accel[j] = 0;
+ }
+
+ VectorAdd (move, vec, move);
+ }
+}
+
+/*
+===============
+CL_QuadTrail
+
+===============
+*/
+void CL_QuadTrail (vec3_t start, vec3_t end)
+{
+ vec3_t move;
+ vec3_t vec;
+ float len;
+ int j;
+ cparticle_t *p;
+ int dec;
+
+ VectorCopy (start, move);
+ VectorSubtract (end, start, vec);
+ len = VectorNormalize (vec);
+
+ dec = 5;
+ VectorScale (vec, 5, vec);
+
+ while (len > 0)
+ {
+ len -= dec;
+
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+ VectorClear (p->accel);
+
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (0.8+frand()*0.2);
+ p->color = 115;
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + crand()*16;
+ p->vel[j] = crand()*5;
+ p->accel[j] = 0;
+ }
+
+ VectorAdd (move, vec, move);
+ }
+}
+
+/*
+===============
+CL_FlagTrail
+
+===============
+*/
+void CL_FlagTrail (vec3_t start, vec3_t end, float color)
+{
+ vec3_t move;
+ vec3_t vec;
+ float len;
+ int j;
+ cparticle_t *p;
+ int dec;
+
+ VectorCopy (start, move);
+ VectorSubtract (end, start, vec);
+ len = VectorNormalize (vec);
+
+ dec = 5;
+ VectorScale (vec, 5, vec);
+
+ while (len > 0)
+ {
+ len -= dec;
+
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+ VectorClear (p->accel);
+
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (0.8+frand()*0.2);
+ p->color = color;
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + crand()*16;
+ p->vel[j] = crand()*5;
+ p->accel[j] = 0;
+ }
+
+ VectorAdd (move, vec, move);
+ }
+}
+
+/*
+===============
+CL_DiminishingTrail
+
+===============
+*/
+void CL_DiminishingTrail (vec3_t start, vec3_t end, centity_t *old, int flags)
+{
+ vec3_t move;
+ vec3_t vec;
+ float len;
+ int j;
+ cparticle_t *p;
+ float dec;
+ float orgscale;
+ float velscale;
+
+ VectorCopy (start, move);
+ VectorSubtract (end, start, vec);
+ len = VectorNormalize (vec);
+
+ dec = 0.5;
+ VectorScale (vec, dec, vec);
+
+ if (old->trailcount > 900)
+ {
+ orgscale = 4;
+ velscale = 15;
+ }
+ else if (old->trailcount > 800)
+ {
+ orgscale = 2;
+ velscale = 10;
+ }
+ else
+ {
+ orgscale = 1;
+ velscale = 5;
+ }
+
+ while (len > 0)
+ {
+ len -= dec;
+
+ if (!free_particles)
+ return;
+
+ // drop less particles as it flies
+ if ((rand()&1023) < old->trailcount)
+ {
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+ VectorClear (p->accel);
+
+ p->time = cl.time;
+
+ if (flags & EF_GIB)
+ {
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (1+frand()*0.4);
+ p->color = 0xe8 + (rand()&7);
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + crand()*orgscale;
+ p->vel[j] = crand()*velscale;
+ p->accel[j] = 0;
+ }
+ p->vel[2] -= PARTICLE_GRAVITY;
+ }
+ else if (flags & EF_GREENGIB)
+ {
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (1+frand()*0.4);
+ p->color = 0xdb + (rand()&7);
+ for (j=0; j< 3; j++)
+ {
+ p->org[j] = move[j] + crand()*orgscale;
+ p->vel[j] = crand()*velscale;
+ p->accel[j] = 0;
+ }
+ p->vel[2] -= PARTICLE_GRAVITY;
+ }
+ else
+ {
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (1+frand()*0.2);
+ p->color = 4 + (rand()&7);
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + crand()*orgscale;
+ p->vel[j] = crand()*velscale;
+ }
+ p->accel[2] = 20;
+ }
+ }
+
+ old->trailcount -= 5;
+ if (old->trailcount < 100)
+ old->trailcount = 100;
+ VectorAdd (move, vec, move);
+ }
+}
+
+void MakeNormalVectors (vec3_t forward, vec3_t right, vec3_t up)
+{
+ float d;
+
+ // this rotate and negat guarantees a vector
+ // not colinear with the original
+ right[1] = -forward[0];
+ right[2] = forward[1];
+ right[0] = forward[2];
+
+ d = DotProduct (right, forward);
+ VectorMA (right, -d, forward, right);
+ VectorNormalize (right);
+ CrossProduct (right, forward, up);
+}
+
+/*
+===============
+CL_RocketTrail
+
+===============
+*/
+void CL_RocketTrail (vec3_t start, vec3_t end, centity_t *old)
+{
+ vec3_t move;
+ vec3_t vec;
+ float len;
+ int j;
+ cparticle_t *p;
+ float dec;
+
+ // smoke
+ CL_DiminishingTrail (start, end, old, EF_ROCKET);
+
+ // fire
+ VectorCopy (start, move);
+ VectorSubtract (end, start, vec);
+ len = VectorNormalize (vec);
+
+ dec = 1;
+ VectorScale (vec, dec, vec);
+
+ while (len > 0)
+ {
+ len -= dec;
+
+ if (!free_particles)
+ return;
+
+ if ( (rand()&7) == 0)
+ {
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ VectorClear (p->accel);
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (1+frand()*0.2);
+ p->color = 0xdc + (rand()&3);
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + crand()*5;
+ p->vel[j] = crand()*20;
+ }
+ p->accel[2] = -PARTICLE_GRAVITY;
+ }
+ VectorAdd (move, vec, move);
+ }
+}
+
+/*
+===============
+CL_RailTrail
+
+===============
+*/
+void CL_RailTrail (vec3_t start, vec3_t end)
+{
+ vec3_t move;
+ vec3_t vec;
+ float len;
+ int j;
+ cparticle_t *p;
+ float dec;
+ vec3_t right, up;
+ int i;
+ float d, c, s;
+ vec3_t dir;
+ byte clr = 0x74;
+
+ VectorCopy (start, move);
+ VectorSubtract (end, start, vec);
+ len = VectorNormalize (vec);
+
+ MakeNormalVectors (vec, right, up);
+
+ for (i=0 ; i<len ; i++)
+ {
+ if (!free_particles)
+ return;
+
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ VectorClear (p->accel);
+
+ d = i * 0.1;
+ c = cos(d);
+ s = sin(d);
+
+ VectorScale (right, c, dir);
+ VectorMA (dir, s, up, dir);
+
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (1+frand()*0.2);
+ p->color = clr + (rand()&7);
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + dir[j]*3;
+ p->vel[j] = dir[j]*6;
+ }
+
+ VectorAdd (move, vec, move);
+ }
+
+ dec = 0.75;
+ VectorScale (vec, dec, vec);
+ VectorCopy (start, move);
+
+ while (len > 0)
+ {
+ len -= dec;
+
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ VectorClear (p->accel);
+
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (0.6+frand()*0.2);
+ p->color = 0x0 + rand()&15;
+
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + crand()*3;
+ p->vel[j] = crand()*3;
+ p->accel[j] = 0;
+ }
+
+ VectorAdd (move, vec, move);
+ }
+}
+
+// RAFAEL
+/*
+===============
+CL_IonripperTrail
+===============
+*/
+void CL_IonripperTrail (vec3_t start, vec3_t ent)
+{
+ vec3_t move;
+ vec3_t vec;
+ float len;
+ int j;
+ cparticle_t *p;
+ int dec;
+ int left = 0;
+
+ VectorCopy (start, move);
+ VectorSubtract (ent, start, vec);
+ len = VectorNormalize (vec);
+
+ dec = 5;
+ VectorScale (vec, 5, vec);
+
+ while (len > 0)
+ {
+ len -= dec;
+
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+ VectorClear (p->accel);
+
+ p->time = cl.time;
+ p->alpha = 0.5;
+ p->alphavel = -1.0 / (0.3 + frand() * 0.2);
+ p->color = 0xe4 + (rand()&3);
+
+ for (j=0; j<3; j++)
+ {
+ p->org[j] = move[j];
+ p->accel[j] = 0;
+ }
+ if (left)
+ {
+ left = 0;
+ p->vel[0] = 10;
+ }
+ else
+ {
+ left = 1;
+ p->vel[0] = -10;
+ }
+
+ p->vel[1] = 0;
+ p->vel[2] = 0;
+
+ VectorAdd (move, vec, move);
+ }
+}
+
+
+/*
+===============
+CL_BubbleTrail
+
+===============
+*/
+void CL_BubbleTrail (vec3_t start, vec3_t end)
+{
+ vec3_t move;
+ vec3_t vec;
+ float len;
+ int i, j;
+ cparticle_t *p;
+ float dec;
+
+ VectorCopy (start, move);
+ VectorSubtract (end, start, vec);
+ len = VectorNormalize (vec);
+
+ dec = 32;
+ VectorScale (vec, dec, vec);
+
+ for (i=0 ; i<len ; i+=dec)
+ {
+ if (!free_particles)
+ return;
+
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ VectorClear (p->accel);
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (1+frand()*0.2);
+ p->color = 4 + (rand()&7);
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + crand()*2;
+ p->vel[j] = crand()*5;
+ }
+ p->vel[2] += 6;
+
+ VectorAdd (move, vec, move);
+ }
+}
+
+
+/*
+===============
+CL_FlyParticles
+===============
+*/
+
+#define BEAMLENGTH 16
+void CL_FlyParticles (vec3_t origin, int count)
+{
+ int i;
+ cparticle_t *p;
+ float angle;
+ float sr, sp, sy, cr, cp, cy;
+ vec3_t forward;
+ float dist = 64;
+ float ltime;
+
+
+ if (count > NUMVERTEXNORMALS)
+ count = NUMVERTEXNORMALS;
+
+ if (!avelocities[0][0])
+ {
+ for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
+ avelocities[0][i] = (rand()&255) * 0.01;
+ }
+
+
+ ltime = (float)cl.time / 1000.0;
+ for (i=0 ; i<count ; i+=2)
+ {
+ angle = ltime * avelocities[i][0];
+ sy = sin(angle);
+ cy = cos(angle);
+ angle = ltime * avelocities[i][1];
+ sp = sin(angle);
+ cp = cos(angle);
+ angle = ltime * avelocities[i][2];
+ sr = sin(angle);
+ cr = cos(angle);
+
+ forward[0] = cp*cy;
+ forward[1] = cp*sy;
+ forward[2] = -sp;
+
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+
+ dist = sin(ltime + i)*64;
+ p->org[0] = origin[0] + bytedirs[i][0]*dist + forward[0]*BEAMLENGTH;
+ p->org[1] = origin[1] + bytedirs[i][1]*dist + forward[1]*BEAMLENGTH;
+ p->org[2] = origin[2] + bytedirs[i][2]*dist + forward[2]*BEAMLENGTH;
+
+ VectorClear (p->vel);
+ VectorClear (p->accel);
+
+ p->color = 0;
+ p->colorvel = 0;
+
+ p->alpha = 1;
+ p->alphavel = -100;
+ }
+}
+
+void CL_FlyEffect (centity_t *ent, vec3_t origin)
+{
+ int n;
+ int count;
+ int starttime;
+
+ if (ent->fly_stoptime < cl.time)
+ {
+ starttime = cl.time;
+ ent->fly_stoptime = cl.time + 60000;
+ }
+ else
+ {
+ starttime = ent->fly_stoptime - 60000;
+ }
+
+ n = cl.time - starttime;
+ if (n < 20000)
+ count = n * 162 / 20000.0;
+ else
+ {
+ n = ent->fly_stoptime - cl.time;
+ if (n < 20000)
+ count = n * 162 / 20000.0;
+ else
+ count = 162;
+ }
+
+ CL_FlyParticles (origin, count);
+}
+
+
+/*
+===============
+CL_BfgParticles
+===============
+*/
+
+#define BEAMLENGTH 16
+void CL_BfgParticles (entity_t *ent)
+{
+ int i;
+ cparticle_t *p;
+ float angle;
+ float sr, sp, sy, cr, cp, cy;
+ vec3_t forward;
+ float dist = 64;
+ vec3_t v;
+ float ltime;
+
+ if (!avelocities[0][0])
+ {
+ for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
+ avelocities[0][i] = (rand()&255) * 0.01;
+ }
+
+
+ ltime = (float)cl.time / 1000.0;
+ for (i=0 ; i<NUMVERTEXNORMALS ; i++)
+ {
+ angle = ltime * avelocities[i][0];
+ sy = sin(angle);
+ cy = cos(angle);
+ angle = ltime * avelocities[i][1];
+ sp = sin(angle);
+ cp = cos(angle);
+ angle = ltime * avelocities[i][2];
+ sr = sin(angle);
+ cr = cos(angle);
+
+ forward[0] = cp*cy;
+ forward[1] = cp*sy;
+ forward[2] = -sp;
+
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+
+ dist = sin(ltime + i)*64;
+ p->org[0] = ent->origin[0] + bytedirs[i][0]*dist + forward[0]*BEAMLENGTH;
+ p->org[1] = ent->origin[1] + bytedirs[i][1]*dist + forward[1]*BEAMLENGTH;
+ p->org[2] = ent->origin[2] + bytedirs[i][2]*dist + forward[2]*BEAMLENGTH;
+
+ VectorClear (p->vel);
+ VectorClear (p->accel);
+
+ VectorSubtract (p->org, ent->origin, v);
+ dist = VectorLength(v) / 90.0;
+ p->color = floor (0xd0 + dist * 7);
+ p->colorvel = 0;
+
+ p->alpha = 1.0 - dist;
+ p->alphavel = -100;
+ }
+}
+
+
+/*
+===============
+CL_TrapParticles
+===============
+*/
+// RAFAEL
+void CL_TrapParticles (entity_t *ent)
+{
+ vec3_t move;
+ vec3_t vec;
+ vec3_t start, end;
+ float len;
+ int j;
+ cparticle_t *p;
+ int dec;
+
+ ent->origin[2]-=14;
+ VectorCopy (ent->origin, start);
+ VectorCopy (ent->origin, end);
+ end[2]+=64;
+
+ VectorCopy (start, move);
+ VectorSubtract (end, start, vec);
+ len = VectorNormalize (vec);
+
+ dec = 5;
+ VectorScale (vec, 5, vec);
+
+ // FIXME: this is a really silly way to have a loop
+ while (len > 0)
+ {
+ len -= dec;
+
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+ VectorClear (p->accel);
+
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (0.3+frand()*0.2);
+ p->color = 0xe0;
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + crand();
+ p->vel[j] = crand()*15;
+ p->accel[j] = 0;
+ }
+ p->accel[2] = PARTICLE_GRAVITY;
+
+ VectorAdd (move, vec, move);
+ }
+
+ {
+
+
+ int i, j, k;
+ cparticle_t *p;
+ float vel;
+ vec3_t dir;
+ vec3_t org;
+
+
+ ent->origin[2]+=14;
+ VectorCopy (ent->origin, org);
+
+
+ for (i=-2 ; i<=2 ; i+=4)
+ for (j=-2 ; j<=2 ; j+=4)
+ for (k=-2 ; k<=4 ; k+=4)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ p->color = 0xe0 + (rand()&3);
+
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (0.3 + (rand()&7) * 0.02);
+
+ p->org[0] = org[0] + i + ((rand()&23) * crand());
+ p->org[1] = org[1] + j + ((rand()&23) * crand());
+ p->org[2] = org[2] + k + ((rand()&23) * crand());
+
+ dir[0] = j * 8;
+ dir[1] = i * 8;
+ dir[2] = k * 8;
+
+ VectorNormalize (dir);
+ vel = 50 + rand()&63;
+ VectorScale (dir, vel, p->vel);
+
+ p->accel[0] = p->accel[1] = 0;
+ p->accel[2] = -PARTICLE_GRAVITY;
+ }
+ }
+}
+
+
+/*
+===============
+CL_BFGExplosionParticles
+===============
+*/
+//FIXME combined with CL_ExplosionParticles
+void CL_BFGExplosionParticles (vec3_t org)
+{
+ int i, j;
+ cparticle_t *p;
+
+ for (i=0 ; i<256 ; i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ p->color = 0xd0 + (rand()&7);
+
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = org[j] + ((rand()%32)-16);
+ p->vel[j] = (rand()%384)-192;
+ }
+
+ p->accel[0] = p->accel[1] = 0;
+ p->accel[2] = -PARTICLE_GRAVITY;
+ p->alpha = 1.0;
+
+ p->alphavel = -0.8 / (0.5 + frand()*0.3);
+ }
+}
+
+
+/*
+===============
+CL_TeleportParticles
+
+===============
+*/
+void CL_TeleportParticles (vec3_t org)
+{
+ int i, j, k;
+ cparticle_t *p;
+ float vel;
+ vec3_t dir;
+
+ for (i=-16 ; i<=16 ; i+=4)
+ for (j=-16 ; j<=16 ; j+=4)
+ for (k=-16 ; k<=32 ; k+=4)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ p->color = 7 + (rand()&7);
+
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (0.3 + (rand()&7) * 0.02);
+
+ p->org[0] = org[0] + i + (rand()&3);
+ p->org[1] = org[1] + j + (rand()&3);
+ p->org[2] = org[2] + k + (rand()&3);
+
+ dir[0] = j*8;
+ dir[1] = i*8;
+ dir[2] = k*8;
+
+ VectorNormalize (dir);
+ vel = 50 + (rand()&63);
+ VectorScale (dir, vel, p->vel);
+
+ p->accel[0] = p->accel[1] = 0;
+ p->accel[2] = -PARTICLE_GRAVITY;
+ }
+}
+
+
+/*
+===============
+CL_AddParticles
+===============
+*/
+void CL_AddParticles (void)
+{
+ cparticle_t *p, *next;
+ float alpha;
+ float time, time2;
+ vec3_t org;
+ int color;
+ cparticle_t *active, *tail;
+
+ active = NULL;
+ tail = NULL;
+
+ for (p=active_particles ; p ; p=next)
+ {
+ next = p->next;
+
+ // PMM - added INSTANT_PARTICLE handling for heat beam
+ if (p->alphavel != INSTANT_PARTICLE)
+ {
+ time = (cl.time - p->time)*0.001;
+ alpha = p->alpha + time*p->alphavel;
+ if (alpha <= 0)
+ { // faded out
+ p->next = free_particles;
+ free_particles = p;
+ continue;
+ }
+ }
+ else
+ {
+ alpha = p->alpha;
+ }
+
+ p->next = NULL;
+ if (!tail)
+ active = tail = p;
+ else
+ {
+ tail->next = p;
+ tail = p;
+ }
+
+ if (alpha > 1.0)
+ alpha = 1;
+ color = p->color;
+
+ time2 = time*time;
+
+ org[0] = p->org[0] + p->vel[0]*time + p->accel[0]*time2;
+ org[1] = p->org[1] + p->vel[1]*time + p->accel[1]*time2;
+ org[2] = p->org[2] + p->vel[2]*time + p->accel[2]*time2;
+
+ V_AddParticle (org, color, alpha);
+ // PMM
+ if (p->alphavel == INSTANT_PARTICLE)
+ {
+ p->alphavel = 0.0;
+ p->alpha = 0.0;
+ }
+ }
+
+ active_particles = active;
+}
+
+
+/*
+==============
+CL_EntityEvent
+
+An entity has just been parsed that has an event value
+
+the female events are there for backwards compatability
+==============
+*/
+extern struct sfx_s *cl_sfx_footsteps[4];
+
+void CL_EntityEvent (entity_state_t *ent)
+{
+ switch (ent->event)
+ {
+ case EV_ITEM_RESPAWN:
+ S_StartSound (NULL, ent->number, CHAN_WEAPON, S_RegisterSound("items/respawn1.wav"), 1, ATTN_IDLE, 0);
+ CL_ItemRespawnParticles (ent->origin);
+ break;
+ case EV_PLAYER_TELEPORT:
+ S_StartSound (NULL, ent->number, CHAN_WEAPON, S_RegisterSound("misc/tele1.wav"), 1, ATTN_IDLE, 0);
+ CL_TeleportParticles (ent->origin);
+ break;
+ case EV_FOOTSTEP:
+ if (cl_footsteps->value)
+ S_StartSound (NULL, ent->number, CHAN_BODY, cl_sfx_footsteps[rand()&3], 1, ATTN_NORM, 0);
+ break;
+ case EV_FALLSHORT:
+ S_StartSound (NULL, ent->number, CHAN_AUTO, S_RegisterSound ("player/land1.wav"), 1, ATTN_NORM, 0);
+ break;
+ case EV_FALL:
+ S_StartSound (NULL, ent->number, CHAN_AUTO, S_RegisterSound ("*fall2.wav"), 1, ATTN_NORM, 0);
+ break;
+ case EV_FALLFAR:
+ S_StartSound (NULL, ent->number, CHAN_AUTO, S_RegisterSound ("*fall1.wav"), 1, ATTN_NORM, 0);
+ break;
+ }
+}
+
+
+/*
+==============
+CL_ClearEffects
+
+==============
+*/
+void CL_ClearEffects (void)
+{
+ CL_ClearParticles ();
+ CL_ClearDlights ();
+ CL_ClearLightStyles ();
+}
diff --git a/client/cl_input.c b/client/cl_input.c
new file mode 100644
index 0000000..800d5a1
--- /dev/null
+++ b/client/cl_input.c
@@ -0,0 +1,542 @@
+/*
+Copyright (C) 1997-2001 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.
+
+*/
+// cl.input.c -- builds an intended movement command to send to the server
+
+#include "client.h"
+
+cvar_t *cl_nodelta;
+
+extern unsigned sys_frame_time;
+unsigned frame_msec;
+unsigned old_sys_frame_time;
+
+/*
+===============================================================================
+
+KEY BUTTONS
+
+Continuous button event tracking is complicated by the fact that two different
+input sources (say, mouse button 1 and the control key) can both press the
+same button, but the button should only be released when both of the
+pressing key have been released.
+
+When a key event issues a button command (+forward, +attack, etc), it appends
+its key number as a parameter to the command so it can be matched up with
+the release.
+
+state bit 0 is the current state of the key
+state bit 1 is edge triggered on the up to down transition
+state bit 2 is edge triggered on the down to up transition
+
+
+Key_Event (int key, qboolean down, unsigned time);
+
+ +mlook src time
+
+===============================================================================
+*/
+
+
+kbutton_t in_klook;
+kbutton_t in_left, in_right, in_forward, in_back;
+kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright;
+kbutton_t in_strafe, in_speed, in_use, in_attack;
+kbutton_t in_up, in_down;
+
+int in_impulse;
+
+
+void KeyDown (kbutton_t *b)
+{
+ int k;
+ char *c;
+
+ c = Cmd_Argv(1);
+ if (c[0])
+ k = atoi(c);
+ else
+ k = -1; // typed manually at the console for continuous down
+
+ if (k == b->down[0] || k == b->down[1])
+ return; // repeating key
+
+ if (!b->down[0])
+ b->down[0] = k;
+ else if (!b->down[1])
+ b->down[1] = k;
+ else
+ {
+ Com_Printf ("Three keys down for a button!\n");
+ return;
+ }
+
+ if (b->state & 1)
+ return; // still down
+
+ // save timestamp
+ c = Cmd_Argv(2);
+ b->downtime = atoi(c);
+ if (!b->downtime)
+ b->downtime = sys_frame_time - 100;
+
+ b->state |= 1 + 2; // down + impulse down
+}
+
+void KeyUp (kbutton_t *b)
+{
+ int k;
+ char *c;
+ unsigned uptime;
+
+ c = Cmd_Argv(1);
+ if (c[0])
+ k = atoi(c);
+ else
+ { // typed manually at the console, assume for unsticking, so clear all
+ b->down[0] = b->down[1] = 0;
+ b->state = 4; // impulse up
+ return;
+ }
+
+ if (b->down[0] == k)
+ b->down[0] = 0;
+ else if (b->down[1] == k)
+ b->down[1] = 0;
+ else
+ return; // key up without coresponding down (menu pass through)
+ if (b->down[0] || b->down[1])
+ return; // some other key is still holding it down
+
+ if (!(b->state & 1))
+ return; // still up (this should not happen)
+
+ // save timestamp
+ c = Cmd_Argv(2);
+ uptime = atoi(c);
+ if (uptime)
+ b->msec += uptime - b->downtime;
+ else
+ b->msec += 10;
+
+ b->state &= ~1; // now up
+ b->state |= 4; // impulse up
+}
+
+void IN_KLookDown (void) {KeyDown(&in_klook);}
+void IN_KLookUp (void) {KeyUp(&in_klook);}
+void IN_UpDown(void) {KeyDown(&in_up);}
+void IN_UpUp(void) {KeyUp(&in_up);}
+void IN_DownDown(void) {KeyDown(&in_down);}
+void IN_DownUp(void) {KeyUp(&in_down);}
+void IN_LeftDown(void) {KeyDown(&in_left);}
+void IN_LeftUp(void) {KeyUp(&in_left);}
+void IN_RightDown(void) {KeyDown(&in_right);}
+void IN_RightUp(void) {KeyUp(&in_right);}
+void IN_ForwardDown(void) {KeyDown(&in_forward);}
+void IN_ForwardUp(void) {KeyUp(&in_forward);}
+void IN_BackDown(void) {KeyDown(&in_back);}
+void IN_BackUp(void) {KeyUp(&in_back);}
+void IN_LookupDown(void) {KeyDown(&in_lookup);}
+void IN_LookupUp(void) {KeyUp(&in_lookup);}
+void IN_LookdownDown(void) {KeyDown(&in_lookdown);}
+void IN_LookdownUp(void) {KeyUp(&in_lookdown);}
+void IN_MoveleftDown(void) {KeyDown(&in_moveleft);}
+void IN_MoveleftUp(void) {KeyUp(&in_moveleft);}
+void IN_MoverightDown(void) {KeyDown(&in_moveright);}
+void IN_MoverightUp(void) {KeyUp(&in_moveright);}
+
+void IN_SpeedDown(void) {KeyDown(&in_speed);}
+void IN_SpeedUp(void) {KeyUp(&in_speed);}
+void IN_StrafeDown(void) {KeyDown(&in_strafe);}
+void IN_StrafeUp(void) {KeyUp(&in_strafe);}
+
+void IN_AttackDown(void) {KeyDown(&in_attack);}
+void IN_AttackUp(void) {KeyUp(&in_attack);}
+
+void IN_UseDown (void) {KeyDown(&in_use);}
+void IN_UseUp (void) {KeyUp(&in_use);}
+
+void IN_Impulse (void) {in_impulse=atoi(Cmd_Argv(1));}
+
+/*
+===============
+CL_KeyState
+
+Returns the fraction of the frame that the key was down
+===============
+*/
+float CL_KeyState (kbutton_t *key)
+{
+ float val;
+ int msec;
+
+ key->state &= 1; // clear impulses
+
+ msec = key->msec;
+ key->msec = 0;
+
+ if (key->state)
+ { // still down
+ msec += sys_frame_time - key->downtime;
+ key->downtime = sys_frame_time;
+ }
+
+#if 0
+ if (msec)
+ {
+ Com_Printf ("%i ", msec);
+ }
+#endif
+
+ val = (float)msec / frame_msec;
+ if (val < 0)
+ val = 0;
+ if (val > 1)
+ val = 1;
+
+ return val;
+}
+
+
+
+
+//==========================================================================
+
+cvar_t *cl_upspeed;
+cvar_t *cl_forwardspeed;
+cvar_t *cl_sidespeed;
+
+cvar_t *cl_yawspeed;
+cvar_t *cl_pitchspeed;
+
+cvar_t *cl_run;
+
+cvar_t *cl_anglespeedkey;
+
+
+/*
+================
+CL_AdjustAngles
+
+Moves the local angle positions
+================
+*/
+void CL_AdjustAngles (void)
+{
+ float speed;
+ float up, down;
+
+ if (in_speed.state & 1)
+ speed = cls.frametime * cl_anglespeedkey->value;
+ else
+ speed = cls.frametime;
+
+ if (!(in_strafe.state & 1))
+ {
+ cl.viewangles[YAW] -= speed*cl_yawspeed->value*CL_KeyState (&in_right);
+ cl.viewangles[YAW] += speed*cl_yawspeed->value*CL_KeyState (&in_left);
+ }
+ if (in_klook.state & 1)
+ {
+ cl.viewangles[PITCH] -= speed*cl_pitchspeed->value * CL_KeyState (&in_forward);
+ cl.viewangles[PITCH] += speed*cl_pitchspeed->value * CL_KeyState (&in_back);
+ }
+
+ up = CL_KeyState (&in_lookup);
+ down = CL_KeyState(&in_lookdown);
+
+ cl.viewangles[PITCH] -= speed*cl_pitchspeed->value * up;
+ cl.viewangles[PITCH] += speed*cl_pitchspeed->value * down;
+}
+
+/*
+================
+CL_BaseMove
+
+Send the intended movement message to the server
+================
+*/
+void CL_BaseMove (usercmd_t *cmd)
+{
+ CL_AdjustAngles ();
+
+ memset (cmd, 0, sizeof(*cmd));
+
+ VectorCopy (cl.viewangles, cmd->angles);
+ if (in_strafe.state & 1)
+ {
+ cmd->sidemove += cl_sidespeed->value * CL_KeyState (&in_right);
+ cmd->sidemove -= cl_sidespeed->value * CL_KeyState (&in_left);
+ }
+
+ cmd->sidemove += cl_sidespeed->value * CL_KeyState (&in_moveright);
+ cmd->sidemove -= cl_sidespeed->value * CL_KeyState (&in_moveleft);
+
+ cmd->upmove += cl_upspeed->value * CL_KeyState (&in_up);
+ cmd->upmove -= cl_upspeed->value * CL_KeyState (&in_down);
+
+ if (! (in_klook.state & 1) )
+ {
+ cmd->forwardmove += cl_forwardspeed->value * CL_KeyState (&in_forward);
+ cmd->forwardmove -= cl_forwardspeed->value * CL_KeyState (&in_back);
+ }
+
+//
+// adjust for speed key / running
+//
+ if ( (in_speed.state & 1) ^ (int)(cl_run->value) )
+ {
+ cmd->forwardmove *= 2;
+ cmd->sidemove *= 2;
+ cmd->upmove *= 2;
+ }
+}
+
+void CL_ClampPitch (void)
+{
+ float pitch;
+
+ pitch = SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]);
+ if (pitch > 180)
+ pitch -= 360;
+ if (cl.viewangles[PITCH] + pitch > 89)
+ cl.viewangles[PITCH] = 89 - pitch;
+ if (cl.viewangles[PITCH] + pitch < -89)
+ cl.viewangles[PITCH] = -89 - pitch;
+}
+
+/*
+==============
+CL_FinishMove
+==============
+*/
+void CL_FinishMove (usercmd_t *cmd)
+{
+ int ms;
+ int i;
+
+//
+// figure button bits
+//
+ if ( in_attack.state & 3 )
+ cmd->buttons |= BUTTON_ATTACK;
+ in_attack.state &= ~2;
+
+ if (in_use.state & 3)
+ cmd->buttons |= BUTTON_USE;
+ in_use.state &= ~2;
+
+ if (anykeydown && cls.key_dest == key_game)
+ cmd->buttons |= BUTTON_ANY;
+
+ // send milliseconds of time to apply the move
+ ms = cls.frametime * 1000;
+ if (ms > 250)
+ ms = 100; // time was unreasonable
+ cmd->msec = ms;
+
+ CL_ClampPitch ();
+ for (i=0 ; i<3 ; i++)
+ cmd->angles[i] = ANGLE2SHORT(cl.viewangles[i]);
+
+ cmd->impulse = in_impulse;
+ in_impulse = 0;
+
+// send the ambient light level at the player's current position
+ cmd->lightlevel = (byte)cl_lightlevel->value;
+}
+
+/*
+=================
+CL_CreateCmd
+=================
+*/
+usercmd_t CL_CreateCmd (void)
+{
+ usercmd_t cmd;
+
+ frame_msec = sys_frame_time - old_sys_frame_time;
+ if (frame_msec < 1)
+ frame_msec = 1;
+ if (frame_msec > 200)
+ frame_msec = 200;
+
+ // get basic movement from keyboard
+ CL_BaseMove (&cmd);
+
+ // allow mice or other external controllers to add to the move
+ IN_Move (&cmd);
+
+ CL_FinishMove (&cmd);
+
+ old_sys_frame_time = sys_frame_time;
+
+//cmd.impulse = cls.framecount;
+
+ return cmd;
+}
+
+
+void IN_CenterView (void)
+{
+ cl.viewangles[PITCH] = -SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]);
+}
+
+/*
+============
+CL_InitInput
+============
+*/
+void CL_InitInput (void)
+{
+ Cmd_AddCommand ("centerview",IN_CenterView);
+
+ Cmd_AddCommand ("+moveup",IN_UpDown);
+ Cmd_AddCommand ("-moveup",IN_UpUp);
+ Cmd_AddCommand ("+movedown",IN_DownDown);
+ Cmd_AddCommand ("-movedown",IN_DownUp);
+ Cmd_AddCommand ("+left",IN_LeftDown);
+ Cmd_AddCommand ("-left",IN_LeftUp);
+ Cmd_AddCommand ("+right",IN_RightDown);
+ Cmd_AddCommand ("-right",IN_RightUp);
+ Cmd_AddCommand ("+forward",IN_ForwardDown);
+ Cmd_AddCommand ("-forward",IN_ForwardUp);
+ Cmd_AddCommand ("+back",IN_BackDown);
+ Cmd_AddCommand ("-back",IN_BackUp);
+ Cmd_AddCommand ("+lookup", IN_LookupDown);
+ Cmd_AddCommand ("-lookup", IN_LookupUp);
+ Cmd_AddCommand ("+lookdown", IN_LookdownDown);
+ Cmd_AddCommand ("-lookdown", IN_LookdownUp);
+ Cmd_AddCommand ("+strafe", IN_StrafeDown);
+ Cmd_AddCommand ("-strafe", IN_StrafeUp);
+ Cmd_AddCommand ("+moveleft", IN_MoveleftDown);
+ Cmd_AddCommand ("-moveleft", IN_MoveleftUp);
+ Cmd_AddCommand ("+moveright", IN_MoverightDown);
+ Cmd_AddCommand ("-moveright", IN_MoverightUp);
+ Cmd_AddCommand ("+speed", IN_SpeedDown);
+ Cmd_AddCommand ("-speed", IN_SpeedUp);
+ Cmd_AddCommand ("+attack", IN_AttackDown);
+ Cmd_AddCommand ("-attack", IN_AttackUp);
+ Cmd_AddCommand ("+use", IN_UseDown);
+ Cmd_AddCommand ("-use", IN_UseUp);
+ Cmd_AddCommand ("impulse", IN_Impulse);
+ Cmd_AddCommand ("+klook", IN_KLookDown);
+ Cmd_AddCommand ("-klook", IN_KLookUp);
+
+ cl_nodelta = Cvar_Get ("cl_nodelta", "0", 0);
+}
+
+
+
+/*
+=================
+CL_SendCmd
+=================
+*/
+void CL_SendCmd (void)
+{
+ sizebuf_t buf;
+ byte data[128];
+ int i;
+ usercmd_t *cmd, *oldcmd;
+ usercmd_t nullcmd;
+ int checksumIndex;
+
+ // build a command even if not connected
+
+ // save this command off for prediction
+ i = cls.netchan.outgoing_sequence & (CMD_BACKUP-1);
+ cmd = &cl.cmds[i];
+ cl.cmd_time[i] = cls.realtime; // for netgraph ping calculation
+
+ *cmd = CL_CreateCmd ();
+
+ cl.cmd = *cmd;
+
+ if (cls.state == ca_disconnected || cls.state == ca_connecting)
+ return;
+
+ if ( cls.state == ca_connected)
+ {
+ if (cls.netchan.message.cursize || curtime - cls.netchan.last_sent > 1000 )
+ Netchan_Transmit (&cls.netchan, 0, buf.data);
+ return;
+ }
+
+ // send a userinfo update if needed
+ if (userinfo_modified)
+ {
+ CL_FixUpGender();
+ userinfo_modified = false;
+ MSG_WriteByte (&cls.netchan.message, clc_userinfo);
+ MSG_WriteString (&cls.netchan.message, Cvar_Userinfo() );
+ }
+
+ SZ_Init (&buf, data, sizeof(data));
+
+ if (cmd->buttons && cl.cinematictime > 0 && !cl.attractloop
+ && cls.realtime - cl.cinematictime > 1000)
+ { // skip the rest of the cinematic
+ SCR_FinishCinematic ();
+ }
+
+ // begin a client move command
+ MSG_WriteByte (&buf, clc_move);
+
+ // save the position for a checksum byte
+ checksumIndex = buf.cursize;
+ MSG_WriteByte (&buf, 0);
+
+ // let the server know what the last frame we
+ // got was, so the next message can be delta compressed
+ if (cl_nodelta->value || !cl.frame.valid || cls.demowaiting)
+ MSG_WriteLong (&buf, -1); // no compression
+ else
+ MSG_WriteLong (&buf, cl.frame.serverframe);
+
+ // send this and the previous cmds in the message, so
+ // if the last packet was dropped, it can be recovered
+ i = (cls.netchan.outgoing_sequence-2) & (CMD_BACKUP-1);
+ cmd = &cl.cmds[i];
+ memset (&nullcmd, 0, sizeof(nullcmd));
+ MSG_WriteDeltaUsercmd (&buf, &nullcmd, cmd);
+ oldcmd = cmd;
+
+ i = (cls.netchan.outgoing_sequence-1) & (CMD_BACKUP-1);
+ cmd = &cl.cmds[i];
+ MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd);
+ oldcmd = cmd;
+
+ i = (cls.netchan.outgoing_sequence) & (CMD_BACKUP-1);
+ cmd = &cl.cmds[i];
+ MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd);
+
+ // calculate a checksum over the move commands
+ buf.data[checksumIndex] = COM_BlockSequenceCRCByte(
+ buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1,
+ cls.netchan.outgoing_sequence);
+
+ //
+ // deliver the message
+ //
+ Netchan_Transmit (&cls.netchan, buf.cursize, buf.data);
+}
+
+
diff --git a/client/cl_inv.c b/client/cl_inv.c
new file mode 100644
index 0000000..8a2d20f
--- /dev/null
+++ b/client/cl_inv.c
@@ -0,0 +1,142 @@
+/*
+Copyright (C) 1997-2001 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.
+
+*/
+// cl_inv.c -- client inventory screen
+
+#include "client.h"
+
+/*
+================
+CL_ParseInventory
+================
+*/
+void CL_ParseInventory (void)
+{
+ int i;
+
+ for (i=0 ; i<MAX_ITEMS ; i++)
+ cl.inventory[i] = MSG_ReadShort (&net_message);
+}
+
+
+/*
+================
+Inv_DrawString
+================
+*/
+void Inv_DrawString (int x, int y, char *string)
+{
+ while (*string)
+ {
+ re.DrawChar (x, y, *string);
+ x+=8;
+ string++;
+ }
+}
+
+void SetStringHighBit (char *s)
+{
+ while (*s)
+ *s++ |= 128;
+}
+
+/*
+================
+CL_DrawInventory
+================
+*/
+#define DISPLAY_ITEMS 17
+
+void CL_DrawInventory (void)
+{
+ int i, j;
+ int num, selected_num, item;
+ int index[MAX_ITEMS];
+ char string[1024];
+ int x, y;
+ char binding[1024];
+ char *bind;
+ int selected;
+ int top;
+
+ selected = cl.frame.playerstate.stats[STAT_SELECTED_ITEM];
+
+ num = 0;
+ selected_num = 0;
+ for (i=0 ; i<MAX_ITEMS ; i++)
+ {
+ if (i==selected)
+ selected_num = num;
+ if (cl.inventory[i])
+ {
+ index[num] = i;
+ num++;
+ }
+ }
+
+ // determine scroll point
+ top = selected_num - DISPLAY_ITEMS/2;
+ if (num - top < DISPLAY_ITEMS)
+ top = num - DISPLAY_ITEMS;
+ if (top < 0)
+ top = 0;
+
+ x = (viddef.width-256)/2;
+ y = (viddef.height-240)/2;
+
+ // repaint everything next frame
+ SCR_DirtyScreen ();
+
+ re.DrawPic (x, y+8, "inventory");
+
+ y += 24;
+ x += 24;
+ Inv_DrawString (x, y, "hotkey ### item");
+ Inv_DrawString (x, y+8, "------ --- ----");
+ y += 16;
+ for (i=top ; i<num && i < top+DISPLAY_ITEMS ; i++)
+ {
+ item = index[i];
+ // search for a binding
+ Com_sprintf (binding, sizeof(binding), "use %s", cl.configstrings[CS_ITEMS+item]);
+ bind = "";
+ for (j=0 ; j<256 ; j++)
+ if (keybindings[j] && !Q_stricmp (keybindings[j], binding))
+ {
+ bind = Key_KeynumToString(j);
+ break;
+ }
+
+ Com_sprintf (string, sizeof(string), "%6s %3i %s", bind, cl.inventory[item],
+ cl.configstrings[CS_ITEMS+item] );
+ if (item != selected)
+ SetStringHighBit (string);
+ else // draw a blinky cursor by the selected item
+ {
+ if ( (int)(cls.realtime*10) & 1)
+ re.DrawChar (x-8, y, 15);
+ }
+ Inv_DrawString (x, y, string);
+ y += 8;
+ }
+
+
+}
+
+
diff --git a/client/cl_main.c b/client/cl_main.c
new file mode 100644
index 0000000..e19423a
--- /dev/null
+++ b/client/cl_main.c
@@ -0,0 +1,1844 @@
+/*
+Copyright (C) 1997-2001 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.
+
+*/
+// cl_main.c -- client main loop
+
+#include "client.h"
+
+cvar_t *freelook;
+
+cvar_t *adr0;
+cvar_t *adr1;
+cvar_t *adr2;
+cvar_t *adr3;
+cvar_t *adr4;
+cvar_t *adr5;
+cvar_t *adr6;
+cvar_t *adr7;
+cvar_t *adr8;
+
+cvar_t *cl_stereo_separation;
+cvar_t *cl_stereo;
+
+cvar_t *rcon_client_password;
+cvar_t *rcon_address;
+
+cvar_t *cl_noskins;
+cvar_t *cl_autoskins;
+cvar_t *cl_footsteps;
+cvar_t *cl_timeout;
+cvar_t *cl_predict;
+//cvar_t *cl_minfps;
+cvar_t *cl_maxfps;
+cvar_t *cl_gun;
+
+cvar_t *cl_add_particles;
+cvar_t *cl_add_lights;
+cvar_t *cl_add_entities;
+cvar_t *cl_add_blend;
+
+cvar_t *cl_shownet;
+cvar_t *cl_showmiss;
+cvar_t *cl_showclamp;
+
+cvar_t *cl_paused;
+cvar_t *cl_timedemo;
+
+cvar_t *lookspring;
+cvar_t *lookstrafe;
+cvar_t *sensitivity;
+
+cvar_t *m_pitch;
+cvar_t *m_yaw;
+cvar_t *m_forward;
+cvar_t *m_side;
+
+cvar_t *cl_lightlevel;
+
+//
+// userinfo
+//
+cvar_t *info_password;
+cvar_t *info_spectator;
+cvar_t *name;
+cvar_t *skin;
+cvar_t *rate;
+cvar_t *fov;
+cvar_t *msg;
+cvar_t *hand;
+cvar_t *gender;
+cvar_t *gender_auto;
+
+cvar_t *cl_vwep;
+
+client_static_t cls;
+client_state_t cl;
+
+centity_t cl_entities[MAX_EDICTS];
+
+entity_state_t cl_parse_entities[MAX_PARSE_ENTITIES];
+
+extern cvar_t *allow_download;
+extern cvar_t *allow_download_players;
+extern cvar_t *allow_download_models;
+extern cvar_t *allow_download_sounds;
+extern cvar_t *allow_download_maps;
+
+//======================================================================
+
+
+/*
+====================
+CL_WriteDemoMessage
+
+Dumps the current net message, prefixed by the length
+====================
+*/
+void CL_WriteDemoMessage (void)
+{
+ int len, swlen;
+
+ // the first eight bytes are just packet sequencing stuff
+ len = net_message.cursize-8;
+ swlen = LittleLong(len);
+ fwrite (&swlen, 4, 1, cls.demofile);
+ fwrite (net_message.data+8, len, 1, cls.demofile);
+}
+
+
+/*
+====================
+CL_Stop_f
+
+stop recording a demo
+====================
+*/
+void CL_Stop_f (void)
+{
+ int len;
+
+ if (!cls.demorecording)
+ {
+ Com_Printf ("Not recording a demo.\n");
+ return;
+ }
+
+// finish up
+ len = -1;
+ fwrite (&len, 4, 1, cls.demofile);
+ fclose (cls.demofile);
+ cls.demofile = NULL;
+ cls.demorecording = false;
+ Com_Printf ("Stopped demo.\n");
+}
+
+/*
+====================
+CL_Record_f
+
+record <demoname>
+
+Begins recording a demo from the current position
+====================
+*/
+void CL_Record_f (void)
+{
+ char name[MAX_OSPATH];
+ char buf_data[MAX_MSGLEN];
+ sizebuf_t buf;
+ int i;
+ int len;
+ entity_state_t *ent;
+ entity_state_t nullstate;
+
+ if (Cmd_Argc() != 2)
+ {
+ Com_Printf ("record <demoname>\n");
+ return;
+ }
+
+ if (cls.demorecording)
+ {
+ Com_Printf ("Already recording.\n");
+ return;
+ }
+
+ if (cls.state != ca_active)
+ {
+ Com_Printf ("You must be in a level to record.\n");
+ return;
+ }
+
+ //
+ // open the demo file
+ //
+ Com_sprintf (name, sizeof(name), "%s/demos/%s.dm2", FS_Gamedir(), Cmd_Argv(1));
+
+ Com_Printf ("recording to %s.\n", name);
+ FS_CreatePath (name);
+ cls.demofile = fopen (name, "wb");
+ if (!cls.demofile)
+ {
+ Com_Printf ("ERROR: couldn't open.\n");
+ return;
+ }
+ cls.demorecording = true;
+
+ // don't start saving messages until a non-delta compressed message is received
+ cls.demowaiting = true;
+
+ //
+ // write out messages to hold the startup information
+ //
+ SZ_Init (&buf, buf_data, sizeof(buf_data));
+
+ // send the serverdata
+ MSG_WriteByte (&buf, svc_serverdata);
+ MSG_WriteLong (&buf, PROTOCOL_VERSION);
+ MSG_WriteLong (&buf, 0x10000 + cl.servercount);
+ MSG_WriteByte (&buf, 1); // demos are always attract loops
+ MSG_WriteString (&buf, cl.gamedir);
+ MSG_WriteShort (&buf, cl.playernum);
+
+ MSG_WriteString (&buf, cl.configstrings[CS_NAME]);
+
+ // configstrings
+ for (i=0 ; i<MAX_CONFIGSTRINGS ; i++)
+ {
+ if (cl.configstrings[i][0])
+ {
+ if (buf.cursize + strlen (cl.configstrings[i]) + 32 > buf.maxsize)
+ { // write it out
+ len = LittleLong (buf.cursize);
+ fwrite (&len, 4, 1, cls.demofile);
+ fwrite (buf.data, buf.cursize, 1, cls.demofile);
+ buf.cursize = 0;
+ }
+
+ MSG_WriteByte (&buf, svc_configstring);
+ MSG_WriteShort (&buf, i);
+ MSG_WriteString (&buf, cl.configstrings[i]);
+ }
+
+ }
+
+ // baselines
+ memset (&nullstate, 0, sizeof(nullstate));
+ for (i=0; i<MAX_EDICTS ; i++)
+ {
+ ent = &cl_entities[i].baseline;
+ if (!ent->modelindex)
+ continue;
+
+ if (buf.cursize + 64 > buf.maxsize)
+ { // write it out
+ len = LittleLong (buf.cursize);
+ fwrite (&len, 4, 1, cls.demofile);
+ fwrite (buf.data, buf.cursize, 1, cls.demofile);
+ buf.cursize = 0;
+ }
+
+ MSG_WriteByte (&buf, svc_spawnbaseline);
+ MSG_WriteDeltaEntity (&nullstate, &cl_entities[i].baseline, &buf, true, true);
+ }
+
+ MSG_WriteByte (&buf, svc_stufftext);
+ MSG_WriteString (&buf, "precache\n");
+
+ // write it to the demo file
+
+ len = LittleLong (buf.cursize);
+ fwrite (&len, 4, 1, cls.demofile);
+ fwrite (buf.data, buf.cursize, 1, cls.demofile);
+
+ // the rest of the demo file will be individual frames
+}
+
+//======================================================================
+
+/*
+===================
+Cmd_ForwardToServer
+
+adds the current command line as a clc_stringcmd to the client message.
+things like godmode, noclip, etc, are commands directed to the server,
+so when they are typed in at the console, they will need to be forwarded.
+===================
+*/
+void Cmd_ForwardToServer (void)
+{
+ char *cmd;
+
+ cmd = Cmd_Argv(0);
+ if (cls.state <= ca_connected || *cmd == '-' || *cmd == '+')
+ {
+ Com_Printf ("Unknown command \"%s\"\n", cmd);
+ return;
+ }
+
+ MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
+ SZ_Print (&cls.netchan.message, cmd);
+ if (Cmd_Argc() > 1)
+ {
+ SZ_Print (&cls.netchan.message, " ");
+ SZ_Print (&cls.netchan.message, Cmd_Args());
+ }
+}
+
+void CL_Setenv_f( void )
+{
+ int argc = Cmd_Argc();
+
+ if ( argc > 2 )
+ {
+ char buffer[1000];
+ int i;
+
+ strcpy( buffer, Cmd_Argv(1) );
+ strcat( buffer, "=" );
+
+ for ( i = 2; i < argc; i++ )
+ {
+ strcat( buffer, Cmd_Argv( i ) );
+ strcat( buffer, " " );
+ }
+
+ putenv( buffer );
+ }
+ else if ( argc == 2 )
+ {
+ char *env = getenv( Cmd_Argv(1) );
+
+ if ( env )
+ {
+ Com_Printf( "%s=%s\n", Cmd_Argv(1), env );
+ }
+ else
+ {
+ Com_Printf( "%s undefined\n", Cmd_Argv(1), env );
+ }
+ }
+}
+
+
+/*
+==================
+CL_ForwardToServer_f
+==================
+*/
+void CL_ForwardToServer_f (void)
+{
+ if (cls.state != ca_connected && cls.state != ca_active)
+ {
+ Com_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
+ return;
+ }
+
+ // don't forward the first argument
+ if (Cmd_Argc() > 1)
+ {
+ MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
+ SZ_Print (&cls.netchan.message, Cmd_Args());
+ }
+}
+
+
+/*
+==================
+CL_Pause_f
+==================
+*/
+void CL_Pause_f (void)
+{
+ // never pause in multiplayer
+ if (Cvar_VariableValue ("maxclients") > 1 || !Com_ServerState ())
+ {
+ Cvar_SetValue ("paused", 0);
+ return;
+ }
+
+ Cvar_SetValue ("paused", !cl_paused->value);
+}
+
+/*
+==================
+CL_Quit_f
+==================
+*/
+void CL_Quit_f (void)
+{
+ CL_Disconnect ();
+ Com_Quit ();
+}
+
+/*
+================
+CL_Drop
+
+Called after an ERR_DROP was thrown
+================
+*/
+void CL_Drop (void)
+{
+ if (cls.state == ca_uninitialized)
+ return;
+ if (cls.state == ca_disconnected)
+ return;
+
+ CL_Disconnect ();
+
+ // drop loading plaque unless this is the initial game start
+ if (cls.disable_servercount != -1)
+ SCR_EndLoadingPlaque (); // get rid of loading plaque
+}
+
+
+/*
+=======================
+CL_SendConnectPacket
+
+We have gotten a challenge from the server, so try and
+connect.
+======================
+*/
+void CL_SendConnectPacket (void)
+{
+ netadr_t adr;
+ int port;
+
+ if (!NET_StringToAdr (cls.servername, &adr))
+ {
+ Com_Printf ("Bad server address\n");
+ cls.connect_time = 0;
+ return;
+ }
+ if (adr.port == 0)
+ adr.port = BigShort (PORT_SERVER);
+
+ port = Cvar_VariableValue ("qport");
+ userinfo_modified = false;
+
+ Netchan_OutOfBandPrint (NS_CLIENT, adr, "connect %i %i %i \"%s\"\n",
+ PROTOCOL_VERSION, port, cls.challenge, Cvar_Userinfo() );
+}
+
+/*
+=================
+CL_CheckForResend
+
+Resend a connect message if the last one has timed out
+=================
+*/
+void CL_CheckForResend (void)
+{
+ netadr_t adr;
+
+ // if the local server is running and we aren't
+ // then connect
+ if (cls.state == ca_disconnected && Com_ServerState() )
+ {
+ cls.state = ca_connecting;
+ strncpy (cls.servername, "localhost", sizeof(cls.servername)-1);
+ // we don't need a challenge on the localhost
+ CL_SendConnectPacket ();
+ return;
+// cls.connect_time = -99999; // CL_CheckForResend() will fire immediately
+ }
+
+ // resend if we haven't gotten a reply yet
+ if (cls.state != ca_connecting)
+ return;
+
+ if (cls.realtime - cls.connect_time < 3000)
+ return;
+
+ if (!NET_StringToAdr (cls.servername, &adr))
+ {
+ Com_Printf ("Bad server address\n");
+ cls.state = ca_disconnected;
+ return;
+ }
+ if (adr.port == 0)
+ adr.port = BigShort (PORT_SERVER);
+
+ cls.connect_time = cls.realtime; // for retransmit requests
+
+ Com_Printf ("Connecting to %s...\n", cls.servername);
+
+ Netchan_OutOfBandPrint (NS_CLIENT, adr, "getchallenge\n");
+}
+
+
+/*
+================
+CL_Connect_f
+
+================
+*/
+void CL_Connect_f (void)
+{
+ char *server;
+
+ if (Cmd_Argc() != 2)
+ {
+ Com_Printf ("usage: connect <server>\n");
+ return;
+ }
+
+ if (Com_ServerState ())
+ { // if running a local server, kill it and reissue
+ SV_Shutdown (va("Server quit\n", msg), false);
+ }
+ else
+ {
+ CL_Disconnect ();
+ }
+
+ server = Cmd_Argv (1);
+
+ NET_Config (true); // allow remote
+
+ CL_Disconnect ();
+
+ cls.state = ca_connecting;
+ strncpy (cls.servername, server, sizeof(cls.servername)-1);
+ cls.connect_time = -99999; // CL_CheckForResend() will fire immediately
+}
+
+
+/*
+=====================
+CL_Rcon_f
+
+ Send the rest of the command line over as
+ an unconnected command.
+=====================
+*/
+void CL_Rcon_f (void)
+{
+ char message[1024];
+ int i;
+ netadr_t to;
+
+ if (!rcon_client_password->string)
+ {
+ Com_Printf ("You must set 'rcon_password' before\n"
+ "issuing an rcon command.\n");
+ return;
+ }
+
+ message[0] = (char)255;
+ message[1] = (char)255;
+ message[2] = (char)255;
+ message[3] = (char)255;
+ message[4] = 0;
+
+ NET_Config (true); // allow remote
+
+ strcat (message, "rcon ");
+
+ strcat (message, rcon_client_password->string);
+ strcat (message, " ");
+
+ for (i=1 ; i<Cmd_Argc() ; i++)
+ {
+ strcat (message, Cmd_Argv(i));
+ strcat (message, " ");
+ }
+
+ if (cls.state >= ca_connected)
+ to = cls.netchan.remote_address;
+ else
+ {
+ if (!strlen(rcon_address->string))
+ {
+ Com_Printf ("You must either be connected,\n"
+ "or set the 'rcon_address' cvar\n"
+ "to issue rcon commands\n");
+
+ return;
+ }
+ NET_StringToAdr (rcon_address->string, &to);
+ if (to.port == 0)
+ to.port = BigShort (PORT_SERVER);
+ }
+
+ NET_SendPacket (NS_CLIENT, strlen(message)+1, message, to);
+}
+
+
+/*
+=====================
+CL_ClearState
+
+=====================
+*/
+void CL_ClearState (void)
+{
+ S_StopAllSounds ();
+ CL_ClearEffects ();
+ CL_ClearTEnts ();
+
+// wipe the entire cl structure
+ memset (&cl, 0, sizeof(cl));
+ memset (&cl_entities, 0, sizeof(cl_entities));
+
+ SZ_Clear (&cls.netchan.message);
+
+}
+
+/*
+=====================
+CL_Disconnect
+
+Goes from a connected state to full screen console state
+Sends a disconnect message to the server
+This is also called on Com_Error, so it shouldn't cause any errors
+=====================
+*/
+void CL_Disconnect (void)
+{
+ byte final[32];
+
+ if (cls.state == ca_disconnected)
+ return;
+
+ if (cl_timedemo && cl_timedemo->value)
+ {
+ int time;
+
+ time = Sys_Milliseconds () - cl.timedemo_start;
+ if (time > 0)
+ Com_Printf ("%i frames, %3.1f seconds: %3.1f fps\n", cl.timedemo_frames,
+ time/1000.0, cl.timedemo_frames*1000.0 / time);
+ }
+
+ VectorClear (cl.refdef.blend);
+ re.CinematicSetPalette(NULL);
+
+ M_ForceMenuOff ();
+
+ cls.connect_time = 0;
+
+ SCR_StopCinematic ();
+
+ if (cls.demorecording)
+ CL_Stop_f ();
+
+ // send a disconnect message to the server
+ final[0] = clc_stringcmd;
+ strcpy ((char *)final+1, "disconnect");
+ Netchan_Transmit (&cls.netchan, strlen(final), final);
+ Netchan_Transmit (&cls.netchan, strlen(final), final);
+ Netchan_Transmit (&cls.netchan, strlen(final), final);
+
+ CL_ClearState ();
+
+ // stop download
+ if (cls.download) {
+ fclose(cls.download);
+ cls.download = NULL;
+ }
+
+ cls.state = ca_disconnected;
+}
+
+void CL_Disconnect_f (void)
+{
+ Com_Error (ERR_DROP, "Disconnected from server");
+}
+
+
+/*
+====================
+CL_Packet_f
+
+packet <destination> <contents>
+
+Contents allows \n escape character
+====================
+*/
+void CL_Packet_f (void)
+{
+ char send[2048];
+ int i, l;
+ char *in, *out;
+ netadr_t adr;
+
+ if (Cmd_Argc() != 3)
+ {
+ Com_Printf ("packet <destination> <contents>\n");
+ return;
+ }
+
+ NET_Config (true); // allow remote
+
+ if (!NET_StringToAdr (Cmd_Argv(1), &adr))
+ {
+ Com_Printf ("Bad address\n");
+ return;
+ }
+ if (!adr.port)
+ adr.port = BigShort (PORT_SERVER);
+
+ in = Cmd_Argv(2);
+ out = send+4;
+ send[0] = send[1] = send[2] = send[3] = (char)0xff;
+
+ l = strlen (in);
+ for (i=0 ; i<l ; i++)
+ {
+ if (in[i] == '\\' && in[i+1] == 'n')
+ {
+ *out++ = '\n';
+ i++;
+ }
+ else
+ *out++ = in[i];
+ }
+ *out = 0;
+
+ NET_SendPacket (NS_CLIENT, out-send, send, adr);
+}
+
+/*
+=================
+CL_Changing_f
+
+Just sent as a hint to the client that they should
+drop to full console
+=================
+*/
+void CL_Changing_f (void)
+{
+ //ZOID
+ //if we are downloading, we don't change! This so we don't suddenly stop downloading a map
+ if (cls.download)
+ return;
+
+ SCR_BeginLoadingPlaque ();
+ cls.state = ca_connected; // not active anymore, but not disconnected
+ Com_Printf ("\nChanging map...\n");
+}
+
+
+/*
+=================
+CL_Reconnect_f
+
+The server is changing levels
+=================
+*/
+void CL_Reconnect_f (void)
+{
+ //ZOID
+ //if we are downloading, we don't change! This so we don't suddenly stop downloading a map
+ if (cls.download)
+ return;
+
+ S_StopAllSounds ();
+ if (cls.state == ca_connected) {
+ Com_Printf ("reconnecting...\n");
+ cls.state = ca_connected;
+ MSG_WriteChar (&cls.netchan.message, clc_stringcmd);
+ MSG_WriteString (&cls.netchan.message, "new");
+ return;
+ }
+
+ if (*cls.servername) {
+ if (cls.state >= ca_connected) {
+ CL_Disconnect();
+ cls.connect_time = cls.realtime - 1500;
+ } else
+ cls.connect_time = -99999; // fire immediately
+
+ cls.state = ca_connecting;
+ Com_Printf ("reconnecting...\n");
+ }
+}
+
+/*
+=================
+CL_ParseStatusMessage
+
+Handle a reply from a ping
+=================
+*/
+void CL_ParseStatusMessage (void)
+{
+ char *s;
+
+ s = MSG_ReadString(&net_message);
+
+ Com_Printf ("%s\n", s);
+ M_AddToServerList (net_from, s);
+}
+
+
+/*
+=================
+CL_PingServers_f
+=================
+*/
+void CL_PingServers_f (void)
+{
+ int i;
+ netadr_t adr;
+ char name[32];
+ char *adrstring;
+ cvar_t *noudp;
+ cvar_t *noipx;
+
+ NET_Config (true); // allow remote
+
+ // send a broadcast packet
+ Com_Printf ("pinging broadcast...\n");
+
+ noudp = Cvar_Get ("noudp", "0", CVAR_NOSET);
+ if (!noudp->value)
+ {
+ adr.type = NA_BROADCAST;
+ adr.port = BigShort(PORT_SERVER);
+ Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION));
+ }
+
+ noipx = Cvar_Get ("noipx", "0", CVAR_NOSET);
+ if (!noipx->value)
+ {
+ adr.type = NA_BROADCAST_IPX;
+ adr.port = BigShort(PORT_SERVER);
+ Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION));
+ }
+
+ // send a packet to each address book entry
+ for (i=0 ; i<16 ; i++)
+ {
+ Com_sprintf (name, sizeof(name), "adr%i", i);
+ adrstring = Cvar_VariableString (name);
+ if (!adrstring || !adrstring[0])
+ continue;
+
+ Com_Printf ("pinging %s...\n", adrstring);
+ if (!NET_StringToAdr (adrstring, &adr))
+ {
+ Com_Printf ("Bad address: %s\n", adrstring);
+ continue;
+ }
+ if (!adr.port)
+ adr.port = BigShort(PORT_SERVER);
+ Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION));
+ }
+}
+
+
+/*
+=================
+CL_Skins_f
+
+Load or download any custom player skins and models
+=================
+*/
+void CL_Skins_f (void)
+{
+ int i;
+
+ for (i=0 ; i<MAX_CLIENTS ; i++)
+ {
+ if (!cl.configstrings[CS_PLAYERSKINS+i][0])
+ continue;
+ Com_Printf ("client %i: %s\n", i, cl.configstrings[CS_PLAYERSKINS+i]);
+ SCR_UpdateScreen ();
+ Sys_SendKeyEvents (); // pump message loop
+ CL_ParseClientinfo (i);
+ }
+}
+
+
+/*
+=================
+CL_ConnectionlessPacket
+
+Responses to broadcasts, etc
+=================
+*/
+void CL_ConnectionlessPacket (void)
+{
+ char *s;
+ char *c;
+
+ MSG_BeginReading (&net_message);
+ MSG_ReadLong (&net_message); // skip the -1
+
+ s = MSG_ReadStringLine (&net_message);
+
+ Cmd_TokenizeString (s, false);
+
+ c = Cmd_Argv(0);
+
+ Com_Printf ("%s: %s\n", NET_AdrToString (net_from), c);
+
+ // server connection
+ if (!strcmp(c, "client_connect"))
+ {
+ if (cls.state == ca_connected)
+ {
+ Com_Printf ("Dup connect received. Ignored.\n");
+ return;
+ }
+ Netchan_Setup (NS_CLIENT, &cls.netchan, net_from, cls.quakePort);
+ MSG_WriteChar (&cls.netchan.message, clc_stringcmd);
+ MSG_WriteString (&cls.netchan.message, "new");
+ cls.state = ca_connected;
+ return;
+ }
+
+ // server responding to a status broadcast
+ if (!strcmp(c, "info"))
+ {
+ CL_ParseStatusMessage ();
+ return;
+ }
+
+ // remote command from gui front end
+ if (!strcmp(c, "cmd"))
+ {
+ if (!NET_IsLocalAddress(net_from))
+ {
+ Com_Printf ("Command packet from remote host. Ignored.\n");
+ return;
+ }
+ Sys_AppActivate ();
+ s = MSG_ReadString (&net_message);
+ Cbuf_AddText (s);
+ Cbuf_AddText ("\n");
+ return;
+ }
+ // print command from somewhere
+ if (!strcmp(c, "print"))
+ {
+ s = MSG_ReadString (&net_message);
+ Com_Printf ("%s", s);
+ return;
+ }
+
+ // ping from somewhere
+ if (!strcmp(c, "ping"))
+ {
+ Netchan_OutOfBandPrint (NS_CLIENT, net_from, "ack");
+ return;
+ }
+
+ // challenge from the server we are connecting to
+ if (!strcmp(c, "challenge"))
+ {
+ cls.challenge = atoi(Cmd_Argv(1));
+ CL_SendConnectPacket ();
+ return;
+ }
+
+ // echo request from server
+ if (!strcmp(c, "echo"))
+ {
+ Netchan_OutOfBandPrint (NS_CLIENT, net_from, "%s", Cmd_Argv(1) );
+ return;
+ }
+
+ Com_Printf ("Unknown command.\n");
+}
+
+
+/*
+=================
+CL_DumpPackets
+
+A vain attempt to help bad TCP stacks that cause problems
+when they overflow
+=================
+*/
+void CL_DumpPackets (void)
+{
+ while (NET_GetPacket (NS_CLIENT, &net_from, &net_message))
+ {
+ Com_Printf ("dumnping a packet\n");
+ }
+}
+
+/*
+=================
+CL_ReadPackets
+=================
+*/
+void CL_ReadPackets (void)
+{
+ while (NET_GetPacket (NS_CLIENT, &net_from, &net_message))
+ {
+// Com_Printf ("packet\n");
+ //
+ // remote command packet
+ //
+ if (*(int *)net_message.data == -1)
+ {
+ CL_ConnectionlessPacket ();
+ continue;
+ }
+
+ if (cls.state == ca_disconnected || cls.state == ca_connecting)
+ continue; // dump it if not connected
+
+ if (net_message.cursize < 8)
+ {
+ Com_Printf ("%s: Runt packet\n",NET_AdrToString(net_from));
+ continue;
+ }
+
+ //
+ // packet from server
+ //
+ if (!NET_CompareAdr (net_from, cls.netchan.remote_address))
+ {
+ Com_DPrintf ("%s:sequenced packet without connection\n"
+ ,NET_AdrToString(net_from));
+ continue;
+ }
+ if (!Netchan_Process(&cls.netchan, &net_message))
+ continue; // wasn't accepted for some reason
+ CL_ParseServerMessage ();
+ }
+
+ //
+ // check timeout
+ //
+ if (cls.state >= ca_connected
+ && cls.realtime - cls.netchan.last_received > cl_timeout->value*1000)
+ {
+ if (++cl.timeoutcount > 5) // timeoutcount saves debugger
+ {
+ Com_Printf ("\nServer connection timed out.\n");
+ CL_Disconnect ();
+ return;
+ }
+ }
+ else
+ cl.timeoutcount = 0;
+
+}
+
+
+//=============================================================================
+
+/*
+==============
+CL_FixUpGender_f
+==============
+*/
+void CL_FixUpGender(void)
+{
+ char *p;
+ char sk[80];
+
+ if (gender_auto->value) {
+
+ if (gender->modified) {
+ // was set directly, don't override the user
+ gender->modified = false;
+ return;
+ }
+
+ strncpy(sk, skin->string, sizeof(sk) - 1);
+ if ((p = strchr(sk, '/')) != NULL)
+ *p = 0;
+ if (Q_stricmp(sk, "male") == 0 || Q_stricmp(sk, "cyborg") == 0)
+ Cvar_Set ("gender", "male");
+ else if (Q_stricmp(sk, "female") == 0 || Q_stricmp(sk, "crackhor") == 0)
+ Cvar_Set ("gender", "female");
+ else
+ Cvar_Set ("gender", "none");
+ gender->modified = false;
+ }
+}
+
+/*
+==============
+CL_Userinfo_f
+==============
+*/
+void CL_Userinfo_f (void)
+{
+ Com_Printf ("User info settings:\n");
+ Info_Print (Cvar_Userinfo());
+}
+
+/*
+=================
+CL_Snd_Restart_f
+
+Restart the sound subsystem so it can pick up
+new parameters and flush all sounds
+=================
+*/
+void CL_Snd_Restart_f (void)
+{
+ S_Shutdown ();
+ S_Init ();
+ CL_RegisterSounds ();
+}
+
+int precache_check; // for autodownload of precache items
+int precache_spawncount;
+int precache_tex;
+int precache_model_skin;
+
+byte *precache_model; // used for skin checking in alias models
+
+#define PLAYER_MULT 5
+
+// ENV_CNT is map load, ENV_CNT+1 is first env map
+#define ENV_CNT (CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT)
+#define TEXTURE_CNT (ENV_CNT+13)
+
+static const char *env_suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"};
+
+void CL_RequestNextDownload (void)
+{
+ unsigned map_checksum; // for detecting cheater maps
+ char fn[MAX_OSPATH];
+ dmdl_t *pheader;
+
+ if (cls.state != ca_connected)
+ return;
+
+ if (!allow_download->value && precache_check < ENV_CNT)
+ precache_check = ENV_CNT;
+
+//ZOID
+ if (precache_check == CS_MODELS) { // confirm map
+ precache_check = CS_MODELS+2; // 0 isn't used
+ if (allow_download_maps->value)
+ if (!CL_CheckOrDownloadFile(cl.configstrings[CS_MODELS+1]))
+ return; // started a download
+ }
+ if (precache_check >= CS_MODELS && precache_check < CS_MODELS+MAX_MODELS) {
+ if (allow_download_models->value) {
+ while (precache_check < CS_MODELS+MAX_MODELS &&
+ cl.configstrings[precache_check][0]) {
+ if (cl.configstrings[precache_check][0] == '*' ||
+ cl.configstrings[precache_check][0] == '#') {
+ precache_check++;
+ continue;
+ }
+ if (precache_model_skin == 0) {
+ if (!CL_CheckOrDownloadFile(cl.configstrings[precache_check])) {
+ precache_model_skin = 1;
+ return; // started a download
+ }
+ precache_model_skin = 1;
+ }
+
+ // checking for skins in the model
+ if (!precache_model) {
+
+ FS_LoadFile (cl.configstrings[precache_check], (void **)&precache_model);
+ if (!precache_model) {
+ precache_model_skin = 0;
+ precache_check++;
+ continue; // couldn't load it
+ }
+ if (LittleLong(*(unsigned *)precache_model) != IDALIASHEADER) {
+ // not an alias model
+ FS_FreeFile(precache_model);
+ precache_model = 0;
+ precache_model_skin = 0;
+ precache_check++;
+ continue;
+ }
+ pheader = (dmdl_t *)precache_model;
+ if (LittleLong (pheader->version) != ALIAS_VERSION) {
+ precache_check++;
+ precache_model_skin = 0;
+ continue; // couldn't load it
+ }
+ }
+
+ pheader = (dmdl_t *)precache_model;
+
+ while (precache_model_skin - 1 < LittleLong(pheader->num_skins)) {
+ if (!CL_CheckOrDownloadFile((char *)precache_model +
+ LittleLong(pheader->ofs_skins) +
+ (precache_model_skin - 1)*MAX_SKINNAME)) {
+ precache_model_skin++;
+ return; // started a download
+ }
+ precache_model_skin++;
+ }
+ if (precache_model) {
+ FS_FreeFile(precache_model);
+ precache_model = 0;
+ }
+ precache_model_skin = 0;
+ precache_check++;
+ }
+ }
+ precache_check = CS_SOUNDS;
+ }
+ if (precache_check >= CS_SOUNDS && precache_check < CS_SOUNDS+MAX_SOUNDS) {
+ if (allow_download_sounds->value) {
+ if (precache_check == CS_SOUNDS)
+ precache_check++; // zero is blank
+ while (precache_check < CS_SOUNDS+MAX_SOUNDS &&
+ cl.configstrings[precache_check][0]) {
+ if (cl.configstrings[precache_check][0] == '*') {
+ precache_check++;
+ continue;
+ }
+ Com_sprintf(fn, sizeof(fn), "sound/%s", cl.configstrings[precache_check++]);
+ if (!CL_CheckOrDownloadFile(fn))
+ return; // started a download
+ }
+ }
+ precache_check = CS_IMAGES;
+ }
+ if (precache_check >= CS_IMAGES && precache_check < CS_IMAGES+MAX_IMAGES) {
+ if (precache_check == CS_IMAGES)
+ precache_check++; // zero is blank
+ while (precache_check < CS_IMAGES+MAX_IMAGES &&
+ cl.configstrings[precache_check][0]) {
+ Com_sprintf(fn, sizeof(fn), "pics/%s.pcx", cl.configstrings[precache_check++]);
+ if (!CL_CheckOrDownloadFile(fn))
+ return; // started a download
+ }
+ precache_check = CS_PLAYERSKINS;
+ }
+ // skins are special, since a player has three things to download:
+ // model, weapon model and skin
+ // so precache_check is now *3
+ if (precache_check >= CS_PLAYERSKINS && precache_check < CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT) {
+ if (allow_download_players->value) {
+ while (precache_check < CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT) {
+ int i, n;
+ char model[MAX_QPATH], skin[MAX_QPATH], *p;
+
+ i = (precache_check - CS_PLAYERSKINS)/PLAYER_MULT;
+ n = (precache_check - CS_PLAYERSKINS)%PLAYER_MULT;
+
+ if (!cl.configstrings[CS_PLAYERSKINS+i][0]) {
+ precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
+ continue;
+ }
+
+ if ((p = strchr(cl.configstrings[CS_PLAYERSKINS+i], '\\')) != NULL)
+ p++;
+ else
+ p = cl.configstrings[CS_PLAYERSKINS+i];
+ strcpy(model, p);
+ p = strchr(model, '/');
+ if (!p)
+ p = strchr(model, '\\');
+ if (p) {
+ *p++ = 0;
+ strcpy(skin, p);
+ } else
+ *skin = 0;
+
+ switch (n) {
+ case 0: // model
+ Com_sprintf(fn, sizeof(fn), "players/%s/tris.md2", model);
+ if (!CL_CheckOrDownloadFile(fn)) {
+ precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 1;
+ return; // started a download
+ }
+ n++;
+ /*FALL THROUGH*/
+
+ case 1: // weapon model
+ Com_sprintf(fn, sizeof(fn), "players/%s/weapon.md2", model);
+ if (!CL_CheckOrDownloadFile(fn)) {
+ precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 2;
+ return; // started a download
+ }
+ n++;
+ /*FALL THROUGH*/
+
+ case 2: // weapon skin
+ Com_sprintf(fn, sizeof(fn), "players/%s/weapon.pcx", model);
+ if (!CL_CheckOrDownloadFile(fn)) {
+ precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 3;
+ return; // started a download
+ }
+ n++;
+ /*FALL THROUGH*/
+
+ case 3: // skin
+ Com_sprintf(fn, sizeof(fn), "players/%s/%s.pcx", model, skin);
+ if (!CL_CheckOrDownloadFile(fn)) {
+ precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 4;
+ return; // started a download
+ }
+ n++;
+ /*FALL THROUGH*/
+
+ case 4: // skin_i
+ Com_sprintf(fn, sizeof(fn), "players/%s/%s_i.pcx", model, skin);
+ if (!CL_CheckOrDownloadFile(fn)) {
+ precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 5;
+ return; // started a download
+ }
+ // move on to next model
+ precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
+ }
+ }
+ }
+ // precache phase completed
+ precache_check = ENV_CNT;
+ }
+
+ if (precache_check == ENV_CNT) {
+ precache_check = ENV_CNT + 1;
+
+ CM_LoadMap (cl.configstrings[CS_MODELS+1], true, &map_checksum);
+
+ if (map_checksum != atoi(cl.configstrings[CS_MAPCHECKSUM])) {
+ Com_Error (ERR_DROP, "Local map version differs from server: %i != '%s'\n",
+ map_checksum, cl.configstrings[CS_MAPCHECKSUM]);
+ return;
+ }
+ }
+
+ if (precache_check > ENV_CNT && precache_check < TEXTURE_CNT) {
+ if (allow_download->value && allow_download_maps->value) {
+ while (precache_check < TEXTURE_CNT) {
+ int n = precache_check++ - ENV_CNT - 1;
+
+ if (n & 1)
+ Com_sprintf(fn, sizeof(fn), "env/%s%s.pcx",
+ cl.configstrings[CS_SKY], env_suf[n/2]);
+ else
+ Com_sprintf(fn, sizeof(fn), "env/%s%s.tga",
+ cl.configstrings[CS_SKY], env_suf[n/2]);
+ if (!CL_CheckOrDownloadFile(fn))
+ return; // started a download
+ }
+ }
+ precache_check = TEXTURE_CNT;
+ }
+
+ if (precache_check == TEXTURE_CNT) {
+ precache_check = TEXTURE_CNT+1;
+ precache_tex = 0;
+ }
+
+ // confirm existance of textures, download any that don't exist
+ if (precache_check == TEXTURE_CNT+1) {
+ // from qcommon/cmodel.c
+ extern int numtexinfo;
+ extern mapsurface_t map_surfaces[];
+
+ if (allow_download->value && allow_download_maps->value) {
+ while (precache_tex < numtexinfo) {
+ char fn[MAX_OSPATH];
+
+ sprintf(fn, "textures/%s.wal", map_surfaces[precache_tex++].rname);
+ if (!CL_CheckOrDownloadFile(fn))
+ return; // started a download
+ }
+ }
+ precache_check = TEXTURE_CNT+999;
+ }
+
+//ZOID
+ CL_RegisterSounds ();
+ CL_PrepRefresh ();
+
+ MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
+ MSG_WriteString (&cls.netchan.message, va("begin %i\n", precache_spawncount) );
+}
+
+/*
+=================
+CL_Precache_f
+
+The server will send this command right
+before allowing the client into the server
+=================
+*/
+void CL_Precache_f (void)
+{
+ //Yet another hack to let old demos work
+ //the old precache sequence
+ if (Cmd_Argc() < 2) {
+ unsigned map_checksum; // for detecting cheater maps
+
+ CM_LoadMap (cl.configstrings[CS_MODELS+1], true, &map_checksum);
+ CL_RegisterSounds ();
+ CL_PrepRefresh ();
+ return;
+ }
+
+ precache_check = CS_MODELS;
+ precache_spawncount = atoi(Cmd_Argv(1));
+ precache_model = 0;
+ precache_model_skin = 0;
+
+ CL_RequestNextDownload();
+}
+
+
+/*
+=================
+CL_InitLocal
+=================
+*/
+void CL_InitLocal (void)
+{
+ cls.state = ca_disconnected;
+ cls.realtime = Sys_Milliseconds ();
+
+ CL_InitInput ();
+
+ adr0 = Cvar_Get( "adr0", "", CVAR_ARCHIVE );
+ adr1 = Cvar_Get( "adr1", "", CVAR_ARCHIVE );
+ adr2 = Cvar_Get( "adr2", "", CVAR_ARCHIVE );
+ adr3 = Cvar_Get( "adr3", "", CVAR_ARCHIVE );
+ adr4 = Cvar_Get( "adr4", "", CVAR_ARCHIVE );
+ adr5 = Cvar_Get( "adr5", "", CVAR_ARCHIVE );
+ adr6 = Cvar_Get( "adr6", "", CVAR_ARCHIVE );
+ adr7 = Cvar_Get( "adr7", "", CVAR_ARCHIVE );
+ adr8 = Cvar_Get( "adr8", "", CVAR_ARCHIVE );
+
+//
+// register our variables
+//
+ cl_stereo_separation = Cvar_Get( "cl_stereo_separation", "0.4", CVAR_ARCHIVE );
+ cl_stereo = Cvar_Get( "cl_stereo", "0", 0 );
+
+ cl_add_blend = Cvar_Get ("cl_blend", "1", 0);
+ cl_add_lights = Cvar_Get ("cl_lights", "1", 0);
+ cl_add_particles = Cvar_Get ("cl_particles", "1", 0);
+ cl_add_entities = Cvar_Get ("cl_entities", "1", 0);
+ cl_gun = Cvar_Get ("cl_gun", "1", 0);
+ cl_footsteps = Cvar_Get ("cl_footsteps", "1", 0);
+ cl_noskins = Cvar_Get ("cl_noskins", "0", 0);
+ cl_autoskins = Cvar_Get ("cl_autoskins", "0", 0);
+ cl_predict = Cvar_Get ("cl_predict", "1", 0);
+// cl_minfps = Cvar_Get ("cl_minfps", "5", 0);
+ cl_maxfps = Cvar_Get ("cl_maxfps", "90", 0);
+
+ cl_upspeed = Cvar_Get ("cl_upspeed", "200", 0);
+ cl_forwardspeed = Cvar_Get ("cl_forwardspeed", "200", 0);
+ cl_sidespeed = Cvar_Get ("cl_sidespeed", "200", 0);
+ cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", 0);
+ cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "150", 0);
+ cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", 0);
+
+ cl_run = Cvar_Get ("cl_run", "0", CVAR_ARCHIVE);
+ freelook = Cvar_Get( "freelook", "0", CVAR_ARCHIVE );
+ lookspring = Cvar_Get ("lookspring", "0", CVAR_ARCHIVE);
+ lookstrafe = Cvar_Get ("lookstrafe", "0", CVAR_ARCHIVE);
+ sensitivity = Cvar_Get ("sensitivity", "3", CVAR_ARCHIVE);
+
+ m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE);
+ m_yaw = Cvar_Get ("m_yaw", "0.022", 0);
+ m_forward = Cvar_Get ("m_forward", "1", 0);
+ m_side = Cvar_Get ("m_side", "1", 0);
+
+ cl_shownet = Cvar_Get ("cl_shownet", "0", 0);
+ cl_showmiss = Cvar_Get ("cl_showmiss", "0", 0);
+ cl_showclamp = Cvar_Get ("showclamp", "0", 0);
+ cl_timeout = Cvar_Get ("cl_timeout", "120", 0);
+ cl_paused = Cvar_Get ("paused", "0", 0);
+ cl_timedemo = Cvar_Get ("timedemo", "0", 0);
+
+ rcon_client_password = Cvar_Get ("rcon_password", "", 0);
+ rcon_address = Cvar_Get ("rcon_address", "", 0);
+
+ cl_lightlevel = Cvar_Get ("r_lightlevel", "0", 0);
+
+ //
+ // userinfo
+ //
+ info_password = Cvar_Get ("password", "", CVAR_USERINFO);
+ info_spectator = Cvar_Get ("spectator", "0", CVAR_USERINFO);
+ name = Cvar_Get ("name", "unnamed", CVAR_USERINFO | CVAR_ARCHIVE);
+ skin = Cvar_Get ("skin", "male/grunt", CVAR_USERINFO | CVAR_ARCHIVE);
+ rate = Cvar_Get ("rate", "25000", CVAR_USERINFO | CVAR_ARCHIVE); // FIXME
+ msg = Cvar_Get ("msg", "1", CVAR_USERINFO | CVAR_ARCHIVE);
+ hand = Cvar_Get ("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE);
+ fov = Cvar_Get ("fov", "90", CVAR_USERINFO | CVAR_ARCHIVE);
+ gender = Cvar_Get ("gender", "male", CVAR_USERINFO | CVAR_ARCHIVE);
+ gender_auto = Cvar_Get ("gender_auto", "1", CVAR_ARCHIVE);
+ gender->modified = false; // clear this so we know when user sets it manually
+
+ cl_vwep = Cvar_Get ("cl_vwep", "1", CVAR_ARCHIVE);
+
+
+ //
+ // register our commands
+ //
+ Cmd_AddCommand ("cmd", CL_ForwardToServer_f);
+ Cmd_AddCommand ("pause", CL_Pause_f);
+ Cmd_AddCommand ("pingservers", CL_PingServers_f);
+ Cmd_AddCommand ("skins", CL_Skins_f);
+
+ Cmd_AddCommand ("userinfo", CL_Userinfo_f);
+ Cmd_AddCommand ("snd_restart", CL_Snd_Restart_f);
+
+ Cmd_AddCommand ("changing", CL_Changing_f);
+ Cmd_AddCommand ("disconnect", CL_Disconnect_f);
+ Cmd_AddCommand ("record", CL_Record_f);
+ Cmd_AddCommand ("stop", CL_Stop_f);
+
+ Cmd_AddCommand ("quit", CL_Quit_f);
+
+ Cmd_AddCommand ("connect", CL_Connect_f);
+ Cmd_AddCommand ("reconnect", CL_Reconnect_f);
+
+ Cmd_AddCommand ("rcon", CL_Rcon_f);
+
+// Cmd_AddCommand ("packet", CL_Packet_f); // this is dangerous to leave in
+
+ Cmd_AddCommand ("setenv", CL_Setenv_f );
+
+ Cmd_AddCommand ("precache", CL_Precache_f);
+
+ Cmd_AddCommand ("download", CL_Download_f);
+
+ //
+ // forward to server commands
+ //
+ // the only thing this does is allow command completion
+ // to work -- all unknown commands are automatically
+ // forwarded to the server
+ Cmd_AddCommand ("wave", NULL);
+ Cmd_AddCommand ("inven", NULL);
+ Cmd_AddCommand ("kill", NULL);
+ Cmd_AddCommand ("use", NULL);
+ Cmd_AddCommand ("drop", NULL);
+ Cmd_AddCommand ("say", NULL);
+ Cmd_AddCommand ("say_team", NULL);
+ Cmd_AddCommand ("info", NULL);
+ Cmd_AddCommand ("prog", NULL);
+ Cmd_AddCommand ("give", NULL);
+ Cmd_AddCommand ("god", NULL);
+ Cmd_AddCommand ("notarget", NULL);
+ Cmd_AddCommand ("noclip", NULL);
+ Cmd_AddCommand ("invuse", NULL);
+ Cmd_AddCommand ("invprev", NULL);
+ Cmd_AddCommand ("invnext", NULL);
+ Cmd_AddCommand ("invdrop", NULL);
+ Cmd_AddCommand ("weapnext", NULL);
+ Cmd_AddCommand ("weapprev", NULL);
+}
+
+
+
+/*
+===============
+CL_WriteConfiguration
+
+Writes key bindings and archived cvars to config.cfg
+===============
+*/
+void CL_WriteConfiguration (void)
+{
+ FILE *f;
+ char path[MAX_QPATH];
+
+ if (cls.state == ca_uninitialized)
+ return;
+
+ Com_sprintf (path, sizeof(path),"%s/config.cfg",FS_Gamedir());
+ f = fopen (path, "w");
+ if (!f)
+ {
+ Com_Printf ("Couldn't write config.cfg.\n");
+ return;
+ }
+
+ fprintf (f, "// generated by quake, do not modify\n");
+ Key_WriteBindings (f);
+ fclose (f);
+
+ Cvar_WriteVariables (path);
+}
+
+
+/*
+==================
+CL_FixCvarCheats
+
+==================
+*/
+
+typedef struct
+{
+ char *name;
+ char *value;
+ cvar_t *var;
+} cheatvar_t;
+
+cheatvar_t cheatvars[] = {
+ {"timescale", "1"},
+ {"timedemo", "0"},
+ {"r_drawworld", "1"},
+ {"cl_testlights", "0"},
+ {"r_fullbright", "0"},
+ {"r_drawflat", "0"},
+ {"paused", "0"},
+ {"fixedtime", "0"},
+ {"sw_draworder", "0"},
+ {"gl_lightmap", "0"},
+ {"gl_saturatelighting", "0"},
+ {NULL, NULL}
+};
+
+int numcheatvars;
+
+void CL_FixCvarCheats (void)
+{
+ int i;
+ cheatvar_t *var;
+
+ if ( !strcmp(cl.configstrings[CS_MAXCLIENTS], "1")
+ || !cl.configstrings[CS_MAXCLIENTS][0] )
+ return; // single player can cheat
+
+ // find all the cvars if we haven't done it yet
+ if (!numcheatvars)
+ {
+ while (cheatvars[numcheatvars].name)
+ {
+ cheatvars[numcheatvars].var = Cvar_Get (cheatvars[numcheatvars].name,
+ cheatvars[numcheatvars].value, 0);
+ numcheatvars++;
+ }
+ }
+
+ // make sure they are all set to the proper values
+ for (i=0, var = cheatvars ; i<numcheatvars ; i++, var++)
+ {
+ if ( strcmp (var->var->string, var->value) )
+ {
+ Cvar_Set (var->name, var->value);
+ }
+ }
+}
+
+//============================================================================
+
+/*
+==================
+CL_SendCommand
+
+==================
+*/
+void CL_SendCommand (void)
+{
+ // get new key events
+ Sys_SendKeyEvents ();
+
+ // allow mice or other external controllers to add commands
+ IN_Commands ();
+
+ // process console commands
+ Cbuf_Execute ();
+
+ // fix any cheating cvars
+ CL_FixCvarCheats ();
+
+ // send intentions now
+ CL_SendCmd ();
+
+ // resend a connection request if necessary
+ CL_CheckForResend ();
+}
+
+
+/*
+==================
+CL_Frame
+
+==================
+*/
+void CL_Frame (int msec)
+{
+ static int extratime;
+ static int lasttimecalled;
+
+ if (dedicated->value)
+ return;
+
+ extratime += msec;
+
+ 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
+ }
+
+ // let the mouse activate or deactivate
+ IN_Frame ();
+
+ // decide the simulation time
+ cls.frametime = extratime/1000.0;
+ cl.time += extratime;
+ cls.realtime = curtime;
+
+ 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 in the debugger last frame, don't timeout
+ if (msec > 5000)
+ cls.netchan.last_received = Sys_Milliseconds ();
+
+ // fetch results from server
+ CL_ReadPackets ();
+
+ // send a new command message to the server
+ CL_SendCommand ();
+
+ // predict all unacknowledged movements
+ CL_PredictMovement ();
+
+ // allow rendering DLL change
+ VID_CheckChanges ();
+ if (!cl.refresh_prepped && cls.state == ca_active)
+ CL_PrepRefresh ();
+
+ // update the screen
+ if (host_speeds->value)
+ time_before_ref = Sys_Milliseconds ();
+ SCR_UpdateScreen ();
+ if (host_speeds->value)
+ time_after_ref = Sys_Milliseconds ();
+
+ // update audio
+ S_Update (cl.refdef.vieworg, cl.v_forward, cl.v_right, cl.v_up);
+
+ CDAudio_Update();
+
+ // advance local effects for next frame
+ CL_RunDLights ();
+ CL_RunLightStyles ();
+ SCR_RunCinematic ();
+ SCR_RunConsole ();
+
+ cls.framecount++;
+
+ if ( log_stats->value )
+ {
+ if ( cls.state == ca_active )
+ {
+ if ( !lasttimecalled )
+ {
+ lasttimecalled = Sys_Milliseconds();
+ if ( log_stats_file )
+ fprintf( log_stats_file, "0\n" );
+ }
+ else
+ {
+ int now = Sys_Milliseconds();
+
+ if ( log_stats_file )
+ fprintf( log_stats_file, "%d\n", now - lasttimecalled );
+ lasttimecalled = now;
+ }
+ }
+ }
+}
+
+
+//============================================================================
+
+/*
+====================
+CL_Init
+====================
+*/
+void CL_Init (void)
+{
+ if (dedicated->value)
+ return; // nothing running on the client
+
+ // all archived variables will now be loaded
+
+ Con_Init ();
+#if defined __linux__ || defined __sgi
+ S_Init ();
+ VID_Init ();
+#else
+ VID_Init ();
+ S_Init (); // sound must be initialized after window is created
+#endif
+
+ V_Init ();
+
+ net_message.data = net_message_buffer;
+ net_message.maxsize = sizeof(net_message_buffer);
+
+ M_Init ();
+
+ SCR_Init ();
+ cls.disable_screen = true; // don't draw yet
+
+ CDAudio_Init ();
+ CL_InitLocal ();
+ IN_Init ();
+
+// Cbuf_AddText ("exec autoexec.cfg\n");
+ FS_ExecAutoexec ();
+ Cbuf_Execute ();
+
+}
+
+
+/*
+===============
+CL_Shutdown
+
+FIXME: this is a callback from Sys_Quit and Com_Error. It would be better
+to run quit through here before the final handoff to the sys code.
+===============
+*/
+void CL_Shutdown(void)
+{
+ static qboolean isdown = false;
+
+ if (isdown)
+ {
+ printf ("recursive shutdown\n");
+ return;
+ }
+ isdown = true;
+
+ CL_WriteConfiguration ();
+
+ CDAudio_Shutdown ();
+ S_Shutdown();
+ IN_Shutdown ();
+ VID_Shutdown();
+}
+
+
diff --git a/client/cl_newfx.c b/client/cl_newfx.c
new file mode 100644
index 0000000..5ad92b6
--- /dev/null
+++ b/client/cl_newfx.c
@@ -0,0 +1,1323 @@
+/*
+Copyright (C) 1997-2001 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.
+
+*/
+// cl_newfx.c -- MORE entity effects parsing and management
+
+#include "client.h"
+
+extern cparticle_t *active_particles, *free_particles;
+extern cparticle_t particles[MAX_PARTICLES];
+extern int cl_numparticles;
+extern cvar_t *vid_ref;
+
+extern void MakeNormalVectors (vec3_t forward, vec3_t right, vec3_t up);
+
+
+/*
+======
+vectoangles2 - this is duplicated in the game DLL, but I need it here.
+======
+*/
+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;
+}
+
+//=============
+//=============
+void CL_Flashlight (int ent, vec3_t pos)
+{
+ cdlight_t *dl;
+
+ dl = CL_AllocDlight (ent);
+ VectorCopy (pos, dl->origin);
+ dl->radius = 400;
+ dl->minlight = 250;
+ dl->die = cl.time + 100;
+ dl->color[0] = 1;
+ dl->color[1] = 1;
+ dl->color[2] = 1;
+}
+
+/*
+======
+CL_ColorFlash - flash of light
+======
+*/
+void CL_ColorFlash (vec3_t pos, int ent, int intensity, float r, float g, float b)
+{
+ cdlight_t *dl;
+
+ if((vidref_val == VIDREF_SOFT) && ((r < 0) || (g<0) || (b<0)))
+ {
+ intensity = -intensity;
+ r = -r;
+ g = -g;
+ b = -b;
+ }
+
+ dl = CL_AllocDlight (ent);
+ VectorCopy (pos, dl->origin);
+ dl->radius = intensity;
+ dl->minlight = 250;
+ dl->die = cl.time + 100;
+ dl->color[0] = r;
+ dl->color[1] = g;
+ dl->color[2] = b;
+}
+
+
+/*
+======
+CL_DebugTrail
+======
+*/
+void CL_DebugTrail (vec3_t start, vec3_t end)
+{
+ vec3_t move;
+ vec3_t vec;
+ float len;
+// int j;
+ cparticle_t *p;
+ float dec;
+ vec3_t right, up;
+// int i;
+// float d, c, s;
+// vec3_t dir;
+
+ VectorCopy (start, move);
+ VectorSubtract (end, start, vec);
+ len = VectorNormalize (vec);
+
+ MakeNormalVectors (vec, right, up);
+
+// VectorScale(vec, RT2_SKIP, vec);
+
+// dec = 1.0;
+// dec = 0.75;
+ dec = 3;
+ VectorScale (vec, dec, vec);
+ VectorCopy (start, move);
+
+ while (len > 0)
+ {
+ len -= dec;
+
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ VectorClear (p->accel);
+ VectorClear (p->vel);
+ p->alpha = 1.0;
+ p->alphavel = -0.1;
+// p->alphavel = 0;
+ p->color = 0x74 + (rand()&7);
+ VectorCopy (move, p->org);
+/*
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + crand()*2;
+ p->vel[j] = crand()*3;
+ p->accel[j] = 0;
+ }
+*/
+ VectorAdd (move, vec, move);
+ }
+
+}
+
+/*
+===============
+CL_SmokeTrail
+===============
+*/
+void CL_SmokeTrail (vec3_t start, vec3_t end, int colorStart, int colorRun, int spacing)
+{
+ vec3_t move;
+ vec3_t vec;
+ float len;
+ int j;
+ cparticle_t *p;
+
+ VectorCopy (start, move);
+ VectorSubtract (end, start, vec);
+ len = VectorNormalize (vec);
+
+ VectorScale (vec, spacing, vec);
+
+ // FIXME: this is a really silly way to have a loop
+ while (len > 0)
+ {
+ len -= spacing;
+
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+ VectorClear (p->accel);
+
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (1+frand()*0.5);
+ p->color = colorStart + (rand() % colorRun);
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + crand()*3;
+ p->accel[j] = 0;
+ }
+ p->vel[2] = 20 + crand()*5;
+
+ VectorAdd (move, vec, move);
+ }
+}
+
+void CL_ForceWall (vec3_t start, vec3_t end, int color)
+{
+ vec3_t move;
+ vec3_t vec;
+ float len;
+ int j;
+ cparticle_t *p;
+
+ VectorCopy (start, move);
+ VectorSubtract (end, start, vec);
+ len = VectorNormalize (vec);
+
+ VectorScale (vec, 4, vec);
+
+ // FIXME: this is a really silly way to have a loop
+ while (len > 0)
+ {
+ len -= 4;
+
+ if (!free_particles)
+ return;
+
+ if (frand() > 0.3)
+ {
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+ VectorClear (p->accel);
+
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (3.0+frand()*0.5);
+ p->color = color;
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + crand()*3;
+ p->accel[j] = 0;
+ }
+ p->vel[0] = 0;
+ p->vel[1] = 0;
+ p->vel[2] = -40 - (crand()*10);
+ }
+
+ VectorAdd (move, vec, move);
+ }
+}
+
+void CL_FlameEffects (centity_t *ent, vec3_t origin)
+{
+ int n, count;
+ int j;
+ cparticle_t *p;
+
+ count = rand() & 0xF;
+
+ for(n=0;n<count;n++)
+ {
+ if (!free_particles)
+ return;
+
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ VectorClear (p->accel);
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (1+frand()*0.2);
+ p->color = 226 + (rand() % 4);
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = origin[j] + crand()*5;
+ p->vel[j] = crand()*5;
+ }
+ p->vel[2] = crand() * -10;
+ p->accel[2] = -PARTICLE_GRAVITY;
+ }
+
+ count = rand() & 0x7;
+
+ for(n=0;n<count;n++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+ VectorClear (p->accel);
+
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (1+frand()*0.5);
+ p->color = 0 + (rand() % 4);
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = origin[j] + crand()*3;
+ }
+ p->vel[2] = 20 + crand()*5;
+ }
+
+}
+
+
+/*
+===============
+CL_GenericParticleEffect
+===============
+*/
+void CL_GenericParticleEffect (vec3_t org, vec3_t dir, int color, int count, int numcolors, int dirspread, float alphavel)
+{
+ int i, j;
+ cparticle_t *p;
+ float d;
+
+ for (i=0 ; i<count ; i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ if (numcolors > 1)
+ p->color = color + (rand() & numcolors);
+ else
+ p->color = color;
+
+ d = rand() & dirspread;
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = org[j] + ((rand()&7)-4) + d*dir[j];
+ p->vel[j] = crand()*20;
+ }
+
+ p->accel[0] = p->accel[1] = 0;
+ p->accel[2] = -PARTICLE_GRAVITY;
+// VectorCopy (accel, p->accel);
+ p->alpha = 1.0;
+
+ p->alphavel = -1.0 / (0.5 + frand()*alphavel);
+// p->alphavel = alphavel;
+ }
+}
+
+/*
+===============
+CL_BubbleTrail2 (lets you control the # of bubbles by setting the distance between the spawns)
+
+===============
+*/
+void CL_BubbleTrail2 (vec3_t start, vec3_t end, int dist)
+{
+ vec3_t move;
+ vec3_t vec;
+ float len;
+ int i, j;
+ cparticle_t *p;
+ float dec;
+
+ VectorCopy (start, move);
+ VectorSubtract (end, start, vec);
+ len = VectorNormalize (vec);
+
+ dec = dist;
+ VectorScale (vec, dec, vec);
+
+ for (i=0 ; i<len ; i+=dec)
+ {
+ if (!free_particles)
+ return;
+
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ VectorClear (p->accel);
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (1+frand()*0.1);
+ p->color = 4 + (rand()&7);
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + crand()*2;
+ p->vel[j] = crand()*10;
+ }
+ p->org[2] -= 4;
+// p->vel[2] += 6;
+ p->vel[2] += 20;
+
+ VectorAdd (move, vec, move);
+ }
+}
+
+//#define CORKSCREW 1
+//#define DOUBLE_SCREW 1
+#define RINGS 1
+//#define SPRAY 1
+
+#ifdef CORKSCREW
+void CL_Heatbeam (vec3_t start, vec3_t end)
+{
+ vec3_t move;
+ vec3_t vec;
+ float len;
+ int j,k;
+ cparticle_t *p;
+ vec3_t right, up;
+ int i;
+ float d, c, s;
+ vec3_t dir;
+ float ltime;
+ float step = 5.0;
+
+ VectorCopy (start, move);
+ VectorSubtract (end, start, vec);
+ len = VectorNormalize (vec);
+
+// MakeNormalVectors (vec, right, up);
+ VectorCopy (cl.v_right, right);
+ VectorCopy (cl.v_up, up);
+ VectorMA (move, -1, right, move);
+ VectorMA (move, -1, up, move);
+
+ VectorScale (vec, step, vec);
+ ltime = (float) cl.time/1000.0;
+
+// for (i=0 ; i<len ; i++)
+ for (i=0 ; i<len ; i+=step)
+ {
+ d = i * 0.1 - fmod(ltime,16.0)*M_PI;
+ c = cos(d)/1.75;
+ s = sin(d)/1.75;
+#ifdef DOUBLE_SCREW
+ for (k=-1; k<2; k+=2)
+ {
+#else
+ k=1;
+#endif
+ if (!free_particles)
+ return;
+
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ VectorClear (p->accel);
+
+ p->alpha = 0.5;
+ // p->alphavel = -1.0 / (1+frand()*0.2);
+ // only last one frame!
+ p->alphavel = INSTANT_PARTICLE;
+ // p->color = 0x74 + (rand()&7);
+// p->color = 223 - (rand()&7);
+ p->color = 223;
+// p->color = 240;
+
+ // trim it so it looks like it's starting at the origin
+ if (i < 10)
+ {
+ VectorScale (right, c*(i/10.0)*k, dir);
+ VectorMA (dir, s*(i/10.0)*k, up, dir);
+ }
+ else
+ {
+ VectorScale (right, c*k, dir);
+ VectorMA (dir, s*k, up, dir);
+ }
+
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + dir[j]*3;
+ // p->vel[j] = dir[j]*6;
+ p->vel[j] = 0;
+ }
+#ifdef DOUBLE_SCREW
+ }
+#endif
+ VectorAdd (move, vec, move);
+ }
+}
+#endif
+#ifdef RINGS
+//void CL_Heatbeam (vec3_t start, vec3_t end)
+void CL_Heatbeam (vec3_t start, vec3_t forward)
+{
+ vec3_t move;
+ vec3_t vec;
+ float len;
+ int j;
+ cparticle_t *p;
+ vec3_t right, up;
+ int i;
+ float c, s;
+ vec3_t dir;
+ float ltime;
+ float step = 32.0, rstep;
+ float start_pt;
+ float rot;
+ float variance;
+ vec3_t end;
+
+ VectorMA (start, 4096, forward, end);
+
+ VectorCopy (start, move);
+ VectorSubtract (end, start, vec);
+ len = VectorNormalize (vec);
+
+ // FIXME - pmm - these might end up using old values?
+// MakeNormalVectors (vec, right, up);
+ VectorCopy (cl.v_right, right);
+ VectorCopy (cl.v_up, up);
+ if (vidref_val == VIDREF_GL)
+ { // GL mode
+ VectorMA (move, -0.5, right, move);
+ VectorMA (move, -0.5, up, move);
+ }
+ // otherwise assume SOFT
+
+ ltime = (float) cl.time/1000.0;
+ start_pt = fmod(ltime*96.0,step);
+ VectorMA (move, start_pt, vec, move);
+
+ VectorScale (vec, step, vec);
+
+// Com_Printf ("%f\n", ltime);
+ rstep = M_PI/10.0;
+ for (i=start_pt ; i<len ; i+=step)
+ {
+ if (i>step*5) // don't bother after the 5th ring
+ break;
+
+ for (rot = 0; rot < M_PI*2; rot += rstep)
+ {
+
+ if (!free_particles)
+ return;
+
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ VectorClear (p->accel);
+// rot+= fmod(ltime, 12.0)*M_PI;
+// c = cos(rot)/2.0;
+// s = sin(rot)/2.0;
+// variance = 0.4 + ((float)rand()/(float)RAND_MAX) *0.2;
+ variance = 0.5;
+ c = cos(rot)*variance;
+ s = sin(rot)*variance;
+
+ // trim it so it looks like it's starting at the origin
+ if (i < 10)
+ {
+ VectorScale (right, c*(i/10.0), dir);
+ VectorMA (dir, s*(i/10.0), up, dir);
+ }
+ else
+ {
+ VectorScale (right, c, dir);
+ VectorMA (dir, s, up, dir);
+ }
+
+ p->alpha = 0.5;
+ // p->alphavel = -1.0 / (1+frand()*0.2);
+ p->alphavel = -1000.0;
+ // p->color = 0x74 + (rand()&7);
+ p->color = 223 - (rand()&7);
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + dir[j]*3;
+ // p->vel[j] = dir[j]*6;
+ p->vel[j] = 0;
+ }
+ }
+ VectorAdd (move, vec, move);
+ }
+}
+#endif
+#ifdef SPRAY
+void CL_Heatbeam (vec3_t start, vec3_t end)
+{
+ vec3_t move;
+ vec3_t vec;
+ float len;
+ int j;
+ cparticle_t *p;
+ vec3_t forward, right, up;
+ int i;
+ float d, c, s;
+ vec3_t dir;
+ float ltime;
+ float step = 32.0, rstep;
+ float start_pt;
+ float rot;
+
+ VectorCopy (start, move);
+ VectorSubtract (end, start, vec);
+ len = VectorNormalize (vec);
+
+// MakeNormalVectors (vec, right, up);
+ VectorCopy (cl.v_forward, forward);
+ VectorCopy (cl.v_right, right);
+ VectorCopy (cl.v_up, up);
+ VectorMA (move, -0.5, right, move);
+ VectorMA (move, -0.5, up, move);
+
+ for (i=0; i<8; i++)
+ {
+ if (!free_particles)
+ return;
+
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ VectorClear (p->accel);
+
+ d = crand()*M_PI;
+ c = cos(d)*30;
+ s = sin(d)*30;
+
+ p->alpha = 1.0;
+ p->alphavel = -5.0 / (1+frand());
+ p->color = 223 - (rand()&7);
+
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j];
+ }
+ VectorScale (vec, 450, p->vel);
+ VectorMA (p->vel, c, right, p->vel);
+ VectorMA (p->vel, s, up, p->vel);
+ }
+/*
+
+ ltime = (float) cl.time/1000.0;
+ start_pt = fmod(ltime*16.0,step);
+ VectorMA (move, start_pt, vec, move);
+
+ VectorScale (vec, step, vec);
+
+// Com_Printf ("%f\n", ltime);
+ rstep = M_PI/12.0;
+ for (i=start_pt ; i<len ; i+=step)
+ {
+ if (i>step*5) // don't bother after the 5th ring
+ break;
+
+ for (rot = 0; rot < M_PI*2; rot += rstep)
+ {
+ if (!free_particles)
+ return;
+
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ VectorClear (p->accel);
+// rot+= fmod(ltime, 12.0)*M_PI;
+// c = cos(rot)/2.0;
+// s = sin(rot)/2.0;
+ c = cos(rot)/1.5;
+ s = sin(rot)/1.5;
+
+ // trim it so it looks like it's starting at the origin
+ if (i < 10)
+ {
+ VectorScale (right, c*(i/10.0), dir);
+ VectorMA (dir, s*(i/10.0), up, dir);
+ }
+ else
+ {
+ VectorScale (right, c, dir);
+ VectorMA (dir, s, up, dir);
+ }
+
+ p->alpha = 0.5;
+ // p->alphavel = -1.0 / (1+frand()*0.2);
+ p->alphavel = -1000.0;
+ // p->color = 0x74 + (rand()&7);
+ p->color = 223 - (rand()&7);
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + dir[j]*3;
+ // p->vel[j] = dir[j]*6;
+ p->vel[j] = 0;
+ }
+ }
+ VectorAdd (move, vec, move);
+ }
+*/
+}
+#endif
+
+/*
+===============
+CL_ParticleSteamEffect
+
+Puffs with velocity along direction, with some randomness thrown in
+===============
+*/
+void CL_ParticleSteamEffect (vec3_t org, vec3_t dir, int color, int count, int magnitude)
+{
+ int i, j;
+ cparticle_t *p;
+ float d;
+ vec3_t r, u;
+
+// vectoangles2 (dir, angle_dir);
+// AngleVectors (angle_dir, f, r, u);
+
+ MakeNormalVectors (dir, r, u);
+
+ for (i=0 ; i<count ; i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ p->color = color + (rand()&7);
+
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = org[j] + magnitude*0.1*crand();
+// p->vel[j] = dir[j]*magnitude;
+ }
+ VectorScale (dir, magnitude, p->vel);
+ d = crand()*magnitude/3;
+ VectorMA (p->vel, d, r, p->vel);
+ d = crand()*magnitude/3;
+ VectorMA (p->vel, d, u, p->vel);
+
+ p->accel[0] = p->accel[1] = 0;
+ p->accel[2] = -PARTICLE_GRAVITY/2;
+ p->alpha = 1.0;
+
+ p->alphavel = -1.0 / (0.5 + frand()*0.3);
+ }
+}
+
+void CL_ParticleSteamEffect2 (cl_sustain_t *self)
+//vec3_t org, vec3_t dir, int color, int count, int magnitude)
+{
+ int i, j;
+ cparticle_t *p;
+ float d;
+ vec3_t r, u;
+ vec3_t dir;
+
+// vectoangles2 (dir, angle_dir);
+// AngleVectors (angle_dir, f, r, u);
+
+ VectorCopy (self->dir, dir);
+ MakeNormalVectors (dir, r, u);
+
+ for (i=0 ; i<self->count ; i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ p->color = self->color + (rand()&7);
+
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = self->org[j] + self->magnitude*0.1*crand();
+// p->vel[j] = dir[j]*magnitude;
+ }
+ VectorScale (dir, self->magnitude, p->vel);
+ d = crand()*self->magnitude/3;
+ VectorMA (p->vel, d, r, p->vel);
+ d = crand()*self->magnitude/3;
+ VectorMA (p->vel, d, u, p->vel);
+
+ p->accel[0] = p->accel[1] = 0;
+ p->accel[2] = -PARTICLE_GRAVITY/2;
+ p->alpha = 1.0;
+
+ p->alphavel = -1.0 / (0.5 + frand()*0.3);
+ }
+ self->nextthink += self->thinkinterval;
+}
+
+/*
+===============
+CL_TrackerTrail
+===============
+*/
+void CL_TrackerTrail (vec3_t start, vec3_t end, int particleColor)
+{
+ vec3_t move;
+ vec3_t vec;
+ vec3_t forward,right,up,angle_dir;
+ float len;
+ int j;
+ cparticle_t *p;
+ int dec;
+ float dist;
+
+ VectorCopy (start, move);
+ VectorSubtract (end, start, vec);
+ len = VectorNormalize (vec);
+
+ VectorCopy(vec, forward);
+ vectoangles2 (forward, angle_dir);
+ AngleVectors (angle_dir, forward, right, up);
+
+ dec = 3;
+ VectorScale (vec, 3, vec);
+
+ // FIXME: this is a really silly way to have a loop
+ while (len > 0)
+ {
+ len -= dec;
+
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+ VectorClear (p->accel);
+
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = -2.0;
+ p->color = particleColor;
+ dist = DotProduct(move, forward);
+ VectorMA(move, 8 * cos(dist), up, p->org);
+ for (j=0 ; j<3 ; j++)
+ {
+// p->org[j] = move[j] + crand();
+ p->vel[j] = 0;
+ p->accel[j] = 0;
+ }
+ p->vel[2] = 5;
+
+ VectorAdd (move, vec, move);
+ }
+}
+
+void CL_Tracker_Shell(vec3_t origin)
+{
+ vec3_t dir;
+ int i;
+ cparticle_t *p;
+
+ for(i=0;i<300;i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+ VectorClear (p->accel);
+
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = INSTANT_PARTICLE;
+ p->color = 0;
+
+ dir[0] = crand();
+ dir[1] = crand();
+ dir[2] = crand();
+ VectorNormalize(dir);
+
+ VectorMA(origin, 40, dir, p->org);
+ }
+}
+
+void CL_MonsterPlasma_Shell(vec3_t origin)
+{
+ vec3_t dir;
+ int i;
+ cparticle_t *p;
+
+ for(i=0;i<40;i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+ VectorClear (p->accel);
+
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = INSTANT_PARTICLE;
+ p->color = 0xe0;
+
+ dir[0] = crand();
+ dir[1] = crand();
+ dir[2] = crand();
+ VectorNormalize(dir);
+
+ VectorMA(origin, 10, dir, p->org);
+// VectorMA(origin, 10*(((rand () & 0x7fff) / ((float)0x7fff))), dir, p->org);
+ }
+}
+
+void CL_Widowbeamout (cl_sustain_t *self)
+{
+ vec3_t dir;
+ int i;
+ cparticle_t *p;
+ static int colortable[4] = {2*8,13*8,21*8,18*8};
+ float ratio;
+
+ ratio = 1.0 - (((float)self->endtime - (float)cl.time)/2100.0);
+
+ for(i=0;i<300;i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+ VectorClear (p->accel);
+
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = INSTANT_PARTICLE;
+ p->color = colortable[rand()&3];
+
+ dir[0] = crand();
+ dir[1] = crand();
+ dir[2] = crand();
+ VectorNormalize(dir);
+
+ VectorMA(self->org, (45.0 * ratio), dir, p->org);
+// VectorMA(origin, 10*(((rand () & 0x7fff) / ((float)0x7fff))), dir, p->org);
+ }
+}
+
+void CL_Nukeblast (cl_sustain_t *self)
+{
+ vec3_t dir;
+ int i;
+ cparticle_t *p;
+ static int colortable[4] = {110, 112, 114, 116};
+ float ratio;
+
+ ratio = 1.0 - (((float)self->endtime - (float)cl.time)/1000.0);
+
+ for(i=0;i<700;i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+ VectorClear (p->accel);
+
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = INSTANT_PARTICLE;
+ p->color = colortable[rand()&3];
+
+ dir[0] = crand();
+ dir[1] = crand();
+ dir[2] = crand();
+ VectorNormalize(dir);
+
+ VectorMA(self->org, (200.0 * ratio), dir, p->org);
+// VectorMA(origin, 10*(((rand () & 0x7fff) / ((float)0x7fff))), dir, p->org);
+ }
+}
+
+void CL_WidowSplash (vec3_t org)
+{
+ static int colortable[4] = {2*8,13*8,21*8,18*8};
+ int i;
+ cparticle_t *p;
+ vec3_t dir;
+
+ for (i=0 ; i<256 ; i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ p->color = colortable[rand()&3];
+
+ dir[0] = crand();
+ dir[1] = crand();
+ dir[2] = crand();
+ VectorNormalize(dir);
+ VectorMA(org, 45.0, dir, p->org);
+ VectorMA(vec3_origin, 40.0, dir, p->vel);
+
+ p->accel[0] = p->accel[1] = 0;
+ p->alpha = 1.0;
+
+ p->alphavel = -0.8 / (0.5 + frand()*0.3);
+ }
+
+}
+
+void CL_Tracker_Explode(vec3_t origin)
+{
+ vec3_t dir, backdir;
+ int i;
+ cparticle_t *p;
+
+ for(i=0;i<300;i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+ VectorClear (p->accel);
+
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = -1.0;
+ p->color = 0;
+
+ dir[0] = crand();
+ dir[1] = crand();
+ dir[2] = crand();
+ VectorNormalize(dir);
+ VectorScale(dir, -1, backdir);
+
+ VectorMA(origin, 64, dir, p->org);
+ VectorScale(backdir, 64, p->vel);
+ }
+
+}
+
+/*
+===============
+CL_TagTrail
+
+===============
+*/
+void CL_TagTrail (vec3_t start, vec3_t end, float color)
+{
+ vec3_t move;
+ vec3_t vec;
+ float len;
+ int j;
+ cparticle_t *p;
+ int dec;
+
+ VectorCopy (start, move);
+ VectorSubtract (end, start, vec);
+ len = VectorNormalize (vec);
+
+ dec = 5;
+ VectorScale (vec, 5, vec);
+
+ while (len >= 0)
+ {
+ len -= dec;
+
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+ VectorClear (p->accel);
+
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (0.8+frand()*0.2);
+ p->color = color;
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + crand()*16;
+ p->vel[j] = crand()*5;
+ p->accel[j] = 0;
+ }
+
+ VectorAdd (move, vec, move);
+ }
+}
+
+/*
+===============
+CL_ColorExplosionParticles
+===============
+*/
+void CL_ColorExplosionParticles (vec3_t org, int color, int run)
+{
+ int i, j;
+ cparticle_t *p;
+
+ for (i=0 ; i<128 ; i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ p->color = color + (rand() % run);
+
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = org[j] + ((rand()%32)-16);
+ p->vel[j] = (rand()%256)-128;
+ }
+
+ p->accel[0] = p->accel[1] = 0;
+ p->accel[2] = -PARTICLE_GRAVITY;
+ p->alpha = 1.0;
+
+ p->alphavel = -0.4 / (0.6 + frand()*0.2);
+ }
+}
+
+/*
+===============
+CL_ParticleSmokeEffect - like the steam effect, but unaffected by gravity
+===============
+*/
+void CL_ParticleSmokeEffect (vec3_t org, vec3_t dir, int color, int count, int magnitude)
+{
+ int i, j;
+ cparticle_t *p;
+ float d;
+ vec3_t r, u;
+
+ MakeNormalVectors (dir, r, u);
+
+ for (i=0 ; i<count ; i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ p->color = color + (rand()&7);
+
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = org[j] + magnitude*0.1*crand();
+// p->vel[j] = dir[j]*magnitude;
+ }
+ VectorScale (dir, magnitude, p->vel);
+ d = crand()*magnitude/3;
+ VectorMA (p->vel, d, r, p->vel);
+ d = crand()*magnitude/3;
+ VectorMA (p->vel, d, u, p->vel);
+
+ p->accel[0] = p->accel[1] = p->accel[2] = 0;
+ p->alpha = 1.0;
+
+ p->alphavel = -1.0 / (0.5 + frand()*0.3);
+ }
+}
+
+/*
+===============
+CL_BlasterParticles2
+
+Wall impact puffs (Green)
+===============
+*/
+void CL_BlasterParticles2 (vec3_t org, vec3_t dir, unsigned int color)
+{
+ int i, j;
+ cparticle_t *p;
+ float d;
+ int count;
+
+ count = 40;
+ for (i=0 ; i<count ; i++)
+ {
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+
+ p->time = cl.time;
+ p->color = color + (rand()&7);
+
+ d = rand()&15;
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = org[j] + ((rand()&7)-4) + d*dir[j];
+ p->vel[j] = dir[j] * 30 + crand()*40;
+ }
+
+ p->accel[0] = p->accel[1] = 0;
+ p->accel[2] = -PARTICLE_GRAVITY;
+ p->alpha = 1.0;
+
+ p->alphavel = -1.0 / (0.5 + frand()*0.3);
+ }
+}
+
+/*
+===============
+CL_BlasterTrail2
+
+Green!
+===============
+*/
+void CL_BlasterTrail2 (vec3_t start, vec3_t end)
+{
+ vec3_t move;
+ vec3_t vec;
+ float len;
+ int j;
+ cparticle_t *p;
+ int dec;
+
+ VectorCopy (start, move);
+ VectorSubtract (end, start, vec);
+ len = VectorNormalize (vec);
+
+ dec = 5;
+ VectorScale (vec, 5, vec);
+
+ // FIXME: this is a really silly way to have a loop
+ while (len > 0)
+ {
+ len -= dec;
+
+ if (!free_particles)
+ return;
+ p = free_particles;
+ free_particles = p->next;
+ p->next = active_particles;
+ active_particles = p;
+ VectorClear (p->accel);
+
+ p->time = cl.time;
+
+ p->alpha = 1.0;
+ p->alphavel = -1.0 / (0.3+frand()*0.2);
+ p->color = 0xd0;
+ for (j=0 ; j<3 ; j++)
+ {
+ p->org[j] = move[j] + crand();
+ p->vel[j] = crand()*5;
+ p->accel[j] = 0;
+ }
+
+ VectorAdd (move, vec, move);
+ }
+}
diff --git a/client/cl_parse.c b/client/cl_parse.c
new file mode 100644
index 0000000..5468c2e
--- /dev/null
+++ b/client/cl_parse.c
@@ -0,0 +1,806 @@
+/*
+Copyright (C) 1997-2001 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.
+
+*/
+// cl_parse.c -- parse a message received from the server
+
+#include "client.h"
+
+char *svc_strings[256] =
+{
+ "svc_bad",
+
+ "svc_muzzleflash",
+ "svc_muzzlflash2",
+ "svc_temp_entity",
+ "svc_layout",
+ "svc_inventory",
+
+ "svc_nop",
+ "svc_disconnect",
+ "svc_reconnect",
+ "svc_sound",
+ "svc_print",
+ "svc_stufftext",
+ "svc_serverdata",
+ "svc_configstring",
+ "svc_spawnbaseline",
+ "svc_centerprint",
+ "svc_download",
+ "svc_playerinfo",
+ "svc_packetentities",
+ "svc_deltapacketentities",
+ "svc_frame"
+};
+
+//=============================================================================
+
+void CL_DownloadFileName(char *dest, int destlen, char *fn)
+{
+ if (strncmp(fn, "players", 7) == 0)
+ Com_sprintf (dest, destlen, "%s/%s", BASEDIRNAME, fn);
+ else
+ Com_sprintf (dest, destlen, "%s/%s", FS_Gamedir(), fn);
+}
+
+/*
+===============
+CL_CheckOrDownloadFile
+
+Returns true if the file exists, otherwise it attempts
+to start a download from the server.
+===============
+*/
+qboolean CL_CheckOrDownloadFile (char *filename)
+{
+ FILE *fp;
+ char name[MAX_OSPATH];
+
+ if (strstr (filename, ".."))
+ {
+ Com_Printf ("Refusing to download a path with ..\n");
+ return true;
+ }
+
+ if (FS_LoadFile (filename, NULL) != -1)
+ { // it exists, no need to download
+ return true;
+ }
+
+ strcpy (cls.downloadname, filename);
+
+ // download to a temp name, and only rename
+ // to the real name when done, so if interrupted
+ // a runt file wont be left
+ COM_StripExtension (cls.downloadname, cls.downloadtempname);
+ strcat (cls.downloadtempname, ".tmp");
+
+//ZOID
+ // check to see if we already have a tmp for this file, if so, try to resume
+ // open the file if not opened yet
+ CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);
+
+// FS_CreatePath (name);
+
+ fp = fopen (name, "r+b");
+ if (fp) { // it exists
+ int len;
+ fseek(fp, 0, SEEK_END);
+ len = ftell(fp);
+
+ cls.download = fp;
+
+ // give the server an offset to start the download
+ Com_Printf ("Resuming %s\n", cls.downloadname);
+ MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
+ MSG_WriteString (&cls.netchan.message,
+ va("download %s %i", cls.downloadname, len));
+ } else {
+ Com_Printf ("Downloading %s\n", cls.downloadname);
+ MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
+ MSG_WriteString (&cls.netchan.message,
+ va("download %s", cls.downloadname));
+ }
+
+ cls.downloadnumber++;
+
+ return false;
+}
+
+/*
+===============
+CL_Download_f
+
+Request a download from the server
+===============
+*/
+void CL_Download_f (void)
+{
+ char filename[MAX_OSPATH];
+
+ if (Cmd_Argc() != 2) {
+ Com_Printf("Usage: download <filename>\n");
+ return;
+ }
+
+ Com_sprintf(filename, sizeof(filename), "%s", Cmd_Argv(1));
+
+ if (strstr (filename, ".."))
+ {
+ Com_Printf ("Refusing to download a path with ..\n");
+ return;
+ }
+
+ if (FS_LoadFile (filename, NULL) != -1)
+ { // it exists, no need to download
+ Com_Printf("File already exists.\n");
+ return;
+ }
+
+ strcpy (cls.downloadname, filename);
+ Com_Printf ("Downloading %s\n", cls.downloadname);
+
+ // download to a temp name, and only rename
+ // to the real name when done, so if interrupted
+ // a runt file wont be left
+ COM_StripExtension (cls.downloadname, cls.downloadtempname);
+ strcat (cls.downloadtempname, ".tmp");
+
+ MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
+ MSG_WriteString (&cls.netchan.message,
+ va("download %s", cls.downloadname));
+
+ cls.downloadnumber++;
+}
+
+/*
+======================
+CL_RegisterSounds
+======================
+*/
+void CL_RegisterSounds (void)
+{
+ int i;
+
+ S_BeginRegistration ();
+ CL_RegisterTEntSounds ();
+ for (i=1 ; i<MAX_SOUNDS ; i++)
+ {
+ if (!cl.configstrings[CS_SOUNDS+i][0])
+ break;
+ cl.sound_precache[i] = S_RegisterSound (cl.configstrings[CS_SOUNDS+i]);
+ Sys_SendKeyEvents (); // pump message loop
+ }
+ S_EndRegistration ();
+}
+
+
+/*
+=====================
+CL_ParseDownload
+
+A download message has been received from the server
+=====================
+*/
+void CL_ParseDownload (void)
+{
+ int size, percent;
+ char name[MAX_OSPATH];
+ int r;
+
+ // read the data
+ size = MSG_ReadShort (&net_message);
+ percent = MSG_ReadByte (&net_message);
+ if (size == -1)
+ {
+ Com_Printf ("Server does not have this file.\n");
+ if (cls.download)
+ {
+ // if here, we tried to resume a file but the server said no
+ fclose (cls.download);
+ cls.download = NULL;
+ }
+ CL_RequestNextDownload ();
+ return;
+ }
+
+ // open the file if not opened yet
+ if (!cls.download)
+ {
+ CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);
+
+ FS_CreatePath (name);
+
+ cls.download = fopen (name, "wb");
+ if (!cls.download)
+ {
+ net_message.readcount += size;
+ Com_Printf ("Failed to open %s\n", cls.downloadtempname);
+ CL_RequestNextDownload ();
+ return;
+ }
+ }
+
+ fwrite (net_message.data + net_message.readcount, 1, size, cls.download);
+ net_message.readcount += size;
+
+ if (percent != 100)
+ {
+ // request next block
+// change display routines by zoid
+#if 0
+ Com_Printf (".");
+ if (10*(percent/10) != cls.downloadpercent)
+ {
+ cls.downloadpercent = 10*(percent/10);
+ Com_Printf ("%i%%", cls.downloadpercent);
+ }
+#endif
+ cls.downloadpercent = percent;
+
+ MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
+ SZ_Print (&cls.netchan.message, "nextdl");
+ }
+ else
+ {
+ char oldn[MAX_OSPATH];
+ char newn[MAX_OSPATH];
+
+// Com_Printf ("100%%\n");
+
+ fclose (cls.download);
+
+ // rename the temp file to it's final name
+ CL_DownloadFileName(oldn, sizeof(oldn), cls.downloadtempname);
+ CL_DownloadFileName(newn, sizeof(newn), cls.downloadname);
+ r = rename (oldn, newn);
+ if (r)
+ Com_Printf ("failed to rename.\n");
+
+ cls.download = NULL;
+ cls.downloadpercent = 0;
+
+ // get another file if needed
+
+ CL_RequestNextDownload ();
+ }
+}
+
+
+/*
+=====================================================================
+
+ SERVER CONNECTING MESSAGES
+
+=====================================================================
+*/
+
+/*
+==================
+CL_ParseServerData
+==================
+*/
+void CL_ParseServerData (void)
+{
+ extern cvar_t *fs_gamedirvar;
+ char *str;
+ int i;
+
+ Com_DPrintf ("Serverdata packet received.\n");
+//
+// wipe the client_state_t struct
+//
+ CL_ClearState ();
+ cls.state = ca_connected;
+
+// parse protocol version number
+ i = MSG_ReadLong (&net_message);
+ cls.serverProtocol = i;
+
+ // BIG HACK to let demos from release work with the 3.0x patch!!!
+ if (Com_ServerState() && PROTOCOL_VERSION == 34)
+ {
+ }
+ else if (i != PROTOCOL_VERSION)
+ Com_Error (ERR_DROP,"Server returned version %i, not %i", i, PROTOCOL_VERSION);
+
+ cl.servercount = MSG_ReadLong (&net_message);
+ cl.attractloop = MSG_ReadByte (&net_message);
+
+ // game directory
+ str = MSG_ReadString (&net_message);
+ strncpy (cl.gamedir, str, sizeof(cl.gamedir)-1);
+
+ // set gamedir
+ if ((*str && (!fs_gamedirvar->string || !*fs_gamedirvar->string || strcmp(fs_gamedirvar->string, str))) || (!*str && (fs_gamedirvar->string || *fs_gamedirvar->string)))
+ Cvar_Set("game", str);
+
+ // parse player entity number
+ cl.playernum = MSG_ReadShort (&net_message);
+
+ // get the full level name
+ str = MSG_ReadString (&net_message);
+
+ if (cl.playernum == -1)
+ { // playing a cinematic or showing a pic, not a level
+ SCR_PlayCinematic (str);
+ }
+ else
+ {
+ // seperate the printfs so the server message can have a color
+ Com_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
+ Com_Printf ("%c%s\n", 2, str);
+
+ // need to prep refresh at next oportunity
+ cl.refresh_prepped = false;
+ }
+}
+
+/*
+==================
+CL_ParseBaseline
+==================
+*/
+void CL_ParseBaseline (void)
+{
+ entity_state_t *es;
+ int bits;
+ int newnum;
+ entity_state_t nullstate;
+
+ memset (&nullstate, 0, sizeof(nullstate));
+
+ newnum = CL_ParseEntityBits (&bits);
+ es = &cl_entities[newnum].baseline;
+ CL_ParseDelta (&nullstate, es, newnum, bits);
+}
+
+
+/*
+================
+CL_LoadClientinfo
+
+================
+*/
+void CL_LoadClientinfo (clientinfo_t *ci, char *s)
+{
+ int i;
+ char *t;
+ char model_name[MAX_QPATH];
+ char skin_name[MAX_QPATH];
+ char model_filename[MAX_QPATH];
+ char skin_filename[MAX_QPATH];
+ char weapon_filename[MAX_QPATH];
+
+ strncpy(ci->cinfo, s, sizeof(ci->cinfo));
+ ci->cinfo[sizeof(ci->cinfo)-1] = 0;
+
+ // isolate the player's name
+ strncpy(ci->name, s, sizeof(ci->name));
+ ci->name[sizeof(ci->name)-1] = 0;
+ t = strstr (s, "\\");
+ if (t)
+ {
+ ci->name[t-s] = 0;
+ s = t+1;
+ }
+
+ if (cl_noskins->value || *s == 0)
+ {
+ Com_sprintf (model_filename, sizeof(model_filename), "players/male/tris.md2");
+ Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/male/weapon.md2");
+ Com_sprintf (skin_filename, sizeof(skin_filename), "players/male/grunt.pcx");
+ Com_sprintf (ci->iconname, sizeof(ci->iconname), "/players/male/grunt_i.pcx");
+ ci->model = re.RegisterModel (model_filename);
+ memset(ci->weaponmodel, 0, sizeof(ci->weaponmodel));
+ ci->weaponmodel[0] = re.RegisterModel (weapon_filename);
+ ci->skin = re.RegisterSkin (skin_filename);
+ ci->icon = re.RegisterPic (ci->iconname);
+ }
+ else
+ {
+ // isolate the model name
+ strcpy (model_name, s);
+ t = strstr(model_name, "/");
+ if (!t)
+ t = strstr(model_name, "\\");
+ if (!t)
+ t = model_name;
+ *t = 0;
+
+ // isolate the skin name
+ strcpy (skin_name, s + strlen(model_name) + 1);
+
+ // model file
+ Com_sprintf (model_filename, sizeof(model_filename), "players/%s/tris.md2", model_name);
+ ci->model = re.RegisterModel (model_filename);
+ if (!ci->model)
+ {
+ strcpy(model_name, "male");
+ Com_sprintf (model_filename, sizeof(model_filename), "players/male/tris.md2");
+ ci->model = re.RegisterModel (model_filename);
+ }
+
+ // skin file
+ Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name);
+ ci->skin = re.RegisterSkin (skin_filename);
+
+ // if we don't have the skin and the model wasn't male,
+ // see if the male has it (this is for CTF's skins)
+ if (!ci->skin && Q_stricmp(model_name, "male"))
+ {
+ // change model to male
+ strcpy(model_name, "male");
+ Com_sprintf (model_filename, sizeof(model_filename), "players/male/tris.md2");
+ ci->model = re.RegisterModel (model_filename);
+
+ // see if the skin exists for the male model
+ Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name);
+ ci->skin = re.RegisterSkin (skin_filename);
+ }
+
+ // if we still don't have a skin, it means that the male model didn't have
+ // it, so default to grunt
+ if (!ci->skin) {
+ // see if the skin exists for the male model
+ Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/grunt.pcx", model_name, skin_name);
+ ci->skin = re.RegisterSkin (skin_filename);
+ }
+
+ // weapon file
+ for (i = 0; i < num_cl_weaponmodels; i++) {
+ Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/%s/%s", model_name, cl_weaponmodels[i]);
+ ci->weaponmodel[i] = re.RegisterModel(weapon_filename);
+ if (!ci->weaponmodel[i] && strcmp(model_name, "cyborg") == 0) {
+ // try male
+ Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/male/%s", cl_weaponmodels[i]);
+ ci->weaponmodel[i] = re.RegisterModel(weapon_filename);
+ }
+ if (!cl_vwep->value)
+ break; // only one when vwep is off
+ }
+
+ // icon file
+ Com_sprintf (ci->iconname, sizeof(ci->iconname), "/players/%s/%s_i.pcx", model_name, skin_name);
+ ci->icon = re.RegisterPic (ci->iconname);
+ }
+
+ // must have loaded all data types to be valud
+ if (!ci->skin || !ci->icon || !ci->model || !ci->weaponmodel[0])
+ {
+ ci->skin = NULL;
+ ci->icon = NULL;
+ ci->model = NULL;
+ ci->weaponmodel[0] = NULL;
+ return;
+ }
+}
+
+/*
+================
+CL_ParseClientinfo
+
+Load the skin, icon, and model for a client
+================
+*/
+void CL_ParseClientinfo (int player)
+{
+ char *s;
+ clientinfo_t *ci;
+
+ s = cl.configstrings[player+CS_PLAYERSKINS];
+
+ ci = &cl.clientinfo[player];
+
+ CL_LoadClientinfo (ci, s);
+}
+
+
+/*
+================
+CL_ParseConfigString
+================
+*/
+void CL_ParseConfigString (void)
+{
+ int i;
+ char *s;
+
+ i = MSG_ReadShort (&net_message);
+ if (i < 0 || i >= MAX_CONFIGSTRINGS)
+ Com_Error (ERR_DROP, "configstring > MAX_CONFIGSTRINGS");
+ s = MSG_ReadString(&net_message);
+ strcpy (cl.configstrings[i], s);
+
+ // do something apropriate
+
+ if (i >= CS_LIGHTS && i < CS_LIGHTS+MAX_LIGHTSTYLES)
+ CL_SetLightstyle (i - CS_LIGHTS);
+ else if (i == CS_CDTRACK)
+ {
+ if (cl.refresh_prepped)
+ CDAudio_Play (atoi(cl.configstrings[CS_CDTRACK]), true);
+ }
+ else if (i >= CS_MODELS && i < CS_MODELS+MAX_MODELS)
+ {
+ if (cl.refresh_prepped)
+ {
+ cl.model_draw[i-CS_MODELS] = re.RegisterModel (cl.configstrings[i]);
+ if (cl.configstrings[i][0] == '*')
+ cl.model_clip[i-CS_MODELS] = CM_InlineModel (cl.configstrings[i]);
+ else
+ cl.model_clip[i-CS_MODELS] = NULL;
+ }
+ }
+ else if (i >= CS_SOUNDS && i < CS_SOUNDS+MAX_MODELS)
+ {
+ if (cl.refresh_prepped)
+ cl.sound_precache[i-CS_SOUNDS] = S_RegisterSound (cl.configstrings[i]);
+ }
+ else if (i >= CS_IMAGES && i < CS_IMAGES+MAX_MODELS)
+ {
+ if (cl.refresh_prepped)
+ cl.image_precache[i-CS_IMAGES] = re.RegisterPic (cl.configstrings[i]);
+ }
+ else if (i >= CS_PLAYERSKINS && i < CS_PLAYERSKINS+MAX_CLIENTS)
+ {
+ if (cl.refresh_prepped)
+ CL_ParseClientinfo (i-CS_PLAYERSKINS);
+ }
+}
+
+
+/*
+=====================================================================
+
+ACTION MESSAGES
+
+=====================================================================
+*/
+
+/*
+==================
+CL_ParseStartSoundPacket
+==================
+*/
+void CL_ParseStartSoundPacket(void)
+{
+ vec3_t pos_v;
+ float *pos;
+ int channel, ent;
+ int sound_num;
+ float volume;
+ float attenuation;
+ int flags;
+ float ofs;
+
+ flags = MSG_ReadByte (&net_message);
+ sound_num = MSG_ReadByte (&net_message);
+
+ if (flags & SND_VOLUME)
+ volume = MSG_ReadByte (&net_message) / 255.0;
+ else
+ volume = DEFAULT_SOUND_PACKET_VOLUME;
+
+ if (flags & SND_ATTENUATION)
+ attenuation = MSG_ReadByte (&net_message) / 64.0;
+ else
+ attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
+
+ if (flags & SND_OFFSET)
+ ofs = MSG_ReadByte (&net_message) / 1000.0;
+ else
+ ofs = 0;
+
+ if (flags & SND_ENT)
+ { // entity reletive
+ channel = MSG_ReadShort(&net_message);
+ ent = channel>>3;
+ if (ent > MAX_EDICTS)
+ Com_Error (ERR_DROP,"CL_ParseStartSoundPacket: ent = %i", ent);
+
+ channel &= 7;
+ }
+ else
+ {
+ ent = 0;
+ channel = 0;
+ }
+
+ if (flags & SND_POS)
+ { // positioned in space
+ MSG_ReadPos (&net_message, pos_v);
+
+ pos = pos_v;
+ }
+ else // use entity number
+ pos = NULL;
+
+ if (!cl.sound_precache[sound_num])
+ return;
+
+ S_StartSound (pos, ent, channel, cl.sound_precache[sound_num], volume, attenuation, ofs);
+}
+
+
+void SHOWNET(char *s)
+{
+ if (cl_shownet->value>=2)
+ Com_Printf ("%3i:%s\n", net_message.readcount-1, s);
+}
+
+/*
+=====================
+CL_ParseServerMessage
+=====================
+*/
+void CL_ParseServerMessage (void)
+{
+ int cmd;
+ char *s;
+ int i;
+
+//
+// if recording demos, copy the message out
+//
+ if (cl_shownet->value == 1)
+ Com_Printf ("%i ",net_message.cursize);
+ else if (cl_shownet->value >= 2)
+ Com_Printf ("------------------\n");
+
+
+//
+// parse the message
+//
+ while (1)
+ {
+ if (net_message.readcount > net_message.cursize)
+ {
+ Com_Error (ERR_DROP,"CL_ParseServerMessage: Bad server message");
+ break;
+ }
+
+ cmd = MSG_ReadByte (&net_message);
+
+ if (cmd == -1)
+ {
+ SHOWNET("END OF MESSAGE");
+ break;
+ }
+
+ if (cl_shownet->value>=2)
+ {
+ if (!svc_strings[cmd])
+ Com_Printf ("%3i:BAD CMD %i\n", net_message.readcount-1,cmd);
+ else
+ SHOWNET(svc_strings[cmd]);
+ }
+
+ // other commands
+ switch (cmd)
+ {
+ default:
+ Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n");
+ break;
+
+ case svc_nop:
+// Com_Printf ("svc_nop\n");
+ break;
+
+ case svc_disconnect:
+ Com_Error (ERR_DISCONNECT,"Server disconnected\n");
+ break;
+
+ case svc_reconnect:
+ Com_Printf ("Server disconnected, reconnecting\n");
+ if (cls.download) {
+ //ZOID, close download
+ fclose (cls.download);
+ cls.download = NULL;
+ }
+ cls.state = ca_connecting;
+ cls.connect_time = -99999; // CL_CheckForResend() will fire immediately
+ break;
+
+ case svc_print:
+ i = MSG_ReadByte (&net_message);
+ if (i == PRINT_CHAT)
+ {
+ S_StartLocalSound ("misc/talk.wav");
+ con.ormask = 128;
+ }
+ Com_Printf ("%s", MSG_ReadString (&net_message));
+ con.ormask = 0;
+ break;
+
+ case svc_centerprint:
+ SCR_CenterPrint (MSG_ReadString (&net_message));
+ break;
+
+ case svc_stufftext:
+ s = MSG_ReadString (&net_message);
+ Com_DPrintf ("stufftext: %s\n", s);
+ Cbuf_AddText (s);
+ break;
+
+ case svc_serverdata:
+ Cbuf_Execute (); // make sure any stuffed commands are done
+ CL_ParseServerData ();
+ break;
+
+ case svc_configstring:
+ CL_ParseConfigString ();
+ break;
+
+ case svc_sound:
+ CL_ParseStartSoundPacket();
+ break;
+
+ case svc_spawnbaseline:
+ CL_ParseBaseline ();
+ break;
+
+ case svc_temp_entity:
+ CL_ParseTEnt ();
+ break;
+
+ case svc_muzzleflash:
+ CL_ParseMuzzleFlash ();
+ break;
+
+ case svc_muzzleflash2:
+ CL_ParseMuzzleFlash2 ();
+ break;
+
+ case svc_download:
+ CL_ParseDownload ();
+ break;
+
+ case svc_frame:
+ CL_ParseFrame ();
+ break;
+
+ case svc_inventory:
+ CL_ParseInventory ();
+ break;
+
+ case svc_layout:
+ s = MSG_ReadString (&net_message);
+ strncpy (cl.layout, s, sizeof(cl.layout)-1);
+ break;
+
+ case svc_playerinfo:
+ case svc_packetentities:
+ case svc_deltapacketentities:
+ Com_Error (ERR_DROP, "Out of place frame data");
+ break;
+ }
+ }
+
+ CL_AddNetgraph ();
+
+ //
+ // we don't know if it is ok to save a demo message until
+ // after we have parsed the frame
+ //
+ if (cls.demorecording && !cls.demowaiting)
+ CL_WriteDemoMessage ();
+
+}
+
+
diff --git a/client/cl_pred.c b/client/cl_pred.c
new file mode 100644
index 0000000..638642c
--- /dev/null
+++ b/client/cl_pred.c
@@ -0,0 +1,278 @@
+/*
+Copyright (C) 1997-2001 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 "client.h"
+
+
+/*
+===================
+CL_CheckPredictionError
+===================
+*/
+void CL_CheckPredictionError (void)
+{
+ int frame;
+ int delta[3];
+ int i;
+ int len;
+
+ if (!cl_predict->value || (cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION))
+ return;
+
+ // calculate the last usercmd_t we sent that the server has processed
+ frame = cls.netchan.incoming_acknowledged;
+ frame &= (CMD_BACKUP-1);
+
+ // compare what the server returned with what we had predicted it to be
+ VectorSubtract (cl.frame.playerstate.pmove.origin, cl.predicted_origins[frame], delta);
+
+ // save the prediction error for interpolation
+ len = abs(delta[0]) + abs(delta[1]) + abs(delta[2]);
+ if (len > 640) // 80 world units
+ { // a teleport or something
+ VectorClear (cl.prediction_error);
+ }
+ else
+ {
+ if (cl_showmiss->value && (delta[0] || delta[1] || delta[2]) )
+ Com_Printf ("prediction miss on %i: %i\n", cl.frame.serverframe,
+ delta[0] + delta[1] + delta[2]);
+
+ VectorCopy (cl.frame.playerstate.pmove.origin, cl.predicted_origins[frame]);
+
+ // save for error itnerpolation
+ for (i=0 ; i<3 ; i++)
+ cl.prediction_error[i] = delta[i]*0.125;
+ }
+}
+
+
+/*
+====================
+CL_ClipMoveToEntities
+
+====================
+*/
+void CL_ClipMoveToEntities ( vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, trace_t *tr )
+{
+ int i, x, zd, zu;
+ trace_t trace;
+ int headnode;
+ float *angles;
+ entity_state_t *ent;
+ int num;
+ cmodel_t *cmodel;
+ vec3_t bmins, bmaxs;
+
+ for (i=0 ; i<cl.frame.num_entities ; i++)
+ {
+ num = (cl.frame.parse_entities + i)&(MAX_PARSE_ENTITIES-1);
+ ent = &cl_parse_entities[num];
+
+ if (!ent->solid)
+ continue;
+
+ if (ent->number == cl.playernum+1)
+ continue;
+
+ if (ent->solid == 31)
+ { // special value for bmodel
+ cmodel = cl.model_clip[ent->modelindex];
+ if (!cmodel)
+ continue;
+ headnode = cmodel->headnode;
+ angles = ent->angles;
+ }
+ else
+ { // encoded bbox
+ x = 8*(ent->solid & 31);
+ zd = 8*((ent->solid>>5) & 31);
+ zu = 8*((ent->solid>>10) & 63) - 32;
+
+ bmins[0] = bmins[1] = -x;
+ bmaxs[0] = bmaxs[1] = x;
+ bmins[2] = -zd;
+ bmaxs[2] = zu;
+
+ headnode = CM_HeadnodeForBox (bmins, bmaxs);
+ angles = vec3_origin; // boxes don't rotate
+ }
+
+ if (tr->allsolid)
+ return;
+
+ trace = CM_TransformedBoxTrace (start, end,
+ mins, maxs, headnode, MASK_PLAYERSOLID,
+ ent->origin, angles);
+
+ if (trace.allsolid || trace.startsolid ||
+ trace.fraction < tr->fraction)
+ {
+ trace.ent = (struct edict_s *)ent;
+ if (tr->startsolid)
+ {
+ *tr = trace;
+ tr->startsolid = true;
+ }
+ else
+ *tr = trace;
+ }
+ else if (trace.startsolid)
+ tr->startsolid = true;
+ }
+}
+
+
+/*
+================
+CL_PMTrace
+================
+*/
+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;
+
+ // check all other solid models
+ CL_ClipMoveToEntities (start, mins, maxs, end, &t);
+
+ return t;
+}
+
+int CL_PMpointcontents (vec3_t point)
+{
+ int i;
+ entity_state_t *ent;
+ int num;
+ cmodel_t *cmodel;
+ int contents;
+
+ contents = CM_PointContents (point, 0);
+
+ for (i=0 ; i<cl.frame.num_entities ; i++)
+ {
+ num = (cl.frame.parse_entities + i)&(MAX_PARSE_ENTITIES-1);
+ ent = &cl_parse_entities[num];
+
+ if (ent->solid != 31) // special value for bmodel
+ continue;
+
+ cmodel = cl.model_clip[ent->modelindex];
+ if (!cmodel)
+ continue;
+
+ contents |= CM_TransformedPointContents (point, cmodel->headnode, ent->origin, ent->angles);
+ }
+
+ return contents;
+}
+
+
+/*
+=================
+CL_PredictMovement
+
+Sets cl.predicted_origin and cl.predicted_angles
+=================
+*/
+void CL_PredictMovement (void)
+{
+ int ack, current;
+ int frame;
+ int oldframe;
+ usercmd_t *cmd;
+ pmove_t pm;
+ int i;
+ int step;
+ int oldz;
+
+ if (cls.state != ca_active)
+ return;
+
+ if (cl_paused->value)
+ return;
+
+ if (!cl_predict->value || (cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION))
+ { // just set angles
+ for (i=0 ; i<3 ; i++)
+ {
+ cl.predicted_angles[i] = cl.viewangles[i] + SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[i]);
+ }
+ return;
+ }
+
+ ack = cls.netchan.incoming_acknowledged;
+ current = cls.netchan.outgoing_sequence;
+
+ // if we are too far out of date, just freeze
+ if (current - ack >= CMD_BACKUP)
+ {
+ if (cl_showmiss->value)
+ Com_Printf ("exceeded CMD_BACKUP\n");
+ return;
+ }
+
+ // copy current state to pmove
+ memset (&pm, 0, sizeof(pm));
+ pm.trace = CL_PMTrace;
+ pm.pointcontents = CL_PMpointcontents;
+
+ pm_airaccelerate = atof(cl.configstrings[CS_AIRACCEL]);
+
+ pm.s = cl.frame.playerstate.pmove;
+
+// SCR_DebugGraph (current - ack - 1, 0);
+
+ frame = 0;
+
+ // run frames
+ while (++ack < current)
+ {
+ frame = ack & (CMD_BACKUP-1);
+ cmd = &cl.cmds[frame];
+
+ pm.cmd = *cmd;
+ Pmove (&pm);
+
+ // save for debug checking
+ VectorCopy (pm.s.origin, cl.predicted_origins[frame]);
+ }
+
+ oldframe = (ack-2) & (CMD_BACKUP-1);
+ oldz = cl.predicted_origins[oldframe][2];
+ step = pm.s.origin[2] - oldz;
+ if (step > 63 && step < 160 && (pm.s.pm_flags & PMF_ON_GROUND) )
+ {
+ cl.predicted_step = step * 0.125;
+ cl.predicted_step_time = cls.realtime - cls.frametime * 500;
+ }
+
+
+ // copy results out for rendering
+ cl.predicted_origin[0] = pm.s.origin[0]*0.125;
+ cl.predicted_origin[1] = pm.s.origin[1]*0.125;
+ cl.predicted_origin[2] = pm.s.origin[2]*0.125;
+
+ VectorCopy (pm.viewangles, cl.predicted_angles);
+}
diff --git a/client/cl_scrn.c b/client/cl_scrn.c
new file mode 100644
index 0000000..8c09b3b
--- /dev/null
+++ b/client/cl_scrn.c
@@ -0,0 +1,1401 @@
+/*
+Copyright (C) 1997-2001 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.
+
+*/
+// cl_scrn.c -- master for refresh, status bar, console, chat, notify, etc
+
+/*
+
+ full screen console
+ put up loading plaque
+ blanked background with loading plaque
+ blanked background with menu
+ cinematics
+ full screen image for quit and victory
+
+ end of unit intermissions
+
+ */
+
+#include "client.h"
+
+float scr_con_current; // aproaches scr_conlines at scr_conspeed
+float scr_conlines; // 0.0 to 1.0 lines of console to display
+
+qboolean scr_initialized; // ready to draw
+
+int scr_draw_loading;
+
+vrect_t scr_vrect; // position of render window on screen
+
+
+cvar_t *scr_viewsize;
+cvar_t *scr_conspeed;
+cvar_t *scr_centertime;
+cvar_t *scr_showturtle;
+cvar_t *scr_showpause;
+cvar_t *scr_printspeed;
+
+cvar_t *scr_netgraph;
+cvar_t *scr_timegraph;
+cvar_t *scr_debuggraph;
+cvar_t *scr_graphheight;
+cvar_t *scr_graphscale;
+cvar_t *scr_graphshift;
+cvar_t *scr_drawall;
+
+typedef struct
+{
+ int x1, y1, x2, y2;
+} dirty_t;
+
+dirty_t scr_dirty, scr_old_dirty[2];
+
+char crosshair_pic[MAX_QPATH];
+int crosshair_width, crosshair_height;
+
+void SCR_TimeRefresh_f (void);
+void SCR_Loading_f (void);
+
+
+/*
+===============================================================================
+
+BAR GRAPHS
+
+===============================================================================
+*/
+
+/*
+==============
+CL_AddNetgraph
+
+A new packet was just parsed
+==============
+*/
+void CL_AddNetgraph (void)
+{
+ int i;
+ int in;
+ int ping;
+
+ // if using the debuggraph for something else, don't
+ // add the net lines
+ if (scr_debuggraph->value || scr_timegraph->value)
+ return;
+
+ for (i=0 ; i<cls.netchan.dropped ; i++)
+ SCR_DebugGraph (30, 0x40);
+
+ for (i=0 ; i<cl.surpressCount ; i++)
+ SCR_DebugGraph (30, 0xdf);
+
+ // see what the latency was on this packet
+ in = cls.netchan.incoming_acknowledged & (CMD_BACKUP-1);
+ ping = cls.realtime - cl.cmd_time[in];
+ ping /= 30;
+ if (ping > 30)
+ ping = 30;
+ SCR_DebugGraph (ping, 0xd0);
+}
+
+
+typedef struct
+{
+ float value;
+ int color;
+} graphsamp_t;
+
+static int current;
+static graphsamp_t values[1024];
+
+/*
+==============
+SCR_DebugGraph
+==============
+*/
+void SCR_DebugGraph (float value, int color)
+{
+ values[current&1023].value = value;
+ values[current&1023].color = color;
+ current++;
+}
+
+/*
+==============
+SCR_DrawDebugGraph
+==============
+*/
+void SCR_DrawDebugGraph (void)
+{
+ int a, x, y, w, i, h;
+ float v;
+ int color;
+
+ //
+ // draw the graph
+ //
+ w = scr_vrect.width;
+
+ x = scr_vrect.x;
+ y = scr_vrect.y+scr_vrect.height;
+ re.DrawFill (x, y-scr_graphheight->value,
+ w, scr_graphheight->value, 8);
+
+ for (a=0 ; a<w ; a++)
+ {
+ i = (current-1-a+1024) & 1023;
+ v = values[i].value;
+ color = values[i].color;
+ v = v*scr_graphscale->value + scr_graphshift->value;
+
+ if (v < 0)
+ v += scr_graphheight->value * (1+(int)(-v/scr_graphheight->value));
+ h = (int)v % (int)scr_graphheight->value;
+ re.DrawFill (x+w-1-a, y - h, 1, h, color);
+ }
+}
+
+/*
+===============================================================================
+
+CENTER PRINTING
+
+===============================================================================
+*/
+
+char scr_centerstring[1024];
+float scr_centertime_start; // for slow victory printing
+float scr_centertime_off;
+int scr_center_lines;
+int scr_erase_center;
+
+/*
+==============
+SCR_CenterPrint
+
+Called for important messages that should stay in the center of the screen
+for a few moments
+==============
+*/
+void SCR_CenterPrint (char *str)
+{
+ char *s;
+ char line[64];
+ int i, j, l;
+
+ strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
+ scr_centertime_off = scr_centertime->value;
+ scr_centertime_start = cl.time;
+
+ // count the number of lines for centering
+ scr_center_lines = 1;
+ s = str;
+ while (*s)
+ {
+ if (*s == '\n')
+ scr_center_lines++;
+ s++;
+ }
+
+ // echo it to the console
+ Com_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
+
+ s = str;
+ do
+ {
+ // scan the width of the line
+ for (l=0 ; l<40 ; l++)
+ if (s[l] == '\n' || !s[l])
+ break;
+ for (i=0 ; i<(40-l)/2 ; i++)
+ line[i] = ' ';
+
+ for (j=0 ; j<l ; j++)
+ {
+ line[i++] = s[j];
+ }
+
+ line[i] = '\n';
+ line[i+1] = 0;
+
+ Com_Printf ("%s", line);
+
+ while (*s && *s != '\n')
+ s++;
+
+ if (!*s)
+ break;
+ s++; // skip the \n
+ } while (1);
+ Com_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
+ Con_ClearNotify ();
+}
+
+
+void SCR_DrawCenterString (void)
+{
+ char *start;
+ int l;
+ int j;
+ int x, y;
+ int remaining;
+
+// the finale prints the characters one at a time
+ remaining = 9999;
+
+ scr_erase_center = 0;
+ start = scr_centerstring;
+
+ if (scr_center_lines <= 4)
+ y = viddef.height*0.35;
+ else
+ y = 48;
+
+ do
+ {
+ // scan the width of the line
+ for (l=0 ; l<40 ; l++)
+ if (start[l] == '\n' || !start[l])
+ break;
+ x = (viddef.width - l*8)/2;
+ SCR_AddDirtyPoint (x, y);
+ for (j=0 ; j<l ; j++, x+=8)
+ {
+ re.DrawChar (x, y, start[j]);
+ if (!remaining--)
+ return;
+ }
+ SCR_AddDirtyPoint (x, y+8);
+
+ y += 8;
+
+ while (*start && *start != '\n')
+ start++;
+
+ if (!*start)
+ break;
+ start++; // skip the \n
+ } while (1);
+}
+
+void SCR_CheckDrawCenterString (void)
+{
+ scr_centertime_off -= cls.frametime;
+
+ if (scr_centertime_off <= 0)
+ return;
+
+ SCR_DrawCenterString ();
+}
+
+//=============================================================================
+
+/*
+=================
+SCR_CalcVrect
+
+Sets scr_vrect, the coordinates of the rendered window
+=================
+*/
+static void SCR_CalcVrect (void)
+{
+ int size;
+
+ // bound viewsize
+ if (scr_viewsize->value < 40)
+ Cvar_Set ("viewsize","40");
+ if (scr_viewsize->value > 100)
+ Cvar_Set ("viewsize","100");
+
+ size = scr_viewsize->value;
+
+ scr_vrect.width = viddef.width*size/100;
+ scr_vrect.width &= ~7;
+
+ scr_vrect.height = viddef.height*size/100;
+ scr_vrect.height &= ~1;
+
+ scr_vrect.x = (viddef.width - scr_vrect.width)/2;
+ scr_vrect.y = (viddef.height - scr_vrect.height)/2;
+}
+
+
+/*
+=================
+SCR_SizeUp_f
+
+Keybinding command
+=================
+*/
+void SCR_SizeUp_f (void)
+{
+ Cvar_SetValue ("viewsize",scr_viewsize->value+10);
+}
+
+
+/*
+=================
+SCR_SizeDown_f
+
+Keybinding command
+=================
+*/
+void SCR_SizeDown_f (void)
+{
+ Cvar_SetValue ("viewsize",scr_viewsize->value-10);
+}
+
+/*
+=================
+SCR_Sky_f
+
+Set a specific sky and rotation speed
+=================
+*/
+void SCR_Sky_f (void)
+{
+ float rotate;
+ vec3_t axis;
+
+ if (Cmd_Argc() < 2)
+ {
+ Com_Printf ("Usage: sky <basename> <rotate> <axis x y z>\n");
+ return;
+ }
+ if (Cmd_Argc() > 2)
+ rotate = atof(Cmd_Argv(2));
+ else
+ rotate = 0;
+ if (Cmd_Argc() == 6)
+ {
+ axis[0] = atof(Cmd_Argv(3));
+ axis[1] = atof(Cmd_Argv(4));
+ axis[2] = atof(Cmd_Argv(5));
+ }
+ else
+ {
+ axis[0] = 0;
+ axis[1] = 0;
+ axis[2] = 1;
+ }
+
+ re.SetSky (Cmd_Argv(1), rotate, axis);
+}
+
+//============================================================================
+
+/*
+==================
+SCR_Init
+==================
+*/
+void SCR_Init (void)
+{
+ scr_viewsize = Cvar_Get ("viewsize", "100", CVAR_ARCHIVE);
+ scr_conspeed = Cvar_Get ("scr_conspeed", "3", 0);
+ scr_showturtle = Cvar_Get ("scr_showturtle", "0", 0);
+ scr_showpause = Cvar_Get ("scr_showpause", "1", 0);
+ scr_centertime = Cvar_Get ("scr_centertime", "2.5", 0);
+ scr_printspeed = Cvar_Get ("scr_printspeed", "8", 0);
+ scr_netgraph = Cvar_Get ("netgraph", "0", 0);
+ scr_timegraph = Cvar_Get ("timegraph", "0", 0);
+ scr_debuggraph = Cvar_Get ("debuggraph", "0", 0);
+ scr_graphheight = Cvar_Get ("graphheight", "32", 0);
+ scr_graphscale = Cvar_Get ("graphscale", "1", 0);
+ scr_graphshift = Cvar_Get ("graphshift", "0", 0);
+ scr_drawall = Cvar_Get ("scr_drawall", "0", 0);
+
+//
+// register our commands
+//
+ Cmd_AddCommand ("timerefresh",SCR_TimeRefresh_f);
+ Cmd_AddCommand ("loading",SCR_Loading_f);
+ Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
+ Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
+ Cmd_AddCommand ("sky",SCR_Sky_f);
+
+ scr_initialized = true;
+}
+
+
+/*
+==============
+SCR_DrawNet
+==============
+*/
+void SCR_DrawNet (void)
+{
+ if (cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged
+ < CMD_BACKUP-1)
+ return;
+
+ re.DrawPic (scr_vrect.x+64, scr_vrect.y, "net");
+}
+
+/*
+==============
+SCR_DrawPause
+==============
+*/
+void SCR_DrawPause (void)
+{
+ int w, h;
+
+ if (!scr_showpause->value) // turn off for screenshots
+ return;
+
+ if (!cl_paused->value)
+ return;
+
+ re.DrawGetPicSize (&w, &h, "pause");
+ re.DrawPic ((viddef.width-w)/2, viddef.height/2 + 8, "pause");
+}
+
+/*
+==============
+SCR_DrawLoading
+==============
+*/
+void SCR_DrawLoading (void)
+{
+ int w, h;
+
+ if (!scr_draw_loading)
+ return;
+
+ scr_draw_loading = false;
+ re.DrawGetPicSize (&w, &h, "loading");
+ re.DrawPic ((viddef.width-w)/2, (viddef.height-h)/2, "loading");
+}
+
+//=============================================================================
+
+/*
+==================
+SCR_RunConsole
+
+Scroll it up or down
+==================
+*/
+void SCR_RunConsole (void)
+{
+// decide on the height of the console
+ if (cls.key_dest == key_console)
+ scr_conlines = 0.5; // half screen
+ else
+ scr_conlines = 0; // none visible
+
+ if (scr_conlines < scr_con_current)
+ {
+ scr_con_current -= scr_conspeed->value*cls.frametime;
+ 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;
+ if (scr_conlines < scr_con_current)
+ scr_con_current = scr_conlines;
+ }
+
+}
+
+/*
+==================
+SCR_DrawConsole
+==================
+*/
+void SCR_DrawConsole (void)
+{
+ Con_CheckResize ();
+
+ if (cls.state == ca_disconnected || cls.state == ca_connecting)
+ { // forced full screen console
+ Con_DrawConsole (1.0);
+ return;
+ }
+
+ if (cls.state != ca_active || !cl.refresh_prepped)
+ { // connected, but can't render
+ Con_DrawConsole (0.5);
+ re.DrawFill (0, viddef.height/2, viddef.width, viddef.height/2, 0);
+ return;
+ }
+
+ if (scr_con_current)
+ {
+ Con_DrawConsole (scr_con_current);
+ }
+ else
+ {
+ if (cls.key_dest == key_game || cls.key_dest == key_message)
+ Con_DrawNotify (); // only draw notify in game
+ }
+}
+
+//=============================================================================
+
+/*
+================
+SCR_BeginLoadingPlaque
+================
+*/
+void SCR_BeginLoadingPlaque (void)
+{
+ S_StopAllSounds ();
+ cl.sound_prepped = false; // don't play ambients
+ CDAudio_Stop ();
+ if (cls.disable_screen)
+ return;
+ if (developer->value)
+ return;
+ if (cls.state == ca_disconnected)
+ return; // if at console, don't bring up the plaque
+ if (cls.key_dest == key_console)
+ return;
+ if (cl.cinematictime > 0)
+ scr_draw_loading = 2; // clear to balack first
+ else
+ scr_draw_loading = 1;
+ SCR_UpdateScreen ();
+ cls.disable_screen = Sys_Milliseconds ();
+ cls.disable_servercount = cl.servercount;
+}
+
+/*
+================
+SCR_EndLoadingPlaque
+================
+*/
+void SCR_EndLoadingPlaque (void)
+{
+ cls.disable_screen = 0;
+ Con_ClearNotify ();
+}
+
+/*
+================
+SCR_Loading_f
+================
+*/
+void SCR_Loading_f (void)
+{
+ SCR_BeginLoadingPlaque ();
+}
+
+/*
+================
+SCR_TimeRefresh_f
+================
+*/
+int entitycmpfnc( const entity_t *a, const entity_t *b )
+{
+ /*
+ ** all other models are sorted by model then skin
+ */
+ if ( a->model == b->model )
+ {
+ return ( ( int ) a->skin - ( int ) b->skin );
+ }
+ else
+ {
+ return ( ( int ) a->model - ( int ) b->model );
+ }
+}
+
+void SCR_TimeRefresh_f (void)
+{
+ int i;
+ int start, stop;
+ float time;
+
+ if ( cls.state != ca_active )
+ return;
+
+ start = Sys_Milliseconds ();
+
+ if (Cmd_Argc() == 2)
+ { // run without page flipping
+ re.BeginFrame( 0 );
+ for (i=0 ; i<128 ; i++)
+ {
+ cl.refdef.viewangles[1] = i/128.0*360.0;
+ re.RenderFrame (&cl.refdef);
+ }
+ re.EndFrame();
+ }
+ else
+ {
+ for (i=0 ; i<128 ; i++)
+ {
+ cl.refdef.viewangles[1] = i/128.0*360.0;
+
+ re.BeginFrame( 0 );
+ re.RenderFrame (&cl.refdef);
+ re.EndFrame();
+ }
+ }
+
+ stop = Sys_Milliseconds ();
+ time = (stop-start)/1000.0;
+ Com_Printf ("%f seconds (%f fps)\n", time, 128/time);
+}
+
+/*
+=================
+SCR_AddDirtyPoint
+=================
+*/
+void SCR_AddDirtyPoint (int x, int y)
+{
+ if (x < scr_dirty.x1)
+ scr_dirty.x1 = x;
+ if (x > scr_dirty.x2)
+ scr_dirty.x2 = x;
+ if (y < scr_dirty.y1)
+ scr_dirty.y1 = y;
+ if (y > scr_dirty.y2)
+ scr_dirty.y2 = y;
+}
+
+void SCR_DirtyScreen (void)
+{
+ SCR_AddDirtyPoint (0, 0);
+ SCR_AddDirtyPoint (viddef.width-1, viddef.height-1);
+}
+
+/*
+==============
+SCR_TileClear
+
+Clear any parts of the tiled background that were drawn on last frame
+==============
+*/
+void SCR_TileClear (void)
+{
+ int i;
+ int top, bottom, left, right;
+ dirty_t clear;
+
+ if (scr_drawall->value)
+ SCR_DirtyScreen (); // for power vr or broken page flippers...
+
+ if (scr_con_current == 1.0)
+ return; // full screen console
+ if (scr_viewsize->value == 100)
+ return; // full screen rendering
+ if (cl.cinematictime > 0)
+ return; // full screen cinematic
+
+ // erase rect will be the union of the past three frames
+ // so tripple buffering works properly
+ clear = scr_dirty;
+ for (i=0 ; i<2 ; i++)
+ {
+ if (scr_old_dirty[i].x1 < clear.x1)
+ clear.x1 = scr_old_dirty[i].x1;
+ if (scr_old_dirty[i].x2 > clear.x2)
+ clear.x2 = scr_old_dirty[i].x2;
+ if (scr_old_dirty[i].y1 < clear.y1)
+ clear.y1 = scr_old_dirty[i].y1;
+ if (scr_old_dirty[i].y2 > clear.y2)
+ clear.y2 = scr_old_dirty[i].y2;
+ }
+
+ scr_old_dirty[1] = scr_old_dirty[0];
+ scr_old_dirty[0] = scr_dirty;
+
+ scr_dirty.x1 = 9999;
+ scr_dirty.x2 = -9999;
+ scr_dirty.y1 = 9999;
+ scr_dirty.y2 = -9999;
+
+ // don't bother with anything convered by the console)
+ top = scr_con_current*viddef.height;
+ if (top >= clear.y1)
+ clear.y1 = top;
+
+ if (clear.y2 <= clear.y1)
+ return; // nothing disturbed
+
+ top = scr_vrect.y;
+ bottom = top + scr_vrect.height-1;
+ left = scr_vrect.x;
+ right = left + scr_vrect.width-1;
+
+ if (clear.y1 < top)
+ { // clear above view screen
+ i = clear.y2 < top-1 ? clear.y2 : top-1;
+ re.DrawTileClear (clear.x1 , clear.y1,
+ clear.x2 - clear.x1 + 1, i - clear.y1+1, "backtile");
+ clear.y1 = top;
+ }
+ if (clear.y2 > bottom)
+ { // clear below view screen
+ i = clear.y1 > bottom+1 ? clear.y1 : bottom+1;
+ re.DrawTileClear (clear.x1, i,
+ clear.x2-clear.x1+1, clear.y2-i+1, "backtile");
+ clear.y2 = bottom;
+ }
+ if (clear.x1 < left)
+ { // clear left of view screen
+ i = clear.x2 < left-1 ? clear.x2 : left-1;
+ re.DrawTileClear (clear.x1, clear.y1,
+ i-clear.x1+1, clear.y2 - clear.y1 + 1, "backtile");
+ clear.x1 = left;
+ }
+ if (clear.x2 > right)
+ { // clear left of view screen
+ i = clear.x1 > right+1 ? clear.x1 : right+1;
+ re.DrawTileClear (i, clear.y1,
+ clear.x2-i+1, clear.y2 - clear.y1 + 1, "backtile");
+ clear.x2 = right;
+ }
+
+}
+
+
+//===============================================================
+
+
+#define STAT_MINUS 10 // num frame for '-' stats digit
+char *sb_nums[2][11] =
+{
+ {"num_0", "num_1", "num_2", "num_3", "num_4", "num_5",
+ "num_6", "num_7", "num_8", "num_9", "num_minus"},
+ {"anum_0", "anum_1", "anum_2", "anum_3", "anum_4", "anum_5",
+ "anum_6", "anum_7", "anum_8", "anum_9", "anum_minus"}
+};
+
+#define ICON_WIDTH 24
+#define ICON_HEIGHT 24
+#define CHAR_WIDTH 16
+#define ICON_SPACE 8
+
+
+
+/*
+================
+SizeHUDString
+
+Allow embedded \n in the string
+================
+*/
+void SizeHUDString (char *string, int *w, int *h)
+{
+ int lines, width, current;
+
+ lines = 1;
+ width = 0;
+
+ current = 0;
+ while (*string)
+ {
+ if (*string == '\n')
+ {
+ lines++;
+ current = 0;
+ }
+ else
+ {
+ current++;
+ if (current > width)
+ width = current;
+ }
+ string++;
+ }
+
+ *w = width * 8;
+ *h = lines * 8;
+}
+
+void DrawHUDString (char *string, int x, int y, int centerwidth, int xor)
+{
+ int margin;
+ char line[1024];
+ int width;
+ int i;
+
+ margin = x;
+
+ while (*string)
+ {
+ // scan out one line of text from the string
+ width = 0;
+ while (*string && *string != '\n')
+ line[width++] = *string++;
+ line[width] = 0;
+
+ if (centerwidth)
+ x = margin + (centerwidth - width*8)/2;
+ else
+ x = margin;
+ for (i=0 ; i<width ; i++)
+ {
+ re.DrawChar (x, y, line[i]^xor);
+ x += 8;
+ }
+ if (*string)
+ {
+ string++; // skip the \n
+ x = margin;
+ y += 8;
+ }
+ }
+}
+
+
+/*
+==============
+SCR_DrawField
+==============
+*/
+void SCR_DrawField (int x, int y, int color, int width, int value)
+{
+ char num[16], *ptr;
+ int l;
+ int frame;
+
+ if (width < 1)
+ return;
+
+ // draw number string
+ if (width > 5)
+ width = 5;
+
+ SCR_AddDirtyPoint (x, y);
+ SCR_AddDirtyPoint (x+width*CHAR_WIDTH+2, y+23);
+
+ Com_sprintf (num, sizeof(num), "%i", value);
+ l = strlen(num);
+ if (l > width)
+ l = width;
+ x += 2 + CHAR_WIDTH*(width - l);
+
+ ptr = num;
+ while (*ptr && l)
+ {
+ if (*ptr == '-')
+ frame = STAT_MINUS;
+ else
+ frame = *ptr -'0';
+
+ re.DrawPic (x,y,sb_nums[color][frame]);
+ x += CHAR_WIDTH;
+ ptr++;
+ l--;
+ }
+}
+
+
+/*
+===============
+SCR_TouchPics
+
+Allows rendering code to cache all needed sbar graphics
+===============
+*/
+void SCR_TouchPics (void)
+{
+ int i, j;
+
+ for (i=0 ; i<2 ; i++)
+ for (j=0 ; j<11 ; j++)
+ re.RegisterPic (sb_nums[i][j]);
+
+ if (crosshair->value)
+ {
+ if (crosshair->value > 3 || crosshair->value < 0)
+ crosshair->value = 3;
+
+ Com_sprintf (crosshair_pic, sizeof(crosshair_pic), "ch%i", (int)(crosshair->value));
+ re.DrawGetPicSize (&crosshair_width, &crosshair_height, crosshair_pic);
+ if (!crosshair_width)
+ crosshair_pic[0] = 0;
+ }
+}
+
+/*
+================
+SCR_ExecuteLayoutString
+
+================
+*/
+void SCR_ExecuteLayoutString (char *s)
+{
+ int x, y;
+ int value;
+ char *token;
+ int width;
+ int index;
+ clientinfo_t *ci;
+
+ if (cls.state != ca_active || !cl.refresh_prepped)
+ return;
+
+ if (!s[0])
+ return;
+
+ x = 0;
+ y = 0;
+ width = 3;
+
+ while (s)
+ {
+ token = COM_Parse (&s);
+ if (!strcmp(token, "xl"))
+ {
+ token = COM_Parse (&s);
+ x = atoi(token);
+ continue;
+ }
+ if (!strcmp(token, "xr"))
+ {
+ token = COM_Parse (&s);
+ x = viddef.width + atoi(token);
+ continue;
+ }
+ if (!strcmp(token, "xv"))
+ {
+ token = COM_Parse (&s);
+ x = viddef.width/2 - 160 + atoi(token);
+ continue;
+ }
+
+ if (!strcmp(token, "yt"))
+ {
+ token = COM_Parse (&s);
+ y = atoi(token);
+ continue;
+ }
+ if (!strcmp(token, "yb"))
+ {
+ token = COM_Parse (&s);
+ y = viddef.height + atoi(token);
+ continue;
+ }
+ if (!strcmp(token, "yv"))
+ {
+ token = COM_Parse (&s);
+ y = viddef.height/2 - 120 + atoi(token);
+ continue;
+ }
+
+ if (!strcmp(token, "pic"))
+ { // draw a pic from a stat number
+ token = COM_Parse (&s);
+ value = cl.frame.playerstate.stats[atoi(token)];
+ if (value >= MAX_IMAGES)
+ Com_Error (ERR_DROP, "Pic >= MAX_IMAGES");
+ if (cl.configstrings[CS_IMAGES+value])
+ {
+ SCR_AddDirtyPoint (x, y);
+ SCR_AddDirtyPoint (x+23, y+23);
+ re.DrawPic (x, y, cl.configstrings[CS_IMAGES+value]);
+ }
+ continue;
+ }
+
+ if (!strcmp(token, "client"))
+ { // draw a deathmatch client block
+ int score, ping, time;
+
+ token = COM_Parse (&s);
+ x = viddef.width/2 - 160 + atoi(token);
+ token = COM_Parse (&s);
+ y = viddef.height/2 - 120 + atoi(token);
+ SCR_AddDirtyPoint (x, y);
+ SCR_AddDirtyPoint (x+159, y+31);
+
+ token = COM_Parse (&s);
+ value = atoi(token);
+ if (value >= MAX_CLIENTS || value < 0)
+ Com_Error (ERR_DROP, "client >= MAX_CLIENTS");
+ ci = &cl.clientinfo[value];
+
+ token = COM_Parse (&s);
+ score = atoi(token);
+
+ token = COM_Parse (&s);
+ ping = atoi(token);
+
+ token = COM_Parse (&s);
+ time = atoi(token);
+
+ DrawAltString (x+32, y, ci->name);
+ DrawString (x+32, y+8, "Score: ");
+ DrawAltString (x+32+7*8, y+8, va("%i", score));
+ DrawString (x+32, y+16, va("Ping: %i", ping));
+ DrawString (x+32, y+24, va("Time: %i", time));
+
+ if (!ci->icon)
+ ci = &cl.baseclientinfo;
+ re.DrawPic (x, y, ci->iconname);
+ continue;
+ }
+
+ if (!strcmp(token, "ctf"))
+ { // draw a ctf client block
+ int score, ping;
+ char block[80];
+
+ token = COM_Parse (&s);
+ x = viddef.width/2 - 160 + atoi(token);
+ token = COM_Parse (&s);
+ y = viddef.height/2 - 120 + atoi(token);
+ SCR_AddDirtyPoint (x, y);
+ SCR_AddDirtyPoint (x+159, y+31);
+
+ token = COM_Parse (&s);
+ value = atoi(token);
+ if (value >= MAX_CLIENTS || value < 0)
+ Com_Error (ERR_DROP, "client >= MAX_CLIENTS");
+ ci = &cl.clientinfo[value];
+
+ token = COM_Parse (&s);
+ score = atoi(token);
+
+ token = COM_Parse (&s);
+ ping = atoi(token);
+ if (ping > 999)
+ ping = 999;
+
+ sprintf(block, "%3d %3d %-12.12s", score, ping, ci->name);
+
+ if (value == cl.playernum)
+ DrawAltString (x, y, block);
+ else
+ DrawString (x, y, block);
+ continue;
+ }
+
+ if (!strcmp(token, "picn"))
+ { // draw a pic from a name
+ token = COM_Parse (&s);
+ SCR_AddDirtyPoint (x, y);
+ SCR_AddDirtyPoint (x+23, y+23);
+ re.DrawPic (x, y, token);
+ continue;
+ }
+
+ if (!strcmp(token, "num"))
+ { // draw a number
+ token = COM_Parse (&s);
+ width = atoi(token);
+ token = COM_Parse (&s);
+ value = cl.frame.playerstate.stats[atoi(token)];
+ SCR_DrawField (x, y, 0, width, value);
+ continue;
+ }
+
+ if (!strcmp(token, "hnum"))
+ { // health number
+ int color;
+
+ width = 3;
+ value = cl.frame.playerstate.stats[STAT_HEALTH];
+ if (value > 25)
+ color = 0; // green
+ else if (value > 0)
+ color = (cl.frame.serverframe>>2) & 1; // flash
+ else
+ color = 1;
+
+ if (cl.frame.playerstate.stats[STAT_FLASHES] & 1)
+ re.DrawPic (x, y, "field_3");
+
+ SCR_DrawField (x, y, color, width, value);
+ continue;
+ }
+
+ if (!strcmp(token, "anum"))
+ { // ammo number
+ int color;
+
+ width = 3;
+ value = cl.frame.playerstate.stats[STAT_AMMO];
+ if (value > 5)
+ color = 0; // green
+ else if (value >= 0)
+ color = (cl.frame.serverframe>>2) & 1; // flash
+ else
+ continue; // negative number = don't show
+
+ if (cl.frame.playerstate.stats[STAT_FLASHES] & 4)
+ re.DrawPic (x, y, "field_3");
+
+ SCR_DrawField (x, y, color, width, value);
+ continue;
+ }
+
+ if (!strcmp(token, "rnum"))
+ { // armor number
+ int color;
+
+ width = 3;
+ value = cl.frame.playerstate.stats[STAT_ARMOR];
+ if (value < 1)
+ continue;
+
+ color = 0; // green
+
+ if (cl.frame.playerstate.stats[STAT_FLASHES] & 2)
+ re.DrawPic (x, y, "field_3");
+
+ SCR_DrawField (x, y, color, width, value);
+ continue;
+ }
+
+
+ if (!strcmp(token, "stat_string"))
+ {
+ token = COM_Parse (&s);
+ index = atoi(token);
+ if (index < 0 || index >= MAX_CONFIGSTRINGS)
+ Com_Error (ERR_DROP, "Bad stat_string index");
+ index = cl.frame.playerstate.stats[index];
+ if (index < 0 || index >= MAX_CONFIGSTRINGS)
+ Com_Error (ERR_DROP, "Bad stat_string index");
+ DrawString (x, y, cl.configstrings[index]);
+ continue;
+ }
+
+ if (!strcmp(token, "cstring"))
+ {
+ token = COM_Parse (&s);
+ DrawHUDString (token, x, y, 320, 0);
+ continue;
+ }
+
+ if (!strcmp(token, "string"))
+ {
+ token = COM_Parse (&s);
+ DrawString (x, y, token);
+ continue;
+ }
+
+ if (!strcmp(token, "cstring2"))
+ {
+ token = COM_Parse (&s);
+ DrawHUDString (token, x, y, 320,0x80);
+ continue;
+ }
+
+ if (!strcmp(token, "string2"))
+ {
+ token = COM_Parse (&s);
+ DrawAltString (x, y, token);
+ continue;
+ }
+
+ if (!strcmp(token, "if"))
+ { // draw a number
+ token = COM_Parse (&s);
+ value = cl.frame.playerstate.stats[atoi(token)];
+ if (!value)
+ { // skip to endif
+ while (s && strcmp(token, "endif") )
+ {
+ token = COM_Parse (&s);
+ }
+ }
+
+ continue;
+ }
+
+
+ }
+}
+
+
+/*
+================
+SCR_DrawStats
+
+The status bar is a small layout program that
+is based on the stats array
+================
+*/
+void SCR_DrawStats (void)
+{
+ SCR_ExecuteLayoutString (cl.configstrings[CS_STATUSBAR]);
+}
+
+
+/*
+================
+SCR_DrawLayout
+
+================
+*/
+#define STAT_LAYOUTS 13
+
+void SCR_DrawLayout (void)
+{
+ if (!cl.frame.playerstate.stats[STAT_LAYOUTS])
+ return;
+ SCR_ExecuteLayoutString (cl.layout);
+}
+
+//=======================================================
+
+/*
+==================
+SCR_UpdateScreen
+
+This is called every frame, and can also be called explicitly to flush
+text to the screen.
+==================
+*/
+void SCR_UpdateScreen (void)
+{
+ int numframes;
+ int i;
+ float separation[2] = { 0, 0 };
+
+ // if the screen is disabled (loading plaque is up, or vid mode changing)
+ // do nothing at all
+ if (cls.disable_screen)
+ {
+ if (Sys_Milliseconds() - cls.disable_screen > 120000)
+ {
+ cls.disable_screen = 0;
+ Com_Printf ("Loading plaque timed out.\n");
+ }
+ return;
+ }
+
+ if (!scr_initialized || !con.initialized)
+ return; // not initialized yet
+
+ /*
+ ** range check cl_camera_separation so we don't inadvertently fry someone's
+ ** brain
+ */
+ if ( cl_stereo_separation->value > 1.0 )
+ Cvar_SetValue( "cl_stereo_separation", 1.0 );
+ else if ( cl_stereo_separation->value < 0 )
+ Cvar_SetValue( "cl_stereo_separation", 0.0 );
+
+ if ( cl_stereo->value )
+ {
+ numframes = 2;
+ separation[0] = -cl_stereo_separation->value / 2;
+ separation[1] = cl_stereo_separation->value / 2;
+ }
+ else
+ {
+ separation[0] = 0;
+ separation[1] = 0;
+ numframes = 1;
+ }
+
+ for ( i = 0; i < numframes; i++ )
+ {
+ re.BeginFrame( separation[i] );
+
+ if (scr_draw_loading == 2)
+ { // loading plaque over black screen
+ int w, h;
+
+ re.CinematicSetPalette(NULL);
+ scr_draw_loading = false;
+ re.DrawGetPicSize (&w, &h, "loading");
+ re.DrawPic ((viddef.width-w)/2, (viddef.height-h)/2, "loading");
+// re.EndFrame();
+// return;
+ }
+ // if a cinematic is supposed to be running, handle menus
+ // and console specially
+ else if (cl.cinematictime > 0)
+ {
+ if (cls.key_dest == key_menu)
+ {
+ if (cl.cinematicpalette_active)
+ {
+ re.CinematicSetPalette(NULL);
+ cl.cinematicpalette_active = false;
+ }
+ M_Draw ();
+// re.EndFrame();
+// return;
+ }
+ else if (cls.key_dest == key_console)
+ {
+ if (cl.cinematicpalette_active)
+ {
+ re.CinematicSetPalette(NULL);
+ cl.cinematicpalette_active = false;
+ }
+ SCR_DrawConsole ();
+// re.EndFrame();
+// return;
+ }
+ else
+ {
+ SCR_DrawCinematic();
+// re.EndFrame();
+// return;
+ }
+ }
+ else
+ {
+
+ // make sure the game palette is active
+ if (cl.cinematicpalette_active)
+ {
+ re.CinematicSetPalette(NULL);
+ cl.cinematicpalette_active = false;
+ }
+
+ // do 3D refresh drawing, and then update the screen
+ SCR_CalcVrect ();
+
+ // clear any dirty part of the background
+ SCR_TileClear ();
+
+ V_RenderView ( separation[i] );
+
+ SCR_DrawStats ();
+ if (cl.frame.playerstate.stats[STAT_LAYOUTS] & 1)
+ SCR_DrawLayout ();
+ if (cl.frame.playerstate.stats[STAT_LAYOUTS] & 2)
+ CL_DrawInventory ();
+
+ SCR_DrawNet ();
+ SCR_CheckDrawCenterString ();
+
+ if (scr_timegraph->value)
+ SCR_DebugGraph (cls.frametime*300, 0);
+
+ if (scr_debuggraph->value || scr_timegraph->value || scr_netgraph->value)
+ SCR_DrawDebugGraph ();
+
+ SCR_DrawPause ();
+
+ SCR_DrawConsole ();
+
+ M_Draw ();
+
+ SCR_DrawLoading ();
+ }
+ }
+ re.EndFrame();
+}
diff --git a/client/cl_tent.c b/client/cl_tent.c
new file mode 100644
index 0000000..c039b49
--- /dev/null
+++ b/client/cl_tent.c
@@ -0,0 +1,1745 @@
+/*
+Copyright (C) 1997-2001 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.
+
+*/
+// cl_tent.c -- client side temporary entities
+
+#include "client.h"
+
+typedef enum
+{
+ ex_free, ex_explosion, ex_misc, ex_flash, ex_mflash, ex_poly, ex_poly2
+} exptype_t;
+
+typedef struct
+{
+ exptype_t type;
+ entity_t ent;
+
+ int frames;
+ float light;
+ vec3_t lightcolor;
+ float start;
+ int baseframe;
+} explosion_t;
+
+
+
+#define MAX_EXPLOSIONS 32
+explosion_t cl_explosions[MAX_EXPLOSIONS];
+
+
+#define MAX_BEAMS 32
+typedef struct
+{
+ int entity;
+ int dest_entity;
+ struct model_s *model;
+ int endtime;
+ vec3_t offset;
+ vec3_t start, end;
+} beam_t;
+beam_t cl_beams[MAX_BEAMS];
+//PMM - added this for player-linked beams. Currently only used by the plasma beam
+beam_t cl_playerbeams[MAX_BEAMS];
+
+
+#define MAX_LASERS 32
+typedef struct
+{
+ entity_t ent;
+ int endtime;
+} laser_t;
+laser_t cl_lasers[MAX_LASERS];
+
+//ROGUE
+cl_sustain_t cl_sustains[MAX_SUSTAINS];
+//ROGUE
+
+//PGM
+extern void CL_TeleportParticles (vec3_t org);
+//PGM
+
+void CL_BlasterParticles (vec3_t org, vec3_t dir);
+void CL_ExplosionParticles (vec3_t org);
+void CL_BFGExplosionParticles (vec3_t org);
+// RAFAEL
+void CL_BlueBlasterParticles (vec3_t org, vec3_t dir);
+
+struct sfx_s *cl_sfx_ric1;
+struct sfx_s *cl_sfx_ric2;
+struct sfx_s *cl_sfx_ric3;
+struct sfx_s *cl_sfx_lashit;
+struct sfx_s *cl_sfx_spark5;
+struct sfx_s *cl_sfx_spark6;
+struct sfx_s *cl_sfx_spark7;
+struct sfx_s *cl_sfx_railg;
+struct sfx_s *cl_sfx_rockexp;
+struct sfx_s *cl_sfx_grenexp;
+struct sfx_s *cl_sfx_watrexp;
+// RAFAEL
+struct sfx_s *cl_sfx_plasexp;
+struct sfx_s *cl_sfx_footsteps[4];
+
+struct model_s *cl_mod_explode;
+struct model_s *cl_mod_smoke;
+struct model_s *cl_mod_flash;
+struct model_s *cl_mod_parasite_segment;
+struct model_s *cl_mod_grapple_cable;
+struct model_s *cl_mod_parasite_tip;
+struct model_s *cl_mod_explo4;
+struct model_s *cl_mod_bfg_explo;
+struct model_s *cl_mod_powerscreen;
+// RAFAEL
+struct model_s *cl_mod_plasmaexplo;
+
+//ROGUE
+struct sfx_s *cl_sfx_lightning;
+struct sfx_s *cl_sfx_disrexp;
+struct model_s *cl_mod_lightning;
+struct model_s *cl_mod_heatbeam;
+struct model_s *cl_mod_monster_heatbeam;
+struct model_s *cl_mod_explo4_big;
+
+//ROGUE
+/*
+=================
+CL_RegisterTEntSounds
+=================
+*/
+void CL_RegisterTEntSounds (void)
+{
+ int i;
+ char name[MAX_QPATH];
+
+ // PMM - version stuff
+// Com_Printf ("%s\n", ROGUE_VERSION_STRING);
+ // PMM
+ cl_sfx_ric1 = S_RegisterSound ("world/ric1.wav");
+ cl_sfx_ric2 = S_RegisterSound ("world/ric2.wav");
+ cl_sfx_ric3 = S_RegisterSound ("world/ric3.wav");
+ cl_sfx_lashit = S_RegisterSound("weapons/lashit.wav");
+ cl_sfx_spark5 = S_RegisterSound ("world/spark5.wav");
+ cl_sfx_spark6 = S_RegisterSound ("world/spark6.wav");
+ cl_sfx_spark7 = S_RegisterSound ("world/spark7.wav");
+ cl_sfx_railg = S_RegisterSound ("weapons/railgf1a.wav");
+ cl_sfx_rockexp = S_RegisterSound ("weapons/rocklx1a.wav");
+ cl_sfx_grenexp = S_RegisterSound ("weapons/grenlx1a.wav");
+ cl_sfx_watrexp = S_RegisterSound ("weapons/xpld_wat.wav");
+ // RAFAEL
+ // cl_sfx_plasexp = S_RegisterSound ("weapons/plasexpl.wav");
+ S_RegisterSound ("player/land1.wav");
+
+ S_RegisterSound ("player/fall2.wav");
+ S_RegisterSound ("player/fall1.wav");
+
+ for (i=0 ; i<4 ; i++)
+ {
+ Com_sprintf (name, sizeof(name), "player/step%i.wav", i+1);
+ cl_sfx_footsteps[i] = S_RegisterSound (name);
+ }
+
+//PGM
+ cl_sfx_lightning = S_RegisterSound ("weapons/tesla.wav");
+ cl_sfx_disrexp = S_RegisterSound ("weapons/disrupthit.wav");
+ // version stuff
+ sprintf (name, "weapons/sound%d.wav", ROGUE_VERSION_ID);
+ if (name[0] == 'w')
+ name[0] = 'W';
+//PGM
+}
+
+/*
+=================
+CL_RegisterTEntModels
+=================
+*/
+void CL_RegisterTEntModels (void)
+{
+ cl_mod_explode = re.RegisterModel ("models/objects/explode/tris.md2");
+ cl_mod_smoke = re.RegisterModel ("models/objects/smoke/tris.md2");
+ cl_mod_flash = re.RegisterModel ("models/objects/flash/tris.md2");
+ cl_mod_parasite_segment = re.RegisterModel ("models/monsters/parasite/segment/tris.md2");
+ cl_mod_grapple_cable = re.RegisterModel ("models/ctf/segment/tris.md2");
+ cl_mod_parasite_tip = re.RegisterModel ("models/monsters/parasite/tip/tris.md2");
+ cl_mod_explo4 = re.RegisterModel ("models/objects/r_explode/tris.md2");
+ cl_mod_bfg_explo = re.RegisterModel ("sprites/s_bfg2.sp2");
+ cl_mod_powerscreen = re.RegisterModel ("models/items/armor/effect/tris.md2");
+
+re.RegisterModel ("models/objects/laser/tris.md2");
+re.RegisterModel ("models/objects/grenade2/tris.md2");
+re.RegisterModel ("models/weapons/v_machn/tris.md2");
+re.RegisterModel ("models/weapons/v_handgr/tris.md2");
+re.RegisterModel ("models/weapons/v_shotg2/tris.md2");
+re.RegisterModel ("models/objects/gibs/bone/tris.md2");
+re.RegisterModel ("models/objects/gibs/sm_meat/tris.md2");
+re.RegisterModel ("models/objects/gibs/bone2/tris.md2");
+// RAFAEL
+// re.RegisterModel ("models/objects/blaser/tris.md2");
+
+re.RegisterPic ("w_machinegun");
+re.RegisterPic ("a_bullets");
+re.RegisterPic ("i_health");
+re.RegisterPic ("a_grenades");
+
+//ROGUE
+ cl_mod_explo4_big = re.RegisterModel ("models/objects/r_explode2/tris.md2");
+ cl_mod_lightning = re.RegisterModel ("models/proj/lightning/tris.md2");
+ cl_mod_heatbeam = re.RegisterModel ("models/proj/beam/tris.md2");
+ cl_mod_monster_heatbeam = re.RegisterModel ("models/proj/widowbeam/tris.md2");
+//ROGUE
+}
+
+/*
+=================
+CL_ClearTEnts
+=================
+*/
+void CL_ClearTEnts (void)
+{
+ memset (cl_beams, 0, sizeof(cl_beams));
+ memset (cl_explosions, 0, sizeof(cl_explosions));
+ memset (cl_lasers, 0, sizeof(cl_lasers));
+
+//ROGUE
+ memset (cl_playerbeams, 0, sizeof(cl_playerbeams));
+ memset (cl_sustains, 0, sizeof(cl_sustains));
+//ROGUE
+}
+
+/*
+=================
+CL_AllocExplosion
+=================
+*/
+explosion_t *CL_AllocExplosion (void)
+{
+ int i;
+ int time;
+ int index;
+
+ for (i=0 ; i<MAX_EXPLOSIONS ; i++)
+ {
+ if (cl_explosions[i].type == ex_free)
+ {
+ memset (&cl_explosions[i], 0, sizeof (cl_explosions[i]));
+ return &cl_explosions[i];
+ }
+ }
+// find the oldest explosion
+ time = cl.time;
+ index = 0;
+
+ for (i=0 ; i<MAX_EXPLOSIONS ; i++)
+ if (cl_explosions[i].start < time)
+ {
+ time = cl_explosions[i].start;
+ index = i;
+ }
+ memset (&cl_explosions[index], 0, sizeof (cl_explosions[index]));
+ return &cl_explosions[index];
+}
+
+/*
+=================
+CL_SmokeAndFlash
+=================
+*/
+void CL_SmokeAndFlash(vec3_t origin)
+{
+ explosion_t *ex;
+
+ ex = CL_AllocExplosion ();
+ VectorCopy (origin, ex->ent.origin);
+ ex->type = ex_misc;
+ ex->frames = 4;
+ ex->ent.flags = RF_TRANSLUCENT;
+ ex->start = cl.frame.servertime - 100;
+ ex->ent.model = cl_mod_smoke;
+
+ ex = CL_AllocExplosion ();
+ VectorCopy (origin, ex->ent.origin);
+ ex->type = ex_flash;
+ ex->ent.flags = RF_FULLBRIGHT;
+ ex->frames = 2;
+ ex->start = cl.frame.servertime - 100;
+ ex->ent.model = cl_mod_flash;
+}
+
+/*
+=================
+CL_ParseParticles
+=================
+*/
+void CL_ParseParticles (void)
+{
+ int color, count;
+ vec3_t pos, dir;
+
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadDir (&net_message, dir);
+
+ color = MSG_ReadByte (&net_message);
+
+ count = MSG_ReadByte (&net_message);
+
+ CL_ParticleEffect (pos, dir, color, count);
+}
+
+/*
+=================
+CL_ParseBeam
+=================
+*/
+int CL_ParseBeam (struct model_s *model)
+{
+ int ent;
+ vec3_t start, end;
+ beam_t *b;
+ int i;
+
+ ent = MSG_ReadShort (&net_message);
+
+ MSG_ReadPos (&net_message, start);
+ MSG_ReadPos (&net_message, end);
+
+// override any beam with the same entity
+ for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+ if (b->entity == ent)
+ {
+ b->entity = ent;
+ b->model = model;
+ b->endtime = cl.time + 200;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ VectorClear (b->offset);
+ return ent;
+ }
+
+// find a free beam
+ for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+ {
+ if (!b->model || b->endtime < cl.time)
+ {
+ b->entity = ent;
+ b->model = model;
+ b->endtime = cl.time + 200;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ VectorClear (b->offset);
+ return ent;
+ }
+ }
+ Com_Printf ("beam list overflow!\n");
+ return ent;
+}
+
+/*
+=================
+CL_ParseBeam2
+=================
+*/
+int CL_ParseBeam2 (struct model_s *model)
+{
+ int ent;
+ vec3_t start, end, offset;
+ beam_t *b;
+ int i;
+
+ ent = MSG_ReadShort (&net_message);
+
+ MSG_ReadPos (&net_message, start);
+ MSG_ReadPos (&net_message, end);
+ MSG_ReadPos (&net_message, offset);
+
+// Com_Printf ("end- %f %f %f\n", end[0], end[1], end[2]);
+
+// override any beam with the same entity
+
+ for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+ if (b->entity == ent)
+ {
+ b->entity = ent;
+ b->model = model;
+ b->endtime = cl.time + 200;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ VectorCopy (offset, b->offset);
+ return ent;
+ }
+
+// find a free beam
+ for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+ {
+ if (!b->model || b->endtime < cl.time)
+ {
+ b->entity = ent;
+ b->model = model;
+ b->endtime = cl.time + 200;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ VectorCopy (offset, b->offset);
+ return ent;
+ }
+ }
+ Com_Printf ("beam list overflow!\n");
+ return ent;
+}
+
+// ROGUE
+/*
+=================
+CL_ParsePlayerBeam
+ - adds to the cl_playerbeam array instead of the cl_beams array
+=================
+*/
+int CL_ParsePlayerBeam (struct model_s *model)
+{
+ int ent;
+ vec3_t start, end, offset;
+ beam_t *b;
+ int i;
+
+ ent = MSG_ReadShort (&net_message);
+
+ MSG_ReadPos (&net_message, start);
+ MSG_ReadPos (&net_message, end);
+ // PMM - network optimization
+ if (model == cl_mod_heatbeam)
+ VectorSet(offset, 2, 7, -3);
+ else if (model == cl_mod_monster_heatbeam)
+ {
+ model = cl_mod_heatbeam;
+ VectorSet(offset, 0, 0, 0);
+ }
+ else
+ MSG_ReadPos (&net_message, offset);
+
+// Com_Printf ("end- %f %f %f\n", end[0], end[1], end[2]);
+
+// override any beam with the same entity
+// PMM - For player beams, we only want one per player (entity) so..
+ for (i=0, b=cl_playerbeams ; i< MAX_BEAMS ; i++, b++)
+ {
+ if (b->entity == ent)
+ {
+ b->entity = ent;
+ b->model = model;
+ b->endtime = cl.time + 200;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ VectorCopy (offset, b->offset);
+ return ent;
+ }
+ }
+
+// find a free beam
+ for (i=0, b=cl_playerbeams ; i< MAX_BEAMS ; i++, b++)
+ {
+ if (!b->model || b->endtime < cl.time)
+ {
+ b->entity = ent;
+ b->model = model;
+ b->endtime = cl.time + 100; // PMM - this needs to be 100 to prevent multiple heatbeams
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ VectorCopy (offset, b->offset);
+ return ent;
+ }
+ }
+ Com_Printf ("beam list overflow!\n");
+ return ent;
+}
+//rogue
+
+/*
+=================
+CL_ParseLightning
+=================
+*/
+int CL_ParseLightning (struct model_s *model)
+{
+ int srcEnt, destEnt;
+ vec3_t start, end;
+ beam_t *b;
+ int i;
+
+ srcEnt = MSG_ReadShort (&net_message);
+ destEnt = MSG_ReadShort (&net_message);
+
+ MSG_ReadPos (&net_message, start);
+ MSG_ReadPos (&net_message, end);
+
+// override any beam with the same source AND destination entities
+ for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+ if (b->entity == srcEnt && b->dest_entity == destEnt)
+ {
+// Com_Printf("%d: OVERRIDE %d -> %d\n", cl.time, srcEnt, destEnt);
+ b->entity = srcEnt;
+ b->dest_entity = destEnt;
+ b->model = model;
+ b->endtime = cl.time + 200;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ VectorClear (b->offset);
+ return srcEnt;
+ }
+
+// find a free beam
+ for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+ {
+ if (!b->model || b->endtime < cl.time)
+ {
+// Com_Printf("%d: NORMAL %d -> %d\n", cl.time, srcEnt, destEnt);
+ b->entity = srcEnt;
+ b->dest_entity = destEnt;
+ b->model = model;
+ b->endtime = cl.time + 200;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ VectorClear (b->offset);
+ return srcEnt;
+ }
+ }
+ Com_Printf ("beam list overflow!\n");
+ return srcEnt;
+}
+
+/*
+=================
+CL_ParseLaser
+=================
+*/
+void CL_ParseLaser (int colors)
+{
+ vec3_t start;
+ vec3_t end;
+ laser_t *l;
+ int i;
+
+ MSG_ReadPos (&net_message, start);
+ MSG_ReadPos (&net_message, end);
+
+ for (i=0, l=cl_lasers ; i< MAX_LASERS ; i++, l++)
+ {
+ if (l->endtime < cl.time)
+ {
+ l->ent.flags = RF_TRANSLUCENT | RF_BEAM;
+ VectorCopy (start, l->ent.origin);
+ VectorCopy (end, l->ent.oldorigin);
+ l->ent.alpha = 0.30;
+ l->ent.skinnum = (colors >> ((rand() % 4)*8)) & 0xff;
+ l->ent.model = NULL;
+ l->ent.frame = 4;
+ l->endtime = cl.time + 100;
+ return;
+ }
+ }
+}
+
+//=============
+//ROGUE
+void CL_ParseSteam (void)
+{
+ vec3_t pos, dir;
+ int id, i;
+ int r;
+ int cnt;
+ int color;
+ int magnitude;
+ cl_sustain_t *s, *free_sustain;
+
+ id = MSG_ReadShort (&net_message); // an id of -1 is an instant effect
+ if (id != -1) // sustains
+ {
+// Com_Printf ("Sustain effect id %d\n", id);
+ free_sustain = NULL;
+ for (i=0, s=cl_sustains; i<MAX_SUSTAINS; i++, s++)
+ {
+ if (s->id == 0)
+ {
+ free_sustain = s;
+ break;
+ }
+ }
+ if (free_sustain)
+ {
+ s->id = id;
+ s->count = MSG_ReadByte (&net_message);
+ MSG_ReadPos (&net_message, s->org);
+ MSG_ReadDir (&net_message, s->dir);
+ r = MSG_ReadByte (&net_message);
+ s->color = r & 0xff;
+ s->magnitude = MSG_ReadShort (&net_message);
+ s->endtime = cl.time + MSG_ReadLong (&net_message);
+ s->think = CL_ParticleSteamEffect2;
+ s->thinkinterval = 100;
+ s->nextthink = cl.time;
+ }
+ else
+ {
+// Com_Printf ("No free sustains!\n");
+ // FIXME - read the stuff anyway
+ cnt = MSG_ReadByte (&net_message);
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadDir (&net_message, dir);
+ r = MSG_ReadByte (&net_message);
+ magnitude = MSG_ReadShort (&net_message);
+ magnitude = MSG_ReadLong (&net_message); // really interval
+ }
+ }
+ else // instant
+ {
+ cnt = MSG_ReadByte (&net_message);
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadDir (&net_message, dir);
+ r = MSG_ReadByte (&net_message);
+ magnitude = MSG_ReadShort (&net_message);
+ color = r & 0xff;
+ CL_ParticleSteamEffect (pos, dir, color, cnt, magnitude);
+// S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
+ }
+}
+
+void CL_ParseWidow (void)
+{
+ vec3_t pos;
+ int id, i;
+ cl_sustain_t *s, *free_sustain;
+
+ id = MSG_ReadShort (&net_message);
+
+ free_sustain = NULL;
+ for (i=0, s=cl_sustains; i<MAX_SUSTAINS; i++, s++)
+ {
+ if (s->id == 0)
+ {
+ free_sustain = s;
+ break;
+ }
+ }
+ if (free_sustain)
+ {
+ s->id = id;
+ MSG_ReadPos (&net_message, s->org);
+ s->endtime = cl.time + 2100;
+ s->think = CL_Widowbeamout;
+ s->thinkinterval = 1;
+ s->nextthink = cl.time;
+ }
+ else // no free sustains
+ {
+ // FIXME - read the stuff anyway
+ MSG_ReadPos (&net_message, pos);
+ }
+}
+
+void CL_ParseNuke (void)
+{
+ vec3_t pos;
+ int i;
+ cl_sustain_t *s, *free_sustain;
+
+ free_sustain = NULL;
+ for (i=0, s=cl_sustains; i<MAX_SUSTAINS; i++, s++)
+ {
+ if (s->id == 0)
+ {
+ free_sustain = s;
+ break;
+ }
+ }
+ if (free_sustain)
+ {
+ s->id = 21000;
+ MSG_ReadPos (&net_message, s->org);
+ s->endtime = cl.time + 1000;
+ s->think = CL_Nukeblast;
+ s->thinkinterval = 1;
+ s->nextthink = cl.time;
+ }
+ else // no free sustains
+ {
+ // FIXME - read the stuff anyway
+ MSG_ReadPos (&net_message, pos);
+ }
+}
+
+//ROGUE
+//=============
+
+
+/*
+=================
+CL_ParseTEnt
+=================
+*/
+static byte splash_color[] = {0x00, 0xe0, 0xb0, 0x50, 0xd0, 0xe0, 0xe8};
+
+void CL_ParseTEnt (void)
+{
+ int type;
+ vec3_t pos, pos2, dir;
+ explosion_t *ex;
+ int cnt;
+ int color;
+ int r;
+ int ent;
+ int magnitude;
+
+ type = MSG_ReadByte (&net_message);
+
+ switch (type)
+ {
+ case TE_BLOOD: // bullet hitting flesh
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadDir (&net_message, dir);
+ CL_ParticleEffect (pos, dir, 0xe8, 60);
+ break;
+
+ case TE_GUNSHOT: // bullet hitting wall
+ case TE_SPARKS:
+ case TE_BULLET_SPARKS:
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadDir (&net_message, dir);
+ if (type == TE_GUNSHOT)
+ CL_ParticleEffect (pos, dir, 0, 40);
+ else
+ CL_ParticleEffect (pos, dir, 0xe0, 6);
+
+ if (type != TE_SPARKS)
+ {
+ CL_SmokeAndFlash(pos);
+
+ // impact sound
+ cnt = rand()&15;
+ if (cnt == 1)
+ S_StartSound (pos, 0, 0, cl_sfx_ric1, 1, ATTN_NORM, 0);
+ else if (cnt == 2)
+ S_StartSound (pos, 0, 0, cl_sfx_ric2, 1, ATTN_NORM, 0);
+ else if (cnt == 3)
+ S_StartSound (pos, 0, 0, cl_sfx_ric3, 1, ATTN_NORM, 0);
+ }
+
+ break;
+
+ case TE_SCREEN_SPARKS:
+ case TE_SHIELD_SPARKS:
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadDir (&net_message, dir);
+ if (type == TE_SCREEN_SPARKS)
+ CL_ParticleEffect (pos, dir, 0xd0, 40);
+ else
+ CL_ParticleEffect (pos, dir, 0xb0, 40);
+ //FIXME : replace or remove this sound
+ S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_SHOTGUN: // bullet hitting wall
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadDir (&net_message, dir);
+ CL_ParticleEffect (pos, dir, 0, 20);
+ CL_SmokeAndFlash(pos);
+ break;
+
+ case TE_SPLASH: // bullet hitting water
+ cnt = MSG_ReadByte (&net_message);
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadDir (&net_message, dir);
+ r = MSG_ReadByte (&net_message);
+ if (r > 6)
+ color = 0x00;
+ else
+ color = splash_color[r];
+ CL_ParticleEffect (pos, dir, color, cnt);
+
+ if (r == SPLASH_SPARKS)
+ {
+ r = rand() & 3;
+ if (r == 0)
+ S_StartSound (pos, 0, 0, cl_sfx_spark5, 1, ATTN_STATIC, 0);
+ else if (r == 1)
+ S_StartSound (pos, 0, 0, cl_sfx_spark6, 1, ATTN_STATIC, 0);
+ else
+ S_StartSound (pos, 0, 0, cl_sfx_spark7, 1, ATTN_STATIC, 0);
+ }
+ break;
+
+ case TE_LASER_SPARKS:
+ cnt = MSG_ReadByte (&net_message);
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadDir (&net_message, dir);
+ color = MSG_ReadByte (&net_message);
+ CL_ParticleEffect2 (pos, dir, color, cnt);
+ break;
+
+ // RAFAEL
+ case TE_BLUEHYPERBLASTER:
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadPos (&net_message, dir);
+ CL_BlasterParticles (pos, dir);
+ break;
+
+ case TE_BLASTER: // blaster hitting wall
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadDir (&net_message, dir);
+ CL_BlasterParticles (pos, dir);
+
+ ex = CL_AllocExplosion ();
+ VectorCopy (pos, ex->ent.origin);
+ ex->ent.angles[0] = acos(dir[2])/M_PI*180;
+ // PMM - fixed to correct for pitch of 0
+ if (dir[0])
+ ex->ent.angles[1] = atan2(dir[1], dir[0])/M_PI*180;
+ else if (dir[1] > 0)
+ ex->ent.angles[1] = 90;
+ else if (dir[1] < 0)
+ ex->ent.angles[1] = 270;
+ else
+ ex->ent.angles[1] = 0;
+
+ ex->type = ex_misc;
+ ex->ent.flags = RF_FULLBRIGHT|RF_TRANSLUCENT;
+ ex->start = cl.frame.servertime - 100;
+ ex->light = 150;
+ ex->lightcolor[0] = 1;
+ ex->lightcolor[1] = 1;
+ ex->ent.model = cl_mod_explode;
+ ex->frames = 4;
+ S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_RAILTRAIL: // railgun effect
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadPos (&net_message, pos2);
+ CL_RailTrail (pos, pos2);
+ S_StartSound (pos2, 0, 0, cl_sfx_railg, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_EXPLOSION2:
+ case TE_GRENADE_EXPLOSION:
+ case TE_GRENADE_EXPLOSION_WATER:
+ MSG_ReadPos (&net_message, pos);
+
+ ex = CL_AllocExplosion ();
+ VectorCopy (pos, ex->ent.origin);
+ ex->type = ex_poly;
+ ex->ent.flags = RF_FULLBRIGHT;
+ ex->start = cl.frame.servertime - 100;
+ ex->light = 350;
+ ex->lightcolor[0] = 1.0;
+ ex->lightcolor[1] = 0.5;
+ ex->lightcolor[2] = 0.5;
+ ex->ent.model = cl_mod_explo4;
+ ex->frames = 19;
+ ex->baseframe = 30;
+ ex->ent.angles[1] = rand() % 360;
+ CL_ExplosionParticles (pos);
+ if (type == TE_GRENADE_EXPLOSION_WATER)
+ S_StartSound (pos, 0, 0, cl_sfx_watrexp, 1, ATTN_NORM, 0);
+ else
+ S_StartSound (pos, 0, 0, cl_sfx_grenexp, 1, ATTN_NORM, 0);
+ break;
+
+ // RAFAEL
+ case TE_PLASMA_EXPLOSION:
+ MSG_ReadPos (&net_message, pos);
+ ex = CL_AllocExplosion ();
+ VectorCopy (pos, ex->ent.origin);
+ ex->type = ex_poly;
+ ex->ent.flags = RF_FULLBRIGHT;
+ ex->start = cl.frame.servertime - 100;
+ ex->light = 350;
+ ex->lightcolor[0] = 1.0;
+ ex->lightcolor[1] = 0.5;
+ ex->lightcolor[2] = 0.5;
+ ex->ent.angles[1] = rand() % 360;
+ ex->ent.model = cl_mod_explo4;
+ if (frand() < 0.5)
+ ex->baseframe = 15;
+ ex->frames = 15;
+ CL_ExplosionParticles (pos);
+ S_StartSound (pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_EXPLOSION1:
+ case TE_EXPLOSION1_BIG: // PMM
+ case TE_ROCKET_EXPLOSION:
+ case TE_ROCKET_EXPLOSION_WATER:
+ case TE_EXPLOSION1_NP: // PMM
+ MSG_ReadPos (&net_message, pos);
+
+ ex = CL_AllocExplosion ();
+ VectorCopy (pos, ex->ent.origin);
+ ex->type = ex_poly;
+ ex->ent.flags = RF_FULLBRIGHT;
+ ex->start = cl.frame.servertime - 100;
+ ex->light = 350;
+ ex->lightcolor[0] = 1.0;
+ ex->lightcolor[1] = 0.5;
+ ex->lightcolor[2] = 0.5;
+ ex->ent.angles[1] = rand() % 360;
+ if (type != TE_EXPLOSION1_BIG) // PMM
+ ex->ent.model = cl_mod_explo4; // PMM
+ else
+ ex->ent.model = cl_mod_explo4_big;
+ if (frand() < 0.5)
+ ex->baseframe = 15;
+ ex->frames = 15;
+ if ((type != TE_EXPLOSION1_BIG) && (type != TE_EXPLOSION1_NP)) // PMM
+ CL_ExplosionParticles (pos); // PMM
+ if (type == TE_ROCKET_EXPLOSION_WATER)
+ S_StartSound (pos, 0, 0, cl_sfx_watrexp, 1, ATTN_NORM, 0);
+ else
+ S_StartSound (pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_BFG_EXPLOSION:
+ MSG_ReadPos (&net_message, pos);
+ ex = CL_AllocExplosion ();
+ VectorCopy (pos, ex->ent.origin);
+ ex->type = ex_poly;
+ ex->ent.flags = RF_FULLBRIGHT;
+ ex->start = cl.frame.servertime - 100;
+ ex->light = 350;
+ ex->lightcolor[0] = 0.0;
+ ex->lightcolor[1] = 1.0;
+ ex->lightcolor[2] = 0.0;
+ ex->ent.model = cl_mod_bfg_explo;
+ ex->ent.flags |= RF_TRANSLUCENT;
+ ex->ent.alpha = 0.30;
+ ex->frames = 4;
+ break;
+
+ case TE_BFG_BIGEXPLOSION:
+ MSG_ReadPos (&net_message, pos);
+ CL_BFGExplosionParticles (pos);
+ break;
+
+ case TE_BFG_LASER:
+ CL_ParseLaser (0xd0d1d2d3);
+ break;
+
+ case TE_BUBBLETRAIL:
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadPos (&net_message, pos2);
+ CL_BubbleTrail (pos, pos2);
+ break;
+
+ case TE_PARASITE_ATTACK:
+ case TE_MEDIC_CABLE_ATTACK:
+ ent = CL_ParseBeam (cl_mod_parasite_segment);
+ break;
+
+ case TE_BOSSTPORT: // boss teleporting to station
+ MSG_ReadPos (&net_message, pos);
+ CL_BigTeleportParticles (pos);
+ S_StartSound (pos, 0, 0, S_RegisterSound ("misc/bigtele.wav"), 1, ATTN_NONE, 0);
+ break;
+
+ case TE_GRAPPLE_CABLE:
+ ent = CL_ParseBeam2 (cl_mod_grapple_cable);
+ break;
+
+ // RAFAEL
+ case TE_WELDING_SPARKS:
+ cnt = MSG_ReadByte (&net_message);
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadDir (&net_message, dir);
+ color = MSG_ReadByte (&net_message);
+ CL_ParticleEffect2 (pos, dir, color, cnt);
+
+ ex = CL_AllocExplosion ();
+ VectorCopy (pos, ex->ent.origin);
+ ex->type = ex_flash;
+ // note to self
+ // we need a better no draw flag
+ ex->ent.flags = RF_BEAM;
+ ex->start = cl.frame.servertime - 0.1;
+ ex->light = 100 + (rand()%75);
+ ex->lightcolor[0] = 1.0;
+ ex->lightcolor[1] = 1.0;
+ ex->lightcolor[2] = 0.3;
+ ex->ent.model = cl_mod_flash;
+ ex->frames = 2;
+ break;
+
+ case TE_GREENBLOOD:
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadDir (&net_message, dir);
+ CL_ParticleEffect2 (pos, dir, 0xdf, 30);
+ break;
+
+ // RAFAEL
+ case TE_TUNNEL_SPARKS:
+ cnt = MSG_ReadByte (&net_message);
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadDir (&net_message, dir);
+ color = MSG_ReadByte (&net_message);
+ CL_ParticleEffect3 (pos, dir, color, cnt);
+ break;
+
+//=============
+//PGM
+ // PMM -following code integrated for flechette (different color)
+ case TE_BLASTER2: // green blaster hitting wall
+ case TE_FLECHETTE: // flechette
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadDir (&net_message, dir);
+
+ // PMM
+ if (type == TE_BLASTER2)
+ CL_BlasterParticles2 (pos, dir, 0xd0);
+ else
+ CL_BlasterParticles2 (pos, dir, 0x6f); // 75
+
+ ex = CL_AllocExplosion ();
+ VectorCopy (pos, ex->ent.origin);
+ ex->ent.angles[0] = acos(dir[2])/M_PI*180;
+ // PMM - fixed to correct for pitch of 0
+ if (dir[0])
+ ex->ent.angles[1] = atan2(dir[1], dir[0])/M_PI*180;
+ else if (dir[1] > 0)
+ ex->ent.angles[1] = 90;
+ else if (dir[1] < 0)
+ ex->ent.angles[1] = 270;
+ else
+ ex->ent.angles[1] = 0;
+
+ ex->type = ex_misc;
+ ex->ent.flags = RF_FULLBRIGHT|RF_TRANSLUCENT;
+
+ // PMM
+ if (type == TE_BLASTER2)
+ ex->ent.skinnum = 1;
+ else // flechette
+ ex->ent.skinnum = 2;
+
+ ex->start = cl.frame.servertime - 100;
+ ex->light = 150;
+ // PMM
+ if (type == TE_BLASTER2)
+ ex->lightcolor[1] = 1;
+ else // flechette
+ {
+ ex->lightcolor[0] = 0.19;
+ ex->lightcolor[1] = 0.41;
+ ex->lightcolor[2] = 0.75;
+ }
+ ex->ent.model = cl_mod_explode;
+ ex->frames = 4;
+ S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
+ break;
+
+
+ case TE_LIGHTNING:
+ ent = CL_ParseLightning (cl_mod_lightning);
+ S_StartSound (NULL, ent, CHAN_WEAPON, cl_sfx_lightning, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_DEBUGTRAIL:
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadPos (&net_message, pos2);
+ CL_DebugTrail (pos, pos2);
+ break;
+
+ case TE_PLAIN_EXPLOSION:
+ MSG_ReadPos (&net_message, pos);
+
+ ex = CL_AllocExplosion ();
+ VectorCopy (pos, ex->ent.origin);
+ ex->type = ex_poly;
+ ex->ent.flags = RF_FULLBRIGHT;
+ ex->start = cl.frame.servertime - 100;
+ ex->light = 350;
+ ex->lightcolor[0] = 1.0;
+ ex->lightcolor[1] = 0.5;
+ ex->lightcolor[2] = 0.5;
+ ex->ent.angles[1] = rand() % 360;
+ ex->ent.model = cl_mod_explo4;
+ if (frand() < 0.5)
+ ex->baseframe = 15;
+ ex->frames = 15;
+ if (type == TE_ROCKET_EXPLOSION_WATER)
+ S_StartSound (pos, 0, 0, cl_sfx_watrexp, 1, ATTN_NORM, 0);
+ else
+ S_StartSound (pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_FLASHLIGHT:
+ MSG_ReadPos(&net_message, pos);
+ ent = MSG_ReadShort(&net_message);
+ CL_Flashlight(ent, pos);
+ break;
+
+ case TE_FORCEWALL:
+ MSG_ReadPos(&net_message, pos);
+ MSG_ReadPos(&net_message, pos2);
+ color = MSG_ReadByte (&net_message);
+ CL_ForceWall(pos, pos2, color);
+ break;
+
+ case TE_HEATBEAM:
+ ent = CL_ParsePlayerBeam (cl_mod_heatbeam);
+ break;
+
+ case TE_MONSTER_HEATBEAM:
+ ent = CL_ParsePlayerBeam (cl_mod_monster_heatbeam);
+ break;
+
+ case TE_HEATBEAM_SPARKS:
+// cnt = MSG_ReadByte (&net_message);
+ cnt = 50;
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadDir (&net_message, dir);
+// r = MSG_ReadByte (&net_message);
+// magnitude = MSG_ReadShort (&net_message);
+ r = 8;
+ magnitude = 60;
+ color = r & 0xff;
+ CL_ParticleSteamEffect (pos, dir, color, cnt, magnitude);
+ S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_HEATBEAM_STEAM:
+// cnt = MSG_ReadByte (&net_message);
+ cnt = 20;
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadDir (&net_message, dir);
+// r = MSG_ReadByte (&net_message);
+// magnitude = MSG_ReadShort (&net_message);
+// color = r & 0xff;
+ color = 0xe0;
+ magnitude = 60;
+ CL_ParticleSteamEffect (pos, dir, color, cnt, magnitude);
+ S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_STEAM:
+ CL_ParseSteam();
+ break;
+
+ case TE_BUBBLETRAIL2:
+// cnt = MSG_ReadByte (&net_message);
+ cnt = 8;
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadPos (&net_message, pos2);
+ CL_BubbleTrail2 (pos, pos2, cnt);
+ S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_MOREBLOOD:
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadDir (&net_message, dir);
+ CL_ParticleEffect (pos, dir, 0xe8, 250);
+ break;
+
+ case TE_CHAINFIST_SMOKE:
+ dir[0]=0; dir[1]=0; dir[2]=1;
+ MSG_ReadPos(&net_message, pos);
+ CL_ParticleSmokeEffect (pos, dir, 0, 20, 20);
+ break;
+
+ case TE_ELECTRIC_SPARKS:
+ MSG_ReadPos (&net_message, pos);
+ MSG_ReadDir (&net_message, dir);
+// CL_ParticleEffect (pos, dir, 109, 40);
+ CL_ParticleEffect (pos, dir, 0x75, 40);
+ //FIXME : replace or remove this sound
+ S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_TRACKER_EXPLOSION:
+ MSG_ReadPos (&net_message, pos);
+ CL_ColorFlash (pos, 0, 150, -1, -1, -1);
+ CL_ColorExplosionParticles (pos, 0, 1);
+// CL_Tracker_Explode (pos);
+ S_StartSound (pos, 0, 0, cl_sfx_disrexp, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_TELEPORT_EFFECT:
+ case TE_DBALL_GOAL:
+ MSG_ReadPos (&net_message, pos);
+ CL_TeleportParticles (pos);
+ break;
+
+ case TE_WIDOWBEAMOUT:
+ CL_ParseWidow ();
+ break;
+
+ case TE_NUKEBLAST:
+ CL_ParseNuke ();
+ break;
+
+ case TE_WIDOWSPLASH:
+ MSG_ReadPos (&net_message, pos);
+ CL_WidowSplash (pos);
+ break;
+//PGM
+//==============
+
+ default:
+ Com_Error (ERR_DROP, "CL_ParseTEnt: bad type");
+ }
+}
+
+/*
+=================
+CL_AddBeams
+=================
+*/
+void CL_AddBeams (void)
+{
+ int i,j;
+ beam_t *b;
+ vec3_t dist, org;
+ float d;
+ entity_t ent;
+ float yaw, pitch;
+ float forward;
+ float len, steps;
+ float model_length;
+
+// update beams
+ for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+ {
+ if (!b->model || b->endtime < cl.time)
+ continue;
+
+ // if coming from the player, update the start position
+ if (b->entity == cl.playernum+1) // entity 0 is the world
+ {
+ VectorCopy (cl.refdef.vieworg, b->start);
+ b->start[2] -= 22; // adjust for view height
+ }
+ VectorAdd (b->start, b->offset, org);
+
+ // calculate pitch and yaw
+ VectorSubtract (b->end, org, dist);
+
+ if (dist[1] == 0 && dist[0] == 0)
+ {
+ yaw = 0;
+ if (dist[2] > 0)
+ pitch = 90;
+ else
+ pitch = 270;
+ }
+ else
+ {
+ // PMM - fixed to correct for pitch of 0
+ if (dist[0])
+ yaw = (atan2(dist[1], dist[0]) * 180 / M_PI);
+ else if (dist[1] > 0)
+ yaw = 90;
+ else
+ yaw = 270;
+ if (yaw < 0)
+ yaw += 360;
+
+ forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);
+ pitch = (atan2(dist[2], forward) * -180.0 / M_PI);
+ if (pitch < 0)
+ pitch += 360.0;
+ }
+
+ // add new entities for the beams
+ d = VectorNormalize(dist);
+
+ memset (&ent, 0, sizeof(ent));
+ if (b->model == cl_mod_lightning)
+ {
+ model_length = 35.0;
+ d-= 20.0; // correction so it doesn't end in middle of tesla
+ }
+ else
+ {
+ model_length = 30.0;
+ }
+ steps = ceil(d/model_length);
+ len = (d-model_length)/(steps-1);
+
+ // PMM - special case for lightning model .. if the real length is shorter than the model,
+ // flip it around & draw it from the end to the start. This prevents the model from going
+ // through the tesla mine (instead it goes through the target)
+ if ((b->model == cl_mod_lightning) && (d <= model_length))
+ {
+// Com_Printf ("special case\n");
+ VectorCopy (b->end, ent.origin);
+ // offset to push beam outside of tesla model (negative because dist is from end to start
+ // for this beam)
+// for (j=0 ; j<3 ; j++)
+// ent.origin[j] -= dist[j]*10.0;
+ ent.model = b->model;
+ ent.flags = RF_FULLBRIGHT;
+ ent.angles[0] = pitch;
+ ent.angles[1] = yaw;
+ ent.angles[2] = rand()%360;
+ V_AddEntity (&ent);
+ return;
+ }
+ while (d > 0)
+ {
+ VectorCopy (org, ent.origin);
+ ent.model = b->model;
+ if (b->model == cl_mod_lightning)
+ {
+ ent.flags = RF_FULLBRIGHT;
+ ent.angles[0] = -pitch;
+ ent.angles[1] = yaw + 180.0;
+ ent.angles[2] = rand()%360;
+ }
+ else
+ {
+ ent.angles[0] = pitch;
+ ent.angles[1] = yaw;
+ ent.angles[2] = rand()%360;
+ }
+
+// Com_Printf("B: %d -> %d\n", b->entity, b->dest_entity);
+ V_AddEntity (&ent);
+
+ for (j=0 ; j<3 ; j++)
+ org[j] += dist[j]*len;
+ d -= model_length;
+ }
+ }
+}
+
+
+/*
+// Com_Printf ("Endpoint: %f %f %f\n", b->end[0], b->end[1], b->end[2]);
+// Com_Printf ("Pred View Angles: %f %f %f\n", cl.predicted_angles[0], cl.predicted_angles[1], cl.predicted_angles[2]);
+// Com_Printf ("Act View Angles: %f %f %f\n", cl.refdef.viewangles[0], cl.refdef.viewangles[1], cl.refdef.viewangles[2]);
+// VectorCopy (cl.predicted_origin, b->start);
+// b->start[2] += 22; // adjust for view height
+// if (fabs(cl.refdef.vieworg[2] - b->start[2]) >= 10) {
+// b->start[2] = cl.refdef.vieworg[2];
+// }
+
+// Com_Printf ("Time: %d %d %f\n", cl.time, cls.realtime, cls.frametime);
+*/
+
+extern cvar_t *hand;
+
+/*
+=================
+ROGUE - draw player locked beams
+CL_AddPlayerBeams
+=================
+*/
+void CL_AddPlayerBeams (void)
+{
+ int i,j;
+ beam_t *b;
+ vec3_t dist, org;
+ float d;
+ entity_t ent;
+ float yaw, pitch;
+ float forward;
+ float len, steps;
+ int framenum;
+ float model_length;
+
+ float hand_multiplier;
+ frame_t *oldframe;
+ player_state_t *ps, *ops;
+
+//PMM
+ if (hand)
+ {
+ if (hand->value == 2)
+ hand_multiplier = 0;
+ else if (hand->value == 1)
+ hand_multiplier = -1;
+ else
+ hand_multiplier = 1;
+ }
+ else
+ {
+ hand_multiplier = 1;
+ }
+//PMM
+
+// update beams
+ for (i=0, b=cl_playerbeams ; i< MAX_BEAMS ; i++, b++)
+ {
+ vec3_t f,r,u;
+ if (!b->model || b->endtime < cl.time)
+ continue;
+
+ if(cl_mod_heatbeam && (b->model == cl_mod_heatbeam))
+ {
+
+ // if coming from the player, update the start position
+ if (b->entity == cl.playernum+1) // entity 0 is the world
+ {
+ // set up gun position
+ // code straight out of CL_AddViewWeapon
+ ps = &cl.frame.playerstate;
+ j = (cl.frame.serverframe - 1) & UPDATE_MASK;
+ oldframe = &cl.frames[j];
+ if (oldframe->serverframe != cl.frame.serverframe-1 || !oldframe->valid)
+ oldframe = &cl.frame; // previous frame was dropped or involid
+ ops = &oldframe->playerstate;
+ for (j=0 ; j<3 ; j++)
+ {
+ b->start[j] = cl.refdef.vieworg[j] + ops->gunoffset[j]
+ + cl.lerpfrac * (ps->gunoffset[j] - ops->gunoffset[j]);
+ }
+ VectorMA (b->start, (hand_multiplier * b->offset[0]), cl.v_right, org);
+ VectorMA ( org, b->offset[1], cl.v_forward, org);
+ VectorMA ( org, b->offset[2], cl.v_up, org);
+ if ((hand) && (hand->value == 2)) {
+ VectorMA (org, -1, cl.v_up, org);
+ }
+ // FIXME - take these out when final
+ VectorCopy (cl.v_right, r);
+ VectorCopy (cl.v_forward, f);
+ VectorCopy (cl.v_up, u);
+
+ }
+ else
+ VectorCopy (b->start, org);
+ }
+ else
+ {
+ // if coming from the player, update the start position
+ if (b->entity == cl.playernum+1) // entity 0 is the world
+ {
+ VectorCopy (cl.refdef.vieworg, b->start);
+ b->start[2] -= 22; // adjust for view height
+ }
+ VectorAdd (b->start, b->offset, org);
+ }
+
+ // calculate pitch and yaw
+ VectorSubtract (b->end, org, dist);
+
+//PMM
+ if(cl_mod_heatbeam && (b->model == cl_mod_heatbeam) && (b->entity == cl.playernum+1))
+ {
+ vec_t len;
+
+ len = VectorLength (dist);
+ VectorScale (f, len, dist);
+ VectorMA (dist, (hand_multiplier * b->offset[0]), r, dist);
+ VectorMA (dist, b->offset[1], f, dist);
+ VectorMA (dist, b->offset[2], u, dist);
+ if ((hand) && (hand->value == 2)) {
+ VectorMA (org, -1, cl.v_up, org);
+ }
+ }
+//PMM
+
+ if (dist[1] == 0 && dist[0] == 0)
+ {
+ yaw = 0;
+ if (dist[2] > 0)
+ pitch = 90;
+ else
+ pitch = 270;
+ }
+ else
+ {
+ // PMM - fixed to correct for pitch of 0
+ if (dist[0])
+ yaw = (atan2(dist[1], dist[0]) * 180 / M_PI);
+ else if (dist[1] > 0)
+ yaw = 90;
+ else
+ yaw = 270;
+ if (yaw < 0)
+ yaw += 360;
+
+ forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);
+ pitch = (atan2(dist[2], forward) * -180.0 / M_PI);
+ if (pitch < 0)
+ pitch += 360.0;
+ }
+
+ if (cl_mod_heatbeam && (b->model == cl_mod_heatbeam))
+ {
+ if (b->entity != cl.playernum+1)
+ {
+ framenum = 2;
+// Com_Printf ("Third person\n");
+ ent.angles[0] = -pitch;
+ ent.angles[1] = yaw + 180.0;
+ ent.angles[2] = 0;
+// Com_Printf ("%f %f - %f %f %f\n", -pitch, yaw+180.0, b->offset[0], b->offset[1], b->offset[2]);
+ AngleVectors(ent.angles, f, r, u);
+
+ // if it's a non-origin offset, it's a player, so use the hardcoded player offset
+ if (!VectorCompare (b->offset, vec3_origin))
+ {
+ VectorMA (org, -(b->offset[0])+1, r, org);
+ VectorMA (org, -(b->offset[1]), f, org);
+ VectorMA (org, -(b->offset[2])-10, u, org);
+ }
+ else
+ {
+ // if it's a monster, do the particle effect
+ CL_MonsterPlasma_Shell(b->start);
+ }
+ }
+ else
+ {
+ framenum = 1;
+ }
+ }
+
+ // if it's the heatbeam, draw the particle effect
+ if ((cl_mod_heatbeam && (b->model == cl_mod_heatbeam) && (b->entity == cl.playernum+1)))
+ {
+ CL_Heatbeam (org, dist);
+ }
+
+ // add new entities for the beams
+ d = VectorNormalize(dist);
+
+ memset (&ent, 0, sizeof(ent));
+ if (b->model == cl_mod_heatbeam)
+ {
+ model_length = 32.0;
+ }
+ else if (b->model == cl_mod_lightning)
+ {
+ model_length = 35.0;
+ d-= 20.0; // correction so it doesn't end in middle of tesla
+ }
+ else
+ {
+ model_length = 30.0;
+ }
+ steps = ceil(d/model_length);
+ len = (d-model_length)/(steps-1);
+
+ // PMM - special case for lightning model .. if the real length is shorter than the model,
+ // flip it around & draw it from the end to the start. This prevents the model from going
+ // through the tesla mine (instead it goes through the target)
+ if ((b->model == cl_mod_lightning) && (d <= model_length))
+ {
+// Com_Printf ("special case\n");
+ VectorCopy (b->end, ent.origin);
+ // offset to push beam outside of tesla model (negative because dist is from end to start
+ // for this beam)
+// for (j=0 ; j<3 ; j++)
+// ent.origin[j] -= dist[j]*10.0;
+ ent.model = b->model;
+ ent.flags = RF_FULLBRIGHT;
+ ent.angles[0] = pitch;
+ ent.angles[1] = yaw;
+ ent.angles[2] = rand()%360;
+ V_AddEntity (&ent);
+ return;
+ }
+ while (d > 0)
+ {
+ VectorCopy (org, ent.origin);
+ ent.model = b->model;
+ if(cl_mod_heatbeam && (b->model == cl_mod_heatbeam))
+ {
+// ent.flags = RF_FULLBRIGHT|RF_TRANSLUCENT;
+// ent.alpha = 0.3;
+ ent.flags = RF_FULLBRIGHT;
+ ent.angles[0] = -pitch;
+ ent.angles[1] = yaw + 180.0;
+ ent.angles[2] = (cl.time) % 360;
+// ent.angles[2] = rand()%360;
+ ent.frame = framenum;
+ }
+ else if (b->model == cl_mod_lightning)
+ {
+ ent.flags = RF_FULLBRIGHT;
+ ent.angles[0] = -pitch;
+ ent.angles[1] = yaw + 180.0;
+ ent.angles[2] = rand()%360;
+ }
+ else
+ {
+ ent.angles[0] = pitch;
+ ent.angles[1] = yaw;
+ ent.angles[2] = rand()%360;
+ }
+
+// Com_Printf("B: %d -> %d\n", b->entity, b->dest_entity);
+ V_AddEntity (&ent);
+
+ for (j=0 ; j<3 ; j++)
+ org[j] += dist[j]*len;
+ d -= model_length;
+ }
+ }
+}
+
+/*
+=================
+CL_AddExplosions
+=================
+*/
+void CL_AddExplosions (void)
+{
+ entity_t *ent;
+ int i;
+ explosion_t *ex;
+ float frac;
+ int f;
+
+ memset (&ent, 0, sizeof(ent));
+
+ for (i=0, ex=cl_explosions ; i< MAX_EXPLOSIONS ; i++, ex++)
+ {
+ if (ex->type == ex_free)
+ continue;
+ frac = (cl.time - ex->start)/100.0;
+ f = floor(frac);
+
+ ent = &ex->ent;
+
+ switch (ex->type)
+ {
+ case ex_mflash:
+ if (f >= ex->frames-1)
+ ex->type = ex_free;
+ break;
+ case ex_misc:
+ if (f >= ex->frames-1)
+ {
+ ex->type = ex_free;
+ break;
+ }
+ ent->alpha = 1.0 - frac/(ex->frames-1);
+ break;
+ case ex_flash:
+ if (f >= 1)
+ {
+ ex->type = ex_free;
+ break;
+ }
+ ent->alpha = 1.0;
+ break;
+ case ex_poly:
+ if (f >= ex->frames-1)
+ {
+ ex->type = ex_free;
+ break;
+ }
+
+ ent->alpha = (16.0 - (float)f)/16.0;
+
+ if (f < 10)
+ {
+ ent->skinnum = (f>>1);
+ if (ent->skinnum < 0)
+ ent->skinnum = 0;
+ }
+ else
+ {
+ ent->flags |= RF_TRANSLUCENT;
+ if (f < 13)
+ ent->skinnum = 5;
+ else
+ ent->skinnum = 6;
+ }
+ break;
+ case ex_poly2:
+ if (f >= ex->frames-1)
+ {
+ ex->type = ex_free;
+ break;
+ }
+
+ ent->alpha = (5.0 - (float)f)/5.0;
+ ent->skinnum = 0;
+ ent->flags |= RF_TRANSLUCENT;
+ break;
+ }
+
+ if (ex->type == ex_free)
+ continue;
+ if (ex->light)
+ {
+ V_AddLight (ent->origin, ex->light*ent->alpha,
+ ex->lightcolor[0], ex->lightcolor[1], ex->lightcolor[2]);
+ }
+
+ VectorCopy (ent->origin, ent->oldorigin);
+
+ if (f < 0)
+ f = 0;
+ ent->frame = ex->baseframe + f + 1;
+ ent->oldframe = ex->baseframe + f;
+ ent->backlerp = 1.0 - cl.lerpfrac;
+
+ V_AddEntity (ent);
+ }
+}
+
+
+/*
+=================
+CL_AddLasers
+=================
+*/
+void CL_AddLasers (void)
+{
+ laser_t *l;
+ int i;
+
+ for (i=0, l=cl_lasers ; i< MAX_LASERS ; i++, l++)
+ {
+ if (l->endtime >= cl.time)
+ V_AddEntity (&l->ent);
+ }
+}
+
+/* PMM - CL_Sustains */
+void CL_ProcessSustain ()
+{
+ cl_sustain_t *s;
+ int i;
+
+ for (i=0, s=cl_sustains; i< MAX_SUSTAINS; i++, s++)
+ {
+ if (s->id)
+ if ((s->endtime >= cl.time) && (cl.time >= s->nextthink))
+ {
+// Com_Printf ("think %d %d %d\n", cl.time, s->nextthink, s->thinkinterval);
+ s->think (s);
+ }
+ else if (s->endtime < cl.time)
+ s->id = 0;
+ }
+}
+
+/*
+=================
+CL_AddTEnts
+=================
+*/
+void CL_AddTEnts (void)
+{
+ CL_AddBeams ();
+ // PMM - draw plasma beams
+ CL_AddPlayerBeams ();
+ CL_AddExplosions ();
+ CL_AddLasers ();
+ // PMM - set up sustain
+ CL_ProcessSustain();
+}
diff --git a/client/cl_view.c b/client/cl_view.c
new file mode 100644
index 0000000..4b75809
--- /dev/null
+++ b/client/cl_view.c
@@ -0,0 +1,584 @@
+/*
+Copyright (C) 1997-2001 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.
+
+*/
+// cl_view.c -- player rendering positioning
+
+#include "client.h"
+
+//=============
+//
+// development tools for weapons
+//
+int gun_frame;
+struct model_s *gun_model;
+
+//=============
+
+cvar_t *crosshair;
+cvar_t *cl_testparticles;
+cvar_t *cl_testentities;
+cvar_t *cl_testlights;
+cvar_t *cl_testblend;
+
+cvar_t *cl_stats;
+
+
+int r_numdlights;
+dlight_t r_dlights[MAX_DLIGHTS];
+
+int r_numentities;
+entity_t r_entities[MAX_ENTITIES];
+
+int r_numparticles;
+particle_t r_particles[MAX_PARTICLES];
+
+lightstyle_t r_lightstyles[MAX_LIGHTSTYLES];
+
+char cl_weaponmodels[MAX_CLIENTWEAPONMODELS][MAX_QPATH];
+int num_cl_weaponmodels;
+
+/*
+====================
+V_ClearScene
+
+Specifies the model that will be used as the world
+====================
+*/
+void V_ClearScene (void)
+{
+ r_numdlights = 0;
+ r_numentities = 0;
+ r_numparticles = 0;
+}
+
+
+/*
+=====================
+V_AddEntity
+
+=====================
+*/
+void V_AddEntity (entity_t *ent)
+{
+ if (r_numentities >= MAX_ENTITIES)
+ return;
+ r_entities[r_numentities++] = *ent;
+}
+
+
+/*
+=====================
+V_AddParticle
+
+=====================
+*/
+void V_AddParticle (vec3_t org, int color, float alpha)
+{
+ particle_t *p;
+
+ if (r_numparticles >= MAX_PARTICLES)
+ return;
+ p = &r_particles[r_numparticles++];
+ VectorCopy (org, p->origin);
+ p->color = color;
+ p->alpha = alpha;
+}
+
+/*
+=====================
+V_AddLight
+
+=====================
+*/
+void V_AddLight (vec3_t org, float intensity, float r, float g, float b)
+{
+ dlight_t *dl;
+
+ if (r_numdlights >= MAX_DLIGHTS)
+ return;
+ dl = &r_dlights[r_numdlights++];
+ VectorCopy (org, dl->origin);
+ dl->intensity = intensity;
+ dl->color[0] = r;
+ dl->color[1] = g;
+ dl->color[2] = b;
+}
+
+
+/*
+=====================
+V_AddLightStyle
+
+=====================
+*/
+void V_AddLightStyle (int style, float r, float g, float b)
+{
+ lightstyle_t *ls;
+
+ if (style < 0 || style > MAX_LIGHTSTYLES)
+ Com_Error (ERR_DROP, "Bad light style %i", style);
+ ls = &r_lightstyles[style];
+
+ ls->white = r+g+b;
+ ls->rgb[0] = r;
+ ls->rgb[1] = g;
+ ls->rgb[2] = b;
+}
+
+/*
+================
+V_TestParticles
+
+If cl_testparticles is set, create 4096 particles in the view
+================
+*/
+void V_TestParticles (void)
+{
+ particle_t *p;
+ int i, j;
+ float d, r, u;
+
+ r_numparticles = MAX_PARTICLES;
+ for (i=0 ; i<r_numparticles ; i++)
+ {
+ d = i*0.25;
+ r = 4*((i&7)-3.5);
+ u = 4*(((i>>3)&7)-3.5);
+ p = &r_particles[i];
+
+ for (j=0 ; j<3 ; j++)
+ p->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j]*d +
+ cl.v_right[j]*r + cl.v_up[j]*u;
+
+ p->color = 8;
+ p->alpha = cl_testparticles->value;
+ }
+}
+
+/*
+================
+V_TestEntities
+
+If cl_testentities is set, create 32 player models
+================
+*/
+void V_TestEntities (void)
+{
+ int i, j;
+ float f, r;
+ entity_t *ent;
+
+ r_numentities = 32;
+ memset (r_entities, 0, sizeof(r_entities));
+
+ for (i=0 ; i<r_numentities ; i++)
+ {
+ ent = &r_entities[i];
+
+ r = 64 * ( (i%4) - 1.5 );
+ f = 64 * (i/4) + 128;
+
+ for (j=0 ; j<3 ; j++)
+ ent->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j]*f +
+ cl.v_right[j]*r;
+
+ ent->model = cl.baseclientinfo.model;
+ ent->skin = cl.baseclientinfo.skin;
+ }
+}
+
+/*
+================
+V_TestLights
+
+If cl_testlights is set, create 32 lights models
+================
+*/
+void V_TestLights (void)
+{
+ int i, j;
+ float f, r;
+ dlight_t *dl;
+
+ r_numdlights = 32;
+ memset (r_dlights, 0, sizeof(r_dlights));
+
+ for (i=0 ; i<r_numdlights ; i++)
+ {
+ dl = &r_dlights[i];
+
+ r = 64 * ( (i%4) - 1.5 );
+ f = 64 * (i/4) + 128;
+
+ for (j=0 ; j<3 ; j++)
+ dl->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j]*f +
+ cl.v_right[j]*r;
+ dl->color[0] = ((i%6)+1) & 1;
+ dl->color[1] = (((i%6)+1) & 2)>>1;
+ dl->color[2] = (((i%6)+1) & 4)>>2;
+ dl->intensity = 200;
+ }
+}
+
+//===================================================================
+
+/*
+=================
+CL_PrepRefresh
+
+Call before entering a new level, or after changing dlls
+=================
+*/
+void CL_PrepRefresh (void)
+{
+ char mapname[32];
+ int i;
+ char name[MAX_QPATH];
+ float rotate;
+ vec3_t axis;
+
+ if (!cl.configstrings[CS_MODELS+1][0])
+ return; // no map loaded
+
+ SCR_AddDirtyPoint (0, 0);
+ SCR_AddDirtyPoint (viddef.width-1, viddef.height-1);
+
+ // let the render dll load the map
+ strcpy (mapname, cl.configstrings[CS_MODELS+1] + 5); // skip "maps/"
+ mapname[strlen(mapname)-4] = 0; // cut off ".bsp"
+
+ // register models, pics, and skins
+ Com_Printf ("Map: %s\r", mapname);
+ SCR_UpdateScreen ();
+ re.BeginRegistration (mapname);
+ Com_Printf (" \r");
+
+ // precache status bar pics
+ Com_Printf ("pics\r");
+ SCR_UpdateScreen ();
+ SCR_TouchPics ();
+ Com_Printf (" \r");
+
+ CL_RegisterTEntModels ();
+
+ num_cl_weaponmodels = 1;
+ strcpy(cl_weaponmodels[0], "weapon.md2");
+
+ for (i=1 ; i<MAX_MODELS && cl.configstrings[CS_MODELS+i][0] ; i++)
+ {
+ strcpy (name, cl.configstrings[CS_MODELS+i]);
+ name[37] = 0; // never go beyond one line
+ if (name[0] != '*')
+ Com_Printf ("%s\r", name);
+ SCR_UpdateScreen ();
+ Sys_SendKeyEvents (); // pump message loop
+ if (name[0] == '#')
+ {
+ // special player weapon model
+ if (num_cl_weaponmodels < MAX_CLIENTWEAPONMODELS)
+ {
+ strncpy(cl_weaponmodels[num_cl_weaponmodels], cl.configstrings[CS_MODELS+i]+1,
+ sizeof(cl_weaponmodels[num_cl_weaponmodels]) - 1);
+ num_cl_weaponmodels++;
+ }
+ }
+ else
+ {
+ cl.model_draw[i] = re.RegisterModel (cl.configstrings[CS_MODELS+i]);
+ if (name[0] == '*')
+ cl.model_clip[i] = CM_InlineModel (cl.configstrings[CS_MODELS+i]);
+ else
+ cl.model_clip[i] = NULL;
+ }
+ if (name[0] != '*')
+ Com_Printf (" \r");
+ }
+
+ Com_Printf ("images\r", i);
+ SCR_UpdateScreen ();
+ for (i=1 ; i<MAX_IMAGES && cl.configstrings[CS_IMAGES+i][0] ; i++)
+ {
+ cl.image_precache[i] = re.RegisterPic (cl.configstrings[CS_IMAGES+i]);
+ Sys_SendKeyEvents (); // pump message loop
+ }
+
+ Com_Printf (" \r");
+ for (i=0 ; i<MAX_CLIENTS ; i++)
+ {
+ if (!cl.configstrings[CS_PLAYERSKINS+i][0])
+ continue;
+ Com_Printf ("client %i\r", i);
+ SCR_UpdateScreen ();
+ Sys_SendKeyEvents (); // pump message loop
+ CL_ParseClientinfo (i);
+ Com_Printf (" \r");
+ }
+
+ CL_LoadClientinfo (&cl.baseclientinfo, "unnamed\\male/grunt");
+
+ // set sky textures and speed
+ Com_Printf ("sky\r", i);
+ SCR_UpdateScreen ();
+ rotate = atof (cl.configstrings[CS_SKYROTATE]);
+ sscanf (cl.configstrings[CS_SKYAXIS], "%f %f %f",
+ &axis[0], &axis[1], &axis[2]);
+ re.SetSky (cl.configstrings[CS_SKY], rotate, axis);
+ Com_Printf (" \r");
+
+ // the renderer can now free unneeded stuff
+ re.EndRegistration ();
+
+ // clear any lines of console text
+ Con_ClearNotify ();
+
+ SCR_UpdateScreen ();
+ cl.refresh_prepped = true;
+ cl.force_refdef = true; // make sure we have a valid refdef
+
+ // start the cd track
+ CDAudio_Play (atoi(cl.configstrings[CS_CDTRACK]), true);
+}
+
+/*
+====================
+CalcFov
+====================
+*/
+float CalcFov (float fov_x, float width, float height)
+{
+ float a;
+ float x;
+
+ if (fov_x < 1 || fov_x > 179)
+ Com_Error (ERR_DROP, "Bad fov: %f", fov_x);
+
+ x = width/tan(fov_x/360*M_PI);
+
+ a = atan (height/x);
+
+ a = a*360/M_PI;
+
+ return a;
+}
+
+//============================================================================
+
+// gun frame debugging functions
+void V_Gun_Next_f (void)
+{
+ gun_frame++;
+ Com_Printf ("frame %i\n", gun_frame);
+}
+
+void V_Gun_Prev_f (void)
+{
+ gun_frame--;
+ if (gun_frame < 0)
+ gun_frame = 0;
+ Com_Printf ("frame %i\n", gun_frame);
+}
+
+void V_Gun_Model_f (void)
+{
+ char name[MAX_QPATH];
+
+ if (Cmd_Argc() != 2)
+ {
+ gun_model = NULL;
+ return;
+ }
+ Com_sprintf (name, sizeof(name), "models/%s/tris.md2", Cmd_Argv(1));
+ gun_model = re.RegisterModel (name);
+}
+
+//============================================================================
+
+
+/*
+=================
+SCR_DrawCrosshair
+=================
+*/
+void SCR_DrawCrosshair (void)
+{
+ if (!crosshair->value)
+ return;
+
+ if (crosshair->modified)
+ {
+ crosshair->modified = false;
+ SCR_TouchPics ();
+ }
+
+ if (!crosshair_pic[0])
+ return;
+
+ re.DrawPic (scr_vrect.x + ((scr_vrect.width - crosshair_width)>>1)
+ , scr_vrect.y + ((scr_vrect.height - crosshair_height)>>1), crosshair_pic);
+}
+
+/*
+==================
+V_RenderView
+
+==================
+*/
+void V_RenderView( float stereo_separation )
+{
+ extern int entitycmpfnc( const entity_t *, const entity_t * );
+
+ if (cls.state != ca_active)
+ return;
+
+ if (!cl.refresh_prepped)
+ return; // still loading
+
+ if (cl_timedemo->value)
+ {
+ if (!cl.timedemo_start)
+ cl.timedemo_start = Sys_Milliseconds ();
+ cl.timedemo_frames++;
+ }
+
+ // an invalid frame will just use the exact previous refdef
+ // we can't use the old frame if the video mode has changed, though...
+ if ( cl.frame.valid && (cl.force_refdef || !cl_paused->value) )
+ {
+ cl.force_refdef = false;
+
+ V_ClearScene ();
+
+ // build a refresh entity list and calc cl.sim*
+ // this also calls CL_CalcViewValues which loads
+ // v_forward, etc.
+ CL_AddEntities ();
+
+ if (cl_testparticles->value)
+ V_TestParticles ();
+ if (cl_testentities->value)
+ V_TestEntities ();
+ if (cl_testlights->value)
+ V_TestLights ();
+ if (cl_testblend->value)
+ {
+ cl.refdef.blend[0] = 1;
+ cl.refdef.blend[1] = 0.5;
+ cl.refdef.blend[2] = 0.25;
+ cl.refdef.blend[3] = 0.5;
+ }
+
+ // offset vieworg appropriately if we're doing stereo separation
+ if ( stereo_separation != 0 )
+ {
+ vec3_t tmp;
+
+ VectorScale( cl.v_right, stereo_separation, tmp );
+ VectorAdd( cl.refdef.vieworg, tmp, cl.refdef.vieworg );
+ }
+
+ // never let it sit exactly on a node line, because a water plane can
+ // dissapear when viewed with the eye exactly on it.
+ // the server protocol only specifies to 1/8 pixel, so add 1/16 in each axis
+ cl.refdef.vieworg[0] += 1.0/16;
+ cl.refdef.vieworg[1] += 1.0/16;
+ cl.refdef.vieworg[2] += 1.0/16;
+
+ cl.refdef.x = scr_vrect.x;
+ cl.refdef.y = scr_vrect.y;
+ cl.refdef.width = scr_vrect.width;
+ cl.refdef.height = scr_vrect.height;
+ cl.refdef.fov_y = CalcFov (cl.refdef.fov_x, cl.refdef.width, cl.refdef.height);
+ cl.refdef.time = cl.time*0.001;
+
+ cl.refdef.areabits = cl.frame.areabits;
+
+ if (!cl_add_entities->value)
+ r_numentities = 0;
+ if (!cl_add_particles->value)
+ r_numparticles = 0;
+ if (!cl_add_lights->value)
+ r_numdlights = 0;
+ if (!cl_add_blend->value)
+ {
+ VectorClear (cl.refdef.blend);
+ }
+
+ cl.refdef.num_entities = r_numentities;
+ cl.refdef.entities = r_entities;
+ cl.refdef.num_particles = r_numparticles;
+ cl.refdef.particles = r_particles;
+ cl.refdef.num_dlights = r_numdlights;
+ cl.refdef.dlights = r_dlights;
+ cl.refdef.lightstyles = r_lightstyles;
+
+ cl.refdef.rdflags = cl.frame.playerstate.rdflags;
+
+ // sort entities for better cache locality
+ qsort( cl.refdef.entities, cl.refdef.num_entities, sizeof( cl.refdef.entities[0] ), (int (*)(const void *, const void *))entitycmpfnc );
+ }
+
+ re.RenderFrame (&cl.refdef);
+ if (cl_stats->value)
+ Com_Printf ("ent:%i lt:%i part:%i\n", r_numentities, r_numdlights, r_numparticles);
+ if ( log_stats->value && ( log_stats_file != 0 ) )
+ fprintf( 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,
+ scr_vrect.y+scr_vrect.height-1);
+
+ SCR_DrawCrosshair ();
+}
+
+
+/*
+=============
+V_Viewpos_f
+=============
+*/
+void V_Viewpos_f (void)
+{
+ Com_Printf ("(%i %i %i) : %i\n", (int)cl.refdef.vieworg[0],
+ (int)cl.refdef.vieworg[1], (int)cl.refdef.vieworg[2],
+ (int)cl.refdef.viewangles[YAW]);
+}
+
+/*
+=============
+V_Init
+=============
+*/
+void V_Init (void)
+{
+ Cmd_AddCommand ("gun_next", V_Gun_Next_f);
+ Cmd_AddCommand ("gun_prev", V_Gun_Prev_f);
+ Cmd_AddCommand ("gun_model", V_Gun_Model_f);
+
+ Cmd_AddCommand ("viewpos", V_Viewpos_f);
+
+ crosshair = Cvar_Get ("crosshair", "0", CVAR_ARCHIVE);
+
+ cl_testblend = Cvar_Get ("cl_testblend", "0", 0);
+ cl_testparticles = Cvar_Get ("cl_testparticles", "0", 0);
+ cl_testentities = Cvar_Get ("cl_testentities", "0", 0);
+ cl_testlights = Cvar_Get ("cl_testlights", "0", 0);
+
+ cl_stats = Cvar_Get ("cl_stats", "0", 0);
+}
diff --git a/client/client.h b/client/client.h
new file mode 100644
index 0000000..2dc57a2
--- /dev/null
+++ b/client/client.h
@@ -0,0 +1,584 @@
+/*
+Copyright (C) 1997-2001 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.
+
+*/
+// client.h -- primary header for client
+
+//define PARANOID // speed sapping error checking
+
+#include <math.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ref.h"
+
+#include "vid.h"
+#include "screen.h"
+#include "sound.h"
+#include "input.h"
+#include "keys.h"
+#include "console.h"
+#include "cdaudio.h"
+
+//=============================================================================
+
+typedef struct
+{
+ qboolean 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
+ player_state_t playerstate;
+ 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 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
+ vec3_t lerp_origin; // for trails (variable hz)
+
+ int fly_stoptime;
+} centity_t;
+
+#define MAX_CLIENTWEAPONMODELS 20 // PGM -- upped from 16 to fit the chainfist vwep
+
+typedef struct
+{
+ char name[MAX_QPATH];
+ char cinfo[MAX_QPATH];
+ struct image_s *skin;
+ struct image_s *icon;
+ char iconname[MAX_QPATH];
+ struct model_s *model;
+ struct model_s *weaponmodel[MAX_CLIENTWEAPONMODELS];
+} clientinfo_t;
+
+extern char cl_weaponmodels[MAX_CLIENTWEAPONMODELS][MAX_QPATH];
+extern int num_cl_weaponmodels;
+
+#define CMD_BACKUP 64 // allow a lot of command backups for very fast systems
+
+//
+// the client_state_t structure is wiped completely at every
+// server map change
+//
+typedef struct
+{
+ int timeoutcount;
+
+ int timedemo_frames;
+ int timedemo_start;
+
+ qboolean refresh_prepped; // false if on new level or new ref dll
+ qboolean sound_prepped; // ambient sounds can start
+ qboolean force_refdef; // vid has changed, so we can't use a paused refdef
+
+ 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
+ short predicted_origins[CMD_BACKUP][3]; // for debug comparing against server
+
+ float predicted_step; // for stair up smoothing
+ unsigned predicted_step_time;
+
+ vec3_t predicted_origin; // generated by CL_PredictMovement
+ vec3_t predicted_angles;
+ vec3_t prediction_error;
+
+ frame_t frame; // received from server
+ int surpressCount; // number of messages rate supressed
+ frame_t frames[UPDATE_BACKUP];
+
+ // the client maintains its own idea of view angles, which are
+ // sent to the server each frame. It is cleared to 0 upon entering each level.
+ // the server sends a delta each frame which is added to the locally
+ // tracked view angles to account for standing on rotating objects,
+ // and teleport direction changes
+ vec3_t viewangles;
+
+ int time; // this is the time value that the client
+ // is rendering at. always <= cls.realtime
+ float lerpfrac; // between oldframe and frame
+
+ refdef_t refdef;
+
+ vec3_t v_forward, v_right, v_up; // set when refdef.angles is set
+
+ //
+ // transient data from server
+ //
+ char layout[1024]; // general 2D overlay
+ int inventory[MAX_ITEMS];
+
+ //
+ // non-gameserver infornamtion
+ // FIXME: move this cinematic stuff into the cin_t structure
+ FILE *cinematic_file;
+ int cinematictime; // cls.realtime for first cinematic frame
+ int cinematicframe;
+ char cinematicpalette[768];
+ qboolean cinematicpalette_active;
+
+ //
+ // server state information
+ //
+ qboolean attractloop; // running the attract loop, any key will menu
+ int servercount; // server identification for prespawns
+ char gamedir[MAX_QPATH];
+ int playernum;
+
+ char configstrings[MAX_CONFIGSTRINGS][MAX_QPATH];
+
+ //
+ // locally derived information from server state
+ //
+ struct model_s *model_draw[MAX_MODELS];
+ struct cmodel_s *model_clip[MAX_MODELS];
+
+ struct sfx_s *sound_precache[MAX_SOUNDS];
+ struct image_s *image_precache[MAX_IMAGES];
+
+ clientinfo_t clientinfo[MAX_CLIENTS];
+ clientinfo_t baseclientinfo;
+} client_state_t;
+
+extern client_state_t cl;
+
+/*
+==================================================================
+
+the client_static_t structure is persistant through an arbitrary number
+of server connections
+
+==================================================================
+*/
+
+typedef enum {
+ ca_uninitialized,
+ ca_disconnected, // not talking to a server
+ ca_connecting, // sending request packets to the server
+ ca_connected, // netchan_t established, waiting for svc_serverdata
+ ca_active // game views should be displayed
+} connstate_t;
+
+typedef enum {
+ dl_none,
+ dl_model,
+ dl_sound,
+ dl_skin,
+ dl_single
+} dltype_t; // download type
+
+typedef enum {key_game, key_console, key_message, key_menu} keydest_t;
+
+typedef struct
+{
+ connstate_t state;
+ keydest_t key_dest;
+
+ int framecount;
+ int realtime; // always increasing, no clamping, etc
+ float frametime; // seconds since last frame
+
+// screen rendering information
+ 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
+ // > cls.disable_servercount, clear disable_screen
+
+// connection information
+ char servername[MAX_OSPATH]; // name of server from original connect
+ float connect_time; // for connection retransmits
+
+ int quakePort; // a 16 bit value that allows quake servers
+ // to work around address translating routers
+ netchan_t netchan;
+ int serverProtocol; // in case we are doing some kind of version hack
+
+ int challenge; // from the server to use for connecting
+
+ FILE *download; // file transfer from server
+ char downloadtempname[MAX_OSPATH];
+ char downloadname[MAX_OSPATH];
+ int downloadnumber;
+ dltype_t downloadtype;
+ int downloadpercent;
+
+// demo recording info must be here, so it isn't cleared on level change
+ qboolean demorecording;
+ qboolean demowaiting; // don't record until a non-delta message is received
+ FILE *demofile;
+} client_static_t;
+
+extern client_static_t cls;
+
+//=============================================================================
+
+//
+// cvars
+//
+extern cvar_t *cl_stereo_separation;
+extern cvar_t *cl_stereo;
+
+extern cvar_t *cl_gun;
+extern cvar_t *cl_add_blend;
+extern cvar_t *cl_add_lights;
+extern cvar_t *cl_add_particles;
+extern cvar_t *cl_add_entities;
+extern cvar_t *cl_predict;
+extern cvar_t *cl_footsteps;
+extern cvar_t *cl_noskins;
+extern cvar_t *cl_autoskins;
+
+extern cvar_t *cl_upspeed;
+extern cvar_t *cl_forwardspeed;
+extern cvar_t *cl_sidespeed;
+
+extern cvar_t *cl_yawspeed;
+extern cvar_t *cl_pitchspeed;
+
+extern cvar_t *cl_run;
+
+extern cvar_t *cl_anglespeedkey;
+
+extern cvar_t *cl_shownet;
+extern cvar_t *cl_showmiss;
+extern cvar_t *cl_showclamp;
+
+extern cvar_t *lookspring;
+extern cvar_t *lookstrafe;
+extern cvar_t *sensitivity;
+
+extern cvar_t *m_pitch;
+extern cvar_t *m_yaw;
+extern cvar_t *m_forward;
+extern cvar_t *m_side;
+
+extern cvar_t *freelook;
+
+extern cvar_t *cl_lightlevel; // FIXME HACK
+
+extern cvar_t *cl_paused;
+extern cvar_t *cl_timedemo;
+
+extern cvar_t *cl_vwep;
+
+typedef struct
+{
+ int key; // so entities can reuse same entry
+ vec3_t color;
+ vec3_t origin;
+ float radius;
+ float die; // stop lighting after this time
+ float decay; // drop this each second
+ float minlight; // don't add when contributing less
+} cdlight_t;
+
+extern centity_t cl_entities[MAX_EDICTS];
+extern cdlight_t cl_dlights[MAX_DLIGHTS];
+
+// the cl_parse_entities must be large enough to hold UPDATE_BACKUP frames of
+// entities, so that when a delta compressed message arives from the server
+// it can be un-deltad from the original
+#define MAX_PARSE_ENTITIES 1024
+extern entity_state_t cl_parse_entities[MAX_PARSE_ENTITIES];
+
+//=============================================================================
+
+extern netadr_t net_from;
+extern sizebuf_t net_message;
+
+void DrawString (int x, int y, char *s);
+void DrawAltString (int x, int y, char *s); // toggle high bit
+qboolean CL_CheckOrDownloadFile (char *filename);
+
+void CL_AddNetgraph (void);
+
+//ROGUE
+typedef struct cl_sustain
+{
+ int id;
+ int type;
+ int endtime;
+ int nextthink;
+ int thinkinterval;
+ vec3_t org;
+ vec3_t dir;
+ int color;
+ int count;
+ int magnitude;
+ void (*think)(struct cl_sustain *self);
+} cl_sustain_t;
+
+#define MAX_SUSTAINS 32
+void CL_ParticleSteamEffect2(cl_sustain_t *self);
+
+void CL_TeleporterParticles (entity_state_t *ent);
+void CL_ParticleEffect (vec3_t org, vec3_t dir, int color, int count);
+void CL_ParticleEffect2 (vec3_t org, vec3_t dir, int color, int count);
+
+// RAFAEL
+void CL_ParticleEffect3 (vec3_t org, vec3_t dir, int color, int count);
+
+
+//=================================================
+
+// ========
+// PGM
+typedef struct particle_s
+{
+ struct particle_s *next;
+
+ float time;
+
+ vec3_t org;
+ vec3_t vel;
+ vec3_t accel;
+ float color;
+ float colorvel;
+ float alpha;
+ float alphavel;
+} cparticle_t;
+
+
+#define PARTICLE_GRAVITY 40
+#define BLASTER_PARTICLE_COLOR 0xe0
+// PMM
+#define INSTANT_PARTICLE -10000.0
+// PGM
+// ========
+
+void CL_ClearEffects (void);
+void CL_ClearTEnts (void);
+void CL_BlasterTrail (vec3_t start, vec3_t end);
+void CL_QuadTrail (vec3_t start, vec3_t end);
+void CL_RailTrail (vec3_t start, vec3_t end);
+void CL_BubbleTrail (vec3_t start, vec3_t end);
+void CL_FlagTrail (vec3_t start, vec3_t end, float color);
+
+// RAFAEL
+void CL_IonripperTrail (vec3_t start, vec3_t end);
+
+// ========
+// PGM
+void CL_BlasterParticles2 (vec3_t org, vec3_t dir, unsigned int color);
+void CL_BlasterTrail2 (vec3_t start, vec3_t end);
+void CL_DebugTrail (vec3_t start, vec3_t end);
+void CL_SmokeTrail (vec3_t start, vec3_t end, int colorStart, int colorRun, int spacing);
+void CL_Flashlight (int ent, vec3_t pos);
+void CL_ForceWall (vec3_t start, vec3_t end, int color);
+void CL_FlameEffects (centity_t *ent, vec3_t origin);
+void CL_GenericParticleEffect (vec3_t org, vec3_t dir, int color, int count, int numcolors, int dirspread, float alphavel);
+void CL_BubbleTrail2 (vec3_t start, vec3_t end, int dist);
+void CL_Heatbeam (vec3_t start, vec3_t end);
+void CL_ParticleSteamEffect (vec3_t org, vec3_t dir, int color, int count, int magnitude);
+void CL_TrackerTrail (vec3_t start, vec3_t end, int particleColor);
+void CL_Tracker_Explode(vec3_t origin);
+void CL_TagTrail (vec3_t start, vec3_t end, float color);
+void CL_ColorFlash (vec3_t pos, int ent, int intensity, float r, float g, float b);
+void CL_Tracker_Shell(vec3_t origin);
+void CL_MonsterPlasma_Shell(vec3_t origin);
+void CL_ColorExplosionParticles (vec3_t org, int color, int run);
+void CL_ParticleSmokeEffect (vec3_t org, vec3_t dir, int color, int count, int magnitude);
+void CL_Widowbeamout (cl_sustain_t *self);
+void CL_Nukeblast (cl_sustain_t *self);
+void CL_WidowSplash (vec3_t org);
+// PGM
+// ========
+
+int CL_ParseEntityBits (unsigned *bits);
+void CL_ParseDelta (entity_state_t *from, entity_state_t *to, int number, int bits);
+void CL_ParseFrame (void);
+
+void CL_ParseTEnt (void);
+void CL_ParseConfigString (void);
+void CL_ParseMuzzleFlash (void);
+void CL_ParseMuzzleFlash2 (void);
+void SmokeAndFlash(vec3_t origin);
+
+void CL_SetLightstyle (int i);
+
+void CL_RunParticles (void);
+void CL_RunDLights (void);
+void CL_RunLightStyles (void);
+
+void CL_AddEntities (void);
+void CL_AddDLights (void);
+void CL_AddTEnts (void);
+void CL_AddLightStyles (void);
+
+//=================================================
+
+void CL_PrepRefresh (void);
+void CL_RegisterSounds (void);
+
+void CL_Quit_f (void);
+
+void IN_Accumulate (void);
+
+void CL_ParseLayout (void);
+
+
+//
+// cl_main
+//
+extern refexport_t re; // interface to refresh .dll
+
+void CL_Init (void);
+
+void CL_FixUpGender(void);
+void CL_Disconnect (void);
+void CL_Disconnect_f (void);
+void CL_GetChallengePacket (void);
+void CL_PingServers_f (void);
+void CL_Snd_Restart_f (void);
+void CL_RequestNextDownload (void);
+
+//
+// cl_input
+//
+typedef struct
+{
+ int down[2]; // key nums holding it down
+ unsigned downtime; // msec timestamp
+ unsigned msec; // msec down this frame
+ int state;
+} kbutton_t;
+
+extern kbutton_t in_mlook, in_klook;
+extern kbutton_t in_strafe;
+extern kbutton_t in_speed;
+
+void CL_InitInput (void);
+void CL_SendCmd (void);
+void CL_SendMove (usercmd_t *cmd);
+
+void CL_ClearState (void);
+
+void CL_ReadPackets (void);
+
+int CL_ReadFromServer (void);
+void CL_WriteToServer (usercmd_t *cmd);
+void CL_BaseMove (usercmd_t *cmd);
+
+void IN_CenterView (void);
+
+float CL_KeyState (kbutton_t *key);
+char *Key_KeynumToString (int keynum);
+
+//
+// cl_demo.c
+//
+void CL_WriteDemoMessage (void);
+void CL_Stop_f (void);
+void CL_Record_f (void);
+
+//
+// cl_parse.c
+//
+extern char *svc_strings[256];
+
+void CL_ParseServerMessage (void);
+void CL_LoadClientinfo (clientinfo_t *ci, char *s);
+void SHOWNET(char *s);
+void CL_ParseClientinfo (int player);
+void CL_Download_f (void);
+
+//
+// cl_view.c
+//
+extern int gun_frame;
+extern struct model_s *gun_model;
+
+void V_Init (void);
+void V_RenderView( float stereo_separation );
+void V_AddEntity (entity_t *ent);
+void V_AddParticle (vec3_t org, int color, float alpha);
+void V_AddLight (vec3_t org, float intensity, float r, float g, float b);
+void V_AddLightStyle (int style, float r, float g, float b);
+
+//
+// cl_tent.c
+//
+void CL_RegisterTEntSounds (void);
+void CL_RegisterTEntModels (void);
+void CL_SmokeAndFlash(vec3_t origin);
+
+
+//
+// cl_pred.c
+//
+void CL_InitPrediction (void);
+void CL_PredictMove (void);
+void CL_CheckPredictionError (void);
+
+//
+// cl_fx.c
+//
+cdlight_t *CL_AllocDlight (int key);
+void CL_BigTeleportParticles (vec3_t org);
+void CL_RocketTrail (vec3_t start, vec3_t end, centity_t *old);
+void CL_DiminishingTrail (vec3_t start, vec3_t end, centity_t *old, int flags);
+void CL_FlyEffect (centity_t *ent, vec3_t origin);
+void CL_BfgParticles (entity_t *ent);
+void CL_AddParticles (void);
+void CL_EntityEvent (entity_state_t *ent);
+// RAFAEL
+void CL_TrapParticles (entity_t *ent);
+
+//
+// menus
+//
+void M_Init (void);
+void M_Keydown (int key);
+void M_Draw (void);
+void M_Menu_Main_f (void);
+void M_ForceMenuOff (void);
+void M_AddToServerList (netadr_t adr, char *info);
+
+//
+// cl_inv.c
+//
+void CL_ParseInventory (void);
+void CL_KeyInventory (int key);
+void CL_DrawInventory (void);
+
+//
+// cl_pred.c
+//
+void CL_PredictMovement (void);
+
+#if id386
+void x86_TimerStart( void );
+void x86_TimerStop( void );
+void x86_TimerInit( unsigned long smallest, unsigned longest );
+unsigned long *x86_TimerGetHistogram( void );
+#endif
diff --git a/client/console.c b/client/console.c
new file mode 100644
index 0000000..99e2e86
--- /dev/null
+++ b/client/console.c
@@ -0,0 +1,682 @@
+/*
+Copyright (C) 1997-2001 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.
+
+*/
+// console.c
+
+#include "client.h"
+
+console_t con;
+
+cvar_t *con_notifytime;
+
+
+#define MAXCMDLINE 256
+extern char key_lines[32][MAXCMDLINE];
+extern int edit_line;
+extern int key_linepos;
+
+
+void DrawString (int x, int y, char *s)
+{
+ while (*s)
+ {
+ re.DrawChar (x, y, *s);
+ x+=8;
+ s++;
+ }
+}
+
+void DrawAltString (int x, int y, char *s)
+{
+ while (*s)
+ {
+ re.DrawChar (x, y, *s ^ 0x80);
+ x+=8;
+ s++;
+ }
+}
+
+
+void Key_ClearTyping (void)
+{
+ key_lines[edit_line][1] = 0; // clear any typing
+ key_linepos = 1;
+}
+
+/*
+================
+Con_ToggleConsole_f
+================
+*/
+void Con_ToggleConsole_f (void)
+{
+ SCR_EndLoadingPlaque (); // get rid of loading plaque
+
+ if (cl.attractloop)
+ {
+ Cbuf_AddText ("killserver\n");
+ return;
+ }
+
+ if (cls.state == ca_disconnected)
+ { // start the demo loop again
+ Cbuf_AddText ("d1\n");
+ return;
+ }
+
+ Key_ClearTyping ();
+ Con_ClearNotify ();
+
+ if (cls.key_dest == key_console)
+ {
+ M_ForceMenuOff ();
+ Cvar_Set ("paused", "0");
+ }
+ else
+ {
+ M_ForceMenuOff ();
+ cls.key_dest = key_console;
+
+ if (Cvar_VariableValue ("maxclients") == 1
+ && Com_ServerState ())
+ Cvar_Set ("paused", "1");
+ }
+}
+
+/*
+================
+Con_ToggleChat_f
+================
+*/
+void Con_ToggleChat_f (void)
+{
+ Key_ClearTyping ();
+
+ if (cls.key_dest == key_console)
+ {
+ if (cls.state == ca_active)
+ {
+ M_ForceMenuOff ();
+ cls.key_dest = key_game;
+ }
+ }
+ else
+ cls.key_dest = key_console;
+
+ Con_ClearNotify ();
+}
+
+/*
+================
+Con_Clear_f
+================
+*/
+void Con_Clear_f (void)
+{
+ memset (con.text, ' ', CON_TEXTSIZE);
+}
+
+
+/*
+================
+Con_Dump_f
+
+Save the console contents out to a file
+================
+*/
+void Con_Dump_f (void)
+{
+ int l, x;
+ char *line;
+ FILE *f;
+ char buffer[1024];
+ char name[MAX_OSPATH];
+
+ if (Cmd_Argc() != 2)
+ {
+ Com_Printf ("usage: condump <filename>\n");
+ return;
+ }
+
+ Com_sprintf (name, sizeof(name), "%s/%s.txt", FS_Gamedir(), Cmd_Argv(1));
+
+ Com_Printf ("Dumped console text to %s.\n", name);
+ FS_CreatePath (name);
+ f = fopen (name, "w");
+ if (!f)
+ {
+ Com_Printf ("ERROR: couldn't open.\n");
+ return;
+ }
+
+ // skip empty lines
+ for (l = con.current - con.totallines + 1 ; l <= con.current ; l++)
+ {
+ line = con.text + (l%con.totallines)*con.linewidth;
+ for (x=0 ; x<con.linewidth ; x++)
+ if (line[x] != ' ')
+ break;
+ if (x != con.linewidth)
+ break;
+ }
+
+ // write the remaining lines
+ buffer[con.linewidth] = 0;
+ for ( ; l <= con.current ; l++)
+ {
+ line = con.text + (l%con.totallines)*con.linewidth;
+ strncpy (buffer, line, con.linewidth);
+ for (x=con.linewidth-1 ; x>=0 ; x--)
+ {
+ if (buffer[x] == ' ')
+ buffer[x] = 0;
+ else
+ break;
+ }
+ for (x=0; buffer[x]; x++)
+ buffer[x] &= 0x7f;
+
+ fprintf (f, "%s\n", buffer);
+ }
+
+ fclose (f);
+}
+
+
+/*
+================
+Con_ClearNotify
+================
+*/
+void Con_ClearNotify (void)
+{
+ int i;
+
+ for (i=0 ; i<NUM_CON_TIMES ; i++)
+ con.times[i] = 0;
+}
+
+
+/*
+================
+Con_MessageMode_f
+================
+*/
+void Con_MessageMode_f (void)
+{
+ chat_team = false;
+ cls.key_dest = key_message;
+}
+
+/*
+================
+Con_MessageMode2_f
+================
+*/
+void Con_MessageMode2_f (void)
+{
+ chat_team = true;
+ cls.key_dest = key_message;
+}
+
+/*
+================
+Con_CheckResize
+
+If the line width has changed, reformat the buffer.
+================
+*/
+void Con_CheckResize (void)
+{
+ int i, j, width, oldwidth, oldtotallines, numlines, numchars;
+ char tbuf[CON_TEXTSIZE];
+
+ width = (viddef.width >> 3) - 2;
+
+ if (width == con.linewidth)
+ return;
+
+ if (width < 1) // video hasn't been initialized yet
+ {
+ width = 38;
+ con.linewidth = width;
+ con.totallines = CON_TEXTSIZE / con.linewidth;
+ memset (con.text, ' ', CON_TEXTSIZE);
+ }
+ else
+ {
+ oldwidth = con.linewidth;
+ con.linewidth = width;
+ oldtotallines = con.totallines;
+ con.totallines = CON_TEXTSIZE / con.linewidth;
+ numlines = oldtotallines;
+
+ if (con.totallines < numlines)
+ numlines = con.totallines;
+
+ numchars = oldwidth;
+
+ if (con.linewidth < numchars)
+ numchars = con.linewidth;
+
+ memcpy (tbuf, con.text, CON_TEXTSIZE);
+ memset (con.text, ' ', CON_TEXTSIZE);
+
+ for (i=0 ; i<numlines ; i++)
+ {
+ for (j=0 ; j<numchars ; j++)
+ {
+ con.text[(con.totallines - 1 - i) * con.linewidth + j] =
+ tbuf[((con.current - i + oldtotallines) %
+ oldtotallines) * oldwidth + j];
+ }
+ }
+
+ Con_ClearNotify ();
+ }
+
+ con.current = con.totallines - 1;
+ con.display = con.current;
+}
+
+
+/*
+================
+Con_Init
+================
+*/
+void Con_Init (void)
+{
+ con.linewidth = -1;
+
+ Con_CheckResize ();
+
+ Com_Printf ("Console initialized.\n");
+
+//
+// register our commands
+//
+ con_notifytime = Cvar_Get ("con_notifytime", "3", 0);
+
+ Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
+ Cmd_AddCommand ("togglechat", Con_ToggleChat_f);
+ Cmd_AddCommand ("messagemode", Con_MessageMode_f);
+ Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
+ Cmd_AddCommand ("clear", Con_Clear_f);
+ Cmd_AddCommand ("condump", Con_Dump_f);
+ con.initialized = true;
+}
+
+
+/*
+===============
+Con_Linefeed
+===============
+*/
+void Con_Linefeed (void)
+{
+ con.x = 0;
+ if (con.display == con.current)
+ con.display++;
+ con.current++;
+ memset (&con.text[(con.current%con.totallines)*con.linewidth]
+ , ' ', con.linewidth);
+}
+
+/*
+================
+Con_Print
+
+Handles cursor positioning, line wrapping, etc
+All console printing must go through this in order to be logged to disk
+If no console is visible, the text will appear at the top of the game window
+================
+*/
+void Con_Print (char *txt)
+{
+ int y;
+ int c, l;
+ static int cr;
+ int mask;
+
+ if (!con.initialized)
+ return;
+
+ if (txt[0] == 1 || txt[0] == 2)
+ {
+ mask = 128; // go to colored text
+ txt++;
+ }
+ else
+ mask = 0;
+
+
+ while ( (c = *txt) )
+ {
+ // count word length
+ for (l=0 ; l< con.linewidth ; l++)
+ if ( txt[l] <= ' ')
+ break;
+
+ // word wrap
+ if (l != con.linewidth && (con.x + l > con.linewidth) )
+ con.x = 0;
+
+ txt++;
+
+ if (cr)
+ {
+ con.current--;
+ cr = false;
+ }
+
+
+ if (!con.x)
+ {
+ Con_Linefeed ();
+ // mark time for transparent overlay
+ if (con.current >= 0)
+ con.times[con.current % NUM_CON_TIMES] = cls.realtime;
+ }
+
+ switch (c)
+ {
+ case '\n':
+ con.x = 0;
+ break;
+
+ case '\r':
+ con.x = 0;
+ cr = 1;
+ break;
+
+ default: // display character and advance
+ y = con.current % con.totallines;
+ con.text[y*con.linewidth+con.x] = c | mask | con.ormask;
+ con.x++;
+ if (con.x >= con.linewidth)
+ con.x = 0;
+ break;
+ }
+
+ }
+}
+
+
+/*
+==============
+Con_CenteredPrint
+==============
+*/
+void Con_CenteredPrint (char *text)
+{
+ int l;
+ char buffer[1024];
+
+ l = strlen(text);
+ l = (con.linewidth-l)/2;
+ if (l < 0)
+ l = 0;
+ memset (buffer, ' ', l);
+ strcpy (buffer+l, text);
+ strcat (buffer, "\n");
+ Con_Print (buffer);
+}
+
+/*
+==============================================================================
+
+DRAWING
+
+==============================================================================
+*/
+
+
+/*
+================
+Con_DrawInput
+
+The input line scrolls horizontally if typing goes beyond the right edge
+================
+*/
+void Con_DrawInput (void)
+{
+ int y;
+ int i;
+ char *text;
+
+ if (cls.key_dest == key_menu)
+ return;
+ if (cls.key_dest != key_console && cls.state == ca_active)
+ return; // don't draw anything (always draw if not active)
+
+ text = key_lines[edit_line];
+
+// add the cursor frame
+ text[key_linepos] = 10+((int)(cls.realtime>>8)&1);
+
+// fill out remainder with spaces
+ for (i=key_linepos+1 ; i< con.linewidth ; i++)
+ text[i] = ' ';
+
+// prestep if horizontally scrolling
+ if (key_linepos >= con.linewidth)
+ text += 1 + key_linepos - con.linewidth;
+
+// draw it
+ y = con.vislines-16;
+
+ for (i=0 ; i<con.linewidth ; i++)
+ re.DrawChar ( (i+1)<<3, con.vislines - 22, text[i]);
+
+// remove cursor
+ key_lines[edit_line][key_linepos] = 0;
+}
+
+
+/*
+================
+Con_DrawNotify
+
+Draws the last few lines of output transparently over the game top
+================
+*/
+void Con_DrawNotify (void)
+{
+ int x, v;
+ char *text;
+ int i;
+ int time;
+ char *s;
+ int skip;
+
+ v = 0;
+ for (i= con.current-NUM_CON_TIMES+1 ; i<=con.current ; i++)
+ {
+ if (i < 0)
+ continue;
+ time = con.times[i % NUM_CON_TIMES];
+ if (time == 0)
+ continue;
+ time = cls.realtime - time;
+ if (time > con_notifytime->value*1000)
+ continue;
+ text = con.text + (i % con.totallines)*con.linewidth;
+
+ for (x = 0 ; x < con.linewidth ; x++)
+ re.DrawChar ( (x+1)<<3, v, text[x]);
+
+ v += 8;
+ }
+
+
+ if (cls.key_dest == key_message)
+ {
+ if (chat_team)
+ {
+ DrawString (8, v, "say_team:");
+ skip = 11;
+ }
+ else
+ {
+ DrawString (8, v, "say:");
+ skip = 5;
+ }
+
+ s = chat_buffer;
+ if (chat_bufferlen > (viddef.width>>3)-(skip+1))
+ s += chat_bufferlen - ((viddef.width>>3)-(skip+1));
+ x = 0;
+ while(s[x])
+ {
+ re.DrawChar ( (x+skip)<<3, v, s[x]);
+ x++;
+ }
+ re.DrawChar ( (x+skip)<<3, v, 10+((cls.realtime>>8)&1));
+ v += 8;
+ }
+
+ if (v)
+ {
+ SCR_AddDirtyPoint (0,0);
+ SCR_AddDirtyPoint (viddef.width-1, v);
+ }
+}
+
+/*
+================
+Con_DrawConsole
+
+Draws the console with the solid background
+================
+*/
+void Con_DrawConsole (float frac)
+{
+ int i, j, x, y, n;
+ int rows;
+ char *text;
+ int row;
+ int lines;
+ char version[64];
+ char dlbar[1024];
+
+ lines = viddef.height * frac;
+ if (lines <= 0)
+ return;
+
+ if (lines > viddef.height)
+ lines = viddef.height;
+
+// draw the background
+ re.DrawStretchPic (0, -viddef.height+lines, viddef.width, viddef.height, "conback");
+ SCR_AddDirtyPoint (0,0);
+ SCR_AddDirtyPoint (viddef.width-1,lines-1);
+
+ Com_sprintf (version, sizeof(version), "v%4.2f", VERSION);
+ for (x=0 ; x<5 ; x++)
+ re.DrawChar (viddef.width-44+x*8, lines-12, 128 + version[x] );
+
+// draw the text
+ con.vislines = lines;
+
+#if 0
+ rows = (lines-8)>>3; // rows of text to draw
+
+ y = lines - 24;
+#else
+ rows = (lines-22)>>3; // rows of text to draw
+
+ y = lines - 30;
+#endif
+
+// draw from the bottom up
+ if (con.display != con.current)
+ {
+ // draw arrows to show the buffer is backscrolled
+ for (x=0 ; x<con.linewidth ; x+=4)
+ re.DrawChar ( (x+1)<<3, y, '^');
+
+ y -= 8;
+ rows--;
+ }
+
+ row = con.display;
+ for (i=0 ; i<rows ; i++, y-=8, row--)
+ {
+ if (row < 0)
+ break;
+ if (con.current - row >= con.totallines)
+ break; // past scrollback wrap point
+
+ text = con.text + (row % con.totallines)*con.linewidth;
+
+ for (x=0 ; x<con.linewidth ; x++)
+ re.DrawChar ( (x+1)<<3, y, text[x]);
+ }
+
+//ZOID
+ // draw the download bar
+ // figure out width
+ if (cls.download) {
+ if ((text = strrchr(cls.downloadname, '/')) != NULL)
+ text++;
+ else
+ text = cls.downloadname;
+
+ x = con.linewidth - ((con.linewidth * 7) / 40);
+ y = x - strlen(text) - 8;
+ i = con.linewidth/3;
+ if (strlen(text) > i) {
+ y = x - i - 11;
+ strncpy(dlbar, text, i);
+ dlbar[i] = 0;
+ strcat(dlbar, "...");
+ } else
+ strcpy(dlbar, text);
+ strcat(dlbar, ": ");
+ i = strlen(dlbar);
+ dlbar[i++] = '\x80';
+ // where's the dot go?
+ if (cls.downloadpercent == 0)
+ n = 0;
+ else
+ n = y * cls.downloadpercent / 100;
+
+ for (j = 0; j < y; j++)
+ if (j == n)
+ dlbar[i++] = '\x83';
+ else
+ dlbar[i++] = '\x81';
+ dlbar[i++] = '\x82';
+ dlbar[i] = 0;
+
+ sprintf(dlbar + strlen(dlbar), " %02d%%", cls.downloadpercent);
+
+ // draw it
+ y = con.vislines-12;
+ for (i = 0; i < strlen(dlbar); i++)
+ re.DrawChar ( (i+1)<<3, y, dlbar[i]);
+ }
+//ZOID
+
+// draw the input prompt, user text, and cursor if desired
+ Con_DrawInput ();
+}
+
+
diff --git a/client/console.h b/client/console.h
new file mode 100644
index 0000000..6e109fe
--- /dev/null
+++ b/client/console.h
@@ -0,0 +1,62 @@
+/*
+Copyright (C) 1997-2001 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.
+
+*/
+
+//
+// console
+//
+
+#define NUM_CON_TIMES 4
+
+#define CON_TEXTSIZE 32768
+typedef struct
+{
+ qboolean initialized;
+
+ char text[CON_TEXTSIZE];
+ int current; // line where next message will be printed
+ int x; // offset in current line for next print
+ int display; // bottom of console displays this line
+
+ int ormask; // high bit mask for colored characters
+
+ int linewidth; // characters across screen
+ int totallines; // total lines in console scrollback
+
+ float cursorspeed;
+
+ int vislines;
+
+ float times[NUM_CON_TIMES]; // cls.realtime time the line was generated
+ // for transparent notify lines
+} console_t;
+
+extern console_t con;
+
+void Con_DrawCharacter (int cx, int line, int num);
+
+void Con_CheckResize (void);
+void Con_Init (void);
+void Con_DrawConsole (float frac);
+void Con_Print (char *txt);
+void Con_CenteredPrint (char *text);
+void Con_Clear_f (void);
+void Con_DrawNotify (void);
+void Con_ClearNotify (void);
+void Con_ToggleConsole_f (void);
diff --git a/client/input.h b/client/input.h
new file mode 100644
index 0000000..8ce3457
--- /dev/null
+++ b/client/input.h
@@ -0,0 +1,34 @@
+/*
+Copyright (C) 1997-2001 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.
+
+*/
+// input.h -- external (non-keyboard) input devices
+
+void IN_Init (void);
+
+void IN_Shutdown (void);
+
+void IN_Commands (void);
+// oportunity for devices to stick commands on the script buffer
+
+void IN_Frame (void);
+
+void IN_Move (usercmd_t *cmd);
+// add additional movement on top of the keyboard move cmd
+
+void IN_Activate (qboolean active);
diff --git a/client/keys.c b/client/keys.c
new file mode 100644
index 0000000..6f6728a
--- /dev/null
+++ b/client/keys.c
@@ -0,0 +1,943 @@
+/*
+Copyright (C) 1997-2001 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 "client.h"
+
+/*
+
+key up events are sent even if in console mode
+
+*/
+
+
+#define MAXCMDLINE 256
+char key_lines[32][MAXCMDLINE];
+int key_linepos;
+int shift_down=false;
+int anykeydown;
+
+int edit_line=0;
+int history_line=0;
+
+int key_waiting;
+char *keybindings[256];
+qboolean consolekeys[256]; // if true, can't be rebound while in console
+qboolean menubound[256]; // if true, can't be rebound while in menu
+int keyshift[256]; // key to map to if shift held down in console
+int key_repeats[256]; // if > 1, it is autorepeating
+qboolean keydown[256];
+
+typedef struct
+{
+ char *name;
+ int keynum;
+} keyname_t;
+
+keyname_t keynames[] =
+{
+ {"TAB", K_TAB},
+ {"ENTER", K_ENTER},
+ {"ESCAPE", K_ESCAPE},
+ {"SPACE", K_SPACE},
+ {"BACKSPACE", K_BACKSPACE},
+ {"UPARROW", K_UPARROW},
+ {"DOWNARROW", K_DOWNARROW},
+ {"LEFTARROW", K_LEFTARROW},
+ {"RIGHTARROW", K_RIGHTARROW},
+
+ {"ALT", K_ALT},
+ {"CTRL", K_CTRL},
+ {"SHIFT", K_SHIFT},
+
+ {"F1", K_F1},
+ {"F2", K_F2},
+ {"F3", K_F3},
+ {"F4", K_F4},
+ {"F5", K_F5},
+ {"F6", K_F6},
+ {"F7", K_F7},
+ {"F8", K_F8},
+ {"F9", K_F9},
+ {"F10", K_F10},
+ {"F11", K_F11},
+ {"F12", K_F12},
+
+ {"INS", K_INS},
+ {"DEL", K_DEL},
+ {"PGDN", K_PGDN},
+ {"PGUP", K_PGUP},
+ {"HOME", K_HOME},
+ {"END", K_END},
+
+ {"MOUSE1", K_MOUSE1},
+ {"MOUSE2", K_MOUSE2},
+ {"MOUSE3", K_MOUSE3},
+
+ {"JOY1", K_JOY1},
+ {"JOY2", K_JOY2},
+ {"JOY3", K_JOY3},
+ {"JOY4", K_JOY4},
+
+ {"AUX1", K_AUX1},
+ {"AUX2", K_AUX2},
+ {"AUX3", K_AUX3},
+ {"AUX4", K_AUX4},
+ {"AUX5", K_AUX5},
+ {"AUX6", K_AUX6},
+ {"AUX7", K_AUX7},
+ {"AUX8", K_AUX8},
+ {"AUX9", K_AUX9},
+ {"AUX10", K_AUX10},
+ {"AUX11", K_AUX11},
+ {"AUX12", K_AUX12},
+ {"AUX13", K_AUX13},
+ {"AUX14", K_AUX14},
+ {"AUX15", K_AUX15},
+ {"AUX16", K_AUX16},
+ {"AUX17", K_AUX17},
+ {"AUX18", K_AUX18},
+ {"AUX19", K_AUX19},
+ {"AUX20", K_AUX20},
+ {"AUX21", K_AUX21},
+ {"AUX22", K_AUX22},
+ {"AUX23", K_AUX23},
+ {"AUX24", K_AUX24},
+ {"AUX25", K_AUX25},
+ {"AUX26", K_AUX26},
+ {"AUX27", K_AUX27},
+ {"AUX28", K_AUX28},
+ {"AUX29", K_AUX29},
+ {"AUX30", K_AUX30},
+ {"AUX31", K_AUX31},
+ {"AUX32", K_AUX32},
+
+ {"KP_HOME", K_KP_HOME },
+ {"KP_UPARROW", K_KP_UPARROW },
+ {"KP_PGUP", K_KP_PGUP },
+ {"KP_LEFTARROW", K_KP_LEFTARROW },
+ {"KP_5", K_KP_5 },
+ {"KP_RIGHTARROW", K_KP_RIGHTARROW },
+ {"KP_END", K_KP_END },
+ {"KP_DOWNARROW", K_KP_DOWNARROW },
+ {"KP_PGDN", K_KP_PGDN },
+ {"KP_ENTER", K_KP_ENTER },
+ {"KP_INS", K_KP_INS },
+ {"KP_DEL", K_KP_DEL },
+ {"KP_SLASH", K_KP_SLASH },
+ {"KP_MINUS", K_KP_MINUS },
+ {"KP_PLUS", K_KP_PLUS },
+
+ {"MWHEELUP", K_MWHEELUP },
+ {"MWHEELDOWN", K_MWHEELDOWN },
+
+ {"PAUSE", K_PAUSE},
+
+ {"SEMICOLON", ';'}, // because a raw semicolon seperates commands
+
+ {NULL,0}
+};
+
+/*
+==============================================================================
+
+ LINE TYPING INTO THE CONSOLE
+
+==============================================================================
+*/
+
+void CompleteCommand (void)
+{
+ char *cmd, *s;
+
+ s = key_lines[edit_line]+1;
+ if (*s == '\\' || *s == '/')
+ s++;
+
+ cmd = Cmd_CompleteCommand (s);
+ if (!cmd)
+ cmd = Cvar_CompleteVariable (s);
+ if (cmd)
+ {
+ key_lines[edit_line][1] = '/';
+ strcpy (key_lines[edit_line]+2, cmd);
+ key_linepos = strlen(cmd)+2;
+ key_lines[edit_line][key_linepos] = ' ';
+ key_linepos++;
+ key_lines[edit_line][key_linepos] = 0;
+ return;
+ }
+}
+
+/*
+====================
+Key_Console
+
+Interactive line editing and console scrollback
+====================
+*/
+void Key_Console (int key)
+{
+
+ switch ( key )
+ {
+ case K_KP_SLASH:
+ key = '/';
+ break;
+ case K_KP_MINUS:
+ key = '-';
+ break;
+ case K_KP_PLUS:
+ key = '+';
+ break;
+ case K_KP_HOME:
+ key = '7';
+ break;
+ case K_KP_UPARROW:
+ key = '8';
+ break;
+ case K_KP_PGUP:
+ key = '9';
+ break;
+ case K_KP_LEFTARROW:
+ key = '4';
+ break;
+ case K_KP_5:
+ key = '5';
+ break;
+ case K_KP_RIGHTARROW:
+ key = '6';
+ break;
+ case K_KP_END:
+ key = '1';
+ break;
+ case K_KP_DOWNARROW:
+ key = '2';
+ break;
+ case K_KP_PGDN:
+ key = '3';
+ break;
+ case K_KP_INS:
+ key = '0';
+ break;
+ case K_KP_DEL:
+ key = '.';
+ break;
+ }
+
+ if ( ( toupper( key ) == 'V' && keydown[K_CTRL] ) ||
+ ( ( ( key == K_INS ) || ( key == K_KP_INS ) ) && keydown[K_SHIFT] ) )
+ {
+ char *cbd;
+
+ if ( ( cbd = Sys_GetClipboardData() ) != 0 )
+ {
+ int i;
+
+ strtok( cbd, "\n\r\b" );
+
+ i = strlen( cbd );
+ if ( i + key_linepos >= MAXCMDLINE)
+ i= MAXCMDLINE - key_linepos;
+
+ if ( i > 0 )
+ {
+ cbd[i]=0;
+ strcat( key_lines[edit_line], cbd );
+ key_linepos += i;
+ }
+ free( cbd );
+ }
+
+ return;
+ }
+
+ if ( key == 'l' )
+ {
+ if ( keydown[K_CTRL] )
+ {
+ Cbuf_AddText ("clear\n");
+ return;
+ }
+ }
+
+ if ( key == K_ENTER || key == K_KP_ENTER )
+ { // backslash text are commands, else chat
+ if (key_lines[edit_line][1] == '\\' || key_lines[edit_line][1] == '/')
+ Cbuf_AddText (key_lines[edit_line]+2); // skip the >
+ else
+ Cbuf_AddText (key_lines[edit_line]+1); // valid command
+
+ Cbuf_AddText ("\n");
+ Com_Printf ("%s\n",key_lines[edit_line]);
+ edit_line = (edit_line + 1) & 31;
+ history_line = edit_line;
+ key_lines[edit_line][0] = ']';
+ key_linepos = 1;
+ if (cls.state == ca_disconnected)
+ SCR_UpdateScreen (); // force an update, because the command
+ // may take some time
+ return;
+ }
+
+ if (key == K_TAB)
+ { // command completion
+ CompleteCommand ();
+ return;
+ }
+
+ if ( ( key == K_BACKSPACE ) || ( key == K_LEFTARROW ) || ( key == K_KP_LEFTARROW ) || ( ( key == 'h' ) && ( keydown[K_CTRL] ) ) )
+ {
+ if (key_linepos > 1)
+ key_linepos--;
+ return;
+ }
+
+ if ( ( key == K_UPARROW ) || ( key == K_KP_UPARROW ) ||
+ ( ( key == 'p' ) && keydown[K_CTRL] ) )
+ {
+ do
+ {
+ history_line = (history_line - 1) & 31;
+ } while (history_line != edit_line
+ && !key_lines[history_line][1]);
+ if (history_line == edit_line)
+ history_line = (edit_line+1)&31;
+ strcpy(key_lines[edit_line], key_lines[history_line]);
+ key_linepos = strlen(key_lines[edit_line]);
+ return;
+ }
+
+ if ( ( key == K_DOWNARROW ) || ( key == K_KP_DOWNARROW ) ||
+ ( ( key == 'n' ) && keydown[K_CTRL] ) )
+ {
+ if (history_line == edit_line) return;
+ do
+ {
+ history_line = (history_line + 1) & 31;
+ }
+ while (history_line != edit_line
+ && !key_lines[history_line][1]);
+ if (history_line == edit_line)
+ {
+ key_lines[edit_line][0] = ']';
+ key_linepos = 1;
+ }
+ else
+ {
+ strcpy(key_lines[edit_line], key_lines[history_line]);
+ key_linepos = strlen(key_lines[edit_line]);
+ }
+ return;
+ }
+
+ if (key == K_PGUP || key == K_KP_PGUP )
+ {
+ con.display -= 2;
+ return;
+ }
+
+ if (key == K_PGDN || key == K_KP_PGDN )
+ {
+ con.display += 2;
+ if (con.display > con.current)
+ con.display = con.current;
+ return;
+ }
+
+ if (key == K_HOME || key == K_KP_HOME )
+ {
+ con.display = con.current - con.totallines + 10;
+ return;
+ }
+
+ if (key == K_END || key == K_KP_END )
+ {
+ con.display = con.current;
+ return;
+ }
+
+ if (key < 32 || key > 127)
+ return; // non printable
+
+ if (key_linepos < MAXCMDLINE-1)
+ {
+ key_lines[edit_line][key_linepos] = key;
+ key_linepos++;
+ key_lines[edit_line][key_linepos] = 0;
+ }
+
+}
+
+//============================================================================
+
+qboolean chat_team;
+char chat_buffer[MAXCMDLINE];
+int chat_bufferlen = 0;
+
+void Key_Message (int key)
+{
+
+ if ( key == K_ENTER || key == K_KP_ENTER )
+ {
+ if (chat_team)
+ Cbuf_AddText ("say_team \"");
+ else
+ Cbuf_AddText ("say \"");
+ Cbuf_AddText(chat_buffer);
+ Cbuf_AddText("\"\n");
+
+ cls.key_dest = key_game;
+ chat_bufferlen = 0;
+ chat_buffer[0] = 0;
+ return;
+ }
+
+ if (key == K_ESCAPE)
+ {
+ cls.key_dest = key_game;
+ chat_bufferlen = 0;
+ chat_buffer[0] = 0;
+ return;
+ }
+
+ if (key < 32 || key > 127)
+ return; // non printable
+
+ if (key == K_BACKSPACE)
+ {
+ if (chat_bufferlen)
+ {
+ chat_bufferlen--;
+ chat_buffer[chat_bufferlen] = 0;
+ }
+ return;
+ }
+
+ if (chat_bufferlen == sizeof(chat_buffer)-1)
+ return; // all full
+
+ chat_buffer[chat_bufferlen++] = key;
+ chat_buffer[chat_bufferlen] = 0;
+}
+
+//============================================================================
+
+
+/*
+===================
+Key_StringToKeynum
+
+Returns a key number to be used to index keybindings[] by looking at
+the given string. Single ascii characters return themselves, while
+the K_* names are matched up.
+===================
+*/
+int Key_StringToKeynum (char *str)
+{
+ keyname_t *kn;
+
+ if (!str || !str[0])
+ return -1;
+ if (!str[1])
+ return str[0];
+
+ for (kn=keynames ; kn->name ; kn++)
+ {
+ if (!Q_strcasecmp(str,kn->name))
+ return kn->keynum;
+ }
+ return -1;
+}
+
+/*
+===================
+Key_KeynumToString
+
+Returns a string (either a single ascii char, or a K_* name) for the
+given keynum.
+FIXME: handle quote special (general escape sequence?)
+===================
+*/
+char *Key_KeynumToString (int keynum)
+{
+ keyname_t *kn;
+ static char tinystr[2];
+
+ if (keynum == -1)
+ return "<KEY NOT FOUND>";
+ if (keynum > 32 && keynum < 127)
+ { // printable ascii
+ tinystr[0] = keynum;
+ tinystr[1] = 0;
+ return tinystr;
+ }
+
+ for (kn=keynames ; kn->name ; kn++)
+ if (keynum == kn->keynum)
+ return kn->name;
+
+ return "<UNKNOWN KEYNUM>";
+}
+
+
+/*
+===================
+Key_SetBinding
+===================
+*/
+void Key_SetBinding (int keynum, char *binding)
+{
+ char *new;
+ int l;
+
+ if (keynum == -1)
+ return;
+
+// free old bindings
+ if (keybindings[keynum])
+ {
+ Z_Free (keybindings[keynum]);
+ keybindings[keynum] = NULL;
+ }
+
+// allocate memory for new binding
+ l = strlen (binding);
+ new = Z_Malloc (l+1);
+ strcpy (new, binding);
+ new[l] = 0;
+ keybindings[keynum] = new;
+}
+
+/*
+===================
+Key_Unbind_f
+===================
+*/
+void Key_Unbind_f (void)
+{
+ int b;
+
+ if (Cmd_Argc() != 2)
+ {
+ Com_Printf ("unbind <key> : remove commands from a key\n");
+ return;
+ }
+
+ b = Key_StringToKeynum (Cmd_Argv(1));
+ if (b==-1)
+ {
+ Com_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
+ return;
+ }
+
+ Key_SetBinding (b, "");
+}
+
+void Key_Unbindall_f (void)
+{
+ int i;
+
+ for (i=0 ; i<256 ; i++)
+ if (keybindings[i])
+ Key_SetBinding (i, "");
+}
+
+
+/*
+===================
+Key_Bind_f
+===================
+*/
+void Key_Bind_f (void)
+{
+ int i, c, b;
+ char cmd[1024];
+
+ c = Cmd_Argc();
+
+ if (c < 2)
+ {
+ Com_Printf ("bind <key> [command] : attach a command to a key\n");
+ return;
+ }
+ b = Key_StringToKeynum (Cmd_Argv(1));
+ if (b==-1)
+ {
+ Com_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
+ return;
+ }
+
+ if (c == 2)
+ {
+ if (keybindings[b])
+ Com_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b] );
+ else
+ Com_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) );
+ return;
+ }
+
+// copy the rest of the command line
+ cmd[0] = 0; // start out with a null string
+ for (i=2 ; i< c ; i++)
+ {
+ strcat (cmd, Cmd_Argv(i));
+ if (i != (c-1))
+ strcat (cmd, " ");
+ }
+
+ Key_SetBinding (b, cmd);
+}
+
+/*
+============
+Key_WriteBindings
+
+Writes lines containing "bind key value"
+============
+*/
+void Key_WriteBindings (FILE *f)
+{
+ int i;
+
+ for (i=0 ; i<256 ; i++)
+ if (keybindings[i] && keybindings[i][0])
+ fprintf (f, "bind %s \"%s\"\n", Key_KeynumToString(i), keybindings[i]);
+}
+
+
+/*
+============
+Key_Bindlist_f
+
+============
+*/
+void Key_Bindlist_f (void)
+{
+ int i;
+
+ for (i=0 ; i<256 ; i++)
+ if (keybindings[i] && keybindings[i][0])
+ Com_Printf ("%s \"%s\"\n", Key_KeynumToString(i), keybindings[i]);
+}
+
+
+/*
+===================
+Key_Init
+===================
+*/
+void Key_Init (void)
+{
+ int i;
+
+ for (i=0 ; i<32 ; i++)
+ {
+ key_lines[i][0] = ']';
+ key_lines[i][1] = 0;
+ }
+ key_linepos = 1;
+
+//
+// init ascii characters in console mode
+//
+ for (i=32 ; i<128 ; i++)
+ consolekeys[i] = true;
+ consolekeys[K_ENTER] = true;
+ consolekeys[K_KP_ENTER] = true;
+ consolekeys[K_TAB] = true;
+ consolekeys[K_LEFTARROW] = true;
+ consolekeys[K_KP_LEFTARROW] = true;
+ consolekeys[K_RIGHTARROW] = true;
+ consolekeys[K_KP_RIGHTARROW] = true;
+ consolekeys[K_UPARROW] = true;
+ consolekeys[K_KP_UPARROW] = true;
+ consolekeys[K_DOWNARROW] = true;
+ consolekeys[K_KP_DOWNARROW] = true;
+ consolekeys[K_BACKSPACE] = true;
+ consolekeys[K_HOME] = true;
+ consolekeys[K_KP_HOME] = true;
+ consolekeys[K_END] = true;
+ consolekeys[K_KP_END] = true;
+ consolekeys[K_PGUP] = true;
+ consolekeys[K_KP_PGUP] = true;
+ consolekeys[K_PGDN] = true;
+ consolekeys[K_KP_PGDN] = true;
+ consolekeys[K_SHIFT] = true;
+ consolekeys[K_INS] = true;
+ consolekeys[K_KP_INS] = true;
+ consolekeys[K_KP_DEL] = true;
+ consolekeys[K_KP_SLASH] = true;
+ consolekeys[K_KP_PLUS] = true;
+ consolekeys[K_KP_MINUS] = true;
+ consolekeys[K_KP_5] = true;
+
+ consolekeys['`'] = false;
+ consolekeys['~'] = false;
+
+ for (i=0 ; i<256 ; i++)
+ keyshift[i] = i;
+ for (i='a' ; i<='z' ; i++)
+ keyshift[i] = i - 'a' + 'A';
+ keyshift['1'] = '!';
+ keyshift['2'] = '@';
+ keyshift['3'] = '#';
+ keyshift['4'] = '$';
+ keyshift['5'] = '%';
+ keyshift['6'] = '^';
+ keyshift['7'] = '&';
+ keyshift['8'] = '*';
+ keyshift['9'] = '(';
+ keyshift['0'] = ')';
+ keyshift['-'] = '_';
+ keyshift['='] = '+';
+ keyshift[','] = '<';
+ keyshift['.'] = '>';
+ keyshift['/'] = '?';
+ keyshift[';'] = ':';
+ keyshift['\''] = '"';
+ keyshift['['] = '{';
+ keyshift[']'] = '}';
+ keyshift['`'] = '~';
+ keyshift['\\'] = '|';
+
+ menubound[K_ESCAPE] = true;
+ for (i=0 ; i<12 ; i++)
+ menubound[K_F1+i] = true;
+
+//
+// register our functions
+//
+ Cmd_AddCommand ("bind",Key_Bind_f);
+ Cmd_AddCommand ("unbind",Key_Unbind_f);
+ Cmd_AddCommand ("unbindall",Key_Unbindall_f);
+ Cmd_AddCommand ("bindlist",Key_Bindlist_f);
+}
+
+/*
+===================
+Key_Event
+
+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, qboolean down, unsigned time)
+{
+ char *kb;
+ char cmd[1024];
+
+ // hack for modal presses
+ if (key_waiting == -1)
+ {
+ if (down)
+ key_waiting = key;
+ return;
+ }
+
+ // update auto-repeat status
+ if (down)
+ {
+ key_repeats[key]++;
+ if (key != K_BACKSPACE
+ && key != K_PAUSE
+ && key != K_PGUP
+ && key != K_KP_PGUP
+ && key != K_PGDN
+ && key != K_KP_PGDN
+ && key_repeats[key] > 1)
+ return; // ignore most autorepeats
+
+ if (key >= 200 && !keybindings[key])
+ Com_Printf ("%s is unbound, hit F4 to set.\n", Key_KeynumToString (key) );
+ }
+ else
+ {
+ key_repeats[key] = 0;
+ }
+
+ if (key == K_SHIFT)
+ shift_down = down;
+
+ // console key is hardcoded, so the user can never unbind it
+ if (key == '`' || key == '~')
+ {
+ if (!down)
+ return;
+ Con_ToggleConsole_f ();
+ return;
+ }
+
+ // any key during the attract mode will bring up the menu
+ if (cl.attractloop && cls.key_dest != key_menu)
+ key = K_ESCAPE;
+
+ // menu key is hardcoded, so the user can never unbind it
+ if (key == K_ESCAPE)
+ {
+ if (!down)
+ return;
+
+ if (cl.frame.playerstate.stats[STAT_LAYOUTS] && cls.key_dest == key_game)
+ { // put away help computer / inventory
+ Cbuf_AddText ("cmd putaway\n");
+ return;
+ }
+ switch (cls.key_dest)
+ {
+ case key_message:
+ Key_Message (key);
+ break;
+ case key_menu:
+ M_Keydown (key);
+ break;
+ case key_game:
+ case key_console:
+ M_Menu_Main_f ();
+ break;
+ default:
+ Com_Error (ERR_FATAL, "Bad cls.key_dest");
+ }
+ return;
+ }
+
+ // track if any key is down for BUTTON_ANY
+ keydown[key] = down;
+ if (down)
+ {
+ if (key_repeats[key] == 1)
+ anykeydown++;
+ }
+ else
+ {
+ anykeydown--;
+ if (anykeydown < 0)
+ anykeydown = 0;
+ }
+
+//
+// key up events only generate commands if the game key binding is
+// a button command (leading + sign). These will occur even in console mode,
+// to keep the character from continuing an action started before a console
+// switch. Button commands include the kenum as a parameter, so multiple
+// downs can be matched with ups
+//
+ if (!down)
+ {
+ kb = keybindings[key];
+ if (kb && kb[0] == '+')
+ {
+ Com_sprintf (cmd, sizeof(cmd), "-%s %i %i\n", kb+1, key, time);
+ Cbuf_AddText (cmd);
+ }
+ if (keyshift[key] != key)
+ {
+ kb = keybindings[keyshift[key]];
+ if (kb && kb[0] == '+')
+ {
+ Com_sprintf (cmd, sizeof(cmd), "-%s %i %i\n", kb+1, key, time);
+ Cbuf_AddText (cmd);
+ }
+ }
+ return;
+ }
+
+//
+// if not a consolekey, send to the interpreter no matter what mode is
+//
+ if ( (cls.key_dest == key_menu && menubound[key])
+ || (cls.key_dest == key_console && !consolekeys[key])
+ || (cls.key_dest == key_game && ( cls.state == ca_active || !consolekeys[key] ) ) )
+ {
+ kb = keybindings[key];
+ if (kb)
+ {
+ if (kb[0] == '+')
+ { // button commands add keynum and time as a parm
+ Com_sprintf (cmd, sizeof(cmd), "%s %i %i\n", kb, key, time);
+ Cbuf_AddText (cmd);
+ }
+ else
+ {
+ Cbuf_AddText (kb);
+ Cbuf_AddText ("\n");
+ }
+ }
+ return;
+ }
+
+ if (!down)
+ return; // other systems only care about key down events
+
+ if (shift_down)
+ key = keyshift[key];
+
+ switch (cls.key_dest)
+ {
+ case key_message:
+ Key_Message (key);
+ break;
+ case key_menu:
+ M_Keydown (key);
+ break;
+
+ case key_game:
+ case key_console:
+ Key_Console (key);
+ break;
+ default:
+ Com_Error (ERR_FATAL, "Bad cls.key_dest");
+ }
+}
+
+/*
+===================
+Key_ClearStates
+===================
+*/
+void Key_ClearStates (void)
+{
+ int i;
+
+ anykeydown = false;
+
+ for (i=0 ; i<256 ; i++)
+ {
+ if ( keydown[i] || key_repeats[i] )
+ Key_Event( i, false, 0 );
+ keydown[i] = 0;
+ key_repeats[i] = 0;
+ }
+}
+
+
+/*
+===================
+Key_GetKey
+===================
+*/
+int Key_GetKey (void)
+{
+ key_waiting = -1;
+
+ while (key_waiting == -1)
+ Sys_SendKeyEvents ();
+
+ return key_waiting;
+}
+
diff --git a/client/keys.h b/client/keys.h
new file mode 100644
index 0000000..bed9735
--- /dev/null
+++ b/client/keys.h
@@ -0,0 +1,146 @@
+/*
+Copyright (C) 1997-2001 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.
+
+*/
+
+//
+// these are the key numbers that should be passed to Key_Event
+//
+#define K_TAB 9
+#define K_ENTER 13
+#define K_ESCAPE 27
+#define K_SPACE 32
+
+// normal keys should be passed as lowercased ascii
+
+#define K_BACKSPACE 127
+#define K_UPARROW 128
+#define K_DOWNARROW 129
+#define K_LEFTARROW 130
+#define K_RIGHTARROW 131
+
+#define K_ALT 132
+#define K_CTRL 133
+#define K_SHIFT 134
+#define K_F1 135
+#define K_F2 136
+#define K_F3 137
+#define K_F4 138
+#define K_F5 139
+#define K_F6 140
+#define K_F7 141
+#define K_F8 142
+#define K_F9 143
+#define K_F10 144
+#define K_F11 145
+#define K_F12 146
+#define K_INS 147
+#define K_DEL 148
+#define K_PGDN 149
+#define K_PGUP 150
+#define K_HOME 151
+#define K_END 152
+
+#define K_KP_HOME 160
+#define K_KP_UPARROW 161
+#define K_KP_PGUP 162
+#define K_KP_LEFTARROW 163
+#define K_KP_5 164
+#define K_KP_RIGHTARROW 165
+#define K_KP_END 166
+#define K_KP_DOWNARROW 167
+#define K_KP_PGDN 168
+#define K_KP_ENTER 169
+#define K_KP_INS 170
+#define K_KP_DEL 171
+#define K_KP_SLASH 172
+#define K_KP_MINUS 173
+#define K_KP_PLUS 174
+
+#define K_PAUSE 255
+
+//
+// mouse buttons generate virtual keys
+//
+#define K_MOUSE1 200
+#define K_MOUSE2 201
+#define K_MOUSE3 202
+
+//
+// joystick buttons
+//
+#define K_JOY1 203
+#define K_JOY2 204
+#define K_JOY3 205
+#define K_JOY4 206
+
+//
+// aux keys are for multi-buttoned joysticks to generate so they can use
+// the normal binding process
+//
+#define K_AUX1 207
+#define K_AUX2 208
+#define K_AUX3 209
+#define K_AUX4 210
+#define K_AUX5 211
+#define K_AUX6 212
+#define K_AUX7 213
+#define K_AUX8 214
+#define K_AUX9 215
+#define K_AUX10 216
+#define K_AUX11 217
+#define K_AUX12 218
+#define K_AUX13 219
+#define K_AUX14 220
+#define K_AUX15 221
+#define K_AUX16 222
+#define K_AUX17 223
+#define K_AUX18 224
+#define K_AUX19 225
+#define K_AUX20 226
+#define K_AUX21 227
+#define K_AUX22 228
+#define K_AUX23 229
+#define K_AUX24 230
+#define K_AUX25 231
+#define K_AUX26 232
+#define K_AUX27 233
+#define K_AUX28 234
+#define K_AUX29 235
+#define K_AUX30 236
+#define K_AUX31 237
+#define K_AUX32 238
+
+#define K_MWHEELDOWN 239
+#define K_MWHEELUP 240
+
+extern char *keybindings[256];
+extern int key_repeats[256];
+
+extern int anykeydown;
+extern char chat_buffer[];
+extern int chat_bufferlen;
+extern qboolean chat_team;
+
+void Key_Event (int key, qboolean down, unsigned time);
+void Key_Init (void);
+void Key_WriteBindings (FILE *f);
+void Key_SetBinding (int keynum, char *binding);
+void Key_ClearStates (void);
+int Key_GetKey (void);
+
diff --git a/client/menu.c b/client/menu.c
new file mode 100644
index 0000000..1835a86
--- /dev/null
+++ b/client/menu.c
@@ -0,0 +1,4016 @@
+/*
+Copyright (C) 1997-2001 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 <ctype.h>
+#ifdef _WIN32
+#include <io.h>
+#endif
+#include "client.h"
+#include "../client/qmenu.h"
+
+static int m_main_cursor;
+
+#define NUM_CURSOR_FRAMES 15
+
+static char *menu_in_sound = "misc/menu1.wav";
+static char *menu_move_sound = "misc/menu2.wav";
+static char *menu_out_sound = "misc/menu3.wav";
+
+void M_Menu_Main_f (void);
+ void M_Menu_Game_f (void);
+ void M_Menu_LoadGame_f (void);
+ void M_Menu_SaveGame_f (void);
+ void M_Menu_PlayerConfig_f (void);
+ void M_Menu_DownloadOptions_f (void);
+ void M_Menu_Credits_f( void );
+ void M_Menu_Multiplayer_f( void );
+ void M_Menu_JoinServer_f (void);
+ void M_Menu_AddressBook_f( void );
+ void M_Menu_StartServer_f (void);
+ void M_Menu_DMOptions_f (void);
+ void M_Menu_Video_f (void);
+ void M_Menu_Options_f (void);
+ void M_Menu_Keys_f (void);
+ void M_Menu_Quit_f (void);
+
+ void M_Menu_Credits( void );
+
+qboolean m_entersound; // play after drawing a frame, so caching
+ // won't disrupt the sound
+
+void (*m_drawfunc) (void);
+const char *(*m_keyfunc) (int key);
+
+//=============================================================================
+/* Support Routines */
+
+#define MAX_MENU_DEPTH 8
+
+
+typedef struct
+{
+ void (*draw) (void);
+ const char *(*key) (int k);
+} menulayer_t;
+
+menulayer_t m_layers[MAX_MENU_DEPTH];
+int m_menudepth;
+
+static void M_Banner( char *name )
+{
+ int w, h;
+
+ re.DrawGetPicSize (&w, &h, name );
+ re.DrawPic( viddef.width / 2 - w / 2, viddef.height / 2 - 110, name );
+}
+
+void M_PushMenu ( void (*draw) (void), const char *(*key) (int k) )
+{
+ int i;
+
+ if (Cvar_VariableValue ("maxclients") == 1
+ && Com_ServerState ())
+ Cvar_Set ("paused", "1");
+
+ // if this menu is already present, drop back to that level
+ // to avoid stacking menus by hotkeys
+ for (i=0 ; i<m_menudepth ; i++)
+ if (m_layers[i].draw == draw &&
+ m_layers[i].key == key)
+ {
+ m_menudepth = i;
+ }
+
+ if (i == m_menudepth)
+ {
+ if (m_menudepth >= MAX_MENU_DEPTH)
+ Com_Error (ERR_FATAL, "M_PushMenu: MAX_MENU_DEPTH");
+ m_layers[m_menudepth].draw = m_drawfunc;
+ m_layers[m_menudepth].key = m_keyfunc;
+ m_menudepth++;
+ }
+
+ m_drawfunc = draw;
+ m_keyfunc = key;
+
+ m_entersound = true;
+
+ cls.key_dest = key_menu;
+}
+
+void M_ForceMenuOff (void)
+{
+ m_drawfunc = 0;
+ m_keyfunc = 0;
+ cls.key_dest = key_game;
+ m_menudepth = 0;
+ Key_ClearStates ();
+ Cvar_Set ("paused", "0");
+}
+
+void M_PopMenu (void)
+{
+ S_StartLocalSound( menu_out_sound );
+ if (m_menudepth < 1)
+ Com_Error (ERR_FATAL, "M_PopMenu: depth < 1");
+ m_menudepth--;
+
+ m_drawfunc = m_layers[m_menudepth].draw;
+ m_keyfunc = m_layers[m_menudepth].key;
+
+ if (!m_menudepth)
+ M_ForceMenuOff ();
+}
+
+
+const char *Default_MenuKey( menuframework_s *m, int key )
+{
+ const char *sound = NULL;
+ menucommon_s *item;
+
+ if ( m )
+ {
+ if ( ( item = Menu_ItemAtCursor( m ) ) != 0 )
+ {
+ if ( item->type == MTYPE_FIELD )
+ {
+ if ( Field_Key( ( menufield_s * ) item, key ) )
+ return NULL;
+ }
+ }
+ }
+
+ switch ( key )
+ {
+ case K_ESCAPE:
+ M_PopMenu();
+ return menu_out_sound;
+ case K_KP_UPARROW:
+ case K_UPARROW:
+ if ( m )
+ {
+ m->cursor--;
+ Menu_AdjustCursor( m, -1 );
+ sound = menu_move_sound;
+ }
+ break;
+ case K_TAB:
+ if ( m )
+ {
+ m->cursor++;
+ Menu_AdjustCursor( m, 1 );
+ sound = menu_move_sound;
+ }
+ break;
+ case K_KP_DOWNARROW:
+ case K_DOWNARROW:
+ if ( m )
+ {
+ m->cursor++;
+ Menu_AdjustCursor( m, 1 );
+ sound = menu_move_sound;
+ }
+ break;
+ case K_KP_LEFTARROW:
+ case K_LEFTARROW:
+ if ( m )
+ {
+ Menu_SlideItem( m, -1 );
+ sound = menu_move_sound;
+ }
+ break;
+ case K_KP_RIGHTARROW:
+ case K_RIGHTARROW:
+ if ( m )
+ {
+ Menu_SlideItem( m, 1 );
+ sound = menu_move_sound;
+ }
+ break;
+
+ case K_MOUSE1:
+ case K_MOUSE2:
+ case K_MOUSE3:
+ case K_JOY1:
+ case K_JOY2:
+ case K_JOY3:
+ case K_JOY4:
+ case K_AUX1:
+ case K_AUX2:
+ case K_AUX3:
+ case K_AUX4:
+ case K_AUX5:
+ case K_AUX6:
+ case K_AUX7:
+ case K_AUX8:
+ case K_AUX9:
+ case K_AUX10:
+ case K_AUX11:
+ case K_AUX12:
+ case K_AUX13:
+ case K_AUX14:
+ case K_AUX15:
+ case K_AUX16:
+ case K_AUX17:
+ case K_AUX18:
+ case K_AUX19:
+ case K_AUX20:
+ case K_AUX21:
+ case K_AUX22:
+ case K_AUX23:
+ case K_AUX24:
+ case K_AUX25:
+ case K_AUX26:
+ case K_AUX27:
+ case K_AUX28:
+ case K_AUX29:
+ case K_AUX30:
+ case K_AUX31:
+ case K_AUX32:
+
+ case K_KP_ENTER:
+ case K_ENTER:
+ if ( m )
+ Menu_SelectItem( m );
+ sound = menu_move_sound;
+ break;
+ }
+
+ return sound;
+}
+
+//=============================================================================
+
+/*
+================
+M_DrawCharacter
+
+Draws one solid graphics character
+cx and cy are in 320*240 coordinates, and will be centered on
+higher res screens.
+================
+*/
+void M_DrawCharacter (int cx, int cy, int num)
+{
+ re.DrawChar ( cx + ((viddef.width - 320)>>1), cy + ((viddef.height - 240)>>1), num);
+}
+
+void M_Print (int cx, int cy, char *str)
+{
+ while (*str)
+ {
+ M_DrawCharacter (cx, cy, (*str)+128);
+ str++;
+ cx += 8;
+ }
+}
+
+void M_PrintWhite (int cx, int cy, char *str)
+{
+ while (*str)
+ {
+ M_DrawCharacter (cx, cy, *str);
+ str++;
+ cx += 8;
+ }
+}
+
+void M_DrawPic (int x, int y, char *pic)
+{
+ re.DrawPic (x + ((viddef.width - 320)>>1), y + ((viddef.height - 240)>>1), pic);
+}
+
+
+/*
+=============
+M_DrawCursor
+
+Draws an animating cursor with the point at
+x,y. The pic will extend to the left of x,
+and both above and below y.
+=============
+*/
+void M_DrawCursor( int x, int y, int f )
+{
+ char cursorname[80];
+ static qboolean cached;
+
+ if ( !cached )
+ {
+ int i;
+
+ for ( i = 0; i < NUM_CURSOR_FRAMES; i++ )
+ {
+ Com_sprintf( cursorname, sizeof( cursorname ), "m_cursor%d", i );
+
+ re.RegisterPic( cursorname );
+ }
+ cached = true;
+ }
+
+ Com_sprintf( cursorname, sizeof(cursorname), "m_cursor%d", f );
+ re.DrawPic( x, y, cursorname );
+}
+
+void M_DrawTextBox (int x, int y, int width, int lines)
+{
+ int cx, cy;
+ int n;
+
+ // draw left side
+ cx = x;
+ cy = y;
+ M_DrawCharacter (cx, cy, 1);
+ for (n = 0; n < lines; n++)
+ {
+ cy += 8;
+ M_DrawCharacter (cx, cy, 4);
+ }
+ M_DrawCharacter (cx, cy+8, 7);
+
+ // draw middle
+ cx += 8;
+ while (width > 0)
+ {
+ cy = y;
+ M_DrawCharacter (cx, cy, 2);
+ for (n = 0; n < lines; n++)
+ {
+ cy += 8;
+ M_DrawCharacter (cx, cy, 5);
+ }
+ M_DrawCharacter (cx, cy+8, 8);
+ width -= 1;
+ cx += 8;
+ }
+
+ // draw right side
+ cy = y;
+ M_DrawCharacter (cx, cy, 3);
+ for (n = 0; n < lines; n++)
+ {
+ cy += 8;
+ M_DrawCharacter (cx, cy, 6);
+ }
+ M_DrawCharacter (cx, cy+8, 9);
+}
+
+
+/*
+=======================================================================
+
+MAIN MENU
+
+=======================================================================
+*/
+#define MAIN_ITEMS 5
+
+
+void M_Main_Draw (void)
+{
+ int i;
+ int w, h;
+ int ystart;
+ int xoffset;
+ int widest = -1;
+ int totalheight = 0;
+ char litname[80];
+ char *names[] =
+ {
+ "m_main_game",
+ "m_main_multiplayer",
+ "m_main_options",
+ "m_main_video",
+ "m_main_quit",
+ 0
+ };
+
+ for ( i = 0; names[i] != 0; i++ )
+ {
+ re.DrawGetPicSize( &w, &h, names[i] );
+
+ if ( w > widest )
+ widest = w;
+ totalheight += ( h + 12 );
+ }
+
+ ystart = ( viddef.height / 2 - 110 );
+ xoffset = ( viddef.width - widest + 70 ) / 2;
+
+ for ( i = 0; names[i] != 0; i++ )
+ {
+ if ( i != m_main_cursor )
+ re.DrawPic( xoffset, ystart + i * 40 + 13, names[i] );
+ }
+ strcpy( litname, names[m_main_cursor] );
+ strcat( litname, "_sel" );
+ 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 );
+
+ re.DrawGetPicSize( &w, &h, "m_main_plaque" );
+ re.DrawPic( xoffset - 30 - w, ystart, "m_main_plaque" );
+
+ re.DrawPic( xoffset - 30 - w, ystart + h + 5, "m_main_logo" );
+}
+
+
+const char *M_Main_Key (int key)
+{
+ const char *sound = menu_move_sound;
+
+ switch (key)
+ {
+ case K_ESCAPE:
+ M_PopMenu ();
+ break;
+
+ case K_KP_DOWNARROW:
+ case K_DOWNARROW:
+ if (++m_main_cursor >= MAIN_ITEMS)
+ m_main_cursor = 0;
+ return sound;
+
+ case K_KP_UPARROW:
+ case K_UPARROW:
+ if (--m_main_cursor < 0)
+ m_main_cursor = MAIN_ITEMS - 1;
+ return sound;
+
+ case K_KP_ENTER:
+ case K_ENTER:
+ m_entersound = true;
+
+ switch (m_main_cursor)
+ {
+ case 0:
+ M_Menu_Game_f ();
+ break;
+
+ case 1:
+ M_Menu_Multiplayer_f();
+ break;
+
+ case 2:
+ M_Menu_Options_f ();
+ break;
+
+ case 3:
+ M_Menu_Video_f ();
+ break;
+
+ case 4:
+ M_Menu_Quit_f ();
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+
+void M_Menu_Main_f (void)
+{
+ M_PushMenu (M_Main_Draw, M_Main_Key);
+}
+
+/*
+=======================================================================
+
+MULTIPLAYER MENU
+
+=======================================================================
+*/
+static menuframework_s s_multiplayer_menu;
+static menuaction_s s_join_network_server_action;
+static menuaction_s s_start_network_server_action;
+static menuaction_s s_player_setup_action;
+
+static void Multiplayer_MenuDraw (void)
+{
+ M_Banner( "m_banner_multiplayer" );
+
+ Menu_AdjustCursor( &s_multiplayer_menu, 1 );
+ Menu_Draw( &s_multiplayer_menu );
+}
+
+static void PlayerSetupFunc( void *unused )
+{
+ M_Menu_PlayerConfig_f();
+}
+
+static void JoinNetworkServerFunc( void *unused )
+{
+ M_Menu_JoinServer_f();
+}
+
+static void StartNetworkServerFunc( void *unused )
+{
+ M_Menu_StartServer_f ();
+}
+
+void Multiplayer_MenuInit( void )
+{
+ s_multiplayer_menu.x = viddef.width * 0.50 - 64;
+ s_multiplayer_menu.nitems = 0;
+
+ s_join_network_server_action.generic.type = MTYPE_ACTION;
+ s_join_network_server_action.generic.flags = QMF_LEFT_JUSTIFY;
+ s_join_network_server_action.generic.x = 0;
+ s_join_network_server_action.generic.y = 0;
+ s_join_network_server_action.generic.name = " join network server";
+ s_join_network_server_action.generic.callback = JoinNetworkServerFunc;
+
+ s_start_network_server_action.generic.type = MTYPE_ACTION;
+ s_start_network_server_action.generic.flags = QMF_LEFT_JUSTIFY;
+ s_start_network_server_action.generic.x = 0;
+ s_start_network_server_action.generic.y = 10;
+ s_start_network_server_action.generic.name = " start network server";
+ s_start_network_server_action.generic.callback = StartNetworkServerFunc;
+
+ s_player_setup_action.generic.type = MTYPE_ACTION;
+ s_player_setup_action.generic.flags = QMF_LEFT_JUSTIFY;
+ s_player_setup_action.generic.x = 0;
+ s_player_setup_action.generic.y = 20;
+ s_player_setup_action.generic.name = " player setup";
+ s_player_setup_action.generic.callback = PlayerSetupFunc;
+
+ Menu_AddItem( &s_multiplayer_menu, ( void * ) &s_join_network_server_action );
+ Menu_AddItem( &s_multiplayer_menu, ( void * ) &s_start_network_server_action );
+ Menu_AddItem( &s_multiplayer_menu, ( void * ) &s_player_setup_action );
+
+ Menu_SetStatusBar( &s_multiplayer_menu, NULL );
+
+ Menu_Center( &s_multiplayer_menu );
+}
+
+const char *Multiplayer_MenuKey( int key )
+{
+ return Default_MenuKey( &s_multiplayer_menu, key );
+}
+
+void M_Menu_Multiplayer_f( void )
+{
+ Multiplayer_MenuInit();
+ M_PushMenu( Multiplayer_MenuDraw, Multiplayer_MenuKey );
+}
+
+/*
+=======================================================================
+
+KEYS MENU
+
+=======================================================================
+*/
+char *bindnames[][2] =
+{
+{"+attack", "attack"},
+{"weapnext", "next weapon"},
+{"+forward", "walk forward"},
+{"+back", "backpedal"},
+{"+left", "turn left"},
+{"+right", "turn right"},
+{"+speed", "run"},
+{"+moveleft", "step left"},
+{"+moveright", "step right"},
+{"+strafe", "sidestep"},
+{"+lookup", "look up"},
+{"+lookdown", "look down"},
+{"centerview", "center view"},
+{"+mlook", "mouse look"},
+{"+klook", "keyboard look"},
+{"+moveup", "up / jump"},
+{"+movedown", "down / crouch"},
+
+{"inven", "inventory"},
+{"invuse", "use item"},
+{"invdrop", "drop item"},
+{"invprev", "prev item"},
+{"invnext", "next item"},
+
+{"cmd help", "help computer" },
+{ 0, 0 }
+};
+
+int keys_cursor;
+static int bind_grab;
+
+static menuframework_s s_keys_menu;
+static menuaction_s s_keys_attack_action;
+static menuaction_s s_keys_change_weapon_action;
+static menuaction_s s_keys_walk_forward_action;
+static menuaction_s s_keys_backpedal_action;
+static menuaction_s s_keys_turn_left_action;
+static menuaction_s s_keys_turn_right_action;
+static menuaction_s s_keys_run_action;
+static menuaction_s s_keys_step_left_action;
+static menuaction_s s_keys_step_right_action;
+static menuaction_s s_keys_sidestep_action;
+static menuaction_s s_keys_look_up_action;
+static menuaction_s s_keys_look_down_action;
+static menuaction_s s_keys_center_view_action;
+static menuaction_s s_keys_mouse_look_action;
+static menuaction_s s_keys_keyboard_look_action;
+static menuaction_s s_keys_move_up_action;
+static menuaction_s s_keys_move_down_action;
+static menuaction_s s_keys_inventory_action;
+static menuaction_s s_keys_inv_use_action;
+static menuaction_s s_keys_inv_drop_action;
+static menuaction_s s_keys_inv_prev_action;
+static menuaction_s s_keys_inv_next_action;
+
+static menuaction_s s_keys_help_computer_action;
+
+static void M_UnbindCommand (char *command)
+{
+ int j;
+ int l;
+ char *b;
+
+ l = strlen(command);
+
+ for (j=0 ; j<256 ; j++)
+ {
+ b = keybindings[j];
+ if (!b)
+ continue;
+ if (!strncmp (b, command, l) )
+ Key_SetBinding (j, "");
+ }
+}
+
+static void M_FindKeysForCommand (char *command, int *twokeys)
+{
+ int count;
+ int j;
+ int l;
+ char *b;
+
+ twokeys[0] = twokeys[1] = -1;
+ l = strlen(command);
+ count = 0;
+
+ for (j=0 ; j<256 ; j++)
+ {
+ b = keybindings[j];
+ if (!b)
+ continue;
+ if (!strncmp (b, command, l) )
+ {
+ twokeys[count] = j;
+ count++;
+ if (count == 2)
+ break;
+ }
+ }
+}
+
+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 ) );
+}
+
+static void DrawKeyBindingFunc( void *self )
+{
+ int keys[2];
+ menuaction_s *a = ( menuaction_s * ) self;
+
+ M_FindKeysForCommand( bindnames[a->generic.localdata[0]][0], keys);
+
+ if (keys[0] == -1)
+ {
+ Menu_DrawString( a->generic.x + a->generic.parent->x + 16, a->generic.y + a->generic.parent->y, "???" );
+ }
+ else
+ {
+ int x;
+ const char *name;
+
+ name = Key_KeynumToString (keys[0]);
+
+ Menu_DrawString( a->generic.x + a->generic.parent->x + 16, a->generic.y + a->generic.parent->y, name );
+
+ x = strlen(name) * 8;
+
+ if (keys[1] != -1)
+ {
+ Menu_DrawString( a->generic.x + a->generic.parent->x + 24 + x, a->generic.y + a->generic.parent->y, "or" );
+ Menu_DrawString( a->generic.x + a->generic.parent->x + 48 + x, a->generic.y + a->generic.parent->y, Key_KeynumToString (keys[1]) );
+ }
+ }
+}
+
+static void KeyBindingFunc( void *self )
+{
+ menuaction_s *a = ( menuaction_s * ) self;
+ int keys[2];
+
+ M_FindKeysForCommand( bindnames[a->generic.localdata[0]][0], keys );
+
+ if (keys[1] != -1)
+ M_UnbindCommand( bindnames[a->generic.localdata[0]][0]);
+
+ bind_grab = true;
+
+ Menu_SetStatusBar( &s_keys_menu, "press a key or button for this action" );
+}
+
+static void Keys_MenuInit( void )
+{
+ int y = 0;
+ int i = 0;
+
+ s_keys_menu.x = viddef.width * 0.50;
+ s_keys_menu.nitems = 0;
+ s_keys_menu.cursordraw = KeyCursorDrawFunc;
+
+ s_keys_attack_action.generic.type = MTYPE_ACTION;
+ s_keys_attack_action.generic.flags = QMF_GRAYED;
+ s_keys_attack_action.generic.x = 0;
+ s_keys_attack_action.generic.y = y;
+ s_keys_attack_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_attack_action.generic.localdata[0] = i;
+ s_keys_attack_action.generic.name = bindnames[s_keys_attack_action.generic.localdata[0]][1];
+
+ s_keys_change_weapon_action.generic.type = MTYPE_ACTION;
+ s_keys_change_weapon_action.generic.flags = QMF_GRAYED;
+ s_keys_change_weapon_action.generic.x = 0;
+ s_keys_change_weapon_action.generic.y = y += 9;
+ s_keys_change_weapon_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_change_weapon_action.generic.localdata[0] = ++i;
+ s_keys_change_weapon_action.generic.name = bindnames[s_keys_change_weapon_action.generic.localdata[0]][1];
+
+ s_keys_walk_forward_action.generic.type = MTYPE_ACTION;
+ s_keys_walk_forward_action.generic.flags = QMF_GRAYED;
+ s_keys_walk_forward_action.generic.x = 0;
+ s_keys_walk_forward_action.generic.y = y += 9;
+ s_keys_walk_forward_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_walk_forward_action.generic.localdata[0] = ++i;
+ s_keys_walk_forward_action.generic.name = bindnames[s_keys_walk_forward_action.generic.localdata[0]][1];
+
+ s_keys_backpedal_action.generic.type = MTYPE_ACTION;
+ s_keys_backpedal_action.generic.flags = QMF_GRAYED;
+ s_keys_backpedal_action.generic.x = 0;
+ s_keys_backpedal_action.generic.y = y += 9;
+ s_keys_backpedal_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_backpedal_action.generic.localdata[0] = ++i;
+ s_keys_backpedal_action.generic.name = bindnames[s_keys_backpedal_action.generic.localdata[0]][1];
+
+ s_keys_turn_left_action.generic.type = MTYPE_ACTION;
+ s_keys_turn_left_action.generic.flags = QMF_GRAYED;
+ s_keys_turn_left_action.generic.x = 0;
+ s_keys_turn_left_action.generic.y = y += 9;
+ s_keys_turn_left_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_turn_left_action.generic.localdata[0] = ++i;
+ s_keys_turn_left_action.generic.name = bindnames[s_keys_turn_left_action.generic.localdata[0]][1];
+
+ s_keys_turn_right_action.generic.type = MTYPE_ACTION;
+ s_keys_turn_right_action.generic.flags = QMF_GRAYED;
+ s_keys_turn_right_action.generic.x = 0;
+ s_keys_turn_right_action.generic.y = y += 9;
+ s_keys_turn_right_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_turn_right_action.generic.localdata[0] = ++i;
+ s_keys_turn_right_action.generic.name = bindnames[s_keys_turn_right_action.generic.localdata[0]][1];
+
+ s_keys_run_action.generic.type = MTYPE_ACTION;
+ s_keys_run_action.generic.flags = QMF_GRAYED;
+ s_keys_run_action.generic.x = 0;
+ s_keys_run_action.generic.y = y += 9;
+ s_keys_run_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_run_action.generic.localdata[0] = ++i;
+ s_keys_run_action.generic.name = bindnames[s_keys_run_action.generic.localdata[0]][1];
+
+ s_keys_step_left_action.generic.type = MTYPE_ACTION;
+ s_keys_step_left_action.generic.flags = QMF_GRAYED;
+ s_keys_step_left_action.generic.x = 0;
+ s_keys_step_left_action.generic.y = y += 9;
+ s_keys_step_left_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_step_left_action.generic.localdata[0] = ++i;
+ s_keys_step_left_action.generic.name = bindnames[s_keys_step_left_action.generic.localdata[0]][1];
+
+ s_keys_step_right_action.generic.type = MTYPE_ACTION;
+ s_keys_step_right_action.generic.flags = QMF_GRAYED;
+ s_keys_step_right_action.generic.x = 0;
+ s_keys_step_right_action.generic.y = y += 9;
+ s_keys_step_right_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_step_right_action.generic.localdata[0] = ++i;
+ s_keys_step_right_action.generic.name = bindnames[s_keys_step_right_action.generic.localdata[0]][1];
+
+ s_keys_sidestep_action.generic.type = MTYPE_ACTION;
+ s_keys_sidestep_action.generic.flags = QMF_GRAYED;
+ s_keys_sidestep_action.generic.x = 0;
+ s_keys_sidestep_action.generic.y = y += 9;
+ s_keys_sidestep_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_sidestep_action.generic.localdata[0] = ++i;
+ s_keys_sidestep_action.generic.name = bindnames[s_keys_sidestep_action.generic.localdata[0]][1];
+
+ s_keys_look_up_action.generic.type = MTYPE_ACTION;
+ s_keys_look_up_action.generic.flags = QMF_GRAYED;
+ s_keys_look_up_action.generic.x = 0;
+ s_keys_look_up_action.generic.y = y += 9;
+ s_keys_look_up_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_look_up_action.generic.localdata[0] = ++i;
+ s_keys_look_up_action.generic.name = bindnames[s_keys_look_up_action.generic.localdata[0]][1];
+
+ s_keys_look_down_action.generic.type = MTYPE_ACTION;
+ s_keys_look_down_action.generic.flags = QMF_GRAYED;
+ s_keys_look_down_action.generic.x = 0;
+ s_keys_look_down_action.generic.y = y += 9;
+ s_keys_look_down_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_look_down_action.generic.localdata[0] = ++i;
+ s_keys_look_down_action.generic.name = bindnames[s_keys_look_down_action.generic.localdata[0]][1];
+
+ s_keys_center_view_action.generic.type = MTYPE_ACTION;
+ s_keys_center_view_action.generic.flags = QMF_GRAYED;
+ s_keys_center_view_action.generic.x = 0;
+ s_keys_center_view_action.generic.y = y += 9;
+ s_keys_center_view_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_center_view_action.generic.localdata[0] = ++i;
+ s_keys_center_view_action.generic.name = bindnames[s_keys_center_view_action.generic.localdata[0]][1];
+
+ s_keys_mouse_look_action.generic.type = MTYPE_ACTION;
+ s_keys_mouse_look_action.generic.flags = QMF_GRAYED;
+ s_keys_mouse_look_action.generic.x = 0;
+ s_keys_mouse_look_action.generic.y = y += 9;
+ s_keys_mouse_look_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_mouse_look_action.generic.localdata[0] = ++i;
+ s_keys_mouse_look_action.generic.name = bindnames[s_keys_mouse_look_action.generic.localdata[0]][1];
+
+ s_keys_keyboard_look_action.generic.type = MTYPE_ACTION;
+ s_keys_keyboard_look_action.generic.flags = QMF_GRAYED;
+ s_keys_keyboard_look_action.generic.x = 0;
+ s_keys_keyboard_look_action.generic.y = y += 9;
+ s_keys_keyboard_look_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_keyboard_look_action.generic.localdata[0] = ++i;
+ s_keys_keyboard_look_action.generic.name = bindnames[s_keys_keyboard_look_action.generic.localdata[0]][1];
+
+ s_keys_move_up_action.generic.type = MTYPE_ACTION;
+ s_keys_move_up_action.generic.flags = QMF_GRAYED;
+ s_keys_move_up_action.generic.x = 0;
+ s_keys_move_up_action.generic.y = y += 9;
+ s_keys_move_up_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_move_up_action.generic.localdata[0] = ++i;
+ s_keys_move_up_action.generic.name = bindnames[s_keys_move_up_action.generic.localdata[0]][1];
+
+ s_keys_move_down_action.generic.type = MTYPE_ACTION;
+ s_keys_move_down_action.generic.flags = QMF_GRAYED;
+ s_keys_move_down_action.generic.x = 0;
+ s_keys_move_down_action.generic.y = y += 9;
+ s_keys_move_down_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_move_down_action.generic.localdata[0] = ++i;
+ s_keys_move_down_action.generic.name = bindnames[s_keys_move_down_action.generic.localdata[0]][1];
+
+ s_keys_inventory_action.generic.type = MTYPE_ACTION;
+ s_keys_inventory_action.generic.flags = QMF_GRAYED;
+ s_keys_inventory_action.generic.x = 0;
+ s_keys_inventory_action.generic.y = y += 9;
+ s_keys_inventory_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_inventory_action.generic.localdata[0] = ++i;
+ s_keys_inventory_action.generic.name = bindnames[s_keys_inventory_action.generic.localdata[0]][1];
+
+ s_keys_inv_use_action.generic.type = MTYPE_ACTION;
+ s_keys_inv_use_action.generic.flags = QMF_GRAYED;
+ s_keys_inv_use_action.generic.x = 0;
+ s_keys_inv_use_action.generic.y = y += 9;
+ s_keys_inv_use_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_inv_use_action.generic.localdata[0] = ++i;
+ s_keys_inv_use_action.generic.name = bindnames[s_keys_inv_use_action.generic.localdata[0]][1];
+
+ s_keys_inv_drop_action.generic.type = MTYPE_ACTION;
+ s_keys_inv_drop_action.generic.flags = QMF_GRAYED;
+ s_keys_inv_drop_action.generic.x = 0;
+ s_keys_inv_drop_action.generic.y = y += 9;
+ s_keys_inv_drop_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_inv_drop_action.generic.localdata[0] = ++i;
+ s_keys_inv_drop_action.generic.name = bindnames[s_keys_inv_drop_action.generic.localdata[0]][1];
+
+ s_keys_inv_prev_action.generic.type = MTYPE_ACTION;
+ s_keys_inv_prev_action.generic.flags = QMF_GRAYED;
+ s_keys_inv_prev_action.generic.x = 0;
+ s_keys_inv_prev_action.generic.y = y += 9;
+ s_keys_inv_prev_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_inv_prev_action.generic.localdata[0] = ++i;
+ s_keys_inv_prev_action.generic.name = bindnames[s_keys_inv_prev_action.generic.localdata[0]][1];
+
+ s_keys_inv_next_action.generic.type = MTYPE_ACTION;
+ s_keys_inv_next_action.generic.flags = QMF_GRAYED;
+ s_keys_inv_next_action.generic.x = 0;
+ s_keys_inv_next_action.generic.y = y += 9;
+ s_keys_inv_next_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_inv_next_action.generic.localdata[0] = ++i;
+ s_keys_inv_next_action.generic.name = bindnames[s_keys_inv_next_action.generic.localdata[0]][1];
+
+ s_keys_help_computer_action.generic.type = MTYPE_ACTION;
+ s_keys_help_computer_action.generic.flags = QMF_GRAYED;
+ s_keys_help_computer_action.generic.x = 0;
+ s_keys_help_computer_action.generic.y = y += 9;
+ s_keys_help_computer_action.generic.ownerdraw = DrawKeyBindingFunc;
+ s_keys_help_computer_action.generic.localdata[0] = ++i;
+ s_keys_help_computer_action.generic.name = bindnames[s_keys_help_computer_action.generic.localdata[0]][1];
+
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_attack_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_change_weapon_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_walk_forward_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_backpedal_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_turn_left_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_turn_right_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_run_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_step_left_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_step_right_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_sidestep_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_look_up_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_look_down_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_center_view_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_mouse_look_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_keyboard_look_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_move_up_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_move_down_action );
+
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_inventory_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_inv_use_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_inv_drop_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_inv_prev_action );
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_inv_next_action );
+
+ Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_help_computer_action );
+
+ Menu_SetStatusBar( &s_keys_menu, "enter to change, backspace to clear" );
+ Menu_Center( &s_keys_menu );
+}
+
+static void Keys_MenuDraw (void)
+{
+ Menu_AdjustCursor( &s_keys_menu, 1 );
+ Menu_Draw( &s_keys_menu );
+}
+
+static const char *Keys_MenuKey( int key )
+{
+ menuaction_s *item = ( menuaction_s * ) Menu_ItemAtCursor( &s_keys_menu );
+
+ if ( bind_grab )
+ {
+ if ( key != K_ESCAPE && key != '`' )
+ {
+ char cmd[1024];
+
+ Com_sprintf (cmd, sizeof(cmd), "bind \"%s\" \"%s\"\n", Key_KeynumToString(key), bindnames[item->generic.localdata[0]][0]);
+ Cbuf_InsertText (cmd);
+ }
+
+ Menu_SetStatusBar( &s_keys_menu, "enter to change, backspace to clear" );
+ bind_grab = false;
+ return menu_out_sound;
+ }
+
+ switch ( key )
+ {
+ case K_KP_ENTER:
+ case K_ENTER:
+ KeyBindingFunc( item );
+ return menu_in_sound;
+ case K_BACKSPACE: // delete bindings
+ case K_DEL: // delete bindings
+ case K_KP_DEL:
+ M_UnbindCommand( bindnames[item->generic.localdata[0]][0] );
+ return menu_out_sound;
+ default:
+ return Default_MenuKey( &s_keys_menu, key );
+ }
+}
+
+void M_Menu_Keys_f (void)
+{
+ Keys_MenuInit();
+ M_PushMenu( Keys_MenuDraw, Keys_MenuKey );
+}
+
+
+/*
+=======================================================================
+
+CONTROLS MENU
+
+=======================================================================
+*/
+static cvar_t *win_noalttab;
+extern cvar_t *in_joystick;
+
+static menuframework_s s_options_menu;
+static menuaction_s s_options_defaults_action;
+static menuaction_s s_options_customize_options_action;
+static menuslider_s s_options_sensitivity_slider;
+static menulist_s s_options_freelook_box;
+static menulist_s s_options_noalttab_box;
+static menulist_s s_options_alwaysrun_box;
+static menulist_s s_options_invertmouse_box;
+static menulist_s s_options_lookspring_box;
+static menulist_s s_options_lookstrafe_box;
+static menulist_s s_options_crosshair_box;
+static menuslider_s s_options_sfxvolume_slider;
+static menulist_s s_options_joystick_box;
+static menulist_s s_options_cdvolume_box;
+static menulist_s s_options_quality_list;
+static menulist_s s_options_compatibility_list;
+static menulist_s s_options_console_action;
+
+static void CrosshairFunc( void *unused )
+{
+ Cvar_SetValue( "crosshair", s_options_crosshair_box.curvalue );
+}
+
+static void JoystickFunc( void *unused )
+{
+ Cvar_SetValue( "in_joystick", s_options_joystick_box.curvalue );
+}
+
+static void CustomizeControlsFunc( void *unused )
+{
+ M_Menu_Keys_f();
+}
+
+static void AlwaysRunFunc( void *unused )
+{
+ Cvar_SetValue( "cl_run", s_options_alwaysrun_box.curvalue );
+}
+
+static void FreeLookFunc( void *unused )
+{
+ Cvar_SetValue( "freelook", s_options_freelook_box.curvalue );
+}
+
+static void MouseSpeedFunc( void *unused )
+{
+ Cvar_SetValue( "sensitivity", s_options_sensitivity_slider.curvalue / 2.0F );
+}
+
+static void NoAltTabFunc( void *unused )
+{
+ Cvar_SetValue( "win_noalttab", s_options_noalttab_box.curvalue );
+}
+
+static float ClampCvar( float min, float max, float value )
+{
+ if ( value < min ) return min;
+ if ( value > max ) return max;
+ return value;
+}
+
+static void ControlsSetMenuItemValues( void )
+{
+ s_options_sfxvolume_slider.curvalue = Cvar_VariableValue( "s_volume" ) * 10;
+ s_options_cdvolume_box.curvalue = !Cvar_VariableValue("cd_nocd");
+ s_options_quality_list.curvalue = !Cvar_VariableValue( "s_loadas8bit" );
+ s_options_sensitivity_slider.curvalue = ( sensitivity->value ) * 2;
+
+ Cvar_SetValue( "cl_run", ClampCvar( 0, 1, cl_run->value ) );
+ s_options_alwaysrun_box.curvalue = cl_run->value;
+
+ s_options_invertmouse_box.curvalue = m_pitch->value < 0;
+
+ Cvar_SetValue( "lookspring", ClampCvar( 0, 1, lookspring->value ) );
+ s_options_lookspring_box.curvalue = lookspring->value;
+
+ Cvar_SetValue( "lookstrafe", ClampCvar( 0, 1, lookstrafe->value ) );
+ s_options_lookstrafe_box.curvalue = lookstrafe->value;
+
+ Cvar_SetValue( "freelook", ClampCvar( 0, 1, freelook->value ) );
+ s_options_freelook_box.curvalue = freelook->value;
+
+ Cvar_SetValue( "crosshair", ClampCvar( 0, 3, crosshair->value ) );
+ s_options_crosshair_box.curvalue = crosshair->value;
+
+ Cvar_SetValue( "in_joystick", ClampCvar( 0, 1, in_joystick->value ) );
+ s_options_joystick_box.curvalue = in_joystick->value;
+
+ s_options_noalttab_box.curvalue = win_noalttab->value;
+}
+
+static void ControlsResetDefaultsFunc( void *unused )
+{
+ Cbuf_AddText ("exec default.cfg\n");
+ Cbuf_Execute();
+
+ ControlsSetMenuItemValues();
+}
+
+static void InvertMouseFunc( void *unused )
+{
+ if ( s_options_invertmouse_box.curvalue == 0 )
+ {
+ Cvar_SetValue( "m_pitch", fabs( m_pitch->value ) );
+ }
+ else
+ {
+ Cvar_SetValue( "m_pitch", -fabs( m_pitch->value ) );
+ }
+}
+
+static void LookspringFunc( void *unused )
+{
+ Cvar_SetValue( "lookspring", s_options_lookspring_box.curvalue );
+}
+
+static void LookstrafeFunc( void *unused )
+{
+ Cvar_SetValue( "lookstrafe", s_options_lookstrafe_box.curvalue );
+}
+
+static void UpdateVolumeFunc( void *unused )
+{
+ Cvar_SetValue( "s_volume", s_options_sfxvolume_slider.curvalue / 10 );
+}
+
+static void UpdateCDVolumeFunc( void *unused )
+{
+ Cvar_SetValue( "cd_nocd", !s_options_cdvolume_box.curvalue );
+}
+
+static void ConsoleFunc( void *unused )
+{
+ /*
+ ** the proper way to do this is probably to have ToggleConsole_f accept a parameter
+ */
+ extern void Key_ClearTyping( void );
+
+ if ( cl.attractloop )
+ {
+ Cbuf_AddText ("killserver\n");
+ return;
+ }
+
+ Key_ClearTyping ();
+ Con_ClearNotify ();
+
+ M_ForceMenuOff ();
+ cls.key_dest = key_console;
+}
+
+static void UpdateSoundQualityFunc( void *unused )
+{
+ if ( s_options_quality_list.curvalue )
+ {
+ Cvar_SetValue( "s_khz", 22 );
+ Cvar_SetValue( "s_loadas8bit", false );
+ }
+ else
+ {
+ Cvar_SetValue( "s_khz", 11 );
+ Cvar_SetValue( "s_loadas8bit", true );
+ }
+
+ Cvar_SetValue( "s_primary", s_options_compatibility_list.curvalue );
+
+ M_DrawTextBox( 8, 120 - 48, 36, 3 );
+ M_Print( 16 + 16, 120 - 48 + 8, "Restarting the sound system. This" );
+ M_Print( 16 + 16, 120 - 48 + 16, "could take up to a minute, so" );
+ M_Print( 16 + 16, 120 - 48 + 24, "please be patient." );
+
+ // the text box won't show up unless we do a buffer swap
+ re.EndFrame();
+
+ CL_Snd_Restart_f();
+}
+
+void Options_MenuInit( void )
+{
+ static const char *cd_music_items[] =
+ {
+ "disabled",
+ "enabled",
+ 0
+ };
+ static const char *quality_items[] =
+ {
+ "low", "high", 0
+ };
+
+ static const char *compatibility_items[] =
+ {
+ "max compatibility", "max performance", 0
+ };
+
+ static const char *yesno_names[] =
+ {
+ "no",
+ "yes",
+ 0
+ };
+
+ static const char *crosshair_names[] =
+ {
+ "none",
+ "cross",
+ "dot",
+ "angle",
+ 0
+ };
+
+ win_noalttab = Cvar_Get( "win_noalttab", "0", CVAR_ARCHIVE );
+
+ /*
+ ** configure controls menu and menu items
+ */
+ s_options_menu.x = viddef.width / 2;
+ s_options_menu.y = viddef.height / 2 - 58;
+ s_options_menu.nitems = 0;
+
+ s_options_sfxvolume_slider.generic.type = MTYPE_SLIDER;
+ s_options_sfxvolume_slider.generic.x = 0;
+ s_options_sfxvolume_slider.generic.y = 0;
+ s_options_sfxvolume_slider.generic.name = "effects volume";
+ s_options_sfxvolume_slider.generic.callback = UpdateVolumeFunc;
+ s_options_sfxvolume_slider.minvalue = 0;
+ s_options_sfxvolume_slider.maxvalue = 10;
+ s_options_sfxvolume_slider.curvalue = Cvar_VariableValue( "s_volume" ) * 10;
+
+ s_options_cdvolume_box.generic.type = MTYPE_SPINCONTROL;
+ s_options_cdvolume_box.generic.x = 0;
+ s_options_cdvolume_box.generic.y = 10;
+ s_options_cdvolume_box.generic.name = "CD music";
+ s_options_cdvolume_box.generic.callback = UpdateCDVolumeFunc;
+ s_options_cdvolume_box.itemnames = cd_music_items;
+ s_options_cdvolume_box.curvalue = !Cvar_VariableValue("cd_nocd");
+
+ s_options_quality_list.generic.type = MTYPE_SPINCONTROL;
+ s_options_quality_list.generic.x = 0;
+ s_options_quality_list.generic.y = 20;;
+ s_options_quality_list.generic.name = "sound quality";
+ s_options_quality_list.generic.callback = UpdateSoundQualityFunc;
+ s_options_quality_list.itemnames = quality_items;
+ s_options_quality_list.curvalue = !Cvar_VariableValue( "s_loadas8bit" );
+
+ s_options_compatibility_list.generic.type = MTYPE_SPINCONTROL;
+ s_options_compatibility_list.generic.x = 0;
+ s_options_compatibility_list.generic.y = 30;
+ s_options_compatibility_list.generic.name = "sound compatibility";
+ s_options_compatibility_list.generic.callback = UpdateSoundQualityFunc;
+ s_options_compatibility_list.itemnames = compatibility_items;
+ s_options_compatibility_list.curvalue = Cvar_VariableValue( "s_primary" );
+
+ s_options_sensitivity_slider.generic.type = MTYPE_SLIDER;
+ s_options_sensitivity_slider.generic.x = 0;
+ s_options_sensitivity_slider.generic.y = 50;
+ s_options_sensitivity_slider.generic.name = "mouse speed";
+ s_options_sensitivity_slider.generic.callback = MouseSpeedFunc;
+ s_options_sensitivity_slider.minvalue = 2;
+ s_options_sensitivity_slider.maxvalue = 22;
+
+ s_options_alwaysrun_box.generic.type = MTYPE_SPINCONTROL;
+ s_options_alwaysrun_box.generic.x = 0;
+ s_options_alwaysrun_box.generic.y = 60;
+ s_options_alwaysrun_box.generic.name = "always run";
+ s_options_alwaysrun_box.generic.callback = AlwaysRunFunc;
+ s_options_alwaysrun_box.itemnames = yesno_names;
+
+ s_options_invertmouse_box.generic.type = MTYPE_SPINCONTROL;
+ s_options_invertmouse_box.generic.x = 0;
+ s_options_invertmouse_box.generic.y = 70;
+ s_options_invertmouse_box.generic.name = "invert mouse";
+ s_options_invertmouse_box.generic.callback = InvertMouseFunc;
+ s_options_invertmouse_box.itemnames = yesno_names;
+
+ s_options_lookspring_box.generic.type = MTYPE_SPINCONTROL;
+ s_options_lookspring_box.generic.x = 0;
+ s_options_lookspring_box.generic.y = 80;
+ s_options_lookspring_box.generic.name = "lookspring";
+ s_options_lookspring_box.generic.callback = LookspringFunc;
+ s_options_lookspring_box.itemnames = yesno_names;
+
+ s_options_lookstrafe_box.generic.type = MTYPE_SPINCONTROL;
+ s_options_lookstrafe_box.generic.x = 0;
+ s_options_lookstrafe_box.generic.y = 90;
+ s_options_lookstrafe_box.generic.name = "lookstrafe";
+ s_options_lookstrafe_box.generic.callback = LookstrafeFunc;
+ s_options_lookstrafe_box.itemnames = yesno_names;
+
+ s_options_freelook_box.generic.type = MTYPE_SPINCONTROL;
+ s_options_freelook_box.generic.x = 0;
+ s_options_freelook_box.generic.y = 100;
+ s_options_freelook_box.generic.name = "free look";
+ s_options_freelook_box.generic.callback = FreeLookFunc;
+ s_options_freelook_box.itemnames = yesno_names;
+
+ s_options_crosshair_box.generic.type = MTYPE_SPINCONTROL;
+ s_options_crosshair_box.generic.x = 0;
+ s_options_crosshair_box.generic.y = 110;
+ s_options_crosshair_box.generic.name = "crosshair";
+ s_options_crosshair_box.generic.callback = CrosshairFunc;
+ s_options_crosshair_box.itemnames = crosshair_names;
+/*
+ s_options_noalttab_box.generic.type = MTYPE_SPINCONTROL;
+ s_options_noalttab_box.generic.x = 0;
+ s_options_noalttab_box.generic.y = 110;
+ s_options_noalttab_box.generic.name = "disable alt-tab";
+ s_options_noalttab_box.generic.callback = NoAltTabFunc;
+ s_options_noalttab_box.itemnames = yesno_names;
+*/
+ s_options_joystick_box.generic.type = MTYPE_SPINCONTROL;
+ s_options_joystick_box.generic.x = 0;
+ s_options_joystick_box.generic.y = 120;
+ s_options_joystick_box.generic.name = "use joystick";
+ s_options_joystick_box.generic.callback = JoystickFunc;
+ s_options_joystick_box.itemnames = yesno_names;
+
+ s_options_customize_options_action.generic.type = MTYPE_ACTION;
+ s_options_customize_options_action.generic.x = 0;
+ s_options_customize_options_action.generic.y = 140;
+ s_options_customize_options_action.generic.name = "customize controls";
+ s_options_customize_options_action.generic.callback = CustomizeControlsFunc;
+
+ s_options_defaults_action.generic.type = MTYPE_ACTION;
+ s_options_defaults_action.generic.x = 0;
+ s_options_defaults_action.generic.y = 150;
+ s_options_defaults_action.generic.name = "reset defaults";
+ s_options_defaults_action.generic.callback = ControlsResetDefaultsFunc;
+
+ s_options_console_action.generic.type = MTYPE_ACTION;
+ s_options_console_action.generic.x = 0;
+ s_options_console_action.generic.y = 160;
+ s_options_console_action.generic.name = "go to console";
+ s_options_console_action.generic.callback = ConsoleFunc;
+
+ ControlsSetMenuItemValues();
+
+ Menu_AddItem( &s_options_menu, ( void * ) &s_options_sfxvolume_slider );
+ Menu_AddItem( &s_options_menu, ( void * ) &s_options_cdvolume_box );
+ Menu_AddItem( &s_options_menu, ( void * ) &s_options_quality_list );
+ Menu_AddItem( &s_options_menu, ( void * ) &s_options_compatibility_list );
+ Menu_AddItem( &s_options_menu, ( void * ) &s_options_sensitivity_slider );
+ Menu_AddItem( &s_options_menu, ( void * ) &s_options_alwaysrun_box );
+ Menu_AddItem( &s_options_menu, ( void * ) &s_options_invertmouse_box );
+ Menu_AddItem( &s_options_menu, ( void * ) &s_options_lookspring_box );
+ Menu_AddItem( &s_options_menu, ( void * ) &s_options_lookstrafe_box );
+ Menu_AddItem( &s_options_menu, ( void * ) &s_options_freelook_box );
+ Menu_AddItem( &s_options_menu, ( void * ) &s_options_crosshair_box );
+ Menu_AddItem( &s_options_menu, ( void * ) &s_options_joystick_box );
+ Menu_AddItem( &s_options_menu, ( void * ) &s_options_customize_options_action );
+ Menu_AddItem( &s_options_menu, ( void * ) &s_options_defaults_action );
+ Menu_AddItem( &s_options_menu, ( void * ) &s_options_console_action );
+}
+
+void Options_MenuDraw (void)
+{
+ M_Banner( "m_banner_options" );
+ Menu_AdjustCursor( &s_options_menu, 1 );
+ Menu_Draw( &s_options_menu );
+}
+
+const char *Options_MenuKey( int key )
+{
+ return Default_MenuKey( &s_options_menu, key );
+}
+
+void M_Menu_Options_f (void)
+{
+ Options_MenuInit();
+ M_PushMenu ( Options_MenuDraw, Options_MenuKey );
+}
+
+/*
+=======================================================================
+
+VIDEO MENU
+
+=======================================================================
+*/
+
+void M_Menu_Video_f (void)
+{
+ VID_MenuInit();
+ M_PushMenu( VID_MenuDraw, VID_MenuKey );
+}
+
+/*
+=============================================================================
+
+END GAME MENU
+
+=============================================================================
+*/
+static int credits_start_time;
+static const char **credits;
+static char *creditsIndex[256];
+static char *creditsBuffer;
+static const char *idcredits[] =
+{
+ "+QUAKE II BY ID SOFTWARE",
+ "",
+ "+PROGRAMMING",
+ "John Carmack",
+ "John Cash",
+ "Brian Hook",
+ "",
+ "+ART",
+ "Adrian Carmack",
+ "Kevin Cloud",
+ "Paul Steed",
+ "",
+ "+LEVEL DESIGN",
+ "Tim Willits",
+ "American McGee",
+ "Christian Antkow",
+ "Paul Jaquays",
+ "Brandon James",
+ "",
+ "+BIZ",
+ "Todd Hollenshead",
+ "Barrett (Bear) Alexander",
+ "Donna Jackson",
+ "",
+ "",
+ "+SPECIAL THANKS",
+ "Ben Donges for beta testing",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "+ADDITIONAL SUPPORT",
+ "",
+ "+LINUX PORT AND CTF",
+ "Dave \"Zoid\" Kirsch",
+ "",
+ "+CINEMATIC SEQUENCES",
+ "Ending Cinematic by Blur Studio - ",
+ "Venice, CA",
+ "",
+ "Environment models for Introduction",
+ "Cinematic by Karl Dolgener",
+ "",
+ "Assistance with environment design",
+ "by Cliff Iwai",
+ "",
+ "+SOUND EFFECTS AND MUSIC",
+ "Sound Design by Soundelux Media Labs.",
+ "Music Composed and Produced by",
+ "Soundelux Media Labs. Special thanks",
+ "to Bill Brown, Tom Ozanich, Brian",
+ "Celano, Jeff Eisner, and The Soundelux",
+ "Players.",
+ "",
+ "\"Level Music\" by Sonic Mayhem",
+ "www.sonicmayhem.com",
+ "",
+ "\"Quake II Theme Song\"",
+ "(C) 1997 Rob Zombie. All Rights",
+ "Reserved.",
+ "",
+ "Track 10 (\"Climb\") by Jer Sypult",
+ "",
+ "Voice of computers by",
+ "Carly Staehlin-Taylor",
+ "",
+ "+THANKS TO ACTIVISION",
+ "+IN PARTICULAR:",
+ "",
+ "John Tam",
+ "Steve Rosenthal",
+ "Marty Stratton",
+ "Henk Hartong",
+ "",
+ "Quake II(tm) (C)1997 Id Software, Inc.",
+ "All Rights Reserved. Distributed by",
+ "Activision, Inc. under license.",
+ "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. All",
+ "other trademarks and trade names are",
+ "properties of their respective owners.",
+ 0
+};
+
+static const char *xatcredits[] =
+{
+ "+QUAKE II MISSION PACK: THE RECKONING",
+ "+BY",
+ "+XATRIX ENTERTAINMENT, INC.",
+ "",
+ "+DESIGN AND DIRECTION",
+ "Drew Markham",
+ "",