From 92396410d44209347109eeabd63678e095cdeb9f Mon Sep 17 00:00:00 2001 From: Arnaud Jamin Date: Fri, 6 Oct 2023 02:27:00 -0400 Subject: [PATCH] Add target acquisition in the sample --- .../Children/Creature1/BP_Creature1.uasset | Bin 42691 -> 42717 bytes .../Children/Creature2/BP_Creature2.uasset | Bin 42281 -> 42200 bytes .../Attributes/CT_Hero1_Attributes.uasset | Bin 3346 -> 3346 bytes .../Characters/Children/Hero1/BP_Hero1.uasset | Bin 54416 -> 54453 bytes .../Characters/Children/Hero2/BP_Hero2.uasset | Bin 44669 -> 44713 bytes .../Core/GameModes/BP_PlayerController.uasset | Bin 0 -> 18950 bytes .../GameModes/DA_TargetAcquisition.uasset | Bin 0 -> 2058 bytes Content/Core/GameModes/GM_CogSample.uasset | Bin 19120 -> 19209 bytes .../5/88/NDHUEK1ROBM2RCW7ZU51MM.uasset | Bin 6905 -> 6905 bytes .../6/U3/6O7DJ03I0JB041XHH0L21L.uasset | Bin 6903 -> 6903 bytes .../Source/CogDebug/Private/CogDebugDraw.cpp | 44 +- ...LogCategoryManager.cpp => CogDebugLog.cpp} | 30 +- .../CogDebug/Private/CogDebugLogBlueprint.cpp | 4 +- .../CogDebug/Private/CogDebugLogCategory.cpp | 4 +- .../CogDebug/Private/CogDebugReplicator.cpp | 8 +- .../CogDebug/Public/CogDebugDrawImGui.h | 2 +- ...ebugLogCategoryManager.h => CogDebugLog.h} | 2 +- .../Private/CogDebugLogCategoryDetails.cpp | 4 +- .../Private/SCogDebugLogCategoryWidget.cpp | 2 +- .../Private/CogEngineWindow_LogCategories.cpp | 18 +- Source/CogSample/CogDefines.h | 10 + Source/CogSample/CogSampleCharacter.cpp | 35 +- Source/CogSample/CogSampleCharacter.h | 26 +- .../CogSampleFunctionLibrary_Gameplay.cpp | 143 +++ .../CogSampleFunctionLibrary_Gameplay.h | 28 +- .../CogSampleFunctionLibrary_Team.cpp | 70 ++ .../CogSample/CogSampleFunctionLibrary_Team.h | 46 + Source/CogSample/CogSampleGameState.cpp | 2 + Source/CogSample/CogSampleLogCategories.cpp | 24 +- Source/CogSample/CogSampleLogCategories.h | 1 + .../CogSample/CogSamplePlayerController.cpp | 15 + Source/CogSample/CogSamplePlayerController.h | 10 + .../CogSample/CogSampleTargetAcquisition.cpp | 886 ++++++++++++++++++ Source/CogSample/CogSampleTargetAcquisition.h | 296 ++++++ .../CogSample/CogSampleTargetableInterface.h | 24 + Source/CogSample/CogSampleTeamInterface.h | 20 + 36 files changed, 1657 insertions(+), 97 deletions(-) create mode 100644 Content/Core/GameModes/BP_PlayerController.uasset create mode 100644 Content/Core/GameModes/DA_TargetAcquisition.uasset rename Plugins/CogDebug/Source/CogDebug/Private/{CogDebugLogCategoryManager.cpp => CogDebugLog.cpp} (77%) rename Plugins/CogDebug/Source/CogDebug/Public/{CogDebugLogCategoryManager.h => CogDebugLog.h} (97%) create mode 100644 Source/CogSample/CogSampleFunctionLibrary_Team.cpp create mode 100644 Source/CogSample/CogSampleFunctionLibrary_Team.h create mode 100644 Source/CogSample/CogSampleTargetAcquisition.cpp create mode 100644 Source/CogSample/CogSampleTargetAcquisition.h create mode 100644 Source/CogSample/CogSampleTargetableInterface.h create mode 100644 Source/CogSample/CogSampleTeamInterface.h diff --git a/Content/Characters/Children/Creature1/BP_Creature1.uasset b/Content/Characters/Children/Creature1/BP_Creature1.uasset index 750342d48a9e98965ad519ddbb21b4d1bff08bc9..925af2e98b2b3617e4dd85080dd6ad3b98aee74e 100644 GIT binary patch delta 8535 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 O5xH?eyULax2LB(VTem|1 delta 8633 zcmbVR2|QHY-@jwcT4`io3YoD+_AL@w8vB+uk<1X;LyrcPYBIzfsdbQ54*TkVa&}?-8CG(Ayy>i?(#h%I^ z1%-l_8-&1j)+oUW603wlmytpEJ5ouEclm4{TptFfo?XXnBNo1lz65B*KhR--CUeoX z+`Dm)h@}F02@kJTLPWteYZvb&wsslk_8!03kEP@)q1a{g1uTw~f&n7-69Mgxue3wN z!kZ+NxOPx;S*rbP`5d&2TWR?deFo6974+wQp20o)u1& zLjLU~_5tuLx2aIz@{5jxTcwsJKXn;l!CewcVk>7u;EIzgor|R}x8WB4|BCKeK@sWj zJISR-PKU+Uuhfiw*7p6ZrT?te{H*o=tbPAk+XSAJJLT!Hi+-VxY; z^YEQ@N(dr6C1ndXo4&NOWZ1F=4e>$R%k^P6N7{M)k>xWHP~y285F8>2FomE9ua(hS zAGw4;LZDn(^@K!t16~o10_KEevnePQmam=#=1g^XK*kt>ge7F1CF7SHac>TG^WtFx zN6R|Hx0OYORuFfXDeJucw`Kb(P~KavA2_zy;k>NMdcR-H*Ou!We_J#+%BieB4j}GR zaId!ETeA25-OTkkT(cSfk{Ox$x2w7Poon8LU>K?CRj#Eb$#Lz0--^vv0Oh~zIb(#lFItC09xr>blIG@WF7!!!Y}4` z%jVK0vzf9n&l&1sM`UFc$>?P(cS2WY;Tp_T-j4{Z)8D{GASiXsTM~Kj$s?{b<6#4c z&L0)~gEf=)T@ZPMAmiDM}73bW;``_4c#1SZOgM$(@np#_LPBI-eq7XB@?$ z2=;)EaG%QB2i*>K^Tqo5mL>!~anJN=XpHogUYq%C{;AxP)g4Ng)EFMpo=qGpPHDxa z0q20tmF!vE!_5vVe4aweuY3Z`2!}@4_9JX-VHS?@=`&s&J79@IklwPL8A%>Wq$q3g z(n}5VJq`2Sy!lh)V3cZsB5ATTMaFI`cBTwV5pV~C2BYsqvzaOD2`GwyvA>fK+tHfe z)T|fxxIaa~P8+-xFnSMT+np31hQVuT`$H;GCaqha%t;peXg^DjNKm1gG?}Lk?%HEl zfo~7l&xty(>b`(YXyYiqX+q#>j}K_qos_!}ZG4q^tFh|`lynYP5QOuNd9L`>;Pa4e z#e?eyEwsD}zD(|Rzff0M^M|XK_p_kwjYFqvc8vN>&Jz8|_PHH;oxTyYQtC}s>R}^- zzouTM3uz)Ww!)3a!`R8+e%x9VOeMmXE@gG`Zc_2$OoMYhvQJMLg8hmU#DF=1{6{$7%i-7e&4A){0s1 zt2$I8LU>OUI}M~WJ#nMv`n$J8F#ykOM>Srza7nl`;L2-@2IfpX$R|9PF)u)o=zEF)OIx?KN`Q|;ES z%c!|9^wIrKQyTOnpu8;S>HFzc|1MV%&iNmk)VK z_DwZ@_+SwcJIo_q^fQ?&!&1)9-y}?eiL!_Bc_9|$>DZfzd#TxHEpM{-hMeNq*GGyJ zVZ!CS3VX~|LiYao^{jNphwt*S`&()^^d78_DL8dcMX-T$)uw3PoE>*cNVJPLS##U$ z{muB0x~jCXoAY0N4U;}OZ};-Fh}oB0WO(YPwAhU0*4&t{k(!r3sol*z&dvz%HPk`A zX?TivIplq&5;cT27VZ|f>mP1H93o)SQzUYedTP4`hB2EJ>^8l&e{IbhYtSBlG!iv{ zMKC;nCmYE^;b1%STOOa(FS;AQwD#-$!%Z7|u~3CNsDRx+jzeR4I;$0<+hQ^TygCnG zS({@o_}nKQ#x*|G*WY_CnO}AR?<{#;6tZc4G)+f8&4|Tfq}yKUXm0Cbcc=&oT(*N4`@yH2hk1Iq#EY%-BXFG#-g`M zRK*=9xBH5z=G&Nd#q8l!er_US!`Mghp*L}lSV1Xa1qGv>VG}ChB)elqcp*;p4$~mN zmi5nRo0%R=pHlhTXq*&P8$$MvyU^;LpkSo?PX2w2`9MxZHv+goUL&$mu~RY<>ZFm@ z2#A?oe3{VATQ6)Z9E^lUJ}aV)$kDM>ia@v(sch7uw^EJ@>Abc+z;D5>AmeZBMyU8#gb@>e*8;wudEt zTr)m5vVu=sqvcWrA|t0xm+kRl7)3H<@ zNAOM%JCEGL&9qKn5h&pC@ky~%T7^>_gd{TavK!raxnNPht4^>Lhsll>ITz;myg(Al zp4?MaAUmPF@$#nn|otO$5RYlxWT=)U`Kd{Mr7&g&iTK_vPbFxa(ThUxG+&O?9R{z}CGsv;*dcv_6bsRK+&b?5 z`DOCG*{(EQ|6~XW!ipV$JGs;eXtRgm>8K1m>KHA;iUIy0z@CwFjm$|!64dU+b{Vil zuWmP?2rdN;$yh|^EC#baCN(pS!Uv!5csLEuRHZfXCYe(L^#e2|TLnY!H6(X^$Cp3t z*|b5~Ti~90CgD;!dlVRo!6zon4LO*`P7xz)C-IRJi!q5iV=*EjTk~#Q zG;<@8#n1*1Mu}{l_|Jxjj;-+Yq?)A&hk?p&Dqt5Bg@50T18Z+x|7&iCjO5J2w~6~<9hXro#7hNISP z#xWVAZMzAu7zmODoqKvu=+u%N0SYFuc~j?mAV>k%TWokj(n zTq1U#ssr?+e%Uxk`330qwp^W45c>sCiNQ;V;^tID4o|7w%V_l8#=Hs;PgN=&CI8u zKILj<$lgoqz0y=#rdzHX%kH9Xahf*vF{Sx9`UU--m6TL3T}~4h*YuD0W9LnUh3rAc zBTxN;ta3d8sb3i;voJenr6UX?3`kxnuZ*Ly1E_tZ2`Hk+9`Jf!|G2eKL(eS%rn|MU zcmm9}R>m7Z6X>Q>a;G4bxbljiqZ?ryg^%jO2sfaFzwPF|Oq)Ui;^&yKcB)?;bu#56 zV9NW6141H~2hj0kBk~_X;|+6T!y28Wt=K}Z0MU_6RdT5Un=yKgUpOn`t%z zNf2Qu4#|2$YypAd-@Ir!l_IF?6lZAWc^LvE6lVy$C>D6piCgAnY7a1~CDPf=CMp%5 ztL2`PMIM*M346CSkMEUAuUV+jNG+7(JM|$_)A+&83wYV`XPZmBqV-q1Mfr?(4Y8m{ zr34KqlVLC_w_l<(PkKYv+?VQkbVY#K-gUe4?QRclST|TNe%of2Hu1%cHI!W1qDC;9 z5M(`>sfpd9yUh;!JhX0CZLqpRUy$IRrjm>h8JzoX568Un)KC51Ms>Zd&$topZKx;S z5uDO09zTMAXLSp)>qbV7t4&4(gGBGoInS>VR}N+Xng#H}C9f ze?U5!c_~T_Nxcq_+sc8spa@JF04|+JAt;k%Vj?N*0EZopgwAeb7Gk#Q{tx=?pYjcK00M@Z6LvjX9AtKl&LyK){O}he zWjvP^e1a70wnTBtfaB$lU<@S8oi+&P(n53`_1|bgpCsY3+9|#VD49jIxD<4tf!ZT+ z#{bE=Z`nZE5&WSrK+%v(Z;;@M3saL&e!hsyRf{{p~-|b$L3~`<{LJJeGT|DE@rqbU|;gVk>&r zlxD#stM;si-yHFD32obpQ{B@-bh~nqRYCP~FMIiGZhVt&G8x`13*=@RSEe76;k*owX_?X6fxqJvU zgn}Z{FS&G0!LQKT_)-EIdVG9rzW1c_=q34Y{QDVfOiWm6AGQ@(eduv>R<^x;yS(l{7=8UFB+l|C5{oMDSBj6+0~nDuc37wo7>g z3F7kk4m&(_WUO99=Q&9yd37kg+NIawN=DEPpB~-8o&DNVzTbB>s3sBazIo^UOdWe| zwBzf;g+%(doY=YUtMm-N5}Y%?a?Egrrz+)v<1SCm<;jY*ZFHSo$1?g0IpjHX;^BMg zi3f(fT6CU`2_$-)_@>q>RUT^ZxM1e#;n%}-%TH9n^AzLPC11C$a&;+oN^|fx^bX!| zrW1`_HAbu$V3?nKaCpwf-ZJt1oUc{%BNgiYo{tw*^A0ZLWCT`9qMV8-*}gt{Z!=2O z8agYOfI=#I_Kb=tq267H?>PxuN0u89UAgB+JHw?^VQWuPfH zrN+%c$$j)3C-!yoa8G8&oiQN41EJ-p+A6V2Nve~Rku5h26GcD0c+y82d}67ooo+*4 zbG)adAo(w)t*JFtI)9RclH2Is5m|Y!*7IgWx-zMZOY|Cxv*Kfe6;vHKa2DoWt1)uk z&TL-&s}GCffIqpWYO96!SEI_${G4mMJ=8w3Y}C)AXRoXwNZkA^9#fQ`FqqX(?+o~u zq|TLHrW1A^ed59P!rKsx5N7R--rDx=wt$rO+cq*bC5bAxJ&~zEw%?^_iiJmY?-ZARTUguYSYpNMmS! zZk(2s`-gxs%Y?RtFn^ui;g(|xy6M`o{nK9O51dyN3kZE?7kBjdy=%LF$NQ%m8%sF6 zG<$novVGs(!lNo7w~u<0zX)xVm?aAIWjr2zAjB~k&(_g)5s;3r#=gZC&kZ` zf+pFq6(c4I5~`_LE+ocTW*$C8*nZsEfd$f)U^p5akeB`FIR=459*-INtDT%3#LdhQ zOUaIcJV0PBb19;c>3O=d{{nV~BH&TsAKx?XVBhl)`k4n6k4P`|u$A|`r~0bbRkRO5 zRcH&CuDo}kZKnFoQFGa7BNb)OAh&2tY#%;8ZQJg%8b2VZ_a6Q(-R;5!o1y4HNO``99 z5UnO1JhsChx-7RU%2~Q4Ika>1l09z2Htpx1y5hgeo)i`JfiDy02+-N@?x}tKMHx2YMDX5!e`h2K~NImc-hXy-bQkN39X)INXkP?SMbl;g({25SDqW?VV4Nn=>~xj`w>Sw zWqE-qjGUbahz~)cSLMjYLJV-M`9!%+H6%oNf5N%eLwC5Mn>Riyi7WUJlE_IK$cIXR z6_w9mp2!q~%fu#7MUw*T7amLewkQVrGK16?^x&J zWgglHxpUP|U&?%HtNho#rc+u`bF-^bM%3lM(CQJ_GR4IDp4n86*=tJId}z*5+dq8R zRAs0i!8eVH_^>3LmPIeK+xgM$SlB!T~qwrY%TZc_(7HdQ4fP?00_H}iy;Hh z7{VZ)2Ol*&t%dB=v=={W{P(C>;Qi^D2TiM0Q{_wto3ejE?COq-fA~tMpggMW1#SEl zWp5rkR_|6lX056l#CB)H+Fe%+#;ADrwr0y(J>L)SJD!B|yo*D+yYa>Tm8#XOpOFy_ zl!E{*tNKaFONnxfNaUw_R@8T^nb1RWm6*Cdz1k}+<#Gv`L)QUimf154~b{sw6jhN!|l89_ifMKE%ek=*zkzw&)$DL@Z3 zgZ$BGEaK~Fy4-2{4j?H0R}E#9q6+lQ>OV|qOt5Z&v@s$lBN0a&`lvl0^Ck>-LDC10 zXJ~x6swMTwawbgSR>u8U5smsc=KY!=5!`QBUiKK@jfqoga0hn*NidyW``&R^@L`0( zOoQ%K06LO|;=%IZY9!@ou93F0H*#Nm^+GCH$F;0`d58q&j!w-+pp zLUbPl#5Q+nAQ&VD2^#$+^Mr@nZPgH~;6H8k`59Xv=uVybb`t}HFC37Nj_`&9Ql;yD zB*Y==vXfN#5ngaHNv7@zemmb%6@G$GtGjx#=-1QnvnU>fAKaa8Qs9nPKk)#^g7LV&L3@-vp&)D;&H5iq7u3q}>CA$;nt6wM<* mL8J6=i-b}gP{0*s$6@dCN`xo;quda&3oiL>b6qes0r6ilyVnGnJ2LpaUi9qP^a{Gw65Tp-UOBX=UH$a+dL(p+E zPyuD>qN%^g1_)9qquq&*-Rx2Hgv3aFXl;SCz4T{YhXLmCriOg{odgA)*%x$_ zRPMx#tIk#dRqx=yQ0P8=`_4*=dY!6zkU~8eG1NBI-J@t>6!IOgu@8Y6^FI-qf;L5`U_0`wzSEMODEZ`(qsV^U|9Rwd3;zGmtDQz~*FW?$abLF=_z%6E)93}EdY@MM z=TG#5oP6|m zMF8!eYC)PnGT^Dk{kSYWGVXwar~~h(;JqGPj=G(B@%j6!8KkPeE$wFi+^ zNft=2o;_yl5MriJ#ziT#(Ahs84~r7OgE=4~eQn+FuV8DWOMffwpn}W-vR+Z&kRUg& zYG97@`3zrG)Rhj)af5+5LO0UVa8jP?7yhsNF6eeZ4^&Xb7@xE)jkPp3D7vjur(VI( z9G3vJ&=*4=L7)VFK#63@v8$0e?l5S6#NG<5m+=M002Op}gyK;SWPX9sK`1_=z@0gA zyw}(qcLa2QG+u&MK>03LjvI{4byKE#AasI4RYe1tG~SAfRWt}dS}N*=9QnXB6LVZD z==x|^OM#oFa4*1VpW$8wZkxicruG;SS8j-JYL491*V4#VXgud4&rAa_@jUr=)s9!L zOfoBhVSJ^``VfW@E07re#{eSZIZUGhLR;H_dl4pAMrdmvAkF5m@UTzC`m;aLMj;fqx5{Kz|owvbdTV|m5yLTCb5(fN6RPwgnSX3qPOg~yNy zd2GW|70~*#GB+Uc`uapjN_e-a*w6)96eFN6_qZEDuru|85^Y43Fe0~i2uyok=p{_> zR030oliWB(hJvvlxMDVksqcc# zhJ<79gm)7@ND-E;sV&gLLmSpU)QYIlx2aT5v>jYlu3j-7<=uPbaeNu>Ou*)Yoj8_L znn~gFr`H8`3t}uzxxQSFblEOdd)L4#>G+QUqGe~w81g8>pq?7WhGGZtaIK1dN1cFb zSX!hM$&KUzr>l;*t!Uj4Q(UAhC9JB-bmxU-L%?V3C0OWLeB{+mo3|6D8QOVZ%8p*KV8Xy#F=Sa?UDI{JhLNyEB`5d9#y~{i6G$rCk-mdzj`F zqk;mD4XcK>j~UyBTK6^Q@6OQ@v>7yb_W+S!I~kR@0QP962dop_jLSZtW8FRLq3ZQUvq|3PH#^MQQ!sn*WE#LV^MWG*ZF z*5sx(qd4;Ti|9eZ)BPuvOt|eLns-l%B+9R5=*A9Q?)kKvP21OY?6JFwT(n=w&^{k~ z{LeVOif$YCwL=pl%lsd|8mr2xJ8|j3+d$*M=vH;LSG_d_QE#@k-*}y7f`4Or@6q+Y zPB~rAc%ZA-^(yDS@!QLD+c-}WhVqB)-~K1g>s4{s^BX>bf9ri5Yh zQZYIr3GGf;kC#va3(MNs{Os8`b0J17R(6Yhi>Zt5jLO>NEy4#FudTvXRJdRv>Z1_} z0W4!Ugu{Y@z(xuaW27?$Hru#@ECQ)F2D}R)+t}JEya+2xcw-3+O zS5d@?lTf#ZN3?G#xw9;=KVc5{_o07R%P#Fx3F=|qzU+Rq+PCjDkEY)I&GtL&*W4`4 zIo9h2PH!UKIkv%VpWaYlXXZtVcMbeZgQY)YlCL~V@vNe5R&8~`&WegD<^^O6>j)L% zg3U5KobBZTS$-HyWUNXWThG)0XM;X?X-R*PYACUYxsuk_@UDSrT%FKbO~n_fnRsCH zJc_|G@KmxrPxg8>CZO1nA^+R$?CwLOB?E>zM7V7r{Y-$q=r#6j;coxiOdn%MhhF^% zLB_Ixd7Dy4Y*$e#3HB2@zS|ljG}IRtE~8(0@uJGxe1x$}vV6X`Dt3qO)y#}|#@OJF z>lw}J>~uuy=so*g#RvY{fGxQBYS8>dUp(VNS%NIvEpqu)>rL|^cMnFmShR49Yo~0> zf#=VI?6ci|{6QtB(&y&7C1txb57!vzM&G`;pl@Jf+{$C3f)&muB>Yt^`z=G3+;}Cc z_a8L&D7kdVycqFVW5n40Q=BzP-UN2UU8#|I)cGZ%F-oYb(Xa?4L6kA9rlpW66 zN4To&+wGYmNeKMzc*Vk=NH_FU4PBodN}RyMOzo%yYL$lJNNzCX7%|3;7~@ksm4(2| z*oHycXv1I#j0a07Duy^n2X2Hn%LrgALDm^UFA~GhR_WfKPkD|tSAY3_$M*|d2~oP6 z$8~GFOMaFJ=znUB?B5$W3Bi36KK5l-AKR}r(s}U6-#~pUzW!puH;qrMbRNKq^2tHsa3nY|e8{FXS#B^6ec?t#rQs^jY6kLE{R{2l|D+ z*!?GR#L6L$1BSEoM3$NND|S7)AzFNN{hov*-TU0o-7d86-THj39AhS-^Dlfh(8Lk* z{W^(d$>DKXYeEg(-6N%WiI(?uZwfv{mp)BQbgb}Z1Wd{KVVi;TMOPU%@vY%JanJrEo zaGUV1C+SA#+&Q5ay2tYFFB7(1)ZFB^X5#ep5#>aBDY>PtQP7EHw}}Z0X>~vRFjijp z+|b1_WRx2+%EK%r#}Z$P5p*KS&SHz_L!`GNaBzA^*Wtk*xNN+1R1E23^Vo{V9U{$T zfQt*nw|h#Q2#O^_=rOd~fm&KBE+DDch^VC%Vx}Q*3FfjNTXeou&=+nYB>oj-jSE(* zzE8K>jW5@_XdcN-8aXVv`0zbvOH;tVe%;O)$1E0K|3kdy>@9L_ayjcKevMjM%bEnQ ze&zT#oR$F&h0L5~abZ`jPi&fOR?P9g&z&0bK7+7rEEr@E4;$DGc`neJ$YP~)VmIkl z+v<+MI?k$kq8)W(71SBhnOKVFDKTZu+>i@kHGu1ga%hj-BlFSg2(L&@sWxTn6^hkU z!2_i@mMtq53+rNs`Rp|2JaFmD6N*!+Qb=<+dDULko)|m7&^3lNSR|Ze!hqD@TnGw0 z?K%u+6i~5<^H1j-rq-e(}L{e7ckLxmnvmS<}T_VXm?* z%b(BY^Hkik2Se5zB!*$3!TmAZh@yf{4JdTUnm{hC2u_d;FhhI9BY+WVQw>ug2e7L# z;7+;zY*zwv9*e-h!vo?1H(&!o!;c%dL`gxwu{~mE@Y|98`h#wDnJrO{d|GbLzioN% zR&Ic5jPFz54^BcU!4k@ZcU$mxRhOHFCF^sX{h2%Il0}!W`RN(5vJ0&N_Y17UE7F0n zn!Ag6EpxX`P###XR!$iv8RPOwr1ZFk+Z1*EMna&p^#pu1d%2?I(U09`hDHIoZF1bDrp`Ubv{9HP6n#7k+T;5uy?EiV4Cn}49`#VL2XEx78&qfrJJv}Tde4>sU| zMrd)0Fleo$l->3~scNpD-CV!mC6N8=CN8ZQb?#ot0O>9G8ek7Ch~P$m0HoC1RL7Gt z67d1T2^3G+S%l^ew4rgjLuS%2^bI(1wZeqb=vZD6CdWZc0e8ei&^HUciVu&wSu-^V zgb)mxglK>*E8n(Kd~uN)*!*9dTJrcdCXg(sn7U9nPCk(*Op{8JlQRj;OhPl0(99$> zGYQR1LNk-lDE?h;CZU;0Xl4?cnS^F0q4|H4(4hZoqyZsn&c?HrErK9D&_w(7sg*C&^`>AnGLk*3GBtAyBiD_t9Lat^ zWg)sU;M_GB4AaR=pJJ#gqMv^R`87WY-d?$}U;+lh@q8;PbS|s_0wHmdkj(s*$j_Wo Vn1Y0GZQ$>ag?TG0Tev&n{{nMo6Tkof delta 8607 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 diff --git a/Content/Characters/Children/Hero1/Attributes/CT_Hero1_Attributes.uasset b/Content/Characters/Children/Hero1/Attributes/CT_Hero1_Attributes.uasset index 1e353641f7f5dd7a1fd3bb91401b95947904e39b..14824e4a33279eea804e662b6e7729edb27c8eda 100644 GIT binary patch delta 41 zcmV+^0M`GK8j>2Y1OX7%X${p8(*#AEwWsk!iCt5(6aoGR0-!*XAPaZ1iwTDVOO6kQ delta 41 xcmbOvHA!j%3!{Ky!hiqCHRV2SK}T1=bM3ygS%mQ)JClOrWCiZ>&E1^s%m7oJ56u7o diff --git a/Content/Characters/Children/Hero1/BP_Hero1.uasset b/Content/Characters/Children/Hero1/BP_Hero1.uasset index 25029fec5fbf8b71c4bc6d90c7ea23cc153796f2..3503d97791d1a4fea6619cd888860cbdeff9993f 100644 GIT binary patch delta 9275 zcmb7q2{=@3*#DWa%TgKpmJp*9vP5WFY-7oil#mEBWZ$ARgHlcQ8DuNlSkj=3C}b<5 z5;H~FB5jBwQcCal9H#fHulM?2*Z*9mbLMy7&pFR|mizww?kTy9t)`5vY6AjMLzY8m z^3x#*azuiEz!64vLu|1LLqU)@__kXXe85>;7=rSZVLJrjRaQTJ<`9IOK7NUC(aR!- zz;k(Nn*T%2WwAiY4%@v2nU}7F-2a9`0Dt&Cbn;7tot&}-1VulE@A1FKKj5e#K#(th z&Q;qX`?>i!z~uptW4_Q6Z~G(FDr;Wx2*o=bZ zD+q~&^Z}nse<05QlJy7j6d>7uAWVSd{DCk4lKThJ3y>>+Al(4DN|qBA5{BOlJ}#}E zAh;J4*cXvVek>wOzAnMdFUGRL??nc!B>7WVNC!YJ!w4dU>?F0km*s4V19%*#nTWKaeK?xdvKF@?U59z$afAL0l(4mRx<~-);UZo3zy5 zw!pvc6!aGo!fN06AML|fHh$^9?|qVGbN&xoB+Ewo58G*$%|=3qyjF$}Ua#QI)J28J z%EBmcWdp|D^k`uL`KNzAUVrc`eQApDMDs>!u?sxy57EO zt^(%d|1j@eHLu;MDU!)DbAxjW>)Nht5*-Mo}FmH5sT^B6GG;J$zZet;z_3#@wx zW_lwzc>|c{O$s9ED-IBZ24|4QRt^`&${q4I$%|A1;8*YYSmq0>W`vTwNY!uVw=6Sd z)qG4z2N}s+xls%7Ok!E#>HjrDh0K-Ju~yj>dEhC0SskGK6@g$@+w6=$5<*Luk{=}? zta1TSDi{6q>4a4tGUbjfLkA0aplmR_jrjTdQ~UFfBu8bW3UK0$TpsW+Z3-ibq^745 zC5G|zw^NpR$edg}{mm3cpn#-|ketc#Ft|HWY>hBNS+i5@bYvaD9~7m2ceXHafyySo zi708b3s;ECW49tuCh^~_I5v$>VFZi(D8n6nl+(jF1l{gS64tqpK@@BB#^ND9l4VC9 zQPyaC;6fRV_O|}A5ppK@s~d5{eX)vHZ7teCM%m?WHKYR9e-aFQa+GFLb`m+mE%EBH zqbbFU|0$zR%Guz|w85+A_Ya%O$M74QaUm}Y6lB&d_14+;K;RWV=?LHk43rU+*Hh;7 zNZWPvg#WW)8>AVZ^1mzWua+;#P6Uv?o0?{nU`y9Kd9mu=v-_V@N%p;PVk^?V&Wu6urOFM|e@3BS!|)-d{M--HFH4d`Wg z-&G42`VypEK!W^wjeD&Mh>}${xeaz@2~T^FGn#&y1ImbIzt@auloB=+(!nhjN=Be2 zeM0ishw$MNz9?eA&N|ImG0o>KUJxJIxj*iQoJT-`udSDSWtuqKC#AVOfpF}nVZuz+ zO|1gtjpZ+%5bt}{h8}1Us0ib;0ZAnDY9>&EZZ7ejmOR+&Dgo(T}v}S9#QV zqO|+r70F#LFTNCv2J6_0)Sdadqcn^#^SZC&f+i1rCqR`kQp;`NwQNsYV&V|tS%~45BB~X=*|{L0AlD4T-#%g_nXoyEX_3d}ubG{oh(ai$ zhFcR_iP9E+#TfyGlDARN0D90;(h^b$DX~Kgwor$Z5nz;!$Be@Rlo51--7N`i2@D>$ zu5c22{GIC-#KN>o$yoLQy!qpp*b-`lcGlso(VX?Bp=G|V&2Br&PMv$QXV#eNc?Z>l zwmrnhVL1PhyN&-z+?a#C#$9uDaRWkYL$R}QlrbT<)AEIqfltp5G~o^A#@pP5b%ocy zMhYt4%H!7UIMKg(Q;r;;Fd+ca8g%kbdAa*bh~n|B!qM|(+d@nqXX`~bavFK*9&t+< z|EW-izb^9P+uY#ECyha;0ybC7S|C;T?33P1$>0sd=FG5%46Na`Y`wL%A1v*4^(kib z9;9uhqjnZF*qO)0u=PXvp_<{nzDsx1)>jiA5PmQq+9c&XB;iU^Smyqt^_Vpv1h5^U zY3G1l2GPsU@$2K$r4#wWw+BYv^X}>M0W;X*rr#L*7rhoA6in_CVfXQ>Yc(> zsyS^)U*y!9&>g~}Pq*0D=GtErW79?6vD|iGE-*IM>0FMn{i{8P=3O$EByNj6?>G@3 zzw2CFASUsrfx}_GPC2#!gD4-DcOBd(1~zbI*5dsgKWjBmI0gnUOqyV?_itsmsy}$A z6+b%i2%n+9J7lY$#IeOguWn!LKGfG;T=K!jwD8Tr7Q-S^YjC<&$XJ)Q8WT?**`r9< zPnEPZBtf1`EH@ixu7!UC4l#J1CV8HwL4pF`Q8zIstgI-Lu-(w^v0m1&G9p~i(Yj4b zS~*V;!=Men+OMKt+#4Gky>MMtXnUr-9aEv-^D7Vts*l_kFD=hb9i+cdrMlMk`p3unS&2v8cf7+qVxFmV z?p|`|qBG%Q)be|byK+G0ZI68&+?OAoaaL(q(WuFU;CXW*gm z`sw4uwGMBF7di`?!_~jyD>cpg1NNARJvT8n2r^82J5!`CR=NMv!JB@xqAEi2YrRq4 z&(m&01KzdPDG4`aXN!s515s%0tJu`k%lurpw;2Ho#kG!D=*-xZX@ry=y0h;x4}Hmo zzGU}4kCV8&lR9L8^S^_qK+A1?39Xq%kQrDe2(Y)Vf)8;$SV3GR*ibD!+a4tI2z6x$ z>AcE${7nMR%U1~BbZ7X(f2#9@dzDzdp4MOAgq?e6#^<2icBArOLWbR^EZr8LFs@G@ zZST1q!N-Jn(J5UIG~AD!?n6@(?LV%t7~+xsX@c zTQ!t=H`k{tUyE*uUn $Hewa8+c#6Fn)2%hrM`-1eMD(6Hb|Xe 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 bd~)n{Kp;HHHyK)p6J(o8wQ4cuNyPsEC-_*? delta 9675 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 diff --git a/Content/Characters/Children/Hero2/BP_Hero2.uasset b/Content/Characters/Children/Hero2/BP_Hero2.uasset index 46edd7a2782bd0a1d6c11fd4d94cc0f62e60fa41..ebf411ef341347fef0e3b2660ead12eea643ff52 100644 GIT binary patch delta 9579 zcma)B2Ut@{*S(DmY}R4R$7!GAVtuaAXX$&0v38rWHBHT zsiLSWAS?n2Mwez|L5P5bq9FQDFz%|KKF@!jz}!3UnKN^zoO9lDXL}Guy@;zCFdW5L z2IJ&d5CnN5APD>*nZ*p1D=W9a;MRRH1Sx@%stQ3Cpqv8FPgX#X5X;6u5DWaJ*#o(a zi(Wf>yE&hWZmy0bKE3_uYE!-yQQVP^4vZeQdD$nF7wTa#p1?%xE6^gm1U!CoeFj$) z1%muRU6csJGB(WDWXL;8m7hdNOUa4@$lZ508EV_N#j89)#!FfuN~GbisGI^?5R8RM zixnb8H4@(-l!(N)Xmdn}NdG%FhCosMne?2^X!}Nl__W|KoGWAyJL7sB8xa z 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 bu4XK{QUvd0+_+*0`!aSX>Xk2Nd%^z)AmU2- delta 9211 zcmb7p2Uru?7w-(AOR5G!aF?f+#`h(t?;E)(BDt0jUuPp#@Z; zgrZ_ubWw^41{Y}x7OD+!MbUQ?qN}_9-}~Nt$v5|&bLPyQJNKUR`<*H86PoK4x~Kue zfruuIlPQHD$Xf`4zz>>Rj5t&+_k=;yehCCAfphp$2(kp{QP5}WLJ&`vj)9;Y@IQlx zAZ5I+_Tw8xPG>q4e8(&}H@Yw0D_-Xz;*_j%kT`1hs-I`i^HYipz#iB>a0R{^bbmO$ zf+H{rI{#(I0d{FTJ2~F4c?W6! z1U&|tlixhR2}pn(f3y;E0{sVMm%M^pq@Z_9aJ~<&I>Yz(D@`U(Bk^5>49cnh&=Z$e z|Bt^`;P3b^{rw3vIsa3W|4Yq1a82C5#_+ei`l9b65a#zDo^u^`wuFr!^aR)#UJNqq2jMcLHnimyI_38UwUsr-Y6(Y91=Y1al~}# zatRYbuM^RbQ$W45h`thEBK^ eB>*dH%_+4Iu+N-g-VS@WlppxqLg`dj2MT zh;%Cbvu+9mw!G6mp6&NpFF+KP|9ScX#QLXRz)!u@pL(pHdV@dp#8rOussB@N>rXxM zPrb6AdYwP@{{E?_q59(;9DeGBs{Zo^e|++oe?57#>T>CAf|+>$X0hbkEJ_y(q`X*h z^qUl>A-k5^2nEI?57m^B8%y=&4hzh%kTA4b&?X|nYCDDK@d)*_8lrJl966(=El(CG zCBQjI(7sGY)|2K<#(>Ewf!GY3<9N{(9O?J?=X9POIOH?<>dIx> zh>NC_nY2HxQ^#9Cb#IRi{i3rw)!yh6~nsp5MW1L_Mv^z8;x0(H>5+DS)S zzUT+_3xPUzUR|kUDzuZqceFrPTmGm(%X_^Ug7zr}|LyP8{ar}4n%Atop*sb`Wwrav zVueWYLoTHz{WvJY%@#5<_b9Jq=Ua=C-jnJ^=jSwhzvR8H+c`Iz~OTmJ*B^g(;#ySl};Xdc=AGctX^6hVi)uwky18 z6SF5gk}CGl77ZnXD+~Ih4O5ryJkC~RBvm&X8RI*4b7s3eOaDF^wBqC9**+(sfU`oZl8Ry)*1fd|*(S8mH37yMJW$#hR5jY*))@0Mr}MOTbXY;6}vhqH8|33+gLM3L5q(XRygj!f3q&CX*a$ z!J<9Ja^Ey@R)v-C^COEyar-6Da{DcwbjJ=h>s^wjnv}*{zCz`99^y7O5R50!YQEoJ zZw8Ufb3HgYH;W4vl5Xx8j9cV;uBd10^UaU7UDb|S6}rb2IkI!9z0xsAd-#U0= z_|sI0*m>QOBHg}1qopUkAL!w=KU8x+-PF{RVed&w-TYEBtIKO$bqw*PO{Rgdn%QPU z(+}hE(&AFdEm14%C{UR#K(%jsQV!=2Psxi@-zT6w~DM~KN zq(H&O@4#w`i0|=-^oHz)@U{@%)+Dqzlq%+-Qcml|w8WyJ9_$lm6=!J0A-V_rGvtsWiVj{Qt=0PpaqOH43 zwAQ_3*LO$+u8x%2(x$of=EoDn?l^6Schs4orR4)?Un>IU7YeSq$*NLBpM>vXWj*F9r%}AY_bB4!Q?{z~DY%)x|r5 zMTDOI#B3$b^+4VrGAM(6|6djrk3C=%w6?G|g0UvG$bO6`FKO>WNr8w)sle&ZYeqo{gwUEVu~U5vM;_9`CAF zp1noL9?tA|x_Q!n#pRU-&m%gBxy~MuB&jrcYevh~1lQu-z0H2Ebsk&vG}HU{Q#@CU zhOV!e_>dfkB47!ah0jMemDV^$LOS!Yuh}2%W-STg-XCKwR3Qq6DjI|fvdWmnz`4#C z4JqYF5Y4D!!D`H4bSnnFTL7kU)2573Xdh?9(aNLEJf{x>N6UH_kUAywp8D2@uJf+k zvTD2kuyfw-;~JKxJ-Iujo!9E5ULM}zNRcMY7}!R?x<(DU_a^%CAwuT&7P+y;znvxOW z#PR?uE?c(Ok|3)^2{JS96ao`EKd0nf!<;%vUfzJ#g-#cTPM5k_FLncA6l_X5N2Q!9 z%*!hy?94QC_f8?sb{cSmH=uMx80 zQWA-?rg-SSi+RtMyBc8=)=K@#rz1PW0;9BFZ0#og#<>*)6{H2e~=d9t1~ZtrDM<#u*t?Fr_FJhA}_4dLW>Y9{_iO9jF!0 z*+{SJ!?i-ZH1s_ck-)};m_WY<_$R69?1IbczyD-)0*l|)f=67-R|{nh9PN5OKsvmMm>0IuVZ3RHwn4A%y9Kke zUM|lrUfxzeD%qVI5S=V?D>be=tgJ7-(y=|_4{@cJJ+}8QJ=%6=+*L9152NiY1p{x3 z)cucb^e5|wW*<2f=nYt1k?#6(m^vg`A!kpe2ie)+ZPCp{08BPz0Jve|=xsLwaxz#J zUJR>j(hXvvwFua{5tl3WC3q3~lc;RS9quHMljb&93-gFHk;4`+Qxu;-k5u7tTxua>^ z`Jk`MRjVr$Ywf(q3la)d!#$%1E*>SlJd(G$;IC@=erHBU;0W_m@*DjQ(-8-y_WmTF z`_mQ4U5_ef)*VE~Je!60K9cfB&lM#Xv&q6EPR{Fob94q^MBSke+nB<@d}QH30{UdD z0^TK&Gg1Np^uq&3e9+QDR_7tGjlrdb;7?u*;*!{M$H+D2B#$zvv<|cyTu}jFVR(Le zL_ZjMX4{(!>7H)=4GZF8hO&>sZmn|`o9lKNy}qm^74v7a+=iOK+S9>NHGS(T7o4FD zPxh)#KWkXISQR2UzBmzXz^xlEI+Wy4a#`kj<#b6)(%in@CxLqB?`bRyOrK&{9?on` zPKmkueogj`C{oVj4FL8u`$d+}f|(hRd(HoVgF>%y!MGlfj$1OM-aGSPq%e42*e+L8 zL|p=&)!9b?aZ}kfI+VaH=aLlyXg$~f>H=d2ROaWEJ0B!%*c*M*Eh*Y6yXuVHP25<$ zes-HPxm=q@8s0ap&52eHlCyT8n8;i^K-$RCPxVqwT1Z^9Rj$~7xoJr=vNLUeLsEsX zWNzrXj%$1OhX^s(7@fb%uFZX|9DoWd4l5}ltBOuKtJqK6N-h1i{6J%(Y?9-M zf!BA4X6-8RWzIsu5A7aqDk5tYWf6)ARAG7zsjn1dN$OCdwMxdi00t2_aES#it1eZ3*qI(#OUMc#Dw~46{9kgAG3P`p% z6qu`ko&eYb1V@>EX_ey;Z|uF8_DK=QnBr5KTuK%hb18-E5T_`OP2hI$RVG2e20t*vav`wIM7o1_$WX4(GyhhA6WwpLvsIh~1%m3K&cQ$^Q$U$JI` zVjls^4}<00^mD{c)GM$j>^_yAci{mWKxNR-+-+|1ZEi7*DmJOcWE+NK0i+Lr8=X~L zXjT*`z=FsIa_$)3nxC*9n3l5X-jH#QaZWrM*12|v%V@wVSw`h#zIJ2X2>n3vXsR+* zNpY+gIK;ugpH4DVjvG=SA38gk&Mvftj0p6VL5l5ILh^@X$l_aulROe^3vLH+rYp+0 z&{3>4mJU)Ir1x~p?&;V@uoxY*gb>2i1tRAaoa;iH9~p&$4S>MDDfQ@{>=Xif4r&?0 zJBsi)7!z4&rmlYd?LQgwPjp=iZ!P?jkky6i(|?A^(l}!I>LO{!&|BM((d%+I*n5_^ zpkgge5}MbXawWP7ZwB$b(Vm7&mk|NmYTwY99dIWJX5s038A22V@?Q~24^qoG`COH^ z)`P8>;0L_`E5K1=xfsnNfGX-HNEF{;?bLTh`<=~^zSB7XGbR9~BkNo-t>1W6VeKa` zpOR7bD`H0Ul9pQU+VEDQmwdR@?k(drYnd@6B15UWQYJs8kly0pNnha4_t=AU z;$A=1?&J6L$1Nq~YC|gST@4D}d(Z9cwaSJ0cM9DD7OQ~SBuoKvrq}~8DLg`&M?5gd z4FnY$WC-HD2#7y>{!Qv0|BBQx0jUdsjVT6oY5cA1+>#Ym`p`iP)kulDiC4}H74z_k ztUK;^lx;Cq8h7SM(lSN3QE1?)wQAXwSk3g`-8~IR$TV*3jFt$~%U}{_%`WoIFaC?r z?Y^RoUKMrYkaq0dXGG++=X9YAtv&BWzmAhEA z7G(VA-dl?$9qeW37Zj(fCidGF+OZs7XlDMtSW9N`E97ls1j2q}J+7Cxu2E5v#**p+;NXn;`nCBsWo6Miw8_F<#-ebC@RO_#)F~!NXTIiXBi({_u3+juv z{|Z%CZ+~17aO$l7Wq-0&{*CoJ)Dx!Z`%k*$>|6BUQ+`C!C$F0OQX#lqhn}=pxD(-* zURO^%?AD&k6J3COsY)gNHnQWlQ?L7HRt5~KuZ3#He4kVkLgO>?NbygZ-Nz2-$40Zs zG17JKsy^nAyW5$epMPRjoy7G{C{|jp*3o@y_y5J_aQ`DhU>P(+9}GQXFlLca+O{YQlYXGyDz&U;Ui_PK;K)zlqfX-@RJt8d{IQDR*~IqmuvCpWYd zg){{c{9^X*O>?|FElLe(I$*sxox&q>P7YwjzhZ6VWlF(o~OKiQdNB_=y1i(y3)&^VvLh!Hoo+U8ENFxmwxX;%H8_USIm zR}ho-tG{HkO5Ndm)&0GwH{79p(e|bX&-U(NT^x4Mm$@f@^nsL$vUlLdJ;K;+r8Y1zi;pBN9mT!PJE_WHCzSNt}1x_9t2w|Nt7CxD~?xf4} zoZ2DM*ggemy>(|eRI0za%=ToL+un~(M66m-w+J({_REQS2FJlO@B!TR<3)RRZ40dTI1gjm+7Rs)^tSYSFXp4) z`&C%?+b_GgAt$fcbArzmNop(gjGGgWTrsYzx=KIH*(5V^v|;d#EK&H-)%PjmMaSOR z%iS9A)i=CjbYE57&qp{qHz;*Kn^bRsTqnq0GuA$IWYP7rmUhCsL^)lc&@jZ@7Ia%r z-0Iwmp2IKtM^sgJb%&Jq&DGj(S*{(nK{Pzav*WK2dAoR)yF|N%N2CPzg$iN&Tm-g1 z-acf^)1iI+G|pLfMp4nzl^W-cq40PN-O>B^5vO_l0ZNc6fanO3YP{nOKmnR832>?w zD8Pc6F^UJ&daxy_p%{3IUt0$BNQI$ediZt@pmbyom5-7@%$>~HiTgm?Ub1 z!A3x>TixI$Rg}+2EirH z$gR8)ExJBoSil3a5RYDyqLodQgI0rbsiSCYDTjwTgU~_fQ4C!Fq`9OwfJgTl+b|X^ zTo3pFumHRwk20!wUO3wG)3zd~=>3NcrTiL+d&+Le$X+oiN5h)ocq?XZvJM)mDkjWs zZiv6VyfhY_vQ3p^s%G&r>PPWs50K? zgn!~CC~o|RgQ2&&Vo-6nW`bfPSDdvf+i%$1-8l2&a#P&C%mU4<`-Y$;8*5A`cWE>Po ztF$@6ITAAC@m%#gD=3+EGD$Wv>#OPb1Mx=;4#a9wJXIaMmL(YF6jcp8VbI1qwJTS& z{9#jL_o1Ro>O0~8_4gm0)q;Xv*+>8{hv1U(D2}`Waxlk^GvWe@$TIyTX5&f58t?%H z^+DF#=DiG-$NZ(iO9aG8yiMtzcKnJ1XZ{NA#l2%DiHc4>LV*$(U~PcDwqjr~sr*#b zR+^_0&=O0VPv?AY2NF8=V}l!$&EvC1gemG_dQLnjkO%={2MV{~;yXw6vBrhSGQ7f0 zf6F5i&}xvaq`-P+aC5B}IL-s1UwzTyMe3GME&r;){7{17Rg~fT+`^5#VgWB9BH4IN zxBzLu>xsC7kF;BAC-53Lcu%d&5iAV*gU@#uWEUwMwAPbUk=G;xcvr0^c}xgihrEqf ztlf~fKm^{2gk|Q|j-0xM5;X0T3xoyDgmjD$?1$)4OlzNIoQ2_?wPBfx^U4S$xInyC zBzIbfZyQk?T=03`Lqu(V(PbE1mR6E9uj82_=V^zL#Z{}2ZIwsi-AHexaV@^=4h-+E um8sYcBM3bTNF~c`5T(*F&=R2^L9D9EVPE7`)f#v&a-l@Ob`xVe{C@z6vmKcL diff --git a/Content/Core/GameModes/BP_PlayerController.uasset b/Content/Core/GameModes/BP_PlayerController.uasset new file mode 100644 index 0000000000000000000000000000000000000000..4e0091a83b6b8e61eb98c49a610a04c740fd5488 GIT binary patch literal 18950 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 literal 0 HcmV?d00001 diff --git a/Content/Core/GameModes/DA_TargetAcquisition.uasset b/Content/Core/GameModes/DA_TargetAcquisition.uasset new file mode 100644 index 0000000000000000000000000000000000000000..a12e0d16c063ad7cb2da21c60031e5efd0ae09c5 GIT binary patch literal 2058 zcmb7FU1$_n6h5m-ZT)Gj|3CFX@(@j$&F;qhNU6BVW^IDm#KhoBi(WUE&6vr~Iy2F% z5)9-)5fMbIPx>GYq7q1H6~W)2z9>R%p`ktmi&E5LQEIKGYI?renQ=GS82s4Td+#~l z{qEd*&bc$tQ*i9X$jHd1$wX5p5k27+@bL4vwg3LZ%0thl-SHm}o)3GMv2E7}LF41Cs+91iu%^_OKZMmT-44jo-Te)4xM}av}=F; z$^18$?sez={NShOw+jvi${fp&DEuajZ&4u zkVU0lzvU;=2RDx6=Sdc7^LE}u=OVBF>y(tu`R5ZI!BJs_JGO5c_k*e`V~U7JC7l`E zz4v37bX_DHbS)uuGf6f5_mmlItyHyWo7C4Q6Vf10!4F?sMom*)=FFV_y$hJRx!I6S zQC-S-NHI)EH$^!tl;7MxXTI&jxu&N2uRr&A4(Cg`Jt%<+2_5M;;%jZN9EiUQV2xqO45PbZz%;X*@W*kkO=%nGUc=h{bc&&lxUvN`pt^rEI{6=w)lslqVG+}?Qr$?47=TS#SngNNoaC2N(PS@O-JkJwo! zO%W2Np!?lF9{i9E_92k5X3t!N&>p+Iqo02#@rnS{f!Cb9^HN zFy*rTXWRXWc+MK>JXBMf6d|nvaOT<93aV{9fMU~Vv=-*P8Ku9-7Q&4YwEN$tRd~9d z?RJ}N`mS?S2Rai3ecXU=&Ln@pwWR~aI15_>Ig z{`<)1Tle@UtOqzP)&#IF;06;8+%N#b0XSqQRa`O%jsotsbWF7e_=;FS$B8K_2XLK4 z!y!};kj1$pgu4u|KbNTi|9c3R3Xo;maL8F;?6mZJPwztt8%FH{OZl1b(>cIj^&`)F dyS-mryNyHn3^UWPZKz^h&W+2vf}^% literal 0 HcmV?d00001 diff --git a/Content/Core/GameModes/GM_CogSample.uasset b/Content/Core/GameModes/GM_CogSample.uasset index caa842f2387b0b55372e9c8cc6f94ba907f56e58..8237ce80596d8f0327b88df5919b53f7da6b9975 100644 GIT binary patch delta 1130 zcmZ{jO-vI(6vt;)YJhax-ELQEVknwgAwXg^Y08HrP!E>)(TWiSwXIr0wUpR!sfjm@ zX=04!1&Hy)S)wJqcp~+H2g9K~^g!ZK!iQhsU?OP2H`@iXUuGps;xbX@B*qhov!ky2S?6H$R!k((l&18 zFl|Z}ES4Ux-x~?WLoxSQbUZc|iG*Tgqq%cB`PUAPn$Pt|kk?@RZqgrr-~qnYosQMM zpmLvT)UU?x=r;hta}vxNt7~@EZ!o8?ldr1Gpc=m-8)U3zCUjtCm}SJuYU@Dx{T}P+ zaZ{6D1D^)JhWv;ozX~%N{3`O-58y?OJ2O7O_?o@`Az`rY`vZ&{Wt?Wlt?9S|-3BW^ zr2buY^IK(3VOT103acComn*m?qjj&pF!;)L@v_~F@qh276|b?1kTDAMF64|7mve&H zT@Qj>1=YeaehZu1^}h!WLE;8n@S(E^j0lbN4kS@Dx1nfDe(q^IS41JB++CUGUCW5Q*jTj>es6s23x+n(q)ao5Sn K?XUr19?7(ZvVvqOSnvk?Uik*Ai>G{ZQ;a z;F^r?oLJ~qYnRky(UaMQ&Ve@+TdcSSAx_)Mr?KNkbH*jRr!hu-gq%R&W}?#Y*^~&F z4HV85tKKri)M#H@cyLIIv})mNkzqA@b!0fIX^~+vboAg{;(qZRDxczJ?L2=)&odAX zImDA3Im`1GaM_%44vP9-rq9bn@-nN~(@~J=EXZ^*CTFe{_+ZJMYX#5fV|wY|iI)SAt7jUi>>_1J9u7&8mn_% z(+?)Qdwu$Tu3@QVlUF~q+ua+w#y369YB~A$ZDD zIB*6eCzBuF%EW{$HtOz+qPvVC5nNfvDgNFy zhjod;QK1a577XT6ZPZJWRPkD>And4AE@Tk03-fMXFCEjnUt#wZgtF!qPTNB|?UgDN zZ3F#@(>pSBOsX@`=FFs0i@LraTwFqd5@A5m(HioCu wKTzY3)HVh~`SY$zc<`H!gG J<|m?}tN@7)6CD5m diff --git a/Content/__ExternalActors__/Maps/L_Default/6/U3/6O7DJ03I0JB041XHH0L21L.uasset b/Content/__ExternalActors__/Maps/L_Default/6/U3/6O7DJ03I0JB041XHH0L21L.uasset index 68bcb7192028c7f47a8cc1dfe4de84db0026d79a..83504b5ee66f8cb26fd1f77bb9c908bce0ff2266 100644 GIT binary patch delta 43 zcmexv`rUNHR7QdH6!VP8kbOS$bGILVrKDxEc`4&2Ax5*!5h5Clj69ngMax(Ll|m3X delta 43 zcmV+`0M!5YHTN~Jl>rdJH>12?sT)m@`9g3e<^zwjr2(=M0W`B%6es}!3$uY0b_J)( B5V-&V diff --git a/Plugins/CogDebug/Source/CogDebug/Private/CogDebugDraw.cpp b/Plugins/CogDebug/Source/CogDebug/Private/CogDebugDraw.cpp index 88f3bd9..01a03a5 100644 --- a/Plugins/CogDebug/Source/CogDebug/Private/CogDebugDraw.cpp +++ b/Plugins/CogDebug/Source/CogDebug/Private/CogDebugDraw.cpp @@ -5,7 +5,7 @@ #include "CogDebugHelper.h" #include "CogDebugDrawImGui.h" #include "CogImguiHelper.h" -#include "CogDebugLogCategoryManager.h" +#include "CogDebugLog.h" #include "CogDebugModule.h" #include "CogDebugReplicator.h" #include "Engine/SkeletalMesh.h" @@ -16,7 +16,7 @@ //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::String2D(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FString& Text, const FVector2D& Location, const FColor& Color, bool Persistent) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { FCogDebugDrawImGui::AddText( FCogImguiHelper::ToImVec2(Location), @@ -31,7 +31,7 @@ void FCogDebugDraw::String2D(const FLogCategoryBase& LogCategory, const UObject* //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::Segment2D(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FVector2D& SegmentStart, const FVector2D& SegmentEnd, const FColor& Color, bool Persistent) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { FCogDebugDrawImGui::AddLine( FCogImguiHelper::ToImVec2(SegmentStart), @@ -46,7 +46,7 @@ void FCogDebugDraw::Segment2D(const FLogCategoryBase& LogCategory, const UObject //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::Circle2D(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FVector2D& Location, float Radius, const FColor& Color, bool Persistent) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { FCogDebugDrawImGui::AddCircle( FCogImguiHelper::ToImVec2(Location), @@ -63,7 +63,7 @@ void FCogDebugDraw::Circle2D(const FLogCategoryBase& LogCategory, const UObject* void FCogDebugDraw::Rect2D(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FVector2D& Min, const FVector2D& Max, const FColor& Color, bool Persistent) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { const ImVec2 ImMin = FCogImguiHelper::ToImVec2(Min); const ImVec2 ImMax = FCogImguiHelper::ToImVec2(Max); @@ -82,7 +82,7 @@ void FCogDebugDraw::Rect2D(const FLogCategoryBase& LogCategory, const UObject* W //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::String(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FString& Text, const FVector& Location, const FColor& Color, const bool Persistent) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { const FColor NewColor = FCogDebugSettings::ModulateDebugColor(WorldContextObject->GetWorld(), Color, Persistent); UE_VLOG_LOCATION(WorldContextObject, LogCategory, Verbose, Location, 10.0f, NewColor, TEXT("%s"), *Text); @@ -102,7 +102,7 @@ void FCogDebugDraw::String(const FLogCategoryBase& LogCategory, const UObject* W //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::Point(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FVector& Location, const float Size, const FColor& Color, const bool Persistent, const uint8 DepthPriority) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { const FColor NewColor = FCogDebugSettings::ModulateDebugColor(WorldContextObject->GetWorld(), Color, Persistent); ::DrawDebugPoint( @@ -121,7 +121,7 @@ void FCogDebugDraw::Point(const FLogCategoryBase& LogCategory, const UObject* Wo //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::Segment(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FVector& SegmentStart, const FVector& SegmentEnd, const FColor& Color, const bool Persistent, const uint8 DepthPriority) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { const FColor NewColor = FCogDebugSettings::ModulateDebugColor(WorldContextObject->GetWorld(), Color, Persistent); UE_VLOG_SEGMENT(WorldContextObject, LogCategory, Verbose, SegmentStart, SegmentEnd, NewColor, TEXT_EMPTY); @@ -142,7 +142,7 @@ void FCogDebugDraw::Segment(const FLogCategoryBase& LogCategory, const UObject* //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::Bone(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FVector& BoneLocation, const FVector& ParentLocation, const FColor& Color, const bool Persistent, const uint8 DepthPriority) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { const FColor NewColor = FCogDebugSettings::ModulateDebugColor(WorldContextObject->GetWorld(), Color, Persistent); UE_VLOG_SEGMENT(WorldContextObject, LogCategory, Verbose, BoneLocation, ParentLocation, NewColor, TEXT_EMPTY); @@ -173,7 +173,7 @@ void FCogDebugDraw::Bone(const FLogCategoryBase& LogCategory, const UObject* Wor //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::Arrow(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FVector& SegmentStart, const FVector& SegmentEnd, const FColor& Color, const bool Persistent, const uint8 DepthPriority) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { const FColor NewColor = FCogDebugSettings::ModulateDebugColor(WorldContextObject->GetWorld(), Color, Persistent); UE_VLOG_ARROW(WorldContextObject, LogCategory, Verbose, SegmentStart, SegmentEnd, NewColor, TEXT_EMPTY); @@ -195,7 +195,7 @@ void FCogDebugDraw::Arrow(const FLogCategoryBase& LogCategory, const UObject* Wo //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::Axis(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FVector& AxisLoc, const FRotator& AxisRot, float Scale, const bool Persistent, const uint8 DepthPriority) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { FRotationMatrix R(AxisRot); UE_VLOG_ARROW(WorldContextObject, LogCategory, Verbose, AxisLoc, AxisLoc + R.GetScaledAxis(EAxis::X) * Scale, FColor::Red, TEXT_EMPTY); @@ -218,7 +218,7 @@ void FCogDebugDraw::Axis(const FLogCategoryBase& LogCategory, const UObject* Wor //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::Circle(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FMatrix& Matrix, float Radius, const FColor& Color, const bool Persistent, const uint8 DepthPriority) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { const FColor NewColor = FCogDebugSettings::ModulateDebugColor(WorldContextObject->GetWorld(), Color, Persistent); const FVector Center = Matrix.GetOrigin(); @@ -243,7 +243,7 @@ void FCogDebugDraw::Circle(const FLogCategoryBase& LogCategory, const UObject* W //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::CircleArc(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FMatrix& Matrix, float InnerRadius, float OuterRadius, float Angle, const FColor& Color, const bool Persistent, const uint8 DepthPriority) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { const FColor NewColor = FCogDebugSettings::ModulateDebugColor(WorldContextObject->GetWorld(), Color, Persistent); @@ -269,7 +269,7 @@ void FCogDebugDraw::CircleArc(const FLogCategoryBase& LogCategory, const UObject //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::FlatCapsule(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FVector2D& Start, const FVector2D& End, const float Radius, const float Z, const FColor& Color, const bool Persistent, const uint8 DepthPriority) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { const FColor NewColor = FCogDebugSettings::ModulateDebugColor(WorldContextObject->GetWorld(), Color, Persistent); // TODO : Add VLOG @@ -294,7 +294,7 @@ void FCogDebugDraw::FlatCapsule(const FLogCategoryBase& LogCategory, const UObje //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::Sphere(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FVector& Location, float Radius, const FColor& Color, const bool Persistent, const uint8 DepthPriority) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { const FColor NewColor = FCogDebugSettings::ModulateDebugColor(WorldContextObject->GetWorld(), Color, Persistent); UE_VLOG_CAPSULE(WorldContextObject, LogCategory, Verbose, Location, 0.0f, Radius, FQuat::Identity, NewColor, TEXT_EMPTY); @@ -316,7 +316,7 @@ void FCogDebugDraw::Sphere(const FLogCategoryBase& LogCategory, const UObject* W //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::Box(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FVector& Center, const FVector& Extent, const FQuat& Rotation, const FColor& Color, const bool Persistent, const uint8 DepthPriority) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { const FColor NewColor = FCogDebugSettings::ModulateDebugColor(WorldContextObject->GetWorld(), Color, Persistent); UE_VLOG_OBOX(WorldContextObject, LogCategory, Verbose, FBox(-Extent, Extent), FQuatRotationTranslationMatrix::Make(Rotation, Center), NewColor, TEXT_EMPTY); @@ -339,7 +339,7 @@ void FCogDebugDraw::Box(const FLogCategoryBase& LogCategory, const UObject* Worl //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::SolidBox(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FVector& Center, const FVector& Extent, const FQuat& Rotation, const FColor& Color, const bool Persistent, const uint8 DepthPriority) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { const FColor NewColor = FCogDebugSettings::ModulateDebugColor(WorldContextObject->GetWorld(), Color, Persistent); UE_VLOG_OBOX(WorldContextObject, LogCategory, Verbose, FBox(-Extent, Extent), FQuatRotationTranslationMatrix::Make(Rotation, Center), NewColor, TEXT_EMPTY); @@ -366,7 +366,7 @@ void FCogDebugDraw::SolidBox(const FLogCategoryBase& LogCategory, const UObject* //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::Frustrum(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FMatrix& Matrix, const float Angle, const float AspectRatio, const float NearPlane, const float FarPlane, const FColor& Color, const bool Persistent, const uint8 DepthPriority) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { const FColor NewColor = FCogDebugSettings::ModulateDebugColor(WorldContextObject->GetWorld(), Color, Persistent); @@ -390,7 +390,7 @@ void FCogDebugDraw::Frustrum(const FLogCategoryBase& LogCategory, const UObject* //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::Capsule(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const FVector& Center, const float HalfHeight, const float Radius, const FQuat& Rotation, const FColor& Color, const bool Persistent, const uint8 DepthPriority) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { const FColor NewColor = FCogDebugSettings::ModulateDebugColor(WorldContextObject->GetWorld(), Color, Persistent); UE_VLOG_CAPSULE(WorldContextObject, LogCategory, Verbose, Center, HalfHeight, Radius, FQuat::Identity, NewColor, TEXT_EMPTY); @@ -414,7 +414,7 @@ void FCogDebugDraw::Capsule(const FLogCategoryBase& LogCategory, const UObject* //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::Points(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const TArray& Points, float Radius, const FColor& StartColor, const FColor& EndColor, const bool Persistent, const uint8 DepthPriority) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { int32 index = 0; for (const FVector& Point : Points) @@ -429,7 +429,7 @@ void FCogDebugDraw::Points(const FLogCategoryBase& LogCategory, const UObject* W //-------------------------------------------------------------------------------------------------------------------------- void FCogDebugDraw::Path(const FLogCategoryBase& LogCategory, const UObject* WorldContextObject, const TArray& Points, float PointSize, const FColor& StartColor, const FColor& EndColor, const bool Persistent, const uint8 DepthPriority) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { if (Points.Num() == 0) { @@ -465,7 +465,7 @@ void FCogDebugDraw::Skeleton(const FLogCategoryBase& LogCategory, const USkeleta return; } - if (FCogDebugLogCategoryManager::IsLogCategoryActive(LogCategory)) + if (FCogDebugLog::IsLogCategoryActive(LogCategory)) { const FReferenceSkeleton& ReferenceSkeleton = Skeleton->GetSkeletalMeshAsset()->GetRefSkeleton(); const FTransform WorldTransform = Skeleton->GetComponentTransform(); diff --git a/Plugins/CogDebug/Source/CogDebug/Private/CogDebugLogCategoryManager.cpp b/Plugins/CogDebug/Source/CogDebug/Private/CogDebugLog.cpp similarity index 77% rename from Plugins/CogDebug/Source/CogDebug/Private/CogDebugLogCategoryManager.cpp rename to Plugins/CogDebug/Source/CogDebug/Private/CogDebugLog.cpp index dc88dea..1fe1f6a 100644 --- a/Plugins/CogDebug/Source/CogDebug/Private/CogDebugLogCategoryManager.cpp +++ b/Plugins/CogDebug/Source/CogDebug/Private/CogDebugLog.cpp @@ -1,4 +1,4 @@ -#include "CogDebugLogCategoryManager.h" +#include "CogDebugLog.h" #include "CogDebugModule.h" #include "CogDebugReplicator.h" @@ -10,7 +10,7 @@ DEFINE_LOG_CATEGORY(LogCogNone); DEFINE_LOG_CATEGORY(LogCogServerDebug); -TMap FCogDebugLogCategoryManager::LogCategories; +TMap FCogDebugLog::LogCategories; //-------------------------------------------------------------------------------------------------------------------------- @@ -34,7 +34,7 @@ FString FCogDebugLogCategoryInfo::GetDisplayName() const //-------------------------------------------------------------------------------------------------------------------------- // FCogDebugLogCategoryManager //-------------------------------------------------------------------------------------------------------------------------- -void FCogDebugLogCategoryManager::AddLogCategory(FLogCategoryBase& LogCategory, const FString& DisplayName, bool bVisible) +void FCogDebugLog::AddLogCategory(FLogCategoryBase& LogCategory, const FString& DisplayName, bool bVisible) { LogCategories.Add(LogCategory.GetCategoryName(), FCogDebugLogCategoryInfo @@ -47,19 +47,19 @@ void FCogDebugLogCategoryManager::AddLogCategory(FLogCategoryBase& LogCategory, } //-------------------------------------------------------------------------------------------------------------------------- -bool FCogDebugLogCategoryManager::IsVerbosityActive(ELogVerbosity::Type Verbosity) +bool FCogDebugLog::IsVerbosityActive(ELogVerbosity::Type Verbosity) { return Verbosity >= ELogVerbosity::Verbose; } //-------------------------------------------------------------------------------------------------------------------------- -bool FCogDebugLogCategoryManager::IsLogCategoryActive(const FLogCategoryBase& LogCategory) +bool FCogDebugLog::IsLogCategoryActive(const FLogCategoryBase& LogCategory) { return IsVerbosityActive(LogCategory.GetVerbosity()); } //-------------------------------------------------------------------------------------------------------------------------- -bool FCogDebugLogCategoryManager::IsLogCategoryActive(FName CategoryName) +bool FCogDebugLog::IsLogCategoryActive(FName CategoryName) { if (FLogCategoryBase* LogCategory = FindLogCategory(CategoryName)) { @@ -70,13 +70,13 @@ bool FCogDebugLogCategoryManager::IsLogCategoryActive(FName CategoryName) } //-------------------------------------------------------------------------------------------------------------------------- -void FCogDebugLogCategoryManager::SetLogCategoryActive(FLogCategoryBase& LogCategory, bool Value) +void FCogDebugLog::SetLogCategoryActive(FLogCategoryBase& LogCategory, bool Value) { LogCategory.SetVerbosity(Value ? ELogVerbosity::Verbose : ELogVerbosity::Warning); } //-------------------------------------------------------------------------------------------------------------------------- -void FCogDebugLogCategoryManager::OnServerVerbosityChanged(FName CategoryName, ELogVerbosity::Type Verbosity) +void FCogDebugLog::OnServerVerbosityChanged(FName CategoryName, ELogVerbosity::Type Verbosity) { if (FCogDebugLogCategoryInfo* LogCategoryInfo = FindLogCategoryInfo(CategoryName)) { @@ -85,7 +85,7 @@ void FCogDebugLogCategoryManager::OnServerVerbosityChanged(FName CategoryName, E } //-------------------------------------------------------------------------------------------------------------------------- -ELogVerbosity::Type FCogDebugLogCategoryManager::GetServerVerbosity(FName CategoryName) +ELogVerbosity::Type FCogDebugLog::GetServerVerbosity(FName CategoryName) { if (FCogDebugLogCategoryInfo* LogCategoryInfo = FindLogCategoryInfo(CategoryName)) { @@ -96,7 +96,7 @@ ELogVerbosity::Type FCogDebugLogCategoryManager::GetServerVerbosity(FName Catego } //-------------------------------------------------------------------------------------------------------------------------- -void FCogDebugLogCategoryManager::SetServerVerbosity(UWorld& World, FName CategoryName, ELogVerbosity::Type Verbosity) +void FCogDebugLog::SetServerVerbosity(UWorld& World, FName CategoryName, ELogVerbosity::Type Verbosity) { if (ACogDebugReplicator* Replicator = FCogDebugModule::Get().GetLocalReplicator(World)) { @@ -105,25 +105,25 @@ void FCogDebugLogCategoryManager::SetServerVerbosity(UWorld& World, FName Catego } //-------------------------------------------------------------------------------------------------------------------------- -void FCogDebugLogCategoryManager::SetServerVerbosityActive(UWorld& World, FName CategoryName, bool Value) +void FCogDebugLog::SetServerVerbosityActive(UWorld& World, FName CategoryName, bool Value) { SetServerVerbosity(World, CategoryName, Value ? ELogVerbosity::Verbose : ELogVerbosity::Warning); } //-------------------------------------------------------------------------------------------------------------------------- -bool FCogDebugLogCategoryManager::IsServerVerbosityActive(FName CategoryName) +bool FCogDebugLog::IsServerVerbosityActive(FName CategoryName) { return IsVerbosityActive(GetServerVerbosity(CategoryName)); } //-------------------------------------------------------------------------------------------------------------------------- -FCogDebugLogCategoryInfo* FCogDebugLogCategoryManager::FindLogCategoryInfo(FName CategoryName) +FCogDebugLogCategoryInfo* FCogDebugLog::FindLogCategoryInfo(FName CategoryName) { return LogCategories.Find(CategoryName); } //-------------------------------------------------------------------------------------------------------------------------- -FLogCategoryBase* FCogDebugLogCategoryManager::FindLogCategory(FName CategoryName) +FLogCategoryBase* FCogDebugLog::FindLogCategory(FName CategoryName) { if (FCogDebugLogCategoryInfo* LogCategoryInfo = FindLogCategoryInfo(CategoryName)) { @@ -138,7 +138,7 @@ FLogCategoryBase* FCogDebugLogCategoryManager::FindLogCategory(FName CategoryNam } //-------------------------------------------------------------------------------------------------------------------------- -void FCogDebugLogCategoryManager::DeactivateAllLogCateories(UWorld& World) +void FCogDebugLog::DeactivateAllLogCateories(UWorld& World) { FString ToggleStr = TEXT("Log LogCogNone Only"); GEngine->Exec(&World, *ToggleStr); diff --git a/Plugins/CogDebug/Source/CogDebug/Private/CogDebugLogBlueprint.cpp b/Plugins/CogDebug/Source/CogDebug/Private/CogDebugLogBlueprint.cpp index 5c56a70..5c30c93 100644 --- a/Plugins/CogDebug/Source/CogDebug/Private/CogDebugLogBlueprint.cpp +++ b/Plugins/CogDebug/Source/CogDebug/Private/CogDebugLogBlueprint.cpp @@ -1,6 +1,6 @@ #include "CogDebugLogBlueprint.h" -#include "CogDebugLogCategoryManager.h" +#include "CogDebugLog.h" #include "CogDebugLogMacros.h" //-------------------------------------------------------------------------------------------------------------------------- @@ -35,7 +35,7 @@ bool UCogDebugLogBlueprint::IsLogActive(const UObject* WorldContextObject, FCogL if (const FLogCategoryBase* LogCategoryPtr = LogCategory.GetLogCategory()) { - if (FCogDebugLogCategoryManager::IsLogCategoryActive(*LogCategoryPtr) == false) + if (FCogDebugLog::IsLogCategoryActive(*LogCategoryPtr) == false) { return false; } diff --git a/Plugins/CogDebug/Source/CogDebug/Private/CogDebugLogCategory.cpp b/Plugins/CogDebug/Source/CogDebug/Private/CogDebugLogCategory.cpp index 30a36ce..2e5fd51 100644 --- a/Plugins/CogDebug/Source/CogDebug/Private/CogDebugLogCategory.cpp +++ b/Plugins/CogDebug/Source/CogDebug/Private/CogDebugLogCategory.cpp @@ -1,6 +1,6 @@ #include "CogDebugLogCategory.h" -#include "CogDebugLogCategoryManager.h" +#include "CogDebugLog.h" //-------------------------------------------------------------------------------------------------------------------------- FLogCategoryBase* FCogLogCategory::GetLogCategory() const @@ -18,7 +18,7 @@ FLogCategoryBase* FCogLogCategory::GetLogCategory() const if (LogCategory == nullptr) { - if (FCogDebugLogCategoryInfo* CategoryInfo = FCogDebugLogCategoryManager::GetLogCategories().Find(Name)) + if (FCogDebugLogCategoryInfo* CategoryInfo = FCogDebugLog::GetLogCategories().Find(Name)) { LogCategory = CategoryInfo->LogCategory; } diff --git a/Plugins/CogDebug/Source/CogDebug/Private/CogDebugReplicator.cpp b/Plugins/CogDebug/Source/CogDebug/Private/CogDebugReplicator.cpp index 6a51d48..4647c9f 100644 --- a/Plugins/CogDebug/Source/CogDebug/Private/CogDebugReplicator.cpp +++ b/Plugins/CogDebug/Source/CogDebug/Private/CogDebugReplicator.cpp @@ -181,7 +181,7 @@ void ACogDebugReplicator::Server_SetCategoryVerbosity_Implementation(FName LogCa ENetMode NetMode = GetWorld()->GetNetMode(); if (NetMode == NM_DedicatedServer || NetMode == NM_ListenServer) { - if (FCogDebugLogCategoryInfo* LogCategoryInfo = FCogDebugLogCategoryManager::FindLogCategoryInfo(LogCategoryName)) + if (FCogDebugLogCategoryInfo* LogCategoryInfo = FCogDebugLog::FindLogCategoryInfo(LogCategoryName)) { LogCategoryInfo->LogCategory->SetVerbosity((ELogVerbosity::Type)Verbosity); @@ -203,7 +203,7 @@ void ACogDebugReplicator::NetMulticast_SendCategoriesVerbosity_Implementation(co { for (const FCogServerCategoryData& Category : Categories) { - FCogDebugLogCategoryManager::OnServerVerbosityChanged(Category.LogCategoryName, (ELogVerbosity::Type)Category.Verbosity); + FCogDebugLog::OnServerVerbosityChanged(Category.LogCategoryName, (ELogVerbosity::Type)Category.Verbosity); } } @@ -219,7 +219,7 @@ void ACogDebugReplicator::Client_SendCategoriesVerbosity_Implementation(const TA { for (const FCogServerCategoryData& Category : Categories) { - FCogDebugLogCategoryManager::OnServerVerbosityChanged(Category.LogCategoryName, (ELogVerbosity::Type)Category.Verbosity); + FCogDebugLog::OnServerVerbosityChanged(Category.LogCategoryName, (ELogVerbosity::Type)Category.Verbosity); } } @@ -235,7 +235,7 @@ void ACogDebugReplicator::Server_RequestAllCategoriesVerbosity_Implementation() if (NetMode == NM_DedicatedServer || NetMode == NM_ListenServer) { TArray CategoriesData; - for (auto& Entry : FCogDebugLogCategoryManager::GetLogCategories()) + for (auto& Entry : FCogDebugLog::GetLogCategories()) { FCogDebugLogCategoryInfo& CategoryInfo = Entry.Value; if (CategoryInfo.LogCategory != nullptr) diff --git a/Plugins/CogDebug/Source/CogDebug/Public/CogDebugDrawImGui.h b/Plugins/CogDebug/Source/CogDebug/Public/CogDebugDrawImGui.h index 493f35a..e871e11 100644 --- a/Plugins/CogDebug/Source/CogDebug/Public/CogDebugDrawImGui.h +++ b/Plugins/CogDebug/Source/CogDebug/Public/CogDebugDrawImGui.h @@ -3,7 +3,7 @@ #include "CoreMinimal.h" #include "imgui.h" -class FCogDebugDrawImGui +class COGDEBUG_API FCogDebugDrawImGui { public: static void AddLine(const ImVec2& P1, const ImVec2& P2, ImU32 Color, float Thickness = 1.0f, float Duration = 0.0f, bool FadeColor = false); diff --git a/Plugins/CogDebug/Source/CogDebug/Public/CogDebugLogCategoryManager.h b/Plugins/CogDebug/Source/CogDebug/Public/CogDebugLog.h similarity index 97% rename from Plugins/CogDebug/Source/CogDebug/Public/CogDebugLogCategoryManager.h rename to Plugins/CogDebug/Source/CogDebug/Public/CogDebugLog.h index 0d69d65..eb7a858 100644 --- a/Plugins/CogDebug/Source/CogDebug/Public/CogDebugLogCategoryManager.h +++ b/Plugins/CogDebug/Source/CogDebug/Public/CogDebugLog.h @@ -20,7 +20,7 @@ struct COGDEBUG_API FCogDebugLogCategoryInfo }; //-------------------------------------------------------------------------------------------------------------------------- -struct COGDEBUG_API FCogDebugLogCategoryManager +struct COGDEBUG_API FCogDebugLog { static void AddLogCategory(FLogCategoryBase& LogCategory, const FString& DisplayName = "", bool bVisible = true); diff --git a/Plugins/CogDebug/Source/CogDebugEditor/Private/CogDebugLogCategoryDetails.cpp b/Plugins/CogDebug/Source/CogDebugEditor/Private/CogDebugLogCategoryDetails.cpp index 2ba2883..fd0ef1e 100644 --- a/Plugins/CogDebug/Source/CogDebugEditor/Private/CogDebugLogCategoryDetails.cpp +++ b/Plugins/CogDebug/Source/CogDebugEditor/Private/CogDebugLogCategoryDetails.cpp @@ -1,7 +1,7 @@ #include "CogDebugLogCategoryDetails.h" #include "CogDebugLogCategory.h" -#include "CogDebugLogCategoryManager.h" +#include "CogDebugLog.h" #include "DetailWidgetRow.h" #include "Editor.h" #include "IPropertyUtilities.h" @@ -27,7 +27,7 @@ void FCogLogCategoryDetails::CustomizeHeader(TSharedRef StructP PropertyOptions.Empty(); PropertyOptions.Add(MakeShareable(new FString("None"))); - for (auto& Entry : FCogDebugLogCategoryManager::GetLogCategories()) + for (auto& Entry : FCogDebugLog::GetLogCategories()) { PropertyOptions.Add(MakeShareable(new FString(Entry.Value.LogCategory->GetCategoryName().ToString()))); } diff --git a/Plugins/CogDebug/Source/CogDebugEditor/Private/SCogDebugLogCategoryWidget.cpp b/Plugins/CogDebug/Source/CogDebugEditor/Private/SCogDebugLogCategoryWidget.cpp index 8b8e1e5..add566b 100644 --- a/Plugins/CogDebug/Source/CogDebugEditor/Private/SCogDebugLogCategoryWidget.cpp +++ b/Plugins/CogDebug/Source/CogDebugEditor/Private/SCogDebugLogCategoryWidget.cpp @@ -247,7 +247,7 @@ TSharedPtr SLogCategoryListWidget::UpdatePropertyOptions PropertyOptions.Add(InitiallySelected); // Gather all ULogCategory classes - for (auto& Entry : FCogDebugLogCategoryManager::GetLogCategories()) + for (auto& Entry : FCogDebugLog::GetLogCategories()) { // if we have a search string and this doesn't match, don't show it if (LogCategoryTextFilter.IsValid() && !LogCategoryTextFilter->PassesFilter(Entry.Value.LogCategory->GetCategoryName())) diff --git a/Plugins/CogEngine/Source/CogEngine/Private/CogEngineWindow_LogCategories.cpp b/Plugins/CogEngine/Source/CogEngine/Private/CogEngineWindow_LogCategories.cpp index fed1ada..78523ad 100644 --- a/Plugins/CogEngine/Source/CogEngine/Private/CogEngineWindow_LogCategories.cpp +++ b/Plugins/CogEngine/Source/CogEngine/Private/CogEngineWindow_LogCategories.cpp @@ -2,7 +2,7 @@ #include "CogDebugHelper.h" #include "CogWindowWidgets.h" -#include "CogDebugLogCategoryManager.h" +#include "CogDebugLog.h" //-------------------------------------------------------------------------------------------------------------------------- void UCogEngineWindow_LogCategories::RenderHelp() @@ -49,7 +49,7 @@ void UCogEngineWindow_LogCategories::RenderContent() if (ImGui::MenuItem("Reset")) { - FCogDebugLogCategoryManager::DeactivateAllLogCateories(*World); + FCogDebugLog::DeactivateAllLogCateories(*World); } if (ImGui::IsItemHovered()) @@ -77,7 +77,7 @@ void UCogEngineWindow_LogCategories::RenderContent() ImGuiStyle& Style = ImGui::GetStyle(); int Index = 0; - for (const auto& Entry : FCogDebugLogCategoryManager::GetLogCategories()) + for (const auto& Entry : FCogDebugLog::GetLogCategories()) { FName CategoryName = Entry.Key; const FCogDebugLogCategoryInfo& CategoryInfo = Entry.Value; @@ -96,8 +96,8 @@ void UCogEngineWindow_LogCategories::RenderContent() const bool IsControlDown = ImGui::GetIO().KeyCtrl; if (IsClient) { - ELogVerbosity::Type Verbosity = FCogDebugLogCategoryManager::GetServerVerbosity(CategoryName); - bool IsActive = FCogDebugLogCategoryManager::IsVerbosityActive(Verbosity); + ELogVerbosity::Type Verbosity = FCogDebugLog::GetServerVerbosity(CategoryName); + bool IsActive = FCogDebugLog::IsVerbosityActive(Verbosity); if (Verbosity == ELogVerbosity::VeryVerbose) { @@ -107,7 +107,7 @@ void UCogEngineWindow_LogCategories::RenderContent() if (ImGui::Checkbox("##Server", &IsActive)) { ELogVerbosity::Type NewVerbosity = IsActive ? (IsControlDown ? ELogVerbosity::VeryVerbose : ELogVerbosity::Verbose) : ELogVerbosity::Warning; - FCogDebugLogCategoryManager::SetServerVerbosity(*World, CategoryName, NewVerbosity); + FCogDebugLog::SetServerVerbosity(*World, CategoryName, NewVerbosity); } if (Verbosity == ELogVerbosity::VeryVerbose) @@ -128,7 +128,7 @@ void UCogEngineWindow_LogCategories::RenderContent() { ELogVerbosity::Type Verbosity = Category->GetVerbosity(); - bool IsActive = FCogDebugLogCategoryManager::IsVerbosityActive(Verbosity); + bool IsActive = FCogDebugLog::IsVerbosityActive(Verbosity); if (Verbosity == ELogVerbosity::VeryVerbose) { @@ -162,7 +162,7 @@ void UCogEngineWindow_LogCategories::RenderContent() { if (IsClient) { - ELogVerbosity::Type CurrentVerbosity = FCogDebugLogCategoryManager::GetServerVerbosity(CategoryName); + ELogVerbosity::Type CurrentVerbosity = FCogDebugLog::GetServerVerbosity(CategoryName); FCogWindowWidgets::SetNextItemToShortWidth(); if (ImGui::BeginCombo("##Server", FCogDebugHelper::VerbosityToString(CurrentVerbosity))) { @@ -173,7 +173,7 @@ void UCogEngineWindow_LogCategories::RenderContent() if (ImGui::Selectable(FCogDebugHelper::VerbosityToString(Verbosity), IsSelected)) { - FCogDebugLogCategoryManager::SetServerVerbosity(*World, CategoryName, Verbosity); + FCogDebugLog::SetServerVerbosity(*World, CategoryName, Verbosity); } } ImGui::EndCombo(); diff --git a/Source/CogSample/CogDefines.h b/Source/CogSample/CogDefines.h index ad566e8..45a03a2 100644 --- a/Source/CogSample/CogDefines.h +++ b/Source/CogSample/CogDefines.h @@ -5,3 +5,13 @@ #ifndef USE_COG #define USE_COG (ENABLE_DRAW_DEBUG && !NO_LOGGING) #endif + +#if !USE_COG + +#define COG(expr) (0) + +#else //!ENABLE_COG + +#define COG(expr) { expr; } + +#endif //!ENABLE_COG diff --git a/Source/CogSample/CogSampleCharacter.cpp b/Source/CogSample/CogSampleCharacter.cpp index 7a8bc77..ead5ef2 100644 --- a/Source/CogSample/CogSampleCharacter.cpp +++ b/Source/CogSample/CogSampleCharacter.cpp @@ -6,6 +6,7 @@ #include "CogSampleAttributeSet_Health.h" #include "CogSampleAttributeSet_Misc.h" #include "CogSampleCharacterMovementComponent.h" +#include "CogSampleFunctionLibrary_Team.h" #include "CogSampleGameplayAbility.h" #include "CogSampleLogCategories.h" #include "CogSampleRootMotionParams.h" @@ -77,7 +78,7 @@ void ACogSampleCharacter::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > Params.Condition = COND_OwnerOnly; DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleCharacter, ActiveAbilityHandles, Params); - DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleCharacter, TeamID, Params); + DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleCharacter, Team, Params); } //-------------------------------------------------------------------------------------------------------------------------- @@ -116,18 +117,16 @@ UAbilitySystemComponent* ACogSampleCharacter::GetAbilitySystemComponent() const //-------------------------------------------------------------------------------------------------------------------------- ECogInterfacesAllegiance ACogSampleCharacter::GetAllegianceWithOtherActor(const AActor* OtherActor) const { - const ACogSampleCharacter* OtherCharacter = Cast(OtherActor); - if (OtherCharacter == nullptr) - { - return ECogInterfacesAllegiance::Neutral; - } + ECogSampleAllegiance Allegiance = UCogSampleFunctionLibrary_Team::GetActorsAllegiance(this, OtherActor); - if (TeamID == OtherCharacter->TeamID) + switch (Allegiance) { - return ECogInterfacesAllegiance::Friendly; + case ECogSampleAllegiance::Enemy: return ECogInterfacesAllegiance::Enemy; + case ECogSampleAllegiance::Friendly: return ECogInterfacesAllegiance::Friendly; + case ECogSampleAllegiance::Neutral: return ECogInterfacesAllegiance::Neutral; } - - return ECogInterfacesAllegiance::Enemy; + + return ECogInterfacesAllegiance::Neutral; } //-------------------------------------------------------------------------------------------------------------------------- @@ -507,8 +506,8 @@ void ACogSampleCharacter::OnScaleAttributeChanged(const FOnAttributeChangeData& //-------------------------------------------------------------------------------------------------------------------------- void ACogSampleCharacter::SetTeamID(int32 Value) { - TeamID = Value; - MARK_PROPERTY_DIRTY_FROM_NAME(ACogSampleCharacter, TeamID, this); + Team = Value; + MARK_PROPERTY_DIRTY_FROM_NAME(ACogSampleCharacter, Team, this); } //-------------------------------------------------------------------------------------------------------------------------- @@ -608,4 +607,16 @@ void ACogSampleCharacter::UpdateActiveAbilitySlots() FGameplayTag SlotTag = FCogSampleTagLibrary::ActiveAbilityCooldownTags[i]; AbilityInstance->SetSlotTag(SlotTag); } +} + +//-------------------------------------------------------------------------------------------------------------------------- +FVector ACogSampleCharacter::GetTargetLocation() const +{ + return GetActorLocation(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void ACogSampleCharacter::GetTargetCapsules(TArray& Capsules) const +{ + Capsules.Add(GetCapsuleComponent()); } \ No newline at end of file diff --git a/Source/CogSample/CogSampleCharacter.h b/Source/CogSample/CogSampleCharacter.h index 4b0ac15..3f8048a 100644 --- a/Source/CogSample/CogSampleCharacter.h +++ b/Source/CogSample/CogSampleCharacter.h @@ -7,6 +7,8 @@ #include "CogDefines.h" #include "CogInterfaceAllegianceActor.h" #include "CogInterfaceDebugFilteredActor.h" +#include "CogSampleTargetableInterface.h" +#include "CogSampleTeamInterface.h" #include "GameFramework/Character.h" #include "GameplayAbilitySpecHandle.h" #include "GameplayTagContainer.h" @@ -26,17 +28,6 @@ struct FCogSampleRootMotionParams; struct FGameplayEffectSpec; struct FOnAttributeChangeData; -//-------------------------------------------------------------------------------------------------------------------------- -UENUM(BlueprintType, meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true")) -enum class ECogSampleAllegianceFilter : uint8 -{ - None = 0 UMETA(Hidden), - Ally = 1 << 0, - Neutral = 1 << 1, - Enemy = 1 << 2, -}; -ENUM_CLASS_FLAGS(ECogSampleAllegianceFilter); - //-------------------------------------------------------------------------------------------------------------------------- USTRUCT(BlueprintType) struct FActiveAbilityInfo @@ -72,6 +63,8 @@ class ACogSampleCharacter : public ACharacter , public IAbilitySystemInterface , public ICogInterfacesDebugFilteredActor , public ICogInterfacesAllegianceActor + , public ICogSampleTeamInterface + , public ICogSampleTargetableInterface { GENERATED_BODY() @@ -102,6 +95,13 @@ public: //---------------------------------------------------------------------------------------------------------------------- ECogInterfacesAllegiance GetAllegianceWithOtherActor(const AActor* OtherActor) const override; + //---------------------------------------------------------------------------------------------------------------------- + // ICogSampleTargetInterface overrides + //---------------------------------------------------------------------------------------------------------------------- + virtual FVector GetTargetLocation() const override; + + virtual void GetTargetCapsules(TArray& Capsules) const override; + //---------------------------------------------------------------------------------------------------------------------- void OnAcknowledgePossession(APlayerController* InController); @@ -181,13 +181,13 @@ public: //---------------------------------------------------------------------------------------------------------------------- UFUNCTION(BlueprintPure) - int32 GetTeamID() const { return TeamID; } + virtual int32 GetTeam() const override { return Team; } UFUNCTION(BlueprintCallable) void SetTeamID(int32 Value); UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Team, Replicated, meta = (AllowPrivateAccess = "true")) - int32 TeamID = 0; + int32 Team = 0; //---------------------------------------------------------------------------------------------------------------------- // Root Motion diff --git a/Source/CogSample/CogSampleFunctionLibrary_Gameplay.cpp b/Source/CogSample/CogSampleFunctionLibrary_Gameplay.cpp index 70fcc7d..42bc85b 100644 --- a/Source/CogSample/CogSampleFunctionLibrary_Gameplay.cpp +++ b/Source/CogSample/CogSampleFunctionLibrary_Gameplay.cpp @@ -4,10 +4,12 @@ #include "AbilitySystemComponent.h" #include "AbilitySystemGlobals.h" #include "CogSampleGameplayEffectContext.h" +#include "CogSampleTargetableInterface.h" #include "Components/CapsuleComponent.h" #include "GameFramework/Character.h" #include "GameplayCueNotifyTypes.h" #include "GameplayEffectTypes.h" +#include "Kismet/KismetMathLibrary.h" #include "Particles/ParticleSystemComponent.h" #include "ScalableFloat.h" @@ -108,3 +110,144 @@ int32 UCogSampleFunctionLibrary_Gameplay::GetIntValue(const FScalableFloat& Scal return (int32)ScalableFloat.GetValueAtLevel(Level); } +//-------------------------------------------------------------------------------------------------------------------------- +FCollisionObjectQueryParams UCogSampleFunctionLibrary_Gameplay::ConfigureCollisionObjectParams(const TArray>& ObjectTypes) +{ + TArray> CollisionObjectTraces; + CollisionObjectTraces.AddUninitialized(ObjectTypes.Num()); + + for (auto Iter = ObjectTypes.CreateConstIterator(); Iter; ++Iter) + { + CollisionObjectTraces[Iter.GetIndex()] = UEngineTypes::ConvertToCollisionChannel(*Iter); + } + + FCollisionObjectQueryParams ObjectParams; + for (auto Iter = CollisionObjectTraces.CreateConstIterator(); Iter; ++Iter) + { + const ECollisionChannel& Channel = (*Iter); + if (FCollisionObjectQueryParams::IsValidObjectQuery(Channel)) + { + ObjectParams.AddObjectTypesToQuery(Channel); + } + } + + return ObjectParams; +} + +//-------------------------------------------------------------------------------------------------------------------------- +FVector UCogSampleFunctionLibrary_Gameplay::GetActorTargetLocation(const AActor* Actor) +{ + if (Actor == nullptr) + { + return FVector::ZeroVector; + } + + if (const ICogSampleTargetableInterface* Targetable = Cast(Actor)) + { + return Targetable->GetTargetLocation(); + } + + return Actor->GetActorLocation(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +float UCogSampleFunctionLibrary_Gameplay::AngleBetweenVector2D(FVector2D A, FVector2D B) +{ + A.Normalize(); + B.Normalize(); + return FMath::RadiansToDegrees(FMath::Acos(FVector2D::DotProduct(A, B))); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleFunctionLibrary_Gameplay::FindSegmentPointDistance(const FVector2D& Segment1, const FVector2D& Segment2, const FVector2D& Point, FVector2D& Projection, float& Time, float& Distance) +{ + const float DistSquared = FVector2D::DistSquared(Segment1, Segment2); + if (FMath::IsNearlyZero(DistSquared)) + { + Time = 0.0f; + Projection = Segment1; + Distance = FVector2D::Distance(Point, Segment1); + } + else + { + Time = FMath::Max(0.0f, FMath::Min(1.0f, FVector2D::DotProduct(Point - Segment1, Segment2 - Segment1) / DistSquared)); + Projection = Segment1 + Time * (Segment2 - Segment1); + Distance = FVector2D::Distance(Point, Projection); + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleFunctionLibrary_Gameplay::FindCapsulePointDistance(const FVector2D& CapsulePoint1, const FVector2D& CapsulePoint2, const float CapsuleRadius, const FVector2D& Point, FVector2D& Projection, float& Time, float& Distance) +{ + FindSegmentPointDistance(CapsulePoint1, CapsulePoint2, Point, Projection, Time, Distance); + + float projectionToPointDistance = Distance; + Distance -= CapsuleRadius; + + if (Distance > 0.0f) + { + Projection = Point + ((Projection - Point) / projectionToPointDistance) * Distance; + } + else + { + Projection = Point; + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +FVector2D UCogSampleFunctionLibrary_Gameplay::ViewportToScreen(const FVector2D& value, const FVector2D& displaySize) +{ + FVector2D result; + + if (displaySize.X > displaySize.Y) + { + const float screenXStart = (displaySize.X - displaySize.Y) * 0.5f; + const float screenXEnd = displaySize.X - screenXStart; + result.X = UKismetMathLibrary::MapRangeUnclamped(value.X, -1.0f, 1.0f, screenXStart, screenXEnd); + result.Y = UKismetMathLibrary::MapRangeUnclamped(value.Y, -1.0f, 1.0f, displaySize.Y, 0.0f); + } + else + { + const float ScreenYStart = (displaySize.Y - displaySize.X) * 0.5f; + const float ScreenYEnd = displaySize.Y - ScreenYStart; + result.X = UKismetMathLibrary::MapRangeUnclamped(value.X, -1.0f, 1.0f, 0.0f, displaySize.X); + result.Y = UKismetMathLibrary::MapRangeUnclamped(value.Y, -1.0f, 1.0f, ScreenYEnd, ScreenYStart); + } + + return result; +} + +//-------------------------------------------------------------------------------------------------------------------------- +float UCogSampleFunctionLibrary_Gameplay::ViewportToScreen(const float Value, const FVector2D& DisplaySize) +{ + return Value * 0.5f * FMath::Min(DisplaySize.X, DisplaySize.Y); +} + +//-------------------------------------------------------------------------------------------------------------------------- +FVector2D UCogSampleFunctionLibrary_Gameplay::ScreenToViewport(const FVector2D& Value, const FVector2D& DisplaySize) +{ + FVector2D Result; + + if (DisplaySize.X > DisplaySize.Y) + { + const float screenXStart = (DisplaySize.X - DisplaySize.Y) * 0.5f; + const float screenXEnd = DisplaySize.X - screenXStart; + Result.X = UKismetMathLibrary::MapRangeUnclamped(Value.X, screenXStart, screenXEnd, -1.0f, 1.0f); + Result.Y = UKismetMathLibrary::MapRangeUnclamped(Value.Y, 0.0f, DisplaySize.Y, -1.0f, 1.0f); + } + else + { + const float screenYStart = (DisplaySize.Y - DisplaySize.X) * 0.5f; + const float screenYEnd = DisplaySize.Y - screenYStart; + Result.X = UKismetMathLibrary::MapRangeUnclamped(Value.X, 0.0f, DisplaySize.X, -1.0f, 1.0f); + Result.Y = UKismetMathLibrary::MapRangeUnclamped(Value.Y, screenYStart, screenYEnd, -1.0f, 1.0f); + } + + return Result; +} + +//-------------------------------------------------------------------------------------------------------------------------- +float UCogSampleFunctionLibrary_Gameplay::ScreenToViewport(const float Value, const FVector2D& DisplaySize) +{ + return Value * 2.0f / FMath::Min(DisplaySize.X, DisplaySize.Y); +} \ No newline at end of file diff --git a/Source/CogSample/CogSampleFunctionLibrary_Gameplay.h b/Source/CogSample/CogSampleFunctionLibrary_Gameplay.h index 29a58ac..7974fc8 100644 --- a/Source/CogSample/CogSampleFunctionLibrary_Gameplay.h +++ b/Source/CogSample/CogSampleFunctionLibrary_Gameplay.h @@ -11,12 +11,14 @@ struct FGameplayAttributeData; struct FGameplayCueNotify_SpawnResult; struct FGameplayCueParameters; +//-------------------------------------------------------------------------------------------------------------------------- #define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \ GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \ GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \ GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \ GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName) +//-------------------------------------------------------------------------------------------------------------------------- UCLASS(meta = (ScriptName = "CogSampleFunctionLibrary_Gameplay")) class UCogSampleFunctionLibrary_Gameplay : public UBlueprintFunctionLibrary { @@ -24,8 +26,6 @@ class UCogSampleFunctionLibrary_Gameplay : public UBlueprintFunctionLibrary public: - static void AdjustAttributeForMaxChange(UAbilitySystemComponent* AbilityComponent, FGameplayAttributeData& AffectedAttribute, float OldValue, float NewMaxValue, const FGameplayAttribute& AffectedAttributeProperty); - UFUNCTION(BlueprintPure) static FVector GetActorBottomLocation(const AActor* Actor); @@ -49,4 +49,28 @@ public: UFUNCTION(BlueprintPure) static int32 GetIntValue(const FScalableFloat& ScalableFloat, int32 Level); + + UFUNCTION(BlueprintPure) + static FVector GetActorTargetLocation(const AActor* Actor); + + UFUNCTION(BlueprintPure) + static float AngleBetweenVector2D(FVector2D A, FVector2D B); + + UFUNCTION(BlueprintPure) + static void FindSegmentPointDistance(const FVector2D& Segment1, const FVector2D& Segment2, const FVector2D& Point, FVector2D& Projection, float& Time, float& Distance); + + UFUNCTION(BlueprintPure) + static void FindCapsulePointDistance(const FVector2D& CapsulePoint1, const FVector2D& CapsulePoint2, const float CapsuleRadius, const FVector2D& Point, FVector2D& Projection, float& Time, float& Distance); + + static void AdjustAttributeForMaxChange(UAbilitySystemComponent* AbilityComponent, FGameplayAttributeData& AffectedAttribute, float OldValue, float NewMaxValue, const FGameplayAttribute& AffectedAttributeProperty); + + static FCollisionObjectQueryParams ConfigureCollisionObjectParams(const TArray>& ObjectTypes); + + static FVector2D ViewportToScreen(const FVector2D& value, const FVector2D& displaySize); + + static float ViewportToScreen(const float value, const FVector2D& displaySize); + + static FVector2D ScreenToViewport(const FVector2D& value, const FVector2D& displaySize); + + static float ScreenToViewport(const float value, const FVector2D& displaySize); }; diff --git a/Source/CogSample/CogSampleFunctionLibrary_Team.cpp b/Source/CogSample/CogSampleFunctionLibrary_Team.cpp new file mode 100644 index 0000000..c5b06ac --- /dev/null +++ b/Source/CogSample/CogSampleFunctionLibrary_Team.cpp @@ -0,0 +1,70 @@ +#include "CogSampleFunctionLibrary_Team.h" + +#include "CogSampleTeamInterface.h" + +//-------------------------------------------------------------------------------------------------------------------------- +ECogSampleAllegiance UCogSampleFunctionLibrary_Team::GetTeamsAllegiance(int32 Team1, int32 Team2) +{ + if (Team1 == 0 || Team2 == 0) + { + return ECogSampleAllegiance::Neutral; + } + + if (Team1 == Team2) + { + return ECogSampleAllegiance::Friendly; + } + + return ECogSampleAllegiance::Enemy; +} + +//-------------------------------------------------------------------------------------------------------------------------- +ECogSampleAllegiance UCogSampleFunctionLibrary_Team::GetActorsAllegiance(const AActor* Actor1, const AActor* Actor2) +{ + const ICogSampleTeamInterface* TeamActor1 = Cast(Actor1); + const ICogSampleTeamInterface* TeamActor2 = Cast(Actor2); + + if (TeamActor1 == nullptr || TeamActor2 == nullptr) + { + return ECogSampleAllegiance::Neutral; + } + + const int32 Team1 = TeamActor1->GetTeam(); + const int32 Team2 = TeamActor2->GetTeam(); + const ECogSampleAllegiance Allegiance = GetTeamsAllegiance(Team1, Team2); + + return Allegiance; +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool UCogSampleFunctionLibrary_Team::MatchAllegiance(const AActor* Actor1, const AActor* Actor2, int32 AllegianceFilter) +{ + const ECogSampleAllegiance Allegiance = GetActorsAllegiance(Actor1, Actor2); + const bool Result = MatchAllegianceFilter(Allegiance, AllegianceFilter); + return Result; +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool UCogSampleFunctionLibrary_Team::MatchAllegianceFilter(ECogSampleAllegiance Allegiance, int32 AllegianceFilter) +{ + const bool HasFriendly = (AllegianceFilter & (int)ECogSampleAllegianceFilter::Friendly) != 0; + const bool HasNeutral = (AllegianceFilter & (int)ECogSampleAllegianceFilter::Neutral) != 0; + const bool HasEnemy = (AllegianceFilter & (int)ECogSampleAllegianceFilter::Enemy) != 0; + + if (Allegiance == ECogSampleAllegiance::Friendly && HasFriendly) + { + return true; + } + + if (Allegiance == ECogSampleAllegiance::Neutral && HasNeutral) + { + return true; + } + + if (Allegiance == ECogSampleAllegiance::Enemy && HasEnemy) + { + return true; + } + + return false; +} \ No newline at end of file diff --git a/Source/CogSample/CogSampleFunctionLibrary_Team.h b/Source/CogSample/CogSampleFunctionLibrary_Team.h new file mode 100644 index 0000000..69ad7ae --- /dev/null +++ b/Source/CogSample/CogSampleFunctionLibrary_Team.h @@ -0,0 +1,46 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CogSampleFunctionLibrary_Team.generated.h" + +//-------------------------------------------------------------------------------------------------------------------------- +UENUM(BlueprintType) +enum class ECogSampleAllegiance : uint8 +{ + Friendly, + Neutral, + Enemy, +}; + +//-------------------------------------------------------------------------------------------------------------------------- +UENUM(BlueprintType, meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true")) +enum class ECogSampleAllegianceFilter : uint8 +{ + None = 0 UMETA(Hidden), + Friendly = 1 << 0, + Neutral = 1 << 1, + Enemy = 1 << 2, +}; +ENUM_CLASS_FLAGS(ECogSampleAllegianceFilter); + +//-------------------------------------------------------------------------------------------------------------------------- +UCLASS(meta = (ScriptName = "CogSampleFunctionLibrary_Team")) +class UCogSampleFunctionLibrary_Team : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + + UFUNCTION(BlueprintPure) + static ECogSampleAllegiance GetTeamsAllegiance(int32 Team1, int32 Team2); + + UFUNCTION(BlueprintPure) + static ECogSampleAllegiance GetActorsAllegiance(const AActor* Actor1, const AActor* Actor2); + + UFUNCTION(BlueprintPure) + static bool MatchAllegiance(const AActor* Actor1, const AActor* Actor2, UPARAM(meta = (Bitmask, BitmaskEnum = "/Script/CogSample.ECogSampleAllegianceFilter")) int32 AllegianceFilter); + + UFUNCTION(BlueprintPure) + static bool MatchAllegianceFilter(ECogSampleAllegiance Allegiance, UPARAM(meta = (Bitmask, BitmaskEnum = "/Script/CogSample.ECogSampleAllegianceFilter")) int32 AllegianceFilter); + +}; diff --git a/Source/CogSample/CogSampleGameState.cpp b/Source/CogSample/CogSampleGameState.cpp index 313c9ce..cb1ca49 100644 --- a/Source/CogSample/CogSampleGameState.cpp +++ b/Source/CogSample/CogSampleGameState.cpp @@ -22,6 +22,7 @@ #include "CogAbilityWindow_Tags.h" #include "CogAbilityWindow_Tweaks.h" #include "CogDebugDefines.h" +#include "CogDebugDrawImGui.h" #include "CogDebugPlot.h" #include "CogEngineDataAsset_Collisions.h" #include "CogEngineDataAsset_Spawns.h" @@ -220,6 +221,7 @@ void ACogSampleGameState::InitializeCog() void ACogSampleGameState::RenderCog(float DeltaTime) { CogWindowManager->Render(DeltaTime); + FCogDebugDrawImGui::Draw(); } #endif //USE_COG diff --git a/Source/CogSample/CogSampleLogCategories.cpp b/Source/CogSample/CogSampleLogCategories.cpp index 53da048..0cd8556 100644 --- a/Source/CogSample/CogSampleLogCategories.cpp +++ b/Source/CogSample/CogSampleLogCategories.cpp @@ -4,7 +4,7 @@ #include "CogDefines.h" #if USE_COG -#include "CogDebugLogCategoryManager.h" +#include "CogDebugLog.h" #endif //USE_COG DEFINE_LOG_CATEGORY(LogCogAlways); @@ -15,22 +15,24 @@ DEFINE_LOG_CATEGORY(LogCogRotation); DEFINE_LOG_CATEGORY(LogCogControlRotation); DEFINE_LOG_CATEGORY(LogCogBaseAimRotation); DEFINE_LOG_CATEGORY(LogCogSkeleton); +DEFINE_LOG_CATEGORY(LogCogTargetAcquisition); namespace CogSampleLog { void RegiterAllLogCategories() { #if USE_COG - FCogDebugLogCategoryManager::AddLogCategory(LogCogAlways, "Always", false); - FCogDebugLogCategoryManager::AddLogCategory(LogAbilitySystem, "Ability System"); - FCogDebugLogCategoryManager::AddLogCategory(LogGameplayEffects, "Gameplay Effects"); - FCogDebugLogCategoryManager::AddLogCategory(LogCogCollision, "Collision"); - FCogDebugLogCategoryManager::AddLogCategory(LogCogInput, "Input"); - FCogDebugLogCategoryManager::AddLogCategory(LogCogPosition, "Position"); - FCogDebugLogCategoryManager::AddLogCategory(LogCogRotation, "Rotation"); - FCogDebugLogCategoryManager::AddLogCategory(LogCogControlRotation, "ControlRotation"); - FCogDebugLogCategoryManager::AddLogCategory(LogCogBaseAimRotation, "BaseAimRotation"); - FCogDebugLogCategoryManager::AddLogCategory(LogCogSkeleton, "Skeleton"); + FCogDebugLog::AddLogCategory(LogCogAlways, "Always", false); + FCogDebugLog::AddLogCategory(LogAbilitySystem, "Ability System"); + FCogDebugLog::AddLogCategory(LogGameplayEffects, "Gameplay Effects"); + FCogDebugLog::AddLogCategory(LogCogCollision, "Collision"); + FCogDebugLog::AddLogCategory(LogCogInput, "Input"); + FCogDebugLog::AddLogCategory(LogCogPosition, "Position"); + FCogDebugLog::AddLogCategory(LogCogRotation, "Rotation"); + FCogDebugLog::AddLogCategory(LogCogControlRotation, "ControlRotation"); + FCogDebugLog::AddLogCategory(LogCogBaseAimRotation, "BaseAimRotation"); + FCogDebugLog::AddLogCategory(LogCogSkeleton, "Skeleton"); + FCogDebugLog::AddLogCategory(LogCogTargetAcquisition, "Target Acquisition"); #endif //USE_COG } } diff --git a/Source/CogSample/CogSampleLogCategories.h b/Source/CogSample/CogSampleLogCategories.h index c85f12b..8d8da8e 100644 --- a/Source/CogSample/CogSampleLogCategories.h +++ b/Source/CogSample/CogSampleLogCategories.h @@ -8,6 +8,7 @@ DECLARE_LOG_CATEGORY_EXTERN(LogCogRotation, Warning, All); DECLARE_LOG_CATEGORY_EXTERN(LogCogControlRotation, Warning, All); DECLARE_LOG_CATEGORY_EXTERN(LogCogBaseAimRotation, Warning, All); DECLARE_LOG_CATEGORY_EXTERN(LogCogSkeleton, Warning, All); +DECLARE_LOG_CATEGORY_EXTERN(LogCogTargetAcquisition, Warning, All); namespace CogSampleLog { diff --git a/Source/CogSample/CogSamplePlayerController.cpp b/Source/CogSample/CogSamplePlayerController.cpp index 4fc271c..6ce3d86 100644 --- a/Source/CogSample/CogSamplePlayerController.cpp +++ b/Source/CogSample/CogSamplePlayerController.cpp @@ -2,6 +2,7 @@ #include "CogDefines.h" #include "CogSampleCharacter.h" +#include "CogSampleTargetAcquisition.h" #include "Net/UnrealNetwork.h" #if USE_COG @@ -38,4 +39,18 @@ void ACogSamplePlayerController::AcknowledgePossession(APawn* P) { PossessedCharacter->OnAcknowledgePossession(this); } +} + +//-------------------------------------------------------------------------------------------------------------------------- +void ACogSamplePlayerController::Tick(float DeltaSeconds) +{ + Super::Tick(DeltaSeconds); + + if (TargetAcquisition != nullptr) + { + TArray TagretToIgnore; + FCogSampleTargetAcquisitionResult Result; + TargetAcquisition->FindBestTarget(this, TagretToIgnore, nullptr, true, FVector2D::ZeroVector, false, Result); + Target = Result.Target; + } } \ No newline at end of file diff --git a/Source/CogSample/CogSamplePlayerController.h b/Source/CogSample/CogSamplePlayerController.h index f071173..89cc44b 100644 --- a/Source/CogSample/CogSamplePlayerController.h +++ b/Source/CogSample/CogSamplePlayerController.h @@ -5,6 +5,8 @@ #include "GameFramework/PlayerController.h" #include "CogSamplePlayerController.generated.h" +class UCogSampleTargetAcquisition; + UCLASS(config=Game) class ACogSamplePlayerController : public APlayerController { @@ -13,11 +15,19 @@ class ACogSamplePlayerController : public APlayerController public: ACogSamplePlayerController(); + virtual void BeginPlay() override; + virtual void AcknowledgePossession(APawn* P); + virtual void Tick(float DeltaSeconds); + private: + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = TargetAcquisition, meta = (AllowPrivateAccess = "true")) + UCogSampleTargetAcquisition* TargetAcquisition = nullptr; + UPROPERTY(BlueprintReadOnly, Category = TargetAcquisition, meta = (AllowPrivateAccess = "true")) + TSoftObjectPtr Target = nullptr; }; diff --git a/Source/CogSample/CogSampleTargetAcquisition.cpp b/Source/CogSample/CogSampleTargetAcquisition.cpp new file mode 100644 index 0000000..4ba3299 --- /dev/null +++ b/Source/CogSample/CogSampleTargetAcquisition.cpp @@ -0,0 +1,886 @@ +#include "CogSampleTargetAcquisition.h" + +#include "Camera/CameraComponent.h" +#include "CogDefines.h" +#include "CogSampleCharacter.h" +#include "CogSampleFunctionLibrary_Gameplay.h" +#include "CogSampleLogCategories.h" +#include "CogSampleTargetableInterface.h" +#include "Components/CapsuleComponent.h" +#include "GameFramework/PlayerController.h" + +#if USE_COG +#include "CogDebugDraw.h" +#include "CogDebugLog.h" +#endif //USE_COG + +//-------------------------------------------------------------------------------------------------------------------------- +// UCogSampleTargetAcquisition_Generic +//-------------------------------------------------------------------------------------------------------------------------- +struct FCogSampleTargetCandidateEvaluationParameters +{ + const AActor* Source; + FVector SourceLocation; + const APlayerController* Controller; + TArray TargetsToIgnore; + bool bWorldDistanceIgnoreZ = false; + float MaxWorldDistance = 0.0f; + FVector2D CrosshairPosition = FVector2D::ZeroVector; + FVector YawDirection = FVector::ZeroVector; + FVector CameraRight = FVector::ZeroVector; + FMatrix ViewProjectionMatrix; + FIntRect ViewRect; + FCollisionObjectQueryParams BlockersParams; + bool bUseSearchDirection = false; + FVector2D SearchDirectionScreenOrigin = FVector2D::ZeroVector; + FVector2D SearchDirectionNormalized = FVector2D::ZeroVector; + bool IsDebugPersistent = false; +}; + +//-------------------------------------------------------------------------------------------------------------------------- +struct FCogSampleTargetCandidateEvaluationResult +{ + AActor* BestTarget; + float MinScore; + bool bFoundLocationInsideShape; +}; + +//-------------------------------------------------------------------------------------------------------------------------- +// UCogSampleTargetAcquisition_Generic +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleTargetAcquisition::FindBestTargets( + const APlayerController* Controller, + const int32 TargetCount, + const TArray& TargetsToIgnore, + const AActor* CurrentLockedTarget, + const bool bForceSynchronousDetection, + const FVector2D ScreenSearchDirection, + bool bIsDebugPersistent, + TArray& Results) const +{ + TRACE_CPUPROFILER_EVENT_SCOPE(UCogSampleTargetAcquisition::FindBestTargets); + + COG(FCogDebugDraw::String2D(LogCogTargetAcquisition, Controller, GetName(), FVector2D(20, 20), FColor::White, bIsDebugPersistent)); + + TArray TempTargetsToIgnore(TargetsToIgnore); + + //--------------------------------------- + // Find multiple target + //--------------------------------------- + for (int32 i = 0; i < TargetCount; i++) + { + FCogSampleTargetAcquisitionResult Result; + FindBestTarget( + Controller, + TempTargetsToIgnore, + CurrentLockedTarget, + bForceSynchronousDetection, + ScreenSearchDirection, + bIsDebugPersistent, + Result); + + //--------------------------------------- + // Stop when out of valid targets + //--------------------------------------- + if (Result.Target == nullptr) + { + break; + } + + Results.Add(Result); + TempTargetsToIgnore.Add(Result.Target); + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleTargetAcquisition::FindBestTarget( + const APlayerController* Controller, + const TArray& TargetsToIgnore, + const AActor* CurrentLockedTarget, + const bool bForceSynchronousDetection, + const FVector2D TargetSwitchSearchDirection, + const bool bIsDebugPersistent, + FCogSampleTargetAcquisitionResult& Result) const +{ + TRACE_CPUPROFILER_EVENT_SCOPE(UCogSampleTargetAcquisition::FindBestTarget); + + ACogSampleCharacter* Character = Cast(Controller->GetPawn()); + if (Character == nullptr) + { + return; + } + + if (FMath::IsNearlyZero(DetectionLength) && FMath::IsNearlyZero(DetectionRadius)) + { + return; + } + + FMatrix ViewProjectionMatrix; + FIntRect ViewRect; + if (GetViewInfo(Controller, ViewProjectionMatrix, ViewRect) == false) + { + return; + } + + FVector2D SearchDirectionScreenOrigin(ViewRect.Width() / 2.0f, ViewRect.Height() / 2.0f); + if (CurrentLockedTarget != nullptr) + { + FVector CurrentLockedTargetLocation = UCogSampleFunctionLibrary_Gameplay::GetActorTargetLocation(CurrentLockedTarget); + FSceneView::ProjectWorldToScreen(CurrentLockedTargetLocation, ViewRect, ViewProjectionMatrix, SearchDirectionScreenOrigin); + } + + //---------------------------------------------------------------------------------------------------------------------- + // Draw the ScreenSearchDirection if valid + //---------------------------------------------------------------------------------------------------------------------- +#if USE_COG + const FVector2D SearchDirectionNormalized = (TargetSwitchSearchDirection.IsNearlyZero() == false) ? TargetSwitchSearchDirection.GetSafeNormal() : FVector2D::ZeroVector; + if (SearchDirectionNormalized.IsNearlyZero() == false) + { + COG(FCogDebugDraw::Segment2D(LogCogTargetAcquisition, Controller, FVector2D::ZeroVector, FVector2D(SearchDirectionNormalized.X, -SearchDirectionNormalized.Y), FColor(255, 255, 0, 255), bIsDebugPersistent)); + } +#endif //USE_COG + + static const FName TraceTag(TEXT("FindLockTarget_GatherTargets")); + FCollisionQueryParams QueryParams(TraceTag, SCENE_QUERY_STAT_ONLY(CogSampleTargetAcquisition), false); + QueryParams.bReturnPhysicalMaterial = true; + QueryParams.bReturnFaceIndex = false; + QueryParams.AddIgnoredActor(Character); + + const FCollisionObjectQueryParams ObjectParams = UCogSampleFunctionLibrary_Gameplay::ConfigureCollisionObjectParams(ObjectTypes); + const FVector CastLocation = GetReferentialLocation(Character, DetectionLocation); + const FRotator CastRotation = GetReferentialRotation(Character, DetectionRotation); + const float CapsuleHalfHeight = DetectionLength * 0.5f; + const float CapsuleRadius = DetectionRadius; + const FVector CapsuleCenter = CastLocation + CastRotation.Vector() * CapsuleHalfHeight; + const FQuat CapsuleRotation = (CastRotation + FRotator(90, 0, 0)).Quaternion(); + const FCollisionShape CapsuleShape = FCollisionShape::MakeCapsule(CapsuleRadius, CapsuleHalfHeight); + + COG(FCogDebugDraw::Capsule(LogCogTargetAcquisition, Controller, CapsuleCenter, CapsuleHalfHeight, CapsuleRadius, CapsuleRotation, FColor::Yellow, bIsDebugPersistent, 0)); + + //------------------------------------------------- + // Gather targets asynchronously + //------------------------------------------------- + TArray QueriedActors; + //if (UseAsyncDetection && bForceSynchronousDetection == false) + //{ + // TRACE_CPUPROFILER_EVENT_SCOPE(UCogSampleTargetAcquisition_Generic::EvaluateCandidateQueryAsync); + + // FOverlapDatum OutData; + // if (Character->GetWorld()->QueryOverlapData(QueryHandle, OutData)) + // { + // for (const FOverlapResult& Overlap : OutData.OutOverlaps) + // { + // if (AActor* Target = Overlap.GetActor()) + // { + // QueriedActors.Add(Target); + // } + // } + // } + + // QueryHandle = Character->GetWorld()->AsyncOverlapByObjectType( + // CapsuleCenter, + // CapsuleRotation, + // ObjectParams, + // CapsuleShape, + // QueryParams); + //} + //------------------------------------------------- + // Gather targets synchronously + //------------------------------------------------- + //else + { + TRACE_CPUPROFILER_EVENT_SCOPE(UCogSampleTargetAcquisition::EvaluateCandidateQuerySync); + + TArray OutOverlaps; + const bool Hit = Character->GetWorld()->OverlapMultiByObjectType( + OutOverlaps, + CapsuleCenter, + CapsuleRotation, + ObjectParams, + CapsuleShape, + QueryParams); + + for (const FOverlapResult& Overlap : OutOverlaps) + { + if (AActor* Target = Overlap.GetActor()) + { + QueriedActors.Add(Target); + } + } + } + + //------------------------------------------------- + // Validate actors + //------------------------------------------------- + TArray ValidCandidates; + { + TRACE_CPUPROFILER_EVENT_SCOPE(UCogSampleTargetAcquisition::EvaluateCandidateCheckValids); + + for (AActor* Actor : QueriedActors) + { + if (CheckIfTargetValid(Controller, Character, Actor) == false) + { + COG(FCogDebugDraw::String(LogCogTargetAcquisition, Controller, "Filter", UCogSampleFunctionLibrary_Gameplay::GetActorTargetLocation(Actor), FColor::Red, bIsDebugPersistent)); + continue; + } + + ValidCandidates.Add(Actor); + } + } + + FindBestTargetInCandidates(Controller, TargetsToIgnore, ValidCandidates, TargetSwitchSearchDirection, SearchDirectionScreenOrigin, bIsDebugPersistent, Result); +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool UCogSampleTargetAcquisition::GetViewInfo( + const APlayerController* Controller, + FMatrix& viewProjectionMatrix, + FIntRect& viewRect) const +{ + const ULocalPlayer* localPlayer = Controller->GetLocalPlayer(); + if (localPlayer == nullptr || localPlayer->ViewportClient == nullptr) + { + return false; + } + + FSceneViewProjectionData projectionData; + if (localPlayer->GetProjectionData(localPlayer->ViewportClient->Viewport, projectionData) == false) + { + return false; + } + + viewProjectionMatrix = projectionData.ComputeViewProjectionMatrix(); + viewRect = projectionData.GetConstrainedViewRect(); + + return true; +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool UCogSampleTargetAcquisition::EvaluateCandidate( + AActor* CandidateTarget, + const FCogSampleTargetCandidateEvaluationParameters& EvalParams, + FCogSampleTargetCandidateEvaluationResult& EvalResult) const +{ + TRACE_CPUPROFILER_EVENT_SCOPE(UCogSampleTargetAcquisition::EvaluateCandidate); + + if (EvalParams.TargetsToIgnore.Contains(CandidateTarget)) + { + return false; + } + + const FVector CandidateTargetLocation = UCogSampleFunctionLibrary_Gameplay::GetActorTargetLocation(CandidateTarget); + + FVector CandidateLocationDelta = CandidateTargetLocation - EvalParams.SourceLocation; + if (EvalParams.bWorldDistanceIgnoreZ) + { + CandidateLocationDelta.Z = 0.0f; + } + const float CandidateWorldDistance = CandidateLocationDelta.Length(); + const FVector CandidateWorldDirection = CandidateWorldDistance > 0.0f ? CandidateLocationDelta / CandidateWorldDistance : FVector::ZeroVector; + const float ScreenMagnitude = FMath::Min(EvalParams.ViewRect.Width(), EvalParams.ViewRect.Height()); + const bool IsSearchDirectionUsed = bUseSearchDirectionScore && EvalParams.SearchDirectionNormalized.IsNearlyZero() == false; + + //-------------------------------------------------------------------------------------------------------------- + // Filter by world distance limit + //-------------------------------------------------------------------------------------------------------------- + if (CandidateWorldDistance > EvalParams.MaxWorldDistance) + { + COG(FCogDebugDraw::String(LogCogTargetAcquisition, EvalParams.Controller, FString::Printf(TEXT("Dist: %0.2f"), CandidateWorldDistance * 0.01f), CandidateTargetLocation, FColor::Red, EvalParams.IsDebugPersistent)); + return false; + } + + //-------------------------------------------------------------------------------------------------------------- + // Filter candidates base on yaw limit when using yaw, otherwise filter anyone behind the character + //-------------------------------------------------------------------------------------------------------------- + const FVector CandidateWorldDirectionFlat = CandidateWorldDirection.GetSafeNormal2D(); + const float CandidateDot = EvalParams.YawDirection.Dot(CandidateWorldDirectionFlat); + const float CandidateYaw = FRotator::NormalizeAxis(FMath::RadiansToDegrees(FMath::Acos(CandidateDot))); + if (bUseYawLimit && CandidateYaw > YawMax) + { + COG(FCogDebugDraw::String(LogCogTargetAcquisition, EvalParams.Controller, FString::Printf(TEXT("Yaw: %0.2f"), CandidateYaw), CandidateTargetLocation, FColor::Red, EvalParams.IsDebugPersistent)); + return false; + } + + //-------------------------------------------------------------------------------------------------------------- + // Find the candidate screen location. If the result distance is lower than zero, it means the crosshair is + // inside the candidate capsules. + //-------------------------------------------------------------------------------------------------------------- + FVector2D CandidateScreenLocation; + FVector2D CandidateClosestScreenLocation; + float CandidateClosestScreenDistance; + const UCapsuleComponent* CandidateBestHitZone = nullptr; + if (!ComputeCandidateScreenLocation(CandidateTarget, EvalParams, CandidateTargetLocation, CandidateScreenLocation, CandidateClosestScreenLocation, CandidateClosestScreenDistance)) + { + return false; + } + + //-------------------------------------------------------------------------------------------------------------- + // Filter candidates not inside the screen distance limits. + //-------------------------------------------------------------------------------------------------------------- + if (bUseScreenLimit) + { + if (!CheckCandidateWithinScreenDistance(EvalParams.Controller, EvalParams.ViewRect, CandidateTargetLocation, CandidateClosestScreenLocation, CandidateClosestScreenDistance, EvalParams.IsDebugPersistent)) + { + return false; + } + } + + //-------------------------------------------------------------------------------------------------------------- + // Raycast to verify this target is not blocked by a collision. + //-------------------------------------------------------------------------------------------------------------- + if (!HasLineOfSightToTarget(EvalParams.Source, CandidateTarget, EvalParams.BlockersParams)) + { + return false; + } + + const bool bIsInsideCandidate = CandidateClosestScreenDistance < 0.0f; + if (!IsSearchDirectionUsed && bPrioritizeInsideHitZones) + { + //-------------------------------------------------------------------------------------------------------------- + // We always prioritize the candidates if the crosshair is inside them. Thus if we are inside another candidate + // but not inside this one, we discard it (unless we are switching the lock target) + //-------------------------------------------------------------------------------------------------------------- + if (EvalResult.bFoundLocationInsideShape && bIsInsideCandidate == false) + { + return false; + } + + //-------------------------------------------------------------------------------------------------------------- + // if we are inside the capsule of this candidate, we can discard subsequent candidates that are not inside + // but also the best previous candidate since we were not inside it. + //-------------------------------------------------------------------------------------------------------------- + if (bIsInsideCandidate && EvalResult.bFoundLocationInsideShape == false) + { + EvalResult.BestTarget = nullptr; + EvalResult.MinScore = FLT_MAX; + } + } + + //-------------------------------------------------------------------------------------------------------------- + // Compute a score based on the world distance + //-------------------------------------------------------------------------------------------------------------- + float CandidateWorldDistanceRatio = 0.0f; + float CandidateWorldDistanceScore = 0.0f; + if (bUseWorldDistanceScore) + { + CandidateWorldDistanceRatio = (WorldDistanceMax > 0.0f) ? CandidateWorldDistance / WorldDistanceMax : 0.0f; + CandidateWorldDistanceScore = WorldDistanceScoreMultiplier * (WorldDistanceScoreCurve != nullptr ? WorldDistanceScoreCurve->GetFloatValue(CandidateWorldDistanceRatio) : CandidateWorldDistanceRatio); + } + + //-------------------------------------------------------------------------------------------------------------- + // Compute a score based on screen distance + //-------------------------------------------------------------------------------------------------------------- + float CandidateScreenDistanceRatio = 0.0f; + float CandidateScreenDistanceScore = 0.0f; + if (bUseScreenDistanceScore && IsSearchDirectionUsed == false) + { + CandidateScreenDistanceRatio = CandidateClosestScreenDistance / ScreenMagnitude; + CandidateScreenDistanceScore = ScreenDistanceScoreMultiplier * (ScreenDistanceScoreCurve != nullptr ? ScreenDistanceScoreCurve->GetFloatValue(CandidateScreenDistanceRatio) : CandidateScreenDistanceRatio); + } + + //-------------------------------------------------------------------------------------------------------------- + // Compute a score based on yaw + //-------------------------------------------------------------------------------------------------------------- + float CandidateYawRatio = 0.0f; + float CandidateYawScore = 0.0f; + if (bUseYawScore) + { + CandidateYawRatio = YawMax > 0.0f ? CandidateYaw / YawMax : 0.0f; + CandidateYawScore = YawScoreMultiplier * (YawScoreCurve != nullptr ? YawScoreCurve->GetFloatValue(CandidateYawRatio) : CandidateYawRatio); + } + + //-------------------------------------------------------------------------------------------------------------- + // Compute a score based on the search direction. (for target lock switch) + //-------------------------------------------------------------------------------------------------------------- + float SearchDirectionDistanceScore = 0.0f; + float SearchDirectionAngleScore = 0.0f; + float SearchDirectionDistanceRatio = 0.0f; + float SearchDirectionAngleRatio = 0.0f; + + if (IsSearchDirectionUsed) + { + const float TargetAngleWithSearchDirection = UCogSampleFunctionLibrary_Gameplay::AngleBetweenVector2D(CandidateScreenLocation - EvalParams.SearchDirectionScreenOrigin, EvalParams.SearchDirectionNormalized); + + if (FMath::Abs(TargetAngleWithSearchDirection) > SearchDirectionMaxAngle) + { + COG(FCogDebugDraw::String(LogCogTargetAcquisition, EvalParams.Controller, FString::Printf(TEXT("MaxAngle: %0.2f"), TargetAngleWithSearchDirection), CandidateTargetLocation, FColor::Red, EvalParams.IsDebugPersistent)); + return false; + } + + SearchDirectionDistanceRatio = FVector2D::Distance(CandidateScreenLocation, EvalParams.SearchDirectionScreenOrigin) / ScreenMagnitude; + SearchDirectionDistanceScore = SearchDirectionDistanceScoreMultiplier * (ScreenSearchDirectionDistanceScoreCurve != nullptr ? ScreenSearchDirectionDistanceScoreCurve->GetFloatValue(SearchDirectionDistanceRatio) : SearchDirectionDistanceRatio); + SearchDirectionAngleRatio = SearchDirectionMaxAngle > 0.0f ? TargetAngleWithSearchDirection / SearchDirectionMaxAngle : 0.0f; + SearchDirectionAngleScore = ScreenSearchDirectionAngleScoreMultiplier * (ScreenSearchDirectionAngleScoreCurve != nullptr ? ScreenSearchDirectionAngleScoreCurve->GetFloatValue(SearchDirectionAngleRatio) : SearchDirectionAngleRatio); + } + + //-------------------------------------------------------------------------------------------------------------- + // Compute final score by summing all the scores. The best score is the smallest one. + //-------------------------------------------------------------------------------------------------------------- + const float TargetScore = CandidateWorldDistanceScore + CandidateScreenDistanceScore + CandidateYawScore + SearchDirectionDistanceScore + SearchDirectionAngleScore; + + //-------------------------------------------------------------------------------------------------------------- + // Draw the score of each candidate + //-------------------------------------------------------------------------------------------------------------- +#if USE_COG + + /* + + if (FCogDebugLog::IsLogCategoryActive(LogCogTargetAcquisition)) + { + ImVec2 CandidateClosestViewportLocation = ImGui::ScreenToViewport(ImGui::ToImVec2(CandidateClosestScreenLocation)); + + FCogDebugDraw::Point(LogCogTargetAcquisition, EvalParams.Controller, CandidateTargetLocation, 8.0f, FColor::Blue, EvalParams.IsDebugPersistent, 0); + + FString Text; + if (LogCogTargetAcquisition.GetVerbosity() == ELogVerbosity::VeryVerbose) + { + if (bUseScreenDistanceScore) + { + Text.Append(FString::Printf(TEXT + ( + "XY: %.0f %.0f \n" + "SD: %.0f => %.0f => %.0f \n" + ), + CandidateClosestViewportLocation.x * 100.0f, CandidateClosestViewportLocation.y * 100.0f, + CandidateClosestScreenDistance, CandidateScreenDistanceRatio * 100, CandidateScreenDistanceScore * 100)); + } + + if (bUseWorldDistanceScore) + { + Text.Append(FString::Printf(TEXT("WD: %.0f => %.0f => %.0f \n"), + CandidateWorldDistance * 0.01f, + CandidateWorldDistanceRatio * 100, + CandidateWorldDistanceScore * 100)); + } + + if (bUseYawScore) + { + Text.Append(FString::Printf(TEXT("Y: %.1f => %.0f => %.0f \n"), + CandidateYaw, + CandidateYawRatio * 100, + CandidateYawScore * 100)); + } + + if (bUseSearchDirectionScore) + { + Text.Append(FString::Printf(TEXT("VD: %.0f => %.0f \n" "VA: %.0f => %.0f \n"), + SearchDirectionDistanceRatio * 100, + SearchDirectionDistanceScore * 100, + SearchDirectionAngleRatio * 100, + SearchDirectionAngleScore * 100)); + } + + Text.Append(FString::Printf(TEXT("==> %.0f \n"), TargetScore * 100)); + } + else + { + Text = FString::Printf(TEXT("%0.f"), TargetScore * 100); + } + FCogDebugDraw::String(LogCogTargetAcquisition, EvalParams.Controller, Text, CandidateTargetLocation, FColor::White, EvalParams.IsDebugPersistent); + } + + */ +#endif //USE_COG + + if (EvalResult.MinScore < TargetScore) + { + return false; + } + + EvalResult.BestTarget = CandidateTarget; + EvalResult.MinScore = TargetScore; + EvalResult.bFoundLocationInsideShape = EvalResult.bFoundLocationInsideShape || bIsInsideCandidate; + return true; +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleTargetAcquisition::FindBestTargetInCandidates( + const APlayerController* Controller, + const TArray& TargetsToIgnore, + const TArray& Candidates, + const FVector2D ScreenSearchDirection, + const FVector2D SearchDirectionScreenOrigin, + const bool bIsDebugPersistent, + FCogSampleTargetAcquisitionResult& Result) const +{ + TRACE_CPUPROFILER_EVENT_SCOPE(UCogSampleTargetAcquisition::FindBestTargetInCandidates); + + ACogSampleCharacter* Character = Cast(Controller->GetPawn()); + if (Character == nullptr) + { + return; + } + + //---------------------------------------------------------------------------------------------------------------------- + // Compute view matrix to project each candidate capsules in screen space + //---------------------------------------------------------------------------------------------------------------------- + FMatrix ViewProjectionMatrix; + FIntRect ViewRect; + if (GetViewInfo(Controller, ViewProjectionMatrix, ViewRect) == false) + { + return; + } + + //---------------------------------------------------------------------------------------------------------------------- + // Draw screen limits + //---------------------------------------------------------------------------------------------------------------------- + FVector2D ScreenCrosshairPosition(0.5f * ViewRect.Width(), 0.5f * ViewRect.Height()); + + COG(FCogDebugDraw::Circle2D(LogCogTargetAcquisition, Controller, ScreenCrosshairPosition, 5.0f, FColor(255, 255, 255, 255), bIsDebugPersistent)); + +#if USE_COG + //if (bUseScreenLimit) + //{ + // if (ScreenLimitType == ECogSampleTargetAcquisitionScreenLimitType::Rectangle) + // { + // COG(FCogDebugDraw::Rect2D( + // LogCogTargetAcquisition, + // Controller, + // FVector2D(-ScreenMaxX, -ScreenMaxY), + // FVector2D(ScreenMaxX, ScreenMaxY), + // FColor(255, 255, 255, 255), + // bIsDebugPersistent)); + // } + // else + // { + // COG(FCogDebugDraw::Circle2D( + // LogCogTargetAcquisition, + // Controller, + // ScreenCrosshairPosition, + // ImGui::ViewportToScreen(ScreenMaxX), + // FColor(255, 255, 255, 255), + // bIsDebugPersistent)); + // } + //} +#endif //USE_COG + + const FRotator YawRotation = GetReferentialRotation(Character, YawReferential); + const FVector YawDirection = YawRotation.Vector(); + + FCogSampleTargetCandidateEvaluationParameters EvalParams; + EvalParams.Source = Character; + EvalParams.SourceLocation = Character->GetActorLocation(); + EvalParams.Controller = Controller; + EvalParams.TargetsToIgnore = TargetsToIgnore; + EvalParams.bWorldDistanceIgnoreZ = WorldDistanceIgnoreZ; + EvalParams.MaxWorldDistance = WorldDistanceMax; + EvalParams.CrosshairPosition = ScreenCrosshairPosition; + EvalParams.YawDirection = YawDirection; + EvalParams.CameraRight = Character->GetFollowCamera()->GetComponentQuat().GetRightVector(); + EvalParams.ViewProjectionMatrix = ViewProjectionMatrix; + EvalParams.ViewRect = ViewRect; + EvalParams.BlockersParams = UCogSampleFunctionLibrary_Gameplay::ConfigureCollisionObjectParams(BlockerTypes); + EvalParams.bUseSearchDirection = ScreenSearchDirection.IsNearlyZero() == false; + EvalParams.SearchDirectionScreenOrigin = SearchDirectionScreenOrigin; + EvalParams.SearchDirectionNormalized = EvalParams.bUseSearchDirection ? ScreenSearchDirection.GetSafeNormal() : FVector2D::ZeroVector; + EvalParams.IsDebugPersistent = bIsDebugPersistent; + + FCogSampleTargetCandidateEvaluationResult EvalResult + { + nullptr, + FLT_MAX, + false + }; + + //---------------------------------------------------------------------------------------------------------------------- + // Evaluate candidates actors + //---------------------------------------------------------------------------------------------------------------------- + for (int32 i = 0; i < Candidates.Num(); ++i) + { + AActor* Candidate = Candidates[i]; + check(Candidate != nullptr); + if (Candidate == nullptr) + { + continue; + } + + EvaluateCandidate(Candidate, EvalParams, EvalResult); + } + + if (EvalResult.BestTarget != nullptr) + { + Result.Target = EvalResult.BestTarget; + Result.Score = EvalResult.MinScore; + + COG(FCogDebugDraw::Point(LogCogTargetAcquisition, Controller, UCogSampleFunctionLibrary_Gameplay::GetActorTargetLocation(Result.Target), 10.0f, FColor::Green, EvalParams.IsDebugPersistent, 0)); + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool UCogSampleTargetAcquisition::ComputeCandidateScreenLocation( + const AActor* CandidateActor, + const FCogSampleTargetCandidateEvaluationParameters& EvalParams, + const FVector& CandidateTargetLocation, + FVector2D& CandidateScreenLocation, + FVector2D& CandidateClosestScreenLocation, + float& CandidateClosestScreenDistance) const +{ + TRACE_CPUPROFILER_EVENT_SCOPE(UCogSampleTargetAcquisition::ComputeCandidateScreenLocation); + + check(CandidateActor); + + bool bFoundValidCandidate = false; + CandidateScreenLocation = FVector2D::ZeroVector; + CandidateClosestScreenDistance = FLT_MAX; + + if (const ICogSampleTargetableInterface* Targetable = Cast(CandidateActor)) + { + TArray Capsules; + Targetable->GetTargetCapsules(Capsules); + + for (const UCapsuleComponent* Capsule : Capsules) + { + const float Radius = Capsule->GetScaledCapsuleRadius(); + const float HalfHeight = Capsule->GetScaledCapsuleHalfHeight(); + + const FVector CapsuleLocation = Capsule->GetComponentLocation(); + const FVector CapsuleTop = CapsuleLocation + FVector::UpVector * (HalfHeight - Radius); + const FVector CapsuleBottom = CapsuleLocation - FVector::UpVector * (HalfHeight - Radius); + const FVector CapsuleRight = CapsuleLocation - EvalParams.CameraRight * Radius; + + FVector2D CapsuleTop2D; + FVector2D CapsuleBot2D; + FVector2D CapsuleRight2D; + + if (FSceneView::ProjectWorldToScreen(CapsuleTop, EvalParams.ViewRect, EvalParams.ViewProjectionMatrix, CapsuleTop2D) == false) + { + continue; + } + + if (FSceneView::ProjectWorldToScreen(CapsuleBottom, EvalParams.ViewRect, EvalParams.ViewProjectionMatrix, CapsuleBot2D) == false) + { + continue; + } + + if (FSceneView::ProjectWorldToScreen(CapsuleRight, EvalParams.ViewRect, EvalParams.ViewProjectionMatrix, CapsuleRight2D) == false) + { + continue; + } + + //if (Type == ECogSampleTargetAcquisitionType::Melee && CandidateCharacter != nullptr && !UCogFunctionLibrary_Targeting::IsTargetCapsuleReachableByMelee(EvalParams.Source, CandidateCharacter, Capsule)) + //{ + // continue; + //} + + const FVector2D CapsuleCenter2D = CapsuleBot2D + 0.5f * (CapsuleTop2D - CapsuleBot2D); + const float CapsuleRadius2D = FVector2D::Distance(CapsuleCenter2D, CapsuleRight2D); + + FVector2D Projection; + float Time; + float ScreenCenterToCapsuleDistance; + UCogSampleFunctionLibrary_Gameplay::FindCapsulePointDistance(CapsuleBot2D, CapsuleTop2D, CapsuleRadius2D, EvalParams.CrosshairPosition, Projection, Time, ScreenCenterToCapsuleDistance); + + if (ScreenCenterToCapsuleDistance < CandidateClosestScreenDistance) + { + CandidateScreenLocation = CapsuleCenter2D; + CandidateClosestScreenDistance = ScreenCenterToCapsuleDistance; + CandidateClosestScreenLocation = Projection; + bFoundValidCandidate = true; + } + +#if USE_COG + const FColor CapsuleColor = (ScreenCenterToCapsuleDistance > 0.0f) ? FColor(255, 255, 255, 100) : FColor(0, 255, 0, 200); + FCogDebugDraw::Segment2D(LogCogTargetAcquisition, CandidateActor, CapsuleBot2D + FVector2D(CapsuleRadius2D, 0), CapsuleTop2D + FVector2D(CapsuleRadius2D, 0), CapsuleColor, EvalParams.IsDebugPersistent); + FCogDebugDraw::Segment2D(LogCogTargetAcquisition, CandidateActor, CapsuleBot2D - FVector2D(CapsuleRadius2D, 0), CapsuleTop2D - FVector2D(CapsuleRadius2D, 0), CapsuleColor, EvalParams.IsDebugPersistent); + FCogDebugDraw::Circle2D(LogCogTargetAcquisition, CandidateActor, CapsuleTop2D, CapsuleRadius2D, CapsuleColor, EvalParams.IsDebugPersistent); + FCogDebugDraw::Circle2D(LogCogTargetAcquisition, CandidateActor, CapsuleBot2D, CapsuleRadius2D, CapsuleColor, EvalParams.IsDebugPersistent); +#endif //USE_COG + } + } + else + { + if (FSceneView::ProjectWorldToScreen(CandidateTargetLocation, EvalParams.ViewRect, EvalParams.ViewProjectionMatrix, CandidateScreenLocation)) + { + CandidateClosestScreenDistance = CandidateScreenLocation.Length(); + CandidateClosestScreenLocation = CandidateScreenLocation; + bFoundValidCandidate = true; + } + } + +#if USE_COG + if (bFoundValidCandidate) + { + FCogDebugDraw::Circle2D(LogCogTargetAcquisition, CandidateActor, CandidateClosestScreenLocation, 2.0f, FColor(0, 255, 0, 255), EvalParams.IsDebugPersistent); + } +#endif //USE_COG + + return bFoundValidCandidate; +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool UCogSampleTargetAcquisition::CheckCandidateWithinScreenDistance( + const APlayerController* Controller, + const FIntRect& ViewRect, + const FVector& CandidateLocation, + const FVector2D& CandidateScreenLocation, + const float CandidateScreenDistance, + const bool bIsDebugPersistent) const +{ + TRACE_CPUPROFILER_EVENT_SCOPE(UCogSampleTargetAcquisition::CheckCandidateWithinScreenDistance); + + const FVector2D ScreenSize(ViewRect.Width(), ViewRect.Height()); + const FVector2D CandidateViewportLocation = UCogSampleFunctionLibrary_Gameplay::ScreenToViewport(CandidateScreenLocation, ScreenSize); + const float CandidateViewportDistance = UCogSampleFunctionLibrary_Gameplay::ScreenToViewport(CandidateScreenDistance, ScreenSize); + + //-------------------------------------------------------------------------------------------------------------- + // Filter by screen distance - within rectangle + //-------------------------------------------------------------------------------------------------------------- + if (ScreenLimitType == ECogSampleTargetAcquisitionScreenLimitType::Rectangle) + { + if (FMath::Abs(CandidateViewportLocation.X) > ScreenMaxX) + { + COG(FCogDebugDraw::String(LogCogTargetAcquisition, Controller, FString::Printf(TEXT("MaxX: %0.2f"), CandidateViewportLocation.X), CandidateLocation, FColor::Red, bIsDebugPersistent)); + return false; + } + + if (FMath::Abs(CandidateViewportLocation.Y) > ScreenMaxY) + { + COG(FCogDebugDraw::String(LogCogTargetAcquisition, Controller, FString::Printf(TEXT("MaxY: %0.2f"), CandidateViewportLocation.Y), CandidateLocation, FColor::Red, bIsDebugPersistent)); + return false; + } + } + //-------------------------------------------------------------------------------------------------------------- + // Filter by screen distance - within circle + //-------------------------------------------------------------------------------------------------------------- + else if (ScreenLimitType == ECogSampleTargetAcquisitionScreenLimitType::Circle) + { + if (CandidateViewportDistance > ScreenMaxX) + { + COG(FCogDebugDraw::String(LogCogTargetAcquisition, Controller, FString::Printf(TEXT("Max: %0.2f"), CandidateViewportDistance), CandidateLocation, FColor::Red, bIsDebugPersistent)); + return false; + } + } + + return true; +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool UCogSampleTargetAcquisition::CheckIfTargetValid( + const APlayerController* Controller, + const AActor* Source, + const AActor* Target) const +{ + TRACE_CPUPROFILER_EVENT_SCOPE(UCogSampleTargetAcquisition::CheckIfTargetValid); + + if (UCogSampleFunctionLibrary_Team::MatchAllegiance(Source, Target, Allegiance) == false) + { + return false; + } + + return true; +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool UCogSampleTargetAcquisition::HasLineOfSightToTarget( + const AActor* Source, + const AActor* Target, + const FCollisionObjectQueryParams& BlockersParams) const +{ + TRACE_CPUPROFILER_EVENT_SCOPE(UCogSampleTargetAcquisition::HasLineOfSightToTarget); + + const FVector Origin = Source->GetActorLocation(); + + static const FName BlockerTraceTag(TEXT("FindLockTarget_Blocker")); + FCollisionQueryParams TargetQueryParams(BlockerTraceTag, SCENE_QUERY_STAT_ONLY(CogSampleTargetAcquisition), false); + TargetQueryParams.AddIgnoredActor(Target); + + return true; + //return FCogHitDetectionHelper::HasLineOfSight(Source->GetWorld(), Origin, Target.GetTargetLocation(), BlockersParams, TargetQueryParams, LogCogTargetAcquisition); +} + +//-------------------------------------------------------------------------------------------------------------------------- +bool UCogSampleTargetAcquisition::HasLineOfSightToTargetBrokenForTooLong( + const AActor* Source, + const AActor* Target, + const float DeltaTime, + float& Timer) const +{ + TRACE_CPUPROFILER_EVENT_SCOPE(UCogSampleTargetAcquisition::HasLineOfSightToTargetBrokenForTooLong); + + const FCollisionObjectQueryParams BlockersParams = UCogSampleFunctionLibrary_Gameplay::ConfigureCollisionObjectParams(BlockerTypes); + bool HasLineOfSight = HasLineOfSightToTarget(Source, Target, BlockersParams); + if (HasLineOfSight) + { + Timer = 0.0f; + } + else + { + Timer += DeltaTime; + + if (Timer > BreakLineOfSightDelay) + { + Timer = 0.0f; + return true; + } + } + + return false; +} + +//-------------------------------------------------------------------------------------------------------------------------- +FVector UCogSampleTargetAcquisition::GetReferentialLocation(const ACogSampleCharacter* Character, ECogSampleTargetAcquisitionLocationReferential Referential) +{ + FVector Location; + + switch (Referential) + { + case ECogSampleTargetAcquisitionLocationReferential::Character: + { + Location = UCogSampleFunctionLibrary_Gameplay::GetActorBottomLocation(Character); + break; + } + + + case ECogSampleTargetAcquisitionLocationReferential::Camera: + { + Location = Character->GetFollowCamera()->GetComponentLocation(); + break; + } + } + + return Location; +} + +//-------------------------------------------------------------------------------------------------------------------------- +FRotator UCogSampleTargetAcquisition::GetReferentialRotation(const ACogSampleCharacter* Character, ECogSampleTargetAcquisitionRotationReferential Referential) +{ + FRotator Rotation; + + switch (Referential) + { + case ECogSampleTargetAcquisitionRotationReferential::Character: + { + Rotation = Character->GetActorRotation(); + break; + } + + case ECogSampleTargetAcquisitionRotationReferential::MoveInput: + { + //const FVector WorldInput = Character->TransformInputInWorldSpace(Character->GetDesiredMoveInput()); + //if (WorldInput.IsNearlyZero()) + //{ + // Rotation = Character->GetActorRotation(); + //} + //else + //{ + // Rotation = WorldInput.GetSafeNormal().Rotation(); + //} + //break; + } + + case ECogSampleTargetAcquisitionRotationReferential::Camera: + { + Rotation = Character->GetFollowCamera()->GetComponentRotation(); + break; + } + + case ECogSampleTargetAcquisitionRotationReferential::CameraFlatten: + { + const FVector CameraForwardFlat = Character->GetFollowCamera()->GetComponentQuat().GetForwardVector().GetSafeNormal2D(); + Rotation = CameraForwardFlat.Rotation(); + break; + } + } + + return Rotation; +} diff --git a/Source/CogSample/CogSampleTargetAcquisition.h b/Source/CogSample/CogSampleTargetAcquisition.h new file mode 100644 index 0000000..20af702 --- /dev/null +++ b/Source/CogSample/CogSampleTargetAcquisition.h @@ -0,0 +1,296 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CogSampleFunctionLibrary_Team.h" +#include "Engine/DataAsset.h" +#include "Engine/EngineTypes.h" +#include "WorldCollision.h" +#include "CogSampleTargetAcquisition.generated.h" + +class ACogSampleCharacter; +class APlayerController; +class UCurveFloat; +struct FCogSampleTargetCandidateEvaluationResult; +struct FCogSampleTargetCandidateEvaluationParameters; + +//-------------------------------------------------------------------------------------------------------------------------- +UENUM(BlueprintType) +enum class ECogSampleTargetAcquisitionType : uint8 +{ + Melee, + Range +}; + +//-------------------------------------------------------------------------------------------------------------------------- +UENUM(BlueprintType) +enum class ECogSampleTargetAcquisitionScreenLimitType : uint8 +{ + Rectangle, + Circle, +}; + + +//-------------------------------------------------------------------------------------------------------------------------- +UENUM(BlueprintType) +enum class ECogSampleTargetAcquisitionLocationReferential : uint8 +{ + Character, + Camera, +}; + +//-------------------------------------------------------------------------------------------------------------------------- +UENUM(BlueprintType) +enum class ECogSampleTargetAcquisitionRotationReferential : uint8 +{ + Camera, + CameraFlatten, + Character, + MoveInput, +}; + +//-------------------------------------------------------------------------------------------------------------------------- +UENUM(BlueprintType) +enum class ECogSampleTargetAcquisitionCrosshairReferential : uint8 +{ + Centered, + Offseted, +}; + +//-------------------------------------------------------------------------------------------------------------------------- +struct FCogSampleTargetAcquisitionResult +{ +public: + AActor* Target = nullptr; + + float Score = FLT_MAX; +}; + +//-------------------------------------------------------------------------------------------------------------------------- +UCLASS() +class UCogSampleTargetAcquisition : public UDataAsset +{ + GENERATED_BODY() + +public: + + //-------------------------------------------------------------------------------------------------------------- + // General + //-------------------------------------------------------------------------------------------------------------- + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General") + TArray> ObjectTypes; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General") + TArray> BlockerTypes; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General") + float BreakLineOfSightDelay = 1.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General", meta = (Bitmask, BitmaskEnum = "/Script/CogSample.ECogSampleAllegianceFilter")) + int32 Allegiance = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General") + bool AcceptDead = false; + + //-------------------------------------------------------------------------------------------------------------- + // Detection + //-------------------------------------------------------------------------------------------------------------- + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Detection") + ECogSampleTargetAcquisitionLocationReferential DetectionLocation = ECogSampleTargetAcquisitionLocationReferential::Camera; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Detection") + ECogSampleTargetAcquisitionRotationReferential DetectionRotation = ECogSampleTargetAcquisitionRotationReferential::Camera; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Detection") + float DetectionLength = 1000.0f; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Detection") + float DetectionRadius = 600.0f; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Detection") + bool UseAsyncDetection = true; + + //-------------------------------------------------------------------------------------------------------------- + // Screen Limit + //-------------------------------------------------------------------------------------------------------------- + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Limit|Screen", meta = (ToolTip = "Limit based on the screen distance. The screen distance is computed between the crosshair and the candidate screen position")) + bool bUseScreenLimit = true; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Limit|Screen", meta = (EditCondition = "bUseScreenLimit", EditConditionHides)) + bool bPrioritizeInsideHitZones = true; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Limit|Screen", meta = (EditCondition = "bUseScreenLimit", EditConditionHides)) + bool ScreenTestUseAspectRatio = true; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Limit|Screen", meta = (EditCondition = "bUseScreenLimit", EditConditionHides)) + ECogSampleTargetAcquisitionCrosshairReferential CrosshairReferential = ECogSampleTargetAcquisitionCrosshairReferential::Centered; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Limit|Screen", meta = (EditCondition = "bUseScreenLimit", EditConditionHides)) + ECogSampleTargetAcquisitionScreenLimitType ScreenLimitType = ECogSampleTargetAcquisitionScreenLimitType::Rectangle; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Limit|Screen", meta = (EditCondition = "bUseScreenLimit", EditConditionHides)) + float ScreenMaxX = 1.0f; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Limit|Screen", meta = (EditCondition = "bUseScreenLimit", EditConditionHides)) + float ScreenMaxY = 1.0f; + + //-------------------------------------------------------------------------------------------------------------- + // Yaw Limit + //-------------------------------------------------------------------------------------------------------------- + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Limit|Yaw", meta = (ToolTip = "Limit based on the yaw angle. The yaw angle is computed between the camera forward vector and the direction between the player character and the candidate")) + bool bUseYawLimit = true; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Limit|Yaw", meta = (EditCondition = "bUseYawLimit", EditConditionHides)) + float YawMax = 90.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Limit|Yaw") + ECogSampleTargetAcquisitionRotationReferential YawReferential = ECogSampleTargetAcquisitionRotationReferential::Camera; + + //-------------------------------------------------------------------------------------------------------------- + // World Distance + //-------------------------------------------------------------------------------------------------------------- + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Scoring|World Distance", meta = (ToolTip = "The world distance is the distance between the character position and the candidate position")) + bool bUseWorldDistanceScore = false; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Scoring|World Distance", meta = (EditCondition = "bUseWorldDistanceScore", EditConditionHides)) + float WorldDistanceMax = 1000.0f; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Scoring|World Distance", meta = (EditCondition = "bUseWorldDistanceScore", EditConditionHides)) + UCurveFloat* WorldDistanceScoreCurve = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Scoring|World Distance", meta = (EditCondition = "bUseWorldDistanceScore", EditConditionHides)) + float WorldDistanceScoreMultiplier = 1.0f; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Scoring|World Distance", meta = (EditCondition = "bUseWorldDistanceScore", EditConditionHides)) + bool WorldDistanceIgnoreZ = false; + + //-------------------------------------------------------------------------------------------------------------- + // Screen Distance Score + //-------------------------------------------------------------------------------------------------------------- + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Scoring|Screen Distance", meta = (ToolTip = "The screen distance is the distance between the crosshair and the candidate screen position")) + bool bUseScreenDistanceScore = false; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Scoring|Screen Distance", meta = (EditCondition = "bUseScreenDistanceScore", EditConditionHides)) + float ScreenDistanceScoreMultiplier = 1.0f; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Scoring|Screen Distance", meta = (EditCondition = "bUseScreenDistanceScore", EditConditionHides)) + UCurveFloat* ScreenDistanceScoreCurve = nullptr; + + //-------------------------------------------------------------------------------------------------------------- + // Yaw Scoring + //-------------------------------------------------------------------------------------------------------------- + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Scoring|Yaw", meta = (ToolTip = "The yaw angle is computed between the camera forward vector and the direction between the player character and the candidate")) + bool bUseYawScore = false; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Scoring|Yaw", meta = (EditCondition = "bUseYawScore", EditConditionHides)) + float YawScoreMultiplier = 1.0f; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Scoring|Yaw", meta = (EditCondition = "bUseYawScore", EditConditionHides)) + UCurveFloat* YawScoreCurve = nullptr; + + //-------------------------------------------------------------------------------------------------------------- + // Search Direction + //-------------------------------------------------------------------------------------------------------------- + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Scoring|Search Direction", meta = (ToolTip = "The search direction is used when the player has already a locked target and request a target switch with a direction performed by the stick or the mouse")) + bool bUseSearchDirectionScore = false; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Scoring|Search Direction", meta = (EditCondition = "bUseSearchDirectionScore", EditConditionHides)) + float SearchDirectionMaxAngle = 60.0f; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Scoring|Search Direction", meta = (EditCondition = "bUseSearchDirectionScore", EditConditionHides)) + UCurveFloat* ScreenSearchDirectionAngleScoreCurve = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Scoring|Search Direction", meta = (EditCondition = "bUseSearchDirectionScore", EditConditionHides)) + float ScreenSearchDirectionAngleScoreMultiplier = 1.0f; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Scoring|Search Direction", meta = (EditCondition = "bUseSearchDirectionScore", EditConditionHides)) + UCurveFloat* ScreenSearchDirectionDistanceScoreCurve = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Scoring|Search Direction", meta = (EditCondition = "bUseSearchDirectionScore", EditConditionHides)) + float SearchDirectionDistanceScoreMultiplier = 1.0f; + + //-------------------------------------------------------------------------------------------------------------- + + virtual void FindBestTargets( + const APlayerController* Controller, + const int32 TargetCount, + const TArray& TargetsToIgnore, + const AActor* CurrentLockedTarget, + const bool bForceSynchronousDetection, + const FVector2D ScreenSearchDirection, + const bool bIsDebugPersistent, + TArray& Results) const; + + virtual bool HasLineOfSightToTargetBrokenForTooLong( + const AActor* Source, + const AActor* Target, + const float DeltaTime, + float& Timer) const; + + virtual bool CheckIfTargetValid( + const APlayerController* Controller, + const AActor* Source, + const AActor* Target) const; + + //-------------------------------------------------------------------------------------------------------------- + // Utility + //-------------------------------------------------------------------------------------------------------------- + + void FindBestTargetInCandidates( + const APlayerController* Controller, + const TArray& TargetsToIgnore, + const TArray& Candidates, + const FVector2D ScreenSearchDirection, + const FVector2D SearchDirectionViewportOrigin, + const bool bIsDebugPersistent, + FCogSampleTargetAcquisitionResult& Result) const; + + void FindBestTarget( + const APlayerController* Controller, + const TArray& TargetsToIgnore, + const AActor* CurrentLockedTarget, + const bool bForceSynchronousDetection, + const FVector2D ScreenSearchDirection, + const bool bIsDebugPersistent, + FCogSampleTargetAcquisitionResult& Result) const; + + bool HasLineOfSightToTarget( + const AActor* Source, + const AActor* Target, + const FCollisionObjectQueryParams& BlockersParams) const; + + bool EvaluateCandidate( + AActor* CandidateTarget, + const FCogSampleTargetCandidateEvaluationParameters& EvaluationParameters, + FCogSampleTargetCandidateEvaluationResult& EvaluationResult) const; + + bool CheckCandidateWithinScreenDistance( + const APlayerController* Controller, + const FIntRect& viewRect, + const FVector& candidateLocation, + const FVector2D& candidateScreenLocation, + const float candidateScreenDistance, + const bool bIsDebugPersistent) const; + + bool ComputeCandidateScreenLocation( + const AActor* CandidateTarget, + const FCogSampleTargetCandidateEvaluationParameters& EvalParams, + const FVector& CandidateTargetLocation, + FVector2D& CandidateScreenLocation, + FVector2D& CandidateClosestScreenLocation, + float& CandidateClosestScreenDistance) const; + + bool GetViewInfo( + const APlayerController* Controller, + FMatrix& viewProjectionMatrix, + FIntRect& viewRect) const; + + static FVector GetReferentialLocation(const ACogSampleCharacter* Character, ECogSampleTargetAcquisitionLocationReferential Referential); + + static FRotator GetReferentialRotation(const ACogSampleCharacter* Character, ECogSampleTargetAcquisitionRotationReferential Referential); +}; \ No newline at end of file diff --git a/Source/CogSample/CogSampleTargetableInterface.h b/Source/CogSample/CogSampleTargetableInterface.h new file mode 100644 index 0000000..c361a21 --- /dev/null +++ b/Source/CogSample/CogSampleTargetableInterface.h @@ -0,0 +1,24 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CogSampleTargetableInterface.generated.h" + +class UCapsuleComponent; + +//-------------------------------------------------------------------------------------------------------------------------- +UINTERFACE(MinimalAPI, Blueprintable) +class UCogSampleTargetableInterface : public UInterface +{ + GENERATED_BODY() +}; + +class ICogSampleTargetableInterface +{ + GENERATED_BODY() + +public: + + virtual FVector GetTargetLocation() const { return FVector::ZeroVector; } + + virtual void GetTargetCapsules(TArray& Capsules) const { } +}; diff --git a/Source/CogSample/CogSampleTeamInterface.h b/Source/CogSample/CogSampleTeamInterface.h new file mode 100644 index 0000000..748a622 --- /dev/null +++ b/Source/CogSample/CogSampleTeamInterface.h @@ -0,0 +1,20 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CogSampleTeamInterface.generated.h" + +//-------------------------------------------------------------------------------------------------------------------------- +UINTERFACE(MinimalAPI, Blueprintable) +class UCogSampleTeamInterface : public UInterface +{ + GENERATED_BODY() +}; + +class ICogSampleTeamInterface +{ + GENERATED_BODY() + +public: + + virtual int32 GetTeam() const { return 0; } +};