From 840457f4712e81e52220c9d6db2eb5e411d54c9e Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 12 Mar 2023 01:55:34 +0500 Subject: [PATCH 001/127] Fix synctype_t enumeration redefinition. --- engine/sprite.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine/sprite.h b/engine/sprite.h index ee0c317b..9b112d9e 100644 --- a/engine/sprite.h +++ b/engine/sprite.h @@ -28,11 +28,14 @@ SPRITE MODELS #define IDSPRITEHEADER (('P'<<24)+('S'<<16)+('D'<<8)+'I') // little-endian "IDSP" #define SPRITE_VERSION 2 // Half-Life sprites +#if !defined(SYNCTYPE_T) +#define SYNCTYPE_T typedef enum { ST_SYNC = 0, ST_RAND } synctype_t; +#endif typedef enum { From b3223d9324ca1ef709a6188985d5ad5326fdc32e Mon Sep 17 00:00:00 2001 From: Elinsrc <99191833+Elinsrc@users.noreply.github.com> Date: Sun, 9 Apr 2023 18:31:30 +0500 Subject: [PATCH 002/127] fixplayerscore (#364) --- dlls/multiplay_gamerules.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dlls/multiplay_gamerules.cpp b/dlls/multiplay_gamerules.cpp index da199271..e8e6a3b5 100644 --- a/dlls/multiplay_gamerules.cpp +++ b/dlls/multiplay_gamerules.cpp @@ -458,12 +458,13 @@ void CHalfLifeMultiplay::InitHUD( CBasePlayer *pl ) // sending just one score makes the hud scoreboard active; otherwise // it is just disabled for single play - MESSAGE_BEGIN( MSG_ONE, gmsgScoreInfo, NULL, pl->edict() ); + //fix a bug in the information about the player's score when he left the server, so that his score would not be transferred to another player(seems to work) + MESSAGE_BEGIN( MSG_ALL, gmsgScoreInfo ); WRITE_BYTE( ENTINDEX(pl->edict()) ); + WRITE_SHORT( (int)pl->pev->frags ); + WRITE_SHORT( pl->m_iDeaths ); WRITE_SHORT( 0 ); - WRITE_SHORT( 0 ); - WRITE_SHORT( 0 ); - WRITE_SHORT( 0 ); + WRITE_SHORT( GetTeamIndex( pl->m_szTeamName ) + 1 ); MESSAGE_END(); SendMOTDToClient( pl->edict() ); From a4a2b34386b2f0a2e1fbd07dc39159282a746249 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 10 Apr 2023 01:53:41 +0500 Subject: [PATCH 003/127] Avoid lags using movevar. --- dlls/player.cpp | 1 + pm_shared/pm_shared.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dlls/player.cpp b/dlls/player.cpp index f011b550..63e11283 100644 --- a/dlls/player.cpp +++ b/dlls/player.cpp @@ -2815,6 +2815,7 @@ void CBasePlayer::Spawn( void ) g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "0" ); g_engfuncs.pfnSetPhysicsKeyValue( edict(), "hl", "1" ); + g_engfuncs.pfnSetPhysicsKeyValue( edict(), "fr", "1" ); pev->fov = m_iFOV = 0;// init field of view. m_iClientFOV = -1; // make sure fov reset is sent diff --git a/pm_shared/pm_shared.c b/pm_shared/pm_shared.c index d5ad05d5..f84cc9ae 100644 --- a/pm_shared/pm_shared.c +++ b/pm_shared/pm_shared.c @@ -3319,7 +3319,8 @@ void PM_Move( struct playermove_s *ppmove, int server ) } // Reset friction after each movement to FrictionModifier Triggers work still. - if( pmove->movetype == MOVETYPE_WALK ) + // Use movevar to avoid lags with different clients and servers. + if( !( pmove->multiplayer && atoi( pmove->PM_Info_ValueForKey( pmove->physinfo, "fr" )) == 0 ) && pmove->movetype == MOVETYPE_WALK ) { pmove->friction = 1.0f; } From 459ac7fa0232a7fb895503cef90785b3587dbd97 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 16 Apr 2023 04:01:26 +0300 Subject: [PATCH 004/127] scripts: waifulib: compiler_optimizations: synchronize with engine --- scripts/waifulib/compiler_optimizations.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/scripts/waifulib/compiler_optimizations.py b/scripts/waifulib/compiler_optimizations.py index 688c6f55..9cc81b7c 100644 --- a/scripts/waifulib/compiler_optimizations.py +++ b/scripts/waifulib/compiler_optimizations.py @@ -118,7 +118,7 @@ POLLY_CFLAGS = { def options(opt): grp = opt.add_option_group('Compiler optimization options') - grp.add_option('-T', '--build-type', action='store', dest='BUILD_TYPE', default=None, + grp.add_option('-T', '--build-type', action='store', dest='BUILD_TYPE', default='release', help = 'build type: debug, release or none(custom flags)') grp.add_option('--enable-lto', action = 'store_true', dest = 'LTO', default = False, @@ -129,12 +129,11 @@ def options(opt): def configure(conf): conf.start_msg('Build type') - if conf.options.BUILD_TYPE == None: - conf.end_msg('not set', color='RED') - conf.fatal('Set a build type, for example "-T release"') - elif not conf.options.BUILD_TYPE in VALID_BUILD_TYPES: + + if not conf.options.BUILD_TYPE in VALID_BUILD_TYPES: conf.end_msg(conf.options.BUILD_TYPE, color='RED') conf.fatal('Invalid build type. Valid are: %s' % ', '.join(VALID_BUILD_TYPES)) + conf.end_msg(conf.options.BUILD_TYPE) conf.msg('LTO build', 'yes' if conf.options.LTO else 'no') @@ -175,5 +174,7 @@ def get_optimization_flags(conf): elif conf.env.DEST_OS == 'psvita': # this optimization is broken in vitasdk cflags.append('-fno-optimize-sibling-calls') + # remove fvisibility to allow everything to be exported by default + cflags.remove('-fvisibility=hidden') return cflags, linkflags From dcdcfa9eeb28abdf33f18b1856c8a7bac24553b0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 16 Apr 2023 04:51:44 +0300 Subject: [PATCH 005/127] Add common file mod_options.txt that's used to set current mod options between all supported build systems --- mod_options.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 mod_options.txt diff --git a/mod_options.txt b/mod_options.txt new file mode 100644 index 00000000..d68090c4 --- /dev/null +++ b/mod_options.txt @@ -0,0 +1,16 @@ +BARNACLE_FIX_VISIBILITY=OFF # Barnacle tongue length fix +CLIENT_WEAPONS=ON # Client local weapons prediction +CROWBAR_IDLE_ANIM=OFF # Crowbar idle animation +CROWBAR_DELAY_FIX=OFF # Crowbar attack delay fix +CROWBAR_FIX_RAPID_CROWBAR=OFF # Rapid crowbar fix +GAUSS_OVERCHARGE_FIX=OFF # Gauss overcharge fix +TRIPMINE_BEAM_DUPLICATION_FIX=OFF # Fix of tripmine beam duplication on level transition +HANDGRENADE_DEPLOY_FIX=OFF # Handgrenade deploy animation fix after finishing a throw +WEAPONS_ANIMATION_TIMES_FIX=OFF # Animation times fix for some weapons +OEM_BUILD=OFF # OEM Build +HLDEMO_BUILD=OFF # Demo Build + +GAMEDIR=valve # Gamedir path +SERVER_INSTALL_DIR=dlls # Where put server dll +CLIENT_INSTALL_DIR=cl_dlls # Where put client dll +SERVER_LIBRARY_NAME=hl # Library name for PC platforms From 822f8906acd05f1d443777177327c3b1511542a9 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 16 Apr 2023 04:52:52 +0300 Subject: [PATCH 006/127] CMakeLists: add support for mod_options.txt * This however, removes support for CMake earlier than 3.9 which isn't a big problem to me, as we also support Waf which supports Python 2.7, so there is always an option for legacy systems --- CMakeLists.txt | 109 ++++++++++++------------------------------------- 1 file changed, 25 insertions(+), 84 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index add3771f..e2ea26c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,8 @@ # SOFTWARE. # -cmake_minimum_required(VERSION 2.8.12) +# 3.9 added captures in if(MATCHES) +cmake_minimum_required(VERSION 3.9) if(NOT ${CMAKE_VERSION} VERSION_LESS "3.15.0") cmake_policy(SET CMP0091 NEW) @@ -58,28 +59,33 @@ else() option(GOLDSOURCE_SUPPORT "Build goldsource compatible client library" OFF) endif() -option(BARNACLE_FIX_VISIBILITY "Enable barnacle tongue length fix" OFF) -option(CLIENT_WEAPONS "Enable client local weapons prediction" ON) -option(CROWBAR_IDLE_ANIM "Enable crowbar idle animation" OFF) -option(CROWBAR_DELAY_FIX "Enable crowbar attack delay fix" OFF) -option(CROWBAR_FIX_RAPID_CROWBAR "Enable rapid crowbar fix" OFF) -option(GAUSS_OVERCHARGE_FIX "Enable gauss overcharge fix" OFF) -option(TRIPMINE_BEAM_DUPLICATION_FIX "Enable fix of tripmine beam duplication on level transition" OFF) -option(HANDGRENADE_DEPLOY_FIX "Enable handgrenade deploy animation fix after finishing a throw" OFF) -option(WEAPONS_ANIMATION_TIMES_FIX "Enable animation times fix for some weapons" OFF) -option(OEM_BUILD "Enable OEM Build" OFF) -option(HLDEMO_BUILD "Enable Demo Build" OFF) - -set(GAMEDIR "valve" CACHE STRING "Gamedir path") -set(SERVER_INSTALL_DIR "dlls" CACHE STRING "Where put server dll") -set(CLIENT_INSTALL_DIR "cl_dlls" CACHE STRING "Where put client dll") -set(SERVER_LIBRARY_NAME "hl" CACHE STRING "Library name for PC platforms") -message(STATUS "Half-Life") - #----------------- # MAIN BUILD CODE \ ###################\ +file(STRINGS "mod_options.txt" MOD_OPTIONS_STRINGS REGEX "^([A-Za-z0-9_]+)=([A-Za-z0-9_]+)\ \#\ (.*)$") +foreach(LINE IN LISTS MOD_OPTIONS_STRINGS) + # file() itself doesn't populate CMAKE_MATCH_, so + # reparse the string + + if(${LINE} MATCHES "^([A-Za-z0-9_]+)=([A-Za-z0-9_]+)\ \#\ (.*)$") + # detect boolean options + if(${CMAKE_MATCH_2} STREQUAL "ON" OR ${CMAKE_MATCH_2} STREQUAL "OFF") + option(${CMAKE_MATCH_1} "${CMAKE_MATCH_3}" ${CMAKE_MATCH_2}) + + # let's check it here as well + if(${CMAKE_MATCH_1}) + message(STATUS ${CMAKE_MATCH_3} " is enabled") + add_definitions(-D${CMAKE_MATCH_1}) + else() + message(STATUS ${CMAKE_MATCH_3} " is disabled") + endif() + else() + set(${CMAKE_MATCH_1} "${CMAKE_MATCH_2}" CACHE STRING "${CMAKE_MATCH_3}") + endif() + endif() +endforeach() + if(HLDEMO_BUILD AND OEM_BUILD) message(FATAL_ERROR "Don't mix Demo and OEM builds!") endif() @@ -109,76 +115,11 @@ else() message(STATUS "Building for 32 Bit") endif() -if(CLIENT_WEAPONS) - message(STATUS "Client weapons enabled.") - add_definitions(-DCLIENT_WEAPONS) -endif() - -if(BARNACLE_FIX_VISIBILITY) - message(STATUS "Barnacle tongue fix enabled") - add_definitions(-DBARNACLE_FIX_VISIBILITY) -endif() - -if(CROWBAR_IDLE_ANIM) - message(STATUS "Crowbar idle animation enabled") - add_definitions(-DCROWBAR_IDLE_ANIM) -endif() - -if(CROWBAR_DELAY_FIX) - message(STATUS "Crowbar attack delay fix enabled") - add_definitions(-DCROWBAR_DELAY_FIX) -endif() - -if(CROWBAR_FIX_RAPID_CROWBAR) - message(STATUS "Rapid crowbar fix enabled") - add_definitions(-DCROWBAR_FIX_RAPID_CROWBAR) -endif() - -if(GAUSS_OVERCHARGE_FIX) - message(STATUS "Gauss overcharge fix enabled") - add_definitions(-DGAUSS_OVERCHARGE_FIX) -endif() - -if(TRIPMINE_BEAM_DUPLICATION_FIX) - message(STATUS "Tripmine beam duplication fix enabled") - add_definitions(-DTRIPMINE_BEAM_DUPLICATION_FIX) -endif() - -if(HANDGRENADE_DEPLOY_FIX) - message(STATUS "Handgrenade deploy animation fix enabled") - add_definitions(-DHANDGRENADE_DEPLOY_FIX) -endif() - -if(WEAPONS_ANIMATION_TIMES_FIX) - message(STATUS "Weapons animation times fix enabled") - add_definitions(-DWEAPONS_ANIMATION_TIMES_FIX) -endif() - -if(OEM_BUILD) - message(STATUS "OEM build enabled") - add_definitions(-DOEM_BUILD) -endif() - -if(HLDEMO_BUILD) - message(STATUS "Demo build enabled") - add_definitions(-DHLDEMO_BUILD) -endif() - if (MINGW) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++ -static-libgcc") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--add-stdcall-alias") endif() -# add_compile_options for older cmake versions -if(${CMAKE_VERSION} VERSION_LESS "3.0.2") - macro(add_compile_options) - set(list_var "${ARGV}") - foreach(arg IN LISTS list_var) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${arg}") - endforeach() - endmacro() -endif() - if(NOT MSVC) #add_compile_options(-Wempty-body) # GCC/Clang flag add_compile_options(-Wreturn-type) # GCC/Clang flag From b15a1388adab4502731bcdf3918da28cf455c4a9 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 17 Apr 2023 03:59:56 +0300 Subject: [PATCH 007/127] waf: upgrade to latest waifu master --- waf | 14 +++++++------- waf.bat | 12 ++++-------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/waf b/waf index 919420c6..f93cde51 100755 --- a/waf +++ b/waf @@ -32,13 +32,13 @@ POSSIBILITY OF SUCH DAMAGE. import os, sys, inspect -VERSION="2.0.22" -REVISION="9848c8ac89183c48b69d47e767462720" -GIT="3f8bb163290eb8fbfc3b26d61dd04aa5a6a29d4a" +VERSION="2.0.25" +REVISION="9dbe1df4d0e92facbc1de0486ec78fed" +GIT="ee63711f9792665ffd31c3397ab0e770300b938d" INSTALL='' -C1='#=' -C2='#4' -C3='#/' +C1='#n' +C2='#Y' +C3='#X' cwd = os.getcwd() join = os.path.join @@ -171,5 +171,5 @@ if __name__ == '__main__': Scripting.waf_entry_point(cwd, VERSION, wafdir) #==> -#BZh91AY&SYqz?&_$e>V#/cgs`P#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/#/'zھ }Ug7Ч>וsF#/Vnګ}m;ni6-9m]Ц^W}{\#m\Ü֯6mNU`}w[馑黾>{y=oz8=(vkoJ=!v7P)EF#/҂Q:v}}f{ۨ u=z#/#/#/9lX]M1TE)Puv*⏦C@i R ʊ6٭ SU#/UQ;#=#/xg}b^.f#vq^M#/vyw*6{w#=Ʌ#4amKj BP)=6b܈#/=`$.ͼ+9 t_J}{#/#/#/#/#/#/;>[[QEh{S@#/ӧ@WU2nqZ5Zl0#/#/*΄JJ#/#=:vhl#/>7wyws#}#܀:Pv[7ηIG}ܥ@:>zEy֗|}t{ǽt{Ͻ}u>I{ma^i}{v]zw{6 w{xvO@4*WTF;Fٶîi%wnkKthaS]0yi|ve.ӷƀ;v=n=xDϧֻS{nwۻ{{|w+{={޾.ٻe}zwt{eZ{xfo_]sfGGlg-osoYKӪȋw<ϳcs>{>8}{GӪzTGzSww;|}ٶfufv-yvglhK<{^47g%Kwy'sׯέ}}k 黷v(*ѹkX;Sk+>wG*]V7voxdPZ6"X1IAn]oi{]i]}RG ͉l=z9۴0w*;U9#/*ze_w[^tmsiͨW;%m]ATvt#/#/gXJ}I.r몚ԠP>y,j#/T=N}n$@(+}}{ϽV8#/+l Gnmf{md]#4κ(zvg/SonAݰJ6P;WwUclϭWL^Au*wyʹEg#49vITIJlon#4,꜁{U#/ fU ]aӵ#/uqծϏݛ#/#/#/ZȠvݎDn;{*![L5Lm#4%|co>&Y6Ivu{ƮƂwsݻnJ%h#/=#/k#=[#/!+mBs#/;亴KEWC$#/r#/#4]#/Ԩ#/#/#/==#4P"m}@p۳O䦛|^<4Ѐ@@#/14hLL&4)ISz2SOPP=GJhA@@#@h)ҧ*x§Oz#4='@P#/4#/#/#/#/#/#/$"&@50#/M3*z5FLOO64h=M#/#/#/#/#/ D#/4OMM$)Hf5G=' =#/#/ #/#/d#/#A4Ѡ4&'ѦL?ВzITɣ@#/#/4hTj"#/4LJx4LA4icQڀ#/#/#/#/#/#/#/?dU'#=`9#=G U7?犧DHS:J"J@@ #=| 4QÏ6/h{"zN8)[ɛ+8|ojJUND].nStLATRO}#/Sƪz *YC@K1#= F0M7UxO4‚7=uu2T;MM\/tPD1% J7` Rl'c( d+B#=t"" #=C@BiYJq#/@]*" _ܶ?x#44T1#/ P RTT"Op^$&T+ BI. 9CGQxDKSCHSE2pU0&),DpO7#=5 WU}((#/Q(DRB#/RJ($1D1DQD1TEAELE1TD$4UI$E1DUDQTASQ,IEQMLDSTS5QESMPLEIBST14P#=Q4E3,E@5H#=PPST$RJ̱#/)3!EI$5Q%Q$ES00T-D#44S!PAI##=İPL4TATE#4DIUTTS1RL-#4#QDUUU AQ TPREKD TUAT4,T$D55S0RP4,PTTAM)TCCS5 EM#4% $Գ5U#44L5*A4$!@RA!I2RTDID%3PĤMTAR0ASMTPA)BI,)KPTCI5D44ST ¥UBDLQ!"(B,D@34DL@UE4D JLTICPQE-,Q$E)QR2IAIDI@SPT4RS$@LQLQTEPRTAUPTUT3SU)Q1I1Q@EU@DC2ELDQUUMLERHS UITD1CC0!M%#4LQC11  C!DAL1PT14E-#4U4T#4ME#4UA(RQCE1)QLEA-DITI-QQL#4DCMPDTQTDQ%$DIDEQPSDQQ1D-DADMAEEM0@TMTD4 EDAC1Q,0CKEE1TQ!$DTDIbJ&("(%(H("!")IbJX("#=iJ%H H!)X X*@*X* &I* a""**Ii*"*`*& (i iR"Z(d&")Y$$#=&*I(h%bfjbJe&))&f"ibZ()hJb((i"$)#=""H%)"("XZ#=#=Y&(J*"*& )i"*Z H "Iei#="#= "(&B*Jb""*"e()"BH* jJ&(*"`bjJ&*P(HQI`)&"h" *"b"*d(#="Xai)B`i"(#=%)"(afSb$UPd #/jV&a bY"J""&#=Yj"H b*("bB") #=h)*#=je"&("HH*"&&#=")i*j)(j`"& **Z"F& 40J10TMAS񳉨Vab&Rf* I(("I)$f"h (f$!!""hb)&f( ()(`#=hhHi %)"h &( $B"(#/$ jdb( aB*JB$ a `f**f"#=*RJB!"RHIH%>r(ii#=b*#=") ""h (R*bb$640LԕS5ULERE@D1QT4IUQ4U3TRM%3L̥#4H#4(55LDC4 EQSADI254S251IEḎU2TULM%T0ԴPCE15HDUE0AUL)1LRQ$T05DDMBEJPKQHDHQA4BQKE5UD43TM4 I1LC T%!4DQJЅ#= 4-S4BPRS4T%1$Q!)M,#4AUBJ !T!#/@PPITMBRRT)C0BK%!LPICP,I%L$ %Q0%D LT45MKIL,RQ0TBPMU!$M#AED0#IDP!@4-RQ#CADBBRPД%+CU0 U-U LIL4JU)3UAKE%%Dą1Q1MT QT%)4HЄE-#=(JHPMQDQEDLCT%R@24B!24LQ#/CL%T#3M-1$Q#/D, EBERE4EPUHLD@RQCADSDE- Q- @U!31,TLJ DPPDĔSDE#4,E$0!SADJUTL1DQ BUTLSR M%M%C3U0CQ4U5TČTQA$4EAESIT5-IDD1 IDTL413#ACDA1I4 3UPUTIC5@A,ED LTUU4QKK0DM#4TM4JQA5#4)QHD5%!-2IK#4+CTEDEALBPPPTHUIA3DIIC0@L$SQ#AC% E EM!R#/RȔE)0DTQA1MPTPRAU!PEA+Q#HIĴTQ,1)10DSPE5C2!$S@Q@D1RAUED% R5%#44#LԕEKLDK0#445HD#4UERU4-TAAALE% C QK E!JIMQ IU T!BP5LUTU$IDDЄ3%D1#/R1$ITATҔQM+5E#=!T0-,QE1%U DTEUM%2L0-!E 4/6hbBh !#=bDiHV"ZX#=#=B$`d)h#=( i "i(Hh(V $"&f*(J@#=H$&Rh*HBj(!Jb!#=#=X a)J"iR )i@((ZB "%i$"h&X`JB"Qe*$( #=I(Z#=BHh h(RH#=Dd#=X$Bh(*(#="bj I*(#=i)b@b" Y*(#/*j $VhTjj((#=hh()i$!)Z#=P&"iV)F $X!Phi"`)*()h("bb BPJ#/O #=J +!bC>?tZUW+?WIms1gdM~M5 T~`SL P~4u (\ۨFz 6j#=~ĭ2R꭬m `r?nk#=mrhM8ԍr0?ھc zBm 0ff?U PbП !ɝS,#=I6]zT](/' Iy75wOX+C#4r<*Pth֦})2= n!V@?Q(~](z.T,Nu넾ߟn׍U֬0:rڻbg :w6b i`G=ȧe1rh8*4l&Ac2VFUŝ\jZ}P2oITнŘBR(T‰Z72%[G(g$ݧA8Ct,ͽny]P |t'Չ#=̴MO&|<Ȑ|߾;4p$嘕4γ";o) &*d >9ĈHjaK[vIlIO'+yD})$l*Xq$&CtKKEBǩ{*BQ-&0TGN#/@aMp!pXP:UP(gڷfk̰F{ǿ0 )2 l^ը^X$єY k\D#=9V"&ý69_]xZѾΛp!khi#=/aI<)AO}SCz0=PPL,̢M i)^=3臵:͚x9tiʿL bJ'&Z#=5r8k#40b>z2}X[xw^t#/S"v偺PZ4 =#4XfqRZi7fFHDr#=)t:lHCˎeGWK8@μ$W>ʱ e;vUiFk[]iGfLYd>JR #/z;Q!h^"4#4:h(J2+9ELE\}Lۜ}^ifi,TP2kSuy} xT;9%^ gѣfՒgdgQRn]mjJvp &WԆ>#4V|*ooy(뻽e?,SrK?eP#4h\sUeAs?v(|CgeVYl(@TuK:tŇ B"{%gk'->#=iB C8juAS #4ērQHNS^9#4\ #c#4F֚;L@#&Ŗ{n>>˗-grz뚅SLM&ҡǾluQ8xI%ww?Ǐpd3T%(2]R uJ^l>~K&YE S$G[Z#=Ei)$gϝG#/vp}t{vO^vb"BQN.c Sծ&+ )'>]pXrL6,d#=r҃ &!Z'蹖A7ZIE/n ۳Mq$ۣ}P+kY#4CSnFU#fED>(~6#?+a,R'Ftj멘;!6F$ARL )(Kb$#yK7m(f, HL2= JsQɅg%R޵M&D W{qtWX(57$N+("|ز"0"B\l4+12f.N|<GkK8>€iaS"QC%];ֿ~rh?#4 y&8qPkE#=ͮW`F9m8฼S@TG4VsR YpxlWPA/F@1J=KB(4`vgY Y`wژa[JVOպ d.n+N.ˇ4:kЏ8啀S6b]m_?6b#/DͣI(SQ1 Dr ;Zv(#CMg$fz@l#/x\4iDu:)P^Q4zգd>]I9k7_&0`+@ф2( #=Wuc͛;!2`xr/冺#Eba#4#=<d`NBd\u Ȫ#_uBjS3(#¥꣝T"oUӟ0qX$3@r`Dd?1#=I#cy.n="&ص|˯ku#=!󍻱㝬^SD$6_Բ#=HϥOc#4_q(}/9`}g%xa5!1m?֜z…I'QIzI8hOYxV>缼3O)HXwfJ&}ZXU6u#ְme5gaUGb'!ӥ׊StS;7ɐ(T-;/v/ AQT) o4N#ʷ[`x_3$E2bCpcsV+ytj DPz>ԩgж8?x_~9̜ȟ'b5317zF7aʬk?U'ZюG p9]+MܐJYYАT*|^ܱe^KcN#44,IP #=pJ(+H=9H$ڔa7P!4*k&dLr][ϞCc ` @B#42B?!7wl0],NmBkspsxfn3 ^\$H2}8J(ZN#96$:4IG29$޿tuoYW)j!t,LJr&֯AbKRJ3BhF#/N?}9P`q1)ʥ^{H4%!a,#rGzkb?LpB,bd²ᦨL>T7q0#=_#/q[YP@ `7g+nu.y^6@JUYq5Zx!XԫnwqSZJdE$2ތ<{ITinbм](tϖa4"QT8&p#=iTRc9D,J.x䡭ղA#4d Ѓ|+#4TYP=4}1դoMFZ׸#42!(+f =ˡAW0ݰ˭3]lGl{u ”|jT{㉫cTQ0ao>C~+:{Đx6VjfBAoN27 j4|׻+,{QʏJl3#=T\ʪR5 1d(MYQuquH1~#/Sh1Tfvw硳 lޖ>ul`JYs!&L]0捛nKU>ծV!CGBo>{Cu25!`/G) "(W-L#4KJl뿧!SM1X#J_8>}"ZT<>Ŝݖ~j5q@4\?y_wwA6i%>BϏSɐ^Td/Q7k"l9_N2/(LYKs|.g iKըlqu?R[Bÿ#=V^~np~#=Jp(0_loĝ9~:cconR& #/ 'uL#=j2 }JWZ)09O}\i>0'#pf9i_#=VfK<CCjR)HƒAJy<S$b$  Hb>i3PUvfĂ.\>C;#=U~QB +.u/^L݊B. d7Cvh!#=k+(Gwt-#=,#>MJPdb1heU"ف jk`dX])x EX4L~ldF:37u#=4_Gߟ/} k_>#4Z2;P6I{Noŷۄ#/^FDA]R*7=1C$/bp#=VЇ-5<'SB4'%;@@ۆ'NRC{OC-q%쯪Eg؈rcE#=JZp`"矏M"f" | %"'fC-RUE>Bc#/;Kؾ(oګniolW+hk.(]"bfkwx&^IS9'щ)^FW(Ż%74<7eൈkn#=a&fw84tU)O:(O #=d#=U|J/;#/q@cnb촂=Xşh@{a2$s0c0@Os;lx8$/kzڪ3z-0 2e"{B oLHf.|Tq[A}U6uX)bntoLG}wn#4G]%hV>EJgpΩ9`6[1[f4u׷M눰)mORLl j"c` y\]LHAVI^(T#uS-얀+#4с׺{5)E\hTPm3Q~^5?&}~rdHƱ!#/YT3-L'^=NfL7#4:ꕩEFmՇ0Hd#=t⑐W$ٕ</ .*.x|Ò`_p#=HvWS%Aձ7^9R8Ւ8#=:PEEt.ջlW St;qA[8SF#,|ܬi|&(EmLj_3s8V2531sB#4F-a°,61W |?kQ>&/ˈ}deȦA{w3Я`9LvOx{)ӻl}c21qR.ߎX$?p)R-rcqjfoiktUf'J"-릘KPYJ;*5q n_ڟ\W'>f^sfrb(}f2q4#X,Fs*#4m1Hbo!3dF6J`=$J]3n6V#T$d-э3fyMK3/_.q+lGqѽƃ3qU`]Y;T$1yz|3egAJܞWBwfI)2:}A~)YK%rn/A.J}<._ط61Uo,VZ-aͲt~yۿ[DAQB!E.EM"|=P$qUdRz-2k~qq+8b#-"j"*Tn. K3#4+]SJ2;eh0u$0+(w<*icNʘ*eڿb *æNK(=!qi-~/[;kf6CTX U.$|n'D <Zɮ###Q9l5#4v>(g)SJ5A6szWMP @( :&bT/}eHڬ귘/- 2${2%AG_vYGuJCJ9iTVS%xɒ/I;vHq>,LJpZD(gw@zunσ{!n$5(Qr oݼBbPo)o#drkNT>78@4K8`@Iө~krsi א3£ ^+,.ml#4#4m<'g\LY"ZP'6- _Vpg_\,yL{ڝDq02`p\8@^R1a{.9snf#Qx@ 9+5RO;\7?ON=jֵB¼<#4_/|<BgZMBm-ogxx6ÍiݫqرZ=ukKWNVamr>ڴ@(]t3P#=Ixv&^Sk9OO|Kp˃ƶyO{tL\ܢ]Nа#4P`wyKŘ-#4_dG.܅^⣏rE—%&MRFlɛn.pQ\:ȜEp7awYBx#/iIo?kF쳣\7-+vFraj/_®+؅pD.$_08plYo]j&M#=Al]y/{,2NѽT C(#KވUWB_ħ!6jJoEZ{|!ߒ Nmo#5 Q3#=3- #=+lMdD0`rZF!>6rIo4Cr;N!uCvw99~s7Y _ŤnִHg!n!z\\?qm+y,Fbz*`Q9M椺_WP^MA#/I;{e%` '[k2mdP;V`] Q †j>7X[ry}ʐ?Utu"=F`ez#;t_ŧ?Q^ޭ+SCH!UXGs躎!gv8 $>o.˰Q^{H}#=Xخ ݓ۱t*d ;w%]ix_veH%q3Cs8i#4~TjuզC_rSAo,]ABKYL^(U};×3&/#4I2ڠ{:ܾ5^jsW-swFFWtT(໏/#=b!. Pbj#=R(o[`6KcAE^Y> "zv-ӲѼ 7#/&ί2r*Hp.Q#|"9(P[#4EgP#/؀ S@w2!>NiHgY1*ͼ >?w]s߃[e7d%FE~ۭ/' eNMc~I?ۇ)@g #U@H}C% 86'bS+u#=55JIk^B喋f.ՃM6@|xTv}O ^yS\b(\B `n,Tdʨ#j1ݺûF&2ab:u*GHV #=#4I Hz*:K#/HHqs*&vn3Q~/&[umÁݫ!2:vJGvf!dؒpd(JWT2ERJUg秹3Lf<.#/|0Ơ|V $!J#4芟I}KR\[r ƃ-&bFQLzjTh:k& LIމBD5IL~,@"+CL@;Gͼ_ԧ%JfosFP5ڛet[6N0AEh%i#4/k^]}LJ;Csyzat,I~dg}1u9#/6#=AO?: /ogP^*W#/7FPp9@vyeQY=>Ӭ Ӱw|@!^ z/9{'hñKfƠzqiT\^8)+ѻJsb]Ntqͨyd=~hPy[U)#4ws-i=w9E[m8za C_KJ&-lcu;c3]4eʜܖjM6t7aX{W>FO#4Ӂֲ495+d[{k(IQd_7xLV9$TmGU35EJ Y @?v]|d}4u"@:$0}.όj<aENs@#ъӻovrWa7>$=wD3h#4#ITR4 BGm~Xu>wz#=4;.Hk1+P0vCI"hJA BBn\;v#XjsxAÁ\''<#/{ו-H{#=Eb*#*e:NGG7nYσJ#4N.v];YbXe A;TKIEE$]WV#=tIUm[*]4)vn3^t W3;|8E\#T`3 gt3D71a)`q\a( `,lו)7H `tʥ7urh)ǎͳ @5n|}7 :K4&lSHnk7,`CFKqE.jT[!BG8B@g#/q>[4E)^ LKfwh#Ga1fRC<`:L&} B6T&(cY/R4EQڣE#߬<{H eHHI>yxU@60h7~k8;X sՋGO*gHZƗlGچG ބ_C"Q#4xj(Y*#3&i.~a.G#49(efbCv=tBOGgzcוcf`TVN3قDl`t|abljCfC&#dY&oML X(ȌKI˚~rf)<ϮUXBH3Ы47I=y-ZHiRьX,gbXXL9.+o4R= J )79^E9cŭ FgHF&kEdf(g!dR0tum%;#cpP\Ii]p"*or#4C]`XAm!V"#=QP+XN4f#=oG#4D#I^&$盏,y1sɁ"ŗ J^Ro#4Thจ\ (-MC{a#+SSp\r*k*G/,BJ#4pҀi3mT6d薐81MF3D%A#4жt.KIIp#=S 6@u*#4xh &Pr1:pbňсX i>a(:vCh4 ;|EA#jj=٢O'#4ClCͺ(4Gډɨ əU?}jHs} bOo٬Ę|R&#/C17%/C.C~h$gb lBGd|5/}s$Ӕ1 ޽zBѥC@^-ÔR#/t&/Qj|)î_c#=AwscK[tV q^elER#/O$Jd#4'.~Lc\| g`?ƙχ #=$[y>ٿ\䝺i\s:֡ZsdǠEһZpB41D|Pv5h\9m*  INu~ϭE~rd40C32>U{'J+#=#OQHPo8*}տL{>y}ܢ+V[)ߋ0k##/H}䤖c:Ըi_/VL}Oun4YqQɫ wz]M~ HI#p5Nxv#&#^qzEyF#4pj#/Gvi _j<є'ǘ{J#4: :N[hx+!#/N &l2QYy X!;bj.ߣ0#UKM 7#4%pƚ-PjLc?b=eezܠ![s D -\""u( P8rŖ!B|>cbE׮jf68Z*)#=\׻!vĖ0P;!% tUC}FjV"f@KA{2I.\/KDFT_!U7éOY&abCMQ'k'b|axT3@[uDP#4_rO,#/5N_il3A[4rR1YBEbz< 媠rvmU n,sł$:ں;YC.0rĘ4QCͩJB]162He#b4J M#=y`t)PqA#42mT( NPBvOȤD{0jo[ޏ=tĎ4~7[#=]yeDTމyQoZ -WLvsы,Ӭ#< 'n:1MTLoTkV &4K D `$AH#4O/*N+drHt2[͝"PysMO tH|5zͯc 4 Sc{g͢=`8pJXBbT@QqL­NmOk1$EoTbSa9qu##='8dfMbqT߳ܿØ1]忛g[}GS`IIBA#/o[-$hsgK/HN(A#=e}K.{ γ֙$̓ELg>yRQC5?U_CHW@$C5:n7ê¢go1ַ˔jؕ'x,17nݘT@ܔյhVLI '.m뉬$i\us̓5=]Jʢz87DPܲA~vL 5MH#4{}\ÕG5[A(<5ѳNU%C<#raE8 0Į^]zz1'mZQ죖[ē_[~λ>,@>#=#wRKSE,f]&;~?-ʥ)~K7>_ 4b A5e|ON~ nVBQ#4]\K`ϣX!_u ]0L?qwW7-1t1^8#4^xx%)҃mc4) 'O.!vꚵ[g wL#=fq#=ӁZ6+j^Fox9G"Н5?#=?1˞3n.#/Fb󿉐 =jӰ`0J0YXbg8*<QIE.Q̰$ ~Q]nשd ]n=p 4g3v:9W3/1vdk:#= }-9̩;KAA7'iyȈ[gIDPE#4[۸Nt8}MG {-AL焲A^E4/u[s!)?8#=8qj-ՄҺGe"=C#45nxhg"BE#/&0W {vF R[q#/49'uI*}M6jQS`9yD3.a1ak,U`a{}ue٬6;jqG*6#ˌP(y`D{eu8!D &Y`K1>@#BUwCӰXysܐOT^Zn) ka|(-SHmZDݦ,Ab)I P2D8@oe:9<(#Aʣvr;w2YM2F [kKtP/@Lc)CZO7@qmM-鐛Y,)Q* 1bYFQ%/-LZO1ϵ:}o z;{Y4!vJ lbeMpd7#_ ŌۙaEٕvS-ᱍqgQfu1l@lB#4()bƹud)Bצ09k#c@(_Jk_gYlKVs {ㄧ#4tS;OTF@#=Ċ模t^圄fv4lӘJ2ن ]NBD<>/xv/" /Z!#so0ë#/Ժ&%M]\#/2PD$1#=~!tgjsxRw8 =NmH-uq&h Гq`G@[5A@ٲj-hH\ē$SG%:đmѹ#7 Ձ %F2C \B2t_.e#/I&^vf!ȏ-}E=aQYԊgڕbg$^k!JsUkbv9 O$tL8aP4l<2`.H.7b2ߢuP[6[s:j+R@ra 6m¸Qg=Dz 7Tyw՚];[2%&^x4o?#/S!etQ^VDc̗o᧯|lzWg ZD͆#/'PndFn g[Zl^@8\1 g"cQ[qAR€r8 h2/p?G`qτeP( tj徬eŽ.{r"zAx|ǛWʹ`d: (/ҹVpX_M2?C.gM\^on':?tG#4QJ({"ve;DcUFUq\1a&Ϟyj"%$/[>fa2ha#/ puqˊ"3FGvD@rzLZ}ue@dj"XH@ WT MOPTF/Or'S`6ʒdn^s*]tߨÄFA@uooӃAHj>O/vxE~_>aGp;Q'bx!#maŸsJNH A۾a0%22@n?x6ɅESZ}=?8G4\:گ^ݷvQOЛjzg2,h6옖m3#4𓀼Kn׍ҿ.A=#"JԽO٘Y5K .;t#49ixCG(~Q~cHl}^$;C U:"vY/{zSRDj<;tC%bHv&#,ʼn)+f(n_`eGzv^ @#/^,7XTϷ3t2deZ(*BE]_#/İ^fDPz-OA#/U69FDIItu]#/#GBb#4Y2AAB$q_N}rQvW)0Ϊ56 Š/ p1`rᗟݗq1(g$-_!~E+m_'}6cJ0#=B#/';}g~_ֵߥfo:߻WPWj;F4 "D~ 𑱙,ǫT)*k3eRmJFq܂*RsF[RG T4#/!ON]qkuAL *0GdE(h}P|k㜁m/vyu.tۯ!ӭBgȚ#,J(ha wΌX;ɳ0k?TXvEssbe#49ꠌ!Ԁp>3mMQ QMw\_'5%̞_#/M< )<!c 4D,\-%p%)O/H"K|%F#˰᧗?6q1@'ti!6##B{A3P@D8a^*{sM-aIGlHH//,_#zCO/gꗰـ:L)i.R'uߧ+3w(QOMT%]F 79fQC-t٢j\Gtbff)Bl-VUbQ%#4[Wyzت{7J`OzzX433!=vD&#J/mǐ~qOs)`mOvLDnu票Ε\sβY1kzPU3bUV]YºhJG ^|>c8!J:qÙ1ͺh#=٩©g%42F,.َ(Xs8pH8\e#4y.D8&5+z) /^μZfψ}j+7a`FJ,`#46$SYPyTh%qa@IjzA]Z0Mw{TkK@24+^GAQr#/3cI kxX)<&؇.+_XdT)nlJ$3bo&R6RYWWuGKvn(sn,5j}_u#4;<&1iGRfW-yz cmخ]#/#~lH#4z|~a j>D{%h|m[%ZB!D5H-a6l9p(M|:$A};㽬10DS>U#/o@ x+!= #4'h &040!as)ŭM(CL3#4KIɖ2Ґ8p+JGD62_tXN&rNx7{2>8$'e?#:~;P6c3Ҩ]ҿrL+6fLF[)g^"87I~\I3m I&3c&0\G/V1}[>;j(WH^&GRסg'R|R)U #L35XBB`gz[ G\1Fo31T48YͿu_}ѐ3ulL#6nLB @__?Ʒ}; 8#4KpHsiA>o$7qz>=fKFc!9AC O~~geԺ,c: =,(X۾W26o+oY6s )dƆ#C֜,%aL$$c֙kU`,ҦZ.J1I$$sE/Oȣs˶V٫&$Na|> QF9h >X~^Y3=?F{@0#4oP6ni5)jf|:(!-50n ="x_dɟo^ߖ0J&CTG!5V@;#&fMI#/G#=~!c]D,R%}x ]gbncZ@Ǯ罘iGWg0f8|Z4dkB=_c x$o<_p/>;CsW'ܦ_L5T4EB*#=%SueS7T,\3wnti}\ǢA.g3,,g#4xZ0H6.Q.7.1& 7\(^:%1Qv#=f}; i#4)XwŐ*E/)d!,ju18™orSRDr];NMdΕw*Q$9j@8<ȓ"\~A#{*}z-۟AK(l% |b"I /w'8U^6E\N|R胃q?L%SC\4_#4_xo ̳r>H8w0j72E9C(,fyyṊJWtpzE;on"\ %/Y0z >'`ۇ1T"8A#4c+S (0ZA-2Lu&I#4v;"!S+ܮi3T>mW,0N!hAoTD.ȞPs3#/r5~q 0Rf#4aM挚+ÚlY+CǤ8K巍qB-U@W|$ӝ#=ZĠvjTj<|j<ˁ$?w&A/QΩ4]I#4EKmWrM(D#/o*/e^e@:<'Jm͑K"XFֱZK:#=bq1QHô`cc,#4̎siWa|֋1e:lCкbra8vL?I$ k矇;l1'/b_?iꌊFw<ãx=>`=Id9gFwXfsa3"#/~r" 5S'iVMhܴnyϤh\_xI.-D*#4X d[PzE=TK@RA#L캓xC:{uצY#*;R7HavHn̥kXHV#4~ݳ c01U  Qb@#=v;Kv̩~G}PgUV[z*] wW}`l+RʷT(CP>1U{f+¦BYpjv N/AI7DF42@}ͬ;iҍO-e'wo]g#=(YgT ̄#=ysX`Uw{9 v=8hk ݥ̰mmgP@&ѓ`tb:ft&BXAc*} ^]xF9#4J'9bܕE *.6ħϽ 7U;(U |u%5I#=>|-Ƹ vesJjr>!CR#4Yk]b-|[٠q >OsZ~O lw|"1}1aqmBDyq~ú>Ry?` >UqwRtRW[,}s6_#4J\%PP{|5*Ŋx;7`Y#4)t/EwQ)7VF$̖I0V&n91w0(U8F.:8;M: 9^Ngj+tը37gBf_EļVL$;EaX:ׁ>˗ a\8+vƦjAJu'si$*J8DJK esS#=LN\0nBM[x3CNvϜ [6A)g#4= R놙ba%Т DŽ]@=FyMپ1~i;׭>_=怡0U @ِRm'(鷄~9SGnw]BkGY# pmAHZҶĸjbT5 EXG#4b܌+CL5֨[9Q(&4Z9®[`10z#@FfpJqA9-8~=#4Gw ~z.S @:#= eZ`l ol#4x.-a>6ݽUrb], 4,=Y:9>5BfߚJXnZtQ ʐPITwF98;ޔgX UsZ,"5Z^#P#4 (Z=idqr4ݮz96t#ƒV\5̅ɟXQ;}-zrp ǥaL{g|\zE2*xZH$_ǡU"d!Hb=H';} U -t^k#4~W>뺏LROrcd?~?V졡$i|^t/#/(5 [*O6X)ٍd4"2mGCc,B?#ntD9e}cnkiU/hiz=rKp|gdN|e(#/"zdY=*B"@T eV(YE̻^.- )#4-⻖c!;/URe,C?neKdUOzg0AVV'dO`TNڵUEF"=OxH$o7aqʸFN#4~o= #4pCq.]p@VyB\$ȠAL;&@wq72x_8)`Y8A"S9 4.{?4/`sy ?Q6htpPP9+[~) .~BxשʥhYYoUuO ժɭ9~+ι%,/T#'dsaH#45S%#=;ib/AEWSjR" yQ026t3A߈3.Ͻ/d (X\;>9tÆQr9=B÷kB(v,_d^p9"|?[z"$f /?s/B뚵fG3o\~%?i|?(CՎ+ДLr4hmulD#4aL^?5 Q #=A`As,4/Z`t4s,AEEjۼsa.Y4Zcd BPZU;i=ݽmjC^>^w!Tl8#4tdeDVBAȥ[R#l||䐟D| ZS.*}f1>dN4/Wy3$UC_~8AhUYg8+YrW#4+-%9q=æb>Nv}M69B62y +-[k@\0mT& DktI#/Rf+r tFE)#nH*ҜwO, eh,e`%fp*K#=t>#/]^+*.񡲘rr4GP9/7t̃ލ;fdܕ>#(\>GSOlΰ]ڱ bQ須olueBWΰ ۛ~c*nl,dTNV4jٓ9f"gץ.AoQƐ~wr@vk爮#49tFLkm؈n13 2 Hvo>Uy|HPocxgq}7@#4H Qo#CGiWћ޻NA},:\!]\0$ԭVq0Q90G`p1}j otoԷbmM\+ni@qn+]~U^pc"¦!MsTo+wtLq[f{#n:xkKoaɏo]Ǹ;0`_fx=p1m}<#=ͮ=#T\f#=\8X8N+۸?t5^*#4) rlr 0ejh7 ModS,t(^nX[|>Y -`^z2/_|QH+CMؗvu bL*J<"z.DJЈX ܺ+#4qٓdb1PK3*0j9>_a74߅ar01/]8rC"ZUҒ61' b9Gi ^&p;1DZ3|PRɪt~w5 aNȅ تl퇏W玭faQSd1",IC6)Bhz>=>pprUaA#=M A/JxlO`'#<7&y;TI,l9tXDj߂; OIKA)a yhLQ꾜|z/Nq&.eRdH_dB{b8 ,g^Lq$9#4\iӸ%6:-Z^<J-'eqv_t:8f}'2wB3F)XkO#=BqۜvAǎy_GU+l#4-U-wVRc`PpȨc@_0!ԐXDS,XFC' 7ÚY51*qȆ n?18%Fvl5 ߘ彅~G|y0Q-9ߜ. DE{: sUF㲱7nd1;[xf#41e2Cd^`oTU L%9|p,=wNH ̞^l<1Ӷe.8#/*#/rɰxZ?y&B8 8VnkvB4L &ɓb_\}fKzț'{#=[N+;c$H`Q%fB!΄"#/mYx#4f+.)Nwa)(lH'd^2>(sUAP,S7#=a-{AHҊY$,\;1;^UI+OIv0xc#4W]rx1h%0ϧN@0TUkͧbbeh8'~׈&L y*X\eMDS@pß7~EϘ`lG9<%)񖆂 <9w8g~p)3?2x* NeW,(U\R̔3Nh=0%Vp6Y./!JD^ih^DTƍj)k^e]U.>O{oˉ/ 3A蟝{|:t#=U WBdWrcŠ4F Z;)]GF`79"\&3ڝW9ѣZİ_le=`m)Ib+#/$M#]#=}U砾!.1?q/3:Z\IeU]a"@!HA"\zРFqh6'/~f3 .)iT>]?T#/Ԅ#=g V7ܸE&6,oR $DFx-8iC;ꌃ7H4!SxMqT½y|X0`aTc8n[9bQp2fqQnyj)8͕33_{ǜq 627HidGK(9PNJ1nA!OSL…M%%-7 ,Sy39< Dus7HQXb~LU^Oz0:p$]E-p0cbEUT^6  Xh ,+>xy6<#11u)\o^WXiKAq]Û~ձ,-:dE RJDeiD₍kq hsKC?`FQ&mt0饆[BaB4H5xS¶EÇ>J3uߝPi~!EA::'AvJ5Kz1.58|o0Q::SeQtقɡţWmת钩fUD#=81'!3Q%a}V. Paa(!ݽ z1>:I!#/:ӴҟZeDoӟc.M6Hm[뤹Z(#/̗wu0(7R3;e0WI,OM%0U{zcsRWcvY,.DqjϓtHV"%0mBl20퀷hnUS(mXa"u`6ȑ䨨U>/vFm#=Խ:72#4œ,eP]q `0AW5fq@`;:{$X A_ҧj?\<2Gn`#4<yF@MYJLGAA,Qre]d19w8EHSA=~G!$9JE U Ք"Y\AT`!;K͠wϱOi pQ 33>~fVn.m wo"< rh?1#;Jԃ}y)mwTk^[ *4|]Tu.,"Ra_QIRH,mzmrJGԵt&6]p粍B%0SDTR_Sխ0كɃF/wb<\EfC#=n*FU$  *;[f+zuuqь>yB[fSW.lD#4rI-]hl4l&C.v_#/ #/v7a'ta@\Mqb>?Qxuzf\z(>0|xp%T(s2h*z0c߮`fv"}gㇶv>֟_ - ˿iCJhI>o; 8o[7ñZ+V/'؇Mm_qB[~fq&+#=U_&eʇ+R>}xWڄ,"#o'ի BԿxuU[<X1&o–'#4lf2H mUkx\2U4vY״C1ʤqInU?S#4+0=:vY)quZ֧8z ͹x q9ܾ[4Ŷ66G5q#4S`j\!`\S%.Rm#'gby8`AǓ]F!%uP"F5<`e?rd",w iq}Ovz3zҺq$7k^]\=s 湇N:S`hHs.6"/,ڳYⴣgefJ9#O#4zwHZ=s:þB}O M$ָܹ,k>ɮ#=k֔=z}xO7:Nzw-֍UVUpP{s}T;V >0*m#AU}*Z\gVƗ9Zuie/6Dv@3#=v%{,*q/k3fM(Ԃ\^4|uze=f_>7ni6-NW9Nw*e(y"-@R[I4,g#4F>un?EyvgGe.AXr ة#Hw>(g@(th#2k?_&Jl_1.: L)/v3w՚%KSXn-+D|ZY1RhgogQ>vO#\Q-ׁVi#=9 ^8ݐ!꛴& ZOBe)<J(UcO\+_s̚6I:꿆UwҿqNcqCOPxeǽĩfV2p a)V{œM.\Lv)R/ʟ׊{|(N̨׮1|%9-G.ZX}%NTŕصr&x00Cgho#=y׀n\_)2uLG^XWX4'_ܭ]Pq! =E- X>/q.^*? gf%IB#/r#/$\>r86S~[}Wx˘ñhKF'XlDs3nps()N""9E{Db6s6h&A(B$!!Eq#/m2a|Y@ރqEJ0ʹ!-F&b-+_2m/5=#/\:)yݦ&lsy^o]&RDlq bU:d9ĝN\5^~:P܉ͫ *\,1g>|pԽc-)r0A#/`U㜉KoWzKqꕝ,鱌m9[1xF=r璩yxq؊b9^/ђm^q*PC9$Ǣ̀aw#=8iH||.7J \X̠pH1")K9hG#=L૗JinQr5„X" TYS#4ᴌ韟U#4)g>1kh}F$DȮ:tzryiPb2аfJ#C዗nOiH3\[aj+,%׋h)f͋|J\Yr+ Z =}N"ãzp9#=Db1eZ O%8cICciz9Qڃ#4tC#4U,Dg{LF]bORЈ$cY%x^ޞX (t 0epIcue3Xd#=Y(BS,XY/ C9*2K$[r刢 D~'QB 4-N4l|%׿j&KqƖSik-x "k#ZL.(^PtuvPv9 #4.aO~Q|TDnz%%}3Kp Hd`g B\%ed<'t|~}շ<SWh<74wDAG!.Q"NLmI$NļA:䓟xoml^|P֙phb7g=j]8$H(d#/mC5Og#4iǻ5X[i8uQâ`a W!\ )cVn e-P Exn >K1;>RD6sPJhOIVa|QQBxwcY/;Zrxc8e]iY[zKUypnw\U}a=>g#4Y^x^XcIWGɿ}s:c9!wE}{pe~/k|N}ϩ7.(jypLz\:])8{<;MLǐn9WfDw+L#ߪ^PU$XU3l=cygM3'÷5n'.@?`#=/lQ]TC.3uq?ͩV!3dK;g/KJp $Qw`48-p2=<#:]]z)pu=Ӎ8a'xDANRʬWOg@#45 JH,hb^B&%")M. JWBd7={&C}j0XKzi#4`P@Z*"iOl$#/&%C5B \Aff}2)&^xC!jYvg BdLz7K~+.]iD]Hގ-!](û8~r{x ABPPOlؗ|'R~CJ_㮶!<{O|(*`jungo8(q:a#/@ ?`9ǖ7Uڮǩ[ن'QDkru[{Om'$)@0SJo{%=2&Ю-Р$SHx"yB}1[@F#=iAR Á$ED lB7K!~vij"qyx}#4nc# ;}c8]:~}Oo sRR?\#/w?8ӏE)+.s_$>Q2ۣo<0[e/E9 &Hm#Τ/ՀT%ہkU H ;˧}z~0#/P֗)"O#4(!9~O&Aށ4?J5#=hS5Cܦ$:~ ^w#=w}B])E<;<.y.^ccjݔt6s9d@ȵh4 m5Qqwz1 H@4,KDj놕iVDuk@D*%[KY#4#4G!:/gT@FO*K>N?166/.qV%-g#sh|chmn#4!+2#4, -c7;GKpE׫u\Ei0)5kTawWVl`ޤ9n vd[9j\8n(FQ3MBcE4QV}Dr}lR};-^ B!JOnd%?Ɨ(PPpEHUNݹr+`k;l%ZE;j(}k@Occ( uB#oHweR;>`(E@"}xXB%"J"hR8A#=?qjI񀤖z#=iv,g:ryQ8Wb h,64wi{T48ðD)TOh0$1ݴ:M7ql#=1__s),cwc.0mE6M_2)TH QRx@ЅCgb23 %>GvyW#=mD[0Q iؤg*ck{GV+D.?ڝѓJ?#C&8y S un^I6,pTJ峉6|7X30}|PC;Q3ECJfb|؆8#4gCێy,"C:W#='$K0~~ J T~~t|P?*0QܚoX@QQֈQvPW † )_562*LT,:#==4Q!2(Pd CʐF# Cz4k|oإK`׍?ŢN:vC<ݚTFǛsw;29Ȫߖu<چ2?S椺pUDO.dAJ]oNk 4}qsF{U|0ّ]έi׃P:kfϷ#/B1&ؿ{t1F1#=BnaHYK _~Hj,ûY˒ԄMJypP$`#/]tz;-7߻QPsu%Q?/#{UO\(.1g"R>CN_3YKV@O?Ԣ_"hmNj}Q(IuJ}brI~Iэ|.#=yh2;׋fFOL&;?7n /ɛ)ڽ|q6)Q+]NE(IZ+@u\=A(#="swyG[$qdPC1dX4G՗#/Rc$Fѿ䞢#45Ӟp^#4<ќ=/=~a/{]t4xzߨuwhk0.> wOV%1| 3zNj1&<.:fV"yB#=` y>zv9~S?ϿW^uo.bL/ҙNeRy3{zj=1ݜ":kS9w{/v==A@$Fbࢉa`T#4s!hb&"%my'ψ|7mFݍ#/_v+ E^d2'D8buMrj#YĂ#4@ O<>'%O^_'#4F[L;>Uz>j#/gN_5^no{E80_]Pt5~Glݤp5z>~9މm^JOg!E6⣳[:{S+q#=s~#xS~ou{>.߈6j.`xE??>K[0aݳ/Y3j>DQJ#՗˖;N;Ƕ6#߲L=՝>:}BO7柤CߧcN ]C )/ rͳKw&&)/aXN|n<}7/Ҙu0:DzSRɦ~v7cGV'mܴ?%uK#4B0./hn,>z9yXW׺1Ϧ럲]Mn;~Ł{k~yF^^}CD͆-KGt?oŷ^[o˿e^N:Qau.ݧOn^+om4,YTF%,]x`fYVj~}f$6}##4VKE 22`|\x 4o\=V)3zak5q&ӗah^M0>^o?,~立N[6l(=?%G_Og~hl []Z}K} >!'yύˇݴ~/c#8q=%֨>C/6!K9"]Oͻ۾B]pFwi ?:OQ,TRG>-r9qM^#=Knw>Ds;Qy83y>|xṹ2/|Oy~eWOOg|ҹ9݌qxTMGڋP$;#=vϹ_p#4>o湶̻aL>zrJ|?K9ՄJm,!1/O~Mw((SC#=@23mP_>ccO~^빛J-ͅe{bɳ4O.m&̵x>|QjL y)*#4NrR #/bf LWig~|&(?/~Yt]86-rv4~7m~sHaO#uuf{4ю||KvG{߈FT[w~"޽@[_fZw=Kz>acM#=وԪ>Uq?cKX|Y'ktm׫1/qseq{gGO~#=7|ãh/#Go7GgޗӠ7@#/GWʠB<@ss#/#PHr N.D%:[iNN7et7A/hˣ$z:FyG~G$^>(GTaww>P.~m%=qsSQ&gQTbS۞YVX|w䡘d,"hH'ce]#/#=D̏wBk#=<:SCU9>mܚç Xom˲98R&; 8TA DupM.~ 3E- "1w /m}[l>>< } V]z_0 #=xEۦuQ$864kB. -Egx=iD:퐛#=aK%D虣MA7-`z%&>oXnLcgV:RC{)vBDqzפɿ-y#4q*8W_1l6r#47#4{!MNPԜ mtLt|ڨ`jpb7tA(B`SqTmFZ'WŨ ,ܢ)1ڳ9w`aI$7>k}k#t"a;9k,pr3!G.@isRcC{#/8tX5xg!se>ϾáзAftUwAR~#=1lM4c6k^-Ň?;|TkWݱO8}2.d)K锆6 Q#4* ֗tuE$;eǮ>oH9NYEݸ9?/=|tӱ%[*o9:9A puPt[Y>L{Q?J5.ͬ9%#4U&'Q6r'7Vq mS#]>~2'XU뫪L26FؚmR2I9#=2?I7(VCIX9ˁt6yyr֔AQ$M*IAcNT ӭ<dh$8p#FHnY("ӧ j剒QP܊i0f1G$KvJ1R=g~=(8#/ӈRөzڹg6eOfw"< %#= (1)*//7Orc`I$% I$@^Gh66󁦏$b 1]߆ƞ ϭfֿ_pgyzyk`7鞰V]>K_5}6w5yͯ_?n<$'x?([~lXr6[>PoG˚C#=Llt']9W& n#sZ MYoJ-T`?~ju~k#4?:$#=Y!|_Lt;CXA>`-(ȺA;aMᯚ˥x WpGD5#4#(??~U/0ؚYURSjxYфr"!,ˡ[G#B1GۚyN#YQ g@չ׉DQcQLŸN)#=e#46^UqV[k42XicَLv4fpF|6#=qRLμ0-101\8DY#}<<}H\ЯnN4Ys:#ss<*#C^0wX2K l*b8+8; i=);{w Dئ6dƴK,\Ab ѱ!O_$ |X`[YJaJ4dr(4g/wL"}xwO=5HwV>a,qQav //'ObSv}rn)OߒV\(&~!;}CNnmBvAy.?JsD9*~F< xb#qyG.aN NܺCs8Cak#+tu`G˵G~>)(:O;#/>IRJ[EC$Pwਸ਼xN#/A&qL~=/d#4CUw?`%{#{K:ܥU|3WZ&*b%P{GUÔQ<5p-OҶ"5>QDog7a3p"wbB)+qz^'\Μ7bbI^pW68+nԧ#4qMR![#4Dc#, h1;was2pF[1njc#4A{HD>L#sCjK bjH&h$39&&!V#E zd!ҡZS1Tlg XYX1;c,TX"T=|ԐY f81UqHa6t"ܡ)#45BQ4h؎ vD\M)_QRqIMqƣ;[E_\ZqE2U3GnI:JA2`rdjvﲠ}VQCj#6c]S+uj mջUa~ <眭iZLnp#4쎇Npqޝrm&%υwCQ {#=6Mƴ>DU4Q+n[RVusSKaN0lzǤڲZcBF=w0\#4dV ٤x`5.A#43]Ǒ[[bɉ(UF0- }`Ar4#41 Qt#4h< f`Y0G1^s`)h#=-`$iZ-ftlFTcN>Eޛ(6+I8isAҦۅj(>>7jQUCDnε1QC?S,=8O[ܜ({NkQE1{%?3GĢE>jĄ1{%1A0v:mbb)>%| N~x#P#4)QGN!;iC8''3{[`O=:1 PVf#4#4OYnO_zH"~$_OC:(v.1(w~a.4zqQiz R[qRO$46Vr5>U;xA!4-Hdܞ)CmUBC(_~ͫǭLXt0ia֚m#4m+:LElCMQj)I# O4̑eE[ LdaxC"mb1$:;I&@#4bMi4Lqʔ1NB E"8nh- }z6" vi"Ŋ#={+5\oQow7Fz8?m a8|&,;#4E?]9j#4:Mc[G jC32(ksҏQ*uwMS~Wb)^ϭXc{낛F7&5]۞{=<]ѷv~O&,Kd!{zVn_'nU.;ml*-uWƆb#=J˖ЙKA]ݙTz%e%z2uyw.YOs_\+*zfXnD7 PU#=t=y&c1&e%梆/)9kfQDF#=BIKa]@QG#4Ói BxrMf˕{iC_Fdߛ3l*^(W/Qw~s%AaOR|~;y~g?ȶ#4wB}n6jiݧC)<V-SC'9w|?n߇3V:{X?5#/F"Y@#=6l_X`dZ~ڿ%#4 0+ܯ E#/^MU=^q!G(k o 홚QO&g-MI\o7#/cƘp[v>QtbJG-Ip_C#4 $<@1ێ> ]݉E*+e ">d4Q X_J`:#=#4$<3tp=.LTÉ`ƍc *Aw_njyů0$'.P(8jsB_HNwߗQ@|)DmOSI_w$I'4z8v0o d0-90g,2{ZhU0U'Eldϑ7z}SS +ZwEGiHĽStn1w#/y <ⶳ$I*"EF4=jxfOdͱUi%|7~ b̳3R4BWf˓ލT׳!Z̳ͯ#JΎ#4a'%hbVUqbfv8FZdچ2u30F\tb8U0~a}3Lɗ,DY+C& Zv__IFO&gZog o';;'L:`&hC>o&W|v,ųICK>g_8AS`_4QkȒ4[X󺋀b#/l.Rw=9-r|?eRi?@;bʃdֽ}ǧSoےY9ѲM|<'5;jN@PQU/FAUAA}h@> z#42eD̿>} jNrr#4[9PF&SIj!L,>DO\RNpˋ-&Q/mHR9pK~ʢ$7@`ԋy1Q]#73egJ~tc&bCZ+85>"W 桥Yn#=T㐼,QO41#4iJTxj5j38i͕Bi8R b-.RE6*'NH׉Z0Ha#4͂ڣ#= =hw>]׌#rU HxOA#4ɻ-nsW !aۿ+.:kEc'5z@d[d KDb@:ћav)U`Rt(#;(| `Ēo|6& h54rn>3ȴiwr艴kxo zNֻV ݿYv;si-2-q#4Ҏ̢Խx\9["g7#4=R.$4bF4UxscqLA|y\J_?=؋F%%%;%o{"G6OMБ\^L\7fhCD,M t!>7-D4Ͻe <5Fm5YLJwIS)LoOdxɼ–&[|/،CA1N,s^~NQĈI -XLNhLWZs7ҵsYRRe^;tOV%1"9j6~)h ӧwԺpN>kx;V&n3Dvk.ώB(YseHd3l{;]gWWcH is4muy>H:#=RbtfGWƫ/ L ֓[JgmwKw20v;a-V6F%ՓExJ4n'.rhZђy|5/O7WefrH[} Pŧ/Il(0D>MN.;ͦ#4nmYL 0&#=\͜,L/Q*{l%хyi͹Eh,H+YYd>);eTm0єp1"^wk'$*n;m׾O ڰ@.Y0+7*@{0LV˦fgxPņ~a֢l9XJM*Ȉ!#=]d\vOl X[o>l%پH`ފ/Yں#=|E@@ԗ<xeV$+sѶ'%#=JB+[LDA @.d]G>wGe#/+WXd]VEٴב|րʼnU06iզ-y\ #=/zff;>1ڍ}T@.m!HQFvJ`325d*4y~+.Y#=\uփ6a<b TpL s<[7Q0Ung:s;YUf^HҖYUXNL(6V3J_f#p|Z3h!դ:#B#/q:Ӝ{++޾_fq.rM"#t5;G)Yغ:_%rk«u@<trYѭbA%jUe4`-9o&.F/x{|^u;-nP~^!rxyGNQB'i5(>Fƞ8_T w7b3aSF[AlyC@(IFZ/Og2Lt4Z׿wjk±(Zs+eX͸87)G>~KI|$9i^$|ҔओN\˵ť4sv_}\Qݛ}ٷxU7˶I{b_γPv[T)νg xt>V: \}J~W;޳lg7UXFH #4'f^6Uqʞ '#4p#=p_[@3dDYf JqARsV=k=(qk-~4Øno!_wl0!=1^)a-DI#/pwϮ)By[1йT.f7D?AՃ4N("VTE АX8 FNԡ#,PL9xwggbsEg;#oa%NQ)j q)BiΧk8?~tˉ5#`*8FjVK\aixyFцYn6z1S߭YP]G}͖roLjJzO](Co|%#4WHSrao#=֮.x?C]#PHjZ*̓Z m(@ܬ~.o3V1=Tea@qgVҏ;k8>:D7I{w[(OrG0qlmGK3HŦm^Yb\8JwHm#OdmBTنM ՠYH}8?6p2~?WqF;g~PAf~.k#?K25mscb0#ωpd6waBzu]O]LkV0+WFDwu)FM"? \\0f&x3EFd=6k* Dv8߽k%\cʅDi yA#Zͬ^ $]ٟlh)xN0լF/8ZAa {C51ƍt#ӫfwhδgPA4Od#4#=GxMѤv+A#/վ='vX=#4Eyʡm̷kI\#4e)㉶v8vPJa{9#C6t;Ͽcpjz8%(cZtQ[anl^.V]P7[UWH̺V*>UUmUSq>?.Q󝵦^j` hQ0yyI5Qݭ\---oZB P躍jJ6.+ic#4/y|1#!I&^5y1U(gtc"7Վ~y3"z,`5a6/<xZQC#4C:җ|yNUp7e.ҽ8"ygɳ2ssˍAI1 #4xٴ|X9fypx!I5M5O"W_@?9L o#=[kaԃ0;]kCuO'׿oRM#/OZ=cSPuċ0UV-z(db䜖5 Nj%l GPn>@,d4G34F> hDϳ<_D4~w]~as7'N~lF^Mwsc3b$ 83M'^`7]HLNy\k𹨕tK%/m큱Բt ~=5xiupnFtlPE=sJ.utu|}_)ѓa 3=ZO5K#=`g$=B<^ ,f7*ƭC7cVV6+AT@9a^_hF e臊qnp6ٸ+AeLdtXAfq~F"Y\7gd$oRFzf P7s?]Z,~/M~Z_FӀ+H`c\KOvwKh#t* .[[]2Y-b.-S1S= S F.Fc+UʅʔlI/ۭFrԘ;0bƼ+A!}Pȁ3_0:pjgQLEf/Oq4g¾yo|#4/v=lv|ծL@u0#^q~ /5/rk+fQL#xo\K|Q?$ҽ# Ra0#s$AtpuO7>T28JYG *E)⥸eJ“]Z@RJ܂hy2CR>4M#=v\}9c6AnV#z1aq#ͶCNt9U7da^aG=0bdl#4i8!~GRJ'1X#=}W׈\R'sPm/b#t6%tϾ mڮڃ>763Z}7=6rj@HaLaRbTtiEr?0]c9aHNcFN{.`ڣI s2%Kp0 ! 󇃗%@O&r{ ݻ{'iss9Vh$E+gaD@=b[C&u˾4[=ߡ3=,J{t'7K|:j5 ?O9}۰4ƃݺߡ`?.v{\a%?[섺)_Ubmo@A#=JX/OJBO xσ_k9']]7"QvU9WN1y#!r0ެ 'RzZ<^XÜ#/к[ hN:tcEØ)(<@)Ga÷~x@Y=AwKm/oDH5P=rrU#/sl|T۹Q?P?\D;pOV/\ 1oK#ᚈxb0 C5T2еC{YW[6^d1x$@Rp75 #=/2bKMĊB? v.鯐nvy.navJ>Fava 99eusЂg=]ˊ}έ#=#=#v[4;P=9D-~878}ޤBT/瀂J&F$bR!!!#/_|_~IG Ô[oOC#=\9J3rFbr&1}I!:t#4?gi1 yJ۷ܽyyJl1deUAB종Ђ.AU^:wϜV#=#=HIFJ `0)S*0P x^F$l&CH]}Ll|KN%ir↚h0,[9#4S<;`6 ڟڋ}#4 t|VtxE#/.&M7H&#HA^b q\ ($Fɯg׬~3c۰kvXc?"dUBRR@/P[RFCЌ.}1^ˎ:m1[yNUO*#4u#4p'Or,\rK'nFIM[~%꘳\xF^Tu ? j{:oUi>:bz`1ù>c驤pѥl_#MOuZ*]bdqaSFZH[រM7#=Y#4N9avz&ծ$X_[v`tLv"o__R2*q54d?,tԹ\åvr708pvpMVPF0"#=y 6|Bb E#/B'ݥ q/r#)a)d]Tܿ{s0y"`Q=]:ƶa)Ȱ"i#/56&ݎB ΢J>Hi3jG@η蠽z&hFɉL|d4$$Xwz':t'wWz|@?K~c9.Xo=~fv.2u~#=|q20#Cqxϝ8\컠yp1Q+Pvyu4%zYFi}@РdIwz-)G&_)&lK'Kr]~Xoݬ!O(i<1 2VLB(f )ͧD'h!) 2/ٮX@vy=VHP\}ϑe`ACH.VBt+޺ֲ{>9G9~6WOdqG#,SQSx`$ڲfV3jmh`2Th'dD|D Yd\|LTC"o#4T`'-ӱ>l#4T1an##'lo,s<]zMnXX'̧'禪u;Q#=@,)ȡ-։GZJ|ux#/AKzS Ksp4J^-CE<󍎜S-ο5a_A802Ϻ5Ň:Szxv%-Fknh#/v>]d[YB3 Ң|d+M@_&!2 a[*SH;0Ķ1d˗JXX,wgՔQ#=^<|'ӧ+탠m7 E<ɝāmrI#/|+SYR]9@ZjNaB6U=&qve:}ְk:#=()SA?@ds DDS-) QW| WyCr)KTBA  }blh-P)haݣ&TH#4]86CՂAzaDEYD5ޘ !Kw:br"9k.`zڣQ+*5Z\";TD01(DE0ys76sR>BS͞%ZN_NnsƤ-3[bqu}W|7 )X%9"")Ȑe߯xsj=$41Ka,~De.ԋ3ՑșqηyqR#oNS7+DW3lh2#/RAE];XI<k HM#4X@ԧ`&$gy[ǭ4k|R:H!Q&QwFuD"t*`QȀ!y)̃BI֗j`0kmJ##/lP,(7L?5vMj˵88X`y3V \O1F^D߽Tri;7ձ^hFCbjby(e *,86â߉XMɂI(Z)%[O Qzzk&UH>;o@1)$3N"G/r MHASREMtH"=!&dfu]%-!!Y#48'D3^4Fm*)tδM['*G#=<#4Yvlhn!\^ɮ."I0~PWNMK4t!yF2:>#/ o0t3分9#4y]1`paNq.`Pz C#/a]Ǣ$6*ÈFħ&7sD10|<#p(eűӯѡ7Vu#4X[B1=V`!W~du2_@)z;:Gt:͏W0xWbPE)aFhrU^cKl/Ljd2k&{=A_J!PPֿ̕O ϙJ≗^4VE'8Yre#CAUQ%QC\tAjhj"Ƌ;?,pT[:qȂ#=㞷&m#/hUQZ?Ps;_a>\TQaPA/?_#4{)+#(.QI/ik/ot]ϏDB)}rW<TTg|4>σ%#=fH7AGt4؀{?#HyHFJϘ~8tx@?ҡӱ@XRd+ ޔCӖVt~x2n2f^k 0)sI*=.vR[#=$p0יC/<3&R+a~(=^Ee @׻h ^=vL=wL_"tt Ý˻5@#/̞Bi>jPXmZFC#/D% rcAݿ(Hn~lFD;7R>Zu+_rk#4:0͟yv鑾"u5o8\dOUY ќ\|'lg;Zhm>,6*D4cn6/m)@:)f7UF<9 .ձFfjO1.s8dcq77-̻Ba$LchKa2V4*!Ә&S"͗[=ޅ̪qB5=z`Dzc{JfA\gYϴqzc}0ȳZ<YTƐg #=JUy\,̻ _;wZi8eFUY#4WYL&[m%07(adFMC؍,Hd&cv񚋥7»8 ?ar[#=ͼ?EluD|f"ª*%L+Aψ /VbñZH%#)s:3؍jJn^XN- h~];<ؕg򸺽#=/C#=JwH$;zQ6 Ҹ07Ԗ:'V(HYۏYBI[v[ŚIŎ #/  z E0 *!GoĀOs"#5^ ?O:$y0?/o??_o/_?Of3?y_>7??_Oc/w#=Xʔw$KMk]46oo@3;j9?I&\9Zbl*QO'OC15x vi) \D d.x #48=Nt06rT(m~ĝĜlpHD#=WŕI:#0Q#4&EAp@(p1@!%ߘiVȋ4t~&?>h9:Pk~ |qܶ8'LAhCQgrA\hGDС;"0]S"hc?,<2|ooСFPc0lS&(Ի'w,γG>.fu{h>OsŗdZ-;& ~n#iP4h؏Jxgs2Pe=PQ; eV[:%&tw#4U0 köSi|/(?&~^Cȍ=<rWF% XHd =FEA8Sx"(JT3N唎{/<+R0dկv6:r H<#4xV&MQs@h|>{Hӥth+A"{,9#/EI풙 <7d& a~<< <z/CЛXIawiF߻)b Zt'H6)"+A!40 $#4LdGQղ|Qyr/A#/t&LX~ y#/@UN!#/Ņ {wɹ:H#9a_N=W_uİB"46jV3#=m" *T颠-H9uzkYFK< 9_rf!)>v!f{O-8)Mdi>E}#H+I_KFؔ`ZL:QULS4U/6f y·cgI#4!#4R`\Mi7diGh&(1?RaKܔXù}w][qF 8;$ UPEC#=L#/! ?jny8c̞8;8Bꎹ<<}]EgSLebhx_9s>^ܔTs=⊞0(i.Cm{o\uM1#=_ԭ0"z'PyyqV2y!,9]JR3ΪZ #4#4a.#/n$obܑ+Q86d!"`~o{]#4O/V?nq՝1虃D~A NOϓDL{:9U׏sؼ*@ٹt(%#/>};01%@[cݛG]eX=tQC `lچ"Y#\q)r-{'NJ[Β7#=%u{J4)F&|t;m"@9L3=9[BϡZEF=ei\8O,OG䙲nQfu-F1SA]r#DTAjbR6zW6< q:Ct0=Ԝ~E'lkc.ގ.-mVrעQa2ȭfs3:k6"Ct#=҅X4Uu$qBwnr$߰5_9JwOߏn.}oGCSmbERROf` h*ZCmǶb?vwrIGu=/0I_Ĺ3mLOGp#=딯)`sg鯫?'~[o#4ޫ“,%0 alJ): CfB7d>>q({vw)4݅Rug)%/XU=$C.BFG'pgRC<ѰggGM@吼aVDD|T#/ |a@~?iOP_Ϯ;y~ S/^ʍLIy8~07H @ԑOL=*}|@[+>ϰ΂I  $DڊB $@>ɮoW݃B$VoGqƙsEv6ms;4 `ƒ%#14Aa{$2@7ѬSb'NuU yE\`P fMDI걸tbX1q_XoO_iY~;@ m?q\y56ۂ""#4{#4|i*6S$!(no#4pG;áy>#/Vg<:yk/k#f?GWC`LQ1՘AIJ|#4?A>ޘpx"V*a#=,6s-G0Mqd u[ʗ`T0Lt*-_$#/fȔRNkU^VZ71v>UQE%"x.ޭ* }jyOН|8ST6z(FKkG/5 cSh8#/t@;kϥ)7$+JQd̠}#40+ۇB#/NR#=%U=6d龆J+Woev߿çޞaHʈ8}ZTRArG$0 |>}N?@#4%#/̈3yh$=APy@7#4F}_ DDP>ԅħ#G#=fqPKlsIu 9Q ~F}O}! '6{ELq?f<,uuu%A!]8]D/g3~p3pjnWlٯ˚Np%c*JmW DJ$o5ynORCjx# :ou0<1I_洺 ܯ@Q6#/GI(O#=AL7sD#/5}zC.gn|;g\_|t[B3먄?7ka[X myױ9S{.\>'@WrcJxڃ-&@)z#Κ(r *",S6uD*n.6~7ƓcxO/#4}d:~)?$/0Ï<}>/繧NRk;_&$ot陆I&^ߌFQe33z(\`0&SCffg)-)@M+qo1PHhCB;t>;w{*"__`5##4ZL#/Vgq߲"XJN!c!!($+$#4MOΉDDHo0Mnmw6;.Mڈ}a#=##/!JXM';P$nv`81{""2uRO}_FИ8%?6{&33mxu$q+9qRͿJo@k7[[h;#4&p#=#/2P(Ax~'_Mofã% 𼣔{=w?5b#/$R~@.FBQ(F#/DyI)۪} rG^q_6B@HR3"F<ɼع܃)8@CwY[.IkQ?Aj"Nv 5]"OG/s|_钩<{ϰ!ȈA/aepQ}Q`#/T}(~X ذU|,Ӛ*?D=ʨ 7=C!5=M]s2xN눚5/UY6ʤP49>RGC$[YjE[CYqo5a /Lx0[W#=PVI jRm a}m=:\ss83X󚪣KBp#u`t#=-NCx-T%Axtx^.ۧU{u"5E;G [-0ՇOFCg1ö}U]Mݼb+e2"i/MITG9mko&C?weP8[T}8d9C|J"1/]$k}r}~kܾ*ˑm9G#=|eS"LLo$Pb8Ln PQFP%?/mzõj\bFtzDZ"k[fiVj Wzy"[<彃^3o{j:WW+;٬ V{Okh+t˙F+HfoCaËESe8nduT/F%#ug3u8 .UyL:,%nvָc:bfU=t__>cyqAN?bPҺs߱`L| 298u~LNQpǻ%Ľ:ꊜ"l8fn]Uct]c6h<‰MQR {C_p6 7&:EM8)#=#=%#=&X3"6ܓ>[Ɍ*vԽn{wf0?f/wd`Ds(6Az"a{}M#4|!V0!%جP>8 <ﺁ?WyMb9n'Ps~}3*;\f7!+ GZ!E9Q|K~\6?Q='?5$)Dzk\>+ӂ.Q3ư^Ơ#>؎]q}1yaA1;{{Xx8uB[JsRcӘ<+b1um zj)fvX ׄ&c?b4[੧w)Ϝ:?5h,⠕I⨒bAlGH.˯m(bm߻8J7:[WK&ORR ɇ1&9(#C?P{n9B^<2h!tg#+KpB^W.nxl"$c-`GrQFW w3#^FEH#=Fք$D 4wPKpϷuSC)ԪU*&"/>IP߳øu#=^#FK`׾s;6;r#ȇl8Qp(YxB05PEɼ)EYG lJ[3EV#=4veR].Y6?NdzANQ3_&nZ-FSv͆>Sl4UJ:3 D@=;p{z- dq#/з)G}ᛩŀ~;]3 *3R!f+:Li0sǜ6(A,`M:qCi:| ¸:q @0.Ly*؁|"'g5EY^#[3L=ABsW+񃐷%J.2 y냅(xjd P4c9U :9Z"#/M. GT*$JaC:CzԥHxǗdH,##/B.#=I3c꽻w67_־d؛7tL}~fad'1:Vxaєb%~La0ⰊlyZs~)洃:˂FO-xpsM/H|<)z_Q爍:dol!D.#/n $K;Y.G{?.KٞE#=Ό8phIxdS `ݧk!Z:!>D=/@c<3>=3NwJ}x{dU8G$gI/J#4lXrvF2d[CCѲeI $Om#4vמVwмPsa@J#`FcVrhMO4Ϣg@_+A'vW~.A7C) nC#4KUUNTdJMT#/#/)%V:!5 xrP=j}_&[O~|nW64s|T{x~SR|iv?z>>OKKeBʽkU E3j>q.D K " e@$>,$'"$͵W=^6h0N#4e,X{0dd-'QWSo`>)Pnu*Օ; #=R%>xV4u~tn|v>X׻btDП14(%@#4t?Y'zpr_zL3΍KeTB_ ?1ň!UOIDQ#=3 $_(9݃"CҌNIq[hZ5Q#8Mkf0;N=i|$lQG\u@a(UH1AF.F6ke }hDE"aL ՚3͓4TهVCeT!XpCLf Z9y/6>OD1À03 }Xbm1: %#աōe{&ؒRhSR; ,ZY;H0#4/&NB2C^;g ? |<=*>=gDEFNѬoT b!,r,rrW%4Nt? ?qrtȀ,Bgnd?oAX~OYm4X$?"kH4=|K?@3y^o3ē}}11,tfuI|>\b/:3 -5kd`v%);c.g[%Jtr"482m)愉ĺX(-j+N\`R &O͢Ł6,#W]#4Rڏ*=4vi=|sr@2&#GNgrH)g@j$ 4(7 dĠPS$\2Kk6k dr|>W\#GIgDߋ,NM?[i;H6S3C% *.{Y Wi"X(7HL3`IF(p(F1#/'}mp`O#G6P6d1x4N76vz%֛5GoΩ?Yeqt?jd0_88#4 C]JE1$+("8uNوfBwjwšAT5#4!#/1rwE7 YsVo#/zix#/cˋq P6ٯq#4`aǗ]!;.X@*#48ڵyɠCchADL_gS<#=~\2AC*Ҫ4t:cxyI($]+d۵ArI ns݈av@Э21|dOߐ#/6@9#Jrv4}y/Go(m(c?i#//a1_Mc>ؕMOp5>!nhNF_^ׂ&8Y2̛twӾCg ^ _k}o#4ϩ䞥hSTN>#ɛ=Ҽ.#4ݐfxG34!#/DȑX(<1Ӑ]{|@6m0%&<5+G< xkv >bB|P^"Pz ] @MUuD) ;9q̦&B *6S\L['0ѭO0xiSN8SDdVO#QZAD%4A~xulwP/>Q|*Nc g#=G 5b~54̑}G6)s¾Eg#43r{6 |t|BOﶾ:As$Z\u8IFknoWwz#4B~+;ukBĝ=B0quxЭ힄N0*+INUt4R>*@ ܫL(+$x`~U(xC=zە;3_6!0x90ف{4Hv̗~5LEf ևІQsQM%(PmP:{A{qL6P >CC:/꤫h;#=FBN# LFeP]<!Z0xw)8scҋ?  li`Zbgc8 vl'%F##/>-`#=ꔈ$)?/4| U,%`|P)Q=6lgFP!#/>Fx5 ɖZ!RʶuAwD/ ߟ{:bO?6J%#=b.i%#=<ժkGld}'Pd=zsmB{^hɃC1V#=(9q__5 tnCe,,,x)Bb7N71~J!6xEik:henhDI3U?5`~'ndYS?3'٢_E3{Wi]13VteaFR>O 5a}}LR=um? ~!A D1YMPsM> 8'>nͳqۆ#@8ԥúpu$6#wQ"q^bjAi/Hq 58& -6!QH{eR¤ߟ4i]C< fEG殀_Xn0!hR}.lGo*~#/{gL b j Q 0o_эbHQ,?xe-8uf̑~ls8(E!Kl4?vbUѯT:r?72g#=usB"my|.>)>J+#/?_(sqF8L_ױ!Sy`mC`!k:ŕl #=6q;t#4G5=tCN@#4;4,B M td_HACDm-^D"l :Fo'J{:<@=H@$HC)g[%d "(utQt@I_қPDD#4[ƣ1{Æ>Q|k.#4Qdw1KM5ր>{b\zBIH`!~9?/Ad>; 7yӯHN? y}U:攕$$Io;7ǻuovNni=X*!(@&_j{LoP)|:0=z$`´w;N67ڹ׮*F5 _xs8fw{CGTynE9F/S*_^]C~'^bYb&)vt#4:#/"^,ݷ 9?VƁCbw70y8䮉p:;GoM?;MTCD߷?L3#/DDUOD}+0=#/|" ԁ T@Xf?Y\[-QFW$m55[ؗmv &j"~t,7N |ّO\^ͱtH?X-U4T}*~0 I}_>'Oo Uzq|)\QO'Nb/ވ:X#4)!֧f%.jMp؂-9lt컚]f52ZЙ'8wyA#΢,0"D1真aUqK4^PQwOrn?QT't@r#=6ɧ:u;qn]Gѭ{ީV&":.-UC[0?PV##/$}#=7 ]?wr;2" " TJ g=Yz#%Rt\m<@#=:t=cǗdw}#4|[P$PACWbxY\t+PCS'fE*67FV[4{|<pCdBOG.(:u1&r|9#/#=MR }[r>FA>k^ӖBju5݂^til)0~OAc#/4Dniꒅi ̺ m70=!l'bJԢ/wOi#7Oc @ru@#/ ~g(#=$+\m&ZN?{n`=_b!g1#/c;>>CF|C0QFGnrv{<WBk׏y7P:F]ľgh!#A,[Åryy"A#/P/R렯^S4&pġ#=#="z_5,СWQ@S܉Dw/P,#1ff&s1Ul\]K?,lOϷ/=KCIMW}xw>2ġe#4 ޤw;x-4UZM'H)1f-#=.^p ?!6L 0>w@@uMZ !V%`/h &%Dٮw Py0Z\\'98`dמ@DKyenٙ_?pow0R:}ڊxDTA0QGUPyCsg4A#=J Fl!-ldߪ^i%SpM!d9Ɂ(*NI\#4upbH @{.R~OWRWM<}|0A[@SM}>~\đuHA1ڍ!moV$)zP(U*$ATI,Zc@X}2~F1??ߙ*)r~!o`V#/&nȀ,3;u>Su|R#=Гf2]=;080^Jf3#4ש)igg]QΤXU PIs=+@:$|S1F8=٤1Y֡(H"fCILlNTChY:.N|4^Hn.U _yá@\utx7;D^N7RnXZ``@#vn ビS؀WI&ݥ zY6q0uݞsP?F/ q< A1CN#|_30U]4Ƿ: mYl)d?aHr@7.#4q1_h4 Gɳ>eX#/ȩ`9:C=S&VdX{rI&m?#4e5TUY2KdX*3F¦*ldqק $cAIb02x,07?G<ߧ~Өyt ͞gY1eQsa`1^|O9#4NHM#921O>pQus˚={YDۇJ?Wd9~B>A͢#4d[(ϏI 1Orr؟#4G倃܇R2i K94wƵ.ҌбI#4]F!mol*WACNv?~Kp1ﺜNʀ&$O?n"o@!Á5Z3EOS~K=5]kuq*U6#4660p ޡ0G=^'>Ss+c5Q?gæCQ|fN-(lM$}Dda&;u?⸔SĜ a]q+`efB*K#=L ' IW#/F/^Sſ?n N*hI |5 p@ gq0A1ab6| Kxz;c?=ޏaޏBU>ܬi ÏuTGdv![PRNUq:d!G0C=#4VޒZ:4e9~3*uYI=C2jT$ #=Qi>n{_DȠ̜mGb)q8I߯tD,Z!=4#/v#4ond#=$$H{'$^z.O#;WES#/@M54xaq:W؋6cy۲#4q(@(07[!5^<'#/#4HҟI~E<0|#/o}UW)oq$}l`#4QdQ.lLM=7Bێw~Fx?f٤:~̓ٙ2Q?WHPq#/ ׯ:ŖlI|͓7ݴ"c0bf5x =5#4&E,%; !=Hq8M!~Tj{USҥC嵂18Ϟtw8ˍP2dP[d,v8vIpL3fhŘYߜ<-?RugS[7Ts߭o~`Vãޠ3jzYx$ Zf\>_,'Ibblt2(qOɲ:ï@D4@#=J^&~3zkuւ?$M!2LPi_N#/Yw#/Lj*'ر8/zW>'Hn(1%$1<^)<\LdNu x,0cia+-!\%Dtor#>N6y6: }p?0 #/5EPj]ǻw$B%IH-O>U+)G1G,H֒FvB#4 kM=Bdp*BI1$ŲGf7E#TAO:ӿԌ\aqIcJsn$)9r"j"Nmfhz0˕d+ 26ŮۼF9PnZڡZ Aqe, JQx`X?[JԩTK?M~qhWlzfEDꋬ#/Ig40U?~odb"!Ej(#=H6#4#71OEk V?#/_?6(Cbb1_T>XM>5I~ǿ#=k3_i$lT({Ow|qGn% ]W!I>Vu@n3_ˢJLPG̅NY@k ԅ|oϴzÿɨldUFtpR"?%P#4Q~@2H}bPҨ!O218djV0, Ԡ2#/. B] 0+g$R#/Er K<}( @HD4><=ԺIqdXUI'|?py{`y&ap{#4@j?H(b=OA@r%7yA:#=/%M4'' D_S/q41BfIk+lR*xMH9S 4-pcb?fhg9BR(Zo#>39NS6p@v#OVva^f]7b?V"fi#/!`0B~~˷ֻbKxudUQ@+ۣ~sTu9Ĭ%(#4J;?5TTD5ZZ[2dA?.GdG+e\rQ$eP %%EqpD(#(^~:DK䰛#=Dre(/A%Y6Ogn,ҋoF 1Lr󁬩y1#46oRaTv-b@!B#0LS_}@(I|`B#uO3|cX=W7J<}HoOœ8L(o#4V{uE9'e.j$txm=ïJK]qg:zX GPû80)HŅ#/c" yHab0X˔jU'cԯ,CJJxC26|wQ9A FX.y35Εed$ыsn\/Ч5o4C(Y!XyP"K'.J*"@t!$@4V0D#/B"fO鷞oh/U; !ؒZox# Iz(QHnY#/qoBwh$f$-Tk/5 u@މNEioI퍗~ޜE&#/(UT,|amQװie"#4Ȯ4EXd-B} 8d#=&s<ƎsdbTP.*#N#=u #4qsl˯rsJһ-Xoa:7Ѡޖ-(Fx{kjm>?nφLwtFażp{S`4.K1LtaJ~}GCvgrՑg~wQEttP8c)K{k4 D[B?zNB|=Sh 9ܢ;, T'Ef z5nS02#4H*"FǎPs*/? #/Pa[Q4ԓy7cǝHu^ a_1޹zY8鱎I~\˽"T. /Y.p:ЃJ׬k\/,_?ĞBHzpM#=FZ1gKNɯ+5xUh2GAP^DSzT Peߤ$F >h!%ɐz'}́>9sz`2f#hRdu?bӿ/* Oe\jT$#/8>m3 뤼EZ(\v\#/ bNgs$@3$ s=Pc8V≖IbɹpD#=wmD@ԅKNj d=xod &~534A@"n4V&Hs%TH<=L9!2G:%FŊuLm렜=&>XNJfO3?^#4,=VQ`Y()C|A.)03=w>a'.EШR$1ߧ;wr>XeC~|G36;N_}`CϪ$j_V?O\& 3'>!GgFg2r7nۭ㇎"d3,@W?+=ذ:z['c4w1$: $el%@x(/M#/E2S#=I$AH+rA;*kC 0Q`b52^H`)U#UKMOϦ1\(&f_nz?lzOݖGbޗ@G!̴ppiu͌p#>oBS11a##45G7u^}bAC`d?70w7G1X͖2!:^MnY>io m`$S!oF*!#=wmt?=j-r/]TEUUi f d8dݹMVw l2"DGwS< ʤ1hH47)ِOɊ I%jcFntVHbhX^y[{=zm7:x#=q?N,d`97O"p8p>| U L/AW az#=]X<\>cÒ`_';t%Oa .a/^l|Nf"d m5!#/w'J(0zaDoƇBa5\Ƕ<&0y*(I bL7m fs\񙗗=;3rmKnk^d ewzg-d?,8.0"8Ɇ#4(ۅY$6Ί&RjօIuXb#4*֬eU9KЇ646IC]}۲=k8a(%؄cWs(0@`N%r&CzMNq7fSZΒm>s B/C>X5XK>[XD@mTtR*.#=QyT(j"$gQi&O/~9V.qfBtM(\x:w>!#4ޣ5<CW hA 2)ݑ4wTJlPjn/WI䓡;0B"<.{jJ2%Zg*|-.,nPV5#ִ<΢}E|ɡbc7 #=}&d,i'C T9-#=kAf\4)4z6Ȣu \yhg&?,s,i#4||8/H=^ޔcw~g?o"~պhd1/oG[cQfm  c:`6 r*A#4W+R˹T9M {_O0'҅+E%RT}b59.HyiLvΆ2L^1i5E=#='97:b(UPHEAJEDV;hZأg)9h':98 zk0uIے̲g!'-aZVZ#4- 1F2o;xwnG9x+zEL$njuSų wW2xs7W :gttB̰KhdE8:ZJ$B338cSX).}ɁxlYoD$ٹsUek0oNx_}e+R5X#f"{|J=a<ꡨ^Ryh)ADw6q$Bux"4stzNPuM#=b˃M Y6Aֽ2I.r`yTb0i8ɂ&]ῳ{ T p6-G u R dg/q_>-*Nrˆ$VbG~{ĪsuÈCT廿<ɀe8<8̌ߏMm*Hh&#4zaiz3<+#=&CD^L[<2,`4"Q(M' N#/Աɓ#ƪ{#3޴U6'@*QFa :qۧS;0Ne3@9Bϯ83Pj[MIbk ̘;ٚfn{Fjb#4ƙc#45"0y%$d/R4ʛA봙 b)ZZjd3ޜl@#4j#ޏǞƈ~/1oGg `j0#0r'{h#xqϳ+#4ˬd.v{O˖B䛧OP8>F(f~׆#4e‡~1.8_ s)_g%!oH(#4akc5#>bƀ$I"LCux]>8reD8uFB'k jNЈiu4!yzR'X 'Cjr3T#50Cu Cgx3q8ԾN2U=G9Ciw@hWN YNrzRKX0;C#/8dGqE*4&J=I8Q[3%gϪ2c/X;В֒HLX{b*wwb1wۺ i $S$6X@.t5^iL0$ueY$إٙ6NP,apȳϯo{a祗MFu,~V^pp>e׮V1y!h\q5/^1LQ̎,S C;_7=mt1wNCtgmix=Nu3r~˕Jq)egQN8R>Q)A.$KaSKXG"Wd'imdǭ`r(!5/@)i#/,'4CoUt7 ,\{0!Z:qkլ!n{)#=N\$٧>$קev-gC|"}CAHc4D @7U#8{j+z&@}XIKщʹF@Y [qXxT?A>WH]ޖ*uRf.j#/~=ۧfr@{.l!#/W׈p=4kߌ̃ubs,~^zO/ǿ?2`U'=B'6C澽ʹ?-O3ʾ(Lsp =Z|g>gϩM:Kws/puwG@0` ۹Wgr*|kS~UЪ#=OT(#=d#= j=bmRz=녌(ˑm#=T1Ʉb#=4J$xEnY( xlxa)'L&SD)**TcAhJl3`[#='~";U`aYDޚU ~ch)Y. A'#=Dhx"?q!T* \!0Gc2,UFD?B^>χh!۫cܕ:b 1h.!9S0MC&TIM#/0L(eh2 *z:<_ Gc]7?23bjt(**|p#/P+z /r""٭j6E (s[uуG_GxCI]"h_Sr*Xv}?GT@@RLLQRR{dL#/@9sUS=,#5[q]ebz~ڃ0a]gt#4 !B2FPϦZulh+HDH&4_uλnD2f`JQlJu' |??'iN0(c1>q4\s6? gʛiL//# Σ(#/hrP& |J> @# /+2;D1%#/H! BGw@_h@T{9'q)5ޤ0X#4@ ؽv15md]73@$KуfE2qͤC#k n(I"+B1JvX678o$kB#A΂!dc0`?`@4c&!` z׫=]ОCNE>g[i>ed)UNqF*~dIJ֜DX'!e)aA~F&dC?G'fnC 1)Hmַ ߸lmjT$VLflŹ}(٭sv_gf*ᖚ&[tHj(7jnޟ*+#DDۏP#/U`uG=^GP0#/4 A()F2!&X~ߤ#4>Ln(#=kb+AƊ*${LQ!* #D@ 4- a(ȄP‹$J??ZI%Bg٘O,ՀϨTF71%0#S J}\|#4& ǯѦ'ZdE8 6tDTRƺ#4٤fӢދ4D@HLb 3P\dBD"'C`.<4h2ZBZjE Hᒔ)ih:#4$@ 3D#=I"hm#/HaPЏ!)C2v#=R1 ^L7,C`@ \GNoj*J;tC}Au'#=Bv/)x *ޤ~N+~J9zn)fD?V$>o^V vF+\C L*(_k#= 4ۖ&I5IԚ#=|A~v%#{^8#/<> mt[u<#gMu#=T"chDb#=T(yjEc$i4sOn=#/#4!QZi#SssV@dKr XA@SV9%\Y9 ==@}'g2"VJ.gh}A+ZZT0P困̎c-n3&#Xpl~PKԸ`fzn̘b6~ Qa眫GYeL^=(匝^#Ohą0#/~SoB6W8u&׺JH?6v@knAbJICon)>QCe *CdǷ#4LUnd>D4z`4$L I*EH&1{UZ*#4OST^}?E*RaQbI/>5Zק8pi\5l|#n԰;\9.9Mq%V~H\B;1}#/he~{(2JF+0s#6 t.M#4#= jIs^@wVm^HP`(UZ &"KϨ`ҫRpm)e[0_I?C]`,Z69NAOgp2rlMj,lm*U"&8^#=] V*r"@0k:ԱQb,lmb ;h,6 "-L0Ȫ4?Nܸ'9 #42S"PE4B#4 P1%!BA-%L0HBUE2B!2@2D%JĤD% 4)P B@PJ¡(P M KABP 2P'g~=+f<$IiK*eNwK#4]-a>E*UJ#/iQTiQ#/C'+#/;#=s\ >O擡4&`m?^6tO8|3`niBHMhyD@q}N;AB\` X(i4PD90lg >x>'> XTO!z!#/<G7JD&C@p~BpiMS~,BAB~nIÅ9| MRcwNF?CDcsftw'6g1]vp Ŷȗǂ!c3P`9`,#4`"$sOr~nXlŷG1869 JL`)Sz";ZhWoM#=?٩=\تsJ=(rQZ#4xv|zXՆ~}Kh)ԯwkR6uLhw*<3?3#4f,giS\8*!4E?daJji/ ]^0>0'l'q=2o(FM0^(!<Ϳfu&#k`#48XG='PM Pbt} ܍\>ǿJ= @6#KA/1+#4i6ĄUxq h9uѨُ#4D!>tLAӗf:afAAa'ivurc9|©YU1"n (GTW)i¸:CATUXÓЁ dd)Q@[2QZI]-_:*;&*UjwďM={O#=ccXΌMǑtLjW'PsX"t#!PΛ1hzarR%t7΁ M/g}QEDŽABLAKBPQ")=A"iՆIA& 'U4Du9)C0Hn=xѿӬd{AdnxDX49@xuMQQ LT#]lQ3_Q{* _p@HA~ey*jgClPezPm7}ܲ=vĂ|h:4b?UGQsz蹧DV064֠c#TNW e#=,'@`#4BJ"*E%Ե00XN#/i:ABh:,HR!ShlTE2lh@bSE.BJ D:}rc;oVP!6#4s6R".kx-#=A Yg9)wbQ-ٳʇD/FGci+FGg;Ʊ}/m]'џr9k>FHcјתA)/{1xp|8Zx#=aK#/W-_(zD-+m"4A6:X77t`p]\P) i(c;βwlОAٛ*M80hs8~gB0.jZoK.J dDNaHEa$;#={uq\k3x<cS<9u1|&!kzI?lܛFbey֧쬇RmeΣN/{/w!ôudw,8%iQbnwX.hWmD#/iA/7@םH$N#=^@jk_#/HGxڑ@jBfn.3tAsJQ p #/p;T3~FT:c|G-R`}(C~K, <.'&zcLlOu?#4 M aD`I]p%#/y`/jU4@D{llEWcvtef;T-[ic9d#4 HK!իY+LѣdChCq:I [L.=P'6]DvuA7pA4ԇ]$/(,\ˠ zKü}TO#/3Jk_qٵ7sn72m5sfR9Hf!њ"1w(1+yB0w G0&h*@i* i)*eS ɪ4C@6/63m#4MA{:y$h8h#48z3Ӌ.\%$*ɇA >07N뼂2!2= |EGتLiօ)h&#=&B%%S++Fҏ_tKCm2t#4=SáHI8mnqEG#=x ӘYQcV*-*¶O5)I{EǙ\rKPU G`5]86;`Pd"{#=#~r?>;y'Ӛ!Jtd02vI ޒ,*@n}H^ i&lm(xo 娪"oNM"TŊ#=:!˥]#/Ҋ Z7&];a@c+@8##/w~@QI}0|=Cv>{8|H#/PmE!}#/|M=6p0Bpt#4h-E#pLpN2sF7 M qG$!e8(&?#45˅_Eh%#\$(RWl#4 ճcbCD)#=((a\Kv\%!!̇8(1F#4Zq h8x8 qO@"Bm)fR{ s߫<몘Pc5ɶI}a*&tNYOAE*]͏̓BX4d#Yox\51'䤂 D#/;N8~7a։B)W-Hu' )l* “5ǹ!3q0]g*Hn;F:dʼnD]"bz/Bz̛a rc<#R@zt:݆u"H+Y4tjg7\#==C}iaЉ>E1]ljl&sb?MmM2bA ڪ;Nf\DrK!?F\y5#=Ȅ&OCTp+4P!Eml=6i-DMbD5 @d@MP l`n mЩCY>F${UhC5%tGPpČ$^+NTe;9Ds_qwhȐi v^eBiaS~:q[J<4L 0AK`9HIh,J_Hc=9A(UApn=3|_'׳rZLGVnz.brx,b,^WHH"" `4T,#4|04V Pa?;SKo#4ږ'NTNw6xr#4aPդ@@<+fu 5ahå570M:: bGUrR%^ٺ/#4#GrET!#=Iݓc j ө߁u; 5-$:D?K :.TBW`Hv[k<9*jk:+\gAm=E<z> >nQP~,' ?JJzk[u:M "a($GD \T!E硧_pF 4%3hU#|babsCʌ|/t'd#/ AWOoa{ HkP* "iA狋:xx$p~EԤ'SG"aʦ0Oj.HYx"$֥#PVF'kpG;%)`N@!I!4QCBh(҅#eb &V9df$id3ESJ=0vbI" )D@"#/2DQU7 1#=#==e@TcHRS#4pnICM̀PP8'3B w#«8GT"%q3q 1hLPbduD3#='T!ީ#4܃]1&vTIt#}1Ը)N5~#4!f>@pwD\źABJfPAψf`5~_aɄTJO,980T1~ LbL}ten^[5o& m@r}[%C0(cTc8cgh5 wktүʊ#4 (V%V4\eNYWJ 'zɗ&xrT߀%f'aGMG\3d%hNXym;ݞC*Y-}ܶe$uzqDkV6My(OA&?#4#=[QF[=1Ks֡/͟ީa8Qj"#5I¿ͱ(EsD5FGP!Î28u) 1ܚf#4&j-=Q+:5#493JAsZ5A8T#=$#=6UAɉ^c촣C[ozXע巫חyz4t3WH1]4d "ik IN7Iwwrׄ]}RV5܌2 orVc1WZ8d-(ԳD#=He"y@s8d,zi3n5LK(h2!@L3Tѭ,୉7G!>Nyj'U(LnҖ: ,4Օ-=piȹDž9pqګ|ES @4rtN\9Hq^4D5.XmC6 Q3,:1tj62EҌcr#=v15ʹ`!;Dr=Hy+Дb}l'Čg#JJ o'n ׾a̩#46ozXXFB1Q0"4ޛ8 LO+fGu_ko#40*]&Ӭ:A~#4]aL[!fsS; ^9/yPL==3g,-ۮDeέ _#/VtB!@x8&#= m֊ dCgHH y\ rb9. |Q5/&<=#=R'V,#/l5aY̽ Э2(1)jѰi lQ#4 )YSyH;SZΆVNDKbI;51:.@zHM],Ȑ2  єuaM$ͬ럏ׇDaW]jg#]1dɑc;=8s5!HTN_ZW|'C_BGuu#4 z҄|8\##4, ( "#}5Q,1$HME@ J'و[0#=TЎ@2 Ay#4#=5Tb)"l;+AwN2\1&'LIg݋ݭàϕ(*݀wlY~>h$bRƒ{3żƭ#4nj&sm5vM 2j~K?ozZhō>(b? ?ʄXjҥse3ԧqF?mnE!N،MHaCgn_"QZu=FO~>DpAgz1xCL^#skFEm'Ӌ#4G 'sE\7Oo73Ozo^Ƅg*pz#4]ݗl+L(#4KHc٣74c)={%{Bb^#=e =9ܚ8u~{c#ƊH_WJOfj(rK8 Zq_lEZ#=&OR<"֠}{i<,r~sSJ'Ҽ;oFX^!i\|!c ˈ|U%ͲƏڅL,֧6ƍtI8LsTa1+tg7R #=њz*)腮X\ZPgZBC]jͮ;QDW0έb:]DKl嫀X>3$9[tŏ##/2hځxDFF~ϼ Z)À]fBoСP8(vڃ6~9>˺S'Q-ӲM2#/$Nid,LU#=beg#/ra=xpx#/,\-SۆѡH!^U/TB#/B8!}(:`Ԧ2/!c)Ot~_ўuEz i2U׆~mҘ pXV={ !ho,hYe#/z<{:B}h&{VfX`; xG#=Qm뒕&dWï{eU<f- p2Y6LzeF4;Y0P:(=^C&Nt*zyJ#_ڨBUVR #4AQp l/Y{:Úvܒ't$iҺV$iRk׼#/D%)>wUuZ?sQ/><!hq:WUdqX(rY霒:NYŨ$Ji#qau`bxc1\̽C†i>1CeP`pk[9*_8tKJy\M[=X|xIH\Z"j^Y:j&Hjl;<l'/!m:i8E+Kg| Z0['7Λ?z1=ۓz]n5:R<}Q*]zTn#=b="#=n-Y[CB:L:aD_gɧJ#,J)M뇺y~iTfw̛t(w\SXʗ\EZOٙEy ]p;j4s3o71nkVlWw,]Nk7#4"Xt/RY|<#4B'QU❁9ImK(v)ÿ3+$vV(X3 PBitbV96y:# Z!$UKoyP}3zMDv#4ܧƬ=(|l(Dǣu"8B3gn]I0= qla#=`pɍ0mL<]=,PZErl_8:v5h(5hn_[1)iˆ+$|,I`sc8-AqT5jɊwV"Anvo?7 d̸&0&P]\a#3Ah&rM萩;O Pid#+``İJܻ=˛1 v|Ze61;,Mc~8B3&2{E]yQ^zFf蒋¶*mvèC#=Lr=5wQ?ND;Yuy)ʕN>6`?{UvW9' ug{߶;REm$XR LkF\h1GmW{$Cdꣅ`.D6`M{~o"]ymc9205͚IKv!O}LC>"':n](t %,pxu/D9s/na8]c N;$>٦e}&R%Eْ0֒3#4z [B[%cPò[#=ߍ=2- I\(@UoVm*a('JYmb U F#=cf#4 -|BȐaxb*ɇlfˑHa&Ĩ8"X>×Z0nR9GS*m_V\TNRUr[o#=k+1XVS[ڌbGdr5\c<%IbﷻJ]o6cT7Hfh1zB)H^#=; ]AGMMl?\n +]LJD8puψ4.ÒšMz)-ݔd6s#/L$J3\]qR^RרiZׄv&=,]a&`i,5{t+Olv4A"$6baH#NhE&!UP!M=:$לԤmDun@81#=ƇmeON!\LbLɜY Đ>XC𣟤 Q{52HM&]2qYaJq5mnAFj+F{e9=uܙ5/#4\Fh"/VKҔe(OXqN:[p֦#G}e]WDf`'e.͎fSeQTJY2g.V5_+7I vwfgmq;ls5cc.S534\VVc1c7 ^29HtsXv &*c'Yq#Q"ʌa''B|HɌnu$BV3>Z]*31롪t™֎mnMd"P`F嶶\"\\?f Et0PQ$ͩXsni`W^$" M$jjMKH-JʇԻR ͸;lc)>gc'Nݲϣ?nIP YeK2zեD-Ќq'ʣO58 -K_1o*:޷~Wy/m%/Y; @B:fyd+be-#=f9Māw!"%@8>WFnm[t)ugHp`Kv̪,2j41Nnᐴ6ܭCJ:5(w#8aY{J 9]P*=8veLo h#G L%7zwA0yG~EϏٛć 'Q #4֡HO3em s`[%8ɇ]] ,#4Y92(Z&hc4Xۥ#=֤Cxc#/0#4[224ڈ[ xfx.BJ#4I{L0plФ*Z#=JQWJiQiGzP5dGrf2.BtPmmه?Re];ø5fI@К5!Yxα5@{-$89CR4Ӎq.C\8`аj:m#42#4Gh,#4'Xc+}OqxfNGbg*Bv9#/t#thA,ȪF=O:uhY~/h=iSzw1Oq-.I9dPj*Vxz2k#DZM$36Rr:}7#=c@y#=+4Q0`IShj(jpn'6b8(2K ƬDf5䁲?.CP#/Ԉ@pi6aB-j#4C`2:#=Cd3˒T(Vďgc'rIdz/RvHdH₝#2#4N+#DXDJ )ؒL9hT`"pE53J&I,!=ʼHKɆBL01L53#4arXa̘3I,ٮVp2r75 FYU#81 \%ʦ $r,`:m SrN jz=㐩f֮iw7yXpĂ3TNc W#݌K#/D4@T̂R0&D LpfP6[1^K#==C3#4p=@R@CÚʰ): LN>ʝ$#/MS¼{>#/P1 Hԝ CE11(Pwg>*O#f"IQ냺S)ζևn)mӦhRSܔ~t~9Bm8bbƌaX- iN#4 ^9!;~#4Xd##/Au݂#/D#4M/Mgo~$= .(^>N=POimwq,&[r<[T#= n;P͓/(y;+(ZGCwQ"fTˆ;^P,9k)w4bT3 f`B]Ï75C!mr$?ztΗfrJ*<ɒA{ƢY8t]39M7A'%q!Zvck/],k{;SR%62bZ|ÐaӜ6^|5,2a3}12</YIӞ׬.!!eN)Flκjtp3sBL*,;y%v2}cy>ӃHx\PKn/2ն֣dΩPkzyUv{9]Ft<]P#4Wu]{谡\){y%%(I 4&6we:; &{(R!_^Jżx~7w:s' ]b58mkزg:C-.A9CK޻|I6D*7NӋsE (}ԤO#KVϡ(ZcLXs|9zsQ" +>dl+jM:y"6-յ"Mk.\fCڶa𹶂 nm Ruˑgk aU"9]{5d=L0\c8UG==$@M^B+*X]Dne@yU: vrB5cC>e&%Ihv9eYvH6*Hgœ` lw[1 HAq*(n:GXDm2=6S=xZ::1S1C3ѕtH gQ}@cH]#/7v*#/R'*#/CuC#Ƭ`)`4l:ZAA@{vُ8bPb#Rc4׭#4$CT478;Es˾M#4Tke{k^2h\2#4zL(QA#H7@V8fib6d"]Y#4@rP\)AaPACF*RG,}gw0D_Phͧ5GhLHVCPq8q~Ћx]aO#=#=A(hs#/T:1#/Y(<@}*<'Px#=tiI$v~=}-S&w93~0zX??BR/պL|v:lYNB]hT긷'F٥0:ރ¤њ⣌+ a3Dy]&|\e;Xglg#d <Ď+@f9'ygNTuV#=-P']QpEi4n،-;#4~\s2 Nbi0*kFCFf7ܨkGYi#=d#4R![h snf1aBpmtڭ%'S.Z*0\%H"úK}u3fE^~/}d 8Car[(;  "3wW~vD0nRdGn/BҨLy `ScmC<E* TLiH#tY#= a6n(V@..X:C#Iu#=bH0Rh1#4I]0Fv!b '}aHb^8cxYq<2b1G:QV1ʊ"4%nENHc}\qa暔>3<a2-q7:YGhg:d_PYxv㼷\r_N0ds0jCˈ#My]7g>RiмQ\=Q 8A`N͡>H5e[KdӘMQv2`=m,!4s/0ط*.u孓#=w"QT#n jrK18SNP95ur1D߼=`k?'B1P3#ݩШB&ñH:7$Oʠ>QC;^Z;\(҉0 0#/xʯ$R PI:`0Xgࣄ*h^d1sa(ly7ۼN#4<-)pt#= ÐcIA#/}\1ՂFkA>#4zsbrI0hm#hh{WyFnS2$@GBzˮٔ 2C5If1GSl#&tŖ4ꩂAJSC :%DQN+ptOy*b#/vJ#/)l qCW0O6]?Q/t??޸^0#/2QIKQDDEJH5LB#0 C.F2? Td|GQ2,J`j+3"?8OIz -0[RC#/8~h qي!na#4QC8xA?fw̬V8t/Ru1)#4pd}צ5T>[?5x>akZAYC#= s#/hKd\BP_"#/B)PFdx"*Kx 9<@FF~ ̜{SEe8.#=eHGb⃨SKl@Np "c \:i=l Idc9ҀL#/a5AӆyXqhҠzń(#mf6|PII )/w'LRPb9B4fXv\ p]$"e'6q5TT%-=Q0E#=ѨUF6JLP@("[0!MD}6SҙW{pC#=51)$abi~ϳc)4r]ʣ-d(#=PaՅo*\LAÜčn {i@!~tmHވ(3#/M3Gv* h^|dd OFuACIAE#$JJL1!$tM#4"EE쀘Z#/ I~78HEtAzuHGA v ؐHW$.|dLJ,P44PD4$#*J`hR$#= ) uPSSUU#4$ HD@$PRL$DDR A5@E1-I3KI0LU$QL4BİBDD1IPDĄQMRP#4L4ADA C24AHDP$S"R T0TM#4D0DA1L@C(RHR#=J2LDP*C#/D%$D #/T a"8HXT!)FePCH<`F#/B"@(J]Dx͇7=3r"|]\hL=~ yur3`эg Nˬ(k#/Hߧ0dIϕ?<v#V7%ʉ8щ5HQbhfha6Zb*v24-UT)UEEUj#=Bj>E^&X AV0?JPfJTQދ_({$I{ ` };;X~*UF#/"fe;bX%&|R5.2G#=g볍D ,;td^'wS>9#4܎s:*Hp13%I)$/ #4)A\ y2D@e+a2E<ƚx(ػטygM84&BPR-,/d*Z(d-%hsb";Q)SYkuaK)ɆTkDm|;6Q1sB\7@c] cDpl0w#=I@GWv Y-1VؓBgGzFK#yPJfؘd R@4Bpa#4C܁ZNTqSҚFݾo{,Cd:!I03d TaMxHuPry{ CN8d\&ubJ4}+:aEf t` ԼԮ9URt$$sDiqMJJOA5QCCp9d`TgPѡ۱;lV#4], :u$#4պ5MqwSNղ,8toQzdnPi~"#=hg( w2L,<(*r1Djx&a~OWMa#=c"PH Ctܭ%wyb#=dZ",#/Xb5ЍGx̓ rN<{}x{X=(y}{Cv2_dcd@j ?bSs-(DChvMHؐd7BIuf`Y!](]wJ*mkeKWNbdB%zW7&9X;Hk@۫yNGVSSUcNJ8=lv`13;j Fg|>>~r؞ڛ>ǜwT@f#91 F4 ANA>1C"޾򫲡FHʼn+C=W{ez䷪Cv^(eh! !1Lu<9|=K096#2ߥóZN|t]tE ZcImsq&߈&&Ѣa$h*Fclhc#4nKO3݂IFPceO-5hhJ{PDNq!r10dɐ=M(U"948"H (VIJJD4m!KP+'~'l.}ݺׂy?o$x> OEL§F#/p>;EPhAW+cwd9&)BŃ0ҩM?kAه7ń8(K@@ 'Ξ|]d夶M#4Z-/DUXmjO?VG0ECS,'Dseh-hHuΖȋ5GDdimQAs(䇜#4mL-.AL_n}[IY%KO+#=PPE#4TKDHT@h#=` d%)`iL(JJD Dc@PA/`äy!#=s(R;2}8#/";+}]hC1(|g,aW#4ѥ 45dle؇Jm$UQP)B9EHgnE\Cp˵?p@DPMUFp74L?oO%N!ʡy8sn)yȧDNj(a1TMsjjH#=J&f*`V#=h*%uzٹ?Bp9!h@#/`;5TӵWj<#/f#44Hj= #-#2Q#4aD"#4` Dz0X57}Bgs_!5bQ⽾%t* doغsKQ/:jHPɇ؇3 l~z|4}g!gfpX]{ j7@p*]ѻ[v1PnKBt%'FI>l=6vPdI#4#/Ł!_9{aLECLü ya򧴀SMifH~\H+H T#4  CQrTV @f@# >3QrPJ@"PTv`S@hրZmIo/D.A\CoSOprrRz|E8M! DToNE1bQPyn1W#+c^BWb+aww!|8pO'sj]$H!!GPy""4ĕ~8xz$F,Ž~l>5B5ZjN5DXS?4hMuIP@upAHwL,t|b DwLO0Z#/}AՁ#4ȃ3Azz~Kouz=7~sA3(}#/R=x)FG֤aXC;~X8[{')d%Q%q زBt =ȟ'EGy5i55V#x+4&0R(Ӑ,tE<^>B_-s}1nV#=f<`+'=Ip,4,b#40Wp#AĀg!G_?d':\MQ5(59cg[#/1lsoBnaO 9=F51#J0]ZX0GHcY<,bGqS4E;pK 6pDV]^i ZL:G|m!(i+8chk0vN G}F4zK$"rGr֢#{%y#%pPF{;Ɗ#=Ye.z8MD^7Wۼŷ̥s݊bhNm6rO%+`(i#=";&zuRWsӆ'"!b(T=ɡ#=lˇσ4plm5N97 dFUK<;̿)|:O5avkݙM{]vH<~c㫺f("[@2 rWaUrA!Im_ x:˔I*"mD D 0Q2#/D(=qeK&޽#/qzʃ@E_ !,`'!ͳ>4?,Qk~?/eI Gg`\P e qC$9'C`C;KR&1IpzƖozq;yI)/=>li5z)NA5u7B@)~:`P*$V!J!BdaD40+@-(#=U߀% i]`SLcBWTִ0$KB4^B#/bR`p}NlXR?5Ad3#/J\HGl u.KD)B4#mlkNp-:J R-MJfu{<#4TXB;N?+#/V衁9ϼP #=%U~~Tt ;'Ui #/U#= CPvkχn~<un{F{,rJ!5z%ؚQ&Y-Hbb %B=ro9ğ5[MN:4S2'D:6~7=]h<\©u@{L26>76$[P9<@E8*j& R}ZblI@$BAshhh$'9{F2b"* `-*fN׉6 RcpRԀ _!yktkB|J#/mfk#4x'@$O#C /h_<8,mJW &a0[o 3/YRgPcWr'TmL>4aQ~v#4w2\ '-ɇqN#=|G(`t2[AarNu3:##4#=#4#p˷;#=fn;9$x_41A(!Ec,f =(&s؂4kW#;hx+Izn<3\*\L▕栧WPG3v#4.xwsnv)X[>RqS.k<uIg᜜Lw(y!"N)U!D+FHowltv*iAN@U! RP|iʋ'b`aUT~yrv4: (8ȊD A3!^ޱp]1lÐij YyHOt30Ya^b~ueyyG?qLo~^PۻG#4jgB?=ni0:>3!= pd88b8:-N*4E%dz6,LA 5ء!"YlR$D'3Т)Ð13ȡ¦tv9`bڄƂ^C(@"OVonL%ttaD}r#/ iUфA;a!*9sICCjjn #4˚z uals,̞H/~y]L J%#4(&|}r< 3: _H/R @RDMHSM-PLQ)RP@R!44#/ATE4#UDKKSM#4 DLDHU K #44S2R4+BRUP!@SIKE4-%2TADTPRJM$|DQM T RBB@IT@UU DT2$A$P4RD$-EB%CJ!(B,"%E$LQ-SP Q0#4E13AT2RI4 RUC#=DQAD5R$*@HP@? ӕM1+.Dc?<;sߔkp#/y(=ЊR_, S#/Ah@1*Bv8Pv~; uWL)HPxyJ&!H>?VD{{}#=kI$EiMR)0AizQ-DħL "#=#=h!imĥ!4AECJU/#JQ"/q85)T2ELMA%{6E+IHIyp ߋ&~v/&hfOvyCAcagŲ"R#G #fpsN0Ljt#=,LsH>#=HB#=r'x/YΆ;#4"/`^߽hFFe)( "#=)"#=)JQ#=#/"M GP 9+9:C' R~I-G4珵kCa:C v9;Ԛg5OĜזoj̛3 L]8iTxYG #=N6 8;"K$- tC,7ٔuv$hUd8R 1&>֊Թ%caJ0Ău֒Cs8&jR#=HD6#/ɰJ#/=-`H5EP_/1ʘ$yiIȎd % pGP@=^i1>9@,U>y|=fJ}r)b1pT#=s>(#/[1i _c-M|<ȃi,1p*z:4~3~D)GL`m4eA(ybp9<hvDEL--e\0H2dG%0<򞟠?a53j10H1PJf柧\Xq%mPTAk8UQE!BUVaic4YĠTq"zCOC)AE5(B& 4JHBE(DAT2A!BLSUTHTJDOu"}cMܲli#E4TG\`Q(-AV$dˌlf#=cURw:g&_›j*j%$_}K2CmzM/gGŒh-wf%յ!Jzƃf<!eR#oaqeDr{.XoZ#MK{Z˭SC%#=pܳI$#4Q`hZu4q!4:urp'#)(ox]LK~%Pqh,1T[( M ]8,L8C#=Tq@YuNO^=Z&<| ihiIOڛySrji ?>yb !}*ŝ׌oB]Д(A7RD.r= ny?>9f#4l~ȥ'!9'uDgE{!t9R9v#=FǪnzz|#B(ѪMR2-R& 1/|!ɤ9#/P,fy̟LX'ĎrnLA3Mp15t0tɢq10CSN!$}ىJ"0 ]`g [X,a6ɂɧq vsuo+SW"h;'qpxqE8#=r6+S6'>#/;\D#47HcMG=k Nc%4!*\'\);ܠY5qbnWn]XGōqFbUA#=# hT.5#ۉq-45dEv*ű$jԏ|gGۤ;O峝?}m8b/fGq{1FcJ.W{e(2n 1Ct\Rhly|諡j[\4Ŏ*&8Ц3g^l%2 T#4 c<[6|pp;C4ˤŪ$A!4A'4i35@+`!1%Q< *fnF6~LWiy&uASUj~H/q#=#/w V x]0z. K!Ƞ%0 $b]""$qA1/_:A!N L~,p;JII #&({CDZ"$xiM=TJS8UNHi|>69pzס{Sĕ]Auy>T(J"$J, ñݞ=; ÔѣDzj.Ζ+sp7BZ `#=F  tȚ"z}Хr>J}WHvJY{,OiwAAIOv=k:SI>(g < H6Jn:A,7 Z#4#/R!;. {滜#/ P4]K *[%TEH$#/>L8-%%DH)JT`!h#/"Q"Vj$&@#/faX4s{J*UL?É 4P@xBZ%M#=iJ?w<3Ӧ~-E߽#R b$Ֆҍ \XM!VF#P!XTR%F _6N^BѶ6ڎg&+6a =+F#4t9`keZNpZ!ps3L8`Ď;4pvq+do h1U7.fLccYbM-%ミ0䨝30xp߇s1l{m9\cKQ͐Dc$)@ĿI.r]Pn1v#=ǒ5+h"]s!ذ%3%#/kk|]`vz.(F+#=GIuv9v;\B)`G9#=֪4%9!L 3A#=x2> WY=3gso$D?%a;#=w<0=ϱ3S_bTL.-}EPO#4͎k E6,+ _ǿ-6}#4ˇH;Q,NM1C|IԁIY؀;h@]xBއPCn=ʺPog#/l"~8hG{Cip2COӌg`uHTAA3ٷ1"83 q>~+o8?PX~gfuӒso-C%hld^ w* DSwVط#=0KqUi[Sbu_Dxi㙉t NpPh0`;'H׹!} */`*`OQqo-)ǐ $0zj,8_yf IO9g#4eɸ!>^Uaebz8 |0z_|\]ʨ؁GEt䉝1)L:6O%#=:Nl!jB9%G_Džx?ƚ8#/" <~}ƣuR`;ℒĩ|GCPz2衊0LKCsSj%lᓁ3$#dMЫ') fAb(J00-,ll 2` 9OTȦq'VE 想K#lmuyTV"X'6" 2"#>zZNӿhw}eZL7jj~\T8d1 򯕄#4IܡTy:wl:WϝRppK0?`lqTܔm>)=X@#=#= 0#/9.'| {9'L(_ȦhJI\ASh&pP`>Ivy( 6Ӑxꟊ8!u0O! 1뱈O#4jlԭc@7g?<-40W,@U7#4^CZ)<cEj$Nupa"p\aCU&St`̈8'U?J"&BƛbO{oJpq o\J>nz&WɃڞK&~ϟz7#/$D ;U : @#=vނu8k%j|z;qad%wټI!E'0s1D#=Srۓ3Ί };G+Hk3*3gdNAl;_dt2EPoikxL{Vyw#4D)aWPbUUh_ Nv8~( 2bw` AҔ iO+2Z"#4X z@E'Qޘ?O-h2͂La5$\QAͩQ d#=W5JI*r G{?#=+ @bގq_#4$>B3@#yrQRy%&#=4 JDAz᝟UT̺۩d‘SU#/6,w?oƿ1~L)#fH:X)\#4Y=R`4$^\yjrQTD MN0tg]X$аeX>347ĻV-Mjz#/٬P!Ō!ӂk#/]ssLATOu0`sYȱ6@8r/|rpBhCbl |tGRz#=uNfh ǯP;*X1h^A &0#=p3Ǐ{ًZY0~긓*wv9w?en.z6La=#4##ZǀpTan?EIMi&@O'af!/]H>N g$z~HP)HQ;+>h &:x`t)¢i\\#4vx;n;znwln#/u :HsKL>h+Ьhy7¡5:eEH~~8h@pHY+T[L'ǧ~kNRW{(C#/ ~d=} dDĠ~i J]Ί#4<7t"quj- @4(P:#4(ʓN#6"#4nKHi)3W)]4"M AJ(P Z@Ҡͦ#= ZV iV0 TP1'4B[\7#4NJthJcM/5E"e-+`#4w>HuXܳǔ4P[9Ah#/MPmzgAF@ ) h6!ȟ(s`םAbW]ͳ!Y$݁(Ԝh)#4A#!風늏M4$B%(Dp)AE8NudBj}FnRYPQx4BI!fBJ#=#4@+'"cfx#47#4kHZDEj!P|Cg?O}53ėKq_#F9mݰ3O!׼v4. 쓞(~ Lɡh9ßkԥ 9qRhM(Xn~ŷ_ 3ɆSLD·Jwbgc˚6DntO0;2 S#=dU^r#/笢iNZ|^#=~Xk_~zЀ>Yߗp/),"} ۻ@=ۜB^h`S9clbט:{ SN+yw#=hi]ߵ20msc:KDmޘXwKF`IX1e$T4ڠe((ilm#=)W=nʢ.?qg:A-0˃Ud! K3A(F(hSDlFy.R0l-%iM0x{)X"H1LK:0dt9!GԦIp)jR2ZKf bG3IH +ͬT}f=#45%Ũ˲G#4?4(Gs#w(N܇^#40mQoVaN=)F<%VZRZ{H>:s8ƙ@驈N١d($ǰo$PLAV0ĭ!$ʨƚ@9Y d ^J#=qc6zM 0IטThPUw;eHA0mn%G"0^sc=d;Sv7*4v"`Wj#pqŁ.TZ3nHP_#=4qm7JU#=D=xMD(DY啙5}j`_f#4FtEI䆣NŔlBW9!zSOhI{;DA!yDŽ#=Wk.c$00:2dec"66Q(jbN,H8,Hކ$uz@&dp9NJj@#48{0p<f,X;B#4dK+1PfW@ 1eh&` V *`""RZ&()R,<:$Sz):CM45Hs2 #4ƬK`"qD69k,6g㎘c[JQӘyp]Tp+ 1' TMYqsF'TijsuT,SvGv[!q*HY "|"q͢ h*ۛ']}%+tpj8\9!Q`9ktmCr&]ߺaGl|XdhǶ܏>2jgG~h<di8[8le΋*Q:KWLw*b5Á,q#4+rlU!Fzⶌ#=D#Oc4:]ULV7sÀ`Üa +u8yxIT3T#=d#/EB#3z_iG3@%#4pO9;*hSD3wA"'"8~mHOgCf)]>(f8ȸ~SV_<=Q{jFcm_-WLgLXTְmPm=8(l [4zy]?IDtaBvT[º4**m#{\a쾡4!wMA[b5%9zUߗdBog e8 8 BB1 BAp0D2&&E?A,GOά{n9QAjZҧJv*BzN;em_FŤ#4i ˄Ѣ" hYOiQP4j~lP@ŵË)f{^tQ~r(etgM+713gKhcKza#/i֮r~xEրzx8yMsþҽ +ʃ#=K4*΢vZHR%ޱ\o#4Gޓ2a%QonAoz̷fǫr&` ]\)\G5o>q9!Im#4ej*szN` U]96͌XFǓ{Q`k#=E*WwtiVvj*,bW# bcoqm9Co ¬o~z@Ɣ"Xw49$w`y@'R|#ʀ* )*vW>xhj4MF66#=K c 9ޗKb(#/bX3N'NlRCn=T-QtT#4 BJ(4}7ЪޠVHyM@#/1ɣ+SLN<6QFJxhHvZDlSS$I#46-$,qᱹ͹8CCˌ h0#/2S9!@qV-[*9<$s#/Pt:F)#Fh1*Ib&0O,*Z:HU LKIC3KlB0$NJ%JBhC#/`|\R (M*#=XnO5nT8i@Y#"#D RZ#Fc#/R&vŸYe :}Ÿp&#=EZDhafCD$8~tJ=#=!RCH&mc񈦁$)bTb.?lhWӴ_,\:NuOP`3L]HAR_ #/냁"@A3ONt#_*- J IA%1#/CMQ)DĀDуPqewLJ4iQ!#=#49 #Ryj B:T7q\v=/#Kw{[|f4msh[\t#dRtC{4fT02`~XHA$#=&Or%5ñ'˜9@1Op Ayk*I^J`J`-hmV:+Sh2(:Ctf:0|BՓl%"V ϹUDE#4L&Zr/1\5&hh2ixRɆ.-#=E9EIOMu@Ol(zzZٍm:FH۾Wkۭtc8zԆhcr6٣cXr#4pHiظ5{A#4c27 HRdfr#4JF"uZ+U4jYH&TQGzP244=9#=4^VbJ&#%گ rm2]{zh7rD+?<'#=HT}z9 (+p3#4il*ŅshQ9IC:BtQLEC "hB$#/)&@6O*;)U?W"Phh$EQ#4-4zù#4i>m@6Xbx'cَQҩ}qO }M#4+V,QDkqAŒ0Re0d/9KBatݚPdN+4DIlMY4m[va1hm286Lf [I*` y dIQ#/%A FiQ(JGgxZIAEiM&ձtsr%۰pns%& A1!4)L2s';$5ځ#=r78#/R !!{2:ͮ{KE!X#J҃Ҡ;sƪ4 :J&(a*GC w4D }R*%Ѕ#=iӧ#=2s#/A/$CApWߌQv~6`#=a+#/(AEGCɟq~ހ?h8W#/xc!9DX44ҥ&Yd7Oz8Bjq#=4[v1K8na=}+AP;HPXJ(fj ZwSED70]\-u}ߤ1(1MZ|n,olB*3U#/%3YOx()Y7O)lH(4TdRՁգVj>Gl#4'$:]#4IdO*uV dR@՚)mӱX#/ߙgz:Dtҧ%) |Ȼ jP")7zPD-Sc_w QTQK͍Gat"@d0$il#4[7kl 垞U@@4#@4UpyX0Miܰ( #=:8oTdt v#4vɭZQ QJYZE;ȼBwuT:nmֻ0P6-1v^Ra# w]X"!%Ou6[c#/Iyc_#4t`'Ln{B4#4#DQa!j4INT.6VIT1YRX4é?Dǁr#4U &g~ .Bk#4dXhXxwb$UdI+!eH껪_s{|_ݜ6}ksq?ΆH`7,YV':%;N)LYJ:6%Qi1*<QC @ N{~ei@"#^`*д21{t.%B쯬80#4pw IODF!#=AiQJQ:Ud$(*i $H("FF(% f hYQQ9f ,y꯯bfs  ~]gpa~f[jsNP*b)LJDl(xՁ#/ 0ҊL#=DE0n#/pTԗM;y_&^z{A)Ti聨ޭA(ߎ2B!:# yü.`2vX(rzFf!`~Dh*!"#4cG@Ԧ@<.{;tj$#/ִPd ti,BE#ЩetzCfc#=#/Љ@>$z]Λ1ɓ.zJ)C ZD"( P]>(UM YYhQTZ2qL" I@ e8zT"q4eh;0P}YY4@|Ԙ uf˟#=DY{.T!#=f9TL̇,NTRcY44?򶟝ۘVZ-eKbO0JN1IO?/<ʔq=$ @dNhL)$i6!n,t;=/R*EQ!Oˊ)L#2T,#/o!ABWFID>4UO @JD '@rC !&^&Tyb+EM$wk>:eIUM"Jp:CRFZ*I.#/F*#F0H(ѪXj JHdzZTܜ$ĚCGƸ6DҜ(~ϓXP!0 M4!Mmއ0%&QW:iY}XkI6{ Ȕ|s%)Rީm95YYƸ¨ȤD&6ڢ,:xVh=#=d0 Ad(T}EP=E }y.P`?dB ƻ\%lj#4t!jnY,n4h@P뗉Ȓh1pChr1!@5@l4&8IPw:CLY&Q1 P5 .ς#ٙSNiZbEa#44>к"}i0@sƊ8xebmSє@@z|Ƌ=1"5Jw/ό,Q0,qO~11rI3#4ms` ^sC=XFғ2)2.6ǦɖtXJ߫6X83SZSV@%A4ʊhUW[ƮTIa}#/9Yͩp)Ke8"XpᓗӉ=Ý&dlL Ҏ50h_-tEQ7|ͬ`v{7hnNF9rQ- j#4mZciiDR'j^G Y /3Pp#e7EcLoy3z2E#%?XC7v\J#4`h`'ѻ{8|:#/aQQT;#==54,3ƕG>YHDڮg%qG~RNݑ,7=J0#Ϧ_c>/#4ƨ;4P?!3"#4V$ϳ`Ã+iyȌiݣ<(Қy+o9_c2z۔[J|^3kzf7Y~I/)4R3E[NzِW#49ɠVR9)(-3BUaÏ;/z *dH37)(N{c*I#=ABH"$D+AV7g]efkGv|QXI}1a I;F#4m#xJ`IXwZ/"f3'of6\5`.Y5*կ[1e֭ڡYS'Y#/Tcr?-a\@#ѐ]RHL$i P=B,}J{:c#QӅ~XA5R2ABRC `R"NXVz[e]7=<[O\^uONaA&3rM@.JpwxF38'˖B4k>#4WzCOl}@G ;R67KOw}Jx|C5@PQ3$&ll( $Acԝ<g/aAqmz"49)pRSz~&pJ#40 E"`hSH4"*e)jO_+ϻɦGğjB@GC%||gk}!?^(ux:Oz')JHy"BH20!T~H4!'kdBPۏZ4j 5@#%{?)C? ] ;O)PC`;|=}L倕Pq%[(0Te`#15SfX[ݑ`TbM?(ƧyAȜMLB HeHdA{#42>@i< /s-Z__(ŗ4Z`1M>j#/2sp?g881Es|M|hPX8rH Be*uDpw,%d#/&qTDn+j#/W=K+f.&Dٜls|U2RN#4h4r Ќ Kw :laBO)ld2cE1 lN-XQv9^u`Gr;5ЩdƄ󴌗}Oh 丞R)rBua@!AF K)#VOJ؀DnJ@dAT\>7rAזIVwՐÑHstXXPQ({zUR<_wmBH!(4`;V-4cb- m2d%BLqjfuA>?&&#=w}>ҰÝPFߍ* YƸֿ =̬tRuCfE|`6Hrc4#/u4 +0qSٷݥp!"g W\rO$ZDv #/`s&J!#?!أta@pє3|d,;Br_-]7pi5D]%0Si4t 4nI͐)U%aI%%!B`<#4PzFU-O4$8s]A}_1y0хrIR}lvރCR%kA`}3a'&?\'laݩT:ѦU>`U;=Yi Lń{dL4kL1qHc-ezZF`q0 fF#4{p8t1yj#4]$.fFMvX)T@#=2J2ӝăɪ1[bt,ю+ M,#)R2јAAC@oUbMAk ! m GFE8XD&ȵAAa7eLFCW(4DAFC!6>ff˓uT:`pg [p`A;ӎ; #=HVI$#=O4ƭLX<(xз dbkytpPt~QnBPR0(pbB7cV.l BAw[:smX։y4ٌhl+y/#=Ƽ I4#/Qn@bU>#ٜǐyr{ALF4v]9jSU #=TBB9BZGICEϳ9!8E E8q5T<0㸍fΪC?O|^SMr#/S&REUB;ci'͏ɪ@=F}Ni%H 9#=Fj95ˊMQAOv`((*HQ#4HIQA`*f(`>$5QߖA1#4vܺsC»:=II$ a'@Ќκk;5bn!#/VicIr'+Gλcj*/#4ͺE1 積hveH#<70c7ahІ'7FAMgmh/MGIJCAЦzz:py]4%0ZS'$h")'U"|<\3T\VL6?8rrWO:b'e6B =ρNJiTX( bƱs)!372Em%)#VX9y<8+Ucg gE/--X,%wC(V#=j:URL#4/ :xOmInٽ#31d(ȚԘ#4"iJ>,cf]75#4;1Q٢^f,';x5mgMć`o)UiEbnr1!&.#=7cm ܽ9hWMGN\=!/12zkILJf+<\"똰#!Ʈ#4-;BWe3qϞبpcI #=GxH2#e#&k`QHQɁXh@\GtR3 'DXdj#4bcb[@dEdU#=L64$`F͏3zFM1P&j<~ 6Pk0#4q0kHJVo[!hzN.B|-KT'|ʖ˃'[({it3'y9#8c d1 {'YC)=[$MjcfO]\>?>Jp. .NLȫTGX2#/:I"lh65BT1NpLhD(SFR>[Ȇ5r=D=LywʋSCH2  FDD"8xd=`8N*8zB u[ 55Vt 26"e=;ApSjM@tT~OTXCA[TE.n%7pbƢ颶m+{z{#/bQRJZH #=#/(Z@`eX#= "dd"#/{:R4B@UHAG$?F)O,4;:)"Q0/5~ok2whz?n@CXB#=Z hh6Ƃ` B|RLT¥L$Jn#d@]]+ 'MM_ʲ{ (p¾_Z#`DRi&D"TJT#=$f@$#=8֐ճN[h?ez3b[\L> .pSnFe* j/2j$`2#=(-]iG.t` neFA=@ h(k XDK3TunCFhM@=~{?߯ U\ATQ'#/HvWGcY0'wVh31##/qb!oqןCEL7hgѢPd='L^IL;|:']#=RO0Zb(yW|}64 FEYNBP! q3}7#/ʜ:#T$؂1TqU!8Bq4W!dSHum5W2h+T(4,G_jwZD:ok0@u.kkr5}H7t=]˒!o)'ʥ#4z8%*o'`{wmHP-HX}4%ͪ!L}~:#/쮐i.B1)@M4A|Cs#H:{W.mQ A6E."rivҺ#/:@p8/#s*%9RikI1)CBbґ#=iXF3 whM|{L!B(g!.yG/QIrO{0A#4-#/J!:|OHh`=!̗59'H1) A@, !Tu8##/)%Ӄ:dE1M<}Vf}9G,D^6@U墴$SIM!4^5q#/ޢ™JJ`8/f~ou"D%銶vUf6h}oq) XPH%0̼5,VԿ[ #/P.hs$'ݹ#4'pV#4@NאG="V!nP"R&A#/#=H rQM#=ҠR&Z$`h"tGdu N;#=I4R$z#=I4H{DF)IiDZB!HQPbV&#/hhFQFQx"lQznZT(y}_b[?0|ϭe7g~}v0? ,~XoT8|=D2 #/x/o68pQ #=Д, Mm/eTH#423w>}}F֓#/` %6]rq0e "hHbHJHJ"#=P &"#/ @hZ#/J ij HB" F#=^O71n89mjRÍ#4r8Fy#NG%MUP:$ ݳ''wh#=9vLM cErqIjNMYH8QֈB8R9F_>g^rW鹌ry袙-^'I #=faI@ir"q6k ByB(2RH'#4l-hJy1,LۅᗐoBZ)8?FLfvIp8I& K䆱+LSc $lö֊vtpp$>xSڣARԐEADmpVw6 6wcEm9$"J8sus&5ΎhL= P5L\,E1LQ3DCBRR!S$F)cxK;!TK%ߩ%: }|s2Y2n?9eYR 3!yv޷"=?A5iw&*1}Q",tpkI̚y!!&êxj:WNLg&P=#2~9ZSӔ:8I2m=pY]:Ր"i3aDHKSШUw{f*rL?FDmm.M8y5.#r$uE5cn˜E5-dQqEid޾7QO#={#4VJy2J_U?ұ ]q1rPAwUXԃu[Q5B_vLfZR=N n>]mbRz&;MG=y2BǂV_LqɌvQEˇH5)W:l(#=XB{I(N1V;:8nz9JOw3?=~祮saB{Tʫ?̳Y)8s)q1\#=F}7-8 0=iו$cYeWF" ggwn,̦-N) "Eݽ/❎(;rZ~۸SYzds򙲯F>[)v*{QqnI#=AwF!5־-GU 1_qciw:YC)[ s14e!R*75S{;UGZ#&s#>z0 },$Ҋ#/9_vj_kgHzqm"N.-xQ2Nb9g&6)}3.p%wD] r&ݛ΄BVm&ڠn\ۘæ܀Y4ebf=j]6.T.C2TbԨx 1{ՉT88;JXxfnHkSscpקS%ҁ&T:xCWs8Qs <7KKCW}aT#{D1o0;s߇ !AC#0{zMw4vE˽-t=h})VQJ]cRDmwo'mR6?ZMzH_䱦{:*2\bŎ14bI[!#8~R;5Nq2srMs\I<$7YW1(H}#{14,kUKl2w]6dˁ7]M)}%ju9L+.Jx3ӻ׻ԋIW*$/! s&PhBNbbTNw#=Zp]38S#ʭyq1)ӹ$:Bhh#}øeBb}1A"l~2udu-I5s5~޸}"P/i[{7MC[x̨K!xgV0a ˤ4)vTLmda6ӯ 6%x~ޣVvQ|'iҧJQiB#qOL1J#4c?h'mJpxMAgD$xH$q2|.5:sa3Y<&^#=M&l=5F7][(OCx\&{&;8i·&T=û>N+^ix3a)S QC&%?/=YVtx ҕ5VrvєY<㷬3*%Tdʋ-DŽ?pՄ{_V(#=LPef6!B.}-Lu`~&JxP52q<-=]&U+1T?zRW(}L<pD_$^FY5#fdjY]e4RQ=xiQsWtKFwdkWoOpW4H|8S#=+Bn㊊qIb26[]]Rn^;+8xekM"D@)G};2&b1,q:n(%oL{lɻ#Dq֎dZDnolm8UJiΙuqf?zW^S7o3}asVq>Vlߒ;M]moee#iSr'JcNptw;ZCIӶ}_oKep<%AM㍞xǯQAALޙ#cS;& afPi.f#4\q:mZrЂGHGAvr^KvUG3k/BlvUJ:n5aqljr/ <&`;b{HOiu˲MXoc2$O类T򍾷#br#$AfbI/;UmpCsp.{Q={ADyRX֖;%"OIFA5yĞ5,vÃ[bi- 3l~:QTvvfͷ͖Q&ٌ*kh4GYr :nY7˳e7>ҚP!B5#4H%̘uVJ%ODLo=;Sf(>Ǯ&;D$΅Nvîm'9gc{axUW'sR9^#L~L:_:u7Zn,PA"3Fu㥼,nBFp`.-6HˡJ1ZWx6s~So8, e,×(t,ן{t!s?b91lsCK%ץTgϯێyJcem ۄ,U5Wލa3dZ^n@-(6av4؏e1I'I:k݄oWʘ|ruD0S0RNY` ^^r%ED|Vn'Yr}&ʂ7'hϋuM#=)bTVU>#=‚A1:<_k%[PߥFsө-);yvd?C3wT"kڹ9 BdV|irXZ$K/eJ#4Wg6b:U>S?#4)͐8vdh8#Pui*9*p_-賀uy,cmRz=]UuְGR>g!IsgE{#/ 8aHP%UTQՎZJ" 0߮pWƼXYV+$$PI^N)9yBwmRF^fD\{D䩡B4R#JtM(l"#4A*Nxc"\^ q8G! A`dw_.&d-dd4:_|ۧ/`wCűcKNgFޓd#=5Q6TT#4 Dt4PQ-**R#=P*%#/rtvI|aܞsC\ icT7l=ն0.ƒyArjX=G!b#1ә"%J;%\5Q!NP}5Q1c#*O)lIvM8tQMDӻM?"#4 ̣#41DO@y~fEAKW2q$CsBa')]R"pt((p9!A$(D cn#/w`H `:3LѢ'cnB~]`6ykA9}gr7,ƍ͹F!h6J_ܼz+#/ۆxpi>X#4%pEQ#=.ޤYdF؛fUJ r= чc@"MI[QF=s[NAIX_i)Hh'{cGa=Ww-Wϊ< yc=ts`tz _:_%ԥUQIVt(]P@T&&{[o@QJBE !%SP]9q7#=6۲.%0[a"i#=j.H&uD\LѦ,ES #4B)|:E'?g?]#=&f?0gKKzJDESE!LSPEAQD $@B"3(ЀЕUU$SDT! 5*I,- T$T"$2#A!AM50QMQ1A-$4RIUM%ERJQKEE4HULE@C4CԂ;2<Q_?F!/?B>H? 7h9>d9I2mN&s*{1owֶ$C RNػ#4P < *q'&I8Gkw2 P?;>>MDAc=u*ԚP4Ӧu@a|1 ,1 Q Š)cw=5oYl@zğ}8x0Jb^Q3Y$(\_\`EmPjf񟆩C|!˥0Y2L5z}g =BS&A;켃`b"(4>|.5#=1t!y>7"?[)OL>Ag*H=60(^ϸPPL)0E $Hģl,dڈ)%#/"ԘؕLT&5Б*PS#JT4LAM4P4ВE1!IAMZ)))0*iD?xa*4C14DT#t׾hڙ*b#=[#8 󂰰hf(}&Nj)-lu+4&7y;{ǥNM#KDRcM|"WLsv#4A{tOmnv#=E`ŴTG&XjyXOY i0ƾ!Us7/W(OޯbitO%#D0Vh~88E}#=% #=T(.lxŗiW>o‚3 r0,#46l`iK[Q+r,ߜ> g/19k=xa8 ^mJ<,;t#4B'b#4LH=OUG[Ƣ)\{.cDkDQPDǗ9E"!V4p_|ۯ短>5OG#=5K#4(_jGi~xyJsبo>*cz{beU"&C_IN:I?N{w#4Y>yE8 ?ߨbu#4;#4'KDFA{#=Wlbf#4^Ʉ:6q@P=#/y:϶+q#=P 9U%S *WxSWb\#=LCWy:^@۟HԔ+%Tp8s;Mx5t]d&?tc꟣@N9;U{& opu]TAuNMf:vGXŠ=ҠB019Kçӿ؞Vߖ}?Hz^&S'CBT )2dU6I't9,M~kkswɹ6lÏq.4+q#yk*EO tþvR;O=LkWc5{!lw FieYWY^%xivЯ,m+w_ɞ2#I/k|6 yыr΢V5ΦyU#4! VNMbA4p8Դqp#=`9}mITrC_9/amRiH8Т;szlܴF*ܷjcQN2ʸ$jP gP$ɌS~<>A,Cp&".L 1L;zEf-8~o:#29u0}MO99pL7G"'*k'`$DjYq̐bN=*C۲\ÔԅYjӔekI:RywOZDM;Pe#4@CsgTZU'4#2f Ԁ"Tr^jKh#/#4 Ӷ/T#=xf+BD\gQk0Nxm,{.ݠPZtLCi׬ylP>3uX~Sr LfK0Vs,69Ϝ``.ָ9̖^Tl։M@Qk)ᣘ#= i-PNWYH]d[`5Qkkp -Le"PgƸU="жkYv֟Bдyuo-,=I(łOS ꪝޱNR#4U1?52M" b/DACq)_'*#=u,1.暦ʙqh%h-e1ńۯf*4!3>YJUjcShke0E6 u02;duks-NLӘ M &/(bk*Ydf=r e͹<`=O4p&噞d$t #4vsC,'˚3( S!`q* u5x1MI'2|K2ܹvi09:MccNۆJJW,NvFzC/ e zՊ /t`K`ypzƤEU2$Pۓb2Tu00QTP{%n:#=FC  2A)/Ǽq< ;BFDTMvAG$95&)7^C$3ItGJCdqc]G#`h9ELWQǛ8#42fܙ{Х꼓,ヤ0)<紑й+aLa%pВ<Аp`Q#=ժWݓ&S9#=E1l%,RC?'Kwʒ7Ш7u5kZ|/,r@j"@d1>+ >ڝ|ͽx+ܪ+r@#/A\J:tV$SK68B#/#/#=æH8B3zd_SO\\0v6qP܇|!Qi %E)#4¢܋zZJ#/6Hsa)c&#/HS{y/LgDqײ#4SVhu8twʈ{>dtiؗ[3[Y?.'>W^2#/WNNHSI,/5rvs$h!a}(zjR0#xt$cnLSs(1UBF0]aa[m?e{1s׸Yc'B) @Q*PB- YdO>}~9@e#/:"zHIvt1 IB)1u9T5<< ~2xBP=ElNul#4R`Qˉ6p >_Ϩ\ݐa2]8#N0n4^pG6""1Z*v;P4sbls *@䮏<"5]QqZ#=-ग़1puLr&+9ΤQ( UDvv!TS mCc9Lm*F0ʙd!=WK@Jpx8˙8L*!}p |>rS!N|p֛cLٜBkxk q܃ʟXF|5s>%vF*ֲL/[wׄap1+36ZV`o3]hduFl4Wfі%& !<ᆁX KG}q_Or#=f1ӂ@> ~WSE"YWa>LM{Ϣu!9R+Ô4hh#=ђb86ѽ9q[pqj34r)(Xjd`~=akoSL -#/Un\36o2$SU64mcmG9$u&䮑9 #/qT#4{s&I']M;շN2ZhrR sgV<`|<`#=#=hP)d%#= {c0P&m#+Ƿ*۱Մ?5LɖQd!k}v©^-7:ꪜItϯ`陯)`<~C/ ob#h^"}'КuQN|/V4YTU.`ڼ]N3"#46fb ]o5Z\nΒrÖn'Ktd"#42;\lNhTiJlvu£aEQQ!MM/Pc6N.--,4UD Q90'9 9S]ۖ j @Je0Kk "aXF!23',HK( 16c`&!9ӱwF/v"6xnsu1bzICСɉ{08B]`RSU )M]łxs[Q3bIIӆ[:ն,] [sN M0QJhhhx' '!@;ۧ#=,,ezt;Ú(2V2S/hVBdE:rU.1䩠]6qHӁk2f'T[_ya,~GMa6rT27aԐ<=#Lr}f:xwBOG'SCm5NLNܼb鷪_9n>OK+_Je8yQoi)9ot\M8VOhTBUN:Pqii71ҝ\B(XN,weJ,C#=gIf"%LG9AmKA(|\V(Z*%s9 T%2˄!"8heYg\욢ò:eO-燣nb"tۉ7H6ݔǹyP%A4I(‘J̎#=a|,X'02dַΦp;b|wI#=1& 3 :4yR68:iTRT>.;:Mݯ8::C>e&x9I(o|>ΒβzBlA4023١$N,]b< .iTdàFPf,_|#/FA@AW=;x#4+2JK4/vP7ef&PZ%fM#=$<ȴ>ZfD(CxO%U6 5v0[ CLQ@AJ! |CM1%@D("D#=#/ t04Gw/0*EBLb#=J(8UuJA1&H)v #7"DL#4K `{@v]Wy 2D*D'7O=qghb7ET)MtL:?ٿ]}?w}8rBJ:#4`,@'xҞN~#/q;`̔ G#4C|3Ss8!?Z&#,31V&iֲЕHxCR@#=:-#/B)#=d)(JE(B$B`"%RM5UNW= |E#4-#="3l{Qh#=J("#=J*ZB!`$*iDJ=DD|@8#="#= b)Z!#=!dD~H$@UI#4$G:0RHrB!@Ne6/ ZCd{툪PrHBOœs+^QClMRTIG CTDPRKJ# R%0DL)I%Du9"#=5I~SXP`;]SUUcШQ/_}ϋc sB!zQ 785~Rr(mD#/gDH1anrUْW9i7t̠-#'%sӼPvwy6+Ȟ*C>8 `Ga,f!`F)+c < BFgZ=0ij^HJ@& w'=>@EDC h?-% QB A()@ Q#/Ӳ(5JAL0OF 8d%&6*Z̑J!#/ RC‘d#=Ih@r Y%El n4#/ĂvuDP^;%πQrJ0@I晭h=i$#Dag>+MNͶi J_>9@e#=IN\#/Ԯ1}$hWi*v>&.:u嵫b%>O>>dPB{KBWXq书KAZhl{dcTr!4.d/T)0b$2Lu0pD~(("$[:<i(Pi#=X#4U:r`:3NJHc[m궚{(k$F4IJDI)EA KIE%PR-!bU%)*2 @ȅ1kgH}"vMvC0 )T! #B"jK4ҏ2((Z*#/!QIHuzǶr'd'͑Ur2Os8>D:hQbi+&=XKF6#=S> mgHS5@?$2b&!5\@ёږ>`@|v^^p,Ny|XC#4O;iTΔ?& b1h )k_ფ@}G`-*t48*i`!r*$0\d`#4$L#4) )Nqg30#49B#4Q D#=6R#=6DH ($`1#"HF1'ԱP! .`a3 %M- 0L+vL #40,bLDC>Q>0vxl#/DY؎E"$%!;&6njL#/"O#/aT ML># ɤ:yQ5< XR0 VA'8na4%c{"xit" U3~ERL3D1u;Gy~CRJQA1$QHF?OԎCO(] {%ܿ̕hXN!! q#4^EU0^sa#/)m Q9aQgtGs^ml^ELV㩲<4 gIQg2RuYu߬ɃyMJЏ#/t@7OO59sHIHD#4=Hų_hGl1b*H䄑 KJfIdkpO-ݦH(d9(`\bq2^s]QbfS̽@|`>~?8nN&&JX/ǃua S۞SxeU1J$:ÓA.̈soA#=:3GQRS#4)Ć()d!N4}Q1\[ -{]ŸsEgV,x[|e'ךPv2P(XWI(Hj3ȎXYt)#=pMh%"}dPe6l&D˰ xw ߇-Qt)'43@ʑ>k\:G=q(u?a!~IrDE,(•0r,NA2TXа" Ilǰ輴ȢG+f9cF3膆Z$$SILh&#=?ќFpČ}"M$#=(@@QJLHt!#l! mU)i9fN/ɘACiA?ϑt&s I$WIFA$#4MS@MNaU,Jʄ(3w|J/`HS`Kd8۠ D$IA15$Ad䉪#=")JV(,R"M5ӠX kF3FaZ.# )ZΠNNj%2}9 'Aht} @}}rC/[*H( #4FH~( &@Gf]#=g DINeDDQ7p:ub#L#/`kkM,lp#45!զ@y N8ڞzi{#4GyQ%J:HvhxxNY~XP8Dڳ.ބ )a;㴻|@#4 #L<0)#/bM$XAS"! \OԮ ThJ}Yg囬2#mǂ~'qfiplLvL4| NEj֯X8#=c[1w6;agŲ< 9<,΅c9j:X#=2V6PwvDqyד#4-unL[O[2C!cdJFmYۿoTW@5$_N(պQNV&tс+#D")V,M?U*)t|9$酓mhj7L&֤[D&'GsM1 d+nma6ͦ&\l1퐔eԄFpf2iS7$:t)W)O._#47D\Өt8#ْk!!g\6XqQiǶ`8s~7XHF#Dlm#4،^>L(qɒ"QHS#=֪O#4I:i39C;LS#/)*`0yofwuI<,|=gKMOZXSJI|IݟCE[&QфAGNKbIC:~ݓRtm%G^1!l_.V1нcfٙHPwAGTjqGE#/ڥTDbj&BVX `u~MzE'w5ӈ '$Y7z=x1h#xG~YοPm4^l8{7v|l d`@B)0AO6@tww\@L4E1#4AUL)!@CT#/$USCQTL @B 2@ 4J!Ae *'yP5!d#/d?,Hu!с W죨,11P_+q x[&#/F Hɠk?kW!D,zR3Y#=6<\N7SS>h&T557EHSf#=1Ah ʹ|[Og~ǪaμC_3#4acz?+w6`k|'^!t2#4I0Ze&kAhm4܌Zp`LisT8v,9k]!㸎u5dcu牜m_a:#=;"3 vx;r+61SޛtL ̞i˓ ȸׄ]<z1#fM&]l̨dhjK2JW:lڝt94OHZJ8dx8'21 zX#4^% )F$f É$P;[G82P| یx:Yɡy|XC#={Y:F~dM]Jaa#/19RaHKe:N̛Z0$.JUhKjBo,ݱ"!hHNv|l#4*bF+mz60RE%S b8"cc#="l>]mF9μSi E 2MЩ%'$~4r@Э#׼k7nlxCX>_yQ$=5#4:܀v\@%{pi` (@M&C>ZqY89j0x '˲vBtHBkʂ#=jrfr#4?WU۔h$xD=` @ Bu=x#= P!JC (Pa9Nҕm+PILÚD3/E)%$W}d/Ϥ/SDu42DDQ#4!Q!CQ!_*4PDMl}n?t#4UzOq6!|Akmӏ_O^QgLs#=#4ּo=#5|3! L>NN1p:Ru8|FV9J;#/!-M-6t_):mKWF.7h8P^agTP 3hkʗ$DmGG⨊gU,#4L;qD 3BJ3% X5YB*9(:Y7#4C<9Uy_~ӫNqA܈zBAQM ĭʠ2:ݕ6O*@?HN?u(/!Q fBLL ,A;7ޗ_vCPO3CN#=EܑN$#^ +#BZh91AY&SYjR"?h&_$m~V#Xc~z݉@#X#X#X#X#X#X#X#X#X#X#X#X#X#X #X(#X#X#X#X#X#X#X#X#X#X#X#X#X#X#Xo}_Oﯫc[ٻvvlqVڧYF{=J޾{}fKnncw<ͧwp;jl{}>-}qǧ7׸>usbBMڱUpvx]aJ{[f{olz#X ;kJD3N۷(#Yn8}#X%T#X\vR(6hCCZd 6tE9QUZ˵uoCǗ֤#nv}h0+J-c,{2)kɬy껮%#X=x)iR"s0;a^Wd.Vi#n#n()#XTUBo#n;:NîQu'Eo64G,'﷞L@ҩEu#X#X#X#X#X#Xj=B#n9#XªQh#nkzѠրk:vf۸Ik@Bm#Xnc#X*c(PT#X#XR#X;(4#ndR{Ev^n5GM+&}D}+X(4&s'#SR>ݏ-}w}}y_Tyswz=G}ݛy&zj=>n| vu8PjUH.nmE;]kKhaSBx瞶F=upyϽWmGzط_o.}{|;q}bκ.ޞwu b=_]}:wy2+.^ϝom{]So7tko=t6v}J]oN"@#X\mj׸j[vz;X'_1Is}^ם՝}v쮝3y(wg%תv:EJ=>{;Z![5tm={ֆ͝-Չ]-\{n]JPU^w^P9qkxy]Xwjn]nݕSlH=+B95rvunּ>vNN#X0l#X$D.Iwe[r궪!ج%vҶI^L>|u6ϝ}^o8km;nfwEسP^P#X#X=)/mt&]d:;ywMsSU]s#Xn6ʵ#X(#X#n6GQܰ:=;_n0Ş{xƎ#n#YT\]wr@#XWk}U(-פo'jM>[#XT#XNήĀ͕ÍaTtklw\Ӳ@)zD-8KlOR67nal l{B{t@tJ"L Xܮ#XW3m[o޾oMVyiOy:{k햴+goX|;:B#X@M#X4#X&M#X#XhhFzAj4fdP=M#Y4ڞQ@ d44dʟ=I䇤Ҟ#X#X#X#X#X#X#X#X#X 4h4iMS)OQy)=L4z=h#X#X#XH@#Fؚd{zI?IhP#YPA#X#X#X#X#X#XR2#X & `=&mM2TDF#Y#Y#X#X#X#X5#XL h#X `4ʦ= )zO҇H#X#X#X#X@T*t?SC©ES#YZ"#nfB$l @#XH$GE~ H5X|`qcU)sD`x˨tCKDMԼAPqfA$fqV_"r$="ziACPv#X2&Șbv2Qnp8#n%#n#X(*)#Y- Шb(E&t*B"!߾n o " @L#nBA"RĐ1Dʙ (V%$^#n"3*$GQx@MD444S+lGS#nab"GSBAUr"#n% RP#XP8#nV E"("*b(#nb "&*I"hH"&&"#nf#nJ*jfb$*jf"*h#n)`(Id(bh" $(DB)RPPfXRBI`("XaEJUdA"j bX(#n&* #n"Y#n))#nb&B*H Z)j"ibHJ*f)I(Ia)H*h* !hjY` `R BV) R*"Jj"#n#nbBJ)"jRh*i%(I "`Q&R*&* hh! I(R *f B fh*h"I#n (#n(ZXHR*d)(#n*(#nh*Hbd *#n)**X#na"B!(%*b" jf*("BffJhb abB&(b"!"d("bIf*#nf&ib #nJ"(hf"e"*" "* h&H&ZH#n&#n"$J)!""(!(#nJH( *#n()*()"( b(Z)`& &H j*&"hh""((f"#n%bb)h* $""bARSICTDC4SDE4Q4Pı4% Q5QD$QE),ITKDAM)CQQQ)S0U4PI5E+30T %H0TK4TTK1A$I2%RDTTL$DTR0EQTQT0QTIQDTTMQK0Q,A0ҥ4DTQ!$MSPEM0SA!!EUQ,MQPEKMLTK%A@TERPSDIITDDLHDPI IL35QDE2MD-STSALL30E!Q,S@PQM0EMCE HR%$RARM,UQ-DL$)T0U0EDTLA4REDT-EEQA4M!,E@PS@čDDESD@DPM1U,S@14 QQ,HD4 1DIM$AUDHRTT1DQP4ML1E4T0STUA5QRCEPH)#X2PTL$MA%DLDAS5%RB 2EDIDP )04MTPAAELI ')Ȫ4ԬMD441PDEDEM(L 2PDA%LA1 CESKMDDPU$1%LTRQKRU-SATUE-U4LQDU$UM CIRDDDE-4ME%ML4LQR44MH0YV*j^8MDD 25(PJLQAAII#4LA#XAD4 $UHD L$QKCUI35AE@IAL4C#XPCDJCMI,AIAQ4T1EE$ṮEP$0A!T%EE DTD 4RUUJR$H@K K1UT1QRPU#Y RDPJD(@Hi)h(h&(iJ#nV* ifjJh`#n$҄²E3RULU1IEP%TEDTQS@PM%3L̥#YH#Y(55LDC4 EQSADI254S251IEḎU2TULM%T0ԴPCE15HDUE0AUL)1LRQ$T05DDMBEJPKQHDHQA4BQKE5UD43TM4 I1LC T%!4DQJЅ#n 4-S4BPRS4T%1$Q!)M,#YAUBJ D!@R  D%+%#B@$1,C$PQ-#X4Q@$M0 @0DHD5D LT45MKIL,RQ0TBPMU!$M#AED0IDP!H4-RQ#CADBBRPҔ%+CU0 U-U LIL4JU)3UAKE%%Dą1Q1MT QT%)4HЄE- JHPMQDQEDL+CU T M$2HL3@*1DHD,%T#3M-1$Q#XD, EBERE4EPUHLDM!UIE#YIUMDQTD%DS-T@SDDPİEPQ1P1IAAPPMTE#Y,Q$ 30HTQU1S EQBPUԤISDIPULMMU1#PI3EMPQTU#YKRQELBĴRQ45M DE#ACDA1I4 3UPЕUTIK5@A,ED LTUU4QKK0DM#YTM4@PMCJDR#YIHAKLRJ5PQQPSRPELRPI0THEPCBQSHE@%2QJLU%TE1PLSD3DD,EUAHA$1SLDT,H!$C,KC@MQDE1)12#DSPE5C0!$S@Q@D1RAUED%R5%#Y4#LԕEKLDK0#Y45HD#YUTM!AKUPPSIC,@AR!BQER#D@RSTC REEDBUP#YS3DUID35Q54! ELQ @IE%$U4DDD%4SJ@QB*HAU +K2QE IUC3@%5UU@DILAE 2 HQH5D)1@sf!)& hZHV*DhX"%("VFHh#n( i &ihHh(V $"&f*(J@#nH$&RhHjHBb#n)R@a"JiRTJZbP""!J(h"j&(&"f)I bF$J*@)#n`BedJB&h !J(Y #n#nHJi(j&JX"#Xd"@@ &@*#nI &)iiU#XF &("覚*#nZI$JVh(*"IhZU&AJi *abT$%d"(`#nJJBB`&f&Xjh"&"b)#n)Ba(J.PqrAS?!/T?7mDST?ITWvx}í3r,?kzI'T3[!Ltj#n}z#`|'4ז?mǩ{Bb#niTꢴZ㍔̈́C~&< AfhIOag:grgTrr#Y]?50q;)%\FwU_֫.9Nf4kRgГ,Ou}I< N>:`U-?˥Wʛ%1>to3@y#QLWU4 9-M3"T)N؊) &*ۗț$DGY- `2%P((kT'E?Ə[9k>uVYvmthJW*0nr#naf}zfұ@q$;|6bSNmn8ΰ7U`!85yϛh٘{wLkAf} >-9fz}F Ą*^9DELEl0P;8#YǸF9ӻF( [L|vCZumoGZҖ #rOuC2o(dC+&B(xu6C }!jowmP䆈^(X#nL4l_f.~AQ/J(0Ϋ鬳=!P4tK:sŇ=%gk'>#niB $ C{]:Ƞ̙ HC=*(;QC4U$D#nڦx'#X6RE=dPu4xse`8F֚;YLJw`[[G*>Ïgrz隅SL=&ҡ|Rpx]tA;P;ߏupd3T%(vhUR uJ^l>]KYE S$G[Z#nEi)$+CK;s˲9;6~5:`D98樷xXù1J30՜snRcuFo6yQ9nߛz\.T+˒cf-֢yǶ\-7;IMQaU%ʚmg_w6ikf_ g"G|Bs#YYF7˔<"D9t_l\hkqsbX<9ݓ,8ANoy>/bPAeݜoׄGPA(!ܷFPF]*">CT، nSq$:2K:3V>h]LX#Y·YсT(Bٖ+B7+]aXDa!2n\t%+u=KF^r_&!Ҕf[DNK#nzՂ@*4L<&۠qtWX*N+("|ز"0S.pR6pQ_3a'z4`#n?],xpw>@||!.QI#Y=P4w~{Gq)>y=bua#n};s:2^j"?#n951?֏O{:vζ+5_lFa7;Ao]zo"Բ/* /_,!de*P|ou.hF#n4TтMn,Teg2Ye+jc6k#nVt;D zMp#np,u?sDM&YTFJsRbљ](&&lKC,0î%/}|(_'ư~>vzmBFn*Ǭ#Y6 3놗M1(ʰP^{2_=ϓ&2I_n1h0 /xu?(d$yq8(+ Dݙe0;xFfuc6#YmUeа'!Y#Y.Z:dU`CBuBEJ| iaRQ֪fe7Ϙ8,De c90"2V$_ً0C#\yPů]~(xC{2zDgI'~ex}U/9`>%xHae5!8N=aB(ƤŽ$J4'L}+?Is/ yD$lߏ=N/O\l=k JA#n7*_{`ӷo>sC>J|rPod&8Nªpޕ<'EP3ɼ2lV1nS"-<SqբA~*4!3:Iϖk K8;BK9R ٓdF0X\.5МK*pF"tqbzp~g?K;\J$Bxx%G'?;oV/SU.Ιozrf!HS*cS#Y/ 9#n_^PçOyQinPB.D x=FfC5s_7|H>Ugi:ec6qQ32tJw$cv4$#n^׷,g- W3v[ci`XMbPV"zzrIoZYUTdu0u38̉YKy`&1m!#Yq|V#XAȡ2_tKz|}.-lyo{[P\.ƺŒĕT(8íM%[Xg,Xn#YG~m4+\YӦLˉܚѩ_)AqBP&=X7mH`x\w} {R2tb]L*ԦD`OYOƳ,n]'#YdvԐ݋=/ۦl,ȵ)gokn^\$HÜ޾{7rr_\"ȡ HT$͏io_:[ܦа@\Ҿ%XG\,Ngu=qp3FV#Xp'XoMH.|08)ʥKzAՠ h a?ߨ{ߗ:2;E7ˆ/#nPzMޒow 8y.#Y|z5vN”|*Wg5~~`ʝ!]ʰ2#Yج">dXvoBY똎ޜe;a,n"&i#Y{!lZ2GTU#X$D( 0¡9]UQŝ[q6QuquH1~#XSh1Tfvw8GCY ,Y^ONȽϬFQOiӬ'jxmpAC!N\7a@CzKF H$*-.jL2"Ky?D_愿y"EH6X>H6~sO=IaFREFPxGlc(-Yo'{2#XwR3#XْcaX)l-zFi}8vKݾ "ld`)xS$b$ k_Zl2ɔsI<9p +UNV\ 4^(+΅e\]A#Xo}ly s;2%&URL} \QGunR®e/̸d.heE"Htۚx i7a|PxGFf஡F|D{(g@5#n+[C, oГID'H:(2 "#n蒑QOcP֨$(%0vO'}0wNMКzMY2#X;WNr9n m>mX$c}RAIl$ϥvd2zlH+E#n7T*Z&ƫ$I&p}:& Ż@J{EJXYXǽhV,0'maKm\:}ڕeno'|yv8߇&}ClvMϊFc7FNp.YCE;䀖KKթOڍsѢ_6&hOOdX U~|U^$BcX퐀,A:bkgd8MjB'>6>8dwU;R248dUĞ2>w?/~ͼjOWM aICذi8QMIP$DiiR+y/Rjbnsq%~`u#X5|ɖkTkySJk+0μ(:kWypx"]TV!MSr5i!~W_Bxsog&61\vAX?cKBq܏ fd2sE%rR!AïJAWTX/n{)e:wsofF2.7FN4(@7>7B]P;5D#Yqkt®W榲t~623Fqۊ8b* THBpt/#nt]:ϙ9mz0!cQÜiP8ko ;:yõp7![qryxrEwPL++E[̯ ;2~Gh;,7V#YՓI%JI痮6[v~"`9=P B(am0C#YZ[j1O‰)"6puhrSarŹVSUngO0(D|j>F5ax0q;q#66d2E?YnOJε+4yC#Yh3OL:gLє\ s#n!d:;#nB`Po)ɸf@;%%0ӠdU#NBLk%V#9ؠDB^haYN'$r|S%h0T$Կ%99цf|SQ6FPSCĶUiɆd{S6:öW/Vpg_r8IQq6yeDP"`p\̬@(xh?bndpeN|Mj/%{NsX+qTHmTK0"DJ宕P0Ņ/}aTdq i6 #mvޞ36QvˈO`2Lto&Eđl)F(;>R}K33h6,PgEK7o)›M+G ;\bxv˷Bnf;W'(GG,ɮ&k|e1bCh1R*f"$'Tme% 4qY9wM:C:(3(ɵmCשU~g'N?}bTȘi o/SF܊2*Ņj5wG*BC(bFqaj/_m®S7\VJ6ֻШϠ:vl?̇f #EVpii͹d德w>׺,PT}Hb2_F0/Ay~ 2iwnPE#nKF6,#X~X0q43PU=pf[R,G=*̀QS#Y%$b.PH0;OHkGX4E4diͫ׭5Zs{oDcA.K(.Z\>jP_^~8Ebz*`Q9M椺_WP^MA#XI;{e%` '[kUMDz (0tW#nvgaC5}-9v_H:uDLJ#02}҈j/ێrW: P#-S#so w>kqL$9vf2t[!kbaoш|`~99zwi|2qyw?9M'qXʑJgJjwF#Y/Jg%Av: J[) G3Ӓ#nxPXpLH#ngeqr>ټ1ޅ86#Y$jK}Uq59;b+neUZp]:˜K>mTBԨ#n?cs߈.jܷ2#YMr,:haӱm0>h^Y6uy RCr}8{qB݀h+>j@%-;AHf7!G嬘 Mn*#n2!DlX2T[4~m+C燉<y vFM?#hzhuq,ɿpL#X9Om Qd`hgP$c`;{gŋ !9~q?Oz%ד)<(ŌA` #n|O傹 ѓ|(x%"v!H30ŧ֋{{]~C֞IɧչAhʃ m,XX,/:~ƿC(=#C(y:^ϧhj$@9 Z8cs]Ai6hxQx Glh((BK#n=,2hr9}^L4m2@ERrB-261SO@h6 cHVvs(\hk.Z!2zXBùdq-Ddør("c DATq ny?On/->%9m-#n55JIk]iȤtsZ-[,V#Y4۹_o(nqmi|#Y>UHS`rB `n,dʨ#j1ݺûF62ab;u*GHV #n#YI Hz*:K#XHHqs*V&vn3Q~/e&[umXyHv{h4yfk,[Y B*9T2UI*{/GOAplNQQ#XM#X~xuU(~+2$!NtEOR_R$1oǻ1XQ~6"锯LF:A{}#Y1?4A[]߆7?'Bjq|l{lϷCG'nRhj}S}p3L] a鸂J^{lv^X1O6#n9O?* u#n$}#493_>B՟q zPOsSa85)-#VR_ #{c>.<Oz`'ah#YC?7ģ%#nS:0ap%A 1#X#qNrJq)2q% *<`aF΍Y#n~Kl]/1#XbҨ35ҽsǓs\^jO6`]#Y9.:8Hg}#vYx#XuoIsa͕r,"h?X#Ye(#nZeHV:"a#LeJ|ܖj}6ut7aXvd%jL'U-coOwb? >}_2,fq rHmGuLeQg$V} WhQުgAzIC"@ ~ {5?aӦԑ6(>ͻNb^-\pWD`kTII,J`=ّ\g[ێpii*9I C)a!$HyH`u#Y'DДBQ!9R!u۴ATJV*#nd#Y`)5Nwf=h@s)X̫E\pA9ijPݹfK>#Y[^JĿa(XmpyGNi#Y8$Cs~%,2˃Ì%$l#X8卓<7|#YL^#_Dž3EdnNz >Ğ;g tf Hǯcf`TVN:C"Ph60:]0X(m6)ɭ'VnɩS:)(ly4$y`#tl"A]o6?~5[#Y4ch>/F1b0<#Y İ*mw&ҵR z%"b$X٩V!7tfݖ(G:80v,iފH3bEiQpOH46D"ΛFYEY@)2:iu5jQvaBdvgjg6tu1{f#noG#YD##YRA1\D&j;4!5i2蜱#n87LR !! p`HDǫ%z#YwXݲy¾N\<8Ҭƻ2rΔ/?AQ9'W_0t^p`.u"5 * tahV(Ɏiu\o^Dǥh҄q!d BE^ x_9#Xl?-O=:Xxv0,iv+r܎3U<8/hln{ׇ>Bi.KԡjȵCt\XD,TmrqI<nlZ\#nQ#Y0vND퍠ݵZ0ŧj#綃 GUEA"ES(p^lfTsMc3#*U0<%$YPUϟ#XQFAT&JwoQ{H~#X~>\􍞞o7֋w|ZOjkf*r^Ӿ(7M~a6ZyS&;t.Trj<=.>p-#YSvFɈv/S`5=?*jLI%XA)H*.Knf.[F ?Zt*6@?6>jžҟ4aD(85]m~@o~8I 0wѫ?Ll~QPt>#nv1osؕX .Jh>$ɱɅ/ğ&>=#XDz8S*{@З4j,ӯ0xm)ǘ{J#Y: :N[hx+!#XNL<'HdALcBvŴ]F`G@nK"4ZԘ~b=eezܠ![s"nOvIe݀h_ɿI mV5ԃh}](mcI6Kd?õ#X?%IH U#n*#Y) H. <$p9.^U 8Tbq2,d#YkU}auF*&7hW,FP#X>RD[->h?{fYF#nF+2~x]Bd~Y~wZe,sł$:ں;YC.0rĘQCͩJB]17u@(P ѡWc>wR!hH7#n]$'qi~vO#X1H3ƈ`zuA1)+/4ijp3|8) @WP s 76Ō xK9;#Yo+VY-GNtvsKN\20ΰ3MTLoTkwPPIΕ7"/) ' <6p~4GT$dmf]!% o4poʛEsv=BM-}L}lhaD'V=3ׯN3W#YّJj ^G1)0F23U1#Y8on_aG_ Lrͳ򾣩c{^$$ r7܄^c93'wE  2_Me˱= ,a(mf˦+et<3Xj3XkMkci$C5:n7ê¢go1ַ˔jؕ'x,1\>M\c֊"Io7̻ͷ&3p̎,* p/5=^%d%#YeQ=#nol̢;X޴Aj$CK3lG^J2#YA*m9#Y>>0@FR[\0s}]嬎qڶFZ(xlOiU뿣#wyw)> (#X T[\gFB]ꘀR)f5EY35#e`\`k8ˈC8 Ʀ@sTC{ᅩӛ5._]iں*xCd13:iR3 gU.! ;vF86e<.$ cb;;-הnq (1𐔧J#YtWLW~M:<3B2ojmE_L`l)(N<}h\6**5=K}ԫ9η9 tθ}u$E&F8c a#Xi|e'GK.yɜ{U|H0f/O;2ZvUFM#nF+}3R0K($Xx$ &#Y~Qf~ʩ+~[fZ;,$&\dN'diƑ<'1O9~*0x;2Z1vdku߇--9̩;KAA7'iyDa4k.Rfq~}ax!Te.Q"QbvJoe{&1bxʠ]JĊ模t^圄fv4lӘJ2ن_Ӑ.ϋah @r H%05.tSeevGp{#X.ey,Q E_}:BTLSN3]*nH-uq&h Гq`G pk"fec!sNMLJk"Za٣xP"^?I0^ExY~zBy_|G=3O_|I\hA72'l0<#n%mD`Ju%#nQ=y(|2E5t۴~Fq'QԵ끸&cH,3m`/ {`Kwj:.>Hq[ŇJ#XCqUsUC}Og=~f6Q-LHl?!N}5aASBs'#X3Xd#YNO]ut)1Qw6.j>c/H.Em(Y(΂ tUyzEaL ML^of'9GFh5[,PE7'Wo;DcUFUaAώ!!b(( Fk9ʖ#X}5#Y , .ڀ&)9}m{\r<0HLQ?}Gl5 ,ө֡hfF2%@P78!N|,]ȅʒf&xJ"On;oW]7;0 f>(pwR0 ~/ka<,0 Y5"'b$Q [s$}>*F#!^Z& Gu=#n+(I;?i #X. )ΉF PnɍESZ|6/G)tg^*U#Y-ozŽ _H  $rbZ@(^?wW?ZN>Hn[vot89ޢ_F( ,xDfMj!RD/wH#n.#Xbn'A3)vA0>c=㐽ÊHu#YYkw&b{誘#QV+ eLILY\zCpb{C2#`Ez/#YJ#X / 7/;]gHR(Z(*Jq%z'/@9r2o^fDP~,#Y=#X*LwJH"$Һ:Ă^c.ϡ1-+HiPϋ󿐉WӟqfTn]L3#Ym #XMx1h lLE|}_6k19!g$-tHZ~",Q__&}6cJ0B#X';}~?ֵ_|tf?#XG\#XāDڋAQ#0`%C1';UMtb&l]m~_TRsF[RG x_#X}BW=2WN_ߧA"S$\c32(9As^k9EPBilm<9h#X8~#n";DN􈢘(y|"FbļM[Y4'uL>;,HiTQ"~#X|#XT1h7SU QqI/&B#+e0:- ꩘A~>n)G1ҍFnJQJs @LnqÆu" 4&pͱ^A "f.RA r^ʟcj"]k#Ywiͳ\m9#XB'O Dng@@Mh(&vj;>Y;sb^nqo%l4Ɔ̱"0PrI"7S|s8G?S=_u3@vz Ե2r1,Hރ~~/`+4.HZo.pse92ܷM+<$zI*Ѷa rє*]r^a2~^*Ҽľ>Xh5}uXu;"QJy&r<!N`{s>Gh |g4lGT`@tX6n;4$Q܎tҫyU&-o]*#nFqUJ9^WMqShkBo g)\B#s5R&9V#Y[58U,䦔fRS;8[ Fljm6%G D޲fjssNqTT5h!S,tAΡ𕹭P>k0w  ݷЩUǗW#nܣ9>vPp<<#XlO;SSN1w׌EۀzyX}c/.R\oHmd?qNub\}31N*Qo\#-Ǯ>'rk)pL'ξ]{xy/. X!C u"G1>|ÐЎ_'f2\/#X7O(_,ЇͻA0.BZ[ޘs ۇ&E#qvGY\jNzp10D#ne: |鍥P_l[CO ճCbb7(?GΡ>|ec?a{~LƱ֨Ҭ]UTWY5ui:mMc#Yt@?BtcBD JrL+6fLF:ϝ<#nMzhM$~SםP"*oƟǖiӤ#n>az:-N:'nrDzy]?L! _}#s=ɛ  R/("i3(Sg6 ~S4Ӌ<NX07&qYgrs~VOb#XQ#X9n#IPngy~qz>3%1C!O7Px&zZ\V]MK28ӯOP3{b…s#ĩl0vLhm2<4<=i[VBF8jiֵV*k5k$ŸpMf}K ri˟Z*MRF=٫&$Na|> QFAh >X~Qk0=aǢ?gt#Xq#Yߵf7梥-Lߢ<χ@*+HKMbz$:C`r f¢vfZxqI֞KМFĜ/bhDAL2:/w]d$EOBUwf'Y9 wWQjRșa#yИmAcx.2#Y21(MbWy#X#YH*$^\fB#XLuzA{xLhޫ}b22i0PU؄OE56k51oϧ-OxcFonE vgl  aFOܴx̚,֜Lr#XmrjF$)%#n[z#nf}:9Hi3tQ]? ZD.:;<hx#ng >˖X*HzUtj^#ܰtyFsIZufo*Yv#nI.`Z.2$Cڿrp>PCߠLm}F6RB\z [;=,zU1|_ۊ"FK T-.A:YvkVO7SJ.]NϐyT/0te'2oEtz)?i!2;K${eh+LY59 T;2+Q]҅Ba[ࠇPX% N :}8O>l#pU*:L}exS#YVĴesc2o:q#nJK JuV+gc"2m晨 )A: JLxrɾSF@vi>GAvD >C yC^xK#Ysf$`, :0kU0QĝB/)f:`0D!k;0RNgI73 i͸E)l#Y$C-_ .1@%pD oJ=);JݷC%R ),n#wN!/5*Ya& cMA󇏅G}j>3_i&ewT@bUQ~?kco H0ތU@wE0=v$IT8]˲QHF'=PgѕHq^T/:3|[JRgl@Zucc.cܿzܢu6q%gYگ|b#XٍaQYD)5;pyka0<t`+ƍ?Z9qGoVkYپR\۫diI(Ty4lp7#n 'ݴ/x0~܌}x]!|up\hdV:h"6QdQŷ(gePYǓT=}{`F.Qa{\h+Q8X>9Ks$.&LDOs\6cһez:r#Yna3$4$Y80;JkFY P4#YΗ_?E.AzKygF#nCV%!rx4Eh"%yx)yAu-~2u&t닯9g޴7n80Z!"%c1kXHV#Y~ Fnٍ΄1N(M{Lc<@s*]RȌ/UUUs^]_IWw1 #YB% rT^P.\f0<6ǚF@I-D* bu2BҠ#nI8n1IDů=#X04okEƚs)S{B1pB#nγTU_oeԝiJ$!r7Zl9P-T#X!n'69m#YT!ժ7rʘ}N`,*rAskp5iZ#AA3!,I淔~s 횋#n5UsaQeEwJ#XUe.f2XYW "b퉽ݶ†G5HFJ?)ۍoqSJ2b6eA+jvzCN ދ= -H'&J-V* P{<a!o) 1JHJ [XgH~#=h#q>T@8mL)\#Y:櫃^a|#X=p`'QBwOu;'3#Y:VD^JBE(#YvA-FT%RAFlV'd]"'XUVLW<﷝{]բ䞤;qAh!}]֫Y{sB,P2e$Z)+ xY8u>?b֫BxO PKoЪgg5F<2 v'('EpHⶱU1K`/[x!k #Y`*M-&QM`u"LU@H׶y+7(8#X{Mf-#X` t>{lP0V_W "'(l1Ԗz$(s-ٖQ*e }C.u ְ"?+j!H(έU"]1Q)n#Y@#R|B<n=FVbkg#n"H'sPm(DGzNEKê u'"^Bʣ.7nT4ԁU8WuSjR,A-=uP$"S8j0g#w#Y6^<0Ro:I̖N)+|_4r?^f 㗕OtsB#Y8.EZ8NFGMcYRt+f%<|97 TA='] HKesB'7}>qfEvץ>_-䀡Tq-fAIN|_G/X<{wÓ{ 9SGnw]BkF80mE+aC'Ob{duk*gG Ź\WRj7PsQM#YXiip=ӝŚrj#Y A#YHf ^x}81s#Y={_,2yF3uģ'+qPcx  C` d/j;8#a{`}nql-#Yy>)xx+`e2Ԕ@MYc:9>5BVߊJ־cig;0WIЙ!$(~}qWʝPzRI`2)h/}j{Jm0 ) xN|.8["ݮz96t#Gb+u.BϤz(vY6YE5;#YMg hp`E'?r-%Ta k#7=[})|}(o{; .gӒ #Yy)j<#YA ʂ`5&;"bʬ#n0v)Ncg1xS6'_>ۖbueQd"ݯ0ok1y?fJϺSNXP0EՀGo =VX0YNk lq7Ѱ/ӦhOu3Qp y 5O.رMBHKyt<4#@e#Y.#n̨IMi:8(F({1i ڀۇ~rCBeWN=BuSҴa,'أjl"~$#r$ Nc2aYm#YES5#nL_tT׫o7Ή¯<&FV9azbeYJNǹ0]0y9EΞ#na۲ШJ:":r9V#7x9yu3a|=#yM/UjW18d0ˢZ2;(UU5a']fKTyvp0e(ݹGN7:D@^.eTl 3qq0m]MȇϚu/J Raљ[}HnL?muF09`8x*Bp3Gdk82?io 8!xT,-uc jp@5 X%A&WׂO,%p5tܿ#nBXJzYaiQw(y6j/m#Xb )#XCXވGW@YTT[FV>||e\Lz}feWѢ*Iԃo^(ɖGЅ¨F*4SgGiԼDw#Y@{x 27 G]f.wFWN#nF>KRETP&tM3:sw9^^ocF8$@mCg< [wY$w#YC.ԏ#JdD7(iA#Y PKQltk넔g4&eCy晜iC)(J^lӔ-#XB1eI, ]맿663\7y/,01݅1i)"ϴ&Bقp3;Lώ%>#YQgLn5"=ݷ`t3g;g$LZ#U9l7>4\BUvɝ"-BB3ӻ8|erΡ\4p5bQoQcSɴf06!7(FO'}~}#K*[%gC4-mXfZ2'QAi9[Ń2\ ZA69 U*v~㡰Ё!to:-I&xCٜLCKryU16!9vdոbh .لǥ-9n]VF6LpL´˓c9w#Y&pw9*dm7. є2 H&# HuʙrҟΌwM'/Geٲsݘ^"aջzA^:~УǿZΆ8yGZ,X`]HL#XigqdN@rR6'~WwCʓHh:wAFDn [i]hV#c,֭#+Wqtm8x;JOyKQR#KnAsL@]m4ar<åP&GWM&Kl#XoJј$1CiLs) Jr޴<P-GɄL|wiô,+1IvX=z;W_UU]]#Y(vj@FPTKM3 鷣@/Imr>EK*iMB]Y %VX1:ܤw]IXr[s|0z/Lwy |PQ;:<0h'&sBIv),#GA?-\۵"7q1b"1#XtV0@D?pYn8BCq7p1S{#nΫg #Y#n-^12D{(b )TNx -X>\% n_xZz0$#Y~O~FƟ#n#nf hݜ7pjpc02(Ў?H#X .ۄL2V5RLǧEQ9Pdz-ⶠW&~sݏO3Ca@yԗe)B}]"ؾq[S|#nۢP[ʸK# Íc-(~798>mm&&;=qKoa*)z0G&9L L [Ag>DOwpI.3.@k B(k_R^kULR?hvB-:ǾpGE=@g>CU)C,?ݛ[LerSUE7,m-|,t3| vBU#XVTx;:-;?9e}-53;z}x -xd _cp:F* d:1AP{#X7[Jڵ/T9i4}aaI|b^;qMs4O7! iL%ɒt`pl~׌qFlqT`+{QV`wN">YofSe'~ZF8|=_I#n1Gn~RzrBMg*^IH]#n1B8w3= "qܪ[{{ qB艊<;:#Y(M+oextLw#Y y;V-̧ѧ8ehzE*XX,1nҲ&,C8"+6e5fq D]lXLlc6nX{Nb9eiвpELj#t^YuTQHT麧6aÚjMĵ{`^0Ⴥ돔bV5[/=s(>uׂ#nx-xIЦ}TLңԹ4U#XDJ~,3 dӾ G}p!ziĴpoΔOx0)_M_}H[vt}1=C42wuMͿ!.A~-`-g7Ǒn'#XqOi:'z|5S~>ν U-/k2fJH%$8xq G"~C5.>g K{5~ߩO8jl8@#Y2o80OinG<Fvx2n4C7F.d#X`M1'\85"]*;M0/`x$vDF*32%6q77<\s)#YyYt:P6&uy.I^zv&{D+k[OQťeQ:}ϝB(O#nFWlJ('/imK#Y,$D*񅶔 TV)7#n#YqQ)#!Kz+wej8,JqwkOz?SFz#m,#YCFhVckYMo႒0陽by)gK96Us:x5((hp/{ahS5"@0"[ 3a r$5y2wC`a sPj eFo #EIIiC(ǔKcJcqBbw@#Xɔ.6䟣#7> 47{3y|gBK{ތ#v{Q-\jԩpi)8D}RNd+ fS>(TbVn vZͤU+l#Y-w*)a\w`0(8HwidTsGQv7?V!71Wibi 7ÚY51*qȆ n?)8%Fvl5 ߘ彅~Bsp9O#n>ɿ~|(ri5Vt@c_7xK!Qrv,;c1iS/xu 8;u 0ߊ L%948P\Qߪ⒈nAiVUX-ջNXe'8#XbLHˏ&_qh3%#ysFBŠ؟8w;U.Ptr߇ ,w5PRR#G9&Vo.։q-pc201l6xn?#YN"5xxp@p)Fqϟo^j#Y/nd>7oeG|1_k|pݒHAB:}Z&Tc1 AIKM)q۱VTN?DV#Y9-&2\AdվKp)qL_ؘ0(7R2~P%٧T]i]׫;-TXݖK !6Z'4ULP%(4{`-ZT%%VH]A#Y$y**O;``Q6KX^_T\Z72#Yœ,ePZ#na,RjȠ#XelPVt0#n$ޒx7XN4WDs.]>n$=6)؁!z<@d#n&ޒՄ6NHs_#nQVb!D*z0л#n ]Ô*6Q̰fYܢkQvJy7",,MYJLG@`r$&bq<^Ng y|4Lb9np.P#Y@ 1W_jo-M_ǐy=clS_ByA)\iL6Oy\~pQV軩V4`po ,;o@f+aIψf;A<|is$s\ 6*#X-NA>T߫~ MQIR|PYnvNO)3/^FHNjjJ%'J?=ԨQ=gwNgoNͯV`fG& Qx,wL9chfC#nn*FU(;ch[{w~gW$!F3dGh} ڻN+cATȝ3t}Ld6"oɹSu$j.`F6@m6MCb/#X#XYdd R00LJ#nEyJ\4D]%@#XaY᫼JQc48u۟х05s? = 9i`š8$SQ ޲#D2=4toxg钎kXZ|b7]}LW7y"b-d˕0qCƺ!ai|}ZkhK9ԙ1/[un/#X$Zk,D?%b[sĘNZBq#nZ&]/Nx;#L2uMUJr(: }V3viS90'EqY臾cHܪ%~V`zuY)q5Z֧8z ͹w q9_-mqҡnRsdAx \1Z+yi_q|n؞G7<kD,0qWkIA]Epf>19GrtCܧ'HBZZj-K'+WJrSzf55 꺇̜#.t;k7#({#~o9Mm'Ax|ӌu\G3diu/NY#0sػiY9(7x=ດLM˝~eL*UZb.Nq#nSՍu^@>7ʹ9v?꣕9ߜZ5VqYU¬aBGnۜ}0Y ( gF:EM$h*/eZ] !l/YZ ^,W+NpW-)ӭ "R1gqqLF1#Yk|uzPs~2t>#Yx٥x9>ߕی̈֯&1hHt0zZm•T?ҟdLl|6PaRnr\F/)\9=eai4ZHgԘΑJ6uW^ۇ5qo3{˧#9ŒbG͏懏ɬ^=oF<ׇ'.yC^&0a9YF> 1P#nF*F4+ž];w)uG?(i!tE7tF"0xHByXҾ?3)4l.$9CM/"Ui@CZ#n^$1Rk`JNBvB$#k%״^@H5ܘi1H 8wf+~kj1,A#YL#nH&!ĝqQ\,4BF *8tQe.S,ي3hJu7+z%gK.1')hD@1ߍ/_>汏O8 0epIcue3Xd#nY(BS,Xa#YImq$[/s'# /)V.{]ڭҠTkMD<ͰC\3)+fNA^ruD sB;dLq.ML ߍ%o,IJ[K'&9ud=e#XnpPr]\,4 g_E#Y'3Q\.2'v¿j?f|&>/Cx7Gڭ༚^vhyhg 9i;UYm|'6 ZTH"\m&f!x]zVMC I `#X}b#(r4& xl?#Yo{Uű板ÇZ5:. AR]D@."1$?( ݩ,#XWba%*Nwt%4Ofy;Rh՘_v(_^|zK5EqN\o3g |k7[YO1EF V|5"HU|#Y]csCx7뻩C{OT7JG{.#Yf;WuM#n?$yW<97o`:Lg4}H]ױG 6YXg)C4DE&RWܛbAL$-Oanf] f@NnWtT%3H/ϖIHs߷_qRF s_2  7x[e'q{V9 &d6sR+XwRoas^\bXJ@2qu}g[X`#n:$Ta#Ys{/BiF2ei+ILRpSO?f8,k38ZMJ4lqhȒ50xV6ZqF] vEI#fqW#n5!O}2kB+("~wně zeBiS-39'NX/Ҕ#Y!4ZF&AMq?N(a#nH?q) 6s9d@ȵh4 na㋽<1 6SїلH-Uso}W Qpx-d44kE\*辑-Q9>(/MM#n?Xb128ڕYtV*˴iR*R;hmz`ܵAAZo&cfX#ۘVriHF9Z[ԇ-X1vVlrⅻD4I#nї*#Y(׫G>ܨNO\Oqaŵ_TCu+z'=;8F?F 3H|bk˧oN;@qƭڵc=)#X#|( uB#}G_^,_:@eNZ"}xXB%"J"hR4 Ȃe$`=|cqp4;3<˰1AϦu{#Y^vi{T$6I=I#X퐊|XU9wnOaM5Rt@%2QtKGߧGpQn(E AJU#Tb"ć!/ T<|#Fs|;<+XU،NANZ!;z|5(߸FR:*ْB1sKSp#nu}Ɵ еQ&K3~%/R%XvQeqq]N!1P!S#F1oX5܎y,-=! 0b] Z ņ>AU?UqxH8/1%tv"'Vp%f@ƛ:VJ>]LT,9sPG5C,#>]?[w#oӯV?֙k=;A7-kь`.*viQ֕$syM=_Pe8{?~]6UN{4>1HP,7Etxv1qgN‰ 7k"o`ݺj9Svſ#kƟO';!3n*O#B#Yc9mUHgPQPB[c?/20"Zڏ>S{xyaJ>}i4a"[[$5uż{:z]A"0)BK(#X\0Z#X}ΐ6rTР(!H@U}#Yl4 x2w@t/x_<-HDԧ}:Jx4@!׭?H0/`#X]tz{mQX7_:v"No\g25S‚꟟r%--#nڵHXePMzP JQ/"XmNˡv(0xoWY/xoA|&B`_6*ĀRa޻5pi?`dS>6MQe|_NǁN##Jq_KIdRT9>#Y'Yܳ=P }H2Obw'u'4Jm̆+J4՗#X)0 胣hrOQχ8z~/Ug~Ξhm0_Wn;fM>wRhkߍ0.Q> w4M/Lf;zJ=1":kWg5>ve/:zGH HS^ru3:9Yash !0jB"[&MjJMI#dvNdF#Xݡ̆Bx,N MG+E TD4U@A5Tb F,W~gG'yI?pd:=,?ށ9)IJG?͜Il_&"P=w'yG;:Y$}O;#nN#XP jW0;~vOw\5oĚ\vO 4װ}~mk|yyy~X/u×>CG3r#ْ*S^װxO-$_?|[OiRó}X#n"F|qQt-{qq}J+B_HOTߩ[^=Ow+aˆyڋXퟟQ˜y>nݗѬ|yaxj>DQJ#՗ɖ'7u'\8wm:mdYSd*@~iD>]v>]ث0’(\4w݂omqy{:ȇ~:#Y)D0:DzS?6~M:4Qޜ~j.-]OFݹa)~L]ϧ~|&Rr:#Yݟo|q/-8ѮPO!/F}6\kvp9, ]ƣpǗ,Z0o&l1o.Z?wmכ~>kFӴy䨰v;ۏѶ#X^/Kt`z9]}Ki!dZO1.b#2͂S7])l5[.0Ɂwwv{&HI[ }C]0wp)ց̅q._vf͙{ݿ_bTn}_tk!t7x?Zizu8 q??3H~?u>#Oi|rHWzna:Ud/WH`a40= <8[DPxR>B}~A]PZ@mc͌o8,_Mkm~άD|_S#nxW~X3|x1tHhWCO&%^|>|#Y?=f NaGll*;^0n BsauWlGo5Ѷw2cg#Yp3=7长Z<߷K)ՄJm,!~m'?w7>L3B4f))#Y4w0B8?#ݳ!/?Us/h[׵g>#'e;dR'p϶fZ<>D(hmx`AQ-ӈA?]z>|?#Y{n#Ytf"4=.~̻"/l2<z.bQѳ GP8TFOG~O@##nHO7.~Њ#Xp$?mB|I?mȗ'X|rS}Gy=+2_h|7~F{N۱Ͻ)뚝#XK5D1R}nnyg!Yj1aaa`L ut(2=cq s +p#XNRbaTʋݳ6G 8#YwffgxLvsjߢiSp])qW~ 3E- "pslKoNq>HGH{_[P/XEM3©7oT׭\i\t>ah-3?G<S]" a\ciHz9=z1w2]?c|:śp/@6ɿaL,n:%*ShosE=.^HR -*Y8a8ݭoRu`|7 oPFѧ'r(ܩ8 1;jUξ]3l>c2BSgjk0=#X0翄*:xGY9GBAI$usp5 V\a>ƈm5:x0^YGDGJΰy9MX͹yn_y>>ȍ٪s5nܧjxB݈}xK#nRze!MuCi0aA +ğa[C#|Ox9f8awvNp8Ps޸wIM;U#nb6󟟣o\F<|s=a)V]g{A`o=*RØ\@[|&ݒ^/֓ HN*9"ϙ|cA}eUd:s16h؜w<6 D\ٖ(bITє&#ni1\9p1΀8.VҘ*$I8Xb64Lij<uqޞt 'h#Y1E#_HWM,l|F.-r?"%U QjV VBçC.}Y t&?!"iJ{2]~c'( ӅE^7$N@AHI0eq/`#X4BC@Fp4@( (A#XL}zw5q5t^{>ח'ָ)Id >9n8wq_ʽxKW0x~q'VH-!EԧDJ6&UTZD*kGЎDU%t+hchPh;#n8qWidADPbw3D'tSHչ׉Ew̜2bKFޖ!!\,`ʮ3#nmbƆWY ,w1Zƈj38D#n>lYF&g[d LN,C("\>6J8pQfI!s7B8g#T^q0$C٣]n׎~>vs3;87/O/7Ňe8c%;rQ;ڽG.JRp* #n! pȰnҐqdqX\.D/z=EE '2:L]QͶ%C"=~\y`x2#nֽC0{#YݳsK^u焁keN@^HOA5:0pQ#͹?G7\ԏ$Gng6!珤qI%I*m`$8u'0J'9&s?z1vm_ѹGa-oDZ_L[rÅxjg?W-1P?\llj@j6Aa6J6s݌W7ѸGk!HDEz^'\Μ7bbIMØmp5 jSũ*#nA$4-K7?_Gvb4$^a?,M8#-Sq"n:@ghKtM !5pNruSp5Z= *LnEȼJ=}=E5$C٦N } OSz{b4RU6 VS:yzu^\#nRF9*qr\^+탱N)Jq '^iH>C djvﲠ}VQCj"KAL3m z6p#Eh6#FqƠ4BZecBXkFA64DQh|V1vpmB2&Ru'G?^b{)O`"쵵-mwX`IdƅU#X1빂l&Bf#D9atb#Y'0=i:<؂]LIEݶ1'o|lfuxXIN#n=(,iK48#Vy"60@s0TAS\EhMjcY6TcN>K6 PmZ<>fc;;7l Tl0+&(zrpqڎ-m;j-۳ELT~LSɶX2DfQ9HP֢#Ybbj2S?:#nʱΉ[;85آ =U’;rV؍d6lr11'?YW'P#Y)QGNCv҇pO-5K1c )\cGa1#nTzь:~&EױywJ=R$?_O@!!E߅1:&%1o{ϑ{9犍w5֦H1J0Ce`FDp3ӷnOҙYF,34Fsgbw'wa̰[m0a1c'ݎ#&d>m̅v&h}XqrDlCMQj)Igf8Fnd,*]=fc$܍hG0.nlv1*8<s(:h&h׿vYaO9"D#q4 aẹIPviAbȓBzjuz8?m a88 99@eONSHmXqkY6 fsFC†V,@MD^#Y鱶3u9!F#nhD7}4RJZoT$΢"ʜCs9m'N7A") kbcON(c&vv̥jՅrhr 61paYMx{[l3(iG%475n [#Y{uӝiX.>ZƼ8g/~tIeɇEE=ݡ5*\J"N';Fx$grQ{-Sgy참sb&fyr\.>Y]3n?=gprtVy]U,RYTfnD%51'L/Q+V3C!fBWFM4:1\kF bӅ/cgm;KKC|N-cNb&lN`LLF)1 )3;Q-_D!QB{ob,c ˬ׈,Q1iuT1,k=шwroҾr֏ub}*̣{hMgO8Wtm݄ljʼnz /_5t2o ͠MQᾪΪXجA\Zda#Y4RٙTz%e%!.l 읩TRWpZN: Lz{/xmj4HnȤJy[) we7l(Qbgp0#.'lF*A dV#Y43m!5vڝb^Q  $^p jޑe]ޝ7#AO`yX TSto0~JAf[/W҄Wʣr~G>ep=p#D#n,{E#Y6"q@R _2&a3HYc_*Ǹeb(OhW@Az#Xj 1pT7u홚Qo݉LϓZēԹQ#nXG`V,~V4Æ򈤣TZ%"H;+g둨;qѥȑ'z#Yvhx1b|jfc*dH&>Ia[jpꔜ5|dV7G6v-rs8dZtmC烆ʚljd]fJNvzܴ7oLfYU=d+Y|DiSKC`EdzA+&TՕb~n-Cfu8x{CRuH ֆ2sD$%L0y&rd(t"#nh0@JzT|S[@ei}<+${#t'w3q,og o'x;b;' ҠIv! S]٣gCK>fξ]q)/Y琦_41kȒ4[X1Ƙ8[0|mii;ۜꖹ>s4Z;b#YeAk}y>ϫ+LJ|2vNv6I|F3qϧ$a(*\ * A}c/e7P>2H",oKI٪P7CtzԽ,ԅcZ>ߢzgGKLP?jluI6fv4[Ki{(!;/"=PøpOv9B$M#nb L#O"]S~6|M;q#7$nq}Sύ+j^ɡL#9֠#ΈNcfFIo@(Uz5,!hp\&q71nNN212Bn1B&'Ԗ[OTUc[.$:<(vXl]#otHǜDDei0Z0y9>~ٿtpB]#;,a0*/ᑂk#Yb)D2G+j9k918aABNɡE5ߔY@UǨ%| Ai ;/Ey*jC^2,ީK!(Qאee=$Qa7v7':QgM0?: ?XU^Kbwc&\Yi7{jB [PɖUQ'#X嵳ƶ*eE+sRmTN5wa<$f!κ]8E˾yH˕]!,<`CĶgR}QMJ57e ;KOQ3#\Af2|\c9t4៏=w8]#XFmeAU"IР\죿O M׾-^#Mأ1H֟}Qhw/.O#Ym&{- >p/fe<i#Y2-i#YҎQjG]ԷeÜ>˻E#hP;a-w z~5#44pCÔK|x(4䤰oR7_ʣOMБNrFk˜&d ?5@žX^^"Z_ďdlx(SǩݳËgSA/a=5>KdG:ƳXAX36u+g4Y.4 ZCӋL{)e?fn=.g8M:mSbMo:ۛѿtx:FZD%0VlWx<ӽHiaiᝄ*2l&A4˫RE ܆K24f^@&SkB4rsn:n4(J&almj,;E ;0GJ!dbh[\ƽg[e1c9ٮj4jȸ}Y52}z{'^/w^MF02؟n>0 |bJIqe3p*xrqόos?X[Zg yVpC?P~9@90x12݈$|;e9!o &gCG'H&#Y)ϸSn2ӋsiaAs[iVq %3g  D ta^}j)-#97(- -R: q8VY4=y鷏ҝܲ6h|\ zȺ!Wm b-|]}<xx^.ՆJ)tɁ^QWF3مberv]60;†,5ܸV>ofWm0VDGWFjnq9=*vcob,F;{h.F7 MU-]1@QTD z IsR_?/&soMrz}^F]R!ܤ^QΦZa.~O Sn|cg(pnot7Y} + \~xOo]xּr6LE-b `6[@s΂ux˄!E}+YU6#VKJanlfK!dg*vsZ d_ϝhcO<fҍ@7Q0Ung:s<[,Hֳ/#nqcJZdQY3ӓ"dE#YҗYk;u1|b;J2DhQ 4^sʎb!$H?:0P?.b:!Vﰑ4m3WZ M50x0ͧqqUePR(B<:6_8nkiRd,ctq倍ĨeL7SzQ3a-5ƪ(5®楃Z o`w+y; h)=Fs"|LE EX]8sJ'&#YʘPG=ZV.[ЙwU[|GrOCL80tjm,B17 \>aQi4IPWJXc#X\f!,^>2#T*%д,SO0[ Jm`t>pa"h-3EpWOTb0x.״9>`i| qƯ|eVѝhΡViOVZdϱ›ﭣHV0t%|zNG°g^b#6yT-i+9\q6Ҵq#n#XL/`<1`u9OgX w%?7FPƋ7V(Cm*UD t_#Ybr5؊2@e=yZ̨j۾;UUSq6qZle銛֦޶#Ya҂l1DDR`#Yv-}˔[l[YV:/j;Υ(lZ)Y4oK5m6Y2sW<x!ݗ.xDGj"|%bxQJDoü]z\WʛIц #\^ :;ۆ炃U 2l#nJ_9WcݔOJhK8=_&3!ςr.5'$$6f`@"s GzCw>!x:BlN5Oar/ K^#X7GC:k& uR"_U~g$rVSqF&NY`}4$^SZM tJH#n[kaԃ0<^.}{:b'7Q)` ]`b3A#X{#njc 3+z(db䜖5 Nj%l GPn=@,d4G34F>w hDϳ/QK"fX'x]*q&lW_.lP(Đ_|Tw|B!sOuɿd&od*ԳP?(xW18ΎS)b_O2Z}Z,Q+-X}ݴ[[wzǺC>0z~G̍9vm*>_"KΣ1j)7(q;`z׮]D(wϖ]pJ9nM?xsQ''w gK^ek*v#n 9sUaJvGsЏ-q#YXI俣=Bqv?xwq5ߍVuQտSьj\ >X31[O Ew0-==GHᝊ2/!CC,t ? ٳ9tJO#Xenv5dz#Xpe/MKS{lcW"i4ل!ozҐ%SPܝF6f=ac+Jtyے+擞/Q1)U;{sR0GU7K:czȷYL.^ tV~!@|= ]+'lc*gO]E%MT3(PR98z$y%DbZþ4#X㒀0U32=he6Kڷ|bO)eRY>{k-v$?7,S#X!/5Rh9V~5y59r͈.>,7.xUo]`ޑ񘘝#t'\k﹨txg?wvXb#j*t ~=5xiupnFtlPE=sJ.utu|}K&!O0#X\֒TSk)3#X,f7*ƭC7cVV6+AT@9a^_hDVWly)12.=³g#Y .x$\Go}Տϡ=ϓ;!#z36w_*FRuMuVl.vح[_F&pBJ`Omnt ‹vc[Ye[暻0* L?)uz٪ QS1s"jB) >hFcŋ\*mBJ6$ m֣9jLF[z^у^;(#4G,G03EoA,?DIx=vo0{K|/<2lCъ.rd"k?'D:sd#]J#{TDi(&(";7RONA%DSj5{ql!֐D$O6_Ch:L5v׍*5}{"CHz.尤JIO=z9{㛼(񓽱#Y_w{9o#Y"0r$.ObLv87OJ@$)XX),pK@=vgo.ywFg3=,J{t'77#R; uCfj#X+`DytR h ƃݺ/0o6K߻a;zG}W?^F,̶@ad>'G*pwߕ{{3ï.#YJ.ʷ'1I/6Adp9[.F{Ձ$R~B?.?0 @}^&#XȒ"G'+]=~e4N>N7 Db2>7B,Fzf{*\zHvݏuqj:[[6^d1$@Rp7 j%bE!{yZ;qst7^;<70}=y(|aBNNYsXL'؂B#n3}w.+Uo:٠Aځͩ%ymDs (?C&rL,H CI@-(4gCjc@XH>{'!dpM(bCAK]Vʌ#19VI!:Tq_חExjt҈񧂈@|~(RQƒܾ+b q׍VDkX|^Ϸͳ݄#7mU?=#nT`bUI~tȴ,&>#Yt:kN.t:>%nW8<#Yˑď#XI4*SUE;5L_%l]#n,֖߭d&,drf$Ld!}uqqی @Û*\TC^#n 2q;p 4Qy( ǎ/l HJmkxJ@"?#n<%݁ d<~:kJ@5d!lA7Jw hA^i/W̓ߪϧ'mP;5TOp=p`BF !t?]SWKZOvX%nrKyZ0cHr\7dMV5Nt8'gW~S*ohM|Hs#Y0 zKes}]a_zoxtǩL!&oPPtAŠ\H!R~z#X}APJ#X`T]r#X |z!w W`IU4H IwzZx.H?))?nش%O':_o/~8;#mT#RS15rы$4#YidnCx_{k$:~(ZqvBtBLUuP Q}䲐muáǀI^Q, ;@{ rjj1 f|+Ob;$ mb˗JXX,x 3(҃g(xOXWArnyO;u0 #X#n#X'#,o>Q\0;B#YG RzQ}NulŠ$5}@DO7Dzq|a@}1:cŠ<\Zb}3E!6#n0VTUj/>n]E03cv<#nS"ќ[GX7S>ţ&TH#Y]86CՂAzaDEYD5ޘ !Kw:br"9Q?(~6z7ڝJeAW}a7+Nχ"T<9ȡV^%|@ ~+Fˆ8MT&Z~| #Y$LR+ĄpXla~K?o0߁mK"8d~A2̅B# Nu1T`"v8__RjPxVy6԰ HEzgFOf䊮<*Վt#YJv*\lFw*Wn N>!I{:7ê$y[<|#n=hTy`0A]v56S(Lpß; &j˵8q\\h4|y}jZ.#X4Ub%};̺8}jòÃh :*ؕ\(Ĝ]#X [rU#nUq?Y|j]gV"Dَ#YݨȒad_N"G/r MHASREMtH"=!&dfu]%-!!Y#Yîq՘OC'Ifh,9G㯇k{؊%OqކDc¸︡% b*Bu8yg3ﱭrV3Ê_B%Glys0dG`яVM SSQ⣓+x`GWoXI\ctuG7[m 0a3h#Yn LJ]Wƻ`>O~a2lm#(6IO\F^xJyG.^H#Vsc U&7#Y(aCSl#YIB! (a+4C{N"f_54W&İփÅ459!Zu8T m7LY*^c|`E#nx2wCi&dz{DAx!}IQ#Ydc:i>,I0בT#X}D)vA?MWN'#Y|!L .HI8zqF#MIܟ/0Bp~#X1ˑLqgg4o1ݭRH--g @w,߲[F]g66Cc5Z*#3sҞb)%]qW8{qp1on&[w!s_dIЗn YPĩ2%l}X#n7m:lʧRf'Y\ǒ>IV-\:Nz57oc^Gbq:uz)X3.ev;Vpfы/.KcR<_J#nfL,؄1DlbF&׆!0wmA%@A巶w'LagMՙ50`!b8PD UXn3֩ g.|Ekkv#{uڒ[WHepvN6%xYﹼ.j‡ 1ޫM⫄4 ?9ռ&RvdВIf~ܝ.zF_ůt #Xz E0 #nk?iq 'A'5^|O/$OA>{D;&'_?_Mg?gWO:ofu{h>/sŗdZ-;& ~Ծn#i0VphGz% <e7EfJWg#n#n7~RlGΎm *]ăUL)0bɠend߼4MxX IYYW*{цxm +|c0nب Gh|No-- s@zŞᳲ!y?@NE*R ^A4dM>2vY)=z;a~G4QAԚ}a {D3#XBPleb fH&#;m6?|*#.ө9e#K'J^xqN}`sGE$%c'q#X5`o͒bBm/ˆXC~ CC!E. `yBz&,Qw<;f#X"J9GaEJ5!2Ed;^#ʇgvޏ gQ8EdDE`d0!fvvx'C:bA[bzrn@&%}H`7qrSzz#Y8e g۸5C{݃i)#XU|3̻NɲFq0d{7g u:vI ԁGŸmMP:Xf:S Kizއa|ԝڞP|AI5[uυ @5Y{b\yALl>axԊ[z,tԄ|nb<3!.y-˘T@vЦ-:A4 ː/`;pehJxs`^!9Ăi1W! 6=JUIMPhPDLGσ:`gK(;$p4`C6hr;AY2{<Z˻7f*ckฐrz6ǿ:ے 2#>Cѝ%Ĭzjt>$\<\;@m 3 Q8u&f@J2yܟN`*Pyf8H)Ǟa0S "B#nBZBT8@HΕ$@U"C]Q8gKI Ab׎L@%d5&_M7Hva;y`_[rhlQnEbQ,00LeM[H`:ao.w%(PXT#nډ9`oDAHv#FE!JwdV+$.vQ7b6T[VYv̈́D#X #nΧ@~A/I3ȝPĹ2q M^JwiFCP3$güF #nM$0$axs#Y7' [(<:UO0d}.^#XL7f x#X{J9xogɾ'nխ3^YP8+1;I _t68Wk!N9ƫnc0؂"RZ,*s;uzC^a3a3RPpӕnۿD3Wrlӄ5TNF~<)wiܻv9&- #nهwu0ue%k#YC^2DFM榓1#X!F"6iH |M@^ׂ)6R,.O'`h|FC* -Q.W{YD.;/}:9.5O5[J0}J5'L4~kw5?8C"TuN4BZr#Xj)Nq87jqmU v 1C\5u8?2`\#Yp2xKژ#Z/(6@lX8DD0ɱR 2?}J jJRiiSzye}q؇ĆZB) 1O**5z1Q`iYM~|tUd>{oO#nr]t}TY#Xhal6^3s3b0෠pC(|Ow2;t`={,k!ω A lxCMΝ60,t\¹G!ށW|_DJ"_#'YxM%UVO*JMgWg~}#YƺQh]ҋ6#toMg連uU`\chFg}r^>U#az3QwcϧԣGE|q\=oRp#,LYȪj|i{E8@r{0vȏ2ƀ&pHΣJ^M`ʸS~OOI?N]Gx )̓D1i bDTCQ4MBlDYETJݔL'puJ1c~(['h0#W I+,`dvY>d#X>j&3k-" 97y396IHpeXxEc(zd H} El@'iпLC7]̹c~Pxo3d;ܣqpqGO_}H&DTAjbPxhh8ფ?a&OIZ~o2h[hUܵD-2zv' GSzʹw´V<<+3;w.OSws;9o%;,JQG(SߘAJXzslve5IA9(#Y93X`xf'c%!fu)SR])"iRcKmdw\)+6lR{|yHôn'@}P#YhPߧjmlHJHyM#Yo_+8h|u'* {O`uzv(;/A~,7a^W<@v_)wYL#nw*Ӊ~,)ӚM;;,XzX{˿&"b}z8, DU-&3(MdJ&8>+mPy}.2XQ@V 6uKVh~׋@݂%~M2aW A@XExu{k1J^#Y?oV#Y~df=, #YXl?q>Mc:\0fzaВT W7S#X,*?|`̈́!PHHz=Û/ϩη#BxC͍F"jp6ƶug;; aCpۺ@qQTTPF&ч""#n-n'a7X> 6m#XJ]yR_];z ^ܪIqI4r196ޗG|3ʸr"@ޜS7a:\F?$$U"rOh x>|ઞ:#Y}sU>"~~^mc 0Յ&% ,!l-iE;f2@w]ٹPv8waM잭]4P]ΌeHJ^x z?˂>P{᭏I8.My#nNEj $)0HBwZv*̀1˜o~ ՕZŝ#YLY4{B#Y?r;|97X:QM3 rM~f.^d'{j|}v8yw&I ?34 l_Gw5ۿvIu^ҭ0=d)Y?4i<`Ju|5?_TZf 8[#n#>¢lDg/cU!bbrSG<Rn|t3z7cLnr#n",XN?C9pz naHMg}^ (|6#Y4k51n; PWZ2COd;&G_ꚾUP8/X2D@[SHCuOwEOwf#6D#nvkNs`@kXa]{\B#X@jrhH\̰?)>Lb߀eGNG[D#ff?Mk?ys6IL-9ȒA7#EV(<}{deF,vN:r|~p ""`:11?{:#X #n#Y\_#ooX k^2tzJ诿?I~(C͐qhNҹkWqqC)_ZYʿ{:mi&I䝞] {?ZGxRV6 #Y{HȋҬd*AޡG>rýWe7G_\j!lZF"Y>8#Y T(;Lzrwd#X$:B'=A>G W ; Tq0),;*GG2k^o$$dvdفlH?g3nûCwW#Xv,~+lEvL5 #\)%'8h_6Xޫ/a oT@oݩr3IZK/&@#n- 8@ކ&ڟNH)O@£(5^?%P>4@\~NO#ƾL#(c =ØwJ''=zr+# __|ʷ7t]c$I /O1H DNt!:y]gA4}hD#nG[^ʁ0&Х`#XcU+ӟU˲_krg`\_u^tKy1"J_`r{Ac30}S##n8|0HPJw}1:o,a)0K#Xxsbm#3e\&:`;Dž0T)FrgUM([6Yh@u'˞9d 8l*1g??r!WAD]|,͍qռ'żjS !AKW`薓i첛 `oKra[UG尯*lQj4yӧ/ŜgbI \l,xi2#X=_!sHd+ A!Y%Ġh:&9n? EA@j7S0eHN)ͺ#X" 髡}d1E]KjW8_#Y #Y18QEra} VcQ=Ag”hY\cc,S0[>#n#Y}of‹Q?g}^~}6@#nR2#X 7 #z@!Iq>nqO|)9 8A$nCa/>ɧl D`.8B$nˉ?)lt#Mg"F;Dɼ۱sRp&Hgp+@}ӡXuCnoq̠%G %~lĨA |J>_{[Kf#Ky89#X2? FYD|x=%jR #YXfP r @4.6UDXpa!mZ:v (UM؟3Y(X5pUrl5B.Ԋg~.j^a@4!#X#Q Hڟ#NmeJ z-&"܄ϣ!&S")HfB$ԚKuDs־od:lG8UmGό53B;K9F#E/ޒJln7cy6W\hB8.*#Y_zt3Y^r_q_$ 972ή(Ϧ{l 4uH )҇DޢQ墫qf_|y]w'%9S.[1)>răm{Sܶ":eNoIV]W&]W#dv|N;#ŶN|jЄ{;x?ǔ )rG3vŰ []uT}]<õif\Ah.W˘\Pc߱JWi]9w|q{1%|f98u~LNQpbA;cB*p*Ii1S|Bc]c6ΖhŠ. `/)X-׵Q<F7!+a֫nQ.񓃎><~]pڸb^KK}G4ԓ(5_^*^x"5?k#Y1蝈ޮa>X8=b;ju;rfhON8_s]k.ĝxHgI!#Y_)ݻVk ANnR1B-TԐl>63|0fBqZpo}yǞ4Eɐ0/;5?WEvJ݂ZY2zo^28L9䠎wTd}As0e6?"D^4B {^ӕx%8{T!LJd/+CZ7<6IFX1Ö0#׹z(+g;/*lV"QĚhq)CTX5걝Cȑ6iEȱ{3ʔ78{xFK`׺s;6;r#TÜoɕ|sI+]ZեO.X=-[Tft4J(v' ٗ eItNfv#Y/F`yNQ3_&nZ-E]C~ۯ}}qT*D3`#XnG w{z- dɕ;mLj?n"Et3z]3 *3R!f+:̠v}np`QbH Vi!4 n^m>YLo`\l_&'9k9yb>BG3w"EY^#[3{y mj[GOK4coY)@+cSAωT$rEr^\Mx#XD=#HA;';֥,8EDc.vLJfj|nFqc#"wn67_׭}5Cpձ6n!|Vad7c ҘΝ-xaєb%~La0ⰊlyZA?t{ZA:˂F#Y~V\)3u3 ~~|ٳ#Y?M_6@vOX2\R" $K;Yd<@G!5%5) ׾6_'qH(Jy3{dU9?O:3EK|p21'D1z z6Y-TOR~#Xmya>BB?dBWl26i5`G! Fm}|&|\dY]zp$ҟۋM #nH.ia&˸`tڿ(}2Q$3GCH~"&_TVz#YvѣӯӇ#nfO8_f.Q#Y#nG (`I%b #nUҡzYbރEb珫u^LdCȃC#XTB I G1HjC#Xޟ`4o=uni!\{gu>0|0LjG'э<87ڐ$G밇݊ 2~qۀz/KeBʽkU E3j&3S|QbB m.4 #Y2 T#nx1L`2HAyRBr"LUsۅ#fRŁ q Q.IvBߛ.'"?p 2i-߁y?eesS7I?=7M#YLU$OSrRXw⪚t"g .#Y۹#XOAvy IJb{\#Y/AȈr9w9I7F }8G#YEݚwW?[#X#nQK@'圕#XrJ9 iBhSA#na&B#n?=)ʃ^797N{D" \((5tpMh'< C #L!~#X|2~2u{?#}?~=vm`ȈkT,$_OF4}>LyQnPTBcC>)HLxzU嗋Z:khO14(%@#Yʒ((`š>#n0DIfHNbMfy"#Y tЖ,AzHr QG<# Ez`ȡv#XFJbX$\[ײxd%iE`vCK,{OVr>{ !0 46Yr LQlE˜SfK/z4jCɏVz_o,< +gݚ_v#Y}LADDzw:M[`{ݍXmg'AA茔?wxwF*"4kˣEHv''rK}/2b)_ʡSx#XcbqKK&/Ώu?dD`#LIFTDmFyp lFN#՘a $dNO}!#n#XЇ*>gj>'W̍PA~\yw$rϗ#UOP ꒀ]"8't |;#XhY#X콑ď bPHPSsXC2=YZQa))#X>1)"~T^)~sXh|r"NABԀ@U{#XCc{#X#XxQ Pk#nz?_X5wg7_\ݴG^"2G"iDkzy(|*3 !ۿt?y}ϛUp!7=\ng% ^SmK~Y(|2Vp5g6|x) Gو4vd4?lc.g[%Jtr"4gw6ޔBD]kSׅiWUl1Bo{k D<&*mޡJj?ܨ G4I:ad>,ílJWAL!p.ٮ$1a$]pY$ΉQb1A:X>(PJF#DfKUuv\]xDP$n;q-gx&>@,PeAx@ [\O_}zjf̂?b?2ǏCA?sFy{<(Ω28^:YDAӸE4̌mup4Fxkbzzxyp|v26d'vql)DMCPj#X)'tP^*x5f_C8on#Xˋq P6ٯ#Y?_~?o1p7g)@%臯2=3uR:1}h#n6GK3D@߇DP _&ߠ #YxJ%#YP?oQ5hәot:kY_yt1b+GqotzvcԿܯ M?˪5#X?[C].cgתW>ar:i @Л#X}^GF[ y@eC)  @2"NG#X\!#YgZ^Alm3dso%nf21##n[|7MSg$"but#XSAatBGwws#Xy#XP{.Ngi9>q=48ǒzұhSTCƽIF6(qIcipGb#n#XDȑX(<2t0yM#X*(Y G 3QݭHϘPGWtj@l)#nses)P`bPvdh֧=Fxg &Bvš's&hs't_W_l쁷r>`ȄΙ5>ΖCB}Aa8Tpl?k~s#9Fes_ fQ1=`fK'x0u]`%t^7PF(,a`,2SXZP~+|:9Nê'm:x;C#r**D{f6:o=:'4.b]3Zƫ^ Z{#n=@~_@#X_]5o<Œsߜ'2r02طf2 6x' )Tq5#Y0BJ;撔(c[4m :=栽\S#Y4H>9n' q:*‘c$0pFU4ǼIh.OIN1v5%#n.i%#n<ժJ j5k:.i-ԓ^ =%F#ך2`8}9q_O5 tnCf<" #nPMӇ) z_HJ 6xEik:HVWMDD5_ɳVr~7G02@@u3D]_;sO#ngk/%)+#Y'Gk#Y^J{#Y [Z_HeQ_Rﻴ%q"6%7*{7}_{BrWvCZֶUg0X(>Nͳ}-3qɿن#`8Ra~8N_JPkG#nj2,Lw.{4z@#n'R:ذRNG&c,;?JmD2ml.:D y, IA#nRyZg Si_.ͱGcDxi*U]wTi _T>2y'#c˔j6}E-"p$EL%A4eξGsmo#Ym#E7I 8;x"0-}|c9 tJ,#n'E~Ѣchwp`d7p/#STI}Lدb=$( Uߗ_V'Ɵìx15kl~PVj`Xa$#??E-#Y2 AN?q˗n]kC=.n -=L<Ncop4O=afr"'$K9Rl_( )0>?/ç3z,a - g#X Clp54d`v/Z/ 殍0T U<]#nlɲG^I3tZk˭FmIUkUK[V`LUɸd%z ְ͔240c5K2=wS4yT>^u}5R#XL_J64C[431K||8GCM$)#X2;5tYYJlbs(ה&2׌$8{| 4lM`CG?=~(vIGA &?W3O3$ J/-(NH=#l11^/d#Y|~@5#Y0#X8O$!}#X>>?BU9n>>Z^J)D@j8W8c$m57d;ƺWEAx>f+WB#X*˾l}_hXlo:^p;D HhG䚩4!"K~ñ#B9a?bNW`?\JeMƺ:yi|78\#nA#Yh'~3CY{GOtT&ә|IeSP?JHz9Ǟgw8z4%OO&Sm#X1z?Uo-H7 'g3>MSH#Y EAD\Ϝv#Y:#X#Y^esۜzaM̍ YCc KÒ$ #X֙~-&ED(ݛ0T S.44#nXzd,e蠗Ygy'gs#I_ˡ2Jx! ji +!rE*0z83iH}N7`aKF'I]'Ȱ:43NO_@>sm>i!~ /JXO_$ѦD֧#Y,sT^Rk![`r+ql\p0A8zx% #gκ>_G>#n]>9]וԝ_Po& 9Lx'U8zQ%c/D&]܇aMo 8XdĜ|q7.֏RkT+#3i괆#YM@lBŽ#nUSQ #XӜU;TN[; L̰Lِ""{r;]ci#XQ#&<: \#YC64UQo4B/ӿY !\#n-19@}Mb aWrv#YĴxxt)dO@~r|HzH^b>3Ծܙ{@u"g~1ź'#oZ{~Nx[ @3qMN`67`}hLCH704nu+8r-c*[8i$j>#Yk94Mh񥦍2<؍MjXpO :aq!-p#nw~*UԦKv4UBXnn*~ú"w#Y0#n(<[?Ԩ^ u@#n?Hh0Ai o,Ѝ;uU{3+#YzG$-z?fvc"h,&4h2@;! cWOÿeO_wIA&c,W"08BdzW#X$^r辒ϣL݃xYW ||}]6$_Yf$ 9BwV\ƑuHA1!i﹏V琤dzICTDR2$Ai2#XAz#n#:CxO'W/`VJy0g^5:NϤ:T7a9wGCMI>c%؃ٱ:8jzy$:'éϬ#YYvGUGo:fROrKAX(8AԱ#ޙJ-6p{1I cYgJ Gٙ &I1C!8t#n?Ӱ.'49ubilzXBCqt\#X^KsҜc]HeCG领ͧ{AfGS=#X7r*zP#n$۬;#X:O?W &&y#Yہz ȃiw=C0U]4Ƿ:!6ɬ#X)d?aHb $R. o ;vʽ;=#{ԴP***X~~*X{_/50ԲƲ?B܊#n&Ilk^.0a$% GπѾ:4cM,F[c!TM>?5`|zHf Yag#n5nl,]s?)T:lt8zGhTE9pCÌ'`y Opξ=?yA¿W7?z]y#Wx!yYoF{tOH_zӏ(LgrK>(ɦ=rW @HH`3јKL# e*t,DCEdWgs/6m-#Y?E̅YB$tӗu?\€(au9#XMzH7+h9DDrC0I(#X=#X>K;,vZ_N*8_2fu!,g!(Bw|#Yu5|f'|xtHj4U[Yڬբ?ҊϣAHaM$# 7߆F~RNVV9rnAgٕ)Lʁm&SFA{y;yws$''WA~@w96#YJ(fNFUT 66D,CHAx:D88A2Cg=IݧSuOΛt2/9^cO5#X`]T>~?rxo7{CМ^{{ڗLd-*@#Y`WvaR`6yn;4+jId˸zrrs@0۹aK*N#XzP9WQK!TWr2xZ_RWƣ#ag:^kNU'Aj~!F]3x嚰wd)`8TGDz8w\rrUֿ_NlJCt b^=/#XA(q1DwHӭaDD3mU*6gdƍF OdPD.#Yx!h#YZX**vuUQt*͗P 9"R`V9%.=‹1o<6A%_3ٶ֟s/{`_HRCHv!97$N~ϏwoS$#nS#Ymԥ n6;$&=ej5FCz/?f倈'`8m_ ,8>T?IWIGU?<Ĕ,`D W bw~z_=ch(Oa׉p$tAӀ9Jۂ#*=#x$6(h<}&uX|EǗR:cH~8l`ԝƠxs#[Q.l:gf_ Dȹ~tFx?v٤:~9oҷ+|@,Ku^(MHzR 6|~x#Xh&&C,{Ba&8n05#Y d,D%j蟣3]@ &?/EL-!<*DI"BdO?A}l >ň{_|Wǐ>'Ψn(B!Eb/C.&^4;?]Xs'8p ΁̎9#nY@k Ќ9}sr[gÏmR+&;#H_׺seS 6|OF:!2'8M#nsr^*2 n3 S#n!}H9VM-!=Ng 'U#~f #Y1:~n=_HIF 4c#XC=3!pW> 0+g$$9:g@ C}@5Wq^}vI2 Ֆjp;OB(J$0ÿWT/HtLlOb|$#Yw>E?BJ`e+YI$QVY$/k/(<ΖuC2/ذ#YHc&΂iA0T1ez*96ev|X+f>A8Hd'#YWMA7Q$hTGHm?eHKH9mDqqac'?~?W4>Plr+~*,#X`!5=S:^֒bu`@߸55k#n;"WryӰ<=P#YBo.lXڀ<"/MwaZ˦G1*poFhm0X Ї#Yab2!?XT?ro5m%&<~t.L6($!E#XLQ^?"("6#nkv~W'R QzV Lӓ#XgWʸi#">HA "ʠLC$߾|1I|:$_ΗF%#XR#)Ez#Y*la`#n}QGJ.iP3Cw _@%H#YሸoPWgn[JمQFx`;ߧ>!){.ab*P;&ҠD kXFğDcQTX#nE#Yz}_g. XC~hOׯ,4ߩa?)Q'~C!ݴwC9/)w :}xE#}k80)HŅe2=" I\Rz{.mV}-bh䘂RS NQF4@J1b^Ļ:Ppk*(ɪIOsױZƲ#Yue z6 Z#X*^9dQTdP'@QoRDRҊ  /~0Þ2}M~'P#@?n٧n3 ~#Yb?_.^l/v|'䖞HqoBwT\rĂE`6#YeD".[2)H]>m=v%rRb#noLE[x26xa^ѭ7";Sj08[xc0 8d#n&s&If)Q7N?u-٭l01`!0u7 aցtP8}Z?=ӊoǶA~)#T 'C#n.*6AØo#2~PpAL BtVl#X^#Y :C#Yc-0q¢,3z5N8QH+ľHTQ>-E,㮚kק"H>6I2#R~"=}t흺_b268L@AI,@R e%krE8'R"4glq8({Ƌ >'".f :W>qym5Oc(]#nJi"8sEɭE3t(Lzpг2RNּﭛ tG5|gb#Yʖe#XcW3[#H0V`dP)s22tWl4Zxr#n@ 07A*)Z+tZ2|խ,j@d뛸TdpDH|7b#q++=ı{"-ښ*:{g9̑.#n61JIByƫ*f ~l"K!bn+j>9'ז#no>Q_|sIEiza/5 BjM ;RI}jܲ|&58M=#Y]2^4x1tCKbcƼyw,KL4a%0jNK#Y U:cw1,S2f }}tUTVjQFs&%:$r΋꬙hɌUElwZP2Kc.P2t> @%V>'l8pC*!__y~Jdzzyi#nI;:燞l%yʆ!> G*z}ß;D9Ӈ"&p^"lX 'P O9'(@xDExK#4S CO;UDRAS򦁑h>?y'{чTU.<Lg#}*z>IdDd8dk#X[7!ANETEX+3*&X`N͙ X4#YJBޞ)m[A( }}I@l*w"b:Qmk!uPsYvlC!SܑF8ۜ[m i`|#Xh*XxFBWh<0O3O0cyMvxma .a//Dƛ(KI S~d0hcl7&a<8I#Y:aٓ#MYņ+L4Q׾62 m B`li6]FE+rm;nk^dw5ɮ\a͜-F5Dq/$I(ۅY$6Ί&ShM#n 5FXb#Y*C_s^%گq\Q0pǰ:`vwy2"io$cN'@l'j&{Y C̳O8K:ϟd,%#Hb#YߒI#n{B0ǿ6Z׶/d&Oc<GHbd榏csr]ͪN Q*/{]PF5k2xb1(uAR.eS":iZTJlj7MIН_!R:O=%׸-3 _ E&ˋ)8ۮ<8նl8# eK'Ap+'/l4HzN`tmBCr;f\4)4yDg}e.{xoA=LۓIw_N4Æ>5>%ed8_yzRl"#YG~^=TPͫ_8y[v-D2yC8=@csT㪃E?=Uͮ#X 5=$-qI{aϣK w gBE.P<$YĔbYfg#YJ"vfu_qM1[N3P=dž/Ԑ<@Eב?Lj Նb鬠IpG/ѧ;GPMpq5@ Rh8̠+P)qRy#Xh8wpﳯB*i>>Ww vOȽ#AcS0՝#Y;e^1{4iH<DǣS6NG^f`X4PRQ1UPI>EwᛋKM˜6½=TȪ*-%eyr,YnaAb{1ӣ}Ky,D`0a#oOv ;fi (^W;:֯wXr'#&lF}׵v\=^iBu]:8v$#nBi{R#Y鳢P8-eKFnF8gd.;)jGZphǼgGݶ1oGg `j0#0r'{h xqϳ+#YArnqwdW"NTv;'@aޒM7}ɻ3Ok2rCjėF/%}/AĈeE#nykZ#YE8o䏘1!#7/7bH^~_O92B#YyECy!굅w']hDe4:FDߴ=)j|S !lJ59*!`:a!|uBܜV_#XDX~OӡU<(+Yr@ A<42p2p,*,/݅M-b2|{֌]vY;Lk&=kA x$lyKHLa9y&OU4Z g"16SˆY@`LT@?/j|vM9r(Sz8#YGӄ<ĉG/nF:we{T=#Y=sosO鎳c}-tk[^kJB(?_OOc^}Uڳ#X-/2Lyf:I,iOx@$#{Nu@/t\8Bp=4kߌ̃h*&#YY.16?oehU؃އ!C A_Fdo Y܆xEn'S1όx܌`v){e@|N#n< buд_ϬݮD"wsDۗO/Jj7*cQEg:+$ y*cx1xcmIc녌)!r-;#YJ!*I8G\冎Aa zRy)c7F0cHԌ,ŀԩMFqlCr+'R r?֯4+(qsJ/R]A`Hp7eGI:k`ٟX0&lbqFY#n§B^>ϛއ{#np~ļu4w%N9}a j<ˈtN{9?DDLE%P$ʖI)#X&)eB!eO%6EKd#XtćF7iɑSe,H(**ߵ#X#n@UoA%PWDU{5ڣ@TqP-I5$tWWo*}Z)ة9o,E чDG??WLD,T E @0k'b?#X#Xs͚5#YVx䮲z1u|ɶČ?L+<5: {!(_\A?ZL#Y[Olh4"$q&9M|7e$2f`JQlM vT:H ||F)`w*RBNqF*~2r$kN!#Y5#YJXPeE&2J>@!gAW{GO!x"?`vXsց˿,Lصj n|gNř,Q9&tݭwTh5#YIPG 2ۧ2CQDV{|DoӧVZ2#DDۏP#XH#XPJBPߞA`!D)A;#n#Xh@F4%*BL2*߬#Y>NTW%**#XJ1DD &tPDvPРЊ#X"C#n/ IP}pch.;*= JֆVa>V[{>dEt#n1]Q0ĩ#XUb<{2$R ڂQ֬`?)'i,S1ҙL6M2^` Bd3Isą "$B"t8&qѹMAОR-0L)#nZy4I0P4iLHb$t#XQ%(fRh(HsWj#Yn!0 bqNZ7"$!OÈz(8=]}>4D;Fx߬b@(b=ćb`kc%d>!/<"W-^ (#YF7<Sˎ&IIК#n|RtEwm;R J4Q9IP#Xg].r, <{Λ0EЈwq17bb#n-A$Bt`b#X(U:z=uW̾8O7"&i@QѥsyS?OupZm\4QY9M8Z¡Izfjv6 N:qU7pU=~zQ&#ne^&LqacҘ(M:k&LB:naFJ$׮eW2z32ux#X8!@?C8}j~Y#ni( H Iy!#XP0#X W/كloXs&IqpI:MkMrgwbq*" tzs$A5>od 'X/mB9#@0 j4{5Q,Z69N@,Lu "~l+AfTpUA$ߞu^,i1J6IzDecf%1$#Yxf?vZb4]T8CLtEQ7䭹#D2& -4#Y֙Rd|']%\.(X[2W8ۗP" 5ZC͓l{ʡxxB#XBcU]眺`Q=p!l#nH#;^'9̐JP*d{y@=s܉7IJQXࠞ0 Ǘjg|DB%$Q"])#YTP5B@#UK[b$ڣQ ?\#Xn=-&LMLT44PIU!H4Rn@hǕNH>CqpQ5(@ki@>">0Rvd p~#Xzd(h҈Pw35uaHwy<DDShJ/U#7M~/%ij'._Ou|{ʩ*X.gqDzky2E)/tX+;ᮅ@ļ!>`6#Y #X@U)uڅGm2L^ CG LI$ m9:(݉XX iL N d?/}ZF&ԅw=Op=\slĜANـ[CUnwOb}l!֕/cyQ:J>*u8#YG[AO.{~CGUΩPgRe43EbR#n&؋v@-L`4E?0%5NC|+;S>GO ;!; Cx!&i#n4 $Qg^' I C=tXO& pӋϩz\O\Jb-3G=),ρ2aӤ;.e csfcDI3u6>I #YlU:y̕AϏf0fAAaMy؆Gg[C#*#X 0/#n5ʄ7 J]/lP-(_}!ʙi,E>< s#nL#Xޤ.ĨmDEC] |>p^-'H(\UeL";+DP3!#n;/XF # T݄}z_ݭ BlW N^ԋ"v狡aD;!ϣ9=_Lw݈Psz8ϋ`l:#c뮝0F UDio.-S-#c)h=z$'ow;džzU T0Ԇcm,^=|yi[iª*R,UݤΛz5U#9% c~UU֕&́Y/t0T( IV&Ou'YG9$'rOnP#Y}3rP"DO*ϦZY孇)#;s H9dt}ٹ6ŋ":ҧY˝GgN/{tqipFY|7:edTq2AŘ۵F)?˚,`-jkG\yOA SlM8)xxz-}"ƫ+2r6Yay}GpA@{v@2#n0H65v;0g<9|1@)otFʆhQt]OhcV8l<9Au7M6*Q"T}v=>z[E3ŠkCc|+j,|CZ$H0}?sYHܵ%E2"*]ԉpM5ܥ`Q%Mq/n.8w3A<802ˋxZ>Ғ##C^.Z9#nX@Cwm{'ɥ<<`@uX%8dLj)5R\nQωpk×RÚʈu7Dn#nUTC!ǿM۶mɶ{D`]D1 Q7s$#n˔0U(﫹^=6z88(5=xk&= Tn:|or@8`v<.FG0SH;g=ECi#^%P$*Rto鸾z.X'GG˰}yI@ vUM2#C?^vѺZgS^l}vTѓ kHj#Y#YSuTRDn8q'kb#q24Ziڅ)h&#n&B%%SVWB!Vr}XS5v0ݍ @p?D5u='Ԟ|tG#nx ӘYQcV*-*¶O5)F̎UbyHr]#b#X@$2Fy@bTCc7='8=7#h>ǒ}bIzNʳ#Xl)BF~3C5ZH<1#Yف#X(Tal:T*[J5[u42i(#YkTtC::q+}P 5 :Nôf:2y@?=WN^[E$a0G0zϟh|PmE!~-]ynuz2u3* *Z1QA#+1 Lэ:$09ejd"B#n0"Ex q!AF6T~sMrmg-WHѴsp \$(R׷nnaZd;-'0@t I8W%6HHs!:J(QVC#7x8 qO@}wfR{jKL81G郔=hgOnehd$>D0L:'m, ' {".f ax$v#nWo8櫧}GJRi)b@)F"@6t 1PpҖ2tTTE*iQ.k˓f 4fX#=G4Ө~[1ֆ@&gjE+(|>o'}܉3ɠ}¨>ҏ4J Yhn$>5̷KM,X dwHCn--"& T]0l>X #Xa4ԙC:VS؅ 0w*6iKĨC0kv;a֬E!RE/V|8Pr= ´+#Y4 [QF* l~qy")Ɂ;؏[Sbl?Q@FUXOX{1'#}.Cj(l&(1-Dó!) kU#n#Y1lQj hc].C0>׬1c6 n!!CP FLt L0 gj 7@ۙS uav`=Ω?)6!F#`81#& dxi8s_#Y۸DdHl4/"D's8<4f#n4B3(.L=D]=y9@]g!Z,J_Xc=9A(UApn=3|_'׳Wi#Y&Q$sJ\Zyp< pXX1 !%\$T|ȩY0`iP!ihD?V7h`NsAs;ױ`XiL%KjoaJ; 6n8聊p{9yUH/{f,܆9s> 磓 ዏd`hvk#Y["HuD?[):.TBW`H׃JyB-_$P%MMgEth CۄSM6B`OCOSD~M,'p ?2Jz[[u:xM RJCyXtprkR3_Pzt4n6֌>ߨL F!'80IQLRvF=7D'T" #XxD{Lݬ/ICZgpUIOMa#.%4!=@Hؙ#Y#XpQ|@it'ڵ)mj1;_8wJS?CݒBhTQ#nGA-#XLs=ɹHٲyѢil;MAu$G" Y Q#Xs"(O#Y*YBQhsdT䈈h*Bh1R )UJC|5!"i $;)#nD s*( & N_}E)NE9J/g11A]D(P1:z7r_]tĘ^!MQ%.ㄍ)CDgB: "d ֆt7N' d9{MG}|#X<͐:;֘Mjp32C3Io0>CIG\Ǿ2^:rLK&wNX3JAsZ5A#naY$u2Parbi1W {-(HP<ޭ$m<"l'L7Xc\Ӛ24 IN700r#nx #Y&44<,5%c_Ec ܕa5+]ZEzj#nmH8]ܦF#Y4Mi3pn5LK(h< si<Ǽ #(MVƨC]3Q̓VQ,ڥAO,uhAGdXi!+5UG:HVpYCVw{=L$#XP,S4P˪jB*`ԺgMa] >y;Y[>\:u3V܆%{PDm6!*CN|>*qVH+|DViXFqauF [l}69aƗDX<79nAN ԆL5GcqG/6_ N`]#bχlD! '#X&g"j3 8/46L#Yͺ`kPIڢY^B~ ڂ#nTV74uçf1W>C)P1n:`WO˿~މ$דz׻j@]l~@#X4@#X!_5.Z D^ՙ2z-QTl{[zIzYUa&hy4Dj;Y{#vVMo:-NL=eWD7^}Gpv[dæHs1TDuzh8 BN杧@uw$αt9Η|[/f@D )@ Jg@~z7xG#X0?O_?(_;`gj6Լ\cEg>!$7c1mN'J1EK=3GIԋ8"M;u,.881X8#=`3/eP4OAT-+fEJ. a>'Rҥ>nWV|V/&x%.N#--Oljl;opSfv(19}L#YӦeQ)Z\T8~0f<:8E>ɼщnOOu֔jtJ(tf=D<\vF3xw:{1] t݆@ڧEKM/9`#bx3tIEa[fv⸚èC#nLr=ѻr(៧t"QY:u]m&kl^#Yb2$9+4;τǜ9xf+&gkM" %EͲ=U5D[Y_UT\#@H굢NCoҘf2;væf;aMM\0ݧ42}:/3~ciY3ЗA;wh#nY@lIQӶ+ӨQotqAYe(%Ă(g}.^_)w<)Gavž<1}PoakҒtcz㞎Y5:۷sHM4{10֣\3?9xz3A l"/PRrpĮc0$x9Î/9za%<{OtԆBri0gX@ g !bM  R*i<0 *Xv02mqVq?n:<3ƞNR h7#nXʤwn6ա%ByeJCf-ZêٽB0fE?q$IX^^VX.Z-xDhRPOYj! w./)E*];)JPʪ "ޭ۲TPN%ijQR&NA#! Ǡ4 I9l5|B306#Ydiяa6"5(gh`Dc1X5`1\C¿H0#n"Z8{)r*fwb *\-9 E5MF+vkz3oX/P &<%E~2+Yt#+Thumu- BbJʽLSOx xG/adgn7unpS[$g%[-JS&rs#Y# rX9)E6Ef3 U$Rfa dF>sU}U#*8'Ih%ţ6Zvf؇^-2tЅx;iƔh19DL)u­*d"ICQZtmH:a["8;*r923&rfh/@b Ž~p%EŽ<.8f,ΰ8wFDn)Q&0VLW j(;.^ָ=IH[;h[2ptxVNYs\BpuZ;a#nC%; f])6s6HC,ڊ"TB͔9t4ZXmepgXwxyO&m#s/PrO3@sQD%[kqYBI6?t1w`b;:|kȍFTc#YNZ^&&߉#]&*gZ|,c,Tfb$H™֎mnMd3p6#8;!wlVwЌhQFr/w7pET'_bZ}лkMYy^J^ u@#Yd,t$ Nt7D/ro:SG*, ؛2rN ,Hqd:O$<1pWeBd&1̹m0l dL+\uF")g83̱S!MMMyktxda";QHj_XHI6jBPO3Mb$+)"MoF:&tۿJԍ4\ː+,h#Y ږU&L7*#ΚN#nWas=͜Ga3q! v9vV$H8(848̊k]=F}Nm>T8BKMO:fUNf@E;0)3Y 6=l(&#Igol89#Y(t0SWvYÇT{%L#Y``1sյbTMΜ^PZAXN:dvx85>E84 PF)ph.3d( Neƒm66 ##n*)#X}D7G`dY|KԝR"E8M6 p!$⼒9tE$DxG0h4C:K#Y2QX4d 3ث@ѡɆBL01L53#YarXa̘3I,n39DpKp#YYAБa.R 3#YtX6LPqZ6y㐩f֮iwίGMVoa3,*yHYİHJPID*JQfȝAjSh5s䰡#4094 " 0潯2#nNC>fN#X&^ϣ}_I ČI {qȪ`tC&%#n#Xe,WOrG{D!GN0s:OZNݣP$.GM3#Y$y#ny<;c=u<8v iN#Y ^9!;~]#YXdx2#YgxKIBB{#XA#Xy.P#XH6L#X0~.v~6Oqflfni\ӗGGrxhx:م#n4}f ;'wl%-SΥ%K PCZI8r򇓵YB "8rDf3Y]r:Coqרr0 XLs3>2xwlpO?YI B0:vZ4*J]PnL tU΋[\֪BU38{ƢYZ(6rznq)p$Lэ ӳM.X4Kı^NlnOe!Sc+I)qU(q99)9;:|&$Chw̘xj55G;oz:>ap= ,rqJ0gt&}(Ms҈I:EBeuҠ X|O%,5T.tqv߳U֣dΩPjĦK=䧗bi#Y9l;"J0x(v4†)s㬔 |$,ú %ߺ.(/fL(R!_^JżxRZxv$̃ œRIL1#YsT@t9҉d:$4U9unpOY@zZ(j lӚL Gϻ+o8оziѣ-\#Ylr+$N<#X;DoMsT0Q!-IH~80'@D))fiĤO#Xv B@C/W (T71v<@5ub#XI׉z6`SƂCM*nh.i4?j0ERn;M҄QzѲ%}#f8VbuI"qu::R^.I AT@:HugI0V ƜbpHV iՊ# 5`}0z|#XЯ{G©zׁNP}#X<ݲ4usO8>ф+Ϯ%)ǎBiQd)8C̈́6/ 0ߠ pLbC dHțN|(VMOVd}Rd͑(@b,?C#XT @Þs!Se`s]ŷ}7`c[]6st5(‘}E26&mpe}RKumE5:&Z˥Gȧ3v!Տ*("'\pfNR#׳]#4'B$#X&Ƒu/X>#n9e93d7LNF%&#nqr[h:bdሼN!VЛh19p-BEGXza]~uS #n-1O!M~#XL1x N ><"JQH%#nDM!CDKTLLP--\JC: (/u>e5ސ~`b7G x] p#Y5HZvf+\G=oa#Y 2l/ܻb,}çN~k1̘{w):"'Jm2#.ɍ'C,1zrceT성|2 x2sny!Hs֟XD=N< \lC$˶39+u]yis߯Cp`>ÅТ"x%tC T@J VR nzZ-g(PMXIGBhuYV]( oI3!ZYE@p(.crp_JBU#YQNtf0J4;~r GG[*f(fyS.b#J0D E#t#Xب˒ H_2t+1#nA#Y [lPPRݢcF9hJGCwc_-LLhb9R?! ?˳htUkhwgaA {Zg䦔4tn"͎U1qꡱe99wISܞ_f"4w~v3\T0Kb7YŤYYڸ4$nbrxG#XX0m sVXk5g ;+o7G)2c$]Fu-*֏+al#nrѶA#5$¤ca:![uXc ÙÓ xr2 ew2=:KtZ$#Xu(2f}:| @U'W@xBʑ!mC$t4ddX%0fay4 XAM*)T :h6E{|$rj xЬB!w$Гbr,M#XX-E<+f9%wWBPqf؄U`r xsf?ԨRqp#X~\!o]9TLc }F+#YL1jT$:CFcM3##Y}ԴOAV}FCq27S:Ot-} 6HXaéBs@#XBZ$bS#YpL%) KB4n!@MY>F+s,Yg"x#<ib"x]C;ԝӻ Zoxw/FfyÌخ1fRi%Lz[Sܿ#YSi89gIAҤ6O8?n&Պ7t܌OXz֓5E#nHة]nOʬHô7i"|L:>g1u&&jUICd'qm#Crf1zHvFPP^Z0}甥5I'4RoVV1)LKq^]XݽgӉo1Q]D+sE#YUF˷If"$18̏MHg#}:Dm @`BoX;?P*꫙nf- Ϫp_کm %6TLIݥΩSh+䗴BncA8k %ڱ16KshIz9 6.&/.s÷s~` ka`7vÌR`;]7|g>RiлQ\=QC11T Q47jzwװqdӘMQv2`=m,!4s*drX҆u孓#nFĄj#Yw$pL6ա:9M5t" !CG&07Bp!I#n"i|;CrT=J:J,$<#Xiu9ʬ( ̳#X?АURi~ewaXG :UEɳm=UΈ$,&{]Sҗ@0l9#X:%ۄ.R@`\[>d9h#^b י>( "HR v;`Hz8:3!JRDqd,-\멽 :k.)*c!Ď'FoUpy;b!ۛf,m||~zaQKJEA#nA(S%F9 V;Zݷ';8ֈZCYIbbXĹIhI#)WF4f %T"VtxHxx0cξ#XNPJDEHAD$#XХR ˂@# @J ,==Fr#n#n)(P#n$B-=ݙSTU0SH3)C*hsB)cE/2L@P>>Ԛ]`Mm1{"RMYTt?=wM߁H*H"B#XE%-DM4U)"Gk#X|PQ~t:gGSBl Dh<3@pwICBIԉ] EK- {rQAN,Cvq¬P2)U3F%A"L{\kLĸ5Y%u~&$άu!Iv,Io MRsr}@:P=C^_Bv6ybͤ+514TڂO6"b>&)ik;?wƕ"/V2N@p2PBT':%b%d&HdfBy&v@БQE""@LB-#XyfFHdtCX! g"SKHТRo_UN>#n"; #nDЏiT`&hhH#nG T$#n+ 4)#YCK亨)$b" b#nh#n()&""#nZba#n H"&X*b $ab*H&$ hj$h(i#n`#n$b*"B" "ZA#n f&"I"e#nI@!X#nP"b"TR&!) $`dNȩ-4$AEMDB L"3(#nX#nA40 !*$h"Q$!DE\Nz#Yq雑7^c/x5ЙƓwrJ'#Y1(.'<;a6&| ϴ^#Y/+!/2zȁİ(>{#)'Ѱoւٰ:&:O(ō,q&}1y.`se(ݧa9RtZK<׿8 S~.ι#Xb? 'li6R&]ͲRĔ8󺁱 ɀ 2&`ɩ5q## $xI'y:rf(vT&qC!>Ą#Yow#Yԫ  4DDCRSxb#XzBh1& WA w$#Xi5f$ p#^s~ZLv|Ja\u`:X dz& Su˯#X2'`$b%dF̓/<3KMY0x.,YX#Q1IS|**~2#Yehbx8G=qUЦGGɮ§DW׀i"kc|i #C*~ۮT[( *$DKkF2hflAQk GENQ}@@ǣ%"mUUTUT#HRQvC@leW|ɋ=j +lh )Y sr ׁ#YNuTXl*zߙ2'^s!щfe~`&8cژ悜PF'rjw}SLIsoOp91wU>MW}zz;t_(G0d`q_!E'!?Ay †I:'#Y(#Y*k#X{\'ѩPʤGpQG_0#n~$*AQ=v:İKE@M{rRXe.2G#ngYͰb$$f)d.gۣ$ N8s45ytT&b gK2I#ARH^Rxh;e*& 6Væ H"cM#,쇦%,t*%‘Ӽ&UU'mݶKQA鞩Aч#J!pCG0$t"X*h#n@L`;@$E?4'@#Xxp};nMP#YRa``O÷e/E.[̓ƅ0##X(w(xvccX,a#Y,+6XLoG<jQ%W Q#By(kA3n$&#QQc(KX#Yl6:Gm#n"ŒcA!T4EO&3Tu? ^*y9W +텶Zɮ.]\sߜIpuoqzpмbKh\*1O`3A e;1O)FJ0_nU(^oD/@UcbA!LdJ  nY+Ks,|3̽9;ki4AZ2kBŜհybFS6NObd4Sd@j ?RSs-(DChv#YH؄d7B`1Y#XP$>Zk]EǿEMUQml{,A5ЉnzlIn %CQ$ y]], z,ri#XJUbSm0zb!̾Yۭ0iw;Sp8xxq%DLFpjLBii#AIH!o_eOHxPiLf$b!yȫ戲䷪Cv^(e C3I2D #X˯FEMN#YS]{Bb#nT#niG/#nJOi腓@T#YRĴ1w`bU!BSO:ʢMB44#M0T$TEyNf$`/ڠ6X(y۬t28DSU UAT%дG`|E2 w$MiBUL)i0h|%8QDPkLz#X"]/lSڃB4M#Y=|+ m294@ʓӂImzͧ{Ls;_< 4i#n;3p=#X!郱d)ٹ7gMbe2ӚLS2ՃJʃh7+{N!P2 iR~с*wD<7(`}1E#Y պχO 9K`rm6QFeWKզ3fuDz=HaϦ"MSV\ <:BZ(`a{|КjfGA]R,Ovv5bL8xGu_7FʚqE& pM}RP4ҩxsdO%]vz{~|LT?BN؝ P(-)I@WJP4%% R4HB+IT DP S2)B,N44B#Xi#nB4#Y!HIRP5K@,E(#Db:#X,B4PSD4AET@4-#XD4%H(ZB#XJJJi)F()b"B)) N#nD$"m#X©7gOxhJ#YC !﯒0vB^] +֙+E* $UJFh)H ״#n3 >Μs*>~K6Abœ!@<Ay&EQ3YD#YMv H8#XH1gnE\Cp˲ ;1UQ#Y#Y'xtOЩ9T=P[[bb)l6PE ?&LUE& >RSMPS4QMPTAID@LLBJ4AMRp8$Аzչ?Ё?P4#Xΰ5@T64RZva PhLi7&8$8"%#Y2Â#nXF!Gz'?;@6#n#XMQ7mBf)l?{"=F𨠕A/QC=ضuq _^tԐM ()1X799es;7;#X+Q!@wcm롡%z'FIEa 8˲"L)''Q4lDQ449m Oi ^D EZPZhĠM(CH NJ*ҁ }Up6j NB)HJ#nGOp(|ِ&VUooSZ#Ygj47[Dk(-&Y36Зg t0ӗJ2fi(<7O#+dFQOϫ$,;#X^sn|^| ^P#X?׎RN"D#Y|;aU,J4J4#XbJ,0 ДjHx?ÎQI5ZjN5B4]_uU$;O$2(DT::]\Rn#n~E"ZJHi7͆}Iy`p@r %5iuW>/{([_E4TOH`4]Y"zhRɝ֤ƹ D=d8&Clc2^MŒ`ܹLK%^Y*(%()+Fŗ*)98'#nf@$NΨ,?SfSLR̵~\]Qόtvt%+),; QJi#Y#qm!(i+8chk0vNG}IF4{K$"rGr֢#{%yDvy+x@mdLh 5vX7 IM21;v)]9Ay<܁`(i#nD";&zuRWsӆ'"!b(#YDmtEQ6栊VHHr ãP}kFr1ƴ3@Em#,K^l.V5rc}dd"b3TLG#X_y%0TEd| P#n Tu~eG+rA!Im_ x:KIDډQe2Δe(@"82Ƀ8 -.4P?"TkB᪈k 6XT5'C9;HGU0 {NlEdC#XAD*$@Ҕ#nz xR#nh#ntRb46A/B<^Y"4p K D&C %UĀP.@i ($)#n6&bW,JfI@H YMPR Rs}BN $() M#XBL 6 ϢS@ss"RJ_e3D@Hf\(ai]A?.Õy}%@pY3%9@=~uQ#n {\0>WtΑ0C ?!vA'5=D? DNJbhvMgo2gPpz^z!g``0a18 !!0hc!WRQGB4Q2lvv%.$##XH& k&i.)B4ٺG]@qE8t@ZMY o7Gdp\0IZq_npN]jp}-VOHWne N 1pLoXCUcq oc^^>{(lЯc!R.f("\9 x44#YXTuÑS{Wnhs_JhRp 5Ѣ,|A8!}ҿuupre}$TǢ0Ao6$[P9<@E8*j& R_2xI$IRCpA44׽1@PDPS5P RN=fd폯V&e8B}Meʖ*1{o(9.O)]:I&#C K#Yd&t g&S[K,[]Gf]䤿ųXڧgPcWr'TmL>4aQ}vSLG.lgRi=&yn&L;v3S#Xd w'r4ftG~G\~o*{v9$x_41A(!Ec,f =(ēk9 "#Y.ŃƼE!nuM3#5*?5p-+ANWPG3d6pT뻾;۝E+ gݒU:*egюv,:cjL$gz$RtNbcVr*z˜f$.>rQ(]nX1aGrҷuvkXЈ+Z1 =0̠c2"N"Q) !LiPQFx-U#g˧[SL2#Yvc2wx'ICO{7q#XUI'`Oա֡ &T#n)-̀/oX xpPa U/#nk<_#X;VXpz:yyG?ǺqLo~^ۻG#YjgB?auJ{Bۀ{ 1@Z΃1ƑlqQ/bPtʼn7Fwe;ODgDS^ !CNPS:M:po1 ́ڃY+ԋE#XmS'}k77okbkf*9`i<\ Uta!`HlC*jn#nHxr4w:d#%ThΠ?y˒tFfu#X>N)F^3*'b>MIDԅ4R -E% !HR#LC@LTSB5TD(1%4@Е2Q1BRM4̔#nДDTPRAM,KIE$Q#(@ԅ13I h*BB)!! $* *U T  DMA "D2EIU$HIEB%CH)!(,"%E$LQ-PC,%DT5TS#4LI$PHLMU )EJLQ$JP%5PpzrI&!c`ٍb1"F3D2̝NaP7N@;?UtCr#X#XJt"PY;@hE0Z@ J2]N3TDqD)#X" DB` [!M"%":{#X(td96Z"w2| "`AT~?ߚ~x P"z׌J4ܿ8ЍP'RQ04@DSHD1BR#XD4>J^ #XA8@8`:@/8oYL@6ؠѤ0@`;Ԛ'[N+7Χj̛3 ]#YѓP9:ğ$'(g#X$kI!˽֊Թ$#a%DmbApbK:W}0uϖ8Gl#XB'D6#XaDy]*xWJX|.{Q#X>H4(oʘ$yioȓ J3LA (X}i1=>2Y9}f@59 (Rb(F@9#X.V?=Ny"O#$f"@w`;dRR*L`m4Dd9q^B "$KeQda(q1i4)JT`ZhljY,9O/{?z}Ip-`H1 ;0#cͫ?Fi⮠qݠ& >ϳ͵$#nBŕp5 Owg,1(/\HoHi{z#n(\9EEJQ0ITRPĥRB4UQEAQP Ü*(dB*#YyOQߝۦeMnHHpa'$3h%*N)',<9}@V1"t/Lh# Ԇ˼´^Ӎc|IZԴ- όbLu1 w.#Yi>1q#g2ΝL$ٚHMmf>(wSE=}'|g#$gpA-4W)?\hB&,J>E(*ė {GRA/} ^ƛ;u C~.$ʣd PwqDB-RK$jBw#3BXƌYR/DŽzn(#n]'֠]j-B#X(Sۀx-U]qG\1boNM#Y,(Ր=lB!!u0#E-@/`<')r}BרFsR; SJD$,޾*^9An+ͧMsI?\ C0䎃#nHb"&!&eN)!H(8|1CX#YMwQ#YŦPAE ;jMqѵOrvy#nTq@Ys`1L6ƷZihiIi@h59H&#nv!Wk?)C`8H_x!P {˦_(A# $'y#Y 2`Ϸ^r2}gTL1vzQ; ݇ GIS!QÞGjjRR!͈i 'Ѵ3N?<,F+"!E!J`#XDL 4F@i(n3^v]Am([?I ;>V9S6':bۥQF/©.F*8A ;X^*pS,\ ٥!R<=#Ys+jYKV{&0RvcN9##YXGōq&bUA#n# hT.5#ۉq-45dEvX ؒDH5jCf=#YVRU2΃qq82yx 2/? $$Ew\҃!#nj.)46F58Z1hYm;i^=:Kg'h;GC^$[J'J oߍ#F#Yx=URxLꂦ^,MdH`Ox^wcς"I4P$mf(8XYn#X4Bv]"A#X?7#|$ wi\ZPy!QWjH"*D ah((Ah )-r"AJP-$H @P1$46#n@4_⢪YH^~QAkh4)'}11L Wu(Kr5enH/+ Q4ɲTc1duT%lミ0䨝308.FԊ( 8O0dnפ-1͖B,kiVA/}G6CAx)HR#!~#] 1>8cm۱=$.A 7@Ł-1`(R=8896qc҄1`Vy!QZ.nѻLe%ݘR'O(TVT١,̈́q "`4E@=)N2*q;{I'%|l!{#8)HN#XOm֕/6Kd~9TSac4dY//?,>#Y`vY8N-1C,0IY׻ҁ#XY7 }#X]xBXC3^Br"Q(+KӍhնk>(1o\̴RçTds4Rϯؚˀ$ yzpI'_Ç0-OWr8=#Y$_ #Y#n3~9U#YHsr=pǧL!Q(e&Őy7ϸӗN)ɾx 8}r@ fS5v^E9Lv´ܦſʰW[fE%:^ocMK9u4rjThcSdbq'VE #K#lmuyVkq+Jq,RZˆ?ThxHϖ\N]#Y_#Y&S5LT82s ܡTq2 ʴ"gV썱R KgRp8,w$m:.*['#XP$3w!Ģ#X9C˴4$(6DIS% SSP4%$O.BS j#nm{ 206#n#XǴ.€O_UFڙBvGD;~&kLS}lvzbCvCZ/:sǓ4E3f#n倨#ns xBEAh[sIνN $\n˃qr(jDpD#n*,rhP#n(iM@= p#Y@| ~fJWq}Ǟdzl=_k}+| agj'XB) #n#XgǤ82Gå:U{i18/ݷ{Llw{X7\l,Ӥ}$w\ʤ!f/V,LgF0\1FѸnzkt`V4%Py I}<n0xǸ۴$Bi;zTۼВ\bƆSiv ÄĤ 8`@$"J# #Xp#nR$HDQ%(,BP#n$8&ǐyAD^X\0ulsAHjhHf,PVsQrR4aGJB%Vgv0&cuRq#n#c*.A6AN"ArC=MB8ASM#@D@$Y:_tyFN{BX"Vk#YE}:N|7:31B; q ˉ=y95$$fcծn-#鑶~^cJSs58g0llŪJ)]9p k10ӫW&:x^SJ(XHL#Y><'Ğ!pfc$pK#n(-`_i$|h^q,4\g .EE1)`B DA#XiJN;h˽0~DBZ xd 'N3kckyӞ|/Bzw@R3)^Q)$˔3ynln 9ʃC#04#Xxb<%U'R`NsH ĤHhp+C 2E.>>N)qʪ#X#n@?_` 1GjƳJz? hHy||_&C$@s-N0tg̺Ia6ʰ}ffhowhZ 4MHk&`&رW"h3E1c7t@58W 6@$ak֊>%-B ?:^NNLi1^bMk`-DP:'{ 8#n:'#4^Vhŏj.#XX `šx.F(sP={^ypB8Kj]sy1 D=ݧo1@gvOoc'#Y2BJ##!J!c85U4#X @.ln u$=}Dvkq);."*BwDXEͲ?Iy#YL60700+8 SENw:ݽrSO{3+ tϺơ:'9UmyC==σĞ=6#Ci-Y"Ho՜Ih<]+*Tè!ېū)d8aHz(C@@u>B~M"bP<1g"q;∻|K`񖄠#nZ(RF#XiA[~<9RiqbAQ9i#Y#&"JaTB wK jه׸sV"8v2i#c*IL7#X۾th,>|)C>(9j*6G22^N䈝!Hj `)TJ#X#bMU Ga8$4C89GFI;#)c/J}f59Jxð1~T0,);0/4}|z~>U^3@ m#Y%8)!IEc#Y(|XX#X% P>Gۛ&Kf#SFc]!Bp2D CO0?#XyHcAP DOs90#nH{nslHl2p2ʓX@tC0?:<͂*>LM4 B\2@?úQ8 4*ȴ&FRhn#Y^ Go~f\hQ@hC Z,NVN,E.'#YmzF~-Ym ""G>;!̅6zPpAIz| ~7v]02ƛ]BO_;yvI(`I’Eݗƶ[)BWu\ #nM?Ţض&_?:4fl3Ҟ|̹w}dFHlg4 @0@5\E陇)99:nQ`pY4-I>#n|om/ iˣC_yf>j}nE:'Ը(y@wss\#n9ց&}ΟVC875?(f3Y q#YŻl44>TH횽MPьE1B[\"4H<NZņ#nF)%,AACM,cfssTҰSALյQ1r5OC> ŗ|c`xG#DT#Y4X)-9Q8\ AA@1jj`w<C.$!#!~$pyͮУVS)] 6SQ d"V*PbfhގfѾ7o|~ѭf"Kn$"\#Ln k6˲G#YXsp/qBban5%Iс#Yj>\j DœyҌy8KZ>\3kT4{0瓂q2$7SCɪbPI.#Yai-(H1kA# JBHaLi7pnkkq;9yDt"7#X14z=& LLRf#Y1X*4(cyBw;ewhE"@ ojޱ̼-1iY:nݍʍ=2Dqڄ-,0v_ϛW` >F6-B#nHއL`{DƵ㕙5}50Ed gQ2QRx#Y]u,cbfJ 6#r I$@hO<}xGN+<%Q׬,x@qJ2A3/FV2#ce1&7a Je#n*[o~)=IFg՘#m``#YjXh 66LԸe8$b1L@xB^ $xY )*bBUBJ%)H%%a!^BCnjffHM#Y49#91uY!"`E`{\8)d?t6,zR ,tpU4$pqv c=4iuF91UI7m]wcKec! Mi#,, Ì&-Syr*48^Z0c@p]׭[] K|iPm6 Lm#YZ 5+#n9Њ ct\QaO1+Mclxeec5,S5ce4#CSk(ֱ61ŭi6be5@E#a1 Ó0LXaD1#nEJ6w.sˇSɖ+xn)|O<bO8'0L( bk6pRD0r)ՠNv;ÎmAT,#YGҹ6**nF:YM54\pFICۑ0nrkݏ,t~#nm{m.ftqM̝!M;' uçm eY*6Hu#yoJeb'Nna:Zi;NSA4sDib{FK\"ʩ±cD( #n(vXBzHm[f2Ŷ2~+9f;eLXzAtWSڞrѾ~8nŘіL!IWgf[fBAeHڪkX6g6fFE-ԄٺѦQ P\s<%pq#nĀK'=NPQXH˸84`3c7QYJˏ0x;iq#XK`Y͊V(`U,|tE 8#n!$A:&7ՂЪP+!o)#XA#X]9#Yj1ŴFk!ssmwuoz;eItMMHKdG@r @)#nܯ`+vh"W=c#X҃4 I3E1gAPM0*mt\Ar)4f;b*(Z#nTaNB NM!!sDM#XRrTŊY@llPR2#]<ո}tUg fDu\%HaGhHXL #n}$/#Xw9Fd#\H(Uio R&)qR@¬#X'.9ʡ3-b2Ch(yP:(#nOw$:M C5(RA#JS4$UAdUT4DEQDxXDEnFA$ECLD4-#XRAKTT4%H4(K@LKQD0E L @PHy11 HRʨ9v#w$ |ȦH8 1y`bop>8G E"A3kF݌E4#Y4!MB"0 @X_V*'Y|Ӆs4Ak >#Y4:E*{!d)E(p pCJ"Izw{#Yd{BJh@L2Д4A#nbQ3O D4+LLBA0U I'A@:i#Xq0A(#XrRbQF(6 ȃ(IR !#nHPJRT(k 0]*8In~U@{o tF?J21HU'rac@4*ГpFS#n&k#Xӷp&"Pfb"Pً@x,[0 &ma:0 H3"wI%^J`J`-P᷁[!^rA@"|T.}6ϿQ}IH*|?Qd"L&Zro\5&haVYxRɆ.t(E#nS!&:'=]R-l{JjFH۾k[wwp#Y]Ir#YmFưN(2pj:\r.ren??O: 7aF"tZ+U4jYH&TQGzP24/Y YTd+d2Պ7We$t?lDTTY'HP't,#'^6 MP"#Y_+s:=#FOtE^@݁(#n5=j\7qy>X:br6Sb¹4W3'p$! ϲ#X&pGp#X(xH$QM U(DRPM#XPy~WLH^INʽ1zEB#n/ $R(}.ah _>]#Xzqؘ#n>ԑd~N#n&_"c$a䆴C;R_[F#X#nPa$/fG^y|h+eVۜ5QMQ1E`qTr8rhg~^ J T!,(WߕOH~8iWER\}! r$?'pɢkXYshHJ(+4C _܀ b9zEMAGd~Fq¸t|a "M?ٽ6qKT5uC~|4^S3q64n7c?$QT%AiE UD+PhF? ^?Anh͔JcჍ|@x\S^e3:OX96f"3YIwq2{+b:}lH(4ddR;վ6*H Vѐt:tD 0DC^TT-Ed4Rc35#XY1t JwD5ҩ`v`I5(s=(qA&6ƿ"/qrxz՚r`F=d@{46}rV:M=@w==thF#X!S\<1#X - +4@dKI(n}~}7j¾ #n\ŠR׽)Ep~{r*4Su(O_v0P66ybu*k*=ԀjripϟLL_0DC6Kq}]u6[c#XIyc_^Fr0]kUҰKl4"iF(VH:u¡qOCYx*MP!vE*aX**#YMU)N 74<4LD,I:8Pd!Ԍ')e>ykz=H:Kg&.j#YUu_b#Xf*t>N(o*cאS&Z:fM$5NX&ODry RCvZH&_# J@z p$<]{RɽnbJy? D2<)()(NL#X!B{E A]>(#Y{\-lb2AP;#X'i}+F 0,bA݉Ȥ#YE5ݦr_N z)N $0y"CbV%ϣ@,T?!\n1cd#n^BRvX ]7>_m8R@#YTQ;A"fh)I)#n$(VB!I#X(!j%۰PӫӈJ|ǬAQBZTRD;#Yî%YI@B$ Z Zb(EfF!DlR,__'@:+0aػ$.TPG *b)LJD|(xՁ#X 0*LDE0nEOItӷepyGd(ӉA{:`Iq`'F(e^_9؊/4gi@~[;"-0?Q0#nHaGyL@8lFtYllȞ6$#Xv9ytq~e<9BrEO& M##Y-G8 H> =dͿ'o":+#tF92v#XHT0wBrQ^w`4BZdPQ?dddHRJ ")}7GP6r050 e"B ʓY^A$AH';{8sv H l9B\+޲m1똖1A#XR1`HJKȨ̚\sw.#n aM(39ДO}-)êYH7ݰRA)$vC#IB-&z>aД% v#YW"#Y"d^Ө!E WH !.NA?i *3>HhAQPG] eU4dfbѠE6$e mT%#X%0L#Xp'dSGrD`c.pOWN#Y #XinU<*BSG2To(!ABWFID>4(UOPJD #Y쓠@ H_`/#YQ俉Z#{bU4zɦCTETR #YW##XX0u#X;#|ь"#n5Xj J7>xIM!8P/LH9@$OAI-`HC!'P2mu x"sLϫcXI[ND/IJM<6ӓU>:JWTEYd0oZh=#nd0 Ad(T}EP=E }x.P!qգi9P!1vb2A/ytQ<@ ''XMZ$XuF#Y󮁵(j~8 )q2VMy2Fm Oiٍ?œ\+/ӌ,Q0,qO}1rI=̨klfsbeN b9JLg,ȸzg-?҃ G;36X83SZSV@%A4JhUW[ƮTIaMbj\g'65h-wDaaÆN_N&g642fA3l?1B2gJ8R#n9d5}_ MB ';6aQ:yqDso괐5 #Y͆ǭb1ǢM)5#Y/셉uLљu,G#Y~"$r11N}Ă߲ai,$@ #=x1jqw0xcN/i~ ɓ{*nH@m6pwpA{Lڢp-yhhK6#YCM^>l!."Qc[FSwۑ#nH<1۷'<"_0!Z1toGT~苑Y.6#XdrB %T#Yȓ ~>r0#XB#XsJ."cXcap ;=PcqO7"#X›&H9A^;׿i?/JR"[.ݚ+cX>X n^+{С[#X(QT>'d$yi읺sj,!+8H}WI3kQ_j#YRNݑrYSw܌yOّg/H1äyЩv^sOה4^F5^J$޵Yypv%q#6Npݣ<(Қy+o9_c26#Y[޸ٍ{{6ɨdS)T"rͭ=,`dAj[c\1W[*dc\`t:z@S$zYaMaBu&#[҈-68I$*N( 2E$D Ill𕡝/S7.bȍ_g\=f/4#fdBu+BrE4:%#YT$LjJaNS&=9 4sQ0x}FIT68%u#~nEAzyYulVs8{*IF@7ܨyB썠5(*:f(B/!P dp4b76A}ҋ%_yh^R?98+PE U"C(AB ?! 漓6-5]5=<[O\^uO=&ÙMg䚀=Ϧ\4<'9Ӣ7~8'OFC{CKA#Y1y5אz<8Pw'z7KOy#nIEJ#n'H[@( c(21 z<r{C*#XWȔ&9)1"!67NVhF!:@[-hǡ_y c ӅfT4o_9+:ԍ3\z@~x?g8qb`]B=d <}:?%@qTDn+#XW-KV Mų9Ov(%e2}#Yh4q|q Ќ v60'ϳ)ld#nƊ!}߽WF7|^ Aغidh`i5C',dP5 l0Vߗcn433q 2zQY.SE|`6rc4#Xu4 +0qSٷݦ l{^^cȖ1ڤ#X(1ɒHOv(ZݖF3a6sʏL>n~fa8zUKjMQWIpMA*$h#Ysd#nUIXReIe He@P" ݈ͪý9/^k?FB@ʂ)`22$#nf& HIPsobLdL`0O|lbk"#Yyvj{~ZT`Zo0rey18!}!t Oq "ArOS휌;15 `q*dؒCHvT=Y1<,&93(Și ֘cJjEƴig Dr2e6M˜.G:ctM6̍/yhh$۷l%JH $#nZ`c$+M9X` dέawIe*VX3Dt~vHypz&mad"x"<eybtScMC 0n3 jecpkt >"|&urr}JgLLKj[lGEFBi:Hhi$#n^"@#I -3yA Q6oe¶)B{:AҚ'etG8=(6s0\+6 j P;pֹkDHY4V6]cޗc^iim 1#X|Z'7q{3.^Or#n`5AR7:8dGHRͪ:J(} )(P!ÎAk6uWǼzr|t Fϋ=P?1yRI>lN75BA$)A' G&qB``#n"()Q@ECBi#n!I"(6q5L_h% Ć;n|uLDlD.pήF#YAWxCXy;pp:Mߓ"0 fo8qMaĜ$/6oiGlcc t0+jchZg3FM36CX.4!9UJ ŴrHvZ ! u4PD5,SXBj(A55QrlpN;թ;o\tDH2zh!QHpwzU4n4tƞ^!r4aN79s ')x|1SA#n^ybV5maQ# 0kxEsV@& n|H7i(nZڱD&i$%8426ߜ~&*c(*[y2ׁ|f󂦈?!C"$J*G*V`(h;t$bPв0 $Wn_",}CTuf8uxixWD|v(BLI#NF>bdHxxRssS%չ#Y 4#Y<q^wW@U#Y$t;m##.$_O3#XYbсiνӧ&{ډ}7{j[h tK컁|M xWSN pT/51FX#>@`w04XEM%#Y(P-@ĸI?qNRvkvnAMArT9*@ɜa9oyi)B&Jy01Q(,~nm&Pdya}#Xm^ }+#Wԉ7pw.KO#*5Dvv{y4{xxHubs#Y(j"i~Ge =._V_{==;+(ZAK #Y4SHA\Ftq [TEW6E.b2rXivҺ#X:@p N4KJNTehZZiLJiPИbit@:V!ѭBSsE_Pw_Y#HE %ՀO().Ixc|6f #X>yRC_&.#XPN2V'H1)"!A@]\DXQx#XcJN )"LD7`nm9GBI/V !p9h 8rS_Ard()F {kIJ@5[XGuKBnҧ L447v#XuΛapVԿo/ @IE̐4< mX5u8CVHY`Δ&D3p:<EhV*h#X)@PL+#X$I E$*؝t5J$)"c:\:(hiWhڀ1-%31@!zzin~&ZZ-dPsZu)PhpR#Y/5$Nv@3#Y,?Gu-_Sc $\ö֊BHH}O`F+L ȩjH"6T[#YG +$X;; CtX#Yk%ERvus&5ΎhL= P5L\,E1LQ3DBRRS$F(BcxK;!^ObT}?GrY2n~O!<ˑŔp#cMf)ʲ!% 2 _YxA:8[|&j^ab$0$ uO{CJɓǘ̠~p_e֔9t&s+_I?$Mc̽&VGN{ABҘf;pPŨ;^^X“Ѝ`v23%3hm.CF1'#nqfVi ICxóՇ;T:Wǿo$_M&)́&`XVN)PZJ:LRXeg.͹YAWP?cR#Yj4%)#Yg+$d7C|us&e ~nM{\b26YI#6iFLwա+/Ŧ]x7>rc8;]|r MJUÎ9(#nXB{I(N 1Vv7=N'^K܏~˿-+.%CFw-8 0=)ו$cYeWF" 3pB};fS]|t{;}Cӱ¢YVze'YO›wk/LY}t>S6Ue8EQc*Ppb)LJТ3;Db5Dr9jL,s{'MWT6NZgSH%JQN̄1I7+At@Χ׊.ýIwtt-7dh:û[N>UReamd3t:hpYڡUĴ׌kL\կvq>l߂;&ڮ׷沲\4zP)91#Yg'f2\--!BK$h:뾷j=_o7#nc.4e~61y}H-^`VٜQ#nb)H{ԫx}Uphkyi-_ewGNviŅ=`+~B4ϧWå_Pqb3-qcrR0#%69qiJF]#nTָŨsk;?"~GCHep8}3cUCXrE1/ΐ|Ś︦^ݜΞä>>ރlY&4#X`QHidU'3j|@%D6EmsK{Fr//gv|0|uiiAbHvD`#YT7 R=y%2FdpB¿.7w |3"# ӵI`}q#m:2w]:&9p~f33BuJ;b#YWI4#Yn&#Y9ۣҟ)_LFo'{b͡ UĄNM*)ʚY@ l(KV)xш}3$B҉ڔt]*k\Hm ^Sb㚀`^d6O=m/Ry|7Aq"egW :yyi#n$4$q#Ν^)ƖX`rmӳvIHZ!0‹ф CU ٟjk#X`PT |'Н@ D9#Xg#Y`~=c4q&oS%q#X^eG0@́$`A2v8MxÔ=!C|% $DX8n#npW4%cTy2PC/":4b<*cm$'_]{0R`0q֠&#nf@]R),!d N\wȨ+7Q,ƹ>eAqY! =Ee7(RZ-V|74[Cw^qq&J.h#nd>$5.w``h\`?~C3wT"kֹ9 BdoM0zyrf:˾=qLa0N38J#YWge ıt|&~&S͐8vdh8#Pui*9*p_x-h^hs d#X64GZLṵJ<;!#X-XdB6:UTQՎX":f@bΞ;5pz'=)+;/mB(IS^NgzQ\Ŗ\B8=qQ-Q m+=MV<^?s9Q:膫dn/mX3=6ܹ&`1PЙX9vVMüQR|я=9?it8#g!MS^6kݯg5!,<#YC000ߜ!d"&.YC=LN3d7#Ydrz1F@`!SQ5ErB(eO)(TM rt6I}c1ѭ:5syECb6twbJF0cbw+6&ٌ}q=%1E"qFA0sd䕠5my`AhNAn$Dǎ=sTkD--qr(`w۹"#nR6>N#2ZB̆&3+=Bgig$Iʂ)x >-B[j% ⎑]AmD)86Mڧüx F( x)<ƪrg@5z<7hBaQ7!i.!B&9b %X8s`h"%w8QSH]%.!T:NVWxdKA[Ca m=kys#XPP(v Fcj+1j,r3i38>y3^#Y:w3A 2K#nElm #Yj$H7pf.d9'C9D6;_8!FBAqsi> EINpj"$F z\?#YWkQbR>pO91BvxcI<ؠ5,#YEAdD] hmmQE TU}<S g#F 5u٦#IKUܲss76%/W+w!,>Ʉ=#X'$(NMP6!`PQ:2PUUD>uzmИ2&] )04}ꂅW-kJ~O.#Y=Ԇzө@]>:M2oPužcߦ0SZ`l#XK-'d%^@JI$cNy^T ;Pˋ^ j:P4AztésSیh`6a`J 9qΫCY< >YT'tJt0%0TL6I)`]o>KF6}5"bϹrT:mRaQ7`0Π#n)R`&"HGXɵ4S KL 1D$%1#Y+lM j!"T*F(#nh(h&h%$bBm%-@S~8R8`T҉`#nPP5G1P)$*Oگ^*jdJ*Il<%(ہliA3r=H4ثrW i϶Lo5Kw}GSH22#nBRg}Hhg0h|{$.$*"cNyC1merҙkѡޤY-86a-c^8oLL$ĝBJ]A[le5aU\`lxQJƶoch+W Y,Ey(#Y1&n]ňܝkWSTt$&QraUH6ˁ;#Xr!9EUl89IA-0IĴ걳wLΜt@Nr=WyIx@=DF21ȄJǣQըm'gIH|ɥnϛp  >DP:2nS⮷} @,)r~#Y?G.r;eת{F+z)T!wI,0 >31(i AIBP;#0H:& 5V&CH9#n%|HЗ ,$-.r/A" !n4%14_B֟O/ɯ5K:HP!"l驖#XxqJrبo}8Z޿>EwU4#X0~oCB0F#n+HPK'ɤ>c>uSծIom\k t4!NP:'uG1@W5ATM'#n-3o<%OoJho_f^U3mz^I)Nq>f2nꪹf+9ƀrS,˙~kkswɹ6lÏNfN )l!8#nwJekXK<-v#iJ^<1ṶSG1w.6()E.Q#Y.$\bafxĖxlE)B> 8)ߑ6xG/k|6 yыr΢V5ΦyU#Y! VNMbA4p8Դqp#n`9}mITrC_s/amRiH8Тr瞛w-8Q-:Z_Ϫ >MϗCEBt&23 !bT#"#÷j`0VG.& '4ŗt{p|6a#hmK; g#Y&Ì9irvaƸqq!#Gbƹz3YF* t9A0\&}o;s1jslzw#X$V5xJv6ZlQcFI˼=Bx)Cpcnc%aAkL7I:Y}TG!2]Tt0McB!QM-3,Z\#ň; N3>r׹#X.#n!sq3s˙-k|٭,v/zR14Mi;]4s#X_t^) _CfW۱YJ4j}GtUVvv޸p;j fmގ8\K}L\ f@4 p3.iPjJE1ڱ́ Zӯ~t"Pu\$b^{;JNGBH5 c{'BD#XQp,cb4mѪeZO2He$x%4ӫwX{w:QU;cq.WT#Y,p pH+8阋DbGi#nq`ʂ]gK hѨoMi-Gﴶ\"|UwcHKZA:,jd| &/(bk*Ydf=b e͹<`=rO{SPbdp&噞d$t #YvsC,'˚3(`CĨ0Awhp#JCf#Y2al&.KvQϖptd#A"I,2ewT{ ^[[_-xRd%Mμu2ːvQFjv4qaM*%55DO4,XW[d{3a#X-"V8N±RD#HMyLU4&GKw \qs_Bb(L#nNP!9 )`3(HyE"Y%Am.G)pָ rWUDj?%Gy$.kz[~nfE!nl>D1MI'k#XA>/yP\4&'1ɧB~ΥMGUѐtCeጣ0A"R|q5by#XA'˹: 0%0]伇F8=[U8pc6(*4fD#nqbXJY9Cf80f#n-A) Ez $ted)81FR_x5\tC;$hRȃb*i6uð>CrF*{3&ѐ|پ>v9)DG#`h9ELWQS;#Y+h:EG` K`g{Х$01wJC0#nhB#ps~{I80M 8]Dtc t2W+}I/8xc= #n \1oF+#V_vLN{hײ\h'" $)bT<.\4\h9lvX:`ޡycQ#XįAC:$Nϯ*0>T19d! :#Yy#YsLQX)H #XIp~TH8B3yd_ڦ&tZ8Hؗ|A4113!4?$RP*)#YȹLGx0m@#BBpH3Z`a/#Xu:G2h(p9Ж0ic&NG8#nн dNNHO8S#nf \y02#n)Bz70ÅC/=JFqdo,mtnv=*Rk,+m[Gd0 vtq>YNyX  +5Zz˱"4 I:ID#Yҵ~\Gߊd$]T))QchZC}IDP .~t~54p3;sxC9iˣ鄢srneYIEkn 9}.>effKT#nҌkJfNxWW%P[DiL#6&(ZM#Yb8f-/DY|kW b;& }$WSEzKB>,M{yυBsm4394W)iѺ9k)1i61#Yn1S9jh5#Y20pCg ^57z+#Xu3|=&`C,"E9SP}'vH((64bmI62ʙhL=l)ϊ4רRFقNc˲("ΔgMS;f ʏ5~gyr*|nFp5!oX}o >#ظHRO_ #)Ű074]L""ag Lzb.*#nIP<;.ف!AchU4d.0)r\+&YƤ#Y cwwy%#Y#vA-qDq_gHT{^QɯQ?& ;֢?vffV1Vr}TZqڦƈ [#(D:䰅ٮ\LhhX)X"'4d.b뿨S#XPA3P?\PHP ]3wn'&`6! #X2DAHMD"}ҙ@4DBN hC NBWaB$IXy#XPC$ )31A)#n@iE5V]#XihD9#XiG#n#n:F B#nY&XOGjt%e%U4=rtaxH?XnA0̰6Dy%#X4%tO@@#nD#STP/]?Ҽw1m {-xҡHR!D#Y#Y3 #_ǰ;?*H{"wCۇDNwh ۱ŎP*#X )9U2<`j3u;F^mrrNo$: :fkz;x{w^iнFD4OiE9x-Ri2b>DȓFrE>h̋L4Ɲ5uִMkurd  VАɴ#YL&#o:\拠$ipibp-DrKJUxCn#Y'#X* LzLTz8%:X-g*krmb7-G6 +RVfLv[s(-#&mٌF!#Y0U+l1!X$mǁG`I`Hnj y!poCTpD<#n:\81/s&hK#nJj%)Okj9lT):pgZŋni6#n)My#Y#X#XZD(2L("K@恩+U?ٯY;gx4_.vnl\`MjcAQw1Y |q i-@3K"(Moa6rT27aHPz<<ܑAYPāK,ibJHjje̡Q/e!SB(R|2@h%N@4*R-H֑)Rh8dz}P_al#X?x%p+#X 01!BC0M4 U#X+HD)HCA@@#nQxpp᠃2ʑSw] MߡD/^X}R4z}^oGeP!.{ 0tGV T P#Y)J4B&W.>YLv{<5@$ #~P"P|#YS׷/5O85>O9.kxTe|*z@f[ZJ[2F?rkw+U)hES;PeΔ1:ZLatW!С1-8#Y%J,C#ngIf"%LG9AmKA(|\V(Z*%s9 T%2K!"8h^p56VmꩪD\+unur^*r^#nYɖ"NZ1x7Y;&r쎡ShyQnb"tۉ7H6ݔɼLP%A4I(‘J̎#na|,X'02d^iwQה@$AQwnic A}KL@#`iRiIP;۔j‚t^ZptH3hPb9tn qC|MdĔK7'MW9BGI<C؂h`eb'gC9 䝊Xy?'A1vsJ&21bo4FOW{f#neSRHI"^O]*-mZRuq72AKZ5pJa%R`mՠX(\s#Xߓ~ '=a ~1W7Q<H4#X>qhJ=APԐN;e:(%B(5uX#Xwj"-o4AITm[&"eby.\5 \^n.d]ifB#nX"Y D&(PIE(ZUTs҃4TQ_#8ǵQzk@RQARR 1P4LR H%4QTPCLKA !LPSB@rEUOWNȠ*I0?ND.p8ᓘ8 ضRsiD *HaR, ÁI-¸;>9%+c늪#Xy#XpbA;:D(DV]d$#Yt$) #X( (:1GP8!B=p#nǫsښa# F& cx$؇OњWs}iSIDB#HU-c$5Tɂ2 9*M"ala[DF6Ӫjm嬑%+I$A--%CIHmVc1"B#nx_^sHI#nQ.DǩWWM#X~:Ԡ qcr(?K J;)2,bd`xBDRx=*4fRv(x`y,z`|N)'v9a(4~Q)LnQ}&4H@ %3傉P*ϱ rd1h2"Tan̈>qk8g9{k(t6~v*gJAu=o>cZǵb6#Y2C_SXqˤ}7囻wE*t|/K."$~ƚ^u`n˶h!B*XPrn#n#n&oNUQжkXA 9\ŸsEgV,tg[{¿{UrN#Yd,4'dD l j-"l[Dc#XnY8s I;M"v馲J(C6E/P#Xut2} H$1ßKQ]nP0rIaI\⥦&#X8P<\9:/\|DDh10q,NNwr!`D(krAPA.FS9ra)ȝv9c?^ha,kJ} đ~R1f1Å.pĝj 9=m#Yq_pH8#Y4*҃SS:br4Nxtϖ*#<H4Q{ՊPʓUR*m(9\!>a$=;5_@$S7sG@#X5K'98 ]t"Y=,Un'"Ib*jI* -ATDM41' ' \ogf+cdMhA<_#Xׂ-G|gPb=@H(>ǭy}о`CCT?=\Jm#Xؐ 7N~AyF щHҋaLK_יfbQ51TM2tV#XY#X5H68'ci1~B㍫w{iӠ E(ꂛ!#E[:vvB#M8zv;:μC;K#X0Q ph?c;iMQWFս-֤pO&yu(X~DNrųJE+?Dzlq_eVcS$wl'(#n!dx^o|:4O"' gJxE,k@ta7R3qTG_6 :욓lE4&HY'>}z>.jGt75/3=#Y!C[*:P%#X@'|ttP#Y@$JF&"d(eYtxC+;g(Z#Ylb$&1O,^!ch:|t?f#?R.UaBV|$5@Q_V?2~>>PUB20 A!ޘyAʧ:u]:.b{i#nhb#n (SB$#n#XH!*)@d&@iBTO~t?l#XiA8~Y0 CC_Gc1%A8T>~Up6L1#W^60RE%RE#n㷎/qk0+35clv#nU-e~`̔bj$K@JeH+TxȚD>#׼k7nlx!;XCxSٸf`N44BJPwSkppAc#X|zL|9Y<2p9UjcnWS8h ! !P;*#n)kst3Ѐ7^jq2}HI][+)xz?dz BP:GTsǷ+6ej#2 2IsHwx#ȥ1$āi 7_*)ELQ2TR@PHEEK̥:?#Yez@L@=b[J4Y56]Ӵthxؑd8XﵟGIGIwFu (~?m2 胄ufҏe)*qP;̀"'|DQ~Go}.63*(?>L?ş}!ǭxy|)ΟpO'Y0=8)w)[ (<΋}'F<|6Խ%p#n1i)[V AN뀖$7#YЗ]{#XRgq^ï>NLb #e1HV/7 K/j)pzD$?"(H5R) #<== diff --git a/waf.bat b/waf.bat index dac6143c..db5e1d46 100644 --- a/waf.bat +++ b/waf.bat @@ -1,24 +1,20 @@ @echo off rem try fix py2 build -chcp 1252 +chcp 65001 set PYTHONIOENCODING=UTF-8 rem from issue #964 Setlocal EnableDelayedExpansion rem Check Windows Version -set TOKEN=tokens=3* +set TOKEN=tokens=2* ver | findstr /i "5\.0\." > nul if %ERRORLEVEL% EQU 0 SET TOKEN=tokens=3* ver | findstr /i "5\.1\." > nul if %ERRORLEVEL% EQU 0 SET TOKEN=tokens=3* ver | findstr /i "5\.2\." > nul if %ERRORLEVEL% EQU 0 SET TOKEN=tokens=3* -ver | findstr /i "6\.0\." > nul -if %ERRORLEVEL% EQU 0 SET TOKEN=tokens=2* -ver | findstr /i "6\.1\." > nul -if %ERRORLEVEL% EQU 0 SET TOKEN=tokens=2* rem Start calculating PYTHON and PYTHON_DIR set PYTHON= @@ -29,7 +25,7 @@ Setlocal EnableDelayedExpansion set PYTHON_DIR_OK=FALSE set REGPATH= -for %%i in (3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0 2.7 2.6 2.5) do ( +for %%i in (3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0 2.7 2.6 2.5) do ( for %%j in (HKCU HKLM) do ( for %%k in (SOFTWARE\Wow6432Node SOFTWARE) do ( for %%l in (Python\PythonCore IronPython) do ( @@ -88,7 +84,7 @@ rem @echo %PYTHON_DIR% if "%PYTHON%" == "" ( rem @echo No Python -set PYTHON=python +set PYTHON=py goto running ) From b9ce326714d56bf0332657dab5d9254eb75f29d2 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 17 Apr 2023 05:23:19 +0300 Subject: [PATCH 008/127] wscript: rework waf script, add mod_options.txt support, synchronize with engine as much as possible --- wscript | 285 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 159 insertions(+), 126 deletions(-) diff --git a/wscript b/wscript index f125a186..46cc1510 100644 --- a/wscript +++ b/wscript @@ -2,16 +2,16 @@ # encoding: utf-8 # a1batross, mittorn, 2018 -from __future__ import print_function -from waflib import Logs, Context, Configure +from waflib import Build, Configure, Context, Logs import sys import os +import re VERSION = '2.4' -APPNAME = 'hlsdk-xash3d' +APPNAME = 'hlsdk-portable' top = '.' -Context.Context.line_just = 55 # should fit for everything on 80x26 +Context.Context.line_just = 60 # should fit for everything on 80x26 @Configure.conf def get_taskgen_count(self): @@ -20,45 +20,29 @@ def get_taskgen_count(self): return idx def options(opt): + opt.load('reconfigure compiler_optimizations xcompile compiler_cxx compiler_c clang_compilation_database strip_on_install msdev msvs msvc subproject') + grp = opt.add_option_group('Common options') grp.add_option('-8', '--64bits', action = 'store_true', dest = 'ALLOW64', default = False, help = 'allow targetting 64-bit engine(Linux/Windows/OSX x86 only) [default: %default]') + grp.add_option('--disable-werror', action = 'store_true', dest = 'DISABLE_WERROR', default = False, + help = 'disable compilation abort on warning') + grp.add_option('--enable-voicemgr', action = 'store_true', dest = 'USE_VOICEMGR', default = False, + help = 'Enable VOICE MANAGER') - grp.add_option('--enable-voicemgr', action = 'store_true', dest = 'VOICEMGR', default = False, - help = 'enable voice manager [default: %default]') - - grp.add_option('--disable-goldsrc-support', action = 'store_false', dest = 'GOLDSRC', default = True, - help = 'disable GoldSource engine support [default: %default]') - - opt.load('compiler_optimizations subproject') - - opt.add_subproject(['cl_dll', 'dlls']) - - opt.load('xcompile compiler_cxx compiler_c clang_compilation_database strip_on_install msdev msvs') - if sys.platform == 'win32': - opt.load('msvc') - opt.load('reconfigure') + opt.add_subproject('dlls') + opt.add_subproject('cl_dll') def configure(conf): - # Configuration - conf.env.GAMEDIR = 'valve' - conf.env.CLIENT_DIR = 'cl_dlls' - conf.env.SERVER_DIR = 'dlls' - conf.env.SERVER_NAME = 'hl' - conf.env.PREFIX = '' + conf.load('fwgslib reconfigure compiler_optimizations') - conf.load('fwgslib reconfigure compiler_optimizations enforce_pic') + # Force XP compatibility, all build targets should add subsystem=bld.env.MSVC_SUBSYSTEM + if conf.env.MSVC_TARGETS[0] == 'x86': + conf.env.MSVC_SUBSYSTEM = 'WINDOWS,5.01' + else: + conf.env.MSVC_SUBSYSTEM = 'WINDOWS' - enforce_pic = True # modern defaults - - conf.env.VOICEMGR = conf.options.VOICEMGR - conf.env.GOLDSRC = conf.options.GOLDSRC - - # Force XP compability, all build targets should add - # subsystem=bld.env.MSVC_SUBSYSTEM - # TODO: wrapper around bld.stlib, bld.shlib and so on? - conf.env.MSVC_SUBSYSTEM = 'WINDOWS,5.01' conf.env.MSVC_TARGETS = ['x86' if not conf.options.ALLOW64 else 'x64'] # Load compilers early @@ -71,108 +55,138 @@ def configure(conf): if conf.env.COMPILER_CC == 'msvc': conf.load('msvc_pdb') - conf.load('msvs msdev strip_on_install') - - if conf.env.DEST_OS == 'android': - conf.options.GOLDSRC = conf.env.GOLDSRC = False - conf.env.SERVER_NAME = 'server' # can't be any other name, until specified - elif conf.env.DEST_OS in ['nswitch', 'psvita']: - conf.options.GOLDSRC = conf.env.GOLDSRC = False - - if conf.env.MAGX: - enforce_pic = False + conf.load('msvs msdev subproject clang_compilation_database strip_on_install enforce_pic') + enforce_pic = True # modern defaults conf.check_pic(enforce_pic) # We restrict 64-bit builds ONLY for Win/Linux/OSX running on Intel architecture # Because compatibility with original GoldSrc - if conf.env.DEST_OS in ['win32', 'linux', 'darwin'] and conf.env.DEST_CPU in ['x86_64']: - conf.env.BIT32_ALLOW64 = conf.options.ALLOW64 - if not conf.env.BIT32_ALLOW64: - Logs.info('WARNING: will build engine for 32-bit target') + if conf.env.DEST_OS in ['win32', 'linux', 'darwin'] and conf.env.DEST_CPU == 'x86_64': + conf.env.BIT32_MANDATORY = not conf.options.ALLOW64 + if conf.env.BIT32_MANDATORY: + Logs.info('WARNING: will build game for 32-bit target') else: - conf.env.BIT32_ALLOW64 = True - conf.env.BIT32_MANDATORY = not conf.env.BIT32_ALLOW64 - conf.load('force_32bit library_naming') + conf.env.BIT32_MANDATORY = False - compiler_optional_flags = [ - '-fdiagnostics-color=always', - '-Werror=return-type', - '-Werror=parentheses', - '-Werror=vla', - '-Werror=tautological-compare', - '-Werror=duplicated-cond', - '-Werror=bool-compare', - '-Werror=bool-operation', - '-Wstrict-aliasing', - ] - - c_compiler_optional_flags = [ - '-Werror=implicit-function-declaration', - '-Werror=int-conversion', - '-Werror=implicit-int', - '-Werror=declaration-after-statement' - ] + conf.load('force_32bit') cflags, linkflags = conf.get_optimization_flags() + cxxflags = list(cflags) # optimization flags are common between C and C++ but we need a copy - # Here we don't differentiate C or C++ flags - if conf.options.LTO: - lto_cflags = { - 'msvc': ['/GL'], - 'gcc': ['-flto'], - 'clang': ['-flto'] - } - - lto_linkflags = { - 'msvc': ['/LTCG'], - 'gcc': ['-flto'], - 'clang': ['-flto'] - } - cflags += conf.get_flags_by_compiler(lto_cflags, conf.env.COMPILER_CC) - linkflags += conf.get_flags_by_compiler(lto_linkflags, conf.env.COMPILER_CC) - - if conf.options.POLLY: - polly_cflags = { - 'gcc': ['-fgraphite-identity'], - 'clang': ['-mllvm', '-polly'] - # msvc sosat :( - } - - cflags += conf.get_flags_by_compiler(polly_cflags, conf.env.COMPILER_CC) - - # And here C++ flags starts to be treated separately - cxxflags = list(cflags) - if conf.env.COMPILER_CC != 'msvc': - conf.check_cc(cflags=cflags, msg= 'Checking for required C flags') - conf.check_cxx(cxxflags=cflags, msg= 'Checking for required C++ flags') - - cflags += conf.filter_cflags(compiler_optional_flags + c_compiler_optional_flags, cflags) - cxxflags += conf.filter_cxxflags(compiler_optional_flags, cflags) - - # on the Switch and the PSVita, allow undefined symbols by default, - # which is needed for the dynamic loaders to work - # additionally, shared libs are linked without libc + # on the Switch, allow undefined symbols by default, which is needed for libsolder to work + # we'll specifically disallow them for the engine executable + # additionally, shared libs are linked without standard libs, we'll add those back in the engine wscript if conf.env.DEST_OS == 'nswitch': linkflags.remove('-Wl,--no-undefined') conf.env.append_unique('LINKFLAGS_cshlib', ['-nostdlib', '-nostartfiles']) conf.env.append_unique('LINKFLAGS_cxxshlib', ['-nostdlib', '-nostartfiles']) + # same on the vita elif conf.env.DEST_OS == 'psvita': - linkflags.remove('-Wl,--no-undefined') conf.env.append_unique('CFLAGS_cshlib', ['-fPIC']) conf.env.append_unique('CXXFLAGS_cxxshlib', ['-fPIC', '-fno-use-cxa-atexit']) conf.env.append_unique('LINKFLAGS_cshlib', ['-nostdlib', '-Wl,--unresolved-symbols=ignore-all']) conf.env.append_unique('LINKFLAGS_cxxshlib', ['-nostdlib', '-Wl,--unresolved-symbols=ignore-all']) + # check if we need to use irix linkflags + elif conf.env.DEST_OS == 'irix' and conf.env.COMPILER_CC == 'gcc': + linkflags.remove('-Wl,--no-undefined') + linkflags.append('-Wl,--unresolved-symbols=ignore-all') + # check if we're in a sgug environment + if 'sgug' in os.environ['LD_LIBRARYN32_PATH']: + linkflags.append('-lc') + + conf.check_cc(cflags=cflags, linkflags=linkflags, msg='Checking for required C flags') + conf.check_cxx(cxxflags=cxxflags, linkflags=linkflags, msg='Checking for required C++ flags') conf.env.append_unique('CFLAGS', cflags) conf.env.append_unique('CXXFLAGS', cxxflags) conf.env.append_unique('LINKFLAGS', linkflags) + if conf.env.COMPILER_CC != 'msvc' and not conf.options.DISABLE_WERROR: + opt_flags = [ + # '-Wall', '-Wextra', '-Wpedantic', + '-fdiagnostics-color=always', + + # stable diagnostics, forced to error, sorted + '-Werror=bool-compare', + '-Werror=bool-operation', + '-Werror=cast-align=strict', + '-Werror=duplicated-cond', + # '-Werror=format=2', + '-Werror=implicit-fallthrough=2', + # '-Werror=logical-op', + '-Werror=packed', + '-Werror=packed-not-aligned', + '-Werror=parentheses', + '-Werror=return-type', + '-Werror=sequence-point', + '-Werror=sizeof-pointer-memaccess', + '-Werror=sizeof-array-div', + '-Werror=sizeof-pointer-div', + # '-Werror=strict-aliasing', + '-Werror=string-compare', + '-Werror=tautological-compare', + '-Werror=use-after-free=3', + '-Werror=vla', + '-Werror=write-strings', + + # unstable diagnostics, may cause false positives + '-Winit-self', + '-Wmisleading-indentation', + '-Wunintialized', + + # disabled, flood + # '-Wdouble-promotion', + ] + + opt_cflags = [ + '-Werror=declaration-after-statement', + '-Werror=enum-conversion', + '-Werror=implicit-int', + '-Werror=implicit-function-declaration', + '-Werror=incompatible-pointer-types', + '-Werror=int-conversion', + '-Werror=jump-misses-init', + # '-Werror=old-style-declaration', + # '-Werror=old-style-definition', + # '-Werror=strict-prototypes', + '-fnonconst-initializers' # owcc + ] + + opt_cxxflags = [] # TODO: + + cflags = conf.filter_cflags(opt_flags + opt_cflags, cflags) + cxxflags = conf.filter_cxxflags(opt_flags + opt_cxxflags, cxxflags) + + conf.env.append_unique('CFLAGS', cflags) + conf.env.append_unique('CXXFLAGS', cxxflags) + + if conf.env.DEST_OS == 'android': + # LIB_M added in xcompile! + pass + elif conf.env.DEST_OS == 'win32': + a = [ 'user32', 'winmm' ] + if conf.env.COMPILER_CC == 'msvc': + for i in a: + conf.start_msg('Checking for MSVC library') + conf.check_lib_msvc(i) + conf.end_msg(i) + else: + for i in a: + conf.check_cc(lib = i) + else: + if conf.env.GOLDSOURCE_SUPPORT: + conf.check_cc(lib='dl') + conf.check_cc(lib='m') + # check if we can use C99 tgmath if conf.check_cc(header_name='tgmath.h', mandatory=False): + if conf.env.COMPILER_CC == 'msvc': + conf.define('_CRT_SILENCE_NONCONFORMING_TGMATH_H', 1) tgmath_usable = conf.check_cc(fragment='''#include - int main(void){ return (int)sin(2.0f); }''', - msg='Checking if tgmath.h is usable', mandatory=False) + const float val = 2, val2 = 3; + int main(void){ return (int)(-asin(val) + cos(val2)); }''', + msg='Checking if tgmath.h is usable', mandatory=False, use='M') conf.define_cond('HAVE_TGMATH_H', tgmath_usable) else: conf.undefine('HAVE_TGMATH_H') @@ -187,29 +201,48 @@ def configure(conf): elif conf.env.COMPILER_CC == 'owcc': pass else: - conf.env.append_unique('DEFINES', ['stricmp=strcasecmp', 'strnicmp=strncasecmp', '_snprintf=snprintf', '_vsnprintf=vsnprintf', '_LINUX', 'LINUX']) - conf.env.append_unique('CXXFLAGS', ['-Wno-invalid-offsetof', '-fno-rtti', '-fno-exceptions']) + conf.env.append_unique('CXXFLAGS', ['-Wno-invalid-offsetof', '-fno-exceptions']) + conf.define('stricmp', 'strcasecmp', quote=False) + conf.define('strnicmp', 'strncasecmp', quote=False) + conf.define('_snprintf', 'snprintf', quote=False) + conf.define('_vsnprintf', 'vsnprintf', quote=False) + conf.define('_LINUX', True) + conf.define('LINUX', True) + + conf.msg(msg='-> processing mod options', result='...', color='BLUE') + regex = re.compile('^([A-Za-z0-9_]+)=([A-Za-z0-9_]+)\ \#\ (.*)$') + with open('mod_options.txt') as fd: + lines = fd.readlines() + for line in lines: + p = regex.match(line.strip()) + if p: + conf.start_msg("* " + p[3]) + if p[2] == 'ON': + conf.env[p[1]] = True + conf.define(p[1], 1) + elif p[2] == 'OFF': + conf.env[p[1]] = False + conf.undefine(p[1]) + else: + conf.env[p[1]] = p[2] + conf.end_msg(p[2]) + if conf.env.HLDEMO_BUILD and conf.env.OEM_BUILD: + conf.fatal('Don\'t mix Demo and OEM builds!') # strip lib from pattern if conf.env.DEST_OS not in ['android']: - if conf.env.cshlib_PATTERN.startswith('lib'): - conf.env.cshlib_PATTERN = conf.env.cshlib_PATTERN[3:] if conf.env.cxxshlib_PATTERN.startswith('lib'): conf.env.cxxshlib_PATTERN = conf.env.cxxshlib_PATTERN[3:] - conf.define('BARNACLE_FIX_VISIBILITY', False) - conf.define('CLIENT_WEAPONS', True) - conf.define('CROWBAR_IDLE_ANIM', False) - conf.define('CROWBAR_DELAY_FIX', False) - conf.define('CROWBAR_FIX_RAPID_CROWBAR', False) - conf.define('GAUSS_OVERCHARGE_FIX', False) - conf.define('OEM_BUILD', False) - conf.define('HLDEMO_BUILD', False) - - conf.add_subproject(["cl_dll", "dlls"]) + conf.load('library_naming') + conf.add_subproject('dlls') + conf.add_subproject('cl_dll') def build(bld): - bld.add_subproject(["cl_dll", "dlls"]) - - + # don't clean QtCreator files and reconfigure saved options + bld.clean_files = bld.bldnode.ant_glob('**', + excl='*.user configuration.py .lock* *conf_check_*/** config.log %s/*' % Build.CACHE_DIR, + quiet=True, generator=True) + bld.add_subproject('dlls') + bld.add_subproject('cl_dll') From 9066063ce90972f67c4172683cd00e3db5319fe4 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 17 Apr 2023 05:24:42 +0300 Subject: [PATCH 009/127] server: wscript: feature parity with CMake, try to use file globs and exclude list to make maintenance easier --- dlls/wscript | 134 +++++---------------------------------------------- 1 file changed, 13 insertions(+), 121 deletions(-) diff --git a/dlls/wscript b/dlls/wscript index 33d37185..2c372154 100644 --- a/dlls/wscript +++ b/dlls/wscript @@ -6,7 +6,6 @@ from waflib import Utils import os def options(opt): - # stub return def configure(conf): @@ -16,116 +15,21 @@ def configure(conf): hlDefNode = conf.path.find_resource("./hl.def") if hlDefNode is not None: - conf.env.append_unique('LINKFLAGS', '/def:%s' % hlDefNode.abspath()) + conf.env.append_value('LINKFLAGS', '/def:%s' % hlDefNode.abspath()) else: conf.fatal("Could not find hl.def") def build(bld): - source = bld.path.parent.ant_glob([ - 'pm_shared/*.c', - ]) + excluded_files = ['mpstubb.cpp', 'stats.cpp', 'prop.cpp', 'Wxdebug.cpp'] - source += [ - 'agrunt.cpp', - 'airtank.cpp', - 'aflock.cpp', - 'animating.cpp', - 'animation.cpp', - 'apache.cpp', - 'barnacle.cpp', - 'barney.cpp', - 'bigmomma.cpp', - 'bloater.cpp', - 'bmodels.cpp', - 'bullsquid.cpp', - 'buttons.cpp', - 'cbase.cpp', - 'client.cpp', - 'combat.cpp', - 'controller.cpp', - 'crossbow.cpp', - 'crowbar.cpp', - 'defaultai.cpp', - 'doors.cpp', - 'effects.cpp', - 'egon.cpp', - 'explode.cpp', - 'flyingmonster.cpp', - 'func_break.cpp', - 'func_tank.cpp', - 'game.cpp', - 'gamerules.cpp', - 'gargantua.cpp', - 'gauss.cpp', - 'genericmonster.cpp', - 'ggrenade.cpp', - 'globals.cpp', - 'glock.cpp', - 'gman.cpp', - 'h_ai.cpp', - 'h_battery.cpp', - 'h_cine.cpp', - 'h_cycler.cpp', - 'h_export.cpp', - 'handgrenade.cpp', - 'hassassin.cpp', - 'headcrab.cpp', - 'healthkit.cpp', - 'hgrunt.cpp', - 'hornet.cpp', - 'hornetgun.cpp', - 'houndeye.cpp', - 'ichthyosaur.cpp', - 'islave.cpp', - 'items.cpp', - 'leech.cpp', - 'lights.cpp', - 'maprules.cpp', - 'monstermaker.cpp', - 'monsters.cpp', - 'monsterstate.cpp', - 'mortar.cpp', - 'mp5.cpp', - 'multiplay_gamerules.cpp', - 'nihilanth.cpp', - 'nodes.cpp', - 'observer.cpp', - 'osprey.cpp', - 'pathcorner.cpp', - 'plane.cpp', - 'plats.cpp', - 'player.cpp', - 'playermonster.cpp', - 'python.cpp', - 'rat.cpp', - 'roach.cpp', - 'rpg.cpp', - 'satchel.cpp', - 'schedule.cpp', - 'scientist.cpp', - 'scripted.cpp', - 'shotgun.cpp', - 'singleplay_gamerules.cpp', - 'skill.cpp', - 'sound.cpp', - 'soundent.cpp', - 'spectator.cpp', - 'squadmonster.cpp', - 'squeakgrenade.cpp', - 'subs.cpp', - 'talkmonster.cpp', - 'teamplay_gamerules.cpp', - 'tempmonster.cpp', - 'tentacle.cpp', - 'triggers.cpp', - 'tripmine.cpp', - 'turret.cpp', - 'util.cpp', - 'weapons.cpp', - 'world.cpp', - 'xen.cpp', - 'zombie.cpp' - ] + source = bld.path.ant_glob('**/*.cpp', excl=excluded_files) + source += bld.path.parent.ant_glob('pm_shared/*.c') + + defines = [] + if bld.env.USE_VOICEMGR: + source += bld.path.parent.ant_glob('game_shared/voice_gamemgr.cpp') + else: + defines += ['NO_VOICEGAMEMGR'] includes = [ '.', @@ -136,32 +40,20 @@ def build(bld): '../public' ] - defines = [] - - if bld.env.VOICEMGR: - source += bld.path.parent.ant_glob([ - 'game_shared/voice_gamemgr.cpp', - ]) - else: - defines += ['NO_VOICEGAMEMGR'] - - libs = [] - if bld.env.DEST_OS not in ['android', 'dos']: - install_path = os.path.join(bld.env.GAMEDIR, bld.env.SERVER_DIR) + install_path = os.path.join(bld.env.GAMEDIR, bld.env.SERVER_INSTALL_DIR) else: install_path = bld.env.PREFIX bld.shlib( source = source, - target = bld.env.SERVER_NAME + bld.env.POSTFIX, + target = bld.env.SERVER_LIBRARY_NAME + bld.env.POSTFIX, name = 'server', features = 'c cxx', includes = includes, defines = defines, - use = libs, install_path = install_path, subsystem = bld.env.MSVC_SUBSYSTEM, - idx = bld.get_taskgen_count() + idx = bld.get_taskgen_count() ) From c3522346e90127bdd9e5974010e00d18a38778f8 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 17 Apr 2023 05:23:52 +0300 Subject: [PATCH 010/127] client: wscript: feature parity with CMake, try to use file globs and exclude list to make maintenance easier --- cl_dll/wscript | 144 ++++++++++++++++++--------------------- scripts/waifulib/vgui.py | 79 +++++++++++++++++++++ 2 files changed, 144 insertions(+), 79 deletions(-) create mode 100644 scripts/waifulib/vgui.py diff --git a/cl_dll/wscript b/cl_dll/wscript index b368ebd8..e776a54d 100644 --- a/cl_dll/wscript +++ b/cl_dll/wscript @@ -6,27 +6,70 @@ from waflib import Utils import os def options(opt): - # stub - return + grp = opt.add_option_group('Client options') + + grp.add_option('--enable-vgui', action = 'store_true', dest = 'USE_VGUI', default = False, + help = 'Enable VGUI1') + grp.add_option('--enable-vgui2', action = 'store_true', dest = 'USE_VGUI2', default = False, + help = 'Enable VGUI2. UNDONE') + grp.add_option('--enable-novgui-motd', action = 'store_true', dest = 'USE_NOVGUI_MOTD', default = False, + help = 'Prefer non-VGUI MOTD when USE_VGUI is enabled') + grp.add_option('--enable-novgui-scoreboard', action = 'store_true', dest = 'USE_NOVGUI_SCOREBOARD', default = False, + help = 'Prefer non-VGUI Scoreboard when USE_VGUI is enabled') + grp.add_option('--disable-goldsrc-support', action = 'store_false', dest = 'GOLDSOURCE_SUPPORT', + default=True, help = 'disable GoldSource compatibility') + + opt.load('vgui') def configure(conf): - if conf.env.DEST_OS == 'win32': - conf.check_cxx(lib='user32') - - if conf.env.GOLDSRC: - if conf.env.DEST_OS == 'win32': - conf.check_cxx(lib='winmm') - else: - conf.check_cc(lib='dl') + conf.env.USE_VGUI = conf.options.USE_VGUI + conf.env.USE_NOVGUI_MOTD = conf.options.USE_NOVGUI_MOTD + conf.env.USE_NOVGUI_SCOREBOARD = conf.options.USE_NOVGUI_SCOREBOARD + conf.env.USE_VOICEMGR = conf.options.USE_VOICEMGR + conf.env.GOLDSOURCE_SUPPORT = conf.options.GOLDSOURCE_SUPPORT + if conf.env.USE_VGUI: + conf.load('vgui') + if not conf.check_vgui(): + conf.fatal('VGUI was enabled but VGUI cannot be used') def build(bld): - source = bld.path.parent.ant_glob([ - 'pm_shared/*.c' - ]) - source += bld.path.ant_glob([ - 'hl/*.cpp' - ]) + libs = [] + defines = ['CLIENT_DLL'] + includes = ['.', + '../dlls', + '../common', + '../engine', + '../pm_shared', + '../game_shared', + '../public'] + excluded_files = ['GameStudioModelRenderer_Sample.cpp', + 'game_shared/voice_vgui_tweakdlg.cpp', + 'game_shared/voice_gamemgr.cpp', + 'game_shared/voice_status.cpp'] + + if bld.env.USE_VGUI: + defines += ['USE_VGUI'] + libs += ['VGUI'] + if bld.env.USE_NOVGUI_MOTD: + defines += ['USE_NOVGUI_MOTD'] + else: + excluded_files += ['MOTD.cpp'] + + if bld.env.USE_NOVGUI_SCOREBOARD: + defines += ['USE_NOVGUI_SCOREBOARD'] + else: + excluded_files += ['scoreboard.cpp'] + else: + includes += ['../utils/fake_vgui/include'] + excluded_files += ['voice_status.cpp', + 'vgui_*.cpp', + 'game_shared/vgui_*.cpp', + 'game_shared/voice_banmgr.cpp'] + + source = bld.path.ant_glob('**/*.cpp', excl=excluded_files) + source += bld.path.parent.ant_glob('game_shared/*.cpp', excl=excluded_files) source += bld.path.parent.ant_glob([ + 'pm_shared/*.c', 'dlls/crossbow.cpp', 'dlls/crowbar.cpp', 'dlls/egon.cpp', @@ -42,78 +85,21 @@ def build(bld): 'dlls/squeakgrenade.cpp', 'dlls/tripmine.cpp' ]) - source += [ - 'GameStudioModelRenderer.cpp', - 'MOTD.cpp', - 'StudioModelRenderer.cpp', - 'ammo.cpp', - 'ammo_secondary.cpp', - 'ammohistory.cpp', - 'battery.cpp', - 'cdll_int.cpp', - 'com_weapons.cpp', - 'death.cpp', - 'demo.cpp', - 'entity.cpp', - 'ev_hldm.cpp', - 'ev_common.cpp', - 'events.cpp', - 'flashlight.cpp', - 'geiger.cpp', - 'health.cpp', - 'hud.cpp', - 'hud_msg.cpp', - 'hud_redraw.cpp', - 'hud_spectator.cpp', - 'hud_update.cpp', - 'in_camera.cpp', - 'input.cpp', - 'input_goldsource.cpp', - 'input_mouse.cpp', - 'input_xash3d.cpp', - 'menu.cpp', - 'message.cpp', - 'parsemsg.cpp', - 'saytext.cpp', - 'scoreboard.cpp', - 'status_icons.cpp', - 'statusbar.cpp', - 'studio_util.cpp', - 'text_message.cpp', - 'train.cpp', - 'tri.cpp', - 'util.cpp', - 'view.cpp' - ] - includes = [ - '.', - 'hl/', - '../dlls', - '../common', - '../engine', - '../pm_shared', - '../game_shared', - '../public', - '../utils/fake_vgui/include' - ] - defines = ['CLIENT_DLL'] - if bld.env.GOLDSRC: + if bld.env.DEST_OS == 'win32': + libs += ['USER32'] + + if bld.env.GOLDSOURCE_SUPPORT: defines += ['GOLDSOURCE_SUPPORT'] - libs = [] - if bld.env.DEST_OS == 'win32': - libs += ["USER32"] - - if bld.env.GOLDSRC: if bld.env.DEST_OS == 'win32': libs += ["WINMM"] else: libs += ['DL'] if bld.env.DEST_OS not in ['android', 'dos']: - install_path = os.path.join(bld.env.GAMEDIR, bld.env.CLIENT_DIR) + install_path = os.path.join(bld.env.GAMEDIR, bld.env.CLIENT_INSTALL_DIR) else: install_path = bld.env.PREFIX @@ -127,6 +113,6 @@ def build(bld): use = libs, install_path = install_path, subsystem = bld.env.MSVC_SUBSYSTEM, - idx = bld.get_taskgen_count() + idx = bld.get_taskgen_count() ) diff --git a/scripts/waifulib/vgui.py b/scripts/waifulib/vgui.py new file mode 100644 index 00000000..7f1b5a68 --- /dev/null +++ b/scripts/waifulib/vgui.py @@ -0,0 +1,79 @@ +#! /usr/bin/env python +# encoding: utf-8 +# mittorn, 2018 + +from waflib.Configure import conf +import os + +VGUI_SUPPORTED_OS = ['win32', 'darwin', 'linux'] + +VGUI_FRAGMENT = '''#include +int main() { return 0; }''' + +def options(opt): + grp = opt.add_option_group('VGUI options') + + vgui_dev_path = os.path.join(opt.path.path_from(opt.path), 'vgui_support', 'vgui-dev') + + grp.add_option('--vgui', action = 'store', dest = 'VGUI_DEV', default=vgui_dev_path, + help = 'path to vgui-dev repo [default: %default]') + + grp.add_option('--skip-vgui-sanity-check', action = 'store_false', dest = 'VGUI_SANITY_CHECK', default=True, + help = 'skip checking VGUI sanity [default: %default]' ) + return + +@conf +def check_vgui(conf): + conf.start_msg('Does this architecture support VGUI?') + + if conf.env.DEST_CPU != 'x86' and not (conf.env.DEST_CPU == 'x86_64' and not conf.options.ALLOW64): + conf.end_msg('no') + Logs.warn('vgui is not supported on this CPU: ' + str(conf.env.DEST_CPU)) + return False + else: conf.end_msg('yes') + + conf.start_msg('Does this OS support VGUI?') + if conf.env.DEST_OS not in VGUI_SUPPORTED_OS: + conf.end_msg('no') + Logs.warn('vgui is not supported on this OS: ' + str(conf.env.DEST_OS)) + return False + else: conf.end_msg('yes') + + conf.start_msg('Does this toolchain able to link VGUI?') + if conf.env.DEST_OS == 'win32' and conf.env.COMPILER_CXX == 'g++': + conf.end_msg('no') + # we have ABI incompatibility ONLY on MinGW + Logs.warn('vgui can\'t be linked with MinGW') + return False + else: conf.end_msg('yes') + + conf.start_msg('Configuring VGUI by provided path') + vgui_dev = conf.options.VGUI_DEV + + if conf.env.DEST_OS == 'win32': + conf.env.LIB_VGUI = ['vgui'] + conf.env.LIBPATH_VGUI = [os.path.abspath(os.path.join(vgui_dev, 'lib/win32_vc6/'))] + else: + libpath = os.path.abspath(os.path.join(vgui_dev, 'lib')) + if conf.env.DEST_OS == 'linux': + conf.env.LIB_VGUI = [':vgui.so'] + conf.env.LIBPATH_VGUI = [libpath] + elif conf.env.DEST_OS == 'darwin': + conf.env.LDFLAGS_VGUI = [os.path.join(libpath, 'vgui.dylib')] + else: + conf.fatal('vgui is not supported on this OS: ' + conf.env.DEST_OS) + conf.env.INCLUDES_VGUI = [os.path.abspath(os.path.join(vgui_dev, 'include'))] + + conf.env.HAVE_VGUI = 1 + conf.end_msg('yes: {0}, {1}, {2}'.format(conf.env.LIB_VGUI, conf.env.LIBPATH_VGUI, conf.env.INCLUDES_VGUI)) + + if conf.env.HAVE_VGUI and conf.options.VGUI_SANITY_CHECK: + try: + conf.check_cxx(fragment=VGUI_FRAGMENT, + msg = 'Checking for library VGUI sanity', + use = 'VGUI', + execute = False) + except conf.errors.ConfigurationError: + conf.fatal("Can't compile simple program. Check your path to vgui-dev repository.") + + return True From 7365d43aefdb0340b062384faf49eb908cbcf6b8 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 17 Apr 2023 06:27:58 +0300 Subject: [PATCH 011/127] public: build: synchronize with engine --- public/build.h | 185 +++++++++++++++++++++---------------------------- 1 file changed, 78 insertions(+), 107 deletions(-) diff --git a/public/build.h b/public/build.h index 4e4fe110..636d97a4 100644 --- a/public/build.h +++ b/public/build.h @@ -30,15 +30,22 @@ For more information, please refer to #ifndef BUILD_H #define BUILD_H -// All XASH_* macros set by this header are guaranteed to have positive value otherwise not defined +/* +All XASH_* macros set by this header are guaranteed to have positive value +otherwise not defined. -// Any new define must be undefined at first -// You can generate #undef list below with this oneliner: -// $ cat build.h | sed 's/\t//g' | grep '^#define XASH' | awk '{ print $2 }' | sort | uniq | awk '{ print "#undef " $1 }' -// -// So in various buildscripts you can grep for ^#undef XASH and select only second word -// or in another oneliner: -// $ cat build.h | grep '^#undef XASH' | awk '{ print $2 }' +Every macro is intended to be the unified interface for buildsystems that lack +platform & CPU detection, and a neat quick way for checks in platform code +For Q_build* macros, refer to buildenums.h + +Any new define must be undefined at first +You can generate #undef list below with this oneliner: + $ sed 's/\t//g' build.h | grep '^#define XASH' | awk '{ print $2 }' | \ + sort | uniq | awk '{ print "#undef " $1 }' + +Then you can use another oneliner to query all variables: + $ grep '^#undef XASH' build.h | awk '{ print $2 }' +*/ #undef XASH_64BIT #undef XASH_AMD64 @@ -53,21 +60,19 @@ For more information, please refer to #undef XASH_ARMv7 #undef XASH_ARMv8 #undef XASH_BIG_ENDIAN -#undef XASH_BSD #undef XASH_DOS4GW #undef XASH_E2K #undef XASH_EMSCRIPTEN #undef XASH_FREEBSD #undef XASH_HAIKU #undef XASH_IOS +#undef XASH_IRIX #undef XASH_JS #undef XASH_LINUX +#undef XASH_LINUX_UNKNOWN #undef XASH_LITTLE_ENDIAN -#undef XASH_MINGW #undef XASH_MIPS -#undef XASH_PPC #undef XASH_MOBILE_PLATFORM -#undef XASH_MSVC #undef XASH_NETBSD #undef XASH_OPENBSD #undef XASH_POSIX @@ -77,74 +82,63 @@ For more information, please refer to #undef XASH_RISCV_SOFTFP #undef XASH_SERENITY #undef XASH_WIN32 -#undef XASH_WIN64 #undef XASH_X86 #undef XASH_NSWITCH #undef XASH_PSVITA //================================================================ // -// OPERATING SYSTEM DEFINES +// PLATFORM DETECTION CODE // //================================================================ -#if defined(_WIN32) +#if defined _WIN32 #define XASH_WIN32 1 - #if defined(__MINGW32__) - #define XASH_MINGW 1 - #elif defined(_MSC_VER) - #define XASH_MSVC 1 - #endif - - #if defined(_WIN64) - #define XASH_WIN64 1 - #endif -#elif defined __SWITCH__ - #define XASH_NSWITCH 1 - #define XASH_LITTLE_ENDIAN 1 - #define XASH_POSIX 1 -#elif defined __vita__ - #define XASH_PSVITA 1 - #define XASH_LITTLE_ENDIAN 1 - #define XASH_POSIX 1 -#elif defined(__linux__) - #define XASH_LINUX 1 - #if defined(__ANDROID__) - #define XASH_ANDROID 1 - #endif // defined(__ANDROID__) - #define XASH_POSIX 1 -#elif defined(__APPLE__) - #include - #define XASH_APPLE 1 - #if TARGET_OS_IOS - #define XASH_IOS 1 - #endif // TARGET_OS_IOS - #define XASH_POSIX 1 -#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - #define XASH_BSD 1 - #if defined(__FreeBSD__) - #define XASH_FREEBSD 1 - #elif defined(__NetBSD__) - #define XASH_NETBSD 1 - #elif defined(__OpenBSD__) - #define XASH_OPENBSD 1 - #endif - #define XASH_POSIX 1 #elif defined __EMSCRIPTEN__ #define XASH_EMSCRIPTEN 1 #elif defined __WATCOMC__ && defined __DOS__ #define XASH_DOS4GW 1 - #define XASH_LITTLE_ENDIAN 1 -#elif defined __HAIKU__ - #define XASH_HAIKU 1 +#else // POSIX compatible #define XASH_POSIX 1 -#elif defined __serenity__ - #define XASH_SERENITY 1 - #define XASH_POSIX 1 -#else -#error "Place your operating system name here! If this is a mistake, try to fix conditions above and report a bug" + #if defined __linux__ + #if defined __ANDROID__ + #define XASH_ANDROID 1 + #else + #include + // if our system libc has features.h header + // try to detect it to not confuse other libcs with built with glibc game libraries + #if !defined __GLIBC__ + #define XASH_LINUX_UNKNOWN 1 + #endif + #endif + #define XASH_LINUX 1 + #elif defined __FreeBSD__ + #define XASH_FREEBSD 1 + #elif defined __NetBSD__ + #define XASH_NETBSD 1 + #elif defined __OpenBSD__ + #define XASH_OPENBSD 1 + #elif defined __HAIKU__ + #define XASH_HAIKU 1 + #elif defined __serenity__ + #define XASH_SERENITY 1 + #elif defined __sgi + #define XASH_IRIX 1 + #elif defined __APPLE__ + #include + #define XASH_APPLE 1 + #if TARGET_OS_IOS + #define XASH_IOS 1 + #endif // TARGET_OS_IOS + #elif defined __SWITCH__ + #define XASH_NSWITCH 1 + #elif defined __vita__ + #define XASH_PSVITA 1 + #else + #error + #endif #endif -#if defined XASH_ANDROID || defined XASH_IOS || defined XASH_NSWITCH || defined XASH_PSVITA +#if XASH_ANDROID || defined XASH_IOS || defined XASH_NSWITCH || defined XASH_PSVITA #define XASH_MOBILE_PLATFORM 1 #endif @@ -154,27 +148,17 @@ For more information, please refer to // //================================================================ -#if defined(XASH_FORCE_LITTLE_ENDIAN) && defined(XASH_FORCE_BIG_ENDIAN) - #error "Both XASH_FORCE_LITTLE_ENDIAN and XASH_FORCE_BIG_ENDIAN are defined" -#elif defined(XASH_FORCE_LITTLE_ENDIAN) - #define XASH_LITTLE_ENDIAN 1 -#elif defined(XASH_FORCE_BIG_ENDIAN) - #define XASH_BIG_ENDIAN 1 -#endif - -#if !defined(XASH_LITTLE_ENDIAN) && !defined(XASH_BIG_ENDIAN) - #if defined XASH_MSVC || __LITTLE_ENDIAN__ +#if !defined XASH_ENDIANNESS + #if defined XASH_WIN32 || __LITTLE_ENDIAN__ //!!! Probably all WinNT installations runs in little endian #define XASH_LITTLE_ENDIAN 1 #elif __BIG_ENDIAN__ #define XASH_BIG_ENDIAN 1 - #elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && defined(__ORDER_LITTLE_ENDIAN__) // some compilers define this + #elif defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__ && defined __ORDER_LITTLE_ENDIAN__ // some compilers define this #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define XASH_BIG_ENDIAN 1 #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define XASH_LITTLE_ENDIAN 1 - #else - #error "Unknown endianness!" #endif #else #include @@ -182,8 +166,6 @@ For more information, please refer to #define XASH_BIG_ENDIAN 1 #elif __BYTE_ORDER == __LITTLE_ENDIAN #define XASH_LITTLE_ENDIAN 1 - #else - #error "Unknown endianness!" #endif #endif // !XASH_WIN32 #endif @@ -193,18 +175,28 @@ For more information, please refer to // CPU ARCHITECTURE DEFINES // //================================================================ -#if defined(__x86_64__) || defined(_M_X64) +#if defined __x86_64__ || defined _M_X64 #define XASH_64BIT 1 #define XASH_AMD64 1 -#elif defined(__i386__) || defined(_X86_) || defined(_M_IX86) +#elif defined __i386__ || defined _X86_ || defined _M_IX86 #define XASH_X86 1 #elif defined __aarch64__ || defined _M_ARM64 #define XASH_64BIT 1 - #define XASH_ARM 8 -#elif defined __arm__ || defined _M_ARM + #define XASH_ARM 8 +#elif defined __mips__ + #define XASH_MIPS 1 +#elif defined __EMSCRIPTEN__ + #define XASH_JS 1 +#elif defined __e2k__ + #define XASH_64BIT 1 + #define XASH_E2K 1 +#elif defined _M_ARM // msvc + #define XASH_ARM 7 + #define XASH_ARM_HARDFP 1 +#elif defined __arm__ #if __ARM_ARCH == 8 || __ARM_ARCH_8__ #define XASH_ARM 8 - #elif __ARM_ARCH == 7 || __ARM_ARCH_7__ || defined _M_ARM // msvc can only armv7 in 32 bit + #elif __ARM_ARCH == 7 || __ARM_ARCH_7__ #define XASH_ARM 7 #elif __ARM_ARCH == 6 || __ARM_ARCH_6__ || __ARM_ARCH_6J__ #define XASH_ARM 6 @@ -216,34 +208,17 @@ For more information, please refer to #error "Unknown ARM" #endif - #if defined _M_ARM - #error "No WinMobile port yet! Need to determine which ARM float ABI msvc uses if applicable" - #endif - #if defined __SOFTFP__ || __ARM_PCS_VFP == 0 #define XASH_ARM_SOFTFP 1 #else // __SOFTFP__ #define XASH_ARM_HARDFP 1 #endif // __SOFTFP__ -#elif defined __mips__ - #define XASH_MIPS 1 -#elif defined __powerpc__ - #define XASH_PPC 1 - #if defined __powerpc64__ - #define XASH_64BIT 1 - #endif -#elif defined __EMSCRIPTEN__ - #define XASH_JS 1 -#elif defined __e2k__ - #define XASH_64BIT 1 - #define XASH_E2K 1 #elif defined __riscv #define XASH_RISCV 1 + #if __riscv_xlen == 64 #define XASH_64BIT 1 - #elif __riscv_xlen == 32 - // ... - #else + #elif __riscv_xlen != 32 #error "Unknown RISC-V ABI" #endif @@ -260,10 +235,6 @@ For more information, please refer to #error "Place your architecture name here! If this is a mistake, try to fix conditions above and report a bug" #endif -#if defined(XASH_WAF_DETECTED_64BIT) && !defined(XASH_64BIT) - #define XASH_64BIT 1 -#endif - #if XASH_ARM == 8 #define XASH_ARMv8 1 #elif XASH_ARM == 7 From 09d3e6327d0bb223e597d2d73473a32f9bd1d30a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 17 Apr 2023 06:30:08 +0300 Subject: [PATCH 012/127] cmake: LibraryNaming: greatly speedup common configurations --- cmake/LibraryNaming.cmake | 118 ++++++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 43 deletions(-) diff --git a/cmake/LibraryNaming.cmake b/cmake/LibraryNaming.cmake index 3c2b8968..5c444e23 100644 --- a/cmake/LibraryNaming.cmake +++ b/cmake/LibraryNaming.cmake @@ -1,49 +1,81 @@ include(CheckSymbolExists) -# generated(see comments in public/build.h) -# cat build.h | grep '^#undef XASH' | awk '{ print "check_symbol_exists(" $2 " \"build.h\" " $2 ")" }' +macro(check_build_target symbol) + check_symbol_exists(${symbol} "build.h" ${symbol}) +endmacro() + +macro(check_group_build_target symbol group) + if(NOT ${group}) + check_build_target(${symbol}) + if(${symbol}) + set(${group} TRUE) + endif() + else() + set(${symbol} FALSE) + endif() +endmacro() + +# So there is a problem: +# 1. Number of these symbols only grows, as we support more and more ports +# 2. CMake was written by morons and can't check these symbols in parallel +# 3. MSVC is very slow at everything (startup, parsing, generating error) + +# Solution: group these symbols and set variable if one of them was found +# this way we can reorder to reorder them by most common configurations +# but we can't generate this list anymore! ... OR IS IT ??? + +# Well, after reordering positions in engine's buildenums.h, we can partially autogenerate this list! +# echo "check_build_target(XASH_64BIT)" +# grep "#define PLATFORM" buildenums.h | cut -d' ' -f 2 | cut -d_ -f 2- | awk '{ print "check_group_build_target(XASH_" $1 " XASH_PLATFORM)" }' +# grep "#define ARCHITECTURE" buildenums.h | cut -d' ' -f 2 | cut -d_ -f 2- | awk '{ print "check_group_build_target(XASH_" $1 " XASH_ARCHITECTURE)" +# grep "#define ENDIAN" buildenums.h | cut -d' ' -f 2 | cut -d_ -f 2- | awk '{ print "check_group_build_target(XASH_" $1 "_ENDIAN XASH_ENDIANNESS)"}' +# echo "if(XASH_ARM)" +# grep '^#undef XASH' build.h | grep "XASH_ARM[v_]" | awk '{ print "check_build_target(" $2 ")"}' +# echo "endif()" +# echo "if(XASH_RISCV)" +# grep '^#undef XASH' build.h | grep "XASH_RISCV_" | awk '{ print "check_build_target(" $2 ")"}' +# echo "endif()" + set(CMAKE_REQUIRED_INCLUDES "${PROJECT_SOURCE_DIR}/public/") -check_symbol_exists(XASH_64BIT "build.h" XASH_64BIT) -check_symbol_exists(XASH_AMD64 "build.h" XASH_AMD64) -check_symbol_exists(XASH_ANDROID "build.h" XASH_ANDROID) -check_symbol_exists(XASH_APPLE "build.h" XASH_APPLE) -check_symbol_exists(XASH_ARM "build.h" XASH_ARM) -check_symbol_exists(XASH_ARM_HARDFP "build.h" XASH_ARM_HARDFP) -check_symbol_exists(XASH_ARM_SOFTFP "build.h" XASH_ARM_SOFTFP) -check_symbol_exists(XASH_ARMv4 "build.h" XASH_ARMv4) -check_symbol_exists(XASH_ARMv5 "build.h" XASH_ARMv5) -check_symbol_exists(XASH_ARMv6 "build.h" XASH_ARMv6) -check_symbol_exists(XASH_ARMv7 "build.h" XASH_ARMv7) -check_symbol_exists(XASH_ARMv8 "build.h" XASH_ARMv8) -check_symbol_exists(XASH_BIG_ENDIAN "build.h" XASH_BIG_ENDIAN) -check_symbol_exists(XASH_BSD "build.h" XASH_BSD) -check_symbol_exists(XASH_DOS4GW "build.h" XASH_DOS4GW) -check_symbol_exists(XASH_E2K "build.h" XASH_E2K) -check_symbol_exists(XASH_EMSCRIPTEN "build.h" XASH_EMSCRIPTEN) -check_symbol_exists(XASH_FREEBSD "build.h" XASH_FREEBSD) -check_symbol_exists(XASH_HAIKU "build.h" XASH_HAIKU) -check_symbol_exists(XASH_IOS "build.h" XASH_IOS) -check_symbol_exists(XASH_JS "build.h" XASH_JS) -check_symbol_exists(XASH_LINUX "build.h" XASH_LINUX) -check_symbol_exists(XASH_LITTLE_ENDIAN "build.h" XASH_LITTLE_ENDIAN) -check_symbol_exists(XASH_MINGW "build.h" XASH_MINGW) -check_symbol_exists(XASH_MIPS "build.h" XASH_MIPS) -check_symbol_exists(XASH_PPC "build.h" XASH_PPC) -check_symbol_exists(XASH_MOBILE_PLATFORM "build.h" XASH_MOBILE_PLATFORM) -check_symbol_exists(XASH_MSVC "build.h" XASH_MSVC) -check_symbol_exists(XASH_NETBSD "build.h" XASH_NETBSD) -check_symbol_exists(XASH_OPENBSD "build.h" XASH_OPENBSD) -check_symbol_exists(XASH_POSIX "build.h" XASH_POSIX) -check_symbol_exists(XASH_RISCV "build.h" XASH_RISCV) -check_symbol_exists(XASH_RISCV_DOUBLEFP "build.h" XASH_RISCV_DOUBLEFP) -check_symbol_exists(XASH_RISCV_SINGLEFP "build.h" XASH_RISCV_SINGLEFP) -check_symbol_exists(XASH_RISCV_SOFTFP "build.h" XASH_RISCV_SOFTFP) -check_symbol_exists(XASH_SERENITY "build.h" XASH_SERENITY) -check_symbol_exists(XASH_WIN32 "build.h" XASH_WIN32) -check_symbol_exists(XASH_WIN64 "build.h" XASH_WIN64) -check_symbol_exists(XASH_X86 "build.h" XASH_X86) -check_symbol_exists(XASH_NSWITCH "build.h" XASH_NSWITCH) -check_symbol_exists(XASH_PSVITA "build.h" XASH_PSVITA) +check_build_target(XASH_64BIT) +check_group_build_target(XASH_WIN32 XASH_PLATFORM) +check_group_build_target(XASH_LINUX XASH_PLATFORM) +check_group_build_target(XASH_FREEBSD XASH_PLATFORM) +check_group_build_target(XASH_ANDROID XASH_PLATFORM) +check_group_build_target(XASH_APPLE XASH_PLATFORM) +check_group_build_target(XASH_NETBSD XASH_PLATFORM) +check_group_build_target(XASH_OPENBSD XASH_PLATFORM) +check_group_build_target(XASH_EMSCRIPTEN XASH_PLATFORM) +check_group_build_target(XASH_DOS4GW XASH_PLATFORM) +check_group_build_target(XASH_HAIKU XASH_PLATFORM) +check_group_build_target(XASH_SERENITY XASH_PLATFORM) +check_group_build_target(XASH_IRIX XASH_PLATFORM) +check_group_build_target(XASH_NSWITCH XASH_PLATFORM) +check_group_build_target(XASH_PSVITA XASH_PLATFORM) +check_group_build_target(XASH_LINUX_UNKNOWN XASH_PLATFORM) +check_group_build_target(XASH_X86 XASH_ARCHITECTURE) +check_group_build_target(XASH_AMD64 XASH_ARCHITECTURE) +check_group_build_target(XASH_ARM XASH_ARCHITECTURE) +check_group_build_target(XASH_MIPS XASH_ARCHITECTURE) +check_group_build_target(XASH_JS XASH_ARCHITECTURE) +check_group_build_target(XASH_E2K XASH_ARCHITECTURE) +check_group_build_target(XASH_RISCV XASH_ARCHITECTURE) +check_group_build_target(XASH_LITTLE_ENDIAN XASH_ENDIANNESS) +check_group_build_target(XASH_BIG_ENDIAN XASH_ENDIANNESS) +if(XASH_ARM) +check_build_target(XASH_ARM_HARDFP) +check_build_target(XASH_ARM_SOFTFP) +check_build_target(XASH_ARMv4) +check_build_target(XASH_ARMv5) +check_build_target(XASH_ARMv6) +check_build_target(XASH_ARMv7) +check_build_target(XASH_ARMv8) +endif() +if(XASH_RISCV) +check_build_target(XASH_RISCV_DOUBLEFP) +check_build_target(XASH_RISCV_SINGLEFP) +check_build_target(XASH_RISCV_SOFTFP) +endif() unset(CMAKE_REQUIRED_INCLUDES) # engine/common/build.c From 399f7213e9dfb22d98f2d5f13e0ec67faf7f67e5 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 17 Apr 2023 19:19:26 +0300 Subject: [PATCH 013/127] LibraryNaming: add IRIX, linuxunkabi definitions, remove unused PowerPC definitions (until somebody ports new engine to PPC) --- cmake/LibraryNaming.cmake | 13 ++++--------- scripts/waifulib/library_naming.py | 17 ++++++----------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/cmake/LibraryNaming.cmake b/cmake/LibraryNaming.cmake index 5c444e23..e233a7de 100644 --- a/cmake/LibraryNaming.cmake +++ b/cmake/LibraryNaming.cmake @@ -81,6 +81,8 @@ unset(CMAKE_REQUIRED_INCLUDES) # engine/common/build.c if(XASH_ANDROID) set(BUILDOS "android") +elseif(XASH_LINUX_UNKNOWN) + set(BUILDOS "linuxunkabi") elseif(XASH_WIN32 OR XASH_LINUX OR XASH_APPLE) set(BUILDOS "") # no prefix for default OS elseif(XASH_FREEBSD) @@ -101,6 +103,8 @@ elseif(XASH_NSWITCH) set(BUILDOS "nswitch") elseif(XASH_PSVITA) set(BUILDOS "psvita") +elseif(XASH_IRIX) + set(BUILDOS "irix") else() message(SEND_ERROR "Place your operating system name here! If this is a mistake, try to fix conditions above and report a bug") endif() @@ -145,15 +149,6 @@ elseif(XASH_MIPS) if(XASH_LITTLE_ENDIAN) set(BUILDARCH "${BUILDARCH}el") endif() -elseif(XASH_PPC) - set(BUILDARCH "powerpc") - if(XASH_64BIT) - set(BUILDARCH "${BUILDARCH}64") - endif() - - if(XASH_LITTLE_ENDIAN) - set(BUILDARCH "${BUILDARCH}le") - endif() elseif(XASH_RISCV) set(BUILDARCH "riscv") if(XASH_64BIT) diff --git a/scripts/waifulib/library_naming.py b/scripts/waifulib/library_naming.py index b8985c92..78acafe9 100644 --- a/scripts/waifulib/library_naming.py +++ b/scripts/waifulib/library_naming.py @@ -35,21 +35,19 @@ DEFINES = [ 'XASH_ARMv7', 'XASH_ARMv8', 'XASH_BIG_ENDIAN', -'XASH_BSD', 'XASH_DOS4GW', 'XASH_E2K', 'XASH_EMSCRIPTEN', 'XASH_FREEBSD', 'XASH_HAIKU', 'XASH_IOS', +'XASH_IRIX', 'XASH_JS', 'XASH_LINUX', +'XASH_LINUX_UNKNOWN', 'XASH_LITTLE_ENDIAN', -'XASH_MINGW', 'XASH_MIPS', -'XASH_PPC', 'XASH_MOBILE_PLATFORM', -'XASH_MSVC', 'XASH_NETBSD', 'XASH_OPENBSD', 'XASH_POSIX', @@ -59,7 +57,6 @@ DEFINES = [ 'XASH_RISCV_SOFTFP', 'XASH_SERENITY', 'XASH_WIN32', -'XASH_WIN64', 'XASH_X86', 'XASH_NSWITCH', 'XASH_PSVITA', @@ -78,6 +75,8 @@ def configure(conf): # engine/common/build.c if conf.env.XASH_ANDROID: buildos = "android" + elif conf.env.XASH_LINUX_UNKNOWN: + buildos = "linuxunkabi" elif conf.env.XASH_WIN32 or conf.env.XASH_LINUX or conf.env.XASH_APPLE: buildos = "" # no prefix for default OS elif conf.env.XASH_FREEBSD: @@ -98,6 +97,8 @@ def configure(conf): buildos = "nswitch" elif conf.env.XASH_PSVITA: buildos = "psvita" + elif conf.env.XASH_IRIX: + buildos = "irix" else: conf.fatal("Place your operating system name in build.h and library_naming.py!\n" "If this is a mistake, try to fix conditions above and report a bug") @@ -136,12 +137,6 @@ def configure(conf): buildarch += "64" if conf.env.XASH_LITTLE_ENDIAN: buildarch += "el" - elif conf.env.XASH_PPC: - buildarch = "powerpc" - if conf.env.XASH_64BIT: - buildarch += "64" - if conf.env.XASH_LITTLE_ENDIAN: - buildarch += "le" elif conf.env.XASH_RISCV: buildarch = "riscv" if conf.env.XASH_64BIT: From 036db21bc194d2b1a738a9261f5c9ce7020a33ed Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 21 Apr 2023 03:09:32 +0300 Subject: [PATCH 014/127] wscript: fix MSVC_TARGETS defined after XP compatibility check was made (thus fixing IndexOutOfRange error) --- wscript | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/wscript b/wscript index 46cc1510..353f9016 100644 --- a/wscript +++ b/wscript @@ -36,6 +36,7 @@ def options(opt): def configure(conf): conf.load('fwgslib reconfigure compiler_optimizations') + conf.env.MSVC_TARGETS = ['x86' if not conf.options.ALLOW64 else 'x64'] # Force XP compatibility, all build targets should add subsystem=bld.env.MSVC_SUBSYSTEM if conf.env.MSVC_TARGETS[0] == 'x86': @@ -43,8 +44,6 @@ def configure(conf): else: conf.env.MSVC_SUBSYSTEM = 'WINDOWS' - conf.env.MSVC_TARGETS = ['x86' if not conf.options.ALLOW64 else 'x64'] - # Load compilers early conf.load('xcompile compiler_c compiler_cxx') From aec0f79806cacdb1861758ade170d33e75f9c8b1 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 10 Apr 2023 02:25:44 +0500 Subject: [PATCH 015/127] Change bunnyhop cap via movevar. --- cl_dll/cdll_int.cpp | 12 ------------ dlls/client.cpp | 13 ------------- dlls/game.h | 1 + dlls/player.cpp | 15 +-------------- dlls/player.h | 2 -- pm_shared/pm_shared.c | 9 ++++++--- 6 files changed, 8 insertions(+), 44 deletions(-) diff --git a/cl_dll/cdll_int.cpp b/cl_dll/cdll_int.cpp index 17fc06f3..57f879d7 100644 --- a/cl_dll/cdll_int.cpp +++ b/cl_dll/cdll_int.cpp @@ -50,20 +50,10 @@ TeamFortressViewport *gViewPort = NULL; #endif mobile_engfuncs_t *gMobileEngfuncs = NULL; -extern "C" int g_bhopcap; void InitInput( void ); void EV_HookEvents( void ); void IN_Commands( void ); -int __MsgFunc_Bhopcap( const char *pszName, int iSize, void *pbuf ) -{ - BEGIN_READ( pbuf, iSize ); - - g_bhopcap = READ_BYTE(); - - return 1; -} - /* ========================== Initialize @@ -299,8 +289,6 @@ void DLLEXPORT HUD_Init( void ) #if USE_VGUI Scheme_Init(); #endif - - gEngfuncs.pfnHookUserMsg( "Bhopcap", __MsgFunc_Bhopcap ); } /* diff --git a/dlls/client.cpp b/dlls/client.cpp index 53786de7..179719bf 100644 --- a/dlls/client.cpp +++ b/dlls/client.cpp @@ -48,16 +48,12 @@ extern DLL_GLOBAL ULONG g_ulFrameCount; extern void CopyToBodyQue( entvars_t* pev ); extern int giPrecacheGrunt; extern int gmsgSayText; -extern int gmsgBhopcap; extern cvar_t allow_spectators; extern cvar_t multibyte_only; extern int g_teamplay; -extern cvar_t bhopcap; -extern "C" int g_bhopcap; - void LinkUserMessages( void ); /* @@ -822,15 +818,6 @@ void StartFrame( void ) gpGlobals->teamplay = teamplay.value; g_ulFrameCount++; - - int oldBhopcap = g_bhopcap; - g_bhopcap = ( g_pGameRules && g_pGameRules->IsMultiplayer() && bhopcap.value != 0.0f ) ? 1 : 0; - if( g_bhopcap != oldBhopcap ) - { - MESSAGE_BEGIN( MSG_ALL, gmsgBhopcap, NULL ); - WRITE_BYTE( g_bhopcap ); - MESSAGE_END(); - } } void ClientPrecache( void ) diff --git a/dlls/game.h b/dlls/game.h index fee1a71b..5f7bee1c 100644 --- a/dlls/game.h +++ b/dlls/game.h @@ -42,6 +42,7 @@ extern cvar_t teamlist; extern cvar_t teamoverride; extern cvar_t defaultteam; extern cvar_t allowmonsters; +extern cvar_t bhopcap; // Engine Cvars extern cvar_t *g_psv_gravity; diff --git a/dlls/player.cpp b/dlls/player.cpp index 63e11283..76f0ed7c 100644 --- a/dlls/player.cpp +++ b/dlls/player.cpp @@ -45,8 +45,6 @@ extern DLL_GLOBAL BOOL g_fDrawLines; int gEvilImpulse101; extern DLL_GLOBAL int g_iSkillLevel, gDisplayTitle; -extern "C" int g_bhopcap; - BOOL gInitHUD = TRUE; extern void CopyToBodyQue( entvars_t *pev); @@ -183,7 +181,6 @@ int gmsgSetFOV = 0; int gmsgShowMenu = 0; int gmsgGeigerRange = 0; int gmsgTeamNames = 0; -int gmsgBhopcap = 0; int gmsgStatusText = 0; int gmsgStatusValue = 0; @@ -230,7 +227,6 @@ void LinkUserMessages( void ) gmsgFade = REG_USER_MSG( "ScreenFade", sizeof(ScreenFade) ); gmsgAmmoX = REG_USER_MSG( "AmmoX", 2 ); gmsgTeamNames = REG_USER_MSG( "TeamNames", -1 ); - gmsgBhopcap = REG_USER_MSG( "Bhopcap", 1 ); gmsgStatusText = REG_USER_MSG( "StatusText", -1 ); gmsgStatusValue = REG_USER_MSG( "StatusValue", 3 ); @@ -2816,6 +2812,7 @@ void CBasePlayer::Spawn( void ) g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "0" ); g_engfuncs.pfnSetPhysicsKeyValue( edict(), "hl", "1" ); g_engfuncs.pfnSetPhysicsKeyValue( edict(), "fr", "1" ); + g_engfuncs.pfnSetPhysicsKeyValue( edict(), "bj", bhopcap.value ? "0" : "1" ); pev->fov = m_iFOV = 0;// init field of view. m_iClientFOV = -1; // make sure fov reset is sent @@ -3347,7 +3344,6 @@ void CBasePlayer::ForceClientDllUpdate( void ) m_fWeapon = FALSE; // Force weapon send m_fKnownItem = FALSE; // Force weaponinit messages. m_fInitHUD = TRUE; // Force HUD gmsgResetHUD message - m_bSentBhopcap = true; // a1ba: Update bhopcap state memset( m_rgAmmoLast, 0, sizeof( m_rgAmmoLast )); // a1ba: Force update AmmoX @@ -4114,15 +4110,6 @@ void CBasePlayer::UpdateClientData( void ) UpdateStatusBar(); m_flNextSBarUpdateTime = gpGlobals->time + 0.2f; } - - // Send the current bhopcap state. - if( !m_bSentBhopcap ) - { - m_bSentBhopcap = true; - MESSAGE_BEGIN( MSG_ONE, gmsgBhopcap, NULL, pev ); - WRITE_BYTE( g_bhopcap ); - MESSAGE_END(); - } } //========================================================= diff --git a/dlls/player.h b/dlls/player.h index d2f5d945..b2484b15 100644 --- a/dlls/player.h +++ b/dlls/player.h @@ -328,8 +328,6 @@ public: int m_iAutoWepSwitch; Vector m_vecLastViewAngles; - - bool m_bSentBhopcap; // If false, the player just joined and needs a bhopcap message. }; #define AUTOAIM_2DEGREES 0.0348994967025 diff --git a/pm_shared/pm_shared.c b/pm_shared/pm_shared.c index f84cc9ae..32585d6a 100644 --- a/pm_shared/pm_shared.c +++ b/pm_shared/pm_shared.c @@ -31,8 +31,6 @@ #include "pm_movevars.h" #include "pm_debug.h" -int g_bhopcap = 1; - #if CLIENT_DLL // Spectator Mode int iJumpSpectator; @@ -2481,6 +2479,8 @@ PM_Jump void PM_Jump( void ) { int i; + qboolean bunnyjump = false; + qboolean tfc = false; qboolean cansuperjump = false; @@ -2564,7 +2564,10 @@ void PM_Jump( void ) // In the air now. pmove->onground = -1; - if( g_bhopcap ) + if( pmove->multiplayer ) + bunnyjump = atoi( pmove->PM_Info_ValueForKey( pmove->physinfo, "bj" ) ) ? true : false; + + if( !bunnyjump ) PM_PreventMegaBunnyJumping(); if( tfc ) From 5cca1eb971ad177ddeb65b14defc845a95b8bec6 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 10 Apr 2023 03:06:30 +0500 Subject: [PATCH 016/127] Change view rolling via separate cvars under GoldSource. --- cl_dll/hud.cpp | 15 ++++++++++++++- cl_dll/view.cpp | 13 +++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/cl_dll/hud.cpp b/cl_dll/hud.cpp index f16e30b9..5155993e 100644 --- a/cl_dll/hud.cpp +++ b/cl_dll/hud.cpp @@ -95,8 +95,11 @@ float g_hud_text_color[3]; extern client_sprite_t *GetSpriteList( client_sprite_t *pList, const char *psz, int iRes, int iCount ); extern cvar_t *sensitivity; +qboolean bIsXash; cvar_t *cl_lw = NULL; cvar_t *cl_viewbob = NULL; +cvar_t *cl_rollspeed; +cvar_t *cl_rollangle; void ShutdownInput( void ); @@ -381,7 +384,17 @@ void CHud::Init( void ) m_pCvarDraw = CVAR_CREATE( "hud_draw", "1", FCVAR_ARCHIVE ); cl_lw = gEngfuncs.pfnGetCvarPointer( "cl_lw" ); cl_viewbob = CVAR_CREATE( "cl_viewbob", "0", FCVAR_ARCHIVE ); - +#if GOLDSOURCE_SUPPORT + if( gEngfuncs.pfnGetCvarPointer( "build" )) + { + bIsXash = true; + } + else + { + cl_rollangle = gEngfuncs.pfnRegisterVariable( "cl_rollangle", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); + cl_rollspeed = gEngfuncs.pfnRegisterVariable( "cl_rollspeed", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); + } +#endif m_pSpriteList = NULL; // Clear any old HUD list diff --git a/cl_dll/view.cpp b/cl_dll/view.cpp index 4c93fb87..5c6316a5 100644 --- a/cl_dll/view.cpp +++ b/cl_dll/view.cpp @@ -313,6 +313,10 @@ void V_AddIdle( struct ref_params_s *pparams ) pparams->viewangles[YAW] += v_idlescale * sin( pparams->time * v_iyaw_cycle.value ) * v_iyaw_level.value; } +extern qboolean bIsXash; +extern cvar_t *cl_rollspeed; +extern cvar_t *cl_rollangle; + /* ============== V_CalcViewRoll @@ -328,9 +332,14 @@ void V_CalcViewRoll( struct ref_params_s *pparams ) viewentity = gEngfuncs.GetEntityByIndex( pparams->viewentity ); if( !viewentity ) return; - +#if GOLDSOURCE_SUPPORT + if( bIsXash ) + side = V_CalcRoll( viewentity->angles, pparams->simvel, pparams->movevars->rollangle, pparams->movevars->rollspeed ); + else + side = V_CalcRoll( viewentity->angles, pparams->simvel, cl_rollangle->value, cl_rollspeed->value ); +#else side = V_CalcRoll( viewentity->angles, pparams->simvel, pparams->movevars->rollangle, pparams->movevars->rollspeed ); - +#endif pparams->viewangles[ROLL] += side; if( pparams->health <= 0 && ( pparams->viewheight[2] != 0 ) ) From 66f4bbf218cc72ed2741eb4890bb53394d25a6c7 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 23 Apr 2023 19:20:10 +0300 Subject: [PATCH 017/127] server: remove unused prop.cpp (#367) --- dlls/prop.cpp | 1185 ------------------------------------------------- 1 file changed, 1185 deletions(-) delete mode 100644 dlls/prop.cpp diff --git a/dlls/prop.cpp b/dlls/prop.cpp deleted file mode 100644 index 7f7dba48..00000000 --- a/dlls/prop.cpp +++ /dev/null @@ -1,1185 +0,0 @@ -/*** -* -* Copyright (c) 1996-2001, Valve LLC. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -* All Rights Reserved. -* -* Use, distribution, and modification of this source code and/or resulting -* object code is restricted to non-commercial enhancements to products from -* Valve LLC. All other use, distribution, or modification is prohibited -* without written permission from Valve LLC. -* -****/ -/* - -===== generic grenade.cpp ======================================================== - -*/ - -#include "extdll.h" -#include "util.h" -#include "cbase.h" -#include "monsters.h" -#include "weapons.h" -#include "nodes.h" -#include "soundent.h" -#include "decals.h" -#include "player.h" -#include "explode.h" -#include "gamerules.h" - -#define SF_PROP_RESPAWN 8 // enable autorespawn -#define SF_PROP_BREAKABLE 16 // enable break/explode -#define SF_PROP_FIXED 32 // don't move untill touch -typedef enum -{ - expRandom, - expDirected -}Explosions; - -typedef enum -{ - matGlass = 0, - matWood, - matMetal, - matFlesh, - matCinderBlock, - matCeilingTile, - matComputer, - matUnbreakableGlass, - matRocks, - matNone, - matLastMaterial -}Materials; - -//extern "C" void AngleVectors( const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up); -Vector UTIL_AngleVectorsF( const Vector &angles ) -{ - float rgflVecOut[3]; - float rgflVecIn[3]; - angles.CopyToArray( rgflVecIn ); - g_engfuncs.pfnAngleVectors( rgflVecIn, rgflVecOut, NULL, NULL ); - return Vector( rgflVecOut ); -} -Vector UTIL_AngleVectorsR( const Vector &angles ) -{ - float rgflVecOut[3]; - float rgflVecIn[3]; - angles.CopyToArray( rgflVecIn ); - g_engfuncs.pfnAngleVectors( rgflVecIn, NULL, rgflVecOut, NULL ); - return Vector( rgflVecOut ); -} -Vector UTIL_AngleVectorsU( const Vector &angles ) -{ - float rgflVecOut[3]; - float rgflVecIn[3]; - angles.CopyToArray( rgflVecIn ); - g_engfuncs.pfnAngleVectors( rgflVecIn, NULL, rgflVecOut, NULL ); - return Vector( rgflVecOut ); -} -//===================grenade - -enum PropShape -{ - SHAPE_CYL_H = 0, - SHAPE_CYL_V, - SHAPE_BOX, - SHAPE_GENERIC, - SHAPE_SPHERE, - SHAPE_NOROTATE -}; - -class CProp : public CBaseEntity -{ -public: - void Spawn( void ); - void Precache(); - - void EXPORT BounceTouch( CBaseEntity *pOther ); - //void EXPORT SlideTouch( CBaseEntity *pOther ); - virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - virtual void Force( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - int TakeDamage( entvars_t *pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); - virtual int ObjectCaps( void ) { return ( CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION ) | FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE; } - virtual void BounceSound( void ); - virtual int BloodColor( void ) { return DONT_BLEED; } - virtual void Killed( entvars_t *pevAttacker, int iGib ); - void CheckRotate(); - void EXPORT RespawnThink(); - void EXPORT AngleThink(); - void EXPORT DeployThink(); - void EXPORT DieThink(); - void DamageSound( void ); - void PropRespawn(); - void KeyValue( KeyValueData *pkvd ); - - static const char *pSoundsWood[]; - static const char *pSoundsFlesh[]; - static const char *pSoundsGlass[]; - static const char *pSoundsMetal[]; - static const char *pSoundsConcrete[]; - static const char *pSpawnObjects[]; - - inline BOOL Explodable( void ) { return ExplosionMagnitude() > 0; } - inline int ExplosionMagnitude( void ) { return pev->impulse; } - inline void ExplosionSetMagnitude( int magnitude ) { pev->impulse = magnitude; } - - static void MaterialSoundPrecache( Materials precacheMaterial ); - static void MaterialSoundRandom( edict_t *pEdict, Materials soundMaterial, float volume ); - static const char **MaterialSoundList( Materials precacheMaterial, int &soundCount ); - void EXPORT Die( void ); - - BOOL m_bBarrel; - float m_flFloorFriction; - float m_flCollideFriction; - - // hull sizes - Vector minsH, maxsH; - Vector minsV, maxsV; - - // spawn backup; - Vector spawnOrigin; - Vector spawnAngles; - - edict_t *m_owner2; - edict_t *m_attacker; - float m_flNextAttack; - float m_flRespawnTime; - PropShape m_shape; - PropShape m_oldshape; - CBasePlayer *m_pHolstered; - float m_flSpawnHealth; - int m_idShard; - float m_angle; - int m_iszGibModel; - Materials m_Material; - Explosions m_Explosion; - int m_iaCustomAnglesX[10]; - int m_iaCustomAnglesZ[10]; -}; - -LINK_ENTITY_TO_CLASS( prop, CProp ) - -const char *CProp::pSoundsWood[] = -{ - "debris/wood1.wav", - "debris/wood2.wav", - "debris/wood3.wav", -}; - -const char *CProp::pSoundsFlesh[] = -{ - "debris/flesh1.wav", - "debris/flesh2.wav", - "debris/flesh3.wav", - "debris/flesh5.wav", - "debris/flesh6.wav", - "debris/flesh7.wav", -}; - -const char *CProp::pSoundsMetal[] = -{ - "debris/metal1.wav", - "debris/metal2.wav", - "debris/metal3.wav", -}; - -const char *CProp::pSoundsConcrete[] = -{ - "debris/concrete1.wav", - "debris/concrete2.wav", - "debris/concrete3.wav", -}; - -const char *CProp::pSoundsGlass[] = -{ - "debris/glass1.wav", - "debris/glass2.wav", - "debris/glass3.wav", -}; - -const char **CProp::MaterialSoundList( Materials precacheMaterial, int &soundCount ) -{ - const char **pSoundList = NULL; - - switch( precacheMaterial ) - { - case matWood: - pSoundList = pSoundsWood; - soundCount = ARRAYSIZE( pSoundsWood ); - break; - case matFlesh: - pSoundList = pSoundsFlesh; - soundCount = ARRAYSIZE( pSoundsFlesh ); - break; - case matComputer: - case matUnbreakableGlass: - case matGlass: - pSoundList = pSoundsGlass; - soundCount = ARRAYSIZE( pSoundsGlass ); - break; - case matMetal: - pSoundList = pSoundsMetal; - soundCount = ARRAYSIZE( pSoundsMetal ); - break; - case matCinderBlock: - case matRocks: - pSoundList = pSoundsConcrete; - soundCount = ARRAYSIZE( pSoundsConcrete ); - break; - case matCeilingTile: - case matNone: - default: - soundCount = 0; - break; - } - - return pSoundList; -} - -void CProp::MaterialSoundPrecache( Materials precacheMaterial ) -{ - const char **pSoundList; - int i, soundCount = 0; - - pSoundList = MaterialSoundList( precacheMaterial, soundCount ); - - for( i = 0; i < soundCount; i++ ) - { - PRECACHE_SOUND( pSoundList[i] ); - } -} - -void CProp::MaterialSoundRandom( edict_t *pEdict, Materials soundMaterial, float volume ) -{ - const char **pSoundList; - int soundCount = 0; - - pSoundList = MaterialSoundList( soundMaterial, soundCount ); - - if( soundCount ) - EMIT_SOUND( pEdict, CHAN_BODY, pSoundList[RANDOM_LONG( 0, soundCount - 1 )], volume, 1.0 ); -} - -void CProp::Precache( void ) -{ - const char *pGibName; - - if( !pev->model ) - pev->model = MAKE_STRING( "models/xash/barrel_brown.mdl" ); - - switch( m_Material ) - { - case matWood: - pGibName = "models/woodgibs.mdl"; - - PRECACHE_SOUND( "debris/bustcrate1.wav" ); - PRECACHE_SOUND( "debris/bustcrate2.wav" ); - break; - case matFlesh: - pGibName = "models/fleshgibs.mdl"; - - PRECACHE_SOUND( "debris/bustflesh1.wav" ); - PRECACHE_SOUND( "debris/bustflesh2.wav" ); - break; - case matComputer: - PRECACHE_SOUND( "buttons/spark5.wav"); - PRECACHE_SOUND( "buttons/spark6.wav"); - pGibName = "models/computergibs.mdl"; - - PRECACHE_SOUND( "debris/bustmetal1.wav" ); - PRECACHE_SOUND( "debris/bustmetal2.wav" ); - break; - case matUnbreakableGlass: - case matGlass: - pGibName = "models/glassgibs.mdl"; - - PRECACHE_SOUND( "debris/bustglass1.wav" ); - PRECACHE_SOUND( "debris/bustglass2.wav" ); - break; - case matMetal: - pGibName = "models/metalplategibs.mdl"; - - PRECACHE_SOUND( "debris/bustmetal1.wav" ); - PRECACHE_SOUND( "debris/bustmetal2.wav" ); - break; - case matCinderBlock: - pGibName = "models/cindergibs.mdl"; - - PRECACHE_SOUND( "debris/bustconcrete1.wav" ); - PRECACHE_SOUND( "debris/bustconcrete2.wav" ); - break; - case matRocks: - pGibName = "models/rockgibs.mdl"; - - PRECACHE_SOUND( "debris/bustconcrete1.wav" ); - PRECACHE_SOUND( "debris/bustconcrete2.wav" ); - break; - case matCeilingTile: - pGibName = "models/ceilinggibs.mdl"; - - PRECACHE_SOUND( "debris/bustceiling.wav" ); - break; - } - MaterialSoundPrecache( m_Material ); - if( m_iszGibModel ) - pGibName = STRING( m_iszGibModel ); - - m_idShard = PRECACHE_MODEL( pGibName ); - PRECACHE_MODEL( STRING( pev->model ) ); -} - -void CProp::DamageSound( void ) -{ - int pitch; - float fvol; - char *rgpsz[6]; - int i; - int material = m_Material; - - //if( RANDOM_LONG( 0, 1 ) ) - // return; - - if( RANDOM_LONG( 0, 2 ) ) - pitch = PITCH_NORM; - else - pitch = 95 + RANDOM_LONG( 0, 34 ); - - fvol = RANDOM_FLOAT( 0.75, 1.0 ); - - if( material == matComputer && RANDOM_LONG( 0, 1 ) ) - material = matMetal; - - switch( material ) - { - case matComputer: - case matGlass: - case matUnbreakableGlass: - rgpsz[0] = "debris/glass1.wav"; - rgpsz[1] = "debris/glass2.wav"; - rgpsz[2] = "debris/glass3.wav"; - i = 3; - break; - case matWood: - rgpsz[0] = "debris/wood1.wav"; - rgpsz[1] = "debris/wood2.wav"; - rgpsz[2] = "debris/wood3.wav"; - i = 3; - break; - case matMetal: - rgpsz[0] = "debris/metal1.wav"; - rgpsz[1] = "debris/metal3.wav"; - rgpsz[2] = "debris/metal2.wav"; - i = 2; - break; - case matFlesh: - rgpsz[0] = "debris/flesh1.wav"; - rgpsz[1] = "debris/flesh2.wav"; - rgpsz[2] = "debris/flesh3.wav"; - rgpsz[3] = "debris/flesh5.wav"; - rgpsz[4] = "debris/flesh6.wav"; - rgpsz[5] = "debris/flesh7.wav"; - i = 6; - break; - case matRocks: - case matCinderBlock: - rgpsz[0] = "debris/concrete1.wav"; - rgpsz[1] = "debris/concrete2.wav"; - rgpsz[2] = "debris/concrete3.wav"; - i = 3; - break; - case matCeilingTile: - // UNDONE: no ceiling tile shard sound yet - i = 0; - break; - } - - if( i ) - EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, rgpsz[RANDOM_LONG( 0, i - 1 )], fvol, ATTN_NORM, 0, pitch ); -} - -void CProp::Die( void ) -{ - Vector vecSpot;// shard origin - Vector vecVelocity;// shard velocity - char cFlag = 0; - int pitch; - float fvol; - - pitch = 95 + RANDOM_LONG( 0, 29 ); - - if( pitch > 97 && pitch < 103 ) - pitch = 100; - - // The more negative pev->health, the louder - // the sound should be. - - fvol = RANDOM_FLOAT( 0.85, 1.0 ) + ( fabs( pev->health ) / 100.0 ); - - if( fvol > 1.0 ) - fvol = 1.0; - - switch( m_Material ) - { - case matGlass: - switch( RANDOM_LONG( 0, 1 ) ) - { - case 0: - EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "debris/bustglass1.wav", fvol, ATTN_NORM, 0, pitch ); - break; - case 1: - EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "debris/bustglass2.wav", fvol, ATTN_NORM, 0, pitch ); - break; - } - cFlag = BREAK_GLASS; - break; - case matWood: - switch( RANDOM_LONG( 0, 1 ) ) - { - case 0: - EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "debris/bustcrate1.wav", fvol, ATTN_NORM, 0, pitch ); - break; - case 1: - EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "debris/bustcrate2.wav", fvol, ATTN_NORM, 0, pitch ); - break; - } - cFlag = BREAK_WOOD; - break; - case matComputer: - case matMetal: - switch( RANDOM_LONG( 0, 1 ) ) - { - case 0: - EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "debris/bustmetal1.wav", fvol, ATTN_NORM, 0, pitch ); - break; - case 1: - EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "debris/bustmetal2.wav", fvol, ATTN_NORM, 0, pitch ); - break; - } - cFlag = BREAK_METAL; - break; - case matFlesh: - switch( RANDOM_LONG( 0, 1 ) ) - { - case 0: - EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "debris/bustflesh1.wav", fvol, ATTN_NORM, 0, pitch ); - break; - case 1: - EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "debris/bustflesh2.wav", fvol, ATTN_NORM, 0, pitch ); - break; - } - cFlag = BREAK_FLESH; - break; - case matRocks: - case matCinderBlock: - switch( RANDOM_LONG( 0, 1 ) ) - { - case 0: - EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "debris/bustconcrete1.wav", fvol, ATTN_NORM, 0, pitch ); - break; - case 1: - EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "debris/bustconcrete2.wav", fvol, ATTN_NORM, 0, pitch ); - break; - } - cFlag = BREAK_CONCRETE; - break; - case matCeilingTile: - EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "debris/bustceiling.wav", fvol, ATTN_NORM, 0, pitch ); - break; - } - - if( m_Explosion == expDirected ) - vecVelocity = g_vecAttackDir * 200; - else - { - vecVelocity.x = 0; - vecVelocity.y = 0; - vecVelocity.z = 0; - } - - vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5; - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot ); - WRITE_BYTE( TE_BREAKMODEL); - - // position - WRITE_COORD( vecSpot.x ); - WRITE_COORD( vecSpot.y ); - WRITE_COORD( vecSpot.z ); - - // size - WRITE_COORD( pev->size.x); - WRITE_COORD( pev->size.y); - WRITE_COORD( pev->size.z); - - // velocity - WRITE_COORD( vecVelocity.x ); - WRITE_COORD( vecVelocity.y ); - WRITE_COORD( vecVelocity.z ); - - // randomization - WRITE_BYTE( 10 ); - - // Model - WRITE_SHORT( m_idShard ); //model id# - - // # of shards - WRITE_BYTE( 0 ); // let client decide - - // duration - WRITE_BYTE( 25 );// 2.5 seconds - - // flags - WRITE_BYTE( cFlag ); - MESSAGE_END(); - - float size = pev->size.x; - if( size < pev->size.y ) - size = pev->size.y; - if( size < pev->size.z ) - size = pev->size.z; - - // !!! HACK This should work! - // Build a box above the entity that looks like an 8 pixel high sheet - Vector mins = pev->absmin; - Vector maxs = pev->absmax; - mins.z = pev->absmax.z; - maxs.z += 8; - - // BUGBUG -- can only find 256 entities on a breakable -- should be enough - CBaseEntity *pList[256]; - int count = UTIL_EntitiesInBox( pList, 256, mins, maxs, FL_ONGROUND ); - if( count ) - { - for( int i = 0; i < count; i++ ) - { - ClearBits( pList[i]->pev->flags, FL_ONGROUND ); - pList[i]->pev->groundentity = NULL; - } - } - - // Don't fire something that could fire myself - pev->targetname = 0; - - pev->solid = SOLID_NOT; - - // Fire targets on break - SUB_UseTargets( NULL, USE_TOGGLE, 0 ); - - if( Explodable() && ( m_attacker != NULL ) ) - { - ExplosionCreate( pev->origin, pev->angles, m_attacker, ExplosionMagnitude(), FALSE ); - RadiusDamage( pev->origin, pev, VARS(m_attacker), ExplosionMagnitude(), ExplosionMagnitude() * 2.5 , CLASS_NONE, DMG_BLAST ); - } - UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); -} - -void CProp::Killed( entvars_t *pevAttacker, int iGib ) -{ - pev->takedamage = DAMAGE_NO; - pev->deadflag = DEAD_DEAD; - pev->solid = SOLID_NOT; - pev->effects |= EF_NODRAW; - pev->nextthink = gpGlobals->time + m_flRespawnTime; - SetThink( &CProp::RespawnThink ); - SetTouch( NULL ); - SetUse( NULL ); -} - -void CProp::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if( pev->health <= 0 ) - return; - if( m_owner2 != pActivator->edict() ) - { - if( pev->velocity.Length() < 100 && pActivator->IsPlayer() ) - { - m_owner2 = m_attacker = pActivator->edict(); - } - else - return; - } - if( pActivator->IsPlayer() ) - { - m_pHolstered = (CBasePlayer *) pActivator; - if( m_pHolstered ) - { - - if( m_pHolstered->m_pActiveItem ) - { - CBasePlayerWeapon *weapon = (CBasePlayerWeapon *)m_pHolstered->m_pActiveItem->GetWeaponPtr(); - - //m_Holstered->m_pActiveItem->Holster(); // strange bug here. ValveWHY? - - // HACK: prevent attack - if( weapon ) - { - weapon->m_flNextPrimaryAttack += 0.1; - weapon->m_flNextSecondaryAttack += 0.1; - } - m_pHolstered->m_iHideHUD |= HIDEHUD_WEAPONS; - m_pHolstered->pev->weaponmodel = 0; - m_pHolstered->pev->viewmodel = 0; - } - SetThink( &CProp::DeployThink ); - pev->nextthink = gpGlobals->time + 0.2; - } - } - Vector target = pActivator->pev->origin + UTIL_GetAimVector( m_owner2, 1000 ) * 50; - target.z = target.z + 32; - pev->velocity = ( target - VecBModelOrigin( pev ) ) * 10; - Vector atarget = UTIL_VecToAngles(UTIL_GetAimVector( m_owner2, 1000 ) ); - pev->angles.x = UTIL_AngleMod( pev->angles.x ); - pev->angles.y = UTIL_AngleMod( pev->angles.y ); - pev->angles.z = UTIL_AngleMod( pev->angles.z ); - atarget.x = UTIL_AngleMod( atarget.x ); - atarget.y = UTIL_AngleMod( atarget.y ); - atarget.z = UTIL_AngleMod( atarget.z ); - pev->avelocity.x = UTIL_AngleDiff( atarget.x, pev->angles.x ) * 10; - pev->avelocity.y = UTIL_AngleDiff( atarget.y, pev->angles.y ) * 10; - pev->avelocity.z = UTIL_AngleDiff( atarget.z, pev->angles.z ) * 10; - //pev->angles.z += ( 0 - pev->angles.z ) * 0.06; - if( ( pActivator->pev->button & ( IN_ATTACK ) ) ) - { - pev->velocity = UTIL_GetAimVector( m_owner2, 1000 ) * 1000; - pev->avelocity.y = pev->avelocity.y * 1.5 + RANDOM_FLOAT( 100, -100 ); - pev->avelocity.x = pev->avelocity.x * 1.5 + RANDOM_FLOAT( 100, -100 ); - //pev->avelocity.z = pev->avelocity.z * 0.5 + RANDOM_FLOAT( 100, -100 ); - } - if ( ( pActivator->pev->button & ( IN_ATTACK2 ) ) ) - { - //m_Horizontal = false; - //pev->angles.z = 0; - } - // m_Horizontal = ( fabs( UTIL_AngleDiff( pev->angles.z, 90 ) ) < 20 ) || ( sin( pev->angles.x / 180 * M_PI ) > 0.1 ); - // CheckRotate(); - //ALERT( at_console, "Prop use!\n" ); -} - -void CProp::Force( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - if( pev->health <= 0 ) - return; - if( m_owner2 != pActivator->edict() ) - { - if( pev->velocity.Length() < 100 && pActivator->IsPlayer() ) - m_attacker = pActivator->edict(); - else - return; - } - - if( ( pActivator->pev->button & ( IN_ATTACK ) ) ) - { - pev->velocity = UTIL_GetAimVector( m_owner2, 3000 ) * 1000; - pev->avelocity.y = pev->avelocity.y * 1.5 + RANDOM_FLOAT( 100, -100 ); - pev->avelocity.x = pev->avelocity.x * 1.5 + RANDOM_FLOAT( 100, -100 ); - //pev->avelocity.z = pev->avelocity.z * 0.5 + RANDOM_FLOAT( 100, -100 ); - } - if( ( pActivator->pev->button & ( IN_ATTACK2 ) ) ) - { - //m_Horizontal = false; - //pev->angles.z = 0; - } - - pev->nextthink = gpGlobals->time + m_flRespawnTime; - SetThink( &CProp::RespawnThink ); -} - -void CProp::CheckRotate() -{ - if( m_shape != SHAPE_CYL_H && m_shape != SHAPE_CYL_V ) - { - UTIL_SetSize(pev, minsH, maxsH); - return; - } - if( ( fabs( UTIL_AngleDiff( pev->angles.z, 90 ) ) < 20 ) || ( fabs( sin( pev->angles.x / 180 * M_PI ) ) > 0.3 ) ) - m_shape = SHAPE_CYL_H; - else - m_shape = SHAPE_CYL_V; - - if( m_oldshape != m_shape ) - { - if( m_shape == SHAPE_CYL_H ) - { - pev->angles.y += 90; - - ALERT( at_console, "setH: %f %f %f\n", pev->angles.x, pev->angles.y, pev->angles.z ); - - UTIL_SetSize( pev, minsH, maxsH ); - } - else if( m_shape == SHAPE_CYL_V ) - { - Vector mins = pev->absmin; - Vector maxs = pev->absmax; - - mins.z = pev->absmax.z; - maxs.z += 10; - ALERT( at_console, "setV: %f %f %f\n", pev->angles.x, pev->angles.y, pev->angles.z ); - - // BUGBUG -- can only find 256 entities on a prop -- should be enough - CBaseEntity *pList[256]; - int count = UTIL_EntitiesInBox( pList, 256, mins, maxs, FL_ONGROUND ); - if( count ) - { - for( int i = 0; i < count; i++ ) - { - pList[i]->pev->origin.z += 10; - } - } - pev->origin.z += 10; - //pev->angles.y -= 90; - UTIL_SetSize( pev, minsV, maxsV ); - } - //DROP_TO_FLOOR( edict() ); - //pev->origin.z += 0.5; - m_oldshape = m_shape; - } -} - -void CProp::DeployThink( void ) -{ - if( m_pHolstered ) - { - if( m_pHolstered->m_pActiveItem ) - { - m_pHolstered->m_pActiveItem->Deploy(); - CBasePlayerWeapon *weapon = (CBasePlayerWeapon *) m_pHolstered->m_pActiveItem->GetWeaponPtr(); - if( weapon ) - { - weapon->m_flNextPrimaryAttack = 0; - weapon->m_flNextSecondaryAttack = 0; - } - } - m_pHolstered ->m_iHideHUD &= ~HIDEHUD_WEAPONS; - m_pHolstered = NULL; - } - if( m_pfnThink == &CProp::DeployThink ) - { - pev->nextthink = gpGlobals->time + m_flRespawnTime; - SetThink( &CProp::RespawnThink ); - } -} - -void CProp::BounceTouch( CBaseEntity *pOther ) -{ - if( pev->health <= 0 ) - return; - //ALERT( at_console, "BounceTouch: %f %f %f\n", pev->angles.x, pev->angles.y, pev->angles.z ); - // only do damage if we're moving fairly fast - DeployThink(); - - if( m_flNextAttack < gpGlobals->time && pev->velocity.Length() > 300 ) - { - entvars_t *pevOwner = VARS( m_attacker ); - if( pevOwner ) - { - float dmg = 50 + pev->velocity.Length() / 40; - if( pOther->edict() == m_owner2 ) - { - dmg = 5; - if( pOther->pev->button & ( IN_USE ) ) - { - dmg = 1; - } - } - TraceResult tr = UTIL_GetGlobalTrace(); - ClearMultiDamage(); - pOther->TraceAttack( pevOwner, dmg, gpGlobals->v_forward, &tr, DMG_CLUB ); - ApplyMultiDamage( pev, pevOwner ); - } - m_flNextAttack = gpGlobals->time + 1.0; // debounce - } - if( ( pOther->edict() != m_owner2 ) && ( pev->spawnflags & SF_PROP_BREAKABLE ) && ( pev->velocity.Length() > 900 ) ) - { - pev->nextthink = gpGlobals->time + 0.1; - SetThink( &CProp::DieThink ); - } - - pev->velocity = pev->velocity + pOther->pev->velocity; - float dp = cos( M_PI / 180 * UTIL_AngleDiff( UTIL_VecToAngles( pev->velocity ).y, pev->angles.y ) ); - if( pev->flags & FL_ONGROUND || fabs( pev->velocity.z ) < 40 ) - { - CheckRotate(); - if( m_shape == SHAPE_CYL_H ) - { - pev->velocity.x *= fabs( dp ) * 0.8 + 0.2; - pev->velocity.y *= fabs( dp ) * 0.8 + 0.2; - pev->velocity.z -= 20; - pev->avelocity.x = -dp*pev->velocity.Length()* 1.5; - pev->avelocity.y = 0; - pev->avelocity.z = 0; - pev->angles.z += UTIL_AngleDiff( 90, pev->angles.z ) * 0.7; - //AngleThink(); - } - else if( m_shape == SHAPE_CYL_V ) - { - // pev->angles.z *= 0.3; - //pev->angles.x *= 0.3; - //AngleThink(); - //CheckRotate(); - pev->velocity.z *= m_flFloorFriction; - pev->velocity.x *= m_flFloorFriction; - pev->velocity.y *= m_flFloorFriction; - pev->velocity.z -= 10; - pev->avelocity.y = pev->avelocity.y * 0.4 + RANDOM_FLOAT( 30, -30 ); - } - else if( m_shape == SHAPE_SPHERE ) - { - pev->velocity.z -= 20; - pev->avelocity.x = -cos( M_PI / 180 * UTIL_AngleDiff( UTIL_VecToAngles( pev->velocity ).y, pev->angles.y ) ) * pev->velocity.Length() * 1.5; - pev->avelocity.y = -sin( M_PI / 180 * UTIL_AngleDiff( UTIL_VecToAngles( pev->velocity ).y, pev->angles.y ) ) * pev->velocity.Length() * 1.5;; - pev->avelocity.z = 0; - } - else if( m_shape == SHAPE_BOX || m_shape == SHAPE_GENERIC ) - { - pev->velocity.z *= m_flFloorFriction; - pev->velocity.x *= m_flFloorFriction; - pev->velocity.y *= m_flFloorFriction; - pev->velocity.z -= 10; - } - } - else - { - { - pev->velocity.z *= 0.3; - pev->velocity.y *= m_flCollideFriction; - pev->velocity.x *= m_flCollideFriction; - if( m_shape != SHAPE_SPHERE ) - { - pev->avelocity.y = pev->avelocity.y * 0.4 + RANDOM_FLOAT( 100, -100 ); - pev->avelocity.x = pev->avelocity.x * 0.5 + RANDOM_FLOAT( 100, -100 ); - } - } - //pev->avelocity.z = pev->avelocity.z*0.5 + RANDOM_FLOAT( 1, -1 ); - BounceSound(); - } - pev->framerate = pev->velocity.Length() / 200.0; - if( pev->framerate > 1.0 ) - pev->framerate = 1; - else if( pev->framerate < 0.2 ) - { - CheckRotate(); - AngleThink(); - if( pev->angles.z == 0 || pev->angles.z == 90 ) - pev->framerate = 0; - else - pev->framerate = 0.2; - } -} - -void CProp::BounceSound( void ) -{ - switch( RANDOM_LONG( 0, 2 ) ) - { - case 0: - EMIT_SOUND( ENT( pev ), CHAN_VOICE, "weapons/grenade_hit1.wav", 0.25, ATTN_NORM ); - break; - case 1: - EMIT_SOUND( ENT( pev ), CHAN_VOICE, "weapons/grenade_hit2.wav", 0.25, ATTN_NORM ); - break; - case 2: - EMIT_SOUND( ENT( pev ), CHAN_VOICE, "weapons/grenade_hit3.wav", 0.25, ATTN_NORM ); - break; - } -} - -void CProp::Spawn( void ) -{ - Precache(); - - if( minsH == g_vecZero ) - { - // default barrel parameters - minsV = Vector( -10, -10, -17 ); - maxsV = Vector( 10, 10, 18 ); - minsH = Vector( -10, -10, -10 ); - maxsH = Vector( 10, 10, 13 ); - } - m_flCollideFriction = 0.7; - m_flFloorFriction = 0.5; - spawnOrigin = pev->origin; - spawnAngles = pev->angles; - m_flSpawnHealth = pev->health; - if( m_flSpawnHealth <= 0 ) - m_flSpawnHealth = 30; - if( !m_flRespawnTime ) - m_flRespawnTime = 20; - pev->dmg = 100; - PropRespawn(); -} - -void CProp::PropRespawn() -{ - UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); - pev->effects &= ~EF_NODRAW; - pev->movetype = MOVETYPE_BOUNCE; - pev->solid = SOLID_SLIDEBOX; - pev->takedamage = DAMAGE_YES; - pev->health = m_flSpawnHealth; - pev->velocity = pev->avelocity = g_vecZero; - pev->angles = spawnAngles; - pev->deadflag = DEAD_NO; - SET_MODEL( ENT( pev ), STRING( pev->model ) ); - m_oldshape = ( PropShape ) - 1; - CheckRotate(); - SetTouch( &CProp::BounceTouch ); - SetUse( &CProp::Use ); - - pev->framerate = 1.0f; - UTIL_SetOrigin( pev, spawnOrigin ); -} - -void CProp::RespawnThink() -{ - if( !( pev->spawnflags & SF_PROP_RESPAWN ) ) - { - if( pev->health <= 0 ) - { - pev->nextthink = gpGlobals->time + 0.1; - SetThink( &CBaseEntity::SUB_Remove ); - } - return; - } - PropRespawn(); -} - -void CProp::AngleThink() -{ - pev->nextthink = gpGlobals->time + m_flRespawnTime; - SetThink( &CProp::RespawnThink); - if(! ( pev->flags & FL_ONGROUND || fabs( pev->velocity.z ) < 40 ) ) - { - m_owner2 = m_attacker = 0; - return; - } - if( m_shape == SHAPE_CYL_H ) - { - pev->angles.z += UTIL_AngleDiff( 90, pev->angles.z ) * 0.7; - if( fabs( UTIL_AngleDiff( 90, pev->angles.z ) ) > 0.1 ) - { - SetThink( &CProp::AngleThink ); - pev->nextthink = gpGlobals->time + 0.1; - } - //ALERT( at_console, "AngleThink: %f %f %f\n", pev->angles.x, pev->angles.y, pev->angles.z ); - pev->avelocity.y = pev->avelocity.z = 0; - } - else if( m_shape == SHAPE_CYL_V ) - { - if( fabs( UTIL_AngleDiff( 90, pev->angles.z ) ) > 0.1 ) - { - SetThink( &CProp::AngleThink ); - pev->nextthink = gpGlobals->time + 0.1; - } - pev->angles.z += UTIL_AngleDiff( 0, pev->angles.z ) * 0.7; - //pev->angles.x += UTIL_AngleDiff( 0, pev->angles.x ) * 0.3; - pev->avelocity.x = pev->avelocity.y = pev->avelocity.z = 0; - } - else if( m_shape == SHAPE_BOX ) - { - Vector iangles; - iangles.x = round( pev->angles.x / 90 ) * 90; - iangles.y = round( pev->angles.y / 90 ) * 90; - iangles.z = round( pev->angles.z / 90 ) * 90; - if( fabs( UTIL_AngleDiff( iangles.x, pev->angles.x ) ) > 0.1 || - //fabs( UTIL_AngleDiff( iangles.y, pev->angles.y ) ) > 0.1 || - fabs( UTIL_AngleDiff( iangles.z, pev->angles.z ) ) > 0.1) - { - SetThink( &CProp::AngleThink ); - pev->nextthink = gpGlobals->time + 0.1; - } - pev->angles.x += UTIL_AngleDiff( iangles.x, pev->angles.x ) * 0.6; - //pev->angles.y += UTIL_AngleDiff( iangles.y, pev->angles.y ) * 0.6; - pev->angles.z += UTIL_AngleDiff( iangles.z, pev->angles.z ) * 0.6; - - pev->avelocity.x = pev->avelocity.y = pev->avelocity.z = 0; - } - else if( m_shape == SHAPE_NOROTATE ) - { - pev->avelocity.x = pev->avelocity.y = pev->avelocity.z = 0; - Vector iangles = spawnAngles; - if( fabs(UTIL_AngleDiff( iangles.x, pev->angles.x ) ) > 0.1 || - fabs( UTIL_AngleDiff( iangles.y, pev->angles.y ) ) > 0.1 || - fabs( UTIL_AngleDiff( iangles.z, pev->angles.z ) ) > 0.1 ) - { - SetThink( &CProp::AngleThink ); - pev->nextthink = gpGlobals->time + 0.1; - } - pev->angles.x += UTIL_AngleDiff( iangles.x, pev->angles.x ) * 0.6; - pev->angles.y += UTIL_AngleDiff( iangles.y, pev->angles.y ) * 0.6; - pev->angles.z += UTIL_AngleDiff( iangles.z, pev->angles.z ) * 0.6; - } - else if( m_shape == SHAPE_GENERIC ) - { - float ianglex = 0, ianglez = 0, imaxanglediff=360.0f; - // if first number is zero, it is angle - // all other zeroes is array end - for( int i = 0; ( i < 10 ) && ( ( i == 0 ) || m_iaCustomAnglesX[i] ); i++ ) - { - float anglediff = fabs( UTIL_AngleDiff( pev->angles.x, m_iaCustomAnglesX[i] ) ); - if( imaxanglediff > anglediff ) - { - ianglex = m_iaCustomAnglesX[i]; - imaxanglediff = anglediff; - } - } - imaxanglediff=360.0f; - for( int i = 0; ( i < 10 ) && ( ( i == 0 ) || m_iaCustomAnglesZ[i] ); i++ ) - { - float anglediff = fabs( UTIL_AngleDiff( pev->angles.z, m_iaCustomAnglesZ[i] ) ); - if( imaxanglediff > anglediff ) - { - ianglez = m_iaCustomAnglesZ[i]; - imaxanglediff = anglediff; - } - } - if( fabs( UTIL_AngleDiff( ianglex, pev->angles.x ) ) > 0.1 || - fabs( UTIL_AngleDiff( ianglez, pev->angles.z ) ) > 0.1 ) - { - SetThink( &CProp::AngleThink ); - pev->nextthink = gpGlobals->time + 0.1; - } - pev->angles.x += UTIL_AngleDiff( ianglex, pev->angles.x ) * 0.6; - pev->angles.z += UTIL_AngleDiff( ianglez, pev->angles.z ) * 0.6; - pev->avelocity.x = pev->avelocity.y = pev->avelocity.z = 0; - } - pev->angles.x = UTIL_AngleMod( pev->angles.x ); - pev->angles.y = UTIL_AngleMod( pev->angles.y ); - pev->angles.z = UTIL_AngleMod( pev->angles.z ); -} - -void CProp::DieThink() -{ - Killed( VARS( m_attacker ), GIB_NORMAL ); - Die(); -} - -int CProp::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - Vector r = ( pevInflictor->origin - pev->origin ); - if( ( !m_attacker || ( pev->velocity.Length() < 700 ) ) - && ( (CBaseEntity*)GET_PRIVATE( ENT( pevAttacker ) ) )->IsPlayer() ) - m_attacker = ENT( pevAttacker ); - DeployThink(); - - pev->velocity = r * flDamage / -7; - pev->avelocity.x = pev->avelocity.x*0.5 + RANDOM_FLOAT( 100, -100 ); - ALERT( at_console, "Takedmg: %s %s %f %f\n", STRING( pevInflictor->classname ), STRING( pevAttacker->classname ), flDamage, pev->health ); - - // now some func_breakable code - if( !( pev->spawnflags & SF_PROP_BREAKABLE ) ) - return 0; - if( pev->health <= 0 ) - return; - // Breakables take double damage from the crowbar - if( bitsDamageType & DMG_CLUB ) - flDamage *= 2; - - // Boxes / glass / etc. don't take much poison damage, just the impact of the dart - consider that 10% - if( bitsDamageType & DMG_POISON ) - flDamage *= 0.1; - g_vecAttackDir = r.Normalize(); - - // do the damage - pev->health -= flDamage; - if( pev->health <= 0 ) - { - // delayed explode - SetThink( &CProp::DieThink ); - pev->nextthink = gpGlobals->time + 0.2; - return 0; - } - - // Make a shard noise each time func breakable is hit. - // Don't play shard noise if cbreakable actually died. - DamageSound(); - return 1; -} - -void CProp::KeyValue( KeyValueData* pkvd ) -{ - ALERT( at_console, "%s %s\n", pkvd->szKeyName, pkvd->szValue ); - // UNDONE_WC: explicitly ignoring these fields, but they shouldn't be in the map file! - if( FStrEq( pkvd->szKeyName, "explosion" ) ) - { - if( !stricmp( pkvd->szValue, "directed" ) ) - m_Explosion = expDirected; - else if( !stricmp(pkvd->szValue, "random" ) ) - m_Explosion = expRandom; - else - m_Explosion = expRandom; - - pkvd->fHandled = TRUE; - } - else if( FStrEq( pkvd->szKeyName, "material" ) ) - { - int i = atoi( pkvd->szValue ); - - // 0:glass, 1:metal, 2:flesh, 3:wood - - if( ( i < 0 ) || ( i >= matLastMaterial ) ) - m_Material = matWood; - else - m_Material = (Materials)i; - - pkvd->fHandled = TRUE; - } - else if( FStrEq( pkvd->szKeyName, "shape" ) ) - { - int i = atoi( pkvd->szValue ); - - if( ( i < 0 ) || ( i >= SHAPE_NOROTATE ) ) - m_shape = SHAPE_NOROTATE; - else - m_shape = (PropShape)i; - - pkvd->fHandled = TRUE; - } - else if( FStrEq( pkvd->szKeyName, "gibmodel" ) ) - { - m_iszGibModel = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if( FStrEq( pkvd->szKeyName, "explodemagnitude" ) ) - { - ExplosionSetMagnitude( atoi( pkvd->szValue ) ); - pkvd->fHandled = TRUE; - } - else if( FStrEq( pkvd->szKeyName, "respawntime" ) ) - { - m_flRespawnTime = atof( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if( FStrEq( pkvd->szKeyName, "customanglesx" ) ) - { - UTIL_StringToIntArray( m_iaCustomAnglesX, ARRAYSIZE( m_iaCustomAnglesX ), pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if( FStrEq( pkvd->szKeyName, "customanglesz" ) ) - { - UTIL_StringToIntArray( m_iaCustomAnglesZ, ARRAYSIZE( m_iaCustomAnglesZ ), pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if( FStrEq( pkvd->szKeyName, "hmin" ) ) - { - UTIL_StringToVector( minsH, pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if( FStrEq( pkvd->szKeyName, "hmax" ) ) - { - UTIL_StringToVector( maxsH, pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if( FStrEq( pkvd->szKeyName, "vmin" ) ) - { - UTIL_StringToVector( minsV, pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if( FStrEq( pkvd->szKeyName, "vmax" ) ) - { - UTIL_StringToVector( maxsV, pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - CBaseEntity::KeyValue( pkvd ); -} From c506f2a58239f1104d0a96b5c359c648960658d2 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 23 Apr 2023 21:40:07 +0500 Subject: [PATCH 018/127] Fix crash while parsing command menu with unsupported custom button. Same as https://github.com/SamVanheer/halflife-updated/commit/3e0214ee12212936d1554b9c56bfed9ae4c51d4a --- cl_dll/vgui_TeamFortressViewport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl_dll/vgui_TeamFortressViewport.cpp b/cl_dll/vgui_TeamFortressViewport.cpp index 21a8a928..debac7ef 100644 --- a/cl_dll/vgui_TeamFortressViewport.cpp +++ b/cl_dll/vgui_TeamFortressViewport.cpp @@ -841,7 +841,7 @@ int TeamFortressViewport::CreateCommandMenu( const char *menuFile, int direction { gEngfuncs.Con_Printf( "Too many menus in %s past '%s'\n", menuFile, szLastButtonText ); } - else + else if( pButton ) { // Create the menu m_pCommandMenus[m_iNumMenus] = CreateSubMenu( pButton, m_pCurrentCommandMenu, iButtonY ); From 5eeb649cec2f4f3ab0a5ba07b37b695475ff5cb3 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 23 Apr 2023 21:58:34 +0500 Subject: [PATCH 019/127] Rename "cl_viewbob" cvar to "cl_bobtilt". --- cl_dll/hud.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl_dll/hud.cpp b/cl_dll/hud.cpp index 5155993e..9c3f333f 100644 --- a/cl_dll/hud.cpp +++ b/cl_dll/hud.cpp @@ -383,7 +383,7 @@ void CHud::Init( void ) m_pCvarStealMouse = CVAR_CREATE( "hud_capturemouse", "1", FCVAR_ARCHIVE ); m_pCvarDraw = CVAR_CREATE( "hud_draw", "1", FCVAR_ARCHIVE ); cl_lw = gEngfuncs.pfnGetCvarPointer( "cl_lw" ); - cl_viewbob = CVAR_CREATE( "cl_viewbob", "0", FCVAR_ARCHIVE ); + cl_viewbob = CVAR_CREATE( "cl_bobtilt", "0", FCVAR_ARCHIVE ); #if GOLDSOURCE_SUPPORT if( gEngfuncs.pfnGetCvarPointer( "build" )) { From 523ee2ad97d51a1b9ce4387ee7070b53a6bf3ae8 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 23 Apr 2023 22:11:06 +0500 Subject: [PATCH 020/127] Clear client flags in ClientDisconnect, because engine doesn't clear them before calling ClientConnect, but only before ClientPutInServer, on next connection to this slot. Same as https://github.com/LevShisterov/BugfixedHL/commit/5caf49f06373a05dbac8d84f8009fb185badc5f9 --- dlls/client.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/client.cpp b/dlls/client.cpp index 179719bf..c27b22d1 100644 --- a/dlls/client.cpp +++ b/dlls/client.cpp @@ -126,6 +126,7 @@ void ClientDisconnect( edict_t *pEntity ) pEntity->v.takedamage = DAMAGE_NO;// don't attract autoaim pEntity->v.solid = SOLID_NOT;// nonsolid pEntity->v.effects = 0;// clear any effects + pEntity->v.flags = 0;// clear any flags UTIL_SetOrigin( &pEntity->v, pEntity->v.origin ); g_pGameRules->ClientDisconnected( pEntity ); From b82f524819b71b1bd4b12a20ab008052a6126a03 Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Mon, 24 Apr 2023 01:55:43 +0300 Subject: [PATCH 021/127] Fix Human Grunts dropping weapons again if the game is saved and loaded while the grunt is dying Same as https://github.com/SamVanheer/halflife-updated/commit/bc7d6e3c0782f94553dd692351e7659903c0fa05 --- dlls/hgrunt.cpp | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/dlls/hgrunt.cpp b/dlls/hgrunt.cpp index 7f7e39e5..2f1dffbd 100644 --- a/dlls/hgrunt.cpp +++ b/dlls/hgrunt.cpp @@ -849,27 +849,30 @@ void CHGrunt::HandleAnimEvent( MonsterEvent_t *pEvent ) { case HGRUNT_AE_DROP_GUN: { - Vector vecGunPos; - Vector vecGunAngles; - - GetAttachment( 0, vecGunPos, vecGunAngles ); - - // switch to body group with no gun. - SetBodygroup( GUN_GROUP, GUN_NONE ); - - // now spawn a gun. - if( FBitSet( pev->weapons, HGRUNT_SHOTGUN ) ) + if ( GetBodygroup( GUN_GROUP ) != GUN_NONE ) { - DropItem( "weapon_shotgun", vecGunPos, vecGunAngles ); - } - else - { - DropItem( "weapon_9mmAR", vecGunPos, vecGunAngles ); - } + Vector vecGunPos; + Vector vecGunAngles; - if( FBitSet( pev->weapons, HGRUNT_GRENADELAUNCHER ) ) - { - DropItem( "ammo_ARgrenades", BodyTarget( pev->origin ), vecGunAngles ); + GetAttachment( 0, vecGunPos, vecGunAngles ); + + // switch to body group with no gun. + SetBodygroup( GUN_GROUP, GUN_NONE ); + + // now spawn a gun. + if( FBitSet( pev->weapons, HGRUNT_SHOTGUN ) ) + { + DropItem( "weapon_shotgun", vecGunPos, vecGunAngles ); + } + else + { + DropItem( "weapon_9mmAR", vecGunPos, vecGunAngles ); + } + + if( FBitSet( pev->weapons, HGRUNT_GRENADELAUNCHER ) ) + { + DropItem( "ammo_ARgrenades", BodyTarget( pev->origin ), vecGunAngles ); + } } } break; From 00596e94bf4e1197d519b1dc00cebcada1960b2e Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Mon, 24 Apr 2023 01:57:25 +0300 Subject: [PATCH 022/127] Use named constants instead of some magical constants in hgrunt code --- dlls/hgrunt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/hgrunt.cpp b/dlls/hgrunt.cpp index 2f1dffbd..d8d198db 100644 --- a/dlls/hgrunt.cpp +++ b/dlls/hgrunt.cpp @@ -278,7 +278,7 @@ void CHGrunt::GibMonster( void ) Vector vecGunPos; Vector vecGunAngles; - if( GetBodygroup( 2 ) != 2 ) + if( GetBodygroup( GUN_GROUP ) != GUN_NONE ) { // throw a gun if the grunt has one GetAttachment( 0, vecGunPos, vecGunAngles ); @@ -604,7 +604,7 @@ void CHGrunt::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir if( ptr->iHitgroup == 11 ) { // make sure we're wearing one - if( GetBodygroup( 1 ) == HEAD_GRUNT && ( bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB ) ) ) + if( GetBodygroup( HEAD_GROUP ) == HEAD_GRUNT && ( bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB ) ) ) { // absorb damage flDamage -= 20; From 7d65d9f3bdf5728b323053bb1651ebaab8a8ab5d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 25 Apr 2023 03:05:56 +0300 Subject: [PATCH 023/127] wscript: set default prefix, do not install without destdir as we don't follow typical unix filesystem hierarchy --- wscript | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/wscript b/wscript index 353f9016..71f485f0 100644 --- a/wscript +++ b/wscript @@ -10,6 +10,7 @@ import re VERSION = '2.4' APPNAME = 'hlsdk-portable' top = '.' +default_prefix = '/' Context.Context.line_just = 60 # should fit for everything on 80x26 @@ -21,7 +22,7 @@ def get_taskgen_count(self): def options(opt): opt.load('reconfigure compiler_optimizations xcompile compiler_cxx compiler_c clang_compilation_database strip_on_install msdev msvs msvc subproject') - + grp = opt.add_option_group('Common options') grp.add_option('-8', '--64bits', action = 'store_true', dest = 'ALLOW64', default = False, @@ -238,6 +239,9 @@ def configure(conf): conf.add_subproject('cl_dll') def build(bld): + if bld.is_install and not bld.options.destdir: + bld.fatal('Set the install destination directory using --destdir option') + # don't clean QtCreator files and reconfigure saved options bld.clean_files = bld.bldnode.ant_glob('**', excl='*.user configuration.py .lock* *conf_check_*/** config.log %s/*' % Build.CACHE_DIR, From 8da71ee6757f3c0d17e80c2871a71958331ea5b9 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 25 Apr 2023 03:06:25 +0300 Subject: [PATCH 024/127] server: wscript: remove prop.cpp from excluded files --- dlls/wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/wscript b/dlls/wscript index 2c372154..00781777 100644 --- a/dlls/wscript +++ b/dlls/wscript @@ -20,7 +20,7 @@ def configure(conf): conf.fatal("Could not find hl.def") def build(bld): - excluded_files = ['mpstubb.cpp', 'stats.cpp', 'prop.cpp', 'Wxdebug.cpp'] + excluded_files = ['mpstubb.cpp', 'stats.cpp', 'Wxdebug.cpp'] source = bld.path.ant_glob('**/*.cpp', excl=excluded_files) source += bld.path.parent.ant_glob('pm_shared/*.c') From 0f1cb315d6a8cbf8c30eefb04cb1309ff63d1e00 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 1 May 2023 02:04:06 +0500 Subject: [PATCH 025/127] Revert "Rename "cl_viewbob" cvar to "cl_bobtilt"." This reverts commit 5eeb649cec2f4f3ab0a5ba07b37b695475ff5cb3. --- cl_dll/hud.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl_dll/hud.cpp b/cl_dll/hud.cpp index 9c3f333f..5155993e 100644 --- a/cl_dll/hud.cpp +++ b/cl_dll/hud.cpp @@ -383,7 +383,7 @@ void CHud::Init( void ) m_pCvarStealMouse = CVAR_CREATE( "hud_capturemouse", "1", FCVAR_ARCHIVE ); m_pCvarDraw = CVAR_CREATE( "hud_draw", "1", FCVAR_ARCHIVE ); cl_lw = gEngfuncs.pfnGetCvarPointer( "cl_lw" ); - cl_viewbob = CVAR_CREATE( "cl_bobtilt", "0", FCVAR_ARCHIVE ); + cl_viewbob = CVAR_CREATE( "cl_viewbob", "0", FCVAR_ARCHIVE ); #if GOLDSOURCE_SUPPORT if( gEngfuncs.pfnGetCvarPointer( "build" )) { From 1ed4c1d899c083a494b4ca0de41d02d6f043d7fa Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 1 May 2023 02:10:06 +0500 Subject: [PATCH 026/127] Always change view rolling on client side. --- cl_dll/hud.cpp | 14 ++------------ cl_dll/view.cpp | 12 +++--------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/cl_dll/hud.cpp b/cl_dll/hud.cpp index 5155993e..bc87f630 100644 --- a/cl_dll/hud.cpp +++ b/cl_dll/hud.cpp @@ -95,7 +95,6 @@ float g_hud_text_color[3]; extern client_sprite_t *GetSpriteList( client_sprite_t *pList, const char *psz, int iRes, int iCount ); extern cvar_t *sensitivity; -qboolean bIsXash; cvar_t *cl_lw = NULL; cvar_t *cl_viewbob = NULL; cvar_t *cl_rollspeed; @@ -384,17 +383,8 @@ void CHud::Init( void ) m_pCvarDraw = CVAR_CREATE( "hud_draw", "1", FCVAR_ARCHIVE ); cl_lw = gEngfuncs.pfnGetCvarPointer( "cl_lw" ); cl_viewbob = CVAR_CREATE( "cl_viewbob", "0", FCVAR_ARCHIVE ); -#if GOLDSOURCE_SUPPORT - if( gEngfuncs.pfnGetCvarPointer( "build" )) - { - bIsXash = true; - } - else - { - cl_rollangle = gEngfuncs.pfnRegisterVariable( "cl_rollangle", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); - cl_rollspeed = gEngfuncs.pfnRegisterVariable( "cl_rollspeed", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); - } -#endif + cl_rollangle = gEngfuncs.pfnRegisterVariable( "cl_rollangle", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); + cl_rollspeed = gEngfuncs.pfnRegisterVariable( "cl_rollspeed", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); m_pSpriteList = NULL; // Clear any old HUD list diff --git a/cl_dll/view.cpp b/cl_dll/view.cpp index 5c6316a5..f0a5deef 100644 --- a/cl_dll/view.cpp +++ b/cl_dll/view.cpp @@ -313,7 +313,6 @@ void V_AddIdle( struct ref_params_s *pparams ) pparams->viewangles[YAW] += v_idlescale * sin( pparams->time * v_iyaw_cycle.value ) * v_iyaw_level.value; } -extern qboolean bIsXash; extern cvar_t *cl_rollspeed; extern cvar_t *cl_rollangle; @@ -332,14 +331,9 @@ void V_CalcViewRoll( struct ref_params_s *pparams ) viewentity = gEngfuncs.GetEntityByIndex( pparams->viewentity ); if( !viewentity ) return; -#if GOLDSOURCE_SUPPORT - if( bIsXash ) - side = V_CalcRoll( viewentity->angles, pparams->simvel, pparams->movevars->rollangle, pparams->movevars->rollspeed ); - else - side = V_CalcRoll( viewentity->angles, pparams->simvel, cl_rollangle->value, cl_rollspeed->value ); -#else - side = V_CalcRoll( viewentity->angles, pparams->simvel, pparams->movevars->rollangle, pparams->movevars->rollspeed ); -#endif + + side = V_CalcRoll( viewentity->angles, pparams->simvel, cl_rollangle->value, cl_rollspeed->value ); + pparams->viewangles[ROLL] += side; if( pparams->health <= 0 && ( pparams->viewheight[2] != 0 ) ) From 991fd0cd4a75753db62c901766eb634767c18d5e Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Mon, 29 May 2023 04:56:39 +0300 Subject: [PATCH 027/127] Fix being able to break scripted_sequence by +using friendly NPCs to make them follow player (#375) --- dlls/talkmonster.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/talkmonster.cpp b/dlls/talkmonster.cpp index 2b3ac14f..83100466 100644 --- a/dlls/talkmonster.cpp +++ b/dlls/talkmonster.cpp @@ -1344,7 +1344,7 @@ void CTalkMonster::StartFollowing( CBaseEntity *pLeader ) BOOL CTalkMonster::CanFollow( void ) { - if( m_MonsterState == MONSTERSTATE_SCRIPT ) + if( m_MonsterState == MONSTERSTATE_SCRIPT || m_IdealMonsterState == MONSTERSTATE_SCRIPT ) { if( !m_pCine ) return FALSE; From 31790c101af30ca9571c6923d3c3dccc8e3e5f0a Mon Sep 17 00:00:00 2001 From: Bohdan Shulyar Date: Tue, 30 May 2023 15:43:37 +0300 Subject: [PATCH 028/127] cmake: LibraryNaming: fix Android check --- cmake/LibraryNaming.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/LibraryNaming.cmake b/cmake/LibraryNaming.cmake index e233a7de..0ffae76f 100644 --- a/cmake/LibraryNaming.cmake +++ b/cmake/LibraryNaming.cmake @@ -36,12 +36,14 @@ endmacro() # grep '^#undef XASH' build.h | grep "XASH_RISCV_" | awk '{ print "check_build_target(" $2 ")"}' # echo "endif()" +# NOTE: Android must have priority over Linux to work correctly! + set(CMAKE_REQUIRED_INCLUDES "${PROJECT_SOURCE_DIR}/public/") check_build_target(XASH_64BIT) check_group_build_target(XASH_WIN32 XASH_PLATFORM) +check_group_build_target(XASH_ANDROID XASH_PLATFORM) check_group_build_target(XASH_LINUX XASH_PLATFORM) check_group_build_target(XASH_FREEBSD XASH_PLATFORM) -check_group_build_target(XASH_ANDROID XASH_PLATFORM) check_group_build_target(XASH_APPLE XASH_PLATFORM) check_group_build_target(XASH_NETBSD XASH_PLATFORM) check_group_build_target(XASH_OPENBSD XASH_PLATFORM) From ef7b4a9015080fc3c70b5220bc6446373d3a83da Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Mon, 12 Jun 2023 01:30:13 +0300 Subject: [PATCH 029/127] Replace copyright symbol with (c) in game_shared/ and common/com_model.h to avoid problems with encoding (#378) --- common/com_model.h | 2 +- game_shared/bitvec.h | 2 +- game_shared/vgui_checkbutton2.cpp | 2 +- game_shared/vgui_checkbutton2.h | 2 +- game_shared/vgui_defaultinputsignal.h | 2 +- game_shared/vgui_grid.cpp | 2 +- game_shared/vgui_grid.h | 2 +- game_shared/vgui_helpers.cpp | 2 +- game_shared/vgui_helpers.h | 2 +- game_shared/vgui_listbox.cpp | 2 +- game_shared/vgui_listbox.h | 2 +- game_shared/vgui_loadtga.cpp | 2 +- game_shared/vgui_loadtga.h | 2 +- game_shared/vgui_scrollbar2.cpp | 2 +- game_shared/vgui_scrollbar2.h | 2 +- game_shared/vgui_slider2.cpp | 2 +- game_shared/vgui_slider2.h | 2 +- game_shared/voice_banmgr.cpp | 2 +- game_shared/voice_banmgr.h | 2 +- game_shared/voice_common.h | 2 +- game_shared/voice_gamemgr.h | 2 +- game_shared/voice_status.cpp | 2 +- game_shared/voice_vgui_tweakdlg.cpp | 2 +- game_shared/voice_vgui_tweakdlg.h | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/common/com_model.h b/common/com_model.h index 61c0434a..a2b8e8bc 100644 --- a/common/com_model.h +++ b/common/com_model.h @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/bitvec.h b/game_shared/bitvec.h index d77796fb..effd6249 100644 --- a/game_shared/bitvec.h +++ b/game_shared/bitvec.h @@ -1,4 +1,4 @@ -//========= Copyright � 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/vgui_checkbutton2.cpp b/game_shared/vgui_checkbutton2.cpp index bb67f31a..977a0a98 100644 --- a/game_shared/vgui_checkbutton2.cpp +++ b/game_shared/vgui_checkbutton2.cpp @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/vgui_checkbutton2.h b/game_shared/vgui_checkbutton2.h index d04fc182..79fd025b 100644 --- a/game_shared/vgui_checkbutton2.h +++ b/game_shared/vgui_checkbutton2.h @@ -1,4 +1,4 @@ -//========= Copyright � 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/vgui_defaultinputsignal.h b/game_shared/vgui_defaultinputsignal.h index cd55b76c..11cba53a 100644 --- a/game_shared/vgui_defaultinputsignal.h +++ b/game_shared/vgui_defaultinputsignal.h @@ -1,4 +1,4 @@ -//========= Copyright � 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/vgui_grid.cpp b/game_shared/vgui_grid.cpp index f94e1cef..57717895 100644 --- a/game_shared/vgui_grid.cpp +++ b/game_shared/vgui_grid.cpp @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/vgui_grid.h b/game_shared/vgui_grid.h index 0e45ac4a..1d37cf36 100644 --- a/game_shared/vgui_grid.h +++ b/game_shared/vgui_grid.h @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/vgui_helpers.cpp b/game_shared/vgui_helpers.cpp index 79c4e378..429910f3 100644 --- a/game_shared/vgui_helpers.cpp +++ b/game_shared/vgui_helpers.cpp @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/vgui_helpers.h b/game_shared/vgui_helpers.h index 91c24343..603b1a30 100644 --- a/game_shared/vgui_helpers.h +++ b/game_shared/vgui_helpers.h @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/vgui_listbox.cpp b/game_shared/vgui_listbox.cpp index 77b7689b..dfea03a3 100644 --- a/game_shared/vgui_listbox.cpp +++ b/game_shared/vgui_listbox.cpp @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/vgui_listbox.h b/game_shared/vgui_listbox.h index a97c780d..30281ba6 100644 --- a/game_shared/vgui_listbox.h +++ b/game_shared/vgui_listbox.h @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/vgui_loadtga.cpp b/game_shared/vgui_loadtga.cpp index bfd8ee7a..37b69483 100644 --- a/game_shared/vgui_loadtga.cpp +++ b/game_shared/vgui_loadtga.cpp @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/vgui_loadtga.h b/game_shared/vgui_loadtga.h index 26505b18..2e4db263 100644 --- a/game_shared/vgui_loadtga.h +++ b/game_shared/vgui_loadtga.h @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/vgui_scrollbar2.cpp b/game_shared/vgui_scrollbar2.cpp index 805bb191..ccba4aa8 100644 --- a/game_shared/vgui_scrollbar2.cpp +++ b/game_shared/vgui_scrollbar2.cpp @@ -1,4 +1,4 @@ -//========= Copyright � 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/vgui_scrollbar2.h b/game_shared/vgui_scrollbar2.h index 71cd965b..3d3ab23a 100644 --- a/game_shared/vgui_scrollbar2.h +++ b/game_shared/vgui_scrollbar2.h @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/vgui_slider2.cpp b/game_shared/vgui_slider2.cpp index d7e2c564..3b1cf311 100644 --- a/game_shared/vgui_slider2.cpp +++ b/game_shared/vgui_slider2.cpp @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: New version of the slider bar // diff --git a/game_shared/vgui_slider2.h b/game_shared/vgui_slider2.h index 9f9388c8..291a39e9 100644 --- a/game_shared/vgui_slider2.h +++ b/game_shared/vgui_slider2.h @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/voice_banmgr.cpp b/game_shared/voice_banmgr.cpp index f8241bf3..8e8b18cb 100644 --- a/game_shared/voice_banmgr.cpp +++ b/game_shared/voice_banmgr.cpp @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/voice_banmgr.h b/game_shared/voice_banmgr.h index 64c0c2bb..41c8e0fc 100644 --- a/game_shared/voice_banmgr.h +++ b/game_shared/voice_banmgr.h @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/voice_common.h b/game_shared/voice_common.h index c32011a3..e10856e0 100644 --- a/game_shared/voice_common.h +++ b/game_shared/voice_common.h @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/voice_gamemgr.h b/game_shared/voice_gamemgr.h index cdbb8815..75b3e003 100644 --- a/game_shared/voice_gamemgr.h +++ b/game_shared/voice_gamemgr.h @@ -1,4 +1,4 @@ -//========= Copyright � 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/voice_status.cpp b/game_shared/voice_status.cpp index cbc81e81..1099fb8c 100644 --- a/game_shared/voice_status.cpp +++ b/game_shared/voice_status.cpp @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/voice_vgui_tweakdlg.cpp b/game_shared/voice_vgui_tweakdlg.cpp index ea61f509..cca316c2 100644 --- a/game_shared/voice_vgui_tweakdlg.cpp +++ b/game_shared/voice_vgui_tweakdlg.cpp @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // diff --git a/game_shared/voice_vgui_tweakdlg.h b/game_shared/voice_vgui_tweakdlg.h index 21af03ae..5e820173 100644 --- a/game_shared/voice_vgui_tweakdlg.h +++ b/game_shared/voice_vgui_tweakdlg.h @@ -1,4 +1,4 @@ -//========= Copyright 1996-2002, Valve LLC, All rights reserved. ============ +//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // From 8da76a8c3fb5246fb08526673f79f9a8d5498336 Mon Sep 17 00:00:00 2001 From: Xiaobaibai <824395314@qq.com> Date: Mon, 12 Jun 2023 11:37:20 +0800 Subject: [PATCH 030/127] ARMv8 neon support --- cl_dll/neon_mathfun.h | 356 +++++++++++++++++++++++++++++++++++++++++ cl_dll/studio_util.cpp | 235 +++++++++++++++++++++++++++ 2 files changed, 591 insertions(+) create mode 100644 cl_dll/neon_mathfun.h diff --git a/cl_dll/neon_mathfun.h b/cl_dll/neon_mathfun.h new file mode 100644 index 00000000..e45b92c1 --- /dev/null +++ b/cl_dll/neon_mathfun.h @@ -0,0 +1,356 @@ +/* NEON implementation of sin, cos, exp and log + + Inspired by Intel Approximate Math library, and based on the + corresponding algorithms of the cephes math library +*/ + +/* Copyright (C) 2011 Julien Pommier + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + (this is the zlib license) +*/ + +#include + +typedef float32x4_t v4sf; // vector of 4 float +typedef uint32x4_t v4su; // vector of 4 uint32 +typedef int32x4_t v4si; // vector of 4 uint32 + +#define s4f_x(s4f) vgetq_lane_f32(s4f, 0) +#define s4f_y(s4f) vgetq_lane_f32(s4f, 1) +#define s4f_z(s4f) vgetq_lane_f32(s4f, 2) +#define s4f_w(s4f) vgetq_lane_f32(s4f, 3) + +#define c_inv_mant_mask ~0x7f800000u +#define c_cephes_SQRTHF 0.707106781186547524 +#define c_cephes_log_p0 7.0376836292E-2 +#define c_cephes_log_p1 - 1.1514610310E-1 +#define c_cephes_log_p2 1.1676998740E-1 +#define c_cephes_log_p3 - 1.2420140846E-1 +#define c_cephes_log_p4 + 1.4249322787E-1 +#define c_cephes_log_p5 - 1.6668057665E-1 +#define c_cephes_log_p6 + 2.0000714765E-1 +#define c_cephes_log_p7 - 2.4999993993E-1 +#define c_cephes_log_p8 + 3.3333331174E-1 +#define c_cephes_log_q1 -2.12194440e-4 +#define c_cephes_log_q2 0.693359375 + +/* natural logarithm computed for 4 simultaneous float + return NaN for x <= 0 +*/ +inline v4sf log_ps(v4sf x) { + v4sf one = vdupq_n_f32(1); + + x = vmaxq_f32(x, vdupq_n_f32(0)); /* force flush to zero on denormal values */ + v4su invalid_mask = vcleq_f32(x, vdupq_n_f32(0)); + + v4si ux = vreinterpretq_s32_f32(x); + + v4si emm0 = vshrq_n_s32(ux, 23); + + /* keep only the fractional part */ + ux = vandq_s32(ux, vdupq_n_s32(c_inv_mant_mask)); + ux = vorrq_s32(ux, vreinterpretq_s32_f32(vdupq_n_f32(0.5f))); + x = vreinterpretq_f32_s32(ux); + + emm0 = vsubq_s32(emm0, vdupq_n_s32(0x7f)); + v4sf e = vcvtq_f32_s32(emm0); + + e = vaddq_f32(e, one); + + /* part2: + if( x < SQRTHF ) { + e -= 1; + x = x + x - 1.0; + } else { x = x - 1.0; } + */ + v4su mask = vcltq_f32(x, vdupq_n_f32(c_cephes_SQRTHF)); + v4sf tmp = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(x), mask)); + x = vsubq_f32(x, one); + e = vsubq_f32(e, vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(one), mask))); + x = vaddq_f32(x, tmp); + + v4sf z = vmulq_f32(x,x); + + v4sf y = vdupq_n_f32(c_cephes_log_p0); + y = vmulq_f32(y, x); + y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p1)); + y = vmulq_f32(y, x); + y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p2)); + y = vmulq_f32(y, x); + y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p3)); + y = vmulq_f32(y, x); + y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p4)); + y = vmulq_f32(y, x); + y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p5)); + y = vmulq_f32(y, x); + y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p6)); + y = vmulq_f32(y, x); + y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p7)); + y = vmulq_f32(y, x); + y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p8)); + y = vmulq_f32(y, x); + + y = vmulq_f32(y, z); + + + tmp = vmulq_f32(e, vdupq_n_f32(c_cephes_log_q1)); + y = vaddq_f32(y, tmp); + + + tmp = vmulq_f32(z, vdupq_n_f32(0.5f)); + y = vsubq_f32(y, tmp); + + tmp = vmulq_f32(e, vdupq_n_f32(c_cephes_log_q2)); + x = vaddq_f32(x, y); + x = vaddq_f32(x, tmp); + x = vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(x), invalid_mask)); // negative arg will be NAN + return x; +} + +#define c_exp_hi 88.3762626647949f +#define c_exp_lo -88.3762626647949f + +#define c_cephes_LOG2EF 1.44269504088896341 +#define c_cephes_exp_C1 0.693359375 +#define c_cephes_exp_C2 -2.12194440e-4 + +#define c_cephes_exp_p0 1.9875691500E-4 +#define c_cephes_exp_p1 1.3981999507E-3 +#define c_cephes_exp_p2 8.3334519073E-3 +#define c_cephes_exp_p3 4.1665795894E-2 +#define c_cephes_exp_p4 1.6666665459E-1 +#define c_cephes_exp_p5 5.0000001201E-1 + +/* exp() computed for 4 float at once */ +inline v4sf exp_ps(v4sf x) { + v4sf tmp, fx; + + v4sf one = vdupq_n_f32(1); + x = vminq_f32(x, vdupq_n_f32(c_exp_hi)); + x = vmaxq_f32(x, vdupq_n_f32(c_exp_lo)); + + /* express exp(x) as exp(g + n*log(2)) */ + fx = vmlaq_f32(vdupq_n_f32(0.5f), x, vdupq_n_f32(c_cephes_LOG2EF)); + + /* perform a floorf */ + tmp = vcvtq_f32_s32(vcvtq_s32_f32(fx)); + + /* if greater, substract 1 */ + v4su mask = vcgtq_f32(tmp, fx); + mask = vandq_u32(mask, vreinterpretq_u32_f32(one)); + + + fx = vsubq_f32(tmp, vreinterpretq_f32_u32(mask)); + + tmp = vmulq_f32(fx, vdupq_n_f32(c_cephes_exp_C1)); + v4sf z = vmulq_f32(fx, vdupq_n_f32(c_cephes_exp_C2)); + x = vsubq_f32(x, tmp); + x = vsubq_f32(x, z); + + static const float cephes_exp_p[6] = { c_cephes_exp_p0, c_cephes_exp_p1, c_cephes_exp_p2, c_cephes_exp_p3, c_cephes_exp_p4, c_cephes_exp_p5 }; + v4sf y = vld1q_dup_f32(cephes_exp_p+0); + v4sf c1 = vld1q_dup_f32(cephes_exp_p+1); + v4sf c2 = vld1q_dup_f32(cephes_exp_p+2); + v4sf c3 = vld1q_dup_f32(cephes_exp_p+3); + v4sf c4 = vld1q_dup_f32(cephes_exp_p+4); + v4sf c5 = vld1q_dup_f32(cephes_exp_p+5); + + y = vmulq_f32(y, x); + z = vmulq_f32(x,x); + y = vaddq_f32(y, c1); + y = vmulq_f32(y, x); + y = vaddq_f32(y, c2); + y = vmulq_f32(y, x); + y = vaddq_f32(y, c3); + y = vmulq_f32(y, x); + y = vaddq_f32(y, c4); + y = vmulq_f32(y, x); + y = vaddq_f32(y, c5); + + y = vmulq_f32(y, z); + y = vaddq_f32(y, x); + y = vaddq_f32(y, one); + + /* build 2^n */ + int32x4_t mm; + mm = vcvtq_s32_f32(fx); + mm = vaddq_s32(mm, vdupq_n_s32(0x7f)); + mm = vshlq_n_s32(mm, 23); + v4sf pow2n = vreinterpretq_f32_s32(mm); + + y = vmulq_f32(y, pow2n); + return y; +} + +#define c_minus_cephes_DP1 -0.78515625 +#define c_minus_cephes_DP2 -2.4187564849853515625e-4 +#define c_minus_cephes_DP3 -3.77489497744594108e-8 +#define c_sincof_p0 -1.9515295891E-4 +#define c_sincof_p1 8.3321608736E-3 +#define c_sincof_p2 -1.6666654611E-1 +#define c_coscof_p0 2.443315711809948E-005 +#define c_coscof_p1 -1.388731625493765E-003 +#define c_coscof_p2 4.166664568298827E-002 +#define c_cephes_FOPI 1.27323954473516 // 4 / M_PI + +/* evaluation of 4 sines & cosines at once. + + The code is the exact rewriting of the cephes sinf function. + Precision is excellent as long as x < 8192 (I did not bother to + take into account the special handling they have for greater values + -- it does not return garbage for arguments over 8192, though, but + the extra precision is missing). + + Note that it is such that sinf((float)M_PI) = 8.74e-8, which is the + surprising but correct result. + + Note also that when you compute sin(x), cos(x) is available at + almost no extra price so both sin_ps and cos_ps make use of + sincos_ps.. + */ +inline void sincos_ps(v4sf x, v4sf *ysin, v4sf *ycos) { // any x + v4sf y; + + v4su emm2; + + v4su sign_mask_sin, sign_mask_cos; + sign_mask_sin = vcltq_f32(x, vdupq_n_f32(0)); + x = vabsq_f32(x); + + /* scale by 4/Pi */ + y = vmulq_n_f32(x, c_cephes_FOPI); + + /* store the integer part of y in mm0 */ + emm2 = vcvtq_u32_f32(y); + /* j=(j+1) & (~1) (see the cephes sources) */ + emm2 = vaddq_u32(emm2, vdupq_n_u32(1)); + emm2 = vandq_u32(emm2, vdupq_n_u32(~1)); + y = vcvtq_f32_u32(emm2); + + /* get the polynom selection mask + there is one polynom for 0 <= x <= Pi/4 + and another one for Pi/4 +#include "neon_mathfun.h" +#endif /* ==================== @@ -17,8 +23,59 @@ AngleMatrix ==================== */ +#if XASH_SIMD_NEON +const uint32x4_t AngleMatrix_sign0 = vreinterpretq_f32_u32(vsetq_lane_u32(0x80000000, vdupq_n_u32(0), 0)); +const uint32x4_t AngleMatrix_sign1 = vreinterpretq_f32_u32(vsetq_lane_u32(0x80000000, vdupq_n_u32(0), 1)); +const uint32x4_t AngleMatrix_sign2 = vreinterpretq_f32_u32(vsetq_lane_u32(0x80000000, vdupq_n_u32(0), 2)); +#endif void AngleMatrix( const float *angles, float (*matrix)[4] ) { +#if XASH_SIMD_NEON + float32x4x3_t out_reg; + float32x4_t angles_reg = {}; + memcpy(&angles_reg, angles, sizeof(float) * 3); + + float32x4x2_t sp_sy_sr_0_cp_cy_cr_1; + sincos_ps(vmulq_n_f32(angles_reg, (M_PI * 2 / 360)), &sp_sy_sr_0_cp_cy_cr_1.val[0], &sp_sy_sr_0_cp_cy_cr_1.val[1]); + + float32x4x2_t sp_sr_cp_cr_sy_0_cy_1 = vuzpq_f32(sp_sy_sr_0_cp_cy_cr_1.val[0], sp_sy_sr_0_cp_cy_cr_1.val[1]); + float32x4x2_t sp_cp_sy_cy_sr_cr_0_1 = vzipq_f32(sp_sy_sr_0_cp_cy_cr_1.val[0], sp_sy_sr_0_cp_cy_cr_1.val[1]); + + float32x4_t _0_sr_cr_0 = vextq_f32(sp_sy_sr_0_cp_cy_cr_1.val[0], sp_cp_sy_cy_sr_cr_0_1.val[1], 3); + float32x4_t cp_cr_sr_0 = vcombine_f32(vget_high_f32(sp_sr_cp_cr_sy_0_cy_1.val[0]), vget_high_f32(sp_sy_sr_0_cp_cy_cr_1.val[0])); + float32x4_t cy_sy_sy_0 = vcombine_f32(vrev64_f32(vget_high_f32(sp_cp_sy_cy_sr_cr_0_1.val[0])), vget_low_f32(sp_sr_cp_cr_sy_0_cy_1.val[1])); + float32x4_t sy_cy_cy_1 = vcombine_f32(vget_high_f32(sp_cp_sy_cy_sr_cr_0_1.val[0]), vget_high_f32(sp_sr_cp_cr_sy_0_cy_1.val[1])); + + float32x4_t _0_srsp_crsp_0 = vmulq_laneq_f32(_0_sr_cr_0, sp_sy_sr_0_cp_cy_cr_1.val[0], 0); // *sp + out_reg.val[0] = vmulq_laneq_f32(_0_srsp_crsp_0, sp_sy_sr_0_cp_cy_cr_1.val[1], 1); // *cy + out_reg.val[1] = vmulq_laneq_f32(_0_srsp_crsp_0, sp_sy_sr_0_cp_cy_cr_1.val[0], 1); // *sy + + cy_sy_sy_0 = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(cy_sy_sy_0), AngleMatrix_sign1)); + sy_cy_cy_1 = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(sy_cy_cy_1), AngleMatrix_sign2)); + out_reg.val[0] = vfmaq_f32(out_reg.val[0], cp_cr_sr_0, cy_sy_sy_0); + out_reg.val[1] = vfmaq_f32(out_reg.val[1], cp_cr_sr_0, sy_cy_cy_1); + + float32x4_t cp_cr_0_1 = vcombine_f32(vget_high_f32(sp_sr_cp_cr_sy_0_cy_1.val[0]), vget_high_f32(sp_cp_sy_cy_sr_cr_0_1.val[1])); + float32x4_t _1_cp_cr_0 = vextq_f32(cp_cr_0_1, cp_cr_0_1, 3); + out_reg.val[2] = vmulq_f32(sp_sr_cp_cr_sy_0_cy_1.val[0], _1_cp_cr_0); + out_reg.val[2] = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(out_reg.val[2]), AngleMatrix_sign0)); + + memcpy(matrix, &out_reg, sizeof(float) * 3 * 4); +/* + matrix[0][0] = cp*cy; + matrix[0][1] = sr*sp*cy-cr*sy; + matrix[0][2] = cr*sp*cy+sr*sy; + matrix[0][3] = 0.0; + matrix[1][0] = cp*sy; + matrix[1][1] = sr*sp*sy+cr*cy; + matrix[1][2] = cr*sp*sy-sr*cy; + matrix[1][3] = 0.0; + matrix[2][0] = -sp*1; + matrix[2][1] = sr*cp; + matrix[2][2] = cp*cr; + matrix[2][3] = cr*0; +*/ +#else float angle; float sr, sp, sy, cr, cp, cy; @@ -45,6 +102,7 @@ void AngleMatrix( const float *angles, float (*matrix)[4] ) matrix[0][3] = 0.0f; matrix[1][3] = 0.0f; matrix[2][3] = 0.0f; +#endif } /* @@ -55,6 +113,13 @@ VectorCompare */ int VectorCompare( const float *v1, const float *v2 ) { +#if XASH_SIMD_NEON + // is this really works? + float32x4_t v1_reg = {}, v2_reg = {}; + memcpy(&v1_reg, v1, sizeof(float) * 3); + memcpy(&v2_reg, v2, sizeof(float) * 3); + return !vaddvq_u32(vceqq_f32(v1_reg, v2_reg)); +#else int i; for( i = 0; i < 3; i++ ) @@ -62,6 +127,7 @@ int VectorCompare( const float *v1, const float *v2 ) return 0; return 1; +#endif } /* @@ -72,9 +138,23 @@ CrossProduct */ void CrossProduct( const float *v1, const float *v2, float *cross ) { +#if XASH_SIMD_NEON + float32x4_t v1_reg = {}, v2_reg = {}; + memcpy(&v1_reg, v1, sizeof(float) * 3); + memcpy(&v2_reg, v2, sizeof(float) * 3); + + float32x4_t yzxy_a = vextq_f32(vextq_f32(v1_reg, v1_reg, 3), v1_reg, 2); // [aj, ak, ai, aj] + float32x4_t yzxy_b = vextq_f32(vextq_f32(v2_reg, v2_reg, 3), v2_reg, 2); // [bj, bk, bi, bj] + float32x4_t zxyy_a = vextq_f32(yzxy_a, yzxy_a, 1); // [ak, ai, aj, aj] + float32x4_t zxyy_b = vextq_f32(yzxy_b, yzxy_b, 1); // [bk, ai, bj, bj] + float32x4_t cross_reg = vfmsq_f32(vmulq_f32(yzxy_a, zxyy_b), zxyy_a, yzxy_b); // [ajbk-akbj, akbi-aibk, aibj-ajbi, 0] + + memcpy(cross, &v2_reg, sizeof(float) * 3); +#else cross[0] = v1[1] * v2[2] - v1[2] * v2[1]; cross[1] = v1[2] * v2[0] - v1[0] * v2[2]; cross[2] = v1[0] * v2[1] - v1[1] * v2[0]; +#endif } /* @@ -85,9 +165,26 @@ VectorTransform */ void VectorTransform( const float *in1, float in2[3][4], float *out ) { +#if XASH_SIMD_NEON + float32x4_t in1_reg = {}; + memcpy(&in1_reg, &in1, sizeof(float) * 3); + + float32x4x4_t in_t; + memcpy(&in_t, &in2, sizeof(float) * 3 * 4); + //memset(&in_t.val[3], 0, sizeof(in_t.val[3])); + in_t = vld4q_f32((const float*)&in_t); + + float32x4_t out_reg = in_t.val[3]; + out_reg = vfmaq_laneq_f32(out_reg, in_t.val[0], in1_reg, 0); + out_reg = vfmaq_laneq_f32(out_reg, in_t.val[1], in1_reg, 1); + out_reg = vfmaq_laneq_f32(out_reg, in_t.val[2], in1_reg, 2); + + memcpy(out, &out_reg, sizeof(float) * 3); +#else out[0] = DotProduct(in1, in2[0]) + in2[0][3]; out[1] = DotProduct(in1, in2[1]) + in2[1][3]; out[2] = DotProduct(in1, in2[2]) + in2[2][3]; +#endif } /* @@ -98,6 +195,29 @@ ConcatTransforms */ void ConcatTransforms( float in1[3][4], float in2[3][4], float out[3][4] ) { +#if XASH_SIMD_NEON + float32x4x3_t in1_reg, in2_reg; + memcpy(&in1_reg, in1, sizeof(float) * 3 * 4); + memcpy(&in2_reg, in2, sizeof(float) * 3 * 4); + float32x4x3_t out_reg = {}; + + out_reg.val[0] = vcopyq_laneq_f32(out_reg.val[0], 3, in1_reg.val[0], 3); // out[0][3] = in[0][3] + out_reg.val[0] = vfmaq_laneq_f32(out_reg.val[0], in2_reg.val[0], in1_reg.val[0], 0); // out[0][n] += in2[0][n] * in1[0][0] + out_reg.val[0] = vfmaq_laneq_f32(out_reg.val[0], in2_reg.val[1], in1_reg.val[0], 1); // out[0][n] += in2[1][n] * in1[0][1] + out_reg.val[0] = vfmaq_laneq_f32(out_reg.val[0], in2_reg.val[2], in1_reg.val[0], 2); // out[0][n] += in2[2][n] * in1[0][2] + + out_reg.val[1] = vcopyq_laneq_f32(out_reg.val[1], 3, in1_reg.val[1], 3); + out_reg.val[1] = vfmaq_laneq_f32(out_reg.val[1], in2_reg.val[0], in1_reg.val[1], 0); + out_reg.val[1] = vfmaq_laneq_f32(out_reg.val[1], in2_reg.val[1], in1_reg.val[1], 1); + out_reg.val[1] = vfmaq_laneq_f32(out_reg.val[1], in2_reg.val[2], in1_reg.val[1], 2); + + out_reg.val[2] = vcopyq_laneq_f32(out_reg.val[2], 3, in1_reg.val[2], 3); + out_reg.val[2] = vfmaq_laneq_f32(out_reg.val[2], in2_reg.val[0], in1_reg.val[2], 0); + out_reg.val[2] = vfmaq_laneq_f32(out_reg.val[2], in2_reg.val[1], in1_reg.val[2], 1); + out_reg.val[2] = vfmaq_laneq_f32(out_reg.val[2], in2_reg.val[2], in1_reg.val[2], 2); + + memcpy(&out, &out_reg, sizeof(out)); +#else out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + @@ -122,6 +242,7 @@ void ConcatTransforms( float in1[3][4], float in2[3][4], float out[3][4] ) in1[2][2] * in2[2][2]; out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3]; +#endif } // angles index are not the same as ROLL, PITCH, YAW @@ -132,8 +253,36 @@ AngleQuaternion ==================== */ +#if XASH_SIMD_NEON +const float32x4_t AngleQuaternion_sign2 = vzipq_f32(vdupq_n_u32(0x80000000), vdupq_n_u32(0x00000000)).val[0]; // { 0x80000000, 0x00000000, 0x80000000, 0x00000000 }; +#endif void AngleQuaternion( float *angles, vec4_t quaternion ) { +#if XASH_SIMD_NEON + float32x4_t angles_reg = {}; + memcpy(&angles_reg, angles, sizeof(float) * 3); + float32x4x2_t sr_sp_sy_0_cr_cp_cy_1; + sincos_ps(vmulq_n_f32(angles_reg, 0.5), &sr_sp_sy_0_cr_cp_cy_1.val[0], &sr_sp_sy_0_cr_cp_cy_1.val[1]); + + float32x4x2_t sr_sy_cr_cy_sp_0_cp_1 = vuzpq_f32(sr_sp_sy_0_cr_cp_cy_1.val[0], sr_sp_sy_0_cr_cp_cy_1.val[1]); + float32x4_t cp_cp_cp_cp = vdupq_laneq_f32(sr_sp_sy_0_cr_cp_cy_1.val[1], 1); + float32x4_t sp_sp_sp_sp = vdupq_laneq_f32(sr_sp_sy_0_cr_cp_cy_1.val[0], 1); + + float32x4_t sr_sy_cr_cy = sr_sy_cr_cy_sp_0_cp_1.val[0]; + float32x4_t sy_cr_cy_sr = vextq_f32(sr_sy_cr_cy_sp_0_cp_1.val[0], sr_sy_cr_cy_sp_0_cp_1.val[0], 1); + float32x4_t cr_cy_sr_sy = vextq_f32(sr_sy_cr_cy_sp_0_cp_1.val[0], sr_sy_cr_cy_sp_0_cp_1.val[0], 2); + float32x4_t cy_sr_sy_cr = vextq_f32(sr_sy_cr_cy_sp_0_cp_1.val[0], sr_sy_cr_cy_sp_0_cp_1.val[0], 3); + float32x4_t sp_sp_sp_sp_signed = veorq_u32(sp_sp_sp_sp, AngleQuaternion_sign2); + + float32x4_t left = vmulq_f32(vmulq_f32(sr_sy_cr_cy, cp_cp_cp_cp), cy_sr_sy_cr); + + float32x4_t out_reg = vfmaq_f32(left, vmulq_f32(cr_cy_sr_sy, sp_sp_sp_sp_signed), sy_cr_cy_sr); + memcpy(quaternion, &out_reg, sizeof(float) * 4); + //quaternion[0] = sr * cp * cy - cr * sp * sy; // X + //quaternion[1] = sy * cp * sr + cy * sp * cr; // Y + //quaternion[2] = cr * cp * sy - sr * sp * cy; // Z + //quaternion[3] = cy * cp * cr + sy * sp * sr; // W +#else float angle; float sr, sp, sy, cr, cp, cy; @@ -152,6 +301,7 @@ void AngleQuaternion( float *angles, vec4_t quaternion ) quaternion[1] = cr * sp * cy + sr * cp * sy; // Y quaternion[2] = cr * cp * sy - sr * sp * cy; // Z quaternion[3] = cr * cp * cy + sr * sp * sy; // W +#endif } /* @@ -162,6 +312,37 @@ QuaternionSlerp */ void QuaternionSlerp( vec4_t p, vec4_t q, float t, vec4_t qt ) { +#if XASH_SIMD_NEON + float32x4_t p_reg = {}, q_reg = {}; + memcpy(&p_reg, p, sizeof(float) * 4); + memcpy(&q_reg, q, sizeof(float) * 4); + + // q = (cos(a/2), xsin(a/2), ysin(a/2), zsin(a/2)) + // cos(a-b) = cosacosb+sinasinb + const uint32x4_t signmask = vdupq_n_u32(0x80000000); + const float32x4_t one_minus_epsilon = vdupq_n_f32(1.0f - 0.00001f); + + float32x4_t vcosom = vdupq_n_f32(DotProduct(p, q)); + uint32x4_t sign = vandq_u32(vreinterpretq_u32_f32(vcosom), signmask); + q_reg = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(p_reg), sign)); + vcosom = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(vcosom), sign)); + + float x[4] = {(1.0f - t), t, 1, 0}; // cosom -> 1, sinom -> 0, sinx ~ x + float32x4_t x_reg; + memcpy(&x_reg, x, sizeof(float) * 4); + + // if ((1.0 - cosom) > 0.000001) x = sin(x * omega) + uint32x4_t cosom_less_then_one = vcltq_f32(vcosom, one_minus_epsilon); + float32x4_t vomega = acos_ps(vcosom); + x_reg = vbslq_f32(cosom_less_then_one, x_reg, sin_ps(vmulq_f32(x_reg, vomega))); + + // qt = (x[0] * p + x[1] * q) / x[2]; + float32x4_t qt_reg = vmulq_laneq_f32(p_reg, x_reg, 0); + qt_reg = vfmaq_laneq_f32(qt_reg, q_reg, x_reg, 1); + qt_reg = vdivq_f32(qt_reg, vdupq_laneq_f32(x_reg, 2)); // vdivq_laneq_f32 ? + + memcpy(qt, &qt_reg, sizeof(float) * 4); +#else int i; float omega, cosom, sinom, sclp, sclq; @@ -216,6 +397,7 @@ void QuaternionSlerp( vec4_t p, vec4_t q, float t, vec4_t qt ) qt[i] = sclp * p[i] + sclq * qt[i]; } } +#endif } /* @@ -224,8 +406,60 @@ QuaternionMatrix ==================== */ +#if XASH_SIMD_NEON +const uint32x4_t QuaternionMatrix_sign1 = vsetq_lane_u32(0x80000000, vdupq_n_u32(0x00000000), 0); // { 0x80000000, 0x00000000, 0x00000000, 0x00000000 }; +const uint32x4_t QuaternionMatrix_sign2 = vsetq_lane_u32(0x80000000, vdupq_n_u32(0x00000000), 1); // { 0x00000000, 0x80000000, 0x00000000, 0x00000000 }; +const uint32x4_t QuaternionMatrix_sign3 = vsetq_lane_u32(0x00000000, vdupq_n_u32(0x80000000), 2); // { 0x80000000, 0x80000000, 0x00000000, 0x80000000 }; +const float32x4_t matrix3x4_identity_0 = vsetq_lane_f32(1, vdupq_n_f32(0), 0); // { 1, 0, 0, 0 } +const float32x4_t matrix3x4_identity_1 = vsetq_lane_f32(1, vdupq_n_f32(0), 1); // { 0, 1, 0, 0 } +const float32x4_t matrix3x4_identity_2 = vsetq_lane_f32(1, vdupq_n_f32(0), 2); // { 0, 0, 1, 0 } +#endif + void QuaternionMatrix( vec4_t quaternion, float (*matrix)[4] ) { +#if XASH_SIMD_NEON + float32x4_t quaternion_reg; + memcpy(&quaternion_reg, quaternion, sizeof(float) * 4); + + float32x4_t q1032 = vrev64q_f32(quaternion_reg); + float32x4_t q1032_signed = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(q1032), QuaternionMatrix_sign1)); + float32x4_t q2301 = vextq_f32(quaternion_reg, quaternion_reg, 2); + float32x4_t q2301_signed = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(q2301), QuaternionMatrix_sign3)); + float32x4_t q3210 = vrev64q_f32(q2301); + float32x4_t q3210_signed = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(q3210), QuaternionMatrix_sign2)); + + float32x4x3_t out_reg; + + out_reg.val[0] = vmulq_laneq_f32(q2301_signed, quaternion_reg, 2); + out_reg.val[0] = vfmaq_laneq_f32(out_reg.val[0], q1032_signed, quaternion_reg, 1); + out_reg.val[0] = vfmaq_n_f32(matrix3x4_identity_0, out_reg.val[0], 2.0f); + + out_reg.val[1] = vmulq_laneq_f32(q3210_signed, quaternion_reg, 2); + out_reg.val[1] = vfmsq_laneq_f32(out_reg.val[1], q1032_signed, quaternion_reg, 0); + out_reg.val[1] = vfmaq_n_f32(matrix3x4_identity_1, out_reg.val[1], 2.0f); + + out_reg.val[2] = vmulq_laneq_f32(q3210_signed, quaternion_reg, 1); + out_reg.val[2] = vfmaq_laneq_f32(out_reg.val[2], q2301_signed, quaternion_reg, 0); + out_reg.val[2] = vfmsq_n_f32(matrix3x4_identity_2, out_reg.val[2], 2.0f); + + memcpy(matrix, &out_reg, sizeof(float) * 3 * 4); +/* + matrix[0][0] = 1.0 + 2.0 * ( quaternion[1] * -quaternion[1] + -quaternion[2] * quaternion[2] ); + matrix[0][1] = 0.0 + 2.0 * ( quaternion[1] * quaternion[0] + -quaternion[3] * quaternion[2] ); + matrix[0][2] = 0.0 + 2.0 * ( quaternion[1] * quaternion[3] + quaternion[0] * quaternion[2] ); + matrix[0][3] = 0.0 + 2.0 * ( quaternion[1] * quaternion[2] + -quaternion[1] * quaternion[2] ); + + matrix[1][0] = 0.0 + 2.0 * ( -quaternion[0] * -quaternion[1] + quaternion[3] * quaternion[2] ); + matrix[1][1] = 1.0 + 2.0 * ( -quaternion[0] * quaternion[0] + -quaternion[2] * quaternion[2] ); + matrix[1][2] = 0.0 + 2.0 * ( -quaternion[0] * quaternion[3] + quaternion[1] * quaternion[2] ); + matrix[1][3] = 0.0 + 2.0 * ( -quaternion[0] * quaternion[2] + quaternion[0] * quaternion[2] ); + + matrix[2][0] = 0.0 + 2.0 * ( -quaternion[0] * -quaternion[2] + -quaternion[3] * quaternion[1] ); + matrix[2][1] = 0.0 + 2.0 * ( -quaternion[0] * -quaternion[3] + quaternion[2] * quaternion[1] ); + matrix[2][2] = 1.0 + 2.0 * ( -quaternion[0] * quaternion[0] + -quaternion[1] * quaternion[1] ); + matrix[2][3] = 0.0 + 2.0 * ( -quaternion[0] * -quaternion[1] + -quaternion[0] * quaternion[1] ); +*/ +#else matrix[0][0] = 1.0f - 2.0f * quaternion[1] * quaternion[1] - 2.0f * quaternion[2] * quaternion[2]; matrix[1][0] = 2.0f * quaternion[0] * quaternion[1] + 2.0f * quaternion[3] * quaternion[2]; matrix[2][0] = 2.0f * quaternion[0] * quaternion[2] - 2.0f * quaternion[3] * quaternion[1]; @@ -237,6 +471,7 @@ void QuaternionMatrix( vec4_t quaternion, float (*matrix)[4] ) matrix[0][2] = 2.0f * quaternion[0] * quaternion[2] + 2.0f * quaternion[3] * quaternion[1]; matrix[1][2] = 2.0f * quaternion[1] * quaternion[2] - 2.0f * quaternion[3] * quaternion[0]; matrix[2][2] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[1] * quaternion[1]; +#endif } /* From f0f4e55c489de6ff6da5e99efdc4328fe5770359 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 3 Jul 2023 01:45:28 +0300 Subject: [PATCH 031/127] Add PowerPC to library naming scheme --- cmake/LibraryNaming.cmake | 10 ++++++++++ public/build.h | 13 ++++++++++++- scripts/waifulib/library_naming.py | 17 ++++++++++++----- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/cmake/LibraryNaming.cmake b/cmake/LibraryNaming.cmake index 0ffae76f..724349bd 100644 --- a/cmake/LibraryNaming.cmake +++ b/cmake/LibraryNaming.cmake @@ -59,6 +59,7 @@ check_group_build_target(XASH_X86 XASH_ARCHITECTURE) check_group_build_target(XASH_AMD64 XASH_ARCHITECTURE) check_group_build_target(XASH_ARM XASH_ARCHITECTURE) check_group_build_target(XASH_MIPS XASH_ARCHITECTURE) +check_group_build_target(XASH_PPC XASH_ARCHITECTURE) check_group_build_target(XASH_JS XASH_ARCHITECTURE) check_group_build_target(XASH_E2K XASH_ARCHITECTURE) check_group_build_target(XASH_RISCV XASH_ARCHITECTURE) @@ -168,6 +169,15 @@ elseif(XASH_JS) set(BUILDARCH "javascript") elseif(XASH_E2K) set(BUILDARCH "e2k") +elseif(XASH_PPC) + set(BUILDARCH "ppc") + if(XASH_64BIT) + set(BUILDARCH "${BUILDARCH}64") + endif() + + if(XASH_LITTLE_ENDIAN) + set(BUILDARCH "${BUILDARCH}el") + endif() else() message(SEND_ERROR "Place your architecture name here! If this is a mistake, try to fix conditions above and report a bug") endif() diff --git a/public/build.h b/public/build.h index 636d97a4..2f8fffae 100644 --- a/public/build.h +++ b/public/build.h @@ -76,6 +76,7 @@ Then you can use another oneliner to query all variables: #undef XASH_NETBSD #undef XASH_OPENBSD #undef XASH_POSIX +#undef XASH_PPC #undef XASH_RISCV #undef XASH_RISCV_DOUBLEFP #undef XASH_RISCV_SINGLEFP @@ -138,7 +139,12 @@ Then you can use another oneliner to query all variables: #endif #endif -#if XASH_ANDROID || defined XASH_IOS || defined XASH_NSWITCH || defined XASH_PSVITA +// XASH_SAILFISH is special: SailfishOS by itself is a normal GNU/Linux platform +// It doesn't make sense to split it to separate platform +// but we still need XASH_MOBILE_PLATFORM for the engine. +// So this macro is defined entirely in build-system: see main wscript +// HLSDK/PrimeXT/other SDKs users note: you may ignore this macro +#if XASH_ANDROID || XASH_IOS || XASH_NSWITCH || XASH_PSVITA || XASH_SAILFISH #define XASH_MOBILE_PLATFORM 1 #endif @@ -190,6 +196,11 @@ Then you can use another oneliner to query all variables: #elif defined __e2k__ #define XASH_64BIT 1 #define XASH_E2K 1 +#elif defined __PPC__ || defined __powerpc__ + #define XASH_PPC 1 + #if defined __PPC64__ || defined __powerpc64__ + #define XASH_64BIT 1 + #endif #elif defined _M_ARM // msvc #define XASH_ARM 7 #define XASH_ARM_HARDFP 1 diff --git a/scripts/waifulib/library_naming.py b/scripts/waifulib/library_naming.py index 78acafe9..4369b3b6 100644 --- a/scripts/waifulib/library_naming.py +++ b/scripts/waifulib/library_naming.py @@ -51,6 +51,7 @@ DEFINES = [ 'XASH_NETBSD', 'XASH_OPENBSD', 'XASH_POSIX', +'XASH_PPC', 'XASH_RISCV', 'XASH_RISCV_DOUBLEFP', 'XASH_RISCV_SINGLEFP', @@ -126,7 +127,7 @@ def configure(conf): buildarch += "4" else: raise conf.fatal('Unknown ARM') - + if conf.env.XASH_ARM_HARDFP: buildarch += "hf" else: @@ -143,7 +144,7 @@ def configure(conf): buildarch += "64" else: buildarch += "32" - + if conf.env.XASH_RISCV_DOUBLEFP: buildarch += "d" elif conf.env.XASH_RISCV_SINGLEFP: @@ -152,12 +153,18 @@ def configure(conf): buildarch = "javascript" elif conf.env.XASH_E2K: buildarch = "e2k" + elif conf.env.XASH_PPC: + buildarch = "ppc" + if conf.env.XASH_64BIT: + buildarch += "64" + if conf.env.XASH_LITTLE_ENDIAN: + buildarch += "el" else: raise conf.fatal("Place your architecture name in build.h and library_naming.py!\n" "If this is a mistake, try to fix conditions above and report a bug") - + conf.env.revert() - + if buildos == 'android': # force disable for Android, as Android ports aren't distributed in normal way and doesn't follow library naming conf.env.POSTFIX = '' @@ -167,5 +174,5 @@ def configure(conf): conf.env.POSTFIX = '_%s' % buildarch else: conf.env.POSTFIX = '' - + conf.end_msg(conf.env.POSTFIX) From fa03ed3bce89e16c86b28b8419e1b540f8d1d627 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 3 Jul 2023 02:36:40 +0300 Subject: [PATCH 032/127] wscript: fix mod_options.txt parsing under Python2 --- wscript | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/wscript b/wscript index 71f485f0..02efd884 100644 --- a/wscript +++ b/wscript @@ -214,18 +214,19 @@ def configure(conf): with open('mod_options.txt') as fd: lines = fd.readlines() for line in lines: - p = regex.match(line.strip()) - if p: - conf.start_msg("* " + p[3]) - if p[2] == 'ON': - conf.env[p[1]] = True - conf.define(p[1], 1) - elif p[2] == 'OFF': - conf.env[p[1]] = False - conf.undefine(p[1]) + m = regex.match(line.strip()) + if m: + p = m.groups() + conf.start_msg("* " + p[2]) + if p[1] == 'ON': + conf.env[p[0]] = True + conf.define(p[0], 1) + elif p[1] == 'OFF': + conf.env[p[0]] = False + conf.undefine(p[0]) else: - conf.env[p[1]] = p[2] - conf.end_msg(p[2]) + conf.env[p[0]] = p[1] + conf.end_msg(p[1]) if conf.env.HLDEMO_BUILD and conf.env.OEM_BUILD: conf.fatal('Don\'t mix Demo and OEM builds!') From d8e4ec04e4927c7050e0bb10fe63d53e54e9e787 Mon Sep 17 00:00:00 2001 From: Xiaobaibai <824395314@qq.com> Date: Fri, 14 Jul 2023 10:17:31 +0800 Subject: [PATCH 033/127] fix typo ( no xref at all? ) --- cl_dll/studio_util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl_dll/studio_util.cpp b/cl_dll/studio_util.cpp index 3b9af7de..2d4bad80 100644 --- a/cl_dll/studio_util.cpp +++ b/cl_dll/studio_util.cpp @@ -149,7 +149,7 @@ void CrossProduct( const float *v1, const float *v2, float *cross ) float32x4_t zxyy_b = vextq_f32(yzxy_b, yzxy_b, 1); // [bk, ai, bj, bj] float32x4_t cross_reg = vfmsq_f32(vmulq_f32(yzxy_a, zxyy_b), zxyy_a, yzxy_b); // [ajbk-akbj, akbi-aibk, aibj-ajbi, 0] - memcpy(cross, &v2_reg, sizeof(float) * 3); + memcpy(cross, &cross_reg, sizeof(float) * 3); #else cross[0] = v1[1] * v2[2] - v1[2] * v2[1]; cross[1] = v1[2] * v2[0] - v1[0] * v2[2]; From a898d773a175760def16c61847a6fd17f5a2abf5 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 17 Jul 2023 22:49:24 +0500 Subject: [PATCH 034/127] Revert "Always change view rolling on client side." This reverts commit 1ed4c1d899c083a494b4ca0de41d02d6f043d7fa. --- cl_dll/hud.cpp | 14 ++++++++++++-- cl_dll/view.cpp | 12 +++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/cl_dll/hud.cpp b/cl_dll/hud.cpp index bc87f630..5155993e 100644 --- a/cl_dll/hud.cpp +++ b/cl_dll/hud.cpp @@ -95,6 +95,7 @@ float g_hud_text_color[3]; extern client_sprite_t *GetSpriteList( client_sprite_t *pList, const char *psz, int iRes, int iCount ); extern cvar_t *sensitivity; +qboolean bIsXash; cvar_t *cl_lw = NULL; cvar_t *cl_viewbob = NULL; cvar_t *cl_rollspeed; @@ -383,8 +384,17 @@ void CHud::Init( void ) m_pCvarDraw = CVAR_CREATE( "hud_draw", "1", FCVAR_ARCHIVE ); cl_lw = gEngfuncs.pfnGetCvarPointer( "cl_lw" ); cl_viewbob = CVAR_CREATE( "cl_viewbob", "0", FCVAR_ARCHIVE ); - cl_rollangle = gEngfuncs.pfnRegisterVariable( "cl_rollangle", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); - cl_rollspeed = gEngfuncs.pfnRegisterVariable( "cl_rollspeed", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); +#if GOLDSOURCE_SUPPORT + if( gEngfuncs.pfnGetCvarPointer( "build" )) + { + bIsXash = true; + } + else + { + cl_rollangle = gEngfuncs.pfnRegisterVariable( "cl_rollangle", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); + cl_rollspeed = gEngfuncs.pfnRegisterVariable( "cl_rollspeed", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); + } +#endif m_pSpriteList = NULL; // Clear any old HUD list diff --git a/cl_dll/view.cpp b/cl_dll/view.cpp index f0a5deef..5c6316a5 100644 --- a/cl_dll/view.cpp +++ b/cl_dll/view.cpp @@ -313,6 +313,7 @@ void V_AddIdle( struct ref_params_s *pparams ) pparams->viewangles[YAW] += v_idlescale * sin( pparams->time * v_iyaw_cycle.value ) * v_iyaw_level.value; } +extern qboolean bIsXash; extern cvar_t *cl_rollspeed; extern cvar_t *cl_rollangle; @@ -331,9 +332,14 @@ void V_CalcViewRoll( struct ref_params_s *pparams ) viewentity = gEngfuncs.GetEntityByIndex( pparams->viewentity ); if( !viewentity ) return; - - side = V_CalcRoll( viewentity->angles, pparams->simvel, cl_rollangle->value, cl_rollspeed->value ); - +#if GOLDSOURCE_SUPPORT + if( bIsXash ) + side = V_CalcRoll( viewentity->angles, pparams->simvel, pparams->movevars->rollangle, pparams->movevars->rollspeed ); + else + side = V_CalcRoll( viewentity->angles, pparams->simvel, cl_rollangle->value, cl_rollspeed->value ); +#else + side = V_CalcRoll( viewentity->angles, pparams->simvel, pparams->movevars->rollangle, pparams->movevars->rollspeed ); +#endif pparams->viewangles[ROLL] += side; if( pparams->health <= 0 && ( pparams->viewheight[2] != 0 ) ) From 8180cf60c270b933096506f61d07ed03ba3619af Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 17 Jul 2023 22:50:16 +0500 Subject: [PATCH 035/127] Revert "Change view rolling via separate cvars under GoldSource." This reverts commit 5cca1eb971ad177ddeb65b14defc845a95b8bec6. --- cl_dll/hud.cpp | 15 +-------------- cl_dll/view.cpp | 13 ++----------- 2 files changed, 3 insertions(+), 25 deletions(-) diff --git a/cl_dll/hud.cpp b/cl_dll/hud.cpp index 5155993e..f16e30b9 100644 --- a/cl_dll/hud.cpp +++ b/cl_dll/hud.cpp @@ -95,11 +95,8 @@ float g_hud_text_color[3]; extern client_sprite_t *GetSpriteList( client_sprite_t *pList, const char *psz, int iRes, int iCount ); extern cvar_t *sensitivity; -qboolean bIsXash; cvar_t *cl_lw = NULL; cvar_t *cl_viewbob = NULL; -cvar_t *cl_rollspeed; -cvar_t *cl_rollangle; void ShutdownInput( void ); @@ -384,17 +381,7 @@ void CHud::Init( void ) m_pCvarDraw = CVAR_CREATE( "hud_draw", "1", FCVAR_ARCHIVE ); cl_lw = gEngfuncs.pfnGetCvarPointer( "cl_lw" ); cl_viewbob = CVAR_CREATE( "cl_viewbob", "0", FCVAR_ARCHIVE ); -#if GOLDSOURCE_SUPPORT - if( gEngfuncs.pfnGetCvarPointer( "build" )) - { - bIsXash = true; - } - else - { - cl_rollangle = gEngfuncs.pfnRegisterVariable( "cl_rollangle", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); - cl_rollspeed = gEngfuncs.pfnRegisterVariable( "cl_rollspeed", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); - } -#endif + m_pSpriteList = NULL; // Clear any old HUD list diff --git a/cl_dll/view.cpp b/cl_dll/view.cpp index 5c6316a5..4c93fb87 100644 --- a/cl_dll/view.cpp +++ b/cl_dll/view.cpp @@ -313,10 +313,6 @@ void V_AddIdle( struct ref_params_s *pparams ) pparams->viewangles[YAW] += v_idlescale * sin( pparams->time * v_iyaw_cycle.value ) * v_iyaw_level.value; } -extern qboolean bIsXash; -extern cvar_t *cl_rollspeed; -extern cvar_t *cl_rollangle; - /* ============== V_CalcViewRoll @@ -332,14 +328,9 @@ void V_CalcViewRoll( struct ref_params_s *pparams ) viewentity = gEngfuncs.GetEntityByIndex( pparams->viewentity ); if( !viewentity ) return; -#if GOLDSOURCE_SUPPORT - if( bIsXash ) - side = V_CalcRoll( viewentity->angles, pparams->simvel, pparams->movevars->rollangle, pparams->movevars->rollspeed ); - else - side = V_CalcRoll( viewentity->angles, pparams->simvel, cl_rollangle->value, cl_rollspeed->value ); -#else + side = V_CalcRoll( viewentity->angles, pparams->simvel, pparams->movevars->rollangle, pparams->movevars->rollspeed ); -#endif + pparams->viewangles[ROLL] += side; if( pparams->health <= 0 && ( pparams->viewheight[2] != 0 ) ) From f00a8f46d8fe9d19729cd36438b0fa5a4d308234 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 22 Jul 2023 04:21:00 +0300 Subject: [PATCH 036/127] client: studio: fix types in AArch64 NEON code --- cl_dll/studio_util.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/cl_dll/studio_util.cpp b/cl_dll/studio_util.cpp index 2d4bad80..c2fb3bed 100644 --- a/cl_dll/studio_util.cpp +++ b/cl_dll/studio_util.cpp @@ -24,9 +24,9 @@ AngleMatrix ==================== */ #if XASH_SIMD_NEON -const uint32x4_t AngleMatrix_sign0 = vreinterpretq_f32_u32(vsetq_lane_u32(0x80000000, vdupq_n_u32(0), 0)); -const uint32x4_t AngleMatrix_sign1 = vreinterpretq_f32_u32(vsetq_lane_u32(0x80000000, vdupq_n_u32(0), 1)); -const uint32x4_t AngleMatrix_sign2 = vreinterpretq_f32_u32(vsetq_lane_u32(0x80000000, vdupq_n_u32(0), 2)); +static const uint32x4_t AngleMatrix_sign0 = vsetq_lane_u32(0x80000000, vdupq_n_u32(0), 0); +static const uint32x4_t AngleMatrix_sign1 = vsetq_lane_u32(0x80000000, vdupq_n_u32(0), 1); +static const uint32x4_t AngleMatrix_sign2 = vsetq_lane_u32(0x80000000, vdupq_n_u32(0), 2); #endif void AngleMatrix( const float *angles, float (*matrix)[4] ) { @@ -254,7 +254,11 @@ AngleQuaternion ==================== */ #if XASH_SIMD_NEON -const float32x4_t AngleQuaternion_sign2 = vzipq_f32(vdupq_n_u32(0x80000000), vdupq_n_u32(0x00000000)).val[0]; // { 0x80000000, 0x00000000, 0x80000000, 0x00000000 }; +static const float32x4_t AngleQuaternion_sign2 = + vzipq_f32( + vreinterpret_f32_u32(vdupq_n_u32(0x80000000)), + vreinterpret_f32_u32(vdupq_n_u32(0x00000000)) + ).val[0]; // { 0x80000000, 0x00000000, 0x80000000, 0x00000000 }; #endif void AngleQuaternion( float *angles, vec4_t quaternion ) { @@ -272,7 +276,7 @@ void AngleQuaternion( float *angles, vec4_t quaternion ) float32x4_t sy_cr_cy_sr = vextq_f32(sr_sy_cr_cy_sp_0_cp_1.val[0], sr_sy_cr_cy_sp_0_cp_1.val[0], 1); float32x4_t cr_cy_sr_sy = vextq_f32(sr_sy_cr_cy_sp_0_cp_1.val[0], sr_sy_cr_cy_sp_0_cp_1.val[0], 2); float32x4_t cy_sr_sy_cr = vextq_f32(sr_sy_cr_cy_sp_0_cp_1.val[0], sr_sy_cr_cy_sp_0_cp_1.val[0], 3); - float32x4_t sp_sp_sp_sp_signed = veorq_u32(sp_sp_sp_sp, AngleQuaternion_sign2); + float32x4_t sp_sp_sp_sp_signed = veorq_u32(vreinterpret_u32_f32(sp_sp_sp_sp), AngleQuaternion_sign2); float32x4_t left = vmulq_f32(vmulq_f32(sr_sy_cr_cy, cp_cp_cp_cp), cy_sr_sy_cr); From b4bd06603e871c54a40e798c6c8a9a6d344e98c8 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 22 Jul 2023 04:28:51 +0300 Subject: [PATCH 037/127] client: studio: fix NEON vreinterpret types, fix memcpy --- cl_dll/studio_util.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cl_dll/studio_util.cpp b/cl_dll/studio_util.cpp index c2fb3bed..00419a39 100644 --- a/cl_dll/studio_util.cpp +++ b/cl_dll/studio_util.cpp @@ -216,7 +216,7 @@ void ConcatTransforms( float in1[3][4], float in2[3][4], float out[3][4] ) out_reg.val[2] = vfmaq_laneq_f32(out_reg.val[2], in2_reg.val[1], in1_reg.val[2], 1); out_reg.val[2] = vfmaq_laneq_f32(out_reg.val[2], in2_reg.val[2], in1_reg.val[2], 2); - memcpy(&out, &out_reg, sizeof(out)); + memcpy(out, &out_reg, sizeof(float) * 3 * 4); #else out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; @@ -256,8 +256,8 @@ AngleQuaternion #if XASH_SIMD_NEON static const float32x4_t AngleQuaternion_sign2 = vzipq_f32( - vreinterpret_f32_u32(vdupq_n_u32(0x80000000)), - vreinterpret_f32_u32(vdupq_n_u32(0x00000000)) + vreinterpretq_f32_u32(vdupq_n_u32(0x80000000)), + vreinterpretq_f32_u32(vdupq_n_u32(0x00000000)) ).val[0]; // { 0x80000000, 0x00000000, 0x80000000, 0x00000000 }; #endif void AngleQuaternion( float *angles, vec4_t quaternion ) @@ -276,7 +276,7 @@ void AngleQuaternion( float *angles, vec4_t quaternion ) float32x4_t sy_cr_cy_sr = vextq_f32(sr_sy_cr_cy_sp_0_cp_1.val[0], sr_sy_cr_cy_sp_0_cp_1.val[0], 1); float32x4_t cr_cy_sr_sy = vextq_f32(sr_sy_cr_cy_sp_0_cp_1.val[0], sr_sy_cr_cy_sp_0_cp_1.val[0], 2); float32x4_t cy_sr_sy_cr = vextq_f32(sr_sy_cr_cy_sp_0_cp_1.val[0], sr_sy_cr_cy_sp_0_cp_1.val[0], 3); - float32x4_t sp_sp_sp_sp_signed = veorq_u32(vreinterpret_u32_f32(sp_sp_sp_sp), AngleQuaternion_sign2); + float32x4_t sp_sp_sp_sp_signed = veorq_u32(vreinterpretq_u32_f32(sp_sp_sp_sp), AngleQuaternion_sign2); float32x4_t left = vmulq_f32(vmulq_f32(sr_sy_cr_cy, cp_cp_cp_cp), cy_sr_sy_cr); From d4995df92ceb921fbfbd46f3f6f41fbd2bcac8bc Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 22 Jul 2023 04:36:03 +0300 Subject: [PATCH 038/127] client: studio: another NEON type fix for AngleQuaternion, static-ize global NEON vectors --- cl_dll/studio_util.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/cl_dll/studio_util.cpp b/cl_dll/studio_util.cpp index 00419a39..fc241b88 100644 --- a/cl_dll/studio_util.cpp +++ b/cl_dll/studio_util.cpp @@ -254,11 +254,7 @@ AngleQuaternion ==================== */ #if XASH_SIMD_NEON -static const float32x4_t AngleQuaternion_sign2 = - vzipq_f32( - vreinterpretq_f32_u32(vdupq_n_u32(0x80000000)), - vreinterpretq_f32_u32(vdupq_n_u32(0x00000000)) - ).val[0]; // { 0x80000000, 0x00000000, 0x80000000, 0x00000000 }; +static const uint32x4_t AngleQuaternion_sign2 = vzipq_u32(vdupq_n_u32(0x80000000), vdupq_n_u32(0x00000000)).val[0]; // { 0x80000000, 0x00000000, 0x80000000, 0x00000000 }; #endif void AngleQuaternion( float *angles, vec4_t quaternion ) { @@ -411,12 +407,12 @@ QuaternionMatrix ==================== */ #if XASH_SIMD_NEON -const uint32x4_t QuaternionMatrix_sign1 = vsetq_lane_u32(0x80000000, vdupq_n_u32(0x00000000), 0); // { 0x80000000, 0x00000000, 0x00000000, 0x00000000 }; -const uint32x4_t QuaternionMatrix_sign2 = vsetq_lane_u32(0x80000000, vdupq_n_u32(0x00000000), 1); // { 0x00000000, 0x80000000, 0x00000000, 0x00000000 }; -const uint32x4_t QuaternionMatrix_sign3 = vsetq_lane_u32(0x00000000, vdupq_n_u32(0x80000000), 2); // { 0x80000000, 0x80000000, 0x00000000, 0x80000000 }; -const float32x4_t matrix3x4_identity_0 = vsetq_lane_f32(1, vdupq_n_f32(0), 0); // { 1, 0, 0, 0 } -const float32x4_t matrix3x4_identity_1 = vsetq_lane_f32(1, vdupq_n_f32(0), 1); // { 0, 1, 0, 0 } -const float32x4_t matrix3x4_identity_2 = vsetq_lane_f32(1, vdupq_n_f32(0), 2); // { 0, 0, 1, 0 } +static const uint32x4_t QuaternionMatrix_sign1 = vsetq_lane_u32(0x80000000, vdupq_n_u32(0x00000000), 0); // { 0x80000000, 0x00000000, 0x00000000, 0x00000000 }; +static const uint32x4_t QuaternionMatrix_sign2 = vsetq_lane_u32(0x80000000, vdupq_n_u32(0x00000000), 1); // { 0x00000000, 0x80000000, 0x00000000, 0x00000000 }; +static const uint32x4_t QuaternionMatrix_sign3 = vsetq_lane_u32(0x00000000, vdupq_n_u32(0x80000000), 2); // { 0x80000000, 0x80000000, 0x00000000, 0x80000000 }; +static const float32x4_t matrix3x4_identity_0 = vsetq_lane_f32(1, vdupq_n_f32(0), 0); // { 1, 0, 0, 0 } +static const float32x4_t matrix3x4_identity_1 = vsetq_lane_f32(1, vdupq_n_f32(0), 1); // { 0, 1, 0, 0 } +static const float32x4_t matrix3x4_identity_2 = vsetq_lane_f32(1, vdupq_n_f32(0), 2); // { 0, 0, 1, 0 } #endif void QuaternionMatrix( vec4_t quaternion, float (*matrix)[4] ) From 43710ea984094aef985c49a4ec30146cec3aeb5e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 22 Jul 2023 04:45:59 +0300 Subject: [PATCH 039/127] client: studio: NEON vector types all over place here --- cl_dll/studio_util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl_dll/studio_util.cpp b/cl_dll/studio_util.cpp index fc241b88..d1dbeb7f 100644 --- a/cl_dll/studio_util.cpp +++ b/cl_dll/studio_util.cpp @@ -272,7 +272,7 @@ void AngleQuaternion( float *angles, vec4_t quaternion ) float32x4_t sy_cr_cy_sr = vextq_f32(sr_sy_cr_cy_sp_0_cp_1.val[0], sr_sy_cr_cy_sp_0_cp_1.val[0], 1); float32x4_t cr_cy_sr_sy = vextq_f32(sr_sy_cr_cy_sp_0_cp_1.val[0], sr_sy_cr_cy_sp_0_cp_1.val[0], 2); float32x4_t cy_sr_sy_cr = vextq_f32(sr_sy_cr_cy_sp_0_cp_1.val[0], sr_sy_cr_cy_sp_0_cp_1.val[0], 3); - float32x4_t sp_sp_sp_sp_signed = veorq_u32(vreinterpretq_u32_f32(sp_sp_sp_sp), AngleQuaternion_sign2); + float32x4_t sp_sp_sp_sp_signed = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(sp_sp_sp_sp), AngleQuaternion_sign2)); float32x4_t left = vmulq_f32(vmulq_f32(sr_sy_cr_cy, cp_cp_cp_cp), cy_sr_sy_cr); From 2b031dd5857ca3e14b7ccbd7167b626c057cd79e Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Tue, 29 Aug 2023 19:00:23 +0300 Subject: [PATCH 040/127] Revert fixing bigmomma wait on info_bigmomma (It affects gameplay) (#385) --- dlls/bigmomma.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/bigmomma.cpp b/dlls/bigmomma.cpp index 62188740..d668f5bd 100644 --- a/dlls/bigmomma.cpp +++ b/dlls/bigmomma.cpp @@ -931,7 +931,7 @@ void CBigMomma::StartTask( Task_t *pTask ) TaskComplete(); break; case TASK_WAIT_NODE: - m_flWaitFinished = gpGlobals->time + GetNodeDelay(); + m_flWait = gpGlobals->time + GetNodeDelay(); if( m_hTargetEnt->pev->spawnflags & SF_INFOBM_WAIT ) ALERT( at_aiconsole, "BM: Wait at node %s forever\n", STRING( pev->netname ) ); else From 26d4fac2aa63f3b4a61599e1d4cd1a875eebb329 Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Sun, 3 Sep 2023 15:54:48 +0300 Subject: [PATCH 041/127] Respect hud_saytext cvar. Remove redundant print to console (#387) --- cl_dll/saytext.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cl_dll/saytext.cpp b/cl_dll/saytext.cpp index 35d08bec..8a4bd1aa 100644 --- a/cl_dll/saytext.cpp +++ b/cl_dll/saytext.cpp @@ -100,10 +100,11 @@ int CHudSayText::Draw( float flTime ) int y = Y_START; #if USE_VGUI - if( ( gViewPort && gViewPort->AllowedToPrintText() == FALSE ) || !m_HUD_saytext->value ) + if( ( gViewPort && gViewPort->AllowedToPrintText() == FALSE ) ) return 1; #endif - + if ( !m_HUD_saytext->value ) + return 1; // make sure the scrolltime is within reasonable bounds, to guard against the clock being reset flScrollTime = Q_min( flScrollTime, flTime + m_HUD_saytext_time->value ); @@ -173,8 +174,6 @@ void CHudSayText::SayTextPrint( const char *pszBuf, int iBufSize, int clientInde ConsolePrint( pszBuf ); return; } -#else - ConsolePrint( pszBuf ); #endif int i; From 04e56448f25a5061f16c063f462d6cc61da1538e Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Tue, 19 Sep 2023 23:13:36 +0500 Subject: [PATCH 042/127] Revert "ARMv8 neon support" This reverts commit 8da76a8c3fb5246fb08526673f79f9a8d5498336. --- cl_dll/neon_mathfun.h | 356 ----------------------------------------- cl_dll/studio_util.cpp | 235 --------------------------- 2 files changed, 591 deletions(-) delete mode 100644 cl_dll/neon_mathfun.h diff --git a/cl_dll/neon_mathfun.h b/cl_dll/neon_mathfun.h deleted file mode 100644 index e45b92c1..00000000 --- a/cl_dll/neon_mathfun.h +++ /dev/null @@ -1,356 +0,0 @@ -/* NEON implementation of sin, cos, exp and log - - Inspired by Intel Approximate Math library, and based on the - corresponding algorithms of the cephes math library -*/ - -/* Copyright (C) 2011 Julien Pommier - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - (this is the zlib license) -*/ - -#include - -typedef float32x4_t v4sf; // vector of 4 float -typedef uint32x4_t v4su; // vector of 4 uint32 -typedef int32x4_t v4si; // vector of 4 uint32 - -#define s4f_x(s4f) vgetq_lane_f32(s4f, 0) -#define s4f_y(s4f) vgetq_lane_f32(s4f, 1) -#define s4f_z(s4f) vgetq_lane_f32(s4f, 2) -#define s4f_w(s4f) vgetq_lane_f32(s4f, 3) - -#define c_inv_mant_mask ~0x7f800000u -#define c_cephes_SQRTHF 0.707106781186547524 -#define c_cephes_log_p0 7.0376836292E-2 -#define c_cephes_log_p1 - 1.1514610310E-1 -#define c_cephes_log_p2 1.1676998740E-1 -#define c_cephes_log_p3 - 1.2420140846E-1 -#define c_cephes_log_p4 + 1.4249322787E-1 -#define c_cephes_log_p5 - 1.6668057665E-1 -#define c_cephes_log_p6 + 2.0000714765E-1 -#define c_cephes_log_p7 - 2.4999993993E-1 -#define c_cephes_log_p8 + 3.3333331174E-1 -#define c_cephes_log_q1 -2.12194440e-4 -#define c_cephes_log_q2 0.693359375 - -/* natural logarithm computed for 4 simultaneous float - return NaN for x <= 0 -*/ -inline v4sf log_ps(v4sf x) { - v4sf one = vdupq_n_f32(1); - - x = vmaxq_f32(x, vdupq_n_f32(0)); /* force flush to zero on denormal values */ - v4su invalid_mask = vcleq_f32(x, vdupq_n_f32(0)); - - v4si ux = vreinterpretq_s32_f32(x); - - v4si emm0 = vshrq_n_s32(ux, 23); - - /* keep only the fractional part */ - ux = vandq_s32(ux, vdupq_n_s32(c_inv_mant_mask)); - ux = vorrq_s32(ux, vreinterpretq_s32_f32(vdupq_n_f32(0.5f))); - x = vreinterpretq_f32_s32(ux); - - emm0 = vsubq_s32(emm0, vdupq_n_s32(0x7f)); - v4sf e = vcvtq_f32_s32(emm0); - - e = vaddq_f32(e, one); - - /* part2: - if( x < SQRTHF ) { - e -= 1; - x = x + x - 1.0; - } else { x = x - 1.0; } - */ - v4su mask = vcltq_f32(x, vdupq_n_f32(c_cephes_SQRTHF)); - v4sf tmp = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(x), mask)); - x = vsubq_f32(x, one); - e = vsubq_f32(e, vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(one), mask))); - x = vaddq_f32(x, tmp); - - v4sf z = vmulq_f32(x,x); - - v4sf y = vdupq_n_f32(c_cephes_log_p0); - y = vmulq_f32(y, x); - y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p1)); - y = vmulq_f32(y, x); - y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p2)); - y = vmulq_f32(y, x); - y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p3)); - y = vmulq_f32(y, x); - y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p4)); - y = vmulq_f32(y, x); - y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p5)); - y = vmulq_f32(y, x); - y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p6)); - y = vmulq_f32(y, x); - y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p7)); - y = vmulq_f32(y, x); - y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p8)); - y = vmulq_f32(y, x); - - y = vmulq_f32(y, z); - - - tmp = vmulq_f32(e, vdupq_n_f32(c_cephes_log_q1)); - y = vaddq_f32(y, tmp); - - - tmp = vmulq_f32(z, vdupq_n_f32(0.5f)); - y = vsubq_f32(y, tmp); - - tmp = vmulq_f32(e, vdupq_n_f32(c_cephes_log_q2)); - x = vaddq_f32(x, y); - x = vaddq_f32(x, tmp); - x = vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(x), invalid_mask)); // negative arg will be NAN - return x; -} - -#define c_exp_hi 88.3762626647949f -#define c_exp_lo -88.3762626647949f - -#define c_cephes_LOG2EF 1.44269504088896341 -#define c_cephes_exp_C1 0.693359375 -#define c_cephes_exp_C2 -2.12194440e-4 - -#define c_cephes_exp_p0 1.9875691500E-4 -#define c_cephes_exp_p1 1.3981999507E-3 -#define c_cephes_exp_p2 8.3334519073E-3 -#define c_cephes_exp_p3 4.1665795894E-2 -#define c_cephes_exp_p4 1.6666665459E-1 -#define c_cephes_exp_p5 5.0000001201E-1 - -/* exp() computed for 4 float at once */ -inline v4sf exp_ps(v4sf x) { - v4sf tmp, fx; - - v4sf one = vdupq_n_f32(1); - x = vminq_f32(x, vdupq_n_f32(c_exp_hi)); - x = vmaxq_f32(x, vdupq_n_f32(c_exp_lo)); - - /* express exp(x) as exp(g + n*log(2)) */ - fx = vmlaq_f32(vdupq_n_f32(0.5f), x, vdupq_n_f32(c_cephes_LOG2EF)); - - /* perform a floorf */ - tmp = vcvtq_f32_s32(vcvtq_s32_f32(fx)); - - /* if greater, substract 1 */ - v4su mask = vcgtq_f32(tmp, fx); - mask = vandq_u32(mask, vreinterpretq_u32_f32(one)); - - - fx = vsubq_f32(tmp, vreinterpretq_f32_u32(mask)); - - tmp = vmulq_f32(fx, vdupq_n_f32(c_cephes_exp_C1)); - v4sf z = vmulq_f32(fx, vdupq_n_f32(c_cephes_exp_C2)); - x = vsubq_f32(x, tmp); - x = vsubq_f32(x, z); - - static const float cephes_exp_p[6] = { c_cephes_exp_p0, c_cephes_exp_p1, c_cephes_exp_p2, c_cephes_exp_p3, c_cephes_exp_p4, c_cephes_exp_p5 }; - v4sf y = vld1q_dup_f32(cephes_exp_p+0); - v4sf c1 = vld1q_dup_f32(cephes_exp_p+1); - v4sf c2 = vld1q_dup_f32(cephes_exp_p+2); - v4sf c3 = vld1q_dup_f32(cephes_exp_p+3); - v4sf c4 = vld1q_dup_f32(cephes_exp_p+4); - v4sf c5 = vld1q_dup_f32(cephes_exp_p+5); - - y = vmulq_f32(y, x); - z = vmulq_f32(x,x); - y = vaddq_f32(y, c1); - y = vmulq_f32(y, x); - y = vaddq_f32(y, c2); - y = vmulq_f32(y, x); - y = vaddq_f32(y, c3); - y = vmulq_f32(y, x); - y = vaddq_f32(y, c4); - y = vmulq_f32(y, x); - y = vaddq_f32(y, c5); - - y = vmulq_f32(y, z); - y = vaddq_f32(y, x); - y = vaddq_f32(y, one); - - /* build 2^n */ - int32x4_t mm; - mm = vcvtq_s32_f32(fx); - mm = vaddq_s32(mm, vdupq_n_s32(0x7f)); - mm = vshlq_n_s32(mm, 23); - v4sf pow2n = vreinterpretq_f32_s32(mm); - - y = vmulq_f32(y, pow2n); - return y; -} - -#define c_minus_cephes_DP1 -0.78515625 -#define c_minus_cephes_DP2 -2.4187564849853515625e-4 -#define c_minus_cephes_DP3 -3.77489497744594108e-8 -#define c_sincof_p0 -1.9515295891E-4 -#define c_sincof_p1 8.3321608736E-3 -#define c_sincof_p2 -1.6666654611E-1 -#define c_coscof_p0 2.443315711809948E-005 -#define c_coscof_p1 -1.388731625493765E-003 -#define c_coscof_p2 4.166664568298827E-002 -#define c_cephes_FOPI 1.27323954473516 // 4 / M_PI - -/* evaluation of 4 sines & cosines at once. - - The code is the exact rewriting of the cephes sinf function. - Precision is excellent as long as x < 8192 (I did not bother to - take into account the special handling they have for greater values - -- it does not return garbage for arguments over 8192, though, but - the extra precision is missing). - - Note that it is such that sinf((float)M_PI) = 8.74e-8, which is the - surprising but correct result. - - Note also that when you compute sin(x), cos(x) is available at - almost no extra price so both sin_ps and cos_ps make use of - sincos_ps.. - */ -inline void sincos_ps(v4sf x, v4sf *ysin, v4sf *ycos) { // any x - v4sf y; - - v4su emm2; - - v4su sign_mask_sin, sign_mask_cos; - sign_mask_sin = vcltq_f32(x, vdupq_n_f32(0)); - x = vabsq_f32(x); - - /* scale by 4/Pi */ - y = vmulq_n_f32(x, c_cephes_FOPI); - - /* store the integer part of y in mm0 */ - emm2 = vcvtq_u32_f32(y); - /* j=(j+1) & (~1) (see the cephes sources) */ - emm2 = vaddq_u32(emm2, vdupq_n_u32(1)); - emm2 = vandq_u32(emm2, vdupq_n_u32(~1)); - y = vcvtq_f32_u32(emm2); - - /* get the polynom selection mask - there is one polynom for 0 <= x <= Pi/4 - and another one for Pi/4 -#include "neon_mathfun.h" -#endif /* ==================== @@ -23,59 +17,8 @@ AngleMatrix ==================== */ -#if XASH_SIMD_NEON -static const uint32x4_t AngleMatrix_sign0 = vsetq_lane_u32(0x80000000, vdupq_n_u32(0), 0); -static const uint32x4_t AngleMatrix_sign1 = vsetq_lane_u32(0x80000000, vdupq_n_u32(0), 1); -static const uint32x4_t AngleMatrix_sign2 = vsetq_lane_u32(0x80000000, vdupq_n_u32(0), 2); -#endif void AngleMatrix( const float *angles, float (*matrix)[4] ) { -#if XASH_SIMD_NEON - float32x4x3_t out_reg; - float32x4_t angles_reg = {}; - memcpy(&angles_reg, angles, sizeof(float) * 3); - - float32x4x2_t sp_sy_sr_0_cp_cy_cr_1; - sincos_ps(vmulq_n_f32(angles_reg, (M_PI * 2 / 360)), &sp_sy_sr_0_cp_cy_cr_1.val[0], &sp_sy_sr_0_cp_cy_cr_1.val[1]); - - float32x4x2_t sp_sr_cp_cr_sy_0_cy_1 = vuzpq_f32(sp_sy_sr_0_cp_cy_cr_1.val[0], sp_sy_sr_0_cp_cy_cr_1.val[1]); - float32x4x2_t sp_cp_sy_cy_sr_cr_0_1 = vzipq_f32(sp_sy_sr_0_cp_cy_cr_1.val[0], sp_sy_sr_0_cp_cy_cr_1.val[1]); - - float32x4_t _0_sr_cr_0 = vextq_f32(sp_sy_sr_0_cp_cy_cr_1.val[0], sp_cp_sy_cy_sr_cr_0_1.val[1], 3); - float32x4_t cp_cr_sr_0 = vcombine_f32(vget_high_f32(sp_sr_cp_cr_sy_0_cy_1.val[0]), vget_high_f32(sp_sy_sr_0_cp_cy_cr_1.val[0])); - float32x4_t cy_sy_sy_0 = vcombine_f32(vrev64_f32(vget_high_f32(sp_cp_sy_cy_sr_cr_0_1.val[0])), vget_low_f32(sp_sr_cp_cr_sy_0_cy_1.val[1])); - float32x4_t sy_cy_cy_1 = vcombine_f32(vget_high_f32(sp_cp_sy_cy_sr_cr_0_1.val[0]), vget_high_f32(sp_sr_cp_cr_sy_0_cy_1.val[1])); - - float32x4_t _0_srsp_crsp_0 = vmulq_laneq_f32(_0_sr_cr_0, sp_sy_sr_0_cp_cy_cr_1.val[0], 0); // *sp - out_reg.val[0] = vmulq_laneq_f32(_0_srsp_crsp_0, sp_sy_sr_0_cp_cy_cr_1.val[1], 1); // *cy - out_reg.val[1] = vmulq_laneq_f32(_0_srsp_crsp_0, sp_sy_sr_0_cp_cy_cr_1.val[0], 1); // *sy - - cy_sy_sy_0 = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(cy_sy_sy_0), AngleMatrix_sign1)); - sy_cy_cy_1 = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(sy_cy_cy_1), AngleMatrix_sign2)); - out_reg.val[0] = vfmaq_f32(out_reg.val[0], cp_cr_sr_0, cy_sy_sy_0); - out_reg.val[1] = vfmaq_f32(out_reg.val[1], cp_cr_sr_0, sy_cy_cy_1); - - float32x4_t cp_cr_0_1 = vcombine_f32(vget_high_f32(sp_sr_cp_cr_sy_0_cy_1.val[0]), vget_high_f32(sp_cp_sy_cy_sr_cr_0_1.val[1])); - float32x4_t _1_cp_cr_0 = vextq_f32(cp_cr_0_1, cp_cr_0_1, 3); - out_reg.val[2] = vmulq_f32(sp_sr_cp_cr_sy_0_cy_1.val[0], _1_cp_cr_0); - out_reg.val[2] = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(out_reg.val[2]), AngleMatrix_sign0)); - - memcpy(matrix, &out_reg, sizeof(float) * 3 * 4); -/* - matrix[0][0] = cp*cy; - matrix[0][1] = sr*sp*cy-cr*sy; - matrix[0][2] = cr*sp*cy+sr*sy; - matrix[0][3] = 0.0; - matrix[1][0] = cp*sy; - matrix[1][1] = sr*sp*sy+cr*cy; - matrix[1][2] = cr*sp*sy-sr*cy; - matrix[1][3] = 0.0; - matrix[2][0] = -sp*1; - matrix[2][1] = sr*cp; - matrix[2][2] = cp*cr; - matrix[2][3] = cr*0; -*/ -#else float angle; float sr, sp, sy, cr, cp, cy; @@ -102,7 +45,6 @@ void AngleMatrix( const float *angles, float (*matrix)[4] ) matrix[0][3] = 0.0f; matrix[1][3] = 0.0f; matrix[2][3] = 0.0f; -#endif } /* @@ -113,13 +55,6 @@ VectorCompare */ int VectorCompare( const float *v1, const float *v2 ) { -#if XASH_SIMD_NEON - // is this really works? - float32x4_t v1_reg = {}, v2_reg = {}; - memcpy(&v1_reg, v1, sizeof(float) * 3); - memcpy(&v2_reg, v2, sizeof(float) * 3); - return !vaddvq_u32(vceqq_f32(v1_reg, v2_reg)); -#else int i; for( i = 0; i < 3; i++ ) @@ -127,7 +62,6 @@ int VectorCompare( const float *v1, const float *v2 ) return 0; return 1; -#endif } /* @@ -138,23 +72,9 @@ CrossProduct */ void CrossProduct( const float *v1, const float *v2, float *cross ) { -#if XASH_SIMD_NEON - float32x4_t v1_reg = {}, v2_reg = {}; - memcpy(&v1_reg, v1, sizeof(float) * 3); - memcpy(&v2_reg, v2, sizeof(float) * 3); - - float32x4_t yzxy_a = vextq_f32(vextq_f32(v1_reg, v1_reg, 3), v1_reg, 2); // [aj, ak, ai, aj] - float32x4_t yzxy_b = vextq_f32(vextq_f32(v2_reg, v2_reg, 3), v2_reg, 2); // [bj, bk, bi, bj] - float32x4_t zxyy_a = vextq_f32(yzxy_a, yzxy_a, 1); // [ak, ai, aj, aj] - float32x4_t zxyy_b = vextq_f32(yzxy_b, yzxy_b, 1); // [bk, ai, bj, bj] - float32x4_t cross_reg = vfmsq_f32(vmulq_f32(yzxy_a, zxyy_b), zxyy_a, yzxy_b); // [ajbk-akbj, akbi-aibk, aibj-ajbi, 0] - - memcpy(cross, &cross_reg, sizeof(float) * 3); -#else cross[0] = v1[1] * v2[2] - v1[2] * v2[1]; cross[1] = v1[2] * v2[0] - v1[0] * v2[2]; cross[2] = v1[0] * v2[1] - v1[1] * v2[0]; -#endif } /* @@ -165,26 +85,9 @@ VectorTransform */ void VectorTransform( const float *in1, float in2[3][4], float *out ) { -#if XASH_SIMD_NEON - float32x4_t in1_reg = {}; - memcpy(&in1_reg, &in1, sizeof(float) * 3); - - float32x4x4_t in_t; - memcpy(&in_t, &in2, sizeof(float) * 3 * 4); - //memset(&in_t.val[3], 0, sizeof(in_t.val[3])); - in_t = vld4q_f32((const float*)&in_t); - - float32x4_t out_reg = in_t.val[3]; - out_reg = vfmaq_laneq_f32(out_reg, in_t.val[0], in1_reg, 0); - out_reg = vfmaq_laneq_f32(out_reg, in_t.val[1], in1_reg, 1); - out_reg = vfmaq_laneq_f32(out_reg, in_t.val[2], in1_reg, 2); - - memcpy(out, &out_reg, sizeof(float) * 3); -#else out[0] = DotProduct(in1, in2[0]) + in2[0][3]; out[1] = DotProduct(in1, in2[1]) + in2[1][3]; out[2] = DotProduct(in1, in2[2]) + in2[2][3]; -#endif } /* @@ -195,29 +98,6 @@ ConcatTransforms */ void ConcatTransforms( float in1[3][4], float in2[3][4], float out[3][4] ) { -#if XASH_SIMD_NEON - float32x4x3_t in1_reg, in2_reg; - memcpy(&in1_reg, in1, sizeof(float) * 3 * 4); - memcpy(&in2_reg, in2, sizeof(float) * 3 * 4); - float32x4x3_t out_reg = {}; - - out_reg.val[0] = vcopyq_laneq_f32(out_reg.val[0], 3, in1_reg.val[0], 3); // out[0][3] = in[0][3] - out_reg.val[0] = vfmaq_laneq_f32(out_reg.val[0], in2_reg.val[0], in1_reg.val[0], 0); // out[0][n] += in2[0][n] * in1[0][0] - out_reg.val[0] = vfmaq_laneq_f32(out_reg.val[0], in2_reg.val[1], in1_reg.val[0], 1); // out[0][n] += in2[1][n] * in1[0][1] - out_reg.val[0] = vfmaq_laneq_f32(out_reg.val[0], in2_reg.val[2], in1_reg.val[0], 2); // out[0][n] += in2[2][n] * in1[0][2] - - out_reg.val[1] = vcopyq_laneq_f32(out_reg.val[1], 3, in1_reg.val[1], 3); - out_reg.val[1] = vfmaq_laneq_f32(out_reg.val[1], in2_reg.val[0], in1_reg.val[1], 0); - out_reg.val[1] = vfmaq_laneq_f32(out_reg.val[1], in2_reg.val[1], in1_reg.val[1], 1); - out_reg.val[1] = vfmaq_laneq_f32(out_reg.val[1], in2_reg.val[2], in1_reg.val[1], 2); - - out_reg.val[2] = vcopyq_laneq_f32(out_reg.val[2], 3, in1_reg.val[2], 3); - out_reg.val[2] = vfmaq_laneq_f32(out_reg.val[2], in2_reg.val[0], in1_reg.val[2], 0); - out_reg.val[2] = vfmaq_laneq_f32(out_reg.val[2], in2_reg.val[1], in1_reg.val[2], 1); - out_reg.val[2] = vfmaq_laneq_f32(out_reg.val[2], in2_reg.val[2], in1_reg.val[2], 2); - - memcpy(out, &out_reg, sizeof(float) * 3 * 4); -#else out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + @@ -242,7 +122,6 @@ void ConcatTransforms( float in1[3][4], float in2[3][4], float out[3][4] ) in1[2][2] * in2[2][2]; out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3]; -#endif } // angles index are not the same as ROLL, PITCH, YAW @@ -253,36 +132,8 @@ AngleQuaternion ==================== */ -#if XASH_SIMD_NEON -static const uint32x4_t AngleQuaternion_sign2 = vzipq_u32(vdupq_n_u32(0x80000000), vdupq_n_u32(0x00000000)).val[0]; // { 0x80000000, 0x00000000, 0x80000000, 0x00000000 }; -#endif void AngleQuaternion( float *angles, vec4_t quaternion ) { -#if XASH_SIMD_NEON - float32x4_t angles_reg = {}; - memcpy(&angles_reg, angles, sizeof(float) * 3); - float32x4x2_t sr_sp_sy_0_cr_cp_cy_1; - sincos_ps(vmulq_n_f32(angles_reg, 0.5), &sr_sp_sy_0_cr_cp_cy_1.val[0], &sr_sp_sy_0_cr_cp_cy_1.val[1]); - - float32x4x2_t sr_sy_cr_cy_sp_0_cp_1 = vuzpq_f32(sr_sp_sy_0_cr_cp_cy_1.val[0], sr_sp_sy_0_cr_cp_cy_1.val[1]); - float32x4_t cp_cp_cp_cp = vdupq_laneq_f32(sr_sp_sy_0_cr_cp_cy_1.val[1], 1); - float32x4_t sp_sp_sp_sp = vdupq_laneq_f32(sr_sp_sy_0_cr_cp_cy_1.val[0], 1); - - float32x4_t sr_sy_cr_cy = sr_sy_cr_cy_sp_0_cp_1.val[0]; - float32x4_t sy_cr_cy_sr = vextq_f32(sr_sy_cr_cy_sp_0_cp_1.val[0], sr_sy_cr_cy_sp_0_cp_1.val[0], 1); - float32x4_t cr_cy_sr_sy = vextq_f32(sr_sy_cr_cy_sp_0_cp_1.val[0], sr_sy_cr_cy_sp_0_cp_1.val[0], 2); - float32x4_t cy_sr_sy_cr = vextq_f32(sr_sy_cr_cy_sp_0_cp_1.val[0], sr_sy_cr_cy_sp_0_cp_1.val[0], 3); - float32x4_t sp_sp_sp_sp_signed = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(sp_sp_sp_sp), AngleQuaternion_sign2)); - - float32x4_t left = vmulq_f32(vmulq_f32(sr_sy_cr_cy, cp_cp_cp_cp), cy_sr_sy_cr); - - float32x4_t out_reg = vfmaq_f32(left, vmulq_f32(cr_cy_sr_sy, sp_sp_sp_sp_signed), sy_cr_cy_sr); - memcpy(quaternion, &out_reg, sizeof(float) * 4); - //quaternion[0] = sr * cp * cy - cr * sp * sy; // X - //quaternion[1] = sy * cp * sr + cy * sp * cr; // Y - //quaternion[2] = cr * cp * sy - sr * sp * cy; // Z - //quaternion[3] = cy * cp * cr + sy * sp * sr; // W -#else float angle; float sr, sp, sy, cr, cp, cy; @@ -301,7 +152,6 @@ void AngleQuaternion( float *angles, vec4_t quaternion ) quaternion[1] = cr * sp * cy + sr * cp * sy; // Y quaternion[2] = cr * cp * sy - sr * sp * cy; // Z quaternion[3] = cr * cp * cy + sr * sp * sy; // W -#endif } /* @@ -312,37 +162,6 @@ QuaternionSlerp */ void QuaternionSlerp( vec4_t p, vec4_t q, float t, vec4_t qt ) { -#if XASH_SIMD_NEON - float32x4_t p_reg = {}, q_reg = {}; - memcpy(&p_reg, p, sizeof(float) * 4); - memcpy(&q_reg, q, sizeof(float) * 4); - - // q = (cos(a/2), xsin(a/2), ysin(a/2), zsin(a/2)) - // cos(a-b) = cosacosb+sinasinb - const uint32x4_t signmask = vdupq_n_u32(0x80000000); - const float32x4_t one_minus_epsilon = vdupq_n_f32(1.0f - 0.00001f); - - float32x4_t vcosom = vdupq_n_f32(DotProduct(p, q)); - uint32x4_t sign = vandq_u32(vreinterpretq_u32_f32(vcosom), signmask); - q_reg = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(p_reg), sign)); - vcosom = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(vcosom), sign)); - - float x[4] = {(1.0f - t), t, 1, 0}; // cosom -> 1, sinom -> 0, sinx ~ x - float32x4_t x_reg; - memcpy(&x_reg, x, sizeof(float) * 4); - - // if ((1.0 - cosom) > 0.000001) x = sin(x * omega) - uint32x4_t cosom_less_then_one = vcltq_f32(vcosom, one_minus_epsilon); - float32x4_t vomega = acos_ps(vcosom); - x_reg = vbslq_f32(cosom_less_then_one, x_reg, sin_ps(vmulq_f32(x_reg, vomega))); - - // qt = (x[0] * p + x[1] * q) / x[2]; - float32x4_t qt_reg = vmulq_laneq_f32(p_reg, x_reg, 0); - qt_reg = vfmaq_laneq_f32(qt_reg, q_reg, x_reg, 1); - qt_reg = vdivq_f32(qt_reg, vdupq_laneq_f32(x_reg, 2)); // vdivq_laneq_f32 ? - - memcpy(qt, &qt_reg, sizeof(float) * 4); -#else int i; float omega, cosom, sinom, sclp, sclq; @@ -397,7 +216,6 @@ void QuaternionSlerp( vec4_t p, vec4_t q, float t, vec4_t qt ) qt[i] = sclp * p[i] + sclq * qt[i]; } } -#endif } /* @@ -406,60 +224,8 @@ QuaternionMatrix ==================== */ -#if XASH_SIMD_NEON -static const uint32x4_t QuaternionMatrix_sign1 = vsetq_lane_u32(0x80000000, vdupq_n_u32(0x00000000), 0); // { 0x80000000, 0x00000000, 0x00000000, 0x00000000 }; -static const uint32x4_t QuaternionMatrix_sign2 = vsetq_lane_u32(0x80000000, vdupq_n_u32(0x00000000), 1); // { 0x00000000, 0x80000000, 0x00000000, 0x00000000 }; -static const uint32x4_t QuaternionMatrix_sign3 = vsetq_lane_u32(0x00000000, vdupq_n_u32(0x80000000), 2); // { 0x80000000, 0x80000000, 0x00000000, 0x80000000 }; -static const float32x4_t matrix3x4_identity_0 = vsetq_lane_f32(1, vdupq_n_f32(0), 0); // { 1, 0, 0, 0 } -static const float32x4_t matrix3x4_identity_1 = vsetq_lane_f32(1, vdupq_n_f32(0), 1); // { 0, 1, 0, 0 } -static const float32x4_t matrix3x4_identity_2 = vsetq_lane_f32(1, vdupq_n_f32(0), 2); // { 0, 0, 1, 0 } -#endif - void QuaternionMatrix( vec4_t quaternion, float (*matrix)[4] ) { -#if XASH_SIMD_NEON - float32x4_t quaternion_reg; - memcpy(&quaternion_reg, quaternion, sizeof(float) * 4); - - float32x4_t q1032 = vrev64q_f32(quaternion_reg); - float32x4_t q1032_signed = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(q1032), QuaternionMatrix_sign1)); - float32x4_t q2301 = vextq_f32(quaternion_reg, quaternion_reg, 2); - float32x4_t q2301_signed = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(q2301), QuaternionMatrix_sign3)); - float32x4_t q3210 = vrev64q_f32(q2301); - float32x4_t q3210_signed = vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(q3210), QuaternionMatrix_sign2)); - - float32x4x3_t out_reg; - - out_reg.val[0] = vmulq_laneq_f32(q2301_signed, quaternion_reg, 2); - out_reg.val[0] = vfmaq_laneq_f32(out_reg.val[0], q1032_signed, quaternion_reg, 1); - out_reg.val[0] = vfmaq_n_f32(matrix3x4_identity_0, out_reg.val[0], 2.0f); - - out_reg.val[1] = vmulq_laneq_f32(q3210_signed, quaternion_reg, 2); - out_reg.val[1] = vfmsq_laneq_f32(out_reg.val[1], q1032_signed, quaternion_reg, 0); - out_reg.val[1] = vfmaq_n_f32(matrix3x4_identity_1, out_reg.val[1], 2.0f); - - out_reg.val[2] = vmulq_laneq_f32(q3210_signed, quaternion_reg, 1); - out_reg.val[2] = vfmaq_laneq_f32(out_reg.val[2], q2301_signed, quaternion_reg, 0); - out_reg.val[2] = vfmsq_n_f32(matrix3x4_identity_2, out_reg.val[2], 2.0f); - - memcpy(matrix, &out_reg, sizeof(float) * 3 * 4); -/* - matrix[0][0] = 1.0 + 2.0 * ( quaternion[1] * -quaternion[1] + -quaternion[2] * quaternion[2] ); - matrix[0][1] = 0.0 + 2.0 * ( quaternion[1] * quaternion[0] + -quaternion[3] * quaternion[2] ); - matrix[0][2] = 0.0 + 2.0 * ( quaternion[1] * quaternion[3] + quaternion[0] * quaternion[2] ); - matrix[0][3] = 0.0 + 2.0 * ( quaternion[1] * quaternion[2] + -quaternion[1] * quaternion[2] ); - - matrix[1][0] = 0.0 + 2.0 * ( -quaternion[0] * -quaternion[1] + quaternion[3] * quaternion[2] ); - matrix[1][1] = 1.0 + 2.0 * ( -quaternion[0] * quaternion[0] + -quaternion[2] * quaternion[2] ); - matrix[1][2] = 0.0 + 2.0 * ( -quaternion[0] * quaternion[3] + quaternion[1] * quaternion[2] ); - matrix[1][3] = 0.0 + 2.0 * ( -quaternion[0] * quaternion[2] + quaternion[0] * quaternion[2] ); - - matrix[2][0] = 0.0 + 2.0 * ( -quaternion[0] * -quaternion[2] + -quaternion[3] * quaternion[1] ); - matrix[2][1] = 0.0 + 2.0 * ( -quaternion[0] * -quaternion[3] + quaternion[2] * quaternion[1] ); - matrix[2][2] = 1.0 + 2.0 * ( -quaternion[0] * quaternion[0] + -quaternion[1] * quaternion[1] ); - matrix[2][3] = 0.0 + 2.0 * ( -quaternion[0] * -quaternion[1] + -quaternion[0] * quaternion[1] ); -*/ -#else matrix[0][0] = 1.0f - 2.0f * quaternion[1] * quaternion[1] - 2.0f * quaternion[2] * quaternion[2]; matrix[1][0] = 2.0f * quaternion[0] * quaternion[1] + 2.0f * quaternion[3] * quaternion[2]; matrix[2][0] = 2.0f * quaternion[0] * quaternion[2] - 2.0f * quaternion[3] * quaternion[1]; @@ -471,7 +237,6 @@ void QuaternionMatrix( vec4_t quaternion, float (*matrix)[4] ) matrix[0][2] = 2.0f * quaternion[0] * quaternion[2] + 2.0f * quaternion[3] * quaternion[1]; matrix[1][2] = 2.0f * quaternion[1] * quaternion[2] - 2.0f * quaternion[3] * quaternion[0]; matrix[2][2] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[1] * quaternion[1]; -#endif } /* From 17b34aa42b2060706deb20aa82df0ea2766e1b18 Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Sat, 30 Sep 2023 21:27:53 +0300 Subject: [PATCH 043/127] Reset fov on crossbow holster (#394) --- dlls/crossbow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/crossbow.cpp b/dlls/crossbow.cpp index 9432b04f..f52be735 100644 --- a/dlls/crossbow.cpp +++ b/dlls/crossbow.cpp @@ -324,7 +324,7 @@ void CCrossbow::Holster( int skiplocal /* = 0 */ ) { m_fInReload = FALSE;// cancel any reload in progress. - if( m_fInZoom ) + if( m_pPlayer->pev->fov != 0 ) { SecondaryAttack(); } From 3cf40ccf121a935d3aea836c1d150df251d95c56 Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Tue, 17 Oct 2023 18:15:10 +0300 Subject: [PATCH 044/127] When finding nearest friend, skip the dying ones as well (they shouldn't participate in talking) (#398) --- dlls/talkmonster.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/talkmonster.cpp b/dlls/talkmonster.cpp index 83100466..ed173231 100644 --- a/dlls/talkmonster.cpp +++ b/dlls/talkmonster.cpp @@ -791,7 +791,7 @@ CBaseEntity *CTalkMonster::FindNearestFriend( BOOL fPlayer ) // for each friend in this bsp... while( ( pFriend = UTIL_FindEntityByClassname( pFriend, pszFriend ) ) ) { - if( pFriend == this || !pFriend->IsAlive() ) + if( pFriend == this || !pFriend->IsAlive() || pFriend->pev->deadflag != DEAD_NO ) // don't talk to self or dead people continue; From 3b2ba69a4fc38f48c2e9dfa5077feec77a5eec35 Mon Sep 17 00:00:00 2001 From: mittorn Date: Tue, 31 Oct 2023 05:12:27 +0300 Subject: [PATCH 045/127] wscript: get node path from waf instead of using relative path --- wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wscript b/wscript index 02efd884..0ea0c636 100644 --- a/wscript +++ b/wscript @@ -211,7 +211,7 @@ def configure(conf): conf.msg(msg='-> processing mod options', result='...', color='BLUE') regex = re.compile('^([A-Za-z0-9_]+)=([A-Za-z0-9_]+)\ \#\ (.*)$') - with open('mod_options.txt') as fd: + with open(str(conf.path.make_node('mod_options.txt'))) as fd: lines = fd.readlines() for line in lines: m = regex.match(line.strip()) From 984986f99840b6d7f369cf3a727dc61250fa3b47 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Wed, 1 Nov 2023 01:25:07 +0500 Subject: [PATCH 046/127] Use definitions from build.h everywhere. --- cl_dll/cdll_int.cpp | 2 +- cl_dll/cl_dll.h | 5 +- cl_dll/in_camera.cpp | 2 +- cl_dll/input_goldsource.cpp | 72 ++++++++++++++-------------- cl_dll/input_mouse.h | 2 +- cl_dll/vgui_TeamFortressViewport.cpp | 4 +- dlls/cbase.cpp | 4 +- dlls/exportdef.h | 2 +- dlls/extdll.h | 6 +-- dlls/h_export.cpp | 2 +- dlls/util.cpp | 2 +- utils/fake_vgui/include/VGUI.h | 2 +- utils/fake_vgui/include/VGUI_Dar.h | 4 +- 13 files changed, 51 insertions(+), 58 deletions(-) diff --git a/cl_dll/cdll_int.cpp b/cl_dll/cdll_int.cpp index 57f879d7..55516b84 100644 --- a/cl_dll/cdll_int.cpp +++ b/cl_dll/cdll_int.cpp @@ -28,7 +28,7 @@ #include "vgui_TeamFortressViewport.h" #endif -#if GOLDSOURCE_SUPPORT && (_WIN32 || __linux__ || __APPLE__) && (__i386 || _M_IX86) +#if GOLDSOURCE_SUPPORT && (XASH_WIN32 || XASH_LINUX || XASH_APPLE) && XASH_X86 #define USE_FAKE_VGUI !USE_VGUI #if USE_FAKE_VGUI #include "VGUI_Panel.h" diff --git a/cl_dll/cl_dll.h b/cl_dll/cl_dll.h index 5e5fe905..e29f1e24 100644 --- a/cl_dll/cl_dll.h +++ b/cl_dll/cl_dll.h @@ -39,7 +39,7 @@ typedef float vec_t; #include "../engine/cdll_int.h" #include "../dlls/cdll_dll.h" -#if !_WIN32 +#if !XASH_WIN32 #define _cdecl #endif #include "exportdef.h" @@ -49,9 +49,6 @@ typedef float vec_t; #else #include #endif -#if __LP64__ || __LLP64__ || _WIN64 || (__x86_64__ && !__ILP32__) || _M_X64 || __ia64 || _M_IA64 || __aarch64__ || __powerpc64__ - #define XASH_64BIT 1 -#endif extern cl_enginefunc_t gEngfuncs; #include "../engine/mobility_int.h" diff --git a/cl_dll/in_camera.cpp b/cl_dll/in_camera.cpp index 7a81a15f..143b8c5f 100644 --- a/cl_dll/in_camera.cpp +++ b/cl_dll/in_camera.cpp @@ -15,7 +15,7 @@ #include "camera.h" #include "in_defs.h" -#if _WIN32 +#if XASH_WIN32 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #define WIN32_EXTRA_LEAN #define HSPRITE WINDOWS_HSPRITE diff --git a/cl_dll/input_goldsource.cpp b/cl_dll/input_goldsource.cpp index 89408423..5f96f304 100644 --- a/cl_dll/input_goldsource.cpp +++ b/cl_dll/input_goldsource.cpp @@ -23,7 +23,7 @@ #include "keydefs.h" #include "view.h" -#if !_WIN32 +#if !XASH_WIN32 #define USE_SDL2 1 #endif @@ -114,7 +114,7 @@ static SDLFunction sdlFunctions[] = { }; #endif -#if _WIN32 +#if XASH_WIN32 #include #else typedef unsigned int DWORD; @@ -149,7 +149,7 @@ extern cvar_t *cl_forwardspeed; extern cvar_t *cl_pitchspeed; extern cvar_t *cl_movespeedkey; -#if _WIN32 +#if XASH_WIN32 static cvar_t* m_rawinput = NULL; static double s_flRawInputUpdateTime = 0.0f; static bool m_bRawInput = false; @@ -157,7 +157,7 @@ static bool m_bMouseThread = false; bool isMouseRelative = false; #endif -#if _WIN32 +#if XASH_WIN32 #include "progdefs.h" #endif @@ -184,7 +184,7 @@ static cvar_t *m_customaccel_max; //Mouse move is raised to this power before being scaled by scale factor static cvar_t *m_customaccel_exponent; -#if _WIN32 +#if XASH_WIN32 // if threaded mouse is enabled then the time to sleep between polls static cvar_t *m_mousethread_sleep; #endif @@ -218,7 +218,7 @@ enum _ControlList AxisTurn }; -#if !USE_SDL2 && _WIN32 +#if !USE_SDL2 && XASH_WIN32 DWORD dwAxisFlags[JOY_MAX_AXES] = { JOY_RETURNX, @@ -234,7 +234,7 @@ DWORD dwAxisMap[ JOY_MAX_AXES ]; DWORD dwControlMap[ JOY_MAX_AXES ]; #if USE_SDL2 int pdwRawValue[ JOY_MAX_AXES ]; -#elif _WIN32 +#elif XASH_WIN32 PDWORD pdwRawValue[ JOY_MAX_AXES ]; #endif DWORD joy_oldbuttonstate, joy_oldpovstate; @@ -244,7 +244,7 @@ DWORD joy_numbuttons; #if USE_SDL2 SDL_GameController *s_pJoystick = NULL; -#elif _WIN32 +#elif XASH_WIN32 DWORD joy_flags; static JOYINFOEX ji; #endif @@ -276,7 +276,7 @@ cvar_t *joy_wwhack2; int joy_avail, joy_advancedinit, joy_haspov; -#if _WIN32 +#if XASH_WIN32 unsigned int s_hMouseThreadId = 0; HANDLE s_hMouseThread = 0; HANDLE s_hMouseQuitEvent = 0; @@ -300,7 +300,7 @@ void Force_CenterView_f (void) } } -#if _WIN32 +#if XASH_WIN32 LONG mouseThreadActive = 0; LONG mouseThreadCenterX = 0; @@ -382,7 +382,7 @@ void IN_SetMouseMode(bool enable) if(enable) { -#if _WIN32 +#if XASH_WIN32 if (mouseparmsvalid) restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0); @@ -402,7 +402,7 @@ void IN_SetMouseMode(bool enable) } else { -#if _WIN32 +#if XASH_WIN32 if(isMouseRelative) { #if USE_SDL2 @@ -423,7 +423,7 @@ void IN_SetMouseMode(bool enable) void IN_SetVisibleMouse(bool visible) { -#if _WIN32 +#if XASH_WIN32 bool lockEntered = MouseThread_ActiveLock_Enter(); #endif @@ -431,7 +431,7 @@ void IN_SetVisibleMouse(bool visible) IN_SetMouseMode(!visible); -#if _WIN32 +#if XASH_WIN32 UpdateMouseThreadActive(); if(lockEntered) MouseThread_ActiveLock_Exit(); #endif @@ -448,7 +448,7 @@ void GoldSourceInput::IN_ActivateMouse (void) { if (mouseinitialized) { -#if _WIN32 +#if XASH_WIN32 bool lockEntered = MouseThread_ActiveLock_Enter(); #endif @@ -456,7 +456,7 @@ void GoldSourceInput::IN_ActivateMouse (void) mouseactive = 1; -#if _WIN32 +#if XASH_WIN32 UpdateMouseThreadActive(); if(lockEntered) MouseThread_ActiveLock_Exit(); #endif @@ -476,7 +476,7 @@ void GoldSourceInput::IN_DeactivateMouse (void) { if (mouseinitialized) { -#if _WIN32 +#if XASH_WIN32 bool lockEntered = MouseThread_ActiveLock_Enter(); #endif @@ -484,7 +484,7 @@ void GoldSourceInput::IN_DeactivateMouse (void) mouseactive = 0; -#if _WIN32 +#if XASH_WIN32 UpdateMouseThreadActive(); if(lockEntered) MouseThread_ActiveLock_Exit(); #endif @@ -502,7 +502,7 @@ void GoldSourceInput::IN_StartupMouse (void) return; mouseinitialized = 1; -#if _WIN32 +#if XASH_WIN32 mouseparmsvalid = SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0); if (mouseparmsvalid) @@ -537,7 +537,7 @@ void GoldSourceInput::IN_Shutdown (void) { IN_DeactivateMouse (); -#if _WIN32 +#if XASH_WIN32 if ( s_hMouseQuitEvent ) { SetEvent( s_hMouseQuitEvent ); @@ -597,7 +597,7 @@ FIXME: Call through to engine? void IN_ResetMouse( void ) { // no work to do in SDL -#if _WIN32 +#if XASH_WIN32 // reset only if mouse is active and not in visible mode: if(mouseactive && !iVisibleMouse && gEngfuncs.GetWindowCenterX && gEngfuncs.GetWindowCenterY) { @@ -712,7 +712,7 @@ void GoldSourceInput::IN_GetMouseDelta( int *pOutX, int *pOutY) if(active) { int deltaX, deltaY; -#if _WIN32 +#if XASH_WIN32 if ( !m_bRawInput ) { if ( m_bMouseThread ) @@ -746,7 +746,7 @@ void GoldSourceInput::IN_GetMouseDelta( int *pOutX, int *pOutY) #endif } -#if _WIN32 +#if XASH_WIN32 if ( !m_bRawInput ) { if ( m_bMouseThread ) @@ -771,7 +771,7 @@ void GoldSourceInput::IN_GetMouseDelta( int *pOutX, int *pOutY) my_accum = 0; // reset mouse position if required, so there is room to move: -#if _WIN32 +#if XASH_WIN32 // do not reset if mousethread would do it: if ( m_bRawInput || !m_bMouseThread ) #else @@ -779,7 +779,7 @@ void GoldSourceInput::IN_GetMouseDelta( int *pOutX, int *pOutY) #endif IN_ResetMouse(); -#if _WIN32 +#if XASH_WIN32 // update m_bRawInput occasionally: const float currentTime = gEngfuncs.GetClientTime(); if ( currentTime - s_flRawInputUpdateTime > 1.0f || s_flRawInputUpdateTime == 0.0f ) @@ -929,7 +929,7 @@ void GoldSourceInput::IN_Accumulate (void) { if (mouseactive) { -#if _WIN32 +#if XASH_WIN32 if ( !m_bRawInput ) { if ( !m_bMouseThread ) @@ -957,7 +957,7 @@ void GoldSourceInput::IN_Accumulate (void) } // force the mouse to the center, so there's room to move -#if _WIN32 +#if XASH_WIN32 // do not reset if mousethread would do it: if ( m_bRawInput || !m_bMouseThread ) #else @@ -1030,7 +1030,7 @@ void IN_StartupJoystick (void) { gEngfuncs.Con_DPrintf ("joystick not found -- driver not present\n\n"); } -#elif _WIN32 +#elif XASH_WIN32 int numdevs; JOYCAPS jc; MMRESULT mmr; @@ -1102,7 +1102,7 @@ int RawValuePointer (int axis) } } -#elif _WIN32 +#elif XASH_WIN32 PDWORD RawValuePointer (int axis) { switch (axis) @@ -1185,7 +1185,7 @@ void Joy_AdvancedUpdate_f (void) dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS; } -#if !USE_SDL2 && _WIN32 +#if !USE_SDL2 && XASH_WIN32 // compute the axes to collect from DirectInput joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV; for (i = 0; i < JOY_MAX_AXES; i++) @@ -1231,7 +1231,7 @@ void GoldSourceInput::IN_Commands (void) { pdwRawValue[i] = RawValuePointer(i); } -#elif _WIN32 +#elif XASH_WIN32 buttonstate = ji.dwButtons; #endif @@ -1257,7 +1257,7 @@ void GoldSourceInput::IN_Commands (void) // this avoids any potential problems related to moving from one // direction to another without going through the center position povstate = 0; -#if !USE_SDL2 && _WIN32 +#if !USE_SDL2 && XASH_WIN32 if(ji.dwPOV != JOY_POVCENTERED) { if (ji.dwPOV == JOY_POVFORWARD) @@ -1298,7 +1298,7 @@ int IN_ReadJoystick (void) #if USE_SDL2 safe_pfnSDL_JoystickUpdate(); return 1; -#elif _WIN32 +#elif XASH_WIN32 memset (&ji, 0, sizeof(ji)); ji.dwSize = sizeof(ji); ji.dwFlags = joy_flags; @@ -1377,7 +1377,7 @@ void IN_JoyMove ( float frametime, usercmd_t *cmd ) // get the floating point zero-centered, potentially-inverted data for the current axis #if USE_SDL2 fAxisValue = (float)pdwRawValue[i]; -#elif _WIN32 +#elif XASH_WIN32 fAxisValue = (float) *pdwRawValue[i]; fAxisValue -= 32768.0; #endif @@ -1571,7 +1571,7 @@ void GoldSourceInput::IN_Init (void) m_customaccel_max = gEngfuncs.pfnRegisterVariable ( "m_customaccel_max", "0", FCVAR_ARCHIVE ); m_customaccel_exponent = gEngfuncs.pfnRegisterVariable ( "m_customaccel_exponent", "1", FCVAR_ARCHIVE ); -#if _WIN32 +#if XASH_WIN32 m_rawinput = gEngfuncs.pfnGetCvarPointer("m_rawinput"); m_bRawInput = m_rawinput && m_rawinput->value != 0; m_bMouseThread = gEngfuncs.CheckParm ("-mousethread", NULL ) != NULL; @@ -1603,7 +1603,7 @@ void GoldSourceInput::IN_Init (void) #endif #if USE_SDL2 -#if __APPLE__ +#if XASH_APPLE #define SDL2_FULL_LIBNAME "libsdl2-2.0.0.dylib" #else #define SDL2_FULL_LIBNAME "libSDL2-2.0.so.0" diff --git a/cl_dll/input_mouse.h b/cl_dll/input_mouse.h index 814e239f..4948ccb0 100644 --- a/cl_dll/input_mouse.h +++ b/cl_dll/input_mouse.h @@ -51,7 +51,7 @@ protected: #if GOLDSOURCE_SUPPORT && ( XASH_WIN32 || ( XASH_LINUX && !XASH_ANDROID ) || XASH_APPLE ) && XASH_X86 #define SUPPORT_GOLDSOURCE_INPUT 1 -#if _WIN32 +#if XASH_WIN32 #define HSPRITE WINDOWS_HSPRITE #include #undef HSPRITE diff --git a/cl_dll/vgui_TeamFortressViewport.cpp b/cl_dll/vgui_TeamFortressViewport.cpp index debac7ef..b19ae2a1 100644 --- a/cl_dll/vgui_TeamFortressViewport.cpp +++ b/cl_dll/vgui_TeamFortressViewport.cpp @@ -673,7 +673,7 @@ int TeamFortressViewport::CreateCommandMenu( const char *menuFile, int direction return newIndex; } -#ifdef _WIN32 +#if XASH_WIN32 try { #endif @@ -873,7 +873,7 @@ int TeamFortressViewport::CreateCommandMenu( const char *menuFile, int direction pfile = gEngfuncs.COM_ParseFile( pfile, token ); } -#ifdef _WIN32 +#if XASH_WIN32 } catch( CException *e ) { diff --git a/dlls/cbase.cpp b/dlls/cbase.cpp index cfc78697..b15f97c7 100644 --- a/dlls/cbase.cpp +++ b/dlls/cbase.cpp @@ -98,7 +98,7 @@ static DLL_FUNCTIONS gFunctionTable = static void SetObjectCollisionBox( entvars_t *pev ); -#if !_WIN32 +#if !XASH_WIN32 extern "C" { #endif int GetEntityAPI( DLL_FUNCTIONS *pFunctionTable, int interfaceVersion ) @@ -125,7 +125,7 @@ int GetEntityAPI2( DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion ) return TRUE; } -#if !_WIN32 +#if !XASH_WIN32 } #endif diff --git a/dlls/exportdef.h b/dlls/exportdef.h index 6de40543..f90eb06f 100644 --- a/dlls/exportdef.h +++ b/dlls/exportdef.h @@ -1,7 +1,7 @@ #pragma once #if !defined(EXPORTDEF_H) #define EXPORTDEF_H -#if _WIN32 || __CYGWIN__ +#if XASH_WIN32 || __CYGWIN__ #if __GNUC__ #define EXPORT __attribute__ ((dllexport)) #else diff --git a/dlls/extdll.h b/dlls/extdll.h index 2b592763..5f66daaa 100644 --- a/dlls/extdll.h +++ b/dlls/extdll.h @@ -35,7 +35,7 @@ #endif // Prevent tons of unused windows definitions -#if _WIN32 +#if XASH_WIN32 #define WIN32_LEAN_AND_MEAN #define NOWINRES #define NOSERVICE @@ -76,10 +76,6 @@ typedef int BOOL; #define M_PI_F (float)M_PI #endif -#if __LP64__ || __LLP64__ || _WIN64 || (__x86_64__ && !__ILP32__) || _M_X64 || __ia64 || _M_IA64 || __aarch64__ || __powerpc64__ - #define XASH_64BIT 1 -#endif - // Header file containing definition of globalvars_t and entvars_t typedef unsigned int func_t; typedef int string_t; // from engine's pr_comp.h; diff --git a/dlls/h_export.cpp b/dlls/h_export.cpp index 11884f9c..d1610275 100644 --- a/dlls/h_export.cpp +++ b/dlls/h_export.cpp @@ -29,7 +29,7 @@ enginefuncs_t g_engfuncs; globalvars_t *gpGlobals; -#if _WIN32 +#if XASH_WIN32 // Required DLL entry point BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) diff --git a/dlls/util.cpp b/dlls/util.cpp index a7ab3ca4..cb651940 100644 --- a/dlls/util.cpp +++ b/dlls/util.cpp @@ -1729,7 +1729,7 @@ void CSaveRestoreBuffer::BufferRewind( int size ) m_pdata->size -= size; } -#if !_WIN32 && !__WATCOMC__ +#if !XASH_WIN32 && !__WATCOMC__ extern "C" { unsigned _rotr( unsigned val, int shift ) { diff --git a/utils/fake_vgui/include/VGUI.h b/utils/fake_vgui/include/VGUI.h index c33e0589..d7cbba94 100644 --- a/utils/fake_vgui/include/VGUI.h +++ b/utils/fake_vgui/include/VGUI.h @@ -77,7 +77,7 @@ // then App can come along later and fire all the signals //TODO: Change all method naming to starting with a capital letter. -#ifdef _WIN32 +#if XASH_WIN32 # define VGUIAPI __declspec( dllexport ) #else # define VGUIAPI __attribute__ ((visibility("default"))) diff --git a/utils/fake_vgui/include/VGUI_Dar.h b/utils/fake_vgui/include/VGUI_Dar.h index 6f8eb513..32ec592a 100644 --- a/utils/fake_vgui/include/VGUI_Dar.h +++ b/utils/fake_vgui/include/VGUI_Dar.h @@ -166,7 +166,7 @@ protected: ELEMTYPE* _data; }; -#ifdef _WIN32 +#if XASH_WIN32 //forward referencing all the template types used so they get exported template class VGUIAPI Dar; template class VGUIAPI Dar; @@ -191,4 +191,4 @@ template class VGUIAPI Dar; } -#endif \ No newline at end of file +#endif From 7581cacd6f7531e0cee6908ad441c36e569969b2 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Wed, 1 Nov 2023 01:31:10 +0500 Subject: [PATCH 047/127] Use fast and simple ROTR algorithm implementation. --- dlls/util.cpp | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/dlls/util.cpp b/dlls/util.cpp index cb651940..6cfebc10 100644 --- a/dlls/util.cpp +++ b/dlls/util.cpp @@ -1730,25 +1730,10 @@ void CSaveRestoreBuffer::BufferRewind( int size ) } #if !XASH_WIN32 && !__WATCOMC__ -extern "C" { -unsigned _rotr( unsigned val, int shift ) +static unsigned _rotr( unsigned val, int shift ) { - unsigned lobit; /* non-zero means lo bit set */ - unsigned num = val; /* number to rotate */ - - shift &= 0x1f; /* modulo 32 -- this will also make - negative shifts work */ - - while( shift-- ) - { - lobit = num & 1; /* get high bit */ - num >>= 1; /* shift right one bit */ - if( lobit ) - num |= 0x80000000; /* set hi bit if lo bit was set */ - } - - return num; -} + // Any modern compiler will generate one single ror instruction for x86, arm and mips here. + return ( val >> shift ) | ( val << ( 32 - shift )); } #endif From 2218731a86054a63c6ce611328ae9b7c714dec0d Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Wed, 1 Nov 2023 01:49:55 +0500 Subject: [PATCH 048/127] Include build.h. --- cl_dll/cl_dll.h | 1 + cl_dll/input_mouse.h | 3 --- dlls/extdll.h | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/cl_dll/cl_dll.h b/cl_dll/cl_dll.h index e29f1e24..83d3d7ab 100644 --- a/cl_dll/cl_dll.h +++ b/cl_dll/cl_dll.h @@ -28,6 +28,7 @@ #pragma once #if !defined(CL_DLL_H) #define CL_DLL_H +#include "build.h" typedef unsigned char byte; typedef unsigned short word; typedef float vec_t; diff --git a/cl_dll/input_mouse.h b/cl_dll/input_mouse.h index 4948ccb0..1fa76eb6 100644 --- a/cl_dll/input_mouse.h +++ b/cl_dll/input_mouse.h @@ -1,7 +1,4 @@ #pragma once - -#include "build.h" - #if !defined(INPUT_MOUSE_H) #define INPUT_MOUSE_H #include "cl_dll.h" diff --git a/dlls/extdll.h b/dlls/extdll.h index 5f66daaa..9541ee16 100644 --- a/dlls/extdll.h +++ b/dlls/extdll.h @@ -15,7 +15,7 @@ #pragma once #if !defined(EXTDLL_H) #define EXTDLL_H - +#include "build.h" // // Global header file for extension DLLs // From dd6995ecbd80e20391ad13ea143340f22156bb97 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Wed, 1 Nov 2023 02:27:00 +0500 Subject: [PATCH 049/127] client: MOTD: Prevent out of bounds access on x86. --- cl_dll/MOTD.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cl_dll/MOTD.cpp b/cl_dll/MOTD.cpp index 1ca1c0f8..78844642 100644 --- a/cl_dll/MOTD.cpp +++ b/cl_dll/MOTD.cpp @@ -73,7 +73,7 @@ int CHudMOTD::Draw( float fTime ) //bool bScroll; // find the top of where the MOTD should be drawn, so the whole thing is centered in the screen int ypos = ( ScreenHeight - LINE_HEIGHT * m_iLines ) / 2; // shift it up slightly - char *ch = m_szMOTD; + unsigned char *ch = (unsigned char*)m_szMOTD; int xpos = ( ScreenWidth - gHUD.m_scrinfo.charWidths['M'] * m_iMaxLength ) / 2; if( xpos < 30 ) xpos = 30; @@ -95,11 +95,13 @@ int CHudMOTD::Draw( float fTime ) gHUD.DrawDarkRectangle( xpos - 5, ypos_r - 5, xmax - xpos + 10, height + 10 ); while( *ch ) { - char *next_line; - int line_length = 0; // count the length of the current line + unsigned char *next_line; for( next_line = ch; *next_line != '\n' && *next_line != 0; next_line++ ) - line_length += gHUD.m_scrinfo.charWidths[*next_line]; - char *top = next_line; + ; + // int line_length = 0; // count the length of the current line + // for( next_line = ch; *next_line != '\n' && *next_line != 0; next_line++ ) + // line_length += gHUD.m_scrinfo.charWidths[*next_line]; + unsigned char *top = next_line; if( *top == '\n' ) *top = 0; else From 7ca2750c3d7f047eccc343970b7873297803eab6 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Wed, 1 Nov 2023 02:40:22 +0500 Subject: [PATCH 050/127] client: Fix compilation. --- cl_dll/MOTD.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl_dll/MOTD.cpp b/cl_dll/MOTD.cpp index 78844642..9267ea40 100644 --- a/cl_dll/MOTD.cpp +++ b/cl_dll/MOTD.cpp @@ -109,7 +109,7 @@ int CHudMOTD::Draw( float fTime ) // find where to start drawing the line if( ( ypos > ROW_RANGE_MIN ) && ( ypos + LINE_HEIGHT <= ypos_r + height ) ) - DrawUtfString( xpos, ypos, xmax, ch, 255, 180, 0 ); + DrawUtfString( xpos, ypos, xmax, (const char*)ch, 255, 180, 0 ); ypos += LINE_HEIGHT; From 497dd83931dd3a0700833d918fc511c3e67bafd1 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 18 Nov 2023 01:22:09 +0500 Subject: [PATCH 051/127] server: increase sentences limit. --- dlls/util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/util.h b/dlls/util.h index 6e3b2cca..ad3d8002 100644 --- a/dlls/util.h +++ b/dlls/util.h @@ -498,7 +498,7 @@ extern DLL_GLOBAL int g_Language; // sentence groups #define CBSENTENCENAME_MAX 16 -#define CVOXFILESENTENCEMAX 1536 // max number of sentences in game. NOTE: this must match +#define CVOXFILESENTENCEMAX 2048 // max number of sentences in game. NOTE: this must match // CVOXFILESENTENCEMAX in engine\sound.h!!! extern char gszallsentencenames[CVOXFILESENTENCEMAX][CBSENTENCENAME_MAX]; From 3523b52f3e10172bed28f844259b5e9071491888 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 18 Nov 2023 02:40:11 +0500 Subject: [PATCH 052/127] server: reduce hornetgun recharge time. --- dlls/hornetgun.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/hornetgun.cpp b/dlls/hornetgun.cpp index 4223ddd6..f43adfd2 100644 --- a/dlls/hornetgun.cpp +++ b/dlls/hornetgun.cpp @@ -252,7 +252,7 @@ void CHgun::Reload( void ) while( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] < HORNET_MAX_CARRY && m_flRechargeTime < gpGlobals->time ) { m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]++; - m_flRechargeTime += 0.5f; + m_flRechargeTime += 0.3f; } } From e1069725992e335cca0e099aaa137479a61395ec Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 18 Nov 2023 02:44:44 +0500 Subject: [PATCH 053/127] server: spawn mp5 with full ammo. --- dlls/weapons.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/weapons.h b/dlls/weapons.h index 46e88edb..9346e984 100644 --- a/dlls/weapons.h +++ b/dlls/weapons.h @@ -136,7 +136,7 @@ public: // the default amount of ammo that comes with each gun when it spawns #define GLOCK_DEFAULT_GIVE 17 #define PYTHON_DEFAULT_GIVE 6 -#define MP5_DEFAULT_GIVE 25 +#define MP5_DEFAULT_GIVE 50 #define MP5_M203_DEFAULT_GIVE 0 #define SHOTGUN_DEFAULT_GIVE 12 #define CROSSBOW_DEFAULT_GIVE 5 From a7bc9d6266f9dc46b872850b9bb2345eb86f37be Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 18 Nov 2023 11:56:53 +0500 Subject: [PATCH 054/127] server: increase python damage into multiplayer. --- dlls/multiplay_gamerules.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/multiplay_gamerules.cpp b/dlls/multiplay_gamerules.cpp index e8e6a3b5..b9f35d41 100644 --- a/dlls/multiplay_gamerules.cpp +++ b/dlls/multiplay_gamerules.cpp @@ -149,7 +149,7 @@ void CHalfLifeMultiplay::RefreshSkillData( void ) gSkillData.plrDmg9MM = 12; // 357 Round - gSkillData.plrDmg357 = 40; + gSkillData.plrDmg357 = 50; // MP5 Round gSkillData.plrDmgMP5 = 12; From acc38487957de49a53d356bc6f8ab5929ab71780 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 18 Nov 2023 14:16:35 +0500 Subject: [PATCH 055/127] client: use normal player tracer to avoid "ghost shots". --- cl_dll/ev_hldm.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cl_dll/ev_hldm.cpp b/cl_dll/ev_hldm.cpp index 166f86bd..9f5ede08 100644 --- a/cl_dll/ev_hldm.cpp +++ b/cl_dll/ev_hldm.cpp @@ -420,7 +420,7 @@ void EV_HLDM_FireBullets( int idx, float *forward, float *right, float *up, int gEngfuncs.pEventAPI->EV_SetSolidPlayers( idx - 1 ); gEngfuncs.pEventAPI->EV_SetTraceHull( 2 ); - gEngfuncs.pEventAPI->EV_PlayerTrace( vecSrc, vecEnd, PM_STUDIO_BOX, -1, &tr ); + gEngfuncs.pEventAPI->EV_PlayerTrace( vecSrc, vecEnd, PM_NORMAL, -1, &tr ); tracer = EV_HLDM_CheckTracer( idx, vecSrc, tr.endpos, forward, right, iBulletType, iTracerFreq, tracerCount ); @@ -883,7 +883,7 @@ void EV_FireGauss( event_args_t *args ) gEngfuncs.pEventAPI->EV_SetSolidPlayers( idx - 1 ); gEngfuncs.pEventAPI->EV_SetTraceHull( 2 ); - gEngfuncs.pEventAPI->EV_PlayerTrace( vecSrc, vecDest, PM_STUDIO_BOX, -1, &tr ); + gEngfuncs.pEventAPI->EV_PlayerTrace( vecSrc, vecDest, PM_NORMAL, -1, &tr ); gEngfuncs.pEventAPI->EV_PopPMStates(); @@ -1004,14 +1004,14 @@ void EV_FireGauss( event_args_t *args ) gEngfuncs.pEventAPI->EV_SetSolidPlayers ( idx - 1 ); gEngfuncs.pEventAPI->EV_SetTraceHull( 2 ); - gEngfuncs.pEventAPI->EV_PlayerTrace( start, vecDest, PM_STUDIO_BOX, -1, &beam_tr ); + gEngfuncs.pEventAPI->EV_PlayerTrace( start, vecDest, PM_NORMAL, -1, &beam_tr ); if( !beam_tr.allsolid ) { vec3_t delta; // trace backwards to find exit point - gEngfuncs.pEventAPI->EV_PlayerTrace( beam_tr.endpos, tr.endpos, PM_STUDIO_BOX, -1, &beam_tr ); + gEngfuncs.pEventAPI->EV_PlayerTrace( beam_tr.endpos, tr.endpos, PM_NORMAL, -1, &beam_tr ); VectorSubtract( beam_tr.endpos, tr.endpos, delta ); @@ -1214,7 +1214,7 @@ void EV_FireCrossbow2( event_args_t *args ) // Now add in all of the players. gEngfuncs.pEventAPI->EV_SetSolidPlayers ( idx - 1 ); gEngfuncs.pEventAPI->EV_SetTraceHull( 2 ); - gEngfuncs.pEventAPI->EV_PlayerTrace( vecSrc, vecEnd, PM_STUDIO_BOX, -1, &tr ); + gEngfuncs.pEventAPI->EV_PlayerTrace( vecSrc, vecEnd, PM_NORMAL, -1, &tr ); //We hit something if( tr.fraction < 1.0f ) @@ -1450,7 +1450,7 @@ void EV_EgonFire( event_args_t *args ) gEngfuncs.pEventAPI->EV_SetSolidPlayers( idx - 1 ); gEngfuncs.pEventAPI->EV_SetTraceHull( 2 ); - gEngfuncs.pEventAPI->EV_PlayerTrace( vecSrc, vecEnd, PM_STUDIO_BOX, -1, &tr ); + gEngfuncs.pEventAPI->EV_PlayerTrace( vecSrc, vecEnd, PM_NORMAL, -1, &tr ); gEngfuncs.pEventAPI->EV_PopPMStates(); From fdf49c8974254db582951c38932b96f96d526dc2 Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Sat, 18 Nov 2023 13:24:37 +0300 Subject: [PATCH 056/127] Add mention of Windows SDK in build Prerequisites [ci skip] (#410) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e1fbcccf..706fa58a 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ git clone --recursive https://github.com/FWGS/hlsdk-portable ### Prerequisites -Install and run [Visual Studio Installer](https://visualstudio.microsoft.com/downloads/). The installer allows you to choose specific components. Select `Desktop development with C++`. You can untick everything you don't need in Installation details, but you must keep `MSVC` ticked. You may also keep `C++ CMake tools for Windows` ticked as you'll need **cmake**. Alternatively you can install **cmake** from the [cmake.org](https://cmake.org/download/) and during installation tick *Add to the PATH...*. +Install and run [Visual Studio Installer](https://visualstudio.microsoft.com/downloads/). The installer allows you to choose specific components. Select `Desktop development with C++`. You can untick everything you don't need in Installation details, but you must keep `MSVC` and corresponding Windows SDK (e.g. Windows 10 SDK or Windows 11 SDK) ticked. You may also keep `C++ CMake tools for Windows` ticked as you'll need **cmake**. Alternatively you can install **cmake** from the [cmake.org](https://cmake.org/download/) and during installation tick *Add to the PATH...*. ### Opening command prompt From 1e4663ab7344d8c391f67e20044367d908e58292 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 18 Nov 2023 22:41:25 +0500 Subject: [PATCH 057/127] server: spawn mp5 with full ammo only in multiplayer. --- dlls/mp5.cpp | 7 +++++++ dlls/weapons.h | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/dlls/mp5.cpp b/dlls/mp5.cpp index 2b2d07ab..53323a74 100644 --- a/dlls/mp5.cpp +++ b/dlls/mp5.cpp @@ -54,6 +54,13 @@ void CMP5::Spawn() m_iDefaultAmmo = MP5_DEFAULT_GIVE; +#if CLIENT_DLL + if( bIsMultiplayer() ) +#else + if( g_pGameRules->IsMultiplayer() ) +#endif + m_iDefaultAmmo = MP5_DEFAULT_GIVE_MP; + FallInit();// get ready to fall down. } diff --git a/dlls/weapons.h b/dlls/weapons.h index 9346e984..e4966125 100644 --- a/dlls/weapons.h +++ b/dlls/weapons.h @@ -136,7 +136,8 @@ public: // the default amount of ammo that comes with each gun when it spawns #define GLOCK_DEFAULT_GIVE 17 #define PYTHON_DEFAULT_GIVE 6 -#define MP5_DEFAULT_GIVE 50 +#define MP5_DEFAULT_GIVE 25 +#define MP5_DEFAULT_GIVE_MP MP5_MAX_CLIP #define MP5_M203_DEFAULT_GIVE 0 #define SHOTGUN_DEFAULT_GIVE 12 #define CROSSBOW_DEFAULT_GIVE 5 From 78c00360468735f677b308ba68ac9cd6bc5a002b Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 18 Nov 2023 23:00:06 +0500 Subject: [PATCH 058/127] server: reduce hornetgun recharge time only in multiplayer. --- dlls/hornetgun.cpp | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/dlls/hornetgun.cpp b/dlls/hornetgun.cpp index f43adfd2..1ae35df2 100644 --- a/dlls/hornetgun.cpp +++ b/dlls/hornetgun.cpp @@ -137,7 +137,12 @@ void CHgun::PrimaryAttack() CBaseEntity *pHornet = CBaseEntity::Create( "hornet", m_pPlayer->GetGunPosition() + gpGlobals->v_forward * 16.0f + gpGlobals->v_right * 8.0f + gpGlobals->v_up * -12.0f, m_pPlayer->pev->v_angle, m_pPlayer->edict() ); pHornet->pev->velocity = gpGlobals->v_forward * 300.0f; - m_flRechargeTime = gpGlobals->time + 0.5f; + float flRechargeTimePause = 0.5f; + + if( g_pGameRules->IsMultiplayer() ) + flRechargeTimePause = 0.3f; + + m_flRechargeTime = gpGlobals->time + flRechargeTimePause; #endif m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--; @@ -223,8 +228,14 @@ void CHgun::SecondaryAttack( void ) pHornet->SetThink( &CHornet::StartDart ); - m_flRechargeTime = gpGlobals->time + 0.5f; + float flRechargeTimePause = 0.5f; + + if( g_pGameRules->IsMultiplayer() ) + flRechargeTimePause = 0.3f; + + m_flRechargeTime = gpGlobals->time + flRechargeTimePause; #endif + int flags; #if CLIENT_WEAPONS flags = FEV_NOTHOST; @@ -251,8 +262,16 @@ void CHgun::Reload( void ) while( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] < HORNET_MAX_CARRY && m_flRechargeTime < gpGlobals->time ) { + float flRechargeTimePause = 0.5f; +#if CLIENT_DLL + if( bIsMultiplayer() ) +#else + if( g_pGameRules->IsMultiplayer() ) +#endif + flRechargeTimePause = 0.3f; + m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]++; - m_flRechargeTime += 0.3f; + m_flRechargeTime += flRechargeTimePause; } } From 4be2334ac92a8377def0e54c2b1c804295146a4f Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Thu, 7 Dec 2023 05:32:10 +0500 Subject: [PATCH 059/127] Update README.md. --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 706fa58a..68c838cd 100644 --- a/README.md +++ b/README.md @@ -186,7 +186,14 @@ cmake --build build Note that the libraries built this way might be not compatible with Steam Half-Life. If you have such issue you can configure it to build statically with c++ and gcc libraries: ``` -cmake .. -DCMAKE_C_FLAGS="-static-libstdc++ -static-libgcc" +cd build +cmake .. -DCMAKE_CXX_FLAGS="-static-libstdc++ -static-libgcc" +``` + +Alternatively, you can avoid libstdc++/libgcc_s linking using small libsupc++ library and optimization build flags instead: +``` +cd build +cmake .. -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=gcc -DCMAKE_C_FLAGS="-O3" -DCMAKE_CXX_FLAGS="-O3 -lsupc++" ``` To ensure portability it's still better to build using Steam Runtime or another chroot of some older distro. From 050092763e1691b4a222f510c9641100b3f24ab1 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:58:52 +0500 Subject: [PATCH 060/127] cmake: update minimum required version. --- cl_dll/CMakeLists.txt | 2 +- dlls/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cl_dll/CMakeLists.txt b/cl_dll/CMakeLists.txt index 45c24b91..b88821a2 100644 --- a/cl_dll/CMakeLists.txt +++ b/cl_dll/CMakeLists.txt @@ -20,7 +20,7 @@ # SOFTWARE. # -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.9) project (CLDLL) set (CLDLL_LIBRARY client) diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index 7e5378e1..638f1a73 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -20,7 +20,7 @@ # SOFTWARE. # -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.9) project (SVDLL) set (SVDLL_LIBRARY server) From 881f013742bcdfbf6522829b210e976006ce6c01 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Fri, 8 Dec 2023 13:34:28 +0500 Subject: [PATCH 061/127] cmake: fix wrong condition. --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e2ea26c0..bb335861 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,9 +53,13 @@ if (CMAKE_SIZEOF_VOID_P EQUAL 4 OR OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64"))) option(64BIT "Disable auto -m32 appending to compiler flags" OFF) - option(GOLDSOURCE_SUPPORT "Build goldsource compatible client library" ON) else() option(64BIT "Disable auto -m32 appending to compiler flags" ON) +endif() + +if ((WIN32 OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR APPLE) AND NOT 64BIT) + option(GOLDSOURCE_SUPPORT "Build goldsource compatible client library" ON) +else() option(GOLDSOURCE_SUPPORT "Build goldsource compatible client library" OFF) endif() From 61a16bc8aab92c2ebb6fe85ba1a6b9a8777aca45 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Fri, 8 Dec 2023 13:46:32 +0500 Subject: [PATCH 062/127] Update README.md. --- README.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 68c838cd..8f1f7032 100644 --- a/README.md +++ b/README.md @@ -318,20 +318,33 @@ cmake -B build -S . cmake --build build ``` +Force 64-bit build: +``` +cmake -D64BIT=1 -B build -S . +cmake --build build +``` + ### Building with waf To use waf, you need to install python (2.7 minimum) ``` -(./waf configure -T release) -(./waf) +./waf configure -T release +./waf +``` + +Force 64-bit build: +``` +./waf configure -T release -8 +./waf ``` ## Build options Some useful build options that can be set during the cmake step. -* **GOLDSOURCE_SUPPORT** - allows to turn off/on the support for GoldSource input. Set to **ON** by default on Windows and Linux, **OFF** on other platforms. +* **GOLDSOURCE_SUPPORT** - allows to turn off/on the support for GoldSource input. Set to **ON** by default on Windows, Linux and MacOS, **OFF** on other platforms. +* **64BIT** - allows to turn off/on 64-bit build. Set to **OFF** by default on x86_64 Windows, x86_64 Linux and 32-bit platforms, **ON** on other 64-bit platforms. * **USE_VGUI** - whether to use VGUI library. **OFF** by default. You need to init `vgui_support` submodule in order to build with VGUI. This list is incomplete. Look at `CMakeLists.txt` to see all available options. From b89ce4c52703fc5b9e13683ecd1919b98694a9a2 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 11 Dec 2023 17:42:26 +0500 Subject: [PATCH 063/127] cmake: force to use library naming scheme. --- CMakeLists.txt | 18 +++++++----------- cl_dll/CMakeLists.txt | 8 ++++---- dlls/CMakeLists.txt | 2 +- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bb335861..0d89a04a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,10 @@ include(VSForceXPToolchain) # Force XP toolchain for Visual Studio project (HLSDK-PORTABLE) +# Xash3D FWGS Library Naming Scheme compliance +# see documentation: https://github.com/FWGS/xash3d-fwgs/blob/master/Documentation/extensions/library-naming.md +include(LibraryNaming) + #-------------- # USER DEFINES \ ################\ @@ -47,17 +51,13 @@ option(USE_VOICEMGR "Enable VOICE MANAGER." OFF) option(BUILD_CLIENT "Build client dll" ON) option(BUILD_SERVER "Build server dll" ON) -if (CMAKE_SIZEOF_VOID_P EQUAL 4 OR - ((WIN32 OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - AND (CMAKE_SYSTEM_PROCESSOR STREQUAL "x64" - OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" - OR CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64"))) +if (CMAKE_SIZEOF_VOID_P EQUAL 4 OR ((XASH_WIN32 OR XASH_LINUX) AND XASH_AMD64)) option(64BIT "Disable auto -m32 appending to compiler flags" OFF) else() option(64BIT "Disable auto -m32 appending to compiler flags" ON) endif() -if ((WIN32 OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR APPLE) AND NOT 64BIT) +if ((XASH_WIN32 OR XASH_LINUX OR XASH_APPLE) AND ((XASH_AMD64 AND NOT 64BIT) OR XASH_X86)) option(GOLDSOURCE_SUPPORT "Build goldsource compatible client library" ON) else() option(GOLDSOURCE_SUPPORT "Build goldsource compatible client library" OFF) @@ -109,10 +109,6 @@ if(64BIT AND CMAKE_SIZEOF_VOID_P EQUAL 4) message(FATAL_ERROR "You enabled XASH_64BIT, but compiler can't create 64 bit code!") endif() -# Xash3D FWGS Library Naming Scheme compliance -# see documentation: https://github.com/FWGS/xash3d-fwgs/blob/master/Documentation/extensions/library-naming.md -include(LibraryNaming) - if(CMAKE_SIZEOF_VOID_P EQUAL 8) message(STATUS "Building for 64 Bit") else() @@ -131,7 +127,7 @@ else() add_definitions(-D_CRT_SILENCE_NONCONFORMING_TGMATH_H) endif() -if(VITA) +if(XASH_PSVITA) add_compile_options(-fno-use-cxa-atexit) endif() diff --git a/cl_dll/CMakeLists.txt b/cl_dll/CMakeLists.txt index b88821a2..98e8e37b 100644 --- a/cl_dll/CMakeLists.txt +++ b/cl_dll/CMakeLists.txt @@ -175,19 +175,19 @@ if (GOLDSOURCE_SUPPORT) endif() if (USE_VGUI) - if (WIN32) + if (XASH_WIN32) add_library(vgui SHARED IMPORTED) set_property(TARGET vgui PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/vgui_support/vgui-dev/lib/win32_vc6/vgui.dll") set_property(TARGET vgui PROPERTY IMPORTED_IMPLIB "${CMAKE_SOURCE_DIR}/vgui_support/vgui-dev/lib/win32_vc6/vgui.lib") target_link_libraries(${CLDLL_LIBRARY} vgui) - elseif(APPLE) + elseif(XASH_APPLE) target_link_libraries(${CLDLL_LIBRARY} "-Wl,--no-undefined -L${CMAKE_SOURCE_DIR}/vgui_support/vgui-dev/lib vgui.dylib") else() target_link_libraries(${CLDLL_LIBRARY} :vgui.so) endif() endif() -if(WIN32) +if(XASH_WIN32) target_link_libraries( ${CLDLL_LIBRARY} user32.lib ) if (GOLDSOURCE_SUPPORT) target_link_libraries( ${CLDLL_LIBRARY} winmm.lib ) @@ -197,7 +197,7 @@ endif() set_target_properties (${CLDLL_LIBRARY} PROPERTIES POSITION_INDEPENDENT_CODE 1) -if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Android") +if(NOT XASH_ANDROID) set(CLDLL_NAME "client") set_target_properties(${CLDLL_LIBRARY} PROPERTIES OUTPUT_NAME ${CLDLL_NAME} diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index 638f1a73..4dc95ffc 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -161,7 +161,7 @@ add_library (${SVDLL_LIBRARY} SHARED ${SVDLL_SOURCES}) set_target_properties (${SVDLL_LIBRARY} PROPERTIES POSITION_INDEPENDENT_CODE 1) -if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Android") +if(NOT XASH_ANDROID) set(SVDLL_NAME "${SERVER_LIBRARY_NAME}") set_target_properties(${SVDLL_LIBRARY} PROPERTIES From 9273b58cfd9150fa9961ba82b7abd1106b598c6f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 21 Dec 2023 05:21:49 +0300 Subject: [PATCH 064/127] server: match sentences limit with Xash3D FWGS engine --- dlls/util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/util.h b/dlls/util.h index ad3d8002..bfe0fd32 100644 --- a/dlls/util.h +++ b/dlls/util.h @@ -498,7 +498,7 @@ extern DLL_GLOBAL int g_Language; // sentence groups #define CBSENTENCENAME_MAX 16 -#define CVOXFILESENTENCEMAX 2048 // max number of sentences in game. NOTE: this must match +#define CVOXFILESENTENCEMAX 4096 // max number of sentences in game. NOTE: this must match // CVOXFILESENTENCEMAX in engine\sound.h!!! extern char gszallsentencenames[CVOXFILESENTENCEMAX][CBSENTENCENAME_MAX]; From ecdcabfe99dd76aef98daba47aee6c6a789ac0ac Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 21 Dec 2023 05:22:12 +0300 Subject: [PATCH 065/127] server: warn mod developer about reaching the GoldSrc sentences limits --- dlls/sound.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dlls/sound.cpp b/dlls/sound.cpp index 3dbd097a..992e0ebd 100644 --- a/dlls/sound.cpp +++ b/dlls/sound.cpp @@ -1341,6 +1341,15 @@ void SENTENCEG_Init() g_engfuncs.pfnFreeFile( pMemFile ); + if( gcallsentences >= 2048 ) + { + ALERT( at_warning, "NOTE: this mod might not work properly under GoldSource (post-anniversary update) engine: more than 2048 sentences\n" ); + } + else if( gcallsentences >= 1536 ) + { + ALERT( at_warning, "NOTE: this mod might not work properly under GoldSource (pre-anniversary update) engine: more than 1536 sentences\n" ); + } + fSentencesInit = TRUE; // init lru lists From 2f13985e23b36dae32414be48beb7d73246430e4 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Thu, 21 Dec 2023 21:47:02 +0500 Subject: [PATCH 066/127] Revert "cmake: force to use library naming scheme." This reverts commit b89ce4c52703fc5b9e13683ecd1919b98694a9a2. --- CMakeLists.txt | 18 +++++++++++------- cl_dll/CMakeLists.txt | 8 ++++---- dlls/CMakeLists.txt | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d89a04a..bb335861 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,10 +36,6 @@ include(VSForceXPToolchain) # Force XP toolchain for Visual Studio project (HLSDK-PORTABLE) -# Xash3D FWGS Library Naming Scheme compliance -# see documentation: https://github.com/FWGS/xash3d-fwgs/blob/master/Documentation/extensions/library-naming.md -include(LibraryNaming) - #-------------- # USER DEFINES \ ################\ @@ -51,13 +47,17 @@ option(USE_VOICEMGR "Enable VOICE MANAGER." OFF) option(BUILD_CLIENT "Build client dll" ON) option(BUILD_SERVER "Build server dll" ON) -if (CMAKE_SIZEOF_VOID_P EQUAL 4 OR ((XASH_WIN32 OR XASH_LINUX) AND XASH_AMD64)) +if (CMAKE_SIZEOF_VOID_P EQUAL 4 OR + ((WIN32 OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + AND (CMAKE_SYSTEM_PROCESSOR STREQUAL "x64" + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64"))) option(64BIT "Disable auto -m32 appending to compiler flags" OFF) else() option(64BIT "Disable auto -m32 appending to compiler flags" ON) endif() -if ((XASH_WIN32 OR XASH_LINUX OR XASH_APPLE) AND ((XASH_AMD64 AND NOT 64BIT) OR XASH_X86)) +if ((WIN32 OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR APPLE) AND NOT 64BIT) option(GOLDSOURCE_SUPPORT "Build goldsource compatible client library" ON) else() option(GOLDSOURCE_SUPPORT "Build goldsource compatible client library" OFF) @@ -109,6 +109,10 @@ if(64BIT AND CMAKE_SIZEOF_VOID_P EQUAL 4) message(FATAL_ERROR "You enabled XASH_64BIT, but compiler can't create 64 bit code!") endif() +# Xash3D FWGS Library Naming Scheme compliance +# see documentation: https://github.com/FWGS/xash3d-fwgs/blob/master/Documentation/extensions/library-naming.md +include(LibraryNaming) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) message(STATUS "Building for 64 Bit") else() @@ -127,7 +131,7 @@ else() add_definitions(-D_CRT_SILENCE_NONCONFORMING_TGMATH_H) endif() -if(XASH_PSVITA) +if(VITA) add_compile_options(-fno-use-cxa-atexit) endif() diff --git a/cl_dll/CMakeLists.txt b/cl_dll/CMakeLists.txt index 98e8e37b..b88821a2 100644 --- a/cl_dll/CMakeLists.txt +++ b/cl_dll/CMakeLists.txt @@ -175,19 +175,19 @@ if (GOLDSOURCE_SUPPORT) endif() if (USE_VGUI) - if (XASH_WIN32) + if (WIN32) add_library(vgui SHARED IMPORTED) set_property(TARGET vgui PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/vgui_support/vgui-dev/lib/win32_vc6/vgui.dll") set_property(TARGET vgui PROPERTY IMPORTED_IMPLIB "${CMAKE_SOURCE_DIR}/vgui_support/vgui-dev/lib/win32_vc6/vgui.lib") target_link_libraries(${CLDLL_LIBRARY} vgui) - elseif(XASH_APPLE) + elseif(APPLE) target_link_libraries(${CLDLL_LIBRARY} "-Wl,--no-undefined -L${CMAKE_SOURCE_DIR}/vgui_support/vgui-dev/lib vgui.dylib") else() target_link_libraries(${CLDLL_LIBRARY} :vgui.so) endif() endif() -if(XASH_WIN32) +if(WIN32) target_link_libraries( ${CLDLL_LIBRARY} user32.lib ) if (GOLDSOURCE_SUPPORT) target_link_libraries( ${CLDLL_LIBRARY} winmm.lib ) @@ -197,7 +197,7 @@ endif() set_target_properties (${CLDLL_LIBRARY} PROPERTIES POSITION_INDEPENDENT_CODE 1) -if(NOT XASH_ANDROID) +if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Android") set(CLDLL_NAME "client") set_target_properties(${CLDLL_LIBRARY} PROPERTIES OUTPUT_NAME ${CLDLL_NAME} diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index 4dc95ffc..638f1a73 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -161,7 +161,7 @@ add_library (${SVDLL_LIBRARY} SHARED ${SVDLL_SOURCES}) set_target_properties (${SVDLL_LIBRARY} PROPERTIES POSITION_INDEPENDENT_CODE 1) -if(NOT XASH_ANDROID) +if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Android") set(SVDLL_NAME "${SERVER_LIBRARY_NAME}") set_target_properties(${SVDLL_LIBRARY} PROPERTIES From 61e28760af091092dd70267092b1be5115b7115a Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Thu, 21 Dec 2023 22:50:24 +0500 Subject: [PATCH 067/127] cmake: fix wrong condition again. --- CMakeLists.txt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bb335861..4b226167 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,7 @@ option(USE_VOICEMGR "Enable VOICE MANAGER." OFF) option(BUILD_CLIENT "Build client dll" ON) option(BUILD_SERVER "Build server dll" ON) -if (CMAKE_SIZEOF_VOID_P EQUAL 4 OR +if(CMAKE_SIZEOF_VOID_P EQUAL 4 OR ((WIN32 OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND (CMAKE_SYSTEM_PROCESSOR STREQUAL "x64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" @@ -57,7 +57,14 @@ else() option(64BIT "Disable auto -m32 appending to compiler flags" ON) endif() -if ((WIN32 OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR APPLE) AND NOT 64BIT) +# It seems CMAKE_SYSTEM_PROCESSOR parameter completely useless for APPLE platform, +# so may need to set options here manually. +if((WIN32 OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + AND (((CMAKE_SYSTEM_PROCESSOR STREQUAL "x64" + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64") AND NOT 64BIT) + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86" + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "i386")) option(GOLDSOURCE_SUPPORT "Build goldsource compatible client library" ON) else() option(GOLDSOURCE_SUPPORT "Build goldsource compatible client library" OFF) From e18919fa110193b2c937132ec1ea6b39f48bb198 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Thu, 21 Dec 2023 22:55:52 +0500 Subject: [PATCH 068/127] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8f1f7032..d7912d20 100644 --- a/README.md +++ b/README.md @@ -343,7 +343,7 @@ Force 64-bit build: Some useful build options that can be set during the cmake step. -* **GOLDSOURCE_SUPPORT** - allows to turn off/on the support for GoldSource input. Set to **ON** by default on Windows, Linux and MacOS, **OFF** on other platforms. +* **GOLDSOURCE_SUPPORT** - allows to turn off/on the support for GoldSource input. Set to **ON** by default on x86 Windows and x86 Linux, **OFF** on other platforms. * **64BIT** - allows to turn off/on 64-bit build. Set to **OFF** by default on x86_64 Windows, x86_64 Linux and 32-bit platforms, **ON** on other 64-bit platforms. * **USE_VGUI** - whether to use VGUI library. **OFF** by default. You need to init `vgui_support` submodule in order to build with VGUI. From 81d8b90acaf2aab1e2157df62259c98dbcb77abe Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Thu, 21 Dec 2023 23:07:11 +0500 Subject: [PATCH 069/127] cmake: fix windows build. --- CMakeLists.txt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b226167..c44c8cae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,8 +50,12 @@ option(BUILD_SERVER "Build server dll" ON) if(CMAKE_SIZEOF_VOID_P EQUAL 4 OR ((WIN32 OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND (CMAKE_SYSTEM_PROCESSOR STREQUAL "x64" + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "X64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" - OR CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64"))) + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "X86_64" + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64" + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "EM64T"))) option(64BIT "Disable auto -m32 appending to compiler flags" OFF) else() option(64BIT "Disable auto -m32 appending to compiler flags" ON) @@ -61,9 +65,14 @@ endif() # so may need to set options here manually. if((WIN32 OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND (((CMAKE_SYSTEM_PROCESSOR STREQUAL "x64" + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "X64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" - OR CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64") AND NOT 64BIT) + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "X86_64" + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64" + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "EM64T") AND NOT 64BIT) OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86" + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "X86" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "i386")) option(GOLDSOURCE_SUPPORT "Build goldsource compatible client library" ON) else() From afc7b8b619d0ed8e6c8468008d0a41de9d137d3e Mon Sep 17 00:00:00 2001 From: Velaron Date: Thu, 28 Dec 2023 16:25:04 +0200 Subject: [PATCH 070/127] Add basic android project --- android/.gitignore | 10 ++ android/app/build.gradle | 76 +++++++++ android/app/src/main/AndroidManifest.xml | 36 ++++ .../app/src/main/ic_launcher-playstore.png | Bin 0 -> 40387 bytes .../java/com/example/hlsdk/MainActivity.java | 54 ++++++ .../drawable-v24/ic_launcher_foreground.xml | 31 ++++ .../res/drawable/ic_launcher_background.xml | 74 ++++++++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 2638 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 4382 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 2086 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 2984 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 3824 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 6368 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 5096 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 9222 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 7344 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 12220 bytes .../res/values/ic_launcher_background.xml | 4 + android/app/src/main/res/values/strings.xml | 4 + android/build.gradle | 17 ++ android/gradle.properties | 21 +++ android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54213 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + android/gradlew | 160 ++++++++++++++++++ android/gradlew.bat | 90 ++++++++++ android/settings.gradle | 3 + 28 files changed, 596 insertions(+) create mode 100644 android/.gitignore create mode 100644 android/app/build.gradle create mode 100644 android/app/src/main/AndroidManifest.xml create mode 100644 android/app/src/main/ic_launcher-playstore.png create mode 100644 android/app/src/main/java/com/example/hlsdk/MainActivity.java create mode 100644 android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 android/app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 android/app/src/main/res/values/ic_launcher_background.xml create mode 100644 android/app/src/main/res/values/strings.xml create mode 100644 android/build.gradle create mode 100644 android/gradle.properties create mode 100644 android/gradle/wrapper/gradle-wrapper.jar create mode 100644 android/gradle/wrapper/gradle-wrapper.properties create mode 100755 android/gradlew create mode 100644 android/gradlew.bat create mode 100644 android/settings.gradle diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 00000000..1b88d186 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,10 @@ +.gradle/ +build/ +.externalNativeBuild +.cxx/ +.idea/ +local.properties +.project +.classpath +.gradle +.settings \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 00000000..5336532a --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,76 @@ +import java.time.LocalDateTime +import java.time.Month +import java.time.temporal.ChronoUnit + +apply plugin: 'com.android.application' + +android { + ndkVersion '26.1.10909125' + namespace 'com.example.hlsdk' + + packagingOptions { + jniLibs { + useLegacyPackaging = true + } + } + + defaultConfig { + applicationId 'com.example.hlsdk' + versionName '1.35' + versionCode getBuildNum() + minSdkVersion 19 + targetSdk 34 + compileSdk 34 + } + + externalNativeBuild { + cmake { + version '3.22.1' + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + buildTypes { + debug { + minifyEnabled false + shrinkResources false + debuggable true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + release { + minifyEnabled false + shrinkResources false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + } + + externalNativeBuild { + cmake { + path file('../../CMakeLists.txt') + } + } + + lint { + abortOnError false + } + + androidResources { + noCompress += '' + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.6.1' +} + +static def getBuildNum() { + LocalDateTime now = LocalDateTime.now() + LocalDateTime releaseDate = LocalDateTime.of(2023, Month.DECEMBER, 28, 0, 0, 0) + int qBuildNum = releaseDate.until(now, ChronoUnit.DAYS) + int minuteOfDay = now.getHour() * 60 + now.getMinute() + return qBuildNum * 10000 + minuteOfDay +} \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..23f98ffe --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/ic_launcher-playstore.png b/android/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..ef31446871ce9b941f175d6700541094eb9ebf29 GIT binary patch literal 40387 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

Ua(K9y3ut|~rO_+KHuOW8{R#_% zt(AcoZ?c&1uWOU-l2z7MZQ{t1nrHO-c;=p6n|ZkE0~2#56Iviq@ii=VJdIdi>b@y;iIoZt%@Aq^TjlTJ-pvl!*RN{lM6A%HMi7XDRlJ=bD~rlrQs{(3`BOu;{PHOi-W-T6^ABRWMF` z?3*+Ze`79n<}B5xeMg7dO`^kl?L^DLF`mJR-J6vQ5A>vh-c0JeNK6f3qC~(u!=A_K zENi=qWqS^Kj227xy5>Hj68SiwiP?+6-4z$PlhI$y8=Ja3SvlOa_HGWVe0x66)tG1l zavQHk+r9wwBtNLP`}vT`p7U5$k(0 z^hJfbUBa)7@r|pE0q(v{fro`KS!Fz%K?60Q)f>;EmTF9HW3eT2`1k$EotyRo6y!5J zK0wJNjv4#-ZJ3DbpT2<+YkT2f5HqL9wJlxQpMh{;q`r?a6^x??=I25x%<8bYhIU)$ z*b&{HJ2_C>3B57Sp#$S*J#vYjh~A^syu=TZ8XONgD@t9i$2ODpDVG!e*wKcn*U8SB zE$Ec-^g~iN5DCD6p_f*O@})^0%7w$?>{r&BR-L>TTe>GAvuf$7*gW(<5v);i>wShJ5>e$y{v-}Zdw5LmT}!?&+G9PUE^_xJG( z&|rhSc}1rdW(8pp30_w&O*vs1j~ffAFsU4-1#-{x?6~^@WUQg?=7vx|NaN9%F=fc7 zVr&XKUDhch+CH^(qluW9Og`SkZgv+^bo^%Q@`Hq*i5y!4!`P!_De5!y+Chn`RPe6f z($EBv3icLib0HHvX|tMrW4PY3p#Lt&h|k@2)9hQTxJeP|9Vb4d^QxhvzKUE`F8B=>IP9e0VJ?axv1f6uZNIndqkou)voA!s+Hfg3pz^cPmKob-5>bMPtOTQAA>#mz zO;hnV-+cMFrHUO-Dm{MapB-lzymOTtgmWile9x|8aTN#X-k(-r7bPDXHE^_v27oWn zV{SiyusKD9nEu@7d+fQu!`{5MxYu~rZMgb`MO=H|-xn!5pl-R~8L53L%-q1Z6-`W+vB{V@M``!r<-+n zl5uPK2BTi4aS|OyRJXH}&T;-SL#JVpnw~e>SNO1a6nE*nwVt3E$LWn59~pn0Mh-R^ z7qJr-gez1slcVLFqUV0kM5Y-mK6J~E$M+wmCCADAlYk=);}&CRL=V+jO9}hMSv)$@ zg@{DTYiJ1Rw0#W2udF5{cR2K#{VsGp8q^~b7je)g)rYMnZPuPGhRLae{mMs&GcPv_ z($6^lHkGp$RydbUC)4)jyhGY%>`5sTlC#t@PQHhiupD;iauvo7HBo;mQ%;p6*G+gI z2ya=ke|rrLRXqYb9l2mfGJ+$VI-@f+EC)S|-`8=}E*Ry0w2|>!1_sq^0QOe7e@zBeEBKixN z(8KMrM!n$R8!>UFD3$ssyoF9JaskA>cuRN}3= z?xXTbMTHjCf}RFM2F3gpO3t;azIU>R#-kmgt0seMx7uSUw!JlvDF3eXUP??+Dz2$~;Mk|vk1 zwFsWZf|fN>DFf2(XEy7=029E>NPQ=;~|ZOa@Ta#_zqr4qG4KhTGQ9syPaQ# zltTrT&G&D1u6-h|G*6x>E}MUzuerrtA$X3twCaV-R4blusNRd4kLXF99d~ea9!o&0 zuW$z9?z-CF)8izyU&D`PKfrzTv#W_{J7471;-fKdNVN?^jb|X=NRj0FnQjjO_r+)X z+$_6OgO=*b&Jr1B#Z(Sb(KON2b`Eo`(<_Q=GMA+i*t)IyV%C*QiaZ>D#kt#WT-&os zyhGie_ql;b;Q$cMLAprQKrZ+(Sx{%hXPSk0vlramT(mdGs^h!~QN*Is#Ntuz~81Tฬ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 diff --git a/android/app/src/main/java/com/example/hlsdk/MainActivity.java b/android/app/src/main/java/com/example/hlsdk/MainActivity.java index c6beb064..ebfc1c36 100644 --- a/android/app/src/main/java/com/example/hlsdk/MainActivity.java +++ b/android/app/src/main/java/com/example/hlsdk/MainActivity.java @@ -6,27 +6,10 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; -import android.util.Log; - -import androidx.activity.result.ActivityResult; -import androidx.activity.result.ActivityResultCallback; -import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; - - -public class MainActivity extends AppCompatActivity { - ActivityResultLauncher mActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { - if (result.getResultCode() != Activity.RESULT_OK) { - Log.e(TAG, result.toString()); - } - }); - - private static final String TAG = "MainActivity"; +public class MainActivity extends Activity { @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { + protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String pkg = "su.xash.engine.test"; @@ -44,7 +27,7 @@ public class MainActivity extends AppCompatActivity { } } - mActivityResultLauncher.launch(new Intent().setComponent(new ComponentName(pkg, "su.xash.engine.XashActivity")) + startActivity(new Intent().setComponent(new ComponentName(pkg, "su.xash.engine.XashActivity")) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) .putExtra("gamedir", "valve") .putExtra("gamelibdir", getApplicationInfo().nativeLibraryDir) diff --git a/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index da1dcd94..00000000 --- a/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/android/app/src/main/res/drawable/ic_launcher_background.xml b/android/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index ca3826a4..00000000 --- a/android/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index bbd3e021..00000000 --- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index bbd3e021..00000000 --- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index cb27667f28bb31edec2534dc4301103c9eabb071..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2638 zcmV-U3bFN4Nk&FS3IG6CMM6+kP&iCF3IG5vN5ByfO(<&H22!*ATK7|xX}Rtf4&TaSPygX-tPe% z{QDwwi)ODPm`@Ll{&qUqj{;`kx|hSmAjBOlJ;0%d#1;;E=j$BA#JB7rdUQU}(f)sN zgnYjLnQWGY}>Y-D>$$(bZgsowI_3(@0ZDxOATNufQ(8g zg`tSLR0p;LB5%G8x3+E7$~+f$rwKB5C;EfP3ZMZBpcoo}%%uP_hb9Q!-oMv@8%d7h zxgo>0jv+n)0{rhT6Yt#lsbMY7KJ;xJN;BX8F+m3FDM-yAgD*bxSxsXzb{$q0p}muq zsZI&B2g~?>1Ui5*J|)l*%=qjdfll6Hg)b5mN90SqIv$EhvQN7kI0H@mz{dpn$De*Z zI6U2sN@hNSZsnM7I%VTq-y8@0NkW(JdP;`@Xl1jWhOc~-ig`ial}F+T?(i+0{M~4gm84CSjuk+WZ za`f6s`)Bk=ri$aEFgYhE4`Qj#ncBSNbnZNVe4frDqaEV>#<|T>?%=b4Wvl-q`ad~G zZ{o-ICMF;u02)e6aP+M7e^2i>={y{;>hdbTe;_yCH=ReN_dS3{+PCGJ+i_E#P#hd) zs&gi%ut@)x>Hi7l0o(VaeM`Q3AKsv4z{+d3(}g|D`-UU=20+1+5$LCR#8rg+>7ICfebr4`v;wio=||o$aRtXhBmH z?Ep?+k;@-({!OZfvaGasSPX=j1uryb%rRVu~^#%x!i+lYEZXWP}ekL>c!?wXQw; zFD2)Ig{yVK!Q)3GlQX7Oo#CD^w|S(5F96Gw7CC$aIVZ~!q1xBy=;_fEu(H~J5n1wg z1^_UWZk$HUcGmAw}xTxJ>1q|F)4aRGB#CXMEdnh>Pe&BpUzjE>K0q}{Xqk-Zn6uDNAmW6FH3kzescb9*RDNO1i%|M~6luPw2q{b-Sd_9*@aL1>nA~_aCy-hy6KR}V8e`@ikvU-V;Sj*mMcDh|=l=ZP!(Z34 zB^!fUPErOm$rrG}pb2wZ9Fxz|d3?YM>iF9H_NN(-bjubt5&!@pIZMoKIzqdwLb=2b zp1h?B=V1#iSu$IBeX2|1^%O9(K>Pv$Xlg1m`+EDG54|%aIdd+Zu;kYJDX+ag-5>!9 z#Sv+d>c0pI5};5XkCJ?fpdd(u@+i#7XQ%;!3R0egReUxG5LE7Y*6dyr3_Vr2L`O<2 zCCS~+XPItL00ICcG=>BK0LWN?Ktcn6gFrzrfZ!aAKtTWoagbzOjM7$6xMCj{U`TL?<(rpHDjBmjdrxk4K{NE6mEJWc$(!vFw^ zazk@5<$T}-gCU^+=Lj?e1cOj40ts`%!x01;1mi#h6fiUZF$m5K*2im_cEn8Rih&U=R!=nsGuAC{BP9iVGm9Sb%~XJRuYk3Vq@6G`wKR zr3ze+i;0VQvLhe_8UP3`0%9mo0EvnbAB_w>h6F=GK?s0?UbGpvezz#-2LII%-ff7$UsD!p7JlP3h5U`TE5q~+o0foc`Oj{X1pk0cR7lgTWqmjS>N`Rc}GTolujNy; zgHfP3M;T1rBp8Ge=amdi-5#i7)_Lh00DU(HE!A)`~y}i=?HXffiesc`vajJQsy!ehE!1wbW`;VPGcdtvM9Z3NIn`)$-2{Q6X zi6CckQZ@h_c2s8jVVH*i!NNAi~g>Tc-OQ#Ot;~Bq3!(SO^ zw7zqyABLaoyV@OYWYe`)ZKRk#?Cl;15itQ>V?hom zAG&CCOV`@|)pGL3ohqcm-70r?cXxMpcXxN!xVvZ08lQMF=j?rKGRR%b%8`9!Zs2zX zcr;DdT$}GyzMmMfEz*^Dz;O_43@l?>Us9Z|7Sc|(maVY7gqSVUW-CL_1cJt#7 zSnE_&lpBY+gQUhPtF>nB3M6aWc1F_Mht?<2D}LYq|M{!CQc;; z%eL*b&7FD%)Al{R#FEUm>@Y)_nVFeBB*?aH+Eu;l`}~q`+qP}nwr!hTux;DwLTuZ~ zJ?BmaZW}2|j+YPTzCcC$|3#9s2ghUVvz~dIvADVmhM60xhM60xcay4Lb@lXd!l0NL z6{BNCYEO{FF=%FXoE!iZNxHy3apaiODw3JbkQ?y0xOrlkT@-_27sZ-yu#wE<64$u| z8A$>F7LwGDwNu=fYSB!MsS#@VNui_O*6xh4Np1#!Sf-Aq1rMi*_DDzPZ_0D}w=7eQr-}0D z(S>q~s5-GH#hgbI#mjZ|w<71=q=|Qbe;NRolN%Ga{))x}$m)JwMKM?@FGVjD#UNW= z3OG_$3UO(dDEyqAHC6Z-a~*jkCohH1KTQC@FKx6O768{c>o1jZg(fPXm|!J>p@<~K zBm_k{5>m%VlZ4q-X_630nWcri5(^px@?`?`{mTOXg0vZbxP%N03(;IX3OuPzQ5;x|JX;w~g zNeVHSrqe7R6T4fY3Q*CN69YpPK=if6Q~^9!noC2E*zJO;R!xaIy)i+rL^H&cs?&|= zfoQx1qL3_>QdMC~aW_2@EkF&<1@B{pxr_^Xu+g+62dYL9u;@l2=oOn5jm=zX(b4qY z62PMiGdN4Ng-wgb2PJ}>SW01)4|eG|lpJCaH=~#<(BvMNxF}<_ihxbm8j>?37JQ#g zi|zrYEnw~1CvP*cG{lr@2q$57(S1Og$c?Urpnf`*>5yAkQ9vyC11so2FgOpEC`ulP znW9vS2M7Fy5FG(+Z2g6yNOBOj>wPwO_?L%OPP1&x!&)}(-v~Vap*%EtfXbmGI#Lgm z>y##W9@elt&IR85*06W2N0vI}FB^!vC4m=BhG-|7!8s$BZlaYE=c@HQS4+VZLZALw z90u^xNLf9iN+NSD`)kxh8A8j&XQ9vk36>y-iRukIAy*3A=a%n0*mH4Mk8IpdZA9vZNIzF*8EVC z?}~|%Vo&?+x%PWYWsQgy=oP~d0{ZDj&CJQ)7T4lu>fAbuiJl_;-Kpp+$*d{3%3|GB zlS8%Zry91e)UKYW8XuTk5j%n<6en9`swRe{zkA)6Y9e6xLhyzFiaLP@A7K_HEk~JX z(u3k$SIdQGosYkGNT{Ubt64hMa`{!@^>2~?dy#|(D)Mh{)j6vthe`@PUJeVq_^IXM zizBo#adx)O?(@)gec%C-gN^MEFC<}hQ4e1AsZ99mO6SvW(*M0}v?Tr6>3FboqhF<7 z)j}EFXj<^GpfeH7>J&$_w!4s^4;}_-JieFQCKe@|PVOAHrZzN@C>mr&>&V+GlIM<^ zk(;toggywK^4B)8D)92B$=V17!c5lhs}gvg0IIWVf6i4Ro~B8(8t^hm{pO{}znyJj z*N636SEklVSwc~Xu$|NUO8|rCd8q&Ku_ZRWSVfQ{b&Zrd7EOE}l6;t`FM zCeA6yb2>h--v}%g7;jdqnV7j)7bgj>BK_OjrmIQ{mT-~=QT(i=?=7os)r>j4+eX4% zaYANh&IrE#1%$k<@X-B^ zt@_TAvzaMQwKSc1r22Hu>7k{DIzrKvSDJV)y)eod1CKFd1Hqpg+W?7|UZhq7QE?2i zoYvFhX2A2{?L7i942eC#iwO?_Y@<@kr~fhz?-X8o75VSc==f{7~l{{ zj6@>og`QVY!X!DXyr0$3?Ta?Kc>pjIAmk9{FHz9OH&F3(rrvqS+n(6K+nMq|Sn_oy zeC>%zll-j-S>l~J;r5Ks{2(+Z#O4P@743OG!yUH8ys_)EIU4J91 z>iWCtYj5(VMsPI70&97y5uxj$XH>5%zmfYufUp_V5WwqiPIn7>X_J?3vCz;TgAL~N z(}x($sNu#ilN+vP78s#sMk<*vM#-7ca!L;@og<7DiE#p<6Ns&!FmRZWMJzgF_arj| z{qTd8hHMmYm4iq)X^4Ohg2xa%v11~UykM%&A9;kXM_jc~l<^v-MMhZA-9X80axl$`BcPTbNEKV2XdSYLvs3fbAr3 z8A1Uhv{WXM2p3t8Y`pRP){d3YbQO{3=hb=huG$kqS4*(9+RXr-W(xC~$B{A`DvMRwhF>~LeG#f{Ra#AuZotx;oC zYOInOC#OunGTM?}N*P4LC=frG1j5W?77nrNjMLYw9A;w?JA*iA=yz*ujc%^^|U?rW>f}`%8xY(n`j@lBu^~?k(7-M{J`FYfr(}owIl499@nx zc3weZ2OVk<6GLe1V9D&`85gM5JlIfI2mOwKb99S=Sl~c~3wgjET z3E*mFtyD|*wQsnRUvn5a{Oh}MJ>0YZps4|qnO+-hPUqNOmorn4>DUo?bE~gdbPoC| zM9yHg1e{1N&4dPAPkEc+Yh204m;Jn;|HNRu*2<}K8yF#gr>;Bq0RprL=x6VpB_k2! zFn`Ajk}Ye4t~O7x>J0Qe|7@rl``_5WQ2CooPvT2{d^0dKw^za&ReyL0yba8*M}Ua; zPrqHm7yNik%N$osyg->(yQkPR3cjApP&tF!!j5oW4_*h{Zd1a}(nOirc07D0MS{04 zpFOYZbn#3FT{d&!(o2B4EuK7_Xzkb%ng|{LBez~4S9{8)F<|W=ffv>%EJ5{pZsY<3 zt~a-JjEFw1MF2l9ZS4Hn)PDugZ4+l6g9K>Q|l1~|04Ip&5X z`G#v!ILb~50x+ZQ7n5vQv-RO%`?ueg|^OinfS*Q9!iyuR;Ghnyy$pO$oVWP5Gg>Yu$t5kLX` z%w6lP*JuJXZ}$B0l;HI^VOP^x%<(^?GERlM49+Vdrf7pxy`K5fTxGv+)=Me+wV#i~ zB{Y8SeEQzCzIcsR&H7GtJbR5IK-2Pn{r8@%SAKBHdT=jfW}=jYSQdK$&MPSpE}c`o z9tMe|JJOL{IfywhG&TU3Q`Z0gKE>;}aTcZj4DfbM51&E=Xh?uggD)SS4p$6|igGU~ zqQce2LqNr~2we}sd8K&b9@&q`jo5n7>5h!Oe$eyy^`05+UI}mhe$d-sh;P?);mTX_ zyN~(=Xj9LPtFaSKX};RLK5fZ{Jht z+mE_&3DBjjOBajAoz;F2rW|CwS%Bj%zg%94MS3xlJWDJ{+FDwMF9b4z7f37DN>Pvs zn_feRJS%K$!;{ag;xBMnBQEGYXz|qP;(y|L|M5{*b<`*lK&|cGL-32Z?^pfeWq7|E zmJd(XDc*;!glQhpwD5mCxn z`1^6a{Y3pLK0XGQ1*5s_e>FV1t$P3g+OAmP=kLKCojh7K)Yi3&6E7Y<4!U&oH|(5l zj8(VHut|!1`^Xi)W)r*o;8R-}zUFshCSEo9JNk~*$Nlfwyx8{XlU29>^KsIJs{Vk$ zvS3tmS8*TasS7>!83EemFAeBk8qoXpnjSrlsr|v-_+&TSUK-GA#r&6i_Sg)kR&jsd z%nimjcl9@5{8#<6m#E6$yw|DV=V!g6A4^)ic+$s?-e%_9(^h|D!_fbTF)nuQ`cr5% YU4Y)ifjzVS^w=u2+^dV`HuRDL0GmgL1ONa4 diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index ce6983985a091606f912592f3ed86002d9928725..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2086 zcmV+>2-){iNk&E<2mk0 z#QohDWTB*yB&l-f6|nx54LrE$v?y-dHd5L?FGI-wQ+5Ehk!+{=p~_IKNd4D>^b86L3P4}bpk@$Z<}Gk9%1pwav&nY+tFs3E(1A&p+8l1=E7f92 z*0#m1r04rnrebDh7~ukH5w(e03ODScSAjF`9x*e6+2PD@U`bs(er{%1u1gAEMMhZF zs+Q^6wsvfL*1qmXpMS+ps=td}v9jIfAX5E0gxI#-+fHX2pC8_vZe-i4ZKub&539Z> zu;o(>&lTnLe;QH<7xV%)Oi>LL|l^R&$QtRk>WGOiTP`}@kGt)Mox#~Lll+>>${cF z`=0u!$F7HTdL*y>B!mC&-2L6ki!<-;!AdnO7)gkF#eACoh8jUSY9uhd5+ImRNzBz{ z8>Sfy8P^WJf5_<=RK9wMBVVnB|7cr8$Ebmk1|l+dsm=)yI(Mmam)eq9s&g}OY{=*~ zEdpO%J>rP|qb|Zz{b25JgriASW>+S{TnL5VUoVifhDIH_hR zv*g5v>PG)E7WZG+J;iOP$a7m9l0nKaAU{sN6AnCzoxC%J*}XqcD|mWl>G=#3Y>qi|r&qsZ-Xw)y_a?pFYYSujCRrVC~eZVVeUs&U7ShlY3< z`9T^#i9E#`^{aO|*!aN{P|Bd(xz6A29Xa58e#^z{a%0@j{JQ5x;>c#=Pxp@O)}1@E zS^%sZobuY(eU761H`GLbgOUKI0c0dtWj-6nujb96*9Y6Af>EW&3oR>)G|DBw+s@)w zUyd7BS5d+tL8j551vniBH%Mo}KqxGn!2v7HakjwFM$3}A5$`mfG|Od3318L!Z}$^ zP6GOq2_F4)9;_=@Hbi7bQC}&=DzhPAijpC+!gwI_SF>$0=rGG9E)dBI(x&JpwhI`k z6~=(nAg#mUV!ypu9gzOfFuIFo9L>fehXjB_47{uu4{Q9T$c`~cr_%tOE(CcMXkc2D zCKFmXNkU0;Oil-@nY|??!^W=2Ln(isvMJEQ0}-S>?ZdPvjcLy@i*#H}g&dzZtk(Cm zQtpOLtu2NDDIkc8sgAHP5=sPj-v*gIDp&4c4QY)>l|Z30Lrn>U^z)fcu6LMh-@s~R(IQrVS_9k zP>l>ZB*cNBrFPTMa-iHowu3zf1B*zXT9^*jd77@pEM_R}#j{K|2^2US$t#~{(0qCK zFFVhteXi6(GZ+>bnW8P9k`+KnkZE<0xTPQ4$}SWUURlC846Tz)EAqfx%7AoW8o-63 z)J*%^G#!WYRQ1)zO*}X3bg`&=U} znGR~R`j4`72M?uR2+9<33mi@>CzwroG)nrA|Bu*yfv=>}#Fbb%Fjtgx)IaOg`zG@W z-gEcM3d$&7oBzE9pCCJ&?o5^GOqCvw)&RhTa8?6w2%vrK*Z#k9*Y^!5j(Pg}zps~{ QSd4h@pNcAIK=I=d0FJE*C;$Ke diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index d72d3abeffd43bee0bf9564e671e9f92bc2a5af3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2984 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 diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 08dff72be3b3123ec4df48815833bf5fcbbaf7c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6368 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 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index b579e14ef98e1bfe1732e24faca9ec828bc7648b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5096 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 K<$&;_Nd*A$aHszO diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index ed75f01dd1556d23342cc023ec364eb2e9423bc4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9222 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 KDw!0}WB>p{?fdQk diff --git a/android/app/src/main/res/values/ic_launcher_background.xml b/android/app/src/main/res/values/ic_launcher_background.xml deleted file mode 100644 index a256bc80..00000000 --- a/android/app/src/main/res/values/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #EEEEEE - \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml deleted file mode 100644 index c35f762c..00000000 --- a/android/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - hlsdk-portable - \ No newline at end of file From 088e69a577261781f0b279519c56b32eb6e12676 Mon Sep 17 00:00:00 2001 From: Velaron Date: Fri, 29 Dec 2023 12:30:40 +0200 Subject: [PATCH 073/127] android: add some documentation --- README.md | 25 ++++++++++++++++++- android/app/build.gradle | 9 ++----- android/app/src/main/AndroidManifest.xml | 2 +- .../xash}/hlsdk/MainActivity.java | 2 +- 4 files changed, 28 insertions(+), 10 deletions(-) rename android/app/src/main/java/{com/example => su/xash}/hlsdk/MainActivity.java (97%) diff --git a/README.md b/README.md index d7912d20..2f8d7bb7 100644 --- a/README.md +++ b/README.md @@ -241,8 +241,31 @@ schroot --chroot jessie -- cmake --build build-in-chroot ``` ## Android +1. Set up [Android Studio/Android SDK](https://developer.android.com/studio). -TODO +### Android Studio +Open the project located in the `android` folder and build. + +### Command-line +``` +cd android +./gradlew assembleRelease +``` + +### Customizing the build +settings.gradle: +* **rootProject.name** - project name displayed in Android Studio (optional). + +app/build.gradle: +* **android->namespace** and **android->defaultConfig->applicationId** - set both to desired package name. +* **getBuildNum** function - set **releaseDate** variable as desired. + +app/java/su/xash/hlsdk/MainActivity.java: +* **.putExtra("gamedir", ...)** - set desired gamedir. + +src/main/AndroidManifest.xml: +* **application->android:label** - set desired application name. +* **su.xash.engine.gamedir** value - set to same as above. ## Nintendo Switch diff --git a/android/app/build.gradle b/android/app/build.gradle index 084b5100..ee029686 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -12,7 +12,7 @@ android { applicationId 'com.example.hlsdk' versionName '1.0' versionCode getBuildNum() - minSdkVersion 19 + minSdkVersion 3 targetSdk 34 compileSdk 34 } @@ -20,6 +20,7 @@ android { externalNativeBuild { cmake { version '3.22.1' + path file('../../CMakeLists.txt') } } @@ -42,12 +43,6 @@ android { } } - externalNativeBuild { - cmake { - path file('../../CMakeLists.txt') - } - } - lint { abortOnError false } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index ee5e0d62..d928f396 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -13,7 +13,7 @@ android:value="valve" /> diff --git a/android/app/src/main/java/com/example/hlsdk/MainActivity.java b/android/app/src/main/java/su/xash/hlsdk/MainActivity.java similarity index 97% rename from android/app/src/main/java/com/example/hlsdk/MainActivity.java rename to android/app/src/main/java/su/xash/hlsdk/MainActivity.java index ebfc1c36..5d1dd353 100644 --- a/android/app/src/main/java/com/example/hlsdk/MainActivity.java +++ b/android/app/src/main/java/su/xash/hlsdk/MainActivity.java @@ -1,4 +1,4 @@ -package com.example.hlsdk; +package su.xash.hlsdk; import android.app.Activity; import android.content.ComponentName; From a21b3456527e688eebdfbe0f70113b6a99f1b2a2 Mon Sep 17 00:00:00 2001 From: Velaron Date: Tue, 2 Jan 2024 10:45:33 +0200 Subject: [PATCH 074/127] android: merge .gitignore --- .gitignore | 10 ++++++++++ android/.gitignore | 10 ---------- 2 files changed, 10 insertions(+), 10 deletions(-) delete mode 100644 android/.gitignore diff --git a/.gitignore b/.gitignore index 42594b50..2c5017bf 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,13 @@ CMakeSettings.json CMakeFiles CMakeCache.txt Makefile + +# Android Studio/Gradle +.gradle/ +.externalNativeBuild +.cxx/ +.idea/ +local.properties +.project +.classpath +.settings \ No newline at end of file diff --git a/android/.gitignore b/android/.gitignore deleted file mode 100644 index 1b88d186..00000000 --- a/android/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -.gradle/ -build/ -.externalNativeBuild -.cxx/ -.idea/ -local.properties -.project -.classpath -.gradle -.settings \ No newline at end of file From 30c1a9fb57c2e68afaecf0a923e90a0187780299 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Fri, 12 Jan 2024 14:02:25 +0500 Subject: [PATCH 075/127] cmake: add option for pollyhedral optimization. --- CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c44c8cae..9412d493 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,7 @@ option(USE_NOVGUI_SCOREBOARD "Prefer non-VGUI Scoreboard when USE_VGUI is enable option(USE_VOICEMGR "Enable VOICE MANAGER." OFF) option(BUILD_CLIENT "Build client dll" ON) option(BUILD_SERVER "Build server dll" ON) +option(POLLY "Enable pollyhedral optimization" OFF) if(CMAKE_SIZEOF_VOID_P EQUAL 4 OR ((WIN32 OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") @@ -177,3 +178,11 @@ endif() if(NOT BUILD_SERVER AND NOT BUILD_CLIENT) message(FATAL_ERROR "Nothing to build") endif() + +if(POLLY) + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + add_compile_options(-mllvm -polly) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + add_compile_options(-fgraphite-identity -floop-interchange -floop-block) + endif() +endif() From b07153f800c967e8255d76bdb948e57d057a56e7 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Fri, 12 Jan 2024 14:08:27 +0500 Subject: [PATCH 076/127] android: enable pollyhedral optimization by default. --- android/app/build.gradle | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index ee029686..9952c3d1 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -15,6 +15,12 @@ android { minSdkVersion 3 targetSdk 34 compileSdk 34 + + externalNativeBuild { + cmake { + arguments '-DPOLLY=ON' + } + } } externalNativeBuild { @@ -64,4 +70,4 @@ static def getBuildNum() { int qBuildNum = releaseDate.until(now, ChronoUnit.DAYS) int minuteOfDay = now.getHour() * 60 + now.getMinute() return qBuildNum * 10000 + minuteOfDay -} \ No newline at end of file +} From 443124ca6fcb488303d2cc1660bd8349b739c66b Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Fri, 12 Jan 2024 22:04:28 +0500 Subject: [PATCH 077/127] wscript: force to use server library name for android. --- wscript | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wscript b/wscript index 02efd884..86418434 100644 --- a/wscript +++ b/wscript @@ -230,6 +230,10 @@ def configure(conf): if conf.env.HLDEMO_BUILD and conf.env.OEM_BUILD: conf.fatal('Don\'t mix Demo and OEM builds!') + # force to use server library name + if conf.env.DEST_OS == 'android': + conf.env.SERVER_LIBRARY_NAME = 'server' # can't be any other name, until specified + # strip lib from pattern if conf.env.DEST_OS not in ['android']: if conf.env.cxxshlib_PATTERN.startswith('lib'): From 63807139c3ee9fc6a370c77e884115a18c2b0e17 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 15 Jan 2024 07:16:49 +0500 Subject: [PATCH 078/127] cmake: always use LTO if possible. --- CMakeLists.txt | 8 ++++++++ cl_dll/CMakeLists.txt | 4 ++++ cl_dll/cl_util.h | 3 ++- cl_dll/util.cpp | 2 +- dlls/CMakeLists.txt | 4 ++++ 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9412d493..04cf5bce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") include(CheckIncludeFile) include(CheckCSourceCompiles) include(VSForceXPToolchain) # Force XP toolchain for Visual Studio +include(CheckIPOSupported) project (HLSDK-PORTABLE) @@ -152,6 +153,13 @@ if(VITA) add_compile_options(-fno-use-cxa-atexit) endif() +check_ipo_supported(RESULT HAVE_LTO OUTPUT LTO_ERROR) +if(HAVE_LTO) + message(STATUS "IPO / LTO enabled") +else() + message(STATUS "IPO / LTO not supported: <${LTO_ERROR}>") +endif() + check_include_file("tgmath.h" HAVE_TGMATH_H) if(HAVE_TGMATH_H) if(NOT MSVC) diff --git a/cl_dll/CMakeLists.txt b/cl_dll/CMakeLists.txt index b88821a2..be856edf 100644 --- a/cl_dll/CMakeLists.txt +++ b/cl_dll/CMakeLists.txt @@ -208,6 +208,10 @@ if(MSVC) set_property(TARGET ${CLDLL_LIBRARY} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") endif() +if(HAVE_LTO) + set_property(TARGET ${CLDLL_LIBRARY} PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) +endif() + install( TARGETS ${CLDLL_LIBRARY} DESTINATION "${GAMEDIR}/${CLIENT_INSTALL_DIR}/" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE diff --git a/cl_dll/cl_util.h b/cl_dll/cl_util.h index 91615660..65275c69 100644 --- a/cl_dll/cl_util.h +++ b/cl_dll/cl_util.h @@ -166,7 +166,8 @@ void VectorScale( const float *in, float scale, float *out ); float VectorNormalize( float *v ); void VectorInverse( float *v ); -extern vec3_t vec3_origin; +// extern vec3_t vec3_origin; +extern float vec3_origin[3]; // disable 'possible loss of data converting float to int' warning message #pragma warning( disable: 4244 ) diff --git a/cl_dll/util.cpp b/cl_dll/util.cpp index 7d6b6669..8b5792b4 100644 --- a/cl_dll/util.cpp +++ b/cl_dll/util.cpp @@ -33,7 +33,7 @@ #if !defined(M_PI_F) #define M_PI_F (float)M_PI #endif -extern vec3_t vec3_origin; +// extern vec3_t vec3_origin; // if C++ mangling differs from C symbol name #if _MSC_VER || __WATCOMC__ diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index 638f1a73..981a2ca1 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -173,6 +173,10 @@ if(MSVC) set_property(TARGET ${SVDLL_LIBRARY} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") endif() +if(HAVE_LTO) + set_property(TARGET ${SVDLL_LIBRARY} PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) +endif() + install( TARGETS ${SVDLL_LIBRARY} DESTINATION "${GAMEDIR}/${SERVER_INSTALL_DIR}/" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE From b2d171869dcfeb761babeb8e5c219107d50dc758 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 15 Jan 2024 07:31:16 +0500 Subject: [PATCH 079/127] Fix windows build. --- cl_dll/util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl_dll/util.cpp b/cl_dll/util.cpp index 8b5792b4..ec5c796f 100644 --- a/cl_dll/util.cpp +++ b/cl_dll/util.cpp @@ -37,7 +37,7 @@ // if C++ mangling differs from C symbol name #if _MSC_VER || __WATCOMC__ -vec3_t vec3_origin; +float vec3_origin[3]; #endif float Length( const float *v ) From 86c00428f52815cdd1cb2db9f3f141d91c259986 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 15 Jan 2024 07:40:06 +0500 Subject: [PATCH 080/127] cmake: disable RTTI for *nix. --- cl_dll/CMakeLists.txt | 2 +- dlls/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cl_dll/CMakeLists.txt b/cl_dll/CMakeLists.txt index be856edf..acf5aafa 100644 --- a/cl_dll/CMakeLists.txt +++ b/cl_dll/CMakeLists.txt @@ -29,7 +29,7 @@ add_definitions(-DCLIENT_DLL) if(NOT MSVC) add_compile_options(-fno-exceptions) # GCC/Clang flag - add_compile_options(-Wno-write-strings) # GCC/Clang flag + add_compile_options(-fno-rtti) # GCC/Clang flag add_compile_options(-fvisibility=hidden) # GCC/Clang flag add_definitions(-D_LINUX -DLINUX) # It seems enough for all non-Win32 systems add_definitions(-Dstricmp=strcasecmp -Dstrnicmp=strncasecmp) diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index 981a2ca1..2530e508 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -27,6 +27,7 @@ set (SVDLL_LIBRARY server) if(NOT MSVC) add_compile_options(-fno-exceptions) # GCC/Clang flag + add_compile_options(-fno-rtti) # GCC/Clang flag add_compile_options(-Wno-invalid-offsetof) # GCC/Clang flag add_compile_options(-fvisibility=hidden) # GCC/Clang flag add_definitions(-D_LINUX) # It seems enough for all non-Win32 systems From 554dc51472e69686409cc83b616a21a42216d817 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 15 Jan 2024 08:55:36 +0500 Subject: [PATCH 081/127] cmake: add more release optimization flags. --- cl_dll/CMakeLists.txt | 11 +++++++++++ dlls/CMakeLists.txt | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/cl_dll/CMakeLists.txt b/cl_dll/CMakeLists.txt index acf5aafa..cc40c27e 100644 --- a/cl_dll/CMakeLists.txt +++ b/cl_dll/CMakeLists.txt @@ -36,6 +36,17 @@ if(NOT MSVC) if(NOT MINGW) add_definitions(-D_snprintf=snprintf -D_vsnprintf=vsnprintf) endif() + if(CMAKE_BUILD_TYPE MATCHES "Release" + OR (CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo" + AND CMAKE_SYSTEM_NAME STREQUAL "Android")) + add_compile_options(-fno-unwind-tables -fno-asynchronous-unwind-tables) # GCC/Clang flag + add_compile_options(-fomit-frame-pointer) # GCC/Clang flag + add_compile_options(-ftree-vectorize) # GCC/Clang flag + add_compile_options(-funsafe-math-optimizations) # GCC/Clang flag + if(CMAKE_SYSTEM_NAME STREQUAL "Android") + add_compile_options(-O3 -DNDEBUG) # gradle compiles release builds with RelWithDebInfo(-O2 -g) and strips debug symbols. + endif() + endif() else() add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE) endif() diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index 2530e508..82460492 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -32,6 +32,17 @@ if(NOT MSVC) add_compile_options(-fvisibility=hidden) # GCC/Clang flag add_definitions(-D_LINUX) # It seems enough for all non-Win32 systems add_definitions(-Dstricmp=strcasecmp -Dstrnicmp=strncasecmp -D_snprintf=snprintf -D_vsnprintf=vsnprintf ) + if(CMAKE_BUILD_TYPE MATCHES "Release" + OR (CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo" + AND CMAKE_SYSTEM_NAME STREQUAL "Android")) + add_compile_options(-fno-unwind-tables -fno-asynchronous-unwind-tables) # GCC/Clang flag + add_compile_options(-fomit-frame-pointer) # GCC/Clang flag + add_compile_options(-ftree-vectorize) # GCC/Clang flag + add_compile_options(-funsafe-math-optimizations) # GCC/Clang flag + if(CMAKE_SYSTEM_NAME STREQUAL "Android") + add_compile_options(-O3 -DNDEBUG) # gradle compiles release builds with RelWithDebInfo(-O2 -g) and strips debug symbols. + endif() + endif() else() add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE) endif() From 38950e4e41c6b17eeb89f8ad73b7e121b4db2d83 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 15 Jan 2024 10:00:46 +0500 Subject: [PATCH 082/127] cmake: strip release builds. --- cl_dll/CMakeLists.txt | 4 ++++ dlls/CMakeLists.txt | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/cl_dll/CMakeLists.txt b/cl_dll/CMakeLists.txt index cc40c27e..f1dce6bf 100644 --- a/cl_dll/CMakeLists.txt +++ b/cl_dll/CMakeLists.txt @@ -228,3 +228,7 @@ install( TARGETS ${CLDLL_LIBRARY} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) + +add_custom_command(TARGET ${CLDLL_LIBRARY} + POST_BUILD DEPENDS ${CLDLL_LIBRARY} + COMMAND $<$:${CMAKE_STRIP}> -s $) diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index 82460492..460f7a19 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -194,3 +194,7 @@ install( TARGETS ${SVDLL_LIBRARY} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + +add_custom_command(TARGET ${SVDLL_LIBRARY} + POST_BUILD DEPENDS ${SVDLL_LIBRARY} + COMMAND $<$:${CMAKE_STRIP}> -s $) From 90193a938312d79ed5de611476c322c9101e9326 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 15 Jan 2024 10:10:02 +0500 Subject: [PATCH 083/127] Check if we have sv_language cvar to detect Xash3D. --- dlls/game.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/game.cpp b/dlls/game.cpp index c648c796..e64f0417 100644 --- a/dlls/game.cpp +++ b/dlls/game.cpp @@ -462,7 +462,7 @@ cvar_t sk_player_leg3 = { "sk_player_leg3","1" }; void GameDLLInit( void ) { // Register cvars here: - if( CVAR_GET_POINTER( "build" ) ) + if( !CVAR_GET_POINTER( "sv_language" ) ) g_fIsXash3D = TRUE; g_psv_gravity = CVAR_GET_POINTER( "sv_gravity" ); From 7c7a12b4bc1ee8f51be3d8e1b6579aaace601e2d Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 15 Jan 2024 11:22:18 +0500 Subject: [PATCH 084/127] Update README.md. --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 2f8d7bb7..3bff2f52 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ sudo ./setup_chroot.sh --i386 --tarball ./com.valvesoftware.SteamRuntime.Sdk-i38 Now you can use cmake and make prepending the commands with `schroot --chroot steamrt_scout_i386 --`: ``` -schroot --chroot steamrt_scout_i386 -- cmake -B build-in-steamrt -S . +schroot --chroot steamrt_scout_i386 -- cmake -DCMAKE_BUILD_TYPE=Release -B build-in-steamrt -S . schroot --chroot steamrt_scout_i386 -- cmake --build build-in-steamrt ``` @@ -180,20 +180,20 @@ sudo apt install cmake build-essential gcc-multilib g++-multilib libsdl2-dev:i38 ### Building ``` -cmake -B build -S . +cmake -DCMAKE_BUILD_TYPE=Release -B build -S . cmake --build build ``` Note that the libraries built this way might be not compatible with Steam Half-Life. If you have such issue you can configure it to build statically with c++ and gcc libraries: ``` -cd build -cmake .. -DCMAKE_CXX_FLAGS="-static-libstdc++ -static-libgcc" +cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -static-libstdc++ -static-libgcc" -B build -S . +cmake --build build ``` -Alternatively, you can avoid libstdc++/libgcc_s linking using small libsupc++ library and optimization build flags instead: +Alternatively, you can avoid libstdc++/libgcc_s linking using small libsupc++ library and optimization build flags instead(Really just set Release build type and set C compiler as C++ compiler): ``` -cd build -cmake .. -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=gcc -DCMAKE_C_FLAGS="-O3" -DCMAKE_CXX_FLAGS="-O3 -lsupc++" +cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=cc -B build -S . +cmake --build build ``` To ensure portability it's still better to build using Steam Runtime or another chroot of some older distro. @@ -236,7 +236,7 @@ Insert your actual user name in place of `yourusername`. Prepend any make or cmake call with `schroot -c jessie --`: ``` -schroot --chroot jessie -- cmake -B build-in-chroot -S . +schroot --chroot jessie -- cmake -DCMAKE_BUILD_TYPE=Release -B build-in-chroot -S . schroot --chroot jessie -- cmake --build build-in-chroot ``` @@ -337,13 +337,13 @@ Install C and C++ compilers (like gcc or clang), cmake and make. ### Building ``` -cmake -B build -S . +cmake -DCMAKE_BUILD_TYPE=Release -B build -S . cmake --build build ``` Force 64-bit build: ``` -cmake -D64BIT=1 -B build -S . +cmake -DCMAKE_BUILD_TYPE=Release -D64BIT=1 -B build -S . cmake --build build ``` From 2af6840ff415557818662a387ad4569026412b11 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 15 Jan 2024 11:35:13 +0500 Subject: [PATCH 085/127] gha: use release configuration for linux builds. --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3b5ae35c..2cb22358 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,13 +52,13 @@ jobs: - name: Build on Linux if: startsWith(matrix.os, 'ubuntu') run: | - schroot --chroot steamrt_scout_i386 -- cmake -B build -S . -DCMAKE_EXE_LINKER_FLAGS="-Wl,--no-undefined" -DCMAKE_INSTALL_PREFIX="$PWD/dist" + schroot --chroot steamrt_scout_i386 -- cmake -DCMAKE_BUILD_TYPE=Release -DPOLLY=ON -B build -S . -DCMAKE_EXE_LINKER_FLAGS="-Wl,--no-undefined" -DCMAKE_INSTALL_PREFIX="$PWD/dist" schroot --chroot steamrt_scout_i386 -- cmake --build build --target all schroot --chroot steamrt_scout_i386 -- cmake --build build --target install - name: Build on Linux with vgui if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.cc, 'gcc') run: | - schroot --chroot steamrt_scout_i386 -- cmake -B build-vgui -S . -DCMAKE_EXE_LINKER_FLAGS="-Wl,--no-undefined" -DUSE_VGUI=ON -DCMAKE_INSTALL_PREFIX="$PWD/dist-vgui" + schroot --chroot steamrt_scout_i386 -- cmake -DCMAKE_BUILD_TYPE=Release -DPOLLY=ON -B build-vgui -S . -DCMAKE_EXE_LINKER_FLAGS="-Wl,--no-undefined" -DUSE_VGUI=ON -DCMAKE_INSTALL_PREFIX="$PWD/dist-vgui" cp vgui_support/vgui-dev/lib/vgui.so build-vgui/cl_dll schroot --chroot steamrt_scout_i386 -- cmake --build build-vgui --target all schroot --chroot steamrt_scout_i386 -- cmake --build build-vgui --target install From b8e143f5acd5cce9e752b4a620779f8c8aedc398 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 15 Jan 2024 14:10:37 +0500 Subject: [PATCH 086/127] cmake: set -O3 option to android linker. --- cl_dll/CMakeLists.txt | 1 + dlls/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/cl_dll/CMakeLists.txt b/cl_dll/CMakeLists.txt index f1dce6bf..9375de27 100644 --- a/cl_dll/CMakeLists.txt +++ b/cl_dll/CMakeLists.txt @@ -45,6 +45,7 @@ if(NOT MSVC) add_compile_options(-funsafe-math-optimizations) # GCC/Clang flag if(CMAKE_SYSTEM_NAME STREQUAL "Android") add_compile_options(-O3 -DNDEBUG) # gradle compiles release builds with RelWithDebInfo(-O2 -g) and strips debug symbols. + target_link_options(${CLDLL_LIBRARY} PUBLIC "LINKER:-O3") endif() endif() else() diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index 460f7a19..7da665d9 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -41,6 +41,7 @@ if(NOT MSVC) add_compile_options(-funsafe-math-optimizations) # GCC/Clang flag if(CMAKE_SYSTEM_NAME STREQUAL "Android") add_compile_options(-O3 -DNDEBUG) # gradle compiles release builds with RelWithDebInfo(-O2 -g) and strips debug symbols. + target_link_options(${SVDLL_LIBRARY} PUBLIC "LINKER:-O3") endif() endif() else() From d77a7e4753b6e6d1becf3c9471fbbe649dc48bf2 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Wed, 17 Jan 2024 14:14:47 +0500 Subject: [PATCH 087/127] Improve handgrenade throw physics. --- dlls/handgrenade.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/handgrenade.cpp b/dlls/handgrenade.cpp index f9f360ec..fb729b2a 100644 --- a/dlls/handgrenade.cpp +++ b/dlls/handgrenade.cpp @@ -140,9 +140,9 @@ void CHandGrenade::WeaponIdle( void ) else angThrow.x = -10.0f + angThrow.x * ( ( 90.0f + 10.0f ) / 90.0f ); - float flVel = ( 90.0f - angThrow.x ) * 4.0f; - if( flVel > 500.0f ) - flVel = 500.0f; + float flVel = ( 90.0f - angThrow.x ) * 6.5f; + if( flVel > 1000.0f ) + flVel = 1000.0f; UTIL_MakeVectors( angThrow ); From 538e3a84d748ce8619493eece871403f10ea3fbe Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 20 Jan 2024 10:45:01 +0500 Subject: [PATCH 088/127] Improve spawn spot randomization in multiplayer. --- dlls/player.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/player.cpp b/dlls/player.cpp index 76f0ed7c..f08ac269 100644 --- a/dlls/player.cpp +++ b/dlls/player.cpp @@ -2718,7 +2718,7 @@ edict_t *EntSelectSpawnPoint( CBaseEntity *pPlayer ) { pSpot = g_pLastSpawn; // Randomize the start spot - for( int i = RANDOM_LONG( 1, 5 ); i > 0; i-- ) + for( int i = RANDOM_LONG( 1, 9 ); i > 0; i-- ) pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" ); if( FNullEnt( pSpot ) ) // skip over the null point pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" ); From edd258e30244ebdca5012de9d13a3eeec2217b65 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Fri, 26 Jan 2024 21:22:21 +0500 Subject: [PATCH 089/127] When a player dies in multiplayer, make their body non-solid immediately. --- dlls/player.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dlls/player.cpp b/dlls/player.cpp index f08ac269..35234835 100644 --- a/dlls/player.cpp +++ b/dlls/player.cpp @@ -903,6 +903,9 @@ void CBasePlayer::Killed( entvars_t *pevAttacker, int iGib ) // UNDONE: Put this in, but add FFADE_PERMANENT and make fade time 8.8 instead of 4.12 // UTIL_ScreenFade( edict(), Vector( 128, 0, 0 ), 6, 15, 255, FFADE_OUT | FFADE_MODULATE ); + if( g_pGameRules->IsMultiplayer()) + pev->solid = SOLID_NOT; + if( ( pev->health < -40 && iGib != GIB_NEVER ) || iGib == GIB_ALWAYS ) { pev->solid = SOLID_NOT; From deee0dcbfbaa1aef409ce40fb863f3b42ca0dba5 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Fri, 26 Jan 2024 21:42:30 +0500 Subject: [PATCH 090/127] Avoid trigger_hurt's heal for dead players. --- dlls/triggers.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dlls/triggers.cpp b/dlls/triggers.cpp index a4bcbbc7..69b3ee3d 100644 --- a/dlls/triggers.cpp +++ b/dlls/triggers.cpp @@ -984,7 +984,12 @@ void CBaseTrigger::HurtTouch( CBaseEntity *pOther ) } #endif if( fldmg < 0 ) - pOther->TakeHealth( -fldmg, m_bitsDamageInflict ); + { + if( !( g_pGameRules->IsMultiplayer() + && pOther->IsPlayer() + && pOther->pev->deadflag )) + pOther->TakeHealth( -fldmg, m_bitsDamageInflict ); + } else pOther->TakeDamage( pev, pev, fldmg, m_bitsDamageInflict ); From 325c61f4879706c250ac004b636c7cdb86f6bb33 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 27 Jan 2024 00:33:17 +0500 Subject: [PATCH 091/127] Fix player's death animation in multiplayer. --- dlls/player.cpp | 15 +++++++++++---- dlls/player.h | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/dlls/player.cpp b/dlls/player.cpp index 35234835..fe5f92e6 100644 --- a/dlls/player.cpp +++ b/dlls/player.cpp @@ -867,7 +867,7 @@ void CBasePlayer::Killed( entvars_t *pevAttacker, int iGib ) SetAnimation( PLAYER_DIE ); - m_iRespawnFrames = 0; + m_flRespawnTimer = 0; pev->modelindex = g_ulModelIndexPlayer; // don't use eyes @@ -1270,8 +1270,8 @@ void CBasePlayer::PlayerDeathThink( void ) { StudioFrameAdvance(); - m_iRespawnFrames++; // Note, these aren't necessarily real "frames", so behavior is dependent on # of client movement commands - if( m_iRespawnFrames < 120 ) // Animations should be no longer than this + m_flRespawnTimer = gpGlobals->frametime + m_flRespawnTimer; // Note, these aren't necessarily real "frames", so behavior is dependent on # of client movement commands + if( m_flRespawnTimer < 4.0f ) // Animations should be no longer than this return; } @@ -1281,7 +1281,14 @@ void CBasePlayer::PlayerDeathThink( void ) pev->movetype = MOVETYPE_NONE; if( pev->deadflag == DEAD_DYING ) + { + if( g_pGameRules->IsMultiplayer() && pev->movetype == MOVETYPE_NONE ) + { + CopyToBodyQue( pev ); + pev->modelindex = 0; + } pev->deadflag = DEAD_DEAD; + } StopAnimation(); @@ -1322,7 +1329,7 @@ void CBasePlayer::PlayerDeathThink( void ) return; pev->button = 0; - m_iRespawnFrames = 0; + m_flRespawnTimer = 0; //ALERT( at_console, "Respawn\n" ); diff --git a/dlls/player.h b/dlls/player.h index b2484b15..f86d25b4 100644 --- a/dlls/player.h +++ b/dlls/player.h @@ -188,7 +188,7 @@ public: Vector m_vecAutoAim; BOOL m_fOnTarget; int m_iDeaths; - float m_iRespawnFrames; // used in PlayerDeathThink() to make sure players can always respawn + float m_flRespawnTimer; // used in PlayerDeathThink() to make sure players can always respawn int m_lastx, m_lasty; // These are the previous update's crosshair angles, DON"T SAVE/RESTORE From b2161d18d05525dbe28f08f26e4f2c91658fca31 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 27 Jan 2024 01:46:15 +0500 Subject: [PATCH 092/127] Set flashlight time on restore. --- dlls/player.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dlls/player.cpp b/dlls/player.cpp index fe5f92e6..83ff51cd 100644 --- a/dlls/player.cpp +++ b/dlls/player.cpp @@ -3012,6 +3012,9 @@ int CBasePlayer::Restore( CRestore &restore ) // Barring that, we clear it out here instead of using the incorrect restored time value. m_flNextAttack = UTIL_WeaponTimeBase(); #endif + if( m_flFlashLightTime == 0.0f ) + m_flFlashLightTime = 1.0f; + return status; } From 1f4a4140168f2042dbc55de2e4b50b025e185ade Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 27 Jan 2024 02:40:20 +0500 Subject: [PATCH 093/127] Fix gauss sound on level transition. --- dlls/gauss.cpp | 4 ++++ dlls/player.cpp | 1 + 2 files changed, 5 insertions(+) diff --git a/dlls/gauss.cpp b/dlls/gauss.cpp index fb512757..a997c804 100644 --- a/dlls/gauss.cpp +++ b/dlls/gauss.cpp @@ -170,6 +170,8 @@ void CGauss::PrimaryAttack() void CGauss::SecondaryAttack() { + if( m_pPlayer->m_flStartCharge > gpGlobals->time ) + m_pPlayer->m_flStartCharge = gpGlobals->time; // don't fire underwater if( m_pPlayer->pev->waterlevel == 3 ) { @@ -315,6 +317,8 @@ void CGauss::StartFire( void ) { float flDamage; + if( m_pPlayer->m_flStartCharge > gpGlobals->time ) + m_pPlayer->m_flStartCharge = gpGlobals->time; UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle ); Vector vecAiming = gpGlobals->v_forward; Vector vecSrc = m_pPlayer->GetGunPosition(); // + gpGlobals->v_up * -8 + gpGlobals->v_right * 8; diff --git a/dlls/player.cpp b/dlls/player.cpp index 83ff51cd..0d984b12 100644 --- a/dlls/player.cpp +++ b/dlls/player.cpp @@ -2797,6 +2797,7 @@ ReturnSpot: void CBasePlayer::Spawn( void ) { + m_flStartCharge = gpGlobals->time; pev->classname = MAKE_STRING( "player" ); pev->health = 100; pev->armorvalue = 0; From d2a43db7e3e17a3e32e7faaf5f610cdf42f31da8 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 28 Jan 2024 09:13:49 +0000 Subject: [PATCH 094/127] HL 25th anniversary pushable fix. (#427) --- dlls/func_break.cpp | 76 +++++++++++++++++++++++++++++++-------------- dlls/game.cpp | 4 +++ dlls/game.h | 1 + 3 files changed, 57 insertions(+), 24 deletions(-) diff --git a/dlls/func_break.cpp b/dlls/func_break.cpp index 0c5e0114..f1829d6c 100644 --- a/dlls/func_break.cpp +++ b/dlls/func_break.cpp @@ -928,22 +928,25 @@ void CPushable::Move( CBaseEntity *pOther, int push ) if( pOther->IsPlayer() ) { - // g-cont. fix pushable acceleration bug (now implemented as cvar) - if (pushablemode.value == 1) - { - // Allow player push when moving right, left and back too - if ( push && !(pevToucher->button & (IN_FORWARD|IN_MOVERIGHT|IN_MOVELEFT|IN_BACK)) ) - return; - // Require player walking back when applying '+use' on pushable - if ( !push && !(pevToucher->button & (IN_BACK)) ) - return; - } - else + if( pushablemode.value == -1 ) { // Don't push unless the player is pushing forward and NOT use (pull) - if( push && !( pevToucher->button & ( IN_FORWARD | IN_USE ) ) ) + if( push && !( pevToucher->button & ( IN_FORWARD | IN_USE ))) return; } + // g-cont. fix pushable acceleration bug (now implemented as cvar) + else if( pushablemode.value != 0 ) + { + // Allow player push when moving right, left and back too + if( push && !( pevToucher->button & ( IN_FORWARD | IN_MOVERIGHT | IN_MOVELEFT | IN_BACK ))) + return; + // Require player walking back when applying '+use' on pushable + if( !push && !( pevToucher->button & ( IN_BACK ))) + return; + } + // Don't push when +use pressed + else if( push && ( pevToucher->button & ( IN_USE ))) + return; playerTouch = 1; } @@ -964,30 +967,55 @@ void CPushable::Move( CBaseEntity *pOther, int push ) else factor = 0.25f; - // Spirit fix for pushable acceleration - if (pushablemode.value == 2) + if( pushablemode.value != 0 ) { - if (!push) - factor *= 0.5f; + pev->velocity.x += pevToucher->velocity.x * factor; + pev->velocity.y += pevToucher->velocity.y * factor; } + else + { + if( push ) + { + factor = 0.25f; + pev->velocity.x += pevToucher->velocity.x * factor; + pev->velocity.y += pevToucher->velocity.y * factor; + } + else + { + // fix for pushable acceleration + if( sv_pushable_fixed_tick_fudge.value >= 0 ) + factor *= ( sv_pushable_fixed_tick_fudge.value * gpGlobals->frametime ); - pev->velocity.x += pevToucher->velocity.x * factor; - pev->velocity.y += pevToucher->velocity.y * factor; + if( fabs( pev->velocity.x ) < fabs( pevToucher->velocity.x - pevToucher->velocity.x * factor )) + pev->velocity.x += pevToucher->velocity.x * factor; + if( fabs( pev->velocity.y ) < fabs( pevToucher->velocity.y - pevToucher->velocity.y * factor )) + pev->velocity.y += pevToucher->velocity.y * factor; + } + } float length = sqrt( pev->velocity.x * pev->velocity.x + pev->velocity.y * pev->velocity.y ); - if( push && ( length > MaxSpeed() ) ) + if( ( push && pushablemode.value != 0 ) + || pushablemode.value == 0 ) { - pev->velocity.x = (pev->velocity.x * MaxSpeed() / length ); - pev->velocity.y = (pev->velocity.y * MaxSpeed() / length ); + if( length > MaxSpeed()) + { + pev->velocity.x = ( pev->velocity.x * MaxSpeed() / length ); + pev->velocity.y = ( pev->velocity.y * MaxSpeed() / length ); + } } + if( playerTouch ) { - pevToucher->velocity.x = pev->velocity.x; - pevToucher->velocity.y = pev->velocity.y; + if( push || pushablemode.value != 0 ) + { + pevToucher->velocity.x = pev->velocity.x; + pevToucher->velocity.y = pev->velocity.y; + } + if( ( gpGlobals->time - m_soundTime ) > 0.7f ) { m_soundTime = gpGlobals->time; - if( length > 0 && FBitSet( pev->flags,FL_ONGROUND ) ) + if( length > 0 && FBitSet( pev->flags, FL_ONGROUND )) { m_lastSound = RANDOM_LONG( 0, 2 ); EMIT_SOUND( ENT( pev ), CHAN_WEAPON, m_soundNames[m_lastSound], 0.5f, ATTN_NORM ); diff --git a/dlls/game.cpp b/dlls/game.cpp index e64f0417..f2c74d6b 100644 --- a/dlls/game.cpp +++ b/dlls/game.cpp @@ -457,6 +457,8 @@ cvar_t sk_player_leg3 = { "sk_player_leg3","1" }; // END Cvars for Skill Level settings +cvar_t sv_pushable_fixed_tick_fudge = { "sv_pushable_fixed_tick_fudge", "15" }; + // Register your console variables here // This gets called one time when the game is initialied void GameDLLInit( void ) @@ -883,6 +885,8 @@ void GameDLLInit( void ) CVAR_REGISTER( &sk_player_leg3 ); // END REGISTER CVARS FOR SKILL LEVEL STUFF + CVAR_REGISTER( &sv_pushable_fixed_tick_fudge ); + SERVER_COMMAND( "exec skill.cfg\n" ); } diff --git a/dlls/game.h b/dlls/game.h index 5f7bee1c..069324bb 100644 --- a/dlls/game.h +++ b/dlls/game.h @@ -43,6 +43,7 @@ extern cvar_t teamoverride; extern cvar_t defaultteam; extern cvar_t allowmonsters; extern cvar_t bhopcap; +extern cvar_t sv_pushable_fixed_tick_fudge; // Engine Cvars extern cvar_t *g_psv_gravity; From ccbb5b763edfde7d03fa834a529d481133c4edc4 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 28 Jan 2024 09:14:47 +0000 Subject: [PATCH 095/127] Port func_vehicle implementation from cs16-client/regamedll_cs. (#428) --- cl_dll/ev_hldm.cpp | 60 +++ cl_dll/hl/hl_events.cpp | 2 + contrib/iZarif/premake5.lua | 1 + dlls/Android.mk | 1 + dlls/CMakeLists.txt | 1 + dlls/cbase.h | 1 + dlls/client.cpp | 2 + dlls/compile.bat | 1 + dlls/multiplay_gamerules.cpp | 11 + dlls/player.cpp | 83 ++- dlls/trains.h | 85 +++ dlls/vehicle.cpp | 996 +++++++++++++++++++++++++++++++++++ 12 files changed, 1229 insertions(+), 15 deletions(-) create mode 100644 dlls/vehicle.cpp diff --git a/cl_dll/ev_hldm.cpp b/cl_dll/ev_hldm.cpp index 9f5ede08..381ee25b 100644 --- a/cl_dll/ev_hldm.cpp +++ b/cl_dll/ev_hldm.cpp @@ -70,6 +70,7 @@ void EV_TripmineFire( struct event_args_s *args ); void EV_SnarkFire( struct event_args_s *args ); void EV_TrainPitchAdjust( struct event_args_s *args ); +void EV_VehiclePitchAdjust( event_args_t *args ); } #define VECTOR_CONE_1DEGREES Vector( 0.00873f, 0.00873f, 0.00873f ) @@ -1752,6 +1753,65 @@ void EV_TrainPitchAdjust( event_args_t *args ) } } +void EV_VehiclePitchAdjust( event_args_t *args ) +{ + int idx; + vec3_t origin; + + unsigned short us_params; + int noise; + float m_flVolume; + int pitch; + int stop; + + const char *pszSound; + + idx = args->entindex; + + VectorCopy( args->origin, origin ); + + us_params = (unsigned short)args->iparam1; + stop = args->bparam1; + + m_flVolume = (float)( us_params & 0x003f ) / 40.0f; + noise = (int)( ( ( us_params ) >> 12 ) & 0x0007 ); + pitch = (int)( 10.0f * (float)( ( us_params >> 6 ) & 0x003f ) ); + + switch( noise ) + { + case 1: + pszSound = "plats/vehicle1.wav"; + break; + case 2: + pszSound = "plats/vehicle2.wav"; + break; + case 3: + pszSound = "plats/vehicle3.wav"; + break; + case 4: + pszSound = "plats/vehicle4.wav"; + break; + case 5: + pszSound = "plats/vehicle6.wav"; + break; + case 6: + pszSound = "plats/vehicle7.wav"; + break; + default: + // no sound + return; + } + + if( stop ) + { + gEngfuncs.pEventAPI->EV_StopSound( idx, CHAN_STATIC, pszSound ); + } + else + { + gEngfuncs.pEventAPI->EV_PlaySound( idx, origin, CHAN_STATIC, pszSound, m_flVolume, ATTN_NORM, SND_CHANGE_PITCH, pitch ); + } +} + int EV_TFC_IsAllyTeam( int iTeam1, int iTeam2 ) { return 0; diff --git a/cl_dll/hl/hl_events.cpp b/cl_dll/hl/hl_events.cpp index c79279dd..e68fabf9 100644 --- a/cl_dll/hl/hl_events.cpp +++ b/cl_dll/hl/hl_events.cpp @@ -40,6 +40,7 @@ void EV_TripmineFire( struct event_args_s *args ); void EV_SnarkFire( struct event_args_s *args ); void EV_TrainPitchAdjust( struct event_args_s *args ); +void EV_VehiclePitchAdjust( event_args_t *args ); } /* @@ -76,4 +77,5 @@ void Game_HookEvents( void ) gEngfuncs.pfnHookEvent( "events/firehornet.sc", EV_HornetGunFire ); gEngfuncs.pfnHookEvent( "events/tripfire.sc", EV_TripmineFire ); gEngfuncs.pfnHookEvent( "events/snarkfire.sc", EV_SnarkFire ); + gEngfuncs.pfnHookEvent( "events/vehicle.sc", EV_VehiclePitchAdjust ); } diff --git a/contrib/iZarif/premake5.lua b/contrib/iZarif/premake5.lua index 91817da5..d959d9ad 100644 --- a/contrib/iZarif/premake5.lua +++ b/contrib/iZarif/premake5.lua @@ -219,6 +219,7 @@ files{"dlls/agrunt.cpp", "dlls/triggers.cpp", "dlls/turret.cpp", "dlls/util.cpp", +"dlls/vehicle.cpp", "dlls/weapons.cpp", "dlls/world.cpp", "dlls/xen.cpp", diff --git a/dlls/Android.mk b/dlls/Android.mk index 1dc79616..80934949 100644 --- a/dlls/Android.mk +++ b/dlls/Android.mk @@ -122,6 +122,7 @@ LOCAL_SRC_FILES := agrunt.cpp airtank.cpp \ tripmine.cpp \ turret.cpp \ util.cpp \ + vehicle.cpp \ weapons.cpp \ world.cpp \ xen.cpp \ diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index 7da665d9..9c518d39 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -144,6 +144,7 @@ set (SVDLL_SOURCES tripmine.cpp turret.cpp util.cpp + vehicle.cpp weapons.cpp world.cpp xen.cpp diff --git a/dlls/cbase.h b/dlls/cbase.h index d64c0f5c..88a161e0 100644 --- a/dlls/cbase.h +++ b/dlls/cbase.h @@ -103,6 +103,7 @@ typedef void(CBaseEntity::*USEPTR)( CBaseEntity *pActivator, CBaseEntity *pCalle #define CLASS_PLAYER_ALLY 11 #define CLASS_PLAYER_BIOWEAPON 12 // hornets and snarks.launched by players #define CLASS_ALIEN_BIOWEAPON 13 // hornets and snarks.launched by the alien menace +#define CLASS_VEHICLE 14 #define CLASS_BARNACLE 99 // special because no one pays attention to it, and it eats a wide cross-section of creatures. class CBaseEntity; diff --git a/dlls/client.cpp b/dlls/client.cpp index c27b22d1..3ce82cfb 100644 --- a/dlls/client.cpp +++ b/dlls/client.cpp @@ -893,6 +893,8 @@ void ClientPrecache( void ) PRECACHE_SOUND( "plats/train_use1.wav" ); // use a train + PRECACHE_SOUND( "plats/vehicle_ignition.wav" ); + PRECACHE_SOUND( "buttons/spark5.wav" ); // hit computer texture PRECACHE_SOUND( "buttons/spark6.wav" ); PRECACHE_SOUND( "debris/glass1.wav" ); diff --git a/dlls/compile.bat b/dlls/compile.bat index 4eba92c7..20b7cc66 100644 --- a/dlls/compile.bat +++ b/dlls/compile.bat @@ -103,6 +103,7 @@ set SOURCES=agrunt.cpp ^ tripmine.cpp ^ turret.cpp ^ util.cpp ^ + vehicle.cpp ^ weapons.cpp ^ world.cpp ^ xen.cpp ^ diff --git a/dlls/multiplay_gamerules.cpp b/dlls/multiplay_gamerules.cpp index b9f35d41..49e24ab3 100644 --- a/dlls/multiplay_gamerules.cpp +++ b/dlls/multiplay_gamerules.cpp @@ -30,6 +30,7 @@ #include "voice_gamemgr.h" #endif #include "hltv.h" +#include "trains.h" extern DLL_GLOBAL CGameRules *g_pGameRules; extern DLL_GLOBAL BOOL g_fGameOver; @@ -647,6 +648,16 @@ void CHalfLifeMultiplay::PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, CBaseEntity *ktmp = CBaseEntity::Instance( pKiller ); if( ktmp && (ktmp->Classify() == CLASS_PLAYER ) ) peKiller = (CBasePlayer*)ktmp; + else if( ktmp && ktmp->Classify() == CLASS_VEHICLE ) + { + CBasePlayer *pDriver = (CBasePlayer *)( (CFuncVehicle *)ktmp )->m_pDriver; + + if( pDriver != NULL ) + { + pKiller = pDriver->pev; + peKiller = (CBasePlayer *)pDriver; + } + } if( pVictim->pev == pKiller ) { diff --git a/dlls/player.cpp b/dlls/player.cpp index 0d984b12..7f5df559 100644 --- a/dlls/player.cpp +++ b/dlls/player.cpp @@ -1494,18 +1494,31 @@ void CBasePlayer::PlayerUse( void ) { m_afPhysicsFlags &= ~PFLAG_ONTRAIN; m_iTrain = TRAIN_NEW|TRAIN_OFF; + + CBaseEntity *pTrain = Instance( pev->groundentity ); + if( pTrain && pTrain->Classify() == CLASS_VEHICLE ) + { + ( (CFuncVehicle *)pTrain )->m_pDriver = NULL; + } return; } else { // Start controlling the train! CBaseEntity *pTrain = CBaseEntity::Instance( pev->groundentity ); - if( pTrain && !( pev->button & IN_JUMP ) && FBitSet( pev->flags, FL_ONGROUND ) && (pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE ) && pTrain->OnControls( pev ) ) + if( pTrain && !( pev->button & IN_JUMP ) && FBitSet( pev->flags, FL_ONGROUND ) && ( pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE ) && pTrain->OnControls( pev ) ) { m_afPhysicsFlags |= PFLAG_ONTRAIN; m_iTrain = TrainSpeed( (int)pTrain->pev->speed, pTrain->pev->impulse ); m_iTrain |= TRAIN_NEW; - EMIT_SOUND( ENT( pev ), CHAN_ITEM, "plats/train_use1.wav", 0.8, ATTN_NORM ); + + if( pTrain->Classify() == CLASS_VEHICLE ) + { + EMIT_SOUND( ENT( pev ), CHAN_ITEM, "plats/vehicle_ignition.wav", 0.8, ATTN_NORM ); + ( (CFuncVehicle *)pTrain )->m_pDriver = this; + } + else + EMIT_SOUND( ENT( pev ), CHAN_ITEM, "plats/train_use1.wav", 0.8, ATTN_NORM ); return; } } @@ -1619,9 +1632,17 @@ void CBasePlayer::Jump() // If you're standing on a conveyor, add it's velocity to yours (for momentum) entvars_t *pevGround = VARS( pev->groundentity ); - if( pevGround && ( pevGround->flags & FL_CONVEYOR ) ) + if( pevGround ) { - pev->velocity = pev->velocity + pev->basevelocity; + if( pevGround->flags & FL_CONVEYOR ) + { + pev->velocity = pev->velocity + pev->basevelocity; + } + + if( FClassnameIs( pevGround, "func_vehicle" )) + { + pev->velocity = pevGround->velocity + pev->velocity; + } } } @@ -1886,30 +1907,62 @@ void CBasePlayer::PreThink( void ) //ALERT( at_error, "In train mode with no train!\n" ); m_afPhysicsFlags &= ~PFLAG_ONTRAIN; m_iTrain = TRAIN_NEW|TRAIN_OFF; + if( pTrain ) + ( (CFuncVehicle *)pTrain )->m_pDriver = NULL; return; } } - else if( !FBitSet( pev->flags, FL_ONGROUND ) || FBitSet( pTrain->pev->spawnflags, SF_TRACKTRAIN_NOCONTROL ) || ( pev->button & ( IN_MOVELEFT | IN_MOVERIGHT ) ) ) + else if( !FBitSet( pev->flags, FL_ONGROUND ) || FBitSet( pTrain->pev->spawnflags, SF_TRACKTRAIN_NOCONTROL ) + || ( ( pev->button & ( IN_MOVELEFT | IN_MOVERIGHT )) && pTrain->Classify() != CLASS_VEHICLE )) { // Turn off the train if you jump, strafe, or the train controls go dead m_afPhysicsFlags &= ~PFLAG_ONTRAIN; m_iTrain = TRAIN_NEW | TRAIN_OFF; + ( (CFuncVehicle *)pTrain )->m_pDriver = NULL; return; } pev->velocity = g_vecZero; vel = 0; - if( m_afButtonPressed & IN_FORWARD ) - { - vel = 1; - pTrain->Use( this, this, USE_SET, (float)vel ); - } - else if( m_afButtonPressed & IN_BACK ) - { - vel = -1; - pTrain->Use( this, this, USE_SET, (float)vel ); - } + if( pTrain->Classify() == CLASS_VEHICLE ) + { + if( pev->button & IN_FORWARD ) + { + vel = 1; + pTrain->Use( this, this, USE_SET, vel ); + } + + if( pev->button & IN_BACK ) + { + vel = -1; + pTrain->Use( this, this, USE_SET, vel ); + } + + if( pev->button & IN_MOVELEFT ) + { + vel = 20; + pTrain->Use( this, this, USE_SET, vel ); + } + if( pev->button & IN_MOVERIGHT ) + { + vel = 30; + pTrain->Use( this, this, USE_SET, vel ); + } + } + else + { + if( m_afButtonPressed & IN_FORWARD ) + { + vel = 1; + pTrain->Use( this, this, USE_SET, vel ); + } + else if( m_afButtonPressed & IN_BACK ) + { + vel = -1; + pTrain->Use( this, this, USE_SET, vel ); + } + } iGearId = TrainSpeed( pTrain->pev->speed, pTrain->pev->impulse ); if( iGearId != ( m_iTrain & 0x0F ) ) // Vit_amiN: speed changed diff --git a/dlls/trains.h b/dlls/trains.h index 046dbc24..652ecbc4 100644 --- a/dlls/trains.h +++ b/dlls/trains.h @@ -122,4 +122,89 @@ public: private: unsigned short m_usAdjustPitch; }; + +class CFuncVehicle: public CBaseEntity +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual void Restart(); + virtual void KeyValue( KeyValueData *pkvd ); + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + virtual int ObjectCaps() { return ( CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION ) | FCAP_DIRECTIONAL_USE; } + virtual int Classify(); + virtual void OverrideReset(); + virtual BOOL OnControls( entvars_t *pev ); + virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + virtual void Blocked( CBaseEntity *pOther ); + +public: + void EXPORT Next(); + void EXPORT Find(); + void EXPORT NearestPath(); + void EXPORT DeadEnd(); + + void NextThink( float thinkTime, BOOL alwaysThink ); + void CollisionDetection(); + void TerrainFollowing(); + void CheckTurning(); + + void SetTrack( CPathTrack *track ) { m_ppath = track->Nearest( pev->origin ); } + void SetControls( entvars_t *pevControls ); + + void StopSound(); + void UpdateSound(); + +public: + static CFuncVehicle *Instance( edict_t *pent ); + static TYPEDESCRIPTION m_SaveData[12]; + + CPathTrack *m_ppath; + float m_length; + float m_width; + float m_height; + float m_speed; + float m_dir; + float m_startSpeed; + Vector m_controlMins; + Vector m_controlMaxs; + int m_soundPlaying; + int m_sounds; + int m_acceleration; + float m_flVolume; + float m_flBank; + float m_oldSpeed; + int m_iTurnAngle; + float m_flSteeringWheelDecay; + float m_flAcceleratorDecay; + float m_flTurnStartTime; + float m_flLaunchTime; + float m_flLastNormalZ; + float m_flCanTurnNow; + float m_flUpdateSound; + Vector m_vFrontLeft; + Vector m_vFront; + Vector m_vFrontRight; + Vector m_vBackLeft; + Vector m_vBack; + Vector m_vBackRight; + Vector m_vSurfaceNormal; + Vector m_vVehicleDirection; + CBaseEntity *m_pDriver; + +private: + unsigned short m_usAdjustPitch; +}; + +class CFuncVehicleControls: public CBaseEntity +{ +public: + virtual void Spawn(); + virtual int ObjectCaps() { return CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + +public: + void EXPORT Find(); +}; + #endif diff --git a/dlls/vehicle.cpp b/dlls/vehicle.cpp new file mode 100644 index 00000000..66e5cc2f --- /dev/null +++ b/dlls/vehicle.cpp @@ -0,0 +1,996 @@ +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "trains.h" +#include "saverestore.h" + +#define VEHICLE_SPEED0_ACCELERATION 0.005000000000000000 +#define VEHICLE_SPEED1_ACCELERATION 0.002142857142857143 +#define VEHICLE_SPEED2_ACCELERATION 0.003333333333333334 +#define VEHICLE_SPEED3_ACCELERATION 0.004166666666666667 +#define VEHICLE_SPEED4_ACCELERATION 0.004000000000000000 +#define VEHICLE_SPEED5_ACCELERATION 0.003800000000000000 +#define VEHICLE_SPEED6_ACCELERATION 0.004500000000000000 +#define VEHICLE_SPEED7_ACCELERATION 0.004250000000000000 +#define VEHICLE_SPEED8_ACCELERATION 0.002666666666666667 +#define VEHICLE_SPEED9_ACCELERATION 0.002285714285714286 +#define VEHICLE_SPEED10_ACCELERATION 0.001875000000000000 +#define VEHICLE_SPEED11_ACCELERATION 0.001444444444444444 +#define VEHICLE_SPEED12_ACCELERATION 0.001200000000000000 +#define VEHICLE_SPEED13_ACCELERATION 0.000916666666666666 + +#define VEHICLE_STARTPITCH 60 +#define VEHICLE_MAXPITCH 200 +#define VEHICLE_MAXSPEED 1500 + +TYPEDESCRIPTION CFuncVehicle::m_SaveData[] = +{ + DEFINE_FIELD( CFuncVehicle, m_ppath, FIELD_CLASSPTR ), + DEFINE_FIELD( CFuncVehicle, m_length, FIELD_FLOAT ), + DEFINE_FIELD( CFuncVehicle, m_height, FIELD_FLOAT ), + DEFINE_FIELD( CFuncVehicle, m_speed, FIELD_FLOAT ), + DEFINE_FIELD( CFuncVehicle, m_dir, FIELD_FLOAT ), + DEFINE_FIELD( CFuncVehicle, m_startSpeed, FIELD_FLOAT ), + DEFINE_FIELD( CFuncVehicle, m_controlMins, FIELD_VECTOR ), + DEFINE_FIELD( CFuncVehicle, m_controlMaxs, FIELD_VECTOR ), + DEFINE_FIELD( CFuncVehicle, m_sounds, FIELD_INTEGER ), + DEFINE_FIELD( CFuncVehicle, m_flVolume, FIELD_FLOAT ), + DEFINE_FIELD( CFuncVehicle, m_flBank, FIELD_FLOAT ), + DEFINE_FIELD( CFuncVehicle, m_oldSpeed, FIELD_FLOAT ), +}; + +static float Fix2( float angle ) +{ + while( angle < 0 ) + angle += 360; + + while( angle > 360 ) + angle -= 360; + + return angle; +} + +static void FixupAngles2( Vector &v ) +{ + v.x = Fix2( v.x ); + v.y = Fix2( v.y ); + v.z = Fix2( v.z ); +} + +IMPLEMENT_SAVERESTORE( CFuncVehicle, CBaseEntity ) + +LINK_ENTITY_TO_CLASS( func_vehicle, CFuncVehicle ) + +void CFuncVehicle::KeyValue( KeyValueData *pkvd ) +{ + if( FStrEq( pkvd->szKeyName, "length" )) + { + m_length = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if( FStrEq( pkvd->szKeyName, "width" )) + { + m_width = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if( FStrEq( pkvd->szKeyName, "height" )) + { + m_height = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if( FStrEq( pkvd->szKeyName, "startspeed" )) + { + m_startSpeed = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if( FStrEq( pkvd->szKeyName, "sounds" )) + { + m_sounds = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if( FStrEq( pkvd->szKeyName, "volume" )) + { + m_flVolume = (float)atoi( pkvd->szValue ); + m_flVolume *= 0.1f; + + pkvd->fHandled = TRUE; + } + else if( FStrEq( pkvd->szKeyName, "bank" )) + { + m_flBank = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if( FStrEq( pkvd->szKeyName, "acceleration" )) + { + m_acceleration = atoi( pkvd->szValue ); + + if( m_acceleration < 1 ) + m_acceleration = 1; + + else if( m_acceleration > 10 ) + m_acceleration = 10; + + pkvd->fHandled = TRUE; + } + else + CBaseEntity::KeyValue( pkvd ); +} + +void CFuncVehicle::NextThink( float thinkTime, BOOL alwaysThink ) +{ + if( alwaysThink ) + pev->flags |= FL_ALWAYSTHINK; + else + pev->flags &= ~FL_ALWAYSTHINK; + + pev->nextthink = thinkTime; +} + +void CFuncVehicle::Blocked( CBaseEntity *pOther ) +{ + entvars_t *pevOther = pOther->pev; + + if( ( pevOther->flags & FL_ONGROUND ) && VARS( pevOther->groundentity ) == pev ) + { + pevOther->velocity = pev->velocity; + return; + } + + pevOther->velocity = ( pevOther->origin - pev->origin ).Normalize() * pev->dmg; + pevOther->velocity.z += 300; + pev->velocity = pev->velocity * 0.85f; + + ALERT( at_aiconsole, "TRAIN(%s): Blocked by %s (dmg:%.2f)\n", STRING( pev->targetname ), STRING( pOther->pev->classname ), pev->dmg ); + UTIL_MakeVectors( pev->angles ); + + Vector forward, right, vOrigin; + Vector vFrontLeft = ( gpGlobals->v_forward * -1 ) * ( m_length * 0.5f ); + Vector vFrontRight = ( gpGlobals->v_right * -1 ) * ( m_width * 0.5f ); + + Vector vBackLeft = pev->origin + vFrontLeft - vFrontRight; + Vector vBackRight = pev->origin - vFrontLeft + vFrontRight; + + float minx = Q_min( vBackLeft.x, vBackRight.x ); + float miny = Q_min( vBackLeft.y, vBackRight.y ); + float maxx = Q_max( vBackLeft.x, vBackRight.x ); + float maxy = Q_max( vBackLeft.y, vBackRight.y ); + + float minz = pev->origin.z; + float maxz = pev->origin.z + ( 2 * abs( (int)( pev->mins.z - pev->maxs.z ))); + + if ( pOther->pev->origin.x < minx + || pOther->pev->origin.x > maxx + || pOther->pev->origin.y < miny + || pOther->pev->origin.y > maxy + || pOther->pev->origin.z < pev->origin.z + || pOther->pev->origin.z > maxz ) + { + pOther->TakeDamage( pev, pev, 150, DMG_CRUSH ); + } +} + +void CFuncVehicle::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + float delta = value; + + if( useType != USE_SET ) + { + if( ShouldToggle( useType, pev->speed != 0 )) + { + if( pev->speed == 0 ) + { + pev->speed = m_dir * m_speed; + Next(); + } + else + { + pev->speed = 0; + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + + StopSound(); + SetThink( NULL ); + } + } + + return; + } + + if( delta < 10 ) + { + if( delta < 0 ) + { + if( pev->speed > 145 ) + { + StopSound(); + } + } + + float flSpeedRatio = delta; + + if( delta > 0 ) + { + flSpeedRatio = (float)( pev->speed / m_speed ); + + if( pev->speed < 0 ) flSpeedRatio = m_acceleration * 0.0005 + flSpeedRatio + VEHICLE_SPEED0_ACCELERATION; + else if( pev->speed < 10 ) flSpeedRatio = m_acceleration * 0.0006 + flSpeedRatio + VEHICLE_SPEED1_ACCELERATION; + else if( pev->speed < 20 ) flSpeedRatio = m_acceleration * 0.0007 + flSpeedRatio + VEHICLE_SPEED2_ACCELERATION; + else if( pev->speed < 30 ) flSpeedRatio = m_acceleration * 0.0007 + flSpeedRatio + VEHICLE_SPEED3_ACCELERATION; + else if( pev->speed < 45 ) flSpeedRatio = m_acceleration * 0.0007 + flSpeedRatio + VEHICLE_SPEED4_ACCELERATION; + else if( pev->speed < 60 ) flSpeedRatio = m_acceleration * 0.0008 + flSpeedRatio + VEHICLE_SPEED5_ACCELERATION; + else if( pev->speed < 80 ) flSpeedRatio = m_acceleration * 0.0008 + flSpeedRatio + VEHICLE_SPEED6_ACCELERATION; + else if( pev->speed < 100 ) flSpeedRatio = m_acceleration * 0.0009 + flSpeedRatio + VEHICLE_SPEED7_ACCELERATION; + else if( pev->speed < 150 ) flSpeedRatio = m_acceleration * 0.0008 + flSpeedRatio + VEHICLE_SPEED8_ACCELERATION; + else if( pev->speed < 225 ) flSpeedRatio = m_acceleration * 0.0007 + flSpeedRatio + VEHICLE_SPEED9_ACCELERATION; + else if( pev->speed < 300 ) flSpeedRatio = m_acceleration * 0.0006 + flSpeedRatio + VEHICLE_SPEED10_ACCELERATION; + else if( pev->speed < 400 ) flSpeedRatio = m_acceleration * 0.0005 + flSpeedRatio + VEHICLE_SPEED11_ACCELERATION; + else if( pev->speed < 550 ) flSpeedRatio = m_acceleration * 0.0005 + flSpeedRatio + VEHICLE_SPEED12_ACCELERATION; + else if( pev->speed < 800 ) flSpeedRatio = m_acceleration * 0.0005 + flSpeedRatio + VEHICLE_SPEED13_ACCELERATION; + } + else if( delta < 0 ) + { + flSpeedRatio = pev->speed / m_speed; + + // TODO: fix float for test demo + if( flSpeedRatio > 0 ) flSpeedRatio = (float)flSpeedRatio - 0.0125f; + else if( flSpeedRatio <= 0 && flSpeedRatio > -0.05f ) flSpeedRatio = (float)flSpeedRatio - 0.0075f; + else if( flSpeedRatio <= 0.05f && flSpeedRatio > -0.1f ) flSpeedRatio = (float)flSpeedRatio - 0.01f; + else if( flSpeedRatio <= 0.15f && flSpeedRatio > -0.15f ) flSpeedRatio = (float)flSpeedRatio - 0.0125f; + else if( flSpeedRatio <= 0.15f && flSpeedRatio > -0.22f ) flSpeedRatio = (float)flSpeedRatio - 0.01375f; + else if( flSpeedRatio <= 0.22f && flSpeedRatio > -0.3f ) flSpeedRatio = (float)flSpeedRatio - 0.0175f; + else if( flSpeedRatio <= 0.3f ) flSpeedRatio = (float)flSpeedRatio - 0.0125f; + } + + if( flSpeedRatio > 1 ) + { + flSpeedRatio = 1; + } + else if( flSpeedRatio < -0.35f ) + { + flSpeedRatio = -0.35f; + } + + pev->speed = flSpeedRatio * m_speed; + Next(); + m_flAcceleratorDecay = gpGlobals->time + 0.25f; + } + else if( m_flCanTurnNow < gpGlobals->time ) + { + if( delta == 20 ) + { + m_iTurnAngle++; + m_flSteeringWheelDecay = gpGlobals->time + 0.075f; + + if (m_iTurnAngle > 8) + { + m_iTurnAngle = 8; + } + } + else if( delta == 30 ) + { + m_iTurnAngle--; + m_flSteeringWheelDecay = gpGlobals->time + 0.075f; + + if( m_iTurnAngle < -8 ) + { + m_iTurnAngle = -8; + } + } + + m_flCanTurnNow = gpGlobals->time + 0.05f; + } +} + +void CFuncVehicle::StopSound() +{ + if( m_soundPlaying && pev->noise ) + { + unsigned short us_sound = ( (unsigned short)m_sounds & 0x0007 ) << 12; + unsigned short us_encode = us_sound; + + PLAYBACK_EVENT_FULL( FEV_RELIABLE | FEV_UPDATE, edict(), m_usAdjustPitch, 0, g_vecZero, g_vecZero, 0, 0, us_encode, 0, 1, 0 ); + } + + m_soundPlaying = 0; +} + +void CFuncVehicle::UpdateSound() +{ + if( !pev->noise ) + return; + + float flpitch = VEHICLE_STARTPITCH + ( abs( (int)pev->speed ) * ( VEHICLE_MAXPITCH - VEHICLE_STARTPITCH ) / VEHICLE_MAXSPEED ); + + if( flpitch > 200 ) + flpitch = 200; + + if( !m_soundPlaying ) + { + if( m_sounds < 5 ) + { + EMIT_SOUND_DYN( ENT(pev), CHAN_ITEM, "plats/vehicle_brake1.wav", m_flVolume, ATTN_NORM, 0, PITCH_NORM ); + } + + EMIT_SOUND_DYN( ENT( pev ), CHAN_STATIC, STRING( pev->noise ), m_flVolume, ATTN_NORM, 0, (int)flpitch ); + m_soundPlaying = 1; + } + else + { + unsigned short us_sound = ( (unsigned short)( m_sounds ) & 0x0007 ) << 12; + unsigned short us_pitch = ( (unsigned short)( flpitch / 10.0 ) & 0x003F ) << 6; + unsigned short us_volume = ( (unsigned short)( m_flVolume * 40 ) & 0x003F ); + unsigned short us_encode = us_sound | us_pitch | us_volume; + + PLAYBACK_EVENT_FULL( FEV_UPDATE, edict(), m_usAdjustPitch, 0.0, g_vecZero, g_vecZero, 0.0, 0.0, us_encode, 0, 0, 0 ); + } +} + +void CFuncVehicle::CheckTurning() +{ + float maxspeed; + TraceResult tr; + bool bTurnIntoWall = false; + + if( m_iTurnAngle < 0 ) + { + if( pev->speed > 0 ) + { + UTIL_TraceLine( m_vFrontRight, m_vFrontRight - ( gpGlobals->v_right * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + } + else if( pev->speed < 0 ) + { + UTIL_TraceLine( m_vBackLeft, m_vBackLeft + ( gpGlobals->v_right * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + } + + if( tr.flFraction != 1.0f ) + { + m_iTurnAngle = 1; + } + } + else if( m_iTurnAngle > 0 ) + { + if( pev->speed > 0 ) + { + UTIL_TraceLine( m_vFrontLeft, m_vFrontLeft + ( gpGlobals->v_right * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + } + else if( pev->speed < 0 ) + { + UTIL_TraceLine( m_vBackRight, m_vBackRight - ( gpGlobals->v_right * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + } + + if( tr.flFraction != 1.0f ) + { + m_iTurnAngle = -1; + } + } + + if( pev->speed > 0 ) + { + int iCountTurn = abs( m_iTurnAngle ); + + if( iCountTurn > 4 ) + { + if ( m_flTurnStartTime != -1 ) + { + float flTurnTime = gpGlobals->time - m_flTurnStartTime; + + if( flTurnTime >= 0 ) maxspeed = m_speed * 0.98f; + else if( flTurnTime > 0.3f ) maxspeed = m_speed * 0.95f; + else if( flTurnTime > 0.6f ) maxspeed = m_speed * 0.9f; + else if( flTurnTime > 0.8f ) maxspeed = m_speed * 0.8f; + else if( flTurnTime > 1 ) maxspeed = m_speed * 0.7f; + else if( flTurnTime > 1.2f ) maxspeed = m_speed * 0.5f; + else maxspeed = flTurnTime; + } + else + { + m_flTurnStartTime = gpGlobals->time; + maxspeed = m_speed; + } + } + else + { + m_flTurnStartTime = -1; + + if( iCountTurn > 2 ) + maxspeed = m_speed * 0.9f; + else + maxspeed = m_speed; + } + + if( maxspeed < pev->speed ) + { + pev->speed -= m_speed * 0.1f; + } + } +} + +void CFuncVehicle::CollisionDetection() +{ + TraceResult tr; + bool bHitSomething = false; + + if( pev->speed < 0 ) + { + UTIL_TraceLine( m_vBackLeft, m_vBackLeft + ( gpGlobals->v_forward * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + + if( tr.flFraction == 1.0f ) + { + UTIL_TraceLine( m_vBackRight, m_vBackRight + ( gpGlobals->v_forward * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + + if( tr.flFraction == 1.0f ) + { + UTIL_TraceLine( m_vBack, m_vBack + ( gpGlobals->v_forward * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + + if( tr.flFraction == 1.0f ) + { + return; + } + } + + if( DotProduct( gpGlobals->v_forward, tr.vecPlaneNormal * -1.0f ) < 0.7f && tr.vecPlaneNormal.z < 0.1f ) + { + m_vSurfaceNormal = tr.vecPlaneNormal; + m_vSurfaceNormal.z = 0; + + pev->speed *= 0.99f; + } + else if( tr.vecPlaneNormal.z < 0.65f || tr.fStartSolid ) + { + pev->speed *= -1.0f; + } + else + { + m_vSurfaceNormal = tr.vecPlaneNormal; + } + } + else + { + if( DotProduct( gpGlobals->v_forward, tr.vecPlaneNormal * -1.0f ) < 0.7f && tr.vecPlaneNormal.z < 0.1f ) + { + m_vSurfaceNormal = tr.vecPlaneNormal; + m_vSurfaceNormal.z = 0; + + pev->speed *= 0.99f; + } + else if( tr.vecPlaneNormal[2] < 0.65f || tr.fStartSolid ) + { + pev->speed *= -1.0f; + } + else + { + m_vSurfaceNormal = tr.vecPlaneNormal; + } + + CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit ); + + if( pHit && pHit->Classify() == CLASS_VEHICLE ) + { + bHitSomething = true; + ALERT( at_console, "I hit another vehicle\n" ); + } + } + } + else if( pev->speed > 0 ) + { + UTIL_TraceLine( m_vFrontLeft, m_vFrontLeft - ( gpGlobals->v_forward * 16.0f ), dont_ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + + if( tr.flFraction == 1.0f ) + { + UTIL_TraceLine( m_vFrontRight, m_vFrontRight - ( gpGlobals->v_forward * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + + if( tr.flFraction == 1.0f ) + { + UTIL_TraceLine( m_vFront, m_vFront - ( gpGlobals->v_forward * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + + if( tr.flFraction == 1.0f ) + { + return; + } + } + } + + if( DotProduct( gpGlobals->v_forward, tr.vecPlaneNormal * -1.0f ) > -0.7f && tr.vecPlaneNormal.z < 0.1f ) + { + m_vSurfaceNormal = tr.vecPlaneNormal; + m_vSurfaceNormal.z = 0; + + pev->speed *= 0.99f; + } + else if( tr.vecPlaneNormal.z < 0.65f || tr.fStartSolid ) + { + pev->speed *= -1.0f; + } + else + { + m_vSurfaceNormal = tr.vecPlaneNormal; + } + } +} + +void CFuncVehicle::TerrainFollowing() +{ + TraceResult tr; + UTIL_TraceLine( pev->origin, pev->origin + Vector( 0, 0, ( m_height + 48 ) * -1 ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr ); + + if( tr.flFraction != 1.0f ) + { + m_vSurfaceNormal = tr.vecPlaneNormal; + } + else if( tr.fInWater ) + { + m_vSurfaceNormal = Vector( 0, 0, 1 ); + } +} + +void CFuncVehicle::Next() +{ + Vector vGravityVector, forward, right, up; + float time = 0.1f; + + vGravityVector = g_vecZero; + UTIL_MakeVectors( pev->angles ); + + forward = ( gpGlobals->v_forward * -1 ) * ( m_length * 0.5f ); + right = ( gpGlobals->v_right * -1 ) * ( m_width * 0.5f ); + up = gpGlobals->v_up * 16; + + m_vFrontLeft = pev->origin + forward - right + up; + m_vFrontRight = pev->origin + forward + right + up; + m_vFront = pev->origin + forward + up; + m_vBackLeft = pev->origin - forward - right + up; + m_vBackRight = pev->origin - forward + right + up; + m_vBack = pev->origin - forward + up; + m_vSurfaceNormal = g_vecZero; + + CheckTurning(); + + if( m_flSteeringWheelDecay < gpGlobals->time ) + { + m_flSteeringWheelDecay = gpGlobals->time + 0.1f; + + if( m_iTurnAngle < 0 ) + m_iTurnAngle++; + + else if( m_iTurnAngle > 0 ) + m_iTurnAngle--; + } + + if( m_flAcceleratorDecay < gpGlobals->time ) + { + m_flAcceleratorDecay = gpGlobals->time + 0.1f; + + if( pev->speed < 0 ) + { + pev->speed += 20; + + if( pev->speed > 0 ) + pev->speed = 0; + } + else if( pev->speed > 0 ) + { + pev->speed -= 20; + + if( pev->speed < 0 ) + pev->speed = 0; + } + } + + if( pev->speed == 0 ) + { + m_iTurnAngle = 0; + pev->avelocity = g_vecZero; + pev->velocity = g_vecZero; + + SetThink( &CFuncVehicle::Next ); + NextThink( pev->ltime + time, TRUE ); + return; + } + + TerrainFollowing(); + CollisionDetection(); + + Vector temp; + if( m_vSurfaceNormal != g_vecZero ) + { + Vector vTargetAngle, vAngle; + + float vx; + float vy; + + m_vVehicleDirection = CrossProduct( m_vSurfaceNormal, gpGlobals->v_forward ); + m_vVehicleDirection = CrossProduct( m_vSurfaceNormal, m_vVehicleDirection ); + + vTargetAngle = UTIL_VecToAngles( m_vVehicleDirection ); + vAngle = pev->angles; + + vTargetAngle.y += 180; + + if( m_iTurnAngle != 0 ) + { + vTargetAngle.y += m_iTurnAngle; + } + + FixupAngles2( vTargetAngle ); + FixupAngles2( vAngle ); + + vx = UTIL_AngleDistance( vTargetAngle.x, vAngle.x ); + vy = UTIL_AngleDistance( vTargetAngle.y, vAngle.y ); + + if( vx > 10 ) + vx = 10; + else if( vx < -10 ) + vx = -10; + + if( vy > 10 ) + vy = 10; + else if( vy < -10 ) + vy = -10; + + pev->avelocity.y = (int)( vy * 10 ); + pev->avelocity.x = (int)( vx * 10 ); + + m_flLaunchTime = -1; + m_flLastNormalZ = m_vSurfaceNormal.z; + } + else + { + if( m_flLaunchTime != -1 ) + { + vGravityVector.x = 0; + vGravityVector.y = 0; + vGravityVector.z = ( gpGlobals->time - m_flLaunchTime ) * -35; + + if( vGravityVector.z < -400 ) + { + vGravityVector.z = -400; + } + } + else + { + m_flLaunchTime = gpGlobals->time; + vGravityVector = Vector( 0, 0, 0 ); + pev->velocity = pev->velocity * 1.5f; + } + + m_vVehicleDirection = gpGlobals->v_forward * -1; + } + + UTIL_VecToAngles( m_vVehicleDirection ); + + if( m_flUpdateSound < gpGlobals->time ) + { + UpdateSound(); + m_flUpdateSound = gpGlobals->time + 1.0f; + } + + if( m_vSurfaceNormal != g_vecZero ) + { + pev->velocity = m_vVehicleDirection.Normalize() * pev->speed; + } + else + { + pev->velocity = pev->velocity + vGravityVector; + } + + SetThink( &CFuncVehicle::Next ); + NextThink( pev->ltime + time, TRUE ); +} + +void CFuncVehicle::DeadEnd() +{ + CPathTrack *pTrack = m_ppath; + ALERT( at_aiconsole, "TRAIN(%s): Dead end ", STRING( pev->targetname )); + + if( pTrack != NULL ) + { + CPathTrack *pNext; + + if( m_oldSpeed < 0 ) + { + do + { + pNext = pTrack->ValidPath( pTrack->GetPrevious(), TRUE ); + + if( pNext != NULL ) + { + pTrack = pNext; + } + } + while( pNext != NULL ); + } + else + { + do + { + pNext = pTrack->ValidPath( pTrack->GetNext(), TRUE ); + + if( pNext != NULL ) + { + pTrack = pNext; + } + } + while( pNext != NULL ); + } + } + + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + + if( pTrack != NULL ) + { + ALERT( at_aiconsole, "at %s\n", STRING( pTrack->pev->targetname )); + + if( !FStringNull( pTrack->pev->netname )) + { + FireTargets( STRING( pTrack->pev->netname ), this, this, USE_TOGGLE, 0 ); + } + } + else + ALERT( at_aiconsole, "\n" ); +} + +void CFuncVehicle::SetControls(entvars_t *pevControls) +{ + Vector offset = pevControls->origin - pev->oldorigin; + m_controlMins = pevControls->mins + offset; + m_controlMaxs = pevControls->maxs + offset; +} + +BOOL CFuncVehicle::OnControls(entvars_t *pevTest) +{ + if( pev->spawnflags & SF_TRACKTRAIN_NOCONTROL ) + return FALSE; + + Vector offset = pevTest->origin - pev->origin; + + UTIL_MakeVectors( pev->angles ); + + Vector local; + local.x = DotProduct( offset, gpGlobals->v_forward ); + local.y = -DotProduct( offset, gpGlobals->v_right ); + local.z = DotProduct( offset, gpGlobals->v_up ); + + return ( local.x >= m_controlMins.x && local.y >= m_controlMins.y && local.z >= m_controlMins.z + && local.x <= m_controlMaxs.x && local.y <= m_controlMaxs.y && local.z <= m_controlMaxs.z ); +} + +void CFuncVehicle::Find() +{ + m_ppath = CPathTrack::Instance( FIND_ENTITY_BY_TARGETNAME( NULL, STRING( pev->target ))); + + if( !m_ppath ) + return; + + entvars_t *pevTarget = m_ppath->pev; + + if( !FClassnameIs( pevTarget, "path_track" )) + { + ALERT( at_error, "func_track_train must be on a path of path_track\n" ); + m_ppath = NULL; + return; + } + + Vector nextPos = pevTarget->origin; + nextPos.z += m_height; + + Vector look = nextPos; + look.z -= m_height; + m_ppath->LookAhead( &look, m_length, 0 ); + look.z += m_height; + + pev->angles = UTIL_VecToAngles( look - nextPos ); + pev->angles.y += 180; + + if( pev->spawnflags & SF_TRACKTRAIN_NOPITCH ) + { + pev->angles.x = 0; + } + + UTIL_SetOrigin( pev, nextPos ); + NextThink( pev->ltime + 0.1f, FALSE ); + SetThink( &CFuncVehicle::Next ); + pev->speed = m_startSpeed; + UpdateSound(); +} + +void CFuncVehicle::NearestPath() +{ + CBaseEntity *pTrack = NULL; + CBaseEntity *pNearest = NULL; + float dist; + float closest = 1024; + + while( ( pTrack = UTIL_FindEntityInSphere( pTrack, pev->origin, 1024 )) != NULL ) + { + if( !( pTrack->pev->flags & ( FL_CLIENT | FL_MONSTER )) && FClassnameIs( pTrack->pev, "path_track" )) + { + dist = ( pev->origin - pTrack->pev->origin ).Length(); + + if( dist < closest ) + { + closest = dist; + pNearest = pTrack; + } + } + } + + if( !pNearest ) + { + ALERT( at_console, "Can't find a nearby track !!!\n" ); + SetThink( NULL ); + return; + } + + ALERT( at_aiconsole, "TRAIN: %s, Nearest track is %s\n", STRING( pev->targetname ), STRING( pNearest->pev->targetname )); + pTrack = ( (CPathTrack *)pNearest )->GetNext(); + + if( pTrack != NULL ) + { + if( ( pev->origin - pTrack->pev->origin ).Length() < ( pev->origin - pNearest->pev->origin ).Length()) + { + pNearest = pTrack; + } + } + + m_ppath = (CPathTrack *)pNearest; + if( pev->speed != 0 ) + { + NextThink( pev->ltime + 0.1f, FALSE ); + SetThink( &CFuncVehicle::Next ); + } +} + +void CFuncVehicle::OverrideReset() +{ + NextThink( pev->ltime + 0.1f, FALSE ); + SetThink( &CFuncVehicle::NearestPath ); +} + +CFuncVehicle *CFuncVehicle::Instance(edict_t *pent) +{ + if( FClassnameIs( pent, "func_vehicle" )) + { + return (CFuncVehicle *)GET_PRIVATE( pent ); + } + + return NULL; +} + +int CFuncVehicle::Classify() +{ + return CLASS_VEHICLE; +} + +void CFuncVehicle::Spawn() +{ + if( pev->speed == 0 ) + m_speed = 165; + else + m_speed = pev->speed; + + if( !m_sounds ) + m_sounds = 3; + + ALERT( at_console, "M_speed = %f\n", m_speed ); + + pev->speed = 0; + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + + pev->impulse = (int)m_speed; + m_acceleration = 5; + + m_dir = 1; + m_flTurnStartTime = -1; + + if( FStringNull( pev->target )) + { + ALERT( at_console, "Vehicle with no target" ); + } + + if( pev->spawnflags & SF_TRACKTRAIN_PASSABLE ) + pev->solid = SOLID_NOT; + else + pev->solid = SOLID_BSP; + + pev->movetype = MOVETYPE_PUSH; + + SET_MODEL( ENT( pev ), STRING(pev->model )); + UTIL_SetSize( pev, pev->mins, pev->maxs ); + UTIL_SetOrigin( pev, pev->origin ); + + pev->oldorigin = pev->origin; + + m_controlMins = pev->mins; + m_controlMaxs = pev->maxs; + m_controlMaxs.z += 72; + + NextThink( pev->ltime + 0.1f, FALSE ); + SetThink( &CFuncVehicle::Find ); + Precache(); +} + +void CFuncVehicle::Restart() +{ + ALERT( at_console, "M_speed = %f\n", m_speed ); + + pev->speed = 0; + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + + pev->impulse = (int)m_speed; + m_flTurnStartTime = -1; + m_flUpdateSound = -1; + m_dir = 1; + m_pDriver = NULL; + + if( FStringNull( pev->target )) + { + ALERT( at_console, "Vehicle with no target" ); + } + + UTIL_SetOrigin( pev, pev->oldorigin ); + STOP_SOUND( ENT( pev ), CHAN_STATIC, STRING( pev->noise )); + + NextThink( pev->ltime + 0.1f, FALSE ); + SetThink( &CFuncVehicle::Find ); +} + +void CFuncVehicle::Precache() +{ + if( m_flVolume == 0.0f ) + m_flVolume = 1.0f; + + switch( m_sounds ) + { + case 1: PRECACHE_SOUND( "plats/vehicle1.wav" );pev->noise = MAKE_STRING( "plats/vehicle1.wav" ); break; + case 2: PRECACHE_SOUND( "plats/vehicle2.wav" );pev->noise = MAKE_STRING( "plats/vehicle2.wav" ); break; + case 3: PRECACHE_SOUND( "plats/vehicle3.wav" );pev->noise = MAKE_STRING( "plats/vehicle3.wav" ); break; + case 4: PRECACHE_SOUND( "plats/vehicle4.wav" );pev->noise = MAKE_STRING( "plats/vehicle4.wav" ); break; + case 5: PRECACHE_SOUND( "plats/vehicle6.wav" );pev->noise = MAKE_STRING( "plats/vehicle6.wav" ); break; + case 6: PRECACHE_SOUND( "plats/vehicle7.wav" );pev->noise = MAKE_STRING( "plats/vehicle7.wav" ); break; + } + + PRECACHE_SOUND( "plats/vehicle_brake1.wav" ); + PRECACHE_SOUND( "plats/vehicle_start1.wav" ); + + m_usAdjustPitch = PRECACHE_EVENT( 1, "events/vehicle.sc" ); +} + +LINK_ENTITY_TO_CLASS( func_vehiclecontrols, CFuncVehicleControls ); + +void CFuncVehicleControls::Find() +{ + edict_t *pTarget = NULL; + + do + { + pTarget = FIND_ENTITY_BY_TARGETNAME( pTarget, STRING( pev->target )); + } + while( !FNullEnt( pTarget ) && !FClassnameIs( pTarget, "func_vehicle" )); + + if( FNullEnt( pTarget )) + { + ALERT( at_console, "No vehicle %s\n", STRING( pev->target )); + return; + } + + CFuncVehicle *pvehicle = CFuncVehicle::Instance( pTarget ); + + pvehicle->SetControls( pev ); + UTIL_Remove( this ); +} + +void CFuncVehicleControls::Spawn() +{ + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + SET_MODEL( ENT( pev ), STRING( pev->model )); + + UTIL_SetSize( pev, pev->mins, pev->maxs ); + UTIL_SetOrigin( pev, pev->origin ); + + SetThink( &CFuncVehicleControls::Find ); + pev->nextthink = gpGlobals->time; +} From d020f6d474684756c30854107597da68236f86c0 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 28 Jan 2024 09:15:15 +0000 Subject: [PATCH 096/127] HL 25th anniversary update satchel changes. (#429) --- dlls/game.cpp | 2 +- dlls/satchel.cpp | 86 ++++++++++++++++++++++++++++++++++++++++-------- mod_options.txt | 1 + 3 files changed, 75 insertions(+), 14 deletions(-) diff --git a/dlls/game.cpp b/dlls/game.cpp index f2c74d6b..26a4d481 100644 --- a/dlls/game.cpp +++ b/dlls/game.cpp @@ -34,7 +34,7 @@ cvar_t falldamage = { "mp_falldamage","0", FCVAR_SERVER }; cvar_t weaponstay = { "mp_weaponstay","0", FCVAR_SERVER }; cvar_t selfgauss = { "selfgauss", "1", FCVAR_SERVER }; cvar_t chargerfix = { "chargerfix", "0", FCVAR_SERVER }; -cvar_t satchelfix = { "satchelfix", "0", FCVAR_SERVER }; +cvar_t satchelfix = { "satchelfix", "1", FCVAR_SERVER }; cvar_t explosionfix = { "explosionfix", "0", FCVAR_SERVER }; cvar_t monsteryawspeedfix = { "monsteryawspeedfix", "1", FCVAR_SERVER }; cvar_t corpsephysics = { "corpsephysics", "0", FCVAR_SERVER }; diff --git a/dlls/satchel.cpp b/dlls/satchel.cpp index 38ebec08..f5fa886b 100644 --- a/dlls/satchel.cpp +++ b/dlls/satchel.cpp @@ -22,6 +22,7 @@ #include "nodes.h" #include "player.h" #include "gamerules.h" +#include "game.h" enum satchel_state { @@ -191,23 +192,39 @@ LINK_ENTITY_TO_CLASS( weapon_satchel, CSatchel ) //========================================================= int CSatchel::AddDuplicate( CBasePlayerItem *pOriginal ) { +#if !CLIENT_DLL CSatchel *pSatchel; + int nNumSatchels, nSatchelsInPocket; + CBaseEntity *ent; -#if CLIENT_DLL - if( bIsMultiplayer() ) -#else if( g_pGameRules->IsMultiplayer() ) -#endif { + if( satchelfix.value ) + { + if( !pOriginal->m_pPlayer ) + return TRUE; + + nNumSatchels = 0; + nSatchelsInPocket = pOriginal->m_pPlayer->m_rgAmmo[pOriginal->PrimaryAmmoIndex()]; + ent = NULL; + + while( ( ent = UTIL_FindEntityInSphere( ent, pOriginal->m_pPlayer->pev->origin, 4096 )) != NULL ) + { + if( FClassnameIs( ent->pev, "monster_satchel" )) + nNumSatchels += ent->pev->owner == pOriginal->m_pPlayer->edict(); + } + } + pSatchel = (CSatchel *)pOriginal; - if( pSatchel->m_chargeReady != SATCHEL_IDLE ) + if( pSatchel->m_chargeReady != SATCHEL_IDLE + && ( satchelfix.value && nSatchelsInPocket + nNumSatchels > SATCHEL_MAX_CARRY - 1 )) { // player has some satchels deployed. Refuse to add more. return FALSE; } } - +#endif return CBasePlayerWeapon::AddDuplicate( pOriginal ); } @@ -330,8 +347,9 @@ void CSatchel::Holster( int skiplocal /* = 0 */ ) } } -void CSatchel::PrimaryAttack() +void CSatchel::PrimaryAttack( void ) { +#if SATCHEL_OLD_BEHAVIOUR switch( m_chargeReady ) { case SATCHEL_IDLE: @@ -347,9 +365,9 @@ void CSatchel::PrimaryAttack() CBaseEntity *pSatchel = NULL; - while( ( pSatchel = UTIL_FindEntityInSphere( pSatchel, m_pPlayer->pev->origin, 4096 ) ) != NULL ) + while( ( pSatchel = UTIL_FindEntityInSphere( pSatchel, m_pPlayer->pev->origin, 4096 )) != NULL ) { - if( FClassnameIs( pSatchel->pev, "monster_satchel" ) ) + if( FClassnameIs( pSatchel->pev, "monster_satchel" )) { if( pSatchel->pev->owner == pPlayer ) { @@ -368,14 +386,56 @@ void CSatchel::PrimaryAttack() // we're reloading, don't allow fire break; } -} - -void CSatchel::SecondaryAttack( void ) -{ +#else if( m_chargeReady != SATCHEL_RELOAD ) { Throw(); } +#endif +} + +void CSatchel::SecondaryAttack( void ) +{ +#if SATCHEL_OLD_BEHAVIOUR + if( m_chargeReady != SATCHEL_RELOAD ) + { + Throw(); + } +#else + switch( m_chargeReady ) + { + case SATCHEL_IDLE: + break; + case SATCHEL_READY: + { + SendWeaponAnim( SATCHEL_RADIO_FIRE ); + + edict_t *pPlayer = m_pPlayer->edict(); + + CBaseEntity *pSatchel = NULL; + + while( ( pSatchel = UTIL_FindEntityInSphere( pSatchel, m_pPlayer->pev->origin, 4096 )) != NULL ) + { + if( FClassnameIs( pSatchel->pev, "monster_satchel" )) + { + if( pSatchel->pev->owner == pPlayer ) + { + pSatchel->Use( m_pPlayer, m_pPlayer, USE_ON, 0 ); + } + } + } + + m_chargeReady = SATCHEL_RELOAD; + m_flNextPrimaryAttack = GetNextAttackDelay( 0.5f ); + m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.5f; + m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.5f; + break; + } + case SATCHEL_RELOAD: + // we're reloading, don't allow fire + break; + } +#endif } void CSatchel::Throw( void ) diff --git a/mod_options.txt b/mod_options.txt index ec7d0844..8cba4164 100644 --- a/mod_options.txt +++ b/mod_options.txt @@ -7,6 +7,7 @@ GAUSS_OVERCHARGE_FIX=OFF # Gauss overcharge fix TRIPMINE_BEAM_DUPLICATION_FIX=OFF # Fix of tripmine beam duplication on level transition HANDGRENADE_DEPLOY_FIX=OFF # Handgrenade deploy animation fix after finishing a throw WEAPONS_ANIMATION_TIMES_FIX=OFF # Animation times fix for some weapons +SATCHEL_OLD_BEHAVIOUR=OFF # Old pre-HL 25th satchel's behaviour OEM_BUILD=OFF # OEM Build HLDEMO_BUILD=OFF # Demo Build From 41ee8a8746c019379568311810d5ed136e6c4e1c Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 28 Jan 2024 09:16:06 +0000 Subject: [PATCH 097/127] HL 25th anniversary update snark changes. (#430) --- dlls/squeakgrenade.cpp | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/dlls/squeakgrenade.cpp b/dlls/squeakgrenade.cpp index f332a5a8..f9e77472 100644 --- a/dlls/squeakgrenade.cpp +++ b/dlls/squeakgrenade.cpp @@ -339,7 +339,8 @@ void CSqueakGrenade::SuperBounceTouch( CBaseEntity *pOther ) // higher pitch as squeeker gets closer to detonation time flpitch = 155.0f - 60.0f * ( ( m_flDie - gpGlobals->time ) / SQUEEK_DETONATE_DELAY ); - if( pOther->pev->takedamage && m_flNextAttack < gpGlobals->time ) + if( !FBitSet( pOther->pev->flags, FL_WORLDBRUSH ) + && pOther->pev->takedamage && m_flNextAttack < gpGlobals->time ) { // attack! @@ -494,20 +495,35 @@ void CSqueak::PrimaryAttack() { if( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] ) { - UTIL_MakeVectors( m_pPlayer->pev->v_angle ); TraceResult tr; - Vector trace_origin; + Vector trace_origin, forward; + float flVel; + + UTIL_MakeVectors( Vector( 0, m_pPlayer->pev->v_angle.y, m_pPlayer->pev->v_angle.z )); + forward = gpGlobals->v_forward; + UTIL_MakeVectors( m_pPlayer->pev->v_angle ); + + if( m_pPlayer->pev->v_angle.x <= 0 ) + { + flVel = 1; + } + else + { + flVel = m_pPlayer->pev->v_angle.x / 90.0f; + } // HACK HACK: Ugly hacks to handle change in origin based on new physics code for players // Move origin up if crouched and start trace a bit outside of body ( 20 units instead of 16 ) trace_origin = m_pPlayer->pev->origin; if( m_pPlayer->pev->flags & FL_DUCKING ) { - trace_origin = trace_origin - ( VEC_HULL_MIN - VEC_DUCK_HULL_MIN ); + trace_origin = trace_origin - Vector( 0, 0, 1 ) * ( flVel + 1.0f ) * -18; } + forward = forward * flVel + gpGlobals->v_forward * ( 1 - flVel ); + // find place to toss monster - UTIL_TraceLine( trace_origin + gpGlobals->v_forward * 20.0f, trace_origin + gpGlobals->v_forward * 64.0f, dont_ignore_monsters, NULL, &tr ); + UTIL_TraceLine( trace_origin + forward * 24.0f, trace_origin + gpGlobals->v_forward * 60.0f, dont_ignore_monsters, NULL, &tr ); int flags; #if CLIENT_WEAPONS @@ -517,13 +533,13 @@ void CSqueak::PrimaryAttack() #endif PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usSnarkFire, 0.0f, g_vecZero, g_vecZero, 0.0f, 0.0f, 0, 0, 0, 0 ); - if( tr.fAllSolid == 0 && tr.fStartSolid == 0 && tr.flFraction > 0.25f ) + if( tr.fAllSolid == 0 && tr.fStartSolid == 0 && tr.flFraction > 0 ) { // player "shoot" animation m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); #if !CLIENT_DLL CBaseEntity *pSqueak = CBaseEntity::Create( "monster_snark", tr.vecEndPos, m_pPlayer->pev->v_angle, m_pPlayer->edict() ); - pSqueak->pev->velocity = gpGlobals->v_forward * 200.0f + m_pPlayer->pev->velocity; + pSqueak->pev->velocity = forward * 200.0f + m_pPlayer->pev->velocity; #endif // play hunt sound float flRndSound = RANDOM_FLOAT( 0.0f, 1.0f ); From 938a6e23618bcba537eee97bd60d51b55f2400cc Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 28 Jan 2024 09:16:25 +0000 Subject: [PATCH 098/127] HL 25th anniversary update rpg rocket changes. (#431) --- dlls/rpg.cpp | 42 ++++++++++++++++++++++++++++++++++-------- dlls/weapons.h | 4 +++- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/dlls/rpg.cpp b/dlls/rpg.cpp index c81e6246..271a9d7c 100644 --- a/dlls/rpg.cpp +++ b/dlls/rpg.cpp @@ -116,6 +116,25 @@ CRpgRocket *CRpgRocket::CreateRpgRocket( Vector vecOrigin, Vector vecAngles, CBa return pRocket; } +void CRpgRocket::Explode( TraceResult *pTrace, int bitsDamageType ) +{ + if( CRpg *pLauncher = GetLauncher()) + { + // my launcher is still around, tell it I'm dead. + pLauncher->m_cActiveRockets--; + m_hLauncher = 0; + } + + STOP_SOUND( edict(), CHAN_VOICE, "weapons/rocket1.wav" ); + + CGrenade::Explode( pTrace, bitsDamageType ); +} + +CRpg *CRpgRocket::GetLauncher( void ) +{ + return (CRpg*)( (CBaseEntity*)m_hLauncher ); +} + //========================================================= //========================================================= void CRpgRocket::Spawn( void ) @@ -150,10 +169,11 @@ void CRpgRocket::Spawn( void ) //========================================================= void CRpgRocket::RocketTouch( CBaseEntity *pOther ) { - if( CRpg* pLauncher = (CRpg*)( (CBaseEntity*)( m_hLauncher ) ) ) + if( CRpg *pLauncher = GetLauncher()) { // my launcher is still around, tell it I'm dead. pLauncher->m_cActiveRockets--; + m_hLauncher = 0; } STOP_SOUND( edict(), CHAN_VOICE, "weapons/rocket1.wav" ); @@ -264,17 +284,23 @@ void CRpgRocket::FollowThink( void ) } pev->velocity = pev->velocity * 0.2f + vecTarget * flSpeed * 0.798f; if( pev->waterlevel == 0 && pev->velocity.Length() < 1500.0f ) - { - if( CRpg *pLauncher = (CRpg*)( (CBaseEntity*)( m_hLauncher ) ) ) - { - // my launcher is still around, tell it I'm dead. - pLauncher->m_cActiveRockets--; - } Detonate(); - } } // ALERT( at_console, "%.0f\n", flSpeed ); + if( CRpg *pLauncher = GetLauncher()) + { + if( ( pev->origin - pLauncher->pev->origin ).Length() > 8192 || gpGlobals->time - m_flIgniteTime > 6.0f ) + { + // my launcher is still around, tell it I'm dead. + pLauncher->m_cActiveRockets--; + m_hLauncher = 0; + } + } + + if( UTIL_PointContents( pev->origin ) == CONTENTS_SKY ) + Detonate(); + pev->nextthink = gpGlobals->time + 0.1f; } #endif diff --git a/dlls/weapons.h b/dlls/weapons.h index e4966125..ca1ec3b7 100644 --- a/dlls/weapons.h +++ b/dlls/weapons.h @@ -37,7 +37,7 @@ public: static void UseSatchelCharges( entvars_t *pevOwner, SATCHELCODE code ); void Explode( Vector vecSrc, Vector vecAim ); - void Explode( TraceResult *pTrace, int bitsDamageType ); + virtual void Explode( TraceResult *pTrace, int bitsDamageType ); void EXPORT Smoke( void ); void EXPORT BounceTouch( CBaseEntity *pOther ); @@ -723,6 +723,8 @@ public: void EXPORT IgniteThink( void ); void EXPORT RocketTouch( CBaseEntity *pOther ); static CRpgRocket *CreateRpgRocket( Vector vecOrigin, Vector vecAngles, CBaseEntity *pOwner, CRpg *pLauncher ); + void Explode( TraceResult *pTrace, int bitsDamageType ); + inline CRpg *GetLauncher( void ); int m_iTrail; float m_flIgniteTime; From eae53ece365413f4c93da516823a280c44c5ec36 Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Wed, 31 Jan 2024 14:01:12 +0300 Subject: [PATCH 099/127] Replace magic numbers related to the max number of sentences with symbolic constants. Change condition from greater-or-equal to just greater. (#432) --- dlls/sound.cpp | 8 ++++---- dlls/util.h | 6 +++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/dlls/sound.cpp b/dlls/sound.cpp index 992e0ebd..31a36085 100644 --- a/dlls/sound.cpp +++ b/dlls/sound.cpp @@ -1341,13 +1341,13 @@ void SENTENCEG_Init() g_engfuncs.pfnFreeFile( pMemFile ); - if( gcallsentences >= 2048 ) + if( gcallsentences > CVOXFILESENTENCEMAX_GOLDSOURCE_ANNIVERSARY_25 ) { - ALERT( at_warning, "NOTE: this mod might not work properly under GoldSource (post-anniversary update) engine: more than 2048 sentences\n" ); + ALERT( at_warning, "NOTE: this mod might not work properly under GoldSource (post-anniversary update) engine: more than %d sentences\n", CVOXFILESENTENCEMAX_GOLDSOURCE_ANNIVERSARY_25 ); } - else if( gcallsentences >= 1536 ) + else if( gcallsentences > CVOXFILESENTENCEMAX_GOLDSOURCE_LEGACY ) { - ALERT( at_warning, "NOTE: this mod might not work properly under GoldSource (pre-anniversary update) engine: more than 1536 sentences\n" ); + ALERT( at_warning, "NOTE: this mod might not work properly under GoldSource (pre-anniversary update) engine: more than %d sentences\n", CVOXFILESENTENCEMAX_GOLDSOURCE_LEGACY ); } fSentencesInit = TRUE; diff --git a/dlls/util.h b/dlls/util.h index bfe0fd32..6ad28546 100644 --- a/dlls/util.h +++ b/dlls/util.h @@ -498,7 +498,11 @@ extern DLL_GLOBAL int g_Language; // sentence groups #define CBSENTENCENAME_MAX 16 -#define CVOXFILESENTENCEMAX 4096 // max number of sentences in game. NOTE: this must match + +#define CVOXFILESENTENCEMAX_GOLDSOURCE_LEGACY 1536 +#define CVOXFILESENTENCEMAX_GOLDSOURCE_ANNIVERSARY_25 2048 +#define CVOXFILESENTENCEMAX_XASH3D 4096 +#define CVOXFILESENTENCEMAX CVOXFILESENTENCEMAX_XASH3D // max number of sentences in game. NOTE: this must match // CVOXFILESENTENCEMAX in engine\sound.h!!! extern char gszallsentencenames[CVOXFILESENTENCEMAX][CBSENTENCENAME_MAX]; From 6787caaf9ebb39ec65870626358f02c05fb9e8be Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Thu, 1 Feb 2024 02:38:59 +0500 Subject: [PATCH 100/127] Read spserver.cfg when load map in singleplayer game mode. --- dlls/singleplay_gamerules.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/singleplay_gamerules.cpp b/dlls/singleplay_gamerules.cpp index ba73747b..59f43fa2 100644 --- a/dlls/singleplay_gamerules.cpp +++ b/dlls/singleplay_gamerules.cpp @@ -35,6 +35,7 @@ extern int gmsgMOTD; //========================================================= CHalfLifeRules::CHalfLifeRules( void ) { + SERVER_COMMAND( "exec spserver.cfg\n" ); RefreshSkillData(); } From 9c247c5bcf6d04fe46214cbf5c114d0f5e99e370 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Fri, 2 Feb 2024 13:02:25 +0000 Subject: [PATCH 101/127] Add new humoristic railgunarena-like multiplayer game mode "GhostBusters" from HL 25th anniversary update. (#433) --- dlls/egon.cpp | 15 ++- dlls/game.cpp | 3 +- dlls/game.h | 1 + dlls/gamerules.cpp | 5 + dlls/gamerules.h | 26 +++++ dlls/multiplay_gamerules.cpp | 216 +++++++++++++++++++++++++++++++++++ dlls/player.cpp | 71 +++++++++--- dlls/player.h | 1 + dlls/weapons.cpp | 15 ++- dlls/weapons.h | 2 +- 10 files changed, 337 insertions(+), 18 deletions(-) diff --git a/dlls/egon.cpp b/dlls/egon.cpp index d315e26f..ab6fe24c 100644 --- a/dlls/egon.cpp +++ b/dlls/egon.cpp @@ -291,7 +291,8 @@ void CEgon::Fire( const Vector &vecOrigSrc, const Vector &vecDir ) // multiplayer uses 1 ammo every 1/10th second if( gpGlobals->time >= m_flAmmoUseTime ) { - UseAmmo( 1 ); + if( !g_pGameRules->IsBustingGame()) + UseAmmo( 1 ); m_flAmmoUseTime = gpGlobals->time + 0.1f; } } @@ -336,7 +337,8 @@ void CEgon::Fire( const Vector &vecOrigSrc, const Vector &vecDir ) //multiplayer uses 5 ammo/second if( gpGlobals->time >= m_flAmmoUseTime ) { - UseAmmo( 1 ); + if( !g_pGameRules->IsBustingGame()) + UseAmmo( 1 ); m_flAmmoUseTime = gpGlobals->time + 0.2f; } } @@ -497,6 +499,15 @@ void CEgon::WeaponIdle( void ) m_deployed = TRUE; } +BOOL CEgon::CanHolster( void ) +{ +#if CLIENT_DLL + return TRUE; +#else + return !g_pGameRules->IsBustingGame(); +#endif +} + void CEgon::EndAttack( void ) { bool bMakeNoise = false; diff --git a/dlls/game.cpp b/dlls/game.cpp index 26a4d481..93cc7eb2 100644 --- a/dlls/game.cpp +++ b/dlls/game.cpp @@ -458,6 +458,7 @@ cvar_t sk_player_leg3 = { "sk_player_leg3","1" }; // END Cvars for Skill Level settings cvar_t sv_pushable_fixed_tick_fudge = { "sv_pushable_fixed_tick_fudge", "15" }; +cvar_t sv_busters = { "sv_busters", "0" }; // Register your console variables here // This gets called one time when the game is initialied @@ -507,7 +508,7 @@ void GameDLLInit( void ) CVAR_REGISTER( &multibyte_only ); CVAR_REGISTER( &mp_chattime ); - + CVAR_REGISTER( &sv_busters ); // REGISTER CVARS FOR SKILL LEVEL STUFF diff --git a/dlls/game.h b/dlls/game.h index 069324bb..8a0f6d34 100644 --- a/dlls/game.h +++ b/dlls/game.h @@ -44,6 +44,7 @@ extern cvar_t defaultteam; extern cvar_t allowmonsters; extern cvar_t bhopcap; extern cvar_t sv_pushable_fixed_tick_fudge; +extern cvar_t sv_busters; // Engine Cvars extern cvar_t *g_psv_gravity; diff --git a/dlls/gamerules.cpp b/dlls/gamerules.cpp index bfbbf59e..2499a67d 100644 --- a/dlls/gamerules.cpp +++ b/dlls/gamerules.cpp @@ -331,6 +331,11 @@ CGameRules *InstallGameRules( void ) g_teamplay = 1; return new CHalfLifeTeamplay; } + if( sv_busters.value > 0 ) + { + g_teamplay = 0; + return new CMultiplayBusters; + } if( (int)gpGlobals->deathmatch == 1 ) { // vanilla deathmatch diff --git a/dlls/gamerules.h b/dlls/gamerules.h index 781ef447..0d86ce02 100644 --- a/dlls/gamerules.h +++ b/dlls/gamerules.h @@ -162,6 +162,7 @@ public: // Immediately end a multiplayer game virtual void EndMultiplayerGame( void ) {} + virtual BOOL IsBustingGame( void ){ return FALSE; }; }; extern CGameRules *InstallGameRules( void ); @@ -362,5 +363,30 @@ protected: void SendMOTDToClient( edict_t *client ); }; +bool IsPlayerBusting( CBaseEntity *pPlayer ); +BOOL BustingCanHaveItem( CBasePlayer *pPlayer, CBaseEntity *pItem ); + +class CMultiplayBusters : public CHalfLifeMultiplay +{ +public: + CMultiplayBusters(); + void Think(); + void PlayerSpawn( CBasePlayer *pPlayer ); + void ClientUserInfoChanged( CBasePlayer *pPlayer, char *infobuffer ); + int IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled ); + void PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor ); + void DeathNotice( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pevInflictor ); + BOOL CanHavePlayerItem( CBasePlayer *pPlayer, CBasePlayerItem *pItem ); + void PlayerGotWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pWeapon ); + int WeaponShouldRespawn( CBasePlayerItem *pWeapon ); + BOOL CanHaveItem( CBasePlayer *pPlayer, CItem *pItem ); + void CheckForEgons(); + void SetPlayerModel( CBasePlayer *pPlayer, BOOL bKnownBuster ); + BOOL IsBustingGame( void ){ return TRUE; }; + +protected: + float m_flEgonBustingCheckTime; +}; + extern DLL_GLOBAL CGameRules *g_pGameRules; #endif // GAMERULES_H diff --git a/dlls/multiplay_gamerules.cpp b/dlls/multiplay_gamerules.cpp index 49e24ab3..1578ec13 100644 --- a/dlls/multiplay_gamerules.cpp +++ b/dlls/multiplay_gamerules.cpp @@ -1723,3 +1723,219 @@ void CHalfLifeMultiplay::SendMOTDToClient( edict_t *client ) FREE_FILE( (void*)aFileList ); } + +int CMultiplayBusters::WeaponShouldRespawn( CBasePlayerItem *pWeapon ) +{ + if( pWeapon->m_iId == WEAPON_EGON ) + return GR_WEAPON_RESPAWN_NO; + + return CHalfLifeMultiplay::WeaponShouldRespawn( pWeapon ); +} + +BOOL CMultiplayBusters::CanHaveItem( CBasePlayer *pPlayer, CItem *pItem ) +{ + return BustingCanHaveItem( pPlayer, pItem ); +} + +BOOL CMultiplayBusters::CanHavePlayerItem( CBasePlayer *pPlayer, CBasePlayerItem *pItem ) +{ + if( !BustingCanHaveItem( pPlayer, pItem )) + return FALSE; + + return CHalfLifeMultiplay::CanHavePlayerItem( pPlayer, pItem ); +} + +int CMultiplayBusters::IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled ) +{ + if( IsPlayerBusting( pAttacker )) + return 1; + + if( IsPlayerBusting( pKilled )) + return 2; + + return 0; +} +void CMultiplayBusters::DeathNotice( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pevInflictor ) +{ + if( IsPlayerBusting( pVictim ) + || IsPlayerBusting( CBaseEntity::Instance( pKiller ))) + CHalfLifeMultiplay::DeathNotice( pVictim, pKiller, pevInflictor ); +} + +void CMultiplayBusters::PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor ) +{ + if( IsPlayerBusting( pVictim )) + { + UTIL_ClientPrintAll( HUD_PRINTCENTER, "The Buster is dead!!" ); + + m_flEgonBustingCheckTime = -1.0f; + + CBasePlayer *peKiller = NULL; + CBaseEntity *ktmp = CBaseEntity::Instance( pKiller ); + if( ktmp && ( ktmp->Classify() == CLASS_PLAYER ) ) + peKiller = (CBasePlayer*)ktmp; + else if( ktmp && ktmp->Classify() == CLASS_VEHICLE ) + { + CBasePlayer *pDriver = (CBasePlayer *)( (CFuncVehicle *)ktmp )->m_pDriver; + + if( pDriver != NULL ) + { + pKiller = pDriver->pev; + peKiller = (CBasePlayer *)pDriver; + } + } + + if( peKiller && peKiller->IsPlayer() ) + { + UTIL_ClientPrintAll( HUD_PRINTTALK, UTIL_VarArgs( "%s has killed the Buster!", STRING( peKiller->pev->netname ))); + } + + pVictim->pev->renderfx = 0; + pVictim->pev->rendercolor = g_vecZero; + } + + CHalfLifeMultiplay::PlayerKilled( pVictim, pKiller, pInflictor ); +} + +void CMultiplayBusters::ClientUserInfoChanged( CBasePlayer *pPlayer, char *infobuffer ) +{ + SetPlayerModel( pPlayer, FALSE ); + CHalfLifeMultiplay::ClientUserInfoChanged( pPlayer, infobuffer ); +} + +void CMultiplayBusters::PlayerSpawn( CBasePlayer *pPlayer ) +{ + CHalfLifeMultiplay::PlayerSpawn( pPlayer ); + SetPlayerModel( pPlayer, FALSE ); +} + +bool IsPlayerBusting( CBaseEntity *pPlayer ) +{ + if( g_pGameRules->IsBustingGame() + && pPlayer && pPlayer->IsPlayer() + && ((CBasePlayer*)pPlayer)->HasPlayerItemFromID( WEAPON_EGON )) + return true; + + return false; +} + +BOOL BustingCanHaveItem( CBasePlayer *pPlayer, CBaseEntity *pItem ) +{ + if( IsPlayerBusting( pPlayer ) + && !( strncmp( STRING( pItem->pev->classname ), "weapon_", 7 ) + && strncmp( STRING( pItem->pev->classname ), "ammo_", 5 ))) + return FALSE; + + return TRUE; +} + +CMultiplayBusters::CMultiplayBusters() +{ + CHalfLifeMultiplay(); + + m_flEgonBustingCheckTime = -1.0; +} + +void CMultiplayBusters::CheckForEgons( void ) +{ + CBaseEntity *pPlayer; + CWeaponBox *pWeaponBox = NULL; + CBasePlayerItem *pWeapon; + CBasePlayer *pNewBuster = NULL; + int i, bestfrags = 9999; + + if( m_flEgonBustingCheckTime <= 0.0f ) + { + m_flEgonBustingCheckTime = gpGlobals->time + 10.0f; + return; + } + + if( gpGlobals->time < m_flEgonBustingCheckTime ) + return; + + m_flEgonBustingCheckTime = -1.0f; + + for( i = 1; i <= gpGlobals->maxClients; i++ ) + { + pPlayer = UTIL_PlayerByIndex( i ); + if( IsPlayerBusting( pPlayer )) + return; + } + + for( i = 1; i <= gpGlobals->maxClients; i++ ) + { + pPlayer = UTIL_PlayerByIndex( i ); + + if( pPlayer && pPlayer->pev->frags < bestfrags ) + { + pNewBuster = (CBasePlayer*)pPlayer; + bestfrags = pPlayer->pev->frags; + } + } + + if( !pNewBuster ) + return; + + pNewBuster->GiveNamedItem( "weapon_egon" ); + + while( ( pWeaponBox = (CWeaponBox*)UTIL_FindEntityByClassname( pWeaponBox, "weaponbox" ))) + { + // destroy weaponboxes with egons + for( i = 0; i < MAX_ITEM_TYPES; i++ ) + { + pWeapon = pWeaponBox->m_rgpPlayerItems[i]; + + while( pWeapon ) + { + if( pWeapon->m_iId != WEAPON_EGON ) + { + pWeapon = pWeapon->m_pNext; + continue; + } + + pWeaponBox->Kill(); + pWeapon = 0; + i = MAX_ITEM_TYPES; + } + } + } +} + +void CMultiplayBusters::Think( void ) +{ + CheckForEgons(); + CHalfLifeMultiplay::Think(); +} + +void CMultiplayBusters::SetPlayerModel( CBasePlayer *pPlayer, BOOL bKnownBuster ) +{ + const char *pszModel = NULL; + + if( bKnownBuster || IsPlayerBusting( pPlayer )) + { + pszModel = "ivan"; + } + else + { + pszModel = "skeleton"; + } + + g_engfuncs.pfnSetClientKeyValue( ENTINDEX( pPlayer->edict()), g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict()), "model", pszModel ); +} + +void CMultiplayBusters::PlayerGotWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pWeapon ) +{ + if( pWeapon->m_iId != WEAPON_EGON ) + return; + + pPlayer->RemoveAllItems( FALSE ); + UTIL_ClientPrintAll( HUD_PRINTCENTER, "Long live the new Buster!" ); + UTIL_ClientPrintAll( HUD_PRINTTALK, UTIL_VarArgs( "%s is busting!\n", STRING( pPlayer->pev->netname ))); + SetPlayerModel( pPlayer, TRUE ); + pPlayer->pev->health = pPlayer->pev->max_health; + pPlayer->pev->armorvalue = MAX_NORMAL_BATTERY; + pPlayer->pev->renderfx = kRenderFxGlowShell; + pPlayer->pev->renderamt = 25; + pPlayer->pev->rendercolor = Vector( 0, 75, 250 ); + pPlayer->m_rgAmmo[pWeapon->PrimaryAmmoIndex()] = pPlayer->ammo_uranium = 100; +} diff --git a/dlls/player.cpp b/dlls/player.cpp index 7f5df559..3b1d57af 100644 --- a/dlls/player.cpp +++ b/dlls/player.cpp @@ -755,24 +755,44 @@ void CBasePlayer::PackDeadPlayerItems( void ) iPA = 0; iPW = 0; - // pack the ammo - while( iPackAmmo[iPA] != -1 ) + if( g_pGameRules->IsBustingGame()) { - pWeaponBox->PackAmmo( MAKE_STRING( CBasePlayerItem::AmmoInfoArray[iPackAmmo[iPA]].pszName ), m_rgAmmo[iPackAmmo[iPA]] ); - iPA++; + while( rgpPackWeapons[iPW] ) + { + // weapon unhooked from the player. Pack it into der box. + if( FClassnameIs( rgpPackWeapons[iPW]->pev, "weapon_egon" )) + { + pWeaponBox->PackWeapon( rgpPackWeapons[iPW] ); + SET_MODEL( pWeaponBox->edict(), "models/w_egon.mdl" ); + pWeaponBox->pev->velocity = g_vecZero; + pWeaponBox->pev->renderfx = kRenderFxGlowShell; + pWeaponBox->pev->renderamt = 25; + pWeaponBox->pev->rendercolor = Vector( 0, 75, 250 ); + break; + } + iPW++; + } } - - // now pack all of the items in the lists - while( rgpPackWeapons[iPW] ) + else { - // weapon unhooked from the player. Pack it into der box. - pWeaponBox->PackWeapon( rgpPackWeapons[iPW] ); + // pack the ammo + while( iPackAmmo[iPA] != -1 ) + { + pWeaponBox->PackAmmo( MAKE_STRING( CBasePlayerItem::AmmoInfoArray[iPackAmmo[iPA]].pszName ), m_rgAmmo[iPackAmmo[iPA]] ); + iPA++; + } - iPW++; + // now pack all of the items in the lists + while( rgpPackWeapons[iPW] ) + { + // weapon unhooked from the player. Pack it into der box. + pWeaponBox->PackWeapon( rgpPackWeapons[iPW] ); + + iPW++; + } + + pWeaponBox->pev->velocity = pev->velocity * 1.2;// weaponbox has player's velocity, then some. } - - pWeaponBox->pev->velocity = pev->velocity * 1.2;// weaponbox has player's velocity, then some. - RemoveAllItems( TRUE );// now strip off everything that wasn't handled by the code above. } @@ -4635,6 +4655,31 @@ BOOL CBasePlayer::HasNamedPlayerItem( const char *pszItemName ) return FALSE; } +//========================================================= +// HasPlayerItemFromID +//========================================================= +BOOL CBasePlayer::HasPlayerItemFromID( int nID ) +{ + CBasePlayerItem *pItem; + int i; + + for( i = 0; i < MAX_ITEM_TYPES; i++ ) + { + pItem = m_rgpPlayerItems[i]; + + while( pItem ) + { + if( nID == pItem->m_iId ) + { + return TRUE; + } + pItem = pItem->m_pNext; + } + } + + return FALSE; +} + //========================================================= // //========================================================= diff --git a/dlls/player.h b/dlls/player.h index f86d25b4..90dd62e1 100644 --- a/dlls/player.h +++ b/dlls/player.h @@ -265,6 +265,7 @@ public: void DropPlayerItem ( char *pszItemName ); BOOL HasPlayerItem( CBasePlayerItem *pCheckItem ); BOOL HasNamedPlayerItem( const char *pszItemName ); + BOOL HasPlayerItemFromID( int nID ); BOOL HasWeapons( void );// do I have ANY weapons? void SelectPrevItem( int iItem ); void SelectNextItem( int iItem ); diff --git a/dlls/weapons.cpp b/dlls/weapons.cpp index 0443d271..3ff1a15f 100644 --- a/dlls/weapons.cpp +++ b/dlls/weapons.cpp @@ -471,6 +471,19 @@ void CBasePlayerItem::FallThink( void ) Materialize(); } + else if( m_pPlayer ) + { + SetThink( NULL ); + } + + if( g_pGameRules->IsBustingGame()) + { + if( !FNullEnt( pev->owner )) + return; + + if( FClassnameIs( pev, "weapon_egon" )) + UTIL_Remove( this ); + } } //========================================================= @@ -1076,7 +1089,7 @@ void CBasePlayerAmmo::Materialize( void ) void CBasePlayerAmmo::DefaultTouch( CBaseEntity *pOther ) { - if( !pOther->IsPlayer() ) + if( !pOther->IsPlayer() || IsPlayerBusting( pOther )) { return; } diff --git a/dlls/weapons.h b/dlls/weapons.h index ca1ec3b7..fae0889d 100644 --- a/dlls/weapons.h +++ b/dlls/weapons.h @@ -793,13 +793,13 @@ public: int AddToPlayer( CBasePlayer *pPlayer ); BOOL Deploy( void ); + BOOL CanHolster( void ); void Holster( int skiplocal = 0 ); void UpdateEffect( const Vector &startPoint, const Vector &endPoint, float timeBlend ); void CreateEffect ( void ); void DestroyEffect ( void ); - void EndAttack( void ); void Attack( void ); void PrimaryAttack( void ); From dd1036a5a014971f305a68cb8f5382da6f5f9dc1 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 3 Feb 2024 02:00:18 +0500 Subject: [PATCH 102/127] Change hive hand's weight. --- dlls/weapons.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/weapons.h b/dlls/weapons.h index fae0889d..f0ba783c 100644 --- a/dlls/weapons.h +++ b/dlls/weapons.h @@ -95,7 +95,7 @@ public: #define RPG_WEIGHT 20 #define GAUSS_WEIGHT 20 #define EGON_WEIGHT 20 -#define HORNETGUN_WEIGHT 10 +#define HORNETGUN_WEIGHT 15 #define HANDGRENADE_WEIGHT 5 #define SNARK_WEIGHT 5 #define SATCHEL_WEIGHT -10 From d438dbfe339d7d82f687a9370cbe811104d6051c Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 3 Feb 2024 22:58:03 +0500 Subject: [PATCH 103/127] Reload dropped weapons. --- dlls/player.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/dlls/player.cpp b/dlls/player.cpp index 3b1d57af..04d161cc 100644 --- a/dlls/player.cpp +++ b/dlls/player.cpp @@ -661,7 +661,7 @@ void CBasePlayer::PackDeadPlayerItems( void ) { int iWeaponRules; int iAmmoRules; - int i; + int i, j; CBasePlayerWeapon *rgpPackWeapons[MAX_WEAPONS] = {0,}; int iPackAmmo[MAX_AMMO_SLOTS]; int iPW = 0;// index into packweapons array @@ -696,16 +696,26 @@ void CBasePlayer::PackDeadPlayerItems( void ) if( m_pActiveItem && pPlayerItem == m_pActiveItem ) { // this is the active item. Pack it. - rgpPackWeapons[iPW++] = (CBasePlayerWeapon *)pPlayerItem; + rgpPackWeapons[iPW] = (CBasePlayerWeapon *)pPlayerItem; } break; case GR_PLR_DROP_GUN_ALL: - rgpPackWeapons[iPW++] = (CBasePlayerWeapon *)pPlayerItem; + rgpPackWeapons[iPW] = (CBasePlayerWeapon *)pPlayerItem; break; default: break; } + if( rgpPackWeapons[iPW] ) + { + // complete the reload. + j = Q_min( rgpPackWeapons[iPW]->iMaxClip() - rgpPackWeapons[iPW]->m_iClip, m_rgAmmo[rgpPackWeapons[iPW]->m_iPrimaryAmmoType] ); + + // Add them to the clip + rgpPackWeapons[iPW]->m_iClip += j; + m_rgAmmo[rgpPackWeapons[iPW]->m_iPrimaryAmmoType] -= j; + iPW++; + } pPlayerItem = pPlayerItem->m_pNext; } } @@ -790,8 +800,8 @@ void CBasePlayer::PackDeadPlayerItems( void ) iPW++; } - - pWeaponBox->pev->velocity = pev->velocity * 1.2;// weaponbox has player's velocity, then some. +end: + pWeaponBox->pev->velocity = pev->velocity * 1.2f;// weaponbox has player's velocity, then some. } RemoveAllItems( TRUE );// now strip off everything that wasn't handled by the code above. } From b37078f019a83da9dc6e0cc5ef29668725b2f25f Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 4 Feb 2024 01:15:33 +0500 Subject: [PATCH 104/127] Change weapon switch rules. --- dlls/gamerules.h | 2 +- dlls/multiplay_gamerules.cpp | 65 ++----------------------------- dlls/rpg.cpp | 2 +- dlls/singleplay_gamerules.cpp | 73 +++++++++++++++++++++++++++++++++++ dlls/weapons.h | 1 + 5 files changed, 79 insertions(+), 64 deletions(-) diff --git a/dlls/gamerules.h b/dlls/gamerules.h index 0d86ce02..adbbaa31 100644 --- a/dlls/gamerules.h +++ b/dlls/gamerules.h @@ -166,7 +166,7 @@ public: }; extern CGameRules *InstallGameRules( void ); - +BOOL HLGetNextBestWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon ); //========================================================= // CHalfLifeRules - rules for the single player Half-Life diff --git a/dlls/multiplay_gamerules.cpp b/dlls/multiplay_gamerules.cpp index 1578ec13..ef2db2d5 100644 --- a/dlls/multiplay_gamerules.cpp +++ b/dlls/multiplay_gamerules.cpp @@ -345,70 +345,11 @@ BOOL CHalfLifeMultiplay::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBasePlayerI return FALSE; } +//========================================================= +//========================================================= BOOL CHalfLifeMultiplay::GetNextBestWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon ) { - CBasePlayerItem *pCheck; - CBasePlayerItem *pBest;// this will be used in the event that we don't find a weapon in the same category. - int iBestWeight; - int i; - - iBestWeight = -1;// no weapon lower than -1 can be autoswitched to - pBest = NULL; - - if( !pCurrentWeapon->CanHolster() ) - { - // can't put this gun away right now, so can't switch. - return FALSE; - } - - for( i = 0; i < MAX_ITEM_TYPES; i++ ) - { - pCheck = pPlayer->m_rgpPlayerItems[i]; - - while( pCheck ) - { - if( pCheck->iWeight() > -1 && pCheck->iWeight() == pCurrentWeapon->iWeight() && pCheck != pCurrentWeapon ) - { - // this weapon is from the same category. - if ( pCheck->CanDeploy() ) - { - if ( pPlayer->SwitchWeapon( pCheck ) ) - { - return TRUE; - } - } - } - else if( pCheck->iWeight() > iBestWeight && pCheck != pCurrentWeapon )// don't reselect the weapon we're trying to get rid of - { - //ALERT ( at_console, "Considering %s\n", STRING( pCheck->pev->classname ) ); - // we keep updating the 'best' weapon just in case we can't find a weapon of the same weight - // that the player was using. This will end up leaving the player with his heaviest-weighted - // weapon. - if( pCheck->CanDeploy() ) - { - // if this weapon is useable, flag it as the best - iBestWeight = pCheck->iWeight(); - pBest = pCheck; - } - } - - pCheck = pCheck->m_pNext; - } - } - - // if we make it here, we've checked all the weapons and found no useable - // weapon in the same catagory as the current weapon. - - // if pBest is null, we didn't find ANYTHING. Shouldn't be possible- should always - // at least get the crowbar, but ya never know. - if( !pBest ) - { - return FALSE; - } - - pPlayer->SwitchWeapon( pBest ); - - return TRUE; + return HLGetNextBestWeapon( pPlayer, pCurrentWeapon ); } //========================================================= diff --git a/dlls/rpg.cpp b/dlls/rpg.cpp index 271a9d7c..4e39ec2c 100644 --- a/dlls/rpg.cpp +++ b/dlls/rpg.cpp @@ -400,7 +400,7 @@ int CRpg::GetItemInfo( ItemInfo *p ) p->iSlot = 3; p->iPosition = 0; p->iId = m_iId = WEAPON_RPG; - p->iFlags = 0; + p->iFlags = ITEM_FLAG_NOCHOICE; p->iWeight = RPG_WEIGHT; return 1; diff --git a/dlls/singleplay_gamerules.cpp b/dlls/singleplay_gamerules.cpp index 59f43fa2..06ad417c 100644 --- a/dlls/singleplay_gamerules.cpp +++ b/dlls/singleplay_gamerules.cpp @@ -95,10 +95,83 @@ BOOL CHalfLifeRules::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBasePlayerItem return TRUE; } +//========================================================= +//========================================================= +BOOL HLGetNextBestWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon ) +{ + CBasePlayerItem *pCheck; + CBasePlayerItem *pBest;// this will be used in the event that we don't find a weapon in the same category. + int iBestWeight; + int i; + + iBestWeight = -1;// no weapon lower than -1 can be autoswitched to + pBest = NULL; + + if( !pCurrentWeapon->CanHolster() ) + { + // can't put this gun away right now, so can't switch. + return FALSE; + } + + for( i = 0; i < MAX_ITEM_TYPES; i++ ) + { + pCheck = pPlayer->m_rgpPlayerItems[i]; + + while( pCheck ) + { + if( !FBitSet( pCheck->iFlags(), ITEM_FLAG_NOCHOICE )) + { + if( pCheck->iWeight() > -1 && pCheck->iWeight() == pCurrentWeapon->iWeight() && pCheck != pCurrentWeapon ) + { + // this weapon is from the same category. + if ( pCheck->CanDeploy() ) + { + if ( pPlayer->SwitchWeapon( pCheck ) ) + { + return TRUE; + } + } + } + else if( pCheck->iWeight() > iBestWeight && pCheck != pCurrentWeapon )// don't reselect the weapon we're trying to get rid of + { + //ALERT ( at_console, "Considering %s\n", STRING( pCheck->pev->classname ) ); + // we keep updating the 'best' weapon just in case we can't find a weapon of the same weight + // that the player was using. This will end up leaving the player with his heaviest-weighted + // weapon. + if( pCheck->CanDeploy() ) + { + // if this weapon is useable, flag it as the best + iBestWeight = pCheck->iWeight(); + pBest = pCheck; + } + } + } + + pCheck = pCheck->m_pNext; + } + } + + // if we make it here, we've checked all the weapons and found no useable + // weapon in the same catagory as the current weapon. + + // if pBest is null, we didn't find ANYTHING. Shouldn't be possible- should always + // at least get the crowbar, but ya never know. + if( !pBest ) + { + return FALSE; + } + + pPlayer->SwitchWeapon( pBest ); + + return TRUE; +} + //========================================================= //========================================================= BOOL CHalfLifeRules::GetNextBestWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon ) { + if( pCurrentWeapon && FBitSet( pCurrentWeapon->iFlags(), ITEM_FLAG_EXHAUSTIBLE )) + return HLGetNextBestWeapon( pPlayer, pCurrentWeapon ); return FALSE; } diff --git a/dlls/weapons.h b/dlls/weapons.h index f0ba783c..4db8381f 100644 --- a/dlls/weapons.h +++ b/dlls/weapons.h @@ -183,6 +183,7 @@ typedef enum #define ITEM_FLAG_NOAUTOSWITCHEMPTY 4 #define ITEM_FLAG_LIMITINWORLD 8 #define ITEM_FLAG_EXHAUSTIBLE 16 // A player can totally exhaust their ammo supply and lose this weapon +#define ITEM_FLAG_NOCHOICE 32 #define WEAPON_IS_ONTARGET 0x40 From e552d2cc06d303335def49075e5cf152eaed623b Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 3 Feb 2024 22:33:15 +0000 Subject: [PATCH 105/127] Make cycler and func_button speakable targets for scripted_sentence. (#434) --- cl_dll/hl/hl_baseentity.cpp | 6 +-- dlls/basemonster.h | 7 +--- dlls/cbase.h | 11 ++++- dlls/h_cycler.cpp | 2 + dlls/monsters.cpp | 23 +---------- dlls/scripted.cpp | 80 +++++++++++++++++++++---------------- dlls/subs.cpp | 21 ++++++++++ 7 files changed, 85 insertions(+), 65 deletions(-) diff --git a/cl_dll/hl/hl_baseentity.cpp b/cl_dll/hl/hl_baseentity.cpp index 2e2ec2d6..a47078e5 100644 --- a/cl_dll/hl/hl_baseentity.cpp +++ b/cl_dll/hl/hl_baseentity.cpp @@ -207,9 +207,9 @@ BOOL CBaseMonster::FindLateralCover( const Vector &vecThreat, const Vector &vecV Vector CBaseMonster::ShootAtEnemy( const Vector &shootOrigin ) { return g_vecZero; } BOOL CBaseMonster::FacingIdeal( void ) { return FALSE; } BOOL CBaseMonster::FCanActiveIdle( void ) { return FALSE; } -void CBaseMonster::PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ) { } -void CBaseMonster::PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener ) { } -void CBaseMonster::SentenceStop( void ) { } +void CBaseToggle::PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ) { } +void CBaseToggle::PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener ) { } +void CBaseToggle::SentenceStop( void ) { } void CBaseMonster::CorpseFallThink( void ) { } void CBaseMonster::MonsterInitDead( void ) { } BOOL CBaseMonster::BBoxFlat( void ) { return TRUE; } diff --git a/dlls/basemonster.h b/dlls/basemonster.h index ea145a93..dc642a6c 100644 --- a/dlls/basemonster.h +++ b/dlls/basemonster.h @@ -120,6 +120,7 @@ public: virtual int BloodColor( void ) { return m_bloodColor; } virtual CBaseMonster *MyMonsterPointer( void ) { return this; } + virtual BOOL IsAllowedToSpeak( void ) { return IsAlive(); } virtual void Look( int iDistance );// basic sight function for monsters virtual void RunAI( void );// core ai function! void Listen( void ); @@ -186,11 +187,7 @@ public: virtual void ScheduleChange( void ) {} // virtual int CanPlaySequence( void ) { return ((m_pCine == NULL) && (m_MonsterState == MONSTERSTATE_NONE || m_MonsterState == MONSTERSTATE_IDLE || m_IdealMonsterState == MONSTERSTATE_IDLE)); } virtual int CanPlaySequence( BOOL fDisregardState, int interruptLevel ); - virtual int CanPlaySentence( BOOL fDisregardState ) { return IsAlive(); } - virtual void PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ); - virtual void PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener ); - - virtual void SentenceStop( void ); + virtual int CanPlaySentence( BOOL fDisregardState ) { return IsAllowedToSpeak(); } Task_t *GetTask( void ); virtual MONSTERSTATE GetIdealState( void ); diff --git a/dlls/cbase.h b/dlls/cbase.h index 88a161e0..caf301d9 100644 --- a/dlls/cbase.h +++ b/dlls/cbase.h @@ -107,6 +107,7 @@ typedef void(CBaseEntity::*USEPTR)( CBaseEntity *pActivator, CBaseEntity *pCalle #define CLASS_BARNACLE 99 // special because no one pays attention to it, and it eats a wide cross-section of creatures. class CBaseEntity; +class CBaseToggle; class CBaseMonster; class CBasePlayerItem; class CSquadMonster; @@ -173,6 +174,7 @@ public: virtual int BloodColor( void ) { return DONT_BLEED; } virtual void TraceBleed( float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); virtual BOOL IsTriggered( CBaseEntity *pActivator ) {return TRUE; } + virtual CBaseToggle *MyTogglePointer( void ) { return NULL; } virtual CBaseMonster *MyMonsterPointer( void ) { return NULL; } virtual CSquadMonster *MySquadMonsterPointer( void ) { return NULL; } virtual int GetToggleState( void ) { return TS_AT_TOP; } @@ -251,7 +253,6 @@ public: int Intersects( CBaseEntity *pOther ); void MakeDormant( void ); int IsDormant( void ); - BOOL IsLockedByMaster( void ) { return FALSE; } static CBaseEntity *Instance( edict_t *pent ) { @@ -524,9 +525,15 @@ public: static TYPEDESCRIPTION m_SaveData[]; + CBaseToggle *MyTogglePointer( void ) { return this; } virtual int GetToggleState( void ) { return m_toggle_state; } virtual float GetDelay( void ) { return m_flWait; } + virtual void PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ); + virtual void PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener ); + virtual void SentenceStop( void ); + virtual BOOL IsAllowedToSpeak( void ) { return FALSE; } + // common member functions void LinearMove( Vector vecDest, float flSpeed ); void EXPORT LinearMoveDone( void ); @@ -693,6 +700,8 @@ public: // Buttons that don't take damage can be IMPULSE used virtual int ObjectCaps( void ) { return (CBaseToggle:: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | (pev->takedamage?0:FCAP_IMPULSE_USE); } + BOOL IsAllowedToSpeak( void ) { return TRUE; } + BOOL m_fStayPushed; // button stays pushed in until touched again? BOOL m_fRotating; // a rotating button? default is a sliding button. diff --git a/dlls/h_cycler.cpp b/dlls/h_cycler.cpp index 49eff1d8..16d15855 100644 --- a/dlls/h_cycler.cpp +++ b/dlls/h_cycler.cpp @@ -49,6 +49,8 @@ public: virtual int Restore( CRestore &restore ); static TYPEDESCRIPTION m_SaveData[]; + BOOL IsAllowedToSpeak( void ) { return TRUE; } + int m_animate; }; diff --git a/dlls/monsters.cpp b/dlls/monsters.cpp index 8b8643a6..f995dff5 100644 --- a/dlls/monsters.cpp +++ b/dlls/monsters.cpp @@ -1392,7 +1392,7 @@ float CBaseMonster::OpenDoorAndWait( entvars_t *pevDoor ) //ALERT( at_aiconsole, "A door. " ); CBaseEntity *pcbeDoor = CBaseEntity::Instance( pevDoor ); - if( pcbeDoor && !pcbeDoor->IsLockedByMaster() ) + if( pcbeDoor ) { //ALERT( at_aiconsole, "unlocked! " ); pcbeDoor->Use( this, this, USE_ON, 0.0 ); @@ -3189,27 +3189,6 @@ BOOL CBaseMonster::FCanActiveIdle( void ) return FALSE; } -void CBaseMonster::PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ) -{ - if( pszSentence && IsAlive() ) - { - if( pszSentence[0] == '!' ) - EMIT_SOUND_DYN( edict(), CHAN_VOICE, pszSentence, volume, attenuation, 0, PITCH_NORM ); - else - SENTENCEG_PlayRndSz( edict(), pszSentence, volume, attenuation, 0, PITCH_NORM ); - } -} - -void CBaseMonster::PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener ) -{ - PlaySentence( pszSentence, duration, volume, attenuation ); -} - -void CBaseMonster::SentenceStop( void ) -{ - EMIT_SOUND( edict(), CHAN_VOICE, "common/null.wav", 1.0, ATTN_IDLE ); -} - void CBaseMonster::CorpseFallThink( void ) { if( pev->flags & FL_ONGROUND ) diff --git a/dlls/scripted.cpp b/dlls/scripted.cpp index da495f98..bdaa85c0 100644 --- a/dlls/scripted.cpp +++ b/dlls/scripted.cpp @@ -902,9 +902,9 @@ public: static TYPEDESCRIPTION m_SaveData[]; - CBaseMonster *FindEntity( void ); - BOOL AcceptableSpeaker( CBaseMonster *pMonster ); - BOOL StartSentence( CBaseMonster *pTarget ); + CBaseToggle *FindEntity( void ); + BOOL AcceptableSpeaker( CBaseToggle *pTarget ); + BOOL StartSentence( CBaseToggle *pTarget ); private: string_t m_iszSentence; // string index for idle animation @@ -1036,20 +1036,20 @@ void CScriptedSentence::Spawn( void ) void CScriptedSentence::FindThink( void ) { - CBaseMonster *pMonster = FindEntity(); - if( pMonster ) + CBaseToggle *pTarget = FindEntity(); + if( pTarget ) { - StartSentence( pMonster ); + StartSentence( pTarget ); if( pev->spawnflags & SF_SENTENCE_ONCE ) UTIL_Remove( this ); SetThink( &CScriptedSentence::DelayThink ); pev->nextthink = gpGlobals->time + m_flDuration + m_flRepeat; m_active = FALSE; - //ALERT( at_console, "%s: found monster %s\n", STRING( m_iszSentence ), STRING( m_iszEntity ) ); + //ALERT( at_console, "%s: found target %s\n", STRING( m_iszSentence ), STRING( m_iszEntity ) ); } else { - //ALERT( at_console, "%s: can't find monster %s\n", STRING( m_iszSentence ), STRING( m_iszEntity ) ); + //ALERT( at_console, "%s: can't find target %s\n", STRING( m_iszSentence ), STRING( m_iszEntity ) ); pev->nextthink = gpGlobals->time + m_flRepeat + 0.5f; } } @@ -1062,45 +1062,57 @@ void CScriptedSentence::DelayThink( void ) SetThink( &CScriptedSentence::FindThink ); } -BOOL CScriptedSentence::AcceptableSpeaker( CBaseMonster *pMonster ) +BOOL CScriptedSentence::AcceptableSpeaker( CBaseToggle *pTarget ) { - if( pMonster ) + CBaseMonster *pMonster; + EHANDLE hTarget; + + if( pTarget ) { - if( pev->spawnflags & SF_SENTENCE_FOLLOWERS ) + hTarget = pTarget->MyMonsterPointer(); + + if( hTarget != 0 ) { - if( pMonster->m_hTargetEnt == 0 || !pMonster->m_hTargetEnt->IsPlayer() ) - return FALSE; + CBaseMonster *pMonster = (CBaseMonster*)( (CBaseEntity*)hTarget ); + if( pev->spawnflags & SF_SENTENCE_FOLLOWERS ) + { + if( pMonster->m_hTargetEnt == 0 || !pMonster->m_hTargetEnt->IsPlayer() ) + return FALSE; + } + + BOOL override; + + if( pev->spawnflags & SF_SENTENCE_INTERRUPT ) + override = TRUE; + else + override = FALSE; + + if( pMonster->CanPlaySentence( override ) ) + return TRUE; } - - BOOL override; - - if( pev->spawnflags & SF_SENTENCE_INTERRUPT ) - override = TRUE; else - override = FALSE; - - if( pMonster->CanPlaySentence( override ) ) - return TRUE; + return pTarget->IsAllowedToSpeak(); } + return FALSE; } -CBaseMonster *CScriptedSentence::FindEntity( void ) +CBaseToggle *CScriptedSentence::FindEntity( void ) { edict_t *pentTarget; - CBaseMonster *pMonster; + CBaseToggle *pTarget; pentTarget = FIND_ENTITY_BY_TARGETNAME( NULL, STRING( m_iszEntity ) ); - pMonster = NULL; + pTarget = NULL; while( !FNullEnt( pentTarget ) ) { - pMonster = GetMonsterPointer( pentTarget ); - if( pMonster != NULL ) + pTarget = (CBaseToggle*)CBaseEntity::Instance( pentTarget ); + if( pTarget != NULL ) { - if( AcceptableSpeaker( pMonster ) ) - return pMonster; - //ALERT( at_console, "%s (%s), not acceptable\n", STRING( pMonster->pev->classname ), STRING( pMonster->pev->targetname ) ); + if( AcceptableSpeaker( pTarget ) ) + return pTarget; + //ALERT( at_console, "%s (%s), not acceptable\n", STRING( pTarget->pev->classname ), STRING( pTarget->pev->targetname ) ); } pentTarget = FIND_ENTITY_BY_TARGETNAME( pentTarget, STRING( m_iszEntity ) ); } @@ -1112,9 +1124,9 @@ CBaseMonster *CScriptedSentence::FindEntity( void ) { if( FBitSet( pEntity->pev->flags, FL_MONSTER ) ) { - pMonster = pEntity->MyMonsterPointer(); - if( AcceptableSpeaker( pMonster ) ) - return pMonster; + pTarget = pEntity->MyTogglePointer(); + if( AcceptableSpeaker( pTarget ) ) + return pTarget; } } } @@ -1122,7 +1134,7 @@ CBaseMonster *CScriptedSentence::FindEntity( void ) return NULL; } -BOOL CScriptedSentence::StartSentence( CBaseMonster *pTarget ) +BOOL CScriptedSentence::StartSentence( CBaseToggle *pTarget ) { if( !pTarget ) { diff --git a/dlls/subs.cpp b/dlls/subs.cpp index 118554d4..9cdb6010 100644 --- a/dlls/subs.cpp +++ b/dlls/subs.cpp @@ -520,6 +520,27 @@ float CBaseToggle::AxisDelta( int flags, const Vector &angle1, const Vector &ang return angle1.y - angle2.y; } +void CBaseToggle::PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ) +{ + if( pszSentence && IsAllowedToSpeak()) + { + if( pszSentence[0] == '!' ) + EMIT_SOUND_DYN( edict(), CHAN_VOICE, pszSentence, volume, attenuation, 0, PITCH_NORM ); + else + SENTENCEG_PlayRndSz( edict(), pszSentence, volume, attenuation, 0, PITCH_NORM ); + } +} + +void CBaseToggle::PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener ) +{ + PlaySentence( pszSentence, duration, volume, attenuation ); +} + +void CBaseToggle::SentenceStop( void ) +{ + EMIT_SOUND( edict(), CHAN_VOICE, "common/null.wav", 1.0, ATTN_IDLE ); +} + /* ============= FEntIsVisible From b91043602e8cba48b8743f67ad935e3e59a6767c Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 4 Feb 2024 05:26:45 +0500 Subject: [PATCH 106/127] Don't stuck on level transitions. --- pm_shared/pm_shared.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pm_shared/pm_shared.c b/pm_shared/pm_shared.c index 32585d6a..dec69184 100644 --- a/pm_shared/pm_shared.c +++ b/pm_shared/pm_shared.c @@ -1699,7 +1699,7 @@ int PM_CheckStuck( void ) // // Deal with precision error in network. // - if( !pmove->server ) + if( !( pmove->server && pmove->multiplayer )) { // World or BSP model if( ( hitent == 0 ) || ( pmove->physents[hitent].model != NULL ) ) From 28112bab77c40bbea88be9f16d13e85a07520230 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 4 Feb 2024 17:46:45 +0500 Subject: [PATCH 107/127] Fix bullet impact sounds and interpolation artifacts. --- cl_dll/ev_hldm.cpp | 4 +++- common/const.h | 3 ++- dlls/client.cpp | 12 ++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/cl_dll/ev_hldm.cpp b/cl_dll/ev_hldm.cpp index 381ee25b..7ece6ad2 100644 --- a/cl_dll/ev_hldm.cpp +++ b/cl_dll/ev_hldm.cpp @@ -99,6 +99,7 @@ float EV_HLDM_PlayTextureSound( int idx, pmtrace_t *ptr, float *vecSrc, float *v int cnt; float fattn = ATTN_NORM; int entity; + cl_entity_t *ent; char *pTextureName; char texname[64]; char szbuffer[64]; @@ -110,7 +111,8 @@ float EV_HLDM_PlayTextureSound( int idx, pmtrace_t *ptr, float *vecSrc, float *v chTextureType = 0; // Player - if( entity >= 1 && entity <= gEngfuncs.GetMaxClients() ) + if( entity >= 1 && entity <= gEngfuncs.GetMaxClients() + || ( ( ent = gEngfuncs.GetEntityByIndex( entity )) && ( ent->curstate.eflags & EFLAG_MONSTER ))) { // hit body chTextureType = CHAR_TEX_FLESH; diff --git a/common/const.h b/common/const.h index 3708371b..5b7e1737 100644 --- a/common/const.h +++ b/common/const.h @@ -127,7 +127,8 @@ // entity flags #define EFLAG_SLERP 1 // do studio interpolation of this entity - +#define EFLAG_MONSTER 2 + // // temp entity events // diff --git a/dlls/client.cpp b/dlls/client.cpp index 3ce82cfb..4e269eae 100644 --- a/dlls/client.cpp +++ b/dlls/client.cpp @@ -1123,6 +1123,7 @@ we could also use the pas/ pvs that we set in SetupVisibility, if we wanted to. int AddToFullPack( struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, unsigned char *pSet ) { int i; + CBaseEntity *Entity; // don't send if flagged for NODRAW and it's not the host getting the message if( ( ent->v.effects & EF_NODRAW ) && ( ent != host ) ) @@ -1308,6 +1309,17 @@ int AddToFullPack( struct entity_state_s *state, int e, edict_t *ent, edict_t *h state->health = (int)ent->v.health; } + if( ( Entity = CBaseEntity::Instance( ent )) + && Entity->Classify() != CLASS_NONE + && Entity->Classify() != CLASS_MACHINE ) + { + SetBits( state->eflags, EFLAG_MONSTER ); + } + else + { + ClearBits( state->eflags, EFLAG_SLERP | EFLAG_MONSTER ); + } + return 1; } From 130a6aad6a8ce6986a6c2225314d4735b2033278 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 10 Feb 2024 00:45:10 +0500 Subject: [PATCH 108/127] Fix error message if LTO does not available. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 04cf5bce..4396dfec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,7 +157,7 @@ check_ipo_supported(RESULT HAVE_LTO OUTPUT LTO_ERROR) if(HAVE_LTO) message(STATUS "IPO / LTO enabled") else() - message(STATUS "IPO / LTO not supported: <${LTO_ERROR}>") + message(STATUS "IPO / LTO not supported: ${LTO_ERROR}") endif() check_include_file("tgmath.h" HAVE_TGMATH_H) From b8845fa4c85dd82734cad7a126e767d2b4618d7c Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 10 Feb 2024 04:42:33 +0500 Subject: [PATCH 109/127] Revert "cmake: set -O3 option to android linker." This reverts commit b8e143f5acd5cce9e752b4a620779f8c8aedc398. --- cl_dll/CMakeLists.txt | 1 - dlls/CMakeLists.txt | 1 - 2 files changed, 2 deletions(-) diff --git a/cl_dll/CMakeLists.txt b/cl_dll/CMakeLists.txt index 9375de27..f1dce6bf 100644 --- a/cl_dll/CMakeLists.txt +++ b/cl_dll/CMakeLists.txt @@ -45,7 +45,6 @@ if(NOT MSVC) add_compile_options(-funsafe-math-optimizations) # GCC/Clang flag if(CMAKE_SYSTEM_NAME STREQUAL "Android") add_compile_options(-O3 -DNDEBUG) # gradle compiles release builds with RelWithDebInfo(-O2 -g) and strips debug symbols. - target_link_options(${CLDLL_LIBRARY} PUBLIC "LINKER:-O3") endif() endif() else() diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index 9c518d39..ca69e13d 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -41,7 +41,6 @@ if(NOT MSVC) add_compile_options(-funsafe-math-optimizations) # GCC/Clang flag if(CMAKE_SYSTEM_NAME STREQUAL "Android") add_compile_options(-O3 -DNDEBUG) # gradle compiles release builds with RelWithDebInfo(-O2 -g) and strips debug symbols. - target_link_options(${SVDLL_LIBRARY} PUBLIC "LINKER:-O3") endif() endif() else() From fd3755e35cfbdb3de8727cf25679396d039a447c Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 10 Feb 2024 04:48:38 +0500 Subject: [PATCH 110/127] Remove additional android compilation flags. --- cl_dll/CMakeLists.txt | 3 --- dlls/CMakeLists.txt | 3 --- 2 files changed, 6 deletions(-) diff --git a/cl_dll/CMakeLists.txt b/cl_dll/CMakeLists.txt index f1dce6bf..37c03d7c 100644 --- a/cl_dll/CMakeLists.txt +++ b/cl_dll/CMakeLists.txt @@ -43,9 +43,6 @@ if(NOT MSVC) add_compile_options(-fomit-frame-pointer) # GCC/Clang flag add_compile_options(-ftree-vectorize) # GCC/Clang flag add_compile_options(-funsafe-math-optimizations) # GCC/Clang flag - if(CMAKE_SYSTEM_NAME STREQUAL "Android") - add_compile_options(-O3 -DNDEBUG) # gradle compiles release builds with RelWithDebInfo(-O2 -g) and strips debug symbols. - endif() endif() else() add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE) diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index ca69e13d..b78706c5 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -39,9 +39,6 @@ if(NOT MSVC) add_compile_options(-fomit-frame-pointer) # GCC/Clang flag add_compile_options(-ftree-vectorize) # GCC/Clang flag add_compile_options(-funsafe-math-optimizations) # GCC/Clang flag - if(CMAKE_SYSTEM_NAME STREQUAL "Android") - add_compile_options(-O3 -DNDEBUG) # gradle compiles release builds with RelWithDebInfo(-O2 -g) and strips debug symbols. - endif() endif() else() add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE) From 49edef9257f2239c1fed1dbbbad21aee7ad975fe Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 10 Feb 2024 04:53:43 +0500 Subject: [PATCH 111/127] Make LTO is optional. --- CMakeLists.txt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4396dfec..19f76e64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,7 @@ option(USE_NOVGUI_SCOREBOARD "Prefer non-VGUI Scoreboard when USE_VGUI is enable option(USE_VOICEMGR "Enable VOICE MANAGER." OFF) option(BUILD_CLIENT "Build client dll" ON) option(BUILD_SERVER "Build server dll" ON) +option(LTO "Enable interprocedural optimization" OFF) option(POLLY "Enable pollyhedral optimization" OFF) if(CMAKE_SIZEOF_VOID_P EQUAL 4 OR @@ -153,11 +154,13 @@ if(VITA) add_compile_options(-fno-use-cxa-atexit) endif() -check_ipo_supported(RESULT HAVE_LTO OUTPUT LTO_ERROR) -if(HAVE_LTO) - message(STATUS "IPO / LTO enabled") -else() - message(STATUS "IPO / LTO not supported: ${LTO_ERROR}") +if(LTO) + check_ipo_supported(RESULT HAVE_LTO OUTPUT LTO_ERROR) + if(HAVE_LTO) + message(STATUS "IPO / LTO enabled") + else() + message(STATUS "IPO / LTO not supported: ${LTO_ERROR}") + endif() endif() check_include_file("tgmath.h" HAVE_TGMATH_H) From a23889423154cd98fcb6c5fd28a0d8796b5ba61b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 5 Feb 2024 03:57:11 +0300 Subject: [PATCH 112/127] Remove linuxunkabi library suffix, as engine doesn't use it anymore --- cmake/LibraryNaming.cmake | 3 --- public/build.h | 8 -------- scripts/waifulib/library_naming.py | 3 --- 3 files changed, 14 deletions(-) diff --git a/cmake/LibraryNaming.cmake b/cmake/LibraryNaming.cmake index 724349bd..d71919fa 100644 --- a/cmake/LibraryNaming.cmake +++ b/cmake/LibraryNaming.cmake @@ -54,7 +54,6 @@ check_group_build_target(XASH_SERENITY XASH_PLATFORM) check_group_build_target(XASH_IRIX XASH_PLATFORM) check_group_build_target(XASH_NSWITCH XASH_PLATFORM) check_group_build_target(XASH_PSVITA XASH_PLATFORM) -check_group_build_target(XASH_LINUX_UNKNOWN XASH_PLATFORM) check_group_build_target(XASH_X86 XASH_ARCHITECTURE) check_group_build_target(XASH_AMD64 XASH_ARCHITECTURE) check_group_build_target(XASH_ARM XASH_ARCHITECTURE) @@ -84,8 +83,6 @@ unset(CMAKE_REQUIRED_INCLUDES) # engine/common/build.c if(XASH_ANDROID) set(BUILDOS "android") -elseif(XASH_LINUX_UNKNOWN) - set(BUILDOS "linuxunkabi") elseif(XASH_WIN32 OR XASH_LINUX OR XASH_APPLE) set(BUILDOS "") # no prefix for default OS elseif(XASH_FREEBSD) diff --git a/public/build.h b/public/build.h index 2f8fffae..851939b5 100644 --- a/public/build.h +++ b/public/build.h @@ -69,7 +69,6 @@ Then you can use another oneliner to query all variables: #undef XASH_IRIX #undef XASH_JS #undef XASH_LINUX -#undef XASH_LINUX_UNKNOWN #undef XASH_LITTLE_ENDIAN #undef XASH_MIPS #undef XASH_MOBILE_PLATFORM @@ -103,13 +102,6 @@ Then you can use another oneliner to query all variables: #if defined __linux__ #if defined __ANDROID__ #define XASH_ANDROID 1 - #else - #include - // if our system libc has features.h header - // try to detect it to not confuse other libcs with built with glibc game libraries - #if !defined __GLIBC__ - #define XASH_LINUX_UNKNOWN 1 - #endif #endif #define XASH_LINUX 1 #elif defined __FreeBSD__ diff --git a/scripts/waifulib/library_naming.py b/scripts/waifulib/library_naming.py index 4369b3b6..ac2faf6f 100644 --- a/scripts/waifulib/library_naming.py +++ b/scripts/waifulib/library_naming.py @@ -44,7 +44,6 @@ DEFINES = [ 'XASH_IRIX', 'XASH_JS', 'XASH_LINUX', -'XASH_LINUX_UNKNOWN', 'XASH_LITTLE_ENDIAN', 'XASH_MIPS', 'XASH_MOBILE_PLATFORM', @@ -76,8 +75,6 @@ def configure(conf): # engine/common/build.c if conf.env.XASH_ANDROID: buildos = "android" - elif conf.env.XASH_LINUX_UNKNOWN: - buildos = "linuxunkabi" elif conf.env.XASH_WIN32 or conf.env.XASH_LINUX or conf.env.XASH_APPLE: buildos = "" # no prefix for default OS elif conf.env.XASH_FREEBSD: From 011f1afeb6a0755aa6d769da3403e2ba87b2275d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Feb 2024 04:08:13 +0300 Subject: [PATCH 113/127] client: fix -Wparentheses --- cl_dll/ev_hldm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl_dll/ev_hldm.cpp b/cl_dll/ev_hldm.cpp index 7ece6ad2..2e6b4932 100644 --- a/cl_dll/ev_hldm.cpp +++ b/cl_dll/ev_hldm.cpp @@ -111,7 +111,7 @@ float EV_HLDM_PlayTextureSound( int idx, pmtrace_t *ptr, float *vecSrc, float *v chTextureType = 0; // Player - if( entity >= 1 && entity <= gEngfuncs.GetMaxClients() + if( ( entity >= 1 && entity <= gEngfuncs.GetMaxClients() ) || ( ( ent = gEngfuncs.GetEntityByIndex( entity )) && ( ent->curstate.eflags & EFLAG_MONSTER ))) { // hit body From 955f7ad4367c32eb6f2985daeccef08577d54822 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Tue, 13 Feb 2024 03:41:13 +0500 Subject: [PATCH 114/127] Port spectator changes from HLSDK 2.4. --- cl_dll/Android.mk | 1 + cl_dll/CMakeLists.txt | 1 + cl_dll/compile.bat | 1 + cl_dll/hud_redraw.cpp | 5 + cl_dll/hud_spectator.cpp | 371 ++++++++++++++++++++++++++++++++---- cl_dll/hud_spectator.h | 30 ++- cl_dll/interpolation.cpp | 217 +++++++++++++++++++++ cl_dll/interpolation.h | 52 +++++ cl_dll/view.cpp | 2 + common/hltv.h | 17 +- contrib/iZarif/premake5.lua | 1 + 11 files changed, 657 insertions(+), 41 deletions(-) create mode 100644 cl_dll/interpolation.cpp create mode 100644 cl_dll/interpolation.h diff --git a/cl_dll/Android.mk b/cl_dll/Android.mk index 9cb79970..9b4974ce 100755 --- a/cl_dll/Android.mk +++ b/cl_dll/Android.mk @@ -70,6 +70,7 @@ SRCS+=./in_camera.cpp SRCS+=./input.cpp SRCS+=./input_goldsource.cpp SRCS+=./input_mouse.cpp +SRCS+=./interpolation.cpp #SRCS+=./inputw32.cpp SRCS+=./menu.cpp SRCS+=./message.cpp diff --git a/cl_dll/CMakeLists.txt b/cl_dll/CMakeLists.txt index 37c03d7c..f6ecbb9f 100644 --- a/cl_dll/CMakeLists.txt +++ b/cl_dll/CMakeLists.txt @@ -107,6 +107,7 @@ set (CLDLL_SOURCES input_goldsource.cpp input_mouse.cpp input_xash3d.cpp + interpolation.cpp menu.cpp message.cpp parsemsg.cpp diff --git a/cl_dll/compile.bat b/cl_dll/compile.bat index 720d1e3f..b7085545 100644 --- a/cl_dll/compile.bat +++ b/cl_dll/compile.bat @@ -52,6 +52,7 @@ set SOURCES=../dlls/crossbow.cpp ^ input_goldsource.cpp ^ input_mouse.cpp ^ input_xash3d.cpp ^ + interpolation.cpp ^ menu.cpp ^ message.cpp ^ parsemsg.cpp ^ diff --git a/cl_dll/hud_redraw.cpp b/cl_dll/hud_redraw.cpp index 64e24e1c..59366208 100644 --- a/cl_dll/hud_redraw.cpp +++ b/cl_dll/hud_redraw.cpp @@ -87,6 +87,11 @@ void CHud::Think( void ) // only let players adjust up in fov, and only if they are not overriden by something else m_iFOV = Q_max( default_fov->value, 90 ); } + + if( gEngfuncs.IsSpectateOnly() ) + { + m_iFOV = gHUD.m_Spectator.GetFOV(); // default_fov->value; + } } // Redraw diff --git a/cl_dll/hud_spectator.cpp b/cl_dll/hud_spectator.cpp index 7e115f51..8e03579b 100644 --- a/cl_dll/hud_spectator.cpp +++ b/cl_dll/hud_spectator.cpp @@ -37,8 +37,6 @@ extern "C" float vJumpAngles[3]; extern void V_GetInEyePos( int entity, float * origin, float * angles ); extern void V_ResetChaseCam(); extern void V_GetChasePos( int target, float * cl_angles, float * origin, float * angles ); -extern void VectorAngles( const float *forward, float *angles ); -extern "C" void NormalizeAngles( float *angles ); extern float * GetClientColor( int clientIndex ); extern vec3_t v_origin; // last view origin @@ -46,6 +44,37 @@ extern vec3_t v_angles; // last view angle extern vec3_t v_cl_angles; // last client/mouse angle extern vec3_t v_sim_org; // last sim origin +#if 0 +const char *GetSpectatorLabel( int iMode ) +{ + switch( iMode ) + { + case OBS_CHASE_LOCKED: + return "#OBS_CHASE_LOCKED"; + + case OBS_CHASE_FREE: + return "#OBS_CHASE_FREE"; + + case OBS_ROAMING: + return "#OBS_ROAMING"; + + case OBS_IN_EYE: + return "#OBS_IN_EYE"; + + case OBS_MAP_FREE: + return "#OBS_MAP_FREE"; + + case OBS_MAP_CHASE: + return "#OBS_MAP_CHASE"; + + case OBS_NONE: + default: + return "#OBS_NONE"; + } +} + +#endif + void SpectatorMode( void ) { if( gEngfuncs.Cmd_Argc() <= 1 ) @@ -152,6 +181,7 @@ int CHudSpectator::Init() m_flNextObserverInput = 0.0f; m_zoomDelta = 0.0f; m_moveDelta = 0.0f; + m_FOV = 90.0f; m_chatEnabled = ( gHUD.m_SayText.m_HUD_saytext->value != 0 ); iJumpSpectator = 0; @@ -363,6 +393,178 @@ void CHudSpectator::SetSpectatorStartPosition() iJumpSpectator = 1; // jump anyway } +void CHudSpectator::SetCameraView( vec3_t pos, vec3_t angle, float fov ) +{ + m_FOV = fov; + VectorCopy( pos, vJumpOrigin ); + VectorCopy( angle, vJumpAngles ); + gEngfuncs.SetViewAngles( vJumpAngles ); + iJumpSpectator = 1; // jump anyway +} + +void CHudSpectator::AddWaypoint( float time, vec3_t pos, vec3_t angle, float fov, int flags ) +{ + if( flags == 0 && time == 0.0f ) + { + // switch instantly to this camera view + SetCameraView( pos, angle, fov ); + return; + } + + if( m_NumWayPoints >= MAX_CAM_WAYPOINTS ) + { + gEngfuncs.Con_Printf( "Too many camera waypoints!\n" ); + return; + } + + VectorCopy( angle, m_CamPath[m_NumWayPoints].angle ); + VectorCopy( pos, m_CamPath[m_NumWayPoints].position ); + m_CamPath[m_NumWayPoints].flags = flags; + m_CamPath[m_NumWayPoints].fov = fov; + m_CamPath[m_NumWayPoints].time = time; + + gEngfuncs.Con_DPrintf( "Added waypoint %i\n", m_NumWayPoints ); + + m_NumWayPoints++; +} + +void CHudSpectator::SetWayInterpolation( cameraWayPoint_t *prev, cameraWayPoint_t *start, cameraWayPoint_t *end, cameraWayPoint_t *next ) +{ + m_WayInterpolation.SetViewAngles( start->angle, end->angle ); + + m_WayInterpolation.SetFOVs( start->fov, end->fov ); + + m_WayInterpolation.SetSmoothing( ( start->flags & DRC_FLAG_SLOWSTART ) != 0, + ( start->flags & DRC_FLAG_SLOWEND ) != 0 ); + + if( prev && next ) + { + m_WayInterpolation.SetWaypoints( &prev->position, start->position, end->position, &next->position ); + } + else if( prev ) + { + m_WayInterpolation.SetWaypoints( &prev->position, start->position, end->position, NULL ); + } + else if( next ) + { + m_WayInterpolation.SetWaypoints( NULL, start->position, end->position, &next->position ); + } + else + { + m_WayInterpolation.SetWaypoints( NULL, start->position, end->position, NULL ); + } +} + +bool CHudSpectator::GetDirectorCamera( vec3_t &position, vec3_t &angle ) +{ + float now = gHUD.m_flTime; + float fov = 90.0f; + + if( m_ChaseEntity ) + { + cl_entity_t *ent = gEngfuncs.GetEntityByIndex( m_ChaseEntity ); + + if( ent ) + { + vec3_t vt = ent->curstate.origin; + + if( m_ChaseEntity <= gEngfuncs.GetMaxClients()) + { + if( ent->curstate.solid == SOLID_NOT ) + { + vt[2]+= -8 ; // PM_DEAD_VIEWHEIGHT + } + else if( ent->curstate.usehull == 1 ) + { + vt[2]+= 12; // VEC_DUCK_VIEW; + } + else + { + vt[2]+= 28; // DEFAULT_VIEWHEIGHT + } + } + + vt = vt - position; + VectorAngles( vt, angle ); + angle[0] = -angle[0]; + return true; + } + else + { + return false; + } + } + + if( !m_IsInterpolating ) + return false; + + if( m_WayPoint < 0 || m_WayPoint >= ( m_NumWayPoints - 1 )) + return false; + + cameraWayPoint_t *wp1 = &m_CamPath[m_WayPoint]; + cameraWayPoint_t *wp2 = &m_CamPath[m_WayPoint+1]; + + if( now < wp1->time ) + return false; + + while( now > wp2->time ) + { + // go to next waypoint, if possible + m_WayPoint++; + + if( m_WayPoint >= ( m_NumWayPoints - 1 )) + { + m_IsInterpolating = false; + return false; // there is no following waypoint + } + + wp1 = wp2; + wp2 = &m_CamPath[m_WayPoint + 1]; + + if( m_WayPoint > 0 ) + { + // we have a predecessor + + if( m_WayPoint < ( m_NumWayPoints - 1 )) + { + // we have also a successor + SetWayInterpolation( &m_CamPath[m_WayPoint - 1], wp1, wp2, &m_CamPath[m_WayPoint + 2] ); + } + else + { + SetWayInterpolation( &m_CamPath[m_WayPoint - 1], wp1, wp2, NULL ); + } + } + else if( m_WayPoint < ( m_NumWayPoints - 1 )) + { + // we only have a successor + SetWayInterpolation( NULL, wp1, wp2, &m_CamPath[m_WayPoint + 2] ); + } + else + { + // we have only two waypoints + SetWayInterpolation( NULL, wp1, wp2, NULL ); + } + } + + if( wp2->time <= wp1->time ) + return false; + + float fraction = ( now - wp1->time ) / ( wp2->time - wp1->time ); + + if( fraction < 0.0f ) + fraction = 0.0f; + else if( fraction > 1.0f ) + fraction = 1.0f; + + m_WayInterpolation.Interpolate( fraction, position, angle, &fov ); + + // gEngfuncs.Con_Printf( "Interpolate time: %.2f, fraction %.2f, point : %.2f,%.2f,%.2f\n", now, fraction, position[0], position[1], position[2] ); + + SetCameraView( position, angle, fov ); + + return true; +} //----------------------------------------------------------------------------- // Purpose: Loads new icons //----------------------------------------------------------------------------- @@ -377,9 +579,21 @@ int CHudSpectator::VidInit() m_hsprCamera = SPR_Load( "sprites/camera.spr" ); m_hCrosshair = SPR_Load( "sprites/crosshairs.spr" ); + m_lastPrimaryObject = m_lastSecondaryObject = 0; + m_flNextObserverInput = 0.0f; + m_lastHudMessage = 0; + m_iSpectatorNumber = 0; + iJumpSpectator = 0; + g_iUser1 = g_iUser2 = 0; + return 1; } +float CHudSpectator::GetFOV( void ) +{ + return m_FOV; +} + //----------------------------------------------------------------------------- // Purpose: // Input : flTime - @@ -427,8 +641,11 @@ int CHudSpectator::Draw( float flTime ) return 1; // make sure we have player info +#if USE_VGUI + gViewPort->GetAllPlayersInfo(); +#else gHUD.GetAllPlayersInfo(); - +#endif // loop through all the players and draw additional infos to their sprites on the map for( int i = 0; i < MAX_PLAYERS; i++ ) { @@ -461,8 +678,10 @@ int CHudSpectator::Draw( float flTime ) void CHudSpectator::DirectorMessage( int iSize, void *pbuf ) { - float value; + float f1, f2; char *string; + vec3_t v1, v2; + int i1, i2, i3; BEGIN_READ( pbuf, iSize ); @@ -479,7 +698,7 @@ void CHudSpectator::DirectorMessage( int iSize, void *pbuf ) gHUD.MsgFunc_InitHUD( NULL, 0, NULL ); gHUD.MsgFunc_ResetHUD( NULL, 0, NULL ); break; - case DRC_CMD_EVENT: + case DRC_CMD_EVENT: // old director style message m_lastPrimaryObject = READ_WORD(); m_lastSecondaryObject = READ_WORD(); m_iObserverFlags = READ_LONG(); @@ -502,19 +721,22 @@ void CHudSpectator::DirectorMessage( int iSize, void *pbuf ) } break; case DRC_CMD_CAMERA: + v1[0] = READ_COORD(); // position + v1[1] = READ_COORD(); + v1[2] = READ_COORD(); // vJumpOrigin + + v1[0] = READ_COORD(); // view angle + v1[1] = READ_COORD(); // vJumpAngles + v1[2] = READ_COORD(); + + f1 = READ_BYTE(); // fov + i1 = READ_WORD(); // target + if( m_autoDirector->value ) { - vJumpOrigin[0] = READ_COORD(); // position - vJumpOrigin[1] = READ_COORD(); - vJumpOrigin[2] = READ_COORD(); - - vJumpAngles[0] = READ_COORD(); // view angle - vJumpAngles[1] = READ_COORD(); - vJumpAngles[2] = READ_COORD(); - - gEngfuncs.SetViewAngles( vJumpAngles ); - - iJumpSpectator = 1; + SetModes( OBS_ROAMING, -1 ); + SetCameraView( v1, v2, f1 ); + m_ChaseEntity = i1; } break; case DRC_CMD_MESSAGE: @@ -551,13 +773,13 @@ void CHudSpectator::DirectorMessage( int iSize, void *pbuf ) break; case DRC_CMD_SOUND: string = READ_STRING(); - value = READ_FLOAT(); + f1 = READ_FLOAT(); - // gEngfuncs.Con_Printf("DRC_CMD_FX_SOUND: %s %.2f\n", string, value ); - gEngfuncs.pEventAPI->EV_PlaySound( 0, v_origin, CHAN_BODY, string, value, ATTN_NORM, 0, PITCH_NORM ); + // gEngfuncs.Con_Printf("DRC_CMD_FX_SOUND: %s %.2f\n", string, f1 ); + gEngfuncs.pEventAPI->EV_PlaySound( 0, v_origin, CHAN_BODY, string, f1, ATTN_NORM, 0, PITCH_NORM ); break; case DRC_CMD_TIMESCALE: - value = READ_FLOAT(); + f1 = READ_FLOAT(); break; case DRC_CMD_STATUS: READ_LONG(); // total number of spectator slots @@ -574,13 +796,71 @@ void CHudSpectator::DirectorMessage( int iSize, void *pbuf ) gViewPort->UpdateSpectatorPanel(); #endif break; - case DRC_CMD_FADE: - break; case DRC_CMD_STUFFTEXT: gEngfuncs.pfnFilteredClientCmd( READ_STRING() ); break; + case DRC_CMD_CAMPATH: + v1[0] = READ_COORD(); // position + v1[1] = READ_COORD(); + v1[2] = READ_COORD(); // vJumpOrigin + + v2[0] = READ_COORD(); // view angle + v2[1] = READ_COORD(); // vJumpAngles + v2[2] = READ_COORD(); + f1 = READ_BYTE(); // FOV + i1 = READ_BYTE(); // flags + + if( m_autoDirector->value ) + { + SetModes( OBS_ROAMING, -1 ); + SetCameraView( v1, v2, f1 ); + } + break; + case DRC_CMD_WAYPOINTS: + i1 = READ_BYTE(); + m_NumWayPoints = 0; + m_WayPoint = 0; + for( i2 = 0; i2 < i1; i2++ ) + { + f1 = gHUD.m_flTime + (float)( READ_SHORT()) / 100.0f; + + v1[0] = READ_COORD(); // position + v1[1] = READ_COORD(); + v1[2] = READ_COORD(); // vJumpOrigin + + v2[0] = READ_COORD(); // view angle + v2[1] = READ_COORD(); // vJumpAngles + v2[2] = READ_COORD(); + f2 = READ_BYTE(); // fov + i3 = READ_BYTE(); // flags + + AddWaypoint( f1, v1, v2, f2, i3 ); + } + + // gEngfuncs.Con_Printf( "CHudSpectator::DirectorMessage: waypoints %i.\n", m_NumWayPoints ); + if( !m_autoDirector->value ) + { + // ignore waypoints + m_NumWayPoints = 0; + break; + } + + SetModes( OBS_ROAMING, -1 ); + + m_IsInterpolating = true; + + if( m_NumWayPoints > 2 ) + { + SetWayInterpolation( NULL, &m_CamPath[0], &m_CamPath[1], &m_CamPath[2] ); + } + else + { + SetWayInterpolation( NULL, &m_CamPath[0], &m_CamPath[1], NULL ); + } + break; default: gEngfuncs.Con_DPrintf( "CHudSpectator::DirectorMessage: unknown command %i.\n", cmd ); + break; } } @@ -595,9 +875,10 @@ void CHudSpectator::FindNextPlayer( bool bReverse ) // if we are NOT in HLTV mode, spectator targets are set on server if( !gEngfuncs.IsSpectateOnly() ) { - char cmdstring[32]; + char cmdstring[256]; // forward command to server - sprintf( cmdstring, "follownext %i", bReverse ? 1 : 0 ); + _snprintf( cmdstring, sizeof( cmdstring ) - 1,"follownext %i", bReverse ? 1 : 0 ); + cmdstring[sizeof( cmdstring ) - 1] = '\0'; gEngfuncs.pfnServerCmd( cmdstring ); return; } @@ -614,8 +895,11 @@ void CHudSpectator::FindNextPlayer( bool bReverse ) int iDir = bReverse ? -1 : 1; // make sure we have player info +#if USE_VGUI + gViewPort->GetAllPlayersInfo(); +#else gHUD.GetAllPlayersInfo(); - +#endif do { iCurrent += iDir; @@ -656,7 +940,7 @@ void CHudSpectator::FindNextPlayer( bool bReverse ) #endif } -void CHudSpectator::FindPlayer(const char *name) +void CHudSpectator::FindPlayer( const char *name ) { // MOD AUTHORS: Modify the logic of this function if you want to restrict the observer to watching // only a subset of the players. e.g. Make it check the target's team. @@ -664,18 +948,22 @@ void CHudSpectator::FindPlayer(const char *name) // if we are NOT in HLTV mode, spectator targets are set on server if ( !gEngfuncs.IsSpectateOnly() ) { - char cmdstring[32]; + char cmdstring[256]; // forward command to server - sprintf(cmdstring,"follow %s",name); - gEngfuncs.pfnServerCmd(cmdstring); + _snprintf( cmdstring, sizeof( cmdstring ) - 1, "follow %s", name ); + cmdstring[sizeof( cmdstring ) - 1] = '\0'; + gEngfuncs.pfnServerCmd( cmdstring ); return; } g_iUser2 = 0; // make sure we have player info +#if USE_VGUI + gViewPort->GetAllPlayersInfo(); +#else gHUD.GetAllPlayersInfo(); - +#endif cl_entity_t * pEnt = NULL; for (int i = 1; i < MAX_PLAYERS; i++ ) @@ -847,12 +1135,20 @@ void CHudSpectator::SetModes( int iNewMainMode, int iNewInsetMode ) return; } - // main modes ettings will override inset window settings + m_IsInterpolating = false; + m_ChaseEntity = 0; + + // main modes settings will override inset window settings if( iNewMainMode != g_iUser1 ) { // if we are NOT in HLTV mode, main spectator mode is set on server if( !gEngfuncs.IsSpectateOnly() ) { + char cmdstring[256]; + // forward command to server + _snprintf( cmdstring, sizeof( cmdstring ) - 1,"specmode %i", iNewMainMode ); + cmdstring[sizeof( cmdstring ) - 1] = '\0'; + gEngfuncs.pfnServerCmd( cmdstring ); return; } @@ -1548,8 +1844,9 @@ void CHudSpectator::CheckSettings() if( gEngfuncs.IsSpectateOnly() ) { // tell proxy our new chat mode - char chatcmd[32]; - sprintf( chatcmd, "ignoremsg %i", m_chatEnabled ? 0 : 1 ); + char chatcmd[256]; + _snprintf( chatcmd, sizeof( chatcmd ) - 1, "ignoremsg %i", m_chatEnabled ? 0 : 1 ); + chatcmd[sizeof( chatcmd ) - 1] = '\0'; gEngfuncs.pfnServerCmd( chatcmd ); } } @@ -1626,6 +1923,12 @@ void CHudSpectator::Reset() memset( &m_OverviewEntities, 0, sizeof(m_OverviewEntities) ); + m_FOV = 90.0f; + + m_IsInterpolating = false; + + m_ChaseEntity = 0; + SetSpectatorStartPosition(); } @@ -1648,7 +1951,7 @@ void CHudSpectator::InitHUDData() Reset(); - SetModes( OBS_CHASE_FREE, INSET_OFF ); + SetModes( OBS_CHASE_LOCKED, INSET_OFF ); g_iUser2 = 0; // fake not target until first camera command diff --git a/cl_dll/hud_spectator.h b/cl_dll/hud_spectator.h index 800928e0..dc192141 100644 --- a/cl_dll/hud_spectator.h +++ b/cl_dll/hud_spectator.h @@ -10,6 +10,7 @@ #define HUD_SPECTATOR_H #include "cl_entity.h" +#include "interpolation.h" #define INSET_OFF 0 #define INSET_CHASE_FREE 1 @@ -22,6 +23,9 @@ #define OVERVIEW_TILE_SIZE 128 // don't change this #define OVERVIEW_MAX_LAYERS 1 +extern void VectorAngles( const float *forward, float *angles ); +extern "C" void NormalizeAngles( float *angles ); + //----------------------------------------------------------------------------- // Purpose: Handles the drawing of the spectator stuff (camera & top-down map and all the things on it ) //----------------------------------------------------------------------------- @@ -49,7 +53,17 @@ typedef struct overviewEntity_s double killTime; } overviewEntity_t; +typedef struct cameraWayPoint_s +{ + float time; + vec3_t position; + vec3_t angle; + float fov; + int flags; +} cameraWayPoint_t; + #define MAX_OVERVIEW_ENTITIES 128 +#define MAX_CAM_WAYPOINTS 32 class CHudSpectator : public CHudBase { @@ -73,7 +87,7 @@ public: void HandleButtonsDown( int ButtonPressed ); void HandleButtonsUp( int ButtonPressed ); void FindNextPlayer( bool bReverse ); - void FindPlayer(const char *name); + void FindPlayer( const char *name ); void DirectorMessage( int iSize, void *pbuf ); void SetSpectatorStartPosition(); int Init(); @@ -81,6 +95,13 @@ public: int Draw( float flTime ); + void AddWaypoint( float time, vec3_t pos, vec3_t angle, float fov, int flags ); + void SetCameraView( vec3_t pos, vec3_t angle, float fov ); + float GetFOV(); + bool GetDirectorCamera( vec3_t &position, vec3_t &angle ); + void SetWayInterpolation( cameraWayPoint_t *prev, cameraWayPoint_t *start, cameraWayPoint_t *end, cameraWayPoint_t *next ); + + int m_iDrawCycle; client_textmessage_t m_HUDMessages[MAX_SPEC_HUD_MESSAGES]; char m_HUDMessageText[MAX_SPEC_HUD_MESSAGES][128]; @@ -100,8 +121,13 @@ public: qboolean m_chatEnabled; + qboolean m_IsInterpolating; + int m_ChaseEntity; // if != 0, follow this entity with viewangles + int m_WayPoint; // current waypoint 1 + int m_NumWayPoints; // current number of waypoints vec3_t m_cameraOrigin; // a help camera vec3_t m_cameraAngles; // and it's angles + CInterpolation m_WayInterpolation; private: vec3_t m_vPlayerPos[MAX_PLAYERS]; @@ -119,9 +145,11 @@ private: struct model_s *m_MapSprite; // each layer image is saved in one sprite, where each tile is a sprite frame float m_flNextObserverInput; + float m_FOV; float m_zoomDelta; float m_moveDelta; int m_lastPrimaryObject; int m_lastSecondaryObject; + cameraWayPoint_t m_CamPath[MAX_CAM_WAYPOINTS]; }; #endif // SPECTATOR_H diff --git a/cl_dll/interpolation.cpp b/cl_dll/interpolation.cpp new file mode 100644 index 00000000..a06f7e29 --- /dev/null +++ b/cl_dll/interpolation.cpp @@ -0,0 +1,217 @@ +/************ (C) Copyright 2003 Valve, L.L.C. All rights reserved. *********** +** +** The copyright to the contents herein is the property of Valve, L.L.C. +** The contents may be used and/or copied only with the written permission of +** Valve, L.L.C., or in accordance with the terms and conditions stipulated in +** the agreement/contract under which the contents have been supplied. +** +******************************************************************************* +** +** Contents: +** +** interpolation.cpp: implementation of the interpolation class +** +******************************************************************************/ + +#include "hud.h" +#include "cl_util.h" +#include "interpolation.h" + +// = determinant of matrix a,b,c +#define Determinant( a, b, c ) ( (a)[2] * ( (b)[0]*(c)[1] - (b)[1]*(c)[0] ) + \ + (a)[1] * ( (b)[2]*(c)[0] - (b)[0]*(c)[2] ) + \ + (a)[0] * ( (b)[1]*(c)[2] - (b)[2]*(c)[1] ) ) + +// solve 3 vector linear system of equations v0 = x*v1 + y*v2 + z*v3 (if possible) +bool SolveLSE( vec3_t v0, vec3_t v1, vec3_t v2, vec3_t v3, float * x, float * y, float * z ) +{ + float d = Determinant( v1, v2, v3 ); + + if( d == 0.0f ) + return false; + + if( x ) + *x = Determinant( v0, v2, v3 ) / d; + + if( y ) + *y= Determinant( v1, v0, v3 ) / d; + + if( z ) + *z= Determinant( v1, v2, v0 ) / d; + + return true; +} + +// p = closest point between vector lines a1+x*m1 and a2+x*m2 +bool GetPointBetweenLines( vec3_t &p, vec3_t a1, vec3_t m1, vec3_t a2, vec3_t m2 ) +{ + float x, z; + + vec3_t t1 = CrossProduct( m1, m2 ); + vec3_t t2 = a2 - a1; + + if( !SolveLSE( t2, m1, t1, m2, &x , NULL, &z ) ) + return false; + + t1 = a1 + x * m1; + t2 = a2 + ( -z ) * m2; + + p = ( t1 + t2 ) / 2.0f; + + return true; +} + +// Bernstein Poynom B(u) with n = 2, i = 0 +#define BernsteinPolynom20(u) ( ( 1.0f - u ) * ( 1.0f - u )) +#define BernsteinPolynom21(u) ( 2.0f * u * ( 1.0f - u ) ) +#define BernsteinPolynom22(u) ( u * u ) + +CInterpolation::CInterpolation() +{ +} + +CInterpolation::~CInterpolation() +{ + m_SmoothStart = m_SmoothEnd = false; +} + +void CInterpolation::SetViewAngles( vec3_t start, vec3_t end ) +{ + m_StartAngle = start; + m_EndAngle = end; + NormalizeAngles( m_StartAngle ); + NormalizeAngles( m_EndAngle ); +} + +void CInterpolation::SetFOVs( float start, float end ) +{ + m_StartFov = start; + m_EndFov = end; +} + +void CInterpolation::SetWaypoints( vec3_t *prev, vec3_t start, vec3_t end, vec3_t *next ) +{ + m_StartPoint = start; + m_EndPoint = end; + + + vec3_t a, b, c, d; + + if( !prev && !next ) + { + // no direction given, straight linear interpolation + m_Center = ( m_StartPoint + m_EndPoint ) / 2.0f; + } + else if( !prev ) + { + a = start - end; + float dist = a.Length() / 2.0f; + a = a.Normalize(); + b = *next - end; + b = b.Normalize(); + c = a - b; + c = c.Normalize(); + m_Center = end + c * dist; + } + else if( !next ) + { + a = *prev - start; + a = a.Normalize(); + b = end - start; + float dist = b.Length() / 2.0f; + b = b.Normalize(); + c = b - a; + c = c.Normalize(); + m_Center = start + c * dist; + } + else + { + // we have a previous and a next point, great! + a = *prev - start; + a = a.Normalize(); + b = end - start; + b = b.Normalize(); + c = b - a; + + a = start - end; + a = a.Normalize(); + b = *next - end; + b = b.Normalize(); + d = a - b; + + GetPointBetweenLines( m_Center, start, c, end, d ); + } +} + +void CInterpolation::Interpolate( float t, vec3_t &point, vec3_t &angle, float *fov ) +{ + if( m_SmoothStart && m_SmoothEnd ) + { + t = ( 1.0f - t ) * ( t * t ) + t * ( 1.0f - ( ( t - 1.0f ) * ( t - 1.0f ))); + } + else if( m_SmoothStart ) + { + t = t * t; + } + else if( m_SmoothEnd ) + { + t = t - 1.0f; + t = -( t * t ) + 1; + } + + if( point ) + { + BezierInterpolatePoint( t, point ); + } + + if( angle ) + { + InterpolateAngle( t, angle ); + } + + if( fov ) + { + *fov = m_StartFov + ( t * ( m_EndFov - m_StartFov )); + } +} + +void CInterpolation::BezierInterpolatePoint( float t, vec3_t &point ) +{ + point = m_StartPoint * BernsteinPolynom20( t ); + point = point + m_Center * BernsteinPolynom21( t ); + point = point + m_EndPoint * BernsteinPolynom22( t ); +} + +void CInterpolation::SetSmoothing( bool start, bool end ) +{ + m_SmoothStart = start; + m_SmoothEnd = end; +} + +void CInterpolation::InterpolateAngle( float t, vec3_t &angle ) +{ + int i; + float ang1, ang2; + float d; + + for( i = 0; i < 3; i++ ) + { + ang1 = m_StartAngle[i]; + ang2 = m_EndAngle[i]; + + d = ang2 - ang1; + if ( d > 180 ) + { + d -= 360; + } + else if ( d < -180 ) + { + d += 360; + } + + angle[i] = ang1 + d * t; + } + + NormalizeAngles( angle ); +} + diff --git a/cl_dll/interpolation.h b/cl_dll/interpolation.h new file mode 100644 index 00000000..a0a3cd1b --- /dev/null +++ b/cl_dll/interpolation.h @@ -0,0 +1,52 @@ +/************ (C) Copyright 2003 Valve, L.L.C. All rights reserved. *********** +** +** The copyright to the contents herein is the property of Valve, L.L.C. +** The contents may be used and/or copied only with the written permission of +** Valve, L.L.C., or in accordance with the terms and conditions stipulated in +** the agreement/contract under which the contents have been supplied. +** +******************************************************************************* +** +** Contents: +** +** interpolation.h: Bezier inpolation classes +** +******************************************************************************/ + +#pragma once +#if !defined(INTERPOLATION_H) +#define INTERPOLATION_H + + +// interpolation class +class CInterpolation +{ +public: + CInterpolation(); + virtual ~CInterpolation(); + + void SetWaypoints( vec3_t *prev, vec3_t start, vec3_t end, vec3_t *next ); + void SetViewAngles( vec3_t start, vec3_t end ); + void SetFOVs( float start, float end ); + void SetSmoothing( bool start, bool end ); + + // get interpolated point 0 =< t =< 1, 0 = start, 1 = end + void Interpolate( float t, vec3_t &point, vec3_t &angle, float * fov ); + +protected: + void BezierInterpolatePoint( float t, vec3_t &point ); + void InterpolateAngle( float t, vec3_t &angle ); + + vec3_t m_StartPoint; + vec3_t m_EndPoint; + vec3_t m_StartAngle; + vec3_t m_EndAngle; + vec3_t m_Center; + float m_StartFov; + float m_EndFov; + + bool m_SmoothStart; + bool m_SmoothEnd; +}; + +#endif // INTERPOLATION_H diff --git a/cl_dll/view.cpp b/cl_dll/view.cpp index 4c93fb87..d9d61205 100644 --- a/cl_dll/view.cpp +++ b/cl_dll/view.cpp @@ -1462,6 +1462,8 @@ void V_CalcSpectatorRefdef( struct ref_params_s * pparams ) case OBS_ROAMING: VectorCopy( v_cl_angles, v_angles ); VectorCopy( v_sim_org, v_origin ); + // override values if director is active + gHUD.m_Spectator.GetDirectorCamera(v_origin, v_angles); break; case OBS_IN_EYE: V_CalcNormalRefdef( pparams ); diff --git a/common/hltv.h b/common/hltv.h index 970c4861..63393a18 100644 --- a/common/hltv.h +++ b/common/hltv.h @@ -37,11 +37,14 @@ #define DRC_CMD_SOUND 7 // plays a particular sound #define DRC_CMD_STATUS 8 // status info about broadcast #define DRC_CMD_BANNER 9 // banner file name for HLTV gui -#define DRC_CMD_FADE 10 // send screen fade command -#define DRC_CMD_SHAKE 11 // send screen shake command -#define DRC_CMD_STUFFTEXT 12 // like the normal svc_stufftext but as director command +#define DRC_CMD_STUFFTEXT 10 // like the normal svc_stufftext but as director command +#define DRC_CMD_CHASE 11 // chase a certain player +#define DRC_CMD_INEYE 12 // view player through own eyes +#define DRC_CMD_MAP 13 // show overview map +#define DRC_CMD_CAMPATH 14 // define camera waypoint +#define DRC_CMD_WAYPOINTS 15 // start moving camera, inetranl message -#define DRC_CMD_LAST 12 +#define DRC_CMD_LAST 15 // HLTV_EVENT event flags #define DRC_FLAG_PRIO_MASK 0x0F // priorities between 0 and 15 (15 most important) @@ -53,7 +56,9 @@ #define DRC_FLAG_FINAL (1<<9) // is a final scene #define DRC_FLAG_NO_RANDOM (1<<10) // don't randomize event data -#define MAX_DIRECTOR_CMD_PARAMETERS 4 -#define MAX_DIRECTOR_CMD_STRING 128 +// DRC_CMD_WAYPOINT flags +#define DRC_FLAG_STARTPATH 1 // end with speed 0.0 +#define DRC_FLAG_SLOWSTART 2 // start with speed 0.0 +#define DRC_FLAG_SLOWEND 4 // end with speed 0.0 #endif//HLTV_H diff --git a/contrib/iZarif/premake5.lua b/contrib/iZarif/premake5.lua index d959d9ad..7ad931dd 100644 --- a/contrib/iZarif/premake5.lua +++ b/contrib/iZarif/premake5.lua @@ -270,6 +270,7 @@ files{"cl_dll/hl/hl_baseentity.cpp", "cl_dll/input_goldsource.cpp", "cl_dll/input_mouse.cpp", "cl_dll/input_xash3d.cpp", +"cl_dll/interpolation.cpp", "cl_dll/menu.cpp", "cl_dll/message.cpp", "cl_dll/overview.cpp", From 7bbc2cf4d72deb7ede395816910bc7de7f03f13d Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Tue, 13 Feb 2024 05:30:42 +0500 Subject: [PATCH 115/127] Update weapon bobbing code. --- cl_dll/view.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/cl_dll/view.cpp b/cl_dll/view.cpp index d9d61205..41389296 100644 --- a/cl_dll/view.cpp +++ b/cl_dll/view.cpp @@ -295,8 +295,11 @@ void V_CalcGunAngle( struct ref_params_s *pparams ) viewent->angles[PITCH] -= v_idlescale * sin( pparams->time * v_ipitch_cycle.value ) * ( v_ipitch_level.value * 0.5f ); viewent->angles[YAW] -= v_idlescale * sin( pparams->time * v_iyaw_cycle.value ) * v_iyaw_level.value; - VectorCopy( viewent->angles, viewent->curstate.angles ); - VectorCopy( viewent->angles, viewent->latched.prevangles ); + if( !( cl_viewbob && cl_viewbob->value )) + { + VectorCopy( viewent->angles, viewent->curstate.angles ); + VectorCopy( viewent->angles, viewent->latched.prevangles ); + } } /* @@ -575,6 +578,7 @@ void V_CalcNormalRefdef( struct ref_params_s *pparams ) { VectorCopy( pparams->cl_viewangles, view->angles ); } + // set up gun position V_CalcGunAngle( pparams ); @@ -597,9 +601,6 @@ void V_CalcNormalRefdef( struct ref_params_s *pparams ) view->angles[ROLL] -= bob * 1.0f; view->angles[PITCH] -= bob * 0.3f; - if( cl_viewbob && cl_viewbob->value ) - VectorCopy( view->angles, view->curstate.angles ); - // pushing the view origin down off of the same X/Z plane as the ent's origin will give the // gun a very nice 'shifting' effect when the player looks up/down. If there is a problem // with view model distortion, this may be a cause. (SJB). @@ -768,6 +769,14 @@ void V_CalcNormalRefdef( struct ref_params_s *pparams ) } } + if( cl_viewbob && cl_viewbob->value ) + { + VectorCopy( view->origin, view->curstate.origin ); + VectorCopy( view->origin, view->latched.prevorigin ); + VectorCopy( view->angles, view->curstate.angles ); + VectorCopy( view->angles, view->latched.prevangles ); + } + lasttime = pparams->time; v_origin = pparams->vieworg; From c949e02e4eca3c30ca8536c5b3d2e5e6f9b838bd Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Tue, 13 Feb 2024 18:42:37 +0500 Subject: [PATCH 116/127] Fix death animation loop. --- dlls/player.cpp | 14 +++++++------- dlls/world.cpp | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/dlls/player.cpp b/dlls/player.cpp index 04d161cc..2911363f 100644 --- a/dlls/player.cpp +++ b/dlls/player.cpp @@ -1296,7 +1296,7 @@ void CBasePlayer::PlayerDeathThink( void ) PackDeadPlayerItems(); } - if( pev->modelindex && ( !m_fSequenceFinished ) && ( pev->deadflag == DEAD_DYING ) ) + if( pev->modelindex && ( !m_fSequenceFinished ) && ( pev->deadflag == DEAD_DYING )) { StudioFrameAdvance(); @@ -1305,14 +1305,9 @@ void CBasePlayer::PlayerDeathThink( void ) return; } - // once we're done animating our death and we're on the ground, we want to set movetype to None so our dead body won't do collisions and stuff anymore - // this prevents a bug where the dead body would go to a player's head if he walked over it while the dead player was clicking their button to respawn - if( pev->movetype != MOVETYPE_NONE && FBitSet( pev->flags, FL_ONGROUND ) ) - pev->movetype = MOVETYPE_NONE; - if( pev->deadflag == DEAD_DYING ) { - if( g_pGameRules->IsMultiplayer() && pev->movetype == MOVETYPE_NONE ) + if( g_pGameRules->IsMultiplayer() && m_fSequenceFinished && pev->movetype == MOVETYPE_NONE ) { CopyToBodyQue( pev ); pev->modelindex = 0; @@ -1320,6 +1315,11 @@ void CBasePlayer::PlayerDeathThink( void ) pev->deadflag = DEAD_DEAD; } + // once we're done animating our death and we're on the ground, we want to set movetype to None so our dead body won't do collisions and stuff anymore + // this prevents a bug where the dead body would go to a player's head if he walked over it while the dead player was clicking their button to respawn + if( pev->movetype != MOVETYPE_NONE && FBitSet( pev->flags, FL_ONGROUND ) ) + pev->movetype = MOVETYPE_NONE; + StopAnimation(); pev->effects |= EF_NOINTERP; diff --git a/dlls/world.cpp b/dlls/world.cpp index 3f4bda0b..d96c10aa 100644 --- a/dlls/world.cpp +++ b/dlls/world.cpp @@ -226,7 +226,7 @@ static void InitBodyQue( void ) // void CopyToBodyQue( entvars_t *pev ) { - if( pev->effects & EF_NODRAW ) + if( ( pev->effects & EF_NODRAW ) || !pev->modelindex ) return; entvars_t *pevHead = VARS( g_pBodyQueueHead ); From d5a8a0d3a36144af8d5cd5bb5bb3d02c3f9e01bd Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Tue, 13 Feb 2024 20:44:58 +0500 Subject: [PATCH 117/127] Clear hitgroup for reflected gauss shots. --- dlls/gauss.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/gauss.cpp b/dlls/gauss.cpp index a997c804..734950f4 100644 --- a/dlls/gauss.cpp +++ b/dlls/gauss.cpp @@ -430,6 +430,8 @@ void CGauss::Fire( Vector vecOrigSrc, Vector vecDir, float flDamage ) if( pEntity->pev->takedamage ) { ClearMultiDamage(); + if( pEntity->pev == m_pPlayer->pev ) + tr.iHitgroup = 0; pEntity->TraceAttack( m_pPlayer->pev, flDamage, vecDir, &tr, DMG_BULLET ); ApplyMultiDamage( m_pPlayer->pev, m_pPlayer->pev ); } From 835ce1df212f16ec57e2d37e90a70eedad7aab2b Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Tue, 13 Feb 2024 22:57:13 +0500 Subject: [PATCH 118/127] cmake: add more checks for x86 CPU. --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 19f76e64..84897728 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,7 +76,10 @@ if((WIN32 OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") OR CMAKE_SYSTEM_PROCESSOR STREQUAL "EM64T") AND NOT 64BIT) OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "X86" - OR CMAKE_SYSTEM_PROCESSOR STREQUAL "i386")) + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "i386" + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "i486" + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "i586" + OR CMAKE_SYSTEM_PROCESSOR STREQUAL "i686")) option(GOLDSOURCE_SUPPORT "Build goldsource compatible client library" ON) else() option(GOLDSOURCE_SUPPORT "Build goldsource compatible client library" OFF) From 1f458edb0be871e729a475228ba025583bd4ee52 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Tue, 13 Feb 2024 23:03:14 +0500 Subject: [PATCH 119/127] Turn on weapon bobbing by default. --- cl_dll/hud.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl_dll/hud.cpp b/cl_dll/hud.cpp index f16e30b9..e82679bf 100644 --- a/cl_dll/hud.cpp +++ b/cl_dll/hud.cpp @@ -380,7 +380,7 @@ void CHud::Init( void ) m_pCvarStealMouse = CVAR_CREATE( "hud_capturemouse", "1", FCVAR_ARCHIVE ); m_pCvarDraw = CVAR_CREATE( "hud_draw", "1", FCVAR_ARCHIVE ); cl_lw = gEngfuncs.pfnGetCvarPointer( "cl_lw" ); - cl_viewbob = CVAR_CREATE( "cl_viewbob", "0", FCVAR_ARCHIVE ); + cl_viewbob = CVAR_CREATE( "cl_viewbob", "1", FCVAR_ARCHIVE ); m_pSpriteList = NULL; From 738890287bb9e363631313fddb04403c8e675087 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Wed, 14 Feb 2024 04:14:02 +0500 Subject: [PATCH 120/127] Add kludge for Uplink's scientist model. --- dlls/scientist.cpp | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/dlls/scientist.cpp b/dlls/scientist.cpp index 15acf989..7fb7bab5 100644 --- a/dlls/scientist.cpp +++ b/dlls/scientist.cpp @@ -29,6 +29,8 @@ #define NUM_SCIENTIST_HEADS 4 // four heads available for scientist model +static cvar_t *g_psv_override_scientist_mdl; + enum { HEAD_GLASSES = 0, @@ -113,6 +115,8 @@ public: CUSTOM_SCHEDULES private: + const char *GetScientistModel( void ); + float m_painTime; float m_healTime; float m_fearTime; @@ -422,6 +426,20 @@ void CScientist::DeclineFollowing( void ) PlaySentence( "SC_POK", 2, VOL_NORM, ATTN_NORM ); } +const char *CScientist::GetScientistModel( void ) +{ + if( !g_psv_override_scientist_mdl ) + g_psv_override_scientist_mdl = CVAR_GET_POINTER( "_sv_override_scientist_mdl" ); + + if( !( g_psv_override_scientist_mdl && g_psv_override_scientist_mdl->string )) + return "models/scientist.mdl"; + + if( strlen( g_psv_override_scientist_mdl->string ) < sizeof( "01.mdl" ) - 1 ) + return "models/scientist.mdl"; + + return g_psv_override_scientist_mdl->string; +} + void CScientist::Scream( void ) { if( FOkToSpeak() ) @@ -648,7 +666,7 @@ void CScientist::Spawn( void ) Precache(); - SET_MODEL( ENT( pev ), "models/scientist.mdl" ); + SET_MODEL( ENT( pev ), GetScientistModel()); UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX ); pev->solid = SOLID_SLIDEBOX; @@ -679,7 +697,7 @@ void CScientist::Spawn( void ) //========================================================= void CScientist::Precache( void ) { - PRECACHE_MODEL( "models/scientist.mdl" ); + PRECACHE_MODEL( GetScientistModel()); PRECACHE_SOUND( "scientist/sci_pain1.wav" ); PRECACHE_SOUND( "scientist/sci_pain2.wav" ); PRECACHE_SOUND( "scientist/sci_pain3.wav" ); @@ -1097,6 +1115,9 @@ public: void KeyValue( KeyValueData *pkvd ); int m_iPose;// which sequence to display static const char *m_szPoses[7]; + +private: + const char *GetScientistModel( void ); }; const char *CDeadScientist::m_szPoses[] = @@ -1127,8 +1148,10 @@ LINK_ENTITY_TO_CLASS( monster_scientist_dead, CDeadScientist ) // void CDeadScientist::Spawn() { - PRECACHE_MODEL( "models/scientist.mdl" ); - SET_MODEL( ENT( pev ), "models/scientist.mdl" ); + const char *pszModel = GetScientistModel(); + + PRECACHE_MODEL( pszModel ); + SET_MODEL( ENT( pev ), pszModel ); pev->effects = 0; pev->sequence = 0; @@ -1160,6 +1183,20 @@ void CDeadScientist::Spawn() MonsterInitDead(); } +const char *CDeadScientist::GetScientistModel( void ) +{ + if( !g_psv_override_scientist_mdl ) + g_psv_override_scientist_mdl = CVAR_GET_POINTER( "_sv_override_scientist_mdl" ); + + if( !( g_psv_override_scientist_mdl && g_psv_override_scientist_mdl->string )) + return "models/scientist.mdl"; + + if( strlen( g_psv_override_scientist_mdl->string ) < sizeof( "01.mdl" ) - 1 ) + return "models/scientist.mdl"; + + return g_psv_override_scientist_mdl->string; +} + //========================================================= // Sitting Scientist PROP //========================================================= From e0cdfe9ae869fd4b53d97eedd4ea338ebf3214c7 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Wed, 14 Feb 2024 04:33:36 +0500 Subject: [PATCH 121/127] Add Steam Friends Rich Presence cmd calls. --- cl_dll/hud.cpp | 2 ++ cl_dll/hud_msg.cpp | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/cl_dll/hud.cpp b/cl_dll/hud.cpp index e82679bf..86f0f6cd 100644 --- a/cl_dll/hud.cpp +++ b/cl_dll/hud.cpp @@ -428,6 +428,8 @@ void CHud::Init( void ) m_Menu.Init(); MsgFunc_ResetHUD( 0, 0, NULL ); + ClientCmd( "richpresence_gamemode\n" ); + ClientCmd( "richpresence_update\n" ); } // CHud destructor diff --git a/cl_dll/hud_msg.cpp b/cl_dll/hud_msg.cpp index 7cc2fbad..3c3b6f52 100644 --- a/cl_dll/hud_msg.cpp +++ b/cl_dll/hud_msg.cpp @@ -87,6 +87,11 @@ int CHud::MsgFunc_GameMode( const char *pszName, int iSize, void *pbuf ) BEGIN_READ( pbuf, iSize ); m_Teamplay = READ_BYTE(); + if( m_Teamplay ) + ClientCmd( "richpresence_gamemode Teamplay\n" ); + else + ClientCmd( "richpresence_gamemode\n" ); + ClientCmd( "richpresence_update\n" ); return 1; } From 7223e0d6c15cede7fd1e93e57d8f0f25a5e7c678 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 19 Feb 2024 01:46:19 +0500 Subject: [PATCH 122/127] Fix C functions declarations. --- pm_shared/pm_shared.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pm_shared/pm_shared.c b/pm_shared/pm_shared.c index dec69184..b8112d7e 100644 --- a/pm_shared/pm_shared.c +++ b/pm_shared/pm_shared.c @@ -167,7 +167,7 @@ void PM_SortTextures( void ) } } -void PM_InitTextureTypes() +void PM_InitTextureTypes( void ) { char buffer[512]; int i, j; @@ -718,7 +718,7 @@ PM_CheckVelocity See if the player has a bogus velocity value. ================ */ -void PM_CheckVelocity() +void PM_CheckVelocity( void ) { int i; @@ -795,7 +795,7 @@ int PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) return blocked; } -void PM_AddCorrectGravity() +void PM_AddCorrectGravity( void ) { float ent_gravity; @@ -816,7 +816,7 @@ void PM_AddCorrectGravity() PM_CheckVelocity(); } -void PM_FixupGravityVelocity() +void PM_FixupGravityVelocity( void ) { float ent_gravity; @@ -1070,7 +1070,7 @@ PM_WalkMove Only used by players. Moves along the ground when player is a MOVETYPE_WALK. ====================== */ -void PM_WalkMove() +void PM_WalkMove( void ) { //int clip; int oldonground; @@ -1511,7 +1511,7 @@ PM_CheckWater Sets pmove->waterlevel and pmove->watertype values. ============= */ -qboolean PM_CheckWater() +qboolean PM_CheckWater( void ) { vec3_t point; int cont; @@ -2259,7 +2259,7 @@ PM_AddGravity ============ */ -void PM_AddGravity() +void PM_AddGravity( void ) { float ent_gravity; @@ -2309,7 +2309,7 @@ PM_Physics_Toss() Dead player flying through air., e.g. ============ */ -void PM_Physics_Toss() +void PM_Physics_Toss( void ) { pmtrace_t trace; vec3_t move; @@ -2410,7 +2410,7 @@ PM_NoClip ==================== */ -void PM_NoClip() +void PM_NoClip( void ) { int i; vec3_t wishvel; From c7b141d67d9bfb443dae23f4a98f0bf68e73967d Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 19 Feb 2024 01:54:43 +0500 Subject: [PATCH 123/127] Fix alien slave beams staying forever if they exist during a level change. Same as https://github.com/twhl-community/halflife-updated/commit/ac808a876977f780e3753a520d227172fad866c2. --- dlls/islave.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dlls/islave.cpp b/dlls/islave.cpp index 577eda24..ba194cea 100644 --- a/dlls/islave.cpp +++ b/dlls/islave.cpp @@ -732,6 +732,7 @@ void CISlave::ArmBeam( int side ) m_pBeam[m_iBeams]->SetColor( 96, 128, 16 ); m_pBeam[m_iBeams]->SetBrightness( 64 ); m_pBeam[m_iBeams]->SetNoise( 80 ); + m_pBeam[m_iBeams]->pev->spawnflags |= SF_BEAM_TEMPORARY; // Flag these to be destroyed on save/restore or level transition m_iBeams++; } @@ -776,6 +777,7 @@ void CISlave::WackBeam( int side, CBaseEntity *pEntity ) m_pBeam[m_iBeams]->SetColor( 180, 255, 96 ); m_pBeam[m_iBeams]->SetBrightness( 255 ); m_pBeam[m_iBeams]->SetNoise( 80 ); + m_pBeam[m_iBeams]->pev->spawnflags |= SF_BEAM_TEMPORARY; // Flag these to be destroyed on save/restore or level transition m_iBeams++; } @@ -806,6 +808,7 @@ void CISlave::ZapBeam( int side ) m_pBeam[m_iBeams]->SetColor( 180, 255, 96 ); m_pBeam[m_iBeams]->SetBrightness( 255 ); m_pBeam[m_iBeams]->SetNoise( 20 ); + m_pBeam[m_iBeams]->pev->spawnflags |= SF_BEAM_TEMPORARY; // Flag these to be destroyed on save/restore or level transition m_iBeams++; pEntity = CBaseEntity::Instance( tr.pHit ); From f58544a1568f6abbbe28bf3c5c94b5b967e4f69c Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 19 Feb 2024 02:07:20 +0500 Subject: [PATCH 124/127] Fix save game system not saving arrays of EHANDLEs if the first half of the array contains null handles. Same as https://github.com/twhl-community/halflife-updated/commit/d5d96d38830be1f9d41427c89d5a600fc778ff30. --- dlls/util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/util.cpp b/dlls/util.cpp index 6cfebc10..56819f44 100644 --- a/dlls/util.cpp +++ b/dlls/util.cpp @@ -2223,7 +2223,7 @@ int CRestore::ReadField( void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCou break; case FIELD_EHANDLE: // Input and Output sizes are different! - pOutputData = (char *)pOutputData + j * ( sizeof(EHANDLE) - gSizes[pTest->fieldType] ); + pInputData = (char*)pData + j * gSizes[pTest->fieldType]; entityIndex = *(int *)pInputData; pent = EntityFromIndex( entityIndex ); if( pent ) From da9ce1e9c3bd3d26e2fd091cef7ea3800cfdd51b Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 19 Feb 2024 02:23:22 +0500 Subject: [PATCH 125/127] Disable jump sounds while player is frozen. Same as https://github.com/twhl-community/halflife-updated/commit/f3810c7107c9ea686a043c28a7a4ceb2e6fb92f7. --- pm_shared/pm_shared.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pm_shared/pm_shared.c b/pm_shared/pm_shared.c index b8112d7e..922c7ca6 100644 --- a/pm_shared/pm_shared.c +++ b/pm_shared/pm_shared.c @@ -2570,13 +2570,17 @@ void PM_Jump( void ) if( !bunnyjump ) PM_PreventMegaBunnyJumping(); - if( tfc ) + // Don't play jump sounds while frozen. + if( !( pmove->flags & FL_FROZEN )) { - pmove->PM_PlaySound( CHAN_BODY, "player/plyrjmp8.wav", 0.5, ATTN_NORM, 0, PITCH_NORM ); - } - else - { - PM_PlayStepSound( PM_MapTextureTypeStepType( pmove->chtexturetype ), 1.0f ); + if( tfc ) + { + pmove->PM_PlaySound( CHAN_BODY, "player/plyrjmp8.wav", 0.5, ATTN_NORM, 0, PITCH_NORM ); + } + else + { + PM_PlayStepSound( PM_MapTextureTypeStepType( pmove->chtexturetype ), 1.0f ); + } } // See if user can super long jump? From c3d9f27bcf736a1b0494e66779e0e99e5a25c9cb Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 19 Feb 2024 03:33:51 +0500 Subject: [PATCH 126/127] Properly set up hand grenade sequence. Same as https://github.com/twhl-community/halflife-updated/commit/2ee36b472fcb43b18fb34fe56125e9fcac74704c. --- dlls/ggrenade.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dlls/ggrenade.cpp b/dlls/ggrenade.cpp index 5995fe53..a4552d36 100644 --- a/dlls/ggrenade.cpp +++ b/dlls/ggrenade.cpp @@ -267,6 +267,7 @@ void CGrenade::BounceTouch( CBaseEntity *pOther ) pev->velocity = pev->velocity * 0.8f; pev->sequence = RANDOM_LONG( 1, 1 ); + ResetSequenceInfo(); } else { @@ -410,7 +411,9 @@ CGrenade *CGrenade::ShootTimed( entvars_t *pevOwner, Vector vecStart, Vector vec pGrenade->pev->velocity = Vector( 0, 0, 0 ); } + SET_MODEL( ENT( pGrenade->pev ), "models/w_grenade.mdl" ); pGrenade->pev->sequence = RANDOM_LONG( 3, 6 ); + pGrenade->ResetSequenceInfo(); pGrenade->pev->framerate = 1.0f; // Tumble through the air @@ -419,7 +422,6 @@ CGrenade *CGrenade::ShootTimed( entvars_t *pevOwner, Vector vecStart, Vector vec pGrenade->pev->gravity = 0.5f; pGrenade->pev->friction = 0.8f; - SET_MODEL( ENT( pGrenade->pev ), "models/w_grenade.mdl" ); pGrenade->pev->dmg = 100; return pGrenade; From 7e21ac28893d5f0e7044fc36fceacf9cb685bbfd Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Tue, 20 Feb 2024 05:12:37 +0500 Subject: [PATCH 127/127] Remove useless label. --- dlls/player.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/dlls/player.cpp b/dlls/player.cpp index 2911363f..eebbc908 100644 --- a/dlls/player.cpp +++ b/dlls/player.cpp @@ -800,7 +800,6 @@ void CBasePlayer::PackDeadPlayerItems( void ) iPW++; } -end: pWeaponBox->pev->velocity = pev->velocity * 1.2f;// weaponbox has player's velocity, then some. } RemoveAllItems( TRUE );// now strip off everything that wasn't handled by the code above.

Ua(K9y3ut|~rO_+KHuOW8{R#_% zt(AcoZ?c&1uWOU-l2z7MZQ{t1nrHO-c;=p6n|ZkE0~2#56Iviq@ii=VJdIdi>b@y;iIoZt%@Aq^TjlTJ-pvl!*RN{lM6A%HMi7XDRlJ=bD~rlrQs{(3`BOu;{PHOi-W-T6^ABRWMF` z?3*+Ze`79n<}B5xeMg7dO`^kl?L^DLF`mJR-J6vQ5A>vh-c0JeNK6f3qC~(u!=A_K zENi=qWqS^Kj227xy5>Hj68SiwiP?+6-4z$PlhI$y8=Ja3SvlOa_HGWVe0x66)tG1l zavQHk+r9wwBtNLP`}vT`p7U5$k(0 z^hJfbUBa)7@r|pE0q(v{fro`KS!Fz%K?60Q)f>;EmTF9HW3eT2`1k$EotyRo6y!5J zK0wJNjv4#-ZJ3DbpT2<+YkT2f5HqL9wJlxQpMh{;q`r?a6^x??=I25x%<8bYhIU)$ z*b&{HJ2_C>3B57Sp#$S*J#vYjh~A^syu=TZ8XONgD@t9i$2ODpDVG!e*wKcn*U8SB zE$Ec-^g~iN5DCD6p_f*O@})^0%7w$?>{r&BR-L>TTe>GAvuf$7*gW(<5v);i>wShJ5>e$y{v-}Zdw5LmT}!?&+G9PUE^_xJG( z&|rhSc}1rdW(8pp30_w&O*vs1j~ffAFsU4-1#-{x?6~^@WUQg?=7vx|NaN9%F=fc7 zVr&XKUDhch+CH^(qluW9Og`SkZgv+^bo^%Q@`Hq*i5y!4!`P!_De5!y+Chn`RPe6f z($EBv3icLib0HHvX|tMrW4PY3p#Lt&h|k@2)9hQTxJeP|9Vb4d^QxhvzKUE`F8B=>IP9e0VJ?axv1f6uZNIndqkou)voA!s+Hfg3pz^cPmKob-5>bMPtOTQAA>#mz zO;hnV-+cMFrHUO-Dm{MapB-lzymOTtgmWile9x|8aTN#X-k(-r7bPDXHE^_v27oWn zV{SiyusKD9nEu@7d+fQu!`{5MxYu~rZMgb`MO=H|-xn!5pl-R~8L53L%-q1Z6-`W+vB{V@M``!r<-+n zl5uPK2BTi4aS|OyRJXH}&T;-SL#JVpnw~e>SNO1a6nE*nwVt3E$LWn59~pn0Mh-R^ z7qJr-gez1slcVLFqUV0kM5Y-mK6J~E$M+wmCCADAlYk=);}&CRL=V+jO9}hMSv)$@ zg@{DTYiJ1Rw0#W2udF5{cR2K#{VsGp8q^~b7je)g)rYMnZPuPGhRLae{mMs&GcPv_ z($6^lHkGp$RydbUC)4)jyhGY%>`5sTlC#t@PQHhiupD;iauvo7HBo;mQ%;p6*G+gI z2ya=ke|rrLRXqYb9l2mfGJ+$VI-@f+EC)S|-`8=}E*Ry0w2|>!1_sq^0QOe7e@zBeEBKixN z(8KMrM!n$R8!>UFD3$ssyoF9JaskA>cuRN}3= z?xXTbMTHjCf}RFM2F3gpO3t;azIU>R#-kmgt0seMx7uSUw!JlvDF3eXUP??+Dz2$~;Mk|vk1 zwFsWZf|fN>DFf2(XEy7=029E>NPQ=;~|ZOa@Ta#_zqr4qG4KhTGQ9syPaQ# zltTrT&G&D1u6-h|G*6x>E}MUzuerrtA$X3twCaV-R4blusNRd4kLXF99d~ea9!o&0 zuW$z9?z-CF)8izyU&D`PKfrzTv#W_{J7471;-fKdNVN?^jb|X=NRj0FnQjjO_r+)X z+$_6OgO=*b&Jr1B#Z(Sb(KON2b`Eo`(<_Q=GMA+i*t)IyV%C*QiaZ>D#kt#WT-&os zyhGie_ql;b;Q$cMLAprQKrZ+(Sx{%hXPSk0vlramT(mdGs^h!~QN*Is#Ntuz~81Tฬ$&7_%XR(`T@P1P~P^?jw?#<8~s5cVD#^mcwBqd(VN z?{>b#j;Uy1uU@RvI5O6(STZ$=)u(vV$6cCP=H4B5)IH!eigk6#g7|HM<*kQl5`itt zMkwP~%NPFT>DU`QQ^;U10iygi%e`-dz=Ew1P&ZX=tn}p={75{l8b{PK6Kdn|tIoE5 z2g=6=uy)^;(mS<|qJ0W5XDQj7F* z%Xko4jyv4rzBZy|V&!^$nWLf2U41#!%~CxGEI5JI*c+;C9DZzV*_t5`rm|etST&Xz z8eXNhbV_7E9X-&IDoXLo@x6PVjMg6yABTA5dqPsK+x!gBAt@G*x>)L~=;4Bnp{Z2o z8Tk-hth|W7%)~-pg`5lZ6s3lA>c$%2MI>KWS66BZ zRb~%XS}$Pry&Y-K?`6y$zqWMsL}kdcX2jG>{z(Xnl{=W;G@ooS6GGA9!P62tPZvV9 zVz>4|&eu(I_T%6GrvhrL+t(W_$f?Qi?jLPUK~v%`9?|7z~25}sPb$8Z`E zucS7PPFcS&_2)Q;aS1LThUyCdZsJGb2C4aFT0so)?5JIp3Et^KOfMzR5MwPm84h- zbZ(`zQhpKaMbEo;J6Gl%p%j zD3i|iL~5XKcYSB`e7i*Y{!?apJbY_<=`m_OBrRGKllfQg+}$aggL$t@)B=%7NaD|B zE}E9NuRNACCORIOeO4|CnKy2iLlVP&XR(^&#?i;K~DhN&Nue(g}@P5k{AuTnvL zwlI-(oYbI7Y!;c;p3#|HW8@fZs`$vV@_S;~vH8`y(RAtwl1k=`y5v4g9bb5u1D3GY z&3bY&gD`qD-l}KEd3|7)gp2g)qufP3+Yz}{#T$BJEl9MX589kYJLq})ZN1B9d6h8gc;{M?{aLY zrh|VQLh^MH*tN9G>DutVqU$GGv5z+fiTQ!VSWK9wS)bQqq~3dT)6aq^@Ba#5cv<$QWHPulJ9<;TE%IAWr^xtDWLLz{ zf<0;50ep$$G_@0_J?^%Dapl}fMatKeyu(!U(Oa?nu61JO#h2C_Ezgr~4@4GPZw7iQ zqi>grP$x0Q6$~ttUvcwjpmlS+cc@SSr?xBt=lEc!YnWf2rw+PR8#GVm6#=pYuZAD{rQ@za8;#DR%T8o_l2Vow*QgZK1L8 zb;eimr$AgD_!_^Tc1F+h`P@Mll2=@9X|LBydTMZ>Ed< zZQUznM$2d0mYDR{5&e;aodwoA9m91*%lwLz&}YDJ80f*=Hcl2Ao<;ZnQaO&7**_q7 znB04VGs+qFIZrpov8(Iw@ljI@h`c?wb@^c1%WUxVBaw|CuGaFi1t@Qz6o}^0kw;0( zC{YWe(`)CSDg5SPfrw#SxFW6s&-%d%!TBf=;cbzVuQ znc*MF=bvGPF)WhrPc_RqzDj?Qe(U@n(PwCnfN``?d$V;ioy;S;tH8rWx~@6Cg|i!P z<>zq{rxj`h%`sFPxLT==EXSPe9SucUmVCIK4-wzOiojNdw-2v=fuke6D&z{%PJWQh zQ(}n;cbL{o8_zPGokVA;{hg6Y6+{6w*j)gE#mIyT?6k=rja-f+=hB3?j*SnN$8Qa` z$~?2Ov>BhI_W>L^tG7Cm7f@_zpEEiq+q2$zM8ShaEq*l#}-5lbecBiN0_iC2YEaw6qd6 zqjjx>GMkN#dk#l8I=_xfTRR6Qd%CCSk~}0Wr*xJr9Tx*1!6)nr_?pOHz$#0B+My}vmlk6AvF zb32Yx%!cP2-~5KqSdx;l?mAzpZb)MH%Iz;Yk(yQW8hXZg?`oB3gsDQqe^>FzrZy9{py1gDfagaW#vQ7Jf+1EQQd zEZbptgKeXC4H4WDHhvQt8%(h#%DQ!2s0aAl=#=wVUm51}w{{4P`&EJF`PO;kC25W#OJpL<%N&B<7DQZR% zStQAkv2FR1G^k*ocmE}N1Y&Wc~a1 zWtDEIYx%~0$T)=!`njb~?kQf@K||w#z|`y{Ifnv?;v+Blx2Tq!DGt9ZC8 ztEoo7WrN*J0wO+z%0cMApZwaWRQ!E`O0|T*LhW_bZSQlHh==}UX8`dCK_CQNwJ(96 z{^ImW8r2sBNgn79)vRUr_6)%Zi$nB@q4$Kx-GbmJ-z9!Y*1HC$&!&k2#M)|ganYgy zu0v;2D1eJTemt2t}i)zP+;`M-T@jgJnMqJa@@WbTI$2 zjbtH;ZAdl?^B2Y87hVS)O%GF)A7(+6>yfv{3bf&v_;L4v?Eo!Be4g+YE#3;z z;@XTsB8L2ti5HFAuJImz3dB#ak$B$ArN9)9ApG#%RCLd;8aDJ(!aa~FGZZE$Ge|ZO z^Osp>Rycn=o{#L6qY!{p)j1usFvb!NCYPq?_^PSWAU?Ocva1xTmj}H0y==Kq@9}Wr z!c)>g%rY0#!1lZrExlROPRXUR^a^bR(H9)|6o$CFHQ)DnboV6d{}~>rBL2RSqTw@= z@I8vDHN&>k&e6(iuh0^Rp<3kmKqZd1c4U(>!;XDF|7@_eZM0W6=}Umz*O-W?+4y^c zGq)Z~#7Q6GiRVd0``^`UJ$u{Je)2Qs44wAeLsO?`pH9@LuBNWHHjVJx&C=eo6N-yq zn|mOyJt-q%LD1*;GBX?U8oib(E6uu)8veb=($&sW(g&R?@ZL=3vuu%HZY1eqQA5a& zub*dWKg$(~p|pO`dfrUAb@eT843ky{9S9drvDq(_<;*2&^@=y%TJFpi#a7OT=Dwnn zIdvBOhLj+qBXWNz>3*q>_p_+skmrTfiy_CAru)aMJ~Vthz4{_qG`*&6Q&>64;-GV~ zis*b|_SjvOr31^U#TlNb<{~&E935l`CZXrUG-G9kXkV>(IVeQEvqf; zM%)|4s!zF7Q5?Tqveu;02ECDP52MwT@^pengM#>{GG{j1;xMa$<|Rxe)6HBG)jyP1 zJ~2h3rmV6Hg3GL@HTcHS>K^MTt{NHz!IrW-#srHz)Pw9!AL}iNN{63o#DMNQ%Uuz? zk0+rMSs_VCPzz+UA}H4u-nWsEUYW<|8pDXFN z{Spw1z3Iv`)xR328@+1WmpJDex-wfI>$uRyxDuVbpppI4XM9egvR}Ao^J|;W4W5TL z2U9xVGEJ?D@PDYvwX-AGwu`$t8tD~UXw_7IS~cmX;iz);5AjvZHa;JSy!2*8Ut5ZV zg%%f~i_=zcwWM3))$C7CBFP%V5}o-j5f@6aXoyT?-`hi zFj(~3fs;GZRs-Z84o?XuvfQ^VhLhq2SpwKna7zFuVqJc`Oy; z@OX^qwT%P8UJ3;02&e#e_drz^&z6GU@t239j=~p)rUtT>jy>tNo^ln2;9s;_0Ze>J zp+f^d9tB+XTnWG)nEBcPJzyKM1O>aQEfRr^#jWshK(NVbbt^(qtl%B|ADk}Ze>si0 zj1zJXf?h6ZA3Et?m+LnF2zDB$B=qKHQPDGn6;d?O7p!^P*GKPVhK54_cG&Eh-xTo% zBmYN-VUK^Ns4L5>;3uyjG}U`s5`>pCosQVgzY_qkg~;aF`2g?&0XpcUdyp@bUj@Vr z@%yuo$?b7xI_Ja$$7F1DHerToCK_=Vs5*aQ$B1=UP#7Ii1Xle|BxeFKldNYVD^1t; z{a4ti<6ad)xS#)N6JXK}ZK%4|m;pMu;24@1Sshr$p$5~zlN4TiL#U_}Y*#OmRj_K^ z@1Z5fCfv8cvG!>F&ql*}RCwX6NZ_XrD&Nq}-np#^tcSt2cQVK>+|D47X;P>xK=2ym z;fzz;z5H&gZ@0Oq%5QF)EFd+I-wl>@uPp%=pCRV7gR}#a0)RKI=}kMtQXMTp?wJ79 z@lYu8Rp>PS^3=c|(3SGafdI;XMY5e5pLz;~>P}LTT?%`3?N!&pYC9wJuT*9(p>8PY z?9m28fZPss`3Oq>PH3xTR(RXCK`+5CD)sVhH827}Foyx>e%i5*%Rkfp6Y?kulg0QH z;7F|F)u(a61n^ypd;1|_5Vrp{hXbxR(Pq%6-}h*|$oc!nbNYUx0fKV{xO>rb+s-~w!C z`*t;d6$yv}AB~9jN5aTeuK=SCcb!!=vMtgER1`7>s{MI>E%N9%$RsX}ZbbY=#T(pY z0EC~Hi1AnYDkcHtx=`(!@$t+Ae4~j^Tj8D&VMFkik+&ke7fg3_kgQ zoIEwAIc3SR?^md&@Av|k2AqYfFy#m9`Wv6C!D^2JXuIhKp0SNQE`|C!hyC}b|FmP&3LLmFVOyXeJ812S%AX!;P4K9C zXnEy*8}h0O2jcy~QoA)-r)y|bdk;g(0b#&7C$eCI7)qgYNF5jTs*L6D`Ti@i0Yr`H zQN|;Qdnr}nupz@C91@^XffFlG1AuQh(rUMh2FNwsfs<$s(CWOP_IgbQd)CtPTcKWq zTOF9i^8$d7_tilrzqRR6V#*Y(a6Y&P97WYpI(S340e(QEHDRZ?ruqO2@o$`9T^!u! zwJ<|@_yXvM;%kv3AbfX%1`G~@-s22pItE<2E1I@H@DzbK$Ku2x@-w!x!<7MSBwZr} z@d7A40Nw%^cj{Oyf1)#05hsC$^{+>%lwJjoAFCcV06`9_G1#FPVS^hAm|vE+t22A1 zjH8922-q3_;Xwd+*yEs(z96k+{+AQaUk0W57AO|Gy-Th?uS*Yt=CJu9bg>(bdfCK~ zL}dtU!fZ&`I|AhsairaI95gytS~LI_*9-hCK@|qTD@>Vb)u9JxK1h( zGI7~66eICmZ=VteY!mQtDF(!O_8bJ-z$4=^jN1{<+M14nbA0yre}+?{p1wI%`4VGr zd3(jbJn0^EbH*F$GT%u+a-3f#d;4|C`NOm1cv1s*fQ73QzJhI-mr-!{EtLwHRI*U| z@YsH2hIxHGvWg!fmYg_`IJ_rTG!PpWQDzHu@b!f3^ZLaN(Sdn_9Wjui+Z=~HUeLz* z%>c^yB>sPA9=PP*XYe<84(PCHtic8M+6qjn0Cd)x{p~KyUr#eb(_m@aXW}4q#fS>) zLGkK2q_F~X3e)D_A+%n35nXV<^baVK>h+o2ACHIlrDJ) zg0X}AvrQL38H$sI3gzG#ZQlaQ2z-mCX?08}U?(6zFu)H9{sVU`Fb0Z`;w^gg;NI`; zUGNni3W8AAV)o1ADKaFDoq38&qVO8G-Q)E)If8Kq(4zcLI)F&(1&LFZ!+h=U(gHGO z>P+=LkK0gjnWf_o3ecF4;Tygt08jx_?b+x1y!3JzQ#EAF1RyZ2_hbh3mzo$n@)!@G zlQEc&7XcK3GzS}=>Y2>^q%PWvlNaG~TmVYW1LyTeeh+!}!qAim7-Jc|7d1H=ho%#0 z%hJZkkh2ehYw$m&A&*7=NJW=y>22SJl4HeAKaLYpsIfQ&dPJD+UTlT;9RXk+9gndg zyRj0k@IdW2w-=fqN;SdlsnjH7VlyaxD+JO2>#%-v&UPTD3>2GiXX2G9e#W%qG z7Et%yQ3dKDU}@X@R)uBPl(xec=C>4hi|x_CfOT;Ffmgf~yw7VZEUmvZzzNWFU?nm~ ze1-Yzy_oBe$pf+u@zxy@9}e|Wr5(yFX zs1szJIz#K_;GmD#57DbA1v*a*kb41Uz;O$l;C3Hopz63w4aP8HmyX4%E;Wxmmido} zS~~#R5SZe&nClW!Nsj*tDFzVZfw&mRKNMZ~$A;Sk8P2W+HN^hy<^j}W28|_7Komh_ z{T`2d05jg(mm%@bYmQ82Re20IpT>P6u(c|5pg-zxLkobTfyu$Vdpn6>y+|Kd$^rcg zsz9QHmIch5I37g@@_8Eow$lx;a%ak+-SQvTaifSvz(+;j_;bI2YiH(VeISo6J|{~hS{$V7h?;>B`7qz243MMS`< z`b$qa5!)uL=GbaQwD@^S){x05?Du12K60E-eJf$>DdNkN2Qc10panP%InO^Fau)dS zY0dIbSpH|Ms{y9Z_YCeu{|R!KRXi0NHmKP|ft@<>`vC}SQ5^oFYRF%Tm+Igsss&VU ziMqY)5El&XJB0>@$FU@TIhefN48hVu|2-}LQ0MEAVqT$4e_kcyZcqNlwADax!3=r@ zj*GxCmOC(m0Tl<%<(-ae_BhaRKfz_^TP~Q9MZUqv?G<+&Ibd8Kt1@KgF zq9;HB{2QgnzJnVWDqarA>Hy8oaSFIa>L#Z-prE17V8xK+DSlxhBaaG zR$-tYTF2uh}lnpp&0TpVwnoJ-p+22 zLjadRyH<-#tnH@(1lbQEULEf7YNYuCAsJqjCO6)l+-9lMK%*=OSghDMNdpu7ZJu+k z9(fe*!O?;cNLpE#`t6j9ZJ+`-0uaqs zm}tUNgJ%F7v7-&79)SK41h)HB5R{?$aD_7XgHM6v1|Hy(`tQ2KltW3BXQ5f0{csq- z)o_RfcA1)(of+Tv`FU-sYWVxFm#P)r6>o*^qxSN`=xD!>{l5Hx)U9p@AC#R0-{;i` zW{me+TTdAjWi*paf9&GJ8|{4ViGiY2CS^(f2br8EF3y^U=PJqWlev419oGAtttaqU z-hQP)pFPs+sKFm0&kkBo$EpWd=Hx3W<;}7@DB4(-?V)1EmM5;|A#2bo$WveMYm_xE zn^?MsxkueQ$TKxCKE1p-9+S3}a=PPAdzUORH&;yQOO|9mml`PNB&)^ZIPWvSd%B-EbkjmS?=efHxuOt(X$U-5wcW*b7^IdX>6{&pW$4N^M+l za(y|-e;#$NY|yAVHg=_@LV2aOdF#9w{Ko^(82_d5YDclC^L+235EQ`~8n^Nxq_f_=Z!1#GpIX8BRH^f*@I& zgnvK0vGBW^5VDsTtM#j=ILnE)B&*41^*qbcM;hAB3BtipqA&#})n>zBt?385W)zio>gsXVew6&Y?T9cK*oa*kk3 z&WmKc^fM~~`3*T=B_0L@$-Ds9hqgz@?s*7Rq!Mz|%i`x}ezf~*Nsh3WTDoB^;#%FU z6mM-79J2o9aVL8q1t_bY)ry5O`&=)o1O`{6ZiN<2w0$rFw#a#G?{X}@f8w;~Lk_Dp z&vY$6>rp}%F*yK*%B*j~Np7Bz-xIf?j~itY4Ze#Tkn0dZFU@7{S+bU&i@l#cDIC`1 zUb=SI)^x0W^CKjnTmq8@o(mu1<9kB^V|W1?0n zEJMXJ`e483TWzJ_=ESqdHP1~-(n8NZ(RsPaQVbX@yY?*UutFX;T|wvK#sH`(P$vLU z-l6QI3?Q#kp_#zeAsGe8T^?Bs1@(p*D3sY2uuK79O?DelL8lN}^7Q*W+;@f@L<@{( z!r%-@ln1LhbRrQZt1#}8FN605WGOb{3{_d`F-M?8@W%v93VCAATH`O4zp6Od02nKE zW)vq?x!$&T1bPiUGVR%(N=JjrRY)8jmE1LK7Xet0ba`G2bxA*yx8a=xTmhh=I{AUy z^c6@J? zt%@aH_*F>aFct_=@Ni~gSC9R%C%~~=#4B-0*_LK^kn9L&24RB>KWn~cf7qI~Jqw>q zSb027{q|-Ki1lOmo`}mcRio46XDFwQpS!iv(Xo`HOF!gQ2|wS|O})pThN<7%Vbw|X z+o)MX_(Hqp^Ub!hgX>QEZ8ZX!OeJqHGN(1SaO+t*l2uEdE3du>5*bnU#zkk0|0-h$ zqfGg$Ea)+c6I(Xaj6`-1-eZ$2H(!M|Le#PAR#{Fj50Q*4PQM`*4fq{l%*^D|_Hnwx zq(epDtzSc9@Z*`_`j&0g{$~j*WkJ%J50^}q#hHBDg$)iY3&Hy?{8x~@O(-Wi<%-O~ z*l8(KQlIR`EIy)VmYZw6N7f0Yow@lX41e^9?JVcCMrmVokw*6{$J}=VciZK<1Y66F zkmn=pc4mmjDpElMJu*LhzV3B(FQcJ(W7o>ft?}7IZsne-C;P&81wY&N;hMUY=~lUp zOcT-m&f%Yqrr?Xo0>z5Zi+E{uUCZ)r^MtyRLx?*|)g4i(LuLGV^;=H6G_Q%$osw@^DAoD75cTXJ&&;jYZ~RUj zL=V**NC~UD>&n+g*Ra_}W2J%lYIEgc%@7H#xWMlwn;F*7*5r9=*#z&m*Y&CXVrb)K zzQPv%^lPJOH~OcmL`J8RL|8sF+%z|>)tsSe4NEU-O-fh!rGK|IoY!ckqOe*qH-k$l zcOZ=6VE^>jAn6dg*q)BCo;hxfejlC9ye`RS1HE#-#H?gKrP4GFsc4ncuBOk`b=*wT z8^eC)s;zvyTf|gi?Nq&Nz8_J-s8!a;B>7>c!geZ0iGR8-#^WfyN;D-?t5mQ^MXOfV z9W@>KrbHUuhfxVG<72Ifo>;>Y9jrE=Vpc6SwLDSzXpc$k z2`|N7QurKyrOCx?bR;zF1hH zBO$X9Dznnsb#Juqr(ht8$;sK-#nje#V<92*2*6aseUPsn>p&>!|Eue}g3;OsdwbqJ*ArzG=f}*H|k-AY3aaK{{EUnf74nPTE306f! zlr2-1>>-2@lHWP^lQ7!v?|Jo~CnUM|o_ogo{XXXtcWiT30!8fSxdfvY#+W^+9t2@N z+BMChwVjImRPIIwiI{Mq<3x4dqXpzyYtK9E(;%G~L}>wE1J-)EyU*)0+EW6;2#QWc z!{ukf+z}{=Ba&mfL_h$fBZrr+?V2*@F(LdRjrOJMZ9?zQ7u^eV0@XA2eV2008v%MDXihW1CCy<>f>$n2?yHm?X z#mi9FpeTYJ$L+n@*53!60PK>W%tO_xB-CDYN+d(+B^Lz(d`sJO$ELmstP@AptCQc~ zwTS&jxEt7wT(8FST$Il3lPelSce7l&5lV6`t!tnKLaCvqX#dgcn+G5nVO z%7eB=n+)N`u3^PPr3Tw;Y7x{n-%+n%Y9qacDWHqM1ADc}&U3~=XK*>zcMx{*bc~~X zF39xmHB#k0u%m!-3V8($p@?(@!&c=3?&STnF178juzF4E~?>?B|q1+PNv&{Ry37h zqjNYPpQ9WL|6K)ifI=FT%)0eSk{u~Mv|6jo>+rs_ta}OP{Itstur6a^UOMEO($8QQ zCBAJLw6oC>P#Q%K0`+qSC}v{E77VOt$>(5w-43&62ZD#4}@2z|4E=sKy@ z4i-l~W={L8GobIVSAJIL#(k=CAba+aBZpUKmVY&7nwzwFrWx+JU0Bgy$0ot6s zBYH=UBp!w)3T}sDC*;uJ(AWR}e}QIqMwt+>;1IbFohWo#$Sm?$=fZz=)dMemLqJg< z%DX|99KkxFGm=bli44A9<*x+eOqdP_SUaGeGs#m()l{2b{=J*7cVw(a`wE&22K}?@ z&uLHjJK)`e0-x@6kQG46bc0TszTBUKCID<=i1El?12W%^)Mo_iRv;_|;RIC|OtFx? z02@M5X0_GT(DX9Qo1vos32(YI53X|PB}!!7E%%+{ZrS7|2v24yZ+)0isAaHc!~CDW zid0oyVL7JetJ62spD$d)8#(QW;g!NAA1|I-8)}$h65Tt3+kMj}=E+BC`IfuypWIxh zn}4kQ>C;^w!p-HtB3{)gfU? z8|uTqEj#d9`%oFYBh6LC&T$v{aKp%LbjEhudAGS_anpdMAAh}a zq+ml*%+tbCO8n(cyH|=dljjkwfb`eeqXeU_Sf~ncE9J^5o8sTGE`KET{x8MHE`$V# zOKUY9)j4Ep*P+N9cuPjKd;Kz1f!Hg8l>#5NKKQK8q|1Jq&4fjFqd(4pFEog?fcG+6 z;O1>1CS8`}14qXiWpdYs2_h?2-i6QAnRFXoi5c6g1ILCx_lL6XwLf1WX(Z47U|;^K ze@3OFHP^ed#w8v3TREUAk;4Z_GA`7E>-mzZaR_DcqaGQUxeNoc7 zb$Rgi+>DW5s`18(bq+;q_3DILKSeuDesa&)f+TmB-mYQk$1(?i3mARH!edf*jdF@C zpyg;SC&XEcj1SOUh|7%Q{O3T~OWk6)>=zp?$igNywvAe5+2STIX$4{!%(czxWAe$e0&Q^EzsRY#s@KOW7Jsu9q*J!ZNZI-t!}JhqWBfJqi~cuwPU{ z7PlL#lh6TofTb2lyvcRZBo*GEr;fg2RQBeaHfe0}R!O=2o^1y)LGQ=IaeTmqgg<r%28|}kQh!9bic1X?Rjh$Yyey+;~5$aojyE+&vvtsUSiw1J5Hs+azx|v zg+7K-9VX6(+(t%O*JR=?*KV1Qlr@M#E9w6GX1JUmON&eQuyzN{v{aajD2 z%FIyK*lBR5>4VE2fMw@Pp>huMh4I1aymry1;Rm-EFEb&vRpplSKBBc+z2rmxg_T`v)x}4OM^@sO_1@3B5Yo2_>c>DJPF?uH&rtNB zq5<|GHzImsp>}An7P+NSMcgp+Xpgd)BiflJjxwsU1w<-$S+sT#zby;OuR2+&VzE4(_oYt$@E!QQU&g=^7yX)WU#5W$T z{ViPAfER0I4{JEi>9Sww7#8>n@(etNDh*Ry^|Qkx(RP@B)~NEvVj^WO1Gi!OtPHPRVpp8+AR{q-Gw0U|Fx~AW0B1ezIpN_HN zO!ACMy5ti0d7~LQ>Ac!}O!2rA3nxJGl4m{GdWU7gZAX=qPRu7< za)4(1g*M;PgyXx8ug1$;U6#9r$qFKrWe(Ak**)W0a{rn9vJ9fQeW&NohowJEZIfoyXp}puuo`= zqhXR!u55QSq~pq@$0~O6wExFgZu;w}_G99&*%eK@dLFVX9@PpCvc|gmXtjBnbldN9 zoZ5W=;xgr|Kl#g8b=Y0xWyqtwaRlW%fqFHS{>rgag;EZTPsX2L7-u=Cwy%%`KK#D~ZymErlSAPW(f4?5_ zdoyzJuk+qi+s+}GRebG#9D9_lO3EIIA6tL00)|HBM2h^#%Pb+i0(qPRY*;M91Poxf z{JDlAHDgl6pkJU^@d{giiDTM?gJVUrnQRVT;b$mbq-@93lMjoS`^FUWADjLXLcJb} zEVZ^av!9qLB&_Fw7S|v(W-Rw)y@nnc$Siz=RQ@W(VqVI2UqAlhvf8t9sgt|oko81! zrw~WyHFa6rwXRV}1$ zFstmPKBWV{(LhShy=A$9he!DnLvDo9^&_U@!+RwwOUrvZTOg-n)*aP5mhrDb6=4h1 zX_^yhEroz<^Gc2Li!#2uX1xxX+I@({SN8CM1nll(DB9`HhwRTF9@U`ZN+J2}Q>)+(6z>a;yfE32rZRwGi`yE4qQH-SgmwSIbx+TjqGvpO0m@Ha& ziY{o_m0B9p_iCW#!M$L{56`eE+_T+^+_}TPycE*ueI2qS;AD)6q}h=I#zB(yhtIE2|`AX_EtTz`YhcYtsczol7j1q)Al8*Hgn(?b(+GJ`1E!% zUp3b$rxM4f)i(k#*v_(T>q+<&RQ5+ce@SL}@h}zTlA%f0DFw z)7~M@rM8Hlw~74K0#Fu`2AmBN)}bpoGt_vun@OSX$|G1yVP9Y&F5o`6%wzHu{q>dY zIwHK3=3hFlBpL}&YCP5ia36^!sjYU^K@d)B6b0R(kZ;VTPbuEP8V<(~OPV|eJlM%q zi$+txNvL0dHlo^ZiT&Wwj`U`dAO!4%=@-*CgD_1e$^lZ7of^+ju=oe9A9xxCB8@4- zm%}jdtAuI^`$51885-jYFiC#vDP1Odnf0KI!FvHAnGZ9pay!6-!vGJ|Kaj<5Gy?;b zG7u3n;6bcII9o9>-KVIc{R#u40t|9FiYv~5%K~aDGhrfx6-ANb2*3J9d zZNE6K1b`Fv6<~11qVsUKl}d{7@`zZYfwKU0jOty~w-RayIx3)UO03=(_3Z}&30FeB z6de8KQ^8y&b5a0J<79Eu{$&t1P}LDys&|9$jM=c$&~5IB0Cxs=Zj_`YH<3NtH|!-0 zm7+EUTH;!GPL~)zlGWZ|=|hfCI8b4p!64%mSS4sHkHvP8U-BqjU-I9i_L!8i+jzPR%-N0n#ll6=3{7mBpv zhCcw;hTQF{olKxW1`2wleg+H6uF*>X(qef~4O)Jexhk6B z(7OtwJowfzL!1kXg?k&+kh_05fBgg;6=?T?XW*@e0g&5VN@&Or1zd{BVJ~cTY6qqe zL1cX+DcsA|CGf0#+#)ZqXsE2mkw5X8%>1eCQ^7Zbreo^+r>zl#E@O`@(u4CL)#aW< zQlv&Q0fZuBe1-+7X~x0+l|eKap=spt5?PVA8N5-Rj~00o!!hQrUmtybWvr}2CP_ExNw$WUkN|+rYMv;(&Up6xENM8ROJu0JNhI=5dV-AaHU({CwdeZN( z=(E8%1n2pxaOV&KMM@(B$ViblS_G~}8MKMlnIS(xW|3FWij_#FE&;^_+(KCm z7S;_-OTe1}&uk633^f-xb${?-nRl21oGjMh{T!?`|CH1}B{c#_M4$vFt10AF0wB$*rg{n}a)LvaY*V80fJ0gi z%S2BG$X{_PG=+T{M;VGTZUG$v42E1io#$veEUz2JTByn@AYK66syr3uz$V}xO%%Cp z!yGa`TAk_$P;cGIgGgD$-A$$pi9k?~WP5!7H>Blak^EHkegMRQ&+pzw!kz+}k|&ys zl-*#!g^sE=&7cx|K$V~ZhzR|T4I%*5e?knVZzP7-#`!jU=@f>i23i2OLU|ao{{Wyr z9wpECkA5)+FU7=-Q3&*8PMLe^QQ(}7fK{Phq7aMUZU7Xftc!}XgG3k*P_jDDc^CNu z>*X!hiI<#Fd%9na?PrgZO=#KO-x2mFuxXU~(Am+Hpf}`3!xI}|D|rpT_-md_;?JoN z58x;hP(hdk8KC=6tc3u=9#Xq)X|-@SX@WSog(uraUj7}30e_Rd)m~J7lfI5 zK-FO&>ATu_+3Qk@W2Tzn0XB2q{n;j0v^zpZZf# z4q=2(u)U=`Q94xR!0W(^2DAdEvgk`UkoD3mqYEFdms@``di^d(zg$jo`B&1rF;P!s z;?e_4Wuo-9!fWsT6mk<1TO1t^mFF%Ck$%^Cqr7yIUdSJPrN^gd8=ZA62^5`w5v3s$ zcUa5Tbu@i`$wu0=DtonS!o$SCn*Exy*Y&2xNKa(4G^?pVlK zZNUkOZSq)pcgDK>m3>dLEw}gX%6`|Vx8;trta+4?Fp%q)cC;}nB0ggBn#e%YM$MT$ z=}QYfUoXsh;mKdW>Yn(J-nOQ$Kg}%6&TR{Rkhq~l4&1CG#$46T~fGGyLCdF*TwkiE4BHnLyAkq#WogA(iMLGRwHa4_C8v& zZsNqCfGrv&6VG_Nz75>iR~4sg-Soe+bNRE1YLergc$aFI>-I)*rQ*O->t%KPgN}X< zx2&Wc|LYXVoZroFm*p>C{A;yG#7`E|hPv$iAZ*NOR3~k?VW7fOTOuaDQ}LRx^}~eR7K$TN*b7 zO_m+Fo0b?f&!?K5eX`lsJNaSbpQFaa%dRJ0*H!LxP_j3-{eEZJS(O)$&R!BWUx{nh z=t=YIS#(`w+Ywb~G{3FZG&}pQbWiKJUj_cwKkclXY5ky8d-^1S_)=VfI8pSe^{k+M zPyRdM-m7am`drQ8WtD%o{31DeQ6!CR_K;{no|^q`Z36 z^XfxceEZQr4WX*Q$K~GvtEU3F8jZ>4BqX28mMr@XmYzk)<5E)2?NsBLo_((e{S|sj zZ0W%JDbb4oU5Tm~CqTTo8|x(!yGGT7@^kA9ktcj zW}a^Yc2s`_9!h@~heFikh#VS$g4=%g2VA5~~}>`j)#q%xP%Wa!P7&$UeEu zwyd!`-*XG+m*wk%>gvl=E}s0Wq54(r>4(lOQ!F3;>|Es4*xFJVZEb$~Qu6i)Q;V{K z(#QQ-+3&4F=G30cURDylIXSlXN^RG&9{+hBc|qBeqsBZs9k}Jg%iEuow6sot&$k`Z zFhaQ3B3XRRT$Hmsp(i{hs?lHOo8#ec^S0rl^K=c1x}+ccTUs2%dmlC>73j_4_Q$%s z3aO5*jk6FIly>*IhDnwM++UvDU7~U!r@!{j&0VRSCc~S$D@br>8CLOn=gzRWT7=59cO6E{Xea;cCQe{?4?H z#$w05i0~FAtu-YjJ2REdz-Oq!HcZbpYDmpR~@@{ zEb0t3>${jxTco?{B_X=OOTJz3(2nNt zY@>qD|7zWkzRXkGXPfj|#4_ux$v5f`WXGEuZ7r%RHLr?K66cL7Xb!$=>6gXUj4(CY zCb+$|BrUGly4mS%!^*#3Mf=a1&Gr2I**X96y!E2Y-S!`Q$PuwMMA@4ja{7AT?(*KW zqHElyy>~;-g^1ow&KAa3yGc){fLw6u+^lWV*#0#MS+bFW9N~w;x>w%IU9G#P+xZ>t zv5k{_+?YD1E|V+Kc-kk~J;=d|{E=}Rvz|tTQqTZc5kDRnNxEyF$!HJ%CA2mk>{D;*}XzVKOq&-&V zG$^Joy$Yvs`9&q=kkwI&YLRm*jF=kJ^+}L)J7+lKg%e-B%XaIzw2S-(*!*}cOvBu- ztg6-3jbZ;#3e7dj3D@vN{U{?!Q{HdDL;cWl+rQ12GummpTK}eG|Ylm0k!-q;rBId?i+>=R(RBX zf=}GkWAmt3Q;2FHI!KsqagI7~Ry@eW!>+9B7hnS40SSM{x|cJj`PkfpVey|Dh*L0^ z%J7Ptty;9K^v$`+q@j_ErXBE))WRq|cEvGhMV8-u;n4;IF$gCqum-(mr4G?Blf~BL zYjM*HHRVzT%H=slYZ*209m`~fV621d#J6UoHd#n1;D9H?zz%fjYK8P=M&ZY~6;N7+;fGdrbT6P7UY{qH-hWzL6Fll^`TH%nTlI0=W@vHqDlE)it_gML_QF&l-qU zAKRvAxOJbl!N2#XA^I6dQ)sUcrvthT+d&?#6OzkZFf#lLn2R2@3B55(v%< zP2zzz;ykh*kUCkW%8O-mB`Vo~1H+uoHQPPZQMjXl0f^>;jM~nbi-G|XM4Bn6Ob-o3 zSxxdQ+$b;?xq%IQ#!6ux1f=o5)Okt0<6grPpo4h;tS36HoHBV!HNRU8-GQ=1YzzjV zss_}+A6$n@1?7!xvc z8Te=0*#`wUb1;|}@rZ%{MfRu83xga2kd2!&vtX`K)B3`TiM7$+kZ5Q;4PF*Z(O|F8 zn!GwDr`gmS!QYSe#TvVU%yP>(f(bi$kYm@gcQElVt^pwoLr$$Q#sjT>Pn5(r^_XSH zxC*Z&ehAMpvQL>YGeC9t97X2rrbZpIbPO}1GdQ<8cwU2#wLQpn=SinxgnjaV3}< zeI+}#WcC)76bq0|u$vD{dXejOZ$9_2&RyS8+jHT5{`-Q9kFI9X0S$UTsD5vh+?19Q zr`-h<(T{C-dsAkqgL=Xn`>``>^V9y-i#1WOc+Dll$|6?xNIdz;fd znYE>*0TkgOQ(4htrA=fr_T}JJP)M+U4cbh?jU45a{AkSPLwIR-2F?xK0iT)$S3eLB zm|!;A42HNTw3ULDuII5DTiRLra?KBAXkgyV00A9WQ3M_c?DSO-yye;emEDb>LNB4k zCmwzg4}SH1APrNcAWSipbPERMRC$(@1ihmAsz*~om{6y)R*Ip6LzMsqzLHP+1Pp{+ zH?{!mv_aGzoFb%AtRk7f2eFT*}87-IA}Ng^KdW>b@%-$72cmT zdBwdUizC+D>wROsev?Ir?0>4VV6}m^;rPDG;S9?{?Jmz9l}{~rO&ojc4N169lb+&) zs1<54LfFvsCD_pBHFsbR0AnBsTJmM&hm@^Kw27hUF3ICt0}L~CvH&xTLNKCE!5J@` z(DUyr<912`kL+YJa$GW?EuV?4>(AP!Omt47e(Wo3oxJhRmq6&zN3t2Kefy`z~T$tjZlBT%()Yygr|b1RT^?c674FO_8W zz0{H{vXsKWLY0qPG#dyWpxvgyD_l2{i=YwslhQJ4n2H-HU`UQr&-BC0&EA^I+5_GP z2jWqltV|~jarddvM*2NSvq6VN;SS6Wf+5;D^AL%`z$RV=5O96z{(+%B#Q?w&p&IX) zOaF4{UJjrT%AN;=GYlt~vMDkh@ha2+(~7(;Whkj;>jzm?1&VOyV_+f>L;@7X(rk^P zIW}Y_5E(yo-iA(84(vfL@Hw^V!ff6Q(uTu`;<4dZUlyJk7zeV+-bzex(uBQ179rk|)I7y4ud9SJe23M$8KyixY z6N1parN)ODLH}(b#mvweGtG*Pxq;CcH2bewuo|Mt5JS?&DLLY&$BR6-bk=< zNE4Pbw}NdLxD{y2$ufx6F{H9|j)xK(JaqU03I)z7W;Ph7Lhw2yxKp*jRP6sQ@c7^d ziHWw0Q)6=fWhpv!yRPNJdjy}9QsDjCU!^^_C;wVu2-Yr2h07=(N5>f{ylZ&>B6+(n z?IWQSnhv9^(hTC1r!Y1!P7D-ip6zqw7v(Bm9+-}b1mG`9Yh>D4XrkXRGUi^(V~v+C zGLz0|fj(xhg*yz&4D`eSIB(dcpvja(Orum)!(2g=hs+28;eatvT{4-0?f&#*1aGeZdSpnqln-)Y+Ock&jZBkX=oX~IxkS6k)s@6n)c z=)~`n!*(mkOKy#x*D5|84=D$f2L9kBc`n`mH8wDzV}$4!2UA>_!IYt65|gbS@Hu3R zhx`T=Fj0Jw5pf~No0=ghZB}})Vhm~uv=#fm(tIO2fHLKL5nQWck_iqm5z@LS^SHwW zV{e>V8`kw!o81)k%P?*Q%`^bz3z$+XxP-Uy^$gxrQJMGL{8XS|OiKxq=$=En4ub2aF8K^s0XG|eDitG z;>O+chJRng113pe^bK&PTKAK4Ilk}(bOwfLP49E`B7s4T@2M_hk8nf@8?NxR6?vQd zXGh~C<4_81u%O(PtCFJK!%V)SPo0=T{Ok*q{;Jnv(V!U1y>P7ir7|o$9tRraEyvSo z!KD|DJx3$Kmk%~^V=eeaiP!(3to^1jX9|o8GSBVcZ7&bBpZ;7>lInXeC z><5r^hclR|Fscejo~PVWhZn&ZuI;*i_|LP$;*^;MfCz8M?f;H-qDf#?(R{?V>Jo<( zm}T9mM^DGmGp;*rrNh`qrx@C@Hz1qS_C0owY4ScV+X=l#`TwC0Isu2@Rr+cs_^6x< zEZPfm!OK-HY9pV+H0`5Jv|Bm7g{W##od+Qh&P#HuR`vI?e3^++1qHya2s}gsYL_lm z8Hez9OiehAiWTUJ4NPMF-hId17ou&l%moVrTpBZ%p79I^5P*iteq@4#vkgQFUIFm( zfwY7}*S(7NFXTI2b#(i+MdS35JY?*F!KE?KkYc8b3N_g{*9_D9l9g(Nddxt(T00X6 zrgj932jGDzWCmsE?HeaAY?~9( znWyAHKm6W5L5=tG+!1eP?R(+gGy`VryKCRSFy8#CIH9!^1490knLXw$u&tm0lL#HF z0p5WjsjbYnM~?hYK@O`{Rn%bB(!sK#zp~=7z1*ft%iY|UqLy89+e&u8*c zF99_GnWrVmcl}&(6HL57hyWJ^qswtEI+5smRYCD86}uHo+0bnPI^#Fbt;J+MB#$1O zIJVm^`nU^4Z{Sl%!@`^k;YlyovUyMBkV%fDJh08-R(BOIf|g0() zp0)1%qQ3Id8o`#ngru$SKVQSyY!H&)t#aYT?x5WftMxdRyZYiUXm}@G=`HxQ`hUf~ zo<*56qk2B=Sk;-l?6as-w_ZFttt+g#5O=#}=95;hJJM`Tu5)LJ(S@bu5|>V)YvQdS zsf)IEYhAz9Ngw}`qP#ne8>cyEyFRcI*Kdh8Gm>?uMhef%emrTPBFW`ldq3T$|MHrS zbFLOWl25&(&c*XfMKH(VCFEXDlseju5nhXuNnX^x@a__cuH~){=7#uf?^|EhT7O4q zV=+Y;dY=`xu8H@1-yT{QrTJ5NLP&n;=}(Df@pCP<_67S@cXO71<54Zh-c&Nqx2ia* z+i7>_%fMgyXZ1gj-FCJS#KtU(KjZE4+b4e1B#YI;4nggsg6NCBMR&5I?rwW>tM}#E zgMC|_-@e!s|0yF1wl7ui@CdtFbktY<*j1d`Vr4 zZf=X!F&o)08h78nOMfp2to)=`oBS;1g(N2Mzm-DCZ6o`SWUL&T8mjX?_&sSUG#&9n z%H)8PIBmIAwg4Ve14(%rP8u)@8?e)Iei6av2L>LHGK2y9YJ6ZA!8RypUmU`z9mwtC dL{^)OMNa1aMy7h$5%}Md@0NdiXQA7^{|6i|Sd9Px literal 0 HcmV?d00001 diff --git a/android/app/src/main/java/com/example/hlsdk/MainActivity.java b/android/app/src/main/java/com/example/hlsdk/MainActivity.java new file mode 100644 index 00000000..c6beb064 --- /dev/null +++ b/android/app/src/main/java/com/example/hlsdk/MainActivity.java @@ -0,0 +1,54 @@ +package com.example.hlsdk; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; + +import androidx.activity.result.ActivityResult; +import androidx.activity.result.ActivityResultCallback; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + + +public class MainActivity extends AppCompatActivity { + ActivityResultLauncher mActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { + if (result.getResultCode() != Activity.RESULT_OK) { + Log.e(TAG, result.toString()); + } + }); + + private static final String TAG = "MainActivity"; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + String pkg = "su.xash.engine.test"; + + try { + getPackageManager().getPackageInfo(pkg, 0); + } catch (PackageManager.NameNotFoundException e) { + try { + pkg = "su.xash.engine"; + getPackageManager().getPackageInfo(pkg, 0); + } catch (PackageManager.NameNotFoundException ex) { + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=su.xash.engine")).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)); + finish(); + return; + } + } + + mActivityResultLauncher.launch(new Intent().setComponent(new ComponentName(pkg, "su.xash.engine.XashActivity")) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) + .putExtra("gamedir", "valve") + .putExtra("gamelibdir", getApplicationInfo().nativeLibraryDir) + .putExtra("package", getPackageName())); + finish(); + } +} \ No newline at end of file diff --git a/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..da1dcd94 --- /dev/null +++ b/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable/ic_launcher_background.xml b/android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..ca3826a4 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..bbd3e021 --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..bbd3e021 --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..cb27667f28bb31edec2534dc4301103c9eabb071 GIT binary patch literal 2638 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 YU4Y)ifjzVS^w=u2+^dV`HuRDL0GmgL1ONa4 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..ce6983985a091606f912592f3ed86002d9928725 GIT binary patch literal 2086 zcmV+>2-){iNk&E<2mk0 z#QohDWTB*yB&l-f6|nx54LrE$v?y-dHd5L?FGI-wQ+5Ehk!+{=p~_IKNd4D>^b86L3P4}bpk@$Z<}Gk9%1pwav&nY+tFs3E(1A&p+8l1=E7f92 z*0#m1r04rnrebDh7~ukH5w(e03ODScSAjF`9x*e6+2PD@U`bs(er{%1u1gAEMMhZF zs+Q^6wsvfL*1qmXpMS+ps=td}v9jIfAX5E0gxI#-+fHX2pC8_vZe-i4ZKub&539Z> zu;o(>&lTnLe;QH<7xV%)Oi>LL|l^R&$QtRk>WGOiTP`}@kGt)Mox#~Lll+>>${cF z`=0u!$F7HTdL*y>B!mC&-2L6ki!<-;!AdnO7)gkF#eACoh8jUSY9uhd5+ImRNzBz{ z8>Sfy8P^WJf5_<=RK9wMBVVnB|7cr8$Ebmk1|l+dsm=)yI(Mmam)eq9s&g}OY{=*~ zEdpO%J>rP|qb|Zz{b25JgriASW>+S{TnL5VUoVifhDIH_hR zv*g5v>PG)E7WZG+J;iOP$a7m9l0nKaAU{sN6AnCzoxC%J*}XqcD|mWl>G=#3Y>qi|r&qsZ-Xw)y_a?pFYYSujCRrVC~eZVVeUs&U7ShlY3< z`9T^#i9E#`^{aO|*!aN{P|Bd(xz6A29Xa58e#^z{a%0@j{JQ5x;>c#=Pxp@O)}1@E zS^%sZobuY(eU761H`GLbgOUKI0c0dtWj-6nujb96*9Y6Af>EW&3oR>)G|DBw+s@)w zUyd7BS5d+tL8j551vniBH%Mo}KqxGn!2v7HakjwFM$3}A5$`mfG|Od3318L!Z}$^ zP6GOq2_F4)9;_=@Hbi7bQC}&=DzhPAijpC+!gwI_SF>$0=rGG9E)dBI(x&JpwhI`k z6~=(nAg#mUV!ypu9gzOfFuIFo9L>fehXjB_47{uu4{Q9T$c`~cr_%tOE(CcMXkc2D zCKFmXNkU0;Oil-@nY|??!^W=2Ln(isvMJEQ0}-S>?ZdPvjcLy@i*#H}g&dzZtk(Cm zQtpOLtu2NDDIkc8sgAHP5=sPj-v*gIDp&4c4QY)>l|Z30Lrn>U^z)fcu6LMh-@s~R(IQrVS_9k zP>l>ZB*cNBrFPTMa-iHowu3zf1B*zXT9^*jd77@pEM_R}#j{K|2^2US$t#~{(0qCK zFFVhteXi6(GZ+>bnW8P9k`+KnkZE<0xTPQ4$}SWUURlC846Tz)EAqfx%7AoW8o-63 z)J*%^G#!WYRQ1)zO*}X3bg`&=U} znGR~R`j4`72M?uR2+9<33mi@>CzwroG)nrA|Bu*yfv=>}#Fbb%Fjtgx)IaOg`zG@W z-gEcM3d$&7oBzE9pCCJ&?o5^GOqCvw)&RhTa8?6w2%vrK*Z#k9*Y^!5j(Pg}zps~{ QSd4h@pNcAIK=I=d0FJE*C;$Ke literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..d72d3abeffd43bee0bf9564e671e9f92bc2a5af3 GIT binary patch literal 2984 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..08dff72be3b3123ec4df48815833bf5fcbbaf7c1 GIT binary patch literal 6368 zcmV<67$4_SNk&H47ytlQMM6+kP&iD>7ytk-U%(d-2}NxiN!#O>@+UkCBBK8jASr;_ zRtkp`#-^<`JKN+NkZr?AQf(YbvWLwTly2j@v`n9*tz{q|N$`>^97nRPDuX-B_f!8P zFaOxEC3})^8%dJej7b5}kQN2#02nYJu{d%1ZIqE$r&iJwFGVd;zrK*@3t&!Ee|s;GjA9*8)n8MpO~4M z;l^-dX4HmhnR(a4Jle4>%aZz6iKSLca^?KxgbFh!M_%-3?j7c2dCy@OSYCyxE4fYO z$aG@nq|dId%840I46agepy+Dbwn$Q*_xr1TX4c(zad(%2kd&))A}&HAQtBi)25X{) z&{k7-clT|b?y7p}+P1CQQpTA3l)JlER1vwpj4vQ{h}<3SIj@jNVls&Nh^qSsxIRviACD=?Qk^ z|5GH(NvNrwhMC#vWrhQw^ZD)rc&`E463 zuWZ}4cD8NXwr$%U&(u^GQWZ*)Bw3ObHLrdg8+w{Or~R$ya1-0MtE?9Ud^b1%GdQAx zF4qhazyWn`snX*<2X5O&k}}2=$Q+)dp4~41i1#oeK**pbd}}fd9F`7=fmnz@#4EMxl2=>;j;|dB7iv`rI(@ z=!_o#23a8T6E=@q;H2uY_l%q`X4`N0`R7l=w$N&RDN-G+00g}_p{)!-S=U%@^FC}qq1a2}@TG!F$r^T3?iPZ9mfLw&I56F=1hpsN zfL+Cj-&h8i+>}`?D-gk8K#RyH1@{=Z^L8bd>z1qjF7MI`T3 zD2oALW0__hdQ)-&uR>(fZ5>c`yTZohGIN?TjJ}Iv>Jzi9J4ik8PdO0YBN$qPQP56` zpjR^2dF3lN7YmAQzm*5Wa4xcT*LS~l9J9kTGAjuynq}rCnq-ASiB#6SONKSv%!w)3lLvSE4V?B&DCm{nA*89v(o$H);2hJSGi7PK?Rzmqj zFt*KbjO;L-Wi(x*CfuulL#d&}NC6LJF_MlF@lb^@46Ve2aq>p3;y7@NQpM{}*tk=p zyK>~tCJeoF7g=cMLiJj#+s%v;H<_+kTe`g}oZ}%=C=EX2L91h&Q%pnJN+luS76X-{g&eWd>(gxv2)Aku)?E1hlvHn# z*7 zrl6UqL{oVDv`t>2_I_vKt{?Z#O#7fcQwp44dsyrwr2--jvza7Fag4^lJqUX8)af>w zB0I%j9HfY3sjT>N&yW}`4pT|=YE$SL{X51U4oq0;gXFh4xH>t4H?TRVWWZ z))Yk_SjGJnrRuGUxi}}F|X8t&SVouxmrXvC< zIju6uGcwx(9Udv5{2UTOBn8HeU{qF&!OO(4zqNbT~nz4JH{|l38;x|;U1%NF{BU%2889ib!^a_aG7 zfK&viuG#5MCAm3zy_EdxT3h9gAn~9Ar13n;QX8#AATBD9xem0_`4}cI;js4Ov41<2 z5f{Nz79TCgV|0!zj|r?!bMbSV1C+?8|heIoaUcX1J8Dv;W9VEaD z6)ezU>C^}up8tY-E_(YjNyzO+|2VDY)Z!^e^2EJgASH0yX@7EG_qooq2roYvkJu2e z@Jz8;bg_sP3+7t!1()4#a?i!TE6p%&Kj&jEIknjH{5)vZ8*wIa+o>O}i##?no##OQ z%Xku0KH_C@s*Q?n(SRkYh-NaCFs;-t4@aMLAx}Pc(&_bcZim#IZcjSEb1xt5%bBe? zquIk_pmuhGQk9p7YVo@=q7 z4te(P;Im&LnaGy)tL9p0oeg-b3F9K^qtW3p24BEtR6A>0$tal>m_hLf5e2v(bPa;k zMGS@k@!1!j;j``eF4(ktKZKm|4^I8lboECxUH?`2%JeghmQt3St!|xEEQYfwWHR04 zti59GH3yzO_THGD{^9_unm~MGXzbngzt_8eh-{K{M$`V_D;22upU$@qs0=VKlrNp+ zkr)YJ2~vh|*xOAYMmm2u!zm)k1+6-3)arM`Omc>Kft5QOn5T4-d9!hl2ux@c%nvMP zFzTA#fbmzSx1!BLzfA%_&lSu6Zxnx%rc@*NQoeN4AKEvij)dTv7#RR)2t?rj%bLIK z;U^ys&FqsDGQdx8zHT*#B+wDbC6MB}G5@;-f-Rp1GB5zSk5RZdyY%>cxkn2(8oVbo0WXVE-s-KFvg;}?1Xj&M&D9tLkav6VLA(=Gx`lj1OgNC z_$VpspRvUtrA{j|OP>>f1Ol@Z5J)ga!2HcX!Yvq)gV!(G#~!X+@~OPTw64f?V0;{Y zL1_G=L7MSEwVz$MWLE2`f_ouB_#Uw_+7q^+t1`e}u zQwmJYe*0S#kYB5wS7s=*rn9U7^bJsp*pDrBx-r-{_-=rmaY0ytF9O(F>(*@? z$gCEXC_>GQJHMAoA^>DCC-PZh9B>9-6~0IcH;l-*j)~WIUaTBOvOhxgxRw+UP zC2)k6$P+VN2D*;uI&j01#-nlCFJ_F?da{xC$Y9l|J?*d8=bAgB%xCe|5*W|(cdUTKN+n!!ctN|h+ zb4)3m+OhQ83Sww zXwU^}h=^1xllve8P6EN7b?hc}WKK?(2@r5y!&CrjsQU7V>5ThKZ#tJ2Th;PQu0ETY zF9#AO3*BqmX$e7!qAW7Ia=XqR?!qkd^FbCQACf}cSPdVNlpO-GHR%P-A3uad6bK2D z>Y5hITn=OuPpMDK&0N(w>ylPmV7A&6eQxIZ|Gjqq5=}P>zENTTKPX*MI9f(}u$Mg+ z0|%Z;K$w*IKtN)!!Y+$K`BVHNutY%D1!}aEJdsXqB85aqBzRp57iH?oQxYA0Zmt-Z z*SNIKjiLqcQNI5Av}^2r=iYvM+^~9pNB~hd{m!z|F)U7VKhFaECeMrso$~Jq1ilUg zo=S|CSo;uS*i$|WJ6~RXij!OdFEnHsRM-ErSv)6!2}m&{^9fZT?vN#xM|`S<`B zV>me!zcD6-l*knP;0NhDYnuq`7%RqzG1wLc#)JZZw|r$g{b0nEfwB1=f$6*7l~G_! z;HDv{J2sz>R!!W^U6NTf1IcyjW|@jh5P?`I=mcPvB>ARu!=5N5X8UMAW&#kcGF5ON z=`!o+QUL0VOa33TaRQ(rDJz1=g!=+BWPE8sjYo(HOoB&Xp(xs=ffkPs`vAfIE15kz znmv$?rEoUJUngLHBVrFFe8#3QeL@FPFY6LsB0<(PT#X7uV4#-#;@mqrMr`IJ4WkV{ zP*|2XMF9R*JLPcRAlz*aLziFzeSLX?ccOB90O;0IsgF#Fg3m~`k4f=t#M@ld5-g( z??;;rE|@O-e?qo0c5=nGJ-^KN2<4(PUp?O5<43Ll_iboLCMv8+e&{xaAwmAN+2lOX z!6X7R8n>$q9EtiH6)7*0@-E) z>Yv4j&bp^R;)$pg;0VSlW35*@@t>}eD>GB zyl2VH*$J=)psQyyExn)ukaEb}vfbj-dJM@{Wi3|GS12G2C_$9F`DOIRrg-p!U?YC{!l%WsCcCgUX@ z(9vr-&GW3a>Q_^WDaWF`OnkX7_fjZUr0UW~15Cd#>{`Aj#2BV&gQ3=jTWqc;AtVq_SQkCy~M+%b)@ z&Z>Oh`FR;2(>#?gq+0^mFZWs^rL{~Y(2W8rmrK5yTr77>B)T?|2-e1A&?B?1-lUE! zj}^;fYUBali|r4cQb0CBnGy7@%9ZmC%ASgedX*B>OB&>6-=nR8^a zj7lY!E@|72(=m-2C_B+)h-XW2(`3FfDr&gkIkxMX$?K|8vDBGWwwin98~zSPfJiFT i*o4qtUt1N%H|}`{5F97ve`YSvtI~#~oi-$y3j_d8Aomjh literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..b579e14ef98e1bfe1732e24faca9ec828bc7648b GIT binary patch literal 5096 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 K<$&;_Nd*A$aHszO literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..ed75f01dd1556d23342cc023ec364eb2e9423bc4 GIT binary patch literal 9222 zcmV+hB>CG?Nk&HgBLDzbMM6+kP&iETBLDy|kH8}k35RXlND^%4Uitbjcph8{5&fS4 z{W%DPm_u%6mOxchwJZs66A4P&s)Y)(dO*as-b+DSt74Zdo~i0JQY6Xa6&jsUy$S&c zG4|W^Q~{s&yeo%+*a)TS2e1{r#2&BuA1INxn>_lB?7Z%c=eU z1M$}I2FQ&ZIg;Y^ipC((80EiN_tN)16JWQtty&q+S2lJ+0WZ4%+^zp_3b0!OpHBKe z0kA7(`7nKa8cc9cE&wc@q=%POXC@t`UOEH-i%s>x04GS>mtF`VBz0=I zKO5T0N@@X~l9ti)W+X|SO9Qx)`Ujk*j>u@&5Hd0~$Y|TvKp|F-HQ19Ohxh68%2D^{ z=u9gP?9}|28rU|BiO8~r;w*XjK5%^yR zY#mka0H9BO1`;pnB>^XR3%CqNeFFgCtsJEfaoM#30Pqrr0(QM{?mOecOL>WFyru2& zM&Pi}ytlMn$GS~U7TJhHLEAP=!XI|K10f>c$2 z5t*}qPdW(|3%~{Zm)RYNwrvH8&hr|H*?sB&vYH>TG;P}=*UkHNPmhjfq@%-(W>~qH zEr)5v+2apLWyic^#+5P5oXCs^%s$L)A4wx=q?w-XuSy!|?$PX@FwM>w<{d{-jaQ+s zD!W(t2?ReL=?Rac!+qV5YlQT)#)ot6h z?R5UtzrOS-=)KKEHzuH`58JlUZ8eoEDdfVr)3I$^wd1UH?)@MzGZ$k=+yQp#jP0;U zdS-^y5l7VtRRbzA#Kp|;W&Qu&J+!xdG9+2Qh|Fq?XKmZIZQD4!vhUB;`3G!kZQJ(l zXl)L=sxl)SxNYM|a&{+^+2XlgKOi8uggZD8(GjS-MhQ@_QxI!PpqYI<>OvJuQaPtU z^V^hqNUE7j>UK;^r>TN%D4$2Er@laB;8?{q@F4^~-U9WYQJ0^}@wdsMomq;dX_{AC zNkbC@m4#=`X~>JM8tN=(qB~SC_^+Ln3CE*uXDXJYa*#|KdKPt$Gvda^?w{9H8Cn`K z+WC*7jX9vXv3#y6mvtBx5abc02`mA-AP|qh5!4Wrb9l+%sXE6UWsF0LP1k8-vBi2tL%K$@wFhk^JvIi)zJ>sn+1Qo!H z%}rZ=4dON9?di?qOg-Rb3j<*obIjsm0zqrwaE9IlWk6(!Bell{*w`kvS^m99bH*n2 zQ?XT;tR723IqH81%$@*23A5XH-Yc|!$CZ#5-<>#A8e1XrOG5b$G(lJzA^OwnD;?m6) zEVs8m2k}PJfLlv~kOtZd9AG-#H$G10uwcJWVD3j9vK9E1UNetiI*>o&ed)7a;3S=y zga66b0P*N+I)Ho3VXI?*Jp%X$IN`}0OnZ9We~yeQ^ndtW1Nd%r>@NrA?vZweeF6i5 z?=5go%)R8EXN4up;i zj{JLOp{O-1PMM2ghhC&KQ1iiD5xBVKaVUUjM+ZjxZ^3Or=}F^!)a;w9^JK?TItPx- zolJHhh)oP&msD_&b`v=1wQ80`U=p#PY~LetaAtp)KyblnN)LrF;OJSWl4IxleY4cj zjx*-j04eCk9RfW(R( zX+Y=2CQBTgws!wHw?-GO#NrdEgV5V5YTv$JUiWr;Nzh3dFWd~UNxF!f!0yIkkQOx zzd}T!hTOs=*^4iVMc2MN$>BHwnDEFTeL4n+kY`rWIzig138Rd^(29)Go)N%3mJWZ; zB|=2+2KJSf6x9BIl=$G&sA%UXfx&*n}Vb8ASZo!E5uO|f`{~0pPbNnDh*9( zkYa-IPp4-^Dp?Tn2!!HULPUjFIbg}9gdS+@Ju@rkJv368u9DbbR!DGQP$pmFo};Z; zuamW&d6j7@e*ZGdUa){cVSazuRCm=Ev*>t2o6$2<+nm}?+WMKngCos@sGPlCpU_LI z-b}%&^d*o#U)e8_m0ulXdbX>Tz-V$I9%X-8wJ9c7A9QE-P|MOzW;W6+2AKSl*3TQ0 zP=xw`O;P(+xgNc9fyh))7nU2{abbXu?ENSXY#$}F`uoJ~0Av4*;IxlJ$G#sRZe{hm zQ4y`%;{+*!Io4k}3`JfAok_*8PfoH7+8H?0l*WOX{d(2sJP|Xy)6CPx*T!t{%2jU? zm1)P5JCg#PT6IU)(o!x`%KjA}BfE%UA~;EKfZpUvMh;@V;*9?{Q9E`H2s8p6sUlSP z00_yUDntZ4FmBVe(#TWY`UI&ka&#ttrPr2hjyIHfJe9*s=ocqb_0LD&SCO3_#X~Th zP}E9g{*(uTs6=%ymWRBD3?4-2Jw375bqYNM@e>$)>seS(LE<`bmBu8^?73yV@bE~J z2aW&s5LJWnD3#^>8feFhBFP|X_Z(eZmnpgxOA-MW6Jwp}0|>MXE#rXh)Ydec0dPPm zk>V(o1D}SE%}0e2sT|UmiXtM`Py9(OGxeD?3K0xY+eK|>aKP{Z1V+%FcXT>ekB7jT zL&ty4gYeKof6yiv>hZCb+_8$*y-fN6rM%M}Pc&DZT^sjyN{bFhDnUJ)kps@rZhKSc zOH0nALTQlUgL9;tULED(H{~St$>4^IPkchVc(o!q+K#-d{q$IS0=iYN<7C|A{52!{ zxBK2qCgcEm;c|mDpv+Jv`p3^RF^6Wv?HYjF4xy_}RWGpbQ zx}6&K0||vg^aG0}cQJYefgiLhem?OBt3H~zoi-sUw;2!##_a2^972*^EEfiLHPIb# zhyzA0Vae?j69?^#W;UxoOP@rpD%u`SmYku`lf`G9 zkxLWUpuO?(mStg|d1zuHUA4vk>KE zj&SR{8%v@cJKjbUwoJ5!#9B`8!KyI2p$c7MUOd=vMt06)fnZ(ib^e}Sjg2JScg3uIo za}Q!j@m!G&K5;&riHk)flBn_!kBZqdvmD!Ltuj0kNPw% zcUf9A)g>*mB@!ti5)6`1ytKxWYbh@dIw9(NX%=V+ZV1egC{ZMnxhM*wfPuAnuN*xq zBPR!TRG8{s-r64GAhiN_B~YEz_DqW{gA$r8QccJND$jqGr4pNYi_Hhp#aHlqJ3GIT z3!FZu!<%y~e7F58ryXqn(YfHG=X`g+y6{u1S_R>xS!Cj`^jd;k3^ia$r!P3sgJB4^ zP$AQs4`5}-e>n4VZ*p*`=QP((uT_4sQ!2$UgGQzf0~*@AYdHf@SN=W7j_yo z_FS26E0_vjU!`FB+gCNwgI=fO#(@d7PIc#**$M1S%;Zc@yqlPT0Uzj@nWI&zvpL<( z9{`3QP0Xq8dO^5ZwS%#}CpiMwFrCiKy#o@NnIq;90@fM(391FIRXN*>Z+71RJTTOk zT9H<%J{u*nH3t=wcFAz4u|aLmnfMRt z4()C8(>c#;#8EufAOHLZ711gSo4xX?^{`h2= zaQ4s)Mzc*5J=|gM-D<;Df&(nG=G&vfLp#~*vuyhQsg@b=a;#J5gqNiCPwuznN3;F6 zQ?D@C%Q4P9M?EE^n`WEseqaKOfrQJ$507T5*I(W&M^9EXo?G#s4&h-Erg;TeX7x7* zGzzJex$2e5Zq~PB5~E(te(x+f2ny|H+i%GynKd9LlG!vY^h^bT+xq|j0K^b(K}77M zJsD_91d+l4_}0u;&|$IH7S=1u6c`6ck&*}zqKVQ4SeTRC_f3l!X4Fs= zf}`XSA%Tt(0}PCC%T6O|vV763$we{(87&d^Ca$G4_&~u0s1ke%L`Qk-l^zC*1m@eh zt0Fw&U1by~y%jeaY^lQ+p>!|t+xw?YX6E~g!EIInC&SR-H)m;GCtC~t?2DU(E$(mB_mw`F^ zvQ*4q1&Q<#BF4mW%fyU?2(FVo*aSv`IGx9!PaqO=;BPU}#dvQ<7*9-OaKR0Agj0v5 zRCG@viEO#F&3m^A(|bBTER^peSPNk!VsODkp!1L#OGaj|Ogy{WjDSo76esPdN`n(qI*))d(iaavAUz(Y3?Xboks1LC z72X=vr@S8z=`hZTNQ{L$Ga~RI$0f;0I7{%DSkQU&i^jRBaadf!TP<~zLKH$w4=n&pU3GNfsP+=Q1nz~5ozNuEvahl%^PVAa zj5krhTvK$1PxIEpYS`wJt%iXtJam)kP$?&Jc-$EV z!$ZrRk#cc7szEas%}1txl)+)|gf{g(kvPWLV%>2Nqm(}NZKHxPSTmLZzCIUtX25&E zJ0Cw12F$8COAyXK(_KR;~>gK4% zfRP*L#Un#yDN!lSNpu7I!0=#{#uECeu4jX;Tt8(TSX2&R=D(A&Hj9GR0tHw7=Pe7m%Yq))-Tq;W$b6ADS%@||Jn|$VN8MHNqI1-6d(l$ z&|*#S;OEr?b_e#00cM-x8w~mr*{Mr47D?wj7W@j6r->k*>DUcxDuEiZ4VdI0v1|sM z)fGtP04N!bz4y#{1E7Hf-g?@;Gl;_k)yq!Y%dEe7Z^=cdq#h=mTi$8>q+md;b9^~7(yIto+%-)B&3 zp>3lT8%)G$u&4&I4Va)Y5_QQ0Lb)evdBH!N6rA-W?a2w88j?JL*hY})-|9jCdW2fD zsO`GBZLK37l3->9NLaZoGlpo1dRiW^9L@Ze1ablg$T!*rz$Pf9n1ZJ|o~%h(+s50Q z5h&!=+spBMvFN7R7t=@BLSsUmP?fR3n=EvNL?||~{hhqy+R2f5*5KQ&6D2F9CDm%rP1+6wa^5o33j;-NA84LgtZauhkX11W3(e^iMGYl-@hRvz;;LWW*1&SjRcBQrfy;gJK z@o>B#^ofgXL{GbXsbjz~2KSq&Kz$$CzCPg5!UDe5VTRYaYt64b*DFo@_}ytF68xwp z8;~(Ubk`w2hKD+Hh~Y!^o+eI+^B}vgBXnj*%7j34qN0d^%aP6RqeXDurzM?RKJn;B zi#FCj5(glC)`+tg#c}+iyjDTZh$Ln7iLI@e&|M#_Vc9$^o9WTAKhSwGnv_HtDWXLw(7bp_yrF z?niw4_M)v(locXDo>CG7)G29##Ca;`c7iNLNINX6MCV9gNDadrESc>c$Oru9B+da0 zdkgA~?SSoD9mjj%lDP0alj&t97V{3K&co>r0+xW4Ah7*+Nlm4hMO6#?!+3`LDOgv5 zIF@-vDYmhj(LMJW1kK6A87YQ$w3uBSpJ6$u3^Ab1@C7^b`qOrj`*Al-U?i;J64<|V zT&!X&VXhifl4R#Sz!8X1KxO2a0cvIIoNV_f1Cp={rY|$Vs+8_n|2zv!>R_FRQU!Na zAXtr#xpkoqO(wXYKf;De#F3amV0kre)dZx30FS;^&|I0mxD+usF!tcNJ2PskSw$9x zC{p#Y4@|eK3RzD&U)Bvfi@nunr`!OPTce|Y^fMbIp1i-v5Aax)&!)YS+OhMmXnh!2n zu~G`D#l+AKUnWUlT?y;RTtTgryJjxxM93w*4|a4^oa6BsWb`aOcpouM#Qp!39tEkKgiRHEZIgv_EJlYwpy5H>l09opunU+5@W8%uX7g+74I}|_ z6l!&4s`c^s3|V?;8P;Ep>{3wMCJU}@as|WM+|YLIqy|_~C`W3yOzW6xZJvO6=4qi2=U zVt|x~cb*dPnHDaj2vH)MyO9dynsWQN0me22B>hlV1$2?#rn@GxE1wylKP@E?a{_^B zVOIsbhUi4r%7vOxaqa`x5g>DZ`oPWkL*&h;=%s*r6zmP|hP*_}-ld+9OC2d7qt*H- z#k%T-M#H>?_4sdjkc-#y70RbLaqR|lSO>|5=4D+qX3*{E0(JnR3OFdY*IL8A7;ByFKe(qRCC7@(IFxLqysM+s_&*? zy+$X9t0A}_4ni_3cD5=AR!{~gh6zJXmvJ=n+-Uotg@G`nT0nL7;grIwC`pZ@^gG*D8ZWU)Lp*5Ie=29f=qK2m zAW{R>2_e$bmw;%}45*x;I9)5NY7q<#WyD5t1vnz}7OOlm2!g3y(%dupEs7NerbeJz zRL!zyX1Tl)Iz^i@q^To>@bY@4>ncfX95_5voU0OO5Tpe4L(fS(a$W(B$SEUMd4QC* zhm&mai0YCm5wn#SHX{#-U<1-C;=I?0K>7;#TokxCmPB0%CRbD}G#Wu!!!~}A7ci_y zk<^AtFBNtF2$%z4$y`7z=FET@icJEss5QU5(0MB)ET8+`r7%?z*i~Ea?QM;3}rRqo)11p{MC6|KQe+Kb^uvi>H zs-Pyapv$PWcInsGO(O6K(gcZbwJ4OE#y}0RPUQ3?X=a6lDwdw42e_KRTv0s- zF3$YiS~Uow##}TTWc+>~)zmoXFyA$zZe!Q$dagxkq})&j zI^^pLs|fN54uRm-ZDJ|;21SBiZVr3iUnk(v6F8ENkfD(A!Ds7I&zi|1 zvsIonq=+M}NRDdG!05GTCVRPH^k^3? z@bLdBeyGkoo=X6_W(bYbk%q!eJEU+bK|Sct{^-E zfH^LV1j2hkU1fQw@JXofPGylrlLlPgXlZn9QlF}sn}>aP&5@tDGy$vZG(lAO?%JOCS9R{zfYY2Su22fKFIQ6pkYEq z^CX7&kU@AIqo|FlZB4BxueZ;mBR&n82eVi5-ngB1XZQw>m<)vw{u1~Tsu$sEA#7`F zYkF*H)_#i$hQLgN29vBh=x~h1QFo+PI=jHSH#=6%i0RbR03R23Zu_oe_@H!-pxVjL@Dj72b cDG^_h$zD1N0U?llEWzrZgLO*m}ZMv`EA@0I&+c;>PLTmupP zp8)+~q^eb=s#T?HF$cWy2}^1Z^q;)rfRVty1xOOyXp$_s?2!!%T-OX+KQCq=Bmnuk z2ATl!KrmrqhlD`LW6}yJ`_7y=B;?`6pC8x4;L@&D1ysuNtZsL6dI2|*B*{$%;_lG= zFK7Y$w~8{Q5%H`x_~8KDNRlioCGsa@V%`IB{I?1-5G6^Hq)3t;hnAU{sZJ~X{}(LN z$%a!(2A28I{|S&6ER^DnXpJQqhKM;Y9Ot)iT>kzxHZ&sTK8y07RHXAl9ASfl=3__h zs80=rtOyIgWM>lDxi41zUY8XSPPhB7LkR?CPaF|PCOZ>?3u9Sg&&!^;9^$%)y&qy) zeHJ^3pq&r#G*17H_pj@V`*C}*1i8#4^0^CnCxjp(GFe(27te)=)p~4ko$KbIh{)Kx z$2B<~#i!4Z%g4QJ2D8}HO+?I|nd5W(R;a34 zY_VmwCWA2<8WrigRK!kZFk)1Fs*2txdY>W{5qo2gw||t_Nz5>#>fLvj>s_=?pH&sP zT&8xOVP;b9bCpXe<#IVy4UiQn*Z05I_b)r>U-^poP9b8CjrQyw+}HN2lH=yL_o=F` zRts7hW^$N0xtsZCf`5VUa>a0!E4pOnYi4E`W)@6$tGl}D?7gfyXYb#fK2^?D{Dl*> zmUmFGev&h(m8I@EX3#(@X%Cn*MtL1Gl$`R+Sf(;ldF1%IwrzRaY}>Z|fRioB)=)JR z2bIbNm0h@)nVETtXFGktpWr!WW@apNz|fc!gB0uJ^Ei&(du`jQ_1LzROB+*)h{eoI z9;~$f%B#hUMl27O6HAUPW`@A;)9j7Twrz*?>U`dr`BrQ@omr)_jVgV*9Qgxw{)5@F zbGGZfJKMJRS~Kq}3$m@Ml@{#rrNtq^owV(Sr9 zKQk@P|NVBo1&Ih+0B7Sj(^}2biCUiIptk!76lcEoXH?%Yptbj^MVTM{3Z*chwNI*D zcQZfuGfE#Ypya9A`X|h%i-y0&fD&pKK5ssShQGpqyi0B4V<=;O^al=%+g#E6Gr@NN zE)$=r&;R{)nM0p_9`soVGA@46mo^Z$pI{YXXAzoAQ1{Xpxiq41SiUfb@D?MY>X#S| zg9yuDDS^RL477V>F|-@8cVVC#tp64bVtyF|g*z*UQRsT4=|4!=9|(O#@D`DW1TW*f zKmR;EBV*`^XYpKI`Qb0GA?GZ?x`YJ_(%2@%OL-xJ0olVKN=6Y3tb+`sqeK{3gc?Y3 zKgZ0wwSZ(85Xag@G?ZEn6AAzT!K)b9+QsjUX4EcjKcTgiVwYY z#+p51jY5zORDcH}!~!r7OXJRx0UqJ4PP^vT?{*BK(T>QH` z`dRTaFR4=aUb*l+-&m{2Y3xvUDgR#sH8}dypM2c*z|VZ3Dyt-u18x-}aH}j4TqQD%y_+ z|D*fRSE#$Q?i^VCL|e(`_62{IR~1=>L+D1Oj#SW6g5CvRm%sWs&r&A0`#*H&t1}es zOziANSW1P6$dpIV9_RH>x#^cx7X0k~rqw#0Ojel5KsRD%gjIJtVX~a5tAC(NS+FU8 zPxKo?qaeWu(Ty-vD1vCJd1(5_;T9RJ+}{kb8l%{^3*D($5OZmDTYZac_ApJ9(6vg7 zpQb*tU>l46d%70}_vp`l3`%XvJE;yNf=mzF=!p7U|?-bvyLha zgS~euHJ-wNEOCyK;}Hf#%M^(V2uF(!nqH{?8k)RACIP9T)j^K8lL!%3N*{5$ zo}e5Ibe#Qh4jDL81mwr2u_g?(TFU3&IA)Yy+Q-%yGdNQmDhXAXYA_(h(vwXF11qBg zqBz8tDh_0`1ewFS5b|U)%L*7ML22y(1BEOfBrFC*8CpP0R%XI(a$(II?B4Tn2CpEM z-3p831&@QGd~9NMt$j(hKQN~Ln1y37`L0WSbZq${jScqX4jTLq?Y>(jf&4`l9ik!> zU|wfQ*^vOH3jOD1!Qy5qgYgX}gaHdF16{2O#4OMrUM6~-J zXrZ~9X0PPsFL(30XV?Pc!axgD$Om5RyKlpo zN(?Z&J>GtZbAlf%;C$?1po0~~gWRb2FJv`uK;@mmg%H~eYOn$Z`-cyLff8f;TAI@U z8&)wzUi(^4XV&09$-!V*BZlHL6Gd4+4$wT#)`v4TT-L#9j3)M3hBI$KdMJ&er7&{& z#Cx~>KfqtDy)_GBSRuzf4;=*9_IOO4M>>DynM>Ke8DQh{sYEOU^%JZe1=zlw8~>Vm zFX((;v-wZsPE^`#eBoNYGjp_;jrTCbomc%hPevDDPh*#W1pq&a6$OyKaVHibsL|<^KF-82 zjQyNesE{aL+J}f8Sd3?y9McSI@n&f7!rCAp$JXByAA@dDw*4XVlR!x(?;r|?D=9i4 z>g3Ji!&U8~G&)#^4@U7ZNKC`G^6@5HVR$qqXHJBe>XXY5`SXjbBN;LHJg8)e?#{u zGY_uWX9kF};Z|y$07Vp!4R?$+BgW73QntD00C?xjvuwXSwy$BD?8KV$tQi8#US#G` z&_y7}mY>9A!9Pykvz8}@ISY$Q1$eTO0j3_Hv9Zuy%G6&jaT(kn`N_`YM}auc z)IUL2VR_=VobVCu%G#-pIN|+c&8?T^E<&}Njc-|NSEKwafLYUI=7D4LuhTkQU4p^d zd)f3>s=WXUz*EpXVDrz{^n-K-qJ-n##^h^Qupam!a%;;UnED@GfJ%p@`j7?mOd^j0lS?TBE!W!ob$CH2!v(!7C?Uv!ZquwvqK=;%jXA6SHe!KrY5cfrl6n zmaSsSSc)+R4_%KwtueX zei$(H4qkO+$4rC>A zJhj1Aw9<4S|Jc|*^3{sj4L;I&BgMvKC1wNW(u6<^748_L#miFT}lN5Gl zw2w|mxnX4(S7tVEXZkRmLVcO~sldwyqz0OtwlFy)o73DY|D3i~P;=?|nJ;tsqdC+e ztiPS$=XmCmY5bAHGy@o%aBI$ZX|l0>+6%^scjU%LvU3HI<7}OEVlW)tu*UQOiaF2; zR9hK870$MdDW9uy;SWb#Mu~CKJ$&j1IhqYen`tinRj&O{$e@Fq4NlVo4CYcU{r}jn zKugjAOx#42gySVbu+z9GkvC^%yt)vOi)EC|famCnheuo>H@0cXDDF1)ZRouJs-%SmmbrM$R$ z0?xdFZDPz=Gz?WE6KL>Xw^iar8ZyzrP+k;ZU<_nX;TQs#xbIH+$@Mk7YmN@vK?p%D z%4sX;lKPF|d~U(kF(p4q7&G&r*_nW5FN&J2(FqK+!@AYylgI3XAR=Bv9pZVZZmQClLp#30Qa?aZ)pHC`hTVmMct%UA5QJODr(j1MRPq_vboHh*ti9Ijae0|Ag|o(!r3svR2J zm@Gx2*`u%oAnXTFU82&zY3|&a*-AVnB?2G`3%Z-Jb7^iTo&W_966q?CupkG=R%jj2 zyn>_}5k_sVSA7p)L==pWj3s}3@XYD>Q^6boNV26EqLF56ur6AC1?w3NqSmU0QN?TAZ`_f?U4o}!hoz|n9B zAm!I(1~PI8a0DX&X=@Q!&r5^+)k~OJ;Ap-;1VeYOP(XwupztV_tJ0;VWIHf|I0aO| zI*`9Q2QFp?&aLNBvLCa=iG7bQ!J!dR+!9ofwOBc<7xS7$(#@@ z?Tnqp76S#^sUakBG%pJvTBOpWb}n{K+e}DoGih5Wpj*@iyvIyOGKpn@AjxE^IJIS3 zGvcdhTTQxLLL#v$NbS^!v((2F$+#?%jS4X1qq6V%R9Fkg zj#4PZnq@`YJ{pdTOXfHOsq`^v;%Q74r2317q^$n1=6A2hX0!oA^@ZLMLhMt=24!3vsPcBGI0I zX>4X{FU><}K0uRq%G--w&K#?rq7d)VeuPi}452`9n+a%B0t`WdFa*GWh6Mm}SIsHF zhyzd*Zh4M39u1`sj)1_B2nqulcI2^MdumDW#KUApSVCYOZ$g>=t8NKH00j9gbe^j^ zkyPxG2!s>2{fRGz2vcF?kAYFb0^u+6$J5S>^T&@ah5(}Q9vuN*0Vr5Q#R0(3FeC@V zIixCbw&dcYG=N*4x0lmFA_?9Y5S9Sp2*~ezT@^VSs}3M(8|ju740}cU5k&z2ct*M- zmjEPnq;R*ROn2@gueC~?Nvn?KX;Xm`7&81NC2l8z5JF0x%)9;VPiQ|RAh)GPoI$+W zBN3EHv*Jic89Z7_#3f5oh{KAja|cL{>*=)WwA=y;P8So%5Q5-M$I=o*ML>xXzCdx% zR*vhblse&VW++*W3?U^?fRsaVCLY4jkpo4p1&k4=5@W#C%ptFd$A*AIE3QS!QYjpN zKp;xK>$#kC#V|42W=;8f-}wUP^w^TePw;jL!G6II`7Y;h&PDzwEdavUk-67!sVsRa zAl@5-@)!gld_=B;Cb-73T%-q&jB-j8KSh4wr)gMrkwdG%g(i zfSlKPkUX>$1Gglp0K!VSaR~^*Q2K9pG$3XXh(K(~du@TMU0#}OK1T?P2{)Dzc!a=_ zl>S^|&|b<4GJ@1lVl65OsVD~n>q4B}99GEX{@@Oq4nV>q+9!&L;v_~JK>(DA1TO9& zyOazGu+uTUP)ZO49TWLPG0_4+FpxB5fE<2H6f$Z%5Tw@&C1A}Wa~KT5BdW>jcmH&- zY;%c{=#Y0aFd5b+ib4v`_wK1e#kjNiH`f$L15vaBqD87*YUg9pNPGN}ct(&Tkbt+E zJSs%{0f>@#PbvzKw4|eq_4&tmvO;QhV;~4p0)+}Pww$=k_!yTD2@n(|xfK_8=J-tt zccxmc>4S`ZH-OAx%pid^+|Rihvj2#+gLR3-YN#1I-9F}->yT$*U@~mAY=)ULLP&SL zIzq`2=a7Z-yZuTHQ|99yBTd2ent1}%0kuN&1SQprx=LG>hYyq>W&5fZQF*}woi zxb`EAxU&RQkQ1>+{?{o`awKJK1>zA}Jk-BF2~-iEk#fKi@UP%JQIcA5`^ZpR!v7b>o?eny?AWbv!tzn*?D<7?vE_f5=q$O{FALrZa)C^?d{Ovxh7LsLPBUxHm4 zX&|#=e*UiD$(Oy~F5a8_88r3}7ypvK$W4VQv2X8^`~>3ary?AoD3TJ-U}gyM3cxh7 z4f_#6|JfI{{?TR9))p1`1Fz>TpfQP{@EBV=hr_(en14DxWzz`4$hAf04?H10p?456nJ?dTE zysbqqi+X+dYCexk1l`Z_b!iK*;xx+1x#2`m!2kgE26pXDhTd261wiDMp=~Y$e+ZW! zIESaYF?g-(A(QYX^YUO*Js$nF^V%OfcdnHf5pr@~!V|!=Q$v^##3{}c0U#-fFch&u zDE?ICBJS?DSdWixHZS+HQJOaUc3Mio zVV81{fOs$&Jh6^gPkQ69k)R zL?i>d4mq&JAR84yBAjpBT7&insW7k&R2~%NfYXMdwT;svAh0HfRlrjc=dA-s@*Fz| z^unmLzDnk`TVYQtk%t#Rwq?q}at*V-@lDqjc$)S_63nsw^K^;UG8aQZW7i*}*Q3?hoOWceo9$Le|6p1UM zaA<$IeYB8m;KM@1OX6(lFgh3r77$SYk~*Iis8TZflF64gB@6^e#6t+M2i9RyNRKFD z4-~Bgu!I2(a0`u_kgMVS-*wlG@5J?V?=gaUl+6<86QmurfqOE|t(gxd5e$84_PFU> zWBqz({y2K~ zUFkJ|LiwZ8i^(waBWXb(K!9LhxP8}wMN30bl0Df%7%Fe4{+JPd-6nl@iMn3>&gAx- z;Qv}xzJG^S`^bj=>gsBL%-x2Miygj|sH#%mLhnneGvjn;Jdp}%D8tx`S{Oslypydi zs>L0gZ;03;@6mGkoESVuW(MrX!n0ONU7>)+6#R zBv#6W#e`z*HNvmI99-Wh@;^aoT6vN)%h$h&Wb}cof<6d_{d>(_y)|N+sw%dU;&vKW z7qUbM7*I*SDlY<#a?NDuhVRh1e2^d$w9CuOJpe$K z;d7tc>-B&x@gE)a`~B5k=3bxMd+u}5>$T~3Ful}9TwU$=7NYic)Xrb@`q-gOVh}~90AhK2@-EjL(($;q;v{c)} zW;Ty=9`DGOmPj+wlBBVDQ|+=Qc?ZpC&k_g(;!RT6l@v;z(&OQR?AWqp%g<$InfGi- z*HS1cDRifqnKH6t%iBdWBT2L5EzNmCp+Mh~&Xcw6%|dQ#+pet)yg!<{y1F{wpOF5G zq5?v)`wS((PHkHbvYa(M9-Ht8ex?ZfufY=XX$iByjU>sDlg~Cf<_S2F5F>#}C&Pak zHc$gj0vGx}0k~eu(V>s;q8Hp3a~F*<3cI8yXVvk0OtRJ8UmDvRRc#iNAUL*@b#GRnAs9|UK4t*0z?FWBeUbj z%zCCA`FH%!K91mAJFH9XE<9#-J0l`;)JMQ~q}lwo07uyN}OXaie)rhv0h zUY-%VfiqAnH*+>8xri~Cru|bSNshZmBoz!TGc)6NUfYks%)4T4#_H;>%F4`$a8s9u z$3G)8Z-SqgS!F47F|$g4F?Na<23*X{m}2R)%-g9GOSiHRGfO3!$&*gZA!cR@8#7we zwjGb1=lvCHY;JoGYum<|ZTHZ&-JaRD+p%qXl1wvk{_p=@Uy*FPwr$&%M`}}RNn~Q0 zpA*8+FcyV{WieR*bj@&U+g7d2d++}^bfy`D)H?E%K}-$guDQFrT#;zoR?tT0c`HiM z3n4jGWm&g9XtKzHjRaQIgfF(m)JC zL~`F7-5(GT@M05!fy73dL5xoBWK~Aq|0)BmX~fwGtYzjKU6(x{fal*i#kvzOw{{M7!NMhT*26`ddDwrEL1*{j@k^vVB|AO-*o_Lpq;-erP$uuto{bTqn) zHrala$-+(~s*bRhL^YW#L5je2w1!dyJ|ZZQ$def+YLIY%WoP^{TFjN+QhIH=;_l8D z7Z2RhMUV#o%nm&mIenMW+FvYeCGcab(610W{Vt0D9IYo(VsV*DwdQiL$iO)snI~X^ zanSnMX@y7NyaoUO45(I_KmdSsX+d=pLlV`1MV`_-b=gADfyxtJgcAY#hM3IjtkdVeM3Moqt0ecAk<&N#Y1Q&rb ze*evmp7@J}t28&QtvuqA&)h-ID+CV`j0Yli85v=ahEy($l2C!{00i9xD@pac)4%@3 zO$s!-;Z_~YDx>!yZJha|PiM?4mM0hg!JFlm>JC7$%P;pd$U*3YAP{@ky_>pkc-1f6 zmmm7)4`6itFQipuHNk8`GuXx{_D|JD&r69LeJ^$*iwW*P1P|o%8$1OvCQ0@0b-6%m zQTW+k@3>WEw~jW=-bG@QRrCG+lMx2{${KyW#P~76JhF>bo54KFHNh;t7lLbo<)8d( zNfrd0(DL%))Jsog5&a~seVD5Pnr$-jtFy<*ug>h=y?%^oZ@nH=`;MLom|}BGM39g` zM>DTFdFj_<@>Y89`qBH~|7mtZR)KePT`C-Q&*yG2gV%^!4n?5RP0r|jn``5cH=b)SsplAP2cn<8W0TG2bDsW@1PQbm9V41@R%{$oF>cG|!G8nrUCo=bjic`P++>sSFu@c= zY~_QSbzUU+Z4rLRq#i!%j;~KfyIE6?u1mm4+(7acK#>qMv9ORo1^(Pg;|AacO*w4j zG=@`k&)1lE8^I*Bzjun^j_+lNaTn9veyZ-F8jPHN$cQtWZ&(C-FY^d)1V9uq^g)6E zSX^J&&3r^O+obu1nK2d;Oanno;MOq;@oirNqNW>hECk-X$tE-ME5wwu338RgR*A3_ z;2#W}J@qRbnu`N(zpnLgIny^hr+WEjDaO_m&s z+c(XZrMWU{1T_X~)vb@;NZo?W7-M>=0dzA!k89|;d9I~w4yuM6BXC61Hs?U@ygev;m{1q zPrfZLX#d;f4fVfI#X#z^R`d%VlC+56Bh?568iJz*PU;ZRX2wz*LC_EqZ6Ro+25l2I zj@g>-)PFkpkK6y##gG1S+s2~kN{GGp|2ca2>wiv1(Fl^2yFQ^r5~zhZiZG*GNLy$m znbC2+H1I(rjzmbLE6RX|PLsqDdIHbT{CKSe-rP8m%yk_;e)1akfB8T77u8sUE29S= zH6MW=7pzayC;$@0belmmgzcRDXry4Gp>?7HmT|Hpmx$vhPGPj6o>2s{Pt?%De<~j_ z%8;eIK26b)XdW70BaI}nlO%_RvOko2ggjArh=f@PCGU}>hK6#W+%GzF;s`WE0OJ@S z2zX8$CpEdhk50FAg=R8pX)t7`%Ml$7BHbri)9Abi6y@lEFoeK8#EcYY1PC zbN%RtgDhI1;TWSH0_Y;jqU}+B`K^9@c>^HM29US_0ug8!ec`TwD`AVvAwFJyjve=H z0mz<0pwJRz7YlI_t~rAppZsyqfQP_t<2b<(6k3D`X&%DC;RSINkj(AWvDGt-7ogVA zDuji30wsC*u;3&{W|&lHZPgEch(CX4gsHt~E#e>?IQUeLertdYeop3~J#PGM0H?VL z8v?bJAgpfKfWo}CU=My9D0!U!J@+eeBRd(|5Vn>L34M>^KHpEgR$Gzd0h8xb7y0vd zzGvJD2tbS3y@kCH#a(wk!Oh=mQ6rqav~#I)^MBYNG_)AVm?we~Vv$-I?)c~yC`lQS z9kZG7EChJ891+61hy`-tSZ-SnNFjO&R=LQZztQ8hS^>R=)*}L0w6M+kr~K7=^_tM{DA)EQ1Attm}^uC?DikI7&5$}Oz-in3s}m$>rh zSxbJ5$O9*fGl7FdOp;_6C<4!ffg_aUX3+q@DBYMi@jA{emwDXiCD~kkJjabVebys+ z)_|p86i7navsBOWVUuJy`hI%r3<1rZx$=Dp^Uhk+tp1qXcHc8Cag6*zXKqyLf>*CZ ztlqTC+_xqNZhQ4RhP=M*nP3I9l{kT*1nx*2;y(ghFCQM-j>deu%{J zd7^ZWQlKm*T=ACFO2Fb_EYzpn4h>BcCuZx(QYFKQA>u_796ua~9_E8)pY-or{F1Ye zx_TJp4Wb|W&he0S{Pyd?Xe(V$o(T)Vks6?pq@z;^1wmOnkSI*F9s)u}TM=}mHbk^U z@I2{4f?BnF4ltAU1pW|>YHJFiMj}~LDm`8X9{3E~`%)?l* zM=2d3NkBwo(3s}7;KGFibMzIGc(VB|X`k)Oh$#CMI)j#zO;|vyb^vq+(-}%<93sjA zbZRt$tXry zb7^iR@C1#@IrJ!}{vex2NjpM9Tu*~Cj!>dAb7^*&E8Tg%4Tv{$NwiLPJ~UYAxWwYp z%)#Fil6bprd7djjcZaSc$-v)Lol1G(wh>E#JV_E+A9yU*zdTCn=}fTi=J%3h23n?d zfU7+C9-w7T{c-+0Pop26>J0mHUesrMTwgOg?%RUhf3@nX-~7qOnCcO^lOCV=^QBov zV@rUQkIvCIA|wMu>T|Wn-)YqXbcQ+c^-FULxsr@RPpZ4G^r6pBe-u`GjvKeI=A!iW zygXpYt3bx2PFtH<`S9p;fXh~YoB^UuRzHqlfS+FZke5fEe6l5w!iLif<1u|EYc;~F zn~2pFop|I4v?Wix-Nkw<0MBb~tkaHidvoV^N+QwBY54-zDH|M9A{dL^|n z`l~mHzS^xnS!BYEU?Y-wwmDIwe@B6Sa-G4B{Z&xkq=7I7A%b<&${2)5$Ee`pL`71GS22 z)}^Uc|Dzt9PuH@+dvoGn6llH`5vAo8vI6-=s5w&S0U_!$#`?isf@r-XMQi(nL#&+!nZ9 zpMUdy)Utnpzps8Hk+>aULvF%sRMOl95JzUMWxmuvs?$11eH%-)G%7;1cVXaXp>cq# zKl_OmtT%J+g^&6cQiJaLzXPDzkj8EX@`ZGmq|m3ji6FedPk!TT*uC_JuY`cF^tCTF znYaW%BM_9UTyd7_0<=fi_wpxS3gt|_BCUFsj^W#sYl>`}gxmDOS^bmFEWITzN06Uu zHhmNp2RQW!2QNo31F2~jpj*tn+~>bMqn6!60}^mIk7b5v#*G@Ijq+Uex(~tt_>+V0 z&U$N>Psrj?r*$xAa&sETraf?0|DBDIbHPJ7@3zblEpXuC_r3vx;6^4QNln@X-Q3rT-k+!GhG>L`(hwfi+?Fd|c%C7ZU{Ik&dSe{9n~nc65W9czn<>?VEnep8u?1WK z4c&(egT`bH{5k(rmK&nn!5||J&8d%0;L9Lj!7z@}-^N0{eMNhd>Rbl+qJPH%E1cVy z$U*~Gf99QfNigWR`m=8=)H4XL>SGgP7J@4?zg%$Fo_=7AfjK%rHcz{%fZh~k5L6A9cAI21G|PG(CvMrjWkUJZFK zu$GnW-(&3V3GX*aLdYm+)--Sx-iUF5-T-Pt5<%X^lntVD!lqYn&g2x7g1C+0YGT8;D| z&*#wf9_3A{8vXs05|EPk7f*L8Ivosc=oG!BXe;?;3z^m!dV0O_bmf@6Gdnk&O@nv~qZ%AI?_3w3qWd?{K$=-n+-+@7(jL z4<7i|dwW|U4WHiL4q1hhbe499fyJGyHX0}skjo??6fU?jAaK7&^N6>2QDGy}yJ5#*pL)&YpO-^J8aaIEH{ImHi!|m^#+M$gFhG!(SV-p`2AxICK`G(}sNwu|9sCyu}I@-jEJ`~Y^rV#Kfyr`(q zUssSot6s@WNy%%JAcI!zGY%a^5J=$ z;JE$|TvN`PB2bNqmoOZ*SV}AkQaJBozMaJR+jF}+r{>P4e(u^?b5(!x{FSR7I@dXK z_Wpb}RdeR1m(D`&&|DqwVgTHC?M#5uc8=nHTPBy^TB zeLczE!%_OxSAUhNR!9FSs5;m+eBadaQY&P1IA;g-0FdClPf~ahQ}%IC0&viyKCJqF z?=h)1in^(lB`mBZLP+8e7pLPm2)%RRCg-0CMu#U||LXs0)l9Qy7$=B#T#6n9-uZUo zzn5^&7mGW<9nzt?k0`2YCF#k-s!pJ5sUfw3G-}E=G&JUk^9y-%%nv+ZgrZeM=t3F1 zH{+yCY~>sssSNR?Vk=I?3Zhdb^P`HY%x}EZ)bp;~MQ6RYM$Sfho7Ff{q@lFOv$o=7 zb^t6M;A=*SM&LB{|LabG*M}fZ>Z2$N8MGK0M#jWJSzK{6mmN^qt(RE!2Qv4zs_vKE zO)t8~pNxVh-grgg8q*mV-_8VX9yLmQPQ#lh1OO-_C(I<)IhR6%qTuDg%?=;i&jTvA zuBvJcQ@mK!T@$@^s8dK@>$5J4HG`3g^J^a1q@1l$G_k0DiSjsGHx*k%+Cj%2rIKof zdlXdzhl8Dt#~Bvk0)#}J)TvjX)Lwnc%$o@kz$8pGaN9W2iGsDx{(StBb|M zXd&t_8|3?QJBJ%!n&3r;lYeku&0#g>fLnNR<+-vu3u{xD$k4>`@aNW zhIarXGjkmdBCF~;I34H0P`QSTgTvQf>cbb9+E>JL5TVtBvJrS0H4goZ)RJl}W+-Bw zl@!tz1#*qnS8nY}_yWP-$|Qs1LC-!a;Y@HCgi=pHc?k0}Y8-D)XaM-r2cA)uQ|64E z0$>hK+~e$Rl6_Dnc0n1^!Xxo0V@wq52?T*ht&CA)V}Y7K@PME=oEDg;_8b<>OMgP+ zNQeotc1h#IS1~6`L2F$Bp=6qvq5L;`kU>?P-4Brx)2%dYufTjuaI(>Y&=n5=DP<*W ziFwg+UGIm(1o0DOXmZ@*uDEAh^d!QM z1sub1n!rVn7a>k>At`_CQf=e_My>2fZK5@FJhnV!81GNY453vj_utMO)|F=r%&sd;u#Y7Kru5vxeS=cfx||3hUIRF!^}O@3Cr8# z=*l&hv*BTg!0HPA|MM|tEfp%wOp;QK>}z)3UrXq|3phJ7@o;u21o+DASk)0fFfJzN;n7G zgQir3dDVG?6V1+hoFN5M{c~PTkbT$%KH8yqr!Be#@RNbMf-k^1Ykh7NU}_qGrwPO~ z=;;y}O95pGoKAeQBbkx?d)v)X3^lBwDK$3S78AtJmeVpz_ILuQQ@Y@JnX?(S%T}r_ z3ZfWrgfyx=#f<4wHnWH+dGuu)r`i+CB~~?UX8Y&k(@I8%bgcUrBM<>vr@rg+b3V~h zJ1)s3O1dh}Os7n^iX=AfG4D0{mkc8(ngSK4(EyTWO)rJS2GL&RzvAZ5QVj$ zw4wGCFmVdX{houICuo55BZ|wo{hDG$35HT(jj;MD_38%kLNsq;)`q#DiXBBTzMS7EbA~-N**=0y66ycmQD*fhS=Owfv;o2fknj21*A6K7v4Wnpy#7^$T-EbX*t> zBvJ@Ggb}60Yz?3?Q-T6^V`-6?NE&Gi&uuD2NOZ@z$d%L45tAxBc^xt{A^cXPgI0C@V12sey8k<*>)|JW1Ah7~n83 zfs^X#gZUB^(?Qg_ zQPJ%AR%t)|w?4Gz8ejSRPfs!BHWNHaw*p?MDYP}24%>2GD8pGe{C<*-PCnu|=BsVA zf=h{&CpsCOA&FuF27Cbr@El;NP9dy3Wo+rn4k+PxHilH9l_<_jkGV+AJ`1DVd_04b z8jyknI8GZC2(Um5{LH93ak~y&Z>QSk0p56GZ8iXeQ8r(qQ)sgGwf>PmKk#(?C?&zD zD}<#=;6N~OVl+*r!w%(zC~XSR*p|>oXEt##_R&7=;m;de&}rI3!^nQe12g4C(CEZN zn4yg~(ui_KixbC^RRS>H3*S<`jS$$MGY0Bs9)UREh58$YQ-yYP12jN9jONEyzRA=4 z;TxM@>w(Yy^8UjcEa@RZy2wATSXGPDR-V8G0_VrK6G0HX`T6P#86 zJ9v-`Ib{Exe@jzYo&5fN6DWq5a2kwoMaR$G&jtMe0JP9@ps(#}>k%1&G=#FC480E0 z0uIin>pKIcX~8j7j;_Mf3^7pIrG-#!Cad0BEu&>=fwS{eW|m?kijH3xH*_LBzSh9t(Hf*M4+C-_|S8(dH3vFo?^I>Ql%G{Km-SdiM=wsI|)V>bZ1{jdu=ItKEPvWA(O zE}B8T5m121=+IKQfs>XRNtA$@n^E7AsD9eh5f7>impctGQ_>}M2DFwHOHAVIXuvM| z6L9Bb;P&?z?u8Ntm zCb#IO_4dF-sl)<X&O{n_-eDlFEOb6s=RP0sh=bBU28jD54*D&>xf5?!Z8jUb`~A zS8!|rwQH10O@O|4i%IRxaYfK!+sd;3TLdT|<$&*M;*g%VV5;C8)zG{E`Yt9wVjzxE zyT#SvkhnMNKD+)N&;+tU5Ed6OIP2lMJrRcQ@tJZo1~>&kA?&?JX?;mcpd!D(B~hEH zlBNP+zmEmusJ+SX)CZQ^g2hFbU)~5b&_A8d04C;9w&a!`?78}NjldwuWI@McQ#lkv z>RLoYM0rNa4@K) zy=_jFa}G?|d4TwovicDY(}*5&)q|eAb$Q2{Oo@$-)*8qm(dl#;Bsi;F9v3MSlq4ZZ z62K*#>%SNSC8$ZqfG3JE_xZYvrXhJMd38{vv(xEl0J-dfPCq23mttcO7F!4*?XP61 zA~I9~0CgN;lDu(16tlTI*$XTM=%W_?a&8Ik-U%f2z!DqQ#4s31j8Cej{2FArF!f-o zJXt7+J+y?Q7n?Hx+~NaMr18RB8Ut@au;Cjd2^|C-7B0Am#*8je9|N!@WXXHmJ7hr@ zlU0=ch=G$50!i1Hv)9ZG;%=@J1X>;yGho7CIBIGP)24*94!>iqFH_${U~oloQvwX` zD-8?KB~$%ka!`t5P_Z>+N+-b_48mW(A_c7 zAoyfKAWgfJ14IX)BCf+QP#KzUQ5R_3V`c`d7~t`YrzkT-OMBSXm)K27TKfpR6REUqLCvYUJx4%Z z=`N@sd82d?1nf|sW=hV?sY;7EgWwq)gn}BGp>!FU2UXs6Q`7D7tSmLhqSV{R@mRM(D>n5@sg5wOK{Lcxw`@9L;U`39KH` zLuaPk1D+k9rp4sm<%kU#7{>w;pf=(d+C0kjqU>ZC=`#Dx(~&_jhBl+)DV6_R3N{yO zB*nT*HW5~&8>I&ZrMYaAx{XdJNg%WY9?W$mFy;nx-uc!a@s{PeFsXQQYBFOx29J{^ zAWs_o-iD~w7G1Q>{DX@f>fiJQ&3#j1Lj)gD8{1rD`iYo*=A*;GY(ADuq4{Mj=>Q%u z8>8@GOi}KjbWbrGYo2aSj~$ddem+KJvyvL`HceN2Vo7Qmx{fv9X{!yVebR{LGi(~J z{DxJlm#~Uy_#@KnQ zCnbqV#9;v5vN#D8aQr#S5p9vg;!5lLu>km;uttHOoSK}0?h~hcTk58TR3>O&wnl6K zpD`da&$!!882|Sd7d~`+m6DT(!IPDm&LcJcQcDPbu_vN@L|1-hs}dSxA((9&SIZhM zmCo$Fq|DTQBLD!;go;x0CtAcJPIZd6HXP$n#}kQJ(Q#ZG-76{VUXr#kp z5K~(C1IKc%Q5&S;qCMmY904ne79bn1empj=#j)L?gT)C@<~ zQ{Hh6*$7uqNRvg4b2X7VhrB!nPwWm%>k%ASSAjB5yMZ@VZ^!ctzgg>ZyH0RV;q@&sG@zKkU}&;R(Nzd@Dz+C#_mn+W z5QV&jiGv3*u_%>qhNjL?Kt8Kr6jfc1{4@9Ajr40j(E zW(%1yn3P>(cE>B(gDOr(xe$j@rKzTC@DVLJqfsC0CmK65ke@-6(A`UhF`l8S?4{{? z{jtI-Jam6NqvWSx&vj$0BZ!pc33rTqKq)k%8AVZ4Xo*t|QP(2vdugngz$9x{69-Po87rzvyh=>sQ804V2qC+aHXdF-o3D1_mUm*>b$~Yxj;#~g4p^dwsO{FN8Ib%gp zyY07|RRM&d@gXt>i4>ZuP0AH8+wK`ZlrU-q5H52!Fj3ku~r%F`+u(7}_RjG+#t z05jpaF(upw>iy?-+WX^DPbBh!+%__E0AxTSDIm}@^5nll&7lZcVItsw5-To`?OYRJ z-$B9zy8oNE$OL}ETo_Yry{2mA^-YTkQwGLvOhPe%4%HT6XW$8g_09u3|Vh#Sp8#{?cZ%4~kwZfq|$iv*TRfQnjZ#&(QKd)az#fFTC}EGJwQiZzyK5zB}Z zs@CUCG+fMHe=2jskeqw>v_-E$l0po@s2^834K{1g2w0ZK*aB<2zJmfgiG-{d1@eF_ zVh%f|U8hcvd#=may+3JJD%p^-hF@r@v~SWTsA#SIi+iIYKmiIx&{*nXwdS0d%#Kct z^Y=7eB)e?4lxqf6vxcaKw3A5#(kgT#Oau%44i@wbco3wJ`r@+hl+^Z9JsiUUmzbbT z^aUdX6B85#iiapk6c7smaC1tAhSpACD(+^MJDr4IsFtIfv~O~g z_EOWnHcAO71rP*rx4`O-&~lTCV_T}~ODE=>Hr0Y*F{d9V_XHDqL{+yyQm|X%z0E~I zBoR9tQ_uu}*H2$;)k+UQ5J0;-2v_M%8k_`5b#M{zdkbPD>*~rXEz=%nj;;Opwbf5g z7KWhWZuVD#i#qib|8slEyCl(aYqSzdDG&r38wBpht?mfAn@;4}=7edjhp4KHc}zKl zoVFEa#9B29IWb-wOS@!}QR&2C7zcI%LL{UpBA|S61rNNwrf8%Kj>~kAvJ2$~N>!9D zfMTKOu2^sqeI9mZgDW%U{PF6S_Kb09MvjI`rP53uJIm_csi(+}?xS|4MNt%nervTE z3JL_Sq8ptcH5ytSF4oC;sTvydDIHy<4Q&~A-M-`49#Yel9UBYA8?M?G3 zr-qUw9R*3DP{q(RRW!;yTIs=GSMjqdeU)(7RU0g~>M1Sg&n+38j?q6O#3t1t6jU-0 zQDRQjFC?NcYFAp+4kNYfn{rheYGTgmC!L@Z=M-2VR-T0@TKR-E^K;*UUGo{1PDgU6 zzs~fg=MxDX2?HdCf-+q_N{gnFty5)Yu#m5m{Xb+CEI>RjVwW8PKm~Q!ZLaJU2Gs-- z!uLUqKnKuRy`o_9WCGV&GJ+teL;S{?{-5;rp|10)5tObeIL!9FE7WH(!2*hmbu+u~ zT`WgT>~PA(hxxK=$Y5`+0w`!eYhAHczf2~lw~FOAj0f26J+;Lyu(AMA0)SBA6aa^& z%4XMPuMj{1fcASdv}y$alZg%j-O+xGO=}J4fTVh!ZzRx+0mLcisOrVXuxxuFj zP*8qbg~}I0i}4^mm4U{xyykgm0R9H$_bZ>ZghERaUF|G@MirI~hXSC{>SWhGHPHTs KDw!0}WB>p{?fdQk literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/values/ic_launcher_background.xml b/android/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 00000000..a256bc80 --- /dev/null +++ b/android/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #EEEEEE + \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..c35f762c --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + hlsdk-portable + \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 00000000..61060cd6 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,17 @@ +buildscript { + repositories { + mavenCentral() + google() + } + + dependencies { + classpath 'com.android.tools.build:gradle:8.2.0' + } +} + +allprojects { + repositories { + mavenCentral() + google() + } +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 00000000..87cc72da --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=384m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# android.useDeprecatedNdk=true +android.enableJetifier=true +android.useAndroidX=true \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..2b338a935b508eb6286ed4b84a91176ee5fb93c5 GIT binary patch literal 54213 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 W0pC&I?SueJAtDe^1>pYz0sTMJrJ5N4 literal 0 HcmV?d00001 diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..61656665 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Dec 28 14:36:02 EET 2023 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/android/gradlew b/android/gradlew new file mode 100755 index 00000000..9d82f789 --- /dev/null +++ b/android/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat new file mode 100644 index 00000000..8a0b282a --- /dev/null +++ b/android/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 00000000..8b31d676 --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,3 @@ +include ':app' + +rootProject.name = 'hlsdk-portable' \ No newline at end of file From 5f4c3a70e22bffed91ea9bfa13e5386d2b30306d Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Fri, 29 Dec 2023 02:24:51 +0500 Subject: [PATCH 071/127] mod_options: enable rapid crowbar fix by default, since it's only used on the server-side. --- mod_options.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod_options.txt b/mod_options.txt index d68090c4..ec7d0844 100644 --- a/mod_options.txt +++ b/mod_options.txt @@ -2,7 +2,7 @@ BARNACLE_FIX_VISIBILITY=OFF # Barnacle tongue length fix CLIENT_WEAPONS=ON # Client local weapons prediction CROWBAR_IDLE_ANIM=OFF # Crowbar idle animation CROWBAR_DELAY_FIX=OFF # Crowbar attack delay fix -CROWBAR_FIX_RAPID_CROWBAR=OFF # Rapid crowbar fix +CROWBAR_FIX_RAPID_CROWBAR=ON # Rapid crowbar fix GAUSS_OVERCHARGE_FIX=OFF # Gauss overcharge fix TRIPMINE_BEAM_DUPLICATION_FIX=OFF # Fix of tripmine beam duplication on level transition HANDGRENADE_DEPLOY_FIX=OFF # Handgrenade deploy animation fix after finishing a throw From de5485b256c1010d6fe74917cafc0c57f5f85b19 Mon Sep 17 00:00:00 2001 From: Velaron Date: Fri, 29 Dec 2023 12:02:50 +0200 Subject: [PATCH 072/127] android: remove some optional bloat --- android/app/build.gradle | 16 ++-- android/app/src/main/AndroidManifest.xml | 8 +- .../app/src/main/ic_launcher-playstore.png | Bin 40387 -> 0 bytes .../java/com/example/hlsdk/MainActivity.java | 23 +----- .../drawable-v24/ic_launcher_foreground.xml | 31 -------- .../res/drawable/ic_launcher_background.xml | 74 ------------------ .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 -- .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 -- .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 2638 -> 0 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 4382 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 2086 -> 0 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 2984 -> 0 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 3824 -> 0 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 6368 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 5096 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 9222 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 7344 -> 0 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 12220 -> 0 bytes .../res/values/ic_launcher_background.xml | 4 - android/app/src/main/res/values/strings.xml | 4 - 20 files changed, 12 insertions(+), 158 deletions(-) delete mode 100644 android/app/src/main/ic_launcher-playstore.png delete mode 100644 android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml delete mode 100644 android/app/src/main/res/drawable/ic_launcher_background.xml delete mode 100644 android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher.webp delete mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp delete mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher.webp delete mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp delete mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp delete mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp delete mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp delete mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp delete mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp delete mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp delete mode 100644 android/app/src/main/res/values/ic_launcher_background.xml delete mode 100644 android/app/src/main/res/values/strings.xml diff --git a/android/app/build.gradle b/android/app/build.gradle index 5336532a..084b5100 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -8,15 +8,9 @@ android { ndkVersion '26.1.10909125' namespace 'com.example.hlsdk' - packagingOptions { - jniLibs { - useLegacyPackaging = true - } - } - defaultConfig { applicationId 'com.example.hlsdk' - versionName '1.35' + versionName '1.0' versionCode getBuildNum() minSdkVersion 19 targetSdk 34 @@ -61,10 +55,12 @@ android { androidResources { noCompress += '' } -} -dependencies { - implementation 'androidx.appcompat:appcompat:1.6.1' + packagingOptions { + jniLibs { + useLegacyPackaging = true + } + } } static def getBuildNum() { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 23f98ffe..ee5e0d62 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -4,9 +4,8 @@ + android:exported="true"> diff --git a/android/app/src/main/ic_launcher-playstore.png b/android/app/src/main/ic_launcher-playstore.png deleted file mode 100644 index ef31446871ce9b941f175d6700541094eb9ebf29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40387 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