From 9e7463010b9c1ee9f55cb8904a2773ada456b6a5 Mon Sep 17 00:00:00 2001 From: Paimon <77665174+Paimon-Kawaii@users.noreply.github.com> Date: Mon, 25 Mar 2024 22:15:24 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E6=9B=B4=E6=96=B0SIPool=E4=B8=8EInfectedCo?= =?UTF-8?q?ntrol?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新SIPool与InfectedControl,修正了[Issue #5](https://github.com/Paimon-Kawaii/L4D2-Plugins/issues/5)中提出的问题。 --- .../optional/AnneHappy/infected_control.smx | Bin 27524 -> 27267 bytes .../plugins/optional/AnneHappy/si_pool.smx | Bin 8326 -> 9295 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/addons/sourcemod/plugins/optional/AnneHappy/infected_control.smx b/addons/sourcemod/plugins/optional/AnneHappy/infected_control.smx index 08d7784e21f3fe6c6d9e451275d570319bc01a63..8b201fbdfbcb49eadcb0613f36d48aa08db90c47 100644 GIT binary patch literal 27267 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 KmEm8kPIW4}+EwBJ literal 27524 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 IU23r8>0Cm(OB$9AsRfpX<=x*K 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 O*zM_`N&NHSQ~w3xPW>(b delta 8192 zcmXY$byQSs*T&~D2$2qH1f-Sj?hYkIx}+HxkcJ~Nbf?lFEgjPFFd!-2ISwf~Lku0? zJn#DcxbEw>&)R42``qWOb!uESTnz6XEkGb5jF(_!iQ)h98!&oq2?BXwdi{&xAS)24^$BKjHc9uR zfN7AI{=g>y%<|?h-W%o5{{~j#Q!Yj-sq*8=~PD!iLoR73~CAusn zE^&wsXsN2}CNI2kE!WU-OwJX>jWUaP<)%q3##o+`IOFE1Nt>w~(Nt878x;T`*P116 zynQ}HbI%j@vM0gYYgf4vKHFo<8O&VxKw;yOAj(K%Ov4TKryJBdFldmBmt>;uR)f+E z6DdF(PNQ-sP`PuccxPWd>NO60WM#OIT(3et<#;LLN~l8_A6ytdUx>hW@^!>VzQYH1 zhhIR$eH42GKJ-ddBeWcSO9eX`sMnP6Yi0_meDKF>5JFDAG(sKH_+ZW6a1xP2~~gKcGwRAbr zkFeZ3LG9h8X`D_*IMRF#l7s&p>^B%$d9T-zkWRtA%xsqj9z5?Xl{K9J>^bgVl$NX= z%v5Ra8My=|Ce)|~%v1jUy&#xu{!E;!nIpx*Qln2YZQ4YGze~6 z&ufdmow@fI?@4}S6Mi;rEBOh1%yF;xbWiV7+TkhteM9%hV9rCB&~LnlY6EkFdET0* z7==53n-8xT#On~B)UgNbxAr?a`LQ8$>MZc3N1i=7UAO$=18ZOP?KX_qx5WHrEvu?t z=MF%O1M)rN$A?ODIvMl4;Ut~(`pa+2E9r_G8qmVzbT|)|ECb~^DP{xdoRZDCOI%yu z>YH3i0~HPz3oXTQ;I;UbVaK4r}e|>K9jOOiL$8X_fbBL zsvYv5gze19z7rZ!xE1d1$;4hLpP85mA0Bg>BPI69B=pOT;%-d!dn>BA#{|N{Pwq?m z{aY(E6PN;|i)8@%>EAqq2gM?5z8jF!kt8G@S@S|}9oXEB_7g$=c+bH=UZan;y9*&FD+?m0vIXLk_ya&YaDPEf4Ta)&Y_N#eba6pU7W{qQyLd z+`8DR$Kj-9e&T4a1mkjnj{=JnVqGu0D~s_?PTlx|J3Dgxk;eHo&20lM$sIAX$Lc2@ zd*1IZk6Pj-wJi#=RSa!UKTVN;2rVU_AUGG=Px~?n-%qpIiOo<+jjcYB+)4j&p8(yH2*n~Su(w=z2+8# zPX1dV5Chq0K^q)eZ%N1}{d|5m`}SW}ZhbfVICF5t{#O(G_8X^a=QpnG);>Q1CU0U6 zr0RA8{XFjPom(N*`oAq49*_rISa+>AT^aoLGhV=$cu|vGNu%DOM(ltz;Ff39dfhQ0 z>3x=|S4#TyAmnV=G$W+?NAk~pvEE~NejqbD6W$o5AlFuz&EZV(!a^<=f791#@J{q! zky2^x*0J12_a;TT*msM*+If5DLry{I9QQWrc{8nNYIUC6H|<8Btd>1;or7|KG|!vM zxND&GZYu8K=Cm2{>|i5Bz1!Tva@_xixCof-=*y%1ex>;~ghgfrl81Bmg}Q(2&^JhW zl;F`-$@%1wzu1Q959COU$cs5d%nH2X$EmBmkI4%>d~}6HI+xAG3o&N>3)5gb+Vf$* zMn=!}2L1{a1R#4DV;5Z|z?=n-8#V?A_ zHZIH9`_Y;??1<`m!drCM@>FSK@%U_d+qH||IECW~7Ry)|ldH%4$YkjSSHNd1F(9I2 z#m_7)+ttl;S4AgD`u%%Z8SyLqmJ<&o8+SB;(FR#o@7_|o(VV6|KDEA=WhhV9eaWM? zg2Y(u_Tkn8X<%k_=Z4cS=)|l?++3A_xeEpRAv;X8qgjThvq`$es?$KKs(BOmDAuaY@DuQZ-tvK17ra&j`BodFgU`xW(0 zuS-hMqx+ksR_7LJ2V1Ma;qfc!Rhr%}bd&;N-LvZ8x-YxOMYCC~sv%9Q{_c|5jZp1? zjkje|^l?}=1xN0juD>c@tt)OwR7uuP3PZsB*-cC9$Ey7@8BMgn&3oS6%YcFPp~fUF zPp~8vJRkEyqAJAg!t($c5^?XgIt@E)r%es(QBMMHh(!Yq3n&VBWN4Or_ z3R3c!`V}3ZytydrvoYwD`ZhfK?PXGN7Zjxj9aQfh;e-x zn)E%cUEOTLIBxO;>e9BVB(J8RkOTHsNAFXJ{d!m z4Og~X5(;{@k_vj8k|w2{`fFC_)jzIfDT7CL z({M71IQM;(OD1EK1KjDSc}Zr(4IoMgplZR*Cb&}yh7KAF(QoND_)=$`i(8)^d^K;p zW14QV_+fd|>U*v+z8K75eq8AAEM;yaRqkGsYIFR5)|8&gKbXf6dYi$|AIuT@(?OdQ ziL2cIrj+NXQMde0S=ufQTZww9WP@BG8rHh&EGTQ9qNp^-8c^ z&A2TxnJe35r!sPD;$$zIyp{HYc}@ebEslys4gWz$3Lu zo?P}&VIV&I$w$7F6fR?_g`(vUiM@w#k8=xH;YWlL14b@T|AA*$9N0j>E0na(^2RO!?^Hin+=5!gs+!@ z4lixofZ-bP3ij4mu?gWe-ZVaX0n%zmZ^dYar6G=x_8}?tBM%7npp@JZ zZ!1u^>0fwEw=kHrq7;%Nc=Ne4%AGp5xi7-!eIt?8F-jm(T&7NO9dRHQ}^*d(a|POrOS_+#`wJyseJ zx5E1^%SpTvwre+0q#3RtwspgyH}j@NfHM9`1e2k5Mjt~{X(a$%Ff8j~z}F@y@CH0i zez86d`K(J21@YH>GqAhiTI5+4>U-WmrpJpK^Vp+HbzF80GUTVy;rF@ouT)R6qlKue$ao1hDBZP{<4d-%FEqdqDDb6=?z; zCpAAqHP7O++y+cX;7)tuZ>YfrZ~;xGs&IoZl3)WitgP)#jcAg}IA_jek8hp5ehIG@wz=`P#ihb0W2TMImVvJOXVPQ5DN0 zHOm}>pL=l6uLX>}M0ApFwOgoVAL8_^XM4qa}?mn@uuSl?zEVulT6(k|)hO$-1 z_iAireyCIa=o3VS&4_Q5_hX8QT6NkA1qAu>1P@#E%H)k*7*iygsJThCjzQMIt5n&*pCFy#1#cgp3t2Gy zyxP@d5g6Y-?`m(NdHnd5&*O6hc|cAO%~R5(5Pi%=^Z0S&8&$cdu>`S`iZpL$2S1zm ziFss~OC^@7W>fA#X$CXap4!-^HoD`DXZ%Bq{%b2LOd|5Af+lOe@ri%3;ktRI*s&Q$ zd;+wHM_1Wnj5T)N7xae)x3AOL7MiZ>70t&_PqXHR`%BfOE|i&e_DJZmOxM|@ zMRygkaH$x=N&X#-uUr*xDee$!7Z@^IL1R>dYum=Ufjis>w|h!Dv(*t`GgteDOx54n z29L&3Kbl|qsVYYRJmQt}{1e+$+YV0!GCQr)OwfF`2oNpqH$%Kv8dRGvl z5VHMqf9~z`0e)UGaK7K_9xEBRSi*t%=JP6hFC9VSL?ZSz?42t4VLq9m+8Z_$sqW=*#cS4`vxw62>nJpJz<1EN!Z% zG;Gb(3L_Pn-`qS9W%VdH_)nbor&#GbMKVbXx@gd9@i@csSBr?>FE1^8Gjj6QGsz}( za5bGLYvIDeqL!o3bxE~l$|Dhm6-0$NxPM9dA+Nz-3|LGrQT~k`@LBk&LlrGD(UyJ< zBY0j*>$8wG5~$8juhGo1(KF$m=#odNrNCTUT~|DtJJM6cvP_iQ>m(|N74S;bB}q;s zeA`9+&{D5Hi{F`F810p!PhE6$5WVa)9=VXdXgeGo`&Pg&xN*RSFY|eZyNak#Hls(p zpihdU67W;O!)?EPr}{~sB;`AU_N-6#yse=TmU^5D9T83Q%qf>s9tp1>o0Vq|FQlY$ znDLdd2A=_*AQ9n5{o@>{yE8#9r1apU#s+Dm2q~uJITNgma7Mq|cByX7GY5(b|9B&#D zLl|m*8$wqT56v5sKFavTs8KgHW{u^soLm%F0Bw#_iKr}+0yVA3x+yu}%Vm{MFbWQS zQ3ik~9<}{Fa2f_3f}Q>%-GktUTBeP_LGIFxc>1YlJ|A}LL_ev#E=pJ1s9mH8HVUbH z)>d28tg_Cb(fE@qeVTJ7(ffDc2f3;pj=-8uigRU$>*HgZl=dk@eH@O!CLJW_5^RD1Acg@iuDe`DzJMaTOAwtR96g|W$I;&Y z{rD~5BuZ5>L_u>%I1tXf!C1jCEi4-ntCfWlJ}`~IIhVHjD?uTSD| zHORA%A+9eH%=5)Y=icTSSY{z5w2zE_cM)eDdF@&iAAN2=#zNYN)N1U>$c4^3 zvv!2f3wGuT>zG6}3nE-!s~PlUD8n|Gb=AICd3|yUH(oNFGqkSGmE#~f^Ow@{rnX4I zWq0K&-{TE52g8=Pi*Yvf_!|VyGFbpZRu_5}wC(l`;0oQ|Z*DSDkG5-)U?8%b&ug_boX9y9-gVs|lJC?uqEN$G-Hwx9K40m~&i)Gm2U(k?R! zSj**?qfcCB3$d0f%CKGLbf{dKrJZXJuU-8{){*#4R>Xwf2MN!c>>hqu2As=0Z*t_J za+#MGwOUTka)FNtF7_*R=8}rLtbRy2Toq4oT_YTkAi&dX3OW|1J+O#<>hh&P>2g>A)Q8^|>ao+hv68N#oDgfXy>;xoJ%!I#+@#RnWexcM%gt)N(q79e34 z?J#UJ#Qcb7Z+u|E62td4mtRQya)RzKcqwV{yY<(Ja0vuhS?SpGHa+?PY_$UT7}e5J zT+{sc16cvaz<5ElG4N@Jl- zJlP0VfiAOk1(~kG>>cFkmSjZfuyOZc`gxGy(uvrW+0xEdv+VdF_&1-9w?YY@_h#2i zq|mxqe+pK@fY=~zf1K^{2-XPdl&J!UyYYlt^dKMOTKpRz2xUVVK0_F7Np*K8U0ijs zA`i8OZu|hAb)Pb*#lRv3Ld1Q(x7TiCwGAvEj@um;fCB|$+W9ATvG69Fw_ID0vKBt4 zCNRMRl~H6M?h1!jrqxTgz8?nb%uOxar*!;812(Z-2$>BDbE=E2Lc2;I6#eWzJy(C+_CPnH*$w)@?N5m`3daGW1N_bBOmv9u*|rH0N`82|_7iua$P|(I z{P{gxfDuWGIB-uy;m1A*&iP<_bg5hl77W;S8q52}*OKj@cu5zUssCkJQ57BC^zjB> z(vW_CK}cly?bcA61wqDm7p9m)CGUG$Tmes97btL0Ld80w#~-a6-i&V)&5?^snsHvMN^h%?j+zD5AnL^&QrM# zDL#TA)fUoIXJI5bestUC6VKSTgo>}kBk|@Cl-UF+6sdgk^mq)$%nlHASDmTBFwe5Y zU4(bqm;d~=gAp>xEP;c`4w-K$&w5RR6MVj)F8I24PP!ar5Dd(K1x!Y+eY<{5T>L9~ zX2x_*k8P(uS6m7cWVBY6a*x_c85j-t|0s?}ldUm+4qnn5xW)yO%QDcF+XQ)iL3QUx zY}c7DNhP33js`7~C3Yt?s!oj=&?8FqgMo7jEhz+@4Pfd09Of^2r^XN26n#PNe+&HO zR`$c`mzX1O7JvyA9=vq|p)=izmq$_xGKe+)v+35Sm3iVX>dn<(+jgE^%Fk)sU;^>h zL`;uMd3fV%>!9iGNc8U`rAni7OmNc2G{@Gm{45CKO}tNfyFh>a#OFhS%m<2w`-O&Y zXjs9oN^An2?JGc+=t8~#dyM;|l)Et{2TCn*rbyRh3JkIVo5KA2+mpk{bq>1xW5$=eTq;k^EO{OYK@+Jm)x5JR2fu zgd|8e{OaLBT($uAt)ZrF6L8@${T2&Y+IHe z(kC6o^G+I3s@c_ta%_x13H-%|jb{edY*Nl$nOsW#5k%}v0?={UTtmu!ivoZFw;Lp8 z+=EoPAPy$J!GiHvt>AXMoY2M9$eQC=eD1(mck45H2Zq(ijnVjm{bf;th?q#7OZ-&$ zzvo6$RK0L{%o%uc#du3FX9YukzlXvVuAqQ@{VIvbscY_sUt_#mBDeIdqy|jhD&-KJ zA0Li}ekYy@ooI*XtedlW06VHH;!zS7>)a_VXcC{|4*S{3N7pKlqD6OygxcR)6-%H_|x`<8jfX{-|#h8T`JqmlFHC6c5KVKR_q zi&jiSi}RCml4HLW^JN33uDE5aIQ2ekNWj%z99BVJSW&LOty1LvgPUkF@T3teQmY8W!%r|Z(@F36b3 zw|5-Wo~}IpdQv4!>I7D2Tpy0b&GA-^48^=u2P(4GlTHW!b?+wPDeFUyq`ofRMbxB8 z0wGu3Uy!@DC$E81)TU=7(T`+K#$3|xIrcd*4g&}0C{rin-Du;)$i*~w(mEw6v0kM< z>7RxDN_`*EzHz$ZkqZUa_MCk4skTvx6gT3xs<=oc6DKi_ZB;dWM(WWxo&`TvGewDU zvuAvI*B9!(4?n@xy4MxYh>Pv5t?j)B>82T(SaWZ9u^oXY?T$4`B|)Lxp>PUbl}KMN zY0{wh*Gd|G^P5D+(J^f0M2zXaoc#g_rX}4AZYJ$nK99HDP8kXLFT)%(1_PpeRT#L+TzLGCEt=CV(5#eA|{w1@Q|PcwLNzxe?!*7XE0t5Z$D+yE5oTjmoSK z+X&JzDNl3X`LJ)(GeP(KU`tPfEY3xR>kOgidUh@onlqfmGKEAzHR`(-`cc~RK}w9@ a_xVq8`bVe4s)wFxY#B4&i~I Date: Mon, 25 Mar 2024 22:16:16 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E6=9B=B4=E6=96=B0SIPool=E7=9A=84gamedata?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新SIPool的gamedata --- addons/sourcemod/gamedata/si_pool.txt | 28 ++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/addons/sourcemod/gamedata/si_pool.txt b/addons/sourcemod/gamedata/si_pool.txt index a2e6b4341..7aa7d5d31 100644 --- a/addons/sourcemod/gamedata/si_pool.txt +++ b/addons/sourcemod/gamedata/si_pool.txt @@ -2,7 +2,7 @@ * @Author: 我是派蒙啊 * @Last Modified by: 我是派蒙啊 * @Create Date: 2024-02-17 16:48:01 - * @Last Modified time: 2024-02-18 18:43:04 + * @Last Modified time: 2024-03-25 19:03:25 * @Github: https://github.com/Paimon-Kawaii */ "Games" @@ -42,6 +42,32 @@ "linux" "@_Z22NextBotCreatePlayerBotI6HunterEPT_PKc" } + "NextBotCreatePlayerBot" + { + "library" "server" + "linux" "@_Z22NextBotCreatePlayerBotI6JockeyEPT_PKc" + } + "NextBotCreatePlayerBot" + { + "library" "server" + "linux" "@_Z22NextBotCreatePlayerBotI7SpitterEPT_PKc" + } + "NextBotCreatePlayerBot" + { + "library" "server" + "linux" "@_Z22NextBotCreatePlayerBotI7ChargerEPT_PKc" + } + "NextBotCreatePlayerBot" + { + "library" "server" + "linux" "@_Z22NextBotCreatePlayerBotI6SmokerEPT_PKc" + } + "NextBotCreatePlayerBot" + { + "library" "server" + "linux" "@_Z22NextBotCreatePlayerBotI6BoomerEPT_PKc" + } + /* * CTerrorPlayer::SetClass(CBaseEntity *, int) */ From 0e5b353a3b06044e336a3fc6c6309dc2f45ca94f Mon Sep 17 00:00:00 2001 From: Paimon <77665174+Paimon-Kawaii@users.noreply.github.com> Date: Mon, 25 Mar 2024 22:34:29 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=90=8E=E7=9A=84SIPool=E6=BA=90=E7=A0=81=EF=BC=8C=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3infected=5Fcontrol=E4=B8=AD=E5=AD=98=E5=9C=A8=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E5=B9=B6=E8=BF=9B=E8=A1=8C=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=83=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 上传更新后的SIPool源码,修正infected_control中存在的问题并进行代码情理 --- .../scripting/AnneHappy/infected_control.sp | 2884 ++++++++--------- .../sourcemod/scripting/AnneHappy/si_pool.sp | 950 +++--- 2 files changed, 1882 insertions(+), 1952 deletions(-) diff --git a/addons/sourcemod/scripting/AnneHappy/infected_control.sp b/addons/sourcemod/scripting/AnneHappy/infected_control.sp index e3ce063b1..c1bdfadf2 100644 --- a/addons/sourcemod/scripting/AnneHappy/infected_control.sp +++ b/addons/sourcemod/scripting/AnneHappy/infected_control.sp @@ -12,233 +12,247 @@ #include #include -#define CVAR_FLAG FCVAR_NOTIFY -#define TEAM_SURVIVOR 2 -#define TEAM_INFECTED 3 +#define CVAR_FLAG FCVAR_NOTIFY +#define TEAM_SURVIVOR 2 +#define TEAM_INFECTED 3 // 数据 -#define NAV_MESH_HEIGHT 20.0 -#define PLAYER_HEIGHT 72.0 -#define PLAYER_CHEST 45.0 -#define HIGHERPOS 300.0 -#define HIGHERPOSADDDISTANCE 300.0 +#define NAV_MESH_HEIGHT 20.0 +#define PLAYER_HEIGHT 72.0 +#define PLAYER_CHEST 45.0 +#define HIGHERPOS 300.0 +#define HIGHERPOSADDDISTANCE 300.0 #define INCAPSURVIVORCHECKDIS 500.0 -#define NORMALPOSMULT 1.4 +#define NORMALPOSMULT 1.4 // 启用特感类型 -#define ENABLE_SMOKER (1 << 0) -#define ENABLE_BOOMER (1 << 1) -#define ENABLE_HUNTER (1 << 2) -#define ENABLE_SPITTER (1 << 3) -#define ENABLE_JOCKEY (1 << 4) -#define ENABLE_CHARGER (1 << 5) +#define ENABLE_SMOKER (1 << 0) +#define ENABLE_BOOMER (1 << 1) +#define ENABLE_HUNTER (1 << 2) +#define ENABLE_SPITTER (1 << 3) +#define ENABLE_JOCKEY (1 << 4) +#define ENABLE_CHARGER (1 << 5) // Spitter吐口水之后能传送的时间 -#define SPIT_INTERVAL 2.0 +#define SPIT_INTERVAL 2.0 //确认为跑男的距离 -#define RushManDistance 1200.0 - -stock const char InfectedName[10][] = -{ - "common", - "smoker", - "boomer", - "hunter", - "spitter", - "jockey", - "charger", - "witch", - "tank", - "survivor" +#define RushManDistance 1200.0 + +stock const char InfectedName[10][] = { + "common", + "smoker", + "boomer", + "hunter", + "spitter", + "jockey", + "charger", + "witch", + "tank", + "survivor" }; - #if (DEBUG) char sLogFile[PLATFORM_MAX_PATH] = "addons/sourcemod/logs/infected_control.txt"; #endif + // 插件基本信息,根据 GPL 许可证条款,需要修改插件请勿修改此信息! -public Plugin myinfo = +public Plugin myinfo = { - name = "Direct InfectedSpawn", - author = "Caibiii, 夜羽真白,东", - description = "特感刷新控制,传送落后特感", - version = "2023.09.04", - url = "https://github.com/fantasylidong/CompetitiveWithAnne" + name = "Direct InfectedSpawn", + author = "Caibiii, 夜羽真白,东", + description = "特感刷新控制,传送落后特感", + version = "2023.09.04", + url = "https://github.com/fantasylidong/CompetitiveWithAnne", + + } // Cvars -ConVar - g_hSpawnDistanceMin, //特感最低生成距离 - g_hSpawnDistanceMax, //特感最大生成距离 - g_hTeleportSi, //是否打开特感传送 -// g_hTeleportDistance, - g_hSiLimit, //一波特感生成数量上限 - g_hSiInterval, //每波特感生成基础间隔 - g_hMaxPlayerZombies, //设置导演系统的特感数量上限 - g_hTeleportCheckTime, //几秒不被看到后可以传送 - g_hEnableSIoption, //设置生成哪几种特感 - g_hAllChargerMode, //是否为全牛模式 - g_hAutoSpawnTimeControl, //自动设置增加时间,加到基础间隔之上,这项不打开,增加时间默认为g_hSiInterval/2.打开为特感数量小于g_hSiLimit/3 + 1后再过基准时间开始刷特。 - //但是这个值大于g_hSiInterval/2也会开始强制刷特 - g_hAddDamageToSmoker, //被smoker拉的时候是否对smoker是否进行增伤 - g_hIgnoreIncappedSurvivorSight, //是否忽视掉倒地生还者视线 - g_hVsBossFlowBuffer, - g_hAllHunterMode; +ConVar g_hSpawnDistanceMin, // 特感最低生成距离 + g_hSpawnDistanceMax, // 特感最大生成距离 + g_hTeleportSi, // 是否打开特感传送 + // g_hTeleportDistance, + g_hSiLimit, // 一波特感生成数量上限 + g_hSiInterval, // 每波特感生成基础间隔 + g_hMaxPlayerZombies, // 设置导演系统的特感数量上限 + g_hTeleportCheckTime, // 几秒不被看到后可以传送 + g_hEnableSIoption, // 设置生成哪几种特感 + g_hAllChargerMode, // 是否为全牛模式 + g_hAutoSpawnTimeControl, // 自动设置增加时间,加到基础间隔之上,这项不打开,增加时间默认为g_hSiInterval/2.打开为特感数量小于g_hSiLimit/3 + 1后再过基准时间开始刷特。 + // 但是这个值大于g_hSiInterval/2也会开始强制刷特 + g_hAddDamageToSmoker, // 被smoker拉的时候是否对smoker是否进行增伤 + g_hIgnoreIncappedSurvivorSight, // 是否忽视掉倒地生还者视线 + g_hVsBossFlowBuffer, g_hAllHunterMode; // Ints -int - g_iSiLimit, //特感数量 - g_iRushManIndex, //跑男id - g_iWaveTime, //Debug时输出这是第几波刷特 - g_iLastSpawnTime, //离上次刷特过去了多久 - g_iTotalSINum = 0, //总共还活着的特感 - g_iEnableSIoption = 63, //可生成的特感种类 - g_iTeleportCheckTime = 5, //特感传送要求的不被看到的次数(1s检查一次) - g_iSINum[6] = {0}, //记录当前还存活的特感数量 - g_ArraySIlimit[6] = {0}, //记录去除队列里特感数量后还能生成的特感 - g_iTeleCount[MAXPLAYERS + 1] = {0}, //每个特感传送的不被看到次数 - g_iTargetSurvivor = -1, //OnGameFrame参数里,以该目标生成生成网络,寻找生成目标 - g_iQueueIndex = 0, //当前生成队列长度 - g_iTeleportIndex = 0, //当前传送队列长度 - g_iSpawnMaxCount = 0, //当前可生成特感数量 - g_iSurvivorNum = 0, //活着的生还者数量 - g_iSurvivors[MAXPLAYERS + 1] = {0}; //活着生还者的索引 +int + g_iSiLimit, //特感数量 + g_iRushManIndex, //跑男id + g_iWaveTime, // Debug时输出这是第几波刷特 + g_iLastSpawnTime, //离上次刷特过去了多久 + g_iTotalSINum = 0, //总共还活着的特感 + g_iEnableSIoption = 63, //可生成的特感种类 + g_iTeleportCheckTime = 5, //特感传送要求的不被看到的次数(1s检查一次) + g_iSINum[6] = { 0 }, //记录当前还存活的特感数量 + g_ArraySIlimit[6] = { 0 }, //记录去除队列里特感数量后还能生成的特感 + g_iTeleCount[MAXPLAYERS + 1] = { 0 }, //每个特感传送的不被看到次数 + g_iTargetSurvivor = -1, // OnGameFrame参数里,以该目标生成生成网络,寻找生成目标 + g_iQueueIndex = 0, //当前生成队列长度 + g_iTeleportIndex = 0, //当前传送队列长度 + g_iSpawnMaxCount = 0, //当前可生成特感数量 + g_iSurvivorNum = 0, //活着的生还者数量 + g_iSurvivors[MAXPLAYERS + 1] = { 0 }; //活着生还者的索引 // Floats -float - g_fSpitterSpitTime[MAXPLAYERS + 1], //Spitter吐口水时间 - g_fSpawnDistanceMin, //特感的最小生成距离 - g_fSpawnDistanceMax, //特感的最大生成距离 - g_fSpawnDistance, //特感的当前生成距离 -// g_fTeleportDistanceMin, //特感传送距离生还的最小距离 - g_fTeleportDistance, //特感当前传送生成距离 - g_fLastSISpawnStartTime, //上一波特感生成时间 - g_fUnpauseNextSpawnTime, //因为暂停记录下下一波特感的时间,方便解除暂停时创建处理线程 - g_fSiInterval; //特感的生成时间间隔 +float + g_fSpitterSpitTime[MAXPLAYERS + 1], // Spitter吐口水时间 + g_fSpawnDistanceMin, //特感的最小生成距离 + g_fSpawnDistanceMax, //特感的最大生成距离 + g_fSpawnDistance, //特感的当前生成距离 + // g_fTeleportDistanceMin, //特感传送距离生还的最小距离 + g_fTeleportDistance, //特感当前传送生成距离 + g_fLastSISpawnStartTime, //上一波特感生成时间 + g_fUnpauseNextSpawnTime, //因为暂停记录下下一波特感的时间,方便解除暂停时创建处理线程 + g_fSiInterval; //特感的生成时间间隔 // Bools -bool - g_bTeleportSi, //是否开启特感传送检测 - g_bPickRushMan, //是否针对跑男 - g_bShouldCheck, //是否开启时间检测 - g_bAutoSpawnTimeControl, //是否开启自动增加时间 - g_bAddDamageToSmoker, //是否对smoker增伤(一般alone模式开启) - g_bIgnoreIncappedSurvivorSight, //是否忽略倒地生还者的视线 - g_bIsLate = false, //text插件是否发送开启刷特命令 - g_bSmokerAvailable = false, //ai_smoker_new是否存在 - g_bSIPoolAvailable = false, //特感池是否存在 - g_bTargetSystemAvailable = false; //目标选择插件是否存在 +bool + g_bTeleportSi, //是否开启特感传送检测 + g_bPickRushMan, //是否针对跑男 + g_bShouldCheck, //是否开启时间检测 + g_bAutoSpawnTimeControl, //是否开启自动增加时间 + g_bAddDamageToSmoker, //是否对smoker增伤(一般alone模式开启) + g_bIgnoreIncappedSurvivorSight, //是否忽略倒地生还者的视线 + g_bIsLate = false, // text插件是否发送开启刷特命令 + g_bSmokerAvailable = false, // ai_smoker_new是否存在 + g_bSIPoolAvailable = false, //特感池是否存在 + g_bTargetSystemAvailable = false; //目标选择插件是否存在 + // Handle -Handle - g_hCheckShouldSpawnOrNot = INVALID_HANDLE, //1s检测一次是否开启刷特进程的维护进程 - g_hSpawnProcess = INVALID_HANDLE, //刷特 handle - g_hTeleHandle = INVALID_HANDLE, //传送sdk Handle - g_hRushManNotifyForward = INVALID_HANDLE; //检测到跑男提醒Target_limit插件放开单人目标限制 +Handle + g_hCheckShouldSpawnOrNot = INVALID_HANDLE, // 1s检测一次是否开启刷特进程的维护进程 + g_hSpawnProcess = INVALID_HANDLE, //刷特 handle + g_hTeleHandle = INVALID_HANDLE, //传送sdk Handle + g_hRushManNotifyForward = INVALID_HANDLE; //检测到跑男提醒Target_limit插件放开单人目标限制 + // ArrayList -ArrayList - aTeleportQueue, //传送队列 - //aSpawnNavList, //储存特感生成的navid,用来限制特感不能生成在同一块Navid上 - aSpawnQueue; //刷特队列 +ArrayList + aTeleportQueue, //传送队列 + // aSpawnNavList, //储存特感生成的navid,用来限制特感不能生成在同一块Navid上 + aSpawnQueue; //刷特队列 -SIPool - g_hSIPool; +SIPool + g_hSIPool; public APLRes AskPluginLoad2(Handle plugin, bool late, char[] error, int err_max) { - RegPluginLibrary("infected_control"); - g_hRushManNotifyForward = CreateGlobalForward("OnDetectRushman", ET_Ignore, Param_Cell); - CreateNative("GetNextSpawnTime", Native_GetNextSpawnTime); - return APLRes_Success; + RegPluginLibrary("infected_control"); + g_hRushManNotifyForward = CreateGlobalForward("OnDetectRushman", ET_Ignore, Param_Cell); + CreateNative("GetNextSpawnTime", Native_GetNextSpawnTime); + return APLRes_Success; } - -public any Native_GetNextSpawnTime(Handle plugin, int numParams) +any Native_GetNextSpawnTime(Handle plugin, int numParams) { - float time = 0.0; - //如果刷特进程还不开始,直接返回刷特间隔 - if (g_hSpawnProcess == null) - { - time = g_fSiInterval; - } - else - { - time = g_fSiInterval - (GetGameTime() - g_fLastSISpawnStartTime); - } - Debug_Print("下一波特感生成时间是%.2f秒后", time); - return time; + float time = 0.0; + //如果刷特进程还不开始,直接返回刷特间隔 + if (g_hSpawnProcess == null) + time = g_fSiInterval; + else time = g_fSiInterval - (GetGameTime() - g_fLastSISpawnStartTime); + + Debug_Print("下一波特感生成时间是%.2f秒后", time); + return time; } -public void OnAllPluginsLoaded(){ - g_bTargetSystemAvailable = LibraryExists("si_target_limit"); - g_bSmokerAvailable = LibraryExists("ai_smoker_new"); - g_bSIPoolAvailable = LibraryExists("si_pool"); +public void OnAllPluginsLoaded() +{ + g_bTargetSystemAvailable = LibraryExists("si_target_limit"); + g_bSmokerAvailable = LibraryExists("ai_smoker_new"); + g_bSIPoolAvailable = LibraryExists("si_pool"); } + public void OnLibraryAdded(const char[] name) { - if ( StrEqual(name, "si_target_limit") ) { g_bTargetSystemAvailable = true; } - else if( StrEqual(name, "ai_smoker_new") ) { g_bSmokerAvailable = true; } - else if( StrEqual(name, "si_pool") ) { g_bSIPoolAvailable = true; } + if (StrEqual(name, "si_target_limit")) + g_bTargetSystemAvailable = true; + else if (StrEqual(name, "ai_smoker_new")) + g_bSmokerAvailable = true; + else if (StrEqual(name, "si_pool")) + g_bSIPoolAvailable = true; } + public void OnLibraryRemoved(const char[] name) { - if ( StrEqual(name, "si_target_limit") ) { g_bTargetSystemAvailable = false; } - else if( StrEqual(name, "ai_smoker_new") ) { g_bSmokerAvailable = false; } - else if( StrEqual(name, "si_pool") ) { g_bSIPoolAvailable = false; } + if (StrEqual(name, "si_target_limit")) + g_bTargetSystemAvailable = false; + else if (StrEqual(name, "ai_smoker_new")) + g_bSmokerAvailable = false; + else if (StrEqual(name, "si_pool")) + g_bSIPoolAvailable = false; } + public void OnPluginStart() { - // CreateConVar - g_hSpawnDistanceMin = CreateConVar("inf_SpawnDistanceMin", "250.0", "特感复活离生还者最近的距离限制", CVAR_FLAG, true, 0.0); - g_hSpawnDistanceMax = CreateConVar("inf_SpawnDistanceMax", "1500.0", "特感复活离生还者最远的距离限制", CVAR_FLAG, true, g_hSpawnDistanceMin.FloatValue); - g_hTeleportSi = CreateConVar("inf_TeleportSi", "1", "是否开启特感距离生还者一定距离将其传送至生还者周围", CVAR_FLAG, true, 0.0, true, 1.0); - g_hTeleportCheckTime = CreateConVar("inf_TeleportCheckTime", "5", "特感几秒后没被看到开始传送", CVAR_FLAG, true, 0.0); - g_hEnableSIoption = CreateConVar("inf_EnableSIoption", "63", "启用生成的特感类型,1 smoker 2 boomer 4 hunter 8 spitter 16 jockey 32 charger,把你想要生成的特感值加起来", CVAR_FLAG, true, 0.0, true, 63.0); - g_hAllChargerMode = CreateConVar("inf_AllChargerMode", "0", "是否是全牛模式", CVAR_FLAG, true, 0.0, true, 1.0); - g_hAllHunterMode = CreateConVar("inf_AllHunterMode", "0", "是否是全猎人模式", CVAR_FLAG, true, 0.0, true, 1.0); - g_hAutoSpawnTimeControl = CreateConVar("inf_EnableAutoSpawnTime", "1", "是否开启自动设置增加时间", CVAR_FLAG, true, 0.0, true, 1.0); - g_hIgnoreIncappedSurvivorSight = CreateConVar("inf_IgnoreIncappedSurvivorSight", "1", "特感传送检测是否被看到的时候是否忽略倒地生还者视线", CVAR_FLAG, true, 0.0, true, 1.0); - g_hAddDamageToSmoker= CreateConVar("inf_AddDamageToSmoker", "0", "单人模式smoker拉人时是否5倍伤害", CVAR_FLAG, true, 0.0, true, 1.0); - //传送会根据这个数值画一个以选定生还者为核心,两边各长inf_TeleportDistance单位距离,高inf_TeleportDistance距离的长方形区域内找复活位置,PS传送最好近一点 - //g_hTeleportDistance = CreateConVar("inf_TeleportDistance", "600.0", "特感传送区域的最小复活大小", CVAR_FLAG, true, g_hSpawnDistanceMin.FloatValue); - g_hSiLimit = CreateConVar("l4d_infected_limit", "6", "一次刷出多少特感", CVAR_FLAG, true, 0.0); - g_hSiInterval = CreateConVar("versus_special_respawn_interval", "16.0", "对抗模式下刷特时间控制", CVAR_FLAG, true, 0.0); - g_hMaxPlayerZombies = FindConVar("z_max_player_zombies"); - g_hVsBossFlowBuffer = FindConVar("versus_boss_buffer"); - SetConVarInt(FindConVar("director_no_specials"), 1); - // HookEvents - HookEvent("player_death", evt_PlayerDeath, EventHookMode_PostNoCopy); - HookEvent("round_start", evt_RoundStart, EventHookMode_PostNoCopy); - HookEvent("finale_win", evt_RoundEnd, EventHookMode_PostNoCopy); - HookEvent("map_transition", evt_RoundEnd, EventHookMode_PostNoCopy); - HookEvent("mission_lost", evt_RoundEnd, EventHookMode_PostNoCopy); - HookEvent("player_hurt", evt_PlayerHurt, EventHookMode_PostNoCopy); - HookEvent("ability_use", evt_GetSpitTime, EventHookMode_PostNoCopy); - HookEvent("player_spawn", evt_PlayerSpawn, EventHookMode_PostNoCopy); - // AddChangeHook - g_hSpawnDistanceMax.AddChangeHook(ConVarChanged_Cvars); - g_hSpawnDistanceMin.AddChangeHook(ConVarChanged_Cvars); - g_hTeleportSi.AddChangeHook(ConVarChanged_Cvars); - g_hTeleportCheckTime.AddChangeHook(ConVarChanged_Cvars); - //g_hTeleportDistance.AddChangeHook(ConVarChanged_Cvars); - g_hSiInterval.AddChangeHook(ConVarChanged_Cvars); - g_hIgnoreIncappedSurvivorSight.AddChangeHook(ConVarChanged_Cvars); - g_hEnableSIoption.AddChangeHook(ConVarChanged_Cvars); - g_hAllChargerMode.AddChangeHook(ConVarChanged_Cvars); - g_hAllHunterMode.AddChangeHook(ConVarChanged_Cvars); - g_hAutoSpawnTimeControl.AddChangeHook(ConVarChanged_Cvars); - g_hAddDamageToSmoker.AddChangeHook(ConVarChanged_Cvars); - g_hSiLimit.AddChangeHook(MaxPlayerZombiesChanged_Cvars); - - // ArrayList - aSpawnQueue = new ArrayList(); - aTeleportQueue = new ArrayList(); - //aSpawnNavList = new ArrayList(); - // GetCvars - GetCvars(); - GetSiLimit(); - // SetConVarBonus - SetConVarBounds(g_hMaxPlayerZombies, ConVarBound_Upper, true, g_hSiLimit.FloatValue); - // Debug - RegAdminCmd("sm_startspawn", Cmd_StartSpawn, ADMFLAG_ROOT, "管理员重置刷特时钟"); - RegAdminCmd("sm_stopspawn", Cmd_StopSpawn, ADMFLAG_ROOT, "管理员重置刷特时钟"); + // CreateConVar + g_hSpawnDistanceMin = CreateConVar("inf_SpawnDistanceMin", "250.0", "特感复活离生还者最近的距离限制", CVAR_FLAG, true, 0.0); + g_hSpawnDistanceMax = CreateConVar("inf_SpawnDistanceMax", "1500.0", "特感复活离生还者最远的距离限制", CVAR_FLAG, true, g_hSpawnDistanceMin.FloatValue); + g_hTeleportSi = CreateConVar("inf_TeleportSi", "1", "是否开启特感距离生还者一定距离将其传送至生还者周围", CVAR_FLAG, true, 0.0, true, 1.0); + g_hTeleportCheckTime = CreateConVar("inf_TeleportCheckTime", "5", "特感几秒后没被看到开始传送", CVAR_FLAG, true, 0.0); + g_hEnableSIoption = CreateConVar("inf_EnableSIoption", "63", "启用生成的特感类型,1 smoker 2 boomer 4 hunter 8 spitter 16 jockey 32 charger,把你想要生成的特感值加起来", CVAR_FLAG, true, 0.0, true, 63.0); + g_hAllChargerMode = CreateConVar("inf_AllChargerMode", "0", "是否是全牛模式", CVAR_FLAG, true, 0.0, true, 1.0); + g_hAllHunterMode = CreateConVar("inf_AllHunterMode", "0", "是否是全猎人模式", CVAR_FLAG, true, 0.0, true, 1.0); + g_hAutoSpawnTimeControl = CreateConVar("inf_EnableAutoSpawnTime", "1", "是否开启自动设置增加时间", CVAR_FLAG, true, 0.0, true, 1.0); + g_hIgnoreIncappedSurvivorSight = CreateConVar("inf_IgnoreIncappedSurvivorSight", "1", "特感传送检测是否被看到的时候是否忽略倒地生还者视线", CVAR_FLAG, true, 0.0, true, 1.0); + g_hAddDamageToSmoker = CreateConVar("inf_AddDamageToSmoker", "0", "单人模式smoker拉人时是否5倍伤害", CVAR_FLAG, true, 0.0, true, 1.0); + // 传送会根据这个数值画一个以选定生还者为核心,两边各长inf_TeleportDistance单位距离,高inf_TeleportDistance距离的长方形区域内找复活位置,PS传送最好近一点 + // g_hTeleportDistance = CreateConVar("inf_TeleportDistance", "600.0", "特感传送区域的最小复活大小", CVAR_FLAG, true, g_hSpawnDistanceMin.FloatValue); + g_hSiLimit = CreateConVar("l4d_infected_limit", "6", "一次刷出多少特感", CVAR_FLAG, true, 0.0); + g_hSiInterval = CreateConVar("versus_special_respawn_interval", "16.0", "对抗模式下刷特时间控制", CVAR_FLAG, true, 0.0); + g_hMaxPlayerZombies = FindConVar("z_max_player_zombies"); + g_hVsBossFlowBuffer = FindConVar("versus_boss_buffer"); + SetConVarInt(FindConVar("director_no_specials"), 1); + + // HookEvents + // PostNoCopy是绝对不正确的,NoCopy 会导致 event 丢弃所有的数据("Use 'PostNoCopy' if your action is Post and ONLY requires the event name.") + // 丢弃的数据包括 userid,attackerid,weaponid 等等,对于求生来说这几个字节的内存没必要省略 + // 详见:https://wiki.alliedmods.net/Events_(SourceMod_Scripting) + HookEvent("finale_win", evt_RoundEnd); + HookEvent("mission_lost", evt_RoundEnd); + HookEvent("player_hurt", evt_PlayerHurt); + HookEvent("ability_use", evt_GetSpitTime); + HookEvent("round_start", evt_RoundStart); + HookEvent("map_transition", evt_RoundEnd); + HookEvent("player_death", evt_PlayerDeath); + HookEvent("player_spawn", evt_PlayerSpawn); + + // AddChangeHook + g_hSpawnDistanceMax.AddChangeHook(ConVarChanged_Cvars); + g_hSpawnDistanceMin.AddChangeHook(ConVarChanged_Cvars); + g_hTeleportSi.AddChangeHook(ConVarChanged_Cvars); + g_hTeleportCheckTime.AddChangeHook(ConVarChanged_Cvars); + // g_hTeleportDistance.AddChangeHook(ConVarChanged_Cvars); + g_hSiInterval.AddChangeHook(ConVarChanged_Cvars); + g_hIgnoreIncappedSurvivorSight.AddChangeHook(ConVarChanged_Cvars); + g_hEnableSIoption.AddChangeHook(ConVarChanged_Cvars); + g_hAllChargerMode.AddChangeHook(ConVarChanged_Cvars); + g_hAllHunterMode.AddChangeHook(ConVarChanged_Cvars); + g_hAutoSpawnTimeControl.AddChangeHook(ConVarChanged_Cvars); + g_hAddDamageToSmoker.AddChangeHook(ConVarChanged_Cvars); + g_hSiLimit.AddChangeHook(MaxPlayerZombiesChanged_Cvars); + + // ArrayList + aSpawnQueue = new ArrayList(); + aTeleportQueue = new ArrayList(); + // aSpawnNavList = new ArrayList(); + + // GetCvars + GetCvars(); + GetSiLimit(); + + // SetConVarBonus + SetConVarBounds(g_hMaxPlayerZombies, ConVarBound_Upper, true, g_hSiLimit.FloatValue); + + // Debug + RegAdminCmd("sm_startspawn", Cmd_StartSpawn, ADMFLAG_ROOT, "管理员重置刷特时钟"); + RegAdminCmd("sm_stopspawn", Cmd_StopSpawn, ADMFLAG_ROOT, "管理员重置刷特时钟"); } public void OnMapStart() @@ -246,26 +260,30 @@ public void OnMapStart() if (g_bSIPoolAvailable && !g_hSIPool) g_hSIPool = SIPool.Instance(); } -public void OnPluginEnd() { - if(g_hAllChargerMode.BoolValue){ - FindConVar("z_charger_health").RestoreDefault(); - FindConVar("z_charge_max_speed").RestoreDefault(); - FindConVar("z_charge_start_speed").RestoreDefault(); - FindConVar("z_charger_pound_dmg").RestoreDefault(); - FindConVar("z_charge_max_damage").RestoreDefault(); - FindConVar("z_charge_interval").RestoreDefault(); - } +public void OnPluginEnd() +{ + if (g_hAllChargerMode.BoolValue) + { + FindConVar("z_charger_health").RestoreDefault(); + FindConVar("z_charge_max_speed").RestoreDefault(); + FindConVar("z_charge_start_speed").RestoreDefault(); + FindConVar("z_charger_pound_dmg").RestoreDefault(); + FindConVar("z_charge_max_damage").RestoreDefault(); + FindConVar("z_charge_interval").RestoreDefault(); + } } -void TweakSettings() { - if(g_hAllChargerMode.BoolValue){ - FindConVar("z_charger_health").SetFloat(500.0); - FindConVar("z_charge_max_speed").SetFloat(750.0); - FindConVar("z_charge_start_speed").SetFloat(350.0); - FindConVar("z_charger_pound_dmg").SetFloat(10.0); - FindConVar("z_charge_max_damage").SetFloat(6.0); - FindConVar("z_charge_interval").SetFloat(2.0); - } +void TweakSettings() +{ + if (g_hAllChargerMode.BoolValue) + { + FindConVar("z_charger_health").SetFloat(500.0); + FindConVar("z_charge_max_speed").SetFloat(750.0); + FindConVar("z_charge_start_speed").SetFloat(350.0); + FindConVar("z_charger_pound_dmg").SetFloat(10.0); + FindConVar("z_charge_max_damage").SetFloat(6.0); + FindConVar("z_charge_interval").SetFloat(2.0); + } } // 向量绘制 @@ -273,20 +291,20 @@ void TweakSettings() { stock Action Cmd_StartSpawn(int client, int args) { - if (L4D_HasAnySurvivorLeftSafeArea()) - { - ResetStatus(); - CreateTimer(0.1, SpawnFirstInfected); - GetSiLimit(); - TweakSettings(); - } - return Plugin_Handled; + if (L4D_HasAnySurvivorLeftSafeArea()) + { + ResetStatus(); + CreateTimer(0.1, SpawnFirstInfected); + GetSiLimit(); + TweakSettings(); + } + return Plugin_Handled; } stock Action Cmd_StopSpawn(int client, int args) { - StopSpawn(); - return Plugin_Handled; + StopSpawn(); + return Plugin_Handled; } // ********************* @@ -294,198 +312,181 @@ stock Action Cmd_StopSpawn(int client, int args) // ********************* void ConVarChanged_Cvars(ConVar convar, const char[] oldValue, const char[] newValue) { - GetCvars(); + GetCvars(); } void MaxPlayerZombiesChanged_Cvars(ConVar convar, const char[] oldValue, const char[] newValue) { - g_iSiLimit = g_hSiLimit.IntValue; - CreateTimer(0.1, MaxSpecialsSet); + g_iSiLimit = g_hSiLimit.IntValue; + CreateTimer(0.1, MaxSpecialsSet); } void GetCvars() { - g_fSpawnDistanceMax = g_hSpawnDistanceMax.FloatValue; - g_fSpawnDistanceMin = g_hSpawnDistanceMin.FloatValue; - g_bTeleportSi = g_hTeleportSi.BoolValue; - //g_fTeleportDistanceMin = g_hTeleportDistance.FloatValue; - g_fSiInterval = g_hSiInterval.FloatValue; - g_iSiLimit = g_hSiLimit.IntValue; - g_iTeleportCheckTime = g_hTeleportCheckTime.IntValue; - g_iEnableSIoption = g_hEnableSIoption.IntValue; - g_bAddDamageToSmoker = g_hAddDamageToSmoker.BoolValue; - g_bAutoSpawnTimeControl = g_hAutoSpawnTimeControl.BoolValue; - g_bIgnoreIncappedSurvivorSight = g_hIgnoreIncappedSurvivorSight.BoolValue; - if(g_hAllChargerMode.BoolValue){ - TweakSettings(); - } + g_fSpawnDistanceMax = g_hSpawnDistanceMax.FloatValue; + g_fSpawnDistanceMin = g_hSpawnDistanceMin.FloatValue; + g_bTeleportSi = g_hTeleportSi.BoolValue; + // g_fTeleportDistanceMin = g_hTeleportDistance.FloatValue; + g_fSiInterval = g_hSiInterval.FloatValue; + g_iSiLimit = g_hSiLimit.IntValue; + g_iTeleportCheckTime = g_hTeleportCheckTime.IntValue; + g_iEnableSIoption = g_hEnableSIoption.IntValue; + g_bAddDamageToSmoker = g_hAddDamageToSmoker.BoolValue; + g_bAutoSpawnTimeControl = g_hAutoSpawnTimeControl.BoolValue; + g_bIgnoreIncappedSurvivorSight = g_hIgnoreIncappedSurvivorSight.BoolValue; + if (g_hAllChargerMode.BoolValue) + TweakSettings(); } public Action MaxSpecialsSet(Handle timer) { - SetConVarBounds(g_hMaxPlayerZombies, ConVarBound_Upper, true, g_hSiLimit.FloatValue); - g_hMaxPlayerZombies.IntValue = g_iSiLimit; - return Plugin_Continue; + SetConVarBounds(g_hMaxPlayerZombies, ConVarBound_Upper, true, g_hSiLimit.FloatValue); + g_hMaxPlayerZombies.IntValue = g_iSiLimit; + return Plugin_Continue; } // ********************* // 事件 // ********************* -//Spitter出生重置能力 -public void evt_PlayerSpawn(Event event, const char[] name, bool dont_broadcast) +// Spitter出生重置能力 +void evt_PlayerSpawn(Event event, const char[] name, bool dont_broadcast) { - int client = GetClientOfUserId(event.GetInt("userid")); - if(IsSpitter(client)) - { - g_fSpitterSpitTime[client] = GetGameTime(); - } - if(IsAiTank(client)) - { - Debug_Print("系统生成一只tank,特感总数量 %d, 真实特感数量:%d", g_iTotalSINum, GetCurrentSINum()); - } + int client = GetClientOfUserId(event.GetInt("userid")); + if (IsSpitter(client)) + g_fSpitterSpitTime[client] = GetGameTime(); + else if (IsAiTank(client)) + Debug_Print("系统生成一只tank,特感总数量 %d, 真实特感数量:%d", g_iTotalSINum, GetCurrentSINum()); } //获取spitter口水时间 -public void evt_GetSpitTime(Event event, const char[] name, bool dont_broadcast) +void evt_GetSpitTime(Event event, const char[] name, bool dont_broadcast) { - int client = GetClientOfUserId(event.GetInt("userid")); - if (!client || !IsClientInGame(client) || !IsFakeClient(client)) - return; + int client = GetClientOfUserId(event.GetInt("userid")); + if (!client || !IsClientInGame(client) || !IsFakeClient(client)) + return; - static char ability[16]; - event.GetString("ability", ability, sizeof ability); - if (strcmp(ability, "ability_spit") == 0) - { - g_fSpitterSpitTime[client] = GetGameTime(); - } + static char ability[16]; + event.GetString("ability", ability, sizeof ability); + if (strcmp(ability, "ability_spit") == 0) + g_fSpitterSpitTime[client] = GetGameTime(); } /* 玩家受伤,增加对smoker得伤害 */ -public void evt_PlayerHurt(Event event, const char[] name, bool dont_broadcast) -{ - if(g_bAddDamageToSmoker){ - int victim = GetClientOfUserId(GetEventInt(event, "userid")); - int attacker = GetClientOfUserId(GetEventInt(event, "attacker")); - int damage = GetEventInt(event, "dmg_health"); - int eventhealth = GetEventInt(event, "health"); - int AddDamage = 0; - if (IsValidSurvivor(attacker) && IsInfectedBot(victim) && GetEntProp(victim, Prop_Send, "m_zombieClass") == 1) - { - if( GetEntPropEnt(victim, Prop_Send, "m_tongueVictim") > 0 ) - { - AddDamage = damage * 5; - } - int health = eventhealth - AddDamage; - if (health < 1) - { - health = 0; - } - SetEntityHealth(victim, health); - SetEventInt(event, "health", health); - } - } -} - -public void InitStatus(){ - if (g_hTeleHandle != INVALID_HANDLE) - { - delete g_hTeleHandle; - g_hTeleHandle = INVALID_HANDLE; - } - if (g_hCheckShouldSpawnOrNot != INVALID_HANDLE) - { - delete g_hCheckShouldSpawnOrNot; - g_hCheckShouldSpawnOrNot = INVALID_HANDLE; - } - if (g_hSpawnProcess != INVALID_HANDLE) - { - KillTimer(g_hSpawnProcess); - Debug_Print("刷特进程终止"); - g_hSpawnProcess = INVALID_HANDLE; - } - - g_bPickRushMan = false; - g_bShouldCheck = false; - g_bIsLate = false; - g_iSpawnMaxCount = 0; - g_fLastSISpawnStartTime = 0.0; - g_fUnpauseNextSpawnTime = 0.0; - aSpawnQueue.Clear(); - aTeleportQueue.Clear(); - //aSpawnNavList.Clear(); - g_iQueueIndex = 0; - g_iTeleportIndex = 0; - g_iWaveTime=0; - for(int i = 0; i <= MAXPLAYERS; i++) - { - g_fSpitterSpitTime[i] = 0.0; - } - for(int i = 0; i < 6; i++){ - g_iSINum[i] =0; - } -} - -public void StopSpawn(){ - InitStatus(); -} - -public void evt_RoundStart(Event event, const char[] name, bool dontBroadcast) -{ - InitStatus(); - CreateTimer(0.1, MaxSpecialsSet); - CreateTimer(1.0, SafeRoomReset, _, TIMER_FLAG_NO_MAPCHANGE); -} - -public void evt_RoundEnd(Event event, const char[] name, bool dontBroadcast) -{ - InitStatus(); -} - -public void evt_PlayerDeath(Event event, const char[] name, bool dontBroadcast) -{ - int client = GetClientOfUserId(event.GetInt("userid")); - if (IsInfectedBot(client)) - { - int type = GetEntProp(client, Prop_Send, "m_zombieClass"); - //防止无声口水 - if (type != ZC_SPITTER) - { - CreateTimer(0.5, Timer_KickBot, client); - } - if(type >= 1 && type <=6){ - if(g_iSINum[type - 1] > 0) - { - g_iSINum[type - 1] --; - } - else - { - g_iSINum[type - 1] = 0; - } - if(g_iTotalSINum > 0) - { - g_iTotalSINum --; - } - else - { - g_iTotalSINum = 0; - } - Debug_Print("杀死%N,特感总数和该种类特感数量减1分别为%d %d", client, g_iTotalSINum, g_iSINum[type - 1]); - } - g_iTeleCount[client] = 0; - } -} - -public Action Timer_KickBot(Handle timer, int client) -{ - if (IsClientInGame(client) && !IsClientInKickQueue(client) && IsFakeClient(client)) - { - Debug_Print("踢出特感%N",client); - if(g_bSIPoolAvailable) - g_hSIPool.ReturnSIBot(client); - else - KickClient(client, "You are worthless and was kicked by console"); - } - return Plugin_Continue; +void evt_PlayerHurt(Event event, const char[] name, bool dont_broadcast) +{ + if (!g_bAddDamageToSmoker) return; + + int victim = GetClientOfUserId(GetEventInt(event, "userid")); + int attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + int damage = GetEventInt(event, "dmg_health"); + int eventhealth = GetEventInt(event, "health"); + int AddDamage = 0; + if (IsValidSurvivor(attacker) && IsInfectedBot(victim) && GetEntProp(victim, Prop_Send, "m_zombieClass") == 1) + { + if (GetEntPropEnt(victim, Prop_Send, "m_tongueVictim") > 0) + AddDamage = damage * 5; + + int health = eventhealth - AddDamage; + if (health < 1) health = 0; + + SetEntityHealth(victim, health); + SetEventInt(event, "health", health); + } +} + +void InitStatus() +{ + if (g_hTeleHandle != INVALID_HANDLE) + { + delete g_hTeleHandle; + g_hTeleHandle = INVALID_HANDLE; + //这里其实可以不用赋值,delete 后变量会被分配为 null,可以使用 if(g_hTeleHandle != null) 进行判断 + } + + if (g_hCheckShouldSpawnOrNot != INVALID_HANDLE) + { + delete g_hCheckShouldSpawnOrNot; + g_hCheckShouldSpawnOrNot = INVALID_HANDLE; + } + + if (g_hSpawnProcess != INVALID_HANDLE) + { + KillTimer(g_hSpawnProcess); + Debug_Print("刷特进程终止"); + g_hSpawnProcess = INVALID_HANDLE; + } + + g_bPickRushMan = false; + g_bShouldCheck = false; + g_bIsLate = false; + g_iSpawnMaxCount = 0; + g_fLastSISpawnStartTime = 0.0; + g_fUnpauseNextSpawnTime = 0.0; + aSpawnQueue.Clear(); + aTeleportQueue.Clear(); + // aSpawnNavList.Clear(); + g_iQueueIndex = 0; + g_iTeleportIndex = 0; + g_iWaveTime = 0; + for (int i = 0; i <= MAXPLAYERS; i++) + g_fSpitterSpitTime[i] = 0.0; + + for (int i = 0; i < 6; i++) + g_iSINum[i] = 0; +} + +void StopSpawn() +{ + InitStatus(); +} + +void evt_RoundStart(Event event, const char[] name, bool dontBroadcast) +{ + InitStatus(); + CreateTimer(0.1, MaxSpecialsSet); + CreateTimer(1.0, SafeRoomReset, _, TIMER_FLAG_NO_MAPCHANGE); +} + +void evt_RoundEnd(Event event, const char[] name, bool dontBroadcast) +{ + InitStatus(); +} + +void evt_PlayerDeath(Event event, const char[] name, bool dontBroadcast) +{ + int client = GetClientOfUserId(event.GetInt("userid")); + if (!IsInfectedBot(client)) return; + + int type = GetEntProp(client, Prop_Send, "m_zombieClass"); + //防止无声口水 + if (type != ZC_SPITTER && !g_bSIPoolAvailable) // 使用SIPool后不会出现无声口水 + CreateTimer(0.5, Timer_KickBot, client); + + if (type >= 1 && type <= 6) + { + if (g_iSINum[type - 1] > 0) g_iSINum[type - 1]--; + else g_iSINum[type - 1] = 0; + + if (g_iTotalSINum > 0) g_iTotalSINum--; + else g_iTotalSINum = 0; + + Debug_Print("杀死%N,特感总数和该种类特感数量减1分别为%d %d", client, g_iTotalSINum, g_iSINum[type - 1]); + } + g_iTeleCount[client] = 0; +} + +Action Timer_KickBot(Handle timer, int client) +{ + if (IsClientInGame(client) && !IsClientInKickQueue(client) && IsFakeClient(client)) + { + Debug_Print("踢出特感%N", client); + if (g_bSIPoolAvailable) + g_hSIPool.ReturnSIBot(client); + else KickClient(client, "You are worthless and was kicked by console"); + + return Plugin_Stop; + } + return Plugin_Continue; } // ********************* @@ -493,269 +494,256 @@ public Action Timer_KickBot(Handle timer, int client) // ********************* public void OnGameFrame() { - // 根据情况动态调整 z_maxplayers_zombie 数值 - if (g_iSiLimit > g_hMaxPlayerZombies.IntValue) - { - CreateTimer(0.1, MaxSpecialsSet); - } - if (g_iTeleportIndex <= 0 && g_iQueueIndex < g_iSiLimit) - { - int zombieclass = 0; - if(g_hAllChargerMode.BoolValue){ - zombieclass = 6; - } - else if(g_hAllHunterMode.BoolValue){ - zombieclass = 3; - }else{ - zombieclass = GetRandomInt(1,6); - } - if (zombieclass != 0 && MeetRequire(zombieclass) && !HasReachedLimit(zombieclass) && g_iQueueIndex < g_iSiLimit) - { - //这里增加一些boomer和spitter生成的判定,让bommer和spitter比较晚生成 - aSpawnQueue.Push(g_iQueueIndex); - aSpawnQueue.Set(g_iQueueIndex, zombieclass, 0, false); - g_ArraySIlimit[zombieclass - 1] -= 1; - g_iQueueIndex += 1; - //Debug_Print("<刷特队列> 当前入队特感:%s,当前队列长度:%d,当前队列索引位置:%d", InfectedName[zombieclass], aSpawnQueue.Length, g_iQueueIndex); - } - } - if (g_bIsLate) - { - /* - // 当nav存储长度超过特感生成上限时,删去第一个 - if (aSpawnNavList.Length > g_iSiLimit) - { - //Debug_Print(" 当前队列长度:%d, 超过特感上限,清除队列第一个元素", aSpawnNavList.Length); - aSpawnNavList.Erase(0); - } - */ - if (g_iTotalSINum < g_iSiLimit) - { - if(g_iTeleportIndex > 0) - { - g_iTargetSurvivor = GetTargetSurvivor(); - if(g_fTeleportDistance < g_fSpawnDistanceMax) - { - g_fTeleportDistance += 20.0; - } - float fSpawnPos[3] = {0.0}; - if(GetSpawnPos(fSpawnPos, g_iTargetSurvivor, g_fTeleportDistance, true)) - { - int iZombieClass = aTeleportQueue.Get(0); - if(!(iZombieClass >= 1 && iZombieClass <= 6)) - { - Debug_Print("特感类型读取错误,读取的特感类型为:%d", iZombieClass); - aTeleportQueue.Erase(0); - g_iTeleportIndex -= 1; - return; - } - if(SpawnInfected(fSpawnPos, g_fTeleportDistance, iZombieClass, true)){ - g_iSINum[iZombieClass - 1] += 1; - g_iTotalSINum += 1; - if (aTeleportQueue.Length > 0 && g_iTeleportIndex > 0) - { - aTeleportQueue.Erase(0); - g_iTeleportIndex -= 1; - } - print_type(iZombieClass, g_fSpawnDistance, true); - } - else - { - if (g_iTeleportIndex <= 0) - { - aTeleportQueue.Clear(); - g_iTeleportIndex = 0; - } - } - } - } - //传送队列优先处理,防止普通刷特刷出来把特感数量刷满了 - if(g_iSpawnMaxCount > 0 && g_iTeleportIndex <= 0 && g_iQueueIndex > 0) - { - g_iTargetSurvivor = GetTargetSurvivor(); - if(g_fSpawnDistance < g_fSpawnDistanceMax) - { - g_fSpawnDistance += 5.0; - } - float fSpawnPos[3] = {0.0}; - if(GetSpawnPos(fSpawnPos, g_iTargetSurvivor, g_fSpawnDistance)) { - int iZombieClass = aSpawnQueue.Get(0); - if(SpawnInfected(fSpawnPos, g_fSpawnDistance, iZombieClass)){ - g_iSpawnMaxCount -= 1; - g_iSINum[iZombieClass - 1] += 1; - g_iTotalSINum += 1; - if (aSpawnQueue.Length > 0 && g_iQueueIndex > 0) - { - aSpawnQueue.Erase(0); - g_iQueueIndex -= 1; - //刷出来之后要求特感激进进攻 - BypassAndExecuteCommand("nb_assault"); - } - print_type(iZombieClass, g_fSpawnDistance); - } - else - { - if (HasReachedLimit(iZombieClass)) - { - ReachedLimit(iZombieClass); - } - if (g_iQueueIndex <= 0) - { - aSpawnQueue.Clear(); - g_iQueueIndex = 0; - } - } - } - } - } - } -} - -stock bool GetSpawnPos(float fSpawnPos[3], int TargetSurvivor, float SpawnDistance, bool IsTeleport = false){ - if(IsValidClient(TargetSurvivor)){ - float fSurvivorPos[3] = {0.0}, fDirection[3] = {0.0}, fEndPos[3] = {0.0}, fMins[3] = {0.0}, fMaxs[3] = {0.0}; - // 根据指定生还者坐标,拓展刷新范围 - GetClientEyePosition(TargetSurvivor, fSurvivorPos); - //增加高度,增加刷房顶的几率 - if(SpawnDistance < 500.0) - { - fMaxs[2] = fSurvivorPos[2] + 800.0; - } - else - { - fMaxs[2] = fSurvivorPos[2] + SpawnDistance + 300.0; - } - fMins[0] = fSurvivorPos[0] - SpawnDistance; - fMaxs[0] = fSurvivorPos[0] + SpawnDistance; - fMins[1] = fSurvivorPos[1] - SpawnDistance; - fMaxs[1] = fSurvivorPos[1] + SpawnDistance; - fMaxs[2] = fSurvivorPos[2] + SpawnDistance; - // 规定射线方向 - fDirection[0] = 90.0; - fDirection[1] = fDirection[2] = 0.0; - // 随机刷新位置 - fSpawnPos[0] = GetRandomFloat(fMins[0], fMaxs[0]); - fSpawnPos[1] = GetRandomFloat(fMins[1], fMaxs[1]); - fSpawnPos[2] = GetRandomFloat(fSurvivorPos[2], fMaxs[2]); - // 找位条件,可视,是否在有效 NavMesh,是否卡住,否则先会判断是否在有效 Mesh 与是否卡住导致某些位置刷不出特感 - int count2=0; - //生成的时候只能在有跑男情况下才特意生成到幸存者前方 - while (PlayerVisibleToSDK(fSpawnPos, IsTeleport) || !IsOnValidMesh(fSpawnPos) || IsPlayerStuck(fSpawnPos) || ((g_bPickRushMan || IsTeleport) && !Is_Pos_Ahead(fSpawnPos, g_iTargetSurvivor))) - { - count2++; - if(count2 > 20) - { - return false; - } - fSpawnPos[0] = GetRandomFloat(fMins[0], fMaxs[0]); - fSpawnPos[1] = GetRandomFloat(fMins[1], fMaxs[1]); - fSpawnPos[2] = GetRandomFloat(fSurvivorPos[2], fMaxs[2]); - TR_TraceRay(fSpawnPos, fDirection, MASK_PLAYERSOLID, RayType_Infinite); - if(TR_DidHit()) - { - TR_GetEndPosition(fEndPos); - fSpawnPos = fEndPos; - fSpawnPos[2] += NAV_MESH_HEIGHT; - } - } - return true; - } - return false; + // 根据情况动态调整 z_maxplayers_zombie 数值 + if (g_iSiLimit > g_hMaxPlayerZombies.IntValue) + CreateTimer(0.1, MaxSpecialsSet); + + if (g_iTeleportIndex <= 0 && g_iQueueIndex < g_iSiLimit) + { + int zombieclass = 0; + if (g_hAllChargerMode.BoolValue) + zombieclass = 6; + else if (g_hAllHunterMode.BoolValue) + zombieclass = 3; + else zombieclass = GetRandomInt(1, 6); + + if (zombieclass != 0 && MeetRequire(zombieclass) && !HasReachedLimit(zombieclass) && g_iQueueIndex < g_iSiLimit) + { + //这里增加一些boomer和spitter生成的判定,让boomer和spitter比较晚生成 + aSpawnQueue.Push(g_iQueueIndex); + aSpawnQueue.Set(g_iQueueIndex, zombieclass, 0, false); + g_ArraySIlimit[zombieclass - 1] -= 1; + g_iQueueIndex += 1; + Debug_Print("<刷特队列> 当前入队特感:%s,当前队列长度:%d,当前队列索引位置:%d", InfectedName[zombieclass], aSpawnQueue.Length, g_iQueueIndex); + } + } + + if (g_bIsLate) + { + /* + // 当nav存储长度超过特感生成上限时,删去第一个 + if (aSpawnNavList.Length > g_iSiLimit) + { + //Debug_Print(" 当前队列长度:%d, 超过特感上限,清除队列第一个元素", aSpawnNavList.Length); + aSpawnNavList.Erase(0); + } + */ + if (g_iTotalSINum < g_iSiLimit) + { + if (g_iTeleportIndex > 0) + { + g_iTargetSurvivor = GetTargetSurvivor(); + if (g_fTeleportDistance < g_fSpawnDistanceMax) + g_fTeleportDistance += 20.0; + + float fSpawnPos[3] = { 0.0 }; + if (GetSpawnPos(fSpawnPos, g_iTargetSurvivor, g_fTeleportDistance, true)) + { + int iZombieClass = aTeleportQueue.Get(0); + if (!(iZombieClass >= 1 && iZombieClass <= 6)) + { + Debug_Print("特感类型读取错误,读取的特感类型为:%d", iZombieClass); + aTeleportQueue.Erase(0); + g_iTeleportIndex -= 1; + return; + } + + if (SpawnInfected(fSpawnPos, g_fTeleportDistance, iZombieClass, true)) + { + g_iSINum[iZombieClass - 1] += 1; + g_iTotalSINum += 1; + if (aTeleportQueue.Length > 0 && g_iTeleportIndex > 0) + { + aTeleportQueue.Erase(0); + g_iTeleportIndex -= 1; + } + print_type(iZombieClass, g_fSpawnDistance, true); + } + else if (g_iTeleportIndex <= 0) + { + aTeleportQueue.Clear(); + g_iTeleportIndex = 0; + } + } + } + + Debug_Print("spawn_max:%d, tpidx:%d, queue_idx:%d", g_iSpawnMaxCount, g_iTeleportIndex, g_iQueueIndex); + //传送队列优先处理,防止普通刷特刷出来把特感数量刷满了 + if (g_iSpawnMaxCount > 0 && g_iTeleportIndex <= 0 && g_iQueueIndex > 0) + { + g_iTargetSurvivor = GetTargetSurvivor(); + if (g_fSpawnDistance < g_fSpawnDistanceMax) + g_fSpawnDistance += 5.0; + + float fSpawnPos[3] = { 0.0 }; + if (GetSpawnPos(fSpawnPos, g_iTargetSurvivor, g_fSpawnDistance)) + { + int iZombieClass = aSpawnQueue.Get(0); + if (SpawnInfected(fSpawnPos, g_fSpawnDistance, iZombieClass)) + { + g_iSpawnMaxCount -= 1; + g_iSINum[iZombieClass - 1] += 1; + g_iTotalSINum += 1; + if (aSpawnQueue.Length > 0 && g_iQueueIndex > 0) + { + aSpawnQueue.Erase(0); + g_iQueueIndex -= 1; + //刷出来之后要求特感激进进攻 + BypassAndExecuteCommand("nb_assault"); + } + print_type(iZombieClass, g_fSpawnDistance); + } + else + { + if (HasReachedLimit(iZombieClass)) + ReachedLimit(iZombieClass); + + if (g_iQueueIndex <= 0) + { + aSpawnQueue.Clear(); + g_iQueueIndex = 0; + } + } + } + } + } + } +} + +stock bool GetSpawnPos(float fSpawnPos[3], int TargetSurvivor, float SpawnDistance, bool IsTeleport = false) +{ + if (!IsValidClient(TargetSurvivor)) return false; + + float fSurvivorPos[3], fDirection[3], fEndPos[3], fMins[3], fMaxs[3]; + // 根据指定生还者坐标,拓展刷新范围 + GetClientEyePosition(TargetSurvivor, fSurvivorPos); + //增加高度,增加刷房顶的几率 + if (SpawnDistance < 500.0) + fMaxs[2] = fSurvivorPos[2] + 800.0; + else fMaxs[2] = fSurvivorPos[2] + SpawnDistance + 300.0; + + fMins[0] = fSurvivorPos[0] - SpawnDistance; + fMaxs[0] = fSurvivorPos[0] + SpawnDistance; + fMins[1] = fSurvivorPos[1] - SpawnDistance; + fMaxs[1] = fSurvivorPos[1] + SpawnDistance; + fMaxs[2] = fSurvivorPos[2] + SpawnDistance; + // 规定射线方向 + fDirection[0] = 90.0; + fDirection[1] = fDirection[2] = 0.0; + // 随机刷新位置 + fSpawnPos[0] = GetRandomFloat(fMins[0], fMaxs[0]); + fSpawnPos[1] = GetRandomFloat(fMins[1], fMaxs[1]); + fSpawnPos[2] = GetRandomFloat(fSurvivorPos[2], fMaxs[2]); + // 找位条件,可视,是否在有效 NavMesh,是否卡住,否则先会判断是否在有效 Mesh 与是否卡住导致某些位置刷不出特感 + int count2 = 0; + //生成的时候只能在有跑男情况下才特意生成到幸存者前方 + while (PlayerVisibleToSDK(fSpawnPos, IsTeleport) || !IsOnValidMesh(fSpawnPos) || IsPlayerStuck(fSpawnPos) || ((g_bPickRushMan || IsTeleport) && !Is_Pos_Ahead(fSpawnPos, g_iTargetSurvivor))) + { + count2++; + if (count2 > 20) + { + return false; + } + fSpawnPos[0] = GetRandomFloat(fMins[0], fMaxs[0]); + fSpawnPos[1] = GetRandomFloat(fMins[1], fMaxs[1]); + fSpawnPos[2] = GetRandomFloat(fSurvivorPos[2], fMaxs[2]); + TR_TraceRay(fSpawnPos, fDirection, MASK_PLAYERSOLID, RayType_Infinite); + if (TR_DidHit()) + { + TR_GetEndPosition(fEndPos); + fSpawnPos = fEndPos; + fSpawnPos[2] += NAV_MESH_HEIGHT; + } + } + return true; } /* stock bool Is_Nav_already_token(Address nav) { - for(int i = 0; i < aSpawnNavList.Length; i++) - { - if(nav == aSpawnNavList.Get(i)) - return true; - } - return false; + for(int i = 0; i < aSpawnNavList.Length; i++) + { + if(nav == aSpawnNavList.Get(i)) + return true; + } + return false; } */ stock bool SpawnInfected(float fSpawnPos[3], float SpawnDistance, int iZombieClass, bool IsTeleport = false) { - - float fSurvivorPos[3]; - //Debug_Print("生还者看不到"); - // 生还数量为 4,循环 4 次,检测此位置到生还的距离是否小于 750 是则刷特,此处可以刷新 1 ~ g_iSiLimit 只特感,如果此处刷完,则上面的 SpawnSpecial 将不再刷特 - for (int count = 0; count < g_iSurvivorNum; count++) - { - int index = g_iSurvivors[count]; - //不是有效生还者不生成 - if(!IsValidSurvivor(index)) - continue; - - //生还者倒地或者挂边,也不生成 - if(IsClientIncapped(index)){ - continue; - } - //非跑男模式目标已满,跳过 - if(g_bTargetSystemAvailable && !g_bPickRushMan && IsClientReachLimit(index)) - { - continue; - } - GetClientEyePosition(index, fSurvivorPos); - fSurvivorPos[2] -= 60.0; - //获取nav地址 - Address nav1 = L4D_GetNearestNavArea(fSpawnPos, 120.0, false, false, false, TEAM_INFECTED); - Address nav2 = L4D_GetNearestNavArea(fSurvivorPos, 120.0, false, false, false, TEAM_INFECTED); - - //这一段是对高处生成位置进行的补偿 - float distance; - if(IsTeleport) - { - distance = g_fTeleportDistance; - }else - { - distance = g_fSpawnDistance; - } - if(distance * (NORMALPOSMULT - 1) <= 250.0) - { - distance += 250.0; - } - else - { - distance *= NORMALPOSMULT; - } - if(fSpawnPos[2] - fSurvivorPos[2] > HIGHERPOS) - { - distance += HIGHERPOSADDDISTANCE; - } - //nav1 和 nav2 必须有网格相连的路,并且生成距离大于distance,增加不能是同nav网格的要求 - if (L4D2_NavAreaBuildPath(nav1, nav2, distance, TEAM_INFECTED, false) && GetVectorDistance(fSurvivorPos, fSpawnPos, true) >= Pow(g_fSpawnDistanceMin, 2.0) && nav1 != nav2) - { - if (iZombieClass > 0 && !HasReachedLimit(iZombieClass) && CheckSIOption(iZombieClass)) - { - if(IsTeleport && g_iTeleportIndex <= 0){ - return false; - } - if(!IsTeleport && g_iSpawnMaxCount <= 0){ - return false; - } - int entityindex; - if(g_bSIPoolAvailable) - entityindex = g_hSIPool.RequestSIBot(iZombieClass, fSpawnPos); - else - entityindex = L4D2_SpawnSpecial(iZombieClass, fSpawnPos, view_as({0.0, 0.0, 0.0})); - if (IsValidEntity(entityindex) && IsValidEdict(entityindex)) - { - //aSpawnNavList.Push(nav1); - //Debug_Print(" 当前入队nav:%d,当前队列长度:%d", nav1, aSpawnNavList.Length); - if(IsInfectedBot(entityindex) && IsPlayerAlive(entityindex)) - return true; - else - { - Debug_Print("生成错误"); - RemoveEntity(entityindex); - return false; - } - } - } - } - } - return false; + float fSurvivorPos[3]; + Debug_Print("生还者看不到"); + // 生还数量为 4,循环 4 次,检测此位置到生还的距离是否小于 750 是则刷特,此处可以刷新 1 ~ g_iSiLimit 只特感,如果此处刷完,则上面的 SpawnSpecial 将不再刷特 + for (int count = 0; count < g_iSurvivorNum; count++) + { + int index = g_iSurvivors[count]; + //不是有效生还者不生成 + if (!IsValidSurvivor(index)) + continue; + + //生还者倒地或者挂边,也不生成 + if (IsClientIncapped(index)) + continue; + + //非跑男模式目标已满,跳过 + if (g_bTargetSystemAvailable && !g_bPickRushMan && IsClientReachLimit(index)) + continue; + + GetClientEyePosition(index, fSurvivorPos); + fSurvivorPos[2] -= 60.0; + //获取nav地址 + Address nav1 = L4D_GetNearestNavArea(fSpawnPos, 120.0, false, false, false, TEAM_INFECTED); + Address nav2 = L4D_GetNearestNavArea(fSurvivorPos, 120.0, false, false, false, TEAM_INFECTED); + + //这一段是对高处生成位置进行的补偿 + float distance; + if (IsTeleport) + distance = g_fTeleportDistance; + else + distance = g_fSpawnDistance; + + if (distance * (NORMALPOSMULT - 1) <= 250.0) + distance += 250.0; + else + distance *= NORMALPOSMULT; + + if (fSpawnPos[2] - fSurvivorPos[2] > HIGHERPOS) + distance += HIGHERPOSADDDISTANCE; + + // nav1 和 nav2 必须有网格相连的路,并且生成距离大于distance,增加不能是同nav网格的要求 + if (L4D2_NavAreaBuildPath(nav1, nav2, distance, TEAM_INFECTED, false) && GetVectorDistance(fSurvivorPos, fSpawnPos, true) >= Pow(g_fSpawnDistanceMin, 2.0) && nav1 != nav2) + { + if (iZombieClass > 0 && !HasReachedLimit(iZombieClass) && CheckSIOption(iZombieClass)) + { + if (IsTeleport && g_iTeleportIndex <= 0) + return false; + + if (!IsTeleport && g_iSpawnMaxCount <= 0) + return false; + + int entityindex; + if (g_bSIPoolAvailable) + entityindex = g_hSIPool.RequestSIBot(iZombieClass, fSpawnPos); + else entityindex = L4D2_SpawnSpecial(iZombieClass, fSpawnPos, view_as({ 0.0, 0.0, 0.0 })); + + Debug_Print("请求%d特感,生成:%d", iZombieClass, entityindex); + if (IsValidEntity(entityindex) && IsValidEdict(entityindex)) + { + // aSpawnNavList.Push(nav1); + // Debug_Print(" 当前入队nav:%d,当前队列长度:%d", nav1, aSpawnNavList.Length); + if (IsInfectedBot(entityindex) && IsPlayerAlive(entityindex)) + return true; + else + { + Debug_Print("生成错误"); + RemoveEntity(entityindex); + return false; + } + } + } + } + } + return false; } // 当前在场的某种特感种类数量达到 Cvar 限制,但因为刷新一个特感,出队此元素,之后再入队相同特感元素,则会刷不出来,需要处理重复情况,如果队列长度大于 1 且索引大于 0,说明队列存在 @@ -763,255 +751,238 @@ stock bool SpawnInfected(float fSpawnPos[3], float SpawnDistance, int iZombieCla // 如:当前存在 2 个 Smoker 未死亡,Smoker 的 Cvar 限制为 2 ,这时入队一个 Smoker 元素,则会导致无法刷出特感 void ReachedLimit(int type) { - if (aSpawnQueue.Length > 1 && g_iQueueIndex > 0) - { - Debug_Print("%s上限已到,无法生成,且队列不为空,删除第一个队列元素", InfectedName[type]); - aSpawnQueue.Erase(0); - g_iQueueIndex -= 1; - } - else - { - for (int i = 1; i <= 6; i++) - { - if (CheckSIOption(i) && !HasReachedLimit(i)) - { - Debug_Print("%s上限已到,无法生成,当前队列为空,遍历1-6类型发现%s类型未满", InfectedName[type], InfectedName[i]); - aSpawnQueue.Set(0, i, 0, false); - } - } - } -} - -public int CheckSIOption(int type){ + if (aSpawnQueue.Length > 1 && g_iQueueIndex > 0) + { + Debug_Print("%s上限已到,无法生成,且队列不为空,删除第一个队列元素", InfectedName[type]); + aSpawnQueue.Erase(0); + g_iQueueIndex -= 1; + } + else + for (int i = 1; i <= 6; i++) + if (CheckSIOption(i) && !HasReachedLimit(i)) + { + Debug_Print("%s上限已到,无法生成,当前队列为空,遍历1-6类型发现%s类型未满", InfectedName[type], InfectedName[i]); + aSpawnQueue.Set(0, i, 0, false); + } +} + +int CheckSIOption(int type) +{ switch (type) { case 1: - { return ENABLE_SMOKER & g_iEnableSIoption; - } case 2: - { return ENABLE_BOOMER & g_iEnableSIoption; - } case 3: - { return ENABLE_HUNTER & g_iEnableSIoption; - } case 4: - { return ENABLE_SPITTER & g_iEnableSIoption; - } case 5: - { return ENABLE_JOCKEY & g_iEnableSIoption; - } case 6: - { return ENABLE_CHARGER & g_iEnableSIoption; - } } return 0; } - // 当前某种特感数量是否达到 Convar 值限制 bool HasReachedLimit(int zombieclass) { - int count = 0; char convar[16] = {'\0'}; - for (int infected = 1; infected <= MaxClients; infected++) - { - if (IsClientConnected(infected) && IsClientInGame(infected) && GetEntProp(infected, Prop_Send, "m_zombieClass") == zombieclass) - { - count += 1; - } - } - if((g_hAllChargerMode.BoolValue || g_hAllHunterMode.BoolValue) && count == g_iSiLimit){ - return true; - } - else if((g_hAllChargerMode.BoolValue || g_hAllHunterMode.BoolValue) && count < g_iSiLimit){ - return false; - } - FormatEx(convar, sizeof(convar), "z_%s_limit", InfectedName[zombieclass]); - if (count == GetConVarInt(FindConVar(convar))) - { - return true; - } - else - { - return false; - } -} - -stock void print_type(int iType, float SpawnDistance, bool Isteleport = false){ - if (iType >= 1 && iType <=6) - { - Debug_Print(" %s生成一只%s,当前%s数量:%d,特感总数量 %d, 真实特感数量:%d, 找位最大单位距离:%f", Isteleport?"传送":"", InfectedName[iType], InfectedName[iType], g_iSINum[iType -1], g_iTotalSINum, GetCurrentSINum(), SpawnDistance); - } + int count = 0; + static char convar[16]; + for (int infected = 1; infected <= MaxClients; infected++) + if (IsClientConnected(infected) && IsClientInGame(infected) && !IsGhost(infected) + && GetEntProp(infected, Prop_Send, "m_zombieClass") == zombieclass) + count += 1; + + if ((g_hAllChargerMode.BoolValue || g_hAllHunterMode.BoolValue) && count == g_iSiLimit) + return true; + else if ((g_hAllChargerMode.BoolValue || g_hAllHunterMode.BoolValue) && count < g_iSiLimit) + return false; + + FormatEx(convar, sizeof(convar), "z_%s_limit\0", InfectedName[zombieclass]); + // if (count == GetConVarInt(FindConVar(convar))) + // { + // return true; + // } + // else + // { + // return false; + // } + return count == GetConVarInt(FindConVar(convar)); +} + +void print_type(int iType, float SpawnDistance, bool Isteleport = false) +{ + if (iType >= 1 && iType <= 6) + { + Debug_Print(" %s生成一只%s,当前%s数量:%d,特感总数量 %d, 真实特感数量:%d, 找位最大单位距离:%f", Isteleport ? "传送" : "", InfectedName[iType], InfectedName[iType], g_iSINum[iType - 1], g_iTotalSINum, GetCurrentSINum(), SpawnDistance); + } } // 初始 & 动态刷特时钟 -public Action SpawnFirstInfected(Handle timer) -{ - if (!g_bIsLate) - { - g_bIsLate = true; - //首先触发一次刷特,然后每1s检测 - g_hCheckShouldSpawnOrNot = CreateTimer(1.0, CheckShouldSpawnOrNot, _, TIMER_REPEAT); - SpawnInfectedSettings(); - if (g_bTeleportSi) - { - g_hTeleHandle = CreateTimer(1.0, Timer_PositionSi, _, TIMER_REPEAT); - } - } - return Plugin_Stop; -} - -public Action SpawnNewInfected(Handle timer) -{ - SpawnInfectedSettings(); - g_hSpawnProcess = INVALID_HANDLE; - return Plugin_Stop; -} - -public void SpawnInfectedSettings() -{ - if (g_bIsLate) - { - g_iSurvivorNum = 0; - g_iLastSpawnTime = 0; - for (int client = 1; client <= MaxClients; client++) - { - if (IsValidSurvivor(client) && IsPlayerAlive(client)) - { - g_iSurvivors[g_iSurvivorNum] = client; - g_iSurvivorNum += 1; - } - } - g_fSpawnDistance = g_fSpawnDistanceMin; - /* - //优化性能,每波刷新前清除aSpawnNavList队列中的值,但是如果刷特时间很短,这个优化估计起的作用不大 - if(g_iSpawnMaxCount == 0) - { - aSpawnNavList.Clear(); - } - */ - - g_iSpawnMaxCount += g_iSiLimit; - g_bShouldCheck = true; - g_iWaveTime++; - Debug_Print("开始第%d波刷特", g_iWaveTime); - - // 当一定时间内刷不出特感,触发时钟使 g_iSpawnMaxCount 超过 g_iSiLimit 值时,最多允许刷出 g_iSiLimit + 2 只特感,防止连续刷 2-3 波的情况 - if (g_iSiLimit < g_iSpawnMaxCount) - { - - g_iSpawnMaxCount = g_iSiLimit; - Debug_Print("当前特感数量达到上限"); - } - - } +Action SpawnFirstInfected(Handle timer) +{ + if (!g_bIsLate) + { + g_bIsLate = true; + //首先触发一次刷特,然后每1s检测 + g_hCheckShouldSpawnOrNot = CreateTimer(1.0, CheckShouldSpawnOrNot, _, TIMER_REPEAT); + SpawnInfectedSettings(); + if (g_bTeleportSi) + g_hTeleHandle = CreateTimer(1.0, Timer_PositionSi, _, TIMER_REPEAT); + } + return Plugin_Stop; +} + +Action SpawnNewInfected(Handle timer) +{ + SpawnInfectedSettings(); + g_hSpawnProcess = INVALID_HANDLE; + return Plugin_Stop; +} + +void SpawnInfectedSettings() +{ + if (g_bIsLate) + { + g_iSurvivorNum = 0; + g_iLastSpawnTime = 0; + for (int client = 1; client <= MaxClients; client++) + if (IsValidSurvivor(client) && IsPlayerAlive(client)) + { + g_iSurvivors[g_iSurvivorNum] = client; + g_iSurvivorNum += 1; + } + + g_fSpawnDistance = g_fSpawnDistanceMin; + /* + //优化性能,每波刷新前清除aSpawnNavList队列中的值,但是如果刷特时间很短,这个优化估计起的作用不大 + if(g_iSpawnMaxCount == 0) + { + aSpawnNavList.Clear(); + } + */ + + g_iSpawnMaxCount += g_iSiLimit; + g_bShouldCheck = true; + g_iWaveTime++; + Debug_Print("开始第%d波刷特", g_iWaveTime); + + // 当一定时间内刷不出特感,触发时钟使 g_iSpawnMaxCount 超过 g_iSiLimit 值时,最多允许刷出 g_iSiLimit + 2 只特感,防止连续刷 2-3 波的情况 + if (g_iSiLimit < g_iSpawnMaxCount) + { + g_iSpawnMaxCount = g_iSiLimit; + Debug_Print("当前特感数量达到上限"); + } + } } public void OnUnpause() { - if(g_hSpawnProcess == INVALID_HANDLE) - { - Debug_Print("解除暂停,原先一波刷特进程已经在处理,下一波刷特是%.2f秒后", g_fUnpauseNextSpawnTime); - g_hSpawnProcess = CreateTimer(g_fUnpauseNextSpawnTime, SpawnNewInfected, _, TIMER_REPEAT); - } -} - -public Action CheckShouldSpawnOrNot(Handle timer) -{ - if(IsInPause()) - { - Debug_Print("处于暂停状态,停止刷特"); - if(g_hSpawnProcess != INVALID_HANDLE) - { - g_fUnpauseNextSpawnTime = g_fSiInterval - (GetGameTime() - g_fLastSISpawnStartTime); - KillTimer(g_hSpawnProcess); - g_hSpawnProcess = INVALID_HANDLE; - } - return Plugin_Continue; - } - g_iLastSpawnTime ++; - if(!g_bIsLate) return Plugin_Stop; - if(!g_bShouldCheck && g_hSpawnProcess != INVALID_HANDLE) return Plugin_Continue; - if(FindConVar("survivor_limit").IntValue >= 2 && IsAnyTankOrAboveHalfSurvivorDownOrDied() && g_iLastSpawnTime < RoundToFloor(g_fSiInterval / 2)) return Plugin_Continue; - //防止0s情况下spitter无法快速踢出导致的特感越刷越少问题 - if(g_iEnableSIoption & ENABLE_SPITTER && g_iLastSpawnTime < 4) {Debug_Print("因为可以刷spitter,所以最低4秒起刷,不然容易造成特感数量统计错误,特感生成不出来");return Plugin_Continue;} - if(!g_bAutoSpawnTimeControl) - { - g_bShouldCheck = false; - if(g_iSpawnMaxCount == g_iSiLimit) - { - Debug_Print("固定增时系统因为等待刷特数量达到上限,暂停刷特, 总用时:%.1f秒", g_iLastSpawnTime + g_fSiInterval); - g_iLastSpawnTime = 0; - } - else - { - Debug_Print("固定增时系统开始新一波刷特, 总用时:%.1f秒", g_iLastSpawnTime + g_fSiInterval); - g_hSpawnProcess = CreateTimer(g_fSiInterval * 1.5, SpawnNewInfected, _, TIMER_REPEAT); - } - } - else - { - if((IsAllKillersDown() && g_iSpawnMaxCount == 0) || (g_iTotalSINum <= (RoundToFloor(g_iSiLimit / 4.0) + 1) && g_iSpawnMaxCount == 0) || (g_iLastSpawnTime >= g_fSiInterval * 0.5)) - { - g_bShouldCheck = false; - if(g_iSpawnMaxCount == g_iSiLimit) - { - Debug_Print("自动增时系统因为等待刷特数量达到上限,暂停刷特, 总用时:%.1f秒", g_iLastSpawnTime + g_fSiInterval); - g_iLastSpawnTime = 0; - } - else - { - Debug_Print("自动增时系统开始新一波刷特, 总用时:%.1f秒", g_iLastSpawnTime + g_fSiInterval); - g_hSpawnProcess = CreateTimer(g_fSiInterval, SpawnNewInfected, _, TIMER_REPEAT); - } - } - } - g_fLastSISpawnStartTime = GetGameTime(); - return Plugin_Continue; + if (g_hSpawnProcess == INVALID_HANDLE) + { + Debug_Print("解除暂停,原先一波刷特进程已经在处理,下一波刷特是%.2f秒后", g_fUnpauseNextSpawnTime); + g_hSpawnProcess = CreateTimer(g_fUnpauseNextSpawnTime, SpawnNewInfected, _, TIMER_REPEAT); + } +} + +Action CheckShouldSpawnOrNot(Handle timer) +{ + if (IsInPause()) + { + Debug_Print("处于暂停状态,停止刷特"); + if (g_hSpawnProcess != INVALID_HANDLE) + { + g_fUnpauseNextSpawnTime = g_fSiInterval - (GetGameTime() - g_fLastSISpawnStartTime); + KillTimer(g_hSpawnProcess); + g_hSpawnProcess = INVALID_HANDLE; + } + return Plugin_Continue; + } + + g_iLastSpawnTime++; + if (!g_bIsLate) return Plugin_Stop; + if (!g_bShouldCheck && g_hSpawnProcess != INVALID_HANDLE) return Plugin_Continue; + if (FindConVar("survivor_limit").IntValue >= 2 && IsAnyTankOrAboveHalfSurvivorDownOrDied() && g_iLastSpawnTime < RoundToFloor(g_fSiInterval / 2)) return Plugin_Continue; + //防止0s情况下spitter无法快速踢出导致的特感越刷越少问题 + if (g_iEnableSIoption & ENABLE_SPITTER && g_iLastSpawnTime < 4 && !g_bSIPoolAvailable) // 使用 SIPool 后无此问题 + { + Debug_Print("因为可以刷spitter,所以最低4秒起刷,不然容易造成特感数量统计错误,特感生成不出来"); + return Plugin_Continue; + } + if (!g_bAutoSpawnTimeControl) + { + g_bShouldCheck = false; + if (g_iSpawnMaxCount == g_iSiLimit) + { + Debug_Print("固定增时系统因为等待刷特数量达到上限,暂停刷特, 总用时:%.1f秒", g_iLastSpawnTime + g_fSiInterval); + g_iLastSpawnTime = 0; + } + else + { + Debug_Print("固定增时系统开始新一波刷特, 总用时:%.1f秒", g_iLastSpawnTime + g_fSiInterval); + g_hSpawnProcess = CreateTimer(g_fSiInterval * 1.5, SpawnNewInfected, _, TIMER_REPEAT); + } + } + else if ((IsAllKillersDown() && g_iSpawnMaxCount == 0) || (g_iTotalSINum <= (RoundToFloor(g_iSiLimit / 4.0) + 1) && g_iSpawnMaxCount == 0) || (g_iLastSpawnTime >= g_fSiInterval * 0.5)) + { + g_bShouldCheck = false; + if (g_iSpawnMaxCount == g_iSiLimit) + { + Debug_Print("自动增时系统因为等待刷特数量达到上限,暂停刷特, 总用时:%.1f秒", g_iLastSpawnTime + g_fSiInterval); + g_iLastSpawnTime = 0; + } + else + { + Debug_Print("自动增时系统开始新一波刷特, 总用时:%.1f秒", g_iLastSpawnTime + g_fSiInterval); + g_hSpawnProcess = CreateTimer(g_fSiInterval, SpawnNewInfected, _, TIMER_REPEAT); + } + } + g_fLastSISpawnStartTime = GetGameTime(); + return Plugin_Continue; } //是否存在非克、舌头、口水、胖子存活 bool IsAllKillersDown() { - return (g_iSINum[view_as(ZC_CHARGER) - 1] + g_iSINum[view_as(ZC_HUNTER) - 1] + g_iSINum[view_as(ZC_JOCKEY)] - 1) == 0; + return (g_iSINum[view_as(ZC_CHARGER) - 1] + + g_iSINum[view_as(ZC_HUNTER) - 1] + + g_iSINum[view_as(ZC_JOCKEY)] - 1) + == 0; } -stock void BypassAndExecuteCommand(char []strCommand) +stock void BypassAndExecuteCommand(char[] strCommand) { - int flags = GetCommandFlags(strCommand); - SetCommandFlags(strCommand, flags & ~ FCVAR_CHEAT); - FakeClientCommand(GetRandomSurvivor(), "%s", strCommand); - SetCommandFlags(strCommand, flags); + int flags = GetCommandFlags(strCommand); + SetCommandFlags(strCommand, flags & ~FCVAR_CHEAT); + FakeClientCommand(GetRandomSurvivor(), "%s", strCommand); + SetCommandFlags(strCommand, flags); } // 开局重置特感状态 -public Action SafeRoomReset(Handle timer) -{ - ResetStatus(); - return Plugin_Continue; -} - -public void ResetStatus(){ - g_iTotalSINum = 0; - for (int client = 1; client <= MaxClients; client++) - { - if (IsInfectedBot(client) && IsPlayerAlive(client)) - { - g_iTeleCount[client] = 0; - int type = GetEntProp(client, Prop_Send, "m_zombieClass"); - g_iSINum[type - 1] += 1; - g_iTotalSINum += 1; - } - if (IsValidSurvivor(client) && !IsPlayerAlive(client)) - { - L4D_RespawnPlayer(client); - } - } +Action SafeRoomReset(Handle timer) +{ + ResetStatus(); + return Plugin_Continue; +} + +void ResetStatus() +{ + g_iTotalSINum = 0; + for (int client = 1; client <= MaxClients; client++) + { + if (IsInfectedBot(client) && !IsGhost(client)) + { + g_iTeleCount[client] = 0; + int type = GetEntProp(client, Prop_Send, "m_zombieClass"); + g_iSINum[type - 1] += 1; + g_iTotalSINum += 1; + } + if (IsValidSurvivor(client) && !IsPlayerAlive(client)) + L4D_RespawnPlayer(client); + } } // ********************* @@ -1019,549 +990,514 @@ public void ResetStatus(){ // ********************* bool IsInfectedBot(int client) { - if (client > 0 && client <= MaxClients && IsClientInGame(client) && IsFakeClient(client) && GetClientTeam(client) == TEAM_INFECTED && GetEntProp(client, Prop_Send, "m_zombieClass") <= 6 && GetEntProp(client, Prop_Send, "m_zombieClass") >=1) - { - return true; - } - else - { - return false; - } + // if (client > 0 && client <= MaxClients && IsClientInGame(client) && IsFakeClient(client) && GetClientTeam(client) == TEAM_INFECTED && GetEntProp(client, Prop_Send, "m_zombieClass") <= 6 && GetEntProp(client, Prop_Send, "m_zombieClass") >= 1) + // { + // return true; + // } + // else + // { + // return false; + // } + + return client > 0 && client <= MaxClients && IsClientInGame(client) && IsFakeClient(client) + && GetClientTeam(client) == TEAM_INFECTED && GetEntProp(client, Prop_Send, "m_zombieClass") <= 6 + && GetEntProp(client, Prop_Send, "m_zombieClass") >= 1; } bool IsOnValidMesh(float fReferencePos[3]) { - Address pNavArea = L4D2Direct_GetTerrorNavArea(fReferencePos); - if (pNavArea != Address_Null && !(L4D_GetNavArea_SpawnAttributes(pNavArea) & CHECKPOINT)) - { - return true; - } - else - { - return false; - } + Address pNavArea = L4D2Direct_GetTerrorNavArea(fReferencePos); + // if (pNavArea != Address_Null && !(L4D_GetNavArea_SpawnAttributes(pNavArea) & CHECKPOINT)) + // { + // return true; + // } + // else + // { + // return false; + // } + + // 我真心建议这样写,可读性不比用if分支差,一个方法太长看着会很乱的 + return pNavArea != Address_Null && !(L4D_GetNavArea_SpawnAttributes(pNavArea) & CHECKPOINT); } //判断该坐标是否可以看到生还或者距离小于g_fSpawnDistanceMin码,减少一层栈函数,增加实时性,单人模式增加2条射线模仿左右眼 stock bool PlayerVisibleTo(float targetposition[3], bool IsTeleport = false) { - float position[3], vAngles[3], vLookAt[3], spawnPos[3]; - for (int client = 1; client <= MaxClients; ++client) - { - if (IsClientConnected(client) && IsClientInGame(client) && IsValidSurvivor(client) && IsPlayerAlive(client)) - { - //传送的时候无视倒地或者挂边生还者的视线,检测到跑男时,也不关注被控生还者的视线 - if(IsTeleport && (IsClientIncapped(client) || (g_bPickRushMan && IsPinned(client)))){ - if(!g_bIgnoreIncappedSurvivorSight){ - int sum = 0; - float temp[3]; - for(int i = 0; i < MaxClients; i++){ - if(i != client && IsValidSurvivor(i) && !IsClientIncapped(i)){ - GetClientAbsOrigin(i, temp); - //倒地生还者INCAPSURVIVORCHECKDIS范围内已经没有正常生还者,掠过这个人的视线判断 - if(GetVectorDistance(temp, position, true) < Pow(INCAPSURVIVORCHECKDIS, 2.0)){ - sum ++; - } - } - } - if(sum == 0){ - Debug_Print("Teleport方法,目标位置已经不能被正常生还者所看到"); - continue; - }else{ - Debug_Print("Teleport方法,目标位置依旧能被正常生还者看到,sum为:%d", sum); - } - } - else{ - continue; - } - - } - GetClientEyePosition(client, position); - //position[0] += 20; - if(GetVectorDistance(targetposition, position, true) < Pow(g_fSpawnDistanceMin, 2.0)) - { - return true; - } - MakeVectorFromPoints(targetposition, position, vLookAt); - GetVectorAngles(vLookAt, vAngles); - Handle trace = TR_TraceRayFilterEx(targetposition, vAngles, MASK_VISIBLE, RayType_Infinite, TraceFilter, client); - if(TR_DidHit(trace)) - { - static float vStart[3]; - TR_GetEndPosition(vStart, trace); - if((GetVectorDistance(targetposition, vStart, false) + 75.0) >= GetVectorDistance(position, targetposition)) - { - return true; - } - else - { - spawnPos = targetposition; - spawnPos[2] += 40.0; - MakeVectorFromPoints(spawnPos, position, vLookAt); - GetVectorAngles(vLookAt, vAngles); - Handle trace2 = TR_TraceRayFilterEx(spawnPos, vAngles, MASK_VISIBLE, RayType_Infinite, TraceFilter, client); - if(TR_DidHit(trace2)) - { - TR_GetEndPosition(vStart, trace2); - if((GetVectorDistance(spawnPos, vStart, false) + 75.0) >= GetVectorDistance(position, spawnPos)) - return true; - } - else - { - return true; - } - delete trace2; - } - } - else - { - return true; - } - delete trace; - } - } - return false; -} - -//thanks fdxx https://github.com/fdxx/l4d2_plugins/blob/main/l4d2_si_spawn_control.sp -stock bool PlayerVisibleToSDK(float targetposition[3], bool IsTeleport = false){ - static float fTargetPos[3]; - - float position[3]; - fTargetPos = targetposition; - fTargetPos[2] += 62.0; //眼睛位置 - - //计算该位置是不是和所有人都相隔大于g_fSpawnDistanceMax - int count = 0, skipcount = 0; - - for (int client = 1; client <= MaxClients; client++) - { - if (IsClientInGame(client) && GetClientTeam(client) == 2 && IsPlayerAlive(client)) - { - GetClientEyePosition(client, position); - //传送的时候无视倒地或者挂边生还者的视线,检测到跑男时,也不关注被控生还者的视线 - if(IsTeleport && (IsClientIncapped(client) || (g_bPickRushMan && IsPinned(client)))){ - if(!g_bIgnoreIncappedSurvivorSight){ - int sum = 0; - float temp[3]; - for(int i = 1; i <= MaxClients; i++){ - if(i != client && IsValidSurvivor(i) && !IsClientIncapped(i)){ - GetClientAbsOrigin(i, temp); - //倒地生还者500范围内已经没有正常生还者,掠过这个人的视线判断 - if(GetVectorDistance(temp, position, true) < Pow(INCAPSURVIVORCHECKDIS, 2.0)){ - sum ++; - } - } - } - if(sum == 0){ - Debug_Print("Teleport方法,目标位置已经不能被正常生还者所看到"); - skipcount++; - continue; - }else{ - Debug_Print("Teleport方法,目标位置依旧能被正常生还者看到,sum为:%d", sum); - } - }else{ - skipcount++; - continue; - } - } - //太近直接返回看见 - if(GetVectorDistance(targetposition, position, true) < Pow(g_fSpawnDistanceMin, 2.0)) - { - return true; - } - //太远直接返回没看见 - if(GetVectorDistance(targetposition, position, true) >= Pow(g_fSpawnDistanceMax, 2.0)) - { - count++; - if(count >= (g_iSurvivorNum - skipcount)){ - return false; - } - - } - if (L4D2_IsVisibleToPlayer(client, 2, 3, 0, targetposition)) - { - return true; - } - if (L4D2_IsVisibleToPlayer(client, 2, 3, 0, fTargetPos)) - { - return true; - } - } - } - - return false; + float position[3], vAngles[3], vLookAt[3], spawnPos[3]; + for (int client = 1; client <= MaxClients; ++client) + { + if (IsClientConnected(client) && IsClientInGame(client) && IsValidSurvivor(client) && IsPlayerAlive(client)) + { + //传送的时候无视倒地或者挂边生还者的视线,检测到跑男时,也不关注被控生还者的视线 + if (IsTeleport && (IsClientIncapped(client) || (g_bPickRushMan && IsPinned(client)))) + if (!g_bIgnoreIncappedSurvivorSight) + { + int sum = 0; + float temp[3]; + for (int i = 0; i < MaxClients; i++) + if (i != client && IsValidSurvivor(i) && !IsClientIncapped(i)) + { + GetClientAbsOrigin(i, temp); + //倒地生还者INCAPSURVIVORCHECKDIS范围内已经没有正常生还者,掠过这个人的视线判断 + if (GetVectorDistance(temp, position, true) < Pow(INCAPSURVIVORCHECKDIS, 2.0)) + sum++; + } + + if (sum == 0) + { + Debug_Print("Teleport方法,目标位置已经不能被正常生还者所看到"); + continue; + } + else Debug_Print("Teleport方法,目标位置依旧能被正常生还者看到,sum为:%d", sum); + } + else continue; + + GetClientEyePosition(client, position); + // position[0] += 20; + if (GetVectorDistance(targetposition, position, true) < Pow(g_fSpawnDistanceMin, 2.0)) + return true; + + MakeVectorFromPoints(targetposition, position, vLookAt); + GetVectorAngles(vLookAt, vAngles); + Handle trace = TR_TraceRayFilterEx(targetposition, vAngles, MASK_VISIBLE, RayType_Infinite, TraceFilter, client); + if (TR_DidHit(trace)) + { + static float vStart[3]; + TR_GetEndPosition(vStart, trace); + delete trace; // 用完就 delete,不然迟早会忘 + if ((GetVectorDistance(targetposition, vStart, false) + 75.0) >= GetVectorDistance(position, targetposition)) + return true; + + // else // 都 return 了就别 else 了,一堆 大括号 + 缩进 看着眼疼 + // { + spawnPos = targetposition; + spawnPos[2] += 40.0; + MakeVectorFromPoints(spawnPos, position, vLookAt); + GetVectorAngles(vLookAt, vAngles); + Handle trace2 = TR_TraceRayFilterEx(spawnPos, vAngles, MASK_VISIBLE, RayType_Infinite, TraceFilter, client); + if (TR_DidHit(trace2)) + { + TR_GetEndPosition(vStart, trace2); + delete trace2; + if ((GetVectorDistance(spawnPos, vStart, false) + 75.0) >= GetVectorDistance(position, spawnPos)) + return true; + } + else delete trace2; + + return true; + // delete trace2; // 你都 return 了,还怎么delete??? + // } + } + else delete trace; + + return true; + // delete trace; // 你都 return 了,还怎么delete??? + } + } + return false; +} + +// thanks fdxx https://github.com/fdxx/l4d2_plugins/blob/main/l4d2_si_spawn_control.sp +stock bool PlayerVisibleToSDK(float targetposition[3], bool IsTeleport = false) +{ + static float fTargetPos[3]; + + float position[3]; + fTargetPos = targetposition; + fTargetPos[2] += 62.0; //眼睛位置 + + //计算该位置是不是和所有人都相隔大于g_fSpawnDistanceMax + int count = 0, skipcount = 0; + + for (int client = 1; client <= MaxClients; client++) + { + if (IsClientInGame(client) && GetClientTeam(client) == 2 && IsPlayerAlive(client)) + { + GetClientEyePosition(client, position); + //传送的时候无视倒地或者挂边生还者的视线,检测到跑男时,也不关注被控生还者的视线 + if (IsTeleport && (IsClientIncapped(client) || (g_bPickRushMan && IsPinned(client)))) + { + if (!g_bIgnoreIncappedSurvivorSight) + { + int sum = 0; + float temp[3]; + for (int i = 1; i <= MaxClients; i++) + if (i != client && IsValidSurvivor(i) && !IsClientIncapped(i)) + { + GetClientAbsOrigin(i, temp); + //倒地生还者500范围内已经没有正常生还者,掠过这个人的视线判断 + if (GetVectorDistance(temp, position, true) < Pow(INCAPSURVIVORCHECKDIS, 2.0)) + sum++; + } + + if (sum == 0) + { + Debug_Print("Teleport方法,目标位置已经不能被正常生还者所看到"); + skipcount++; + continue; + } + else Debug_Print("Teleport方法,目标位置依旧能被正常生还者看到,sum为:%d", sum); + } + else + { + skipcount++; + continue; + } + } + //太近直接返回看见 + if (GetVectorDistance(targetposition, position, true) < Pow(g_fSpawnDistanceMin, 2.0)) + return true; + + //太远直接返回没看见 + if (GetVectorDistance(targetposition, position, true) >= Pow(g_fSpawnDistanceMax, 2.0)) + { + count++; + if (count >= (g_iSurvivorNum - skipcount)) + return false; + } + if (L4D2_IsVisibleToPlayer(client, 2, 3, 0, targetposition)) + return true; + if (L4D2_IsVisibleToPlayer(client, 2, 3, 0, fTargetPos)) + return true; + } + } + + return false; } bool IsPlayerStuck(float fSpawnPos[3]) { - //似乎所有客户端的尺寸都一样 - static const float fClientMinSize[3] = {-16.0, -16.0, 0.0}; - static const float fClientMaxSize[3] = {16.0, 16.0, 72.0}; + //似乎所有客户端的尺寸都一样 + static const float fClientMinSize[3] = { -16.0, -16.0, 0.0 }; + static const float fClientMaxSize[3] = { 16.0, 16.0, 72.0 }; - static bool bHit; - static Handle hTrace; + static bool bHit; + static Handle hTrace; - hTrace = TR_TraceHullFilterEx(fSpawnPos, fSpawnPos, fClientMinSize, fClientMaxSize, MASK_PLAYERSOLID, TraceFilter_Stuck); - bHit = TR_DidHit(hTrace); + hTrace = TR_TraceHullFilterEx(fSpawnPos, fSpawnPos, fClientMinSize, fClientMaxSize, MASK_PLAYERSOLID, TraceFilter_Stuck); + bHit = TR_DidHit(hTrace); - delete hTrace; - return bHit; + delete hTrace; + return bHit; } stock bool TraceFilter_Stuck(int entity, int contentsMask) { - if (entity <= MaxClients || !IsValidEntity(entity)) - { - return false; - } - else{ - static char sClassName[20]; - GetEntityClassname(entity, sClassName, sizeof(sClassName)); - if (strcmp(sClassName, "env_physics_blocker") == 0 && !EnvBlockType(entity)){ - return false; - } - } - return true; -} - -stock bool EnvBlockType(int entity){ - int BlockType = GetEntProp(entity, Prop_Data, "m_nBlockType"); - //阻拦ai infected - if(BlockType == 1 || BlockType == 2){ - return false; - } - else{ - return true; - } + if (entity <= MaxClients || !IsValidEntity(entity)) + return false; + + static char sClassName[20]; + GetEntityClassname(entity, sClassName, sizeof(sClassName)); + if (strcmp(sClassName, "env_physics_blocker") == 0 && !EnvBlockType(entity)) + return false; + + return true; +} + +stock bool EnvBlockType(int entity) +{ + int BlockType = GetEntProp(entity, Prop_Data, "m_nBlockType"); + //阻拦ai infected + // if (BlockType == 1 || BlockType == 2) + // return false; + // else + // return true; + return !(BlockType == 1 || BlockType == 2); } stock bool TraceFilter(int entity, int contentsMask) { - if (entity <= MaxClients || !IsValidEntity(entity)) - { - return false; - } - else - { - static char sClassName[9]; - GetEntityClassname(entity, sClassName, sizeof(sClassName)); - if (strcmp(sClassName, "infected") == 0 || strcmp(sClassName, "witch") == 0) - { - return false; - } - } - return true; + if (entity <= MaxClients || !IsValidEntity(entity)) + return false; + + static char sClassName[9]; + GetEntityClassname(entity, sClassName, sizeof(sClassName)); + if (strcmp(sClassName, "infected") == 0 || strcmp(sClassName, "witch") == 0) + return false; + + return true; } bool IsPinned(int client) { - bool bIsPinned = false; - if (IsValidSurvivor(client) && IsPlayerAlive(client)) - { - if(GetEntPropEnt(client, Prop_Send, "m_tongueOwner") > 0) bIsPinned = true; - if(GetEntPropEnt(client, Prop_Send, "m_pounceAttacker") > 0) bIsPinned = true; - if(GetEntPropEnt(client, Prop_Send, "m_carryAttacker") > 0) bIsPinned = true; - if(GetEntPropEnt(client, Prop_Send, "m_pummelAttacker") > 0) bIsPinned = true; - if(GetEntPropEnt(client, Prop_Send, "m_jockeyAttacker") > 0) bIsPinned = true; - } - return bIsPinned; + if (!(IsValidSurvivor(client) && IsPlayerAlive(client))) return false; + + return GetEntPropEnt(client, Prop_Send, "m_tongueOwner") > 0 + || GetEntPropEnt(client, Prop_Send, "m_carryAttacker") > 0 + || GetEntPropEnt(client, Prop_Send, "m_jockeyAttacker") > 0 + || GetEntPropEnt(client, Prop_Send, "m_pounceAttacker") > 0 + || GetEntPropEnt(client, Prop_Send, "m_pummelAttacker") > 0; } bool IsPinningSomeone(int client) { - bool bIsPinning = false; - if (IsInfectedBot(client)) - { - if (GetEntPropEnt(client, Prop_Send, "m_tongueVictim") > 0) bIsPinning = true; - if (GetEntPropEnt(client, Prop_Send, "m_jockeyVictim") > 0) bIsPinning = true; - if (GetEntPropEnt(client, Prop_Send, "m_pounceVictim") > 0) bIsPinning = true; - if (GetEntPropEnt(client, Prop_Send, "m_pummelVictim") > 0) bIsPinning = true; - if (GetEntPropEnt(client, Prop_Send, "m_carryVictim") > 0) bIsPinning = true; - } - return bIsPinning; + bool bIsPinning = false; + if (IsInfectedBot(client)) + { + if (GetEntPropEnt(client, Prop_Send, "m_tongueVictim") > 0) bIsPinning = true; + if (GetEntPropEnt(client, Prop_Send, "m_jockeyVictim") > 0) bIsPinning = true; + if (GetEntPropEnt(client, Prop_Send, "m_pounceVictim") > 0) bIsPinning = true; + if (GetEntPropEnt(client, Prop_Send, "m_pummelVictim") > 0) bIsPinning = true; + if (GetEntPropEnt(client, Prop_Send, "m_carryVictim") > 0) bIsPinning = true; + } + return bIsPinning; } bool CanBeTeleport(int client) { - if (IsInfectedBot(client) && IsClientInGame(client)&& IsPlayerAlive(client) && GetEntProp(client, Prop_Send, "m_zombieClass") != ZC_TANK && !IsPinningSomeone(client)) - { - // 防止无声口水 - if(IsSpitter(client) && GetGameTime() - g_fSpitterSpitTime[client] < SPIT_INTERVAL) - { - return false; - } - - if(GetClosetSurvivorDistance(client) < g_fSpawnDistanceMin) - { - return false; - } - - //舌头能力检查 - if(IsAiSmoker(client) && g_bSmokerAvailable && !IsSmokerCanUseAbility(client)) - { - return false; - } - float fPos[3]; - GetClientAbsOrigin(client, fPos); - if(Is_Pos_Ahead(fPos)) - { - return false; - } - return true; - } - else - { - return false; - } -} - -//5秒内以1s检测一次,5次没被看到,就可以踢出并加入传送队列 -public Action Timer_PositionSi(Handle timer) -{ - if(IsInPause()) - { - Debug_Print("处于暂停状态,停止传送检测"); - return Plugin_Continue; - } - //每1s找一次跑男或者是否所有全被控 - if(CheckRushManAndAllPinned()) - { - return Plugin_Continue; - } - for (int client = 1; client <= MaxClients; client++) - { - if(CanBeTeleport(client)){ - float fSelfPos[3] = {0.0}; - GetClientEyePosition(client, fSelfPos); - if (!PlayerVisibleToSDK(fSelfPos, true)) - { - // 如果是跑男状态,只要1s没被看到后就能传送 - if ((g_iTeleCount[client] > g_iTeleportCheckTime || (g_bPickRushMan && g_iTeleCount[client] > 0))) - { - int type = GetInfectedClass(client); - if(type >= 1 && type <= 6){ - if(g_iTeleportIndex == 0) - { - g_fTeleportDistance = g_fSpawnDistanceMin; - } - aTeleportQueue.Push(type); - g_iTeleportIndex += 1; - Debug_Print("<传送队列> %N踢出,进入传送队列,当前 <传送队列> 队列长度:%d 队列索引:%d 当前记录特感总数为:%d , 真实数量为:%d", client, aTeleportQueue.Length, g_iTeleportIndex, g_iTotalSINum, GetCurrentSINum()); - //不再单独处理spitter防止无声口水,已经在canbeteleport处理 - if(g_iSINum[type - 1] > 0) - { - g_iSINum[type - 1] --; - } - else - { - g_iSINum[type - 1] = 0; - } - if(g_iTotalSINum > 0) - { - g_iTotalSINum --; - } - else - { - g_iTotalSINum = 0; - } - KickClient(client, "传送刷特,踢出"); - //Debug_Print("当前 <传送队列> 队列长度:%d 队列索引:%d 当前记录特感总数为:%d , 真实数量为:%d", aTeleportQueue.Length, g_iTeleportIndex, g_iTotalSINum, GetCurrentSINum()); - g_iTeleCount[client] = 0; - } - } - g_iTeleCount[client] += 1; - } - else{ - g_iTeleCount[client] = 0; - } - } - - } - //每1s找一次攻击目标,主要用于检测跑男,正常情况ongameframe会调用找攻击目标 - g_iTargetSurvivor = GetTargetSurvivor(); - return Plugin_Continue; + if (IsInfectedBot(client) && IsClientInGame(client) && IsPlayerAlive(client) && GetEntProp(client, Prop_Send, "m_zombieClass") != ZC_TANK && !IsPinningSomeone(client)) + { + // 防止无声口水 (使用 SIPool 后无此问题) + if (!g_bSIPoolAvailable && IsSpitter(client) && GetGameTime() - g_fSpitterSpitTime[client] < SPIT_INTERVAL) + return false; + + if (GetClosetSurvivorDistance(client) < g_fSpawnDistanceMin) + return false; + + //舌头能力检查 + if (IsAiSmoker(client) && g_bSmokerAvailable && !IsSmokerCanUseAbility(client)) + return false; + + float fPos[3]; + GetClientAbsOrigin(client, fPos); + if (Is_Pos_Ahead(fPos)) + return false; + + return true; + } + + return false; +} + +// 5秒内以1s检测一次,5次没被看到,就可以踢出并加入传送队列 +Action Timer_PositionSi(Handle timer) +{ + if (IsInPause()) + { + Debug_Print("处于暂停状态,停止传送检测"); + return Plugin_Continue; + } + + //每1s找一次跑男或者是否所有全被控 + if (CheckRushManAndAllPinned()) + return Plugin_Continue; + + for (int client = 1; client <= MaxClients; client++) + { + if (CanBeTeleport(client)) + { + float fSelfPos[3] = { 0.0 }; + GetClientEyePosition(client, fSelfPos); + if (!PlayerVisibleToSDK(fSelfPos, true)) + { + // 如果是跑男状态,只要1s没被看到后就能传送 + if ((g_iTeleCount[client] > g_iTeleportCheckTime || (g_bPickRushMan && g_iTeleCount[client] > 0))) + { + int type = GetInfectedClass(client); + if (type >= 1 && type <= 6) + { + if (g_iTeleportIndex == 0) + g_fTeleportDistance = g_fSpawnDistanceMin; + + aTeleportQueue.Push(type); + g_iTeleportIndex += 1; + Debug_Print("<传送队列> %N踢出,进入传送队列,当前 <传送队列> 队列长度:%d 队列索引:%d 当前记录特感总数为:%d , 真实数量为:%d", client, aTeleportQueue.Length, g_iTeleportIndex, g_iTotalSINum, GetCurrentSINum()); + //不再单独处理spitter防止无声口水,已经在canbeteleport处理 + if (g_iSINum[type - 1] > 0) g_iSINum[type - 1]--; + else g_iSINum[type - 1] = 0; + + if (g_iTotalSINum > 0) g_iTotalSINum--; + else g_iTotalSINum = 0; + + // KickClient(client, "传送刷特,踢出"); + if (g_bSIPoolAvailable) g_hSIPool.ReturnSIBot(client); + else KickClient(client, "传送刷特,踢出"); + + Debug_Print("当前 <传送队列> 队列长度:%d 队列索引:%d 当前记录特感总数为:%d , 真实数量为:%d", aTeleportQueue.Length, g_iTeleportIndex, g_iTotalSINum, GetCurrentSINum()); + g_iTeleCount[client] = 0; + } + } + g_iTeleCount[client] += 1; + } + else g_iTeleCount[client] = 0; + } + } + //每1s找一次攻击目标,主要用于检测跑男,正常情况ongameframe会调用找攻击目标 + g_iTargetSurvivor = GetTargetSurvivor(); + return Plugin_Continue; } stock int GetCurrentSINum() { - int sum = 0; - for(int i = 0; i < MaxClients; i++){ - if(IsInfectedBot(i) && IsPlayerAlive(i)) - { - sum ++; - } - } - return sum; + int sum = 0; + for (int i = 0; i < MaxClients; i++) + if (IsInfectedBot(i) && !IsGhost(i)) + sum++; + + return sum; } stock bool IsSpitter(int client) { - if (IsInfectedBot(client) && IsPlayerAlive(client) && GetEntProp(client, Prop_Send, "m_zombieClass") == ZC_SPITTER) - { - return true; - } - else - { - return false; - } + // if (IsInfectedBot(client) && IsPlayerAlive(client) && GetEntProp(client, Prop_Send, "m_zombieClass") == ZC_SPITTER) + // { + // return true; + // } + // else + // { + // return false; + // } + return IsInfectedBot(client) && IsPlayerAlive(client) && (GetEntProp(client, Prop_Send, "m_zombieClass") == ZC_SPITTER); } // 跑男定义为距离所有生还者或者特感超过RushManDistance距离 bool CheckRushManAndAllPinned() { - bool TempRushMan = g_bPickRushMan; - int iSurvivors[8] = {0}, iSurvivorIndex = 0, PinnedNumber = 0; - int iInfecteds[MAXPLAYERS] = {0}, iInfectedIndex = 0; - float fInfectedssOrigin[MAXPLAYERS][3], fSurvivorsOrigin[8][3], OriginTemp[3]; - for (int client = 1; client <= MaxClients; client++) - { - if (IsValidSurvivor(client) && IsPlayerAlive(client)) - { - if(IsPinned(client) || IsClientIncapped(client)){ - PinnedNumber++; - } - GetClientAbsOrigin(client, OriginTemp); - if(iSurvivorIndex < 8) - { - fSurvivorsOrigin[iSurvivorIndex] = OriginTemp; - iSurvivors[iSurvivorIndex++] = client; - } - } - else if(IsInfectedBot(client) && IsPlayerAlive(client)) - { - iInfecteds[iInfectedIndex] = client; - GetClientAbsOrigin(client, OriginTemp); - fInfectedssOrigin[iInfectedIndex++] = OriginTemp; - } - } - if(iSurvivorIndex == 1) - { - //一个人有什么跑男 - return false; - } - int target = L4D_GetHighestFlowSurvivor(); - if (iSurvivorIndex >= 1 && IsValidClient(target)) - { - GetClientAbsOrigin(target, OriginTemp); - bool testSurvior = false; - if(iSurvivorIndex == 1) - { - testSurvior = true; - } - for(int i =0; i < iSurvivorIndex && !testSurvior; i++){ - if(IsPinned(target) || IsClientIncapped(target) || (iSurvivors[i] != target && GetVectorDistance(fSurvivorsOrigin[i], OriginTemp, true) <= Pow(RushManDistance, 2.0))) - { - testSurvior = true; - break; - } - } - if(!testSurvior || g_iTotalSINum < (g_iSiLimit / 2 + 1)) - { - g_bPickRushMan = false; - g_iRushManIndex = -1; - if(TempRushMan != g_bPickRushMan){ - StartForward(g_bPickRushMan); - } - return PinnedNumber == iSurvivorIndex; - } - else - { - for(int i =0; i < iInfectedIndex; i++) - { - if(IsPinned(target) || IsClientIncapped(target) || (GetVectorDistance(fInfectedssOrigin[i], OriginTemp, true) <= Pow(RushManDistance, 2.0) * 1.3)) - { - g_bPickRushMan = false; - g_iRushManIndex = -1; - if(TempRushMan != g_bPickRushMan){ - StartForward(g_bPickRushMan); - } - return PinnedNumber == iSurvivorIndex; - } - } - } - if(!testSurvior) - { - Debug_Print("跑男由于和其他正常生还者过远触发") ; - } - else - { - Debug_Print("跑男由于和特感过远触发") ; - } - g_bPickRushMan = true; - g_iRushManIndex = target; - if(TempRushMan != g_bPickRushMan){ - StartForward(g_bPickRushMan); - } - } - return PinnedNumber == iSurvivorIndex; + bool TempRushMan = g_bPickRushMan; + int iSurvivors[8] = { 0 }, iSurvivorIndex = 0, PinnedNumber = 0; + int iInfecteds[MAXPLAYERS] = { 0 }, iInfectedIndex = 0; + float fInfectedssOrigin[MAXPLAYERS][3], fSurvivorsOrigin[8][3], OriginTemp[3]; + for (int client = 1; client <= MaxClients; client++) + { + if (IsValidSurvivor(client) && IsPlayerAlive(client)) + { + if (IsPinned(client) || IsClientIncapped(client)) + PinnedNumber++; + + GetClientAbsOrigin(client, OriginTemp); + if (iSurvivorIndex < 8) + { + fSurvivorsOrigin[iSurvivorIndex] = OriginTemp; + iSurvivors[iSurvivorIndex++] = client; + } + } + else if (IsInfectedBot(client) && IsPlayerAlive(client)) + { + iInfecteds[iInfectedIndex] = client; + GetClientAbsOrigin(client, OriginTemp); + fInfectedssOrigin[iInfectedIndex++] = OriginTemp; + } + } + + //一个人有什么跑男 + if (iSurvivorIndex == 1) + return false; + + int target = L4D_GetHighestFlowSurvivor(); + if (iSurvivorIndex >= 1 && IsValidClient(target)) + { + GetClientAbsOrigin(target, OriginTemp); + bool testSurvior = false; + if (iSurvivorIndex == 1) + testSurvior = true; + + for (int i = 0; i < iSurvivorIndex && !testSurvior; i++) + if (IsPinned(target) || IsClientIncapped(target) || (iSurvivors[i] != target && GetVectorDistance(fSurvivorsOrigin[i], OriginTemp, true) <= Pow(RushManDistance, 2.0))) + { + testSurvior = true; + break; + } + + if (!testSurvior || g_iTotalSINum < (g_iSiLimit / 2 + 1)) + { + g_bPickRushMan = false; + g_iRushManIndex = -1; + if (TempRushMan != g_bPickRushMan) + StartForward(g_bPickRushMan); + + return PinnedNumber == iSurvivorIndex; + } + else + for (int i = 0; i < iInfectedIndex; i++) + if (IsPinned(target) || IsClientIncapped(target) || (GetVectorDistance(fInfectedssOrigin[i], OriginTemp, true) <= Pow(RushManDistance, 2.0) * 1.3)) + { + g_bPickRushMan = false; + g_iRushManIndex = -1; + if (TempRushMan != g_bPickRushMan) + StartForward(g_bPickRushMan); + + return PinnedNumber == iSurvivorIndex; + } + + if (!testSurvior) + Debug_Print("跑男由于和其他正常生还者过远触发"); + else Debug_Print("跑男由于和特感过远触发"); + + g_bPickRushMan = true; + g_iRushManIndex = target; + if (TempRushMan != g_bPickRushMan) + StartForward(g_bPickRushMan); + } + return PinnedNumber == iSurvivorIndex; } int GetTargetSurvivor() { - //如果有跑男,抓跑男 - if(g_bPickRushMan && IsValidSurvivor(g_iRushManIndex) && IsPlayerAlive(g_iRushManIndex) && !IsPinned(g_iRushManIndex)){ - return g_iRushManIndex; - } - //没有跑男,抓目标未满的生还者 - else - { - int iSurvivors[8] = {0} , iSurvivorIndex = 0; - for (int client = 1; client <= MaxClients; client++) - { - if (IsValidSurvivor(client) && IsPlayerAlive(client) && (!IsPinned(client) || !IsClientIncapped(client))) - { - g_bIsLate = true; - if(g_bTargetSystemAvailable && IsClientReachLimit(client)) - { - continue; - } - if (iSurvivorIndex < 8) - { - iSurvivors[iSurvivorIndex] = client; - iSurvivorIndex += 1; - } - } - } - if (iSurvivorIndex > 0) - { - return iSurvivors[GetRandomInt(0, iSurvivorIndex - 1)]; - } - else{ - return L4D_GetHighestFlowSurvivor(); - } - } -} - -void StartForward(bool IsRush){ - Debug_Print("跑男检测状态变化,发送forward"); - Call_StartForward(g_hRushManNotifyForward);//转发触发 - Call_PushCell(IsRush);//按顺序将参数push进forward传参列表里 - Call_Finish();//转发结束 + //如果有跑男,抓跑男 + if (g_bPickRushMan && IsValidSurvivor(g_iRushManIndex) && IsPlayerAlive(g_iRushManIndex) && !IsPinned(g_iRushManIndex)) + return g_iRushManIndex; + + //没有跑男,抓目标未满的生还者 + int iSurvivors[8] = { 0 }, iSurvivorIndex = 0; + for (int client = 1; client <= MaxClients; client++) + { + if (IsValidSurvivor(client) && IsPlayerAlive(client) && (!IsPinned(client) || !IsClientIncapped(client))) + { + g_bIsLate = true; + if (g_bTargetSystemAvailable && IsClientReachLimit(client)) + continue; + + if (iSurvivorIndex < 8) + { + iSurvivors[iSurvivorIndex] = client; + iSurvivorIndex += 1; + } + } + } + if (iSurvivorIndex > 0) + return iSurvivors[GetRandomInt(0, iSurvivorIndex - 1)]; + + return L4D_GetHighestFlowSurvivor(); +} + +void StartForward(bool IsRush) +{ + Debug_Print("跑男检测状态变化,发送forward"); + Call_StartForward(g_hRushManNotifyForward); //转发触发 + Call_PushCell(IsRush); //按顺序将参数push进forward传参列表里 + Call_Finish(); //转发结束 } stock bool IsAiSmoker(int client) { - if (client && client <= MaxClients && IsClientInGame(client) && IsPlayerAlive(client) && IsFakeClient(client) && GetClientTeam(client) == TEAM_INFECTED && GetEntProp(client, Prop_Send, "m_zombieClass") == 1 && GetEntProp(client, Prop_Send, "m_isGhost") != 1) - { - return true; - } - else - { - return false; - } + // if (client && client <= MaxClients && IsClientInGame(client) && IsPlayerAlive(client) && IsFakeClient(client) && GetClientTeam(client) == TEAM_INFECTED && GetEntProp(client, Prop_Send, "m_zombieClass") == 1 && GetEntProp(client, Prop_Send, "m_isGhost") != 1) + // { + // return true; + // } + // else + // { + // return false; + // } + + return client && client <= MaxClients && IsClientInGame(client) && IsPlayerAlive(client) + && IsFakeClient(client) && (GetClientTeam(client) == TEAM_INFECTED) + && (GetEntProp(client, Prop_Send, "m_zombieClass") == 1) && (GetEntProp(client, Prop_Send, "m_isGhost") != 1); } stock bool IsAiTank(int client) { - if (client && client <= MaxClients && IsClientInGame(client) && IsPlayerAlive(client) && IsFakeClient(client) && GetClientTeam(client) == TEAM_INFECTED && GetEntProp(client, Prop_Send, "m_zombieClass") == 8 && GetEntProp(client, Prop_Send, "m_isGhost") != 1) - { - return true; - } - else - { - return false; - } + // if (client && client <= MaxClients && IsClientInGame(client) && IsPlayerAlive(client) && IsFakeClient(client) && GetClientTeam(client) == TEAM_INFECTED && GetEntProp(client, Prop_Send, "m_zombieClass") == 8 && GetEntProp(client, Prop_Send, "m_isGhost") != 1) + // { + // return true; + // } + // else + // { + // return false; + // } + + return client && client <= MaxClients && IsClientInGame(client) && IsPlayerAlive(client) + && IsFakeClient(client) && (GetClientTeam(client) == TEAM_INFECTED) + && (GetEntProp(client, Prop_Send, "m_zombieClass") == 8) && (GetEntProp(client, Prop_Send, "m_isGhost") != 1); } stock bool IsGhost(int client) @@ -1570,186 +1506,182 @@ stock bool IsGhost(int client) } //获取队列里Hunter和Charger数量 -stock int getArrayHunterAndChargetNum(){ - int count = 0; - for(int i = 0; i < aSpawnQueue.Length; i++){ - int type = aSpawnQueue.Get(i); - if(type == 3 || type == 6){ - count++; - } - } - return count; -} -stock int getArrayDominateSINum(){ - int count = 0; - for(int i = 0; i < aSpawnQueue.Length; i++){ - int type = aSpawnQueue.Get(i); - if(type != 2 || type == 4){ - count++; - } - } - return count; +stock int getArrayHunterAndChargetNum() +{ + int count = 0; + for (int i = 0; i < aSpawnQueue.Length; i++) + { + int type = aSpawnQueue.Get(i); + if (type == 3 || type == 6) + count++; + } + return count; +} + +stock int getArrayDominateSINum() +{ + int count = 0; + for (int i = 0; i < aSpawnQueue.Length; i++) + { + int type = aSpawnQueue.Get(i); + if (type != 2 || type == 4) + count++; + } + return count; } // 返回在场特感数量,根据 z_%s_limit 限制每种特感上限 bool MeetRequire(int iType) { - if(g_hAllChargerMode.BoolValue || g_hAllHunterMode.BoolValue){ - return true; - } - GetSiLimit(); - if (iType == 1) - { - if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0)) - { - return true; - } - } - else if (iType == 2) - { - if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0) && ((getArrayDominateSINum() > (g_iSiLimit/4 +1)) || (g_iQueueIndex >= g_iSiLimit -2))) - { - return true; - } - } - else if (iType == 3) - { - if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0)) - { - return true; - } - } - else if (iType == 4) - { - if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0) && ((getArrayHunterAndChargetNum() > (g_iSiLimit/5 +1) || (g_iQueueIndex >= g_iSiLimit -2)))) - { - return true; - } - } - else if (iType == 5) - { - if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0)) - { - return true; - } - } - else if (iType == 6) - { - if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0)) - { - return true; - } - } - return false; + if (g_hAllChargerMode.BoolValue || g_hAllHunterMode.BoolValue) + return true; + + GetSiLimit(); + if (iType < 1 || iType > 6) return false; + switch (iType) + { + case 2: + if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0) && ((getArrayDominateSINum() > (g_iSiLimit / 4 + 1)) || (g_iQueueIndex >= g_iSiLimit - 2))) + return true; + case 4: + if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0) && ((getArrayHunterAndChargetNum() > (g_iSiLimit / 5 + 1) || (g_iQueueIndex >= g_iSiLimit - 2)))) + return true; + default: + if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0)) + return true; + } + return false; + // if (iType == 1) + // { + // if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0)) + // return true; + // } + // else if (iType == 2) + // { + // if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0) && ((getArrayDominateSINum() > (g_iSiLimit / 4 + 1)) || (g_iQueueIndex >= g_iSiLimit - 2))) + // return true; + // } + // else if (iType == 3) + // { + // if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0)) + // return true; + // } + // else if (iType == 4) + // { + // if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0) && ((getArrayHunterAndChargetNum() > (g_iSiLimit / 5 + 1) || (g_iQueueIndex >= g_iSiLimit - 2)))) + // return true; + // } + // else if (iType == 5) + // { + // if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0)) + // return true; + // } + // else if (iType == 6) + // { + // if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0)) + // return true; + // } + // return false; } // 特感种类限制数组,刷完一波特感时重新读取 Cvar 数值,重置特感种类限制数量 void GetSiLimit() { - g_ArraySIlimit[0] = GetConVarInt(FindConVar("z_smoker_limit")); - g_ArraySIlimit[1] = GetConVarInt(FindConVar("z_boomer_limit")); - g_ArraySIlimit[2] = GetConVarInt(FindConVar("z_hunter_limit")); - g_ArraySIlimit[3] = GetConVarInt(FindConVar("z_spitter_limit")); - g_ArraySIlimit[4] = GetConVarInt(FindConVar("z_jockey_limit")); - g_ArraySIlimit[5] = GetConVarInt(FindConVar("z_charger_limit")); - //删除队列里已有元素 - for(int i = 0; i < aSpawnQueue.Length; i++){ - int type = aSpawnQueue.Get(i); - if(type > 0 && type < 7){ - if(g_ArraySIlimit[type - 1] > 0){ - g_ArraySIlimit[type - 1]--; - } - else - { - g_ArraySIlimit[type - 1] = 0; - } - } - } + g_ArraySIlimit[0] = GetConVarInt(FindConVar("z_smoker_limit")); + g_ArraySIlimit[1] = GetConVarInt(FindConVar("z_boomer_limit")); + g_ArraySIlimit[2] = GetConVarInt(FindConVar("z_hunter_limit")); + g_ArraySIlimit[3] = GetConVarInt(FindConVar("z_spitter_limit")); + g_ArraySIlimit[4] = GetConVarInt(FindConVar("z_jockey_limit")); + g_ArraySIlimit[5] = GetConVarInt(FindConVar("z_charger_limit")); + //删除队列里已有元素 + for (int i = 0; i < aSpawnQueue.Length; i++) + { + int type = aSpawnQueue.Get(i); + if (type > 0 && type < 7) + { + if (g_ArraySIlimit[type - 1] > 0) + g_ArraySIlimit[type - 1]--; + else g_ArraySIlimit[type - 1] = 0; + } + } } // 判断一个坐标是否在当前最高路程的生还者前面 bool Is_Pos_Ahead(float refpos[3], int target = -1) { - int pos_flow = 0, target_flow = 0; - Address pNowNav = L4D2Direct_GetTerrorNavArea(refpos); - if (pNowNav == Address_Null) - { - pNowNav = view_as
(L4D_GetNearestNavArea(refpos, 300.0)); - } - pos_flow = Calculate_Flow(pNowNav); - if(target == -1) - { - target = L4D_GetHighestFlowSurvivor(); - } - if (IsValidSurvivor(target)) - { - float targetpos[3] = {0.0}; - GetClientAbsOrigin(target, targetpos); - Address pTargetNav = L4D2Direct_GetTerrorNavArea(targetpos); - if (pTargetNav == Address_Null) - { - pTargetNav = view_as
(L4D_GetNearestNavArea(refpos, 300.0)); - } - target_flow = Calculate_Flow(pTargetNav); - } - return view_as(pos_flow >= target_flow); + int pos_flow = 0, target_flow = 0; + Address pNowNav = L4D2Direct_GetTerrorNavArea(refpos); + if (pNowNav == Address_Null) + pNowNav = view_as
(L4D_GetNearestNavArea(refpos, 300.0)); + + pos_flow = Calculate_Flow(pNowNav); + if (target == -1) + target = L4D_GetHighestFlowSurvivor(); + + if (IsValidSurvivor(target)) + { + float targetpos[3] = { 0.0 }; + GetClientAbsOrigin(target, targetpos); + Address pTargetNav = L4D2Direct_GetTerrorNavArea(targetpos); + if (pTargetNav == Address_Null) + pTargetNav = view_as
(L4D_GetNearestNavArea(refpos, 300.0)); + + target_flow = Calculate_Flow(pTargetNav); + } + return view_as(pos_flow >= target_flow); } + int Calculate_Flow(Address pNavArea) { - float now_nav_flow = L4D2Direct_GetTerrorNavAreaFlow(pNavArea) / L4D2Direct_GetMapMaxFlowDistance(); - float now_nav_promixity = now_nav_flow + g_hVsBossFlowBuffer.FloatValue / L4D2Direct_GetMapMaxFlowDistance(); - if (now_nav_promixity > 1.0) - { - now_nav_promixity = 1.0; - } - return RoundToNearest(now_nav_promixity * 100.0); + float now_nav_flow = L4D2Direct_GetTerrorNavAreaFlow(pNavArea) / L4D2Direct_GetMapMaxFlowDistance(); + float now_nav_promixity = now_nav_flow + g_hVsBossFlowBuffer.FloatValue / L4D2Direct_GetMapMaxFlowDistance(); + if (now_nav_promixity > 1.0) + now_nav_promixity = 1.0; + + return RoundToNearest(now_nav_promixity * 100.0); } // @key:需要调整的 key 值 // @retVal:原 value 值,使用 return Plugin_Handled 覆盖 public Action L4D_OnGetScriptValueInt(const char[] key, int &retVal) { - if ((strcmp(key, "cm_ShouldHurry", false) == 0) || (strcmp(key, "cm_AggressiveSpecials", false) == 0) && retVal != 1) - { - retVal = 1; - return Plugin_Handled; - } - return Plugin_Continue; + if ((strcmp(key, "cm_ShouldHurry", false) == 0) || (strcmp(key, "cm_AggressiveSpecials", false) == 0) && retVal != 1) + { + retVal = 1; + return Plugin_Handled; + } + return Plugin_Continue; } -stock void Debug_Print(char[] format, any ...) +stock void Debug_Print(char[] format, any...) { - #if (DEBUG) - { - char sTime[32]; - FormatTime(sTime, sizeof(sTime), "%I-%M-%S", GetTime()); - char sBuffer[512]; - VFormat(sBuffer, sizeof(sBuffer), format, 2); - Format(sBuffer, sizeof(sBuffer), "[%s] %s: %s", "DEBUG", sTime, sBuffer); - // PrintToChatAll(sBuffer); - PrintToConsoleAll(sBuffer); - PrintToServer(sBuffer); - LogToFile(sLogFile, sBuffer); - } - #endif +#if (DEBUG) + { + char sTime[32]; + FormatTime(sTime, sizeof(sTime), "%I-%M-%S", GetTime()); + char sBuffer[512]; + VFormat(sBuffer, sizeof(sBuffer), format, 2); + Format(sBuffer, sizeof(sBuffer), "[%s] %s: %s", "DEBUG", sTime, sBuffer); + // PrintToChatAll(sBuffer); + PrintToConsoleAll(sBuffer); + PrintToServer(sBuffer); + LogToFile(sLogFile, sBuffer); + } +#endif } stock bool IsAnyTankOrAboveHalfSurvivorDownOrDied() { - int count = 0; - for(int i = 1; i <= MaxClients; i ++) - { - if(IsAiTank(i)) - return true; - if(IsValidSurvivor(i) && (L4D_IsPlayerIncapacitated(i) || !IsPlayerAlive(i))) - { - count ++; - } - } - if(count >= RoundToCeil(FindConVar("survivor_limit").IntValue / 2.0)) - { - return true; - } - return false; -} + int count = 0; + for (int i = 1; i <= MaxClients; i++) + { + if (IsAiTank(i)) + return true; + if (IsValidSurvivor(i) && (L4D_IsPlayerIncapacitated(i) || !IsPlayerAlive(i))) + count++; + } + if (count >= RoundToCeil(FindConVar("survivor_limit").IntValue / 2.0)) + return true; + return false; +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/AnneHappy/si_pool.sp b/addons/sourcemod/scripting/AnneHappy/si_pool.sp index 0db73939c..2c620cc7b 100644 --- a/addons/sourcemod/scripting/AnneHappy/si_pool.sp +++ b/addons/sourcemod/scripting/AnneHappy/si_pool.sp @@ -1,477 +1,475 @@ -/* - * @Author: 我是派蒙啊 - * @Last Modified by: 我是派蒙啊 - * @Create Date: 2024-02-17 11:15:10 - * @Last Modified time: 2024-02-24 23:03:11 - * @Github: https://github.com/Paimon-Kawaii - */ - -#pragma semicolon 1 -#pragma newdecls required - -#define DEBUG 0 - -#define RECOVER 0 - -#define VERSION "2024.02.24#20" - -#define LIBRARY_NAME "si_pool" -#define GAMEDATA_FILE "si_pool" - -#include - -#include -#include - -#include - -public Plugin myinfo = -{ - name = "Special Infected Bot Client Pool", - author = "我是派蒙啊", - description = "A Client Pool for SI Bots, used to avoid lots of CreateFakeClient() operation", - version = VERSION, - url = "http://github.com/Paimon-Kawaii/L4D2-Plugins" -}; - -#define MAXSIZE MAXPLAYERS + 1 - -static char g_sZombieClass[][] = { - "Smoker", - "Boomer", - "Hunter", - "Spitter", - "Jockey", - "Charger", - "Witch", - "Tank", -}; - -void ResetDeadZombie(int client) -{ - SetStateTransition(client, STATE_ACTIVE); - CreateTimer(0.1, Timer_RestDeadZombie, client, TIMER_FLAG_NO_MAPCHANGE); -} - -Action Timer_RestDeadZombie(Handle timer, int client) -{ - if (!IsValidClient(client)) return Plugin_Stop; - - RespawnPlayer(client); - SetEntProp(client, Prop_Send, "m_isGhost", true); - SetEntProp(client, Prop_Send, "m_lifeState", true); - SetEntProp(client, Prop_Send, "movetype", MOVETYPE_NOCLIP); - -#if DEBUG - LogMessage("[SIPool] Dead SI(%d) reset, is alive: %d", client, IsPlayerAlive(client)); -#endif - - return Plugin_Stop; -} - -#define FSOLID_NOT_STANDABLE 0x10 -void InitializeSpecial(int ent, const float vPos[3] = NULL_VECTOR, const float vAng[3] = NULL_VECTOR, bool bSpawn = false) -{ - ChangeClientTeam(ent, TEAM_INFECTED); - SetEntProp(ent, Prop_Send, "m_usSolidFlags", FSOLID_NOT_STANDABLE); - SetEntProp(ent, Prop_Send, "movetype", MOVETYPE_WALK); - SetEntProp(ent, Prop_Send, "deadflag", false); - SetEntProp(ent, Prop_Send, "m_lifeState", false); - SetEntProp(ent, Prop_Send, "m_iObserverMode", false); - SetEntProp(ent, Prop_Send, "m_iPlayerState", false); - SetEntProp(ent, Prop_Send, "m_zombieState", false); - SetEntProp(ent, Prop_Send, "m_isGhost", false); - if (bSpawn) DispatchSpawn(ent); - TeleportEntity(ent, vPos, vAng, NULL_VECTOR); -} - -Handle - g_hSDK_CTerrorPlayer_SetClass, - g_hSDK_CBaseAbility_CreateForPlayer, - g_hSDK_CCSPlayer_State_Transition, - g_hSDK_CTerrorPlayer_RoundRespawn, - g_hSDK_NextBotCreatePlayerBot_Hunter; - -static SIPool g_hSIPool; -static int g_iPoolSize; -static int g_iPoolArray[MAXSIZE] = { -1, ... }; - -public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) -{ - RegPluginLibrary(LIBRARY_NAME); - g_hSIPool = view_as(myself); - - return APLRes_Success; -} - -public void OnPluginStart() -{ - CreateNatives(); - - HookEvents(); - PrepareSDKCalls(); -} - -public bool OnClientConnect(int client) -{ - if (IsFakeClient(client)) return true; - if (g_iPoolSize && g_iPoolSize + GetClientCount() >= MaxClients) - { - KickClient(g_iPoolArray[g_iPoolSize - 1]); - OnPoolSizeChanged(g_iPoolSize, g_iPoolSize - 1); - g_iPoolSize--; - } - - return true; -} - -public void OnClientDisconnect(int client) -{ - if (IsFakeClient(client)) return; - - OnPoolSizeChanged(g_iPoolSize, g_iPoolSize + 1); - g_iPoolSize++; -} - -#if RECOVER -void RecoverSIPool() -{ - OnPoolSizeChanged(0, g_iPoolSize); -} -#endif - -void CreateNatives() -{ - // CreateNative("SIPool.Instance.get", Native_SIPool_Instance_get); - CreateNative("SIPool.Instance", Native_SIPool_Instance_get); - CreateNative("SIPool.Size.get", Native_SIPool_Size_get); - -#if SIZABLE - CreateNative("SIPool.Narrow", Native_SIPool_Narrow); - CreateNative("SIPool.Expand", Native_SIPool_Expand); - CreateNative("SIPool.Resize", Native_SIPool_Resize); -#endif - - CreateNative("SIPool.RequestSIBot", Native_SIPool_RequestSIBot); - CreateNative("SIPool.ReturnSIBot", Native_SIPool_ReturnSIBot); -} - -any Native_SIPool_Instance_get(Handle plugin, int numParams) -{ - return g_hSIPool; -} - -any Native_SIPool_Size_get(Handle plugin, int numParams) -{ - return g_iPoolSize; -} - -#if SIZABLE -any Native_SIPool_Narrow(Handle plugin, int numParams) -{ - int narrow = GetNativeCell(2); - if (narrow < 1) - { - LogMessage("[SIPool] Narrow size must greater than 1 !"); - return 0; - } - int size = g_iPoolSize - narrow; - - size = size > 0 ? size : 0; - OnPoolSizeChanged(g_iPoolSize, size); - g_iPoolSize = size; - - return 0; -} - -any Native_SIPool_Expand(Handle plugin, int numParams) -{ - int expand = GetNativeCell(2); - if (expand < 1) - { - LogMessage("[SIPool] Expand size must greater than 1 !"); - return 0; - } - - int size = g_iPoolSize + expand; - - if (size > MaxClients) - { - LogMessage("[SIPool] Size too much !"); - return 0; - } - - OnPoolSizeChanged(g_iPoolSize, size); - g_iPoolSize = size; - - return 0; -} - -any Native_SIPool_Resize(Handle plugin, int numParams) -{ - int size = GetNativeCell(2); - if (size < 0) - { - LogMessage("[SIPool] Resize must greater than 0 !"); - return 0; - } - - if (size > MAXSIZE) - { - LogMessage("[SIPool] Size too much ! %d", size); - return 0; - } - - OnPoolSizeChanged(g_iPoolSize, size); - g_iPoolSize = size; - - return 0; -} -#endif - -any Native_SIPool_RequestSIBot(Handle plugin, int numParams) -{ - static bool log = false; - if (g_iPoolSize < 1) - { - if (!log) - { - LogMessage("[SIPool] Pool empty or not sized !"); - LogMessage("[SIPool] SIPool will auto set size to 1 !"); - LogMessage("[SIPool] This log only showed once."); - log = true; - } - OnPoolSizeChanged(g_iPoolSize, g_iPoolSize + 1); - g_iPoolSize++; - } - - int index = 1; - int zclass = GetNativeCell(2); - int bot = g_iPoolArray[g_iPoolSize - index]; - while (!(IsValidClient(bot) && IsFakeClient(bot)) && ++index <= g_iPoolSize) - bot = g_iPoolArray[g_iPoolSize - index]; - if (index > g_iPoolSize && !(IsValidClient(bot) && IsFakeClient(bot))) - { - // LogMessage("[SIPool] No SI available !"); - OnPoolSizeChanged(g_iPoolSize, 0); - g_iPoolSize = 0; - - return -1; - } - - static float origin[3], angle[3]; - bool bPos = !IsNativeParamNullVector(3), bAngle = !IsNativeParamNullVector(4); - if (bPos) GetNativeArray(3, origin, 3); - if (bAngle) GetNativeArray(4, angle, 3); - - if (bPos && bAngle) InitializeSpecial(bot, origin, angle); - else if (bPos) InitializeSpecial(bot, origin); - else if (bAngle) InitializeSpecial(bot, _, angle); - else InitializeSpecial(bot); - SetClass(bot, zclass); - SetClientName(bot, g_sZombieClass[zclass - 1]); - - Event event = CreateEvent("player_spawn"); - if (event != INVALID_HANDLE) - { - event.SetInt("userid", GetClientUserId(bot)); - FireEvent(event); - } - -#if DEBUG - LogMessage("[SIPool] SI request: %d", bot); -#endif - - OnPoolSizeChanged(g_iPoolSize, g_iPoolSize - index); - g_iPoolSize -= index; - - return bot; -} - -any Native_SIPool_ReturnSIBot(Handle plugin, int numParams) -{ - int bot = GetNativeCell(2); - if (!(IsInfected(bot) && IsFakeClient(bot))) - { - LogMessage("[SIPool] SI is not available!"); - return false; - } - - ResetDeadZombie(bot); - g_iPoolArray[g_iPoolSize++] = bot; - - // Return bot dont need to resize... - // OnPoolSizeChanged(g_iPoolSize, g_iPoolSize + 1); - // g_iPoolSize++; - - return true; -} - -void OnPoolSizeChanged(int iOldPoolSize, int iNewPoolSize) -{ - if (GetClientCount(false) >= MaxClients) return; - - bool add; - int idx_min, idx_max; - if (iOldPoolSize < iNewPoolSize) - { - idx_min = iOldPoolSize; - idx_max = iNewPoolSize; - add = true; - } - if (!add) return; - - for (int i = idx_min; i < idx_max; i++) - { - int bot = CreateSIBot(); - if (bot == -1) - { - LogMessage("[SIPool] SI create failed, maybe too many..."); - break; - } - g_iPoolArray[i] = bot; - InitializeSpecial(bot, _, _, true); -#if DEBUG - LogMessage("[SIPool] SI create: %d", bot); -#endif - } -} - -void HookEvents() -{ - HookEvent("player_death", Event_PlayerDeath); -#if RECOVER - HookEvent("round_start", Event_RoundStart); -#endif -} - -void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) -{ - int client = GetClientOfUserId(event.GetInt("userid")); - if (!(IsInfected(client) && IsFakeClient(client))) return; - -#if DEBUG - LogMessage("[SIPool] SI dead: %d", client); -#endif - - // Return bot; - ResetDeadZombie(client); - g_iPoolArray[g_iPoolSize++] = client; -} - -#if RECOVER -void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) -{ - RecoverSIPool(); -} -#endif - -void PrepareSDKCalls() -{ - GameData hGameData = new GameData(GAMEDATA_FILE); - Address pReplaceWithBot = hGameData.GetAddress("NextBotCreatePlayerBot.jumptable"); - if (pReplaceWithBot != Address_Null && LoadFromAddress(pReplaceWithBot, NumberType_Int8) == 0x68) - PrepWindowsCreateBotCalls(pReplaceWithBot); - else - PrepLinuxCreateBotCalls(hGameData); - - StartPrepSDKCall(SDKCall_Player); - if (PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "CTerrorPlayer::SetClass")) - { - PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); - g_hSDK_CTerrorPlayer_SetClass = EndPrepSDKCall(); - if (g_hSDK_CTerrorPlayer_SetClass == null) - LogError("Failed to create SDKCall: \"CTerrorPlayer::SetClass\""); - } - else LogError("Failed to find signature: \"CTerrorPlayer::SetClass\""); - - StartPrepSDKCall(SDKCall_Static); - if (PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "CBaseAbility::CreateForPlayer")) - { - PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer); - PrepSDKCall_SetReturnInfo(SDKType_CBaseEntity, SDKPass_Pointer); - g_hSDK_CBaseAbility_CreateForPlayer = EndPrepSDKCall(); - if (g_hSDK_CBaseAbility_CreateForPlayer == null) - LogError("Failed to create SDKCall: \"CBaseAbility::CreateForPlayer\""); - } - else LogError("Failed to find signature: \"CBaseAbility::CreateForPlayer\""); - - StartPrepSDKCall(SDKCall_Player); - if (PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "CCSPlayer::State_Transition")) - { - PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); - g_hSDK_CCSPlayer_State_Transition = EndPrepSDKCall(); - if (g_hSDK_CCSPlayer_State_Transition == null) - LogError("Failed to create SDKCall: \"CCSPlayer::State_Transition\""); - } - else LogError("Failed to find signature: \"CCSPlayer::State_Transition\""); - - StartPrepSDKCall(SDKCall_Player); - if (PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "CTerrorPlayer::RoundRespawn")) - { - g_hSDK_CTerrorPlayer_RoundRespawn = EndPrepSDKCall(); - if (g_hSDK_CTerrorPlayer_RoundRespawn == null) - LogError("Failed to create SDKCall: \"CTerrorPlayer::RoundRespawn\""); - } - else LogError("Failed to find signature: \"CTerrorPlayer::RoundRespawn\""); - - delete hGameData; -} - -void PrepWindowsCreateBotCalls(Address pBaseAddr) -{ - Address pFuncRefAddr = pBaseAddr + view_as
(6); - int funcRelOffset = LoadFromAddress(pFuncRefAddr, NumberType_Int32); - Address pCallOffsetBase = pBaseAddr + view_as
(10); - Address pNextBotCreatePlayerBotTAddr = pCallOffsetBase + view_as
(funcRelOffset); - - StartPrepSDKCall(SDKCall_Static); - if (!PrepSDKCall_SetAddress(pNextBotCreatePlayerBotTAddr)) - SetFailState("Unable to find NextBotCreatePlayer address in memory."); - PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); - PrepSDKCall_SetReturnInfo(SDKType_CBasePlayer, SDKPass_Pointer); - g_hSDK_NextBotCreatePlayerBot_Hunter = EndPrepSDKCall(); -} - -void PrepLinuxCreateBotCalls(GameData hGameData = null) -{ - StartPrepSDKCall(SDKCall_Static); - if (!PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "NextBotCreatePlayerBot")) - SetFailState("Failed to find signature: %s", "NextBotCreatePlayerBot"); - PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); - PrepSDKCall_SetReturnInfo(SDKType_CBasePlayer, SDKPass_Pointer); - g_hSDK_NextBotCreatePlayerBot_Hunter = EndPrepSDKCall(); -} - -void SetClass(int client, int zombieClass) -{ - int weapon = GetPlayerWeaponSlot(client, 0); - if (weapon != -1) - { - RemovePlayerItem(client, weapon); - RemoveEntity(weapon); - } - - int ability = GetEntPropEnt(client, Prop_Send, "m_customAbility"); - if (ability != -1) RemoveEntity(ability); - - SDKCall(g_hSDK_CTerrorPlayer_SetClass, client, zombieClass); - - ability = SDKCall(g_hSDK_CBaseAbility_CreateForPlayer, client); - if (ability != -1) SetEntPropEnt(client, Prop_Send, "m_customAbility", ability); -} - -void SetStateTransition(int client, int state) -{ - SDKCall(g_hSDK_CCSPlayer_State_Transition, client, state); -} - -void RespawnPlayer(int client) -{ - SDKCall(g_hSDK_CTerrorPlayer_RoundRespawn, client); -} - -int CreateSIBot() -{ - return SDKCall(g_hSDK_NextBotCreatePlayerBot_Hunter, "Hunter"); +/* + * @Author: 我是派蒙啊 + * @Last Modified by: 我是派蒙啊 + * @Create Date: 2024-02-17 11:15:10 + * @Last Modified time: 2024-03-25 21:07:19 + * @Github: https://github.com/Paimon-Kawaii + */ + +#pragma semicolon 1 +#pragma newdecls required + +#define DEBUG 0 +#if DEBUG + #define LOGFILE "addons/sourcemod/logs/si_pool_log.txt" +#endif + +#define VERSION "2024.03.25#93" + +#define LIBRARY_NAME "si_pool" +#define GAMEDATA_FILE "si_pool" + +#include + +#include +#include + +#include + +public Plugin myinfo = +{ + name = "Special Infected Bot Client Pool", + author = "我是派蒙啊", + description = "A Client Pool for SI Bots, used to avoid lots of CreateFakeClient() operation", + version = VERSION, + url = "http://github.com/Paimon-Kawaii/L4D2-Plugins" +}; + +#define MAXSIZE MAXPLAYERS + 1 + +static char g_sZombieClass[][] = { + "Smoker", + "Boomer", + "Hunter", + "Spitter", + "Jockey", + "Charger", + "Witch", + "Tank", +}; + +#define DEAD 1 +void ResetDeadZombie(int client) +{ + SetStateTransition(client, STATE_ACTIVE); + SetEntProp(client, Prop_Send, "m_isGhost", true); + SetEntProp(client, Prop_Send, "deadflag", DEAD); + SetEntProp(client, Prop_Send, "m_lifeState", DEAD); + SetEntProp(client, Prop_Send, "m_iPlayerState", DEAD); + SetEntProp(client, Prop_Send, "m_zombieState", DEAD); + SetEntProp(client, Prop_Send, "m_iObserverMode", DEAD); + SetEntProp(client, Prop_Send, "movetype", MOVETYPE_NOCLIP); +} + +#define ALIVE 0 +#define FSOLID_NOT_STANDABLE 0x10 +void InitializeSpecial(int ent, const float vPos[3] = NULL_VECTOR, const float vAng[3] = NULL_VECTOR, bool bSpawn = false) +{ + if (bSpawn) DispatchSpawn(ent); + else RespawnPlayer(ent); + + if (GetClientTeam(ent) != TEAM_INFECTED) ChangeClientTeam(ent, TEAM_INFECTED); + SetEntProp(ent, Prop_Send, "m_usSolidFlags", FSOLID_NOT_STANDABLE); + SetEntProp(ent, Prop_Send, "movetype", MOVETYPE_WALK); + SetEntProp(ent, Prop_Send, "deadflag", ALIVE); + SetEntProp(ent, Prop_Send, "m_lifeState", ALIVE); + SetEntProp(ent, Prop_Send, "m_iObserverMode", ALIVE); + SetEntProp(ent, Prop_Send, "m_iPlayerState", ALIVE); + SetEntProp(ent, Prop_Send, "m_zombieState", ALIVE); + SetEntProp(ent, Prop_Send, "m_isGhost", false); + TeleportEntity(ent, vPos, vAng, NULL_VECTOR); +} + +#define ZC_COUNT 6 +Handle + // g_hSDK_CTerrorPlayer_SetClass, + g_hSDK_CBaseAbility_CreateForPlayer, + g_hSDK_CCSPlayer_State_Transition, + g_hSDK_CTerrorPlayer_RoundRespawn, + g_hSDK_NextBotCreatePlayerBot[ZC_COUNT]; + +static SIPool g_hSIPool; +static int g_iLastDeadTypeIdx = -1; +static int g_iPoolSize[ZC_COUNT] = { 0, ... }; +static int g_iPoolArray[ZC_COUNT][MAXSIZE] = { + {-1, ...}, + { -1, ...}, + { -1, ...}, + { -1, ...}, + { -1, ...}, + { -1, ...}, +}; + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + RegPluginLibrary(LIBRARY_NAME); + g_hSIPool = view_as(myself); + + return APLRes_Success; +} + +public void OnPluginStart() +{ + CreateNatives(); + + HookEvents(); + PrepareSDKCalls(); +} + +public bool OnClientConnect(int client) +{ + if (IsFakeClient(client)) return true; + if (g_iLastDeadTypeIdx == -1) return true; + + int size = g_iPoolSize[g_iLastDeadTypeIdx]; + if (size > 0) + { + KickClient(g_iPoolArray[g_iLastDeadTypeIdx][size - 1]); + OnPoolSizeChanged(size, size - 1, g_iLastDeadTypeIdx); + g_iPoolSize[g_iLastDeadTypeIdx]--; + } + + return true; +} + +void CreateNatives() +{ + CreateNative("SIPool.Instance", Native_SIPool_Instance_get); + + CreateNative("SIPool.RequestSIBot", Native_SIPool_RequestSIBot); + CreateNative("SIPool.ReturnSIBot", Native_SIPool_ReturnSIBot); +} + +any Native_SIPool_Instance_get(Handle plugin, int numParams) +{ + return g_hSIPool; +} + +any Native_SIPool_RequestSIBot(Handle plugin, int numParams) +{ + static bool log = false; + int zclass_idx = GetNativeCell(2) - 1; + int size = g_iPoolSize[zclass_idx]; + if (size < 1) + { + if (!log) + { + LogMessage("[SIPool] Pool empty or not sized !"); + LogMessage("[SIPool] SIPool will auto set size to 1 !"); + LogMessage("[SIPool] This log only showed once."); + log = true; + } + OnPoolSizeChanged(0, 1, zclass_idx); + g_iPoolSize[zclass_idx] = 1; + } + + int index = 1; + size = g_iPoolSize[zclass_idx]; + int bot = g_iPoolArray[zclass_idx][size - index]; + while (!(IsValidClient(bot) && IsFakeClient(bot) && IsGhost(bot)) && ++index <= size) + bot = g_iPoolArray[zclass_idx][size - index]; + if (index > size && !(IsValidClient(bot) && IsFakeClient(bot) && IsGhost(bot))) + { + LogMessage("[SIPool] No SI available !"); + OnPoolSizeChanged(size, 0, zclass_idx); + g_iPoolSize[zclass_idx] = 0; + + return -1; + } + + static float origin[3], angle[3]; + bool bPos = !IsNativeParamNullVector(3), bAngle = !IsNativeParamNullVector(4); + if (bPos) GetNativeArray(3, origin, 3); + if (bAngle) GetNativeArray(4, angle, 3); + + if (bPos && bAngle) InitializeSpecial(bot, origin, angle); + else if (bPos) InitializeSpecial(bot, origin); + else if (bAngle) InitializeSpecial(bot, _, angle); + else InitializeSpecial(bot); + // SetClass(bot, zclass_idx + 1); + SetClientName(bot, g_sZombieClass[zclass_idx]); + + Event event = CreateEvent("player_spawn", true); + if (event != INVALID_HANDLE) + { + event.SetInt("userid", GetClientUserId(bot)); + event.Fire(); + } + + OnPoolSizeChanged(size, size - index, zclass_idx); + g_iPoolSize[zclass_idx] -= index; + +#if DEBUG + LogToFile(LOGFILE, "[SIPool] SI request: %d, type: %d", bot, zclass_idx + 1); +#endif + + return bot; +} + +any Native_SIPool_ReturnSIBot(Handle plugin, int numParams) +{ + int bot = GetNativeCell(2); + if (!(IsInfected(bot) && IsFakeClient(bot))) + { + LogMessage("[SIPool] SI is not available!"); + return false; + } + + // g_iPoolArray[GetZombieClass(bot) - 1][g_iPoolSize[GetZombieClass(bot) - 1]++] = bot; + // ResetDeadZombie(bot); + ForcePlayerSuicide(bot); + + // Return bot dont need to resize... + // OnPoolSizeChanged(g_iPoolSize, g_iPoolSize + 1); + // g_iPoolSize++; + + return true; +} + +void OnPoolSizeChanged(int iOldPoolSize, int iNewPoolSize, int zclass_idx) +{ + if (GetClientCount(false) >= MaxClients) return; + +#if DEBUG + LogToFile(LOGFILE, "[SIPool] SI size change: %d -> %d of %d pool", iOldPoolSize, iNewPoolSize, zclass_idx); +#endif + + bool add; + int idx_min, idx_max; + if (iOldPoolSize < iNewPoolSize) + { + idx_min = iOldPoolSize; + idx_max = iNewPoolSize; + add = true; + } + if (!add) return; + + for (int i = idx_min; i < idx_max; i++) + { + int bot = CreateSIBot(zclass_idx); + if (bot == -1) + { + int max_count_class = 0; + for (int v = 0, count = 0; v < ZC_COUNT; v++) + if (count < g_iPoolSize[v]) + { + count = g_iPoolSize[v]; + max_count_class = v; + } + KickClient(g_iPoolArray[max_count_class][g_iPoolSize[max_count_class]--], "Kicked because client full."); + + bot = CreateSIBot(zclass_idx); + if (bot == -1) + { + LogMessage("[SIPool] SI create failed for the unknow reason ?!"); + break; + } + } + g_iPoolArray[zclass_idx][i] = bot; + InitializeSpecial(bot, _, _, true); + ResetDeadZombie(bot); +#if DEBUG + LogToFile(LOGFILE, "[SIPool] SI create: %d", bot); +#endif + } +} + +void HookEvents() +{ + HookEvent("player_death", Event_PlayerDeath); + HookEvent("round_start", Event_RoundStart); +} + +void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) +{ + int client = GetClientOfUserId(event.GetInt("userid")); + if (!(IsInfected(client) && IsFakeClient(client))) return; + +#if DEBUG + LogToFile(LOGFILE, "[SIPool] SI dead: %d", client); +#endif + + // Return bot; + g_iLastDeadTypeIdx = GetZombieClass(client) - 1; + g_iPoolArray[g_iLastDeadTypeIdx][g_iPoolSize[g_iLastDeadTypeIdx]++] = client; + ResetDeadZombie(client); +} + +void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) +{ + for (int i = 0; i < ZC_COUNT; i++) + { + g_iPoolSize[i] = 0; + for (int v = 0; v < MAXSIZE; v++) + g_iPoolArray[i][v] = -1; + } + g_iLastDeadTypeIdx = -1; +} + +void PrepareSDKCalls() +{ + GameData hGameData = new GameData(GAMEDATA_FILE); + Address pReplaceWithBot = hGameData.GetAddress("NextBotCreatePlayerBot.jumptable"); + if (pReplaceWithBot != Address_Null && LoadFromAddress(pReplaceWithBot, NumberType_Int8) == 0x68) + PrepWindowsCreateBotCalls(pReplaceWithBot); + else + PrepLinuxCreateBotCalls(hGameData); + + // StartPrepSDKCall(SDKCall_Player); + // if (PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "CTerrorPlayer::SetClass")) + // { + // PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); + // g_hSDK_CTerrorPlayer_SetClass = EndPrepSDKCall(); + // if (g_hSDK_CTerrorPlayer_SetClass == null) + // LogError("Failed to create SDKCall: \"CTerrorPlayer::SetClass\""); + // } + // else LogError("Failed to find signature: \"CTerrorPlayer::SetClass\""); + + StartPrepSDKCall(SDKCall_Static); + if (PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "CBaseAbility::CreateForPlayer")) + { + PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer); + PrepSDKCall_SetReturnInfo(SDKType_CBaseEntity, SDKPass_Pointer); + g_hSDK_CBaseAbility_CreateForPlayer = EndPrepSDKCall(); + if (g_hSDK_CBaseAbility_CreateForPlayer == null) + LogError("Failed to create SDKCall: \"CBaseAbility::CreateForPlayer\""); + } + else LogError("Failed to find signature: \"CBaseAbility::CreateForPlayer\""); + + StartPrepSDKCall(SDKCall_Player); + if (PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "CCSPlayer::State_Transition")) + { + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); + g_hSDK_CCSPlayer_State_Transition = EndPrepSDKCall(); + if (g_hSDK_CCSPlayer_State_Transition == null) + LogError("Failed to create SDKCall: \"CCSPlayer::State_Transition\""); + } + else LogError("Failed to find signature: \"CCSPlayer::State_Transition\""); + + StartPrepSDKCall(SDKCall_Player); + if (PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "CTerrorPlayer::RoundRespawn")) + { + g_hSDK_CTerrorPlayer_RoundRespawn = EndPrepSDKCall(); + if (g_hSDK_CTerrorPlayer_RoundRespawn == null) + LogError("Failed to create SDKCall: \"CTerrorPlayer::RoundRespawn\""); + } + else LogError("Failed to find signature: \"CTerrorPlayer::RoundRespawn\""); + + delete hGameData; +} + +// #define HUNTER_ADDR 0 +// #define JOCKEY_ADDR 12 +// #define SPITTER_ADDR 24 +// #define CHARGER_ADDR 36 +// #define SMOKER_ADDR 48 +// #define BOOMER_ADDR 60 +// #define TANK_ADDR 72 +static int g_iZombieAddr[ZC_COUNT] = { + 48, 60, 0, 24, 12, 36 +}; +void PrepWindowsCreateBotCalls(Address pBaseAddr) +{ +#if DEBUG + TestName(pBaseAddr); +#endif + for (int i = 0; i < ZC_COUNT; i++) + { + Address pJumpAddr = pBaseAddr + view_as
(g_iZombieAddr[i]); + Address pFuncRefAddr = pJumpAddr + view_as
(6); + int funcRelOffset = LoadFromAddress(pFuncRefAddr, NumberType_Int32); + Address pCallOffsetBase = pJumpAddr + view_as
(10); + Address pNextBotCreatePlayerBotTAddr = pCallOffsetBase + view_as
(funcRelOffset); + + StartPrepSDKCall(SDKCall_Static); + if (!PrepSDKCall_SetAddress(pNextBotCreatePlayerBotTAddr)) + SetFailState("Unable to find NextBotCreatePlayer address in memory."); + PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); + PrepSDKCall_SetReturnInfo(SDKType_CBasePlayer, SDKPass_Pointer); + g_hSDK_NextBotCreatePlayerBot[i] = EndPrepSDKCall(); + } +} + +void PrepLinuxCreateBotCalls(GameData hGameData = null) +{ + static char signature_name[32]; + for (int i = 0; i < ZC_COUNT; i++) + { + Format(signature_name, sizeof(signature_name), "NextBotCreatePlayerBot<%s>", g_sZombieClass[i]); + StartPrepSDKCall(SDKCall_Static); + if (!PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, signature_name)) + SetFailState("Failed to find signature: %s", signature_name); + PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); + PrepSDKCall_SetReturnInfo(SDKType_CBasePlayer, SDKPass_Pointer); + g_hSDK_NextBotCreatePlayerBot[i] = EndPrepSDKCall(); + } +} + +#if DEBUG +void TestName(Address pBaseAddr) +{ + for (int i = 0; i < 7; i++) + { + Address pCaseBase = pBaseAddr + view_as
(i * 12); + Address pSIStringAddr = view_as
(LoadFromAddress(pCaseBase + view_as
(1), NumberType_Int32)); + static char SIName[32]; + LoadStringFromAddress(pSIStringAddr, SIName, sizeof(SIName)); + LogToFile(LOGFILE, "[SIPool] Found \"%s\"(%d) in memory.", SIName, i); + } +} + +void LoadStringFromAddress(Address pAddr, char[] buffer, int maxlength) +{ + int i; + char val; + while (i < maxlength) + { + val = LoadFromAddress(pAddr + view_as
(i), NumberType_Int8); + if (val == 0) + { + buffer[i] = '\0'; + break; + } + buffer[i++] = val; + } + buffer[maxlength - 1] = '\0'; +} +#endif + +// #define ABILITY_TRYTIMES 3 +// void SetClass(int client, int zombieClass) +// { +// int weapon = GetPlayerWeaponSlot(client, 0); +// if (weapon != -1) +// { +// RemovePlayerItem(client, weapon); +// RemoveEntity(weapon); +// } + +// int ability = GetEntPropEnt(client, Prop_Send, "m_customAbility"); +// if (ability != -1) RemoveEntity(ability); +// ability = -1; + +// SDKCall(g_hSDK_CTerrorPlayer_SetClass, client, zombieClass); + +// for (int count = 0; count < ABILITY_TRYTIMES && ability == -1; count++) +// ability = SDKCall(g_hSDK_CBaseAbility_CreateForPlayer, client); + +// if (ability != -1) SetEntPropEnt(client, Prop_Send, "m_customAbility", ability); +// else LogMessage("[SIPool] Failed to create ability for %N after %d times tried.", client, ABILITY_TRYTIMES); +// } + +void SetStateTransition(int client, int state) +{ + SDKCall(g_hSDK_CCSPlayer_State_Transition, client, state); +} + +void RespawnPlayer(int client) +{ + SDKCall(g_hSDK_CTerrorPlayer_RoundRespawn, client); +} + +int CreateSIBot(int zclass_idx) +{ + return SDKCall(g_hSDK_NextBotCreatePlayerBot[zclass_idx], g_sZombieClass[zclass_idx]); } \ No newline at end of file From ca3a77f92707e29b16417de60a84a772b2f9b698 Mon Sep 17 00:00:00 2001 From: Paimon <77665174+Paimon-Kawaii@users.noreply.github.com> Date: Mon, 25 Mar 2024 22:37:41 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E6=9B=B4=E6=96=B0SIPool=E5=A4=B4=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新SIPool头文件 --- .../sourcemod/scripting/include/si_pool.inc | 175 +++++++++--------- 1 file changed, 89 insertions(+), 86 deletions(-) diff --git a/addons/sourcemod/scripting/include/si_pool.inc b/addons/sourcemod/scripting/include/si_pool.inc index b1cfb4f35..2b05baf95 100644 --- a/addons/sourcemod/scripting/include/si_pool.inc +++ b/addons/sourcemod/scripting/include/si_pool.inc @@ -1,87 +1,90 @@ -/* - * @Author: 我是派蒙啊 - * @Last Modified by: 我是派蒙啊 - * @Create Date: 2024-02-17 18:11:22 - * @Last Modified time: 2024-02-20 21:33:55 - * @Github: https://github.com/Paimon-Kawaii - */ - -#if defined _sipool_included_ - #endinput -#endif -#define _sipool_included_ - -public SharedPlugin __pl_sipool = { - name = "si_pool", - file = "si_pool.smx", -#if defined REQUIRE_PLUGIN - required = 1, -#else - required = 0, -#endif -}; - -#define SIZABLE 0 - -public void __pl_sipool_SetNTVOptional() -{ - MarkNativeAsOptional("SIPool.Instance"); - MarkNativeAsOptional("SIPool.Size.get"); - -#if SIZABLE - MarkNativeAsOptional("SIPool.Narrow"); - MarkNativeAsOptional("SIPool.Expand"); - MarkNativeAsOptional("SIPool.Resize"); -#endif - - MarkNativeAsOptional("SIPool.RequestSIBot"); - MarkNativeAsOptional("SIPool.ReturnSIBot"); -} - -// Provided by "BHaType": -// For the "L4D_State_Transition" native -// X -> Y (means X state will become Y state on next frame or some seconds later) -#define STATE_ACTIVE 0 -#define STATE_WELCOME 1 // -> STATE_PICKING_TEAM -#define STATE_PICKING_TEAM 2 -#define STATE_PICKINGCLASS 3 // -> STATE_ACTIVE -#define STATE_DEATH_ANIM 4 // -> STATE_DEATH_WAIT_FOR_KEY -#define STATE_DEATH_WAIT_FOR_KEY 5 // -> STATE_OBSERVER_MODE -#define STATE_OBSERVER_MODE 6 -#define STATE_WAITING_FOR_RESCUE 7 -#define STATE_GHOST 8 -#define STATE_INTRO_CAMERA 9 - -methodmap SIPool -{ - public static native SIPool Instance(); - - property int Size { - public native get(); - } - -#if SIZABLE - public native void Narrow(int size); - public native void Expand(int size); - public native void Resize(int size); -#endif - - /** - * Request a Special infected Bot from SIPool - * - * @param zclass Zombie class - * @param origin Position to spawn - * @param angle Angle that zombie look at - * @return Client index - */ - public native int RequestSIBot(int zclass, const float origin[3] = NULL_VECTOR, const float angle[3] = NULL_VECTOR); - - /** - * Return a Special infected Bot to SIPool - * Bot will automatically return after dead - * - * @param client Zombie Bot to return - * @return True is success, false otherwise - */ - public native bool ReturnSIBot(int client); +/* + * @Author: 我是派蒙啊 + * @Last Modified by: 我是派蒙啊 + * @Create Date: 2024-02-17 18:11:22 + * @Last Modified time: 2024-03-25 18:59:48 + * @Github: https://github.com/Paimon-Kawaii + */ + +#if defined _sipool_included_ + #endinput +#endif +#define _sipool_included_ + +public SharedPlugin __pl_sipool = { + name = "si_pool", + file = "si_pool.smx", +#if defined REQUIRE_PLUGIN + required = 1, +#else + required = 0, +#endif +}; + +// #define SIZABLE 0 + +public void __pl_sipool_SetNTVOptional() +{ + // MarkNativeAsOptional("SIPool.SIPool"); + MarkNativeAsOptional("SIPool.Instance"); + // MarkNativeAsOptional("SIPool.Size.get"); + +// #if SIZABLE +// MarkNativeAsOptional("SIPool.Narrow"); +// MarkNativeAsOptional("SIPool.Expand"); +// MarkNativeAsOptional("SIPool.Resize"); +// #endif + + MarkNativeAsOptional("SIPool.RequestSIBot"); + MarkNativeAsOptional("SIPool.ReturnSIBot"); +} + +// Provided by "BHaType": +// For the "L4D_State_Transition" native +// X -> Y (means X state will become Y state on next frame or some seconds later) +#define STATE_ACTIVE 0 +#define STATE_WELCOME 1 // -> STATE_PICKING_TEAM +#define STATE_PICKING_TEAM 2 +#define STATE_PICKINGCLASS 3 // -> STATE_ACTIVE +#define STATE_DEATH_ANIM 4 // -> STATE_DEATH_WAIT_FOR_KEY +#define STATE_DEATH_WAIT_FOR_KEY 5 // -> STATE_OBSERVER_MODE +#define STATE_OBSERVER_MODE 6 +#define STATE_WAITING_FOR_RESCUE 7 +#define STATE_GHOST 8 +#define STATE_INTRO_CAMERA 9 + +methodmap SIPool __nullable__ +{ + // public native SIPool(); + + public static native SIPool Instance(); + + // property int Size { + // public native get(); + // } + +// #if SIZABLE +// public native void Narrow(int size); +// public native void Expand(int size); +// public native void Resize(int size); +// #endif + + /** + * Request a Special infected Bot from SIPool + * + * @param zclass Zombie class + * @param origin Position to spawn + * @param angle Angle that zombie look at + * @return Client index + */ + public native int RequestSIBot(int zclass, const float origin[3] = NULL_VECTOR, const float angle[3] = NULL_VECTOR); + + /** + * Return a Special infected Bot to SIPool + * Bot will automatically return after dead + * + * @param client Zombie Bot to return + * @return True is success, false otherwise + */ + public native bool ReturnSIBot(int client); } \ No newline at end of file From 611bc361ec6922aa810d4bd8f2faa25c48938965 Mon Sep 17 00:00:00 2001 From: Paimon <77665174+Paimon-Kawaii@users.noreply.github.com> Date: Mon, 25 Mar 2024 23:46:54 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=88=A4=E6=96=AD?= =?UTF-8?q?=E8=B8=A2=E5=87=BA=E5=8F=A3=E6=B0=B4=E6=97=B6=E7=9A=84=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修正判断踢出口水时的逻辑错误 --- .../optional/AnneHappy/infected_control.smx | Bin 27267 -> 27241 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/addons/sourcemod/plugins/optional/AnneHappy/infected_control.smx b/addons/sourcemod/plugins/optional/AnneHappy/infected_control.smx index 8b201fbdfbcb49eadcb0613f36d48aa08db90c47..bb30cf44fdcdd0faa2d5b4a107ba169f5135593d 100644 GIT binary patch literal 27241 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 P`Cq}9D8M_4XVw1$ZQ+Jr literal 27267 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 KmEm8kPIW4}+EwBJ From 58c3bfc0bb2fc9bec8c8524364ae60844765fab5 Mon Sep 17 00:00:00 2001 From: Paimon <77665174+Paimon-Kawaii@users.noreply.github.com> Date: Mon, 25 Mar 2024 23:47:55 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E6=9B=B4=E6=96=B0InfectedControl=E6=BA=90?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修正踢出口水时的逻辑错误 --- addons/sourcemod/scripting/AnneHappy/infected_control.sp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sourcemod/scripting/AnneHappy/infected_control.sp b/addons/sourcemod/scripting/AnneHappy/infected_control.sp index c1bdfadf2..f8c66cc71 100644 --- a/addons/sourcemod/scripting/AnneHappy/infected_control.sp +++ b/addons/sourcemod/scripting/AnneHappy/infected_control.sp @@ -459,7 +459,7 @@ void evt_PlayerDeath(Event event, const char[] name, bool dontBroadcast) int type = GetEntProp(client, Prop_Send, "m_zombieClass"); //防止无声口水 - if (type != ZC_SPITTER && !g_bSIPoolAvailable) // 使用SIPool后不会出现无声口水 + if (type != ZC_SPITTER || g_bSIPoolAvailable) // 使用SIPool后不会出现无声口水 CreateTimer(0.5, Timer_KickBot, client); if (type >= 1 && type <= 6)