From a86093660fd17be3ca35bead3febb58e53bf71c0 Mon Sep 17 00:00:00 2001 From: Jackz Date: Tue, 9 Nov 2021 10:30:12 -0600 Subject: [PATCH] Add hide and seek gamemode plugin --- plugins/l4d2_hideandseek.smx | Bin 0 -> 14522 bytes scripting/l4d2_hideandseek.sp | 753 +++++++++++++++++++++++++--------- 2 files changed, 567 insertions(+), 186 deletions(-) create mode 100644 plugins/l4d2_hideandseek.smx diff --git a/plugins/l4d2_hideandseek.smx b/plugins/l4d2_hideandseek.smx new file mode 100644 index 0000000000000000000000000000000000000000..c621a756c0fee4392da16063a9ca30c0bfae04c2 GIT binary patch literal 14522 zcmYkC1yozj_P1&A(&7|{7K#;j3s9gCT#9>xy9bAq5}*_-P~5$^yOp5Dg1Z!#;BJAB z_r3S7|ChD)`8~5|XV0Fjb57@%-7dW+4=mA8jZouirdFL7{t&^6%>(oduzw zU_7;M%}`KEDNs;eK5;<+3X1Mi9LW1%)3E1%=~@ zkA9$_sGy>tJdJ@O_7erA^(n8+6W=7DpqxMPrzbv0MnSoMQllr^O+i7SdQ!V5Lr+CP zdH1B=PsZ}Zeoy-C$?~7L^hux6p{S>!ppgCJdor6R{->VSC#!m5wI}U+vS;Z}2T~N2 z@qf4$1qJ^}7oUvaiO-*O_sP)fP*4(|>bZR4w1y`?`cwU8PUaRU9OfW*5DJGg*wo&} z% zVo}DqLC=2mmVsqdz=6jcgU5o0H$8bYt2Ad#Y9(jwd&RGvoVC~STAF?)wT(GB)!&ai zOw`p~WMpJWJ?BW`-y2-sEN?emWi+{MTaMq}dOtdgH{E3T24rpnwgGVOk*6+PTYbqe zV3*SiqhR+}cK0}=7i>oByD!GHBxfA4&zM1793Wpq7;ZC^+7nxJlh9+DVZT*z)*`Xe zjsw*AheUgeaLW_B!~@%4m2i5OaC)0C!2^3?ldvAnu%D$kOORN(&H*YkgpD*q(><|U z_6a?f7)pO5#*a8a0g08iiIpTApd}6vDhEi^5O&!FrS`xUUHx~I9K~7w#7a7o!^&h1 z&=d#Afdk~v0kTP~G~xi=8NqN{py|%oqU(en%MAP96_d1oh}wTw)Xi7a4N}xCRGj^y zIE$NDiR1u<8p5KU2LDg&2*dtUUadqf2c^SG*Tl*f|JHz!SSgTL>6ln4o>=+o-}j0A zXN;$M!2i}W&#*6(SQ*X%5;TN;$1%Enn#2f0=`cfS9|PMeVf`S({?mBsiIo~0plw4~ zc_Wl$WsT0w5LRpi(>H_(G(%;KU>?oTgQw4MjnHG@-)xE;pcy0B_i#gZmc&Yte@mcA ztgKa>eU?~h#sT8o!dhT=R6e9de;mE=9B3ZGyzLJ@Jai4y$ZqT#Kmu5DG0qIb3YL_G zmJ%FM!;ul^AuHBrEUx{9%y2)~I>`4}t7LXaP`UAmZ$!Cqs9*7iwp2l<1grow#8ET5!UmR8cn`8-ezA_&UIAa-(wUCg^Whg^$Z%+ejxo9zCS#Nmygm#P;A8wp z5^ivsW!20E@u2`4eQz?)nR*?4TCrA69?asY*$c8EKK%W?U`Oz@xYQF?iKvfm#QJ`F8aJYPIo0GK+a5g?ZFmC0{vtU%#I=s_$($ zHRLAumEs^hNS_B!lG7>xc z1kLYPew;vTN*Px8Ebb{?CYu*F`-o3~Vm=nFnMtNSzy%fMsx=j2zHvcjvvy;b8J0ua z9;5t5Rua$PwM*8+6`Zmem_K&b6b zehbO-N{*A?P_EjhN1O}Bdmo0NI+269PMNj0crwe3*5id|%ZxD6)!^Sxnr`eGxJg;T z0t0LbHw~)Rt`;d%1#S7-{)z{7-#(Q6mFJt#NAHj4#X+DZgC>^(zlEK0}$-<7Hb8me?6DAI&#fjaLYk-Bh5Q zbj|fQEDSW@1*64V{cX^8Ap%oI`dKkIw~GL?0(3|ymThxEEoF{020mI99g}vEkd_Ct zNt+|ILH~v|$VZm#dMcY}RvT5N{>i|XS5iIx+JD>%c3_U)#{DtdbUpqSKfR6NG24DU zMw$tts-so?>ugN-u)^Ji8gHqFg#?R1=J;*A_){uS9z-+wu`?5c^J=K}e!qJ0hbk&B z@3y-R^06TQ@oMpqO~+B^{4r#qrpupW)fRd8Q|JMGi8xrpKcLX23E+-Cqd?m0GBN1ZebbAx~fHXMYn)6Tb7#fYwA<> zhRqgkV7uoZipko@M2oA$6sI=!6*Zb0k$ge--oBqFF_+s>PCrShWsqvoa{|raM9V_* z`gra%D_SyFolVTUMlJAz5&iKNm5kNATWHM)pH2+K5wtfum)tk&eNw)EBRJEjZc}mO zztIoLP_~-S9deia{qRMSne$)Z1bwncaNJ?7bdxQFbwaW6ErB7_GPcGWT* z*GnolGW9c>ag&%q#ej0cRH=5?Ql+B1eBps6?BGO>c#DcePeqY^znv>--ailu_B9a(?cH$et1lguvykmg-PNz1be>j(CW^(#GkEj7H<=>w?~8CwS(5u4G{*72R_Md!{G5PvoqrM z^t_$WxJAUK&f0V&egkN{|GcPN(;b!JD3IV(h1tb33f~i+cWAm z8mmX^1!RM{VSq7O^X_+;JUyIJ4x(^Appm1Kk!ijgZ}Ht&k3oRATkOWwV^Ma&I3&)X zZiWgnH?F9K+?gC!x`TWDS-_IGalJGwulH;ywF@_o63;#avR7IMz~0ibvpU`N*cHud;W%Wp@2R>PJ4ZWJ4AIr+G z>ID=WPO$eIniaR^!^7hiudpK?WR1I?+1a<0A#w+j+7kK zW59kh2)Mp|?_EPEljz`q;VHP`Jb6YE*Z_|w?c1ymy_^(*SM2%B%5@m*A4Z$x!Op! ztXiY#=N%^ONgs(})G?XgMwCa~h{W)lP@d0grP$EEV*m4Uur9i}WYWkOkf#2SU3xkN z_z@6YMx&Ep9IFfwHRI1ER!WN@j>cMrjm4JN?-iz8NR|ZRWj0%BEMc;xJ5)4VmU7Y{^(WdTjQROJAEWr-bxQsgXK5l8 zU=H}AU1Rn!Hf+ECCH>-GqHgZYvZR!@(30q9JrXgwBY3lyO`7Dq0De)oopzh_FA}Wd(1w zr2s}+_l+{-LnZQYGwj6G@7~jjWH?bJM=?<>?d}j~D5|=AH?SKVtj$&)gx7nukG0W` zl=UBm#_YIe&FKleQEB&()1nd1r(|`YainR&)8=cr>ilkLWh5Et)Bq_4-ONk4U4$*P ze-P($Lv*l4(l212?M{@i(@)&=y!I3k4c=Y@7dF;$cnj}3a@Ly<)=%xt)lebFlKSr5 zi-jecZR@uQH1CeWw35M;+s^GK{wgS7J3@ z^fK2fZywe?)NF{aZw6QRSb!Cya~yMreYdf^by%!CwQD!wOucEMDSRqtEp5gnr+y>! zGvsS+CGl4L1s1(7vA#{0PLo!WetRi?;7dTxUY&2DMT?k#(GfSpgCM+KLJbrrSiCf} ztU`ZZDZPAV-adL|RUm{=a&)@~TSOflI*a$^kDP$1=T^lNo%{H~z5|k#CwM` zmWiZ7U>sv2jWM?vgl1+_GUH^5mqkTDtM=x2?|ufRV%Nz}-s*h$U}z`V;I49a*z4z` zF;cU4w>gmx{(~8O#rVD&eK(@X89r~Hp<~guhr7wV{}TH>{iW$LJF^UfpA4$(+{hZ!3o~XM+`i?xyy%0p}maZ-Prx(lT)e+jV5Y^roA)U z+vH2Y`0OAHGp)aH&*n}1x;e0_qkSpR8LZ-8XO7!vye=>f%Z{pSFfSZ_ifsMwv`&M!RP<|rODy12) z+;*(sW%sUm!3J5`<%XF3=(3jx7NE>)4mot4UEtY1;aqIJsob(B+Ns}6Ml2D9xo(Sy zndXMy?*A|l7IZyc7Fp2axFCPqa_Ou zIk?ZuZLh5hZqk;%i_`t{y&9MbKyx|n1`sFKc6(o{fZD+GA}=BkJR~9NTfM8E25i5; zby*w`z5jN>R!%7aq#fL_icXW(y8GzMlR7gl>@^&K(H-_!F0p#==Wr6S?_$G=a9LRX zk1TX@3Y5qt=~kfOWG}0BXmN)ZOzbZ zx0hf~PNEf~Dk4e}t@-)Gi;wg)4U80tq?IqKoA)gE`xRzr`F8DOex`J6dGWm5C$*2c z2tF|&QK@)|IU)VWStsQUdIxy5FqH03<+Yu@8S^9+E!E3jse>3z5h@%e0xPa|_O#71 zeuw2B5qVw8Ctj28PO71=JkLIG*)+Cbto^CA%%8(o4SDI=H=4EE!9#&pe9}DrN6tPR zyh5P}9A7BA>zz%o)(u;s0`^WLZ=EH4^{%K-Ha>F|&z;KcULjzeKbEPwBDcO7I7Kx! z3GYv*(mA%VtZfy@`Q?(kItfT0Hj#gdnk03sxt(s)G?7nS^ZVsl+H$LJqyLyF*8E!j$Z{H<6}n~>!!o$CbFlBH zA=UL|Zg)@Ytje3S)qa`v-u~91@|OKGPmpWHaeaJr&q_fiZ2if$wbR**1(C!7I!Mh7 z_|U}K-IqTif$X5RjP%sM9U=(=ON(e8ywX(*YfkM>QNVS17q_W7Wi9!>IBh_Cg+r&< z$x*ZO*R5=4Ym0YO`G62p<`UMlt@o7%zkam@7lIo!B|Y6v_FoU|IcQE9 z7A?s)1P0$U=!J+YD*O+36L`E{4_mL@es*WJA2n4LihtpVom$InTKw>{5L^OgT;IJe z$R2)Hl3IPAFga%us_@!UrQ z=HJRc=}QhRWS%^EK<{S_@etOKaBZ4MtmzRKgUTT7=NuVz-z~A;;U(N!m0zg|cb-iksz~~AG zeWFtal@Bt?!!2_L%!1S$b`-lPvsS2v=DhVc?%m(UL;OJ^eS99b%>71>(z-t9kk(v( z1`tovIHfLJKs(a&A~WPkrW~1HpVGSu?PgA7!y7Jz!mOQrh4hkBj_}5nl+k8iTbL@o-q ze{0fRE3ruuD_Vt;M=n!@?fJkzybup*d;9UL#NH9pTh$2Ad51i6c(bj~;0!w+zk?3l zBJEu3l7uSUi^j)4g8;WhTk#z_x5K8xO=OaHNx7Xhxy_u@hL7GRkMc%_W{34V2BZUy z8$*&7sY=(Cwny^AVJ+>jlO0<%`F)*?yHEuCfbn)4a9#TL%S+j~JeUKMgTvc_Q%sGA ztcaa28c)9h&8UH!Z{uPNet;JfNci`G(h?&}lxpcZE{xX^I)y`!!0&C`MZZRJgb)OHs z>yaG8YVXq`)ShP)+`~Vb*3{PV>E&?9LwEpWc`QG77A=%UAFS4hWg?G`XtKgm^jB41 zF*L~3tE-Ahe@veU6oU!B9eK~-y=#Xx01atf&U_$a`4iAqZ)tRTldXK4Miv#)sTnjm z{SM5M5azS(dHk|NSgKXnzMtAghM8vTLgsom`}3sgDo)($16P$5&)=hn!cPRH+(|)+ zRcdSJWr*oAqlO_>#4?BNG0LC3o5}KXH9|8}(5r^XkB&aS3X*A!U|`~ zTq&@K?69&9?5Y+LNwyBMOd@#+dI*nS>pdrc6*2S z{Rr7d^P|UH*XawpYA1iA45EzPRVzQ^?NzH@iNn>aG-O=pic!ds(0Z!NmDdfMMQ$P- zh}L$`cIm;{akcMn98idq@VPNcd|ch!Msd74aXYZ{zmF>MhlbgtctlmUeO)8C)U=&% zd|o_g7>k5jMX zigElLc6i5k5CGF?u$k|IVxQtgu5Q0GR@D8-nnsEh&2KkPizS_&qWwwkKsV1O02b+x zqOF+32pTo%zx%Q)wagiBX1%4?wCfsz<6Fzyck*DjAhaw&L(>O$*6MF6%60DuJ1+Y< z6S{Nz(uisI7Q={iw^?b1zqz%+dj4axV=lt6nPNv@dM3`@As?aFj5~gD&wh{WuW?6y zFJBqgk^`ry&WO2}71)Ux8SMImlw0lk&>CgtAt>FE-^y1;FEE=G`c4~q5=N&abtH@y zc6_4BJ;%Z3Y!F%m!X5dg+$4JQh%7Q!K)Dd7zSP2 zUpgT@#39;|Xl&Wq$oh-XcZVo|T+Ha^-5cfP)OG!=`hwf#{J^burKM$64$1&^575Eu zaaXUimqSv)JdF+YDm**TFKTIl^YcAZQ-NF0PGgjf+1D|ArUtB?6%`gr9}CeG?GUK7 z_a^f5F_u|$u5`p!F8uLwdy{H8Q%Og>M?6EA)fxPWHMh<2=v18Qda>39T?=&%%?=`J z20BJ2p)8*=>-h#H`9IG*tuj*OGxE-pQ`R`ki(ctACl&~W%6WZ)GJr2y)xS&@1U*mq zBwwQ8Gjd|>=MZbPsp$(K`|7>S&|fWJZ7P7pR$fwp$2R#o)((5@7kw+kuOls(Zl(*7 zuAeIP7+1P|a7Iyb)bF+1l>ESvcV)#b>%*v>?({5-^$Q=2hGX@kCPGk5+YcMbIVV`v zLMcFxsw;unN-%ZPF}COrOR=|mzeQp<7h2ZsZJd-Di*j&{0~z?Q*k0A*%8nbUy z$C8vV8rG?TxZ)kP^oB8h^iEEzES&~x*6EvB-HDh^2~c2Yet-FG+0w}0aeqcQe%>DA zGq@z5=-?#4Yo`Cl&Qu??F_&3q5qTS+BQi}Xe}OC8Gcy#SEUg@({$`>@!z8@HF*oI^ zJQiI9>~D`_OKzY#wO%<@JF>fxpL+Oqa_a&QsICQBwgWwack;C<9tRu}+uuO>D?J?| z1`Wu=#;(mBzTKbiKdpgnUYp9y9y^8QnFErH)1^P&mOIB5046F}8<&!Nr+X`zClnd# z2ou{eOeoil*M3Zs3EPYw=QYVNeM{K;7=-b1&AK`RrXW@%1|U{(6htU&*y76OrhY{l zFubE#q2OjNy2B@ZDKnuTyGKkrsTOL^sT_+muxKnXri68(y~f|aP!meSXN6vulGA~8 z_E5D~`Rg{<97gfUf{B>(8qgFthgGSU5HpOwgZWrWu^AgYMmL(+7n69z!Wd(Q7hJgd zvvAqn?l6h?%v=yUeeR~}#w9r?%AMANbzD8`x&|GDeHi0=L$SiXWb4a2Y;N{;-Ne=4 zKe<0C8nl{JK|-E=Q)GPPRvz>M9zik8s-1Yj4G7WLqA$DE_q7Gta(sE7f)Bf_>_a4F zT_2?wj6Yv|rpr*OQ0m1bDCGyP7gfG=D1ULH|Jz+Jz{ky8m%DOytiiGe^{)EX%bI?f zK=l%f9F3U6YOLg{l<`si+5wyC#?r_OC%@pru+f_f8iBGgM@FZP+b{;6ep91={de?p zj;PAj^gGhZ0#VlGVlNRDDbe^tih1a)!ndZG^i`%vlBi0aqdcwJ0IR{)pxLejmsad` zR^FLP@z6ci{vkjb+=QG4x1oqPg<$@4^{25v1{p(B zu~p<{qEu9qjk&~lDXTs<{as$esD=n!VAo$7;q)4jo`=X=Xp=}CPoviO+5W5u)u9P| zu*7mmoH=?%>o3zwn3t(25WPouYd)I~`)&SMNrbE?V;8dXA&@JYqmm51bqh&fS0bGT z&(l{04hi+Y*d`qEs8|}ZQ(&FVc_|oKSXFKf7!Klnm25_hBfqoGW=E&Rq)Z_YLoCo~ zk=0j(QP$P(kS-p5`u5xMa{PfEWsC1Zi?46ph_fs2i7x3Rh&3->YLvmwtfa*tlqk<3 zgfC8A{#Ra5RX_x&I8zwu4CGhe{95#rBarg$t_yqjgVADYF>l}G+z(g9&xqx}6Kr_o z)FGavUTAUg_;O#1JfgqNFA~cmik*7JZUe29r%PMxuU<=#$Fb&Tp@$ZURj8;t(7lbL zzTqI=)d4c)uXfrT!zqk%N51{w9zW+87s*Y6@|%%VCo?KGR&|n2qyL%~NyF_s|Bzn& zEgL;_+=bavKNkM;Qv)Z$8-E+qM}G||9i(>Z^9vF?iSw~0WAd|;vXinh=Sb`2M^ofj zwpt@*Ec*w++DQ~Ado$aD#wgUzoh@d2*G{!&!C!)ejs}hs}Yq49OG= z)tL`JZ2Mk78S!5fp1=KN=Pteb?7cfP`P7hqUx3}Q?5Aq^9GNhhQKCQO^5CbH<2AR( zPC30bTi?(uF}gx3ctKKsqu@a9ORpG_(r)jf%2Gm$xJos#o2YH&Yg1|4-;CDlBctN_ z`Z@yQQmDU5nyT8gmfR_^3YtqrCr%j`{0$l;b8TOd-Q%na{m;I{GvW_vJP3zWdYQ zo_l4=*7B%=vIW(3b#s_F@=RPn*?MRUl9P;iEY_z2>TBatP8yP+Y|b&h5Z+UM5m?WwU&x?|WzR-}=^B@^tfkn%WC5P6rvYJ_Sc<)KtUgVuKXMBuPwR?ZLGj+-E37Q{Vr_I}XY&-2&XRKK* zUsdX+z)oW{p>pRe|M#F zr-NBWncMp(n4)a*sP4m4d3wjqrkjX0~mbN^w|)3!LcWt`*#YTkcc!>QEH z<;mE6k2AqJ656UY^qcFM+2B4TOqi_T^P&lH4VMzki`XMAdWq{*7jbiUpJURulV2lQ zN#P*6UgNGaQ-P2ovgn0$qBCx##)^qqzX-0Rs0-C<(n?|Sg3m~9ZEn`CyokG`q(lPD zD<@)JUZp`_egWRD#g`MOkCU^DPvNmHk7C>sUFH#YgQg)Nw1*~yZhWWr29d*Kzi^ZW z;CA*2A>I@P){85gmbO2`?m~MEBL%w5X$foiPd`4kYY)wQ3%Voij(DYU;SxDK`-}3v z{a2MRrKQ6$*&)(&S(e(_gw@h9snKPHbIURBCMT%{bbz6%IcUb#2$33^*`ZIW>@?sq zgJ_v)TuRv3kI1He9)IYdNUiIH&!B;pF<{+DjmY6eL4E-+sn_h{tANlyzB!@8%YRvt z+6%pA?+>mjv0DeaERPvz1cFzNKyF^Sp~`FQGy>rgJD`bcA{v2cdWocdlXVd94W*d< zdq{Zb@Fpg}o(JgrSpc%!Rd8lTBajK{?P5G91=!0zHis(jZj0HgJcflT?|s9px$W?o ztqqv!+PXl{2vn_;^C!)5(|~L<|5~`+X>z76I-OO|QJY~Ect%hstw@?A{U!C9dl~Hm zBgTUYKNxeSZX8{G*IOPexWR0*&w$JhGT!#J*%#2BRu|D=xgim)_SL{9u7#t^>fMKQ zV}X^9g+a!9PT#qRM`g~`(~biRw}-1%`|-!@#NpeXk&p3 zUqAc9KEG#ikmTs$2VOt>Tj1U2hs=X@c9coH1(YnD`scrav$(%5PZChB*%&j)x~Eab zC78akf6W(PX4E}gFZ-%Z>qMI6XV%{9ch9kKGfdPzbeB1Q2(f7CuxWwlNNypS?62Ta ze_9{w4<7pJ?`BqFy&rKGkTMHM1N*)&kJc@My`nVPt&vnU){eLY;zo|a+4931SOgBs zd{GS;h3d5avx6>{1G(PU{uM%3ms_;qWnPynVT2rmyI1jq>sp#~VA?|ROwMNAAv~@L zz{m3Ho{lf)Tt*$dMrH~xXM`sBv%CBA7u|(>++K*;HIJm0hg;f65K639IaSWMMqf0x ztwuX8KY#2gJcpA;Ut-P=zhCvf$BsEd(#>SP2a!LxV-N1l{H%;v)`C^vP&2T_N$A3= zuNb^HuO45|2WXoaZMdCYui>2j5^_(bc1NQ2k{Owd>dL0WO04)3ICY+$PxgQgcRclS z*s5lB@yD!QXj5H07PDOjGk4f1T zVZ)i&7JZyqMS`%Ha}PN-@FGq)PU}@dIvjJXdLB25t$JqT5oCnYe7qI|a428oXUE;@ zhpT1xcZD1G=7tYM@_S^F?wM0cj;htM*YKfV1$93&Efn-mnk?4ZX4|Pp-RmYzXzn#|oEnsgI9)J|PjyxvX>0<#Tu1%0+ zhH!|*gL(ZfpH&nH3&)!nxO_i8;(ti%;;xN8j_c3d=q%*wAPmqqUWn-12`xNfmA+J3 zpRuJ%E9l_=D9RVP>uygm_>C0(t+hA5OoYI^y2K=#_rVG6c^H z{hM5P>$=6;O2~@a{F9gZ>gO2~p=!dJ{^lp)3Gmj-@dobb7t;jds1NC#?olroExFn} z3EMIJ071I}`o}{r3m~0ktu$rt{KU384Vom5^)b)8)M0{l_Z(v#vMrA57uzul*YW6u z*qPD+4JRV!t$)#B4IcuA%%83f?S7d~aacok8`KHdhJJ-Z^X8uNoOD(6w_t6#AJgrw zN5UUptQ8z#SucDfn^(%Q|6e=Ul{Cjn=Xx8kGWr;QW0)H7aJAUE{y6&t70aFL9jhwi zsOp)bHy7AChvp9{yM^_+G*GYMQ5K0a0|pi`T1+ICM4+oBcP2GHKqwsiEe@I~*{oL@ zYRCY1CbHDyBe%mbEgeM^K;!2@ilE-CA-5W_MCLjQAZRaY^d7B~hi`k!=Sr=pGKYP7 zBj-v@G&L&&#?(4*d%nMz<`iMojovB$EJpV>>FK!UjE*2kMB~({6jv7ry$HJ%E>*1^ z0{NRJ{>>0>^$>pYg`c_UcKj~f&ju31EyQ3Fi5DT?n@R;Iv8Uk{$ZRJL1m1NX;5-!R zrUwxcPQ1YM!=>@*ihPPtO78QKx&cfRy+<$Es%jqKhrh&ZMBfiNtM}m3k&ux zxaZ_tXTk>Pa-J{kfGK^P9%|j*dXn z8M5-v_7_LsY;V=HN=RgG0+!{`grikGq}$=d_5}9yX}HMG&jyiQUxBYH?CL83JyN#-BAG#`#olJEdRza*OZyQ4EXr z>iEtg5WTK7;kT7;=J~OeWtH)gJee$n$WgXg-`L8CF5<%xVXNdd@pUv5sf`DjcE@*r z8zKGpM(N8VnkaUrtT0p_4^qt+dHVWH-;#9IJacV;_Cy(8mt^2fW(wV_`f9%(Ws^LgodfPRr-*PxaJC z@BApA?jLwJMD5fEZJ`@jx6#McG!JN-q6CjL^)Ux@eu4vU{qW9z`SiNNu5tpPn4Oqk z55A^DkGig3x9Y-gzdstsvXUi!J%G|eAEd6|;z2NuxvqncyLKcx>7kJARn?nLZBLow z_p3m)3X6zMDDA~D)3tcWePUGr6poPOgfZP`k*1Tj%3c36M}qp+$g8UMIq7S?2+U6Z ztEva@d+~Z~>(+gc^2}I@r)I{dt>m61{ z$cdsC#@R6HyG(r5%Cl$nl9%`asOx*rjOl3!0ZP}(c#uOJU~2`IF@qn0Uqjk-56uH~ zrU7tYRC|<@0hRdeC*$=D$|tzyyUgw zkQKB825A23kY|Nm@uuxB7-o{&D%&dteGGBMexyc9UsFaHbgnV>^1$r=8o*Y*(tH)g zf`q!Fs}elW?555#hC9`?Zmg=(A_>mv{m79DI~SkYPX4@ug?~*A+wnU2>Qo*3p}N`ZSqFX=kQYBWNhLRadW)uB4abG2JQ^CqybQ z_84W=4;J*b5FK7>N$EZn6fp(Wf;vEPu!*40W~;GDL2$X_7a;zfQ~WXwY*m73pBrAn zaBKs#1uO)9rZYBZlOop4_~G4i8s%9|uP>D}=Z0PNN0iXP(Ffe+1&YJJKr#>aO7?p) z5bvJ!?DJcqdRRzXLRFx1exix=N{9zhIffA$XPJe8i1FjH%B)Ibi1Qa`_RDE3QFVz< z)O35d3a_N9*h-uO{Fh=wzK&k$H$J05&Zq-Ki;Yp!TK%Na9$(6#s&Bw5(+ETq!)v+fl+*XPI&*KfO5`aO1-SI@HI{z}5=|GIUC zLun-?#@8-#e`S77IX1&$x<|VXI*ua?g=WGU10t%@jwg12u=O2aXVBMBEQuhOQ?jF& z*JC-?Xty%>6~aiWOMNs|{Pw?_IzKYH{XSfKAG5BK5>$j<8IHVYm-C`H$@qLu6oBrh zdaZSivpUmyKR*YNZHKN%`N7=vu{uyi@B>KKgO35dEq{}lyg4x|p!ecC z($}cxsH@Q%>5sE_>_puN8c4*$+k5OSPFSYt!>&at;%#RaRO0LuK?aGsBzSo42fX%; zfWQ#UK>P}AKkPHMRodST{;i=u-#neyKrf;T(#46s`lYt7Pj}z*lZ*-PP8_H)j1nqN z+W~Wqz^hQq;_+{-_~J2lCGXhKcG$(5s9MflP3jun7{001m%paNc%$^QlI66x--b|| z1H?O9n+Hijb8!~SGc@yThn9I z^A>yB6WH^;P#_ht+cw3rMaKlkOpEmt@(xLl?IVVZce#beZM)fKgqL&___m97#fgsi z&K6~_miQ$jLK+ThLl^>C!Sd>8qehu$4EM2>ZOkw9##H#(@#mU8% zYrRZig2K+TofAVuIloKCF}sP*nzrt^N!>pJCR9bbyI+>w1i-jE-eyqxQk%ZMV#$Zc z{J|$GpUbg5S)AJ*y=*HkjA6P&uN7<)uaTLh>i>?d2+>wbx*JX^F|VMmcXHtI^<81h z;Brpo&Ln~>YOt4I`pW}AFZju3ufQj*IX)EBnDoEqTx7*S*?gkqYEl!oPmfzJL%VC( z21&xBj-;w&zaw`0Jrli*OEsBk@l}($YyaH8M_=JNDb1`P{0K1EVjboSYyA*5$HKkv zTF9NsUb|r1>K)_jYR1qsV%1X9;Q`7Z9L3qvjY`_2j1@vvGQ4Vq3N?W8IH<`w%9NfF zP!nqwn6w$DRl2M97Wn@2i!;($Q&_uWZ-n9y>XZU$VK>ohmfUdvBSKYs$n`{T>$?oC zTS40^u6W#VtDo=MIt)w$3;|V&pcoZE6*@>MMBL2sx6eex4P}^jTYTgSIXg^2ApDJP zeNu+36OEs4_C0Fw_>e%b%>orb*@wnPW2Z^Q^w8iy?N)N?Og=0*K&_~?Q&C4mXbXaB JbW$|^{{XK=LrDMt literal 0 HcmV?d00001 diff --git a/scripting/l4d2_hideandseek.sp b/scripting/l4d2_hideandseek.sp index 5584210..5731d96 100644 --- a/scripting/l4d2_hideandseek.sp +++ b/scripting/l4d2_hideandseek.sp @@ -4,12 +4,18 @@ //#define DEBUG #define PLUGIN_VERSION "1.0" +// #define DEBUG_BLOCKERS 1 // #define FORCE_ENABLED 1 -//TODO: Preload models +// #define DEBUG_LOG_MAPSTART 1 #include #include #include +#include +#if defined DEBUG_BLOCKERS +#include +int g_iLaserIndex; +#endif //#include public Plugin myinfo = @@ -21,51 +27,64 @@ public Plugin myinfo = url = "" }; -#define PROP_DUMPSTER "models/props_junk/dumpster.mdl" -#define PROP_DOCK "models/props_swamp/boardwalk_384.mdl" -#define PROP_SHELF "models/props/cs_office/shelves_metal.mdl" -#define PROP_SIGN "models/props_swamp/river_sign01.mdl" +/* +script g_ModeScript.DeepPrintTable(g_ModeScript.MutationState) +{ + MapName = "c6m2_bedlam" + StartActive = true + CurrentSlasher = ([1] player) + MapTime = 480 + StateTick = 464 + LastGiveAdren = 10 + ModeName = "hideandseek" + Tick = 484 + CurrentStage = 3 + SlasherLastStumbled = 0 +} +*/ -static char gamemode[32]; +#define SOUND_SUSPENSE_1 "custom/suspense1.mp3" +#define SOUND_SUSPENSE_1_FAST "custom/suspense1fast.mp3" + +enum GameState { + State_Unknown = -1, + State_Startup, + State_Hiding, + State_Restarting, + State_Hunting +} + +static char gamemode[32], currentMap[64]; static bool isEnabled, lateLoaded; static bool isPendingPlay[MAXPLAYERS+1]; -static bool isNavBlockersEnabled = true; -static bool hasFiredRoundStart = false; +static bool isNavBlockersEnabled = true, isPropsEnabled = true; +static bool isNearbyPlaying[MAXPLAYERS+1]; +static bool wasThirdPersonVomitted[MAXPLAYERS+1]; +static bool gameOver; +static int currentSeeker; +static int currentPlayers = 0; -static const float C8M3_SEWERS_A[3] = { 13265.965820, 8547.057617, -250.7 }; -static const float C8M3_SEWERS_A_PROP[3] = { 13265.965820, 8497.057617, -240.7 }; -static const float C8M3_SEWERS_B[3] = { 14130.535156, 8026.46386, -254.7 }; - -static const float C3M4_PLANTATION_A[3] = { 2122.044189, -588.200195, 470.435608}; -static const float C3M4_PLANTATION_A2[3] = {2000.802612, -426.686829, 402.803497}; - -static const float C1M3_MALL_A[3] = { 1714.133179, -1023.777527, 347.735168}; -static const float C1M3_MALL_A_PROP[3] = { 1581.286865, -1029.394043, 280.079254}; - - -static const float SCALE_FLAT_MEDIUM[3] = { 50.0, 50.00, 1.0 }; -static const float SCALE_FLAT_LARGE[3] = { 150.0, 150.00, 1.0 }; -static const float SCALE_TALL_MEDIUM[3] = { 25.0, 25.00, 100.0 }; - -static const float ROT_H_90[3] = { 0.0, 90.0, 0.0 }; -static const float ROT_V_90_H_90[3] = { 90.0, 90.0, 0.0 }; -static const float ROT_V_90[3] = { 90.0, 00.0, 0.0 }; static const float DEFAULT_SCALE[3] = { 5.0, 5.0, 5.0 }; +static float spawnpoint[3]; +static bool hasSpawnpoint; static ArrayList entities; -static char currentMapConfig[32]; +static ArrayList inputs; static KeyValues kv; static StringMap mapConfigs; +static StringMap mapInputs; +static Handle suspenseTimer, thirdPersonTimer; + +// TODO: Disable weapon drop + enum struct EntityConfig { float origin[3]; float rotation[3]; - char type[16]; + char type[32]; char model[64]; float scale[3]; - - bool toggleable; } public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { @@ -90,23 +109,55 @@ public void OnPluginStart() { } mapConfigs = new StringMap(); + mapInputs = new StringMap(); ConVar hGamemode = FindConVar("mp_gamemode"); hGamemode.GetString(gamemode, sizeof(gamemode)); hGamemode.AddChangeHook(Event_GamemodeChange); Event_GamemodeChange(hGamemode, gamemode, gamemode); - lateLoaded = false; + if(lateLoaded) { + int seeker = GetSlasher(); + if(seeker > -1) { + currentSeeker = seeker; + PrintToServer("[H&S] Late load, found seeker %N", currentSeeker); + } + if(IsGameSoloOrPlayersLoading()) { + Handle timer = CreateTimer(10.0, Timer_KeepWaiting, _, TIMER_REPEAT); + TriggerTimer(timer); + PrintToServer("[H&S] Late load, player(s) are connecting, or solo. Waiting..."); + SetState(State_Startup); + } + } RegConsoleCmd("sm_joingame", Command_Join, "Joins or joins someone else"); - RegAdminCmd("sm_hs_toggle", Command_ToggleBlockers, ADMFLAG_KICK, "Toggle nav blockers"); + RegAdminCmd("sm_hs_blockers", Command_ToggleBlockers, ADMFLAG_KICK, "Toggle nav blockers"); + RegAdminCmd("sm_hs_props", Command_ToggleProps, ADMFLAG_KICK, "Toggle props"); + RegAdminCmd("sm_hs_clear", Command_Clear, ADMFLAG_KICK, "Toggle props"); + +} + +public Action OnClientSayCommand(int client, const char[] command, const char[] sArgs) { + if(isEnabled) { + if(!StrEqual(command, "say")) { //Is team message + if(currentSeeker <= 0 || currentSeeker == client) { + return Plugin_Continue; + } + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i) && i != currentSeeker) + PrintToChat(i, "[Hiders] %N: %s", client, sArgs); + } + return Plugin_Handled; + } + } + return Plugin_Continue; } ArrayList LoadConfigForMap(const char[] map) { if (kv.JumpToKey(map)) { - strcopy(currentMapConfig, sizeof(currentMapConfig), map); ArrayList configs = new ArrayList(sizeof(EntityConfig)); - if(kv.JumpToKey("blockers")) { + ArrayList entInputs = new ArrayList(ByteCountToCells(64)); + if(kv.JumpToKey("ents")) { kv.GotoFirstSubKey(); do { EntityConfig config; @@ -114,56 +165,94 @@ ArrayList LoadConfigForMap(const char[] map) { kv.GetVector("rotation", config.rotation, NULL_VECTOR); kv.GetString("type", config.type, sizeof(config.type), "env_physics_blocker"); kv.GetString("model", config.model, sizeof(config.model), ""); + if(config.model[0] != '\0') + Format(config.model, sizeof(config.model), "models/%s", config.model); kv.GetVector("scale", config.scale, DEFAULT_SCALE); - config.toggleable = true; - configs.PushArray(config); } while (kv.GotoNextKey()); + // Both JumpToKey and GotoFirstSubKey both traverse, i guess, go back + kv.GoBack(); + kv.GoBack(); } - if(kv.JumpToKey("props")) { + if(kv.JumpToKey("inputs")) { + // Use 'false' to propery grab + // "key" "value" in a section + kv.GotoFirstSubKey(false); + static char buffer[64]; do { - EntityConfig config; - kv.GetVector("origin", config.origin, NULL_VECTOR); - kv.GetVector("rotation", config.rotation, NULL_VECTOR); - kv.GetString("type", config.type, sizeof(config.type), "env_physics_blocker"); - kv.GetString("model", config.model, sizeof(config.model), ""); - kv.GetVector("scale", config.scale, DEFAULT_SCALE); + kv.GetSectionName(buffer, sizeof(buffer)); + entInputs.PushString(buffer); - configs.PushArray(config); - } while (kv.GotoNextKey()); + kv.GetString(NULL_STRING, buffer, sizeof(buffer)); + entInputs.PushString(buffer); + } while (kv.GotoNextKey(false)); + kv.GoBack(); + } + + if(kv.GetVector("spawnpoint", spawnpoint)) { + hasSpawnpoint = true; + } else { + hasSpawnpoint = false; } - kv.GoBack(); // Store ArrayList handle mapConfigs.SetValue(map, configs); + // Discard entInputs if unused + if(entInputs.Length > 0) + mapInputs.SetValue(map, entInputs); + else + delete entInputs; return configs; - } else { + } else { return null; } } -public Action Command_ToggleBlockers(int client, int args) { - static char targetname[32]; - int entity = INVALID_ENT_REFERENCE; - while ((entity = FindEntityByClassname(entity, "env_physics_blocker")) != INVALID_ENT_REFERENCE) { - GetEntPropString(entity, Prop_Data, "m_iName", targetname, sizeof(targetname)); - if(StrEqual(targetname, "hsblocker")) { - if(isNavBlockersEnabled) - AcceptEntityInput(entity, "Disable"); - else - AcceptEntityInput(entity, "Enable"); +public Action Command_Clear(int client, int args) { + if(args > 0) { + static char arg[16]; + GetCmdArg(1, arg, sizeof(arg)); + if(StrEqual(arg, "props")) { + EntFire("hsprop", "kill"); + ReplyToCommand(client, "Removed all custom gamemode props"); + return Plugin_Continue; + } else if(StrEqual(arg, "blockers")) { + EntFire("hsblockers", "kill"); + ReplyToCommand(client, "Removed all custom gamemode blockers"); + return Plugin_Continue; + } } + ReplyToCommand(client, "Specify 'props' or 'blockers'"); + return Plugin_Continue; +} +public Action Command_ToggleBlockers(int client, int args) { if(isNavBlockersEnabled) { - ReplyToCommand(client, "Disabled all custom nav blockers"); + EntFire("hsblocker", "Disable"); + ReplyToCommand(client, "Disabled all custom gamemode blockers"); } else { - ReplyToCommand(client, "Enabled all custom nav blockers"); + EntFire("hsblocker", "Enable"); + ReplyToCommand(client, "Enabled all custom gamemode blockers"); } isNavBlockersEnabled = !isNavBlockersEnabled; return Plugin_Handled; } +public Action Command_ToggleProps(int client, int args) { + if(isPropsEnabled) { + EntFire("hsprop", "Disable"); + EntFire("hsprop", "DisableCollision"); + ReplyToCommand(client, "Disabled all custom gamemode props"); + } else { + EntFire("hsprop", "Enable"); + EntFire("hsprop", "EnableCollision"); + ReplyToCommand(client, "Enabled all custom gamemode props"); + } + isPropsEnabled = !isPropsEnabled; + return Plugin_Handled; +} + public Action Command_Join(int client, int args) { static float tpLoc[3]; if(args == 1) { @@ -178,7 +267,7 @@ public Action Command_Join(int client, int args) { client, target_list, MAXPLAYERS, - COMMAND_FILTER_ALIVE, + 0, target_name, sizeof(target_name), tn_is_ml)) <= 0) @@ -194,10 +283,15 @@ public Action Command_Join(int client, int args) { L4D_RespawnPlayer(target); TeleportEntity(target, tpLoc, NULL_VECTOR, NULL_VECTOR); isPendingPlay[client] = false; + CheatCommand(target, "give", "knife"); } } ReplyToCommand(client, "Joined %s", target_name); } else { + if(currentSeeker == client) { + ReplyToCommand(client, "You are already in-game as a seeker."); + return Plugin_Handled; + } for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) { GetClientAbsOrigin(i, tpLoc); @@ -208,58 +302,307 @@ public Action Command_Join(int client, int args) { ChangeClientTeam(client, 2); L4D_RespawnPlayer(client); TeleportEntity(client, tpLoc, NULL_VECTOR, NULL_VECTOR); + CheatCommand(client, "give", "knife"); } return Plugin_Handled; } -public void OnMapStart() { - static char map[16]; - GetCurrentMap(map, sizeof(map)); - - ArrayList configs; - if(!mapConfigs.GetValue(map, configs)) { - configs = LoadConfigForMap(map); - PrintToServer("H&S: Fetching config for map %s", map); - } - if(isEnabled) { - for(int i = 0; i < configs.Length; i++) { - EntityConfig config; - configs.GetArray(i, config); - if(config.model[0] != '\0') - PrecacheModel(config.model); - bool isEnabled = true; - if(config.toggleable && !isNavBlockersEnabled) { - isEnabled = false; - } - if(StrEqual(config.type, "env_physics_blocker")) { - CreateEnvBlockerBoxScaled(config.origin, config.scale, isEnabled); - } else { - CreateProp(config.model, config.origin, config.rotation); +public void OnClientConnected(int client) { + if(!IsFakeClient(client)) { + currentPlayers++; + if(isEnabled) { + GameState state = GetState(); + if(currentPlayers == 1 && state == State_Startup) { + CreateTimer(10.0, Timer_KeepWaiting, _, TIMER_REPEAT); } } - // - // if(StrEqual(map, "c8m3_sewers")) { - // if(isNavBlockersEnabled) { - // CreateEnvBlockerBox(C8M3_SEWERS_A, isNavBlockersEnabled); - // CreateEnvBlockerBox(C8M3_SEWERS_B, isNavBlockersEnabled); - // CreateProp(PROP_SHELF, C8M3_SEWERS_A_PROP, ROT_V_90_H_90); - // CreateProp(PROP_SIGN, C8M3_SEWERS_B, ROT_V_90); - // } - // } else if(StrEqual(map, "c3m4_plantation")) { - // if(isNavBlockersEnabled) { - // CreateEnvBlockerBoxScaled(C3M4_PLANTATION_A2, SCALE_FLAT_LARGE); - // CreateProp(PROP_DOCK, C3M4_PLANTATION_A2, ROT_H_90); - // } - // // CreateEnvBlockerBoxScaled(C3M4_PLANTATION_A, SCALE_FLAT_LARGE); - // } else if(StrEqual(map, "c1m3_mall")) { - // CreateProp(PROP_DUMPSTER, C1M3_MALL_A_PROP, ROT_H_90); - // if(isNavBlockersEnabled) { - // CreateEnvBlockerBoxScaled(C1M3_MALL_A, SCALE_TALL_MEDIUM); - // } - // } } } +public Action Timer_KeepWaiting(Handle h) { + L4D2_ExecVScriptCode("g_ModeScript.MutationState.StateTick = -40"); + SetState(State_Startup); + PrintHintTextToAll("Waiting for players to join..."); + return IsGameSoloOrPlayersLoading() ? Plugin_Continue : Plugin_Stop; +} + +public void OnClientDisconnect(int client) { + if(!IsFakeClient(client)) + currentPlayers--; +} + + +public void OnMapStart() { + if(!isEnabled) return; + + char map[64]; + GetCurrentMap(map, sizeof(map)); + + if(!StrEqual(currentMap, map)) { + strcopy(currentMap, sizeof(currentMap), map); + static char mapTime[16]; + L4D2_GetVScriptOutput("g_ModeScript.MutationState.MapTime", mapTime, sizeof(mapTime)); + PrintToServer("[H&S] Map %s has a time of %s seconds", map, mapTime); + + + if(!mapConfigs.GetValue(map, entities)) { + entities = LoadConfigForMap(map); + } + mapInputs.GetValue(map, inputs); + } + + #if defined DEBUG_BLOCKERS + g_iLaserIndex = PrecacheModel("materials/sprites/laserbeam.vmt"); + #endif + PrecacheSound(SOUND_SUSPENSE_1); + PrecacheSound(SOUND_SUSPENSE_1_FAST); + AddFileToDownloadsTable("sound/custom/suspense1.mp3"); + AddFileToDownloadsTable("sound/custom/suspense1fast.mp3"); + + if(lateLoaded) { + lateLoaded = false; + SetupEntities(); + } +} + +public Action L4D2_OnChangeFinaleStage(int &finaleType, const char[] arg) { + if(isEnabled) { + finaleType = 0; + return Plugin_Changed; + } + return Plugin_Continue; +} + + +public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[] newValue) { + cvar.GetString(gamemode, sizeof(gamemode)); + #if defined FORCE_ENABLED + isEnabled = true; + PrintToServer("[H&S] Force-enabled debug"); + #else + isEnabled = StrEqual(gamemode, "hideandseek", false); + #endif + if(isEnabled) { + HookEvent("round_end", Event_RoundEnd); + HookEvent("round_start", Event_RoundStart); + HookEvent("item_pickup", Event_ItemPickup); + HookEvent("player_death", Event_PlayerDeath); + SetupEntities(); + CreateTimer(15.0, Timer_RoundStart); + if(suspenseTimer != null) + delete suspenseTimer; + suspenseTimer = CreateTimer(20.0, Timer_Music, _, TIMER_REPEAT); + if(thirdPersonTimer != null) + delete thirdPersonTimer; + thirdPersonTimer = CreateTimer(1.0, Timer_CheckPlayers, _, TIMER_REPEAT); + } else if(!lateLoaded) { + UnhookEvent("round_end", Event_RoundEnd); + UnhookEvent("round_start", Event_RoundStart); + UnhookEvent("item_pickup", Event_ItemPickup); + UnhookEvent("player_death", Event_PlayerDeath); + delete suspenseTimer; + delete thirdPersonTimer; + } +} + + +public Action Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) { + int client = GetClientOfUserId(event.GetInt("userid")); + if(!gameOver && client && GetClientTeam(client) == 2) { + int alive = 0; + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) { + alive++; + } + } + if(client == currentSeeker) { + PrintToChatAll("Hiders win!"); + gameOver = true; + } else { + if(alive == 2) { + PrintToChatAll("One hider remains."); + } else if(alive == 1) { + // Player died and not seeker, therefore seeker killed em + if(client != currentSeeker) { + PrintToChatAll("Seeker %N won!", currentSeeker); + } else { + PrintToChatAll("Hiders win! The last survivor was %N!", client); + + } + gameOver = true; + } else if(alive > 2 && client != currentSeeker) { + PrintToChatAll("%d hiders remain", alive - 1); + } + } + } + return Plugin_Continue; +} + +public void OnClientPutInServer(int client) { + if(isEnabled && !IsFakeClient(client)) { + ChangeClientTeam(client, 1); + isPendingPlay[client] = true; + isNearbyPlaying[client] = false; + PrintToChatAll("%N will play next round", client); + } +} + +public Action Event_ItemPickup(Event event, const char[] name, bool dontBroadcast) { + int client = GetClientOfUserId(event.GetInt("userid")); + if(client && client > 0 && currentSeeker != client) { + static char item[32]; + event.GetString("item", item, sizeof(item)); + if(StrEqual(item, "melee")) { + int entity = GetPlayerWeaponSlot(client, 1); + GetEntPropString(entity, Prop_Data, "m_strMapSetScriptName", item, sizeof(item)); + if(StrEqual(item, "fireaxe")) { + gameOver = false; + currentSeeker = GetSlasher(); + if(currentSeeker != client) { + PrintToChatAll("[H&S] Seeker does not equal axe-receiver. Possible seeker: %N", client); + } + if(currentSeeker == -1) { + PrintToServer("[H&S] ERROR: GetSlasher() returned -1"); + currentSeeker = client; + } + PrintToChatAll("%N is the seeker", currentSeeker); + } + } + } + return Plugin_Continue; + +} + +public Action Event_RoundStart(Event event, const char[] name, bool dontBroadcast) { + if(hasSpawnpoint) { + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i)) { + TeleportEntity(i, spawnpoint, NULL_VECTOR, NULL_VECTOR); + } + } + } + SetupEntities(); + CreateTimer(15.0, Timer_RoundStart); + return Plugin_Continue; + +} + +public Action Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) { + currentSeeker = 0; + static float tpLoc[3]; + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) { + GetClientAbsOrigin(i, tpLoc); + break; + } + } + + for(int i = 1; i <= MaxClients; i++) { + isNearbyPlaying[i] = false; + if(isPendingPlay[i]) { + if(IsClientConnected(i) && IsClientInGame(i)) { + ChangeClientTeam(i, 2); + L4D_RespawnPlayer(i); + TeleportEntity(i, tpLoc, NULL_VECTOR, NULL_VECTOR); + } + isPendingPlay[i] = false; + } + } + return Plugin_Continue; + +} + +public Action Timer_CheckPlayers(Handle h) { + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i) && i != currentSeeker) + QueryClientConVar(i, "cam_collision", QueryClientConVarCallback); + } + return Plugin_Continue; +} + +public void QueryClientConVarCallback(QueryCookie cookie, int client, ConVarQueryResult result, const char[] sCvarName, const char[] bCvarValue) { + int value = 0; + if (result == ConVarQuery_Okay && StringToIntEx(bCvarValue, value) > 0 && value == 0) { + wasThirdPersonVomitted[client] = true; + PrintHintText(client, "Third person is disabled in this mode"); + // L4D_OnITExpired(client); + // L4D_CTerrorPlayer_OnVomitedUpon(client, client); + float random = GetRandomFloat(); + if(random < 0.3) + PerformScene(client, "Playerareaclear"); + else if(random <= 0.6) + PerformScene(client, "PlayerLaugh"); + else + PerformScene(client, "PlayerDeath"); + } else if(wasThirdPersonVomitted[client]) { + wasThirdPersonVomitted[client] = false; + L4D_OnITExpired(client); + } +} + +public Action Timer_Music(Handle h) { + static float seekerLoc[3]; + static float playerLoc[3]; + if(currentSeeker > 0) { + GetClientAbsOrigin(currentSeeker, seekerLoc); + GameState state = GetState(); + if(state == State_Hunting) { + EmitSoundToClient(currentSeeker, SOUND_SUSPENSE_1, currentSeeker, SNDCHAN_AUTO, SNDLEVEL_NORMAL, SND_CHANGEPITCH, 0.2, 90, currentSeeker, seekerLoc, seekerLoc, true); + } + } + int playerCount; + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i) && i != currentSeeker) { + + playerCount++; + GetClientAbsOrigin(i, playerLoc); + float dist = GetVectorDistance(seekerLoc, playerLoc, true); + if(dist <= 250000.0) { + StopSound(i, SNDCHAN_AUTO, SOUND_SUSPENSE_1); + EmitSoundToClient(i, SOUND_SUSPENSE_1_FAST, i, SNDCHAN_AUTO, SNDLEVEL_NORMAL, SND_CHANGEPITCH, 1.0, 100, currentSeeker, seekerLoc, playerLoc, true); + isNearbyPlaying[i] = true; + } else if(dist <= 1000000.0) { + EmitSoundToClient(i, SOUND_SUSPENSE_1, i, SNDCHAN_AUTO, SNDLEVEL_NORMAL, SND_CHANGEPITCH, 0.2, 90, currentSeeker, seekerLoc, playerLoc, true); + isNearbyPlaying[i] = true; + StopSound(i, SNDCHAN_AUTO, SOUND_SUSPENSE_1_FAST); + } else if(isNearbyPlaying[i]) { + isNearbyPlaying[i] = false; + StopSound(i, SNDCHAN_AUTO, SOUND_SUSPENSE_1_FAST); + StopSound(i, SNDCHAN_AUTO, SOUND_SUSPENSE_1); + } + } + } + + return Plugin_Continue; +} +public Action Timer_RoundStart(Handle h) { + CreateTimer(0.1, Timer_CheckWeapons); + CreateTimer(10.0, Timer_CheckWeapons); + int entity = INVALID_ENT_REFERENCE; + while ((entity = FindEntityByClassname(entity, "func_button")) != INVALID_ENT_REFERENCE) { + AcceptEntityInput(entity, "Press"); + } + entity = INVALID_ENT_REFERENCE; + while ((entity = FindEntityByClassname(entity, "func_brush")) != INVALID_ENT_REFERENCE) { + AcceptEntityInput(entity, "Kll"); + } + PrintToServer("[H&S] Pressing buttons"); + return Plugin_Continue; +} +public Action Timer_CheckWeapons(Handle h) { + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) { + // Check if has no melee: + if(GetPlayerWeaponSlot(i, 1) == -1) { + CheatCommand(i, "give", "knife"); + } + int item = GetPlayerWeaponSlot(i, 0); + if(item != -1) AcceptEntityInput(item, "Kill"); + } + } + return Plugin_Continue; +} + stock int CreateEnvBlockerBox(const float pos[3], bool enabled = true) { int entity = CreateEntityByName("env_physics_blocker"); DispatchKeyValue(entity, "targetname", "hsblocker"); @@ -269,12 +612,11 @@ stock int CreateEnvBlockerBox(const float pos[3], bool enabled = true) { DispatchSpawn(entity); if(enabled) AcceptEntityInput(entity, "Enable"); - PrintToServer("spawn blocker %f %f %f", pos[0], pos[1], pos[2]); return entity; } -stock int CreateEnvBlockerBoxScaled(const float pos[3], const float scale[3], bool enabled = true) { - int entity = CreateEntityByName("env_physics_blocker"); +stock int CreateEnvBlockerScaled(const char[] entClass, const float pos[3], const float scale[3] = { 5.0, 5.0, 5.0 }, bool enabled = true) { + int entity = CreateEntityByName(entClass); DispatchKeyValue(entity, "targetname", "hsblocker"); DispatchKeyValue(entity, "initialstate", "1"); DispatchKeyValue(entity, "BlockType", "0"); @@ -292,106 +634,145 @@ stock int CreateEnvBlockerBoxScaled(const float pos[3], const float scale[3], bo SetEntPropVector(entity, Prop_Send, "m_vecMins", mins); if(enabled) AcceptEntityInput(entity, "Enable"); - PrintToServer("spawn blocker scaled %f %f %f scale [%f %f %f]", pos[0], pos[1], pos[2], scale[0], scale[1], scale[2]); + #if defined DEBUG_BLOCKERS + Effect_DrawBeamBoxRotatableToAll(pos, mins, scale, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 150.0, 0.1, 0.1, 0, 0.0, {0, 255, 0, 255}, 0); + #endif + #if defined DEBUG_LOG_MAPSTART + PrintToServer("spawn blocker scaled %.1f %.1f %.1f scale [%.0f %.0f %.0f]", pos[0], pos[1], pos[2], scale[0], scale[1], scale[2]); + #endif return entity; } -stock int CreateProp(const char[] model, const float pos[3], const float ang[3]) { - int entity = CreateEntityByName("prop_dynamic"); +stock int CreatePropDynamic(const char[] model, const float pos[3], const float ang[3]) { + return CreateProp("prop_dynamic", model, pos, ang); +} + +stock int CreatePropPhysics(const char[] model, const float pos[3], const float ang[3]) { + return CreateProp("prop_physics", model, pos, ang); +} + +stock int CreateProp(const char[] entClass, const char[] model, const float pos[3], const float ang[3]) { + int entity = CreateEntityByName(entClass); DispatchKeyValue(entity, "model", model); DispatchKeyValue(entity, "solid", "6"); DispatchKeyValue(entity, "targetname", "hsprop"); DispatchKeyValue(entity, "disableshadows", "1"); TeleportEntity(entity, pos, ang, NULL_VECTOR); DispatchSpawn(entity); - PrintToServer("spawn prop %f %f %f", pos[0], pos[1], pos[2]); + #if defined DEBUG_LOG_MAPSTART + PrintToServer("spawn prop %.1f %.1f %.1f model %s", pos[0], pos[1], pos[2], model[7]); + #endif return entity; } -public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[] newValue) { - cvar.GetString(gamemode, sizeof(gamemode)); - #if defined FORCE_ENABLED - isEnabled = true; - #else - isEnabled = StrEqual(gamemode, "hideandseek"); - #endif - if(isEnabled) { - HookEvent("player_first_spawn", Event_PlayerFirstSpawn); - HookEvent("round_end", Event_RoundEnd); - HookEvent("round_start", Event_RoundStart); - } else if(!lateLoaded) { - UnhookEvent("player_first_spawn", Event_PlayerFirstSpawn); - UnhookEvent("round_end", Event_RoundEnd); - UnhookEvent("round_start", Event_RoundStart); - } -} +stock void CheatCommand(int client, const char[] command, const char[] argument1) { + int userFlags = GetUserFlagBits(client); + SetUserFlagBits(client, ADMFLAG_ROOT); + int flags = GetCommandFlags(command); + SetCommandFlags(command, flags & ~FCVAR_CHEAT); + FakeClientCommand(client, "%s %s", command, argument1); + SetCommandFlags(command, flags); + SetUserFlagBits(client, userFlags); +} -public Action Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBroadcast) { - int client = GetClientOfUserId(event.GetInt("userid")); - if(client && GetClientTeam(client) != 2 && !isPendingPlay[client]) { - PrintToChat(client, "You will be put in game next round."); - isPendingPlay[client] = true; - } -} - -public Action Event_RoundStart(Event event, const char[] name, bool dontBroadcast) { - CreateTimer(10.0, Timer_CheckItems); -} - -public Action Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) { - static float tpLoc[3]; - for(int i = 1; i <= MaxClients; i++) { - if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) { - GetClientAbsOrigin(i, tpLoc); - break; - } - } - - for(int i = 1; i <= MaxClients; i++) { - if(isPendingPlay[i]) { - isPendingPlay[i] = false; - ChangeClientTeam(i, 2); - L4D_RespawnPlayer(i); - TeleportEntity(i, tpLoc, NULL_VECTOR, NULL_VECTOR); - } - } -} - -/// -public Action Timer_CheckItems(Handle h) { - for(int i = 1; i <= MaxClients; i++) { - if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) { - // Check if has no melee: - if(GetPlayerWeaponSlot(i, 1) == -1) { - GiveClientWeapon(i, "knife", false); +stock void EntFire(const char[] name, const char[] input) { + static char targetname[64]; + for(int i = MAXPLAYERS + 1; i <= GetMaxEntities(); i++) { + if(IsValidEntity(i) && IsValidEdict(i)) { + GetEntPropString(i, Prop_Data, "m_iName", targetname, sizeof(targetname)); + if(StrEqual(targetname, name)) { + AcceptEntityInput(i, input); } - int item = GetPlayerWeaponSlot(i, 0); - if(item != -1) AcceptEntityInput(item, "Kill"); } } - PrintToServer("H&S: Pressing buttons"); - int entity = INVALID_ENT_REFERENCE; - while ((entity = FindEntityByClassname(entity, "func_button")) != INVALID_ENT_REFERENCE) { - AcceptEntityInput(entity, "Press"); - } } -stock bool GiveClientWeapon(int client, const char[] wpnName, bool lasers) { - char sTemp[64]; - float pos[3]; - GetClientAbsOrigin(client, pos); - Format(sTemp, sizeof(sTemp), "weapon_%s", wpnName); +void SetupEntities() { + if(entities != null) { + PrintToServer("[H&S] Found map entity config, deploying %d entities", entities.Length); + for(int i = 0; i < entities.Length; i++) { + EntityConfig config; + entities.GetArray(i, config); + + if(config.model[0] != '\0') PrecacheModel(config.model); + + if(StrEqual(config.type, "env_physics_blocker")) { + CreateEnvBlockerScaled(config.type, config.origin, config.scale, isNavBlockersEnabled); + } else { + CreateProp(config.type, config.model, config.origin, config.rotation); + } + } + + static char key[64]; + static char value[64]; + if(inputs != null) { + for(int i = 0; i < inputs.Length - 1; i += 2) { + inputs.GetString(i, key, sizeof(key)); + inputs.GetString(i + 1, value, sizeof(value)); + EntFire(key, value); + #if defined DEBUG_LOG_MAPSTART + PrintToServer("[H&S] EntFire: %s %s", key, value); + #endif + } + } - int entity = CreateEntityByName(sTemp); - if( entity != -1 ) { - DispatchSpawn(entity); - TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR); - if(lasers) SetEntProp(entity, Prop_Send, "m_upgradeBitVec", 4); - EquipPlayerWeapon(client, entity); - return true; - }else{ - return false; } +} + +GameState GetState() { + if(!isEnabled) return State_Unknown; + static char buffer[4]; + L4D2_GetVScriptOutput("g_ModeScript.MutationState.CurrentStage", buffer, sizeof(buffer)); + int stage = -1; + if(StringToIntEx(buffer, stage) > 0) { + return view_as(stage); + } else { + return State_Unknown; + } +} + +int GetSlasher() { + if(!isEnabled) return -1; + static char buffer[8]; + L4D2_GetVScriptOutput("g_ModeScript.MutationState.CurrentSlasher ? g_ModeScript.MutationState.CurrentSlasher.GetPlayerUserId() : -1", buffer, sizeof(buffer)); + int uid = StringToInt(buffer); + if(uid > 0) { + return GetClientOfUserId(uid); + } else { + return -1; + } +} + +int GetTick() { + if(!isEnabled) return -1; + static char buffer[4]; + L4D2_GetVScriptOutput("g_ModeScript.MutationState.StateTick", buffer, sizeof(buffer)); + int value = -1; + if(StringToIntEx(buffer, value) > 0) { + return value; + } else { + return value; + } +} + +bool SetState(GameState state) { + if(!isEnabled) return false; + static char buffer[64]; + Format(buffer, sizeof(buffer), "g_ModeScript.MutationState.CurrentStage = %d", view_as(state)); + return L4D2_ExecVScriptCode(buffer); +} + +bool IsGameSoloOrPlayersLoading() { + int connecting, ingame; + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i)) { + if(IsClientInGame(i)) + ingame++; + else + connecting++; + } + } + return connecting > 0 || ingame == 1; } \ No newline at end of file