From 8de09a56d50d2890b8a6f4e6e241243c70cbb065 Mon Sep 17 00:00:00 2001 From: vermeulenl Date: Tue, 2 Nov 2004 02:58:10 +0000 Subject: [PATCH] Updating git-svn-id: svn://svn.icculus.org/nexuiz/trunk@232 f962a42d-fe04-0410-a3ab-8c8b0445ebaa --- qcsrc/Nexuiz.dsp | 281 ++++++ qcsrc/Nexuiz.dsw | 29 + qcsrc/Nexuiz.opt | Bin 0 -> 51712 bytes qcsrc/gamec/GameC.dsp | 325 +++++++ qcsrc/gamec/GameC.dsw | 29 + qcsrc/gamec/GameC.opt | Bin 0 -> 58880 bytes qcsrc/gamec/Makefile | 5 + qcsrc/gamec/bot.c | 1237 ++++++++++++++++++++++++++ qcsrc/gamec/bot_ai.c | 1010 +++++++++++++++++++++ qcsrc/gamec/bot_ed.c | 1367 +++++++++++++++++++++++++++++ qcsrc/gamec/bot_fight.c | 458 ++++++++++ qcsrc/gamec/bot_maps.c | 152 ++++ qcsrc/gamec/bot_misc.c | 777 ++++++++++++++++ qcsrc/gamec/bot_move.c | 512 +++++++++++ qcsrc/gamec/bot_phys.c | 678 ++++++++++++++ qcsrc/gamec/bot_way.c | 1001 +++++++++++++++++++++ qcsrc/gamec/builtins.h | 59 ++ qcsrc/gamec/cl_aliases.c | 32 + qcsrc/gamec/cl_client.c | 477 ++++++++++ qcsrc/gamec/cl_impulse.c | 224 +++++ qcsrc/gamec/cl_physics.c | 192 ++++ qcsrc/gamec/cl_player.c | 254 ++++++ qcsrc/gamec/cl_weaponanimations.c | 25 + qcsrc/gamec/cl_weapons.c | 175 ++++ qcsrc/gamec/cl_weaponsystem.c | 251 ++++++ qcsrc/gamec/constants.h | 188 ++++ qcsrc/gamec/defs.h | 74 ++ qcsrc/gamec/extensions.h | 907 +++++++++++++++++++ qcsrc/gamec/g_casings.c | 91 ++ qcsrc/gamec/g_damage.c | 193 ++++ qcsrc/gamec/g_decors.c | 247 ++++++ qcsrc/gamec/g_subs.c | 462 ++++++++++ qcsrc/gamec/g_tetris.c | 746 ++++++++++++++++ qcsrc/gamec/g_triggers.c | 392 +++++++++ qcsrc/gamec/g_violence.c | 41 + qcsrc/gamec/g_world.c | 141 +++ qcsrc/gamec/sv_main.c | 19 + qcsrc/gamec/sv_stats.c | 5 + qcsrc/gamec/sys.h | 145 +++ qcsrc/gamec/t_halflife.c | 58 ++ qcsrc/gamec/t_items.c | 148 ++++ qcsrc/gamec/t_jumppads.c | 80 ++ qcsrc/gamec/t_plats.c | 446 ++++++++++ qcsrc/gamec/t_quake.c | 1 + qcsrc/gamec/t_quake3.c | 16 + qcsrc/gamec/t_teleporters.c | 66 ++ qcsrc/gamec/w_common.c | 587 +++++++++++++ qcsrc/gamec/w_crylink.c | 103 +++ qcsrc/gamec/w_electro.c | 299 +++++++ qcsrc/gamec/w_grenadelauncher.c | 170 ++++ qcsrc/gamec/w_hagar.c | 179 ++++ qcsrc/gamec/w_laser.c | 135 +++ qcsrc/gamec/w_nex.c | 109 +++ qcsrc/gamec/w_rocketlauncher.c | 151 ++++ qcsrc/gamec/w_shotgun.c | 81 ++ qcsrc/gamec/w_uzi.c | 123 +++ qcsrc/progdefs.h | 143 +++ qcsrc/progs.src | 62 ++ qcsrc/todo.txt | 130 +++ 59 files changed, 16288 insertions(+) create mode 100644 qcsrc/Nexuiz.dsp create mode 100644 qcsrc/Nexuiz.dsw create mode 100644 qcsrc/Nexuiz.opt create mode 100644 qcsrc/gamec/GameC.dsp create mode 100644 qcsrc/gamec/GameC.dsw create mode 100644 qcsrc/gamec/GameC.opt create mode 100644 qcsrc/gamec/Makefile create mode 100644 qcsrc/gamec/bot.c create mode 100644 qcsrc/gamec/bot_ai.c create mode 100644 qcsrc/gamec/bot_ed.c create mode 100644 qcsrc/gamec/bot_fight.c create mode 100644 qcsrc/gamec/bot_maps.c create mode 100644 qcsrc/gamec/bot_misc.c create mode 100644 qcsrc/gamec/bot_move.c create mode 100644 qcsrc/gamec/bot_phys.c create mode 100644 qcsrc/gamec/bot_way.c create mode 100644 qcsrc/gamec/builtins.h create mode 100644 qcsrc/gamec/cl_aliases.c create mode 100644 qcsrc/gamec/cl_client.c create mode 100644 qcsrc/gamec/cl_impulse.c create mode 100644 qcsrc/gamec/cl_physics.c create mode 100644 qcsrc/gamec/cl_player.c create mode 100644 qcsrc/gamec/cl_weaponanimations.c create mode 100644 qcsrc/gamec/cl_weapons.c create mode 100644 qcsrc/gamec/cl_weaponsystem.c create mode 100644 qcsrc/gamec/constants.h create mode 100644 qcsrc/gamec/defs.h create mode 100644 qcsrc/gamec/extensions.h create mode 100644 qcsrc/gamec/g_casings.c create mode 100644 qcsrc/gamec/g_damage.c create mode 100644 qcsrc/gamec/g_decors.c create mode 100644 qcsrc/gamec/g_subs.c create mode 100644 qcsrc/gamec/g_tetris.c create mode 100644 qcsrc/gamec/g_triggers.c create mode 100644 qcsrc/gamec/g_violence.c create mode 100644 qcsrc/gamec/g_world.c create mode 100644 qcsrc/gamec/sv_main.c create mode 100644 qcsrc/gamec/sv_stats.c create mode 100644 qcsrc/gamec/sys.h create mode 100644 qcsrc/gamec/t_halflife.c create mode 100644 qcsrc/gamec/t_items.c create mode 100644 qcsrc/gamec/t_jumppads.c create mode 100644 qcsrc/gamec/t_plats.c create mode 100644 qcsrc/gamec/t_quake.c create mode 100644 qcsrc/gamec/t_quake3.c create mode 100644 qcsrc/gamec/t_teleporters.c create mode 100644 qcsrc/gamec/w_common.c create mode 100644 qcsrc/gamec/w_crylink.c create mode 100644 qcsrc/gamec/w_electro.c create mode 100644 qcsrc/gamec/w_grenadelauncher.c create mode 100644 qcsrc/gamec/w_hagar.c create mode 100644 qcsrc/gamec/w_laser.c create mode 100644 qcsrc/gamec/w_nex.c create mode 100644 qcsrc/gamec/w_rocketlauncher.c create mode 100644 qcsrc/gamec/w_shotgun.c create mode 100644 qcsrc/gamec/w_uzi.c create mode 100644 qcsrc/progdefs.h create mode 100644 qcsrc/progs.src create mode 100644 qcsrc/todo.txt diff --git a/qcsrc/Nexuiz.dsp b/qcsrc/Nexuiz.dsp new file mode 100644 index 000000000..45fe4d757 --- /dev/null +++ b/qcsrc/Nexuiz.dsp @@ -0,0 +1,281 @@ +# Microsoft Developer Studio Project File - Name="Game code" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=Game code - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "GameC.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "GameC.mak" CFG="Game code - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Game code - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "Game code - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Game code - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Game_code___Win32_Release" +# PROP BASE Intermediate_Dir "Game_code___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Game_code___Win32_Release" +# PROP Intermediate_Dir "Game_code___Win32_Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GAMECODE_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GAMECODE_EXPORTS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x419 /d "NDEBUG" +# ADD RSC /l 0x419 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 + +!ELSEIF "$(CFG)" == "Game code - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GAMECODE_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GAMECODE_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x419 /d "_DEBUG" +# ADD RSC /l 0x419 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "Game code - Win32 Release" +# Name "Game code - Win32 Debug" +# Begin Group "Header Files" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\builtins.h +# End Source File +# Begin Source File + +SOURCE=.\constants.h +# End Source File +# Begin Source File + +SOURCE=.\defs.h +# End Source File +# Begin Source File + +SOURCE=.\extensions.h +# End Source File +# Begin Source File + +SOURCE=.\sys.h +# End Source File +# End Group +# Begin Group "Source Files" + +# PROP Default_Filter "" +# Begin Group "client" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\cl_aliases.c +# End Source File +# Begin Source File + +SOURCE=.\cl_client.c +# End Source File +# Begin Source File + +SOURCE=.\cl_impulse.c +# End Source File +# Begin Source File + +SOURCE=.\cl_physics.c +# End Source File +# Begin Source File + +SOURCE=.\cl_player.c +# End Source File +# Begin Source File + +SOURCE=.\cl_weaponanimations.c +# End Source File +# Begin Source File + +SOURCE=.\cl_weaponsystem.c +# End Source File +# End Group +# Begin Group "server" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\sv_main.c +# End Source File +# Begin Source File + +SOURCE=.\sv_stats.c +# End Source File +# End Group +# Begin Group "frikbot" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\bot.c +# End Source File +# Begin Source File + +SOURCE=.\bot_ai.c +# End Source File +# Begin Source File + +SOURCE=.\bot_ed.c +# End Source File +# Begin Source File + +SOURCE=.\bot_fight.c +# End Source File +# Begin Source File + +SOURCE=.\bot_maps.c +# End Source File +# Begin Source File + +SOURCE=.\bot_misc.c +# End Source File +# Begin Source File + +SOURCE=.\bot_move.c +# End Source File +# Begin Source File + +SOURCE=.\bot_phys.c +# End Source File +# Begin Source File + +SOURCE=.\bot_way.c +# End Source File +# End Group +# Begin Group "game" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\g_casings.c +# End Source File +# Begin Source File + +SOURCE=.\g_damage.c +# End Source File +# Begin Source File + +SOURCE=.\g_decors.c +# End Source File +# Begin Source File + +SOURCE=.\g_subs.c +# End Source File +# Begin Source File + +SOURCE=.\g_tetris.c +# End Source File +# Begin Source File + +SOURCE=.\g_triggers.c +# End Source File +# Begin Source File + +SOURCE=.\g_violence.c +# End Source File +# Begin Source File + +SOURCE=.\g_world.c +# End Source File +# End Group +# Begin Group "map" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\t_halflife.c +# End Source File +# Begin Source File + +SOURCE=.\t_items.c +# End Source File +# Begin Source File + +SOURCE=.\t_jumppads.c +# End Source File +# Begin Source File + +SOURCE=.\t_plats.c +# End Source File +# Begin Source File + +SOURCE=.\t_quake.c +# End Source File +# Begin Source File + +SOURCE=.\t_quake3.c +# End Source File +# Begin Source File + +SOURCE=.\t_teleporters.c +# End Source File +# End Group +# Begin Group "weapons" + +# PROP Default_Filter "" +# End Group +# End Group +# Begin Source File + +SOURCE=..\progs.src +# End Source File +# Begin Source File + +SOURCE=..\todo.txt +# End Source File +# End Target +# End Project diff --git a/qcsrc/Nexuiz.dsw b/qcsrc/Nexuiz.dsw new file mode 100644 index 000000000..80c73ac6f --- /dev/null +++ b/qcsrc/Nexuiz.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Game code"=.\gamec\GameC.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/qcsrc/Nexuiz.opt b/qcsrc/Nexuiz.opt new file mode 100644 index 0000000000000000000000000000000000000000..6765a954697372159d29ffb85c0d1fe2f661bc48 GIT binary patch literal 51712 zcmeHQOK=>=c^>d35PDg%EJd=^@S*)_FT$L(cd`VKNq*A5in1iZvNiI3)kW()4DY+&-Ij&01IV54e|L>XE*(E@c z3M3>|J>-wso$2Yv|DJ#U?*92dJoV3i{#P&ji!*h+?HqOf`|e5SMD6+vJ`?cqB?tHK z-lfmwnLgu^J^mkI;J?sU0lFOp90ME&5X~O}%mPjV<^YcZh|Z7W`~=`h09e3z3h*@G z%YbJ9Ujb0L=WzZt!1I6?0AB@s4e;xLuLHgTpmM7?&jU^Yz6tmZz_$P|0bT~Y0{Be; zm0iHO0cZkF1I_@>0?q+m1Dpp?xz}<2ZNM9VHvty_zXNy+@NK}`04nz#oEHI0fEHjG z&<3mkE&?tAs2ss}@%b{~3g9Z>8sIwMdw?~-I)KW(i}MY@Cg455O~Cg7?*p~~9{_#; z*arL%AORhK3!vw}=wr>%_?I~XBad)SoDq8R;K#Gzn6l4DKIO6R{M+My`{PgkZidEL z7TrYaiL>LRP7GYQjzUk?(GLkwWug0=g>!^?lz5l=ai5~qE$$~VD~=_!uAL#;SNQbs z$CsQVGe_~*4B$cAe~g+b+HX6a<72FZh0k(4f%ZvWsckwQq>g8)P|^Mj=tq4&*^{9X zuJ+XV(qC=7Ol~~ICz!Ra0@&zFykFG`IM;5b8x_zqk zdZN*Zv^nQ>dJCIW$fJsSsCa>z6t0ZWDvF5Pi*Du*WTvjDP$#2JKl1eO4prj$X&lHA zH*9W-A>VDzWxLtJ&7zt_#Y$#eDv)U=R!|1m2}A0jAMp)?6`?#o(+Lfe+ooaOrs7xC zZg$7ix`9znmZq+%Fb~j0v9z|@UU_-r$}2qjcAWZwjs#t3ij@*c8(Ifxfh0z!64&C) z*HOAqAT03R9nsQZEHl632Yxnc&H)(M6A@0zbmFI#Ms`P~B;Yl0@}q6{xb$Q{ef=!qc*E1dS2jV1Vk`!0Q%(efMYbqs%=4XIvX6}k74e8sdE2yUiyIt+(spv>h z%3zBHi?D?jeYar6v1l;cb@Z+PLlBNKa6v~Vrbon8v5p&g%zOa^WRQtIh->-)Su8j< z6=|$ozl#>oY}N;7S4>}{Mh+=KXTk0g;sd^5*(JUrs!;pyQi)EUW#~`QC5Uw7$pm6s z_KoCdNp&^Ypp;6t4;Df9ArdrL=!LlNyI@@^5awvH>nHe|ssQZ7P5GG$`SwsoxI!KC z{4RL9E~W(;FX|0V;?_iUA~^E!T2&)GiEvHH;ffUQP05FL>ksbit3?` z68;;0)L8KYP%xC4+rPj1AuSiEMw+P@Y68u5^Jt7q?k?f-cENZmUPU4BCJE{kZ>~nE zO0q@Q1x^wP6|*5bfnr{B6DTJIA%Q{$t*R1@jEX=cjk1}<)xf915xV6Dxu-nQSJDHI zbTKv}bmI5=S;H;KU6A)wQQXKQ;R<{Z6@)6;;#!PL%iwJ&aS)jG(wK>BJUS^#1y}U- zPz+?^Q{Pi2huSrmA{HcVQYdZoxlp~ELRJ0)tJy>m0>U(N?(aX3p=rQ3yceKdnwVwR1r@vD^!eb=!m&t+yk$=LE4b|mAG6dRh@5!?&uM}JHM3xe5GFTJCZwp1`)SVqQ60!&5*PQDFUL}Fhmf$t$RaOTn4V~5Em6eu|aSRcG zjV<(}!Fiz{Z?v}C?`^CtUT!a5rDos*k;?)`O-Erz2Tk}sL_03-gCqGWj4O*|DiUye zFlyu}nvZp28YxIw6W7$( zm>Vl;ProQ`_-PKNZ@v1e7_t!=s6fYr2$rYu?fZ$tJBx`}2Mh3^7Q$=pQBEP_)CG(baw)DmVV%EOLIjB}|$_5^}$v;&ApLQalLfnJcR zt97OzA$6TB1HT8SR7Y7tei&rBQk29;>yeBbnQmN6de z9tw{wByl&;2$~1R>p$gpp=Lo3QYuYB4r$#RWhFy)A3~T6jc0$4nw{XQ>!dPCIgQx~ z_l*S~>%d9u6%~{(2G{Z|&a-`949v~SN7~PE+|WH3`z09Tomhj7xJwyd1VeSI>r<*% zY>*=CL%dxu#!x3aX=&G5q=ey7xjXRcwSJL()>Y&C-Kb1tVd)BEEb8WOY?ox70sFVe zOK^3Nho}VJ2&CSJUMEIo1rJ8{bPmINkKy|)@(4SwPMEu7Xnp-grRUZ=2G+?&EBBZ%->M*{o6sT#m-3ZMZZ_*Nv8 zumOR2@f-N5PmU7Z6HvoyAcGwI>A`aJ@x7z^65N!)u1UBckT`y~{E(atR8jaXW1odg zd>~z7e3_=u6zm68EYIYXMHjhEA{|LDUQy{GP-RFuff_2^-O^>o*s6x6U&W%L&T|kp zs4Owhj{mpM&a;|D6B>XUh)S@&nl=gvce&NImVs75*GE>LU&-$bg@oIwiNglq8 zMj|3_4Gu{4U?$TY&~1qKgg$gVur68a^9w!qq;zvd00D$a1RKCCm_EVmgZYSqJ?L{d zKi)vX26?c!@Xf=N?oxCDMO`{;jG$y!B-|BxziX<4ymTd0!HRA76+GUNGq03vGAvfSLh2G)1fK z>(95iqH!(rG!T|C(uNA|F1>_;k*neG+ZDTuuwm#xWpRzGO+ExMDqant6wrLA$XHey z$$Z5-5G(^#z>W`I22ir_OZc%Qcj4oiR*SF-RGEgK1e_DeD|A+P#JkJ;ADEZFj>6jl zxm;YbD&fHo&D8rJm})nv79t2x+#nR+ga5Je1Fguwi&S<^F&9A`Ghjq;{MZCuh_d_B zFYLM%{iNr?r__PiKs=)$4VY@|3cf_Ij`Ug#J_Ly=9x=bQ6&XQfSt_13@4z4yU37H< zCXUJOf(^-uhAL1YD!Y!jPfXOLmy_D~+vM?*7{V+=c5=8lrFLT3r*FnZFSHaXH)s}E zU^F-q+J*lQo+0JNoj5xFMTcT|HvVT+y^a6b_+J@Zo(x(PE)WNB)@=Nbyj2_jtCX;f z|JnE-%xm#3%5Tax{%7NVHvVVhe>VPSFkwjHvbQ= zbTfKv*=eb8~!3@ ze1|;y+eN$H1I_)WYQBc~-(}kmI{*3AJ=&*BpXHeQ4DJ6GHB^khJ zh)E&g{gI)P$ek^^adNngUSYz5Yq~Z4{Zreok9Df=cK)}W|9x|EvXssLwDZ3?<9>4a z37h|E^FM9=r=9<8=YN-r71;UTn384Ze^--X?fh?>|2e&eip~GD^S|x+4HvNJo9tX>G^^s~W6gQv7}o*B+UO7|vkXTJG+}MCWl)>G9cT zmfzW&2qx}Xu81)1dC$~w0-;7MFmj&~k8~4%r=zp`6gk#I;PpQDkA@N}YwkOaG5~s? zBBrzTzjpn{+A^UW#ap-QKbl1kiY3#qw1-{)5$j{%|B7Az(XRh!*MGF@Kic&lYs*90 z^&jo}kCe1}Z^FA>{}HQd(V~!c{YP4dtX!Lp^EvGLkGyELUH{Rp|7h2L#JiPU|MCCA z`j2J&Z|~h}D4%n@#fy#qQDD->|ICW7yk(5t|HbbAV%L8hFT!i{|7`xB&HuCce>VTm z=Kq;(JM8{1!tVcKHYX~!-m>|BHviA=|6=!l(RTkAyZ_5!@BdQn|9tl@=i9Y{bS3cC z1Kk-<3Z@J@-s0KD|7`q^_A0aSzhVPlyZ;MTuCn{T)OG{5@jn~?v++M0|FiKw8~?NM zKfC`k?+ZP(XR(d{+4!H0|JnGTjsMyGpAUWi=Vh!p*l}{FhkwdRDAxB-$J3u-{lj;t zg<}1~Wvuq-qJ3KXF>*4jgZOaAGgyCe7LObUJm~t58`OlLKL>L>?D~@j)9SdE|4sXI z*#E~ICH9Wt>xoUuRXjckM8TAlqH5yyB^|0eO|jRr3jWkS zPhCbm?Z~^c|1REk{&%sorJesRI=LTsLM4fv|82q-r|kT1JOA6x|F-kL?fmbEe)!H3 z9i-%!Ozev{v3*Y`&oV5IT&$05^FM9=r?mN>Dz)>!?fh@)@e+TuAg+!7+W4=H{~mh$ z_XO5ie3HVa$DVNhxD4v?xplhr>Z=Z-fx^&W|>?u&hikZKlcR!vT*@4gQOkgP&s1e*rjEJC}I)YRLh^?OviH z>8U{Gk=v(jj}Al}BD6&C1b!7DahN+~Wc&bq`49k2QJwLTk0-~-$Zz=vgn%VfP4Ej0 zo)B>IkP(32V4N!e;)e2!k9<`6V7Q(3b=J$H146>HsGi`j3na`QG7@g1FMkLiB$Q`- z?l`5FfbB^|wdGMN{76W_IW@wr|7h2LwCg|G^&cta$<>D*|8tJ~_{b3_ z6Cd6B<;?6SA3K?kw+6B1_xf3bf8s2E$K-r0nm?TU==u# zvRx7L>nao2<2j?rp>VpO44d;j>%mNiKrL!lxjD6k8U`w*3^8nzmk6^&im4431nt$~ zhoSO(R1}QR0yVA#lEU&JODXA#pF@ExeR0m+-K}!VawMK1f{CVF6?0R4^by18QEU8J zi9E{FTn10#n$O7v8u65lQN#b#V|5tpG>Q_%&p z%cSFHiA+W!8^!cnSlLs&xaY_?@*_XDIZM>BKy@fpofo z_Pu(xQfhV(%VTuhqcL_99fEdBV3(Sd5Dioro(p5_sGV?9UZim;SrDPzQ6kL~MdKHU zDk+SLQK8Yf>&r_#r0cCqqFBnAy1&6ofDci;llP{Z;i#EN@~Dyd*jzmDJ3M-#XI}*| zx6{S$8u%t-7KGdo3ApbBD%^({t4wdgk#<1?LX-wl*}i(9!9aP23T8>Z0=qms8oZ~{ zEMZJCRp?R&lsAuvfuCexhB0xdx5P$Nj#$YXMKbi=ZEy=sb5)51O}M61c9n>AFhY0Y zK3Jg|IhC!^o2o?M4nq{(K+6oIY8a`g_!Ok{vpzSvU1S_|V^Q+cWbc5jfe(RXF^PdU z$QmjIxrysyqG&Oysshel@Dz|wY$C<>HWhP;?Izo3@0#38;^CZAF!#g*2|h73__0Ju z3Vvr9)d3L%x}rh{cT4+GM(j>?OfA3JxdQkH=Q$f(7>S3YJ*F zV0dJ}TgFUc6dBiQn5Lu`GGT!t2q6$h9Ku=&I!Uw|X*L3Lls5|2xw6s%P9hJpDjBzn ztqi>W9(9;9QuvGoS%K81fO;UNMP-Z7K($f}7$}AZTwxK1ny#ZAy%uUDUX8H1RaQOS zs@e!cA^>X^Vue`8jXZ`7MmbeSmzJT#bWgOQj-jnc=d(&>J?!^W5$x+~q!^QWW3C=^DHfMziA6D$#?pnff$-pSQRm$XPkRwQ=)$d-dwdwdSR_4UeGxN)MTahdv0^0UcekU&?FF4cxJH z>m)S6mb-n!XA?$D3VRE}#j;08ro z_~`|)74@?$zHs{Va5!v2|0X)s-K+^MaC*RFcTdl`x5l`Ud8VoVq|?YYc=R^LoXg`f zhRql#W`IiF&aa1rzg2!>zGN8zzKJkWa*S>kJ_(zytnZ`yhObI|Z^#aqNp?zsxW-lu z2xJzY9{w%MN9r z)EKTVETycFq4_kvGfj&2F3ed3`$3Kddr0svF|(ngp#D-7OE?O^Xr=GiNKkp015&<) uS4EneT}d)g +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=Game code - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "GameC.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "GameC.mak" CFG="Game code - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Game code - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "Game code - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Game code - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Game_code___Win32_Release" +# PROP BASE Intermediate_Dir "Game_code___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Game_code___Win32_Release" +# PROP Intermediate_Dir "Game_code___Win32_Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GAMECODE_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GAMECODE_EXPORTS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x419 /d "NDEBUG" +# ADD RSC /l 0x419 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 + +!ELSEIF "$(CFG)" == "Game code - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GAMECODE_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GAMECODE_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x419 /d "_DEBUG" +# ADD RSC /l 0x419 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "Game code - Win32 Release" +# Name "Game code - Win32 Debug" +# Begin Group "Header Files" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\builtins.h +# End Source File +# Begin Source File + +SOURCE=.\constants.h +# End Source File +# Begin Source File + +SOURCE=.\defs.h +# End Source File +# Begin Source File + +SOURCE=.\extensions.h +# End Source File +# Begin Source File + +SOURCE=.\sys.h +# End Source File +# End Group +# Begin Group "Source Files" + +# PROP Default_Filter "" +# Begin Group "client" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\cl_aliases.c +# End Source File +# Begin Source File + +SOURCE=.\cl_client.c +# End Source File +# Begin Source File + +SOURCE=.\cl_impulse.c +# End Source File +# Begin Source File + +SOURCE=.\cl_physics.c +# End Source File +# Begin Source File + +SOURCE=.\cl_player.c +# End Source File +# Begin Source File + +SOURCE=.\cl_weaponanimations.c +# End Source File +# Begin Source File + +SOURCE=.\cl_weapons.c +# End Source File +# Begin Source File + +SOURCE=.\cl_weaponsystem.c +# End Source File +# End Group +# Begin Group "server" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\sv_main.c +# End Source File +# Begin Source File + +SOURCE=.\sv_stats.c +# End Source File +# End Group +# Begin Group "frikbot" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\bot.c +# End Source File +# Begin Source File + +SOURCE=.\bot_ai.c +# End Source File +# Begin Source File + +SOURCE=.\bot_ed.c +# End Source File +# Begin Source File + +SOURCE=.\bot_fight.c +# End Source File +# Begin Source File + +SOURCE=.\bot_maps.c +# End Source File +# Begin Source File + +SOURCE=.\bot_misc.c +# End Source File +# Begin Source File + +SOURCE=.\bot_move.c +# End Source File +# Begin Source File + +SOURCE=.\bot_phys.c +# End Source File +# Begin Source File + +SOURCE=.\bot_way.c +# End Source File +# End Group +# Begin Group "game" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\g_casings.c +# End Source File +# Begin Source File + +SOURCE=.\g_damage.c +# End Source File +# Begin Source File + +SOURCE=.\g_decors.c +# End Source File +# Begin Source File + +SOURCE=.\g_subs.c +# End Source File +# Begin Source File + +SOURCE=.\g_tetris.c +# End Source File +# Begin Source File + +SOURCE=.\g_triggers.c +# End Source File +# Begin Source File + +SOURCE=.\g_violence.c +# End Source File +# Begin Source File + +SOURCE=.\g_world.c +# End Source File +# End Group +# Begin Group "map" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\t_halflife.c +# End Source File +# Begin Source File + +SOURCE=.\t_items.c +# End Source File +# Begin Source File + +SOURCE=.\t_jumppads.c +# End Source File +# Begin Source File + +SOURCE=.\t_plats.c +# End Source File +# Begin Source File + +SOURCE=.\t_quake.c +# End Source File +# Begin Source File + +SOURCE=.\t_quake3.c +# End Source File +# Begin Source File + +SOURCE=.\t_teleporters.c +# End Source File +# End Group +# Begin Group "weapons" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\w_common.c +# End Source File +# Begin Source File + +SOURCE=.\w_crylink.c +# End Source File +# Begin Source File + +SOURCE=.\w_electro.c +# End Source File +# Begin Source File + +SOURCE=.\w_grenadelauncher.c +# End Source File +# Begin Source File + +SOURCE=.\w_hagar.c +# End Source File +# Begin Source File + +SOURCE=.\w_laser.c +# End Source File +# Begin Source File + +SOURCE=.\w_nex.c +# End Source File +# Begin Source File + +SOURCE=.\w_rocketlauncher.c +# End Source File +# Begin Source File + +SOURCE=.\w_shotgun.c +# End Source File +# Begin Source File + +SOURCE=.\w_uzi.c +# End Source File +# End Group +# End Group +# Begin Source File + +SOURCE=..\progs.src +# End Source File +# Begin Source File + +SOURCE=..\todo.txt +# End Source File +# End Target +# End Project diff --git a/qcsrc/gamec/GameC.dsw b/qcsrc/gamec/GameC.dsw new file mode 100644 index 000000000..cfdee20ad --- /dev/null +++ b/qcsrc/gamec/GameC.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Game code"=.\GameC.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/qcsrc/gamec/GameC.opt b/qcsrc/gamec/GameC.opt new file mode 100644 index 0000000000000000000000000000000000000000..74a16e503b04fb2d1ddd81364d61bea6b1717130 GIT binary patch literal 58880 zcmeI*4VV=5eFyN_x#M2oct76pxPZI~citT*c!3K0VlgAnR{9*pr2#83ow0c|*9 z1Y#tj7%>W=rW=FtSj0HQc*F$6M8su?62v5gnr;cklMz!8mm{VkrXi*yW*}xFu0W`1 zXJcH3C`Vk0s6bRAst|J!)d)4+T#V--u13@#<|D2_EI?d~Scp*5EyB1KQHQ8UG$0xg zO^C&a>k(=?6`#lBWr!OP%MmLOD-ky$Rw0@ZYPvNTuSK*VZbEzku@2FSa3j_uZbq~r zHXuBRc7zwvfiMuA2p^&g;YV~MdJq9b5D`Lz5xt0w2({cx9adpZVJ)VWqU0frBid%n z@xq7E*ki0^>*smd%I#5tJ^9(s-~H46I2r*29BP+v0pfsivp) zQMK=?^*G=3>iN`qs+|=>9hM%}5?H>0M;AW~({xu3Ugknv=<J4`o)eZCAA)_zu-=aqCM$F@mhT{>h z5rq@ch}YeWP?LMzi}8Pbc}KKY%|gFi?flxSiHg!!61S~M>hs7C!z;-L;68E^zKc9y zZBl=coDaWD9uL1kE`xtUUIPzS713OuAUu`)B-~2g3-^$3Y)R^csuG*8Z-xhv4fry0 zA6!BH3-~7TJ~%{v9o|L$5&Wm*sq2#ZQFZVzmp>i;5qU2B4tWXeQU@Ay`i<~R^6hX9 z`D^e7@*cRId;snvzX{(%{$F@6xv4d&pC+$`hp2;|xjqx!N&Wle68JE=3O+$@gin!! z@KkjGHRpF6-c5cReuR7)-bWt3KB<3)Tm!#LUIFK;gR?pRZg?_z3tUBh9`=*>!ad|y z;jQGu@O|Xd@ILbBo0IzY$hW}N>O^6#&zIoq$$DE--%s8Im#dSCIsKjRLh=##e)7M< zUnai`KTXcvkktQ{JR5$STnC>dx56Vg*vs#Mr;s0qzd(KxZYA%BBjiKyPV)QkGvq;@ zr2Zesgtz%yc;ei?|>JO zAAwhs55YUhzlNV9Pv}VMZ<1%h|3Q8U-lQ%N%;o>UNb2{dPLS#HjO!Pk)=fR~YH5CZcft$F&%&ML*WhjB+1;p*av&fIZ zE#!mncJlZhoL|X}@G0^dc(A%CHRt~%+)925UQgZ+N63fZo#gl7XUKyCN&O$l?eKVY z0cTZiPpxi)(ZKyWs1{--LtYvIP@pS&NAkPpN6k>7`(A?snBFUTX|gXB8+BsmJt zQXev`_ZMy@?}dBFKZL(R{x|pm@;mTX$*$g{{%!J1_{Zc%_!RkOc(nS!W3EpRJcWD< zTur_oUQGTwcnkRr_-^vJjd*{^6W}+=mGIBW4e&?g0DQUnFlMgb4tN21Y$T~C$<^?G zk(=Q`sgD5Z@z&uj|Bv8W@>hnBF})2kypY6+wJx1giFYMa0U4;coF%1_!;uA;pfP?JFtG_V)&oQ3*i4DuZD|u z*z5BUyd2K4ztM5wS;_yI*Kd;lll-6L|Lo(1 z`9HfpNd8aqf0F;R>x1O~B>yM*Kf69;Oa89_8NeHsEnS8+llV+B0SAV*cVc;b!|0=a0bNTgX)$c*;^C<2b;76$wS}E=w za8cX;qRW3BEk6hCzYA3}?o`|F!uo(G8eQu*5(5@Au2{8j_{Yzbdet*VH2wE;Kl^E~ zDroWt414C2TEdZ@Xs^d>lul1IB%eONw6e4}67DvkUtb5h+!3 zi54}d+N7tE=MDIcP%JNML^c_b{LYBKr#&3Y^`TffK~Haf!tnHlL*}~YC)&`iz1*8F z!zVQmFKY|>qh9Oj^h6meuG+Ut`)_IgE$zS6fh65tMB0B#`)_IgZPy2B|1IsmrTw>E zAEf=awEvd&-*$bF_TSR}TiSoy^+DQyOZ#tW|83WY-&OnX4JXn+{2XH^+saB?{GmBj zr42@V+?UD?nzwi?(CSyFvvZ#|^|;@j%PJ#ac%rF0jeXjybQJr<0QK`zHRKf(H#gU< z!p~6$Y4xi;5uXuT5;KCI_F{U;ClZ7%+_pBgx{D*>c&|Cr0R_bkOItpXJ3Q-jWi3V*|BLkhBK^PE=YQ${Mf!h{{$K3+ApO5c|1Z-2 zi(Mb2{}<{1Mf!iS>x1+Sh>!_xncy*;G=AL;)` z`v0-(gY^F+{r^b+KX!eP{(q$ZAL;+ct`E}xkM#c|{r}kYLHhrZ{(q$ZAGu7Uv$WhB&>;sT8c611T-POTvROYaE1U7MqRuT$k8 zjrm@NxXUd6)P63%D(fi3Vj8QzsiC;sooI`My*)-O;E9L4T}H&Ay&$tXT%HA6AzkL* z{(q3#3SAzb$Em_wi?FJ|lzuM!Hk_~2ZaEWSjjeSoQib=1gTZjfp|@V0BU1NzXRM0U(CAKF}HPcRF<@$UGB%F5AoB9S`>QdODms0Petseire8`Tmz?arsg8 zV{Lxi2;8Z{XCC(^WqaX!1D=>uh2M>L{UD+U*Wc7(&>r^=pr!XKN( zWyb}duWge*956!O&+5oOCX36D3m#uvA{+^Hd`1WU(OF#hRJHH$1U){d-uldI{ZUz8 zdczxb?2MOry;q#YrOz*SqZ@9AcKXay{m3jXIF9zJ|8c{q3ZHqNACbjnM}=2qR|!6c z-ucW5KRk;|k7KU#oy^bqG%y|=ERLr7|cklGmB&*fJ~;W3CSXl$+HNcwswx;cvMmP7SFvkDaT z^G2`=e713VCrE$C<iWk|v&5nJiV{K+D!!=A;KD4+o$Y;wofeF5iXDQ#T?l@nHS9k8Jj1Yej1`K9OM2#%#W|do%wl4=FX9y z#idur4*Vb><_Y1e0cVPz`R3Ya7$9FWE3$J?K1LwejB zi-aAza>)G9n487LAEk=#ix?qKhvD$AA~FvGIa$0JQoo7_;1b%YUqxhIJ?Z^i_~odR zn-Sw_Y*o`jwGYI%_&=lF-__4Ww~BeTajN!5yTUPF+^LVwnMo5Zi;JIn=W)q+k?%Y{ z5~kX`XZ|WQ^CQ#Qwz&LFwt#E_*#dt!Ti|q#D^JH4@55FebuAitRQ+6D2H66#1!N1z X7LY9 time) + return; + self.switch_wallhug = 0; + + b_temp2 = nextent(world); + cno = 0; + + while (cno < max_clients) + { + if ((!b_temp2.ishuman) && (active_clients & ClientBitFlag(cno))) + UpdateClient(b_temp2); + cno = cno + 1; + b_temp2 = nextent(b_temp2); + } +}; + +void() ClientInRankings = +{ + local float cno; + if (player_head) + player_head._last = self; + + self._next = player_head; + self._last = world; + player_head = self; + + if (!self.phys_obj) + { + b_temp2 = phys_head; + while (b_temp2 != world && b_temp2.owner != self) + b_temp2 = b_temp2._next; + self.phys_obj = b_temp2; + } + + if (self.ishuman == 2) + { + self.ishuman = FALSE; + return; + } + cno = self.colormap - 1; + BotInvalidClientNo (cno); + active_clients = active_clients | ClientBitFlag(cno); + + self.b_clientno = cno; + self.ishuman = TRUE; + self.switch_wallhug = time + 1; +}; + + +void() ClientDisconnected = +{ + if (player_head == self) + player_head = self._next; + if (self._next) + self._next._last = self._last; + if (self._last) + self._last._next = self._next; + + active_clients = active_clients - active_clients & ClientBitFlag(self.b_clientno); +}; +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +BotPreFrame & BotPostFrame, used to make the +bot easier to install + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ +float () BotPreFrame = +{ + if (self.b_clientno == -1) + return TRUE; + if (self.ishuman) + { + if (self.switch_wallhug) + ClientFixRankings(); + if (self.classname == "botcam") + return TRUE; + } + if (self.b_frags != self.frags) + { + + if (self.b_frags > self.frags) + { + if (pointcontents(self.origin) == CONTENT_LAVA) + bot_start_topic(10); + else + bot_start_topic(9); + } + else + bot_start_topic(2); + self.b_frags = self.frags; + } + DynamicWaypoint(); + return FALSE; +}; +float () BotPostFrame = +{ + if (self.b_clientno == -1) + return TRUE; + if (self.ishuman) + { + + if (waypoint_mode > WM_LOADED) + bot_menu_display(); + + BotImpulses(); + + if (botcam()) + return TRUE; + } + return FALSE; +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +Bot Chat code + +The rest of this code is in bot_misc.qc + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ +void(string h) BotSay = // simulate talking by composing a 'chat' message +{ + WriteByte(MSG_ALL, 8); + WriteByte(MSG_ALL, 1); + WriteString(MSG_ALL, self.netname); + WriteByte(MSG_ALL, 8); + WriteByte(MSG_ALL, 2); + WriteString(MSG_ALL, h); +}; +void() BotSayInit = +{ + WriteByte(MSG_ALL, 8); + WriteByte(MSG_ALL, 1); + WriteString(MSG_ALL, self.netname); +}; +void(string h) BotSay2 = +{ + WriteByte(MSG_ALL, 8); + WriteByte(MSG_ALL, 2); + WriteString(MSG_ALL, h); +}; +void(string h) BotSayTeam = +{ + local entity t; + if (!teamplay) + return; + t = player_head; + while(t) + { + if (t.team == self.team) + { + msg_entity = t; + WriteByte(MSG_ONE, 8); + WriteByte(MSG_ONE, 1); + WriteByte(MSG_ONE, 40); + WriteString(MSG_ONE, self.netname); + WriteByte(MSG_ONE, 8); + WriteByte(MSG_ONE, 2); + WriteByte(MSG_ONE, 41); + WriteString(MSG_ONE, h); + } + t = t._next; + } +}; +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +BotInit + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + + +void() BotInit = +{ + local entity ent, fisent; + local float numents; + + // spawn entities for the physics + ent = nextent(world); + max_clients = 0; + + while(ent != world) + { + max_clients = max_clients + 1; + ent = nextent(ent); + } + if (max_clients > 16) + max_clients = 16; + + ent = nextent(world); + fisent = world; + while (numents < max_clients) + { + + phys_head = spawn(); + if (fisent) + fisent._next = phys_head; + phys_head._last = fisent; + fisent = phys_head; + ent.phys_obj = phys_head; + phys_head.classname = "phys_obj"; + phys_head.owner = ent; + numents = numents + 1; + ent = nextent(ent); + } + precache_model("progs/s_light.spr"); + precache_model("progs/s_bubble.spr"); + // the bots return! + b_options = cvar("saved1"); + if (coop || (b_options & OPT_SAVEBOTS)) + { + saved_bots = cvar("scratch1"); + saved_skills1 = cvar("scratch2"); + saved_skills2 = cvar("scratch3"); + } + cvar_set ("saved4", "0"); + if (max_clients > 1) + { + localcmd("exec maps/"); + localcmd(mapname); + localcmd(".way\n"); + waypoint_mode = WM_DYNAMIC; + bot_map_load(); + } + else + waypoint_mode = WM_LOADED; + +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +Rankings 'utilities'. Written by Alan Kivlin, +this code just fools clients by sending precisely +the same network messages as when a real player +signs on to the server. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + + +void(entity who) UpdateClient = +{ + WriteByte (MSG_ALL, SVC_UPDATENAME); + WriteByte (MSG_ALL, who.b_clientno); + WriteString (MSG_ALL, who.netname); + WriteByte (MSG_ALL, SVC_UPDATECOLORS); + WriteByte (MSG_ALL, who.b_clientno); + WriteByte (MSG_ALL, who.b_shirt * 16 + who.b_pants); + WriteByte (MSG_ALL, SVC_UPDATEFRAGS); + WriteByte (MSG_ALL, who.b_clientno); + WriteShort (MSG_ALL, who.frags); +}; + +float(float clientno) ClientBitFlag = +{ + // bigger, but faster + if (clientno == 0) + return 1; + else if (clientno == 1) + return 2; + else if (clientno == 2) + return 4; + else if (clientno == 3) + return 8; + else if (clientno == 4) + return 16; + else if (clientno == 5) + return 32; + else if (clientno == 6) + return 64; + else if (clientno == 7) + return 128; + else if (clientno == 8) + return 256; + else if (clientno == 9) + return 512; + else if (clientno == 10) + return 1024; + else if (clientno == 11) + return 2048; + else if (clientno == 12) + return 4096; + else if (clientno == 13) + return 8192; + else if (clientno == 14) + return 16384; + else if (clientno == 15) + return 32768; + return 0; +}; + +float() ClientNextAvailable = +{ + local float clientno; + + clientno = max_clients; + while(clientno > 0) + { + clientno = clientno - 1; + + if(!(active_clients & ClientBitFlag(clientno))) + return clientno; + } + + return -1; +}; + + +void(entity e1, entity e2, float flag) DeveloperLightning = +{ + // used to show waypoint links for debugging + WriteByte (MSG_BROADCAST, 23); + if (flag) + WriteByte (MSG_BROADCAST, 6); + else + WriteByte (MSG_BROADCAST, 13); + WriteEntity (MSG_BROADCAST, e2); + WriteCoord (MSG_BROADCAST, e1.origin_x); + WriteCoord (MSG_BROADCAST, e1.origin_y); + WriteCoord (MSG_BROADCAST, e1.origin_z); + WriteCoord (MSG_BROADCAST, e2.origin_x); + WriteCoord (MSG_BROADCAST, e2.origin_y); + WriteCoord (MSG_BROADCAST, e2.origin_z); +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +Find Another Color + +Team finding code + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +float(float tcolor) FindAnotherColor = +{ + local float bestbet, scolor, pcount, bestp; + bestbet = -1; + bestp = 16; + while(scolor < 14) + { + if (scolor != tcolor) + { + b_temp2 = player_head; + pcount = 0; + while(b_temp2 != world) + { + if (b_temp2.team == scolor + 1) + pcount = pcount + 1; + b_temp2 = b_temp2._next; + } + if ((pcount < bestp) && pcount) + { + bestbet = scolor; + bestp = pcount; + } + } + scolor = scolor + 1; + } + if (bestbet < 0) + { + bestbet = tcolor; + while (bestbet == tcolor) + { + bestbet = floor(random() * 13); + } + } + return bestbet; +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +BotConnect and related functions. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ +entity(float num) GetClientEntity = +{ + local entity upsy; + upsy = world; + num = num + 1; + while (num > 0) + { + num = num - 1; + upsy = nextent(upsy); + } + return upsy; +}; + +void(float whichteam, float whatbot, float whatskill) BotConnect = +{ + local float f; + local string h; + local entity uself; + + f = ClientNextAvailable(); + uself = self; + if(f == -1) + { + bprint("Unable to connect a bot, server is full.\n"); + return; + } + + // chat thing + + active_clients = active_clients | ClientBitFlag(f); + bot_count = bot_count + 1; + self = GetClientEntity(f); + if (!saved_bots) + bot_start_topic(1); + self.b_clientno = f; + self.colormap = f + 1; + if (whatbot) + self.netname = BotName(whatbot); + else + self.netname = PickARandomName(); + + + // players can set skill all weird, so leave these checks in + whatskill = rint(whatskill); + if (whatskill > 3) + whatskill = 3; + else if (whatskill < 0) + whatskill = 0; + self.b_skill = whatskill; + + if (teamplay && !coop) + { + if (whichteam) + self.b_pants = FindAnotherColor(uself.team - 1); + else + self.b_pants = uself.team - 1; + self.b_shirt = self.b_pants; + } + + self.team = self.b_pants + 1; + UpdateClient(self); + SetNewParms(); + self.ishuman = 2; + ClientConnect(); + PutClientInServer(); + + // this is risky... could corrupt .way files if done wrong + // If you're not the gambling type, comment this out + + f = ClientBitFlag(self.b_num - 1); + current_bots = current_bots | f; + + if (self.b_num <= 8) + saved_skills1 = (saved_skills1 & (65536 - (3 * f)) | (self.b_skill * f)); + else + { + f = ClientBitFlag(self.b_num - 9); + saved_skills2 = (saved_skills2 & (65536 - (3 * f)) | (self.b_skill * f)); + } + + h = ftos(current_bots); + cvar_set("scratch1", h); + h = ftos(saved_skills1); + cvar_set("scratch2", h); + h = ftos(saved_skills2); + cvar_set("scratch3", h); + self = uself; + +}; + +void(entity bot) BotDisconnect = +{ + local string h; + local entity uself; + uself = self; + self = bot; + + bot_count = bot_count - 1; + current_bots = current_bots - (current_bots & ClientBitFlag(self.b_num - 1)); + h = ftos(current_bots); + cvar_set("scratch1", h); + + + ClientDisconnect(); + + if (self.b_clientno != -1) + { + // the bot's client number is not in use by a real player so we + // must remove it's entry in the rankings + // Quake engine sets all fields to 0, can only do the most important here + self.b_frags = self.frags = 0; + self.netname = ""; + self.classname = ""; + self.health = 0; + self.items = 0; + self.armorvalue = 0; + self.weaponmodel = ""; + self.b_pants = 0; + self.b_shirt = 0; + self.ammo_shells = self.ammo_nails = self.ammo_rockets = self.ammo_cells = 0; + UpdateClient(self); + active_clients = active_clients - (active_clients & ClientBitFlag(self.b_clientno)); + self.b_clientno = -1; + } + self = uself; +}; +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +BotInvalidClientNo +kicks a bot if a player connects and takes the bot's space + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void(float clientno) BotInvalidClientNo = +{ + local entity bot; + + bot = GetClientEntity(clientno); + if(bot.b_clientno > 0) + { + if (!bot.ishuman) + { + bot.b_clientno = -1; + BotDisconnect(bot); + active_clients = active_clients | ClientBitFlag(self.b_clientno); + BotConnect(0, bot.b_num, bot.b_skill); + return; + } + } +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +Waypoint Loading from file + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ +void() LoadWaypoint = +{ + local vector org; + local entity tep; + local float r; + org_x = cvar("saved1"); + org_y = cvar("saved2"); + org_z = cvar("saved3"); + + tep = make_waypoint(org); + + r = cvar("saved4"); + + tep.b_aiflags = floor(r / 4); + tep.b_pants = cvar("scratch1"); + tep.b_skill = cvar("scratch2"); + tep.b_shirt = cvar("scratch3"); + tep.b_frags = cvar("scratch4"); +}; + +void() bot_return = +{ + if (time > 2) + { + if ((waypoint_mode == WM_DYNAMIC) || (waypoint_mode == WM_LOADED)) + { + // minor precaution + + if (saved_bots & 1) BotConnect(0, 1, saved_skills1 & 3); + if (saved_bots & 2) BotConnect(0, 2, (saved_skills1 & 12) / 4); + if (saved_bots & 4) BotConnect(0, 3, (saved_skills1 & 48) / 16); + if (saved_bots & 8) BotConnect(0, 4, (saved_skills1 & 192) / 64); + if (saved_bots & 16) BotConnect(0, 5, (saved_skills1 & 768) / 256); + if (saved_bots & 32) BotConnect(0, 6, (saved_skills1 & 3072) / 1024); + if (saved_bots & 64) BotConnect(0, 7, (saved_skills1 & 12288) / 4096); + if (saved_bots & 128) BotConnect(0, 8, (saved_skills1 & 49152) / 16384); + if (saved_bots & 256) BotConnect(0, 9, saved_skills2 & 3); + if (saved_bots & 512) BotConnect(0, 10, (saved_skills2 & 12) / 4); + if (saved_bots & 1024) BotConnect(0, 11, (saved_skills2& 48) / 16); + if (saved_bots & 2048) BotConnect(0, 12, (saved_skills2 & 192) / 64); + if (saved_bots & 4096) BotConnect(0, 13, (saved_skills2 & 768) / 256); + if (saved_bots & 8192) BotConnect(0, 14, (saved_skills2 & 3072) / 1024); + if (saved_bots & 16384) BotConnect(0, 15, (saved_skills2 & 12288) / 4096); + if (saved_bots & 32768) BotConnect(0, 16, (saved_skills2 & 49152) / 16384); + saved_bots = 0; + } + } +}; + + +void() WaypointWatch = +{ + // Waypoint Baywatch + local float bigboobs; + local string h; + + if (max_clients < 2) + return; + if (waypoint_mode != WM_UNINIT) + { + bigboobs = cvar("saved4"); + if (bigboobs != 0) + { + if ((bigboobs & 3) == 1) + ClearAllWays(); + else if ((bigboobs & 3) == 3) + { + FixWaypoints(); + h = ftos(b_options); + cvar_set("saved1", h); + cvar_set("saved4", "0"); + cvar_set("scratch1", "0"); + waypoint_mode = WM_LOADED; + return; + } + LoadWaypoint(); + waypoint_mode = WM_LOADING; + cvar_set("saved4", "0"); + } + } +}; +void() BotFrame = +{ + local float num; + + // for the sake of speed + sv_maxspeed = cvar("sv_maxspeed"); + sv_gravity = cvar("sv_gravity"); + sv_friction = cvar("sv_friction"); + sv_accelerate = cvar("sv_accelerate"); + sv_stopspeed = cvar("sv_stopspeed"); + real_frametime = frametime; // in NQ this is alright + + self = nextent(world); + num = 0; + while (num < max_clients) + { + if (self.ishuman == FALSE) + { + if (active_clients & ClientBitFlag(num)) + { + frik_obstacles(); + CL_KeyMove(); + SV_ClientThink(); + SV_Physics_Client(); + } + } + self = nextent(self); + num = num + 1; + } + WaypointWatch(); + + if (saved_bots) + bot_return(); +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +Bot Impulses. Allows the player to perform bot +related functions. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void() BotImpulses = +{ + local float f; + if (self.impulse == 100) + { + f = cvar("skill"); + BotConnect(0, 0, f); + } + if (self.impulse == 101) + { + f = cvar("skill"); + BotConnect(1, 0, f); + } + else if (self.impulse == 102) + KickABot(); + else if (self.impulse == 103) + botcam_u(); + else if (self.impulse == 104) + bot_way_edit(); + else + return; + + self.impulse = 0; +}; + + + diff --git a/qcsrc/gamec/bot_ai.c b/qcsrc/gamec/bot_ai.c new file mode 100644 index 000000000..85fbb6c3a --- /dev/null +++ b/qcsrc/gamec/bot_ai.c @@ -0,0 +1,1010 @@ +/*********************************************** +* * +* FrikBot General AI * +* "The I'd rather be playing Quake AI" * +* * +***********************************************/ + +/* + +This program is in the Public Domain. My crack legal +team would like to add: + +RYAN "FRIKAC" SMITH IS PROVIDING THIS SOFTWARE "AS IS" +AND MAKES NO WARRANTY, EXPRESS OR IMPLIED, AS TO THE +ACCURACY, CAPABILITY, EFFICIENCY, MERCHANTABILITY, OR +FUNCTIONING OF THIS SOFTWARE AND/OR DOCUMENTATION. IN +NO EVENT WILL RYAN "FRIKAC" SMITH BE LIABLE FOR ANY +GENERAL, CONSEQUENTIAL, INDIRECT, INCIDENTAL, +EXEMPLARY, OR SPECIAL DAMAGES, EVEN IF RYAN "FRIKAC" +SMITH HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES, IRRESPECTIVE OF THE CAUSE OF SUCH DAMAGES. + +You accept this software on the condition that you +indemnify and hold harmless Ryan "FrikaC" Smith from +any and all liability or damages to third parties, +including attorney fees, court costs, and other +related costs and expenses, arising out of your use +of this software irrespective of the cause of said +liability. + +The export from the United States or the subsequent +reexport of this software is subject to compliance +with United States export control and munitions +control restrictions. You agree that in the event you +seek to export this software, you assume full +responsibility for obtaining all necessary export +licenses and approvals and for assuring compliance +with applicable reexport restrictions. + +Any reproduction of this software must contain +this notice in its entirety. + +*/ + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +target_onstack + +checks to see if an entity is on the bot's stack + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +float(entity scot) target_onstack = +{ + if (scot == world) + return FALSE; + else if (self.target1 == scot) + return 1; + else if (self.target2 == scot) + return 2; + else if (self.target3 == scot) + return 3; + else if (self.target4 == scot) + return 4; + else + return FALSE; +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +target_add + +adds a new entity to the stack, since it's a +LIFO stack, this will be the bot's new target1 + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void(entity ent) target_add = +{ + if (ent == world) + return; + if (target_onstack(ent)) + return; + self.target4 = self.target3; + self.target3 = self.target2; + self.target2 = self.target1; + self.target1 = ent; + self.search_time = time + 5; +}; + + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +target_drop + +Removes an entity from the bot's target stack. +The stack will empty everything up to the object +So if you have target2 item_health, target1 +waypoint, and you drop the health, the waypoint +is gone too. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void(entity ent) target_drop = +{ + local float tg; + + tg = target_onstack(ent); + if (tg == 1) + { + self.target1 = self.target2; + self.target2 = self.target3; + self.target3 = self.target4; + self.target4 = world; + } + else if (tg == 2) + { + self.target1 = self.target3; + self.target2 = self.target4; + self.target3 = self.target4 = world; + } + else if (tg == 3) + { + self.target1 = self.target4; + self.target2 = self.target3 = self.target4 = world; + } + else if (tg == 4) + self.target1 = self.target2 = self.target3 = self.target4 = world; + self.search_time = time + 5; +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +bot_lost + +Bot has lost its target. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void(entity targ, float success) bot_lost = +{ + if (!targ) + return; + + target_drop(targ); + if (targ.classname == "waypoint") + targ.b_sound = targ.b_sound - (targ.b_sound & ClientBitFlag(self.b_clientno)); + + // find a new route + if (!success) + { + self.target1 = self.target2 = self.target3 = self.target4 = world; + self.last_way = FindWayPoint(self.current_way); + ClearMyRoute(); + self.b_aiflags = 0; + } + else + { + if (targ.classname == "item_artifact_invisibility") + if (self.items & 524288) + bot_start_topic(3); + + if (targ.flags & FL_ITEM) + { + if (targ.model == string_null) + targ._last = world; + else + targ._last = self; + } + } + + + if (targ.classname != "player") + targ.search_time = time + 5; +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +bot_check_lost + +decide if my most immediate target should be +removed. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ +void(entity targ) bot_check_lost = +{ + local vector dist; + dist = realorigin(targ) - self.origin; + dist_z = 0; + if (targ == world) + return; + + // waypoints and items are lost if you get close enough to them + + else if (targ.flags & FL_ITEM) + { + if (vlen(targ.origin - self.origin) < 32) + bot_lost(targ, TRUE); + else if (targ.model == string_null) + bot_lost(targ, TRUE); + } + else if (targ.classname == "waypoint") + { + if (!(self.b_aiflags & (AI_SNIPER | AI_AMBUSH))) + { + if (self.b_aiflags & AI_RIDE_TRAIN) + { + if (vlen(targ.origin - self.origin) < 48) + bot_lost(targ, TRUE); + } + else if (self.b_aiflags & AI_PRECISION) + { + if (vlen(targ.origin - self.origin) < 24) + bot_lost(targ, TRUE); + } + else if (vlen(targ.origin - self.origin) < 32) + bot_lost(targ, TRUE); + else if (self.b_aiflags & AI_CARELESS) // Electro - better for jumppads + { + if (vlen(targ.origin - self.origin) < 128) + bot_lost(targ, TRUE); + } + } + } + else if (targ.classname == "temp_waypoint") + { + if (vlen(targ.origin - self.origin) < 32) + bot_lost(targ, TRUE); + } + else if (targ.classname == "player") + { + if (targ.health <= 0) + bot_lost(targ, TRUE); + else if ((coop) || (teamplay && targ.team == self.team)) + { + if (targ.target1.classname == "player") + { + if (!targ.target1.ishuman) + bot_lost(targ, TRUE); + } + else if (targ.teleport_time > time) + { + // try not to telefrag teammates + self.keys = self.keys & 960; + } + else if (vlen(targ.origin - self.origin) < 128) + { + if (vlen(targ.origin - self.origin) < 48) + frik_walkmove(self.origin - targ.origin); + else + { + self.keys = self.keys & 960; + bot_start_topic(4); + } + self.search_time = time + 5; // never time out + } + else if (!fisible(targ)) + bot_lost(targ, FALSE); + } + else if (waypoint_mode > WM_LOADED) + { + if (vlen(targ.origin - self.origin) < 128) + { + bot_lost(targ, TRUE); + } + } + } + + // buttons are lost of their frame changes + else if (targ.classname == "func_button") + { + if (targ.frame) + { + bot_lost(targ, TRUE); + if (self.enemy == targ) + self.enemy = world; + //if (self.target1) + // bot_get_path(self.target1, TRUE); + + } + } + // trigger_multiple style triggers are lost if their thinktime changes + else if ((targ.movetype == MOVETYPE_NONE) && (targ.solid == SOLID_TRIGGER)) + { + if (targ.nextthink >= time) + { + bot_lost(targ, TRUE); + //if (self.target1) + // bot_get_path(self.target1, TRUE); + } + } + // lose any target way above the bot's head + // FIXME: if the bot can fly in your mod.. + if ((targ.origin_z - self.origin_z) > 64) + { + dist = targ.origin - self.origin; + dist_z = 0; + if (vlen(dist) < 32) + if (self.flags & FL_ONGROUND) + if(!frik_recognize_plat(FALSE)) + bot_lost(targ, FALSE); + } + else if (targ.classname == "train") + { + if (frik_recognize_plat(FALSE)) + bot_lost(targ, TRUE); + } + // targets are lost if the bot's search time has expired + if (time > self.search_time) + bot_lost(targ, FALSE); +}; + + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +bot_handle_ai + +This is a 0.10 addition. Handles any action +based b_aiflags. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void() bot_handle_ai = +{ + local entity newt; + local vector v; + + // handle ai flags -- note, not all aiflags are handled + // here, just those that perform some sort of action + + // wait is used by the ai to stop the bot until his search time expires / or route changes + + if (self.b_aiflags & AI_WAIT) + self.keys = self.keys & 960; + + if (self.b_aiflags & AI_DOORFLAG) // was on a door when spawned + { + b_temp3 = self; + self = self.last_way; + if (!frik_recognize_plat(FALSE)) // if there is nothing there now + { + newt = FindThing("door"); // this is likely the door responsible (crossfingers) + self = b_temp3; + + if (self.b_aiflags & AI_DOOR_NO_OPEN) + { + if (newt.nextthink) + self.keys = self.keys & 960; // wait until it closes + else + { + bot_lost(self.last_way, FALSE); + } + } + else + { + if (newt.targetname) + { + newt = find(world, target, newt.targetname); + if (newt.health > 0) + { + self.enemy = newt; + bot_weapon_switch(1); + } + else + { + // target_drop(self.last_way); + target_add(newt); + // bot_get_path(newt, TRUE); + } + } + self.b_aiflags = self.b_aiflags - AI_DOORFLAG; + } + } + else + self = b_temp3; + } + + if (self.b_aiflags & AI_JUMP) + { + if (self.flags & FL_ONGROUND) + { + bot_jump(); + self.b_aiflags = self.b_aiflags - AI_JUMP; + } + } + else if (self.b_aiflags & AI_SUPER_JUMP) + { + if (self.weapon != 32) + self.impulse = 7; + else if (self.flags & FL_ONGROUND) + { + self.b_aiflags = self.b_aiflags - AI_SUPER_JUMP; + if (bot_can_rj(self)) + { + bot_jump(); + self.v_angle_x = self.b_angle_x = 80; + self.button0 = TRUE; + } + else + bot_lost(self.target1, FALSE); + + } + } + if (self.b_aiflags & AI_SURFACE) + { + if (self.waterlevel > 2) + { + self.keys = KEY_MOVEUP; + self.button2 = TRUE; // swim! + } + else + self.b_aiflags = self.b_aiflags - AI_SURFACE; + } + if (self.b_aiflags & AI_RIDE_TRAIN) + { + // simple, but effective + // this can probably be used for a lot of different + // things, not just trains (door elevators come to mind) + b_temp3 = self; + self = self.last_way; + + if (!frik_recognize_plat(FALSE)) // if there is nothing there now + { + self = b_temp3; + self.keys = self.keys & 960; + } + else + { + self = b_temp3; + if (frik_recognize_plat(FALSE)) + { + v = realorigin(trace_ent) + trace_ent.origin - self.origin; + v_z = 0; + if (vlen(v) < 24) + self.keys = self.keys & 960; + else + { + self.b_aiflags = self.b_aiflags | AI_PRECISION; + self.keys = frik_KeysForDir(v); + } + } + } + } + if (self.b_aiflags & AI_PLAT_BOTTOM) + { + newt = FindThing("plat"); + if (newt.state != 1) + { + v = self.origin - realorigin(newt); + v_z = 0; + if (vlen(v) > 96) + self.keys = self.keys & 960; + else + frik_walkmove(v); + } + else + self.b_aiflags = self.b_aiflags - AI_PLAT_BOTTOM; + } + if (self.b_aiflags & AI_DIRECTIONAL) + { + if ((normalize(self.last_way.origin - self.origin) * self.b_dir) > 0.4) + { + self.b_aiflags = self.b_aiflags - AI_DIRECTIONAL; + bot_lost(self.target1, TRUE); + } + } + if (self.b_aiflags & AI_SNIPER) + { + self.b_aiflags = (self.b_aiflags | AI_WAIT | AI_PRECISION) - AI_SNIPER; + // FIXME: Add a switch to wep command + // FIXME: increase delay? + } + if (self.b_aiflags & AI_AMBUSH) + { + self.b_aiflags = (self.b_aiflags | AI_WAIT) - AI_AMBUSH; + // FIXME: Add a switch to wep command + // FIXME: increase delay? + } + +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +bot_path + +Bot will follow a route generated by the +begin_route set of functions in bot_way.qc. +This code, while it works pretty well, can get +confused + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void() bot_path = +{ + + local entity jj, tele; + local vector org; + + bot_check_lost(self.target1); + if (!self.target1) + { + self.keys=0; + return; + } + if (target_onstack(self.last_way)) + return; // old waypoint still being hunted + + jj = FindRoute(self.last_way); + if (!jj) + { + // this is an ugly hack + if (self.target1.current_way != self.last_way) + { + if (self.target1.classname != "temp_waypoint") + if (self.target1.classname != "player") + bot_lost(self.target1, FALSE); + } + + return; + } + + // update the bot's special ai features + + // Readahed types are AI conditions to perform while heading to a waypoint + // point types are AI flags that should be executed once reaching a waypoint + + self.b_aiflags = (jj.b_aiflags & AI_READAHEAD_TYPES) | (self.last_way.b_aiflags & AI_POINT_TYPES); + target_add(jj); + if (self.last_way) + { + if (CheckLinked(self.last_way, jj) == 2) // waypoints are telelinked + { + tele = FindThing("trigger_teleport"); // this is probbly the teleport responsible + target_add(tele); + } + traceline(self.last_way.origin, jj.origin, FALSE, self); // check for blockage + if (trace_fraction != 1) + { + if (trace_ent.classname == "door" && !(self.b_aiflags & AI_DOOR_NO_OPEN)) // a door blocks the way + { + // linked doors fix + if (trace_ent.owner) + trace_ent = trace_ent.owner; + if ((trace_ent.health > 0) && (self.enemy == world)) + { + self.enemy = trace_ent; + bot_weapon_switch(1); + self.b_aiflags = self.b_aiflags | AI_BLIND; // nick knack paddy hack + } + else if (trace_ent.targetname) + { + tele = find(world, target, trace_ent.targetname); + if (tele.health > 0) + { + self.enemy = tele; + bot_weapon_switch(1); + } + else + { + // target_drop(jj); + target_add(tele); + // bot_get_path(tele, TRUE); + self.b_aiflags = self.b_aiflags | AI_BLIND; // give a bot a bone + return; + } + } + } + else if (trace_ent.classname == "func_wall") + { + // give up + bot_lost(self.target1, FALSE); + return; + } + } + } + // this is used for AI_DRIECTIONAL + self.b_dir = normalize(jj.origin - self.last_way.origin); + + self.last_way = jj; +}; + + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +Bot Priority Look. What a stupid name. This is where +the bot finds things it wants to kill/grab. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ +// priority scale +// 0 - 10 virtually ignore +// 10 - 30 normal item range +// 30 - 50 bot will consider this a target worth changing course for +// 50 - 90 bot will hunt these as vital items + +// *!* Make sure you add code to bot_check_lost to remove the target *!* + +float(entity thing) priority_for_thing = +{ + local float thisp; + thisp = 0; + // This is the most executed function in the bot. Careful what you do here. + + if (thing.flags & FL_ITEM && thing.model != string_null && thing.search_time < time) + { + // ugly hack + if (thing._last != self) + thisp = 20; + else if (thing.classname == "item_strength") // IT_STRENGTH + thisp = 65; + else if (thing.classname == "item_invincible") // IT_INVINCIBLE + thisp = 65; + else if (thing.classname == "item_speed") // IT_SPEED + thisp = 65; + else if (thing.classname == "item_slowmo") // IT_SLOWMO + thisp = 65; + else if (thing.classname == "item_health") + { + if (thing.spawnflags & 2) + thisp = 55; + if (self.health < 40) + thisp = thisp + 50; + } + else if (thing.model == "progs/armor.mdl") + { + if (self.armorvalue < 200) + { + if (thing.skin == 2) + thisp = 60; + else if (self.armorvalue < 100) + thisp = thisp + 25; + } + } + else if (thing.classname == "weapon_shotgun") + { + if (!(self.items & IT_SHOTGUN)) + thisp = 25; + } + else if (thing.classname == "weapon_uzi") + { + if (!(self.items & IT_UZI)) + thisp = 30; + } + else if (thing.classname == "weapon_electro") + { + if (!(self.items & IT_ELECTRO)) + thisp = 35; + } + else if (thing.classname == "weapon_grenadelauncher") + { + if (!(self.items & IT_GRENADE_LAUNCHER)) + thisp = 45; + } + else if (thing.classname == "weapon_crylink") + { + if (!(self.items & IT_CRYLINK)) + thisp = 60; + } + else if (thing.classname == "weapon_nex") + { + if (!(self.items & IT_NEX)) // IT_LIGHTNING + thisp = 50; + } + else if (thing.classname == "weapon_hagar") + { + if (!(self.items & IT_HAGAR)) + thisp = 45; + } + else if (thing.classname == "weapon_rocketlauncher") + { + if (!(self.items & IT_ROCKET_LAUNCHER)) + thisp = 50; + } + } + else if (thing.classname == "player") + { + if (thing.health > 0) + { + if (thing == self) + return 0; + else + { + if (coop) + { + thisp = 100; + if (thing.target1.classname == "player") + if (!thing.target1.ishuman) + return 0; + } + else if (teamplay && thing.team == self.team) + { + thisp = 100; + if (thing.target1.classname == "player") + return 0; + } + else thisp = 30; + } + } + } + else if (thing.classname == "waypoint") + { + if (thing.b_aiflags & AI_SNIPER) + thisp = 30; + else if (thing.b_aiflags & AI_AMBUSH) + thisp = 30; + } + if (pointcontents(thing.origin) < -3) + return 0; + if (thisp) + { + if (thing.current_way) + { + // check to see if it's unreachable + if (thing.current_way.items == -1) + return 0; + else + thisp = thisp + (13000 - thing.current_way.items) * 0.05; + + } + } + return thisp; +}; + +void(float scope) bot_look_for_crap = +{ + local entity foe, best; + local float thatp, bestp, dist; + + if (scope == 1) + foe = findradius(self.origin, 13000); + else + foe = findradius(self.origin, 500); + + bestp = 1; + while(foe) + { + thatp = priority_for_thing(foe); + if (thatp) + if (!scope) + if (!sisible(foe)) + thatp = 0; + if (thatp > bestp) + { + bestp = thatp; + best = foe; + dist = vlen(self.origin - foe.origin); + } + foe = foe.chain; + } + if (best == world) + return; + if (!target_onstack(best)) + { + target_add(best); + if (scope) + { + bot_get_path(best, FALSE); + self.b_aiflags = self.b_aiflags | AI_WAIT; + } + } +}; + + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +bot_angle_set + +Sets the bots look keys & b_angle to point at +the target - used for fighting and just +generally making the bot look good. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void() bot_angle_set = +{ + local float h; + local vector view; + + if (self.enemy) + { + if (self.enemy.items & 524288) + if (random() > 0.2) + return; + if (self.missile_speed == 0) + self.missile_speed = 10000; + if (self.enemy.solid == SOLID_BSP) + { + view = (((self.enemy.absmin + self.enemy.absmax) * 0.5) - self.origin); + } + else + { + h = vlen(self.enemy.origin - self.origin) / self.missile_speed; + if (self.enemy.flags & FL_ONGROUND) + view = self.enemy.velocity * h; + else + view = (self.enemy.velocity - (sv_gravity * '0 0 1') * h) * h; + view = self.enemy.origin + view; + // FIXME: ? + traceline(self.enemy.origin, view, FALSE, self); + view = trace_endpos; + + if (self.weapon == 32) + view = view - '0 0 22'; + + view = normalize(view - self.origin); + } + view = vectoangles(view); + view_x = view_x * -1; + self.b_angle = view; + } + else if (self.target1) + { + view = realorigin(self.target1); + if (self.target1.flags & FL_ITEM) + view = view + '0 0 48'; + view = view - (self.origin + self.view_ofs); + view = vectoangles(view); + view_x = view_x * -1; + self.b_angle = view; + } + else + self.b_angle_x = 0; + // HACK HACK HACK HACK + // The bot falls off ledges a lot because of "turning around" + // so let the bot use instant turn around when not hunting a player + if (self.b_skill == 3) + { + self.keys = self.keys & 63; + self.v_angle = self.b_angle; + while (self.v_angle_x < -180) + self.v_angle_x = self.v_angle_x + 360; + while (self.v_angle_x > 180) + self.v_angle_x = self.v_angle_x - 360; + + } + else if ((self.enemy == world || self.enemy.movetype == MOVETYPE_PUSH) && self.target1.classname != "player") + { + self.keys = self.keys & 63; + self.v_angle = self.b_angle; + while (self.v_angle_x < -180) + self.v_angle_x = self.v_angle_x + 360; + while (self.v_angle_x > 180) + self.v_angle_x = self.v_angle_x - 360; + } + else if (self.b_skill < 2) // skill 2 handled in bot_phys + { + if (self.b_angle_x > 180) + self.b_angle_x = self.b_angle_x - 360; + self.keys = self.keys & 63; + + if (angcomp(self.b_angle_y, self.v_angle_y) > 10) + self.keys = self.keys | KEY_LOOKLEFT; + else if (angcomp(self.b_angle_y, self.v_angle_y) < -10) + self.keys = self.keys | KEY_LOOKRIGHT; + if (angcomp(self.b_angle_x, self.v_angle_x) < -10) + self.keys = self.keys | KEY_LOOKUP; + else if (angcomp(self.b_angle_x, self.v_angle_x) > 10) + self.keys = self.keys | KEY_LOOKDOWN; + } +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +BotAI + +This is the main ai loop. Though called every +frame, the ai_time limits it's actual updating + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ +float stagger_think; + +void() BotAI = +{ + // am I dead? Fire randomly until I respawn + // health < 1 is used because fractional healths show up as 0 on normal player + // status bars, and the mod probably already compensated for that + + if (self.health < 1) + { + self.button0 = floor(random() * 2); + self.button2 = 0; + self.keys = 0; + self.b_aiflags = 0; + ClearMyRoute(); + self.target1 = self.target2 = self.target3 = self.target4 = self.enemy = world; + self.last_way = world; + return; + } + + // stagger the bot's AI out so they all don't think at the same time, causing game + // 'spikes' + if (self.b_skill < 2) + { + if (self.ai_time > time) + return; + + self.ai_time = time + 0.05; + if (bot_count > 0) + { + if ((time - stagger_think) < (0.1 / bot_count)) + self.ai_time = self.ai_time + 0.1 / (2 * bot_count); + } + else + return; + } + if (self.view_ofs == '0 0 0') + bot_start_topic(7); + stagger_think = time; + + // shut the bot's buttons off, various functions will turn them on by AI end + + self.button2 = 0; + self.button0 = 0; + + + // target1 is like goalentity in normal Quake monster AI. + // it's the bot's most immediate target + if (route_table == self) + { + if (busy_waypoints <= 0) + { + if (waypoint_mode < WM_EDITOR) + bot_look_for_crap(TRUE); + } + self.b_aiflags = 0; + self.keys = 0; + } + else if (self.target1) + { + frik_movetogoal(); + bot_path(); + } + else + { + if (waypoint_mode < WM_EDITOR) + { + if(self.route_failed) + { + frik_bot_roam(); + self.route_failed = 0; + } + else if(!begin_route()) + { + bot_look_for_crap(FALSE); + } + self.keys = 0; + } + else + { + self.b_aiflags = AI_WAIT; + self.keys = 0; + } + } + + // bot_angle_set points the bot at it's goal (self.enemy or target1) + + bot_angle_set(); + + // fight my enemy. Enemy is probably a field QC coders will most likely use a lot + // for their own needs, since it's unused on a normal player + // FIXME + if (self.enemy) + bot_fight_style(); + else if (random() < 0.2) + if (random() < 0.2) + bot_weapon_switch(-1); + bot_dodge_stuff(); + + // checks to see if bot needs to start going up for air +/* if (self.waterlevel > 2) + { + if (time > (self.air_finished - 2)) + { + traceline (self.origin, self.origin + '0 0 6800', TRUE, self); + if (trace_inopen) + { + self.keys = KEY_MOVEUP; + self.button2 = TRUE; // swim! + return; // skip ai flags for now - this is life or death + } + } + } +*/ + // b_aiflags handling + + + if (self.b_aiflags) + bot_handle_ai(); + else + bot_chat(); // don't want chat to screw him up if he's rjing or something +}; diff --git a/qcsrc/gamec/bot_ed.c b/qcsrc/gamec/bot_ed.c new file mode 100644 index 000000000..22948ca0c --- /dev/null +++ b/qcsrc/gamec/bot_ed.c @@ -0,0 +1,1367 @@ +/*********************************************** +* * +* FrikBot Waypoint Editor * +* "The 'wtf is this doing in my mod' code" * +* * +***********************************************/ + +/* + +This program is in the Public Domain. My crack legal +team would like to add: + +RYAN "FRIKAC" SMITH IS PROVIDING THIS SOFTWARE "AS IS" +AND MAKES NO WARRANTY, EXPRESS OR IMPLIED, AS TO THE +ACCURACY, CAPABILITY, EFFICIENCY, MERCHANTABILITY, OR +FUNCTIONING OF THIS SOFTWARE AND/OR DOCUMENTATION. IN +NO EVENT WILL RYAN "FRIKAC" SMITH BE LIABLE FOR ANY +GENERAL, CONSEQUENTIAL, INDIRECT, INCIDENTAL, +EXEMPLARY, OR SPECIAL DAMAGES, EVEN IF RYAN "FRIKAC" +SMITH HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES, IRRESPECTIVE OF THE CAUSE OF SUCH DAMAGES. + +You accept this software on the condition that you +indemnify and hold harmless Ryan "FrikaC" Smith from +any and all liability or damages to third parties, +including attorney fees, court costs, and other +related costs and expenses, arising out of your use +of this software irrespective of the cause of said +liability. + +The export from the United States or the subsequent +reexport of this software is subject to compliance +with United States export control and munitions +control restrictions. You agree that in the event you +seek to export this software, you assume full +responsibility for obtaining all necessary export +licenses and approvals and for assuring compliance +with applicable reexport restrictions. + +Any reproduction of this software must contain +this notice in its entirety. + +*/ + +float saved1, saved2, saved3, scratch1, scratch2, scratch3, scratch4; +float bytecounter, filecount; + +float MENU_MAIN = 1; +float MENU_WAYPOINTS = 2; +float MENU_LINKS = 3; +float MENU_FLAGS = 4; +float MENU_FLAGS2 = 5; +float MENU_BOTS = 6; +float MENU_WAYLIST = 7; +// 8 = link way +// 9 = telelink way +// 10 = delete link +// 11 = create link X2 +// 12 = delete link x2 +// 13 = confirmation of delete all +// 14 = Teleport to way +// 15 = confirmation of delete point + +void() BSPDumpWaypoints; +void() QCDumpWaypoints; +void() DumpWaypoints; +/* +// source for the menu strings... + +-- Main Menu --\n +[1] >>Waypoint Management\n +[2] >>Link Management \n +[3] >>AI Flag Management \n +[4] >>Bot Management \n +[5] >>Waylist Management \n +[6] [#] Noclip \n +[7] [#] Godmode \n +[8] [#] Hold Select \n +[9] Teleport to Way # \n +[0] Close Menu \n + +// missing from main is show way info +// iffy on the teleport to way thing being on main...seems like either a bot or way list thing + +-- Waypoint Management --\n +[1] Move Waypoint \n +[2] Delete Waypoint \n +[3] Make Waypoint \n +[4] Make Way + Link \n +[5] Make Way + Link X2 \n +[6] Make Way + Telelink \n +[7] Show waypoint info \n +[8] >>Link Management \n +[9] >>AI Flag Management \n +[0] >>Main Menu \n + +-- Link Management --\n +[1] Unlink Waypoint \n +[2] Create Link \n +[3] Create Telelink \n +[4] Delete Link \n +[5] Create Link X2 \n +[6] Delete Link X2 \n +[7] >Make Waypoint \n +[8] >>Waypoint Management\n +[9] >>AI Flag Management \n +[0] >>Main Menu \n + +// Ai flags...ugh + +-- AI Flag Management --\n +[1] [#] Door Flag \n +[2] [#] Precision \n +[3] [#] Surface for air \n +[4] [#] Blind mode \n +[5] [#] Jump \n +[6] [#] Dark \n +[7] [#] Super Jump \n +\n +[9] >>AI Flags page 2 \n +[0] >>Main Menu \n + +-- AI Flags pg. 2--\n +[1] [#] Difficult \n +[2] [#] Wait for plat \n +[3] [#] Ride train \n +[4] [#] Door flag no open\n +[5] [#] Ambush \n +[6] [#] Snipe \n +[7] [#] Trace Test \n +\n +[9] >>AI Flag Management \n +[0] >>Main Menu \n + +-- Bot Management --\n +[1] Add a Test Bot \n +[2] Order Test Bot here \n +[3] Remove Test Bot \n +[4] Stop Test Bot \n +[5] Teleport Bot here \n +[6] Teleport to Way # \n +\n +\n +\n +[0] >>Main Menu \n + +-- Waylist Management --\n +[1] Delete ALL Waypoints \n +[2] Dump Waypoints \n +[3] Check For Errors \n +[4] Save Waypoints \n +[5] [#] Dynamic Mode \n +[6] [#] Dynamic Link \n +[7] [#] WAY output \n +[8] [#] QC output \n +[9] [#] BSP ents output \n +[0] Main Menu \n + +*/ + +void() bot_menu_display = +{ +// build options + local string s1, s2, s3, s4, s5, s6, s7, h; + local entity t; + +// check impulses + if (self.impulse > 0 && self.impulse < 11 && self.b_menu) + { + if (self.b_menu == MENU_MAIN) + { + if (self.impulse == 1) + { + self.b_menu = MENU_WAYPOINTS; + self.b_menu_time = time; + } + else if (self.impulse == 2) + { + self.b_menu = MENU_LINKS; + self.b_menu_time = time; + } + else if (self.impulse == 3) + { + self.b_menu = MENU_FLAGS; + self.b_menu_time = time; + } + else if (self.impulse == 4) + { + self.b_menu = MENU_BOTS; + self.b_menu_time = time; + } + else if (self.impulse == 5) + { + self.b_menu = MENU_WAYLIST; + self.b_menu_time = time; + } + else if (self.impulse == 6) + { + if (self.movetype == MOVETYPE_NOCLIP) + self.movetype = MOVETYPE_WALK; + else + self.movetype = MOVETYPE_NOCLIP; + self.b_menu_time = time; + + } + else if (self.impulse == 7) + { + if (self.flags & FL_GODMODE) + self.flags = self.flags - FL_GODMODE; + else + self.flags = self.flags | FL_GODMODE; + self.b_menu_time = time; + + } + else if (self.impulse == 8) + { + if (self.b_aiflags & AI_HOLD_SELECT) + self.b_aiflags = self.b_aiflags - AI_HOLD_SELECT; + else + self.b_aiflags = self.b_aiflags | AI_HOLD_SELECT; + self.b_menu_time = time; + } + else if (self.impulse == 9) + { + self.b_menu = 14; + self.b_menu_time = time; + } + else if (self.impulse == 10) + bot_way_edit(); + } + else if (self.b_menu == MENU_WAYPOINTS) + { + if (self.impulse == 1) + { + if (self.current_way) + setorigin(self.current_way, self.origin + self.view_ofs); + } + else if (self.impulse == 2) + { + if (self.current_way) + { + self.b_menu = 15; + self.b_menu_time = time; + self.last_way = self.current_way; + } + } + else if (self.impulse == 3) + { + make_waypoint(self.origin + self.view_ofs); + } + else if (self.impulse == 4) + { + t = make_waypoint(self.origin + self.view_ofs); + if (!LinkWays(self.current_way, t)) + sprint(self, "Unable to link them\n"); + } + else if (self.impulse == 5) + { + t = make_waypoint(self.origin + self.view_ofs); + if (!LinkWays(self.current_way, t)) + sprint(self, "Unable to link old to new\n"); + LinkWays(t, self.current_way); + } + else if (self.impulse == 6) + { + t = make_waypoint(self.origin + self.view_ofs); + if (!TeleLinkWays(self.current_way, t)) + sprint(self, "Unable to link them\n"); + } + else if (self.impulse == 7) + { + if (self.current_way) + { + sprint(self, "\nwaypoint info for waypoint #"); + h = ftos(self.current_way.count); + sprint(self, h); + sprint(self, "\nAI Flag value: "); + h = ftos(self.current_way.b_aiflags); + sprint(self, h); + + if (self.current_way.target1) + { + h = ftos(self.current_way.target1.count); + if (self.current_way.b_aiflags & AI_TELELINK_1) + sprint(self, "\nTelelink1 to:"); + else + sprint(self, "\nLink1 to:"); + sprint(self, h); + } + if (self.current_way.target2) + { + h = ftos(self.current_way.target2.count); + if (self.current_way.b_aiflags & AI_TELELINK_2) + sprint(self, "\nTelelink2 to:"); + else + sprint(self, "\nLink2 to:"); + sprint(self, h); + } + if (self.current_way.target3) + { + h = ftos(self.current_way.target3.count); + if (self.current_way.b_aiflags & AI_TELELINK_3) + sprint(self, "\nTelelink3 to:"); + else + sprint(self, "\nLink3 to:"); + sprint(self, h); + } + if (self.current_way.target4) + { + h = ftos(self.current_way.target4.count); + if (self.current_way.b_aiflags & AI_TELELINK_4) + sprint(self, "\nTelelink4 to:"); + else + sprint(self, "\nLink4 to:"); + sprint(self, h); + } + sprint(self, "\n\n"); + } + + } + if (self.impulse == 8) + { + self.b_menu = MENU_LINKS; + self.b_menu_time = time; + } + else if (self.impulse == 9) + { + self.b_menu = MENU_FLAGS; + self.b_menu_time = time; + } + else if (self.impulse == 10) + { + self.b_menu = MENU_MAIN; + self.b_menu_time = time; + } + } + else if (self.b_menu == MENU_LINKS) + { + if (self.impulse == 1) + { + if (self.current_way) + self.current_way.target1 = self.current_way.target2 = self.current_way.target3 = self.current_way.target4 = world; + } + else if (self.impulse == 2) + { + self.b_menu = 8; + self.b_menu_time = time; + self.last_way = self.current_way; + } + else if (self.impulse == 3) + { + self.b_menu = 9; + self.b_menu_time = time; + self.last_way = self.current_way; + } + else if (self.impulse == 4) + { + self.b_menu = 10; + self.b_menu_time = time; + self.last_way = self.current_way; + } + else if (self.impulse == 5) + { + self.b_menu = 11; + self.b_menu_time = time; + self.last_way = self.current_way; + } + else if (self.impulse == 6) + { + self.b_menu = 12; + self.b_menu_time = time; + self.last_way = self.current_way; + } + else if (self.impulse == 7) + make_waypoint(self.origin + self.view_ofs); + else if (self.impulse == 8) + { + self.b_menu = MENU_WAYPOINTS; + self.b_menu_time = time; + } + else if (self.impulse == 9) + { + self.b_menu = MENU_FLAGS; + self.b_menu_time = time; + } + else if (self.impulse == 10) + { + self.b_menu = MENU_MAIN; + self.b_menu_time = time; + } + } + else if (self.b_menu == MENU_FLAGS) + { + + if (self.current_way) + { + if (self.impulse == 1) + { + if (self.current_way.b_aiflags & AI_DOORFLAG) + self.current_way.b_aiflags = self.current_way.b_aiflags - (self.current_way.b_aiflags & AI_DOORFLAG); + else + self.current_way.b_aiflags = self.current_way.b_aiflags | AI_DOORFLAG; + + self.b_menu_time = time; + } + else if (self.impulse == 2) + { + if (self.current_way.b_aiflags & AI_PRECISION) + self.current_way.b_aiflags = self.current_way.b_aiflags - (self.current_way.b_aiflags & AI_PRECISION); + else + self.current_way.b_aiflags = self.current_way.b_aiflags | AI_PRECISION; + self.b_menu_time = time; + } + else if (self.impulse == 3) + { + if (self.current_way.b_aiflags & AI_SURFACE) + self.current_way.b_aiflags = self.current_way.b_aiflags - (self.current_way.b_aiflags & AI_SURFACE); + else + self.current_way.b_aiflags = self.current_way.b_aiflags | AI_SURFACE; + self.b_menu_time = time; + } + else if (self.impulse == 4) + { + if (self.current_way.b_aiflags & AI_BLIND) + self.current_way.b_aiflags = self.current_way.b_aiflags - (self.current_way.b_aiflags & AI_BLIND); + else + self.current_way.b_aiflags = self.current_way.b_aiflags | AI_BLIND; + self.b_menu_time = time; + } + else if (self.impulse == 5) + { + if (self.current_way.b_aiflags & AI_JUMP) + self.current_way.b_aiflags = self.current_way.b_aiflags - (self.current_way.b_aiflags & AI_JUMP); + else + self.current_way.b_aiflags = self.current_way.b_aiflags | AI_JUMP; + self.b_menu_time = time; + } + else if (self.impulse == 6) + { + if (self.current_way.b_aiflags & AI_DIRECTIONAL) + self.current_way.b_aiflags = self.current_way.b_aiflags - (self.current_way.b_aiflags & AI_DIRECTIONAL); + else + self.current_way.b_aiflags = self.current_way.b_aiflags | AI_DIRECTIONAL; + self.b_menu_time = time; + } + else if (self.impulse == 7) + { + if (self.current_way.b_aiflags & AI_SUPER_JUMP) + self.current_way.b_aiflags = self.current_way.b_aiflags - (self.current_way.b_aiflags & AI_SUPER_JUMP); + else + self.current_way.b_aiflags = self.current_way.b_aiflags | AI_SUPER_JUMP; + self.b_menu_time = time; + } + else if (self.impulse == 8) + { + if (self.current_way.b_aiflags & AI_CARELESS) + self.current_way.b_aiflags = self.current_way.b_aiflags - (self.current_way.b_aiflags & AI_CARELESS); + else + self.current_way.b_aiflags = self.current_way.b_aiflags | AI_CARELESS; + self.b_menu_time = time; + } + } + if (self.impulse == 9) + { + self.b_menu = MENU_FLAGS2; + self.b_menu_time = time; + } + else if (self.impulse == 10) + { + self.b_menu = MENU_MAIN; + self.b_menu_time = time; + } + } + else if (self.b_menu == MENU_FLAGS2) + { + + if (self.current_way) + { + if (self.impulse == 1) + { + if (self.current_way.b_aiflags & AI_DIFFICULT) + self.current_way.b_aiflags = self.current_way.b_aiflags - (self.current_way.b_aiflags & AI_DIFFICULT); + else + self.current_way.b_aiflags = self.current_way.b_aiflags | AI_DIFFICULT; self.b_menu_time = time; + } + else if (self.impulse == 2) + { + if (self.current_way.b_aiflags & AI_PLAT_BOTTOM) + self.current_way.b_aiflags = self.current_way.b_aiflags - (self.current_way.b_aiflags & AI_PLAT_BOTTOM); + else + self.current_way.b_aiflags = self.current_way.b_aiflags | AI_PLAT_BOTTOM; + self.b_menu_time = time; + } + else if (self.impulse == 3) + { + if (self.current_way.b_aiflags & AI_RIDE_TRAIN) + self.current_way.b_aiflags = self.current_way.b_aiflags - (self.current_way.b_aiflags & AI_RIDE_TRAIN); + else + self.current_way.b_aiflags = self.current_way.b_aiflags | AI_RIDE_TRAIN; + self.b_menu_time = time; + } + else if (self.impulse == 4) + { + if (self.current_way.b_aiflags & AI_DOOR_NO_OPEN) + self.current_way.b_aiflags = self.current_way.b_aiflags - (self.current_way.b_aiflags & AI_DOOR_NO_OPEN); + else + self.current_way.b_aiflags = self.current_way.b_aiflags | AI_DOOR_NO_OPEN; + self.b_menu_time = time; + } + else if (self.impulse == 5) + { + if (self.current_way.b_aiflags & AI_AMBUSH) + self.current_way.b_aiflags = self.current_way.b_aiflags - (self.current_way.b_aiflags & AI_AMBUSH); + else + self.current_way.b_aiflags = self.current_way.b_aiflags | AI_AMBUSH; + self.b_menu_time = time; + } + else if (self.impulse == 6) + { + if (self.current_way.b_aiflags & AI_SNIPER) + self.current_way.b_aiflags = self.current_way.b_aiflags - (self.current_way.b_aiflags & AI_SNIPER); + else + self.current_way.b_aiflags = self.current_way.b_aiflags | AI_SNIPER; + self.b_menu_time = time; + } + else if (self.impulse == 7) + { + if (self.current_way.b_aiflags & AI_TRACE_TEST) + self.current_way.b_aiflags = self.current_way.b_aiflags - (self.current_way.b_aiflags & AI_TRACE_TEST); + else + self.current_way.b_aiflags = self.current_way.b_aiflags | AI_TRACE_TEST; + self.b_menu_time = time; + } + + } + if (self.impulse == 9) + { + self.b_menu = MENU_FLAGS; + self.b_menu_time = time; + } + else if (self.impulse == 10) + { + self.b_menu = MENU_MAIN; + self.b_menu_time = time; + } + } + + else if (self.b_menu == MENU_BOTS) + { + if (self.impulse == 1) + { + self.impulse = 100; + return; + } + else if (self.impulse == 2) + { + b_temp3 = self; + self = player_head; + while(self) + { + if (!self.ishuman) + { + target_add(b_temp3); + bot_get_path(b_temp3, TRUE); + self = world; + } + else + self = self._next; + } + self = b_temp3; + } + else if (self.impulse == 3) + { + self.impulse = 102; + return; + } + else if (self.impulse == 4) + { + b_temp1 = self; + self = player_head; + while(self) + { + if (!self.ishuman) + { + self.target1 = self.target2 = self.target3 = self.target4 = world; + route_table = world; + } + self = self._next; + } + self = b_temp1; + } + else if (self.impulse == 5) + { + if (self.current_way) + { + b_temp1 = self; + self = player_head; + while(self) + { + if (!self.ishuman) + { + setorigin(self, b_temp1.current_way.origin); + } + self = self._next; + } + self = b_temp1; + } + else + sprint(self, "select a waypoint first\n"); + } + else if (self.impulse == 6) + { + self.b_menu = 14; + self.b_menu_time = time; + } + else if (self.impulse == 10) + { + self.b_menu = MENU_MAIN; + self.b_menu_time = time; + } + } + else if (self.b_menu == MENU_WAYLIST) + { + if (self.impulse == 1) + { + self.b_menu = 13; + self.b_menu_time = time; + } + else if (self.impulse == 2) + { + if (dump_mode == 0) + DumpWaypoints(); + else if (dump_mode == 1) + QCDumpWaypoints(); + else if (dump_mode == 2) + BSPDumpWaypoints(); + } + else if (self.impulse == 3) + { + t = way_head; + while(t) + { + if ((t.target1 == world) && (t.target2 == world) && (t.target3 == world) && (t.target4 == world)) + { + sprint(self, "Waypoint #"); + h = ftos(t.count); + sprint(self, h); + sprint(self, " has no outbound links\n"); + } + if ((t.target1 == t) || (t.target2 == t) || (t.target3 == t) || (t.target4 == t)) + { + sprint(self, "Waypoint #"); + h = ftos(t.count); + sprint(self, h); + sprint(self, " links to itself (??)\n"); + } + t = t._next; + } + sprint(self, "Error check complete\n"); + } + else if (self.impulse == 4) + { + sprint(self, "not in this version (FBX 0.10.0)\n"); + } + else if (self.impulse == 5) + { + if (waypoint_mode == WM_EDITOR_DYNAMIC) + waypoint_mode = WM_EDITOR; + else + waypoint_mode = WM_EDITOR_DYNAMIC; + self.b_menu_time = time; + + } + else if (self.impulse == 6) + { + if (waypoint_mode == WM_EDITOR_DYNLINK) + waypoint_mode = WM_EDITOR; + else + waypoint_mode = WM_EDITOR_DYNLINK; + self.b_menu_time = time; + } + else if (self.impulse == 7) + { + dump_mode = 0; + self.b_menu_time = time; + } + else if (self.impulse == 8) + { + dump_mode = 1; + self.b_menu_time = time; + } + else if (self.impulse == 9) + { + dump_mode = 2; + self.b_menu_time = time; + } + else if (self.impulse == 10) + { + self.b_menu = MENU_MAIN; + self.b_menu_time = time; + } + } + else if (self.b_menu == 8) + { + if (self.impulse == 1) + { + if (self.current_way) + { + if (!LinkWays(self.last_way, self.current_way)) + sprint(self, "Unable to link them\n"); + self.b_menu = MENU_LINKS; + self.b_menu_time = time; + } + } + else if (self.impulse == 2) + { + self.b_menu = MENU_LINKS; + self.b_menu_time = time; + } + } + else if (self.b_menu == 9) + { + if (self.impulse == 1) + { + if (self.current_way) + { + if (!TeleLinkWays(self.last_way, self.current_way)) + sprint(self, "Unable to link them\n"); + self.b_menu = MENU_LINKS; + self.b_menu_time = time; + } + } + else if (self.impulse == 2) + { + self.b_menu = MENU_LINKS; + self.b_menu_time = time; + } + } + else if (self.b_menu == 10) + { + if (self.impulse == 1) + { + if (self.current_way) + { + UnlinkWays(self.last_way, self.current_way); + self.b_menu = MENU_LINKS; + self.b_menu_time = time; + } + } + else if (self.impulse == 2) + { + self.b_menu = MENU_LINKS; + self.b_menu_time = time; + } + } + else if (self.b_menu == 11) + { + if (self.impulse == 1) + { + if (self.current_way) + { + if (!LinkWays(self.last_way, self.current_way)) + sprint(self, "Unable to link 1 to 2\n"); + if (!LinkWays(self.current_way, self.last_way)) + sprint(self, "Unable to link 2 to 1\n"); + self.b_menu = MENU_LINKS; + self.b_menu_time = time; + } + } + else if (self.impulse == 2) + { + self.b_menu = MENU_LINKS; + self.b_menu_time = time; + } + } + else if (self.b_menu == 12) + { + if (self.impulse == 1) + { + if (self.current_way) + { + UnlinkWays(self.last_way, self.current_way); + UnlinkWays(self.current_way, self.last_way); + self.b_menu = MENU_LINKS; + self.b_menu_time = time; + } + } + else if (self.impulse == 2) + { + self.b_menu = MENU_LINKS; + self.b_menu_time = time; + } + } + else if (self.b_menu == 13) + { + if (self.impulse == 1) + { + ClearAllWays(); + self.b_menu = MENU_WAYLIST; + self.b_menu_time = time; + } + else if (self.impulse == 2) + { + self.b_menu = MENU_WAYLIST; + self.b_menu_time = time; + } + } + else if (self.b_menu == 14) + { + if (self.impulse == 10) + self.impulse = 0; + self.b_menu_value = self.b_menu_value * 10 + self.impulse; + self.b_menu_time = 0; + } + else if (self.b_menu == 15) + { + if (self.impulse == 1) + { + delete_waypoint(self.last_way); + self.b_menu = MENU_WAYPOINTS; + self.b_menu_time = time; + } + else if (self.impulse == 2) + { + self.b_menu = MENU_WAYPOINTS; + self.b_menu_time = time; + } + } + self.impulse = 0; + + } + if (self.b_menu_time < time) + { + if (self.b_menu == MENU_MAIN) + { + s1 = "-- Main Menu --\n[1] >>Waypoint Management\n[2] >>Link Management \n[3] >>AI Flag Management \n[4] >>Bot Management \n[5] >>Waylist Management \n"; + if (self.movetype == MOVETYPE_NOCLIP) + s2 = "[6] [#] Noclip \n"; + else + s2 = "[6] [ ] Noclip \n"; + + if (self.flags & FL_GODMODE) + s3 = "[7] [#] Godmode \n"; + else + s3 = "[7] [ ] Godmode \n"; + if (self.b_aiflags & AI_HOLD_SELECT) + s4 = "[8] [#] Hold Select \n"; + else + s4 = "[8] [ ] Hold Select \n"; + s5 = "[9] Teleport to Way # \n[0] Close Menu \n"; + } + else if (self.b_menu == MENU_WAYPOINTS) + { + s1 = "-- Waypoint Management --\n[1] Move Waypoint \n[2] Delete Waypoint \n[3] Make Waypoint \n[4] Make Way + Link \n[5] Make Way + Link X2 \n[6] Make Way + Telelink \n[7] Show waypoint info \n[8] >>Link Management \n[9] >>AI Flag Management \n[0] >>Main Menu \n"; + } + else if (self.b_menu == MENU_LINKS) + { + s1 = "-- Link Management --\n[1] Unlink Waypoint \n[2] Create Link \n[3] Create Telelink \n[4] Delete Link \n[5] Create Link X2 \n[6] Delete Link X2 \n[7] >Make Waypoint \n[8] >>Waypoint Management\n[9] >>AI Flag Management \n[0] >>Main Menu \n"; + } + else if (self.b_menu == MENU_FLAGS) + { + if (self.current_way.b_aiflags & AI_DOORFLAG) + s1 = "-- AI Flag Management --\n[1] [#] Door Flag \n"; + else + s1 = "-- AI Flag Management --\n[1] [ ] Door Flag \n"; + + if (self.current_way.b_aiflags & AI_PRECISION) + s2 = "[2] [#] Precision \n"; + else + s2 = "[2] [ ] Precision \n"; + + if (self.current_way.b_aiflags & AI_SURFACE) + s3 = "[3] [#] Surface for air \n"; + else + s3 = "[3] [ ] Surface for air \n"; + + if (self.current_way.b_aiflags & AI_BLIND) + s4 = "[4] [#] Blind mode \n"; + else + s4 = "[4] [ ] Blind mode \n"; + + if (self.current_way.b_aiflags & AI_JUMP) + s5 = "[5] [#] Jump \n"; + else + s5 = "[5] [ ] Jump \n"; + + if (self.current_way.b_aiflags & AI_DIRECTIONAL) + s6 = "[6] [#] Directional \n"; + else + s6 = "[6] [ ] Directional \n"; + + // Electro - eww + if ( (self.current_way.b_aiflags & AI_SUPER_JUMP) && (self.current_way.b_aiflags & AI_CARELESS) ) + s7 = "[7] [#] Super Jump \n[8] [#] Careless \n[9] >>AI Flags page 2 \n[0] >>Main Menu \n"; + else if ( (self.current_way.b_aiflags & AI_SUPER_JUMP) && (!(self.current_way.b_aiflags & AI_CARELESS)) ) + s7 = "[7] [#] Super Jump \n[8] [ ] Careless \n[9] >>AI Flags page 2 \n[0] >>Main Menu \n"; + else if ( (!(self.current_way.b_aiflags & AI_SUPER_JUMP)) && (self.current_way.b_aiflags & AI_CARELESS) ) + s7 = "[7] [ ] Super Jump \n[8] [#] Careless \n[9] >>AI Flags page 2 \n[0] >>Main Menu \n"; + else if ( (!(self.current_way.b_aiflags & AI_SUPER_JUMP)) && (!(self.current_way.b_aiflags & AI_CARELESS)) ) + s7 = "[7] [ ] Super Jump \n[8] [ ] Careless \n[9] >>AI Flags page 2 \n[0] >>Main Menu \n"; + } + else if (self.b_menu == MENU_FLAGS2) + { + if (self.current_way.b_aiflags & AI_DIFFICULT) + s1 = "-- AI Flags pg. 2--\n[1] [#] Difficult \n"; + else + s1 = "-- AI Flags pg. 2--\n[1] [ ] Difficult \n"; + + if (self.current_way.b_aiflags & AI_PLAT_BOTTOM) + s2 = "[2] [#] Wait for plat \n"; + else + s2 = "[2] [ ] Wait for plat \n"; + + if (self.current_way.b_aiflags & AI_RIDE_TRAIN) + s3 = "[3] [#] Ride train \n"; + else + s3 = "[3] [ ] Ride train \n"; + + if (self.current_way.b_aiflags & AI_DOOR_NO_OPEN) + s4 = "[4] [#] Door flag no open\n"; + else + s4 = "[4] [ ] Door flag no open\n"; + + if (self.current_way.b_aiflags & AI_AMBUSH) + s5 = "[5] [#] Ambush \n"; + else + s5 = "[5] [ ] Ambush \n"; + + if (self.current_way.b_aiflags & AI_SNIPER) + s6 = "[6] [#] Snipe \n"; + else + s6 = "[6] [ ] Snipe \n"; + + if (self.current_way.b_aiflags & AI_TRACE_TEST) + s7 = "[7] [#] Trace Test \n\n[9] >>AI Flag Management \n[0] >>Main Menu \n"; + else + s7 = "[7] [ ] Trace Test \n\n[9] >>AI Flag Management \n[0] >>Main Menu \n"; + + } + else if (self.b_menu == MENU_BOTS) + { + s1 = "-- Bot Management --\n[1] Add a Test Bot \n[2] Order Test Bot here \n[3] Remove Test Bot \n[4] Stop Test Bot \n[5] Teleport Bot here \n[6] Teleport to Way # \n\n\n\n[0] >>Main Menu \n"; + } + else if (self.b_menu == MENU_WAYLIST) + { + s1 = "-- Waylist Management --\n[1] Delete ALL Waypoints \n[2] Dump Waypoints \n[3] Check For Errors \n[4] Save Waypoints \n"; + + if (waypoint_mode == WM_EDITOR_DYNAMIC) + s2 = "[5] [#] Dynamic Mode \n[6] [#] Dynamic Link \n"; + else if (waypoint_mode == WM_EDITOR_DYNLINK) + s2 = "[5] [ ] Dynamic Mode \n[6] [#] Dynamic Link \n"; + else + s2 = "[5] [ ] Dynamic Mode \n[6] [ ] Dynamic Link \n"; + if (dump_mode == 0) + s3 = "[7] [#] WAY output \n[8] [ ] QC output \n[9] [ ] BSP ents output \n[0] Main Menu \n"; + else if (dump_mode == 1) + s3 = "[7] [ ] WAY output \n[8] [#] QC output \n[9] [ ] BSP ents output \n[0] Main Menu \n"; + else if (dump_mode == 2) + s3 = "[7] [ ] WAY output \n[8] [ ] QC output \n[9] [#] BSP ents output \n[0] Main Menu \n"; + + } + else if (self.b_menu == 8) + s1 = "-- Link Ways --\n\nSelect another way and push 1\nor press 2 to cancel"; + else if (self.b_menu == 9) + s1 = "-- Telelink Ways --\n\nSelect another way and push 1\nor press 2 to cancel"; + else if (self.b_menu == 10) + s1 = "-- Delete Link --\n\nSelect another way and push 1\nor press 2 to cancel"; + else if (self.b_menu == 11) + s1 = "-- Create Link X2 --\n\nSelect another way and push 1\nor press 2 to cancel"; + else if (self.b_menu == 12) + s1 = "-- Delete Link X2 --\n\nSelect another way and push 1\nor press 2 to cancel"; + else if (self.b_menu == 13) + s1 = "-- Delete ALL Ways --\n\nAre you sure? Push 1 to go\nthrough with it, 2 to cancel"; + else if (self.b_menu == 14) + { + s1 = "-- Teleport to Way # --\n\nEnter way number and press\nimpulse 104 to warp\n\nWaypoint #"; + s2 = ftos(self.b_menu_value); + + } + else if (self.b_menu == 15) + s1 = "-- Delete Waypoint --\n\nAre you sure? Push 1 to go\nthrough with it, 2 to cancel"; + frik_big_centerprint(self, s1, s2, s3, s4, s5, s6, s7); + self.b_menu_time = time + 1.25; + } +}; + + +// engage menu +void() bot_way_edit = +{ + local entity t; + local float f; + if (self.b_menu_value) + { + if (self.b_menu == 14) + { + t = WaypointForNum(self.b_menu_value); + if (t) + setorigin(self, t.origin - self.view_ofs); + else + sprint(self, "No waypoint with that number\n"); + + self.b_menu = MENU_MAIN; + self.b_menu_time = time; + } + self.b_menu_value = 0; + return; + } + if (waypoint_mode < WM_EDITOR) + { + self.b_menu = MENU_MAIN; + waypoint_mode = WM_EDITOR; + self.b_menu_time = 0; + cvar_set("saved2", "0"); + WriteByte(MSG_ALL, 8); + WriteByte(MSG_ALL, 1); + WriteString(MSG_ALL, "MAKE SURE THE FOLLOWING LINE CONTAINS -CONDEBUG BEFORE PROCEEDING\n"); + localcmd("cmdline\n"); + t = way_head; + while (t) + { + setmodel(t, "progs/s_bubble.spr"); // show the waypoints + t = t._next; + } + if (self.current_way) + setmodel(self.current_way, "progs/s_light.spr"); + } + else + { + saved2 = cvar("saved2"); + if (saved2 != 0) + { + f = self.b_menu; + self.b_menu = floor(saved2/16); + self.impulse = saved2 & 15; + bot_menu_display(); + self.b_menu = f; + cvar_set("saved2", "0"); + return; + } + self.b_menu = 0; + waypoint_mode = WM_LOADED; + t = way_head; + while (t) + { + setmodel(t, string_null); // hide the waypoints + t = t._next; + } + } +}; + + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +Waypoint Saving to file. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + + +// bytecount is really iffy +// since there is no true way to determine the length of an ftos +// it uses an approximate of 5 +// various other things are guesses, but I don't cut it at the absolute +// max so it should be okay + +void() PrintWaypoint = +{ + local entity t; + local float needcolon; + local string h; + + if (self.enemy == world) + t = way_head; + else + t = self.enemy._next; + if (bytecounter >= 8000) + { + bprint("exec maps/"); + bprint(mapname); + bprint(".wa"); + h = ftos(filecount); + bprint(h); + filecount = filecount + 1; + bprint("\n// **** break here **** \n"); + bytecounter = 26; + } + if (t == world) + { + remove(self); + fixer = world; + bprint("saved4 3\n// end waypoint dump\n"); + bytecounter = bytecounter + 27; + return; + } + if ((t.origin_x != saved1) || (t.count == 1)) + { + bprint("saved1 "); + h = ftos(t.origin_x); + bprint(h); + saved1 = t.origin_x; + bytecounter = bytecounter + 12; + needcolon = TRUE; + } + if ((t.origin_y != saved2) || (t.count == 1)) + { + if (needcolon) + { + bprint("; "); + bytecounter = bytecounter + 2; + } + else + needcolon = TRUE; + bprint("saved2 "); + h = ftos(t.origin_y); + bprint(h); + bytecounter = bytecounter + 12; + saved2 = t.origin_y; + } + if ((t.origin_z != saved3) || (t.count == 1)) + { + if (needcolon) + { + bprint("; "); + bytecounter = bytecounter + 2; + } + else + needcolon = TRUE; + bprint("saved3 "); + h = ftos(t.origin_z); + bprint(h); + bytecounter = bytecounter + 12; + saved3 = t.origin_z; + } + bytecounter = bytecounter + 1; + bprint("\n"); + needcolon = FALSE; + if ((scratch1 != t.target1.count) || t.count == 1) + { + needcolon = TRUE; + bprint("scratch1 "); + bytecounter = bytecounter + 14; + h = ftos(t.target1.count); + bprint(h); + scratch1 = t.target1.count; + } + if ((scratch2 != t.target2.count) || t.count == 1) + { + if (needcolon) + { + bprint("; "); + bytecounter = bytecounter + 2; + } + else + needcolon = TRUE; + bprint("scratch2 "); + bytecounter = bytecounter + 14; + h = ftos(t.target2.count); + bprint(h); + scratch2 = t.target2.count; + } + if ((scratch3 != t.target3.count) || t.count == 1) + { + if (needcolon) + { + bprint("; "); + bytecounter = bytecounter + 2; + } + else + needcolon = TRUE; + bprint("scratch3 "); + bytecounter = bytecounter + 14; + h = ftos(t.target3.count); + bprint(h); + scratch3 = t.target3.count; + } + if ((scratch4 != t.target4.count) || t.count == 1) + { + if (needcolon) + { + bprint("; "); + bytecounter = bytecounter + 2; + } + else + needcolon = TRUE; + bprint("scratch4 "); + bytecounter = bytecounter + 14; + h = ftos(t.target4.count); + bprint(h); + scratch4 = t.target4.count; + } + bprint("\nsaved4 "); + bytecounter = bytecounter + 19; + if (t.count != 1) + h = ftos(t.b_aiflags * 4 + 2); + else + h = ftos(t.b_aiflags * 4 + 1); + bprint(h); + bprint ("; wait\n"); + self.nextthink = time + 0.01; + self.enemy = t; +}; + +// to allow for 100+ waypoints, we need to trick the runaway loop counter +void() DumpWaypoints = +{ + bytecounter = 50; + filecount = 1; + + bprint("// "); + bprint(world.message); + bprint("- maps/"); + bprint(mapname); + bprint(".way\n"); + bprint("// Ways by "); + bprint(self.netname); + bprint("\n"); + if (!fixer) + { + fixer = spawn(); + fixer.nextthink = time + 0.01; + fixer.think = PrintWaypoint; + fixer.enemy = world; + } +}; + +void() PrintQCWaypoint = +{ + local entity t; + local string h; + + if (self.enemy == world) + t = way_head; + else + t = self.enemy._next; + + if (t == world) + { + remove(self); + fixer = world; + bprint("};\n\n// End dump\n"); + return; + } + bprint(" make_way("); + h = vtos(t.origin); + bprint(h); + bprint(", '"); + h = ftos(t.target1.count); + bprint(h); + bprint(" "); + h = ftos(t.target2.count); + bprint(h); + bprint(" "); + h = ftos(t.target3.count); + bprint(h); + bprint("', "); + h = ftos(t.target4.count); + bprint(h); + bprint(", "); + h = ftos(t.b_aiflags); + bprint(h); + bprint(");\n"); + self.nextthink = time + 0.01; + self.enemy = t; + +}; +void() QCDumpWaypoints = +{ + bprint("/* QC Waypoint Dump - src/frikbot/map_"); + + bprint(mapname); + bprint(".qc\nFor instructions please read the\nreadme.html that comes with FrikBot */\n\nvoid(vector org, vector bit1, float bit4, float flargs) make_way;\n"); + bprint("// Ways by "); + bprint(self.netname); + bprint("\n\n"); + + bprint("void() map_"); + bprint(mapname); + bprint(" =\n{\n"); + + + if (!fixer) + { + fixer = spawn(); + fixer.nextthink = time + 0.01; + fixer.think = PrintQCWaypoint; + fixer.enemy = world; + } +}; + +void() PrintBSPWaypoint = +{ + local entity t; + local string h; + + if (self.enemy == world) + t = way_head; + else + t = self.enemy._next; + + if (t == world) + { + bprint("\n\n// End dump\n"); + remove(self); + fixer = world; + return; + } + bprint("{\n\"classname\" \"waypoint\"\n\"origin\" \""); + h = ftos(t.origin_x); + bprint(h); + bprint(" "); + h = ftos(t.origin_y); + bprint(h); + bprint(" "); + h = ftos(t.origin_z); + bprint(h); + if (t.target1.count) + { + bprint("\"\n\"b_pants\" \""); + h = ftos(t.target1.count); + bprint(h); + } + if (t.target2.count) + { + bprint("\"\n\"b_skill\" \""); + h = ftos(t.target2.count); + bprint(h); + } + if (t.target3.count) + { + bprint("\"\n\"b_shirt\" \""); + h = ftos(t.target3.count); + bprint(h); + } + if (t.target4.count) + { + bprint("\"\n\"b_frags\" \""); + h = ftos(t.target4.count); + bprint(h); + } + if (t.b_aiflags) + { + bprint("\"\n\"b_aiflags\" \""); + h = ftos(t.b_aiflags); + bprint(h); + } + bprint("\"\n}\n"); + self.nextthink = time + 0.01; + self.enemy = t; + +}; +void() BSPDumpWaypoints = +{ + bprint("/* BSP entities Dump - maps/"); + + bprint(mapname); + bprint(".ent\nFor instructions please read the\nreadme.html that comes with FrikBot */\n\n\n"); + + if (!fixer) + { + fixer = spawn(); + fixer.nextthink = time + 0.01; + fixer.think = PrintBSPWaypoint; + fixer.enemy = world; + } +}; diff --git a/qcsrc/gamec/bot_fight.c b/qcsrc/gamec/bot_fight.c new file mode 100644 index 000000000..8098b9fcf --- /dev/null +++ b/qcsrc/gamec/bot_fight.c @@ -0,0 +1,458 @@ +/*********************************************** +* * +* FrikBot Fight Code * +* "Because I ain't no Ghandi code" * +* * +***********************************************/ + +/* + +This program is in the Public Domain. My crack legal +team would like to add: + +RYAN "FRIKAC" SMITH IS PROVIDING THIS SOFTWARE "AS IS" +AND MAKES NO WARRANTY, EXPRESS OR IMPLIED, AS TO THE +ACCURACY, CAPABILITY, EFFICIENCY, MERCHANTABILITY, OR +FUNCTIONING OF THIS SOFTWARE AND/OR DOCUMENTATION. IN +NO EVENT WILL RYAN "FRIKAC" SMITH BE LIABLE FOR ANY +GENERAL, CONSEQUENTIAL, INDIRECT, INCIDENTAL, +EXEMPLARY, OR SPECIAL DAMAGES, EVEN IF RYAN "FRIKAC" +SMITH HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES, IRRESPECTIVE OF THE CAUSE OF SUCH DAMAGES. + +You accept this software on the condition that you +indemnify and hold harmless Ryan "FrikaC" Smith from +any and all liability or damages to third parties, +including attorney fees, court costs, and other +related costs and expenses, arising out of your use +of this software irrespective of the cause of said +liability. + +The export from the United States or the subsequent +reexport of this software is subject to compliance +with United States export control and munitions +control restrictions. You agree that in the event you +seek to export this software, you assume full +responsibility for obtaining all necessary export +licenses and approvals and for assuring compliance +with applicable reexport restrictions. + +Any reproduction of this software must contain +this notice in its entirety. + +*/ + +.entity avoid; + +float(entity e) bot_size_player = +{ + local float sz; + + sz = e.health + e.armorvalue * e.armortype; + if (e.weapon == 32) + sz = sz + 60; + else if (e.weapon == 64) + sz = sz + 60; + else if (e.weapon == 16) + sz = sz + 50; + else if (e.weapon == 8) + sz = sz + 50; + else if (e.weapon == 4) + sz = sz + 40; + else if (e.weapon == 2) + sz = sz + 40; + else if (e.weapon == 1) + sz = sz + 10; + else if (e.weapon == 4096) + sz = sz - 50; + if (e.items & 4194304) // Quad + sz = sz + 200; + if (e.items & 1048576) // Invul + sz = sz + 300; + if (e.items & 524288) // Invis + sz = sz + 250; + return sz; +}; + +void() bot_dodge_stuff = +{ + local entity foe; + local float foedist, avdist, scandist, foesz, flen, tsz; + local vector v; + + if (waypoint_mode > WM_LOADED) + return; + + self.avoid = world; + + + if (self.enemy) + { + v = self.origin - realorigin(self.enemy); + foedist = vlen(v); + foesz = bot_size_player(self.enemy); + } + else + { + foedist = 3000; + foesz = 9999999; + } + avdist = 256; + + foe = find(world, classname, "grenade"); + while(foe) + { + flen = vlen(foe.origin - self.origin); + if (flen < avdist) + { + avdist = flen; + self.avoid = foe; + } + foe = find(foe, classname, "grenade"); + } + if (!self.avoid) + { + foe = find(world, classname, "missile"); + while(foe) + { + if (foe.owner != self) + { + flen = vlen(foe.origin - self.origin); + if (flen < avdist) + { + avdist = flen; + self.avoid = foe; + } + } + foe = find(foe, classname, "missile"); + } + if (!self.avoid) + { + foe = find(world, classname, "spike"); + while(foe) + { + if (foe.owner != self) + { + flen = vlen(foe.origin - self.origin); + if (flen < avdist) + { + avdist = flen; + self.avoid = foe; + } + } + foe = find(foe, classname, "spike"); + } + } + } + if (coop) + { + if (!self.enemy) + { + foe = findradius(self.origin, foedist); + while(foe) + { + if(foe.flags & FL_MONSTER) + { + if(foe.health > 0) + { + flen = vlen(foe.origin - self.origin); + if (flen < foedist) + { + tsz = bot_size_player(foe); + if (tsz < foesz) + { + if (fisible(foe)) + { + self.enemy = foe; + foedist = flen; + foesz = tsz; + } + } + } + } + } + foe = foe.chain; + } + } + } + else + { + foe = player_head; + while(foe) + { + if(foe != self) + { + if (foe.modelindex != 0) + { + if (foe.health > 0) + { + if (!(teamplay && self.team == foe.team)) + { + flen = vlen(foe.origin - self.origin); + if (flen < foedist) + { + tsz = bot_size_player(foe); + if (tsz < foesz) + { + if (fov(foe) || foe.b_sound > time || self.b_skill == 3) + { + if (fisible(foe)) + { + self.enemy = foe; + foedist = vlen(foe.origin - self.origin); + } + } + } + } + } + } + } + } + foe = foe._next; + } + } +}; + + + + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +weapon_range + +_x "sweet spot range" - try to maintain this range if possible +_y minimum range bot can be to be effective (rl/gl) (move away) +_z maximum range bot can be to be effective (lg/axe) (move in) +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +vector(float wep) weapon_range = +{ + if (wep == 4096) // IT_AXE + return '48 0 64'; + else if (wep == 1) // IT_SHOTGUN + return '128 0 99999'; + else if (wep == 2) // IT_SUPER_SHOTGUN + return '128 0 99999'; + else if (wep == 4) // IT_NAILGUN + return '180 0 3000'; + else if (wep == 8) // IT_SUPER_NAILGUN + return '180 0 3000'; + else if (wep == 16) // IT_GRENADE_LAUNCHER + return '180 48 3000'; + else if (wep == 32) // IT_ROCKET_LAUNCHER + return '180 48 3000'; + else if (wep == 64) // IT_LIGHTNING + return '350 0 512'; +}; +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +bot_weapon_switch + +Pick a weapon based on range / ammo + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void(float brange) bot_weapon_switch = +{ + local float it, flag, pulse; + local vector v; + + it = self.items & 127; + + while(it) + { + if ((self.ammo_rockets >= 1) && (it & 32)) + { + flag = 32; + pulse = 7; + } + else if (self.waterlevel <= 1 && self.ammo_cells >= 1 && (it & 64)) + { + flag = 64; + pulse = 8; + } + else if(self.ammo_nails >= 2 && (it & 8)) + { + flag = 8; + pulse = 5; + } + else if ((self.ammo_rockets >= 1) && (it & 16)) + { + flag = 16; + pulse = 6; + } + else if(self.ammo_shells >= 2 && (it & 2)) + { + flag = 2; + pulse = 3; + } + else if(self.ammo_nails >= 1 && (it & 4)) + { + flag = 4; + pulse = 4; + } + else if(self.ammo_shells >= 1 && (it & 1)) + { + flag = 1; + pulse = 2; + } + else + { + if (pulse) + self.impulse = pulse; + return; + } + + if (brange == -1) + { + if (pulse) + self.impulse = pulse; + return; + } + + v = weapon_range(flag); + if (brange < v_y || brange > v_z) + it = it - flag; + else + { + if (pulse) + self.impulse = pulse; + return; + } + } +}; + +void() bot_shoot = +{ + // quick little function to stop making him shoot the wrong way ! Argh + local float g; + g = angcomp(self.v_angle_x, self.b_angle_x); + if (fabs(g) > 30) + return; // argh, too far away + g = angcomp(self.v_angle_y, self.b_angle_y); + if (fabs(g) > 30) + return; // not again! + self.button0 = TRUE; +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +Bot_fight_style + +This is the core of the bot's thinking when +attacking an enemy. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void() bot_fight_style = +{ + local vector v, v1, v2, org; + local float foedist, mysz, foesz; + + + if (self.enemy.health <= 0) + { + self.enemy = world; + return; + } + else if (!self.enemy.takedamage) + { + self.enemy = world; + return; + } + else if (!fisible(self.enemy)) + { + self.enemy = world; + return; + } + + org = realorigin(self.enemy); + makevectors(self.v_angle); + + // decide if I should shoot + + foedist = vlen(org - self.origin); + v = weapon_range(self.weapon); + if (foedist > v_y && foedist < v_z) + { + traceline(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * v_z, FALSE, self); + if (vlen(trace_endpos - (self.origin + self.view_ofs)) >= v_y) + { + // try to avoid shooting teammates + if (trace_ent.classname == "player") + if ((trace_ent.team == self.team && teamplay) || (coop)) + return; + bot_shoot(); + } + } + else + bot_weapon_switch(foedist); + + if (!(self.b_aiflags & (AI_PRECISION | AI_BLIND | AI_OBSTRUCTED))) + { + foesz = bot_size_player(self.enemy); + mysz = bot_size_player(self) + 5; + + if (foesz > mysz) + { + if (teamplay) + { + if (random() < 0.02) + { + bot_start_topic(5); + self.b_chattime = 1; + } + } + + return; + } + else if (mysz < 140) + return; + else if (self.avoid) + { + if (self.avoid.velocity) + v = self.avoid.velocity; + else + v = normalize(self.avoid.origin - self.origin); + v1_x = v_y; + v1_y = v_y * -1; + v2_x = v_y; + v2_y = v_y * -1; + foedist = vlen(self.avoid.origin - (self.origin + v1)); + if (foedist < vlen(self.avoid.origin - (self.origin + v2))) + frik_walkmove(v2); + else + frik_walkmove(v1); + } + else if (!self.enemy.flags & FL_MONSTER) + { + if (foedist + 32 < v_x) + frik_walkmove(self.origin - org); + else if (foedist - 32 > v_x) + frik_walkmove(org - self.origin); + else if (self.wallhug) + frik_walkmove(v_right); + else + frik_walkmove(v_right * -1); + } + } + else + { + foesz = bot_size_player(self.enemy); + mysz = bot_size_player(self) + 5; + + if (foesz > mysz) + return; + else if (mysz < 140) + return; + self.keys = self.keys & 960; + } +}; + + diff --git a/qcsrc/gamec/bot_maps.c b/qcsrc/gamec/bot_maps.c new file mode 100644 index 000000000..57a12e82e --- /dev/null +++ b/qcsrc/gamec/bot_maps.c @@ -0,0 +1,152 @@ +void(vector org, vector bit1, float bit4, float flargs) make_way; +// Ways by Electro + +void() map_mattrye1_nex = +{ + make_way('655.3 370.1 -34.0', '2 43 0', 0, 0); + make_way('214.7 338.9 -34.0', '1 3 0', 0, 0); + make_way('-70.7 197.7 -34.0', '2 4 5', 0, 0); + make_way('-263.8 27.5 -34.0', '3 5 6', 10, 0); + make_way('-263.8 199.7 -34.0', '4 6 3', 0, 0); + make_way('-394.6 201.2 -34.0', '5 7 4', 0, 0); + make_way('-617.8 260.5 -34.0', '6 8 0', 0, 0); + make_way('-644.1 428.5 -34.0', '7 9 0', 0, 0); + make_way('-642.2 727.9 -34.0', '8 0 0', 0, 0); + make_way('-259.3 -187.2 -34.0', '4 11 12', 0, 0); + make_way('-26.8 -384.2 -34.0', '10 12 32', 0, 0); + make_way('-254.3 -368.7 -34.0', '10 11 13', 0, 0); + make_way('-453.0 -380.6 -34.0', '12 14 0', 0, 0); + make_way('-634.6 -436.6 -34.0', '13 15 0', 0, 0); + make_way('-724.5 -730.3 -34.0', '14 16 0', 0, 0); + make_way('-1118.6 -771.6 -34.0', '15 17 0', 0, 0); + make_way('-1509.0 -737.1 62.0', '16 18 0', 0, 0); + make_way('-1519.2 -374.8 182.0', '17 19 41', 0, 0); + make_way('-1035.7 -196.8 182.0', '18 20 39', 41, 0); + make_way('-749.3 -135.9 182.0', '19 21 0', 0, 0); + make_way('-576.4 -26.8 182.0', '20 22 0', 0, 0); + make_way('-404.1 -18.0 182.0', '21 23 12', 0, 0); + make_way('-123.2 -18.2 182.0', '22 24 10', 12, 0); + make_way('102.6 -27.2 182.0', '23 25 0', 0, 0); + make_way('106.8 -383.1 182.0', '24 26 36', 0, 0); + make_way('-232.4 -342.7 278.0', '25 27 13', 0, 0); + make_way('-245.5 -20.1 374.0', '26 28 29', 21, 0); + make_way('107.1 6.3 374.0', '27 25 0', 0, 0); + make_way('-277.8 554.4 374.0', '27 30 0', 0, 0); + make_way('-520.5 574.9 374.0', '29 31 9', 8, 0); + make_way('-768.3 574.9 374.0', '30 8 9', 0, 0); + make_way('465.9 -423.7 -34.0', '11 33 0', 0, 0); + make_way('543.3 -660.4 -34.0', '32 34 0', 0, 0); + make_way('537.0 -955.6 -34.0', '33 35 0', 0, 0); + make_way('165.7 -926.3 86.0', '34 36 0', 0, 0); + make_way('138.8 -740.6 155.8', '35 25 33', 0, 0); + make_way('581.8 195.7 -21.1', '38 0 0', 0, 262144); + make_way('847.0 120.2 191.0', '28 0 0', 0, 262144); + make_way('-1101.5 176.0 182.0', '19 40 0', 0, 0); + make_way('-1584.0 173.6 182.0', '39 0 0', 0, 0); + make_way('-1295.4 -37.8 148.2', '18 42 19', 0, 0); + make_way('-1362.7 -55.6 -221.2', '41 0 0', 0, 0); + make_way('769.2 196.2 -34.0', '1 44 0', 0, 0); + make_way('633.1 193.1 -34.0', '43 37 0', 0, 256); +}; +/* +// Ways by Electro + +void() map_mattrye2 = +{ + make_way('-652.5 -1043.9 511.0', '4 0 0', 0, 0); + make_way('-992.5 -617.0 415.0', '3 7 0', 0, 0); + make_way('-953.0 -50.2 351.0', '6 8 0', 0, 0); + make_way('-647.0 77.1 351.0', '7 9 0', 0, 0); + make_way('-698.8 764.0 239.0', '8 10 0', 0, 0); + make_way('-1054.6 766.4 239.0', '9 11 0', 0, 0); + make_way('-1357.9 786.5 239.0', '10 12 0', 0, 0); + make_way('-1373.6 1502.7 127.0', '11 13 0', 0, 0); + make_way('-1110.3 1625.0 127.0', '12 14 0', 0, 0); + make_way('-985.9 2144.7 127.0', '13 15 0', 0, 0); + make_way('-495.4 2230.9 127.0', '14 16 17', 0, 0); + make_way('-47.4 2158.4 127.0', '15 40 0', 0, 0); + make_way('-501.6 2626.1 127.0', '15 18 0', 0, 0); + make_way('346.4 2679.1 223.0', '17 19 20', 0, 0); + make_way('433.6 2248.8 223.0', '18 20 0', 0, 0); + make_way('550.4 2675.8 223.0', '19 18 21', 0, 0); + make_way('876.8 2671.6 271.0', '20 22 0', 0, 0); + make_way('837.9 2263.6 319.0', '21 19 23', 0, 0); + make_way('925.9 1798.9 319.0', '22 24 0', 0, 0); + make_way('1644.3 1772.2 351.0', '23 25 0', 0, 0); + make_way('1628.4 1068.5 351.0', '24 26 0', 0, 0); + make_way('1409.9 965.9 351.0', '25 27 0', 0, 0); + make_way('1073.8 789.9 351.0', '26 28 44', 45, 0); + make_way('1408.4 645.5 416.0', '27 29 26', 0, 0); + make_way('1653.5 624.0 479.0', '28 30 0', 0, 0); + make_way('1662.9 317.8 479.0', '29 31 45', 0, 0); + make_way('1637.0 28.1 479.0', '30 32 0', 0, 0); + make_way('1060.8 -48.7 479.0', '31 33 0', 0, 0); + make_way('996.9 -484.3 479.0', '32 34 0', 0, 0); + make_way('550.8 -491.1 479.0', '33 35 37', 39, 0); + make_way('542.9 -914.0 479.0', '34 36 37', 39, 0); + make_way('712.4 -1089.3 511.0', '35 0 0', 0, 0); + make_way('-13.4 -289.6 479.0', '34 39 35', 3, 0); + make_way('-97.3 -677.8 479.0', '2 4 3', 0, 0); + make_way('144.7 -679.1 479.0', '35 37 34', 0, 0); + make_way('2.3 1824.9 127.0', '16 41 0', 0, 0); + make_way('6.0 1232.9 127.0', '40 42 0', 0, 0); + make_way('10.5 768.3 127.0', '41 43 0', 0, 0); + make_way('5.0 375.1 127.0', '42 44 0', 0, 0); + make_way('875.6 378.7 351.0', '43 45 27', 0, 0); + make_way('1090.9 453.8 351.0', '44 27 0', 0, 0); + make_way('-560.0 1269.1 351.0', '0 0 0', 0, 0); + make_way('-434.0 1282.5 127.0', '41 40 42', 0, 0); + make_way('602.4 1275.2 351.0', '0 0 0', 0, 0); + make_way('430.0 1282.1 127.0', '41 42 40', 0, 0); +}; + +// Ways by Electro + +void() map_mattrye1_nex = +{ + make_way('655.3 370.1 -34.0', '2 43 0', 0, 0); + make_way('214.7 338.9 -34.0', '1 3 0', 0, 0); + make_way('-70.7 197.7 -34.0', '2 4 5', 0, 0); + make_way('-263.8 27.5 -34.0', '3 5 6', 10, 0); + make_way('-263.8 199.7 -34.0', '4 6 3', 0, 0); + make_way('-394.6 201.2 -34.0', '5 7 4', 0, 0); + make_way('-617.8 260.5 -34.0', '6 8 0', 0, 0); + make_way('-644.1 428.5 -34.0', '7 9 0', 0, 0); + make_way('-642.2 727.9 -34.0', '8 0 0', 0, 0); + make_way('-259.3 -187.2 -34.0', '4 11 12', 0, 0); + make_way('-26.8 -384.2 -34.0', '10 12 32', 0, 0); + make_way('-254.3 -368.7 -34.0', '10 11 13', 0, 0); + make_way('-453.0 -380.6 -34.0', '12 14 0', 0, 0); + make_way('-634.6 -436.6 -34.0', '13 15 0', 0, 0); + make_way('-724.5 -730.3 -34.0', '14 16 0', 0, 0); + make_way('-1118.6 -771.6 -34.0', '15 17 0', 0, 0); + make_way('-1509.0 -737.1 62.0', '16 18 0', 0, 0); + make_way('-1519.2 -374.8 182.0', '17 19 41', 0, 0); + make_way('-1035.7 -196.8 182.0', '18 20 39', 41, 0); + make_way('-749.3 -135.9 182.0', '19 21 0', 0, 0); + make_way('-576.4 -26.8 182.0', '20 22 0', 0, 0); + make_way('-404.1 -18.0 182.0', '21 23 12', 0, 0); + make_way('-123.2 -18.2 182.0', '22 24 10', 12, 0); + make_way('102.6 -27.2 182.0', '23 25 0', 0, 0); + make_way('106.8 -383.1 182.0', '24 26 36', 0, 0); + make_way('-232.4 -342.7 278.0', '25 27 13', 0, 0); + make_way('-245.5 -20.1 374.0', '26 28 29', 21, 0); + make_way('107.1 6.3 374.0', '27 25 0', 0, 0); + make_way('-277.8 554.4 374.0', '27 30 0', 0, 0); + make_way('-520.5 574.9 374.0', '29 31 9', 8, 0); + make_way('-768.3 574.9 374.0', '30 8 9', 0, 0); + make_way('465.9 -423.7 -34.0', '11 33 0', 0, 0); + make_way('543.3 -660.4 -34.0', '32 34 0', 0, 0); + make_way('537.0 -955.6 -34.0', '33 35 0', 0, 0); + make_way('165.7 -926.3 86.0', '34 36 0', 0, 0); + make_way('138.8 -740.6 155.8', '35 25 33', 0, 0); + make_way('581.8 195.7 -21.1', '38 0 0', 0, 262144); + make_way('847.0 120.2 191.0', '28 0 0', 0, 262144); + make_way('-1101.5 176.0 182.0', '19 40 0', 0, 0); + make_way('-1584.0 173.6 182.0', '39 0 0', 0, 0); + make_way('-1295.4 -37.8 148.2', '18 42 19', 0, 0); + make_way('-1362.7 -55.6 -221.2', '41 0 0', 0, 0); + make_way('769.2 196.2 -34.0', '1 44 0', 0, 0); + make_way('633.1 193.1 -34.0', '43 37 0', 0, 256); +}; +*/ diff --git a/qcsrc/gamec/bot_misc.c b/qcsrc/gamec/bot_misc.c new file mode 100644 index 000000000..31d37e2a2 --- /dev/null +++ b/qcsrc/gamec/bot_misc.c @@ -0,0 +1,777 @@ +/*********************************************** +* * +* FrikBot Misc Code * +* "Because you can't name it anything else" * +* * +***********************************************/ + +/* +This program is in the Public Domain. My crack legal +team would like to add: + +RYAN "FRIKAC" SMITH IS PROVIDING THIS SOFTWARE "AS IS" +AND MAKES NO WARRANTY, EXPRESS OR IMPLIED, AS TO THE +ACCURACY, CAPABILITY, EFFICIENCY, MERCHANTABILITY, OR +FUNCTIONING OF THIS SOFTWARE AND/OR DOCUMENTATION. IN +NO EVENT WILL RYAN "FRIKAC" SMITH BE LIABLE FOR ANY +GENERAL, CONSEQUENTIAL, INDIRECT, INCIDENTAL, +EXEMPLARY, OR SPECIAL DAMAGES, EVEN IF RYAN "FRIKAC" +SMITH HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES, IRRESPECTIVE OF THE CAUSE OF SUCH DAMAGES. + +You accept this software on the condition that you +indemnify and hold harmless Ryan "FrikaC" Smith from +any and all liability or damages to third parties, +including attorney fees, court costs, and other +related costs and expenses, arising out of your use +of this software irrespective of the cause of said +liability. + +The export from the United States or the subsequent +reexport of this software is subject to compliance +with United States export control and munitions +control restrictions. You agree that in the event you +seek to export this software, you assume full +responsibility for obtaining all necessary export +licenses and approvals and for assuring compliance +with applicable reexport restrictions. + +Any reproduction of this software must contain +this notice in its entirety. + +*/ + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +BotName + +Sets bot's name and colors + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ +string(float r) BotName = +{ + self.b_num = r; + if (r == 1) + { + self.b_pants = 11; + self.b_shirt = 0; + return "Vincent"; + } + else if (r == 2) + { + self.b_pants = 1; + self.b_shirt = 3; + return "Bishop"; + } + else if (r == 3) + { + self.b_pants = 13; + self.b_shirt = 2; + return "Nomad"; + } + else if (r == 4) + { + self.b_pants = 7; + self.b_shirt = 6; + return "Hudson"; + } + else if (r == 5) + { + self.b_pants = 12; + self.b_shirt = 6; + return "Lore"; + } + else if (r == 6) + { + self.b_pants = 4; + self.b_shirt = 4; + return "Servo"; + } + else if (r == 7) + { + self.b_pants = 2; + self.b_shirt = 5; + return "Gort"; + } + else if (r == 8) + { + self.b_pants = 10; + self.b_shirt = 3; + return "Kryten"; + } + else if (r == 9) + { + self.b_pants = 9; + self.b_shirt = 4; + return "Pimp Bot"; + } + else if (r == 10) + { + self.b_pants = 4; + self.b_shirt = 7; + return "Max"; + } + else if (r == 11) + { + self.b_pants = 3; + self.b_shirt = 11; + return "Marvin"; + } + else if (r == 12) + { + self.b_pants = 13; + self.b_shirt = 12; + return "Erwin"; + } + else if (r == 13) + { + self.b_pants = 11; + self.b_shirt = 2; + return "FrikBot"; + } + else if (r == 14) + { + self.b_pants = 0; + self.b_shirt = 2; + return "Krosis"; + } + else if (r == 15) + { + self.b_pants = 8; + self.b_shirt = 9; + return "Gypsy"; + } + else if (r == 16) + { + self.b_pants = 5; + self.b_shirt = 10; + return "Hal"; + } +}; +string () PickARandomName = +{ + if (bot_count > 16) + return "player"; + + local float y, test; + local string h; + local entity t; + y = TRUE; + while(y) + { + test = ceil(random() * 16); + h = BotName(test); + t = find(world, netname, h); + if (t == world) + y = FALSE; + } + return h; +}; + + + +// I didn't like the old code so this is very stripped down + +entity b_originator; +float b_topic; +/* FBX Topics + +b_originator == self + 1 - sign on + 2 - killed targ + 3 - team message "friendly eyes" + 4 - team message "on your back" + 5 - team message "need back up" + 6 - excuses + ---- + 7 - gameover + ---- + 8 - welcoming someone onto server + 9 - ridicule lost frag (killed self?) + 10 - ridicule lost frag (lava) + 11 - lag +b_originator == targ + + +*/ +void(float tpic) bot_start_topic = +{ + if (random() < 0.2) + { + b_topic = tpic; + b_originator = self; + } + else + b_topic = 0; +}; + +void() bot_chat = +{ + local float r; + if (b_options & OPT_NOCHAT) + return; + r = ceil (random() * 6); + + if (self.b_chattime > time) + { + if (self.b_skill < 2) + self.keys = self.button0 = self.button2 = 0; + return; + } + else if (self.b_chattime) + { + if (b_topic == 1) + { + if (b_originator == self) + { + if (r == 1) + { + BotSay(": lo all\n"); + bot_start_topic(8); + } + else if (r == 2) + { + BotSay(": hey everyone\n"); + bot_start_topic(8); + } + else if (r == 3) + { + BotSay(": prepare to be fragged!\n"); + bot_start_topic(0); + } + else if (r == 4) + { + BotSay(": boy this is laggy\n"); + bot_start_topic(11); + } + else if (r == 5) + { + BotSay(": #mm getting some lag here\n"); + bot_start_topic(11); + } + else + { + BotSay(": hi everyone\n"); + bot_start_topic(8); + } + } + } + else if (b_topic == 2) + { + if (b_originator == self) + { + if (r == 1) + BotSay(": take that\n"); + else if (r == 2) + BotSay(": yehaww!\n"); + else if (r == 3) + BotSay(": wh00p\n"); + else if (r == 4) + BotSay(": j00_sawk();\n"); + else if (r == 5) + BotSay(": i rule\n"); + else + BotSay(": eat that\n"); + bot_start_topic(0); + } + } + else if (b_topic == 3) + { + if (b_originator == self) + { + if (r < 3) + BotSayTeam(": friendly eyes\n"); + else + BotSayTeam(": team eyes\n"); + bot_start_topic(0); + } + } + else if (b_topic == 4) + { + if (b_originator == self) + { + if (r < 3) + BotSayTeam(": on your back\n"); + else + BotSayTeam(": I'm with you\n"); + bot_start_topic(0); + } + } + else if (b_topic == 5) + { + if (b_originator == self) + { + if (r < 3) + BotSayTeam(": I need help\n"); + else + BotSayTeam(": need backup\n"); + bot_start_topic(0); + } + } + else if (b_topic == 6) + { + if (b_originator == self) + { + if (r == 1) + { + BotSay(": sun got in my eyes\n"); + bot_start_topic(0); + } + else if (r == 2) + { + BotSay(": mouse needs cleaning\n"); + bot_start_topic(0); + } + else if (r == 3) + { + BotSay(": i meant to do that\n"); + bot_start_topic(0); + } + else if (r == 4) + { + BotSay(": lag\n"); + bot_start_topic(11); + } + else if (r == 5) + { + BotSay(": killer lag\n"); + bot_start_topic(11); + } + else + { + BotSay(": 100% lag\n"); + bot_start_topic(11); + } + } + } + else if (b_topic == 7) + { + if (r == 1) + BotSay(": gg\n"); + else if (r == 2) + BotSay(": gg all\n"); + else if (r == 3) + BotSay(": that was fun\n"); + else if (r == 4) + BotSay(": good game\n"); + else if (r == 5) + BotSay(": pah\n"); + else + BotSay(": hrm\n"); + bot_start_topic(0); + } + else if (b_topic == 8) + { + if (b_originator != self) + { + if (r == 1) + { + BotSay(": heya\n"); + bot_start_topic(0); + } + else if (r == 2) + { + BotSay(": welcome\n"); + bot_start_topic(0); + } + else if (r == 3) + { + BotSayInit(); + BotSay2(": hi "); + BotSay2(b_originator.netname); + BotSay2("\n"); + bot_start_topic(0); + } + else if (r == 4) + { + BotSayInit(); + BotSay2(": hey "); + BotSay2(b_originator.netname); + BotSay2("\n"); + bot_start_topic(0); + } + else if (r == 5) + { + BotSay(": howdy\n"); + bot_start_topic(0); + } + else + { + BotSay(": lo\n"); + bot_start_topic(0); + } + } + } + + else if (b_topic == 9) + { + if (b_originator != self) + { + if (r == 1) + BotSay(": hah\n"); + else if (r == 2) + BotSay(": heheh\n"); + else if (r == 3) + { + BotSayInit(); + BotSay2(": good work "); + BotSay2(b_originator.netname); + BotSay2("\n"); + } + else if (r == 4) + { + BotSayInit(); + BotSay2(": nice1 "); + BotSay2(b_originator.netname); + BotSay2("\n"); + } + else if (r == 5) + BotSay(": lol\n"); + else + BotSay(": :)\n"); + b_topic = 6; + } + } + else if (b_topic == 10) + { + if (b_originator != self) + { + if (r == 1) + BotSay(": have a nice dip?\n"); + else if (r == 2) + BotSay(": bah I hate levels with lava\n"); + else if (r == 3) + { + BotSayInit(); + BotSay2(": good job "); + BotSay2(b_originator.netname); + BotSay2("\n"); + } + else if (r == 4) + { + BotSayInit(); + BotSay2(": nice backflip "); + BotSay2(b_originator.netname); + BotSay2("\n"); + } + else if (r == 5) + BotSay(": watch your step\n"); + else + BotSay(": hehe\n"); + b_topic = 6; + } + } + + else if (b_topic == 11) + { + if (b_originator != self) + { + if (r == 1) + { + BotSayInit(); + BotSay2(": yeah right "); + BotSay2(b_originator.netname); + BotSay2("\n"); + bot_start_topic(0); + } + else if (r == 2) + { + BotSay(": ping\n"); + bot_start_topic(0); + } + else if (r == 3) + { + BotSay(": shuddup, you're an lpb\n"); + bot_start_topic(0); + } + else if (r == 4) + { + BotSay(": lag my eye\n"); + bot_start_topic(0); + } + else if (r == 5) + { + BotSay(": yeah\n"); + bot_start_topic(11); + } + else + { + BotSay(": totally\n"); + bot_start_topic(11); + } + } + } + self.b_chattime = 0; + } + else if (b_topic) + { + if (random() < 0.5) + { + if (self == b_originator) + { + if (b_topic <= 7) + self.b_chattime = time + 2; + } + else + { + if (b_topic >= 7) + self.b_chattime = time + 2; + } + } + } +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +Kick A Bot. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void() KickABot = +{ + local entity ty; + ty = find(world, classname, "player"); + while (ty != world) + { + if (!(ty.ishuman)) + { + + BotDisconnect(ty); + ty.ishuman = TRUE; + ty = world; + } + else + ty = find(ty, classname, "player"); + } + +}; + + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +Simplified origin checking. + +God, I wish I had inline + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +vector(entity ent) realorigin = +{ +// even more simplified... + return (ent.absmin + ent.absmax) * 0.5; +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +fisible + +a version of visible that checks for corners +of the bounding boxes + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +float (entity targ) fisible = +{ + local vector spot1, org; + local float thruwater, pc1, pc2; + + org = realorigin(targ); + spot1 = self.origin + self.view_ofs; + + if (targ.solid == SOLID_BSP) + { + traceline (spot1, org, TRUE, self); + if (trace_ent == targ) + return TRUE; + else if (trace_fraction == 1) + return TRUE; + return FALSE; + } + else + { + pc1 = pointcontents(org); + pc2 = pointcontents(spot1); + if (targ.classname == "player") + thruwater = FALSE; + else if (pc1 == CONTENT_LAVA) + return FALSE; + else + thruwater = TRUE; + } + + if (pc1 < -1) // targ's origin is in water or other liquid + { + if (pc2 != pc1) + { + // look for their head + traceline (spot1, org + targ.mins, TRUE, self); + // cross the water check + if (trace_inopen) + if (trace_inwater) + if (!thruwater) + return FALSE; + if (trace_ent == targ) + return TRUE; + else if (trace_fraction == 1) + return TRUE; + return FALSE; + } + } + else + { + if (pc2 != pc1) + { + traceline (spot1, org + targ.maxs, TRUE, self); + if (trace_inopen) + if (trace_inwater) + if (!thruwater) + return FALSE; + if (trace_ent == targ) + return TRUE; + else if (trace_fraction == 1) + return TRUE; + return FALSE; + } + } + traceline (spot1, org, TRUE, self); + if (trace_ent == targ) + return TRUE; + else if (trace_fraction == 1) + return TRUE; + traceline (spot1, org + targ.maxs, TRUE, self); + if (trace_ent == targ) + return TRUE; + else if (trace_fraction == 1) + return TRUE; + traceline (spot1, org + targ.mins, TRUE, self); + if (trace_ent == targ) + return TRUE; + else if (trace_fraction == 1) + return TRUE; + return FALSE; +}; + + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +Wisible + +goes through movable brushes/entities, used +for waypoints + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +// this is used for waypoint stuff.... +float (entity targ1, entity targ2) wisible = +{ + local vector spot1, spot2; + local entity ignore; + + spot1 = targ1.origin; + spot2 = realorigin(targ2); + + ignore = self; + do + { + traceline (spot1, spot2, TRUE, ignore); + spot1 = realorigin(trace_ent); + ignore = trace_ent; + } while ((trace_ent != world) && (trace_fraction != 1)); + if (trace_endpos == spot2) + return TRUE; + else + return FALSE; +}; + + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +sisible + +Now this is getting ridiculous. Simple visible, +used when we need just a simple traceline nothing else + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +float (entity targ) sisible = +{ + traceline (self.origin, targ.origin, TRUE, self); + if (trace_ent == targ) + return TRUE; + else if (trace_fraction == 1) + return TRUE; +}; +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +angcomp + +subtracts one angle from another + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +float (float y1, float y2) angcomp = +{ + y1 = frik_anglemod(y1); + y2 = frik_anglemod(y2); + + local float answer; + answer = y1 - y2; + if (answer > 180) + answer = (360 - answer) * -1; + else if (answer < -180) + answer = answer + 360; + return answer; +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +fov + +is the entity in the bot's field of view + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ +float (entity targ) fov = +{ + local vector yawn; + local float g; + yawn = realorigin(targ); + yawn = (yawn + targ.view_ofs) - (self.origin + self.view_ofs); + yawn = normalize(yawn); + yawn = vectoangles(yawn); + g = angcomp(self.v_angle_x, yawn_x); + if (fabs(g) > 45) + return FALSE; + g = angcomp(self.v_angle_y, yawn_y); + if (fabs(g) > 60) + return FALSE; + + return TRUE; +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +frik_anglemod + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ +float(float v) frik_anglemod = +{ + return v - floor(v/360) * 360; +}; \ No newline at end of file diff --git a/qcsrc/gamec/bot_move.c b/qcsrc/gamec/bot_move.c new file mode 100644 index 000000000..1ad345ed6 --- /dev/null +++ b/qcsrc/gamec/bot_move.c @@ -0,0 +1,512 @@ +/*********************************************** +* * +* FrikBot Movement AI * +* "The slightly better movement AI" * +* * +***********************************************/ + +/* + +This program is in the Public Domain. My crack legal +team would like to add: + +RYAN "FRIKAC" SMITH IS PROVIDING THIS SOFTWARE "AS IS" +AND MAKES NO WARRANTY, EXPRESS OR IMPLIED, AS TO THE +ACCURACY, CAPABILITY, EFFICIENCY, MERCHANTABILITY, OR +FUNCTIONING OF THIS SOFTWARE AND/OR DOCUMENTATION. IN +NO EVENT WILL RYAN "FRIKAC" SMITH BE LIABLE FOR ANY +GENERAL, CONSEQUENTIAL, INDIRECT, INCIDENTAL, +EXEMPLARY, OR SPECIAL DAMAGES, EVEN IF RYAN "FRIKAC" +SMITH HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES, IRRESPECTIVE OF THE CAUSE OF SUCH DAMAGES. + +You accept this software on the condition that you +indemnify and hold harmless Ryan "FrikaC" Smith from +any and all liability or damages to third parties, +including attorney fees, court costs, and other +related costs and expenses, arising out of your use +of this software irrespective of the cause of said +liability. + +The export from the United States or the subsequent +reexport of this software is subject to compliance +with United States export control and munitions +control restrictions. You agree that in the event you +seek to export this software, you assume full +responsibility for obtaining all necessary export +licenses and approvals and for assuring compliance +with applicable reexport restrictions. + +Any reproduction of this software must contain +this notice in its entirety. + +*/ + +void() bot_jump = +{ + // TODO check for precision, etc. + self.button2 = TRUE; +}; + +float(entity e) bot_can_rj = +{ + // this returns true of the bot can rocket/superjump/hook + // if your mod doesn't have an RL you can just return FALSE all the time + // if it has a hook or some other means for the bot to get to high places + // you can check here for that capability + + // am I dumb? + if (e.b_skill == 0) + return FALSE; + + // quad = bad + if (e.items & 4194304) + return FALSE; + + // do I have rockets & RL? + if (!((e.items & 32) && (e.ammo_rockets > 0))) + return FALSE; + + // do I have pent? + if (e.items & 1048576) + return TRUE; + + if (e.health > 50) + return TRUE; + else + return FALSE; +}; + +float(float flag) frik_recognize_plat = +{ + if ((self.classname != "waypoint") && !(self.flags & FL_ONGROUND)) + return FALSE; + traceline(self.origin, self.origin - '0 0 64', TRUE, self); + if (trace_ent != world) + { + if (flag) // afect bot movement too + { + if (self.keys & KEY_MOVEUP) + { + if (trace_ent.velocity_z > 0) + self.keys = self.keys & 960; // 960 is all view keys + } + else if (self.keys & KEY_MOVEDOWN) + { + if (trace_ent.velocity_z < 0) + self.keys = self.keys & 960; + } + } + return TRUE; + } + else + return FALSE; +}; + +float(vector sdir) frik_KeysForDir = +{ + + local vector keydir; + local float outkeys, tang; + outkeys = 0; + if (sdir_x || sdir_y) + { + // Everything is tested against 60 degrees, + // this allows the bot to overlap the keys + // 30 degrees on each diagonal 45 degrees + // might look more realistic + + keydir = vectoangles(sdir); + tang = angcomp(keydir_y, self.v_angle_y); + if ((tang <= 150) && (tang >= 30)) + outkeys = outkeys + KEY_MOVELEFT; + else if ((tang >= -150) && (tang <= -30)) + outkeys = outkeys + KEY_MOVERIGHT; + if (fabs(tang) <= 60) + outkeys = outkeys + KEY_MOVEFORWARD; + else if (fabs(tang) >= 120) + outkeys = outkeys + KEY_MOVEBACK; + } + if (sdir_z > 0.7) + outkeys = outkeys + KEY_MOVEUP; + else if (sdir_z < 0.7) + outkeys = outkeys + KEY_MOVEDOWN; + return outkeys; + +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +frik_obstructed + +Bot has hit a ledge or wall that he should +manuever around. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void(vector whichway, float danger) frik_obstructed = +{ + local float dist; + local vector disway, org; +// TODO: something + if (self.b_aiflags & AI_BLIND) + return; + org = realorigin(self.target1); + + if (danger) + { + self.b_aiflags = self.b_aiflags | AI_DANGER; + self.keys = frik_KeysForDir('0 0 0' - whichway); + } + if (self.b_aiflags & AI_PRECISION) + return; + + + if (self.target1) + { + if (self.b_aiflags & AI_OBSTRUCTED) + { + if (!(self.b_aiflags & AI_DANGER)) + { + self.b_aiflags = self.b_aiflags - AI_OBSTRUCTED; + return; + } + else if (!danger) + return; + } + self.obs_dir = whichway; + disway_x = whichway_y * -1; + disway_y = whichway_x; + dist = vlen(org - (self.origin + disway)); + disway_x = whichway_y; + disway_y = whichway_x * -1; + self.wallhug = vlen(org - (self.origin + disway)) > dist; + self.b_aiflags = self.b_aiflags | AI_OBSTRUCTED; + + } + else + { + disway_x = whichway_y * -1; + disway_y = whichway_x; + dist = vlen(disway - self.obs_dir); + disway_x = whichway_y; + disway_y = whichway_x * -1; + self.wallhug = vlen(disway - self.obs_dir) < dist; + self.obs_dir = whichway; + + self.b_aiflags = self.b_aiflags | AI_OBSTRUCTED; + } +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +frik_obstacles + +Detects small bumps the bot needs to jump over +or ledges the bot should avoid falling in. + +Also responsible for jumping gaps. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void() frik_obstacles = +{ + local vector start, stop, ang; + local float test, conts, dist, hgt; + + if (!(self.flags & FL_ONGROUND)) + return; + if (self.b_aiflags & AI_BLIND) + return; + + ang = normalize(self.velocity); + ang_z = 0; + start = self.origin + ang * 32; // ahem + start_z = self.origin_z + self.maxs_z; + stop = start; + stop_z = self.origin_z + self.mins_z; + traceline(start, stop - '0 0 256', TRUE, self); + if (trace_allsolid || trace_startsolid) + return; + hgt = trace_endpos_z - stop_z; + + if (hgt > 18) + { + bot_jump(); + return; + } + if (hgt >= 0) + return; + + conts = pointcontents(trace_endpos + '0 0 4'); + start = stop - '0 0 8'; + stop = start + ang * 256; + traceline(start, stop, TRUE, self); + test = vlen(trace_endpos - start); + if (test <= 20) + return; // it's a walkable gap, do nothing + ang_x = self.velocity_y * -1; + ang_y = self.velocity_x; + ang = normalize(ang); + traceline(start - (ang * 10), start + (ang * 10), TRUE, self); + if ((trace_fraction != 1) || trace_startsolid) + return; // gap is only 20 wide, walkable + ang = self.velocity; + ang_z = 0; + dist = ((540 / sv_gravity) * vlen(ang))/* + 32*/; + if (test > dist) // I can't make it + { + if (conts < -3) // bad stuff down dare + { + frik_obstructed(ang, TRUE); + return; + } + else + { + if (self.target1) + { + stop = realorigin(self.target1); + if ((stop_z - self.origin_z) < -32) + return; // safe to fall + } + frik_obstructed(ang, FALSE); + return; + } + } + else + { + ang = normalize(ang); + //look for a ledge + traceline(self.origin, self.origin + (ang * (test + 20)), TRUE, self); + if (trace_fraction != 1) + { + if (conts < -3) // bad stuff down dare + { + frik_obstructed(ang, TRUE); + return; + } + else + { + if (self.target1) + { + stop = realorigin(self.target1); + if ((stop_z - self.origin_z) < -32) + return; // safe to fall + } + frik_obstructed(ang, FALSE); + return; + } + } + + if (self.target1) + { + // getting furter away from my target? + test = vlen(self.target1.origin - (ang + self.origin)); + if (test > vlen(self.target1.origin - self.origin)) + { + if (conts < -3) // bad stuff down dare + { + frik_obstructed(ang, TRUE); + return; + } + else + { + frik_obstructed(ang, FALSE); + return; + } + } + } + } + if (hgt < -18) + { + if (self.target1) + { + stop = realorigin(self.target1); + if ((stop_z - self.origin_z) < -32) + return; // safe to fall + } + bot_jump(); + } + // go for it + +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +After frik_obstructed, the bot uses the +following funtion to move "around" the obstacle + +I have no idea how well it will work + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void() frik_dodge_obstruction = +{ + local vector way, org; + local float oflags, yaw; + + if (!(self.b_aiflags & AI_OBSTRUCTED)) + return; + if ((self.b_aiflags & (AI_BLIND | AI_PRECISION)) || !(self.flags & FL_ONGROUND)) + { + self.b_aiflags = self.b_aiflags - AI_OBSTRUCTED; + return; + } + + // perform a walkmove check to see if the obs_dir is still obstructed + // walkmove is less forgiving than frik_obstacles, so I dunno + // how well this will work + + oflags = self.flags; + org = self.origin; + + yaw = vectoyaw(self.obs_dir); + if (walkmove(yaw, 32)) + self.b_aiflags = self.b_aiflags - AI_OBSTRUCTED; + else + { + if (self.b_aiflags & AI_DANGER) + { + way = '0 0 0' - self.obs_dir; + } + else if (self.wallhug) + { + way_x = self.obs_dir_y * -1; + way_y = self.obs_dir_x; + } + else + { + way_x = self.obs_dir_y; + way_y = self.obs_dir_x * -1; + } + self.keys = self.keys & 960 + frik_KeysForDir(way); + } + + // fix the bot + + self.origin = org; + self.flags = oflags; +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +movetogoal and walkmove replacements + +blah + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void() frik_movetogoal = +{ + local vector way, start, stop, ang; + local float g; + + if (self.target1 == world) + { + makevectors(self.v_angle); + frik_walkmove(v_forward); + return; + } + way = realorigin(self.target1) - self.origin; + if (vlen(way) < 25) + { + self.keys = self.keys & 960; + return; + } + + way = normalize(way); + self.keys = self.keys & 960 + frik_KeysForDir(way); + + frik_dodge_obstruction(); + frik_recognize_plat(TRUE); + + if (self.b_aiflags & AI_PRECISION) + { + g = angcomp(self.v_angle_x, self.b_angle_x); + if (fabs(g) > 10) + self.keys = self.keys & 960; + g = angcomp(self.v_angle_y, self.b_angle_y); + if (fabs(g) > 10) + self.keys = self.keys & 960; + } +}; + +float(vector weird) frik_walkmove = +{ + // okay so it's not walkmove + // sue me + self.keys = self.keys & 960 + frik_KeysForDir(weird); + + frik_dodge_obstruction(); + frik_recognize_plat(TRUE); + if (self.b_aiflags & AI_OBSTRUCTED) + return FALSE; + else + return TRUE; +}; + + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +The "hook" method of navigation. This nav +system is copyrighted 1999 by Ryan "Frika C" +Smith, keep that in mind when you steal it. + +I brought this back because normal roaming +won't work - the bot gets distracted by it's +own waypoints. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void() frik_bot_roam = +{ + local vector org, ang, org1; + local float loopcount, flag, dist; + + loopcount = 26; + flag = FALSE; + while((loopcount > 0) && !flag) + { + loopcount = loopcount - 1; + org = self.origin + self.view_ofs; + ang = self.angles; + ang_y = frik_anglemod(ang_y - 90 + (random() * 180)); + ang_x = 0; // avoid upward sloping + makevectors(ang); + traceline(org, org + v_forward * 2300, TRUE, self); + if (trace_fraction != 1) + { + org1 = trace_endpos; + ang = normalize(trace_plane_normal); + ang_z = 0; // avoid upward sloping + traceline(org1, org1 + (ang * 2300), TRUE, self); + if ((trace_fraction != 1) && (vlen(trace_endpos - org1) >= 64)) + { + org = trace_endpos; + traceline(org, self.origin + self.view_ofs, TRUE, self); + if (trace_fraction != 1) + { + dist = vlen(org1 - org) /2; + org = org1 + (ang * dist); + traceline(org, org - '0 0 48', TRUE, self); + if (trace_fraction != 1) + { + SpawnTempWaypoint(org); + flag = TRUE; + } + } + } + } + } + self.b_angle_y = self.v_angle_y + 10; +}; diff --git a/qcsrc/gamec/bot_phys.c b/qcsrc/gamec/bot_phys.c new file mode 100644 index 000000000..597c485af --- /dev/null +++ b/qcsrc/gamec/bot_phys.c @@ -0,0 +1,678 @@ +/*********************************************** +* * +* FrikBot Physics * +* The near-perfect emulation of * +* Client movement * +* * +* Special Thanks to: Asdf, Frog * +* Alan "Strider" Kivlin * +* * +* * +***********************************************/ + +/* + +This program is in the Public Domain. My crack legal +team would like to add: + +RYAN "FRIKAC" SMITH IS PROVIDING THIS SOFTWARE "AS IS" +AND MAKES NO WARRANTY, EXPRESS OR IMPLIED, AS TO THE +ACCURACY, CAPABILITY, EFFICIENCY, MERCHANTABILITY, OR +FUNCTIONING OF THIS SOFTWARE AND/OR DOCUMENTATION. IN +NO EVENT WILL RYAN "FRIKAC" SMITH BE LIABLE FOR ANY +GENERAL, CONSEQUENTIAL, INDIRECT, INCIDENTAL, +EXEMPLARY, OR SPECIAL DAMAGES, EVEN IF RYAN "FRIKAC" +SMITH HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES, IRRESPECTIVE OF THE CAUSE OF SUCH DAMAGES. + +You accept this software on the condition that you +indemnify and hold harmless Ryan "FrikaC" Smith from +any and all liability or damages to third parties, +including attorney fees, court costs, and other +related costs and expenses, arising out of your use +of this software irrespective of the cause of said +liability. + +The export from the United States or the subsequent +reexport of this software is subject to compliance +with United States export control and munitions +control restrictions. You agree that in the event you +seek to export this software, you assume full +responsibility for obtaining all necessary export +licenses and approvals and for assuring compliance +with applicable reexport restrictions. + +Any reproduction of this software must contain +this notice in its entirety. + +*/ + +/* +========================================= + +Stuff mimicking cl_input.c code + +========================================= +*/ +float(float key) CL_KeyState = +{ + return ((self.keys & key) > 0); +}; + +void() CL_KeyMove = // CL_BaseMove + CL_AdjustAngles +{ + local float anglespeed; + local vector view; + if (self.keys != self.oldkeys) + { + self.movement = '0 0 0'; + self.movement_y = self.movement_y + (350 * CL_KeyState(KEY_MOVERIGHT)); + // 350 is the default cl_sidespeed + self.movement_y = self.movement_y - (350 * CL_KeyState(KEY_MOVELEFT)); + // 350 is the default cl_sidespeed + self.movement_x = self.movement_x + (200 * CL_KeyState(KEY_MOVEFORWARD)); + // 200 is the default cl_forwardspeed + self.movement_x = self.movement_x - (200 * CL_KeyState(KEY_MOVEBACK)); + // 200 is the default cl_backspeed + self.movement_z = self.movement_z + (200 * CL_KeyState(KEY_MOVEUP)); + // 200 is the default cl_upspeed + self.movement_z = self.movement_z - (200 * CL_KeyState(KEY_MOVEDOWN)); + // 200 is the default cl_upspeed + if (!self.b_aiflags & AI_PRECISION) + self.movement = self.movement * 2; + // 2 is the default cl_movespeedkey & bot always has +speed + } + self.oldkeys = self.keys; + + if (self.b_skill != 2) // use mouse emulation + { + anglespeed = 1.5 * real_frametime; + // 1.5 is the default cl_anglespeedkey & bot always has +speed + self.v_angle_y = self.v_angle_y + anglespeed * CL_KeyState(KEY_LOOKLEFT) * 140; + // 140 is default cl_yawspeed + self.v_angle_y = self.v_angle_y - anglespeed * CL_KeyState(KEY_LOOKRIGHT) * 140; + // 140 is default cl_yawspeed + self.v_angle_x = self.v_angle_x - anglespeed * CL_KeyState(KEY_LOOKUP) * 150; + // 150 is default cl_pitchspeed + self.v_angle_x = self.v_angle_x + anglespeed * CL_KeyState(KEY_LOOKDOWN) * 150; + // 150 is default cl_pitchspeed + } + else + { + view_x = angcomp(self.b_angle_x, self.v_angle_x); + view_y = angcomp(self.b_angle_y, self.v_angle_y); + if (vlen(view) > 30) + { + self.mouse_emu = self.mouse_emu + (view * 30); + if (vlen(self.mouse_emu) > 180) + self.mouse_emu = normalize(self.mouse_emu) * 180; + } + else + self.mouse_emu = view * (1 / real_frametime); + self.v_angle = self.v_angle + self.mouse_emu * real_frametime; + + + } + if (self.v_angle_x > 80) + self.v_angle_x = 80; + else if (self.v_angle_x < -70) + self.v_angle_x = -70; + + if (self.v_angle_z > 50) + self.v_angle_z = 50; + else if (self.v_angle_z < -50) + self.v_angle_z = -50; + self.v_angle_y = frik_anglemod(self.v_angle_y); + +}; + +/* +========================================= + +Stuff mimicking sv_user.c + +========================================= +*/ +void() SV_UserFriction = +{ + local vector vel, start, stop; + local float sped, friction, newspeed; + + vel = self.velocity; + vel_z =0; + sped = vlen(vel); + vel = self.velocity; + + if (!sped) + return; + +// if the leading edge is over a dropoff, increase friction + + start_x = stop_x = self.origin_x + vel_x / (sped * 16); + start_y = stop_y = self.origin_y + vel_y / (sped * 16); + start_z = self.origin_z + self.mins_z; + stop_z = start_z - 34; + + traceline(start, stop, TRUE, self); + + if (trace_fraction == 1) + friction = sv_friction * 2; // 2 is default edgefriction, removed for QW compatability + else + friction = sv_friction; + if (sped < sv_stopspeed) + newspeed = sped - real_frametime * sv_stopspeed * friction; + else + newspeed = sped - real_frametime * sped * friction; + + if (newspeed < 0) + newspeed = 0; + newspeed = newspeed / sped; + + self.velocity_y = vel_y * newspeed; + self.velocity_x = vel_x * newspeed; +}; +void() SV_WaterJump = +{ + if (time > self.teleport_time || !self.waterlevel) + { + self.flags = self.flags - (self.flags & FL_WATERJUMP); + self.teleport_time = 0; + } + self.velocity_x = self.movedir_x; + self.velocity_y = self.movedir_y; +}; + +void() DropPunchAngle = +{ + local float len; + len = vlen(self.punchangle); + self.punchangle = normalize(self.punchangle); + len = len - 10 * real_frametime; + if (len < 0) + len = 0; + self.punchangle = self.punchangle * len; +}; + + +void(vector wishvel) SV_AirAccelerate = +{ + local float addspeed, wishspd, accelspeed, currentspeed; + + wishspd = vlen(wishvel); + wishvel = normalize(wishvel); + if (wishspd > 30) + wishspd = 30; + currentspeed = self.velocity * wishvel; + addspeed = wishspd - currentspeed; + if (addspeed <= 0) + return; + accelspeed = 10 * sv_accelerate * wishspd * real_frametime; + if (accelspeed > addspeed) + accelspeed = addspeed; + + self.velocity = self.velocity + accelspeed * wishvel; +}; + +void(vector wishvel) SV_Accelerate = +{ + local float addspeed, wishspd, accelspeed, currentspeed; + + wishspd = vlen(wishvel); + wishvel = normalize(wishvel); + + currentspeed = self.velocity * wishvel; + addspeed = wishspd - currentspeed; + if (addspeed <= 0) + return; + accelspeed = sv_accelerate * wishspd * real_frametime; + if (accelspeed > addspeed) + accelspeed = addspeed; + + self.velocity = self.velocity + accelspeed * wishvel; +}; +void() SV_WaterMove = +{ + local vector wishvel; + local float wishspeed, addspeed, cspeed, newspeed; + makevectors(self.v_angle); + wishvel = v_right * self.movement_y + v_forward * self.movement_x; + + if (self.movement == '0 0 0') + wishvel_z = wishvel_z - 60; + else + wishvel_z = wishvel_z + self.movement_z; + wishspeed = vlen(wishvel); + + if (wishspeed > sv_maxspeed) + { + wishvel = (sv_maxspeed / wishspeed) * wishvel; + wishspeed = sv_maxspeed; + } + wishspeed = wishspeed * 0.7; + cspeed = vlen(self.velocity); + if (cspeed) + { + newspeed = cspeed - (real_frametime * cspeed * sv_friction); + if (newspeed < 0) + newspeed = 0; + self.velocity = self.velocity * (newspeed / cspeed); + + } + else + newspeed = 0; + + if (!wishspeed) + return; + addspeed = wishspeed - newspeed; + if (addspeed <= 0) + return; + wishvel = normalize(wishvel); + cspeed = sv_accelerate * wishspeed * real_frametime; + if (cspeed > addspeed) + cspeed = addspeed; + self.velocity = self.velocity + cspeed * wishvel; +}; +void() SV_AirMove = +{ + local vector wishvel, vangle; + + vangle = self.v_angle; + vangle_x = vangle_z = 0; + makevectors(vangle); + if (time < self.teleport_time && (self.movement_x < 0)) + self.movement_x = 0; + wishvel = v_right * self.movement_y + v_forward * self.movement_x; + + + if (self.movetype != MOVETYPE_WALK) + wishvel_z = self.movement_z; + else + wishvel_z = 0; + if (vlen(wishvel) > sv_maxspeed) + wishvel = normalize(wishvel) * sv_maxspeed; + if (self.movetype == MOVETYPE_NOCLIP) + self.velocity = wishvel; + else if (self.flags & FL_ONGROUND) + { + SV_UserFriction(); + SV_Accelerate(wishvel); + } + else + SV_AirAccelerate (wishvel); +}; + +void() SV_ClientThink = +{ + local vector vangle; + + if (self.movetype == MOVETYPE_NONE) + return; + DropPunchAngle(); + if (self.health <= 0) + return; + self.v_angle_z = 0; // V_CalcRoll removed, sucks + self.angles_z = self.v_angle_z * 4; + vangle = self.v_angle + self.punchangle; + if (!self.fixangle) + { + self.angles_x = (vangle_x / -3); + self.angles_y = vangle_y; + } else + { + self.v_angle = self.angles; + self.fixangle = 0; + } + if (self.flags & FL_WATERJUMP) + { + SV_WaterJump(); + return; + } + if ((self.waterlevel >= 2) && (self.movetype != MOVETYPE_NOCLIP)) + { + SV_WaterMove(); + return; + } + SV_AirMove(); + +}; +/* +========================================= + +Stuff mimicking sv_phys.c + +========================================= +*/ + +float() SV_RunThink = +{ + local float thinktime, bkuptime; + thinktime = self.nextthink; + bkuptime = time; + if (thinktime <= 0 || thinktime > (time + real_frametime)) + return TRUE; + if (thinktime < time) + thinktime = time; + self.nextthink = 0; + time = thinktime; + other = world; + makevectors(self.v_angle); // hack + self.think(); + time = bkuptime; + return TRUE; +}; + +void(float scale) SV_AddGravity = +{ + self.velocity_z = self.velocity_z - (scale * sv_gravity * real_frametime); +}; + +float() SV_CheckWater = +{ + local vector point; + local float cont; + + point_x = self.origin_x; + point_y = self.origin_y; + self.waterlevel = 0; + self.watertype = CONTENT_EMPTY; + point_z = self.origin_z + self.mins_z + 1; + cont = pointcontents(point); + if (cont <= CONTENT_WATER) + { + self.watertype = cont; + self.waterlevel = 1; + point_z = self.origin_z + (self.mins_z + self.maxs_z) * 0.5; + cont = pointcontents(point); + if (cont <= CONTENT_WATER) + { + self.waterlevel = 2; + point_z = self.origin_z + self.view_ofs_z; + cont = pointcontents(point); + if (cont <= CONTENT_WATER) + self.waterlevel = 3; + } + } + return (self.waterlevel > 1); + +}; +void() RemoveThud = // well sometimes +{ + local entity oself; + if (other == world) + { + if (self.flags & FL_ONGROUND) + { + self.flags = self.flags - FL_ONGROUND; + } + } + else + { + if (other.solid == SOLID_BSP && (self.flags & FL_ONGROUND)) + { + // RM: Does this break anything? + // If not, then some more thuds have been removed. + self.flags = self.flags - FL_ONGROUND; + } + if (other == self.owner) + return; + if (self.owner.solid == SOLID_NOT) + return; + oself = other; + other = self.owner; + self = oself; + if (self.solid == SOLID_BSP) + if (self.touch) + self.touch(); + } + +}; +void() SV_CheckOnGround = +{ + local vector org, v; + org = self.origin; + local float currentflags; + currentflags = self.flags; + self.flags = self.flags | FL_ONGROUND | FL_PARTIALGROUND; + walkmove(0,0); // perform C touch function + self.flags = currentflags | FL_ONGROUND; + if ((org_x != self.origin_x) || (org_y != self.origin_y)) + org = self.origin; + else + self.origin = org; + v = org; + v_z = self.maxs_z + org_z + 1; + traceline (org, v, TRUE, self); + if ((self.waterlevel == 3) && (self.movetype == MOVETYPE_WALK)) + self.flags = self.flags - FL_ONGROUND; + else if ((trace_plane_normal_z <= 0.7) && (trace_fraction != 1)) + self.flags = self.flags - FL_ONGROUND; + else if (!droptofloor(0,0)) + self.flags = self.flags - FL_ONGROUND; + else if (org_z - self.origin_z < 2) + self.flags = self.flags | FL_ONGROUND; + else + self.flags = self.flags - FL_ONGROUND; + setorigin(self, org); +}; +// Thanks to Alan Kivlin for this function +// modified heavily by me +float(vector dir) botCheckForStep = +{ + local vector currentorigin, v; + local float currentflags, yaw, stepdistance, movedistance; + currentorigin = self.origin; + currentflags = self.flags; + self.flags = FL_ONGROUND | FL_PARTIALGROUND; + dir = normalize(dir); + dir_z = 0; + yaw = vectoyaw(dir); + if(walkmove(yaw, 3)) + { + if(droptofloor(0,0)) + { + stepdistance = self.origin_z - currentorigin_z; + v = self.origin - currentorigin; + v_z = 0; + movedistance = vlen(v); + if((stepdistance > 0 && stepdistance <= 16) && movedistance != 0) + { + self.flags = currentflags | FL_PARTIALGROUND; + return 1; + } + } + } + self.flags = currentflags; + setorigin(self, currentorigin); + return 0; +}; +// this is merely here to fix a problem with e3m5 +void(vector dir) BruteForceStep = +{ + local vector currentorigin; + local float currentflags, i, len; + + currentorigin = self.origin; + currentflags = self.flags; + len = vlen(dir); + if (len > 16) + dir = normalize(dir) * 16; + + setorigin(self, currentorigin + dir); + + while(i < 18 && !walkmove(0, 0)) + { + self.origin_z = currentorigin_z + i; + i = i + 2; + } + self.flags = currentflags; + if (i >=18) + setorigin(self, currentorigin); +}; + +void() PostPhysics = +{ + local vector obstr, org; + local float back, dst,cflags; + + self = self.owner; + + self.velocity = self.velocity - self.phys_obj.dest1 + self.phys_obj.velocity; + if (self.phys_obj.dest2 == self.origin) + { + setorigin(self, self.phys_obj.origin); + // might've been moved during other person's physics + // (teleporters / plats) + + if (self.movetype == MOVETYPE_WALK) + { + + if (self.phys_obj.dest1_x || self.phys_obj.dest1_y) + { + if ((self.flags & FL_ONGROUND) || (self.waterlevel <= 2)) + { + obstr = self.phys_obj.movedir - self.origin; + obstr_z = 0; + if (vlen(obstr) > 0.1) + { + dst = vlen(obstr); + back = vectoyaw(obstr); + cflags = self.flags; + self.flags = self.flags | FL_PARTIALGROUND; + if(walkmove(back, dst)) + { + self.flags = cflags; + self.phys_obj.dest1_z = 0; + self.velocity = self.velocity + self.phys_obj.dest1 - self.phys_obj.velocity; + } + else + { + if (dst > 1) + frik_obstructed(obstr, FALSE); + + org = self.origin; + self.flags = cflags; + obstr = self.phys_obj.dest1; + obstr_x = 0; + if (!botCheckForStep(obstr)) + { + obstr = self.phys_obj.dest1; + obstr_y = 0; + if (!botCheckForStep(obstr)) + { + // if no steps were found, bot is really obstucted + BruteForceStep(self.phys_obj.dest1); + } + } + } + } + } + } + } + } + + SV_CheckOnGround(); + + PlayerPostThink(); + BotAI(); + self.dmg_take = self.dmg_save = 0; + +}; +// Avoid calling BotAI and the physics at the same time +// Can trip the runaway loop counter + +void() SV_FlyMove = +{ + // This is nothing like the Quake function. + + if (self.phys_obj == world) + { + self.phys_obj = find(world,classname,"phys_obj"); + while (self.phys_obj.owner != self) + { + self.phys_obj = find(self.phys_obj,classname,"phys_obj"); + if (self.phys_obj == world) + { + error("No physics entity spawned!\nMake sure BotInit was called\n"); + } + } + } + + setmodel (self.phys_obj, string_null); + self.phys_obj.movetype = MOVETYPE_STEP; + + self.phys_obj.solid = SOLID_TRIGGER; + self.phys_obj.touch = RemoveThud; + setsize(self.phys_obj, self.mins, self.maxs); + self.phys_obj.dest2 = self.phys_obj.origin = self.origin; + self.phys_obj.watertype = 0; + self.phys_obj.movedir = self.origin + real_frametime * self.velocity; + self.phys_obj.dest1 = self.phys_obj.velocity = self.velocity; + self.phys_obj.velocity_z = self.phys_obj.velocity_z + sv_gravity * real_frametime; + self.phys_obj.flags = 0; + self.phys_obj.think = PostPhysics; + self.phys_obj.nextthink = time; +}; + + +void() SV_Physics_Toss = +{ + if (!SV_RunThink()) + return; + if (self.flags & FL_ONGROUND) + { + self.velocity = '0 0 0'; + BotAI(); + return; + } + if (self.movetype != MOVETYPE_FLY) + SV_AddGravity(1); + self.angles = self.angles + real_frametime * self.avelocity; + SV_FlyMove(); + +}; +void() SV_Physics_Client = +{ + + PlayerPreThink(); + + if (self.movetype == MOVETYPE_NONE) + { + if (!SV_RunThink()) + return; + PlayerPostThink(); + BotAI(); + + } + else if ((self.movetype == MOVETYPE_WALK) || (self.movetype == MOVETYPE_STEP)) + { + if (!SV_RunThink()) + return; + if (!(SV_CheckWater()) && (!(self.flags & FL_WATERJUMP))) + SV_AddGravity(1); + SV_FlyMove(); + } + else if ((self.movetype == MOVETYPE_TOSS) || (self.movetype == MOVETYPE_BOUNCE)) + { + SV_Physics_Toss(); + } + else if (self.movetype == MOVETYPE_FLY) + { + if (!SV_RunThink()) + return; + SV_FlyMove(); + } + else if (self.movetype == MOVETYPE_NOCLIP) + { + if (!SV_RunThink()) + return; + self.origin = self.origin + real_frametime * self.velocity; + + PlayerPostThink(); + BotAI(); + } + else + error ("SV_Physics_Client: Bad Movetype (BOT)"); + +}; + + diff --git a/qcsrc/gamec/bot_way.c b/qcsrc/gamec/bot_way.c new file mode 100644 index 000000000..c00078060 --- /dev/null +++ b/qcsrc/gamec/bot_way.c @@ -0,0 +1,1001 @@ + /*********************************************** +* * +* FrikBot Waypoints * +* "The better than roaming AI" * +* * +***********************************************/ + +/* + +This program is in the Public Domain. My crack legal +team would like to add: + +RYAN "FRIKAC" SMITH IS PROVIDING THIS SOFTWARE "AS IS" +AND MAKES NO WARRANTY, EXPRESS OR IMPLIED, AS TO THE +ACCURACY, CAPABILITY, EFFICIENCY, MERCHANTABILITY, OR +FUNCTIONING OF THIS SOFTWARE AND/OR DOCUMENTATION. IN +NO EVENT WILL RYAN "FRIKAC" SMITH BE LIABLE FOR ANY +GENERAL, CONSEQUENTIAL, INDIRECT, INCIDENTAL, +EXEMPLARY, OR SPECIAL DAMAGES, EVEN IF RYAN "FRIKAC" +SMITH HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES, IRRESPECTIVE OF THE CAUSE OF SUCH DAMAGES. + +You accept this software on the condition that you +indemnify and hold harmless Ryan "FrikaC" Smith from +any and all liability or damages to third parties, +including attorney fees, court costs, and other +related costs and expenses, arising out of your use +of this software irrespective of the cause of said +liability. + +The export from the United States or the subsequent +reexport of this software is subject to compliance +with United States export control and munitions +control restrictions. You agree that in the event you +seek to export this software, you assume full +responsibility for obtaining all necessary export +licenses and approvals and for assuring compliance +with applicable reexport restrictions. + +Any reproduction of this software must contain +this notice in its entirety. + +*/ + + + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +Waypoint Linking code + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + + +float (entity e1, entity e2) CheckLinked = +{ + if ((e1 == e2) || (e2 == world) || (e1 == world)) + return FALSE; + else if (e1.target1 == e2) + { + if (e1.b_aiflags & AI_TELELINK_1) + return 2; + else return TRUE; + } + else if (e1.target2 == e2) + { + if (e1.b_aiflags & AI_TELELINK_2) + return 2; + else return TRUE; + } + else if (e1.target3 == e2) + { + if (e1.b_aiflags & AI_TELELINK_3) + return 2; + else return TRUE; + } + else if (e1.target4 == e2) + { + if (e1.b_aiflags & AI_TELELINK_4) + return 2; + else return TRUE; + } + + else return FALSE; +}; + + +float (entity e1, entity e2) LinkWays = +{ + if ((e1 == e2) || (e2 == world) || (e1 == world)) + return FALSE; + else if (CheckLinked(e1, e2)) + return FALSE; // already linked!!! + + if (e1.target1 == world) + { + e1.target1 = e2; + return TRUE; + } + else if (e1.target2 == world) + { + e1.target2 = e2; + return TRUE; + } + else if (e1.target3 == world) + { + e1.target3 = e2; + return TRUE; + } + else if (e1.target4 == world) + { + e1.target4 = e2; + return TRUE; + } + else return FALSE; + +}; +// Link Ways part 2, used only for teleporters + +float (entity e1, entity e2) TeleLinkWays = +{ + if ((e1 == e2) || (e2 == world) || (e1 == world)) + return FALSE; + else if (CheckLinked(e1, e2)) + return FALSE; // already linked!!! + + if (e1.target1 == world) + { + e1.target1 = e2; + e1.b_aiflags = e1.b_aiflags | AI_TELELINK_1; + return TRUE; + } + else if (e1.target2 == world) + { + e1.target2 = e2; + e1.b_aiflags = e1.b_aiflags | AI_TELELINK_2; + return TRUE; + } + else if (e1.target3 == world) + { + e1.target3 = e2; + e1.b_aiflags = e1.b_aiflags | AI_TELELINK_3; + return TRUE; + } + else if (e1.target4 == world) + { + e1.target4 = e2; + e1.b_aiflags = e1.b_aiflags | AI_TELELINK_4; + return TRUE; + } + else + return FALSE; + +}; + +void (entity e1, entity e2) UnlinkWays = +{ + if ((e1 == e2) || (e2 == world) || (e1 == world)) + return; + else if (!CheckLinked(e1, e2)) + return; + + if (e1.target1 == e2) + { + e1.b_aiflags = e1.b_aiflags - (e1.b_aiflags & AI_TELELINK_1); + e1.target1 = world; + } + if (e1.target2 == e2) + { + e1.b_aiflags = e1.b_aiflags - (e1.b_aiflags & AI_TELELINK_2); + e1.target2 = world; + } + if (e1.target3 == e2) + { + e1.b_aiflags = e1.b_aiflags - (e1.b_aiflags & AI_TELELINK_3); + e1.target3 = world; + } + if (e1.target4 == e2) + { + e1.b_aiflags = e1.b_aiflags - (e1.b_aiflags & AI_TELELINK_4); + e1.target4 = world; + } + +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +FindWaypoint + +This is used quite a bit, by many different +functions big lag causer + +Finds the closest, fisible, waypoint to e + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +entity(entity start) FindWayPoint = +{ + local entity t; + local vector org; + local float dst, tdst; + local entity best; + + org = realorigin(self); + + t = way_head; + if (start != world) + { + dst = vlen(start.origin - org); + best = start; + } + else + { + dst = 100000; + best = world; + } + while(t) + { + // real players cut through ignore types + if (dst < 20) + return best; + if (!(t.b_aiflags & AI_IGNORE_TYPES) || self.ishuman) + { + tdst = vlen(t.origin - org); + if (tdst < dst) + { + if (sisible(t)) + { + dst = tdst; + best = t; + } + } + } + t = t._next; + } + return best; +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +Waypoint Spawning Code + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +entity way_foot; // Ugh. Do I need a foot for this or not? + +entity(vector org) make_waypoint = +{ + local entity point; + point = spawn(); + point.classname = "waypoint"; + point.search_time = time; // don't double back for me; + point.solid = SOLID_TRIGGER; + point.movetype = MOVETYPE_NONE; + point.items = -1; + setorigin(point, org); + + setsize(point, PL_MIN, PL_MAX); + waypoints = waypoints + 1; + if (!way_head) + { + way_head = point; + way_foot = point; + } + else + { + way_foot._next = point; + point._last = way_foot; + way_foot = point; + } + + point.count = waypoints; + if (waypoint_mode > WM_LOADED) // editor modes + setmodel(point, "progs/s_bubble.spr"); + return point; +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +Dynamic Waypoint spawning and linking. Not +very good all things considered. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void() DynamicWaypoint = +{ + local entity t; + local float dist, dynlink, dynpoint, editor; + + if (self.teleport_time > self.portal_time) + { + if (!self.flags & FL_WATERJUMP) + { + self.dyn_flags = 2; + if (!self.ishuman) + { + bot_lost(self.target1, TRUE); + self.enemy = world; + } + } + self.portal_time = self.teleport_time; + } +// stacking everything on waypoint_mode might've been good for the editor, +// but it sucks to beat hell for this code. + + +// convert waypoint_mode to something more usable.. + if (waypoint_mode > WM_LOADED) + { + if (self.ishuman) + { + if (waypoint_mode == WM_EDITOR_DYNLINK) + dynlink = 1; + else if (waypoint_mode == WM_EDITOR_DYNAMIC) + dynlink = dynpoint = 1; + editor = 1; + } + } + else if (waypoint_mode == WM_DYNAMIC) + dynlink = dynpoint = 1; + +// if there's nothing for dynamic to do.. + if (!dynpoint) + { + if (!editor) + return; + } +// for speed sake, I won't have bots dynamic waypoint in coop + if (!self.ishuman) + if (coop) + return; + +// don't waypoint in single player + if (max_clients < 2) + return; +// if you're dead + else if (self.health <= 0) + { + if (dynpoint) + { + if (self.current_way) + { + if (pointcontents(self.origin) < -4) + { + if (self.current_way.b_aiflags & AI_BLIND) + self.current_way.b_aiflags = self.current_way.b_aiflags | AI_PRECISION; + else + self.current_way.b_aiflags = self.current_way.b_aiflags | AI_BLIND; + } + } + } + self.dyn_dest = '0 0 0'; + self.current_way = world; + self.dyn_flags = 0; + return; + } + +// you shouldn't be making waypoints mid air + if (dynpoint) + { + if (!((self.flags & FL_ONGROUND) || self.waterlevel == 3)) + { + if (self.dyn_flags != 2) + { + self.dyn_flags = 1; + } + return; + } + } +// keep from doing the rest of this every frame + if (self.dyn_time > time) + return; + self.dyn_time = time + 0.2; + +// display the links for editor mode + if (editor) + { + if (self.current_way) + { + if (self.current_way.target1) + DeveloperLightning(self.current_way, self.current_way.target1, self.current_way.b_aiflags & AI_TELELINK_1); + if (self.current_way.target2) + DeveloperLightning(self.current_way, self.current_way.target2, self.current_way.b_aiflags & AI_TELELINK_2); + if (self.current_way.target3) + DeveloperLightning(self.current_way, self.current_way.target3, self.current_way.b_aiflags & AI_TELELINK_3); + if (self.current_way.target4) + DeveloperLightning(self.current_way, self.current_way.target4, self.current_way.b_aiflags & AI_TELELINK_4); + } + if (self.b_aiflags & AI_HOLD_SELECT) + return; + } + + t = FindWayPoint(self.current_way); + if (t) + { + dist = vlen(self.origin - t.origin); + if (dist < 192) + { + if (dist < 64) + { + + if (t != self.current_way) + { + if (dynlink) + { + if (!self.dyn_flags) + { + if (wisible(t, self.current_way)) + LinkWays(t, self.current_way); + } + if (self.dyn_flags == 2) + TeleLinkWays(self.current_way, t); + else if (wisible(t, self.current_way)) + LinkWays(self.current_way, t); + } + if (editor) + { + setmodel(t, "progs/s_light.spr"); + if (self.current_way) + setmodel(self.current_way, "progs/s_bubble.spr"); + } + } + self.current_way = t; + self.dyn_flags = 0; + } + self.dyn_dest = self.origin + self.view_ofs; + return; + } + } + + if (frik_recognize_plat(FALSE)) + { + if (vlen(trace_ent.velocity) > 0) + { + if (self.dyn_plat) + return; + self.dyn_plat = TRUE; + if (!self.dyn_flags) + self.dyn_flags = 1; + //bprint("on a plat!!!!!\n"); + } + else + self.dyn_plat = FALSE; + } + else + self.dyn_plat = FALSE; + + if (self.dyn_flags == 2) + self.dyn_dest = self.origin + self.view_ofs; + else if (self.dyn_dest == '0 0 0') + self.dyn_dest = self.origin + self.view_ofs; + if (!dynpoint) + return; + t = make_waypoint(self.dyn_dest); + + if (!self.dyn_flags) + { + if (wisible(t, self.current_way)) + LinkWays(t, self.current_way); + } + if (self.dyn_flags == 2) + TeleLinkWays(self.current_way, t); + else if (wisible(t, self.current_way)) + LinkWays(self.current_way, t); + + if (editor) + { + setmodel(t, "progs/s_light.spr"); + if (self.current_way) + setmodel(self.current_way, "progs/s_bubble.spr"); + } + self.current_way = t; + self.dyn_flags = 0; + + self.dyn_dest = self.origin + self.view_ofs; + + if (frik_recognize_plat(FALSE)) + { + if (trace_ent.classname == "door") + t.b_aiflags = t.b_aiflags | AI_DOORFLAG; + } +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +Waypoint Loading from file + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void() ClearAllWays = +{ + + local entity t, n; + t = way_head; + while(t) + { + n = t._next; + remove(t); + t = n; + } + way_head = world; + way_foot = world; + waypoints = 0; +}; + +entity(float num) WaypointForNum = +{ + local entity t; + if (!num) + return world; + + t = way_head; + while (t) + { + if (t.count == num) + return t; + t = t._next; + } + return world; +}; + +void() FixThisWaypoint = +{ + self.enemy.target1 = WaypointForNum(self.enemy.b_pants); + self.enemy.target2 = WaypointForNum(self.enemy.b_skill); + self.enemy.target3 = WaypointForNum(self.enemy.b_shirt); + self.enemy.target4 = WaypointForNum(self.enemy.b_frags); + self.enemy = self.enemy._next; + self.nextthink = time; + if (self.enemy == world) + { + remove(self); + fixer = world; + } +}; + +void() FixWaypoints = +{ + if (!fixer) + fixer = spawn(); + fixer.nextthink = time; + fixer.think = FixThisWaypoint; + fixer.enemy = way_head; +}; + + + +void(entity what) delete_waypoint = +{ + local entity t; + + if (way_head == what) + way_head = what._next; + if (way_foot == what) + way_foot = what._last; + if (what._last) + what._last._next = what._next; + if (what._next) + what._next._last = what._last; + waypoints = 0; + t = way_head; + while(t) + { + t.count = waypoints = waypoints + 1; + if (CheckLinked(t, what)) + UnlinkWays(t, what); + t = t._next; + } + if (self.current_way == what) + self.current_way = world; + remove(what); +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +FindRoute & FindThing used by the pathing code +in bot_ai.qc + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + + +entity(string s) FindThing = +{ + local entity t; + local float tdst, dst; + local entity best; + dst = 100000; + best = world; + t = find (world, classname, s); + while (t != world) + { + tdst = vlen(((t.absmin + t.absmax) * 0.5) - self.origin); + if (tdst < dst) + { + dst = tdst; + best = t; + } + t = find(t, classname, s); + } + return best; +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +FindRoute, this is a key function in the +pathing. The name is a bit misleading, this +code finds the closest waypoint that is part +of a route calculated by the begin_route and +end_route routines This is a definite path to +an object. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +entity(entity lastone) FindRoute = +{ + // kinda like FindWaypoint, only of this bots route though + local entity t, best; + local float dst, tdst, flag; + flag = ClientBitFlag(self.b_clientno); + t = way_head; + dst = 100000; + best = world; + while(t) + { + tdst = vlen(t.origin - self.origin); + if ((tdst < dst) && (t.b_sound & flag)) + { + if ((lastone == world) || (CheckLinked(lastone, t))) + { + dst = tdst; + best = t; + } + } + t = t._next; + } + return best; +}; +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +Route & path table management + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void() ClearRouteTable = +{ + // cleans up route table + + local entity t; + t = way_head; + while (t) + { + t. keys = FALSE; + t.enemy = world; + t.items = -1; // not in table + t = t._next; + } +}; + +void() ClearMyRoute = +{ + local float flag; + local entity t; + + flag = ClientBitFlag(self.b_clientno); + + t = way_head; + while (t) + { + t.b_sound = t.b_sound - (t.b_sound & flag); + t = t._next; + } +}; + + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +Mark_path + +After the route has been found, mark it with +bitflags so the table can be used for a +different bot. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + + +void(entity this) mark_path = +{ + local entity t, oself; + local float flag; + + ClearMyRoute(); + + oself = self; + self = this; + t = FindWayPoint(this.current_way); + self = oself; + // FIXME + // ugh, better way to find players please!!! + if (this.classname != "player") + this.current_way = t; + + if (t.enemy == world) + { + bot_lost(this, FALSE); + if (waypoint_mode == WM_DYNAMIC) + self.route_failed = TRUE; + return; + } + + flag = ClientBitFlag(self.b_clientno); + + while(t) + { + if (t.b_sound & flag) + return; + if (t == self.last_way) + return; + t.b_sound = t.b_sound | flag; + t = t.enemy; + } +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +WaypointThink + +Calculates the routes. We use thinks to avoid +tripping the runaway loop counter + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void(entity e2, float b_bit) FollowLink = +{ + local float dist; + + if (self.b_aiflags & b_bit) + dist = self.items; + else + dist = vlen(self.origin - e2.origin) + self.items; + + // check if this is an RJ link + if (e2.b_aiflags & AI_SUPER_JUMP) + { + if (!bot_can_rj(route_table)) + return; + } + if (e2.b_aiflags & AI_DIFFICULT) + dist = dist + 1000; + + dist = dist + random() * 100; // add a little chaos + + if ((dist < e2.items) || (e2.items == -1)) + { + if (!e2.keys) + busy_waypoints = busy_waypoints + 1; + e2.keys = TRUE; + e2.items = dist; + e2.think = WaypointThink; + e2.nextthink = time; + e2.enemy = self; + } +}; + +void() WaypointThink = +{ + local entity oself; + + if (self.items == -1) + return; + // can you say ugly? + if (self.b_aiflags & AI_TRACE_TEST) + { + if (self.target1) + { + traceline(self.origin, self.target1.origin, TRUE, self); + if (trace_fraction == 1) + FollowLink(self.target1, AI_TELELINK_1); + } + if (self.target2) + { + traceline(self.origin, self.target2.origin, TRUE, self); + if (trace_fraction == 1) + FollowLink(self.target2, AI_TELELINK_2); + } + if (self.target3) + { + traceline(self.origin, self.target3.origin, TRUE, self); + if (trace_fraction == 1) + FollowLink(self.target3, AI_TELELINK_3); + } + if (self.target4) + { + traceline(self.origin, self.target4.origin, TRUE, self); + if (trace_fraction == 1) + FollowLink(self.target4, AI_TELELINK_4); + } + } + else + { + if (self.target1) + FollowLink(self.target1, AI_TELELINK_1); + if (self.target2) + FollowLink(self.target2, AI_TELELINK_2); + if (self.target3) + FollowLink(self.target3, AI_TELELINK_3); + if (self.target4) + FollowLink(self.target4, AI_TELELINK_4); + } + + busy_waypoints = busy_waypoints - 1; + self.keys = FALSE; + + if (busy_waypoints <= 0) + { + if (direct_route) + { + oself = self; + self = route_table; + bot_get_path(self.target1, FALSE); + self = oself; + direct_route = FALSE; + } + } +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +begin_route and bot_get_path + +PLEASE NOTE: bot_get_path replaces the old +calls to begin_route. + +Routing isn't done all at once now, but in two +stages, the bot will calc a route *THEN* +choose a target, *THEN* mark a path. + +Boy it's confusing. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +float() begin_route = +{ + if (busy_waypoints > 0) + return FALSE; + + if (route_table != world) + { + if (!route_table.ishuman) + { + if (route_table.b_clientno != -1) + return FALSE; + } + } + + route_table = self; + ClearRouteTable(); + self.last_way = FindWayPoint(self.current_way); + + if (self.last_way != world) + { + self.last_way.items = vlen(self.last_way.origin - self.origin); + self.last_way.nextthink = time; + self.last_way.think = WaypointThink; + self.last_way.keys = TRUE; + busy_waypoints = 1; + return TRUE; + } + else + { + route_table = world; + busy_waypoints = 0; + return FALSE; + } +}; + +void(entity this, float direct) bot_get_path = +{ + if (this == world) + return; + + if (route_table == self) + { + if (busy_waypoints <= 0) + { + route_table = world; + mark_path(this); + } + return; + } + if (direct) + { + if(begin_route()) + direct_route = TRUE; + else + bot_lost(this, FALSE); + return; + } +}; + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +BSP/QC Waypoint loading + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void() waypoint = +{ + self.search_time = time; + self.solid = SOLID_TRIGGER; + self.movetype = MOVETYPE_NONE; + setorigin(self, self.origin); + + setsize(self, PL_MIN, PL_MAX); + waypoints = waypoints + 1; + if (!way_head) + { + way_head = self; + way_foot = self; + } + else + { + way_foot._next = self; + self._last = way_foot; + way_foot = self; + } + + self.count = waypoints; + waypoint_mode = WM_LOADED; + if (self.count == 1) + { + self.think = FixWaypoints; // wait until all bsp loaded points are spawned + self.nextthink = time; + } +}; + +void(vector org, vector bit1, float bit4, float flargs) make_way = +{ + local entity y; + waypoint_mode = WM_LOADED; + y = make_waypoint(org); + y.b_aiflags = flargs; + y.b_pants = bit1_x; + y.b_skill = bit1_y; + y.b_shirt = bit1_z; + y.b_frags = bit4; + if (y.count == 1) + { + y.think = FixWaypoints; // wait until all qc loaded points are spawned + y.nextthink = time; + } +}; + + +/* +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +Temporary Marker code + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +*/ + +void(vector org) SpawnTempWaypoint = +{ + local entity tep; + + if (!self.temp_way) + self.temp_way = tep = spawn(); + else + tep = self.temp_way; + + tep.classname = "temp_waypoint"; + tep.search_time = 0; + tep.solid = SOLID_TRIGGER; + tep.movetype = MOVETYPE_NOCLIP; + setorigin(tep, org); + target_add(tep); + setsize(tep, PL_MIN, PL_MAX); // FIXME: convert these to numerical +}; \ No newline at end of file diff --git a/qcsrc/gamec/builtins.h b/qcsrc/gamec/builtins.h new file mode 100644 index 000000000..99fc98ebc --- /dev/null +++ b/qcsrc/gamec/builtins.h @@ -0,0 +1,59 @@ + +void makevectors (vector ang) = #1; +void setorigin (entity e, vector o) = #2; +void setmodel (entity e, string m) = #3; +void setsize (entity e, vector min, vector max) = #4; + +void crash (void) = #6; +float random (void) = #7; +vector normalize (vector v) = #9; +void error (string e) = #10; +void objerror (string e) = #11; +float vlen (vector v) = #12; +float vectoyaw (vector v) = #13; +entity spawn (void) = #14; +void remove (entity e) = #15; +void traceline (vector v1, vector v2, float nomonst, entity forent) = #16; +entity checkclient (void) = #17; +entity find (entity start, .string fld, string match) = #18; +string precache_sound (string s) = #19; +string precache_model (string s) = #20; +entity findradius (vector org, float rad) = #22; +void bprint (string s) = #23; +void dprint (string s) = #25; +string ftos (float f) = #26; +string vtos (vector v) = #27; +void coredump (void) = #28; +void traceon (void) = #29; +void traceoff (void) = #30; +void eprint (entity e) = #31; +float walkmove (float yaw, float dist) = #32; + +float droptofloor (float yaw, float dist) = #34; +void lightstyle (float style, string value) = #35; +float rint (float v) = #36; +float floor (float v) = #37; +float ceil (float v) = #38; + +float checkbottom (entity e) = #40; +float pointcontents (vector v) = #41; + +float fabs (float f) = #43; +float cvar (string s) = #45; +void localcmd (string s) = #46; +entity nextent (entity e) = #47; +void particle (vector v, vector d, float colour, float count) = #48; +void ChangeYaw (void) = #49; + +vector vectoangles (vector v) = #51; + +void movetogoal (float step) = #67; +string precache_file (string s) = #68; +void makestatic (entity e) = #69; +void changelevel (string s) = #70; + +void cvar_set (string var, string val) = #72; +void ambientsound (vector pos, string samp, float vol, float atten) = #74; +string precache_model2 (string s) = #75; +string precache_sound2 (string s) = #76; +string precache_file2 (string s) = #77; diff --git a/qcsrc/gamec/cl_aliases.c b/qcsrc/gamec/cl_aliases.c new file mode 100644 index 000000000..45cbc4fb8 --- /dev/null +++ b/qcsrc/gamec/cl_aliases.c @@ -0,0 +1,32 @@ +void AddAlias(entity client, string alias, string command) { + stuffcmd(client, "alias "); + stuffcmd(client, alias); + stuffcmd(client, " \""); + stuffcmd(client, command); + stuffcmd(client, "\"\n"); +}; + +void AddImpulse(entity client, string alias, float imp) { + stuffcmd(client, "alias "); + stuffcmd(client, alias); + stuffcmd(client, " \"impulse "); + stuffcmd(client, ftos(imp)); + stuffcmd(client, "\"\n"); +}; + +void DoAliases() { + if (self.hasaliases) + return; + + AddImpulse(self, "weapnext", 10); + AddImpulse(self, "weapnext", 12); + AddImpulse(self, "+crouch", 97); + AddImpulse(self, "-crouch", 98); + AddImpulse(self, "bot_add", 100); + AddImpulse(self, "bot_add2", 101); + AddImpulse(self, "bot_kick", 102); + AddImpulse(self, "bot_cam", 102); + AddImpulse(self, "bot_wayedit", 102); + + self.hasaliases = TRUE; +}; diff --git a/qcsrc/gamec/cl_client.c b/qcsrc/gamec/cl_client.c new file mode 100644 index 000000000..11a5dee18 --- /dev/null +++ b/qcsrc/gamec/cl_client.c @@ -0,0 +1,477 @@ + +void info_player_start (void) +{ + self.classname = "info_player_deathmatch"; +} + +void info_player_deathmatch (void) +{ +} + +/* +============= +SelectSpawnPoint + +Finds a point to respawn +============= +*/ +entity SelectSpawnPoint (void) +{ + local entity spot, thing; + local float pcount; + + spot = find (world, classname, "testplayerstart"); + if (spot) + return spot; + + spot = lastspawn; + while (1) + { + spot = find(spot, classname, "info_player_deathmatch"); + if (spot != world) + { + if (spot == lastspawn) + return lastspawn; + pcount = 0; + thing = findradius(spot.origin, 70); + while(thing) + { + if (thing.classname == "player") + pcount = pcount + 1; + thing = thing.chain; + } + if (pcount == 0) + { + lastspawn = spot; + return spot; + } + } + } + + spot = find (world, classname, "info_player_start"); + if (!spot) + error ("PutClientInServer: no info_player_start on level"); + + return spot; +} + + +/* +============= +PutClientInServer + +Called when a client spawns in the server +============= +*/ +void PutClientInServer (void) +{ + entity spot; + float mdlrandom; + + spot = SelectSpawnPoint (); + + self.classname = "player"; + self.movetype = MOVETYPE_WALK; + self.solid = SOLID_SLIDEBOX; + self.flags = FL_CLIENT; + self.takedamage = DAMAGE_YES; + self.effects = 0; + self.health = 150; + self.damageforcescale = 2; + self.death_time = 0; + self.dead_time = 0; + self.dead_frame = 0; + self.die_frame = 0; + self.alpha = 0; + self.scale = 0; + self.fade_time = 0; + self.pain_frame = 0; + self.pain_finished = 0; + self.strength_finished = 0; + self.invincible_finished = 0; + self.speed_finished = 0; + self.slowmo_finished = 0; + // players have no think function + self.think = SUB_Null; + self.nextthink = 0; + + self.deadflag = DEAD_NO; + + self.view_ofs = PL_VIEW_OFS; + self.angles = spot.angles; + + self.viewzoom = 0.6; + + mdlrandom = random() * 5; + if (mdlrandom < 1) + { + setmodel (self, "models/player/insurrectionist.zym"); + self.angleoffset = '0 90 0'; + } + else if (mdlrandom < 2) + { + setmodel (self, "models/player/mulder.zym"); + self.angleoffset = '0 90 0'; + } + else if (mdlrandom < 3) + { + setmodel (self, "models/player/marine.zym"); + self.angleoffset = '0 90 0'; + } + else if (mdlrandom < 4) + { + setmodel (self, "models/player/specop.zym"); + self.angleoffset = '0 90 0'; + } + else + { + setmodel (self, "models/player/fshock.zym"); + self.angleoffset = '0 90 0'; + } + + + setsize (self, PL_MIN, PL_MAX); + setorigin (self, spot.origin + '0 0 1' * (1 - self.mins_z - 24)); + // don't reset back to last position, even if new position is stuck in solid + self.oldorigin = self.origin; + +// self.items = IT_LASER | IT_UZI| IT_SHOTGUN | IT_GRENADE_LAUNCHER | IT_ELECTRO | IT_CRYLINK | IT_NEX | IT_HAGAR | IT_ROCKET_LAUNCHER; +// self.weapon = IT_UZI; + + if (game & GAME_INSTAGIB) + { + self.items = IT_NEX; + self.switchweapon = WEP_NEX; + self.ammo_shells = 0; + self.ammo_nails = 0; + self.ammo_rockets = 0; + self.ammo_cells = 999; + } + else if (game & GAME_ROCKET_ARENA) + { + self.items = IT_ROCKET_LAUNCHER; + self.switchweapon = WEP_ROCKET_LAUNCHER; + self.ammo_shells = 0; + self.ammo_nails = 0; + self.ammo_rockets = 999; + self.ammo_cells = 0; + } + else + { + self.items = IT_LASER; + self.switchweapon = WEP_LASER; + self.ammo_shells = 0; + self.ammo_nails = 0; + self.ammo_rockets = 0; + self.ammo_cells = 0; + } + + if (game & GAME_FULLBRIGHT_PLAYERS) + self.effects = EF_FULLBRIGHT; + + self.event_damage = PlayerDamage; + + self.statdraintime = time + 5; + self.button0 = self.button1 = self.button2 = self.button3 = 0; + + /* + W_UpdateWeapon(); + W_UpdateAmmo(); + */ + CL_SpawnWeaponentity(); + + //stuffcmd(self, "chase_active 0"); +} + +/* +============= +SetNewParms +============= +*/ +void SetNewParms (void) +{ + +} + +/* +============= +SetChangeParms +============= +*/ +void SetChangeParms (void) +{ + +} + +/* +============= +ClientKill + +Called when a client types 'kill' in the console +============= +*/ +void ClientKill (void) +{ + +} + +/* +============= +ClientConnect + +Called when a client connects to the server +============= +*/ +void ClientConnect (void) +{ + ClientInRankings(); + bprint (self.netname); + bprint (" connected\n"); +} + +/* +============= +ClientDisconnect + +Called when a client disconnects from the server +============= +*/ +void ClientDisconnect (void) +{ + ClientDisconnected(); + bprint (self.netname); + bprint (" disconnected\n"); +} + +/* +============= +PlayerJump + +When you press the jump key +============= +*/ +void PlayerJump (void) +{ + if (!(self.flags & FL_ONGROUND)) + return; + if (!(self.flags & FL_JUMPRELEASED)) + return; + + if (self.items & IT_SPEED) + self.velocity_z = self.velocity_z + POWERUP_SPEED_JUMPVELOCITY; + else + self.velocity_z = self.velocity_z + JUMP_VELOCITY; + + self.flags = self.flags - FL_ONGROUND; + self.flags = self.flags - FL_JUMPRELEASED; +} + +void respawn(void) +{ + CopyBody(); + PutClientInServer(); +} + +void player_powerups (void) +{ + if (self.items & IT_STRENGTH) + { + if (time > self.strength_finished) + { + self.items = self.items - (self.items & IT_STRENGTH); + sprint(self, "Strength has worn off\n"); + } + } + else + { + if (time < self.strength_finished) + { + self.items = self.items | IT_STRENGTH; + sprint(self, "Strength infuses your weapons with devestating power\n"); + } + } + if (self.items & IT_INVINCIBLE) + { + if (time > self.invincible_finished) + { + self.items = self.items - (self.items & IT_INVINCIBLE); + sprint(self, "Invincible has worn off\n"); + } + } + else + { + if (time < self.invincible_finished) + { + self.items = self.items | IT_INVINCIBLE; + sprint(self, "Invincible shielding surrounds you\n"); + } + } + if (self.items & IT_SPEED) + { + if (time > self.speed_finished) + { + self.items = self.items - (self.items & IT_SPEED); + sprint(self, "Speed has worn off\n"); + } + } + else + { + if (time < self.speed_finished) + { + self.items = self.items | IT_SPEED; + sprint(self, "Speed makes you run faster than ever before\n"); + } + } + if (self.items & IT_SLOWMO) + { + if (time > self.slowmo_finished) + { + self.items = self.items - (self.items & IT_SLOWMO); + sprint(self, "Slow Motion has worn off\n"); + } + } + else + { + if (time < self.slowmo_finished) + { + self.items = self.items | IT_SLOWMO; + sprint(self, "Slow Motion slows time around you\n"); + } + } + /* + self.items = self.items - (self.items & (IT_STRENGTH + IT_INVINCIBLE + IT_SLOWMO + IT_SPEED)); + if (time < self.strength_finished) + self.items = self.items | IT_STRENGTH; + if (time < self.invincible_finished) + self.items = self.items | IT_INVINCIBLE; + if (time < self.slowmo_finished) + self.items = self.items | IT_SLOWMO; + if (time < self.speed_finished) + self.items = self.items | IT_SPEED; + */ +} + +void player_regen (void) +{ + // GAME_REGENERATION does fast health regeneration up to 200. Note that your armour doesn't rot anymore either. + if (game & GAME_REGENERATION) + { + self.health = self.health + (200 - self.health) * 0.2 * frametime; + self.armorvalue = bound(0, self.armorvalue, 1000); + } + else + { + self.health = bound(0, self.health + (100 - self.health) * 0.05 * frametime, 1000); + if (self.armorvalue > 100) + self.armorvalue = bound(100, self.armorvalue + (100 - self.armorvalue) * 0.05 * frametime, 1000); + } +} + +/* +============= +PlayerPreThink + +Called every frame for each client before the physics are run +============= +*/ +.float attack_finished; +void PlayerPreThink (void) +{ + if (BotPreFrame()) + return; + if (!self.hasaliases) + DoAliases(); + + if (self.deadflag != DEAD_NO) + { + player_anim(); + weapon_freeze(); + if (self.deadflag == DEAD_DYING) + { + if (time > self.dead_time) + self.deadflag = DEAD_DEAD; + } + else if (self.deadflag == DEAD_DEAD) + { + if (!self.button0 && !self.button2 && !self.button3) + self.deadflag = DEAD_RESPAWNABLE; + } + else if (self.deadflag == DEAD_RESPAWNABLE) + { + if (self.button0 || self.button2 || self.button3 || self.button4) + respawn(); + } + return; + } + +/* + if (self.button4) + { + if (self.weapon == IT_ROCKET_LAUNCHER) + W_ThirdAttack (); + if (!(game & GAME_INSANE)) + if (self.attack_finished < time && !self.button0 && !self.button3) + W_ThirdAttack (); + } +*/ + + W_WeaponFrame(); + + if (self.button3) + { + if (self.weapon == IT_NEX) + if (self.viewzoom > 0.4) + self.viewzoom = max (0.4, self.viewzoom - frametime * 2); + } + else if (self.viewzoom < 1.0) + self.viewzoom = min (1.0, self.viewzoom + frametime); + + /* Old weapon system + if (self.button3) + { + if (self.weapon == IT_ROCKET_LAUNCHER) + W_SecondaryAttack (); + if (!(game & GAME_INSANE)) + if (self.attack_finished < time && !self.button0) + W_SecondaryAttack (); + if (game & GAME_INSANE) + W_SecondaryAttack (); + } + + if (!(game & GAME_INSANE)) + if (self.attack_finished < time) + if (self.button0) + W_Attack (); +*/ + + + if (self.button2) + PlayerJump (); + else + self.flags = self.flags | FL_JUMPRELEASED; + + + player_powerups(); + player_regen(); + player_anim(); +// weapon_anim(); + + if (TetrisPreFrame()) return; +} + +/* +============= +PlayerPostThink + +Called every frame for each client after the physics are run +============= +*/ +void PlayerPostThink (void) +{ + if (BotPostFrame()) + return; + if (self.health > 0) + if (self.impulse) + ImpulseCommands (); + if (TetrisPostFrame()) return; +} \ No newline at end of file diff --git a/qcsrc/gamec/cl_impulse.c b/qcsrc/gamec/cl_impulse.c new file mode 100644 index 000000000..d567a3e9b --- /dev/null +++ b/qcsrc/gamec/cl_impulse.c @@ -0,0 +1,224 @@ + +// changes by LordHavoc on 03/30/04 +// cleaned up dummy code +// dummies are now removed eventually after being gibbed (norespawn = TRUE) +// dummy impulse now checks sv_cheats to prevent players from overwhelming server with dummies +// dummies now use player code where possible + +void player_anim (void); +void DummyThink(void) +{ + self.think = DummyThink; + self.nextthink = time; + SV_PlayerPhysics(); + PlayerPreThink(); + //player_anim(); + PlayerPostThink(); +} + +void PlayerDamage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype); +void CreateDummy (vector org, float type) +{ + entity oldself; + oldself = self; + self = spawn (); + self.norespawn = TRUE; + self.solid = SOLID_SLIDEBOX; + self.movetype = MOVETYPE_WALK; + self.classname = "corpse"; + self.takedamage = DAMAGE_YES; + self.damageforcescale = oldself.damageforcescale; + self.health = 100; + self.weapon = IT_LASER; + setsize (self, oldself.mins, oldself.maxs); + setorigin (self, org); + if (type==1) + setmodel (self, "models/player/marine.zym"); + if (type==2) + setmodel (self, "models/player/grunt.zym"); + if (type==3) + setmodel (self, "models/player/specop.zym"); + if (type==4) + setmodel (self, "models/player/pyria.zym"); + if (type==5) + setmodel (self, "models/player/lurk.zym"); + if (type==6) + setmodel (self, "models/player/visitant.zym"); + if (type==7) + setmodel (self, "models/player/headhunter.zym"); + if (type==8) + setmodel (self, "models/player/jeandarc.zym"); + if (type==9) + setmodel (self, "models/player/mulder.zym"); + if (type==10) + setmodel (self, "models/player/insurrectionist.zym"); + if (type==11) + setmodel (self, "models/player/robot.zym"); + if (type==12) + setmodel (self, "models/player/lycanthrope.zym"); + if (type==13) + setmodel (self, "models/player/fshock.zym"); + setsize (self, PL_MIN, PL_MAX); + self.event_damage = PlayerDamage; + DummyThink(); + self = oldself; +} + +void ImpulseCommands (void) +{ + if (self.impulse >= 1 && self.impulse <= 9) + W_SwitchWeapon (self.impulse); + else if (self.impulse == 10) + W_NextWeapon (); + else if (self.impulse == 12) + W_PreviousWeapon (); + else if (self.impulse == 13 && cvar("sv_cheats")) + { + makevectors (self.v_angle); + CreateDummy (self.origin + self.view_ofs + v_forward * 64, 1); + } + else if (self.impulse == 14 && cvar("sv_cheats")) + { + makevectors (self.v_angle); + CreateDummy (self.origin + self.view_ofs + v_forward * 64, 2); + } + else if (self.impulse == 15 && cvar("sv_cheats")) + { + makevectors (self.v_angle); + CreateDummy (self.origin + self.view_ofs + v_forward * 64, 3); + } + else if (self.impulse == 16 && cvar("sv_cheats")) + { + makevectors (self.v_angle); + CreateDummy (self.origin + self.view_ofs + v_forward * 64, 4); + } + else if (self.impulse == 17 && cvar("sv_cheats")) + { + makevectors (self.v_angle); + CreateDummy (self.origin + self.view_ofs + v_forward * 64, 5); + } + else if (self.impulse == 18 && cvar("sv_cheats")) + { + makevectors (self.v_angle); + CreateDummy (self.origin + self.view_ofs + v_forward * 64, 6); + } + else if (self.impulse == 19 && cvar("sv_cheats")) + { + makevectors (self.v_angle); + CreateDummy (self.origin + self.view_ofs + v_forward * 64, 7); + } + else if (self.impulse == 20 && cvar("sv_cheats")) + { + makevectors (self.v_angle); + CreateDummy (self.origin + self.view_ofs + v_forward * 64, 8); + } + else if (self.impulse == 21 && cvar("sv_cheats")) + { + makevectors (self.v_angle); + CreateDummy (self.origin + self.view_ofs + v_forward * 64, 9); + } + else if (self.impulse == 22 && cvar("sv_cheats")) + { + makevectors (self.v_angle); + CreateDummy (self.origin + self.view_ofs + v_forward * 64, 10); + } + else if (self.impulse == 23 && cvar("sv_cheats")) + { + makevectors (self.v_angle); + CreateDummy (self.origin + self.view_ofs + v_forward * 64, 11); + } + else if (self.impulse == 24 && cvar("sv_cheats")) + { + makevectors (self.v_angle); + CreateDummy (self.origin + self.view_ofs + v_forward * 64, 12); + } + else if (self.impulse == 25 && cvar("sv_cheats")) + { + makevectors (self.v_angle); + CreateDummy (self.origin + self.view_ofs + v_forward * 64, 13); + } + else if (self.impulse == 26) + { + setmodel (self, "models/player/marine.zym"); + setsize (self, PL_MIN, PL_MAX); + } + else if (self.impulse == 27) + { + setmodel (self, "models/player/grunt.zym"); + setsize (self, PL_MIN, PL_MAX); + } + else if (self.impulse == 28) + { + setmodel (self, "models/player/specop.zym"); + setsize (self, PL_MIN, PL_MAX); + } + else if (self.impulse == 29) + { + setmodel (self, "models/player/pyria.zym"); + setsize (self, PL_MIN, PL_MAX); + } + else if (self.impulse == 30) + { + setmodel (self, "models/player/lurk.zym"); + setsize (self, PL_MIN, PL_MAX); + } + else if (self.impulse == 31) + { + setmodel (self, "models/player/headhunter.zym"); + setsize (self, PL_MIN, PL_MAX); + } + else if (self.impulse == 32) + { + setmodel (self, "models/player/visitant.zym"); + setsize (self, PL_MIN, PL_MAX); + } + else if (self.impulse == 33) + { + setmodel (self, "models/player/jeandarc.zym"); + setsize (self, PL_MIN, PL_MAX); + } + else if (self.impulse == 34) + { + setmodel (self, "models/player/mulder.zym"); + setsize (self, PL_MIN, PL_MAX); + } + else if (self.impulse == 35) + { + setmodel (self, "models/player/insurrectionist.zym"); + setsize (self, PL_MIN, PL_MAX); + } + else if (self.impulse == 36) + { + setmodel (self, "models/player/robot.zym"); + setsize (self, PL_MIN, PL_MAX); + } + else if (self.impulse == 37) + { + setmodel (self, "models/player/lycanthrope.zym"); + setsize (self, PL_MIN, PL_MAX); + } + else if (self.impulse == 38) + { + setmodel (self, "models/player/fshock.zym"); + setsize (self, PL_MIN, PL_MAX); + } + else if (self.impulse == 97 && !self.crouch) + { + self.crouch = TRUE; + self.view_ofs_z = self.view_ofs_z - 20; + } + else if (self.impulse == 98 && self.crouch) { + self.crouch = FALSE; + self.view_ofs_z = self.view_ofs_z + 20; + } + else if (self.impulse == 99) + { + self.items = IT_LASER | IT_UZI | IT_SHOTGUN | IT_GRENADE_LAUNCHER | IT_ELECTRO | IT_CRYLINK | IT_NEX | IT_HAGAR | IT_ROCKET_LAUNCHER; + self.ammo_shells = 100; + self.ammo_nails = 100; + self.ammo_rockets = 100; + self.ammo_cells = 100; + } + TetrisImpulses(); + self.impulse = 0; +} \ No newline at end of file diff --git a/qcsrc/gamec/cl_physics.c b/qcsrc/gamec/cl_physics.c new file mode 100644 index 000000000..f50a8799d --- /dev/null +++ b/qcsrc/gamec/cl_physics.c @@ -0,0 +1,192 @@ +float lastclientthink, sv_maxspeed, sv_friction, sv_accelerate, sv_stopspeed; +float sv_edgefriction, cl_rollspeed, cl_divspeed, cl_rollangle; +.float ladder_time; +.entity ladder_entity; + +void SV_PlayerPhysics() +{ + local vector wishvel, wishdir, v; + local float wishspeed, f; + + if (self.movetype == MOVETYPE_NONE) + return; + + if (self.punchangle != '0 0 0') + { + f = vlen(self.punchangle) - 10 * frametime; + if (f > 0) + self.punchangle = normalize(self.punchangle) * f; + else + self.punchangle = '0 0 0'; + } + + // if dead, behave differently + if (self.health <= 0) + return; + + if (time != lastclientthink) + { + lastclientthink = time; + sv_maxspeed = cvar("sv_maxspeed"); + sv_friction = cvar("sv_friction"); + sv_accelerate = cvar("sv_accelerate"); + sv_stopspeed = cvar("sv_stopspeed"); + sv_edgefriction = cvar("edgefriction"); + // LordHavoc: this * 4 is an optimization + cl_rollangle = cvar("cl_rollangle") * 4; + // LordHavoc: this 1 / is an optimization + cl_divspeed = 1 / cvar("cl_rollspeed"); + } + + // show 1/3 the pitch angle and all the roll angle + self.angles_x = 0; + self.angles_y = self.v_angle_y; // FIXME: rotate the models, not the entity! + self.angles_z = bound(-1, self.velocity * v_right * cl_divspeed, 1) * cl_rollangle; + self.angles = self.angles + self.angleoffset; +/* if (!self.fixangle) + { + self.angles_x = (self.v_angle_x + self.punchangle_x) * -0.333; + self.angles_y = self.v_angle_y + self.punchangle_y; + }*/ + + if (self.flags & FL_WATERJUMP ) + { + self.velocity_x = self.movedir_x; + self.velocity_y = self.movedir_y; + if (time > self.teleport_time || self.waterlevel == 0) + { + self.flags = self.flags - (self.flags & FL_WATERJUMP); + self.teleport_time = 0; + } + return; + } + + makevectors(self.v_angle); + + if (self.movetype == MOVETYPE_NOCLIP) + { + // noclip + self.velocity = v_forward * self.movement_x + v_right * self.movement_y + '0 0 1' * self.movement_z; + return; + } + + if (self.waterlevel >= 2) + { + // swimming + // friction + self.velocity = self.velocity * (1 - frametime * sv_friction); + wishvel = v_forward * self.movement_x + v_right * self.movement_y + '0 0 1' * self.movement_z; + wishdir = normalize(wishvel); + wishspeed = vlen(wishvel); + if (wishspeed > sv_maxspeed) + wishspeed = sv_maxspeed; + if (self.items & IT_SPEED) + wishspeed = wishspeed * POWERUP_SPEED_MOVEMENT; + if (self.crouch) + wishspeed = wishspeed * 0.5; + wishspeed = wishspeed * 0.6; + // acceleration + f = wishspeed - (self.velocity * wishdir); + if (f > 0) + self.velocity = self.velocity + wishdir * min(f, sv_accelerate * frametime * wishspeed); + return; + } + + if (time < self.ladder_time) + { + // on a func_ladder or swimming in func_water + wishvel = v_forward * self.movement_x + v_right * self.movement_y + '0 0 1' * self.movement_z; + wishdir = normalize(wishvel); + wishspeed = vlen(wishvel); + if (wishspeed > sv_maxspeed) + wishspeed = sv_maxspeed; + if (self.items & IT_SPEED) + wishspeed = wishspeed * POWERUP_SPEED_MOVEMENT; + if (self.crouch) + wishspeed = wishspeed * 0.5; + if (self.ladder_entity.classname == "func_water") + { + if (wishspeed > self.ladder_entity.speed) + wishspeed = self.ladder_entity.speed; + self.watertype = self.ladder_entity.skin; + f = self.ladder_entity.origin_z + self.ladder_entity.maxs_z; + if ((self.origin_z + self.view_ofs_z) < f) + self.waterlevel = 3; + else if ((self.origin_z + (self.mins_z + self.maxs_z) * 0.5) < f) + self.waterlevel = 2; + else if ((self.origin_z + self.mins_z + 1) < f) + self.waterlevel = 1; + else + { + self.waterlevel = 0; + self.watertype = CONTENT_EMPTY; + } + } + // friction + self.velocity = self.velocity * (1 - frametime * sv_friction); + // acceleration + f = wishspeed - (self.velocity * wishdir); + if (f > 0) + self.velocity = self.velocity + wishdir * min(f, sv_accelerate * frametime * wishspeed); + return; + } + + // calculate wishvel/wishdir/wishspeed for normal walking + makevectors(self.v_angle_y * '0 1 0'); + // hack to not let you back into teleporter + if (time < self.teleport_time && self.movement_x < 0) + wishvel = v_right * self.movement_y; + else + wishvel = v_forward * self.movement_x + v_right * self.movement_y; + wishdir = normalize(wishvel); + wishspeed = vlen(wishvel); + if (wishspeed > sv_maxspeed) + wishspeed = sv_maxspeed; + if (self.items & IT_SPEED) + wishspeed = wishspeed * POWERUP_SPEED_MOVEMENT; + if (self.crouch) + wishspeed = wishspeed * 0.5; + + if (self.flags & FL_ONGROUND) // walking + { + // friction + if (self.velocity_x || self.velocity_y) + { + v = self.velocity; + v_z = 0; + f = vlen(v); + + // if the leading edge is over a dropoff, increase friction + v = self.origin + normalize(v) * 16 + '0 0 1' * self.mins_z; + + traceline(v, v + '0 0 -34', TRUE, self); + + // apply friction + if (trace_fraction == 1.0) + { + if (f < sv_stopspeed) + f = 1 - frametime * (sv_stopspeed / f) * sv_friction * sv_edgefriction; + else + f = 1 - frametime * sv_friction * sv_edgefriction; + } + else + { + if (f < sv_stopspeed) + f = 1 - frametime * (sv_stopspeed / f) * sv_friction; + else + f = 1 - frametime * sv_friction; + } + + if (f < 0) + self.velocity = '0 0 0'; + else + self.velocity = self.velocity * f; + } + } + else if (!(game & GAME_NO_AIR_CONTROL)) + wishspeed = wishspeed * 0.25; + //if (wishspeed > 50) + // wishspeed = 50; + + self.velocity = self.velocity + wishdir * sv_accelerate * frametime * wishspeed; +} diff --git a/qcsrc/gamec/cl_player.c b/qcsrc/gamec/cl_player.c new file mode 100644 index 000000000..489bee02b --- /dev/null +++ b/qcsrc/gamec/cl_player.c @@ -0,0 +1,254 @@ +$frame die1 die2 draw duck duckwalk duckjump duckidle idle +$frame jump pain1 pain2 shoot taunt run runbackwards +$frame strafeleft straferight dead1 dead2 forwardright +$frame forwardleft backright backleft + +// changes by LordHavoc on 03/29/04 and 03/30/04 at Vermeulen's request +// merged player_run and player_stand to player_anim +// added death animations to player_anim +// can now spawn thrown weapons from anywhere, not just from players +// thrown weapons now fade out after 20 seconds +// created PlayerGib function +// PlayerDie no longer uses hitloc or damage +// PlayerDie now supports dying animations as well as gibbing +// cleaned up PlayerDie a lot +// added CopyBody + +void CopyBody() +{ + local entity oldself; + if (self.effects & EF_NODRAW) + return; + oldself = self; + self = spawn(); + self.angles = oldself.angles; + self.avelocity = oldself.avelocity; + self.classname = "body"; + self.damageforcescale = oldself.damageforcescale; + self.effects = oldself.effects; + self.event_damage = oldself.event_damage; + self.frame = oldself.frame; + self.health = oldself.health; + self.model = oldself.model; + self.modelindex = oldself.modelindex; + self.movetype = oldself.movetype; + self.nextthink = oldself.nextthink; + self.norespawn = TRUE; + self.skin = oldself.skin; + self.solid = oldself.solid; + self.takedamage = oldself.takedamage; + self.think = oldself.think; + self.velocity = oldself.velocity; + self.weapon = oldself.weapon; + setorigin(self, oldself.origin); + setsize(self, '-16 -16 -24', '16 16 5'); + self = oldself; +} + +void player_anim (void) +{ + if (self.deadflag != DEAD_NO) + { + if (time > self.dead_time) + self.frame = self.dead_frame; + else + self.frame = self.die_frame; + return; + } + + + if (self.crouch) + { + if (self.movement_x * self.movement_x + self.movement_y * self.movement_y > 20) + self.frame = $duckwalk; + else + self.frame = $duckidle; + } + else if ((self.movement_x * self.movement_x + self.movement_y * self.movement_y) > 20) + { + if (self.movement_x > 0 && self.movement_y == 0) + self.frame = $run; + else if (self.movement_x < 0 && self.movement_y == 0) + self.frame = $runbackwards; + else if (self.movement_x == 0 && self.movement_y > 0) + self.frame = $straferight; + else if (self.movement_x == 0 && self.movement_y < 0) + self.frame = $strafeleft; + else if (self.movement_x > 0 && self.movement_y > 0) + self.frame = $forwardright; + else if (self.movement_x > 0 && self.movement_y < 0) + self.frame = $forwardleft; + else if (self.movement_x < 0 && self.movement_y > 0) + self.frame = $backright; + else if (self.movement_x < 0 && self.movement_y < 0) + self.frame = $backleft; + else + self.frame = $run; + } + else if (self.pain_finished > time) + self.frame = self.pain_frame; + else if (self.attack_finished > time) + self.frame = $shoot; + else + self.frame = $idle; + + if (!(self.flags & FL_ONGROUND)) + self.frame = $idle; +} +//End change by Supajoe on 11:44 PM EST 11/16/03 (Subject: Player animations) + +void SpawnThrownWeapon (vector org, float w) +{ + local entity oldself; + + if (game & (GAME_INSTAGIB | GAME_ROCKET_ARENA)) + return; + if (w == IT_LASER) + return; + + oldself = self; + self = spawn(); + + setorigin(self, org); + self.velocity = randomvec() * 100 + '0 0 200'; + self.norespawn = 1; + //SUB_SetFade(self, time + 20); + + if (w == IT_UZI) + weapon_uzi (); + else if (w == IT_SHOTGUN) + weapon_shotgun (); + else if (w == IT_GRENADE_LAUNCHER) + weapon_grenadelauncher (); + else if (w == IT_ELECTRO) + weapon_electro (); + else if (w == IT_CRYLINK) + weapon_crylink (); + else if (w == IT_NEX) + weapon_nex (); + else if (w == IT_HAGAR) + weapon_hagar (); + else //if (w == IT_ROCKET_LAUNCHER) + weapon_rocketlauncher (); + + self = oldself; +} + +void PlayerCorpseDamage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype) +{ + local float take, save; + te_blood (hitloc, '0 0 20', damage); + // damage resistance (ignore most of the damage from a bullet or similar) + damage = max(damage - 5, 1); + save = bound(0, damage * 0.6, self.armorvalue); + take = bound(0, damage - save, damage); + self.armorvalue = self.armorvalue - save; + self.health = self.health - take; + self.dmg_save = self.dmg_save + save; + self.dmg_take = self.dmg_take + take; + self.dmg_inflictor = inflictor; + if (self.health <= -50) + { + // don't use any animations as a gib + self.frame = 0; + self.dead_frame = 0; + self.die_frame = 0; + // view just above the floor + self.view_ofs = '0 0 4'; + // make a juicy mess + te_bloodshower (self.origin + self.mins, self.origin + self.maxs, 500, 2000); + // make a meaty mess + TossGib (world, "models/gibs/gib1.md3", self.origin, self.velocity); + TossGib (world, "models/gibs/gib2.mdl", self.origin, self.velocity); + TossGib (world, "models/gibs/gib3.mdl", self.origin, self.velocity); + TossGib (world, "models/gibs/gib4.mdl", self.origin, self.velocity); + TossGib (world, "models/gibs/bloodyskull.md3", self.origin, self.velocity); + TossGib (self, "models/gibs/eye.md3", self.origin, self.velocity); + sound (trace_ent, CHAN_VOICE, "misc/gib.wav", 1, ATTN_NORM); + } +} + +void PlayerDamage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype) +{ + local float take, save; + if (attacker == self) + if (game & GAME_NO_SELF_DAMAGE) + return; + + te_blood (hitloc, '0 0 20', damage); + if (self.pain_finished < time) //Don't switch pain sequences like crazy + { + if (random() > 0.5) + self.pain_frame = $pain1; + else + self.pain_frame = $pain2; + self.pain_finished = time + 0.5; //Supajoe + } + + if (game & GAME_INSTAGIB) + damage = self.health + 75; + + save = bound(0, damage * 0.6, self.armorvalue); + take = bound(0, damage - save, damage); + self.armorvalue = self.armorvalue - save; + self.health = self.health - take; + self.dmg_save = self.dmg_save + save; + self.dmg_take = self.dmg_take + take; + self.dmg_inflictor = inflictor; + if (self.health <= 0) + { + // print an obituary message + Obituary (attacker, self, deathtype); + // make the corpse upright (not tilted) + self.angles_x = 0; + self.angles_z = 0; + // don't spin + self.avelocity = '0 0 0'; + // no weapon when dead + self.weaponmodel = ""; + // view from the floor + self.view_ofs = '0 0 -8'; + // toss the corpse + self.movetype = MOVETYPE_TOSS; + // shootable corpse + self.solid = SOLID_CORPSE; + // don't stick to the floor + self.flags = self.flags - (self.flags & FL_ONGROUND); + // dying animation + self.deadflag = DEAD_DYING; + // when to allow respawn + self.death_time = time + 0.5; + // when to switch to the dead_frame + self.dead_time = time + 1.5; + if (random() < 0.5) + { + self.die_frame = $die1; + self.dead_frame = $dead1; + } + else + { + self.die_frame = $die2; + self.dead_frame = $dead2; + } + // start the animation + player_anim(); + // throw a weapon + SpawnThrownWeapon (self.origin + (self.mins + self.maxs) * 0.5, self.weapon); + // set damage function to corpse damage + self.event_damage = PlayerCorpseDamage; + // call the corpse damage function just in case it wants to gib + self.event_damage(hitloc, 0, inflictor, attacker, deathtype); + // set up to fade out later + SUB_SetFade (self, time + 12 + random () * 4); + // Sajt - added this, but I'm not sure the powerups are even implemented? This might act strange because + // there is no code handled to disable powerups when their time is up... + if (game & GAME_STRENGTH_GAIN) + { + if (attacker.strength_finished < time) + attacker.strength_finished = time; + attacker.strength_finished = attacker.strength_finished + 5; + attacker.items = attacker.items | IT_STRENGTH; + } + } +} + diff --git a/qcsrc/gamec/cl_weaponanimations.c b/qcsrc/gamec/cl_weaponanimations.c new file mode 100644 index 000000000..0e5898b42 --- /dev/null +++ b/qcsrc/gamec/cl_weaponanimations.c @@ -0,0 +1,25 @@ +$frame fire fire2 idle run + +// VorteX: static frame globals +float WFRAME_FIRE1 = 0; +float WFRAME_FIRE2 = 1; +float WFRAME_IDLE = 2; +float WFRAME_RUN = 3; + +// changes by LordHavoc on 03/30/04 +// cleaned up code formatting a bit +// renamed to weapon_anim +void weapon_anim () +{ + if (self.attack_finished > time) + self.weaponframe = $fire; + else if (self.movement_x || self.movement_y) + self.weaponframe = $run; + else + self.weaponframe = $idle; +} +void weapon_freeze () +{ + if (self.weaponentity) + self.weaponentity.frame = WFRAME_IDLE; +} \ No newline at end of file diff --git a/qcsrc/gamec/cl_weapons.c b/qcsrc/gamec/cl_weapons.c new file mode 100644 index 000000000..86541c793 --- /dev/null +++ b/qcsrc/gamec/cl_weapons.c @@ -0,0 +1,175 @@ +// generic weapons table +// add new weapons here +void(float wpn, float wrequest) weapon_action = +{ + if (wpn == WEP_LASER) + w_laser(wrequest); + else if (wpn == WEP_SHOTGUN) + w_shotgun(wrequest); + else if (wpn == WEP_UZI) + w_uzi(wrequest); + else if (wpn == WEP_GRENADE_LAUNCHER) + w_glauncher(wrequest); + else if (wpn == WEP_ELECTRO) + w_electro(wrequest); + else if (wpn == WEP_CRYLINK) + w_crylink(wrequest); + else if (wpn == WEP_NEX) + w_nex(wrequest); + else if (wpn == WEP_HAGAR) + w_hagar(wrequest); + else if (wpn == WEP_ROCKET_LAUNCHER) + w_rlauncher(wrequest); +}; + +// switch between weapons +void(float imp) W_SwitchWeapon +{ + weapon_hasammo = TRUE; + if (!client_hasweapon(self, imp, TRUE)) + { + if (!weapon_hasammo) + sprint(self, "You don't have any ammo for that weapon\n"); + else + sprint(self, "You don't own that weapon\n"); + } + else + self.switchweapon = imp; +}; + +// next weapon +void() W_NextWeapon = +{ + local float weaponwant; + + weaponwant = self.weapon + 1; + weapon_hasammo = TRUE; + while(!client_hasweapon(self, weaponwant, TRUE)) + { + weaponwant = weaponwant + 1; + if (weaponwant > WEP_LAST) + weaponwant = WEP_FIRST; + } + self.switchweapon = weaponwant; +}; + +// prev weapon +void() W_PreviousWeapon = +{ + local float weaponwant; + + weaponwant = self.weapon - 1; + weapon_hasammo = TRUE; + while(!client_hasweapon(self, weaponwant, TRUE)) + { + weaponwant = weaponwant - 1; + if (weaponwant < WEP_FIRST) + weaponwant = WEP_LAST; + } + self.switchweapon = weaponwant; +}; + +// Bringed back weapon frame +float JUMPBOUND_STARTSPEED = 120; +float JUMPBOUND_MAXAMP = 6; +float JUMPBOUND_MINAMP = 0.5; +void() W_WeaponFrame = +{ + if (!self.weaponentity || self.health <= 0) + return; // Dead player can't use weapons and injure impulse commands + + // Change weapon + if (self.switchweapon > 0) + { + if (self.weapon == self.switchweapon) + self.switchweapon = FALSE; // same weapon + else + { + if (self.weaponentity.state == WS_CLEAR) + { + self.weaponentity.state = WS_RAISE; + weapon_action(self.switchweapon, WR_SETUP); + // VorteX: add player model weapon select frame here + // setcustomframe(PlayerWeaponRaise); + weapon_action(self.weapon, WR_UPDATECOUNTS); + weapon_action(self.weapon, WR_RAISE); + self.switchweapon = FALSE; + } + else if (self.weaponentity.state == WS_READY) + { + self.weaponentity.state = WS_DROP; + // VorteX: add player model weapon deselect frame here + // setcustomframe(PlayerWeaponDrop); + weapon_action(self.weapon, WR_DROP); + } + } + } + + if (self.button0) + weapon_action(self.weapon, WR_FIRE1); + if (self.button3) + weapon_action(self.weapon, WR_FIRE2); + if (self.button4) + weapon_action(self.weapon, WR_FIRE3); + + // do weapon think + if (time >= self.weapon_nextthink) + if (self.weapon_nextthink > 0) + self.weapon_think(); + + // weapon bobbing and script actions + local float bobintensity, q1pitching, framespeed, diff; + local vector vel, realorg, layer1, layer2, boblayer; + + bobintensity = cvar("cl_weapon_bobintensity"); // weapon bob intensity + q1pitching = fabs(cvar("cl_weapon_q1pitching")); // q1 style of "bob" when looking up and down + + realorg = self.weaponentity.origin + self.weaponentity.view_ofs; + realorg = realorg - self.weaponentity.finaldest; // finaldest is last bob position + + // VorteX: actually this is needed for weapon screen offset + if (q1pitching) + { + self.weaponentity.view_ofs_x = q1pitching*bound(-5.5, self.v_angle_x/45, 5.5); + self.weaponentity.view_ofs_z = q1pitching*bound(-1.5, self.v_angle_x/60, 1.5); + } + + // weapon origin interpolation, layer 1 + if (realorg != self.weaponentity.pos1) + { + framespeed = frametime*self.weaponentity.lip*10; // lip is speed of origin changing (of layer1) + diff = vlen(realorg - self.weaponentity.pos1); + // VorteX: add speed modifier (haste)? + layer1 = frametime*10*self.weaponentity.lip*normalize(self.weaponentity.pos1 - realorg); + if (diff <= vlen(layer1)) + layer1 = normalize(self.weaponentity.pos1 - realorg)*diff; + } + + // weapon bobbing (q3-style) + if (self.flags & FL_ONGROUND && self.waterlevel < 2) + { + // VorteX: only xy velocity matters + vel_x = self.velocity_x; + vel_y = self.velocity_y; + framespeed = vlen(vel); + // Y axis + diff = bobintensity*framespeed/300; + self.weaponentity.destvec_y = self.weaponentity.destvec_y + frametime*10; + boblayer_y = diff*cos(self.weaponentity.destvec_y + 90); + // Z axis + diff = bobintensity*framespeed/540; + self.weaponentity.destvec_z = self.weaponentity.destvec_z + frametime*20; + boblayer_z = diff*cos(self.weaponentity.destvec_z); + self.weaponentity.finaldest = boblayer; + } + else if (self.waterlevel > 0) + {// swim, all velocity matters + // X axis + framespeed = vlen(self.velocity); + diff = bobintensity*framespeed/100; + self.weaponentity.destvec_x = self.weaponentity.destvec_x + frametime*6; + boblayer_x = diff*cos(self.weaponentity.destvec_x); + self.weaponentity.finaldest = boblayer; + } + self.weaponentity.origin = realorg + boblayer + layer1 - self.weaponentity.view_ofs; +}; \ No newline at end of file diff --git a/qcsrc/gamec/cl_weaponsystem.c b/qcsrc/gamec/cl_weaponsystem.c new file mode 100644 index 000000000..42200b279 --- /dev/null +++ b/qcsrc/gamec/cl_weaponsystem.c @@ -0,0 +1,251 @@ +/* +=========================================================================== + + CLIENT WEAPONSYSTEM CODE + Bring back W_Weaponframe + +=========================================================================== +*/ +// definitions (move this part to defs after finishing of weapon system) +.entity weaponentity; +.float switchweapon; +void(float wpn, float wrequest) weapon_action; +void() w_clear; +// VorteX: standalone think for weapons, so normal think on weaponentity can be reserved by weaponflashes which needs update even player dies) +.float weapon_nextthink; +.void() weapon_think; +.vector shotdir, shotorg; +float weapon_hasammo; // sets by WR_CHECKAMMO request + +// weapon states (self.weaponentity.state) +float WS_CLEAR = 0; // no weapon selected +float WS_RAISE = 1; // raise frame +float WS_DROP = 2; // deselecting frame +float WS_INUSE = 3; // fire state +float WS_READY = 4; // idle frame + +// weapon requests +float WR_SETUP = 1; // setup weapon data +float WR_UPDATECOUNTS = 2; // update ammo display +float WR_IDLE = 3; // idle frame +float WR_DROP = 4; // deselect frame +float WR_RAISE = 5; // select frame +float WR_FIRE1 = 6; // primary fire frame +float WR_FIRE2 = 7; // secondary fire +float WR_FIRE3 = 8; // third fire +float WR_CHECKAMMO = 9; // checks ammo for weapon +float WR_CLEAR = 10; // runs afted deselecting frames, remove weapon parts (if presented). This useful for quake3-style chaingun + +// Weapon indexes +float WEP_LASER = 1; // float IT_LASER = 4096; +float WEP_SHOTGUN = 2; // float IT_SHOTGUN = 1; +float WEP_UZI = 3; // float IT_UZI = 2; +float WEP_GRENADE_LAUNCHER = 4; // float IT_GRENADE_LAUNCHER = 4; +float WEP_ELECTRO = 5; // float IT_ELECTRO = 8; +float WEP_CRYLINK = 6; // float IT_CRYLINK = 16; +float WEP_NEX = 7; // float IT_NEX = 32; +float WEP_HAGAR = 8; // float IT_HAGAR = 64; +float WEP_ROCKET_LAUNCHER = 9; // float IT_ROCKET_LAUNCHER = 128; + +// For weapon cycling commands +float WEP_FIRST = 1; +float WEP_LAST = 9; + +// spawning weaponentity for client +void() CL_SpawnWeaponentity = +{ + if (self.weaponentity) + { + w_clear(); + return; + } + self.weaponentity = spawn(); + self.weaponentity.solid = SOLID_NOT; + self.weaponentity.owner = self; + self.weaponentity.weaponentity = self.weaponentity; + setmodel(self.weaponentity, ""); + self.weaponentity.origin = '0 0 0'; + self.weaponentity.angles = '0 0 0'; + self.weaponentity.viewmodelforclient = self; + self.weaponentity.flags = 0; +}; + +// convertion from index (= impulse) to flag in .items +float(float index) weapon_translateindextoflag = +{ + if (index == WEP_LASER) + return IT_LASER; + else if (index == WEP_SHOTGUN) + return IT_SHOTGUN; + else if (index == WEP_UZI) + return IT_UZI; + else if (index == WEP_GRENADE_LAUNCHER) + return IT_GRENADE_LAUNCHER; + else if (index == WEP_ELECTRO) + return IT_ELECTRO; + else if (index == WEP_CRYLINK) + return IT_CRYLINK; + else if (index == WEP_NEX) + return IT_NEX; + else if (index == WEP_HAGAR) + return IT_HAGAR; + else if (index == WEP_ROCKET_LAUNCHER) + return IT_ROCKET_LAUNCHER; +}; + +float(entity cl, float wpn, float andammo) client_hasweapon = +{ + local float itemcode; + + itemcode = weapon_translateindextoflag(wpn); + if (cl.items & itemcode) + { + if (andammo) + { + weapon_action(wpn, WR_CHECKAMMO); + if (weapon_hasammo) + return TRUE; + return FALSE; + } + return TRUE; + } + return FALSE; +}; + +// Weapon subs +void() w_clear = +{ + weapon_action(self.weapon, WR_CLEAR); + if (self.weapon != -1) + self.weapon = 0; + self.weaponentity.state = WS_CLEAR; + setmodel(self.weaponentity, ""); + self.weaponentity.effects = 0; +}; + +void() w_ready = +{ + self.weaponentity.state = WS_READY; + weapon_action(self.weapon, WR_IDLE); +}; + +// FIXME: add qw-style client-custom weaponrating (cl_weaponrating)? +void() w_bestweapon +{// add new weapons here + weapon_hasammo = TRUE; + if (client_hasweapon(self, WEP_ROCKET_LAUNCHER, TRUE)) + self.switchweapon = WEP_ROCKET_LAUNCHER; + else if (client_hasweapon(self, WEP_NEX, TRUE)) + self.switchweapon = WEP_NEX; + else if (client_hasweapon(self, WEP_HAGAR, TRUE)) + self.switchweapon = WEP_HAGAR; + else if (client_hasweapon(self, WEP_GRENADE_LAUNCHER, TRUE)) + self.switchweapon = WEP_GRENADE_LAUNCHER; + else if (client_hasweapon(self, WEP_ELECTRO, TRUE)) + self.switchweapon = WEP_ELECTRO; + else if (client_hasweapon(self, WEP_CRYLINK, TRUE)) + self.switchweapon = WEP_CRYLINK; + else if (client_hasweapon(self, WEP_UZI, TRUE)) + self.switchweapon = WEP_UZI; + else if (client_hasweapon(self, WEP_SHOTGUN, TRUE)) + self.switchweapon = WEP_SHOTGUN; + else + self.switchweapon = WEP_LASER; +}; + +// Setup weapon for client (after this raise frame will be launched) +void(float windex, string wmodel, float hudammo) weapon_setup = +{ + local string wdir, weaponmdl; + + self.items = self.items - (self.items & (IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS)); + self.items = self.items | hudammo; + + self.weapon = windex; + + if (wmodel != "") + { + weaponmdl = strzone(strcat("models/weapons/", wmodel)); + setmodel(self.weaponentity, weaponmdl); + } + // VorteX: update visible weapon + // CL_ViswepUpdate(); +}; + +// shot direction +float WEAPON_MAXRELX = 14; // if more, shot can be spawned after wall surface (in empty worldspace) or inside other entity if client stands close to it +void(float x, float y, float z) weapon_shotdir = +{ + makevectors(self.v_angle); + self.shotorg = self.origin + self.view_ofs + v_forward*bound(0, x, WEAPON_MAXRELX) + v_right*(y + self.weaponentity.view_ofs_y) + v_up*z; + self.shotdir = aim(self, 1000); +}; + +// perform weapon to attack (weaponstate and attack_finished check is here) +void(float() checkfunc1, float() checkfunc2, void() firefunc, float atktime) weapon_prepareattack = +{ + // Change to best weapon if failed + if (!(game & GAME_INSTAGIB) && !(game & GAME_ROCKET_ARENA)) + { + if (!checkfunc1()) + { + if (!checkfunc2()) + w_bestweapon(); + return; + } + } + // Don't do shot if previos attack not finished + if (!(game & GAME_INSANE)) + if (time < self.attack_finished) + return; + // Can't do shot if changing weapon + if (!(game & GAME_INSANE)) + if (self.weaponentity.state != WS_READY) + return; + + self.attack_finished = time + atktime; + firefunc(); +}; + +// perform weapon attack +void(float() checkfunc1, float() checkfunc2, void() firefunc) weapon_doattack +{ + // Change to best weapon if failed + if (!(game & GAME_INSTAGIB) && !(game & GAME_ROCKET_ARENA)) + { + if (!checkfunc1()) + { + if (!checkfunc2()) + w_bestweapon(); + return; + } + } + self.weaponentity.state = WS_INUSE; + firefunc(); + weapon_action(self.weapon, WR_UPDATECOUNTS); // update ammo now +}; + +void(entity ent, float recoil) weapon_recoil = +{ + ent.punchangle = (randomvec() + '-1 0 0')*recoil; + ent.punchangle_z = 0; // don't want roll + if (recoil > 3) // push back if large recoil + ent.velocity = ent.velocity - normalize(ent.shotdir)*recoil*25; +}; + +void(float fr, float t, void() func) weapon_thinkf = +{ + if (fr >= 0) + if (self.weaponentity != world) + self.weaponentity.frame = fr; + // VorteX: haste can be added here + self.weapon_nextthink = time + t; + self.weapon_think = func; +}; + +void(float spd, vector org) weapon_boblayer1 = +{ + // VorteX: haste can be added here + self.weaponentity.pos1 =org; + self.weaponentity.lip = spd; +}; diff --git a/qcsrc/gamec/constants.h b/qcsrc/gamec/constants.h new file mode 100644 index 000000000..a38f44553 --- /dev/null +++ b/qcsrc/gamec/constants.h @@ -0,0 +1,188 @@ + +float FALSE = 0; +float TRUE = 1; + +float FL_FLY = 1; +float FL_SWIM = 2; +float FL_CLIENT = 8; +float FL_INWATER = 16; +float FL_MONSTER = 32; +float FL_GODMODE = 64; +float FL_NOTARGET = 128; +float FL_ITEM = 256; +float FL_ONGROUND = 512; +float FL_PARTIALGROUND = 1024; +float FL_WATERJUMP = 2048; +float FL_JUMPRELEASED = 4096; +float FL_WEAPON = 8192; + +float MOVETYPE_NONE = 0; +float MOVETYPE_ANGLENOCLIP = 1; +float MOVETYPE_ANGLECLIP = 2; +float MOVETYPE_WALK = 3; +float MOVETYPE_STEP = 4; +float MOVETYPE_FLY = 5; +float MOVETYPE_TOSS = 6; +float MOVETYPE_PUSH = 7; +float MOVETYPE_NOCLIP = 8; +float MOVETYPE_FLYMISSILE = 9; +float MOVETYPE_BOUNCE = 10; +//float MOVETYPE_BOUNCEMISSILE = 11; // Like bounce but doesn't lose speed on bouncing +//float MOVETYPE_FOLLOW = 12; // 'Attaches' the entity to its aim_ent + +float SOLID_NOT = 0; +float SOLID_TRIGGER = 1; +float SOLID_BBOX = 2; +float SOLID_SLIDEBOX = 3; +float SOLID_BSP = 4; +//float SOLID_CORPSE = 5; // Unobstructed by CORPSE or SLIDEBOX + +float DEAD_NO = 0; +float DEAD_DYING = 1; +float DEAD_DEAD = 2; +float DEAD_RESPAWNABLE = 3; + +float DAMAGE_NO = 0; +float DAMAGE_YES = 1; +float DAMAGE_AIM = 2; + +float CONTENT_EMPTY = -1; +float CONTENT_SOLID = -2; +float CONTENT_WATER = -3; +float CONTENT_SLIME = -4; +float CONTENT_LAVA = -5; +float CONTENT_SKY = -6; + +float SVC_BAD = 0; +float SVC_NOP = 1; +float SVC_DISCONNECT = 2; +float SVC_UPDATESTAT = 3; +float SVC_VERSION = 4; +float SVC_SETVIEW = 5; +float SVC_SOUND = 6; +float SVC_TIME = 7; +float SVC_PRINT = 8; +float SVC_STUFFTEXT = 9; +float SVC_SETANGLE = 10; +float SVC_SERVERINFO = 11; +float SVC_LIGHTSTYLE = 12; +float SVC_UPDATENAME = 13; +float SVC_UPDATEFRAGS = 14; +float SVC_CLIENTDATA = 15; +float SVC_STOPSOUND = 16; +float SVC_UPDATECOLORS = 17; +float SVC_PARTICLE = 18; +float SVC_DAMAGE = 19; +float SVC_SPAWNSTATIC = 20; +float SVC_SPAWNBINARY = 21; +float SVC_SPAWNBASELINE = 22; +float SVC_TEMPENTITY = 23; +float SVC_SETPAUSE = 24; +float SVC_SIGNONNUM = 25; +float SVC_CENTERPRINT = 26; +float SVC_KILLEDMONSTER = 27; +float SVC_FOUNDSECRET = 28; +float SVC_SPAWNSTATICSOUND = 29; +float SVC_INTERMISSION = 30; +float SVC_FINALE = 31; +float SVC_CDTRACK = 32; +float SVC_SELLSCREEN = 33; +float SVC_CUTSCENE = 34; + +float TE_SPIKE = 0; +float TE_SUPERSPIKE = 1; +float TE_GUNSHOT = 2; +float TE_EXPLOSION = 3; +float TE_TAREXPLOSION = 4; +float TE_LIGHTNING1 = 5; +float TE_LIGHTNING2 = 6; +float TE_WIZSPIKE = 7; +float TE_KNIGHTSPIKE = 8; +float TE_LIGHTNING3 = 9; +float TE_LAVASPLASH = 10; +float TE_TELEPORT = 11; + +float CHAN_AUTO = 0; +float CHAN_WEAPON = 1; +float CHAN_VOICE = 2; +float CHAN_ITEM = 3; +float CHAN_BODY = 4; +float CHAN_IMPACT = 5; + +float ATTN_NONE = 0; +float ATTN_NORM = 1; +float ATTN_IDLE = 2; +float ATTN_STATIC = 3; + +float UPDATE_GENERAL = 0; +float UPDATE_STATIC = 1; +float UPDATE_BINARY = 2; +float UPDATE_TEMP = 3; + +float EF_BRIGHTFIELD = 1; +float EF_MUZZLEFLASH = 2; +float EF_BRIGHTLIGHT = 4; +float EF_DIMLIGHT = 8; + +float MSG_BROADCAST = 0; +float MSG_ONE = 1; +float MSG_ALL = 2; +float MSG_INIT = 3; + +float IT_LASER = 4096; +float IT_SHOTGUN = 1; +float IT_UZI = 2; +float IT_GRENADE_LAUNCHER = 4; +float IT_ELECTRO = 8; +float IT_CRYLINK = 16; +float IT_NEX = 32; +float IT_HAGAR = 64; +float IT_ROCKET_LAUNCHER = 128; + +float IT_SHELLS = 256; +float IT_NAILS = 512; +float IT_ROCKETS = 1024; +float IT_CELLS = 2048; + +float IT_STRENGTH = 8192; +float IT_INVINCIBLE = 16384; +float IT_SPEED = 32768; +float IT_SLOWMO = 65536; + +vector PL_VIEW_OFS = '0 0 35'; +vector PL_MIN = '-16 -16 -24'; +vector PL_MAX = '16 16 45'; + +//vector PL_VIEW_OFS = '0 0 32'; +//vector PL_MIN = '-16 -16 -48'; +//vector PL_MAX = '16 16 48'; + + +// Sajt - added these, just as constants. Not sure how you want them actually put in the game, but I just +// did this so at least they worked +// NOTE: instagib IS NOT compatible with rocket-arena, so make sure to prevent selecting both in a menu +float GAME_INSTAGIB = 1; /// everyone gets the nex gun with infinite ammo, and one shot kills +float GAME_INSANE = 2; /// no time between shots for any gun +float GAME_STRENGTH_GAIN = 4; /// NOT DONE +float GAME_REGENERATION = 8; /// Fast health regeneration +float GAME_ROCKET_ARENA = 16; /// Everyone starts with a rocket launcher +float GAME_NO_SELF_DAMAGE = 32; /// no self damage, so rocket jumping and such can be used a lot more +float GAME_NO_AIR_CONTROL = 64; /// turns off air control +float GAME_LOW_GRAVITY = 128; /// has 1/4 of the gravity +float GAME_FULLBRIGHT_PLAYERS = 256; /// makes the players model fullbright + +float game; // set to "gamecfg" on worldspawn + + + +float POWERUP_SPEED_MOVEMENT = 3; // movement multiplier for speed powerup +float POWERUP_SPEED_JUMPVELOCITY = 640; // how much jump velocity with speed powerup +float JUMP_VELOCITY = 300; // normal jump velocity + +float POWERUP_STRENGTH_DAMAGE = 2; // damage multiplier for strength powerup +float POWERUP_STRENGTH_FORCE = 4; // force multiplier for strength powerup + +float POWERUP_INVINCIBLE_TAKEDAMAGE = 0.2; // received damage multiplier for invincible powerup + +float POWERUP_SLOWMO_MOVEMENT = 0.3; // movement speed multiplier for slowmo powerup + diff --git a/qcsrc/gamec/defs.h b/qcsrc/gamec/defs.h new file mode 100644 index 000000000..dee82f425 --- /dev/null +++ b/qcsrc/gamec/defs.h @@ -0,0 +1,74 @@ + +// Globals + +entity activator; +string string_null; +entity casing; +entity dest; + +// Fields + +.void(vector hitloc, float damage, entity inflictor, entity attacker, float deathtype) event_damage; + +.string wad; +.string map; + +.float worldtype; +.float delay; +.float wait; +.float lip; +.float light_lev; +.float speed; +.float style; +.float skill; + +.string killtarget; + +.vector pos1, pos2; +.vector mangle; + +.float jump_flag; // storing velocity_z for falling damage +.float attack_finished; +.float pain_finished //Added by Supajoe +.float pain_frame //" +.float statdraintime; // record the one-second intervals between draining health and armour when they're over 100 +.float crouch; // Crouching or not? +.float hasaliases; // Has aliases (like +crouch) binded or not? + +.float strength_finished; +.float speed_finished; +.float invincible_finished; +.float slowmo_finished; + +.vector finaldest, finalangle; //plat.qc stuff +.void() think1; +.float state; +.float t_length, t_width; + +.vector destvec; // for rain +.float cnt; // for rain +.float count; +.float cnt2; + +.float death_time; +.float dead_time; +.float dead_frame; +.float die_frame; +.float fade_time; + +.string mdl; + +.float norespawn; +.float respawntime; +.float chasecam; + +.float electrocount; +.float crylinkcount; + +.float damageforcescale; + +.float gravity; + +.float dmg; + +.vector angleoffset; // for incorrectly player models diff --git a/qcsrc/gamec/extensions.h b/qcsrc/gamec/extensions.h new file mode 100644 index 000000000..86ff0b0b6 --- /dev/null +++ b/qcsrc/gamec/extensions.h @@ -0,0 +1,907 @@ + +//DarkPlaces supported extension list, draft version 1.04 + +//checkextension function +//idea: expected by almost everyone +//darkplaces implementation: LordHavoc +float(string s) checkextension = #99; +//description: +//check if (cvar("pr_checkextension")) before calling this, this is the only +//guarenteed extension to be present in the extension system, it allows you +//to check if an extension is available, by name, to check for an extension +//use code like this: +//// (it is recommended this code be placed in worldspawn or a worldspawn called function somewhere) +//if (cvar("pr_checkextension")) +//if (checkextension("DP_SV_SETCOLOR")) +// ext_setcolor = TRUE; +//from then on you can check ext_setcolor to know if that extension is available + +//DP_QC_TRACE_MOVETYPES +//idea: LordHavoc +//darkplaces implementation: id Software +//constant definitions: +float MOVE_NORMAL = 0; // same as FALSE +float MOVE_NOMONSTERS = 1; // same as TRUE +float MOVE_MISSILE = 2; // save as movement with .movetype == MOVETYPE_FLYMISSILE +//description: +//this extension does nothing (do not check for it), it only documents existing MOVE_ values that were never defined in defs.qc, these are passed as the 'nomonsters' parameter to traceline/tracebox + +//DP_QC_TRACE_MOVETYPE_WORLDONLY +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//constant definitions: +float MOVE_WORLDONLY = 3; +//description: +//allows traces to hit only world (see DP_QC_TRACE_MOVETYPES for how to use this) + +//DP_QC_TRACE_MOVETYPE_HITMODEL +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//constant definitions: +float MOVE_HITMODEL = 4; +//description: +//allows traces to hit alias models (not sprites!) instead of entity boxes (see DP_QC_TRACE_MOVETYPES for how to use this) + +// LordHavoc: HIGHLY experimental, do not implement this in other engines +//DP_CGAME +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//SVC definitions: +float svc_cgame = 50; // [short] length [bytes] data +//description: +//contains network messages to client gamecode. + +//DP_CL_LOADSKY +//idea: Nehahra, LordHavoc +//darkplaces implementation: LordHavoc +//client console commands: +//"loadsky" (parameters: "basename", example: "mtnsun_" would load "mtnsun_up.tga" and "mtnsun_rt.tga" and similar names, use "" to revert to quake sky, note: this is the same as Quake2 skybox naming) +//description: +//sets global skybox for the map for this client (can be stuffed to a client by QC), does not hurt much to repeatedly execute this command, please don't use this in mods if it can be avoided (only if changing skybox is REALLY needed, otherwise please use DP_GFX_SKYBOX). + +//DP_EF_ADDITIVE +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//effects bit: +float EF_ADDITIVE = 32; +//description: +//additive blending when this object is rendered + +//DP_EF_BLUE +//idea: id Software +//darkplaces implementation: LordHavoc +//effects bit: +float EF_BLUE = 64; +//description: +//entity emits blue light (used for quad) + +//DP_EF_FLAME +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//effects bit: +float EF_FLAME = 1024; +//description: +//entity is on fire + +//DP_EF_FULLBRIGHT +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//effects bit: +float EF_FULLBRIGHT = 512; +//description: +//entity is always brightly lit + +//DP_EF_NODRAW +//idea: id Software +//darkplaces implementation: LordHavoc +//effects bit: +float EF_NODRAW = 16; +//description: +//prevents server from sending entity to client (forced invisible, even if it would have been a light source or other such things) + +//DP_EF_RED +//idea: id Software +//darkplaces implementation: LordHavoc +//effects bit: +float EF_RED = 128; +//description: +//entity emits red light (used for invulnerability) + +//DP_EF_STARDUST +//idea: MythWorks Inc +//darkplaces implementation: LordHavoc +//effects bit: +float EF_STARDUST = 2048; +//description: +//entity emits bouncing sparkles in every direction + +//entity attributes used for rendering/networking: +//idea: Nehahra +//darkplaces implementation: LordHavoc +//DP_ENT_ALPHA +//field definition: +.float alpha; +//description: +//controls opacity of the entity, 0.0 is forced to be 1.0 (otherwise everything would be invisible), use -1 if you want to make something invisible, 1.0 is solid (like normal). + +/* +//NOTE: no longer supported by darkplaces because no one used it +//DP_ENT_COLORMOD // no longer supported +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//field definition: +.vector colormod; +//description: +//controls color of the entity, '0 0 0', is forced to be '1 1 1' (otherwise everything would be black), used for tinting objects, for instance using '1 0.6 0.4' on an ogre would give you an orange ogre (order is red green blue). +*/ + +//DP_ENT_CUSTOMCOLORMAP +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//description: +//if .colormap is set to 1024 + pants + shirt * 16, those colors will be used for colormapping the entity, rather than looking up a colormap by player number. + +/* +//NOTE: no longer supported by darkplaces because all entities are delta compressed now +//DP_ENT_DELTACOMPRESS // no longer supported +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//effects bit: +float EF_DELTA = 8388608; +//description: +//(obsolete) applies delta compression to the network updates of the entity, making updates smaller, this might cause some unreliable behavior in packet loss situations, so it should only be used on numerous (nails/plasma shots/etc) or unimportant objects (gibs/shell casings/bullet holes/etc). +*/ + +//DP_ENT_EXTERIORMODELTOCLIENT +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//fields: +.entity exteriormodeltoclient; +//description: +//the entity is visible to all clients with one exception: if the specified client is using first person view (not using chase_active) the entity will not be shown. + +//DP_ENT_GLOW +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//field definitions: +.float glow_color; +.float glow_size; +.float glow_trail; +//description: +//customizable glowing light effect on the entity, glow_color is a paletted (8bit) color in the range 0-255 (note: 0 and 254 are white), glow_size is 0 or higher (up to the engine what limit to cap it to, darkplaces imposes a 1020 limit), if glow_trail is true it will leave a trail of particles of the same color as the light. + +//DP_ENT_LOWPRECISION +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//effects bit: +float EF_LOWPRECISION = 4194304; +//description: +//uses low quality origin coordinates, reducing network traffic compared to the default high precision, intended for numerous objects (projectiles/gibs/bullet holes/etc). + +//DP_ENT_SCALE +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//field definitions: +.float scale; +//description: +//controls rendering scale of the object, 0 is forced to be 1, darkplaces uses 1/16th accuracy and a limit of 15.9375, can be used to make an object larger or smaller. + +//DP_ENT_VIEWMODEL +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//field definitions: +.entity viewmodelforclient; +//description: +//this is a very special capability, attachs the entity to the view of the client specified, origin and angles become relative to the view of that client, all effects can be used (multiple skins on a weapon model etc)... the entity is not visible to any other client. + +//DP_GFX_FOG +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//worldspawn fields: +//"fog" (parameters: "density red green blue", example: "0.1 0.3 0.3 0.3") +//description: +//global fog for the map, can not be changed by QC + +//DP_GFX_SKYBOX +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//worldspawn fields: +//"sky" (parameters: "basename", example: "mtnsun_" would load "mtnsun_up.tga" and "mtnsun_rt.tga" and similar names, note: "sky" is also used the same way by Quake2) +//description: +//global skybox for the map, can not be changed by QC + +//DP_GFX_EXTERNALTEXTURES +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//description: +//loads external textures found in various directories (tenebrae compatible)... +/* +in all examples .tga is merely the base texture, it can be any of these: +.tga (base texture) +_glow.tga (fullbrights or other glowing overlay stuff, NOTE: this is done using additive blend, not alpha) +_pants.tga (pants overlay for colormapping on models, this should be shades of grey (it is tinted by pants color) and black wherever the base texture is not black, as this is an additive blend) +_shirt.tga (same idea as pants, but for shirt color) +_diffuse.tga (this may be used instead of base texture for per pixel lighting) +_gloss.tga (specular texture for per pixel lighting, note this can be in color (tenebrae only supports greyscale)) +_norm.tga (normalmap texture for per pixel lighting) +_bump.tga (bumpmap, converted to normalmap at load time, supported only for reasons of tenebrae compatibility) +_luma.tga (same as _glow but supported only for reasons of tenebrae compatibility) + +due to glquake's incomplete Targa(r) loader, this section describes +required Targa(r) features support: +types: +type 1 (uncompressed 8bit paletted with 24bit/32bit palette) +type 2 (uncompressed 24bit/32bit true color, glquake supported this) +type 3 (uncompressed 8bit greyscale) +type 9 (RLE compressed 8bit paletted with 24bit/32bit palette) +type 10 (RLE compressed 24bit/32bit true color, glquake supported this) +type 11 (RLE compressed 8bit greyscale) +attribute bit 0x20 (Origin At Top Left, top to bottom, left to right) + +image formats guarenteed to be supported: tga, pcx, lmp +image formats that are optional: png, jpg + +mdl/spr/spr32 examples: +skins are named _A (A being a number) and skingroups are named like _A_B +these act as suffixes on the model name... +example names for skin _2_1 of model "progs/armor.mdl": +game/override/progs/armor.mdl_2_1.tga +game/textures/progs/armor.mdl_2_1.tga +game/progs/armor.mdl_2_1.tga +example names for skin _0 of the model "progs/armor.mdl": +game/override/progs/armor.mdl_0.tga +game/textures/progs/armor.mdl_0.tga +game/progs/armor.mdl_0.tga +note that there can be more skins files (of the _0 naming) than the mdl +contains, this is only useful to save space in the .mdl file if classic quake +compatibility is not a concern. + +bsp/md2/md3 examples: +example names for the texture "quake" of model "maps/start.bsp": +game/override/quake.tga +game/textures/quake.tga +game/progs/quake.tga + +sbar/menu/console textures: for example the texture "conchars" (console font) in gfx.wad +game/override/gfx/conchars.tga +game/textures/gfx/conchars.tga +game/gfx/conchars.tga +*/ + +//DP_GFX_QUAKE3MODELTAGS +//idea: id Software +//darkplaces implementation: LordHavoc +//field definitions: +.entity tag_entity; // entity this is attached to (call setattachment to set this) +.float tag_index; // which tag on that entity (0 is relative to the entity, > 0 is an index into the tags on the model if it has any) (call setattachment to set this) +//builtin definitions: +void(entity e, entity tagentity, string tagname) setattachment = #443; // attachs e to a tag on tagentity (note: use "" to attach to entity origin/angles instead of a tag) +//description: +//allows entities to be visually attached to model tags (which follow animations perfectly) on other entities, for example attaching a weapon to a player's hand, or upper body attached to lower body, allowing it to change angles and frame separately (note: origin and angles are relative to the tag, use '0 0 0' for both if you want it to follow exactly, this is similar to viewmodelforclient's behavior). +//note 2: if the tag is not found, it defaults to "" (attach to origin/angles of entity) +//note 3: attaching to world turns off attachment +//note 4: the entity that this is attached to must be visible for this to work + +//DP_GFX_SKINFILES +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//description: +//alias models (mdl, md2, md3) can have .skin files to replace conventional texture naming, these have a naming format such as: +//progs/test.md3_0.skin +//progs/test.md3_1.skin +//... +// +//these files contain replace commands (replace meshname shadername), example: +//replace "helmet" "progs/test/helmet1.tga" // this is a mesh shader replacement +//replace "teamstripes" "progs/test/redstripes.tga" +//replace "visor" "common/nodraw" // this makes the visor mesh invisible +////it is not possible to rename tags using this format +// +//Or the Quake3 syntax (100% compatible with Quake3's .skin files): +//helmet,progs/test/helmet1.tga // this is a mesh shader replacement +//teamstripes,progs/test/redstripes.tga +//visor,common/nodraw // this makes the visor mesh invisible +//tag_camera, // this defines that the first tag in the model is called tag_camera +//tag_test, // this defines that the second tag in the model is called tag_test +// +//any names that are not replaced are automatically show up as a grey checkerboard to indicate the error status, and "common/nodraw" is a special case that is invisible. +//this feature is intended to allow multiple skin sets on md3 models (which otherwise only have one skin set). +//other commands might be added someday but it is not expected. + +//DP_HALFLIFE_MAP +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//description: +//simply indicates that the engine supports HalfLife maps (BSP version 30, NOT the QER RGBA ones which are also version 30). + +//DP_HALFLIFE_MAP_CVAR +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//cvars: +//halflifebsp 0/1 +//description: +//engine sets this cvar when loading a map to indicate if it is halflife format or not. + +//DP_INPUTBUTTONS +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//field definitions: +.float button3; +.float button4; +.float button5; +.float button6; +.float button7; +.float button8; +//description: +//set to the state of the +button3, +button4, +button5, +button6, +button7, and +button8 buttons from the client, this does not involve protocol changes (the extra 6 button bits were simply not used). + +//DP_MONSTERWALK +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//description: +//MOVETYPE_WALK is permitted on non-clients, so bots can move smoothly, run off ledges, etc, just like a real player. + +//DP_MOVETYPEBOUNCEMISSILE +//idea: id Software +//darkplaces implementation: id Software +//movetype definitions: +float MOVETYPE_BOUNCEMISSILE = 11; +//description: +//MOVETYPE_BOUNCE but without gravity, and with full reflection (no speed loss like grenades have), in other words - bouncing laser bolts. + +//DP_MOVETYPEFOLLOW +//idea: id Software, LordHavoc (redesigned) +//darkplaces implementation: LordHavoc +//movetype definitions: +float MOVETYPE_FOLLOW = 12; +//description: +//MOVETYPE_FOLLOW implemented, this uses existing entity fields in unusual ways: +//aiment - the entity this is attached to. +//punchangle - the original angles when the follow began. +//view_ofs - the relative origin (note that this is un-rotated by punchangle, and that is actually the only purpose of punchangle). +//v_angle - the relative angles. +//here's an example of how you would set a bullet hole sprite to follow a bmodel it was created on, even if the bmodel rotates: +//hole.movetype = MOVETYPE_FOLLOW; // make the hole follow +//hole.solid = SOLID_NOT; // MOVETYPE_FOLLOW is always non-solid +//hole.aiment = bmodel; // make the hole follow bmodel +//hole.punchangle = bmodel.angles; // the original angles of bmodel +//hole.view_ofs = hole.origin - bmodel.origin; // relative origin +//hole.v_angle = hole.angles - bmodel.angles; // relative angles + +//DP_QC_CHANGEPITCH +//idea: id Software +//darkplaces implementation: id Software +//field definitions: +.float idealpitch; +.float pitch_speed; +//builtin definitions: +void(entity ent) changepitch = #63; +//description: +//equivilant to changeyaw, ent is normally self. (this was a Q2 builtin) + +//DP_QC_COPYENTITY +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +void(entity from, entity to) copyentity = #400; +//description: +//copies all data in the entity to another entity. + +//DP_QC_ETOS +//idea: id Software +//darkplaces implementation: id Software +//builtin definitions: +string(entity ent) etos = #65; +//description: +//lists all of the entity's fields into a string (similar to edict command in console). (this was a Q2 builtin) + +//DP_QC_FINDCHAIN +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +entity(.string fld, string match) findchain = #402; +//description: +//similar to find() but returns a chain of entities like findradius. + +//DP_QC_FINDCHAINFLOAT +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +entity(.entity fld, entity match) findchainentity = #403; +entity(.float fld, float match) findchainfloat = #403; +//description: +//similar to findentity()/findfloat() but returns a chain of entities like findradius. + +//DP_QC_FINDFLOAT +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +entity(entity start, .entity fld, entity match) findentity = #98; +entity(entity start, .float fld, float match) findfloat = #98; +//description: +//finds an entity or float field value, similar to find(), but for entity and float fields. + +//DP_QC_GETLIGHT +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +vector(vector org) getlight = #92; +//description: +//returns the lighting at the requested location (in color), 0-255 range (can exceed 255). + +//DP_QC_GETSURFACE +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +float(entity e, float s) getsurfacenumpoints = #434; +vector(entity e, float s, float n) getsurfacepoint = #435; +vector(entity e, float s) getsurfacenormal = #436; +string(entity e, float s) getsurfacetexture = #437; +float(entity e, vector p) getsurfacenearpoint = #438; +vector(entity e, float s, vector p) getsurfaceclippedpoint = #439; +//description: +//functions to query surface information. + +//DP_QC_MINMAXBOUND +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +float(float a, float b) min = #94; +float(float a, float b, float c) min3 = #94; +float(float a, float b, float c, float d) min4 = #94; +float(float a, float b, float c, float d, float e) min5 = #94; +float(float a, float b, float c, float d, float e, float f) min6 = #94; +float(float a, float b, float c, float d, float e, float f, float g) min7 = #94; +float(float a, float b, float c, float d, float e, float f, float g, float h) min8 = #94; +float(float a, float b) max = #95; +float(float a, float b, float c) max3 = #95; +float(float a, float b, float c, float d) max4 = #95; +float(float a, float b, float c, float d, float e) max5 = #95; +float(float a, float b, float c, float d, float e, float f) max6 = #95; +float(float a, float b, float c, float d, float e, float f, float g) max7 = #95; +float(float a, float b, float c, float d, float e, float f, float g, float h) max8 = #95; +float(float minimum, float val, float maximum) bound = #96; +//description: +//min returns the lowest of all the supplied numbers. +//max returns the highest of all the supplied numbers. +//bound clamps the value to the range and returns it. + +//DP_QC_RANDOMVEC +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +vector() randomvec = #91; +//description: +//returns a vector of length < 1, much quicker version of this QC: do {v_x = random();v_y = random();v_z = random();} while(vlen(v) > 1) + +//DP_QC_SINCOSSQRTPOW +//idea: id Software, LordHavoc +//darkplaces implementation: id Software, LordHavoc +//builtin definitions: +float(float val) sin = #60; +float(float val) cos = #61; +float(float val) sqrt = #62; +float(float a, float b) pow = #97; +//description: +//useful math functions, sine of val, cosine of val, square root of val, and raise a to power b, respectively. + +//DP_QC_TRACEBOX +//idea: id Software +//darkplaces implementation: id Software +//builtin definitions: +void(vector v1, vector min, vector max, vector v2, float nomonsters, entity forent) tracebox = #90; +//description: +//similar to traceline but much more useful, traces a box of the size specified (technical note: currently the hull size can only be one of the sizes used in the map for bmodel collisions, entity collisions will pay attention to the exact size specified however, this is a collision code limitation in quake itself, and will be fixed eventually). + +//DP_QC_TRACETOSS +//idea: id Software +//darkplaces implementation: id Software +//builtin definitions: +void(entity ent, entity ignore) tracetoss = #64; +//description: +//simulates movement of the entity as if it is MOVETYPE_TOSS and starting with it's current state (location, velocity, etc), returns relevant trace_ variables (trace_fraction is always 0, all other values are supported - trace_ent, trace_endpos, trace_plane_normal), does not actually alter the entity. + +//DP_QC_VECTORVECTORS +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +void(vector dir) vectorvectors = #432; +//description: +//creates v_forward, v_right, and v_up vectors given a forward vector, similar to makevectors except it takes a forward direction vector instead of angles. + +//DP_QUAKE2_MODEL +//idea: quake community +//darkplaces implementation: LordHavoc +//description: +//shows that the engine supports Quake2 .md2 files. + +//DP_QUAKE3_MODEL +//idea: quake community +//darkplaces implementation: LordHavoc +//description: +//shows that the engine supports Quake3 .md3 files. + +//DP_REGISTERCVAR +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +float(string name, string value) registercvar = #93; +//description: +//adds a new console cvar to the server console (in singleplayer this is the player's console), the cvar exists until the mod is unloaded or the game quits. + +//DP_SOLIDCORPSE +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//solid definitions: +float SOLID_CORPSE = 5; +//description: +//the entity will not collide with SOLID_CORPSE and SOLID_SLIDEBOX entities (and likewise they will not collide with it), this is useful if you want dead bodies that are shootable but do not obstruct movement by players and monsters, note that if you traceline with a SOLID_SLIDEBOX entity as the ignoreent, it will ignore SOLID_CORPSE entities, this is desirable for visibility and movement traces, but not for bullets, for the traceline to hit SOLID_CORPSE you must temporarily force the player (or whatever) to SOLID_BBOX and then restore to SOLID_SLIDEBOX after the traceline. + +//DP_SPRITE32 +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//description: +//the engine supports .spr32 sprites. + +//DP_SV_CLIENTCOLORS +//idea: LordHavoc +//darkplaces implementation: LordHavoc +.float clientcolors; // colors of the client (format: pants + shirt * 16) +//description: +//allows qc to read and modify the client colors associated with a client entity (not particularly useful on other entities), and automatically sends out any appropriate network updates if changed + +//DP_SV_CLIENTNAME +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//allows qc to modify the client's .netname, and automatically sends out any appropriate network updates if changed + +//DP_SV_DRAWONLYTOCLIENT +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//field definitions: +.entity drawonlytoclient; +//description: +//the entity is only visible to the specified client. + +//DP_SV_EFFECT +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +void(vector org, string modelname, float startframe, float endframe, float framerate) effect = #404; +//SVC definitions: +//float svc_effect = #52; // [vector] org [byte] modelindex [byte] startframe [byte] framecount [byte] framerate +//float svc_effect2 = #53; // [vector] org [short] modelindex [byte] startframe [byte] framecount [byte] framerate +//description: +//clientside playback of simple custom sprite effects (explosion sprites, etc). + +//DP_SV_NODRAWTOCLIENT +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//field definitions: +.entity nodrawtoclient; +//description: +//the entity is not visible to the specified client. + +//DP_SV_PLAYERPHYSICS +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//field definitions: +.vector movement; +//engine-called QC prototypes: +//void() SV_PlayerPhysics; +//description: +//.movement vector contains the movement input from the player, allowing QC to do as it wishs with the input, and SV_PlayerPhysics will completely replace the player physics if present (works for all MOVETYPE's), see darkplaces mod source for example of this function (in playermovement.qc, adds HalfLife ladders support, as well as acceleration/deceleration while airborn (rather than the quake sudden-stop while airborn), and simplifies the physics a bit) + +//DP_SV_SETCOLOR +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +void(entity ent, float colors) setcolor = #401; +//engine called QC functions (optional): +//void(float color) SV_ChangeTeam; +//description: +//setcolor sets the color on a client and updates internal color information accordingly (equivilant to stuffing a "color" command but immediate) +//SV_ChangeTeam is called by the engine whenever a "color" command is recieved, it may decide to do anything it pleases with the color passed by the client, including rejecting it (by doing nothing), or calling setcolor to apply it, preventing team changes is one use for this. +//the color format is pants + shirt * 16 (0-255 potentially) + +//DP_SV_SLOWMO +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//cvars: +//"slowmo" (0+, default 1) +//description: +//sets the time scale of the server, mainly intended for use in singleplayer by the player, however potentially useful for mods, so here's an extension for it. +//range is 0 to infinite, recommended values to try are 0.1 (very slow, 10% speed), 1 (normal speed), 5 (500% speed). + +//DP_TE_BLOOD +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +void(vector org, vector velocity, float howmany) te_blood = #405; +//temp entity definitions: +float TE_BLOOD = 50; +//protocol: +//vector origin +//byte xvelocity (-128 to +127) +//byte yvelocity (-128 to +127) +//byte zvelocity (-128 to +127) +//byte count (0 to 255, how much blood) +//description: +//creates a blood effect. + +//DP_TE_BLOODSHOWER +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower = #406; +//temp entity definitions: +//float TE_BLOODSHOWER = 52; +//protocol: +//vector mins (minimum corner of the cube) +//vector maxs (maximum corner of the cube) +//coord explosionspeed (velocity of blood particles flying out of the center) +//short count (number of blood particles) +//description: +//creates an exploding shower of blood, for making gibbings more convincing. + +//DP_TE_CUSTOMFLASH +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +void(vector org, float radius, float lifetime, vector color) te_customflash = #417; +//temp entity definitions: +//float TE_CUSTOMFLASH = 73; +//protocol: +//vector origin +//byte radius ((MSG_ReadByte() + 1) * 8, meaning 8-2048 unit radius) +//byte lifetime ((MSG_ReadByte() + 1) / 256.0, meaning approximately 0-1 second lifetime) +//byte red (0.0 to 1.0 converted to 0-255) +//byte green (0.0 to 1.0 converted to 0-255) +//byte blue (0.0 to 1.0 converted to 0-255) +//description: +//creates a customized light flash. + +//DP_TE_EXPLOSIONRGB +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +void(vector org, vector color) te_explosionrgb = #407; +//temp entity definitions: +//float TE_EXPLOSIONRGB = 53; +//protocol: +//vector origin +//byte red (0.0 to 1.0 converted to 0 to 255) +//byte green (0.0 to 1.0 converted to 0 to 255) +//byte blue (0.0 to 1.0 converted to 0 to 255) +//description: +//creates a colored explosion effect. + +//DP_TE_FLAMEJET +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//temp entity definitions: +float TE_FLAMEJET = 74; +//protocol: +//vector origin +//vector velocity +//byte count (0 to 255, how many flame particles) +//description: +//creates a single puff of flame particles. (not very useful really) + +//DP_TE_PARTICLECUBE +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube = #408; +//temp entity definitions: +//float TE_PARTICLECUBE = 54; +//protocol: +//vector mins (minimum corner of the cube) +//vector maxs (maximum corner of the cube) +//vector velocity +//short count +//byte color (palette color) +//byte gravity (TRUE or FALSE, FIXME should this be a scaler instead?) +//coord randomvel (how much to jitter the velocity) +//description: +//creates a cloud of particles, useful for forcefields but quite customizable. + +//DP_TE_PARTICLERAIN +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain = #409; +//temp entity definitions: +//float TE_PARTICLERAIN = 55; +//protocol: +//vector mins (minimum corner of the cube) +//vector maxs (maximum corner of the cube) +//vector velocity (velocity of particles) +//short count (number of particles) +//byte color (8bit palette color) +//description: +//creates a shower of rain, the rain will appear either at the top (if falling down) or bottom (if falling up) of the cube. + +//DP_TE_PARTICLESNOW +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow = #410; +//temp entity definitions: +//float TE_PARTICLERAIN = 56; +//protocol: +//vector mins (minimum corner of the cube) +//vector maxs (maximum corner of the cube) +//vector velocity (velocity of particles) +//short count (number of particles) +//byte color (8bit palette color) +//description: +//creates a shower of snow, the snow will appear either at the top (if falling down) or bottom (if falling up) of the cube, low velocities are advisable for convincing snow. + +//DP_TE_QUADEFFECTS1 +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +void(vector org) te_gunshotquad = #412; +void(vector org) te_spikequad = #413; +void(vector org) te_superspikequad = #414; +void(vector org) te_explosionquad = #415; +//temp entity definitions: +//float TE_GUNSHOTQUAD = 57; // [vector] origin +//float TE_SPIKEQUAD = 58; // [vector] origin +//float TE_SUPERSPIKEQUAD = 59; // [vector] origin +//float TE_EXPLOSIONQUAD = 70; // [vector] origin +//protocol: +//vector origin +//description: +//all of these just take a location, and are equivilant in function (but not appearance :) to the original TE_GUNSHOT, etc. + +//DP_TE_SMALLFLASH +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +void(vector org) te_smallflash = #416; +//temp entity definitions: +//float TE_SMALLFLASH = 72; +//protocol: +//vector origin +//description: +//creates a small light flash (radius 200, time 0.2). + +//DP_TE_SPARK +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +void(vector org, vector vel, float howmany) te_spark = #411; +//temp entity definitions: +//float TE_SPARK = 51; +//protocol: +//vector origin +//byte xvelocity (-128 to 127) +//byte yvelocity (-128 to 127) +//byte zvelocity (-128 to 127) +//byte count (number of sparks) +//description: +//creates a shower of sparks and a smoke puff. + +//DP_TE_STANDARDEFFECTBUILTINS +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +void(vector org) te_gunshot = #418; +void(vector org) te_spike = #419; +void(vector org) te_superspike = #420; +void(vector org) te_explosion = #421; +void(vector org) te_tarexplosion = #422; +void(vector org) te_wizspike = #423; +void(vector org) te_knightspike = #424; +void(vector org) te_lavasplash = #425; +void(vector org) te_teleport = #426; +void(vector org, float color) te_explosion2 = #427; +void(entity own, vector start, vector end) te_lightning1 = #428; +void(entity own, vector start, vector end) te_lightning2 = #429; +void(entity own, vector start, vector end) te_lightning3 = #430; +void(entity own, vector start, vector end) te_beam = #431; +//description: +//to make life easier on mod coders. + +//DP_TE_PLASMABURN +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +void(vector org) te_plasmaburn = #433; +//temp entity definitions: +//float TE_PLASMABURN = 75; +//protocol: +//vector origin +//description: +//creates a small light flash (radius 200, time 0.2) and marks the walls. + +//DP_VIEWZOOM +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//field definitions: +.float viewzoom; +//description: +//scales fov and sensitivity of player, valid range is 0 to 1 (intended for sniper rifle zooming, and such) + +//FRIK_FILE +//idea: FrikaC +//darkplaces implementation: LordHavoc +//builtin definitions: +float(string s) stof = #81; // get numerical value from a string +float(string filename, float mode) fopen = #110; // opens a file inside quake/gamedir/data/ (mode is FILE_READ, FILE_APPEND, or FILE_WRITE), returns fhandle >= 0 if successful, or fhandle < 0 if unable to open file for any reason +void(float fhandle) fclose = #111; // closes a file +string(float fhandle) fgets = #112; // reads a line of text from the file and returns as a tempstring +void(float fhandle, string s) fputs = #113; // writes a line of text to the end of the file +float(string s) strlen = #114; // returns how many characters are in a string +string(string s1, string s2) strcat = #115; // concatenates two strings (for example "abc", "def" would return "abcdef") and returns as a tempstring +string(string s, float start, float length) substring = #116; // returns a section of a string as a tempstring +vector(string s) stov = #117; // returns vector value from a string +string(string s) strzone = #118; // makes a copy of a string into the string zone and returns it, this is often used to keep around a tempstring for longer periods of time (tempstrings are replaced often) +void(string s) strunzone = #119; // removes a copy of a string from the string zone (you can not use that string again or it may crash!!!) +//constants: +float FILE_READ = 0; +float FILE_APPEND = 1; +float FILE_WRITE = 2; +//cvars: +//pr_zone_min_strings : default 64 (64k), min 64 (64k), max 8192 (8mb) +//description: +//provides text file access functions and string manipulation functions, note that you may want to set pr_zone_min_strings in the worldspawn function if 64k is not enough string zone space. + +//KRIMZON_SV_PARSECLIENTCOMMAND +//idea: KrimZon +//darkplaces implementation: KrimZon, LordHavoc +//engine-called QC prototypes: +//void(string s) SV_ParseClientCommand; +//builtin definitions: +void(entity e, string s) clientcommand = #440; +float(string s) tokenize = #441; +string(float n) argv = #442; +//description: +//provides QC the ability to completely control server interpretation of client commands ("say" and "color" for example, clientcommand is necessary for this and substring (FRIK_FILE) is useful) as well as adding new commands (tokenize, argv, and stof (FRIK_FILE) are useful for this)), whenever a clc_stringcmd is received the QC function is called, and it is up to the QC to decide what (if anything) to do with it + +//NEH_RESTOREGAME +//idea: Nehahra +//darkplaces implementation: LordHavoc +//engine-called QC prototypes: +//void() RestoreGame; +//description: +//when a savegame is loaded, this function is called + +//NEH_CMD_PLAY2 +//idea: Nehahra +//darkplaces implementation: LordHavoc +//description: +//shows that the engine supports the "play2" console command (plays a sound without spatialization). + +//TW_SV_STEPCONTROL +//idea: Transfusion +//darkplaces implementation: LordHavoc +//cvars: +//sv_jumpstep (0/1, default 1) +//sv_stepheight (default 18) +//description: +//sv_jumpstep allows stepping up onto stairs while airborn, sv_stepheight controls how high a single step can be. + +// UNFINISHED section +//DP_QC_BOTCLIENT +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +// FIXME: these need numbers assigned, and they need to be added to the engine! +//entity() spawnclient = #; // like spawn but for client slots (also calls relevant connect/spawn functions), returns world if no clients available +//void(entity e) removeclient = #; // like remove but for client slots (also calls relevant disconnect functions) +//description: +//functions to allow bots to use client slots with no hacks, some notes: +.string netname; //controls name on scoreboard (so be sure to set it for a bot) +.float frags; //controls score on scoreboard (this is expected) +.float clientcolors; //controls coloring on scoreboard (this determines player colors exactly like the "color" command, except for using a single number that contains pants and shirt colors (pants + shirt * 16, 0-255 potentially), so be sure to set it for a bot) +//note also that these fields can be set on a real client to change the relevant settings. +//see also DP_SV_SETCOLOR extension for another way to manipulate clientcolors (made obsolete by this extension). + +//unassigned stuff: (need to write up specs but haven't yet) +.vector punchvector; // DP_SV_PUNCHVECTOR +.float ping; // DP_SV_PING + + diff --git a/qcsrc/gamec/g_casings.c b/qcsrc/gamec/g_casings.c new file mode 100644 index 000000000..34266d89c --- /dev/null +++ b/qcsrc/gamec/g_casings.c @@ -0,0 +1,91 @@ +void() casingtouch = +{ + if (other.solid == SOLID_BSP) + if (vlen(self.velocity) >= 50) + if (time >= self.attack_finished) + sound (self, CHAN_WEAPON, "weapons/tink1.wav", 0.5, ATTN_NORM); + self.attack_finished = time + 0.2; + //self.touch = SUB_Null; // one tink is enough + //self.dest = self.origin - self.groundentity.origin; +}; + +void() casingthink = +{ + local float p; + self.nextthink = time + 0.1; + if (self.flags & FL_ONGROUND) + { + // just keep the yaw angle + self.angles_x = 0; + self.angles_z = 0; + self.flags = self.flags - FL_ONGROUND; + self.nextthink = time + 0.5; + } + p = pointcontents(self.origin); + if (p == CONTENT_SOLID || p == CONTENT_LAVA || p == CONTENT_SKY) + { + removedecor(self); + return; + } + if (time > self.cnt) + { + self.nextthink = time; + self.alpha = self.alpha - frametime; + if (self.alpha < 0.0625) + removedecor(self); + } +}; + +// knock loose the casing when disturbed +void() casingknockedloosefunc = +{ + self.movetype = MOVETYPE_BOUNCE; + self.flags = self.flags - (self.flags & FL_ONGROUND); + self.avelocity = randomvec() * 300; + self.nextthink = time + 0.1; + self.touch = casingtouch; +}; + +void(vector org, vector vel, float randomvel, vector ang, vector avel, float randomavel, float casingtype) SpawnCasing = +{ + local entity e; + if (cvar("temp1") & 2048) + return; + + e = newdecor(); + //e.isdecor = TRUE; + e.alpha = 1; + //e.forcescale = 15; + e.movetype = MOVETYPE_BOUNCE; + e.solid = SOLID_TRIGGER; + e.velocity = vel + randomvec() * randomvel; + e.angles = ang; + e.avelocity = avel + randomvec() * randomavel; + e.nextthink = time; + e.think = casingthink; + e.touch = casingtouch; + //e.knockedloosefunc = casingknockedloosefunc; + //e.effects = EF_LOWPRECISION; + e.createdtime = time; + if (casingtype == 1) + { + setmodel (e, "models/casing_shell.mdl"); + e.cnt = time + 30; + // bias to make these be considered more important than other things + e.createdtime = time + 1; + } + else if (casingtype == 2) + { + setmodel (e, "models/casing_steel.mdl"); + e.cnt = time + 10; + } + else + { + setmodel (e, "models/casing_bronze.mdl"); + e.cnt = time + 10; + } + if (maxclients == 1) + e.cnt = time + 3000; + setsize (e, '0 0 -1', '0 0 -1'); + setorigin (e, org); +}; diff --git a/qcsrc/gamec/g_damage.c b/qcsrc/gamec/g_damage.c new file mode 100644 index 000000000..04d4d2122 --- /dev/null +++ b/qcsrc/gamec/g_damage.c @@ -0,0 +1,193 @@ + +void Obituary (entity attacker, entity targ, float deathtype) +{ + local string s; + if (targ.classname == "player" || targ.classname == "corpse") + { + s = targ.netname; + if (targ.classname == "corpse") + s = "A corpse"; + if (targ == attacker) + { + if (deathtype == IT_LASER) {bprint(s);bprint(" was unable to resist the urge to self-immolate\n");} + else if (deathtype == IT_GRENADE_LAUNCHER) {bprint(s);bprint(" detonated\n");} + else if (deathtype == IT_ELECTRO) {bprint(s);bprint(" played with plasma\n");} + else if (deathtype == IT_HAGAR) {bprint(s);bprint(" should have used a different weapon\n");} + else if (deathtype == IT_ROCKET_LAUNCHER) {bprint(s);bprint(" exploded\n");} + else {bprint(s);bprint(" competes for the darwin awards\n");} + self.frags = self.frags - 1; + } + else if (attacker.classname == "player") + { + if (deathtype == IT_LASER) {bprint(s);bprint(" was a victim of laser surgeon ");bprint(attacker.netname);bprint("\n");} + else if (deathtype == IT_UZI) {bprint(s);bprint(" was gunned down by ");bprint(attacker.netname);bprint("\n");} + else if (deathtype == IT_SHOTGUN) {bprint(s);bprint(" was gunned down by ");bprint(attacker.netname);bprint("\n");} + else if (deathtype == IT_GRENADE_LAUNCHER) {bprint(s);bprint(" was blasted by ");bprint(attacker.netname);bprint("\n");} + else if (deathtype == IT_ELECTRO) {bprint(s);bprint(" bathed in plasma from ");bprint(attacker.netname);bprint("\n");} + else if (deathtype == IT_CRYLINK) {bprint(s);bprint(" was zapped by ");bprint(attacker.netname);bprint("\n");} + else if (deathtype == IT_NEX) {bprint(s);bprint(" sports a new hole from ");bprint(attacker.netname);bprint("\n");} + else if (deathtype == IT_HAGAR) {bprint(s);bprint(" was pummeled by ");bprint(attacker.netname);bprint("\n");} + else if (deathtype == IT_ROCKET_LAUNCHER) {bprint(s);bprint(" was given a lesson in rocketry by ");bprint(attacker.netname);bprint("\n");} + else {bprint(s);bprint(" was killed by ");bprint(attacker.netname);bprint("\n");} + attacker.frags = attacker.frags + 1; + } + else + { + {bprint(s);bprint(" died\n");} + } + } +} + +void Damage (entity targ, entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + local entity oldself; + oldself = self; + self = targ; + if (attacker.items & IT_STRENGTH) + { + damage = damage * POWERUP_STRENGTH_DAMAGE; + force = force * POWERUP_STRENGTH_FORCE; + } + if (targ.items & IT_INVINCIBLE) + damage = damage * POWERUP_INVINCIBLE_TAKEDAMAGE; + // apply push + if (self.damageforcescale) + { + self.velocity = self.velocity + self.damageforcescale * force; + self.flags = self.flags - (self.flags & FL_ONGROUND); + } + // apply damage + if (self.event_damage) + self.event_damage (hitloc, damage, inflictor, attacker, deathtype); + self = oldself; +} + +void RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity ignore, float forceintensity, float deathtype) +{ + entity targ; + float finaldmg; + float power; + vector blastorigin; + vector force; + vector m1; + vector m2; + vector nearest; + vector diff; + + blastorigin = (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5); + + targ = findradius (blastorigin, rad); + while (targ) + { + if (targ != inflictor) + if (ignore != targ) + { + // LordHavoc: measure distance to nearest point on target (not origin) + // (this guarentees 100% damage on a touch impact) + nearest = blastorigin; + m1 = targ.origin + targ.mins; + m2 = targ.origin + targ.maxs; + if (nearest_x < m1_x) nearest_x = m1_x; + if (nearest_y < m1_y) nearest_y = m1_y; + if (nearest_z < m1_z) nearest_z = m1_z; + if (nearest_x > m2_x) nearest_x = m2_x; + if (nearest_y > m2_y) nearest_y = m2_y; + if (nearest_z > m2_z) nearest_z = m2_z; + diff = nearest - blastorigin; + // round up a little on the damage to ensure full damage on impacts + // and turn the distance into a fraction of the radius + power = 1 - ((vlen (diff) - 2) / rad); + //bprint(" "); + //bprint(ftos(power)); + if (power > 0) + { + if (power > 1) + power = 1; + finaldmg = coredamage * power + edgedamage * (1 - power); + force = normalize(diff) * (finaldmg / coredamage) * forceintensity; + if (targ == attacker) + finaldmg = finaldmg * 0.6; // Partial damage if the attacker hits himself + if (finaldmg > 0) + Damage (targ, inflictor, attacker, finaldmg, deathtype, inflictor.origin, force); + } + } + targ = targ.chain; + } +} + +/* +entity multi_ent; +float multi_damage; +vector multi_force; + +void ClearMultiDamage (void) +{ + multi_ent = world; + multi_damage = 0; + multi_force = '0 0 0'; +} + +void ApplyMultiDamage (void) +{ + if (!multi_ent) + return; + + Damage (self, multi_ent.origin, multi_ent, 0, multi_damage, multi_force); +} + +void AddMultiDamage (entity hit, float damage, vector force) +{ + if (!hit) + return; + + if (hit != multi_ent) + { + ApplyMultiDamage (); + ClearMultiDamage (); + multi_ent = hit; + } + multi_damage = multi_damage + damage; + multi_force = multi_force + force; +} + +void FireBullets (float shotcount, vector dir, vector spread, float deathtype) +{ + vector direction; + vector source; + vector vel; + vector org; + + makevectors (self.v_angle); + + source = self.origin + v_forward * 10; // FIXME + source_x = self.absmin_z + self.size_z * 0.7; // ??? whaddabout view_ofs + + // LordHavoc: better to use normal damage + //ClearMultiDamage (); + while (shotcount > 0) + { + direction = dir + crandom () * spread_x * v_right + crandom () * spread_y * v_up; + + traceline (source, source + direction * 2048, FALSE, self); + if (trace_fraction != 1.0) + { + vel = normalize (direction + v_up * crandom () + v_right * crandom ()); + vel = vel + 2 * trace_plane_normal; + vel = vel * 200; + + org = trace_endpos - direction * 4; + + if (!trace_ent.takedamage) + te_gunshot (org); + // LordHavoc: better to use normal damage + //AddMultiDamage (trace_ent, 4, direction * 4); + Damage (trace_ent, self, self, 4, deathtype, trace_endpos, direction * 4); + } + + shotcount = shotcount + 1; + } + + // LordHavoc: better to use normal damage + //ApplyMultiDamage (); +} +*/ diff --git a/qcsrc/gamec/g_decors.c b/qcsrc/gamec/g_decors.c new file mode 100644 index 000000000..7547fb62c --- /dev/null +++ b/qcsrc/gamec/g_decors.c @@ -0,0 +1,247 @@ + +float maxclients; // set by worldspawn code +float numdecors; +float maxdecors; +.float createdtime; +.void() th_gib; + +//void(vector org, entity en, vector dir, float splattype, float importance) newbloodsplat; +//void(vector org, float bodydamage, float armordamage, vector vel, float damgtype) genericbleedfunc; + +// removes the oldest decors each frame to maintain a certain maximum decors +void() decorframe = +{ + local entity estart, e, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10; + local float bt1, bt2, bt3, bt4, bt5, bt6, bt7, bt8, bt9, bt10, iterations; + + // numdecors is allowed to be bogus as long as it is >= the real number of decors + // (but perfection is clearly preferable) + if (numdecors <= maxdecors) + return; + + // recount all the decors + numdecors = 0; + estart = e = findchain(classname, "decor"); + while(e) + { + numdecors = numdecors + 1; + e = e.chain; + } + if (numdecors <= maxdecors) + return; // nothing to do + + // limit it to considering 10000 entities per frame, + // otherwise it can cause a runaway loop error + iterations = 0; + while (numdecors > maxdecors && iterations < 10000) + { + iterations = iterations + 1; + // find and remove the oldest decors (upto 10 at once) + b1 = b2 = b3 = b4 = b5 = b6 = b7 = b8 = b9 = b10 = world; + bt1 = bt2 = bt3 = bt4 = bt5 = bt6 = bt7 = bt8 = bt9 = bt10 = time + 10000; + if (iterations > 0) + estart = findchain(classname, "decor"); + e = estart; + while(e) + { + iterations = iterations + 1; + if (e.createdtime < bt10) + { + if (e.createdtime < bt9) + { + if (e.createdtime < bt8) + { + if (e.createdtime < bt7) + { + if (e.createdtime < bt6) + { + if (e.createdtime < bt5) + { + if (e.createdtime < bt4) + { + if (e.createdtime < bt3) + { + if (e.createdtime < bt2) + { + if (e.createdtime < bt1) + { + b10=b9;bt10=bt9; + b9=b8;bt9=bt8; + b8=b7;bt8=bt7; + b7=b6;bt7=bt6; + b6=b5;bt6=bt5; + b5=b4;bt5=bt4; + b4=b3;bt4=bt3; + b3=b2;bt3=bt2; + b2=b1;bt2=bt1; + b1=e;bt1=e.createdtime; + } + else + { + b10=b9;bt10=bt9; + b9=b8;bt9=bt8; + b8=b7;bt8=bt7; + b7=b6;bt7=bt6; + b6=b5;bt6=bt5; + b5=b4;bt5=bt4; + b4=b3;bt4=bt3; + b3=b2;bt3=bt2; + b2=e;bt2=e.createdtime; + } + } + else + { + b10=b9;bt10=bt9; + b9=b8;bt9=bt8; + b8=b7;bt8=bt7; + b7=b6;bt7=bt6; + b6=b5;bt6=bt5; + b5=b4;bt5=bt4; + b4=b3;bt4=bt3; + b3=e;bt3=e.createdtime; + } + } + else + { + b10=b9;bt10=bt9; + b9=b8;bt9=bt8; + b8=b7;bt8=bt7; + b7=b6;bt7=bt6; + b6=b5;bt6=bt5; + b5=b4;bt5=bt4; + b4=e;bt4=e.createdtime; + } + } + else + { + b10=b9;bt10=bt9; + b9=b8;bt9=bt8; + b8=b7;bt8=bt7; + b7=b6;bt7=bt6; + b6=b5;bt6=bt5; + b5=e;bt5=e.createdtime; + } + } + else + { + b10=b9;bt10=bt9; + b9=b8;bt9=bt8; + b8=b7;bt8=bt7; + b7=b6;bt7=bt6; + b6=e;bt6=e.createdtime; + } + } + else + { + b10=b9;bt10=bt9; + b9=b8;bt9=bt8; + b8=b7;bt8=bt7; + b7=e;bt7=e.createdtime; + } + } + else + { + b10=b9;bt10=bt9; + b9=b8;bt9=bt8; + b8=e;bt8=e.createdtime; + } + } + else + { + b10=b9;bt10=bt9; + b9=e;bt9=e.createdtime; + } + } + else + { + b10=e;bt10=e.createdtime; + } + } + // failed all 10 slots + e = e.chain; + } + // remove the oldest decors + if (numdecors > maxdecors) {numdecors = numdecors - 1;remove(b1);} + if (numdecors > maxdecors) {numdecors = numdecors - 1;remove(b2);} + if (numdecors > maxdecors) {numdecors = numdecors - 1;remove(b3);} + if (numdecors > maxdecors) {numdecors = numdecors - 1;remove(b4);} + if (numdecors > maxdecors) {numdecors = numdecors - 1;remove(b5);} + if (numdecors > maxdecors) {numdecors = numdecors - 1;remove(b6);} + if (numdecors > maxdecors) {numdecors = numdecors - 1;remove(b7);} + if (numdecors > maxdecors) {numdecors = numdecors - 1;remove(b8);} + if (numdecors > maxdecors) {numdecors = numdecors - 1;remove(b9);} + if (numdecors > maxdecors) {numdecors = numdecors - 1;remove(b10);} + } +}; + +entity() newdecor = +{ + local entity e; + numdecors++; + e = spawn(); + e.classname = "decor"; + return e; +}; + +void(entity e) removedecor = +{ + numdecors--; + remove(e); +}; + + +void(vector org, vector vel, float amount) blood = +{ + te_blood(org, vel, amount); +}; + +void(vector org, vector vel, float amount) spark = +{ + te_spark(org, vel, amount); +}; + +void(vector m1, vector m2, float vel, float amount) bloodshower = +{ + te_bloodshower(m1, m2, vel, amount); +}; + +void(vector org, float quad) bulletpuff = +{ + if (quad) + te_gunshotquad(org); + else + te_gunshot(org); +}; + +void(vector org, float quad) nailpuff = +{ + if (quad) + te_superspikequad(org); + else + te_superspike(org); +}; + +// used for various little bouncing debris to avoid getting stuck in the air +void() DecorThink = +{ + self.nextthink = time; + self.flags = self.flags - (self.flags & FL_ONGROUND); + if (pointcontents(self.origin) == CONTENT_SOLID) + removedecor(self); +}; + +.float createdtime; + +void() InitDecors = +{ + // different number of decors in multi-player (to avoid lag on modems) + if (maxclients > 1) + maxdecors = 32; + else + maxdecors = 100; + // overridable by saved2 cvar on server console + if (cvar("decors")) + maxdecors = cvar("decors"); + if (maxdecors < 1) + maxdecors = 1; +}; diff --git a/qcsrc/gamec/g_subs.c b/qcsrc/gamec/g_subs.c new file mode 100644 index 000000000..935d7f8ea --- /dev/null +++ b/qcsrc/gamec/g_subs.c @@ -0,0 +1,462 @@ +void(vector destangle, float tspeed, void() func) SUB_CalcAngleMove; +void() SUB_CalcMoveDone; +void() SUB_CalcAngleMoveDone; +void() SUB_Null; +//void() SUB_UseTargets; +void() SUB_Remove; + +void info_null (void) +{ + +} + +/* +================== +SUB_Null + +Do nothing +================== +*/ +void SUB_Null (void) +{ + +} + +/* +================== +SUB_Remove + +Remove self +================== +*/ +void SUB_Remove (void) +{ + remove (self); +} + +/* +================== +SUB_VanishOrRemove + +Makes ent invisible or removes it if ent.norespawn +================== +*/ +void SUB_VanishOrRemove (entity ent) +{ + if (ent.norespawn) + { + // remove + remove (ent); + } + else + { + // vanish + ent.effects = EF_NODRAW; + } +} + +void SUB_SetFade_Think (void) +{ + self.think = SUB_SetFade_Think; + self.nextthink = self.fade_time; + self.alpha = 1 - (time - self.fade_time) * 0.5; + if (self.alpha > 1) + self.alpha = 1; + if (self.alpha < 0.01) // don't let it reach 0, lest it become fully visible again + SUB_VanishOrRemove(self); +} + +/* +================== +SUB_SetFade + +Fade 'ent' out when time >= 'when' +================== +*/ +void SUB_SetFade (entity ent, float when) +{ + //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO) + // return; + //ent.alpha = 1; + ent.fade_time = when; + ent.think = SUB_SetFade_Think; + ent.nextthink = when; +} + +/* +============= +SUB_CalcMove + +calculate self.velocity and self.nextthink to reach dest from +self.origin traveling at speed +=============== +*/ +void SUB_CalcMoveDone (void) +{ + // After moving, set origin to exact final destination + + setorigin (self, self.finaldest); + self.velocity = '0 0 0'; + self.nextthink = -1; + if (self.think1) + self.think1 (); +} + +void SUB_CalcMove (vector tdest, float tspeed, void() func) +{ + vector delta; + float traveltime; + + if (!tspeed) + objerror ("No speed is defined!"); + + self.think1 = func; + self.finaldest = tdest; + self.think = SUB_CalcMoveDone; + + if (tdest == self.origin) + { + self.velocity = '0 0 0'; + self.nextthink = self.ltime + 0.1; + return; + } + + delta = tdest - self.origin; + traveltime = vlen (delta) / tspeed; + + if (traveltime < 0.1) + { + self.velocity = '0 0 0'; + self.nextthink = self.ltime + 0.1; + return; + } + + self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division + + self.nextthink = self.ltime + traveltime; +} + +void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func) +{ + entity oldself; + + oldself = self; + self = ent; + + SUB_CalcMove (tdest, tspeed, func); + + self = oldself; +} + +/* +============= +SUB_CalcAngleMove + +calculate self.avelocity and self.nextthink to reach destangle from +self.angles rotating + +The calling function should make sure self.think is valid +=============== +*/ +void SUB_CalcAngleMoveDone (void) +{ + // After rotating, set angle to exact final angle + self.angles = self.finalangle; + self.avelocity = '0 0 0'; + self.nextthink = -1; + if (self.think1) + self.think1 (); +} + +void SUB_CalcAngleMove (vector destangle, float tspeed, void() func) +{ + vector delta; + float traveltime; + + if (!tspeed) + objerror ("No speed is defined!"); + + delta = destangle = self.angles; + traveltime = vlen (delta) / tspeed; + + self.avelocity = delta * (1 / traveltime); + + self.think1 = func; + self.finalangle = destangle; + + self.think = SUB_CalcAngleMoveDone; + self.nextthink = self.ltime + traveltime; +} + +void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func) +{ + entity oldself; + + oldself = self; + self = ent; + + SUB_CalcAngleMove (destangle, tspeed, func); + + self = oldself; +} + +/* +================== +main + +unused but required by the engine +================== +*/ +void main (void) +{ + +} + +// Sound functions + +/* +================== +PointSound + +Play a sound at the given location +================== +*/ +void PointSound (vector org, string snd, float vol, float attn) +{ + entity speaker; + + speaker = spawn (); + setorigin (speaker, org); + sound (speaker, CHAN_BODY, snd, vol, attn); + remove (speaker); +} + +// Misc + +/* +================== +traceline_hitcorpse + +A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack +================== +*/ +void traceline_hitcorpse (entity source, vector v1, vector v2, float nomonst, entity forent) +{ + float oldsolid; + + oldsolid = source.solid; + source.solid = SOLID_BBOX; + + traceline (v1, v2, nomonst, forent); + + source.solid = oldsolid; +} + +/* +================== +findbetterlocation + +Returns a point at least 12 units away from walls +(useful for explosion animations, although the blast is performed where it really happened) +Ripped from DPMod +================== +*/ +vector findbetterlocation (vector org) +{ + vector loc; + + traceline (org, org - '12 0 0', TRUE, world); + if (trace_fraction < 1) + { + loc = trace_endpos; + traceline (loc, loc + '12 0 0', TRUE, world); + if (trace_fraction >= 1) + org = loc + '12 0 0'; + } + + traceline (org, org - '-12 0 0', TRUE, world); + if (trace_fraction < 1) + { + loc = trace_endpos; + traceline (loc, loc + '-12 0 0', TRUE, world); + if (trace_fraction >= 1) + org = loc + '-12 0 0'; + } + + traceline (org, org - '0 12 0' , TRUE, world); + if (trace_fraction < 1) + { + loc = trace_endpos; + traceline (loc, loc + '0 12 0', TRUE, world); + if (trace_fraction >= 1) + org = loc + '0 12 0'; + } + + traceline (org, org - '0 -12 0', TRUE, world); + if (trace_fraction < 1) + { + loc = trace_endpos; + traceline (loc, loc + '0 -12 0', TRUE, world); + if (trace_fraction >= 1) + org = loc + '0 -12 0'; + } + + traceline (org, org - '0 0 12' , TRUE, world); + if (trace_fraction < 1) + { + loc = trace_endpos; + traceline (loc, loc + '0 0 12', TRUE, world); + if (trace_fraction >= 1) + org = loc + '0 0 12'; + } + + traceline (org, org - '0 0 -12', TRUE, world); + if (trace_fraction < 1) + { + loc = trace_endpos; + traceline (loc, loc + '0 0 -12', TRUE, world); + if (trace_fraction >= 1) + org = loc + '0 0 -12'; + } + + return org; +} + +/* +================== +crandom + +Returns a random number between -1.0 and 1.0 +================== +*/ +float crandom (void) +{ + return 2 * (random () - 0.5); +} + +// Violence + +/* +================== +ImpactEffect + +Plays an impact effect +================== +*/ + +void ImpactEffect (entity ent, float weapontype) +{ + vector org2; + org2 = findbetterlocation (ent.origin); + float b; + + if (weapontype == IT_ROCKET_LAUNCHER) + { + te_explosion (org2); + effect (org2, "models/sprites/dpexplosion1.spr32", 0, 20, 40); + sound (ent, CHAN_BODY, "weapons/rocket_impact.wav", 1, ATTN_NORM); + } + else if (weapontype == IT_GRENADE_LAUNCHER) + { + te_explosion (org2); + effect (org2, "models/sprites/dpexplosion1.spr32", 0, 20, 30); + sound (ent, CHAN_BODY, "weapons/grenade_impact.wav", 1, ATTN_NORM); + } + else if (weapontype == IT_HAGAR) + { + effect (org2, "models/sprites/dpexplosion2.spr32", 0, 20, 30); + b = crandom(); + if (b<-0.7) + sound (ent, CHAN_BODY, "weapons/hagexp1.wav", 1, ATTN_NORM); + else if (b<0.4) + sound (ent, CHAN_BODY, "weapons/hagexp2.wav", 1, ATTN_NORM); + else if (b<1) + sound (ent, CHAN_BODY, "weapons/hagexp3.wav", 1, ATTN_NORM); + } +} + + +/* +================== +Angc used for animations +================== +*/ + + +float angc (float a1, float a2) +{ + float a; + + while (a1 > 180) + a1 = a1 - 360; + while (a1 < -179) + a1 = a1 + 360; + + while (a2 > 180) + a2 = a2 - 360; + while (a2 < -179) + a2 = a2 + 360; + + a = a1 - a2; + while (a > 180) + a = a - 360; + while (a < -179) + a = a + 360; + + return a; +} + + +/* +================ +InitTrigger +================ +*/ + +void() SetMovedir = +{ + if (self.movedir != '0 0 0') + self.movedir = normalize(self.movedir); + else + { + if (self.angles == '0 -1 0') + self.movedir = '0 0 1'; + else if (self.angles == '0 -2 0') + self.movedir = '0 0 -1'; + else + { + makevectors (self.angles); + self.movedir = v_forward; + } + } + + self.angles = '0 0 0'; +}; + +void() InitTrigger = +{ +// trigger angles are used for one-way touches. An angle of 0 is assumed +// to mean no restrictions, so use a yaw of 360 instead. + if (self.movedir == '0 0 0') + if (self.angles != '0 0 0') + SetMovedir (); + self.solid = SOLID_TRIGGER; + setmodel (self, self.model); // set size and link into world + self.movetype = MOVETYPE_NONE; + self.modelindex = 0; + self.model = ""; +}; + +void() InitSolidBSPTrigger = +{ +// trigger angles are used for one-way touches. An angle of 0 is assumed +// to mean no restrictions, so use a yaw of 360 instead. + if (self.movedir == '0 0 0') + if (self.angles != '0 0 0') + SetMovedir (); + self.solid = SOLID_BSP; + setmodel (self, self.model); // set size and link into world + self.movetype = MOVETYPE_PUSH; +// self.modelindex = 0; + self.model = ""; +}; diff --git a/qcsrc/gamec/g_tetris.c b/qcsrc/gamec/g_tetris.c new file mode 100644 index 000000000..2fb148480 --- /dev/null +++ b/qcsrc/gamec/g_tetris.c @@ -0,0 +1,746 @@ +/* + +Installation: + +in weapons.qc add TetrisImpulses(); to ImpulseCommands + +in progs.src add tetris.qc after subs.qc +in client.qc add if (TetrisPreFrame()) return; to PlayerPreThink +in client.qc add if (TetrisPostFrame()) return; to PlayerPostThink + +*/ + +.vector tet_org; + +.float tetris_on, tet_time, tet_autodown; +.vector piece_pos; +.float piece_type, next_piece, tet_score, tet_lines; + +var float tet_high_score = 0; + +float TET_LINES = 20; +float TET_WIDTH = 10; +//character values +float TET_BORDER = 132; +float TET_BLOCKS = 132; // +1 = first color, +2, +3; +float TET_SPACE = 32; // blankness + + + +float TETKEY_UP = 1; +float TETKEY_DOWN = 2; +float TETKEY_LEFT = 4; +float TETKEY_RIGHT = 8; +float TETKEY_ROTLEFT = 16; +float TETKEY_ROTRIGHT = 32; + +float PIECES = 7; + +.float line1, line2, line3, line4, line5, line6, line7, +line8, line9, line10, line11, line12, line13, line14, line15, +line16, line17, line18, line19, line20; + + +float SVC_CENTERPRINTa = 26; + + +/* +********************************* + +Library Functions + +********************************* +*/ +void (float ln, float vl) SetLine = +{ + if (ln == 1) + self.line1 = vl; + else if (ln == 2) + self.line2 = vl; + else if (ln == 3) + self.line3 = vl; + else if (ln == 4) + self.line4 = vl; + else if (ln == 5) + self.line5 = vl; + else if (ln == 6) + self.line6 = vl; + else if (ln == 7) + self.line7 = vl; + else if (ln == 8) + self.line8 = vl; + else if (ln == 9) + self.line9 = vl; + else if (ln == 10) + self.line10 = vl; + else if (ln == 11) + self.line11 = vl; + else if (ln == 12) + self.line12 = vl; + else if (ln == 13) + self.line13 = vl; + else if (ln == 14) + self.line14 = vl; + else if (ln == 15) + self.line15 = vl; + else if (ln == 16) + self.line16 = vl; + else if (ln == 17) + self.line17 = vl; + else if (ln == 18) + self.line18 = vl; + else if (ln == 19) + self.line19 = vl; + else if (ln == 20) + self.line20 = vl; +}; + +float (float ln) GetLine = +{ + if (ln == 1) + return self.line1; + else if (ln == 2) + return self.line2; + else if (ln == 3) + return self.line3; + else if (ln == 4) + return self.line4; + else if (ln == 5) + return self.line5; + else if (ln == 6) + return self.line6; + else if (ln == 7) + return self.line7; + else if (ln == 8) + return self.line8; + else if (ln == 9) + return self.line9; + else if (ln == 10) + return self.line10; + else if (ln == 11) + return self.line11; + else if (ln == 12) + return self.line12; + else if (ln == 13) + return self.line13; + else if (ln == 14) + return self.line14; + else if (ln == 15) + return self.line15; + else if (ln == 16) + return self.line16; + else if (ln == 17) + return self.line17; + else if (ln == 18) + return self.line18; + else if (ln == 19) + return self.line19; + else if (ln == 20) + return self.line20; + else + return 0; +}; + +float(float x, float dat) GetXBlock = +{ + if (x == 1) + return dat & 3; + else if (x == 2) + return (dat & 12) / 4; + else if (x == 3) + return (dat & 48) / 16; + else if (x == 4) + return (dat & 192) / 64; + else if (x == 5) + return (dat & 768) / 256; + else if (x == 6) + return (dat & 3072) / 1024; + else if (x == 7) + return (dat & 12288) / 4096; + else if (x == 8) + return (dat & 49152) / 16384; + else if (x == 9) + return (dat & 196608) / 65536; + else if (x == 10) + return (dat & 786432) / 262144; + else + return 0; +}; + +float(float x, float dat, float new) SetXBlock = +{ + if (x == 1) + return (dat - (dat & 3)) | new; + else if (x == 2) + return (dat - (dat & 12)) | (new*4); + else if (x == 3) + return (dat - (dat & 48)) | (new*16); + else if (x == 4) + return (dat - (dat & 192)) | (new*64); + else if (x == 5) + return (dat - (dat & 768)) | (new*256); + else if (x == 6) + return (dat - (dat & 3072)) | (new*1024); + else if (x == 7) + return (dat - (dat & 12288)) | (new*4096); + else if (x == 8) + return (dat - (dat & 49152)) | (new*16384); + else if (x == 9) + return (dat - (dat & 196608)) | (new*65536); + else if (x == 10) + return (dat - (dat & 786432)) | (new*262144); + else + return dat; +}; + + +float(float x, float y) GetSquare = +{ + return GetXBlock(x, GetLine(y)); +}; + +void (float x, float y, float val) SetSquare = +{ + float dat; + + dat = GetLine(y); + dat = SetXBlock(x, dat, val & 3); + SetLine(y, dat); +}; + + + +vector(float pc) PieceShape = +{ + +/* +1 = + ## + ## +*/ + if (pc == 1) + return '20 20 0'; // 1 * 4 + 1 * 16 +/* +2 = + +#### +*/ + else if (pc == 2) + return '85 0 0'; + +/* +3 = + +### +# +*/ + else if (pc == 3) + return '21 1 0'; +/* +4 = + +# +### +*/ + else if (pc == 4) + return '1 21 0'; +/* +5 = +## + ## +*/ + else if (pc == 5) + return '5 20 0'; + +/* +6 = + ## +## +*/ + else if (pc == 6) + return '20 5 0'; + +/* +7 = + # +### +*/ + else if (pc == 7) + return '4 21 0'; + + + else + return '0 0 0'; + +} + +// do x 1..4 and y 1..4 in case of rotation +float(float x, float y, float rot, float pc) PieceMetric = +{ + float t; + vector piece_dat; + + // return bits of a piece + if (rot == 1) // 90 degrees + { + t = y; + y = x; + x = 5 - t; + } + else if (rot == 2)//180 + { + x = 5 - x; + y = 3 - y; + } + else if (rot == 3) // 270 + { + t = y; + y = 5 - x; + x = t; + } + if (x < 1 || y < 1 || x > 4 || y > 2) + return 0; + piece_dat = PieceShape(pc); + if (y == 1) + return GetXBlock(x, piece_dat_x); // first row + else if (y == 2) + return GetXBlock(x, piece_dat_y); // second row + else if (y == 3) + return GetXBlock(x, piece_dat_z); // third row (doesn't exist) + else + return 0; // illegal parms +}; +/* +********************************* + +Draw + +********************************* +*/ + + +/* some prydon gate functions to make life easier.... + +somewhat modified because we don't need all the fanciness Prydon Gate is capable of + +*/ + +void(float c1, float c2, float c3, float c4, float c5, float c6) p6 = +{ + WriteChar(MSG_ONE, c1); + WriteChar(MSG_ONE, c2); + WriteChar(MSG_ONE, c3); + WriteChar(MSG_ONE, c4); + WriteChar(MSG_ONE, c5); + WriteChar(MSG_ONE, c6); +}; + +float(float num, float dig) pnum = +{ + local float f, i; + if (num < 0) + { + WriteChar(MSG_ONE, 45); + num = 0 - num; + } + f = floor(num / 10); + num = num - (f * 10); + if (f) + dig = pnum(f, dig+1); + else + { + // pad to 6 + for (i = 0; i < (5 - dig); i = i + 1) + WriteChar(MSG_ONE, TET_SPACE); + } + WriteChar(MSG_ONE, 48 + num); + return dig; +}; + +void (float ln) DrawLine = +{ + float x, d; + WriteChar(MSG_ONE, TET_BORDER); + + for (x = 1; x <= TET_WIDTH; x = x + 1) + { + d = GetSquare(x, ln); + if (d) + WriteChar(MSG_ONE, TET_BLOCKS + d); + else + WriteChar(MSG_ONE, TET_SPACE); + } + WriteChar(MSG_ONE, TET_BORDER); +} + +void (float pc, float ln) DrawPiece = +{ + float x, d, piece_ln, color; + vector piece_dat; + color = pc & 3; + if (color == 0) // 4 + color = 1; + WriteChar(MSG_ONE, TET_SPACE); // pad to 6 + + piece_dat = PieceShape(pc); + if (ln == 1) + piece_ln = piece_dat_x; + else + piece_ln = piece_dat_y; + for (x = 1; x <= 4; x = x + 1) + { + d = GetXBlock(x, piece_ln) * color; + if (d) + WriteChar(MSG_ONE, TET_BLOCKS + d); + else + WriteChar(MSG_ONE, TET_SPACE); + } + WriteChar(MSG_ONE, TET_SPACE); // pad to 6 +} +void() Draw_Tetris = +{ + float i; + msg_entity = self; + WriteChar(MSG_ONE, SVC_CENTERPRINTa); + // decoration + for (i = 1; i <= (TET_WIDTH + 2); i = i + 1) + WriteChar(MSG_ONE, TET_BORDER); + p6(' ', ' ', ' ', ' ', ' ', ' '); + WriteChar(MSG_ONE, 10); + for (i = 1; i <= TET_LINES; i = i + 1) + { + DrawLine(i); + if (i == 1) + p6(' ', 'N', 'E', 'X', 'T', ' '); + else if (i == 3) + DrawPiece(self.next_piece, 1); + else if (i == 4) + DrawPiece(self.next_piece, 2); + else if (i == 6) + p6(' ', 'L', 'I', 'N', 'E', 'S'); + else if (i == 7) + pnum(self.tet_lines, 0); + else if (i == 9) + p6(' ', 'S', 'C', 'O', 'R', 'E'); + else if (i == 10) + pnum(self.tet_score, 0); + else if (i == 12) + p6(' ', 'H', 'I', 'G', 'H', ' '); + else if (i == 13) + p6(' ', 'S', 'C', 'O', 'R', 'E'); + else if (i == 14) + pnum(tet_high_score, 0); + else if (i == 16) + p6(' ', 'L', 'E', 'V', 'E', 'L'); + else if (i == 17) + pnum(floor(self.tet_lines / 20)+ 1, 0); + else + p6(' ', ' ', ' ', ' ', ' ', ' '); + WriteChar(MSG_ONE, 10); + } + // decoration + + for (i = 1; i <= (TET_WIDTH + 2); i = i + 1) + WriteChar(MSG_ONE, TET_BORDER); + p6(' ', ' ', ' ', ' ', ' ', ' '); + WriteChar(MSG_ONE, 10); + WriteChar(MSG_ONE, 0); +} +/* +********************************* + +Game Functions + +********************************* +*/ + +// reset the game +void() ResetTetris = +{ + float i; + + for (i=1; i<=TET_LINES; i = i + 1) + SetLine(i, 0); + self.piece_pos = '0 0 0'; + self.piece_type = 0; + self.next_piece = self.tet_lines = self.tet_score = 0; + +}; + +void () Tet_GameOver = +{ + centerprint(self, "Game Over"); + self.tetris_on = 0; + ResetTetris(); + self.movetype = MOVETYPE_WALK; +}; + + + +/* +********************************* + +Game Mechanics + +********************************* +*/ +float() RandomPiece = +{ + return floor(random() * PIECES) + 1; +}; + +void(float n) TetAddScore = +{ + self.tet_score = self.tet_score + n; + if (self.tet_score > tet_high_score) + tet_high_score = self.tet_score; +}; +float CheckMetrics(float piece, float orgx, float orgy, float rot) = +{ + // check to see if the piece, if moved to the locations will overlap + + float x, y; + // why did I start counting from 1, damnit + orgx = orgx - 1; + orgy = orgy - 1; + + for (y = 1; y < 5; y = y + 1) + { + for (x = 1; x < 5; x = x + 1) + { + if (PieceMetric(x, y, rot, piece)) + { + if (GetSquare(x + orgx, y + orgy)) + return FALSE; // uhoh, gonna hit something. + if (x+orgx<1 || x+orgx > TET_WIDTH || y+orgy<1 || y+orgy> TET_LINES) + return FALSE; // ouside the level + } + } + } + return TRUE; +} + +void ClearPiece(float piece, float orgx, float orgy, float rot) = +{ + + float x, y; + // why did I start counting from 1, damnit + orgx = orgx - 1; + orgy = orgy - 1; + + for (y = 1; y < 5; y = y + 1) + { + for (x = 1; x < 5; x = x + 1) + { + if (PieceMetric(x, y, rot, piece)) + { + SetSquare(x + orgx, y + orgy, 0); + } + } + } +} +void CementPiece(float piece, float orgx, float orgy, float rot) = +{ + float color; + float x, y; + // why did I start counting from 1, damnit + orgx = orgx - 1; + orgy = orgy - 1; + + color = piece & 3; + if (color == 0) // 4 + color = 1; + + for (y = 1; y < 5; y = y + 1) + { + for (x = 1; x < 5; x = x + 1) + { + if (PieceMetric(x, y, rot, piece)) + { + SetSquare(x + orgx, y + orgy, color); + } + } + } +} + +float LINE_LOW = 349525; +float LINE_HIGH = 699050; // above number times 2 + +void() CompletedLines = +{ + float y, cleared, ln; + + cleared = 0; + y = TET_LINES; + while(y >= 1) + { + ln = GetLine(y); + if (((ln & LINE_LOW) | ((ln & LINE_HIGH)/2)) == LINE_LOW) + cleared = cleared + 1; + else + y = y - 1; + ln = GetLine(y - cleared); + SetLine(y, ln); + } + self.tet_lines = self.tet_lines + cleared; + TetAddScore(cleared * cleared * 10); +}; + +void(float keyss) HandleGame = +{ + + // first off, we need to see if we need a new piece + vector check_pos; + float brand_new; + brand_new = 0; + if (self.tet_time > time) + return; + self.tet_time = time + 0.1; + + + if (self.piece_type == 0) + { + self.piece_pos = '5 1 0'; // that's about middle top, we count from 1 ARGH + if (self.next_piece) + self.piece_type = self.next_piece; + else + self.piece_type = RandomPiece(); + self.next_piece = RandomPiece(); + keyss = 0; // no movement first frame + self.tet_autodown = time + 0.2; + brand_new = 1; + } + else + ClearPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z); + + // next we need to check the piece metrics against what's on the level + // based on the key order + + check_pos = self.piece_pos; + + if (keyss & TETKEY_RIGHT) + check_pos_x = check_pos_x + 1; + else if (keyss & TETKEY_LEFT) + check_pos_x = check_pos_x - 1; + else if (keyss & TETKEY_ROTRIGHT) + check_pos_z = check_pos_z + 1; + else if (keyss & TETKEY_ROTLEFT) + check_pos_z = check_pos_z - 1; + // bounds check + if (check_pos_z > 3) + check_pos_z = 0; + else if (check_pos_z < 0) + check_pos_z = 3; + + // reality check + if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z)) + self.piece_pos = check_pos; + else if (brand_new) + Tet_GameOver(); + check_pos = self.piece_pos; + if (keyss & TETKEY_DOWN) + check_pos_y = check_pos_y + 1; + else if (self.tet_autodown < time) + { + check_pos_y = check_pos_y + 1; + self.tet_autodown = time + 1 / (floor(self.tet_lines / 20) + 1); + } + if (CheckMetrics(self.piece_type, check_pos_x, check_pos_y, check_pos_z)) + self.piece_pos = check_pos; + else + { + CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z); + TetAddScore(1); + CompletedLines(); + self.piece_type = 0; + return; + } + CementPiece(self.piece_type, self.piece_pos_x, self.piece_pos_y, self.piece_pos_z); +}; + +/* +********************************* + +Important Linking Into Quake stuff + +********************************* +*/ + + +void() TetrisImpulses = +{ + if (self.impulse == 200) + { + self.tetris_on = 1; + ResetTetris(); + self.tet_org = self.origin; + self.movetype = MOVETYPE_NOCLIP; + stuffcmd(self, "cl_bob 0\ncl_rollangle 0\n"); + } +}; + + +float() TetrisPreFrame = +{ + if (!self.tetris_on) + return 0; + + self.tet_org = self.origin; + if (self.tet_time > time) + return 1; + Draw_Tetris(); + return 1; + +}; +float(float v) frik_anglemoda = +{ + return v - floor(v/360) * 360; +}; +float (float y1, float y2) angcompa = +{ + y1 = frik_anglemoda(y1); + y2 = frik_anglemoda(y2); + + local float answer; + answer = y1 - y2; + if (answer > 180) + answer = answer - 360; + else if (answer < -180) + answer = answer + 360; + return answer; +}; + + +float() TetrisPostFrame = +{ + vector mov; + float keysa, norm; + + keysa = 0; + + if (!self.tetris_on) + return 0; + if (self.origin != self.tet_org) + { + mov = vectoangles(self.origin - self.tet_org); + self.origin = self.tet_org; + + norm = angcompa(self.v_angle_y, mov_y); + + if (norm > -80 && norm < 80) + keysa = keysa | TETKEY_UP; + if (norm > 10 && norm < 170) + keysa = keysa | TETKEY_RIGHT; + if (norm > 100 || norm < -260) + keysa = keysa | TETKEY_DOWN; + if (norm > -170 && norm < -10) + keysa = keysa | TETKEY_LEFT; + } + if (self.button0) + keysa = keysa | TETKEY_ROTRIGHT; + if (self.button2) + keysa = keysa | TETKEY_ROTLEFT; + HandleGame(keysa); + return 1; +}; + diff --git a/qcsrc/gamec/g_triggers.c b/qcsrc/gamec/g_triggers.c new file mode 100644 index 000000000..0d9ad1c60 --- /dev/null +++ b/qcsrc/gamec/g_triggers.c @@ -0,0 +1,392 @@ + +void() SUB_UseTargets; + +void() DelayThink = +{ + activator = self.enemy; + SUB_UseTargets (); + remove(self); +}; + +/* +============================== +SUB_UseTargets + +the global "activator" should be set to the entity that initiated the firing. + +If self.delay is set, a DelayedUse entity will be created that will actually +do the SUB_UseTargets after that many seconds have passed. + +Centerprints any self.message to the activator. + +Removes all entities with a targetname that match self.killtarget, +and removes them, so some events can remove other triggers. + +Search for (string)targetname in all entities that +match (string)self.target and call their .use function + +============================== +*/ +void() SUB_UseTargets = +{ + local entity t, stemp, otemp, act; + +// +// check for a delay +// + if (self.delay) + { + // create a temp object to fire at a later time + t = spawn(); + t.classname = "DelayedUse"; + t.nextthink = time + self.delay; + t.think = DelayThink; + t.enemy = activator; + t.message = self.message; + t.killtarget = self.killtarget; + t.target = self.target; + return; + } + + +// +// print the message +// + if (activator.classname == "player" && self.message != "") + { + centerprint (activator, self.message); + if (!self.noise) + sound (activator, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NORM); + } + +// +// kill the killtagets +// + if (self.killtarget) + { + t = world; + do + { + t = find (t, targetname, self.killtarget); + if (!t) + return; + remove (t); + } while ( 1 ); + } + +// +// fire targets +// + if (self.target) + { + act = activator; + t = world; + do + { + t = find (t, targetname, self.target); + if (!t) + { + return; + } + stemp = self; + otemp = other; + self = t; + other = stemp; + if (self.use != SUB_Null) + { + if (self.use) + self.use (); + } + self = stemp; + other = otemp; + activator = act; + } while ( 1 ); + } + + +}; + +entity stemp, otemp, s, old; + + +void() trigger_reactivate = +{ + self.solid = SOLID_TRIGGER; +}; + +//============================================================================= + +float SPAWNFLAG_NOMESSAGE = 1; +float SPAWNFLAG_NOTOUCH = 1; + +// the wait time has passed, so set back up for another activation +void() multi_wait = +{ + if (self.max_health) + { + self.health = self.max_health; + self.takedamage = DAMAGE_YES; + self.solid = SOLID_BBOX; + } +}; + + +// the trigger was just touched/killed/used +// self.enemy should be set to the activator so it can be held through a delay +// so wait for the delay time before firing +void() multi_trigger = +{ + if (self.nextthink > time) + { + return; // allready been triggered + } + + if (self.classname == "trigger_secret") + { + if (self.enemy.classname != "player") + return; + found_secrets = found_secrets + 1; + WriteByte (MSG_ALL, SVC_FOUNDSECRET); + } + + if (self.noise) + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); + +// don't trigger again until reset + self.takedamage = DAMAGE_NO; + + activator = self.enemy; + + SUB_UseTargets(); + + if (self.wait > 0) + { + self.think = multi_wait; + self.nextthink = time + self.wait; + } + else + { // we can't just remove (self) here, because this is a touch function + // called wheil C code is looping through area links... + self.touch = SUB_Null; + self.nextthink = time + 0.1; + self.think = SUB_Remove; + } +}; + +void() multi_use = +{ + self.enemy = activator; + multi_trigger(); +}; + +void() multi_touch = +{ + if (other.classname != "player") + return; + +// if the trigger has an angles field, check player's facing direction + if (self.movedir != '0 0 0') + { + makevectors (other.angles); + if (v_forward * self.movedir < 0) + return; // not facing the right way + } + + self.enemy = other; + multi_trigger (); +}; + +void multi_eventdamage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype) +{ + if (!self.takedamage) + return; + self.health = self.health - damage; + if (self.health <= 0) + { + self.enemy = attacker; + multi_trigger(); + } +} + +/*QUAKED trigger_multiple (.5 .5 .5) ? notouch +Variable sized repeatable trigger. Must be targeted at one or more entities. If "health" is set, the trigger must be killed to activate each time. +If "delay" is set, the trigger waits some time after activating before firing. +"wait" : Seconds between triggerings. (.2 default) +If notouch is set, the trigger is only fired by other entities, not by touching. +NOTOUCH has been obsoleted by trigger_relay! +sounds +1) secret +2) beep beep +3) large switch +4) +set "message" to text string +*/ +void() trigger_multiple = +{ + if (self.sounds == 1) + { + precache_sound ("misc/secret.wav"); + self.noise = "misc/secret.wav"; + } + else if (self.sounds == 2) + { + precache_sound ("misc/talk.wav"); + self.noise = "misc/talk.wav"; + } + else if (self.sounds == 3) + { + precache_sound ("misc/trigger1.wav"); + self.noise = "misc/trigger1.wav"; + } + + if (!self.wait) + self.wait = 0.2; + self.use = multi_use; + + InitTrigger (); + + if (self.health) + { + if (self.spawnflags & SPAWNFLAG_NOTOUCH) + objerror ("health and notouch don't make sense\n"); + self.max_health = self.health; + self.event_damage = multi_eventdamage; + self.takedamage = DAMAGE_YES; + self.solid = SOLID_BBOX; + setorigin (self, self.origin); // make sure it links into the world + } + else + { + if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) ) + { + self.touch = multi_touch; + } + } +}; + + +/*QUAKED trigger_once (.5 .5 .5) ? notouch +Variable sized trigger. Triggers once, then removes itself. You must set the key "target" to the name of another object in the level that has a matching +"targetname". If "health" is set, the trigger must be killed to activate. +If notouch is set, the trigger is only fired by other entities, not by touching. +if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired. +if "angle" is set, the trigger will only fire when someone is facing the direction of the angle. Use "360" for an angle of 0. +sounds +1) secret +2) beep beep +3) large switch +4) +set "message" to text string +*/ +void() trigger_once = +{ + self.wait = -1; + trigger_multiple(); +}; + +//============================================================================= + +/*QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8) +This fixed size trigger cannot be touched, it can only be fired by other events. It can contain killtargets, targets, delays, and messages. +*/ +void() trigger_relay = +{ + self.use = SUB_UseTargets; +}; + + +//============================================================================= + + +void() counter_use = +{ + local string junk; + + self.count = self.count - 1; + if (self.count < 0) + return; + + if (self.count != 0) + { + if (activator.classname == "player" + && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0) + { + if (self.count >= 4) + centerprint (activator, "There are more to go..."); + else if (self.count == 3) + centerprint (activator, "Only 3 more to go..."); + else if (self.count == 2) + centerprint (activator, "Only 2 more to go..."); + else + centerprint (activator, "Only 1 more to go..."); + } + return; + } + + if (activator.classname == "player" + && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0) + centerprint(activator, "Sequence completed!"); + self.enemy = activator; + multi_trigger (); +}; + +/*QUAKED trigger_counter (.5 .5 .5) ? nomessage +Acts as an intermediary for an action that takes multiple inputs. + +If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished. + +After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself. +*/ +void() trigger_counter = +{ + self.wait = -1; + if (!self.count) + self.count = 2; + + self.use = counter_use; +}; + +void() hurt_on = +{ + self.solid = SOLID_TRIGGER; + self.nextthink = -1; +}; + +void() hurt_touch = +{ + if (other.takedamage) + { + self.solid = SOLID_NOT; + Damage (other, self, self, self.dmg, 1, '0 0 0', '0 0 0'); + self.think = hurt_on; + self.nextthink = time + 1; + } + + return; +}; + +/*QUAKED trigger_hurt (.5 .5 .5) ? +Any object touching this will be hurt +set dmg to damage amount +defalt dmg = 5 +*/ +void() trigger_hurt = +{ + InitTrigger (); + self.touch = hurt_touch; + if (!self.dmg) + self.dmg = 5; +}; + +//void() target_speaker_use = {sound(self, CHAN_VOICE, self.noise, 1, 1);} +//void() target_speaker = {self.use = target_speaker_use;} + +void() target_speaker = +{ +if(self.noise) { + precache_sound (self.noise); + ambientsound (self.origin, self.noise, 1, ATTN_STATIC); +} +//remove(self); +}; + diff --git a/qcsrc/gamec/g_violence.c b/qcsrc/gamec/g_violence.c new file mode 100644 index 000000000..e05f36ef2 --- /dev/null +++ b/qcsrc/gamec/g_violence.c @@ -0,0 +1,41 @@ + +void GibDamage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype) +{ + te_blood (self.origin + '0 0 1', '0 0 30', damage); + self.health = self.health - damage; + if (self.health <= -300) + { + self.event_damage = SUB_Null; + SUB_VanishOrRemove (self); + } +} + +// changes by LordHavoc on 03/30/04 +// TossGib now takes a gib entity so it can be used for tossing heads +// gib.velocity now uses randomvec() instead of a bunch of manual random calls +// merged Gib() into PlayerGib() +void TossGib (entity gib, string mdlname, vector org, vector v) +{ + if (gib == world) + { + gib = spawn (); + gib.norespawn = TRUE; + } + gib.classname = "gib"; + gib.movetype = MOVETYPE_BOUNCE; + gib.solid = SOLID_CORPSE; + + setmodel (gib, mdlname); + setsize (gib, '-8 -8 -8', '8 8 8'); + setorigin (gib, org); + + gib.health = -1; + gib.takedamage = DAMAGE_YES; + gib.damageforcescale = 3.5; + gib.event_damage = GibDamage; + + gib.velocity = v + randomvec() * 150; + gib.avelocity = randomvec() * 300; + + SUB_SetFade (gib, time + 12 + random () * 4); +} diff --git a/qcsrc/gamec/g_world.c b/qcsrc/gamec/g_world.c new file mode 100644 index 000000000..ccb6f1b1c --- /dev/null +++ b/qcsrc/gamec/g_world.c @@ -0,0 +1,141 @@ +entity lastspawn; + +void worldspawn (void) +{ + lastspawn = world; + BotInit(); + + game = cvar ("gamecfg"); // load game options + + if (game & GAME_LOW_GRAVITY) + localcmd ("sv_gravity 200\n"); + else + localcmd ("sv_gravity 800\n"); + + precache_model ("models/player/marine.zym"); + precache_model ("models/player/specop.zym"); + precache_model ("models/player/mulder.zym"); + precache_model ("models/player/insurrectionist.zym"); + precache_model ("models/player/fshock.zym"); + + precache_model ("models/weapons/g_uzi.md3"); + precache_model ("models/weapons/g_shotgun.md3"); + precache_model ("models/weapons/g_gl.md3"); + precache_model ("models/weapons/g_electro.md3"); + precache_model ("models/weapons/g_crylink.md3"); + precache_model ("models/weapons/g_nex.md3"); + precache_model ("models/weapons/g_hagar.md3"); + precache_model ("models/weapons/g_rl.md3"); + + precache_model ("models/sprites/plasmahitwall.spr32"); + precache_model ("models/sprites/muzzleflash.spr32"); + precache_model ("models/sprites/plasmashot.spr32"); + precache_model ("models/sprites/dpexplosion1.spr32"); + precache_model ("models/sprites/dpexplosion2.spr32"); + precache_model ("models/sprites/bubbles.spr"); + + precache_model ("models/grenademodel.md3"); + precache_model ("models/hagarmissile.mdl"); + precache_model ("models/rocketmissile.mdl"); + precache_model ("models/bullet.mdl"); + precache_model ("models/casing_bronze.mdl"); + precache_model ("models/casing_shell.mdl"); + precache_model ("models/casing_steel.mdl"); + precache_model ("models/beam.mdl"); + precache_model ("models/flash.md3"); + precache_model ("models/laser.mdl"); + precache_model ("models/elaser.mdl"); + precache_model ("models/fire.mdl"); + + precache_model ("models/items/g_h1.md3"); + precache_model ("models/items/g_h25.md3"); + precache_model ("models/items/g_h100.md3"); + + precache_model ("models/items/g_a1.md3"); + precache_model ("models/items/g_a25.md3"); + + precache_model ("models/weapons/w_uzi.zym"); + precache_model ("models/weapons/w_laser.zym"); + precache_model ("models/weapons/w_shotgun.zym"); + precache_model ("models/weapons/w_gl.zym"); + precache_model ("models/weapons/w_electro.zym"); + precache_model ("models/weapons/w_crylink.zym"); + precache_model ("models/weapons/w_nex.zym"); + precache_model ("models/weapons/w_hagar.zym"); + precache_model ("models/weapons/w_rl.zym"); + + precache_model ("models/items/a_shells.md3"); + precache_model ("models/items/a_cells.md3"); + precache_model ("models/items/a_rockets.md3"); + precache_model ("models/items/a_bullets.zym"); + + precache_model ("models/items/g_strength.zym"); + precache_model ("models/items/g_invincible.zym"); + precache_model ("models/items/g_slowmo.zym"); + precache_model ("models/items/g_speed.zym"); + + precache_model ("models/gibs/eye.md3"); + precache_model ("models/gibs/gib1.md3"); + precache_model ("models/gibs/gib2.mdl"); + precache_model ("models/gibs/gib3.mdl"); + precache_model ("models/gibs/gib4.mdl"); + precache_model ("models/gibs/bloodyskull.md3"); + + precache_sound ("weapons/lasergun_fire.wav"); + precache_sound ("weapons/laserimpact.wav"); + precache_sound ("weapons/uzi_fire.wav"); + precache_sound ("weapons/shotgun_fire.wav"); + precache_sound ("weapons/grenade_fire.wav"); + precache_sound ("weapons/grenade_impact.wav"); + precache_sound ("weapons/grenade_bounce.wav"); + precache_sound ("weapons/nexfire.wav"); + precache_sound ("weapons/neximpact.wav"); + precache_sound ("weapons/electro_fire.wav"); + precache_sound ("weapons/rocket_fire.wav"); + precache_sound ("weapons/rocket_impact.wav"); + precache_sound ("weapons/hagar_fire.wav"); + precache_sound ("weapons/hagar_fire2.wav"); + precache_sound ("weapons/hagexp1.wav"); + precache_sound ("weapons/hagexp2.wav"); + precache_sound ("weapons/hagexp3.wav"); + precache_sound ("weapons/ric1.wav"); + precache_sound ("weapons/ric2.wav"); + precache_sound ("weapons/ric3.wav"); + precache_sound ("weapons/weaponpickup.wav"); + precache_sound ("weapons/crylink.wav"); + precache_sound ("weapons/crylink2.wav"); + precache_sound ("weapons/plasmahit.wav"); + precache_sound ("weapons/uzi_fire_secondary.wav"); + precache_sound ("weapons/tink1.wav"); + + precache_sound ("misc/itempickup.wav"); + precache_sound ("misc/bodyimpact1.wav"); + precache_sound ("misc/bodyimpact2.wav"); + precache_sound ("misc/enemyimpact.wav"); + precache_sound ("misc/teleport.wav"); + precache_sound ("misc/megahealth.wav"); + precache_sound ("misc/mediumhealth.wav"); + precache_sound ("misc/gib.wav"); + precache_sound ("misc/jumppad.wav"); + precache_sound ("misc/talk.wav"); // temp + + precache_sound ("misc/itemrespawn.wav"); + + precache_sound ("player/hitground1.wav"); + precache_sound ("player/hitground2.wav"); + precache_sound ("demon/dland2.wav"); + + + // plays music for the level if there is any + if (self.noise) + { + precache_sound (self.noise); + ambientsound ('0 0 0', self.noise, 1.00, ATTN_NONE); + } + +} + +void light (void) +{ + makestatic (self); +} \ No newline at end of file diff --git a/qcsrc/gamec/sv_main.c b/qcsrc/gamec/sv_main.c new file mode 100644 index 000000000..009c67f2c --- /dev/null +++ b/qcsrc/gamec/sv_main.c @@ -0,0 +1,19 @@ +/* +============= +StartFrame + +Called before each frame by the server +============= +*/ +void StartFrame (void) +{ + sv_maxspeed = cvar ("sv_maxspeed"); + sv_friction = cvar ("sv_friction"); + sv_accelerate = cvar ("sv_accelerate"); + sv_stopspeed = cvar ("sv_stopspeed"); + + cl_rollangle = cvar ("cl_rollangle") * 4; + cl_divspeed = 1 / cvar ("cl_rollspeed"); + + BotFrame (); +} \ No newline at end of file diff --git a/qcsrc/gamec/sv_stats.c b/qcsrc/gamec/sv_stats.c new file mode 100644 index 000000000..bd1188c26 --- /dev/null +++ b/qcsrc/gamec/sv_stats.c @@ -0,0 +1,5 @@ + +// Accuracy +.float shots; +.float hits; + diff --git a/qcsrc/gamec/sys.h b/qcsrc/gamec/sys.h new file mode 100644 index 000000000..e6e007eb3 --- /dev/null +++ b/qcsrc/gamec/sys.h @@ -0,0 +1,145 @@ + +// DO NOT modify the contents of this file, or you will risk incompatibility with the game engine. + +entity self; +entity other; +entity world; + +float time; +float frametime; +float force_retouch; +string mapname; +float deathmatch; +float coop; +float teamplay; +float serverflags; +float total_secrets; +float total_monsters; +float found_secrets; +float killed_monsters; +float parm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16; +vector v_forward, v_up, v_right; +float trace_allsolid; +float trace_startsolid; +float trace_fraction; +vector trace_endpos; +vector trace_plane_normal; +float trace_plane_dist; +entity trace_ent; +float trace_inopen; +float trace_inwater; + +entity msg_entity; + +void main (void); +void StartFrame (void); +void PlayerPreThink (void); +void PlayerPostThink (void); +void ClientKill (void); +void ClientConnect (void); +void PutClientInServer (void); +void ClientDisconnect (void); +void SetNewParms (void); +void SetChangeParms (void); + +///////////////////////////////////////////////////////// +void end_sys_globals; +///////////////////////////////////////////////////////// + +.float modelindex; + +.vector absmin, absmax; + +.float ltime; +.float movetype; +.float solid; + +.vector origin; +.vector oldorigin; +.vector velocity; +.vector angles; +.vector avelocity; +.vector punchangle; + +.string classname; +.string model; + +.float frame; +.float skin; +.float effects; + +.vector mins, maxs; +.vector size; + +.void() touch; +.void() use; +.void() think; +.void() blocked; + +.float nextthink; + +.entity groundentity; + +.float health; +.float frags; + +.float weapon; +.string weaponmodel; +.float weaponframe; + +.float currentammo; +.float ammo_shells, ammo_nails, ammo_rockets, ammo_cells; +.float items; + +.float takedamage; + +.entity chain; + +.float deadflag; + +.vector view_ofs; + +.float button0; +.float button1; +.float button2; +.float impulse; +.float fixangle; +.vector v_angle; +.float idealpitch; + +.string netname; +.entity enemy; + +.float flags; +.float colormap; +.float team; +.float max_health; +.float teleport_time; +.float armortype; +.float armorvalue; +.float waterlevel; +.float watertype; +.float ideal_yaw; +.float yaw_speed; + +.entity aiment; +.entity goalentity; + +.float spawnflags; + +.string target; +.string targetname; + +.float dmg_take; +.float dmg_save; +.entity dmg_inflictor; + +.entity owner; +.vector movedir; +.string message; +.float sounds; +.string noise, noise1, noise2, noise3; + +///////////////////////////////////////////////////////// +void end_sys_fields; +///////////////////////////////////////////////////////// diff --git a/qcsrc/gamec/t_halflife.c b/qcsrc/gamec/t_halflife.c new file mode 100644 index 000000000..4825d316e --- /dev/null +++ b/qcsrc/gamec/t_halflife.c @@ -0,0 +1,58 @@ + +.float roomtype; +.float radius; +.float pitch; +.float renderamt; +.float rendermode; +.vector rendercolor; + +void() weapon_crossbow = {}; +void() weapon_handgrenade = {}; +void() ammo_crossbow = {}; +void() ammo_9mmclip = {}; +void() ammo_gaussclip = {}; +void() weapon_rpg = {}; +void() weapon_357 = {}; +void() ammo_ARgrenades = {}; +void() item_battery = {}; +void() ammo_rpgclip = {}; +void() weapon_9mmAR = {}; +void() weapon_tripmine = {}; +void() weapon_snark = {}; +void() ammo_buckshot = {}; +void() ammo_9mmAR = {}; +void() ammo_357 = {}; +void() weapon_gauss = {}; +void() weapon_hornetgun = {}; +//void() weapon_shotgun = {}; +void() item_healthkit = {}; +void() item_longjump = {}; +void() item_antidote = {}; +void() func_recharge = {}; +void() info_node = {}; +void() env_sound = {}; +void() light_spot = {}; +void() func_healthcharger = {}; + + +void() func_ladder_touch = +{ + if (other.classname != "player") + return; + other.ladder_time = time + 0.1; + other.ladder_entity = self; +}; + +void() func_ladder = +{ + InitTrigger (); + self.touch = func_ladder_touch; +}; + +void() func_water = +{ + self.solid = SOLID_TRIGGER; + setmodel (self, self.model); // set size and link into world + self.touch = func_ladder_touch; +}; + diff --git a/qcsrc/gamec/t_items.c b/qcsrc/gamec/t_items.c new file mode 100644 index 000000000..13ad03c02 --- /dev/null +++ b/qcsrc/gamec/t_items.c @@ -0,0 +1,148 @@ + +void Item_Respawn (void) +{ + self.model = self.mdl; // restore original model + self.solid = SOLID_TRIGGER; // allow it to be touched again + sound (self, CHAN_VOICE, "misc/itemrespawn.wav", 1, ATTN_NORM); // play respawn sound + setorigin (self, self.origin); +} + +void Item_Touch (void) +{ + local entity oldself; + local float _switchweapon; + + if (self.solid != SOLID_TRIGGER) + return; + + if (other.classname != "player" && other.classname != "bot") + return; + if (other.health <= 0) + return; + + sound (self, CHAN_BODY, self.noise, 1, ATTN_NORM); + + // if the player is using their best weapon before items are given, they + // probably want to switch to an even better weapon after items are given + _switchweapon = other.weapon == W_GetBestWeapon(other); + + if (self.ammo_shells) + other.ammo_shells = min (other.ammo_shells + self.ammo_shells, 100); + if (self.ammo_nails) + other.ammo_nails = min (other.ammo_nails + self.ammo_nails, 200); + if (self.ammo_rockets) + other.ammo_rockets = min (other.ammo_rockets + self.ammo_rockets, 100); + if (self.ammo_cells) + other.ammo_cells = min (other.ammo_cells + self.ammo_cells, 100); + + if (self.items & IT_UZI) W_GiveWeapon (other, IT_UZI); + if (self.items & IT_SHOTGUN) W_GiveWeapon (other, IT_SHOTGUN); + if (self.items & IT_GRENADE_LAUNCHER) W_GiveWeapon (other, IT_GRENADE_LAUNCHER); + if (self.items & IT_ELECTRO) W_GiveWeapon (other, IT_ELECTRO); + if (self.items & IT_NEX) W_GiveWeapon (other, IT_NEX); + if (self.items & IT_HAGAR) W_GiveWeapon (other, IT_HAGAR); + if (self.items & IT_ROCKET_LAUNCHER) W_GiveWeapon (other, IT_ROCKET_LAUNCHER); + if (self.items & IT_CRYLINK) W_GiveWeapon (other, IT_CRYLINK); + + if (self.strength_finished) + other.strength_finished = max(other.strength_finished, time) + self.strength_finished; + if (self.invincible_finished) + other.invincible_finished = max(other.invincible_finished, time) + self.invincible_finished; + if (self.speed_finished) + other.speed_finished = max(other.speed_finished, time) + self.speed_finished; + if (self.slowmo_finished) + other.slowmo_finished = max(other.slowmo_finished, time) + self.slowmo_finished; + + if (self.max_health) + other.health = other.health + self.max_health; + if (self.armorvalue) + other.armorvalue = other.armorvalue + self.armorvalue; + + if (other.classname == "player") + { + sprint (other, "You got the "); + sprint (other, self.netname); + sprint (other, "\n"); + } + + oldself = self; + self = other; + + if (_switchweapon) + self.weapon = W_GetBestWeapon(self); + + W_UpdateWeapon (); + W_UpdateAmmo (); + + self = oldself; + + if (self.norespawn) + remove (self); + else + { + self.solid = SOLID_NOT; + self.model = string_null; + self.nextthink = time + self.respawntime; + self.think = Item_Respawn; + setorigin (self, self.origin); + } +} + +void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, string itemname, float itemid, float itemflags) +{ + if (game & (GAME_INSTAGIB | GAME_ROCKET_ARENA)) + { + remove (self); + return; + } + self.mdl = itemmodel; + self.noise = pickupsound; + // let mappers override respawntime + if (!self.respawntime) + self.respawntime = defaultrespawntime; + self.netname = itemname; + self.items = itemid; + self.flags = FL_ITEM | itemflags; + setmodel (self, self.mdl); + if (itemflags & FL_WEAPON) + setsize (self, '-12 -12 -12', '12 12 12'); + else + setsize (self, '-8 -8 -8', '8 8 8'); + self.movetype = MOVETYPE_TOSS; + self.solid = SOLID_TRIGGER; + self.touch = Item_Touch; +} + +void weapon_uzi (void) {self.ammo_nails = 50;StartItem ("models/weapons/g_uzi.md3", "weapons/weaponpickup.wav", 30, "Uzi", IT_UZI, FL_WEAPON);} +void weapon_shotgun (void) {self.ammo_shells = 10;StartItem ("models/weapons/g_shotgun.md3", "weapons/weaponpickup.wav", 30, "Shotgun", IT_SHOTGUN, FL_WEAPON);} +void weapon_grenadelauncher (void) {self.ammo_rockets = 10;StartItem ("models/weapons/g_gl.md3", "weapons/weaponpickup.wav", 30, "Grenade Launcher", IT_GRENADE_LAUNCHER, FL_WEAPON);} +void weapon_electro (void) {self.ammo_cells = 30;StartItem ("models/weapons/g_electro.md3", "weapons/weaponpickup.wav", 30, "Electro", IT_ELECTRO, FL_WEAPON);} +void weapon_crylink (void) {self.ammo_cells = 30;StartItem ("models/weapons/g_crylink.md3", "weapons/weaponpickup.wav", 30, "Crylink", IT_CRYLINK, FL_WEAPON);} +void weapon_nex (void) {self.ammo_cells = 30;StartItem ("models/weapons/g_nex.md3", "weapons/weaponpickup.wav", 30, "Nex Gun", IT_NEX, FL_WEAPON);} +void weapon_hagar (void) {self.ammo_rockets = 10;StartItem ("models/weapons/g_hagar.md3", "weapons/weaponpickup.wav", 30, "Hagar", IT_HAGAR, FL_WEAPON);} +void weapon_rocketlauncher (void) {self.ammo_rockets = 10;StartItem ("models/weapons/g_rl.md3", "weapons/weaponpickup.wav", 30, "Rocket Launcher", IT_ROCKET_LAUNCHER, FL_WEAPON);} + +void item_rockets (void) {self.ammo_rockets = 25;StartItem ("models/items/a_rockets.md3", "misc/itempickup.wav", 30, "rockets", IT_ROCKETS, 0);} +void item_bullets (void) {self.ammo_nails = 100;StartItem ("models/items/a_bullets.zym", "misc/itempickup.wav", 30, "bullets", IT_NAILS, 0);} +void item_cells (void) {self.ammo_cells = 50;StartItem ("models/items/a_cells.md3", "misc/itempickup.wav", 30, "cells", IT_CELLS, 0);} +void item_shells (void) {self.ammo_shells = 50;StartItem ("models/items/a_shells.md3", "misc/itempickup.wav", 30, "shells", IT_SHELLS, 0);} + +void item_armor1 (void) {self.armorvalue = 5;StartItem ("models/items/g_a1.md3", "misc/itempickup.wav", 30, "Armor Shard", 0, 0);} +void item_armor25 (void) {self.armorvalue = 100;StartItem ("models/items/g_a25.md3", "misc/itempickup.wav", 30, "Armor", 0, 0);} +void item_health1 (void) {self.max_health = 5;StartItem ("models/items/g_h1.md3", "misc/itempickup.wav", 30, "5 Health", 0, 0);} +void item_health25 (void) {self.max_health = 25;StartItem ("models/items/g_h25.md3", "misc/mediumhealth.wav", 30, "25 Health", 0, 0);} +void item_health100 (void) {self.max_health = 100;StartItem ("models/items/g_h100.md3", "misc/megahealth.wav", 30, "100 Health", 0, 0);} + +//void item_strength (void) {self.strength_finished = 30;StartItem ("models/items/g_strength.zym", "misc/itempickup.wav", 120, "Strength Powerup", IT_STRENGTH, 0);} +//void item_invincible (void) {self.invincible_finished = 30;StartItem ("models/items/g_invincible.zym", "misc/itempickup.wav", 120, "Invulnerability", IT_INVINCIBLE, 0);} +//void item_speed (void) {self.speed_finished = 30;StartItem ("models/items/g_speed.zym", "misc/itempickup.wav", 120, "Speed Powerup", IT_SPEED, 0);} +//void item_slowmo (void) {self.slowmo_finished = 30;StartItem ("models/items/g_slowmo.zym", "misc/itempickup.wav", 120, "Slow Motion Powerup", IT_SLOWMO, 0);} + +void misc_models (void) +{ + precache_model (self.model); + setmodel (self, self.model); + setsize (self, self.mins, self.maxs); +} + + diff --git a/qcsrc/gamec/t_jumppads.c b/qcsrc/gamec/t_jumppads.c new file mode 100644 index 000000000..ef4012ddf --- /dev/null +++ b/qcsrc/gamec/t_jumppads.c @@ -0,0 +1,80 @@ +float PUSH_ONCE = 1; +float PUSH_SILENT = 2; + +void() trigger_push_touch = +{ + local float flighttime, dist, grav; + local vector org; + + if (!other.health) + return; + + org = other.origin; + + sound (other, CHAN_ITEM, "misc/jumppad.wav", 1, ATTN_NORM); + + // figure out how long it will take to hit the point considering gravity + grav = cvar("sv_gravity"); + flighttime = sqrt((self.enemy.origin_z - org_z) / (0.5 * grav)); + if (!flighttime) + return; + + // how far in X and Y to move + self.movedir = (self.enemy.origin - org); + self.movedir_z = 0; + dist = vlen(self.movedir); + + // finally calculate the velocity + self.movedir = normalize(self.movedir) * (dist / flighttime); + self.movedir_z = flighttime * grav; + + other.velocity = self.movedir; + other.flags = other.flags - (other.flags & FL_ONGROUND); + + if (self.spawnflags & PUSH_ONCE) + { + self.touch = SUB_Null; + self.think = SUB_Remove; + self.nextthink = time; + } +}; + +void() trigger_push_findtarget = +{ + // find the target + self.enemy = find(world, targetname, self.target); + if (!self.enemy) + objerror("trigger_push: target not found\n"); +}; + +void() trigger_push = +{ + if (self.angles != '0 0 0') + SetMovedir (); + + self.solid = SOLID_TRIGGER; + setmodel (self, self.model); + self.movetype = MOVETYPE_NONE; + self.modelindex = 0; + self.model = ""; + + self.touch = trigger_push_touch; + + // check if this is a jump pad + if (self.target) + { + self.think = trigger_push_findtarget; + self.nextthink = time + 0.2; + } + else + { + // normal push setup + if (!self.speed) + self.speed = 1000; + self.movedir = self.movedir * self.speed * 10; + } +}; + +void() target_push = { trigger_push(); }; +void() info_notnull = {}; +void() target_position = {}; diff --git a/qcsrc/gamec/t_plats.c b/qcsrc/gamec/t_plats.c new file mode 100644 index 000000000..3ea01f251 --- /dev/null +++ b/qcsrc/gamec/t_plats.c @@ -0,0 +1,446 @@ +void() plat_center_touch; +void() plat_outside_touch; +void() plat_trigger_use; +void() plat_go_up; +void() plat_go_down; +void() plat_crush; +float PLAT_LOW_TRIGGER = 1; + +void() plat_spawn_inside_trigger = +{ + local entity trigger; + local vector tmin, tmax; + + trigger = spawn(); + trigger.touch = plat_center_touch; + trigger.movetype = MOVETYPE_NONE; + trigger.solid = SOLID_TRIGGER; + trigger.enemy = self; + + tmin = self.mins + '25 25 0'; + tmax = self.maxs - '25 25 -8'; + tmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8); + if (self.spawnflags & PLAT_LOW_TRIGGER) + tmax_z = tmin_z + 8; + + if (self.size_x <= 50) + { + tmin_x = (self.mins_x + self.maxs_x) / 2; + tmax_x = tmin_x + 1; + } + if (self.size_y <= 50) + { + tmin_y = (self.mins_y + self.maxs_y) / 2; + tmax_y = tmin_y + 1; + } + + setsize (trigger, tmin, tmax); +}; + +void() plat_hit_top = +{ + sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); + self.state = 1; + self.think = plat_go_down; + self.nextthink = self.ltime + 3; +}; + +void() plat_hit_bottom = +{ + sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); + self.state = 2; +}; + +void() plat_go_down = +{ + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); + self.state = 3; + SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom); +}; + +void() plat_go_up = +{ + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); + self.state = 4; + SUB_CalcMove (self.pos1, self.speed, plat_hit_top); +}; + +void() plat_center_touch = +{ + if (other.classname != "player") + return; + + if (other.health <= 0) + return; + + self = self.enemy; + if (self.state == 2) + plat_go_up (); + else if (self.state == 1) + self.nextthink = self.ltime + 1; // delay going down +}; + +void() plat_outside_touch = +{ + if (other.classname != "player") + return; + + if (other.health <= 0) + return; + + self = self.enemy; + if (self.state == 1) + plat_go_down (); +}; + +void() plat_trigger_use = +{ + if (self.think) + return; // allready activated + plat_go_down(); +}; + + +void() plat_crush = +{ + if (self.state == 4) + plat_go_down (); + else if (self.state == 3) + plat_go_up (); + else + objerror ("plat_crush: bad self.state\n"); +}; + +void() plat_use = +{ + self.use = SUB_Null; + if (self.state != 4) + objerror ("plat_use: not in up state"); + plat_go_down(); +}; + + +.string sound1, sound2; + +void() func_plat = +{ + if (!self.t_length) + self.t_length = 80; + if (!self.t_width) + self.t_width = 10; + + if (self.sounds == 0) + self.sounds = 2; + + if (self.sounds == 1) + { + precache_sound ("plats/plat1.wav"); + precache_sound ("plats/plat2.wav"); + self.noise = "plats/plat1.wav"; + self.noise1 = "plats/plat2.wav"; + } + + if (self.sounds == 2) + { + precache_sound ("plats/medplat1.wav"); + precache_sound ("plats/medplat2.wav"); + self.noise = "plats/medplat1.wav"; + self.noise1 = "plats/medplat2.wav"; + } + + if (self.sound1) + { + precache_sound (self.sound1); + self.noise = self.sound1; + } + if (self.sound2) + { + precache_sound (self.sound2); + self.noise1 = self.sound2; + } + + self.mangle = self.angles; + self.angles = '0 0 0'; + + self.classname = "plat"; + self.solid = SOLID_BSP; + self.movetype = MOVETYPE_PUSH; + setorigin (self, self.origin); + setmodel (self, self.model); + setsize (self, self.mins , self.maxs); + + self.blocked = plat_crush; + if (!self.speed) + self.speed = 150; + self.pos1 = self.origin; + self.pos2 = self.origin; + self.pos2_z = self.origin_z - self.size_z + 8; + + self.use = plat_trigger_use; + + plat_spawn_inside_trigger (); // the "start moving" trigger + + if (self.targetname) + { + self.state = 4; + self.use = plat_use; + } + else + { + setorigin (self, self.pos2); + self.state = 2; + } +}; + + +/* +void() train_next; +void() func_train_find; + +void() train_blocked = +{ + if (time < self.attack_finished) + return; + self.attack_finished = time + 0.5; +}; +void() train_use = +{ + if (self.think != func_train_find) + return; + train_next(); +}; + +void() train_wait = +{ + if (self.wait) + { + self.nextthink = self.ltime + self.wait; + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); + } + else + self.nextthink = self.ltime + 0.1; + + self.think = train_next; +}; + +void() train_next = +{ + local entity targ; + + targ = find (world, targetname, self.target); + self.target = targ.target; + if (!self.target) + objerror ("train_next: no next target"); + if (targ.wait) + self.wait = targ.wait; + else + self.wait = 0; + sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); + SUB_CalcMove (targ.origin - self.mins, self.speed, train_wait); +}; + +void() func_train_find = +{ + local entity targ; + + targ = find (world, targetname, self.target); + self.target = targ.target; + setorigin (self, targ.origin - self.mins); + if (!self.targetname) + { // not triggered, so start immediately + self.nextthink = self.ltime + 0.1; + self.think = train_next; + } +}; + + +void() func_train = +{ + if (!self.speed) + self.speed = 100; + if (!self.target) + objerror ("func_train without a target"); + + if (self.sounds == 0) + { + self.noise = ("misc/null.wav"); + precache_sound ("misc/null.wav"); + self.noise1 = ("misc/null.wav"); + precache_sound ("misc/null.wav"); + } + + if (self.sounds == 1) + { + self.noise = ("plats/train2.wav"); + precache_sound ("plats/train2.wav"); + self.noise1 = ("plats/train1.wav"); + precache_sound ("plats/train1.wav"); + } + + self.solid = SOLID_BSP; + self.movetype = MOVETYPE_PUSH; + self.blocked = train_blocked; + self.use = train_use; + self.classname = "train"; + + setmodel (self, self.model); + setsize (self, self.mins , self.maxs); + setorigin (self, self.origin); + self.nextthink = self.ltime + 0.1; + self.think = func_train_find; +}; +*/ + +void() train_next; +void() train_wait = +{ + self.think = train_next; + self.nextthink = self.ltime + self.wait; +}; + +void() train_next = +{ + local entity targ; + targ = find(world, targetname, self.target); + self.target = targ.target; + if (!self.target) + objerror("train_next: no next target"); + self.wait = targ.wait; + if (!self.wait) + self.wait = 0.1; + if (targ.speed) + SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait); + else + SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait); +}; + +void() func_train_find = +{ + local entity targ; + targ = find(world, targetname, self.target); + self.target = targ.target; + setorigin(self, targ.origin - self.mins); + self.nextthink = self.ltime + 1; + self.think = train_next; +}; + +/*QUAKED func_train (0 .5 .8) ? +Ridable platform, targets path_corner path to follow. +speed : speed the train moves (can be overridden by each path_corner) +target : targetname of first path_corner (starts here) +*/ +void() func_train = +{ + if (!self.target) + objerror("func_train without a target"); + if (!self.speed) + self.speed = 100; + + self.solid = SOLID_BSP; + self.movetype = MOVETYPE_PUSH; + + setmodel(self, self.model); + setsize(self, self.mins, self.maxs); + setorigin(self, self.origin); + + // wait for targets to spawn + self.nextthink = self.ltime + 0.1; + self.think = func_train_find; +}; + +/*QUAKED func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS +Brush model that spins in place on one axis (default Z). +speed : speed to rotate (in degrees per second) +noise : path/name of looping .wav file to play. +*/ +void() func_rotating = +{ + if (self.noise) + { + precache_sound(self.noise); + ambientsound(self.origin, self.noise, 1, ATTN_IDLE); + } + if (!self.speed) + self.speed = 100; + // FIXME: test if this turns the right way, then remove this comment (negate as needed) + if (self.spawnflags & 4) // X (untested) + self.avelocity = '0 0 1' * self.speed; + // FIXME: test if this turns the right way, then remove this comment (negate as needed) + else if (self.spawnflags & 8) // Y (untested) + self.avelocity = '1 0 0' * self.speed; + // FIXME: test if this turns the right way, then remove this comment (negate as needed) + else // Z + self.avelocity = '0 1 0' * self.speed; + + self.solid = SOLID_BSP; + self.movetype = MOVETYPE_PUSH; + + setmodel(self, self.model); + setsize(self, self.mins, self.maxs); + setorigin(self, self.origin); + + // wait for targets to spawn + self.nextthink = self.ltime + 999999999; + self.think = SUB_Null; +}; + +.float height; +.float phase; +void() func_bobbing_controller_think = +{ + local vector v; + self.nextthink = time + 0.1; + // calculate sinewave using makevectors + makevectors((time * self.owner.cnt + self.owner.phase) * '0 1 0'); + v = self.owner.destvec + self.owner.movedir * v_forward_y; + // * 10 so it will arrive in 0.1 sec + self.owner.velocity = (v - self.owner.origin) * 10; +}; + +/*QUAKED func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS +Brush model that moves back and forth on one axis (default Z). +speed : how long one cycle takes in seconds (default 4) +height : how far the cycle moves (default 32) +phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0) +noise : path/name of looping .wav file to play. +*/ +void() func_bobbing = +{ + local entity controller; + if (self.noise) + { + precache_sound(self.noise); + ambientsound(self.origin, self.noise, 1, ATTN_IDLE); + } + if (!self.speed) + self.speed = 4; + if (!self.height) + self.height = 32; + // center of bobbing motion + self.destvec = self.origin; + // time scale to get degrees + self.cnt = 360 / self.speed; + // how far to bob + if (self.spawnflags & 1) // X + self.movedir = '1 0 0' * self.height; + else if (self.spawnflags & 2) // Y + self.movedir = '0 1 0' * self.height; + else // Z + self.movedir = '0 0 1' * self.height; + + self.solid = SOLID_BSP; + self.movetype = MOVETYPE_PUSH; + + setmodel(self, self.model); + setsize(self, self.mins, self.maxs); + setorigin(self, self.origin); + + // wait for targets to spawn + controller = spawn(); + controller.classname = "func_bobbing_controller"; + controller.owner = self; + controller.nextthink = time + 1; + controller.think = func_bobbing_controller_think; + self.nextthink = self.ltime + 999999999; + self.think = SUB_Null; +}; diff --git a/qcsrc/gamec/t_quake.c b/qcsrc/gamec/t_quake.c new file mode 100644 index 000000000..8337712ea --- /dev/null +++ b/qcsrc/gamec/t_quake.c @@ -0,0 +1 @@ +// diff --git a/qcsrc/gamec/t_quake3.c b/qcsrc/gamec/t_quake3.c new file mode 100644 index 000000000..78b4c457c --- /dev/null +++ b/qcsrc/gamec/t_quake3.c @@ -0,0 +1,16 @@ +//*********************** +//QUAKE 3 ENTITIES - So people can play quake3 maps with the nexuiz weapons +//*********************** +void weapon_machinegun (void) {weapon_uzi();} +void weapon_plasmagun (void) {weapon_electro();} +void weapon_lightning (void) {weapon_crylink();} +void weapon_railgun (void) {weapon_nex();} +void weapon_bfg (void) {weapon_hagar();} +void item_slugs (void) {item_cells();} +void item_grenades (void) {item_grenades();} +void item_lightning (void) {item_cells();} +void item_armorshard (void) {item_armor1();} +void item_armorbody (void) {item_armor25();} +void item_health_small (void) {item_health1();} +void item_health_large (void) {item_health25();} +void item_health_mega (void) {item_health100();} diff --git a/qcsrc/gamec/t_teleporters.c b/qcsrc/gamec/t_teleporters.c new file mode 100644 index 000000000..67eb36e58 --- /dev/null +++ b/qcsrc/gamec/t_teleporters.c @@ -0,0 +1,66 @@ + +void Teleport_Touch (void) +{ + if (other.health < 1) + return; + if (other.classname != "player") // FIXME: Make missiles firable through the teleport too + return; + + // Make teleport effect where the player left + sound (other, CHAN_ITEM, "misc/teleport.wav", 1, ATTN_NORM); + te_teleport (other.origin); + + dest = find (world, targetname, self.target); + if (!dest) + objerror ("Teleporter with nonexistant target"); + + // Make teleport effect where the player arrived + sound (other, CHAN_ITEM, "misc/teleport.wav", 1, ATTN_NORM); + makevectors (dest.mangle); + te_teleport (dest.origin + v_forward * 32); + + // Relocate the player + //setorigin (other, dest.origin); + setorigin (other, dest.origin + '0 0 1' * (1 - other.mins_z - 24)); + other.angles = dest.mangle; + other.fixangle = TRUE; + + other.velocity = '0 0 0'; + + other.flags = other.flags - (other.flags & FL_ONGROUND); +} + +void info_teleport_destination (void) +{ + self.mangle = self.angles; + self.angles = '0 0 0'; + + //setorigin (self, self.origin + '0 0 27'); // To fix a mappers' habit as old as Quake + setorigin (self, self.origin); + + if (!self.targetname) + objerror ("Teleport destination without a targetname"); +} + +void misc_teleporter_dest (void) +{ + info_teleport_destination(); +} + +void trigger_teleport (void) +{ + self.angles = '0 0 0'; + + self.solid = SOLID_TRIGGER; + self.movetype = MOVETYPE_NONE; + + setmodel (self, self.model); + + self.model = ""; + self.modelindex = 0; + + self.touch = Teleport_Touch; + + if (!self.target) + objerror ("Teleporter with no target"); +} \ No newline at end of file diff --git a/qcsrc/gamec/w_common.c b/qcsrc/gamec/w_common.c new file mode 100644 index 000000000..308179370 --- /dev/null +++ b/qcsrc/gamec/w_common.c @@ -0,0 +1,587 @@ + +// increments sprite frame, loops when end is hit.. simple + +float TE_SMOKE =77; +void (vector vec) WriteVec = +{ + WriteCoord (MSG_BROADCAST, vec_x); + WriteCoord (MSG_BROADCAST, vec_y); + WriteCoord (MSG_BROADCAST, vec_z); +} +void (vector org, vector dir, float counts) W_Smoke = +{ + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_SMOKE); + WriteVec (org); + WriteVec (dir); + WriteByte (MSG_BROADCAST, counts); +} + +// increments sprite frame, loops when end is hit.. simple +void animate_sprite (float startframe, float frame_count) +{ + if ((self.frame - startframe) >= (frame_count - 1 )) + self.frame = startframe; + else + self.frame = self.frame + 1; +} + +void W_UpdateAmmo (void) +{ + /* + self.items = self.items - (self.items & (IT_NAILS | IT_SHELLS | IT_ROCKETS | IT_CELLS)); + + if (self.weapon == IT_LASER) + self.currentammo = 1; + else if (self.weapon == IT_SHOTGUN) + { + self.currentammo = self.ammo_shells; + self.items = self.items | IT_SHELLS; + } + else if (self.weapon == IT_UZI) + { + self.currentammo = self.ammo_nails; + self.items = self.items | IT_NAILS; + } + else if (self.weapon == IT_GRENADE_LAUNCHER || self.weapon == IT_HAGAR || self.weapon == IT_ROCKET_LAUNCHER) + { + self.currentammo = self.ammo_rockets; + self.items = self.items | IT_ROCKETS; + } + else if (self.weapon == IT_ELECTRO || self.weapon == IT_NEX || self.weapon == IT_CRYLINK) + { + self.currentammo = self.ammo_cells; + self.items = self.items | IT_CELLS; + } + */ +} + +void W_UpdateWeapon (void) +{ + /* + if (self.weapon == IT_LASER) + self.weaponmodel = "models/weapons/w_laser.zym"; + else if (self.weapon == IT_SHOTGUN) + self.weaponmodel = "models/weapons/w_shotgun.zym"; + else if (self.weapon == IT_UZI) + self.weaponmodel = "models/weapons/w_uzi.zym"; + else if (self.weapon == IT_GRENADE_LAUNCHER) + self.weaponmodel = "models/weapons/w_gl.zym"; + else if (self.weapon == IT_ELECTRO) + self.weaponmodel = "models/weapons/w_electro.zym"; + else if (self.weapon == IT_CRYLINK) + self.weaponmodel = "models/weapons/w_crylink.zym"; + else if (self.weapon == IT_NEX) + self.weaponmodel = "models/weapons/w_nex.zym"; + else if (self.weapon == IT_HAGAR) + self.weaponmodel = "models/weapons/w_hagar.zym"; + else if (self.weapon == IT_ROCKET_LAUNCHER) + self.weaponmodel = "models/weapons/w_rl.zym"; + else + objerror ("Illegal weapon - please register your guns please!"); + */ +} + +float W_GetBestWeapon (entity e) +{ + /* + if ((e.items & IT_ROCKET_LAUNCHER) && e.ammo_rockets) + return IT_ROCKET_LAUNCHER; + else if ((e.items & IT_NEX) && e.ammo_cells) + return IT_NEX; + else if ((e.items & IT_HAGAR) && e.ammo_rockets) + return IT_HAGAR; + else if ((e.items & IT_GRENADE_LAUNCHER) && e.ammo_rockets) + return IT_GRENADE_LAUNCHER; + else if ((e.items & IT_ELECTRO) && e.ammo_cells) + return IT_ELECTRO; + else if ((e.items & IT_CRYLINK) && e.ammo_cells) + return IT_CRYLINK; + else if ((e.items & IT_UZI) && e.ammo_nails) + return IT_UZI; + else if ((e.items & IT_SHOTGUN) && e.ammo_shells) + return IT_SHOTGUN; + else + + */ + return IT_LASER; +} + +void W_GiveWeapon (entity e, float wep) +{ + entity oldself; + + if (!wep) + return; + + e.items = e.items | wep; + + oldself = self; + self = e; + + weapon_action(self.weapon, WR_UPDATECOUNTS); +/* + W_UpdateWeapon (); + W_UpdateAmmo (); +*/ + self = oldself; +} +/* +void W_SwitchWeapon (float wep) +{ + float nextwep; + var float noammo = FALSE; + + if (wep == 1) + nextwep = IT_LASER; + else if (wep == 2) + { + nextwep = IT_SHOTGUN; + if (!self.ammo_shells) + noammo = TRUE; + } + else if (wep == 3) + { + nextwep = IT_UZI; + if (!self.ammo_nails) + noammo = TRUE; + } + else if (wep == 4) + { + nextwep = IT_CRYLINK; + if (!self.ammo_cells) + noammo = TRUE; + } + else if (wep == 5) + { + nextwep = IT_ELECTRO; + if (!self.ammo_cells) + noammo = TRUE; + } + else if (wep == 6) + { + nextwep = IT_GRENADE_LAUNCHER; + if (!self.ammo_rockets) + noammo = TRUE; + } + else if (wep == 7) + { + nextwep = IT_HAGAR; + if (!self.ammo_rockets) + noammo = TRUE; + } + else if (wep == 8) + { + nextwep = IT_NEX; + if (!self.ammo_cells) + noammo = TRUE; + } + else if (wep == 9) + { + nextwep = IT_ROCKET_LAUNCHER; + if (!self.ammo_rockets) + noammo = TRUE; + } + + + if (!(self.items & nextwep)) + { + sprint (self, "You don't own that weapon\n"); + return; + } + else if (noammo) + { + sprint (self, "You don't have any ammo for that weapon\n"); + return; + } + + self.weapon = nextwep; + W_UpdateWeapon (); + W_UpdateAmmo (); + self.attack_finished = time + 0.2; + if (self.viewzoom != 1) + self.viewzoom = 1; +} + +void W_NextWeapon (void) +{ + float noammo; + + while (TRUE) + { + noammo = FALSE; + + if (self.weapon == IT_ROCKET_LAUNCHER) + self.weapon = IT_LASER; + else if (self.weapon == IT_LASER) + { + self.weapon = IT_SHOTGUN; + if (!self.ammo_shells) + noammo = TRUE; + } + else if (self.weapon == IT_SHOTGUN) + { + self.weapon = IT_UZI; + if (!self.ammo_nails) + noammo = TRUE; + } + else if (self.weapon == IT_UZI) + { + self.weapon = IT_CRYLINK; + if (!self.ammo_cells) + noammo = TRUE; + } + else if (self.weapon == IT_CRYLINK) + { + self.weapon = IT_ELECTRO; + if (!self.ammo_cells) + noammo = TRUE; + } + else if (self.weapon == IT_ELECTRO) + { + self.weapon = IT_GRENADE_LAUNCHER; + if (!self.ammo_cells) + noammo = TRUE; + } + else if (self.weapon == IT_GRENADE_LAUNCHER) + { + self.weapon = IT_HAGAR; + if (!self.ammo_rockets) + noammo = TRUE; + } + else if (self.weapon == IT_HAGAR) + { + self.weapon = IT_NEX; + if (!self.ammo_rockets) + noammo = TRUE; + } + else if (self.weapon == IT_NEX) + { + self.weapon = IT_ROCKET_LAUNCHER; + if (!self.ammo_cells) + noammo = TRUE; + } + + if ((self.items & self.weapon) && !noammo) + { + W_UpdateWeapon (); + W_UpdateAmmo (); + return; + } + } +} + +void W_PreviousWeapon (void) +{ + float noammo; + + while (TRUE) + { + noammo = FALSE; + + if (self.weapon == IT_SHOTGUN) + self.weapon = IT_LASER; + else if (self.weapon == IT_UZI) + { + self.weapon = IT_SHOTGUN; + if (!self.ammo_shells) + noammo = TRUE; + } + else if (self.weapon == IT_CRYLINK) + { + self.weapon = IT_UZI; + if (!self.ammo_nails) + noammo = TRUE; + } + else if (self.weapon == IT_ELECTRO) + { + self.weapon = IT_CRYLINK; + if (!self.ammo_cells) + noammo = TRUE; + } + else if (self.weapon == IT_GRENADE_LAUNCHER) + { + self.weapon = IT_ELECTRO; + if (!self.ammo_cells) + noammo = TRUE; + } + else if (self.weapon == IT_HAGAR) + { + self.weapon = IT_GRENADE_LAUNCHER; + if (!self.ammo_rockets) + noammo = TRUE; + } + else if (self.weapon == IT_NEX) + { + self.weapon = IT_HAGAR; + if (!self.ammo_rockets) + noammo = TRUE; + } + else if (self.weapon == IT_ROCKET_LAUNCHER) + { + self.weapon = IT_NEX; + if (!self.ammo_cells) + noammo = TRUE; + } + else if (self.weapon == IT_LASER) + { + self.weapon = IT_ROCKET_LAUNCHER; + if (!self.ammo_rockets) + noammo = TRUE; + } + + if ((self.items & self.weapon) && !noammo) + { + W_UpdateWeapon (); + W_UpdateAmmo (); + return; + } + } +} +*/ +float W_CheckAmmo (void) +{ + if (game & (GAME_INSTAGIB | GAME_ROCKET_ARENA)) + return TRUE; + + W_UpdateAmmo (); + if (self.weapon == IT_LASER) + return TRUE; + else if (self.currentammo) + return TRUE; + + self.weapon = W_GetBestWeapon (self); + W_UpdateWeapon (); + + return FALSE; +} + +/* +void FireRailgunBullet (vector src, float bdamage, vector dir, float spread, float deathtype) +{ + vector v, lastpos; + entity saveself, last; + vector org; + org = self.origin + self.view_ofs; + if (bdamage < 1) + return; + + last = self; + lastpos = src; + + while (bdamage > 0) + { + traceline_hitcorpse (self, org, org + v_forward * 4096 + v_right * crandom () * spread + v_up * crandom () * spread, FALSE, self); + last = trace_ent; + lastpos = trace_endpos; + if (trace_fraction != 1.0) + { + if (pointcontents(trace_endpos - dir*4) == CONTENT_SKY) + return; + + if (trace_ent.takedamage || trace_ent.classname == "case") + { + if (trace_ent.classname == "player" || trace_ent.classname == "corpse" || trace_ent.classname == "gib") + te_blood (trace_endpos, dir * bdamage * 16, bdamage); + Damage (trace_ent, self, self, bdamage, deathtype, trace_endpos, dir * bdamage); + } + } + if (last.solid == SOLID_BSP) + bdamage = 0; + } +} +*/ + +void FireRailgunBullet (vector start, vector end, float damage, float dtype) +{ + vector dir; + + dir = normalize (end - start); + traceline_hitcorpse (self, start, end, FALSE, self); + + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, 76); + WriteCoord (MSG_BROADCAST, start_x); + WriteCoord (MSG_BROADCAST, start_y); + WriteCoord (MSG_BROADCAST, start_z); + WriteCoord (MSG_BROADCAST, trace_endpos_x); + WriteCoord (MSG_BROADCAST, trace_endpos_y); + WriteCoord (MSG_BROADCAST, trace_endpos_z); + WriteCoord (MSG_BROADCAST, 0); + WriteCoord (MSG_BROADCAST, 0); + WriteCoord (MSG_BROADCAST, 0); + + if ((trace_fraction != 1.0) && (trace_ent != self) && (pointcontents (trace_endpos) != CONTENT_SKY)) + { + if (trace_ent.classname == "case") + { + Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * damage); + } + else if (trace_ent.classname == "player" || trace_ent.classname == "corpse" || trace_ent.classname == "gib") + { + te_blood (trace_endpos, dir * damage * 16, damage); + Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * damage); + } + } +} + + +void fireBullet (vector dir, float spread, float damage, float dtype) +{ + vector org; + float r; + + // use traceline_hitcorpse to make sure it can hit gibs and corpses too + org = self.origin + self.view_ofs; + traceline_hitcorpse (self, org, org + v_forward * 4096 + v_right * crandom () * spread + v_up * crandom () * spread, FALSE, self); + + // FIXME - causes excessive 'tinking'. Hopefully remove "tink1.wav" from the ricochets with csqc + if ((trace_fraction != 1.0) && (trace_ent != self) && (pointcontents (trace_endpos) != CONTENT_SKY)) + { + if (trace_ent == world) + { + pointcontents (self.origin); + te_gunshot (trace_endpos); + r = random (); + if (r < 0.10) + sound (self, CHAN_IMPACT, "weapons/ric1.wav", 1, ATTN_NORM); + else if (r < 0.20) + sound (self, CHAN_IMPACT, "weapons/ric2.wav", 1, ATTN_NORM); + else if (r < 0.30) + sound (self, CHAN_IMPACT, "weapons/ric3.wav", 1, ATTN_NORM); + } + else if (trace_ent.classname == "case") + { + Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * damage); + } + else if (trace_ent.classname == "player" || trace_ent.classname == "corpse" || trace_ent.classname == "gib") + { + te_blood (trace_endpos, dir * damage * 16, damage); + Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * damage); + sound (trace_ent, CHAN_IMPACT, "misc/enemyimpact.wav", 1, ATTN_NORM); + } + } +} + +/* +void W_Attack (void) +{ + if (self.deadflag != DEAD_NO) + { + if (self.death_time < time) + PutClientInServer(); + + return; + } + + if (!W_CheckAmmo ()) + return; + + makevectors (self.v_angle); + //if (self.weapon == IT_LASER) + // W_Laser_Attack (); + //if (self.weapon == IT_SHOTGUN) + //W_Shotgun_Attack (); + //else if (self.weapon == IT_UZI) + //W_Uzi_Attack (); + if (self.weapon == IT_CRYLINK) + W_Crylink_Attack (); + else if (self.weapon == IT_ELECTRO) + { + W_Electro_Attack (self.electrocount); + self.electrocount = self.electrocount + 1; + if (self.electrocount == 3) + self.electrocount = 0; + } + else if (self.weapon == IT_GRENADE_LAUNCHER) + W_Grenade_Attack (); + else if (self.weapon == IT_HAGAR) + W_Hagar_Attack (); + else if (self.weapon == IT_NEX) + W_Nex_Attack (); + //else if (self.weapon == IT_ROCKET_LAUNCHER) + // W_Rocket_Attack (); + + W_UpdateAmmo (); +} + +void W_SecondaryAttack (void) +{ + if (self.deadflag != DEAD_NO) + { + if (self.death_time < time) + PutClientInServer(); + + return; + } + + if (!W_CheckAmmo ()) + return; + + makevectors (self.v_angle); + //if (self.weapon == IT_LASER) + //W_Laser_Attack2 (); + //if (self.weapon == IT_SHOTGUN) + //W_Shotgun_Attack2 (); + //else if (self.weapon == IT_UZI) + //W_Uzi_Attack2 (); + else if (self.weapon == IT_CRYLINK) + W_Crylink_Attack2 (); + else if (self.weapon == IT_ELECTRO) { + W_Electro_Attack2 (self.electrocount); + self.electrocount = self.electrocount + 1; + if (self.electrocount == 3) + self.electrocount = 0; + } + else if (self.weapon == IT_GRENADE_LAUNCHER) + W_Grenade_Attack2 (); + else if (self.weapon == IT_HAGAR) + W_Hagar_Attack2 (); + else if (self.weapon == IT_NEX) + W_Nex_Attack2 (); + //else if (self.weapon == IT_ROCKET_LAUNCHER) + //W_Rocket_Attack2 (); + + W_UpdateAmmo (); +} + +void W_ThirdAttack (void) +{ + if (self.deadflag != DEAD_NO) + { + if (self.death_time < time) + PutClientInServer(); + + return; + } + + if (!W_CheckAmmo ()) + return; + + makevectors (self.v_angle); + //if (self.weapon == IT_LASER) + //W_Laser_Attack2 (); + //if (self.weapon == IT_SHOTGUN) + //W_Shotgun_Attack2 (); + //else if (self.weapon == IT_UZI) + //W_Uzi_Attack3 (); + else if (self.weapon == IT_CRYLINK) + W_Crylink_Attack2 (); + else if (self.weapon == IT_ELECTRO) { + W_Electro_Attack3 (self.electrocount); + self.electrocount = self.electrocount + 1; + if (self.electrocount == 3) + self.electrocount = 0; + } + else if (self.weapon == IT_GRENADE_LAUNCHER) + W_Grenade_Attack3 (); + else if (self.weapon == IT_HAGAR) + W_Hagar_Attack3 (); + else if (self.weapon == IT_NEX) + W_Nex_Attack2 (); + //else if (self.weapon == IT_ROCKET_LAUNCHER) + //W_Rocket_Attack3 (); + + W_UpdateAmmo (); +} +*/ \ No newline at end of file diff --git a/qcsrc/gamec/w_crylink.c b/qcsrc/gamec/w_crylink.c new file mode 100644 index 000000000..e52263a28 --- /dev/null +++ b/qcsrc/gamec/w_crylink.c @@ -0,0 +1,103 @@ +void() crylink_ready_01; +void() crylink_fire1_01; +void() crylink_fire2_01; +void() crylink_fire3_01; +void() crylink_deselect_01; +void() crylink_select_01; + +float() crylink_check = +{ + if (self.ammo_cells > 0) + return TRUE; + return FALSE; +}; + +void(float req) w_crylink = +{ + if (req == WR_IDLE) + crylink_ready_01(); + else if (req == WR_FIRE1) + weapon_prepareattack(crylink_check, crylink_check, crylink_fire1_01, 0.2); + else if (req == WR_FIRE2) + weapon_prepareattack(crylink_check, crylink_check, crylink_fire2_01, 0.2); + else if (req == WR_FIRE3) + weapon_prepareattack(crylink_check, crylink_check, crylink_fire3_01, 0.2); + else if (req == WR_RAISE) + crylink_select_01(); + else if (req == WR_UPDATECOUNTS) + self.currentammo = self.ammo_cells; + else if (req == WR_DROP) + crylink_deselect_01(); + else if (req == WR_SETUP) + weapon_setup(WEP_CRYLINK, "w_crylink.zym", IT_CELLS); + else if (req == WR_CHECKAMMO) + weapon_hasammo = crylink_check(); +}; + + +void W_Crylink_Touch (void) +{ + self.event_damage = SUB_Null; + te_smallflash(self.origin); + RadiusDamage (self, self.owner, 45, 0, 3, world, 55, IT_CRYLINK); + remove (self); +} + +void W_Crylink_Attack (void) //(float postion) +{ + entity proj; + + sound (self, CHAN_WEAPON, "weapons/crylink2.wav", 1, ATTN_NORM); + + proj = spawn (); + proj.owner = self; + proj.classname = "spike"; + + proj.movetype = MOVETYPE_FLY; + proj.solid = SOLID_BBOX; + + setmodel (proj, "models/sprites/bubbles.spr"); + setsize (proj, '0 0 0', '0 0 0'); + setorigin (proj, self.origin + self.view_ofs + v_forward * 10 + v_right * 5 + v_up * -14); + + proj.velocity = v_forward * 4000; + proj.velocity = proj.velocity + v_right * ( crandom() * 50 ); + proj.velocity = proj.velocity + v_up * ( crandom() * 50 ); + proj.touch = W_Crylink_Touch; + proj.think = SUB_Remove; + proj.nextthink = time + 9; + + proj.glow_color = 10; + proj.glow_size = 30; + + self.attack_finished = time + 0.20; + self.ammo_cells = self.ammo_cells - 0.2; +} + +void W_Crylink_Attack2 (void) +{ + + +} + + +// weapon frames +void() crylink_ready_01 = {weapon_thinkf(WFRAME_IDLE, 0.1, crylink_ready_01); self.weaponentity.state = WS_READY;}; +void() crylink_select_01 = {weapon_thinkf(-1, 0.3, w_ready); weapon_boblayer1(16, '0 0 0');}; +void() crylink_deselect_01 = {weapon_thinkf(-1, 0.3, w_clear); weapon_boblayer1(16, '0 20 -40');}; +void() crylink_fire1_01 = +{ + weapon_doattack(crylink_check, crylink_check, W_Crylink_Attack); + weapon_thinkf(WFRAME_FIRE1, 0.15, crylink_ready_01); +}; +void() crylink_fire2_01 = +{ + weapon_doattack(crylink_check, crylink_check, W_Crylink_Attack); + weapon_thinkf(WFRAME_FIRE1, 0.15, crylink_ready_01); +}; +void() crylink_fire3_01 = +{ + weapon_doattack(crylink_check, crylink_check, W_Crylink_Attack); + weapon_thinkf(WFRAME_FIRE1, 0.15, crylink_ready_01); +}; + diff --git a/qcsrc/gamec/w_electro.c b/qcsrc/gamec/w_electro.c new file mode 100644 index 000000000..c442e0038 --- /dev/null +++ b/qcsrc/gamec/w_electro.c @@ -0,0 +1,299 @@ +void() electro_ready_01; +void() electro_fire1_01; +void() electro_fire2_01; +void() electro_fire3_01; +void() electro_deselect_01; +void() electro_select_01; + +float() electro_check = +{ + if (self.ammo_cells > 0) + return TRUE; + return FALSE; +}; + +void(float req) w_electro = +{ + if (req == WR_IDLE) + electro_ready_01(); + else if (req == WR_FIRE1) + weapon_prepareattack(electro_check, electro_check, electro_fire1_01, 0.4); + else if (req == WR_FIRE2) + weapon_prepareattack(electro_check, electro_check, electro_fire2_01, 2); + else if (req == WR_FIRE3) + weapon_prepareattack(electro_check, electro_check, electro_fire3_01, 0.4); + else if (req == WR_RAISE) + electro_select_01(); + else if (req == WR_UPDATECOUNTS) + self.currentammo = self.ammo_cells; + else if (req == WR_DROP) + electro_deselect_01(); + else if (req == WR_SETUP) + weapon_setup(WEP_ELECTRO, "w_electro.zym", IT_CELLS); + else if (req == WR_CHECKAMMO) + weapon_hasammo = electro_check(); +}; + + +void W_Electro_Touch (void) +{ + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, 79); + WriteCoord (MSG_BROADCAST, self.origin_x); + WriteCoord (MSG_BROADCAST, self.origin_y); + WriteCoord (MSG_BROADCAST, self.origin_z); + WriteCoord (MSG_BROADCAST, 0); // SeienAbunae: groan... Useless clutter + WriteCoord (MSG_BROADCAST, 0); // Sajt: Yeah.. I agree with him + WriteCoord (MSG_BROADCAST, 0); + WriteByte (MSG_BROADCAST, 155); + self.event_damage = SUB_Null; + RadiusDamage (self, self.owner, 50, 10, 70, world, 50, IT_ELECTRO); + sound (self, CHAN_IMPACT, "weapons/plasmahit.wav", 1, ATTN_NORM); + remove (self); +} + +void() W_Electro_Attack +{ + entity proj; + float postion; + + postion = self.electrocount; + makevectors(self.v_angle); + sound (self, CHAN_WEAPON, "weapons/electro_fire.wav", 1, ATTN_NORM); + + proj = spawn (); + proj.owner = self; + proj.classname = "spike"; + + proj.movetype = MOVETYPE_FLY; + proj.solid = SOLID_BBOX; + proj.effects = 1; + + vector org; + org = self.origin + self.view_ofs + v_forward * 18 + v_right * 7 + v_up * -9; + + te_smallflash(org); + + //entity flash; + //flash = spawn (); + //flash.drawonlytoclient; + //setorigin (flash, org); + //setmodel (flash, "models/flash.md3"); + //flash.velocity = v_forward * 9; + //flash.angles = vectoangles (flash.velocity); + //SUB_SetFade (flash, time + 0 + random () * 4); + + setmodel (proj, "models/elaser.mdl"); + setsize (proj, '0 0 0', '0 0 0'); + if (postion == 0) + setorigin (proj, self.origin + self.view_ofs + v_forward * 18 + v_right * 5 + v_up * -14); + if (postion == 1) + setorigin (proj, self.origin + self.view_ofs + v_forward * 18 + v_right * 10 + v_up * -12); + if (postion == 2) + setorigin (proj, self.origin + self.view_ofs + v_forward * 18 + v_right * 15 + v_up * -14); + + proj.velocity = v_forward * 9999; + proj.touch = W_Electro_Touch; + proj.think = SUB_Remove; + proj.nextthink = time + 1.5; + + proj.angles = vectoangles (proj.velocity); + + proj.effects = proj.effects | EF_ADDITIVE; + + self.attack_finished = time + 0.4; + self.ammo_cells = self.ammo_cells - 1; +} + +void W_Plasma_Explode (entity ignore) +{ + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, 79); + WriteCoord (MSG_BROADCAST, self.origin_x); + WriteCoord (MSG_BROADCAST, self.origin_y); + WriteCoord (MSG_BROADCAST, self.origin_z); + WriteCoord (MSG_BROADCAST, 0); // SeienAbunae: groan... Useless clutter + WriteCoord (MSG_BROADCAST, 0); + WriteCoord (MSG_BROADCAST, 0); + WriteByte (MSG_BROADCAST, 155); + + te_customflash (self.origin, 5000, 10, '0 0 1'); + + self.event_damage = SUB_Null; + RadiusDamage (self, self.owner, 100, 50, 100, world, 50, IT_ELECTRO); + sound (self, CHAN_IMPACT, "weapons/plasmahit.wav", 1, ATTN_NORM); + + remove (self); +} + +void W_Plasma_Explode2 (entity ignore) +{ + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, 79); + WriteCoord (MSG_BROADCAST, self.origin_x); + WriteCoord (MSG_BROADCAST, self.origin_y); + WriteCoord (MSG_BROADCAST, self.origin_z); + WriteCoord (MSG_BROADCAST, 0); // SeienAbunae: groan... Useless clutter + WriteCoord (MSG_BROADCAST, 0); + WriteCoord (MSG_BROADCAST, 0); + WriteByte (MSG_BROADCAST, 155); + + te_customflash (self.origin, 5000, 10, '0 0 1'); + + self.event_damage = SUB_Null; + RadiusDamage (self, self.owner, 150, 50, 150, world, 150, IT_ELECTRO); + sound (self, CHAN_IMPACT, "weapons/plasmahit.wav", 1, ATTN_NORM); + + remove (self); +} + +void W_Plasma_FuseExplode (void) +{ + W_Plasma_Explode2 (world); +} + +void W_Plasma_Touch (void) +{ + if (other.classname == "player" || other.classname == "corpse") + W_Plasma_Explode (other); + else + sound (self, CHAN_BODY, "weapons/grenade_bounce.wav", 1, ATTN_NORM); +} + +void W_Plasma_Damage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype) +{ + self.health = self.health - damage; + if (self.health <= 0) + W_Plasma_FuseExplode (); +} + +void() W_Electro_Attack2 +{ + entity Plasma; + float postion; + + postion = self.electrocount; + makevectors(self.v_angle); + sound (self, CHAN_WEAPON, "weapons/electro_fire.wav", 1, ATTN_NORM); + + self.punchangle_x = -4; + + Plasma = spawn (); + Plasma.owner = self; + Plasma.classname = "grenade"; + Plasma.effects = 1; + + Plasma.movetype = MOVETYPE_BOUNCE; + Plasma.solid = SOLID_BBOX; + + vector org; + org = self.origin + self.view_ofs + v_forward * 18 + v_right * 7 + v_up * -9; + te_smallflash(org); + + Plasma.takedamage = DAMAGE_YES; + Plasma.damageforcescale = 4; + Plasma.health = 5; + Plasma.event_damage = W_Plasma_Damage; + + setmodel (Plasma, "models/elaser.mdl"); + setsize (Plasma, '-6 -6 -3', '6 6 3'); + + if (postion == 0) + setorigin (Plasma, self.origin + self.view_ofs + v_forward * 18 + v_right * 0 + v_up * -14); + if (postion == 1) + setorigin (Plasma, self.origin + self.view_ofs + v_forward * 18 + v_right * 10 + v_up * -12); + if (postion == 2) + setorigin (Plasma, self.origin + self.view_ofs + v_forward * 18 + v_right * 20 + v_up * -14); + + Plasma.velocity = v_forward * 900 + v_up * 200; + Plasma.angles = vectoangles (Plasma.velocity); + Plasma.avelocity = '0 0 0'; + + Plasma.touch = W_Plasma_Touch; + Plasma.think = W_Plasma_FuseExplode; + Plasma.nextthink = time + 2; + + Plasma.effects = Plasma.effects | EF_ADDITIVE; + + self.attack_finished = time + 1; + self.ammo_cells = self.ammo_cells - 2; + +} + +void() W_Electro_Attack3 +{ + entity proj; + vector org; + float postion; + + postion = self.electrocount; + makevectors(self.v_angle); + sound (self, CHAN_WEAPON, "weapons/electro_fire.wav", 1, ATTN_NORM); + + proj = spawn (); + proj.owner = self; + proj.classname = "spike"; + + proj.movetype = MOVETYPE_BOUNCEMISSILE; + proj.solid = SOLID_BBOX; + proj.effects = 1; + + org = self.origin + self.view_ofs + v_forward * 18 + v_right * 7 + v_up * -9; + te_smallflash(org); + + setmodel (proj, "models/bullet.mdl"); + setsize (proj, '0 0 0', '0 0 0'); + if (postion == 0) + setorigin (proj, self.origin + self.view_ofs + v_forward * 18 + v_right * 5 + v_up * -14); + if (postion == 1) + setorigin (proj, self.origin + self.view_ofs + v_forward * 18 + v_right * 10 + v_up * -12); + if (postion == 2) + setorigin (proj, self.origin + self.view_ofs + v_forward * 18 + v_right * 15 + v_up * -14); + + proj.velocity = v_forward * 9999; + proj.think = W_Electro_Touch; + proj.nextthink = time + 8; + + proj.effects = proj.effects | EF_ADDITIVE; + + self.attack_finished = time + 0.4; + self.ammo_cells = self.ammo_cells - 1; + +} + + +// weapon frames + +void() electro_ready_01 = {weapon_thinkf(WFRAME_IDLE, 0.1, electro_ready_01); self.weaponentity.state = WS_READY;}; +void() electro_select_01 = {weapon_thinkf(-1, 0.3, w_ready); weapon_boblayer1(16, '0 0 0');}; +void() electro_deselect_01 = {weapon_thinkf(-1, 0.3, w_clear); weapon_boblayer1(16, '0 20 -40');}; +void() electro_fire1_01 = +{ + weapon_doattack(electro_check, electro_check, W_Electro_Attack); + + self.electrocount = self.electrocount + 1; + if (self.electrocount == 3) + self.electrocount = 0; + + weapon_thinkf(WFRAME_FIRE1, 0.3, electro_ready_01); +}; +void() electro_fire2_01 = +{ + weapon_doattack(electro_check, electro_check, W_Electro_Attack2); + + self.electrocount = self.electrocount + 1; + if (self.electrocount == 3) + self.electrocount = 0; + + weapon_thinkf(WFRAME_FIRE2, 0.3, electro_ready_01); +}; +void() electro_fire3_01 = +{ + weapon_doattack(electro_check, electro_check, W_Electro_Attack3); + + self.electrocount = self.electrocount + 1; + if (self.electrocount == 3) + self.electrocount = 0; + + weapon_thinkf(WFRAME_FIRE2, 0.3, electro_ready_01); +}; diff --git a/qcsrc/gamec/w_grenadelauncher.c b/qcsrc/gamec/w_grenadelauncher.c new file mode 100644 index 000000000..96fad830f --- /dev/null +++ b/qcsrc/gamec/w_grenadelauncher.c @@ -0,0 +1,170 @@ +void() glauncher_ready_01; +void() glauncher_fire1_01; +void() glauncher_fire2_01; +void() glauncher_fire3_01; +void() glauncher_deselect_01; +void() glauncher_select_01; + +float() glauncher_check = +{ + if (self.ammo_rockets > 0) + return TRUE; + return FALSE; +}; + +void(float req) w_glauncher = +{ + if (req == WR_IDLE) + glauncher_ready_01(); + else if (req == WR_FIRE1) + weapon_prepareattack(glauncher_check, glauncher_check, glauncher_fire1_01, 0.8); + else if (req == WR_FIRE2) + weapon_prepareattack(glauncher_check, glauncher_check, glauncher_fire2_01, 1); + else if (req == WR_FIRE3) + weapon_prepareattack(glauncher_check, glauncher_check, glauncher_fire3_01, 0.3); + else if (req == WR_RAISE) + glauncher_select_01(); + else if (req == WR_UPDATECOUNTS) + self.currentammo = self.ammo_rockets; + else if (req == WR_DROP) + glauncher_deselect_01(); + else if (req == WR_SETUP) + weapon_setup(WEP_GRENADE_LAUNCHER, "w_gl.zym", IT_ROCKETS); + else if (req == WR_CHECKAMMO) + weapon_hasammo = glauncher_check(); +}; + +void W_Grenade_Explode (entity ignore) +{ + ImpactEffect (self, IT_GRENADE_LAUNCHER); + + self.event_damage = SUB_Null; + RadiusDamage (self, self.owner, 65, 35, 140, world, 400, IT_GRENADE_LAUNCHER); + + remove (self); +} + +void W_Grenade_FuseExplode (void) +{ + W_Grenade_Explode (world); +} + + +void W_Grenade_Touch (void) +{ + if (other.classname == "player" || other.classname == "corpse") + W_Grenade_Explode (other); + else + sound (self, CHAN_BODY, "weapons/grenade_bounce.wav", 1, ATTN_NORM); +} + +void W_Grenade_Damage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype) +{ + self.health = self.health - damage; + if (self.health <= 0) + W_Grenade_FuseExplode(); +} + +void W_Grenade_Attack (void) +{ + makevectors(self.v_angle); + sound (self, CHAN_WEAPON, "weapons/grenade_fire.wav", 1, ATTN_NORM); + self.punchangle_x = -4; + + entity gren; + + gren = spawn (); + gren.owner = self; + gren.classname = "grenade"; + gren.movetype = MOVETYPE_BOUNCE; + gren.solid = SOLID_BBOX; + setmodel(gren, "models/grenademodel.md3"); + setsize(gren, '-14 -5 -4', '16 4 5'); + setorigin(gren, (self.origin + self.view_ofs)); + gren.health = 1; + gren.takedamage = DAMAGE_YES; + if (self.v_angle_x) + gren.velocity = v_forward * (1200 + 1 * 800) + (v_up * 200) + (crandom () * v_right * 10) + (crandom () * v_up * 10); + else + { + gren.velocity = aim (self, 10000) * (1200 + 1 * 800); + gren.velocity_z = 200; + } + gren.avelocity_x = random () * -500 - 500; + gren.mins = normalize (gren.angles) * gren.mins; + gren.maxs = normalize (gren.angles) * gren.maxs; + setsize (gren, gren.mins, gren.maxs); + gren.angles = vectoangles (gren.velocity); + gren.touch = W_Grenade_Explode; + gren.think = W_Grenade_Explode; + gren.nextthink = time + 3; + self.ammo_rockets = self.ammo_rockets - 1; + + + self.attack_finished = time + 0.8; +} + +void W_Grenade_Attack2 (void) +{ + + entity gren; + makevectors(self.v_angle); + sound (self, CHAN_WEAPON, "weapons/grenade_fire.wav", 1, ATTN_NORM); + + self.punchangle_x = -4; + + gren = spawn (); + gren.owner = self; + gren.classname = "grenade"; + + gren.movetype = MOVETYPE_BOUNCE; + gren.solid = SOLID_BBOX; + + gren.takedamage = DAMAGE_YES; + gren.damageforcescale = 4; + gren.health = 10; + gren.event_damage = W_Grenade_Damage; + + setmodel (gren, "models/grenademodel.md3"); + setsize (gren, '-6 -6 -3', '6 6 3'); + + setorigin (gren, self.origin + self.view_ofs + v_forward * 18 + v_right * 5 + v_up * -12); + + gren.velocity = v_forward * 900 + v_up * 200; + gren.angles = vectoangles (gren.velocity); + gren.avelocity = '100 150 100'; + + gren.touch = W_Grenade_Explode; + //gren.think = W_Grenade_FuseExplode; + //gren.nextthink = time + 5; + + self.attack_finished = time + 1; + self.ammo_rockets = self.ammo_rockets - 1; +} + +void W_Grenade_Attack3 (void) +{ + makevectors(self.v_angle); +} + +// weapon frames + +void() glauncher_ready_01 = {weapon_thinkf(WFRAME_IDLE, 0.1, glauncher_ready_01); self.weaponentity.state = WS_READY;}; +void() glauncher_select_01 = {weapon_thinkf(-1, 0.3, w_ready); weapon_boblayer1(16, '0 0 0');}; +void() glauncher_deselect_01 = {weapon_thinkf(-1, 0.3, w_clear); weapon_boblayer1(16, '0 20 -40');}; +void() glauncher_fire1_01 = +{ + weapon_doattack(glauncher_check, glauncher_check, W_Grenade_Attack); + weapon_thinkf(WFRAME_FIRE1, 0.3, glauncher_ready_01); +}; +void() glauncher_fire2_01 = +{ + weapon_doattack(glauncher_check, glauncher_check, W_Grenade_Attack2); + weapon_thinkf(WFRAME_FIRE2, 0.8, glauncher_ready_01); +}; +void() glauncher_fire3_01 = +{ + weapon_doattack(glauncher_check, glauncher_check, W_Grenade_Attack3); + weapon_thinkf(WFRAME_FIRE2, 0.3, glauncher_ready_01); +}; + diff --git a/qcsrc/gamec/w_hagar.c b/qcsrc/gamec/w_hagar.c new file mode 100644 index 000000000..15d8b1fe3 --- /dev/null +++ b/qcsrc/gamec/w_hagar.c @@ -0,0 +1,179 @@ +void() hagar_ready_01; +void() hagar_fire1_01; +void() hagar_fire2_01; +void() hagar_fire3_01; +void() hagar_deselect_01; +void() hagar_select_01; + +float() hagar_check = +{ + if (self.ammo_rockets > 0) + return TRUE; + return FALSE; +}; + +void(float req) w_hagar = +{ + if (req == WR_IDLE) + hagar_ready_01(); + else if (req == WR_FIRE1) + weapon_prepareattack(hagar_check, hagar_check, hagar_fire1_01, 0.2); + else if (req == WR_FIRE2) + weapon_prepareattack(hagar_check, hagar_check, hagar_fire2_01, 0.2); + else if (req == WR_FIRE3) + weapon_prepareattack(hagar_check, hagar_check, hagar_fire3_01, 0); + else if (req == WR_RAISE) + hagar_select_01(); + else if (req == WR_UPDATECOUNTS) + self.currentammo = self.ammo_rockets; + else if (req == WR_DROP) + hagar_deselect_01(); + else if (req == WR_SETUP) + weapon_setup(WEP_HAGAR, "w_hagar.zym", IT_ROCKETS); + else if (req == WR_CHECKAMMO) + weapon_hasammo = hagar_check(); +}; + +void W_Hagar_Explode (void) +{ + ImpactEffect (self, IT_HAGAR); + + self.event_damage = SUB_Null; + RadiusDamage (self, self.owner, 40, 15, 70, world, 100, IT_HAGAR); + + remove (self); +} + +void W_Hagar_Touch (void) +{ + if (other == self.owner) + return; + else if (pointcontents (self.origin) == CONTENT_SKY) + { + remove (self); + return; + } + + W_Hagar_Explode (); +} + +void W_Hagar_Damage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype) +{ + self.health = self.health - damage; + if (self.health <= 0) + W_Hagar_Explode(); +} + +void W_Hagar_Attack (void) +{ + entity missile; + + makevectors(self.v_angle); + sound (self, CHAN_WEAPON, "weapons/hagar_fire.wav", 1, ATTN_NORM); + + missile = spawn (); + missile.owner = self; + missile.classname = "missile"; + + missile.takedamage = DAMAGE_YES; + missile.damageforcescale = 4; + missile.health = 10; + missile.event_damage = W_Hagar_Damage; + + missile.movetype = MOVETYPE_FLY; + missile.solid = SOLID_BBOX; + setmodel (missile, "models/hagarmissile.mdl"); + setsize (missile, '0 0 0', '0 0 0'); + + setorigin (missile, self.origin + self.view_ofs + v_forward * 18 + v_right * 5 + v_up * -12); + + missile.velocity = v_forward * 2000; + missile.velocity = missile.velocity + v_right * ( crandom() * 70 ); + missile.velocity = missile.velocity + v_up * ( crandom() * 30 ); + missile.angles = vectoangles (missile.velocity); + setorigin (missile, self.origin + self.view_ofs + v_forward * 18 + v_right * 5 + v_up * -12); + + missile.touch = W_Hagar_Touch; + missile.think = W_Hagar_Explode; + missile.nextthink = time + 10; + + self.attack_finished = time + 0.2; + self.ammo_rockets = self.ammo_rockets - 0.25; +} + +void W_Hagar_Attack2 (void) +{ + entity missile; + + sound (self, CHAN_WEAPON, "weapons/hagar_fire.wav", 1, ATTN_NORM); + + makevectors(self.v_angle); + + missile = spawn (); + missile.owner = self; + missile.classname = "missile"; + + missile.movetype = MOVETYPE_TOSS; + missile.solid = SOLID_BBOX; + + missile.takedamage = DAMAGE_YES; + missile.damageforcescale = 4; + missile.health = 5; + missile.event_damage = W_Hagar_Damage; + + setmodel (missile, "models/hagarmissile.mdl"); + setsize (missile, '-6 -6 -3', '6 6 3'); + + setorigin (missile, self.origin + self.view_ofs + v_forward * 18 + v_right * 5 + v_up * -12); + + missile.velocity = v_forward * 1400 + v_up * 100; + missile.angles = vectoangles (missile.velocity); + missile.avelocity = '100 10 10'; + + missile.touch = W_Hagar_Touch; + missile.think = W_Hagar_Explode; + missile.nextthink = time + 10; + + self.attack_finished = time + 0.2; + self.ammo_rockets = self.ammo_rockets - 0.25; +} + +void W_Hagar_Attack3 (void) +{ + entity proj; + makevectors(self.v_angle); + proj = findradius (self.origin, 50000); + while (proj) + { + if (proj.classname == "missile" && proj.owner == self) + { + proj.velocity = proj.velocity - v_up * 500; + proj.velocity = proj.velocity - v_forward * 1000; + } + proj = proj.chain; + } + + self.attack_finished = time; +} + + +// weapon frames +void() hagar_ready_01 = {weapon_thinkf(WFRAME_IDLE, 0.1, hagar_ready_01); self.weaponentity.state = WS_READY;}; +void() hagar_select_01 = {weapon_thinkf(-1, 0.3, w_ready); weapon_boblayer1(16, '0 0 0');}; +void() hagar_deselect_01 = {weapon_thinkf(-1, 0.3, w_clear); weapon_boblayer1(16, '0 20 -40');}; +void() hagar_fire1_01 = +{ + weapon_doattack(hagar_check, hagar_check, W_Hagar_Attack); + weapon_thinkf(WFRAME_FIRE1, 0.15, hagar_ready_01); +}; +void() hagar_fire2_01 = +{ + weapon_doattack(hagar_check, hagar_check, W_Hagar_Attack2); + weapon_thinkf(WFRAME_FIRE2, 0.15, hagar_ready_01); +}; +void() hagar_fire3_01 = +{ + weapon_doattack(hagar_check, hagar_check, W_Hagar_Attack3); + weapon_thinkf(WFRAME_FIRE2, 0.15, hagar_ready_01); +}; + diff --git a/qcsrc/gamec/w_laser.c b/qcsrc/gamec/w_laser.c new file mode 100644 index 000000000..ea244d044 --- /dev/null +++ b/qcsrc/gamec/w_laser.c @@ -0,0 +1,135 @@ +void() laser_ready_01; +void() laser_fire1_01; +void() laser_fire2_01; +void() laser_fire3_01; +void() laser_deselect_01; +void() laser_select_01; + +float() laser_check = {return TRUE;}; + +void(float req) w_laser = +{ + if (req == WR_IDLE) + laser_ready_01(); + else if (req == WR_FIRE1) + weapon_prepareattack(laser_check, laser_check, laser_fire1_01, 0.4); + else if (req == WR_FIRE2) + weapon_prepareattack(laser_check, laser_check, laser_fire2_01, 0.3); + else if (req == WR_FIRE3) + weapon_prepareattack(laser_check, laser_check, laser_fire3_01, 0.3); + else if (req == WR_RAISE) + laser_select_01(); + else if (req == WR_UPDATECOUNTS) + self.currentammo = 1; + else if (req == WR_DROP) + laser_deselect_01(); + else if (req == WR_SETUP) + weapon_setup(WEP_LASER, "w_laser.zym", 0); + else if (req == WR_CHECKAMMO) + weapon_hasammo = laser_check(); +}; + +void W_Laser_Touch (void) +{ + vector dir; + + if (other == self.owner) + return; + else if (pointcontents (self.origin) == CONTENT_SKY) + { + remove (self); + return; + } + + dir = normalize (self.owner.origin - self.origin); + + /* + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_FLAMEJET); + WriteCoord (MSG_BROADCAST, self.origin_x); + WriteCoord (MSG_BROADCAST, self.origin_y); + WriteCoord (MSG_BROADCAST, self.origin_z); + WriteCoord (MSG_BROADCAST, 0); // SeienAbunae: groan... Useless clutter + WriteCoord (MSG_BROADCAST, 0); + WriteCoord (MSG_BROADCAST, 0); + WriteByte (MSG_BROADCAST, 155); */ + + + + self.event_damage = SUB_Null; + RadiusDamage (self, self.owner, 15, 20, 50, world, 200, IT_LASER); + sound (self, CHAN_IMPACT, "weapons/laserimpact.wav", 1, ATTN_NORM); + + remove (self); +} + +void W_Laser_Attack (void) +{ + weapon_shotdir(18, 5, -12); + // self.origin + self.view_ofs + v_forward * 18 + v_right * 5 + v_up * -12); + sound (self, CHAN_WEAPON, "weapons/crylink.wav", 1, ATTN_NORM); + //org = self.origin + self.view_ofs + v_forward * 10 + v_right * 5 + v_up * -14; + FireRailgunBullet (self.shotorg, self.origin + self.shotdir*4096, 25, IT_LASER); + te_spark(trace_endpos, self.shotdir, 55); + self.attack_finished = time + 0.400; +} + +void W_Laser_Attack2 (void) +{ + entity missile; + + makevectors(self.v_angle); + + sound (self, CHAN_WEAPON, "weapons/lasergun_fire.wav", 1, ATTN_NORM); + + missile = spawn (); + missile.owner = self; + missile.classname = "spike"; + + missile.movetype = MOVETYPE_FLY; + missile.solid = SOLID_BBOX; + + setmodel (missile, "models/laser.mdl"); + setsize (missile, '0 0 0', '0 0 0'); + setorigin (missile, self.origin + self.view_ofs + v_forward * 18 + v_right * 5 + v_up * -12); + + missile.velocity = v_forward * 1000; + missile.velocity = missile.velocity + v_right * ( crandom() * 45 ); + missile.velocity = missile.velocity + v_up * ( crandom() * 25 ); + missile.angles = vectoangles (missile.velocity); + missile.glow_color = 250; // 244, 250 + missile.glow_size = 30; + missile.touch = W_Laser_Touch; + missile.think = SUB_Remove; + missile.nextthink = time + 9; + + missile.effects = missile.effects | EF_ADDITIVE; + + self.punchangle_x = random () - 0.5; + self.punchangle_y = random () - 0.5; + self.punchangle_z = random () - 0.5; + + self.attack_finished = time + 0.3; +} + +// weapon frames + +void() laser_ready_01 = {weapon_thinkf(WFRAME_IDLE, 0.1, laser_ready_01); self.weaponentity.state = WS_READY;}; +void() laser_select_01 = {weapon_thinkf(-1, 0.3, w_ready); weapon_boblayer1(16, '0 0 0');}; +void() laser_deselect_01 = {weapon_thinkf(-1, 0.3, w_clear); weapon_boblayer1(16, '0 20 -40');}; +void() laser_fire1_01 = +{ + weapon_doattack(laser_check, laser_check, W_Laser_Attack); + weapon_thinkf(WFRAME_FIRE1, 0.3, laser_ready_01); +}; +void() laser_fire2_01 = +{ + weapon_doattack(laser_check, laser_check, W_Laser_Attack2); + weapon_thinkf(WFRAME_FIRE1, 0.2, laser_ready_01); +}; +void() laser_fire3_01 = +{ + weapon_doattack(laser_check, laser_check, W_Laser_Attack2); + weapon_thinkf(WFRAME_FIRE1, 0.2, laser_ready_01); +}; + diff --git a/qcsrc/gamec/w_nex.c b/qcsrc/gamec/w_nex.c new file mode 100644 index 000000000..f1b143ed8 --- /dev/null +++ b/qcsrc/gamec/w_nex.c @@ -0,0 +1,109 @@ +void() nex_ready_01; +void() nex_fire1_01; +void() nex_fire2_01; +void() nex_fire3_01; +void() nex_deselect_01; +void() nex_select_01; + +float() nex_check = +{ + if (self.ammo_cells > 0) + return TRUE; + return FALSE; +}; + +void(float req) w_nex = +{ + if (req == WR_IDLE) + nex_ready_01(); + else if (req == WR_FIRE1) + weapon_prepareattack(nex_check, nex_check, nex_fire1_01, 1); + else if (req == WR_FIRE2) + weapon_prepareattack(nex_check, nex_check, nex_fire2_01, 1); + else if (req == WR_FIRE3) + weapon_prepareattack(nex_check, nex_check, nex_fire3_01, 1); + else if (req == WR_RAISE) + nex_select_01(); + else if (req == WR_UPDATECOUNTS) + self.currentammo = self.ammo_cells; + else if (req == WR_DROP) + nex_deselect_01(); + else if (req == WR_SETUP) + weapon_setup(WEP_NEX, "w_nex.zym", IT_CELLS); + else if (req == WR_CHECKAMMO) + weapon_hasammo = nex_check(); +}; + + +void W_Nex_Attack (void) +{ + vector org; + vector dir; + entity explosion; + + sound (self, CHAN_WEAPON, "weapons/nexfire.wav", 1, ATTN_NORM); + self.punchangle_x = -4; + makevectors(self.v_angle); + org = self.origin + self.view_ofs + v_forward * 18 + v_right * 8 + v_up * -5; + te_smallflash(org); + + if (game & GAME_INSTAGIB) + FireRailgunBullet (org, self.origin + self.view_ofs + v_forward * 4096, 800, IT_NEX); + else + FireRailgunBullet (org, self.origin + self.view_ofs + v_forward * 4096, 100, IT_NEX); + + te_plasmaburn (trace_endpos); + + if (trace_fraction < 1.0) + { + dir = trace_plane_normal * 100; + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_FLAMEJET); + WriteCoord (MSG_BROADCAST, trace_endpos_x); + WriteCoord (MSG_BROADCAST, trace_endpos_y); + WriteCoord (MSG_BROADCAST, trace_endpos_z); + WriteCoord (MSG_BROADCAST, dir_x); + WriteCoord (MSG_BROADCAST, dir_y); + WriteCoord (MSG_BROADCAST, dir_z); + WriteByte (MSG_BROADCAST, 255); + + explosion = spawn (); + setorigin (explosion, trace_endpos); + RadiusDamage (explosion, self, 10, 0, 50, world, 300, IT_ROCKET_LAUNCHER); + remove (explosion); + + PointSound (trace_endpos, "weapons/neximpact.wav", 1, ATTN_NORM); + } + + self.attack_finished = time + 1; + + if (!(game & GAME_INSTAGIB)) + self.ammo_cells = self.ammo_cells - 1; +} + +void W_Nex_Attack2 (void) +{ + makevectors(self.v_angle); +} + +// weapon frames +void() nex_ready_01 = {weapon_thinkf(WFRAME_IDLE, 0.1, nex_ready_01); self.weaponentity.state = WS_READY;}; +void() nex_select_01 = {weapon_thinkf(-1, 0.3, w_ready); weapon_boblayer1(16, '0 0 0');}; +void() nex_deselect_01 = {weapon_thinkf(-1, 0.3, w_clear); weapon_boblayer1(16, '0 20 -40');}; +void() nex_fire1_01 = +{ + weapon_doattack(nex_check, nex_check, W_Nex_Attack); + weapon_thinkf(WFRAME_FIRE1, 0.3, nex_ready_01); +}; +void() nex_fire2_01 = +{ + weapon_doattack(nex_check, nex_check, W_Nex_Attack); + weapon_thinkf(WFRAME_FIRE2, 0.5, nex_ready_01); +}; +void() nex_fire3_01 = +{ + weapon_doattack(nex_check, nex_check, W_Nex_Attack); + weapon_thinkf(WFRAME_FIRE2, 0.5, nex_ready_01); +}; + + diff --git a/qcsrc/gamec/w_rocketlauncher.c b/qcsrc/gamec/w_rocketlauncher.c new file mode 100644 index 000000000..c2545c296 --- /dev/null +++ b/qcsrc/gamec/w_rocketlauncher.c @@ -0,0 +1,151 @@ +void() rlauncher_ready_01; +void() rlauncher_fire1_01; +void() rlauncher_fire2_01; +void() rlauncher_fire3_01; +void() rlauncher_deselect_01; +void() rlauncher_select_01; + +float() rlauncher_check = +{ + if (self.ammo_rockets > 0) + return TRUE; + return FALSE; +}; + +void(float req) w_rlauncher = +{ + if (req == WR_IDLE) + rlauncher_ready_01(); + else if (req == WR_FIRE1) + weapon_prepareattack(rlauncher_check, rlauncher_check, rlauncher_fire1_01, 1.5); + else if (req == WR_FIRE2) + weapon_prepareattack(rlauncher_check, rlauncher_check, rlauncher_fire2_01, 1.5); + else if (req == WR_FIRE3) + weapon_prepareattack(rlauncher_check, rlauncher_check, rlauncher_fire3_01, 1.5); + else if (req == WR_RAISE) + rlauncher_select_01(); + else if (req == WR_UPDATECOUNTS) + self.currentammo = self.ammo_rockets; + else if (req == WR_DROP) + rlauncher_deselect_01(); + else if (req == WR_SETUP) + weapon_setup(WEP_ROCKET_LAUNCHER, "w_rl.zym", IT_ROCKETS); + else if (req == WR_CHECKAMMO) + weapon_hasammo = rlauncher_check(); +}; + + +void W_Rocket_Explode (entity ignore) +{ + ImpactEffect (self, IT_ROCKET_LAUNCHER); + + self.event_damage = SUB_Null; + RadiusDamage (self, self.owner, 130, 50, 170, ignore, 600, IT_ROCKET_LAUNCHER); + + remove (self); +} + +void W_Rocket_Think (void) +{ + W_Rocket_Explode (world); +} + +void W_Rocket_Touch (void) +{ + if (other == self.owner) + return; + else if (pointcontents (self.origin) == CONTENT_SKY) + { + remove (self); + return; + } + else + W_Rocket_Explode (world); +} + +void W_Rocket_Damage (vector hitloc, float damage, entity inflictor, entity attacker, float deathtype) +{ + self.health = self.health - damage; + if (self.health <= 0) + W_Rocket_Explode(world); +} + +void W_Rocket_Attack (void) +{ + entity missile; + vector org; + makevectors (self.v_angle); + sound (self, CHAN_WEAPON, "weapons/rocket_fire.wav", 1, ATTN_NORM); + + missile = spawn (); + missile.owner = self; + missile.classname = "missile"; + + missile.takedamage = DAMAGE_YES; + missile.damageforcescale = 4; + missile.health = 10; + missile.event_damage = W_Rocket_Damage; + + missile.movetype = MOVETYPE_FLY; + missile.solid = SOLID_BBOX; + setmodel (missile, "models/rocketmissile.mdl"); + setsize (missile, '0 0 0', '0 0 0'); + + org = self.origin + self.view_ofs + v_forward * 20 + v_right * 4 + v_up * -15; + + setorigin (missile, org); + missile.velocity = v_forward * 850; + missile.angles = vectoangles (missile.velocity); + + missile.touch = W_Rocket_Touch ; + missile.think = W_Rocket_Think; + missile.nextthink = time + 9; + + self.attack_finished = time + 1.5; + + if (!(game & GAME_ROCKET_ARENA)) + self.ammo_rockets = self.ammo_rockets - 1; +} + +void W_Rocket_Attack2 (void) +{ + entity proj; + proj = findradius (self.origin, 50000); + while (proj) + { + if (proj.classname == "missile" && proj.owner == self) + { + proj.nextthink = time; + } + proj = proj.chain; + } + + self.attack_finished = time; +} + + +void W_Rocket_Attack3 (void) +{ + +} + +// weapon frames + +void() rlauncher_ready_01 = {weapon_thinkf(WFRAME_IDLE, 0.1, rlauncher_ready_01); self.weaponentity.state = WS_READY;}; +void() rlauncher_select_01 = {weapon_thinkf(-1, 0.3, w_ready); weapon_boblayer1(16, '0 0 0');}; +void() rlauncher_deselect_01 = {weapon_thinkf(-1, 0.3, w_clear); weapon_boblayer1(16, '0 20 -40');}; +void() rlauncher_fire1_01 = +{ + weapon_doattack(rlauncher_check, rlauncher_check, W_Rocket_Attack); + weapon_thinkf(WFRAME_FIRE1, 0.3, rlauncher_ready_01); +}; +void() rlauncher_fire2_01 = +{ + weapon_doattack(rlauncher_check, rlauncher_check, W_Rocket_Attack2); + weapon_thinkf(WFRAME_FIRE2, 0.3, rlauncher_ready_01); +}; +void() rlauncher_fire3_01 = +{ + weapon_doattack(rlauncher_check, rlauncher_check, W_Rocket_Attack3); + weapon_thinkf(WFRAME_FIRE2, 0.3, rlauncher_ready_01); +}; \ No newline at end of file diff --git a/qcsrc/gamec/w_shotgun.c b/qcsrc/gamec/w_shotgun.c new file mode 100644 index 000000000..9107dc0d7 --- /dev/null +++ b/qcsrc/gamec/w_shotgun.c @@ -0,0 +1,81 @@ +void() shotgun_ready_01; +void() shotgun_fire1_01; +void() shotgun_fire2_01; +void() shotgun_fire3_01; +void() shotgun_deselect_01; +void() shotgun_select_01; + +float() shotgun_check = +{ + if (self.ammo_shells > 0) + return TRUE; + return FALSE; +}; + +void(float req) w_shotgun = +{ + if (req == WR_IDLE) + shotgun_ready_01(); + else if (req == WR_FIRE1) + weapon_prepareattack(shotgun_check, shotgun_check, shotgun_fire1_01, 0.7); + else if (req == WR_FIRE2) + weapon_prepareattack(shotgun_check, shotgun_check, shotgun_fire2_01, 0.7); + else if (req == WR_FIRE3) + weapon_prepareattack(shotgun_check, shotgun_check, shotgun_fire3_01, 0.7); + else if (req == WR_RAISE) + shotgun_select_01(); + else if (req == WR_UPDATECOUNTS) + self.currentammo = self.ammo_shells; + else if (req == WR_DROP) + shotgun_deselect_01(); + else if (req == WR_SETUP) + weapon_setup(WEP_SHOTGUN, "w_shotgun.zym", IT_SHELLS); + else if (req == WR_CHECKAMMO) + weapon_hasammo = shotgun_check(); +}; + +void W_Shotgun_Attack (void) +{ + float sc; + float bullets; + + makevectors(self.v_angle); + sound (self, CHAN_WEAPON, "weapons/shotgun_fire.wav", 1, ATTN_NORM); + bullets = 10; + + for (sc = bullets; sc > 0; sc = sc - 1) + fireBullet (v_forward, 150, 8, IT_SHOTGUN); + self.ammo_shells = self.ammo_shells - 1; + self.attack_finished = time + 0.7; + + vector org; // casing code + org = self.origin + self.view_ofs + (v_right * 6) - (v_up * 4) + (v_forward * 15); + SpawnCasing (org, ((random () * 50 + 50) * v_right) - ((random () * 25 + 25) * v_forward) - ((random () * 5 + 10) * v_up), 2, v_forward,'0 250 0', 100, 1); +} + +void W_Shotgun_Attack2 (void) +{ +} + +// weapon frames + +void() shotgun_ready_01 = {weapon_thinkf(WFRAME_IDLE, 0.1, shotgun_ready_01); self.weaponentity.state = WS_READY;}; +void() shotgun_select_01 = {weapon_thinkf(-1, 0.3, w_ready); weapon_boblayer1(16, '0 0 0');}; +void() shotgun_deselect_01 = {weapon_thinkf(-1, 0.3, w_clear); weapon_boblayer1(16, '0 20 -40');}; +void() shotgun_fire1_01 = +{ + weapon_doattack(shotgun_check, shotgun_check, W_Shotgun_Attack); + weapon_thinkf(WFRAME_FIRE1, 0.3, shotgun_ready_01); +}; +void() shotgun_fire2_01 = +{ + weapon_doattack(shotgun_check, shotgun_check, W_Shotgun_Attack2); + weapon_thinkf(WFRAME_FIRE2, 0.5, shotgun_ready_01); +}; +void() shotgun_fire3_01 = +{ + weapon_doattack(shotgun_check, shotgun_check, W_Shotgun_Attack2); + weapon_thinkf(WFRAME_FIRE2, 0.5, shotgun_ready_01); +}; + + diff --git a/qcsrc/gamec/w_uzi.c b/qcsrc/gamec/w_uzi.c new file mode 100644 index 000000000..0830d53e0 --- /dev/null +++ b/qcsrc/gamec/w_uzi.c @@ -0,0 +1,123 @@ +void() uzi_ready_01; +void() uzi_fire1_01; +void() uzi_fire2_01; +void() uzi_fire3_01; +void() uzi_deselect_01; +void() uzi_select_01; + +float() uzi_check = +{ + if (self.ammo_nails > 0) + return TRUE; + return FALSE; +}; + +void(float req) w_uzi = +{ + if (req == WR_IDLE) + uzi_ready_01(); + else if (req == WR_FIRE1) + weapon_prepareattack(uzi_check, uzi_check, uzi_fire1_01, 0.075); + else if (req == WR_FIRE2) + weapon_prepareattack(uzi_check, uzi_check, uzi_fire2_01, 0.4); + else if (req == WR_FIRE3) + weapon_prepareattack(uzi_check, uzi_check, uzi_fire3_01, 0.16); + else if (req == WR_RAISE) + uzi_select_01(); + else if (req == WR_UPDATECOUNTS) + self.currentammo = self.ammo_nails; + else if (req == WR_DROP) + uzi_deselect_01(); + else if (req == WR_SETUP) + weapon_setup(WEP_UZI, "w_uzi.zym", IT_SHELLS); + else if (req == WR_CHECKAMMO) + weapon_hasammo = uzi_check(); +}; + +void W_Uzi_Attack (void) +{ + makevectors(self.v_angle); + sound (self, CHAN_WEAPON, "weapons/uzi_fire.wav", 1, ATTN_NORM); + + fireBullet (v_forward, 100, 8, IT_UZI); + + self.punchangle_x = random () - 0.5; + self.punchangle_y = random () - 0.5; + self.punchangle_z = random () - 0.5; + + self.attack_finished = time + 0.075; + self.ammo_nails = self.ammo_nails - 1; + + vector org; // casing code + org = self.origin + self.view_ofs + (v_right * 6) - (v_up * 1) + (v_forward * 20); + SpawnCasing (org, ((random () * 50 + 50) * v_right) - ((random () * 25 + 25) * v_forward) - ((random () * 5 + 10) * v_up), 2, v_forward,'0 250 0', 100, 2); + //W_Smoke(org, v_forward, 12); +} + +void W_Uzi_Attack2 (void) +{ + float sc; + float bullets; + + makevectors(self.v_angle); + sound (self, CHAN_WEAPON, "weapons/shotgun_fire.wav", 1, ATTN_NORM); + + bullets = 5; + if (bullets > self.ammo_nails) + bullets = self.ammo_nails; + + sound (self, CHAN_WEAPON, "weapons/uzi_fire_secondary.wav", 1, ATTN_NORM); + + for (sc = bullets; sc > 0; sc = sc - 1) + fireBullet (v_forward, 400, 10, IT_SHOTGUN); + + self.punchangle_x = -1.5; + + self.ammo_nails = self.ammo_nails - bullets; + self.attack_finished = time + 0.4; + + vector org; // casing code + org = self.origin + self.view_ofs + (v_right * 6) - (v_up * 4) + (v_forward * 15); + SpawnCasing (org, ((random () * 50 + 50) * v_right) - ((random () * 25 + 25) * v_forward) - ((random () * 5 + 10) * v_up), 2, v_forward,'0 250 0', 100, 3); +} + +void W_Uzi_Attack3 (void) +{ + makevectors(self.v_angle); + sound (self, CHAN_WEAPON, "weapons/uzi_fire.wav", 1, ATTN_NORM); + + fireBullet (v_forward, 0, 10, IT_UZI); + + self.attack_finished = time + 0.16; + self.ammo_nails = self.ammo_nails - 1; + + vector org; // casing code + org = self.origin + self.view_ofs + (v_right * 6) - (v_up * 1) + (v_forward * 20); + SpawnCasing (org, ((random () * 50 + 50) * v_right) - ((random () * 25 + 25) * v_forward) - ((random () * 5 + 10) * v_up), 2, v_forward,'0 250 0', 100, 2); +} +// weapon frames + +void() uzi_ready_01 = {weapon_thinkf(WFRAME_IDLE, 0.1, uzi_ready_01); self.weaponentity.state = WS_READY;}; +void() uzi_select_01 = {weapon_thinkf(-1, 0.3, w_ready); weapon_boblayer1(16, '0 0 0');}; +void() uzi_deselect_01 = {weapon_thinkf(-1, 0.3, w_clear); weapon_boblayer1(16, '0 20 -40');}; +void() uzi_fire1_01 = +{ + weapon_doattack(uzi_check, uzi_check, W_Uzi_Attack); + if (self.button0) + weapon_thinkf(WFRAME_FIRE1, 0.075, uzi_fire1_01); + else + weapon_thinkf(WFRAME_FIRE1, 0.075, uzi_ready_01); +}; +void() uzi_fire2_01 = +{ + weapon_doattack(uzi_check, uzi_check, W_Uzi_Attack2); + weapon_thinkf(WFRAME_FIRE2, 0.4, uzi_ready_01); +}; +void() uzi_fire3_01 = +{ + weapon_doattack(uzi_check, uzi_check, W_Uzi_Attack3); + if (self.button0) + weapon_thinkf(WFRAME_FIRE2, 0.16, uzi_fire3_01); + else + weapon_thinkf(WFRAME_FIRE2, 0.1, uzi_ready_01); +}; diff --git a/qcsrc/progdefs.h b/qcsrc/progdefs.h new file mode 100644 index 000000000..eb15c45c6 --- /dev/null +++ b/qcsrc/progdefs.h @@ -0,0 +1,143 @@ + +/* file generated by qcc, do not modify */ + +typedef struct +{ int pad[28]; + int self; + int other; + int world; + float time; + float frametime; + float force_retouch; + string_t mapname; + float deathmatch; + float coop; + float teamplay; + float serverflags; + float total_secrets; + float total_monsters; + float found_secrets; + float killed_monsters; + float parm1; + float parm2; + float parm3; + float parm4; + float parm5; + float parm6; + float parm7; + float parm8; + float parm9; + float parm10; + float parm11; + float parm12; + float parm13; + float parm14; + float parm15; + float parm16; + vec3_t v_forward; + vec3_t v_up; + vec3_t v_right; + float trace_allsolid; + float trace_startsolid; + float trace_fraction; + vec3_t trace_endpos; + vec3_t trace_plane_normal; + float trace_plane_dist; + int trace_ent; + float trace_inopen; + float trace_inwater; + int msg_entity; + func_t main; + func_t StartFrame; + func_t PlayerPreThink; + func_t PlayerPostThink; + func_t ClientKill; + func_t ClientConnect; + func_t PutClientInServer; + func_t ClientDisconnect; + func_t SetNewParms; + func_t SetChangeParms; +} globalvars_t; + +typedef struct +{ + float modelindex; + vec3_t absmin; + vec3_t absmax; + float ltime; + float movetype; + float solid; + vec3_t origin; + vec3_t oldorigin; + vec3_t velocity; + vec3_t angles; + vec3_t avelocity; + vec3_t punchangle; + string_t classname; + string_t model; + float frame; + float skin; + float effects; + vec3_t mins; + vec3_t maxs; + vec3_t size; + func_t touch; + func_t use; + func_t think; + func_t blocked; + float nextthink; + int groundentity; + float health; + float frags; + float weapon; + string_t weaponmodel; + float weaponframe; + float currentammo; + float ammo_shells; + float ammo_nails; + float ammo_rockets; + float ammo_cells; + float items; + float takedamage; + int chain; + float deadflag; + vec3_t view_ofs; + float button0; + float button1; + float button2; + float impulse; + float fixangle; + vec3_t v_angle; + float idealpitch; + string_t netname; + int enemy; + float flags; + float colormap; + float team; + float max_health; + float teleport_time; + float armortype; + float armorvalue; + float waterlevel; + float watertype; + float ideal_yaw; + float yaw_speed; + int aiment; + int goalentity; + float spawnflags; + string_t target; + string_t targetname; + float dmg_take; + float dmg_save; + int dmg_inflictor; + int owner; + vec3_t movedir; + string_t message; + float sounds; + string_t noise; + string_t noise1; + string_t noise2; + string_t noise3; +} entvars_t; + +#define PROGHEADER_CRC 5927 diff --git a/qcsrc/progs.src b/qcsrc/progs.src new file mode 100644 index 000000000..8677bd262 --- /dev/null +++ b/qcsrc/progs.src @@ -0,0 +1,62 @@ +progs.dat + +gamec/sys.h +gamec/builtins.h +gamec/constants.h +gamec/defs.h // Should rename this, it has fields and globals + +gamec/bot_maps.c +gamec/bot.c +gamec/bot_way.c +gamec/bot_fight.c +gamec/bot_ai.c +gamec/bot_misc.c +gamec/bot_phys.c +gamec/bot_move.c +gamec/bot_ed.c + +gamec/extensions.h + +gamec/g_subs.c + +gamec/cl_physics.c + +gamec/g_world.c +gamec/g_decors.c +gamec/g_casings.c + +gamec/t_jumppads.c +gamec/t_teleporters.c + +gamec/sv_main.c +gamec/sv_stats.c + +gamec/g_violence.c +gamec/g_damage.c + +gamec/g_triggers.c + +gamec/cl_weaponsystem.c +gamec/cl_weaponanimations.c +gamec/w_common.c +gamec/w_laser.c +gamec/w_shotgun.c +gamec/w_uzi.c +gamec/w_grenadelauncher.c +gamec/w_electro.c +gamec/w_crylink.c +gamec/w_nex.c +gamec/w_hagar.c +gamec/w_rocketlauncher.c +gamec/cl_weapons.c + +gamec/t_items.c +gamec/t_halflife.c +gamec/t_quake3.c +gamec/t_quake.c +gamec/g_tetris.c +gamec/cl_impulse.c +gamec/cl_player.c +gamec/cl_aliases.c +gamec/cl_client.c +gamec/t_plats.c diff --git a/qcsrc/todo.txt b/qcsrc/todo.txt new file mode 100644 index 000000000..5c43d374a --- /dev/null +++ b/qcsrc/todo.txt @@ -0,0 +1,130 @@ +********************************* +************MENU***************** +- Lan listing +- Player model and skin selection +- Player options (player name) +- Replace Cursor graphics +- Add menu music +- Add deathmatch game options +- Option to play music + + +********************************* +*************QC****************** +-ARMOR- + - 25 Armor, Armor Shard, 100 Armor (25 armor with new skin) + +-POWERUPS- + - Strength - Player model turns completely white. This is alike to the painkiller powerup, the users screen with turn mostly white, they will be able to run twice as fast, their weapon will automaticly be a railgun with radius damage equal to the rocket launcher. They have 100 health, or 15 seconds (if the 100 health is taken away the powerup is gone). This powerup can also be earned, with kills in a short amount of time. If the user gets about 8 kills in 13 seconds, this powerup is given to the user. + - Speed - Player model turns completely yellow. The users view will turn blury, and the user will be able to run 2x as fast. + - Invincible - Player model turns completely red. Completely invincible from normal attacks. The users view will be tinted red. + +-ANIMATION- + -Jumping animation + +-WEAPONS- + -Shotgun - secondary fire - auto 3 shots fired + -Electro - Replace secondary fire with third fire with one bounce + +-BUGS/OTHER- + - Add quake1/halflife entity support (just the weapons) + - Weird bug where the user starts firing from the other side of the screen, mostly happens with laser/crylink + - corpses not fading away + - People not dying when falling into the black + - change the player's solid to SOLID_BBOX before the traceline and back after, so that the uzi affects corpses + +-BOTS- + - Make waypoints for all levels + - Personalitys for the bots (names for bots with certain models) + - fix HORRIBLE use of weapons + - Secondary weapon support (grenade/rocket launcher might be difficult) + + +********************************* +************Engine*************** + +-PARTICLE EFFECTS- + - Crylink (white trail, make the hit effect white also) + - Electro secondary explosion (smaller DP fireball effect, with blue skin) + - Grey Smoke for Uzi, Shotgun + - New effect for the laser explosion + - New Nexgun beam and explosion + - Hagar explosion (less spamy) + - Spark effect for levels + - Rocket launcher explosion, more powerful + +-3D GRAPHICS- + - Add DPshader + - Bloom effect + - Decal clipping + +-2D GRAPHICS + - Powerup images when powerup is picked up (showlmp) + - Don't show monsters/secrets on the bottom of the scoreboard + - More advanced scoreboard? + +********************************* +******Animation and art********** + +-3D GRAPHICS- + - New weapon model for Uzi + - New weapon model for Hagar + - A small piece of flesh for Gib model + - A medium piece of flesh for gib model + - A large piece of flesh for gib model + - Large Rocket model (for rocket launcher) + - Small rocket model (for Hagar) + +-2D GRAPHICS- + - Change hud numbers to fit hud + - Next/Prev button + - New menu cursor + +-Textures- + - Slime textures for Slime Pit + - Water texture for oil rig + - Lava texture + +-SKINS- + - carni + - grunt + - headhunter + - female shock + - specop + +-SHADERS + - Make dpshaders for the laser-crylink-nex + - Convert quake3 shaders for the evil textures to DPshader + - Use envmap for rocket launcher + - Pulsing glow effect for the laser/Nex/Crylink + - Electric wave effect for g_electro model + - Glowing White/Red/Yellow scrolling effect for Strength/Invinicable/Speed + +********************************* +*************AUDIO*************** + +-LEVELS- + - Add spark sounds to levels + - Machine sound + +-WEAPONS- + - Change Electro fire sound + - Missile sounds for electro, rocket + +-ITEMS- + - Armor pickup sound + +-OTHER- + - Gib Splat sound + +********************************* +*************MAPS**************** +Nexdm01 - + +Nexdm02 - + +Nexdm03 - + +Nexdm04 - + +Nexdm05 - -- 2.39.2