From 1c6d91b8182b83d991f6efb56254ed2c91eb4999 Mon Sep 17 00:00:00 2001 From: Chris Lynch Date: Sun, 29 Jun 2014 07:05:54 -0400 Subject: [PATCH] Pushing the new version of the in game plug-in that has the CS:GO Protobuf changes. Thanks to Popoklopsi for making the change and sharing it with the community. https://forums.alliedmods.net/showpost.php?p=1879655&postcount=10 Also adding the source for all the super logs plugins so it is all in one place. The complied version in the scripting folder are built with sourcemod 1.5.3 on windows.Pushing the new version of the in game plug-in that has the CS:GO Protobuf changes. Thanks to Popoklopsi for making the change and sharing it with the community. https://forums.alliedmods.net/showpost.php?p=1879655&postcount=10 Also adding the source for all the super logs plugins so it is all in one place. The complied version in the scripting folder are built with sourcemod 1.5.3 on windows.init push --- sourcemod/plugins/hlstatsx.smx | Bin 26276 -> 28285 bytes sourcemod/plugins/superlogs-aoc.smx | Bin 0 -> 7711 bytes sourcemod/plugins/superlogs-css.smx | Bin 0 -> 15214 bytes sourcemod/plugins/superlogs-ddd.smx | Bin 0 -> 18564 bytes sourcemod/plugins/superlogs-dods.smx | Bin 0 -> 14302 bytes sourcemod/plugins/superlogs-generic.smx | Bin 0 -> 4306 bytes sourcemod/plugins/superlogs-ges.smx | Bin 0 -> 13909 bytes sourcemod/plugins/superlogs-hl2mp.smx | Bin 0 -> 11959 bytes sourcemod/plugins/superlogs-ins.smx | Bin 0 -> 14590 bytes sourcemod/plugins/superlogs-l4d.smx | Bin 0 -> 17914 bytes sourcemod/plugins/superlogs-neotokyo.smx | Bin 0 -> 5335 bytes sourcemod/plugins/superlogs-nucleardawn.smx | Bin 0 -> 15908 bytes sourcemod/plugins/superlogs-pvkii.smx | Bin 0 -> 11557 bytes sourcemod/plugins/superlogs-tf2.smx | Bin 0 -> 30056 bytes sourcemod/plugins/superlogs-zps.smx | Bin 0 -> 11908 bytes sourcemod/scripting/hlstatsx.sp | 117 +- sourcemod/scripting/include/wstatshelper.inc | 138 ++ sourcemod/scripting/superlogs-aoc.sp | 270 ++++ sourcemod/scripting/superlogs-cspromod.sp | 116 ++ sourcemod/scripting/superlogs-css.sp | 471 ++++++ sourcemod/scripting/superlogs-ddd.sp | 444 +++++ sourcemod/scripting/superlogs-dods.sp | 410 +++++ sourcemod/scripting/superlogs-generic.sp | 89 ++ sourcemod/scripting/superlogs-ges.sp | 478 ++++++ sourcemod/scripting/superlogs-hl2mp.sp | 402 +++++ sourcemod/scripting/superlogs-ins.sp | 508 ++++++ sourcemod/scripting/superlogs-l4d.sp | 686 ++++++++ sourcemod/scripting/superlogs-neotokyo.sp | 122 ++ sourcemod/scripting/superlogs-nucleardawn.sp | 445 ++++++ sourcemod/scripting/superlogs-pvkii.sp | 326 ++++ sourcemod/scripting/superlogs-tf2.sp | 1512 ++++++++++++++++++ sourcemod/scripting/superlogs-zps.sp | 325 ++++ 32 files changed, 6833 insertions(+), 26 deletions(-) create mode 100644 sourcemod/plugins/superlogs-aoc.smx create mode 100644 sourcemod/plugins/superlogs-css.smx create mode 100644 sourcemod/plugins/superlogs-ddd.smx create mode 100644 sourcemod/plugins/superlogs-dods.smx create mode 100644 sourcemod/plugins/superlogs-generic.smx create mode 100644 sourcemod/plugins/superlogs-ges.smx create mode 100644 sourcemod/plugins/superlogs-hl2mp.smx create mode 100644 sourcemod/plugins/superlogs-ins.smx create mode 100644 sourcemod/plugins/superlogs-l4d.smx create mode 100644 sourcemod/plugins/superlogs-neotokyo.smx create mode 100644 sourcemod/plugins/superlogs-nucleardawn.smx create mode 100644 sourcemod/plugins/superlogs-pvkii.smx create mode 100644 sourcemod/plugins/superlogs-tf2.smx create mode 100644 sourcemod/plugins/superlogs-zps.smx create mode 100644 sourcemod/scripting/include/wstatshelper.inc create mode 100644 sourcemod/scripting/superlogs-aoc.sp create mode 100644 sourcemod/scripting/superlogs-cspromod.sp create mode 100644 sourcemod/scripting/superlogs-css.sp create mode 100644 sourcemod/scripting/superlogs-ddd.sp create mode 100644 sourcemod/scripting/superlogs-dods.sp create mode 100644 sourcemod/scripting/superlogs-generic.sp create mode 100644 sourcemod/scripting/superlogs-ges.sp create mode 100644 sourcemod/scripting/superlogs-hl2mp.sp create mode 100644 sourcemod/scripting/superlogs-ins.sp create mode 100644 sourcemod/scripting/superlogs-l4d.sp create mode 100644 sourcemod/scripting/superlogs-neotokyo.sp create mode 100644 sourcemod/scripting/superlogs-nucleardawn.sp create mode 100644 sourcemod/scripting/superlogs-pvkii.sp create mode 100644 sourcemod/scripting/superlogs-tf2.sp create mode 100644 sourcemod/scripting/superlogs-zps.sp diff --git a/sourcemod/plugins/hlstatsx.smx b/sourcemod/plugins/hlstatsx.smx index 68c693ec89e3a7fbfb9c26015bcd22dad8e53a65..7e9a165531c6a3ded912b6db2d7ef71b16cd8ca4 100644 GIT binary patch literal 28285 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 QF%>nAY1{w{liDl(2ZtGsGynhq literal 26276 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 literal 0 HcmV?d00001 diff --git a/sourcemod/plugins/superlogs-css.smx b/sourcemod/plugins/superlogs-css.smx new file mode 100644 index 0000000000000000000000000000000000000000..be027d7c8ef1df18fe6f856b9c37825efc6375fc GIT binary patch literal 15214 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 literal 0 HcmV?d00001 diff --git a/sourcemod/plugins/superlogs-ddd.smx b/sourcemod/plugins/superlogs-ddd.smx new file mode 100644 index 0000000000000000000000000000000000000000..5d5d6d2769d2a9c580eaa6e9be60ceb6e8ccebde GIT binary patch literal 18564 zcmeIa`#;nF8$Yh2j+71(CLO30A!oBnAwoHyMkVLN(#&Bq>qv5{mr7xlRCAW|VMaw% z%F1b&8J5%Lki(2IzE98X{r&t0pPxQIY`>vO;(1h^>$ zY!G<`JSqaWdf@u@1`(0dz_ohN1`+QqA|hvj>(uZKB7vJkM9u@(TX7piw1LMf!0kM6 z{d7C94siYF$p(=_5+WkEfa~eB4I;aM$3Wn=4!=RfQcOfd*&T7qLqz%3jo=$1%C}KB z{e9e#;6=!dJHSQYjbNV;58xvBh8OrWVC~7Rn_mCtowX;PKK}oE-QOqhf0xMcfSU+^ z;pcqmTeo!q{l)rQnwiBjJW9Y!9?z9VmQ)qOksa}SmwyBnlrtFP$AKT%G#Rj2xt zqnyi8>VbKDGGR6?Ded9Lo=IamyKIVxorQ+C=uk#y=Y-?zU34AjPidg?m*G_u%`$CPa)j6V(h z$+}&u`%Y!*X>GM3?}OOb2H93_sEORfQ^>^ANVn*@ThVi(dhcMp^8B#qxg^ zlnFPa!V>rxqg@{R#*pV7JqNt$4rV9EdYTSI))=BQ40+$h#(-1{Lrh>D^7OVg1MKDK z;1WX~OKeP9?;QeIv(OM-Y0l%%7q==M`mLr6&n`e|6F7iYRW z38Ed^mG5Xj3h`69f_c8Egahs`aEaI}BQzh5!H)cB(P0)=A)}GU zD9-FDx8$5I(4Q^fcT`tgNGMgBz&>aAlU#gBp#DFQi;95;xn!R}bp zY{{GExVD|~cvxr#d}z#K#C(_G|+e8ZLb$&K_9#;VC*3fQPlomNyW`bH-7`NVvG znsHm;9uE>$k2>o@N>B5fj9H8iDSoC_#U(Kc2!T`%3)vEis9(BFbDjt-{)$M?J*`|y z)#DgYY9YN%FF#DX1|&}(uB`E>OCR~jP^Y_caLsV+8zjWOe2!p23N4t<)pZ$}VBu=X zh+M8Se55LI_I4dV{2n>rMT8yACx>6?(luJLs`Xrlb+V+XZ~i&lb5^Hx;_n4=A0~>^ zqUy-}bYUW+GhTUx;3f%;JxIMYbBg-)b;L}0V#NbWWwYYY%=ggBm6x<74uP@4!~ESr zbz_&mepy`Rn)`YrEZ;MoS(XyMpUS$hsJuz$F!0)0s>Rt~kV+>_dYd@ZS8z3>xD zbefvfi;b1Wj%<+RQc&f<`a@ShsQMUT0y zb$OB3iMevxb%uoSpS1?7g`L+{2HQp&z1T`5I5rm@f-A|f!#|uA{UP41Laykp| zrsglzH|oAHPD=`o#ZMX}ZiiDgwd-;nN_>3W^SVi;L3QK$iD*o;T6TnFH;33_e3$8i z-T&ORCv5+_Noe&%){nAIgFZ~MMmlweO8dy-bAFAddFt$V>34Q}WocfJnD4w!B||y! z&jxE=6U4PHi^enEF5)IQ^$iBX|OGVB6xZ%G! zjoRTqA{G9|B{jY5rtG)JFKX)=NLI6UP;dpl;VVd6W-YHTS2vN~^b#NLf>Wk6zAOz_ z@m%$ z_L1wzLVKb=`1SfQS2VopAXCn55zum1EpEYB1er(mB@FqsxO}LfEoIGrm^qbS1Vg%5 z(CmCB*|S9&@$8cF;kj~cmR}8*I1@yDJ#dm4RW;E(C$QKeVVe6$4zxE zhcP%ZJ>gTor|kpAyL2ZOAxphkA^+8sl2&9NwoSgIQM+Ez?X!?mj?{0+pOJQ~cO;tG zRJ9St(Ps(!yKibnllf7-G2KU|0Q$g7!*}uYnN7Ja3~6mjIxQZ)qUmnW(1@>Hyu$9K z79;^`iAB@KnFSRqelE;@jd*kqd!n}>&AoDT2NOwR{>ww)WGv8AQuAX0C6;ZAZVc^& z8C1OH%0oYy+E0*Zg7;#=^naR^8p$3SJs?pjpCPH@_MV1)Kg1ig2{oHw{|%y|W-7_# zCJ07}EzRLpHq*#%B(I6&o}oLu)_XhglmV+H9_vm3yP%0I9SlvMLd(6S8CY<+FU8$L zc^X65%}CmhlCbig$sS$FNHdqn7Vfv&m!a*2#T9iq^))N2#*TK?E&OXs7$Yx|1?Nuy zf#^q>))|8uO4-zYdx@~%?`PlouN%bl|5TXjQ3|UsN z0>PRsR!_{}U$u30V$I`f8#u15W(ScE%*Mk5NL8QJPtWf0IzGGS^iWpyk7t#&RdsiU zvo=vHS$}zBMyx&Q!}{%y|ed|yk1NG z44I&|uevd^QWrQxiWUTBRx4RL&Q5nOOI6bDlf-)%^m(jKUL3+M^BT#HT*p53+c^82 z8LeJMMo2ez%5rScfzvwqVU~Y^N={npo@tbJsk;_#$`H>Qtr5+fAu$XLh!pF9;I?>l z-KBZWmdG57^uTaf23*=tw#`qg6ybG}Y^`U_?j2cb-x)tk9%yV^%1Cdnj_-L-H|L(7 zj5HZ4;zbRQ@LFo}iR_=;&t*DZFodrjqqhXX{5zEHrc#x+7*X(XDJ}%Xu2opp!lx&9 z%NM?wA9)cl{BM$P4~ymFxe~Z$SG&dR5u5kszo^!~P|(w;$yLp=-qir#G`HCgZH$52 zKaf>y$0wM+iAdL>Nb$e>PP1ErC!N?g>r1j?EvR=jC~O#X%A7K-{{qgxKilW?y#=}C zfnR#mxpa1mT<#Us^g-!D4!uH++!k_vQny4481v`}8}`}rM{ ze=H1Qxgsr^1bxoxcS`7H7#FderVIBel~XJ%Cd>kRpH9!n^G#5{{W`LIFpcV7P(Cne zT_0%NRz@L{9NdYtF5>t9+wfRjibnAomxis0{hVNlj)9QxixF5HLNC$C66tKtnS5??@%rhR&UVJnvVC1w(Obx(o;U{^%q}WSXCa% zqF3fu^+i_@R`eiTB-61473!Q>hii>&?CcnT4!!DLI8C`s#r%L4FkUz@NCvU`iO>i7 z_?6QEAzJ*G-9CCPvrmR<+v>c$wW5Pq8FcOrd^xVQuP=P_?3KwFIwb&2_>AH`T^yf8 zdUjI@2xBC=LW%ApH=Hm$Ki!ouKf$yyThhKhm7M9{St7TD{M=rrdTr$2ZmLd1-jGb^ zs`9R-UZ|&>U-o~EUwo~WEBY(9%1WFdvbC_F2tFZ=Zu^SXMH61aU+rg~cj z^xB1s@99$LiJx7;-z|uC0f0NAwIo-(cTP^FV3O7q8t6 zWXS%qPrzHA_}9KZogGU^{+V$K?~K13sm8a;b-B@tv{1l5tSi@|JfG9>44+|`BOxl5 z6A*Gm^Cjx_T(N-#x%W6>rN+ZI!nen@)1$3>kdcLCakTa@d3si#U%kR zClW~IoPWlUo56$YbUEAJIo2gw{AzwzD>7ZxYt~j}D_{R>a@}ycf$sd_qoaqGUj6Mj zM0#wns?T%Pos&2!6<4m(DgGF_>jQGG?yl-aE6F=)a|{T9hY9%S)hbbW0B53peA4~? zU-CxWW49-(9<20DINkXTDh-75C0n*`t~J=4eDK!m<8ieIaJn3cqx=Q8Y{U=bu3FPN zQJbi}d!p8D-bis;mNz029&LNIWAoCXt(!E?xIBP8l;7a-@6W9Gx%D$wABZr&9DkRw z(RK6JiCxh$HlHciF(Ou1H;O0yGI)G`|H#g5(HG{&8W`Pv?KpNB0K)+0bdU#c=bF ze)kzQk*(hhcdqYEO}KE-w0~$NNUuR&)b<}zUgqxJy`SsT>cnq81#3{kpSksB zM=j}J^v1s#o7X8&GM`3CgmDMNgT9^Lw|wQc*tu;_`ooW{?0Rs@=2OJJm8TEy^P21C znc_FTos)_vY!oc!`Tz!?*>_&Jn8?Z+B=;8#cNAY;H+u*O`f^?)=!+bf-~o*2p>^R| z=kNC2+1q+`{gk{Qi13Uh^6t%+^lO4`eSa4scQoUTLS@~3LFHtHOC30BB){|6Kee$g zF)+oX1w$ai*Ixu1yRj_4b<-KwSV2s7kSK`7=(O=#Oe+%+t^N1SwiPK5jNS*ZhC|!; zok{E$zkFOqHLvdI|A03;cy|qrd(;7OpX2wq00(65T-~%v2A2xoAOzzGu&?z85W;N@ zt`X$wgHMHnV9YOJ3_u2;#z;9q%->=6wOs{X?28g)7{nBkW9#x-hIcZHWWgF&ZPgVy zas2)WUWnm=HQ>>|=lz6q6xQpWTaz4sAwmKqV?Ry{1inwWF-9!xXvRNR&R6KluBaW) zKXavpIW7sfkt)a$w+&}HaxW^!GVP6FK)!ha2V+ui!T`-i`c70h@h~BYf`8D1i zN(5t6n1U8@`wVs?LAa+qU`)h5!8%I^g?l1`dpg}Oh`~jH>l88tI3U&t7-5L5`X3TH z!YFf)6p`BnNT>{ouTg>~%uY|RlB8fIy%=GY#6iRvO@bY*t_Q0Gq`syVi^+l=?Gx^3 zgJ4Jfs^B^+yM&1E1`&^p6CmC!h|v(n>=DKw1RT;{0rhjWLIC#(0neAE)_R`OUVmm; zeuvcY2UAnRBGrL>oq`F00GP10CAsP71Gh5P55ookAns061SEIcCm_*+q#!*d;Zg`e z8v>kPQ|c8xkVUKa1P#TP5d>ukT31rY2Alx9x_^R&POPzkBMJO|CfwCt0P$sO7XI)u zp;4~YGIuJm9oeDmE)eQal$7WpKs|r5)Od&1aO|dgj05_4j@&L3UC&Ud4Ip{X5StG? z0(4hTQh+5F+@Kdk083gTKwgo?^#V`|B?YNE2uOHIm})q@*0p!1t%RwC2vWPQ2(A(;G*FTNGf@F%GJ@TB2t^zachE8*AZC+7 zdv_CvB%ttMPtd{k8?P6br|Ufd0cs>b&;WcvyWldxKI#xkg9aG0V^B~UQDN@^J`7Y- zd{Cf4hTvwT8U>;cm@wd*VgrH*Ks&+2j)M_R!paDh4p92F9?(eu5!)zeAT6Q%Tmjoo z3y_Yr38!v9s6K6iwAAmfMHmTrnfh5UI!p?vyjw5@zy!*eDclGO+(->XNM&Keglz!x zVhGD$w-sC^NT_kSf*2`53}(Bq`f`GpC&J8jf-wMYpc8Ebi3to=;DplmKalr;ApigS zuLsHezrq0O4b=ZxgD?j0r8SqV5d~wWgvA8RbZu4x6dqg$n5)(}0~qg`b-4k?3-%cd z%nf!|klX5hP=6r}LO*T*TcAQXsfi1%3ou?_V2J#mOKuUkB(S};kw)$d5Oa0)&>tXo zaK1}q3Jj0%37i);3zc3L9E6sY1@v5of-HbE3bthMKT8491WX(dMv!YIAaNFV2xCrx z&=G|Egb1q|33Sj}%>Qf%=y71g&=Hm>8Z6N=pjY5nvPsz0+8_*d!m_Rc0s~BuwGRbb znq7-wHFpEP3knXvMsT)U*$A$rC?qK`eyuqQFeibtr{$V_p5wdQNecvoBJ2gAcx#iL zw6L=Qz5$)Rh!o}r_%6^5wT}dHxNjPqjqf1@F@W!a(_pBewS_hm3Ha`Xb#YU|>~KN~ zJr}qn;JaXq+W?rIs4zRgcY$>x9}BGu7?#%PumEfvXjH&=1u=r&RusknzPq*~<}Kll z5W?&Tg7L>o5QWI8PH<9dc@28;r&q$6KU|n5 zV55LV;1dN~Qvv(XQbj;o7ht3XeE6_%XZiyEeToIu9&lXnknk1cM3soZAe29YUARdo z-H(I{c?n#FBUF1c0VRNeaqYC^0%F0E6Yt#uGB&wi(1Xc>N(oP9j0O-3z`{Yfj|D+@ z5Kc4sLj05kV^gd!H6_ryn8JbGesE1xina+wQYb18@!)Q9g&}|?01S7G74TsL*noM$ zrY#V*QYT38P~d#Cb|l7~2H8+hFOVS>5CHchf}Rb93T0Iw#fS!hHfRa>tsv;5aDY$1 z38_LjB4CA>d4kFXI4(#!z>fiiw1f#T>lOI0rm!-=Kr*-wJR+yuU&Cqx-=T%hshg$Ak8B($P0;8X>C1K4;5 zpg7j77Z9Kpa9p6l?S&S(By2l-fhYj*f-!){0v#kKl>4=l@A?CxLgfZL7TgRlkO92_ zJOdBryTAw|q1ywF3mmXTZT_d*|5G_JL1|VonPi8$b{kv&9E%g~uU`w2(Cd!`mYSjRv;Ote9{m z;HL_pI0-_a>x9|W2qdHqn7-Du3Q!?ne47mv#s~`9C)5C_a7X$b-@yS6P$#^g(U*cx@+8#6cP@Yn$}8WhubYo&s~_F z8Mvii|2yRh6x&`H1)O#P$q8C7j5#SN>pu+;x)tCMxF#TO0+td61hWrdxofk}PGM_b z6RtxMzyMe-*bdI%mQDyyKY-<~#Q>0j<3C9V!(~B<#D#DJ`@TQO^^!XL%;cy<%*Frr z@c$tAzsQ2OBgRbw8}+v-4ycC7q~BE>*gF?2Wu$WI-q1%0yZX&t8~Gpi%zueI5Eb^I=;cQO5n+X?KkWj_Pkttl#tRXxjaB$Ax+c2Qj4^oV;P?t1XBv+RcCd zWXIgT5Zs+z%b?zgXd7fB4TTD&E{^ zv$%S6+Q($^&BjL259%8e#`^u5e#$02dp-8-_pP&U%dv|oP`{t>&pyMa0$>$%FSP?P zaq(B8US&>7bya&z4U5`o5kn`f18!x-nZ{O#1e^(hMvJc zx^(u*KvHE?74P5h+e7>XH)vm=Xj?mfRHWu;8(;YRZ!hOS+|4m6A@7RLMbzrMZ8v16 zMjhN8WzF0#9oOzH^_aVj4bs-$Ewi-)_UQhfYZy39*VnDU&qC={aj@Eua+B|-PSHuW z!hOTcmZs_78Q!;TGgeq6^}eGv|-+=x3R3WG{qTv(qsm=G!l)B+Ar&#)tKnP#D_b3tDTjN zi#qIiTR~G3l4j6TI(FsgyVQ*_p4!^7Tg{Dse%sNsH|`ZJ)M;NaWk&X+>5XIkq`;l4 zyNxsd{M+eAh3MS5^=mZD|9GjV_m7iQY%?idMt{pav+DTxb4OARLdwOycj@WwSFP`* zCzz+4GB;0<`>H9Eo|16qj14(=^WhxV<8HTpf4}h$@(o%Y@RKNO2j+L7CUBlnn|WCbtG1 zG-Rb$Y9z*d{tnHcwmkG!=`D%M{%B%dd-|(Jl&8&-oh0FgHaW>w>+F9;14hAxZITJf z)PL;_T}R8ZX41vzd)`K+EcdeE+R2adGT1BpcXxJu`6P#P)?U76lIb*6 zAkHm6`Bi7#+0~e$EZ_QPuZuE@ZeURwtQ<~S-p8Jw{hy~_|9(>zw)ip0Xqa;p4lB+5 zJnDT$H>@G&X2%2Popay1{$9Tw5fL{)n%2?W?~^UcuC6g{T8NJdf&|G;ZyWrOx`Syq zXc*CFmd8;~JhZ<+D~`ISjkc3X?MU$3!K~;|C&@-lOTESAM}DxcYB^)dLf-Yb`5%Af z!9qV^_|Qamm`c#~vKyzYDPskyaY`{h$}Q~25JMO(mixQ5irD=wy?$56gL19iqK~JJ z8vNjKcegbp?ly0gyT%@KTY0r`aOUwR=LxU7bsh}k>=B&Xy{Y=dez!NVnC#i>Cl+o% z{i7!S-8pdYc+Jkf?Q)MSzXUC8j*yJph!d^5TeYBaPx7;F*B1WmWNhMmM_}&BIM}fq z<0pv{i>v3J!L-wA>yEZ4yo^CUh8<}+W=G2BjV%|oVqrMz8O<_Z(=n)?DVwTT&*bl&F%l}F!*$8k^sk!kKhwcq4G$WeO$cf3bSC%fA;7U^*Bb zQ+PP?ze>WZBef|Ds6oHS40*Af`78S8BJLl!HGO&Td`g4$MTLZ6nS)yVq^idzBmS-? z`WI`SYhQ>^eWu**!Ytw)oz)#I+=Y;bCSGd$_DU3TGCDK2iT@$W6~<4%?bU&Tyb|C{dFYS`2HebixY^yS!7jpKCHssMl8 zVD^qA?661ZmA?IHTU2*ine8z7S*#!~e#VD6TW~#0yyRM079MYjc@S;L?`&|yz7i9A zr0Kk&T2=eN6IHqEGwPL!d+B6uf!-HZ=Mn9|+a+OPp=WeMEWMHM(!(T=^k!r_cP%Sp zLOf-s`m*Wr3P+#IOW+|XTcj%F^cZhgL-z? zEn9e2Tkp|3-c_DGw$&(D%q71@>6-|g5gKc7UcUK#QHuCUN`N)9K4Xj?-`l-h&|mt) zRHa+A1nbNC*rEKYL7X;X{ysYqW4PyrMJXr9a&^M4mxTIf;TcKtQh2_{PxJ%PrQ;nj z;rX?SufLdSvIf^miw5%Rd!g0yJvn}TcMvucVcx~*)rN?Sl3M0dcs6FdtiqzrJNkZF z8~pRDK82g(k&7+(2blT3DtB%Gda)qix!v%-k$SP@k@{$3oi`>-_h4UN7i{nc&K~0? z1#$B~;WD=#?M~D8^-p~*V>R*fV=T!L*P|*^tSst&-9oBoLw#&Y>uMxVw)8*4v;0cv8OMnek26#fob&aRnI64=Ta&NI z4u7dp-wT0Nd--~%EbS*hE9*SbGY556i0as;_t7*}p6g*qj*w?~K%SN`PawdQdyc9gJ&e*gWMK6=ff(5aaA;YF5N{(e+a zs^(R=oLqDwDvV_7 zrd^OcwS)D+Z|7e0sbaIr(A=_n1rEet(&!{T&$k1wvmm>Gp3kwjK5e7bv_cC_kWCY_ zkh6`=*?MPCtQ%$gq~wo?;cUUx;9%dbvAy{kY4Tp%Uz88oxxfFBiFhDOrA<5O)ixwh zyMpktg-8QEy}VpR(I3a#^KdV$yR7xBO>%WyI%Fm6p3>)(lg?JwD;*)e$<>S-lUc*7 zz82o69hU+Nr~I(J9p6lc-b1R?Fm`Px%u06p{cxWs=#)0b%QD(+HZu+CtBGg&JS4yN z41XRDFz|KYN;fKUo{cuB=t=EQ(ON+gO<3k)sI#zOxvvaO#4^`HVb#fU#HexM5&VFk zG^f)zF?wiw^$ya_%I~VmtYW__kCdwO^dRo>de_MNzf2?+y>CW&ld5GhH|6woun%wV zIPyc8oLXi_)+;ul*U_Z1mIg56{F8#~xk@#H|< zYKZ-p@RF1t5d){#D=j>OqGY6J0FsmUVLrrsw#hjKS&4lK?@!L&gzDt#oLIs?&v5Ld zXQH_N+8VcSy~Gblduf!7ETz#I8qL+zvytvEbN6Ahil%qwSadW}rFjZz(pJopl5x*B zIb+Qwc_eG5Ux|yxKeCZnXtt)V8Spo=*F)8}R$M)ru}`K|eD6u`Wx_liwaOdYN?as4 z%RH_8W?Sd6&Sb)i`md};S9rF{c)9rOSGqYTa6{NL-iD=W`|#w)IFmeyRg;d5JEykKzsTuJp_wcn4!XP0 z#M*jh+KhDqqKX*R+F)ahKNfk3bEE-wjd=ZKq^|vu$d2C#u?TZn*fq`wwgG0{WVu|e z8#$=8gYS6{ZKguc*W<}dr?M7e7*cjIB!HD*y&bu%|C z*ix1=+NUu2hi=r9iP<=%k0o;{ZT7b#(rfQPUVA}x)KAU^OS`Et&^JBD2?(f^y4cT( z^v6=%Ulr*OrM@qACY{MV!$7b1)K_Jo2R-)Lp-Rcy^AzEYtWD^0iK()5Zz*X>C(FDz z^X>VHwv7*fdmAIq49>$J$5l$xuSh{-^AxS3LZ5Li{^+VTfqj>Cy9Etb8w6gpXe_$a zxkr&XaEa&i^K^0gS*iEHbA`;+2=?XwcEkOlC)94n<||$_@+m6dHc7I1u95dR7r%{? z&RNEWxW1uKv67>+FXVHGgH18&tjZW}7Vj|R8iL{SW;nQA+Hj2hTzo^fv@KcvEcR-d z=wP%B=~$!#-iuJ%XV05M8BDfOQC-Bs5e$}xj{FT=ytQ6m=wY>JHX*3LXfpIty_spddo3CZ&~{IpMOE7YTLv51fmC4me~G-!hfv z7R~ZLrtdviwXZYj9CKkCd+VF&zuTa;bW$sN#!JS{2fCT$ZkU?WK--Up%9>105%&SPO?&G=yfc31L@c6{o2-m|7=F zmliy~4%=;cC1>_hM1`*FAjXC)A$N7+-hy-v^!FC4969;Ob8zvC8r`t#I82gLW>dW= zb#(d+*(fnOvG5P`dO$p6Ro{P6rdy}p{S;z*>e{y?E4bjvLg&BXKDm{&dV5L}3BW)r?a10A6g z5)ZL|nMY;DRJkT9(x~j9qqVMyN4hy^nebpT1a^|m2`j=qfT-M*p3}b8{iZ2qv<#h= z10{3=+@1oyI&47~3ei8mtfdjSdle4ny12mXIhU7JY$FG8xerv0CakTa#Imog%tIzIe-9Kc=x3Ev`52w;AF)-&FfS~#@3c$(!wfuC=on0w|#n- zTmG;EmY4c@ThP@PiO!R6PD%!ubZl3>ctygvV_Krg28!qmjfPgRwA8o~iniF@M4PIZ zLR-$A+?CEhy%%7aeSJr3&-2&cTQC~3jN8-Ppq0yW{}&OfTN}wYA6hp^v#D*U$aOn0 z$97XqV^NVtoU*6Y%<&_&_AijRD>&`upYIP)lKU`ctI1)ry~9r5 zNS$=mimLrQNA9OfXnH^JQkZ{`JH4TIxTL`JU;m}*Uc@U}L?<4V^Dw5W$t9*AsS+gxFXS)P^A1hn?gf2kNc*ZB^z zGrKqP3^jY~_zA^`5a%>@?O`buEKllg50jXOY3<1@x1l`kfUIg$!;j_`@|GMz8EWJv zieqw_m7HV2Naab6B$Bg8eDsv}5zli&qAU$pCr$CcVxR?>J@%|k5aM`;hpz35zjrK= z-HAn$yD^Cz2r(ku>SbQBb9Pg=T|h;z1>J_xX=&PJK_3i4e;*3T$r+G^5TiH{?td$< z$W6`cDp;*4?EJOtP-=5}jcMK2%pWT$=x!{hBDP((tSPHWXUZbyju@tQgq-V>i18ok z`_N9MHFfwsyqpcsw`RD&dR&KdnmR|@L;pKSwt?DNt2-g{BU^ZWoFi5UrEIO|?d2yD z)x~y*&S6YWatt{UT9sCE^P@-R958y>gp$PH-lqKfO;y|QHmF`4%Lh`@%dWL`^P;&)z<)!x^!Ft66gW z;Ej8JerTqSwR^lXC@4H`&oZ@AvSV4;vpU`x+$cLiz~}U|@0?9j(VQzfUiXCDG#uVi zRB0XBf=u{|uO>#GnryeHCVs`Ez0=#o0UK&b(N)L>9AG`P@;vAD42LHRwN;mUHL~nvqDLsD zW{cMi@WwBLWh6UnX*Vd+}Qm02ZnHLMVa%e>!1 zknaQWgO>HUDrsz|dHZLOS%0|O_$gOZi9Spw5{Z6g5;?m{( zo@`?l>TN=SC$YB3Rna0RC&~RZYOs$<4>^H}>!Z!V@1LX~I-A`II+F%gG!8`kSEy3@pDiwN6gE3Wv4m z;%-N@s_V<85%@&xUf6g?rn`HbP4?>V+~&4do3h%S3JWYrb`w*2!>8Jtd6b8S&>9`iw+m203hnt6Z9j|sQB|>`V7C>QQ0R2OEunqJIIldr?~R;m z?}V?rFWrm9ZdCDxbHDPcyCg!>f7E@d9`(s)96=RUXb|K&XRDH-=saC2+f!<_@?(%! z){2a4R=M{$w0YrC*;l7}pYB8rxFUU7EA&}XT?roTeE4Ht>WVph$YM;{<=vD?9FHHB zQ}QBqPMSxH7e)B`Vz}AUtDjRaH=+*tFtEnUN#D^Gj-CzH#j)ZJLxFx6mXNF#xx ztwvAxV$5tms&b?=d1m?bIhCwLd|v!$GNGqt)Yl7T={;H+>N;WjtFX2wCSL2h_?VWn zGvi@dg&u8t5mB}v3Eq}ke64JC;P*8Wk7zhB-G7xN=`DR}vT}B;up`|r>I2t5zeGbu zcW;_hB5Ox>d&Zb*F8x4s$IcM#*;|S&^xOUE-s~+aKqmO|M7eF z@qiZ2O2Lr!-x`EY27~U8eji%;3q$?`{8faRwRFsF3Duc}Cgi7QvVSo?@>>dLd4(8^ zANWi4>4T`T)K#tP-x_$Xqpk8@yGVMy#aLroXDzncL=-MW*8ddBDV44#FP z1AmqIb-H06=0>ngZsBgf+ToJRrR96xxdr_7?f1gZ+un8P*zlclEtiL*+nbw5PH`Wj z4D!a=u&f$%SXh|((Flv>Z@9B(6aMJvy*<*Icnz*VluZu2^9xJv;5%=)$HC~bB}!ig zyIC&D*vK7uVA$z>X7>(D*0VzwPNXp~7Gt$tEAOgNDetRyTxh$-QLh;&dd%7Wd-?== zN5{nIX0)X@?>cz{6*d~!@_pr2A#w(*tgIV5@5v#gRFvfwVmC*HSl&$Sz-?jme6>Al zn?GsG;tpA?%l7mN>N$8PkhtN1_3EX~9aAq1Ildp4M3$1+KQ-)=@ZApB&pk%T_JlcJ z&NsG=JsqYQ@bCjswBy{a;y#p|J)Yn2$HaU2sVeJ=N@Lsuz5JPR-{)N|w$5EX(Tz)n zd%vw@dF?lxS2=dLuq3MJPtoXO-nzR^+S>cn@9)`TZHLRjHBLIcn~OdW@pf;HYf}iG zire8r$i<5Z_p=KF1uIdLRs^{4iX?h-c4_R;**sHTd$dCyMk}fV27E?Pc_UR@o@$!G}HeNx|Rp? literal 0 HcmV?d00001 diff --git a/sourcemod/plugins/superlogs-dods.smx b/sourcemod/plugins/superlogs-dods.smx new file mode 100644 index 0000000000000000000000000000000000000000..73dfcc8fba506ee2f46230a178f723066e428447 GIT binary patch literal 14302 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 literal 0 HcmV?d00001 diff --git a/sourcemod/plugins/superlogs-ges.smx b/sourcemod/plugins/superlogs-ges.smx new file mode 100644 index 0000000000000000000000000000000000000000..44050bb26adc5209250e62cae1d55ab6075a648e GIT binary patch literal 13909 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 T433hr8^>6X#0ye!cP#!7oS1fL literal 0 HcmV?d00001 diff --git a/sourcemod/plugins/superlogs-hl2mp.smx b/sourcemod/plugins/superlogs-hl2mp.smx new file mode 100644 index 0000000000000000000000000000000000000000..325e58da0546e5a58b347e7f8cab2beb2080c14c GIT binary patch literal 11959 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 O>H5@9co&-LUjGM+Naz{> literal 0 HcmV?d00001 diff --git a/sourcemod/plugins/superlogs-ins.smx b/sourcemod/plugins/superlogs-ins.smx new file mode 100644 index 0000000000000000000000000000000000000000..ac2230f8125e315dafa06d8c80ad07f044d94d26 GIT binary patch literal 14590 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 literal 0 HcmV?d00001 diff --git a/sourcemod/plugins/superlogs-l4d.smx b/sourcemod/plugins/superlogs-l4d.smx new file mode 100644 index 0000000000000000000000000000000000000000..aa0a93e689a402ba765e4633e97536abdb2edd52 GIT binary patch literal 17914 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⋘KZzRhm96{fOimY;65k*J2Ru32ZNy4Ol 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 literal 0 HcmV?d00001 diff --git a/sourcemod/plugins/superlogs-nucleardawn.smx b/sourcemod/plugins/superlogs-nucleardawn.smx new file mode 100644 index 0000000000000000000000000000000000000000..2f5b7af999522091d82560d714b86c82caf03ac7 GIT binary patch literal 15908 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 literal 0 HcmV?d00001 diff --git a/sourcemod/plugins/superlogs-pvkii.smx b/sourcemod/plugins/superlogs-pvkii.smx new file mode 100644 index 0000000000000000000000000000000000000000..8f4b23e3030821b7de3fafccb768057b63545ba2 GIT binary patch literal 11557 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 literal 0 HcmV?d00001 diff --git a/sourcemod/plugins/superlogs-zps.smx b/sourcemod/plugins/superlogs-zps.smx new file mode 100644 index 0000000000000000000000000000000000000000..f3080d60c8a36581243430a53c608e0b3a4c5537 GIT binary patch literal 11908 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 0 && !IsFakeClient(client) && IsClientInGame(client)) { new Handle:hBf; - hBf = StartMessageOne("SayText", client); + hBf = StartMessageOne("SayText", client, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS); if (hBf != INVALID_HANDLE) { - BfWriteByte(hBf, 0); // send as console - BfWriteString(hBf, message); - BfWriteByte(hBf, 1); // 1 to enable color parsing, 0 to not + if(GetUserMessageType() == UM_Protobuf) + { + PbSetInt(hBf, "ent_idx", 0); + PbSetBool(hBf, "chat", true); + PbSetString(hBf, "text", message); + } + else + { + BfWriteByte(hBf, 0); // send as console + BfWriteString(hBf, message); + BfWriteByte(hBf, 1); // 1 to enable color parsing, 0 to not + } EndMessage(); } } diff --git a/sourcemod/scripting/include/wstatshelper.inc b/sourcemod/scripting/include/wstatshelper.inc new file mode 100644 index 0000000..2648d83 --- /dev/null +++ b/sourcemod/scripting/include/wstatshelper.inc @@ -0,0 +1,138 @@ +#define HITGROUP_GENERIC 0 +#define HITGROUP_HEAD 1 +#define HITGROUP_CHEST 2 +#define HITGROUP_STOMACH 3 +#define HITGROUP_LEFTARM 4 +#define HITGROUP_RIGHTARM 5 +#define HITGROUP_LEFTLEG 6 +#define HITGROUP_RIGHTLEG 7 + +#define LOG_HIT_OFFSET 7 + +#define LOG_HIT_SHOTS 0 +#define LOG_HIT_HITS 1 +#define LOG_HIT_KILLS 2 +#define LOG_HIT_HEADSHOTS 3 +#define LOG_HIT_TEAMKILLS 4 +#define LOG_HIT_DAMAGE 5 +#define LOG_HIT_DEATHS 6 +#define LOG_HIT_GENERIC 7 +#define LOG_HIT_HEAD 8 +#define LOG_HIT_CHEST 9 +#define LOG_HIT_STOMACH 10 +#define LOG_HIT_LEFTARM 11 +#define LOG_HIT_RIGHTARM 12 +#define LOG_HIT_LEFTLEG 13 +#define LOG_HIT_RIGHTLEG 14 + +new Handle:g_weapon_trie = INVALID_HANDLE; + +CreatePopulateWeaponTrie() +{ + // Create a Trie + g_weapon_trie = CreateTrie(); + + // Initial populate + for (new i = 0; i < MAX_LOG_WEAPONS; i++) + { + if (g_weapon_list[i][0] == 0) + { + // some games have a couple blanks as place holders (so array indexes match with weapon ids) + decl String:randomKey[6]; + Format(randomKey, sizeof(randomKey), "%c%c%c%c%c%c", GetURandomInt(), GetURandomInt(), GetURandomInt(), GetURandomInt(), GetURandomInt(), GetURandomInt()); + SetTrieValue(g_weapon_trie, randomKey, i); + continue; + } + + SetTrieValue(g_weapon_trie, g_weapon_list[i], i); + } +} + +dump_player_stats(client) +{ + if (IsClientInGame(client) && IsClientConnected(client)) + { + decl String: player_authid[64]; + if (!GetClientAuthString(client, player_authid, sizeof(player_authid))) + { + strcopy(player_authid, sizeof(player_authid), "UNKNOWN"); + } + new player_team_index = GetClientTeam(client); + + new player_userid = GetClientUserId(client); + + new is_logged; + for (new i = 0; (i < MAX_LOG_WEAPONS); i++) + { + #if defined INS + if (g_weapon_stats[client][i][LOG_HIT_HITS] > 0) + { + LogToGame("\"%N<%d><%s><%s>\" triggered \"weaponstats\" (weapon \"weapon_%s\") (shots \"%d\") (hits \"%d\") (kills \"%d\") (headshots \"%d\") (tks \"%d\") (damage \"%d\") (deaths \"%d\")", client, player_userid, player_authid, g_team_list[player_team_index], g_weapon_list[i], g_weapon_stats[client][i][LOG_HIT_SHOTS], g_weapon_stats[client][i][LOG_HIT_HITS], g_weapon_stats[client][i][LOG_HIT_KILLS], g_weapon_stats[client][i][LOG_HIT_HEADSHOTS], g_weapon_stats[client][i][LOG_HIT_TEAMKILLS], g_weapon_stats[client][i][LOG_HIT_DAMAGE], g_weapon_stats[client][i][LOG_HIT_DEATHS]); + LogToGame("\"%N<%d><%s><%s>\" triggered \"weaponstats2\" (weapon \"weapon_%s\") (head \"%d\") (chest \"%d\") (stomach \"%d\") (leftarm \"%d\") (rightarm \"%d\") (leftleg \"%d\") (rightleg \"%d\")", client, player_userid, player_authid, g_team_list[player_team_index], g_weapon_list[i], g_weapon_stats[client][i][LOG_HIT_HEAD], g_weapon_stats[client][i][LOG_HIT_CHEST], g_weapon_stats[client][i][LOG_HIT_STOMACH], g_weapon_stats[client][i][LOG_HIT_LEFTARM], g_weapon_stats[client][i][LOG_HIT_RIGHTARM], g_weapon_stats[client][i][LOG_HIT_LEFTLEG], g_weapon_stats[client][i][LOG_HIT_RIGHTLEG]); + #else + if (g_weapon_stats[client][i][LOG_HIT_SHOTS] > 0) + { + #if defined GES + LogToGame("\"%N<%d><%s><%s>\" triggered \"weaponstats\" (weapon \"weapon_%s\") (shots \"%d\") (hits \"%d\") (kills \"%d\") (headshots \"%d\") (tks \"%d\") (damage \"%d\") (deaths \"%d\")", client, player_userid, player_authid, g_team_list[player_team_index], g_weapon_loglist[i], g_weapon_stats[client][i][LOG_HIT_SHOTS], g_weapon_stats[client][i][LOG_HIT_HITS], g_weapon_stats[client][i][LOG_HIT_KILLS], g_weapon_stats[client][i][LOG_HIT_HEADSHOTS], g_weapon_stats[client][i][LOG_HIT_TEAMKILLS], g_weapon_stats[client][i][LOG_HIT_DAMAGE], g_weapon_stats[client][i][LOG_HIT_DEATHS]); + LogToGame("\"%N<%d><%s><%s>\" triggered \"weaponstats2\" (weapon \"weapon_%s\") (head \"%d\") (chest \"%d\") (stomach \"%d\") (leftarm \"%d\") (rightarm \"%d\") (leftleg \"%d\") (rightleg \"%d\")", client, player_userid, player_authid, g_team_list[player_team_index], g_weapon_loglist[i], g_weapon_stats[client][i][LOG_HIT_HEAD], g_weapon_stats[client][i][LOG_HIT_CHEST], g_weapon_stats[client][i][LOG_HIT_STOMACH], g_weapon_stats[client][i][LOG_HIT_LEFTARM], g_weapon_stats[client][i][LOG_HIT_RIGHTARM], g_weapon_stats[client][i][LOG_HIT_LEFTLEG], g_weapon_stats[client][i][LOG_HIT_RIGHTLEG]); + #else + LogToGame("\"%N<%d><%s><%s>\" triggered \"weaponstats\" (weapon \"%s\") (shots \"%d\") (hits \"%d\") (kills \"%d\") (headshots \"%d\") (tks \"%d\") (damage \"%d\") (deaths \"%d\")", client, player_userid, player_authid, g_team_list[player_team_index], g_weapon_list[i], g_weapon_stats[client][i][LOG_HIT_SHOTS], g_weapon_stats[client][i][LOG_HIT_HITS], g_weapon_stats[client][i][LOG_HIT_KILLS], g_weapon_stats[client][i][LOG_HIT_HEADSHOTS], g_weapon_stats[client][i][LOG_HIT_TEAMKILLS], g_weapon_stats[client][i][LOG_HIT_DAMAGE], g_weapon_stats[client][i][LOG_HIT_DEATHS]); + LogToGame("\"%N<%d><%s><%s>\" triggered \"weaponstats2\" (weapon \"%s\") (head \"%d\") (chest \"%d\") (stomach \"%d\") (leftarm \"%d\") (rightarm \"%d\") (leftleg \"%d\") (rightleg \"%d\")", client, player_userid, player_authid, g_team_list[player_team_index], g_weapon_list[i], g_weapon_stats[client][i][LOG_HIT_HEAD], g_weapon_stats[client][i][LOG_HIT_CHEST], g_weapon_stats[client][i][LOG_HIT_STOMACH], g_weapon_stats[client][i][LOG_HIT_LEFTARM], g_weapon_stats[client][i][LOG_HIT_RIGHTARM], g_weapon_stats[client][i][LOG_HIT_LEFTLEG], g_weapon_stats[client][i][LOG_HIT_RIGHTLEG]); + #endif + #endif + is_logged++; + } + } + if (is_logged > 0) + { + reset_player_stats(client); + } + } +} + +reset_player_stats(client) +{ + for (new i = 0; (i < MAX_LOG_WEAPONS); i++) + { + g_weapon_stats[client][i][LOG_HIT_SHOTS] = 0; + g_weapon_stats[client][i][LOG_HIT_HITS] = 0; + g_weapon_stats[client][i][LOG_HIT_KILLS] = 0; + g_weapon_stats[client][i][LOG_HIT_HEADSHOTS] = 0; + g_weapon_stats[client][i][LOG_HIT_TEAMKILLS] = 0; + g_weapon_stats[client][i][LOG_HIT_DAMAGE] = 0; + g_weapon_stats[client][i][LOG_HIT_DEATHS] = 0; + g_weapon_stats[client][i][LOG_HIT_GENERIC] = 0; + g_weapon_stats[client][i][LOG_HIT_HEAD] = 0; + g_weapon_stats[client][i][LOG_HIT_CHEST] = 0; + g_weapon_stats[client][i][LOG_HIT_STOMACH] = 0; + g_weapon_stats[client][i][LOG_HIT_LEFTARM] = 0; + g_weapon_stats[client][i][LOG_HIT_RIGHTARM] = 0; + g_weapon_stats[client][i][LOG_HIT_LEFTLEG] = 0; + g_weapon_stats[client][i][LOG_HIT_RIGHTLEG] = 0; + } +} + +stock get_weapon_index(const String:weapon_name[]) +{ + new index = -1; + GetTrieValue(g_weapon_trie, weapon_name, index); + return index; +} + + +WstatsDumpAll() +{ + for (new i = 1; i <= MaxClients; i++) + { + dump_player_stats(i); + } +} + +OnPlayerDisconnect(client) +{ + if(client > 0 && IsClientInGame(client)) + { + dump_player_stats(client); + reset_player_stats(client); + } +} \ No newline at end of file diff --git a/sourcemod/scripting/superlogs-aoc.sp b/sourcemod/scripting/superlogs-aoc.sp new file mode 100644 index 0000000..655ee75 --- /dev/null +++ b/sourcemod/scripting/superlogs-aoc.sp @@ -0,0 +1,270 @@ +/** + * HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging + * http://www.hlxcommunity.com + * Copyright (C) 2009 Nicholas Hastings (psychonic) + * Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma semicolon 1 + +#include +#include + +#define NAME "SuperLogs: Age of Chivalry" +#define VERSION "1.0.2" + +new Handle:g_cvar_headshots = INVALID_HANDLE; +new Handle:g_cvar_locations = INVALID_HANDLE; +new Handle:g_cvar_classchanges = INVALID_HANDLE; +new Handle:g_cvar_actions = INVALID_HANDLE; + +new bool:g_logheadshots = true; +new bool:g_loglocations = true; +new bool:g_logclasschanges = true; +new bool:g_logactions = true; + +new bool:g_bLogClassNextSpawn[MAXPLAYERS+1]; + +#include + +public Plugin:myinfo = { + name = NAME, + author = "psychonic", + description = "Advanced logging for Age of Chivalry. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats", + version = VERSION, + url = "http://www.hlxcommunity.com" +}; + + +public OnPluginStart() +{ + g_cvar_locations = CreateConVar("superlogs_locations", "1", "Enable logging of kill coordinates (default on)", 0, true, 0.0, true, 1.0); + g_cvar_headshots = CreateConVar("superlogs_headshots", "1", "Enable logging of headshot and decapitation actions (default on)", 0, true, 0.0, true, 1.0); + g_cvar_classchanges = CreateConVar("superlogs_classchanges", "1", "Enable logging of character changes (default on)", 0, true, 0.0, true, 1.0); + g_cvar_actions = CreateConVar("superlogs_actions", "1", "Enable logging of actions, such as \"Round_Win\" (default on)", 0, true, 0.0, true, 1.0); + + HookConVarChange(g_cvar_locations, OnCvarLocationsChange); + HookConVarChange(g_cvar_headshots, OnCvarHeadshotsChange); + HookConVarChange(g_cvar_classchanges, OnCvarClasschangesChange); + + CreateConVar("superlogs_aoc_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); + + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + HookEvent("round_end", Event_RoundEnd); + hook_classchanges(); + + CreateTimer(1.0, LogMap); + + GetTeams(); +} + +hook_classchanges() +{ + HookUserMessage(GetUserMessageId("ClassChanged"), Event_ClassChanged); + HookEvent("player_spawn", Event_Spawn, EventHookMode_Pre); + + for (new i = 1; i <= MAXPLAYERS; i++) + { + g_bLogClassNextSpawn[i] = false; + } +} + +unhook_classchanges() +{ + UnhookUserMessage(GetUserMessageId("ClassChanged"), Event_ClassChanged); + UnhookEvent("player_spawn", Event_Spawn, EventHookMode_Pre); +} + + +public OnMapStart() +{ + GetTeams(); +} + +public Action:Event_ClassChanged(UserMsg:msg_id, Handle:bf, const players[], playersNum, bool:reliable, bool:init) +{ + g_bLogClassNextSpawn[players[0]] = true; + return Plugin_Continue; +} + +public Action:Event_Spawn(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + if (client > 0 && g_bLogClassNextSpawn[client]) + { + switch (GetEntProp(client, Prop_Send, "m_iClass")) + { + case 0: + LogRoleChange(client, "Longbowman"); + case 1: + LogRoleChange(client, "Crossbowman"); + case 2: + LogRoleChange(client, "Javelineer"); + case 3: + LogRoleChange(client, "Man at Arms"); + case 4: + LogRoleChange(client, "Sergeant"); + case 5: + LogRoleChange(client, "Guardsman"); + case 6: + LogRoleChange(client, "Crusader"); + case 7: + LogRoleChange(client, "Knight"); + case 8: + LogRoleChange(client, "Heavy Knight"); + } + g_bLogClassNextSpawn[client] = false; + } + + return Plugin_Continue; +} + + +public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID who died + // "attacker" "short" // user ID who killed + // "weapon" "string" // weapon name killed used + // "printweapon" "string" // full print weapon name killed used + // "hitgroup" "long" // Part of body that was hit + // "damagetype" "long" // CDamageInfo.GetAOCDamageType() + // "weaponid" "short" // Weapon ID + // "team" "short" // victim's team + + new attacker = GetEventInt(event, "attacker"); + new victim = GetEventInt(event, "userid"); + + if (attacker > 0 && victim > 0) + { + if (g_loglocations) + { + LogKillLoc(GetClientOfUserId(attacker), GetClientOfUserId(victim)); + } + + if (g_logheadshots) + { + new bool:headshot = GetEventBool(event, "headshot"); + new bool:decapped = GetEventBool(event, "decapped"); + new bool:headexplodie = GetEventBool(event, "headexplodie"); + if (decapped) + { + //decapitation + LogPlayerEvent(GetClientOfUserId(attacker), "triggered", "headshot", true, " (hstype \"decap\")"); + } + else if (headshot) + { + //headshot + LogPlayerEvent(GetClientOfUserId(attacker), "triggered", "headshot", true, " (hstype \"headshot\")"); + } + else if (headexplodie) + { + // head "explodie" + LogPlayerEvent(GetClientOfUserId(attacker), "triggered", "headshot", true, " (hstype \"headexplodie\")"); + } + } + } + + return Plugin_Continue; +} + +public OnClientPutInGame(client) +{ + g_bLogClassNextSpawn[client] = false; +} + +public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogTeamEvent(GetEventInt(event, "winner"), "triggered", "Round_Win"); +} + +public OnCvarLocationsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_loglocations; + g_loglocations = GetConVarBool(g_cvar_locations); + + if (old_value != g_loglocations) + { + if (g_loglocations & !g_logheadshots) + { + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + else if (!g_logheadshots) + { + UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + } +} + +public OnCvarActionsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logactions; + g_logactions = GetConVarBool(g_cvar_actions); + + if (old_value != g_logactions) + { + if (g_logactions) + { + HookEvent("round_end", Event_RoundEnd); + } + else + { + UnhookEvent("round_end", Event_RoundEnd); + } + } +} + +public OnCvarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logheadshots; + g_logheadshots = GetConVarBool(g_cvar_headshots); + + if (old_value != g_logheadshots) + { + if (g_logheadshots & !g_loglocations) + { + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + else if (!g_loglocations) + { + UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + } +} + +public OnCvarClasschangesChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logclasschanges; + g_logclasschanges = GetConVarBool(g_cvar_classchanges); + + if (old_value != g_logclasschanges) + { + if (g_logclasschanges) + { + hook_classchanges(); + } + else + { + unhook_classchanges(); + } + } +} + +public Action:LogMap(Handle:timer) +{ + // Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats + LogMapLoad(); +} \ No newline at end of file diff --git a/sourcemod/scripting/superlogs-cspromod.sp b/sourcemod/scripting/superlogs-cspromod.sp new file mode 100644 index 0000000..04b4306 --- /dev/null +++ b/sourcemod/scripting/superlogs-cspromod.sp @@ -0,0 +1,116 @@ +#include +#include + +#define VERSION "2.2" + +public Plugin:myinfo = +{ + name = "SuperLogs: CSpromod", + author = "NeoCortex, psychonic", + description = "Rewrites the logs from CSProMod so HLStatsX:CE will understand them", + version = VERSION, + url = "http://www.sourcemod.net/" +}; + +public OnPluginStart() +{ + CreateConVar("superlogs_cspromod_version", VERSION, "SuperLogs: CSpromod", FCVAR_NOTIFY); + + HookEvent("player_death", Event_PlayerDeath) + HookEvent("player_connect", Event_PlayerConnect) + HookEvent("player_disconnect", Event_PlayerDisconnect) + HookEvent("player_team", Event_PlayerTeam) + + HookEvent("round_start", Event_RoundStart) + HookEvent("round_end", Event_RoundEnd) + + CreateTimer(1.0, LogMap); +} + +public Action:LogMap(Handle:timer) +{ + // Taken from superlogs-generic.sp by psychonic + // Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats + LogMapLoad(); +} + +public OnMapStart() +{ + // For loghelper + GetTeams(); +} + +public Event_PlayerConnect(Handle:event, const String:name[], bool:dontBroadcast) +{ + decl String:cname[MAX_NAME_LENGTH]; + GetEventString(event, "name", cname, sizeof(cname)); + decl String:steamid[24]; + GetEventString(event, "networkid", steamid, sizeof(steamid)); + decl String:ip[32]; + GetEventString(event, "address", ip, sizeof(ip)); + + LogToGame("\"%s<%d><%s><>\" connected, address \"%s\"", cname, GetEventInt(event, "userid"), steamid, ip); +} + +public Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + decl String:cname[MAX_NAME_LENGTH]; + GetClientName(client, cname, sizeof(cname)); + decl String:cauth[32]; + GetClientAuthString(client, cauth, 32); + decl String:creason[128]; + GetEventString(event, "reason", creason, sizeof(creason)); + + LogToGame("\"%s<%d><%s><>\" disconnected (reason \"%s\")", cname, client, cauth, creason); +} + +public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) +{ + // Version 2 of this subroutine (using loghelper) is written by psychonic + new victim = GetClientOfUserId(GetEventInt(event, "userid")); + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + decl String:AWeapon[64]; + GetEventString(event, "weapon", AWeapon, sizeof(AWeapon)); + + decl String:properties[12] = ""; + if (GetEventBool(event, "headshot")) + { + strcopy(properties, sizeof(properties), " (headshot)"); + } + + // Player_Suicide + if (attacker == victim) + { + LogSuicide(victim, AWeapon, true, properties); + return; + } + + LogKill(attacker, victim, AWeapon, true, properties); +} + +public Event_RoundStart(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogToGame("World triggered \"Round_Start\""); +} + +public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) +{ + new winner = GetEventInt(event, "winner"); + if (winner == 2 || winner == 3) + { + LogTeamEvent(winner, "triggered", "Round_Win"); + } + + LogToGame("World triggered \"Round_End\""); +} + +public Event_PlayerTeam(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + new NTeam = GetEventInt(event, "team"); + decl String:STeam[32]; + GetTeamName(NTeam, STeam, sizeof(STeam)); + + LogPlayerEvent(client, "joined team", STeam, true) +} diff --git a/sourcemod/scripting/superlogs-css.sp b/sourcemod/scripting/superlogs-css.sp new file mode 100644 index 0000000..2754f2c --- /dev/null +++ b/sourcemod/scripting/superlogs-css.sp @@ -0,0 +1,471 @@ +/** + * HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging + * http://www.hlxcommunity.com + * Copyright (C) 2009 Nicholas Hastings (psychonic) + * Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma semicolon 1 + +#include +#include + +#define NAME "SuperLogs: CSS" +#define VERSION "1.2.4" + +#define MAX_LOG_WEAPONS 28 +#define IGNORE_SHOTS_START 25 +#define MAX_WEAPON_LEN 13 + + +new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][15]; +new const String:g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = { + "ak47", + "m4a1", + "awp", + "deagle", + "mp5navy", + "aug", + "p90", + "famas", + "galil", + "scout", + "g3sg1", + "usp", + "glock", + "m249", + "m3", + "elite", + "fiveseven", + "mac10", + "p228", + "sg550", + "sg552", + "tmp", + "ump45", + "xm1014", + "knife", + "hegrenade", + "smokegrenade", + "flashbang" + }; + +new Handle:g_cvar_wstats = INVALID_HANDLE; +new Handle:g_cvar_headshots = INVALID_HANDLE; +new Handle:g_cvar_actions = INVALID_HANDLE; +new Handle:g_cvar_locations = INVALID_HANDLE; +new Handle:g_cvar_ktraj = INVALID_HANDLE; +new Handle:g_cvar_version = INVALID_HANDLE; + +new bool:g_logwstats = true; +new bool:g_logheadshots = true; +new bool:g_logactions = true; +new bool:g_loglocations = true; +new bool:g_logktraj = false; + +#include +#include + + +public Plugin:myinfo = { + name = NAME, + author = "psychonic", + description = "Advanced logging for CSS. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats", + version = VERSION, + url = "http://www.hlxcommunity.com" +}; + +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 +public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) +#else +public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max) +#endif +{ + decl String:game_description[64]; + GetGameDescription(game_description, sizeof(game_description), true); + if (StrContains(game_description, "Counter-Strike", false) == -1) + { + decl String:game_folder[64]; + GetGameFolderName(game_folder, sizeof(game_folder)); + if (StrContains(game_folder, "cstrike", false) == -1) + { + strcopy(error, err_max, "This plugin is only supported on CS:S"); + #if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 + return APLRes_Failure; + #else + return false; + #endif + } + } +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 + return APLRes_Success; +#else + return true; +#endif +} + + +public OnPluginStart() +{ + CreatePopulateWeaponTrie(); + + g_cvar_wstats = CreateConVar("superlogs_wstats", "1", "Enable logging of weapon stats (default on)", 0, true, 0.0, true, 1.0); + g_cvar_headshots = CreateConVar("superlogs_headshots", "1", "Enable logging of headshot player action (default on)", 0, true, 0.0, true, 1.0); + g_cvar_actions = CreateConVar("superlogs_actions", "1", "Enable logging of player actions (default on)", 0, true, 0.0, true, 1.0); + g_cvar_locations = CreateConVar("superlogs_locations", "1", "Enable logging of location on player death (default on)", 0, true, 0.0, true, 1.0); + g_cvar_ktraj = CreateConVar("superlogs_ktraj", "0", "Enable Psychostats \"KTRAJ\" logging (default off)", 0, true, 0.0, true, 1.0); + + // cvars will have already existed if plugin was reloaded and might be set to non-default values + g_logwstats = GetConVarBool(g_cvar_wstats); + g_logheadshots = GetConVarBool(g_cvar_headshots); + g_logactions = GetConVarBool(g_cvar_actions); + g_loglocations = GetConVarBool(g_cvar_locations); + g_logktraj = GetConVarBool(g_cvar_ktraj); + + HookConVarChange(g_cvar_wstats, OnCvarWstatsChange); + HookConVarChange(g_cvar_headshots, OnCvarHeadshotsChange); + HookConVarChange(g_cvar_actions, OnCvarActionsChange); + HookConVarChange(g_cvar_locations, OnCvarLocationsChange); + HookConVarChange(g_cvar_ktraj, OnCvarKtrajChange); + + g_cvar_version = CreateConVar("superlogs_css_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); + + hook_wstats(); + hook_actions(); + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + HookEvent("player_death", Event_PlayerDeath); + + CreateTimer(1.0, LogMap); + + GetTeams(); +} + + +public OnMapStart() +{ + GetTeams(); +} + +public OnConfigsExecuted() +{ + decl String:version[255]; + GetConVarString(g_cvar_version, version, sizeof(version)); + SetConVarString(g_cvar_version, version); +} + +hook_wstats() +{ + HookEvent("weapon_fire", Event_PlayerShoot); + HookEvent("player_hurt", Event_PlayerHurt); + HookEvent("player_spawn", Event_PlayerSpawn); + HookEvent("round_end", Event_RoundEnd, EventHookMode_PostNoCopy); + HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre); +} + +unhook_wstats() +{ + UnhookEvent("weapon_fire", Event_PlayerShoot); + UnhookEvent("player_hurt", Event_PlayerHurt); + UnhookEvent("player_spawn", Event_PlayerSpawn); + UnhookEvent("round_end", Event_RoundEnd, EventHookMode_PostNoCopy); + UnhookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre); +} + +hook_actions() +{ + HookEvent("round_mvp", Event_RoundMVP); +} + +unhook_actions() +{ + UnhookEvent("round_mvp", Event_RoundMVP); +} + +public OnClientPutInServer(client) +{ + reset_player_stats(client); +} + + +public Event_PlayerShoot(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" + // "weapon" "string" // weapon name used + + new attacker = GetClientOfUserId(GetEventInt(event, "userid")); + if (attacker > 0) + { + decl String: weapon[MAX_WEAPON_LEN]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + new weapon_index = get_weapon_index(weapon); + if (weapon_index > -1 && weapon_index < IGNORE_SHOTS_START) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_SHOTS]++; + } + } +} + + +public Event_PlayerHurt(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // player index who was hurt + // "attacker" "short" // player index who attacked + // "health" "byte" // remaining health points + // "armor" "byte" // remaining armor points + // "weapon" "string" // weapon name attacker used, if not the world + // "dmg_health" "byte" // damage done to health + // "dmg_armor" "byte" // damage done to armor + // "hitgroup" "byte" // hitgroup that was damaged + + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + + if (attacker > 0) { + decl String: weapon[MAX_WEAPON_LEN]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + new weapon_index = get_weapon_index(weapon); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++; + g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += GetEventInt(event, "dmg_health"); + new hitgroup = GetEventInt(event, "hitgroup"); + if (hitgroup < 8) + { + g_weapon_stats[attacker][weapon_index][hitgroup + LOG_HIT_OFFSET]++; + } + } + } +} + + +public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID who died + // "attacker" "short" // user ID who killed + // "weapon" "string" // weapon name killer used + // "headshot" "bool" // signals a headshot + + new attacker = GetEventInt(event, "attacker"); + if (g_loglocations) + { + LogKillLoc(GetClientOfUserId(attacker), GetClientOfUserId(GetEventInt(event, "userid"))); + } + + if (g_logheadshots && GetEventBool(event, "headshot")) + { + LogPlayerEvent(GetClientOfUserId(attacker), "triggered", "headshot"); + } + + return Plugin_Continue; +} + +public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) +{ + // this extents the original player_death by a new fields + // "userid" "short" // user ID who died + // "attacker" "short" // user ID who killed + // "weapon" "string" // weapon name killer used + // "headshot" "bool" // signals a headshot + // "dominated" "short" // did killer dominate victim with this kill + // "revenge" "short" // did killer get revenge on victim with this kill + + new victim = GetClientOfUserId(GetEventInt(event, "userid")); + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + decl String: weapon[MAX_WEAPON_LEN]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + + if (attacker <= 0 || victim <= 0) + { + return; + } + + if (g_logwstats) + { + new weapon_index = get_weapon_index(weapon); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++; + if (GetEventBool(event, "headshot")) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_HEADSHOTS]++; + } + g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++; + if (GetClientTeam(attacker) == GetClientTeam(victim)) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++; + } + dump_player_stats(victim); + } + } + if (g_logktraj) + { + LogPSKillTraj(attacker, victim, weapon); + } + if (g_logactions) + { + // these are only in Orangebox CS:S. These properties won't exist on ep1 css and should eval to 0/false. + if (GetEventInt(event, "dominated")) + { + LogPlyrPlyrEvent(attacker, victim, "triggered", "domination"); + } + else if (GetEventInt(event, "revenge")) + { + LogPlyrPlyrEvent(attacker, victim, "triggered", "revenge"); + } + } +} + +public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID on server + + new client = GetClientOfUserId(GetEventInt(event, "userid")); + if (client > 0) + { + reset_player_stats(client); + } +} + +public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) +{ + WstatsDumpAll(); +} + +public Event_RoundMVP(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "round_mvp"); +} + +public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + OnPlayerDisconnect(client); + return Plugin_Continue; +} + + +public Action:LogMap(Handle:timer) +{ + // Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats + LogMapLoad(); +} + + +public OnCvarWstatsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logwstats; + g_logwstats = GetConVarBool(g_cvar_wstats); + + if (old_value != g_logwstats) + { + if (g_logwstats) + { + hook_wstats(); + if (!g_logktraj && !g_logactions) + { + HookEvent("player_death", Event_PlayerDeath); + } + } + else + { + unhook_wstats(); + if (!g_logktraj && !g_logactions) + { + UnhookEvent("player_death", Event_PlayerDeath); + } + } + } +} + +public OnCvarActionsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logactions; + g_logactions = GetConVarBool(g_cvar_actions); + + if (old_value != g_logactions) + { + if (g_logactions) + { + hook_actions(); + if (!g_logktraj && !g_logwstats) + { + HookEvent("player_death", Event_PlayerDeath); + } + } + else + { + unhook_actions(); + if (!g_logktraj && !g_logwstats) + { + UnhookEvent("player_death", Event_PlayerDeath); + } + } + } +} + +public OnCvarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logheadshots; + g_logheadshots = GetConVarBool(g_cvar_headshots); + + if (old_value != g_logheadshots) + { + if (g_logheadshots && !g_loglocations) + { + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + else if (!g_loglocations) + { + UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + } +} + +public OnCvarLocationsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_loglocations; + g_loglocations = GetConVarBool(g_cvar_locations); + + if (old_value != g_loglocations) + { + if (g_loglocations && !g_logheadshots) + { + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + else if (!g_logheadshots) + { + UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + } +} + +public OnCvarKtrajChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logktraj; + g_logktraj = GetConVarBool(g_cvar_ktraj); + + if (old_value != g_logktraj) + { + if (g_logktraj && !g_logwstats && !g_logactions) + { + HookEvent("player_death", Event_PlayerDeath); + } + else if (!g_logwstats && !g_logactions) + { + UnhookEvent("player_death", Event_PlayerDeath); + } + } +} \ No newline at end of file diff --git a/sourcemod/scripting/superlogs-ddd.sp b/sourcemod/scripting/superlogs-ddd.sp new file mode 100644 index 0000000..a18fc44 --- /dev/null +++ b/sourcemod/scripting/superlogs-ddd.sp @@ -0,0 +1,444 @@ +/** + * HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging + * http://www.hlxcommunity.com + * Copyright (C) 2009-2012 Nicholas Hastings (psychonic) + * Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma semicolon 1 + +#include +#include +#include + +#define NAME "SuperLogs: Dino D-Day" +#define VERSION "1.1.1" + +#define MAX_LOG_WEAPONS 47 +#define MAX_WEAPON_LEN 12 +#define PREFIX_LEN 7 + +// Some DDD Things +#define WEAPON_MG42 18 +#define WEAPON_TREX 37 +#define PLAYERCLASS_TREX 8 +#define PLAYERCLASS_DILOPHOSAURUS 5 +#define CUSTOMKILL_GOAT 5 +#define GOAT_BITE 19 +#define DDD_TEAM_ALLIES 2 +#define DDD_TEAM_AXIS 3 + +new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][15]; +new const String:g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = { + "", + "mp40", + "thompson", + "shotgun", + "", + "pistol", + "", + "", + "garand", + "bar", + "luger", + "", + "piat", + "", + "mosin", + "k98", + "", + "", + "mg42", + "", + "k98sniper", + "", + "", + "", + "flechette", + "", + "", + "", + "", + "mp44", + "", + "", + "sten", + "p38", + "nagant", + "", + "", + "trex", + "", + "", + "trigger", + "stygimoloch", + "", + "", + "", + "carbine", + "greasegun" + }; + +new g_iNextHitgroup[MAXPLAYERS+1]; +new bool:g_DiloGoatBite[MAXPLAYERS+1]; + +new g_LastClass[MAXPLAYERS+1] = { -1, ... }; +new g_LastTeam[MAXPLAYERS+1] = { -1, ... }; + +new bool:g_bLate; +new bool:g_bIgnoreLog; + +PauseLogging() { g_bIgnoreLog = true; } +ResumeLogging() { g_bIgnoreLog = false; } + +#include +#include + + +public Plugin:myinfo = { + name = NAME, + author = "psychonic and FeuerSturm", + description = "Advanced logging for Dino D-Day. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats", + version = VERSION, + url = "http://www.hlxce.com" +}; + +public APLRes:AskPluginLoad2( Handle:myself, bool:late, String:error[], err_max ) +{ + decl String:szGameDir[64]; + GetGameFolderName( szGameDir, sizeof(szGameDir) ); + if ( !!strcmp( szGameDir, "dinodday" ) ) + { + strcopy( error, err_max, "This plugin is only supported on Dino D-Day" ); + return APLRes_Failure; + } + + g_bLate = late; + + return APLRes_Success; +} + +public OnPluginStart() +{ + CreatePopulateWeaponTrie(); + new Handle:hVersion = CreateConVar( "superlogs_dinodday_version", VERSION, NAME, FCVAR_NOTIFY|FCVAR_DONTRECORD ); + SetConVarString(hVersion, VERSION, false, true); + + HookEvent( "player_death", Event_PlayerDeathPre, EventHookMode_Pre ); + HookEvent( "player_death", Event_PlayerDeath, EventHookMode_Post ); + HookEvent( "player_spawn", Event_PlayerSpawn, EventHookMode_Post ); + HookEvent( "update_roundscore", Event_RoundEnd, EventHookMode_Post ); + HookEvent( "player_changeclass", Event_PlayerChangeClassPre, EventHookMode_Pre ); + HookEvent( "player_changeclass", Event_PlayerChangeClass, EventHookMode_Post ); + HookEvent( "teamplay_point_captured", Event_PointCaptured, EventHookMode_Post ); + + AddGameLogHook( OnGameLog ); + AddTempEntHook( "Shotgun Shot", OnFireBullets ); +} + +public OnAllPluginsLoaded() +{ + for ( new i = 1; i <= MaxClients; i++ ) + { + if ( IsClientInGame( i ) ) + { + OnClientPutInServer( i ); + } + } +} + +public OnMapStart() +{ + static bool:bLoggedMap = false; + if( g_bLate && !bLoggedMap ) + { + LogMapLoad(); + } + + bLoggedMap = true; + + GetTeams(); +} + +public OnClientPutInServer( client ) +{ + SDKHook( client, SDKHook_TraceAttackPost, OnTraceAttack ); + SDKHook( client, SDKHook_OnTakeDamagePost, OnTakeDamage ); + reset_player_stats( client ); + + g_LastTeam[client] = -1; + g_LastClass[client] = -1; + g_DiloGoatBite[client] = false; +} + +public Action:OnFireBullets( const String:szName[], const clients[], clientCount, Float:flDelay ) +{ + new client = TE_ReadNum( "m_iPlayer" ) + 1; + new weapon_index = TE_ReadNum( "m_iWeaponID" ); + + if( weapon_index >= 0 ) + { + if( GetEntProp(client, Prop_Send, "m_iPlayerClass") == PLAYERCLASS_TREX && weapon_index == WEAPON_MG42 ) + { + weapon_index = WEAPON_TREX; + } + g_weapon_stats[client][weapon_index][LOG_HIT_SHOTS]++; + } + + return Plugin_Continue; +} + +public OnTraceAttack( victim, attacker, inflictor, Float:damage, damagetype, ammotype, hitbox, hitgroup ) +{ + if ( hitgroup > 0 && attacker > 0 && attacker <= MaxClients && victim > 0 && victim <= MaxClients ) + { + g_iNextHitgroup[victim] = hitgroup; + } +} + +public OnTakeDamage( victim, attacker, inflictor, Float:damage, damagetype ) +{ + if ( attacker > 0 && attacker <= MaxClients && victim > 0 && victim <= MaxClients ) + { + decl String: weapon[MAX_WEAPON_LEN + PREFIX_LEN]; + GetClientWeapon( attacker, weapon, sizeof(weapon) ); + + new weapon_index = get_weapon_index(weapon[PREFIX_LEN]); + new hitgroup = g_iNextHitgroup[victim]; + if ( hitgroup < 8 ) + { + hitgroup += LOG_HIT_OFFSET; + } + + new bool:headshot = ( !IsPlayerAlive( victim ) && g_iNextHitgroup[victim] == HITGROUP_HEAD ); + if ( weapon_index > -1 ) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++; + g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += RoundToNearest( damage ); + g_weapon_stats[attacker][weapon_index][hitgroup]++; + if ( headshot ) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_HEADSHOTS]++; + LogPlayerEvent( attacker, "triggered", "headshot" ); + } + } + + g_iNextHitgroup[victim] = 0; + } +} + +public OnEntityCreated(entity, const String:classname[]) +{ + if(entity > MaxClients && IsValidEdict(entity) && StrEqual(classname, "npc_goat", true)) + { + SDKHook(entity, SDKHook_ThinkPost, OnGoatThinkPost); + } +} + +public OnGoatThinkPost(npc_goat) +{ + if(npc_goat > MaxClients && IsValidEdict(npc_goat)) + { + new client = GetEntPropEnt(npc_goat, Prop_Send, "moveparent"); + if(client >= 1 && client <= MaxClients && IsClientInGame(client) && IsPlayerAlive(client) && GetClientTeam(client) == DDD_TEAM_AXIS && GetEntProp(client, Prop_Send, "m_iPlayerClass") == PLAYERCLASS_DILOPHOSAURUS) + { + if(GetEntProp(client, Prop_Send, "m_nSequence") == GOAT_BITE) + { + g_DiloGoatBite[client] = true; + } + } + } +} + +public Action:OnGameLog( const String:szMessage[] ) +{ + if( g_bIgnoreLog ) + return Plugin_Handled; + + return Plugin_Continue; +} + +public Action:Event_PointCaptured( Handle:event, const String:name[], bool:dontBroadcast ) +{ + new client; + decl String:cappers[256]; + GetEventString(event, "cappers", cappers, sizeof(cappers)); + for (new i = 0 ; i < strlen(cappers); i++) + { + client = cappers[i]; + if(client > 0 && client <= MaxClients && IsClientInGame(client)) + { + LogPlayerEvent(client, "triggered", "point_captured"); + } + } + + return Plugin_Continue; +} + +public Action:Event_PlayerChangeClassPre( Handle:event, const String:name[], bool:dontBroadcast ) +{ + PauseLogging(); + + return Plugin_Continue; +} + +public Event_PlayerChangeClass( Handle:event, const String:name[], bool:dontBroadcast ) +{ + ResumeLogging(); +} + +public Action:Event_PlayerDeathPre( Handle:event, const String:name[], bool:dontBroadcast ) +{ + PauseLogging(); + + new customkill = GetEventInt(event, "customkill"); + if( customkill == CUSTOMKILL_GOAT ) + { + new attacker = GetClientOfUserId( GetEventInt( event, "attacker" ) ); + new victim = GetClientOfUserId( GetEventInt( event, "userid" ) ); + if(attacker == victim && g_DiloGoatBite[attacker]) + { + SetEventBroadcast(event, true); + return Plugin_Continue; + } + } + + return Plugin_Continue; +} + +public Event_PlayerDeath( Handle:event, const String:name[], bool:dontBroadcast ) +{ + ResumeLogging(); + + new victim = GetClientOfUserId( GetEventInt( event, "userid" ) ); + + if ( victim > 0 ) + { + new attacker = GetClientOfUserId( GetEventInt( event, "attacker" ) ); + + decl String:weapon[32]; + GetEventString( event, "weapon", weapon, sizeof(weapon) ); + if( attacker > 0 && attacker != victim ) + { + new weapon_index = get_weapon_index( weapon ); + if ( weapon_index > -1 ) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++; + g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++; + if ( GetClientTeam( attacker ) == GetClientTeam( victim ) ) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++; + } + } + + LogKill( attacker, victim, weapon, true ); + + new dominated = GetEventInt(event, "dominated"); + new revenge = GetEventInt(event, "revenge"); + if(dominated == 1) + { + LogPlyrPlyrEvent(attacker, victim, "triggered", "dominated"); + } + else if(revenge == 1) + { + LogPlyrPlyrEvent(attacker, victim, "triggered", "revenge"); + } + + new assisteruid = GetEventInt(event, "assister"); + if(assisteruid != -1) + { + new assister = GetClientOfUserId(assisteruid); + if(assister > 0 && attacker != assister) + { + LogPlayerEvent(assister, "triggered", "assister"); + if(GetEventInt(event, "assister_revenge") == 1) + { + LogPlyrPlyrEvent(assister, victim, "triggered", "assister_revenge"); + } + else if(GetEventInt(event, "assister_dominated") == 1) + { + LogPlyrPlyrEvent(assister, victim, "triggered", "assister_dominated"); + } + } + } + + } + else + { + new customkill = GetEventInt(event, "customkill"); + if( customkill == CUSTOMKILL_GOAT ) + { + if(g_DiloGoatBite[attacker]) + { + g_DiloGoatBite[attacker] = false; + return; + } + LogPlayerEvent(attacker, "triggered", "kill_goat"); + return; + } + + LogPlayerEvent(victim, "committed suicide with", weapon); + } + + dump_player_stats( victim ); + } +} + +public Event_PlayerSpawn( Handle:event, const String:name[], bool:dontBroadcast ) +{ + new client = GetClientOfUserId( GetEventInt( event, "userid" ) ); + if( client == 0 || !IsClientInGame(client) ) + return; + + reset_player_stats( client ); + + new currentTeam = GetClientTeam( client ); + if( currentTeam != DDD_TEAM_ALLIES && currentTeam != DDD_TEAM_AXIS ) + return; + + new currentClass = GetEntProp( client, Prop_Send, "m_iPlayerClass" ); + if( g_LastClass[client] != currentClass || g_LastTeam[client] != currentTeam ) + { + decl String:szRoleString[32]; + if( currentTeam == DDD_TEAM_ALLIES ) + Format( szRoleString, sizeof(szRoleString), "#class_blue_class%d", currentClass+1 ); + else // == DDD_TEAM_AXIS + Format( szRoleString, sizeof(szRoleString), "#class_red_class%d", currentClass+1 ); + + LogRoleChange( client, szRoleString ); + g_LastTeam[client] = currentTeam; + g_LastClass[client] = currentClass; + } + g_DiloGoatBite[client] = false; +} + +public Event_RoundEnd( Handle:event, const String:name[], bool:dontBroadcast ) +{ + new winner = GetEventInt( event, "team_that_won" ); + if( winner == DDD_TEAM_ALLIES || winner == DDD_TEAM_AXIS ) + { + decl String:round_win[32]; + Format(round_win, sizeof(round_win), "%s", winner == DDD_TEAM_ALLIES ? "roundwin_allies" : "roundwin_axis"); + LogTeamEvent(winner, "triggered", round_win); + WstatsDumpAll(); + } +} + +public OnClientDisconnect( client ) +{ + OnPlayerDisconnect( client ); +} \ No newline at end of file diff --git a/sourcemod/scripting/superlogs-dods.sp b/sourcemod/scripting/superlogs-dods.sp new file mode 100644 index 0000000..82958de --- /dev/null +++ b/sourcemod/scripting/superlogs-dods.sp @@ -0,0 +1,410 @@ +/** + * HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging + * http://www.hlxcommunity.com + * Copyright (C) 2009 Nicholas Hastings (psychonic) + * Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma semicolon 1 + +#include +#include + +#define NAME "SuperLogs: DOD:S" +#define VERSION "1.1.3" + +#define MAX_LOG_WEAPONS 27 +#define IGNORE_SHOTS_START 20 +#define MAX_WEAPON_LEN 16 + + +new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][15]; +new const String: g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = { + "amerknife", + "spade", + "colt", + "p38", + "c96", + "garand", + "m1carbine", + "k98", + "spring", + "k98_scoped", + "thompson", + "mp40", + "mp44", + "bar", + "30cal", + "mg42", + "bazooka", + "pschreck", + "frag_us", + "frag_ger", + "", + "", + "smoke_us", + "smoke_ger", + "riflegren_us", + "riflegren_ger", + "dod_bomb_target" + }; + + +new Handle:g_cvar_wstats = INVALID_HANDLE; +new Handle:g_cvar_headshots = INVALID_HANDLE; +new Handle:g_cvar_locations = INVALID_HANDLE; +new Handle:g_cvar_ktraj = INVALID_HANDLE; +new Handle:g_cvar_version = INVALID_HANDLE; + +new bool:g_logwstats = true; +new bool:g_logheadshots = true; +new bool:g_loglocations = true; +new bool:g_logktraj = true; + +#include +#include + + +public Plugin:myinfo = { + name = NAME, + author = "psychonic", + description = "Advanced logging for DOD:S. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats", + version = VERSION, + url = "http://www.hlxcommunity.com" +}; + +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 +public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) +#else +public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max) +#endif +{ + decl String:game_description[64]; + GetGameDescription(game_description, sizeof(game_description), true); + if (StrContains(game_description, "Day of Defeat", false) == -1) + { + decl String:game_folder[64]; + GetGameFolderName(game_folder, sizeof(game_folder)); + if (strncmp(game_folder, "dod", 3, false) != 0) + { + strcopy(error, err_max, "This plugin is only supported on DOD:S"); + #if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 + return APLRes_Failure; + #else + return false; + #endif + } + } +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 + return APLRes_Success; +#else + return true; +#endif +} + + +public OnPluginStart() +{ + CreatePopulateWeaponTrie(); + + g_cvar_wstats = CreateConVar("superlogs_wstats", "1", "Enable logging of weapon stats (default on)", 0, true, 0.0, true, 1.0); + g_cvar_headshots = CreateConVar("superlogs_headshots", "1", "Enable logging of headshot player action (default on)", 0, true, 0.0, true, 1.0); + g_cvar_locations = CreateConVar("superlogs_locations", "1", "Enable logging of location on player death (default on)", 0, true, 0.0, true, 1.0); + g_cvar_ktraj = CreateConVar("superlogs_ktraj", "0", "Enable Psychostats \"KTRAJ\" logging (default off)", 0, true, 0.0, true, 1.0); + HookConVarChange(g_cvar_wstats, OnCvarWstatsChange); + HookConVarChange(g_cvar_headshots, OnCvarHeadshotsChange); + HookConVarChange(g_cvar_locations, OnCvarLocationsChange); + HookConVarChange(g_cvar_ktraj, OnCvarKtrajChange); + g_cvar_version = CreateConVar("superlogs_dods_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); + + hook_wstats(); + HookEvent("player_hurt", Event_PlayerHurt); + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + HookEvent("player_death", Event_PlayerDeath); + + CreateTimer(1.0, LogMap); + + GetTeams(); +} + + +public OnMapStart() +{ + GetTeams(); +} + + +public OnConfigsExecuted() +{ + decl String:version[255]; + GetConVarString(g_cvar_version, version, sizeof(version)); + SetConVarString(g_cvar_version, version); +} + + +hook_wstats() +{ + HookEvent("dod_stats_weapon_attack", Event_PlayerShoot); + HookEvent("player_spawn", Event_PlayerSpawn); + HookEvent("dod_round_win", Event_RoundEnd, EventHookMode_PostNoCopy); + HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre); +} + +unhook_wstats() +{ + UnhookEvent("dod_stats_weapon_attack", Event_PlayerShoot); + UnhookEvent("player_spawn", Event_PlayerSpawn); + UnhookEvent("dod_round_win", Event_RoundEnd, EventHookMode_PostNoCopy); + UnhookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre); +} + +public OnClientPutInServer(client) +{ + reset_player_stats(client); +} + + +public Event_PlayerShoot(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "attacker" "short" + // "weapon" "byte" + + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + if (attacker > 0) + { + new weapon_index = GetEventInt(event, "weapon") - 1; + switch (weapon_index) + { + case 28, 29: + weapon_index = -1; + case 30: + weapon_index = 5; + case 32: + weapon_index = 8; + case 33: + weapon_index = 9; + case 34: + weapon_index = 14; + case 35: + weapon_index = 15; + case 37: + weapon_index = 12; + } + if (weapon_index > -1 && weapon_index < IGNORE_SHOTS_START) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_SHOTS]++; + } + } +} + + +public Event_PlayerHurt(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID who was hurt + // "attacker" "short" // user ID who attacked + // "weapon" "string" // weapon name attacker used + // "health" "byte" // health remaining + // "damage" "byte" // how much damage in this attack + // "hitgroup" "byte" // what hitgroup was hit + + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + new hitgroup = GetEventInt(event, "hitgroup"); + new bool:headshot = (GetEventInt(event, "health") <= 0 && hitgroup == HITGROUP_HEAD); + + if (g_logwstats && attacker > 0) + { + decl String: weapon[MAX_WEAPON_LEN]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + new weapon_index = get_weapon_index(weapon); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++; + g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += GetEventInt(event, "damage"); + if (hitgroup < 8) + { + g_weapon_stats[attacker][weapon_index][hitgroup + LOG_HIT_OFFSET]++; + } + if (headshot) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_HEADSHOTS]++; + } + } + } + if (g_logheadshots && headshot) + { + LogPlayerEvent(attacker, "triggered", "headshot"); + } +} + +public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogKillLoc(GetClientOfUserId(GetEventInt(event, "attacker")), GetClientOfUserId(GetEventInt(event, "userid"))); + + return Plugin_Continue; +} + +public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) +{ + // this extents the original player_death + // "userid" "short" // user ID who died + // "attacker" "short" // user ID who killed + // "weapon" "string" // weapon name killed used + + new victim = GetClientOfUserId(GetEventInt(event, "userid")); + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + decl String: weapon[MAX_WEAPON_LEN]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + + if (g_logwstats && victim > 0 && attacker > 0) + { + new weapon_index = get_weapon_index(weapon); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++; + g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++; + if (GetClientTeam(attacker) == GetClientTeam(victim)) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++; + } + dump_player_stats(victim); + } + } + if (g_logktraj) + { + LogPSKillTraj(attacker, victim, weapon); + } +} + +public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID on server + + new client = GetClientOfUserId(GetEventInt(event, "userid")); + if (client > 0) + { + reset_player_stats(client); + } +} + +public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) +{ + WstatsDumpAll(); +} + +public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + OnPlayerDisconnect(client); + return Plugin_Continue; +} + + +public Action:LogMap(Handle:timer) +{ + // Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats + LogMapLoad(); +} + + +public OnCvarWstatsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logwstats; + g_logwstats = GetConVarBool(g_cvar_wstats); + + if (old_value != g_logwstats) + { + if (g_logwstats) + { + hook_wstats(); + if (!g_logheadshots) + { + HookEvent("player_hurt", Event_PlayerHurt); + } + if (!g_logktraj) + { + HookEvent("player_death", Event_PlayerDeath); + } + } + else + { + unhook_wstats(); + if (!g_logheadshots) + { + UnhookEvent("player_hurt", Event_PlayerHurt); + } + if (!g_logktraj) + { + UnhookEvent("player_death", Event_PlayerDeath); + } + } + } +} + + +public OnCvarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logheadshots; + g_logheadshots = GetConVarBool(g_cvar_headshots); + + if (old_value != g_logheadshots) + { + if (g_logheadshots && !g_logwstats) + { + HookEvent("player_hurt", Event_PlayerHurt); + } + else if (!g_logwstats) + { + UnhookEvent("player_hurt", Event_PlayerHurt); + } + } +} + +public OnCvarLocationsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_loglocations; + g_loglocations = GetConVarBool(g_cvar_locations); + + if (old_value != g_loglocations) + { + if (g_loglocations) + { + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + else + { + UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + } +} + +public OnCvarKtrajChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logktraj; + g_logktraj = GetConVarBool(g_cvar_ktraj); + + if (old_value != g_logktraj) + { + if (g_logktraj && !g_logwstats) + { + HookEvent("player_death", Event_PlayerDeath); + } + else if (!g_logwstats) + { + UnhookEvent("player_death", Event_PlayerDeath); + } + } +} \ No newline at end of file diff --git a/sourcemod/scripting/superlogs-generic.sp b/sourcemod/scripting/superlogs-generic.sp new file mode 100644 index 0000000..b367d8a --- /dev/null +++ b/sourcemod/scripting/superlogs-generic.sp @@ -0,0 +1,89 @@ +/** + * HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging + * http://www.hlxcommunity.com + * Copyright (C) 2009 Nicholas Hastings (psychonic) + * Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma semicolon 1 + +#include +#include + +#define NAME "SuperLogs: Generic" +#define VERSION "1.0" + +new Handle:g_cvar_enable = INVALID_HANDLE; + +new bool:g_log = true; + +#include + + +public Plugin:myinfo = { + name = NAME, + author = "psychonic", + description = "Advanced logging. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats", + version = VERSION, + url = "http://www.hlxcommunity.com" +}; + + +public OnPluginStart() +{ + g_cvar_enable = CreateConVar("superlogs_locations", "1", "Enable logging of kill coordinates (default on)", 0, true, 0.0, true, 1.0); + + HookConVarChange(g_cvar_enable, OnCvarEnableChange); + + CreateConVar("superlogs_generic_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); + + HookEvent("player_death", Event_PlayerDeath, EventHookMode_Pre); + + CreateTimer(1.0, LogMap); +} + + +public Action:Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogKillLoc(GetClientOfUserId(GetEventInt(event, "attacker")), GetClientOfUserId(GetEventInt(event, "userid"))); + + return Plugin_Continue; +} + +public OnCvarEnableChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_log; + g_log = GetConVarBool(g_cvar_enable); + + if (old_value != g_log) + { + if (g_log) + { + HookEvent("player_death", Event_PlayerDeath, EventHookMode_Pre); + } + else + { + UnhookEvent("player_death", Event_PlayerDeath, EventHookMode_Pre); + } + } +} + +public Action:LogMap(Handle:timer) +{ + // Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats + LogMapLoad(); +} diff --git a/sourcemod/scripting/superlogs-ges.sp b/sourcemod/scripting/superlogs-ges.sp new file mode 100644 index 0000000..2119be1 --- /dev/null +++ b/sourcemod/scripting/superlogs-ges.sp @@ -0,0 +1,478 @@ +/** + * HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging + * http://www.hlxcommunity.com + * Copyright (C) 2009 Nicholas Hastings (psychonic) + * Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma semicolon 1 + +#include +#include + +#define NAME "SuperLogs: GES" +#define VERSION "1.1.2" + +#define MAX_LOG_WEAPONS 18 +#define MAX_WEAPON_LEN 13 +#define PREFIX_LEN 7 + +#define GES + + +new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][20]; +new const String:g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = { + "pp7", + "pp7_silenced", + "dd44", + "klobb", + "cmag", + "kf7", + "zmg", + "d5k", + "d5k_silenced", + "phantom", + "AR33", + "rcp90", + "shotgun", + "auto_shotgun", + "sniper_rifle", + "golden_gun", + "silver_pp7", + "golden_pp7" + }; + +new const String:g_weapon_loglist[MAX_LOG_WEAPONS][] = { + "#GE_PP7", + "#GE_PP7_SILENCED", + "#GE_DD44", + "#GE_Klobb", + "#GE_CougarMagnum", + "#GE_KF7Soviet", + "#GE_ZMG", + "#GE_D5K", + "#GE_D5K_SILENCED", + "#GE_Phantom", + "#GE_AR33", + "#GE_RCP90", + "#GE_Shotgun", + "#GE_AutoShotgun", + "#GE_SniperRifle", + "#GE_GoldenGun", + "#GE_SilverPP7", + "#GE_GoldPP7" + }; + +new Handle:g_cvar_wstats = INVALID_HANDLE; +new Handle:g_cvar_headshots = INVALID_HANDLE; +new Handle:g_cvar_locations = INVALID_HANDLE; +new Handle:g_cvar_actions = INVALID_HANDLE; +new Handle:g_cvar_classchanges = INVALID_HANDLE; + +new bool:g_logwstats = true; +new bool:g_logheadshots = true; +new bool:g_loglocations = true; +new bool:g_logactions = true; +new bool:g_logclasschanges = true; + +#include +#include + + +public Plugin:myinfo = { + name = NAME, + author = "psychonic", + description = "Advanced logging for GoldenEye: Source. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats", + version = VERSION, + url = "http://www.hlxcommunity.com" +}; + + +public OnPluginStart() +{ + CreatePopulateWeaponTrie(); + + g_cvar_wstats = CreateConVar("superlogs_wstats", "1", "Enable logging of weapon stats (default on)", 0, true, 0.0, true, 1.0); + g_cvar_headshots = CreateConVar("superlogs_headshots", "1", "Enable logging of headshot player action (default off)", 0, true, 0.0, true, 1.0); + g_cvar_locations = CreateConVar("superlogs_locations", "1", "Enable logging of location on player death (default on)", 0, true, 0.0, true, 1.0); + g_cvar_actions = CreateConVar("superlogs_actions", "1", "Enable logging of actions, such as round winner and awards won (default on)", 0, true, 0.0, true, 1.0); + g_cvar_classchanges = CreateConVar("superlogs_classchanges", "1", "Enable logging of character changes (default on)", 0, true, 0.0, true, 1.0); + HookConVarChange(g_cvar_wstats, OnCvarWstatsChange); + HookConVarChange(g_cvar_headshots, OnCvarHeadshotsChange); + HookConVarChange(g_cvar_locations, OnCvarLocationsChange); + HookConVarChange(g_cvar_actions, OnCvarActionsChange); + HookConVarChange(g_cvar_classchanges, OnCvarClasschangesChange); + CreateConVar("superlogs_ges_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); + + hook_wstats(); + HookEvent("player_hurt", Event_PlayerHurt); + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + HookEvent("player_changeident", Event_RoleChange); + HookEvent("round_end", Event_RoundEnd); + + CreateTimer(1.0, LogMap); + + GetTeams(); +} + + +public OnMapStart() +{ + GetTeams(); +} + +hook_wstats() +{ + HookEvent("player_death", Event_PlayerDeath); + HookEvent("player_shoot", Event_PlayerShoot); + HookEvent("player_spawn", Event_PlayerSpawn); + HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre); +} + +unhook_wstats() +{ + UnhookEvent("player_death", Event_PlayerDeath); + UnhookEvent("player_shoot", Event_PlayerShoot); + UnhookEvent("player_spawn", Event_PlayerSpawn); + UnhookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre); +} + +public OnClientPutInServer(client) +{ + reset_player_stats(client); +} + + +public Event_PlayerShoot(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "local" // user ID on server + // "weapon" "local" // weapon name + // "mode" "local" // weapon mode [0 normal, 1 aimed] + + new attacker = GetClientOfUserId(GetEventInt(event, "userid")); + if (g_logwstats && attacker > 0) + { + decl String:weapon[MAX_WEAPON_LEN+PREFIX_LEN]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + new weapon_index = get_weapon_index(weapon[PREFIX_LEN]); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_SHOTS]++; + } + } +} + + +public Event_PlayerHurt(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "local" // user ID who was hurt + // "attacker" "local" // user ID who attacked + // "weapon" "local" // weapon name attacker used + // "health" "local" // health remaining + // "armor" "local" // armor remaining + // "damage" "local" // how much damage in this attack + // "hitgroup" "local" // what hitgroup was hit + + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + new hitgroup = GetEventInt(event, "hitgroup"); + new bool:headshot = (GetEventInt(event, "health") <= 0 && hitgroup == HITGROUP_HEAD); + + if (g_logwstats && attacker > 0) + { + decl String: weapon[MAX_WEAPON_LEN+PREFIX_LEN]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + + new weapon_index = get_weapon_index(weapon[PREFIX_LEN]); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++; + g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += GetEventInt(event, "damage"); + if (hitgroup < 8) + { + g_weapon_stats[attacker][weapon_index][hitgroup + LOG_HIT_OFFSET]++; + } + if (headshot) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_HEADSHOTS]++; + } + } + } + if (g_logheadshots && headshot) + { + LogPlayerEvent(attacker, "triggered", "headshot"); + } +} + +public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogKillLoc(GetClientOfUserId(GetEventInt(event, "attacker")), GetClientOfUserId(GetEventInt(event, "userid"))); + + return Plugin_Continue; +} + + +public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID who died + // "attacker" "short" // user ID who killed + // "weapon" "string" // weapon name killed used + // "weaponid" "short" // GE Weapon ID (for easy comparison) + // "custom" "byte" // Used for achievements + + new victim = GetClientOfUserId(GetEventInt(event, "userid")); + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + if (g_logwstats && (victim > 0) && (attacker > 0)) + { + decl String: weapon[MAX_WEAPON_LEN+PREFIX_LEN]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + new weapon_index = get_weapon_index(weapon[PREFIX_LEN]); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++; + g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++; + if (GetClientTeam(attacker) == GetClientTeam(victim)) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++; + } + dump_player_stats(victim); + } + } +} + +public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID on server + + new client = GetClientOfUserId(GetEventInt(event, "userid")); + if (client > 0) + { + reset_player_stats(client); + } +} + +public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) +{ + if (g_logwstats) + { + WstatsDumpAll(); + } + if (g_logactions) + { + new winner = GetEventInt(event, "winnerid"); + if (winner > 0) + { + LogPlayerEvent(winner, "triggered", "Round_Win", true); + } + else + { + winner = GetEventInt(event, "teamid"); + if (winner > 0) + { + LogTeamEvent(winner, "triggered", "Round_Win_Team"); + } + } + new awards[6]; + awards[0] = GetEventInt(event, "award1_id"); + awards[1] = GetEventInt(event, "award2_id"); + awards[2] = GetEventInt(event, "award3_id"); + awards[3] = GetEventInt(event, "award4_id"); + awards[4] = GetEventInt(event, "award5_id"); + awards[5] = GetEventInt(event, "award6_id"); + + new winners[6]; + winners[0] = GetEventInt(event, "award1_winner"); + winners[1] = GetEventInt(event, "award2_winner"); + winners[2] = GetEventInt(event, "award3_winner"); + winners[3] = GetEventInt(event, "award4_winner"); + winners[4] = GetEventInt(event, "award5_winner"); + winners[5] = GetEventInt(event, "award6_winner"); + + for (new i = 0; i < 6; i++) + { + if (winners[i] > 0) + { + switch(awards[i]) + { + case 0: + LogPlayerEvent(winners[i], "triggered", "GE_AWARD_DEADLY", true); + case 1: + LogPlayerEvent(winners[i], "triggered", "GE_AWARD_HONORABLE", true); + case 2: + LogPlayerEvent(winners[i], "triggered", "GE_AWARD_PROFESSIONAL", true); + case 3: + LogPlayerEvent(winners[i], "triggered", "GE_AWARD_MARKSMANSHIP", true); + case 4: + LogPlayerEvent(winners[i], "triggered", "GE_AWARD_AC10", true); + case 5: + LogPlayerEvent(winners[i], "triggered", "GE_AWARD_FRANTIC", true); + case 6: + LogPlayerEvent(winners[i], "triggered", "GE_AWARD_WTA", true); + case 7: + LogPlayerEvent(winners[i], "triggered", "GE_AWARD_LEMMING", true); + case 8: + LogPlayerEvent(winners[i], "triggered", "GE_AWARD_LONGIN", true); + case 9: + LogPlayerEvent(winners[i], "triggered", "GE_AWARD_SHORTIN", true); + case 10: + LogPlayerEvent(winners[i], "triggered", "GE_AWARD_DISHONORABLE", true); + case 11: + LogPlayerEvent(winners[i], "triggered", "GE_AWARD_NOTAC10", true); + case 12: + LogPlayerEvent(winners[i], "triggered", "GE_AWARD_MOSTLYHARMLESS", true); + } + } + } + } +} + + +public Event_RoleChange(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "playerid" "short" + // "ident" "string" + + new client = GetEventInt(event, "playerid"); + + if (client > 0) + { + decl String:ident[32]; + GetEventString(event, "ident", ident, sizeof(ident)); + LogRoleChange(client, ident); + } +} + +public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + OnPlayerDisconnect(client); + return Plugin_Continue; +} + +public Action:LogMap(Handle:timer) +{ + // Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats + LogMapLoad(); +} + + +public OnCvarWstatsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logwstats; + g_logwstats = GetConVarBool(g_cvar_wstats); + + if (old_value != g_logwstats) + { + if (g_logwstats) + { + hook_wstats(); + if (!g_logheadshots) + { + HookEvent("player_hurt", Event_PlayerHurt); + } + if (!g_logactions) + { + HookEvent("round_end", Event_RoundEnd); + } + } + else + { + unhook_wstats(); + if (!g_logheadshots) + { + UnhookEvent("player_hurt", Event_PlayerHurt); + } + if (!g_logactions) + { + UnhookEvent("round_end", Event_RoundEnd); + } + } + } +} + + + +public OnCvarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logheadshots; + g_logheadshots = GetConVarBool(g_cvar_headshots); + + if (old_value != g_logheadshots) + { + if (g_logheadshots && !g_logwstats) + { + HookEvent("player_hurt", Event_PlayerHurt); + } + else if (!g_logwstats) + { + UnhookEvent("player_hurt", Event_PlayerHurt); + } + } +} + +public OnCvarLocationsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_loglocations; + g_loglocations = GetConVarBool(g_cvar_locations); + + if (old_value != g_loglocations) + { + if (g_loglocations) + { + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + else + { + UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + } +} + +public OnCvarActionsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logactions; + g_logactions = GetConVarBool(g_cvar_actions); + + if (old_value != g_logactions) + { + if (g_logactions && !g_logwstats) + { + HookEvent("round_end", Event_RoundEnd); + } + else if (!g_logactions) + { + UnhookEvent("round_end", Event_RoundEnd); + } + } +} + +public OnCvarClasschangesChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logclasschanges; + g_logclasschanges = GetConVarBool(g_cvar_classchanges); + + if (old_value != g_logclasschanges) + { + if (g_logclasschanges) + { + HookEvent("player_changeident", Event_RoleChange); + } + else + { + UnhookEvent("player_changeident", Event_RoleChange); + } + } +} diff --git a/sourcemod/scripting/superlogs-hl2mp.sp b/sourcemod/scripting/superlogs-hl2mp.sp new file mode 100644 index 0000000..9ea1a8c --- /dev/null +++ b/sourcemod/scripting/superlogs-hl2mp.sp @@ -0,0 +1,402 @@ +/** + * HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging + * http://www.hlxcommunity.com + * Copyright (C) 2009-2010 Nicholas Hastings (psychonic) + * Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma semicolon 1 + +#include +#include +#include + +#define NAME "SuperLogs: HL2MP" +#define VERSION "1.1.3" + +#define MAX_LOG_WEAPONS 6 +#define MAX_WEAPON_LEN 14 +#define PREFIX_LEN 7 +#define CROSSBOW 0 + + +new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][15]; +new const String:g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = { + "crossbow_bolt", + "pistol", + "357", + "smg1", + "ar2", + "shotgun" + }; + +new Handle:g_cvar_headshots = INVALID_HANDLE; +new Handle:g_cvar_locations = INVALID_HANDLE; +new Handle:g_cvar_teamplay = INVALID_HANDLE; + +new bool:g_logheadshots = true; +new bool:g_loglocations = true; + +new g_iNextHitgroup[MAXPLAYERS+1]; +new g_iNextBowHitgroup[MAXPLAYERS+1]; + +new g_bTeamPlay; + +new g_iCrossBowOwnerOffs = -1; + +new Handle:g_hBoltChecks = INVALID_HANDLE; + +#include +#include + + +public Plugin:myinfo = { + name = NAME, + author = "psychonic", + description = "Advanced logging for HL2DM & Sourceforts. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats", + version = VERSION, + url = "http://www.hlxcommunity.com" +}; + +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 +public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) +#else +public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max) +#endif +{ + decl String:szGameDesc[64]; + GetGameDescription(szGameDesc, sizeof(szGameDesc), true); + if (StrContains(szGameDesc, "Half-Life 2 Deathmatch", false) == -1 && StrContains(szGameDesc, "SourceForts", false) == -1) + { + decl String:szGameDir[64]; + GetGameFolderName(szGameDir, sizeof(szGameDir)); + if (StrContains(szGameDir, "hl2mp", false) == -1 && StrContains(szGameDir, "sourceforts", false) == -1) + { + strcopy(error, err_max, "This plugin is only supported on HL2MP & SourceForts"); + #if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 + return APLRes_Failure; + #else + return false; + #endif + } + } +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 + return APLRes_Success; +#else + return true; +#endif +} + +public OnPluginStart() +{ + CreatePopulateWeaponTrie(); + + g_cvar_headshots = CreateConVar("superlogs_headshots", "1", "Enable logging of headshot player action (default on)", 0, true, 0.0, true, 1.0); + g_cvar_locations = CreateConVar("superlogs_locations", "1", "Enable logging of location on player death (default on)", 0, true, 0.0, true, 1.0); + HookConVarChange(g_cvar_headshots, OnCvarHeadshotsChange); + HookConVarChange(g_cvar_locations, OnCvarLocationsChange); + CreateConVar("superlogs_hl2mp_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); + + g_iCrossBowOwnerOffs = FindSendPropInfo("CCrossbowBolt", "m_hOwnerEntity"); + + hook_wstats(); + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + + CreateTimer(1.0, LogMap); + + g_cvar_teamplay = FindConVar("mp_teamplay"); + if (g_cvar_teamplay != INVALID_HANDLE) + { + g_bTeamPlay = GetConVarBool(g_cvar_teamplay); + HookConVarChange(g_cvar_teamplay, OnTeamPlayChange); + } + + g_hBoltChecks = CreateStack(); +} + +public OnAllPluginsLoaded() +{ + if (GetExtensionFileStatus("sdkhooks.ext") != 1) + { + SetFailState("SDK Hooks v1.3 or higher is required for SuperLogs: HL2MP"); + } + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i)) + { + SDKHook(i, SDKHook_FireBulletsPost, OnFireBullets); + SDKHook(i, SDKHook_TraceAttackPost, OnTraceAttack); + SDKHook(i, SDKHook_OnTakeDamagePost, OnTakeDamage); + } + } +} + +public OnMapStart() +{ + GetTeams(); +} + +hook_wstats() +{ + HookEvent("player_death", Event_PlayerDeath); + HookEvent("player_spawn", Event_PlayerSpawn); + HookEvent("round_end", Event_RoundEnd, EventHookMode_PostNoCopy); + HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre); +} + +public OnClientPutInServer(client) +{ + SDKHook(client, SDKHook_FireBulletsPost, OnFireBullets); + SDKHook(client, SDKHook_TraceAttackPost, OnTraceAttack); + SDKHook(client, SDKHook_OnTakeDamagePost, OnTakeDamage); + reset_player_stats(client); +} + +public OnEntityCreated(entity, const String:classname[]) +{ + if (strcmp(classname, "crossbow_bolt") == 0) + { + PushStackCell(g_hBoltChecks, entity); + } +} + +public OnGameFrame() +{ + new bowent; + while (PopStackCell(g_hBoltChecks, bowent)) + { + if (!IsValidEntity(bowent)) + continue; + + new owner = GetEntDataEnt2(bowent, g_iCrossBowOwnerOffs); + if (owner < 0 || owner > MaxClients) + continue; + + g_weapon_stats[owner][CROSSBOW][LOG_HIT_SHOTS]++; + } +} + +public OnFireBullets(attacker, shots, String:weaponname[]) +{ + if (attacker > 0 && attacker <= MaxClients) + { + new weapon_index = get_weapon_index(weaponname[PREFIX_LEN]); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_SHOTS]++; + } + } +} + +public OnTraceAttack(victim, attacker, inflictor, Float:damage, damagetype, ammotype, hitbox, hitgroup) +{ + if (hitgroup > 0 && attacker > 0 && attacker <= MaxClients && victim > 0 && victim <= MaxClients) + { + if (IsValidEntity(inflictor)) + { + decl String:inflictorclsname[64]; + if (GetEntityNetClass(inflictor, inflictorclsname, sizeof(inflictorclsname)) && strcmp(inflictorclsname, "CCrossbowBolt") == 0) + { + g_iNextBowHitgroup[victim] = hitgroup; + return; + } + } + g_iNextHitgroup[victim] = hitgroup; + } +} + +public OnTakeDamage(victim, attacker, inflictor, Float:damage, damagetype) +{ + if (attacker > 0 && attacker <= MaxClients && victim > 0 && victim <= MaxClients) + { + decl String: weapon[MAX_WEAPON_LEN + PREFIX_LEN]; + GetClientWeapon(attacker, weapon, sizeof(weapon)); + new weapon_index = -1; + if (IsValidEntity(inflictor)) + { + decl String:inflictorclsname[64]; + if (GetEntityNetClass(inflictor, inflictorclsname, sizeof(inflictorclsname)) && strcmp(inflictorclsname, "CCrossbowBolt") == 0) + { + weapon_index = CROSSBOW; + } + } + if (weapon_index == -1) + { + weapon_index = get_weapon_index(weapon[PREFIX_LEN]); + } + new hitgroup = ((weapon_index == CROSSBOW)?g_iNextBowHitgroup[victim]:g_iNextHitgroup[victim]); + if (hitgroup < 8) + { + hitgroup += LOG_HIT_OFFSET; + } + new bool:headshot = (GetClientHealth(victim) <= 0 && hitgroup == HITGROUP_HEAD); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++; + g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += RoundToNearest(damage); + g_weapon_stats[attacker][weapon_index][hitgroup]++; + if (headshot) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_HEADSHOTS]++; + } + } + if (weapon_index == CROSSBOW) + { + g_iNextBowHitgroup[victim] = 0; + } + else + { + g_iNextHitgroup[victim] = 0; + } + } +} + + +public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID who died + // "attacker" "short" // user ID who killed + // "weapon" "string" // weapon name killer used + + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + new victim = GetClientOfUserId(GetEventInt(event, "userid")); + + if (g_loglocations) + { + LogKillLoc(attacker, victim); + } + if (g_logheadshots) + { + decl String:weapon[64]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + if (strcmp(weapon, "crossbow_bolt") == 0) + { + if (g_iNextBowHitgroup[victim] == HITGROUP_HEAD) + { + LogPlayerEvent(attacker, "triggered", "headshot"); + } + } + else if (g_iNextHitgroup[victim] == HITGROUP_HEAD) + { + LogPlayerEvent(attacker, "triggered", "headshot"); + } + } + + return Plugin_Continue; +} + +public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) +{ + // this extents the original player_death by a new fields + // "userid" "short" // user ID who died + // "attacker" "short" // user ID who killed + // "weapon" "string" // weapon name killer used + + new victim = GetClientOfUserId(GetEventInt(event, "userid")); + + if (victim > 0) + { + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + if (attacker != victim && attacker > 0) + { + decl String: weapon[MAX_WEAPON_LEN]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + new weapon_index = get_weapon_index(weapon); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++; + g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++; + if (g_bTeamPlay && GetClientTeam(attacker) == GetClientTeam(victim)) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++; + } + } + } + dump_player_stats(victim); + } +} + +public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID on server + + new client = GetClientOfUserId(GetEventInt(event, "userid")); + if (client > 0) + { + reset_player_stats(client); + } +} + +public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) +{ + WstatsDumpAll(); +} + +public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + OnPlayerDisconnect(client); + return Plugin_Continue; +} + + +public Action:LogMap(Handle:timer) +{ + // Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats + LogMapLoad(); +} + +public OnCvarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logheadshots; + g_logheadshots = GetConVarBool(g_cvar_headshots); + + if (old_value != g_logheadshots) + { + if (g_logheadshots && !g_loglocations) + { + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + else if (!g_loglocations) + { + UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + } +} + +public OnCvarLocationsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_loglocations; + g_loglocations = GetConVarBool(g_cvar_locations); + + if (old_value != g_loglocations) + { + if (g_loglocations && !g_logheadshots) + { + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + else if (!g_logheadshots) + { + UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + } +} + +public OnTeamPlayChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + g_bTeamPlay = GetConVarBool(g_cvar_teamplay); +} \ No newline at end of file diff --git a/sourcemod/scripting/superlogs-ins.sp b/sourcemod/scripting/superlogs-ins.sp new file mode 100644 index 0000000..77e1517 --- /dev/null +++ b/sourcemod/scripting/superlogs-ins.sp @@ -0,0 +1,508 @@ +/** + * HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging + * http://www.hlxcommunity.com + * Copyright (C) 2009 Nicholas Hastings (psychonic) + * Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma semicolon 1 + +#include +#include + +#define NAME "SuperLogs: Insurgency" +#define VERSION "1.1.4" + +#define MAX_LOG_WEAPONS 19 +#define MAX_WEAPON_LEN 8 + +#define PREFIX_LEN 7 + +#define INS + +new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][15]; +new const String: g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = { + "makarov", + "m9", + "sks", + "m1014", + "toz", + "svd", + "rpk", + "m249", + "m16m203", + "l42a1", + "m4med", + "m4", + "m16a4", + "m14", + "fnfal", + "aks74u", + "ak47", + "kabar", + "bayonet" + }; + +new Handle:g_cvar_wstats = INVALID_HANDLE; +new Handle:g_cvar_actions = INVALID_HANDLE; +new Handle:g_cvar_headshots = INVALID_HANDLE; +new Handle:g_cvar_chat = INVALID_HANDLE; +new Handle:g_cvar_captures = INVALID_HANDLE; +new Handle:g_cvar_locations = INVALID_HANDLE; +new Handle:g_cvar_ktraj = INVALID_HANDLE; + +new bool:g_logwstats = true; +new bool:g_logheadshots = true; +new bool:g_logactions = true; +new bool:g_logchat = true; +new bool:g_logcaptures = true; +new bool:g_loglocations = true; +new bool:g_logktraj = false; + +new g_client_last_weapon[MAXPLAYERS+1] = {-1, ...}; +new String:g_client_last_weaponstring[MAXPLAYERS+1][64]; + +#include +#include + + +public Plugin:myinfo = { + name = NAME, + author = "psychonic", + description = "Advanced logging for Insurgency. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats", + version = VERSION, + url = "http://www.hlxcommunity.com" +}; + +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 +public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) +#else +public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max) +#endif +{ + decl String:game_description[64]; + GetGameDescription(game_description, sizeof(game_description), true); + if (StrContains(game_description, "Insurgency", false) == -1) + { + decl String:game_folder[64]; + GetGameFolderName(game_folder, sizeof(game_folder)); + if (StrContains(game_folder, "insurgency", false) == -1) + { + #if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 + return APLRes_Failure; + #else + return false; + #endif + } + } +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 + return APLRes_Success; +#else + return true; +#endif +} + +public OnPluginStart() +{ + CreatePopulateWeaponTrie(); + + g_cvar_wstats = CreateConVar("superlogs_wstats", "1", "Enable logging of weapon stats (default on)", 0, true, 0.0, true, 1.0); + g_cvar_actions = CreateConVar("superlogs_actions", "1", "Enable logging of actions, such as \"Round_Win\" (default on)", 0, true, 0.0, true, 1.0); + g_cvar_headshots = CreateConVar("superlogs_headshots", "1", "Enable logging of headshot player action (default on)", 0, true, 0.0, true, 1.0); + g_cvar_chat = CreateConVar("superlogs_chat", "1", "Enable logging of chat (default on)", 0, true, 0.0, true, 1.0); + g_cvar_captures = CreateConVar("superlogs_captures", "1", "Enable logging of capturing objectives (default on)", 0, true, 0.0, true, 1.0); + g_cvar_locations = CreateConVar("superlogs_locations", "1", "Enable logging of location on player death (default on)", 0, true, 0.0, true, 1.0); + g_cvar_ktraj = CreateConVar("superlogs_ktraj", "0", "Enable Psychostats \"KTRAJ\" logging (default off)", 0, true, 0.0, true, 1.0); + HookConVarChange(g_cvar_wstats, OnCvarWstatsChange); + HookConVarChange(g_cvar_actions, OnCvarActionsChange); + HookConVarChange(g_cvar_headshots, OnCvarHeadshotsChange); + HookConVarChange(g_cvar_chat, OnCvarChatChange); + HookConVarChange(g_cvar_captures, OnCvarCapturesChange); + HookConVarChange(g_cvar_locations, OnCvarLocationsChange); + HookConVarChange(g_cvar_ktraj, OnCvarKtrajChange); + CreateConVar("superlogs_ins_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); + + hook_wstats(); + HookUserMessage(GetUserMessageId("ObjMsg"), objmsg); + HookEvent("player_hurt", Event_PlayerHurt); + HookEvent("round_end", Event_RoundEnd); + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + HookEvent("player_death", Event_PlayerDeath); + + RegConsoleCmd("say2", Command_Chat); + + CreateTimer(1.0, LogMap); + + GetTeams(true); +} + + +public OnMapStart() +{ + GetTeams(true); +} + + +hook_wstats() +{ + HookEvent("player_spawn", Event_PlayerSpawn); + HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre); +} + +unhook_wstats() +{ + UnhookEvent("player_spawn", Event_PlayerSpawn); + UnhookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre); +} + +public OnClientPutInServer(client) +{ + reset_player_stats(client); +} + +public Event_PlayerHurt(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID on server + // "attacker" "short" // CLIENT INDEX! on server of the attacker + // "dmg_health" "short" // lost health points + // "hitgroup" "short" // Hit groups + // "weapon" "string" // Weapon name, like WEAPON_AK47 + + new attacker = GetEventInt(event, "attacker"); + new victim = GetEventInt(event, "userid"); + + if (attacker > 0 && attacker != victim) + { + // ... wtf insurgency... userid is userid and attacker is client index? + //attacker = GetClientOfUserId(attacker); + + new hitgroup = GetEventInt(event, "hitgroup"); + if (hitgroup < 8) + { + hitgroup += LOG_HIT_OFFSET; + } + + if (g_logwstats) + { + decl String:weapon[MAX_WEAPON_LEN+PREFIX_LEN]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + + new weapon_index = get_weapon_index(weapon[PREFIX_LEN]); + + if (weapon_index > -1) { + g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++; + g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += GetEventInt(event, "dmg_health"); + g_weapon_stats[attacker][weapon_index][hitgroup]++; + + if (hitgroup == (HITGROUP_HEAD+LOG_HIT_OFFSET)) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_HEADSHOTS]++; + } + g_client_last_weapon[attacker] = weapon_index; + g_client_last_weaponstring[attacker] = weapon; + } + } + + if (g_logheadshots && hitgroup == (HITGROUP_HEAD+LOG_HIT_OFFSET)) + { + LogPlayerEvent(attacker, "triggered", "headshot"); + } + } +} + +public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogKillLoc(GetClientOfUserId(GetEventInt(event, "attacker")), GetClientOfUserId(GetEventInt(event, "userid"))); + + return Plugin_Continue; +} + +public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID who died + // "attacker" "short" // user ID who killed + // "type" "byte" // type of death + // "nodeath" "bool" // true if death messages were off when player died + + new victim = GetClientOfUserId(GetEventInt(event, "userid")); + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + if (attacker == 0 || victim == 0 || attacker == victim) + { + return; + } + + if (g_logwstats) + { + new weapon_index = g_client_last_weapon[attacker]; + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++; + g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++; + if (GetClientTeam(attacker) == GetClientTeam(victim)) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++; + } + dump_player_stats(victim); + } + } + + if (g_logktraj) + { + LogPSKillTraj(attacker, victim, g_client_last_weaponstring[attacker]); + } +} + +public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID on server + + new client = GetClientOfUserId(GetEventInt(event, "userid")); + if (client > 0) + { + reset_player_stats(client); + } +} + +public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) +{ + if (g_logwstats) + { + WstatsDumpAll(); + } + + if (g_logactions) + { + LogTeamEvent(GetEventInt(event, "winner"), "triggered", "Round_Win"); + } +} + +public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + OnPlayerDisconnect(client); + return Plugin_Continue; +} + + +public Action:objmsg(UserMsg:msg_id, Handle:bf, const players[], playersNum, bool:reliable, bool:init) +{ + new point = BfReadByte(bf); // Objective Point: 1 = point A, 2 = point B, 3 = point C, etc. + new capstatus = BfReadByte(bf); // Capture Status: 1 on starting capture, 2 on finished capture + new team = BfReadByte(bf); // Team Index: 1 = Marines, 2 = Insurgents + + if (capstatus == 2) + { + switch (point) + { + case 1: + LogTeamEvent(team, "triggered", "captured_a"); + case 2: + LogTeamEvent(team, "triggered", "captured_b"); + case 3: + LogTeamEvent(team, "triggered", "captured_c"); + case 4: + LogTeamEvent(team, "triggered", "captured_d"); + case 5: + LogTeamEvent(team, "triggered", "captured_e"); + } + } + + return Plugin_Continue; +} + + +public Action:Command_Chat(client, args) +{ + // method partially taken from "Insurgency Chat" by "Stevo.TVR" + + if (g_logchat) + { + new String:message[192]; + new startidx = 4; + + if (GetCmdArgString(message, sizeof(message)) < 1 || client == 0) + { + return Plugin_Continue; + } + + new lastchar = strlen(message) - 1; + if (message[lastchar] == '"') + { + message[lastchar] = '\0'; + startidx += 1; + } + + if (message[0] == '1') + { + LogPlayerEvent(client, "say", message[startidx], false); + } + else + { + LogPlayerEvent(client, "say_team", message[startidx], false); + } + } + return Plugin_Continue; +} + + +public Action:LogMap(Handle:timer) +{ + // Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats + LogMapLoad(); +} + + +public OnCvarWstatsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logwstats; + g_logwstats = GetConVarBool(g_cvar_wstats); + + if (old_value != g_logwstats) + { + if (g_logwstats) + { + hook_wstats(); + if (!g_logheadshots) + { + HookEvent("player_hurt", Event_PlayerHurt); + } + if (!g_logactions) + { + HookEvent("round_end", Event_RoundEnd); + } + if (!g_logktraj) + { + HookEvent("player_death", Event_PlayerDeath); + } + } + else + { + unhook_wstats(); + if (!g_logheadshots) + { + UnhookEvent("player_hurt", Event_PlayerHurt); + } + if (!g_logactions) + { + UnhookEvent("round_end", Event_RoundEnd); + } + if (!g_logktraj) + { + UnhookEvent("player_death", Event_PlayerDeath); + } + } + } +} + + +public OnCvarActionsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logactions; + g_logactions = GetConVarBool(g_cvar_actions); + + if (old_value != g_logactions) + { + if (g_logactions && !g_logwstats) + { + HookEvent("round_end", Event_RoundEnd); + } + else if (!g_logwstats) + { + UnhookEvent("round_end", Event_RoundEnd); + } + } +} + + +public OnCvarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logheadshots; + g_logheadshots = GetConVarBool(g_cvar_headshots); + + if (old_value != g_logheadshots) + { + if (g_logheadshots && !g_logwstats) + { + HookEvent("player_hurt", Event_PlayerHurt); + } + else if (!g_logwstats) + { + UnhookEvent("player_hurt", Event_PlayerHurt); + } + } +} + + +public OnCvarCapturesChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logcaptures; + g_logcaptures = GetConVarBool(g_cvar_captures); + + if (old_value != g_logcaptures) + { + if (g_logcaptures) + { + HookUserMessage(GetUserMessageId("ObjMsg"), objmsg); + } + else + { + UnhookUserMessage(GetUserMessageId("ObjMsg"), objmsg); + } + } +} + + +public OnCvarChatChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + g_logchat = GetConVarBool(g_cvar_chat); +} + +public OnCvarLocationsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_loglocations; + g_loglocations = GetConVarBool(g_cvar_locations); + + if (old_value != g_loglocations) + { + if (g_loglocations) + { + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + else + { + UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + } +} + +public OnCvarKtrajChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logktraj; + g_logktraj = GetConVarBool(g_cvar_ktraj); + + if (old_value != g_logktraj) + { + if (g_logktraj && !g_logwstats) + { + HookEvent("player_death", Event_PlayerDeath); + } + else if (!g_logwstats) + { + UnhookEvent("player_death", Event_PlayerDeath); + } + } +} \ No newline at end of file diff --git a/sourcemod/scripting/superlogs-l4d.sp b/sourcemod/scripting/superlogs-l4d.sp new file mode 100644 index 0000000..05518bf --- /dev/null +++ b/sourcemod/scripting/superlogs-l4d.sp @@ -0,0 +1,686 @@ +/** + * HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging + * http://www.hlxcommunity.com + * Copyright (C) 2009 Nicholas Hastings (psychonic) + * Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma semicolon 1 + +#include +#include + +#define NAME "SuperLogs: L4D" +#define VERSION "1.3.3" + +#define MAX_LOG_WEAPONS 27 +#define MAX_WEAPON_LEN 16 + + +new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][15]; +new const String:g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = { + "autoshotgun", + "rifle", + "pumpshotgun", + "smg", + "dual_pistols", + "pipe_bomb", + "hunting_rifle", + "pistol", + "prop_minigun", + "tank_claw", + "hunter_claw", + "smoker_claw", + "boomer_claw", + "smg_silenced", //l4d2 start 14 [13] + "pistol_magnum", + "rifle_ak47", + "rifle_desert", + "shotgun_chrome", + "shotgun_spas", + "sniper_military", + "rifle_sg552", + "smg_mp5", + "sniper_awp", + "sniper_scout", + "jockey_claw", + "splitter_claw", + "charger_claw" + }; + +new Handle:g_cvar_wstats = INVALID_HANDLE; +new Handle:g_cvar_actions = INVALID_HANDLE; +new Handle:g_cvar_headshots = INVALID_HANDLE; +new Handle:g_cvar_meleeoverride = INVALID_HANDLE; + +new bool:g_logwstats = true; +new bool:g_logactions = true; +new bool:g_logheadshots = false; +new bool:g_logmeleeoverride = true; + +new g_iActiveWeaponOffset; + +new bool:g_bIsL4D2; + +#include +#include + + +public Plugin:myinfo = { + name = NAME, + author = "psychonic", + description = "Advanced logging for Left 4 Dead. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats", + version = VERSION, + url = "http://www.hlxcommunity.com" +}; + +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 +public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) +#else +public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max) +#endif +{ + new String:szGameDesc[64]; + GetGameDescription(szGameDesc, sizeof(szGameDesc), true); + if (strncmp(szGameDesc, "L4D", 3, false) != 0 && StrContains(szGameDesc, "Left 4 D", false) == -1) + { + new String:szGameDir[64]; + GetGameFolderName(szGameDir, sizeof(szGameDir)); + if (StrContains(szGameDir, "left4dead", false) == -1) + { + strcopy(error, err_max, "This plugin is only supported on L4D & L4D2"); + #if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 + return APLRes_Failure; + #else + return false; + #endif + } + } +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 + return APLRes_Success; +#else + return true; +#endif +} + +public OnPluginStart() +{ + CreatePopulateWeaponTrie(); + + g_cvar_wstats = CreateConVar("superlogs_wstats", "1", "Enable logging of weapon stats (default on)", 0, true, 0.0, true, 1.0); + g_cvar_actions = CreateConVar("superlogs_actions", "1", "Enable logging of player actions, such as \"Got_The_Bomb\" (default on)", 0, true, 0.0, true, 1.0); + g_cvar_headshots = CreateConVar("superlogs_headshots", "0", "Enable logging of headshot player action (default off)", 0, true, 0.0, true, 1.0); + g_cvar_meleeoverride = CreateConVar("superlogs_meleeoverride", "1", "Enable changing \"melee\" weapon in server logs to specific weapon (L4D2-only) (default on)", 0, true, 0.0, true, 1.0); + HookConVarChange(g_cvar_wstats, OnCvarWstatsChange); + HookConVarChange(g_cvar_actions, OnCvarActionsChange); + HookConVarChange(g_cvar_headshots, OnCvarHeadshotsChange); + HookConVarChange(g_cvar_meleeoverride, OnCvarMeleeOverrideChange); + CreateConVar("superlogs_l4d_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); + + if (GuessSDKVersion() != SOURCE_SDK_LEFT4DEAD) + { + g_bIsL4D2 = true; + } + + hook_actions(); + hook_wstats(); + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + CreateTimer(120.0, FlushWeaponLogs, 0, TIMER_REPEAT); + + CreateTimer(1.0, LogMap); + + GetTeams(); + + g_iActiveWeaponOffset = FindSendPropInfo("CTerrorPlayer", "m_hActiveWeapon"); +} + + +public OnMapStart() +{ + GetTeams(); +} + + +hook_actions() +{ + HookEvent("survivor_rescued", Event_RescueSurvivor); + HookEvent("heal_success", Event_Heal); + HookEvent("revive_success", Event_Revive); + HookEvent("witch_harasser_set", Event_StartleWitch); + HookEvent("lunge_pounce", Event_Pounce); + HookEvent("player_now_it", Event_Boomered); + HookEvent("friendly_fire", Event_FF); + HookEvent("witch_killed", Event_WitchKilled); + HookEvent("award_earned", Event_Award); + if (g_bIsL4D2) + { + HookEvent("defibrillator_used", Event_Defib); + HookEvent("adrenaline_used", Event_Adrenaline); + HookEvent("jockey_ride", Event_JockeyRide); + HookEvent("charger_pummel_start", Event_ChargerPummelStart); + HookEvent("vomit_bomb_tank", Event_VomitBombTank); + HookEvent("scavenge_match_finished", Event_ScavengeEnd); + HookEvent("versus_match_finished", Event_VersusEnd); + } +} + +unhook_actions() +{ + UnhookEvent("survivor_rescued", Event_RescueSurvivor); + UnhookEvent("heal_success", Event_Heal); + UnhookEvent("revive_success", Event_Revive); + UnhookEvent("witch_harasser_set", Event_StartleWitch); + UnhookEvent("lunge_pounce", Event_Pounce); + UnhookEvent("player_now_it", Event_Boomered); + UnhookEvent("friendly_fire", Event_FF); + UnhookEvent("witch_killed", Event_WitchKilled); + UnhookEvent("award_earned", Event_Award); + + if (g_bIsL4D2) + { + UnhookEvent("defibrillator_used", Event_Defib); + UnhookEvent("adrenaline_used", Event_Adrenaline); + UnhookEvent("jockey_ride", Event_JockeyRide); + UnhookEvent("charger_pummel_start", Event_ChargerPummelStart); + UnhookEvent("vomit_bomb_tank", Event_VomitBombTank); + UnhookEvent("scavenge_match_finished", Event_ScavengeEnd); + UnhookEvent("versus_match_finished", Event_VersusEnd); + } +} + +hook_wstats() +{ + HookEvent("weapon_fire", Event_PlayerShoot); + HookEvent("weapon_fire_on_empty", Event_PlayerShoot); + HookEvent("player_hurt", Event_PlayerHurt); + HookEvent("infected_hurt", Event_InfectedHurt); + HookEvent("player_death", Event_PlayerDeath); + HookEvent("player_spawn", Event_PlayerSpawn); + HookEvent("round_end_message", Event_RoundEnd, EventHookMode_PostNoCopy); + HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre); +} + +unhook_wstats() +{ + UnhookEvent("weapon_fire", Event_PlayerShoot); + UnhookEvent("weapon_fire_on_empty", Event_PlayerShoot); + UnhookEvent("player_hurt", Event_PlayerHurt); + UnhookEvent("infected_hurt", Event_InfectedHurt); + UnhookEvent("player_death", Event_PlayerDeath); + UnhookEvent("player_spawn", Event_PlayerSpawn); + UnhookEvent("round_end_message", Event_RoundEnd, EventHookMode_PostNoCopy); + UnhookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre); +} + +public OnClientPutInServer(client) +{ + reset_player_stats(client); +} + + +public Action:FlushWeaponLogs(Handle:timer, any:index) +{ + WstatsDumpAll(); +} + +public Event_PlayerShoot(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "local" "1" // don't network this, its way too spammy + // "userid" "short" + // "weapon" "string" // used weapon name + // "weaponid" "short" // used weapon ID + // "count" "short" // number of bullets + + new attacker = GetClientOfUserId(GetEventInt(event, "userid")); + if (attacker > 0) + { + decl String: weapon[MAX_WEAPON_LEN]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + new weapon_index = get_weapon_index(weapon); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_SHOTS]++; + } + } +} + +public Event_PlayerHurt(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "local" "1" // Not networked + // "userid" "short" // user ID who was hurt + // "attacker" "short" // user id who attacked + // "attackerentid" "long" // entity id who attacked, if attacker not a player, and userid therefore invalid + // "health" "short" // remaining health points + // "armor" "byte" // remaining armor points + // "weapon" "string" // weapon name attacker used, if not the world + // "dmg_health" "short" // damage done to health + // "dmg_armor" "byte" // damage done to armor + // "hitgroup" "byte" // hitgroup that was damaged + // "type" "long" // damage type + + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + + if (attacker > 0) + { + decl String: weapon[MAX_WEAPON_LEN]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + new weapon_index = get_weapon_index(weapon); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++; + g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += GetEventInt(event, "dmg_health"); + new hitgroup = GetEventInt(event, "hitgroup"); + if (hitgroup < 8) + { + g_weapon_stats[attacker][weapon_index][hitgroup + LOG_HIT_OFFSET]++; + } + } + else if (g_logactions && !strcmp(weapon, "insect_swarm")) + { + new victim = GetClientOfUserId(GetEventInt(event, "userid")); + if (victim > 0 && IsClientInGame(victim) && GetClientTeam(victim) == 2 && !GetEntProp(victim, Prop_Send, "m_isIncapacitated")) + { + LogPlyrPlyrEvent(attacker, victim, "triggered", "spit_hurt", true); + } + } + } +} + +public Event_InfectedHurt(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "local" "1" // don't network this, its way too spammy + // "attacker" "short" // player userid who attacked + // "entityid" "long" // entity id of infected + // "hitgroup" "byte" // hitgroup that was damaged + // "amount" "short" // how much damage was done + // "type" "long" // damage type + + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + + if (attacker > 0) + { + decl String: weapon[MAX_WEAPON_LEN]; + GetClientWeapon(attacker, weapon, sizeof(weapon)); + + new weapon_index = get_weapon_index(weapon[7]); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++; + g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += GetEventInt(event, "amount"); + new hitgroup = GetEventInt(event, "hitgroup"); + if (hitgroup < 8) + { + g_weapon_stats[attacker][weapon_index][hitgroup + LOG_HIT_OFFSET]++; + } + } + } +} + +public Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast) +{ + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + + if (g_logheadshots && GetEventBool(event, "headshot")) + { + LogPlayerEvent(attacker, "triggered", "headshot"); + } + if (g_logmeleeoverride && g_bIsL4D2 && attacker > 0 && IsClientInGame(attacker)) + { + decl String:szWeapon[64]; + GetEventString(event, "weapon", szWeapon, sizeof(szWeapon)); + if (strncmp(szWeapon, "melee", 5) == 0) + { + new iWeapon = GetEntDataEnt2(attacker, g_iActiveWeaponOffset); + if (IsValidEdict(iWeapon)) + { + // They have time to switch weapons after the kill before the death event + GetEdictClassname(iWeapon, szWeapon, sizeof(szWeapon)); + if (strncmp(szWeapon[7], "melee", 5) == 0) + { + GetEntPropString(iWeapon, Prop_Data, "m_strMapSetScriptName", szWeapon, sizeof(szWeapon)); + SetEventString(event, "weapon", szWeapon); + } + } + } + } +} + +public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID who died + // "entityid" "long" // entity ID who died, userid should be used first, to get the dead Player. Otherwise, it is not a player, so use this. $ + // "attacker" "short" // user ID who killed + // "attackername" "string" // What type of zombie, so we don't have zombie names + // "attackerentid" "long" // if killer not a player, the entindex of who killed. Again, use attacker first + // "weapon" "string" // weapon name killer used + // "headshot" "bool" // signals a headshot + // "attackerisbot" "bool" // is the attacker a bot + // "victimname" "string" // What type of zombie, so we don't have zombie names + // "victimisbot" "bool" // is the victim a bot + // "abort" "bool" // did the victim abort + // "type" "long" // damage type + // "victim_x" "float" + // "victim_y" "float" + // "victim_z" "float" + + new victim = GetClientOfUserId(GetEventInt(event, "userid")); + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + + if (g_logwstats && victim > 0 && attacker > 0) + { + decl String: weapon[MAX_WEAPON_LEN]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + + new weapon_index = get_weapon_index(weapon); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++; + if (GetEventBool(event, "headshot")) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_HEADSHOTS]++; + } + g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++; + if (GetClientTeam(attacker) == GetClientTeam(victim)) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++; + } + dump_player_stats(victim); + } + } +} + +public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) +{ + WstatsDumpAll(); +} + +public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID on server + + new client = GetClientOfUserId(GetEventInt(event, "userid")); + if (client > 0) + { + reset_player_stats(client); + } +} + +public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + OnPlayerDisconnect(client); + return Plugin_Continue; +} + +public Action:LogMap(Handle:timer) +{ + // Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats + LogMapLoad(); + + return Plugin_Continue; +} + +public Event_RescueSurvivor(Handle:event, const String:name[], bool:dontBroadcast) +{ + new player = GetClientOfUserId(GetEventInt(event, "rescuer")); + + if (player > 0) + { + LogPlayerEvent(player, "triggered", "rescued_survivor", true); + } +} + +public Event_Heal(Handle:event, const String:name[], bool:dontBroadcast) +{ + new player = GetClientOfUserId(GetEventInt(event, "userid")); + + if (player > 0 && player != GetClientOfUserId(GetEventInt(event, "subject"))) + { + LogPlayerEvent(player, "triggered", "healed_teammate", true); + } +} + +public Event_Revive(Handle:event, const String:name[], bool:dontBroadcast) +{ + new player = GetClientOfUserId(GetEventInt(event, "userid")); + + if (player > 0) + { + LogPlayerEvent(player, "triggered", "revived_teammate", true); + } +} + +public Event_StartleWitch(Handle:event, const String:name[], bool:dontBroadcast) +{ + new player = GetClientOfUserId(GetEventInt(event, "userid")); + + if (player > 0 && (!g_bIsL4D2 || GetEventBool(event, "first"))) + { + LogPlayerEvent(player, "triggered", "startled_witch", true); + } +} + +public Event_Pounce(Handle:event, const String:name[], bool:dontBroadcast) +{ + new player = GetClientOfUserId(GetEventInt(event, "userid")); + new victim = GetClientOfUserId(GetEventInt(event, "victim")); + + if (victim > 0) + { + LogPlyrPlyrEvent(player, victim, "triggered", "pounce", true); + } + else + { + LogPlayerEvent(player, "triggered", "pounce", true); + } +} + +public Event_Boomered(Handle:event, const String:name[], bool:dontBroadcast) +{ + new player = GetClientOfUserId(GetEventInt(event, "attacker")); + new victim = GetClientOfUserId(GetEventInt(event, "userid")); + + if (player > 0 && (!g_bIsL4D2 || GetEventBool(event, "by_boomer"))) + { + if (victim > 0) + { + LogPlyrPlyrEvent(player, victim, "triggered", "vomit", true); + } + else + { + LogPlayerEvent(player, "triggered", "vomit", true); + } + } +} + +public Event_FF(Handle:event, const String:name[], bool:dontBroadcast) +{ + new player = GetClientOfUserId(GetEventInt(event, "attacker")); + new victim = GetClientOfUserId(GetEventInt(event, "victim")); + + if (player > 0 && player == GetClientOfUserId(GetEventInt(event, "guilty"))) + { + if (victim > 0) + { + LogPlyrPlyrEvent(player, victim, "triggered", "friendly_fire", true); + } + else + { + LogPlayerEvent(player, "triggered", "friendly_fire", true); + } + } +} + +public Event_WitchKilled(Handle:event, const String:name[], bool:dontBroadcast) +{ + if (GetEventBool(event, "oneshot")) + { + LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "cr0wned", true); + } +} + +public Event_Defib(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "defibrillated_teammate", true); +} + +public Event_Adrenaline(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "used_adrenaline", true); +} + +public Event_JockeyRide(Handle:event, const String:name[], bool:dontBroadcast) +{ + new player = GetClientOfUserId(GetEventInt(event, "userid")); + new victim = GetClientOfUserId(GetEventInt(event, "victim")); + + if (player > 0) + { + if (victim > 0) + { + LogPlyrPlyrEvent(player, victim, "triggered", "jockey_ride", true); + } + else + { + LogPlayerEvent(player, "triggered", "jockey_ride", true); + } + } +} + +public Event_ChargerPummelStart(Handle:event, const String:name[], bool:dontBroadcast) +{ + new player = GetClientOfUserId(GetEventInt(event, "userid")); + new victim = GetClientOfUserId(GetEventInt(event, "victim")); + + if (victim > 0) + { + LogPlyrPlyrEvent(player, victim, "triggered", "charger_pummel", true); + } + else + { + LogPlayerEvent(player, "triggered", "charger_pummel", true); + } +} + +public Event_VomitBombTank(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "bilebomb_tank", true); +} + +public Event_ScavengeEnd(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogTeamEvent(GetEventInt(event, "winners"), "triggered", "Scavenge_Win"); +} + +public Event_VersusEnd(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogTeamEvent(GetEventInt(event, "winners"), "triggered", "Versus_Win"); +} + +public Action:Event_Award(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // player who earned the award + // "entityid" "long" // client likes ent id + // "subjectentid" "long" // entity id of other party in the award, if any + // "award" "short" // id of award earned + + switch(GetEventInt(event, "award")) + { + case 21: + LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "hunter_punter", true); + case 27: + LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "tounge_twister", true); + case 67: + LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "protect_teammate", true); + case 80: + LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "no_death_on_tank", true); + case 136: + LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "killed_all_survivors", true); + } +} + +public OnCvarWstatsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logwstats; + g_logwstats = GetConVarBool(g_cvar_wstats); + + if (old_value != g_logwstats) + { + if (g_logwstats) + { + hook_wstats(); + } + else + { + unhook_wstats(); + } + } +} + +public OnCvarActionsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logactions; + g_logactions = GetConVarBool(g_cvar_actions); + + if (old_value != g_logactions) + { + if (g_logactions) + { + hook_actions(); + } + else + { + unhook_actions(); + } + } +} + +public OnCvarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logheadshots; + g_logheadshots = GetConVarBool(g_cvar_headshots); + + if (old_value != g_logheadshots) + { + if (g_logheadshots && !g_logmeleeoverride) + { + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + else if (!g_logmeleeoverride) + { + UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + } +} + +public OnCvarMeleeOverrideChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logmeleeoverride; + g_logmeleeoverride = GetConVarBool(g_cvar_meleeoverride); + + if (old_value != g_logmeleeoverride) + { + if (g_logmeleeoverride && !g_logheadshots) + { + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + else if (!g_logheadshots) + { + UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + } +} diff --git a/sourcemod/scripting/superlogs-neotokyo.sp b/sourcemod/scripting/superlogs-neotokyo.sp new file mode 100644 index 0000000..96f75a5 --- /dev/null +++ b/sourcemod/scripting/superlogs-neotokyo.sp @@ -0,0 +1,122 @@ +/** + * HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging + * http://www.hlxcommunity.com + * Copyright (C) 2009 Nicholas Hastings (psychonic) + * Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma semicolon 1 + +#include +#include + +#define NAME "SuperLogs: NeoTokyo" +#define VERSION "1.0.2" + +new Handle:g_cvar_headshots = INVALID_HANDLE; +new Handle:g_cvar_locations = INVALID_HANDLE; + +new bool:g_logheadshots = true; +new bool:g_loglocations = true; + +#include + + +public Plugin:myinfo = { + name = NAME, + author = "psychonic", + description = "Advanced logging for NeoTokyo. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats", + version = VERSION, + url = "http://www.hlxcommunity.com" +}; + + +public OnPluginStart() +{ + g_cvar_headshots = CreateConVar("superlogs_headshots", "1", "Enable logging of headshot player action (default off)", 0, true, 0.0, true, 1.0); + g_cvar_locations = CreateConVar("superlogs_locations", "1", "Enable logging of location on player death (default on)", 0, true, 0.0, true, 1.0); + + HookConVarChange(g_cvar_headshots, OnCvarHeadshotsChange); + HookConVarChange(g_cvar_locations, OnCvarLocationsChange); + + CreateConVar("superlogs_nts_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); + + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + + CreateTimer(1.0, LogMap); +} + + +public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast) +{ + new attacker = GetEventInt(event, "attacker"); + if (attacker > 0) + { + if (g_loglocations) + { + LogKillLoc(GetClientOfUserId(attacker), GetClientOfUserId(GetEventInt(event, "userid"))); + } + if (g_logheadshots && GetEventInt(event, "icon") == 2) + { + LogPlayerEvent(GetClientOfUserId(attacker), "triggered", "headshot"); + } + } + + return Plugin_Continue; +} + +public OnCvarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logheadshots; + g_logheadshots = GetConVarBool(g_cvar_headshots); + + if (old_value != g_logheadshots) + { + if (g_logheadshots && !g_loglocations) + { + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + else if (!g_loglocations) + { + UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + } +} + +public OnCvarLocationsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_loglocations; + g_loglocations = GetConVarBool(g_cvar_locations); + + if (old_value != g_loglocations) + { + if (g_loglocations && !g_logheadshots) + { + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + else if (!g_logheadshots) + { + UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + } +} + +public Action:LogMap(Handle:timer) +{ + // Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats + LogMapLoad(); +} diff --git a/sourcemod/scripting/superlogs-nucleardawn.sp b/sourcemod/scripting/superlogs-nucleardawn.sp new file mode 100644 index 0000000..daf1b78 --- /dev/null +++ b/sourcemod/scripting/superlogs-nucleardawn.sp @@ -0,0 +1,445 @@ +/** + * HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging + * http://www.hlxcommunity.com + * Copyright (C) 2009 Nicholas Hastings (psychonic) + * Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma semicolon 1 + +#include +#include +#include + +#define NAME "SuperLogs: Nuclear Dawn" +#define VERSION "1.0" + + +#define MAX_LOG_WEAPONS 28 +#define IGNORE_SHOTS_START 16 +#define MAX_WEAPON_LEN 20 + +#define BUNKER_DAMAGE_TIMES 10 + +#define ND_TEAM_EMP 3 +#define ND_TEAM_CT 2 + +new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][15]; +new const String:g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = { + "avenger", + "bag90", + "chaingun", + "daisy cutter", + "f2000", + "grenade launcher", + "m95", + "mp500", + "mp7", + "nx300", + "p900", + "paladin", + "pp22", + "psg", + "shotgun", + "sp5", + "x01", + "medpack", + "armblade", + "mine", + "emp grenade", + "p12 grenade", + "remote grenade", + "repair tool", + "svr grenade", + "u23 grenade", + "armknives", + "frag grenade" + }; + +#include +#include + +new g_bReadyToShoot[MAXPLAYERS+1] = {false,...}; +new g_iBunkerAttacked[2] = {0,...}; + +public Plugin:myinfo = { + name = NAME, + author = "Peace-Maker", + description = "Advanced logging. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats", + version = VERSION, + url = "http://www.hlxcommunity.com" +}; + + +public OnPluginStart() +{ + CreatePopulateWeaponTrie(); + CreateConVar("superlogs_nucleardawn_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); + + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + HookEvent("promoted_to_commander", Event_PromotedToCommander); + HookEvent("resource_captured", Event_ResourceCaptured); + HookEvent("structure_damage_sparse", Event_StructureDamageSparse); + HookEvent("structure_death", Event_StructureDeath); + + // wstats + HookEvent("player_spawn", Event_PlayerSpawn); + HookEvent("round_win", Event_RoundWin); + HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre); + + CreateTimer(1.0, LogMap); + + GetTeams(); + // GetTeamName gets #ND_Consortium and #ND_Empire in release version -.-. Game logs with CONSORTIUM and EMPIRE translated + strcopy(g_team_list[ND_TEAM_CT], sizeof(g_team_list[]), "CONSORTIUM"); + strcopy(g_team_list[ND_TEAM_EMP], sizeof(g_team_list[]), "EMPIRE"); + + for(new i=1;i<=MaxClients;i++) + { + if(IsClientInGame(i)) + OnClientPutInServer(i); + } +} + +public OnMapStart() +{ + GetTeams(); + // GetTeamName gets #ND_Consortium and #ND_Empire in release version -.-. Game logs with CONSORTIUM and EMPIRE translated + strcopy(g_team_list[ND_TEAM_CT], sizeof(g_team_list[]), "CONSORTIUM"); + strcopy(g_team_list[ND_TEAM_EMP], sizeof(g_team_list[]), "EMPIRE"); + + g_iBunkerAttacked[0] = 0; + g_iBunkerAttacked[1] = 0; +} + +public OnClientPutInServer(client) +{ + g_bReadyToShoot[client] = false; + SDKHook(client, SDKHook_TraceAttackPost, Hook_TraceAttackPost); + SDKHook(client, SDKHook_PostThink, Hook_PostThink); + SDKHook(client, SDKHook_PostThinkPost, Hook_PostThinkPost); + reset_player_stats(client); +} + +public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast) +{ + new victim = GetClientOfUserId(GetEventInt(event, "userid")); + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + + decl String:weapon[MAX_WEAPON_LEN]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + + if (attacker <= 0 || victim <= 0) + { + return Plugin_Continue; + } + + // Which commander ablilty?! + if(StrEqual(weapon, "commander ability")) + { + new damagebits = GetEventInt(event, "damagebits"); + if(damagebits & DMG_ENERGYBEAM) + { + Format(weapon, sizeof(weapon), "commander poison"); + SetEventString(event, "weapon", weapon); + } + else if(damagebits & DMG_BLAST) + { + Format(weapon, sizeof(weapon), "commander damage"); + SetEventString(event, "weapon", weapon); + } + } + + if(attacker != victim) + { + // Check if victim was commander? + if(GameRules_GetPropEnt("m_hCommanders", ND_TEAM_CT-2) == victim || GameRules_GetPropEnt("m_hCommanders", ND_TEAM_EMP-2) == victim) + LogPlayerEvent(attacker, "triggered", "killed_commander"); + } + + new weapon_index = get_weapon_index(weapon); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++; + g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++; + if (GetClientTeam(attacker) == GetClientTeam(victim)) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++; + } + dump_player_stats(victim); + } + + return Plugin_Continue; +} + +public Hook_PostThink(client) +{ + if(!IsPlayerAlive(client)) + return; + + new iWeapon = GetEntPropEnt(client, Prop_Send, "m_hActiveWeapon"); + if(iWeapon == -1 || !IsValidEdict(iWeapon)) + { + g_bReadyToShoot[client] = false; + return; + } + + decl String:sWeapon[32]; + GetEdictClassname(iWeapon, sWeapon, sizeof(sWeapon)); + if(StrContains(sWeapon, "weapon_", false) != 0) + return; + + new Float:flNextAttackTime = GetEntPropFloat(iWeapon, Prop_Send, "m_flNextPrimaryAttack"); + if(flNextAttackTime <= GetGameTime() && GetEntProp(iWeapon, Prop_Send, "m_iClip1") > 0) + g_bReadyToShoot[client] = true; + else + g_bReadyToShoot[client] = false; +} + +public Hook_PostThinkPost(client) +{ + if(!IsPlayerAlive(client)) + return; + + new iWeapon = GetEntPropEnt(client, Prop_Send, "m_hActiveWeapon"); + if(iWeapon == -1 || !IsValidEdict(iWeapon)) + return; + + decl String:sWeapon[30]; + GetEdictClassname(iWeapon, sWeapon, sizeof(sWeapon)); + if(StrContains(sWeapon, "weapon_", false) != 0) + return; + + ReplaceString(sWeapon, sizeof(sWeapon), "weapon_", "", false); + FixWeaponLoggingName(sWeapon, sizeof(sWeapon)); + + new Float:flNextAttackTime = GetEntPropFloat(iWeapon, Prop_Send, "m_flNextPrimaryAttack"); + if(g_bReadyToShoot[client] && flNextAttackTime > GetGameTime()) + { + new weapon_index = get_weapon_index(sWeapon); + if (weapon_index > -1 && weapon_index < IGNORE_SHOTS_START) + { + g_weapon_stats[client][weapon_index][LOG_HIT_SHOTS]++; + } + g_bReadyToShoot[client] = false; + } +} + +public Hook_TraceAttackPost(victim, attacker, inflictor, Float:damage, damagetype, ammotype, hitbox, hitgroup) +{ + if(IsClientInGame(victim)) + { + if(1 <= attacker <= MaxClients && IsClientInGame(attacker)) + { + new iWeapon = GetEntPropEnt(attacker, Prop_Send, "m_hActiveWeapon"); + new String:sWeapon[64]; + if(iWeapon > 0) + GetEdictClassname(iWeapon, sWeapon, sizeof(sWeapon)); + + ReplaceString(sWeapon, sizeof(sWeapon), "weapon_", "", false); + FixWeaponLoggingName(sWeapon, sizeof(sWeapon)); + + new weapon_index = get_weapon_index(sWeapon); + + // player_death + if((GetClientHealth(victim) - RoundToCeil(damage)) < 0) + { + if (hitgroup == HITGROUP_HEAD) + { + LogPlayerEvent(attacker, "triggered", "headshot"); + if (weapon_index > -1) + g_weapon_stats[attacker][weapon_index][LOG_HIT_HEADSHOTS]++; + } + } + // player_hurt + else + { + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++; + g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += RoundToCeil(damage); + if (hitgroup < 8) + { + g_weapon_stats[attacker][weapon_index][hitgroup + LOG_HIT_OFFSET]++; + } + } + } + } + } +} + +public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID on server + + new client = GetClientOfUserId(GetEventInt(event, "userid")); + if (client > 0) + { + reset_player_stats(client); + } +} + +public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + OnPlayerDisconnect(client); + return Plugin_Continue; +} + +public Event_RoundWin(Handle:event, const String:name[], bool:dontBroadcast) +{ + new team = GetEventInt(event, "team"); + if(team >= 2) + { + LogTeamEvent(team, "triggered", "round_win"); + LogTeamEvent(GetOtherTeam(team), "triggered", "round_lose"); + } + + g_iBunkerAttacked[0] = 0; + g_iBunkerAttacked[1] = 0; + WstatsDumpAll(); +} + +public Event_PromotedToCommander(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "promoted_to_commander"); +} + +public Event_ResourceCaptured(Handle:event, const String:name[], bool:dontBroadcast) +{ + new team = GetEventInt(event, "team"); + if(team >= 2) + LogTeamEvent(team, "triggered", "resource_captured"); +} + +public Event_StructureDamageSparse(Handle:event, const String:name[], bool:dontBroadcast) +{ + if(!GetEventBool(event, "bunker")) + return; + new team = GetEventInt(event, "ownerteam"); + if(team >= 2) + { + g_iBunkerAttacked[team-2]++; + + if(g_iBunkerAttacked[team-2] == BUNKER_DAMAGE_TIMES) + { + LogTeamEvent(GetOtherTeam(team), "triggered", "damaged_opposite_bunker"); + g_iBunkerAttacked[team-2] = 0; + } + } +} + +public Event_StructureDeath(Handle:event, const String:name[], bool:dontBroadcast) +{ + new iEnt = GetEventInt(event, "entindex"); + new iAttacker = GetClientOfUserId(GetEventInt(event, "attacker")); + if(iAttacker > 0 && iAttacker <= MaxClients && iEnt != -1 && IsValidEntity(iEnt)) + { + decl String:sBuffer[32]; + GetEdictClassname(iEnt, sBuffer, sizeof(sBuffer)); + PrintToChatAll("%N destroyed %s", iAttacker, sBuffer); + + if(StrEqual(sBuffer, "struct_armoury")) + { + LogPlayerEvent(iAttacker, "triggered", "armoury_destroyed"); + } + else if(StrEqual(sBuffer, "struct_artillery_explosion")) + { + LogPlayerEvent(iAttacker, "triggered", "artillery_destroyed"); + } + else if(StrEqual(sBuffer, "struct_assembler")) + { + LogPlayerEvent(iAttacker, "triggered", "assembler_destroyed"); + } + else if(StrEqual(sBuffer, "struct_flamethrower_turret")) + { + LogPlayerEvent(iAttacker, "triggered", "flamethrowerturret_destroyed"); + } + else if(StrEqual(sBuffer, "struct_fusion_reactor")) + { + LogPlayerEvent(iAttacker, "triggered", "wirelessrepeater_destroyed"); + } + else if(StrEqual(sBuffer, "struct_power_station")) + { + LogPlayerEvent(iAttacker, "triggered", "powerstation_destroyed"); + } + else if(StrEqual(sBuffer, "struct_radar")) + { + LogPlayerEvent(iAttacker, "triggered", "radar_destroyed"); + } + else if(StrEqual(sBuffer, "struct_power_relay")) + { + LogPlayerEvent(iAttacker, "triggered", "powerrelay_destroyed"); + } + else if(StrEqual(sBuffer, "struct_rocket_turret")) + { + LogPlayerEvent(iAttacker, "triggered", "rocketturret_destroyed"); + } + else if(StrEqual(sBuffer, "struct_sonic_turret")) + { + LogPlayerEvent(iAttacker, "triggered", "sonicturret_destroyed"); + } + else if(StrEqual(sBuffer, "struct_support_station")) + { + LogPlayerEvent(iAttacker, "triggered", "supply_destroyed"); + } + else if(StrEqual(sBuffer, "struct_transport_gate")) + { + LogPlayerEvent(iAttacker, "triggered", "transportgate_destroyed"); + } + else if(StrEqual(sBuffer, "struct_machinegun_turret")) + { + LogPlayerEvent(iAttacker, "triggered", "machineguneturret_destroyed"); + } + } +} + +public Action:LogMap(Handle:timer) +{ + // Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats + LogMapLoad(); +} + +stock GetOtherTeam(team) +{ + if(team == 2) + return 3; + else if(team == 3) + return 2; + + return team; +} + +stock FixWeaponLoggingName(String:sWeapon[], maxlength) +{ + if(StrEqual(sWeapon, "daisycutter")) + strcopy(sWeapon, maxlength, "daisy cutter"); + else if(StrEqual(sWeapon, "emp_grenade")) + strcopy(sWeapon, maxlength, "emp grenade"); + else if(StrEqual(sWeapon, "frag_grenade")) + strcopy(sWeapon, maxlength, "frag grenade"); + else if(StrEqual(sWeapon, "grenade_launcher")) + strcopy(sWeapon, maxlength, "grenade launcher"); + else if(StrEqual(sWeapon, "p12_grenade")) + strcopy(sWeapon, maxlength, "p12 grenade"); + else if(StrEqual(sWeapon, "remote_grenade")) + strcopy(sWeapon, maxlength, "remote grenade"); + //else if(StrEqual(sWeapon, "repair_tool")) + // strcopy(sWeapon, maxlength, "repair tool"); + else if(StrEqual(sWeapon, "u23_grenade")) + strcopy(sWeapon, maxlength, "u23 grenade"); +} \ No newline at end of file diff --git a/sourcemod/scripting/superlogs-pvkii.sp b/sourcemod/scripting/superlogs-pvkii.sp new file mode 100644 index 0000000..872ab4a --- /dev/null +++ b/sourcemod/scripting/superlogs-pvkii.sp @@ -0,0 +1,326 @@ +/** + * HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging + * http://www.hlxcommunity.com + * Copyright (C) 2009-2010 Nicholas Hastings (psychonic) + * Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma semicolon 1 + +#include +#include + +#define NAME "SuperLogs: PVKII" +#define VERSION "1.0.1" + +#define MAX_LOG_WEAPONS 6 +#define MAX_WEAPON_LEN 14 + + +new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][15]; +new const String:g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = { + "blunderbuss", + "flintlock", + "arrow", + "crossbow_bolt", + "throwaxe", + "javalin" + }; + +new Handle:g_cvar_ktraj = INVALID_HANDLE; + +new bool:g_logktraj = true; + +new bool:g_bHasChest[MAXPLAYERS+1] = {false,...}; +new bool:g_bHasGrail[MAXPLAYERS+1] = {false,...}; + +new g_iLastClass = -1; + +new const String:g_szClassNames[][] = { + "Skirmisher", + "Captain", + "", + "Berserker", + "Huscarl", + "Gestir", + "Heavy Knight", + "Archer" +}; + +#define PVKII + +#include +#include + + +public Plugin:myinfo = { + name = NAME, + author = "psychonic", + description = "Advanced logging for PVKII. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats", + version = VERSION, + url = "http://www.hlxce.com" +}; + +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 +public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) +#else +public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max) +#endif +{ + decl String:szGameDesc[64]; + GetGameDescription(szGameDesc, sizeof(szGameDesc), true); + if (StrContains(szGameDesc, "PVKII", false) == -1) + { + decl String:szGameDir[64]; + GetGameFolderName(szGameDir, sizeof(szGameDir)); + if (StrContains(szGameDir, "pvkii", false) == -1) + { + strcopy(error, err_max, "This plugin is only supported on Pirate, Vikings, and Knights"); + #if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 + return APLRes_Failure; + #else + return false; + #endif + } + } +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 + return APLRes_Success; +#else + return true; +#endif +} + + +public OnPluginStart() +{ + CreatePopulateWeaponTrie(); + + g_cvar_ktraj = CreateConVar("superlogs_ktraj", "0", "Enable Psychostats \"KTRAJ\" logging (default off)", 0, true, 0.0, true, 1.0); + HookConVarChange(g_cvar_ktraj, OnCvarKtrajChange); + CreateConVar("superlogs_pvkii_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); + + HookEvent("player_ranged_impact", Event_PlayerRangedImpact); + HookEvent("player_death", Event_PlayerDeath); + HookEvent("player_spawn", Event_PlayerSpawn); + HookEvent("update_mvp_panel", Event_RoundEnd); + HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre); + + HookEvent("player_nemesis", Event_PlayerNemesis); + HookEvent("player_revenge", Event_PlayerRevenge); + HookEvent("player_objective", Event_PlayerObjective); + HookEvent("grail_pickup", Event_GrailPickup); + HookEvent("grail_drop", Event_GrailDrop); + HookEvent("chest_pickup", Event_ChestPickup); + HookEvent("chest_drop", Event_ChestDrop); + HookEvent("chest_capture", Event_ChestCapture); + + CreateTimer(1.0, LogMap); +} + +public OnMapStart() +{ + GetTeams(); +} + +public OnClientPutInServer(client) +{ + g_iLastClass = -1; + reset_player_stats(client); + g_bHasChest[client] = false; + g_bHasGrail[client] = false; +} + +public Event_PlayerNemesis(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogPlyrPlyrEvent(GetClientOfUserId(GetEventInt(event, "userid")), GetClientOfUserId(GetEventInt(event, "victim")), "triggered", "domination"); +} + +public Event_PlayerRevenge(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogPlyrPlyrEvent(GetClientOfUserId(GetEventInt(event, "userid")), GetClientOfUserId(GetEventInt(event, "victim")), "triggered", "revenge"); +} + +public Event_PlayerObjective(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "obj_complete"); +} + +public Event_ChestPickup(Handle:event, const String:name[], bool:dontBroadcast) +{ + g_bHasChest[GetClientOfUserId(GetEventInt(event, "userid"))] = true; +} + +public Event_ChestDrop(Handle:event, const String:name[], bool:dontBroadcast) +{ + g_bHasChest[GetClientOfUserId(GetEventInt(event, "userid"))] = false; +} + +public Event_GrailPickup(Handle:event, const String:name[], bool:dontBroadcast) +{ + g_bHasGrail[GetClientOfUserId(GetEventInt(event, "userid"))] = true; +} + +public Event_GrailDrop(Handle:event, const String:name[], bool:dontBroadcast) +{ + g_bHasGrail[GetClientOfUserId(GetEventInt(event, "userid"))] = false; +} + +public Event_ChestCapture(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "chest_capture"); +} + +public Event_PlayerRangedImpact(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID of player who fired + // "victim" "short" // entindex of entity that was hit (if any) + // "weapon" "string" // weapon that was fired + // "damage" "float" // how much damage was dealt, if 0 obviously missed or blocked by shield if victim is set + + new attacker = GetClientOfUserId(GetEventInt(event, "userid")); + if (attacker == 0 || !IsClientInGame(attacker)) + return; + + decl String:weapon[MAX_WEAPON_LEN]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + new weapon_index = get_weapon_index(weapon); + if (weapon_index == -1) + return; + + if (!strcmp(weapon, "blunderbuss")) + { + // buckshot of 8 + g_weapon_stats[attacker][weapon_index][LOG_HIT_SHOTS] += 8; + } + else + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_SHOTS]++; + } + + new victim = GetEventInt(event, "victim"); + if (victim < 1 || victim > MaxClients || !IsClientInGame(victim)) + return; + + g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++; + g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += RoundToNearest(GetEventFloat(event, "damage")); +} + +public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) +{ + // this extents the original player_death by a new fields + // "userid" "short" // user ID who died + // "attacker" "short" // user ID who killed + // "weapon" "string" // weapon name killer used + + new victim = GetClientOfUserId(GetEventInt(event, "userid")); + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + decl String: weapon[MAX_WEAPON_LEN]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + + if (victim > 0 && attacker > 0) + { + new weapon_index = get_weapon_index(weapon); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++; + g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++; + if (GetClientTeam(attacker) == GetClientTeam(victim)) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++; + } + } + dump_player_stats(victim); + + LogPlyrPlyrEvent(GetEventInt(event, "assistid"), victim, "triggered", "kill assist", true); + + if (g_bHasGrail[victim]) + { + LogPlayerEvent(attacker, "triggered", "grail_defend"); + } + if (g_bHasChest[victim]) + { + LogPlayerEvent(attacker, "triggered", "chest_defend"); + } + g_bHasGrail[victim] = false; + g_bHasChest[victim] = false; + } + + if (g_logktraj) + { + LogPSKillTraj(attacker, victim, weapon); + } +} + +public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID on server + + new client = GetClientOfUserId(GetEventInt(event, "userid")); + if (client > 0) + { + reset_player_stats(client); + if (IsClientInGame(client)) + { + new iCurrentClass = GetEntProp(client, Prop_Send, "m_iPlayerClass"); + if (iCurrentClass > -1 && iCurrentClass != g_iLastClass) + { + LogRoleChange(client, g_szClassNames[iCurrentClass]); + } + g_iLastClass = iCurrentClass; + } + } +} + +public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) +{ + new winner = GetEventInt(event, "winner"); + if (winner > 1) + { + LogTeamEvent(winner, "triggered", "Round_Win"); + } + + LogPlayerEvent(GetEventInt(event, "pid_1"), "triggered", "mvp1"); + LogPlayerEvent(GetEventInt(event, "pid_2"), "triggered", "mvp2"); + LogPlayerEvent(GetEventInt(event, "pid_3"), "triggered", "mvp3"); + + for (new i = 1; i <= MaxClients; i++) + { + g_bHasChest[i] = false; + g_bHasGrail[i] = false; + } + + WstatsDumpAll(); +} + +public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + OnPlayerDisconnect(client); + return Plugin_Continue; +} + + +public Action:LogMap(Handle:timer) +{ + // Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats + LogMapLoad(); +} + +public OnCvarKtrajChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + g_logktraj = GetConVarBool(g_cvar_ktraj); +} \ No newline at end of file diff --git a/sourcemod/scripting/superlogs-tf2.sp b/sourcemod/scripting/superlogs-tf2.sp new file mode 100644 index 0000000..14d0b52 --- /dev/null +++ b/sourcemod/scripting/superlogs-tf2.sp @@ -0,0 +1,1512 @@ +/* + * HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging + * http://www.hlxcommunity.com + * Copyright (C) 2009-2010 Nicholas Hastings (psychonic) + * Copyright (C) 2010 Thomas "CmptrWz" Berezansky + * Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#pragma semicolon 1 + +#include +#include // NOTE: Gives us tf2 AND sdktools +#include // http://forums.alliedmods.net/showthread.php?t=100084 +#undef REQUIRE_EXTENSIONS +#include // http://forums.alliedmods.net/showthread.php?t=106748 +#define REQUIRE_EXTENSIONS + +#define VERSION "2.0.32" +#define NAME "SuperLogs: TF2" + +#define UNLOCKABLE_BIT (1<<30) +#define MAX_LOG_WEAPONS 28 +#define MAX_WEAPON_LEN 29 +#define MAX_BULLET_WEAPONS 14 +#define MAX_UNLOCKABLE_WEAPONS 6 +#define MAX_LOADOUT_SLOTS 8 +#define WEAPON_PREFIX_LENGTH 10 +#define WEAPON_FULL_LENGTH (WEAPON_PREFIX_LENGTH + MAX_WEAPON_LEN) +#define TELEPORT_AGAIN_TIME 10.0 +#define OBJ_DISPENSER 0 +#define OBJ_TELEPORTER_ENTRANCE 1 +#define OBJ_TELEPORTER_EXIT 2 +#define OBJ_SENTRYGUN 3 +#define OBJ_ATTACHMENT_SAPPER 4 +#define OBJ_SENTRYGUN_MINI 20 +#define ITEMINDEX_DEMOSHIELD 131 +#define ITEMINDEX_GUNBOATS 133 +#define JUMP_NONE 0 +#define JUMP_ROCKET_START 1 +#define JUMP_ROCKET 2 +#define JUMP_STICKY 3 +#define LOG_SHOTS 0 +#define LOG_HITS 1 +#define LOG_KILLS 2 +#define LOG_HEADSHOTS 3 +#define LOG_TEAMKILLS 4 +#define LOG_DAMAGE 5 +#define LOG_DEATHS 6 +#define LUNCHBOX_CHOCOLATE 159 +#define LUNCHBOX_STEAK 311 + +public Plugin:myinfo = { + name = NAME, + author = "Thomas \"CmptrWz\" Berezansky & psychonic", + description = "Advanced logging for TF2. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats", + version = VERSION, + url = "http://www.hlxcommunity.com" +}; + +// Convars we need to monitor +new Handle:cvar_crits; // tf_weapon_criticals - Needs to be on for weapon stats + +// Our convars +new Handle:cvar_actions; +new Handle:cvar_teleports; +new Handle:cvar_teleports_again; +new Handle:cvar_headshots; +new Handle:cvar_backstabs; +new Handle:cvar_sandvich; +new Handle:cvar_fire; +new Handle:cvar_wstats; +new Handle:cvar_heals; +new Handle:cvar_rolelogfix; +new Handle:cvar_objlogfix; + +// Booleans to keep track of them +new bool:b_actions; +new bool:b_teleports; +new bool:b_teleports_again; +new bool:b_headshots; +new bool:b_backstabs; +new bool:b_sandvich; +new bool:b_fire; +new bool:b_wstats; +new bool:b_heals; +new bool:b_rolelogfix; +new bool:b_objlogfix; + +// Monitoring outside libraries/cvars +new bool:b_sdkhookloaded = false; + +// Weapon trie +new Handle:h_weapontrie; +// Weapon Stats +new weaponStats[MAXPLAYERS+1][MAX_LOG_WEAPONS][7]; +new nextHurt[MAXPLAYERS+1] = {-1, ...}; +// Loadout Info +new playerLoadout[MAXPLAYERS+1][MAX_LOADOUT_SLOTS][2]; +new bool:playerLoadoutUpdated[MAXPLAYERS+1]; +new Handle:itemsKv; +new Handle:slotsTrie; +// Stunball id (so we aren't looking it up in gameframe) +new stunBallId = -1; +// Stacks for "object destroyed at spawn" +new Handle:h_objList[MAXPLAYERS+1]; +// Time storage for the same +new Float:f_objRemoved[MAXPLAYERS+1]; +// Stunball Stack +new Handle:h_stunBalls; +// Wearables Stack +new Handle:h_wearables; + +// Teleporter Stat-Padding Fix: Keep track of last use of teleporter +new Float:f_lastTeleport[MAXPLAYERS+1][MAXPLAYERS+1]; +// Heals +new healPoints[MAXPLAYERS+1]; +// Rocket/Sticky Jump Status +new jumpStatus[MAXPLAYERS+1]; +// Last dalokohs eaten +new Float:dalokohs[MAXPLAYERS+1]; + +// Last known class of players +// Likely less intensive to keep track of this for sound hooks +new TFClassType:playerClass[MAXPLAYERS+1]; + +// Is player carrying a building +new bool:g_bCarryingObject[MAXPLAYERS+1] = {false,...}; +new g_iCarryingOffs = -1; +new bool:g_bBlockLog = false; + +// Bullet Weapons Variable +new bulletWeapons = 0; + +// Arrays +// ONLY RULE OF THIS WEAPON LIST: +// Unlockable variants of a weapon go AFTER the original variant +// This will need to be tweaked if they make a second alt version of a weapon we track +new const String:weaponList[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = { + "ball", + "flaregun", + "minigun", + "natascha", + "pistol", + "pistol_scout", + "revolver", + "ambassador", + "scattergun", + "force_a_nature", + "shotgun_hwg", + "shotgun_primary", + "shotgun_pyro", + "shotgun_soldier", + "smg", + "sniperrifle", + "syringegun_medic", + "blutsauger", + "tf_projectile_arrow", + "tf_projectile_pipe", + "tf_projectile_pipe_remote", + "sticky_resistance", + "tf_projectile_rocket", + "rocketlauncher_directhit", + "deflect_rocket", + "deflect_promode", + "deflect_flare", + "deflect_arrow" +}; +// This list has none of the above rules +new const String:weaponBullet[MAX_BULLET_WEAPONS][MAX_WEAPON_LEN] = { + "ambassador", + "force_a_nature", + "minigun", + "natascha", + "pistol", + "pistol_scout", + "revolver", + "scattergun", + "shotgun_hwg", + "shotgun_primary", + "shotgun_pyro", + "shotgun_soldier", + "smg", + "sniperrifle" +}; +// This list is the list of weapons with an alternate in the next slot in the first list +new const String:weaponUnlockables[MAX_UNLOCKABLE_WEAPONS][MAX_WEAPON_LEN] = { + "minigun", + "revolver", + "scattergun", + "syringegun_medic", + "tf_projectile_pipe_remote", + "tf_projectile_rocket" +}; + +public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) +{ + MarkNativeAsOptional("SDKHook"); // This needs to be marked optional for a number of reasons + return APLRes_Success; +} + +public OnPluginStart() +{ + CreateConVar("superlogs_tf_version", VERSION, NAME, FCVAR_PLUGIN|FCVAR_NOTIFY); + + cvar_crits = FindConVar("tf_weapon_criticals"); + cvar_actions = CreateConVar("superlogs_actions", "1", "Enable logging of most player actions, such as \"stun\" (default on)", 0, true, 0.0, true, 1.0); + cvar_teleports = CreateConVar("superlogs_teleports", "1", "Enable logging of teleports (default on)", 0, true, 0.0, true, 1.0); + cvar_teleports_again = CreateConVar("superlogs_teleports_again", "1", "Repeated use of same teleporter in 10 seconds adds _again to event (default on)", 0, true, 0.0, true, 1.0); + cvar_headshots = CreateConVar("superlogs_headshots", "0", "Enable logging of headshot player action (default off)", 0, true, 0.0, true, 1.0); + cvar_backstabs = CreateConVar("superlogs_backstabs", "1", "Enable logging of backstab player action (default on)", 0, true, 0.0, true, 1.0); + cvar_sandvich = CreateConVar("superlogs_sandvich", "1", "Enable logging of sandvich eating (default on)", 0, true, 0.0, true, 1.0); + cvar_fire = CreateConVar("superlogs_fire", "1", "Enable logging of fiery arrows as a separate weapon from regular arrows (default on)", 0, true, 0.0, true, 1.0); + cvar_wstats = CreateConVar("superlogs_wstats", "1", "Enable logging of weapon stats (default on, only works when tf_weapon_criticals is 1)", 0, true, 0.0, true, 1.0); + cvar_heals = CreateConVar("superlogs_heals", "1", "Enable logging of healpoints upon death (default on)", 0, true, 0.0, true, 1.0); + cvar_rolelogfix = CreateConVar("superlogs_rolelogfix", "1", "Enable logging of healpoints upon death (default on)", 0, true, 0.0, true, 1.0); + cvar_objlogfix = CreateConVar("superlogs_objlogfix", "1", "Enable logging of owner object destruction on team/class change (default on)", 0, true, 0.0, true, 1.0); + + HookConVarChange(cvar_crits,OnConVarStatsChange); + HookConVarChange(cvar_actions,OnConVarActionsChange); + HookConVarChange(cvar_teleports,OnConVarTeleportsChange); + HookConVarChange(cvar_teleports_again,OnConVarTeleportsAgainChange); + HookConVarChange(cvar_headshots,OnConVarHeadshotsChange); + HookConVarChange(cvar_backstabs,OnConVarBackstabsChange); + HookConVarChange(cvar_sandvich,OnConVarSandvichChange); + HookConVarChange(cvar_fire,OnConVarFireChange); + HookConVarChange(cvar_wstats,OnConVarStatsChange); + HookConVarChange(cvar_heals,OnConVarHealsChange); + HookConVarChange(cvar_rolelogfix,OnConVarRolelogfixChange); + HookConVarChange(cvar_objlogfix,OnConVarObjlogfixChange); + + h_stunBalls = CreateStack(); + h_wearables = CreateStack(); + itemsKv = CreateKeyValues("items_game"); + if(FileToKeyValues(itemsKv, "scripts/items/items_game.txt")) + KvJumpToKey(itemsKv, "items"); + slotsTrie = CreateTrie(); + SetTrieValue(slotsTrie, "primary", 0); + SetTrieValue(slotsTrie, "secondary", 1); + SetTrieValue(slotsTrie, "melee", 2); + SetTrieValue(slotsTrie, "pda", 3); + SetTrieValue(slotsTrie, "pda2", 4); + SetTrieValue(slotsTrie, "building", 5); + SetTrieValue(slotsTrie, "head", 6); + SetTrieValue(slotsTrie, "misc", 7); + + // Populate the Weapon Trie + // Creates it too, technically + PopulateWeaponTrie(); + + // Populate stacks + for(new i = 0; i <= MAXPLAYERS; i++) + h_objList[i] = CreateStack(); + + // Hook Events + HookEvent("player_death", Event_PlayerDeath); + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + HookEvent("object_destroyed", Event_ObjectDestroyed); + HookEvent("object_destroyed", Event_ObjectDestroyedPre, EventHookMode_Pre); + HookEvent("player_builtobject", Event_PlayerBuiltObject); + HookEvent("player_builtobject", Event_PlayerBuiltObjectPre, EventHookMode_Pre); + HookEvent("player_hurt", Event_PlayerHurt); + HookEvent("player_spawn", Event_PlayerSpawn); + HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre); + HookEvent("post_inventory_application", Event_PostInventoryApplication); + + HookEvent("arena_win_panel", Event_WinPanel); + HookEvent("teamplay_win_panel", Event_WinPanel); + + g_iCarryingOffs = FindSendPropInfo("CTFPlayer", "m_bCarryingObject"); + + //AutoExecConfig(); // Create/request load of config file + AutoExecConfig(false); // Dont auto-make, but load if you find it. + + CreateTimer(1.0, LogMap); + + AddGameLogHook(OnGameLog); +} + +public OnMapStart() +{ + GetTeams(); // Loghelper says to put this here. Who am I to argue? +} + +public OnConfigsExecuted() +{ + OnConVarStatsChange(cvar_wstats, "", ""); + OnConVarActionsChange(cvar_actions, "", ""); + OnConVarTeleportsChange(cvar_teleports, "", ""); + OnConVarTeleportsAgainChange(cvar_teleports_again, "", ""); + OnConVarHeadshotsChange(cvar_headshots, "", ""); + OnConVarBackstabsChange(cvar_backstabs, "", ""); + OnConVarSandvichChange(cvar_sandvich, "", ""); + OnConVarFireChange(cvar_fire, "", ""); + OnConVarHealsChange(cvar_heals, "", ""); + OnConVarRolelogfixChange(cvar_rolelogfix, "", ""); + OnConVarObjlogfixChange(cvar_objlogfix, "", ""); +} + +public Action:TF2_CalcIsAttackCritical(attacker, weapon, String:weaponname[], &bool:result) +{ + if(b_wstats && attacker > 0 && attacker <= MaxClients) + { + new weapon_index = GetWeaponIndex(weaponname[WEAPON_PREFIX_LENGTH], attacker); + if(weapon_index != -1) + { + weaponStats[attacker][weapon_index][LOG_SHOTS]++; + if((1< 0 && attacker <= MaxClients && attacker != victim && inflictor > MaxClients && damage > 0.0 && IsValidEntity(inflictor) && (GetEntityFlags(victim) & (FL_ONGROUND | FL_INWATER)) == 0) + { + decl String:weapon[WEAPON_FULL_LENGTH]; + GetEdictClassname(inflictor, weapon, sizeof(weapon)); + if(weapon[3] == 'p' && weapon[4] == 'r') // Eliminate pumpkin bomb with the r + { + switch(weapon[14]) + { + case 'r': + { + LogPlayerEvent(attacker, "triggered", "airshot_rocket"); + if(jumpStatus[attacker] == JUMP_ROCKET) + LogPlayerEvent(attacker, "triggered", "air2airshot_rocket"); + } + case 'p': + { + if(weapon[18] != 0) + { + LogPlayerEvent(attacker, "triggered", "airshot_sticky"); + if(jumpStatus[attacker] == JUMP_STICKY) + LogPlayerEvent(attacker, "triggered", "air2airshot_sticky"); + } + else + { + LogPlayerEvent(attacker, "triggered", "airshot_pipebomb"); + if(jumpStatus[attacker] == JUMP_STICKY) + LogPlayerEvent(attacker, "triggered", "air2airshot_pipebomb"); + } + } + case 'a': + LogPlayerEvent(attacker, "triggered", "airshot_arrow"); + case 'f': + if(damage > 10.0) + LogPlayerEvent(attacker, "triggered", "airshot_flare"); + } + } + } + return Plugin_Continue; +} + +public OnTakeDamage_Post(victim, attacker, inflictor, Float:damage, damagetype) +{ + if(b_wstats && attacker > 0 && attacker <= MaxClients) + { + new weapon_index = -1; + new idamage = RoundFloat(damage); + decl String:weapon[WEAPON_FULL_LENGTH]; + if (inflictor <= MaxClients) // Inflictor is a player + { + if (damagetype & DMG_BURN) + return; + if(inflictor == attacker && damagetype & 1 && damage == 1000.0) // Telefrag + return; + GetClientWeapon(attacker, weapon, sizeof(weapon)); + weapon_index = GetWeaponIndex(weapon[WEAPON_PREFIX_LENGTH], attacker); + } + else if (IsValidEdict(inflictor)) + { + GetEdictClassname(inflictor, weapon, sizeof(weapon)); + if (weapon[WEAPON_PREFIX_LENGTH] == 'g') + return; // grenadelauncher, but the projectile does damage, not the weapon. So this must be Charge N Targe. + else if(weapon[3] == 'p') + { + weapon_index = GetWeaponIndex(weapon, attacker, inflictor); + } + else + { + // Baseballs are funky. + // Inflictor is the BAT + // But the melee Crush damage (and the nevergib I don't check here) aren't set + // Still has CLUB damage though, and on a melee strike the inflictor is the PLAYER, not the weapon + // Just in case either way, forcing it to get the index of "ball" + if(!(damagetype & DMG_CRUSH) && (damagetype & DMG_CLUB) && StrEqual(weapon, "tf_weapon_bat_wood")) + weapon_index = GetWeaponIndex("ball", attacker); + else + weapon_index = GetWeaponIndex(weapon[WEAPON_PREFIX_LENGTH], attacker); + } + } + if(b_wstats && weapon_index > -1) + { + weaponStats[attacker][weapon_index][LOG_DAMAGE] += idamage; + weaponStats[attacker][weapon_index][LOG_HITS]++; + } + } +} + +public OnGameFrame() +{ + new entity; + new owner; + new itemindex, slot; + decl String:tempstring[15]; + if(stunBallId > -1) + { + while(PopStackCell(h_stunBalls, entity)) + { + if(IsValidEntity(entity)) + { + owner = GetEntPropEnt(entity, Prop_Send, "m_hThrower"); + if(owner > 0 && owner <= MaxClients) + weaponStats[owner][stunBallId][LOG_SHOTS]++; + } + } + } + while(PopStackCell(h_wearables, entity)) + { + if(IsValidEntity(entity)) + { + owner = GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity"); + if(owner > 0 && owner <= MaxClients) + { + itemindex = GetEntProp(entity, Prop_Send, "m_iItemDefinitionIndex"); + Format(tempstring, sizeof(tempstring), "%d", itemindex); + if(KvJumpToKey(itemsKv, tempstring)) + { + KvGetString(itemsKv, "item_slot", tempstring, sizeof(tempstring)); + if(GetTrieValue(slotsTrie, tempstring, slot)) + { + if(slot == 0 && playerClass[owner] == TFClass_DemoMan) + slot++; + if(playerLoadout[owner][slot][0] != itemindex) + { + playerLoadout[owner][slot][0] = itemindex; + playerLoadoutUpdated[owner] = true; + } + playerLoadout[owner][slot][1] = entity; + } + KvGoBack(itemsKv); + } + } + } + } + + new cnt = GetClientCount(); + for (new i = 1; i <= cnt; i++) + { + if (IsClientInGame(i) && GetEntData(i, g_iCarryingOffs, 1)) + g_bCarryingObject[i] = true; + } +} + +public OnClientPutInServer(client) +{ + if (b_sdkhookloaded) + { + SDKHook(client, SDKHook_OnTakeDamagePost, OnTakeDamage_Post); + SDKHook(client, SDKHook_OnTakeDamage, OnTakeDamage); + } + playerLoadoutUpdated[client] = true; + + g_bCarryingObject[client] = false; + + for(new i = 0; i <= MaxClients; i++) + { + // Clear both "we built" (client first) and "we used" (i first) + f_lastTeleport[client][i] = 0.0; + f_lastTeleport[i][client] = 0.0; + } + f_objRemoved[client] = 0.0; + playerClass[client] = TFClass_Unknown; + healPoints[client] = 0; + for(new i = 0; i < MAX_LOADOUT_SLOTS; i++) + playerLoadout[client][i] = {-1, -1}; + ResetWeaponStats(client); +} + +public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + if(client > 0 && IsClientInGame(client)) + { + if(b_wstats) DumpWeaponStats(client); + if(b_heals) DumpHeals(client, " (disconnect)"); + } + + return Plugin_Continue; +} + +HookAllClients() +{ + for (new i = 1; i <= MaxClients; i++) + if (IsClientInGame(i)) + { + SDKHook(i, SDKHook_OnTakeDamagePost, OnTakeDamage_Post); + SDKHook(i, SDKHook_OnTakeDamage, OnTakeDamage); + } +} + +public Action:Event_PlayerChangeclassPre(Handle:event, const String:name[], bool:dontBroadcast) +{ + // Stop log entry! + return Plugin_Handled; +} + +public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast) +{ + new Float:time = GetGameTime(); + new userid = GetEventInt(event, "userid"); + new client = GetClientOfUserId(userid); + new TFClassType:spawnClass = TFClassType:GetEventInt(event, "class"); + jumpStatus[client] = JUMP_NONE; // Play it safe + if(b_wstats) DumpWeaponStats(client); // Changed class without death, dump and reset stats + //if(b_wstats) ResetWeaponStats(client); + if(b_heals) DumpHeals(client, " (spawn)"); + if(time == f_objRemoved[client]) + { + decl String:owner[96]; + decl String:player_authid[32]; + decl String:objname[24]; + if (!GetClientAuthString(client, player_authid, sizeof(player_authid))) + strcopy(player_authid, sizeof(player_authid), "UNKNOWN"); + Format(owner, sizeof(owner), "\"%N<%d><%s><%s>\"", client, userid, player_authid, g_team_list[GetClientTeam(client)]); + new objecttype; + while(PopStackCell(h_objList[client], objecttype)) + { + switch(objecttype) + { + case OBJ_DISPENSER: + objname = "OBJ_DISPENSER"; + case OBJ_TELEPORTER_ENTRANCE: + objname = "OBJ_TELEPORTER_ENTRANCE"; + case OBJ_TELEPORTER_EXIT: + objname = "OBJ_TELEPORTER_EXIT"; + case OBJ_SENTRYGUN: + objname = "OBJ_SENTRYGUN"; + case OBJ_ATTACHMENT_SAPPER: + objname = "OBJ_ATTACHMENT_SAPPER"; + case OBJ_SENTRYGUN_MINI: + objname = "OBJ_SENTRYGUN_MINI"; + default: + continue; + } + LogToGame("%s triggered \"killedobject\" (object \"%s\") (weapon \"pda_engineer\") (objectowner %s) (spawn)", owner, objname, owner); + } + } + if(b_rolelogfix && playerClass[client] != spawnClass) + { + switch(spawnClass) + { + case TFClass_Scout: + LogRoleChange(client, "scout"); + case TFClass_Sniper: + LogRoleChange(client, "sniper"); + case TFClass_Soldier: + LogRoleChange(client, "soldier"); + case TFClass_DemoMan: + LogRoleChange(client, "demoman"); + case TFClass_Medic: + LogRoleChange(client, "medic"); + case TFClass_Heavy: + LogRoleChange(client, "heavyweapons"); + case TFClass_Pyro: + LogRoleChange(client, "pyro"); + case TFClass_Spy: + LogRoleChange(client, "spy"); + case TFClass_Engineer: + LogRoleChange(client, "engineer"); + default: + LogRoleChange(client, "unknown"); + } + } + playerClass[client] = spawnClass; + dalokohs[client] = -30.0; +} + +public Event_ObjectRemoved(Handle:event, const String:name[], bool:dontBroadcast) +{ + new Float:time = GetGameTime(); + new client = GetClientOfUserId(GetEventInt(event, "userid")); + if(time != f_objRemoved[client]) + { + f_objRemoved[client] = time; + while(PopStack(h_objList[client])) + continue; + } + new objtype = GetEventInt(event, "objecttype"); + new objindex = GetEventInt(event, "index"); + if (IsValidEdict(objindex) && GetEntProp(GetEventInt(event, "index"), Prop_Send, "m_bMiniBuilding", 1)) + { + objtype = OBJ_SENTRYGUN_MINI; + } + PushStackCell(h_objList[client], objtype); +} + +public Event_PlayerStealsandvich(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogPlyrPlyrEvent(GetClientOfUserId(GetEventInt(event, "target")), GetClientOfUserId(GetEventInt(event, "owner")), "triggered", "steal_sandvich", true); +} + +public Event_PlayerStunned(Handle:event, const String:name[], bool:dontBroadcast) +{ + new String: properties[33]; + new stunner = GetClientOfUserId(GetEventInt(event, "stunner")); + if(stunner > 0) // Stunner == 0 would be map stun (ghost/trigger), natascha stun (slowdown), taunt kill stun (medic, sniper) + { + new victim = GetClientOfUserId(GetEventInt(event, "victim")); + if(GetEventBool(event, "victim_capping")) StrCat(properties, sizeof(properties), " (victim_capping)"); + if(GetEventBool(event, "big_stun")) StrCat(properties, sizeof(properties), " (big_stun)"); + LogPlyrPlyrEvent(stunner, victim, "triggered", "stun", true); + if((GetEntityFlags(victim) & (FL_ONGROUND | FL_INWATER)) == 0) + LogPlayerEvent(stunner, "triggered", "airshot_stun"); + } +} + +public Event_PlayerTeleported(Handle:event, const String:name[], bool:dontBroadcast) +{ + new builderid = GetClientOfUserId(GetEventInt(event, "builderid")); + new userid = GetClientOfUserId(GetEventInt(event, "userid")); + new Float:curTime = GetGameTime(); + if(b_teleports_again && f_lastTeleport[builderid][userid] > curTime - TELEPORT_AGAIN_TIME) + { + if(userid == builderid) + { + LogPlayerEvent(userid, "triggered", "teleport_self_again"); + } + else + { + LogPlayerEvent(builderid, "triggered", "teleport_again"); + LogPlayerEvent(userid, "triggered", "teleport_used_again"); + } + } + else + { + if(userid == builderid) + { + LogPlayerEvent(userid, "triggered", "teleport_self"); + } + else + { + LogPlayerEvent(builderid, "triggered", "teleport"); + LogPlayerEvent(userid, "triggered", "teleport_used"); + } + } + f_lastTeleport[builderid][userid] = curTime; +} + +public Event_DeployBuffBanner(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "buff_owner")), "triggered", "buff_deployed"); +} + +public Event_MedicDefended(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "userid")), "triggered", "defended_medic"); +} + +public Event_PlayerEscortScore(Handle:event, const String:name[], bool:dontBroadcast) +{ + LogPlayerEvent(GetEventInt(event, "player"), "triggered", "escort_score"); +} + +public Event_MedicDeath(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + healPoints[client] = GetEntProp(client, Prop_Send, "m_iHealPoints"); +} + +public Event_PlayerHurt(Handle:event, const String:name[], bool:dontBroadcast) +{ + // NOTE: The weaponid in this event is the weapon the player is HOLDING as of the event happening + // Thus, only if we don't have SDK Hooks do we use it for airshot detection. + if(!b_sdkhookloaded) + { + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + if(b_wstats) + { + if(nextHurt[attacker] > -1) + weaponStats[attacker][nextHurt[attacker]][LOG_HITS]++; + nextHurt[attacker] = -1; + } + if(b_actions) + { + new client = GetClientOfUserId(GetEventInt(event, "userid")); + if(client != attacker && client > 0 && client <= MaxClients && IsClientInGame(client) + && IsPlayerAlive(client) && (GetEntityFlags(client) & (FL_ONGROUND | FL_INWATER)) == 0) + { + switch(GetEventInt(event, "weaponid")) + { + case TF_WEAPON_ROCKETLAUNCHER, TF_WEAPON_DIRECTHIT: + { + LogPlayerEvent(attacker, "triggered", "airshot_rocket"); + if(jumpStatus[attacker] == JUMP_ROCKET) + LogPlayerEvent(attacker, "triggered", "air2airshot_rocket"); + } + case TF_WEAPON_GRENADELAUNCHER: + { + LogPlayerEvent(attacker, "triggered", "airshot_pipebomb"); + if(jumpStatus[attacker] == JUMP_STICKY) + LogPlayerEvent(attacker, "triggered", "air2airshot_pipebomb"); + } + case TF_WEAPON_PIPEBOMBLAUNCHER: + { + LogPlayerEvent(attacker, "triggered", "airshot_sticky"); + if(jumpStatus[attacker] == JUMP_STICKY) + LogPlayerEvent(attacker, "triggered", "air2airshot_sticky"); + } + case TF_WEAPON_FLAREGUN: + { + if(GetEventInt(event, "damageamount") > 10) + { + LogPlayerEvent(attacker, "triggered", "airshot_flare"); + } + } + case TF_WEAPON_COMPOUND_BOW: + { + LogPlayerEvent(attacker, "triggered", "airshot_arrow"); + } + } + } + } + } +} + +public Action:Event_ObjectDestroyedPre(Handle:event, const String:name[], bool:dontBroadcast) +{ + if (GetEntProp(GetEventInt(event, "index"), Prop_Send, "m_bMiniBuilding", 1)) + { + g_bBlockLog = true; + } + + return Plugin_Continue; +} + +public Event_ObjectDestroyed(Handle:event, const String:name[], bool:dontBroadcast) +{ + if (g_bBlockLog) + { + g_bBlockLog = false; + decl String:weapon[64]; + decl String:team[64]; + decl String:auth[32]; + decl String:properties[255]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + new victimuid = GetEventInt(event, "userid"); + new victim = GetClientOfUserId(victimuid); + if (victim == 0 || !IsClientInGame(victim)) + return; + GetClientAuthString(victim, auth, sizeof(auth)); + GetTeamName(GetClientTeam(victim), team, sizeof(team)); + Format(properties, sizeof(properties), " (object \"OBJ_SENTRYGUN_MINI\") (weapon \"%s\") (objectowner \"%N<%d><%s><%s>\")", weapon, victim, victimuid, auth, team); + LogPlayerEvent(GetClientOfUserId(GetEventInt(event, "attacker")), "triggered", "killedobject", true, properties); + } +} + +public Action:Event_PlayerBuiltObjectPre(Handle:event, const String:name[], bool:dontBroadcast) +{ + if (g_bCarryingObject[GetClientOfUserId(GetEventInt(event, "userid"))] || GetEntProp(GetEventInt(event, "index"), Prop_Send, "m_bMiniBuilding", 1)) + { + g_bBlockLog = true; + } + + return Plugin_Continue; +} + +public Event_PlayerBuiltObject(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + + if (g_bBlockLog) + { + g_bBlockLog = false; + if (!g_bCarryingObject[client] && GetEntProp(GetEventInt(event, "index"), Prop_Send, "m_bMiniBuilding", 1)) + { + LogPlayerEvent(client, "triggered", "builtobject", true, " (object \"OBJ_SENTRYGUN_MINI\")"); + } + } + + g_bCarryingObject[client] = false; +} + +public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) +{ + new death_flags = GetEventInt(event, "death_flags"); + if((death_flags & TF_DEATHFLAG_DEADRINGER) == TF_DEATHFLAG_DEADRINGER) // Not a dead ringer death? + { + return; + } + + new client = GetClientOfUserId(GetEventInt(event, "userid")); + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + new customkill = GetEventInt(event, "customkill"); + new bits = GetEventInt(event, "damagebits"); + if(b_heals && playerClass[client] != TFClass_Medic) // medic_death event handles this for dead medics + DumpHeals(client); + jumpStatus[client] = JUMP_NONE; // Not jumping + g_bCarryingObject[client] = false; + if(b_actions) + { + if(attacker == client && customkill == TF_CUSTOM_SUICIDE) + LogPlayerEvent(client, "triggered", "force_suicide"); + else + { + switch(jumpStatus[client]) + { + case 2: + { + LogPlayerEvent(client, "triggered", "rocket_failjump"); + if(attacker > 0 && attacker != client) + LogPlayerEvent(attacker, "triggered", "rocket_jumper_kill"); + } + case 3: + { + LogPlayerEvent(client, "triggered", "sticky_failjump"); + if(attacker > 0 && attacker != client) + LogPlayerEvent(attacker, "triggered", "sticky_jumper_kill"); + } + } + if(bits & DMG_DROWN) + { + LogPlayerEvent(client, "triggered", "drowned"); + } + else if(attacker != client) + { + switch(jumpStatus[attacker]) // Don't need to check attacker != 0 here as world will never rocket/sticky jump + { + case 2: + LogPlayerEvent(attacker, "triggered", "rocket_jump_kill"); + case 3: + LogPlayerEvent(attacker, "triggered", "sticky_jump_kill"); + } + if ((bits & DMG_ACID) && attacker > 0 && customkill != TF_CUSTOM_HEADSHOT) + LogPlayerEvent(attacker, "triggered", "crit_kill"); + else if((death_flags & TF_DEATHFLAG_FIRSTBLOOD) == TF_DEATHFLAG_FIRSTBLOOD) + LogPlayerEvent(attacker, "triggered", "first_blood"); + if (customkill == TF_CUSTOM_HEADSHOT && client > 0 && client <= MaxClients + && IsClientInGame(client) && (GetEntityFlags(client) & (FL_ONGROUND | FL_INWATER)) == 0) + LogPlayerEvent(attacker, "triggered", "airshot_headshot"); + } + } + } + if(b_wstats && client > 0 && attacker > 0 && attacker <= MaxClients) + { + decl String:weaponlogname[MAX_WEAPON_LEN]; + GetEventString(event, "weapon_logclassname", weaponlogname, sizeof(weaponlogname)); + new weapon_index = GetWeaponIndex(weaponlogname, attacker); + if(weapon_index != -1) + { + weaponStats[attacker][weapon_index][LOG_KILLS]++; + if(customkill == TF_CUSTOM_HEADSHOT) + weaponStats[attacker][weapon_index][LOG_HEADSHOTS]++; + weaponStats[client][weapon_index][LOG_DEATHS]++; + if(GetClientTeam(client) == GetClientTeam(attacker)) + weaponStats[attacker][weapon_index][LOG_TEAMKILLS]++; + } + DumpWeaponStats(client); + } +} + +public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast) +{ + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + new victim = GetClientOfUserId(GetEventInt(event, "userid")); + new customkill = GetEventInt(event, "customkill"); + new inflictor = GetEventInt(event, "inflictor_entindex"); + if (!IsValidEdict(inflictor)) + { + inflictor = 0; + } + + switch (customkill) + { + case TF_CUSTOM_HEADSHOT: + if(b_headshots) + { + LogPlyrPlyrEvent(attacker, victim, "triggered", "headshot"); + } + case TF_CUSTOM_BACKSTAB: + if(b_backstabs) + { + LogPlyrPlyrEvent(attacker, victim, "triggered", "backstab"); + } + case TF_CUSTOM_BURNING_ARROW, TF_CUSTOM_FLYINGBURN: + if(b_fire) + { + decl String:logweapon[64]; + GetEventString(event, "weapon_logclassname", logweapon, sizeof(logweapon)); + if(logweapon[0] != 'd') // No changing reflects - was 'r' but it is deflects + { + SetEventString(event, "weapon_logclassname", "tf_projectile_arrow_fire"); + } + } + case TF_CUSTOM_TAUNT_UBERSLICE: + { + if(GetEventInt(event, "weaponid") == TF_WEAPON_BONESAW) + { + SetEventString(event, "weapon_logclassname", "taunt_medic"); + + // Might as well fix the kill icon, too, as long as we're here + // Courtesy of FlaminSarge + SetEventString(event, "weapon", "taunt_medic"); + } + } + + case TF_CUSTOM_DECAPITATION_BOSS: + { + LogPlayerEvent(attacker, "triggered", "killed_by_horseman", true); + } + } + + return Plugin_Continue; +} + +public Event_WinPanel(Handle:event, const String:name[], bool:dontBroadcast) +{ + if(b_actions) + { + LogPlayerEvent(GetEventInt(event, "player_1"), "triggered", "mvp1"); + LogPlayerEvent(GetEventInt(event, "player_2"), "triggered", "mvp2"); + LogPlayerEvent(GetEventInt(event, "player_3"), "triggered", "mvp3"); + } + if(b_wstats) + DumpAllWeaponStats(); +} + +public Event_RocketJump(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + new status = jumpStatus[client]; + if(status == JUMP_ROCKET_START) // Taunt kills trigger one event, rocket jumps two + { + jumpStatus[client] = JUMP_ROCKET; + LogPlayerEvent(client, "triggered", "rocket_jump"); + } + else if(status != JUMP_ROCKET) + jumpStatus[client] = JUMP_ROCKET_START; +} + +public Event_StickyJump(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + if(jumpStatus[client] != JUMP_STICKY) + { + jumpStatus[client] = JUMP_STICKY; + LogPlayerEvent(client, "triggered", "sticky_jump"); + } +} + +public Event_JumpLanded(Handle:event, const String:name[], bool:dontBroadcast) +{ + jumpStatus[GetClientOfUserId(GetEventInt(event, "userid"))] = JUMP_NONE; +} + +public Event_ObjectDeflected(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + new owner = GetClientOfUserId(GetEventInt(event, "ownerid")); + switch(GetEventInt(event, "weaponid")) + { + case TF_WEAPON_NONE: + { + LogPlyrPlyrEvent(client, owner, "triggered", "airblast_player", true); + } + case TF_WEAPON_ROCKETLAUNCHER: + { + LogPlyrPlyrEvent(client, owner, "triggered", "deflected_rocket", true); + if(b_wstats && b_sdkhookloaded) + { + new weapon_index = GetWeaponIndex("deflect_rocket"); + if(weapon_index > -1) + weaponStats[client][weapon_index][LOG_SHOTS]++; + } + } + case TF_WEAPON_GRENADE_DEMOMAN: + { + LogPlyrPlyrEvent(client, owner, "triggered", "deflected_pipebomb", true); + if(b_wstats && b_sdkhookloaded) + { + new weapon_index = GetWeaponIndex("deflect_promode"); + if(weapon_index > -1) + weaponStats[client][weapon_index][LOG_SHOTS]++; + } + } + case TF_WEAPON_FLAREGUN: + { + LogPlyrPlyrEvent(client, owner, "triggered", "deflected_flare", true); + if(b_wstats && b_sdkhookloaded) + { + new weapon_index = GetWeaponIndex("deflect_flare"); + if(weapon_index > -1) + weaponStats[client][weapon_index][LOG_SHOTS]++; + } + } + case TF_WEAPON_JAR: + { + LogPlyrPlyrEvent(client, owner, "triggered", "deflected_jarate", true); + } + case TF_WEAPON_COMPOUND_BOW: + { + LogPlyrPlyrEvent(client, owner, "triggered", "deflected_arrow", true); + if(b_wstats && b_sdkhookloaded) + { + new weapon_index = GetWeaponIndex("deflect_arrow"); + if(weapon_index > -1) + weaponStats[client][weapon_index][LOG_SHOTS]++; + } + } + case TF_WEAPON_DIRECTHIT: + { + LogPlyrPlyrEvent(client, owner, "triggered", "deflected_rocket_dh", true); + if(b_wstats && b_sdkhookloaded) + { + new weapon_index = GetWeaponIndex("deflect_rocket"); + if(weapon_index > -1) + weaponStats[client][weapon_index][LOG_SHOTS]++; + } + } + case TF_WEAPON_GRENADE_STUNBALL: + { + LogPlyrPlyrEvent(client, owner, "triggered", "deflected_baseball", true); + } + } +} + +public Event_PostInventoryApplication(Handle:event, const String:name[], bool:dontBroadcast) +{ + CreateTimer(0.2, CheckPlayerLoadout, GetEventInt(event, "userid")); +} + +public Action:CheckPlayerLoadout(Handle:timer, any:userid) +{ + new client = GetClientOfUserId(userid); + if (client == 0 || !IsClientInGame(client)) + { + return Plugin_Stop; + } + + new ent = -1; + new bool:newLoadout = false; + new TFClassType:pClass = playerClass[client]; + for(new checkslot = 0; checkslot <=5; checkslot++) + { + if(playerLoadout[client][checkslot][1] != 0 && IsValidEntity(playerLoadout[client][checkslot][1])) + { + continue; + } + ent = GetPlayerWeaponSlot(client, checkslot); + if(ent == -1) + { + // Nothing in slot? + if(b_sdkhookloaded && checkslot < 3 && (pClass == TFClass_Soldier || pClass == TFClass_DemoMan)) // Maybe gunboats? Or charge n targe? + { + playerLoadout[client][checkslot][1] = -1; + continue; + } + if(playerLoadout[client][checkslot][0] == -1) + continue; + playerLoadout[client][checkslot] = {-1, -1}; + newLoadout = true; + } + else + { + new itemindex = GetEntProp(ent, Prop_Send, "m_iItemDefinitionIndex"); + if(playerLoadout[client][checkslot][0] != itemindex) + { + playerLoadout[client][checkslot][0] = itemindex; + newLoadout = true; + } + playerLoadout[client][checkslot][1] = EntIndexToEntRef(ent); + } + } + if(b_sdkhookloaded) + { + if(newLoadout) // Just in case we already updated it due to a hat spawning or being a new client + playerLoadoutUpdated[client] = true; + CreateTimer(0.2, LogWeaponLoadout, userid); + return Plugin_Stop; + } + if (newLoadout) + LogWeaponLoadout(INVALID_HANDLE, userid); + + return Plugin_Stop; +} + +public Action:LogWeaponLoadout(Handle:timer, any:userid) +{ + new client = GetClientOfUserId(userid); + if (client > 0 && IsClientInGame(client)) + { + for (new i = 0; i < MAX_LOADOUT_SLOTS; i++) + { + if(playerLoadout[client][i][0] != -1 && !IsValidEntity(playerLoadout[client][i][1]) || playerLoadout[client][i][1] == 0) + { + playerLoadout[client][i] = {-1, -1}; + playerLoadoutUpdated[client] = true; + } + } + if (playerLoadoutUpdated[client] == false) + return Plugin_Stop; + playerLoadoutUpdated[client] = false; + + decl String:logString[255]; + Format(logString, sizeof(logString), " (primary \"%d\") (secondary \"%d\") (melee \"%d\") (pda \"%d\") (pda2 \"%d\") (building \"%d\") (head \"%d\") (misc \"%d\")", playerLoadout[client][0][0], playerLoadout[client][1][0], playerLoadout[client][2][0], playerLoadout[client][3][0], playerLoadout[client][4][0], playerLoadout[client][5][0], playerLoadout[client][6][0], playerLoadout[client][7][0]); + + LogPlayerEvent(client, "triggered", "player_loadout", _, logString); + } + return Plugin_Stop; +} + +public Action:Event_PlayerJarated(UserMsg:msg_id, Handle:bf, const players[], playersNum, bool:reliable, bool:init) +{ + new client = BfReadByte(bf); + new victim = BfReadByte(bf); + + if (!victim || !IsClientInGame(victim)) + { + return Plugin_Continue; + } + + + if (TF2_IsPlayerInCondition(victim, TFCond_Jarated)) + { + LogPlyrPlyrEvent(client, victim, "triggered", "jarate", true); + } + else if (TF2_IsPlayerInCondition(victim, TFCond_Milked)) + { + LogPlyrPlyrEvent(client, victim, "triggered", "madmilk", true); + } + + return Plugin_Continue; +} + +public Action:Event_PlayerShieldBlocked(UserMsg:msg_id, Handle:bf, const players[], playersNum, bool:reliable, bool:init) +{ + new victim = BfReadByte(bf); + new client = BfReadByte(bf); + + LogPlyrPlyrEvent(client, victim, "triggered", "shield_blocked", true); + return Plugin_Continue; +} + +// Modified Octo's method a bit to try and reduce checking of sound strings +public Action:SoundHook(clients[64], &numClients, String:sample[PLATFORM_MAX_PATH], &entity, &channel, &Float:volume, &level, &pitch, &flags) +{ + if(entity <= MaxClients && clients[0] == entity && playerClass[entity] == TFClass_Heavy && StrEqual(sample,"vo/SandwichEat09.wav")) + { + switch(playerLoadout[entity][1][0]) + { + case LUNCHBOX_CHOCOLATE: + { + LogPlayerEvent(entity, "triggered", "dalokohs"); + new Float:time = GetGameTime(); + if(time - dalokohs[entity] > 30) + LogPlayerEvent(entity, "triggered", "dalokohs_healthboost"); + dalokohs[entity] = time; + if(GetClientHealth(entity) < 350) + LogPlayerEvent(entity, "triggered", "dalokohs_healself"); + } + case LUNCHBOX_STEAK: + { + LogPlayerEvent(entity, "triggered", "steak"); + } + default: + { + LogPlayerEvent(entity, "triggered", "sandvich"); + if(GetClientHealth(entity) < 300) + LogPlayerEvent(entity, "triggered", "sandvich_healself"); + } + } + } + return Plugin_Continue; +} + +public Action:LogMap(Handle:timer) +{ + // Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats + LogMapLoad(); +} + +PopulateWeaponTrie() +{ + // Create a Trie + h_weapontrie = CreateTrie(); + + // Initial populate + for(new i = 0; i < MAX_LOG_WEAPONS; i++) + SetTrieValue(h_weapontrie, weaponList[i], i); + + // Figure out the Bullet Weapon ids (based on the list of bullet weapons) + new index; + bulletWeapons = 0; + for(new i = 0; i < MAX_BULLET_WEAPONS; i++) + if(GetTrieValue(h_weapontrie, weaponBullet[i], index)) + bulletWeapons |= (1< -1) // Projectile? + { + if(client == GetEntProp(weapon, Prop_Send, "m_iDeflected")) + { + switch(weaponname[14]) + { + case 'a': + reflectindex = GetWeaponIndex("deflect_arrow"); + case 'f': + reflectindex = GetWeaponIndex("deflect_flare"); + case 'p': + if(weaponname[19] == 0) // we aren't a _remote + reflectindex = GetWeaponIndex("deflect_promode"); + case 'r': + reflectindex = GetWeaponIndex("deflect_rocket"); + } + } + } + if(reflectindex > -1) + return reflectindex; + if(unlockable && client > 0) + { + new slot = 0; + if(playerClass[client] == TFClass_DemoMan) + slot = 1; + new itemindex = playerLoadout[client][slot][0]; + switch(itemindex) // Hell of a lot easier than a set of ifs. <_< + { + case 36, 41, 45, 61, 127, 130: + index++; + } + } + return index; + } + else + return -1; +} + +DumpHeals(client, String:addProp[] = "") +{ + new curHeals = GetEntProp(client, Prop_Send, "m_iHealPoints"); + new lifeHeals = curHeals - healPoints[client]; + if(lifeHeals > 0) + { + decl String:szProperties[32]; + Format(szProperties, sizeof(szProperties), " (healing \"%d\")%s", lifeHeals, addProp); + LogPlayerEvent(client, "triggered", "healed", _, szProperties); + } + healPoints[client] = curHeals; +} + +DumpAllWeaponStats() +{ + for(new i = 1; i <= MaxClients; i++) + DumpWeaponStats(i); +} + +DumpWeaponStats(client) +{ + if(IsClientInGame(client)) + { + decl String:player_authid[64]; + if(!GetClientAuthString(client, player_authid, sizeof(player_authid))) + strcopy(player_authid, sizeof(player_authid), "UNKNOWN"); + new player_team = GetClientTeam(client); + new player_userid = GetClientUserId(client); + for (new i = 0; i < MAX_LOG_WEAPONS; i++) + if(weaponStats[client][i][LOG_SHOTS] > 0 || weaponStats[client][i][LOG_DEATHS] > 0) + LogToGame("\"%N<%d><%s><%s>\" triggered \"weaponstats\" (weapon \"%s\") (shots \"%d\") (hits \"%d\") (kills \"%d\") (headshots \"%d\") (tks \"%d\") (damage \"%d\") (deaths \"%d\")", client, player_userid, player_authid, g_team_list[player_team], weaponList[i], weaponStats[client][i][LOG_SHOTS], weaponStats[client][i][LOG_HITS], weaponStats[client][i][LOG_KILLS], weaponStats[client][i][LOG_HEADSHOTS], weaponStats[client][i][LOG_TEAMKILLS], weaponStats[client][i][LOG_DAMAGE], weaponStats[client][i][LOG_DEATHS]); + } + ResetWeaponStats(client); +} + +ResetWeaponStats(client) +{ + for(new i = 0; i < MAX_LOG_WEAPONS; i++) + weaponStats[client][i] = {0,0,0,0,0,0,0}; +} + +public OnConVarActionsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:newval = GetConVarBool(cvar_actions); + if(newval != b_actions) + { + if(newval) + { + HookEvent("player_escort_score", Event_PlayerEscortScore); + HookEvent("player_stealsandvich", Event_PlayerStealsandvich); + HookEvent("player_stunned", Event_PlayerStunned); + HookEvent("deploy_buff_banner", Event_DeployBuffBanner); + HookEvent("medic_defended", Event_MedicDefended); + HookEvent("rocket_jump", Event_RocketJump); + HookEvent("rocket_jump_landed", Event_JumpLanded); + HookEvent("sticky_jump", Event_StickyJump); + HookEvent("sticky_jump_landed", Event_JumpLanded); + HookEvent("object_deflected", Event_ObjectDeflected); + HookUserMessage(GetUserMessageId("PlayerJarated"), Event_PlayerJarated); + HookUserMessage(GetUserMessageId("PlayerShieldBlocked"), Event_PlayerShieldBlocked); + } + else + { + UnhookEvent("player_escort_score", Event_PlayerEscortScore); + UnhookEvent("player_stealsandvich", Event_PlayerStealsandvich); + UnhookEvent("player_stunned", Event_PlayerStunned); + UnhookEvent("deploy_buff_banner", Event_DeployBuffBanner); + UnhookEvent("medic_defended", Event_MedicDefended); + UnhookEvent("rocket_jump", Event_RocketJump); + UnhookEvent("rocket_jump_landed", Event_JumpLanded); + UnhookEvent("sticky_jump", Event_StickyJump); + UnhookEvent("sticky_jump_landed", Event_JumpLanded); + UnhookEvent("object_deflected", Event_ObjectDeflected); + UnhookUserMessage(GetUserMessageId("PlayerJarated"), Event_PlayerJarated); + UnhookUserMessage(GetUserMessageId("PlayerShieldBlocked"), Event_PlayerShieldBlocked); + for(new i = 1; i <= MaxClients; i++) + jumpStatus[i] = 0; + } + b_actions = newval; + } +} + + +public OnConVarTeleportsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:newval = GetConVarBool(cvar_teleports); + if(newval != b_teleports) + { + if(newval) + HookEvent("player_teleported", Event_PlayerTeleported); + else + UnhookEvent("player_teleported", Event_PlayerTeleported); + b_teleports = newval; + } +} + +public OnConVarTeleportsAgainChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + b_teleports_again = GetConVarBool(cvar_teleports_again); +} + +public OnConVarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + b_headshots = GetConVarBool(cvar_headshots); +} + +public OnConVarBackstabsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + b_backstabs = GetConVarBool(cvar_backstabs); +} + +public OnConVarSandvichChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:newval = GetConVarBool(cvar_sandvich); + if(newval != b_sandvich) + { + if(newval) + AddNormalSoundHook(SoundHook); + else + RemoveNormalSoundHook(SoundHook); + b_sandvich = newval; + } +} + +public OnConVarFireChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + b_fire = GetConVarBool(cvar_fire); +} + +public OnConVarStatsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:newval = GetConVarBool(cvar_crits) && GetConVarBool(cvar_wstats); + if(newval != b_wstats) + { + if(!newval) + { + DumpAllWeaponStats(); + } + b_wstats = newval; + } +} + +public OnConVarHealsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:newval = GetConVarBool(cvar_heals); + if(newval != b_heals) + { + if(newval) + { + HookEvent("medic_death", Event_MedicDeath); + for(new i = 1; i <= MaxClients; i++) + if(IsClientInGame(i)) + healPoints[i] = GetEntProp(i, Prop_Send, "m_iHealPoints"); + } + else + { + UnhookEvent("medic_death", Event_MedicDeath); + } + b_heals = newval; + } +} + +public OnConVarRolelogfixChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:newval = GetConVarBool(cvar_rolelogfix); + if(newval != b_rolelogfix) + { + if(newval) + HookEvent("player_changeclass", Event_PlayerChangeclassPre, EventHookMode_Pre); + else + UnhookEvent("player_changeclass", Event_PlayerChangeclassPre, EventHookMode_Pre); + b_rolelogfix = newval; + } +} + +public OnConVarObjlogfixChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:newval = GetConVarBool(cvar_objlogfix); + if(newval != b_objlogfix) + { + if(newval) + HookEvent("object_removed", Event_ObjectRemoved); + else + UnhookEvent("object_removed", Event_ObjectRemoved); + b_objlogfix = newval; + } +} \ No newline at end of file diff --git a/sourcemod/scripting/superlogs-zps.sp b/sourcemod/scripting/superlogs-zps.sp new file mode 100644 index 0000000..316ec8e --- /dev/null +++ b/sourcemod/scripting/superlogs-zps.sp @@ -0,0 +1,325 @@ +/** + * HLstatsX Community Edition - SourceMod plugin to generate advanced weapon logging + * http://www.hlxcommunity.com + * Copyright (C) 2009-2010 Nicholas Hastings (psychonic) + * Copyright (C) 2007-2008 TTS Oetzel & Goerz GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma semicolon 1 + +#include +#include + +#include + +#define NAME "SuperLogs: ZPS" +#define VERSION "1.1.2" + +#define MAX_LOG_WEAPONS 11 +#define MAX_WEAPON_LEN 12 +#define PREFIX_LEN 7 + + +new g_weapon_stats[MAXPLAYERS+1][MAX_LOG_WEAPONS][15]; +new const String:g_weapon_list[MAX_LOG_WEAPONS][MAX_WEAPON_LEN] = { + "870", + "revolver", + "ak47", + "usp", + "glock18c", + "glock", + "mp5", + "m4", + "supershorty", + "winchester", + "ppk" + }; + +new Handle:g_cvar_headshots = INVALID_HANDLE; +new Handle:g_cvar_locations = INVALID_HANDLE; +new Handle:g_cvar_ktraj = INVALID_HANDLE; + +new bool:g_logheadshots = true; +new bool:g_loglocations = true; +new bool:g_logktraj = true; + +new g_iNextHitgroup[MAXPLAYERS+1]; + +#include +#include + + +public Plugin:myinfo = { + name = NAME, + author = "psychonic", + description = "Advanced logging for ZPS. Generates auxilary logging for use with log parsers such as HLstatsX and Psychostats", + version = VERSION, + url = "http://www.hlxcommunity.com" +}; + +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 +public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) +#else +public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max) +#endif +{ + new String:szGameDesc[64]; + GetGameDescription(szGameDesc, sizeof(szGameDesc), true); + if (StrContains(szGameDesc, "ZPS", false) == -1) + { + new String:szGameDir[64]; + GetGameFolderName(szGameDir, sizeof(szGameDir)); + if (StrContains(szGameDir, "zps", false) == -1) + { + strcopy(error, err_max, "This plugin is only supported on Zombie Panic: Source"); + #if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 + return APLRes_Failure; + #else + return false; + #endif + } + } +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 + return APLRes_Success; +#else + return true; +#endif +} + + +public OnPluginStart() +{ + CreatePopulateWeaponTrie(); + + g_cvar_headshots = CreateConVar("superlogs_headshots", "1", "Enable logging of headshot player action (default on)", 0, true, 0.0, true, 1.0); + g_cvar_locations = CreateConVar("superlogs_locations", "1", "Enable logging of location on player death (default on)", 0, true, 0.0, true, 1.0); + g_cvar_ktraj = CreateConVar("superlogs_ktraj", "0", "Enable Psychostats \"KTRAJ\" logging (default off)", 0, true, 0.0, true, 1.0); + HookConVarChange(g_cvar_headshots, OnCvarHeadshotsChange); + HookConVarChange(g_cvar_locations, OnCvarLocationsChange); + HookConVarChange(g_cvar_ktraj, OnCvarKtrajChange); + CreateConVar("superlogs_zps_version", VERSION, NAME, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); + + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + HookEvent("player_death", Event_PlayerDeath); + HookEvent("player_spawn", Event_PlayerSpawn); + HookEvent("round_end", Event_RoundEnd, EventHookMode_PostNoCopy); + HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre); + + CreateTimer(1.0, LogMap); +} + +public OnAllPluginsLoaded() +{ + if (GetExtensionFileStatus("sdkhooks.ext") != 1) + { + SetFailState("SDK Hooks v1.3 or higher is required for SuperLogs: ZPS"); + } + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i)) + { + SDKHook(i, SDKHook_FireBulletsPost, OnFireBullets); + SDKHook(i, SDKHook_TraceAttackPost, OnTraceAttack); + SDKHook(i, SDKHook_OnTakeDamagePost, OnTakeDamage); + } + } +} + +public OnMapStart() +{ + GetTeams(); +} + +public OnClientPutInServer(client) +{ + SDKHook(client, SDKHook_FireBulletsPost, OnFireBullets); + SDKHook(client, SDKHook_TraceAttackPost, OnTraceAttack); + SDKHook(client, SDKHook_OnTakeDamagePost, OnTakeDamage); + reset_player_stats(client); +} + +public OnFireBullets(attacker, shots, String:weaponname[]) +{ + if (attacker > 0 && attacker <= MaxClients) + { + new weapon_index = get_weapon_index(weaponname[PREFIX_LEN]); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_SHOTS]++; + } + } +} + +public OnTraceAttack(victim, attacker, inflictor, Float:damage, damagetype, ammotype, hitbox, hitgroup) +{ + if (hitgroup > 0 && attacker > 0 && attacker <= MaxClients && victim > 0 && victim <= MaxClients) + { + g_iNextHitgroup[victim] = hitgroup; + } +} + +public OnTakeDamage(victim, attacker, inflictor, Float:damage, damagetype) +{ + if (attacker > 0 && attacker <= MaxClients && victim > 0 && victim <= MaxClients) + { + new hitgroup = g_iNextHitgroup[victim]; + if (hitgroup < 8) + { + hitgroup += LOG_HIT_OFFSET; + } + new bool:headshot = (GetClientHealth(victim) <= 0 && hitgroup == HITGROUP_HEAD); + + decl String: weapon[MAX_WEAPON_LEN + PREFIX_LEN]; + GetClientWeapon(attacker, weapon, sizeof(weapon)); + new weapon_index = get_weapon_index(weapon[PREFIX_LEN]); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_HITS]++; + g_weapon_stats[attacker][weapon_index][LOG_HIT_DAMAGE] += RoundToNearest(damage); + if (headshot) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_HEADSHOTS]++; + } + } + g_iNextHitgroup[victim] = 0; + } +} + + +public Action:Event_PlayerDeathPre(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID who died + // "attacker" "short" // user ID who killed + // "weapon" "string" // weapon name killer used + + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + new victim = GetClientOfUserId(GetEventInt(event, "userid")); + + if (g_loglocations) + { + LogKillLoc(attacker, victim); + } + if (g_logheadshots && g_iNextHitgroup[victim] == HITGROUP_HEAD) + { + LogPlayerEvent(attacker, "triggered", "headshot"); + } + + return Plugin_Continue; +} + +public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) +{ + // this extents the original player_death by a new fields + // "userid" "short" // user ID who died + // "attacker" "short" // user ID who killed + // "weapon" "string" // weapon name killer used + + new victim = GetClientOfUserId(GetEventInt(event, "userid")); + new attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + decl String: weapon[MAX_WEAPON_LEN]; + GetEventString(event, "weapon", weapon, sizeof(weapon)); + + if (victim > 0 && attacker > 0) + { + new weapon_index = get_weapon_index(weapon); + if (weapon_index > -1) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_KILLS]++; + g_weapon_stats[victim][weapon_index][LOG_HIT_DEATHS]++; + if (GetClientTeam(attacker) == GetClientTeam(victim)) + { + g_weapon_stats[attacker][weapon_index][LOG_HIT_TEAMKILLS]++; + } + } + dump_player_stats(victim); + } + if (g_logktraj) + { + LogPSKillTraj(attacker, victim, weapon); + } +} + +public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast) +{ + // "userid" "short" // user ID on server + + new client = GetClientOfUserId(GetEventInt(event, "userid")); + if (client > 0) + { + reset_player_stats(client); + } +} + +public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) +{ + WstatsDumpAll(); +} + +public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + OnPlayerDisconnect(client); + return Plugin_Continue; +} + + +public Action:LogMap(Handle:timer) +{ + // Called 1 second after OnPluginStart since srcds does not log the first map loaded. Idea from Stormtrooper's "mapfix.sp" for psychostats + LogMapLoad(); +} + +public OnCvarHeadshotsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_logheadshots; + g_logheadshots = GetConVarBool(g_cvar_headshots); + + if (old_value != g_logheadshots) + { + if (g_logheadshots && !g_loglocations) + { + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + else if (!g_loglocations) + { + UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + } +} + +public OnCvarLocationsChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + new bool:old_value = g_loglocations; + g_loglocations = GetConVarBool(g_cvar_locations); + + if (old_value != g_loglocations) + { + if (g_loglocations && !g_logheadshots) + { + HookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + else if (!g_logheadshots) + { + UnhookEvent("player_death", Event_PlayerDeathPre, EventHookMode_Pre); + } + } +} + +public OnCvarKtrajChange(Handle:cvar, const String:oldVal[], const String:newVal[]) +{ + g_logktraj = GetConVarBool(g_cvar_ktraj); +} \ No newline at end of file