From e2bf9aae6657fad5f10e7b13dc0a07568b16e728 Mon Sep 17 00:00:00 2001 From: landgreen Date: Wed, 22 Mar 2023 18:03:05 -0700 Subject: [PATCH] borders for no image selection menu mob damage difficulty setting is lower recycling now flashes green when it heals merged cancel and research bars for single column selection added some dark grey borders for no images selection mode new images with midJourney V5 spores, pilot wave, standing wave bug fixes --- img/field/pilot wave.webp | Bin 7468 -> 31018 bytes img/field/standing wave.webp | Bin 35930 -> 31240 bytes img/gun/spores.webp | Bin 47790 -> 70356 bytes img/microstates.webp | Bin 21122 -> 48456 bytes img/mycelial fragmentation.webp | Bin 53020 -> 80530 bytes index.html | 1 + js/bullet.js | 4433 ++++--- js/index.js | 127 +- js/level.js | 1429 +- js/lore.js | 24 +- js/player.js | 4530 +++---- js/powerup.js | 99 +- js/simulation.js | 33 +- js/spawn.js | 994 +- js/tech.js | 20756 +++++++++++++++--------------- style.css | 65 +- todo.txt | 68 +- 17 files changed, 16835 insertions(+), 15724 deletions(-) diff --git a/img/field/pilot wave.webp b/img/field/pilot wave.webp index b8b54ae2b8e2ac9e3567efe79904789f980ddf94..d6b52813db5b7b631621e55ee7075c87214a38e4 100644 GIT binary patch literal 31018 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 B;~4+| diff --git a/img/field/standing wave.webp b/img/field/standing wave.webp index 5f237b6b3c3d5d9608f05583d1c1525798ebdb34..e8f83f61c582a8a60771bdc446b5d24ad9347d70 100644 GIT binary patch literal 31240 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

ZTxue~5A{nM<4eI8pIyM8VLD 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 ^Co<`jeOK|!J5;1|D;cgb_^0_!nbq*tK(@Ng7 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 SM?hn`AS=D*kf zy?;K{Zy8mSV z!~Ub-zwO`Rzh!@}e&_o)`6uV!+CRMh&;N7%lKTSuH~6>oU+e$mzrFuu|HJ;5=U3W) z#D9VRL;kn>KZ<65{a5^__ut$9fL~GmP5qDiNBK|kf8M|Nf2{TX{p0-4?HBJi$lsj* zQU4G9!}hQHkN%Iq590sHzpVdZ{}ui>`&a(Y^q*RPga1SI(dVLWa`Tu+OA^#Ke&*snT|NOtr@1n2NkMn=CAN2o!|MDzF z<(9P_i@sy>7rVC04} zL-}}dbuy`*Gr2|M;s*GQ!@nf^bnk@?qeZ9=M<9D_#{dbg-C_XL7^Is`-G42&^S|&@ zvSxLoHG{nJViy17W?^86&pp%^UnXD$CJ6kgyQwl;==I-|L_99@I1cf$Ff!^j}3l| z-BBL{WLy`TRJ$o87k*BD$qfWt(&aHT1^%+v&L)8wHOeL0ZFb#XqNH1=Kg=!c#ahLI(6M<0JFc=HeSeJsxpNSmEv-Gnf?X zO-h0he-qyBy2vezEY`(Rza>7+z*~*bTg7%smruf@CpIZ#iYF_(DIdM zO@6mOY3-+s8RDEbJ{F<7+c4N^*@}vt1agw$HA>C4M2B=7Az@)0Kys1))YpXqdH8@< zD&9w?Vye5c3vVepM|CsNh(&s+Tkz|&gUw-1(Mf?Y)?GD(uMxoYt~)uQt^C`FQP4J4 z*q`<9Qer2BrUck1#ECy^s$)NiC1+V%GIi3}4|y>VG)PL~#boMeqotGres4E2Tl8GQw7rXGqYBe$PXKL~xS=rv=m=qP|7_} zqkvNgT8mM`F@XkP5i1`JAyxmkgg`Hmx%Hmr@+}%rUksc&MqwLLkdC;!b6DZa!PdjB z`8GrxwL$-1qBwoRIwPC|K~57h%0ZBFA5secIF{b z(vba{E7`?lZMiN-mqBi*{HF&X4_m?Hk&emupcVbh2O1ppa2*+mMZd07fFph zMcS>4n{JN(lg=V9Vu164KLypAO>yOrOGtb+G0T|D-j%7BAv~{XO~+*tA7fec1c^oO z2s1m9oI=%5aVkM@jRlotd>6+YA{10tzjOtWr91g76XE`L9|t`@R72noUMU`3u^N@dnox{u?Zq!;vu(tPX6(fm${IYJGTX!Z)U0{4D+)il>RA*nYNC4Iv!K@^_~y+qwM8Roe8f z^3#Q`l&jFq0OLhz-Nx@ZSZgZeXhkRiqctQ=qR4c48B#(kF73a@O6G9AL8G!2>s8`v zYHIq;Muod=yY(Iw7(?rG6Rp=z)4kvTGFV6K+A4ubL$p$12q zWDuwfneoN1hE`JFHdic+?IZ0!jZPyI*%NE|91ktPOP!&1?K*8SwiR4e`pQ1{;-%=sma^gkqzw$e`aoM7DC0M}NK%&`; zC|W9;fW6~c!1n>$6AM2xkTwQQ;S5TfzAJfO@X0|yGf(XTSd<9cH9n1n!p}*|dzF<< zzzTdPKJe*gP2LEx2Lxr!kMMjJ;R6=aru`Zc7%%7#P~XDthI?6>HPV2YuMiA*Z!j$! z9@|c$fs^C|Ej-bas)KZ)!>NYrn8#l6@y}KthGn7h_lYaG`kWnCJI~}F^A~4&8}(-x z=)#&&zj(&)aEjWA;x$5%`fVE$F%~32+s?;?SHwBIJ{j4ExQd^@G#i5e)3nAf{}pp7 z`-c7t;#0kQ53wWm1$|PrsURtzY|Z9}rhaY}D8Jxo^@s~35@4C<>lb4`LbSqy)d^iV zZn;-an*6oNQWy8VL@u zA&4jiJOMunV?9MeLFaK-mTdHuvM?>-6b+S;F|h!ruE*=6Q(=@%4+5Bz?Km)p+Hd;J zUV;)aJZH)umvr*m>zN+MzFlJ^%8e2rU7%{Np;SlUFmt60#JAD657&cjjo)m*)!UUr zn~e^Lk4N4IB2MEF=v7=}DIfIV6@;g*GnmcKG_o`PguL3L56Gp#pZhcr^P{YJzN~3%1=PX?lA^~kGSH$m)6TkGrQukNsWOQEc z3QOo7);HW8iaZV{3w3}r4(`v*?l|nz3727rS5`8Avx7; zRXA@IGoSqfr3Q-s6IftpP+Fho2h_%`r=<2>P=cskquV_x1(8o>3ch9jD- zx9~21XZqM)l7(3XR`D-ThFx{WjyET07=UM#;Z_~TTpCd@%xQHM{8~~cPI#i%NB6}U zm_X3=LZb4rH{M{g{(LZ@d-yzJe3OYxx=@g8gO3@_adp&Rc3C0=Q$iODIzD0Ee45V6 z2q_2x?v-=%D3%fYy;;i(}h?C-;e;LD3zi7Tt88Br6M$7nsxK%Y5>=3Uh3*C~mk0$ANyn+kC zuLkG!b>rtCxqrqZGEnZTc?#hz=#H%Pq`eItjtaVoKN=GSgT2*Sfu>GlF zY$qM?kCw>wn%Rf0C@EapnYL=LtWhrN!QLgID(nvYKfIq}_&OuVh)7_{=+uSO99p$( z@IZ&X;Db)W;VjoMWW5H6=o$u&vis}ze+ZE#{nMN zNR~fOk?I`Jmh%pytJ~ zqsq2LX4P>Y6vCZT<gc*YHs5`23rr%K!Qp%hvla zr&K$#f$L?Rd!+Z#085CpYr0720gUl34MhV{f!cWw9(2G5AM2w4-laL!8PT zUc5=-wTz<-J6eD&PxUimUnKIFriu>lukgYmp@zsw;SRy9yaHnILH<(^Qlr7LT!6hAK8J^`uMAkAmoLT(|Rn~D;w9OSfJ^dI}5NXp`zJ_g<<1z_D ztpr^r0OIQyD(?sJdmCZc2_aCY%Crt{3w$1J+yv|y;;*90zkWNlQvB;(548h4Z}k8+ z&#>mznYWn873WW`Vg@=rEB%fSSGh0jkjL0@O!`AqRibGG7ne^Ly7bErBKyl@t4c_~9+wnQDI-B+<49uS zjBA=!<56c(Z8N$!GI)k~3wU8Ky-L3HmH3ueM*)ilO!Jhd3zjL357c;H^z+qneUHVP z4>j1noWhTQr_|4Ac}ha5zwOF|JwlJ{u+=SPddGr-?#U~*!yz{^cr*BvK;z66q4{WV zh1MW1mm{Fx;+`N*EGyep1>#=6LSD?=WuKEX8Jvtcv^GaF*v#jJP{3g0p+F_hfsC|a2JwKbcQV0wxy`3g`-3@l|wYHj>s9i&2l6YR<^ETQ1EJkSq#lfN-g}ZZ?EeD@!F8yp)Tw;_lsM~cer=~66n%k7baZngd_iKAMo`~ zhei~?lEC#sLS|F9z9^Xd{z`VWj+8b_%LwQ2D{PPAuS9X(T?+T1b?CVd6<@)eTkz)<=-x>PTT|u`EHJ<#HdWY8k#@LGB*CmYFRM1StQfi2+5Id%?P_6E!6Awczd~h z;n~{m<(YAriMSA54!!$k+mKI2;42?jUbpZ;ZB|Y8LMQSoJkAmh{5 z-NNCB|8S=h7qME8GOES?zhBDnvs&pYcA@9Xs}*JFi$ieVKR-1;L?V6a67a@2vu7dZ zZnSOYwd7XL@no~Drv=G1zg|#n)EtEio065Cch-bmNzWa$xRQ?o7kQ?E7*NSSVDe;} zEr0zO#c^ifmBldm37fmnrT&tl69~uqFUi~)@muDXxSo=#V0|d&z zCxdTTpCJ_6t9>ii5x?}^s|PoKsdRo=a*}c=7_SCgJBOulM9tqiLB~r0qDw`HH>9| ztBz~@dfrh-@a#=qSzHP9NQ2MN=DSa9h%b98YRG(Dz77JZ@%#w7go`-)Re$nif9Bi@ z4MODSc>P)-dCQ!BywC@sTFKsNnjv~_FXt}pth(|d==P?I<@16Yzzxa<>uR2#D)^-+ z>(Q6!U4~^;Vb)!zn0`eZsEoOKSkP8U7q!Wr-ya@lu%N_8$TswcKcEGLyR^W6;GR^o z1J;WE#s?JYGFw{o!SctXw36t;Gx-8%NyxjhC zv>(o*B3?eldvwP)q1~zlXX?m#nAy`TM1y?Sw!_}bUsZGSS>xS|%Ew2n=Z9RvWna*p#Yf<-Kt?HHmR(b}9>(~>U;P<5 zW0g9Z_}+sbMq)qZ7&~#&PyBtaFD55{p|e2l6FZ(Ofr?XD&JK9N_Mvn0BgbXeR4o-| zzJ}~w=7@PiR8!oCZ>ypI9WW9XUT0!9b@W;UUYMdl&a+N&m|}Z&TS)RZ6wh&)DQ<}f zr7K4+lS%2r=`X!15)Le#+S7)}?^gX#Mwx14e&B!*a$0{1 ze}98OR%iQVh+tohZjItKL{isOYEL=Dc{E1!IUG>`mL_fu4p$x>GeiT%0TM(R$?23K ztb1TI^-c&^zF@UDX60tmnrGEvuSDhO$q+>A4fEtB23RCuxzYf&WB7nDv9hG5Y-M@Q zrO+1jxnP~p(+qzEseM}k{4LCuTyS+;m+amsQa>h8tU#iSt8^HCb2#$ciO`_&8Ge>m zg)(JV#yW)CVJq)>l#TfxWKpXf9IRGD+03-h%1LgtDb~_yAv$kxKKqw)0*2c-B-AD@Mwj=_B(isHe z_uwTaV*#-f#8YsHPS=bM*)%wNVcXA$$USdQ+h`g)0byl<@ZjQkWh5Z0BYtCk_&ugX zoSv^Z0n{29m()NukF6F)9lEjrL~eHAxih^h5_hj5qb`GCYjx3comBBl^h24&P_k|_ z!z%Q=G;$@;5$U?`Sg-2VUmJk6lN&1V)u}kLJy!*0?1V zQQxRVgm(R9M=OC^|2!1Do(In+x-Gcfr_QUpb*N8^D}BE;j%PZ?}>yeYkh@S zKSa}&Xihrg^o(umBtX8#>9)flZjDh{sqja#J>FZqQ~6o4HUcsq3^v`}vbn=-U9(arI6tFHZEXFP)|0` zedhwHfA8$B?^KOwC=jsVbQFl!?hoxt7hnOE8Ap)70h`T1~jE+kuK1A-f zA2-y!)NLnj&Ym0g^ftY&VZOnE5m+?4!icsR^>%3vcE*4{%-Qk)8tEZ*1=IsYG9mQ8 zW2od_@A^g#2?_1@oBQ`J8HVdEO>Z~s1kD}ozj(cnEqGSHSJx)AN>S|;7Txrdac;#L zW=1ZPwW@2TWB>;WEoZ)hnFlD_%gb#>Y9X5snwjCly?{32N!y?RU(i!FWOU668Y4uE zw^3Wbx3y1Pjp`JX(F_X@?JMl&a^GTY@f8ZqHmH@oCo$Tt1MezD=tLj6xN4yrXIL0 zTzi%s$cMZ3v>XCr-I&&J3a~B_DHP}rO_5NPD=P~G(LyC^E&*DxDnCPovf`mY)w?PB z>1{&t0rH^Ve(yzY$>!DMxHoWFeLYO)!hE?!iwMDo+Y4pyDwj3yFl&C~V^Pv0Y>pVU zX4Etcn;?i9mCiYWFSDx5xA#rA3Q*gV)cXXx=&TZ4j~^y^$yk*xO?JO?6jm#p_JS)B zKvwMhO)lytj{>Py-DiXN2+^#b_fdnm$)O=rm%&&#Tw?V)gLUC0E&XZ*u*x47-yh)R zKEu?Z$y!4j*gF3Ojf;8I=dgv)MHU%as$B0wUk#jJscva|E?c&JXJ6!&|A)4o9$|uB zNpo)gT35%+c4dZS5cvr@o(B-HSH~pu$e<7u4IN_usaokrBk8LDwnk+7x+*dBr8d!g zrkJ3aG`Dl8s~YcK6i(F8F3c3<;Vhr+<^yD%j=C zUAB>jE9nA1;xm=&t*0ESr7SHZ9;ubXJbrmHn+rM-N?c9CMfbjj+R8&pou$AG5>)R# z_`4yA2VZ{!5pKqPLBmVSnKdq--rwOI6uq#4``(~>+rd9|)JFJ|NQ<*W9}Y+Vt3?-B z2$R0F7L$6Ob#oUkwFs(FSs?-F6s=$-#4^w4YY*U>=e^zcGSqVtkK$FmN zMcTC~e7LOWbdPwo_r(n^jK9CxRmBtEgI7v)y{#v+O8&Lu=y22)G8PHC`FybL68Mtq z!PjxhA9n?WU8Y;`)r;{MAP_uN?5G{K7AX=okBXH!&0O1bk)j*oqL#)J_pBRJHoP-j zU_1vpIR4DzCypIGR%%Io-1P6^_upaFghx#j639GKVfH5Yr`t9Q1+a1h5C4fugh)fV zeu^$F4k+_9HCw?QQN}V*C%~Lj8_g0-j@)v2AkkHMVOXjm`Xq{9U*S;5$IJ24HWSVY z-mOdU_eA_I_zkJ*+LUr83tm7~>h9&~%GDhfxpTJFZz*(Kgx9xbR_6lVe65SB_d?yzOAt4#|9Q6e7En4Z)vCOny z@M;J;hyhzRp0y{*l6rf?wudLb40dCggVWidnfhyv2W5bs3%TGhYi1QTc%s43S<^R6%}lj!qYW)zP-XSMd(5WjwNr>R{C zfr~n3bd(vJi{zp@Fkxy-vm)M%+8XtX56?J8a-USRQLX6KI<(Bob zzjUd;KMONdu{c7ZndhXqy0crDJLieJBu@)QknNI^m*Q3i?y@XBC74Z*ip6bES@pT^ ze(R1IN;ijALnUYiSsJCb=>;{JB(vho?4S!%ZvL`o2;sCu-keU``~kvHcCRbG%9KX_ zxQT%GOIP&MsXgf!9u%31&Mv%c9~K!xhWfGV`buFV@MBFbDDNniDT%&O*1A&>YwL+e z6meX<-8WJEYtw?aO2>ytv4@lz^O+p~_+})1UXf`Wx6xQD2fDAAOU6v)PKsK)O?#VV z9Ox_~xo0VcwkCzUA_GrWpejqUm86jpd~BJ|n(g}<)-_wdz4~2%VPT-{oTBn^ua#*xA8ud> zTTDC+Bc6McJZ+loxfkispV}u;E@Dd};_9!&P8>@DD2S#wuE~z<&tCCH1Jo0{o2dw3OG9pF1o?wK|JFkc0V{Au!!2swXuN}e057<98y0PfcE8lT7)i#irk!ol1 zt=}}a&lMwAmqH-5jyvvjPm{N z`0~pqjO@xiv2TRoP~da>BVdbb*6|}GMAc$um6-xNr?0_;#^&-Kj%1(2w;)|Hl#$J& zI?aGObe4x)zzKGJl5kbf0HBro-Rb=a4(W@Z6NFuEHBnWn<%-Q3Z@#Zf|9O&5T zi2F<~(QZs6j`TPl(ov`MYS(f?&J8q@FbvA7-Bba6X5_-=Qq5B4)I)gM5cdBK(dW}? z+wkGA`f8>L!;;pOWLmOiW~SvNQYZ^+ASh?FkH+djzJo~wnNR6lc6gW@pXHmHU=0|3 zc%CdGX4>O-p`0c5x$DSTtdqM~>?ge5D(ci8HHFclM3wWVSJ`0dFf@ zadIs93%Zsgn~Rt3J`{qG@zUrrXDvbk#fnMgucQ&it>N4KD7K$gyP#^5kTtm!X;xFp zY`O+CQM)uqq*(ruhn|0ryBIm~C zIN1O-7ICJwdU~Ixxz3Hg(IAx{x(&%sB{BuA^lu)TJ)KlN3I{thBn|>ci75F8T&APZ zLS;x2s)z@~%n_`sQY>ZUNkVW$fiocsC?ta_8Uh1UP~rxadJZtmxRl22!sx6HE2k7R z0^Vm zg_<*$!BbT1p)M1UoM%ISuhFupYq@Oc!}N9!XWf9kk^=PT-NF!t1S)Y=+FeF77jL1=T6) z%{{n{n=3%i(6+aVqXR0e!85$dqa@PoH6@*ajguf^U3a0oI_>!ueA?Z%!D}R%8B*f16kX_1t0x@iq6w zVeVB^?hVfAZ9!ezf@OlaUaY#(YS+)b{T6k?VGKb&kE%Q0u5pO-9ct(j2r%2CyU}KK zl@qq3Cd8h+wo-2>*sm(#>P`XQctBE}y{qXAXE3$1yf1mfubtt{qr8k8$zZ^#$aM0Kle*ccR}99{ikA%<);W%Wu8f}{ z8Z2G&;`!R_4`GC>7C%P%mCVkX(FmhL^qkbuE9#Jx7Y%x3?xGi5c5+L?t??pz%{OP}Yke8Y|U8=scI{ft&d(|0M>EGJL?4!|B)J2RB zY{TuJ^*fsg6bmFy()~vvkiwE@Y3@wf$Q6pev5l8?`V&4S3#`4X;585gP@cvZd^7$B zh=TA^8oxG*CTQSywN(WE$)*fq05X*&Tk&~Hd4lvVRdDP6XrS{Q>D4OWhca81K~X*&fxtqchU=5z+RR9Rc@qsbqM zapB%e8Xrm~_z5R$LqCJ0Hn|)*l^^8t#~mvXQb%SXUq59<>4*=xU{X)KeAg-2S>TYK zL2eKaCM-ex+VEHc4+Q0e6RmGXdb)4*ARcPsjYT>zLON!$*Q6?~%Q9+*-R34-sW)2= z{5**m>$hqwfg_H9EyK)>+m{aSRv6j3dHU729EIKfB)lQLFuS0QxDQ zggRz^;&92??DKe*9h$`JengFklTT;~Nt_E9G0i#yM9TiNkt(SoUg~y@*8yAQ1OSj2 z&Ry9bJ?!YtyK87U1o$uGAOlL72U;k(M3H-JX?L;!$TD^ln}y-yr6dN3i@SOjN5yD% z0Jw-U>^QQMAn8r}l4Iq)M1kbPL-PRql_36J****`scmZZj}O3gpTPL(8D{c1ha|k5 zskDNSmyqr+=1rfE6tlD%!CX5lZbVpuzQxx4Zc5MmwYsba+9)`G%;LE)q5-z)k4JjLGW zX}96F<{_>S-j6C=h(SPqt{qQLHR{EAfsK3epslR)Gf2i z>bxw+u$V0muy8`ApHXg;!9UkfGFt3Kg$yIGWWDVw)ThLxQggHgdDY|s46eA8Hwe~t zXrtfXLaJrp5$TE{116_cN9zyF*-rZ z6OzkFI1cACqk}rvr&!HS=Xkl)mG$jzgy8C)0c|{*1WN)7CbWM&iNOprdvAhCdudo$ zIeOEf3iIMwi+v6GFDAx5^Q`VZXen$k{RS~bVf1<8_@%`ra+Embb6gKW{)F=3?ncJ# zeBlR_Q0-8PNh?59B(K2tt%@HWsvpA0@s|1Unt~b#JNF-nxYoS}msyZ32Yzl~`%lO%v3@qN7@?CWnSBe(BnReHI5MUXCd*F65fUAaQZBg&>eVKpFf zcDv6Uf8#_Ej=V13DlxkPVv>% z|JUJ|Wrgx)?unD`s7LFd`M8a7IUw}kHGhqWSL0x=Va=JbZ6S#m0$2sgaVL2QJ6s=` zaJsv`l2APh8onkZEQd{JViZSkcHWK9vs0;7A}Ve zdu<~zZdS?6sX_>xg#(mol8vJE8{TKDku@Mb#M^$A0(TV^i4g3q0`;`_e9||`S+1hW zVV&lsf%q5Zq&s_ID1PI8klFI)PbVhHrBY*~SfaEg*|II=&y zCtgT{p9_M@IyV)rx8+K2-ktxqu8fh}VSvoS4r>mM*h;=M+UxaJ5+8PXyF+gFrbn(( z%b6Y?#F3KLsuSEKf{O6?gX6Qqn~vZ=VCl;vDES0&l2S1ZVqhbY>=Y(s{3r3 zZnH*pbl~Cz^<-1MF0aCcn)dO6T!8U6VnZ?0!UQvvQTrLWwh4?u&b2R51*i}e4GjAFxBPJ};I9XAHFHGI*~ zi}_n3pIj2gGXgV8Q^OFewtD zG>H+J_M6{Jj|?`#OIy!r9n}a&M)G!u&YA1g-2)~bk({$%aQaWNxt7{ zYEv04M9dzCLu6T{Xh-;oEAYR%aMo3&l~Enm_#-_|r4&s8&pA~>yf{Z!FcCRU3|#rT z<;B>(o$uf*e&Jn9Z4|6VIdf&5h(>Zn&lx3@#`rqt@mJFvqM@U7N$Mu{{e}h3>5E;` zh}o3P43=6pRD?4@fGKFSq@t^_XVjIn@HGD=O0c+UQ9&8AU9*xsC=I&kFuk^%NS}ORiXx_f@(GlM+f>aA)(Se{)r~;! z+{KN02>-c{qn4JfqkM_muAx+Y>Cgk0l0`ql?mC~xmH3>KdHd8O@m~^iouIocD%y8< zZP_qxD?BK8haXBf;d?sl1D49}AHl>tc+e~+*vRZuq6wZ8FtLYW@Er5i_gT~=Jbo+_ zGWzafhxK6Ui_v&Qtv7Z%^8>uuVsdptg^Ht0G^BsKu#*`p!zz@+*%QDbm`OF|2tuM# z0Xti-+z9iw2aF8rHIE}M58+{3J}&w2ctjRYh@WVghFcdMdI{m8=m~|En_vl{ijM{h znv-Uh#UlZ4zWr!2z~fm7wBCojs=7l3{O2SS2BmESurtvBE>Z^xSA+%{M-jtHa6EFX zH|f;_-_B42Rxp8MrZxJ|lF+oj^7Ki2n96`9PU#^%dcEgXKFha$Q|Vnux#^yQvW71@ zKt9r}M64bB7qy@5l?Z^{<;@)SwLA>;osq}hWj93wzlGVA>i$_DY@p-r{yB;U74wdC zl~YFcc*0vQ1}T?nY*s+LbAp{wUqOEqDuB8+)^c18)rGp$jiAi|8NMfG3;zA7aa|Za znY%nxze)@H1dyQU?M_V)rt-o{Ur(O8(RcrT*HP820WNzdaf9h%b~cdS3n*sm`J6$g z51O@}TRj45r{m2|xKPR&;{06S-+U*B2RB+5B|O5iJ~-B#9ri{?Jpd|gl*ndPI4Frr z-(=H#S>k*zKP2-4`+$%T!j)o_g|ia$J&J(^$>X@5UmL}Fo|{+a*3S)TD=;xvan63~ z7B9aSyh3Z({YdEpeco74j&f&-PSLi#OZWy!M)}qwL*D39+=GH?3BC^MZKLAbo2|IVQKgb+4q8KyqM(wkW>*~|bgO7wfu{_WZHC7S9NTag zL$`dy=aSKkXpM+s2|{rLN^@uP8Kw<$Za%lQDSTnEgxoFfR8cO-NCAUI9@m=bf5cK~ z_G1_t;G*j}VC`9)lXXbsjT|7i?Qw>ONk92}-L9l)jZsymEvx&yuas2!+{v|$e!@OU zf@Hw7fn*PgYq#c=I2cAj+Am!H=sjs9Dh87^rQ8|)s0tvmV7}K{J?{XPw`NP-r<=R& z9p>+3eTI%cGvd3yW_ZozZvw+wVT#kjX{9*=9R$#u)&WiXAL9-wfqUtvFbm9b;=nYL zp>3lQ@J;CIkQLQPwHCGK3LL0Oa72~2&`UWkg+}>dq73-0nf%=`HBKCxN^&) zM3Xc#DTA}b!t;}Q=~>R#QqK$WkU0R+z^8gZ4T3BZZ?q5Z>;y^q&Ch2j04uVb>l!gTuLl}BRji5f01DFR zyL;C7CViZJJ?eK0=`mdK_XCRcx8r20@zFq67|q_iF2dHsCKku4Cn!M`H)+r-oF=iX z1<}s1O=5xCVm$R>(kFovr_$gL#3m?zyLnEm#n9Im(<9S*06s z3O)DxneLBtWXEuZ4=Oun>Sf;OOE?OkZ=Fg;1YHXvBicX(JF!-XH(?aHswpaBnCS@H zUpE-wI^S&kW?oXfb9P3>BmxpfIexe*!i!M>vn{o*! zvj3u7yF~w`UNYi1#u8$Ng=og>uA3+4vnJ(Q^F`{88DE4cItAC|`r8fkb&IO*LPwJ* zDJ^gK-D7@zq-LIstXwi7)3$uFqc)pK94~`q5cMv?P`?i=Lm`MycmfX2GDJx-ER$ra z;<4>WZIfv~_+3Blw!Vk*Q@?pzfKGvH$JZP9*F&DUJk7rjsA+aXv3#YhEZe8bN;yqD z!gFpsHzy!C}llh z)*!52*b{D?;bc+6=;JDIg{7wmzUM^+1}50sbatbN3?cOqkJUc1=qXNvC2(#ex1rl4 zn!96|(yP$CV^rR()_Y&Q9WX#bne1Q;#VlC;Gq?gq_f_voyqNm3T!oWzpu5|QPzT4ejivml8(QSboVjGPVB}zr*Dq#nekNROZYoPda36bmh{QMP zm>&W+2*80?8xpBrasmxw?9FE>T8wCI%bBP-`-&*n+5(<-+7om}S&-7T<30EsFcy!9 z$U3hoz{X{l$Iaf>$8P^!zDsdqbV_f+h9)B?1Oj;X6*A4((){L091z}R-8{d;NowH( z+ZaN{#M1v#=&njLVKXMq-T$u`YANWBQe5w~a65B5C zi%A20BZYJaOITWo+RQH-7&#_NmYnR@@mwXzGrR#rp6GRPNgO5DK*06uim{QURreo< zzf@kqL;cA+lf4I2Qv|RgcPq++pNro-eLCJJtc(v1r+N#YbTAD~>ccltu%Dx93Ts?! z+mZ$thmtAbmN0t-jBs`d&~Fg2f9+ZyYXj%lag3hNoH@DIl<|f0=#hwbD|1`z7wKZv zhDd3&D52i!sRV|Bop<9F`drg~Ib!EE;cjCfKz7+#Wm>|vL(DM{$ql1W2qSa_c22!} zqq&H8ml*6E(Kkytcb51)f@D2~#x{rYAL-LWgcW*r2gzTshC4z!#Cva~xP1TU^GIF9 zY$?mW#Sr=jZDl_b!GPuRMFhsVU1_0QScRB3PBA%Lq`j+1kY!epLLU#3}^NsL>CKhculEEnKu7wWl_9tS9qX) zjWs#P)H zC;#o8F<{m)XgXriJm4zg{2%+{x@7j|JJxMB1J3Vc()WT?l{&n)S1SKXxKbDjPESdz z!sGxd{cH59h}` zb9nOsz5xgVx{0>@eYFOQ87iW;P5gU1xl*;aGw{Bl+=MGDD7M+P;T?Lk5sKrV%B_

GM!V2)%O8=w$AN;a!h2QVi%&nX`VS3vmDMhD!g>3vHZOpFcd;qTneNm zCCLpFHSy$4eK9IZbl_|Z4jyB`ZE!cmi+g`^y{~TOkqUIjXjolAkpw58#f!ZlVPf`p z=$+L>9H93mXyu@|NojRp@unxg@4sAb$d+3t z{6EG;a~vW_E4J}b@51vJ!CjzGLbZ9QcJ1ZX^WL;6{qoy>;22n&2_F+FS`EOMNg$V$ z(;7rS1J@EESQyNSRM|_M8lF8KR67g$#?yw~SNjs`!B2L%vjVIfhxf?wePYTEu^{m{ zG-JX7M%$oxygDl@B6Ka$Tm$pmxmKz~qi(Et&eB;k#>*01qJKs_eimBVA0iUe0&SS2 zj^3d`3^H7T`%O6LD2*wdfZUmr3xcO@b$8L$eX%pCsTuoA8reANubX+uZu#v>uD0#h z3gw(tc2A1AqJ1ShtH}CW$mXACW z%_fKRydlcpmWj-szSzoSmT36hs^DX}TVnNtKOG+az6AD$i0CE$n!Q$@S`bsq|6arV z?#9MY_2}DYU>IYc37b8i;PECj zJ$AKMzqmes{!@CSCO1+Rmkf=B(20L7remXbZxY7)+VKtPfX`gXy*!BZ$W}xY)mX?80X01$zOD-SuR&8(o(9~Xlaf=ht!@c}5{kTc7FZnMf=e7KK@@Z{gd z>j_(kU>S7YXW*I4SI=c7QKsq`nI_*Amp!Xx0yRMc&N^Dor%n(7Q10}<$_H+b2B<4e zI6WMYc5BF>IqfrE=g+wYW?(s~u^ie#!WZfbD$32 zetZz52dUYnu)JG7PwOs$^YF6R8~wV17I|ccGkU$7Fp!HAI*gqWEr|k;BhC{)6g#lK)9mO z4PL&8%$0SB(sfi~=55XX47M*BQ?T3#aQ3;nRhK} zloEAZI#BBZD%W$@weHkE5Qytopwg<)*M}Of89?Qdj*{Du2Yn!zX|}L0?3}9R(}l!q z`F1EX&~<_d968gLW5izv;vO3H+ep1+-lF?`OqB$t2kq%#EQ#{Rng}JAE{?bH|Eqdk z5?MxA4Y)r>c#NbFvV^r}k5c##Hu?UFRf4k5sBpwRzn#kOymokF->YG3z=7Bwjo|e` zzRgDWKKD-v7fFDH@2`s8n}UU&c02)W>PO|4e_Z@!o5Zo- zc)Vn%g6Axf3$~t=`wAu!Z$59FL*yqNOg-RF7ZggQlNvmSk#-ekf2y@G{lZ-Gr_T zKeD_Il@EXZumxS{MnCP(X%*_Bn+~-1In8tx)C`FR5}}b#5M6=ir*3%BlrVh7h0)Zj z8Q956Tau&Z5IdcGzwL+Jk#a}|M)Td@*iq^?O6ODdIB0kIh~IOUD-Bp=$4lBOH2u2gl+3UXd%EMmv(CpLp=%$(?^=O-DOzflMHVi6!{^ZSfp+_9)wPg9EE0t!R+uJL{tV{S7@C_w|?qeFcuAyU#=z{OqVfwOg-9eDr5duz_ zXYwyYhtb`a)%CfX#G0A_50A&@`2|T=MLrlJjZc5cRm$bEuO~% z>KU3GkTRpRms8Z~w@G!=9gkm09vBGoe&ONj!x2HAhQ1jEJ(U=^{kQ8$3eBW`>)mU? zr<%qkO{Gx5!Qmk#O0?`?=4jXv)By8u0--M?ALNEd>dE3h=u3>znHxI;?Ymgu_2X|u zq3`^|N?Ualx#;m{a9mZRo5wkTse_u?V7cu-i421bpg=;D#9((^t=HQEd4 znMkqiN~6}rGV)GtjflU}2UXnY_k?;U0qqpMz-&}a8TfmW@t7!rWX&S^J*)!W8u)Tf zEBoJA@uyj$^#WfF^_`%sNyx)FTRC`kLY%FBX-0O{*`?m9dmPzfCW+LK%|v4X+`A7G z%HlUhJ40QBuUr^+tnKLiz$wy-&6LSZLd!7bpfZxS~5#CMcPOh0V+>rpPi{TnY| zqy3!D=@7>HHTkh9J-PNRvKzq4XQw?rQv!>8J2E*W2Se$8qn&1g&p;PR5Qy5J50XRs zVf6O*&Ruoi0He7|4!iy~BP6CrbhLvj{jY zrcN(oySav(&+bOtlM|xSc_MV=xMC(oS?9wj*TpmEc~Ut%_MQGI(qSUT>@z#9%6bHO zoKGIsvd4{9MqOp9v-4EG!p55FXv~bA=HmOn{E3wWRJ{>zRNc5;S*Cg0gsQh7TvJ21 zEt^&x=Spp6aSa#dd<*~Sc=22q5SPj6Mj+=20ym#_9wl#!6n)1Z?KIWkXY2E{a1n;D zel>t4`j?(83RB`o&vkcRirdW!(vNc@ zC0erMV7M3Wu8R}C^spMnbQtIL2JEsIYj0ckN`2P6(JcRa)d2GX&~9#$%pCNvhQfn} z5x_dGliEf=a#(i3irguMC{ZVe^=$j(Butex5&0>eL%QCMwMvx5Fe_a101R82!6jy< zmu|9B3{F&xy{v!Vo95-=fR}D6mB575&3+jUa9=cp%8gW9!RoL4B3UwBB@LIfP*E`4Ewtc%f!ugv8S`Y zc&};)uy+I&JyG}~v8pijTs4N_hp!=Z-n-hngzyV52kSmrf&Y_Hx)uBDn4Mm)kA}$e z)<%&Vb^B8M+@Pamzj(|HG+o$y2#om|y=dcR%CyvI*K*Iqj=MZjtr?7 zp67OVV8b|Ic0uQH@9QQCy=2!eq@X_BsM0Wt$XFh>b1d(3hWar5jODd7+AtU1#W_up z7N0+f^(HJQZ=l*1ZGYAnQc}|19@fN(W-O^3v>wE+MzTcTRUnRMGXXqW&9g+#?S{NH z2LGPkREo{UNSH9%YT3Q9j(pQ&F?b3l{m>2OJymVlJgxKtLn7j%zKa&*ywgTOh>34I zw^bJCpB2Y5lLDjmlgnuVc|tVpO8LKU+F#muXI1suO?}EpS|)x>tW%8IX%#wo`=`gY z=G-Pbu8jIS&_tnZC%M{3btYN0a3fjW3zq=qQNa|0#H$U@(Xjnq!AZF69b(hCHpWJu>KOD+zb3orihAa~M*cN>uU zwU}`!`CLb&AD<1aLVuBUQZF@9DjAhOAsL$t2Dk6Y{CWS>zTv~H-|vM`rfbHIBK+3D z1`z12HU6J_go}j>2+Av(Sj9cWS;ObjcrDmL$K|*BZx({R;S_}pN8k$tEMDoMqXKyD zL>qZun2{?8nweQA3PAOkS(eAthWNc;$#HI6DF6D>kDQg^3`+*dadS0;NNb7ibX&R9 z57daS**pu(TTYNALbWX*MlAI*Tr_5zlaVMo;PNOyZ-d^Lho216Kr|m7?){M|>W4Q( z@Z#GhS?B8rf^&g)wKIo!jX3Bi9lNtzjxk(o*;7*r0ggX!(&hyllY1@k!_?<_;l}VE z^qz~J#Nl6YODr^Ue=h&?BD*#cF|s#+|IUqJB7>KJNGv#mX*c&ZRML}O-Ck9w&TGVV zoH4!2Ap3~OTHw^K} zA%_X-XOI4*b$ky>xu9cNR|j56S*QD2u-{fUk#*ESK#~xkKCTGeh=O=7;Bxw%lqAl; z6}pOdYH1t$JOn5GPaVnJn@#sEO0pOg8pcfdUl(Fu6yY+ccg*9d37z3eRpzhDq4X4p zo&!-zfl(!V?}VpRjiZR;djT^Lm)N-a(|Eg{E4@GwzlVzU^M&z@x~s1C-Ad)Z^i3wB z`$xJGg8!{jlyxm&6}w@JKk|3}TU4}V$jXxtqkGjXx|lv^wRSoC6Bo+h|I5b8%x)=z zJQ=TmkE+^1Qt76Q`$^+z%o99Emdprg5Idl=Y0uc@-&aNZdp2?+(;-W|P#ni!p?lh! z#5>Ne#*K@6<_w#V!W0kswoJrlzd`Z;Sc4s=msT+<^WyS1!GC`MqRLYAf$8ipjzhxFY$%<>@s=ALw-085vg;Q}yyp!%IlX1KEHmr;u}< zgs&WomusxxhDbY!M36oTf5qoZj35t~3tWRk#~(Hov#YLV*-{v?L)i}+Ifjn02mI zgm?8h@4(@oCt*@f8kd4lthD{%JRFJAS9Ti*+rt1@a7MAYz5fog8U+)cx5?g9ifCcF z$l4q@-%J*JAOt6-OHhDib?)CJvM}tD?+?9~Y_u0!4AnyeJd*vlU8Pid$9zg4Q$ftB zJRbQz6;CNcU~6B{pz3!c{dyhfA1^`VHmTC)Qs}LCL>x9iET$5Q(eC&3H2ei~>)rCk z;_+DR=Byb9>uFMEC@u^bda(}dbGFSsO&!j#^0%Q`d9Rv8d$bD4XF2# z;xP%U$s*~mzXW zctDg`>G>jh`8Jh)u;v)^`gOwjZ*GACtZ?{M4_c6`e%hKZJI6fZKm}!%l}QQ=4+6-5 zWzO-AUjuRSchb>@&FuYq>;PWm3^SWwTRuen8oT0G^Sm!PbS$Pny{a^ff8W4lbbXGj zjN`&rJJQk~Rb51W4INoz~P=f>~bQ zJ*6jQ+>V4d#MPSA(%^;@=K5eS<0UvDLmxCToP8g<_JIZ>`^$430QoE}jMIS(P7CZW9ues8@}YwR(&k5t8wOId+_TU% zTyP?iyl(wku!RF#6*9OSKUw$}?IN248t7=(548?6L5;?c&{4Q0PQ%h(&iV)H4zZtE zP0iN{loca#HgJe|WPvE{qjeQIzmrusm=3WqL`KE=+!1r;Wilm&LHwCKJ_ib6HidFk zK$bhE+?MdzH2?yr+1~g@mDSc(^7XZu(VsL=okSb0MMDKKHP2nk-N5aiDQjn_TGi?>c(=Km9N5R7dt@x@jVHw}>V3G?XMRd>794sL|41^vvDL zn^3f@!P4gM?9P#$TY{JNs^jEXBH06jZdipVLeI+(km13HN%UV6#aOI~%#0BL-qKjT zSCtI3L60#5U4bi-M#Uh;R02pM6*?t(WP@RIwBOq(%=aEpyd~mZz<-)Va6TpmqL5HJ zoQ0LQ2n$a#+}B2=ykF-!X1fxH2+q|63GiVBd$QOOKI>v0-vr}PLo!e4Nf+_bNFg1I zT(c}f^o;VXB%+bMF!?r>3!`67Hc3ih?>F%fi<9a%ljef5sl4g6d)D&)z6(>XYHCzo zD7ViB@JJUdeS?gw?3*X{P>P1e1}P_OlPV&NdPQN&U+>$815+y2+ab;=9YEXZJhp{zXpEAE zj6u0n4k-agNweS9guKPrn1g26UvL>l?xlb+j5puRZ4w#eK)Z(`gd^X$$VFl$-G(?M zXN4A=ByhIDOW72?oXb^^=6`6>O)O5@=RJ}^4Nc=5UgtwQGd3;zs>wzwF>`@6{+lT(@DaO)*_KVjcItz{=*j~IjS40jb^{#6o?09jxr^L(c z18dltKhH>bgLA);!}sR=)3$PaMDVg)s&7)nLzH~FwfC5Iypc`73=cQwF#+GHuu1oN zsekpm+UOjQgOFViTVv_3fkwl?QVMP{fqS2(#PDNrupvqg#XBn3MAs%Cq!Q-&;I+%N z&aa17Gs#Emk5k*=;RzLzu9h`>SEAD8yA<=Enujp6_^=24T7SGpk)#%JGCu-P3`yM&fH1oxlbC1^4tR% zOv%#2OA#R083;h6V6P}ZEaW*o+h6_jYQX?UH&V;jWD>W?R~GY#v#eP@i2Z1LP6#{_fPG(wacyj>5qF6D8elV|NvnM~LRuar+k3Ej>v&@VfJuy?0Io__;j= z#bmQ z9Z)UCkMb*4`8`}#Z(=mTE(L& zx*Ogvc;el)8SNnXcpY(tegvWRX_+5@I>;r>t8_D2=02gm-DzF*WM_D!DJ1Td6IWsm zg%8COlJJ<O{KlGmYNBHdM;qzExPD7~H1t*KOz>D(_nIr4KMUvncJE<}t-d1W52 z#O!igm7d*dgF}j{f(9MYfTQas`F3Kf>(@f@5Eoj0#h6HI`H!F65(~`pumF*hi)uKC zmkE6`$S~R;!DdjT{RZwKRZ38%h&1g>%FP}P3K3XWxBs!(zl!O=dOjSRZf*f1E}xsaP&#A2Divn)sl%F2 zWwx*O;0W57>qiazfl@N<%6pnQ!4iyrq}@n;7~m$Dwk~z>Tb}NxT%`VWC|@>VUF#N& z?)?#*-ib#YcmwHnYJh}(w#_akIsDuSevxUrc!_IqMxpVY>7A%o)CmM?sq==D!WNd0 z?i^YKO-_{~o(_7Sw;pqu3-C#7)y6CYii2|-#}C5<^m67Y?V6!|EIUQxqIqAXjzmaQ0CXIM5wWL8NvxwKkT|(75a9b z_wj9ZQw*-bA3dw7>bIS<5(R?QuV^oBC<>vgd~bfAB&{wP3jJX@i( z*)C+aCI_cwy^GM6QH(`GF3Tl7Sbz~5`2brK4EDjNu(6YZ7vM~p5lQ!2rL{=}A6Pn` z@c?9FiulTZq$vn@ZG6#Vahn%ohAGw3m$dpNPXcvAk_n}P{ki7L|>WgSuyB3to)w5QlFzLTH1aI&1g%o`(EcBW881Z6FG% zo*kvCM}Y&~5a2oIK`ebbF5En8dy?aclNSMwi=9}6Xd{liBtj~=ipZpzMAeDYUxVTx z<3Naj%GoOx);sY6lGPR+?HqXu% zdpB>r|4P^)np}>qIduJ4F=f~Q&C;?ZGbQxRvhc{c`wA4z;toK5qT{#$41y?~3#~47 zC4ow6=7**yL_x+@?5eDeegHK1YR zB0oZ?8x{<*mPHq=#~)QFqxzAu!?qzNi)NxW@lUnp%H=a;K~uD#ooc1uvMVpj`I?sF zk?14nMKu^F=uncJx}hkeV5aC?R0&7yBM z6EMueu^RJh^Rm-m^STBnVqXqi%v1Qgbr!HD7HhVehv`_mv1|=q3E~m_R(8S1?e4o0 zhEUqc3^_ipx>v}J9Mx;hIX`g;{pZN=5~s1iRM}8^mf$=73;@PNgDBLpx^r~&OQ7!O zqj5{grd<<*i|k%Vl*gB6P(+X8=&&?>3(3KB0N>7v+_?z zF~W5V0`h9cp4O&(>@T5gvf4-R|Fb9cnLGI0m|+L$a#$o2{GZoZP&Bf*I)n;D-$2X=Hc*!pS zHUUNcNl(+;G0i4+b9@kVYVt##Q7nH$M@**B#sU!m=>#_*ZPoJloF@W5HN#nTtk%xN ztF(+lh%Pvn#3eYUy5t6=2l`TBAX0{brsb+*?GdGQNKKAYu5yj)E|+?J-pLl&&sUvb z4%*TiQ)#J{Jk9s!#A!;!F>_d>*fi>EIB-l~(?uYs)sd2^gUQq9Tu1WhCMA2}^*B(J z>=HUl6&k8tUdf^1mf$q;6j#Db@0l0VPgJ!am7|fGyi1QS`km`HD>klyQPp%@NhY`< z+)KR*oeD8~L=(4T)*8f2#1E4BFHtt~K5Cev_3WVMlAG1yZb&6mx^Cx0LQznI#`&1npDJ$7zK%Ic?>LdBRH*anR!)PO(J1YvT!s))A^ZHk_dHf=);FMaPLE}p zkN>5-#f1B>jh>;HvAk2*$^mxTykS&(xI%!!9 z11bXLVL)&`*u6R3PK*4DTa0rZ1B>+(h?Y?r{9pY;KdSbn?8TOzDA?;*{4e5v@0IPlXmHLe zARUhqA$0gvde}wrRy{8`3o^vV7hZ1Dn(0ONruVvj)Y)mwDkK7yz#LW--WQY$_?j>e zXZYLfZts-rA3Fq3<;5(=DUF=qzApTY=}`pzRU|%or0DpBQc9nB>!p%I_%H?Oi@CkC zr+JsGXwDle`-{T=NP?8O&^qM&42iwaD@048-@S}C0g;Yr^-Tj&MlbPbT)zO@1MZM?)s(oDaw=H z?5-JCnml(x-vYBF44!T~Bu_x* z&W$5D!)_h;%`%fzI(EO__Qm^|hhR&;JfSu=$fZfTcnqvXnu%Tv_jE%PbSljGw03Ja zv~dvpHiE{IFjJPwQ6qKu%f7w+CK>0f+oRI}oE{6D-j=+1?P`OwJ@=HadGTzDil*b~ z>q&~fBL8_C3aREl^}_3l^H!2Nn)@axyyZ*s2H6VDBoj!eVP(bDOeSbkHi6gANax@- z+ zl1@8MY$WI_XgP5(KOhBJiNc+rQEd9OWX$f@QhELvPn95O(;1X{IIqmy=oqR#Bo`j1 z4InC>z7j6|#$z(^)dzt!cvPpKf)KoCI~a>purU+f4eLdC9u!nYE#*+~M6M~fw+2HM zu)$9%z@S~#%U`qh-!4{85&~+)A%GrlBBZ4MfeZT*>K*ANq<&w`uO8NW^F9PjkrZy1 z2S?~0s+O?gCH8lgtI@5mvGx)q+$(L|<*KJF4>Wk}BRau;BdWc{V`D6sJ_ zP`1T8C1+`WQl#wV%YvVYBx4QTU1@!KefeBnU9f9Z8N7-pSYtpL&IVDxuCHaim=}X& z9b5i8XW#z-K{Th)?`EZ*|fjV}j=PuMBqZMX^Bx|3|1vfap`ArM3VaSVnq)zP;Iz9G&ODJ7!x2jE=ryidR7G}^;EGL_j!5VPCyNC_{M zXC~A-oq441bFh5)B;AAT25bZD1O|vT>LyJq4HYF@t^9TkqA_*7Vyid%Qu7wE-n&aP zp*9Ak*p0atk(sgjHavSgP<(uQr>Im5A;M?5`-0WmC#rZM5);mYKAFg?1H$a4(eMnI zgHpOnLOj-uqXnuAw%XQR?3h-8#JtZFE(%1jAyd^PWgP|C^rjvJW>^=jf{giL(y9Xa zbC6(m#}O@k3+~&au|FX7VuyQJSfmBI@E)T(xX(H^q3?d8$4R~`<09wbP|M0oQZn36 zr)&UFs7>I`YhoPSjauf(YYarjjB}9322|owSWB-JclZxB^B0!w7#HNnQxKZ~h43!r zyjZx0;kyKxerdh-uUTg*PF_SUt9xe5&sPHe7$7QrS@fl4wyj(Oi56=@g$0GfcEzP8 zE$bmVhbv*6dKC&;UIf4v=??y0&wC@j#@lz@C|sfm{1dHCH?l}^BSb(4nQHLJ_}B9z zk*mgD`t#zj*+SWVuE_}Xm5kswE$h)&NLDD&5U80f;v~VUC7&rQGn?$ykFB@^r^5+J zZWY~%p#c`2)tHN42$340ROl(uMW)7_Bku?I<|y;_#%9fi+M6+EmRASi!GRb-Y8Z5; zSBG%g*Y{$=`416SaF=jiFO3wO^1Ueh!hF#iLvp;vKlnnFITb33jiy|(UBvzE7yys+fY<}_D9Tl_x- zBpFss5zhupN4)b@O7SG?^?!?1L-!fW;9gRm$Y z%%ZSTSY2f)Y3_YMLLs0=-?Vs0)3@xR!g}}+;1<$^Vu*{P6D{&^J^cP`#zRB?jHRdF z%ephxjr)0T*pSRT7tsL%pePi;$Gx}mk6dwTAzg_XnXbG*2hwxw5p*6javWb1Yn}7!V4PtBsf5FZTT^ zo_6yglyQ5@G^mwfj{d3%XIXUJrtV&wkq|3WTou3@D%S!=c&$gMdgFG*=eA}B^35bu zrf4sX$H&WQ;r>vph9G?OQir*L2cisz@xzfbWsK&B2E!71S<)y3Q+}Et#1ki{n()rj zm>`Dt;nb59dg&Ru$U3sX+ceCoV6B9a5u`&eb1X{-!n@&{lpTIpC?)0<{aGjOoD=cAAL5v6QNP{efW09N)snT2H0VCT%IA9VAl1;;%+&9 zE&;!!wPGLy#F%=Jc*8)q+245mE;vn0#2COFCdcy5zN4j-j@IxqjKH8TIaJ= zgSEj;j|`6AWqcwNH#KFviDz(u69CXT%D`SSI5c&ht(DSC=CxI&uQt2bDZ&+L^cN%L z#xhF4K-9Wg1wE~LFcOlmTlMW$pWyspFx$Q^MGjyc{-*wX!-k+c;9v+*D>!pFcG?0uXr^xnq#+{v{u2bhP0wD5?kQgU ziR$uL13-iY5@eI=85QR%=Vv-Y*}c_!3TtTV46DVll)z!0ayLftGQg761R&L3hZtJR zEx;PC%VaGr^5&KXW>GjJ07}j)Kha%!b(Tj7pmG(WlnN`_aiw!%Qqla;*=+Jv@61z- zkMx0Hv|rkQ*VzL4>a#zDe5Z>?w2NFnK@Ln?vKc}aC+d0ETD^L$ojl^#SVx7)uyX?~ z^XmlOH34AT{?;>PmQbP_c=PLFynmp3dPp*NPxQ-$3kcVCOB%S3GUtnQXVB=!FgDb- z?j81}OvD&`8NUuT2cwKz9}uhKS0B@vM*Zk>w9-<<1J3;`8%rxt8XsWSa=7Ougv4f! z9+8kXpvJyk_0M0o97Ek50A6nY_GcTjlJ$fbS@7M7j_R}`_Y&_USX2vVKn+cY8=mev zD1Ng&!(Ze$w0{2lWJ26ccb}ISP^|?5=jw9Br2%OAKC~&Tf1upj?5G=9!bWM(tPJQO z?#Zi_ob48H9rjoM?obbi2V9qOR#<|kYgn2s(mF@po?dS(t-RFBQ+S^ZEgswsOW?_<5tH$=kE)&ij``Tr5q2o0 zWYBZhF6z>3Jbijrs~L9tcyA7=I0Z7X*BsDCTfa@mVPvrn{^!-iaoRMcfal-|zyLRO zljmARYEM8;xgvV{gWse*Ga`LM2}!{BW?y4yHE(!Hm!~4(yAEv#?S~#l)%cJIw#1}0Dn(%lxQCg0-%ZR5iPxY@#X}(*ZIey3kKrS6!Q>@8bp|kvkKsEnmsD? zEr3tk6gc0lSi=b4QOIF0&&G$^w2~OdcpsxQ@P7hh@x zD#bQz(>)8ddJe^HmF>UP@^pT@C$7l8iE*4HJXceW`IrSByVg0PVw-rgL6QPXqlk-6 zt4%VeEg$f!4&M)gnvu}VVTT*3Sv2Ub*oRBagLy5cfDtN2;95pmXq$%t)`_2>p??#= z=?F?T2?z657My-@jmTv#`R~5}x&@s5c6T496%zO5w+Z7FjvsR&GHIhXo#|zREw!V# zF*x6eNGty`K~%*R$8@jD6sllIp*PEqt+icam;?zzslXJgJh{DO(AlTWE6Y~W=#!h; zMuYm&u;+#G%dg8rZs=`*Y6MB$Dp@A{JL8-3`yOChim`cy@g&^D1gLM-(q3V1NF<*E zg;bS${ykTs(gh)!!%O=`f!Gx@h@h6F?L`nx#_KH=N~G*YO2&Ya^>^tE?W3NIpO(aA z&?V_zZ`9XWkRCl$!^s@gtC3hyb;w9EL7d3rOn0fp<{Uuf*z(@cE^+(t(+?HMFrt@M z^!}57byM%wOu|+U3!FXaN4sjV@^FSbXQGN&A!1P}O`A+NRNJ4dBT%L|-U+A{c7{V$ zivcJA0F=pza=O0?bvD;TEY$g(5#jpM3rh>eMNKG>u*4~%<~S9jnWM}X_U3SmVB;aH z%^A+SsrGKsx~TMtdK!o8@T@!uC}dI0C3^Jp1{4_LZ8jps)*(7UUpKaD_pgP2~T{RPs zRUic0_KV;*^T--72t4#UoqA1&t{Wh`|Ea2Ge%8hMQi0*;{V+zoe6=4EYX=+B8~K#R z3h+v5W9|Y{L+8+Ho6804_!i4~bG!q{-Cq8j$Qh7y@=nG00leZ4m#-U`nlx({*{G(A zrO>{!VqEp8x%8O{c#CK&F-&wg(1IX-HLH^!w$(}B+(B0_+hj@LP)uEHCM-Poj}H5Z zoxd4(wnsrtvX5-STqC|e_4n|&rYJ3-G;w0Stz)B|n}M{>ElefLf%xMGMG&pr=s@>B zP0X7SkpMrRBpne>01P{U<8>}{ryEaG>ZLPoXKBRphvQ36?n`kn0ajUNq=Oaynmft$ zm6Tn+<~4p{t4+bYr&1);R9-AL?pbMQb{(Q5FlcaH=f;lRSb7g8<&t+vr+3EvK{-hA z;2OoYL!EgBoh!~?(t zxA=7Phu)S6|0u4xY4MydTvmXr!U9vd2}||%(Ec}jiel=hYgO`^ueK|K4v_a>!IUxG z|5#kP>*E46r&~hlj_ky_ZaX`h(!U7D8QOl~@u@x8=d<5{coJohZ@m58%Cwv0=!Rl; zlIfi@NpWFqRbSZfYT3{NNb9Aao*cqM8Iu+(-^zre29!&D%F}X`1msTGWo_3jOE7rF z(D&{z895FlX!AT=Stgx@)zC2vaPBD*>>V2;az_4FxUcW*?g@wwK((Oae50a}zu($s z7zH_dI_uXag`uYUwg`F(7;R++Sa9CE-^k(g`Q-q1Jc#DaQ{;J@VKL0s3sBg-A>22t z2^Ju$!U_0KLDJX6nT;RElgz}2sMey|g1@ZLjzv~5aYJ(S!MmNh8)=$rMpL(^u2T^6 zQk}Fcu8VNt+%K4v>l21InM-%whOZ-w{cA0L$ADeYOc8MX9jhNhq`c>YH7h7FoA8HN zM}vMbwJw=V8g!LKkw2%!+4fj%OrPHsAY!_RIlL3vPNUMwmz(S~u*|pvodL6D7#hzl zv(`o`PWsQ=ik=q+p+09|TV))e?gSR0O3Z01h)a9#_alTmJT{7LNM6Zt&kKlf*HZxI+LoDHHV89 zJZat2njLeTP=;sP)z<>LFp)_9pp)8!97ZUi*CK55 z9)7@o*_Y7%q!cEC4#OPh9zeDqVucaQ7T!O^Q+>s{&82qUK@~ambhSGj^6Ef48%`YE zCf6tof7m-uWjtMvzhM@OZ!RQjh@S%>69+7Vg)>OGZXB1wT>jr(RU9w|=sqF5eh0F^qEDr8J-7k=9-91iq~Y8eg3=xYW~ms&gPP z9L3il&{76F*JrunYU=}ri-uCAPV8Vlwyuo7&8jF%Q~r*aQptO66Pv6b4{6n-iFejR z*Du*A^|W7)4q$zrid*p9hRiIt3L4lR$#f8IV@eOM%$i8REtZz>-#CdAf_v`cbI}_hhmBx~j!aCO8~fX`VY#8jw3bl3;}yV7MJq>|+k zLEPE|U?*G!BeQ`&r7oy*?-}+{v+}stE$y4t4XL{x$7qS=fM(BR?SgT>tnDOfbtZ~l zM04%0+YP>S05|sBEDN~I{mS(Ftds|;$>GIP&K3t=)P`Sd@P+rxG8J(@AbcENH8?c0 z2irZ=Lg@Ke0bpD9jWC+7`EJ~X?60^FEpP9};?)l_Ox`+n$JQM1Qxz3lk<1@H=87j1 z%n)+d5tOlD8vrqtU8vvvdiJ)5KsL1S&avuw)%-ZVJ*g5FYL6h!^O~Xpmgy&ox&|%t zs9DzN9LKC5E3L25?Q!mZeH5=moQ;=)^uxvS64o3>CRB69f*0|BS)N1jDO9Ku^1()G z1|N)NO`kHykSvYuT_9#_RN1fDeQkke$pHtY-`^j+zx7Sg;E?c-3=i!1kY8dXqNf7M zZ1}Ep(>(n*u{)&}3R z!-5K(fF2sBlB6 z_+qm6hb8*d%bJ1|P--nNUR57p&A~wh8*sTz(7|JoU$LUL3p_OIB$=K60Hv#6%W4gQ=EnmA#RZ-8i)bA{xp2)I3U?NLpa^Pww z2I*JY4&$u(8;b^^-Yc9>e|I8oO`Q7nIWtLlNM>c^{?3y9B<}~N_mxOc=v^1qVJieH zVhTu~cpnll6|?yjvLbNc7{YFpYR%kkoed{t$Nck&a5bbPe*o0Vz_iSLFy{xH%q2Pm zShqIeHQ!T4{of#(S=L7!g(H;na^ahmOtk?v=djwN->LKi*B`H0oo6f;>$u)Ii#~sV zE-Vsp$(u6y1$X5ARFouuxVWvq%hG^z@Nw*^q(YT zR>$p99qjL^*b5=9?jN`3Zs+@(MxiD@tYxen#hs>n(x&ztJjbDip$F3zPrH5$lV$=v zI;0`Nvi*e>O8mqkYTq(hf_z()rh`(7vMN2b-v?Tc+S(7EALZ~K zC>T<+fm|9~17etSev~#l+$*JW+%t)QU#}GNr44g`q3KnsT&bj2;}YdTuw6ETO#YI= zw*CI*=pnl;qG6w;V4D1=>x*6zzQcUr^kp)6uY(%T>uRVb$aK*WjZP_Fdh2seWK={< zJz+mWTkSQvm=F1~P9sn@r_Jvo-JN~cLF8BsoKI?UPQYl8NK6yRWA0M3wlY`SSW=b> z=Q!y&Aa*Bk_Bg)0aptUej8jUtj5PW771${UG~B1Z-_Ge*ue!3*ruDr!M61}_C$(j@ z#JfE#(c;7g)uXDr5L+u^ynPgpW4f1s7z*5rxp=EX?%~Z6c)Ri-uAmF~@xdk2v23u_ z$H99GS?5W2=J9-Qb))u?6Dh%ulnKVvF*Eauh&IGp*A=5&i3Td_O;~a5r{XOx8SD_1 zK}`3A1?glhw74TYciC=)Yv4X<M;tA_bJZ{r_HQS?u5{FWY_`g}5^ecb#s2DHVQzDLuFv_--7q|~~ECm!V_-U^wS>r`@sa4Vi?DAR01qq47x zJg_hsWnYiJ$fFP$wC1G3Lc=qv^U1zmEbmAS;o;Gm_EsU^hi7hZFQr1;exHQt;7Adj z7o&+p6fL$6dRd}ttOkO0*!sx=$1VV4=g|8f+Om~gZ5i5v)KYrHI`=Bkfl^9o@Fs(HyWs8IvQuSLkv72j}*PBFY% zmI`?*(b~s(RZ2ft-ufz)kRcWJ|A%h17q~-gJJpRsX+Unzao&^{(ZR638M$nIW`(~? zX96epr^n1Ham5V%Ou^k#KF%G)x3Is)vPvSX)LJdZ?D%!&yR%IRI?%G2+B{&YL{?Ro z$oesSqE~vz{4W>j+q~ja)0sQBg#s9LF3@h9%z0XiiOgXLG3a?Y8Bj#jGVYWj)ue=u z^ENk%wIu>`p;Xz)TZIW+wrtph#&c) z_CLa!tZJPUrKTppUh<5@9E-c0)x^WAP!?50oL0UzYB^=)?^)3v#Cm2Rv^e{_4x@e%ra(mYwIVlT~x@ zv*O))^&T!r-aV=L5`JGX&hMA}_kaQ)>mvv0zDt1N?o?=DR}ihEqdDQn1V!Ln2Ee=A z=Cr+oH*0doM9ui?pZ9$wC}JkGVod<#64uKby;J&0QIQNy!c3*I>vV_I99wlqp{6as z2Iw{`pnoGey|SXMx)N5A(5kFQp82K~#gfwCVXE`c#yvOOZr zTJTe-IcOBfx|&^+uJYn{h0E&5fwx8%#?WNw32z^F-rp>j77%}<-4#Gv{|`_vyFpkg zRZB=TrG_Y9bEp+H(aNd7Uv$6}W(moBG}*>V_=~=jn_FAYE=;k4LxsRNIN4gyNm4qb zf#TY80gQ?FNIZ8+snyvh=kRDChq4^OA^SD}KF}q+pNIiCajq+3gqk^H<~Cb9N_f8=_@-o_ z(VSQ7S^UM}l(pnVp!cV?fZ~L_g*zw!T_8Ten|ml z4rTX`dSA_oS}^c2MDPvyOZ^gyX&_utpG1u%m|(m43Ic`uGzk%nfmpASBWQ|q(5FnX za;a1{N#fTyJP?(qXy(Q3`XP|Mqpl!Bskadmn;h;|*Vl}%F_8;dpFd3@Cw{Qpn_x+l z4~34SL31v4qUVuJuGZo6$g7`4p@fMv3nik!{&DxE5%F^6=d+usI0R!9MJ(SQcwofU z_L+U2${aw88y6to%8_Mbg-1YMYW$TLs(coA^~-8db_?Z!4i|MexZz32+jFzMxTC44 zD)`;$-w3xD9D`$Wko*sqEbcK&ftp1&#c#X!G%QL+AQh(dPBsH#SuP}1X>~AZ0@_2u(i}is2$7&|Ipw*Xvtycl5 zFd;Jn_BI6~Z*UMWOzr+Y%L&lmi^huyMB_d|R5SRfmh`FmU4IbsE(?!K=t=JE_-p`d zPc3SH6RBQ?VbhfD76TY=pCfWq=!pi$LMO9(b_Mq|p)QxTQc=@(R?xfj$FwT1FV#J6U zmQ+MVWS^A_@qeEKY7Pf>o89P`s$PG)vg1wuGU2$rXvBvzq9+|^yvrVTDH^>pBOn34O=DV!1`@e!+PRbb(?Puc*e zINU&rg{P@Q=}%%5PZnix2&ZUi(3v0xCty(WQnV$`beyFe@1zV7i>0P2H6XB(T`nP* zDiq$p!|QFGT}&0jLY{Xy*!BOu?QXCks>qBMoi~#D+oZI_dv&2q7)oVm&>&N&tB*RH zXPYp(9e|-HhzqwM^N7zC{Pw))LUy!%VLQEIHy;dVVF0b1ACA)oYDi6a=XujT--b%}{*+WBFI|0s{l`XR-xB5yQ_hGC;e*6CXa)p(~59G4ioR zpDk+&TY(+^(EEXYAVcnYk56+Sp0t;vvuF^?L}}6}DMr_M>wR`+rhg4*Hvn?|m;ccP z=B%prR!PN`{0IV3`uK=)q_jqz@BL$ZX~M}pHvRyTD*7yXKf9&?ku7HL<7d;d67ys- zpfLqOMC50jmVf@w>gfD8~nFo_yx`HL;qB@X0M=;q;QTMRuAkH@yohkP27eix84@t z%7l5{>v+4WJz6mMuizU2Xc*9DPn1LxyiOyp8pXbXsfj9gSz`t0Hx}<-nCr}77v$Ii zfrK_@QRl4W7-}^VRghI|pe5q3mhBsY{bS|ZGgNIs(I*@^L*#D3*#orib;RDl1Ot)5 zWiilI(B9Bo;zU|PX||%CBA^Z)PL`>D4_Jkz69&B_x1-xOs5JGe#m6%@nL!F&b$6 zo*s|i=mJTQWp>^B^(RK#Zbh)>odE)othbtbg`qN7c~Kuot^ZAbciar`u^T;rc7XUN z8zeu+J5RO{d)zYNbd23A$s&2o9IIKZt zk`DRu=8RBs5>5r8Zzcvkgdfl5B=$6zc0b{N!pXTTi)I$J-8)yje~6oo=thtBiL9q83 zJr7TcRWscglthKz@_mNGzaJM*Ecaxy2SL8m_U!cON>x+Vk0iFxVe==Ppyp4z`5uGKPM`w4JcZ`9Ge($Mr!2vU|a#ND+G zx3&||0Pu59h)_|zxvltm=6j@d^@zNxojRScVpL7ae-FZzbe4cqp#Ttw@O~{HJW!V+ zhMQbUM9oJxTyuB*qZ#4C3+l-nhUc-a_7Hlcj?E(!T=?`0GtyizCU8J zb6sJ?&y^704#-z|`5GWe)K)O^VKGtMoyuR3fprd$Hk!9?RM@8{B=)kmNu=*?CHc1~ zPn~>!ih3PisQQ$;C{Dtpz1sRH4_+e77S?9-l^iRy31ab}et9=s{>J4P<%IMmVv-O> zn_Tr(FqvCyT2&u*L@J|k%>U#@4MJOF<-fOkKKMf*a+I; zI?YYQRj=WEiouO?|4+^Zcu+C^vWFe;x`#}RyzK2g%xg~*u?s7(SCNQ1;#SOL0cTR> zrZ#FdBMh*;t0lD49hH=8G3OwFsnlZ;a{l0DU!pCM>)HL;t~tGUW*vEb97nBtS{u$H z1CaHY1HUlb_XUo{Nd|K3o2~Rprr_A zD=Ac^raG?6b(Fy}J($yS!8jVM?EIb38J#m)ml+1lXx)^Vf@o}QTz?0fH{&;4lS$#b z9sM}TYnO&q?WvbB1L`_4o{M_Wjxd`W;h&sIn>MboNl2^D+U5Og@P7eE=wbAl zcb9eifN0?Qx`=5!aGrq*637PymzuCR>QLhFna`#?sm&=-ckz#&^h_V zVxx`E|6aK>RM}Qc+V0S`gG@Ew(_8xzG;2Y`LAM&J?d5{K#gk`yv4C#a38TY8{tG1R8=TMiH8;UP&y*u&+#)FxS4g9q6{tR|v^ z0g~9&*fq3+?aikU(1srkP>0vyXUmVS$$9Jotw8y z@Y27_q834euhI_zldSFazb0Z~YpDzfm@-^siu`}XfdLL@4g5KvoTD*Jm!SrMp=e&?LFsEI*)}k4SI6D!(^jycjnuGmx;$RR6-@63D zzNhyF&df$cClQoQH$@{qzPcWbv5QI218kZS=AaBvEeK(r0jPCj{j7wKa3HqT=QX0C zCe;gEZsPu1nrYBwjcIEni|*F+mDHP0H5$)<VF>$ft$ZaP#MvVD$xceYAm@k|31U^?4f@TKUdD5&c`29q>+2~iVMyL4Q1vG z&oMe0!;^h@N?7N1F=P`O=+;ML-ch>a{@s8L-pdHkM9j5O3c~w_foebKxqe6senlYL z(Ag}EPvikh zz-p73F@?cd-+l2T0#33G6HgQu7xBtH=&Wr(CUl?w+_}SGd-P&cE8n_1AGL(F_J3Lz zAFFk8%KQh1OX6eI{0NsQG6Cw2DV_-qo=T(@=k-=&G}S)^$|sxL+2ATok9@-7b!tnL ziIeV3REFZO-znrk*oK?xR^k!JgbTsKtZk6FSe(ZhPo#1Efuv-`OmV_HsDAq5=a)yU zeA2qWkP;_#uo(1IFZ~}%*NjPjg#;&DZlhGIPP{rw!P}Y$}hDZh&R%vBGUTI8?ZIzx?CEO=gG?mkWmAh z{v*PV-*>$AX76qVzSL!vV6!hQJH;U6XGdbsqMb^YUVbq6fi+HsD|sF<&7ser5JPz; zB&&x%W_$EhELz~c&KvIQ$F6w(9&mkHXIvPkAOw-j{j2}UvaA%MWvZ&r3QlF5a) z_2S~eTQA!KFn2{bCv^Rj>_ESkTLd!VWHpNrdMZHti{-_TZQ2UPWi97=9^cl_pD}zM6(l#FtTD5B&bn#8`^d|yiQ>*bCc%H- z3Knpv|41$4p(N)IB%v2wdEFrNWH@Wp<4JAZ^8bTWAqh8N{rpQvUR+hX-sF%v=ms7? z-&a~x=4NSmQvrfm-#-l?I1TyMbtT35?yqrTYSt*q%%QOw=WalXT5J;Fy^4+pL2fh5 z$}`sj`G|0gE+-Az+YycI;7Ye)cVod59sRCNEuJ=7$2!Pn6`{xZ*?z23?OF9*4k!H3;QHj8eqEc#4r>glbX^uiLHLRSN2NEx?TN>41mZvaTi zU-Ad7;zavuMD*)PKWv(ybSOWq4^36!HIECLchVL34hY8@4y#Jbme4F;;r2=9(#0zwV}jEvUxv5&Z~DRkWI_U%6~)hAs1teescr`%pncvVX%5cX zoS!E8x%x1`Cs^8zsg(0F9L2cow zKXOW-`Cl1ibQs!T02Zdik>4KIRZ;w|XUF>tnxp)qJlaJ*fgZHl%Jlm}S0d8k9fNC* zcLW*%zrBKC2_iys2TUhPiaf=($U0aSmTZI@l(Z5?3EOQYeELR}@_D~ZvU?!U$gekB zLb(`hW%f1R8NzP%iy!Ng2}wK;rR5Tei9JL7sLApM19{pa0YQFvc2G)&6&cxH`C}yW zD<05~JjDqXoX09eib6lQvwLjF0{NS6|Gg&vnGObvHU{LZcsTJ@T{23B{r~p{s%0ndU=DXyK#)qCgI1 z+3YWEX5&_9DqTx=$YMT0AX!y-cv~2C37ZQTIVNjl@kra|*4Kq&fN0SoOC0fGsgA`X zdCIR&Qk;U2fx!WRzVLX9D-4Oul^+2k{GD68L(q09JfABjBS*P)yA6(8(~eu|?g7$> zjAvMTH(q(vNS0p%WpBOv@fdFV5rhJ^H|85PB_ERKriI#b0Z7;9Uu?8V7Wg6FW^cQZ-Qt(F z_7P%s)7t!02i^X+qdBba-&ga4N-LiPYR92966A7^V3(Zm2wwu$;M+mD*e=R&qTK40 zPl`h^8p{AbVoLoR0{<%1Q?&45vp|3LIBh@PBXD$ATD?Ftp;Y`hJN|KzBo_0-g75E3 z-3_{mS?ye1K$Yx`BcyL$aTCqH+M<|W6ov@&E-tXngUzZ)feV8@%K0A3U}!)&Rs0%G z1BvqfOe3}F5kLw27uO{*KtgWZq)P5)w}`8_G7g}al-{1t4U-*&TxqwUrib5qs7Evn zSo-Yk7CQT=SG90x-PS7qh+bED&z=h>aTCB-%5%Ur zAe(2ag<@jpxq6?aj|H0lmSyK03`E#|z*u7`1{ERr0;wf@3UEu|git+OZa8Qd%|nTH z@MWHdvYj~}kR^I|E;^x!v4H?27|GS5X7*D~Thg#0cLVBkr&f?PlxkoqXJq!+0~QAA zL1NcTHI%E5d?E6O;zQgzO*qR^vR^)l8Ne@;^rGM`w!syLW{fH@(iJ&@<9gLI1qFn8 z>s7m|l6*CSQLP$bx*zV|0F&xDLQIEAHQ>h|#Sd%kl%<5-Y7qL(8GSJx6OFmJ>g6$= z_u00KtlQ=sMXQlu7E!n%7n?^##(syyMHOY2Ey;Q|Q2!=}aY#0xHT7~>X$t;u{EVG{ z0J89Wx}aE4xXY>|P)Am-h!q2R09ZwOhZf{nFHhm#b5D-r3sel7P z`8Q$bTI_W3*BW#nE8DTuWTa2L5z649b@6I0J3TFx=q4dw8m(AGhxhxzsQmD>T9vuw zU~|iBqB^nA?aaz`vX~Rn)WG8g99`Zh*czy{j4%8#joJ>k-WE*k*p|;76^;oJr8e0pHGLltM4MwW05d3Tb|AD<+gDNHu0HnhR)2E$5Q4l5ip^;3^?UilN0ZAkP`F9|D zouV=MbRSenf|jesh%mv0euAL|@oPR%zH5VF(d!tzV+hQh>V@~eY@ouJuj{%H!`a5M z6PkSbK&f=r!1%}6u9;=6v7yK=`U`-MLQHToBRe-+x;-dx*`yWZJ24TO5{13&S`j+_ z(I_-EA!`j>r(T0J?iM)YEqA-Pnfs=l0QAz9mj-7T>n{`ux^GSoI<1ph0^qxkXnkr3 zV@ta!pf7N=1Jrv)?4pZr0e^0GzuI#c_HN-MTJHrTTWwGF$f`M ze$Us9n-0H3>dnU8J!P^Me$o*z&JPyA7%*c+X03b+0$kf>kJWI)ji}w{@4>&Xke{4Y zG`9n4V8_WT-^i$|~u-w*hPD`OMSnl5FZfQO+0dOk4`0Fp|x-4y>6DWXWA1eJGcQgx_ripMTvSOveL? z1Yvf+c5_8U1s#%x50`GFDg=NY631hTx1ihnB7p$VZkC%GLo5BJUe7`4a6sBxFXVYA z0%gp1qL%@ZOz_CmKMzBeKNwHF-rofWIwD&&qU6dXL@4 zeC5P)&E*5@;Rs&YhNJOBz_QHVEjz>~(u>ryiHmVfha{7c8i>GDX7kwfD%IRr%kY`Z zE+Z|9NUG2Xf}q^iU$F3dO7Q<8Y}akG)hJ4(a5GtbKL@!U{PlGUz&%cC|0Atsdz@XtDq&`U934YUj>b zmn8~{Q@xgrS!Zx|D$H@^8*^>pvRm_<^VwOgvIA^Lbj#-&hb4eofK57LC8h^NYKd?0 zubH#qOh;OyrV({TgekQMb>11W;qFMd)Wbs*DMkpF=!<*WS37`@^`D6ir?f;oq_t|@ z&8B;?K9pQFdp^>a=cXR2*=){}K6`8$$krO*;`iiao5mI<9^n4-o`04Akx(~-G(phU zgCSSyD~-vPICznayzF@=9=*)0y8^n_6o+DA>)14yt6eaCTWYYVbNc*Zivc8FZ;%9~ z9szQB-ZYOjFE@q&1fEp7cwxO%!3G7M8^f!_+;w7~SwZg3+UjM4%W{SZct3)l@e+48 zEr`V2`Fo3+f8_Bt(VE|XvhWe2ED#0T%LG@5{ni@@D}<`^0s@=S{bx)Btyi1HWQ1v> zF0hFFOLJ|7WRj(XWD{95*W)}f?1OdcRu3aGFiV0=iQ5XP#u<1Nd?!A^e^Kx91 zGbM)mJ69iV&LUeR0o7c*z_9-c&V-8eqEN7N@k8`D(6eCUw3^(~OV6r`U5}lkw>k38NLXl`N^?~GCw87OjKw@>NzS0eF zD{ozN-3M;NEductY-(Og>)}01M=jHvBJp@Kxz$Sl++W^--Z_!fWDH;{k6@kS2&%Ss zg*6vbrGSmZ_3S6)%JN&nhvBL2*a%Fun%)5U6#wP*KJIce+qC_gjuwJMjuJn}t7Y>+ zqaF&YmMc%B+Midxq*>ry*gEgEm$^;Cd!;;NI%=B6g^f0zd9@7xNFJmuG>ue0@aWp& zyN`Ik3y@Acr2^mrQAE+ehOp2)fV`P*23PWm8$ZH$A zHEM6o&5;)vhcAAsWnxd}ochBS)`{ie%tHmWm~59+iZR55LzcAYrcrnBojCRBE*cdW zD3xhbE-07Qw}}XjXPefBaaj?;<2j@A<|GI?{wvj<#@w3b3Bj0$l5W<3 zg;#aCyl~VVre~J+lfKIsAI;;p&Y9txUSFQ0lpuywB#`=nGVfR^#3C~S!FKR>1FNbW zZ#Et77=n#^pZnKmx!-0-?K@WpXFh++b$+Z%-#Cx`NSmW}*N>Y7(3^Ra=8!cc;45JL z;FpT?B+(sm-^ApGszS;2K;jLe5*%ED%1iTbz?~GaRBO^)TyG=a>4WV9e>*v8D){6#WAkQg+I@ z4JI7c_dxXzBlX;qjE16rlxk3FDiC9I7#?wBMa8B!ROfy(N^+MdfmPNYR7YGL!#EVw z&tAmj8Ceh2{}XIrXKeb-!{R7q%NK@4H>&wN5)}*V{9T>< z$YAEUjZeY$+80KP_YNL2#9SX&jCv#e* z<;rPAyUrnXkFpP%)Br?n@D&4V6*MwV$A#a>bZ~xDy3zo zH+BEG$kBDcSvCiw?A~})?heNWDZWtu9^a|>Nh}Eap?_uJ?t8kGo`yC)QVo9&G!eMANzY)wO{hQ754=BcsHfq4eyh3$YbBp z)Tloj&`Q(*1Z~qb>f0x>fEF|8`F60$yQg)CE1jhj%gPPiF}ZZ>feByHumLHtX+*zu zM^JV^B1U7t7wRSp)Jg;}=o4fMO^gA_Y(^%xNOr^rKKyAsNR+YsAzj@A&7~$z&Nv3% zX;f&hbLQ!wqsbl_&_eK5d-hvE2n>1ofn#r9lot$8b0=kWAe6Xcz)%a+9m3gW8sP`Y z8^Vg$QTZ{n2URY~RTr<2u|}NX8AS>~>L3;It}G_a%|(6Yg@&Izp2pZF zjxIU@@8uzk%+flE)3?$#2?)&BR_x|y!_dBASOv*1dR8y#J{L`r6=V&E6d+sm92y(( ziJ^Re5#Z(MZbinA0eQ(pSZ_~$F8Cw-pJqdwFSdM`D=@u|M>yGQO{&ki<^!a&DGP8D z_}M_VxWh+X;6v^7-?H&gI~#`~8Lp38-mq(719a~jz18gM&ktE2%Q*P>7{{ZHhzsHx zYU&r{if|f~oC5rX)Txo{;sV|m^!8Y`1!&%mxD10z`VAJ6gDbboy2nDkYg2`wgo!JM z1(luu{V5_CbhcryswTS%4j^Bmz!Da=_i}^mWesT+p=sX5D7FmC5>m3MNEM)UIfD0I5f)Y!BYC%Za~zOO zC5~WaZeQqoEhu=8fHd|0ix)2TA=4m_aAMO>4{{(%%uhJ(y`FFQA?{*5-EEsI@zUl+ z0q!8`uQ7Fk4#e6vuC=i4+d6W{=|VLbJF%B#n?l~YnCT+j9pnJ8lBD0;x)A?4Yu)kn zwHFcUhw^>7G9-C@pKL3Bt%~?YpGg6M9}$(NkO#E0W+x@DWudGnb^srt1S5?mwQI97 zY|0}A!lN>k-E9PIxdZbd;}lu!9pZ=fsG{7jOTtvH)FaRUx@w6{H!q6`{9Fg@*8Qco z*~=OG9wfL>++}m{w$NptFkVDG#!p}cjJp&d@$h3@Q|m?I{ohD)4Ujc2za85Pu>r{X zTI7t+xRv9LcFD|_G3zq+YUs{hQLB>r@KKev#WlF5usR7GZhE)^06Fj7nP@;h9#s9R zXe;y;6XM;tTp;T-E98*#&7hyhO|Y4d;& zNg_>n<~#sJo3U6c-d9N{zjK+dK2jdxLhi~OU6P5*YuQY*W%yldVh2lwq&_K;oz_N& z-(uA2aG|VeN9?4!0N%A`_K}GpV7q6#AMk5>emN))n21dpXrvVsg}J(!Csl4~74f(a5Jq=*b@drI=qWEzP@p4<*JcIMewFC4IyY$IX zx^q>M`6&vh#H`s?^&u)wWb+eyVxs@QD-X+RCv8@dLMk3#l@u7$A%@RyR9mme_i<9u zq^6X^VkA2TuP|yCPlC$GVX2Wi$>6){5c;wDc{Wse2zwEM^`Rddz~A!O?Q%{xPp#bK zty%FdP@qW0yD}9#P}Mya**P=w>~Z4(+5DzX((ae^QI}miZhuWol<2~yQ_+8N#M=Bc z_IFdq=OYbV7R^*ak==KkS2X0hm_tSvxtf@(DlRa;M4z~7)fx(UokaWM6=i2JQ^$Se zv}GzpEYP%qWDgT%0(s=;XP~3PPmiYgh{f!GlbS44)is1QCQA!H3&#@XKxD|giH_(Y zN*2GVt9H%8eIEuFc`6xmf7wNM(qEScd7B*10fIOU7zWRDYRN_1nVf3RwMvZ9927_);RMBb3+nowSC1hH6vTgB zKN@h9A+B;s!*(DpXLOhj3$zhdeu=KjdWX*zdGlPnk^Dro`ARW@A60PPDTtVOGWe39 zss)k#ONd)&H?GdT)xa_E%)Fksu_y+Q!hH*fk~FaBEE}Xw3hfGQ(+S$YD^f-4b$Y0k z_2;crJ!VNsrA4PTS8vJoZb(pLXD_pR^n5!gq29>&mA;gYn-p6I_-y-YmtB;0>9;)vdXr@+jL(fsb3BNtuoK%qGTA{6a~_0I3)>tkYxZakaVWS zH|#_Q_`;x@AXh>i88ti*0TeDBX)&rbtT8Ha0LA5&%>)=lgzFIH@DMcX{9DJ8#4Mt=$@h4 zW2?Aj;*rJZ^CS9TvFQHKHbO(c(#L_*YVftyL0NL=mB6m~Ib+7hohx6jvSzhlRFx*^b`>Y;rO4TXV$6-; z5GC%9`>BEC$NhkzX{^MDkyjyDLVgD!AG2T9aPkv7vH%Dh*(lDBrMA@kN>c4XdMDuZ zEblokTGX9f^~rldIl2Cyh{^h!F+k~JS42M zQcj_uaz7{S%BWRt3q0u!pOK8TpdKXRwF8bisKNHFlRJ}j?yW6j9~K;s20p%*&RDMB zj5Cl#CPb(bfshg+ZH`e(P!w>py4g=ka@P0I5HSGc@;e3aS{^dnifHeyU#gx4Fr`<9 zYuy9}tbM8w$toeurw3*DOA{i#r=6a^8`ske%ad<|k-N5k=9MQ4u?Mje<2$GirZh_Og5_1_ zA`?^FY5QrD1AF-%@=L$AHwFbL^tKcyM&s_PmVkfXjzX6Wps2e}d;QL8F!G1e(t8Ka zr0NaofETs(-q$KJl6#p_va@Z3qxevfgpm}MjST<}wU-6kt;dz(r-n|i2|j0Uji^R# zS%Xkm&?RZ5n-jpQV8mf{XE_V0W=5iCq z=7R5#7v@Qk!{ZH z>vgJysu3_5>lVTt#Nv8e%dJ=|%cWDQB1Ywgafo9#*md?e30_Q^VDEQBN;?=- zV5Nip@Oh&rS`peUKmMMwFRb{d15`b3P8#671Wwef6A@b}WNK9^Bk^F=P!4;*!Tli& zd<(eNNm*iN+N%If6--01BV44KhBZM)g=)@x&%8ZxlIqzH=Sl@OG;Hwh7547W)q4qV z!9IuxycjL>ry4F`d{yQQdh7T`gDkX(S z?-II%_)PB^>?V_jWi-HJD9i8CP!E=()y9MFX;x})N1ykBn02Fwm*|$=76YVIbrVaE zah0O2CdiMIW$RXXxjdXJYjP$JHj`+VT9bFF$I z^dt(pxz6oa`J2J$K|`IfR>Zb{Fg07uPp4erl1$k8JY+nz6crRfIVgPo+4)ajfIX{j-j z;avm|+YoDxF@YeO*sr^0Wf|yO5^Rp|dM`~%g0Y(RZp`VB%b~$e$7_{?@@PVAtHIdM z*u<6>0>LY=0P#-({{-K|jYQH@mUh>ShDMK(u`yJI1A3l2Wg*~GQOYXLem-L%h#3&g zyzj0Rxnzln*wB;;Yah7(M9+#*+I{B2nsUxJbJB`qwViRBHq73m;mez zr53hU(U+jYZC`1R!eWxAoX8HX8hV~!3=RMk-sr|xGO=+WI!lU}RkWn&;c=X2o^kZI zDNvYf5v-Tz>ErHPnY8A3^617{Yp9nq`U>nT4l>rkOeK)dgFzc>fvnSic}TksACpke;1uFY?J(wnTp$FWM(l?rZT7agNv9E6ZBeM&CTJIoRl5%ZL{Rb8p7#8{kkeHbF%J{b9cmWILx2L4xzEpIg zGUYPE57nuz26~ne=oYhBqjbQO9c6uj68y!eEMcD%XAP6_U0VR@Q_0ELK06|)e~MTy zMPk=pGh)?h5_3}KB)i5XmT1M&1+P1f3GtKeE+_x=ee(?z@~iDbKtf@w`ubP(y+bY+b%BEHsCMfgvKzW;4du`HTc3}Ve#kO$ zUkOr?$p@SHrtL|1*NqxbH-6B<F{rUyn3(J8J?dLlSWX?J4?NQc_i3P`zF z8SP#*=S>-bx+m~bsabpH!2GWJnz9V!P^^ej`@5H>8eSIx= z91xKV3%dm(e`#uu@j6~9=#b?8%2zlm6GM=lZIeY_A4XL2N{kJrloYRB%jhOvuJuf9 z#U$u2z@edWXF(HkOk6?Db(1Y}&@5Rp=kTT%9V-vU_50F};ivLqu3}PVpvetv5b^v_ zi^Y9_aBHRdKg$U+wV&=z|J`mU8CmxQLuS+qC**G zvo@O5_2`7f4AZAQR*AZ;t=B$%;-hCSE+U`wxz(6^7ZgV>dB>B5E9q@`$4uLTRg&O$Fg3nt-w0YuTN+A`E%X%l3vjLHo4 zV$cNp6K%hT8`_xX11F+EMemn^;d$OoaSb|2h$r=D`m6VJJ}N(#{}W!zv0e6IT~tQO zFqe`98_yV(&Q2}#eJi9y4p#e(hjbHh967e$IL~dvpmuUzq@*t%7-<=*$@JwWR9(&R z`!r;1jEn0*WWRdp$#rACO+vvD1H|>K_wg(nq!6s+p2WO60ueStejEipnd&^ z%K%v8ES`~-3#(Yb6=kiI>$)gW+g) z@<2hp32qrpJ+N`Wh`@=*M;s8w_+6B6i^$UYq+!$&rb670q@7NR8Yz*47}Ne*m%rL1)K%vWt^sG!x*5) zTZaD7v4Z8nfX=hWE#&qO8VKUxJFI822nqT}yxSuPl>2l}&fyU_3VeQm00L)j#NS7Y z)qo(JIW{3v(`-b4vH*lK;6N^s`Nz-P069R$zvf|-MrOC$=<^x4=73LUBx6&BB~?{3 zcsDUy54spFkexi@$CH?FvGoI*G=(2kC0eE&jI7rfT4C-g8gjD4)%%*bNh=Lfie51;jCG(*h_;Vo*hd z&z+}A-Xta|jq#d=;}XRqYY{pBIkR?|>6e;IVcRVK)Y842 zQSb%~!ik%p`Q==iA+`d#(a~Mi_CW#gq3*Q=vnuPGAfp8tUv#txnnXK*aWA0Ai3>&7&@8$A8~s&GwPP`W~Th*q_Og zY(H!Ix?o&Vv8aQrn2XAp2};STBMlz2b;k8ixyC5{@T)jZd@Db5+BZpHNOd}eARju4 zriWyX%7Q4)oK>LOigd_8)!iZkUooiomlcT#dl`pU-MWO562BYd(ffVV{yBzi1Dd7> zKxB^-(zE0;mbPE!P{MxGa7p2cpyXgr9(@dOaL8wO~F zVL0jJNlR6O#wDxRzBgoVAM9@aP=71}bVT4F1ipo^x$jcE@6<`xVMMA zUl%9cubXYHgYw-LxNm;ApJc)}dV++#1^C(37zXQlX8pj3nkEL zGsyQCIi%1v_QDsU2zW*7(n>apCid%~@&->09^jz#OTPX^cGCbILB{ zaC1Qo?#9N9k95p)eUG56OSGF;m?Ld}N@5^d0>@)BlH-UMdPK)6Y~%LF;zcfXZHb=KNkS7-j+P~XHg zygTtdNnp!HxZFcWw|SQIfMjag^_b$JveD@<-xP9&1u_pCytUxvPuAnBFPLlYmALw| zxW0XZ7-dUyK?*IM{ew;4p%I;Oz8!NOar4d10q|^pz*kV3r`CPH8Uv#Bh z-zv?U}W&*&wYt z_aplJBelJntXl_G^%1r38j2)c&n6F(ST*M$AKSH%HY??<&}?!p!A-QU_5bd<)q*&} zK#V8$s**Ff3KRK8wyXG;n$x-e$R{!ZSkH}R-P8G?jX;(1>t6|$}mRnDAQNV79Knj zi1DerEA0a_deKk{T$8`sxZ}tC2n%amZfl}=i?Vdy50mts8Slpgn;R9$o#Cn5fSJ{q zw)$LXvwWjJY_zjn`P~Io*$Oyz1cv6f2i-{_v0wyhUR3p_W)_RWB?g_2h;HftFxAi% z)Z}||2`oM7+1g@M#gm&@^3r)(fXoP2^S5hlE)E?t$A|7+-lx1U>t;ao^YY1BP5tyc zlD&16*l!c4<&oqr{T6qs(CG_Yc3hL2HFckpxmVfQfJX16tJ zQ7{pNP63{jP>LmxLXSmGA@DI(Ye~jomHBxzO;CVbh*!Flk!*Xbl#nt=X4dxMAq#dC z(oOub^RSq6^?9j~gy&zjfdHt&ipT(~gOczpy1O<3Gm{sa#&Z@`GAkMqE;(`Ig-qcv zI!cS58yjRf%63-hGl5&Kt>?Z|`aZA2Gq~R;qWs~eF%4x^vQKuW?|Tpm`SH>p>2e9oi0AwaK2&i+h!VTGlhFZ;$iUYr^z_Xs3F^T8HLf}5F0X+iK5Q4u({a;LiEF7)WO-HU zLq;T_)pPRMf^&o;q4l;I$y{CgZwf9g9MU2iuz`9D{t1sW$>XzNjQRqR2ajB&Q=vu& zaVKI+wb*!MR(Ipde$}M`#h=cSk__qO#%yx$kz|MJi~)uO0sP`MUU;BpR4e zV*B;ySTRNAi?^Z430pCB;OX-lf8F--*!43!I3gB81T8PwAeFDOt==jU!JPnLAUldYV#PbEO0mc6J-TwNAwYb3j)<`J%H+Np6%49BZam8 zSaUe!?0UH&y_9s1T=#>=7A()#sr*!vxXj(|MA7{AOuqJSfvo;D)!Z* zMcU)PgT2rItr=8b$F9s(mdF9_gVXMx7|w(S_$B#3iI#fykXtDb13diqQd@fF5`~HF z^Ucv)&(G}M-brN90Y}ZYaCp`$2#~0oDlAd-HW5i6Ro^22(=xK`b`QpU-U6bG`zn|c zn2|z9z~8Mxn)2bMbTI3?8Ox_V+qfPKI|LGO(58I|<`b57+J;PNbSX+T$5ldXbWWvS?xg!s7oQtq$tVb=Cv-IG8$ z1($HDBaLwDJ|EU9`=!;}sL5jiCirN6(Sh?h?Lp6CF?I0ED)7Rq>0=DdBqY%`Ng)vE z@7?(*X;g3vu=Rz#7WCnHOLLyw&=z9AEkoikjrxGzZAgQCi6Cr;u^uhN?!pqngdlLE zVq4OefMd^aZBeJYyS0wU<0KekjMWu2r##8x5>_(O{Om=5=~;am(gDrjPR}3BgYga< zzQ%jGXjFcpp@em>`U=Mwb#TZK?Q?yZPvO8@9mJ5(Q!0=YrO&O~sK1u1N<*Mt&r`Oa zn^@Kk7q&y6#egOx;v-vsxi~1ri`QY@oNHciIr0BO_Y?s>6#NgLU-%R=jrU!y#qX$q za#T&CXLdpUM1r}J2tP$ncmP*{sjE;(7C`3Eb%5wJwd}{z>|YB}p6NLV-8E!gH0odr(J;w<%lk>DMzP` zXhSM`p};p27ju=*%}OT@I8Z z_fx|GT%r>G8=<=UbveS0*#R+&B}#}ip@f8tgzA!XJIckD)Q99AaXoZWU1w@W{V-M` zg+iKf3B^$DoWbQJG^Bt#2g&}mfoX2;S>q_u+>B-o3+bXzu0XjDNGR-jZ*3^;u`y_^ z;+uHpKI&`dhh*emjviGG-Nm8}hCMiC59OWD9dh<=4do6WZ@w&#>B!A>*sS+Xmlt|Qaa9{4A$A-C%586%O{#W|4ohl1lCW-3X*5OBfj{d#9 zp=4RN*!^4joSe*V$8U&m(nBJ zca%co+@PyinO!A8THx0jER=V3(zR|}eD;|6G;O&YFC!=aPeCb*73qsk>4*ekP07ZC zd%h3VZ-=(P-E3a$0FGSqv}hrN*DXQ4tc0t)ZpK4kZ0}uwM9Tgl;OKPpi#T0A#Sq|u zL7_0*Uz|nysU)FxtOpVa zF7ZS2wnjhA1GU*bq3wNNrscxvhYV4hkUWz!a@6NRNp2MRHy8#fsfD=$USx#Gf6hmg?Oc)Ak+@WT_9G8MECYq$oHZG_|87 zAU6evx5kP?cj2Nx45E=WP}je^=!j+wZuf}+y6vXnb^J|}%>TzFMU!oR+w~zl^0zT_ zdH7lfrcT8NzL&;#d8<$Z3UxUBaP=r^Q5R$tY+(bjPT7FeBjU;I^F#VBl_R&J>l}JT zn?W?Hxkcv^77+TrD1L8fkm>w2U^PhZM}rt5V`J#_1y7_ADI9|k?I|)4w5#5;sO|r4YGrSkq4?C@SC?0D%Q#gwn~b1uF9FYG8o@ zw!Zv0v5G~dwE`$1N#>p0X)QNroK%NR%lD)6jf)H+(LX2hsDLZbwet|*rSMZ!rV;EW zZ52dthbMDUg*h=UL2yNWs=xEwYWgtnYc(t`e$CU|n#RVYY+!DYtu5ek_Ey<0K zPGgr@*|IP^bZszEr}0xFIA+%r$x(bioai0$;{vYSpfQJ5WewB~hk=6+_!RbBQpzFV zTwrc}QnJm>A=ArTvrJd2ab)b0fjN5~I<8V=&jrC`-A#=77B>233rYzjHfTJ$!;noN ziu#u_q}hVU)8C$N+!2Sg>GOpebj?uGjPzFw2cc_n2v}mwFGU%*tafMAeMPc$Q5My# zCR0EYEuo`Oti&14%?GlxoB-{dBYHvV4SY8S(TgXaQ^>D%GqEODRZ(&V>9!OSxmnEF z($%Z?n&)ZA$8sC$)|6JLp0Uy zc$hcbqWz6bWSE#vWB9sBk?|D~(I)0jads?HLlLKm*S0!J=T@hWiY=NOxP7IxmiwZh z=e*Cas-r7ORzh?xeFBdl{Q@f`y>9k$-_Uqzhx*r|b^Smk#FNEHRvC@DH^mqu`};r6 zL{3a9#q#&nl+sFF{6gI@j<&{7+ecS2b@?e`v~fhCE#7g{@qs%rR4h;06W8Ea~V zt;rgiFl*!{SWT*d8Ha>2Za|SS;ADzbT+6%vDl3@VsYTg-`yn2gGMh0KMm|keaZq6; z;Q+lkt7eCq85O%KW{DLR;_8id!Cm5IkLWFzWe-Vh;pLlre?_7F*A<7etzJd#_YUG~ zG)a%Hfg?#9mVVwZNRe#0ief2ctfJp|wH}Lf&PK$sk4p8TteEBQAH=_9PJ&6jrcUJ` zW;NK!H6i-GQ?u+It1%*CmqvYk7uD*rh#Kqf-jHMc0&i(k%1yP?f~JJYAohxZ@*Gyu zkd~=cU#Jk!K^DhDOO=Z)IJGZq&%1@gHt65o)l7WnnnKimJesrBj}hS3LPdF|b}BMy zMjCb@@t!U3z|vogwkTXWB!%CLJavL(s6s5ek0&~(Sk&l@7{D7z)X;BU69T{Jm1ZT`s8=x z!7-Qns1ypki|x5zEsQ0JVcET$wUJ+hZkmY!rixD%Qt8j@onXlbb0 zstVd$K>WqyMlT*5`rlPWjYF%mtW$;^@OL#4^ST6I`d$#(QTgP!32(D?UaG?yVp!kl zZLQSd@rhWK4GMD53=R{pr{GNPMEi;bB1tPxfKcJsfB)s5;5M@l?*v? zwU*Ym&p&a%C-;zDu<@DTZ4|<1`$x=$e~h-o%Srj(CKN)&K7LpwdYr+P7Q_ zWu|c^+tbA&(54|=LhZ;LXNnV7mbC?MsZDWctr`9OE?W=Gnx&55WcRy{K)^ecKQfYx z4)EMOxr%+6MY|dY$*Y5(stVs4$PxEtt?l;y5w8Z2Qr=2OPK93^QH-+mOuB8NTe@O* z_T#1ETt4ReP1iQX5Qh4URXB&){cmI->=PIrfU<-eHPCmDXS}PIw8R63bpp5wNEZ{E z9K`U)2q{$pe>C&8H@Rj?ka)v4jXHx&3SfZKxaB?Esvyj@RFBz@w&6%4>06CxqBSsi z#Aw+fD>JKO54PpZ)_6p+5R$lk3({-0%6^a0ZKO>ADE$25<*>ih573yL<)Rk=+zf%4 zf7T9wjZ(PH=C?|NmpGkQ(i6v@3+;-p%fiT*K@3S`IVBRw6K8J29J0@YMsueDpx{tg z3Fe&vrBd~xY^uC2(>I4vF+#t4+++cL2$hMiFG{gvw&do~$Iom}82S0~{-_(|nwIHp zO~D~pN#${YC`j%mHn6ZWemm#Z&12$OtqO?B3DB0`ffB`BCRnpNw-u}TzSKkpsZw0+ zeBnOqoK>-zy%rNRxC3*glkt+s_M|)BumGy)d^KuKn-A`p7>7-1n3v#NpKgl`I1=3c zMOz3N4k1u{(30S1TGrN@Msw z7%Bz0M1jUCFl6TJfC|WSqdJ^AEx-VX$w~Fy3@~q{)BiA@hOfu~VqMiDgcziB^ zCpZcJ4dN91n3yJ)D`7cE)!Dtgpj8*_T$MoM;3 z+C~w)^A}m*aR1HWN7c@G)vG5G4!)^d&gP%cx$m;wz`mZqed~uOK|LVc)2a)GOn|(5 zNC|WM^Mt}CXSl0Q77VVoDHVx)Kv+{QWHx#;Qv@T0Ccp1&r(G(^p7({YF2TgTKN#YR z@%=fptBOrmL`d&L2I)Pi$|uabaAb24-kAi%CezKh2qntS5kvwzcp%mashS@8v)KEz z)zUk16Mue7Q+fljjGtfA^Wq9maXIh6G2L>D3Z2HMSVRWbh*D}lFfENsu=)l)B#~12 zjiVKqd!w@#onqL|4gc$iNi}cLg#Bn>;J^c+R$j#^B^5=auOCi@O77I(3(SmUz-xCg zB4Sgxh0q*TNa=u!47Q(l{rcy0ilK9z!m6XW4x^rkp67VHn`OiR9>{Xr{bqHng4B!9 zzoZo~7!q)qQh}FLO1EoT(F*4Ok6gzoF46MA$K1qhIvTL)bK(RJ4=M+*Xx6mvpA72T z6Z`!B6DJQzK&h*v7!K2cN-ynN+{h*B-U75oZKXS; zVxv;=RmQ^~kI;B@lF?)-$!-w*AzKvda{iI@*pCz1s3KAguOcbod3IPi;lZZ90EEy-(PQgYqIkT9C63tWzx_(RL~hgp z*xHVk<5G4Jb*lL}pG%1s-dA38bUAdN=bonJ?dp$>fR2KE0CG$FBuGLyXK2IWBGoPg zO&iNHgLDS+n3#lrCRI{_I_VCC$1bS{FqN8FJ^qcmcHXqVL|PYUE8dz?G&+3(ABj>B z`y0(Q_E`COExp>r$(22CUwt$oUIu#hB= zaE1dt@W7%kK?)g~%Q&wJ`BMHpIQGMlHS_5(GBh2?^VC=b=u2on)7heVtd0a!BZqw@ z6KlqfsxqLX2*@-VhFifwM3y_H?Zmj%;@7$5yZld9^nF*AAM~5dO8Ez<)`wf!N$~pH zhlA)KGFr2@Z5qX3+VCh2DFhMBez6XkXw{t<)}}-nf*SB-BhBj;7qUsI88v5^8HUxM z3l6hDussg~WDqV#B3=gxI8tuz1Js+E6FA-HHHI&<)Qk>D4vgO5E8mt`1oF%fB|_^m zX_IOyvV~-qkbL`FFn0{S3|xeD)l0TRIWYl_k@N|IDMKjT5BJgpyAud{Jc^LyzHBx(~hbOlU+uGvuU%nz_oVQreC#K?bslTy+6$4(nAf>o3a)x;gNkDx7 z;+B?;o-{(r7&nQ^Bfw)Bu^};{7-v>&yktteq0EE7m z*>}vieQ|O*M7VpeOH)w6;}cpeX+1XQEHfN-3#5*r68HK#?8Z-o(Xr!2n`(YDdNb3r zID>pr6T*?`Pe@bG#U2k4?@3q&;_@#yORQ~xIU;nnk$ybKJw{d`x< zQl5+1rUd?~aZuqCk!-K>ast+14KFffk2GI@e^UaOQMy$cZ;0jW-7T!MFp0QG7dO|d z1P@|o)VE+!klGU}vOyr&nN*}Z^HTO@;0-7hw7GZkr{rr`)Jp{!r8J(Wn^YH2>8Xv~ zcf=XXmQyw;7hn)(v3&`&b*W)eW{H%h`D55Y^VdXrk77aiyGA$+`d*=$chdZ_rjopc zs#0?XN8AGt0>;QPhMb56Yux8Fip1fC7D;p{m`x7CkW1{mazZm2g=cP(qL3Q7*LcC> zeegfvmM56zR6R_%5F7m_J<$Yg&YcPHlEzpXGGMLYOUz}`2|9^CPaO0=z#PJSZU-)m?F-aB-QSxZdY2ST zDGxcd*OKdCg)&hyL9{|80xK0VwhRiPsK-eUTx;idpCzR(n#@%dI0g>}1NNJ+wnL?v zWqB#K*uXArt5zWt`HW{i^;w`w|5J-k2m`i)a;@GCCo1b#SubckHp__f^ZVAlLu6jE zK)$K;isst7=(k~0yT82Y1_a$%HD|A5RF{ox(^6h)-Vbn(LYrJxqXBRM@aUN*jNshV zmX4&4pXl$}HXrPO2)fKb4O^^trf7i4(n)8cL=7PxZB-@~5MG9)LIDVE_G>7-2vC}< z(41yK8Ro;F`K{7el#2IMKgF#Z1?qnbv>#m$EtZ~IJS=ZPpsey|_}_X19(!hW-z3T* zuaqJ;(fo+Lx&A(Y0DTj5q~5cDTf+bnK$bdCI`lbB_SItnXU{R!Z@-p4$yYbdm5IPZ zKn7Swk(8=s<5P=7C`o>AOkJaZGF5CmFTl=*Gw6+sC5BjZ=yU6#Nu_S zI1=<+i(ugPM^!gb8;&f$E8UW1Q&&wE^s-I1V+l>!JG|62areZC88H0%@uT?v)Gbl%9KF`Y6{a+9;>Q{ z40X4?8(Vwy9jn2TDLu7WjJ8ei38LhgLqjZhCKgU!m{XP8EONQ0zg<&IuU5CKf=S~y zeK^2SwRHQcR&8c;b#)4~Gqw!Jt7wZ-eH$u$3A=0IXtQF>$SK?MCJ_v3h*Y5YFq99( zHp(kRh_ocHcPEPkhhbj?Lqv{eRz-y`9lDCK2+312hnh^Hz-)?&k>*->PboO%6BQ^~ zE&XSjSd5@sL3mJ4)`oBfnXd~?l0$R-m9?mq%kl+3N=LnXRtu#3dqB%x1_@`lhpxKk zD(!04^oa#V(1@k_F+^zRsyh%nQ~^`3k$;1Us%zteJtFsCzYdH~HDkH`mrGW{;gOT6 zNEeKx(>YrwZP%+t76oizS7FCh8_ls6?-*(RpEA9=0tm@RQ z0Hm!5R<96>4BB8n{0yIzdN6&QsEHK2@6d5-$K4DRhNZEPChEB6YJ2djG;wINwXjHW zEyMS}urpH1ROl%CMPJ{h^LuH8Q)^x^`Q~1miHRf&q;GILBSD#su^zW~2;6LK=7WyY zw(W`jUOFK=wE}Djud+HyOwDrP0qjJr2QJmD6e;De@IUycoP6DMsf&Z14(QW_dfsLh zLfI>~SjS)zM4b2K0FAi-4DHGbdD`av$kbu3m%q`W4XRZDZlMOK(`24WS#L-1G``6D z?Td%h-glE%>qsTwa_V`r#^%US@B{ld^Orx1JSCHwaY=K{3&Hm2y}{u_k#mdlzI%62 zldr*-AovCAdV$?T133d~a`;sQ?SbIO_1t>pkc;QKZ$tEreoq=vY>Hj9omS(f*DJc+ zx($L6ANTFtCz`98ggrcDv1#PMI36FEyq}`l9I^~44dPlSf+zH2VLQKLQ9m1)#9926 z`=gL0HJlxLBCG``o9{^y6t`-{I}0~JS@7bqPd7?jQ=wZZ@8+nL@z%9If}SC-qr zxyc!lSPdC<>^k$LNz^4EMkOnFa`g)!K;#gs?31doI{+&G#ivWNA`pG`_>*bvDD#~X z6Sx{1mDNt`HMP+r6cTJ?77fnDcH=maluV73J__i!ZHaRJD{|F|ixA*Y+pf9tkY<@V zSyvV2ujMS)ux=lxh+&2vB>uNOVByNL+Wtkc658Xv{Z=ox6i#=%O!yCt(2n}Y;D5em z)*_!8b0k>G{?;9H{+T#IX!9>7*c=WYxY%?HNjsw~Pwy>23`<=dseX9+cCc_CvWnU{ zlLgPdKwV=2n!Wn4V85l$u6xHQXg zlE{cK(K1v}>=Ed1FPQM|)|ot?_y|jh8o<-qW8!sE!WN@Fv!Vg;hcsb2=*gVKMzmRh zi2c@wfZ66;0(OW2#=N@-$@0iJj7(ER49k?7{_eC4Qc5m6ci@~Sg9p(3cmZ@&0Iea6 z6DgM7gjqoMv+@diUSBjo$LuDxhDgvv6el#>e_{LvizFIvb00GG+W_RtDqgJk7epk) z7xX1&)4SfXB89Rb75Il|V4Mqf2+TkjKl&skKmoEH5S>|t_rp#6d?~qTu~4YktUjV( zC9IZPEax5r;k)9f`RNr|%G0v6$7#iVs<;;@#{va>yTD*}=qO|0Wvphg6js^A8T>6I zUzzAcIXkp(S`nqF6vuKpDkyf{UlCol*Cb*(2~m|XTocY8W#P1TX5f+#0d2Nix{Ll3 zHSBSoWCE@cZ#28HL5%v|Vf%mK15}H%uQYyI9H!j;=3h{;QyFi-uxNTK4-Sh?F#F3J(*_<{ zg$H@2*%$)=s(GJ&SQ*w~f`71<1&z(W9vdVIyU2Jos5VqO(4$s2Ny8Z@9(~LmJYv#f zndYTvPXPo0!mUBBQH4MiA4;N>FHUaXN^aMBF6ivd!W7vNnKSP+rZ5t*hs%)8(P!b) z!udv}yqxu1x?}c9-bBzLU2&bZ)%|jjH~uBCu;9J=kg@8H$KHfod!O(;MdzNoWp?7& zQR|bs5m#2kosc_Z&)koPDSYxlF2(>$7xL5q*lK$Xx`BqtF?b~_KskAj}0sh!q17x2Yigf-c3QV8P3ObAq)UQ7EO~rdT zRg$OyP!b^9;rLGmAavYuU?9+O%7|$pOQT%1qmmb&)2Slh(t|h-2#sDXmLkkS6B)(v ztr7TDEMEY?3kBzw>$pRH@u=rW)D$VOQwCZFgS?YtxQ+6O@s@|tPN!JIAyv46yCLoX#ape&27 z1_*!0ia1FYGm5b`k29FONgrEus)}5i2)f9}`!Ja*g9ecI`b&|5#>{lNtomXjBa`ff z>WJ(?tEG$Vw|tTWc8Wc_K^$i^$-J@ms*J4A)C@oFkd}CwW`$)i9Pm*h|I*=7)g&U( zU_{$PV4y*Eg*;4!Tlg~RB2Fy-0JpK6w@hujiR=K>i!NfZH6=vKNywMO9{u?GP{0J^ zK2nok%wA1^i#mMGUFRVf7U_8bMMIuCi3;QbrXn!%NQLgB1ukO0JIA6WR zqJyh~?9soKx>`%&SVdsNp%JT_mRh=8;k!V5Np;p7NGZm06pZeNU5i=vp8I6;v6Kr4 zDRns8O3ZR4d$zB1q&`Z>?C`%e)Ij7eKm%>^CuE4yp=)aT1nIR|GeAa>1hB(U09y=I zLC*pZOlqKJK@FrY0{hme9MVNh+eTye@uONl<22-8{>y6MO~lVl|;%Lm8Vy=2+KqIAaFH2 zmQ4$nXJ*nQ7SI^DLhWN1-wPwm6ifvW`COFYFKZ*;~vUpL%a7W&U=J zZfh?ozgurgGrFYpGT)JG$LJqIuITxYvZ_ab?yv0zTXHF!2uen&wIX3{lD}RJ2=TFc zsDDAQE+L|WoItkRa9585kh>5mZ^%t0k>y6Sog2a3Q12r(x%C`U{JmC^e^m%j^yPEt zkY3fwF$0`Dgs9;`Sm+8x$td9+qZ(4VnJ8f4C?!(Rwv9awD|uF|X~~xm3$y(hE||Uk zU>c7CiqzMr>`tmt^ybMTF&5lscLm*Y7d&8!Z<*A}eenBBI}2brbwEqADcwh83N8JA zFMz%uEV@s3RO4;0#@&JWs6qHTo!236yU;=YJsXn8`CKms2`IU*dtIHHll*^Mq@6i| z!kVC$gQ#|pfSHIUJ%33yB|^3XutNTzb-XHNpUp-h_$&<_;f(MYc@U_N@qeNeg=FM7kCey;xE*I}p6eodRMv z>a@EV@UwheX(7|LL=V?)z^888Q=+W5^$le^dh@f#!NL7eqK6ZMR;&$Pe-s_Cl7mgg z9v~T_mQ^-W6m9Cd$a(9Qm!O%{pd~XAUd4K!qr&64;)>V*mJZz%cu!GQ&opL10xVsQP7`#Qo(rOm~a7*D>v@UH2J|<-+XI4mVkO zZ`e^~Lcm&l5#YlhC|(v!p*yazhd+zgK7@8tZT%@^W6-p*)K`rpW)92!sOGRFOggxO z2MT4&!vCv7Mzw1t(Z|{$`0r!k$Enfd>fShc&)>vYd3mg2M(?CBjW(#bfsXja2!#n} ztrjHf;Qe{fXyq3?{lGZlnrKbHa5X&hj=AJ6$JJn<|I7@qBdDb5ALk78R+Q}mLl4}B z;iP2Ub?nh8;Ly3b7I-3=DF)fY=8N%@hoWC1h}bd9_c8)im7A5Dy^<>p&2p|s0&&<`tneR#9%W4a9E_GNINMImq`Y;6Rnn> zGiq;H|j90QY31u%Ae8YbKijUNuAsTIg3bh&A%T{I*>zX7J zJ~aKVQkr63^n=eAgsxaCe_z+_yr|vG!6aS>?HlA&WPS-;cq~yddynJy{{)Nu#jcBB zBzCd1S>)K~XyHwZ)aO3!%Rrj$|0(CNrV{{XF(P|A9iq!*@X(hX zu>#a&*jYnpZNhOnB6rWp7nR%tFfaS_i5_WRKV>p7Qlel;J7SvK92=9U?2w=}1CR1@ zB91;}tlmm{RRJ>Ym;P5q*f+7|Yu!yhaMO>Ii5ZG|WX30!27Ok>4SW{P@sOgP%8|_@ zpWIfv-?1RE^{;|avGyLkU}}KP4lioVcPd)ZBy8g-f-s=R$YouhxH^)(njug$SMy?eN`8I7%eXxNB=-YZ$uU?8fO1DAoUdbSxJ7&@x4{Ab zMvx&gY;_=EQlbhn3Iiz^2ch9Fj8fgIsn27*pn>XF2U5k-=_)L zoz0VnkStl>f#Dw}+{_?$q@A1T1kPWRM9WxTY7;*=WfUKP!~U3PgNCQcl!}=J_G@cg zr}WZl>^Vje(v$=EXUkSe>@evxuT1qx^~i1nJ=wdMKRj7|9#W%F$iqy4^G7ptnRT9N z)a;Pr67+{!;rND3+d()(SQY!B641%uj<^V%F+{3hIbfS}5C`p>eE6^mVBbYlOSCcW zb8F|YlwJ34HM^AU+9Jj7jJ)VE2&Y<;6X5)-fP^9KN1WSwkmcSsLnZv#VqQLCs8nLy z>WJ9>n1~2b|%X$;|u!^#*^3bqUZZ6(Y&{Y=%_0QiLc=tweGPi6B#mF%g^0V zRfB+!0K1e~kxvD$43AFuRzJ&-h%9n<8kcxec-)r~wMm}6GnD(`o0-gmXvg6z_NmxWiX+yi z2lr(6$m{0%yEK4I@EZ24;4(uyj-*9(S?u0-qyzgXQ&1@xufn|ShT|we-I@uAVEHqJ z@h*l^=2M!P9(W-X*RFjDxrX)g@Gt3<)6?*tQ>|qgPvM-7)Y==f5_~^cXlc8zelmq1 zpBf9>DSbI=)S>%7&V(?sSa{I{gE=yJ@Uys>5!{NY#QkuwvY_o?dX#LiBkoWU|mu%VXh1=7dO_6 z1B~<1NFQ*px|lWt8_Jf#x^zy1<4%kggqwM_IiQnF4yk8qxGj;Q&udx*m|A$Ml@kZ8uNgND54ocbw3{ZRL zsnEl*O9k}==u6k!o|d6=G`ZrFJE2lE`mS0Jnb-%w6mvpf`XPV&6u`I z9S25*IGnaSgmKMY&!P@-n+|b?m*!N+xPn$XO?Iou(zgH2bB2k@rMdPbVj)i>#-sda z42hURk$>CF54=PskD=nlbK|Wjw1_srGb81)*A?ug=|E!!=vfS+%fD0qcC`hfzMe%k zhfy^crjozE{zOste#G!nqopXIQm(&}1vmT4Ej;JCD*JRUlKN4>IhJ(H*i_34W{p(c=!OoOt<& z9$yXKpOa{#6!7grPIWtH%jF?QjC>12&-Oc-`1L;~2L_9-Hk(Hqqha3m+b;itgRTjw zvlOH;!@t7=(=A`sOk017O!fWki8Jh$~~+k4vPrz9COO=nXH5`?hl1Xxn2uBs!QOPY{y%w8(Q? z#`~%5nS2v^{S7uDugs1n>O9RAuLQ0F3J!0LGgvvVj(FAH08|)mh^NSe2NyDBp`eLsdYBF|7wa_q=I*=8rJr_O0dxJu0yM}*HrbGq?{N^t_QshW-{ zZdEr-Ns84tZ$Nh)+kZaj&+_QbD_Q@=JHsr0wg-C2Ix7>{yt2~VkpJ~h4E*s=5nU8F z+{Rn}+D6<~U*h^0xQ6`6zMF*CQ#@L@Vd&KM{0W4g7V=vx|h3xcHC4k0?SIp@%$|N)L zB1G))Ud9eSGK5N;rDRBY26B=P*D185T+ecxjJ#FA zQ9a8nF6W@G$I$RDo~=PAIVZ+$4AV`#{AbI3M#K$CkE3W-r<8{!|FR&g!=R|;+OM6G z&qcdvkXgyRC&*)F+Ko zylgF+^lUVL*VF%YHr#@~s28p*DMsxpd`j8qW^W?OH=` z)0J535e81OT8G@7LC(-_{e?wlmY_E?mM%4sK4@z3Kf5>wcd!8i#oS@@0r-M zm^eNa(>TXvG>T|0rL)}Al?-6$yEPPbx4`Pbf_{>of@d9Ypp{aX)7qm2<;x*__7;Z^ zx0z0}zE5cFqEvyb5tC*Lp2cJ_83GINF`kMF8tTL~rc$at}V!sE*H1RCm4D8=1ugz2$JJ<)UebZ2Zac)7CDnRj#PCisxo2l; z{J*YL>1R&_MmY!N4x;NDk@$miSC*S0^zAy%fh!Fv_oSBk0gLLbm?N6J#&2<5db%b7 z@Jr8g=c0RdSk(G+sQQa&v`+}9$CR&`%6D_hSNm-uTC@2Fs9C!d`sPm z&++{d1IG7DoH+Aj7rUPD9>s)aPmp#ssWz?V!U=hw1S7$pEjcq zA?{gAR-QSt<9xqJPi%;Z@$R)+tVVbcZFz4%z?(drjtdSC1>FnJ%%a!DpXB^CDzv|6Q1m7VuN<#H!g0pSm1c~vH-t6+S@gxNQuZ5iVs;Qi zL01D1V@gk>uwrN+ylOkzXj43nyxg9uC>L75%ePTS`30 zIk=S#q5vm7qN{0Vc$H_n6qQpDxd<20T39-1kj-9VA>=0+tEG%I9^aeia+oPi8n6Mm zlw@o~k-g#qK5TU#Ve0x@qyJ3kTCIWE(D}?3s_ZzkH>mF>6kHcT-k7YdCepAM;mZyY z1{aNYOWxZ-Q2t(~n?d)c`_qm|FHj8QETFReFe&M@{36Sy0*l>%2k$Q=U33*XoXSeF zUBqn~l`tgnoi0wF>2~*vd&wUmlq>omw|!70DxmhOH=>&UhrD|y-d>GgKMqh()c3%_ zc)d!E-W!=p$nYW;vLNIk8+?`e;_so~XJ8iu)u2B*@@9Wa!H0RrCWc&syV&Hdb9Ls0 zsH1U}uj5k9nD6>QSOiF#^9yjExK&SQxh0^`0MimVh7I^XmZFU7VOi%%~isBP8)(=ZLoLfX!&g|H^u97?Wumw zV>w1b$eeK_Iza*;Ke#~jC zQY+Xl+X-;MOZ(z_xdz?h>xA7N8_WQHF>GurbYsbuhvJ^Nv=2H^*vkFA4lc!GQ&^*N z?gK~CLU;#uGNN15kF(!%2M0uM@>Z^i0?ODrtR&2TuXsp=2b*OtpROSZnbnBN&KW-m z2RktCqmsK7!HIFaPo{Z=4w)+<@@Z3qr}ADMTT{()9dGJN8wGE7nr-4;#+gNRkutf~ zqd;}Bf_gQl4C$j?7;~39{oX48=5U&H!>G(HA0*z3D%u(}8M$c)lDuu%hh^LN#K?)S zC-mFyC~*Ths{hF<6kWx7q$93SOdXG}51Z?0pp?#SP`Ot0JjzT_>Y6I?SIbtrOR3;EM5Mk-ok{`jaw>Vr zCqm_pJJ#=?A4n}|mBdE}Mv2qtgEI{Ugg?|e>l!xwK9#cV&COXuZMakbW5W}!6W=MW=M!exf`$@#GBB&#{homS*lGF{e!ss#htt2XOWms zz|q@Qas(Z~IhL&Up!p?kNQVZT#yEWgEO$HFoxwP8TeA)@IRJ}fCBlnAjWq^&HnYs5 zcF0ELa8Sa*xQ#K7h8zrykoiff^$5ui`HRMST3*M_E7g;sHQ>c1xuav}ra59%g>Ox@ z?o|bZ^ylpMksxtCOlm{v1r#6^C z)P|p*XE56}8LujVl-+2`SV9&QH+YHm6j5`sEkn6oMqQ4qR!E!19$j&-(>IibhYJM3 z6NeEW|77unC>Bi@5SB?!A|YDj6Sh8|y8+2f0husXxLh|l#Cq^V6GJv%v7#%b$Bo{P z72YOGM~q{!tmDu__2bN+v|1-aN6WKti(_9(co*f4#Mvx#-W>h&w1d<>j!2vVUs7WY zUY#o0H}lWWf4YPh1I-%ZxN~T$VPL2r_YZOVu9<2$il!TmJz|VO;ec2Za9c5!p_m4# z^dWBZUv;shONdBrAijihoi5d}Td+XOqrUURilbmd+*k0tAzsR!Lvmxsm~2>-8|3{N zr-1QTHL?$cyfhrsL?+&dsvIgZDSD5uFH|}orf_r+T=LHeZ?BAnV#G&kr+^~a^alH4 zW*vAD;8-$~M^HN(Udyl0fv``EgleCtrs5|hgF(M!{p*Y30mtGDf>w*OqV~_EnUhl# zWuj#+SZV0Q40s97bYL1OS=`6x)0X3FKh?xx${3s%Q=5>#y&i5IT+C_)uNfGib0BT> zM6TfYcy!_xryVN@<>)r656j1%?K|a*qmk*ey^ipr^G{b#)kVEWA;4?JI~P*67`|-P z*y0D->EI#p4E=*^m!;LquENj>*;B=67vV9YNfBME@Hd9v6=y#MHu|XlOLV?Y{(EGx z*1J7E!1M}Td`)Lkm!b#1p>hEB7U_=3dKr1 z8X6ra~K66PwUGk7ysNzZ(` z>@*OcIYGyU%r)f8n76>tKcepfZL^Wtf{FiL3KEtoQ%r9%V>sqnM=E#tQ41>EMRWNXI(}hbVnh{p%R9orRzF z%cS|#`MY0v?p=L5{=cPJTysVTxGC%F_XKw*3n{TJz?dMbqL<@%!S^RLEij=>5oP_s z(08_9P#>1zllZMdNws3#>|E3$6yv;`mbBi{!6uJ(Q04Pgm($M^?YRwY^8Q4QAMRD` zM4h&SE<%)3irANK-%KF72i!0|^5G*00QarmN&<@w?b;ogPTkA!gtnUJURkpE>a<1d zHWhz>GT=-CoFJzH$4}>+9jM{4<5#-^C4S?5c(v!P(!2h|62AE?4~gW|;vuxZ`eWnD z2kU7u4wbP0PTrbCdW>&_xO{(!tGjiW+b|+-OIa-N4GB#w4x2nu51s34@+O8*=$??f z+YUe<65!r>1Qa}LnDUB-Tm_D@X6rU6!TlN9RA9z~2-4;e5h>_CG+hT&Xi1o(c_*5dMX4F%XUrgFM!S7Kl>>pdx`UT%ObBvP0SED=J63B z^A`QHabD;y%imdpJsn~7w>Jjy2{SJoBh*Ud`KwuIiAFP50N`KmHh8h3!7R%!u29z! zEPjj#9~)#h?Yf5aBK{qEKwn58cfaRwsrG4<%dECeZAo9A(fkek91T?%R<>M8sF&|D5$aFxRF$v4Jy!d zRAWpyqEW&th@$HD`vdV4=Zkm>3+dT~)j!$qmce`$o?22`ZzZ(d4L|^j@^%xRt(sT~ zfxdqKW3NFzXvX$42HjaDy7+Z+hu(Y~B`vUJPU(i?po3cVlWS*3jL9|}Mod$38eP&}w}$CghO%$o%TZbG zTwE9o`K$=ncH`MD%KVWwnwqzhl}@qDJR4qV5`5t)yH$esO>dvi!*_MO&ikdNhrvmO zVNM0o8h`sNua6&AD(T}hkuj1vuZ3t7^z}~26&#}plR3I0^p6A^2= z$$avol~K_eQto{+d;3m~`xq{Vx>fFfSK1N%OQt@l0AM#Tr^@}^JR}P!rA(=(poWfT zQp!T|-)r;hH|&h_nCViQEz%w zDL^2-z6l%o7XTx1E|BcvP6UqFhe57n;0y|}DTU`BgZS z=^u+`N;b|h+p~L*hc>a|t-#vayXn)H$aqsPLZ6tID>4Vp8Cm~GvN1&+s5p2_b|ovdYWFWv;gT)AmcK;`AC zHT&SGiwsWhBKmKW|Y?n=2`cY5N!z5#I9#-kyYN9ag zAG{V4G<(YTnZ=+FZ>aI{3T??Q7nBw0F+^0pAn4+PrJ;oQ-LL=^%9wVs#7@vM8)$ak z-;I&+bFunF4ht*biq0nIDadNrWTzlR1$56$k$ZQkQ{zmv{u|5+Vi5uP9SRs4{Y8tK zq9A#=;(j7YNcRLsr5p!Hjxc^76F|BDu4k10X_r)U;qFJmL~2N@(-Q6 z)xm8%kLBm}FL8>8_22D3;(yNnef`AwOaBj*o{#^3_j&HK<=?4)w*K?}TmL8d5C8vA zzr6pu{{#LX;BV(2_MhKBdwzg_Qa{%JZ2u$wd;M4c|NsAgf6Ra2|0V8U`#1RS_@Cpy z*n9&1YyEHi&-b6-50db@|8hMgE`f|L9Nq zcljUs{@_2)|F{3J{OS2>|5^Ws{J;Aj{(ta3$G@fjjsL&?OZ~t9-}gUYpUnTTf5-ix z|L^|8|NsC0zz_OA{QZDG-hc7;Oa1}-+wNc2-{;+!e@OgV{mS((@UMq{Vf|zLSM?9- zzwp1y`}Tgh`HT5S>TAvaU;g3!>-QJjKjA-5zJL9X{GayE?4S03&wN$=U-EzZm+m*V zzmR@e{crLk`d{u}*e~H<#DAOrR{d}OrT=IAx66;9|7`r5{;J%hM16pN8UAJc`}=43Pxjy6fBpZ!{4D)L{CD?n z^1t#w#DD+)eehHAU+912KfZsE|AYP8|L^>N&>!O8$^Wll`#2W}F18AppT0{R=pMXmFi#7Au7Fk97F%QK+eZQ1z+??*v+?Af99tz~ve; z%b{KMds^p#5MP7aZcy9&aVxXkn8Q%ygKUARtu2s)L~)$qj;!&4W|*&BH2Tg}o*nb* zoA$FTAsQK@8bq_AtQ%9s6We=KthO5Z<0G+WdDekvlEblb_R#vbq1WY3z<0-JAtF_t z&F2%kV^g-Bo(=fy+|1?eN<#^ocBU{%LQ-ADVG{tWo zB9P>w%1%lHiFZPExq4SI(h>bOS6Y9a{!VyQsp3a?gL>OPrk%L?RM{Xm7OPkZuqYE=A(28 zB}rD4PiYGMV$Uu4t|wg4$<-BpjA7$v!H2ykuCpD|@&AN(5@W+T;||%=Ke}w$1A=Gv z%;&ued#czNmLH=BLMa3{7aLsvJBWYJwHKN*wZ*8W3ON_=5XUp@>!TZ6T14oNgEWBB zGnWO+AT_dkuHt@P75r{!U<#*)*~ySB((R9Tj~!lR@1-c(8K*~@ zZHcwan&Ac&A4J_7AsZP)^VOBDfNY0++pjG`If`Q<4RDvos5e$xf}oPJH>vnyhqeR{ zce8hTf5#Sf-4VRFA~7D_377 z>7+>XBuXc|NhiBpSYT&m%7j&Mq`Y6S=MnSefY=+wI{emt{2fmbrX9S@?kB-01lwjve=0yyA^~t%I}QN8tiZhpF;;j>VeD~+){b;Ay$@wyU$=I zyp`&e8?xv+Q?fx%<1|LXw{llxI4@7Qcz7UeGT&N`C<^qYf9IlQQV;kbr@J$0%IL}) zOStGF6c`oWJ^(4mpfp-3T5nZwg%T{h;+$`nJO4ot32}Ml>0deysJBr;vgAh2P6q?T zO2P=2T_AWTbF*i38$`Fn+-Iwde^%3#4pd}2GA~sIFKC&5T13uZUk0MNn^ASW7~(D; zN|0>iBLeOrtbJl^Y%Ml~d<^r}42L^_mCk%>iqYe&>RpubrYnLrSzP?3P{Tg6IK%SI z<I{z0J73SP-F zAtM%f<;sN3@uDoe3VgcIgiP^`XU|Y7R2u=7$zvjx!qLdqxo{vM@_h^-oiD*LMSbtZ z(vtV2yud^E4pki%=}JflqfXGKW@iWsa8J{Pyzx(j6fy*X#^a?GRpoN2al&}ioY*jZcAHdd-*-zvCr_1X(qQTS9i2n|h{7^L|P1Yv&VR}(tl zlU7s_i6egk`px>{svaJ_mF)HON!XIVYx^Lxb%7!(PS=}}#<9#&0+?>tdZJOFvm%3? z#<@_9N}OqSUU{!4^)Kc`v;KsYIre>CP;$@!0RI2?*Cu}OQ3@NtVg4zE(aU?lL)p`R zkrae^fJj;)7&k&iH?FuMX5f3-REicdzk6|}<4F=W^pcN=zQes<&U0$WSCY}0zPA1O zD6Ac)g>y=^F6mn)il<_qa8dXlf`CwQkK0_?mXJ;D+*5BSr0u24e;oDL_I_txMz1EH zoh8Z8&S9NLxT^`Y@;UI$XM`LHa$DGI7M)S%T?L5e1RJy_Vti+R~55b*Jj9F|w_KZ88 za~sxo->S~)VZ25dkMa_^Z*_z!m8DcmE0-kT+5Wo!b+qBbrMNJ6^=S581?LDEmFT*AJI}p$#hV<|Qpk>&e4b;|jH?w>P+p>g zrw0?OTX3<(XM$569t3C;zy9BS6F;s$L}Etj?$#WQ*MCfl&XB&9iyDocpl*oksz?`u z08POjS)8O*3!SoDIa0Xjz;9dZqvVgWk zsSu4f=XpJ1RxeKnV~yA@y01ZoT@`jvC4Z>t`srt(v(3@eSwIPbd_aoi!~X9D@ihrV zDjr23#_3=a8tHj&-703|KC1eFNic<-y;lo4aol1b<}`4%uOF=7h3^yLsX^*@2Xq0J z$Y0L{5R_{GN{YL->q-nxIRqvFgg$Rjddx6osS%7(LBp@$s?bjtgDejD%U3!Kx-`J) zRKy^Zi?%8K`=cNZ*{58@to7x*di+Dz7k;ZPU6Q7XHn<9`!DN6UBQebguBf`QXn`h^YSWnSw$=hOqkSqL{d-RfPvFbE2=pD)+@wuOk*kJhEAO#*P(`?cait zO!a`_u?m(t26gXFVLa03R~r=$JK@{>BIdFdIvcIee2VMLQ3Z0iyO9q4jOYuxdF;_Q3qfnhfya`tCBe4~ z2;q{_aDp6fV{u8WdLfUGTwypZp)#P0L}=Q);p*qHc7VH z8iM{pauI!xT|!TPihVBvT|x1_&FuN}aZnO#m&goB@DMffDnchSwqL6S7W~A8X}#&6!({nwBWAD?hN(c%C=uLMcXu^pN|q z=d*>0=YY~8Q;HIWh^&-6DpWz@?Vi-h8}--SQ>M0@7nec-=wJM#8#m{O+{VU8?>BYV zx}3kh0gxit8nen9l2`AK*c67{L1Jg-03cxSl~?*W3>hWOSc){PF2yt9lb?;QWhJp7L8{g4=`} z>ZnPsEE&QyVJ3o~-+J@Tm_UwIptoFB9+UCyJ{!kvNvt$k`^K2qIR!=p)=e;amWAKM;RGBj@TIR# zTYWKl{*emjC{4W&{9r^MrVp$men~BC61re0gPOz6DCDO6C?3WepxZ>y2jkSR zM8QS@urSLl5J~JPoyG{BR)z|Jrn5a+M(GargNXFQ+P4#WNe#Zh3)zG$ zXldNw9Lt&Syf`IA_!eOwfzTxB`e|3qD!ZCUnB|nAOyQ+i$_vOh?$wek_klnB3sa?e zz5`B!8&PoNP9sN3!z_GO69Wj%K6na!idPq(6TnrkQUCI>=?2A&sfJA|Ga8gpJVZg{ zWn3%O6`@g!eo|X<1qNr43{kA$8fZ2NVLU2AmdlX(R(v5|`m{GbUEU?e zcK`{I#3TJLV=vkrKkPbO%_g6ewL<`+`O{uTXM-Me(HH2`cH#s?rrUF0T-QwmYmOzs z?I;w9v7|S~pPP@r>(6_~AYoZjo7^pxaGKAPH6e|pWmp*SFSHY5a9Q-ho$t>A+DPqd zGCmL|Z?O*_BuMxiML*-(PZf4q@9@o4p;k^T5|Rr2Y_E@`txTcvD^^K)D=)zeXunsZ z;lIiuG@FSRF@D(2*&@AyE4@dVVj@r~vKnvaY4z5e23UP>cVE}am`KF44)-0->rZi` zD@10Z>F8Net(YS`@p$9(-PDbE%}Pn)Q(DYF>+Zo$%@*13JaJ7C^14xiadzIvzjx`f z=qLNh55Z0pD9uZhzmAC+woj)t-EZnSn(lH7aDFKl^X$;)Y8CnYfYd4D)0dYULiE{s z0zW4%?`{SU#y?XkLY+9)Jz z*Ui$QOY^tBS75^g46gKaY9`*LRFWOxq8K7V#{TI;IhGXmQl(?Jn2%-B5+5i&CijA_ zubx%pOR7BQ1BCT#(HpUDVn65SberI{xik~s!y1DOFp#U&x&+e&F*fcqmzc9enqR89 z{Tf!A$+em6;>AB(tu-qiz>ZDD&y+X;6I_%=)w?&`)kO5k`hDX!6%@;9No5Dogj0Hl zOCQ+I(WHBbTARKV)k*6OJbI(y0)CE zkEIRFbDBo_FG`3Eo6Spk~7=vDbDxHkz0xs1kP4W^X4jdn;*^>blm=*%M<6)OHJ-lU~} zW$mHEf?Y)%{hJ_4rVp8GohY?6*dFOz0Z4T1$FQO4=z)bkNi>-ae>E}G3JZ{3BC4HN z7ZV_(CV%wYT(Sxd26bChx}!X{byrSiy^U#$gXSc{FI0pRWXpTAfpVGp7Cx#i8LLtN z2ysq3D_*WnQ6?rO^|W+6{^JAr?v+IgWL5e;-?vrucK3%TtqsV%i!pQi!*s1N_AV1a zay!o*5BhPTf2Y7Om+Ber!WTa^M2Bht>c+M^QvenSbSK3jZv3jxK!7*~2oRvv&fYNM ztYj_5msZ02ZRjzrT2yR3s@^QjOWhZY@&TS%OrGfXxkBRmN zz!fdbtdYc}qS@}&4Ii{76sRnw=UJj@2aC`Nq?lV9)=Zrt8t_B#hL?xZ`7O3Sz#DB3 zE)Bv^8i#)n2%L>~++)tXYZCoggoPB&_3Y8~&*icU$;w?~(*R5>Bsd$7#UNT~ATR@Y zoP#S}EvKd8EW0@62-zs(ukw}CEJdVt02MI))}9vtCSTpQ{kHj77*eM7e8Pg7|Nqtc z!kN{>2Q{0T2|F>Q%|DbNCY*bJfeNh!Rb{EG8LpaNC8@Pu22z|N@dkObmV5Zc2ReXk z35{)qDqOe=K;--Umkj4`$2;Mep^nHF$l6G+ws;I!usMhIEUBjM`<$?{NhEs_Br_|v z+zz3QXBK;Jesp4ND@=N{e>>=0%=99_&_zPUh!ty2?*>Im7-%E;6D9AAPI-^{F9kyGj?O+;o-WvmLm>m1AS>CeEvW?H$3l zGAsB)^-h^VCv(y~a<0&{mu*$;9PH7ts`mG*2cV@I84OZAr)BKcpA{xwSimvTLY zJKokK6snU}N3Rqke|@ZcSPu5D?MQHYkhO~&Cv9FskxV};QT;?xjTW#bo4_7QNt2~o zBU-Sf!1$A-r!3tP+eRO56ia^~6nOrr`hB(|TK@?H#*Q;}9G=FDVgh6pLvtVXmPUpZ z=rKs~zuY9C631*zd@p4SAfV2Z|EJ_T%A$*StwHHee?MAPm#Pt_Oo#e0uFg%Xq|+H3 zeX_ohV3LE-thxWNMX_v9{o9|}+g=ZuJ?(VjJT-+d*5h7_VZru4^GLMV<8axxm#2^a z{EWoZ?-Y`8|B3UGHo&Vxl440;zYC_rC)wOmj-CU$TnVczZzX>Z*9gmpwYG}WdJz=< zokcL2hhJD%D{pI{20wZn;Wbyt_q` z=f12GVMWBq11ij42&7(+EV<)rai-!5m!hOKPjeV z9_W;%vu^6&C1yp&0yS}BhWt>DwP6?bKONaknlLE*Mvgoo+EiM{H zsHk4-zQyTv=%Do-lYx0(=q+1{sVH?;2x3PDg{(F}kXb0Fn+nk|O9U7ut8(y5uX&Z? zCd@U#|5~8HP)3j!r+P7MvwIf2uxWp|A!QJ%r51s-0Nhy`#_{5M#owAoAQN{<9qg?G zw7u2C>dXi`*l%=5dILgYrM_W&$Cv|r4^%bNQRi(NM|Dorgd{L2dIB(jwrOWsh>C-; zofkqJ0uzf51S;$eQte$^#Fi~f$NJdUw2c3Rq*y(AlqJ~^+-B#j0ACgQ-QuY`3SuEO z=ooAxcwHewA4@tF`6r`5l-(fE zT>z>TqTfMdZ_uDhZZ!}$)n8rkIWwRZexf~oaWxIqX%~*s@Byj<&I;aT%?5+VL%xrV zkh$1#K1oJR2uT!7bR7L~nNOEKf%TtAD8e{;>&5ETKYFUQ-)Xyb{Ee6YQnZHNht&`J}R7gG1#$FJQZK9Kv z=+t_#zst<`w~RR*XJj}%sceGPoYQfV$W2&tLj;{Z?JV4p-^h-Pg-Rj8-XM%Ob^1hU zVGT}Ku?pXKqwgpC^qd7FU-rDo2goQEz*EgDqK?wheaVDRJX zLZ5WyE}4geVUss%DSxZyE~;Vxi}xb{AyQESwag_44J!_}z?r~pV+i%;ysrHkW*d3M z1VGw=2ex8`BCD4k;YVOYvWk_G&`w$z+_algvHh)lpmJ7+_Sjx!H*BW%^?|(W0R3~c z;#+VylWo(5MRMR_^qB6jFotDL>QexyDii1 zgXyg409qDmm3men&7LBot5(ny_X#$W)`xr2*P`h=+?z=`H`e9x`KeLNz8Wj>LK&Y# zDgVDGR|YgZy-VO#p2r;eOgtQ6Ybxi5HxJk99gJ|8SVNyhoFsT${8UVXsAH`aL8w^_ zyLt8CYyd927aS#T@XRxEj}gwsC?Z4)(E*CfWA2e#>aivAi`=oUo6C^_?51d^tb1qD zda!dh8<1Etx0>zNriJP>2ysB&SI`euUFO{C}ly7xcv< z@#hq2q7z85h5ons%kNj-e~(q-v}#s1uUiRz^WS!WbP>(^CpP4Yrv_ASSKB=e z$XW1e!dl>g2>}K+bB&uicGT6+sS5tkAaX3AraR1|z$>c>!;4N~UOP}y0K6p2-D1xM zFS4iij&X}~mtf`;ETfsYBQ_dN-G|WjT60)Dzve-pw>sN5IiUK^^nQO@varey}_ z-yt2csD7hP9T&5FXOypeB2=?rvF_OlFfPG{1W)VGVyVq+SfSb+L%EOcI6CTyXc^m2 zsMpdGQ}BmwWe6FBDq$lZf-;)KUX=@TGSs3Vc>_>zEa@9$ln2O}o9qx9_Uf=o2@h)S2`YX{m3+EGk#0l;sW}@+6aevL!9UE0lH1!QmCnW^@qblk`eD5+!+=+HVT!YQ`N1Dd6fs`nZ zUIi$=L(g9|3NqG<=WLOY_nsp7jJdt99*e`{1-WFleKfss*@R`88pCLb5KFU)X%*S9 zF=fL$c4^)*=_}~opdwNtzP4so)c-LBsP~AVYAlgi?~-)`AOiR>$2=hcQtJ_l-J*q1 ztqNT=v~>32jvvcy=W$t!h}kU@f+}Sb(@3{i{I#Cc>KG8+K&pO#xp@0iZ z=O&OIpCerj_x7r_WFpIX*EnWf|5*h_;c13(&k4Evv1grFEAOgNDA9EVYb4}ULD2G* zXCWh!QrDAZ?@neJ_*RebD^jSkn|S@&YF(U9vX2C$iMVblV`0CzaRicYPqZC&+9ukk zdw&u$WZ*=gEsc@UT}$!IVuesNC&TG<6Yt%>#_YmdT9SgDLF?xI-cuCL^dhI#;q`aJ z)Teh?xGmN)h#M{6l)W>?CE+Ud=6awJM^1MmncWkOoL?*9Yd ze|Oo@ZiC8V-%CV-MG=V4cf!z4dwS{mIDzxAHo`wS9T?r>$HGu>`StKwq)*_g7YLC^ z?#O(7(j_pGfr{Q)E!o4k)IO!qD3g8CkDj~E6pt!m8DXBE#4tQ>0IcdMjRLL_9S@VB z2ZQi7ZLGa-utq8~1m`7%5-;-VemS7xW7T^q&rHvrNhD$i#rf1O8AMc}6vl*4M~5sp zs0DS^D)Ym3e?5V-3#tjtDXWYB~tW&p1Sq%c(O_s)a1OrsxY8P~Re7Wyh{_Iy!0wL+>php{e0i7@An$zNY7H^H%&&!!j8 zIO8Tz7>ia_2cJrFj&NwdOksy(4pF<|R2!6=3vNQ6nE>+bdw;n>Bw=)> zGFT7U{Zf#MVhu~0sAJ@Z{&@u&%=?+a3a?hl@%TNRlY|b1ofcCW{!B?%0P}!ZKummy zmT>(SI42gu96S*zYHE^W9XsNQc6P$f9J6`=E2HiJh|<UN}-Fm9RY#CGh zlvV8&&3!_)CLb5DX?Py?Rg`;~X6#^M9zmG!OUry1A#tA(D+X}mLlFJRDmgl}CbDOL zJD=ywF47x*I3*#5S4(G{mK)oYBoUuIp?*gHvTfeJ+lv|n!Qwzv9&`58(CWS_$qV&A zz~`AeNC<&x?VV`>05~j7B4_Q!n9whBBSjO+&m!4lyAv8MMza9naO0=i{aPpjg-%sJ zV2iHKD^&IvbBPj(<1AANXC&z4k*vN(dX*9pWT35U0So~YC@?>q6_3nv4u@!f6iMpz z?{Fa6OsbI=2APVUJTbph!yNXSMA?q@>=Y`k&(X4%TSgjPX5I@p1#On$&L-ec0ZScO zp&*RPVj4&nPeZzK;+^9|GR65s<_qaa(eDzu*81p#gt8m5` z*+)sd49WY^jx2NsY0wk+2v0(m#t^cX8x{RQUkv5qUZX>X*q;Nfic58tIK5;yzL#suuv2a3| zvRO&%IM72aA*i2z%(G18(87Kn87Dubmxnjr%_dEd?2HxUJJsbJ$*{a@;CKsC6P6Z) zMm*t%eN##9@$)aUB=x})yLFjh<{?)tJN%8N*A74;R4}(gi)#vepWxGrJ`54>$QoxIo!7IViAUFa7tiz*VePN-o zNBog{u|>MREWcBEUjdeLI~CKp%0?ge^O|})RuCnF8IBbQ>H*x*KQQc8q8_74rhyFU zftTI8Bv)0`CHgynqQTu_mq?_$DHMJjDL9p3oI%B%F^X4FbLdF0OU&@1&dW{{wbCvH zOYsGk1@=91%IP%5fyH8o>43OjYm6+#5swRg?i;w4k!~p;o^Q3%>=3Ya>~DW9#8J(M zE{ulDW#ZW^!caHE7^D4!Pk+UHvXweH{JEvU(!*SajzN^v=%nM_54V0nI&AFg=wpP_ zz&q;t7WJjYADEUaQj&uU)v{VH6tY5&DT0ftd>yRLJePQu@8e?Q!bX)GjVK~m5ekMZ9g0}Kc z{Jx$Cr3PVfD?UhcP0|&xl_vJJuhDKhRg z;bj7C14Uf#ZlhI+Piw%Cfx?CQ*+2;xycP_N`luO3X$*!ADo7*Q~nl&ydHr19g{7#NJ z#B52pI1WJ*Tk%Q&giY@>ti=d{aCG$Vhh#Hw(V8mstBHjj^_-kKU&~R1q6%dIYgy5I zg5bo~ahfh9{Tq}P^)J?y3a12b!p*t|lo+W1?KZ2V`Nzm6^hS~Kv%CxQ-XCsU>@6U1$ z<&X+8Mmz$6Qh1Q*5}8hVNUEoQ*ITTC4F_h8mFir_|K%?fb`%YFj<6&baknC($!BQ2 zU|h+En{-P@ zC8S49sw^hy7ex6!Z`0`50{%`(-(_0^IhWzGkk`#ZSgG6N*^6@J9wjzwrm!1J7)_lt zDNF?jKZZ=k5jENS35{M%3HbUF`FbF-w65!)Qvxz~4nPH4Yro+^L#(q+e#D>^8Ijn{&2qyG!JzKK@%k4H zDN=P+0XCPSRQIuYZ=p&T>s`{$E4ucdgILY!#*$o%Q^ zsBHOn%}<9p&R@+4#tc`xYe*R;whQm6r0C)BzSL+#E*v~ z>&nm^_v%*sc;dvd7)e&lUzva!qIKoUX~dZfy862q^h-LXKcj3W0@rCe?vhlK)sdyK z7De1u*0r=q%{q+u&fAlngsx4V;z&Kf$Gy5x zIYUMh?`Lmaf7i{vO1oV+j4}IhSwpU$AT&_l(X~v{jj6`awqg*02-2uvzWq>>{_=pY zPfmbec3l8Cys6K_vB3AeYjBEr`lrzm;+Pa3iZEgpq2TrnWg1~QQ277*8!ipNDATLB z5(yspXdE^<0Q?GBEJ2;oHt=05CJAja`s`ABF@rpGT&=>0i=ptQWVI^QaDbsf6?ou?T0$T*33Rx37wc;$&9yey1DtAiZThWeYlQU2zDE>qWRkQ@ffBQ;>Ym1s7hv<^G$1s#!q_zbW-p@MN%4P+ zjb$j0Z?y?ThuAXzaiEQidrfNfd1E%x1(_zHIsAg$k05CReLQA9< z6OqGw)O2H|Sfl6}QpCd5Lh9F1pJJuM%M^i?9|*Zfy>gH|2mytB-s5MkM#e6S>>X5O zh#*?B{F*!Q;G8W?;*a@Pz>X#*gJFiGd+1Y^Wt8gR5Rbz2^N;x!$Z82@ROK5(bfzdI zRaA)eD1jmu4w#r78Ft~eeBx!!i;pUy{d`4YmeMfKxZKr(RDHv2IC^5k!@0DnA$YYj zB?5`tNC2S&ka}g5FBaV_%PoxIyDut^c{*xio9*QbPA(mA!+S2!x01GSJ#Sr>-}t(8 zxsTuQ*%+9}>?P9CCl~8-9wXsXtDQnOpf%Dk5=X)9yIRnXt)ht|5ZMBzVt_hJkJt0B z`gJ^5+LhQX-X$A>@CSHCyYDuZWimN#*z@TY4RwV8`7<)<+s8tT!JPA>r++589{=h) zoIOZ(ZtRM)S(-Bj=qi{BjEakhk&OAsc_ouZXIqXV-=ZWw-$h>ryuD7PHY%lFHdTdI~MN>Hl88_ib}9fpRFJB zt)NeK)>)Pcw+E>}>fWhPJ3=ED)thD}+OfzQ z8U-O8OffcPRnO9-qLSNW{PjI!V?TTi&15v|$|b@WSB4sjA0r6se4N=PY77DH9UU=a z5D@6bYL6~v%4;9;IN)jpCw6d38S_+7%LF;6Si=B;?RGGIdbn=Zuc_DUAPPZHxv(vVJ`hNC0|b8CqVr)tlMeRm)Mtjh8As`P ziZ{t)s9>iFbd$q0JDaDc86TQ9jy$y-0-v@fR5%oMEbT@?B`#SttxpH{Y{l*^RoQto z7;2BO#jjteZAqhd+yB^XrOadj19?vN>UP<8qoi!syC#0*9!Xi~ojJhTsCf!I`nlTa zt}W+ydteFKfKg=VDg^7W>PfNaAPGo&KNDB~6$s!0mgNB`y@aWd6y-@9#BK#oC-F%S zp+sNl5cTSZQmf|Nc`}IFUl-*m6}ClzUX!HeZF1QUJo(HzKQI?c!GXjd1U&7-+JDU# zx9Xl9G;hzvt}%A&;i{_b9dMO=C?302fJc-|9gW{=P+FaV6EnY8YoGd*yoZ|cuK#l_ zYad6$O9=I_UN_Bc5aYmsX;FN7+p4xjXybrL?a}kTRx_iH-23L+$J`}~ zpVSpsL4P`Rqn{TvC6U;DbT$?#K0tsaxeS#{|GQf*(|bL+(`v5e%>WW}R<4)%CCq`* zR1LaOdYAbV$sld3ai4i0#-E-d?zH2defU`AnP+N`-`d2;AlV8P=--cI{GqM2A90Km zof1Nl>3x%&K%+mo1Mq_QzvmwOa(+^m*K`CvT&BXo!`zNkW^3jW?$>;+%A1rB(!sM{ zy8y|;R&fO<`_q{j|17^PYTlB>Kwjl>jNW1fds6R$;BR(KLMj7q;vv>dJE%%(IfdZMz<*cW-dNO#!G4!2QT$3*OBr732~ z8AA2S^uo3h*^HHfi=8R5p%?1prZw6P?s2XwsE5G8m!5Ct;C_nIzf6EC|8}^JQ@QRx z4g7aB_E5(cGl;+OhV>YEl1*M%6P^a~EOL>@_AzUK`*;9LsTpc1v>?}ai;L~NIl0kd zp^+W27)#ofl_%Vg2@ObAZ;x>yj+hlPyK6gcOkD`w|I7Iz#MefIYBCN>!lqZRHN%-C zpWnXjft#VtN-iqK5z6q|+t!Tg;`pXxNDCqxenI`TR+Hvtk+pRPFUMyYl8qbD`JGx>#-#0j9U!P}UpwjQfp)#h=oZe3$xlNeiIJv= z65}7V%LF(D`Srx9C>y;|aXm4c_h%!Jqi;-=fPXYr3H|nm8#pAvI39Q^Kf6%|ammQf z2qVLNNGUtcY@m8=eC7Kug%_m%vgsmgG1K69r!*GxW^sKzJHk3TnGe~KZJThrbB}qE z5*|LfM=_Ai;DFegPYvTV0+;f5M?F8rTk}Tb$(NfPRpE&Wy9BR!0^kX+=u|Nb;9HZ(CFU!gtaVRI-S*qrwXx9$rKG zALPPxB!_GZ30>d=FP~|wlc@+Qys0mzsvnwM;jM*xM_W5DX~9FY#KTXXM3*h%^gqaj zcH>*;8At?w0*w_#DSeev_lA?=PylvbqUaP%J`zfy^iH&lg(T%_PVI*LxxYT6qm*$-0j9@BVsp(Pccj7 zPc7e+E?CH%vNZr2@UGtxNz^T=U zE=~+>u?PQGoR9U${F`IwZGD8!&e+GiNgeP|UN6$da*~@!DNLkx@9*GyOk_@Dt={?q zw{9BQg+!=E$Za>yCBY(T|J=0f!6sN2#kAXMoE|uAq5lshr(>CYJlM}rxy)gCnvl8P zCz`adBB9Qsm_JDV_(JUp>zW?&)U}odw|K*dh!o7;wz_h%rE|vG2g*ohiU36BbaMi~fMr6G z25Vxi>`q_blydlwA8viS*Cif~8%qKAn5rNpL~BmzMAz;ibhbQAYVmEx$uJ?&Bl?SGlWoy&>aZRN@ z>@|JCZ8!R3L{hKC3;a6f!8*2>*Ir`%&ng))Ilz(ZdSX*cWOMLYeXk$?+sFkiZjUE= zO%Yo?0>qXpA7jQaUwngvo5XC1l5EmCxxqF)W;uQ&X6sN9{23`Y+A0B{|0D7^z2$B70_)Fw zUb4b|eMs{=b`4)+-{BPVW?F0tZlQ+Phxl1 z3TjWGO8Wmcj-O)u;f3E~xy{K600_m!$bQ|p>%Y!f)}MT{Ts+GLY&9z5G`;iYWEI}= znVQO7udYE6Qv~4Q?V+B*u%&XgViYg;2;bm;d`z~R5Qc&MaCkh|BKXXNFB?4gMSt+S z6Pqh#*4&!E7oXM354j?QvhF?6&aq4fbgBE|{_#|@LY6=Fu-G!Z@*CM@9&SCKC8I0B zY_BP<@VZNOY;2!c;jJl{$@sAb=&HA_-r*UPGVQpH7wo5%@i{<<6VWO_Taz4PL4 z3s9$I0GfrOQgV3Qm(TZW&V~s~%ssuc*}3c$sYHjHXx;SG&M6 z!wg{7-nXGJvTVfr2#DL04-QIlU39Ae#82Fk+3pl^0xejrAbscc%?%zvgi`28di%Gb@9gB3T>$}@=F z+ca2Bp2;#u3g9`|LcIqAEKgu7j8D~+M$mpC23LUaTf<4}+P+Nu@4fbS_cB1sAC_SG zb_QyuHEcfVfiqhFV~O#TZ)Qmn=^j9#1296bA`>sygjw8oDA12 zBao{nPPq|)WVpT}XQQ#`gjBMhqMSo_whOz1wzLRgmH;O}*uSKxn?T(J7){x&{KjGC zedy(bE^2uRFKF5(1A*T#n_t6GS>J#Jb*jl*BIP_bNo`TP)E6g)M_U)6O9(3`7Pf=- zbS2KqM-)skz2ssYLVpdAKP03Z?wS)RpA}_=RTR#M?wI4Fj?Qg^r;o(fiI zlun|}kZ0TgolW#&>RK3bgwtrSYu5eN@Vw39D|OqP)MKYQ*=-~R`)z8x;*v6edU4-) zLhT$!5F@g(GAC{HFYHomByG}DCUDeH;^xD5VcL4H|1?0z4k6>77Plnis$pA6Zhwpm zjgI$!e^_s084OX2l*TNE@VXG9o(-c!>e41yzz9Kh@2oc?x5Eqrwl==WF`1rC zH=h~vf_FI1gQ(OGZTWo--y@`|NaL{RDp;A#&cX--MU$j?w`gocYrwZ1nHSSe%4r&? zh{nJ=Ni}Sq#N4Klr7sT#$rrP8ZbYqb{@m@qeKJFz^2QRY12jqTC^BbdD{i*(Eh9K| zg_L;9KKTAng<-;Ne6p?HJ9+I1LJL_T7;uViL~}>grA7*NemtWk_uOZfuGo=l$jE&} zvI9=*TPDz;x+-Z>!H_&OS+IxfCV@ZCBbGK=RILh3X*vi$*nkRlRHPw4Jr1C_8%DHO zd_f{DNQ5}*%e$UNFOTq6cNll zBH3xKQO2qwL6@;a3U}9FUdz(f69Ka8q4MDqfr0_|kUQYmm>#d& zk3hS~q^dFSJ z?>C-ma?yE&zXDX9-Q)NSJl}NM|5@Cd6;m3CzlmHP;BA`4;*G6u%W5RcxZl5eowd{K zB=6_{$|%pfRlAd^e_XM-iwU!J-hVSQk}2DQXIa{dBzE&)??c6ny@v<~4ky|mW_z8% z#IWStE&2yXkC=x)9#KA+7JUD#Q43Mbg|fWtdw2Sh8l7_vK}AEypEbgLu*_bJ-=BuD z@euqQ0h8H%F8M9-ebxYvM0Zj&PlT`US$BBUE9%dvaJ%y7#5}l9=v5hEMz4bvg7Jj> zY=*9&+<-5$XxGv;P*C~NT6Hwjx@S?ln2ucna?Q&s=)tu8VupooGMo;oz9EsT?E{Zy z;zFm1dn>x#hzto`J7hYAkvI@!O3C_$1I+vw!Y3boZYQDf|GIA1Xqw>Lt3uViN+tu4 zDW0fRPOLOFLN~7@-n12DfH0JlA*l@A6xxa;V)e-yTB|9;`G+u=_Ov>27N2uq68OgI zu|q4v5PC<5$e@yHX(WciI@2vacbxEM!aRbA6#Dk=LF##uf6mpjFA?+7 zr=6#bL%~8sb)*?JXrCH;K8iu%0VTdtwXW1-G6@&w6Y|!{2579a_%j2V7DPT~jC*@G zkSGop25}`o_gm!oXB1{@_`Jb0cMIsVxA;?wK1f8msro74^xBWC%`|+Ri69BHkq_&4 z$5BdsrV*5;`o;n@R2uW})In9XYDd*snB8oaLpTCo-N>)^(}=N1DxR?$6~7_g4`B+x z+NYlTw`{ErkA+#BJjmDCuFHk3FiHh_T8!Nh$6j^nx%Ccywes$@t{d8WtmEXbS|7S) zBd$lt@1Ykg5r@`awmpm;m^{Jr&ht1kdmHqr5K`&k}Im|RvwRBk?y?fb(_CfdQ9CgaBBU4=l80p0K=iIlp+OU5g) zW0H(l<7Q4llRE6i(`3`ZvG|u={;QRa7i&UjKyy5lg^OCmG5>IuJ^m zpjQmV)`!Yk##~N4^9f|JOs00^mn&sH3AmzdOaCs1JnD|@zxCKwMgnD7MIouwo<% z+hHm+WYA;2o7tLz0cU48X6r{JPiK)i308 zcDV@=?C1caLw%$;Oxds&snJlq>itiby4wCQFF7cIvHoegMaUP(V!a*bk zr5j$>Lc#;&Qv~|zXR!CL_S|~{${5~>L8h4hxg&Bf0fu-Jp1nzku&}g_IXGQvz+mW& zkxZOw;<^ot>nUr>B6ZiM9F+yr+s`9^e(pB*9^!rCMJa7d>~=tZ03n`~Jg2V0PF~Kh zdXbfd=Nf4l&RFqUC(12#$FkG!KW)z7$L$RKu_w#_RZ+cR+Q(kE4QHDpe*^v zPilBbtV|=16q+tGlQ=9E8r`#zfA6w3c_PyY_8qzWmAybGIo5-pg5{@{Vfr!v_Y!Yx zPy8X8!0L8$9zJC`HI-N+$@&B1YY|mW@&av?WULU*L$>?jNE=1ED^7PZ#G>-ZtaW9* zv69o0%X~HgO19m^g`{u4RJuDYv-EGhNNo}6KXcbuuzvP0gM}r!Aso;n3f>zyf(V+( zs(iiIvXGU+2Bn9>0(uDcxe$P|L3-|kt<=gNzocNjT1z*V1LlDUM)qY|VD|?JnJ)1g zv@Waxj}%VA!BV*%%c$_7tH*PsBLv3Y_TC6FXTilHSWxDbU`%sO863tLt-HY4tRik!`>S zB_+Np&tF1KIxb+8%fBJDmH^7xf!4qZriUlRzr|Je2>=D*Y7?6C*SUa=XoI5us}?dw?n(eJ4c zCO7olRRR8`#!P#Q%V=C_!u_PqgE%M@ol!9~G5EFqrsBZjC&EYf(lsmHtd6CsOK-@r zkHx=sw%@{BzAKDXeHP4OADv%ln0XzwV?$?hc8e3aL2pf{r&++_!>(_3i+DqE%pR{C zDMMoeG>{Bznmod7PxE=W5!tk9QLsfEV~lXL?%_QflOkjj^fh`%6SUWO+1bRv%8u_X z1ymizPhBCq)uP^G0a&V!c3$Q6EW)opfVZ(y4>(R;>TJul`bkHsU$7$1Fp`+6#M(AzkmP$h|BeyVx=Wm7uHkZ~b)46cNw0MGO+THaZ)GaGCF8Hpj zY#1-Fu{wKfXb12`qyR9aA}`}WY}}Rr1B%?WuoHv8kO19wUHQb)IiYvp11iN6+R@hu z2u})73)~c#sW>;58kj+eU7XgVU?gR0{cKviz%F4S?r9mBHKGDk39{vRSrP3E2nDvc zKp2TAk>p><3i7CK*KUgQT%;rCWVJu2IyC?ZrRf&eyszROMvr4>m(19=l0Xx337@-} z5SM^KcJsb~B1k)%s<;XqtXEMyU6BZa zJP?T{GGD#5j#03mYojh$qaNwt=g?hn_cPYrcj;D*It6^_R0!19r+M=7a~_hOq4&Ot1J|G`?!$-8A(yVswxXDUg-OkU zFnzDo(nozA3lBT--N?n+p$Q=mbXyF)2Lmx*??7wTFDjaGa}h>{eH1XS4w?11S} zN)!78uZWBS@-%!pEUPu|unine72qZkCX;3UHbyVi|J+`@ zj_2ke9Yia8=~Twt6Cmx?F2|zm?S!=+l|K1shXkCRvaER@fA*9a9-)Dmf24Fd8b$_; zJufzpW3od8;mX>hnqldrZZ|y2t+Sdvou^oJ@k~S)%I3&d|F%%j6(<|czN1M!E-4#} z4GkR)ra#pVEnlJO(6D2RiEw@4@GLvar3H@>o)@`p;q0(fy<`B!rupT^uA~KDoY7pS zY!*1v%=5^P-%)%v%yH)TGMtF!=?D`Noub=Vb<`{iKhC~vLb#fS>p3KpmV>$qY%}p{ zmDaS?)p~QY8X)C&a1dX-dzwU`b#8nOWCqBK@zvRAAiTBz6 z&F((~vpPw|Xsvaw)OFfv^5;8?_mZ5fuau}l^JVe&mu8&GGQGQkw&g8eAeOwOLG@8 z({>DG4T+AMiPcV`DSlDZyySP5gqWq>Lz=i}!LwNpA4iRN#eqB(mS(uehn|HM)tPNq zYO$KoF%a`OR>xzD*ra@;jr<@qAsi3k)=w7UvDWo((UYL}3pCll<6aw;Da|Jr%8k`! zV}^Klkh$GvygY`ZlEj=U3__|-!;fgX*4o>3v_rmo#RH@dXIuIvP#7u(vZLC|tK{bE zH#QLYrR;eR`C8&Q12GQnp!{A!_zZnJ?Ne1h`Xz=@zqo31HSKh(Nc#ViY~W^$@nzQ8 z2Tx=){i={cliL&mF~dEKp1$HjeF%q` zkr7?c!wDC{i!}|v1mCSlD3el1eW+Wolw6Z%m#;&8u23ty zbA=BViE<0k@0kK|?a@5#QbxCGOuz|bOTh!Ln6&7(FC(X9pB%oiOQHUPOIr(~bsSoF zVs~Y71$t6Y*=LWCCmj#ud*yrk3(E)OcQgi>Bu($6NrrlH2YTBOduDXR{$_`VsX{rZ zcB|>3cob_Tk<1X2^fL+(d*Gw)eKhT5}H`?}cOZKSC zf|ceHVW=a>b1= zjr*A;@A`%x_HBpEPH9q|O8rRE#kI%@fBqH~b(Zo1_iyFRshfed`=MH)`uBm+yc!jL z%2>ubGjf=2DUJ1-Sb${n)226(z4%q5X8w!%zLS7msIMt}tR2ScU<-5anXPymbL1Nvem&%DSQSNP7k{3Bxe*KG}59uU)ex&1_oB5&tYu$m|O$O zvQu_#9{9b6)*yh2so{D0lkCNAIJEGk19=hq+dIq2iQ%5R{ACGR?l(& z#Vi$-B1ERnKM11-P-oGEaHi_^h=$>!z!cZfqKA*0dx9*bx7D6oheV`WFIxp2z2l=&GaN&ACKm7Z*zITRyFzlP^ux%khvYUs2A=si#V$B!SN9LB?{!g2k+NqJ! zT(g)k>W-Nh-6%V*My-Ejj1vc))@>anQ5P1OSNgVPXs$R_3P;VGcaL60iMt_z$;?$@!MBKuTl)FZcwUk#aC!u7_ zQzgMKE=_?KYOgrF&9K%1y+l@f4{&10(6)?SzKZr-U zPW~zri4baM(u@E04JwCqV^#leO*FVkGA{&E>t-9FB^Gd4jmxex$|d46cnDt7JUZis zNp%r5rWFsea(0Wvfo@CMj)!^FqLNKAoD8>AD_Ywk3afI97GlJK$WmaV$5~|qO}orc zO7P5iWZ`SK&_C3_@ECiEiht`=SppK%ab4si(rpT9WkZt+xx)5PFh(#*@${;zRp>0b zY*K=3DtydpeQQDK-$a*R9R)mQBZ4Gxa=B7&{nX89Hi?VmlnRQOsge&}Wahds$<^ea zxPmHFY=#E{^;hvK<+t4tZQ**KHKC&O3hE(uTD}-ZA)=9J4@v!_xgab_Ya$F-rhn`G zZxlWpLU4^;i3E(-s#8~N)YEl`)whlB@f?+IOloD4tTtWiheEe zLBiNcuf2OFgwGehdt)`+HBMXnmnpu>#l8+TZz#+*W8tEy4b^HK=h`YS&b|a5QuxVr zQ8%OQ_h|T<+7SEVzi59_h6$OdByV+ZF=L+F;>Dr4BzAexKQFiuQa5pkNOC_CmIZ13 zcy@uqm`}DcK9q}QN|*jIZW>b5D7^+3}El8#g)7=;nEefdx@e@Ydv^t)A&QVXbj^zf zynAks(?(}UCdrrJK0Eo-nB|yzv(}tIu$^epH+CI-J;}uVzi*7<1I-vPh|+8`$>0Q5 z+B^rR5smgr+!J6xeTljnVdliW41pj9{17 zodZXn#x*bT2g>G#+05qKm!lIg65kxqw}hiIP4Bg&UIJ{fc33@@vwA#P{_%|?hiJ@d zF%B%b2dcJ?)BjH}{{0CHWs^F6mi7A45tPwaiC_Bx;8TImLTX`Hn#xGyI@HFfb339v zZmtSANK9$9VWWwC&5y9aZuNv)WO#29S3=Eh?!<(ykWtDoaQg&RAD+r_=%=bZo_D|T`2L(HS zLK$Zsxd5Ks+G{rH2j*r`FFe$me_{p=MhO~*`yjr*?CVKlZls6<3nM)LEqXcO#QgnT zoYoBoDUZyieuL^+Gb7@y3pm$>>=_A`>w1>4E>uYFz_*FXC+?|ViN;x~?6E&t`d%J%8FN!lBMu> zhCK3_#MLmfNQ6DdTdb8*7TYlAiL6q7S(RR#dU|tmBrW=*m53H>$r5s=aT^WB&KQFf+K3Ehh7Svlafiqc z0t7$)e6*p}Yo;MAW{a#+$&@}>^O9CfV$1egd_XK-T3)eqKY)bB$Eniix~iDV+Eqql zkq;-WZ5(Hehg_hJGL$xiObb=&T`R;-U$?(~xMEa+%|v^gCc~xAE=?Uvt355*$Qy_M zhtSJgz{K&H&%(Z)g)1W?@XEJ%RqXh(vu*ojT0WxnHPm`G>Tv87+*ZJkvg~x;V6ohT z!kof}MuF4U&*I(0F@pM#)ASU6i#q5w zf`*b23#T)AplP2kLg0dB ztuRC=Zeu^;l!x0dMeQ+e=TwXATqc@8yx1;fV4*ZQ2WrHum1?^4_f%nanZYgaS=RW! z!78XDZ`0Ow$k+1=62s54K@M~gHp@e@EghRRs9zc?UJjdoa$^@%HOSoqbx%>q8+N{o z!URDZwwiZ6OcbxvUx6K1GFebGD~Cmfy|dX#a>nl_8FBo~YXi)~i4M=knIEwmOV!8` z4w?M(k0bvL75kNLAKe#8~-%ylm;FwoTk+q(TUzwZnE3jdDlHy8KaKv1--zw7n>AsXH=P^d%nf2=vAE zMX9dKf4n3e!+yU?YUjEqw!UY|-+)gs^S%?Ein8xcKvr7BAY@&8IJ&5x^iHJS zJ$te|f^HTpVKkkM1K$y-P?z)=t(ezqQnUjxz88%e&ju7@iRO{WYy4oF$0%tK=I*zh z>H+HuRzUEI&~J~q5!eQZs-*qnguKjdIo50iJ|hYt4Ap_zJ_knjho&>#ZP`nM#jLU6 zVb{^g66~mA!xpuWVAZE`Zv>M7ZkL_za;3U6TonH;4@=wFIK(njcwkbx!%__44mOkb z@)OX%gT5M|o~bLRdg6dQxgN@eKJU&uso6QIK;4gtkWkYlemGsSLQ5mH&H)cBu?~@h ztMz&t5i*De%}lp`YNB_AO>}_nt!$!33Gf9UQxp||K8O$IDJQ@H*U&$A*?6>{6`rxx z19+|?dpH`dv_cK=oxNoOnO!#;X)bP!CJ~BNdH^|lz*vd(D*_YgWDab)e8d@PkEzui zJ-TKp{d!ZLEsC5ZY^EicMKs%QMS+Uh+2@{$pT)nAXfV!8LrR{a|F0q6md?o+3&FL> z=c{Le2HZBqrs-LBQvv!3owstcXeT_zSnrEs*Ut>O!Qw=@?HJ1DvXg2#hT~1dW^|jn z-KAI`5L4w_A9)VO zIWYFud9;FPz<+bzj%o1TCR7URUHma$*tX8kY)TtWG4^5lY8zs)B%J4?2BA3bMBD!g zKveR4|F`Wfsd{T74o3m58pGhRp4;fe_(ra+T_aS$e}wPE7xUp0>x7m^a2kvED&o{ zN{LCL-|YQCV07kZt~pv*<)SvbrRL4OO$;(|+=Pp)NLYsa#Wt0bRCR*LFQ-FXbcRZf(FNv}% zds{Jv!Smrt9@ukSezksKZ@tZS621>UJ}q^6chPiRXW!SQpGoIn({EV`3J&!k*4$h< z&y-4tZKQ`ZzINe3^?jxgD$1k8)fEwI?elr2SzXb;oGpqBNxy z_gH_u4s7qQZRI{}5?WSX(`_ON$aYizSAX)?!y84g(nREuk8~0B@9}Zod=uuKUy@XE zx1$(L>1KN_Dr54}xRV1B2c68~3jU&M^NEyx+$r?k})6cWIIRmja&saeM1w zp_MUh{&WtXS%@yqJKRx@{J%z_F)X}l+R(u4h7I>@ zQ(5y0Hpv`+;CmpdRl|&$!2!O-jD@;}e5)4M0 zXG#KgpuE%#U(3f2QRY(#tec5R1_Q?+ODWB)E?K5>1)$UKp!9|oE|JEXY9TkBNSi6{ z9{mlWCJN>93zAuk?%eHOl(L3#&_0VxG=eRsq@8o}z;t|0JLe@)y}!_D7yuV)aD|GM z{E8zr8``&SGPT(PhHnMOpr8`Ayb}yu!;lH-O>;s)rSAvn>-Xw#0J&3|1qPN&K?b{- z{a(o6?c_u|5(t7C1{sp0)0uIRpqTSrtw4AL5ehXzjd^iXov5E>z4mTml|%AUre;=w zCFE2b1R5z(A2)#4bHlhv`Ua)mctULRY(u%&B*_U>YJ9^F5%jDWNhY~QPGy`wo`p~H z>hje?p3y6!US|$icm0|CR8UF}B*fb_GS8qjXz+~E`O;^IE1uP1JH(iHP3!}CxURV~3dv)eaOjhS!5|-E z?It0l^&zJi-XWQFHM$?W1axL8{@mrxHy$^XOy!`ji8xHwi463J$Z9~Ri8gg%FVq-$ zwemeaNvV%K0MRjQ2k-7M-|O7A3&`G$)})iDlqiOA8#c)+mP281sm*GiX6DlTqT-<5 zh!ap8xNe~#rI7KQhIKg{-SA(QPL44PhHTKGJjR(0OT#C>uZO*^FihQWEUlbcP^5#t z@7Bd7n@@mLkQOF1?x=#)VzMO!B`%+BOY{>YP{BR%8BM45_W(-3J|%;vtI% zjPAM^B-xX@&PGfUjmF=Ag;`d}|*If?HCP3HTP?vYdt2?Sn?v`STy1IWUwG(fZA^ZFU zOSFkD`Gepe8*Lnz%oCWqk%R6bQMELsx~#sJn+ORZSeVl$;&Wny-$I@$ajHdJ#X1Mf4f3i8Vq8I-sxlQ6i_koLp{!tVwtbOjOdGN|myb=Y?spZUN+nmWJz1k_(g zeZ^w!$oD1E7^h)`Hl#1P0D?Z>3yCx-8`F6}zsQb!6EpQ~r{Hrad0%bwetl9EGSv7E zO-sk4bJ@8K8g9Q^EaJX^Uku_cRDp$@dK=y1A|yD>mzP+c+vGB}4p;qhhN<5b65G2u z4~5ODOrwXYh{5@g|H)Hw*KG4jxB99=3}MXklh4!iSRSEglK$H5+W9FO7gNWuXOue3wA`iGs(`jUO<>fgeJ@i!Jk zaI(gP?onCV7gAY4&WQKaBk)3FXI*TZ&0en4bf}UW1Wfrwwe>ameDHF2?V;$=HG32RwMmc@C?$ zLCthH;i4nyf9amZ_(ODx{Q%MK$tySMEd=((++cpvl5?;53{@@THlUW?s%rYS4J>x; zuu)6lSlo&WD`Zx-52crY>|NPbs{9sJ|H^=nD=!MSq3CA+90%kemdcX3>r9sz6dd$V zG^_8DOqumfszcP8%{6kp`+bcZcB`4fKsbYVkT0xZKHEAy!2L-e>x8{H?}4shMNx^+4acH7HLp^JO7-K<9-+J10OEV1u7`dS8N!W7a5Ez-s{F=+SXrS^1Ju;(fZV1^}P13p11c`*)TK{X} z64YHUr_HXDg|6~KXZ-~M=RX)F&%m|?QVw>aK+{G+dQiN{Zp=BGMjEDzfH6#Ygr7&` z@P1v`&TSV*0xYJSra}D=O__4$=m|k$AB-{KykjNBeQl<7YX%SSkTy77^Bp`wRUW-1 zq}ixSPoft`uLULZ+785TGZa5P@pdnQ*ZK_P=2!S6_E*OIdF8G6n`Zm~m*EM*3PS?>j(P-{HOEV5`7=<1 zzs*7oB)oc}V+N)Pp`}`|*2zpikFd4;NxVQ$e-nX6PS6rfmIxYH2cpZ%?8 zPxJK+pIBImLDqH%vR}^B(;R#pI)foQCY+#kK65NQ9HB(@L!&;b$I(j+m(1kMLHJ*I zxKU(YLFd+j<^h6<&z+cN)KP;2*!ss8is=nSq?&()F(*@G6B_k2bRZhdr$v>ba%h+% zBu@9}wfkhU~WV79K7w9tRXXoaD{PsC01lx=)5A}AbnwQ7l1Nhid zp!RGc{LjK7cn4w;TMX`%n+N!g*aXhs_E#y4z4OM>QuA|U@!kZ@jHluW&}rhm`P-t;g7*ACZSLK96T~)(=G{=Sq>O1Gb5} z2L&DGh~TznWzJBKKa7H8Ai7N6F`v=-Q}(!ze9+%3g#6tf?t6BX`U+(2K1r+v89yBD zw1}R}s8X$%fc=p}2I05el$;l_v}UBk>c-S|sT3iwpe)ebKG2pI*D1b^ zw>8=S0LE+o%o8?{P)&V-0Ul|~=q>N_N=zn4GT_+)4$ua2$1uQeyphY?`k6W1nZw^@ zx-F?Zh?oU38 zBk@oR`@5EPJKQ?-B}1d??i@RJlh6P^rlV1GIMTnNceQc$my_jvw)1yS(Gqoq!^9X< z(8bpDLp49hibi4yAf9wUVu|y6PmD0X*?N}$v=W1W_w=}KXa(^-iEfZF3;qc;4(0?~ z27~6^+HHG~JkYdSyL8EZK1!(DO~2X9@1#oQK1*p_zNJ*=kPtcGE!Fd=D!ILTUfWXq~hmZ6DBs0ZV(o?wWu~v1- zagEsQg#;(Fil9xq8_J6_QicpYOIU?EeV}v+4ih-#I<8U+Ax{dTc?E7$6b|Nbb=|7L z|FNC3lQr=yVn(N59A;TW*K5K)i94o7>0caioSi@g(atTaFT3fDf6ZaS)a!4f;d}^| zL@vy_gl-!XIj%UV-aq`2Se#1s#vCIpz|!VSP>&_^!VN*J-X{&}NI|Wu+!l47o69EN zp10UjQBIW~H>{wLJ^8(b^9(mjgD;1R0#s@F%3oQn-K%d>@{p zh$-|#49;af!(xReT}k60+2zT+nw|hQ%Y6rQgo?RPhCMH6-CK+4u;4HvqA%v4(PynCrjJ=> zN$5_WQg`p%4_E(Geg&8r`M@$N#$FfB8Dzf9QCqjPz3xqI!;6l?66#9{YQ4^`T?S5X zwNt%((tQ*HfDFNy@(IFE9iXiFv%rxApaLhXVwqc20v)0}_tH6thkIaZbRRE^YiFyw z5!O`d-uV}(s!}yF-^SbF+m0^J9m-De`klq^G7r-)Vp7`FkZLgLU=*BpMygEQbK_uv_s11S6dWcwinZHto&jSe6 zTWRoA$b4^e%bk>U;_;6w_$4wY7!(m>f*~eJ!{N!AgtFc}B@3;m)F^U6=yV<}x+3Tw`(9c>k^P8%`JBa_DhEM1f!mbdv2& zixKWB%m(Yz7Vo)2ucl*;gl$8`podc4 zqFjy3o+fqqGWwh0IiLhSR`-jkpyrwYFKH8D(E=mR)6b}ST_nt}d5RBsu1Qe4aI~|b zcV7t#c$s++tIOT5Y*!IQye*O@2jmk>bKICIIDqsWhqVu z1L=6s%A{7=>mp&K>oaC^vuv+V1X0^v7tVn2HLaH&#e~4(EBa!44f}v*hi}|l(m^Vdr>Bcf{WOA=zEuh4?N%9eP zPkx(_a##CQ(%mrZ)Algr9kVEI3Sd_iM|5Da_y0%&Lv3^<=|NHD`vY^Awv@S}VW+OY z((_I)KZd*@*b(5ZKkKkS55@)WrETo|BQ=IvfsgFGK|xiD-$ZLXxDHXgD>s7$XgrG< zA?kOzfAx=)>#@p8nNP%qLqxk{GlGxQ_^;`unSZQ_SIC6rTq}sd+Gz`*HB2l!&9lJ;aD`g>a^QAj>bsowvH9%Kz>P%N!5t7J@!4W`~74o)!7vV<=6e@mE1 zg3?9*m?;mfwW;SL(GLMfJXybGG&GgQvsOQb;|DQI8hn2oYMTw%{j8~q^685YAzcQE zFTu5@0R+9C{-rHi$~MUWz3+V6=0an6A?&BkVM&nIMy=5F2(a*c_+gW)2j!+A^Mwr# z<>GJ4K+V<`^04Y-5HW_ixHMh9F@HuNjI!_o-yrE1XslM;SeLVicE|ya*k=)^@m&Yj zM4CaAEg-A0*H;-nTG2?4ckLSpV*G{vVG+*!Lkun1t)jh5%lNGdDnR0#dG8*w-w&x5 zT(x)W4EO8CyLBRLm#)L0=L=?Ak;-&Gwdl6cb}mZ-$UP=>hN|9VLVIozx{%l?<=$YN z8>AT=O-cKGs~;Uo_Gp3Qm}^?@Ztf{2?LB-unetFIdHN&KHlk`v`+})&{Y;z%bnz88 z5-1E>bVA z3pjBeuuo3xY5hW1#TxB|+HTF?kYA%oaFMGI?0hoiiN@#ea9(TA0j$8Er;HZ%r+nv- z4?i-EYxL)5ywzb5e<9 z5hvP~N=6B`NKUnPW7c4OYb?MK41#=s4hzWI-#>4;RYTeET&Ws%fBv*Ea2m4+TEPS0 ze@zuQF%>jqyp?og6|Q1swUdi#SXqJMX?ruoY(bB^IGeNMEmZ(eVrKWYHo&nnDAn#@ zh25xWI7$NXoxD!5^HmdW#s{Oe3ANaFNhEmVy(B>U-6T+fVCi5kJlR)tND%;7UH^u*i9KrqOX+ao%Fnu2hLIgL?o@e z!BW#CvI`gQY(op3fv_*6OU9(2k=GqOZXQ5xP|dc<@58wyiIbBj-dPHxH`|RA9s)Gm ztFqq6LY{9)TlC)Ov{oq+Nb_i#o)&82I0(We(>)X|PAMRI$9!1b{eQ_kaAkQ65!nzv zn=jjq1_(&a-trkBB%=^!+ix=8B9WpAX6IJwr}ES9HjTCU_3HN3n;hDb($7Fz^ku5F zOr_SoA(pFtS|MpeJG5JoVgORKkrW1}9}8fWJ(swyxsz2tWUY^ZN8Kw~Zw{nR z!MF->BE3@KW-jRi;t|AlWDKkkgs}>)VA>3S7{~TqM0f zV-|ft2UHSO+aG=db2TN?mIf#xR!EjiQo@?ymI#d!E`g`>Jw`BP^VzO?uAu?tp<|qr z{FWSOvNb;WJfBL~RN*z{m=$s7@hww6b&rcEv{w#n&ISCub;U!+k6K|mOFsw+`n5>K zAth)|Pr(37#KAo+PQ5QuXOnEh31f_YVe7ig{HeI79Il?RmN`hAM#6IxYWz2u_VF<9 zOX58d7};+T^>8e{P?c0kT#-NM>Q98ilyO24JV94<5Njntn8Al?)Y_Zn$OhNb(&Fhz zfL=88Q=94oY6Q|}M2qF|X_=wOuEAY$F8|+;??3-T@U@A5!7g;wMh!dC@MYTyHfM2f ztRs+g>VfAy&=&YfFDm<)Hb3QL`e`8^PJ(ofB>PprJpG8MpfYS&Be2D^13B%=0@)XB z0D9m&s>NiRAJxjN%)-%Pj4rbb1SLhK>OIPnPm}2R5?UAMgwWw)bS{0XsTa4C8}yM7 zSrx%7QrUMF5XB69mym&8vnB2M1`U02o^8qoGGj8q^l@#eWmpLhcQrLENgb%y2Fw+_ z{oHm&pFnSoAnF}_D(ZG@*pqhZ7H!TFMbA|}=s>8-=XHdMb7@i5fsEX}>wM|vBn}`e z*pRdNPDihVp?RY6N6D0qBIT{v8R?eJLWb%A#>`e8Yb%B$0FJSX1@hn&6K{2(JQ)WF zrSf9nrNm)+xt`kg3iR~rBmQ5v=ouZ7@eL==HVwaVzyKlK7KV!lB-ztB*f>V-){?3X zi_QsI-qnmlhiQD0l5Np~?q7jer9TPP1;Jh=lQ&nBk`e}9xtEaX?vN0z6!`6ZT<^?R zq-zh#fW4{YOK^n?xrh9_{d_T7%m;|tR@z=Wt-&GL8(p6XQZ-!TfeH=s`sI0 zcPuljp$fiuC$W-f#&dyG@=}6~7KUDAhLL-AYhydaEz|Kp346ZvemUx5xTwL1@#muE zkxJDk*_`ck3@AiNi1FyuNV93f^x}BD&u#eBKjFy^ybVtI%fr_RvU}4_2_RDF;43go zyUK^^#K(HKnF`}zLja?U!1*9~bj+YnB08x5c|T*#IJ%_qs3eBPPS&Y_7eZF(B2}5#qR_LR9t!Bqr zA&S$VPejWo)BD3K!|4;b@K|nO^`;`NE`bZKVJcAdDy+vHeFW2QJr9pjTF?Etc655n zYR|iEGRpwp$yz8=J=YROaSrsIE%a*h)~*l?E8i}nLfcb=}=1P9XO!qTiV*bEX`8OduxBvhSFiL3v)22ycLQ*)&r=9HfN9x7ya@e4V9cLe> zS6IPCOtHeCMwRf#KnkqpsVn~poIQ7hv6=u<>`OxWiLu^rz$^dja5=S>fs^`=?IVdt z-DA9Ve|oX(+sc^Bf1JtiU~#&7H)v-(2>-O!oQNvOI9^Yd^@xQekZI4W^Vs(GU<0^ClTn z-13#nGk4gonh)_o1HjR8j{)k3Ij`m2G2IrTXJ**}PEfO2{<0y!kIWynNJRbE+xac< zmdN!%z|8Gg&EW@Gdg#)c7`hp|{p0Uq52$|;P%7?w;k2e21?RRdIlOx}w^WQUv8 zdi>4t^rd>j;Uu9MGPF>AQJgkuC9j)4_jieX1Pc_O)iGu7@?U}LUD~H?yf|`BK$Pw^ z{52~Zdv7t|Q?->-bE=O&q+1T)ZU^OVy&kvV{Z7(+^BMl8?s(*<^6UIG^m`Wj7vsj@ zR6S8M07YSA;oj_435D0)D;9++yxa?5Q!4teP&2g%q?W#P3rcykf;_0c6#?Bt@DvG5 z!Rke8K|EZJTjCc4tk7~xVa8v0V~yu+y4lwHWl9yuHmV47EQI2o_3_Vi`RxaBTc!A+ zu^2D#DeL1cgZ$Hn>|?v^r>yz3S0{_vtx(Vp@?o_)r{VjEd}XX7s*J;$fq(C>7T+}SCW|1Yl+PsJgG+oYm>C3V9_Dp!@_C@yOJHTfBvWw4taUZQ zf0xE^N}#E47D2JrPNNF#{fG4iMw(YQP&tJ~4Y}RVx2Vp-2Rd6*1Cs@I+!b{xfh?>Z zM^W)9q8>k-n;K0YD4_F%yos5iNJ&y$>?N4{w2*%8Kb93}uQ?@3A@8abOg>)^Gk6s@RDHBhWYZuWlaJH~5>fLbY6J6S36_}zK; z=Ym`~OB_-PCXun5E8L;sU~yx|@C`dQtR@fr7DfOXsbU29hWn^CNx zU=l5k>$jo1S7Vay0?oy}_XZLarBghjA8%k_-6;d&))fpugd;g$rQ#QtCccOx6!-I3 zpLrcI^2ogF!gE7U6F)tQj1=0}nGB#|TIoiEAKs>L4Kw%GDSO^00DpS|>OjYjGB5>F zc0^KtT%RyfF7JaD{r7RnRi($!(AB)-0|$FF3^Pt1xGq$TN93Oe>O6DWEm4)&YKq#y zfW3X)N^&wI;Cy-Gbq(Da?a++}E;)dvSf8g-Y@oyiQ`wbI&rEvmpgWJ?M--}D)Cff_ zo8SEeH!WmqO8?EIdqlK|)Cn(qeJ3tCL4JcgS|)S@cS%4w+OMv~r;3C)X{gL@!0wsa zdoTVS;v+Il-q7UzG^`Do;UY11vKFBzRGt!|e5LY1ol9~7jic|%G1HFbmNvpE*>##o-LdxYQ;6!T7 zF`@9CGcAiH_Q-zUNs5!e-PJ}>zT-aK$7JA5I_$5?w#P=JbbSnYaypB%B%z2NA;c~u zG?c<(g;2w8GkK@F7G?(6`xk>Cngkj!a!NouU3P}GxtL#A>@%PYn2R<9xdZ`k^^aYf zyXe{>dZh6c7(}NVqE47QIkn&e7Wh)_gh9@G7pIh+7k}i4o=Tm@^ncAO;{AA`NDjVj zC}or$tjNJX8K+dVWlsgK?}VQoTzkx#mgLALTH8HxVw%&z%~BPEZ5MU+_n=eSKk=tJK1#H=Fe`vz#m2Np>C#Ql8lLc z=CfNJZ$Ndjr=X`x2Rh<)KlyIaTT|sbz*;=?!R(O(IdS)9zbptkdJ3xa=2*+s9?_jvzxagb8n9YPI^!w4YeOSe3XI^AAG^MtDRI z$e*I$Wwr1b|CE-sgkzajuanrr+v1yOtPMc|$l`fv$6;4mbHsj9S>lOlZiT+a_gaa? zk&Jo17-zgp__hS=-Gaz?hxx%lN^oHTZ}^W8+uyhaq8B#IL-{)c*R|+N$|WW0&zAJd zHLF3^IBP)>nzL|;--bPoHg~>!edPu^5&$6#KD2o^<@w9D=W! zZW#|OUbuhB7Jy_E7016jQSEZNu6X=W2kY|womXuRHCesDF>E9##P;a7HJVx zZ)AJo(o#{izJg;%)LJ5E?dr9Ke@1q24ulhyJH64|_>N?6m{lI)UD}V40ZAdr2s)3) zNQyDcr;i75r974plS89`8MSC%l|jKi^tsyd$tFRldmFO7VQ!CyxdWhr`G5S zM(ogHxCj!Eo;C|kGK?xSC&+KUjK2AGo%dGd*w<7w*XwAM@K%|=wRJ#m8(v9tWT;yR z(9-!YEvksFZv`IiUO0vl6MFSi@tzEbfk?s!Xm1=_>D1cApP%m0+yqCw8rMrc z_T{h_@CG#hE8sl$nuV%~XGFVKZwX~`zO9RLKycJW!2=FR7Us0@XTygw_pYY^0}=csJm8Lv6|_k(*+s>S}nP_a+LZ6hT<7 z>z2{{tDz?|ZDKMx^c7i2(o}1%SuyPP#G-suzHP zR0-eG)(j-ZO^OrEMTozwgkH?)Oyj;&X^t_m`y-jr2(+ZzIi{O|a06R6U*b!v3Q9xL zQLV3djGweWt*Uq{TC|iGnEv_HpXGuo;tKno;CGHTXA6}|bBF!Lym8Mp(S#q#)og}P zY0uS!ghf_jk((4)Z*LsQN5XK+BC7)o)+geA0McLy-xFfV83OsOPewcQ5C7Umb(Gp_ zHpf_gUYO4`05x6NNz94RpnL&uUkrP;PHP0!i zgA_mRK*Akm^S<&suwP}o+9(j5bb>faNx`z^d6!H;!qf^H2^Y$0yV{qrMY5>fgezbD zA}P;?Y`=9I;vx8)@bY|81T0MG^OLqMk1K*yz^drzS8>y@xPN0IYf$Jt%#Vq`&jSLW z`owdnHSKu~>q-TIs%l*7xkx{!Qg}3EH~tnkFKDWz>JZvslyh3cF8v?ZG1iaHbl_o7 zqW_@u90MjZ7ZU#U%HvVomgjRSYZd6Bqzjk4h$x;yvsKkpjL7+odhWoe$%g}w=Ceuk zfaphSnU&892euN6qx&`SZ35mrrW_+I@J6B~L~H^(1lb8V+%|K8ag>?{B2ond#LyLs z^^Xam8hW0od3rSv{tYMq7R*4szU;IM&N#a;J1r^BPJPFS#aXHqssaO{@quDPM+_W> z|8zKN6fr*%11LEqD8S%6z;8-3Z#Z#Fpi2q@_`!-qCG}#|#O_CeI}X{~(0|n2x@h+H zA*nRrOs|gee3=MX{^~~GGjr&HG2&8NRdj^6=eGz6JN%W|i78MN3bzmW;iECCm_jqG zDDUH!hOU+$3vNqy+je3nwc04{ya5<*>1+asSjcu8tI>(IN9^QRz{=?JBokaVVA3>9!Gd{3C zM3Hn$%7GIzm3WAAX(j)8LNvLFPeS0KwD&w9i6y+>&Oz4ydSKUeQIM5tr3W3tNle&<($)Un_M)J@|>0>fD6NL zvAZ(&%E9Y!n_~g??~c122|i2vX-V_OLq1LNDjqvpGy-r*uIeht;|JuDDR2wmp>_I- zH>=fU#@i2iN82X4Cz)caS|Ct6iZ`JNQ_(>h7zTl?QORm6*s<8qSih{Rp==tisKhSr zqs!!eD(wSBhd;rxWTQr%1#)3XFo4LE@GC0qaOzDi`f6O0=ro9awy2T^9$)Z3cUZ!H z+a)d7RX)%ac-06r9id5`OP^Y)Dv>BgnxLnMGcxwD3N5TWGjVt_?s$d4bC;f=&GhF} zzhlZn%5vN{lt+Dwq~y3qEe(?(5!1c?3)N!-h1)X_@wV>bp-^6B-IzY^{!Utf{oUDpMmG`h5?NawM1a!G2?y)iPSrV(05d6rs z#R%P{96)l>)T0tAxko^FTEzQ zae;59*-$Q?W;A?+kpaC$u2awx6uDM7eVfB+q95!Y)9D~*b-fJ43dSWEo zc{=!1i0`G&Ga!E$x^zYHJtD56SRc8W^wT=a==hN*_+mUkB{^*xM6g)WJX8s@B!wK( zCEhlWPh%O!QUxnVY2t5M=2yQdS8aRZD;!d-Y|WVe190-GW!Jq?nOLAmSK#0#tZw<( z6q^x^iL}0{pQ=;<$|=S>Ec?v5rBF>mgt-mO)uFCrukD8^{^gZU4f;;3*}hQDPl4eb_oi}!0buLd^jfKB^9ixY?CHwajZVENYr zpOS}^74UaED{hFmT)Jc%uJn})7*V9^F^3K%`4bzoljd)AJxVqYWruGv0W3a=L$ien>)KAy3w1efJ*R^wopKK zib)2}s9=9f~0MUbrmFNTTyj z2VeQn;#MMRvkt7xtWS&`iLPjj{as|0p7JPV3mwb~5xrctaT3;6HUyu_jie_W@xr4P zKwHN)t!`1$!)ys-m1C~2?D2>MYdag3O#k`2ydiO=3pxH-4I-8Z;yhb_qt+-RIdwjm zi=zyy^K13E5m5s?Y~d-XVHIciiOLhTJ9abLmZYI=d1U-$ zV1IO+)Pp%6x1`davnXB!qGju0APxI1g&hcCKdU-~X{eO_YClaF>|~{op67(|L2^ky z8fgkj@9*yi%tH1Za0pAZXvpYY)m@B14$zE7VYb#^A{wPBPDU9I?a}9Wb0$o_hL%j& zkePNb!b$w(^5+?;^IcZGTY)1X+(T7tCgriYP%hK`(3xuv`FcOGY0f&}t=lxUYX>jB zgz1kk1J_pU4HGOj=2>PF9T2n0q6k7V_r2 zNq;M3!Gsff<#{2eHiF{i#&D| zugqt!3qhuwWuV?9MU?2L1A2F^zOruy2o?oh-%sWuKKi+pfc^O-DA`64xM=x*dfHa> zXiJ!YCIWa!wIgc7uPu=|=M;>3%g+-PXX-*pA5YFDux3ahLihp(gh|!z>Nk2*qpI<` zfiRd0{gmR#^QOR#T7KuKClIoXQPE`)69Qa%lGfJonGfsvnk{Qs51JP_5r`X-KV{nz z3)XXXJ+tp0jc2qNZoE-KDeC(c-I&X|mUWrlSYASZo+@QwaEcBm>B70~UyB51bDgF#+qBA8>OK+rO551jT8TPm_@VLK* za+i%?;Of342>@8L4GAX75G!~VNuJ-=_z0kZTUg;7HrmY9@@ESdj1qm=`o*nNNFrnK ziC6Tp`x9Rh&KNW%_}q83n_uCr_L@xhiNNXW8@#QgazRP;z}2H&U|ObhCM4 zlC0pj{{cPq z;Dyp-?0{)uS{~XWp?2;TLgYj^sTegZVTb>EF)iT1rCj=Ry(c0@hjhU_GHl1#dpG?= zn3xYauvDW3$XBBg0OUrBr*){gPQ~Dv9zC2Q^^(TLoD5qa!ot!RCJ!d1xP1+W=9ls_ zNGTx2k4z8}r@~XpwfqJ`x7ym5)PlOoFe<|6CWw1umvyX*dpU@HuQoj({A#RDCQ0o+YVpk4aUcRyUNZmBj%rJd_TJ@n{`%oKj-=p&5Qd zQB(`wT!)U_@>_4i+A_H&osLM@=*#14P6B-JU74moe1t>v3*VUyQN3lAskUun<>tjZ zm{q-PW20ORG^fBSEu5}+VNb3v<;M7WTC+PIcSq?iXTJl%1hojmTWksUgHs~%+?XRp zYlLahEhWE0rT8?;E3?QrSb^y7GF^(MRQo(}qv7vO-Bo2H_^?WfC>?;;r54xD6Dc){ z$iyA^zPL|&5kfKd8^xR~EDyI?Fx>Fn{!Y#WGFRecuq7Ap?(2NE|C0;(mfzXq`k8T| zwsi-x#us{UAcs>`QpJ!3wwbwpI0nika2SIAA2pHTn3V3^tiFwCv%^<_VBAAJ8IQK^ zHT7(H?EJ>wlJ%-CUalfv>ZdbeKmA;cX>v2AFy*%k3l8mi6;89te+Ixk!y-wXbFNoI z0Q>{8Q%RJr1P$5y(mmgD6~dj^b!8tEl}*_s1{UA&5F^&JV_+?4;PiDST1cbdFxhT9 zwTEYQUM=KYQI(hISZ7wvXE%vumu>9^B@kWNoDfW^5XZ4Lv<4p6D2;~I1ceB>Ngowi zUEB|1Ed7R>`Or>PF^A@(rd3S0Phu!c?8;?A`-rFqB&0TrmWLGSfpjUC>*I|>>m^@~ z?L7G-6E=Ktxnmx4I&^BnDp+l@LYcP2>OQ(a@HiFQxx&J-&Naa{3&NVjOa!WNR`?&D zxpQyypjbNgno{*dmXXBu*c+FWxP@#H4YMO!5rCgkCJ6j)khtV{1^Sco^!2RP^}vK0 zq~=pVepb331U{$LS8qQ<=wGKs!GoFLym57IXe9nu{Aw9*>BRQJx5UdpbBFs$OnvEc zBTIzJ@O|I;_S#|^+ENLqgeOeesgcU%6;r%kF>kTsECSp*O`GWxN2ox|LBO@p07SL1 zN5nqu-pc=c@1zALdG+l;$^-S!6fu1%Vz>Un!#g2<(ls%-VOsp?B*E*ifk*)FoH1Ab z0Q*Z7tW=O{hDuWSZ{rYshi*CqN+B=ei3)1>p z++@B?jpfokAkJHWyPW&zg2e<%>6tLF7EQ9$M8go~^GsF9Pr7cWCCd(2=ZnYy{~gxt zB5(9>EZWm}4~qN;?vnzsl_)M9IGY@#Q;{jDU||lvQ~IBosSy3IGm*NyQ|OGs>FB^| zKBNB1Bh52({wSLlaV$>N)xMIQjzK%_N}u>OI=OZ7j-wu@yyL8c?%@rmz>7;ej9oE59kTx;yUl^@OT9Dr*QhdpY}67PFo^)T7pQr{*=e`NQ5C z8iF+B4>{EZ^~rKZo&bRj1daVpG0R`})W}!HYoHu{O%E6v==+yuGrytm(Fi|^@UG); zdOZZJK_RiL-*>7J8yrHMd25n3UOKjDxKhM`pTiE~_D$e+c@F#918o;b$oba~bv*~opJ zOIZu?GMz z(8n$ws}T~w>Wn;>@58(2vNFEHhydc-l{!mEaPJFKWgZ}mUdW!K;Wgp9!t;Bd+#Z6K zo?khzBU_Q5d670=*)XEAoT}-6uh(E6CiRQVfor%1FmBu0_^VpGb_YUrm?V8SBpvo= z8lm0?Lv22rz}qu4de73LjXF6l{`BxCW@T>RW}U0y^eEeO%{GD+66u1=dw!)c&g}bz zoRp%ZcQF6kYG6)njsX~(vXI(5G<#-5(Nf-cnI{4o0x@K;*dQ{>idVdow&ZaUBAuyC zyO9Hv5^Zz0`pD4qiv8DNi`?cwnu3=?_0ryMqAWw8;tn8VrsZd|B@T5B6-8UWU=RWI z`P}A6j-6z~!0OHmLz1;p`#0zr$@VYu0S?%_55#wKm2I;QES!G;*G&+XLUp#1sw@!p zYZIBHvHlZfP>_v+Bl0a`Iaf6U2Vn%I4Y6H(p{kvvpy?}4USLPT=_953bNEB)fiOKm z1p+sGl(c>i(W?cU1yS7VtifkKZEq5lRGIk1Jm4laB^XV`7c#|ETvJOz~R( z{y8J&q_52LN02O6phdz^Rs30WXzL=uJgxxX`k;=s29g~?H;IQIAlGu@zox&e%EDU6 zv2mU;sG`Q>Oe7Qq>`vOrxp@a2A2pg!56avD0%Nqlr8Ic}G$oC_M+%x-Q2jex;A%mi zh@n2Szhx5E3_ka{7)KVR*YmT?27*J%_n3^vC?RxwTZGb)V4EpX+`O~sUCiLSgx>vB zF!0mh{V43`%knAbEqN<6CFv8Sh+3(f4;(}%xJYo?p{+UFlI+_z1biOd8 zR=5Mf^tq0BMnpj&I^D$wVbj`{?^9vhPf5WauMq=V?K=CWp2T-&q`YV4*9dVuXYt)} zBA8wvABqMOCWM~)Zm`Q;#TeqIVE2QZ;5qykx_OK_f6f2;W(xC?Y#AS#z%=HP&r5`< zwx3E#dmkiWy@f(9Y-<&;khmuK?K<_?V-1D*pQPr(bpG z1)x}f&2UmFUxk5WIzqz1kk-gd{VJ>kO~K>*CfL zLe{X$_0b^tvwX6TDM+wt+%OSz&vy04bWcg>yWU%RD%MjlpxHM=0?G@fAq(G@ERrSe zQu)z1br4-xVc|$GBJ()m0&KI7z0$OOmGlOyTtsuir?NR=+T>ZpSRY;l+9xLhe=C{W=%;>O-)ofq3Kw7Bg zZ*h|Bcu|k1iF`Oi-C;rktBnPgtsYLjcGS*rnm4;y6T?#6D+nK^4Nlz&3;HVfAL-_X z75+t3Zdf#hF2Vk+_j|lXw1H#iZzzBH#{Fpd`ja$%O@pt)BNraKHI`HowR>Ti^TUsi znf2|*#WSpb`Ed5M2%Eg({!Lb8Z^3nqKw3<~wM?&Y@kkSU z86aJpBYTHb3kC8Qum;2C0*dN~M>J!7ruT7;{5vGi&}bWl1bk%K3g(yyW^6UjkYKPS zte<{Une*rOkOnh-h-urq#D4T&)n@TC+)bMObj2ri$N3!;Z|lO}AR{unTYi}2(CXAW zJt`nq>Na=e3h!4dDnPe033hZR#I`5T5>vE%UKh)caLTbvp#qe)S3*LQdB$G`|egmkGs~~kqvhP##G1V~K47uTg23AZD)~$#eiV8q)FxLQ$VW4KvUm>M>@gfi` zCKjs-07W~;Z|8g$*^E)ju@2Y+k$EFzVE9VzD+YH&>@t5%l(U@nzuA202oBf~Kww1i z@y}ql*tHZZue6WX0BZqXKDc!KsokQB&s&;Pnz!zg0+*()Lh^!GV7btj8u3f0ZE2lL zCn60n5K45&_oEMMJ{yKHYdxAUQueD{U&%@Of z-?7#ha5HKbuTRyIt6xV>RQY?dVT%C-CdY31quCDn*)7+V1vb0R!SUZB4P*sN(Q3pt4{TC2~rxr@2`- zFj3Dsnp18QNZ7P|y=MV`n+eJ3_If!1f)OlH5} zQzSsC>L9&L*QZgNE-!P-Q{uj^<3b$z9|MB z(4X9Yjr>(d1Jdjzu-Vjv*JTeU#B(@2^f5|xO=cPhy{chOiqE<8V;3+zxG-K*K?m*8 zP=Pzq^293$Bu{&+0ti?4KZVH=a6g!FpA8HzIAFu)FI7_7hSpwp=n!`z?YO^x1@5JA zZNt4*^)pkU9lYJiGXeMHmVDNQc_$sRukJ>)0t}WYQ>}$=;1GW&G@jMd4;u_rT_5lA z_N;NV8FT8RofkE9jteIaFWK?8|91}YRLg*~j{xlzdNG<)|2M9+(X})-6@o>8t%K==8TlX0Zyk-M7l7V!wi0Os!955538TIiId&a~w@enMsab z&;%ie)!dO3Es0_01>UpD98H7u5}uUAe?*nTg5!ZD<}&QfgG&n$w2sox2Phd9!KU#A zbQ-1Q*GR}J*eeyEIeie$#H-43Bjj@w_ZQO+KYcJ;7r!P9%|nEV+a|`fvWi2>`|kv_ zNLI@Xs?UlY(L9wn9sPQj*E2tVEvo9dKbSk=RAz5|u)v z)|ep#wy4Q{>Tur_Dp+RMVYzE#ahaqC?Duxo#j0KeOac-ry^(}kCk16XnE7B~uR^X9 zbF(Q1>O;CR%~3m*4UEZvO!7TJrv{myn`seEHNH+jPN@FVKgvksRD!slw|b7%3GU(9 zO{=MKOZ|J8Lg(;J6rESwrCmF_l(9-6Vv1v3B@esqoW^M5d}X&w!jRO!$hPV0XNG9V z_!K7B7b;&p4YlM6L*sQlYQlH9%4^vyTvWfANVqXG-#`h0D&0&ZEu0BpJ?Y>^EGDL#1dFx3SvPF2yz;;(__ZH$-XPJe^og9%O>&nrJ1%c;$ zE4EK2(aTmL$VGq`9R+1A;I8RDvGHnpitpvUidil=YeU)s5cPdNKPO@O^F%6@@7DW+k3Mp!!TFBeAN1(0xrO>sMgSp5FeKa|9~;xUVt;PTNucTThq5 zS@ei>KEa;1)Z^?-jp8)|AgkX4o#$iyST95A}ZlFDumbp)XmQ zOsd8;XIEZ@C;qccgnLCHTZ42}pDImG(p}<5lNQx*rjim3{V}$DwdV1eBML@(OR!6~ za0u#6^&Gn5!a2fdcNZp?l%O)>%zN1>BO~*(Hvg)YKm!ENMeq$hXr{*7KAnKWCF%J$mzxun>bm`-d&=^v5r$mq^b+6wqp! zilmm`8$MHIyT1#bnZGbHV2N9C0W=WSJSh`mrmI~}Nv77M?OVEQl7wIE?rORjoPH;wsUqqsx)xh9YWvB_>u z<%mQaWWh)j53%vWnH6JNU|X(Zce!a#gxr z=Qe;+E4(azpc3Y6+``--^5b|%hcrn%uHvCpsYPJ6X!g|rg%9^;&mEP@sF9es%vT06 zReO+_JLlYm{bpT3*RQq5Nq7BS-q<~+6fTqnDa$j5^AR0D5G-6{OR+n1 zGh*&Emw~hkz0Ni)$9M2`U8?$pl%J^PGwi6k)8tL)atsiog0TxJY>PMgR!bS)1-#ss zvGBX->S_SmCL68<6S9yznIw{+Me;-IEmio}f=0>i0@V47NFJ+R&;R$F;nu|yx|Gu> zb+Q0D@X~{k4B71WgkkkfaQP~SQvvcIugWdr-cAn;=gnv~a0loXIz?1+_Oh-cal;y5 zz2N7hX!>Wb@X?ZC0na-uooTJ`jhA)u7v8jub3Jz~v0nQuG+TmN25z@3m*E+OSJrcd zeOz0aT#7YK?;{%@V?^uun>ye~TiH_HbBtG8zR9jkI zW^P9}vtxIJ?mylsd@svY(H%~SJqv#uzAS`4qjxOgmIKw8KufK`4+`U!%izHYJ^g8v za9d!~-F?%Sxj05DkTl%`RY2^j<(6!JtPZHnw>4jH+gYO~jP$JJoNlI&=^BkRTS`f3HD!*@ttWU`I$p1J`6x z2p^FkpWSo{K>%_Jj{JZ_K-!`RTLj1qwvg5YpRAA#hztd!)1Z?sM;Idkq6p5n#lu5g zg(iABRK8jS`^hFlcm%RQE2UTf4?toHP(x!2dIFhS-QPb&dw1QbDi;k zk=1(zlK`IT{?%|K4v-O57JGYT6EXz5|ChV3BT^ec=L09$&Kio*em62Mo* z3w>muO-y*+tZvisRiU9G{#Ll*D_ShTKfjL6WbEZR9b4esNZq9+OboJAhqulSNN3K)sL?@ zgIS^WzpVYA?6hFEtNB=>L1r;z`y@szwtqKoWpF&D1xPNw1WM6IFnpTji|DWnc`Kdj z^-#snb+A`WF9=PjQ9)^~=s<(1{fZSy3CtkqP)(fZv}uu3^AQz|`83#^?kfEO?d^VC zxLk-c++zluTH(?x`hCE^f%pGrUIG|M=~vgYrY|CJ%lx2?O_}nh%!7r?OF78)RB(vL z8=PTZ`H1~RG~y^hv>M2VX}!?P?ei$~jkg)puv!!h^s9WqsB4SUN*4b$?mSzI?MT=* z2NynoON_7@+mDV4Idp3)xUGEU%uuUL$*Gb@!b@v8 z&gimNg6H$ea5@_%A+0&`R9IGf^eK(**jLMR#UF$RO)d76_5LcgF_!%Gg_|!pWhnR& zXBp>DB}`1L-W7=fEI1lc{;MhHhL$rELvb$Isc&r9 z1?{7x-W_PyvY75T%l$MQ+%(o_?d*?imDqjF?YYDS#6r9Q;~^Le*e^ZEJA7V!=Y?i0 zl{b`PUaH6E=h%16s#3=Rjv|^ye4?p2V~fV&t>K+`b<|Xrgl0AkL?8n^Y|T?GBG>y| znSf5G;wH(C&}w6$)VdvXNJ@xX{n$|cTMx4IB?nZ}O7IfM-a{81xtgFlnzj`kIWxM? z7hDl)>=;N8=x!F^Uan4gtM}l#-IwIl1|Qr{NgM`(roBsdfo_L5yo;6J12c<=xN!I0 z2-?0UhG2(u>M}e5ZV2bw55Y>sK>G%oRE$Zp;ecj;nFVilb7BlB7Eax33 z$Tb`s&`=C@L$3obD-NZn?b0KpJBME^J%2n2#Yx?KPB7D+@-lxJ!IXXl0Mx8aKgi~U zVe#{qzm3w%zodEl>u76KAVbflJuf|_&G2q`M2GWsY%;ebPC=jLMW#peJKA5y;DRXX z;O|7i4Q`c&^_z+dw64|m*pQ#n3KkDh*hTf#gUZ-x zg;aR_r}Gj@o;^r46N*RgnxVSRAq^LgvTrqmT8PEMZAL$Hwoz1-O-%;l7|+wD}}RXMD(&;`>i>T^2Gev>y9M~ zFM|n92NC7XFkbsz`Epu$vl_V`U8p$>UT719i+p?4QN=EGB?@g zsT+dp@Gqjz*;JJfc`qzM-BNxwz4d}Yr%r*va2;v7K{+Agc)^tYwLt~GLG_!gQ*@aR zNkIun_1+hE3b_D&l`-1&x=KP&sqOZI)Pm-Z)z5O2Nnk*pQaGq4KSLe?LAR2CLp4`* zBsO$37~{aE3KYZRciqOT5Q)i`=Mj-l%uP13{7IixHG4eM?Py4`@?32-AApZ*T}c4q zD~o6%KF<1$)NqakUN+?+JwtyH@xMD_0b25e8`0(FKy3Q>G{P+}S5T_X62u{r@BW;! zpckfWLbqlneM~1$kFFiEMNZ6Uf#@;sktxjlYCpF5$_#4~c~D*rU!5+;o>MS3*t_(! z&`6jRAS*dDFUUj41a_$;5#?S$yxQFL!Ic614{ux%gI*i!<0(Fn<7N78|Ibn(lTQa_ z`8L2=Hv}Q{Egmr7&oEQi zn{gdao)Tdap@Tl12yJH!%sZ_hbr*^n_iXVs%W$2+`3-5?rL*X?2->2AFP4{EzR1H- zQAInr-iLkLvQ0rhk>4+3KN2?I!8LE=2nnxah=}RAl1VI67M}XIIB9-i$av>-hhm&+z`igq^%lo0RO8HK?{U`W+1_9 WfOW2+L25C@dWiD?1Lt2r0000DGI13E diff --git a/img/microstates.webp b/img/microstates.webp index f3794f2b6cfad85bfee2091839b371ea5a524d96..df7f97875f4e039e01ba93f206835765eb03c990 100644 GIT binary patch literal 48456 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

NhnN5 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 MfPCt(1EBnX0L6kQ>;M1& literal 21122 zcmaf(V~{366QJL*ZQJIKZF^?Nwr$(CZQIzfZJRqbZoj*@`0mGDTy&;s*ry0RSY0g%!qt{+$AV#{M&dG6DcLw$6@9 z;zEQPzcmT}qx;{(z{tt|fAjxk{TJnV1@J#(EztTi*vQ`TKmO7Ge0XfE zUH-Gzf5v|@hBdKORsOd^{u5k)6F>8#o&Li~q04{{-076acs{0|4ND0{|#<006Ys|Fr#^{vWv! z{xjkJ%ggTHVFs`Um;eX?k^ozP5rF<5F#{L@OaS(OcbG8xUqx>B0Xaa_{y+dAJ{#IZ zF;e1!0=gAke}A|L*&Ri7^f$2=UhRjxledX`0{@@B4txh4>63@=w(2;hSYKa@&&AKR z9#YRzA1F_gdO2_ZH@5dWxX;dyuFW{Z>Wt@K6~AWr5x!?X!`=vd^_Jb59)!OU6!gY_ zihhc}|9&*wn!fgC`s980Dd?H|4*XzzwjI3;{o3){d1Sa6{kFT$0}w3uwf!tUwSCJ6 zu5SMv_-=Xw{V;xi{EU9;HBfx>U;gZTKYr-FTm9I*6Fd_T{!III{_OnVT_^lJY!O`d z{rJ87Kzr{{;UosKHSSXJx%YmAIVJ+{Lh~58I(!eL6BdzQJS;7$~DR9=?-w%gJlZ#IIpjUlc>cti z5}6Q2ilkkv?2w(WrZJ(W+tnO5IW-BPVi5)?gxRuXCjM>igjhoB0Dy@fMAlN|c0jQv z9db4wVIhpqU4-jo3XS@DAsN^%E(-R-SO@8Lfn;!KsnO0&TC0j)It3$SS3fo~<3h;h zdM$i3Q8FFoH)=G96E0mYPZ*EVFb0JFffAZ6i|su1rrmPU95YyaeHYNsa0!oe0;Z-> z;*L#^erCUgL?DGAz72aB)7Q2v*+!eL?8p_sl6MtIX5QbTC(#`%kT7Eib47tTF^`| z%U+#f9REvVvjf&&ir>ypsIsJ9L2c2x&#UUO3iS#UdpEa%2tNI$wsnJFN;V|*KILLq zK=J7BgTUABi=o|c7Y26(0d5W2o1@W#^xzhfCt3RoUUDhc$D~MlB)Vy!i}dy*#12k+ zd|^rLk8u&m+H`Vh1!AP~Qht1^8N`M}rcu)GI>vxlH|RpLM)b)Ib&AryqYKjfb%P(; z?mG7FKJUv(lSTt&L>@$r$I|)qjw=^&5fe<9i<1Q)IKCTg^Y|^Aca=^H4oibPhx> zH);dQ^z$%#+6Z6EGa7oJC&KI_1;gl!DHg#Piab8NV#9o585s2`=iL9 z>e;VBOYvANx;B%a2Aob(WSNtyPjm)VWM(R^S3nJ;q1|1Xe?7u%_`*f}%4e$LaxvD! z1)UXzyCYddI$fwRAW^jYL{T3YisXk(;OhdOf5X zT4=>XZ{A;Sl2;6ZVjHR^D)x0k-SSq?Zu>G4Jbmi&I7Y)rU42@>57(^goe>;_Zf^UE z)~zBhc3xMMizXps_9t(-Smk=2!e4Q5r?{E?1hpG|7W3<_(w&Z~pk8pCeWjWwB*z0C z9;U9_#A-k~6`MFR+(Bxy;y2?TjS}n+`2I+wz98T(IV=~=2fwcB zaaj^^t6&@vJ@l<8iX!GiVM7gHt-t!XHjHa~K7{00`(B(8gTn+Rt%8$s;5qXyFecjX zTTsn9ZN1Mk&9LMBp5@c@@4NVL-=Y{*M%f;j%pIL!R5a}iUP2P62`o^fl|R{S1N-N)RK*N6DsHu6n&DP`#wB*cMkoXI7TjsH4keRh*^HLWZ0{>e$x% zq@qqV_>)cih3rw~LmXDiX8-gq9btRXBqKj15(z(IuI@-a{z<`cl;L*2jUsr0Rs$AZ z)S;CgOAsj-h=n!pMc=#Q)o9b*$W+-O$yz4#yuap+YQ#?AHU+$U8y*cdQK1~2*t~CW z?4uf%cB;ou3R~WWQ4etuS4cwlj7kY3G(LuI`t57kTlE$@uvk5dpdu$f(D=ip6xhOj- zrO!nG@ETSTD~DwTS=3h?i><5d`~zlivTG>wcgJcI21-IA7gKKkIq0+d@omPiREL z#X)i5XO9>eC}p9U;hPMyNyh#0=ek54Ir*>_ z+nubq=)V2&;^=_Zjk0I*pi}nu{0!yc!Pag3wi+VJ;0LV$M)^0bP}+W;F>l|aXCTUD z-7j9-c$Q(sXIuU($PKj-S2oNC5+8@A6GseHW;oY{w_CN{bh*M_bcZE|66+{&2U9Lc zi>d`uC6Hpkuj!ioz~tsX-7!So6OOYC;lQw@)~hfOc?2P71HaT8tJgXFSkXhf{@x@t zvIFZq!|uIvuHod|lV8vZ*g0-8$Y$I)7zC+s`yN1xCOq`lTQf{HX6NZ zL~#qCmCsuR3MN*1G(l^Abi?tA-rzs$zphLwowVY*nsYJWB&#@B=cVLFtkO;-j##El z)9c9J9oabp;>72)o6sAIZ4-on!VICXo$;qamI4Gblt=8X0hn#?LtjuNPXI1ZU+%T* z{v>!X@y8kg6SS%sPUPCZqfS+<&p4Y?*3Qt)9uen?jm7W8(mvs?#e@3AW`xXD)v!ptz%x?!+fn!KTr6D@N-R~Y-t&0=LM3pxtW_<_gD*MNOr_w!jIU{7iLZfDLvZQ_986yAZD z7L04#zg~}6P9d&Dg{WloToD4RI#d)`=3_jLUV6LF0j0;|cdxW7+-wv2WXb9$;VM#i z8Iyb{>QmIs^g6uHARP2~K?eEsBH<$BNEbkd@W3g)jxHKveFYhx=bt6`v>vK=b(QeB zY7NcsTrR}LN%fWixc3dfC5lwLpx{Ge4S>lY3vA!<#!kEl1=Rw|5aV4{S4wc1e7ZVT zj?}sywp&w~){dCq`&3QJh=XYKQi6`~lt`&i1D0~#a`f+-8T!7liak5xZR-Ad9o0sL ziSQ$!;w;18?&GY5_|;qqZCy9JyMbeld)uN+C@C3MWW2oyq`uD7XQ?4)v_npR-^73L z3YWHI_!cvP4IcQQZaALQSqIZRH*%zQAI*K~fW8(Z=bzbIN@hJK#dVz>gfSy-uzhRGpmw|Q-+lpez9lUA4v9wjvG(1CG8S#BFWA`|`iu(T-8RD6;n%NV zk2_s)7e8`+M-4^FVgUe<({npqm$^1cj1FE^%cAtsKSuOK{dHV4{vvu4YAi=iPxqun z8?2A)KypWl4JF(y5;~~(#3!g*%r?l%IBe6*`WMuxEMQ`gW$#%FE4}2FN+*0 z!K>eKtLusqCds5|iae|%ABR4_a|u7g?2M8D2~@rME%XaX>R%l+TU;teURVCS9id;6 zxAh}JDh|V*iAJl#-%101-=E9C|G*CigY4gl&ixz#rpJURbxBG3H_FzIvsXaf%0eY{ z0}Qd#OrjiYL!Tk~EcGnL_&ifnj96baa*j5cT|S2JU`BK^NviQhA|`X#cX=Q6Tv>oA zQbPg?3VR*a0Zs6U`(zA~UdHQlI73D;jL;Vt&k^jw06ep&&In4AA1t-@MB~m0YMsLc zo#1_@ZVk#Sb}c6P*M>A1G!G;gW6VMBfK3r5K!myAayV=j^d^I|&Q2d(~@zDBU` zokOVee^GJXqEJ&%`F{6A>uDRdQDUfL&S&GK?62vmdab4R&jIdhyjAi*UHr=kgG_{b zM|0ZfxW9eJAXl|v?y3Y_Z*R8h`lffEJC+5yF<+yJw>*Igfp)>lNjFn6RPBY^Y1NKGty?Rq223PHdQ5 z&jjvZeHM3E0syA~eE`TRc;DcvTITd+N5S42$f6SDNmEo*svcW>TQjGHD`d>c!d1?QuH6!D`6?b(i6_^)w`r z)myi?#V*17OPMd0^-ExWK&e*Q7EkGz!q9rwdSd@CS{*)7kv1UN_C`G;B#7hbn+spLzy0T?%2$YM~#~R0Ah6=IwyaD*sMv6+7FV$@VXvjhCoQ3a_&_BN7@83nVenpLRi$=DaLXKv=Ya zrVoFfkZ=A1nRtS9{G3O=*&mSLXHF0ng8W}mL(nH2U8*^A1{Hg12)uYb*O>uRE6_rJ zY20sk_jU>;hW1v#wT@6P#iENa9^@8#sHle|DOym4XwgHP3ENo9NeAE8XQ3T@_dcF5 z+?W^VtC+L_kX{=$V29$Ohc-OOcmo80Zec_ELs)sZOQRPoXx^IBUe$Wy*I3|^nqi{H z=;gsrCvG%jBCWFYvtWl8dvfs-mdaHfS&2qEO04_~ES}93p=jB#3Ngch#40eWU0nB7 zmqcf=x5scUi^aI(Vs4po@HK*OjClj#v+0ea+~);Xa%lQuOzJ!s-418R)Xqx|A75K0 zyQ}XPO|FhEi?qkf+YE?icSP-YJ!T7KX;qN`R>bZw@wH+yScIuYDw6nnwMb3`9a!F& zh9?rm1O-R2%V+V8B5D_5D_xO00T>u}dP--MiQHl01X{*$#J2a}*un?)_H@y?x09-v zt7E60f5xhps{~r}uJpThlsryHz+1;vLn`92m6Ufi1XXLUtS+?oC+5^`uz;x0;L?5|wN5z{)eyCqw)MR}L}JZ`z%sfziztm{`Kx_NX1fih3iBC9{) z{g`%*j#HC~?i081`Og`Hzsh%AU|78((XO$EFkzOpBlzmIICG}@m&6E(<=e^33SZY$ zRr`z*iuES0%S@j1^GT(#VK*n-HZ?`wPyNle-Rmwfg+dZONJ$t5R~)imx(xM;;jjMvSvNj!zHQE|Sj$Ehk{X z?xoKx1eNB-E_?djox-Gg(eTZ7gyZ4!?W?nYsR^g|m(@i8`8${_ogKGki-F6asrm;j z@yb^5Ip7mskr=eQTSZQ9zjm$^gE7?tG59{k=D>fpLBh~9X|{&e)g#sW`nIruir-$M z__j@3mTz(jLEyZXa~$6-c9m!qe?;4Y$FNVj(7`jjM_HtHNW38RISMt#1U{RG)zt9& zF7F`K5pZgxS8@5|+VqER^Z+X$6r*}5Zbtq>9JwYPWL+^L^@r}NrQ8z z+chnCI9!`_4@DK2xr^K-?tQ+M!`Lq;R40EGV~|9W`=-Y2r_hnpT4nje@RmVr*u@Ui zNnLI~FVo@gT1yq-9k%(hQdKDSoqSNKW6Qi$wLvyWf$&B(6o%_Q@*4Lv4IWg1D4^2J zHWbM=+pA{nM7cbCbApMe#FZJQq!v;==l30OfXB!#Ue2G1dV$URwv&jVQ&%qZqa?N_*Zi@g^zYnd~)F-`?Kq+5&D86)#FviDSQzks+aSluD z#$2&su*%y(IAj$$1|WV6O4Gs{`tncVRh@X)H4{ zCr{$->st7sxD^7o#gRLHsAt{p=vTy*E{@{u-zepvt)nvPim;+UIOW=OiQ7bOS4}u3 zZbp-}*OTvOMWy2_lnT;049`Pfy^7qQE|X*ukVNuEh(M~ATbnCl$}4c@M67M2}E+XV<@>QwJC_)d8ds_mgjdiE? zI_9-xz%7K*f}K3^YMK{v5&EG!XCh8a(Afk+h4|%f@1f+X%k>z~e%SyvjVAObFtLHT zgsq$%{#_+eNpFQjeMu7*V9A$adOXk_jAJf<=2Fu0IwO6+VG~uMlt{;e2@D^m5&U{g zBdboe!28m22-I^5^C<`lxeDzl`-J_s?*yeDt7RR7ubK#Gzw_0eSCKLpW7LQ0;o3 z3ZBKTpw}BSm*#l93;J!<@H5)wtX$*F(icxw%(auyvNAXY^t#6yf!~E_jbe@oex(Xl z4)q`?7Nw0m6YUw9fA9_~7s_WAN)aE4^!+(U>eq&yLj-FkUI zhgs`s?ZQ30Zp__5^F!40kLQw=>%8}Rg>ab}wz}>lGrXdJr-j(xb}ZJ|zKbwMFN0!6 zuKv~r(blCBa8agGv2QYVr7rG-CZt2LcPcUcilFJuiZ%k9Q;UK8(#^k{q_4N9=K*EC zXt#e)boYs#IJ1m6&qlL1lAQQ$%LBJNt;skp=5toeS#V8`rQ^*lNi z1N{;5RC-cTWuKgxmbpP@0r2u$`ZU7YODNcFfdWY1dn?lW_*OZU1YR{kV)Ey>u2otq zw9h`31E6GWCD8PIoRow){#=>J*cOG{V)FPZ0svSDX>P$bK!la3$V=LmX9*U44#d`3 z#BLi1?R$_5!Qm+5zr$ihmm{Iw};7O0emet@p{91Mv|GPis z=gA}9p7AuV&t!bKY(5r@&zHc#9SA+(Yx&2ckaMqiNKbfQv7a5F*qT$Yrc*!bSD^y(eFviNnnc)Od_LS{ghvx3zlkbRrE5c^g1UB z2B;o%lQ^e!chFwRYae3bw2eSNyr->_(3^L;XM>S8r`f z>cQl6kiOgzIcNL&#y9wQ*5FT-Yqb>!n-3bIP`p!bzGOqgy+2%GeY_VlXpPq(S|Qj6CtJ*l7fUo>G6MGF0{C)<5CH-A74k?VJfLUqP>!VcZH4_WiMx2I)C$lT=SiZ zPkK49W3?08({&hp#57hgNLKETeZ|R^+tD9xA)-@##Ws4tHP9g0#o40?p<4_?>lD|* z1SQLAPI$TzoUnJJ_oHaPe&N7}s3nk&AHB2JZKXc*GM>Cfh9*hU1&z7W6sMNj9|rr+ z*}|Go8On(+)aWDq=`^S1QjIxqk-*Id7Mqk>kUxa^dx!d_RgACEpIsu;VO-<9Eo@~H z=TS!xM=EA~tZ3oqK^YAcXAzrA2I?d3>*_$@aV}AX`QjsA03M{%b@;ZQ)jznqzw;+i za#HTl_@y(;iQ<#DO;~trSje|!8lgo11uU>UMeJ^q2M7@BXRunHjucYVl&G&J%P%KB zw!qB55U?vJD;272{A7N?V8H|xns8Ff!aizbH7|y8NGvEigZqc{c=(qv7e>tQAx08b z)8a68zg&iLuCbEgZn{X#?vYOK0u~@}FVVv^b9<3a7BesGo07J2T`JV&(ONR5xelIV zw^0fKuDxF)FqAt4E|@5a;$EdOPYZiQbr%#sJK7Lc_CC5iTfu*Pu}a_%$|Pza+F%g8 z9M%^&BG4B_ALm~?jvA`}{mY2E@FwsBKRt9-eTiDw>ns?{{k0>Zqk~2EN())h%WF8H zVf6L6CoN>J&NA9)u7lnaw<>!s>f#WcTFO?0M0=}$pVOOAGH<;5F@%hnU0QhpvUCuv z*V+ssCLB;l@aGuvPz0aBZ$CvqX)xrO+Jw|8fB?cI0aKAF0dsG2!mqc=XpZ7?r@@}} zmr(X^?DVI%PZHqbRIW2bFJM&5{s9Ch=j13lU{Af9bch^drEsC2`2LLkZ^j6Z#AHZ< zoy2GyptbC+9}{lUHqCD#`PllUGZk=Dv~!(Z6zdM+YG7bO(+m1`!6Pu>Ck| zGN{H=E5R6}(2(qz3;m5n_d#0&n%hZf0{8UoP#Nm&Qy2 zZ5o(Q|5}KR5>lCR_l`@ra3Ej1HW`+~D?rCu=S00L7FGrNHDb@9d`u!_aO83GQLGgF zIju#^5UZ+k{yCD>y@@ZDgSpW%D1ZhT128BW=~R>LyOuT8oL{q^-6Yai04?_n-D6>( z?gsa#(I(_Sf7MzMa5LU^lnfWq$H~+_$w~LEHcVCtM!Z-+hno;SXS-cbt+gr`?wBI2 zONgKhcNM+sg+ax#YboPN5`yP}Tq>MoS3YNLw-z2Q4Y{T&7_MNc)MJT}3&$sis7*;4 zxi*}^-_^?3c1CKw|M*~vuA*ycPB)jX8auMDkhLRcq(sLc_AoXTTz|IC82p9#@B}K( z%bOvo6AXNmoH@^Z;T^0~JgPpGMZx8764If!WgGFamSk(Uy~bbVO95a z=ujQPSzt=s@6k6EWe3uwu*Yycy3GUz2Iu3j!zd0*q^{ z?W&0wRr4SyLl*6_{8E__u{;^6LzrB9Vr`=R(a9M~>Ul;-%i?q79f(hY+tRL&t8Q7W zD9m-Cn*c+WDyC}6IhA|GG7YDSMV<{15rmXq ziQr=}+1d)Q(*;v zZH5?#JU7Kp^kE9{?ArX^8soQe(c{iaOwf|^tnHJtv3Nr$y=oY|ir2;qHq|7Ale9UO zjqEkU>+9OA7lRH0>9^S*szrIUlTxxatwcX$G7A$Q#hA*U#%?PArmw7H`po}nLXs>A zFf>3g(|Ln%~{5x(95@fOUN4Ah1A{*;^x{< zYJuWqeM%nF-sb>%N9rNDT-wNIE_}A&MICO4r*vz3N#@6PtP;lnH*wN-tEXe6n#$-_ z|JqS~x&^l(w`xk>MmFy@72q(-Ih8G1V%76rw{8G5Pp4W}_bC9C@(opX$rN4$JN)2Y z$&ZAsfAs2_f)82q&RWU!F>y^WF6`u6^bpATOse4JQLTp}lf;~c{T6R~e({AK@2$c0 zTU6EXmcXT-i@i=YTcKUWUQ-n!F5gX8Dya@*5%PWOdTFs@um*`En`SQ`ZykgZ;J)hm zDn+;*A5k^Fyop;mMVx&Bx&pkBB7=fl*a)I&EmOswf)B+~F&(LCQIYG6462jTTX?k0 zR8+!X*)f&bDY}1IZ4A)DDchrgE9qtp3!Kaa<0ua{fIMY#rHk0)_M#Z%*QLpc8~GBm z+<6MLnHC>@?zeeDiyPY=T8i1;^{^4N7eFAFZ{`Q6ncvw?oDx;j3?;RXCL9+H>sK zK4MLqt=JOk&|&;ONA;*fP*!(tq0D^?QIBneV7*_qL{K_}#Wwg!o$m3s9t9@s9tH2` zB0TE25V6yEEqHcK%)5vdQbUWf;755ILa*G`O(PSSFd_*rQmcU2vw}v7Z}sAn5)Jej zg%SgmhS)TVmrCI8LzwehCfvYUC2nAeWiU6}w9e5`O2+1~j6A$>G1B7qn&#BMxoz&IJ%N4N#xM}A8{k9S@7>?Vt;SXP zr?P}Dg!RCBv49h%yx+}xxFWxIal71jQ%?J~yH#7yh?BZ!SMaCC)Z{y768?xUF5U5LZYB?$7FI)F3 znMGjbqbhQv(wQ+yh4j}z^=42*I_?CRhVY6Ea54rWbRT$7d2hbK8l0d{a!!lUHA{Wj zHq~jhX5v1tP;-N?HyT7qr3=m6qviUbZ?q>NC)%Z~(SR-5NKt`@#R5su{F(hxfb@Li zKBwv!+|29-DpM@Az@S57y`FXo)|8BQWW;!{7ER{g{An(Yb|JePv1HTdKHo-d8Y(kx zJfE8lr$iP-ljTZcb2(>tYvxVp$AT@?+>ED?EsY)B;VMh9Dvffhn! z9F4&+H%&UCGQ)nZjCS0I<=fRvS5T$KWA}N1Nr0h9JJ`e&-O%U}ST$~_rM-gjX0_3T zle8|x59`opO++v0wktdZyEmjR&UZ4%D=lS-EWf(nrpu_8)sspY+moJ(cOB_>;PM>{ zzHcN<7?OktQg_Q+ruYLT;yvN8cUGfI?>F?RjY7Rst}caU z%xmPJ`OVefvtYD`YOtj+`UBB)qI$B zdo!X#UT2s7il}Iz3rLQ~v3mFue>jaEI*f-$4xzZbw5Qmlql`de4x<+$zFA}lM(!<) z*D^s_47t<1$X}OdT=CUKI#r+{5$KO>bK#^Nj=k!C05|rOgFk_&f&FS_?UfOxR!L4b zfAqICOmh__5T`FCBIp#l@84>9ar|8kqc^aJ>`+Em6wK=yU||Fvg6$0*162OCw{X!n zb%SIx{|(65cKn?@JL0+1I zpxLZel<#pd#;D%aC>ET(&2|^cnS2$ae*XTtFS_L}9(hm@NspaZppbvX?2B2cwZjEvRmA!!Ft>MJszK~q z_Uj9+YWTrQ-=mOv!CyXl5$>^tdcfIUf$DxB@|Jt40Ohlh;#){FSW7)#8U$|}J~%$V zu^Rz}DWgjuwZ;0VH!js!%MIuu2*jjRFL!}4s*1d`?3XpaBNp48Glf}jxuCy3eW%qS z?77BmHLxB-rm=CH67MilA&mpD&lYDswo`WCD6Y=^4?>-i&1wZzaHeOK2!e zHG2kQv?Gpmn4Ubv=UjaGu3MFHQ~RfXa*&-l6mP|Y$G%aeU*}J9)Qa`$rM|a!IS*ppQoJqRVwR$C*lJ;MY&2BNCf4vOLbO zd6vZ4)V3IX4H1eshy06Fg&Tpu({nS}7cM#@y<+%V4?+mKR87NJ1l@xw4}o6W{5ZqO zOuWSil~E{M!gIMFmEQn7B_W5fWKwd_lg=yoyB}y7&|nduq)QG=+?K)4sLiMyy%EkL zxks%9Qz9+5JTlLBuNc1jFRmy;Yd?v$)THJ}$2RVYbnm7mo>+EvxQq&0?(&h~IN_s` zusT_>?_cY_b>o2_hm3w5-hFrS1`eh2kkojGbOF}>&}k>b-#a})#HXI@WX)a46r!7nedK!cq z57J%(_b|}&=wDwA{WPU=bO?o5;p}ggJg#e}K3F-U+QhI$=cj;tD^Ii)h9J!&I(Jl5 zVU%!gLUBrw&zy;;;CxfAH9P8+P26Oh76v1bRMl0suKV1g{ZWqh(a030t06H$R@TQX z13NVy$#76(VG4s;4f?EAaT_=t?j3()-w^p+j=y~C`b(uNIq1C$d%i^iZ2LLAjTZT` zs$wMu{gu#!m(SCm$3YNn@c@GKOH4LHhM&~($8b9^q!B1nI@k^1bfS$n6~fO6MjcyN zb6ML0t^@I)CAg7wO`b7n@b}W&>6RiNK`b(V{wqxbZjKe9b==3G*>5@de8pAear%u&wItlEpq}K5yi9 znOtE?fKn9uFw`Voy4+qj+t(i!9f$O|M>aO-+gcTDXraH(Ivgsu%6d@6rECc28XC(u_vRp_jwa3 z5~Y+;nrYfZNPxw!$VcxU;&0_3$gkyFI$RBP?PfY~0f1W=L+hs-mM`Fx z@$Y6{0leR$D9x#`GJxZ#TlSdof-Yk2TmB81E)iWR8tY`66pU&zf6vK=a;~i+@XvUU z*`bu`77#u2sVBp|)5i;|m+Ro20((kAe-?!)*7wJ@Lk)0m(@j2c7g#A1u2-xxi_yH~H;#S*S| ziD&E(f_%RzoEqbwQ$q2kSY`q;1|CSID(l)zsT2&^`((HL=6EY;8>Qwp;OQhaBN&u$ z>#kfUGzqsq94hcmm6bPpST4exl{SYB7!98ED4=gy#Oo1X$x%d&Pa9u&;eD5JTxTcR z2-&x}^%qlokYJDcoTYCItvnX> z>}yc5mT+jxBLc4e(9^l#D7Fw1yiE}0J8NJ$7$8{aC`k&zJj3!s2J2)ET8D~OtL_y1 zk+Rm;MqRfU{}99FUtb=SHo@JF5Qe5rAI`Szez1m1T_fZ_bNrfcIUjFZKALJ2`Nh^6cnkjF z7b%Dv$$J7`i}eG>M8QkCjENoUOpU60exnCbbyodAhbYV;wXPGhdem@zZ>!F%Eifgg zG)_q}llvbY3zKxe{1|`+HKLw=BRj}58sc@|2|iD;TrlCii>ApNFW41ZsV1U{;k*iH zjwT|d;h%oU&f0b(J<-Yi6-k_|GWUm2D|Lm%%D}7Ct|g#?@n8BZIIe4>huVNGppm(` zT!sx$lMofixh9K)cOBC(g7Q#8jTZC<1gamFzYrUpwQiK(@oXKN1NtGXVrf%l4uW^V z-(iaV=t1VlsZ=PEnKfzotvt+G*p()v3by{z%l}Fq~E~Zk0*&G$cJwGltAB8Vlk$>iXlgb2_cg z3O=FM{$hFnC1>yYcVXyv5@KA2b=CGc3!q0c?7N9}V&v$$9sxxM1Xu(Zf$20{@atrF zIrbzmk+lbf^vl~(#z;juDkIb6M0&uJKht{8IpPzI^pS@`Qy{a6~uJ}vuNDVm0gzttPrW}pGRM{8U ztm09Fa9lWU=kI1+QnxA?Y4L||Vh?i*UseWPMmWx@dD?JtlTquYV)hpPH3DC+Tx~4# zf4oyNgUSK{L+fhh2J=$tw$<06lxN`)osu*bD9I)XaYRbvj3qhB|2kf%SaZ7(!axyT z5zQxot}MlH8D%59dQ|@{3ir}mCDaIaM?|OL!MxO>O7Ee1Fi+Fo!@rGk7QLB?Z2MP76N z6i6))j(TVxJ~;O?Gg1-Rs!z0(BwOdBWdx6xSY^KG7mBoMX5AIX9MjUN6jWtH39=t1 z6$T^m_3GtKgp{z$lojcU8N$4xQ7N1Z3;N;)EVWatMc!JA-e<#s(b4p6ZkrYC$Vn#! zYr6OLs5pM6XW(_5>->khEDzqyJ1p79OZ{(Q_ATP;-QN-^7A09YE}fGEoBS~MTfF2K zZK*zDTVJt*N!E3Iyw%QR37zI?8b4SC@BD5`2O2i^KF2>V#SbGPr>S0Hr#KieSeQ9o zMPb5%op<`0m_V8sQ+BE{2}T#W{OqGg!?xI8Mj=Lig^(rW-ot9l4xs)v>G?B*GMnFG z#pc+xZPH=!D-VK7ha8?!DT%U?)mV#pZ+Gzmsw5~52lm4e@YT0G8L=rDqf#MVi3nub zqA_Er>SE5nG9B%Y8#mz+YFf71*VkQRzAJ}xoy8!Fzq`p}Cm(V>rIY5#wJy`q zjwu5#>p$-}=ctl6IpJY?xXMjhxy(RnnNs#u$SoSY2x40vX`KaLkM@*V`S3+>*v2;4 z_VhPZQ=45DibY5CkBL=bmnti3CQDvXc{l2jIE7|zc~pnX!7`pyxZpEy?y9RCVN!t2 zM^K-il)VTB<+28;oh4hop|$zmeu{_GP6$-jXwxv^M`9AaxKjv>VLT2*DN(pzZI5ij zP!_U4m%+x9vHqSq|B7z2bAm4WRaln5Zce3fS5o&qFN33LdcIZ?( z7HR&d-d!Y#m7|y(5{c@X9G(4j8j+nXiApjB(QU2wWN|A7RYU0p2pt^YPD1a<--@xQ1?Hxcwd(`L3Qm zjtmMgF?vRkYXxjA| zKFIJqu`j}DEv&fY`h2^uU`AUnp0+yN9)U0Vj(Qynde^DLUeod22fVPPHhZkS%=3i@ z?_uZ+U%Ju!+({#%pzR&;{!$%wei@@x;n4&8l-rlCgWm6 zj@fyo8>q0gb)54HZ!eswrQqwUiTxxDmEB$?38Bw)uz8Rn zEvB|UI$kt^HhcBInypEop!tn@Q`BG4<2djcXc!GYowVhTyx;`Nh;-hLRHAQl+I*$f@rN6fA-@e~6K2tt9mYh!E+7%@db}^$ z(8JZoSf!^lorCZf?_fo!#y)=_2Do_XT7h&zV~l$A$A)>j9(V9VNy6#<@pDqZi7@Nq zLo@6aEY<(MA13RvYEBX~0)ts0TsfiCve`CvLv;qLTV2Zt1U2pA(r))>s;7hQg=j_o z4gv|DQ0~Gje+MEuzyf0`I3CL7Kfr&sl*HF9yaRs_d1Tpf_*kg%y4wowowXv02kM98 z6f`Wt%Whm8&-c@a6>AeDL{GJoaf1pb+I464{_WxGI-9-`Hb9IGY*x;a871JIArfu8 zajIY5g#4iSH8lEkjxhYawQR&m1p3Ko+q<`aQOr09KJ1~Kk-4+F7~Q6^zEOi^(~I13 zn=%mjDZ5BwQQI>CL`FjS2`(Omb^=+I!^Z1ynEU61Y4{Z<2eA-UP*(ek00X*Z$SiQL zLmuhICA;;+BHrQv@?n$JtAh$$y4?G!>|wAg{P!0P;WYV?zppL?Dn)N1XEpTs_V|-I z9ee&P>P2Dus+AgdX_CAs|58&pyuQ?DYa;F17;CqtSH`+$mlhJ?qxABQYh5M2JLScB z%Nj;lEQ`{Hu4(&rX;QE_=RMgjgJ`gf&xLN5pWLJr4fZavSN-?oP=85b9%b3gIL+Em zk$k@iurNC+Dg?%&2>KLGLaR%R7YVfmb?R4y@=W{|(0T>u*i@>9$dU8MjHvhn=_~t! zLQqlv0}`AcjD}p>Q z*E{QNWn=~(1gTV!3H&wP&UW~>ndDop{sMHs{!2hX!nAn%ZXid{;*C!!m4#=$fQrewTV zc-`W#8$Be;vU4lVAf$_eS6Loy3J?B%Llv>r@}K7bQniaj6fg1QhnItT;8x-pmK@p9 zE^U=L*kc8{5MIee5*iMtaNMPlG?I0Xg-Eixr4vKJ=>) zB1NzU1WARzBUyyEW~kd1i}N#26BZPMW|}^AdjGs^`nX;1tk|S}ktXh$Ib1KnN7TTqvZ$fICFesvh{WS^hbSVLXA7s7xty<2&mIoJ--O<<2J>Gmx}^^ z%Q}V5KC!#r20XRUIyoLOHBOZje{#gfidEn0W)o zNI)oINkWrxPQ1sJi*kFir3`}({>qCi@!jXf9b)cpSf->v1Sh~lWEkYFko5gwK4gr` zhh!4--BTT>nywGq(J=|EAT-k_)tb6*z6e^;X;3nwnEh>pLKg|Kd(W^^XigJP&oYKa z5q$h+B&M9FTmgDXaV**aRJ%=ia`5b<1gKjJCWfskY;AZ4A>xMq9|3F;lkTjNd^-DH zMOD1jZQ!$XgGemWwH1I#`zL$IUCkmf9BX(DQ-u}XX7Cqy=EYDQO^sx2$o zQ}FW{1tTNRjr8pe`optA)aGp;MNtOK(Xo444})et(BR`EXqmI!xk%3d5!H1H(`(X6 zSh419BRrkCqPo1`xq$$N%e5g6Z|oME_^x}R-sM|OWOo5VPT4xWX1As@t(Et`YoEib zO+uh6+ZU?-WJzT8W1>uEkB5@!)x`h$WCqDKO?kwND;UZITX9! zR|qrU(S!HL8o2zTwr+mGb9BE7zd%Sr95|N)V_0(rt5!zUgzQ;iTrf;PZuQOPj<1o> zW1XdDE=0aW#*AD-5O2bPD`891;r77=mHanc#r$)wUJ}-+JmHyWJ$$4H#jz z?(1iPY}d+Rp_1R;8zgL&%?WWPcFRGMHj133(;_iXMy~cYx&V5Ic(L(c!6+U_izU%= z(kta1yk!2~zm|&B6tQQIuUc1_u?A42f zEyFJFYumR&lJp15G_*Ipn###;x(C-o`;WoB?g4!T_)n@ziFN`>}rZn-*IR>!3 zg5x@=R@>A=?$Uby9g`>s;);7ClEh)?}Do!es{96<9x z6#R?AUz=03f^MCtbqhBx!3{JyDQB=MYP+4EzholBm4GEWt6cDnr4-e)3+#&@22;EW zpvZ&7?VLavsGaqUy>jq~B`#^y^z*@$To~qpg#I$_A*VeWc2Rj75&Gz!>oKz__}MkY z#8EI1@exT-M3}jPc~EDaH^k51B?wvBZA1e@syLO64&=YOy};-i(hD&G8t zXFXnc(N3OMf>^RsLS3a#JRD!3JwF6JSg8h4=Wc~jZsgu(Xl7rsA(-aW$X~c#8foK2 zxdj1eWk5f2eV|JYN!HP5Q`|E>dv0wDLy*9h3&5!foUTV^T8b3kx)ZiL9+j(rgvE!< z_B_0I1bpgvtf=NCK2bS8b^;DBy+?yuO4^v3_4kYtiWdyeW@UWfezcP>E!pfv-F^PR zRbIP<&~WP6ejDQm9ZX2d>HhkypN#C6y{8$C8Zk2ClR9%!xL>G4Olswmpljg`qb8H= z#laHQg>b&lcYy+G2E&nKZPya#pcpxF3Fwfw#C!geUoqgKRn0sWH9fRHdISv7G2p30 zXck7_ZE#pbJJV+K3uE~YZs8%rwZd+gU!iaB8Jh}hBWFA|$uPNUzxc=3D@gy*WQ}~6 z%p;V>ayLO*P4_R%#{vLex8P{l{1-#UHYp)}hM^w^KYFuSzPtKEe$mi9F_~Y~P4IU_ z&4`F~40<@A^nu}E6d;zRl|#KJMQ&?%_}+bNNwP3%M*LCxLn3MgXxxx8_b(DJskMeS zc64eh?>%t{hVZ5 zSgQpCEwiK@{2~;N!G{p-I=6b;^XW>f{3$pkt8+dsM%HK8Tz?^Pur=9xZtlR2POkl@ zwAm?_&ng}%-ksP7Z>tLItCe#UC$K-Z2b+D?*M4M3s$=k)LKXX)g|1|FLCyy0KvgBYJ66?~XjBDEn1)3PL1y=&bbA#iB0uN89v zp}#>$wwMidD9?50NAJy}Z}7O))CP`mPE?E)SVK|x;^Jx@;d7y8uZ6@m3_0e&0+;Sj zrj$Yx0))6BC+|;@h8bXtZ#i?r+8bMjGm4`0*@{9x@|*Pba+82#Sm3zg9!jn4yk9hP z?N2_Ci(V-olPJ@EQ|#YI3IsM*Z6Oe@l5qmvcPT_Yea;DDI*ZpIA!8ep9cV&T%_(AA zEOu~Yl-Yov%v`m&S@_nHOY$O&kYhU%B>{TX;9;(z!mmKUL^6!bbV))J*Om}oz}qf7 z@lr$1&yaqszhoF=qN{o`TrlCk3QYr55c|KstH={%sTh=N3y&r4<;0*bj#PTK^jEC-Ma~1EC(=n|PCZ$q&L3e9S_Trjv5pIx)M0 zC_n2N+yVk$RxT^Zb4jQ8x%*XbM5AC-{y^y1;Mi{kjS8>w? z6F9%rETfHS0R;4e)%e<`@xMaGqkB5scab+U=NsmFcxe)s!>&?*k};(M@TU zO70AzfP)F}vD+~-4kgut%u zpN5-=;<@!WjfpuCa@qb+cp;DYg-GrL;DW&W^u!(B0P@W#>dkny&|${9E_3AsQyDBb zx-09p5(g9$2)*Bl(hT;a^)}lp%pl>pYi4l3cfZu^cW+=B`l?h$p`%x6;=y&@ONVjy zLlkjK<|hn3t`k%R97QvyCO-{~W1+F?9=zSlpk{b)^WRc!d0rWLQXH9f4@!?3wG)&Q z;D)fFz9}}-h`*56c4}OiM)ENu?q5J35?u0MF&LH|%F8raiv!ve&sOK;sHr+t*DDCB z5ZmZW_c5A5IzQA(2LOwa*8*kZ4PG13wY#>-@^nRaB(%I#nE1RKzSdgT6;{wQiY{}2 zibXfcVKS2Tkr540$zcF-o014IAe8nb8OhEd`FsvLfJy%~xB^%*0RY$XFjf>=hL%(C zfepo{KhNszV2U{b*z&;l2TN|Zk>4GjH_3JL0Yc%u^iRpJ%|2sR;vMp>ldTtUw`KfQ zI|bAa>J*?RSqK;cd;oaU76xur_|i@hwO&ivhTTHPGOd1UbB=bI7444tB9lk{T|@J> zSz6heuiKS3u|8~M;*c?Xfd2!=1A5nY^lTFxOqx@ph4 z4#E4}*K-@)-z!6-ALq3HL({uv7yHQ&flOj2*A>7Bd(3j-RN67pEt!+RhK}*WeSN_I z1$t4*SWy_u3GJ7xnf1{?OxCUlUZ?HNPnZ8#k!{MiW{Quo$Wa+DY*E|#xSw2gm#HBAR^v*BS{*A+e91Dtei|`$3HvLy9B$!wK-lrNMfC) z!ve@dlhZ{Ayz2Q=e5-P}dOi-9Jy@pR`cv}R5MfF{s5T$V7t>dwTdNgw`UP?fhJHo! z>HKAelqLy$g5z%dvas7$qA1wW_juwa#ws;#>M0^OJWGF@%B{J3yX^Iqq+Rj*v$T{P zu$_T(R6LVS1{mbZ7a9)Fo|pi7`1{+48j@tXBRv0-Iti3Zj6yHh)%cc!o9sJk#%<$# zr_WV-{6-z}a+m2p${E)Rp%Q2am2#mMP=(HwjYX@|s4XanmbElhCax~KN0lU20oz{m zHMQnY+*b{d0jCiWa9|gWGvR5FO&*1A791re%G~l6jq4Wv>gZc-uYJV{EIcHG zD5#uhkh0_c28u~xMB-x|ji&r&KK)zYgNRTskjF}5chfBe5hg2R_RX>9{N!zC3t%#R zKWxJc$mWJF)vN=`_Yu7R4F;bl%EC0H!`7q|=QVm)A1%Hj7$}CJgk$0Q0=50%m7ipS zIn?mHO!B0his}ojT3d}C>12I1QW{c9xL)dWoQh#}Q~`b1HnVjb@Q4$xVTm7n)9Dnk zKi;vze$Vu#Mh;7im5^P9^`ZW8f!uS=-OyX%CPBjT#8T31BypfPtwHto;(nK>k~Z;3^2bPX@IXQ6+&z*{87cR!f?V~iwX1S8+NNy3oT8Uj!U7@&B@i={p z^M24)sw0D2F%f53-N2;NJ*vQ4G7Bd<7U3xo?!VY^d807Vszi_5+h%JVj-?_eu2)2D z70JM}fsLqREdNkJ3%sPUg?*ts%DQhfx2C191EFGM2tLwA`Efc8iKz%Y>g(G-^mo>P zz}*EJla+&cnVwn+9;23BOxhk!_R4zLU+klo zlZTjosQg^Q#~BNPZ^&PEp6KYG!G9r!TmaRHf{rfSK13mba9Wg_pwv7>_FPptS{6gt zHf?0q6mZ;LEhOcz}*ng6#+>=s2GF_sc@i%1oK<* z>@9If0)dR5VgsIURFH+vvA^mY-0y07uk{Lr4GlaZABQ<$fbP`|6FeF!A*MRr{$* zty9)ZNoM7=^|zpIK0J^`^JNuW*uNEhPJ=0jh5MnvoZD^8?-rWLmz|dK#e|ErWg&#; zn0YJuI^OV?ecZp6#0hR!+4~@XzuccAYTDt l;5RBqLA>q|*P>KoM$E+`cCBe#>woTs3)@A){Tdc5VIJN=*b z@7Ozz`fKw)+JD3UxBmU^2mUYk56XUwdK3QF`M=4Zb8cz>h5iTp|Nj5C|GNL3|NrPe_DBC$`VZg#`d>gF{C%!}fPd@t0N&}{|JMHv|75*I z{2$@Jk$+wP4gCxHulld?zgnKt{(1Qo`K#*p%RfH;hxoDkqw*K@V)G&@ZL`7ygU=ulyhPpX?v_|IYktd7t$!@1Lyy zyuTxUOZ?;dzxBV^pX`78{{TOSe;EFe{a^eq_uuTl_i3VucW=lier z-|~Oyf4hJ5|7rRI{7d-n^-t`d;lIZJYX9T@ukADM7x(}FN8_K>AG_b*|G}T&AIg8U ze{21;{r&%fdL|%f(ny#8Ke@zRj($SexBGbA_a_OzZFif|WGxCLp@_n}`{O6T6G?aP z-_>d;GE47+gj6Nkzu)G+quGqIc4hvReB$7uBO&2~oM`eLx{iU!{5X&9<_eYY(q)?1 zA-%xH#9g0p^y~H46s_#m1ytRCwrVH+g@0r1UeAAjQhi}HyytAvn^qvjbo6$sD91=w zJ@v&xAgeuCJ#SxgQMJ`^lCu#m1DNX5mKZ^j&t{|y%HDA%${hZ2DyVk!oKp%0)}v@S zCM6&Cg)6M&Q|LXzEKqL4IcdvxDR6b1JcV$eNl7Qubw04QM>&_$gt`RuB%_j)&vn!& z6mzC9_E+>7CtpL4OXu)4Z>lFKN|SpX6+w{C!7GJni_O81%*S%{nZ)99UpBae;7{K< z=RU*%R=AC0hV8ENQI0&CU4d2^#MJ}2&L;+v-6bJs<2peV#(H<*hX{Qu8%VHn1#i4w zXU1-)%M|NHd1rUti?B+hUG<2}P#9ddCAn&K{C%U#H%uV?W4-@3`&>At_PTCzs>vhtwH6&Wc_!oUL@8JVA(jh`Mb3vCUL+~?Cgvw7IjW*;Ax1KVcD?ZAiI_X+#Neu@ zjUzz47aoeH8S@>;@6n1J*GFRNO$=6sJm?9;+l70t@pm$)*ZL86T;p@zlfvH(Kb_5? zV(x*4L2})R#?B#9Qr~ZU@~Z!4C>_t--;CyXDh-%$C)!NXMvJgMp&$O77JsoaaUNPA zfMT%KJ@`cwY)_hQnur>efd~;KrUQFC;v%G`R9#-sUWO(UXOyYrhjSU z2`)(io=L&+r@-jiPu9v)`9yH(8*>}V6jUA*8BJMucu2?|VU9$1gDA5>+AD&5cYGCA z*O*`r8cN(Y;M?3$EkXYUyX;4uj@QdrAtIpM*_5zAWnwDSon5CW(}dA9kcQR9$#-N{ z5(mpv?h_p|6Ws6JC}oJkz5FNEY~sG-r05 z19pdWyX8M`rI~wJUC9oeDTT`Jrd?4bmvZT=Hk@)DN&29QcwPMadsQ9>*^tHwN1 zhu~4Psd>i2tM)0myk|9u%0R@$b0f1^#+u#cQe`5Zuxof%U%yYdT6i`8(HjbKm45jl zz5VVxZ(vjWBz1^6YEd!|9n8^vZug>lbm2%Oa+-7hvN(KXq!lP+#<8#wouzZ4Bv)78 zloRBy?D$X9@jX5BM&3QIpW_|}#??pB{)o>>%ZQ$8trt7_#FA(A^rrFX(gU9B?m2*| zf+KZMtP2zAY?vABl0Cahvq!eQFt@$fE+A<|0`+@a1mO7+QqS6eTXm^Atbai0_q0Kn z#>Ogp4yP8%uFdOrJ%wvvN>{T#5M7tf=$*T+P?P;IQ~l~;{e|!ONR7+gfpgfKTHlAU zedQUkghvz)ka$H>|J_W}R@f#j3s44E|l zl(OA2U+ls_drPKi`nu6?jD<0(Baol0g~pmhYa-#I>aw7ogsWoaZwuDQ*Hsbc!8Jg6 z+3(`ULc(1enEu;XffUFNfau4DYl0KADwr*{;t1Pu>OP-NtAQK*@$b6BP;+;_(B_vJ z7jY^xOJ=L*-@cr!r(72mp7)$D+iz) zVDAA!Q~$%9_d`1SdBORv~F9|x)zpyvj*{L&Ojb5k~Tf}7W+=Szi=d9 zN&q*DN`bSJFM1)z8p1|+m@KV(Pl%hsniZ{v3y(%(=5M;0d;gcc$K!QgmeZQZ04a(v zzSj4fBPg7ylyaUCSL87-;{EHn@k-5yh_ifsYjrO&ezpbiM_ci+qp@%_Et6ZZ z(#1Bm(;tc)Ya&&wj?`kvx^ML6y3qx0qyi{^gMPn2s)AB)gj7T7r8}J+on!JpiR9I-c}=ZZJSlzG3txo zGj*DE9-gKPz&sVYx0M06yYgR}{=Af#uN{JBhWTy8K9b~0s)DyfxBqXv#_5_drD+ym zO|D+7Y?*`DicqA^zw_EO(^@N`X*Sih{>~>t&`UcEYQu*~rQ(v3yAqL( zRp@|WYcUfH{uHBw`BSTj08+vmEgIndZ#Vz|{{R1>PS2Az^*{mv`-NgfQfH(90d3|Nqa^Km6#5AD1isMzxRs z&N7quV{xb3u5yIflMRA0IOG8MG7;{TkR+hL4cWv1Oqxp9N@;oa%}YW7t={5 zJ6J0_@2c=gxoPjn+6;DT!v@X#D062LAiXewJ4VJ%M?Ow@6w=(I8pYN5jO4FB;)ah~o^ok1{I--%(JxjDg)@H+JZ=ex2l6{4VN<$|3 zX1i+;vNiMcB8ts%$ZsBlsq|ti!xGb~gIL4cl=gtNjPA%_FE@dGm!b!)*cHU`17fq5~+Ni_gjTM#%gkU3@D zG|#T9$RQf)v4_6nf$wEOX826n+L2~sj>NkC;6WkqXM?J2{T%kgiJ$V_&JP^6oB3*R z0;On~2b>+rt;_%D3TnKqdKdp z_-oIw$;db1w`p7d?Sl&1xp9@W>UDVOngD6LEn>%YuSn8S=*ZF9<8ViSzXvJx9IEga zla_}>^e#ae!i$Bc%e=>DwF%q@qRO3c^_^j>*0R<&wH_jE)c^VrViHkiP3~cezU?dt zb{I8>#`1l`Jt~Qy@oQiJ1c$;-Nd)2mwhm6ZrUJ2|kW+25ExKO;ov^limi&UTS zrsQTViA7WrjRD{nkGZZcAA%AbeywqxymGZ(ZO*E23ba?4Kr~Fm%qQo=v3-8Ij})C4 zq_Vm}nlQ-mro?-92@|o3JB$A(BW+P(js9$ryP_Np?W53u+LE`11X*Q`DmK9^wMRGv z^#uVV!SYpHC?KSwKXQDn9O+aZEfqL)0a13PdfxkL9}8*>(96H;Bc}IsPFoL9g~R)e z+>BI|-4aIv{ICJZ$V8x0i1iIxSNfPV_&#UsubC(x@-1a1W5Nl!WA7T9kb-pWOM1q@ z&3C-+XR*Rz)`H@>AG|>Dc;P-tnk|PPp>xVa8NSIGVK+D?ggdcKh~t4zXTg^}gmSJ8 zbO!JM004%_y5WzZ&GOzeP~g{*P(h1z!v<4?J=dCp|F9|e$(pH9jqz#a&T*^0Co$9t z6C-mxT+Wsl9Yyx>@TG?tw}i2CkWvhUNe>i}?M#CsYYizk-X4p#wX#2axzy{!PHm%5 zSIC0Jy6z&sd1h+jYYU9+NY#AQv}JK*`7)F)z*bNmfEMg*jB37T;TDw|73OzTB||Hk znD|eyr4g(HJe$p{!Kbj{iK<4vr{cf0&-^f+4a&R#05cTN3rj{~91YiQ2d}I?qxomQ zj0^QK4ntaEh50hMBF26mirc**dkpknur2q|j%ICttXu-Ve9r_7PD~IB$7P4^!GGBs z4H%D$%lXNFOuRS}wn5U~xA=2clKY+HX}9~Y_81H?BeKwy@5{BdWAet`>-yI;d0T=X zkmd9=&ULt*uXNbnhiqPJz01F>;A#_WOBFSxWml*0zn*Ac6Z3N$VLCF-p>bVo+XA>m zW^nEs^7T4y!zl&ZTtKK9Wl!;7jv{IeHie%=Qov~vG%UgYD4TOY;{VcKM;0S|rA}Jm3$J~`M{ygdZmT3wIDCm6=8VIcP*5ztTBE42SNfpP)$R+*YXG=#EY2P#0 z^o@n%(g^lJx>>eAFhE=?0eIXlGs;YoU^t$A5Fezx?m)bz9E=RX_dxPw-7_Wq@B9eB zc8G5?T@#wpI~I_=D%N){h^y5~yMj?>W1h11Dh zVOl(o*7R9FfCO$u$E^_Tz-I;fu#&JNkEb6~Rx%)p`lhbN6QE-K5942*!D{hc39uuu zPJYD1Lj?o?jmEa%<@QVB7t1kzv0hVEQvy3Cz+Wv{;`m?KMiB~)+}DbD&$X;c*!)nY{@QO{PKMy z8h}hPPODeQMuOl~|KRm-rk;dP$s8A06ndhoWXVh~{SdDpvH>**rxZ5?#ODZb3St&d z@Nji?z{UT?zbN1SVU$7aBKmj3$n{sB(ly2iAC*d)Rf^ zk#u#3yB=P#+01i1b&JTb;6c9Gq>`{K!7i{hjY!raGnP0_;q^Bu(n`z|Ew{e#mIu<3 zO^B41_*<|js&=HBz>q>ckNglDzM2_ix4gOEluHU#$bBf|ej!lc?I43k3;@nhXvY6f zlQauY2h5DXU}7p>jlEn{m=LOvt~-FAy(T`EHO;G8xNv9*+-2nPtwE!< zJ`K)8|Lh%}7D_OWR1<|M;v#m$EtJ9!gcAQ@y~!7lToUj3p`CSdoPfk?Eq6h%ix36C z3x0LoLNAhRq$wqm&ePz*sOS5}u618Q;ld#$)m-Dz!-p5>q}q-C2rik=^V&lXZQkxn&MxG#yE3k{C9E zd3Z_x)avvy6MqBmih)gGnr~+-WHtii(JY{o8v$4MF<>5 z-u5c>mbRTcg5)yf7rk1v^>DS7@553R>ZLpJf=j22yBwobX-fSeU2|D~_o$>Ga6nYK zI^eeCS?TRx>uYyPoEW(xr(;DX=avs8a?TzfL%@jEN@AkE<6$f^ho~&4-P1Do1G*{! ztA=4?9B;=%q&bnZqof&%AQj7heNi7z%=CLy5vm%j5qWQ^R~GvWSOyspr`P5_P%QV# z^`*?E7=>gh610k_me8BQKfDY!$Ok*6Wz2m?S?;X0Hb8>f)5^$KlcaJMWaw|wPcnx@ z9-b%)X6J{Iq-0cl(akY+^IDY=(b6_bSU-OnXIms%YvytTU_a6Tr{g3i(cS<7nJ#bu z$svHmsZ|wbAxrDGstEAwP0(Cm?+eK}W*M8EtdAIE_n!1FxRRM9z4gBw5Z-5isrx2I z8X1OcLV)c$r$pvCb|HGYk6G`|s;fCp0fk`BY^rdHCNZK^!!}w%BMfk@s-1pz_K-!t zerht&*~7_Jz;GvJK$sf`HDEEiIOs-RN|LO1M8 zq((ZTry6xB7LO<*9BskHNYx2d7#G{{WSO!@R$0BlgFhXXPMU?`KC&o)KRDsyn7nTY zmCOJ0x-4v74+MeN29r)wV$=tP?ptK&Ecg&uNEsUmvc6Wca+U@?ph%Fr>OY0r z24IC>=?P=$i(1)V=t?L);E=F)G}aJTX_~Ji_>0Qb;(x}idb5 zRK5n(gNu7Un)gpV8t6R!&d%ZLc_t}6Ef+z8;lm2NGbovTTJBFA6jixc5%<93%DQ%{ zZ9b3^4lMaJXE+j-TeeYX)}jmqk4#Me1O0R6wKm&yRw7cYcpZs-bh%%Vj1e$4wZAG!|_o9 znrAmL!XM@I28YMkFN9EhX@E~ZRh|Mm;%$$hS+O|_tq{59+7h!fPNYxZ`h&RL7MPPf z_CGf!r1%qqJBh5BE8;KznJL376?jvOgYjBc02*f@+(OUXSwxRZ?H|q9E^pQdJ#juE z+lXe>u}bFH8+zKVMSx5 zlM==Y&n$=70C@W*$Ad&mYqRIWb5Lc2-MrFOmxIgj5GAGq*Xf_nIRd~#Uef{sQxJ^` z$7Ne!mOInKL5lCIUc2mT>ap=)00E4V;F8Y8Y7*P~ZRGBRUj{yHy7x^PvKTZUcmoUj z3IVO+l{+=A-<%_! z=J@5om`xT|wt?5O_ll-Ug%NC=MjOZuJ%+8qXMRJyrikT?Ht=BZ)LyNVSkSXfP?26w zywgP8s!N|FJ<}Xw_jW>#17rw&DqMqzuNofG-A`_s`vNGZ6P5@Lnq993K`{Z2j0hxl zDX`$Cs)*5}WE`#bO3+Guc*(&54305Mc)xMx%i=aDx~_44*eQKTY!6<<9wbJ@5Xmp| zCB9QbtHYD0So-mftPG~NSyMrr-?Co68%!ZxX+hE)L^=bsF$khAID9gi|Exiz#$yTwE=EiV7+t)UyG1#LNz_D88G)nfzq1=*!^upHV+v*!v&p5m_x z!ytB&Yg^ToY&+=Jn@Zrb=OE;Lc_o1Qawyh9r$Yv(rY_w2f8pC-lV1XF8R7aZ;9hDX zqTv$y4ome_AVHsxI=jR9G>_TNlD{hYO-}1%XX!q?O$mx6ex`t-#six$M}o{%4Bd`M3&_X(IAJy742jLW3;4qs%EIL5Ls1UC zzAZO$$O|(lMV~?PTlGIFdxi_%qEjCDs&-L0VgeMb|M)68lw6Z-f2K9J9DMlkA|hJn zAhU?2P`7xZftmJJum&3fIi(LRT$IhiP@rn9sC1DHp&z*VS+gIsuC2;ku(26*!Xhh- z15i`kCc!_vZl^qkBYdg~TX`j%xMJMg?XE@k)9SY*D*@m5jvvX_@Q3kcQ}l+AokMKi zvtdDpk*gIhf{pBo(cV`iGtQ`jyOjH@Djfc{wb=HU2WZaaU|x!*_cW@QqKjPQr%Kqg34TO2??l_K! zjQDjd9kK}FwaiE4YJ#AHP?YmSR1rXJY#)u~8v%CpVhrG6diuQ#caWUMJTo!e0~DQ| z>dVw!?T9x;{vq#3H<%g7ajGh}SE>Ki?Ux$QdTKh<8TjEF>a5&p!KjOO$Jq z8h)eO)PIB4->X(%)%*qjETJSO-=%&FqtrDBJ(I8WjGl|B@UwOpj;mM;TD}rf+@~}QWHqH!x0?o7y&6QT3 z%->KmEH35M>o87~1#E_l6Ya|5{YuM!Z#E^DF~n^lu*4T@Ff{3Ye!EfX9TQD^4dddT%h}-I%!^W92UwJ4T|K%TEk3f`=e(^$zrpU z_wc|KX*k~tetD~{pEY~;4P=+B_SNQXez7^w@*oHrI9O8u~6S(ZT9K>FLZ04_36 zsAP%+j}`c^UEX?>!F<}qie*d9$-(^Sj>%IW>2-=BTC1%J7&_R0t%m z;mLmAi&(_ucXyVSNIhk|v@^&X!)pf>F(9IpssJsCB^Ua=E4DFr^#9g4+rb3f?U9XI z&TE6w?-M`#Hgh?vfw_Z3%*&`9AU=XC9xB=nENQD*MXfS|Zyex3zYE+Kx=ln(f3+VI zz)~>N;dMlK!n&JA%puldR`^>_6u*~LfIX~eHveOP73ijxJN#`P2xeg9dZBip@Ntf4I4t`$gQ+w7gbQYiQH2TDg4 zYn;jRqtu7@@#QE0)tZVfdddGrgUem|rh+`>=NEI$-U+<1R`}@ZE=z9qtF|}zb^VZy zwL2*5hO!|Ly3@?XL6ep})`FwqAON~ij)j~`iuolF>UM`=*<;T*#qonZR~sB( zDld%jXnAfG{{)zQXh!duF)m^GctV1Oiig{U;^{zY-qD_1@$X{jES+=jS{XH8J+IDK zjq1{pJ9uJN8D^zE zzX%SA=jCG%n@*BGXOjp>f9X@dJG{{qUwEU0>Z?e>Z80f2Hn!R7;36I@Xon`#1M&+4 zJP(VSFqrx+{KDdKSkcCh!r!FL{UaBQZIz7t+hl+pSQeFxwgx>8-_sSJqlhrtv~H;U zc;o5Ko@=w`brZsM+Z+I2H(C$;&>R=&u&7N5ve+#*H@T+B5$d@#OI^tys%Rqt#0-7E zNWpbqSm9y~RkTkXH;;FqqIu4UVe+;Ro@l7C6_4K)?<2zn|JjC>UAMt`-Dvjq zkt2i09C$W%4=-3-=4}LY(LeH%ur-tuPFh6jWU8GTi;FYBP>4w1)KoF&t@w6T@vxuP z)1ZGV?qJ$CNh+2yCsgk*)JZ}6G`&!?6HF65{zN+3hwF<32?hg!IwosueIXCdl=T>z zp@KM~*&61DcYJKAECz)8m(0KZ)V?mcF@Gnzz>b2uv@j*1dn?3%s1n(;C_d~?kjQzf zT*&`XXY`fQlE^_>xgb&rrrfZ?Yf81nzKgR>G!>UP4^yCsfVu-1y$8Ge!*s%h*d=Tw zBXgDOMw+(I7x5=3J~~!~T-xp;krpJWPnD5L0ZQXQpnE0pw`JlZ9^eM*ht2Y#=%!yD z5Lz^=tLu!G*s2dp5?=Knv_DHBh>P7H?@I48%e@5a)ge%&$GXe>q5+pG{75wAW&6_I zJwO&Vrng`-tda*>gdfIoJV}|0DH?tXj;!CZR(}sn$AQ~Ww$f+j)=n_LB zEnq@+I78xU>Kujj5r*iqUyZH@^Vd+)2&NMg!t?+;1aLkk0kWKmPB|88#Tr(@gYmu` zdb0I*ehF#tdDly-a6IP^xRy`^hZv8S@SsOmJsQBrl~A7#T@dDVwZ7~Mba19fi2i1P z3*k;u8C%_Ty!wGqY#UZu3|ekoU6k~lZAP|Q8|bMIR7nJ1B5p@!VU?e%?#V#BQxJ~H zju4W+EU>00DN*&5twYsBsC78dV*`3ByG|&U6`>q4Co)A}hMl322i1kASo0ZPTwyafM0}ctctmF@UUYQ$*AtJeY#=XA90&24a zyw0N#)?JwxQJTtAz1Qym(ofLxDRK#Sst z+6W-C(S#}KiJt2mpoPPqhfDWu8IGd|%Vr9uNVz%&EpIOi)ZqLb#jawu+%}0{-!|cY z0FSPAM9~4(uy{p6GU$k)V^%rLh5Zekxi`hTS@OWL)c@NXD`b+nkX1G^)GPBX`8^h_ zc)ZOTUbSKn|0}}mlHgvw0n}?kd0pb8M>r<)4RsKF7w$$}#iWr| zISjeB_#WQp9Q~8?Qr`Oey@^8axEN~lUF~$v3|O5dGY6Mq@L4U~?qdBbm-~a&Gv48n z9RcnC>nG(+7f2}ma3O5^mpMHEWQESk7GAUrOFae6pOSQ-k(og25>e-Z$-%;!DwXEl zUE24$c)#QMXu^swn(Zw4mpY_Eh~OjXc6F?1{+bpju!60$QK>^c?#Oj%H*thXRr^T= zb2{FO*iWepnP_&f53D1S7#mwhncD4_Buf95WNzPBt_iJ%eLBm3j_1G4WneI@5Z`|?tK?SZ%AA@$NiDeN zB*JHpHE&JKYBQ@iyY>?Z@B1ic%4=OBkyx6^U4Xq{!+_f5eDRH>&?(}h8gg;6yku)* z1?(NNbePO*fviFzc0R4N0Pf#!ZlzpzcI8nw1?~FpH(z$U{RARAarzv{rCsE-%CGVkhSf_$AP(VBkn13 z@FSwGHV1o4L2VPvh5ok@0BLhU;QDiwnq>ookO<0e_2E)b~nUa!~N(h1Du{Ly9G zK`PUuS$<)zx)Dvk)7T9H8bxSqA#t;UP6X|(9_;W{IbFq^)=U%DKL*aLn?FB+$cqH( zEDdh5dS*6cVH;k_PD2~TF_^u37*NeQb{N`tavdDaa%)Q-x+13YBG4XYW|{=pYWSTU zmmsbJXvnQvOqW^CRu4RXjNE}zi4>i%<6Fa0mx$TGCk7wOAEVlk`~G*esSVKG3=bVB zzklca4sB50y{H0fy)M;ZxFalBKGI7zx)7y`9nCLE=jtse)1J zb!jqb`PSeFSFVTa7^CW$jcJwm`AhSG%Xfz7n2A9zc?D;jN0+u#FGIU~jfN;0_kY$k zLHxU{qIyE22?1_iBaL?IMWd;%gQEX&L*{vAx;7c`DKY|>RQhC zmK$nd&_*W5;tW7Jql$L09wV8c?CQ7mWbEnQPQcY&-V426Gxk9AA}K#iC<*^F#}8a8 zi@>j@{{?uDaa>|TgU@tA!ro0KT&6&L+hc-#!@y)~Va}`vmk0Y(N*p8rjx9_(a%)4L z_ZsAjXyt8GOwU>Ij<*Og*!;U>)@zR{wVgh5=|Jcl!v>`mYF=HZ}^eb6Ar#G-}~# z|Cdp7kd(4Un>`C(8P3v530d4I-3aSn)duxIBt!eg|BG4~-lTP(px$&I&fU+PM8bmNPUd3pbxYQ$M)j<0%vQEIK>EBchWA_((}%X)B3bm)7u^;6$g7Gpv7 z!&%Az zm}*7~Q3O!Ta6siXa*lnTu9l7&{3srI7NM7yBb=sN)7ThJlX57E4+ zgmj%N#(ra>CA?y)04gupIrH2w0xNT`DGexUWUAMIiBp>f(?BDhXdO~-Q~1W}UpW8k zI(Zk`Ru`}HD06M0fF}))1akj9oh{c_2lYc<_C?5NTx7UoYMZU2UE0L?o@6pDgTJ%C z+1R|ivsE(o^W*8I=>`@+-rL)K>97@Unc)x!x3~d89|RqFbV}+&BDZMtFrzXo;aNwLQk|~DhAyKLa4AjMG+Xj86Nz=o zdyc0>vF*ir9c8a|9~MlI?yT3zbSh!@%r^`HlGThjX2J6_LL8{s&A zS9CE~!K0V#*zWHyM7OGNtx3U8>C87lL>f^k?yAk_o|E!sMWEq%N<24l!Uw2#eti^HxoBKyGM8~s`%zQw&_%{;tXn})4Si*3b`bRAH zI<`-$8P~HwM12r#Tk!U;%h)>+EVCR8FqieP%hT_9vtWH=SxgD-O_dcGZbz*<7x$a* z!S?7->keEuQxe1w)}U>(KVI(XuAC8^;daK%OjsBVTDOsHZIzqa-b$$PMbV7E9AGpR z)((}%nHQbs=2;cH7$!S83u&KyOLEFY7(FXdiG1Q?I#WT-z^^bryjhhkjX=?7KZL){7=MM!b`gj+* zh&9yfgKieb%j=W&7b@qXDII@Y;_5sOVdszXOU%7r&Z>!t(V4pq`4s@Yg6UlrSqq94 z%bT?M7Xg9E6HWY`j^8*ECwO}Qt5O3&l>j6>uW#Ug%S@tUv zRz(*Oo5U3hVm-z;K82KTaJi|tKJ>>l|Mz3O7h!o4VjyH@NFio+yb{@BS&8lUAru)! z;x7xX)is{iB6qgzlWc;SzIuKu>@EapxgnsX+Fgr2>2R03xWLS;bwIW!M)NC2J2I$z zHmI=lTPe(^uqG|V_RPH|1pl}w>i{VQZ+T^r=JOKkNTiDP762IA3D1!%DQ>TWYU46T znBa$jFd=0}$mw(lX!aML%D+|JnHYu8Xl(|NtT>oR_VYVtKn#FDbjq-E*}kFY+U!lQG!M_f~JuwVuRIV2hQ=|Aa%m-=RGKO~eqO;>w}G!D#48xUo#RCB}wCqh^Z;iFgceMj%Pi`Ka^bLpsRL6y8`te((gXjxnyg} zz;3BCmi~)w#VUY|ed?-Ixubt$dRNh!15n1LLeIQ%I!O zJAW>OJAZYHjlEH+_8)jQ$q^dsc|DJ)ppIb{4 zR=pzZwJSdLK)!HEMV$Iaxb z-}hns-r}amOd+ilxPX|sJdRs6MjOJNyp_0pLHzYryJHmh@)`%Wx7nFjs7*c;Pe`tq zG=i63MKX!=-WK#I>c5youcB+lPECI)88%P>R~=|GlZvtYHIEkUhT>TL`M90&MUxp{ z?3`$jZa-6X2i(`v)j;gDillajV>0X?={eptm_o%A6llo^PuF#~!f;F`XGj#XB&qHa zMlEpq2TDPlL3!F#C+AX>c%kJbqzp0X12cNgwL;AUNjbWv9k|e|7Os<6a_j*E{F(#R zKVnCIwRh~Sx8GY|fO15IP~8cC)IYY2X6*rLGQE@YUaTyf=#^=&CxB>9 zw7g=fzcYZ*tD}SW!FFtc0K~1l3GavaGs`&2pe8e}qWB7m7EHEfZtZe)H-N!hI0h8_ z4Rb1uf-8l#i{n4<#ia1{n{hq+!8KIicgk8A9xrBJo+^a~(H|2{-Qzg6fFMzel9HxcIc1Tvd(~+4Ox3kPN%Lpe$Y9v2Eo| z!20>c#`k{JQ&I4CW`Dq=oW$hqV5z>fdwhtfMnxj0y*4E`%QIVTw_TRBXow&jd8F2A zHLuVxml1KStN3hV5;@WK^&@iGOvKM~Cz2x(*cuZC{b;@4lhHMfph#d4d?ooK zKZ!oKxorM(hSfcwk7r<@T&y(I4PB5YH~$pbEq>J}sa5CTmCAs6UH?j%NyM3t?AG3X zUj+*;xx)vboXD(7e=)MW^no{x`UB|q6Wr~|=CzIluH6`6NxdK**a+xeRM#$wz@=~> zCN7%ema&jx7&2-8xyD+A2txu0mUI7v6Z#H%2zJt+ADKMm;xN~VU0Lcwkj8DO=CK4U zL9IZJyM3Pt>uz15TgjRuv0A5^TYzpo<>QT`k5CXeFW#@|ar4<&!b|;$pqFZDY)wz6 z3#G=ndSGP?b#HODoH2E|n?hE6)L{-95e4bC5LhoABppZ8c@y__5iJy zve7hS%L#=8p?wbl^zsgES!2={dG+`{koFa{mUcin{q5Q0^5Oi-nz=o(bxrR zGr=8+cAR;Ow#A5>Y8`C~_MdED-MSfK=WnBCW_q+ZlBx(tG)v zmp}YyY~+GLee};^CSLY7lLn|t+Yq6EZA;QmCPLX=oPF3VN-qdWUQlrN>Q5}Z)A}8^ z1?2`fiI)%__pTu0XZ?p*T+Bt*d|X^uU80V#kIZwDqphpuRPuBTB1=@TPk+zswCVCC zI@?Bn=GD)83>TIk<{?uE_h{M}H4V9Wfc*FXX01tbJyvB-RPtw)R{5*N@+uhmE?s8pmnuKVZ}Z$3IE@SUJ>qsR2F=G988 zyp*6>|9E-P;EO)9ydHSH*P}5iHZn?hnu<*AHmpahJSH3bo&b?IMXG9Y>f6g$x!g{SG&Tqm>PFO6aa27P;2jUY5(7(-oG zKm1R~8dg69y<13Uz+k*K`<@m%DtixM-)f|elk$ROqO0$ned5+TnxUCS zGL9NqK7K~Hp#;J;BJoWiebKD6+nI%-c4ccGFGydHO~l1xh-i~iaSZJ3ye=iGj8$dL z2=Nc5_e1YS8)aKj;Z8D*@^=gez+WF%PC4ZKU8nInp$U4c`fQQn{|B|! zhzX*txI`5@Ur;n4B|YYwu+wOf>u#tG99vcFqc?Y9i-!>+zL)A1@gDnprbwbC_qNaj z$z&enB$czp!2%L6dBbr+S9c|P{qXR4KIoG4a&H;}h`NxU@+|6lf1eIDF>`1 zc)1S;dpfgfmr$Z4eI5u8Pn2MH{+daK0d%%JUmzz1J4N@7oAft2f2p16j}9D|z=>>$ zLJjaPmx59LurO^BR?*|;A~R~Z_ch_)49Ey8dMziqDBi)%+GX78^@7^@;pA?>Gx=h3 z;x=qdRIEq;b~@QJjjE<8xl&#icv6+kE?&BfF;t!46Vb@1}_+n`<9$bEG^qJkzMUbL#H!8HW*i4ns&nCLtFgB zA?>>dWZMH8s!&u}0g=_a_F1@gL65`oP@M;`C0?sUUNm(s`|(_i`F&B06sY_Ds4fbL z7pgojK@qF(YEJEX&~j|*@V3KLr)CpZleSy@;gTJ%K-XYOpT`DQ~Y%+=F5+DQ1 z#pt6A?7H{IbzW3{-@m2mKFgL%RFri4N0SBI6m2pRQ}H2D32E(qxnj7>XWY)EB7&CI z-SCOKx>RrW5R-Rb_1EL(Y*tMHv)k2%0|SAbF#WkqOZpF85h~YZc|uB1cd3ewPOM45 zKgZMIKvuImxMZ+3oDr;Fqa>+-4xf$ z2cy+<8o8%z@{>d^t+Z4Fqtr?cn9RhgZ1Bluh= zJZEWluz=48X>3seoPRrj3{Z!-)2~8k&tcfh^V-$OyR9kfx1Bb;lKcBqeg<0oyxC3^|8`*{UN_u1Ow0j{8<{+1X2(Nr6fu}sCgjS_tA=AmyhHz0wNMN9SdPAHsapV!|f1Hho^#qSxDg3M57 z2iDd!NBqbeD1q9X&trk`+{kd=n-qbvIY9d&x!z--oL3uRC~;zyETsxXzwWl32{Gueg-Ex zs_y!dG+q-VWl~x;dZ3cu3+hO-_pM`jzD?0{AM%w-4><|HtCYX99QnTBumCX;93U4~ zb&=6m_{SY(-CiH1voT{R!XTq1%`*hRZ-Ro2AfRG1C_dL$Ayvw0{eEJg8eaoACtT}Qt2e~z>f_7(I(uoh zKY8MpKDo6c4^L-G8ms5uGkT#)Ff9B z6!%xciSZhly*P1*=5}!*#^+VXlGR24l%(p=jLq0JLJM{`>`K|wRr_Z9{xWHy5!JR9 zYQ~EP= zUO4uWk6b~v7?kKkOT`ScTA*rDWC9V&X&QK^%kmk=-J_F3F`!aJ?u7AAAo3P5hqKz9 z*F_lz4$dWUJdcM;tQrlCLQOyByJOEVk?BhbWo^FrfQE@e$mlR;LRH+lfK)Co2?9=w5ELexkz@Ckts%h4r#9OzMf@d-3xo zraju+!Ku~Be@t{B1?`IIy`4z490wyGyJ1kBs=s47sJ$^@*)oR z$>gbXitQd1%V>KOnSsgL(PfTb2um2(x@iM;(-aV)BGH%iN7vQ4kC~#$$;GB}2tB9R z$4A#*xp?Vv797lN&-w4kXgSK2QVT;K&GUr4&5rS)%ujhTOFg8%Xkt=g!yMULG`mge zww_;Q;Mc9qY`TC^$5Q$bxKp>g&4|GbPhn`8D!97)B2r^cuNE`I$z-)U(wGM+nyC}R zxr|Zw?scnQVD{m?xV=Q~lEC*OF7F819+2}|$W6V*;Dro{syi$GYC+l)hcvSSd}-l@ zoCF0yHIr(kHr!{Str#2?cLf05ON+OHmy!)?$$i-C;OPChk=D}K#=U-&o(LyKnm1{g zgu1z2MLM3&jxLKujFTZ3&3&!Qgg;3H@>BZ(yc5^1`ZlE0rta}36R*iKFILw{;s_dp zb=}q!mH*78_j&v(gaRGJLOFtQt6?aVL$M<&1?0Ll3RW&x z>(lj_N%S;Y99Ee9bBh@Z6b?ok$%(Mg+$9IKL2N@v``ds%0y%M3x+& zo?hK1Jv;kzS0q6jvKe#iNb=4}t7C>1{>WsxZTvk>u``ef z@wi2F;_$DD*Ml;3vUOs0*!K(3&+$^DZH_^En-`qwuAbHi;zwYma%oUkg~{|; z#GWu!)a6t4u`S7eo~+z%9*;T7FtkiDS?hN=Pq&f(0(N+)gJoyzQ%xP)W^<-SZ6T_G zPn`=I)izuhtVlgPBaD#OF_^w$d0Rx$NPCYo*U)?cE3j)@^8cnZV1f+~Ks+)8R78z& zEwU6CYB22W=Lo1*84O0?UDr-GxoQMf0md$l{Njs}m@F%9-b~IP^4ebm^H1hG11S>8 z6xn+9RVo+b^lV>g&!cG&aIU{UxS>SEW<%1rzd3<|oj)JoOB^CWyysJXTbcYY0bB&% zi5Q)31@QB$oz2)HrjFi=%eX^o2jQ0^SWO`|F&K1@(ux1AN8vYXxeSHhi`EBQb~Qph zW|IQJX{POnaSwL|<*d_=xtrn=GQ2(9z8+2dopyoYbVthxGd0?(*P+)|C*5!rU1Bhh z3S~Ru-vmSt?zUf~4HK@y2gMu1;~U=<16p;%p#+u~+T#IzwbNFLl25N@zD9j4@`HwQ zPE6+-Vh1BT@pI=}`r!my*#b^hSB2+!)SxOR5HqbSux>It9MkD+!VaPB;#>5ZO(;K% zGDfwAou^9z2aKD8GQE~tD>=AikWG>q-s%KE;Y;QIxhU#5+hLG9r)QJ;LE?O~b^Mr3 zb4kPyHA#uH81WfC6qL#r@J)h!6Yl{~VgfujcEksAoTb1&g>j`Y;&ZB|&3h|O5;mVg z#4y<3)Sm#+Wyh*!V;wq#eS=XPJ+eVvF1gFt1`M9u8lNCYf;!4`$>RKGS{_YSwafLa zp~S%03+?)a2xVab&CT*Rx`R0y_0iR0Ps}D(Xy*oFEWFO~uT(7=+LZn^74r^a_sA)Wc3YKIG1-6nM>(fwt#Po$~ zt?%_iIEei}Rs;Bx-iv2hK7=t;esV%6xkIRwe*BrK*~dZEhU9=I;~+cqcc?EgYI7ql z5B%ek-vb%KrmViDTDW+ai9jQhX(JFMXvYb4{NFj8G!=|@203u^OH&V^Can1+Vu?u` zn&+|+MP}{S81}LrYJ83L{#R{pY%9@}bP2MalsrlkG=5E4-9{0R3bqQY22M1xu>+|~ zYD!*+zMyn8l#oMyVGLS1+ZvHS&YK6~1&uOqJ{pxZx9YRTC|ExFJX^zy@i*HWYPA|4 zh0`2a5SEp6Dc*Hp%Laiv6RB`gQT%nt1PcBEDID!2Xe?PbG}2pEjdyg;tiBp7-PVrhxiyY^&qY75qK?ETDsFf{jxJ z%Fa9MeERlCyXbJkx>!^r#yCn>s}@DnOK8B3fMqhco$;pT2#LEWYQ(9o3R;r8cQFDkUFp(jU0WjDx_parne5~s_}KM&<08`&7ov0}!rl6nq%`C?WNQzlv!g=?evd>H(wr-DY_OkwB1 zO9#-#{LE^WM&Wsr#RBoi>wypgwaoLyQgHV2+)}>~myEK7+si&9uu@H?CGgbFDc=q@ z%T@Dh5olRRSwWBV((awf%j}Zgu^va386I}&lz%@S;X=8egStppwrNsPw-!#~0B z%{d>E-N$2_zdy78kp2R|Y=5rS_ITo0#=Zd#NP7&?oR4oM3iHdzQ(>o~;Ymz$uA^kj zZDyLQl_1^Ij|DXX^#d^9iTkFBxOf<~_uw@xG2%MVP{#u9H}k>iHyAQsUI9}^q}OGe z1%R4~U(j&;+qMbv+mhB(I%6Y<8e|OKuNY_0&oHS0Xf=);c2f^oz89+16_~o##I=`1 zWb>ua?mGnS9s>PZq9}|{m~uOTJXqT~`gHE2TF`^#Fdu+A_SqTl6@6SRR!mlNz?nP6F5V?j zhthN5D_Q8o-C=nOM=R!A=hMzJpY1Lhtwv=mfSP1M3YGfThKk&H2qEL=U*-|8P?(-p z4r^SqtlbLI;wYFlkhZg@cJf>)r>!&(Y{Z*$Eg5ZYee6C5S*D8q8?V84_ahLKJ`a6L zlu=DC`cnC3d4Gn`IR6iLA*bg-0vHmbVp_*(>FdHM1L)qF9T-M5+-g6JWQR9K zb^|?T3mVs4oQXHD-Wz7strdC@oG8}fC$OnTDs}0!!k$&yxP&O~K0hcq;7t-+_thae z5asaD=(u!$#|}Pm=bur9r}3^C{+lpLr*oAQDX_9YQ;*tiXfG2VI2z7g0n()-BUS73 zAbq;?`oao=kc5zf+&X&}tb99@f|sF@1~x>SYezecxnqB*?K*^`*z)HSBgVD!h9JS~$1ee@#zVn8m>?a?O)?8a zBTm{;W75X^r?1$`9}_cT6Hb~|7SV$+5@9$Z3*5R1M?CGkVhPmR#>Rp=(#!pkLD{b2 z#_#o;P8B89g4N)>C5#U5reu4c#@}w7g#bA{Qp_BR!Eb%XiKaJ#UYX{bbcC-JDN^rR zcq-BQ)escV)X&=HTcRxx!loj%tFNR~XR_n8C*t z{pV1W3YM8Hw0u?>_fu&D;=vX;H;|*Kp&K7=3`$8X*f$++21Y#e*0b>D4g9wac3U#= zR15MNlOVncwTh078isyy{@21ZK`wU9MfpM0dpEfAY<+l2QudQrKf5zy8O+^&E?r+> z88KtXor_NGfj$H2i-dX#c3L)wE;iE~Z3-qeNtWRM8;)AY6q!=!CV5!7pmcgL z-kWS24%&;;5r2sPsi^erW+FYuM^jSnA@g0r6iWCwO8LLp!qMs4B~q6P%7KQ68cR+R z^gmndQ)bPXv@8O8s+lD>_Jg{hFq+s=+^Kl>t%8$rUv<8Gp$fkKh0-SG+wWmi>MZ~( z?^`-IFq*9JF?_r~BNYBtCm)VV9~|r7JN&x$ClJH_SXGEDn1Z6}-W}I{#{s zHPDaaoG;0_b*F=0`TkFQq~cVqDNgSy`e2x~S5Py^RmKd2n61C8S{xGk{p-*-T=z&n zPjvyc$Inu^BOFgP>Wb4d^O2mAN>QRNpI(!0#9QEir08{5hnWb69pKw~O#FU2&(f=x zub8NceY-UI>Lqoq3gW70R72ptNl)Z$y}0tZU?Twjt6zYeQ27IlCHHhY2J1%f{SWBF z;HcMZN|=x#FV$`CBKaIyBM4NP&a^-ut27Nr^88@Ii-&p?FMa( zqTXIn>Wnd1gV%t0iaZa|^N&%_dv~_Yp~m08%GaROjGytv|A#8n6i@HJ2^dlI^=QqaCZvM^_mNH%){8MlGv3rGL4RY<(j4X0<$^PHq;I zDI-cqGa7Nl)nd|I&a*F9tO#|UtUsgzvwQbj(bhs6Vbw6vb7=*C+BV6u?22do*@($XtBnf;8Z36S8S5x65ax+VvoU|Mpl;nZHmL7H&R zZ(ZxXwFQBtwb0>sM3Fw+B0tW{NMu}JYWZy}1jI%kq~&8ZhI2k3KZuwsb}y-@YZB*# zITcMCE*^XTLG*+`+uFI7!cNWJmWvOcZ6}dO(|`ldKViqHyIOd)bO6$wYv-z*P0USSCULna#ZWdZc2sG>6RG4; zCj)~(s!*fPQdWsm^l&FX;kiu&xgdoiQ9_iTjNZ+w_c5V+ijTowtsDy!(QclMjl$dj z%W^G54iu?~l%`bb6^AkuF@*OgL!S7BN>1An6Y$o0izN? zBXw{GS$W#lU`j8DzaNHsQFmgY%5%7)HV$>ka3%*`zup4kl>iF!^7^v-OfEIsl+zUT-~>Z z-Xhdz8zB_GdjG$!u|bvl(bDm8`stnu$aVYtd?LuslI93>Kf6wg zm@0MInYz;-6pD?sHT{#{>2Nq3 zHi7XOp>QhQS#Y=;lwF_=p-!RHs3pk_wv zw-vw^d?$zziSRvqND)%e#}%MLC{}16t-&$3juJiIAn^-hB%=;i@1ro-l4Wenh*fuT zN+>vH>jB<%-mX|T&RO@blzk z0vRC&*S&nB60Csf;~8;|@ll(xF*u}`<}(^6W;@X+=T*jo+vb?bZvn=r*7oX8W} zpyiWnMq_Z0L^6WWO&^I_dGau(QPLW_hTA33>XZNoc07cZ?M8zQGI`a^1 z2CS#uslU?8eJ)Ewshrp1q_ZE9LA5`S(~9?|e7z{@q=2!!-z6iNz0=`xttYdBu?j4v zc2Ua1b2zICP2&t#9@$nv@vUGKd+lXS$NRVU;RAVYP~5Uf+?7_x`VfSdX*sNxkbA~P z+d{7>&DMAO2`73`$2aTtr-AMcrKIk4oBt2?XrAt|4Y8^NxaPB~`|ExBw9hi^o*#@l zRxT+dQQ_F_r_CHP0Tn$Lc@^j~Fc9R8){I9o7#|g9*CX2W>+>VcLc!e^%fc7t zPU=_e?})50m=Y}M@$zzfpxav?$gN$A)PG7rh3-b$l&DTLcI7Da*xu3X5m;h{whC*^h>}h20zBc~vh{sDl?us24va zX=98ya4l}##nC}Eu|DDMEsXUETW2HVpg#6F#APjwRroMH2$6@j!ju#8o~VhV-;jGN z>S1^QKpDgFi!;%|5RCEk&Cqd{-i)`NElPpY^=IL!EL|+8s>_Li;d8o9*#tBLI6;Wn zgP`+ zw(TZNdKn2c$ZQzvKFDvB$H<2*VvTk`8n^i|u%pCfbRRN$gBnkf2v}a?gVzo-V!Vx~ z*>JQ$Fn@ZZV!b2>6F3utoxtVn?D^C0VL!eM=!3F31YBcyw5N^b+G)JiAIoO(O(`*1 zOD)mkzf!TqpP^^EuYdX_h4t4C0JC%mkrhOVEAb=rxC+u)GomU{)#jOt7VEjAi7(R+ z)D?Ghl2wr9f8?1op31aZtS#uj1Hw|eX#7|aR>%MXyYH8U(?tSK1L|2(d;87cNwptq z{K1+88{pP7`QzvM&PDX}>rYPw^2`@`C#M<0uaWdUh4>Zlt$;m3vPzwUw$InlD$1~~++8se=F6yX53p|r1V{aKNHtC(Gz8qFFyJ)+bPss=8aosW8 zn--rMzPkjWGyESVpt-4$xM%pPiM|VLdtvz9uSQ`aMDR3qdhH297U`E1y5B;t)XxPLTe=yl?G=m#Qn1WJx6z1>TybFivlBal1KHi4g| z2LeeUqtQl7vGeP;ed@rRRBP+-#ln+g$gaF0`>)u62kH+ij%KkL<`c``SmC1&mTZg> z?;KuPqw3_(2*yu83_ZH)g?Q_^CDV>tgSb6$x-LOEcBG@a{`C%h>xPwYQNzJQ9xu=Y zm-cI5Azdh(N6nHN-P=(J(jr^-xBbZ+o}h#BTBE8?IABys#6prq;|RBv#}>GdfiD6f zS_4SXAjt7ZqkVag8P*0^_Ois0geZL3JWnilq^87x_yyDamnaIu1(-b39@ql*)=9+^ z{mI#kkOup!J-S`mj;vbyV+9!%zZ@1vJXZBCI_qFqgt*Un5OBmL?Q<3RrN4w>Qro*r z>8oUE!F>NDfbBgvyHXW+k z&71($DLPV`@l^F9UVR#A=oCJid~5UmMm<={9^?$E?1&k#?WQ)~IxnvZ9mzEL9;?Nd z1_F`2ompnR`)9F*O3_tEFFRK}?L!*+SD7wZT4G$FyZio!7^dgbMsJnPk;9boEbjv( zS4bW{(^#+9h&I-^Hs9~jv|T}i#i-}FEA6sB1A4l3BN8< zs{0$Ufw-{KifdgsMubijaYUwR_#LaLt~fDEmN(K8QnbZT8u@`v!O9G(qI@e={Qhy& zzjo+PlA2c$8YW$vBnT3QMq9qNPI~4?9Yl`O7Y^3hsT#-`%2$(tkX9GN#KzH&g3+i{SYP9okH>HdnsohlJrv8yWATdxIHWPe z*~kfqqS}V9<@s7)vE3)GkWWj9!@YN0)y%hrzbmhp2vNJFKBq2LOh?6)JXF;DDm}7fo8fvVci&T4s^2jcNJ8@EK zU_07$fWQCfJOA}BPBO~$&Fh!scNi&!XB=3CmQUxV3Rn66-=PwGTnf8=w8MgQ1`(71 zGL&L->z1o$V;kmTsvq#K{-7XyGPWeM{PrnVOeevW;$)zPcHu2mNTMbqWOm7FmuYTD z6LGOZWR;?x5kJH=y)tqbTWb>m5iu_QOpo_stvkqRbPeL105+6%>R}YFP`s2CJs4nW zJz53^G#F#s1Kt5|ZF0kE7mokI2EUv1mojYn?~&)rBNk3CLV*a?8!iQnSSF?m>HJA> z+v{e|DevR3Ea|vPLb8h_s34~H^sx)^Y!*;1gzf~2U^XfXyqi2O!c>qM>1hZ=PG5Eh z2?A2KUR)1ul9bvK+crl{fcyzVTM@g_XwBr-g`*Cm{h{)!FKb-+){u=tL$M!+#S4_R|Hor-zs1bV6p#Cy8f@g`Bd(lGSp z>Tz1y+p&C^X(bIp776=;Pg1y!tb^O#6yd5$8s-U-YjpEBlmJJ zO&a`g2xDEL0;cpCMdY+i*n`HRW2r}}MIkCl7QxtN3f?nfqFhxKbFFR?X!~R2C!1)H z*H)qL$Z;Y$M=puv8yEi z=Y}>z_7pl0PiN8JD>ZBQkp&@`o*%kp50s&1i!D>k4|J4SnO>teDACDO>#|jyi64Bu z+t8_76r~iBCq~CJx(#UYF4FW2fsai<3B?)!OkOF8EK_kG7B6?=$f$Xo38qP+up+%R z)GR@9Ghkx5{)-G!%FkK$Ak(aH-7M0zF|w=J1pc8mqT<3WCK2L2KaN-fl-V3oZlNH| zS9QdZ4m5EoxQ@qAEb}$l)I&r){}{cvcHbWwT#wq0?yG$OE_1NzIBJKKh*s}ly5!%2 z9AoQla-_LWx>v~!YkE{V|78G!qE3~C6SP9|$}FEB-RdsK=I{y7f(>##-K^-IPU;a3 zQ~3}|GM_&=k#FZr6)8`dUUH4C2F}DA){WA@t?eR;%1QtR8!=e37aR`O(MrrRLDpxc zXjusrQD#LNtshV~7=YJh-wIcDlRPEM%H{Xtt>*EK3Ez+MhXr0YxT!&dfG%Lw?bbeF z#`3y)!Pr=SgcCtxV5lS$#xuoAAlF6_?I>X6xZ$BS!$>;SC~NpKrnSaHVbFpTIECc4BmZ^d2mpg2)Asau{m*#E*t61Rb-Be`0ZaxzGxo{~6LC2zBO zfE#rEQZrzl+X}<9w{Rg%P(~?_!uw0pc37^&(r;zS-sk?F;qrt{hKE+wA(-P?s0Eg? ztA?8=3r=GSW~i?{w|zVJEJT#tDjM)13oVIfDG+~-bvAxX6ZlfDLePk3Kee+SD#Iva zwM=OyUJav!V<+vx?W^jq;1}%paE=h6ZG;sf9kpzxWuUVg)4f7$5gWA<%g<-;?u|z# z9alH{)(-6nc`W0jaY=*&U+^b`mfU(YXId&EWnBfP?lqBYOq@aJagNI-1gwY7f)3z( z44WP2*$X&EQE?Z?YOEk{Xbgt^2xQBhrt;{Jg&icTzFt}YALb-m(tg=b2N*sme84j} z-O+u{1Xr`?jlBE#-hsrWVmr<>N`*{1V;EGdbbfE2%m@e#Xw6mzoFR;i;;$x}iCdAwY=lv{kUhE)WnCNzpNgi}> z-a#|GOt@EQRZ>42bz83sjKmCx%T~+kZk4)pf1sdq&ch(c-8tOG0=7N^{6dU?kc1-m zOB{R&$$G9WnkT}z8QMPr?{VRw)6dF6+mGh&c=n5`>=C|Br)k+Z8i2{ov@}sMGied& zmR$$kwAPBnqBt}iCGIKBn|G@nH+f|Yy9fbeeNItmDL3sp-;>VWDyu~k^>DH@`9y$L zJQ~dpHoasHpjKmh2g^#iVI18X`xCi?8R@fYaUFyERd2&VBB|DyYNh?Habc<|>p186_WvG=DbWx% z?AnbJIgWf=hC~{Eoh2(?{*uL7zYQWvRjYXV)Bg@0Cs4*2XQQXaGc@0`EHqBHsAMl9 zFquSg?H`R|j!6)n|DpVZ`Gd8$$QEewrCM&)sk5wwZ4=(_P@W13=d1WpOJE~~zEBU} zTcGK;WEOs#p!EL{fH=3Ai6IX_k?7;J=hT@+F>;E`;zcE@#y$7E>QV1!TMl(d3@5`5 z2$bRfCz0E#-Q1#r{F4~bG8uy2c0nNcf}=eg!Vk7D|Cvf7ZA~fU=h#!elo#>eN!E^s z2asE<37W<#%m!zG4{^pzl=_p(Odt2)&}P4Rs@C~i$$a(&B0S=}l{0vs>an}wv(MLW~mf)zZmdY6r3~1D}k?n4Id|;=G0C`!5DSq=l(?JE?<^A#=6Hg zqt!>B$oX40{4>TT@p|oaN`Ut=9VTmWg3nLDs|FJh>2MeW!tK!)jLF)@=xYTIO6DLG zeRi^~kCDNIFJa|rIjF}MnVhf6n0^85r#eg)BYfUMArZ@3xqv_chN_GX^S@}J-HQaZ zrP^Pt?txx3q~g&}7^Oogz)1AmxbiDOc$#UtDg7+e0#lDOQsLjfAkEs(+vlOn3p8rh zU=$4U>|XyZv@W@;e%qDbItZK_>+VraT0X_1Jn_;wa%0=S!JStZgsF&I)uZD=hk18` z+dKG&gW&R}RA0Pqeh`>002DMH-%gf|e{iUN;@0~)xi9jQ`CXB4FH;CZp(mnZlaVDC{z&YOY2?g|zP$sf?Uph?n$XzG$bm<1F7WDV|A}a)W^B60!pum1d6iMPOuOeK zWdB+)OLk6>KkO0A{}lyAH*a9SOH(iW$+}G-5UvsR-|TY~3>=L22!3oXU?aNpP~>cm z8!KKY*IM*kCk5Ryet6OGCW%QxR+X2S)M<|veF$Fk>b@D^JbQV}aaM>&F&DD#vJ!wJ zdpCq!M52&Ou7g+H2_2`et?XSUk6fxzY*ret0_FDNXnnanlasB8}{Yxo@!7D-_ zI9pxcA<8+9^?p{o2C>AfF`1);Lu=Rnu0|1Ck#1o9e#WBJlbt=fm!SVn7XRzu*B9og z6EkHal+g0B5oEaZd@RaDFGF7-whp2^sk{%0y8F?Kw%9D^2}zO#^DRB>!<@pO9Z}R4 zHog;b*WGeUOR!O(Gfq~ZkTQHu;4s-t8TRTipch(%H+=9A;Qs5AXX*_~W6uy}8M(PZ zjovR$`Xrp1a=LUoAM@Y+E~mT3zef!(ywUa=bsE?whM^VBaL+VyJa>{e?zh^nBt6A* z5pnC*ajg`GfbWv2jvN7Och8A4y$_-%rRfk;QSG#nJiSLv)c9t^`XHb^AU68X&+=KA z$LeJ8vD(2Q$cy)vqdg-m^=uSI`sBRFMniTgfH7Y%;ZP4KghY$^;?oQ(eI7feofO@a z$uw007K>4MUUx8RY?bb!7%xa1Z_B0pr$>m{$gV!{SmNLU2(U2Io>aH|?f~cnrWFbT?xEg#Dd_R+ zA{pJSaDbOiBsX`+TLIR%2+MhWaU%Q!Jw{KZO4u}NGZ7utMd6u~0l3=F9+f~*isv~U z3fu9@>~^IHw|XK_uhMPQrK%35nX;_woCqg!g1o`XNMnpo){v!WBHw#5af7y0#mYp) z!&<{f=E0AnJ*YLe+LyhzN>HSym9z zBnXJuTf$m-t<|d*DkTkXD1AVk)4Y?Y`#tp_@zP58yoIEc$lWhhNMhDcir|?ElYpuL zThY&~|6Uh=DqsGh-c2BQ*OPBEzJxWWd3kYXL)R+ivW__yZgJ-}0O^2v2R7u91nYoM)+s>AXS_8JyCnu?Lnvrog&7Dnzd zSqUg+ro{jPwXQd?S79iK046|{d8v+hvLf;jt^vJ}^h3zHYtGznEaK-tT2C1H7i>@{ zOChnnM6tLDYb2-JnZ+{Z3fZTtd0K0zgZU^1>J)Apf-HQE`-Lh)lY+CF2zNIR*0VvvwJYh#qqPQROP>4=p;Hy_Y6A`(~^(G|7hVs;X6eCi)j zXK*k@Q_KMPXQLCV5JY>He8BQ=spT1ZsHEJW-d7(|5CBPb0P^S96eubJ)%)4ol#Yhu zXVS2C#k@&GZl3MHJ2n{y+wwB( zLw+PjMWf&Be|g#M(G=DBfkcQfv`EjdTedIrJfOdoH!9+jg5DD&hU+~JOSBw+dAuQq z4&P;E`A;&!A3Q?csbb_ym2>Z+3E>6Q9ox(Io4!2M3Tq4Oht7_2;gi&S14Hp7i(x2l zXDZDS?D7M=oK67nB9%`JjFj3FY2Fwayxi56Tg1_@sQaLZ=xdEy{e$`!ZbQd|?UFxu z<480{!qzJj-zEV*@DOfiL^Fh)qhDhV?=t$b>OppNzt-fi*t%V=Bf*>^0hozs7(Naq zQidKQXNDlWTFQaws5Vopsv@Bxt6UZ+-Bfi z`*FzhZ8L?l5|2#xb;~tfqLD^)8v8)W9{WZLR9r0O@ldTL>b!G}8YCMMfO@v9d*yQQ5LaoJbK=>w(f4~neij2vw~AJfmDzH4bCOXokU%fjCK zN$qZ>iPwPfEfSW!M@#p&ky8W4}Iohu^UKfZ75Wg~%F^b&ZohRgSkB4${)q z05w3$zZrA@ACY=%Q#Kt2yFKoyWuY_ge?~eQOpp=T*^=_^xwC5N+*Zy2VFIUx>IUN-8#&CoAE8AaLZsdFex~@`UC#*#_`SgoZcu$uk{vqpZ@)K*$cfnLET7X>C)WQqO!fYKkz3uoC(RQsVJu3tYs5 zZsdxoND1|74d(u6PdSX%r2T~dxJxzaFkI@dJ+?W*y`JjI!699~g(ZGC#J&G)E<{DRz=d^H$y;@L~ma4sY z%uw$!=i@+LNZtqQ3e~W8CM_kgeOs@( zEx^oIo*5j&EBs4=!h(m$j&aE}#3o8wDrGbgSa!94GEIR`y;lxQ$NvW$`qk*m*mocILiEDm`R{NG@A;*L=^k@K{atIa|*@xlgUVQbpHHV1ja9 z6)t{I>JvkbOBElVPk>u|Y}gI}2e8BANQ4)MeH!+LFNYrbioz#ziRSdSNe+ed_7FRa zORoMNc~v3@m2Cg4bj`!drF{0VVw#2GdMm+g5w$%m9(Uar z)ObiX$Y>TUhC99yA#+tTV!Y^_SRWFrjAcJk459BSxU0ZV<>bR&*5jN2(4%cRGg6xT zXPM~z{>%<}(*7Mrz5UzhP62?eyQCJq=WEz2DlqK|%=;KHPN)R^UJpx2q`KEbVCUz4 zw6P+6jj_iU+oo{7u?Y&u%A>m0KF1`^=-U}H7BuBJC}sI)Gr>dZ`8CU{>c`l7ixNV4 zKGQ`Nl~rPmR?iD(0det}bqRDG4#;xlfYXja1*D!fC+GO0o~+qOp|~;htw6+uhKMtu zdML#S#1lfN?wdBX-^9@Z@6o z@mPIrOaN@(YU8-ULk%hI(f$GdNR=X|zg34VI&$zzTs0(btJwxIJI%YYd$cwAPkIXW=Ma)%e!p zPO8T%5|b7b2L10uG*ruZ(;}`Ijq=>sJU(ft}q2z=p(4poeKoMOsY`du5XzRhwO&TvpF(E&t z^C?|GwtcTxoJYrP$vm4xUREAoNELvRA|J^kCA4Dj1>gr-&5ux2ynZ5sNr79Ug)UQW zn49sWA^KE)zEw@1TM2xRPuwDt<#p^nNN&qcheplTB!TavC$ zf9U5&Oj;{9WNMxcPD<|7AjqpZDCGt`c<<=*r$1d&3F zT|7GPg!a`dx7yL1or`dR7K0cAgs1w@G}iN8n)oX*6Vu-!D{9iPX2Skrp1*p_I`;4~ z87>BlyG_G8#iAaVC$l2J-iX4-Dtm7VYt8NDeO*r|BpUr_Z64YW%3=Of`xXrhM5SO& z^82-lowz$Z)$C$+*)=#%l=?ED%h_Yq8XyNY<%x82rKAtuc?#p62($6o0EzV)o3hlc z#Y^vd8Fe4%bYHGw{KdYDF;NsIFZvbmw`VbzS_BQF6#83+meoJ-ccxd;2L=i1Z1m$# zK_xk1Le<>RgtR+~va5z;wCNU#3|2$C}tnN^T7Kpe21KA6IkK!7G2N z+ze-(!J0S!M|eq+&eu_-juLo-&y&>9)n+(-&5=_5CuY0Uw?MVZ;39cBS=P9$cPsB{ zV?{?L*$qnq{&4axz)`mS*3ZH?A``jH>`BJRTIx@JFf4#bkhY0;%)S+rdFK$9gTSov zKI-ld^tg!*(VZdqlgTNY|78N6bWZH}*Ox|K_Bwfz<5e}>HE}QA@NE?@5mUv^`bum^ z-Ai+B|05J^hHC3}4dISSsL1}ReK)V=3dp)kTnxpA#j2|#AYs@%RQKPx{gtOVqx$o3 ze?!|byvJ$H{ln@pYLhw1mbCc9Ky3BHqUb+q%tYMLq@Ga#;S8GQ-T$YOTFw@pX+34eGtQQEpVMW#pv~EFO(@>IU)z2KlLp|kiGSDF(0b(V z;ohV1>(kN5g4*9pe?-XiS;vt=e}P%}zQ zF}sX)0Bg$Z9umnXIBa-hcx@G_$vu}_%+k3>J?6XHKJGT{p<*`qiw`Afo=(K?9%z-d5VK3jZ~q zNMMI?je{H$%VO<$`GBx}s6OQ?soR!vxbQKuu|BPj@KJ7;Bd(=mj2S-~#6_$6hOyY( z<4xsyCmf$=w}HAclYFWB{L*e@G_6Mscrk9sdsMO!m^)l(J-|7qjVUd~W-zhtP+jZGvt~cWaQKw+8{sUfDZ;n(tMGXYk=dbil^^Pm8F_cKdGPzk5XS zehtWn)uWMYP$(9H2T&B0kesVxk*k99c1YG)#GlnXX*P(8ZO4pGj0@Gv5J#x91=aKG z6%@+#t@licD}f8u$Pq<4wQE(dtgL#ipbuNT{My8q3alo`rt)>B12WNVkpE$1GED@WWk5TUYoZ&>TN7Z{W}8r+Bln-IHuo2BvK z%ez}oK#=|aQv@AP9TsWyHSS_rX}_I|P|R{N?H|u;ou6p`Cke*@9{5Q2RVEO zs5ox(r`GDJY6>O6!Eg^Bs?1GTWn=R(*-0og`)Whtf?%?KFQ?db*3K~|i`%iP%`s^M zT6Q%Qa=SF>LeecxvcBoMRt4{0DcP-?->bM3!p;|MrTqKk(Vl=V0TL>B{9e(nk$xAc zPz&p)99#$K`^x3#x)3}1F$Z)X%kSN#pxoJTRSd-xQ%A4t+AaP;1u0~2tj%3>@{(g~ z{3oahjWqnKA2ZFlK!-M!~lr9n<%Z_Ib!aRqF35w{zP?+<_zpGx%(L}nz{x7?T8m+M>?={ZfH zmVn_dwWO5Pu-uz@Qjg1KS<_&0@NH1$P-%A6WazDAk!yPPq@V@cS&@gm_R$E5zFQ?t zoDpuvz1f7=1gX969H^$pvlNd$={6}Tl41bhmZB~CP3%VQc11`4H69V&PXD{bCkSF0uJusJMs2*zOuTTre&s9WLs7aJy`h|b~9zxc7|sYgG=ST z(aCu2v^GB*<3IUjk(MZV|bY6TA&Yb$fcX~4JYr;@#W^uF{hfLI9U$gl;Oa! zM1J)S!hosdx_k090yYnzBA>#jJuc+K-yPaaS+2X-%CpG298TyyPQx-f)>h_T=#X}C zhXaWHd%N4<*qAtv3Fyvz3N3)^5^PcYeE-yWgev;L%SiHuQxw=?SrtVj_J>1TsN@_< zO;qxphU~b$^LCf=ZZ{DQ!Wfn{`%ptCifW^le)R(DF?ycbP2-B>*3>x|6*|7hl2I6S ziT}>p*M|EwjVpveGZ-q(>fvvDAZ>B)*=>^r21ypY__zP_L(IGBUoJpKh);RVP7`5; zLB+uz`1z26MVI#4HHut+--2P+_)it4KQqHid|H*TCl~U_j;Sex@k16e4xLn0n#BMQ z6j<}!!EA{KNlaWjnpH*Hk zv+4@^5~xe80cXIRlL0iY#+(6r@MrU_95@3yY2m@aYE08N)yMk*En;ZU!k@@9ukb%{ z&rg8i??R?alY>U*CYvFw@hNG46>^;EE{vdVUo8`lK==YzDL3-=AY@W%hW7#f>IyH&T%5q!32}R6?s~WWs8(_{vUQwyGO%gX>Kb*=ZP*`GaS$HXw4kDX zg+qQUxcA^eTmCDP>;)0DZ79{)9=WLY*sB%bb zftq+ykp@Sqyky(JYTu9h;^V5;6k$&+0AWkY0aSMRlu zlYhDt+Z>Q0x8p@{noL3-ybalKhv>xt1l++6Fq9jGiH2&OqTdFI~xbGgb{?d$tZew+tHiOGRaUd} zlH%|f^1{wizaa_$Ucpx4Q(ru;p!LvLk*m^vGTWzaHHfeghynsf4eYn4NRnG(|#q$$~k=3%Jd_FB2xk z;0)@bB!lDn*HDB!2FCJKj$;DW8h`x_G>3>bbCw;~%3rJEX^w6M8`V%CAM&Y@_&qj<7J|+ki8berg5u^aokwMaoX;ds{ z(2_M>)xUk5D_jKiT}U;A^uqsa@cuQ7^CtfyQ}jIXXeu5dy;5u4$oyHh-`$~>3`#oV zr`r%1l|(RkzVfQJ5DU{+kSl)eYc#DoNW6TZ;&~16R8A^ElC*RTaF_A zN#z*Clxk4olv#cR47Sk2gR?org|9=WT;eKH2VTe>LZ!Rm!&ys+VDOkDQT)fNpvY~F zD)T88Yg)LhX>vz^c+;dkY@%DETVVs8;t853^|X$SE=s4klNZbeJ5FBric*mB1Apvs zu5+;$D#4W~Zu;BH{OiROL}=gsKbg^tvfT6yn@V}w%w7;#a>cKLZLBo~lxSNUDJhgA zkH8HAn(RlEv|<^LNAFpI+LVI)El0L7sCqCrJ2JXf-dI-6qq?7p{?3mi5t{)F1V`nI zYPi%>{l@hiUkabcvN1NermDUW+~Y=i7>qx~frdxA(u0#(N(z#=IDsxHwe>%`mpdBi z5Zq4~9>>^_lB8^tx}K5nHh`t<;aq5}JpL7tSgl71kbugFjwB&Uv1!yzO?mc*h8u}| z;|LwsuDOa%3!;bgp&XHNUdo(H4x5Y55$^prq3Cgx6?4Cfjmxq4)NziCf`mLZ+9Ynxnhn$cZodsYH$xe=qZ1%g+5gn6i-l^7~9&=2yXzmwlG>e;D%i2FppsgYq zex;j26Xm_7TeW40;K)Txgdma0)ureOuI%47d+LgCe`F6og0g)t6Znxaq_^LrJ57S6 zcHkWGAec-Yo=F5gn8VlHPN0#Frpe(X!T!0uAV&SiYcnrF#Mgi1z=60)u(y&5<`JJH zsPMgyXw`mz(&U3d$P>g9NUZD^7D^duw0H=FTEV&gDf5F z|4RsHLvXjaVe%}vnJyDq$`A8zCBJ%RIgO)?n;lI(B@8~7Jc#sG57DhrQ;)u)f&oyS zsKb8x;xoa|JG60gC{X;Tv3dM5Oom$u@UsZp^$H}utS?DtO!+U&TB01`p!Yv)iyxBH zK+gPSs2kX?#9;F86l1MN+x}t&NIpEOaM01B5;&7r-s5G;js@zQ*Hj&iIk^{WRF!dk zrtFh^23%c0eQO05M)4Q!RoA9CCWu%=-^+W1bJpc0t=sgI6nw5NET!k*`c4Y_A`j5L zBMp_AUu8jHWGuLHYmJZLMF}v^5Y|UD=DJ86tAU*;^1{2a4)3(#pKt<(NT9tqO+CM_ z!;BGMq)mrwA$pEDll$R8h!w z%`1Bdog;B71-(_=yV!GWT6qD3!4@*%^kj#Fj{Z5WF~uKrkXqk$uSQZfr>6PKwr5be z(31-reUt6nxIrtXJHDbtiHpz5uZMLg2ko0#7i;_ivw1p7#TiTrH&9PJ6(!y+aKa=_ z*z64Ce|>uMxQm}bpjj`P=mWCW)!IJVnkp?blZKiQq;?Oqt%oK>9~hSlloh@;d1+Ah2Uo*N(B14c`kc6;bs#TDxT8 zb|jwCFB2X~Y;c;kn{)6Su(UX1GS!*i;AKh5iGNO$_nDhKd5Awfs|aW*;IMM4zp9Ib zBTORnu=<&~IXIU7H9d`MIpHLX0cqmtw!(pogv=w&Q^^>$OaKVIbk%dLT_20;qR+Hm z9rO=zdITsBQ1apPQ{m4lE^rqycoN~)pouStRq{L(w-H%pTTqJq2qJ@)10T-FpN|Nm zdcQmJXWQ9{@OmizAeu~epX@x7dwE02)p&ryg#LfuMDVr@qxXvD5(i}Z^PFm4^VUTh zw5J!S9zeIC=+_+Ek5M(psRFt6BV*i9ODNPDl$-L}mT?_hJ|HTS4b$MB29xMYgjwwC zKW8kn!*ZYq=s=$z;>Mu()W>O!{Ou7>S+}A1J-ho)%RlxB)%*(kMp36Apci^AjNzL)A`W zwH-KUc}H>FdFV14s;v{Wr|5278s=c7{rj>KmkC+&>o@#y-~rN&5Lk;^p7 z_kZBqw3ZGtI&`prZtsJACmL!C>y~xe@Sp%{Ag_mmDIF{aT1gAx-dS9O%LrT-aKBy~ z{QIHOHn?{#hRT%{N7w z7QhJLI_|s*kRvfS@|wvr{7M|8j_tnuUx+Iz2-y*=elU4%%XEpiIG`yIna8t~=x96= z86MGd{5s~KH}b2#SWxScThv}Oh3rxb=sna2zY#`|GRS^CIu22&NUG8#&6b zj7jJJl&eFt)mf{~!hSQXDpvG$sBu9kirA9kVmb0A%Kt(Iijp`0|EpJ61BX0Cj<)W8 ztqBG9LT8ezno2uM80-hD!ds(&uXb_d%P31%Sk}arL>cn@m>JTi&UxuG(AoL2xBf5N zyir>bUl}#yq@lwpYOJ=rx)DQ*Kd-0x+}$k%^G3sR&P#IBr>1*kBz5ZPKsYX^832|k zGEl(tt2RTM=0J_<^%+lLe!yc0?3SSG+yAG$U6H26_;eJc;nODeE0;Xp9uIo6@C@YF zX)?~v>TFY7(u)XZhP{z|STk_q+#gNSrvb-YuFQ^HmQ7Cm%3Mx3Kj328zanN0Y1qqC z(!%|qz+OdqZC7qz$oY-6F8_}xaBwFJE^VPiX|Iz8rC8?~U{zw=}Q%mHhOwiAF4ZAPc z(Cx-(O+| zRB^uXDl?@VCr7MPC^9@A&p~1!;Aw@l@6iM5`3>J^3fgE7trDGw1iPuh;>SS*6c$@yer!U;oaaL&9B(-TJB%5rv!Vf8mpw@HSaD>V6gBRqVo&BuKL8@UkB8i|Cn+erH~%vsS-FC_7UTEEZsGtSNf@ z5g?C@1fof-$$*)Z6WbuQ7{78P#&Dp9;_|&vW$Pi*Punp&%RaaR2{@|QkbsE14(w^t zF9W(7W#e@`WlbZp{ebdS!1N*V>R{=72zFFh4x}m;BpMC6)}Hf;QagH=I<0EtkeWMX zdZ`O~buI>AMdZSNANa`uOvw+9z7q+)uRvdR#-$J10&hVB)gMlqHM)Yi;RHfLDm>^V zR8#zm-9>I@=RR~kD3HhcA-;UtZ0nsKk^&jAwdA!P2`V%Fqq_q7?M z>ib1Vq6TMs=YcVjeNwVi5`7s#eaJS0gSKptUQg(Q9`i}9?qWhX(&hYb$yDL3w5;nUBjG5Ss2M5FIY2SUBNdhJ`gxLQ zbCYMD{={(5e78**s4lk!HVbWf(a?LgZFNr3`Fif@eH;Tau`1ek1pNGXdMwPfRLJ0g z&OEXQKVqIcsfi;!kdgi~VfGFk1Em}b(7o`L7mP}mRFEDR{j?=CeI1c2Fl85}L>|~b z(;F<)WT`eAU5TJZta%Mk_`09+(3Q|a*vK5SF9Xx6XInKbf#i?*ErvhlrI)vmU&Ib& zP%4B%T~)_bau)al4Yfbl99(c<2w#b=qYdC#H103N9EbA*0fYEN6Ri6$9;8J4fCOQhz9V)GiKv2Ys?ct=+e4IA|ZIv+kI*a%5QO_ z*H^A#Z9ga$^(kJOv^EbTw-l%7S^k)7&9~#XrtN&dLJVEBq3~_)vU&_Kv#4sPgqNZQ z90DJWi%Qjq*$I#+^K7#?U6ngoLSncb^gSg)ziCyaz*yP>WCYwzd?vu>y*Xr+3Vbgh(x3;Rn%IM?!KpWvM!C!=;LrkS$8& zMT8$d87}C#*1=Nh$eoFMU#_^atcRiAz)0PQv!QQ+Xr^>M5@H`LQC$gWc7Kk=>nBPi z5d|kG5+t9Nx&eYZ+hhz?0bU^a{chnFvso)aS}bONgVS5Pt$mAk;9vv~uIt_8U)mzD zHT?E?->9?yn8j0+OVLmBF*G_rO_5L_f1f1nzK9rGS(Pr7XVSq-DRhb?_5B_^VTva{ zs%uWrQ~#)loABzW@3|5_Fm{bY%s^h;uiE!`EtuHz51nP<)?*eBLaC&DVo9Z$eSw^%C?*oSGHMshfJ4%Pxh=a+}I{yP)V)$4ei# z@=NTNZT9mW0aNNpf!Xhn@hV4+9;Q{iMw|Yd6KY*LQ%goSRgb(2&?t&k0U1cPZ1*;? zpvep;`%%D5K%VHr3{r7Hn7Wp(_uRV;28li2(C_#Zff z{ItGC{!awUf!k;5KIAOE)39|wPtxx|(F_u_keHLFjnUYo13pr4kwJ?u^?U)oH&_uk zy%{KC`rdAFS6}9Bm5AD4H4d`FDC&O6LBK{8Fhfxn+H6!y7`~2?-2tyFKyMBNkRLytT!6 zt7YMxdVbwe73uOiSoXA-X?pzSPWJnH3;yPp0PE@UJ_GM7(xk*_nq?q8hj-vp0?1ce zNNW5qjaYLVsp0ly8a$|o74AzgW`)Uf3}Iht6$j1%FGzYJ`m191jiedOI4TNKwz{vH zKqP9PnYn=j{-Pr2m={P$CF%h{zE{&d6q4t^DuLTpQi6y3DVDbU!V~<`_#XzcGG@kO ze+1;im*q3l{RU{kr7N2@u@<)ej31~L_kYeY0LTDemQWQ1+)~<%0}?p*dV&{c(P4dR z)kt4kZ}gx1Rk>5kEoBCq{fAB6(WPrm+I)fNO9))l(suaWK}}}DW*Q`rBAU<@SE-p* z*{Y%nxuy80wK-51%+uw3+%lYy*kGfP(&2b4j1vMb=l}s-(3w$|{2NXKyEbNu%Cb~< z>=W@vy5;=mFgP9FZQhpl*Yk{g(<7G0uyV7Zok}-8ngt|;$@U%l2z_!V=IIcYRb%e3%v#<$d;2M%Srrl(MvrSzR zvdLJeSE(d9<&vCx6#i9zp*(KY#3@|mfEUf|by7JjIUt1Suj+)af=znD)Y)CN8-^W! zXXT8f2$_FkW$s%NHpI%J;f3_dZrQiO(cNIFQRdXn5{trh8HB^KQ0+bV-+b}tM zqE6v*M@62qrXBsaVY;=m@27{hk~6I9h4pAR<{sws4o>_rU< z&5+R1^#t?Y?x1X3Pu?@>U}}L*gOHwoK+ExceO>s;-e_h4h#T&R4vek!f9w>Mwvot2 z@2*FgA`y)N>dB>klpc)t39>kvWmiZAWKvtgue1&!$#8HHh4vK!3E*(EQgFgCxUr&Z z8lP<2vPXDZB@coXCV^+}%h*e8d&+e7mZO9Pd48D0Jwp)(r@6w?$5VWM9&Zr3LSdb= z_jGHQMn&eIApd{+=i1?R*Tf>uPLcR4wqtm{@l#85RJ0=70$n1oMsuJG{OBa9!sgE` zm%b{{UR7d+IzSvQ-kF{}HO6?sKI~l-Xi&hq(dhCy3>eApfq?)_V^hRN0&Yt*oz5MV zH3ru%V1|58R(jr_71oC>Z6?{);^i#1L!X(#RtL)qhEs?zBQTs8F0d(C{|z2B8^Ec8 z#+=YZECc`YUSMl}C=aLANJFpB>YD!@&%cA5Alwi0aEYe@p2K`lh?Il~PTgs1R&6~6 zhr*~EH(E4J9smhD zaZLfAhoSirI+2^%B!!7t0OuQ8FNk3LphJnv{_Z z#7%7bT!?P&+>Hf7Q31q3=@?eoxB|<(J+`-X&Aa+!SYt5)OI1W+uFu*lQFk3MUBOl(|VsdpnxJwtyK<)%&htkpnyI_Nqoceya`%jlYV zHN8RsPcIdi`Kb;dZI==qTVN238BFXy5~JUfV?>;I*~I?9R9gpUmtTM8J@?SbJ6yKh zhsb1#Z%bsPVin9-czSkk%bmt?O=M$B|2%Bo zt;;(V+-kkh49dF97Kpi|KX%*W*i>=TS=4AO8w?w?AugIL1 zA1Y+>9ADjd20Em&X4wUqic2R3ruo_i_Cvh*TnCf>t4>Tw>;C9gon!N_)`G$yMQlXS_uKbu^_+Z8U zMb{T`)@}Y+DOC6uM=sym)kCQq{)2TG9pR0*N)YV$QUpxvM5)8SzAZ#~-O}2Oe6g7m zmN89mlu;8rWj)WO{Ga-uZS~B+5N~O|dLpRkW)Frx4>BR#F_=bMt4f?a?6Gvkg@V5L zi~i@IW}#UGV}|B0p@t+S*zjeHl1F%wSZgi4`j6uDB16nx@!iW8=N<5~7F#XWNx$HQ z`sjj=&K(D0Sy?~t%HJHaIKS(JiXoVCPIP-a9S^#3l4`j>G73HPOuUR{Pr`vFDZed| zCSi1rgxSg}EF;XL{8BMFu+rA5>QCFL*rZpB+8;*~!ZwX4X6y*r@xZ@m4*N1kWP`wR z8Rgp5M?d3fbUVh8Y1ir!66&b=zb{R7o!D2Ej(0%y=l9aOYsuWUV3o@5z?EgC zmz!Hac5}J}XL)8tfio8FaFA6S>cYm)uB0qmembBTeGD_okoDwAB2Bcb&ASWe)slpq z(&R{GRp(}E1Rz#H=6SJ8+URJ>(!z&aPTlZiddt`{Z4*wRTgG)`B55B7&_v@~%UnKK z?R=E)>VV9^X9Uf2n<+Wuh8^qci`+t^x5o)lLW z(429Zl2&P<0>WJ%coiHDn+JV+^F!^WRcmZRIgSw(ZX# z7-h0Fs~U!l4yrrJM@6|tm}G!giU$&yE4u7}-zguxlSB&>sUoZJcESD-Vq|b+aQ##- zGZN>3e|SF7@gH|6qiki^beUJToll)KD%TAndJLg4wzVEmWirn$T_Z*!6a`yZpkN*W zsVL|r-=s}ssCmNu=dv&XO0O%sVASX)bWT#WFBMS=*QTznxXi_?@GgeZhFwpaRzW@+!q zI{j3?lxid;WF#aD+?owMIdiS1OZK^jPtolR7DdfZDfQOvhc7TuC=jlJjee_&rXU&O zIlL|BV;@rXN*{q0X$3Y;aaRSGK#EjvcB|T~qjs*bN!_rx;fal_2tw`So(Zo6#@I`g zZ8C+}6}j$zZq;7s6OnUt9=zwrj7HVvlODcIAhx4z=q=Fvqa>$#GjeG?SWZO_lEtKqpCTf@Rae{n_gVJAKmAx z4q#T~p|Ku>nI)|!bR@@1X><_PxzPg@QEdUIgIUzReM z%26FbvknOeR&B%H-&f(LNIRyZWLejSQ51~_D8Ery(2MyGWtIr{ru?D%YrF5nU;o{^ z7DaYl#1KU!52Skc5?l&aH^Ah`BG3gqbmz>!CV|VkGPg`6;B?R$-ExtAQmC>43!0WU zTh31w+RYgtwLx1#MjIb+@!hy%tDS12_{(HGeOS1`x`Q8Y8jen-JU7FVTMpPSa&J4| zwJ6<4p5+C;SwR4wVEU|GXwfa8k2*?~r~soQFx2Y(2Hn*YH}!s?<=*Q|Jx~0Wp{bT5 zEez5*bB>~@@uvWK1ITYEYagtXH{M@EMM-!jy@TI+ZVQn**F8}tlH=$G{BcGU;-C5! zVn4jS<`zTZF{?96l!gPO^_gI=C#%EmHzP?{4G`hq;48u z9u$mfla*fAuK~;R&nhHiy@?MHss&qEl%- z3nZ!wgC+dXVTs2(8;T=m^3_F<>L3={J%{p_&ai&Mr-Z7;Qx=_A*{6oW*5p{mz@89nME~);uD^AXZqW}h-%t^` znQdL|j~FCzQWb6t=5>lGIraQl-SCY;*WgZ4B$Z1et40-QfiX~O4}?9;b}9R%F0n;{ zlq(~XG>p245d;CdvVOb-5rXxZyaFd@uEaVk@}ez58c1|AJ8u?|{FCT&-4`@lol~Do z&f`g0+tI4vz+}MBKSXIbN?VZZciPKW>SXHH*Ui~jjczpkPKCNkaBVNW!0zPha~%@- zNdQj48F@iV3v`uTF#bvf>#HY8f<}K|AzFGcVIe|hSIC`g`I+xb0u|j-0dXNJ+3o3fcp4Tl{aXHiL-`0a{i?)>q&e~%8IiuLdn2_7~U zbWQ+9p8ym6_(K-D?S7hmxbHMWZYLkga7)sN@i2u`)8X2w-&zFA!w||I{ni<1zNikq zhH8AdQwr_Jl!Zllw9R4hKKjDoUWi7-K%z{~F!1YIzNE>jQM;Qfxt~M#uFym_@NLM9 zUxYVEVlD7^A)iUaU%D;FNzm&$zPrVh%1GB;l2ZvvC(-v1)adYBNh7j;K4;TO+&DW* zjpt8c;7VlMtxdNC`ggv?okS-$UqF~tuP-}Qv{OXv(=uLbV8l^%NhnJIbh(y1^(>W( zT#hI(>29|clhzNIhCfm%8BNgh05W$-;s+5CHhk)D=1U*4r(nz6)w5MP0~Xl(=YHBh zq%N7LbKigOn$~S&x7*5^=22_8orI*2gNUOwMbdGiT3<3@;Mb~@k)MvtoYV{d3gZfz zyPL){kyeg0V=Dx~7v!;5hcBT%4+8cw!*4-+#*dqjyrkUPGXmNbWXFOthqtQVhIlz`8IQTdWFcWh5DL-rC$8I7n`tp=weBj=eTWFx8%v3@Az!rDqvF{`-4 zg=E*^PmPI9;C_Jku(ssr%>bP6*{U z>S^YN6DowZoVXd!V?MP@#5`W9>zGf`19mK0{71@?{;w^X)}ZW7P$5Xj_0bSrv7H6U z>qqb-WPwiA=BDB0rJsLjO|@s4qXZ_C5gsgps=(DZ^r71#z1)Er-R5+3)^=XfC}Lel zUeZ}D1~q$8)i}E_)U>c53l+rDx}a4!-y?diWOPon?Fx`1HT*#n7nP*bB8ZZlf%%$c zgVd8cV4w;L-JYVvwx$4QjxU8U*q{uy5)em{YAeORT+htwZD?XO4p63iubFYT3)$p* zYstl+#v4%7-dw&;;`s)8DDFhOCATXid?Z&@9~uu}Fds&T3{HmkQ+5t-Zjc~0!Oe4e z_Rh|n_d;Z26yR^!?Bx4oSn+TLeaGp6bdOe;`E;No`ULk&{+&lUb6#5!(?qM-d|O0- zOV>OQ2arE+e>OZkXYgnFyu~%;$vYZM<5S6&IBio89gEK0PCL}o3OZ;~2lVF|5TGXo zr-42xLc#1S1R2DuE^xnsf6<~)DoM(8aIUVo}yNgI8;tt4b0F8a1lAE4Jz|M zLz_aB9R2ysQYONx2ct#nyn|8;Z3YYr&yID&dL5^z5K4V8G+=FqJB%@{e}Hk~n(_Ia z4#W00+U1KNTkL9q9cVMsNPNSlDF_!0H#8t*XCLy|y7_0HAEU&05@?WLhbT|tOz z2QE)&i87AEm~&cJd0mnX2?g+h5aHpcNx>mxlj&s44Y07>3lOhoZK6af2W!!Af8Q56lPDWQ zrx^`}!Mo^<5iY+e6~A&wz`ByyjIcQR0PN-fKcP(&u#StjP7VoN#R@cAj0HzHflP%L z(8vsW=gAPgMjB&#K1!s8UO3fi?jQ6flY^vo6VkFR!$BEGB1$?TRLqi&jcjqI9h*ogt02PDc+>DgA`B z(_`DOMgFvNxSYU(0_VN4stWuVo| zpaOiT(hPyq=y6dhBvAJVK+eD4h^RFCTbXNzg3D@vnoXp;F0m_p+r1jR4`)4=%#15hU~lED zLBrG4ct?Ay%?Tmq|J_Kh8y^+Tu2IL+ORN4cpXQNp%X>1t zT04)?eQjix_?O$0BZyYRag_9@3euBn1hCjnSOrZ)4e##tV^z-5FK2c{A^90P;OFmE zt-6QV?8tLahW*0|#4}<>il19PU|6wExiq_txOAKX1A5J~*Q3UE6T`HL8f#jlypr#L z+mlbTtp_Ood5x(`f?!vS*XO3{qStbkYEm0JQ)tV1)W*(K;WIu;iXv>UBadY&d zt84GGhJ7<=-|bUd83)xs?gyV#@J}UVN{7To3X^z%uR8hx%(}$>(7ztD_d=KVS5R(Y zg+3dSmYYfrs;s-xo`*}QF)(xf#L@BH-&%c7;~p57Mvn<^RaD-Eg9{Ja&b3 zp=q{C`y8=897LHyz;9X!pDLa+2To}-h@>KR zA8=?qiKdpYcNJP+2Tz!0HE5Q+RKlIUOJ_Wdbo!I5R|NhGLLIq4``{79 zBv%EZC!X6xg4KI-JFtu!b}2z=GlO;=OipahdjoPM&L)2&s1C$HE(jjD$zYf29!LDu zm?D*bwXu#G_075)ckRKoxu`gkO&BD{9Fzg!)7s`dp>aBgNdWvpWa?_lqcshdCo{JC?yO z6U>m{hP5SBd8WYr?!@FeG7(|hT&***O|pm)1Z6QY+(k(8Xmz>JW_I$i;aSXvu>JTG zz+aH>T)Od3siC+$bJRb-y0SM0ez@uIo915ij?2YsMW1NKFJMdI1*VP<=~ zKbPdqZrAk$@J!o1C{?R$=mn;(hqH(m0r*}c51Iw)l5JaLb%!_DCA&CROBacXu17^X z83v(6>3_l52Hedd{rz(RARPNU$SYNJyfbm8jQGQD%bDa{hSvB=dDOXy=QENNCJf2? z#e`moJ@5Wb`VNTz3TD;$7Tb zX|MidzBX&DzA;3UE|?Nks}0w(D&NCo;U2KX`41F!w8O(_kWT%_zQl{+WKh?dcLPc+ zAC^X+etPIgoF2KGQ*_)k>OXJQR=EjGh@Zc}*u)@I2F1o((#4i`dh-gaI3*cFTSG{! zA{j=XHi{+zM(5!-)(qv>R}#*vMyfa=1MX(-w{xRS_%svq%?+;OAHa6odp`+mXSr<| zbI&jR+Yl1}oaHHtYaUds{8?TOX%-(q6#b`qGokg4BCU$}_Sx!)uQ$v7>y5KsmBhDH z+KZ&|K;)14MgR-oV+9+YY#9Rx{;rlbOGK^|rQKO!D&(Dh)&!4(7JZOVbrl48YV+o= zdFfPte~P*Fh(bs}`r6jWt5+5FU0yO>*Nbdst=XDEjiw2KI*ixM|aXjBABspyo*h2(uhOo@WGeT?Cl$(xT z6g8IiT3M3XTO~2tInOr|pZ=Es+@3jy2fD488@`aw95UcPw}sg4Mww+So$Tr(m)8iC zNVo@iQ!OOGKMk4ZRZG@+A$IU1pQWUv;5e2LJ5*NVc;DpLqr>y1g((nEg^hGT`SseB zg<~;K1S%p#8Jtqti|j{{)!)tHNLS#9!0g)Qu6iHqqvy~j-Q}!Df$&P$Syas?xt9HRT%Zo$U>oiB}S|Prc z08wp^1VQIkNu7LMgc}D7_2=Es!Cck55Q({Gj3@eG-`;{3YY*^fYpZ+suXF z4L`m&mVH;kuH7w{2tPB~`!&mG6C0a_`Nd>&s3}s9cq2NClb6lt%Vr1TqBo3O-=%wX z>&=rz*CGtc!h~Tc1{DQuiT?Eq%fVfqPc+Ax^h~W&KxaNQDSXtw#0dB>8vS-FNpO`Z zrluN@klN4x*vd}b#E4((C1DwG%XO9JPiGoe_K0iUUkhU;Xz5nA#*ByRnRXEdX|fg& zQtvJ-+W0B1yr*jX6BUea7~zIw`!VKil*T}mnID6t2$wL-2e%0#r2?(HhhRqr8)C4Z zD&b^8Dvw_{fLjD?1~%s?Qj>qMJB^yU-oD1IMB!Mjuf&9T=R%B+lA9|r&zb7d^+Mw0 z4CcS)hEKbB!if#U-2lz*=)$VpF;Ie-fr|&Abm^X|h;B2cu1V(&p>dE`5W-)QTZv_V zL%qG;YK;P#CCebWDh_+oHlS<7at#sqj3mG=!o&e!?}-Vg;1Nq&v~R3-m5&N# z0eSI@f4OtJD_upU1+gU)xv3o` z&Zz{dA^A{MqeKs3E|t7BVujX7yk2A>kTJv2ZSaFwl`~okesnFtRp%Q~oHt-uX=orHK1i20GuGb?G%bPu9K@Du-H%eyVXA=x5UgIXYh>S)(+#N-{e8EdrnGAZ zwL{~-QUgT)q39|}JF!*2jQN5qy*b6@&s_RZ;JSDyv&(i-Dyu&hY1Ilgn;r;~{CZ?wC*~ zg9urGjg<*O!KhGx1WGQIa{_Xd4jGQF_s9+!Ly%O(3<--j}|?P#&h?f%M6VUJLkz$od$mOG0!CW(`;>0PT3{U zt|~|SceyRCfKxW2H{BP0#M}EZp*dq3i&0YU2$)vkq{Et>`&i&epiPoLj};VN?4VeS zBWqVNpOSYL4Kv>mzVDp^doO{ys)YyJG`*<#%+{R1s5ovy{fvtf+UfT+U1Pnayz?0}gpIfNF`ZL{%d+F+bl zUu*j_;%ND{gaEv>CA{PFa)K>U37C$)bFjku3*#w}uRgfrL&Q~`i8wp&feC_~bPaZn z%-OqwCc8VOQLJY~|KjQ$83n8!Y*Clv?SP1^`bij;2oGyu80em*bitu4z48%H-*H}7 zf<^0Cm-q!cAbD!mwvh1YNVGw$&GwF|Qi}Qs+Qxn2ox7d_G!}^?yq#(@e)+pp{`nOieJGq}@t%;WC z+$8h|{%US&@qj7ld&D_xWdM$#LDj%bn67JaP%4?iJ{*!gpevlF^6l#l?S#)ooBDni zZ8wn#a8d&TLP8q)GMMXFhr9eG9!Fh^5~wY7d#)72$*uC}`}5Rr#y)yd>4B1+T55b@ zTK*O+)EORKc(zqbhrrW&iNmvHqs3lIvnUFNuy6*3|#4+~QlA&jz{yYCayc(&z5Q_w^Z(xMdPof3ymBuzsP$r!s`DG5BMkfmuJvv8vq{L(~d zXzVbNW0D!u1ICIM*|^#in!M5H_frVVgBQpE`RjBQhk}r#EREcvT9}@Im~vdrUht$dS-L_)K?I-7 z2b>YAjJBu$0$F}r99Oq3KtJQc*);iEz#}AO%2u3b!Jr>a|A6h&jPAK8!nS4fkcPJ~ zPyPHU6u_GSR(Y)JAvO=Cf9E^IIZtbQ|?e4;7UK+=i}& zkMuer)X7=qca zt<>Iw(`^n>c?P>luhbAjg6}dtOs+S9K;S&v|8mzI%n~dm3r*|IP;v)i$m<3)9rmVK zt=T8cO^Kq~WO|+#6y8{jq|#GsO!daC?Vs_z_c$Hl0yyt*i{cW5R7#Sn5#YpjX^|a6y;HZ)%eAIzD`4>!8l<^>JWj{Nijk59myno~d)yik%FrGxl;sDfHJI8+$z zlH42HC}b2>BY>^xQj=EVH_IdIJNUyU6MY*kXLk7Zk(&**P7y_s1c$z3Kan!BX;Q|E zAZjuWXf;>`$@p>=yFz} z`qO-!CB7DWG=^;gSi|dBo972j}=V>v-n7_hE|UwkdSrt}E-l)c1{bpAE|I5Pqs z3L<6nbL>k3jG^Sui-X60BaK>R#;iaiO%Knj8v&uL8W*OB`SvPU!rN;^vEva!gLxQ} zE&ZuoAt2-op%nV`choAMVe?n`j|__VTxbJQ<}^MqtSkQxA%eV*Q4L*-t(!d@H*1`{ zOiq$zM;$xIw4B1VItnHNM^uL!h6b(8rF9&)Y;OGqOPc{vIAR0Sx0RyP6B?802nK z((-~n5bokeN6&rF85w0ko}Q^o%fY)7%D96^UDuN+)gY@Q5Jw_Aoy2DF1h|*=$IB`c z_W(^@^%Kv65U8Rgbq4touY0KjLZE7yBIIj9yV39WHXn%8lbrVw>M4D`k>@00MC2!l zxtj2e^7t8jcS$*~(hIbr1DF_sZ*`Xgxsm0gX$e4-MO}RXh3|Rs`A|ZKqZq;i*Avz& z@+yioGf9dG$zqNK>FZmUD)8mMg+J;JJg>+eV8{?XLRLt4=ph!oc`#m;zlaqQ9XEZ_ zA~nR54I(ZbSTaMRs5J6g7#2->_zW8v0esG+ zDH}dTUeaJR zc62fify55Yhhadc=aU_{Pura+*RSH;mN=$g`R66Gp}?C#Aj_D2H@Tq)fJGcC=wU=9 zj=)F_LcAgYJo3f}=4!u%M*T0`iC;v_Qh}$nMWmA|K5MDgPZcBAcVozf70pTPk3#(% zW~AdR7*a-uWxOwbVrW)|FkS^Q+Ihp&ik zhx|MaNp{506?NswzFtS?{A(gfjK}Czy3X1{*j$BQxwy6U^qAy)QJn0Jh5TkKufNmU zr$UK(i8qptVh%3lVQf6{^z-xyq0lP=3n3H118mFI1WOm?{u!v(pXaUc9X^D4QkBFT z-WUs&L$UmL$g`3MIa?KM>Z>O6SFBX2)}@TjANooCU&WccnEL^aKRYB$D3T zq=vpS0nwrTv8A|5z&CGM00ukw1v5%uPW`5X#z|0782+wEG0YGpcHOJP6F-kmz_ez< z{7nVo(8%;&L76Q+zbYST5{ZC%$-q*%eAynVzf`gjJ1FY<9Azb0GO)r+3yOK&RrEd3 zY1kJ%&_KHtg4lB82O@vlFEJAKz<8a;C^)7Em4EP})P0TT<@6(%v)V;(@i3A>9IY zG=epnT6nVmt#tZGTZ6u`qsssw?>G45j?Fc_1KW8g?#(usyIv-~py;DWI#?@3MFp*r z5lq6DfP_7y=Pc5@2L5khIJdKE*}ATv+7&emdcRp@^9Y3M#f#N{Skg94r&gOMHm~X$ z^zYf+dhFa)q-Phk-f(V{47K>Xa9mGw?FLQ!{d07lC?twr>xAvreWVbE_$T^3pDLjI z?yh_x-GxGdHBkqD1|l5U7oE4v?pZP3h(|gyXRsBtpL&Xywf$JJx!vh|*@*~A15uFM zLX?hXq%%DXfsbyqAWmqi_NuLyty*x(D1a^Xw$xZw%*5x{VlxbaW-TQsmh6=Dd(tP) zS=H2{NyStz*D{b_pe#N6z5-+O5h(ur_>-Ks8|l}EquD7R8bjbciO9eS%n1f#CBsrh z)oc5*oIqx*=x6xRgZosznHs#*0V?490%hAEBZIVG4saQI!X{MOQ74+FSj?std9~Yy zH;;pJWsSE6Nsw%JkmEOPl0%?dj7AnPT;9*N>dr7bE8{qk?T$jN7eT60I)b}Vtt43x zq@RI{dRluBP=hA(3|}E*6eM!IDhDb0h<<0Z$}ciJ;0CPiCZrUJZ_~P{Yl$WkW~4?4 zJgj_c&7&nVq%cSnhJna6NhF(f#XQ0q99as2B14*2oK`4+-=rNhm+8S;_)G^2YPCdw0 zFJDsDoCiC7l&k$BagYW%r=^5K*~(3d16DwV_;oH!lVD#x>5BhebCY`r4JJrqdP?6X zmg=7dr)YN3IY7fN#shXMBWDhRQQ1-I-w2+ouMm3T#8aEAh>+PfMuxbgjm{8o8fwAe z9wvKE$wp)fk}l`5v%F$}vHJGvix4LjVl(xsjq){@ftl6R%Xbtk#%I5$Hgjz+@pjV!kX75}W9`B9lJW61L|g1* zWkp|v2ennkB7EnZA7i8=k!VI)<+zzkNZIFv<{bC_&z-t+#REHAZ9 z(p_2wxkNANDTYZP7)G)|7T(-+t&MBp6WeZ1M9NyVg4Yuc3SHx)oCFyREi=<%h*_@X zwtR2JqNiQ>ukO|O;RV65O~!Li@UIzuxrtGhBX+MfX5Yj7{Q7>Gofyv}Tza8Z)LJ)` zvL7LJwL%7*>|2r*^X&mBJNmnCm6Mp*eIM6s)>OL6VbPdIZ_C2p{c8<17h)?WVL&^6 z9g;@+D|gR_3eZKt@5f;Tj@$URQl0-Z-pKGC>^Ol`cYA_i9v?>8}=TE_uWV?!|LUZWo(_B_GO&^vguu7khgY z+iY6ZMJXK;AFS{17tq!W=J><~m_S+@4o5g%TepB>hO~9Eu3X=hpdC%08Hsx7PkH1_ z3ajxa=;*|Gch2$s-edw!j;76fGK|p_QfBOB-=D>?O&SGoqEIk6bxok`4hCmFA6uG~ zmKj^3N1@dV#mT0ivq|9O$rW(HF9Pp|@=8$N7^jm_Y_ZHo-*)#pB-QtTtd;YD(t*IG zCT^0iBi-`oq07BfYkq@t;BT8_cCP|I%lFDt(MN)c(%ZU)&_b4k zj1v+Y@dt}IB0G)f8}X*odH<_6`8!f-w;~R*_drrr1E%(i8$3!ukY)ro7-eZPz9e?& z_ffGL8+7<}RJKF09=6MKNb^i+%`pRZ8TIOzP%`drD{}9$mKNwCU8J)=4 z-U^YJ70XNR&&PEabZT=0K?R$g(b^)6c5WWZY-dYt`<#IYRoJ8z)B;d80^zyz(eSh2 zqCd!L{c%)=)vz9rYCa^}-eXL`!0x+?0*L=g^x`{pCBzp?AG2reh%2B^#km>6XhZLU z!K`COs>ZPit8gjMeSBb%w!Jp~$VHMa!VeuWWKGy{8qn*uEs{j+k6N9AmtjOWrZX9* z77^ly?!dt>8wsO$FCWZXtkZWj1k5Ck{#qOtzRueJEc#Y!s#fi&cz2J_;EyR7o8rI? z9Jl4O0}T&xo4R!@ruzXJmVB+~!_$FGBiyion~%}mXCXPZKo@hjnnM1I?~(2RV=Wo+ zlt9hVP;)8PM)tW|$PdqTIPfYdkmM}$qA~1EvDRcm&~wtV65ulV2GN8Y6Fw9nb>R^* zwScqRILD?|02J-`K_k(Qv8oU!SL36eam=19oY@vE8w{q)ViZk}#kG?ebdz5aKB)yy zr@dft)`lksroLIZ0;g+?s88TgMNSU zl)F8!^X63l@j6=M>uNK<+w^!S!2lm-RKK)hwgZ&(!O`aR~ zW_KtNN{a3sU$l*!$Byt0z;*MBg+y&ez8dm;ri$F2#!{<5M_-|4D~d$}cu_YS>y6V+ z@~AZ@7UY7o$)@}(AFzaD!q@`x000DoC1l)xtbQwMH_rQo$Ep!AsIMx9H}oS*Vr$;m zLfL6@#8Gc`{6_&un4UUc;YRy)^)4@0epYNg8Yn~vx%k*FHuA$46~gTmmIgkuiNvP( z2xWaJW-FnlII$FN0~V{;iJH^jj!K~eTC%`j{xiC*-a0vCnoG23isO!WvM6+drK0C^ zqKWyQi#-_zezVxL+ew$}*Ct}c15~Mn5WE~!D3F2=MFR?DvJA_Y4xC*V@iYz^`gQ*Q zCb+3=M#Y1jhqVtz_QO{K2N(*fJLuJ5?`KmO53Yw>?W*Y!vwofjG-I+A7HLH=LG=$s zTH}4*2oinF+;BVbtR8?008_f4^NcqCJM!Rz#T)dCT=+2BRLT>o>1 z9n9WyD@VVZh{;zV8D#(c8S&`pW3(UvYloq|xC37gim+B5$)Qf<_ipTPI}JWoQ0NRO zUDppLJ;eJgWVORa#nNfndv9g`xhDRZOSeBv-+`_HE|T8|AubK>HT}fNjBSiQeDqpSQdy1jU@tY}^CM;5xcj=|j0yG(X-|g@m;KN+Kc|Xp^aCzg6;GFp0V04}z zG$X|n`DvrioI)WA@yzCpmuhD)Lk9sE565kg#@ltkyU&U3X4w?SF-5&8D5Sec-#_eJRV?H-252zYmE$ zP7p*YFfgl0ruc-S6SuE(iN+-48Rw(m+O{(@s#||!8~gBm0sBW|cwavyFU|2(0s2a~ zimszH0;a$r^$+-tQI^K8?R9Hc#eEtU^FOvR;`nd@?Kt4;XRU6MQ=fQK78t*?FRvo- z7HJMna)Am}TW)u&xV3bwa|=&Gm&iB)CAt7rUpdX^ou|t8a(2Lebs`8=_UHG4H5X;q zuh28Cl=-MS^mtqt-P7yuOW*GV=BwB`cS9LuY^l%9!Qe^pPV0{a_c#IrdJruH5hS_1 zsjD-x|1=Wdv1_BX3jUD*v~T;9D1u^OCG_WVw%r=reF=hKClV17+wE9yjL5)X{p^Wh zUNT`&n|aC6!y26f&vCdxLLT)4wx1XvV8Y9|aRDcz=Y#J5FFX?geOR7+cyQF!1Ucx= zYGd-oc1cCRuyi~}ypOG^q}tFlEvqF^G`qMT(y-iV?G#Fb5Dx9&n|0H{uX!b+XoHAJ zU%FpY@8!$A=`d_suskFkWT=$x+sIF_t^%)SsxAOEuTVIEOmsjl9~`e#?Wp}`KZAAb z9TaEapAI;_j?s%2p*D^vNc;EP2gI?QH~_e)bsYgwj}l7?2iY`O9oz6!9adgbX!t!5 zrWNsuFQlQOo`lNzX%=*2S!u=EM%8FS?3RDqhg**E?v=fFW5+ zi~B{|?3;W}t2zMit;g$7K*}1G)KSdhL<}yLu+b#QB+UVo$WTsu#L;nuSCWq(==}Yo z-5Mrkj`ReawNy;dI;7Ge;$`q9{~v!itYpGV#SY!VG5|{z`huXZjnH2G)iv34jd|yn z#189%L1WYVOL>wZWJ>$k@f;Ez5WKXSuR$eCUNb?ohB-kb5@|K$3{2SX<5f20Sd;$zzs`G(1!gr8vjXno6_q?o_x`O86Y{K^3KyrFF=ObX(mbP&H6?U#c zZoRxO1f*a3njl%uAUB-BtivMm^5s1Zlx!ULYy>W;CI;wv1;-3X7={&j2WVwYvLimA za}_kjHfOmrPa7(odg2fx=O|1xzT9rB<*UMALS1|VUYtoVmwlSFB0Gg9nF>O}#aR#k z)d{x``+uf(5ZwZ|8gux~fr{YC*fpn`sv1Q@X~e#>&Llwaz*1e3LXHY!u@%DI zgCka+Jl4B4sIv$DHjta<+Rj)|sD$Z;R4tzj-Ajgvhhs4k_kbaN%-pHSvw0ly`JF@m zG9!XFtLkw2%=wp|%}Ku`q5E>UBgA9Zn(Aa=D0vE$iafhQf2tJq?ph)Ji^ZQ-455uK z!|ZQ&AdE>1z=#`w(&~KS0O$$e2s;a?YEM4eU97Q+kCmmZmvaB1-(;zXH6uM$I#SSk zM&E-2xfMS3@Sz@VrbcNzfnn2$i#+Fu#aT_VG1{_Ht$*vUn*EZ@Si^^^y&2j*`!~hs z9#AV5$(%+AEf0M|Q&$~KX#80t$ElUlA&-hVVJJKujq0>u9cQ6H%=#IGPk<$0%S(N9 zJJu8)eka1Yb@g!$ISSV)F*j)8|e}aUUPHZ-=|F5;FSZ*e^ca9 zg2qZdmL722+1iM3#njWD8Fk8Y8^=*LGrZGT^hyZev}=*=E^hr_h@W z$OI`E;YKp&U04FOERM)`p85aK@KSI_zdV4-GW=(C@Hj(i->>DY#$!&impJuq`f5Fa zO)E0q^3m@{-iF66jIg4+E5^fOd8bLdZEjj;``J%Is;TV<;5UQFyHLZ?*edc``h^s3 z?W}?VUzk~-s_3dQtYXX`8?AWRFzPU+v)=lN;Pj|Voprk1PT;f_ov+VGb#V5II802_ z{7u#$!$0yRfo^CW9+DYlmJTM7(EP1L`-o@wTRXFagh@SMgJl}y=!@2h2C`Y81PG;5yR&l7faR!FYG5Fo(e zN>G*#l!$9!gfq}w35**kygzCWydf>1D8(VD|7Pd(KuM3!sF?T}2`pt??9y~6{rqa@ zp`;ptLuy`hP58xTB34i#pDnVKTszV)N}fS6=kG24ch&HxE3oIV!g~@wluiT91^w*e z{O+xcq9;f0&{-a(uK{fV0UWQj_0)q$Vo!pNWcT3rn%~iM99;%m0TXxS(z_H373IB2TXr1uB}o-U_f@^Kt-fNmBOfM5y-jhk&|J;f=~x zXo4{wR%kUik!DbW?n_e=&x<-6lJjJ2pW%9iQJ;e}3P}%a5~0byej|4yoG{s1k@Th? zB3a$+Qo)lzW4N7bMLe&gvtim}z%G<@<0SmWIgiGjG53aqplh_ZIxHZly6ndQAiH;!zECHsQn{yQR$FJz>s(9?eju zGUhm3l3w-wnU>V==5^t?;ijPXQmcOlEvDJdM1^3qJAnDdT#+1&G7&A$v)O6xKVB0c z-x!LwY6DYjpXPw8r(j|@6S?@XiyoUFEW>!RM|V~8&qA0wG*_EKuZ&u#idm{F@4V90 zzMbuynvodEL_T2`(WfJHAi$!|vp-?h7CQS*If8jIvEbA{qu=RD*B{&@CsoAD-|wWk zIg3U695IctO~(55^hw=vM2cfUm~ptJ&IRgg7p37odxZ?i0}g?@e+78#-DsEzJ!zT3 zFl3~1C>3VhM6idv>-cN#r4-2(XX{?8b$u(^@u&jKC}!@N($Gd{w*iDjfj zd87zP?-LFaNhH>1DoYvV1ZN7Dh-Jevg+Cz!p?iM11&;dgHLZ=OxKRF0mR^5SM06;+ zs#C z!%fLJbVUHhC`$vDnr#i^q<(mYnj)qBtLqGyD8WI6t^COdtPt;TMzp2M`-K6k+Vf2O zhsHxU0!}mGHYM*ciu5Aw)xhVFTE5k(^iyZ|7ZP8^Q52$k>__X>;IJJsh%l`XnX;NjE0K+pE}Y2RfsmDx9I|c zUu69*85rzvQ}O{QgQG3W0o(+&h)l7{%1m9sVCIOXw;%nfTC95P@@7=;w=sCWg#-sp zqDk@oRkUQ%kdV5JilDVY}5qn~iS_82kcw(r`Y6*r1vq!)v z+}YgdYEZs*>DPQLu}S3IGL0|ah7GJ7M{w*HtQ3a{j&iD?$0LeAqR01v;Gim#VXiPNpM!(fMu+1omZ7w{;`4=aJaxsBP zD+2T*7RQcZ+BO_Jv-g~86UMFHlS#84Jcj1$lvmhyUhFXN7RH+zU(pO6t-w6e^2&v4 zu<7WQoY%a1J3X#U7i374i^$qJ3T=d#EF&NCzLTOxO)Pn%H1I{_kv?rcgm%3?3rWMl zPBV)a8eM8=gHs?v+n^xw{LCH8PkJE7n+R-~ z21(ZiVRs{)?5@x|OOA!cIT#(k3!$bHYNx5^13+K|PofI5mL5jbJfI`NFIwyBIX2VVoHKe$FO ztxA-<=rKDNxUpawI#V2K84e=nE33NemdV*K4QXcV00D@rQUK_TYVMsw)&N=qk4#&i z)lPo+)Td-}3(^Wu^hxzQuNfzL90wckaVo(_y}mmoThi$((CZPzWo+LbSV)EC#=XAq z(|pSsw>%U#^bO9-k+@b6oBvw+vB;RV@9Wzc3shDuXG3(4FvE6#hT)~`#7pyVtQai~ z*SZZFsF8YD_>YjKpxEG}M{1R3!?G>N6El$Y0G#5O)280nc%V5oL#QeNn>=4fmA`(4 z>u3REyoASdq7%t+h&I@*GA$6YzZpaUbI_`XX!&@s*KViKQLpIVcO+5{5@f$wmhf~S zVAy1@LC!!onT+4sF<_>uF?rr%8m>+~gDaK*I-@{p_@aFH^p_aTJ!!$RDSgBw`~`Wl zF5IJxZ`JN~q!emXLT_btu5LDV___$YA_4E&1zx?7xFLmTkYiK+3K8YnNDd3}3u9{Y zF_5?iP-hOn&}@JlqwJTNtYc{kMN|O0b{Vc)|7bO&V*5RTGzK33c4yAXJTeT+y3@Ic*M@ z*@_nI!*TF^0YxyDtYOx~vXPM;`=JhHPkai!oZx_=@NS)ivmtxIqu~NDb^Glb&1ZGp zd>PlhHs;PpKF~_$y|J+Hg`d_+K>Q1$xyc51@|FHi2T|$3NA_yv#gk6#+LzB>x>JZO z(9B}>m1_h2J=FXA!T(%{X2`YjoxN$e4jVk&gRa&4IxwFN`pGHyUvb3FXGhzOqIn<$4Vw4mY^<6N=9OgE0VD+5 z4oh7H-{O}poBo$~yF$y&>1TmJ72>EQgRUvRi}gHK#gliD*fm_J3UK5Dk-kp4{cC&1 z`4$*=FxxqAk`SW`nK$ufl-!-$eLv^k+SY6{KA&o?_-PM;uNDUGA8N-aOGC}POm2{^Ew3x)QCg`9(lKtP@ ztN&M3iIgq$_^Bc|n^k-W?6yhX*S{YbqC#twHUJ86ePZ-0$PbF??K?l_SKFlHRrwb> z@3VKu4A#D_Lu>OU;i#Yj0R*h>cCCatF^;Dk@jV`RN=N}u2+T%YxMRL2|5g3wLNC9(tiaA6P{KFlxxRD3onc7AIeFTq&uosLG+ zFc&UxDRFv!01Z+E6oR%EedQHWdx3fsTW;tmLAoA7o~N>!kGeM%adPT6I4Qu%iDN6ZM z$fzqfu)x}_iLBd07M5l)2q}@zt@mWi zHsyQ{uRU1y?GWs*EV`=OXCutoHc&Vujkwl;^F`lOvD|;^2vY7-8sGFl zbAv5rU;&qNys!x++CC~%ilLaY&b^L3Tos{$&(^=CAJ-^pSa748>zQoM-O+xMRUG7G zaY*pc93)U8jgw3je8nKQLQG(tl^!hj^?bM)|5DP)XMe#k9`Ds!@>W2!s)&)T-$X)@ zk3RV8QN9?`<&oCcnjy(cPkPbw!KR?0j&q5O2h9}86~7P7R}IN)454|SR@Am(X6d>I9}MT3C(A>2Q@Rvv%l_N zC0`gQ2;E4odYu5ACOIU<3g~Zg+!-$(H1wlZ7&UYVitRwGJ<6@K~R&tMk}2| z-opeVcI!yHy>Tl$W5=I=6jhK`z_cjfU_7LsP61;LoUxng&VVEZjBX{hcZ|zo)P-~W zA(U&A=)HyahURIW_{p^+_$CeJ0c>{ci%<5<6Cq>+KOnH>Ipa1oeROD``Rn^|_+}5M zbizW1LAMmuFWm@IyOuEqRztqECEuD;Asb;O<9_gJ0mU=-KvhqL3tj}$X@~(-*#caM zGT64Cy;aX4|Jzmzxa~-#CYyULdl~&_F0+uV-AsmzNiGinn4PzcR*}> zlTXFos+{99z>GvUE;q6c5yeMqcTIelz0*t&eo?^fiTMeQg=~X~xbMv*u(8|HU2&6T zT#A=053b0n!M@Xi;)43g!U}nY1b0XO}(0*ldez#XDQI&#~QKijnNixKs?!IP$a&LX&l<%>^nv(kY{HNI00Hg^dUe)5@ zwUSzPAXWj%ZB338+~OPW?Na3(8)Tgt$Ijch{(I&`o+;ZLG6E}aPqs%sgu?8*8JRXk zL74}G0`^W~vVmgXE8*qJT$dXq=JJ4JZIn= zziiPt_qUDydU_d!xk8qD==U&d7}eshRlA?PF0|5rRDmNs5lE(5s;gqOS} zcZW(f{CO*(Q-1u>2g0gN--x;vo_X{X#ufm6R?M@oV^7x&@GTst-F z`rH$5t5 z9Ni8^-L;~^}fgFf`j67mF?Z*gh8E%~-MVCpP?p8~D1#~kD6RZ)!FbVVggbxD5}wQY&RQLswgCCZ6E(qs!hfL8S( z^=gHo=y>*rOw`8>DZ*Uit>}6))f=C&_J1cH-vxK!?4t}yV8(QC_)_@F{!qPiFhLHg zj@53NdcHDDCGY3TG-)-e@ms4fX~n^85=HQ9*{^yzE(DDoVf(|{OOUtRhxP?Ax8O<; z5vjy|Fi+_gRQCybC+TIJDr1t^7@#!JzMo77NTb~Ru<70Q1;&wfD$Oa?CUUF=iPuEn zz5e+KWXlE!y7hXnnxbBjQk!w_7LUDPP;8+PW7PNnfZA(CH@B#K%gXFtP9IbT!+>-2 z^@lP`x4S>))vW#NU`LI}o#gw^xVsaJlemqldrGoJox5kdOih(G>Zqt6RfcM|dQ>T67`9JyfQ;9D13OSKIeKOA4efN+)5d z)sd{P?yPi{8LqTzv$?HP(vU#$yv~sKzrUCwxNwhw`m(danzW&c7>!F{Afdk*)j$h$ zY7rUvBPdzrflz9T(g;?eK2CU?8t`(;?M&ybTUz~vS|ofb$e027{<1 zhnfUNLs@OW`L}&wM@UwxRfn*@^O$x61qgho=OOUaDiTr6t~7w%%tH7UoZ(kQ#}jpK zArY9YDGNc~BBFWduBlroKm16f*x&rx@U6bAD5u5Gh=JC~JSvsivhcUa79f05;DWaj!YZnaM#!Y8^Zb4FQ|baM*arLe^N8o+upb z>5DWLc4$NA20^+ViRveINidpqUWK81U*YYl6L7yYA~@8 z{A9g?col%s^t30s^8k8vk0XF!n>glHpiQL!Kb1T&?PBqKupQvhyNm-!kWJ18fUV%c z`mY~5P(C=mUx z+gv9tWr@f$+5pX!OS?|=^j}ornU*v|QSk90KL~)x+B=W9WGf`hGe<#3;Zp2K+)B0h zxw5KomPBh1oyJV0a~_uW(vsCR&T6Dx!*2o|N+Y?x_#<*Y^DeW@*;*G^#^_(a2v*;h zP*Lm;FSU4%;#j2Z9JR9-$b&%E^3MU42&b8-ePn@G>WejEeI<%H6r3HYi56M5#SOKm zKjtkD3;D0!CqPM@{=iHY?#fYd{o3;go<*8a(zoZ00Phxt%5;}$MNhrG1jkbVBX~=1 z;9#Ite8cJ?PGbNALTvqA`Y}BTV zXF?N+G4wv3zA;Pz7j9g_BgB4*vixfZ#XRWOrC9;ED6)@=2;9=7F#ACw)rIPFk8cQ` zdB*B`Io{qww!l=?ISw?2MRRNVdAclVBA;SUP^xmlk&qNf-^%j4o1T$=!YQ5X29GN7 z{*F3Mopptp#w?F%@Ih8$bfdXTyj))8bi{Kob>`!~F$O58FO&RZAr~G9`!RH&0Y==^ z9~BfU#^wcbo=}RDz;_#mp9zwb6DDLFbQW^6j`&B&8=vu)-yqD9{KkVq7wi68sXT9G z^!Z4I{lU+l@g(|p&>;yWk$vG=AD`X`j(0UI8BRZL!W1Tm)Lh%gA zarHWO{Ub9oI4NE_E!COC(xUMjTg@p_>6dqsh)0}0Jnu(6BtgWJE7lv5~Q zLX4t7mg5@3=?bU{GAjMeF(l~K#DUHi3QXhaWQRTc%s17(>ec zrE<+t@O;W4X+lUb6K>55#Us7)CPL+<3MMTC?uca63STDW56tj%T=5eV&)kI_Uul27 zb{Vc55wi?H1)A4?D(FZitqyT~nU#TxArYi&TY$EmkGf3LZ&qfk1Jc(uB?N> zlE^oGAif&Ny8b`%^Yf3B=Q#{9?9aU1Eqv*Lt;w@P0N5n&=SO-Xq}d*FoOsLEnS}gw z&-uJcR;dXgnTchmf{(RBc)3?QpTox!f80`1Z_I!XOYoo*FLd*eA(7t`t#$WWH0O%E zQ#H>p_OSHQ{+F(Jd&1VJ9o}n2fB<6I^Y5B-VZXlg5NplQJ~|U-Xwc@3hHWI0$7O>9 z@5u0rUOF`^BQ!!j2NuT$u-Qa-j@uRI*mPEi%h!vg^|!DWiJh#%;quIIG(cXt%jk0$ z4**wm=r_OZPcTjA%OB^|*T*`l3r~t#UV)8` zWmsdx2~DQ=>&m-HW=B}&rA@BsKB312Xy4;Y9H0P*Aw*}WB+R-?#j92c?1fT@Sg5c` z*wBpucJ_3c&1@8G+q2y{5~b?36-l3Z@5_fPfOM$`U~83-XHwjzg6JMWK%b9KVjd;hjM8|6(_ibWl=5Dp=_A<2@G^hz_KW;n-g%)2 z91NAIlIt?-gYe{flrIodhd*l=J6Xo0flwh_P^3I}Q*5DON)HLMN0Vr$W|*eH3WrMH z>5(-4iOdY?N~`4SzU^hasQeMP*RK!JBM8DlvoS;&!k^nYeTLin{`cR(iT0k)%gZxy zu0bkil7TVDt);ofqdRD%tR#WoJWVH(nD7N^{a2trlsGJ{-6xCAO@iMQ%3GY+C%oRu z^bVZq;tlI6*XF~7QM3SnxitD+9U$GMdXK0Bnlf@?5Vd#{4Pdk?kW8~S!T@$deRf>L6#TDL0lYXaMWGMAh{vm zBdgPY5jJfSG7?5%LU0da^8?T5K1D~iLzm(;9Zx*~_&tD$d+9*vC8V-U2^G@=S4*Z} z5W+k=4BU0fCs|aESh^eV7Vepjj50(~+*8!HvcBAiES0(&s)H(?dT|KQ&c7 z(jBSknOf01oLms`>(_skiJx~(8#VxBor^dkZl4Zo3RnC7H4}=Ph;htu!-Vqyl`=#7 z=7^vpehdf@z1N8NH}C$F{&4c|xBN(do_d|#!AWbPPmp0C;@krUQCV0@5kqGt&V?g% z@$yE|NL1nSs-Bky#+h_ZX)ATIg*>~+bc1#t-SP<#XWZbhKhdJUk47Sx;q99mSX{>= zpatoKjJdYI_2N`Zd!+qf+L1sd6W!PgvjGx6Alo%@z?x}|mI`ojO`#b}s--nUp|n)k z)4zZ!4T5Omd{mthx1C$;>$ST%dFn zorDjE@?lyUib4dCr{M~=-huYC{1%^w+4#xKdQ%mz(_4`|Q>^f|S&cUFc;zu^F&$5& zUle|$?PSMB&yvMw*6pf4N6 z`-^UkEz?kESGR4b1Ffya@DwwMGbf|%_pujN10FNDbX^6iQ}|YF}$Gc3?7ZKxVKp6USr& zPkXa7Yo*oGHgm(b%*6-SV}d_W)`A>?U0-))h?Qz^G@W+*=gWOIlC4vhP|V$qUIOQZ}vU-3P@)M4)AVP3yBH4IZal5mg;G)(1@m<;F-I!OPlReTZ+7V2SjG9bTJ;< z=GVCo)1h8v)52GvEs85D>V?zi8B7AA`w`@;R z4U1b~E9m1`2}TuXSf?|NwPl*;KwP%%5_9SASrQH7=Qm)-3qq{GD!$Ks*qs$%ySYc~ zXEyZP907;89Gk~|^eeN!-@aFya>>X`*i`_u39!wm34nxOh?0E@oE4KdSGmgjA2A#s zt$1M-A?K;J>jf`%Lpw7T?#+@RInF!=;d;vB&UVpQF~5Hk32y`vKV2oLOd64R`s1&$ zQALu7%D+7)0;RExCJzswfgV(wGtXjXkQ8T%ADM*04Mq-i$*$CCmf_pd_JG3Ff#^yL z(%hRGacb(H*|Pm9X!kiQa0; z`SLAe=bBZfF-y2SXgf#r)hv))q`Y@|f4_9${(WP=UZ@|Jc|MVH1A70X7&bQYI8nG2 zm(Ly1F7k9V_$tNb8T`(D5!F&m7-XrUGGG9vhYMJUu#xVU=b*e$wAhoM!y;77Y5cHK z)V}4S=H5txG5%{7mbI#~xrugYAc&b`SyOXXDl(`f?JCvF)I$z^JpxAiHy%2~f8^|)u|W2&CP z$~~hBw!4Ev6;rIYNhoQiyW?{0#14Vyr0l9IAEwqW4Lxh0R#ud^d}Gw!TxxVP*8 z&kTkr32G}Omm%Z@kqb!RGA}%AegLQbMJ*7KrHP5^>Im%Grjd#a(qfxiGk zNyL@N!1_uFbBabj(F$O{NRwEx}Mf#jAb7;AXB}p1eEAzxyCKra7^off>Mwn0--DOK&ad z12(cWA6}`p7mWFPs6+9~5c9sG=CgqkkJYn49qMBA4)qxgyyZ3_=JWzVgF44qq3co+ zIR(BB4aaMxa&&Z6@ni;*cJnYS0x!q)HPpathnir9Agw!3*zQ$n{Jl@2w;^^8Gc7Kde*v48tcMJO)|;F4 z#Ij=gkjuur@a`#PTuypAe#3);1VG2T__p{(@c@}iPY6d3$!Y}uJ!j*oMm3d%Q*#{K zS6SRiW<8THl%O7;XIq3w6G(s7ncdvq%b_Ev7a8L`$Vk^1p*v zs25VkO^O;%PdkOi9e4Cb=rR)tT3ZVo5}?fEu!rI{;L^yMj`bA*u{L}ley$tGF7y-S zv6LkmAacaxJ_;Sf*_ekP>l4;+rf{`JTndk5{2tc++HwwzN;*q)b$F?(x8_^Me|e`f z(z70(`~5gzP=(?g=~55JRSo^e~k>+F&#H1{=I^AO~sL@;+-|4e6a_&u{3nCaw(ctKKO&mXqh)Cn+s3KQ9+m$Yn;=doq z2kywC2sU3fUsNw^a6yIB+PPlgbH%SXKZP3W6F8`J$2=`l5W`M$=l)9MS&G!=9sJVq zrDnk$FIHn5rz0krT;_FF#!Td@v!U^vl`0vB6dCK)rsFLb$<2b{+Dkb6(5+hzd$*2* zW04S8q*<3nH|g&7`10z(YTptm@-|~aK0tHT7hVvMN8JRLCmbm|t*gfp;ci0|@+Rw* z6{>b0$|F5aEjYkOlc$RknAd)-!tvgF8reUKbKIJpU%F|L(|EeVN7_%CeAnVVj4$gE zy(V*o^R^Q4hQv{kNZe9V;4q8X+q=;G{k=}?A@XiP*#PTq8m5*4;5SM0%{q2o9uc<5 z&KBJwUg|9R9bsGRPDNx`VIcNzLe{B%4p%;-q;VU)foVQAaPi1cOjY?-{TZMhrO|H# z*&HSjFO$S3YKMD>`c30gVDx)XW*r%w_S-4wd4guZ0MS#DG8M3ow3sw*SH_`keBI-$$3~NN`tbVlpFE;s{P)muxr`_?I=3rh7bN_1el>aonP<;H|?6AH9n0Q z-0ffoLnswxC$#Q{N@$e#k62RHi7dW@TU{1P32yVPFB>E+jEIME*e4eMsMkpBITdla zf>=3@C!K@>E^eY@!q}1&^HwoAw0DHW(mGDt&!>Me9>Rwl@+1I8#4J%ZIh}F?3Fsp2 zZQWK$iSS8`{NCdk2GJ@4ws75Vqj2;;r+2?-y94jj*ottwHJ@2Ut?OdL#oV0Ds4q6_|XBYd&RpqKnD zd*J@>JGDnNJbD#NqiRb&*7~D7@qIQqD|c6GJ323|Npo|UvdZ&2B`&c!BHMl{T*PF5 zsJ8&!2>q4KVVzZgIyc#Ij1N6RDN*)l+3;|;+C0+c&MSerjbpx67#Zjr1tG}2vihQT$`n8E@AdRMfoLHtLH;e7 zA#|8SU(Yd$HV+5e58TF{<9BP zuUW%5S$WWAet3mm#|Jfpj`?D|8(HBR8Ho@=$s=B7+c;Ky3T*QC(Cx(>wQW3(fFI}o zUmj-doLoSLA|Kbm$n0e;qUL?`0#9);g;T>k%TB;?k^o>_S}t0E$X3vKb$;O$W#Y}8 z%39t{Y1m{W?UNj$#{DjLqs_t4Mw6rR)o|{1f_MEXg_y5+dHg8Fk{?Ij{2gW$fZ|7) zprX_5FN@+)nFU2Hw2>|xIB1k7b6?vbLpyc!ed{-W2Qi~GI?@YSVtgkA^I~`T7}J~NC~~leNk+D;b+|HRzKewLH^o)(}0TF$?;~P14e(9<)O?kzu&+c>`K#9 zYmmtx+sCyC)ZhNH9H}(x2^Z~;hkd%oFW0e9=2r_!5Mtx7IjL2Ia2{;DDuD( zOKElNNiIgE@6lTDU+C#{1;g6~NUMbQ2!M^Sv~4}i-@&P`1nc<%!VL$_QJZAW;u zZpnP_eiJ|W_iH$ujGrE}6b;U)wp6f%35uwRsy#O@xBIdU` z?t_x>#9SQ8U+aM0HwE1{bI5JU-U&yBv?XtHm-MDOY_$8eYMO8+WOB288>I=mjK5jY z+%|tIE)WT%0OyVUeiuT|3eU!XsZp~eHtr#uW*)tNPgA-&Cg#$5CCn0}X1g}5BPOhc zX^fiKO9~Y}*(}8AfWWN-wsNv>CdEiS9N<7E0y zg_!+yKE_lra4{QRO%|Zj4F2RcLp1I0wRnSRr`q3d`|)~hE}5W1Y`jvZ)$ADH-65s$A{p#2#Z<>s$SwPfi95*QS({UaWVKHwA~7v8 zxgDI&naKPuIYA3ilGVuYZUqbIb`1R7s^qgxtC7M?X8*3I^%9vC3c(4;sbyaZ>li%(=i`P^Oa4AM+yokxsC;d7wbs z_lfe>Q}zNwO#p_aE?~m*kuVGg_$jNVm81#l82gh)=sE3LQS8k0iO0}yUXw=}<-+3B zSllU4M3t56`rf5ihpn)lc1;hC#jRuolP$X0U*&4V0lO;%^&s!fv?@+!dhKz0N_ju^ zLzATT-G`+t6(pq@ZVxj#ulr+|9En{!x4dW>n)E-xVNy*z+ph`l?SR(Mxyt>Swc_L? z)3#W$>H=lixYq}7Rpw{Z>j^M4GX}WN+%F=vM}`S9gRdmr|C5DCFUrr~**OW9 zL8)ru#_Otas8Z!fMrq8<@RPsl-CCG{0nTZ@S4f8cD|ey_u&v$EUV}T<5>vh#Js6wy z>ES5hpQ4GHHa=l$_V$hl#a5E*UNLTN5cK?8JHK5Ele~%S%Dn!!Y!k8r1$?OOecqz<%5*_hiayiA4Q3hVRTi zB;{n|QV>}McVAO`gFR7BE>5b$^qZpu`8DVQdj@&dPd5dUx?|81D|pjD2CU#C_LpVbDZRWA#}uO!cA@D@N6hKJw3ino9Vs# z=wcT!2c+!&1-FpLUoW-1Px!{1sC+VWb<^)nrpBmpI{()>>nY`awf<`>0}|P~_-7-7 zVO1#l;RBmmR6tVuVX`hnrP}5y8sY{*h$)36GF0Yf+YwA2To^0n4I|3d#MvEpQlwq$ zdegSRN4Y1}{6mu~!G`VpCXrM%L*z6W@NiFlERV2+^nm0k`neR~W-)f?zXLKL(TUaYrKbFA>p1F!G9sXm9ydsRAib4;a({d1ywZEuRo)BZPh4SMf ztSG4TzZ8wirLKnK_oaI~=gVRzm@3X*cNM8)gGN~7R)c}F2H^!a4t|lW3)4i8!9vtE zr6;hkvo6k@sHTSHIkF(@DNvFYfK@$W$gZqASH836y^A1^)6S(2VJxhPE+Es}-Dl?A z7M0#>2g{*wQTSG7SRoE0hQ<%p}_h-7ZBoMapzKULzl^Ozv*tZ)0jf9^yi6Yaj)) z+!JS{zrNeFusJUUH6U{s`uAPN@dGk1#;dkonb27$-8mSVvXC}o(wI^QBWBq+*8c=E z+HRs3c5i5edY;@1Z=WwOT^reh6eZBJt-$qKTD4xWu2;Bw1@wwkG{d!h@n)S`4b_UH zVY^Ch+8MbGTJ5pMG}iu}9XS>a` z*6uPRan22UlKb|mK~pKWbL)t%*JdT@)OKM>+L(E7(^42Qu7kCRJc*x*5(-myh&mQK zCUM_30W7+%tTl{;HPjB;#hr+T!MuQLAVx{$A9bPgk28Ijn6qN7UdUbQ?fytzmgx+E0)65 z;@o^-d#*t4Wpex}_yP|9Ccd8z5u0kG#r7a{hA}4n>lz0d+X8dMnIYHzmm z4Fn;{AM+Fdo(PaB`7`ki6bOJklLDj`r#H-Oi0pbRlniFIOtJ`qM%!Lwsl&f3(i3EN zm%v@^w2F>`KsmwZ20f9{d@It$xb0!)ix%XzZ#v6*AV)f_#ftY?v9t$bve{M9$SR5m zue9_n*d+PiBXT(*Br3M34}jEbD6eIOdPw5(HvMF$fYW%`?rDP*R(sINb~`%eB)cOU zDlsVgR6J7WY+grFB7Bi)c5{a00(7woYfUaJNVcbh;yaXO{enGYvtzC^L@({Be|QV8 zGapsxWM3n0yl9zQqSwY+(u7$4WC}&>Hn!;5h3?2t!g#SVsVr+9NQM$u+rJD% z!*J(bF!eJ&%4v^{6ogMPiUQfa{zJtat_TL-(B))AT*05k>ewDAd|+m`3@}>RjgQ)7 zsdwNGV{u4Om}-6e=A?`cek=tW4Bx(oiOnGbAIP?WB^yHXEo))P$Eh;zmh{VM&m0sm z#s&l?^+CPAv;J(Bf_56jIt_KfIr7Ic{*0gcKeMOqYHt!x`K6zD3R-{=loX4Vqby)) z(r0*Dn9UJneiZnduP6=XC@1*Nr8oGK8yqn;u$o<*F8J6artIkdhfxkRlDfXY0x%n7 zH4X;bJh#gBxRN-q!LrM4RlroVwl5H_^jU?7ZN{qsHY#%z5ihQ&NP8Eb2roV2R&6k9 z!>VyoR+(YO1dS@Koh+8n;fhe76s}z~)dj7r);X(Cwm=Ek_5DKAB9HBi|B(|`ZAo8I zt)%OnLAK*eI$dzvCNBfRb!3w74oq!X#-uxeLf{!(@CBgM8S!l?)xSS@i&M@MIh1`d zB2ej9p9Rh?je@cfAEWOf+)_sDZP{Z6R0N1sb$V(UHQHhm*;!R0?OhbT)HYg#LWmfB>QDv#A?9nu6)erJRa;NP###e}pKCHVN zFeeChb_ZsAbZA_#*WBQaa|iPV$1u$tel}7G(2u;e{k84u6dLEX$tw+udnCU#V5*c} z)rIgFEcC?F24*EQR+&J|afU+@$N+lGOFcaOO2PXU?9GF28N31wjF2J|j&5I3smK*9#Z1-8{BCg` ze?uXE+w~bnSXc%YFf@cX+^&Q$yKT9cEf{VTcs}mX(?6g3>vW4UcOfX+dF!q_p~e%4 z$hY=-jj2(E*O35E>tdC#e`kOXiQPgX0-=(^sP>l4N> zAvP5F{(NBGrN;cjd@qmz%o`!TvIT5?6aF*B5OO@LM(oxJHNXJBYkIx^DA6-G6SM1- z6KqhqG$>D!ek!B<^oT$bvgnML7dj4QmDp^yl2_pG%BkNJh@qR<%#BMf0iV3ao*9)J z2uNsk6cw=cwIZ%eXC}`iJ@~i;AS*LO+c&W%BHfW4^eW6?qCx;Jt_a@rh2WjmTa-j> z#6kdDJ8r{jvG0&>0KsXvBB<_0)8oxV?D&qyAfG1sY8Ec)4(TO8G5F0{C~91FiH1lA3DgaU2VV8I}c2 zl(#nds}&3CUQ-}Gw7-t(b-S!NWBmC} zsz{g;AnzeFq$j_*y{-V8WKguyvOkoF)6KAP1aI4?bnJZb@B?0C2e@xjm(5B|#d{2xYc@K;Z$=)RbR7PfPD z6&Ap1$?mQ6y1eAunV`GRsfCfK%Oc^ZeAw;~ye>ch005xC55JNjZK-wvp$>LGAkwMe z9_GMM6v4~x<}C~#r|sF(Vfpox>6PsBJEfT|HbfEW%&U(|Fb_E2kF zB5_6!b@*!pV)+>ywCK%ZK{&?jIyu0wNT@s#gF{zy1XSk5I`ZxHVy6;mLu-Jcq6jr$ zCAZG4G$KAgVPI>0b0j?F`M$X-n3RPm5&`csg$&x^^CgNj-*-`77-r#klSLU|Zv$Z{ zfHqU%Gf{RPrRF4FRR*Z|OX-^{;R9Mr#=npz07@bsVRx>ETeTbl2G^4)EZ4Y$BGe#L z;KjTtz)$QqL#Ns5C$H@s1|^SQE<_S{>Ir6?KYzA zUR5p{3iRe#Sv?j|*5oQ_1b~R$nNH4izVn_A+i;gZ1{0sBjj&QF$XWtKYsvBnT)wz>^RvS6D`)Hp$Bpjj(Oe>t zE*f#sn56Ak?i2KYdqmX=!+W1-Qkq}hSzlwi?>5JJ4ijzkpyN*UF(=tA0(-w;tTiOO zt_~9hd(-!=xZJV!@&WIYurbpO37W!MnX)-E4mwYoa1DPx+1M72x`MT!C0&P9nvxyZ zXK>bJf9tRZY-T|pN4v>3Qa2STDQzrR^GReZB*L^YslDd>l9D;swgSs}IzEzp@C;Xr z5o$2aEuL1nhsG9rFCu1BQx#lgc0kIg+@m%N{Z5aLi>Bt$7r=OLJTVoZ(}rm}MvcAZ zN*dl&l`m%($I-%T$J61pvlX;v;JGX(9yg6&=>GKS!7Rl5nW0Hzn$9U zYRqOz`kSE1S`bgJXQojzY;}!ZDY|kwT;himZA=T-f62NxPjp>zuE^p8HTk|jD}QOg z)w5{duCeq&xbWQXHr7*SNphr-*@XGdDK7~CXlZex%Xx?$w(YTL~;6$#v+j=1k z!qJ7;wF2H9Pz?^iEy2FD?#+#WfnEIkwK}T8{i?GLagnj0SsL36(?es&At*D|m(V`$ zIF-Q70{FW`5sSXi%a6Ulc9mw>$mtAiy{Y|4a_ybi|CzF>h`DdyNv944)fZu7-37=B ztRduH2)*w?*{A~SM0^Xmy`cR7)=9{o_qLAq)&RJu3vmCsc5}kG)P?2c*r+L8B>PvS z`Ynw(RJpPMC>hIH*RvduI>VH~c_X7MxP>5Fx}BHsX$V`#D7!1UxtclcU5O<&g)<$@ z=YCC(VD9r2)znf!!?rW<4GWtYF9!>aC@~QEy566isg3`1ji#kW| zau0<S2ppf(jqF_1%mNGkKR^;>PHSb7m=dwikWE!T)bV&KP*AjVGd#u;zS#809x&G{RXWvoWv}4 zARZ{oc{)6tXV`X+ySNfAs3b2=VCZ*(;3|6s;YBzS01TS|q$BpsR6v%tvpcyCZte^{ zDFlM>V*mUy#rWBQRvyHHP!wjZn{Tt(fg7rMUar{2uSKquc&`ALIVZV*{A=r z-LC$RJ|9p~>%ko*u5P-x(`R-W3x|1BmSsZXCo^k%k(hWN4ekezj|>0+9F`W34p1Uw zBSRiW@g4sx!5i<_c8F^WgWz8kRGc1!Ez$d5hRWUD<+sC|XtmHr{ZkI$|K0Y7()93| zw3jo6R^2a0G7l3A5zSZ3XM>u`N5=4Irm>Ju0kvlo^qx#fVOR)+b=H=O%Hc2)b`aEa z9QgOi_d+4M1OI*n;BzRuEL0;c38UkYd`3k z7g7FT_oZUxl~vwme!n6=r2_5vF2*}CQjF^S%#d(>B$$!A0*KX{LM(qzGmg%p* zhkcQlOn*W}$|$N*5knHY;+y~nv_1E^!&g}6$(sFU{-sDK$d74nEFkgVJMto1KXL_3 z4aMcQh5etqQ>h5+-d?{){yaa+0Kzrl+!6*fpuzQwH{c!$R-5vQUc|%^KXGXv3hl(! zNe3bQ@Zro!D!g>PQ(u&)q!X2e@Gj1CJk%Z7*aC10WYk=nudIwu$J>47u2(v6r?2!4 zqG!2)(%Lie8!&4`t6I^l`S2$Vgyo{dVB7b*z(LRdBc5KmA`ywc`6SJa&nD0K7Lfgm zVfPx<9W0m=)UtGUTC4o-{|H{5L#8y{=qMZ7e$2bm5+IL;(!Ew2n#n^WVfuq=1;4y$8XH4YDAAnzmGQ(zKK5Jz-_Y}uts;yX z&Zv|<%U;ucaCEel!`Nipz-&dK?AP>pY=#Q)J%V{@+Msl++~{Tuoq8y}Gda_{xk(FG zQt2e(|LJ|j|0@TljT(PP)d$wble_6l_i3r8T%{TDlXb~MP0hgFy|^wzkA+BkG7oM- zQL5If4{F$d{fa9csM{pY+y{wJ-GAA8LVrg0U7Q!3IMO5);MMV94^$u+WdZ}&8J~Er z03$sv_Os0Pf_lEZg~1cnvZ*s=HO0~>Velv3tp?IVzXL2O+tO9yXKY+4mqr`U%jUQk zf&S9E2liU*5&#Fki(HEwo9wdM843=kuBx<|O(FDm1xw&h;&}mofRRykUC+=iKK^1J z-Lwu_@cz!t@!72`3(IhY34Q+lGrO}ZpAz++0OP>vwE65>>Xo=L2U*&LA#H*P=lPzJ7 z9>tfD&iBgrv+F(m$vVmpzI2r}VzDdB%cPV>8F|_L*G^%16{EJ4;O;Fj_7A3}wBYvm zyj^Fu2kB^5Y#Q}gHMrKxp&)DpNS{*lvGzi#7#wXUixw>`L51W5MR5Sy%TP_BSH4@X znx_2$y%~&2x^?tyB~X4YjSnkoT%PDGT#3x;k`ib5dxBM=a3wI#uMd<|N{o{&z+3hL zv@WS#Q{mdEa5q{|z0|-w-xZ}@84E|*p}S0(B{J{{xATjaR*uiYKecAmT(Yj2v4QRs MuEILze*ho=0AiwOcmMzZ literal 53020 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

3Ka$YBM2wUED9vpjEj^+S8( zVP}ucnEIV6iYW<2ud?E5yBdc30>}w;0i5Np!6s0zr-tw>Zk02yp740Ylih+ys-e+V zV?^`K8E>p1SRv)KU&B0#pjV*^ur#H?^;4S8FD;C2z&N+DIc=O0>h;+>Z`=k$fWSv- zOKFe2*erSz@c!$RadRE$yzTVbHavh%3?jZ%ROZ(o`L{#R8`vEl59e=ISL@zX=yX#S z`Tn1WkPp)xS}!yC0d-!lr5m7=U7yWdnU9Y&1M-Yeqs&KPkYcDfE7h<|?t!gm zcbdn!#t*tpwG=VG7YeRT+lt42E$lkAVWeLB=1{^}IJ9}t$X@+Pb~6^o^}uJMmj0=e z>80LmGLL#V3AsAya$`8IL{q+30#sRxAtIQ|QJ6hNrL&(L<|(Hde0u8bn0~#_NC>jo zzCfGrm6o;Sf6e>=s;y>_J?y*2PvwMQ{)xc+L%h@vf&=cF0@ZtUIBJK2W zSrw~!wJ8W(3L?w2q+4y(!;NH!urH6|jGmj_S@Q+Q6ZStTy9P~HvdgEdE0ZnaHRZb* z(FQW~h?mJOEXg>I9nT5P5foR`S=-G#9GsU+-L31M;c5P0p=<`c7`l<8b?g+7qhPhE zq;nPAZ$wf&N%4IKnOQ8|KEPMK1B^EPQbPi=oJISGq+q{pc$QCQ3koxr=8esvNq(@7 z=&v&^pc#YKI?7(E`eUr2Rf$+ND=wVyRjvs;hR~t<7Tg3W|Ht)0>0?~GY%>lcgCY<^ zrNq#Y_4Ded4Yvf^jL&2pUoOHR{8T-(%{~{#+h)Z{bT0ajbzhfFxAc)wbj=a2S+-7c zYV&oRPynz3^|F(1gQKt1*9Uca71^xZibW}N_{lcGg7r8FIlVaneiHORPoG4uMlMEb zWOgA*$nDY2ebDxx^cStk2R%w@q(Y@9uYjU+RB6R9%FIh-~$# zpHnQtT`yjtLK8vP@smO|R;L76tD+ z-igpro$Z0Tf3NcvN#dKFm)~-4^HOh59$wJJ^f=gBe1R%nhey(o$N14R)6^>raW_JZ zG!TRVUb6FwW$Dxx-uDQ2`^85)M_+s7#ah-eYS&j48#n=0i~s@vq|qWhAW`;!)*sl} zL+r5EwFkyI=Fphc`$>d`rFH`TS2?$OpQI)qG*4W)oy0RZurIVd*eph9X1!{H#~(EW zZV+yNroMyxN5lspA4AVQiQP+FIXsHw*JwfPr$WE~{o`-salKufwkAu@&>pyh8kS}` zRSB0xdNcxeor99$2l@(aL2?E-p8lK-f~S*8THZIjvSz0Q{r7YDW=?5`J6dLvNqlYU z|2IhWa%+NJgi>+A5Jy|{mlQl1Py%p!D*!T}5WVIRUd4sO-s^@;yz;kBCjjl}y{iE1 zH<*>-yTnox-bEx;(+q{%ANm~h00F>nl0L_;D4P_H28E__asQ?EeL6832Iz^|%9hnx zvdo&o2CR!3Z4&IE1~hpiH08h$@+%dN2pxH+wb~Vg#RVds18(-7$=_j~bmh&5wI_gv z!c`qH7+RiOhc$4<-S6{&P+tCwT&95HmB82`?*X6T)NrU|DF*04tc6NKP!q+~X$W&t zX_ahV+yhcJXU#W-cd}8h^`?>qM%Db;6?Wk9G~Waqi>~V?Z9_oY$Cw88KAFZ&74s~3 zrD^6@r}*=@i2B|Ns6*vOCR$%b*`IY)@RJmNT3;S%0a{s;pCfgzCh>4eU%nB#wiMTD|0I{_z#oJgq?XIbGW z06ye&F$hRjS>Z$KykeArvdl1@N~&FB6IaH{jQE=@MuPwX8g}zLiGLjmSuyEP+;)2D z3OdF!RgJYc*|OE^j3HjbHaSdF`e|yG6OdmXg7iQ55|`9%tx$KVK#whyKfAnD`XwQp=98DeB{Qpn0&y4jVdk%9a9x=M)^SiS2TG8$W}qGk3ZVeRy(*^ zRg=BH!pJpVAH{C`-1Gr{*Ylix4X1z=l3cuUaMHrbR1f}qF|`!vKSW{cS(*kz?5klt zmD6bRT=+_KS-`)1Db?66v6`h=L(-ZW(LL$TE8q2F!)nYrV*)J8X3=RRst(DdjL!pw z9~fAS_Idkzb-2!Fym#e=Xp$|KD)rPus6N88IF(?r8PTl&0nH#-gB1p2=cC}?(Dvm(hbHy2}h^0s5J(PW-XXMClus&9cRS7Rx0Rl}S>n%unRCT*XAcpG1 z@4F*Or=gJL_}QjpiQFnvq$Q0V7!@ zT{l9pMvN$)rISJ(?NT!&&$dL9x8U$}WB62S$T8;%KVG8N#uwmc&!MRT5}L1>Ut{)$ z51zEC{LfXbQr04WCfMLE{_hFm1g@_N%#_%4(5;Laf3ExOF zEnJvdGsx$R7m^U`eE8P}sTM^OZ%t9kfU;nEX#(oAynRfM-*x>t#fj*0*j*)2cPCX% zd7C!J)JprKB6Pb51j6Dwh)!gJ*(nQ)S##h~AViy&W@;;0Yh5*)E`iw<-Heg}oF8gG zATwI2t7GJ4W&4poyHR3^PSM-HBo-cK(I>C9`@QTf(s8LjRx`NMS%afc35!L9qE)4b z+U|jXa2^Rhhg!=S@E3v-T|QgnRI?AwohMpAXyllWq>C8Jt$d(7-UiQ@fn$kv`rVR$ z!dHZAV4yh&>>(MW;nE&8o8R<^3Ke7{*hxy^HHbX4Epvd#3^YifoZ~0K%}z&=8M*No z=-{u&J2E0=MZYWox9$OdpuH1~OtT)L0Du|BM6({#T>uFq=*1X|62q$;;6oIrwhYYE zw|~YISdz9=eb1S%Q$C8;#MuM8u&T~4TIE}}C`4vy%E@5eB@FT9sdZMv1pzzu35i5Z zx|%DrBt#O6JX&pcyMcJq_cAC>q_#&3ZIzi01#Gg`F$pGcr1^fKw8a%Mr|z3$vI>Qf zRd5fz#_4*7eSfaFB4vEqRT1L3UhOLxjBV75K%8^PPT8klGM>@@-K_z7m7h1UgD>;6 z-TpE7Xy2I_{I|5cB&efAiGtJO4e8fLYd{n6$1uJ^2O6W~8_syj(HP&&@OVlQ$dAD|GQ@<3T(|Ecs?9z?Kb zRpA7L+3hpxj;BA+I&`=qyXBemRwD1kp{cW+oRWco>ud~V@u zG5f))1WSCruZX@*bD%u=FSdO<3I?+ zr$dLyHu>2Oi=TAt=p(jcq$X_0U`vA5QbJcl{(EMTK5?3(mK|H+-}ZnNN3gJb$)Hyb zK2i4-@=mCOD<5s)V4UoIqFkkeQ3UVLE&;GCPS-5A%sxhPNw>FbVy85^Gm{4qW5^)+ z!5{%*eiFfow%WQz$&=%!V7ed+&E`#bm5p7vzHoQ?A~-<^8+0Lq%sn@Y9kNvnKpEcX zrXYJyl;i`1h<8%saQ&)nmDIz|KVMjF7;`l4qJ`gGgec>TmTWK}qBW72Hh?GN(0G)i zrVK*^^G*rDLu2dGjPzTc0OPCxzd*{3pVYShO;oE$l@=2^GKqnLp9KeDRPq22Dr6}D F005Ebjivwq diff --git a/index.html b/index.html index 597c88c..99081f2 100644 --- a/index.html +++ b/index.html @@ -106,6 +106,7 @@

` + text += `
${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` // } else if (tech.tech[i].isLore) { // text += `
  ${tech.tech[i].name} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` } else if (tech.tech[i].isFieldTech) { @@ -432,7 +432,7 @@ ${simulation.isCheating ? "

lore disabled": ""} text += build.techText(i) + "" } } else if (tech.tech[i].isLost) { - text += `
${tech.tech[i].link}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` + text += `
${tech.tech[i].link}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` } } el = document.getElementById("pause-grid-right") @@ -478,7 +478,7 @@ ${simulation.isCheating ? "

lore disabled": ""} techText(i) { return `
  ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
- ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` + ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}` }, skinTechText(i) { return `
@@ -486,7 +486,7 @@ ${simulation.isCheating ? "

lore disabled": ""}
       ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
- ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` + ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}` }, gunTechText(i) { return `
@@ -494,7 +494,7 @@ ${simulation.isCheating ? "

lore disabled": ""}
          ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
- ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` + ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}` }, fieldTechText(i) { return `
@@ -502,12 +502,12 @@ ${simulation.isCheating ? "

lore disabled": ""}
          ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
- ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` + ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}` }, junkTechText(i) { return `
  ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
- ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` + ${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}` }, choosePowerUp(index, type, isAllowed = false) { if (type === "gun") { @@ -594,7 +594,7 @@ ${simulation.isCheating ? "

lore disabled": ""} techID.setAttribute("onClick", `javascript: build.choosePowerUp(${i},'tech')`); } } else { //disabled color for disabled tech - techID.innerHTML = `
${tech.tech[i].name}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}` + techID.innerHTML = `
${tech.tech[i].name}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}` if (!techID.classList.contains("experiment-grid-disabled")) { techID.classList.add("experiment-grid-disabled"); techID.onclick = null @@ -630,7 +630,7 @@ ${simulation.isCheating ? "

lore disabled": ""}
- +
@@ -775,17 +775,17 @@ ${simulation.isCheating ? "

lore disabled": ""} } console.log('n-gon build URL copied to clipboard.\nPaste into browser address bar.') console.log(url) - navigator.clipboard.writeText(url).then(function() { + navigator.clipboard.writeText(url).then(function () { /* clipboard successfully set */ if (isCustom) { - setTimeout(function() { + setTimeout(function () { alert('n-gon build URL copied to clipboard.\nPaste into browser address bar.') }, 300); } - }, function() { + }, function () { /* clipboard write failed */ if (isCustom) { - setTimeout(function() { + setTimeout(function () { alert('copy failed') }, 300); } @@ -946,23 +946,23 @@ const input = { setKeys(event) { //check for duplicate keys if (event.code && !( - event.code === "ArrowRight" || - event.code === "ArrowLeft" || - event.code === "ArrowUp" || - event.code === "ArrowDown" || - event.code === input.key.fire || - event.code === input.key.field || - event.code === input.key.up || - event.code === input.key.down || - event.code === input.key.left || - event.code === input.key.right || - event.code === input.key.pause || - // event.code === "Escape" || - event.code === input.key.nextGun || - event.code === input.key.previousGun || - event.code === input.key.testing || - event.code === "Digit1" || event.code === "Digit2" || event.code === "Digit3" || event.code === "Digit4" || event.code === "Digit5" || event.code === "Digit6" || event.code === "Digit7" || event.code === "Digit8" || event.code === "Digit9" || event.code === "Digit0" || event.code === "Minus" || event.code === "Equal" - )) { + event.code === "ArrowRight" || + event.code === "ArrowLeft" || + event.code === "ArrowUp" || + event.code === "ArrowDown" || + event.code === input.key.fire || + event.code === input.key.field || + event.code === input.key.up || + event.code === input.key.down || + event.code === input.key.left || + event.code === input.key.right || + event.code === input.key.pause || + // event.code === "Escape" || + event.code === input.key.nextGun || + event.code === input.key.previousGun || + event.code === input.key.testing || + event.code === "Digit1" || event.code === "Digit2" || event.code === "Digit3" || event.code === "Digit4" || event.code === "Digit5" || event.code === "Digit6" || event.code === "Digit7" || event.code === "Digit8" || event.code === "Digit9" || event.code === "Digit0" || event.code === "Minus" || event.code === "Equal" + )) { switch (input.focus.id) { case "key-fire": input.key.fire = event.code @@ -1013,14 +1013,14 @@ document.getElementById("control-table").addEventListener('click', (event) => { window.addEventListener("keydown", input.setKeys); } }); -document.getElementById("control-details").addEventListener("toggle", function() { +document.getElementById("control-details").addEventListener("toggle", function () { input.controlTextUpdate() input.endKeySensing(); }) document.getElementById("control-reset").addEventListener('click', input.setDefault); -window.addEventListener("keyup", function(event) { +window.addEventListener("keyup", function (event) { switch (event.code) { case input.key.right: case "ArrowRight": @@ -1047,7 +1047,7 @@ window.addEventListener("keyup", function(event) { } }); -window.addEventListener("keydown", function(event) { +window.addEventListener("keydown", function (event) { // console.log(event.code) switch (event.code) { case input.key.right: @@ -1081,7 +1081,7 @@ window.addEventListener("keydown", function(event) { case input.key.pause: if (!simulation.isChoosing && input.isPauseKeyReady && m.alive) { input.isPauseKeyReady = false - setTimeout(function() { + setTimeout(function () { input.isPauseKeyReady = true }, 300); if (simulation.paused) { @@ -1110,7 +1110,7 @@ window.addEventListener("keydown", function(event) { } m.energy = energy //return to current energy // document.getElementById("pause-field").innerHTML = `
  ${m.fieldUpgrades[m.fieldMode].name}
${m.fieldUpgrades[m.fieldMode].description}` - document.getElementById("pause-field").style.backgroundImage = `url('img/field/${m.fieldUpgrades[m.fieldMode].name}${m.fieldMode === 0 ? Math.floor(Math.random()*10) : ""}.webp')` + document.getElementById("pause-field").style.backgroundImage = `url('img/field/${m.fieldUpgrades[m.fieldMode].name}${m.fieldMode === 0 ? Math.floor(Math.random() * 10) : ""}.webp')` document.getElementById("pause-field").innerHTML = `
  ${build.nameLink(m.fieldUpgrades[m.fieldMode].name)}
@@ -1310,7 +1310,7 @@ window.addEventListener("keydown", function(event) { break case "b": tech.isRerollDamage = true - powerUps.research.changeRerolls(100000) + powerUps.research.changeRerolls(1000000) break case "r": m.resetHistory(); @@ -1564,10 +1564,10 @@ document.getElementById("difficulty-select").addEventListener("input", () => { }); -document.getElementById("updates").addEventListener("toggle", function() { +document.getElementById("updates").addEventListener("toggle", function () { function loadJSON(path, success, error) { //generic function to get JSON var xhr = new XMLHttpRequest(); - xhr.onreadystatechange = function() { + xhr.onreadystatechange = function () { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status === 200) { if (success) @@ -1586,7 +1586,7 @@ document.getElementById("updates").addEventListener("toggle", function() { /// https://api.github.com/repos/landgreen/n-gon/stats/commit_activity loadJSON('https://api.github.com/repos/landgreen/n-gon/commits', - function(data) { + function (data) { // console.log(data) for (let i = 0, len = 20; i < len; i++) { text += "" + data[i].commit.author.date.substr(0, 10) + " - "; //+ "
" @@ -1595,14 +1595,14 @@ document.getElementById("updates").addEventListener("toggle", function() { } document.getElementById("updates-div").innerHTML = text.replace(/\n/g, "
") }, - function(xhr) { + function (xhr) { console.error(xhr); } ); }) const sound = { tone(frequency, end = 1000, gain = 0.05) { - const audioCtx = new(window.AudioContext || window.webkitAudioContext)(); //setup audio context + const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); //setup audio context const oscillator = audioCtx.createOscillator(); const gainNode = audioCtx.createGain(); gainNode.gain.value = gain; //controls volume @@ -1618,7 +1618,7 @@ const sound = { // return audioCtx }, portamento(frequency, end = 1000, shiftRate = 10, gain = 0.05) { - const audioCtx = new(window.AudioContext || window.webkitAudioContext)(); //setup audio context + const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); //setup audio context const oscillator = audioCtx.createOscillator(); const gainNode = audioCtx.createGain(); gainNode.gain.value = gain; //controls volume @@ -1681,6 +1681,9 @@ if (!localSettings.isHideImages) { } // console.log(urls, images) }); + document.getElementById("choose-grid").classList.add('choose-grid'); +} else { + document.getElementById("choose-grid").classList.add('choose-grid-no-images'); } diff --git a/js/level.js b/js/level.js index af7ec3c..9ec60f0 100644 --- a/js/level.js +++ b/js/level.js @@ -10,7 +10,7 @@ const level = { // playableLevels: ["pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion"], //see level.populateLevels: (intro, ... , reservoir or factory, reactor, ... , gauntlet, final) added later playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion", "lock"], - communityLevels: ["stronghold", "basement", "crossfire", "vats", "run", "n-gon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock"], + communityLevels: ["stronghold", "basement", "crossfire", "vats", "run", "n-gon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton"], trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon"], levels: [], start() { @@ -18,37 +18,39 @@ const level = { // simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode // simulation.isHorizontalFlipped = true // tech.giveTech("performance") - // level.difficultyIncrease(16 * 4) //30 is near max on hard //60 is near max on why + // level.difficultyIncrease(0 * 4) //30 is near max on hard //60 is near max on why // spawn.setSpawnList(); // spawn.setSpawnList(); // m.maxHealth = m.health = 100 // tech.isRerollDamage = true - // powerUps.research.changeRerolls(50) + // powerUps.research.changeRerolls(500) // m.immuneCycle = Infinity //you can't take damage // tech.tech[297].frequency = 100 // m.couplingChange(5) - // m.setField("metamaterial cloaking") //molecular assembler standing wave time dilation perfect diamagnetism metamaterial cloaking wormhole negative mass pilot wave plasma torch + // m.setField("wormhole") //1 standing wave 2 perfect diamagnetism 3 negative mass 4 molecular assembler 5 plasma torch 6 time dilation 7 metamaterial cloaking 8 pilot wave 9 wormhole // simulation.molecularMode = 2 // m.damage(0.1); // b.giveGuns("nail gun") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser // b.giveGuns("wave") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser // b.guns[3].ammo = 100000000 - // tech.giveTech("phonon") + // tech.giveTech("recycling") // tech.giveTech("dead reckoning") - // for (let i = 0; i < 1; ++i) tech.giveTech("mass-energy equivalence") - // for (let i = 0; i < 1; ++i) tech.giveTech("decorrelation") - // for (let i = 0; i < 1; i++) tech.giveTech("gun turret") + // for (let i = 0; i < 1; ++i) tech.giveTech("pseudoscience") + // for (let i = 0; i < 1; ++i) tech.giveTech("options exchange") + // for (let i = 0; i < 1; i++) tech.giveTech("laser-bot") // for (let i = 0; i < 1; i++) tech.giveTech("simulated annealing") // for (let i = 0; i < 3; i++) powerUps.directSpawn(450, -50, "tech"); // for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "boost"); // for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "coupling"); // level.testing(); - // spawn.nodeGroup(1200, -300, "starter") + // spawn.nodeGroup(3200, -300, "sniper") + // spawn.nodeGroup(2200, -300, "sniper") + // spawn.nodeGroup(2200, -300, "sniper") // spawn.mantisBoss(1900, -500) // spawn.sneakBoss(1900, -500) // spawn.starter(1900, -500, 50) // spawn.sneaker(1900, -500, 25) - // spawn.hopper(2538, -950) + // spawn.sniper(2000, -450) // spawn.zombie(1000 + 1000 * Math.random(), -500 + 300 * Math.random(), 30, 5, "white") // zombie(x, y, radius, sides, color) // for (let i = 0; i < 20; ++i) spawn.starter(1000 + 1000 * Math.random(), -500 + 300 * Math.random()) // tech.addJunkTechToPool(2) @@ -61,9 +63,9 @@ const level = { // simulation.isAutoZoom = false; //look in close // simulation.zoomScale *= 0.5; // simulation.setZoom(); - // for (let i = 0; i < 2; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "tech"); + // for (let i = 0; i < 4; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "tech"); // for (let i = 0; i < 2; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "gun"); - // for (let i = 0; i < 13; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "research"); + // for (let i = 0; i < 10; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "research"); // for (let i = 0; i < 2; i++) powerUps.spawn(player.position.x + Math.random() * 50, player.position.y - Math.random() * 50, "field", false); //lore testing // for (let i = 0; i < 5; i++) tech.giveTech("undefined") @@ -81,6 +83,7 @@ const level = { // lore.unlockTesting(); // tech.giveTech("tinker"); //show junk tech in experiment mode // simulation.isCheating = false + // m.storeTech() // powerUps.spawn(m.pos.x, m.pos.y, "entanglement", false); } else { @@ -181,20 +184,20 @@ const level = { trainingText(say) { simulation.lastLogTime = 0; //clear previous messages simulation.isTextLogOpen = true - simulation.makeTextLog(`supervised.learning(${(Date.now()/1000).toFixed(0)} s):
${say}
`, Infinity) + simulation.makeTextLog(`supervised.learning(${(Date.now() / 1000).toFixed(0)} s):
${say}
`, Infinity) simulation.isTextLogOpen = false // lore.trainer.text("Wow. Just a platform.") }, trainingBackgroundColor: "#e1e1e1", - custom() {}, - customTopLayer() {}, + custom() { }, + customTopLayer() { }, setDifficulty() { simulation.difficulty = 0 m.dmgScale = 1; //damage done by player decreases each level simulation.accelScale = 1 //mob acceleration increases each level simulation.CDScale = 1 //mob CD time decreases each level - simulation.dmgScale = Math.max(0.1, 0.31 * simulation.difficulty) //damage done by mobs scales with total levels - simulation.healScale = 1 / (1 + simulation.difficulty * 0.047) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale; + simulation.dmgScale = Math.max(0.1, 0.29 * simulation.difficulty) //damage done by mobs scales with total levels + simulation.healScale = 1 / (1 + simulation.difficulty * 0.045) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale; }, difficultyIncrease(num = 1) { for (let i = 0; i < num; i++) { @@ -203,8 +206,8 @@ const level = { if (simulation.accelScale < 6) simulation.accelScale *= 1.024 //mob acceleration increases each level if (simulation.CDScale > 0.15) simulation.CDScale *= 0.964 //mob CD time decreases each level } - simulation.dmgScale = Math.max(0.1, 0.31 * simulation.difficulty) //damage done by mobs scales with total levels - simulation.healScale = 1 / (1 + simulation.difficulty * 0.047) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale; + simulation.dmgScale = Math.max(0.1, 0.29 * simulation.difficulty) //damage done by mobs scales with total levels + simulation.healScale = 1 / (1 + simulation.difficulty * 0.045) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale; // console.log(`CD = ${simulation.CDScale}`) }, difficultyDecrease(num = 1) { //used in easy mode for simulation.reset() @@ -215,8 +218,8 @@ const level = { if (simulation.CDScale < 1) simulation.CDScale /= 0.964 //mob CD time decreases each level } if (simulation.difficulty < 1) simulation.difficulty = 0; - simulation.dmgScale = Math.max(0.1, 0.31 * simulation.difficulty) //damage done by mobs scales with total levels - simulation.healScale = 1 / (1 + simulation.difficulty * 0.047) + simulation.dmgScale = Math.max(0.1, 0.29 * simulation.difficulty) //damage done by mobs scales with total levels + simulation.healScale = 1 / (1 + simulation.difficulty * 0.045) }, difficultyText() { if (simulation.difficultyMode === 1) { @@ -262,7 +265,7 @@ const level = { document.getElementById("damage-bar").style.display = "none" document.getElementById("text-log").style.display = "none" document.getElementById("fade-out").style.opacity = 1; //slowly fades out - setTimeout(function() { + setTimeout(function () { simulation.paused = true; level.disableExit = false; engine.world.bodies.forEach((body) => { @@ -554,7 +557,7 @@ const level = { x: who.position.x, y: who.position.y } - who.rotate = function() { + who.rotate = function () { if (!m.isBodiesAsleep) { Matter.Body.applyForce(this, { x: this.position.x + 100, @@ -789,9 +792,9 @@ const level = { } } else { if (Vector.magnitudeSquared(Vector.sub(this.position, { - x: x, - y: y - })) < distance * distance) { + x: x, + y: y + })) < distance * distance) { this.force.y -= force * this.mass } else { this.constraint.damping = 1 @@ -1267,7 +1270,7 @@ const level = { const mapWidth = 200 const unitA = Matter.Vector.rotate({ x: 1, y: 0 }, angleA) const unitB = Matter.Vector.rotate({ x: 1, y: 0 }, angleB) - draw = function() { + draw = function () { ctx.beginPath(); //portal let v = this.vertices; ctx.moveTo(v[0].x, v[0].y); @@ -1275,7 +1278,7 @@ const level = { ctx.fillStyle = this.color ctx.fill(); } - query = function(isRemoveBlocks = false) { + query = function (isRemoveBlocks = false) { if (Matter.Query.collides(this, [player]).length === 0) { //not touching player if (player.isInPortal === this) player.isInPortal = null } else if (player.isInPortal !== this) { //touching player @@ -2069,7 +2072,7 @@ const level = { button.isReadyToFire = true } else if (button.isReadyToFire && !button.isUp) { button.isReadyToFire = false - fireBlock = function(xPos, yPos) { + fireBlock = function (xPos, yPos) { const index = body.length spawn.bodyRect(xPos, yPos, 35 + 50 * Math.random(), 35 + 50 * Math.random()); const bodyBullet = body[body.length - 1] @@ -2126,7 +2129,7 @@ const level = { button.isReadyToFire = true } else if (button.isReadyToFire && !button.isUp) { button.isReadyToFire = false - fireBlock = function(xPos, yPos) { + fireBlock = function (xPos, yPos) { const index = body.length spawn.bodyRect(xPos, yPos, 35 + 50 * Math.random(), 35 + 50 * Math.random()); const bodyBullet = body[body.length - 1] @@ -3149,7 +3152,7 @@ const level = { // spawn.mapRect(1200, -1300, 600, 800); const a = 400 //side length const c = 100 //corner offset - spawn.mapVertex(1487, -900, `${-a} ${-a+c} ${-a+c} ${-a} ${a-c} ${-a} ${a} ${-a+c} ${a} ${a-c} ${a-c} ${a} ${-a+c} ${a} ${-a} ${a-c}`); //square with edges cut off + spawn.mapVertex(1487, -900, `${-a} ${-a + c} ${-a + c} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${a - c} ${a - c} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //square with edges cut off //entrance spawn.mapRect(-2025, -2825, 1250, 4925); spawn.mapRect(-900, -2825, 1125, 1725); @@ -3371,7 +3374,7 @@ const level = { level.enter.draw(); }; - level.customTopLayer = () => {}; + level.customTopLayer = () => { }; spawn.mapRect(-100, 0, 1000, 100); // powerUps.spawnStartingPowerUps(1475, -1175); @@ -3888,7 +3891,7 @@ const level = { slime.query(); slime2.query(); - ctx.fillStyle = `hsla(160, 100%, 43%,${0.3+0.07*Math.random()})` + ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})` ctx.fillRect(15900 + 400 * Math.random(), -1360, 2, 6000) if (buttonDoor.isUp) { door.isClosing = true @@ -4560,7 +4563,7 @@ const level = { if (isWaterfallFilling) { if (slime.height < 5500) { //draw slime fill - ctx.fillStyle = `hsla(160, 100%, 43%,${0.3+0.07*Math.random()})` + ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})` ctx.fillRect(waterFallX, -5050, waterFallWidth, 6175 - slime.height) if (!m.isBodiesAsleep) { waterFallWidth = 0.98 * waterFallWidth + 4.7 * Math.random() @@ -4676,7 +4679,7 @@ const level = { if (isWaterfallFilling) { if (slime.height < 5500) { //draw slime fill - ctx.fillStyle = `hsla(160, 100%, 43%,${0.3+0.07*Math.random()})` + ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})` ctx.fillRect(waterFallX, -5050, waterFallWidth, 6175 - slime.height) if (!m.isBodiesAsleep) { waterFallWidth = 0.98 * waterFallWidth + 4.7 * Math.random() @@ -8042,11 +8045,11 @@ const level = { body[body.length] = part4; body[body.length] = part5; body[body.length] = part6; - setTimeout(function() { + setTimeout(function () { chair.collisionFilter.category = cat.body; chair.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map }, 1000); - setTimeout(function() { + setTimeout(function () { chair2.collisionFilter.category = cat.body; chair2.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map }, 1000); @@ -8101,7 +8104,7 @@ const level = { body[body.length] = rightUpperLeg body[body.length] = rightLowerArm body[body.length] = rightUpperArm - setTimeout(function() { + setTimeout(function () { person.collisionFilter.category = cat.body; person.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map }, 1000); @@ -8495,7 +8498,7 @@ const level = { level.enter.draw(); }; - level.customTopLayer = () => {}; + level.customTopLayer = () => { }; level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#dcdcde"; @@ -9562,7 +9565,7 @@ const level = { body[body.length] = part1; body[body.length] = part2; body[body.length] = part3; - setTimeout(function() { + setTimeout(function () { compoundParts.collisionFilter.category = cat.body; compoundParts.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map }, 1000); @@ -9683,8 +9686,8 @@ const level = { // prevent the user from getting into the secreter room without defeating all mobs if (m.pos.x > 1500 && m.pos.x < 2500 && m.pos.y > -4000 && m.pos.y < -3500 && mob.reduce((a, i) => { - return a || ((Math.sqrt((i.position.x - 3600) * (i.position.x - 3600) + (i.position.y + 3600) * (i.position.y + 3600)) < 20000) && i.isDropPowerUp); - }, false) && !emergencyActivated) { + return a || ((Math.sqrt((i.position.x - 3600) * (i.position.x - 3600) + (i.position.y + 3600) * (i.position.y + 3600)) < 20000) && i.isDropPowerUp); + }, false) && !emergencyActivated) { Matter.Body.setPosition(player, { x: 2800, y: m.pos.y @@ -10518,7 +10521,7 @@ const level = { level.customTopLayer = () => { slimePit1.query(); slimePit2.query(); - ctx.fillStyle = `rgba(68, 68, 68, ${Math.max(0.3,Math.min((-17650 - m.pos.y) / 100, 0.99))})`; + ctx.fillStyle = `rgba(68, 68, 68, ${Math.max(0.3, Math.min((-17650 - m.pos.y) / 100, 0.99))})`; ctx.fillRect(58390, -17655, 1490, 740); }; document.body.style.backgroundColor = "hsl(138, 3%, 74%)"; @@ -10710,25 +10713,25 @@ const level = { // 2.5 * Math.PI // ); //right portal2 = level.portal({ - x: 61920, - y: -16525, - }, + x: 61920, + y: -16525, + }, 1.5 * Math.PI, { - //right - x: 58400, - y: -17325, - }, + //right + x: 58400, + y: -17325, + }, 2 * Math.PI ); portal3 = level.portal({ - x: 59865, - y: -17300, - }, + x: 59865, + y: -17300, + }, 3 * Math.PI, { - //right - x: 60820, - y: -31130, - }, + //right + x: 60820, + y: -31130, + }, 2.5 * Math.PI ); @@ -10827,19 +10830,19 @@ const level = { simulation.makeTextLog(`temple by Scar1337`); const V = Vector; - const Equation = (function() { + const Equation = (function () { function Equation(a, b, c) { this.a = a; this.b = b; this.c = c; } - Equation.prototype.getXfromY = function(y) { + Equation.prototype.getXfromY = function (y) { return (-this.b * y - this.c) / this.a; } - Equation.prototype.getYfromX = function(x) { + Equation.prototype.getYfromX = function (x) { return (-this.a * x - this.c) / this.b; } - Equation.fromPoints = function(v1, v2) { + Equation.fromPoints = function (v1, v2) { if (v1.x === v2.x) return new Equation(1, 0, -v1.x); if (v1.y === v2.y) return new Equation(0, 1, -v1.y); const d = (v2.y - v1.y) / (v2.x - v1.x); @@ -10847,7 +10850,7 @@ const level = { }; return Equation; })(); - const Rect = (function() { + const Rect = (function () { function Rect(x, y, w, h) { this.pos = { x, @@ -10856,14 +10859,14 @@ const level = { this.width = w; this.height = h; } - Rect.prototype.has = function({ + Rect.prototype.has = function ({ x, y }) { return x >= this.pos.x && x <= this.pos.x + this.width && y >= this.pos.y && y <= this.pos.y + this.height; } - Rect.prototype.hasLine = function(eq) { + Rect.prototype.hasLine = function (eq) { const leftInter = eq.getYfromX(this.pos.x); const rightInter = eq.getYfromX(this.pos.x + this.width); const topInter = eq.getXfromY(this.pos.y); @@ -10871,7 +10874,7 @@ const level = { (rightInter >= this.pos.y && rightInter <= this.pos.y + this.height) || (topInter >= this.pos.x && topInter <= this.pos.x + this.width); } - Rect.prototype.addToMap = function() { + Rect.prototype.addToMap = function () { spawn.mapRect(this.pos.x, this.pos.y, this.width, this.height); } Object.defineProperty(Rect.prototype, "midPos", { @@ -10882,10 +10885,10 @@ const level = { }); } }); - Rect.fromBounds = function(min, max) { + Rect.fromBounds = function (min, max) { return new Rect(min.x, min.y, max.x - min.x, max.y - min.y); } - Rect.prototype.isCollidingWith = function(other) { + Rect.prototype.isCollidingWith = function (other) { const tc = { p1: [this.pos.x, this.pos.y], p2: [this.pos.x + this.width, this.pos.y + this.height] @@ -10946,14 +10949,14 @@ const level = { me.attackCycle = 0; me.lastAttackCycle = 0; Matter.Body.setDensity(me, 0.012); // extra dense, normal is 0.001 // makes effective life much larger - me.onDeath = function() { + me.onDeath = function () { // applying forces to player doesn't seem to work inside this method, not sure why powerUps.spawn(this.position.x + 20, this.position.y, "ammo"); if (Math.random() > 0.5) powerUps.spawn(this.position.x, this.position.y, "ammo"); if (Math.random() > 0.3) powerUps.spawn(this.position.x, this.position.y, "heal", true, null, 30 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals) * Math.sqrt(0.1 + Math.random() * 0.5)); }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); - me.do = function() { + me.do = function () { // keep it slow, to stop issues from explosion knock backs if (this.speed > 1) { Matter.Body.setVelocity(this, { @@ -11050,13 +11053,13 @@ const level = { me.attackCycle = 0; me.maxAttackCycle = isDark ? 90 : 240; Matter.Body.setDensity(me, 0.006); // extra dense, normal is 0.001 // makes effective life much larger - me.onDeath = function() { + me.onDeath = function () { powerUps.spawn(this.position.x + 20, this.position.y, "ammo"); if (Math.random() > 0.5) powerUps.spawn(this.position.x, this.position.y, "ammo"); if (Math.random() > 0.3) powerUps.spawn(this.position.x, this.position.y, "heal", true, null, 30 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals) * Math.sqrt(0.1 + Math.random() * 0.5)); }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); - me.do = function() { + me.do = function () { // keep it slow, to stop issues from explosion knock backs if (this.speed > 2) { Matter.Body.setVelocity(this, { @@ -11108,12 +11111,12 @@ const level = { me.maxAttackCycle = 10; me.inertia = Infinity; } - me.do = isDark ? function() { + me.do = isDark ? function () { Matter.Body.setVelocity(this, { x: this.velocity.x * 0.95, y: this.velocity.y * 0.95 }); - } : function() { + } : function () { Matter.Body.setVelocity(this, { x: this.velocity.x * 0.95, y: this.velocity.y * 0.95 @@ -11153,7 +11156,7 @@ const level = { let me = mob[mob.length - 1]; me.fill = "#ace"; me.damageReduction = 0; - me.onDeath = function() { + me.onDeath = function () { //damage player if in range if (distance(player.position, this.position) < pulseRadius && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage @@ -11167,7 +11170,7 @@ const level = { time: simulation.drawTime }); }; - me.do = function() { + me.do = function () { this.timeLimit(); ctx.beginPath(); //draw explosion outline ctx.arc(this.position.x, this.position.y, pulseRadius * (1.01 - this.timeLeft / this.lifeSpan), 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay @@ -11201,7 +11204,7 @@ const level = { me.lastAttackCycle = 0; me.spawnCycle = 0; Matter.Body.setDensity(me, 0.08); //extra dense //normal is 0.001 //makes effective life much larger - me.onDeath = function() { + me.onDeath = function () { for (let j = 0; j < 8; j++) { //in case some mobs leave things after they die for (let i = 0, len = mob.length; i < len; ++i) { if (mob[i] !== this) { @@ -11220,7 +11223,7 @@ const level = { }; me.nextHealthThreshold = 0.75; me.trapCycle = 0; - me.onDamage = function() { + me.onDamage = function () { if (this.health < this.nextHealthThreshold) { this.health = this.nextHealthThreshold - 0.01 this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 @@ -11243,7 +11246,7 @@ const level = { radius: 500, id: 2 }]; - me.ring = function() { + me.ring = function () { if (this.isInvulnerable) return; ctx.lineWidth = 10; for (const ring of this.rings) { @@ -11255,7 +11258,7 @@ const level = { DrawTools.arcOut(this.position.x, this.position.y, radius, 0, Math.PI * 2); } } - me.horizon = function() { + me.horizon = function () { if (this.isInvulnerable) return this.fill = "#f00"; // eventHorizon waves in and out const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008)); @@ -11310,7 +11313,7 @@ const level = { DrawTools.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); } } - me.periodicSpawns = function() { + me.periodicSpawns = function () { if (this.isInvulnerable) return; this.spawnCycle++; // Spawn annoying purple thing(s) that chases the player @@ -11350,7 +11353,7 @@ const level = { spawn.allowShields = true; } } - me.invulnerableTrap = function() { + me.invulnerableTrap = function () { if (this.trapCycle < 1) return; this.trapCycle++; // 24 is just an arbitrarily large number @@ -11402,7 +11405,7 @@ const level = { ctx.fillText("!", 2700, -14350); ctx.shadowBlur = 0; } - me.do = function() { + me.do = function () { this.checkStatus(); this.horizon(); this.ring(); @@ -11423,7 +11426,7 @@ const level = { let bounds = []; let mobPositionsQueue = Array.from(Array(10), () => []); m.oldDeath = m.death; - m.death = function() { + m.death = function () { if (!tech.isImmortal) { requestAnimationFrame(() => color.map = "#444"); m.death = m.oldDeath; @@ -11534,7 +11537,7 @@ const level = { spawn.mapRect(-500, -8250, 800, 20); for (let i = 0; i < 2; i++) spawn.mapRect(-250, -8400 + 150 * i, 500, 60); const room2SlimePit = level.hazard(-400, -8410, 800, 1090); - room2SlimePit.logic = function() { + room2SlimePit.logic = function () { if (this.height > 0 && Matter.Query.region([player], this).length) { if (m.immuneCycle < m.cycle) { // Trolled @@ -11574,7 +11577,7 @@ const level = { }); } } - room2SlimePit.draw = function() { + room2SlimePit.draw = function () { if (this.isOn) { ctx.fillStyle = "hsla(160, 100%, 35%, 0.75)"; ctx.fillRect(this.min.x, this.min.y, this.width, this.height); @@ -11776,7 +11779,7 @@ const level = { } }, room2GeneratedPath: { - rects: (function() { + rects: (function () { const rects = []; for (let i = 0; i < 4; i++) { rects.push(new Rect(-1405 + (i & 1) * 200, -9700 + i * 300, 205, 30)); @@ -11807,16 +11810,16 @@ const level = { } }, room3Rotors: { - rotor1: (function() { + rotor1: (function () { const rotor = level.spinner(900, -13700, 200, 30); - rotor.rotate = function() { + rotor.rotate = function () { Matter.Body.setAngularVelocity(this.bodyB, (this.bodyB.angularVelocity + 0.01) * 0.9) } return rotor; })(), - rotor2: (function() { + rotor2: (function () { const rotor = level.spinner(2700, -13700, 200, 30); - rotor.rotate = function() { + rotor.rotate = function () { Matter.Body.setAngularVelocity(this.bodyB, (this.bodyB.angularVelocity - 0.01) * 0.9) } return rotor; @@ -12355,22 +12358,22 @@ const level = { }, holy(x, y, size = 12) { this.line([{ - x, - y: y - size - }, { - x: x - size, - y - }, - { - x, - y: y + size - }, { - x: x + size, - y - }, { - x, - y: y - size - } + x, + y: y - size + }, { + x: x - size, + y + }, + { + x, + y: y + size + }, { + x: x + size, + y + }, { + x, + y: y - size + } ]); } }; @@ -12772,7 +12775,7 @@ const level = { me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.mob | cat.bullet me.g = simulation.g me.leaveBody = me.isDropPowerUp = false - me.do = function() { + me.do = function () { this.gravity() // apply shock damage when touching the map, if it's fast if (this.speed > 5) { @@ -12787,7 +12790,7 @@ const level = { this.fill = `rgb(${232 * this.health}, 191, 40)` } - me.onDeath = function() { + me.onDeath = function () { const END = Math.floor(input.down ? 10 : 7) const totalBullets = 10 const angleStep = (input.down ? 0.4 : 1.3) / totalBullets @@ -12811,16 +12814,16 @@ const level = { x: speed * Math.cos(dirOff), y: speed * Math.sin(dirOff) }) - bullet[me].onEnd = function() { + bullet[me].onEnd = function () { b.explosion( this.position, 150 + (Math.random() - 0.5) * 40 ) //makes bullet do explosive damage at end } - bullet[me].beforeDmg = function() { + bullet[me].beforeDmg = function () { this.endCycle = 0 //bullet ends cycle after hitting a mob and triggers explosion } - bullet[me].do = function() {} + bullet[me].do = function () { } Composite.add(engine.world, bullet[me]) //add bullet to world } // barrels drop a ton of ammo and some heals, scales up with difficulty because I have mercy @@ -12923,12 +12926,12 @@ const level = { spawn[mobType](x, y + chainLength + radius * 2) const trappedMob = mob[mob.length - 1] // destroy its mind so it won't attack - trappedMob.do = () => {} + trappedMob.do = () => { } // spawn the cage mobs.spawn(x, y + chainLength + radius * 2, 4, trappedMob.radius + 50, 'rgba(150, 255, 150, 0.3)') const cage = mob[mob.length - 1] cage.g = simulation.g - cage.do = function() { + cage.do = function () { this.gravity() } // label it @@ -13226,7 +13229,7 @@ const level = { const color = `rgba(${150 + 105 * charge}, 81, 50, 0.6)` mobs.spawn(origin.x, origin.y, 12, 20 + 20 * charge, color) const me = mob[mob.length - 1] - me.end = function() { + me.end = function () { simulation.drawList.push({ // some nice graphics x: this.position.x, @@ -13249,7 +13252,7 @@ const level = { me.life = 0 me.isDropPowerUp = false me.leaveBody = false - me.do = function() { + me.do = function () { // die on collision with the map if (Matter.Query.collides(this, map).length > 0) { this.end() @@ -13312,7 +13315,7 @@ const level = { me.bossPos = null // the position that the mob remembers when charging me.density = me.density * 2 Matter.Body.setDensity(me, 0.0022 * 3 + 0.0002 * Math.sqrt(simulation.difficulty)) //extra dense - me.do = function() { + me.do = function () { // if the boss is dead, die if (!parentBoss.alive) { this.death() @@ -13337,8 +13340,8 @@ const level = { ctx.lineTo(m.pos.x, m.pos.y) ctx.lineWidth = 3 + Math.abs(Math.sin((simulation.cycle + this.seed) / 100)) * 2 ctx.strokeStyle = `rgb(${( - Math.abs(Math.sin((simulation.cycle + this.seed + 100) / 100)) * 255 - ).toFixed(3)}, 204, 255)` + Math.abs(Math.sin((simulation.cycle + this.seed + 100) / 100)) * 255 + ).toFixed(3)}, 204, 255)` ctx.setLineDash([125 * Math.random(), 125 * Math.random()]) ctx.stroke() ctx.setLineDash([]) @@ -13369,8 +13372,8 @@ const level = { ctx.lineTo(this.bossPos.x, this.bossPos.y) ctx.lineWidth = 10 + Math.abs(Math.sin((simulation.cycle + this.seed) / 100)) * 5 ctx.strokeStyle = `rgb(${( - Math.abs(Math.sin((simulation.cycle + this.seed + 100) / 100)) * 255 - ).toFixed(3)}, 204, 255)` + Math.abs(Math.sin((simulation.cycle + this.seed + 100) / 100)) * 255 + ).toFixed(3)}, 204, 255)` ctx.setLineDash([125 * Math.random(), 125 * Math.random()]) ctx.stroke() ctx.setLineDash([]) @@ -13380,7 +13383,7 @@ const level = { // draw energy bar drawEnergyBar(this) } - me.onDeath = function() { + me.onDeath = function () { // remove itself from the list const beacons = parentBoss.energyBeacons beacons.splice(beacons.indexOf(this), 1) @@ -13425,7 +13428,7 @@ const level = { me.showHealthBar = false me.collisionFilter.category = cat.mobBullet me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet - me.do = function() { + me.do = function () { this.alwaysSeePlayer() this.attraction() this.timeLimit() @@ -13441,7 +13444,7 @@ const level = { } // give a bonus if some projectile is nearby or the mouse position is close (like laser pointing) // if a mob survives this for long, then it gets a score benefit. - const bulletCloseToOrb = bullet.some(it => dist2(this.position, it.position) < 10000 /* 100 ^ 2 */ ) + const bulletCloseToOrb = bullet.some(it => dist2(this.position, it.position) < 10000 /* 100 ^ 2 */) // player shoots and aims close const mouseCloseToOrb = dist2(this.position, simulation.mouseInGame) < 10000 && input.fire if (bulletCloseToOrb || mouseCloseToOrb) { @@ -13456,7 +13459,7 @@ const level = { // ctx.fillStyle = 'rgba(252, 0, 143, 1)' // ctx.fillText(~~this.score, this.position.x - this.radius, this.position.y - this.radius) } - me.onHit = function() { + me.onHit = function () { // hitting the player gives a 50 points score bonus this.score += 50 this.score += this.mass * 2 // bigger mass = bigger damage, add that too @@ -13464,7 +13467,7 @@ const level = { this.hitPlayer = true this.explode(this.mass) } - me.onDeath = function() { + me.onDeath = function () { if (!this.hitPlayer) { // if it didn't hit the player, give it a score based on its distance this.score += 10000 / this.distanceToPlayer() @@ -13518,7 +13521,7 @@ const level = { me.laserRange = radius * 4 Matter.Body.setDensity(me, 0.0022 * 4 + 0.0002 * Math.sqrt(simulation.difficulty)) //extra dense //normal is 0.001 //makes effective life much larger - me.onDeath = function() { + me.onDeath = function () { if (spawnBossPowerUp) { powerUps.spawnBossPowerUp(this.position.x, this.position.y) const amount = ~~(5 * Math.random() * simulation.difficulty / 10) * 2 @@ -13537,8 +13540,8 @@ const level = { // stop spawning barrels bossInit = false } - me.onDamage = function() {} - me.spawnBeacon = function() { + me.onDamage = function () { } + me.spawnBeacon = function () { // the vertex to spawn the beacon from const vert = this.vertices[~~(Math.random() * this.vertices.length)] // the position should be a little to the side to prevent crashing into the boss @@ -13554,7 +13557,7 @@ const level = { y: this.velocity.y + velocity.y }) } - me.spawnOrbs = function() { + me.spawnOrbs = function () { Matter.Body.setAngularVelocity(this, 0.11) // sort the vertices by the distance to the player const sorted = [...this.vertices].sort(dist2) @@ -13611,7 +13614,7 @@ const level = { }) } } - me.do = function() { + me.do = function () { this.seePlayerCheck() this.checkStatus() this.attraction() @@ -13823,7 +13826,7 @@ const level = { stereoMadness() { simulation.makeTextLog(`stereoMadness by Richard0820`); let totalCoin = 0; - const hunter = function(x, y, radius = 30) { //doesn't stop chasing until past 105000 + const hunter = function (x, y, radius = 30) { //doesn't stop chasing until past 105000 mobs.spawn(x, y, 6, radius, "black"); let me = mob[mob.length - 1]; me.stroke = "transparent"; @@ -13837,7 +13840,7 @@ const level = { Matter.Body.setDensity(me, 1) simulation.makeTextLog(`Ω: Intruder Detected`); me.boost = 10; - me.do = function() { + me.do = function () { if (me.boost == 1 && m.fieldMode == 3 || m.fieldMode == 9 && me.boost == 1) { me.accelMag *= 1.5; me.boost--; @@ -13851,13 +13854,13 @@ const level = { this.death() } }; - me.onHit = function() { + me.onHit = function () { for (let i = 0; i < 10; i++) { spawn.spawns(this.position.x + Math.random() * 1000 - Math.random() * 1000, this.position.y - Math.random() * 1000) } } } - const coin = function(x, y, radius = 50) { + const coin = function (x, y, radius = 50) { mobs.spawn(x, y, 40, radius, "yellow"); let me = mob[mob.length - 1]; me.stroke = "#00000055" @@ -13869,11 +13872,11 @@ const level = { me.showHealthBar = false; me.collisionFilter.category = 0; Matter.Body.setDensity(me, 0.0045); - me.onDeath = function() { + me.onDeath = function () { totalCoin++; }; me.damageReduction = 0.35 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { ctx.save() ctx.translate(this.position.x, this.position.y) ctx.rotate(Math.PI / 2 + 0.5) @@ -13911,7 +13914,7 @@ const level = { }; } } - const spike = function(x, y, angle = Math.PI * 0.5, radius = 50) { + const spike = function (x, y, angle = Math.PI * 0.5, radius = 50) { mobs.spawn(x, y, 3, radius, "#454545"); let me = mob[mob.length - 1]; me.stroke = "transparent"; @@ -13928,7 +13931,7 @@ const level = { stiffness: 0, damping: 0 }); - me.do = function() { + me.do = function () { if (this.health < 1) { this.health += 0.001; //regen simulation.drawList.push({ @@ -13942,14 +13945,14 @@ const level = { this.checkStatus(); Matter.Body.setAngle(me, angle); }; - me.onHit = function() { + me.onHit = function () { m.damage(0.01) //extra damage me.collisionFilter.mask = 0; setTimeout(() => { me.collisionFilter.mask = cat.player | cat.mob | cat.bullet; }, 1000); } - me.onDeath = function() { + me.onDeath = function () { tech.addJunkTechToPool(0.1) } Composite.add(engine.world, me.constraint); @@ -14017,7 +14020,7 @@ const level = { level.custom = () => { level.exit.drawAndCheck(); if (barThere == true) { - innerBar.style.width = "calc(" + `${Math.max(0, Math.min(player.position.x/1310, 80))}` + "vw - 10px)"; + innerBar.style.width = "calc(" + `${Math.max(0, Math.min(player.position.x / 1310, 80))}` + "vw - 10px)"; innerBar.style.backgroundColor = m.eyeFillColor; } if (m.pos.x > 25360 && textlogOne == 0) { @@ -14961,7 +14964,7 @@ const level = { } } } - ctx.fillStyle = `rgba(68, 68, 68, ${Math.max(0.1,Math.min((-1400 - m.pos.y) / -100, 0.99))})`; + ctx.fillStyle = `rgba(68, 68, 68, ${Math.max(0.1, Math.min((-1400 - m.pos.y) / -100, 0.99))})`; ctx.fillRect(91900, -1675, 12050, 375) ctx.save() ctx.translate(104700, -1675); @@ -15408,7 +15411,7 @@ const level = { let destroyed = false; const lock = level.door(425, -1400, 50, 300, 300); - const core = function(x, y, radius = 100 + Math.ceil(Math.random() * 25)) { + const core = function (x, y, radius = 100 + Math.ceil(Math.random() * 25)) { radius = 9 + radius / 8; //extra small mobs.spawn(x, y, 6, radius, "transparent"); let me = mob[mob.length - 1]; @@ -15432,11 +15435,11 @@ const level = { me.memory = Infinity; me.isBoss = true; Matter.Body.setDensity(me, 1); //extra dense //normal is 0.001 //makes effective life much larger - me.onDeath = function() { + me.onDeath = function () { destroyed = true; powerUps.spawnBossPowerUp(this.position.x, this.position.y); } - me.do = function() { + me.do = function () { if (this.health < 1) { this.health += 0.001; //regen simulation.drawList.push({ @@ -15488,7 +15491,7 @@ const level = { } } } - const sniper = function(x, y, radius = 35 + Math.ceil(Math.random() * 30)) { //same, just white so that we can seen + const sniper = function (x, y, radius = 35 + Math.ceil(Math.random() * 30)) { //same, just white so that we can seen mobs.spawn(x, y, 3, radius, "transparent"); //"rgb(25,0,50)") let me = mob[mob.length - 1]; me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front @@ -15514,11 +15517,11 @@ const level = { x: 0, y: 0 }; - me.onDeath = function() { //helps collisions functions work better after vertex have been changed + me.onDeath = function () { //helps collisions functions work better after vertex have been changed // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) } // spawn.shield(me, x, y); - me.do = function() { + me.do = function () { // this.seePlayerByLookingAt(); this.seePlayerCheck(); this.checkStatus(); @@ -16005,20 +16008,29 @@ const level = { const door2 = level.door(17041, -412, 110, 510, 480) const buttonDoor3 = level.button(20456.6, -1636.2) const door3 = level.door(20238, -781.4, 88, 452, 412) - const hazard2 = level.hazard(2550, -150, 10, 0.4) //y=-1485 + const hazard2 = level.hazard(2550, -150, 10, 0.4) //y=-1485 simulation.enableConstructMode() level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 15316; - level.exit.y = -84; + level.exit.y = -30; spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#001738"; color.map = "#444" //custom map color + + + level.custom = () => { - ctx.fillStyle = "rgba(250,250,250,0.8)" //lights + //spawn.mapRect(22330, -2688.75, 400, 800); + //spawn.mapRect(22330, -1793.5, 400, 800);//-46.25*2=-92.5 + //spawn.mapRect(22330, -804.25, 400, 800);//-46.25*3 + + + + ctx.fillStyle = "rgba(250,250,250,0.8)"//lights ctx.beginPath() ctx.moveTo(1124, -628) ctx.lineTo(496, 0) @@ -16034,37 +16046,56 @@ const level = { ctx.fill() ctx.beginPath() - ctx.moveTo(3330, -1905) //-700 + ctx.moveTo(3330, -1905)//-700 ctx.lineTo(2815.6, -1405.8) ctx.lineTo(2815.6, -1230) ctx.lineTo(4022.9, -1283.9) ctx.lineTo(4023.5, -1405.8) ctx.lineTo(3430, -1905) + ctx.fill() + + + ctx.fillStyle = "rgba(63,247,251,0.8)" - ctx.fillRect(22330, -2713.75, 550, 700) //15845.0, -1262.2 + ctx.fillRect(22330, -2713.75, 550, 700)//15845.0, -1262.2 ctx.fillRect(22330, -1743.5, 550, 700) ctx.fillRect(22330, -754.25, 550, 700) + ctx.fillRect(6237, -1830.7, 550, 700) ctx.fillRect(6237, -840.4, 550, 700) ctx.fillRect(15845.0, -1262.2, 550, 300) ctx.fillStyle = "rgba(200,200,200,0.8)" ctx.fillRect(-192, -1973, 6484, 2071) ctx.fillStyle = "rgba(240,240,240,0.8)" + ctx.fillRect(15109.5, -2867.5, 7284, 2971) ctx.fillStyle = "rgba(35,35,35,0.8)" ctx.fillRect(15145.9, -960, 200, 25) + + + + ctx.fillStyle = "rgba(255,255,255,0.9)" + + + + + buttonDoor.query(); buttonDoor.draw(); buttonDoor2.query(); buttonDoor2.draw(); buttonDoor3.query(); buttonDoor3.draw(); + + slime.query(); slime2.query(); - ctx.fillStyle = `hsla(160, 100%, 43%,${0.3+0.07*Math.random()})` + + ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})` + ctx.fillRect(15900 + 400 * Math.random(), -1360, 2, 6000) ctx.fillRect(15900 + 400 * Math.random(), -1360, 2, 6000) if (buttonDoor.isUp) { door.isClosing = true @@ -16088,6 +16119,7 @@ const level = { portal[3].query() portal2[2].query() portal2[3].query() + boost1.query(); boost2.query(); boost3.query(); @@ -16095,15 +16127,18 @@ const level = { boost5.query(); level.exit.drawAndCheck(); level.enter.draw(); - ctx.fillStyle = "rgba(0,0,0,0.2)" //shadows + ctx.fillStyle = "rgba(0,0,0,0.2)"//shadows ctx.fillRect(2773, -682, 469, 500) ctx.fillRect(3947, -851, 469, 700) ctx.fillRect(4818, -1006, 400, 400) ctx.fillRect(5313, -1309, 1000, 700) + ctx.fillRect(16705, -2831, 40, 700) ctx.fillRect(16140, -2812, 40, 400) ctx.fillRect(15559, -2855, 40, 800) ctx.fillRect(16530, -2855, 30, 200) + + ctx.beginPath() ctx.moveTo(18254.7, -2194.1) ctx.lineTo(18554.6, -1952.7) @@ -16116,25 +16151,33 @@ const level = { ctx.lineTo(18554.6, -802.7) ctx.lineTo(18214.7, -1004.1) ctx.fill() + ctx.beginPath() ctx.moveTo(17585.2, -1123.8) ctx.lineTo(17151.2, -781.7) ctx.lineTo(17151.2, -741.7) ctx.lineTo(17625.2, -1123.8) ctx.fill() + + ctx.fillRect(20540, -1103, 610, 300) ctx.fillRect(20820, -243, 410, 300) ctx.fillRect(5772, -609, 469, 700) ctx.fillRect(5772, -609, 469, 700) - ctx.fillStyle = "rgba(0,0,0,0.4)" //wires + ctx.fillStyle = "rgba(0,0,0,0.4)"//wires ctx.fillRect(20990, -2672, 20, 112) ctx.fillRect(21090, -2506, 72, 20) ctx.fillRect(21090, -1970, 72, 20) + + + ctx.fillRect(16901.8, -2497.7, 25, 100) ctx.fillRect(16901.8, -2397.7, 50, 25) ctx.fillRect(16951.8, -2397.7, 25, 1640) ctx.fillRect(16901.8, -782.7, 50, 25) ctx.fillRect(16901.8, -757.7, 25, 100) + + ctx.fillRect(20900, -2666, 500, 9) ctx.fillRect(20900, -2651, 1315, 9) ctx.fillRect(20900, -2636, 1300, 9) @@ -16142,41 +16185,72 @@ const level = { ctx.fillRect(20900, -2606, 230, 9) ctx.fillRect(20900, -2591, 215, 9) ctx.fillRect(20900, -2576, 200, 9) + ctx.fillRect(21145, -2621, 9, 700) ctx.fillRect(21130, -2606, 9, 1000) ctx.fillRect(21115, -2591, 9, 1000) ctx.fillRect(21100, -2576, 9, 850) + ctx.fillRect(21400, -3066, 9, 409) + + ctx.fillRect(20900, -1726, 209, 9) ctx.fillRect(21145, -1921, 270, 9) ctx.fillRect(21415, -1921, 9, 50) + ctx.fillRect(22200, -2636, 9, 1300) ctx.fillRect(22215, -2651, 9, 300) + ctx.fillRect(22200, -1336, 300, 9) ctx.fillRect(22215, -2351, 300, 9) - ctx.fillRect(916.5, -1725, 80, 80) //+55 // 55/2=27.5 - ctx.fillRect(1204, -1706, 25, 40) //179 + + + //943.9, -1698.0 + + ctx.fillRect(916.5, -1725, 80, 80)//+55 // 55/2=27.5 + ctx.fillRect(1204, -1706, 25, 40)//179 ctx.fillRect(1354, -1706, 25, 40) ctx.fillRect(1504, -1885, 25, 40) ctx.fillRect(3504, -1885, 25, 40) ctx.fillRect(5504, -1885, 25, 40) + + ctx.fillRect(1019, -1718, 9, 20) ctx.fillRect(1019, -1674, 9, 20) + ctx.fillRect(996, -1718, 23, 9) ctx.fillRect(996, -1663, 23, 9) + + + + ctx.fillRect(1019, -1698, 425, 9) ctx.fillRect(1444, -1868, 9, 179) ctx.fillRect(1444, -1877, 4700, 9) + ctx.fillRect(1019, -1683, 440, 9) ctx.fillRect(1459, -1853, 9, 179) ctx.fillRect(1459, -1862, 4670, 9) + ctx.fillRect(6144, -1877, 9, 100) ctx.fillRect(6144, -1777, 100, 9) + ctx.fillRect(6129, -1862, 9, 1100) ctx.fillRect(6129, -762, 150, 9) + + + + + + + }; + level.customTopLayer = () => { + + + door.draw(); door2.draw(); door3.draw(); @@ -16190,6 +16264,35 @@ const level = { portal2[2].draw(); portal2[3].draw(); elevator.move() + + + // if (player.position.x > 15900 && player.position.x < 16300 && player.position.y > -1360.2) { + // Matter.Body.setVelocity(player, { + // x: player.velocity.x, + // y: player.velocity.y + 10 + // }); + // }else{ + // if (Math.abs(player.velocity.x) > 0.5){ + // if (m.onGround){ + // Matter.Body.setVelocity(player, { + // x: player.velocity.x + (0.07 * (Math.abs(player.velocity.x) / player.velocity.x)), + // y: player.velocity.y - 0.2 + + // }); + // }else{ + // Matter.Body.setVelocity(player, { + // x: player.velocity.x, + // y: player.velocity.y - 0.2 + // }); + // } + // }else{ + // Matter.Body.setVelocity(player, { + // x: player.velocity.x, + // y: player.velocity.y - 0.2 + // }); + // } + // } + if (player.position.x > 15900 && player.position.x < 16300 && player.position.y > -1360.2) { Matter.Body.setVelocity(player, { x: player.velocity.x, @@ -16217,37 +16320,46 @@ const level = { } } hazard2.opticalQuery(); + + }; + + + + + //1273.2, -1404.7 + spawn.mapRect(1124, -653, 100, 25); spawn.mapRect(906, -1390, 100, 25); spawn.mapRect(3330, -1930, 100, 25); + //first ship base - spawn.mapRect(-300, 0, 6684, 100); //lower floor - spawn.mapRect(-300, -2071, 154, 2071); //left right wall - spawn.mapRect(2511, -300, 1309, 308); //left big block - spawn.mapRect(3820, -184, 1309, 184); //right big block - spawn.mapRect(-300, -739, 2549, 100); //upper right floor - spawn.mapRect(2056, -1309, 2764, 169); //upper center floor - spawn.mapRect(2056, -1309, 193, 650); //upper left floor wall - spawn.mapRect(4636, -1309, 193, 793); //upper right floor wall - spawn.mapRect(4821, -654, 955, 138); //upper right floor - spawn.mapRect(6237, -2071, 147, 2071); //far right wall - spawn.mapRect(-300, -2071, 6684, 154); //roof + spawn.mapRect(-300, 0, 6684, 100);//lower floor + spawn.mapRect(-300, -2071, 154, 2071);//left right wall + spawn.mapRect(2511, -300, 1309, 308);//left big block + spawn.mapRect(3820, -184, 1309, 184);//right big block + spawn.mapRect(-300, -739, 2549, 100);//upper right floor + spawn.mapRect(2056, -1309, 2764, 169);//upper center floor + spawn.mapRect(2056, -1309, 193, 650);//upper left floor wall + spawn.mapRect(4636, -1309, 193, 793);//upper right floor wall + spawn.mapRect(4821, -654, 955, 138);//upper right floor + spawn.mapRect(6237, -2071, 147, 2071);//far right wall + spawn.mapRect(-300, -2071, 6684, 154);//roof //first ship details - spawn.mapRect(245, -360, 70, 400); //start room wall + spawn.mapRect(245, -360, 70, 400);//start room wall spawn.mapRect(500, -1929, 154, 462); spawn.mapRect(185, -1517, 469, 77); - spawn.mapRect(2773, -682, 469, 77); //walls in 1st room + spawn.mapRect(2773, -682, 469, 77);//walls in 1st room spawn.mapRect(3743, -566, 77, 469); spawn.mapRect(3947, -851, 469, 77); - spawn.mapRect(5313, -1309, 1000, 70); //walls in second area + spawn.mapRect(5313, -1309, 1000, 70);//walls in second area spawn.mapRect(4818, -1006, 400, 70); spawn.mapRect(4768, -1626, 800, 70); spawn.mapRect(4760, -1626, 70, 400); - spawn.mapRect(645.1, -1480.8, 700, 100); //room for shielding boss + spawn.mapRect(645.1, -1480.8, 700, 100);//room for shielding boss spawn.mapVertex(515, -1447, "0 0 0 100 -400 0"); spawn.mapRect(1245.1, -1980.8, 100, 500); spawn.mapRect(2346.9, -1658.8, 469, 77); @@ -16257,6 +16369,7 @@ const level = { spawn.mapRect(6237, -1880.7, 400, 800); spawn.mapRect(6237, -890.4, 400, 800); + //first ship blocks/debris spawn.debris(3267.6, -797.1, 700, 5); //16 debris per level spawn.debris(1626.0, -372.5, 1700, 8); //16 debris per level @@ -16265,8 +16378,43 @@ const level = { spawn.debris(1563.8, -1087.9, 700, 5); //16 debris per level spawn.bodyRect(1540, -1110, 218, 125, 0.9); + + //first ship mobs spawn.randomSmallMob(893.5, -120.8); + + // spawn.randomMob(2903.9, -754.5, 0.4); + // spawn.randomMob(5577.0, -217.0, 0.2); + // spawn.randomMob(765.8, -1029.7, 0.5); + // spawn.randomMob(2680.1, -1779.2, 0.6); + // spawn.randomMob(20079.4, -2219.7, 0.4); + // spawn.randomMob(3924.9, -1504.1, 0.5); + // spawn.randomMob(21284.2, -983.1, 0.3); + // spawn.randomMob(20381.0, -254.2, 0.5); + // spawn.randomMob(18375.6, -1574.4, 0.6); + // spawn.randomMob(19448.2, -1323.3, 0.3); + // spawn.randomMob(18397.7, -711.2, 0.3); + // spawn.randomMob(15547.2, -2249.6, 0.5); + // spawn.randomSmallMob(16114.6, -2524.2); + // spawn.randomSmallMob(15378.9, -2549.6); + + // spawn.randomSmallMob(3266.4, -1578.4); + // spawn.randomSmallMob(4386.2, -439.6); + // spawn.randomSmallMob(5667.0, -847.8); + // spawn.randomSmallMob(3158.5, -1581.8); + // spawn.randomSmallMob(3866.7, -1483.2); + // spawn.randomSmallMob(4652.3, -1729.4); + // spawn.randomSmallMob(1068.7, -106.1); + // spawn.randomSmallMob(3382.5, -1590.6);//3545.0, -413.0 + // spawn.randomSmallMob(5099.7, -1204.2); + // spawn.randomSmallMob(1456.4, -1014.8); + // spawn.randomSmallMob(20432.4, -1374.3); + // spawn.randomSmallMob(20381.0, -254.2); + // spawn.randomSmallMob(3505.1, -1531.1); + // spawn.randomSmallMob(20648.1, -136.8); + // spawn.randomSmallMob(17502.8, -1520.6); + // spawn.randomSmallMob(17438.7, -876.7); + spawn.randomMob(18375.6, -1574.4, 0.2); spawn.randomSmallMob(15378.9, -2549.6); spawn.randomSmallMob(5820.2, -1545.2); @@ -16297,8 +16445,13 @@ const level = { spawn.randomSmallMob(4652.3, -1729.4); spawn.randomMob(18397.7, -711.2, 0.3); spawn.randomSmallMob(4386.2, -439.6); + spawn.randomSmallMob(3505.1, -1531.1); spawn.randomSmallMob(3866.7, -1483.2); + + + + //second ship mobs spawn.debris(17732.3, -550.0, 700, 5); //16 debris per level spawn.debris(17827.2, -2357.1, 700, 5); //16 debris per level @@ -16306,32 +16459,44 @@ const level = { spawn.debris(20823.6, -1332.1, 1300, 5); //16 debris per level spawn.debris(21095.5, -423.4, 700, 5); //16 debris per level spawn.debris(20534.5, -1282.1, 700, 5); //16 debris per level + + + + + + + spawn.randomSmallMob(1300, -70); spawn.shieldingBoss(943.9, -1698.0) + + //second ship base - spawn.mapRect(15000, 0, 515, 185); //lower floor 1 - spawn.mapRect(17015, 0, 5500, 185); //lower floor 2 - spawn.mapRect(15000, -2972, 185, 2972); //left wall - spawn.mapRect(15000, -2972, 7515, 185); //roof - spawn.mapRect(22330, -2972, 185, 2972); //right wall - spawn.mapRect(17002, -2972, 169, 2564); //left middle wall - spawn.mapRect(19089, -2972, 169, 855); //right middle wall upper - spawn.mapRect(19089, -1625, 169, 1800); //right middle wall lower - spawn.mapRect(20760, -2972, 169, 1350); //medium wall left of portal - spawn.mapRect(19720, -1625, 1725, 162); //right room upper floor - spawn.mapRect(21440, -2325, 169, 863); //medium wall right of portal - spawn.mapRect(19720, -855, 2725, 162); //right room lower floor + spawn.mapRect(15000, 0, 515, 185);//lower floor 1 + spawn.mapRect(17015, 0, 5500, 185);//lower floor 2 + spawn.mapRect(15000, -2972, 185, 2972);//left wall + spawn.mapRect(15000, -2972, 7515, 185);//roof + spawn.mapRect(22330, -2972, 185, 2972);//right wall + spawn.mapRect(17002, -2972, 169, 2564);//left middle wall + spawn.mapRect(19089, -2972, 169, 855);//right middle wall upper + spawn.mapRect(19089, -1625, 169, 1800);//right middle wall lower + spawn.mapRect(20760, -2972, 169, 1350);//medium wall left of portal + spawn.mapRect(19720, -1625, 1725, 162);//right room upper floor + spawn.mapRect(21440, -2325, 169, 863);//medium wall right of portal + spawn.mapRect(19720, -855, 2725, 162);//right room lower floor //engines //y -2972 -> 0 spawn.mapRect(22330, -2763.75, 400, 800); spawn.mapRect(22330, -1793.5, 400, 800); spawn.mapRect(22330, -804.25, 400, 800); + + + //second ship details - spawn.mapRect(19904, -1465, 85, 362); //upper L - spawn.mapRect(19542, -1191, 412, 88); //lower L - spawn.mapRect(18546, -2199, 600, 82); //2nd room enternce wall + spawn.mapRect(19904, -1465, 85, 362);//upper L + spawn.mapRect(19542, -1191, 412, 88);//lower L + spawn.mapRect(18546, -2199, 600, 82);//2nd room enternce wall spawn.mapRect(18546, -2499, 82, 2300); - spawn.mapRect(18108, -326, 500, 82); //walls/floors in middle room + spawn.mapRect(18108, -326, 500, 82);//walls/floors in middle room spawn.mapRect(17750, -682, 300, 82); spawn.mapRect(17156, -468, 500, 60); spawn.mapRect(18022, -1082, 600, 82); @@ -16345,7 +16510,7 @@ const level = { spawn.mapRect(20238, -330.2, 88, 412); spawn.mapRect(20819, -328.3, 412, 88); spawn.mapRect(21532, -708, 88, 412); - spawn.mapRect(15483.8, 12.5, 388, 30); //broken floor + spawn.mapRect(15483.8, 12.5, 388, 30);//broken floor spawn.mapRect(15487.6, 76.6, 488, 24); spawn.mapRect(15506.5, 134.2, 288, 45); spawn.mapVertex(16758.6, 135.3, "400 -30 -350 -40 -400 30 400 30"); @@ -16361,18 +16526,27 @@ const level = { spawn.mapVertex(17626.3, -3035, "-245 0 -220 -110 -173 -173 -110 -220 0 -250 110 -220 173 -173 220 -110 245 0"); spawn.mapRect(17226.3, -3035, 400, 40); + spawn.mapVertex(17626.3, 225, "-245 0 -220 110 -173 173 -110 220 0 250 110 220 173 173 220 110 245 0"); spawn.mapRect(17226.3, 225, 400, 40); + spawn.mapVertex(19626.3, -3035, "-245 0 -220 -110 -173 -173 -110 -220 0 -250 110 -220 173 -173 220 -110 245 0"); spawn.mapRect(19226.3, -3035, 400, 40); + spawn.mapVertex(19626.3, 225, "-245 0 -220 110 -173 173 -110 220 0 250 110 220 173 173 220 110 245 0"); spawn.mapRect(19226.3, 225, 400, 40); + spawn.mapVertex(21626.3, -3035, "-245 0 -220 -110 -173 -173 -110 -220 0 -250 110 -220 173 -173 220 -110 245 0"); spawn.mapRect(21226.3, -3035, 400, 40); + spawn.mapVertex(21626.3, 225, "-245 0 -220 110 -173 173 -110 220 0 250 110 220 173 173 220 110 245 0"); spawn.mapRect(21226.3, 225, 400, 40); + + //add fuel tanks in the last room + + spawn.mapRect(21531.9, -707.8, 488, 8); //22185.5, -114.8 @@ -16388,13 +16562,17 @@ const level = { //the parts in quotes is "x y x y x y x y x y" x and y need to be the coordinates of points that define the shape in a concave clockwise direction //second ship blocks/debris - spawn.bodyRect(21525, -113, 50, 50, 9); //first button block - spawn.bodyRect(18993, -2283, 50, 50, 9); //second button block - spawn.bodyRect(20303, -1736, 50, 50, 9); //third button block + spawn.bodyRect(21525, -113, 50, 50, 9);//first button block + spawn.bodyRect(18993, -2283, 50, 50, 9);//second button block + spawn.bodyRect(20303, -1736, 50, 50, 9);//third button block - let randomBoss = Math.floor(Math.random() * 5); //change the bosses + + + let randomBoss = Math.floor(Math.random() * 5);//change the bosses spawn[["blinkBoss", "shooterBoss", "launcherBoss", "pulsarBoss", "beetleBoss", "bladeBoss", "revolutionBoss", "dragonFlyBoss", "spiderBoss"][randomBoss]](17902, -1689, 100, false); + + // powerUps.spawnStartingPowerUps(1475, -1175); // spawn.debris(750, -2200, 3700, 16); //16 debris per level // spawn.bodyRect(1540, -1110, 300, 25, 0.9); @@ -17726,6 +17904,867 @@ const level = { lastBlock = Math.sin(m.cycle / 50) * 0.3 < 0; } }, + buttonbutton() { + simulation.makeTextLog(`buttonbutton by ||Destabilized E||`); + const mover = level.mover(1425, -1949, 600, 25); //x,y,width.height,VxGoal,force + + let portal + portal = level.portal({ + x: -146, + y: 131 + }, 2 * Math.PI, { + x: 1805, + y: -2295 + }, 90) + + const button = level.button(-456, -1320) + spawn.bodyRect(-400, -1475, 75, 75); + const button2 = level.button(1781, -61) + spawn.bodyRect(1781, (-61) - 100, 75, 75); + const boost1 = level.boost(1366, -1942, 1300) + + button.isUp = true + button2.isUp = true + + const train = level.transport(-250, 1151, 400, 50, 8 + simulation.difficultyMode) + level.custom = () => { + if (train.position.x < -244) { + train.changeDirection(true) //go right + } else if (train.position.x > 1700) { + train.changeDirection(false) //go left + } + if (button.isUp && button2.isUp) train.move(); + mover.push(); + ctx.fillStyle = "rgba(0,255,255,0.1)"; + ctx.fillRect(6400, -550, 300, 350); + level.exit.drawAndCheck(); + level.enter.draw(); + }; + level.customTopLayer = () => { + button.query(); + button.draw(); + button2.query(); + button2.draw(); + boost1.query(); + train.draw() + portal[2].query() + portal[3].query() + portal[0].draw(); + portal[1].draw(); + mover.draw(); + ctx.fillStyle = "rgba(0,0,0,0.1)" + ctx.fillRect(-150, -650, 900, 250) + }; + level.setPosToSpawn(0, -450); //normal spawn + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); + level.exit.x = -525; + level.exit.y = 1128; + level.defaultZoom = 1500 + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#ddd"; + spawn.mapRect(-725, -1325, 575, 1900); + spawn.mapRect(1425, -1925, 600, 1550); + spawn.mapRect(1450, -50, 500, 425); + spawn.mapRect(1950, 75, 325, 300); + spawn.mapRect(2275, 200, 200, 175); + spawn.mapRect(-150, -400, 900, 250); + spawn.mapRect(-150, 300, 900, 275); + spawn.mapRect(1700, 900, 450, 275); + spawn.mapRect(1800, 1600, 450, 250); + spawn.mapRect(1675, 1675, 275, 175); + spawn.mapRect(1575, 1675, 275, 175); + spawn.mapRect(-550, 1150, 150, 100); + spawn.bodyRect(-1475, -225, 50, 50); + spawn.bodyRect(2450, 1525, 925, 850); + spawn.mapRect(2275, 1400, 300, 150); + spawn.mapRect(2125, 1025, 125, 150); + spawn.mapRect(2250, 1175, 175, 75); + spawn.mapRect(2150, 1175, 175, 75); + spawn.mapRect(1725, 1150, 475, 100); + spawn.mapRect(2225, 675, 650, 50); + spawn.bodyRect(2400, 500, 150, 175); + spawn.nodeGroup(326, 85, "grenadier", 6) + spawn.mapRect(-225, -1325, 625, 225); + + spawn.randomMob(151, -1500) + spawn.randomMob(-88, -1829) + spawn.randomMob(2339, 896) + + + spawn.randomMob(1907, 1381) + spawn.randomMob(2398, 1301) + spawn.randomMob(1839, 811) + + + spawn.randomMob(2282, 1103) + spawn.randomMob(8, 124) + spawn.randomMob(629, 111) + + spawn.randomMob(43, 831) + spawn.randomMob(168, 1002) + spawn.randomMob(2956, 1006) + + spawn.randomMob(2713, 535) + spawn.randomMob(2396, 117) + spawn.randomMob(1498, -121) + + spawn.nodeGroup(2030, -16, "grower", 6) + spawn.randomLevelBoss(1840, 675) + }, + movers() { + simulation.makeTextLog(`movers by ryanbear`); + level.custom = () => { + level.exit.drawAndCheck(); + level.enter.draw(); + for (var i = 0; i < trains.length; i++) { + //oscillate back and forth + if (trains[i].position.x < 5075) { + trains[i].changeDirection(true) //go right + } else if (trains[i].position.x > 7875) { + trains[i].changeDirection(false) //go left + } + trains[i].draw(); + trains[i].move(); + } + for (var j = 0; j < zzz.length; j++) { + zzz[j][0].query(); + } + mvr.push(); + v3.query(); + ctx.fillStyle = "rgba(68,68,68,1)"; + ctx.fillRect(1725, -2400, 1000, 150); + ctx.fillRect(2175, -2775, 250, 450); + ctx.fillRect(2200, -2825, 225, 200); + ctx.fillRect(2075, -2575, 150, 200); + ctx.fillRect(2075, -2700, 150, 150); + ctx.fillRect(1875, -2525, 300, 125); + ctx.fillRect(1975, -2575, 150, 75); + ctx.fillRect(1800, -2475, 175, 100); + ctx.fillRect(2150, -2725, 350, 375); + ctx.fillRect(2475, -2575, 175, 200); + ctx.fillRect(2675, -2550, 25, 175); + ctx.fillRect(2625, -2550, 75, 200); + ctx.fillRect(2025, -2600, 200, 175); + ctx.fillRect(2025, -2675, 225, 225); + ctx.fillRect(2125, -2800, 250, 375); + ctx.fillRect(2400, -2625, 175, 175); + ctx.fillRect(2450, -2700, 100, 225); + ctx.fillRect(1950, -2600, 150, 200); + ctx.fillRect(1675, -2325, 250, 75); + ctx.fillRect(2700, -2525, 25, 150); + }; + simulation.enableConstructMode() + level.setPosToSpawn(0, -50); //normal spawn + level.exit.x = 23885; + level.exit.y = 800; + spawn.mapRect(-98, -8, 1000, 20); //bump for level entrance + spawn.mapRect(972, -287, 200, 20); //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { + level.defaultZoom = 1800 + simulation.zoomTransition(level.defaultZoom) + var trains = []; + var zzz = []; + spawn.mapRect(9850, 475, 200, 75); + for (var i = 0; i < 6; i++) { + trains.push(level.transport(6275, -2100 + 525 * i, 600, 50, (2 * i % 2 - 1) * 4 * Math.min(simulation.difficulty / 2, 2) * (1 + Math.random()))) + zzz.push([level.boost(6275, -2100 + 525 * i, 100), 6275]); + } + document.body.style.backgroundColor = "#d8dadf"; + + const portal1 = level.portal({ + x: 3984, + y: 1293 + }, -2 * Math.PI, { //right + x: 23863, + y: 82 + }, 2 * Math.PI) //right + + spawn.mapRect(1825, -2250, 3300, 300); spawn.mapRect(3250, -2875, 150, 625); + spawn.mapRect(3250, -2875, 425, 125); + spawn.mapRect(3425, -2725, 150, 300); + spawn.mapRect(3400, -2750, 175, 350); + spawn.mapRect(3575, -2625, 150, 375); + spawn.mapRect(3175, -2750, 75, 300); + spawn.mapRect(3100, -2750, 275, 300); + spawn.mapRect(3675, -2875, 75, 125); + spawn.mapRect(3675, -2625, 75, 400); + spawn.mapRect(3350, -2425, 100, 175); + spawn.mapRect(8350, 825, 1825, 250); + spawn.mapRect(3950, 800, 800, 375); + var hzd = level.hazard(3750, -2625, 1375, 375); + // spawn.mapRect(3750, -2625, 1375, 375); + var v1 = level.vanish(3975, -2600, 225, 25); + var v2 = level.vanish(4275, -2975, 225, 25); + var mvr = level.mover(2585, 1928, 2375, 100); + //spawn.mapRect(4925, 1725, 300, 25); + var v3 = level.vanish(4925, 1725, 100, 25); + for (var i = 0; i < 16; i++) { + if (i < 10) { + level.boost(1600 + 62 * i, -2307 - 62 * i, 100); + } + else { + level.boost(1600 + 62 * i, -2307 - 62 * 20 + 62 * i, 100); + } + } + + + for (var i = -1; i < 10; i++) { + level.boost(3847 - 62 * i, 879 + 62 * i, 100); + } + spawn.mapRect(3050, 1500, 1600, 200); + spawn.mapRect(1850, -1950, 3275, 1275); spawn.mapRect(1850, -675, 3275, 1300); + + spawn.mapRect(2700, -2525, 25, 175); + spawn.mapRect(3825, 925, 125, 575); spawn.mapRect(3600, 1100, 350, 400); spawn.mapRect(3375, 1350, 275, 150); spawn.mapRect(3550, 1300, 100, 50); spawn.mapRect(3800, 1000, 100, 150); spawn.mapRect(3725, 1075, 150, 125); spawn.mapRect(3725, 1025, 150, 125); spawn.mapRect(3550, 1225, 150, 125); spawn.mapRect(3500, 1275, 175, 125); + // color.map = "#444" //custom map color + bosses = ["laserBoss", "blinkBoss", "shooterBoss", "launcherBoss", "pulsarBoss", "beetleBoss", "bladeBoss", "revolutionBoss", "dragonFlyBoss", "spiderBoss"]; + let randomBoss = Math.floor(Math.random() * bosses.length); + spawn[bosses[randomBoss]](2240, -2499, 100, false); + var btn = level.button(9889, 747); + btn.isUp = true; + spawn.randomMob(475, -725, 0.7); spawn.randomMob(825, -1825, 0.7); spawn.randomMob(3275, -3475, 0.7); spawn.randomMob(8550, 350, 0.7); spawn.randomMob(9350, -175, 0.7); spawn.randomMob(1575, 225, 0.7); spawn.randomMob(22825, 250, 0.7); + spawn.mapRect(-100, 0, 1000, 100); + var ddd = level.elevator(1326, -447, 200, 200, -2131, 0.003, { up: 0.1, down: 0.2 }); + /// transport(x, y, width, height, VxGoal = -6, force = VxGoal > 0 ? 0.0005 : -0.0005) { + spawn.mapRect(9500, 750, 675, 75); + spawn.mapRect(22350, 825, 3000, 150); + powerUps.spawn(4246, 1335, "tech") + powerUps.spawn(4246.8, 1335, "heal") + powerUps.spawn(4246.8, 1335.4, "ammo") + spawn.bodyRect(9200, 725, 50, 25); spawn.mapRect(12200, 675, 125, 50); spawn.mapRect(12925, 675, 100, 100); spawn.mapRect(13675, 650, 150, 150); spawn.mapRect(14200, 750, 25, 25); spawn.mapRect(14200, 675, 25, 75); spawn.mapRect(14550, 675, 125, 50); spawn.mapRect(15850, 675, 125, 100); spawn.mapRect(17175, 600, 25, 200); spawn.mapRect(17725, 700, 175, 50); spawn.mapRect(18775, 675, 175, 75); + spawn.bodyRect(8975, 700, 25, 25); spawn.bodyRect(8850, 575, 50, 50); spawn.bodyRect(9050, 650, 50, 50); spawn.bodyRect(8625, 575, 100, 75); spawn.bodyRect(8475, 675, 75, 25); + var train1 = level.transport(10250 - 700, 775, Math.max(1200 / simulation.difficulty, 200), 1350, 8); + level.customTopLayer = () => { + ddd.move(); + hzd.query(); + v1.query(); + v2.query(); + btn.draw(); + portal1[2].query(); + portal1[2].draw(); + portal1[3].query(); + portal1[3].draw(); + btn.query(); + if (!btn.isUp) { + spawn.mapRect(4050, 1175, 600, 325); + } + if (!btn.isUp && train1.position.x < 23785) { + train1.draw(); + train1.move(); + } + // if (trains[i].position.x < 5075) { + // trains[i].changeDirection(true) //go right + }; + // powerUps.spawnStartingPowerUps(1475, -1175); + // spawn.debris(750, -2200, 3700, 16); //16 debris per level + // spawn.bodyRect(1540, -1110, 300, 25, 0.9); + // spawn.randomSmallMob(1300, -70); + // spawn.randomMob(2650, -975, 0.8); + // spawn.randomGroup(1700, -900, 0.4); + // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); + // spawn.secondaryBossChance(100, -1500) + powerUps.addResearchToLevel() //needs to run after mobs are spawned + }, + downpour() { + simulation.makeTextLog(`Downpour by DesBoot`); + let mobsspawned = 0 + const laser = level.hazard(7492, -2612, 10, 500, 0.3) //laserintro + + //5381, -3268, 10, 0.4 + spawn.mapRect(340, -2032.5, 20, 25); //laser nose //laserintro + const laserbutton = level.button(5485, -2510) + const doorbutton = level.button(7618, -3204) + const doortoggle = level.toggle(5088.4, 1226.7) + const door = level.door(6500, -1200, 100, 350, 100) + const bunkerdoor = level.door(10700, -2500, 100, 500, 200) + + const boost1 = level.boost(7300, 1209, 2200) + const boost2 = level.boost(6232.6, -832.8, 1400) + const portal = level.portal({ x: 4886.4, y: 1050.7 }, 2 * Math.PI, { x: 7686, y: -2121 }, 2 * Math.PI) + //let portal + const slime = level.hazard(-1800, 10, 4200, 400); + const slime2 = level.hazard(2400, -2100, 200, 2100); + const slime3 = level.hazard(2600, -2100, 3600, 200); + const slime4 = level.hazard(6400, -2100, 3600, 200); + simulation.enableConstructMode() + level.setPosToSpawn(0, -50); //normal spawn + level.exit.x = 13130.3; + let rainCount = 1 + level.exit.y = -370; + spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance + spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit + level.defaultZoom = 1800 + let stopcycle = 0 + let flashcycle = Math.round(Math.random() * 25 + 260) + simulation.zoomTransition(level.defaultZoom) + document.body.style.backgroundColor = "#2e416e";//d8dadf + // color.map = "#444" //custom map color + + + //simulation.makeTextLog(stopcycle) + level.custom = () => { + do { + + ctx.beginPath() + // ctx.fillStyle = "rgba(30,150,117,255)" + ctx.rect(Math.random() * 4500 - 2000, -5000, Math.random() * 3 + 2.5, 5000) + ctx.rect(Math.random() * 4500 - 2000, -5000, Math.random() * 3 + 2.5, 5000) + ctx.rect(Math.random() * 4500 - 2000, -5000, Math.random() * 3 + 2.5, 5000) + ctx.rect(Math.random() * 2000 + 2500, -5000, Math.random() * 3 + 2.5, 3000) + ctx.rect(Math.random() * 2000 + 2500, -5000, Math.random() * 3 + 2.5, 3000) + ctx.rect(Math.random() * 1300 + 4500, -5000, Math.random() * 3 + 2.5, 2500) + ctx.rect(Math.random() * 1300 + 7500, -5000, Math.random() * 3 + 2.5, 1800) + ctx.rect(Math.random() * 1800 + 5700, -5000, Math.random() * 3 + 2.5, 3000) + ctx.rect(Math.random() * 1800 + 5700, -5000, Math.random() * 3 + 2.5, 3000) + ctx.rect(Math.random() * 1800 + 8400, -5000, Math.random() * 3 + 2.5, 3000) + ctx.rect(Math.random() * 1800 + 8400, -5000, Math.random() * 3 + 2.5, 3000) + ctx.rect(Math.random() * 4500 - 2000, -5000, Math.random() * 3 + 2.5, 5000) + ctx.rect(Math.random() * 1800 + 10200, -5000, Math.random() * 3 + 2.5, 3000) + ctx.rect(Math.random() * 1800 + 10200, -5000, Math.random() * 3 + 2.5, 3000) + ctx.rect(Math.random() * 1800 + 12000, -5000, Math.random() * 3 + 2.5, 3000) + ctx.rect(Math.random() * 1800 + 12000, -5000, Math.random() * 3 + 2.5, 3000) + ctx.fillStyle = "rgba(30,150,117,255)" + ctx.fill() + // } + // if (rainCount > 12) { + // rainCount = 1 + // simulation.makeTextLog(rainCount) + + // } else { + // rainCount = rainCount + 1 + // simulation.makeTextLog(rainCount) + // } + } while (Math.random() < 0.8); + //simulation.makeTextLog(stopcycle) + //simulation.makeTextLog(m.cycle) + // ctx.fillStyle = "rgba(228,255,0,0.8)" + // //simulation.makeTextLog(stopcycle) + // ctx.fillRect(50.4, -1210.0, 100, 100) + // stopcycle = m.cycle + Math.random * 600; + //stopcycle = m.cycles + Math.random * 600 + + if (stopcycle > 300) { + stopcycle = 0 + flashcycle = Math.round(Math.random() * 25 + 260) + document.body.style.backgroundColor = "#2e416e"; + } else { + if (stopcycle > flashcycle) { + document.body.style.backgroundColor = "#7391ff"; + for (let i = 0; i < mob.length; i++) mobs.statusStun(mob[i], Math.random() * 20 + 30) + } + stopcycle = stopcycle + 1 + } + + ctx.fillStyle = "#d4f4f4" + ctx.fillRect(12984, -704, 420, 450) + ctx.fillStyle = "rgba(0,0,0,0.5)" + ctx.fillRect(4703, -2362, 100, 100) + ctx.fillRect(5053, -2362, 100, 100) + ctx.fillRect(5403, -2362, 100, 100) + ctx.fillRect(4703, -2062, 100, 100) + ctx.fillRect(5053, -2062, 100, 100) + ctx.fillRect(5403, -2062, 100, 100) + ctx.fillRect(4523, -2512, 1150, 800) + ctx.fillRect(4735, -1233, 100, 500)//tree + ctx.beginPath() + ctx.moveTo(4487, -1195)//slope of -1/3 + ctx.lineTo(4736, -792) + ctx.lineTo(4736, -852) + ctx.lineTo(4527, -1195) + + + ctx.moveTo(5087, -1195)//slope of -1/3 + ctx.lineTo(4836, -792) + ctx.lineTo(4836, -852) + ctx.lineTo(5047, -1195) + ctx.fill() + ctx.moveTo(5252.4, -2483.5) + ctx.lineTo(5141.2, -2507.8) + ctx.lineTo(5209.2, -2625.2) + ctx.lineTo(5290.2, -2626.6) + + ctx.lineTo(5361.2, -2697.9) + ctx.lineTo(5410.6, -2717.0) + + ctx.lineTo(5680.2, -2648.7) + ctx.lineTo(5687.7, -2471.5) + + ctx.fill() + + + + //building 2 spawn.mapRect(8473, -2513, 50, 50); + ctx.fillRect(8673, -2137, 50, 175) + + ctx.fillRect(7630, -2540, 100, 100) + ctx.fillRect(7930, -2540, 100, 100) + ctx.fillRect(8230, -2540, 100, 100) + + ctx.fillRect(8530, -2765, 100, 100) + + ctx.fillRect(7630, -2990, 100, 100) + ctx.fillRect(7930, -2990, 100, 100) + + ctx.fillRect(8230, -2990, 100, 100) + + + ctx.beginPath() + ctx.moveTo(7475, -3213) + ctx.lineTo(8100, -3213) + ctx.lineTo(8191.2, -3334.7) + ctx.lineTo(8318.0, -3388.3) + ctx.lineTo(8348.5, -3496.9) + ctx.lineTo(8480.0, -3512.6) + ctx.lineTo(8670, -3482) + ctx.lineTo(8725, -3213) + ctx.lineTo(8725, -1463) + ctx.lineTo(7475, -1463) + ctx.fill() + + + + //stairs spawn.mapRect(7523, -2313, 800, 75); + ctx.fillRect(8523, -2563, 50, 50) + ctx.fillRect(8473, -2613, 50, 50) + ctx.fillRect(8423, -2663, 50, 50) + ctx.fillRect(8373, -2713, 50, 50) + ctx.fillRect(8323, -2763, 50, 50) + + ctx.fillRect(8323, -2813, 50, 50) + ctx.fillRect(8373, -2863, 50, 50) + ctx.fillRect(8423, -2913, 50, 50) + ctx.fillRect(8473, -2963, 50, 50) + + ctx.fillRect(8523, -3013, 50, 50)//make block + ctx.fillRect(8473, -3063, 50, 50)//make block + ctx.fillRect(8423, -3113, 50, 50)//make block + ctx.fillRect(8373, -3163, 50, 50) + ctx.fillRect(8323, -3213, 50, 50) + + //caves + + ctx.fillStyle = "rgba(30,150,117,255)"//fake slime + //87,189,146,255 + ctx.fillRect(6100, -1900, 100, 1050) + ctx.fillRect(6400, -1900, 100, 1050) + ctx.fillRect(2600, -850, 4700, 200) + ctx.fillRect(7200, -650, 100, 1900) + ctx.fillRect(2399, -1, 200, 400) + + //bunker + ctx.fillStyle = "rgba(0,0,0,0.5)" + + ctx.beginPath() + ctx.moveTo(10800, -2400)//slope of -1/3 + ctx.lineTo(10800, -340) + ctx.lineTo(12980, -340) + ctx.lineTo(12980, -700) + ctx.lineTo(13465, -700) + ctx.lineTo(13541, -1737) + ctx.lineTo(11864.6, -1967.0) + ctx.lineTo(11003, -2400) + ctx.fill() + ctx.fillRect(6100, -2000, 400, 50) + + + + + + + + + + + + + + + // -2000 -> 2500 + // Math.random() * 5000 -2500 + ctx.fillStyle = "rgba(0,0,0,0.6)" + ctx.beginPath() + ctx.moveTo(6100, -1700) + ctx.lineTo(5799.5, -800) + ctx.lineTo(2600, -800) + ctx.lineTo(2600, -1700) + ctx.lineTo(5799.5, -1700) + + ctx.moveTo(6500, -1200) + ctx.lineTo(7600, -1200) + ctx.lineTo(8000, 1400) + ctx.lineTo(4600, 1500) + ctx.lineTo(4500.5, 0) + ctx.lineTo(6500, -200) + ctx.lineTo(6500, -1200) + ctx.fill() + + + + + portal[2].query() + portal[3].query() + if (laserbutton.isUp) { + laser.isOn = true; + } else { + laser.isOn = false; + } + + + ctx.fillStyle = "rgba(0,0,0,0.6)" + ctx.fillRect(2113, -791, 500, 75) + ctx.fillRect(1766, -1091, 250, 310) + ctx.fillRect(4473, -2912, 50, 1000) + ctx.fillRect(5673, -2712, 50, 800) + ctx.fillStyle = "rgba(0,0,0,0.2)" + + ctx.fillRect(4523, -2512, 350, 75) + ctx.fillRect(5273, -2212, 400, 75) + + + level.exit.drawAndCheck(); + slime.query(); + slime2.query(); + slime3.query(); + slime4.query(); + spawn.mapRect(4873, -2512, 800, 75); + spawn.mapRect(4473, -2212, 800, 75); + //setTimeout(function(){/*YourCode*/},1000); + + //water falling/flowing effect + ctx.fillStyle = `hsla(160, 100%, 26%,${0.5 + 0.07 * Math.random()})`//lower river + ctx.fillRect(-1800 + Math.random() * 100, 10 + 400 * Math.random(), 3900, 5) + ctx.fillRect(-1800, 10 + 400 * Math.random(), 4400, 5) + + ctx.fillRect(2400 + 200 * Math.random(), Math.random() * - 100 - 2000, 5, 2000)//first waterfall + ctx.fillRect(6100 + 100 * Math.random(), Math.random() * - 100 - 1900, 5, 1050)//twin waterfalls + ctx.fillRect(6400 + 100 * Math.random(), Math.random() * - 100 - 1900, 5, 1050) + + ctx.fillRect(7200 + 100 * Math.random(), -800 - 50 * Math.random(), 5, 2032) + level.enter.draw(); + laserbutton.query(); + laserbutton.draw(); + doortoggle.query(); + if (!doortoggle.isOn) { + door.isClosing = true + bunkerdoor.isClosing = true + + } else { + door.isClosing = false + bunkerdoor.isClosing = false + if (mobsspawned == 0) { + spawn.randomSmallMob(6128.0, 822.6); + spawn.randomSmallMob(6854.8, 560.2); + spawn.randomSmallMob(8320.7, -3402.4); + spawn.randomMob(6629.0, 711.3, 0.8); + spawn.randomMob(8199.2, -2545.5, 0.8); + spawn.randomMob(8067.7, -2957.2, 0.8); + spawn.randomMob(5149.6, -1444.1, 0.8); + + mobsspawned = 1 + + } + + } + door.openClose(); + bunkerdoor.openClose(); + + }; + level.customTopLayer = () => { + door.draw(); + bunkerdoor.draw(); + + laser.opticalQuery(); + if (player.position.y > -70 && player.position.x < 2785) { + if (m.onGround) { + Matter.Body.setVelocity(player, { + x: player.velocity.x - (2 + m.pos.y / 150), + y: player.velocity.y + }); + } else { + Matter.Body.setVelocity(player, { + x: player.velocity.x - (1 + m.pos.y / 150), + y: player.velocity.y + }); + } + + } + if (player.position.x > 2400 && player.position.x < 2600) { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y + 4 + }); + + + } + boost1.query(); + boost2.query(); + if (player.position.x > 2600 && player.position.x < 4500 && player.position.y < -1900 && player.position.y > -2121.3) { + Matter.Body.setVelocity(player, { + x: player.velocity.x - 2, + y: player.velocity.y + }); + } + if (player.position.x > 4500 && player.position.x < 6000 && player.position.y < -1900 && player.position.y > -2121.3) { + + if (input.left) { + Matter.Body.setVelocity(player, { + x: player.velocity.x + 0.1, + y: player.velocity.y + }); + } else { + Matter.Body.setVelocity(player, { + x: player.velocity.x + 0.5, + y: player.velocity.y + }); + } + } + if (player.position.x > 6500 && player.position.x < 7500 && player.position.y < -1900 && player.position.y > -2121.3) { + Matter.Body.setVelocity(player, { + x: player.velocity.x - 1, + y: player.velocity.y + }); + } + if (player.position.x > 7500 && player.position.x < 10000 && player.position.y < -1900 && player.position.y > -2121.3) { + Matter.Body.setVelocity(player, { + x: player.velocity.x - 1, + y: player.velocity.y + }); + } + if (player.position.x > 2600 && player.position.x < 6100 && player.position.y < -650 && player.position.y > -920) { + if (input.right) { + Matter.Body.setVelocity(player, { + x: player.velocity.x - 0.2, + y: player.velocity.y + }); + } else { + Matter.Body.setVelocity(player, { + x: player.velocity.x - 0.4, + y: player.velocity.y + }); + } + } + if (player.position.x > 6500 && player.position.x < 7300 && player.position.y < -650 && player.position.y > -920 && m.onGround) { + if (input.left) { + Matter.Body.setVelocity(player, { + x: player.velocity.x + 0.2, + y: player.velocity.y + }); + } else { + Matter.Body.setVelocity(player, { + x: player.velocity.x + 0.4, + y: player.velocity.y + }); + } + } + if (player.position.x > 7200 && player.position.x < 7350 && player.position.y > -950 && player.position.y < 1250) { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y + 0.8 + }); + } + if (player.position.x > 6100 && player.position.x < 6200 && player.position.y < -800 && player.position.y > -2000) { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y + 0.3 + }); + } + if (player.position.x > 6400 && player.position.x < 6500 && player.position.y < -800 && player.position.y > -2000) { + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: player.velocity.y + 0.3 + }); + } + // ctx.fillRect(7200, -650, 100, 1900) + + portal[0].draw(); + portal[1].draw(); + portal[2].draw(); + portal[3].draw(); + + + }; + + spawn.mapRect(-100, 0, 1000, 100); + spawn.mapRect(-1800, 400, 4400, 1300); + spawn.mapRect(-1800, 0, 100, 400); + spawn.mapRect(2600, -2000, 3500, 300); + spawn.mapRect(2600, -2000, 500, 800); + spawn.mapRect(2955, -1779, 800, 300); + spawn.mapRect(2600, -800, 2300, 2500); + spawn.mapRect(-460, 100, 1570, 400); + spawn.mapVertex(965, 67, "0 -100 220 0 0 0"); + spawn.mapVertex(-185, 67, "0 -100 -420 0 0 0"); + spawn.mapVertex(1210, 365, "0 -400 300 0 0 0"); + spawn.mapRect(217.5, -358.5, 50, 360); + spawn.mapRect(-83, -358.5, 300, 50); + + //blocks in river/waterfall + + spawn.mapRect(1275, 0, 450, 75); + spawn.mapRect(2027, -388, 600, 75); + spawn.mapRect(1666, -791, 450, 75); + spawn.mapRect(1666, -1091, 450, 75); + //buildings + + spawn.mapRect(4873, -2512, 800, 75); + spawn.mapRect(4473, -2212, 800, 75); + spawn.mapRect(4473, -2912, 50, 800); + spawn.mapRect(5673, -2712, 50, 575); + + spawn.mapRect(6671.5, -2401.4, 500, 50); + spawn.mapRect(6105.1, -2354.1, 400, 50); + + spawn.mapRect(4473, -2952, 8, 75);//1,3,2 + spawn.mapRect(4493, -3032, 15, 150); + spawn.mapRect(4513, -2982, 7, 75); + + spawn.mapRect(5673, -2742, 12, 50); + spawn.mapRect(5703, -2772, 8, 100); + + + //building 2 + // ctx.fillRect(8323, -2363, 50, 50) + + spawn.mapRect(7473, -3412, 50, 800); + spawn.mapRect(7473, -2312, 50, 500); + spawn.mapRect(8673, -3212, 50, 1075); + spawn.mapRect(7523, -2313, 800, 75); + spawn.mapRect(7523, -2763, 800, 75); + spawn.mapRect(7523, -3213, 800, 75); + spawn.mapRect(8725, -2340, 400, 50); + spawn.mapRect(8925, -2640, 200, 50); + spawn.mapRect(8725, -2940, 200, 50); + + //stairs + spawn.mapRect(8323, -2363, 50, 50); + spawn.mapRect(8373, -2413, 50, 50); + spawn.mapRect(8423, -2463, 50, 50); + spawn.mapRect(8473, -2513, 250, 50); + //stairs 2 + spawn.mapRect(8523, -3013, 50, 50)//make block + spawn.mapRect(8473, -3063, 50, 50)//make block + spawn.mapRect(8423, -3113, 50, 50)//make block + //trees in tunnel + spawn.mapRect(4485, -1243, 600, 50) + spawn.mapRect(3967, -1056, 400, 50) + spawn.mapRect(5453, -1150, 50, 300) + spawn.mapRect(5453, -1700, 50, 300) + + + //tunnels and boss + spawn.mapRect(6500, -2000, 3100, 800); + spawn.mapRect(7500, -2000, 3300, 3700); + spawn.mapRect(4900, -800, 2300, 1000); + spawn.mapRect(4354, 1230, 4000, 470); + spawn.mapRect(5388, 863, 100, 500); + spawn.mapRect(5388, 63, 100, 500); + spawn.mapRect(5834, 549, 500, 80); + spawn.mapRect(6756, 897, 400, 80); + + + //extra boss + spawn.mapRect(9196, -11492, 500, 100); + spawn.mapRect(9196, -11492, 500, 100); + + //bunker + spawn.mapRect(11500, -2000, 1900, 500); + spawn.mapRect(10800, -900, 800, 2600); + spawn.mapRect(11600, -340, 1800, 2600); + spawn.mapRect(13400, -2000, 1800, 3600); + spawn.mapRect(10800, -2500, 200, 100); + spawn.mapVertex(11400, -2235, "0 10 900 510 800 510 750 510 0 110"); + + spawn.mapVertex(10100, -2000, "0 0 0 -250 400 0"); + spawn.mapRect(12945.0, -741.9, 600, 50); + spawn.mapRect(12945.0, -741.9, 50, 250); + //stairs + spawn.mapRect(11600, -850, 50, 550); + spawn.mapRect(11650, -800, 50, 500); + spawn.mapRect(11700, -750, 50, 450); + spawn.mapRect(11750, -700, 50, 400); + spawn.mapRect(11800, -650, 50, 350); + spawn.mapRect(11850, -600, 50, 300); + spawn.mapRect(11900, -550, 50, 250); + spawn.mapRect(11950, -500, 50, 200); + spawn.mapRect(12000, -450, 50, 150); + spawn.mapRect(12050, -400, 50, 100); + spawn.mapRect(12100, -350, 50, 50); + + + //mobs + //spawn.tetherBoss(6480, 992, { x: 6480, y: 210 }) + + if (Math.random() < 0.5) { + spawn.tetherBoss(6480, 992, { x: 6480, y: 210 }) + } else { + spawn.randomLevelBoss(5977, 992) + } + + + //mobs for waterfall and first cavern + //spawn.randomSmallMob(1999.2, -487.4); + spawn.randomMob(1999.2, -487.4, 0.8); + //spawn.randomSmallMob(2080.0, -1206.4); + spawn.randomMob(2080.0, -1206.4, 0.8); + spawn.randomSmallMob(3287.5, -1021.1); + //spawn.randomSmallMob(3992.2, -1223.9); + spawn.randomSmallMob(5018.1, -1483.5); + spawn.randomGroup(6776.2, -3054.5, 0.4); + spawn.randomGroup(4217.4, -1403.6, 0.4); + + + //surface area mobs + spawn.randomSmallMob(5089.0, -2284.1); + spawn.randomSmallMob(6988.3, -2580.2); + spawn.randomSmallMob(7975.0, -2920.3); + spawn.randomMob(5132.0, -2646.2, 0.8); + spawn.randomMob(6365.2, -2459.2, 0.8); + spawn.randomMob(8129.0, -2406.7, 0.8); + spawn.randomMob(8129.0, -2406.7, 0.8); + spawn.randomGroup(2225.3, -1543.2, 0.4); + + + spawn.debris(4426.9, -1433.8, 700, 1); //16 debris per level + spawn.debris(4651.2, -2597.3, 700, 1); //16 debris per level + spawn.debris(9920.9, -2378.3, 700, 2); //16 debris per level + spawn.debris(8298.5, -2883.8, 700, 1); //16 debris per level + spawn.debris(6779.2, -2662.9, 700, 1); //16 debris per level + spawn.debris(6371.5, 442.3, 700, 2); //16 debris per level + spawn.debris(1873.5, -1297.5, 700, 1); //16 debris per level + + spawn.bodyRect(6457.9, -2541.5, 300, 25, 0.9); + //spawn.bodyRect(5685, -2140, 25, 140, 0.9); + spawn.bodyRect(4473, -2110, 50, 110, 0.9); + //spawn.bodyRect(5292.1, -2617.2, 50, 50, 0.9); + spawn.bodyRect(6370.1, -2408.4, 50, 50, 0.9); + //spawn.bodyRect(5467, -1400, 25, 250, 0.9); + + spawn.bodyRect(4509.0, -1425.7, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9); + //spawn.bodyRect(8082.9, -2488.1, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9); + spawn.bodyRect(7859.6, -2883.6, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9); + //spawn.bodyRect(5609.5, 948.5, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9); + spawn.bodyRect(5803.7, 1125.5, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9); + //spawn.bodyRect(5492.1, 1061.7, 90, 169, 0.9); + spawn.bodyRect(5582.1, 1061.7, 110, 70, 0.9); + //spawn.bodyRect(5582.1, 961.7, 50, 30, 0.9); + + + + + // spawn.randomSmallMob(1300, -70); + // spawn.randomSmallMob(1300, -70); + // spawn.randomSmallMob(1300, -70); + // spawn.randomSmallMob(1300, -70); + + // powerUps.spawnStartingPowerUps(1475, -1175); + // spawn.debris(750, -2200, 3700, 16); //16 debris per level + // spawn.bodyRect(1540, -1110, 300, 25, 0.9); + // spawn.randomSmallMob(1300, -70); + // spawn.randomMob(2650, -975, 0.8); + // spawn.randomGroup(1700, -900, 0.4); + // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); + // spawn.secondaryBossChance(100, -1500) + powerUps.addResearchToLevel() //needs to run after mobs are spawned + }, // ******************************************************************************************************** // ******************************************************************************************************** // ***************************************** training levels ********************************************** @@ -17983,7 +19022,7 @@ const level = { spawn.mapRect(1600, -1200, 500, 850); //exit roof spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall }, - throw () { //throw a block on button to open door + throw() { //throw a block on button to open door m.addHealth(Infinity) level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player diff --git a/js/lore.js b/js/lore.js index bc9746e..c841ea4 100644 --- a/js/lore.js +++ b/js/lore.js @@ -2,15 +2,7 @@ const lore = { techCount: 0, techGoal: 7, setTechGoal() { - if (simulation.difficultyMode === 1) { - this.techGoal = 8 - } else if (simulation.difficultyMode === 2) { - this.techGoal = 5 - } else if (simulation.difficultyMode === 4) { - this.techGoal = 2 - } else if (simulation.difficultyMode === 6) { - this.techGoal = 1 - } + this.techGoal = Math.max(1, Math.floor(8 - 1.5 * simulation.difficultyMode)) }, talkingColor: "#dff", //set color of graphic on level.null isSpeech: false, @@ -55,8 +47,8 @@ const lore = { trainer: { color: "#f20", voice: undefined, - text: function(say) { - simulation.makeTextLog(`input.audio(${(Date.now()/1000).toFixed(0)} s): "${say}"`, Infinity); + text: function (say) { + simulation.makeTextLog(`input.audio(${(Date.now() / 1000).toFixed(0)} s): "${say}"`, Infinity); lore.talkingColor = this.color const utterance = new SpeechSynthesisUtterance(say); utterance.lang = "en-AU" //"en-IN"; //de-DE en-GB fr-FR en-US en-AU @@ -67,9 +59,9 @@ const lore = { anand: { color: "#e0c", voice: undefined, - text: function(say) { + text: function (say) { if (level.levels[level.onLevel] === undefined) { //only talk if on the lore level (which is undefined because it is popped out of the level.levels array) - simulation.makeTextLog(`input.audio(${(Date.now()/1000).toFixed(0)} s): "${say}"`, Infinity); + simulation.makeTextLog(`input.audio(${(Date.now() / 1000).toFixed(0)} s): "${say}"`, Infinity); lore.talkingColor = this.color if (lore.isSpeech) { const utterance = new SpeechSynthesisUtterance(say); @@ -101,9 +93,9 @@ const lore = { }, miriam: { color: "#f20", - text: function(say) { + text: function (say) { if (level.levels[level.onLevel] === undefined) { //only talk if on the lore level (which is undefined because it is popped out of the level.levels array) - simulation.makeTextLog(`input.audio(${(Date.now()/1000).toFixed(0)} s): "${say}"`, Infinity); + simulation.makeTextLog(`input.audio(${(Date.now() / 1000).toFixed(0)} s): "${say}"`, Infinity); lore.talkingColor = this.color if (lore.isSpeech) { utterance = new SpeechSynthesisUtterance(say); @@ -116,7 +108,7 @@ const lore = { lore.isSpeech = false lore.nextSentence() } - speechFrozen = setTimeout(function() { // speech frozen after 10 seconds of no end + speechFrozen = setTimeout(function () { // speech frozen after 10 seconds of no end console.log('speech frozen') lore.isSpeech = false lore.nextSentence() diff --git a/js/player.js b/js/player.js index 32dd3ac..06179af 100644 --- a/js/player.js +++ b/js/player.js @@ -193,7 +193,7 @@ const m = { lastGroundedPositionY: 0, // mouseZoom: 0, lookSmoothing: 0.07, //1 is instant jerky, 0.001 is slow smooth zoom, 0.07 is standard - look() {}, //set to lookDefault() + look() { }, //set to lookDefault() lookDefault() { //always on mouse look m.angle = Math.atan2( @@ -415,7 +415,7 @@ const m = { m.health = 1; // m.addHealth(1) - simulation.wipe = function() { //set wipe to have trails + simulation.wipe = function () { //set wipe to have trails ctx.fillStyle = "rgba(255,255,255,0)"; ctx.fillRect(0, 0, canvas.width, canvas.height); } @@ -425,8 +425,8 @@ const m = { m.switchWorlds() const swapPeriod = 1000 for (let i = 0, len = 5; i < len; i++) { - setTimeout(function() { - simulation.wipe = function() { //set wipe to have trails + setTimeout(function () { + simulation.wipe = function () { //set wipe to have trails ctx.fillStyle = "rgba(255,255,255,0)"; ctx.fillRect(0, 0, canvas.width, canvas.height); } @@ -436,14 +436,14 @@ const m = { simulation.isTextLogOpen = true; simulation.makeTextLog(`simulation.amplitude = 0.${len - i - 1}`, swapPeriod); simulation.isTextLogOpen = false; - simulation.wipe = function() { //set wipe to have trails + simulation.wipe = function () { //set wipe to have trails ctx.fillStyle = `rgba(255,255,255,${(i + 1) * (i + 1) * 0.006})`; ctx.fillRect(0, 0, canvas.width, canvas.height); } }, (i + 1) * swapPeriod); } - setTimeout(function() { - simulation.wipe = function() { //set wipe to normal + setTimeout(function () { + simulation.wipe = function () { //set wipe to normal ctx.clearRect(0, 0, canvas.width, canvas.height); } simulation.isTextLogOpen = true; @@ -461,7 +461,7 @@ const m = { document.getElementById("text-log").style.display = "none" document.getElementById("fade-out").style.opacity = 0.9; //slowly fade to 90% white on top of canvas // build.shareURL(false) - setTimeout(function() { + setTimeout(function () { Composite.clear(engine.world); Engine.clear(engine); simulation.splashReturn(); @@ -644,7 +644,7 @@ const m = { if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles let isDrawPlayer = true - const shortPause = function() { + const shortPause = function () { if (m.defaultFPSCycle < m.cycle) { //back to default values simulation.fpsCap = simulation.fpsCapDefault simulation.fpsInterval = 1000 / simulation.fpsCap; @@ -717,13 +717,13 @@ const m = { for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "heal", false); m.energy = m.maxEnergy if (m.immuneCycle < m.cycle + 300) m.immuneCycle = m.cycle + 300 //disable this.immuneCycle bonus seconds - simulation.wipe = function() { //set wipe to have trails + simulation.wipe = function () { //set wipe to have trails ctx.fillStyle = "rgba(255,255,255,0.03)"; ctx.fillRect(0, 0, canvas.width, canvas.height); } - setTimeout(function() { + setTimeout(function () { tech.maxDuplicationEvent() - simulation.wipe = function() { //set wipe to normal + simulation.wipe = function () { //set wipe to normal ctx.clearRect(0, 0, canvas.width, canvas.height); } }, 3000); @@ -746,13 +746,13 @@ const m = {
${powerUps.research.count}`) for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "heal", false); if (m.immuneCycle < m.cycle + 300) m.immuneCycle = m.cycle + 300 //disable this.immuneCycle bonus seconds - simulation.wipe = function() { //set wipe to have trails + simulation.wipe = function () { //set wipe to have trails ctx.fillStyle = "rgba(255,255,255,0.03)"; ctx.fillRect(0, 0, canvas.width, canvas.height); } - setTimeout(function() { + setTimeout(function () { tech.maxDuplicationEvent() - simulation.wipe = function() { //set wipe to normal + simulation.wipe = function () { //set wipe to normal ctx.clearRect(0, 0, canvas.width, canvas.height); } }, 3000); @@ -772,7 +772,7 @@ const m = { if (dmg > 0.06 / m.holdingMassScale) m.drop(); //drop block if holding // m.holdingMassScale = 0.5 for most fields if (m.isCloak) m.fireCDcycle = m.cycle //forced exit cloak } - const normalFPS = function() { + const normalFPS = function () { if (m.defaultFPSCycle < m.cycle) { //back to default values simulation.fpsCap = simulation.fpsCapDefault simulation.fpsInterval = 1000 / simulation.fpsCap; @@ -814,7 +814,7 @@ const m = { // ********************************************* // ****** drawing player and skins ************* // ********************************************* - drawLeg(stroke) {}, + drawLeg(stroke) { }, calcLeg(cycle_offset, offset) { m.hip.x = 12 + offset; m.hip.y = 24 + offset; @@ -834,7 +834,7 @@ const m = { m.knee.x = (l / d) * (m.foot.x - m.hip.x) - (h / d) * (m.foot.y - m.hip.y) + m.hip.x + offset; m.knee.y = (l / d) * (m.foot.y - m.hip.y) + (h / d) * (m.foot.x - m.hip.x) + m.hip.y; }, - draw() {}, + draw() { }, isAltSkin: false, resetSkin() { m.yOffWhen.jump = 70 @@ -847,7 +847,7 @@ const m = { light: 100, } m.setFillColors(); - m.draw = function() { + m.draw = function () { ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; ctx.save(); @@ -870,7 +870,7 @@ const m = { m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal powerUps.boost.draw() } - m.drawLeg = function(stroke) { + m.drawLeg = function (stroke) { // if (simulation.mouseInGame.x > m.pos.x) { if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { m.flipLegs = 1; @@ -927,7 +927,7 @@ const m = { // light: 55, // } // m.setFillColors(); - m.draw = function() { + m.draw = function () { m.walk_cycle += m.flipLegs * m.Vx; ctx.save(); ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 //|| (m.cycle % 40 > 20) @@ -949,7 +949,7 @@ const m = { m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal powerUps.boost.draw() } - m.drawLeg = function(stroke) { + m.drawLeg = function (stroke) { if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { m.flipLegs = 1; } else { @@ -1012,7 +1012,7 @@ const m = { light: 85, } m.setFillColors(); - m.draw = function() { + m.draw = function () { ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; ctx.save(); @@ -1040,7 +1040,7 @@ const m = { m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal powerUps.boost.draw() } - m.drawLeg = function(stroke) { + m.drawLeg = function (stroke) { // if (simulation.mouseInGame.x > m.pos.x) { if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { m.flipLegs = 1; @@ -1107,7 +1107,7 @@ const m = { // grdRad.addColorStop(0.5, `rgba(210,210,210,0)`); m.bodyGradient = grd - m.draw = function() { + m.draw = function () { ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; ctx.save(); @@ -1140,7 +1140,7 @@ const m = { m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal powerUps.boost.draw() } - m.drawLeg = function(stroke) { + m.drawLeg = function (stroke) { // if (simulation.mouseInGame.x > m.pos.x) { if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { m.flipLegs = 1; @@ -1231,7 +1231,7 @@ const m = { m.bodyGradient = grd - m.draw = function() { + m.draw = function () { ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; ctx.save(); @@ -1254,7 +1254,7 @@ const m = { m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal powerUps.boost.draw() } - m.drawLeg = function(stroke) { + m.drawLeg = function (stroke) { // if (simulation.mouseInGame.x > m.pos.x) { if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { m.flipLegs = 1; @@ -1302,7 +1302,7 @@ const m = { }, dilate() { m.isAltSkin = true - m.draw = function() { + m.draw = function () { const amplitude = 8 + 4 * Math.sin(m.cycle * 0.0075) ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; @@ -1334,7 +1334,7 @@ const m = { }, dilate2() { m.isAltSkin = true - m.draw = function() { + m.draw = function () { const amplitude = Math.sin(m.cycle * 0.0075) ctx.fillStyle = m.fillColor; @@ -1366,7 +1366,7 @@ const m = { m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal powerUps.boost.draw() } - m.drawLeg = function(stroke) { + m.drawLeg = function (stroke) { // if (simulation.mouseInGame.x > m.pos.x) { if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { m.flipLegs = 1; @@ -1424,7 +1424,7 @@ const m = { // grd.addColorStop(1, m.fillColor); m.bodyGradient = grd - m.draw = function() { + m.draw = function () { ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; ctx.save(); @@ -1448,7 +1448,7 @@ const m = { m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal powerUps.boost.draw() } - m.drawLeg = function(stroke) { + m.drawLeg = function (stroke) { // if (simulation.mouseInGame.x > m.pos.x) { if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { m.flipLegs = 1; @@ -1492,7 +1492,7 @@ const m = { }, stubs() { m.isAltSkin = true - m.draw = function() { + m.draw = function () { ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; ctx.save(); @@ -1515,7 +1515,7 @@ const m = { m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal powerUps.boost.draw() } - m.drawLeg = function(stroke) { + m.drawLeg = function (stroke) { // if (simulation.mouseInGame.x > m.pos.x) { if (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2) { m.flipLegs = 1; @@ -1536,7 +1536,7 @@ const m = { }, Sleipnir() { m.isAltSkin = true - m.draw = function() { + m.draw = function () { ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; ctx.save(); @@ -1561,7 +1561,7 @@ const m = { }, diegesis() { m.isAltSkin = true - m.draw = function() { + m.draw = function () { ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; @@ -1588,7 +1588,7 @@ const m = { }, cat() { m.isAltSkin = true - m.draw = function() { + m.draw = function () { ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; ctx.save(); @@ -1656,7 +1656,7 @@ const m = { }, pareidolia() { m.isAltSkin = true - m.draw = function() { + m.draw = function () { ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; ctx.save(); @@ -1711,7 +1711,7 @@ const m = { }, flipFlop() { m.isAltSkin = true - m.draw = function() { + m.draw = function () { ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; @@ -1877,7 +1877,7 @@ const m = { ctx.fillRect(xOff, yOff, range * m.energy, 10); } }, - drawRegenEnergyCloaking: function() { + drawRegenEnergyCloaking: function () { if (m.energy < m.maxEnergy) { // replaces m.drawRegenEnergy() with custom code m.regenEnergy(); const xOff = m.pos.x - m.radius * m.maxEnergy @@ -1895,13 +1895,15 @@ const m = { }, setFieldRegen() { if (m.fieldMode === 6) { - m.fieldRegen = 0.002333 //14 energy per second + m.fieldRegen = 0.002 //12 energy per second for time dilation } else if (m.fieldMode === 2) { - m.fieldRegen = 0.000833 //5 energy per second + m.fieldRegen = 0.000833 //5 energy per second perfect dia } else if (m.fieldMode === 4) { - m.fieldRegen = 0.002 //12 energy per second + m.fieldRegen = 0.002 //12 energy per second molecular assembler } else if (m.fieldMode === 5) { - m.fieldRegen = 0.001667 //10 energy per second + m.fieldRegen = 0.001667 //10 energy per second plasma torch + } else if (m.fieldMode === 8) { + m.fieldRegen = 0.001667 //10 energy per second pilot wave } else { m.fieldRegen = 0.001 //6 energy per second } @@ -1913,11 +1915,11 @@ const m = { m.fieldRegen *= 0.6 } }, - regenEnergy: function() { //used in drawRegenEnergy // rewritten by some tech + regenEnergy: function () { //used in drawRegenEnergy // rewritten by some tech if (m.immuneCycle < m.cycle) m.energy += m.fieldRegen; if (m.energy < 0) m.energy = 0 }, - regenEnergyDefault: function() { + regenEnergyDefault: function () { if (m.immuneCycle < m.cycle) m.energy += m.fieldRegen; if (m.energy < 0) m.energy = 0 }, @@ -2102,7 +2104,7 @@ const m = { m.holdingTarget.friction = m.holdingTarget.frictionStatic = m.holdingTarget.frictionAir = 0.001 } //check every second to see if player is away from thrown body, and make solid - const solid = function(that) { + const solid = function (that) { const dx = that.position.x - player.position.x; const dy = that.position.y - player.position.y; // if (that.speed < 3 && dx * dx + dy * dy > 10000 && that !== m.holdingTarget) { @@ -2135,7 +2137,7 @@ const m = { m.definePlayerMass() //return to normal player mass if (tech.isAddBlockMass) { - const expand = function(that, massLimit) { + const expand = function (that, massLimit) { if (that.mass < massLimit) { const scale = 1.05; Matter.Body.scale(that, scale, scale); @@ -2423,7 +2425,7 @@ const m = { // wake(powerUp); } }, - hold() {}, + hold() { }, couplingDescription(couple = m.coupling) { switch (m.fieldMode) { case 0: //field emitter @@ -2432,21 +2434,21 @@ const m = { return `deflecting condenses +${couple.toFixed(1)} ice IX` case 2: //perfect diamagnetism return `deflecting condenses +${couple.toFixed(1)} ice IX` - // return `invulnerable +${2*couple} seconds post collision` + // return `invulnerable +${2*couple} seconds post collision` case 3: //negative mass - return `+${((1-0.73 ** couple)*100).toFixed(1)}% defense` + return `+${((1 - 0.73 ** couple) * 100).toFixed(1)}% defense` case 4: //assembler - return `generate ${(8*couple).toFixed(0)} energy per second` + return `generate ${(8 * couple).toFixed(0)} energy per second` case 5: //plasma - return `+${(15*couple).toFixed(0)}% damage` + return `+${(15 * couple).toFixed(0)}% damage` case 6: //time dilation - return `+${(50*couple).toFixed(0)}% longer stopped time` //movement, jumping, and + return `+${(50 * couple).toFixed(0)}% longer stopped time` //movement, jumping, and case 7: //cloaking - return `+${(33*couple).toFixed(0)}% ambush damage` + return `+${(33 * couple).toFixed(0)}% ambush damage` case 8: //pilot wave - return `+${(40*couple).toFixed(0)}% block collision damage` + return `+${(40 * couple).toFixed(0)}% block collision damage` case 9: //wormhole - return `after eating blocks +${(20*couple).toFixed(0)} energy` + return `after eating blocks +${(20 * couple).toFixed(0)} energy` } }, couplingChange(change = 0) { @@ -2518,1908 +2520,1880 @@ const m = { simulation.makeTextLog(`m.setField("${m.fieldUpgrades[m.fieldMode].name}")`); }, fieldUpgrades: [{ - name: "field emitter", - imageNumber: Math.floor(Math.random() * 20), - description: `use energy to deflect mobs + name: "field emitter", + imageNumber: Math.floor(Math.random() * 20), + description: `use energy to deflect mobs
generate 6 energy per second`, //
100 max energy - effect: () => { - m.hold = function() { - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if ((input.field && m.fieldCDcycle < m.cycle)) { //not hold but field button is pressed - if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen - m.grabPowerUp(); - m.lookForPickUp(); - if (m.energy > 0.05) { - m.drawField(); - m.pushMobsFacing(); - } - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - } else { - m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + effect: () => { + m.hold = function () { + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if ((input.field && m.fieldCDcycle < m.cycle)) { //not hold but field button is pressed + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + if (m.energy > 0.05) { + m.drawField(); + m.pushMobsFacing(); } - m.drawRegenEnergy() + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) } + m.drawRegenEnergy() } - }, - { - name: "standing wave", - //deflecting protects you in every direction - description: `3 oscillating shields are permanently active + } + }, + { + name: "standing wave", + //deflecting protects you in every direction + description: `3 oscillating shields are permanently active
+66 max energy
generate 6 energy per second`, - drainCD: 0, - effect: () => { - m.fieldBlockCD = 0; - m.blockingRecoil = 2 //4 is normal - m.fieldRange = 185 - m.fieldShieldingScale = (tech.isStandingWaveExpand ? 0.9 : 1.6) * Math.pow(0.6, (tech.harmonics - 2)) - // m.fieldHarmReduction = 0.66; //33% reduction + drainCD: 0, + effect: () => { + m.fieldBlockCD = 0; + m.blockingRecoil = 2 //4 is normal + m.fieldRange = 185 + m.fieldShieldingScale = (tech.isStandingWaveExpand ? 0.9 : 1.6) * Math.pow(0.6, (tech.harmonics - 2)) + // m.fieldHarmReduction = 0.66; //33% reduction - m.harmonic3Phase = () => { //normal standard 3 different 2-d circles - const fieldRange1 = (0.75 + 0.3 * Math.sin(m.cycle / 23)) * m.fieldRange * m.harmonicRadius - const fieldRange2 = (0.68 + 0.37 * Math.sin(m.cycle / 37)) * m.fieldRange * m.harmonicRadius - const fieldRange3 = (0.7 + 0.35 * Math.sin(m.cycle / 47)) * m.fieldRange * m.harmonicRadius - const netfieldRange = Math.max(fieldRange1, fieldRange2, fieldRange3) - ctx.fillStyle = "rgba(110,170,200," + Math.min(0.6, (0.04 + m.energy * (0.1 + 0.11 * Math.random()))) + ")"; - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, fieldRange1, 0, 2 * Math.PI); - ctx.fill(); - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, fieldRange2, 0, 2 * Math.PI); - ctx.fill(); - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, fieldRange3, 0, 2 * Math.PI); - ctx.fill(); - //360 block - for (let i = 0, len = mob.length; i < len; ++i) { - if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < netfieldRange && !mob[i].isUnblockable) { // && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 - mob[i].locatePlayer(); - if (this.drainCD > m.cycle) { - m.pushMass(mob[i], 0); - } else { - m.pushMass(mob[i]); - this.drainCD = m.cycle + 15 - } - if (mob[i].isShielded || mob[i].shield) m.fieldCDcycle = m.cycle + 20 + m.harmonic3Phase = () => { //normal standard 3 different 2-d circles + const fieldRange1 = (0.75 + 0.3 * Math.sin(m.cycle / 23)) * m.fieldRange * m.harmonicRadius + const fieldRange2 = (0.68 + 0.37 * Math.sin(m.cycle / 37)) * m.fieldRange * m.harmonicRadius + const fieldRange3 = (0.7 + 0.35 * Math.sin(m.cycle / 47)) * m.fieldRange * m.harmonicRadius + const netfieldRange = Math.max(fieldRange1, fieldRange2, fieldRange3) + ctx.fillStyle = "rgba(110,170,200," + Math.min(0.6, (0.04 + m.energy * (0.1 + 0.11 * Math.random()))) + ")"; + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, fieldRange1, 0, 2 * Math.PI); + ctx.fill(); + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, fieldRange2, 0, 2 * Math.PI); + ctx.fill(); + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, fieldRange3, 0, 2 * Math.PI); + ctx.fill(); + //360 block + for (let i = 0, len = mob.length; i < len; ++i) { + if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < netfieldRange && !mob[i].isUnblockable) { // && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 + mob[i].locatePlayer(); + if (this.drainCD > m.cycle) { + m.pushMass(mob[i], 0); + } else { + m.pushMass(mob[i]); + this.drainCD = m.cycle + 15 } + if (mob[i].isShielded || mob[i].shield) m.fieldCDcycle = m.cycle + 20 } } - m.harmonicRadius = 1 //for smoothing function when player holds mouse (for harmonicAtomic) - m.harmonicAtomic = () => { //several ellipses spinning about different axises - const rotation = simulation.cycle * 0.0031 - const phase = simulation.cycle * 0.023 - const radius = m.fieldRange * m.harmonicRadius - ctx.lineWidth = 1; - ctx.strokeStyle = "rgba(110,170,200,0.8)" - ctx.fillStyle = "rgba(110,170,200," + Math.min(0.6, m.energy * (0.11 + 0.1 * Math.random()) * (3 / tech.harmonics)) + ")"; - // ctx.fillStyle = "rgba(110,170,200," + Math.min(0.7, m.energy * (0.22 - 0.01 * tech.harmonics) * (0.5 + 0.5 * Math.random())) + ")"; - for (let i = 0; i < tech.harmonics; i++) { - ctx.beginPath(); - ctx.ellipse(m.pos.x, m.pos.y, radius * Math.abs(Math.sin(phase + i / tech.harmonics * Math.PI)), radius, rotation + i / tech.harmonics * Math.PI, 0, 2 * Math.PI); - ctx.fill(); - ctx.stroke(); - } - //360 block - for (let i = 0, len = mob.length; i < len; ++i) { - if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < radius && !mob[i].isUnblockable) { // && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 - mob[i].locatePlayer(); - if (this.drainCD > m.cycle) { - m.pushMass(mob[i], 0); - } else { - m.pushMass(mob[i]); - this.drainCD = m.cycle + 15 - } - } - } - } - if (tech.harmonics === 2) { - m.harmonicShield = m.harmonic3Phase - } else { - m.harmonicShield = m.harmonicAtomic - } - m.hold = function() { - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if ((input.field) && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen - m.grabPowerUp(); - m.lookForPickUp(); - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - } else { - m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - } - if (m.energy > 0.1 && m.fieldCDcycle < m.cycle) { - if (tech.isStandingWaveExpand) { - if (input.field) { - // const oldHarmonicRadius = m.harmonicRadius - m.harmonicRadius = 0.99 * m.harmonicRadius + 0.01 * 4 - // m.energy -= 0.1 * (m.harmonicRadius - oldHarmonicRadius) - } else { - m.harmonicRadius = 0.994 * m.harmonicRadius + 0.006 - } - } - if (!simulation.isTimeSkipping) m.harmonicShield() - } - m.drawRegenEnergy() - } } - }, - { - name: "perfect diamagnetism", - description: "deflecting does not drain energy
maintains functionality while inactive
generate 5 energy per second", - //
attract power ups from far away - // description: "attract power ups from far away
deflecting doesn't drain energy
thrown blocks have", - // description: "gain energy when blocking
no recoil when blocking", - effect: () => { - m.fieldMeterColor = "#48f" //"#0c5" - m.eyeFillColor = m.fieldMeterColor - - m.fieldShieldingScale = 0; - m.fieldBlockCD = 3; - m.grabPowerUpRange2 = 10000000 - m.fieldPosition = { - x: m.pos.x, - y: m.pos.y + m.harmonicRadius = 1 //for smoothing function when player holds mouse (for harmonicAtomic) + m.harmonicAtomic = () => { //several ellipses spinning about different axises + const rotation = simulation.cycle * 0.0031 + const phase = simulation.cycle * 0.023 + const radius = m.fieldRange * m.harmonicRadius + ctx.lineWidth = 1; + ctx.strokeStyle = "rgba(110,170,200,0.8)" + ctx.fillStyle = "rgba(110,170,200," + Math.min(0.6, m.energy * (0.11 + 0.1 * Math.random()) * (3 / tech.harmonics)) + ")"; + // ctx.fillStyle = "rgba(110,170,200," + Math.min(0.7, m.energy * (0.22 - 0.01 * tech.harmonics) * (0.5 + 0.5 * Math.random())) + ")"; + for (let i = 0; i < tech.harmonics; i++) { + ctx.beginPath(); + ctx.ellipse(m.pos.x, m.pos.y, radius * Math.abs(Math.sin(phase + i / tech.harmonics * Math.PI)), radius, rotation + i / tech.harmonics * Math.PI, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); } - m.fieldAngle = m.angle - m.perfectPush = (isFree = false) => { - if (m.fieldCDcycle < m.cycle) { - for (let i = 0, len = mob.length; i < len; ++i) { - if ( - Vector.magnitude(Vector.sub(mob[i].position, m.fieldPosition)) - mob[i].radius < m.fieldRange && - !mob[i].isUnblockable && - Vector.dot({ - x: Math.cos(m.fieldAngle), - y: Math.sin(m.fieldAngle) - }, Vector.normalise(Vector.sub(mob[i].position, m.fieldPosition))) > m.fieldThreshold && - Matter.Query.ray(map, mob[i].position, m.fieldPosition).length === 0 - ) { - mob[i].locatePlayer(); - const unit = Vector.normalise(Vector.sub(m.fieldPosition, mob[i].position)) - m.fieldCDcycle = m.cycle + m.fieldBlockCD + (mob[i].isShielded ? 15 : 0); - if (!mob[i].isInvulnerable && bullet.length < 250) { - for (let i = 0; i < m.coupling; i++) { - if (m.coupling - i > Math.random()) { - const angle = m.fieldAngle + 4 * m.fieldArc * (Math.random() - 0.5) - const radius = m.fieldRange * (0.6 + 0.3 * Math.random()) - b.iceIX(6 + 6 * Math.random(), angle, Vector.add(m.fieldPosition, { - x: radius * Math.cos(angle), - y: radius * Math.sin(angle) - })) - } + //360 block + for (let i = 0, len = mob.length; i < len; ++i) { + if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < radius && !mob[i].isUnblockable) { // && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 + mob[i].locatePlayer(); + if (this.drainCD > m.cycle) { + m.pushMass(mob[i], 0); + } else { + m.pushMass(mob[i]); + this.drainCD = m.cycle + 15 + } + } + } + } + if (tech.harmonics === 2) { + m.harmonicShield = m.harmonic3Phase + } else { + m.harmonicShield = m.harmonicAtomic + } + m.hold = function () { + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if ((input.field) && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + } + if (m.energy > 0.1 && m.fieldCDcycle < m.cycle) { + if (tech.isStandingWaveExpand) { + if (input.field) { + // const oldHarmonicRadius = m.harmonicRadius + m.harmonicRadius = 0.99 * m.harmonicRadius + 0.01 * 4 + // m.energy -= 0.1 * (m.harmonicRadius - oldHarmonicRadius) + } else { + m.harmonicRadius = 0.994 * m.harmonicRadius + 0.006 + } + } + if (!simulation.isTimeSkipping) m.harmonicShield() + } + m.drawRegenEnergy() + } + } + }, + { + name: "perfect diamagnetism", + description: "deflecting does not drain energy
maintains functionality while inactive
generate 5 energy per second", + //
attract power ups from far away + // description: "attract power ups from far away
deflecting doesn't drain energy
thrown blocks have", + // description: "gain energy when blocking
no recoil when blocking", + effect: () => { + m.fieldMeterColor = "#48f" //"#0c5" + m.eyeFillColor = m.fieldMeterColor + + m.fieldShieldingScale = 0; + m.fieldBlockCD = 3; + m.grabPowerUpRange2 = 10000000 + m.fieldPosition = { + x: m.pos.x, + y: m.pos.y + } + m.fieldAngle = m.angle + m.perfectPush = (isFree = false) => { + if (m.fieldCDcycle < m.cycle) { + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + Vector.magnitude(Vector.sub(mob[i].position, m.fieldPosition)) - mob[i].radius < m.fieldRange && + !mob[i].isUnblockable && + Vector.dot({ + x: Math.cos(m.fieldAngle), + y: Math.sin(m.fieldAngle) + }, Vector.normalise(Vector.sub(mob[i].position, m.fieldPosition))) > m.fieldThreshold && + Matter.Query.ray(map, mob[i].position, m.fieldPosition).length === 0 + ) { + mob[i].locatePlayer(); + const unit = Vector.normalise(Vector.sub(m.fieldPosition, mob[i].position)) + m.fieldCDcycle = m.cycle + m.fieldBlockCD + (mob[i].isShielded ? 15 : 0); + if (!mob[i].isInvulnerable && bullet.length < 250) { + for (let i = 0; i < m.coupling; i++) { + if (m.coupling - i > Math.random()) { + const angle = m.fieldAngle + 4 * m.fieldArc * (Math.random() - 0.5) + const radius = m.fieldRange * (0.6 + 0.3 * Math.random()) + b.iceIX(6 + 6 * Math.random(), angle, Vector.add(m.fieldPosition, { + x: radius * Math.cos(angle), + y: radius * Math.sin(angle) + })) } } - if (tech.blockDmg) { //electricity - Matter.Body.setVelocity(mob[i], { - x: 0.5 * mob[i].velocity.x, - y: 0.5 * mob[i].velocity.y - }); + } + if (tech.blockDmg) { //electricity + Matter.Body.setVelocity(mob[i], { + x: 0.5 * mob[i].velocity.x, + y: 0.5 * mob[i].velocity.y + }); - if (mob[i].isShielded) { - for (let j = 0, len = mob.length; j < len; j++) { - if (mob[j].id === mob[i].shieldID) mob[j].damage(tech.blockDmg * m.dmgScale * (tech.isBlockRadiation ? 6 : 2), true) - } - } else if (tech.isBlockRadiation) { - if (mob[i].isMobBullet) { - mob[i].damage(tech.blockDmg * m.dmgScale * 3, true) - } else { - mobs.statusDoT(mob[i], tech.blockDmg * m.dmgScale * 4 / 12, 360) //200% increase -> x (1+2) //over 7s -> 360/30 = 12 half seconds -> 3/12 - } + if (mob[i].isShielded) { + for (let j = 0, len = mob.length; j < len; j++) { + if (mob[j].id === mob[i].shieldID) mob[j].damage(tech.blockDmg * m.dmgScale * (tech.isBlockRadiation ? 6 : 2), true) + } + } else if (tech.isBlockRadiation) { + if (mob[i].isMobBullet) { + mob[i].damage(tech.blockDmg * m.dmgScale * 3, true) } else { - mob[i].damage(tech.blockDmg * m.dmgScale, true) + mobs.statusDoT(mob[i], tech.blockDmg * m.dmgScale * 4 / 12, 360) //200% increase -> x (1+2) //over 7s -> 360/30 = 12 half seconds -> 3/12 } - // if (mob[i].isShielded) { - // for (let j = 0, len = mob.length; j < len; j++) { - // if (mob[j].id === mob[i].shieldID) mob[j].damage(tech.blockDmg * m.dmgScale * (tech.isBlockRadiation ? 3 : 1), true) - // } - // } else { - // if (tech.isBlockRadiation && !mob[i].isMobBullet) { - // mobs.statusDoT(mob[i], tech.blockDmg * m.dmgScale * 4 / 12, 360) //200% increase -> x (1+2) //over 7s -> 360/30 = 12 half seconds -> 3/12 - // } else { - // mob[i].damage(tech.blockDmg * m.dmgScale) - // } - // } - const step = 40 - ctx.beginPath(); - for (let i = 0, len = 0.8 * tech.blockDmg; i < len; i++) { - let x = m.fieldPosition.x - 20 * unit.x; - let y = m.fieldPosition.y - 20 * unit.y; - ctx.moveTo(x, y); - for (let i = 0; i < 8; i++) { - x += step * (-unit.x + 1.5 * (Math.random() - 0.5)) - y += step * (-unit.y + 1.5 * (Math.random() - 0.5)) - ctx.lineTo(x, y); - } - } - ctx.lineWidth = 3; - ctx.strokeStyle = "#f0f"; - ctx.stroke(); - } else if (isFree) { - ctx.lineWidth = 2; //when blocking draw this graphic - ctx.fillStyle = `rgba(110,150,220, ${0.2 + 0.4 * Math.random()})` - ctx.strokeStyle = "#000"; - const len = mob[i].vertices.length - 1; - const mag = mob[i].radius - ctx.beginPath(); - ctx.moveTo(mob[i].vertices[len].x + mag * (Math.random() - 0.5), mob[i].vertices[len].y + mag * (Math.random() - 0.5)) - for (let j = 0; j < len; j++) { - ctx.lineTo(mob[i].vertices[j].x + mag * (Math.random() - 0.5), mob[i].vertices[j].y + mag * (Math.random() - 0.5)); - } - ctx.lineTo(mob[i].vertices[len].x + mag * (Math.random() - 0.5), mob[i].vertices[len].y + mag * (Math.random() - 0.5)) - ctx.fill(); - ctx.stroke(); } else { + mob[i].damage(tech.blockDmg * m.dmgScale, true) + } + // if (mob[i].isShielded) { + // for (let j = 0, len = mob.length; j < len; j++) { + // if (mob[j].id === mob[i].shieldID) mob[j].damage(tech.blockDmg * m.dmgScale * (tech.isBlockRadiation ? 3 : 1), true) + // } + // } else { + // if (tech.isBlockRadiation && !mob[i].isMobBullet) { + // mobs.statusDoT(mob[i], tech.blockDmg * m.dmgScale * 4 / 12, 360) //200% increase -> x (1+2) //over 7s -> 360/30 = 12 half seconds -> 3/12 + // } else { + // mob[i].damage(tech.blockDmg * m.dmgScale) + // } + // } + const step = 40 + ctx.beginPath(); + for (let i = 0, len = 0.8 * tech.blockDmg; i < len; i++) { + let x = m.fieldPosition.x - 20 * unit.x; + let y = m.fieldPosition.y - 20 * unit.y; + ctx.moveTo(x, y); + for (let i = 0; i < 8; i++) { + x += step * (-unit.x + 1.5 * (Math.random() - 0.5)) + y += step * (-unit.y + 1.5 * (Math.random() - 0.5)) + ctx.lineTo(x, y); + } + } + ctx.lineWidth = 3; + ctx.strokeStyle = "#f0f"; + ctx.stroke(); + } else if (isFree) { + ctx.lineWidth = 2; //when blocking draw this graphic + ctx.fillStyle = `rgba(110,150,220, ${0.2 + 0.4 * Math.random()})` + ctx.strokeStyle = "#000"; + const len = mob[i].vertices.length - 1; + const mag = mob[i].radius + ctx.beginPath(); + ctx.moveTo(mob[i].vertices[len].x + mag * (Math.random() - 0.5), mob[i].vertices[len].y + mag * (Math.random() - 0.5)) + for (let j = 0; j < len; j++) { + ctx.lineTo(mob[i].vertices[j].x + mag * (Math.random() - 0.5), mob[i].vertices[j].y + mag * (Math.random() - 0.5)); + } + ctx.lineTo(mob[i].vertices[len].x + mag * (Math.random() - 0.5), mob[i].vertices[len].y + mag * (Math.random() - 0.5)) + ctx.fill(); + ctx.stroke(); + } else { - const eye = 15; //when blocking draw this graphic - const len = mob[i].vertices.length - 1; - ctx.lineWidth = 1; - ctx.fillStyle = `rgba(110,150,220, ${0.2 + 0.4 * Math.random()})` - ctx.strokeStyle = "#000"; + const eye = 15; //when blocking draw this graphic + const len = mob[i].vertices.length - 1; + ctx.lineWidth = 1; + ctx.fillStyle = `rgba(110,150,220, ${0.2 + 0.4 * Math.random()})` + ctx.strokeStyle = "#000"; + ctx.beginPath(); + ctx.moveTo(m.fieldPosition.x + eye * Math.cos(m.fieldAngle), m.fieldPosition.y + eye * Math.sin(m.fieldAngle)); + ctx.lineTo(mob[i].vertices[len].x, mob[i].vertices[len].y); + ctx.lineTo(mob[i].vertices[0].x, mob[i].vertices[0].y); + ctx.fill(); + ctx.stroke(); + for (let j = 0; j < len; j++) { ctx.beginPath(); ctx.moveTo(m.fieldPosition.x + eye * Math.cos(m.fieldAngle), m.fieldPosition.y + eye * Math.sin(m.fieldAngle)); - ctx.lineTo(mob[i].vertices[len].x, mob[i].vertices[len].y); - ctx.lineTo(mob[i].vertices[0].x, mob[i].vertices[0].y); + ctx.lineTo(mob[i].vertices[j].x, mob[i].vertices[j].y); + ctx.lineTo(mob[i].vertices[j + 1].x, mob[i].vertices[j + 1].y); ctx.fill(); ctx.stroke(); - for (let j = 0; j < len; j++) { - ctx.beginPath(); - ctx.moveTo(m.fieldPosition.x + eye * Math.cos(m.fieldAngle), m.fieldPosition.y + eye * Math.sin(m.fieldAngle)); - ctx.lineTo(mob[i].vertices[j].x, mob[i].vertices[j].y); - ctx.lineTo(mob[i].vertices[j + 1].x, mob[i].vertices[j + 1].y); - ctx.fill(); - ctx.stroke(); + } + } + if (tech.isStunField) mobs.statusStun(mob[i], tech.isStunField) + //mob knock backs + const massRoot = Math.sqrt(Math.max(1, mob[i].mass)); + Matter.Body.setVelocity(mob[i], { + x: player.velocity.x - (30 * unit.x) / massRoot, + y: player.velocity.y - (30 * unit.y) / massRoot + }); + if (mob[i].isUnstable) { + if (m.fieldCDcycle < m.cycle + 10) m.fieldCDcycle = m.cycle + 6 + mob[i].death(); + } + if (!isFree) { //player knock backs + if (mob[i].isDropPowerUp && player.speed < 12) { + const massRootCap = Math.sqrt(Math.min(10, Math.max(0.2, mob[i].mass))); + Matter.Body.setVelocity(player, { + x: 0.9 * player.velocity.x + 0.6 * unit.x * massRootCap, + y: 0.9 * player.velocity.y + 0.6 * unit.y * massRootCap + }); + } + } + } + } + } + } + m.hold = function () { + const wave = Math.sin(m.cycle * 0.022); + m.fieldRange = 180 + 12 * wave + 100 * tech.isBigField + m.fieldArc = 0.35 + 0.045 * wave + 0.065 * tech.isBigField //run calculateFieldThreshold after setting fieldArc, used for powerUp grab and mobPush with lookingAt(mob) + m.calculateFieldThreshold(); + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field) { //not hold but field button is pressed + //float while field is on + const angleReduction = 0.5 + 0.7 * (Math.PI / 2 - Math.min(Math.PI / 2, Math.abs(m.angle + Math.PI / 2))) + // console.log(angleReduction) + if (player.velocity.y > 1) { + player.force.y -= angleReduction * (tech.isBigField ? 0.95 : 0.5) * player.mass * simulation.g; + Matter.Body.setVelocity(player, { + x: player.velocity.x, + y: 0.98 * player.velocity.y + }); //set velocity to cap, but keep the direction + } + + + + + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + m.fieldPosition = { + x: m.pos.x, + y: m.pos.y + } + m.fieldAngle = m.angle + //draw field attached to player + if (m.holdingTarget) { + ctx.fillStyle = `rgba(110,150,220, ${0.06 + 0.03 * Math.random()})` + ctx.strokeStyle = `rgba(110,150,220, ${0.35 + 0.05 * Math.random()})` + } else { + ctx.fillStyle = `rgba(110,150,220, ${0.27 + 0.2 * Math.random() - 0.1 * wave})` + ctx.strokeStyle = `rgba(110,150,220, ${0.4 + 0.5 * Math.random()})` + } + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, m.fieldRange, m.angle - Math.PI * m.fieldArc, m.angle + Math.PI * m.fieldArc, false); + ctx.lineWidth = 2.5 - 1.5 * wave; + ctx.stroke(); + const curve = 0.57 + 0.04 * wave + const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc + let a = m.angle + aMag + let cp1x = m.pos.x + curve * m.fieldRange * Math.cos(a) + let cp1y = m.pos.y + curve * m.fieldRange * Math.sin(a) + ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 30 * Math.cos(m.angle), m.pos.y + 30 * Math.sin(m.angle)) + a = m.angle - aMag + cp1x = m.pos.x + curve * m.fieldRange * Math.cos(a) + cp1y = m.pos.y + curve * m.fieldRange * Math.sin(a) + ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 1 * m.fieldRange * Math.cos(m.angle - Math.PI * m.fieldArc), m.pos.y + 1 * m.fieldRange * Math.sin(m.angle - Math.PI * m.fieldArc)) + ctx.fill(); + m.perfectPush(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + if (!input.field) { //&& tech.isFieldFree + //draw field free of player + ctx.fillStyle = `rgba(110,150,220, ${0.27 + 0.2 * Math.random() - 0.1 * wave})` + ctx.strokeStyle = `rgba(110,180,255, ${0.4 + 0.5 * Math.random()})` + ctx.beginPath(); + ctx.arc(m.fieldPosition.x, m.fieldPosition.y, m.fieldRange, m.fieldAngle - Math.PI * m.fieldArc, m.fieldAngle + Math.PI * m.fieldArc, false); + ctx.lineWidth = 2.5 - 1.5 * wave; + ctx.stroke(); + const curve = 0.8 + 0.06 * wave + const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc + let a = m.fieldAngle + aMag + ctx.quadraticCurveTo(m.fieldPosition.x + curve * m.fieldRange * Math.cos(a), m.fieldPosition.y + curve * m.fieldRange * Math.sin(a), m.fieldPosition.x + 1 * m.fieldRange * Math.cos(m.fieldAngle - Math.PI * m.fieldArc), m.fieldPosition.y + 1 * m.fieldRange * Math.sin(m.fieldAngle - Math.PI * m.fieldArc)) + ctx.fill(); + m.perfectPush(true); + } + } + // m.drawRegenEnergy() + m.drawRegenEnergy("rgba(0,0,0,0.2)") + if (tech.isPerfectBrake) { //cap mob speed around player + const range = 200 + 140 * wave + 150 * m.energy + for (let i = 0; i < mob.length; i++) { + const distance = Vector.magnitude(Vector.sub(m.pos, mob[i].position)) + if (distance < range) { + const cap = mob[i].isShielded ? 8 : 4 + if (mob[i].speed > cap && Vector.dot(mob[i].velocity, Vector.sub(m.pos, mob[i].position)) > 0) { // if velocity is directed towards player + Matter.Body.setVelocity(mob[i], Vector.mult(Vector.normalise(mob[i].velocity), cap)); //set velocity to cap, but keep the direction + } + } + } + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, range, 0, 2 * Math.PI); + ctx.fillStyle = "hsla(200,50%,61%,0.08)"; + ctx.fill(); + } + } + } + }, + { + name: "negative mass", + //
hold blocks as if they have a lower mass + description: "use energy to nullify  gravity
+55% defense
generate 6 energy per second", + fieldDrawRadius: 0, + effect: () => { + m.fieldFire = true; + m.holdingMassScale = 0.01; //can hold heavier blocks with lower cost to jumping + m.fieldMeterColor = "#333" + m.eyeFillColor = m.fieldMeterColor + m.fieldHarmReduction = 0.45; //55% reduction + m.fieldDrawRadius = 0; + + m.hold = function () { + m.airSpeedLimit = 125 //5 * player.mass * player.mass + m.FxAir = 0.016 + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field && m.fieldCDcycle < m.cycle) { //push away + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + const DRAIN = 0.00035 + if (m.energy > DRAIN) { + if (tech.isFlyFaster) { + //look for nearby objects to make zero-g + function moveThis(who, range, mag = 1.06) { + for (let i = 0, len = who.length; i < len; ++i) { + sub = Vector.sub(who[i].position, m.pos); + dist = Vector.magnitude(sub); + if (dist < range) { + who[i].force.y -= who[i].mass * (simulation.g * mag); //add a bit more then standard gravity + if (input.left) { //blocks move horizontally with the same force as the player + who[i].force.x -= m.FxAir * who[i].mass / 10; // move player left / a + } else if (input.right) { + who[i].force.x += m.FxAir * who[i].mass / 10; //move player right / d + } + //loose attraction to player + // const sub = Vector.sub(m.pos, body[i].position) + // const unit = Vector.mult(Vector.normalise(sub), who[i].mass * 0.0000002 * Vector.magnitude(sub)) + // body[i].force.x += unit.x + // body[i].force.y += unit.y } } - if (tech.isStunField) mobs.statusStun(mob[i], tech.isStunField) - //mob knock backs - const massRoot = Math.sqrt(Math.max(1, mob[i].mass)); - Matter.Body.setVelocity(mob[i], { - x: player.velocity.x - (30 * unit.x) / massRoot, - y: player.velocity.y - (30 * unit.y) / massRoot - }); - if (mob[i].isUnstable) { - if (m.fieldCDcycle < m.cycle + 10) m.fieldCDcycle = m.cycle + 6 - mob[i].death(); + } + //control horizontal acceleration + m.airSpeedLimit = 1000 // 7* player.mass * player.mass + m.FxAir = 0.01 + //control vertical acceleration + if (input.down) { //down + player.force.y += 0.5 * player.mass * simulation.g; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 500 * 0.03; + moveThis(powerUp, this.fieldDrawRadius, 0); + moveThis(body, this.fieldDrawRadius, 0); + } else if (input.up) { //up + m.energy -= 5 * DRAIN; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 1100 * 0.03; + player.force.y -= 2.25 * player.mass * simulation.g; + moveThis(powerUp, this.fieldDrawRadius, 1.8); + moveThis(body, this.fieldDrawRadius, 1.8); + } else { + m.energy -= DRAIN; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 800 * 0.03; + player.force.y -= 1.07 * player.mass * simulation.g; // slow upward drift + moveThis(powerUp, this.fieldDrawRadius); + moveThis(body, this.fieldDrawRadius); + } + } else { + //look for nearby objects to make zero-g + function verticalForce(who, range, mag = 1.06) { + for (let i = 0, len = who.length; i < len; ++i) { + sub = Vector.sub(who[i].position, m.pos); + dist = Vector.magnitude(sub); + if (dist < range) who[i].force.y -= who[i].mass * (simulation.g * mag); } - if (!isFree) { //player knock backs - if (mob[i].isDropPowerUp && player.speed < 12) { - const massRootCap = Math.sqrt(Math.min(10, Math.max(0.2, mob[i].mass))); - Matter.Body.setVelocity(player, { - x: 0.9 * player.velocity.x + 0.6 * unit.x * massRootCap, - y: 0.9 * player.velocity.y + 0.6 * unit.y * massRootCap + } + //control horizontal acceleration + m.airSpeedLimit = 400 // 7* player.mass * player.mass + m.FxAir = 0.005 + //control vertical acceleration + if (input.down) { //down + player.force.y -= 0.5 * player.mass * simulation.g; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 400 * 0.03; + verticalForce(powerUp, this.fieldDrawRadius, 0.7); + verticalForce(body, this.fieldDrawRadius, 0.7); + } else if (input.up) { //up + m.energy -= 5 * DRAIN; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 850 * 0.03; + player.force.y -= 1.45 * player.mass * simulation.g; + verticalForce(powerUp, this.fieldDrawRadius, 1.38); + verticalForce(body, this.fieldDrawRadius, 1.38); + } else { + m.energy -= DRAIN; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 650 * 0.03; + player.force.y -= 1.07 * player.mass * simulation.g; // slow upward drift + verticalForce(powerUp, this.fieldDrawRadius); + verticalForce(body, this.fieldDrawRadius); + } + } + + if (m.energy < 0.001) { + m.fieldCDcycle = m.cycle + 120; + m.energy = 0; + } + //add extra friction for horizontal motion + if (input.down || input.up || input.left || input.right) { + Matter.Body.setVelocity(player, { + x: player.velocity.x * 0.99, + y: player.velocity.y * 0.98 + }); + } else { //slow rise and fall + Matter.Body.setVelocity(player, { + x: player.velocity.x * 0.99, + y: player.velocity.y * 0.98 + }); + } + // if (tech.isFreezeMobs) { + // const ICE_DRAIN = 0.0005 + // for (let i = 0, len = mob.length; i < len; i++) { + // if (!mob[i].isMobBullet && !mob[i].shield && !mob[i].isShielded && ((mob[i].distanceToPlayer() + mob[i].radius) < this.fieldDrawRadius)) { + // if (m.energy > ICE_DRAIN * 2) { + // m.energy -= ICE_DRAIN; + // this.fieldDrawRadius -= 2; + // mobs.statusSlow(mob[i], 60) + // } else { + // break; + // } + // } + // } + // } + //draw zero-G range + if (!simulation.isTimeSkipping) { + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, this.fieldDrawRadius, 0, 2 * Math.PI); + ctx.fillStyle = "#f5f5ff"; + ctx.globalCompositeOperation = "difference"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + } + } + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + this.fieldDrawRadius = 0 + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + this.fieldDrawRadius = 0 + } + m.drawRegenEnergy("rgba(0,0,0,0.2)") + } + } + }, + { + name: "molecular assembler", + description: `excess energy used to build ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
use energy to deflect mobs
generate 12 energy per second`, + // simulation.molecularMode: Math.floor(4 * Math.random()), //0 spores, 1 missile, 2 ice IX, 3 drones + setDescription() { + return `excess energy used to build ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
use energy to deflect mobs
generate 12 energy per second` + }, + effect: () => { + m.fieldMeterColor = "#ff0" + m.eyeFillColor = m.fieldMeterColor + m.hold = function () { + if (m.energy > m.maxEnergy - 0.02 && m.fieldCDcycle < m.cycle && !input.field && bullet.length < 300 && (m.cycle % 2)) { + if (simulation.molecularMode === 0) { + if (tech.isSporeFlea) { + const drain = 0.18 + (Math.max(bullet.length, 130) - 130) * 0.02 + if (m.energy > drain) { + m.energy -= drain + const speed = m.crouch ? 20 + 8 * Math.random() : 10 + 3 * Math.random() + b.flea({ + x: m.pos.x + 35 * Math.cos(m.angle), + y: m.pos.y + 35 * Math.sin(m.angle) + }, { + x: speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + }) + } + } else if (tech.isSporeWorm) { + const drain = 0.18 + (Math.max(bullet.length, 130) - 130) * 0.02 + if (m.energy > drain) { + m.energy -= drain + b.worm({ + x: m.pos.x + 35 * Math.cos(m.angle), + y: m.pos.y + 35 * Math.sin(m.angle) + }) + const SPEED = 2 + 1 * Math.random(); + Matter.Body.setVelocity(bullet[bullet.length - 1], { + x: SPEED * Math.cos(m.angle), + y: SPEED * Math.sin(m.angle) + }); + } + } else { + const drain = 0.095 + (Math.max(bullet.length, 130) - 130) * 0.01 + for (let i = 0, len = Math.random() * 20; i < len; i++) { + if (m.energy > 3 * drain) { + m.energy -= drain + b.spore(m.pos) + } else { + break + } + } + } + } else if (simulation.molecularMode === 1) { + m.energy -= 0.33; + const direction = { + x: Math.cos(m.angle), + y: Math.sin(m.angle) + } + const push = Vector.mult(Vector.perp(direction), 0.08) + b.missile({ + x: m.pos.x + 30 * direction.x, + y: m.pos.y + 30 * direction.y + }, m.angle, -15) + bullet[bullet.length - 1].force.x += push.x * (Math.random() - 0.5) + bullet[bullet.length - 1].force.y += 0.005 + push.y * (Math.random() - 0.5) + // b.missile({ x: m.pos.x, y: m.pos.y - 40 }, -Math.PI / 2 + 0.5 * (Math.random() - 0.5), 0, 1) + } else if (simulation.molecularMode === 2) { + m.energy -= 0.045; + b.iceIX(1) + } else if (simulation.molecularMode === 3) { + if (tech.isDroneRadioactive) { + const drain = 0.8 + (Math.max(bullet.length, 50) - 50) * 0.01 + if (m.energy > drain) { + m.energy -= drain + b.droneRadioactive({ + x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), + y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) + }, 25) + } + } else { + //every bullet above 100 adds 0.005 to the energy cost per drone + //at 200 bullets the energy cost is 0.45 + 100*0.006 = 1.05 + const drain = (0.45 + (Math.max(bullet.length, 100) - 100) * 0.006) * tech.droneEnergyReduction + if (m.energy > drain) { + m.energy -= drain + b.drone() + } + } + } + } + + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if ((input.field && m.fieldCDcycle < m.cycle)) { //not hold but field button is pressed + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + if (m.energy > 0.05) { + m.drawField(); + m.pushMobsFacing(); + } + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + } + m.drawRegenEnergy() + } + } + }, + // { + // name: "plasma torch", + // description: "use energy to emit short range plasma
damages and pushes mobs away", + // effect() { + // m.fieldMeterColor = "#f0f" + // m.eyeFillColor = m.fieldMeterColor + // m.hold = function() { + // b.isExtruderOn = false + // if (m.isHolding) { + // m.drawHold(m.holdingTarget); + // m.holding(); + // m.throwBlock(); + // } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + // m.grabPowerUp(); + // m.lookForPickUp(); + // if (tech.isExtruder) { + // b.extruder(); + // } else { + // b.plasma(); + // } + // } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + // m.pickUp(); + // } else { + // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + // } + // m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") + + // if (tech.isExtruder) { + // if (input.field) { + // b.wasExtruderOn = true + // } else { + // b.wasExtruderOn = false + // b.canExtruderFire = true + // } + // ctx.beginPath(); //draw all the wave bullets + // for (let i = 0, len = bullet.length; i < len; i++) { + // if (bullet[i].isWave) { + // if (bullet[i].isBranch) { + // ctx.moveTo(bullet[i].position.x, bullet[i].position.y) + // } else { + // ctx.lineTo(bullet[i].position.x, bullet[i].position.y) + // } + // } + // } + // if (b.wasExtruderOn && b.isExtruderOn) ctx.lineTo(m.pos.x + 15 * Math.cos(m.angle), m.pos.y + 15 * Math.sin(m.angle)) + // ctx.lineWidth = 4; + // ctx.strokeStyle = "#f07" + // ctx.stroke(); + // ctx.lineWidth = tech.extruderRange; + // ctx.strokeStyle = "rgba(255,0,110,0.05)" + // ctx.stroke(); + // } + // } + // } + // }, + { + name: "plasma torch", + description: "use energy to emit short range plasma
damages and pushes mobs away
generate 10 energy per second", + set() { + b.isExtruderOn = false + // m.fieldCDcycleAlternate = 0 + + if (m.plasmaBall) { + m.plasmaBall.reset() + Matter.Composite.remove(engine.world, m.plasmaBall); + } + if (tech.isPlasmaBall) { + const circleRadiusScale = 2 + m.plasmaBall = Bodies.circle(m.pos.x + 10 * Math.cos(m.angle), m.pos.y + 10 * Math.sin(m.angle), 1, { + // collisionFilter: { + // group: 0, + // category: 0, + // mask: 0 //cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield + // }, + isSensor: true, + frictionAir: 0, + alpha: 0.7, + isPopping: false, + isAttached: false, + isOn: false, + drain: 0.0017, + radiusLimit: 10, + damage: 0.8, + setPositionToNose() { + const nose = { + x: m.pos.x + 10 * Math.cos(m.angle), + y: m.pos.y + 10 * Math.sin(m.angle) + } + Matter.Body.setPosition(this, Vector.add(nose, Vector.mult(Vector.normalise(Vector.sub(nose, m.pos)), circleRadiusScale * this.circleRadius))); + }, + fire() { + this.isAttached = false; + const speed = 10 //scale with mass? + Matter.Body.setVelocity(this, { + x: player.velocity.x * 0.4 + speed * Math.cos(m.angle), + y: speed * Math.sin(m.angle) + }); + m.plasmaBall.setPositionToNose() + if (this.circleRadius < 10) this.isPopping = true + }, + scale(scale) { + Matter.Body.scale(m.plasmaBall, scale, scale); //shrink fast + if (this.circleRadius < this.radiusLimit) this.reset() + }, + reset() { + // console.log(this.circleRadius) + const scale = 1 / m.plasmaBall.circleRadius + Matter.Body.scale(m.plasmaBall, scale, scale); //grow + // console.log(this.circleRadius) + // this.circleRadius = 0 + this.alpha = 0.7 + this.isOn = false + this.isPopping = false + // this.isAttached = true; + }, + do() { + if (this.isOn) { + //collisions with map + if (Matter.Query.collides(this, map).length > 0) { + if (this.isAttached) { + this.scale(Math.max(0.9, 0.998 - 0.1 / m.plasmaBall.circleRadius)) + } else { + this.isPopping = true + } + } + if (this.isPopping) { + this.alpha -= 0.03 + if (this.alpha < 0.1) { + this.reset() + } else { + const scale = 1.04 + 4 / Math.max(1, m.plasmaBall.circleRadius) + Matter.Body.scale(m.plasmaBall, scale, scale); //grow + } + // if (this.speed > 2.5) { + // const slow = 0.9 + // Matter.Body.setVelocity(this, { + // x: slow * this.velocity.x, + // y: slow * this.velocity.y + // }); + // } + } + //collisions with mobs + // const whom = Matter.Query.collides(this, mob) + // const dmg = this.damage * m.dmgScale + // for (let i = 0, len = whom.length; i < len; i++) { + // const mobHit = (who) => { + // if (who.alive) { + // if (!this.isAttached && !who.isMobBullet) this.isPopping = true + // who.damage(dmg); + // // if (who.shield) this.scale(Math.max(0.9, 0.99 - 0.5 / m.plasmaBall.circleRadius)) + // if (who.speed > 5) { + // Matter.Body.setVelocity(who, { //friction + // x: who.velocity.x * 0.6, + // y: who.velocity.y * 0.6 + // }); + // } else { + // Matter.Body.setVelocity(who, { //friction + // x: who.velocity.x * 0.93, + // y: who.velocity.y * 0.93 + // }); + // } + // } + // } + // mobHit(whom[i].bodyA) + // mobHit(whom[i].bodyB) + // } + + //damage nearby mobs + const dmg = this.damage * m.dmgScale + const arcList = [] + const damageRadius = circleRadiusScale * this.circleRadius + const dischargeRange = 150 + 1600 * tech.plasmaDischarge + 1.3 * damageRadius + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].alive && (!mob[i].isBadTarget || mob[i].isMobBullet) && !mob[i].isInvulnerable) { + const sub = Vector.magnitude(Vector.sub(this.position, mob[i].position)) + if (sub < damageRadius + mob[i].radius) { + // if (!this.isAttached && !mob[i].isMobBullet) this.isPopping = true + mob[i].damage(dmg); + if (mob[i].speed > 5) { + Matter.Body.setVelocity(mob[i], { //friction + x: mob[i].velocity.x * 0.6, + y: mob[i].velocity.y * 0.6 + }); + } else { + Matter.Body.setVelocity(mob[i], { //friction + x: mob[i].velocity.x * 0.93, + y: mob[i].velocity.y * 0.93 + }); + } + } else if (sub < dischargeRange + mob[i].radius && Matter.Query.ray(map, mob[i].position, this.position).length === 0) { + arcList.push(mob[i]) //populate electrical arc list + } + } + } + for (let i = 0; i < arcList.length; i++) { + if (tech.plasmaDischarge > Math.random()) { + const who = arcList[Math.floor(Math.random() * arcList.length)] + who.damage(dmg * 4); + //draw arcs + const sub = Vector.sub(who.position, this.position) + const unit = Vector.normalise(sub) + let len = 12 + const step = Vector.magnitude(sub) / (len + 2) + let x = this.position.x + let y = this.position.y + ctx.beginPath(); + ctx.moveTo(x, y); + for (let i = 0; i < len; i++) { + x += step * (unit.x + (Math.random() - 0.5)) + y += step * (unit.y + (Math.random() - 0.5)) + ctx.lineTo(x, y); + } + ctx.lineTo(who.position.x, who.position.y); + ctx.strokeStyle = "#88f"; + ctx.lineWidth = 4 + 3 * Math.random(); + ctx.stroke(); + if (who.damageReduction) { + simulation.drawList.push({ + x: who.position.x, + y: who.position.y, + radius: 15, + color: "rgba(150,150,255,0.4)", + time: 15 }); } } } + + + //slowly slow down if too fast + if (this.speed > 10) { + const scale = 0.998 + Matter.Body.setVelocity(this, { + x: scale * this.velocity.x, + y: scale * this.velocity.y + }); + } + + //graphics + const radius = circleRadiusScale * this.circleRadius * (0.99 + 0.02 * Math.random()) + 3 * Math.random() + const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 0, this.position.x, this.position.y, radius); + const alpha = this.alpha + 0.1 * Math.random() + gradient.addColorStop(0, `rgba(255,255,255,${alpha})`); + gradient.addColorStop(0.35 + 0.1 * Math.random(), `rgba(255,150,255,${alpha})`); + gradient.addColorStop(1, `rgba(255,0,255,${alpha})`); + // gradient.addColorStop(1, `rgba(255,150,255,${alpha})`); + ctx.fillStyle = gradient + ctx.beginPath(); + ctx.arc(this.position.x, this.position.y, radius, 0, 2 * Math.PI); + ctx.fill(); + //draw arcs + const unit = Vector.rotate({ + x: 1, + y: 0 + }, Math.random() * 6.28) + let len = 8 + const step = this.circleRadius / len + let x = this.position.x + let y = this.position.y + ctx.beginPath(); + if (Math.random() < 0.5) { + x += step * (unit.x + 6 * (Math.random() - 0.5)) + y += step * (unit.y + 6 * (Math.random() - 0.5)) + len -= 2 + } + if (Math.random() < 0.5) { + x += step * (unit.x + 6 * (Math.random() - 0.5)) + y += step * (unit.y + 6 * (Math.random() - 0.5)) + len -= 2 + } + ctx.moveTo(x, y); + + for (let i = 0; i < len; i++) { + x += step * (unit.x + 1.9 * (Math.random() - 0.5)) + y += step * (unit.y + 1.9 * (Math.random() - 0.5)) + ctx.lineTo(x, y); + } + ctx.strokeStyle = "#88f"; + ctx.lineWidth = 2 * Math.random(); + ctx.stroke(); } - } - } - m.hold = function() { - const wave = Math.sin(m.cycle * 0.022); - m.fieldRange = 180 + 12 * wave + 100 * tech.isBigField - m.fieldArc = 0.35 + 0.045 * wave + 0.065 * tech.isBigField //run calculateFieldThreshold after setting fieldArc, used for powerUp grab and mobPush with lookingAt(mob) - m.calculateFieldThreshold(); + }, + }); + + Composite.add(engine.world, m.plasmaBall); + // m.plasmaBall.startingVertices = m.plasmaBall.vertices.slice(); + m.hold = function () { if (m.isHolding) { m.drawHold(m.holdingTarget); m.holding(); m.throwBlock(); - } else if (input.field) { //not hold but field button is pressed - //float while field is on - const angleReduction = 0.5 + 0.7 * (Math.PI / 2 - Math.min(Math.PI / 2, Math.abs(m.angle + Math.PI / 2))) - // console.log(angleReduction) - if (player.velocity.y > 1) { - player.force.y -= angleReduction * (tech.isBigField ? 0.95 : 0.5) * player.mass * simulation.g; - Matter.Body.setVelocity(player, { - x: player.velocity.x, - y: 0.98 * player.velocity.y - }); //set velocity to cap, but keep the direction - } - - - - + } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen m.grabPowerUp(); m.lookForPickUp(); - m.fieldPosition = { - x: m.pos.x, - y: m.pos.y - } - m.fieldAngle = m.angle - //draw field attached to player - if (m.holdingTarget) { - ctx.fillStyle = `rgba(110,150,220, ${0.06 + 0.03 * Math.random()})` - ctx.strokeStyle = `rgba(110,150,220, ${0.35 + 0.05 * Math.random()})` + + //field is active + if (!m.plasmaBall.isAttached) { //return ball to player + if (m.plasmaBall.isOn) { + m.plasmaBall.isPopping = true + } else { + m.plasmaBall.isAttached = true + m.plasmaBall.isOn = true + m.plasmaBall.isPopping = false + m.plasmaBall.alpha = 0.7 + m.plasmaBall.setPositionToNose() + // m.plasmaBall.reset() + + } + // const scale = 0.7 + // Matter.Body.scale(m.plasmaBall, scale, scale); //shrink fast + // if (m.plasmaBall.circleRadius < m.plasmaBall.radiusLimit) { + // m.plasmaBall.isAttached = true + // m.plasmaBall.isOn = true + // m.plasmaBall.setPositionToNose() + // } + } else if (m.energy > m.plasmaBall.drain) { //charge up when attached + if (tech.isCapacitor) { + m.energy -= m.plasmaBall.drain * 2; + const scale = 1 + 48 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8) + Matter.Body.scale(m.plasmaBall, scale, scale); //grow + } else { + m.energy -= m.plasmaBall.drain; + const scale = 1 + 16 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8) + Matter.Body.scale(m.plasmaBall, scale, scale); //grow + } + if (m.energy > m.maxEnergy) { + m.energy -= m.plasmaBall.drain * 2; + const scale = 1 + 16 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8) + Matter.Body.scale(m.plasmaBall, scale, scale); //grow + } + m.plasmaBall.setPositionToNose() + + //add friction for player when holding ball, more friction in vertical + // const floatScale = Math.sqrt(m.plasmaBall.circleRadius) + // const friction = 0.0002 * floatScale + // const slowY = (player.velocity.y > 0) ? Math.max(0.8, 1 - friction * player.velocity.y * player.velocity.y) : Math.max(0.98, 1 - friction * Math.abs(player.velocity.y)) //down : up + // Matter.Body.setVelocity(player, { + // x: Math.max(0.95, 1 - friction * Math.abs(player.velocity.x)) * player.velocity.x, + // y: slowY * player.velocity.y + // }); + + // if (player.velocity.y > 7) player.force.y -= 0.95 * player.mass * simulation.g //less gravity when falling fast + // player.force.y -= Math.min(0.95, 0.05 * floatScale) * player.mass * simulation.g; //undo some gravity on up or down + + //float + const slowY = (player.velocity.y > 0) ? Math.max(0.8, 1 - 0.002 * player.velocity.y * player.velocity.y) : Math.max(0.98, 1 - 0.001 * Math.abs(player.velocity.y)) //down : up + Matter.Body.setVelocity(player, { + x: Math.max(0.95, 1 - 0.003 * Math.abs(player.velocity.x)) * player.velocity.x, + y: slowY * player.velocity.y + }); + if (player.velocity.y > 5) { + player.force.y -= 0.9 * player.mass * simulation.g //less gravity when falling fast + } else { + player.force.y -= 0.5 * player.mass * simulation.g; + } } else { - ctx.fillStyle = `rgba(110,150,220, ${0.27 + 0.2 * Math.random() - 0.1 * wave})` - ctx.strokeStyle = `rgba(110,150,220, ${0.4 + 0.5 * Math.random()})` + m.fieldCDcycle = m.cycle + 90; + m.plasmaBall.fire() } - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, m.fieldRange, m.angle - Math.PI * m.fieldArc, m.angle + Math.PI * m.fieldArc, false); - ctx.lineWidth = 2.5 - 1.5 * wave; - ctx.stroke(); - const curve = 0.57 + 0.04 * wave - const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc - let a = m.angle + aMag - let cp1x = m.pos.x + curve * m.fieldRange * Math.cos(a) - let cp1y = m.pos.y + curve * m.fieldRange * Math.sin(a) - ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 30 * Math.cos(m.angle), m.pos.y + 30 * Math.sin(m.angle)) - a = m.angle - aMag - cp1x = m.pos.x + curve * m.fieldRange * Math.cos(a) - cp1y = m.pos.y + curve * m.fieldRange * Math.sin(a) - ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 1 * m.fieldRange * Math.cos(m.angle - Math.PI * m.fieldArc), m.pos.y + 1 * m.fieldRange * Math.sin(m.angle - Math.PI * m.fieldArc)) - ctx.fill(); - m.perfectPush(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + if (m.plasmaBall.isAttached) { + m.fieldCDcycle = m.cycle + 30; + m.plasmaBall.fire() + } + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + if (m.plasmaBall.isAttached) { + m.fieldCDcycle = m.cycle + 30; + m.plasmaBall.fire() + } + } + m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") + m.plasmaBall.do() + } + } else if (tech.isExtruder) { + m.hold = function () { + b.isExtruderOn = false + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + b.extruder(); } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released m.pickUp(); } else { m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - if (!input.field) { //&& tech.isFieldFree - //draw field free of player - ctx.fillStyle = `rgba(110,150,220, ${0.27 + 0.2 * Math.random() - 0.1 * wave})` - ctx.strokeStyle = `rgba(110,180,255, ${0.4 + 0.5 * Math.random()})` - ctx.beginPath(); - ctx.arc(m.fieldPosition.x, m.fieldPosition.y, m.fieldRange, m.fieldAngle - Math.PI * m.fieldArc, m.fieldAngle + Math.PI * m.fieldArc, false); - ctx.lineWidth = 2.5 - 1.5 * wave; - ctx.stroke(); - const curve = 0.8 + 0.06 * wave - const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc - let a = m.fieldAngle + aMag - ctx.quadraticCurveTo(m.fieldPosition.x + curve * m.fieldRange * Math.cos(a), m.fieldPosition.y + curve * m.fieldRange * Math.sin(a), m.fieldPosition.x + 1 * m.fieldRange * Math.cos(m.fieldAngle - Math.PI * m.fieldArc), m.fieldPosition.y + 1 * m.fieldRange * Math.sin(m.fieldAngle - Math.PI * m.fieldArc)) - ctx.fill(); - m.perfectPush(true); - } } - // m.drawRegenEnergy() - m.drawRegenEnergy("rgba(0,0,0,0.2)") - if (tech.isPerfectBrake) { //cap mob speed around player - const range = 200 + 140 * wave + 150 * m.energy - for (let i = 0; i < mob.length; i++) { - const distance = Vector.magnitude(Vector.sub(m.pos, mob[i].position)) - if (distance < range) { - const cap = mob[i].isShielded ? 8 : 4 - if (mob[i].speed > cap && Vector.dot(mob[i].velocity, Vector.sub(m.pos, mob[i].position)) > 0) { // if velocity is directed towards player - Matter.Body.setVelocity(mob[i], Vector.mult(Vector.normalise(mob[i].velocity), cap)); //set velocity to cap, but keep the direction - } + m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") + if (input.field) { + b.wasExtruderOn = true + } else { + b.wasExtruderOn = false + b.canExtruderFire = true + } + ctx.beginPath(); //draw all the wave bullets + for (let i = 1, len = bullet.length; i < len; i++) { //skip the first bullet (which is is oldest bullet) + if (bullet[i].isWave) { + if (bullet[i].isBranch || bullet[i - 1].isBranch) { + ctx.moveTo(bullet[i].position.x, bullet[i].position.y) + } else { + ctx.lineTo(bullet[i].position.x, bullet[i].position.y) } } - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, range, 0, 2 * Math.PI); - ctx.fillStyle = "hsla(200,50%,61%,0.08)"; - ctx.fill(); } + if (b.wasExtruderOn && b.isExtruderOn) ctx.lineTo(m.pos.x + 15 * Math.cos(m.angle), m.pos.y + 15 * Math.sin(m.angle)) + ctx.lineWidth = 4; + ctx.strokeStyle = "#f07" + ctx.stroke(); + ctx.lineWidth = tech.extruderRange; + ctx.strokeStyle = "rgba(255,0,110,0.06)" + ctx.stroke(); } - } - }, - { - name: "negative mass", - //
hold blocks as if they have a lower mass - description: "use energy to nullify  gravity
+55% defense
generate 6 energy per second", - fieldDrawRadius: 0, - effect: () => { - m.fieldFire = true; - m.holdingMassScale = 0.01; //can hold heavier blocks with lower cost to jumping - m.fieldMeterColor = "#333" - m.eyeFillColor = m.fieldMeterColor - m.fieldHarmReduction = 0.45; //55% reduction - m.fieldDrawRadius = 0; - - m.hold = function() { - m.airSpeedLimit = 125 //5 * player.mass * player.mass - m.FxAir = 0.016 + } else { + m.hold = function () { if (m.isHolding) { m.drawHold(m.holdingTarget); m.holding(); m.throwBlock(); - } else if (input.field && m.fieldCDcycle < m.cycle) { //push away + } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen m.grabPowerUp(); m.lookForPickUp(); - const DRAIN = 0.00035 - if (m.energy > DRAIN) { - if (tech.isFlyFaster) { - //look for nearby objects to make zero-g - function moveThis(who, range, mag = 1.06) { - for (let i = 0, len = who.length; i < len; ++i) { - sub = Vector.sub(who[i].position, m.pos); - dist = Vector.magnitude(sub); - if (dist < range) { - who[i].force.y -= who[i].mass * (simulation.g * mag); //add a bit more then standard gravity - if (input.left) { //blocks move horizontally with the same force as the player - who[i].force.x -= m.FxAir * who[i].mass / 10; // move player left / a - } else if (input.right) { - who[i].force.x += m.FxAir * who[i].mass / 10; //move player right / d - } - //loose attraction to player - // const sub = Vector.sub(m.pos, body[i].position) - // const unit = Vector.mult(Vector.normalise(sub), who[i].mass * 0.0000002 * Vector.magnitude(sub)) - // body[i].force.x += unit.x - // body[i].force.y += unit.y + b.plasma(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + } + m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") + } + } + }, + effect() { + m.fieldMeterColor = "#f0f" + m.eyeFillColor = m.fieldMeterColor + this.set(); + } + }, + { + name: "time dilation", + description: "use energy to stop time
+20% movement and fire rate
generate 12 energy per second", + set() { + // m.fieldMeterColor = "#0fc" + // m.fieldMeterColor = "#ff0" + m.fieldMeterColor = "#3fe" + m.eyeFillColor = m.fieldMeterColor + m.fieldFx = 1.25 + // m.fieldJump = 1.09 + m.setMovement(); + b.setFireCD() + const timeStop = () => { + m.immuneCycle = m.cycle + 10; //immune to harm while time is stopped, this also disables regen + //draw field everywhere + ctx.globalCompositeOperation = "saturation" + ctx.fillStyle = "#ccc"; + ctx.fillRect(-50000, -50000, 100000, 100000) + ctx.globalCompositeOperation = "source-over" + //stop time + m.isBodiesAsleep = true; + + function sleep(who) { + for (let i = 0, len = who.length; i < len; ++i) { + if (!who[i].isSleeping) { + who[i].storeVelocity = who[i].velocity + who[i].storeAngularVelocity = who[i].angularVelocity + } + Matter.Sleeping.set(who[i], true) + } + } + sleep(mob); + sleep(body); + sleep(bullet); + simulation.cycle--; //pause all functions that depend on game cycle increasing + } + if (tech.isRewindField) { + this.rewindCount = 0 + m.grabPowerUpRange2 = 300000 + m.hold = function () { + // console.log(m.fieldCDcycle) + m.grabPowerUp(); + // //grab power ups + // for (let i = 0, len = powerUp.length; i < len; ++i) { + // if ( + // Vector.magnitudeSquared(Vector.sub(m.pos, powerUp[i].position)) < 100000 && + // !simulation.isChoosing && + // (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal) + // ) { + // powerUps.onPickUp(powerUp[i]); + // powerUp[i].effect(); + // Matter.Composite.remove(engine.world, powerUp[i]); + // powerUp.splice(i, 1); + // break; //because the array order is messed up after splice + // } + // } + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + m.wakeCheck(); + } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + const drain = 0.002 / (1 + 0.5 * m.coupling) + if (m.energy > drain) m.energy -= drain + + m.grabPowerUp(); + if (this.rewindCount === 0) m.lookForPickUp(); + + if (!m.holdingTarget) { + this.rewindCount += 6; + const DRAIN = 0.003 + let history = m.history[(m.cycle - this.rewindCount) % 600] + if (this.rewindCount > 599 || m.energy < DRAIN) { + this.rewindCount = 0; + m.resetHistory(); + if (m.fireCDcycle < m.cycle + 60) m.fieldCDcycle = m.cycle + 60 + m.immuneCycle = m.cycle //if you reach the end of the history disable harm immunity + } else { + //draw field everywhere + ctx.globalCompositeOperation = "saturation" + ctx.fillStyle = "#ccc"; + ctx.fillRect(-100000, -100000, 200000, 200000) + ctx.globalCompositeOperation = "source-over" + // m.grabPowerUp(); //a second grab power up to make the power ups easier to grab, and they more fast which matches the time theme + m.energy -= DRAIN + if (m.immuneCycle < m.cycle + 60) m.immuneCycle = m.cycle + 60; //player is immune to damage for __ cycles + Matter.Body.setPosition(player, history.position); + Matter.Body.setVelocity(player, { + x: history.velocity.x, + y: history.velocity.y + }); + if (m.health < history.health) { + m.health = history.health + if (m.health > m.maxHealth) m.health = m.maxHealth + m.displayHealth(); + } + m.yOff = history.yOff + if (m.yOff < 48) { + m.doCrouch() + } else { + m.undoCrouch() + } + if (!(this.rewindCount % 30)) { + if (tech.isRewindBot) { + for (let i = 0; i < tech.isRewindBot; i++) { + b.randomBot(m.pos, false, false) + bullet[bullet.length - 1].endCycle = simulation.cycle + 480 + Math.floor(120 * Math.random()) //8-9 seconds } } - } - //control horizontal acceleration - m.airSpeedLimit = 1000 // 7* player.mass * player.mass - m.FxAir = 0.01 - //control vertical acceleration - if (input.down) { //down - player.force.y += 0.5 * player.mass * simulation.g; - this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 500 * 0.03; - moveThis(powerUp, this.fieldDrawRadius, 0); - moveThis(body, this.fieldDrawRadius, 0); - } else if (input.up) { //up - m.energy -= 5 * DRAIN; - this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 1100 * 0.03; - player.force.y -= 2.25 * player.mass * simulation.g; - moveThis(powerUp, this.fieldDrawRadius, 1.8); - moveThis(body, this.fieldDrawRadius, 1.8); - } else { - m.energy -= DRAIN; - this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 800 * 0.03; - player.force.y -= 1.07 * player.mass * simulation.g; // slow upward drift - moveThis(powerUp, this.fieldDrawRadius); - moveThis(body, this.fieldDrawRadius); - } - } else { - //look for nearby objects to make zero-g - function verticalForce(who, range, mag = 1.06) { - for (let i = 0, len = who.length; i < len; ++i) { - sub = Vector.sub(who[i].position, m.pos); - dist = Vector.magnitude(sub); - if (dist < range) who[i].force.y -= who[i].mass * (simulation.g * mag); + if (tech.isRewindGrenade) { + b.grenade(m.pos, this.rewindCount) //Math.PI / 2 + const who = bullet[bullet.length - 1] + who.endCycle = simulation.cycle + 60 } } - //control horizontal acceleration - m.airSpeedLimit = 400 // 7* player.mass * player.mass - m.FxAir = 0.005 - //control vertical acceleration - if (input.down) { //down - player.force.y -= 0.5 * player.mass * simulation.g; - this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 400 * 0.03; - verticalForce(powerUp, this.fieldDrawRadius, 0.7); - verticalForce(body, this.fieldDrawRadius, 0.7); - } else if (input.up) { //up - m.energy -= 5 * DRAIN; - this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 850 * 0.03; - player.force.y -= 1.45 * player.mass * simulation.g; - verticalForce(powerUp, this.fieldDrawRadius, 1.38); - verticalForce(body, this.fieldDrawRadius, 1.38); - } else { - m.energy -= DRAIN; - this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 650 * 0.03; - player.force.y -= 1.07 * player.mass * simulation.g; // slow upward drift - verticalForce(powerUp, this.fieldDrawRadius); - verticalForce(body, this.fieldDrawRadius); - } - } - - if (m.energy < 0.001) { - m.fieldCDcycle = m.cycle + 120; - m.energy = 0; - } - //add extra friction for horizontal motion - if (input.down || input.up || input.left || input.right) { - Matter.Body.setVelocity(player, { - x: player.velocity.x * 0.99, - y: player.velocity.y * 0.98 - }); - } else { //slow rise and fall - Matter.Body.setVelocity(player, { - x: player.velocity.x * 0.99, - y: player.velocity.y * 0.98 - }); - } - // if (tech.isFreezeMobs) { - // const ICE_DRAIN = 0.0005 - // for (let i = 0, len = mob.length; i < len; i++) { - // if (!mob[i].isMobBullet && !mob[i].shield && !mob[i].isShielded && ((mob[i].distanceToPlayer() + mob[i].radius) < this.fieldDrawRadius)) { - // if (m.energy > ICE_DRAIN * 2) { - // m.energy -= ICE_DRAIN; - // this.fieldDrawRadius -= 2; - // mobs.statusSlow(mob[i], 60) - // } else { - // break; - // } - // } - // } - // } - //draw zero-G range - if (!simulation.isTimeSkipping) { - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, this.fieldDrawRadius, 0, 2 * Math.PI); - ctx.fillStyle = "#f5f5ff"; - ctx.globalCompositeOperation = "difference"; - ctx.fill(); - ctx.globalCompositeOperation = "source-over"; } } + m.wakeCheck(); } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released m.pickUp(); - this.fieldDrawRadius = 0 + this.rewindCount = 0; + m.wakeCheck(); + } else if (tech.isTimeStop && player.speed < 1 && m.onGround && !input.fire) { + timeStop(); + this.rewindCount = 0; } else { m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - this.fieldDrawRadius = 0 + this.rewindCount = 0; + m.wakeCheck(); } - m.drawRegenEnergy("rgba(0,0,0,0.2)") + m.drawRegenEnergy() // this calls m.regenEnergy(); also } - } - }, - { - name: "molecular assembler", - description: `excess energy used to build ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
use energy to deflect mobs
generate 12 energy per second`, - // simulation.molecularMode: Math.floor(4 * Math.random()), //0 spores, 1 missile, 2 ice IX, 3 drones - setDescription() { - return `excess energy used to build ${simulation.molecularMode === 0 ? "spores" : simulation.molecularMode === 1 ? "missiles" : simulation.molecularMode === 2 ? "ice IX" : "drones"}
use energy to deflect mobs
generate 12 energy per second` - }, - effect: () => { - m.fieldMeterColor = "#ff0" - m.eyeFillColor = m.fieldMeterColor - m.hold = function() { - if (m.energy > m.maxEnergy - 0.02 && m.fieldCDcycle < m.cycle && !input.field && bullet.length < 300 && (m.cycle % 2)) { - if (simulation.molecularMode === 0) { - if (tech.isSporeFlea) { - const drain = 0.18 + (Math.max(bullet.length, 130) - 130) * 0.02 - if (m.energy > drain) { - m.energy -= drain - const speed = m.crouch ? 20 + 8 * Math.random() : 10 + 3 * Math.random() - b.flea({ - x: m.pos.x + 35 * Math.cos(m.angle), - y: m.pos.y + 35 * Math.sin(m.angle) - }, { - x: speed * Math.cos(m.angle), - y: speed * Math.sin(m.angle) - }) - } - } else if (tech.isSporeWorm) { - const drain = 0.18 + (Math.max(bullet.length, 130) - 130) * 0.02 - if (m.energy > drain) { - m.energy -= drain - b.worm({ - x: m.pos.x + 35 * Math.cos(m.angle), - y: m.pos.y + 35 * Math.sin(m.angle) - }) - const SPEED = 2 + 1 * Math.random(); - Matter.Body.setVelocity(bullet[bullet.length - 1], { - x: SPEED * Math.cos(m.angle), - y: SPEED * Math.sin(m.angle) - }); - } - } else { - const drain = 0.095 + (Math.max(bullet.length, 130) - 130) * 0.01 - for (let i = 0, len = Math.random() * 20; i < len; i++) { - if (m.energy > 3 * drain) { - m.energy -= drain - b.spore(m.pos) - } else { - break - } - } - } - } else if (simulation.molecularMode === 1) { - m.energy -= 0.33; - const direction = { - x: Math.cos(m.angle), - y: Math.sin(m.angle) - } - const push = Vector.mult(Vector.perp(direction), 0.08) - b.missile({ - x: m.pos.x + 30 * direction.x, - y: m.pos.y + 30 * direction.y - }, m.angle, -15) - bullet[bullet.length - 1].force.x += push.x * (Math.random() - 0.5) - bullet[bullet.length - 1].force.y += 0.005 + push.y * (Math.random() - 0.5) - // b.missile({ x: m.pos.x, y: m.pos.y - 40 }, -Math.PI / 2 + 0.5 * (Math.random() - 0.5), 0, 1) - } else if (simulation.molecularMode === 2) { - m.energy -= 0.045; - b.iceIX(1) - } else if (simulation.molecularMode === 3) { - if (tech.isDroneRadioactive) { - const drain = 0.8 + (Math.max(bullet.length, 50) - 50) * 0.01 - if (m.energy > drain) { - m.energy -= drain - b.droneRadioactive({ - x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), - y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) - }, 25) - } - } else { - //every bullet above 100 adds 0.005 to the energy cost per drone - //at 200 bullets the energy cost is 0.45 + 100*0.006 = 1.05 - const drain = (0.45 + (Math.max(bullet.length, 100) - 100) * 0.006) * tech.droneEnergyReduction - if (m.energy > drain) { - m.energy -= drain - b.drone() - } - } - } - } - + } else { + m.fieldFire = true; + m.isBodiesAsleep = false; + m.hold = function () { if (m.isHolding) { + m.wakeCheck(); m.drawHold(m.holdingTarget); m.holding(); m.throwBlock(); - } else if ((input.field && m.fieldCDcycle < m.cycle)) { //not hold but field button is pressed - if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + } else if (input.field && m.fieldCDcycle < m.cycle) { + const drain = 0.0026 / (1 + 0.3 * m.coupling) + if (m.energy > drain) m.energy -= drain m.grabPowerUp(); - m.lookForPickUp(); - if (m.energy > 0.05) { - m.drawField(); - m.pushMobsFacing(); + m.lookForPickUp(); //this drains energy 0.001 + if (m.energy > drain) { + timeStop(); + } else { //holding, but field button is released + m.fieldCDcycle = m.cycle + 120; + m.energy = 0; + m.wakeCheck(); + m.wakeCheck(); } + } else if (tech.isTimeStop && player.speed < 1 && m.onGround && m.fireCDcycle < m.cycle && !input.fire) { + timeStop(); + //makes things move at 1/5 time rate, but has an annoying flicker for mob graphics, and other minor bugs + // if (!(m.cycle % 4)) { + // // requestAnimationFrame(() => { + // m.wakeCheck(); + // // simulation.timePlayerSkip(1) + // // }); //wrapping in animation frame prevents errors, probably + // ctx.globalCompositeOperation = "saturation" + // ctx.fillStyle = "#ccc"; + // ctx.fillRect(-100000, -100000, 200000, 200000) + // ctx.globalCompositeOperation = "source-over" + // } else { + // timeStop(); + // } } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.wakeCheck(); m.pickUp(); } else { + m.wakeCheck(); m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) } m.drawRegenEnergy() } } }, - // { - // name: "plasma torch", - // description: "use energy to emit short range plasma
damages and pushes mobs away", - // effect() { - // m.fieldMeterColor = "#f0f" - // m.eyeFillColor = m.fieldMeterColor - // m.hold = function() { - // b.isExtruderOn = false - // if (m.isHolding) { - // m.drawHold(m.holdingTarget); - // m.holding(); - // m.throwBlock(); - // } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - // m.grabPowerUp(); - // m.lookForPickUp(); - // if (tech.isExtruder) { - // b.extruder(); - // } else { - // b.plasma(); - // } - // } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - // m.pickUp(); - // } else { - // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - // } - // m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") - - // if (tech.isExtruder) { - // if (input.field) { - // b.wasExtruderOn = true - // } else { - // b.wasExtruderOn = false - // b.canExtruderFire = true - // } - // ctx.beginPath(); //draw all the wave bullets - // for (let i = 0, len = bullet.length; i < len; i++) { - // if (bullet[i].isWave) { - // if (bullet[i].isBranch) { - // ctx.moveTo(bullet[i].position.x, bullet[i].position.y) - // } else { - // ctx.lineTo(bullet[i].position.x, bullet[i].position.y) - // } - // } - // } - // if (b.wasExtruderOn && b.isExtruderOn) ctx.lineTo(m.pos.x + 15 * Math.cos(m.angle), m.pos.y + 15 * Math.sin(m.angle)) - // ctx.lineWidth = 4; - // ctx.strokeStyle = "#f07" - // ctx.stroke(); - // ctx.lineWidth = tech.extruderRange; - // ctx.strokeStyle = "rgba(255,0,110,0.05)" - // ctx.stroke(); - // } - // } - // } - // }, - { - name: "plasma torch", - description: "use energy to emit short range plasma
damages and pushes mobs away
generate 10 energy per second", - set() { - b.isExtruderOn = false - // m.fieldCDcycleAlternate = 0 - - if (m.plasmaBall) { - m.plasmaBall.reset() - Matter.Composite.remove(engine.world, m.plasmaBall); + effect() { + if (tech.isTimeStop) { + m.fieldHarmReduction = 0.66; //33% reduction + } else { + m.fieldHarmReduction = 1; + } + this.set(); + } + }, + { + name: "metamaterial cloaking", + description: "when not firing activate cloaking
after decloaking +333% damage for 2 s
generate 6 energy per second", + effect: () => { + m.fieldFire = true; + m.fieldMeterColor = "#333"; + m.eyeFillColor = m.fieldMeterColor + m.fieldPhase = 0; + m.isCloak = false + m.fieldDrawRadius = 0 + m.isSneakAttack = true; + m.sneakAttackCycle = 0; + m.enterCloakCycle = 0; + m.drawCloak = function () { + m.fieldPhase += 0.007 + const wiggle = 0.15 * Math.sin(m.fieldPhase * 0.5) + ctx.beginPath(); + ctx.ellipse(m.pos.x, m.pos.y, m.fieldDrawRadius * (1 - wiggle), m.fieldDrawRadius * (1 + wiggle), m.fieldPhase, 0, 2 * Math.PI); + ctx.fillStyle = "#fff" + ctx.lineWidth = 2; + ctx.strokeStyle = "#000" + // ctx.stroke() + ctx.globalCompositeOperation = "destination-in"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + ctx.clip(); + } + m.hold = function () { + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold and field button is pressed + if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen + m.grabPowerUp(); + m.lookForPickUp(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding target exists, and field button is not pressed + m.pickUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) } - if (tech.isPlasmaBall) { - const circleRadiusScale = 2 - m.plasmaBall = Bodies.circle(m.pos.x + 10 * Math.cos(m.angle), m.pos.y + 10 * Math.sin(m.angle), 1, { - // collisionFilter: { - // group: 0, - // category: 0, - // mask: 0 //cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield - // }, - isSensor: true, - frictionAir: 0, - alpha: 0.7, - isPopping: false, - isAttached: false, - isOn: false, - drain: 0.0017, - radiusLimit: 10, - damage: 0.8, - setPositionToNose() { - const nose = { - x: m.pos.x + 10 * Math.cos(m.angle), - y: m.pos.y + 10 * Math.sin(m.angle) - } - Matter.Body.setPosition(this, Vector.add(nose, Vector.mult(Vector.normalise(Vector.sub(nose, m.pos)), circleRadiusScale * this.circleRadius))); - }, - fire() { - this.isAttached = false; - const speed = 10 //scale with mass? - Matter.Body.setVelocity(this, { - x: player.velocity.x * 0.4 + speed * Math.cos(m.angle), - y: speed * Math.sin(m.angle) - }); - m.plasmaBall.setPositionToNose() - if (this.circleRadius < 10) this.isPopping = true - }, - scale(scale) { - Matter.Body.scale(m.plasmaBall, scale, scale); //shrink fast - if (this.circleRadius < this.radiusLimit) this.reset() - }, - reset() { - // console.log(this.circleRadius) - const scale = 1 / m.plasmaBall.circleRadius - Matter.Body.scale(m.plasmaBall, scale, scale); //grow - // console.log(this.circleRadius) - // this.circleRadius = 0 - this.alpha = 0.7 - this.isOn = false - this.isPopping = false - // this.isAttached = true; - }, - do() { - if (this.isOn) { - //collisions with map - if (Matter.Query.collides(this, map).length > 0) { - if (this.isAttached) { - this.scale(Math.max(0.9, 0.998 - 0.1 / m.plasmaBall.circleRadius)) - } else { - this.isPopping = true - } - } - if (this.isPopping) { - this.alpha -= 0.03 - if (this.alpha < 0.1) { - this.reset() - } else { - const scale = 1.04 + 4 / Math.max(1, m.plasmaBall.circleRadius) - Matter.Body.scale(m.plasmaBall, scale, scale); //grow - } - // if (this.speed > 2.5) { - // const slow = 0.9 - // Matter.Body.setVelocity(this, { - // x: slow * this.velocity.x, - // y: slow * this.velocity.y - // }); - // } - } - //collisions with mobs - // const whom = Matter.Query.collides(this, mob) - // const dmg = this.damage * m.dmgScale - // for (let i = 0, len = whom.length; i < len; i++) { - // const mobHit = (who) => { - // if (who.alive) { - // if (!this.isAttached && !who.isMobBullet) this.isPopping = true - // who.damage(dmg); - // // if (who.shield) this.scale(Math.max(0.9, 0.99 - 0.5 / m.plasmaBall.circleRadius)) - // if (who.speed > 5) { - // Matter.Body.setVelocity(who, { //friction - // x: who.velocity.x * 0.6, - // y: who.velocity.y * 0.6 - // }); - // } else { - // Matter.Body.setVelocity(who, { //friction - // x: who.velocity.x * 0.93, - // y: who.velocity.y * 0.93 - // }); - // } - // } - // } - // mobHit(whom[i].bodyA) - // mobHit(whom[i].bodyB) - // } - - //damage nearby mobs - const dmg = this.damage * m.dmgScale - const arcList = [] - const damageRadius = circleRadiusScale * this.circleRadius - const dischargeRange = 150 + 1600 * tech.plasmaDischarge + 1.3 * damageRadius - for (let i = 0, len = mob.length; i < len; i++) { - if (mob[i].alive && (!mob[i].isBadTarget || mob[i].isMobBullet) && !mob[i].isInvulnerable) { - const sub = Vector.magnitude(Vector.sub(this.position, mob[i].position)) - if (sub < damageRadius + mob[i].radius) { - // if (!this.isAttached && !mob[i].isMobBullet) this.isPopping = true - mob[i].damage(dmg); - if (mob[i].speed > 5) { - Matter.Body.setVelocity(mob[i], { //friction - x: mob[i].velocity.x * 0.6, - y: mob[i].velocity.y * 0.6 - }); - } else { - Matter.Body.setVelocity(mob[i], { //friction - x: mob[i].velocity.x * 0.93, - y: mob[i].velocity.y * 0.93 - }); - } - } else if (sub < dischargeRange + mob[i].radius && Matter.Query.ray(map, mob[i].position, this.position).length === 0) { - arcList.push(mob[i]) //populate electrical arc list - } - } - } - for (let i = 0; i < arcList.length; i++) { - if (tech.plasmaDischarge > Math.random()) { - const who = arcList[Math.floor(Math.random() * arcList.length)] - who.damage(dmg * 4); - //draw arcs - const sub = Vector.sub(who.position, this.position) - const unit = Vector.normalise(sub) - let len = 12 - const step = Vector.magnitude(sub) / (len + 2) - let x = this.position.x - let y = this.position.y - ctx.beginPath(); - ctx.moveTo(x, y); - for (let i = 0; i < len; i++) { - x += step * (unit.x + (Math.random() - 0.5)) - y += step * (unit.y + (Math.random() - 0.5)) - ctx.lineTo(x, y); - } - ctx.lineTo(who.position.x, who.position.y); - ctx.strokeStyle = "#88f"; - ctx.lineWidth = 4 + 3 * Math.random(); - ctx.stroke(); - if (who.damageReduction) { - simulation.drawList.push({ - x: who.position.x, - y: who.position.y, - radius: 15, - color: "rgba(150,150,255,0.4)", - time: 15 - }); - } - } - } - - - //slowly slow down if too fast - if (this.speed > 10) { - const scale = 0.998 - Matter.Body.setVelocity(this, { - x: scale * this.velocity.x, - y: scale * this.velocity.y - }); - } - - //graphics - const radius = circleRadiusScale * this.circleRadius * (0.99 + 0.02 * Math.random()) + 3 * Math.random() - const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 0, this.position.x, this.position.y, radius); - const alpha = this.alpha + 0.1 * Math.random() - gradient.addColorStop(0, `rgba(255,255,255,${alpha})`); - gradient.addColorStop(0.35 + 0.1 * Math.random(), `rgba(255,150,255,${alpha})`); - gradient.addColorStop(1, `rgba(255,0,255,${alpha})`); - // gradient.addColorStop(1, `rgba(255,150,255,${alpha})`); - ctx.fillStyle = gradient - ctx.beginPath(); - ctx.arc(this.position.x, this.position.y, radius, 0, 2 * Math.PI); - ctx.fill(); - //draw arcs - const unit = Vector.rotate({ - x: 1, - y: 0 - }, Math.random() * 6.28) - let len = 8 - const step = this.circleRadius / len - let x = this.position.x - let y = this.position.y - ctx.beginPath(); - if (Math.random() < 0.5) { - x += step * (unit.x + 6 * (Math.random() - 0.5)) - y += step * (unit.y + 6 * (Math.random() - 0.5)) - len -= 2 - } - if (Math.random() < 0.5) { - x += step * (unit.x + 6 * (Math.random() - 0.5)) - y += step * (unit.y + 6 * (Math.random() - 0.5)) - len -= 2 - } - ctx.moveTo(x, y); - - for (let i = 0; i < len; i++) { - x += step * (unit.x + 1.9 * (Math.random() - 0.5)) - y += step * (unit.y + 1.9 * (Math.random() - 0.5)) - ctx.lineTo(x, y); - } - ctx.strokeStyle = "#88f"; - ctx.lineWidth = 2 * Math.random(); - ctx.stroke(); - } - }, - }); - - Composite.add(engine.world, m.plasmaBall); - // m.plasmaBall.startingVertices = m.plasmaBall.vertices.slice(); - m.hold = function() { - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen - m.grabPowerUp(); - m.lookForPickUp(); - - //field is active - if (!m.plasmaBall.isAttached) { //return ball to player - if (m.plasmaBall.isOn) { - m.plasmaBall.isPopping = true - } else { - m.plasmaBall.isAttached = true - m.plasmaBall.isOn = true - m.plasmaBall.isPopping = false - m.plasmaBall.alpha = 0.7 - m.plasmaBall.setPositionToNose() - // m.plasmaBall.reset() - - } - // const scale = 0.7 - // Matter.Body.scale(m.plasmaBall, scale, scale); //shrink fast - // if (m.plasmaBall.circleRadius < m.plasmaBall.radiusLimit) { - // m.plasmaBall.isAttached = true - // m.plasmaBall.isOn = true - // m.plasmaBall.setPositionToNose() - // } - } else if (m.energy > m.plasmaBall.drain) { //charge up when attached - if (tech.isCapacitor) { - m.energy -= m.plasmaBall.drain * 2; - const scale = 1 + 48 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8) - Matter.Body.scale(m.plasmaBall, scale, scale); //grow - } else { - m.energy -= m.plasmaBall.drain; - const scale = 1 + 16 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8) - Matter.Body.scale(m.plasmaBall, scale, scale); //grow - } - if (m.energy > m.maxEnergy) { - m.energy -= m.plasmaBall.drain * 2; - const scale = 1 + 16 * Math.pow(Math.max(1, m.plasmaBall.circleRadius), -1.8) - Matter.Body.scale(m.plasmaBall, scale, scale); //grow - } - m.plasmaBall.setPositionToNose() - - //add friction for player when holding ball, more friction in vertical - // const floatScale = Math.sqrt(m.plasmaBall.circleRadius) - // const friction = 0.0002 * floatScale - // const slowY = (player.velocity.y > 0) ? Math.max(0.8, 1 - friction * player.velocity.y * player.velocity.y) : Math.max(0.98, 1 - friction * Math.abs(player.velocity.y)) //down : up - // Matter.Body.setVelocity(player, { - // x: Math.max(0.95, 1 - friction * Math.abs(player.velocity.x)) * player.velocity.x, - // y: slowY * player.velocity.y - // }); - - // if (player.velocity.y > 7) player.force.y -= 0.95 * player.mass * simulation.g //less gravity when falling fast - // player.force.y -= Math.min(0.95, 0.05 * floatScale) * player.mass * simulation.g; //undo some gravity on up or down - - //float - const slowY = (player.velocity.y > 0) ? Math.max(0.8, 1 - 0.002 * player.velocity.y * player.velocity.y) : Math.max(0.98, 1 - 0.001 * Math.abs(player.velocity.y)) //down : up - Matter.Body.setVelocity(player, { - x: Math.max(0.95, 1 - 0.003 * Math.abs(player.velocity.x)) * player.velocity.x, - y: slowY * player.velocity.y + //not shooting (or using field) enable cloak + if (m.energy < 0.05 && m.fireCDcycle < m.cycle && !input.fire) m.fireCDcycle = m.cycle + if (m.fireCDcycle + 30 < m.cycle && !input.fire) { //automatically cloak if not firing + const drain = 0.03 + if (!m.isCloak && m.energy > drain + 0.03) { + m.energy -= drain + m.isCloak = true //enter cloak + m.enterCloakCycle = m.cycle + if (tech.isCloakHealLastHit && m.lastHit > 0) { + const heal = Math.min(0.75 * m.lastHit, m.energy) + if (m.energy > heal) { + m.energy -= heal + m.addHealth(heal); //heal from last hit + m.lastHit = 0 + simulation.drawList.push({ //add dmg to draw queue + x: m.pos.x, + y: m.pos.y, + radius: Math.sqrt(heal) * 200, + color: "rgba(0,255,200,0.6)", + time: 16 }); - if (player.velocity.y > 5) { - player.force.y -= 0.9 * player.mass * simulation.g //less gravity when falling fast - } else { - player.force.y -= 0.5 * player.mass * simulation.g; - } - } else { - m.fieldCDcycle = m.cycle + 90; - m.plasmaBall.fire() - } - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - if (m.plasmaBall.isAttached) { - m.fieldCDcycle = m.cycle + 30; - m.plasmaBall.fire() - } - } else { - m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - if (m.plasmaBall.isAttached) { - m.fieldCDcycle = m.cycle + 30; - m.plasmaBall.fire() } } - m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") - m.plasmaBall.do() - } - } else if (tech.isExtruder) { - m.hold = function() { - b.isExtruderOn = false - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen - m.grabPowerUp(); - m.lookForPickUp(); - b.extruder(); - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - } else { - m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - } - m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") - if (input.field) { - b.wasExtruderOn = true - } else { - b.wasExtruderOn = false - b.canExtruderFire = true - } - ctx.beginPath(); //draw all the wave bullets - for (let i = 1, len = bullet.length; i < len; i++) { //skip the first bullet (which is is oldest bullet) - if (bullet[i].isWave) { - if (bullet[i].isBranch || bullet[i - 1].isBranch) { - ctx.moveTo(bullet[i].position.x, bullet[i].position.y) - } else { - ctx.lineTo(bullet[i].position.x, bullet[i].position.y) - } - } - } - if (b.wasExtruderOn && b.isExtruderOn) ctx.lineTo(m.pos.x + 15 * Math.cos(m.angle), m.pos.y + 15 * Math.sin(m.angle)) - ctx.lineWidth = 4; - ctx.strokeStyle = "#f07" - ctx.stroke(); - ctx.lineWidth = tech.extruderRange; - ctx.strokeStyle = "rgba(255,0,110,0.06)" - ctx.stroke(); - } - } else { - m.hold = function() { - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen - m.grabPowerUp(); - m.lookForPickUp(); - b.plasma(); - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - } else { - m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - } - m.drawRegenEnergy("rgba(0, 0, 0, 0.2)") - } - } - }, - effect() { - m.fieldMeterColor = "#f0f" - m.eyeFillColor = m.fieldMeterColor - this.set(); - } - }, - { - name: "time dilation", - description: "use energy to stop time
+20% movement and fire rate
generate 14 energy per second", - set() { - // m.fieldMeterColor = "#0fc" - // m.fieldMeterColor = "#ff0" - m.fieldMeterColor = "#3fe" - m.eyeFillColor = m.fieldMeterColor - m.fieldFx = 1.25 - // m.fieldJump = 1.09 - m.setMovement(); - b.setFireCD() - const timeStop = () => { - m.immuneCycle = m.cycle + 10; //immune to harm while time is stopped, this also disables regen - //draw field everywhere - ctx.globalCompositeOperation = "saturation" - ctx.fillStyle = "#ccc"; - ctx.fillRect(-50000, -50000, 100000, 100000) - ctx.globalCompositeOperation = "source-over" - //stop time - m.isBodiesAsleep = true; - - function sleep(who) { - for (let i = 0, len = who.length; i < len; ++i) { - if (!who[i].isSleeping) { - who[i].storeVelocity = who[i].velocity - who[i].storeAngularVelocity = who[i].angularVelocity - } - Matter.Sleeping.set(who[i], true) - } - } - sleep(mob); - sleep(body); - sleep(bullet); - simulation.cycle--; //pause all functions that depend on game cycle increasing - } - if (tech.isRewindField) { - this.rewindCount = 0 - m.grabPowerUpRange2 = 300000 - m.hold = function() { - // console.log(m.fieldCDcycle) - m.grabPowerUp(); - // //grab power ups - // for (let i = 0, len = powerUp.length; i < len; ++i) { - // if ( - // Vector.magnitudeSquared(Vector.sub(m.pos, powerUp[i].position)) < 100000 && - // !simulation.isChoosing && - // (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal) - // ) { - // powerUps.onPickUp(powerUp[i]); - // powerUp[i].effect(); - // Matter.Composite.remove(engine.world, powerUp[i]); - // powerUp.splice(i, 1); - // break; //because the array order is messed up after splice - // } - // } - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - m.wakeCheck(); - } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - const drain = 0.002 / (1 + 0.5 * m.coupling) - if (m.energy > drain) m.energy -= drain - - m.grabPowerUp(); - if (this.rewindCount === 0) m.lookForPickUp(); - - if (!m.holdingTarget) { - this.rewindCount += 6; - const DRAIN = 0.003 - let history = m.history[(m.cycle - this.rewindCount) % 600] - if (this.rewindCount > 599 || m.energy < DRAIN) { - this.rewindCount = 0; - m.resetHistory(); - if (m.fireCDcycle < m.cycle + 60) m.fieldCDcycle = m.cycle + 60 - m.immuneCycle = m.cycle //if you reach the end of the history disable harm immunity - } else { - //draw field everywhere - ctx.globalCompositeOperation = "saturation" - ctx.fillStyle = "#ccc"; - ctx.fillRect(-100000, -100000, 200000, 200000) - ctx.globalCompositeOperation = "source-over" - // m.grabPowerUp(); //a second grab power up to make the power ups easier to grab, and they more fast which matches the time theme - m.energy -= DRAIN - if (m.immuneCycle < m.cycle + 60) m.immuneCycle = m.cycle + 60; //player is immune to damage for __ cycles - Matter.Body.setPosition(player, history.position); - Matter.Body.setVelocity(player, { - x: history.velocity.x, - y: history.velocity.y - }); - if (m.health < history.health) { - m.health = history.health - if (m.health > m.maxHealth) m.health = m.maxHealth - m.displayHealth(); - } - m.yOff = history.yOff - if (m.yOff < 48) { - m.doCrouch() - } else { - m.undoCrouch() - } - if (!(this.rewindCount % 30)) { - if (tech.isRewindBot) { - for (let i = 0; i < tech.isRewindBot; i++) { - b.randomBot(m.pos, false, false) - bullet[bullet.length - 1].endCycle = simulation.cycle + 480 + Math.floor(120 * Math.random()) //8-9 seconds - } - } - if (tech.isRewindGrenade) { - b.grenade(m.pos, this.rewindCount) //Math.PI / 2 - const who = bullet[bullet.length - 1] - who.endCycle = simulation.cycle + 60 - } - } - } - } - m.wakeCheck(); - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - this.rewindCount = 0; - m.wakeCheck(); - } else if (tech.isTimeStop && player.speed < 1 && m.onGround && !input.fire) { - timeStop(); - this.rewindCount = 0; - } else { - m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - this.rewindCount = 0; - m.wakeCheck(); - } - m.drawRegenEnergy() // this calls m.regenEnergy(); also - } - } else { - m.fieldFire = true; - m.isBodiesAsleep = false; - m.hold = function() { - if (m.isHolding) { - m.wakeCheck(); - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if (input.field && m.fieldCDcycle < m.cycle) { - const drain = 0.0026 / (1 + 0.3 * m.coupling) - if (m.energy > drain) m.energy -= drain - m.grabPowerUp(); - m.lookForPickUp(); //this drains energy 0.001 - if (m.energy > drain) { - timeStop(); - } else { //holding, but field button is released - m.fieldCDcycle = m.cycle + 120; - m.energy = 0; - m.wakeCheck(); - m.wakeCheck(); - } - } else if (tech.isTimeStop && player.speed < 1 && m.onGround && m.fireCDcycle < m.cycle && !input.fire) { - timeStop(); - //makes things move at 1/5 time rate, but has an annoying flicker for mob graphics, and other minor bugs - // if (!(m.cycle % 4)) { - // // requestAnimationFrame(() => { - // m.wakeCheck(); - // // simulation.timePlayerSkip(1) - // // }); //wrapping in animation frame prevents errors, probably - // ctx.globalCompositeOperation = "saturation" - // ctx.fillStyle = "#ccc"; - // ctx.fillRect(-100000, -100000, 200000, 200000) - // ctx.globalCompositeOperation = "source-over" - // } else { - // timeStop(); - // } - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.wakeCheck(); - m.pickUp(); - } else { - m.wakeCheck(); - m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - } - m.drawRegenEnergy() - } - } - }, - effect() { - if (tech.isTimeStop) { - m.fieldHarmReduction = 0.66; //33% reduction - } else { - m.fieldHarmReduction = 1; - } - this.set(); - } - }, - { - name: "metamaterial cloaking", - description: "when not firing activate cloaking
after decloaking +333% damage for 2 s
generate 6 energy per second", - effect: () => { - m.fieldFire = true; - m.fieldMeterColor = "#333"; - m.eyeFillColor = m.fieldMeterColor - m.fieldPhase = 0; - m.isCloak = false - m.fieldDrawRadius = 0 - m.isSneakAttack = true; - m.sneakAttackCycle = 0; - m.enterCloakCycle = 0; - m.drawCloak = function() { - m.fieldPhase += 0.007 - const wiggle = 0.15 * Math.sin(m.fieldPhase * 0.5) - ctx.beginPath(); - ctx.ellipse(m.pos.x, m.pos.y, m.fieldDrawRadius * (1 - wiggle), m.fieldDrawRadius * (1 + wiggle), m.fieldPhase, 0, 2 * Math.PI); - ctx.fillStyle = "#fff" - ctx.lineWidth = 2; - ctx.strokeStyle = "#000" - // ctx.stroke() - ctx.globalCompositeOperation = "destination-in"; - ctx.fill(); - ctx.globalCompositeOperation = "source-over"; - ctx.clip(); - } - m.hold = function() { - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold and field button is pressed - if (m.energy > m.fieldRegen) m.energy -= m.fieldRegen - m.grabPowerUp(); - m.lookForPickUp(); - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding target exists, and field button is not pressed - m.pickUp(); - } else { - m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - } - //not shooting (or using field) enable cloak - if (m.energy < 0.05 && m.fireCDcycle < m.cycle && !input.fire) m.fireCDcycle = m.cycle - if (m.fireCDcycle + 30 < m.cycle && !input.fire) { //automatically cloak if not firing - const drain = 0.03 - if (!m.isCloak && m.energy > drain + 0.03) { - m.energy -= drain - m.isCloak = true //enter cloak - m.enterCloakCycle = m.cycle - if (tech.isCloakHealLastHit && m.lastHit > 0) { - const heal = Math.min(0.75 * m.lastHit, m.energy) - if (m.energy > heal) { - m.energy -= heal - m.addHealth(heal); //heal from last hit - m.lastHit = 0 - simulation.drawList.push({ //add dmg to draw queue - x: m.pos.x, - y: m.pos.y, - radius: Math.sqrt(heal) * 200, - color: "rgba(0,255,200,0.6)", - time: 16 - }); - } - } - if (tech.isIntangible) { - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType && bullet[i].botType !== "orbit") bullet[i].collisionFilter.mask = cat.map | cat.bullet | cat.mobBullet | cat.mobShield - } - } - } - } else if (m.isCloak) { //exit cloak - m.sneakAttackCycle = m.cycle - m.isCloak = false if (tech.isIntangible) { for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType && bullet[i].botType !== "orbit") bullet[i].collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield - } - } - if (tech.isCloakStun) { //stun nearby mobs after exiting cloak - let isMobsAround = false - const stunRange = m.fieldDrawRadius * 1.5 - const drain = 0.15 - if (m.energy > drain) { - for (let i = 0, len = mob.length; i < len; ++i) { - if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) < stunRange && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 && !mob[i].isBadTarget) { - isMobsAround = true - mobs.statusStun(mob[i], 180) - } - } - if (isMobsAround) { - m.energy -= drain - simulation.drawList.push({ - x: m.pos.x, - y: m.pos.y, - radius: stunRange, - color: "hsla(0,50%,100%,0.7)", - time: 7 - }); - } + if (bullet[i].botType && bullet[i].botType !== "orbit") bullet[i].collisionFilter.mask = cat.map | cat.bullet | cat.mobBullet | cat.mobShield } } } - if (m.isCloak) { - m.fieldRange = m.fieldRange * 0.85 + 115 - m.fieldDrawRadius = m.fieldRange * 1.1 //* 0.88 //* Math.min(1, 0.3 + 0.5 * Math.min(1, energy * energy)); - m.drawCloak() - } else if (m.fieldRange < 4000) { - m.fieldRange += 90 - m.fieldDrawRadius = m.fieldRange //* Math.min(1, 0.3 + 0.5 * Math.min(1, energy * energy)); - m.drawCloak() - } + } else if (m.isCloak) { //exit cloak + m.sneakAttackCycle = m.cycle + m.isCloak = false if (tech.isIntangible) { - if (m.isCloak) { - player.collisionFilter.mask = cat.map - let inPlayer = Matter.Query.region(mob, player.bounds) - if (inPlayer.length > 0) { - for (let i = 0; i < inPlayer.length; i++) { - if (m.energy > 0) { - if (!inPlayer[i].isUnblockable) m.energy -= 0.007; - if (inPlayer[i].shield) m.energy -= 0.025; - } - } - } - } else { - player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType && bullet[i].botType !== "orbit") bullet[i].collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield + } + } + if (tech.isCloakStun) { //stun nearby mobs after exiting cloak + let isMobsAround = false + const stunRange = m.fieldDrawRadius * 1.5 + const drain = 0.15 + if (m.energy > drain) { + for (let i = 0, len = mob.length; i < len; ++i) { + if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) < stunRange && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 && !mob[i].isBadTarget) { + isMobsAround = true + mobs.statusStun(mob[i], 180) + } + } + if (isMobsAround) { + m.energy -= drain + simulation.drawList.push({ + x: m.pos.x, + y: m.pos.y, + radius: stunRange, + color: "hsla(0,50%,100%,0.7)", + time: 7 + }); + } } } - this.drawRegenEnergyCloaking() - //show sneak attack status - // if (m.cycle > m.lastKillCycle + 240) { - // if (m.sneakAttackCharge > 0) { - // if (m.sneakAttackCycle + Math.min(120, 0.7 * (m.cycle - m.enterCloakCycle)) > m.cycle) { - // ctx.strokeStyle = "rgba(0,0,0,0.5)" //m.fieldMeterColor; //"rgba(255,255,0,0.2)" //ctx.strokeStyle = `rgba(0,0,255,${0.5+0.5*Math.random()})` - // ctx.beginPath(); - // ctx.arc(simulation.mouseInGame.x, simulation.mouseInGame.y, 16, 0, 2 * Math.PI); - // ctx.fillStyle = "rgba(0,0,0,0.2)" - // ctx.fill(); - // } } - } - }, - // { - // name: "phase decoherence field", - // description: "use energy to become intangible
firing and touching shields drains energy
unable to see and be seen by mobs", - // effect: () => { - // m.fieldFire = true; - // m.fieldMeterColor = "#fff"; - // m.fieldPhase = 0; - - // m.hold = function () { - // function drawField(radius) { - // radius *= Math.min(4, 0.9 + 2.2 * m.energy * m.energy); - // const rotate = m.cycle * 0.005; - // m.fieldPhase += 0.5 - 0.5 * Math.sqrt(Math.max(0.01, Math.min(m.energy, 1))); - // const off1 = 1 + 0.06 * Math.sin(m.fieldPhase); - // const off2 = 1 - 0.06 * Math.sin(m.fieldPhase); - // ctx.beginPath(); - // ctx.ellipse(m.pos.x, m.pos.y, radius * off1, radius * off2, rotate, 0, 2 * Math.PI); - // if (m.fireCDcycle > m.cycle && (input.field)) { - // ctx.lineWidth = 5; - // ctx.strokeStyle = `rgba(0, 204, 255,1)` - // ctx.stroke() - // } - // ctx.fillStyle = "#fff" //`rgba(0,0,0,${0.5+0.5*m.energy})`; - // ctx.globalCompositeOperation = "destination-in"; //in or atop - // ctx.fill(); - // ctx.globalCompositeOperation = "source-over"; - // ctx.clip(); - // } - - // m.isCloak = false //isCloak disables most uses of foundPlayer() - // player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions - // if (m.isHolding) { - // if (this.fieldRange < 2000) { - // this.fieldRange += 100 - // drawField(this.fieldRange) - // } - // m.drawHold(m.holdingTarget); - // m.holding(); - // m.throwBlock(); - // } else if (input.field) { - // m.grabPowerUp(); - // m.lookForPickUp(); - - // if (m.fieldCDcycle < m.cycle) { - // // simulation.draw.bodyFill = "transparent" - // // simulation.draw.bodyStroke = "transparent" - - // const DRAIN = 0.00013 + (m.fireCDcycle > m.cycle ? 0.005 : 0) - // if (m.energy > DRAIN) { - // m.energy -= DRAIN; - // // if (m.energy < 0.001) { - // // m.fieldCDcycle = m.cycle + 120; - // // m.energy = 0; - // // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - // // } - // this.fieldRange = this.fieldRange * 0.8 + 0.2 * 160 - // drawField(this.fieldRange) - - // m.isCloak = true //isCloak disables most uses of foundPlayer() - // player.collisionFilter.mask = cat.map - - - // let inPlayer = Matter.Query.region(mob, player.bounds) - // if (inPlayer.length > 0) { - // for (let i = 0; i < inPlayer.length; i++) { - // if (inPlayer[i].shield) { - // m.energy -= 0.005; //shields drain player energy - // //draw outline of shield - // ctx.fillStyle = `rgba(140,217,255,0.5)` - // ctx.fill() - // } else if (tech.superposition && inPlayer[i].isDropPowerUp) { - // // inPlayer[i].damage(0.4 * m.dmgScale); //damage mobs inside the player - // // m.energy += 0.005; - - // mobs.statusStun(inPlayer[i], 300) - // //draw outline of mob in a few random locations to show blurriness - // const vertices = inPlayer[i].vertices; - // const off = 30 - // for (let k = 0; k < 3; k++) { - // const xOff = off * (Math.random() - 0.5) - // const yOff = off * (Math.random() - 0.5) - // ctx.beginPath(); - // ctx.moveTo(xOff + vertices[0].x, yOff + vertices[0].y); - // for (let j = 1, len = vertices.length; j < len; ++j) { - // ctx.lineTo(xOff + vertices[j].x, yOff + vertices[j].y); - // } - // ctx.lineTo(xOff + vertices[0].x, yOff + vertices[0].y); - // ctx.fillStyle = "rgba(0,0,0,0.1)" - // ctx.fill() - // } - // break; - // } - // } - // } - // } else { - // m.fieldCDcycle = m.cycle + 120; - // m.energy = 0; - // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - // drawField(this.fieldRange) - // } - // } - // } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - // m.pickUp(); - // if (this.fieldRange < 2000) { - // this.fieldRange += 100 - // drawField(this.fieldRange) - // } - // } else { - // // this.fieldRange = 3000 - // if (this.fieldRange < 2000 && m.holdingTarget === null) { - // this.fieldRange += 100 - // drawField(this.fieldRange) - // } - // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - // } - - // if (m.energy < m.maxEnergy) { - // m.energy += m.fieldRegen; - // const xOff = m.pos.x - m.radius * m.maxEnergy - // const yOff = m.pos.y - 50 - // ctx.fillStyle = "rgba(0, 0, 0, 0.3)"; - // ctx.fillRect(xOff, yOff, 60 * m.maxEnergy, 10); - // ctx.fillStyle = m.fieldMeterColor; - // ctx.fillRect(xOff, yOff, 60 * m.energy, 10); - // ctx.beginPath() - // ctx.rect(xOff, yOff, 60 * m.maxEnergy, 10); - // ctx.strokeStyle = "rgb(0, 0, 0)"; - // ctx.lineWidth = 1; - // ctx.stroke(); - // } - // if (m.energy < 0) m.energy = 0 - // } - // } - // }, - { - name: "pilot wave", - //
blocks can't collide with intangible mobs - //field radius decreases out of line of sight - //unlock tech from other fields - description: "use energy to guide blocks
tech, fields, and guns have +1 choice
generate 6 energy per second", - effect: () => { - m.fieldMeterColor = "#333" - m.eyeFillColor = m.fieldMeterColor - - m.fieldPhase = 0; - m.fieldPosition = { - x: simulation.mouseInGame.x, - y: simulation.mouseInGame.y + if (m.isCloak) { + m.fieldRange = m.fieldRange * 0.85 + 115 + m.fieldDrawRadius = m.fieldRange * 1.1 //* 0.88 //* Math.min(1, 0.3 + 0.5 * Math.min(1, energy * energy)); + m.drawCloak() + } else if (m.fieldRange < 4000) { + m.fieldRange += 90 + m.fieldDrawRadius = m.fieldRange //* Math.min(1, 0.3 + 0.5 * Math.min(1, energy * energy)); + m.drawCloak() } - m.lastFieldPosition = { - x: simulation.mouseInGame.x, - y: simulation.mouseInGame.y - } - m.fieldOn = false; - m.fieldRadius = 0; - m.drop(); - m.hold = function() { - if (input.field) { - if (m.fieldCDcycle < m.cycle) { - const scale = 25 - const bounds = { - min: { - x: m.fieldPosition.x - scale, - y: m.fieldPosition.y - scale - }, - max: { - x: m.fieldPosition.x + scale, - y: m.fieldPosition.y + scale + if (tech.isIntangible) { + if (m.isCloak) { + player.collisionFilter.mask = cat.map + let inPlayer = Matter.Query.region(mob, player.bounds) + if (inPlayer.length > 0) { + for (let i = 0; i < inPlayer.length; i++) { + if (m.energy > 0) { + if (!inPlayer[i].isUnblockable) m.energy -= 0.007; + if (inPlayer[i].shield) m.energy -= 0.025; } } - const isInMap = Matter.Query.region(map, bounds).length - // const isInMap = Matter.Query.point(map, m.fieldPosition).length - - if (!m.fieldOn) { // if field was off, and it starting up, teleport to new mouse location - m.fieldOn = true; - // m.fieldPosition = { //smooth the mouse position, set to starting at player - // x: m.pos.x, - // y: m.pos.y - // } - m.fieldPosition = { //smooth the mouse position, set to mouse's current location - x: simulation.mouseInGame.x, - y: simulation.mouseInGame.y - } - m.lastFieldPosition = { //used to find velocity of field changes - x: m.fieldPosition.x, - y: m.fieldPosition.y - } - } else { //when field is on it smoothly moves towards the mouse - m.lastFieldPosition = { //used to find velocity of field changes - x: m.fieldPosition.x, - y: m.fieldPosition.y - } - const smooth = isInMap ? 0.985 : 0.96; - m.fieldPosition = { //smooth the mouse position - x: m.fieldPosition.x * smooth + simulation.mouseInGame.x * (1 - smooth), - y: m.fieldPosition.y * smooth + simulation.mouseInGame.y * (1 - smooth), - } - } - - //grab power ups into the field - for (let i = 0, len = powerUp.length; i < len; ++i) { - const dxP = m.fieldPosition.x - powerUp[i].position.x; - const dyP = m.fieldPosition.y - powerUp[i].position.y; - const dist2 = dxP * dxP + dyP * dyP + 200; - // float towards field if looking at and in range or if very close to player - if ( - dist2 < m.fieldRadius * m.fieldRadius && - (m.lookingAt(powerUp[i]) || dist2 < 16000) - ) { - powerUp[i].force.x += 0.05 * (dxP / Math.sqrt(dist2)) * powerUp[i].mass; - powerUp[i].force.y += 0.05 * (dyP / Math.sqrt(dist2)) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity - //extra friction - Matter.Body.setVelocity(powerUp[i], { - x: powerUp[i].velocity.x * 0.11, - y: powerUp[i].velocity.y * 0.11 - }); - if ( - dist2 < 5000 && - !simulation.isChoosing && - (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal) - // (powerUp[i].name !== "heal" || m.health < 0.94 * m.maxHealth) - // (powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity) - ) { //use power up if it is close enough - powerUps.onPickUp(powerUp[i]); - powerUp[i].effect(); - Matter.Composite.remove(engine.world, powerUp[i]); - powerUp.splice(i, 1); - // m.fieldRadius += 50 - break; //because the array order is messed up after splice - } - } - } - //grab power ups normally too - m.grabPowerUp(); - - if (m.energy > 0.01) { - //find mouse velocity - const diff = Vector.sub(m.fieldPosition, m.lastFieldPosition) - const speed = Vector.magnitude(diff) - const velocity = Vector.mult(Vector.normalise(diff), Math.min(speed, 60)) //limit velocity - let radius, radiusSmooth - if (Matter.Query.ray(map, m.fieldPosition, player.position).length) { //is there something block the player's view of the field - radius = 0 - radiusSmooth = Math.max(0, isInMap ? 0.96 - 0.02 * speed : 0.995); //0.99 - } else { - radius = Math.max(50, 250 - 2 * speed) - radiusSmooth = 0.97 - } - m.fieldRadius = m.fieldRadius * radiusSmooth + radius * (1 - radiusSmooth) - - for (let i = 0, len = body.length; i < len; ++i) { - if (Vector.magnitude(Vector.sub(body[i].position, m.fieldPosition)) < m.fieldRadius && !body[i].isNotHoldable) { - const DRAIN = speed * body[i].mass * 0.0000035 // * (1 + m.energy * m.energy) //drain more energy when you have more energy - if (m.energy > DRAIN) { - m.energy -= DRAIN; - Matter.Body.setVelocity(body[i], velocity); //give block mouse velocity - Matter.Body.setAngularVelocity(body[i], body[i].angularVelocity * 0.8) - // body[i].force.y -= body[i].mass * simulation.g; //remove gravity effects - //blocks drift towards center of pilot wave - const sub = Vector.sub(m.fieldPosition, body[i].position) - const push = Vector.mult(Vector.normalise(sub), 0.0001 * body[i].mass * Vector.magnitude(sub)) - body[i].force.x += push.x - body[i].force.y += push.y - body[i].mass * simulation.g //remove gravity effects - // if (body[i].collisionFilter.category !== cat.bullet) { - // body[i].collisionFilter.category = cat.bullet; - // } - } else { - m.fieldCDcycle = m.cycle + 120; - m.fieldOn = false - m.fieldRadius = 0 - break - } - } - } - - - // m.holdingTarget.collisionFilter.category = cat.bullet; - // m.holdingTarget.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield; - // //check every second to see if player is away from thrown body, and make solid - // const solid = function(that) { - // const dx = that.position.x - player.position.x; - // const dy = that.position.y - player.position.y; - // if (that.speed < 3 && dx * dx + dy * dy > 10000 && that !== m.holdingTarget) { - // that.collisionFilter.category = cat.body; //make solid - // that.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; //can hit player now - // } else { - // setTimeout(solid, 40, that); - // } - // }; - // setTimeout(solid, 200, m.holdingTarget); - - - - // if (tech.isFreezeMobs) { - // for (let i = 0, len = mob.length; i < len; ++i) { - // if (!mob[i].isMobBullet && !mob[i].shield && !mob[i].isShielded && Vector.magnitude(Vector.sub(mob[i].position, m.fieldPosition)) < m.fieldRadius + mob[i].radius) { - // const ICE_DRAIN = 0.0005 - // if (m.energy > ICE_DRAIN) m.energy -= ICE_DRAIN; - // mobs.statusSlow(mob[i], 180) - // } - // } - // } - - ctx.beginPath(); - const rotate = m.cycle * 0.008; - m.fieldPhase += 0.2 // - 0.5 * Math.sqrt(Math.min(m.energy, 1)); - const off1 = 1 + 0.06 * Math.sin(m.fieldPhase); - const off2 = 1 - 0.06 * Math.sin(m.fieldPhase); - ctx.beginPath(); - ctx.ellipse(m.fieldPosition.x, m.fieldPosition.y, 1.2 * m.fieldRadius * off1, 1.2 * m.fieldRadius * off2, rotate, 0, 2 * Math.PI); - ctx.globalCompositeOperation = "exclusion"; //"exclusion" "difference"; - ctx.fillStyle = "#fff"; //"#eef"; - ctx.fill(); - ctx.globalCompositeOperation = "source-over"; - ctx.beginPath(); - ctx.ellipse(m.fieldPosition.x, m.fieldPosition.y, 1.2 * m.fieldRadius * off1, 1.2 * m.fieldRadius * off2, rotate, 0, 2 * Math.PI * m.energy / m.maxEnergy); - ctx.strokeStyle = "#000"; - ctx.lineWidth = 4; - ctx.stroke(); - } else { - m.fieldCDcycle = m.cycle + 120; - m.fieldOn = false - m.fieldRadius = 0 - } - } else { - m.grabPowerUp(); } } else { - m.fieldOn = false - m.fieldRadius = 0 + player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions } - m.drawRegenEnergy("rgba(0,0,0,0.2)") } + this.drawRegenEnergyCloaking() + //show sneak attack status + // if (m.cycle > m.lastKillCycle + 240) { + // if (m.sneakAttackCharge > 0) { + // if (m.sneakAttackCycle + Math.min(120, 0.7 * (m.cycle - m.enterCloakCycle)) > m.cycle) { + // ctx.strokeStyle = "rgba(0,0,0,0.5)" //m.fieldMeterColor; //"rgba(255,255,0,0.2)" //ctx.strokeStyle = `rgba(0,0,255,${0.5+0.5*Math.random()})` + // ctx.beginPath(); + // ctx.arc(simulation.mouseInGame.x, simulation.mouseInGame.y, 16, 0, 2 * Math.PI); + // ctx.fillStyle = "rgba(0,0,0,0.2)" + // ctx.fill(); + // } } - }, - { - name: "wormhole", - //wormholes attract blocks and power ups
- description: "use energy to tunnel through a wormhole
+3% chance to duplicate spawned power ups
generate 6 energy per second", //
bullets may also traverse wormholes - drain: 0, - effect: function() { - m.fieldMeterColor = "#bbf" //"#0c5" - m.eyeFillColor = m.fieldMeterColor + } + }, + // { + // name: "phase decoherence field", + // description: "use energy to become intangible
firing and touching shields drains energy
unable to see and be seen by mobs", + // effect: () => { + // m.fieldFire = true; + // m.fieldMeterColor = "#fff"; + // m.fieldPhase = 0; - m.duplicateChance = 0.03 - m.fieldRange = 0 - powerUps.setDupChance(); //needed after adjusting duplication chance + // m.hold = function () { + // function drawField(radius) { + // radius *= Math.min(4, 0.9 + 2.2 * m.energy * m.energy); + // const rotate = m.cycle * 0.005; + // m.fieldPhase += 0.5 - 0.5 * Math.sqrt(Math.max(0.01, Math.min(m.energy, 1))); + // const off1 = 1 + 0.06 * Math.sin(m.fieldPhase); + // const off2 = 1 - 0.06 * Math.sin(m.fieldPhase); + // ctx.beginPath(); + // ctx.ellipse(m.pos.x, m.pos.y, radius * off1, radius * off2, rotate, 0, 2 * Math.PI); + // if (m.fireCDcycle > m.cycle && (input.field)) { + // ctx.lineWidth = 5; + // ctx.strokeStyle = `rgba(0, 204, 255,1)` + // ctx.stroke() + // } + // ctx.fillStyle = "#fff" //`rgba(0,0,0,${0.5+0.5*m.energy})`; + // ctx.globalCompositeOperation = "destination-in"; //in or atop + // ctx.fill(); + // ctx.globalCompositeOperation = "source-over"; + // ctx.clip(); + // } - m.hold = function() { - // m.hole = { //this is reset with each new field, but I'm leaving it here for reference - // isOn: false, - // isReady: true, - // pos1: {x: 0,y: 0}, - // pos2: {x: 0,y: 0}, - // angle: 0, - // unit:{x:0,y:0}, - // } - if (m.hole.isOn) { - // draw holes - m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) - const semiMajorAxis = m.fieldRange + 30 - const edge1a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos1) - const edge1b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos1) - const edge2a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos2) - const edge2b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos2) - ctx.beginPath(); - ctx.moveTo(edge1a.x, edge1a.y) - ctx.bezierCurveTo(m.hole.pos1.x, m.hole.pos1.y, m.hole.pos2.x, m.hole.pos2.y, edge2a.x, edge2a.y); - ctx.lineTo(edge2b.x, edge2b.y) - ctx.bezierCurveTo(m.hole.pos2.x, m.hole.pos2.y, m.hole.pos1.x, m.hole.pos1.y, edge1b.x, edge1b.y); - ctx.fillStyle = `rgba(255,255,255,${200 / m.fieldRange / m.fieldRange})` //"rgba(0,0,0,0.1)" - ctx.fill(); - ctx.beginPath(); - ctx.ellipse(m.hole.pos1.x, m.hole.pos1.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI) - ctx.ellipse(m.hole.pos2.x, m.hole.pos2.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI) - ctx.fillStyle = `rgba(255,255,255,${32 / m.fieldRange})` - ctx.fill(); + // m.isCloak = false //isCloak disables most uses of foundPlayer() + // player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions + // if (m.isHolding) { + // if (this.fieldRange < 2000) { + // this.fieldRange += 100 + // drawField(this.fieldRange) + // } + // m.drawHold(m.holdingTarget); + // m.holding(); + // m.throwBlock(); + // } else if (input.field) { + // m.grabPowerUp(); + // m.lookForPickUp(); - //suck power ups - for (let i = 0, len = powerUp.length; i < len; ++i) { - //which hole is closer - const dxP1 = m.hole.pos1.x - powerUp[i].position.x; - const dyP1 = m.hole.pos1.y - powerUp[i].position.y; - const dxP2 = m.hole.pos2.x - powerUp[i].position.x; - const dyP2 = m.hole.pos2.y - powerUp[i].position.y; - let dxP, dyP, dist2 - if (dxP1 * dxP1 + dyP1 * dyP1 < dxP2 * dxP2 + dyP2 * dyP2) { - dxP = dxP1 - dyP = dyP1 - } else { - dxP = dxP2 - dyP = dyP2 + // if (m.fieldCDcycle < m.cycle) { + // // simulation.draw.bodyFill = "transparent" + // // simulation.draw.bodyStroke = "transparent" + + // const DRAIN = 0.00013 + (m.fireCDcycle > m.cycle ? 0.005 : 0) + // if (m.energy > DRAIN) { + // m.energy -= DRAIN; + // // if (m.energy < 0.001) { + // // m.fieldCDcycle = m.cycle + 120; + // // m.energy = 0; + // // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + // // } + // this.fieldRange = this.fieldRange * 0.8 + 0.2 * 160 + // drawField(this.fieldRange) + + // m.isCloak = true //isCloak disables most uses of foundPlayer() + // player.collisionFilter.mask = cat.map + + + // let inPlayer = Matter.Query.region(mob, player.bounds) + // if (inPlayer.length > 0) { + // for (let i = 0; i < inPlayer.length; i++) { + // if (inPlayer[i].shield) { + // m.energy -= 0.005; //shields drain player energy + // //draw outline of shield + // ctx.fillStyle = `rgba(140,217,255,0.5)` + // ctx.fill() + // } else if (tech.superposition && inPlayer[i].isDropPowerUp) { + // // inPlayer[i].damage(0.4 * m.dmgScale); //damage mobs inside the player + // // m.energy += 0.005; + + // mobs.statusStun(inPlayer[i], 300) + // //draw outline of mob in a few random locations to show blurriness + // const vertices = inPlayer[i].vertices; + // const off = 30 + // for (let k = 0; k < 3; k++) { + // const xOff = off * (Math.random() - 0.5) + // const yOff = off * (Math.random() - 0.5) + // ctx.beginPath(); + // ctx.moveTo(xOff + vertices[0].x, yOff + vertices[0].y); + // for (let j = 1, len = vertices.length; j < len; ++j) { + // ctx.lineTo(xOff + vertices[j].x, yOff + vertices[j].y); + // } + // ctx.lineTo(xOff + vertices[0].x, yOff + vertices[0].y); + // ctx.fillStyle = "rgba(0,0,0,0.1)" + // ctx.fill() + // } + // break; + // } + // } + // } + // } else { + // m.fieldCDcycle = m.cycle + 120; + // m.energy = 0; + // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + // drawField(this.fieldRange) + // } + // } + // } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + // m.pickUp(); + // if (this.fieldRange < 2000) { + // this.fieldRange += 100 + // drawField(this.fieldRange) + // } + // } else { + // // this.fieldRange = 3000 + // if (this.fieldRange < 2000 && m.holdingTarget === null) { + // this.fieldRange += 100 + // drawField(this.fieldRange) + // } + // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + // } + + // if (m.energy < m.maxEnergy) { + // m.energy += m.fieldRegen; + // const xOff = m.pos.x - m.radius * m.maxEnergy + // const yOff = m.pos.y - 50 + // ctx.fillStyle = "rgba(0, 0, 0, 0.3)"; + // ctx.fillRect(xOff, yOff, 60 * m.maxEnergy, 10); + // ctx.fillStyle = m.fieldMeterColor; + // ctx.fillRect(xOff, yOff, 60 * m.energy, 10); + // ctx.beginPath() + // ctx.rect(xOff, yOff, 60 * m.maxEnergy, 10); + // ctx.strokeStyle = "rgb(0, 0, 0)"; + // ctx.lineWidth = 1; + // ctx.stroke(); + // } + // if (m.energy < 0) m.energy = 0 + // } + // } + // }, + { + name: "pilot wave", + //
blocks can't collide with intangible mobs + //field radius decreases out of line of sight + //unlock tech from other fields + description: "use energy to guide blocks
tech, fields, and guns have +1 choice
generate 10 energy per second", + effect: () => { + m.fieldMeterColor = "#333" + m.eyeFillColor = m.fieldMeterColor + + m.fieldPhase = 0; + m.fieldPosition = { + x: simulation.mouseInGame.x, + y: simulation.mouseInGame.y + } + m.lastFieldPosition = { + x: simulation.mouseInGame.x, + y: simulation.mouseInGame.y + } + m.fieldOn = false; + m.fieldRadius = 0; + m.drop(); + m.hold = function () { + if (input.field) { + if (m.fieldCDcycle < m.cycle) { + const scale = 25 + const bounds = { + min: { + x: m.fieldPosition.x - scale, + y: m.fieldPosition.y - scale + }, + max: { + x: m.fieldPosition.x + scale, + y: m.fieldPosition.y + scale } - dist2 = dxP * dxP + dyP * dyP; - if (dist2 < 600000) { //&& !(m.health === m.maxHealth && powerUp[i].name === "heal") - powerUp[i].force.x += 4 * (dxP / dist2) * powerUp[i].mass; // float towards hole - powerUp[i].force.y += 4 * (dyP / dist2) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity - Matter.Body.setVelocity(powerUp[i], { //extra friction - x: powerUp[i].velocity.x * 0.05, - y: powerUp[i].velocity.y * 0.05 + } + const isInMap = Matter.Query.region(map, bounds).length + // const isInMap = Matter.Query.point(map, m.fieldPosition).length + + if (!m.fieldOn) { // if field was off, and it starting up, teleport to new mouse location + m.fieldOn = true; + // m.fieldPosition = { //smooth the mouse position, set to starting at player + // x: m.pos.x, + // y: m.pos.y + // } + m.fieldPosition = { //smooth the mouse position, set to mouse's current location + x: simulation.mouseInGame.x, + y: simulation.mouseInGame.y + } + m.lastFieldPosition = { //used to find velocity of field changes + x: m.fieldPosition.x, + y: m.fieldPosition.y + } + } else { //when field is on it smoothly moves towards the mouse + m.lastFieldPosition = { //used to find velocity of field changes + x: m.fieldPosition.x, + y: m.fieldPosition.y + } + const smooth = isInMap ? 0.985 : 0.96; + m.fieldPosition = { //smooth the mouse position + x: m.fieldPosition.x * smooth + simulation.mouseInGame.x * (1 - smooth), + y: m.fieldPosition.y * smooth + simulation.mouseInGame.y * (1 - smooth), + } + } + + //grab power ups into the field + for (let i = 0, len = powerUp.length; i < len; ++i) { + const dxP = m.fieldPosition.x - powerUp[i].position.x; + const dyP = m.fieldPosition.y - powerUp[i].position.y; + const dist2 = dxP * dxP + dyP * dyP + 200; + // float towards field if looking at and in range or if very close to player + if ( + dist2 < m.fieldRadius * m.fieldRadius && + (m.lookingAt(powerUp[i]) || dist2 < 16000) + ) { + powerUp[i].force.x += 0.05 * (dxP / Math.sqrt(dist2)) * powerUp[i].mass; + powerUp[i].force.y += 0.05 * (dyP / Math.sqrt(dist2)) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity + //extra friction + Matter.Body.setVelocity(powerUp[i], { + x: powerUp[i].velocity.x * 0.11, + y: powerUp[i].velocity.y * 0.11 }); - if (dist2 < 1000 && !simulation.isChoosing) { //use power up if it is close enough - - // if (true) { //AoE radiation effect - // const range = 800 - - // for (let i = 0, len = mob.length; i < len; ++i) { - // if (mob[i].alive && !mob[i].isShielded) { - // dist = Vector.magnitude(Vector.sub(powerUp[i].position, mob[i].position)) - mob[i].radius; - // if (dist < range) mobs.statusDoT(mob[i], 0.5) //apply radiation damage status effect on direct hits - // } - // } - - // simulation.drawList.push({ - // x: powerUp[i].position.x, - // y: powerUp[i].position.y, - // radius: range, - // color: "rgba(0,150,200,0.3)", - // time: 4 - // }); - // } - - m.fieldRange *= 0.8 + if ( + dist2 < 5000 && + !simulation.isChoosing && + (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal) + // (powerUp[i].name !== "heal" || m.health < 0.94 * m.maxHealth) + // (powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity) + ) { //use power up if it is close enough powerUps.onPickUp(powerUp[i]); powerUp[i].effect(); Matter.Composite.remove(engine.world, powerUp[i]); powerUp.splice(i, 1); + // m.fieldRadius += 50 break; //because the array order is messed up after splice } } } - //suck and shrink blocks - const suckRange = 500 - const shrinkRange = 100 - const shrinkScale = 0.97; - const slowScale = 0.9 - for (let i = 0, len = body.length; i < len; i++) { - if (!body[i].isNotHoldable) { - const dist1 = Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position)) - const dist2 = Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position)) - if (dist1 < dist2) { - if (dist1 < suckRange) { - const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, body[i].position)), 1) - const slow = Vector.mult(body[i].velocity, slowScale) - Matter.Body.setVelocity(body[i], Vector.add(slow, pull)); - //shrink - if (Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position)) < shrinkRange) { - Matter.Body.scale(body[i], shrinkScale, shrinkScale); - if (body[i].mass < 0.05) { - Matter.Composite.remove(engine.world, body[i]); - body.splice(i, 1); - m.fieldRange *= 0.8 - if ((m.fieldMode === 0 || m.fieldMode === 9) && m.immuneCycle < m.cycle) m.energy += 0.2 * m.coupling - if (tech.isWormholeWorms) { //pandimensional spermia - b.worm(Vector.add(m.hole.pos2, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random()))) - Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), -10)); - // for (let i = 0, len = Math.ceil(1.25 * Math.random()); i < len; i++) { - // } - } - break - } - } + //grab power ups normally too + m.grabPowerUp(); + + if (m.energy > 0.01) { + //find mouse velocity + const diff = Vector.sub(m.fieldPosition, m.lastFieldPosition) + const speed = Vector.magnitude(diff) + const velocity = Vector.mult(Vector.normalise(diff), Math.min(speed, 60)) //limit velocity + let radius, radiusSmooth + if (Matter.Query.ray(map, m.fieldPosition, player.position).length) { //is there something block the player's view of the field + radius = 0 + radiusSmooth = Math.max(0, isInMap ? 0.96 - 0.02 * speed : 0.995); //0.99 + } else { + radius = Math.max(50, 250 - 2 * speed) + radiusSmooth = 0.97 + } + m.fieldRadius = m.fieldRadius * radiusSmooth + radius * (1 - radiusSmooth) + + for (let i = 0, len = body.length; i < len; ++i) { + if (Vector.magnitude(Vector.sub(body[i].position, m.fieldPosition)) < m.fieldRadius && !body[i].isNotHoldable) { + const DRAIN = speed * body[i].mass * 0.0000035 // * (1 + m.energy * m.energy) //drain more energy when you have more energy + if (m.energy > DRAIN) { + m.energy -= DRAIN; + Matter.Body.setVelocity(body[i], velocity); //give block mouse velocity + Matter.Body.setAngularVelocity(body[i], body[i].angularVelocity * 0.8) + // body[i].force.y -= body[i].mass * simulation.g; //remove gravity effects + //blocks drift towards center of pilot wave + const sub = Vector.sub(m.fieldPosition, body[i].position) + const push = Vector.mult(Vector.normalise(sub), 0.0001 * body[i].mass * Vector.magnitude(sub)) + body[i].force.x += push.x + body[i].force.y += push.y - body[i].mass * simulation.g //remove gravity effects + // if (body[i].collisionFilter.category !== cat.bullet) { + // body[i].collisionFilter.category = cat.bullet; + // } + } else { + m.fieldCDcycle = m.cycle + 120; + m.fieldOn = false + m.fieldRadius = 0 + break } - } else if (dist2 < suckRange) { - const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, body[i].position)), 1) + } + } + + + // m.holdingTarget.collisionFilter.category = cat.bullet; + // m.holdingTarget.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield; + // //check every second to see if player is away from thrown body, and make solid + // const solid = function(that) { + // const dx = that.position.x - player.position.x; + // const dy = that.position.y - player.position.y; + // if (that.speed < 3 && dx * dx + dy * dy > 10000 && that !== m.holdingTarget) { + // that.collisionFilter.category = cat.body; //make solid + // that.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; //can hit player now + // } else { + // setTimeout(solid, 40, that); + // } + // }; + // setTimeout(solid, 200, m.holdingTarget); + + + + // if (tech.isFreezeMobs) { + // for (let i = 0, len = mob.length; i < len; ++i) { + // if (!mob[i].isMobBullet && !mob[i].shield && !mob[i].isShielded && Vector.magnitude(Vector.sub(mob[i].position, m.fieldPosition)) < m.fieldRadius + mob[i].radius) { + // const ICE_DRAIN = 0.0005 + // if (m.energy > ICE_DRAIN) m.energy -= ICE_DRAIN; + // mobs.statusSlow(mob[i], 180) + // } + // } + // } + + ctx.beginPath(); + const rotate = m.cycle * 0.008; + m.fieldPhase += 0.2 // - 0.5 * Math.sqrt(Math.min(m.energy, 1)); + const off1 = 1 + 0.06 * Math.sin(m.fieldPhase); + const off2 = 1 - 0.06 * Math.sin(m.fieldPhase); + ctx.beginPath(); + ctx.ellipse(m.fieldPosition.x, m.fieldPosition.y, 1.2 * m.fieldRadius * off1, 1.2 * m.fieldRadius * off2, rotate, 0, 2 * Math.PI); + ctx.globalCompositeOperation = "exclusion"; //"exclusion" "difference"; + ctx.fillStyle = "#fff"; //"#eef"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + ctx.beginPath(); + ctx.ellipse(m.fieldPosition.x, m.fieldPosition.y, 1.2 * m.fieldRadius * off1, 1.2 * m.fieldRadius * off2, rotate, 0, 2 * Math.PI * m.energy / m.maxEnergy); + ctx.strokeStyle = "#000"; + ctx.lineWidth = 4; + ctx.stroke(); + } else { + m.fieldCDcycle = m.cycle + 120; + m.fieldOn = false + m.fieldRadius = 0 + } + } else { + m.grabPowerUp(); + } + } else { + m.fieldOn = false + m.fieldRadius = 0 + } + m.drawRegenEnergy("rgba(0,0,0,0.2)") + } + } + }, + { + name: "wormhole", + //wormholes attract blocks and power ups
+ description: "use energy to tunnel through a wormhole
+3% chance to duplicate spawned power ups
generate 6 energy per second", //
bullets may also traverse wormholes + drain: 0, + effect: function () { + m.fieldMeterColor = "#bbf" //"#0c5" + m.eyeFillColor = m.fieldMeterColor + + m.duplicateChance = 0.03 + m.fieldRange = 0 + powerUps.setDupChance(); //needed after adjusting duplication chance + + m.hold = function () { + // m.hole = { //this is reset with each new field, but I'm leaving it here for reference + // isOn: false, + // isReady: true, + // pos1: {x: 0,y: 0}, + // pos2: {x: 0,y: 0}, + // angle: 0, + // unit:{x:0,y:0}, + // } + if (m.hole.isOn) { + // draw holes + m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) + const semiMajorAxis = m.fieldRange + 30 + const edge1a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos1) + const edge1b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos1) + const edge2a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos2) + const edge2b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos2) + ctx.beginPath(); + ctx.moveTo(edge1a.x, edge1a.y) + ctx.bezierCurveTo(m.hole.pos1.x, m.hole.pos1.y, m.hole.pos2.x, m.hole.pos2.y, edge2a.x, edge2a.y); + ctx.lineTo(edge2b.x, edge2b.y) + ctx.bezierCurveTo(m.hole.pos2.x, m.hole.pos2.y, m.hole.pos1.x, m.hole.pos1.y, edge1b.x, edge1b.y); + ctx.fillStyle = `rgba(255,255,255,${200 / m.fieldRange / m.fieldRange})` //"rgba(0,0,0,0.1)" + ctx.fill(); + ctx.beginPath(); + ctx.ellipse(m.hole.pos1.x, m.hole.pos1.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI) + ctx.ellipse(m.hole.pos2.x, m.hole.pos2.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI) + ctx.fillStyle = `rgba(255,255,255,${32 / m.fieldRange})` + ctx.fill(); + + //suck power ups + for (let i = 0, len = powerUp.length; i < len; ++i) { + //which hole is closer + const dxP1 = m.hole.pos1.x - powerUp[i].position.x; + const dyP1 = m.hole.pos1.y - powerUp[i].position.y; + const dxP2 = m.hole.pos2.x - powerUp[i].position.x; + const dyP2 = m.hole.pos2.y - powerUp[i].position.y; + let dxP, dyP, dist2 + if (dxP1 * dxP1 + dyP1 * dyP1 < dxP2 * dxP2 + dyP2 * dyP2) { + dxP = dxP1 + dyP = dyP1 + } else { + dxP = dxP2 + dyP = dyP2 + } + dist2 = dxP * dxP + dyP * dyP; + if (dist2 < 600000) { //&& !(m.health === m.maxHealth && powerUp[i].name === "heal") + powerUp[i].force.x += 4 * (dxP / dist2) * powerUp[i].mass; // float towards hole + powerUp[i].force.y += 4 * (dyP / dist2) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity + Matter.Body.setVelocity(powerUp[i], { //extra friction + x: powerUp[i].velocity.x * 0.05, + y: powerUp[i].velocity.y * 0.05 + }); + if (dist2 < 1000 && !simulation.isChoosing) { //use power up if it is close enough + + // if (true) { //AoE radiation effect + // const range = 800 + + // for (let i = 0, len = mob.length; i < len; ++i) { + // if (mob[i].alive && !mob[i].isShielded) { + // dist = Vector.magnitude(Vector.sub(powerUp[i].position, mob[i].position)) - mob[i].radius; + // if (dist < range) mobs.statusDoT(mob[i], 0.5) //apply radiation damage status effect on direct hits + // } + // } + + // simulation.drawList.push({ + // x: powerUp[i].position.x, + // y: powerUp[i].position.y, + // radius: range, + // color: "rgba(0,150,200,0.3)", + // time: 4 + // }); + // } + + m.fieldRange *= 0.8 + powerUps.onPickUp(powerUp[i]); + powerUp[i].effect(); + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + break; //because the array order is messed up after splice + } + } + } + //suck and shrink blocks + const suckRange = 500 + const shrinkRange = 100 + const shrinkScale = 0.97; + const slowScale = 0.9 + for (let i = 0, len = body.length; i < len; i++) { + if (!body[i].isNotHoldable) { + const dist1 = Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position)) + const dist2 = Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position)) + if (dist1 < dist2) { + if (dist1 < suckRange) { + const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, body[i].position)), 1) const slow = Vector.mult(body[i].velocity, slowScale) Matter.Body.setVelocity(body[i], Vector.add(slow, pull)); //shrink - if (Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position)) < shrinkRange) { + if (Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position)) < shrinkRange) { Matter.Body.scale(body[i], shrinkScale, shrinkScale); if (body[i].mass < 0.05) { Matter.Composite.remove(engine.world, body[i]); body.splice(i, 1); m.fieldRange *= 0.8 - // if (tech.isWormholeEnergy && m.energy < m.maxEnergy * 2) m.energy = m.maxEnergy * 2 - // if (tech.isWormholeEnergy && m.immuneCycle < m.cycle) m.energy += 0.5 if ((m.fieldMode === 0 || m.fieldMode === 9) && m.immuneCycle < m.cycle) m.energy += 0.2 * m.coupling - if (m.fieldMode === 0 || m.fieldMode === 9) m.energy += 0.2 * m.coupling if (tech.isWormholeWorms) { //pandimensional spermia - b.worm(Vector.add(m.hole.pos1, Vector.rotate({ - x: m.fieldRange * 0.4, - y: 0 - }, 2 * Math.PI * Math.random()))) - Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), 5)); + b.worm(Vector.add(m.hole.pos2, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random()))) + Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), -10)); // for (let i = 0, len = Math.ceil(1.25 * Math.random()); i < len; i++) { // } } @@ -4427,370 +4401,432 @@ const m = { } } } - } - } - if (tech.isWormHoleBullets) { - //teleport bullets - for (let i = 0, len = bullet.length; i < len; ++i) { //teleport bullets from hole1 to hole2 - if (!bullet[i].botType && !bullet[i].isInHole) { //don't teleport bots - if (Vector.magnitude(Vector.sub(m.hole.pos1, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1 - Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos2, Vector.sub(m.hole.pos1, bullet[i].position))); - m.fieldRange += 5 - bullet[i].isInHole = true - } else if (Vector.magnitude(Vector.sub(m.hole.pos2, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1 - Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos1, Vector.sub(m.hole.pos2, bullet[i].position))); - m.fieldRange += 5 - bullet[i].isInHole = true + } else if (dist2 < suckRange) { + const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, body[i].position)), 1) + const slow = Vector.mult(body[i].velocity, slowScale) + Matter.Body.setVelocity(body[i], Vector.add(slow, pull)); + //shrink + if (Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position)) < shrinkRange) { + Matter.Body.scale(body[i], shrinkScale, shrinkScale); + if (body[i].mass < 0.05) { + Matter.Composite.remove(engine.world, body[i]); + body.splice(i, 1); + m.fieldRange *= 0.8 + // if (tech.isWormholeEnergy && m.energy < m.maxEnergy * 2) m.energy = m.maxEnergy * 2 + // if (tech.isWormholeEnergy && m.immuneCycle < m.cycle) m.energy += 0.5 + if ((m.fieldMode === 0 || m.fieldMode === 9) && m.immuneCycle < m.cycle) m.energy += 0.2 * m.coupling + if (m.fieldMode === 0 || m.fieldMode === 9) m.energy += 0.2 * m.coupling + if (tech.isWormholeWorms) { //pandimensional spermia + b.worm(Vector.add(m.hole.pos1, Vector.rotate({ + x: m.fieldRange * 0.4, + y: 0 + }, 2 * Math.PI * Math.random()))) + Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), 5)); + // for (let i = 0, len = Math.ceil(1.25 * Math.random()); i < len; i++) { + // } + } + break } } } - // mobs get pushed away - for (let i = 0, len = mob.length; i < len; i++) { - if (Vector.magnitude(Vector.sub(m.hole.pos1, mob[i].position)) < 200) { - const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, mob[i].position)), -0.07) - Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull)); - } - if (Vector.magnitude(Vector.sub(m.hole.pos2, mob[i].position)) < 200) { - const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, mob[i].position)), -0.07) - Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull)); - } - } } } - - if (m.fieldCDcycle < m.cycle) { - const scale = 60 - const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame) - const sub = Vector.sub(simulation.mouseInGame, m.pos) - const mag = Vector.magnitude(sub) - - if (input.field) { - if (tech.isWormHolePause) { - const drain = m.fieldRegen + 0.0002 - if (m.energy > drain) { - m.energy -= drain - if (m.immuneCycle < m.cycle + 1) m.immuneCycle = m.cycle + 1; //player is immune to damage for 1 cycle - m.isBodiesAsleep = true; - - function sleep(who) { - for (let i = 0, len = who.length; i < len; ++i) { - if (!who[i].isSleeping) { - who[i].storeVelocity = who[i].velocity - who[i].storeAngularVelocity = who[i].angularVelocity - } - Matter.Sleeping.set(who[i], true) - } - } - sleep(mob); - sleep(body); - sleep(bullet); - simulation.cycle--; //pause all functions that depend on game cycle increasing - Matter.Body.setVelocity(player, { //keep player frozen - x: 0, - y: -55 * player.mass * simulation.g //undo gravity before it is added - }); - player.force.x = 0 - player.force.y = 0 - } else { - m.wakeCheck(); - m.energy = 0; + if (tech.isWormHoleBullets) { + //teleport bullets + for (let i = 0, len = bullet.length; i < len; ++i) { //teleport bullets from hole1 to hole2 + if (!bullet[i].botType && !bullet[i].isInHole) { //don't teleport bots + if (Vector.magnitude(Vector.sub(m.hole.pos1, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1 + Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos2, Vector.sub(m.hole.pos1, bullet[i].position))); + m.fieldRange += 5 + bullet[i].isInHole = true + } else if (Vector.magnitude(Vector.sub(m.hole.pos2, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1 + Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos1, Vector.sub(m.hole.pos2, bullet[i].position))); + m.fieldRange += 5 + bullet[i].isInHole = true } } + } + // mobs get pushed away + for (let i = 0, len = mob.length; i < len; i++) { + if (Vector.magnitude(Vector.sub(m.hole.pos1, mob[i].position)) < 200) { + const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, mob[i].position)), -0.07) + Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull)); + } + if (Vector.magnitude(Vector.sub(m.hole.pos2, mob[i].position)) < 200) { + const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, mob[i].position)), -0.07) + Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull)); + } + } + } + } - m.grabPowerUp(); - //draw possible wormhole - if (tech.isWormholeMapIgnore && Matter.Query.ray(map, m.pos, justPastMouse).length !== 0) { - this.drain = (0.06 + 0.006 * Math.sqrt(mag)) * 2 - } else { - this.drain = tech.isFreeWormHole ? 0 : 0.06 + 0.006 * Math.sqrt(mag) - } - const unit = Vector.perp(Vector.normalise(sub)) - const where = { - x: m.pos.x + 30 * Math.cos(m.angle), - y: m.pos.y + 30 * Math.sin(m.angle) - } - m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) - const edge2a = Vector.add(Vector.mult(unit, 1.5 * m.fieldRange), simulation.mouseInGame) - const edge2b = Vector.add(Vector.mult(unit, -1.5 * m.fieldRange), simulation.mouseInGame) - ctx.beginPath(); - ctx.moveTo(where.x, where.y) - ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2a.x, edge2a.y); - ctx.moveTo(where.x, where.y) - ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2b.x, edge2b.y); - if ( - mag > 250 && m.energy > this.drain && - (tech.isWormholeMapIgnore || Matter.Query.ray(map, m.pos, justPastMouse).length === 0) && - Matter.Query.region(map, { - min: { - x: simulation.mouseInGame.x - scale, - y: simulation.mouseInGame.y - scale - }, - max: { - x: simulation.mouseInGame.x + scale, - y: simulation.mouseInGame.y + scale - } - }).length === 0 - ) { - m.hole.isReady = true; - // ctx.fillStyle = "rgba(255,255,255,0.5)" - // ctx.fill(); - ctx.lineWidth = 1 - ctx.strokeStyle = "#000" - ctx.stroke(); - } else { - m.hole.isReady = false; - ctx.lineWidth = 1 - ctx.strokeStyle = "#000" - ctx.lineDashOffset = 30 * Math.random() - ctx.setLineDash([20, 40]); - ctx.stroke(); - ctx.setLineDash([]); - } - } else { - if (tech.isWormHolePause && m.isBodiesAsleep) m.wakeCheck(); + if (m.fieldCDcycle < m.cycle) { + const scale = 60 + const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame) + const sub = Vector.sub(simulation.mouseInGame, m.pos) + const mag = Vector.magnitude(sub) - //make new wormhole - if ( - m.hole.isReady && mag > 250 && m.energy > this.drain && - (tech.isWormholeMapIgnore || Matter.Query.ray(map, m.pos, justPastMouse).length === 0) && - Matter.Query.region(map, { - min: { - x: simulation.mouseInGame.x - scale, - y: simulation.mouseInGame.y - scale - }, - max: { - x: simulation.mouseInGame.x + scale, - y: simulation.mouseInGame.y + scale + if (input.field) { + if (tech.isWormHolePause) { + const drain = m.fieldRegen + 0.0002 + if (m.energy > drain) { + m.energy -= drain + if (m.immuneCycle < m.cycle + 1) m.immuneCycle = m.cycle + 1; //player is immune to damage for 1 cycle + m.isBodiesAsleep = true; + + function sleep(who) { + for (let i = 0, len = who.length; i < len; ++i) { + if (!who[i].isSleeping) { + who[i].storeVelocity = who[i].velocity + who[i].storeAngularVelocity = who[i].angularVelocity + } + Matter.Sleeping.set(who[i], true) } - }).length === 0 - ) { - m.energy -= this.drain - m.hole.isReady = false; - m.fieldRange = 0 - Matter.Body.setPosition(player, simulation.mouseInGame); - m.buttonCD_jump = 0 //this might fix a bug with jumping - const velocity = Vector.mult(Vector.normalise(sub), 20) - Matter.Body.setVelocity(player, { - x: velocity.x, - y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer + } + sleep(mob); + sleep(body); + sleep(bullet); + simulation.cycle--; //pause all functions that depend on game cycle increasing + Matter.Body.setVelocity(player, { //keep player frozen + x: 0, + y: -55 * player.mass * simulation.g //undo gravity before it is added }); - if (m.immuneCycle < m.cycle + 5) m.immuneCycle = m.cycle + 5; //player is immune to damage for 1/4 seconds - // move bots to player - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType) { - Matter.Body.setPosition(bullet[i], Vector.add(player.position, { - x: 250 * (Math.random() - 0.5), - y: 250 * (Math.random() - 0.5) - })); - Matter.Body.setVelocity(bullet[i], { - x: 0, - y: 0 - }); - } + player.force.x = 0 + player.force.y = 0 + } else { + m.wakeCheck(); + m.energy = 0; + } + } + + m.grabPowerUp(); + //draw possible wormhole + if (tech.isWormholeMapIgnore && Matter.Query.ray(map, m.pos, justPastMouse).length !== 0) { + this.drain = (0.06 + 0.006 * Math.sqrt(mag)) * 2 + } else { + this.drain = tech.isFreeWormHole ? 0 : 0.06 + 0.006 * Math.sqrt(mag) + } + const unit = Vector.perp(Vector.normalise(sub)) + const where = { + x: m.pos.x + 30 * Math.cos(m.angle), + y: m.pos.y + 30 * Math.sin(m.angle) + } + m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) + const edge2a = Vector.add(Vector.mult(unit, 1.5 * m.fieldRange), simulation.mouseInGame) + const edge2b = Vector.add(Vector.mult(unit, -1.5 * m.fieldRange), simulation.mouseInGame) + ctx.beginPath(); + ctx.moveTo(where.x, where.y) + ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2a.x, edge2a.y); + ctx.moveTo(where.x, where.y) + ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2b.x, edge2b.y); + if ( + mag > 250 && m.energy > this.drain && + (tech.isWormholeMapIgnore || Matter.Query.ray(map, m.pos, justPastMouse).length === 0) && + Matter.Query.region(map, { + min: { + x: simulation.mouseInGame.x - scale, + y: simulation.mouseInGame.y - scale + }, + max: { + x: simulation.mouseInGame.x + scale, + y: simulation.mouseInGame.y + scale } + }).length === 0 + ) { + m.hole.isReady = true; + // ctx.fillStyle = "rgba(255,255,255,0.5)" + // ctx.fill(); + ctx.lineWidth = 1 + ctx.strokeStyle = "#000" + ctx.stroke(); + } else { + m.hole.isReady = false; + ctx.lineWidth = 1 + ctx.strokeStyle = "#000" + ctx.lineDashOffset = 30 * Math.random() + ctx.setLineDash([20, 40]); + ctx.stroke(); + ctx.setLineDash([]); + } + } else { + if (tech.isWormHolePause && m.isBodiesAsleep) m.wakeCheck(); - //set holes - m.hole.isOn = true; - m.hole.pos1.x = m.pos.x - m.hole.pos1.y = m.pos.y - m.hole.pos2.x = player.position.x - m.hole.pos2.y = player.position.y - m.hole.angle = Math.atan2(sub.y, sub.x) - m.hole.unit = Vector.perp(Vector.normalise(sub)) + //make new wormhole + if ( + m.hole.isReady && mag > 250 && m.energy > this.drain && + (tech.isWormholeMapIgnore || Matter.Query.ray(map, m.pos, justPastMouse).length === 0) && + Matter.Query.region(map, { + min: { + x: simulation.mouseInGame.x - scale, + y: simulation.mouseInGame.y - scale + }, + max: { + x: simulation.mouseInGame.x + scale, + y: simulation.mouseInGame.y + scale + } + }).length === 0 + ) { + m.energy -= this.drain + m.hole.isReady = false; + m.fieldRange = 0 + Matter.Body.setPosition(player, simulation.mouseInGame); + m.buttonCD_jump = 0 //this might fix a bug with jumping + const velocity = Vector.mult(Vector.normalise(sub), 20) + Matter.Body.setVelocity(player, { + x: velocity.x, + y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer + }); + if (m.immuneCycle < m.cycle + 5) m.immuneCycle = m.cycle + 5; //player is immune to damage for 1/4 seconds + // move bots to player + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) { + Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + x: 250 * (Math.random() - 0.5), + y: 250 * (Math.random() - 0.5) + })); + Matter.Body.setVelocity(bullet[i], { + x: 0, + y: 0 + }); + } + } - if (tech.isWormholeDamage) { - who = Matter.Query.ray(mob, m.pos, simulation.mouseInGame, 100) - for (let i = 0; i < who.length; i++) { - if (who[i].body.alive) { - mobs.statusDoT(who[i].body, 1, 420) - mobs.statusStun(who[i].body, 360) - } + //set holes + m.hole.isOn = true; + m.hole.pos1.x = m.pos.x + m.hole.pos1.y = m.pos.y + m.hole.pos2.x = player.position.x + m.hole.pos2.y = player.position.y + m.hole.angle = Math.atan2(sub.y, sub.x) + m.hole.unit = Vector.perp(Vector.normalise(sub)) + + if (tech.isWormholeDamage) { + who = Matter.Query.ray(mob, m.pos, simulation.mouseInGame, 100) + for (let i = 0; i < who.length; i++) { + if (who[i].body.alive) { + mobs.statusDoT(who[i].body, 1, 420) + mobs.statusStun(who[i].body, 360) } } } } } - // if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - // const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame) - // const scale = 60 - // const sub = Vector.sub(simulation.mouseInGame, m.pos) - // const mag = Vector.magnitude(sub) - // const drain = tech.isFreeWormHole ? 0 : 0.06 + 0.006 * Math.sqrt(mag) - // if (m.hole.isReady && mag > 250 && m.energy > drain) { - // if ( - // Matter.Query.region(map, { - // min: { - // x: simulation.mouseInGame.x - scale, - // y: simulation.mouseInGame.y - scale - // }, - // max: { - // x: simulation.mouseInGame.x + scale, - // y: simulation.mouseInGame.y + scale - // } - // }).length === 0 && - // Matter.Query.ray(map, m.pos, justPastMouse).length === 0 - // // Matter.Query.ray(map, m.pos, simulation.mouseInGame).length === 0 && - // // Matter.Query.ray(map, player.position, simulation.mouseInGame).length === 0 && - // // Matter.Query.ray(map, player.position, justPastMouse).length === 0 - // ) { - // m.energy -= drain - // m.hole.isReady = false; - // m.fieldRange = 0 - // Matter.Body.setPosition(player, simulation.mouseInGame); - // m.buttonCD_jump = 0 //this might fix a bug with jumping - // const velocity = Vector.mult(Vector.normalise(sub), 20) - // Matter.Body.setVelocity(player, { - // x: velocity.x, - // y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer - // }); - // if (m.immuneCycle < m.cycle + 15) m.immuneCycle = m.cycle + 15; //player is immune to damage for 1/4 seconds - // // move bots to player - // for (let i = 0; i < bullet.length; i++) { - // if (bullet[i].botType) { - // Matter.Body.setPosition(bullet[i], Vector.add(player.position, { - // x: 250 * (Math.random() - 0.5), - // y: 250 * (Math.random() - 0.5) - // })); - // Matter.Body.setVelocity(bullet[i], { - // x: 0, - // y: 0 - // }); - // } - // } - // //set holes - // m.hole.isOn = true; - // m.hole.pos1.x = m.pos.x - // m.hole.pos1.y = m.pos.y - // m.hole.pos2.x = player.position.x - // m.hole.pos2.y = player.position.y - // m.hole.angle = Math.atan2(sub.y, sub.x) - // m.hole.unit = Vector.perp(Vector.normalise(sub)) + // if (true && m.energy > 0.5) { //teleport away low mass mobs + // // && !(m.cycle % 1) + // const hit = Matter.Query.region(mob, { + // min: { + // x: m.pos.x - 80, + // y: m.pos.y - 80 + // }, + // max: { + // x: m.pos.x + 80, + // y: m.pos.y + 160 + // } + // }) - // if (tech.isWormholeDamage) { - // who = Matter.Query.ray(mob, m.pos, simulation.mouseInGame, 100) - // for (let i = 0; i < who.length; i++) { - // if (who[i].body.alive) { - // mobs.statusDoT(who[i].body, 1, 420) - // mobs.statusStun(who[i].body, 360) - // } - // } + // // find incoming mob with low mass + // for (let i = 0; i < hit.length; i++) { + // if (hit[i].mass < 4 && m.energy > hit[i].mass * 0.06) { + // //is the mob moving towards the player? + + // // console.log('found one', hit[i].mass) + // const unit = Vector.normalise(hit[i].velocity) + // const jump = Vector.mult(unit, 200) + // const where = Vector.add(hit[i].position, jump) + // if (Matter.Query.ray(map, hit[i].position, where).length === 0) { // check if space 180 from mob is clear of body and map + // // m.energy -= hit[i].mass * 0.06 + // // m.fieldCDcycle = m.cycle + 30; + // simulation.drawList.push({ x: hit[i].position.x, y: hit[i].position.y, radius: 20, color: "#fff", time: 16 }); + // Matter.Body.setPosition(hit[i], where); + // simulation.drawList.push({ x: hit[i].position.x, y: hit[i].position.y, radius: 20, color: "#fff", time: 16 }); // } - // } else { - // //draw failed wormhole - // const unit = Vector.perp(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos))) - // const where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle), } - // m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) - // const edge2a = Vector.add(Vector.mult(unit, 1.5 * m.fieldRange), simulation.mouseInGame) - // const edge2b = Vector.add(Vector.mult(unit, -1.5 * m.fieldRange), simulation.mouseInGame) - // ctx.beginPath(); - // ctx.moveTo(where.x, where.y) - // ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2a.x, edge2a.y); - // ctx.lineTo(edge2b.x, edge2b.y) - // ctx.bezierCurveTo(simulation.mouseInGame.x, simulation.mouseInGame.y, where.x, where.y, where.x, where.y); - // // ctx.fillStyle = "rgba(255,255,255,0.5)" - // // ctx.fill(); - // ctx.lineWidth = 1 - // ctx.strokeStyle = "#000" - // ctx.lineDashOffset = 30 * Math.random() - // ctx.setLineDash([20, 40]); - // ctx.stroke(); - // ctx.setLineDash([]); + // // break // } // } - // m.grabPowerUp(); - // } else { - // m.hole.isReady = true; // } - m.drawRegenEnergy() } - }, + // if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + // const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame) + // const scale = 60 + // const sub = Vector.sub(simulation.mouseInGame, m.pos) + // const mag = Vector.magnitude(sub) + // const drain = tech.isFreeWormHole ? 0 : 0.06 + 0.006 * Math.sqrt(mag) + // if (m.hole.isReady && mag > 250 && m.energy > drain) { + // if ( + // Matter.Query.region(map, { + // min: { + // x: simulation.mouseInGame.x - scale, + // y: simulation.mouseInGame.y - scale + // }, + // max: { + // x: simulation.mouseInGame.x + scale, + // y: simulation.mouseInGame.y + scale + // } + // }).length === 0 && + // Matter.Query.ray(map, m.pos, justPastMouse).length === 0 + // // Matter.Query.ray(map, m.pos, simulation.mouseInGame).length === 0 && + // // Matter.Query.ray(map, player.position, simulation.mouseInGame).length === 0 && + // // Matter.Query.ray(map, player.position, justPastMouse).length === 0 + // ) { + // m.energy -= drain + // m.hole.isReady = false; + // m.fieldRange = 0 + // Matter.Body.setPosition(player, simulation.mouseInGame); + // m.buttonCD_jump = 0 //this might fix a bug with jumping + // const velocity = Vector.mult(Vector.normalise(sub), 20) + // Matter.Body.setVelocity(player, { + // x: velocity.x, + // y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer + // }); + // if (m.immuneCycle < m.cycle + 15) m.immuneCycle = m.cycle + 15; //player is immune to damage for 1/4 seconds + // // move bots to player + // for (let i = 0; i < bullet.length; i++) { + // if (bullet[i].botType) { + // Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + // x: 250 * (Math.random() - 0.5), + // y: 250 * (Math.random() - 0.5) + // })); + // Matter.Body.setVelocity(bullet[i], { + // x: 0, + // y: 0 + // }); + // } + // } - // rewind: function() { - // if (input.down) { - // if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - // const DRAIN = 0.01 - // if (this.rewindCount < 289 && m.energy > DRAIN) { - // m.energy -= DRAIN + // //set holes + // m.hole.isOn = true; + // m.hole.pos1.x = m.pos.x + // m.hole.pos1.y = m.pos.y + // m.hole.pos2.x = player.position.x + // m.hole.pos2.y = player.position.y + // m.hole.angle = Math.atan2(sub.y, sub.x) + // m.hole.unit = Vector.perp(Vector.normalise(sub)) - - // if (this.rewindCount === 0) { - // const shortPause = function() { - // if (m.defaultFPSCycle < m.cycle) { //back to default values - // simulation.fpsCap = simulation.fpsCapDefault - // simulation.fpsInterval = 1000 / simulation.fpsCap; - // // document.getElementById("dmg").style.transition = "opacity 1s"; - // // document.getElementById("dmg").style.opacity = "0"; - // } else { - // requestAnimationFrame(shortPause); - // } - // }; - // if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause); - // simulation.fpsCap = 4 //1 is longest pause, 4 is standard - // simulation.fpsInterval = 1000 / simulation.fpsCap; - // m.defaultFPSCycle = m.cycle - // } - - - // this.rewindCount += 10; - // simulation.wipe = function() { //set wipe to have trails - // // ctx.fillStyle = "rgba(255,255,255,0)"; - // ctx.fillStyle = `rgba(221,221,221,${0.004})`; - // ctx.fillRect(0, 0, canvas.width, canvas.height); - // } - // let history = m.history[(m.cycle - this.rewindCount) % 300] - // Matter.Body.setPosition(player, history.position); - // Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y }); - // if (history.health > m.health) { - // m.health = history.health - // m.displayHealth(); - // } - // //grab power ups - // for (let i = 0, len = powerUp.length; i < len; ++i) { - // const dxP = player.position.x - powerUp[i].position.x; - // const dyP = player.position.y - powerUp[i].position.y; - // if (dxP * dxP + dyP * dyP < 50000 && !simulation.isChoosing && !(m.health === m.maxHealth && powerUp[i].name === "heal")) { - // powerUps.onPickUp(player.position); - // powerUp[i].effect(); - // Matter.Composite.remove(engine.world, powerUp[i]); - // powerUp.splice(i, 1); - // const shortPause = function() { - // if (m.defaultFPSCycle < m.cycle) { //back to default values - // simulation.fpsCap = simulation.fpsCapDefault - // simulation.fpsInterval = 1000 / simulation.fpsCap; - // // document.getElementById("dmg").style.transition = "opacity 1s"; - // // document.getElementById("dmg").style.opacity = "0"; - // } else { - // requestAnimationFrame(shortPause); - // } - // }; - // if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause); - // simulation.fpsCap = 3 //1 is longest pause, 4 is standard - // simulation.fpsInterval = 1000 / simulation.fpsCap; - // m.defaultFPSCycle = m.cycle - // break; //because the array order is messed up after splice - // } - // } - // m.immuneCycle = m.cycle + 5; //player is immune to damage for 30 cycles - // } else { - // m.fieldCDcycle = m.cycle + 30; - // // m.resetHistory(); - // } - // } else { - // if (this.rewindCount !== 0) { - // m.fieldCDcycle = m.cycle + 30; - // m.resetHistory(); - // this.rewindCount = 0; - // simulation.wipe = function() { //set wipe to normal - // ctx.clearRect(0, 0, canvas.width, canvas.height); - // } - // } - // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - // } - // } - // m.drawRegenEnergy() - // }, + // if (tech.isWormholeDamage) { + // who = Matter.Query.ray(mob, m.pos, simulation.mouseInGame, 100) + // for (let i = 0; i < who.length; i++) { + // if (who[i].body.alive) { + // mobs.statusDoT(who[i].body, 1, 420) + // mobs.statusStun(who[i].body, 360) + // } + // } + // } + // } else { + // //draw failed wormhole + // const unit = Vector.perp(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos))) + // const where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle), } + // m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) + // const edge2a = Vector.add(Vector.mult(unit, 1.5 * m.fieldRange), simulation.mouseInGame) + // const edge2b = Vector.add(Vector.mult(unit, -1.5 * m.fieldRange), simulation.mouseInGame) + // ctx.beginPath(); + // ctx.moveTo(where.x, where.y) + // ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2a.x, edge2a.y); + // ctx.lineTo(edge2b.x, edge2b.y) + // ctx.bezierCurveTo(simulation.mouseInGame.x, simulation.mouseInGame.y, where.x, where.y, where.x, where.y); + // // ctx.fillStyle = "rgba(255,255,255,0.5)" + // // ctx.fill(); + // ctx.lineWidth = 1 + // ctx.strokeStyle = "#000" + // ctx.lineDashOffset = 30 * Math.random() + // ctx.setLineDash([20, 40]); + // ctx.stroke(); + // ctx.setLineDash([]); + // } + // } + // m.grabPowerUp(); + // } else { + // m.hole.isReady = true; + // } + m.drawRegenEnergy() + } }, + + // rewind: function() { + // if (input.down) { + // if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + // const DRAIN = 0.01 + // if (this.rewindCount < 289 && m.energy > DRAIN) { + // m.energy -= DRAIN + + + // if (this.rewindCount === 0) { + // const shortPause = function() { + // if (m.defaultFPSCycle < m.cycle) { //back to default values + // simulation.fpsCap = simulation.fpsCapDefault + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // // document.getElementById("dmg").style.transition = "opacity 1s"; + // // document.getElementById("dmg").style.opacity = "0"; + // } else { + // requestAnimationFrame(shortPause); + // } + // }; + // if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause); + // simulation.fpsCap = 4 //1 is longest pause, 4 is standard + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // m.defaultFPSCycle = m.cycle + // } + + + // this.rewindCount += 10; + // simulation.wipe = function() { //set wipe to have trails + // // ctx.fillStyle = "rgba(255,255,255,0)"; + // ctx.fillStyle = `rgba(221,221,221,${0.004})`; + // ctx.fillRect(0, 0, canvas.width, canvas.height); + // } + // let history = m.history[(m.cycle - this.rewindCount) % 300] + // Matter.Body.setPosition(player, history.position); + // Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y }); + // if (history.health > m.health) { + // m.health = history.health + // m.displayHealth(); + // } + // //grab power ups + // for (let i = 0, len = powerUp.length; i < len; ++i) { + // const dxP = player.position.x - powerUp[i].position.x; + // const dyP = player.position.y - powerUp[i].position.y; + // if (dxP * dxP + dyP * dyP < 50000 && !simulation.isChoosing && !(m.health === m.maxHealth && powerUp[i].name === "heal")) { + // powerUps.onPickUp(player.position); + // powerUp[i].effect(); + // Matter.Composite.remove(engine.world, powerUp[i]); + // powerUp.splice(i, 1); + // const shortPause = function() { + // if (m.defaultFPSCycle < m.cycle) { //back to default values + // simulation.fpsCap = simulation.fpsCapDefault + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // // document.getElementById("dmg").style.transition = "opacity 1s"; + // // document.getElementById("dmg").style.opacity = "0"; + // } else { + // requestAnimationFrame(shortPause); + // } + // }; + // if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause); + // simulation.fpsCap = 3 //1 is longest pause, 4 is standard + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // m.defaultFPSCycle = m.cycle + // break; //because the array order is messed up after splice + // } + // } + // m.immuneCycle = m.cycle + 5; //player is immune to damage for 30 cycles + // } else { + // m.fieldCDcycle = m.cycle + 30; + // // m.resetHistory(); + // } + // } else { + // if (this.rewindCount !== 0) { + // m.fieldCDcycle = m.cycle + 30; + // m.resetHistory(); + // this.rewindCount = 0; + // simulation.wipe = function() { //set wipe to normal + // ctx.clearRect(0, 0, canvas.width, canvas.height); + // } + // } + // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + // } + // } + // m.drawRegenEnergy() + // }, + }, ], //************************************************************************************ //************************************************************************************ @@ -4807,85 +4843,85 @@ const m = { m.isShipMode = true // simulation.isCheating = true const points = [{ - x: 29.979168754143455, - y: 4.748337243898336 - }, - { - x: 27.04503734408824, - y: 13.7801138209198 - }, - { - x: 21.462582474874278, - y: 21.462582475257523 - }, - { - x: 13.780113820536943, - y: 27.045037344471485 - }, - { - x: 4.74833724351507, - y: 29.979168754526473 - }, - { - x: -4.748337245049098, - y: 29.979168754526473 - }, - { - x: -13.780113822071026, - y: 27.045037344471485 - }, - { - x: -21.46258247640829, - y: 21.462582475257523 - }, - { - x: -27.045037345621797, - y: 13.7801138209198 - }, - { - x: -29.979168755677012, - y: 4.748337243898336 - }, - { - x: -29.979168755677012, - y: -4.7483372446656045 - }, - { - x: -27.045037345621797, - y: -13.78011382168726 - }, - { - x: -21.46258247640829, - y: -21.462582476024817 - }, - { - x: -13.780113822071026, - y: -27.045037345239006 - }, - { - x: -4.748337245049098, - y: -29.97916875529422 - }, - { - x: 4.74833724351507, - y: -29.97916875529422 - }, - { - x: 13.780113820536943, - y: -27.045037345239006 - }, - { - x: 21.462582474874278, - y: -21.462582476024817 - }, - { - x: 27.04503734408824, - y: -13.78011382168726 - }, - { - x: 29.979168754143455, - y: -4.7483372446656045 - } + x: 29.979168754143455, + y: 4.748337243898336 + }, + { + x: 27.04503734408824, + y: 13.7801138209198 + }, + { + x: 21.462582474874278, + y: 21.462582475257523 + }, + { + x: 13.780113820536943, + y: 27.045037344471485 + }, + { + x: 4.74833724351507, + y: 29.979168754526473 + }, + { + x: -4.748337245049098, + y: 29.979168754526473 + }, + { + x: -13.780113822071026, + y: 27.045037344471485 + }, + { + x: -21.46258247640829, + y: 21.462582475257523 + }, + { + x: -27.045037345621797, + y: 13.7801138209198 + }, + { + x: -29.979168755677012, + y: 4.748337243898336 + }, + { + x: -29.979168755677012, + y: -4.7483372446656045 + }, + { + x: -27.045037345621797, + y: -13.78011382168726 + }, + { + x: -21.46258247640829, + y: -21.462582476024817 + }, + { + x: -13.780113822071026, + y: -27.045037345239006 + }, + { + x: -4.748337245049098, + y: -29.97916875529422 + }, + { + x: 4.74833724351507, + y: -29.97916875529422 + }, + { + x: 13.780113820536943, + y: -27.045037345239006 + }, + { + x: 21.462582474874278, + y: -21.462582476024817 + }, + { + x: 27.04503734408824, + y: -13.78011382168726 + }, + { + x: 29.979168754143455, + y: -4.7483372446656045 + } ] // Matter.Body.setVertices(player, Matter.Vertices.create(points, player)) @@ -5042,7 +5078,7 @@ const m = { } //fix collisions - collisionChecks = function(event) { + collisionChecks = function (event) { const pairs = event.pairs; for (let i = 0, j = pairs.length; i != j; i++) { //mob + (player,bullet,body) collisions diff --git a/js/powerup.js b/js/powerup.js index 401f1f2..53a5c91 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -75,7 +75,7 @@ const powerUps = { } let text = '' for (let i = 0; i < num; i++) { - text += `
` + text += `
` } text += '
    ' for (let i = 0; i < num; i++) { @@ -90,7 +90,7 @@ const powerUps = { } let text = '' for (let i = 0; i < num; i++) { - text += `
` + text += `
` } text += '
    ' for (let i = 0; i < num; i++) { @@ -106,7 +106,7 @@ const powerUps = { } let text = '' for (let i = 0; i < num; i++) { - text += `
` + text += `
` } text += '
    ' for (let i = 0; i < num; i++) { @@ -120,7 +120,7 @@ const powerUps = { } let text = '' for (let i = 0; i < num; i++) { - text += `
` + text += `
` } text += '
    ' for (let i = 0; i < num; i++) { @@ -139,7 +139,7 @@ const powerUps = { } let text = '' for (let i = 0; i < num; i++) { - text += `
` + text += `
` } text += '
   ' for (let i = 0; i < num; i++) { @@ -154,7 +154,7 @@ const powerUps = { } let text = '' for (let i = 0; i < num; i++) { - text += `
` + text += `
` } text += '
    ' for (let i = 0; i < num; i++) { @@ -164,7 +164,7 @@ const powerUps = { }, }, totalPowerUps: 0, //used for tech that count power ups at the end of a level - do() {}, + do() { }, setDupChance() { if (tech.duplicationChance() > 0 || tech.isAnthropicTech) { if (tech.isPowerUpsVanish) { @@ -639,6 +639,8 @@ const powerUps = { } }, cancelText(type) { + // if (localSettings.isHideImages) { } + if (tech.isSuperDeterminism) { return `
` } else if (tech.isCancelTech) { @@ -655,18 +657,54 @@ const powerUps = { text += `
` // style = "margin-left: 192px; margin-right: -192px;" tech.junkResearchNumber = Math.ceil(4 * Math.random()) text += `
` - for (let i = 0; i < tech.junkResearchNumber; i++) text += `
` + for (let i = 0; i < tech.junkResearchNumber; i++) { + text += `
` + } text += `
  pseudoscience
` } else if (powerUps.research.count > 0) { text += `
` // style = "margin-left: 192px; margin-right: -192px;" text += `
` - for (let i = 0, len = Math.min(powerUps.research.count, 30); i < len; i++) text += `
` - text += `
  ${tech.isResearchReality?"alternate reality": "research"}
` + for (let i = 0, len = Math.min(powerUps.research.count, 30); i < len; i++) text += `
` + text += `  ${tech.isResearchReality ? "alternate reality" : "research"}
` } else { text += `
` } return text }, + researchAndCancelText(type) { + let text = `
` + if (type === "entanglement") { + text += `entanglement` //‌ + } else if (tech.isJunkResearch && powerUps.research.currentRerollCount < 3) { + text += `` // style = "margin-left: 192px; margin-right: -192px;" + tech.junkResearchNumber = Math.ceil(4 * Math.random()) + text += `
` + for (let i = 0, len = tech.junkResearchNumber; i < len; i++) { + text += `
` + } + text += `
  ${tech.isResearchReality ? "alternate reality" : "research"}
` + } else if (powerUps.research.count > 0) { + text += `` // style = "margin-left: 192px; margin-right: -192px;" + text += `
` + let researchCap = 18 + if (tech.isCancelTech) researchCap -= 2 + if (canvas.width < 1951) researchCap -= 3 + if (canvas.width < 1711) researchCap -= 4 + for (let i = 0, len = Math.min(powerUps.research.count, researchCap); i < len; i++) { + text += `
` + } + text += `
  ${tech.isResearchReality ? "alternate reality" : "research"}
` + } else { + text += `research` //‌ + } + if (tech.isCancelTech) { + text += `randomize` + } else { + text += `cancel` + } + + return text + "
" + }, buildColumns(totalChoices, type) { let width if (canvas.width < 1710) { @@ -676,18 +714,17 @@ const powerUps = { } else { width = "384px" } - // if (canvas.width < 1500) { - // width = "340px" - // } else if (canvas.width < 1950) { - // width = "360px" - // } else { - // width = "384px" - // } + let text = "" - if (totalChoices === 1 || localSettings.isHideImages || canvas.width < 1200) { + if (localSettings.isHideImages) { document.getElementById("choose-grid").style.gridTemplateColumns = width - text += powerUps.cancelText(type) - text += powerUps.researchText(type) + text += powerUps.researchAndCancelText(type) + } else if (totalChoices === 1 || canvas.width < 1200) { + document.getElementById("choose-grid").style.gridTemplateColumns = width + text += powerUps.researchAndCancelText(type) + // console.log('hi') + // text += powerUps.cancelText(type) + // text += powerUps.researchText(type) } else if (totalChoices === 2) { document.getElementById("choose-grid").style.gridTemplateColumns = `repeat(2, ${width})` text += powerUps.researchText(type) @@ -727,14 +764,14 @@ const powerUps = { ${b.guns[choose].description}` }, fieldText(choose, click) { - const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/field/${m.fieldUpgrades[choose].name}${choose === 0 ? Math.floor(Math.random()*10) : ""}.webp');"` + const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/field/${m.fieldUpgrades[choose].name}${choose === 0 ? Math.floor(Math.random() * 10) : ""}.webp');"` return `
  ${m.fieldUpgrades[choose].name}
${m.fieldUpgrades[choose].description}
` }, techText(choose, click) { - const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : ""; + const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; const style = localSettings.isHideImages || tech.tech[choose].isLore ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` return `
@@ -742,7 +779,7 @@ const powerUps = { ${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` }, skinTechText(choose, click) { - const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : ""; + const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` return `
@@ -755,7 +792,7 @@ const powerUps = { ${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` }, fieldTechText(choose, click) { - const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : ""; + const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` return `
@@ -768,7 +805,7 @@ const powerUps = { ${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` }, gunTechText(choose, click) { - const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : ""; + const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` return `
@@ -781,7 +818,7 @@ const powerUps = { ${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` }, junkTechText(choose, click) { - const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : ""; + const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-size: contain;background-repeat: no-repeat;background-image: url('img/junk.webp');"` if (!localSettings.isHideImages) { setTimeout(() => { //delay so that the html element exists @@ -873,7 +910,7 @@ const powerUps = { // const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : ""; // text += `
⭓▸●■   ${tech.tech[choose].name} ${isCount}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` const choose = botTech[Math.floor(Math.random() * botTech.length)]; - const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : ""; + const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` text += `
@@ -938,7 +975,7 @@ const powerUps = { // const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : ""; // text += `
⭓▸●■   ${tech.tech[choose].name} ${isCount}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` const choose = botTech[Math.floor(Math.random() * botTech.length)]; - const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : ""; + const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` text += `
@@ -1021,7 +1058,7 @@ const powerUps = { } removeOption(choose) //move from future options pool to avoid repeats on this selection tech.tech[choose].isRecentlyShown = true //this flag prevents this option from being shown the next time you pick up a tech power up - const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : ""; + const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; if (tech.tech[choose].isFieldTech) { text += powerUps.fieldTechText(choose, `powerUps.choose('tech',${choose})`) } else if (tech.tech[choose].isGunTech) { @@ -1045,7 +1082,7 @@ const powerUps = { // const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : ""; // text += `
⭓▸●■   ${tech.tech[choose].name} ${isCount}
${tech.tech[choose].descriptionFunction ? tech.tech[choose].descriptionFunction() : tech.tech[choose].description}
` const choose = botTech[Math.floor(Math.random() * botTech.length)]; - const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : ""; + const techCountText = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; const style = localSettings.isHideImages ? powerUps.hideStyle : `style="background-image: url('img/${tech.tech[choose].name}.webp');"` text += `
@@ -1162,7 +1199,7 @@ const powerUps = { } for (let i = 0; i < localSettings.entanglement.techIndexes.length; i++) { //add tech let choose = localSettings.entanglement.techIndexes[i] - const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : ""; + const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count + 1}x)` : ""; if (choose === null || tech.tech[choose].count + 1 > tech.tech[choose].maxCount || !tech.tech[choose].allowed()) { // text += `
${tech.tech[choose].name} - incoherent
` diff --git a/js/simulation.js b/js/simulation.js index 5844627..6dc83ed 100644 --- a/js/simulation.js +++ b/js/simulation.js @@ -1137,31 +1137,16 @@ const simulation = { // }, checks() { if (!(m.cycle % 15)) { //4 times a second - //update defense bar - const defense = m.defense() + const defense = m.defense() //update defense bar if (m.lastCalculatedDefense !== defense) { document.getElementById("defense-bar").style.width = Math.floor(300 * m.maxHealth * (1 - defense)) + "px"; - - // if (m.lastCalculatedDefense === 1) document.getElementById("defense-bar").style.display = "inline" - // if (defense === 1) document.getElementById("defense-bar").style.display = "none" - // Math.pow(m.defense(), 0.13) m.lastCalculatedDefense = defense - // console.log(defense) } - - //update damage bar - const damage = tech.damageFromTech() + const damage = tech.damageFromTech() //update damage bar if (m.lastCalculatedDamage !== damage) { - canvas.width - // document.getElementById("damage-bar").style.width = Math.floor(Math.atan(damage - 1) / 6.28 * canvas.width) + "px"; - document.getElementById("damage-bar").style.height = Math.floor(Math.atan(damage - 1) / 3.14 * canvas.height) + "px"; - + document.getElementById("damage-bar").style.height = Math.floor(Math.atan(0.25 * damage - 0.25) / 1.65 * canvas.height) + "px"; m.lastCalculatedDamage = damage - console.log(damage) } - - - } if (!(m.cycle % 60)) { //once a second //energy overfill @@ -1211,7 +1196,17 @@ const simulation = { if (isNaN(player.position.x)) m.death(); if (m.lastKillCycle + 300 > m.cycle) { //effects active for 5 seconds after killing a mob if (tech.isEnergyRecovery && m.immuneCycle < m.cycle) m.energy += m.maxEnergy * 0.05 - if (tech.isHealthRecovery) m.addHealth(0.005 * m.maxHealth) + if (tech.isHealthRecovery) { + const heal = 0.005 * m.maxHealth + m.addHealth(heal) + simulation.drawList.push({ //add dmg to draw queue + x: m.pos.x, + y: m.pos.y, + radius: Math.sqrt(heal) * 150, + color: "rgba(0,255,200,0.6)", + time: 8 + }); + } } if (!(m.cycle % 420)) { //once every 7 seconds diff --git a/js/spawn.js b/js/spawn.js index 5bfe02e..53e713b 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -239,10 +239,10 @@ const spawn = { me.chaseSpeed = 3.3 me.isMACHO = true; me.frictionAir = 0.006 - me.onDeath = function() { + me.onDeath = function () { tech.isHarmMACHO = false; } - me.do = function() { + me.do = function () { if (!simulation.isTimeSkipping) { const sine = Math.sin(simulation.cycle * 0.015) this.radius = 370 * (1 + 0.1 * sine) @@ -300,7 +300,7 @@ const spawn = { me.collisionFilter.mask = 0; //cat.player //| cat.body me.chaseSpeed = 1.2 + 2.3 * Math.random() - me.awake = function() { + me.awake = function () { //chase player const sub = Vector.sub(player.position, this.position) const where = Vector.add(this.position, Vector.mult(Vector.normalise(sub), this.chaseSpeed)) @@ -334,7 +334,7 @@ const spawn = { ctx.fill(); this.radius = 100 * (1 + 0.25 * Math.sin(simulation.cycle * 0.03)) } - me.do = function() { //wake up after the player moves + me.do = function () { //wake up after the player moves if (player.speed > 1 && !m.isCloak) { if (this.distanceToPlayer() < 500) { const unit = Vector.rotate({ x: 1, y: 0 }, Math.random() * 6.28) @@ -375,7 +375,7 @@ const spawn = { me.isInvulnerable = false me.totalModes = 0 me.lastDamageCycle = 0 - me.onDamage = function() { + me.onDamage = function () { this.lastDamageCycle = this.cycle if (this.health < this.nextHealthThreshold) { if (this.health === 1) me.cycle = 1; //reset fight @@ -386,7 +386,7 @@ const spawn = { this.damageReduction = 0 } }; - me.invulnerable = function() { + me.invulnerable = function () { if (this.isInvulnerable) { this.invulnerableCount-- if (this.invulnerableCount < 0) { @@ -407,17 +407,17 @@ const spawn = { for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 15 + 6 * Math.random(); - ctx.strokeStyle = `rgba(255,255,255,${0.5+0.2*Math.random()})`; + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.stroke(); - ctx.fillStyle = `rgba(255,255,255,${Math.min(1, 120/(this.invulnerableCount+60))})`; + ctx.fillStyle = `rgba(255,255,255,${Math.min(1, 120 / (this.invulnerableCount + 60))})`; ctx.fill() } } - me.damageReductionDecay = function() { //slowly make the boss take more damage over time //damageReduction resets with each invulnerability phase + me.damageReductionDecay = function () { //slowly make the boss take more damage over time //damageReduction resets with each invulnerability phase if (!(me.cycle % 60) && this.lastDamageCycle + 240 > this.cycle) this.damageReduction *= 1.02 //only decay once a second //only decay if the player has done damage in the last 4 seconds } me.mobType = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)] - me.spawnMobs = function(index = 0) { + me.spawnMobs = function (index = 0) { const vertex = me.vertices[index] const unit = Vector.normalise(Vector.sub(me.position, vertex)) const where = Vector.add(vertex, Vector.mult(unit, -30)) @@ -427,271 +427,271 @@ const spawn = { } me.maxMobs = 400 me.mode = [{ - name: "boulders", - spawnRate: 170 - 6 * simulation.difficultyMode, - do() { - if (!(me.cycle % this.spawnRate) && mob.length < me.maxMobs) { - me.boulder(me.position.x, me.position.y + 250) - } - }, - enter() {}, - exit() {}, - }, { - name: "mobs", - // whoSpawn: spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)], - spawnRate: 280 - 20 * simulation.difficultyMode, - do() { - if (!(me.cycle % this.spawnRate) && mob.length < me.maxMobs) { - me.torque += 0.000015 * me.inertia; //spin - const index = Math.floor((me.cycle % (this.spawnRate * 6)) / this.spawnRate) //int from 0 to 5 - if (index === 0) me.mobType = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]; //fire a bullet from each vertex - me.spawnMobs(index) - // const vertex = me.vertices[index] - // const unit = Vector.normalise(Vector.sub(me.position, vertex)) - // const where = Vector.add(vertex, Vector.mult(unit, -30)) - // spawn[me.mobType](where.x + 50 * (Math.random() - 0.5), where.y + 50 * (Math.random() - 0.5)); - // const velocity = Vector.mult(Vector.perp(unit), -18) //give the mob a rotational velocity as if they were attached to a vertex - // Matter.Body.setVelocity(mob[mob.length - 1], { x: me.velocity.x + velocity.x, y: me.velocity.y + velocity.y }); - } - }, - enter() {}, - exit() {}, + name: "boulders", + spawnRate: 170 - 6 * simulation.difficultyMode, + do() { + if (!(me.cycle % this.spawnRate) && mob.length < me.maxMobs) { + me.boulder(me.position.x, me.position.y + 250) + } }, - { - name: "hoppers", - spawnRate: 480 - 16 * simulation.difficultyMode, - do() { - if (!(me.cycle % this.spawnRate) && mob.length < me.maxMobs) { - me.torque += 0.00002 * me.inertia; //spin - for (let i = 0; i < 6; i++) { - const vertex = me.vertices[i] - spawn.hopBullet(vertex.x + 50 * (Math.random() - 0.5), vertex.y + 50 * (Math.random() - 0.5), 13 + Math.ceil(Math.random() * 8)); //hopBullet(x, y, radius = 10 + Math.ceil(Math.random() * 8)) - Matter.Body.setDensity(mob[mob.length - 1], 0.002); //normal is 0.001 - const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(me.position, vertex))), -18) //give the mob a rotational velocity as if they were attached to a vertex - Matter.Body.setVelocity(mob[mob.length - 1], { - x: me.velocity.x + velocity.x, - y: me.velocity.y + velocity.y - }); - } - let where = { x: 600 - Math.random() * 100, y: -225 } - if (simulation.isHorizontalFlipped) where.x = -600 + Math.random() * 100 - spawn.hopBullet(where.x, where.y, 13 + Math.ceil(Math.random() * 8)); //hopBullet(x, y, radius = 10 + Math.ceil(Math.random() * 8)) + enter() { }, + exit() { }, + }, { + name: "mobs", + // whoSpawn: spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)], + spawnRate: 280 - 20 * simulation.difficultyMode, + do() { + if (!(me.cycle % this.spawnRate) && mob.length < me.maxMobs) { + me.torque += 0.000015 * me.inertia; //spin + const index = Math.floor((me.cycle % (this.spawnRate * 6)) / this.spawnRate) //int from 0 to 5 + if (index === 0) me.mobType = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]; //fire a bullet from each vertex + me.spawnMobs(index) + // const vertex = me.vertices[index] + // const unit = Vector.normalise(Vector.sub(me.position, vertex)) + // const where = Vector.add(vertex, Vector.mult(unit, -30)) + // spawn[me.mobType](where.x + 50 * (Math.random() - 0.5), where.y + 50 * (Math.random() - 0.5)); + // const velocity = Vector.mult(Vector.perp(unit), -18) //give the mob a rotational velocity as if they were attached to a vertex + // Matter.Body.setVelocity(mob[mob.length - 1], { x: me.velocity.x + velocity.x, y: me.velocity.y + velocity.y }); + } + }, + enter() { }, + exit() { }, + }, + { + name: "hoppers", + spawnRate: 480 - 16 * simulation.difficultyMode, + do() { + if (!(me.cycle % this.spawnRate) && mob.length < me.maxMobs) { + me.torque += 0.00002 * me.inertia; //spin + for (let i = 0; i < 6; i++) { + const vertex = me.vertices[i] + spawn.hopBullet(vertex.x + 50 * (Math.random() - 0.5), vertex.y + 50 * (Math.random() - 0.5), 13 + Math.ceil(Math.random() * 8)); //hopBullet(x, y, radius = 10 + Math.ceil(Math.random() * 8)) Matter.Body.setDensity(mob[mob.length - 1], 0.002); //normal is 0.001 + const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(me.position, vertex))), -18) //give the mob a rotational velocity as if they were attached to a vertex + Matter.Body.setVelocity(mob[mob.length - 1], { + x: me.velocity.x + velocity.x, + y: me.velocity.y + velocity.y + }); } - }, - enter() {}, - exit() {}, + let where = { x: 600 - Math.random() * 100, y: -225 } + if (simulation.isHorizontalFlipped) where.x = -600 + Math.random() * 100 + spawn.hopBullet(where.x, where.y, 13 + Math.ceil(Math.random() * 8)); //hopBullet(x, y, radius = 10 + Math.ceil(Math.random() * 8)) + Matter.Body.setDensity(mob[mob.length - 1], 0.002); //normal is 0.001 + } }, - { - name: "seekers", - spawnRate: 100 - 3 * simulation.difficultyMode, - do() { - if (!(me.cycle % this.spawnRate) && mob.length < me.maxMobs) { //spawn seeker - const index = Math.floor((me.cycle % 360) / 60) - spawn.seeker(me.vertices[index].x, me.vertices[index].y, 18 * (0.5 + Math.random())); //seeker(x, y, radius = 8, sides = 6) - const who = mob[mob.length - 1] - Matter.Body.setDensity(who, 0.00003); //normal is 0.001 - who.timeLeft = 720 + 30 * simulation.difficulty //* (0.8 + 0.4 * Math.random()); - who.accelMag = 0.0004 * simulation.accelScale; //* (0.8 + 0.4 * Math.random()) - who.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random()); - } - }, - enter() {}, - exit() {}, + enter() { }, + exit() { }, + }, + { + name: "seekers", + spawnRate: 100 - 3 * simulation.difficultyMode, + do() { + if (!(me.cycle % this.spawnRate) && mob.length < me.maxMobs) { //spawn seeker + const index = Math.floor((me.cycle % 360) / 60) + spawn.seeker(me.vertices[index].x, me.vertices[index].y, 18 * (0.5 + Math.random())); //seeker(x, y, radius = 8, sides = 6) + const who = mob[mob.length - 1] + Matter.Body.setDensity(who, 0.00003); //normal is 0.001 + who.timeLeft = 720 + 30 * simulation.difficulty //* (0.8 + 0.4 * Math.random()); + who.accelMag = 0.0004 * simulation.accelScale; //* (0.8 + 0.4 * Math.random()) + who.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random()); + } }, - { - name: "mines", - bombCycle: 0, - bombInterval: 10 - simulation.difficultyMode, - do() { - const yOff = 120 - this.bombCycle++ - if (!(this.bombCycle % this.bombInterval) && (this.bombCycle % 660) > 330) { //mines above player - if (simulation.isHorizontalFlipped) { - const x = m.pos.x + 200 * (Math.random() - 0.5) - if (x > -750) { //mines above player IN tunnel - spawn.mine(Math.min(Math.max(-730, x), 100), -450 - yOff * Math.random()) //player in main room - mob[mob.length - 1].fallHeight = -209 - } else { //mines above player NOT in tunnel - spawn.mine(Math.min(Math.max(-5375, x), -765), -1500 - yOff * Math.random()) //player in tunnel - mob[mob.length - 1].fallHeight = -9 - } - if (Math.random() < 0.5) { - spawn.mine(-5350 + 4550 * Math.random(), -1500 - yOff * Math.random()) //random mines - mob[mob.length - 1].fallHeight = -9 - } - } else { - const x = m.pos.x + 200 * (Math.random() - 0.5) - if (x < 750) { //mines above player IN tunnel - spawn.mine(Math.min(Math.max(-100, x), 735), -450 - yOff * Math.random()) //player in main room - mob[mob.length - 1].fallHeight = -209 - } else { //mines above player NOT in tunnel - spawn.mine(Math.min(Math.max(760, x), 5375), -1500 - yOff * Math.random()) //player in tunnel - mob[mob.length - 1].fallHeight = -9 - } - if (Math.random() < 0.5) { //random mines, but not in tunnel - spawn.mine(800 + 4550 * Math.random(), -1500 - yOff * Math.random()) //random mines - mob[mob.length - 1].fallHeight = -9 - } + enter() { }, + exit() { }, + }, + { + name: "mines", + bombCycle: 0, + bombInterval: 10 - simulation.difficultyMode, + do() { + const yOff = 120 + this.bombCycle++ + if (!(this.bombCycle % this.bombInterval) && (this.bombCycle % 660) > 330) { //mines above player + if (simulation.isHorizontalFlipped) { + const x = m.pos.x + 200 * (Math.random() - 0.5) + if (x > -750) { //mines above player IN tunnel + spawn.mine(Math.min(Math.max(-730, x), 100), -450 - yOff * Math.random()) //player in main room + mob[mob.length - 1].fallHeight = -209 + } else { //mines above player NOT in tunnel + spawn.mine(Math.min(Math.max(-5375, x), -765), -1500 - yOff * Math.random()) //player in tunnel + mob[mob.length - 1].fallHeight = -9 + } + if (Math.random() < 0.5) { + spawn.mine(-5350 + 4550 * Math.random(), -1500 - yOff * Math.random()) //random mines + mob[mob.length - 1].fallHeight = -9 + } + } else { + const x = m.pos.x + 200 * (Math.random() - 0.5) + if (x < 750) { //mines above player IN tunnel + spawn.mine(Math.min(Math.max(-100, x), 735), -450 - yOff * Math.random()) //player in main room + mob[mob.length - 1].fallHeight = -209 + } else { //mines above player NOT in tunnel + spawn.mine(Math.min(Math.max(760, x), 5375), -1500 - yOff * Math.random()) //player in tunnel + mob[mob.length - 1].fallHeight = -9 + } + if (Math.random() < 0.5) { //random mines, but not in tunnel + spawn.mine(800 + 4550 * Math.random(), -1500 - yOff * Math.random()) //random mines + mob[mob.length - 1].fallHeight = -9 } } - for (let i = 0; i < mob.length; i++) { //mines fall - if (mob[i].isMine) { - if (mob[i].position.y < mob[i].fallHeight) { - mob[i].force.y += mob[i].mass * 0.03; - } else if (!mob[i].isOnGround) { - mob[i].isOnGround = true - Matter.Body.setPosition(mob[i], { - x: mob[i].position.x, - y: mob[i].fallHeight - }) - } + } + for (let i = 0; i < mob.length; i++) { //mines fall + if (mob[i].isMine) { + if (mob[i].position.y < mob[i].fallHeight) { + mob[i].force.y += mob[i].mass * 0.03; + } else if (!mob[i].isOnGround) { + mob[i].isOnGround = true + Matter.Body.setPosition(mob[i], { + x: mob[i].position.x, + y: mob[i].fallHeight + }) } } - }, - enter() { - this.bombCycle = 0; - }, - exit() { - for (let i = 0; i < mob.length; i++) { - if (mob[i].isMine) mob[i].isExploding = true //explode the mines at the start of new round - } - }, + } }, - { - name: "orbiters", - spawnRate: Math.ceil(4 - 0.25 * simulation.difficultyMode), - orbitersCycle: 0, - do() { - this.orbitersCycle++ - if (!(this.orbitersCycle % this.spawnRate) && (this.orbitersCycle % 660) > 600 && mob.length < me.maxMobs) { - const speed = (0.01 + 0.0005 * simulation.difficultyMode) * ((Math.random() < 0.5) ? 0.85 : -1.15) - const phase = 0 //Math.floor(2 * Math.random()) * Math.PI - //find distance to play and set orbs at that range - const dist = me.distanceToPlayer() - //360 + 2150 * Math.random() - me.orbitalNoVelocity(me, dist + 900 * (Math.random() - 0.5), 0.1 * Math.random() + phase, speed) // orbital(who, radius, phase, speed) - } - }, - enter() {}, - exit() {}, + enter() { + this.bombCycle = 0; }, - { - name: "laser", - spinForce: 0.00000008, // * (Math.random() < 0.5 ? -1 : 1), - fadeCycle: 0, //fades in over 4 seconds - do() { - this.fadeCycle++ - if (this.fadeCycle > 0) { - me.torque += this.spinForce * me.inertia; //spin //0.00000015 - if (this.fadeCycle > 360) this.fadeCycle = -150 + 2 * simulation.difficultyMode * simulation.difficultyMode //turn laser off and reset + exit() { + for (let i = 0; i < mob.length; i++) { + if (mob[i].isMine) mob[i].isExploding = true //explode the mines at the start of new round + } + }, + }, + { + name: "orbiters", + spawnRate: Math.ceil(4 - 0.25 * simulation.difficultyMode), + orbitersCycle: 0, + do() { + this.orbitersCycle++ + if (!(this.orbitersCycle % this.spawnRate) && (this.orbitersCycle % 660) > 600 && mob.length < me.maxMobs) { + const speed = (0.01 + 0.0005 * simulation.difficultyMode) * ((Math.random() < 0.5) ? 0.85 : -1.15) + const phase = 0 //Math.floor(2 * Math.random()) * Math.PI + //find distance to play and set orbs at that range + const dist = me.distanceToPlayer() + //360 + 2150 * Math.random() + me.orbitalNoVelocity(me, dist + 900 * (Math.random() - 0.5), 0.1 * Math.random() + phase, speed) // orbital(who, radius, phase, speed) + } + }, + enter() { }, + exit() { }, + }, + { + name: "laser", + spinForce: 0.00000008, // * (Math.random() < 0.5 ? -1 : 1), + fadeCycle: 0, //fades in over 4 seconds + do() { + this.fadeCycle++ + if (this.fadeCycle > 0) { + me.torque += this.spinForce * me.inertia; //spin //0.00000015 + if (this.fadeCycle > 360) this.fadeCycle = -150 + 2 * simulation.difficultyMode * simulation.difficultyMode //turn laser off and reset + ctx.strokeStyle = "#50f"; + ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); + ctx.lineWidth = 1.5; + ctx.beginPath(); + if (this.fadeCycle < 120) { //damage scales up over 2 seconds to give player time to move as it fades in + const scale = this.fadeCycle / 120 + const dmg = this.fadeCycle < 60 ? 0 : 0.13 * simulation.dmgScale * scale + me.lasers(me.vertices[0], me.angle + Math.PI / 6, dmg); + me.lasers(me.vertices[1], me.angle + 3 * Math.PI / 6, dmg); + me.lasers(me.vertices[2], me.angle + 5 * Math.PI / 6, dmg); + me.lasers(me.vertices[3], me.angle + 7 * Math.PI / 6, dmg); + me.lasers(me.vertices[4], me.angle + 9 * Math.PI / 6, dmg); + me.lasers(me.vertices[5], me.angle + 11 * Math.PI / 6, dmg); + ctx.strokeStyle = `rgba(85, 0, 255,${scale})`; + ctx.stroke(); + ctx.strokeStyle = `rgba(80, 0, 255,${0.07 * scale})` + } else if (this.fadeCycle > 0) { + me.lasers(me.vertices[0], me.angle + Math.PI / 6); + me.lasers(me.vertices[1], me.angle + 3 * Math.PI / 6); + me.lasers(me.vertices[2], me.angle + 5 * Math.PI / 6); + me.lasers(me.vertices[3], me.angle + 7 * Math.PI / 6); + me.lasers(me.vertices[4], me.angle + 9 * Math.PI / 6); + me.lasers(me.vertices[5], me.angle + 11 * Math.PI / 6); ctx.strokeStyle = "#50f"; - ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); - ctx.lineWidth = 1.5; - ctx.beginPath(); - if (this.fadeCycle < 120) { //damage scales up over 2 seconds to give player time to move as it fades in - const scale = this.fadeCycle / 120 - const dmg = this.fadeCycle < 60 ? 0 : 0.13 * simulation.dmgScale * scale - me.lasers(me.vertices[0], me.angle + Math.PI / 6, dmg); - me.lasers(me.vertices[1], me.angle + 3 * Math.PI / 6, dmg); - me.lasers(me.vertices[2], me.angle + 5 * Math.PI / 6, dmg); - me.lasers(me.vertices[3], me.angle + 7 * Math.PI / 6, dmg); - me.lasers(me.vertices[4], me.angle + 9 * Math.PI / 6, dmg); - me.lasers(me.vertices[5], me.angle + 11 * Math.PI / 6, dmg); - ctx.strokeStyle = `rgba(85, 0, 255,${scale})`; - ctx.stroke(); - ctx.strokeStyle = `rgba(80, 0, 255,${0.07*scale})` - } else if (this.fadeCycle > 0) { - me.lasers(me.vertices[0], me.angle + Math.PI / 6); - me.lasers(me.vertices[1], me.angle + 3 * Math.PI / 6); - me.lasers(me.vertices[2], me.angle + 5 * Math.PI / 6); - me.lasers(me.vertices[3], me.angle + 7 * Math.PI / 6); - me.lasers(me.vertices[4], me.angle + 9 * Math.PI / 6); - me.lasers(me.vertices[5], me.angle + 11 * Math.PI / 6); - ctx.strokeStyle = "#50f"; - ctx.stroke(); - ctx.strokeStyle = "rgba(80,0,255,0.07)"; - } - ctx.setLineDash([]); - ctx.lineWidth = 20; ctx.stroke(); + ctx.strokeStyle = "rgba(80,0,255,0.07)"; } - }, - enter() { this.fadeCycle = 0 }, - exit() {}, + ctx.setLineDash([]); + ctx.lineWidth = 20; + ctx.stroke(); + } }, - { - name: "black hole", - eventHorizon: 0, - eventHorizonRadius: 1900, - eventHorizonCycle: 0, - do() { - this.eventHorizonCycle++ - this.eventHorizon = Math.max(0, this.eventHorizonRadius * Math.sin(this.eventHorizonCycle * 0.007)) //eventHorizon waves in and out - //draw darkness - ctx.beginPath(); - ctx.arc(me.position.x, me.position.y, this.eventHorizon * 0.2, 0, 2 * Math.PI); - ctx.fillStyle = "rgba(0,20,40,0.3)"; - ctx.fill(); - ctx.beginPath(); - ctx.arc(me.position.x, me.position.y, this.eventHorizon * 0.4, 0, 2 * Math.PI); - ctx.fillStyle = "rgba(0,20,40,0.25)"; - ctx.fill(); - ctx.beginPath(); - ctx.arc(me.position.x, me.position.y, this.eventHorizon * 0.6, 0, 2 * Math.PI); - ctx.fillStyle = "rgba(0,20,40,0.2)"; - ctx.fill(); - ctx.beginPath(); - ctx.arc(me.position.x, me.position.y, this.eventHorizon * 0.8, 0, 2 * Math.PI); - ctx.fillStyle = "rgba(0,20,40,0.15)"; - ctx.fill(); - ctx.beginPath(); - ctx.arc(me.position.x, me.position.y, this.eventHorizon, 0, 2 * Math.PI); - ctx.fillStyle = "rgba(0,0,0,0.1)"; - ctx.fill(); - //when player is inside event horizon - if (Vector.magnitude(Vector.sub(me.position, player.position)) < this.eventHorizon) { - if (m.immuneCycle < m.cycle) { - if (m.energy > 0) m.energy -= 0.018 - if (m.energy < 0.05 && m.immuneCycle < m.cycle) m.damage(0.0003 * simulation.dmgScale); - } - const angle = Math.atan2(player.position.y - me.position.y, player.position.x - me.position.x); - player.force.x -= 0.0017 * Math.cos(angle) * player.mass * (m.onGround ? 1.7 : 1); - player.force.y -= 0.0017 * Math.sin(angle) * player.mass; - //draw line to player - ctx.beginPath(); - ctx.moveTo(me.position.x, me.position.y); - ctx.lineTo(m.pos.x, m.pos.y); - ctx.lineWidth = Math.min(60, me.radius * 2); - ctx.strokeStyle = "rgba(0,0,0,0.5)"; - ctx.stroke(); - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); - ctx.fillStyle = "rgba(0,0,0,0.3)"; - ctx.fill(); + enter() { this.fadeCycle = 0 }, + exit() { }, + }, + { + name: "black hole", + eventHorizon: 0, + eventHorizonRadius: 1900, + eventHorizonCycle: 0, + do() { + this.eventHorizonCycle++ + this.eventHorizon = Math.max(0, this.eventHorizonRadius * Math.sin(this.eventHorizonCycle * 0.007)) //eventHorizon waves in and out + //draw darkness + ctx.beginPath(); + ctx.arc(me.position.x, me.position.y, this.eventHorizon * 0.2, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,20,40,0.3)"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(me.position.x, me.position.y, this.eventHorizon * 0.4, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,20,40,0.25)"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(me.position.x, me.position.y, this.eventHorizon * 0.6, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,20,40,0.2)"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(me.position.x, me.position.y, this.eventHorizon * 0.8, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,20,40,0.15)"; + ctx.fill(); + ctx.beginPath(); + ctx.arc(me.position.x, me.position.y, this.eventHorizon, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,0,0,0.1)"; + ctx.fill(); + //when player is inside event horizon + if (Vector.magnitude(Vector.sub(me.position, player.position)) < this.eventHorizon) { + if (m.immuneCycle < m.cycle) { + if (m.energy > 0) m.energy -= 0.018 + if (m.energy < 0.05 && m.immuneCycle < m.cycle) m.damage(0.0003 * simulation.dmgScale); } - me.curl(this.eventHorizon); - }, - enter() { this.eventHorizonCycle = 0 }, - exit() {}, + const angle = Math.atan2(player.position.y - me.position.y, player.position.x - me.position.x); + player.force.x -= 0.0017 * Math.cos(angle) * player.mass * (m.onGround ? 1.7 : 1); + player.force.y -= 0.0017 * Math.sin(angle) * player.mass; + //draw line to player + ctx.beginPath(); + ctx.moveTo(me.position.x, me.position.y); + ctx.lineTo(m.pos.x, m.pos.y); + ctx.lineWidth = Math.min(60, me.radius * 2); + ctx.strokeStyle = "rgba(0,0,0,0.5)"; + ctx.stroke(); + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(0,0,0,0.3)"; + ctx.fill(); + } + me.curl(this.eventHorizon); }, - { - name: "oscillation", - waveCycle: 0, - whereX: simulation.isHorizontalFlipped ? -3000 : 3000, - do() { - this.waveCycle += !me.isStunned + !me.isSlowed - // if (!me.isShielded && (!(this.waveCycle % 1800) || !(this.waveCycle % 1801))) spawn.shield(me, me.position.x, me.position.y, 1); - me.constraint.pointA = { - x: this.whereX + 600 * Math.sin(this.waveCycle * 0.005), - y: me.constraint.pointA.y - } - }, - enter() { - spawn.shield(me, me.position.x, me.position.y, 1); - }, - exit() { this.waveCycle = 0 }, + enter() { this.eventHorizonCycle = 0 }, + exit() { }, + }, + { + name: "oscillation", + waveCycle: 0, + whereX: simulation.isHorizontalFlipped ? -3000 : 3000, + do() { + this.waveCycle += !me.isStunned + !me.isSlowed + // if (!me.isShielded && (!(this.waveCycle % 1800) || !(this.waveCycle % 1801))) spawn.shield(me, me.position.x, me.position.y, 1); + me.constraint.pointA = { + x: this.whereX + 600 * Math.sin(this.waveCycle * 0.005), + y: me.constraint.pointA.y + } }, + enter() { + spawn.shield(me, me.position.x, me.position.y, 1); + }, + exit() { this.waveCycle = 0 }, + }, // { // name: "__", // do() {}, @@ -700,7 +700,7 @@ const spawn = { // }, ] shuffle(me.mode); //THIS SHOULDN'T BE COMMENTED OUT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - me.do = function() { + me.do = function () { this.fill = `hsl(${360 * Math.sin(this.cycle * 0.011)},${80 + 20 * Math.sin(this.cycle * 0.004)}%,${60 + 20 * Math.sin(this.cycle * 0.009)}%)` if (this.health < 1) { this.cycle++; @@ -716,13 +716,13 @@ const spawn = { // this.mode[7].do() }; me.spawnRate = 5800 - 30 * simulation.difficultyMode * simulation.difficultyMode - me.spawnBoss = function() { //if the fight lasts too long start spawning bosses + me.spawnBoss = function () { //if the fight lasts too long start spawning bosses if (!(me.cycle % this.spawnRate) && this.health < 1) { this.spawnRate = Math.max(300, this.spawnRate - 10 * simulation.difficultyMode * simulation.difficultyMode) //reduce the timer each time a boss spawns spawn.randomLevelBoss(3000 * (simulation.isHorizontalFlipped ? -1 : 1) + 2000 * (Math.random() - 0.5), -1100 + 200 * (Math.random() - 0.5)) } } - me.pushAway = function(magX = 0.13, magY = 0.05) { + me.pushAway = function (magX = 0.13, magY = 0.05) { for (let i = 0, len = body.length; i < len; ++i) { //push blocks away horizontally body[i].force.x += magX * body[i].mass * (body[i].position.x > this.position.x ? 1 : -1) body[i].force.y -= magY * body[i].mass @@ -738,16 +738,16 @@ const spawn = { player.force.x += magX * player.mass * (player.position.x > this.position.x ? 1 : -1) player.force.y -= magY * player.mass } - me.boulder = function(x, y) { + me.boulder = function (x, y) { mobs.spawn(x, y, 6, Math.floor(50 + 50 * Math.random()), this.fill); let boss = this let me = mob[mob.length - 1]; me.stroke = "transparent"; - me.onHit = function() { + me.onHit = function () { this.timeLeft = 0 }; me.explodeRange = 500 - me.onDeath = function() { //explode + me.onDeath = function () { //explode simulation.drawList.push({ //draw explosion x: this.position.x, y: this.position.y, @@ -797,14 +797,14 @@ const spawn = { me.spin = me.inertia * 0.000005 * (1 + Math.random()) * (m.pos.x > me.position.x ? 1 : -1) Matter.Body.setAngularVelocity(me, 0.1 * (1 + 0.3 * Math.random()) * (m.pos.x > me.position.x ? 1 : -1)); Matter.Body.setVelocity(me, { x: 0, y: 10 }); - me.do = function() { + me.do = function () { this.fill = boss.fill this.torque += this.spin; this.gravity(); this.timeLimit(); }; } - me.orbitalNoVelocity = function(who, radius, phase, speed) { //orbitals that don't include their host velocity //specifically for finalBoss + me.orbitalNoVelocity = function (who, radius, phase, speed) { //orbitals that don't include their host velocity //specifically for finalBoss let boss = this mobs.spawn(who.position.x, who.position.y, 6, 20, "rgb(255,0,150)"); let me = mob[mob.length - 1]; @@ -818,7 +818,7 @@ const spawn = { me.isOrbital = true; me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body - me.do = function() { + me.do = function () { this.fill = boss.fill const time = simulation.cycle * speed + phase const orbit = { x: Math.cos(time), y: Math.sin(time) } @@ -838,8 +838,8 @@ const spawn = { } }; } - me.lasers = function(where, angle, dmg = 0.1 * simulation.dmgScale) { - const vertexCollision = function(v1, v1End, domain) { + me.lasers = function (where, angle, dmg = 0.1 * simulation.dmgScale) { + const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; @@ -909,7 +909,7 @@ const spawn = { ctx.moveTo(where.x, where.y); ctx.lineTo(best.x, best.y); } - me.onDeath = function() { + me.onDeath = function () { if (!this.hasRunDeathScript) { this.hasRunDeathScript = true //make a block body to replace this one @@ -923,7 +923,7 @@ const spawn = { body[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; body[len].classType = "body"; Composite.add(engine.world, body[len]); //add to world - const expand = function(that, massLimit) { + const expand = function (that, massLimit) { const scale = 1.05; Matter.Body.scale(that, scale, scale); if (that.mass < massLimit) setTimeout(expand, 20, that, massLimit); @@ -946,7 +946,7 @@ const spawn = { //add lore level as next level if player took lore tech earlier in the game if (lore.techCount > (lore.techGoal - 1) && !simulation.isCheating) { simulation.makeTextLog(`undefined = ${lore.techCount}/${lore.techGoal}`, 360); - setTimeout(function() { + setTimeout(function () { simulation.makeTextLog(`level.levels.push("null")`, 720); unlockExit() level.levels.push("null") @@ -981,7 +981,7 @@ const spawn = { document.getElementById("text-log").style.display = "none" document.getElementById("fade-out").style.opacity = 1; //slowly fades out // build.shareURL(false) - setTimeout(function() { + setTimeout(function () { if (!simulation.onTitlePage) { m.alive = false simulation.paused = true; @@ -998,7 +998,7 @@ const spawn = { } if (simulation.testing) { unlockExit() - setTimeout(function() { + setTimeout(function () { simulation.makeTextLog(`level.levels.length = Infinite`); }, 1500); } else { @@ -1571,7 +1571,7 @@ const spawn = { // for (let i = 0; i < amount; i++) b.spore(this.position) // } // } - me.do = function() { + me.do = function () { this.zombieHealthBar(); this.lookForMobTargets(); this.attack(); @@ -1579,7 +1579,7 @@ const spawn = { }; me.mobSearchIndex = 0; me.target = null - me.lookForMobTargets = function() { + me.lookForMobTargets = function () { if (this.target === null && mob.length > 1 && !(simulation.cycle % this.seePlayerFreq)) { //find mob targets let closeDist = Infinity; for (let i = 0, len = mob.length; i < len; ++i) { @@ -1607,7 +1607,7 @@ const spawn = { this.target = null //chance to forget target } } - me.zombieHealthBar = function() { + me.zombieHealthBar = function () { this.health -= 0.0004 //decay if ((this.health < 0.01 || isNaN(this.health)) && this.alive) this.death(); const h = this.radius * 0.3; @@ -1620,7 +1620,7 @@ const spawn = { ctx.fillRect(x, y, w * this.health, h); } me.hitCD = 0 - me.attack = function() { //hit non zombie mobs + me.attack = function () { //hit non zombie mobs if (this.hitCD < simulation.cycle) { if (this.target) { this.force = Vector.mult(Vector.normalise(Vector.sub(this.target.position, this.position)), this.accelMag * this.mass) @@ -1676,7 +1676,7 @@ const spawn = { // me.memory = 120; me.seeAtDistance2 = 2000000 //1400 vision range Matter.Body.setDensity(me, 0.0005) // normal density is 0.001 // this reduces life by half and decreases knockback - me.do = function() { + me.do = function () { this.seePlayerByLookingAt(); this.attraction(); this.repulsion(); @@ -1701,7 +1701,7 @@ const spawn = { me.isGrouper = true; me.seeAtDistance2 = 600 * 600 me.seePlayerFreq = Math.floor(50 + 50 * Math.random()) - me.do = function() { + me.do = function () { this.gravity(); this.checkStatus(); this.seePlayerCheck(); @@ -1741,7 +1741,7 @@ const spawn = { me.damageReduction = 0.04 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); //extra reduction for a boss, because normal density me.frictionAir = 0.01; me.accelMag = 0.0002; - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y); for (const who of mob) { if (who.isNecroMob) { //blockMobs leave their body, and die @@ -1751,7 +1751,7 @@ const spawn = { } } me.target = player; // the target to lock on. Usually a block, but will be the player under certain conditions - me.do = function() { + me.do = function () { this.checkStatus(); this.seePlayerCheck(); if (this.target) { //(this.target === player && this.seePlayer.yes) || this.target !== player @@ -1861,7 +1861,7 @@ const spawn = { me.isDropPowerUp = false; // me.showHealthBar = false; me.cycle = 0 - me.do = function() { //grow phase only occurs for growCycles + me.do = function () { //grow phase only occurs for growCycles this.checkStatus(); this.seePlayerCheck(); this.cycle++ @@ -1874,7 +1874,7 @@ const spawn = { this.radius *= scale; } } - me.normalDo = function() { + me.normalDo = function () { this.gravity(); this.checkStatus(); this.seePlayerCheck(); @@ -1907,20 +1907,20 @@ const spawn = { me.damageReduction = 0.17 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); //me.damageReductionGoal const k = 642 //k=r^2/m - me.split = function() { + me.split = function () { Matter.Body.scale(this, 0.45, 0.45); this.radius = Math.sqrt(this.mass * k / Math.PI) spawn.cellBoss(this.position.x, this.position.y, this.radius, this.cellID); mob[mob.length - 1].health = this.health } - me.onHit = function() { //run this function on hitting player + me.onHit = function () { //run this function on hitting player this.health = 1; this.split(); }; - me.onDamage = function(dmg) { + me.onDamage = function (dmg) { if (Math.random() < 0.34 * dmg * Math.sqrt(this.mass) && this.health > dmg) this.split(); } - me.do = function() { + me.do = function () { this.seePlayerByDistOrLOS(); this.checkStatus(); this.attraction(); @@ -1946,7 +1946,7 @@ const spawn = { } } }; - me.onDeath = function() { + me.onDeath = function () { this.isCell = false; let count = 0 //count other cells by id // console.log(this.cellID) @@ -1985,11 +1985,11 @@ const spawn = { Matter.Body.setAngularVelocity(me, 0.12 * (Math.random() - 0.5)) // spawn.shield(me, x, y, 1); - me.onHit = function() { //run this function on hitting player + me.onHit = function () { //run this function on hitting player this.explode(); }; me.damageReduction = 0.14 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); - me.doAwake = function() { + me.doAwake = function () { this.alwaysSeePlayer(); this.checkStatus(); this.attraction(); @@ -2010,7 +2010,7 @@ const spawn = { } } } - me.do = function() { + me.do = function () { this.checkStatus(); if (this.seePlayer.recall) { this.do = this.doAwake @@ -2020,7 +2020,7 @@ const spawn = { } } }; - me.onDeath = function() { + me.onDeath = function () { this.isSpawnBoss = false; let count = 0 //count other cells by id // console.log(this.spawnID) @@ -2073,7 +2073,7 @@ const spawn = { me.buffCount = 0 me.accelMag = 0.00005 //* simulation.accelScale; - me.setBuffed = function() { + me.setBuffed = function () { this.buffCount++ this.accelMag += 0.000024 //* Math.sqrt(simulation.accelScale) this.fill = `hsl(144, ${5 + 10 * this.buffCount}%, 50%)` @@ -2086,7 +2086,7 @@ const spawn = { // this.damageReduction = 0 // this.invulnerabilityCountDown = simulation.difficulty } - me.onDeath = function() { + me.onDeath = function () { this.isBuffBoss = false; let count = 0 //count other cells by id for (let i = 0, len = mob.length; i < len; i++) { @@ -2107,7 +2107,7 @@ const spawn = { //required setup for invulnerable // me.isInvulnerable = false me.invulnerabilityCountDown = 0 - me.do = function() { + me.do = function () { // if (this.isInvulnerable) { // if (this.invulnerabilityCountDown > 0) { // this.invulnerabilityCountDown-- @@ -2169,7 +2169,7 @@ const spawn = { me.startingDamageReduction = me.damageReduction me.damageReduction = 0 me.invulnerabilityCountDown = 40 + simulation.difficulty - me.onHit = function() { //run this function on hitting player + me.onHit = function () { //run this function on hitting player if (powerUps.ejectTech()) { powerUps.ejectGraphic("150, 138, 255"); // powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "ammo"); @@ -2178,7 +2178,7 @@ const spawn = { Matter.Body.setDensity(this, this.density * 1.4); //normal is 0.001 } }; - me.onDeath = function() { + me.onDeath = function () { this.leaveBody = false; if (vertices > 3) { this.isDropPowerUp = false; @@ -2190,7 +2190,7 @@ const spawn = { } for (let i = 0; i < powerUp.length; i++) powerUp[i].collisionFilter.mask = cat.map | cat.powerUp }; - me.do = function() { + me.do = function () { if (this.isInvulnerable) { if (this.invulnerabilityCountDown > 0) { this.invulnerabilityCountDown-- @@ -2200,7 +2200,7 @@ const spawn = { for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 13 + 5 * Math.random(); - ctx.strokeStyle = `rgba(255,255,255,${0.5+0.2*Math.random()})`; + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.stroke(); } else { this.isInvulnerable = false @@ -2253,7 +2253,7 @@ const spawn = { // me.damageReduction = 0 // me.invulnerabilityCountDown = 60 + simulation.difficulty * 2 - me.onHit = function() { //run this function on hitting player + me.onHit = function () { //run this function on hitting player if (powerUps.ejectTech()) { powerUps.ejectGraphic("150, 138, 255"); // powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "ammo"); @@ -2262,7 +2262,7 @@ const spawn = { Matter.Body.setDensity(this, this.density * 1.4); //normal is 0.001 } }; - me.onDeath = function() { + me.onDeath = function () { this.leaveBody = false; if (vertices > 3) { this.isDropPowerUp = false; @@ -2314,7 +2314,7 @@ const spawn = { // for (let i = 0; i < this.powerUpList.length; i++) {} // } // me.constrainPowerUps() - me.do = function() { + me.do = function () { this.stroke = `hsl(0,0%,${80 + 25 * Math.sin(simulation.cycle * 0.01)}%)` // if (this.isInvulnerable) { // if (this.invulnerabilityCountDown > 0) { @@ -2379,7 +2379,7 @@ const spawn = { // me.onDeath = function () { //helps collisions functions work better after vertex have been changed // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) // } - me.do = function() { + me.do = function () { this.seePlayerByLookingAt(); this.checkStatus(); this.attraction(); @@ -2427,14 +2427,14 @@ const spawn = { Composite.add(engine.world, cons[cons.length - 1]); cons[len2].length = 100 + 1.5 * radius; me.cons2 = cons[len2]; - me.do = function() { + me.do = function () { this.gravity(); this.searchSpring(); this.checkStatus(); this.springAttack(); }; - me.onDeath = function() { + me.onDeath = function () { this.removeCons(); }; spawn.shield(me, x, y); @@ -2453,7 +2453,7 @@ const spawn = { me.randomHopCD = simulation.cycle + me.randomHopFrequency; Matter.Body.rotate(me, Math.random() * Math.PI); spawn.shield(me, x, y); - me.do = function() { + me.do = function () { this.gravity(); this.seePlayerCheck(); this.checkStatus(); @@ -2501,10 +2501,10 @@ const spawn = { // Matter.Body.rotate(me, Math.random() * Math.PI); me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; - me.onHit = function() { + me.onHit = function () { this.explode(this.mass); }; - me.do = function() { + me.do = function () { this.gravity(); this.checkStatus(); if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { @@ -2533,11 +2533,11 @@ const spawn = { me.delay = 120 + 40 * simulation.CDScale; Matter.Body.rotate(me, Math.random() * Math.PI); spawn.shield(me, x, y, 1); - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) // for (let i = 0, len = 3 + 0.1 * simulation.difficulty; i < len; ++i) spawn.hopBullet(this.position.x + 100 * (Math.random() - 0.5), this.position.y + 100 * (Math.random() - 0.5)) }; - me.do = function() { + me.do = function () { this.gravity(); this.seePlayerCheck(); this.checkStatus(); @@ -2653,7 +2653,7 @@ const spawn = { me.lookTorque = 0.0000014; me.restitution = 0; spawn.shield(me, x, y); - me.look = function() { + me.look = function () { this.seePlayerByLookingAt(); this.checkStatus(); if (this.seePlayer.recall && this.cd < simulation.cycle) { @@ -2663,7 +2663,7 @@ const spawn = { } } me.do = me.look - me.spin = function() { + me.spin = function () { this.checkStatus(); this.torque += 0.000035 * this.inertia; //draw attack vector @@ -2697,7 +2697,7 @@ const spawn = { me.collisionFilter.mask = cat.player | cat.bullet //| cat.body me.memory = Infinity; Matter.Body.setDensity(me, 0.015); //extra dense //normal is 0.001 //makes effective life much larger - me.do = function() { + me.do = function () { //keep it slow, to stop issues from explosion knock backs if (this.speed > 5) { Matter.Body.setVelocity(this, { @@ -2828,7 +2828,7 @@ const spawn = { // me.frictionAir = 0.005; me.memory = 1600; Matter.Body.setDensity(me, 0.06); //extra dense //normal is 0.001 //makes effective life much larger - me.onDeath = function() { + me.onDeath = function () { //applying forces to player doesn't seem to work inside this method, not sure why powerUps.spawnBossPowerUp(this.position.x, this.position.y) if (simulation.difficulty > 5) { @@ -2850,7 +2850,7 @@ const spawn = { } }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { //keep it slow, to stop issues from explosion knock backs if (this.speed > 1) { Matter.Body.setVelocity(this, { @@ -2984,7 +2984,7 @@ const spawn = { Composite.add(engine.world, cons[cons.length - 1]); cons[len2].length = 100 + 1.5 * radius; me.cons2 = cons[len2]; - me.do = function() { + me.do = function () { // this.armor(); this.gravity(); this.searchSpring(); @@ -2992,7 +2992,7 @@ const spawn = { this.springAttack(); }; - me.onDeath = function() { + me.onDeath = function () { this.removeCons(); powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; @@ -3070,7 +3070,7 @@ const spawn = { me.startingDamageReduction = me.damageReduction me.isInvulnerable = false me.invulnerabilityCountDown = 0 - me.do = function() { + me.do = function () { this.checkStatus(); this.gravity(); //draw the two dots on the end of the springs @@ -3097,7 +3097,7 @@ const spawn = { } } ctx.lineWidth = 13 + 5 * Math.random(); - ctx.strokeStyle = `rgba(255,255,255,${0.5+0.2*Math.random()})`; + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.stroke(); if (this.invulnerabilityCountDown < 0) { this.invulnerabilityCountDown = 110 @@ -3140,7 +3140,7 @@ const spawn = { } else { this.torque = this.lookTorque * this.inertia; //spring to random place on map - const vertexCollision = function(v1, v1End, domain) { + const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; @@ -3205,7 +3205,7 @@ const spawn = { } } }; - me.onDeath = function() { + me.onDeath = function () { this.removeCons(); if (isSpawnBossPowerUp) powerUps.spawnBossPowerUp(this.position.x, this.position.y) for (let i = 0; i < this.babyList.length; i++) { @@ -3232,7 +3232,7 @@ const spawn = { babyMob.collisionFilter.mask = cat.bullet | cat.player //can't touch other mobs //cat.map | cat.body | babyMob.delay = 60 + 55 * simulation.CDScale + Math.floor(Math.random() * 20); babyMob.strikeRange = 400 - babyMob.onHit = function() { + babyMob.onHit = function () { this.cd = simulation.cycle + this.delay; //dislodge ammo if (b.inventory.length) { @@ -3376,7 +3376,7 @@ const spawn = { me.frictionStatic = 0; me.friction = 0; spawn.shield(me, x, y); - me.do = function() { + me.do = function () { this.seePlayerByLookingAt(); this.checkStatus(); this.attraction(); @@ -3403,7 +3403,7 @@ const spawn = { me.stroke = "transparent"; //used for drawGhost me.collisionFilter.mask = cat.bullet | cat.body me.memory = Infinity - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) requestAnimationFrame(() => { requestAnimationFrame(() => { @@ -3413,7 +3413,7 @@ const spawn = { }) }; me.warpIntensity = 0 - me.awake = function() { + me.awake = function () { this.checkStatus(); //health bar needs to be here because the position is being set const h = this.radius * 0.3; @@ -3487,7 +3487,7 @@ const spawn = { Matter.Body.setPosition(this, { x: history.position.x, y: history.position.y - history.yOff + 24.2859 }) //bullets move with player } } - me.do = function() { + me.do = function () { if (this.seePlayer.recall || (!(simulation.cycle % this.seePlayerFreq) && this.distanceToPlayer2() < this.seeAtDistance2 && !m.isCloak)) { setTimeout(() => { this.do = this.awake @@ -3510,11 +3510,11 @@ const spawn = { me.accelMag = 0.00009 * simulation.accelScale; me.frictionStatic = 0; me.friction = 0; - me.onDamage = function() { + me.onDamage = function () { this.laserPos = this.position; }; spawn.shield(me, x, y); - me.do = function() { + me.do = function () { this.seePlayerByLookingAt(); this.checkStatus(); this.attraction(); @@ -3584,7 +3584,7 @@ const spawn = { // me.onDeath = function() {}; me.flapRate = 0.3 + Math.floor(3 * Math.random()) / 10 + 100 * me.accelMag me.flapRadius = 75 + radius * 3 - me.do = function() { + me.do = function () { this.seePlayerByHistory() this.checkStatus(); if (this.seePlayer.recall) { @@ -3616,7 +3616,7 @@ const spawn = { } const flapArc = 0.7 //don't go past 1.57 for normal flaps - ctx.fillStyle = `hsla(${160+40*Math.random()}, 100%, ${25 + 25*Math.random()*Math.random()}%, 0.2)`; //"rgba(0,235,255,0.3)"; // ctx.fillStyle = `hsla(44, 79%, 31%,0.4)`; //"rgba(0,235,255,0.3)"; + ctx.fillStyle = `hsla(${160 + 40 * Math.random()}, 100%, ${25 + 25 * Math.random() * Math.random()}%, 0.2)`; //"rgba(0,235,255,0.3)"; // ctx.fillStyle = `hsla(44, 79%, 31%,0.4)`; //"rgba(0,235,255,0.3)"; this.wing(this.angle + Math.PI / 2 + flapArc * Math.sin(simulation.cycle * this.flapRate), this.flapRadius) this.wing(this.angle - Math.PI / 2 - flapArc * Math.sin(simulation.cycle * this.flapRate), this.flapRadius) } @@ -3676,7 +3676,7 @@ const spawn = { // me.isAlreadyHadBabies = true // } - me.pushAway = function(magX = 0.13, magY = 0.05) { + me.pushAway = function (magX = 0.13, magY = 0.05) { for (let i = 0, len = body.length; i < len; ++i) { //push blocks away horizontally if (Vector.magnitudeSquared(Vector.sub(body[i].position, this.position)) < 4000000) { //2000 body[i].force.x += magX * body[i].mass * (body[i].position.x > this.position.x ? 1 : -1) @@ -3701,7 +3701,7 @@ const spawn = { } } - me.babies = function(len) { + me.babies = function (len) { const delay = Math.max(3, Math.floor(15 - len / 2)) let i = 0 let spawnFlutters = () => { @@ -3730,11 +3730,11 @@ const spawn = { } // me.babies(0.05 * simulation.difficulty + 1) - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) me.babies(0.05 * simulation.difficulty + 1) }; - me.onDamage = function() { + me.onDamage = function () { if (this.health < this.nextHealthThreshold && this.alive) { this.health = this.nextHealthThreshold - 0.01 this.nextHealthThreshold = Math.floor(this.health * 4) / 4 @@ -3748,7 +3748,7 @@ const spawn = { this.accelMag *= 1.4 } }; - me.do = function() { + me.do = function () { this.seePlayerByHistory(50) this.checkStatus(); if (this.isInvulnerable) { @@ -3768,7 +3768,7 @@ const spawn = { for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 13 + 5 * Math.random(); - ctx.strokeStyle = `rgba(255,255,255,${0.5+0.2*Math.random()})`; + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.stroke(); } else if (this.seePlayer.recall) { // const force = Vector.mult(Vector.normalise(Vector.sub(this.seePlayer.position, this.position)), this.accelMag * this.mass) @@ -3804,7 +3804,7 @@ const spawn = { } const flapArc = 0.7 //don't go past 1.57 for normal flaps this.wingSize = 0.97 * this.wingSize + 0.03 * this.wingGoal - ctx.fillStyle = this.fill = `hsla(${160+40*Math.random()}, 100%, ${25 + 25*Math.random()*Math.random()}%, 0.9)`; //"rgba(0,235,255,0.3)"; // ctx.fillStyle = `hsla(44, 79%, 31%,0.4)`; //"rgba(0,235,255,0.3)"; + ctx.fillStyle = this.fill = `hsla(${160 + 40 * Math.random()}, 100%, ${25 + 25 * Math.random() * Math.random()}%, 0.9)`; //"rgba(0,235,255,0.3)"; // ctx.fillStyle = `hsla(44, 79%, 31%,0.4)`; //"rgba(0,235,255,0.3)"; this.wing(this.angle + Math.PI / 2 + flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingSize, 0.5, 0.0012) this.wing(this.angle - Math.PI / 2 - flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingSize, 0.5, 0.0012) } else { @@ -3836,11 +3836,11 @@ const spawn = { for (let i = 0, len = 2 + 0.3 * Math.sqrt(simulation.difficulty); i < len; i++) spawn.spawnOrbitals(me, radius + 40 + 10 * i, 1); // me.onHit = function() { }; // spawn.shield(me, x, y, 1); //not working, not sure why - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; me.laserInterval = 100 - me.do = function() { + me.do = function () { // this.armor(); this.seePlayerByLookingAt(); this.checkStatus(); @@ -3860,7 +3860,7 @@ const spawn = { this.torque -= 0.000004 * this.inertia; } if (simulation.cycle % this.laserInterval > this.laserInterval / 2) { - const vertexCollision = function(v1, v1End, domain) { + const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; @@ -3974,13 +3974,13 @@ const spawn = { Matter.Body.setDensity(me, 0.01); //extra dense //normal is 0.001 //makes effective life much larger spawn.shield(me, x, y, 1); spawn.spawnOrbitals(me, radius + 200 + 300 * Math.random()) - me.onHit = function() {}; - me.onDeath = function() { + me.onHit = function () { }; + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; me.targetingCount = 0; me.targetingTime = 60 - Math.min(58, 3 * simulation.difficulty) - me.do = function() { + me.do = function () { // //wings // const wing = (simulation.cycle % 9) > 4 ? this.vertices[0] : this.vertices[2] //Vector.add(this.position, { x: 100, y: 0 }) @@ -4018,7 +4018,7 @@ const spawn = { } else if (c < -threshold) { this.torque -= 0.000004 * this.inertia; } - const vertexCollision = function(v1, v1End, domain) { + const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; @@ -4145,10 +4145,10 @@ const spawn = { me.delay = 55 + 35 * simulation.CDScale; me.nextBlinkCycle = me.delay; spawn.shield(me, x, y, 1); - me.onDamage = function() { + me.onDamage = function () { // this.cd = simulation.cycle + this.delay; }; - me.onDeath = function() { + me.onDeath = function () { const offAngle = Math.PI * Math.random() for (let i = 0, len = 3; i < len; i++) { spawn.grenade(this.position.x, this.position.y, this.grenadeDelay); @@ -4162,7 +4162,7 @@ const spawn = { } powerUps.spawnBossPowerUp(this.position.x, this.position.y) } - me.do = function() { + me.do = function () { this.seePlayerByHistory(40) if (this.nextBlinkCycle < simulation.cycle && this.seePlayer.yes) { //teleport towards the player this.nextBlinkCycle = simulation.cycle + this.delay; @@ -4219,15 +4219,15 @@ const spawn = { spawn.shield(me, x, y, 1); spawn.spawnOrbitals(me, radius + 200 + 300 * Math.random(), 1) - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; - me.onHit = function() {}; - me.do = function() { + me.onHit = function () { }; + me.do = function () { if (player.speed > 5) this.do = this.fire //don't attack until player moves } me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.fire = function() { + me.fire = function () { // this.armor(); this.checkStatus(); if (!m.isCloak && !this.isStunned) { @@ -4327,8 +4327,8 @@ const spawn = { me.pulseRadius = Math.min(400, 170 + simulation.difficulty * 3) me.fireDelay = Math.max(75, 140 - simulation.difficulty * 0.5) me.isFiring = false - me.onHit = function() {}; - me.canSeeTarget = function() { + me.onHit = function () { }; + me.canSeeTarget = function () { const angle = this.angle + Math.PI / 2; const dot = Vector.dot({ x: Math.cos(angle), @@ -4347,7 +4347,7 @@ const spawn = { return true } } - me.do = function() { + me.do = function () { this.seePlayerByLookingAt(); this.checkStatus(); if (this.seePlayer.recall) { @@ -4443,11 +4443,11 @@ const spawn = { Matter.Body.rotate(me, Math.random() * Math.PI * 2); me.accelMag = 0.0001 * simulation.accelScale; me.laserInterval = 100 - me.onHit = function() { + me.onHit = function () { //run this function on hitting player this.explode(); }; - me.do = function() { + me.do = function () { this.torque = this.lookTorque * this.inertia * 0.5; this.seePlayerByLookingAt(); this.checkStatus(); @@ -4457,7 +4457,7 @@ const spawn = { if (!(simulation.cycle % this.seePlayerFreq)) this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); if (simulation.cycle % this.laserInterval > this.laserInterval / 2) { - const vertexCollision = function(v1, v1End, domain) { + const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; @@ -4572,11 +4572,11 @@ const spawn = { me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.isBoss = true; // spawn.shield(me, x, y, 1); //not working, not sure why - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; me.rotateVelocity = Math.min(0.0045, 0.0015 * simulation.accelScale * simulation.accelScale) * (level.levelsCleared > 8 ? 1 : -1) * (simulation.isHorizontalFlipped ? -1 : 1) - me.do = function() { + me.do = function () { this.fill = '#' + Math.random().toString(16).substr(-6); //flash colors this.checkStatus(); @@ -4617,8 +4617,8 @@ const spawn = { // Matter.Body.setPosition(this, this.startingPosition); }; - me.lasers = function(where, angle) { - const vertexCollision = function(v1, v1End, domain) { + me.lasers = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; @@ -4709,7 +4709,7 @@ const spawn = { // me.onDamage = function () {}; // me.onHit = function() { //run this function on hitting player // }; - me.onDeath = function() { + me.onDeath = function () { if (this.spikeLength > 4) { this.spikeLength = 4 const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.spikeVertex], this.position)), this.radius * this.spikeLength) @@ -4718,7 +4718,7 @@ const spawn = { // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) } }; - me.do = function() { + me.do = function () { this.seePlayerByLookingAt(); this.checkStatus(); this.attraction(); @@ -4786,10 +4786,10 @@ const spawn = { me.strikeRange = 300 Matter.Body.rotate(me, Math.PI * 0.1); spawn.shield(me, x, y); - me.onDamage = function() { + me.onDamage = function () { this.cd = simulation.cycle + this.delay; }; - me.do = function() { + me.do = function () { this.gravity(); if (!(simulation.cycle % this.seePlayerFreq)) { // this.seePlayerCheck(); from mobs if ( @@ -4846,8 +4846,8 @@ const spawn = { Matter.Body.setDensity(me, 0.005); //extra dense //normal is 0.001 //makes effective life much larger me.damageReduction = 0.12 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.isBoss = true; - me.onDamage = function() {}; - me.onDeath = function() { + me.onDamage = function () { }; + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; @@ -4856,7 +4856,7 @@ const spawn = { me.isInvulnerable = false me.isNextInvulnerability = 0.75 me.invulnerabilityCountDown = 0 - me.invulnerable = function() { + me.invulnerable = function () { if (this.health < this.isNextInvulnerability) { this.isNextInvulnerability = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 this.isInvulnerable = true @@ -4874,7 +4874,7 @@ const spawn = { for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 13 + 5 * Math.random(); - ctx.strokeStyle = `rgba(255,255,255,${0.5+0.2*Math.random()})`; + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.stroke(); } else { this.isInvulnerable = false @@ -4882,7 +4882,7 @@ const spawn = { } } } - me.do = function() { + me.do = function () { this.invulnerable(); this.checkStatus(); this.seePlayerByHistory(60); @@ -4896,8 +4896,8 @@ const spawn = { if (long > 0) this.laserSword(this.vertices[i], bend + this.angle + (i + 0.5) / sides * 2 * Math.PI, Math.abs(long)); } }; - me.laserSword = function(where, angle, length) { - const vertexCollision = function(v1, v1End, domain) { + me.laserSword = function (where, angle, length) { + const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; @@ -4968,10 +4968,10 @@ const spawn = { me.startingDamageReduction = me.damageReduction me.isInvulnerable = false me.nextHealthThreshold = 0.75 - me.onDeath = function() { + me.onDeath = function () { if (isSpawnBossPowerUp) powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; - me.onDamage = function() { + me.onDamage = function () { if (this.health < this.nextHealthThreshold) { this.health = this.nextHealthThreshold - 0.01 this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 @@ -4985,7 +4985,7 @@ const spawn = { }; //draw radial lines from verticies showing future bullet paths? - me.radialLines = function() { + me.radialLines = function () { ctx.beginPath(); for (let i = 0, len = this.vertices.length; i < len; i++) { ctx.moveTo(this.vertices[i].x, this.vertices[i].y) @@ -4998,7 +4998,7 @@ const spawn = { } me.phaseCycle = 0 - me.normalDoStuff = function() { + me.normalDoStuff = function () { this.checkStatus(); me.seePlayer.recall = 1 //maintain speed //faster in the vertical to help avoid repeating patterns @@ -5009,7 +5009,7 @@ const spawn = { if (Math.abs(this.velocity.x) < 7) Matter.Body.setVelocity(this, { x: this.velocity.x * 1.03, y: this.velocity.y }); } } - me.burstFire = function() { + me.burstFire = function () { this.normalDoStuff(); this.radialLines() //draw invulnerable @@ -5019,7 +5019,7 @@ const spawn = { for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 13 + 5 * Math.random(); - ctx.strokeStyle = `rgba(255,255,255,${0.5+0.2*Math.random()})`; + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.stroke(); if (!(simulation.cycle % this.burstFireFreq)) { @@ -5076,7 +5076,7 @@ const spawn = { Matter.Body.setVelocity(me, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }); me.seePlayer.recall = 1; // spawn.shield(me, x, y, 1); - me.onDamage = function() { + me.onDamage = function () { if (this.health < this.nextHealthThreshold) { this.health = this.nextHealthThreshold - 0.01 this.nextHealthThreshold = Math.floor(this.health * 4) / 4 @@ -5104,14 +5104,14 @@ const spawn = { } }; - me.onDeath = function() { + me.onDeath = function () { if (isSpawnBossPowerUp) powerUps.spawnBossPowerUp(this.position.x, this.position.y) for (let i = 0, len = mob.length; i < len; ++i) { //trigger nearby mines if (mob[i].isMine && Vector.magnitude(Vector.sub(this.position, mob[i].position)) < this.explodeRange) mob[i].isExploding = true } }; // console.log(me.mass) //100 - me.do = function() { + me.do = function () { me.seePlayer.recall = 1 //maintain speed //faster in the vertical to help avoid repeating patterns if (this.speed < 0.01) { @@ -5141,7 +5141,7 @@ const spawn = { for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 13 + 5 * Math.random(); - ctx.strokeStyle = `rgba(255,255,255,${0.5+0.2*Math.random()})`; + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.stroke(); } this.checkStatus(); @@ -5178,7 +5178,7 @@ const spawn = { // this.health = 1 // this.isExploding = true // }; - me.do = function() { + me.do = function () { this.checkStatus(); if (Matter.Query.collides(this, [player]).length > 0) { @@ -5230,7 +5230,7 @@ const spawn = { Matter.Body.setVelocity(me, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }); me.seePlayer.recall = 1; // spawn.shield(me, x, y, 1); - me.onDamage = function() { + me.onDamage = function () { if (this.health < this.nextHealthThreshold) { this.health = this.nextHealthThreshold - 0.01 this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 @@ -5239,12 +5239,12 @@ const spawn = { this.damageReduction = 0 } }; - if (isSpawnBossPowerUp) me.onDeath = function() { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; + if (isSpawnBossPowerUp) me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; me.cycle = 0 me.nextHealthThreshold = 0.75 me.fireCount = 0 // console.log(me.mass) //100 - me.do = function() { + me.do = function () { me.seePlayer.recall = 1 //maintain speed //faster in the vertical to help avoid repeating patterns if (this.speed < 0.01) { @@ -5278,7 +5278,7 @@ const spawn = { for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 13 + 5 * Math.random(); - ctx.strokeStyle = `rgba(255,255,255,${0.5+0.2*Math.random()})`; + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.stroke(); } else if (this.mass < 100) { Matter.Body.scale(this, 1.01, 1.01); //grow back to normal size @@ -5315,7 +5315,7 @@ const spawn = { me.damageReduction = 0.05 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.startingDamageReduction = me.damageReduction me.isInvulnerable = false - me.onDamage = function() { + me.onDamage = function () { if (this.health < this.nextHealthThreshold) { this.health = this.nextHealthThreshold - 0.01 this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 @@ -5325,7 +5325,7 @@ const spawn = { // requestAnimationFrame(() => { simulation.timePlayerSkip(300) }); //wrapping in animation frame prevents errors, probably } }; - me.onDeath = function() { + me.onDeath = function () { if (isSpawnBossPowerUp) powerUps.spawnBossPowerUp(this.position.x, this.position.y) requestAnimationFrame(() => { simulation.timePlayerSkip(60) }); //wrapping in animation frame prevents errors, probably }; @@ -5334,9 +5334,9 @@ const spawn = { me.nextHealthThreshold = 0.75 me.invulnerableCount = 0 // console.log(me.mass) //100 - me.do = function() { + me.do = function () { this.cycle++ - this.fill = `hsl(${this.cycle*0.5}, 100%, 80%)`; + this.fill = `hsl(${this.cycle * 0.5}, 100%, 80%)`; // this.fill = `hsl(${270 + 50*Math.sin(this.cycle*0.02)}, 100%, 60%)`; this.seePlayer.recall = 1 //maintain speed //faster in the vertical to help avoid repeating patterns @@ -5371,7 +5371,7 @@ const spawn = { for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 15 + 6 * Math.random(); - ctx.strokeStyle = `rgba(255,255,255,${0.5+0.2*Math.random()})`; + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.stroke(); } } @@ -5406,10 +5406,10 @@ const spawn = { me.isBadTarget = true; me.isMobBullet = true; me.showHealthBar = false; - me.onHit = function() { + me.onHit = function () { this.explode(this.mass * 12); }; - me.do = function() { + me.do = function () { this.timeLimit(); }; Matter.Body.setVelocity(me, velocity); @@ -5443,11 +5443,11 @@ const spawn = { me.laserAngle = 3 * Math.PI / 5 const seeDistance2 = 200000 spawn.shield(me, x, y); - me.onDamage = function() {}; - me.onDeath = function() { + me.onDamage = function () { }; + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; - me.do = function() { + me.do = function () { this.seePlayerByHistory(40); this.attraction(); this.checkStatus(); @@ -5458,7 +5458,7 @@ const spawn = { // ctx.fillStyle = "#444"; // ctx.fill(); }; - me.swordWaiting = function() { + me.swordWaiting = function () { if ( this.seePlayer.recall && this.cd < simulation.cycle && @@ -5487,7 +5487,7 @@ const spawn = { } } me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing - me.swordGrow = function() { + me.swordGrow = function () { this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); this.swordRadius += this.swordRadiusGrowRate if (this.swordRadius > this.swordRadiusMax) { @@ -5501,10 +5501,10 @@ const spawn = { for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 13 + 5 * Math.random(); - ctx.strokeStyle = `rgba(255,255,255,${0.5+0.2*Math.random()})`; + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.stroke(); } - me.swordSlash = function() { + me.swordSlash = function () { this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); this.torque += this.torqueMagnitude; this.spinCount++ @@ -5523,11 +5523,11 @@ const spawn = { for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 13 + 5 * Math.random(); - ctx.strokeStyle = `rgba(255,255,255,${0.5+0.2*Math.random()})`; + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.stroke(); } - me.laserSword = function(where, angle) { - const vertexCollision = function(v1, v1End, domain) { + me.laserSword = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let v = domain[i].vertices; const len = v.length - 1; @@ -5599,14 +5599,14 @@ const spawn = { me.laserAngle = 3 * Math.PI / 5 const seeDistance2 = 200000 spawn.shield(me, x, y); - me.onDamage = function() {}; - me.do = function() { + me.onDamage = function () { }; + me.do = function () { this.checkStatus(); this.seePlayerByHistory(15); this.attraction(); this.sword() //does various things depending on what stage of the sword swing }; - me.swordWaiting = function() { + me.swordWaiting = function () { if ( this.seePlayer.recall && this.cd < simulation.cycle && @@ -5631,7 +5631,7 @@ const spawn = { } } me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing - me.swordGrow = function() { + me.swordGrow = function () { this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); this.swordRadius += this.swordRadiusGrowRate if (this.swordRadius > this.swordRadiusMax || this.isStunned) { @@ -5639,7 +5639,7 @@ const spawn = { this.spinCount = 0 } } - me.swordSlash = function() { + me.swordSlash = function () { this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); this.torque += this.torqueMagnitude; this.spinCount++ @@ -5650,8 +5650,8 @@ const spawn = { this.cd = simulation.cycle + this.delay; } } - me.laserSword = function(where, angle) { - const vertexCollision = function(v1, v1End, domain) { + me.laserSword = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let v = domain[i].vertices; const len = v.length - 1; @@ -5721,10 +5721,10 @@ const spawn = { me.showHealthBar = false; me.memory = 30; me.vanishesLeft = Math.ceil(1 + simulation.difficultyMode * 0.5) - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; - me.onDamage = function() { + me.onDamage = function () { if (this.vanishesLeft > 0 && this.health < 0.1) { //if health is below 10% teleport to a random spot on player history, heal, and cloak this.vanishesLeft-- @@ -5733,7 +5733,7 @@ const spawn = { x: this.position.x, y: this.position.y, radius: 3000, - color: `rgba(0, 0, 0,${1-0.1*i})`, + color: `rgba(0, 0, 0,${1 - 0.1 * i})`, time: (i + 2) * 4 }); } @@ -5749,7 +5749,7 @@ const spawn = { this.health = 1; } }; - me.cloak = function() { + me.cloak = function () { if (!this.isCloaked) { //stealth this.alpha = 0; this.isCloaked = true; @@ -5758,7 +5758,7 @@ const spawn = { this.damageReduction = 0.04 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) } } - me.deCloak = function() { + me.deCloak = function () { if (this.isCloaked) { this.damageReduction = 0.4 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) this.isCloaked = false; @@ -5766,7 +5766,7 @@ const spawn = { this.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob; //can touch player } } - me.do = function() { + me.do = function () { if (this.damageReduction === 0) { this.damageReduction = 0.04 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) let i = this.status.length //clear bad status effects @@ -5818,7 +5818,7 @@ const spawn = { me.showHealthBar = false; me.memory = 240; me.isVanished = false; - me.onDamage = function() { + me.onDamage = function () { if (!this.isVanished && this.health < 0.1 && !this.isStunned && !this.isSlowed) { //if health is below 10% teleport to a random spot on player history, heal, and cloak this.health = 1; this.isVanished = true @@ -5829,7 +5829,7 @@ const spawn = { this.damageReduction = 0 //immune to harm for the rest of this game cycle } }; - me.cloak = function() { + me.cloak = function () { if (this.isNotCloaked) { //stealth this.alpha = 0; this.isNotCloaked = false; @@ -5837,7 +5837,7 @@ const spawn = { this.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player } } - me.do = function() { + me.do = function () { if (this.damageReduction === 0) { this.damageReduction = 1 //stop being immune to harm immediately let i = this.status.length //clear bad status effects @@ -5895,7 +5895,7 @@ const spawn = { me.collisionFilter.mask = cat.bullet //| cat.body me.showHealthBar = false; me.memory = 480; - me.do = function() { + me.do = function () { //cap max speed if (this.speed > 7) { Matter.Body.setVelocity(this, { @@ -6014,11 +6014,11 @@ const spawn = { radiusOrbitals = radius + 125 + 350 * Math.random() for (let i = 0; i < len; i++) spawn.orbital(me, radiusOrbitals, i / len * 2 * Math.PI, -speed) - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { // this.armor(); this.seePlayerCheckByDistance(); this.checkStatus(); @@ -6050,11 +6050,11 @@ const spawn = { x: 0, y: 0 }; - me.onDeath = function() { //helps collisions functions work better after vertex have been changed + me.onDeath = function () { //helps collisions functions work better after vertex have been changed // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) } // spawn.shield(me, x, y); - me.do = function() { + me.do = function () { this.seePlayerByLookingAt(); this.checkStatus(); this.fire(); @@ -6093,11 +6093,11 @@ const spawn = { setTimeout(() => { for (let i = 0, len = 3 + 0.5 * Math.sqrt(simulation.difficulty); i < len; i++) spawn.spawnOrbitals(me, radius + 40 + 10 * i, 1); }, 100); //have to wait a sec so the tether constraint doesn't attach to an orbital - me.onDeath = function() { + me.onDeath = function () { if (isSpawnBossPowerUp) powerUps.spawnBossPowerUp(this.position.x, this.position.y) // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //helps collisions functions work better after vertex have been changed }; - me.do = function() { + me.do = function () { // this.armor(); this.seePlayerByLookingAt(); this.checkStatus(); @@ -6152,7 +6152,7 @@ const spawn = { mobs.spawn(x, y, sides, radius, "rgb(255,0,0)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; - me.onHit = function() { + me.onHit = function () { this.explode(this.mass * 15); }; Matter.Body.setDensity(me, 0.00004); //normal is 0.001 @@ -6167,7 +6167,7 @@ const spawn = { me.showHealthBar = false; me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; - me.do = function() { + me.do = function () { this.gravity(); this.timeLimit(); }; @@ -6176,10 +6176,10 @@ const spawn = { mobs.spawn(x, y, sides, radius, "rgb(255,0,0)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; - me.onHit = function() { + me.onHit = function () { this.explode(this.mass * 120); }; - me.onDeath = function() { + me.onDeath = function () { spawn.bullet(this.position.x, this.position.y, this.radius / 3, 5); spawn.bullet(this.position.x, this.position.y, this.radius / 3, 5); spawn.bullet(this.position.x, this.position.y, this.radius / 3, 5); @@ -6219,7 +6219,7 @@ const spawn = { me.showHealthBar = false; me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; - me.do = function() { + me.do = function () { this.gravity(); this.timeLimit(); }; @@ -6250,11 +6250,11 @@ const spawn = { x: 0, y: 0 }; - me.onDeath = function() { //helps collisions functions work better after vertex have been changed + me.onDeath = function () { //helps collisions functions work better after vertex have been changed // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) } // spawn.shield(me, x, y); - me.do = function() { + me.do = function () { // this.seePlayerByLookingAt(); this.seePlayerCheck(); this.checkStatus(); @@ -6344,7 +6344,7 @@ const spawn = { mobs.spawn(x, y, sides, radius, "rgb(255,0,155)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; - me.onHit = function() { + me.onHit = function () { this.explode(this.mass * 20); }; Matter.Body.setDensity(me, 0.00005); //normal is 0.001 @@ -6359,7 +6359,7 @@ const spawn = { me.showHealthBar = false; me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; - me.onDeath = function() { + me.onDeath = function () { if (simulation.difficulty > 11) { //explode AoE const radius = 100 + 0.5 * simulation.difficulty + 50 * Math.random() if (m.immuneCycle < m.cycle && Vector.magnitude(Vector.sub(this.position, player.position)) < radius) m.damage(0.0003 * radius * simulation.dmgScale); @@ -6372,7 +6372,7 @@ const spawn = { }); } }; - me.do = function() { + me.do = function () { // this.gravity(); this.timeLimit(); if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0 && this.speed < 10) { @@ -6390,8 +6390,8 @@ const spawn = { me.friction = 0; me.frictionAir = 0.015; spawn.shield(me, x, y); - me.onDamage = function() {}; - me.do = function() { + me.onDamage = function () { }; + me.do = function () { this.seePlayerCheck(); this.checkStatus(); this.attraction(); @@ -6420,8 +6420,8 @@ const spawn = { me.friction = 0; me.frictionAir = 0.02; spawn.shield(me, x, y); - me.onDamage = function() {}; - me.do = function() { + me.onDamage = function () { }; + me.do = function () { this.seePlayerCheck(); this.checkStatus(); this.attraction(); @@ -6457,12 +6457,12 @@ const spawn = { spawn.shield(me, x, y, 1); spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) - me.onDeath = function() { + me.onDeath = function () { if (isSpawnBossPowerUp) powerUps.spawnBossPowerUp(this.position.x, this.position.y) // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //helps collisions functions work better after vertex have been changed }; - me.onDamage = function() {}; - me.do = function() { + me.onDamage = function () { }; + me.do = function () { // this.armor(); this.seePlayerCheck(); this.checkStatus(); @@ -6657,7 +6657,7 @@ const spawn = { spawn.spawnOrbitals(me, radius + 125, 1); spawn.spawnOrbitals(me, radius + 200, 1); Matter.Body.setDensity(me, 0.004 + 0.0002 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - me.onDeath = function() { //helps collisions functions work better after vertex have been changed + me.onDeath = function () { //helps collisions functions work better after vertex have been changed setTimeout(() => { //fix mob in place, but allow rotation for (let i = 0, len = 6; i < len; i++) { const speed = 2.25 * simulation.accelScale; @@ -6673,7 +6673,7 @@ const spawn = { powerUps.spawnBossPowerUp(this.position.x, this.position.y) } me.grenadeLimiter = 0 - me.onDamage = function() { + me.onDamage = function () { if (this.grenadeLimiter < 240 && this.health > 0) { this.grenadeLimiter += 60 spawn.grenade(this.position.x, this.position.y, 80 + Math.floor(60 * Math.random())); @@ -6686,7 +6686,7 @@ const spawn = { } }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { // this.armor(); if (this.grenadeLimiter > 1) this.grenadeLimiter-- this.seePlayerCheck(); @@ -6714,13 +6714,13 @@ const spawn = { x: 0, y: 0 }; - me.onDeath = function() { //helps collisions functions work better after vertex have been changed + me.onDeath = function () { //helps collisions functions work better after vertex have been changed spawn.grenade(this.position.x, this.position.y, 200 * simulation.CDScale); // mob[mob.length - 1].collisionFilter.category = 0 mob[mob.length - 1].collisionFilter.mask = cat.player | cat.map; } // spawn.shield(me, x, y); - me.do = function() { + me.do = function () { this.seePlayerCheck(); this.checkStatus(); @@ -6774,7 +6774,7 @@ const spawn = { mobs.spawn(x, y, 4, size, "rgb(215,0,190)"); //rgb(215,80,190) let me = mob[mob.length - 1]; me.stroke = "transparent"; - me.onHit = function() { + me.onHit = function () { this.explode(this.mass); }; Matter.Body.setDensity(me, 0.00004); //normal is 0.001 @@ -6788,7 +6788,7 @@ const spawn = { me.isDropPowerUp = false; me.isBadTarget = true; me.isMobBullet = true; - me.onDeath = function() { + me.onDeath = function () { //damage player if in range if (Vector.magnitude(Vector.sub(player.position, this.position)) < pulseRadius && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage @@ -6806,7 +6806,7 @@ const spawn = { me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.map | cat.body | cat.player // me.collisionFilter.mask = 0 - me.do = function() { + me.do = function () { this.timeLimit(); ctx.beginPath(); //draw explosion outline ctx.arc(this.position.x, this.position.y, pulseRadius * (1.01 - this.timeLeft / this.lifeSpan), 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay @@ -6843,15 +6843,15 @@ const spawn = { spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) Matter.Body.setDensity(me, 0.0045); //extra dense //normal is 0.001 //makes effective life much larger - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //helps collisions functions work better after vertex have been changed }; - me.onDamage = function() { + me.onDamage = function () { this.cycle = 0 }; me.damageReduction = 0.35 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { Matter.Body.rotate(this, 0.003) //gently spin around this.checkStatus(); ctx.beginPath(); //draw cycle timer @@ -6901,17 +6901,17 @@ const spawn = { me.damageReduction = 0.07 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.startingDamageReduction = me.damageReduction me.isInvulnerable = false - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) // requestAnimationFrame(() => { simulation.timePlayerSkip(120) }); //wrapping in animation frame prevents errors, probably }; - me.onDamage = function() { + me.onDamage = function () { //find side of mob closest to player //causes lag for foam,laser too many seekers //maybe scale chance with dmg // const where = Vector.add(this.position, Vector.mult(Vector.normalise(Vector.sub(m.pos, this.position)), this.radius + 10)) // spawn.seeker(where.x, where.y); //give the bullet a rotational velocity as if they were attached to a vertex }; - me.do = function() { + me.do = function () { this.seePlayerByHistory(60); this.attraction(); this.checkStatus(); @@ -6982,13 +6982,13 @@ const spawn = { spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) Matter.Body.setDensity(me, 0.01); //extra dense //normal is 0.001 //makes effective life much larger - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //helps collisions functions work better after vertex have been changed }; - me.onDamage = function() {}; + me.onDamage = function () { }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { // this.armor(); this.seePlayerCheck(); this.checkStatus(); @@ -7048,7 +7048,7 @@ const spawn = { mobs.spawn(x, y, sides, radius, "rgb(255,0,255)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; - me.onHit = function() { + me.onHit = function () { this.explode(this.mass * 20); }; Matter.Body.setDensity(me, 0.000015); //normal is 0.001 @@ -7063,7 +7063,7 @@ const spawn = { me.showHealthBar = false; me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; - me.do = function() { + me.do = function () { // this.seePlayer.yes = false; this.alwaysSeePlayer() this.attraction(); @@ -7076,7 +7076,7 @@ const spawn = { me.g = 0.0004; //required if using this.gravity me.leaveBody = false; // me.isDropPowerUp = false; - me.onDeath = function() { //run this function on death + me.onDeath = function () { //run this function on death for (let i = 0; i < Math.ceil(this.mass * 0.15 + Math.random() * 2.5); ++i) { spawn.spawns(this.position.x + (Math.random() - 0.5) * radius * 2.5, this.position.y + (Math.random() - 0.5) * radius * 2.5); Matter.Body.setVelocity(mob[mob.length - 1], { @@ -7086,7 +7086,7 @@ const spawn = { } }; spawn.shield(me, x, y); - me.do = function() { + me.do = function () { this.gravity(); this.seePlayerCheck(); this.checkStatus(); @@ -7096,7 +7096,7 @@ const spawn = { spawns(x, y, radius = 15) { mobs.spawn(x, y, 4, radius, "rgb(255,0,0)"); let me = mob[mob.length - 1]; - me.onHit = function() { //run this function on hitting player + me.onHit = function () { //run this function on hitting player this.explode(); }; // me.stroke = "transparent" @@ -7110,7 +7110,7 @@ const spawn = { me.leaveBody = false; me.seePlayerFreq = Math.floor((80 + 50 * Math.random())); me.frictionAir = 0.004; - me.do = function() { + me.do = function () { this.gravity(); this.seePlayerCheck(); this.checkStatus(); @@ -7167,12 +7167,12 @@ const spawn = { exploder(x, y, radius = 40 + Math.ceil(Math.random() * 50)) { mobs.spawn(x, y, 4, radius, "rgb(255,0,0)"); let me = mob[mob.length - 1]; - me.onHit = function() { + me.onHit = function () { //run this function on hitting player this.explode(); }; me.g = 0.0004; //required if using this.gravity - me.do = function() { + me.do = function () { this.gravity(); this.seePlayerCheck(); this.checkStatus(); @@ -7196,9 +7196,9 @@ const spawn = { me.damageReduction = 0 me.isInvulnerable = true - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) for (let i = 0, len = mob.length; i < len; i++) { if (this.id === mob[i].snakeHeadID && mob[i].alive) mob[i].death() @@ -7208,7 +7208,7 @@ const spawn = { me.canFire = false; me.closestVertex1 = 0; me.cycle = 0 - me.do = function() { + me.do = function () { // this.armor(); this.seePlayerByHistory(40) this.checkStatus(); @@ -7334,13 +7334,13 @@ const spawn = { me.ellipticity = 0.3 me.angleOff = 0.4 - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) for (let i = 0, len = mob.length; i < len; i++) { if (this.id === mob[i].snakeHeadID && mob[i].alive) mob[i].death() } }; - me.do = function() { + me.do = function () { this.seePlayerByHistory(40) this.checkStatus(); this.attraction(); @@ -7361,7 +7361,7 @@ const spawn = { a = Math.atan2(this.velocity.y, this.velocity.x) } - ctx.fillStyle = `hsla(${160+40*Math.random()}, 100%, ${25 + 25*Math.random()*Math.random()}%, 0.9)`; //"rgba(0,235,255,0.3)"; // ctx.fillStyle = `hsla(44, 79%, 31%,0.4)`; //"rgba(0,235,255,0.3)"; + ctx.fillStyle = `hsla(${160 + 40 * Math.random()}, 100%, ${25 + 25 * Math.random() * Math.random()}%, 0.9)`; //"rgba(0,235,255,0.3)"; // ctx.fillStyle = `hsla(44, 79%, 31%,0.4)`; //"rgba(0,235,255,0.3)"; this.wing(a + Math.PI / 2 + this.angleOff + this.flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingLength, this.ellipticity) this.wing(a - Math.PI / 2 - this.angleOff - this.flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingLength, this.ellipticity) this.wing(a - Math.PI / 2 + this.angleOff + this.flapArc * Math.sin(simulation.cycle * this.flapRate), this.wingLength, this.ellipticity) @@ -7375,7 +7375,7 @@ const spawn = { angle -= 0.1 spawn.snakeBody(x + tailRadius * Math.cos(angle), y + tailRadius * Math.sin(angle), i === 0 ? 25 : 20); const who = mob[mob.length - 1] - who.fill = `hsl(${160+40*Math.random()}, 100%, ${5 + 25*Math.random()*Math.random()}%)` + who.fill = `hsl(${160 + 40 * Math.random()}, 100%, ${5 + 25 * Math.random() * Math.random()}%)` if (i < 4) who.snakeHeadID = me.id if (i === 0) me.snakeBody1 = who //track this segment, so the difference in position between this segment and the head can be used to angle the wings who.previousTailID = previousTailID @@ -7421,7 +7421,7 @@ const spawn = { me.frictionAir = 0; me.isSnakeTail = true; me.stroke = "transparent" - me.onDeath = function() { + me.onDeath = function () { setTimeout(() => { for (let i = 0, len = mob.length; i < len; i++) { if (this.id === mob[i].previousTailID && mob[i].alive) mob[i].death() @@ -7432,7 +7432,7 @@ const spawn = { } }, 500); }; - me.do = function() { + me.do = function () { this.checkStatus(); }; // me.doActive = function() { @@ -7467,12 +7467,12 @@ const spawn = { spawn.shield(me, x, y, 1); setTimeout(() => { spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) }, 100); //have to wait a sec so the tether constraint doesn't attach to an orbital - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) this.removeCons(); //remove constraint me.babies(0.05 * simulation.difficulty + 1) }; - me.babies = function(len) { + me.babies = function (len) { const delay = Math.max(3, Math.floor(15 - len / 2)) let i = 0 let spawnFlutters = () => { @@ -7499,7 +7499,7 @@ const spawn = { } requestAnimationFrame(spawnFlutters); } - me.onDamage = function() { + me.onDamage = function () { if (this.health < this.nextHealthThreshold && this.alive) { this.health = this.nextHealthThreshold - 0.01 this.nextHealthThreshold = Math.floor(this.health * 4) / 4 @@ -7508,7 +7508,7 @@ const spawn = { this.damageReduction = 0 } }; - me.do = function() { + me.do = function () { this.gravity(); if (this.isInvulnerable) { this.repulsion(); @@ -7531,7 +7531,7 @@ const spawn = { for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 13 + 5 * Math.random(); - ctx.strokeStyle = `rgba(255,255,255,${0.5+0.2*Math.random()})`; + ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.stroke(); } else { this.seePlayerCheck(); @@ -7560,7 +7560,7 @@ const spawn = { }); Composite.add(engine.world, consBB[consBB.length - 1]); - me.onDamage = function() { + me.onDamage = function () { //make sure the mob that owns the shield can tell when damage is done this.alertNearByMobs(); this.fill = `rgba(220,220,255,${0.3 + 0.6 * this.health})` @@ -7572,13 +7572,13 @@ const spawn = { me.shieldTargetID = target.id target.isShielded = true; target.shieldID = me.id - me.onDeath = function() { + me.onDeath = function () { //clear isShielded status from target for (let i = 0, len = mob.length; i < len; i++) { if (mob[i].id === this.shieldTargetID) mob[i].isShielded = false; } }; - me.do = function() { + me.do = function () { this.checkStatus(); }; @@ -7611,11 +7611,11 @@ const spawn = { }); Composite.add(engine.world, consBB[consBB.length - 1]); } - me.onDamage = function() { + me.onDamage = function () { this.alertNearByMobs(); //makes sure the mob that owns the shield can tell when damage is done this.fill = `rgba(220,220,255,${0.3 + 0.6 * this.health})` }; - me.onDeath = function() { + me.onDeath = function () { //clear isShielded status from target for (let j = 0; j < targets.length; j++) { for (let i = 0, len = mob.length; i < len; i++) { @@ -7628,7 +7628,7 @@ const spawn = { me.showHealthBar = false; mob[mob.length - 1] = mob[mob.length - 1 - nodes]; mob[mob.length - 1 - nodes] = me; - me.do = function() { + me.do = function () { this.checkStatus(); }; }, @@ -7656,7 +7656,7 @@ const spawn = { // me.isShielded = true me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body - me.do = function() { + me.do = function () { //if host is gone if (!who || !who.alive) { this.death(); @@ -7717,10 +7717,10 @@ const spawn = { } for (let i = 0, len = 3 + 0.5 * Math.sqrt(simulation.difficulty); i < len; i++) spawn.spawnOrbitals(me, radius + 40 + 10 * i, 1); - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; - me.do = function() { + me.do = function () { this.seePlayerByHistory(); this.checkStatus(); this.attraction(); @@ -7889,7 +7889,7 @@ const spawn = { me.isBadTarget = true; me.isUnblockable = true; - me.do = function() { + me.do = function () { let wireX = -50; let wireY = -1000; if (this.freeOfWires) { @@ -7958,7 +7958,7 @@ const spawn = { me.isBadTarget = true; me.isUnblockable = true; - me.do = function() { + me.do = function () { let wireX = -50 - 20; let wireY = -1000; @@ -8010,7 +8010,7 @@ const spawn = { me.isBadTarget = true; me.isUnblockable = true; - me.do = function() { + me.do = function () { let wireX = -50 - 35; let wireY = -1000; @@ -8061,7 +8061,7 @@ const spawn = { me.isBadTarget = true; me.isUnblockable = true; - me.do = function() { + me.do = function () { let wireX = -50 + 16; let wireY = -1000; @@ -8112,7 +8112,7 @@ const spawn = { me.isBadTarget = true; me.isUnblockable = true; - me.do = function() { + me.do = function () { let wireX = -50 + 26; let wireY = -1000; diff --git a/js/tech.js b/js/tech.js index fa1be3b..98f4a27 100644 --- a/js/tech.js +++ b/js/tech.js @@ -242,7 +242,7 @@ const tech = { if (tech.isDamageForGuns) dmg *= 1 + 0.22 * Math.max(0, b.inventory.length - 1) if (tech.isOneGun && b.inventory.length < 2) dmg *= 1.25 if (tech.isAcidDmg && m.health > 1) dmg *= 1.35; - if (tech.isRerollDamage) dmg *= 1 + 0.03 * powerUps.research.count + if (tech.isRerollDamage) dmg *= 1 + Math.max(0, 0.03 * powerUps.research.count) if (tech.isBotDamage) dmg *= 1 + 0.06 * b.totalBots() if (tech.restDamage > 1 && player.speed < 1) dmg *= tech.restDamage if (tech.isLowEnergyDamage) dmg *= 1 + 0.7 * Math.max(0, 1 - m.energy) @@ -308,9161 +308,9160 @@ const tech = { } }, tech: [{ - name: "tungsten carbide", - description: "+200 maximum health
lose health after hard landings", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isSkin: true, - allowed() { - return !m.isAltSkin - }, - requires: "not skin", - effect() { - tech.hardLanding = 70 - tech.isFallingDamage = true; - m.setMaxHealth(); - m.addHealth(1 / simulation.healScale) - m.skin.tungsten() - }, - remove() { - tech.hardLanding = 130 - tech.isFallingDamage = false; - m.setMaxHealth(); - if (this.count) m.resetSkin(); - } + name: "tungsten carbide", + description: "+200 maximum health
lose health after hard landings", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isSkin: true, + allowed() { + return !m.isAltSkin }, - { - name: "elasticity", - description: "+33% movement and jumping
+30% defense", - maxCount: 3, - count: 0, - frequency: 1, - frequencyDefault: 1, - isSkin: true, - allowed() { - return !m.isAltSkin - }, - requires: "not skinned", - effect() { - m.skin.mech(); - tech.hardLanding = 110 - tech.squirrelFx += 0.4; - tech.squirrelJump += 0.16; - m.setMovement() - }, - remove() { - tech.hardLanding = 130 - tech.squirrelFx = 1; - tech.squirrelJump = 1; - m.setMovement() - if (this.count) m.resetSkin(); - } + requires: "not skin", + effect() { + tech.hardLanding = 70 + tech.isFallingDamage = true; + m.setMaxHealth(); + m.addHealth(1 / simulation.healScale) + m.skin.tungsten() }, - { - name: "aperture", - description: "every 6 seconds your damage cycles
between -50% and +150% damage", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isSkin: true, - allowed() { - return !m.isAltSkin - }, - requires: "not skinned", - effect() { - tech.isDilate = true - m.skin.dilate() - }, - remove() { - tech.isDilate = false - if (this.count) m.resetSkin(); - } + remove() { + tech.hardLanding = 130 + tech.isFallingDamage = false; + m.setMaxHealth(); + if (this.count) m.resetSkin(); + } + }, + { + name: "elasticity", + description: "+33% movement and jumping
+30% defense", + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + isSkin: true, + allowed() { + return !m.isAltSkin }, - { - name: "diaphragm", - description: "every 6 seconds your defense cycles
between +100% and -33% defense", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - isSkin: true, - allowed() { - return tech.isDilate - }, - requires: "aperture", - effect() { - tech.isDiaphragm = true - m.resetSkin(); - m.skin.dilate2() - }, - remove() { - tech.isDiaphragm = false - if (this.count) m.resetSkin(); - } + requires: "not skinned", + effect() { + m.skin.mech(); + tech.hardLanding = 110 + tech.squirrelFx += 0.4; + tech.squirrelJump += 0.16; + m.setMovement() }, - { - name: "mass-energy equivalence", - // description: "energy protects you instead of health
√ of defense reduction reduces max energy", - description: "energy protects you instead of health
exponentially reduced defense (~ x^0.13)", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isSkin: true, - allowed() { - return !m.isAltSkin && !tech.isPiezo && !tech.isRewindAvoidDeath && !tech.isAnnihilation //&& !tech.isAmmoFromHealth && !tech.isRewindGun - }, - requires: "not piezoelectricity, CPT, annihilation", - effect() { - m.health = 0 - document.getElementById("health").style.display = "none" - document.getElementById("health-bg").style.display = "none" - document.getElementById("dmg").style.backgroundColor = "#0cf"; - tech.isEnergyHealth = true; - simulation.mobDmgColor = "rgba(0, 255, 255,0.6)" //"#0cf" + remove() { + tech.hardLanding = 130 + tech.squirrelFx = 1; + tech.squirrelJump = 1; + m.setMovement() + if (this.count) m.resetSkin(); + } + }, + { + name: "aperture", + description: "every 6 seconds your damage cycles
between -50% and +150% damage", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isSkin: true, + allowed() { + return !m.isAltSkin + }, + requires: "not skinned", + effect() { + tech.isDilate = true + m.skin.dilate() + }, + remove() { + tech.isDilate = false + if (this.count) m.resetSkin(); + } + }, + { + name: "diaphragm", + description: "every 6 seconds your defense cycles
between +100% and -33% defense", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isSkin: true, + allowed() { + return tech.isDilate + }, + requires: "aperture", + effect() { + tech.isDiaphragm = true + m.resetSkin(); + m.skin.dilate2() + }, + remove() { + tech.isDiaphragm = false + if (this.count) m.resetSkin(); + } + }, + { + name: "mass-energy equivalence", + // description: "energy protects you instead of health
√ of defense reduction reduces max energy", + description: "energy protects you instead of health
exponentially reduced defense (~ x^0.13)", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isSkin: true, + allowed() { + return !m.isAltSkin && !tech.isPiezo && !tech.isRewindAvoidDeath && !tech.isAnnihilation //&& !tech.isAmmoFromHealth && !tech.isRewindGun + }, + requires: "not piezoelectricity, CPT, annihilation", + effect() { + m.health = 0 + document.getElementById("health").style.display = "none" + document.getElementById("health-bg").style.display = "none" + document.getElementById("dmg").style.backgroundColor = "#0cf"; + tech.isEnergyHealth = true; + simulation.mobDmgColor = "rgba(0, 255, 255,0.6)" //"#0cf" + m.displayHealth(); + m.lastCalculatedDefense = 0 //this triggers a redraw of the defense bar + m.skin.energy(); + }, + remove() { + if (tech.isEnergyHealth) { + tech.isEnergyHealth = false; + document.getElementById("health").style.display = "inline" + document.getElementById("health-bg").style.display = "inline" + document.getElementById("dmg").style.backgroundColor = "#f67"; + m.health = Math.max(Math.min(m.maxHealth, m.energy), 0.1); + simulation.mobDmgColor = "rgba(255,0,0,0.7)" m.displayHealth(); m.lastCalculatedDefense = 0 //this triggers a redraw of the defense bar - m.skin.energy(); - }, - remove() { - if (tech.isEnergyHealth) { - tech.isEnergyHealth = false; - document.getElementById("health").style.display = "inline" - document.getElementById("health-bg").style.display = "inline" - document.getElementById("dmg").style.backgroundColor = "#f67"; - m.health = Math.max(Math.min(m.maxHealth, m.energy), 0.1); - simulation.mobDmgColor = "rgba(255,0,0,0.7)" - m.displayHealth(); - m.lastCalculatedDefense = 0 //this triggers a redraw of the defense bar - m.resetSkin(); - } - tech.isEnergyHealth = false; + m.resetSkin(); + } + tech.isEnergyHealth = false; + } + }, + { + name: "1st ionization energy", + link: `
1st ionization energy`, + // description: `after you collect ${powerUps.orb.heal()}
+${0.1 * tech.largerHeals} maximum energy`, + // descriptionFunction: `convert current and future ${powerUps.orb.heal()} into

give +${10 * tech.largerHeals} maximum energy`, + descriptionFunction() { + return `convert current and future
into

give +${8 * tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1)} maximum energy` + }, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isEnergyHealth + }, + requires: "mass-energy equivalence", + effect() { + powerUps.healGiveMaxEnergy = true; //tech.healMaxEnergyBonus given from heal power up + powerUps.heal.color = "#ff0" //"#0ae" + for (let i = 0; i < powerUp.length; i++) { //find active heal power ups and adjust color live + if (powerUp[i].name === "heal") powerUp[i].color = powerUps.heal.color } }, - { - name: "1st ionization energy", - link: `1st ionization energy`, - // description: `after you collect ${powerUps.orb.heal()}
+${0.1 * tech.largerHeals} maximum energy`, - // descriptionFunction: `convert current and future ${powerUps.orb.heal()} into

give +${10 * tech.largerHeals} maximum energy`, - descriptionFunction() { - return `convert current and future
into

give +${8 * tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1)} maximum energy` - }, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isEnergyHealth - }, - requires: "mass-energy equivalence", - effect() { - powerUps.healGiveMaxEnergy = true; //tech.healMaxEnergyBonus given from heal power up - powerUps.heal.color = "#ff0" //"#0ae" - for (let i = 0; i < powerUp.length; i++) { //find active heal power ups and adjust color live - if (powerUp[i].name === "heal") powerUp[i].color = powerUps.heal.color - } - }, - remove() { - powerUps.healGiveMaxEnergy = false; - // tech.healMaxEnergyBonus = 0 - powerUps.heal.color = "#0eb" - for (let i = 0; i < powerUp.length; i++) { //find active heal power ups and adjust color live - if (powerUp[i].name === "heal") powerUp[i].color = powerUps.heal.color + remove() { + powerUps.healGiveMaxEnergy = false; + // tech.healMaxEnergyBonus = 0 + powerUps.heal.color = "#0eb" + for (let i = 0; i < powerUp.length; i++) { //find active heal power ups and adjust color live + if (powerUp[i].name === "heal") powerUp[i].color = powerUps.heal.color + } + } + }, + { + name: "CPT symmetry", + // description: "charge, parity, and time invert to undo defense
rewind (1.5—5) seconds for (66—220) energy", + // description: "after losing health, if you have full energy
rewind time for 44 energy per second", + descriptionFunction() { + return `after losing health, if you have ${(100 * Math.min(100, m.maxEnergy)).toFixed(0)} energy
rewind time for 40 energy per second` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isSkin: true, + allowed() { + return !m.isAltSkin && m.fieldUpgrades[m.fieldMode].name !== "standing wave" && !tech.isRewindField && !tech.isEnergyHealth + }, + requires: "not skinned, standing wave, max energy reduction, retrocausality, mass-energy", + effect() { + tech.isRewindAvoidDeath = true; + m.skin.CPT() + }, + remove() { + tech.isRewindAvoidDeath = false; + if (this.count) m.resetSkin(); + } + }, + { + name: "causality bots", + link: `causality bots`, + description: "when you rewind build scrap bots
that protect you for about 9 seconds", + maxCount: 3, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBotTech: true, + allowed() { + return tech.isRewindAvoidDeath || tech.isRewindField + }, + requires: "CPT, retrocausality", + effect() { + tech.isRewindBot++; + }, + remove() { + tech.isRewindBot = 0; + } + }, + { + name: "causality bombs", + link: `causality bombs`, + description: "when you rewind drop several grenades
become invulnerable until they explode", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isRewindAvoidDeath || tech.isRewindField + }, + requires: "CPT, retrocausality", + effect() { + tech.isRewindGrenade = true; + }, + remove() { + tech.isRewindGrenade = false; + } + }, + { + name: "ternary", //"divisor", + descriptionFunction() { + return `+40% damage while one of your guns
has ammo divisible by 3` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + // divisible: 3, // + Math.floor(6 * Math.random()), + effect() { + tech.isDivisor = true; + }, + remove() { + tech.isDivisor = false; + } + }, + { + name: "ordnance", + description: "double the frequency of finding guntech
spawn a gun and +7% JUNK to tech pool", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed: () => true, + requires: "", + effect() { + powerUps.spawn(m.pos.x, m.pos.y, "gun"); + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isGunTech) tech.tech[i].frequency *= 2 + } + this.refundAmount += tech.addJunkTechToPool(0.07) + }, + refundAmount: 0, + remove() { + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "ad hoc", + descriptionFunction() { + return `spawn a ${powerUps.orb.heal()}, ${powerUps.orb.research(1)}, ${powerUps.orb.ammo(1)}, field, gun, or tech
for each of your guns` + }, + maxCount: 1, //random power up + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + allowed() { + return b.inventory.length > 1 + }, + requires: "at least 2 guns", + effect() { + for (let i = 0; i < b.inventory.length; i++) { + if (Math.random() < 1 / 6) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "gun"); + } else if (Math.random() < 1 / 5) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "tech"); + } else if (Math.random() < 1 / 4) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "field"); + } else if (Math.random() < 1 / 3) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "heal"); + } else if (Math.random() < 1 / 2) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "ammo"); + } else { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "research"); } } }, - { - name: "CPT symmetry", - // description: "charge, parity, and time invert to undo defense
rewind (1.5—5) seconds for (66—220) energy", - // description: "after losing health, if you have full energy
rewind time for 44 energy per second", - descriptionFunction() { - return `after losing health, if you have ${(100*Math.min(100,m.maxEnergy)).toFixed(0)} energy
rewind time for 40 energy per second` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isSkin: true, - allowed() { - return !m.isAltSkin && m.fieldUpgrades[m.fieldMode].name !== "standing wave" && !tech.isRewindField && !tech.isEnergyHealth - }, - requires: "not skinned, standing wave, max energy reduction, retrocausality, mass-energy", - effect() { - tech.isRewindAvoidDeath = true; - m.skin.CPT() - }, - remove() { - tech.isRewindAvoidDeath = false; - if (this.count) m.resetSkin(); - } + remove() { } + }, + { + name: "applied science", + description: `get a random guntech
for each of your guns`, //spawn ${powerUps.orb.research(1)} and + maxCount: 9, + count: 0, + isNonRefundable: true, + frequency: 2, + frequencyDefault: 2, + allowed() { + return b.inventory.length > 1 }, - { - name: "causality bots", - link: `causality bots`, - description: "when you rewind build scrap bots
that protect you for about 9 seconds", - maxCount: 3, - count: 0, - frequency: 2, - frequencyDefault: 2, - isBotTech: true, - allowed() { - return tech.isRewindAvoidDeath || tech.isRewindField - }, - requires: "CPT, retrocausality", - effect() { - tech.isRewindBot++; - }, - remove() { - tech.isRewindBot = 0; - } - }, - { - name: "causality bombs", - link: `causality bombs`, - description: "when you rewind drop several grenades
become invulnerable until they explode", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isRewindAvoidDeath || tech.isRewindField - }, - requires: "CPT, retrocausality", - effect() { - tech.isRewindGrenade = true; - }, - remove() { - tech.isRewindGrenade = false; - } - }, - { - name: "ternary", //"divisor", - descriptionFunction() { - return `+40% damage while one of your guns
has ammo divisible by 3` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed: () => true, - requires: "", - // divisible: 3, // + Math.floor(6 * Math.random()), - effect() { - tech.isDivisor = true; - }, - remove() { - tech.isDivisor = false; - } - }, - { - name: "ordnance", - description: "double the frequency of finding guntech
spawn a gun and +7% JUNK to tech pool", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - isBadRandomOption: true, - allowed: () => true, - requires: "", - effect() { - powerUps.spawn(m.pos.x, m.pos.y, "gun"); - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isGunTech) tech.tech[i].frequency *= 2 - } - this.refundAmount += tech.addJunkTechToPool(0.07) - }, - refundAmount: 0, - remove() { - if (this.count > 0 && this.refundAmount > 0) { - tech.removeJunkTechFromPool(this.refundAmount) - this.refundAmount = 0 - } - } - }, - { - name: "ad hoc", - descriptionFunction() { - return `spawn a ${powerUps.orb.heal()}, ${powerUps.orb.research(1)}, ${powerUps.orb.ammo(1)}, field, gun, or tech
for each of your guns` - }, - maxCount: 1, //random power up - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - allowed() { - return b.inventory.length > 1 - }, - requires: "at least 2 guns", - effect() { - for (let i = 0; i < b.inventory.length; i++) { - if (Math.random() < 1 / 6) { - powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "gun"); - } else if (Math.random() < 1 / 5) { - powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "tech"); - } else if (Math.random() < 1 / 4) { - powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "field"); - } else if (Math.random() < 1 / 3) { - powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "heal"); - } else if (Math.random() < 1 / 2) { - powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "ammo"); - } else { - powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "research"); + requires: "at least 2 guns", + effect() { + for (let i = b.inventory.length - 1; i > -1; i--) { //backwards because some tech can remove or add guns + const gunTechPool = [] //find gun tech for this gun + for (let j = 0, len = tech.tech.length; j < len; j++) { + // console.log(j, tech.tech[j].isGunTech, tech.tech[j].allowed(), !tech.tech[j].isJunk, !tech.tech[j].isBadRandomOption, tech.tech[j].count < tech.tech[j].maxCount) + const originalActiveGunIndex = b.activeGun //set current gun to active so allowed works + b.activeGun = b.inventory[i] //to make the .allowed work for guns that aren't active + if (tech.tech[j].isGunTech && tech.tech[j].allowed() && !tech.tech[j].isJunk && !tech.tech[j].isBadRandomOption && tech.tech[j].count < tech.tech[j].maxCount) { + const regex = tech.tech[j].requires.search(b.guns[b.inventory[i]].name) //get string index of gun name + const not = tech.tech[j].requires.search(' not ') //get string index of ' not ' + if (regex !== -1 && (not === -1 || not > regex)) gunTechPool.push(j) //look for the gun name in the requirements, but the gun name needs to show up before the word ' not ' } - } - }, - remove() {} - }, - { - name: "applied science", - description: `get a random guntech
for each of your guns`, //spawn ${powerUps.orb.research(1)} and - maxCount: 9, - count: 0, - isNonRefundable: true, - frequency: 2, - frequencyDefault: 2, - allowed() { - return b.inventory.length > 1 - }, - requires: "at least 2 guns", - effect() { - for (let i = b.inventory.length - 1; i > -1; i--) { //backwards because some tech can remove or add guns - const gunTechPool = [] //find gun tech for this gun - for (let j = 0, len = tech.tech.length; j < len; j++) { - // console.log(j, tech.tech[j].isGunTech, tech.tech[j].allowed(), !tech.tech[j].isJunk, !tech.tech[j].isBadRandomOption, tech.tech[j].count < tech.tech[j].maxCount) - const originalActiveGunIndex = b.activeGun //set current gun to active so allowed works - b.activeGun = b.inventory[i] //to make the .allowed work for guns that aren't active - if (tech.tech[j].isGunTech && tech.tech[j].allowed() && !tech.tech[j].isJunk && !tech.tech[j].isBadRandomOption && tech.tech[j].count < tech.tech[j].maxCount) { - const regex = tech.tech[j].requires.search(b.guns[b.inventory[i]].name) //get string index of gun name - const not = tech.tech[j].requires.search(' not ') //get string index of ' not ' - if (regex !== -1 && (not === -1 || not > regex)) gunTechPool.push(j) //look for the gun name in the requirements, but the gun name needs to show up before the word ' not ' + b.activeGun = originalActiveGunIndex + if (!b.guns[b.activeGun].have) { + if (b.inventory.length === 0) { + b.activeGun = null + } else { + b.activeGun = b.inventory[0] } - b.activeGun = originalActiveGunIndex - if (!b.guns[b.activeGun].have) { - if (b.inventory.length === 0) { - b.activeGun = null - } else { - b.activeGun = b.inventory[0] - } - b.inventoryGun = 0; - } - } - if (gunTechPool.length) { - const index = Math.floor(Math.random() * gunTechPool.length) - tech.giveTech(gunTechPool[index]) // choose from the gun pool - tech.tech[gunTechPool[index]].isFromAppliedScience = true //makes it not remove properly under paradigm shift - simulation.makeTextLog(`tech.giveTech("${tech.tech[gunTechPool[index]].name}")`, 360) + b.inventoryGun = 0; } } - simulation.boldActiveGunHUD(); - }, - remove() {} - }, - { - name: "arsenal", - descriptionFunction() { - return `+22% damage per unequipped gun (${(22 * Math.max(0, b.inventory.length-1)).toFixed(0)}%)` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed: () => b.inventory.length > 1, - requires: "at least 2 guns", - effect() { - tech.isDamageForGuns = true; - }, - remove() { - tech.isDamageForGuns = false; - } - }, - { - name: "active cooling", - descriptionFunction() { - return `+28% fire rate per unequipped gun (${(28 * Math.max(0, b.inventory.length-1)).toFixed(0)}%)` - }, //
but not including your equipped gun` }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed: () => b.inventory.length > 1, - requires: "at least 2 guns", - effect() { - tech.isFireRateForGuns = true; - b.setFireCD(); - }, - remove() { - tech.isFireRateForGuns = false; - b.setFireCD(); - } - }, - { - name: "pigeonhole principle", - descriptionFunction() { - let info = "" - if (this.count > 0 && Number.isInteger(tech.buffedGun) && b.inventory.length) { - let gun = b.guns[b.inventory[tech.buffedGun]].name - info = `
this level: +${(31 * Math.max(0, b.inventory.length)).toFixed(0)}% damage for ${gun}` + if (gunTechPool.length) { + const index = Math.floor(Math.random() * gunTechPool.length) + tech.giveTech(gunTechPool[index]) // choose from the gun pool + tech.tech[gunTechPool[index]].isFromAppliedScience = true //makes it not remove properly under paradigm shift + simulation.makeTextLog(`tech.giveTech("${tech.tech[gunTechPool[index]].name}")`, 360) } - return ` + } + simulation.boldActiveGunHUD(); + }, + remove() { } + }, + { + name: "arsenal", + descriptionFunction() { + return `+22% damage per unequipped gun (${(22 * Math.max(0, b.inventory.length - 1)).toFixed(0)}%)` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => b.inventory.length > 1, + requires: "at least 2 guns", + effect() { + tech.isDamageForGuns = true; + }, + remove() { + tech.isDamageForGuns = false; + } + }, + { + name: "active cooling", + descriptionFunction() { + return `+28% fire rate per unequipped gun (${(28 * Math.max(0, b.inventory.length - 1)).toFixed(0)}%)` + }, //
but not including your equipped gun` }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => b.inventory.length > 1, + requires: "at least 2 guns", + effect() { + tech.isFireRateForGuns = true; + b.setFireCD(); + }, + remove() { + tech.isFireRateForGuns = false; + b.setFireCD(); + } + }, + { + name: "pigeonhole principle", + descriptionFunction() { + let info = "" + if (this.count > 0 && Number.isInteger(tech.buffedGun) && b.inventory.length) { + let gun = b.guns[b.inventory[tech.buffedGun]].name + info = `
this level: +${(31 * Math.max(0, b.inventory.length)).toFixed(0)}% damage for ${gun}` + } + return ` a new gun is chosen to be improved each level
+31% damage per gun for the chosen gun${info}` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return b.inventory.length > 1 - }, - requires: "at least 2 guns", - effect() { - tech.isGunChoice = true - //switches gun on new level - //generalist uses the same chosen gun so they match - }, - remove() { - tech.isGunChoice = false; - } }, - { - name: "generalist", - description: "spawn 7 guns, but you can't switch guns
your equipped gun cycles after each level", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - isBadRandomOption: true, - allowed() { - return b.inventory.length < b.guns.length - 5 && b.inventory.length > 1 - }, - requires: "at least 2 guns, at least 5 unclaimed guns", - effect() { - tech.isGunCycle = true; - for (let i = 0; i < 7; i++) powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "gun"); - }, - remove() { - if (!this.count) tech.isGunCycle = false; // only set to false if you don't have this tech - // if (tech.isGunCycle) { - // for (let i = 0; i < 8; i++) { - // if (b.inventory.length) b.removeGun(b.guns[b.inventory[b.inventory.length - 1]].name) //remove your last gun - // } - // tech.isGunCycle = false; - // } - } + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return b.inventory.length > 1 }, - { - name: "integrated armament", - link: `integrated armament`, - description: `+25% damage, but new guns
replace your current gun and convert guntech
`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return b.inventory.length === 1 - }, - requires: "only 1 gun", - effect() { - tech.isOneGun = true; - }, - remove() { - tech.isOneGun = false; - } + requires: "at least 2 guns", + effect() { + tech.isGunChoice = true + //switches gun on new level + //generalist uses the same chosen gun so they match }, - { - name: "supply chain", - descriptionFunction() { - return `double your current ammo
+4% JUNK to tech pool` - }, - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { + remove() { + tech.isGunChoice = false; + } + }, + { + name: "generalist", + description: "spawn 7 guns, but you can't switch guns
your equipped gun cycles after each level", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return b.inventory.length < b.guns.length - 5 && b.inventory.length > 1 + }, + requires: "at least 2 guns, at least 5 unclaimed guns", + effect() { + tech.isGunCycle = true; + for (let i = 0; i < 7; i++) powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "gun"); + }, + remove() { + if (!this.count) tech.isGunCycle = false; // only set to false if you don't have this tech + // if (tech.isGunCycle) { + // for (let i = 0; i < 8; i++) { + // if (b.inventory.length) b.removeGun(b.guns[b.inventory[b.inventory.length - 1]].name) //remove your last gun + // } + // tech.isGunCycle = false; + // } + } + }, + { + name: "integrated armament", + link: `integrated armament`, + description: `+25% damage, but new guns
replace your current gun and convert guntech
`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return b.inventory.length === 1 + }, + requires: "only 1 gun", + effect() { + tech.isOneGun = true; + }, + remove() { + tech.isOneGun = false; + } + }, + { + name: "supply chain", + descriptionFunction() { + return `double your current ammo
+4% JUNK to tech pool` + }, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0; i < b.guns.length; i++) { + if (b.guns[i].have) b.guns[i].ammo = Math.floor(2 * b.guns[i].ammo) + } + simulation.makeGunHUD(); + this.refundAmount += tech.addJunkTechToPool(0.04) + }, + refundAmount: 0, + remove() { + for (let j = 0; j < this.count; j++) { for (let i = 0; i < b.guns.length; i++) { - if (b.guns[i].have) b.guns[i].ammo = Math.floor(2 * b.guns[i].ammo) - } - simulation.makeGunHUD(); - this.refundAmount += tech.addJunkTechToPool(0.04) - }, - refundAmount: 0, - remove() { - for (let j = 0; j < this.count; j++) { - for (let i = 0; i < b.guns.length; i++) { - if (b.guns[i].have) b.guns[i].ammo = Math.floor(0.5 * b.guns[i].ammo) - } - } - simulation.makeGunHUD(); - if (this.count > 0 && this.refundAmount > 0) { - tech.removeJunkTechFromPool(this.refundAmount) - this.refundAmount = 0 + if (b.guns[i].have) b.guns[i].ammo = Math.floor(0.5 * b.guns[i].ammo) } } - }, - { - name: "logistics", - description: `${powerUps.orb.ammo()} give 80% more ammo, but
it's only added to your current gun`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isEnergyNoAmmo - }, - requires: "not non-renewables", - effect() { - tech.isAmmoForGun = true; - }, - remove() { - tech.isAmmoForGun = false; + simulation.makeGunHUD(); + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 } + } + }, + { + name: "logistics", + description: `${powerUps.orb.ammo()} give 80% more ammo, but
it's only added to your current gun`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyNoAmmo }, - { - name: "cache", - link: `cache`, - description: `${powerUps.orb.ammo()} give 1500% more ammo, but
you can't store any more ammo than that`, - // ammo powerups always max out your gun, - // but the maximum ammo ti limited - // description: `${powerUps.orb.ammo()} give 13x more ammo, but
you can't store any more ammo than that`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isEnergyNoAmmo - }, - requires: "not non-renewables", - effect() { - tech.ammoCap = 15; - powerUps.ammo.effect() - }, - remove() { - tech.ammoCap = 0; - } + requires: "not non-renewables", + effect() { + tech.isAmmoForGun = true; }, - { - name: "catabolism", - descriptionFunction() { - return `if you fire while out of ammo
spawn ${powerUps.orb.ammo(4)} and ${tech.isEnergyHealth ? "–4 maximum energy" : "–2 maximum health"}` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isEnergyNoAmmo - }, - requires: "not non-renewables", - effect() { - tech.isAmmoFromHealth = true; - }, - remove() { - tech.isAmmoFromHealth = false; - } + remove() { + tech.isAmmoForGun = false; + } + }, + { + name: "cache", + link: `cache`, + description: `${powerUps.orb.ammo()} give 1500% more ammo, but
you can't store any more ammo than that`, + // ammo powerups always max out your gun, + // but the maximum ammo ti limited + // description: `${powerUps.orb.ammo()} give 13x more ammo, but
you can't store any more ammo than that`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyNoAmmo }, - { - name: "non-renewables", - description: `+67% damage
${powerUps.orb.ammo()} can't spawn`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isAmmoFromHealth && !tech.isBoostReplaceAmmo - }, - requires: "not catabolism, quasiparticles", - damage: 1.67, - effect() { - tech.damage *= this.damage - tech.isEnergyNoAmmo = true; - }, - remove() { - if (this.count) tech.damage /= this.damage - tech.isEnergyNoAmmo = false; - } + requires: "not non-renewables", + effect() { + tech.ammoCap = 15; + powerUps.ammo.effect() }, - { - name: "desublimated ammunition", - link: `desublimated ammunition`, - description: `if crouching
alternating shots use no ammo`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed: () => true, - requires: "", - effect() { - tech.crouchAmmoCount = true - }, - remove() { - tech.crouchAmmoCount = false; - } + remove() { + tech.ammoCap = 0; + } + }, + { + name: "catabolism", + descriptionFunction() { + return `if you fire while out of ammo
spawn ${powerUps.orb.ammo(4)} and ${tech.isEnergyHealth ? "–4 maximum energy" : "–2 maximum health"}` }, - { - name: "gun turret", - description: "if crouching
+66% defense ", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.isTurret = true - }, - remove() { - tech.isTurret = false; - } + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyNoAmmo }, - { - name: "dead reckoning", - description: "if your speed is 0
+50% damage", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.restDamage += 0.5 - }, - remove() { - tech.restDamage = 1; - } + requires: "not non-renewables", + effect() { + tech.isAmmoFromHealth = true; }, - { - name: "kinetic bombardment", - description: "far away mobs take more damage
up to +33% damage at 3000 displacement", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.isFarAwayDmg = true; //used in mob.damage() - }, - remove() { - tech.isFarAwayDmg = false; - } + remove() { + tech.isAmmoFromHealth = false; + } + }, + { + name: "non-renewables", + description: `+67% damage
${powerUps.orb.ammo()} can't spawn`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isAmmoFromHealth && !tech.isBoostReplaceAmmo }, - { - name: "Higgs mechanism", - description: "+45% fire rate
while firing your position is fixed", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !m.isShipMode && !tech.isAlwaysFire, !tech.isGrapple - }, - requires: "not ship mode, automatic, grappling hook", - effect() { - tech.isFireMoveLock = true; + requires: "not catabolism, quasiparticles", + damage: 1.67, + effect() { + tech.damage *= this.damage + tech.isEnergyNoAmmo = true; + }, + remove() { + if (this.count) tech.damage /= this.damage + tech.isEnergyNoAmmo = false; + } + }, + { + name: "desublimated ammunition", + link: `desublimated ammunition`, + description: `if crouching
alternating shots use no ammo`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + effect() { + tech.crouchAmmoCount = true + }, + remove() { + tech.crouchAmmoCount = false; + } + }, + { + name: "gun turret", + description: "if crouching
+66% defense ", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isTurret = true + }, + remove() { + tech.isTurret = false; + } + }, + { + name: "dead reckoning", + description: "if your speed is 0
+50% damage", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.restDamage += 0.5 + }, + remove() { + tech.restDamage = 1; + } + }, + { + name: "kinetic bombardment", + description: "far away mobs take more damage
up to +33% damage at 3000 displacement", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isFarAwayDmg = true; //used in mob.damage() + }, + remove() { + tech.isFarAwayDmg = false; + } + }, + { + name: "Higgs mechanism", + description: "+45% fire rate
while firing your position is fixed", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !m.isShipMode && !tech.isAlwaysFire, !tech.isGrapple + }, + requires: "not ship mode, automatic, grappling hook", + effect() { + tech.isFireMoveLock = true; + b.setFireCD(); + b.setFireMethod(); + }, + remove() { + if (tech.isFireMoveLock) { + tech.isFireMoveLock = false b.setFireCD(); b.setFireMethod(); - }, - remove() { - if (tech.isFireMoveLock) { - tech.isFireMoveLock = false - b.setFireCD(); - b.setFireMethod(); - } } - }, + } + }, - // { - // name: "coyote", - // description: "", - // maxCount: 1, - // count: 0, - // frequency: 1, - // frequencyDefault: 1, - // allowed() { return true }, - // requires: "", - // effect() { // good with melee builds, content skipping builds - // tech.coyoteTime = 120 - // // simulation.gravity = function() { - // // function addGravity(bodies, magnitude) { - // // for (var i = 0; i < bodies.length; i++) { - // // bodies[i].force.y += bodies[i].mass * magnitude; - // // } - // // } - // // if (!m.isBodiesAsleep) { - // // addGravity(powerUp, simulation.g); - // // addGravity(body, simulation.g); - // // } - // // player.force.y += player.mass * simulation.g - // // } - // }, - // remove() { - // tech.coyoteTime = 5 - // } - // }, - { - name: "Newtons 1st law", - description: "defense is proportional to your speed
up to +66% defense at 40 speed", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.isSpeedHarm = true //max at speed = 40 - }, - remove() { - tech.isSpeedHarm = false + // { + // name: "coyote", + // description: "", + // maxCount: 1, + // count: 0, + // frequency: 1, + // frequencyDefault: 1, + // allowed() { return true }, + // requires: "", + // effect() { // good with melee builds, content skipping builds + // tech.coyoteTime = 120 + // // simulation.gravity = function() { + // // function addGravity(bodies, magnitude) { + // // for (var i = 0; i < bodies.length; i++) { + // // bodies[i].force.y += bodies[i].mass * magnitude; + // // } + // // } + // // if (!m.isBodiesAsleep) { + // // addGravity(powerUp, simulation.g); + // // addGravity(body, simulation.g); + // // } + // // player.force.y += player.mass * simulation.g + // // } + // }, + // remove() { + // tech.coyoteTime = 5 + // } + // }, + { + name: "Newtons 1st law", + description: "defense is proportional to your speed
up to +66% defense at 40 speed", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isSpeedHarm = true //max at speed = 40 + }, + remove() { + tech.isSpeedHarm = false + } + }, + { + name: "Newtons 2nd law", + description: "damage is proportional to your speed
up to +66% damage at 40 speed", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isSpeedDamage = true //max at speed = 40 + }, + remove() { + tech.isSpeedDamage = false + } + }, + { + name: "microstates", + link: `microstates`, + description: "for each active bullet / bot
+0.7% damage", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isDamageFromBulletCount = true + }, + remove() { + tech.isDamageFromBulletCount = false + } + }, + { + name: "regression", + description: "bullet collisions increase vulnerability to
damage by +5% for mobs (+0.25% for bosses)", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isLessDamageReduction = true + }, + remove() { + tech.isLessDamageReduction = false + } + }, + { + name: "simulated annealing", + description: "+20% damage
–20% fire rate", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + damage: 1.2, + effect() { + tech.damage *= this.damage + tech.slowFire = 1.2 + b.setFireCD(); + }, + remove() { + if (this.count) tech.damage /= this.damage + tech.slowFire = 1; + b.setFireCD(); + } + }, + { + name: "heuristics", + description: "+25% fire rate
spawn a gun", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.fireRate *= 0.75 + b.setFireCD(); + powerUps.spawn(m.pos.x, m.pos.y, "gun"); + }, + remove() { + tech.fireRate = 1; + b.setFireCD(); + } + }, + { + name: "anti-shear topology", + link: `anti-shear topology`, + description: "+30% projectile duration", //
drone spore worm flea missile foam wave neutron ice", + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + effect() { + tech.isBulletsLastLonger += 0.3 + }, + remove() { + tech.isBulletsLastLonger = 1; + } + }, + { + name: "fracture analysis", + description: "if a mob is stunned it takes
+400% damage from bullet impacts", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isStunField || tech.oneSuperBall || tech.isCloakStun || tech.isOrbitBotUpgrade || tech.isStun + }, + requires: "a stun effect", + effect() { + tech.isCrit = true; + }, + remove() { + tech.isCrit = false; + } + }, + { + name: "shear stress", + description: "after mobs die
they release a nail that targets nearby mobs", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.iceIXOnDeath + }, + requires: "no other mob death tech", + effect() { + tech.nailsDeathMob++ + }, + remove() { + tech.nailsDeathMob = 0; + } + }, + { + name: "thermal runaway", + description: "after mobs die
they explode", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.sporesOnDeath && !tech.nailsDeathMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.iceIXOnDeath + }, + requires: "no other mob death tech", + effect() { + tech.isExplodeMob = true; + }, + remove() { + tech.isExplodeMob = false; + } + }, + { + name: "zoospore vector", + link: `zoospore vector`, + descriptionFunction() { + return `after mobs die there is a +10% chance
they grow ${b.guns[6].nameString('s')}` + }, + // description: "after mobs die
they have a +10% chance to grow spores", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.nailsDeathMob && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.iceIXOnDeath + }, + requires: "no other mob death tech", + effect() { + tech.sporesOnDeath += 0.1; + // if (tech.isSporeWorm) { + // for (let i = 0; i < 4; i++) b.worm(m.pos) + // } else { + // for (let i = 0; i < 8; i++) b.spore(m.pos) + // } + }, + remove() { + tech.sporesOnDeath = 0; + } + }, + { + name: "propagator", + description: "after mobs die advance time 0.5 seconds
+60% damage", + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + effect() { + tech.deathSkipTime++ + }, + remove() { + tech.deathSkipTime = 0 + } + }, + { + name: "collider", + descriptionFunction() { + return `after mobs die there is a +33% chance
to smash power ups into a different flavor` + }, + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + effect() { + tech.collidePowerUps += 0.33333 + }, + remove() { + tech.collidePowerUps = 0 + } + }, + { + name: "bubble fusion", + descriptionFunction() { + return `after destroying a mob's natural shield
spawn 1-2 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isShieldAmmo = true; + }, + remove() { + tech.isShieldAmmo = false; + } + }, + { + name: "reaction inhibitor", + description: "-12% maximum mob health", //health + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true //tech.nailsDeathMob || tech.sporesOnDeath || tech.isExplodeMob || tech.botSpawner || tech.isMobBlockFling || tech.iceIXOnDeath + }, + requires: "", //"any mob death tech", + effect() { + tech.mobSpawnWithHealth++ + mobs.setMobSpawnHealth() + //set all mobs at full health to 0.85 + for (let i = 0; i < mob.length; i++) { + if (mob.health > mobs.mobSpawnWithHealth) mob.health = mobs.mobSpawnWithHealth } }, - { - name: "Newtons 2nd law", - description: "damage is proportional to your speed
up to +66% damage at 40 speed", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.isSpeedDamage = true //max at speed = 40 - }, - remove() { - tech.isSpeedDamage = false - } + remove() { + tech.mobSpawnWithHealth = 0 + mobs.setMobSpawnHealth() + } + }, + { + name: "scrap bots", + link: `scrap bots`, + description: "after mobs die you have a +33% chance
to build scrap bots that operate for 13 seconds", + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBotTech: true, + allowed() { + return !tech.sporesOnDeath && !tech.nailsDeathMob && !tech.isExplodeMob && !tech.isMobBlockFling && !tech.iceIXOnDeath }, - { - name: "microstates", - link: `microstates`, - description: "for each active bullet / bot
+0.7% damage", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.isDamageFromBulletCount = true - }, - remove() { - tech.isDamageFromBulletCount = false - } + requires: "no other mob death tech", + effect() { + tech.botSpawner += 0.33; }, - { - name: "regression", - description: "bullet collisions increase vulnerability to
damage by +5% for mobs (+0.25% for bosses)", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.isLessDamageReduction = true - }, - remove() { - tech.isLessDamageReduction = false - } + remove() { + tech.botSpawner = 0; + } + }, + { + name: "scrap refit", + link: `scrap refit`, + description: "after mobs die
reset scrap bots to 13 seconds of operation", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.botSpawner }, - { - name: "simulated annealing", - description: "+20% damage
–20% fire rate", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - damage: 1.2, - effect() { - tech.damage *= this.damage - tech.slowFire = 1.2 - b.setFireCD(); - }, - remove() { - if (this.count) tech.damage /= this.damage - tech.slowFire = 1; - b.setFireCD(); - } + requires: "scrap bots", + effect() { + tech.isBotSpawnerReset = true; }, - { - name: "heuristics", - description: "+25% fire rate
spawn a gun", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.fireRate *= 0.75 - b.setFireCD(); - powerUps.spawn(m.pos.x, m.pos.y, "gun"); - }, - remove() { - tech.fireRate = 1; - b.setFireCD(); - } + remove() { + tech.isBotSpawnerReset = false; + } + }, + { + name: "nail-bot", + link: `nail-bot`, + description: "a bot fires nails at mobs in line of sight", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { + return true }, - { - name: "anti-shear topology", - link: `anti-shear topology`, - description: "+30% projectile duration", //
drone spore worm flea missile foam wave neutron ice", - maxCount: 3, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed: () => true, - requires: "", - effect() { - tech.isBulletsLastLonger += 0.3 - }, - remove() { - tech.isBulletsLastLonger = 1; - } + requires: "", + effect() { + tech.nailBotCount++; + b.nailBot(); }, - { - name: "fracture analysis", - description: "if a mob is stunned it takes
+400% damage from bullet impacts", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isStunField || tech.oneSuperBall || tech.isCloakStun || tech.isOrbitBotUpgrade || tech.isStun - }, - requires: "a stun effect", - effect() { - tech.isCrit = true; - }, - remove() { - tech.isCrit = false; + remove() { + if (this.count) { + tech.nailBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); } + } + }, + { + name: "nail-bot upgrade", + link: `nail-bot upgrade`, + description: "convert your bots to nail-bots
+500% fire rate and +40% nail velocity", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.nailBotCount > 1 && !b.hasBotUpgrade() }, - { - name: "shear stress", - description: "after mobs die
they release a nail that targets nearby mobs", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.iceIXOnDeath - }, - requires: "no other mob death tech", - effect() { - tech.nailsDeathMob++ - }, - remove() { - tech.nailsDeathMob = 0; + requires: "2 or more nail bots and no other bot upgrade", + effect() { + tech.isNailBotUpgrade = true + b.convertBotsTo("nail-bot") + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'nail') bullet[i].isUpgraded = true } + tech.setBotTechFrequency() + tech.setTechFrequency("nail-bot", 5) }, - { - name: "thermal runaway", - description: "after mobs die
they explode", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.sporesOnDeath && !tech.nailsDeathMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.iceIXOnDeath - }, - requires: "no other mob death tech", - effect() { - tech.isExplodeMob = true; - }, - remove() { - tech.isExplodeMob = false; - } - }, - { - name: "zoospore vector", - link: `zoospore vector`, - descriptionFunction() { - return `after mobs die there is a +10% chance
they grow ${b.guns[6].nameString('s')}` - }, - // description: "after mobs die
they have a +10% chance to grow spores", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.nailsDeathMob && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.iceIXOnDeath - }, - requires: "no other mob death tech", - effect() { - tech.sporesOnDeath += 0.1; - // if (tech.isSporeWorm) { - // for (let i = 0; i < 4; i++) b.worm(m.pos) - // } else { - // for (let i = 0; i < 8; i++) b.spore(m.pos) - // } - }, - remove() { - tech.sporesOnDeath = 0; - } - }, - { - name: "propagator", - description: "after mobs die advance time 0.5 seconds
+60% damage", - maxCount: 3, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed: () => true, - requires: "", - effect() { - tech.deathSkipTime++ - }, - remove() { - tech.deathSkipTime = 0 - } - }, - { - name: "collider", - descriptionFunction() { - return `after mobs die there is a +33% chance
to smash power ups into a different flavor` - }, - maxCount: 3, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed: () => true, - requires: "", - effect() { - tech.collidePowerUps += 0.33333 - }, - remove() { - tech.collidePowerUps = 0 - } - }, - { - name: "bubble fusion", - descriptionFunction() { - return `after destroying a mob's natural shield
spawn 1-2 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.isShieldAmmo = true; - }, - remove() { - tech.isShieldAmmo = false; - } - }, - { - name: "reaction inhibitor", - description: "-12% maximum mob health", //health - maxCount: 3, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true //tech.nailsDeathMob || tech.sporesOnDeath || tech.isExplodeMob || tech.botSpawner || tech.isMobBlockFling || tech.iceIXOnDeath - }, - requires: "", //"any mob death tech", - effect() { - tech.mobSpawnWithHealth++ - mobs.setMobSpawnHealth() - //set all mobs at full health to 0.85 - for (let i = 0; i < mob.length; i++) { - if (mob.health > mobs.mobSpawnWithHealth) mob.health = mobs.mobSpawnWithHealth - } - }, - remove() { - tech.mobSpawnWithHealth = 0 - mobs.setMobSpawnHealth() - } - }, - { - name: "scrap bots", - link: `scrap bots`, - description: "after mobs die you have a +33% chance
to build scrap bots that operate for 13 seconds", - maxCount: 3, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBotTech: true, - allowed() { - return !tech.sporesOnDeath && !tech.nailsDeathMob && !tech.isExplodeMob && !tech.isMobBlockFling && !tech.iceIXOnDeath - }, - requires: "no other mob death tech", - effect() { - tech.botSpawner += 0.33; - }, - remove() { - tech.botSpawner = 0; - } - }, - { - name: "scrap refit", - link: `scrap refit`, - description: "after mobs die
reset scrap bots to 13 seconds of operation", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isBotTech: true, - allowed() { - return tech.botSpawner - }, - requires: "scrap bots", - effect() { - tech.isBotSpawnerReset = true; - }, - remove() { - tech.isBotSpawnerReset = false; - } - }, - { - name: "nail-bot", - link: `nail-bot`, - description: "a bot fires nails at mobs in line of sight", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBot: true, - isBotTech: true, - allowed() { - return true - }, - requires: "", - effect() { - tech.nailBotCount++; - b.nailBot(); - }, - remove() { - if (this.count) { - tech.nailBotCount -= this.count; - b.clearPermanentBots(); - b.respawnBots(); - } - } - }, - { - name: "nail-bot upgrade", - link: `nail-bot upgrade`, - description: "convert your bots to nail-bots
+500% fire rate and +40% nail velocity", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isBotTech: true, - allowed() { - return tech.nailBotCount > 1 && !b.hasBotUpgrade() - }, - requires: "2 or more nail bots and no other bot upgrade", - effect() { - tech.isNailBotUpgrade = true - b.convertBotsTo("nail-bot") + remove() { + if (this.count) { for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'nail') bullet[i].isUpgraded = true + if (bullet[i].botType === 'nail') bullet[i].isUpgraded = false } - tech.setBotTechFrequency() - tech.setTechFrequency("nail-bot", 5) - }, - remove() { - if (this.count) { - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'nail') bullet[i].isUpgraded = false - } - tech.setBotTechFrequency(1) - } - tech.isNailBotUpgrade = false + tech.setBotTechFrequency(1) } + tech.isNailBotUpgrade = false + } + }, + { + name: "foam-bot", + link: `foam-bot`, + description: "a bot fires foam at nearby mobs", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { + return true }, - { - name: "foam-bot", - link: `foam-bot`, - description: "a bot fires foam at nearby mobs", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBot: true, - isBotTech: true, - allowed() { - return true - }, - requires: "", - effect() { - tech.foamBotCount++; - b.foamBot(); - }, - remove() { - if (this.count) { - tech.foamBotCount -= this.count; - b.clearPermanentBots(); - b.respawnBots(); - } + requires: "", + effect() { + tech.foamBotCount++; + b.foamBot(); + }, + remove() { + if (this.count) { + tech.foamBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); } + } + }, + { + name: "foam-bot upgrade", + link: `foam-bot upgrade`, + description: "convert your bots to foam-bots
+300% foam size and fire rate", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.foamBotCount > 1 && !b.hasBotUpgrade() }, - { - name: "foam-bot upgrade", - link: `foam-bot upgrade`, - description: "convert your bots to foam-bots
+300% foam size and fire rate", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isBotTech: true, - allowed() { - return tech.foamBotCount > 1 && !b.hasBotUpgrade() - }, - requires: "2 or more foam bots and no other bot upgrade", - effect() { - tech.isFoamBotUpgrade = true - b.convertBotsTo("foam-bot") + requires: "2 or more foam bots and no other bot upgrade", + effect() { + tech.isFoamBotUpgrade = true + b.convertBotsTo("foam-bot") + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'foam') bullet[i].isUpgraded = true + } + tech.setBotTechFrequency() + tech.setTechFrequency("foam-bot", 5) + }, + remove() { + if (this.count) { for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'foam') bullet[i].isUpgraded = true + if (bullet[i].botType === 'foam') bullet[i].isUpgraded = false } - tech.setBotTechFrequency() - tech.setTechFrequency("foam-bot", 5) - }, - remove() { - if (this.count) { - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'foam') bullet[i].isUpgraded = false - } - tech.setBotTechFrequency(1) - } - tech.isFoamBotUpgrade = false + tech.setBotTechFrequency(1) } + tech.isFoamBotUpgrade = false + } + }, + { + name: "boom-bot", + link: `boom-bot`, + description: "a bot defends the space around you
ignites an explosion after hitting a mob", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { + return true }, - { - name: "boom-bot", - link: `boom-bot`, - description: "a bot defends the space around you
ignites an explosion after hitting a mob", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBot: true, - isBotTech: true, - allowed() { - return true - }, - requires: "", - effect() { - tech.boomBotCount++; - b.boomBot(); - }, - remove() { - if (this.count) { - tech.boomBotCount -= this.count; - b.clearPermanentBots(); - b.respawnBots(); - } + requires: "", + effect() { + tech.boomBotCount++; + b.boomBot(); + }, + remove() { + if (this.count) { + tech.boomBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); } + } + }, + { + name: "boom-bot upgrade", + link: `boom-bot upgrade`, + description: "convert your bots to boom-bots
+300% explosion damage and size", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.boomBotCount > 1 && !b.hasBotUpgrade() }, - { - name: "boom-bot upgrade", - link: `boom-bot upgrade`, - description: "convert your bots to boom-bots
+300% explosion damage and size", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isBotTech: true, - allowed() { - return tech.boomBotCount > 1 && !b.hasBotUpgrade() - }, - requires: "2 or more boom bots and no other bot upgrade", - effect() { - tech.isBoomBotUpgrade = true - b.convertBotsTo("boom-bot") + requires: "2 or more boom bots and no other bot upgrade", + effect() { + tech.isBoomBotUpgrade = true + b.convertBotsTo("boom-bot") + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'boom') bullet[i].isUpgraded = true + } + tech.setBotTechFrequency() + tech.setTechFrequency("boom-bot", 5) + }, + remove() { + if (this.count) { for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'boom') bullet[i].isUpgraded = true + if (bullet[i].botType === 'boom') bullet[i].isUpgraded = false } - tech.setBotTechFrequency() - tech.setTechFrequency("boom-bot", 5) - }, - remove() { - if (this.count) { - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'boom') bullet[i].isUpgraded = false - } - tech.setBotTechFrequency(1) - } - tech.isBoomBotUpgrade = false + tech.setBotTechFrequency(1) } + tech.isBoomBotUpgrade = false + } + }, + { + name: "laser-bot", + link: `laser-bot`, + description: "a bot uses energy to emit a laser beam
that targets nearby mobs", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { + return m.maxEnergy > 0.5 }, - { - name: "laser-bot", - link: `laser-bot`, - description: "a bot uses energy to emit a laser beam
that targets nearby mobs", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBot: true, - isBotTech: true, - allowed() { - return m.maxEnergy > 0.5 - }, - requires: "maximum energy above 50", - effect() { - tech.laserBotCount++; - b.laserBot(); - }, - remove() { - if (this.count) { - tech.laserBotCount -= this.count; - b.clearPermanentBots(); - b.respawnBots(); - } + requires: "maximum energy above 50", + effect() { + tech.laserBotCount++; + b.laserBot(); + }, + remove() { + if (this.count) { + tech.laserBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); } + } + }, + { + name: "laser-bot upgrade", + link: `laser-bot upgrade`, + description: "convert your bots to laser-bots
+100% damage, efficiency, and range", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.laserBotCount > 1 && !b.hasBotUpgrade() }, - { - name: "laser-bot upgrade", - link: `laser-bot upgrade`, - description: "convert your bots to laser-bots
+100% damage, efficiency, and range", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isBotTech: true, - allowed() { - return tech.laserBotCount > 1 && !b.hasBotUpgrade() - }, - requires: "2 or more laser bots and no other bot upgrade", - effect() { - tech.isLaserBotUpgrade = true - b.convertBotsTo("laser-bot") + requires: "2 or more laser bots and no other bot upgrade", + effect() { + tech.isLaserBotUpgrade = true + b.convertBotsTo("laser-bot") + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'laser') bullet[i].isUpgraded = true + } + tech.setBotTechFrequency() + tech.setTechFrequency("laser-bot", 5) + }, + remove() { + if (this.count) { for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'laser') bullet[i].isUpgraded = true + if (bullet[i].botType === 'laser') bullet[i].isUpgraded = false } - tech.setBotTechFrequency() - tech.setTechFrequency("laser-bot", 5) - }, - remove() { - if (this.count) { - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'laser') bullet[i].isUpgraded = false - } - tech.setBotTechFrequency(1) - } - tech.isLaserBotUpgrade = false + tech.setBotTechFrequency(1) } + tech.isLaserBotUpgrade = false + } + }, + { + name: "orbital-bot", + link: `orbital-bot`, + description: "a bot is locked in orbit around you
stuns and damages mobs on contact", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { + return true }, - { - name: "orbital-bot", - link: `orbital-bot`, - description: "a bot is locked in orbit around you
stuns and damages mobs on contact", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBot: true, - isBotTech: true, - allowed() { - return true - }, - requires: "", - effect() { - b.orbitBot(); - tech.orbitBotCount++; - }, - remove() { - if (this.count) { - tech.orbitBotCount -= this.count; - b.clearPermanentBots(); - b.respawnBots(); + requires: "", + effect() { + b.orbitBot(); + tech.orbitBotCount++; + }, + remove() { + if (this.count) { + tech.orbitBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); + } + } + }, + { + name: "orbital-bot upgrade", + link: `orbital-bot upgrade`, + description: "convert your bots to orbital-bots
+300% orbital damage and +50% radius", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.orbitBotCount > 1 && !b.hasBotUpgrade() + }, + requires: "2 or more orbital bots and no other bot upgrade", + effect() { + tech.isOrbitBotUpgrade = true + b.convertBotsTo("orbital-bot") + const range = 190 + 120 * tech.isOrbitBotUpgrade + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'orbit') { + bullet[i].isUpgraded = true + bullet[i].range = range + bullet[i].orbitalSpeed = Math.sqrt(0.25 / range) } } + tech.setBotTechFrequency() + tech.setTechFrequency("orbital-bot", 5) }, - { - name: "orbital-bot upgrade", - link: `orbital-bot upgrade`, - description: "convert your bots to orbital-bots
+300% orbital damage and +50% radius", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isBotTech: true, - allowed() { - return tech.orbitBotCount > 1 && !b.hasBotUpgrade() - }, - requires: "2 or more orbital bots and no other bot upgrade", - effect() { - tech.isOrbitBotUpgrade = true - b.convertBotsTo("orbital-bot") - const range = 190 + 120 * tech.isOrbitBotUpgrade + remove() { + if (this.count) { + const range = 190 + 100 * tech.isOrbitBotUpgrade for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType === 'orbit') { - bullet[i].isUpgraded = true bullet[i].range = range bullet[i].orbitalSpeed = Math.sqrt(0.25 / range) } } - tech.setBotTechFrequency() - tech.setTechFrequency("orbital-bot", 5) - }, - remove() { - if (this.count) { - const range = 190 + 100 * tech.isOrbitBotUpgrade - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'orbit') { - bullet[i].range = range - bullet[i].orbitalSpeed = Math.sqrt(0.25 / range) - } - } - tech.setBotTechFrequency(1) - } - tech.isOrbitBotUpgrade = false + tech.setBotTechFrequency(1) } + tech.isOrbitBotUpgrade = false + } + }, + { + name: "dynamo-bot", + link: `dynamo-bot`, + description: "a bot damages mobs while it traces your path
when it's near generate +7 energy per second", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { + return true }, - { - name: "dynamo-bot", - link: `dynamo-bot`, - description: "a bot damages mobs while it traces your path
when it's near generate +7 energy per second", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBot: true, - isBotTech: true, - allowed() { - return true - }, - requires: "", - effect() { - tech.dynamoBotCount++; - b.dynamoBot(); - }, - remove() { - if (this.count) { - tech.dynamoBotCount -= this.count; - b.clearPermanentBots(); - b.respawnBots(); - } + requires: "", + effect() { + tech.dynamoBotCount++; + b.dynamoBot(); + }, + remove() { + if (this.count) { + tech.dynamoBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); } + } + }, + { + name: "dynamo-bot upgrade", + link: `dynamo-bot upgrade`, + description: "convert your bots to dynamo-bots
when it's near generate +23 energy per second", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.dynamoBotCount > 1 && !b.hasBotUpgrade() }, - { - name: "dynamo-bot upgrade", - link: `dynamo-bot upgrade`, - description: "convert your bots to dynamo-bots
when it's near generate +23 energy per second", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isBotTech: true, - allowed() { - return tech.dynamoBotCount > 1 && !b.hasBotUpgrade() - }, - requires: "2 or more dynamo bots and no other bot upgrade", - effect() { - tech.isDynamoBotUpgrade = true - b.convertBotsTo("dynamo-bot") + requires: "2 or more dynamo bots and no other bot upgrade", + effect() { + tech.isDynamoBotUpgrade = true + b.convertBotsTo("dynamo-bot") + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'dynamo') bullet[i].isUpgraded = true + } + tech.setBotTechFrequency() + tech.setTechFrequency("dynamo-bot", 5) + }, + remove() { + if (this.count) { for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'dynamo') bullet[i].isUpgraded = true + if (bullet[i].botType === 'dynamo') bullet[i].isUpgraded = false } - tech.setBotTechFrequency() - tech.setTechFrequency("dynamo-bot", 5) - }, - remove() { - if (this.count) { - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'dynamo') bullet[i].isUpgraded = false - } - tech.setBotTechFrequency(1) - } - tech.isDynamoBotUpgrade = false + tech.setBotTechFrequency(1) } + tech.isDynamoBotUpgrade = false + } + }, + { + name: "perimeter defense", + description: "for each permanent bot
+6% defense", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBotTech: true, + allowed() { + return b.totalBots() > 1 }, - { - name: "perimeter defense", - description: "for each permanent bot
+6% defense", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - isBotTech: true, - allowed() { - return b.totalBots() > 1 - }, - requires: "at least 2 bots", - effect() { - tech.isBotArmor = true - }, - remove() { - tech.isBotArmor = false - } + requires: "at least 2 bots", + effect() { + tech.isBotArmor = true }, - { - name: "network effect", - description: "for each permanent bot
+6% damage", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - isBotTech: true, - allowed() { - return b.totalBots() > 1 - }, - requires: "at least 2 bots", - effect() { - tech.isBotDamage = true - }, - remove() { - tech.isBotDamage = false - } + remove() { + tech.isBotArmor = false + } + }, + { + name: "network effect", + description: "for each permanent bot
+6% damage", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBotTech: true, + allowed() { + return b.totalBots() > 1 }, - { - name: "bot fabrication", - link: `bot fabrication`, - descriptionFunction() { - return `after you collect ${powerUps.orb.research(2 + Math.floor(0.1666 * b.totalBots()))}use them to build a
random bot (+1 cost every 5 bots)` - }, - // description: `if you collect ${powerUps.orb.research(2)}use them to build a
random bot (+1 cost every 5 bots)`, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - isBotTech: true, - allowed() { - return powerUps.research.count > 1 || build.isExperimentSelection - }, - requires: "at least 2 research", - effect() { - tech.isRerollBots = true; - powerUps.research.changeRerolls(0) - simulation.makeTextLog(`m.research = 0`) - }, - remove() { - tech.isRerollBots = false; - // this.description = `if you collect ${powerUps.orb.research(2 + Math.floor(0.2 * b.totalBots()))}use them to build a
random bot (+1 cost every 5 bots)` - } + requires: "at least 2 bots", + effect() { + tech.isBotDamage = true }, - { - name: "ersatz bots", - link: `ersatz bots`, - description: "double your current permanent bots
remove all guns in your inventory", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - isBotTech: true, - // isNonRefundable: true, - isBadRandomOption: true, - numberOfGunsLost: 0, - allowed() { - return b.totalBots() > 3 && !build.isExperimentSelection - }, - requires: "NOT EXPERIMENT MODE, at least 4 bots", - effect() { - this.numberOfGunsLost = b.inventory.length - b.removeAllGuns(); - simulation.makeGunHUD(); - //double bots - for (let i = 0; i < tech.nailBotCount; i++) b.nailBot(); - tech.nailBotCount *= 2 - for (let i = 0; i < tech.laserBotCount; i++) b.laserBot(); - tech.laserBotCount *= 2 - for (let i = 0; i < tech.foamBotCount; i++) b.foamBot(); - tech.foamBotCount *= 2 - for (let i = 0; i < tech.boomBotCount; i++) b.boomBot(); - tech.boomBotCount *= 2 - for (let i = 0; i < tech.orbitBotCount; i++) b.orbitBot(); - tech.orbitBotCount *= 2 - for (let i = 0; i < tech.dynamoBotCount; i++) b.dynamoBot(); - tech.dynamoBotCount *= 2 - for (let i = 0; i < tech.plasmaBotCount; i++) b.plasmaBot(); - tech.plasmaBotCount *= 2 - for (let i = 0; i < tech.missileBotCount; i++) b.missileBot(); - tech.missileBotCount *= 2 - }, - remove() { - if (this.count) { - //return guns - for (let i = 0; i < this.numberOfGunsLost; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun"); - this.numberOfGunsLost = 0; + remove() { + tech.isBotDamage = false + } + }, + { + name: "bot fabrication", + link: `bot fabrication`, + descriptionFunction() { + return `after you collect ${powerUps.orb.research(2 + Math.floor(0.1666 * b.totalBots()))}use them to build a
random bot (+1 cost every 5 bots)` + }, + // description: `if you collect ${powerUps.orb.research(2)}use them to build a
random bot (+1 cost every 5 bots)`, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBotTech: true, + allowed() { + return powerUps.research.count > 1 || build.isExperimentSelection + }, + requires: "at least 2 research", + effect() { + tech.isRerollBots = true; + powerUps.research.changeRerolls(0) + simulation.makeTextLog(`m.research = 0`) + }, + remove() { + tech.isRerollBots = false; + // this.description = `if you collect ${powerUps.orb.research(2 + Math.floor(0.2 * b.totalBots()))}use them to build a
random bot (+1 cost every 5 bots)` + } + }, + { + name: "ersatz bots", + link: `ersatz bots`, + description: "double your current permanent bots
remove all guns in your inventory", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBotTech: true, + // isNonRefundable: true, + isBadRandomOption: true, + numberOfGunsLost: 0, + allowed() { + return b.totalBots() > 3 && !build.isExperimentSelection + }, + requires: "NOT EXPERIMENT MODE, at least 4 bots", + effect() { + this.numberOfGunsLost = b.inventory.length + b.removeAllGuns(); + simulation.makeGunHUD(); + //double bots + for (let i = 0; i < tech.nailBotCount; i++) b.nailBot(); + tech.nailBotCount *= 2 + for (let i = 0; i < tech.laserBotCount; i++) b.laserBot(); + tech.laserBotCount *= 2 + for (let i = 0; i < tech.foamBotCount; i++) b.foamBot(); + tech.foamBotCount *= 2 + for (let i = 0; i < tech.boomBotCount; i++) b.boomBot(); + tech.boomBotCount *= 2 + for (let i = 0; i < tech.orbitBotCount; i++) b.orbitBot(); + tech.orbitBotCount *= 2 + for (let i = 0; i < tech.dynamoBotCount; i++) b.dynamoBot(); + tech.dynamoBotCount *= 2 + for (let i = 0; i < tech.plasmaBotCount; i++) b.plasmaBot(); + tech.plasmaBotCount *= 2 + for (let i = 0; i < tech.missileBotCount; i++) b.missileBot(); + tech.missileBotCount *= 2 + }, + remove() { + if (this.count) { + //return guns + for (let i = 0; i < this.numberOfGunsLost; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun"); + this.numberOfGunsLost = 0; - //half all current guns - tech.nailBotCount = Math.round(tech.nailBotCount / 2) - tech.laserBotCount = Math.round(tech.laserBotCount / 2) - tech.foamBotCount = Math.round(tech.foamBotCount / 2) - tech.boomBotCount = Math.round(tech.boomBotCount / 2) - tech.orbitBotCount = Math.round(tech.orbitBotCount / 2) - tech.dynamoBotCount = Math.round(tech.dynamoBotCount / 2) - tech.plasmaBotCount = Math.round(tech.plasmaBotCount / 2) - tech.missileBotCount = Math.round(tech.missileBotCount / 2) - b.clearPermanentBots(); - b.respawnBots(); - } + //half all current guns + tech.nailBotCount = Math.round(tech.nailBotCount / 2) + tech.laserBotCount = Math.round(tech.laserBotCount / 2) + tech.foamBotCount = Math.round(tech.foamBotCount / 2) + tech.boomBotCount = Math.round(tech.boomBotCount / 2) + tech.orbitBotCount = Math.round(tech.orbitBotCount / 2) + tech.dynamoBotCount = Math.round(tech.dynamoBotCount / 2) + tech.plasmaBotCount = Math.round(tech.plasmaBotCount / 2) + tech.missileBotCount = Math.round(tech.missileBotCount / 2) + b.clearPermanentBots(); + b.respawnBots(); + } + } + }, + // { + // name: "robotics", + // description: `spawn 2 random bots
quadruple the frequency of finding bot tech`, + // maxCount: 1, + // count: 0, + // frequency: 1, + // frequencyDefault: 1, + // isBotTech: true, + // allowed() { + // return b.totalBots() > 1 || build.isExperimentSelection + // }, + // requires: "at least 2 bots", + // effect: () => { + // b.randomBot() + // b.randomBot() + // for (let i = 0, len = tech.tech.length; i < len; i++) { + // if (tech.tech[i].isBotTech) tech.tech[i].frequency *= 4 + // } + // }, + // remove() { + // if (this.count > 0) { + // b.removeBot() + // b.removeBot() + // b.clearPermanentBots(); + // b.respawnBots(); + // for (let i = 0, len = tech.tech.length; i < len; i++) { + // if (tech.tech[i].isBotTech) tech.tech[i].frequency = Math.ceil(tech.tech[i].frequency / 4) + // } + // } + // } + // }, + { + name: "robotics", + description: `spawn 2 random bots
tech, fields, and guns have +1 bot choice`, //tech have an extra bot tech option + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBotTech: true, + allowed() { + return b.totalBots() > 1 + }, + requires: "at least 2 bots", + effect() { + tech.isExtraBotOption = true + for (let i = 0; i < 2; i++) b.randomBot() + }, + remove() { + if (this.count > 0) { + for (let i = 0; i < 2; i++) b.removeBot() + b.clearPermanentBots(); + b.respawnBots(); + } + tech.isExtraBotOption = false + } + }, + { + name: "open-source", //digital fabricator + description: `spawn 3 random bots
triple the frequency of finding bot tech`, + maxCount: 1, + count: 0, + frequency: 0, + frequencyDefault: 0, + isBotTech: true, + allowed() { + return tech.isExtraBotOption + }, + requires: "robotics", + effect() { + for (let i = 0; i < 3; i++) b.randomBot() + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isBotTech) tech.tech[i].frequency *= 3 } }, - // { - // name: "robotics", - // description: `spawn 2 random bots
quadruple the frequency of finding bot tech`, - // maxCount: 1, - // count: 0, - // frequency: 1, - // frequencyDefault: 1, - // isBotTech: true, - // allowed() { - // return b.totalBots() > 1 || build.isExperimentSelection - // }, - // requires: "at least 2 bots", - // effect: () => { - // b.randomBot() - // b.randomBot() - // for (let i = 0, len = tech.tech.length; i < len; i++) { - // if (tech.tech[i].isBotTech) tech.tech[i].frequency *= 4 - // } - // }, - // remove() { - // if (this.count > 0) { - // b.removeBot() - // b.removeBot() - // b.clearPermanentBots(); - // b.respawnBots(); - // for (let i = 0, len = tech.tech.length; i < len; i++) { - // if (tech.tech[i].isBotTech) tech.tech[i].frequency = Math.ceil(tech.tech[i].frequency / 4) - // } - // } - // } - // }, - { - name: "robotics", - description: `spawn 2 random bots
tech, fields, and guns have +1 bot choice`, //tech have an extra bot tech option - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBotTech: true, - allowed() { - return b.totalBots() > 1 - }, - requires: "at least 2 bots", - effect() { - tech.isExtraBotOption = true - for (let i = 0; i < 2; i++) b.randomBot() - }, - remove() { - if (this.count > 0) { - for (let i = 0; i < 2; i++) b.removeBot() - b.clearPermanentBots(); - b.respawnBots(); - } - tech.isExtraBotOption = false - } - }, - { - name: "open-source", //digital fabricator - description: `spawn 3 random bots
triple the frequency of finding bot tech`, - maxCount: 1, - count: 0, - frequency: 0, - frequencyDefault: 0, - isBotTech: true, - allowed() { - return tech.isExtraBotOption - }, - requires: "robotics", - effect() { - for (let i = 0; i < 3; i++) b.randomBot() + remove() { + if (this.count > 0) { + for (let i = 0; i < 3; i++) b.removeBot() + b.clearPermanentBots(); + b.respawnBots(); for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isBotTech) tech.tech[i].frequency *= 3 + if (tech.tech[i].isBotTech) tech.tech[i].frequency = Math.ceil(tech.tech[i].frequency / 3) } - }, - remove() { - if (this.count > 0) { - for (let i = 0; i < 3; i++) b.removeBot() - b.clearPermanentBots(); - b.respawnBots(); - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isBotTech) tech.tech[i].frequency = Math.ceil(tech.tech[i].frequency / 3) - } + } + } + }, + { + name: "decorrelation", + description: "if your gun or field are unused for 2 seconds
+70% defense", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isRewindField + }, + requires: "not retrocausality", + effect() { + tech.isNoFireDefense = true + }, + remove() { + tech.isNoFireDefense = false + } + }, + { + name: "anticorrelation", + description: "if your gun or field are unused for 2 seconds
+100% damage", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isRewindField + }, + requires: "not retrocausality", + effect() { + tech.isNoFireDamage = true + }, + remove() { + tech.isNoFireDamage = false + } + }, + { + name: "mass driver", + description: "+300% block collision damage", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return m.fieldUpgrades[m.fieldMode].name !== "wormhole" + }, + requires: "not wormhole", + effect() { + tech.blockDamage = 0.3 + }, + remove() { + tech.blockDamage = 0.075 + } + }, + { + name: "inflation", + link: `inflation`, + description: "if holding a block +85% defense
after throwing a block it expands 300%", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.blockDamage > 0.075 && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && m.fieldUpgrades[m.fieldMode].name !== "wormhole" && !tech.isTokamak + }, + requires: "mass driver, not pilot wave, tokamak, wormhole", + effect() { + tech.isAddBlockMass = true + }, + remove() { + tech.isAddBlockMass = false + } + }, + { + name: "restitution", + description: "+150% block collision damage
after throwing a block it becomes very bouncy", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.blockDamage > 0.075 && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && m.fieldUpgrades[m.fieldMode].name !== "wormhole" && !tech.isTokamak + }, + requires: "mass driver, not pilot wave, tokamak, wormhole", + effect() { + tech.isBlockRestitution = true + }, + remove() { + tech.isBlockRestitution = false + } + }, + { + name: "flywheel", + description: "+150% block collision damage
after a mob dies its block is flung at mobs", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.blockDamage > 0.075 && !tech.nailsDeathMob && !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.iceIXOnDeath + }, + requires: "mass driver, no other mob death tech", + effect() { + tech.isMobBlockFling = true + }, + remove() { + tech.isMobBlockFling = false + } + }, + // { + // name: "fermions", + // description: "blocks thrown by you or pilot wave will
collide with intangible mobs, but not you", + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return (tech.blockDamage > 0.075 || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && !tech.isTokamak + // }, + // requires: "mass driver or pilot wave, not tokamak", + // effect() { + // tech.isBlockBullets = true + // }, + // remove() { + // tech.isBlockBullets = false + // } + // }, + { + name: "buckling", + descriptionFunction() { + return `if a block you threw kills a mob
spawn either ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}` + }, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.blockDamage > 0.075 && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && !tech.isTokamak + }, + requires: "mass driver, not pilot wave, tokamak", + effect() { + tech.isBlockPowerUps = true + }, + remove() { + tech.isBlockPowerUps = false + } + }, + { + name: "NOR gate", + description: "if flip-flop is OFF
become invulnerable to your next collision", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isFlipFlop + }, + requires: "flip-flop", + effect() { + tech.isFlipFlopHarm = true //do you have this tech + }, + remove() { + tech.isFlipFlopHarm = false + } + }, + { + name: "shape-memory alloy", + descriptionFunction() { + return `if flip-flop is ON
+400 maximum health and +100% ${powerUps.orb.heal()} effect` + }, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isFlipFlop + }, + requires: "flip-flop", + effect() { + tech.isFlipFlopHealth = true; + m.setMaxHealth(); + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === "heal") { + const oldSize = powerUp[i].size + powerUp[i].size = powerUps.heal.size() //update current heals + const scale = powerUp[i].size / oldSize + Matter.Body.scale(powerUp[i], scale, scale); //grow } } }, - { - name: "decorrelation", - description: "if your gun or field are unused for 2 seconds
+70% defense", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isRewindField - }, - requires: "not retrocausality", - effect() { - tech.isNoFireDefense = true - }, - remove() { - tech.isNoFireDefense = false - } - }, - { - name: "anticorrelation", - description: "if your gun or field are unused for 2 seconds
+100% damage", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isRewindField - }, - requires: "not retrocausality", - effect() { - tech.isNoFireDamage = true - }, - remove() { - tech.isNoFireDamage = false - } - }, - { - name: "mass driver", - description: "+300% block collision damage", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return m.fieldUpgrades[m.fieldMode].name !== "wormhole" - }, - requires: "not wormhole", - effect() { - tech.blockDamage = 0.3 - }, - remove() { - tech.blockDamage = 0.075 - } - }, - { - name: "inflation", - link: `inflation`, - description: "if holding a block +85% defense
after throwing a block it expands 300%", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.blockDamage > 0.075 && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && m.fieldUpgrades[m.fieldMode].name !== "wormhole" && !tech.isTokamak - }, - requires: "mass driver, not pilot wave, tokamak, wormhole", - effect() { - tech.isAddBlockMass = true - }, - remove() { - tech.isAddBlockMass = false - } - }, - { - name: "restitution", - description: "+150% block collision damage
after throwing a block it becomes very bouncy", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.blockDamage > 0.075 && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && m.fieldUpgrades[m.fieldMode].name !== "wormhole" && !tech.isTokamak - }, - requires: "mass driver, not pilot wave, tokamak, wormhole", - effect() { - tech.isBlockRestitution = true - }, - remove() { - tech.isBlockRestitution = false - } - }, - { - name: "flywheel", - description: "+150% block collision damage
after a mob dies its block is flung at mobs", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.blockDamage > 0.075 && !tech.nailsDeathMob && !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.iceIXOnDeath - }, - requires: "mass driver, no other mob death tech", - effect() { - tech.isMobBlockFling = true - }, - remove() { - tech.isMobBlockFling = false - } - }, - // { - // name: "fermions", - // description: "blocks thrown by you or pilot wave will
collide with intangible mobs, but not you", - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return (tech.blockDamage > 0.075 || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && !tech.isTokamak - // }, - // requires: "mass driver or pilot wave, not tokamak", - // effect() { - // tech.isBlockBullets = true - // }, - // remove() { - // tech.isBlockBullets = false - // } - // }, - { - name: "buckling", - descriptionFunction() { - return `if a block you threw kills a mob
spawn either ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}` - }, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.blockDamage > 0.075 && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && !tech.isTokamak - }, - requires: "mass driver, not pilot wave, tokamak", - effect() { - tech.isBlockPowerUps = true - }, - remove() { - tech.isBlockPowerUps = false - } - }, - { - name: "NOR gate", - description: "if flip-flop is OFF
become invulnerable to your next collision", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isFlipFlop - }, - requires: "flip-flop", - effect() { - tech.isFlipFlopHarm = true //do you have this tech - }, - remove() { - tech.isFlipFlopHarm = false - } - }, - { - name: "shape-memory alloy", - descriptionFunction() { - return `if flip-flop is ON
+400 maximum health and +100% ${powerUps.orb.heal()} effect` - }, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isFlipFlop - }, - requires: "flip-flop", - effect() { - tech.isFlipFlopHealth = true; - m.setMaxHealth(); - for (let i = 0; i < powerUp.length; i++) { - if (powerUp[i].name === "heal") { - const oldSize = powerUp[i].size - powerUp[i].size = powerUps.heal.size() //update current heals - const scale = powerUp[i].size / oldSize - Matter.Body.scale(powerUp[i], scale, scale); //grow - } - } - }, - remove() { - tech.isFlipFlopHealth = false; - m.setMaxHealth(); - for (let i = 0; i < powerUp.length; i++) { - if (powerUp[i].name === "heal") { - const oldSize = powerUp[i].size - powerUp[i].size = powerUps.heal.size() //update current heals - const scale = powerUp[i].size / oldSize - Matter.Body.scale(powerUp[i], scale, scale); //grow - } + remove() { + tech.isFlipFlopHealth = false; + m.setMaxHealth(); + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === "heal") { + const oldSize = powerUp[i].size + powerUp[i].size = powerUps.heal.size() //update current heals + const scale = powerUp[i].size / oldSize + Matter.Body.scale(powerUp[i], scale, scale); //grow } } - }, - { - name: "flip-flop", - link: `flip-flop`, - description: `toggle ON and OFF after a collision
unlock advanced tech that runs if ON`, - nameInfo: "", - addNameInfo() { - setTimeout(function() { - if (document.getElementById("tech-flip-flop")) { - if (tech.isFlipFlopOn) { - document.getElementById("tech-flip-flop").innerHTML = ` = ON` - m.eyeFillColor = m.fieldMeterColor //'#5af' - } else { - document.getElementById("tech-flip-flop").innerHTML = ` = OFF` - m.eyeFillColor = "transparent" - } - } - }, 100); - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isRelay - }, - requires: "not relay switch", - effect() { - tech.isFlipFlop = true //do you have this tech? - if (!tech.isFlipFlopOn) { - tech.isFlipFlopOn = true //what is the state of flip-Flop? - if (tech.isFlipFlopCoupling) { - m.couplingChange(5) - for (let i = 0; i < mob.length; i++) { - if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP - } + } + }, + { + name: "flip-flop", + link: `flip-flop`, + description: `toggle ON and OFF after a collision
unlock advanced tech that runs if ON`, + nameInfo: "", + addNameInfo() { + setTimeout(function () { + if (document.getElementById("tech-flip-flop")) { + if (tech.isFlipFlopOn) { + document.getElementById("tech-flip-flop").innerHTML = ` = ON` + m.eyeFillColor = m.fieldMeterColor //'#5af' + } else { + document.getElementById("tech-flip-flop").innerHTML = ` = OFF` + m.eyeFillColor = "transparent" } } - // if (!m.isShipMode) { - // m.skin.flipFlop() - // } - }, - remove() { - tech.isFlipFlop = false - if (tech.isFlipFlopOn) { - tech.isFlipFlopOn = false //what is the state of flip-Flop? - if (tech.isFlipFlopCoupling) { - m.couplingChange(5) - for (let i = 0; i < mob.length; i++) { - if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP - } - } - } - m.eyeFillColor = 'transparent' - // m.resetSkin(); - } + }, 100); }, - { - name: "NAND gate", - description: "if ON
+55.5% damage", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isFlipFlop || tech.isRelay - }, - requires: "ON/OFF tech", - effect() { - tech.isFlipFlopDamage = true; - }, - remove() { - tech.isFlipFlopDamage = false; - } + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isRelay }, - { - name: "integrated circuit", - description: "if ON +7 power up choices
if OFF -1 power up choices", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (tech.isFlipFlop || tech.isRelay) && !tech.isDeterminism - }, - requires: "ON/OFF tech, not determinism", - effect() { - tech.isFlipFlopChoices = true //do you have this tech - }, - remove() { - tech.isFlipFlopChoices = false - } - }, - { - name: "transistor", - description: "if ON generate +20 energy per second
if OFF drain -1 energy per second", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isFlipFlop || tech.isRelay - }, - requires: "ON/OFF tech", - effect() { - tech.isFlipFlopEnergy = true; - }, - remove() { - tech.isFlipFlopEnergy = false; - } - }, - { - name: "decoupling", - link: `decoupling`, - descriptionFunction() { - //(${ m.couplingDescription(this.bonus)}) - return `if ON +5 coupling
if OFF a dangerous particle slowly chases you` - }, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - bonus: 5, //coupling given - allowed() { - return tech.isFlipFlop || tech.isRelay - }, - requires: "ON/OFF tech", - effect() { - tech.isFlipFlopCoupling = true; - if (tech.isFlipFlopOn) { - m.couplingChange(this.bonus) - } else { + requires: "not relay switch", + effect() { + tech.isFlipFlop = true //do you have this tech? + if (!tech.isFlipFlopOn) { + tech.isFlipFlopOn = true //what is the state of flip-Flop? + if (tech.isFlipFlopCoupling) { + m.couplingChange(5) for (let i = 0; i < mob.length; i++) { if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP } - spawn.WIMP() - mob[mob.length - 1].isDecoupling = true //so you can find it to remove } - }, - remove() { - tech.isFlipFlopCoupling = false; - if (this.count) { - if (tech.isFlipFlop || tech.isRelay) { - if (tech.isFlipFlopOn) { - m.couplingChange(-this.bonus) - } else { - for (let i = 0; i < mob.length; i++) { - if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP - } - } + } + // if (!m.isShipMode) { + // m.skin.flipFlop() + // } + }, + remove() { + tech.isFlipFlop = false + if (tech.isFlipFlopOn) { + tech.isFlipFlopOn = false //what is the state of flip-Flop? + if (tech.isFlipFlopCoupling) { + m.couplingChange(5) + for (let i = 0; i < mob.length; i++) { + if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP } } } + m.eyeFillColor = 'transparent' + // m.resetSkin(); + } + }, + { + name: "NAND gate", + description: "if ON
+55.5% damage", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isFlipFlop || tech.isRelay }, - { - name: "relay switch", - description: `toggle ON and OFF after picking up a power up
unlock advanced tech that runs if ON`, - nameInfo: "", - addNameInfo() { - setTimeout(function() { - if (document.getElementById("tech-switch")) { - if (tech.isFlipFlopOn) { - document.getElementById("tech-switch").innerHTML = ` = ON` - m.eyeFillColor = m.fieldMeterColor //'#5af' - } else { - document.getElementById("tech-switch").innerHTML = ` = OFF` - m.eyeFillColor = "transparent" - } - } - }, 100); - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isFlipFlop - }, - requires: "not flip-flop", - effect() { - m.isAltSkin = true - tech.isRelay = true //do you have this tech? - if (!tech.isFlipFlopOn) { - tech.isFlipFlopOn = true //what is the state of flip-Flop? - if (tech.isFlipFlopCoupling) { - m.couplingChange(5) + requires: "ON/OFF tech", + effect() { + tech.isFlipFlopDamage = true; + }, + remove() { + tech.isFlipFlopDamage = false; + } + }, + { + name: "integrated circuit", + description: "if ON +7 power up choices
if OFF -1 power up choices", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (tech.isFlipFlop || tech.isRelay) && !tech.isDeterminism + }, + requires: "ON/OFF tech, not determinism", + effect() { + tech.isFlipFlopChoices = true //do you have this tech + }, + remove() { + tech.isFlipFlopChoices = false + } + }, + { + name: "transistor", + description: "if ON generate +20 energy per second
if OFF drain -1 energy per second", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isFlipFlop || tech.isRelay + }, + requires: "ON/OFF tech", + effect() { + tech.isFlipFlopEnergy = true; + }, + remove() { + tech.isFlipFlopEnergy = false; + } + }, + { + name: "decoupling", + link: `decoupling`, + descriptionFunction() { + //(${ m.couplingDescription(this.bonus)}) + return `if ON +5 coupling
if OFF a dangerous particle slowly chases you` + }, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + bonus: 5, //coupling given + allowed() { + return tech.isFlipFlop || tech.isRelay + }, + requires: "ON/OFF tech", + effect() { + tech.isFlipFlopCoupling = true; + if (tech.isFlipFlopOn) { + m.couplingChange(this.bonus) + } else { + for (let i = 0; i < mob.length; i++) { + if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP + } + spawn.WIMP() + mob[mob.length - 1].isDecoupling = true //so you can find it to remove + } + }, + remove() { + tech.isFlipFlopCoupling = false; + if (this.count) { + if (tech.isFlipFlop || tech.isRelay) { + if (tech.isFlipFlopOn) { + m.couplingChange(-this.bonus) + } else { for (let i = 0; i < mob.length; i++) { if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP } } } - // if (!m.isShipMode) { - // m.skin.flipFlop() - // } - }, - remove() { - tech.isRelay = false - if (tech.isFlipFlopOn) { - tech.isFlipFlopOn = false //what is the state of flip-Flop? - if (tech.isFlipFlopCoupling) { - m.couplingChange(-5) - for (let i = 0; i < mob.length; i++) { - if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP - } + } + } + }, + { + name: "relay switch", + description: `toggle ON and OFF after picking up a power up
unlock advanced tech that runs if ON`, + nameInfo: "", + addNameInfo() { + setTimeout(function () { + if (document.getElementById("tech-switch")) { + if (tech.isFlipFlopOn) { + document.getElementById("tech-switch").innerHTML = ` = ON` + m.eyeFillColor = m.fieldMeterColor //'#5af' + } else { + document.getElementById("tech-switch").innerHTML = ` = OFF` + m.eyeFillColor = "transparent" } } - m.eyeFillColor = 'transparent' - // m.resetSkin(); - } + }, 100); }, - { - name: "lithium-ion", - description: "if relay switch is ON
+300 maximum energy", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isRelay - }, - requires: "relay switch", - effect() { - tech.isRelayEnergy = true - m.setMaxEnergy() - }, - remove() { - tech.isRelayEnergy = false - m.setMaxEnergy() - } + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isFlipFlop }, - { - name: "thermocouple", - description: "if relay switch is ON
condense 4-13 ice IX crystals per second", - maxCount: 9, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isRelay - }, - requires: "relay switch", - effect() { - tech.relayIce++ - }, - remove() { - tech.relayIce = 0 - } - }, - { - name: "first derivative", - descriptionFunction() { - return `while your first gun is equipped
+15% defense per gun (${(100*(1-0.85 ** b.inventory.length)).toFixed(0)}%)` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.isFirstDer = true - }, - remove() { - tech.isFirstDer = false; - } - }, - { - name: "MACHO", - description: "a massive but compact object slowly follows you
if you are inside the MACHO +60% defense", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.isMACHO = true; //this harm reduction comes from the particle toggling tech.isHarmMACHO - spawn.MACHO() - }, - remove() { - tech.isMACHO = false; - tech.isHarmMACHO = false; - for (let i = 0, len = mob.length; i < len; i++) { - if (mob[i].isMACHO) mob[i].alive = false; + requires: "not flip-flop", + effect() { + m.isAltSkin = true + tech.isRelay = true //do you have this tech? + if (!tech.isFlipFlopOn) { + tech.isFlipFlopOn = true //what is the state of flip-Flop? + if (tech.isFlipFlopCoupling) { + m.couplingChange(5) + for (let i = 0; i < mob.length; i++) { + if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP + } } } + // if (!m.isShipMode) { + // m.skin.flipFlop() + // } }, - { - name: "axion", - description: "while inside the MACHO
defense increases damage", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isMACHO - }, - requires: "MACHO", - effect() { - tech.isAxion = true - }, - remove() { - tech.isAxion = false + remove() { + tech.isRelay = false + if (tech.isFlipFlopOn) { + tech.isFlipFlopOn = false //what is the state of flip-Flop? + if (tech.isFlipFlopCoupling) { + m.couplingChange(-5) + for (let i = 0; i < mob.length; i++) { + if (mob[i].isDecoupling) mob[i].alive = false //remove WIMP + } + } } + m.eyeFillColor = 'transparent' + // m.resetSkin(); + } + }, + { + name: "lithium-ion", + description: "if relay switch is ON
+300 maximum energy", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isRelay }, - { - name: "ablative drones", - description: "after losing health there is a chance
to rebuild your broken parts as drones", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed: () => true, - requires: "", - effect() { - tech.isDroneOnDamage = true; - // for (let i = 0; i < 4; i++) b.drone() - }, - remove() { - tech.isDroneOnDamage = false; + requires: "relay switch", + effect() { + tech.isRelayEnergy = true + m.setMaxEnergy() + }, + remove() { + tech.isRelayEnergy = false + m.setMaxEnergy() + } + }, + { + name: "thermocouple", + description: "if relay switch is ON
condense 4-13 ice IX crystals per second", + maxCount: 9, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isRelay + }, + requires: "relay switch", + effect() { + tech.relayIce++ + }, + remove() { + tech.relayIce = 0 + } + }, + { + name: "first derivative", + descriptionFunction() { + return `while your first gun is equipped
+15% defense per gun (${(100 * (1 - 0.85 ** b.inventory.length)).toFixed(0)}%)` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isFirstDer = true + }, + remove() { + tech.isFirstDer = false; + } + }, + { + name: "MACHO", + description: "a massive but compact object slowly follows you
if you are inside the MACHO +60% defense", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isMACHO = true; //this harm reduction comes from the particle toggling tech.isHarmMACHO + spawn.MACHO() + }, + remove() { + tech.isMACHO = false; + tech.isHarmMACHO = false; + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].isMACHO) mob[i].alive = false; } + } + }, + { + name: "axion", + description: "while inside the MACHO
defense increases damage", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isMACHO }, - { - name: "non-Newtonian armor", - link: `non-Newtonian armor`, - description: "after mob collisions
+66% defense for 10 seconds", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.isHarmArmor = true; - }, - remove() { - tech.isHarmArmor = false; - } + requires: "MACHO", + effect() { + tech.isAxion = true }, - { - name: "Pauli exclusion", - description: `after mob collisions
become invulnerable for +3 seconds`, - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - m.collisionImmuneCycles += 180; - if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage - }, - remove() { - m.collisionImmuneCycles = 30; - } + remove() { + tech.isAxion = false + } + }, + { + name: "ablative drones", + description: "after losing health there is a chance
to rebuild your broken parts as drones", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + effect() { + tech.isDroneOnDamage = true; + // for (let i = 0; i < 4; i++) b.drone() }, - { - name: "spin–statistics theorem", - description: `every 7 seconds
become invulnerable for +1.8 seconds`, - maxCount: 3, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true //m.collisionImmuneCycles > 30 - }, - requires: "", - effect() { - tech.cyclicImmunity += 108; - }, - remove() { - tech.cyclicImmunity = 0; - } + remove() { + tech.isDroneOnDamage = false; + } + }, + { + name: "non-Newtonian armor", + link: `non-Newtonian armor`, + description: "after mob collisions
+66% defense for 10 seconds", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true }, - { - name: "liquid cooling", - description: `after losing health
freeze all mobs for 7 seconds`, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isSlowFPS - }, - requires: "clock gating", - effect() { - tech.isHarmFreeze = true; - }, - remove() { - tech.isHarmFreeze = false; - } + requires: "", + effect() { + tech.isHarmArmor = true; }, - { - name: "clock gating", - description: `after losing health slow time by 50%
+20% defense`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return simulation.fpsCapDefault > 45 - }, - requires: "FPS above 45", - effect() { - tech.isSlowFPS = true; - }, - remove() { - tech.isSlowFPS = false; - } + remove() { + tech.isHarmArmor = false; + } + }, + { + name: "Pauli exclusion", + description: `after mob collisions
become invulnerable for +3 seconds`, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true }, + requires: "", + effect() { + m.collisionImmuneCycles += 180; + if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage + }, + remove() { + m.collisionImmuneCycles = 30; + } + }, + { + name: "spin–statistics theorem", + description: `every 7 seconds
become invulnerable for +1.8 seconds`, + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true //m.collisionImmuneCycles > 30 + }, + requires: "", + effect() { + tech.cyclicImmunity += 108; + }, + remove() { + tech.cyclicImmunity = 0; + } + }, + { + name: "liquid cooling", + description: `after losing health
freeze all mobs for 7 seconds`, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isSlowFPS + }, + requires: "clock gating", + effect() { + tech.isHarmFreeze = true; + }, + remove() { + tech.isHarmFreeze = false; + } + }, + { + name: "clock gating", + description: `after losing health slow time by 50%
+20% defense`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return simulation.fpsCapDefault > 45 + }, + requires: "FPS above 45", + effect() { + tech.isSlowFPS = true; + }, + remove() { + tech.isSlowFPS = false; + } + }, - { - name: "piezoelectricity", - description: "if you collide with a mob
generate +2048 energy", //
reduce defense by 15% - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isEnergyHealth - }, - requires: "not mass-energy", - effect() { - tech.isPiezo = true; - // if (simulation.isTextLogOpen) m.energy += 20.48; - }, - remove() { - tech.isPiezo = false; + { + name: "piezoelectricity", + description: "if you collide with a mob
generate +2048 energy", //
reduce defense by 15% + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyHealth + }, + requires: "not mass-energy", + effect() { + tech.isPiezo = true; + // if (simulation.isTextLogOpen) m.energy += 20.48; + }, + remove() { + tech.isPiezo = false; + } + }, + { + name: "electronegativity", + descriptionFunction() { + return `+0.1% damage per current stored energy
(+${(10 * m.energy).toFixed(0)}%)` + }, + // description: "+1% damage per 8 stored energy", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.energyDamage++ + }, + remove() { + tech.energyDamage = 0; + } + }, + { + name: "ground state", + description: "+200 maximum energy
–40% passive energy generation", + // description: "reduce defense by 66%
you no longer passively regenerate energy", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isTimeCrystals + }, + requires: "not time crystals", + effect() { + tech.isGroundState = true + m.setFieldRegen() + m.setMaxEnergy() + }, + remove() { + tech.isGroundState = false + m.setFieldRegen() + m.setMaxEnergy() + } + }, + { + name: "heat engine", + description: `+50% damage
–50 maximum energy`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "not CPT", + damage: 1.5, + effect() { + tech.damage *= this.damage + tech.isMaxEnergyTech = true; + m.setMaxEnergy() + }, + remove() { + if (this.count) tech.damage /= this.damage + tech.isMaxEnergyTech = false; + m.setMaxEnergy() + } + }, + { + name: "exothermic process", + description: "+50% damage
after mobs die –20% energy", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + damage: 1.55, + effect() { + tech.damage *= this.damage + tech.isEnergyLoss = true; + }, + remove() { + if (this.count) tech.damage /= this.damage + tech.isEnergyLoss = false; + } + }, + { + name: "Gibbs free energy", + description: `for each energy below 100
+0.7% damage`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + effect() { + tech.isLowEnergyDamage = true; + }, + remove() { + tech.isLowEnergyDamage = false; + } + }, + { + name: "overcharge", + description: "+66 maximum energy
+6% JUNK to tech pool", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.bonusEnergy += 0.66 + m.setMaxEnergy() + this.refundAmount += tech.addJunkTechToPool(0.06) + }, + refundAmount: 0, + remove() { + tech.bonusEnergy = 0; + m.setMaxEnergy() + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "Maxwells demon", + description: "energy above your max decays 96% slower
+5% JUNK to tech pool", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.energy > m.maxEnergy || build.isExperimentSelection + }, + requires: "energy above your max", + effect() { + tech.overfillDrain = 0.94 //70% = 1-(1-0.75)/(1-0.15) //92% = 1-(1-0.75)/(1-0.87) + this.refundAmount += tech.addJunkTechToPool(0.05) + }, + refundAmount: 0, + remove() { + tech.overfillDrain = 0.7 + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "inductive charging", + description: "if crouched +600% passive energy generation
if not crouched energy generation is disabled", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isDamageAfterKillNoRegen + }, + requires: "not parasitism", + effect() { + tech.isCrouchRegen = true; //only used to check for requirements + m.regenEnergy = function () { + if (m.immuneCycle < m.cycle && m.crouch) m.energy += 7 * m.fieldRegen; + if (m.energy < 0) m.energy = 0 } }, - { - name: "electronegativity", - descriptionFunction() { - return `+0.1% damage per current stored energy
(+${(10*m.energy).toFixed(0)}%)` - }, - // description: "+1% damage per 8 stored energy", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.energyDamage++ - }, - remove() { - tech.energyDamage = 0; + remove() { + tech.isCrouchRegen = false; + m.regenEnergy = m.regenEnergyDefault + } + }, + { + name: "energy conservation", + description: "4% of damage done recovered as energy", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.energySiphon += 0.04; + }, + remove() { + tech.energySiphon = 0; + } + }, + { + name: "parasitism", + description: "if a mob has died in the last 5 seconds
+60% damage, inhibit energy generation", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isCrouchRegen + }, + requires: "not inductive charging", + effect() { + tech.isDamageAfterKillNoRegen = true; + m.regenEnergy = function () { + if (m.immuneCycle < m.cycle && (m.lastKillCycle + 300 < m.cycle)) m.energy += m.fieldRegen; + if (m.energy < 0) m.energy = 0 } }, - { - name: "ground state", - description: "+200 maximum energy
–40% passive energy generation", - // description: "reduce defense by 66%
you no longer passively regenerate energy", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isTimeCrystals - }, - requires: "not time crystals", - effect() { - tech.isGroundState = true - m.setFieldRegen() - m.setMaxEnergy() - }, - remove() { - tech.isGroundState = false - m.setFieldRegen() - m.setMaxEnergy() - } + remove() { + if (this.count) m.regenEnergy = m.regenEnergyDefault + tech.isDamageAfterKillNoRegen = false; + } + }, + { + name: "waste heat recovery", + description: "if a mob has died in the last 5 seconds
generate 5% of max energy per second", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true }, - { - name: "heat engine", - description: `+50% damage
–50 maximum energy`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed: () => true, - requires: "not CPT", - damage: 1.5, - effect() { - tech.damage *= this.damage - tech.isMaxEnergyTech = true; - m.setMaxEnergy() - }, - remove() { - if (this.count) tech.damage /= this.damage - tech.isMaxEnergyTech = false; - m.setMaxEnergy() - } + requires: "", + effect() { + tech.isEnergyRecovery = true; }, - { - name: "exothermic process", - description: "+50% damage
after mobs die –20% energy", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - damage: 1.55, - effect() { - tech.damage *= this.damage - tech.isEnergyLoss = true; - }, - remove() { - if (this.count) tech.damage /= this.damage - tech.isEnergyLoss = false; - } + remove() { + tech.isEnergyRecovery = false; + } + }, + { + name: "recycling", + description: "if a mob has died in the last 5 seconds
recover 0.5% of max health per second", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isHealTech: true, + allowed() { + return true }, - { - name: "Gibbs free energy", - description: `for each energy below 100
+0.7% damage`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed: () => true, - requires: "", - effect() { - tech.isLowEnergyDamage = true; - }, - remove() { - tech.isLowEnergyDamage = false; - } + requires: "", + effect() { + tech.isHealthRecovery = true; }, - { - name: "overcharge", - description: "+66 maximum energy
+6% JUNK to tech pool", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.bonusEnergy += 0.66 - m.setMaxEnergy() - this.refundAmount += tech.addJunkTechToPool(0.06) - }, - refundAmount: 0, - remove() { - tech.bonusEnergy = 0; - m.setMaxEnergy() - if (this.count > 0 && this.refundAmount > 0) { - tech.removeJunkTechFromPool(this.refundAmount) - this.refundAmount = 0 + remove() { + tech.isHealthRecovery = false; + } + }, + { + name: "torpor", + description: "if a mob has not died in the last 5 seconds
+66% defense", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isHarmReduceNoKill = true; + }, + remove() { + tech.isHarmReduceNoKill = false; + } + }, + { + name: "homeostasis", + descriptionFunction() { + return `for each health below 100
+0.8% defense (${(100 * (Math.max(0, 1 - m.health) * 0.8)).toFixed(0)}%)` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return m.health < 0.6 || build.isExperimentSelection + }, + requires: "health below 60", + effect() { + tech.isLowHealthDefense = true; + }, + remove() { + tech.isLowHealthDefense = false; + } + }, + { + name: "negative feedback", + descriptionFunction() { + return `for each ${tech.isEnergyHealth ? "energy" : "health"} below 100
+0.7% damage (${(70 * Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health))).toFixed(0)}%)` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return m.health < 0.6 || build.isExperimentSelection + }, + requires: "health below 60", + effect() { + tech.isLowHealthDmg = true; //used in mob.damage() + }, + remove() { + tech.isLowHealthDmg = false; + } + }, + { + name: "Zenos paradox", + descriptionFunction() { + return `+85% defense
–5% of current ${tech.isEnergyHealth ? "energy" : "health"} every 5 seconds` + }, + // description: "+85% defense
–5% of current health every 5 seconds", + // description: "every 5 seconds remove 1/10 of your health
reduce defense by 90%", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isZeno = true; + }, + remove() { + tech.isZeno = false; + } + }, + { + name: "antiscience", + descriptionFunction() { + return `+66% damage
–10 ${tech.isEnergyHealth ? "energy" : "health"} after picking up a tech` + }, + // description: "+66% damage
–10 health after picking up a tech", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + damage: 1.66, + effect() { + tech.damage *= this.damage + tech.isTechDamage = true; + }, + remove() { + if (this.count) tech.damage /= this.damage + tech.isTechDamage = false; + } + }, + { + name: "ergodicity", + descriptionFunction() { + return `${powerUps.orb.heal()} have -50% effect
+66% damage` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + damage: 1.66, + effect() { + tech.damage *= this.damage + tech.isHalfHeals = true; + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === "heal") { + const scale = Math.sqrt(0.5) + powerUp[i].size *= scale + Matter.Body.scale(powerUp[i], scale, scale); //grow } } }, - { - name: "Maxwells demon", - description: "energy above your max decays 96% slower
+5% JUNK to tech pool", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.energy > m.maxEnergy || build.isExperimentSelection - }, - requires: "energy above your max", - effect() { - tech.overfillDrain = 0.94 //70% = 1-(1-0.75)/(1-0.15) //92% = 1-(1-0.75)/(1-0.87) - this.refundAmount += tech.addJunkTechToPool(0.05) - }, - refundAmount: 0, - remove() { - tech.overfillDrain = 0.7 - if (this.count > 0 && this.refundAmount > 0) { - tech.removeJunkTechFromPool(this.refundAmount) - this.refundAmount = 0 - } - } - }, - { - name: "inductive charging", - description: "if crouched +600% passive energy generation
if not crouched energy generation is disabled", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isDamageAfterKillNoRegen - }, - requires: "not parasitism", - effect() { - tech.isCrouchRegen = true; //only used to check for requirements - m.regenEnergy = function() { - if (m.immuneCycle < m.cycle && m.crouch) m.energy += 7 * m.fieldRegen; - if (m.energy < 0) m.energy = 0 - } - }, - remove() { - tech.isCrouchRegen = false; - m.regenEnergy = m.regenEnergyDefault - } - }, - { - name: "energy conservation", - description: "4% of damage done recovered as energy", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.energySiphon += 0.04; - }, - remove() { - tech.energySiphon = 0; - } - }, - { - name: "parasitism", - description: "if a mob has died in the last 5 seconds
+60% damage, inhibit energy generation", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isCrouchRegen - }, - requires: "not inductive charging", - effect() { - tech.isDamageAfterKillNoRegen = true; - m.regenEnergy = function() { - if (m.immuneCycle < m.cycle && (m.lastKillCycle + 300 < m.cycle)) m.energy += m.fieldRegen; - if (m.energy < 0) m.energy = 0 - } - }, - remove() { - if (this.count) m.regenEnergy = m.regenEnergyDefault - tech.isDamageAfterKillNoRegen = false; - } - }, - { - name: "waste heat recovery", - description: "if a mob has died in the last 5 seconds
generate 5% of max energy per second", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.isEnergyRecovery = true; - }, - remove() { - tech.isEnergyRecovery = false; - } - }, - { - name: "recycling", - description: "if a mob has died in the last 5 seconds
recover 0.5% of max health per second", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isHealTech: true, - allowed() { - return true - }, - requires: "", - effect() { - tech.isHealthRecovery = true; - }, - remove() { - tech.isHealthRecovery = false; - } - }, - { - name: "torpor", - description: "if a mob has not died in the last 5 seconds
+66% defense", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.isHarmReduceNoKill = true; - }, - remove() { - tech.isHarmReduceNoKill = false; - } - }, - { - name: "homeostasis", - descriptionFunction() { - return `for each health below 100
+0.8% defense (${(100*(Math.max(0, 1 - m.health) * 0.8)).toFixed(0)}%)` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return m.health < 0.6 || build.isExperimentSelection - }, - requires: "health below 60", - effect() { - tech.isLowHealthDefense = true; - }, - remove() { - tech.isLowHealthDefense = false; - } - }, - { - name: "negative feedback", - descriptionFunction() { - return `for each ${tech.isEnergyHealth ? "energy": "health"} below 100
+0.7% damage (${(70*Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health))).toFixed(0)}%)` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return m.health < 0.6 || build.isExperimentSelection - }, - requires: "health below 60", - effect() { - tech.isLowHealthDmg = true; //used in mob.damage() - }, - remove() { - tech.isLowHealthDmg = false; - } - }, - { - name: "Zenos paradox", - descriptionFunction() { - return `+85% defense
–5% of current ${tech.isEnergyHealth ? "energy": "health"} every 5 seconds` - }, - // description: "+85% defense
–5% of current health every 5 seconds", - // description: "every 5 seconds remove 1/10 of your health
reduce defense by 90%", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.isZeno = true; - }, - remove() { - tech.isZeno = false; - } - }, - { - name: "antiscience", - descriptionFunction() { - return `+66% damage
–10 ${tech.isEnergyHealth ? "energy": "health"} after picking up a tech` - }, - // description: "+66% damage
–10 health after picking up a tech", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - damage: 1.66, - effect() { - tech.damage *= this.damage - tech.isTechDamage = true; - }, - remove() { - if (this.count) tech.damage /= this.damage - tech.isTechDamage = false; - } - }, - { - name: "ergodicity", - descriptionFunction() { - return `${powerUps.orb.heal()} have -50% effect
+66% damage` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - damage: 1.66, - effect() { - tech.damage *= this.damage - tech.isHalfHeals = true; + remove() { + if (this.count) { + tech.damage /= this.damage for (let i = 0; i < powerUp.length; i++) { if (powerUp[i].name === "heal") { - const scale = Math.sqrt(0.5) + const scale = 1 / Math.sqrt(0.5) powerUp[i].size *= scale Matter.Body.scale(powerUp[i], scale, scale); //grow } } - }, - remove() { - if (this.count) { - tech.damage /= this.damage - for (let i = 0; i < powerUp.length; i++) { - if (powerUp[i].name === "heal") { - const scale = 1 / Math.sqrt(0.5) - powerUp[i].size *= scale - Matter.Body.scale(powerUp[i], scale, scale); //grow - } - } - } - tech.isHalfHeals = false; } + tech.isHalfHeals = false; + } + }, + { + name: "fluoroantimonic acid", + description: "if your health is above 100
+35% damage", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.maxHealth > 1; }, - { - name: "fluoroantimonic acid", - description: "if your health is above 100
+35% damage", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.maxHealth > 1; - }, - requires: "max health above 100", - effect() { - tech.isAcidDmg = true; - }, - remove() { - tech.isAcidDmg = false; - } + requires: "max health above 100", + effect() { + tech.isAcidDmg = true; }, + remove() { + tech.isAcidDmg = false; + } + }, - { - name: "adiabatic healing", - descriptionFunction() { - return `${powerUps.orb.heal()} have +100% effect
+5% JUNK to tech pool` - }, - maxCount: 3, - count: 0, - frequency: 1, - frequencyDefault: 1, - isHealTech: true, - allowed() { - return (m.health / m.maxHealth) < 0.7 || build.isExperimentSelection - }, - requires: "under 70% health", - effect() { - tech.largerHeals++; - for (let i = 0; i < powerUp.length; i++) { - if (powerUp[i].name === "heal") { - const oldSize = powerUp[i].size - powerUp[i].size = powerUps.heal.size() //update current heals - const scale = powerUp[i].size / oldSize - Matter.Body.scale(powerUp[i], scale, scale); //grow - } - } - this.refundAmount += tech.addJunkTechToPool(0.05) - }, - refundAmount: 0, - remove() { - tech.largerHeals = 1; - for (let i = 0; i < powerUp.length; i++) { - if (powerUp[i].name === "heal") { - const oldSize = powerUp[i].size - powerUp[i].size = powerUps.heal.size() //update current heals - const scale = powerUp[i].size / oldSize - Matter.Body.scale(powerUp[i], scale, scale); //grow - } - } - if (this.count > 0 && this.refundAmount > 0) { - tech.removeJunkTechFromPool(this.refundAmount) - this.refundAmount = 0 + { + name: "adiabatic healing", + descriptionFunction() { + return `${powerUps.orb.heal()} have +100% effect
+5% JUNK to tech pool` + }, + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + isHealTech: true, + allowed() { + return (m.health / m.maxHealth) < 0.7 || build.isExperimentSelection + }, + requires: "under 70% health", + effect() { + tech.largerHeals++; + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === "heal") { + const oldSize = powerUp[i].size + powerUp[i].size = powerUps.heal.size() //update current heals + const scale = powerUp[i].size / oldSize + Matter.Body.scale(powerUp[i], scale, scale); //grow } } + this.refundAmount += tech.addJunkTechToPool(0.05) }, - { - name: "quenching", - descriptionFunction() { - return `after over healing from ${powerUps.orb.heal()}
gain max health and lose current health` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.isOverHeal = true; - }, - remove() { - tech.isOverHeal = false; - } - }, - { - name: "negative entropy", - descriptionFunction() { - return `at the start of each level
for every 33% missing ${tech.isEnergyHealth ? "energy": "health"} spawn ${powerUps.orb.heal()}` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isHealTech: true, - allowed() { - return true - }, - requires: "", - effect() { - tech.isHealLowHealth = true; - }, - remove() { - tech.isHealLowHealth = false; - } - }, - { - name: "enthalpy", - descriptionFunction() { - return `doing damage has a small chance to spawn ${powerUps.orb.heal(1)}` //
–10% defense - }, - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - isHealTech: true, - allowed() { - return true - }, - requires: "", - effect() { - tech.healthDrain += 0.019; - }, - remove() { - tech.healthDrain = 0; - } - }, - { - name: "maintenance", - descriptionFunction() { - return `
double
the frequency of finding healing tech
spawn ${powerUps.orb.heal(13)}` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - isBadRandomOption: true, - allowed() { - return true - }, - requires: "", - effect() { - for (let i = 0; i < 13; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "heal"); - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isHealTech) tech.tech[i].frequency *= 2 + refundAmount: 0, + remove() { + tech.largerHeals = 1; + for (let i = 0; i < powerUp.length; i++) { + if (powerUp[i].name === "heal") { + const oldSize = powerUp[i].size + powerUp[i].size = powerUps.heal.size() //update current heals + const scale = powerUp[i].size / oldSize + Matter.Body.scale(powerUp[i], scale, scale); //grow } - }, - remove() {} + } + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "quenching", + descriptionFunction() { + return `after over healing from ${powerUps.orb.heal()}
gain max health and lose current health` }, - { - name: "anthropic principle", - nameInfo: "", - addNameInfo() { - setTimeout(function() { - powerUps.research.changeRerolls(0) - }, 1000); - }, - descriptionFunction() { - return `once per level, instead of dying
use ${powerUps.orb.research(1)} and spawn ${powerUps.orb.heal(5)}` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isHealTech: true, - allowed() { - return powerUps.research.count > 0 || build.isExperimentSelection - }, - requires: "at least 1 research", - effect() { - tech.isDeathAvoid = true; - tech.isDeathAvoidedThisLevel = false; - setTimeout(function() { - powerUps.research.changeRerolls(0) - }, 1000); - }, - remove() { - tech.isDeathAvoid = false; + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isOverHeal = true; + }, + remove() { + tech.isOverHeal = false; + } + }, + { + name: "negative entropy", + descriptionFunction() { + return `at the start of each level
for every 33% missing ${tech.isEnergyHealth ? "energy" : "health"} spawn ${powerUps.orb.heal()}` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isHealTech: true, + allowed() { + return true + }, + requires: "", + effect() { + tech.isHealLowHealth = true; + }, + remove() { + tech.isHealLowHealth = false; + } + }, + { + name: "enthalpy", + descriptionFunction() { + return `doing damage has a small chance to spawn ${powerUps.orb.heal(1)}` //
–10% defense + }, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isHealTech: true, + allowed() { + return true + }, + requires: "", + effect() { + tech.healthDrain += 0.019; + }, + remove() { + tech.healthDrain = 0; + } + }, + { + name: "maintenance", + descriptionFunction() { + return `
double the frequency of finding healing tech
spawn ${powerUps.orb.heal(13)}` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0; i < 13; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "heal"); + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isHealTech) tech.tech[i].frequency *= 2 } }, - { - name: "weak anthropic principle", - description: "after anthropic principle prevents your death
+50% duplication chance for that level", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isDeathAvoid - }, - requires: "anthropic principle", - effect() { - tech.isAnthropicTech = true - powerUps.setDupChance(); //needed after adjusting duplication chance - }, - remove() { - tech.isAnthropicTech = false - powerUps.setDupChance(); //needed after adjusting duplication chance - } + remove() { } + }, + { + name: "anthropic principle", + nameInfo: "", + addNameInfo() { + setTimeout(function () { + powerUps.research.changeRerolls(0) + }, 1000); }, - { - name: "strong anthropic principle", - description: "after anthropic principle prevents your death
+137.03599% damage for that level", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isDeathAvoid - }, - requires: "anthropic principle", - effect() { - tech.isAnthropicDamage = true - }, - remove() { - tech.isAnthropicDamage = false - } + descriptionFunction() { + return `once per level, instead of dying
use ${powerUps.orb.research(1)} and spawn ${powerUps.orb.heal(5)}` }, - { - name: "quantum immortality", - description: "+33% defense
after dying, continue in an alternate reality", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.isImmortal = true; - }, - remove() { - tech.isImmortal = false; - } + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isHealTech: true, + allowed() { + return powerUps.research.count > 0 || build.isExperimentSelection }, - { - name: "Hilbert space", - description: "+91% damage
after a collision enter an alternate reality", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isResearchReality && !tech.isSwitchReality - }, - requires: "not Ψ(t) collapse, many-worlds", - damage: 1.91, - effect() { - tech.damage *= this.damage - tech.isCollisionRealitySwitch = true; - }, - remove() { - if (this.count) tech.damage /= this.damage - tech.isCollisionRealitySwitch = false; - } + requires: "at least 1 research", + effect() { + tech.isDeathAvoid = true; + tech.isDeathAvoidedThisLevel = false; + setTimeout(function () { + powerUps.research.changeRerolls(0) + }, 1000); }, - { - name: "many-worlds", - // description: "each level is an alternate reality, where you
find a tech at the start of each level", - description: `on each new level spawn a tech power up
and enter an alternate reality`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isResearchReality && !tech.isCollisionRealitySwitch - }, - requires: "not Ψ(t) collapse, Hilbert space", - effect() { - tech.isSwitchReality = true; - }, - remove() { - tech.isSwitchReality = false; - } + remove() { + tech.isDeathAvoid = false; + } + }, + { + name: "weak anthropic principle", + description: "after anthropic principle prevents your death
+50% duplication chance for that level", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isDeathAvoid }, - { - name: "Ψ(t) collapse", - link: `Ψ(t) collapse`, - description: `spawn ${powerUps.orb.research(16)}
after you research enter an alternate reality`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isSwitchReality && !tech.isCollisionRealitySwitch && !tech.isJunkResearch - }, - requires: "not many-worlds, Hilbert space, pseudoscience", - bonusResearch: 16, - effect() { - tech.isResearchReality = true; - for (let i = 0; i < this.bonusResearch; i++) powerUps.spawn(m.pos.x + Math.random() * 60, m.pos.y + Math.random() * 60, "research", false); - }, - remove() { - tech.isResearchReality = false; - if (this.count > 0) powerUps.research.changeRerolls(-this.bonusResearch) - } + requires: "anthropic principle", + effect() { + tech.isAnthropicTech = true + powerUps.setDupChance(); //needed after adjusting duplication chance }, - { - name: "decoherence", - description: `tech options you don't choose won't reoccur
spawn ${powerUps.orb.research(6)}`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isSuperDeterminism - }, - requires: "not superdeterminism", - bonusResearch: 6, - effect() { - tech.isBanish = true - for (let i = 0; i < this.bonusResearch; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); - }, - remove() { - if (tech.isBanish) { - tech.isBanish = false - //reset banish list - for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].isBanished) tech.tech[i].isBanished = false - } - powerUps.research.changeRerolls(-this.bonusResearch) - } + remove() { + tech.isAnthropicTech = false + powerUps.setDupChance(); //needed after adjusting duplication chance + } + }, + { + name: "strong anthropic principle", + description: "after anthropic principle prevents your death
+137.03599% damage for that level", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isDeathAvoid + }, + requires: "anthropic principle", + effect() { + tech.isAnthropicDamage = true + }, + remove() { + tech.isAnthropicDamage = false + } + }, + { + name: "quantum immortality", + description: "+33% defense
after dying, continue in an alternate reality", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isImmortal = true; + }, + remove() { + tech.isImmortal = false; + } + }, + { + name: "Hilbert space", + description: "+91% damage
after a collision enter an alternate reality", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isResearchReality && !tech.isSwitchReality + }, + requires: "not Ψ(t) collapse, many-worlds", + damage: 1.91, + effect() { + tech.damage *= this.damage + tech.isCollisionRealitySwitch = true; + }, + remove() { + if (this.count) tech.damage /= this.damage + tech.isCollisionRealitySwitch = false; + } + }, + { + name: "many-worlds", + // description: "each level is an alternate reality, where you
find a tech at the start of each level", + description: `on each new level spawn a tech power up
and enter an alternate reality`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isResearchReality && !tech.isCollisionRealitySwitch + }, + requires: "not Ψ(t) collapse, Hilbert space", + effect() { + tech.isSwitchReality = true; + }, + remove() { + tech.isSwitchReality = false; + } + }, + { + name: "Ψ(t) collapse", + link: `Ψ(t) collapse`, + description: `spawn ${powerUps.orb.research(16)}
after you research enter an alternate reality`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSwitchReality && !tech.isCollisionRealitySwitch && !tech.isJunkResearch + }, + requires: "not many-worlds, Hilbert space, pseudoscience", + bonusResearch: 16, + effect() { + tech.isResearchReality = true; + for (let i = 0; i < this.bonusResearch; i++) powerUps.spawn(m.pos.x + Math.random() * 60, m.pos.y + Math.random() * 60, "research", false); + }, + remove() { + tech.isResearchReality = false; + if (this.count > 0) powerUps.research.changeRerolls(-this.bonusResearch) + } + }, + { + name: "decoherence", + description: `tech options you don't choose won't reoccur
spawn ${powerUps.orb.research(6)}`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSuperDeterminism + }, + requires: "not superdeterminism", + bonusResearch: 6, + effect() { + tech.isBanish = true + for (let i = 0; i < this.bonusResearch; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); + }, + remove() { + if (tech.isBanish) { tech.isBanish = false - } - }, - { - name: "renormalization", - description: `44% chance to spawn ${powerUps.orb.research(1)}
after consuming ${powerUps.orb.research(1)}`, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (powerUps.research.count > 3 || build.isExperimentSelection) && !tech.isSuperDeterminism - }, - requires: "at least 4 research, not superdeterminism", - effect() { - tech.renormalization = true; - }, - remove() { - tech.renormalization = false; - } - }, - { - name: "perturbation theory", - description: `if you have no ${powerUps.orb.research(1)} in your inventory
+60% fire rate`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return powerUps.research.count === 0 - }, - requires: "no research", - effect() { - tech.isRerollHaste = true; - tech.researchHaste = 0.4; - b.setFireCD(); - }, - remove() { - tech.isRerollHaste = false; - tech.researchHaste = 1; - b.setFireCD(); - } - }, - { - name: "ansatz", - description: `after choosing a field, tech, or gun
if you have no ${powerUps.orb.research(1)} in your inventory spawn ${powerUps.orb.research(2)}`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return powerUps.research.count < 1 && !tech.isSuperDeterminism && !tech.isRerollHaste && !tech.isResearchReality - }, - requires: "no research, not superdeterminism, Ψ(t) collapse, perturbation theory", - effect() { - tech.isAnsatz = true; - }, - remove() { - tech.isAnsatz = false; - } - }, - { - name: "Bayesian statistics", - // description: `for each ${powerUps.orb.research(1)} in your inventory
+3.8% damage`, - descriptionFunction() { - return `spawn ${powerUps.orb.research(this.bonusResearch)}
+3% damage per ${powerUps.orb.research(1)} (${(3*powerUps.research.count).toFixed(0)}%)` - }, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return powerUps.research.count > 2 || build.isExperimentSelection - }, - requires: "at least 3 research", - bonusResearch: 3, - effect() { - powerUps.spawnDelay("research", this.bonusResearch) - tech.isRerollDamage = true; - }, - remove() { - tech.isRerollDamage = false; - if (this.count) { - powerUps.research.changeRerolls(-this.bonusResearch) - } - } - }, - { - name: "pseudoscience", - description: "when selecting a power up, research 3 times
for free, but add 1-4% JUNK to the tech pool", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isResearchReality && !tech.isSuperDeterminism //tech.isResearchBoss || tech.isMetaAnalysis || tech.isRerollBots || tech.isDeathAvoid || tech.isRerollDamage || build.isExperimentSelection - }, - requires: "not Ψ(t) collapse, superdeterminism", //"abiogenesis, meta-analysis, bot fabrication, anthropic principle, or Bayesian statistics, not Ψ(t) collapse", - effect() { - tech.isJunkResearch = true; - }, - remove() { - tech.isJunkResearch = false; - } - }, - { - name: "brainstorming", - description: "tech choices randomize
every 2 seconds for 10 seconds", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isSuperDeterminism - }, - requires: "not superdeterminism", - effect() { - tech.isBrainstorm = true - tech.isBrainstormActive = false - tech.brainStormDelay = 2000 - simulation.difficultyMode * 100 - }, - remove() { - tech.isBrainstorm = false - tech.isBrainstormActive = false - } - }, - { - name: "cross-disciplinary", - description: "tech have an extra field or gun choice
+5% chance to duplicate spawned power ups", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isDeterminism - }, - requires: "not determinism", - effect() { - tech.isExtraGunField = true; - }, - remove() { - tech.isExtraGunField = false; - } - }, - { - name: "emergence", - description: "tech, fields, and guns have +1 choice
+8% damage", - // description: "tech, fields, and guns have +2 choices
+3% JUNK to tech pool", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isDeterminism - }, - requires: "not determinism", - damage: 1.08, - effect() { - tech.extraChoices += 1; - tech.damage *= this.damage - // this.refundAmount += tech.addJunkTechToPool(0.03) - }, - refundAmount: 0, - remove() { - tech.extraChoices = 0; - if (this.count > 0) { - tech.damage /= this.damage - // if (this.refundAmount > 0) tech.removeJunkTechFromPool(this.refundAmount) - } - } - }, - { - name: "path integral", - link: `path integral`, - description: "your next tech choice has all possible options
+5% JUNK to tech pool", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - // isJunk: true, - allowed() { - return !tech.isDeterminism && !tech.isBrainstorm - }, - requires: "not determinism, brainstorm", - effect() { - tech.tooManyTechChoices = 1 - // for (let i = 0; i < this.bonusResearch; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); - this.refundAmount += tech.addJunkTechToPool(0.05) - }, - refundAmount: 0, - remove() { - tech.tooManyTechChoices = 0 - if (this.count > 0 && this.refundAmount > 0) { - tech.removeJunkTechFromPool(this.refundAmount) - this.refundAmount = 0 - } - } - }, - { - name: "determinism", - description: "spawn 5 tech
only 1 choice for tech, fields, and guns", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBadRandomOption: true, - isNonRefundable: true, - allowed() { - return !tech.extraChoices && !tech.isExtraGunField && !tech.isFlipFlopChoices - }, - requires: "not emergence, cross-disciplinary, integrated circuit", - effect() { - tech.isDeterminism = true; - //if you change the number spawned also change it in Born rule - for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech"); - }, - remove() { - if (!this.count) tech.isDeterminism = false; - } - }, - { - name: "superdeterminism", - description: `spawn 5 tech
you can't cancel and ${powerUps.orb.research(1)} no longer spawn`, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isBadRandomOption: true, - isNonRefundable: true, - allowed() { - return tech.isDeterminism && !tech.isAnsatz && !tech.isJunkResearch && !tech.isBrainstorm - }, - requires: "determinism, not ansatz, pseudoscience, brainstorming", - effect() { - tech.isSuperDeterminism = true; - //if you change the number spawned also change it in Born rule - for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech"); - }, - remove() { - tech.isSuperDeterminism = false; - } - }, - { - name: "technical debt", - descriptionFunction() { - return `+300% damage –15% damage
for each tech you have learned (${(Math.floor(100*(Math.min(Math.pow(0.85, tech.totalCount-20), 4 - 0.15 * tech.totalCount)))-100)}%)` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - tech.isTechDebt = true; - }, - remove() { - tech.isTechDebt = false; - } - }, - { - name: "abiogenesis", - // description: `use ${powerUps.orb.research(4)}(or 49% JUNK to the tech pool if you can't) to add a 2nd boss to each level`, - description: `as a level begins spawn a 2nd boss using ${powerUps.orb.research(4)}
(+49% JUNK to the tech pool if you can't pay)
`, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (build.isExperimentSelection || powerUps.research.count > 3) && !tech.isDuplicateBoss - }, - requires: "at least 4 research, not parthenogenesis", - effect() { - tech.isResearchBoss = true; - }, - remove() { - tech.isResearchBoss = false; - } - }, - { - name: "meta-analysis", - description: `if you choose a JUNKtech you instead get a
random normal tech and spawn ${powerUps.orb.research(2)}`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.junkCount > 0 - }, - requires: "some JUNK tech", - effect() { - tech.isMetaAnalysis = true - }, - remove() { - tech.isMetaAnalysis = false - } - }, - { - name: "dark patterns", - description: "+15% damage
+15% JUNK to tech pool", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - damage: 1.15, - effect() { - tech.damage *= this.damage - this.refundAmount += tech.addJunkTechToPool(0.15) - }, - refundAmount: 0, - remove() { - if (this.count > 0) { - tech.damage /= this.damage - if (this.refundAmount > 0) tech.removeJunkTechFromPool(this.refundAmount) - } - } - }, - { - name: "exciton", - descriptionFunction() { - return `after mobs die they have a 16% chance to
spawn ${powerUps.orb.boost(1)} that give +${(powerUps.boost.damage*100).toFixed(0)}% damage for ${(powerUps.boost.duration/60).toFixed(0)} seconds
` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed: () => true, - requires: "", - effect() { - tech.isBoostPowerUps = true - }, - remove() { - tech.isBoostPowerUps = false - } - }, - { - name: "band gap", - descriptionFunction() { - return `${powerUps.orb.boost(1)} give +77% damage
but their duration is reduced by 1 second` - }, - maxCount: 9, - count: 1, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isBoostPowerUps || tech.isBoostReplaceAmmo - }, - requires: "exciton, quasiparticles", - effect() { - powerUps.boost.duration -= 60 - powerUps.boost.damage += 0.77 - }, - remove() { - powerUps.boost.duration = 600 - powerUps.boost.damage = 1.25 - } - }, - { - name: "eternalism", - description: "+30% damage
time can't be paused (time can be dilated)", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isPauseSwitchField && !tech.isPauseEjectTech && !tech.isWormHolePause - }, - requires: "not unified field theory, paradigm shift, invariant", - damage: 1.3, - effect() { - tech.damage *= this.damage - tech.isNoDraftPause = true - }, - remove() { - if (this.count) tech.damage /= this.damage - tech.isNoDraftPause = false - } - }, - { - name: "paradigm shift", - description: `clicking tech while paused ejects them
20% chance to remove without ejecting`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isSuperDeterminism && !tech.isNoDraftPause - }, - requires: "not superdeterminism, eternalism", - effect() { - tech.isPauseEjectTech = true; - }, - remove() { - tech.isPauseEjectTech = false; - } - }, - { - name: "unified field theory", - description: `clicking the field box when paused cycles your field
double the frequency of finding fieldtech
`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isSuperDeterminism && !tech.isNoDraftPause - }, - requires: "not superdeterminism, eternalism", - effect() { - tech.isPauseSwitchField = true; - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isFieldTech) tech.tech[i].frequency *= 2 - } - }, - remove() { - tech.isPauseSwitchField = false; - if (this.count > 1) { - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isFieldTech) tech.tech[i].frequency /= 2 - } - } - } - }, - { - name: "field coupling", - descriptionFunction() { - return `spawn ${powerUps.orb.coupling(10)}
that each give +0.1 coupling
${ m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"}` - }, - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true - }, - requires: "", - effect() { - powerUps.spawnDelay("coupling", 10) - }, - remove() { - if (this.count) { - m.couplingChange(-this.count) - } - } - }, - { - name: "quintessence", - descriptionFunction() { - let converted = powerUps.research.count * this.couplingToResearch * 10 - if (this.count) converted = this.researchUsed * this.couplingToResearch * 10 - - let orbText - if (converted > 15) { - orbText = `${converted} ${powerUps.orb.coupling()}` - } else { - orbText = powerUps.orb.coupling(converted) - } - return `use all your ${powerUps.orb.research(1)} to spawn ${orbText}
that each give +0.1 coupling
${ m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"}` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return powerUps.research.count > 3 - }, - requires: "", - researchUsed: 0, - couplingToResearch: 0.25, - effect() { - // let count = 0 - // while (powerUps.research.count > 0 && powerUps.research.count !== Infinity) { - // powerUps.research.changeRerolls(-1) - // count += 2.5 - // this.researchUsed++ - // } - // powerUps.spawnDelay("coupling", Math.floor(count)) - - let cycle = () => { - if (powerUps.research.count > 0 && powerUps.research.count !== Infinity) { - if (m.alive) requestAnimationFrame(cycle); - if (!simulation.paused && !simulation.isChoosing) { //&& !(simulation.cycle % 2) - powerUps.research.changeRerolls(-1) - this.researchUsed++ - powerUps.spawn(m.pos.x + 50 * (Math.random() - 0.5), m.pos.y + 50 * (Math.random() - 0.5), "coupling"); - } - } else { //exit delay loop - } - } - requestAnimationFrame(cycle); - }, - remove() { - if (this.count) { - m.couplingChange(-this.researchUsed * this.couplingToResearch) - powerUps.research.changeRerolls(this.researchUsed) - this.researchUsed = 0 - } - } - }, - { - name: "virtual particles", - descriptionFunction() { - return `after mobs die they have a 17% chance to
spawn ${powerUps.orb.coupling(1)} that each give +0.1 coupling
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"}` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed: () => true, - requires: "", - effect() { - tech.isCouplingPowerUps = true //about 20-30 mobs per level so at 16% and 0.1 coupling that's about 25 * 0.16 * 0.1 = 0.4 coupling per level with out duplication - }, - remove() { - tech.isCouplingPowerUps = false - } - }, - { - name: "fine-structure constant", - descriptionFunction() { - return `spawn ${this.value} ${powerUps.orb.coupling(1)} that each give +0.1 coupling
-0.5 coupling after mob collisions
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"}` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - allowed: () => true, - requires: "", - value: 60, - effect() { - tech.isCouplingNoHit = true - powerUps.spawnDelay("coupling", this.value) - }, - remove() { - if (this.count) { - m.couplingChange(-this.value) - } - tech.isCouplingNoHit = false - } - }, - { - name: "residual dipolar coupling", - descriptionFunction() { - return `clicking cancel for a field, tech, or gun
spawns ${powerUps.orb.coupling(5)}that each give +0.1 coupling
${ m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"}` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isSuperDeterminism - }, - requires: "not superdeterminism", - effect() { - tech.isCancelCouple = true - }, - remove() { - tech.isCancelCouple = false - } - }, - { - name: "commodities exchange", - descriptionFunction() { - return `clicking cancel for a field, tech, or gun
spawns 5-10 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isSuperDeterminism - }, - requires: "not superdeterminism", - effect() { - tech.isCancelRerolls = true - }, - remove() { - tech.isCancelRerolls = false - } - }, - { - name: "options exchange", - link: `options exchange`, - description: `clicking cancel for a field, tech, or gun
has a 85% chance to randomize choices`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isSuperDeterminism //&& (tech.isCancelRerolls || tech.isCancelDuplication) - }, - requires: "not superdeterminism", //futures exchange, commodities exchange, - effect() { - tech.isCancelTech = true - }, - remove() { - tech.isCancelTech = false - } - }, - { - name: "futures exchange", - description: "clicking cancel for a field, tech, or gun
gives +4.3% power up duplication chance", - // descriptionFunction() { - // return `clicking × to cancel a field, tech, or gun
gives +${4.9 - 0.15*simulation.difficultyMode}% power up duplication chance` - // }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.duplicationChance() < 1 && !tech.isSuperDeterminism - }, - requires: "below 100% duplication chance, not superdeterminism", - effect() { - tech.isCancelDuplication = true //search for tech.cancelCount to balance - powerUps.setDupChance(); //needed after adjusting duplication chance - }, - remove() { - tech.isCancelDuplication = false - powerUps.setDupChance(); //needed after adjusting duplication chance - } - }, - { - name: "replication", - description: "+10% chance to duplicate spawned power ups
+33% JUNK to tech pool", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.duplicationChance() < 1. - }, - requires: "below 100% duplication chance", - effect() { - tech.duplicateChance += 0.1 - powerUps.setDupChance(); //needed after adjusting duplication chance - if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.11); - this.refundAmount += tech.addJunkTechToPool(0.33) - }, - refundAmount: 0, - remove() { - tech.duplicateChance = 0 - powerUps.setDupChance(); //needed after adjusting duplication chance - if (this.count > 0 && this.refundAmount > 0) { - tech.removeJunkTechFromPool(this.refundAmount) - this.refundAmount = 0 - } - } - }, - { - name: "stimulated emission", - description: "+15% chance to duplicate spawned power ups,
but after a collision eject 1 tech", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.duplicationChance() < 1 - }, - requires: "below 1% duplication chance", - effect() { - tech.isStimulatedEmission = true - powerUps.setDupChance(); //needed after adjusting duplication chance - if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.15); - }, - remove() { - tech.isStimulatedEmission = false - powerUps.setDupChance(); //needed after adjusting duplication chance - } - }, - { - name: "metastability", - description: "+12% chance to duplicate spawned power ups
duplicates explode with a 3 second half-life", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.duplicationChance() < 1 - }, - requires: "below 100% duplication chance", - effect() { - tech.isPowerUpsVanish = true - powerUps.setDupChance(); //needed after adjusting duplication chance - if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.11); - }, - remove() { - tech.isPowerUpsVanish = false - powerUps.setDupChance(); //needed after adjusting duplication chance - } - }, - { - name: "correlated damage", - description: "duplication increases damage", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.duplicationChance() > 0.15 - }, - requires: "duplication chance > 15%", - effect() { - tech.isDupDamage = true; - }, - remove() { - tech.isDupDamage = false; - } - }, - { - name: "parthenogenesis", - description: "your duplication has a chance to
duplicate mobs and bosses", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.duplicationChance() > 0 && !tech.isResearchBoss - }, - requires: "some duplication chance, not abiogenesis", - effect() { - tech.isDuplicateBoss = true; - }, - remove() { - tech.isDuplicateBoss = false; - } - }, - { - name: "apomixis", - description: `when you reach 100% duplication
spawn 11 bosses with 100% more durability`, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isNonRefundable: true, - allowed() { - return tech.duplicationChance() > 0.5 - }, - requires: "duplication chance above 50%", - effect() { - tech.is100Duplicate = true; - tech.maxDuplicationEvent() - }, - remove() { - tech.is100Duplicate = false; - } - }, - { - name: "Born rule", - description: "remove all current tech
spawn new tech to replace them", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - isBadRandomOption: true, - allowed() { - return (tech.totalCount > 6) - }, - requires: "more than 6 tech", - effect() { - //remove active bullets //to get rid of bots - for (let i = 0; i < bullet.length; ++i) Matter.Composite.remove(engine.world, bullet[i]); - bullet = []; - let count = 1 //count tech - for (let i = 0, len = tech.tech.length; i < len; i++) { // spawn new tech power ups - if (!tech.tech[i].isNonRefundable) count += tech.tech[i].count - } - if (tech.isDeterminism) count -= 4 //remove the bonus tech - if (tech.isSuperDeterminism) count -= 4 //remove the bonus tech - - tech.setupAllTech(); // remove all tech - if (simulation.isCheating) tech.setCheating(); - lore.techCount = 0; - // tech.addLoreTechToPool(); - for (let i = 0; i < count; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "tech"); // spawn new tech power ups - //have state is checked in m.death() - }, - remove() {} - }, - - { - name: "Occams razor", - descriptionFunction() { - return `randomly remove half your tech
for each removed +${this.damagePerRemoved * 100 }% damage (~${(this.count === 0) ? this.damagePerRemoved * 50 * tech.totalCount : this.damage*100}%)` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - isBadRandomOption: true, - allowed() { - return (tech.totalCount > 6) - }, - requires: "more than 6 tech", - // removePercent: 0.5, - damagePerRemoved: 0.5, - damage: null, - effect() { - let pool = [] - for (let i = 0, len = tech.tech.length; i < len; i++) { // spawn new tech power ups - if (tech.tech[i].count && !tech.tech[i].isNonRefundable && !tech.tech[i].isFromAppliedScience) pool.push(i) - } - pool = shuffle(pool); //shuffles order of maps - let removeCount = 0 - for (let i = 0, len = pool.length * this.damagePerRemoved; i < len; i++) removeCount += tech.removeTech(pool[i]) - this.damage = 1 + this.damagePerRemoved * removeCount - tech.damage *= this.damage - }, - remove() { - if (this.count) tech.damage /= this.damage - } - }, - { - name: "exchange symmetry", - description: "remove 1 random tech
spawn 2 new guns", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - isBadRandomOption: true, - allowed() { - return (tech.totalCount > 3) && !tech.isSuperDeterminism - }, - requires: "at least 4 tech, not superdeterminism", - effect() { - const have = [] //find which tech you have + //reset banish list for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) have.push(i) + if (tech.tech[i].isBanished) tech.tech[i].isBanished = false } - const choose = have[Math.floor(Math.random() * have.length)] - simulation.makeTextLog(`tech.removeTech("${tech.tech[choose].name}")`, 360) - for (let i = 0; i < tech.tech[choose].count; i++) { - powerUps.spawn(m.pos.x, m.pos.y, "gun"); - } - powerUps.spawn(m.pos.x, m.pos.y, "gun"); - // powerUps.spawn(m.pos.x, m.pos.y, "gun"); - tech.tech[choose].count = 0; - tech.tech[choose].remove(); // remove a random tech form the list of tech you have - tech.tech[choose].isLost = true - simulation.updateTechHUD(); - }, - remove() {} + powerUps.research.changeRerolls(-this.bonusResearch) + } + tech.isBanish = false + } + }, + { + name: "renormalization", + description: `44% chance to spawn ${powerUps.orb.research(1)}
after consuming ${powerUps.orb.research(1)}`, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (powerUps.research.count > 3 || build.isExperimentSelection) && !tech.isSuperDeterminism }, - { - name: "Monte Carlo method", - description: "remove 1 random tech
spawn 2 tech", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - isBadRandomOption: true, - allowed() { - return (tech.totalCount > 3) && tech.duplicationChance() > 0 && !tech.isSuperDeterminism - }, - requires: "some duplication, at least 4 tech, not superdeterminism", - effect() { - const removeTotal = tech.removeTech() - for (let i = 0; i < removeTotal + 1; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech"); - }, - remove() {} + requires: "at least 4 research, not superdeterminism", + effect() { + tech.renormalization = true; }, - { - name: "strange attractor", - descriptionFunction() { - return `use ${powerUps.orb.research(2)} to spawn 1 tech with
double your duplication chance (${(2*tech.duplicationChance()*100).toFixed(0)}%)` - }, - // description: `use ${powerUps.orb.research(2)} to spawn 1 tech with double
your duplication chance (${(2*tech.duplicationChance()*100).toFixed(0)}%)`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - isBadRandomOption: true, - allowed() { - return !tech.isSuperDeterminism && tech.duplicationChance() > 0 && powerUps.research.count > 1 - }, - requires: "some duplication, not superdeterminism", - effect() { - powerUps.research.changeRerolls(-2) - simulation.makeTextLog(`m.research -= 2`) - powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); - if (Math.random() < tech.duplicationChance() * 2) powerUps.directSpawn(m.pos.x + 10, m.pos.y + 5, "tech"); - }, - remove() {} + remove() { + tech.renormalization = false; + } + }, + { + name: "perturbation theory", + description: `if you have no ${powerUps.orb.research(1)} in your inventory
+60% fire rate`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return powerUps.research.count === 0 }, - { - name: "reinforcement learning", - description: "increase the frequency of finding copies of
your current tech by 1000%", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.totalCount > 9 - }, - requires: "at least 10 tech", - effect() { + requires: "no research", + effect() { + tech.isRerollHaste = true; + tech.researchHaste = 0.4; + b.setFireCD(); + }, + remove() { + tech.isRerollHaste = false; + tech.researchHaste = 1; + b.setFireCD(); + } + }, + { + name: "ansatz", + description: `after choosing a field, tech, or gun
if you have no ${powerUps.orb.research(1)} in your inventory spawn ${powerUps.orb.research(2)}`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return powerUps.research.count < 1 && !tech.isSuperDeterminism && !tech.isRerollHaste && !tech.isResearchReality + }, + requires: "no research, not superdeterminism, Ψ(t) collapse, perturbation theory", + effect() { + tech.isAnsatz = true; + }, + remove() { + tech.isAnsatz = false; + } + }, + { + name: "Bayesian statistics", + // description: `for each ${powerUps.orb.research(1)} in your inventory
+3.8% damage`, + descriptionFunction() { + return `spawn ${powerUps.orb.research(this.bonusResearch)}
+3% damage per ${powerUps.orb.research(1)} (${(3 * powerUps.research.count).toFixed(0)}%)` + }, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return powerUps.research.count > 2 || build.isExperimentSelection + }, + requires: "at least 3 research", + bonusResearch: 3, + effect() { + powerUps.spawnDelay("research", this.bonusResearch) + tech.isRerollDamage = true; + }, + remove() { + tech.isRerollDamage = false; + if (this.count) { + powerUps.research.changeRerolls(-this.bonusResearch) + } + } + }, + { + name: "pseudoscience", + description: "when selecting a power up, research 3 times
for free, but add 1-4% JUNK to the tech pool", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isResearchReality && !tech.isSuperDeterminism //tech.isResearchBoss || tech.isMetaAnalysis || tech.isRerollBots || tech.isDeathAvoid || tech.isRerollDamage || build.isExperimentSelection + }, + requires: "not Ψ(t) collapse, superdeterminism", //"abiogenesis, meta-analysis, bot fabrication, anthropic principle, or Bayesian statistics, not Ψ(t) collapse", + effect() { + tech.isJunkResearch = true; + }, + remove() { + tech.isJunkResearch = false; + } + }, + { + name: "brainstorming", + description: "tech choices randomize
every 2 seconds for 10 seconds", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSuperDeterminism + }, + requires: "not superdeterminism", + effect() { + tech.isBrainstorm = true + tech.isBrainstormActive = false + tech.brainStormDelay = 2000 - simulation.difficultyMode * 100 + }, + remove() { + tech.isBrainstorm = false + tech.isBrainstormActive = false + } + }, + { + name: "cross-disciplinary", + description: "tech have an extra field or gun choice
+5% chance to duplicate spawned power ups", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isDeterminism + }, + requires: "not determinism", + effect() { + tech.isExtraGunField = true; + }, + remove() { + tech.isExtraGunField = false; + } + }, + { + name: "emergence", + description: "tech, fields, and guns have +1 choice
+8% damage", + // description: "tech, fields, and guns have +2 choices
+3% JUNK to tech pool", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isDeterminism + }, + requires: "not determinism", + damage: 1.08, + effect() { + tech.extraChoices += 1; + tech.damage *= this.damage + // this.refundAmount += tech.addJunkTechToPool(0.03) + }, + refundAmount: 0, + remove() { + tech.extraChoices = 0; + if (this.count > 0) { + tech.damage /= this.damage + // if (this.refundAmount > 0) tech.removeJunkTechFromPool(this.refundAmount) + } + } + }, + { + name: "path integral", + link: `path integral`, + description: "your next tech choice has all possible options
+5% JUNK to tech pool", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + // isJunk: true, + allowed() { + return !tech.isDeterminism && !tech.isBrainstorm + }, + requires: "not determinism, brainstorm", + effect() { + tech.tooManyTechChoices = 1 + // for (let i = 0; i < this.bonusResearch; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); + this.refundAmount += tech.addJunkTechToPool(0.05) + }, + refundAmount: 0, + remove() { + tech.tooManyTechChoices = 0 + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "determinism", + description: "spawn 5 tech
only 1 choice for tech, fields, and guns", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBadRandomOption: true, + isNonRefundable: true, + allowed() { + return !tech.extraChoices && !tech.isExtraGunField && !tech.isFlipFlopChoices + }, + requires: "not emergence, cross-disciplinary, integrated circuit", + effect() { + tech.isDeterminism = true; + //if you change the number spawned also change it in Born rule + for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech"); + }, + remove() { + if (!this.count) tech.isDeterminism = false; + } + }, + { + name: "superdeterminism", + description: `spawn 5 tech
you can't cancel and ${powerUps.orb.research(1)} no longer spawn`, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBadRandomOption: true, + isNonRefundable: true, + allowed() { + return tech.isDeterminism && !tech.isAnsatz && !tech.isJunkResearch && !tech.isBrainstorm + }, + requires: "determinism, not ansatz, pseudoscience, brainstorming", + effect() { + tech.isSuperDeterminism = true; + //if you change the number spawned also change it in Born rule + for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech"); + }, + remove() { + tech.isSuperDeterminism = false; + } + }, + { + name: "technical debt", + descriptionFunction() { + return `+300% damage –15% damage
for each tech you have learned (${(Math.floor(100 * (Math.min(Math.pow(0.85, tech.totalCount - 20), 4 - 0.15 * tech.totalCount))) - 100)}%)` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + tech.isTechDebt = true; + }, + remove() { + tech.isTechDebt = false; + } + }, + { + name: "abiogenesis", + // description: `use ${powerUps.orb.research(4)}(or 49% JUNK to the tech pool if you can't) to add a 2nd boss to each level`, + description: `as a level begins spawn a 2nd boss using ${powerUps.orb.research(4)}
(+49% JUNK to the tech pool if you can't pay)
`, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (build.isExperimentSelection || powerUps.research.count > 3) && !tech.isDuplicateBoss + }, + requires: "at least 4 research, not parthenogenesis", + effect() { + tech.isResearchBoss = true; + }, + remove() { + tech.isResearchBoss = false; + } + }, + { + name: "meta-analysis", + description: `if you choose a JUNKtech you instead get a
random normal tech and spawn ${powerUps.orb.research(2)}`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.junkCount > 0 + }, + requires: "some JUNK tech", + effect() { + tech.isMetaAnalysis = true + }, + remove() { + tech.isMetaAnalysis = false + } + }, + { + name: "dark patterns", + description: "+15% damage
+15% JUNK to tech pool", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + damage: 1.15, + effect() { + tech.damage *= this.damage + this.refundAmount += tech.addJunkTechToPool(0.15) + }, + refundAmount: 0, + remove() { + if (this.count > 0) { + tech.damage /= this.damage + if (this.refundAmount > 0) tech.removeJunkTechFromPool(this.refundAmount) + } + } + }, + { + name: "exciton", + descriptionFunction() { + return `after mobs die they have a 16% chance to
spawn ${powerUps.orb.boost(1)} that give +${(powerUps.boost.damage * 100).toFixed(0)}% damage for ${(powerUps.boost.duration / 60).toFixed(0)} seconds
` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + effect() { + tech.isBoostPowerUps = true + }, + remove() { + tech.isBoostPowerUps = false + } + }, + { + name: "band gap", + descriptionFunction() { + return `${powerUps.orb.boost(1)} give +77% damage
but their duration is reduced by 1 second` + }, + maxCount: 9, + count: 1, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isBoostPowerUps || tech.isBoostReplaceAmmo + }, + requires: "exciton, quasiparticles", + effect() { + powerUps.boost.duration -= 60 + powerUps.boost.damage += 0.77 + }, + remove() { + powerUps.boost.duration = 600 + powerUps.boost.damage = 1.25 + } + }, + { + name: "eternalism", + description: "+30% damage
time can't be paused (time can be dilated)", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isPauseSwitchField && !tech.isPauseEjectTech && !tech.isWormHolePause + }, + requires: "not unified field theory, paradigm shift, invariant", + damage: 1.3, + effect() { + tech.damage *= this.damage + tech.isNoDraftPause = true + }, + remove() { + if (this.count) tech.damage /= this.damage + tech.isNoDraftPause = false + } + }, + { + name: "paradigm shift", + description: `clicking tech while paused ejects them
20% chance to remove without ejecting`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSuperDeterminism && !tech.isNoDraftPause + }, + requires: "not superdeterminism, eternalism", + effect() { + tech.isPauseEjectTech = true; + }, + remove() { + tech.isPauseEjectTech = false; + } + }, + { + name: "unified field theory", + description: `clicking the field box when paused cycles your field
double the frequency of finding fieldtech
`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSuperDeterminism && !tech.isNoDraftPause + }, + requires: "not superdeterminism, eternalism", + effect() { + tech.isPauseSwitchField = true; + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isFieldTech) tech.tech[i].frequency *= 2 + } + }, + remove() { + tech.isPauseSwitchField = false; + if (this.count > 1) { for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].count > 0) tech.tech[i].frequency *= 10 - } - }, - remove() { - if (this.count) { - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].count > 0 && tech.tech[i].frequency > 1) tech.tech[i].frequency /= 10 - } + if (tech.tech[i].isFieldTech) tech.tech[i].frequency /= 2 } } + } + }, + { + name: "field coupling", + descriptionFunction() { + return `spawn ${powerUps.orb.coupling(10)}
that each give +0.1 coupling` //
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"} }, - // { - // name: "backward induction", - // descriptionFunction() { - // if (build.isExperimentSelection || powerUps.tech.choiceLog.length < 10) return `use ${powerUps.orb.research(2)} to choose all the unchosen tech
from your last selection` + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true + }, + requires: "", + effect() { + powerUps.spawnDelay("coupling", 10) + }, + remove() { + if (this.count) { + m.couplingChange(-this.count) + } + } + }, + { + name: "quintessence", + descriptionFunction() { + let converted = powerUps.research.count * this.couplingToResearch * 10 + if (this.count) converted = this.researchUsed * this.couplingToResearch * 10 - // text = `` - // let num = 3 - // if (tech.extraChoices) num = 5 - // if (tech.isDeterminism) num = 1 - // for (let i = 0; i < num; i++) { - // const index = powerUps.tech.choiceLog[powerUps.tech.choiceLog.length - i - 1] - // if (index !== powerUps.lastTechIndex && tech.tech[index].count < tech.tech[index].maxCount && tech.tech[index].allowed() && tech.tech[index].name !== "backward induction") { - // text += `${tech.tech[index].name}, ` - // } - // } - // text = text.slice(0, -2); - // return `use ${powerUps.orb.research(2)}to choose the unchosen
tech from your previous selection:
${text}` - // }, - // // description: `use ${powerUps.orb.research(2)}to choose all the unchosen
tech from your previous tech selection`, - // maxCount: 1, - // count: 0, - // frequency: 100, - // frequencyDefault: 100, - // isNonRefundable: true, - // isBadRandomOption: true, - // allowed() { - // return powerUps.tech.choiceLog.length > 10 && !tech.isDeterminism && powerUps.research.count > 1 - // }, - // requires: "NOT EXPERIMENT MODE, rejected an option in the last tech selection, at least 2 research, not determinism", - // effect: () => { - // powerUps.research.changeRerolls(-2) - // let num = 3 - // if (tech.extraChoices) num = 5 - // if (tech.isDeterminism) num = 1 - // for (let i = 0; i < num; i++) { - // const index = powerUps.tech.choiceLog[powerUps.tech.choiceLog.length - i - 1] - // if (index !== powerUps.lastTechIndex && tech.tech[index].count < tech.tech[index].maxCount && tech.tech[index].allowed() && tech.tech[index].name !== "backward induction") { - // tech.giveTech(index) - // simulation.makeTextLog(`tech.giveTech("${tech.tech[index].name}") //backward induction`); - // } - // } - // }, - // remove() {} + let orbText + if (converted > 15) { + orbText = `${converted} ${powerUps.orb.coupling()}` + } else { + orbText = powerUps.orb.coupling(converted) + } + return `use all your ${powerUps.orb.research(1)} to spawn ${orbText}
that each give +0.1 coupling`//
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"} + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return powerUps.research.count > 3 + }, + requires: "", + researchUsed: 0, + couplingToResearch: 0.25, + effect() { + // let count = 0 + // while (powerUps.research.count > 0 && powerUps.research.count !== Infinity) { + // powerUps.research.changeRerolls(-1) + // count += 2.5 + // this.researchUsed++ + // } + // powerUps.spawnDelay("coupling", Math.floor(count)) + + let cycle = () => { + if (powerUps.research.count > 0 && powerUps.research.count !== Infinity) { + if (m.alive) requestAnimationFrame(cycle); + if (!simulation.paused && !simulation.isChoosing) { //&& !(simulation.cycle % 2) + powerUps.research.changeRerolls(-1) + this.researchUsed++ + powerUps.spawn(m.pos.x + 50 * (Math.random() - 0.5), m.pos.y + 50 * (Math.random() - 0.5), "coupling"); + } + } else { //exit delay loop + } + } + requestAnimationFrame(cycle); + }, + remove() { + if (this.count) { + m.couplingChange(-this.researchUsed * this.couplingToResearch) + powerUps.research.changeRerolls(this.researchUsed) + this.researchUsed = 0 + } + } + }, + { + name: "virtual particles", + descriptionFunction() { + return `after mobs die they have a 17% chance to
spawn ${powerUps.orb.coupling(1)} that each give +0.1 coupling` //
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"} + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => true, + requires: "", + effect() { + tech.isCouplingPowerUps = true //about 20-30 mobs per level so at 16% and 0.1 coupling that's about 25 * 0.16 * 0.1 = 0.4 coupling per level with out duplication + }, + remove() { + tech.isCouplingPowerUps = false + } + }, + { + name: "fine-structure constant", + descriptionFunction() { + return `spawn ${this.value} ${powerUps.orb.coupling(1)} that each give +0.1 coupling
-0.5 coupling after mob collisions`//
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"} + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + allowed: () => true, + requires: "", + value: 60, + effect() { + tech.isCouplingNoHit = true + powerUps.spawnDelay("coupling", this.value) + }, + remove() { + if (this.count) { + m.couplingChange(-this.value) + } + tech.isCouplingNoHit = false + } + }, + { + name: "residual dipolar coupling", + descriptionFunction() { + return `clicking cancel for a field, tech, or gun
spawns ${powerUps.orb.coupling(5)}that each give +0.1 coupling`//
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"} + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSuperDeterminism + }, + requires: "not superdeterminism", + effect() { + tech.isCancelCouple = true + }, + remove() { + tech.isCancelCouple = false + } + }, + { + name: "commodities exchange", + descriptionFunction() { + return `clicking cancel for a field, tech, or gun
spawns 5-10 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSuperDeterminism + }, + requires: "not superdeterminism", + effect() { + tech.isCancelRerolls = true + }, + remove() { + tech.isCancelRerolls = false + } + }, + { + name: "options exchange", + link: `options exchange`, + description: `clicking cancel for a field, tech, or gun
has a 85% chance to randomize choices`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSuperDeterminism //&& (tech.isCancelRerolls || tech.isCancelDuplication) + }, + requires: "not superdeterminism", //futures exchange, commodities exchange, + effect() { + tech.isCancelTech = true + }, + remove() { + tech.isCancelTech = false + } + }, + { + name: "futures exchange", + description: "clicking cancel for a field, tech, or gun
gives +4.3% power up duplication chance", + // descriptionFunction() { + // return `clicking × to cancel a field, tech, or gun
gives +${4.9 - 0.15*simulation.difficultyMode}% power up duplication chance` // }, - //************************************************** - //************************************************** gun - //************************************************** tech - //************************************************** - { - name: "needle ice", - description: `after needles impact walls
they chip off 1-2 freezing ice IX crystals`, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.isNeedles || tech.isNeedles) && !tech.needleTunnel - }, - requires: "nail gun, needle gun, not nanowires", - effect() { - tech.isNeedleIce = true - }, - remove() { - tech.isNeedleIce = false + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.duplicationChance() < 1 && !tech.isSuperDeterminism + }, + requires: "below 100% duplication chance, not superdeterminism", + effect() { + tech.isCancelDuplication = true //search for tech.cancelCount to balance + powerUps.setDupChance(); //needed after adjusting duplication chance + }, + remove() { + tech.isCancelDuplication = false + powerUps.setDupChance(); //needed after adjusting duplication chance + } + }, + { + name: "replication", + description: "+10% chance to duplicate spawned power ups
+33% JUNK to tech pool", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.duplicationChance() < 1. + }, + requires: "below 100% duplication chance", + effect() { + tech.duplicateChance += 0.1 + powerUps.setDupChance(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.11); + this.refundAmount += tech.addJunkTechToPool(0.33) + }, + refundAmount: 0, + remove() { + tech.duplicateChance = 0 + powerUps.setDupChance(); //needed after adjusting duplication chance + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "stimulated emission", + description: "+15% chance to duplicate spawned power ups,
but after a collision eject 1 tech", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.duplicationChance() < 1 + }, + requires: "below 1% duplication chance", + effect() { + tech.isStimulatedEmission = true + powerUps.setDupChance(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.15); + }, + remove() { + tech.isStimulatedEmission = false + powerUps.setDupChance(); //needed after adjusting duplication chance + } + }, + { + name: "metastability", + description: "+12% chance to duplicate spawned power ups
duplicates explode with a 3 second half-life", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.duplicationChance() < 1 + }, + requires: "below 100% duplication chance", + effect() { + tech.isPowerUpsVanish = true + powerUps.setDupChance(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.11); + }, + remove() { + tech.isPowerUpsVanish = false + powerUps.setDupChance(); //needed after adjusting duplication chance + } + }, + { + name: "correlated damage", + description: "duplication increases damage", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.duplicationChance() > 0.15 + }, + requires: "duplication chance > 15%", + effect() { + tech.isDupDamage = true; + }, + remove() { + tech.isDupDamage = false; + } + }, + { + name: "parthenogenesis", + description: "your duplication has a chance to
duplicate mobs and bosses", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.duplicationChance() > 0 && !tech.isResearchBoss + }, + requires: "some duplication chance, not abiogenesis", + effect() { + tech.isDuplicateBoss = true; + }, + remove() { + tech.isDuplicateBoss = false; + } + }, + { + name: "apomixis", + description: `when you reach 100% duplication
spawn 11 bosses with 100% more durability`, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isNonRefundable: true, + allowed() { + return tech.duplicationChance() > 0.5 + }, + requires: "duplication chance above 50%", + effect() { + tech.is100Duplicate = true; + tech.maxDuplicationEvent() + }, + remove() { + tech.is100Duplicate = false; + } + }, + { + name: "Born rule", + description: "remove all current tech
spawn new tech to replace them", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return (tech.totalCount > 6) + }, + requires: "more than 6 tech", + effect() { + //remove active bullets //to get rid of bots + for (let i = 0; i < bullet.length; ++i) Matter.Composite.remove(engine.world, bullet[i]); + bullet = []; + let count = 1 //count tech + for (let i = 0, len = tech.tech.length; i < len; i++) { // spawn new tech power ups + if (!tech.tech[i].isNonRefundable) count += tech.tech[i].count + } + if (tech.isDeterminism) count -= 4 //remove the bonus tech + if (tech.isSuperDeterminism) count -= 4 //remove the bonus tech + + tech.setupAllTech(); // remove all tech + if (simulation.isCheating) tech.setCheating(); + lore.techCount = 0; + // tech.addLoreTechToPool(); + for (let i = 0; i < count; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "tech"); // spawn new tech power ups + //have state is checked in m.death() + }, + remove() { } + }, + + { + name: "Occams razor", + descriptionFunction() { + return `randomly remove half your tech
for each removed +${this.damagePerRemoved * 100}% damage (~${(this.count === 0) ? this.damagePerRemoved * 50 * tech.totalCount : this.damage * 100}%)` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return (tech.totalCount > 6) + }, + requires: "more than 6 tech", + // removePercent: 0.5, + damagePerRemoved: 0.5, + damage: null, + effect() { + let pool = [] + for (let i = 0, len = tech.tech.length; i < len; i++) { // spawn new tech power ups + if (tech.tech[i].count && !tech.tech[i].isNonRefundable && !tech.tech[i].isFromAppliedScience) pool.push(i) + } + pool = shuffle(pool); //shuffles order of maps + let removeCount = 0 + for (let i = 0, len = pool.length * this.damagePerRemoved; i < len; i++) removeCount += tech.removeTech(pool[i]) + this.damage = 1 + this.damagePerRemoved * removeCount + tech.damage *= this.damage + }, + remove() { + if (this.count) tech.damage /= this.damage + } + }, + { + name: "exchange symmetry", + description: "remove 1 random tech
spawn 2 new guns", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return (tech.totalCount > 3) && !tech.isSuperDeterminism + }, + requires: "at least 4 tech, not superdeterminism", + effect() { + const have = [] //find which tech you have + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) have.push(i) + } + const choose = have[Math.floor(Math.random() * have.length)] + simulation.makeTextLog(`tech.removeTech("${tech.tech[choose].name}")`, 360) + for (let i = 0; i < tech.tech[choose].count; i++) { + powerUps.spawn(m.pos.x, m.pos.y, "gun"); + } + powerUps.spawn(m.pos.x, m.pos.y, "gun"); + // powerUps.spawn(m.pos.x, m.pos.y, "gun"); + tech.tech[choose].count = 0; + tech.tech[choose].remove(); // remove a random tech form the list of tech you have + tech.tech[choose].isLost = true + simulation.updateTechHUD(); + }, + remove() { } + }, + { + name: "Monte Carlo method", + description: "remove 1 random tech
spawn 2 tech", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return (tech.totalCount > 3) && tech.duplicationChance() > 0 && !tech.isSuperDeterminism + }, + requires: "some duplication, at least 4 tech, not superdeterminism", + effect() { + const removeTotal = tech.removeTech() + for (let i = 0; i < removeTotal + 1; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech"); + }, + remove() { } + }, + { + name: "strange attractor", + descriptionFunction() { + return `use ${powerUps.orb.research(2)} to spawn 1 tech with
double your duplication chance (${(2 * tech.duplicationChance() * 100).toFixed(0)}%)` + }, + // description: `use ${powerUps.orb.research(2)} to spawn 1 tech with double
your duplication chance (${(2*tech.duplicationChance()*100).toFixed(0)}%)`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return !tech.isSuperDeterminism && tech.duplicationChance() > 0 && powerUps.research.count > 1 + }, + requires: "some duplication, not superdeterminism", + effect() { + powerUps.research.changeRerolls(-2) + simulation.makeTextLog(`m.research -= 2`) + powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); + if (Math.random() < tech.duplicationChance() * 2) powerUps.directSpawn(m.pos.x + 10, m.pos.y + 5, "tech"); + }, + remove() { } + }, + { + name: "reinforcement learning", + description: "increase the frequency of finding copies of
your current tech by 1000%", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.totalCount > 9 + }, + requires: "at least 10 tech", + effect() { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].count > 0) tech.tech[i].frequency *= 10 } }, - { - name: "nanowires", - description: `needles tunnel through blocks and map
+20% needle damage`, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return ((tech.haveGunCheck("nail gun") && tech.isNeedles) || (tech.isNeedles && tech.haveGunCheck("shotgun"))) && !tech.isNeedleIce - }, - requires: "nail gun, needle gun, not needle ice", - effect() { - tech.needleTunnel = true - }, - remove() { - tech.needleTunnel = false + remove() { + if (this.count) { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].count > 0 && tech.tech[i].frequency > 1) tech.tech[i].frequency /= 10 + } + } + } + }, + // { + // name: "backward induction", + // descriptionFunction() { + // if (build.isExperimentSelection || powerUps.tech.choiceLog.length < 10) return `use ${powerUps.orb.research(2)} to choose all the unchosen tech
from your last selection` + + // text = `` + // let num = 3 + // if (tech.extraChoices) num = 5 + // if (tech.isDeterminism) num = 1 + // for (let i = 0; i < num; i++) { + // const index = powerUps.tech.choiceLog[powerUps.tech.choiceLog.length - i - 1] + // if (index !== powerUps.lastTechIndex && tech.tech[index].count < tech.tech[index].maxCount && tech.tech[index].allowed() && tech.tech[index].name !== "backward induction") { + // text += `${tech.tech[index].name}, ` + // } + // } + // text = text.slice(0, -2); + // return `use ${powerUps.orb.research(2)}to choose the unchosen
tech from your previous selection:
${text}` + // }, + // // description: `use ${powerUps.orb.research(2)}to choose all the unchosen
tech from your previous tech selection`, + // maxCount: 1, + // count: 0, + // frequency: 100, + // frequencyDefault: 100, + // isNonRefundable: true, + // isBadRandomOption: true, + // allowed() { + // return powerUps.tech.choiceLog.length > 10 && !tech.isDeterminism && powerUps.research.count > 1 + // }, + // requires: "NOT EXPERIMENT MODE, rejected an option in the last tech selection, at least 2 research, not determinism", + // effect: () => { + // powerUps.research.changeRerolls(-2) + // let num = 3 + // if (tech.extraChoices) num = 5 + // if (tech.isDeterminism) num = 1 + // for (let i = 0; i < num; i++) { + // const index = powerUps.tech.choiceLog[powerUps.tech.choiceLog.length - i - 1] + // if (index !== powerUps.lastTechIndex && tech.tech[index].count < tech.tech[index].maxCount && tech.tech[index].allowed() && tech.tech[index].name !== "backward induction") { + // tech.giveTech(index) + // simulation.makeTextLog(`tech.giveTech("${tech.tech[index].name}") //backward induction`); + // } + // } + // }, + // remove() {} + // }, + //************************************************** + //************************************************** gun + //************************************************** tech + //************************************************** + { + name: "needle ice", + description: `after needles impact walls
they chip off 1-2 freezing ice IX crystals`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.isNeedles || tech.isNeedles) && !tech.needleTunnel + }, + requires: "nail gun, needle gun, not nanowires", + effect() { + tech.isNeedleIce = true + }, + remove() { + tech.isNeedleIce = false + } + }, + { + name: "nanowires", + description: `needles tunnel through blocks and map
+20% needle damage`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return ((tech.haveGunCheck("nail gun") && tech.isNeedles) || (tech.isNeedles && tech.haveGunCheck("shotgun"))) && !tech.isNeedleIce + }, + requires: "nail gun, needle gun, not needle ice", + effect() { + tech.needleTunnel = true + }, + remove() { + tech.needleTunnel = false + } + }, + { + name: "ceramics", + description: `needles and harpoons pierce shields
directly damaging shielded mobs`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (!tech.isLargeHarpoon && tech.haveGunCheck("harpoon")) || tech.isNeedles + }, + requires: "needle gun, harpoon, not Bessemer process", + effect() { + tech.isShieldPierce = true + }, + remove() { + tech.isShieldPierce = false + } + }, + { + name: "needle gun", + description: "nail gun and shotgun fire mob piercing needles", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return ((tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.nailRecoil && !tech.isRicochet) || (tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isFoamShot && !tech.isSporeWorm && !tech.isSporeFlea)) && !tech.isRivets && !tech.isIncendiary && !tech.isIceCrystals && !tech.isIceShot + }, + requires: "nail gun, shotgun, not ice crystal, rivets, rotary cannon, pneumatic, incendiary, nail-shot, foam-shot, worm-shot, ice-shot", + effect() { + tech.isNeedles = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") { + b.guns[i].ammo = Math.ceil(b.guns[i].ammo / 3); + b.guns[i].ammoPack = Math.ceil(b.guns[i].defaultAmmoPack / 3); + b.guns[i].chooseFireMethod() + simulation.updateGunHUD(); + break + } } }, - { - name: "ceramics", - description: `needles and harpoons pierce shields
directly damaging shielded mobs`, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (!tech.isLargeHarpoon && tech.haveGunCheck("harpoon")) || tech.isNeedles - }, - requires: "needle gun, harpoon, not Bessemer process", - effect() { - tech.isShieldPierce = true - }, - remove() { - tech.isShieldPierce = false - } - }, - { - name: "needle gun", - description: "nail gun and shotgun fire mob piercing needles", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return ((tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.nailRecoil && !tech.isRicochet) || (tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isFoamShot && !tech.isSporeWorm && !tech.isSporeFlea)) && !tech.isRivets && !tech.isIncendiary && !tech.isIceCrystals && !tech.isIceShot - }, - requires: "nail gun, shotgun, not ice crystal, rivets, rotary cannon, pneumatic, incendiary, nail-shot, foam-shot, worm-shot, ice-shot", - effect() { - tech.isNeedles = true + remove() { + if (tech.isNeedles) { + tech.isNeedles = false for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "nail gun") { - b.guns[i].ammo = Math.ceil(b.guns[i].ammo / 3); - b.guns[i].ammoPack = Math.ceil(b.guns[i].defaultAmmoPack / 3); b.guns[i].chooseFireMethod() + b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 3); + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack; simulation.updateGunHUD(); break } } - }, - remove() { - if (tech.isNeedles) { - tech.isNeedles = false - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "nail gun") { - b.guns[i].chooseFireMethod() - b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 3); - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack; - simulation.updateGunHUD(); - break - } - } + } + } + }, + { + name: "stress concentration", + description: "mobs below 50% durability die after you shoot
them near their center with needles or rivets", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.isNeedles || tech.isRivets) && !tech.isNailCrit && !tech.isIncendiary + }, + requires: "needles, rivets, not incendiary, supercritical fission", + effect() { + tech.isCritKill = true + }, + remove() { + tech.isCritKill = false + } + }, + { + name: "rivet gun", + description: "nail gun and shotgun slowly lob a heavy rivet", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return ((tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.isRicochet) || (tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isFoamShot && !tech.isSporeWorm && !tech.isSporeFlea)) && !tech.isNeedles && !tech.isIceCrystals && !tech.isIceShot + }, + requires: "nail gun, shotgun, not ice crystal, needles, or pneumatic actuator", + effect() { + tech.isRivets = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") { + b.guns[i].chooseFireMethod() + break } } }, - { - name: "stress concentration", - description: "mobs below 50% durability die after you shoot
them near their center with needles or rivets", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.isNeedles || tech.isRivets) && !tech.isNailCrit && !tech.isIncendiary - }, - requires: "needles, rivets, not incendiary, supercritical fission", - effect() { - tech.isCritKill = true - }, - remove() { - tech.isCritKill = false - } - }, - { - name: "rivet gun", - description: "nail gun and shotgun slowly lob a heavy rivet", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return ((tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.isRicochet) || (tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isFoamShot && !tech.isSporeWorm && !tech.isSporeFlea)) && !tech.isNeedles && !tech.isIceCrystals && !tech.isIceShot - }, - requires: "nail gun, shotgun, not ice crystal, needles, or pneumatic actuator", - effect() { - tech.isRivets = true + remove() { + if (tech.isRivets) { + tech.isRivets = false for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "nail gun") { b.guns[i].chooseFireMethod() break } } - }, - remove() { - if (tech.isRivets) { - tech.isRivets = false - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "nail gun") { - b.guns[i].chooseFireMethod() - break - } - } - } - tech.isRivets = false + } + tech.isRivets = false + } + }, + { + name: "pneumatic actuator", + description: "nail gun takes no time to ramp up
to its fastest fire rate", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles && !tech.nailRecoil + }, + requires: "nail gun, not rotary cannon, rivets, or needles", + effect() { + tech.nailInstantFireRate = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() } }, - { - name: "pneumatic actuator", - description: "nail gun takes no time to ramp up
to its fastest fire rate", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles && !tech.nailRecoil - }, - requires: "nail gun, not rotary cannon, rivets, or needles", - effect() { - tech.nailInstantFireRate = true + remove() { + if (tech.nailInstantFireRate) { + tech.nailInstantFireRate = false for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() } - }, - remove() { - if (tech.nailInstantFireRate) { - tech.nailInstantFireRate = false - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() - } - } } + } + }, + { + name: "ice crystal nucleation", + link: `ice crystal nucleation`, + description: "nail gun uses energy to condense
unlimited freezing ice shards", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles // && !tech.isNailRadiation && !tech.isNailCrit }, - { - name: "ice crystal nucleation", - link: `ice crystal nucleation`, - description: "nail gun uses energy to condense
unlimited freezing ice shards", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles // && !tech.isNailRadiation && !tech.isNailCrit - }, - requires: "nail gun, not rivets, needles", - effect() { - tech.isIceCrystals = true; - b.guns[0].ammoPack = Infinity - b.guns[0].recordedAmmo = b.guns[i].ammo - b.guns[0].ammo = Infinity - simulation.updateGunHUD(); - }, - remove() { - if (tech.isIceCrystals) { - tech.isIceCrystals = false; - b.guns[0].ammoPack = b.guns[0].defaultAmmoPack; - if (b.guns[0].recordedAmmo) b.guns[0].ammo = b.guns[0].recordedAmmo - simulation.updateGunHUD(); - } + requires: "nail gun, not rivets, needles", + effect() { + tech.isIceCrystals = true; + b.guns[0].ammoPack = Infinity + b.guns[0].recordedAmmo = b.guns[i].ammo + b.guns[0].ammo = Infinity + simulation.updateGunHUD(); + }, + remove() { + if (tech.isIceCrystals) { tech.isIceCrystals = false; - if (b.guns[0].ammo === Infinity) b.guns[0].ammo = 0 + b.guns[0].ammoPack = b.guns[0].defaultAmmoPack; + if (b.guns[0].recordedAmmo) b.guns[0].ammo = b.guns[0].recordedAmmo + simulation.updateGunHUD(); + } + tech.isIceCrystals = false; + if (b.guns[0].ammo === Infinity) b.guns[0].ammo = 0 + } + }, + { + name: "rotary cannon", + description: "nail gun has increased muzzle speed,
maximum fire rate, accuracy, and recoil", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.isNeedles + }, + requires: "nail gun, not pneumatic actuator, needle gun", + effect() { + tech.nailRecoil = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() } }, - { - name: "rotary cannon", - description: "nail gun has increased muzzle speed,
maximum fire rate, accuracy, and recoil", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.isNeedles - }, - requires: "nail gun, not pneumatic actuator, needle gun", - effect() { - tech.nailRecoil = true + remove() { + if (tech.nailRecoil) { + tech.nailRecoil = false for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() } - }, - remove() { - if (tech.nailRecoil) { - tech.nailRecoil = false - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() - } + } + } + }, + { + name: "gauge", + description: `rivets, needles, super balls, and nails
have +30% mass and physical damage`, + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isMineDrop + tech.isNailBotUpgrade + tech.fragments + tech.nailsDeathMob + (tech.haveGunCheck("super balls") + (tech.haveGunCheck("mine") && !tech.isFoamMine) + (tech.haveGunCheck("nail gun")) + tech.isNeedles + tech.isNailShot + tech.isRivets) * 2 > 1 + }, + requires: "nails, nail gun, rivets, shotgun, super balls, mine", + effect() { + tech.bulletSize = 1 + 0.25 * Math.pow(this.count + 1, 0.5) + }, + remove() { + tech.bulletSize = 1; + } + }, + { + name: "supercritical fission", + description: "if nails, needles, or rivets strike mobs
near their center they can explode", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.isNailShot || tech.isNeedles || tech.isNailBotUpgrade || tech.haveGunCheck("nail gun") || tech.isRivets || (tech.haveGunCheck("mine") && !(tech.isFoamMine || tech.isSuperMine))) && !tech.isIncendiary && !tech.isCritKill + }, + requires: "nail gun, mine, needles, nails, rivets, not incendiary, stress concentration", + effect() { + tech.isNailCrit = true + }, + remove() { + tech.isNailCrit = false + } + }, + { + name: "irradiated nails", + link: `irradiated nails`, + description: "nails, needles, and rivets are radioactive
+90% radioactive damage over 3 seconds", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isMineDrop || tech.isNailBotUpgrade || tech.fragments || tech.nailsDeathMob || (tech.haveGunCheck("mine") && !(tech.isFoamMine || tech.isSuperMine)) || (tech.haveGunCheck("nail gun") && !tech.isShieldPierce) || (tech.haveGunCheck("shotgun") && (tech.isNeedles || tech.isNailShot)) + }, + requires: "nail gun, nails, rivets, mine, not ceramic needles", + effect() { + tech.isNailRadiation = true; + }, + remove() { + tech.isNailRadiation = false; + } + }, + { + name: "6s half-life", + link: `6s half-life`, + description: "nails, needles, rivets are made of plutonium-238
radioactive damage lasts +3 seconds", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isNailRadiation && !tech.isFastRadiation + }, + requires: "nail gun, mine, irradiated nails, not 1s half-life", + effect() { + tech.isSlowRadiation = true; + }, + remove() { + tech.isSlowRadiation = false; + } + }, + { + name: "1s half-life", + link: `1s half-life`, + description: "nails, needles, rivets are made of lithium-8
+300% radioactive damage for 1 second
", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isNailRadiation && !tech.isSlowRadiation + }, + requires: "nail gun, mine, irradiated nails, not 6s half-life", + effect() { + tech.isFastRadiation = true; + }, + remove() { + tech.isFastRadiation = false; + } + }, + { + name: "spin-statistics", + link: `spin-statistics`, + description: "after firing the shotgun you are invulnerable
shotgun has 50% fewer shots", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("shotgun") + }, + requires: "shotgun", + effect() { + tech.isShotgunImmune = true; + + //cut current ammo by 1/2 + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "shotgun") { + b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 0.5); + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * 0.5 + break; } } + simulation.updateGunHUD(); }, - { - name: "gauge", - description: `rivets, needles, super balls, and nails
have +30% mass and physical damage`, - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isMineDrop + tech.isNailBotUpgrade + tech.fragments + tech.nailsDeathMob + (tech.haveGunCheck("super balls") + (tech.haveGunCheck("mine") && !tech.isFoamMine) + (tech.haveGunCheck("nail gun")) + tech.isNeedles + tech.isNailShot + tech.isRivets) * 2 > 1 - }, - requires: "nails, nail gun, rivets, shotgun, super balls, mine", - effect() { - tech.bulletSize = 1 + 0.25 * Math.pow(this.count + 1, 0.5) - }, - remove() { - tech.bulletSize = 1; - } - }, - { - name: "supercritical fission", - description: "if nails, needles, or rivets strike mobs
near their center they can explode", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.isNailShot || tech.isNeedles || tech.isNailBotUpgrade || tech.haveGunCheck("nail gun") || tech.isRivets || (tech.haveGunCheck("mine") && !(tech.isFoamMine || tech.isSuperMine))) && !tech.isIncendiary && !tech.isCritKill - }, - requires: "nail gun, mine, needles, nails, rivets, not incendiary, stress concentration", - effect() { - tech.isNailCrit = true - }, - remove() { - tech.isNailCrit = false - } - }, - { - name: "irradiated nails", - link: `irradiated nails`, - description: "nails, needles, and rivets are radioactive
+90% radioactive damage over 3 seconds", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isMineDrop || tech.isNailBotUpgrade || tech.fragments || tech.nailsDeathMob || (tech.haveGunCheck("mine") && !(tech.isFoamMine || tech.isSuperMine)) || (tech.haveGunCheck("nail gun") && !tech.isShieldPierce) || (tech.haveGunCheck("shotgun") && (tech.isNeedles || tech.isNailShot)) - }, - requires: "nail gun, nails, rivets, mine, not ceramic needles", - effect() { - tech.isNailRadiation = true; - }, - remove() { - tech.isNailRadiation = false; - } - }, - { - name: "6s half-life", - link: `6s half-life`, - description: "nails, needles, rivets are made of plutonium-238
radioactive damage lasts +3 seconds", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isNailRadiation && !tech.isFastRadiation - }, - requires: "nail gun, mine, irradiated nails, not 1s half-life", - effect() { - tech.isSlowRadiation = true; - }, - remove() { - tech.isSlowRadiation = false; - } - }, - { - name: "1s half-life", - link: `1s half-life`, - description: "nails, needles, rivets are made of lithium-8
+300% radioactive damage for 1 second
", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isNailRadiation && !tech.isSlowRadiation - }, - requires: "nail gun, mine, irradiated nails, not 6s half-life", - effect() { - tech.isFastRadiation = true; - }, - remove() { - tech.isFastRadiation = false; - } - }, - { - name: "spin-statistics", - link: `spin-statistics`, - description: "after firing the shotgun you are invulnerable
shotgun has 50% fewer shots", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("shotgun") - }, - requires: "shotgun", - effect() { - tech.isShotgunImmune = true; - - //cut current ammo by 1/2 + remove() { + if (tech.isShotgunImmune) { + tech.isShotgunImmune = false; for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "shotgun") { - b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 0.5); - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * 0.5 + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack; + b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 2); break; } } simulation.updateGunHUD(); - }, - remove() { - if (tech.isShotgunImmune) { - tech.isShotgunImmune = false; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "shotgun") { - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack; - b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 2); - break; - } - } - simulation.updateGunHUD(); - } + } + } + }, + { + name: "Newtons 3rd law", + description: "+66% shotgun fire rate and recoil", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("shotgun") && !tech.isShotgunReversed + }, + requires: "shotgun, not Noether violation", + effect() { + tech.isShotgunRecoil = true; + }, + remove() { + tech.isShotgunRecoil = false; + } + }, + { + name: "Noether violation", + link: `Noether violation`, + description: "+50% shotgun damage
shotgun recoil is reversed", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return (tech.haveGunCheck("shotgun")) && !tech.isShotgunRecoil + }, + requires: "shotgun, not Newtons 3rd law", + effect() { + tech.isShotgunReversed = true; + }, + remove() { + tech.isShotgunReversed = false; + } + }, + { + name: "repeater", + description: "shotgun immediately fires again for no ammo
-50% shotgun fire rate", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("shotgun")) + }, + requires: "shotgun, not Newtons 3rd law", + effect() { + tech.shotgunExtraShots++; + }, + remove() { + tech.shotgunExtraShots = 0 + } + }, + { + name: "nail-shot", + link: `nail-shot`, + description: "shotgun drives a long clip of nails", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isSporeWorm && !tech.isSporeFlea && !tech.isNeedles + }, + requires: "shotgun, not incendiary, rivets, foam-shot, worm-shot, ice-shot, needles", + effect() { + tech.isNailShot = true; + }, + remove() { + tech.isNailShot = false; + } + }, + { + name: "foam-shot", + link: `foam-shot`, + description: "shotgun sprays sticky foam bubbles", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isSporeWorm && !tech.isSporeFlea && !tech.isNeedles + }, + requires: "shotgun, not incendiary, nail-shot, rivet, worm-shot, ice-shot, needle", + effect() { + tech.isFoamShot = true; + }, + remove() { + tech.isFoamShot = false; + } + }, + { + name: "ice-shot", + link: `ice-shot`, + description: "shotgun grows freezing ice IX crystals", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isRivets && !tech.isFoamShot && !tech.isSporeWorm && !tech.isSporeFlea && !tech.isNeedles + }, + requires: "shotgun, not incendiary, nail-shot, rivet, foam-shot, worm-shot", + effect() { + tech.isIceShot = true; + }, + remove() { + tech.isIceShot = false; + } + }, + { + name: "freezer burn", + description: "mobs frozen while below 33% durability die", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isIceCrystals || tech.isSporeFreeze || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 2) || tech.isIceShot || tech.relayIce || tech.isNeedleIce || (m.coupling && m.fieldMode < 3) + }, + requires: "a freeze effect", + effect() { + tech.isIceKill = true + }, + remove() { + tech.isIceKill = false + } + }, + { + name: "flash freeze", + description: "mobs frozen while above 66% durability
have their durability reduced to 66%", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isIceCrystals || tech.isSporeFreeze || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 2) || tech.isIceShot || tech.relayIce || tech.isNeedleIce || (m.coupling && m.fieldMode < 3) + }, + requires: "a freeze effect", + effect() { + tech.isIceMaxHealthLoss = true + }, + remove() { + tech.isIceMaxHealthLoss = false + } + }, + { + name: "crystallizer", + description: "after frozen mobs die they
shatter into ice IX crystals", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.isIceCrystals || tech.isSporeFreeze || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 2) || tech.isIceShot || tech.relayIce || tech.isNeedleIce || (m.coupling && m.fieldMode < 3)) && !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.nailsDeathMob + }, + requires: "a localized freeze effect, no other mob death tech", + effect() { + tech.iceIXOnDeath++ + }, + remove() { + tech.iceIXOnDeath = 0 + } + }, + { + name: "thermoelectric effect", + description: "after killing mobs with ice IX
+100 energy", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 2) || tech.relayIce || tech.isNeedleIce || (m.coupling && m.fieldMode < 3) || tech.iceIXOnDeath || tech.isIceShot + }, + requires: "ice IX", + effect() { + tech.iceEnergy++ + }, + remove() { + tech.iceEnergy = 0; + } + }, + { + name: "superfluidity", + description: "freeze effects are applied
to a small area around the target", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isIceCrystals || tech.isSporeFreeze || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 2) || tech.relayIce || tech.isNeedleIce || (m.coupling && m.fieldMode < 3) || tech.iceIXOnDeath || tech.isIceShot + }, + requires: "a localized freeze effect", + effect() { + tech.isAoESlow = true + }, + remove() { + tech.isAoESlow = false + } + }, + { + name: "incendiary ammunition", + description: "shotgun, rivets, super balls, and drones
are loaded with explosives", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return (tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIceShot && !tech.isRivets && !tech.isFoamShot && !tech.isSporeWorm && !tech.isSporeFlea && !tech.isNeedles) || ((tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isSuperBounce && !tech.isFoamBall && !tech.isSuperHarm) || (tech.isRivets && !tech.isNailCrit) || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 3) || (tech.haveGunCheck("drones") && !tech.isForeverDrones && !tech.isDroneRadioactive && !tech.isDroneTeleport) + }, + requires: "shotgun, super balls, rivets, drones, not irradiated drones, burst drones, polyurethane, Zectron", + effect() { + tech.isIncendiary = true + }, + remove() { + tech.isIncendiary = false; + } + }, + { + name: "rebound", + description: `after they collide with a mob, super balls
gain speed, duration, and +33% damage`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isIncendiary && !tech.isFoamBall + }, + requires: "super balls, not incendiary", + effect() { + tech.isSuperBounce = true + }, + remove() { + tech.isSuperBounce = false + } + }, + { + name: "Zectron", + description: `+75% super ball density and damage, but
after colliding with super balls -25% energy`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isIncendiary + }, + requires: "super balls not incendiary ammunition", + effect() { + tech.isSuperHarm = true + }, + remove() { + tech.isSuperHarm = false + } + }, + { + name: "polyurethane foam", + description: "super balls and harpoons colliding with mobs
catalyzes a reaction that yields foam bubbles", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return ((tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isSuperBounce) || (tech.haveGunCheck("harpoon") && !tech.fragments) + }, + requires: "super balls, harpoon, not fragmentation", + effect() { + tech.isFoamBall = true; + }, + remove() { + tech.isFoamBall = false; + } + }, + { + name: "autocannon", + description: "fire +1 extra super ball
balls are quickly released in same direction", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("super balls") && !tech.oneSuperBall + }, + requires: "super balls, but not the tech super ball", + effect() { + tech.superBallDelay = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() } }, - { - name: "Newtons 3rd law", - description: "+66% shotgun fire rate and recoil", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("shotgun") && !tech.isShotgunReversed - }, - requires: "shotgun, not Noether violation", - effect() { - tech.isShotgunRecoil = true; - }, - remove() { - tech.isShotgunRecoil = false; - } - }, - { - name: "Noether violation", - link: `Noether violation`, - description: "+50% shotgun damage
shotgun recoil is reversed", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return (tech.haveGunCheck("shotgun")) && !tech.isShotgunRecoil - }, - requires: "shotgun, not Newtons 3rd law", - effect() { - tech.isShotgunReversed = true; - }, - remove() { - tech.isShotgunReversed = false; - } - }, - { - name: "repeater", - description: "shotgun immediately fires again for no ammo
-50% shotgun fire rate", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.haveGunCheck("shotgun")) - }, - requires: "shotgun, not Newtons 3rd law", - effect() { - tech.shotgunExtraShots++; - }, - remove() { - tech.shotgunExtraShots = 0 - } - }, - { - name: "nail-shot", - link: `nail-shot`, - description: "shotgun drives a long clip of nails", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isSporeWorm && !tech.isSporeFlea && !tech.isNeedles - }, - requires: "shotgun, not incendiary, rivets, foam-shot, worm-shot, ice-shot, needles", - effect() { - tech.isNailShot = true; - }, - remove() { - tech.isNailShot = false; - } - }, - { - name: "foam-shot", - link: `foam-shot`, - description: "shotgun sprays sticky foam bubbles", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isSporeWorm && !tech.isSporeFlea && !tech.isNeedles - }, - requires: "shotgun, not incendiary, nail-shot, rivet, worm-shot, ice-shot, needle", - effect() { - tech.isFoamShot = true; - }, - remove() { - tech.isFoamShot = false; - } - }, - { - name: "ice-shot", - link: `ice-shot`, - description: "shotgun grows freezing ice IX crystals", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isRivets && !tech.isFoamShot && !tech.isSporeWorm && !tech.isSporeFlea && !tech.isNeedles - }, - requires: "shotgun, not incendiary, nail-shot, rivet, foam-shot, worm-shot", - effect() { - tech.isIceShot = true; - }, - remove() { - tech.isIceShot = false; - } - }, - { - name: "freezer burn", - description: "mobs frozen while below 33% durability die", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isIceCrystals || tech.isSporeFreeze || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 2) || tech.isIceShot || tech.relayIce || tech.isNeedleIce || (m.coupling && m.fieldMode < 3) - }, - requires: "a freeze effect", - effect() { - tech.isIceKill = true - }, - remove() { - tech.isIceKill = false - } - }, - { - name: "flash freeze", - description: "mobs frozen while above 66% durability
have their durability reduced to 66%", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isIceCrystals || tech.isSporeFreeze || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 2) || tech.isIceShot || tech.relayIce || tech.isNeedleIce || (m.coupling && m.fieldMode < 3) - }, - requires: "a freeze effect", - effect() { - tech.isIceMaxHealthLoss = true - }, - remove() { - tech.isIceMaxHealthLoss = false - } - }, - { - name: "crystallizer", - description: "after frozen mobs die they
shatter into ice IX crystals", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.isIceCrystals || tech.isSporeFreeze || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 2) || tech.isIceShot || tech.relayIce || tech.isNeedleIce || (m.coupling && m.fieldMode < 3)) && !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.nailsDeathMob - }, - requires: "a localized freeze effect, no other mob death tech", - effect() { - tech.iceIXOnDeath++ - }, - remove() { - tech.iceIXOnDeath = 0 - } - }, - { - name: "thermoelectric effect", - description: "after killing mobs with ice IX
+100 energy", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 2) || tech.relayIce || tech.isNeedleIce || (m.coupling && m.fieldMode < 3) || tech.iceIXOnDeath || tech.isIceShot - }, - requires: "ice IX", - effect() { - tech.iceEnergy++ - }, - remove() { - tech.iceEnergy = 0; - } - }, - { - name: "superfluidity", - description: "freeze effects are applied
to a small area around the target", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isIceCrystals || tech.isSporeFreeze || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 2) || tech.relayIce || tech.isNeedleIce || (m.coupling && m.fieldMode < 3) || tech.iceIXOnDeath || tech.isIceShot - }, - requires: "a localized freeze effect", - effect() { - tech.isAoESlow = true - }, - remove() { - tech.isAoESlow = false - } - }, - { - name: "incendiary ammunition", - description: "shotgun, rivets, super balls, and drones
are loaded with explosives", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return (tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIceShot && !tech.isRivets && !tech.isFoamShot && !tech.isSporeWorm && !tech.isSporeFlea && !tech.isNeedles) || ((tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isSuperBounce && !tech.isFoamBall && !tech.isSuperHarm) || (tech.isRivets && !tech.isNailCrit) || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 3) || (tech.haveGunCheck("drones") && !tech.isForeverDrones && !tech.isDroneRadioactive && !tech.isDroneTeleport) - }, - requires: "shotgun, super balls, rivets, drones, not irradiated drones, burst drones, polyurethane, Zectron", - effect() { - tech.isIncendiary = true - }, - remove() { - tech.isIncendiary = false; - } - }, - { - name: "rebound", - description: `after they collide with a mob, super balls
gain speed, duration, and +33% damage`, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isIncendiary && !tech.isFoamBall - }, - requires: "super balls, not incendiary", - effect() { - tech.isSuperBounce = true - }, - remove() { - tech.isSuperBounce = false - } - }, - { - name: "Zectron", - description: `+75% super ball density and damage, but
after colliding with super balls -25% energy`, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isIncendiary - }, - requires: "super balls not incendiary ammunition", - effect() { - tech.isSuperHarm = true - }, - remove() { - tech.isSuperHarm = false - } - }, - { - name: "polyurethane foam", - description: "super balls and harpoons colliding with mobs
catalyzes a reaction that yields foam bubbles", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return ((tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isSuperBounce) || (tech.haveGunCheck("harpoon") && !tech.fragments) - }, - requires: "super balls, harpoon, not fragmentation", - effect() { - tech.isFoamBall = true; - }, - remove() { - tech.isFoamBall = false; - } - }, - { - name: "autocannon", - description: "fire +1 extra super ball
balls are quickly released in same direction", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("super balls") && !tech.oneSuperBall - }, - requires: "super balls, but not the tech super ball", - effect() { - tech.superBallDelay = true + remove() { + if (tech.superBallDelay) { + tech.superBallDelay = false; for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() } - }, - remove() { - if (tech.superBallDelay) { - tech.superBallDelay = false; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() - } - } + } + } + }, + { + name: "super duper", + description: `randomly fire +0, +1, +2, or +3 extra super balls`, + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.oneSuperBall + }, + requires: "super balls, not super ball", + effect() { + tech.extraSuperBalls += 4 + }, + remove() { + tech.extraSuperBalls = 0; + } + }, + { + name: "super ball", + description: "fire just 1 large super ball
that stuns mobs for 2 second", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.extraSuperBalls && !tech.superBallDelay + }, + requires: "super balls, not super duper or autocannon", + effect() { + tech.oneSuperBall = true; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() } }, - { - name: "super duper", - description: `randomly fire +0, +1, +2, or +3 extra super balls`, - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.oneSuperBall - }, - requires: "super balls, not super ball", - effect() { - tech.extraSuperBalls += 4 - }, - remove() { - tech.extraSuperBalls = 0; - } - }, - { - name: "super ball", - description: "fire just 1 large super ball
that stuns mobs for 2 second", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.extraSuperBalls && !tech.superBallDelay - }, - requires: "super balls, not super duper or autocannon", - effect() { - tech.oneSuperBall = true; + remove() { + if (tech.oneSuperBall) { + tech.oneSuperBall = false; for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() } - }, - remove() { - if (tech.oneSuperBall) { - tech.oneSuperBall = false; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() - } - } } + } + }, + { + name: "phase velocity", + description: "wave particles propagate faster as solids
+40% wave damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("wave") && !tech.isLongitudinal }, - { - name: "phase velocity", - description: "wave particles propagate faster as solids
+40% wave damage", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("wave") && !tech.isLongitudinal - }, - requires: "wave, not phonon", - effect() { - tech.isPhaseVelocity = true; - }, - remove() { - tech.isPhaseVelocity = false; - } + requires: "wave, not phonon", + effect() { + tech.isPhaseVelocity = true; }, - { - name: "amplitude", - description: "+37% wave damage
+37% wave particle amplitude", - isGunTech: true, - maxCount: 3, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("wave") - }, - requires: "wave", - effect() { - tech.waveFrequency *= 0.66 - tech.wavePacketDamage *= 1.37 - }, - remove() { - tech.waveFrequency = 0.2 - tech.wavePacketDamage = 1 - } + remove() { + tech.isPhaseVelocity = false; + } + }, + { + name: "amplitude", + description: "+37% wave damage
+37% wave particle amplitude", + isGunTech: true, + maxCount: 3, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("wave") }, - { - name: "propagation", - description: "–25% wave packet propagation speed
+41% wave damage", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("wave") - }, - requires: "wave", - effect() { - tech.waveBeamSpeed *= 0.75; - tech.waveBeamDamage += 0.27 * 0.41 //this sets base wave damage - }, - remove() { - tech.waveBeamSpeed = 11; - tech.waveBeamDamage = 0.27 //this sets base wave damage - } + requires: "wave", + effect() { + tech.waveFrequency *= 0.66 + tech.wavePacketDamage *= 1.37 }, - { - name: "bound state", - description: "wave packets reflect backwards 2 times
–33% range", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("wave") - }, - requires: "wave", - effect() { - tech.waveReflections += 2 - }, - remove() { - tech.waveReflections = 1 - } + remove() { + tech.waveFrequency = 0.2 + tech.wavePacketDamage = 1 + } + }, + { + name: "propagation", + description: "–25% wave packet propagation speed
+41% wave damage", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("wave") }, - { - name: "frequency", - description: `wave has unlimited ammo
-25% wave damage`, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed: () => tech.haveGunCheck("wave"), - requires: "wave", - effect() { - tech.isInfiniteWaveAmmo = true - b.guns[3].savedAmmo = b.guns[3].ammo - b.guns[3].ammo = Infinity + requires: "wave", + effect() { + tech.waveBeamSpeed *= 0.75; + tech.waveBeamDamage += 0.27 * 0.41 //this sets base wave damage + }, + remove() { + tech.waveBeamSpeed = 11; + tech.waveBeamDamage = 0.27 //this sets base wave damage + } + }, + { + name: "bound state", + description: "wave packets reflect backwards 2 times
–33% range", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("wave") + }, + requires: "wave", + effect() { + tech.waveReflections += 2 + }, + remove() { + tech.waveReflections = 1 + } + }, + { + name: "frequency", + description: `wave has unlimited ammo
-25% wave damage`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed: () => tech.haveGunCheck("wave"), + requires: "wave", + effect() { + tech.isInfiniteWaveAmmo = true + b.guns[3].savedAmmo = b.guns[3].ammo + b.guns[3].ammo = Infinity + simulation.updateGunHUD(); + }, + remove() { + tech.isInfiniteWaveAmmo = false + if (this.count > 0 && b.guns[3].savedAmmo !== undefined) { + b.guns[3].ammo = b.guns[3].savedAmmo simulation.updateGunHUD(); - }, - remove() { - tech.isInfiniteWaveAmmo = false - if (this.count > 0 && b.guns[3].savedAmmo !== undefined) { - b.guns[3].ammo = b.guns[3].savedAmmo - simulation.updateGunHUD(); - } else if (b.guns[3].ammo === Infinity) { - b.guns[3].ammo = 0 - } + } else if (b.guns[3].ammo === Infinity) { + b.guns[3].ammo = 0 } + } + }, + { + name: "phonon", //longitudinal //gravitational wave? + description: "waves are low frequency, high damage
expanding arcs that propagate through solids", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.haveGunCheck("wave") && !tech.isPhaseVelocity }, - { - name: "phonon", //longitudinal //gravitational wave? - description: "waves are low frequency, high damage
expanding arcs that propagate through solids", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.haveGunCheck("wave") && !tech.isPhaseVelocity - }, - requires: "wave, not phase velocity", - ammoScale: 6, - effect() { - tech.isLongitudinal = true; - b.guns[3].chooseFireMethod() - b.guns[3].ammoPack = b.guns[3].defaultAmmoPack / this.ammoScale - if (tech.isInfiniteWaveAmmo) { - b.guns[3].savedAmmo = Math.ceil(b.guns[3].savedAmmo / this.ammoScale); //used with low frequency - } else { - b.guns[3].ammo = Math.ceil(b.guns[3].ammo / this.ammoScale); - } - simulation.updateGunHUD(); - }, - remove() { - if (tech.isLongitudinal) { - tech.isLongitudinal = false; - b.guns[3].chooseFireMethod() - b.guns[3].ammoPack = b.guns[3].defaultAmmoPack - if (tech.isInfiniteWaveAmmo) { - b.guns[3].savedAmmo = Math.ceil(b.guns[3].savedAmmo * this.ammoScale); //used with low frequency - } else { - b.guns[3].ammo = Math.ceil(b.guns[3].ammo * this.ammoScale); - } - simulation.updateGunHUD(); - } + requires: "wave, not phase velocity", + ammoScale: 6, + effect() { + tech.isLongitudinal = true; + b.guns[3].chooseFireMethod() + b.guns[3].ammoPack = b.guns[3].defaultAmmoPack / this.ammoScale + if (tech.isInfiniteWaveAmmo) { + b.guns[3].savedAmmo = Math.ceil(b.guns[3].savedAmmo / this.ammoScale); //used with low frequency + } else { + b.guns[3].ammo = Math.ceil(b.guns[3].ammo / this.ammoScale); + } + simulation.updateGunHUD(); + }, + remove() { + if (tech.isLongitudinal) { tech.isLongitudinal = false; - } - }, - { - name: "isotropic", - description: "waves expand in all directions
–40% range and +50% damage", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isLongitudinal && tech.haveGunCheck("wave") && !tech.isBulletTeleport - }, - requires: "wave, phonon, not uncertainty principle", - effect() { - tech.is360Longitudinal = true; - b.guns[3].chooseFireMethod() - }, - remove() { - tech.is360Longitudinal = false; b.guns[3].chooseFireMethod() + b.guns[3].ammoPack = b.guns[3].defaultAmmoPack + if (tech.isInfiniteWaveAmmo) { + b.guns[3].savedAmmo = Math.ceil(b.guns[3].savedAmmo * this.ammoScale); //used with low frequency + } else { + b.guns[3].ammo = Math.ceil(b.guns[3].ammo * this.ammoScale); + } + simulation.updateGunHUD(); + } + tech.isLongitudinal = false; + } + }, + { + name: "isotropic", + description: "waves expand in all directions
–40% range and +50% damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isLongitudinal && tech.haveGunCheck("wave") && !tech.isBulletTeleport + }, + requires: "wave, phonon, not uncertainty principle", + effect() { + tech.is360Longitudinal = true; + b.guns[3].chooseFireMethod() + }, + remove() { + tech.is360Longitudinal = false; + b.guns[3].chooseFireMethod() + } + }, + { + name: "mechanical resonance", + description: "after a block gets vibrated by a phonon
there is a chance it's flung at nearby mobs", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isLongitudinal && tech.haveGunCheck("wave") + }, + requires: "wave, phonon", + effect() { + tech.isPhononBlock = true + }, + remove() { + tech.isPhononBlock = false + } + }, + { + name: "sympathetic resonance", + description: "after a mob gets vibrated by a phonon
a new resonance wave expands", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isLongitudinal && tech.haveGunCheck("wave") + }, + requires: "wave, phonon", + effect() { + tech.isPhononWave = true + }, + remove() { + tech.isPhononWave = false + } + }, + { + name: "cruise missile", + description: "+100% missile explosive damage, radius
–50% missile speed", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("missiles") && tech.missileFireCD === 45) || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 1) || tech.missileBotCount + }, + requires: "missiles, not launch system", + effect() { + tech.isMissileBig = true + }, + remove() { + tech.isMissileBig = false + } + }, + { + name: "ICBM", + description: "+75% missile explosive damage, radius
–50% missile speed", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("missiles") || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 1)) && tech.isMissileBig + }, + requires: "missiles, cruise missile", + effect() { + tech.isMissileBiggest = true + }, + remove() { + tech.isMissileBiggest = false + } + }, + { + name: "launch system", + description: `+500% missile gun fire rate
+20% missile ammo per ${powerUps.orb.ammo(1)}`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("missiles") && !tech.isMissileBig + }, + requires: "missiles, not cruise missile", + ammoBonus: 1.2, + effect() { + tech.missileFireCD = 10 + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "missiles") { + b.guns[i].ammoPack *= this.ammoBonus; + b.guns[i].ammo = Math.ceil(b.guns[i].ammo * this.ammoBonus); + simulation.updateGunHUD(); + break + } } }, - { - name: "mechanical resonance", - description: "after a block gets vibrated by a phonon
there is a chance it's flung at nearby mobs", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isLongitudinal && tech.haveGunCheck("wave") - }, - requires: "wave, phonon", - effect() { - tech.isPhononBlock = true - }, - remove() { - tech.isPhononBlock = false - } - }, - { - name: "sympathetic resonance", - description: "after a mob gets vibrated by a phonon
a new resonance wave expands", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isLongitudinal && tech.haveGunCheck("wave") - }, - requires: "wave, phonon", - effect() { - tech.isPhononWave = true - }, - remove() { - tech.isPhononWave = false - } - }, - { - name: "cruise missile", - description: "+100% missile explosive damage, radius
–50% missile speed", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.haveGunCheck("missiles") && tech.missileFireCD === 45) || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 1) || tech.missileBotCount - }, - requires: "missiles, not launch system", - effect() { - tech.isMissileBig = true - }, - remove() { - tech.isMissileBig = false - } - }, - { - name: "ICBM", - description: "+75% missile explosive damage, radius
–50% missile speed", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.haveGunCheck("missiles") || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 1)) && tech.isMissileBig - }, - requires: "missiles, cruise missile", - effect() { - tech.isMissileBiggest = true - }, - remove() { - tech.isMissileBiggest = false - } - }, - { - name: "launch system", - description: `+500% missile gun fire rate
+20% missile ammo per ${powerUps.orb.ammo(1)}`, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("missiles") && !tech.isMissileBig - }, - requires: "missiles, not cruise missile", - ammoBonus: 1.2, - effect() { - tech.missileFireCD = 10 + remove() { + if (tech.missileFireCD !== 45) { + tech.missileFireCD = 45; for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "missiles") { - b.guns[i].ammoPack *= this.ammoBonus; - b.guns[i].ammo = Math.ceil(b.guns[i].ammo * this.ammoBonus); + b.guns[i].ammoPack = 5; + b.guns[i].ammo = Math.ceil(b.guns[i].ammo / this.ammoBonus); simulation.updateGunHUD(); break } } - }, - remove() { - if (tech.missileFireCD !== 45) { - tech.missileFireCD = 45; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "missiles") { - b.guns[i].ammoPack = 5; - b.guns[i].ammo = Math.ceil(b.guns[i].ammo / this.ammoBonus); - simulation.updateGunHUD(); + } + } + }, + { + name: "missile-bot", + link: `missile-bot`, + description: `use ${powerUps.orb.research(1)}to trade your missile gun
for a bot that fires missiles`, + isGunTech: true, + isRemoveGun: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { + return tech.haveGunCheck("missiles", false) && tech.missileFireCD === 45 && (build.isExperimentSelection || powerUps.research.count > 0) + }, + requires: "missiles, not launch system", + effect() { + tech.missileBotCount++; + b.missileBot(); + if (tech.haveGunCheck("missiles", false)) b.removeGun("missiles") //remove your last gun + for (let i = 0; i < 1; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + if (this.count) { + tech.missileBotCount = 0; + b.clearPermanentBots(); + b.respawnBots(); + if (!tech.haveGunCheck("missiles", false)) b.giveGuns("missiles") + powerUps.research.changeRerolls(1) + } + } + }, + { + name: "iridium-192", + description: "explosions release gamma radiation
+100% explosion damage over 4 seconds", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isImmuneExplosion && tech.explosiveRadius === 1 && !tech.isSmallExplosion && !tech.isBlockExplode && !tech.fragments && (tech.haveGunCheck("missiles") || tech.missileBotCount || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.isPulseLaser || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 1) || tech.isBoomBotUpgrade || tech.isTokamak) + }, + requires: "an explosive damage source, not ammonium nitrate, nitroglycerin, chain reaction, fragmentation, electric armor", + effect() { + tech.isExplodeRadio = true; //iridium-192 + }, + remove() { + tech.isExplodeRadio = false; + } + }, + { + name: "fragmentation", + description: "some detonations and collisions eject nails
blocks, grenades, missiles, rivets, harpoon", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isExplodeRadio && ((tech.haveGunCheck("harpoon") && !tech.isFoamBall) || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("missiles") || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 1) || tech.missileBotCount || tech.isRivets || tech.blockDamage > 0.075) + }, + requires: "grenades, missiles, rivets, harpoon, or mass driver, not iridium-192, not polyurethane foam", + effect() { + tech.fragments++ + }, + remove() { + tech.fragments = 0 + } + }, + { + name: "ammonium nitrate", + description: "+24% explosive damage, radius", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() + }, + requires: "an explosive damage source, not iridium-192", + effect() { + tech.explosiveRadius += 0.24; + }, + remove() { + tech.explosiveRadius = 1; + } + }, + { + name: "nitroglycerin", + description: "+66% explosive damage
–33% explosive radius", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() && !tech.isExplosionHarm + }, + requires: "an explosive damage source, not iridium-192, acetone peroxide", + effect() { + tech.isSmallExplosion = true; + }, + remove() { + tech.isSmallExplosion = false; + } + }, + { + name: "acetone peroxide", + description: "+70% explosive radius
–40% explosive defense", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBadRandomOption: true, + allowed() { + return tech.hasExplosiveDamageCheck() && !tech.isSmallExplosion + }, + requires: "an explosive damage source, not nitroglycerin", + effect() { + tech.isExplosionHarm = true; + }, + remove() { + tech.isExplosionHarm = false; + } + }, + { + name: "shock wave", + description: "mines and sporangium stun for 3-5 seconds
explosions stun for 0.5 seconds", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.haveGunCheck("spores") || tech.haveGunCheck("mine") || (!tech.isExplodeRadio && tech.hasExplosiveDamageCheck()) + }, + requires: "mine, spores, an explosive damage source, not iridium-192", + effect() { + tech.isStun = true; + }, + remove() { + tech.isStun = false; + } + }, + { + name: "shaped charge", + description: `use ${powerUps.orb.research(3)} to dynamically reduce
all explosions to prevent health loss`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isImmuneExplosion && (build.isExperimentSelection || powerUps.research.count > 2) && (tech.haveGunCheck("missiles") || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 1) || tech.missileBotCount > 0 || tech.isIncendiary || tech.isPulseLaser || tech.isTokamak || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb)) + }, + requires: "an explosive damage source, not rocket propelled grenade", + effect() { + tech.isSmartRadius = true; + for (let i = 0; i < 3; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.isSmartRadius = false; + if (this.count > 0) powerUps.research.changeRerolls(3) + } + }, + // { + // name: "electric armor", + // // description: "explosions do no defense
while your energy is above 98%", + // description: "instead of causing health loss, explosions
drain 12 energy and have more knockback", + // isGunTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return !tech.isSmartRadius && !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() + // }, + // requires: "an explosive damage source, not iridium-192", + // effect() { + // tech.isImmuneExplosion = true; + // }, + // remove() { + // tech.isImmuneExplosion = false; + // } + // }, + { + name: "MIRV", + description: "fire +1 missile or grenade per shot
–12% explosion damage and radius", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("missiles") || tech.missileBotCount || tech.haveGunCheck("grenades") + }, + requires: "missiles, grenades", + effect() { + tech.missileCount++; + }, + remove() { + tech.missileCount = 1; + } + }, + { + name: "rocket-propelled grenade", + description: "grenades explode on map collisions
explosions drain energy, not health", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("grenades") && !tech.isVacuumBomb + }, + requires: "grenades, not vacuum bomb", + effect() { + tech.isImmuneExplosion = true; + tech.isRPG = true; + b.setGrenadeMode() + }, + remove() { + tech.isImmuneExplosion = false; + tech.isRPG = false; + b.setGrenadeMode() + } + }, + { + name: "vacuum bomb", + description: "grenades fire slower, explode bigger,
and suck everything towards them", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("grenades") && !tech.isNeutronBomb && !tech.isBlockExplode && !tech.isRPG + }, + requires: "grenades, not neutron bomb, chain reaction, RPG", + effect() { + tech.isVacuumBomb = true; + b.setGrenadeMode() + }, + remove() { + tech.isVacuumBomb = false; + b.setGrenadeMode() + } + }, + { + name: "chain reaction", + description: "+33% grenade radius and damage
blocks caught in explosions also explode", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("grenades") && !tech.isExplodeRadio && !tech.isNeutronBomb && !tech.isVacuumBomb + }, + requires: "grenades, not iridium-192, neutron bomb, vacuum bomb", + effect() { + tech.isBlockExplode = true; //chain reaction + }, + remove() { + tech.isBlockExplode = false; + } + }, + { + name: "flame test", + description: "after grenades detonate they release
a colorful cluster of small explosions", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("grenades") && !tech.isNeutronBomb && !tech.isCircleExplode && !tech.isPetalsExplode + }, + requires: "grenades, not neutron bomb, pyrotechnics, fireworks", + effect() { + tech.isClusterExplode = true; + }, + remove() { + tech.isClusterExplode = false; + } + }, + { + name: "pyrotechnics", + description: "after grenades detonate they release
a colorful circle of explosions", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("grenades") && !tech.isNeutronBomb && !tech.isClusterExplode && !tech.isPetalsExplode + }, + requires: "grenades, not neutron bomb, flame test, fireworks", + effect() { + tech.isCircleExplode = true; + }, + remove() { + tech.isCircleExplode = false; + } + }, + { + name: "fireworks", + description: "after grenades detonate they release
colorful petals of explosions", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("grenades") && !tech.isNeutronBomb && !tech.isClusterExplode && !tech.isCircleExplode + }, + requires: "grenades, not neutron bomb, pyrotechnics, flame test", + effect() { + tech.isPetalsExplode = true; + }, + remove() { + tech.isPetalsExplode = false; + } + }, + { + name: "neutron bomb", + description: "grenades are irradiated with Cf-252
does radioactive damage over time", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("grenades") && !tech.fragments && !tech.isVacuumBomb && !tech.isExplodeRadio && !tech.isBlockExplode && !tech.isClusterExplode && !tech.isPetalsExplode && !tech.isCircleExplode + }, + requires: "grenades, not fragmentation, vacuum bomb, iridium-192, pyrotechnics, fireworks, flame test, chain reaction", + effect() { + tech.isNeutronBomb = true; + b.setGrenadeMode() + }, + remove() { + tech.isNeutronBomb = false; + b.setGrenadeMode() + } + }, + { + name: "vacuum permittivity", + description: "+20% radioactive range
objects in range of the bomb are slowed", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isNeutronBomb + }, + requires: "grenades, neutron bomb", + effect() { + tech.isNeutronSlow = true + }, + remove() { + tech.isNeutronSlow = false + } + }, + { + name: "radioactive contamination", + description: "after a mob or shield dies,
leftover radiation spreads to a nearby mob", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isNailRadiation || tech.isWormholeDamage || tech.isNeutronBomb || tech.isExplodeRadio || tech.isBlockRadiation + }, + requires: "radiation damage source", + effect() { + tech.isRadioactive = true + }, + remove() { + tech.isRadioactive = false + } + }, + { + name: "nuclear transmutation", + description: "+47% radiation damage
nail, drone, neutron bomb, iridium, cosmic string, deflect", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isNailRadiation || tech.isWormholeDamage || tech.isNeutronBomb || tech.isExplodeRadio || tech.isBlockRadiation || tech.isDroneRadioactive + }, + requires: "radiation damage source", + effect() { + tech.radioactiveDamage += 1.47 + }, + remove() { + tech.radioactiveDamage = 1 + } + }, + { + name: "water shielding", + link: `water shielding`, + description: "radioactive effects on you are reduced by 75%
neutron bomb, drones, explosions, slime", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isNeutronBomb || tech.isDroneRadioactive || tech.isExplodeRadio + }, + requires: "neutron bomb, irradiated drones, iridium-192", + effect() { + tech.isRadioactiveResistance = true + }, + remove() { + tech.isRadioactiveResistance = false + } + }, + { + name: "ricochet", + description: "after nails hit a mob they rebound towards
a new mob with +180% damage per bounce", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + // return (tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles) || (tech.haveGunCheck("mines")) + return tech.isMineDrop || tech.isNailBotUpgrade || tech.fragments || tech.nailsDeathMob || (tech.haveGunCheck("mine") && !(tech.isLaserMine || tech.isFoamMine || tech.isSuperMine)) || (tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles) || (tech.haveGunCheck("shotgun") && (tech.isNeedles || tech.isNailShot) && !tech.isRivets && !tech.isNeedles) + }, + // + requires: "nail gun, not rotary cannon, rivets, or needles", + effect() { + tech.isRicochet = true + }, + remove() { + tech.isRicochet = false + } + }, + { + name: "booby trap", + description: "50% chance to drop a mine from power ups
+36% JUNK to tech pool", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("mine") + }, + requires: "mines", + effect() { + tech.isMineDrop = true; + if (tech.isMineDrop) b.mine(m.pos, { + x: 0, + y: 0 + }, 0) + this.refundAmount += tech.addJunkTechToPool(0.36) + }, + refundAmount: 0, + remove() { + tech.isMineDrop = false; + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "elephants toothpaste", + description: "instead of nails mines catalyze a reaction
that yields foam bubbles", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("mine") && !tech.isSuperMine && !tech.isRicochet && !tech.isNailRadiation && !tech.isNailCrit + }, + requires: "mines, not blast ball, ricochet, irradiated nails, supercritical fission", + effect() { + tech.isFoamMine = true; + }, + remove() { + tech.isFoamMine = false; + } + }, + { + name: "blast ball", + descriptionFunction() { + return `instead of nails mines fire bouncy balls` + }, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("mine") && !tech.isFoamMine && !tech.isRicochet && !tech.isNailRadiation && !tech.isNailCrit + }, + requires: "mines, not elephants toothpaste, ricochet, irradiated nails, supercritical fission", + effect() { + tech.isSuperMine = true; + }, + remove() { + tech.isSuperMine = false; + } + }, + { + name: "laser-mines", + link: `laser-mines`, + description: "mines laid while you are crouched
use energy to emit 3 unaimed lasers", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("mine") + }, + requires: "mines", + effect() { + tech.isLaserMine = true; + }, + remove() { + tech.isLaserMine = false; + } + }, + { + name: "sentry", + descriptionFunction() { + return `mines fire one ${b.guns[10].nameString()} at a time
mines fire 50% more ${b.guns[10].nameString('s')}` + }, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("mine") && !tech.isFoamMine + }, + requires: "mines, not elephants toothpaste", + effect() { + tech.isMineSentry = true; + }, + remove() { + tech.isMineSentry = false; + } + }, + { + name: "extended magazine", + descriptionFunction() { + return `sentry mines fire 50% more ${b.guns[10].nameString('s')}` + }, + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("mine") && tech.isMineSentry + }, + requires: "mines, sentry", + effect() { + tech.sentryAmmo += 17; + }, + remove() { + tech.sentryAmmo = 33; + } + }, + { + name: "mycelial fragmentation", + link: `mycelial fragmentation`, + description: "during their growth phase
+70% sporangium discharge", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("spores") + }, + requires: "spores", + effect() { + tech.isSporeGrowth = true + }, + remove() { + tech.isSporeGrowth = false + } + }, + { + name: "cordyceps", + // descriptionFunction() { + // return `mobs infected by ${b.guns[6].nameString('s')} have a 5% chance
to resurrect and attack other mobs` + // }, + description: "sporangium infect mobs they attach to
infected mobs resurrect and attack other mobs", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("spores") + }, + requires: "spores", + effect() { + tech.isZombieMobs = true + }, + remove() { + tech.isZombieMobs = false + } + }, + { + name: "colony", + description: "+50% sporangium discharge
40% chance to discharge something different", + link: `colony`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("spores") + }, + requires: "spores", + effect() { + tech.isSporeColony = true + }, + remove() { + tech.isSporeColony = false + } + }, + { + name: "cryodesiccation", + descriptionFunction() { + return `+25% sporangium discharge
${b.guns[6].nameString('s')} freeze mobs for 1.5 second` + }, + // description: "+25% sporangium discharge
spores freeze mobs for 1.5 second", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 0) || tech.isSporeWorm || tech.isSporeFlea + }, + requires: "spores", + effect() { + tech.isSporeFreeze = true + }, + remove() { + tech.isSporeFreeze = false + } + }, + { + name: "flagella", + descriptionFunction() { + return `+50% ${b.guns[6].nameString()} acceleration
if they can't find a target ${b.guns[6].nameString('s')} follow you` + }, + // description: "+50% spore acceleration
if they can't find a target spores follow you", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 0) || tech.isSporeWorm || tech.isSporeFlea + }, + requires: "spores", + effect() { + tech.isSporeFollow = true + }, + remove() { + tech.isSporeFollow = false + } + }, + { + name: "junk DNA", + descriptionFunction() { + return `+53% ${b.guns[6].nameString()} damage per JUNKtech (${(53 * tech.junkCount).toFixed(0)}%)
+50% JUNK to tech pool` + }, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 0) || tech.isSporeWorm || tech.isSporeFlea + }, + requires: "spores", + effect() { + tech.isJunkDNA = true + this.refundAmount += tech.addJunkTechToPool(0.5) + }, + refundAmount: 0, + remove() { + tech.isJunkDNA = false + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + // { + // name: "junk DNA", + // //increase damage by 10% for each JUNK tech percent in the tech pool, remove all JUNK tech, + // descriptionFunction() { return `+50% ${b.guns[6].nameString()} damage
+15% JUNK to tech pool` }, + // isGunTech: true, + // maxCount: 1, + // count: 0, + // frequency: 3, + // frequencyDefault: 3, + // allowed() { + // return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 0) || tech.isSporeWorm || tech.isSporeFlea + // }, + // requires: "spores", + // effect() { + // tech.isSporeWorm = true + // this.refundAmount += tech.addJunkTechToPool(0.15) + // }, + // refundAmount: 0, + // remove() { + // tech.isSporeWorm = false + // if (this.count > 0 && this.refundAmount > 0) { + // tech.removeJunkTechFromPool(this.refundAmount) + // this.refundAmount = 0 + // } + // } + // }, + { + name: "mutualism", + descriptionFunction() { + return `+200% ${b.guns[6].nameString()} damage
${b.guns[6].nameString('s')} borrow 1 health until they die` + }, + // description: `+150% ${b.guns[6].name()} damage
spores borrow 0.5 health until they die`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 0)) || tech.isSporeWorm || tech.isSporeFlea + }, + requires: "spores", + effect() { + tech.isMutualism = true + }, + remove() { + tech.isMutualism = false + } + }, + { + name: "necrophage", + description: "if foam, fleas, or worms kill their target
they grow 3 copies", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("foam") || tech.isFoamBall || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isSporeWorm || tech.isSporeFlea || tech.isFoamMine + }, + requires: "foam, spores, worms, fleas", + effect() { + tech.isSpawnBulletsOnDeath = true + }, + remove() { + tech.isSpawnBulletsOnDeath = false; + } + }, + { + name: "siphonaptera", + description: "sporangium and shotgun hatch fleas", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 0) || (tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isNeedles && !tech.isNailShot)) && !tech.isSporeWorm + }, + requires: "spores, not worms", + effect() { + tech.isSporeFlea = true + }, + remove() { + tech.isSporeFlea = false + + } + }, + { + name: "nematodes", + description: "shotgun and sporangium hatch worms", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 0) || (tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isNeedles && !tech.isNailShot)) && !tech.isSporeFlea + }, + requires: "spores, not fleas", + effect() { + tech.isSporeWorm = true + }, + remove() { + tech.isSporeWorm = false + } + }, + { + name: "K-selection", + description: "+37% worm and flea damage", + isGunTech: true, + maxCount: 3, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isSporeWorm || tech.isSporeFlea + }, + requires: "spores, shotgun, worms, fleas", + effect() { + tech.wormSize++ + }, + remove() { + tech.wormSize = 0 + } + }, + { + name: "path integration", + descriptionFunction() { + return `drones and ${b.guns[6].nameString("s")}
travel with you through levels` + }, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.isSporeFollow && (tech.haveGunCheck("spores") || (tech.haveGunCheck("shotgun") && tech.isSporeWorm))) || tech.haveGunCheck("drones") || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && (simulation.molecularMode === 0 || simulation.molecularMode === 3)) + }, + requires: "spores, worms, flagella, drones", + effect() { + tech.isDronesTravel = true + }, + remove() { + tech.isDronesTravel = false + } + }, + { + name: "reduced tolerances", + link: `reduced tolerances`, + description: `+66% drones per ${powerUps.orb.ammo()} and energy
–40% drone duration`, + isGunTech: true, + maxCount: 3, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isDroneRadioactive && (tech.haveGunCheck("drones") || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 3)) + }, + requires: "drones, not irradiated drones", + effect() { + tech.droneCycleReduction = Math.pow(0.6, 1 + this.count) + tech.droneEnergyReduction = Math.pow(0.333, 1 + this.count) + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "drones") { + const scale = Math.pow(3, this.count + 1) + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * scale + } + } + }, + remove() { + tech.droneCycleReduction = 1 + tech.droneEnergyReduction = 1 + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "drones") b.guns[i].ammoPack = b.guns[i].defaultAmmoPack + } + } + }, + { + name: "delivery drone", + description: "if a drone picks up a power up,
it becomes larger, faster, and more durable", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 3) + }, + requires: "drones", + effect() { + tech.isDroneGrab = true + }, + remove() { + tech.isDroneGrab = false + } + }, + { + name: "drone repair", + link: `drone repair`, + description: "after a drone expires it redeploys
for a 20% chance to use 1 drone ammo", + // description: "broken drones repair if the drone gun is active
repairing has a 25% chance to use 1 drone", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("drones") + }, + requires: "drones", + effect() { + tech.isDroneRespawn = true + }, + remove() { + tech.isDroneRespawn = false + } + }, + { + name: "brushless motor", + description: "drones rapidly rush towards their target
+33% drone collision damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 3)) && !tech.isDroneRadioactive && !tech.isIncendiary + }, + requires: "drones, molecular assembler, not irradiated drones, incendiary", + effect() { + tech.isDroneTeleport = true + }, + remove() { + tech.isDroneTeleport = false + } + }, + { + name: "axial flux motor", + description: "+66% drones rush frequency
+44% drone collision damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isDroneTeleport + }, + requires: "drones, brushless motor", + effect() { + tech.isDroneFastLook = true + }, + remove() { + tech.isDroneFastLook = false + } + }, + { + name: "irradiated drones", + link: `irradiated drones`, + description: `the space around drones is irradiated
–75% drones per ${powerUps.orb.ammo()} and energy`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.droneCycleReduction === 1 && !tech.isIncendiary && !tech.isDroneTeleport && (tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 3)) + }, + requires: "drones, not reduced tolerances, incendiary, torque bursts", + effect() { + tech.isDroneRadioactive = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "drones") { + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * 0.25 + b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 0.25) + simulation.makeGunHUD(); + } + } + }, + remove() { + if (tech.isDroneRadioactive) { + tech.isDroneRadioactive = false + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "drones") { + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack + b.guns[i].ammo = b.guns[i].ammo * 4 + simulation.makeGunHUD(); + } + } + } + } + }, + { + name: "beta radiation", //"control rod ejection", + description: "–50% drone duration
+100% drone radiation damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isDroneRadioactive + }, + requires: "drones, irradiated drones", + effect() { + tech.droneRadioDamage = 2 + }, + remove() { + tech.droneRadioDamage = 1 + } + }, + { + name: "orthocyclic winding", + link: `orthocyclic winding`, + description: "+66% drone acceleration
+33% radiation damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isDroneRadioactive + }, + requires: "drones, irradiated drones", + effect() { + tech.isFastDrones = true + }, + remove() { + tech.isFastDrones = false + } + }, + { + name: "fault tolerance", + description: `use ${powerUps.orb.research(2)}to trade your drone gun
for 5 drones that last forever`, + isGunTech: true, + isRemoveGun: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.haveGunCheck("drones", false) && !tech.isDroneRespawn && tech.isBulletsLastLonger === 1 && !tech.isDronesTravel && (build.isExperimentSelection || powerUps.research.count > 1) + }, + requires: "drones, not drone repair, anti-shear topology, autonomous navigation", + effect() { + const num = 5 + tech.isForeverDrones += num + if (tech.haveGunCheck("drones", false)) b.removeGun("drones") + //spawn drones + if (tech.isDroneRadioactive) { + for (let i = 0; i < num * 0.25; i++) { + b.droneRadioactive({ + x: m.pos.x + 30 * (Math.random() - 0.5), + y: m.pos.y + 30 * (Math.random() - 0.5) + }, 5) + bullet[bullet.length - 1].endCycle = Infinity + } + } else { + for (let i = 0; i < num; i++) { + b.drone({ + x: m.pos.x + 30 * (Math.random() - 0.5), + y: m.pos.y + 30 * (Math.random() - 0.5) + }, 5) + bullet[bullet.length - 1].endCycle = Infinity + } + } + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.isForeverDrones = 0 + if (this.count && !tech.haveGunCheck("drones", false)) b.giveGuns("drones") + if (this.count > 0) powerUps.research.changeRerolls(2) + } + }, + { + name: "surfactant", + description: `use ${powerUps.orb.research(2)}to trade your foam gun
for 2 foam-bots and foam-bot upgrade`, + isGunTech: true, + isRemoveGun: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + isNonRefundable: true, + requires: "foam gun, not bot upgrades, fractionation, pressure vessel", + allowed() { + return tech.haveGunCheck("foam", false) && !b.hasBotUpgrade() && !tech.isAmmoFoamSize && !tech.isFoamPressure && (build.isExperimentSelection || powerUps.research.count > 1) + }, + effect() { + tech.giveTech("foam-bot upgrade") + for (let i = 0; i < 2; i++) { + b.foamBot() + tech.foamBotCount++; + } + simulation.makeTextLog(`tech.isFoamBotUpgrade = true`) + if (tech.haveGunCheck("foam", false)) b.removeGun("foam") + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + // if (this.count) { + // b.clearPermanentBots(); + // b.respawnBots(); + // if (!tech.haveGunCheck("foam")) b.giveGuns("foam") + // } + // if (this.count > 0) powerUps.research.changeRerolls(2) + } + }, + { + name: "electrostatic induction", + description: "foam bubbles are electrically charged
causing attraction to nearby mobs", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isBulletTeleport && (tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine) + }, + requires: "foam, not uncertainty", + effect() { + tech.isFoamAttract = true + }, + remove() { + tech.isFoamAttract = false + } + }, + { + name: "uncertainty principle", + description: "foam and wave positions are erratic
+53% foam and wave damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return (!tech.isFoamAttract && (tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine)) || (tech.haveGunCheck("wave") && !tech.is360Longitudinal) + }, + requires: "foam, wave, not isotropic, electrostatic induction", + effect() { + tech.isBulletTeleport = true + }, + remove() { + tech.isBulletTeleport = false; + } + }, + { + name: "aerogel", + description: "–50% foam duration and foam bubbles float
+180% foam damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine + }, + requires: "foam", + effect() { + tech.isFastFoam = true + tech.foamGravity = -0.0003 + }, + remove() { + tech.isFastFoam = false; + tech.foamGravity = 0.00008 + } + }, + { + name: "surface tension", + description: "+43% foam damage", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine + }, + requires: "foam", + effect() { + tech.foamDamage += 0.011 * 0.43 + }, + remove() { + tech.foamDamage = 0.011; + } + }, + { + name: "foam fractionation", + description: "if you have below 300 ammo
+100% foam gun bubble size", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("foam") + }, + requires: "foam", + effect() { + tech.isAmmoFoamSize = true + }, + remove() { + tech.isAmmoFoamSize = false; + } + }, + { + name: "ideal gas law", + description: `remove all current foam ammo
+1200% foam ammo per ${powerUps.orb.ammo(1)}`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("foam") && !tech.isEnergyNoAmmo + }, + requires: "foam, not non-renewables", + ammoLost: 0, + effect() { + b.guns[8].ammoPack = b.guns[8].ammoPack * 12; + this.ammoLost = b.guns[8].ammo + b.guns[8].ammo = 0 + simulation.updateGunHUD() + }, + remove() { + b.guns[8].ammoPack = 24 + if (this.count) { + b.guns[8].ammo += this.ammoLost + simulation.updateGunHUD() + } + } + }, + { + name: "pressure vessel", + description: "build up charge while firing foam gun
after firing discharge foam bubbles", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("foam") + }, + requires: "foam", + effect() { + tech.isFoamPressure = true; + b.guns[8].chooseFireMethod() + }, + remove() { + tech.isFoamPressure = false; + b.guns[8].chooseFireMethod() + } + }, + { + name: "capacitor bank", + // description: "charge effects build up almost instantly
throwing blocks, foam, railgun, pulse, tokamak", + descriptionFunction() { + return `charge effects build up almost instantly
throwing, ${tech.haveGunCheck("foam", false) ? "foam" : "foam"}, ${tech.isPlasmaBall ? "plasma ball" : "plasma ball"}, ${tech.isRailGun ? "railgun" : "railgun"}, ${tech.isPulseLaser ? "pulse" : "pulse"}, ${tech.isTokamak ? "tokamak" : "tokamak"}` + }, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.blockDamage > 0.075 || tech.isRailGun || (tech.haveGunCheck("foam") && tech.isFoamPressure) || tech.isTokamak || tech.isPulseLaser || tech.isPlasmaBall + }, + requires: "mass driver, railgun, foam, pressure vessel, pulse, tokamak, plasma ball", + effect() { + tech.isCapacitor = true; + }, + remove() { + tech.isCapacitor = false; + } + }, + { + name: "Bitter electromagnet", + descriptionFunction() { + return `railgun charges +33% slower
+100% harpoon density and damage` + }, + isGunTech: true, + maxCount: 3, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") && tech.isRailGun + }, + requires: "harpoon, railgun", + effect() { + tech.railChargeRate *= 1.06 + tech.harpoonDensity += 0.0065 + }, + remove() { + tech.railChargeRate = 0.97; + tech.harpoonDensity = 0.0065 + } + }, + { + name: "railgun", + description: `hold fire to charge harpoon and release to launch
harpoons can't retract`, + // description: `+900% harpoon ammo, but it can't retract
+50% harpoon density and damage`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") && !tech.isFilament && !tech.isHarpoonPowerUp && !tech.isGrapple && !tech.isBoostReplaceAmmo + }, + requires: "harpoon, not UHMWPE, induction furnace, grappling hook, quasiparticles", + ammoBonus: 9, + effect() { + tech.isRailGun = true; + b.guns[9].chooseFireMethod() + b.guns[9].ammoPack = 5; + b.guns[9].ammo = b.guns[9].ammo * 6; + simulation.updateGunHUD(); + }, + remove() { + if (tech.isRailGun) { + tech.isRailGun = false; + b.guns[9].chooseFireMethod() + b.guns[9].ammoPack = 1.7; + b.guns[9].ammo = Math.ceil(b.guns[9].ammo / 6); + simulation.updateGunHUD(); + } + } + }, + { + name: "grappling hook", + description: `harpoons attach to the map and pull you
your rope extends while holding fire`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") && !tech.isFilament && !tech.isHarpoonPowerUp && !tech.isRailGun && !tech.isFireMoveLock + }, + requires: "harpoon, not railgun, UHMWPE, induction furnace, Higgs mechanism", + effect() { + tech.isGrapple = true; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "harpoon") b.guns[i].chooseFireMethod() + } + }, + remove() { + if (tech.isGrapple) { + tech.isGrapple = false; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "harpoon") b.guns[i].chooseFireMethod() + } + } + } + }, + { + name: "bulk modulus", + description: `while grappling become invulnerable
drain energy`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") && tech.isGrapple && !tech.isRailEnergy + }, + requires: "grappling hook, not alternator", + effect() { + tech.isImmuneGrapple = true; + }, + remove() { + tech.isImmuneGrapple = false + } + }, + { + name: "alternator", + description: "+90% harpoon energy efficiency", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") && !tech.isImmuneGrapple + }, + requires: "harpoon, not bulk modulus", + effect() { + tech.isRailEnergy = true; + }, + remove() { + tech.isRailEnergy = false; + } + }, + { + name: "Bessemer process", + descriptionFunction() { + return `+${(10 * Math.sqrt(b.guns[9].ammo)).toFixed(0)}% harpoon size and damage
(1/10 √ harpoon ammo)` + }, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") && !tech.isShieldPierce + }, + requires: "harpoon, not ceramics", + effect() { + tech.isLargeHarpoon = true; + }, + remove() { + tech.isLargeHarpoon = false; + } + }, + { + name: "smelting", + descriptionFunction() { + return `forge ${this.removeAmmo()} ammo into a new harpoon
fire +1 harpoon with each shot` + }, + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + ammoRemoved: 0, + removeAmmo() { + return (tech.isRailGun ? 5 : 1) * (2 + 2 * this.count) + }, + allowed() { + return tech.haveGunCheck("harpoon") && b.guns[9].ammo >= this.removeAmmo() + }, + requires: "harpoon", + effect() { + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "harpoon") { + const removeAmmo = this.removeAmmo() + this.ammoRemoved += removeAmmo + b.guns[i].ammo -= removeAmmo + if (b.guns[i].ammo < 0) b.guns[i].ammo = 0 + simulation.updateGunHUD(); + tech.extraHarpoons++; + break + } + } + }, + remove() { + if (tech.extraHarpoons) { + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "harpoon") { + b.guns[i].ammo += this.ammoRemoved + simulation.updateGunHUD(); + break + } + } + } + this.ammoRemoved = 0 + tech.extraHarpoons = 0; + } + }, + { + name: "UHMWPE", + descriptionFunction() { + return `+${(b.guns[9].ammo * 1.25).toFixed(0)}% harpoon rope length
(1/80 of harpoon ammo)` + }, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") && !tech.isRailGun && !tech.isGrapple + }, + requires: "harpoon, not grappling hook, railgun", + effect() { + tech.isFilament = true; + }, + remove() { + tech.isFilament = false; + } + }, + { + name: "induction furnace", + description: "after using harpoon to collect a power up
+600% harpoon damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") && !tech.isRailGun && !tech.isGrapple + }, + requires: "harpoon, not grappling hook, railgun", + effect() { + tech.isHarpoonPowerUp = true + }, + remove() { + tech.isHarpoonPowerUp = false + } + }, + { + name: "quasiparticles", + descriptionFunction() { + return `convert current and future ${powerUps.orb.ammo(1)} into ${powerUps.orb.boost(1)} which
give +${(powerUps.boost.damage * 100).toFixed(0)}% damage for ${(powerUps.boost.duration / 60).toFixed(0)} seconds` + }, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return ((tech.haveGunCheck("wave") && !tech.isInfiniteWaveAmmo) || tech.haveGunCheck("laser") || (tech.haveGunCheck("harpoon") && !tech.isRailGun)) && !tech.isEnergyNoAmmo + }, + requires: "harpoon, laser, wave, frequency, not railgun, non-renewables", + effect() { + tech.isBoostReplaceAmmo = true + for (let i = powerUp.length - 1; i > -1; i--) { + if (powerUp[i].name === "ammo") { + powerUps.spawn(powerUp[i].position.x + 50 * (Math.random() - 0.5), powerUp[i].position.y + 50 * (Math.random() - 0.5), "boost"); + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + } + } + + }, + remove() { + tech.isBoostReplaceAmmo = false + } + }, + { + name: "optical amplifier", + description: "gain 3 random laser guntech
laser only turns off if you have no energy", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isNonRefundable: true, + allowed() { + return tech.haveGunCheck("laser") && !tech.isPulseLaser + }, + requires: "laser gun, not pulse", + effect() { + let techGiven = 0 + for (let j = 0; j < 3; j++) { + const names = ["quasiparticles", "lens", "compound lens", "arc length", "infrared diode", "free-electron laser", "dye laser", "relativistic momentum", "specular reflection", "diffraction grating", "diffuse beam", "output coupler", "slow light", "laser-bot", "laser-bot upgrade"] + //convert names into indexes + const options = [] + for (let i = 0; i < names.length; i++) { + for (let k = 0; k < tech.tech.length; k++) { + if (tech.tech[k].name === names[i]) { + options.push(k) break } } } - } - }, - { - name: "missile-bot", - link: `missile-bot`, - description: `use ${powerUps.orb.research(1)}to trade your missile gun
for a bot that fires missiles`, - isGunTech: true, - isRemoveGun: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBot: true, - isBotTech: true, - allowed() { - return tech.haveGunCheck("missiles", false) && tech.missileFireCD === 45 && (build.isExperimentSelection || powerUps.research.count > 0) - }, - requires: "missiles, not launch system", - effect() { - tech.missileBotCount++; - b.missileBot(); - if (tech.haveGunCheck("missiles", false)) b.removeGun("missiles") //remove your last gun - for (let i = 0; i < 1; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - }, - remove() { - if (this.count) { - tech.missileBotCount = 0; - b.clearPermanentBots(); - b.respawnBots(); - if (!tech.haveGunCheck("missiles", false)) b.giveGuns("missiles") - powerUps.research.changeRerolls(1) - } - } - }, - { - name: "iridium-192", - description: "explosions release gamma radiation
+100% explosion damage over 4 seconds", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isImmuneExplosion && tech.explosiveRadius === 1 && !tech.isSmallExplosion && !tech.isBlockExplode && !tech.fragments && (tech.haveGunCheck("missiles") || tech.missileBotCount || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.isPulseLaser || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 1) || tech.isBoomBotUpgrade || tech.isTokamak) - }, - requires: "an explosive damage source, not ammonium nitrate, nitroglycerin, chain reaction, fragmentation, electric armor", - effect() { - tech.isExplodeRadio = true; //iridium-192 - }, - remove() { - tech.isExplodeRadio = false; - } - }, - { - name: "fragmentation", - description: "some detonations and collisions eject nails
blocks, grenades, missiles, rivets, harpoon", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isExplodeRadio && ((tech.haveGunCheck("harpoon") && !tech.isFoamBall) || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("missiles") || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 1) || tech.missileBotCount || tech.isRivets || tech.blockDamage > 0.075) - }, - requires: "grenades, missiles, rivets, harpoon, or mass driver, not iridium-192, not polyurethane foam", - effect() { - tech.fragments++ - }, - remove() { - tech.fragments = 0 - } - }, - { - name: "ammonium nitrate", - description: "+24% explosive damage, radius", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() - }, - requires: "an explosive damage source, not iridium-192", - effect() { - tech.explosiveRadius += 0.24; - }, - remove() { - tech.explosiveRadius = 1; - } - }, - { - name: "nitroglycerin", - description: "+66% explosive damage
–33% explosive radius", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() && !tech.isExplosionHarm - }, - requires: "an explosive damage source, not iridium-192, acetone peroxide", - effect() { - tech.isSmallExplosion = true; - }, - remove() { - tech.isSmallExplosion = false; - } - }, - { - name: "acetone peroxide", - description: "+70% explosive radius
–40% explosive defense", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - isBadRandomOption: true, - allowed() { - return tech.hasExplosiveDamageCheck() && !tech.isSmallExplosion - }, - requires: "an explosive damage source, not nitroglycerin", - effect() { - tech.isExplosionHarm = true; - }, - remove() { - tech.isExplosionHarm = false; - } - }, - { - name: "shock wave", - description: "mines and sporangium stun for 3-5 seconds
explosions stun for 0.5 seconds", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.haveGunCheck("spores") || tech.haveGunCheck("mine") || (!tech.isExplodeRadio && tech.hasExplosiveDamageCheck()) - }, - requires: "mine, spores, an explosive damage source, not iridium-192", - effect() { - tech.isStun = true; - }, - remove() { - tech.isStun = false; - } - }, - { - name: "shaped charge", - description: `use ${powerUps.orb.research(3)} to dynamically reduce
all explosions to prevent health loss`, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return !tech.isImmuneExplosion && (build.isExperimentSelection || powerUps.research.count > 2) && (tech.haveGunCheck("missiles") || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 1) || tech.missileBotCount > 0 || tech.isIncendiary || tech.isPulseLaser || tech.isTokamak || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb)) - }, - requires: "an explosive damage source, not rocket propelled grenade", - effect() { - tech.isSmartRadius = true; - for (let i = 0; i < 3; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - }, - remove() { - tech.isSmartRadius = false; - if (this.count > 0) powerUps.research.changeRerolls(3) - } - }, - // { - // name: "electric armor", - // // description: "explosions do no defense
while your energy is above 98%", - // description: "instead of causing health loss, explosions
drain 12 energy and have more knockback", - // isGunTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return !tech.isSmartRadius && !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() - // }, - // requires: "an explosive damage source, not iridium-192", - // effect() { - // tech.isImmuneExplosion = true; - // }, - // remove() { - // tech.isImmuneExplosion = false; - // } - // }, - { - name: "MIRV", - description: "fire +1 missile or grenade per shot
–12% explosion damage and radius", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("missiles") || tech.missileBotCount || tech.haveGunCheck("grenades") - }, - requires: "missiles, grenades", - effect() { - tech.missileCount++; - }, - remove() { - tech.missileCount = 1; - } - }, - { - name: "rocket-propelled grenade", - description: "grenades explode on map collisions
explosions drain energy, not health", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("grenades") && !tech.isVacuumBomb - }, - requires: "grenades, not vacuum bomb", - effect() { - tech.isImmuneExplosion = true; - tech.isRPG = true; - b.setGrenadeMode() - }, - remove() { - tech.isImmuneExplosion = false; - tech.isRPG = false; - b.setGrenadeMode() - } - }, - { - name: "vacuum bomb", - description: "grenades fire slower, explode bigger,
and suck everything towards them", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("grenades") && !tech.isNeutronBomb && !tech.isBlockExplode && !tech.isRPG - }, - requires: "grenades, not neutron bomb, chain reaction, RPG", - effect() { - tech.isVacuumBomb = true; - b.setGrenadeMode() - }, - remove() { - tech.isVacuumBomb = false; - b.setGrenadeMode() - } - }, - { - name: "chain reaction", - description: "+33% grenade radius and damage
blocks caught in explosions also explode", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("grenades") && !tech.isExplodeRadio && !tech.isNeutronBomb && !tech.isVacuumBomb - }, - requires: "grenades, not iridium-192, neutron bomb, vacuum bomb", - effect() { - tech.isBlockExplode = true; //chain reaction - }, - remove() { - tech.isBlockExplode = false; - } - }, - { - name: "flame test", - description: "after grenades detonate they release
a colorful cluster of small explosions", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("grenades") && !tech.isNeutronBomb && !tech.isCircleExplode && !tech.isPetalsExplode - }, - requires: "grenades, not neutron bomb, pyrotechnics, fireworks", - effect() { - tech.isClusterExplode = true; - }, - remove() { - tech.isClusterExplode = false; - } - }, - { - name: "pyrotechnics", - description: "after grenades detonate they release
a colorful circle of explosions", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("grenades") && !tech.isNeutronBomb && !tech.isClusterExplode && !tech.isPetalsExplode - }, - requires: "grenades, not neutron bomb, flame test, fireworks", - effect() { - tech.isCircleExplode = true; - }, - remove() { - tech.isCircleExplode = false; - } - }, - { - name: "fireworks", - description: "after grenades detonate they release
colorful petals of explosions", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("grenades") && !tech.isNeutronBomb && !tech.isClusterExplode && !tech.isCircleExplode - }, - requires: "grenades, not neutron bomb, pyrotechnics, flame test", - effect() { - tech.isPetalsExplode = true; - }, - remove() { - tech.isPetalsExplode = false; - } - }, - { - name: "neutron bomb", - description: "grenades are irradiated with Cf-252
does radioactive damage over time", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("grenades") && !tech.fragments && !tech.isVacuumBomb && !tech.isExplodeRadio && !tech.isBlockExplode && !tech.isClusterExplode && !tech.isPetalsExplode && !tech.isCircleExplode - }, - requires: "grenades, not fragmentation, vacuum bomb, iridium-192, pyrotechnics, fireworks, flame test, chain reaction", - effect() { - tech.isNeutronBomb = true; - b.setGrenadeMode() - }, - remove() { - tech.isNeutronBomb = false; - b.setGrenadeMode() - } - }, - { - name: "vacuum permittivity", - description: "+20% radioactive range
objects in range of the bomb are slowed", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isNeutronBomb - }, - requires: "grenades, neutron bomb", - effect() { - tech.isNeutronSlow = true - }, - remove() { - tech.isNeutronSlow = false - } - }, - { - name: "radioactive contamination", - description: "after a mob or shield dies,
leftover radiation spreads to a nearby mob", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isNailRadiation || tech.isWormholeDamage || tech.isNeutronBomb || tech.isExplodeRadio || tech.isBlockRadiation - }, - requires: "radiation damage source", - effect() { - tech.isRadioactive = true - }, - remove() { - tech.isRadioactive = false - } - }, - { - name: "nuclear transmutation", - description: "+47% radiation damage
nail, drone, neutron bomb, iridium, cosmic string, deflect", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isNailRadiation || tech.isWormholeDamage || tech.isNeutronBomb || tech.isExplodeRadio || tech.isBlockRadiation || tech.isDroneRadioactive - }, - requires: "radiation damage source", - effect() { - tech.radioactiveDamage += 1.47 - }, - remove() { - tech.radioactiveDamage = 1 - } - }, - { - name: "water shielding", - link: `water shielding`, - description: "radioactive effects on you are reduced by 75%
neutron bomb, drones, explosions, slime", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isNeutronBomb || tech.isDroneRadioactive || tech.isExplodeRadio - }, - requires: "neutron bomb, irradiated drones, iridium-192", - effect() { - tech.isRadioactiveResistance = true - }, - remove() { - tech.isRadioactiveResistance = false - } - }, - { - name: "ricochet", - description: "after nails hit a mob they rebound towards
a new mob with +180% damage per bounce", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - // return (tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles) || (tech.haveGunCheck("mines")) - return tech.isMineDrop || tech.isNailBotUpgrade || tech.fragments || tech.nailsDeathMob || (tech.haveGunCheck("mine") && !(tech.isLaserMine || tech.isFoamMine || tech.isSuperMine)) || (tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles) || (tech.haveGunCheck("shotgun") && (tech.isNeedles || tech.isNailShot) && !tech.isRivets && !tech.isNeedles) - }, - // - requires: "nail gun, not rotary cannon, rivets, or needles", - effect() { - tech.isRicochet = true - }, - remove() { - tech.isRicochet = false - } - }, - { - name: "booby trap", - description: "50% chance to drop a mine from power ups
+36% JUNK to tech pool", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("mine") - }, - requires: "mines", - effect() { - tech.isMineDrop = true; - if (tech.isMineDrop) b.mine(m.pos, { - x: 0, - y: 0 - }, 0) - this.refundAmount += tech.addJunkTechToPool(0.36) - }, - refundAmount: 0, - remove() { - tech.isMineDrop = false; - if (this.count > 0 && this.refundAmount > 0) { - tech.removeJunkTechFromPool(this.refundAmount) - this.refundAmount = 0 - } - } - }, - { - name: "elephants toothpaste", - description: "instead of nails mines catalyze a reaction
that yields foam bubbles", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("mine") && !tech.isSuperMine && !tech.isRicochet && !tech.isNailRadiation && !tech.isNailCrit - }, - requires: "mines, not blast ball, ricochet, irradiated nails, supercritical fission", - effect() { - tech.isFoamMine = true; - }, - remove() { - tech.isFoamMine = false; - } - }, - { - name: "blast ball", - descriptionFunction() { - return `instead of nails mines fire bouncy balls` - }, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("mine") && !tech.isFoamMine && !tech.isRicochet && !tech.isNailRadiation && !tech.isNailCrit - }, - requires: "mines, not elephants toothpaste, ricochet, irradiated nails, supercritical fission", - effect() { - tech.isSuperMine = true; - }, - remove() { - tech.isSuperMine = false; - } - }, - { - name: "laser-mines", - link: `laser-mines`, - description: "mines laid while you are crouched
use energy to emit 3 unaimed lasers", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("mine") - }, - requires: "mines", - effect() { - tech.isLaserMine = true; - }, - remove() { - tech.isLaserMine = false; - } - }, - { - name: "sentry", - descriptionFunction() { - return `mines fire one ${b.guns[10].nameString()} at a time
mines fire 50% more ${b.guns[10].nameString('s')}` - }, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("mine") && !tech.isFoamMine - }, - requires: "mines, not elephants toothpaste", - effect() { - tech.isMineSentry = true; - }, - remove() { - tech.isMineSentry = false; - } - }, - { - name: "extended magazine", - descriptionFunction() { - return `sentry mines fire 50% more ${b.guns[10].nameString('s')}` - }, - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("mine") && tech.isMineSentry - }, - requires: "mines, sentry", - effect() { - tech.sentryAmmo += 17; - }, - remove() { - tech.sentryAmmo = 33; - } - }, - { - name: "mycelial fragmentation", - link: `mycelial fragmentation`, - description: "during their growth phase
+70% sporangium discharge", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("spores") - }, - requires: "spores", - effect() { - tech.isSporeGrowth = true - }, - remove() { - tech.isSporeGrowth = false - } - }, - { - name: "cordyceps", - // descriptionFunction() { - // return `mobs infected by ${b.guns[6].nameString('s')} have a 5% chance
to resurrect and attack other mobs` - // }, - description: "sporangium infect mobs they attach to
infected mobs resurrect and attack other mobs", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("spores") - }, - requires: "spores", - effect() { - tech.isZombieMobs = true - }, - remove() { - tech.isZombieMobs = false - } - }, - { - name: "colony", - description: "+50% sporangium discharge
40% chance to discharge something different", - link: `colony`, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("spores") - }, - requires: "spores", - effect() { - tech.isSporeColony = true - }, - remove() { - tech.isSporeColony = false - } - }, - { - name: "cryodesiccation", - descriptionFunction() { - return `+25% sporangium discharge
${b.guns[6].nameString('s')} freeze mobs for 1.5 second` - }, - // description: "+25% sporangium discharge
spores freeze mobs for 1.5 second", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 0) || tech.isSporeWorm || tech.isSporeFlea - }, - requires: "spores", - effect() { - tech.isSporeFreeze = true - }, - remove() { - tech.isSporeFreeze = false - } - }, - { - name: "flagella", - descriptionFunction() { - return `+50% ${b.guns[6].nameString()} acceleration
if they can't find a target ${b.guns[6].nameString('s')} follow you` - }, - // description: "+50% spore acceleration
if they can't find a target spores follow you", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 0) || tech.isSporeWorm || tech.isSporeFlea - }, - requires: "spores", - effect() { - tech.isSporeFollow = true - }, - remove() { - tech.isSporeFollow = false - } - }, - { - name: "junk DNA", - descriptionFunction() { - return `+53% ${b.guns[6].nameString()} damage per JUNKtech (${(53*tech.junkCount).toFixed(0)}%)
+50% JUNK to tech pool` - }, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 0) || tech.isSporeWorm || tech.isSporeFlea - }, - requires: "spores", - effect() { - tech.isJunkDNA = true - this.refundAmount += tech.addJunkTechToPool(0.5) - }, - refundAmount: 0, - remove() { - tech.isJunkDNA = false - if (this.count > 0 && this.refundAmount > 0) { - tech.removeJunkTechFromPool(this.refundAmount) - this.refundAmount = 0 - } - } - }, - // { - // name: "junk DNA", - // //increase damage by 10% for each JUNK tech percent in the tech pool, remove all JUNK tech, - // descriptionFunction() { return `+50% ${b.guns[6].nameString()} damage
+15% JUNK to tech pool` }, - // isGunTech: true, - // maxCount: 1, - // count: 0, - // frequency: 3, - // frequencyDefault: 3, - // allowed() { - // return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 0) || tech.isSporeWorm || tech.isSporeFlea - // }, - // requires: "spores", - // effect() { - // tech.isSporeWorm = true - // this.refundAmount += tech.addJunkTechToPool(0.15) - // }, - // refundAmount: 0, - // remove() { - // tech.isSporeWorm = false - // if (this.count > 0 && this.refundAmount > 0) { - // tech.removeJunkTechFromPool(this.refundAmount) - // this.refundAmount = 0 - // } - // } - // }, - { - name: "mutualism", - descriptionFunction() { - return `+200% ${b.guns[6].nameString()} damage
${b.guns[6].nameString('s')} borrow 1 health until they die` - }, - // description: `+150% ${b.guns[6].name()} damage
spores borrow 0.5 health until they die`, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 0)) || tech.isSporeWorm || tech.isSporeFlea - }, - requires: "spores", - effect() { - tech.isMutualism = true - }, - remove() { - tech.isMutualism = false - } - }, - { - name: "necrophage", - description: "if foam, fleas, or worms kill their target
they grow 3 copies", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("foam") || tech.isFoamBall || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isSporeWorm || tech.isSporeFlea || tech.isFoamMine - }, - requires: "foam, spores, worms, fleas", - effect() { - tech.isSpawnBulletsOnDeath = true - }, - remove() { - tech.isSpawnBulletsOnDeath = false; - } - }, - { - name: "siphonaptera", - description: "sporangium and shotgun hatch fleas", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 0) || (tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isNeedles && !tech.isNailShot)) && !tech.isSporeWorm - }, - requires: "spores, not worms", - effect() { - tech.isSporeFlea = true - }, - remove() { - tech.isSporeFlea = false - - } - }, - { - name: "nematodes", - description: "shotgun and sporangium hatch worms", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 0) || (tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isNeedles && !tech.isNailShot)) && !tech.isSporeFlea - }, - requires: "spores, not fleas", - effect() { - tech.isSporeWorm = true - }, - remove() { - tech.isSporeWorm = false - } - }, - { - name: "K-selection", - description: "+37% worm and flea damage", - isGunTech: true, - maxCount: 3, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isSporeWorm || tech.isSporeFlea - }, - requires: "spores, shotgun, worms, fleas", - effect() { - tech.wormSize++ - }, - remove() { - tech.wormSize = 0 - } - }, - { - name: "path integration", - descriptionFunction() { - return `drones and ${b.guns[6].nameString("s")}
travel with you through levels` - }, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.isSporeFollow && (tech.haveGunCheck("spores") || (tech.haveGunCheck("shotgun") && tech.isSporeWorm))) || tech.haveGunCheck("drones") || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && (simulation.molecularMode === 0 || simulation.molecularMode === 3)) - }, - requires: "spores, worms, flagella, drones", - effect() { - tech.isDronesTravel = true - }, - remove() { - tech.isDronesTravel = false - } - }, - { - name: "reduced tolerances", - link: `reduced tolerances`, - description: `+66% drones per ${powerUps.orb.ammo()} and energy
–40% drone duration`, - isGunTech: true, - maxCount: 3, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return !tech.isDroneRadioactive && (tech.haveGunCheck("drones") || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 3)) - }, - requires: "drones, not irradiated drones", - effect() { - tech.droneCycleReduction = Math.pow(0.6, 1 + this.count) - tech.droneEnergyReduction = Math.pow(0.333, 1 + this.count) - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "drones") { - const scale = Math.pow(3, this.count + 1) - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * scale + //remove options that don't meet requirements + for (let i = options.length - 1; i > -1; i--) { + const index = options[i] + if (!(tech.tech[index].count < tech.tech[index].maxCount) || !tech.tech[index].allowed()) { + options.splice(i, 1); } } - }, - remove() { - tech.droneCycleReduction = 1 - tech.droneEnergyReduction = 1 - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "drones") b.guns[i].ammoPack = b.guns[i].defaultAmmoPack + //pick one option + if (options.length) { + const index = options[Math.floor(Math.random() * options.length)] + simulation.makeTextLog(`tech.giveTech("${tech.tech[index].name}") //optical amplifier`, 360); + tech.giveTech(index) + techGiven++ } } - }, - { - name: "delivery drone", - description: "if a drone picks up a power up,
it becomes larger, faster, and more durable", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 3) - }, - requires: "drones", - effect() { - tech.isDroneGrab = true - }, - remove() { - tech.isDroneGrab = false - } - }, - { - name: "drone repair", - link: `drone repair`, - description: "after a drone expires it redeploys
for a 20% chance to use 1 drone ammo", - // description: "broken drones repair if the drone gun is active
repairing has a 25% chance to use 1 drone", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("drones") - }, - requires: "drones", - effect() { - tech.isDroneRespawn = true - }, - remove() { - tech.isDroneRespawn = false - } - }, - { - name: "brushless motor", - description: "drones rapidly rush towards their target
+33% drone collision damage", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 3)) && !tech.isDroneRadioactive && !tech.isIncendiary - }, - requires: "drones, molecular assembler, not irradiated drones, incendiary", - effect() { - tech.isDroneTeleport = true - }, - remove() { - tech.isDroneTeleport = false - } - }, - { - name: "axial flux motor", - description: "+66% drones rush frequency
+44% drone collision damage", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isDroneTeleport - }, - requires: "drones, brushless motor", - effect() { - tech.isDroneFastLook = true - }, - remove() { - tech.isDroneFastLook = false - } - }, - { - name: "irradiated drones", - link: `irradiated drones`, - description: `the space around drones is irradiated
–75% drones per ${powerUps.orb.ammo()} and energy`, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.droneCycleReduction === 1 && !tech.isIncendiary && !tech.isDroneTeleport && (tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && simulation.molecularMode === 3)) - }, - requires: "drones, not reduced tolerances, incendiary, torque bursts", - effect() { - tech.isDroneRadioactive = true - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "drones") { - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * 0.25 - b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 0.25) - simulation.makeGunHUD(); - } - } - }, - remove() { - if (tech.isDroneRadioactive) { - tech.isDroneRadioactive = false - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "drones") { - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack - b.guns[i].ammo = b.guns[i].ammo * 4 - simulation.makeGunHUD(); + if (techGiven > 0) { + tech.isStuckOn = true + } else { //eject if none found + simulation.makeTextLog(`0 tech found //optical amplifier`); + const loop = () => { + if (!simulation.paused && m.alive) { + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].name === this.name) powerUps.ejectTech(i) } + return } + requestAnimationFrame(loop); } + requestAnimationFrame(loop); } }, - { - name: "beta radiation", //"control rod ejection", - description: "–50% drone duration
+100% drone radiation damage", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isDroneRadioactive - }, - requires: "drones, irradiated drones", - effect() { - tech.droneRadioDamage = 2 - }, - remove() { - tech.droneRadioDamage = 1 + remove() { + tech.isStuckOn = false + } + }, + { + name: "relativistic momentum", + description: "lasers push mobs and blocks", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("laser") && !tech.isPulseLaser) || tech.isLaserBotUpgrade + }, + requires: "laser, not pulse", + effect() { + tech.isLaserPush = true; + }, + remove() { + tech.isLaserPush = false; + } + }, + { + name: "iridescence", + // description: "if a laser hits a mob at a low angle of illumination
+66% laser damage", + description: "if laser beams hit mobs near their center
+100% laser damage", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("laser") && !tech.isPulseLaser) || tech.isLaserBotUpgrade || tech.isLaserMine + }, + requires: "laser, not pulse", + effect() { + tech.laserCrit += 1; + }, + remove() { + tech.laserCrit = 0; + } + }, + { + name: "lens", + description: "+150% laser gun damage if it passes
through a revolving 90° arc circular lens", //π / 2 + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("laser") + }, + requires: "laser", + effect() { + tech.isLaserLens = true + b.guns[11].chooseFireMethod() + // if (this.count > 0) b.guns[11].lensDamageOn += 20 * Math.PI / 180 + // b.guns[11].arcRange = 0.78 + }, + remove() { + tech.isLaserLens = false + b.guns[11].chooseFireMethod() + // b.guns[11].lensDamageOn = 2.5 // 100% + 150% + // b.guns[11].arcRange = 0 + } + }, + { + name: "compound lens", + description: "+50% laser lens damage
+15° lens arc", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("laser") && tech.isLaserLens + }, + requires: "lens", + effect() { + b.guns[11].arcRange += 15 * Math.PI / 180 / 2 + b.guns[11].lensDamageOn += 0.5 + }, + remove() { + b.guns[11].arcRange = 90 * Math.PI / 180 / 2 //0.78 divded by 2 because of how it's drawn + b.guns[11].lensDamageOn = 2.5 + } + }, + { + name: "specular reflection", + description: "+2 laser beam reflections", + isGunTech: true, + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.isLaserBotUpgrade) && !tech.isWideLaser && !tech.isPulseLaser && !tech.historyLaser + }, + requires: "laser, not diffuse beam, pulse, or slow light", + effect() { + tech.laserReflections += 2; + }, + remove() { + tech.laserReflections = 2; + } + }, + { + name: "diffraction grating", + description: `+1 diverging laser gun beam`, + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.haveGunCheck("laser") && !tech.isWideLaser && !tech.historyLaser + }, + requires: "laser gun, diffuse beam, or slow light", + effect() { + tech.beamSplitter++ + b.guns[11].chooseFireMethod() + }, + remove() { + if (tech.beamSplitter !== 0) { + tech.beamSplitter = 0 + b.guns[11].chooseFireMethod() + } + } + }, + { + name: "diffuse beam", + link: `diffuse beam`, + description: "laser gun beam is wider and doesn't reflect
+220% laser damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.beamSplitter && !tech.isPulseLaser && !tech.historyLaser + }, + requires: "laser gun, not specular reflection, diffraction grating, slow light, pulse", + effect() { + if (tech.wideLaser === 0) tech.wideLaser = 3 + tech.isWideLaser = true; + b.guns[11].chooseFireMethod() + }, + remove() { + if (tech.isWideLaser) { + // tech.wideLaser = 0 + tech.isWideLaser = false; + b.guns[11].chooseFireMethod() + } + } + }, + { + name: "output coupler", + description: "+30% laser gun beam width
+30% laser damage", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("laser") && tech.isWideLaser + }, + requires: "laser gun, diffuse beam", + effect() { + tech.wideLaser += 2 + b.guns[11].chooseFireMethod() + }, + remove() { + if (tech.isWideLaser) { + tech.wideLaser = 3 + } else { + tech.wideLaser = 0 + } + b.guns[11].chooseFireMethod() + } + }, + { + name: "slow light", + description: "laser gun beam is spread into your recent past
+300% total beam damage", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.beamSplitter && !tech.isWideLaser + }, + requires: "laser gun, not specular reflection, diffraction grating, diffuse beam", + effect() { + // this.description = `add 5 more laser beams into into your past` + tech.historyLaser++ + b.guns[11].chooseFireMethod() + }, + remove() { + if (tech.historyLaser) { + tech.historyLaser = 0 + b.guns[11].chooseFireMethod() + } + } + }, + { + name: "infrared diode", + description: "+60% laser energy efficiency
infrared light is outside visual perception", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return (tech.haveGunCheck("laser") || tech.isLaserBotUpgrade || tech.isLaserMine) && !tech.isPulseLaser && tech.laserDrain === 0.0018 + }, + requires: "laser, not free-electron, pulse", + effect() { + tech.laserDrain *= 0.4; //100%-50% + tech.laserColor = "transparent" //"rgb(255,0,20,0.02)" + // tech.laserColorAlpha = "rgba(255,0,20,0.05)" + }, + remove() { + tech.laserDrain = 0.0018; + tech.laserColor = "#f02" + tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)" + } + }, + { + name: "dye laser", + description: "+25% laser energy efficiency
+25% laser damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.isLaserBotUpgrade) && !tech.isPulseLaser && tech.laserDrain === 0.0018 + }, + requires: "laser, not pulse, infrared diode", + effect() { + tech.laserDrain *= 0.75 + tech.laserDamage *= 1.25 + tech.laserColor = "rgb(0, 11, 255)" + tech.laserColorAlpha = "rgba(0, 11, 255,0.5)" + }, + remove() { + tech.laserDrain = 0.0018; + tech.laserDamage = 0.18; //used in check on pulse and diode: tech.laserDamage === 0.18 + tech.laserColor = "#f00" + tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)" + } + }, + { + name: "free-electron laser", + description: "–250% laser energy efficiency
+200% laser damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.isLaserBotUpgrade) && !tech.isPulseLaser && tech.laserDrain === 0.0018 + }, + requires: "laser, not pulse, infrared diode", + effect() { + tech.laserDrain *= 1 + 2.5 //250% more drain + tech.laserDamage *= 1 + 2 //190% more damage + tech.laserColor = "#83f" + tech.laserColorAlpha = "rgba(136, 51, 255,0.5)" + }, + remove() { + tech.laserDrain = 0.0018; + tech.laserDamage = 0.18; //used in check on pulse and diode: tech.laserDamage === 0.18 + tech.laserColor = "#f00" + tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)" + } + }, + { + name: "pulse", + description: "charge your energy and release it as a
laser pulse that initiates an explosion cluster", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.isWideLaser && tech.laserDrain === 0.0018 && !tech.isStuckOn + }, + requires: "laser gun, not specular reflection, diffuse, free-electron laser, optical amplifier", + effect() { + tech.isPulseLaser = true; + b.guns[11].chooseFireMethod() + }, + remove() { + if (tech.isPulseLaser) { + tech.isPulseLaser = false; + b.guns[11].chooseFireMethod() + } + } + }, + //************************************************** + //************************************************** field + //************************************************** tech + //************************************************** + { + name: "zero point energy", + description: `use ${powerUps.orb.research(2)}
+100 maximum energy`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "time dilation") && (build.isExperimentSelection || powerUps.research.count > 1) + }, + requires: "standing wave, pilot wave, time dilation", + effect() { + tech.harmonicEnergy = 1 + m.setMaxEnergy() + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) } }, - { - name: "orthocyclic winding", - link: `orthocyclic winding`, - description: "+66% drone acceleration
+33% radiation damage", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isDroneRadioactive - }, - requires: "drones, irradiated drones", - effect() { - tech.isFastDrones = true - }, - remove() { - tech.isFastDrones = false + remove() { + tech.harmonicEnergy = 0; + m.setMaxEnergy() + if (this.count > 0) powerUps.research.changeRerolls(2) + } + }, + { + name: "spherical harmonics", + description: "+40% standing wave deflection efficiency
no longer deactivates on mob shields", //standing wave oscillates in a 3rd dimension
+ isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "standing wave" + }, + requires: "standing wave", + effect() { + tech.harmonics++ + m.fieldShieldingScale = (tech.isStandingWaveExpand ? 0.9 : 1.6) * Math.pow(0.6, (tech.harmonics - 2)) + m.harmonicShield = m.harmonicAtomic + }, + remove() { + tech.harmonics = 2 + m.fieldShieldingScale = (tech.isStandingWaveExpand ? 0.9 : 1.6) * Math.pow(0.6, (tech.harmonics - 2)) + m.harmonicShield = m.harmonic3Phase + } + }, + { + name: "expansion", + description: "+50% standing wave deflection efficiency
using standing wave field expands its radius", + // description: "use energy to expand standing wave
the field slowly contracts when not used", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "standing wave" + }, + requires: "standing wave", + effect() { + tech.isStandingWaveExpand = true + m.fieldShieldingScale = (tech.isStandingWaveExpand ? 0.9 : 1.3) * Math.pow(0.6, (tech.harmonics - 2)) + }, + remove() { + tech.isStandingWaveExpand = false + m.fieldShieldingScale = (tech.isStandingWaveExpand ? 0.9 : 1.3) * Math.pow(0.6, (tech.harmonics - 2)) + m.harmonicRadius = 1 + } + }, + { + name: "triple point", + descriptionFunction() { + return `+1.5 second ice IX freeze effect
spawn ${powerUps.orb.coupling(10)} that each give +0.1 coupling` //
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"} + }, + isFieldTech: true, + maxCount: 3, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" + }, + requires: "standing wave, perfect diamagnetism", + effect() { + tech.iceIXFreezeTime += 90 + powerUps.spawnDelay("coupling", 10) + }, + remove() { + tech.iceIXFreezeTime = 150 + if (this.count) { + m.couplingChange(-this.count) + } + } + }, + { + name: "bremsstrahlung", + description: "deflecting and thrown blocks
do braking damage to mobs", + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" + }, + requires: "standing wave, perfect diamagnetism, pilot wave", + effect() { + tech.blockDmg += 3 //if you change this value also update the for loop in the electricity graphics in m.pushMass + }, + remove() { + tech.blockDmg = 0; + } + }, + { + name: "cherenkov radiation", //deflecting and blocks + description: "bremsstrahlung's effects are radioactive
+300% damage over 6 seconds", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && tech.blockDmg + }, + requires: "bremsstrahlung", + effect() { + tech.isBlockRadiation = true + }, + remove() { + tech.isBlockRadiation = false; + } + }, + { + name: "flux pinning", + description: "after deflecting a mob
it is stunned for up to 4 seconds", + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" + }, + requires: "a field that can block", + effect() { + tech.isStunField += 240; + }, + remove() { + tech.isStunField = 0; + } + }, + { + name: "eddy current brake", + description: "perfect diamagnetism slows nearby mobs
effect radius scales with stored energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" + }, + requires: "perfect diamagnetism", + effect() { + tech.isPerfectBrake = true; + }, + remove() { + tech.isPerfectBrake = false; + } + }, + { + name: "Meissner effect", + description: "+55% perfect diamagnetism radius
+22° perfect diamagnetism circular arc", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" + }, + requires: "perfect diamagnetism", + effect() { + tech.isBigField = true; + }, + remove() { + tech.isBigField = false; + } + }, + { + name: "tessellation", + description: `use ${powerUps.orb.research(2)}
+50% defense`, + // description: "use 4 research
reduce defense by 50%", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "negative mass") && (build.isExperimentSelection || powerUps.research.count > 3) + }, + requires: "perfect diamagnetism, negative mass, pilot wave", + effect() { + tech.isFieldHarmReduction = true + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) } }, - { - name: "fault tolerance", - description: `use ${powerUps.orb.research(2)}to trade your drone gun
for 5 drones that last forever`, - isGunTech: true, - isRemoveGun: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.haveGunCheck("drones", false) && !tech.isDroneRespawn && tech.isBulletsLastLonger === 1 && !tech.isDronesTravel && (build.isExperimentSelection || powerUps.research.count > 1) - }, - requires: "drones, not drone repair, anti-shear topology, autonomous navigation", - effect() { - const num = 5 - tech.isForeverDrones += num - if (tech.haveGunCheck("drones", false)) b.removeGun("drones") - //spawn drones - if (tech.isDroneRadioactive) { - for (let i = 0; i < num * 0.25; i++) { - b.droneRadioactive({ - x: m.pos.x + 30 * (Math.random() - 0.5), - y: m.pos.y + 30 * (Math.random() - 0.5) - }, 5) - bullet[bullet.length - 1].endCycle = Infinity - } - } else { - for (let i = 0; i < num; i++) { - b.drone({ - x: m.pos.x + 30 * (Math.random() - 0.5), - y: m.pos.y + 30 * (Math.random() - 0.5) - }, 5) - bullet[bullet.length - 1].endCycle = Infinity - } + remove() { + tech.isFieldHarmReduction = false + if (this.count > 0) powerUps.research.changeRerolls(2) + } + }, + { + name: "radiative equilibrium", + description: "after losing health
+200% damage for 10 seconds", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "negative mass" + }, + requires: "negative mass, pilot wave", + effect() { + tech.isHarmDamage = true; + }, + remove() { + tech.isHarmDamage = false; + } + }, + { + name: "dynamic equilibrium", + descriptionFunction() { + return `increase damage by your defense and
5% of your last ${tech.isEnergyHealth ? "energy" : "health"} loss   (+${(100 * Math.max(5, tech.lastHitDamage) * m.lastHit * (2 - m.defense())).toFixed(0)}% damage)` + }, // = +${10*m.defense()}% + // descriptionFunction() { return `increase damage by your last ${tech.isEnergyHealth ? "energy" : "health"} loss
(${(tech.lastHitDamage).toFixed(0)}%)(${(100*m.lastHit).toFixed(0)} ${tech.isEnergyHealth ? "energy" : "health"})(${2 - m.defense()} defense) = ${(100*tech.lastHitDamage * m.lastHit * (2 - m.defense())).toFixed(0)}% damage ` }, // = +${10*m.defense()}% + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "negative mass" || m.fieldUpgrades[m.fieldMode].name === "standing wave") && !tech.isCloakHealLastHit + }, + requires: "negative mass, pilot wave, standing wave, not patch", + effect() { + tech.lastHitDamage += 5; + }, + remove() { + tech.lastHitDamage = 0; + } + }, + { + name: "neutronium", + description: `move and jump 20% slower
if your field is active +90% defense`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "negative mass" + }, + requires: "negative mass", + effect() { + tech.isNeutronium = true + tech.baseFx *= 0.8 + tech.baseJumpForce *= 0.8 + m.setMovement() + }, + //also removed in m.setHoldDefaults() if player switches into a bad field + remove() { + tech.isNeutronium = false + if (!tech.isFreeWormHole) { + tech.baseFx = 0.08 + tech.baseJumpForce = 10.5 + m.setMovement() + } + } + }, + { + name: "aerostat", + description: `+88% damage while off the ground
-22% damage while on the ground`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "negative mass" + }, + requires: "negative mass", + effect() { + tech.isNoGroundDamage = true + }, + remove() { + tech.isNoGroundDamage = false + } + }, + { + name: "annihilation", + description: "after colliding with non-boss mobs
they are annihilated and –33% energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "negative mass" && !tech.isEnergyHealth + }, + requires: "negative mass, not mass-energy", + effect() { + tech.isAnnihilation = true + }, + remove() { + tech.isAnnihilation = false; + } + }, + { + name: "inertial mass", + description: "negative mass is larger and faster
blocks also move horizontally with the field", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "negative mass" + }, + requires: "negative mass", + effect() { + tech.isFlyFaster = true + }, + remove() { + tech.isFlyFaster = false; + } + }, + // { + // name: "Bose Einstein condensate", + // description: "use energy to freeze mobs in your field
pilot wave, negative mass, time dilation", + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "negative mass" || (m.fieldUpgrades[m.fieldMode].name === "time dilation" && !tech.isRewindField) + // }, + // requires: "pilot wave, negative mass, time dilation, not retrocausality", + // effect() { + // tech.isFreezeMobs = true + // }, + // remove() { + // tech.isFreezeMobs = false + // } + // }, + { + name: "bot manufacturing", + description: `use ${powerUps.orb.research(1)} to build
3 random bots`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBotTech: true, + isNonRefundable: true, + allowed() { + return powerUps.research.count > 0 && (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") + }, + requires: "molecular assembler, pilot wave", + effect() { + for (let i = 0; i < 1; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + m.energy = 0.01; + b.randomBot() + b.randomBot() + b.randomBot() + }, + remove() { } + }, + { + name: "bot prototypes", + description: `use ${powerUps.orb.research(2)}to build 2 random bots
and upgrade all bots to that type`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBotTech: true, + isNonRefundable: true, + allowed() { + return powerUps.research.count > 1 && (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") + }, + requires: "molecular assembler, pilot wave", + effect() { + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + //fill array of available bots + const notUpgradedBots = [] + const num = 2 + notUpgradedBots.push(() => { + tech.giveTech("nail-bot upgrade") + for (let i = 0; i < num; i++) { + b.nailBot() + tech.nailBotCount++; } - for (let i = 0; i < 2; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - }, - remove() { - tech.isForeverDrones = 0 - if (this.count && !tech.haveGunCheck("drones", false)) b.giveGuns("drones") - if (this.count > 0) powerUps.research.changeRerolls(2) - } - }, - { - name: "surfactant", - description: `use ${powerUps.orb.research(2)}to trade your foam gun
for 2 foam-bots and foam-bot upgrade`, - isGunTech: true, - isRemoveGun: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBot: true, - isBotTech: true, - isNonRefundable: true, - requires: "foam gun, not bot upgrades, fractionation, pressure vessel", - allowed() { - return tech.haveGunCheck("foam", false) && !b.hasBotUpgrade() && !tech.isAmmoFoamSize && !tech.isFoamPressure && (build.isExperimentSelection || powerUps.research.count > 1) - }, - effect() { + simulation.makeTextLog(`tech.isNailBotUpgrade = true`) + }) + notUpgradedBots.push(() => { tech.giveTech("foam-bot upgrade") - for (let i = 0; i < 2; i++) { + for (let i = 0; i < num; i++) { b.foamBot() tech.foamBotCount++; } simulation.makeTextLog(`tech.isFoamBotUpgrade = true`) - if (tech.haveGunCheck("foam", false)) b.removeGun("foam") + }) + notUpgradedBots.push(() => { + tech.giveTech("boom-bot upgrade") + for (let i = 0; i < num; i++) { + b.boomBot() + tech.boomBotCount++; + } + simulation.makeTextLog(`tech.isBoomBotUpgrade = true`) + }) + notUpgradedBots.push(() => { + tech.giveTech("laser-bot upgrade") + for (let i = 0; i < num; i++) { + b.laserBot() + tech.laserBotCount++; + } + simulation.makeTextLog(`tech.isLaserBotUpgrade = true`) + }) + notUpgradedBots.push(() => { + tech.giveTech("orbital-bot upgrade") + for (let i = 0; i < num; i++) { + b.orbitBot() + tech.orbitBotCount++; + } + simulation.makeTextLog(`tech.isOrbitalBotUpgrade = true`) + }) + for (let i = 0; i < 2; i++) { //double chance for dynamo-bot, since it's very good for assembler + notUpgradedBots.push(() => { + tech.giveTech("dynamo-bot upgrade") + for (let i = 0; i < num; i++) { + b.dynamoBot() + tech.dynamoBotCount++; + } + simulation.makeTextLog(`tech.isDynamoBotUpgrade = true`) + }) + } + notUpgradedBots[Math.floor(Math.random() * notUpgradedBots.length)]() //choose random function from the array and run it + }, + remove() { } + }, + // { + // name: "mycelium manufacturing", + // link: `mycelium manufacturing`, + // // description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to grow spores`, + // descriptionFunction() { return `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to grow ${b.guns[6].nameString('s')}` }, + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // frequency: 3, + // frequencyDefault: 3, + // allowed() { + // return (build.isExperimentSelection || powerUps.research.count > 0) && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isMissileField || tech.isIceField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport) + // }, + // requires: "molecular assembler, no other manufacturing, no drone tech", + // effect() { + // if (!build.isExperimentSelection) { + // for (let i = 0; i < 1; i++) { + // if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + // } + // } + // tech.isSporeField = true; + // }, + // remove() { + // tech.isSporeField = false; + // if (this.count > 0) powerUps.research.changeRerolls(1) + // } + // }, + // { + // name: "missile manufacturing", + // link: `missile manufacturing`, + // description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to construct missiles`, + // // description: "use 3 research to repurpose assembler
excess energy used to construct missiles", + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // frequency: 3, + // frequencyDefault: 3, + // allowed() { + // return (build.isExperimentSelection || powerUps.research.count > 0) && m.maxEnergy > 0.5 && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isIceField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport || tech.isDronesTravel) + // }, + // requires: "molecular assembler, no other manufacturing, no drone tech", + // effect() { + // if (!build.isExperimentSelection) { + // for (let i = 0; i < 1; i++) { + // if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + // } + // } + // tech.isMissileField = true; + // }, + // remove() { + // tech.isMissileField = false; + // if (this.count > 0) powerUps.research.changeRerolls(1) + // } + // }, + // { + // name: "ice IX manufacturing", + // link: `ice IX manufacturing`, + // description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to condense ice IX`, + // // description: "use 3 research to repurpose assembler
excess energy used to condense ice IX", + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // frequency: 3, + // frequencyDefault: 3, + // allowed() { + // return (build.isExperimentSelection || powerUps.research.count > 0) && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isMissileField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport || tech.isDronesTravel) + // }, + // requires: "molecular assembler, no other manufacturing, no drone tech", + // effect() { + // if (!build.isExperimentSelection) { + // for (let i = 0; i < 1; i++) { + // if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + // } + // } + // tech.isIceField = true; + // }, + // remove() { + // tech.isIceField = false; + // if (this.count > 0) powerUps.research.changeRerolls(1) + // } + // }, + { + name: "pair production", + description: "after picking up a power up
+200 energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" + }, + requires: "molecular assembler, pilot wave, standing wave", + effect() { + tech.isMassEnergy = true // used in m.grabPowerUp + m.energy += 2 + }, + remove() { + tech.isMassEnergy = false; + } + }, + { + name: "electric generator", + description: "after deflecting mobs
molecular assembler generates +50 energy", + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "molecular assembler" + }, + requires: "molecular assembler", + effect() { + tech.deflectEnergy += 0.5; + }, + remove() { + tech.deflectEnergy = 0; + } + }, + { + name: "combinatorial optimization", + description: "+35% damage
–35% fire rate", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" + }, + requires: "cloaking, molecular assembler, plasma torch, pilot wave", + damage: 1.35, + effect() { + tech.damage *= this.damage + tech.aimDamage = 1.35 + b.setFireCD(); + }, + remove() { + if (this.count) tech.damage /= this.damage + tech.aimDamage = 1 + b.setFireCD(); + } + }, + { + name: "tokamak", + description: "throwing a block converts it into energy
and a pulsed fusion explosion", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" + }, + requires: "plasma torch, molecular assembler", + effect() { + tech.isTokamak = true; + }, + remove() { + tech.isTokamak = false; + } + }, + { + name: "degenerate matter", + description: "if your field is active
+75% defense", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") + }, + requires: "molecular assembler, plasma torch, perfect diamagnetism, pilot wave", + effect() { + tech.isHarmReduce = true + }, + remove() { + tech.isHarmReduce = false; + } + }, + { + name: "plasma-bot", + link: `plasma-bot`, + description: `use ${powerUps.orb.research(2)}to trade your field
for a bot that uses energy to emit plasma`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBot: true, + isBotTech: true, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && !tech.isPlasmaBall && !tech.isExtruder && (build.isExperimentSelection || powerUps.research.count > 1) + }, + requires: "plasma torch, not extruder, plasma ball", + effect() { + tech.plasmaBotCount++; + b.plasmaBot(); + if (build.isExperimentSelection) { + document.getElementById("field-" + m.fieldMode).classList.remove("build-field-selected"); + document.getElementById("field-0").classList.add("build-field-selected"); + } + m.setField("field emitter") + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + if (this.count > 0) { + tech.plasmaBotCount = 0; + b.clearPermanentBots(); + b.respawnBots(); + if (m.fieldMode === 0) { + m.setField("plasma torch") + if (build.isExperimentSelection) { + document.getElementById("field-0").classList.remove("build-field-selected"); + document.getElementById("field-" + m.fieldMode).classList.add("build-field-selected"); + } + } + powerUps.research.changeRerolls(2) + } + } + }, + { + name: "plasma jet", + link: `plasma jet`, + description: `use ${powerUps.orb.research(2)}
+50% plasma torch range`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.plasmaBotCount || m.fieldUpgrades[m.fieldMode].name === "plasma torch") && (build.isExperimentSelection || powerUps.research.count > 1) && !tech.isPlasmaBall + }, + requires: "plasma torch, not plasma ball", + effect() { + tech.isPlasmaRange += 0.5; + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.isPlasmaRange = 1; + if (this.count > 0) powerUps.research.changeRerolls(this.count * 2) + } + }, + { + name: "extruder", + description: "extrude a thin hot wire of plasma
increases damage and energy drain", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && !tech.isPlasmaBall + }, + requires: "plasma torch, not plasma ball", + effect() { + tech.isExtruder = true; + m.fieldUpgrades[m.fieldMode].set() + }, + remove() { + tech.isExtruder = false; + if (this.count && m.fieldUpgrades[m.fieldMode].name === "plasma torch") m.fieldUpgrades[m.fieldMode].set() + } + }, + { + name: "refractory metal", + description: "extrude metals at a higher temperature
increases effective radius and damage", + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && tech.isExtruder + }, + requires: "extruder", + effect() { + tech.extruderRange += 55 + }, + remove() { + tech.extruderRange = 15 + } + }, + { + name: "plasma ball", + description: "grow an expanding ball of plasma
increases damage and energy drain", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && !tech.isExtruder && tech.isPlasmaRange === 1 + }, + requires: "plasma torch, not extruder, plasma jet", + effect() { + tech.isPlasmaBall = true; + m.fieldUpgrades[m.fieldMode].set() + }, + remove() { + tech.isPlasmaBall = false; + if (this.count && m.fieldUpgrades[m.fieldMode].name === "plasma torch") m.fieldUpgrades[m.fieldMode].set() + } + }, + { + name: "corona discharge", + description: "increase the range and frequency
of plasma ball's electric arc ", + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && tech.isPlasmaBall + }, + requires: "plasma ball", + effect() { + tech.plasmaDischarge += 0.03 + }, + remove() { + tech.plasmaDischarge = 0.01 //default chance per cycle of a discharge + } + }, + { + name: "retrocausality", + description: "time dilation uses energy to rewind your
health, velocity, and position up to 10 seconds", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "time dilation" && !m.isShipMode && !tech.isRewindAvoidDeath && !tech.isTimeSkip + }, + requires: "time dilation, not CPT symmetry", + effect() { + tech.isRewindField = true; + m.fieldUpgrades[m.fieldMode].set() + m.wakeCheck(); + }, + remove() { + tech.isRewindField = false; + if (this.count) m.fieldUpgrades[m.fieldMode].set() + } + }, + { + name: "frame-dragging", //"non-inertial frame", + description: "when not moving time dilation stops time
+33% defense", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "time dilation" + }, + requires: "time dilation", + effect() { + tech.isTimeStop = true; + m.fieldHarmReduction = 0.66; //33% reduction + }, + remove() { + tech.isTimeStop = false; + if (m.fieldUpgrades[m.fieldMode].name === "time dilation") m.fieldHarmReduction = 1; + } + }, + { + name: "Lorentz transformation", + description: `use ${powerUps.orb.research(3)}
+50% movement, jumping, and fire rate`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "time dilation") && (build.isExperimentSelection || powerUps.research.count > 2) + }, + requires: "time dilation", + effect() { + tech.isFastTime = true + m.setMovement(); + b.setFireCD(); + for (let i = 0; i < 3; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.isFastTime = false + m.setMovement(); + b.setFireCD(); + if (this.count > 0) powerUps.research.changeRerolls(3) + } + }, + { + name: "time crystals", + description: "+200% passive energy generation", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isGroundState && (m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") + }, + requires: "time dilation or pilot wave, not ground state", + effect() { + tech.isTimeCrystals = true + m.setFieldRegen() + }, + remove() { + tech.isTimeCrystals = false + m.setFieldRegen() + } + }, + { + name: "no-cloning theorem", + // descriptionFunction() { return `+45% chance to duplicate spawned power ups
after a mob dies –2% duplication (${tech.duplicationChance()})` }, + description: `+45% chance to duplicate spawned power ups
after a mob dies –2% duplication`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking") && !tech.isQuantumEraser + }, + requires: "cloaking, time dilation, not quantum eraser", + effect() { + tech.cloakDuplication = 0.45 + powerUps.setDupChance(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.4); + }, + remove() { + tech.cloakDuplication = 0 + powerUps.setDupChance(); //needed after adjusting duplication chance + } + }, + { + name: "quantum eraser", + descriptionFunction() { + return `for each mob left alive after you exit a level
kill a mob as they spawn at +${100 - 1.6 * simulation.difficultyMode ** 2}% duplication
` + }, + // description: `for each mob left alive after you exit a level
kill a mob as they spawn at 100% duplication
`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" || m.fieldUpgrades[m.fieldMode].name === "time dilation") && !tech.cloakDuplication + }, + requires: "cloaking or time dilation", + effect() { + tech.quantumEraserCount = 0 + tech.isQuantumEraserDuplication = 0 + tech.isQuantumEraser = true + }, + remove() { + tech.quantumEraserCount = 0 + tech.isQuantumEraserDuplication = 0 + tech.isQuantumEraser = false + } + }, + { + name: "symbiosis", + descriptionFunction() { + return `after a boss dies spawn ${powerUps.orb.research(3)}${powerUps.orb.heal(3)} and a tech
after a mob dies –0.5 maximum ${tech.isEnergyHealth ? "energy" : "health"}` + }, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" //|| m.fieldUpgrades[m.fieldMode].name === "time dilation" + }, + requires: "cloaking", + effect() { + tech.isAddRemoveMaxHealth = true + }, + remove() { + tech.isAddRemoveMaxHealth = false + } + }, + { + name: "boson composite", + link: `boson composite`, + description: "while cloaked you are intangible
to blocks and mobs, but mobs drain energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" + }, + requires: "metamaterial cloaking", + effect() { + tech.isIntangible = true; + }, + remove() { + if (tech.isIntangible) { + tech.isIntangible = false; + player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions + } + } + }, + { + name: "patch", + link: `patch`, + description: "after cloaking recover 75% of your
last health loss using that much energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" && !tech.lastHitDamage && !tech.isEnergyHealth + }, + requires: "metamaterial cloaking, not dynamic equilibrium, mass-energy", + effect() { + tech.isCloakHealLastHit = true; + }, + remove() { + tech.isCloakHealLastHit = false; + } + }, + { + name: "dazzler", + link: `dazzler`, + description: "after decloaking stun nearby mobs
and drain –15 energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" + }, + requires: "metamaterial cloaking", + effect() { + tech.isCloakStun = true; + }, + remove() { + tech.isCloakStun = false; + } + }, + // { + // name: "ambush", + // description: "metamaterial cloaking field damage effect
is increased from 333% to 555%", + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" + // }, + // requires: "metamaterial cloaking", + // effect() { + // tech.sneakAttackDmg = 6.55 //555% + 100% + // }, + // remove() { + // tech.sneakAttackDmg = 4.33 //333% + 100% + // } + // }, + { + name: "dynamical systems", + description: `use ${powerUps.orb.research(2)}
+35% damage`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && (build.isExperimentSelection || powerUps.research.count > 1) + }, + requires: "cloaking, pilot wave, or plasma torch", + damage: 1.35, + effect() { + tech.damage *= this.damage + tech.isCloakingDamage = true + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.isCloakingDamage = false + if (this.count > 0) { + tech.damage /= this.damage + powerUps.research.changeRerolls(2) + } + } + }, + { + name: "WIMPs", + description: `at the end of each level spawn ${powerUps.orb.research(4)}
and a dangerous particle that slowly chases you`, + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "time dilation" + }, + requires: "wormhole, pilot wave, time dilation", + effect() { + tech.wimpCount++ + spawn.WIMP() + for (let j = 0, len = 4; j < len; j++) powerUps.spawn(level.exit.x + 100 * (Math.random() - 0.5), level.exit.y - 100 + 100 * (Math.random() - 0.5), "research", false) + }, + remove() { + tech.wimpCount = 0 + } + }, + { + name: "vacuum fluctuation", + description: `use ${powerUps.orb.research(4)}to exploit your field for a
+11% chance to duplicate spawned power ups`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "negative mass" || m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "wormhole") && (build.isExperimentSelection || powerUps.research.count > 3) + }, + requires: "wormhole, time dilation, negative mass, pilot wave", + effect() { + tech.fieldDuplicate = 0.11 + powerUps.setDupChance(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.11); + for (let i = 0; i < 4; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.fieldDuplicate = 0 + powerUps.setDupChance(); //needed after adjusting duplication chance + if (this.count > 0) powerUps.research.changeRerolls(4) + } + }, + // { + // name: "Penrose process", + // description: "after a block falls into a wormhole
+50 energy", + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return m.fieldUpgrades[m.fieldMode].name === "wormhole" + // }, + // requires: "wormhole", + // effect() { + // tech.isWormholeEnergy = true + // }, + // remove() { + // tech.isWormholeEnergy = false + // } + // }, + { + name: "transdimensional worms", + link: `transdimensional worms`, + description: "after a block falls into a wormhole
spawn a worm", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" + }, + requires: "wormhole", + effect() { + tech.isWormholeWorms = true + }, + remove() { + tech.isWormholeWorms = false + } + }, + { + name: "geodesics", + description: `your bullets can traverse wormholes
spawn 2 guns and ${powerUps.orb.ammo(4)}`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" + }, + requires: "wormhole", + effect() { + tech.isWormHoleBullets = true + for (let i = 0; i < 2; i++) powerUps.spawn(m.pos.x + 200 * (Math.random() - 0.5), m.pos.y + 200 * (Math.random() - 0.5), "gun"); + for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x + 200 * (Math.random() - 0.5), m.pos.y + 200 * (Math.random() - 0.5), "ammo"); + }, + remove() { + if (tech.isWormHoleBullets) { for (let i = 0; i < 2; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + if (b.inventory.length) b.removeGun(b.guns[b.inventory[b.inventory.length - 1]].name) //remove your last gun } - }, - remove() { - // if (this.count) { - // b.clearPermanentBots(); - // b.respawnBots(); - // if (!tech.haveGunCheck("foam")) b.giveGuns("foam") + tech.isWormHoleBullets = false; + } + } + }, + { + name: "cosmic string", + description: "after tunneling through mobs with a wormhole
stun them and do radioactive damage", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" + }, + requires: "wormhole", + effect() { + tech.isWormholeDamage = true + }, + remove() { + tech.isWormholeDamage = false + } + }, + { + name: "invariant", + description: "while placing your wormhole
use energy to pause time", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" && !tech.isNoDraftPause + }, + requires: "wormhole, not eternalism", + effect() { + tech.isWormHolePause = true + }, + remove() { + if (tech.isWormHolePause && m.isBodiesAsleep) m.wakeCheck(); + tech.isWormHolePause = false + } + }, + { + name: "charmed baryons", + description: `–33% movement and jumping
wormholes drain zero energy`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" && !tech.isWormholeMapIgnore + }, + requires: "wormhole, not affine connection", + effect() { + tech.isFreeWormHole = true + tech.baseFx *= 0.66 + tech.baseJumpForce *= 0.66 + m.setMovement() + }, + //also removed in m.setHoldDefaults() if player switches into a bad field + remove() { + tech.isFreeWormHole = false + if (!tech.isNeutronium) { + tech.baseFx = 0.08 + tech.baseJumpForce = 10.5 + m.setMovement() + } + } + }, + { + name: "affine connection", + description: "wormholes can tunnel through anything
for +200% energy drain", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" && !tech.isFreeWormHole + }, + requires: "wormhole, not charmed baryons", + effect() { + tech.isWormholeMapIgnore = true + }, + remove() { + tech.isWormholeMapIgnore = false + } + }, + //************************************************** + //************************************************** experimental + //************************************************** modes + //************************************************** + // { + // name: "-ship-", + // description: "experiment: fly around with no legs
aim with the keyboard", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isNonRefundable: true, + // isBadRandomOption: true, + // isExperimentalMode: true, + // allowed() { + // return build.isExperimentSelection && !m.isShipMode && m.fieldUpgrades[m.fieldMode].name !== "negative mass" + // }, + // requires: "", + // effect() { + // m.shipMode() + // }, + // remove() {} + // }, + // { + // name: "-quantum leap-", + // description: "experiment: every 20 seconds
become an alternate version of yourself", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isBadRandomOption: true, + // isExperimentalMode: true, + // allowed() { + // return build.isExperimentSelection + // }, + // requires: "", + // interval: undefined, + // effect() { + // this.interval = setInterval(() => { + // if (!build.isExperimentSelection) { + // m.switchWorlds() + // simulation.trails() + // } + // }, 20000); //every 20 seconds + + // }, + // remove() { + // if (this.count > 0) clearTimeout(this.interval); + // } + // }, + // { + // name: "-shields-", + // description: "experiment: every 5 seconds
all mobs gain a shield", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isBadRandomOption: true, + // isExperimentalMode: true, + // allowed() { + // return build.isExperimentSelection + // }, + // requires: "", + // effect() { + // this.interval = setInterval(() => { + // if (!build.isExperimentSelection) { + // for (let i = 0; i < mob.length; i++) { + // if (!mob[i].isShielded && !mob[i].shield && mob[i].isDropPowerUp) spawn.shield(mob[i], mob[i].position.x, mob[i].position.y, 1, true); + // } + // } + // }, 5000); //every 5 seconds + // }, + // interval: undefined, + // remove() { + // if (this.count > 0) clearTimeout(this.interval); + // } + // }, + // { + // name: "-Fourier analysis-", + // description: "experiment: your aiming is random", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isBadRandomOption: true, + // isExperimentalMode: true, + // allowed() { + // return build.isExperimentSelection && !m.isShipMode + // }, + // requires: "not ship", + // effect() { + // m.look = () => { + // m.angle = 2 * Math.sin(m.cycle * 0.0133) + Math.sin(m.cycle * 0.013) + 0.5 * Math.sin(m.cycle * 0.031) + 0.33 * Math.sin(m.cycle * 0.03) + // const scale = 0.8; + // m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + // m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + // m.transX += (m.transSmoothX - m.transX) * 0.07; + // m.transY += (m.transSmoothY - m.transY) * 0.07; + // } + // }, + // remove() { + // if (this.count > 0) m.look = m.lookDefault() + // } + // }, + // { + // name: "-panopticon-", + // description: "experiment: mobs can always see you", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isBadRandomOption: true, + // isExperimentalMode: true, + // allowed() { + // return build.isExperimentSelection + // }, + // requires: "", + // effect() { + // this.interval = setInterval(() => { + // if (!build.isExperimentSelection) { + // for (let i = 0; i < mob.length; i++) { + // if (!mob[i].shield && mob[i].isDropPowerUp) { + // mob[i].locatePlayer() + // mob[i].seePlayer.yes = true; + // } + // } + // } + // }, 1000); //every 1 seconds + // }, + // interval: undefined, + // remove() { + // if (this.count > 0) clearTimeout(this.interval); + // } + // }, + // { + // name: "-decomposers-", + // description: "experiment: after they die
mobs leave behind spawns", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isBadRandomOption: true, + // isExperimentalMode: true, + // allowed() { + // return build.isExperimentSelection + // }, + // requires: "", + // effect() { + // tech.deathSpawns = 0.2 + // }, + // remove() { + // tech.deathSpawns = 0 + // } + // }, + + + + //************************************************** + //************************************************** JUNK + //************************************************** tech + //************************************************** + // { + // name: "junk", + // description: "", + // maxCount: 9, + // count: 0, + // frequency: 0, + // isNonRefundable: true, + // isJunk: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() { + + // }, + // remove() {} + // }, + { + name: "swap meet", + description: "normal tech become JUNK
and JUNK become normal tech", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0, len = tech.tech.length; i < len; i++) { + tech.tech[i].isJunk = !tech.tech[i].isJunk + if (tech.tech[i].isJunk) { } else { } + + if (tech.tech[i].frequency > 0) { + tech.tech[i].frequency = 0 + } else { + tech.tech[i].frequency = 2 + } + } + }, + remove() { } + }, + // { + // name: "pocket dimension", + // description: "rotate tech descriptions into a higher spacial dimension", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isJunk: true, + // isNonRefundable: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() { + // document.getElementById("choose-grid").classList.add("flipX"); + // }, + // remove() {} + // }, + { + name: "random", + link: `random`, + delay: 333, + descriptionFunction() { + const delay = 333 + const loop = () => { + if ((simulation.isChoosing) && m.alive && !build.isExperimentSelection) { + const dmg = Math.floor(27 * Math.random()) * 0.01 + this.text = `+${(dmg * 100).toFixed(0).padStart(2, '0')}% damage` + this.damage = 1 + dmg + if (document.getElementById(`damage-JUNK-id${this.id}`)) document.getElementById(`damage-JUNK-id${this.id}`).innerHTML = this.text + setTimeout(() => { + loop() + }, delay); + } + } + setTimeout(() => { + loop() + }, delay); + this.id++ + return `${this.text}` + }, + maxCount: 3, + count: 0, + frequency: 1, + isJunk: true, + allowed() { + return !build.isExperimentSelection + }, + requires: "NOT EXPERIMENT MODE", + damage: 0, + effect() { + tech.damage *= this.damage + }, + remove() { + if (this.count > 0) tech.damage /= this.damage + } + }, + { + name: "boost", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed() { + return !build.isExperimentSelection + }, + requires: "NOT EXPERIMENT MODE", + effect() { + powerUps.spawnDelay("boost", this.spawnCount) + }, + remove() { }, + id: 0, + text: "", + delay: 100, + spawnCount: 0, + descriptionFunction() { + let count = 9999 * Math.random() + const loop = () => { + if ((simulation.isChoosing) && m.alive && !build.isExperimentSelection) { //&& (!simulation.isChoosing || this.count === 0) //simulation.paused || + count += 4.5 + const waves = 2 * Math.sin(count * 0.0133) + Math.sin(count * 0.013) + 0.5 * Math.sin(count * 0.031) + 0.33 * Math.sin(count * 0.03) + this.spawnCount = Math.floor(100 * Math.abs(waves)) + this.text = `spawn ${this.spawnCount.toLocaleString(undefined, { minimumIntegerDigits: 3 })} ${powerUps.orb.boost(1)}
that give +${(powerUps.boost.damage * 100).toFixed(0)}% damage for ${(powerUps.boost.duration / 60).toFixed(0)} seconds` + if (document.getElementById(`boost-JUNK-id${this.id}`)) document.getElementById(`boost-JUNK-id${this.id}`).innerHTML = this.text + setTimeout(() => { + loop() + }, this.delay); + } + } + setTimeout(() => { + loop() + }, this.delay); + this.id++ + return `${this.text}` + }, + }, + { + name: "placebo", + description: "+777% damage
+777% defense", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed: () => true, + requires: "", + effect() { + if (Math.random() < 0.1) tech.damage *= 8.77 + }, + remove() { } + }, + { + name: "universal healthcare", + description: "make your damage negative", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed: () => true, + requires: "", + effect() { + tech.damage *= -1 + }, + remove() { } + }, + // { + // name: "synchrotron", + // descriptionFunction() { + // return `power ups change into a different flavor after a boss dies` + // }, + // maxCount: 3, + // count: 0, + // frequency: 1, + // frequencyDefault: 1, + // allowed: () => true, + // requires: "", + // effect() { + // }, + // remove() { + // } + // }, + { + name: "return", + description: "return to the introduction level
reduce combat difficulty by 2 levels", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed: () => true, + requires: "", + effect() { + level.difficultyDecrease(simulation.difficultyMode * 2) + level.onLevel = 0 + simulation.clearNow = true //end current level + }, + remove() { } + }, + { + name: "panpsychism", + description: "awaken all blocks
blocks have a chance to spawn power ups", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed: () => true, + requires: "", + effect() { + setInterval(() => { + for (let i = body.length - 1; i > -1; i--) { + if (!body[i].isNotHoldable) { + Matter.Composite.remove(engine.world, body[i]); + spawn.blockMob(body[i].position.x, body[i].position.y, body[i], 0); + if (!body[i].isAboutToBeRemoved) mob[mob.length - 1].isDropPowerUp = true + body.splice(i, 1); + } + } + }, 6000); + }, + remove() { } + }, + { + name: "meteor shower", + description: "take a shower, but meteors instead of water", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed: () => true, + requires: "", + effect() { + setInterval(() => { + + fireBlock = function (xPos, yPos) { + const index = body.length + spawn.bodyRect(xPos, yPos, 20 + 50 * Math.random(), 20 + 50 * Math.random()); + const bodyBullet = body[index] + Matter.Body.setVelocity(bodyBullet, { + x: 5 * (Math.random() - 0.5), + y: 10 * (Math.random() - 0.5) + }); + bodyBullet.isAboutToBeRemoved = true + bodyBullet.collisionFilter.category = cat.body; + bodyBullet.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet + bodyBullet.classType = "body"; + Composite.add(engine.world, bodyBullet); //add to world + setTimeout(() => { //remove block + for (let i = 0; i < body.length; i++) { + if (body[i] === bodyBullet) { + Matter.Composite.remove(engine.world, body[i]); + body.splice(i, 1); + } + } + }, 4000 + Math.floor(9000 * Math.random())); + } + fireBlock(player.position.x + 600 * (Math.random() - 0.5), player.position.y - 500 - 500 * Math.random()); + // for (let i = 0, len = Math.random(); i < len; i++) { // } - // if (this.count > 0) powerUps.research.changeRerolls(2) - } + + }, 1000); }, - { - name: "electrostatic induction", - description: "foam bubbles are electrically charged
causing attraction to nearby mobs", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return !tech.isBulletTeleport && (tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine) - }, - requires: "foam, not uncertainty", - effect() { - tech.isFoamAttract = true - }, - remove() { - tech.isFoamAttract = false - } - }, - { - name: "uncertainty principle", - description: "foam and wave positions are erratic
+53% foam and wave damage", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return (!tech.isFoamAttract && (tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine)) || (tech.haveGunCheck("wave") && !tech.is360Longitudinal) - }, - requires: "foam, wave, not isotropic, electrostatic induction", - effect() { - tech.isBulletTeleport = true - }, - remove() { - tech.isBulletTeleport = false; - } - }, - { - name: "aerogel", - description: "–50% foam duration and foam bubbles float
+180% foam damage", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine - }, - requires: "foam", - effect() { - tech.isFastFoam = true - tech.foamGravity = -0.0003 - }, - remove() { - tech.isFastFoam = false; - tech.foamGravity = 0.00008 - } - }, - { - name: "surface tension", - description: "+43% foam damage", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine - }, - requires: "foam", - effect() { - tech.foamDamage += 0.011 * 0.43 - }, - remove() { - tech.foamDamage = 0.011; - } - }, - { - name: "foam fractionation", - description: "if you have below 300 ammo
+100% foam gun bubble size", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("foam") - }, - requires: "foam", - effect() { - tech.isAmmoFoamSize = true - }, - remove() { - tech.isAmmoFoamSize = false; - } - }, - { - name: "ideal gas law", - description: `remove all current foam ammo
+1200% foam ammo per ${powerUps.orb.ammo(1)}`, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("foam") && !tech.isEnergyNoAmmo - }, - requires: "foam, not non-renewables", - ammoLost: 0, - effect() { - b.guns[8].ammoPack = b.guns[8].ammoPack * 12; - this.ammoLost = b.guns[8].ammo - b.guns[8].ammo = 0 - simulation.updateGunHUD() - }, - remove() { - b.guns[8].ammoPack = 24 - if (this.count) { - b.guns[8].ammo += this.ammoLost - simulation.updateGunHUD() - } - } - }, - { - name: "pressure vessel", - description: "build up charge while firing foam gun
after firing discharge foam bubbles", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("foam") - }, - requires: "foam", - effect() { - tech.isFoamPressure = true; - b.guns[8].chooseFireMethod() - }, - remove() { - tech.isFoamPressure = false; - b.guns[8].chooseFireMethod() - } - }, - { - name: "capacitor bank", - // description: "charge effects build up almost instantly
throwing blocks, foam, railgun, pulse, tokamak", - descriptionFunction() { - return `charge effects build up almost instantly
throwing, ${tech.haveGunCheck("foam", false) ? "foam" : "foam"}, ${tech.isPlasmaBall ? "plasma ball" : "plasma ball"}, ${tech.isRailGun ? "railgun" : "railgun"}, ${tech.isPulseLaser ? "pulse" : "pulse"}, ${tech.isTokamak ? "tokamak" : "tokamak"}` - }, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.blockDamage > 0.075 || tech.isRailGun || (tech.haveGunCheck("foam") && tech.isFoamPressure) || tech.isTokamak || tech.isPulseLaser || tech.isPlasmaBall - }, - requires: "mass driver, railgun, foam, pressure vessel, pulse, tokamak, plasma ball", - effect() { - tech.isCapacitor = true; - }, - remove() { - tech.isCapacitor = false; - } - }, - { - name: "Bitter electromagnet", - descriptionFunction() { - return `railgun charges +33% slower
+100% harpoon density and damage` - }, - isGunTech: true, - maxCount: 3, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("harpoon") && tech.isRailGun - }, - requires: "harpoon, railgun", - effect() { - tech.railChargeRate *= 1.06 - tech.harpoonDensity += 0.0065 - }, - remove() { - tech.railChargeRate = 0.97; - tech.harpoonDensity = 0.0065 - } - }, - { - name: "railgun", - description: `hold fire to charge harpoon and release to launch
harpoons can't retract`, - // description: `+900% harpoon ammo, but it can't retract
+50% harpoon density and damage`, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("harpoon") && !tech.isFilament && !tech.isHarpoonPowerUp && !tech.isGrapple && !tech.isBoostReplaceAmmo - }, - requires: "harpoon, not UHMWPE, induction furnace, grappling hook, quasiparticles", - ammoBonus: 9, - effect() { - tech.isRailGun = true; - b.guns[9].chooseFireMethod() - b.guns[9].ammoPack = 5; - b.guns[9].ammo = b.guns[9].ammo * 6; - simulation.updateGunHUD(); - }, - remove() { - if (tech.isRailGun) { - tech.isRailGun = false; - b.guns[9].chooseFireMethod() - b.guns[9].ammoPack = 1.7; - b.guns[9].ammo = Math.ceil(b.guns[9].ammo / 6); - simulation.updateGunHUD(); - } - } - }, - { - name: "grappling hook", - description: `harpoons attach to the map and pull you
your rope extends while holding fire`, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("harpoon") && !tech.isFilament && !tech.isHarpoonPowerUp && !tech.isRailGun && !tech.isFireMoveLock - }, - requires: "harpoon, not railgun, UHMWPE, induction furnace, Higgs mechanism", - effect() { - tech.isGrapple = true; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "harpoon") b.guns[i].chooseFireMethod() - } - }, - remove() { - if (tech.isGrapple) { - tech.isGrapple = false; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "harpoon") b.guns[i].chooseFireMethod() - } - } - } - }, - { - name: "bulk modulus", - description: `while grappling become invulnerable
drain energy`, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("harpoon") && tech.isGrapple && !tech.isRailEnergy - }, - requires: "grappling hook, not alternator", - effect() { - tech.isImmuneGrapple = true; - }, - remove() { - tech.isImmuneGrapple = false - } - }, - { - name: "alternator", - description: "+90% harpoon energy efficiency", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("harpoon") && !tech.isImmuneGrapple - }, - requires: "harpoon, not bulk modulus", - effect() { - tech.isRailEnergy = true; - }, - remove() { - tech.isRailEnergy = false; - } - }, - { - name: "Bessemer process", - descriptionFunction() { - return `+${(10 * Math.sqrt(b.guns[9].ammo)).toFixed(0)}% harpoon size and damage
(1/10 √ harpoon ammo)` - }, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("harpoon") && !tech.isShieldPierce - }, - requires: "harpoon, not ceramics", - effect() { - tech.isLargeHarpoon = true; - }, - remove() { - tech.isLargeHarpoon = false; - } - }, - { - name: "smelting", - descriptionFunction() { - return `forge ${this.removeAmmo()} ammo into a new harpoon
fire +1 harpoon with each shot` - }, - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - ammoRemoved: 0, - removeAmmo() { - return (tech.isRailGun ? 5 : 1) * (2 + 2 * this.count) - }, - allowed() { - return tech.haveGunCheck("harpoon") && b.guns[9].ammo >= this.removeAmmo() - }, - requires: "harpoon", - effect() { - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "harpoon") { - const removeAmmo = this.removeAmmo() - this.ammoRemoved += removeAmmo - b.guns[i].ammo -= removeAmmo - if (b.guns[i].ammo < 0) b.guns[i].ammo = 0 - simulation.updateGunHUD(); - tech.extraHarpoons++; - break - } - } - }, - remove() { - if (tech.extraHarpoons) { - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "harpoon") { - b.guns[i].ammo += this.ammoRemoved - simulation.updateGunHUD(); + remove() { } + }, + { + name: "startle response", + description: `if a threat is nearby, activate a ${powerUps.orb.boost(1)}
and lock your mouse until you press escape`, + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed: () => true, + requires: "", + effect() { + // tech.damage *= 1.33 + setInterval(() => { + if (powerUps.boost.endCycle < m.cycle && !simulation.paused && m.alive) { + for (let i = 0; i < mob.length; i++) { + if (mob[i].distanceToPlayer2() < 400000) { //650 + canvas.requestPointerLock(); + powerUps.boost.effect(); break } } } - this.ammoRemoved = 0 - tech.extraHarpoons = 0; - } + }, 2000); }, - { - name: "UHMWPE", - descriptionFunction() { - return `+${(b.guns[9].ammo*1.25).toFixed(0)}% harpoon rope length
(1/80 of harpoon ammo)` - }, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("harpoon") && !tech.isRailGun && !tech.isGrapple - }, - requires: "harpoon, not grappling hook, railgun", - effect() { - tech.isFilament = true; - }, - remove() { - tech.isFilament = false; - } - }, - { - name: "induction furnace", - description: "after using harpoon to collect a power up
+600% harpoon damage", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("harpoon") && !tech.isRailGun && !tech.isGrapple - }, - requires: "harpoon, not grappling hook, railgun", - effect() { - tech.isHarpoonPowerUp = true - }, - remove() { - tech.isHarpoonPowerUp = false - } - }, - { - name: "quasiparticles", - descriptionFunction() { - return `convert current and future ${powerUps.orb.ammo(1)} into ${powerUps.orb.boost(1)} which
give +${(powerUps.boost.damage*100).toFixed(0)}% damage for ${(powerUps.boost.duration/60).toFixed(0)} seconds` - }, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return ((tech.haveGunCheck("wave") && !tech.isInfiniteWaveAmmo) || tech.haveGunCheck("laser") || (tech.haveGunCheck("harpoon") && !tech.isRailGun)) && !tech.isEnergyNoAmmo - }, - requires: "harpoon, laser, wave, frequency, not railgun, non-renewables", - effect() { - tech.isBoostReplaceAmmo = true - for (let i = powerUp.length - 1; i > -1; i--) { - if (powerUp[i].name === "ammo") { - powerUps.spawn(powerUp[i].position.x + 50 * (Math.random() - 0.5), powerUp[i].position.y + 50 * (Math.random() - 0.5), "boost"); - Matter.Composite.remove(engine.world, powerUp[i]); - powerUp.splice(i, 1); - } - } + remove() { } + }, + { + name: "closed timelike curve", + description: "spawn 5 field power ups, but every 12 seconds
teleport a second into your future or past", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed: () => true, + requires: "", + effect() { + for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "field"); - }, - remove() { - tech.isBoostReplaceAmmo = false - } - }, - { - name: "optical amplifier", - description: "gain 3 random laser guntech
laser only turns off if you have no energy", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - isNonRefundable: true, - allowed() { - return tech.haveGunCheck("laser") && !tech.isPulseLaser - }, - requires: "laser gun, not pulse", - effect() { - let techGiven = 0 - for (let j = 0; j < 3; j++) { - const names = ["quasiparticles", "lens", "compound lens", "arc length", "infrared diode", "free-electron laser", "dye laser", "relativistic momentum", "specular reflection", "diffraction grating", "diffuse beam", "output coupler", "slow light", "laser-bot", "laser-bot upgrade"] - //convert names into indexes - const options = [] - for (let i = 0; i < names.length; i++) { - for (let k = 0; k < tech.tech.length; k++) { - if (tech.tech[k].name === names[i]) { - options.push(k) - break + function loop() { + if (!simulation.paused && m.alive) { + if (!(simulation.cycle % 720)) { + requestAnimationFrame(() => { + if ((simulation.cycle % 1440) > 720) { //kinda alternate between each option + m.rewind(60) + m.energy += 0.4 //to make up for lost energy + } else { + simulation.timePlayerSkip(60) } - } + }); //wrapping in animation frame prevents errors, probably } - //remove options that don't meet requirements - for (let i = options.length - 1; i > -1; i--) { - const index = options[i] - if (!(tech.tech[index].count < tech.tech[index].maxCount) || !tech.tech[index].allowed()) { - options.splice(i, 1); - } - } - //pick one option - if (options.length) { - const index = options[Math.floor(Math.random() * options.length)] - simulation.makeTextLog(`tech.giveTech("${tech.tech[index].name}") //optical amplifier`, 360); - tech.giveTech(index) - techGiven++ - } - } - if (techGiven > 0) { - tech.isStuckOn = true - } else { //eject if none found - simulation.makeTextLog(`0 tech found //optical amplifier`); - const loop = () => { - if (!simulation.paused && m.alive) { - for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].name === this.name) powerUps.ejectTech(i) - } - return - } - requestAnimationFrame(loop); - } - requestAnimationFrame(loop); - } - }, - remove() { - tech.isStuckOn = false - } - }, - { - name: "relativistic momentum", - description: "lasers push mobs and blocks", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.haveGunCheck("laser") && !tech.isPulseLaser) || tech.isLaserBotUpgrade - }, - requires: "laser, not pulse", - effect() { - tech.isLaserPush = true; - }, - remove() { - tech.isLaserPush = false; - } - }, - { - name: "iridescence", - // description: "if a laser hits a mob at a low angle of illumination
+66% laser damage", - description: "if laser beams hit mobs near their center
+100% laser damage", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.haveGunCheck("laser") && !tech.isPulseLaser) || tech.isLaserBotUpgrade || tech.isLaserMine - }, - requires: "laser, not pulse", - effect() { - tech.laserCrit += 1; - }, - remove() { - tech.laserCrit = 0; - } - }, - { - name: "lens", - description: "+150% laser gun damage if it passes
through a revolving 90° arc circular lens", //π / 2 - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("laser") - }, - requires: "laser", - effect() { - tech.isLaserLens = true - b.guns[11].chooseFireMethod() - // if (this.count > 0) b.guns[11].lensDamageOn += 20 * Math.PI / 180 - // b.guns[11].arcRange = 0.78 - }, - remove() { - tech.isLaserLens = false - b.guns[11].chooseFireMethod() - // b.guns[11].lensDamageOn = 2.5 // 100% + 150% - // b.guns[11].arcRange = 0 - } - }, - { - name: "compound lens", - description: "+50% laser lens damage
+15° lens arc", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("laser") && tech.isLaserLens - }, - requires: "lens", - effect() { - b.guns[11].arcRange += 15 * Math.PI / 180 / 2 - b.guns[11].lensDamageOn += 0.5 - }, - remove() { - b.guns[11].arcRange = 90 * Math.PI / 180 / 2 //0.78 divded by 2 because of how it's drawn - b.guns[11].lensDamageOn = 2.5 - } - }, - { - name: "specular reflection", - description: "+2 laser beam reflections", - isGunTech: true, - maxCount: 3, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.isLaserBotUpgrade) && !tech.isWideLaser && !tech.isPulseLaser && !tech.historyLaser - }, - requires: "laser, not diffuse beam, pulse, or slow light", - effect() { - tech.laserReflections += 2; - }, - remove() { - tech.laserReflections = 2; - } - }, - { - name: "diffraction grating", - description: `+1 diverging laser gun beam`, - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.haveGunCheck("laser") && !tech.isWideLaser && !tech.historyLaser - }, - requires: "laser gun, diffuse beam, or slow light", - effect() { - tech.beamSplitter++ - b.guns[11].chooseFireMethod() - }, - remove() { - if (tech.beamSplitter !== 0) { - tech.beamSplitter = 0 - b.guns[11].chooseFireMethod() - } - } - }, - { - name: "diffuse beam", - link: `diffuse beam`, - description: "laser gun beam is wider and doesn't reflect
+220% laser damage", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.beamSplitter && !tech.isPulseLaser && !tech.historyLaser - }, - requires: "laser gun, not specular reflection, diffraction grating, slow light, pulse", - effect() { - if (tech.wideLaser === 0) tech.wideLaser = 3 - tech.isWideLaser = true; - b.guns[11].chooseFireMethod() - }, - remove() { - if (tech.isWideLaser) { - // tech.wideLaser = 0 - tech.isWideLaser = false; - b.guns[11].chooseFireMethod() - } - } - }, - { - name: "output coupler", - description: "+30% laser gun beam width
+30% laser damage", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("laser") && tech.isWideLaser - }, - requires: "laser gun, diffuse beam", - effect() { - tech.wideLaser += 2 - b.guns[11].chooseFireMethod() - }, - remove() { - if (tech.isWideLaser) { - tech.wideLaser = 3 - } else { - tech.wideLaser = 0 - } - b.guns[11].chooseFireMethod() - } - }, - { - name: "slow light", - description: "laser gun beam is spread into your recent past
+300% total beam damage", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.beamSplitter && !tech.isWideLaser - }, - requires: "laser gun, not specular reflection, diffraction grating, diffuse beam", - effect() { - // this.description = `add 5 more laser beams into into your past` - tech.historyLaser++ - b.guns[11].chooseFireMethod() - }, - remove() { - if (tech.historyLaser) { - tech.historyLaser = 0 - b.guns[11].chooseFireMethod() - } - } - }, - { - name: "infrared diode", - description: "+60% laser energy efficiency
infrared light is outside visual perception", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return (tech.haveGunCheck("laser") || tech.isLaserBotUpgrade || tech.isLaserMine) && !tech.isPulseLaser && tech.laserDrain === 0.0018 - }, - requires: "laser, not free-electron, pulse", - effect() { - tech.laserDrain *= 0.4; //100%-50% - tech.laserColor = "transparent" //"rgb(255,0,20,0.02)" - // tech.laserColorAlpha = "rgba(255,0,20,0.05)" - }, - remove() { - tech.laserDrain = 0.0018; - tech.laserColor = "#f02" - tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)" - } - }, - { - name: "dye laser", - description: "+25% laser energy efficiency
+25% laser damage", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.isLaserBotUpgrade) && !tech.isPulseLaser && tech.laserDrain === 0.0018 - }, - requires: "laser, not pulse, infrared diode", - effect() { - tech.laserDrain *= 0.75 - tech.laserDamage *= 1.25 - tech.laserColor = "rgb(0, 11, 255)" - tech.laserColorAlpha = "rgba(0, 11, 255,0.5)" - }, - remove() { - tech.laserDrain = 0.0018; - tech.laserDamage = 0.18; //used in check on pulse and diode: tech.laserDamage === 0.18 - tech.laserColor = "#f00" - tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)" - } - }, - { - name: "free-electron laser", - description: "–250% laser energy efficiency
+200% laser damage", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.isLaserBotUpgrade) && !tech.isPulseLaser && tech.laserDrain === 0.0018 - }, - requires: "laser, not pulse, infrared diode", - effect() { - tech.laserDrain *= 1 + 2.5 //250% more drain - tech.laserDamage *= 1 + 2 //190% more damage - tech.laserColor = "#83f" - tech.laserColorAlpha = "rgba(136, 51, 255,0.5)" - }, - remove() { - tech.laserDrain = 0.0018; - tech.laserDamage = 0.18; //used in check on pulse and diode: tech.laserDamage === 0.18 - tech.laserColor = "#f00" - tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)" - } - }, - { - name: "pulse", - description: "charge your energy and release it as a
laser pulse that initiates an explosion cluster", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.isWideLaser && tech.laserDrain === 0.0018 && !tech.isStuckOn - }, - requires: "laser gun, not specular reflection, diffuse, free-electron laser, optical amplifier", - effect() { - tech.isPulseLaser = true; - b.guns[11].chooseFireMethod() - }, - remove() { - if (tech.isPulseLaser) { - tech.isPulseLaser = false; - b.guns[11].chooseFireMethod() - } - } - }, - //************************************************** - //************************************************** field - //************************************************** tech - //************************************************** - { - name: "zero point energy", - description: `use ${powerUps.orb.research(2)}
+100 maximum energy`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "time dilation") && (build.isExperimentSelection || powerUps.research.count > 1) - }, - requires: "standing wave, pilot wave, time dilation", - effect() { - tech.harmonicEnergy = 1 - m.setMaxEnergy() - for (let i = 0; i < 2; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - }, - remove() { - tech.harmonicEnergy = 0; - m.setMaxEnergy() - if (this.count > 0) powerUps.research.changeRerolls(2) - } - }, - { - name: "spherical harmonics", - description: "+40% standing wave deflection efficiency
no longer deactivates on mob shields", //standing wave oscillates in a 3rd dimension
- isFieldTech: true, - maxCount: 9, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "standing wave" - }, - requires: "standing wave", - effect() { - tech.harmonics++ - m.fieldShieldingScale = (tech.isStandingWaveExpand ? 0.9 : 1.6) * Math.pow(0.6, (tech.harmonics - 2)) - m.harmonicShield = m.harmonicAtomic - }, - remove() { - tech.harmonics = 2 - m.fieldShieldingScale = (tech.isStandingWaveExpand ? 0.9 : 1.6) * Math.pow(0.6, (tech.harmonics - 2)) - m.harmonicShield = m.harmonic3Phase - } - }, - { - name: "expansion", - description: "+50% standing wave deflection efficiency
using standing wave field expands its radius", - // description: "use energy to expand standing wave
the field slowly contracts when not used", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "standing wave" - }, - requires: "standing wave", - effect() { - tech.isStandingWaveExpand = true - m.fieldShieldingScale = (tech.isStandingWaveExpand ? 0.9 : 1.3) * Math.pow(0.6, (tech.harmonics - 2)) - }, - remove() { - tech.isStandingWaveExpand = false - m.fieldShieldingScale = (tech.isStandingWaveExpand ? 0.9 : 1.3) * Math.pow(0.6, (tech.harmonics - 2)) - m.harmonicRadius = 1 - } - }, - { - name: "triple point", - descriptionFunction() { - return `+1.5 second ice IX freeze effect
spawn ${powerUps.orb.coupling(10)} that each give +0.1 coupling
${ m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"}` - }, - isFieldTech: true, - maxCount: 3, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" - }, - requires: "standing wave, perfect diamagnetism", - effect() { - tech.iceIXFreezeTime += 90 - powerUps.spawnDelay("coupling", 10) - }, - remove() { - tech.iceIXFreezeTime = 150 - if (this.count) { - m.couplingChange(-this.count) - } - } - }, - { - name: "bremsstrahlung", - description: "deflecting and thrown blocks
do braking damage to mobs", - isFieldTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" - }, - requires: "standing wave, perfect diamagnetism, pilot wave", - effect() { - tech.blockDmg += 3 //if you change this value also update the for loop in the electricity graphics in m.pushMass - }, - remove() { - tech.blockDmg = 0; - } - }, - { - name: "cherenkov radiation", //deflecting and blocks - description: "bremsstrahlung's effects are radioactive
+300% damage over 6 seconds", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && tech.blockDmg - }, - requires: "bremsstrahlung", - effect() { - tech.isBlockRadiation = true - }, - remove() { - tech.isBlockRadiation = false; - } - }, - { - name: "flux pinning", - description: "after deflecting a mob
it is stunned for up to 4 seconds", - isFieldTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" - }, - requires: "a field that can block", - effect() { - tech.isStunField += 240; - }, - remove() { - tech.isStunField = 0; - } - }, - { - name: "eddy current brake", - description: "perfect diamagnetism slows nearby mobs
effect radius scales with stored energy", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" - }, - requires: "perfect diamagnetism", - effect() { - tech.isPerfectBrake = true; - }, - remove() { - tech.isPerfectBrake = false; - } - }, - { - name: "Meissner effect", - description: "+55% perfect diamagnetism radius
+22° perfect diamagnetism circular arc", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" - }, - requires: "perfect diamagnetism", - effect() { - tech.isBigField = true; - }, - remove() { - tech.isBigField = false; - } - }, - { - name: "tessellation", - description: `use ${powerUps.orb.research(2)}
+50% defense`, - // description: "use 4 research
reduce defense by 50%", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "negative mass") && (build.isExperimentSelection || powerUps.research.count > 3) - }, - requires: "perfect diamagnetism, negative mass, pilot wave", - effect() { - tech.isFieldHarmReduction = true - for (let i = 0; i < 2; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - }, - remove() { - tech.isFieldHarmReduction = false - if (this.count > 0) powerUps.research.changeRerolls(2) - } - }, - { - name: "radiative equilibrium", - description: "after losing health
+200% damage for 10 seconds", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "negative mass" - }, - requires: "negative mass, pilot wave", - effect() { - tech.isHarmDamage = true; - }, - remove() { - tech.isHarmDamage = false; - } - }, - { - name: "dynamic equilibrium", - descriptionFunction() { - return `increase damage by your defense and
5% of your last ${tech.isEnergyHealth ? "energy" : "health"} loss   (+${(100*Math.max(5,tech.lastHitDamage) * m.lastHit * (2 - m.defense())).toFixed(0)}% damage)` - }, // = +${10*m.defense()}% - // descriptionFunction() { return `increase damage by your last ${tech.isEnergyHealth ? "energy" : "health"} loss
(${(tech.lastHitDamage).toFixed(0)}%)(${(100*m.lastHit).toFixed(0)} ${tech.isEnergyHealth ? "energy" : "health"})(${2 - m.defense()} defense) = ${(100*tech.lastHitDamage * m.lastHit * (2 - m.defense())).toFixed(0)}% damage ` }, // = +${10*m.defense()}% - isFieldTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "negative mass" || m.fieldUpgrades[m.fieldMode].name === "standing wave") && !tech.isCloakHealLastHit - }, - requires: "negative mass, pilot wave, standing wave, not patch", - effect() { - tech.lastHitDamage += 5; - }, - remove() { - tech.lastHitDamage = 0; - } - }, - { - name: "neutronium", - description: `move and jump 20% slower
if your field is active +90% defense`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "negative mass" - }, - requires: "negative mass", - effect() { - tech.isNeutronium = true - tech.baseFx *= 0.8 - tech.baseJumpForce *= 0.8 - m.setMovement() - }, - //also removed in m.setHoldDefaults() if player switches into a bad field - remove() { - tech.isNeutronium = false - if (!tech.isFreeWormHole) { - tech.baseFx = 0.08 - tech.baseJumpForce = 10.5 - m.setMovement() - } - } - }, - { - name: "aerostat", - description: `+88% damage while off the ground
-22% damage while on the ground`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "negative mass" - }, - requires: "negative mass", - effect() { - tech.isNoGroundDamage = true - }, - remove() { - tech.isNoGroundDamage = false - } - }, - { - name: "annihilation", - description: "after colliding with non-boss mobs
they are annihilated and –33% energy", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "negative mass" && !tech.isEnergyHealth - }, - requires: "negative mass, not mass-energy", - effect() { - tech.isAnnihilation = true - }, - remove() { - tech.isAnnihilation = false; - } - }, - { - name: "inertial mass", - description: "negative mass is larger and faster
blocks also move horizontally with the field", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "negative mass" - }, - requires: "negative mass", - effect() { - tech.isFlyFaster = true - }, - remove() { - tech.isFlyFaster = false; - } - }, - // { - // name: "Bose Einstein condensate", - // description: "use energy to freeze mobs in your field
pilot wave, negative mass, time dilation", - // isFieldTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "negative mass" || (m.fieldUpgrades[m.fieldMode].name === "time dilation" && !tech.isRewindField) - // }, - // requires: "pilot wave, negative mass, time dilation, not retrocausality", - // effect() { - // tech.isFreezeMobs = true - // }, - // remove() { - // tech.isFreezeMobs = false - // } - // }, - { - name: "bot manufacturing", - description: `use ${powerUps.orb.research(1)} to build
3 random bots`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBotTech: true, - isNonRefundable: true, - allowed() { - return powerUps.research.count > 0 && (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") - }, - requires: "molecular assembler, pilot wave", - effect() { - for (let i = 0; i < 1; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - m.energy = 0.01; - b.randomBot() - b.randomBot() - b.randomBot() - }, - remove() {} - }, - { - name: "bot prototypes", - description: `use ${powerUps.orb.research(2)}to build 2 random bots
and upgrade all bots to that type`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBotTech: true, - isNonRefundable: true, - allowed() { - return powerUps.research.count > 1 && (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") - }, - requires: "molecular assembler, pilot wave", - effect() { - for (let i = 0; i < 2; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - //fill array of available bots - const notUpgradedBots = [] - const num = 2 - notUpgradedBots.push(() => { - tech.giveTech("nail-bot upgrade") - for (let i = 0; i < num; i++) { - b.nailBot() - tech.nailBotCount++; - } - simulation.makeTextLog(`tech.isNailBotUpgrade = true`) - }) - notUpgradedBots.push(() => { - tech.giveTech("foam-bot upgrade") - for (let i = 0; i < num; i++) { - b.foamBot() - tech.foamBotCount++; - } - simulation.makeTextLog(`tech.isFoamBotUpgrade = true`) - }) - notUpgradedBots.push(() => { - tech.giveTech("boom-bot upgrade") - for (let i = 0; i < num; i++) { - b.boomBot() - tech.boomBotCount++; - } - simulation.makeTextLog(`tech.isBoomBotUpgrade = true`) - }) - notUpgradedBots.push(() => { - tech.giveTech("laser-bot upgrade") - for (let i = 0; i < num; i++) { - b.laserBot() - tech.laserBotCount++; - } - simulation.makeTextLog(`tech.isLaserBotUpgrade = true`) - }) - notUpgradedBots.push(() => { - tech.giveTech("orbital-bot upgrade") - for (let i = 0; i < num; i++) { - b.orbitBot() - tech.orbitBotCount++; - } - simulation.makeTextLog(`tech.isOrbitalBotUpgrade = true`) - }) - for (let i = 0; i < 2; i++) { //double chance for dynamo-bot, since it's very good for assembler - notUpgradedBots.push(() => { - tech.giveTech("dynamo-bot upgrade") - for (let i = 0; i < num; i++) { - b.dynamoBot() - tech.dynamoBotCount++; - } - simulation.makeTextLog(`tech.isDynamoBotUpgrade = true`) - }) - } - notUpgradedBots[Math.floor(Math.random() * notUpgradedBots.length)]() //choose random function from the array and run it - }, - remove() {} - }, - // { - // name: "mycelium manufacturing", - // link: `mycelium manufacturing`, - // // description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to grow spores`, - // descriptionFunction() { return `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to grow ${b.guns[6].nameString('s')}` }, - // isFieldTech: true, - // maxCount: 1, - // count: 0, - // frequency: 3, - // frequencyDefault: 3, - // allowed() { - // return (build.isExperimentSelection || powerUps.research.count > 0) && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isMissileField || tech.isIceField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport) - // }, - // requires: "molecular assembler, no other manufacturing, no drone tech", - // effect() { - // if (!build.isExperimentSelection) { - // for (let i = 0; i < 1; i++) { - // if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - // } - // } - // tech.isSporeField = true; - // }, - // remove() { - // tech.isSporeField = false; - // if (this.count > 0) powerUps.research.changeRerolls(1) - // } - // }, - // { - // name: "missile manufacturing", - // link: `missile manufacturing`, - // description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to construct missiles`, - // // description: "use 3 research to repurpose assembler
excess energy used to construct missiles", - // isFieldTech: true, - // maxCount: 1, - // count: 0, - // frequency: 3, - // frequencyDefault: 3, - // allowed() { - // return (build.isExperimentSelection || powerUps.research.count > 0) && m.maxEnergy > 0.5 && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isIceField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport || tech.isDronesTravel) - // }, - // requires: "molecular assembler, no other manufacturing, no drone tech", - // effect() { - // if (!build.isExperimentSelection) { - // for (let i = 0; i < 1; i++) { - // if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - // } - // } - // tech.isMissileField = true; - // }, - // remove() { - // tech.isMissileField = false; - // if (this.count > 0) powerUps.research.changeRerolls(1) - // } - // }, - // { - // name: "ice IX manufacturing", - // link: `ice IX manufacturing`, - // description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to condense ice IX`, - // // description: "use 3 research to repurpose assembler
excess energy used to condense ice IX", - // isFieldTech: true, - // maxCount: 1, - // count: 0, - // frequency: 3, - // frequencyDefault: 3, - // allowed() { - // return (build.isExperimentSelection || powerUps.research.count > 0) && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isMissileField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport || tech.isDronesTravel) - // }, - // requires: "molecular assembler, no other manufacturing, no drone tech", - // effect() { - // if (!build.isExperimentSelection) { - // for (let i = 0; i < 1; i++) { - // if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - // } - // } - // tech.isIceField = true; - // }, - // remove() { - // tech.isIceField = false; - // if (this.count > 0) powerUps.research.changeRerolls(1) - // } - // }, - { - name: "pair production", - description: "after picking up a power up
+200 energy", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" - }, - requires: "molecular assembler, pilot wave, standing wave", - effect() { - tech.isMassEnergy = true // used in m.grabPowerUp - m.energy += 2 - }, - remove() { - tech.isMassEnergy = false; - } - }, - { - name: "electric generator", - description: "after deflecting mobs
molecular assembler generates +50 energy", - isFieldTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "molecular assembler" - }, - requires: "molecular assembler", - effect() { - tech.deflectEnergy += 0.5; - }, - remove() { - tech.deflectEnergy = 0; - } - }, - { - name: "combinatorial optimization", - description: "+35% damage
–35% fire rate", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" - }, - requires: "cloaking, molecular assembler, plasma torch, pilot wave", - damage: 1.35, - effect() { - tech.damage *= this.damage - tech.aimDamage = 1.35 - b.setFireCD(); - }, - remove() { - if (this.count) tech.damage /= this.damage - tech.aimDamage = 1 - b.setFireCD(); - } - }, - { - name: "tokamak", - description: "throwing a block converts it into energy
and a pulsed fusion explosion", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" - }, - requires: "plasma torch, molecular assembler", - effect() { - tech.isTokamak = true; - }, - remove() { - tech.isTokamak = false; - } - }, - { - name: "degenerate matter", - description: "if your field is active
+75% defense", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") - }, - requires: "molecular assembler, plasma torch, perfect diamagnetism, pilot wave", - effect() { - tech.isHarmReduce = true - }, - remove() { - tech.isHarmReduce = false; - } - }, - { - name: "plasma-bot", - link: `plasma-bot`, - description: `use ${powerUps.orb.research(2)}to trade your field
for a bot that uses energy to emit plasma`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isBot: true, - isBotTech: true, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && !tech.isPlasmaBall && !tech.isExtruder && (build.isExperimentSelection || powerUps.research.count > 1) - }, - requires: "plasma torch, not extruder, plasma ball", - effect() { - tech.plasmaBotCount++; - b.plasmaBot(); - if (build.isExperimentSelection) { - document.getElementById("field-" + m.fieldMode).classList.remove("build-field-selected"); - document.getElementById("field-0").classList.add("build-field-selected"); - } - m.setField("field emitter") - for (let i = 0; i < 2; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - }, - remove() { - if (this.count > 0) { - tech.plasmaBotCount = 0; - b.clearPermanentBots(); - b.respawnBots(); - if (m.fieldMode === 0) { - m.setField("plasma torch") - if (build.isExperimentSelection) { - document.getElementById("field-0").classList.remove("build-field-selected"); - document.getElementById("field-" + m.fieldMode).classList.add("build-field-selected"); - } - } - powerUps.research.changeRerolls(2) - } - } - }, - { - name: "plasma jet", - link: `plasma jet`, - description: `use ${powerUps.orb.research(2)}
+50% plasma torch range`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.plasmaBotCount || m.fieldUpgrades[m.fieldMode].name === "plasma torch") && (build.isExperimentSelection || powerUps.research.count > 1) && !tech.isPlasmaBall - }, - requires: "plasma torch, not plasma ball", - effect() { - tech.isPlasmaRange += 0.5; - for (let i = 0; i < 2; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - }, - remove() { - tech.isPlasmaRange = 1; - if (this.count > 0) powerUps.research.changeRerolls(this.count * 2) - } - }, - { - name: "extruder", - description: "extrude a thin hot wire of plasma
increases damage and energy drain", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && !tech.isPlasmaBall - }, - requires: "plasma torch, not plasma ball", - effect() { - tech.isExtruder = true; - m.fieldUpgrades[m.fieldMode].set() - }, - remove() { - tech.isExtruder = false; - if (this.count && m.fieldUpgrades[m.fieldMode].name === "plasma torch") m.fieldUpgrades[m.fieldMode].set() - } - }, - { - name: "refractory metal", - description: "extrude metals at a higher temperature
increases effective radius and damage", - isFieldTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && tech.isExtruder - }, - requires: "extruder", - effect() { - tech.extruderRange += 55 - }, - remove() { - tech.extruderRange = 15 - } - }, - { - name: "plasma ball", - description: "grow an expanding ball of plasma
increases damage and energy drain", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && !tech.isExtruder && tech.isPlasmaRange === 1 - }, - requires: "plasma torch, not extruder, plasma jet", - effect() { - tech.isPlasmaBall = true; - m.fieldUpgrades[m.fieldMode].set() - }, - remove() { - tech.isPlasmaBall = false; - if (this.count && m.fieldUpgrades[m.fieldMode].name === "plasma torch") m.fieldUpgrades[m.fieldMode].set() - } - }, - { - name: "corona discharge", - description: "increase the range and frequency
of plasma ball's electric arc ", - isFieldTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && tech.isPlasmaBall - }, - requires: "plasma ball", - effect() { - tech.plasmaDischarge += 0.03 - }, - remove() { - tech.plasmaDischarge = 0.01 //default chance per cycle of a discharge - } - }, - { - name: "retrocausality", - description: "time dilation uses energy to rewind your
health, velocity, and position up to 10 seconds", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "time dilation" && !m.isShipMode && !tech.isRewindAvoidDeath && !tech.isTimeSkip - }, - requires: "time dilation, not CPT symmetry", - effect() { - tech.isRewindField = true; - m.fieldUpgrades[m.fieldMode].set() - m.wakeCheck(); - }, - remove() { - tech.isRewindField = false; - if (this.count) m.fieldUpgrades[m.fieldMode].set() - } - }, - { - name: "frame-dragging", //"non-inertial frame", - description: "when not moving time dilation stops time
+33% defense", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "time dilation" - }, - requires: "time dilation", - effect() { - tech.isTimeStop = true; - m.fieldHarmReduction = 0.66; //33% reduction - }, - remove() { - tech.isTimeStop = false; - if (m.fieldUpgrades[m.fieldMode].name === "time dilation") m.fieldHarmReduction = 1; - } - }, - { - name: "Lorentz transformation", - description: `use ${powerUps.orb.research(3)}
+50% movement, jumping, and fire rate`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "time dilation") && (build.isExperimentSelection || powerUps.research.count > 2) - }, - requires: "time dilation", - effect() { - tech.isFastTime = true - m.setMovement(); - b.setFireCD(); - for (let i = 0; i < 3; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - }, - remove() { - tech.isFastTime = false - m.setMovement(); - b.setFireCD(); - if (this.count > 0) powerUps.research.changeRerolls(3) - } - }, - { - name: "time crystals", - description: "+200% passive energy generation", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return !tech.isGroundState && (m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") - }, - requires: "time dilation or pilot wave, not ground state", - effect() { - tech.isTimeCrystals = true - m.setFieldRegen() - }, - remove() { - tech.isTimeCrystals = false - m.setFieldRegen() - } - }, - { - name: "no-cloning theorem", - // descriptionFunction() { return `+45% chance to duplicate spawned power ups
after a mob dies –2% duplication (${tech.duplicationChance()})` }, - description: `+45% chance to duplicate spawned power ups
after a mob dies –2% duplication`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking") && !tech.isQuantumEraser - }, - requires: "cloaking, time dilation, not quantum eraser", - effect() { - tech.cloakDuplication = 0.45 - powerUps.setDupChance(); //needed after adjusting duplication chance - if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.4); - }, - remove() { - tech.cloakDuplication = 0 - powerUps.setDupChance(); //needed after adjusting duplication chance - } - }, - { - name: "quantum eraser", - descriptionFunction() { - return `for each mob left alive after you exit a level
kill a mob as they spawn at +${100-1.6*simulation.difficultyMode**2}% duplication
` - }, - // description: `for each mob left alive after you exit a level
kill a mob as they spawn at 100% duplication
`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" || m.fieldUpgrades[m.fieldMode].name === "time dilation") && !tech.cloakDuplication - }, - requires: "cloaking or time dilation", - effect() { - tech.quantumEraserCount = 0 - tech.isQuantumEraserDuplication = 0 - tech.isQuantumEraser = true - }, - remove() { - tech.quantumEraserCount = 0 - tech.isQuantumEraserDuplication = 0 - tech.isQuantumEraser = false - } - }, - { - name: "symbiosis", - descriptionFunction() { - return `after a boss dies spawn ${powerUps.orb.research(3)}${powerUps.orb.heal(3)} and a tech
after a mob dies –0.5 maximum ${tech.isEnergyHealth ? "energy" : "health"}` - }, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" //|| m.fieldUpgrades[m.fieldMode].name === "time dilation" - }, - requires: "cloaking", - effect() { - tech.isAddRemoveMaxHealth = true - }, - remove() { - tech.isAddRemoveMaxHealth = false - } - }, - { - name: "boson composite", - link: `boson composite`, - description: "while cloaked you are intangible
to blocks and mobs, but mobs drain energy", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" - }, - requires: "metamaterial cloaking", - effect() { - tech.isIntangible = true; - }, - remove() { - if (tech.isIntangible) { - tech.isIntangible = false; - player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions - } - } - }, - { - name: "patch", - link: `patch`, - description: "after cloaking recover 75% of your
last health loss using that much energy", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" && !tech.lastHitDamage && !tech.isEnergyHealth - }, - requires: "metamaterial cloaking, not dynamic equilibrium, mass-energy", - effect() { - tech.isCloakHealLastHit = true; - }, - remove() { - tech.isCloakHealLastHit = false; - } - }, - { - name: "dazzler", - link: `dazzler`, - description: "after decloaking stun nearby mobs
and drain –15 energy", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" - }, - requires: "metamaterial cloaking", - effect() { - tech.isCloakStun = true; - }, - remove() { - tech.isCloakStun = false; - } - }, - // { - // name: "ambush", - // description: "metamaterial cloaking field damage effect
is increased from 333% to 555%", - // isFieldTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" - // }, - // requires: "metamaterial cloaking", - // effect() { - // tech.sneakAttackDmg = 6.55 //555% + 100% - // }, - // remove() { - // tech.sneakAttackDmg = 4.33 //333% + 100% - // } - // }, - { - name: "dynamical systems", - description: `use ${powerUps.orb.research(2)}
+35% damage`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && (build.isExperimentSelection || powerUps.research.count > 1) - }, - requires: "cloaking, pilot wave, or plasma torch", - damage: 1.35, - effect() { - tech.damage *= this.damage - tech.isCloakingDamage = true - for (let i = 0; i < 2; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - }, - remove() { - tech.isCloakingDamage = false - if (this.count > 0) { - tech.damage /= this.damage - powerUps.research.changeRerolls(2) - } - } - }, - { - name: "WIMPs", - description: `at the end of each level spawn ${powerUps.orb.research(4)}
and a dangerous particle that slowly chases you`, - isFieldTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "wormhole" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "time dilation" - }, - requires: "wormhole, pilot wave, time dilation", - effect() { - tech.wimpCount++ - spawn.WIMP() - for (let j = 0, len = 4; j < len; j++) powerUps.spawn(level.exit.x + 100 * (Math.random() - 0.5), level.exit.y - 100 + 100 * (Math.random() - 0.5), "research", false) - }, - remove() { - tech.wimpCount = 0 - } - }, - { - name: "vacuum fluctuation", - description: `use ${powerUps.orb.research(4)}to exploit your field for a
+11% chance to duplicate spawned power ups`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "negative mass" || m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "wormhole") && (build.isExperimentSelection || powerUps.research.count > 3) - }, - requires: "wormhole, time dilation, negative mass, pilot wave", - effect() { - tech.fieldDuplicate = 0.11 - powerUps.setDupChance(); //needed after adjusting duplication chance - if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.11); - for (let i = 0; i < 4; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - }, - remove() { - tech.fieldDuplicate = 0 - powerUps.setDupChance(); //needed after adjusting duplication chance - if (this.count > 0) powerUps.research.changeRerolls(4) - } - }, - // { - // name: "Penrose process", - // description: "after a block falls into a wormhole
+50 energy", - // isFieldTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return m.fieldUpgrades[m.fieldMode].name === "wormhole" - // }, - // requires: "wormhole", - // effect() { - // tech.isWormholeEnergy = true - // }, - // remove() { - // tech.isWormholeEnergy = false - // } - // }, - { - name: "transdimensional worms", - link: `transdimensional worms`, - description: "after a block falls into a wormhole
spawn a worm", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "wormhole" - }, - requires: "wormhole", - effect() { - tech.isWormholeWorms = true - }, - remove() { - tech.isWormholeWorms = false - } - }, - { - name: "geodesics", - description: `your bullets can traverse wormholes
spawn 2 guns and ${powerUps.orb.ammo(4)}`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "wormhole" - }, - requires: "wormhole", - effect() { - tech.isWormHoleBullets = true - for (let i = 0; i < 2; i++) powerUps.spawn(m.pos.x + 200 * (Math.random() - 0.5), m.pos.y + 200 * (Math.random() - 0.5), "gun"); - for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x + 200 * (Math.random() - 0.5), m.pos.y + 200 * (Math.random() - 0.5), "ammo"); - }, - remove() { - if (tech.isWormHoleBullets) { - for (let i = 0; i < 2; i++) { - if (b.inventory.length) b.removeGun(b.guns[b.inventory[b.inventory.length - 1]].name) //remove your last gun - } - tech.isWormHoleBullets = false; - } - } - }, - { - name: "cosmic string", - description: "after tunneling through mobs with a wormhole
stun them and do radioactive damage", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "wormhole" - }, - requires: "wormhole", - effect() { - tech.isWormholeDamage = true - }, - remove() { - tech.isWormholeDamage = false - } - }, - { - name: "invariant", - description: "while placing your wormhole
use energy to pause time", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "wormhole" && !tech.isNoDraftPause - }, - requires: "wormhole, not eternalism", - effect() { - tech.isWormHolePause = true - }, - remove() { - if (tech.isWormHolePause && m.isBodiesAsleep) m.wakeCheck(); - tech.isWormHolePause = false - } - }, - { - name: "charmed baryons", - description: `–33% movement and jumping
wormholes drain zero energy`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "wormhole" && !tech.isWormholeMapIgnore - }, - requires: "wormhole, not affine connection", - effect() { - tech.isFreeWormHole = true - tech.baseFx *= 0.66 - tech.baseJumpForce *= 0.66 - m.setMovement() - }, - //also removed in m.setHoldDefaults() if player switches into a bad field - remove() { - tech.isFreeWormHole = false - if (!tech.isNeutronium) { - tech.baseFx = 0.08 - tech.baseJumpForce = 10.5 - m.setMovement() - } - } - }, - { - name: "affine connection", - description: "wormholes can tunnel through anything
for +200% energy drain", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "wormhole" && !tech.isFreeWormHole - }, - requires: "wormhole, not charmed baryons", - effect() { - tech.isWormholeMapIgnore = true - }, - remove() { - tech.isWormholeMapIgnore = false - } - }, - //************************************************** - //************************************************** experimental - //************************************************** modes - //************************************************** - // { - // name: "-ship-", - // description: "experiment: fly around with no legs
aim with the keyboard", - // maxCount: 1, - // count: 0, - // frequency: 0, - // isNonRefundable: true, - // isBadRandomOption: true, - // isExperimentalMode: true, - // allowed() { - // return build.isExperimentSelection && !m.isShipMode && m.fieldUpgrades[m.fieldMode].name !== "negative mass" - // }, - // requires: "", - // effect() { - // m.shipMode() - // }, - // remove() {} - // }, - // { - // name: "-quantum leap-", - // description: "experiment: every 20 seconds
become an alternate version of yourself", - // maxCount: 1, - // count: 0, - // frequency: 0, - // isBadRandomOption: true, - // isExperimentalMode: true, - // allowed() { - // return build.isExperimentSelection - // }, - // requires: "", - // interval: undefined, - // effect() { - // this.interval = setInterval(() => { - // if (!build.isExperimentSelection) { - // m.switchWorlds() - // simulation.trails() - // } - // }, 20000); //every 20 seconds - - // }, - // remove() { - // if (this.count > 0) clearTimeout(this.interval); - // } - // }, - // { - // name: "-shields-", - // description: "experiment: every 5 seconds
all mobs gain a shield", - // maxCount: 1, - // count: 0, - // frequency: 0, - // isBadRandomOption: true, - // isExperimentalMode: true, - // allowed() { - // return build.isExperimentSelection - // }, - // requires: "", - // effect() { - // this.interval = setInterval(() => { - // if (!build.isExperimentSelection) { - // for (let i = 0; i < mob.length; i++) { - // if (!mob[i].isShielded && !mob[i].shield && mob[i].isDropPowerUp) spawn.shield(mob[i], mob[i].position.x, mob[i].position.y, 1, true); - // } - // } - // }, 5000); //every 5 seconds - // }, - // interval: undefined, - // remove() { - // if (this.count > 0) clearTimeout(this.interval); - // } - // }, - // { - // name: "-Fourier analysis-", - // description: "experiment: your aiming is random", - // maxCount: 1, - // count: 0, - // frequency: 0, - // isBadRandomOption: true, - // isExperimentalMode: true, - // allowed() { - // return build.isExperimentSelection && !m.isShipMode - // }, - // requires: "not ship", - // effect() { - // m.look = () => { - // m.angle = 2 * Math.sin(m.cycle * 0.0133) + Math.sin(m.cycle * 0.013) + 0.5 * Math.sin(m.cycle * 0.031) + 0.33 * Math.sin(m.cycle * 0.03) - // const scale = 0.8; - // m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; - // m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; - // m.transX += (m.transSmoothX - m.transX) * 0.07; - // m.transY += (m.transSmoothY - m.transY) * 0.07; - // } - // }, - // remove() { - // if (this.count > 0) m.look = m.lookDefault() - // } - // }, - // { - // name: "-panopticon-", - // description: "experiment: mobs can always see you", - // maxCount: 1, - // count: 0, - // frequency: 0, - // isBadRandomOption: true, - // isExperimentalMode: true, - // allowed() { - // return build.isExperimentSelection - // }, - // requires: "", - // effect() { - // this.interval = setInterval(() => { - // if (!build.isExperimentSelection) { - // for (let i = 0; i < mob.length; i++) { - // if (!mob[i].shield && mob[i].isDropPowerUp) { - // mob[i].locatePlayer() - // mob[i].seePlayer.yes = true; - // } - // } - // } - // }, 1000); //every 1 seconds - // }, - // interval: undefined, - // remove() { - // if (this.count > 0) clearTimeout(this.interval); - // } - // }, - // { - // name: "-decomposers-", - // description: "experiment: after they die
mobs leave behind spawns", - // maxCount: 1, - // count: 0, - // frequency: 0, - // isBadRandomOption: true, - // isExperimentalMode: true, - // allowed() { - // return build.isExperimentSelection - // }, - // requires: "", - // effect() { - // tech.deathSpawns = 0.2 - // }, - // remove() { - // tech.deathSpawns = 0 - // } - // }, - - - - //************************************************** - //************************************************** JUNK - //************************************************** tech - //************************************************** - // { - // name: "junk", - // description: "", - // maxCount: 9, - // count: 0, - // frequency: 0, - // isNonRefundable: true, - // isJunk: true, - // allowed() { - // return true - // }, - // requires: "", - // effect() { - - // }, - // remove() {} - // }, - { - name: "swap meet", - description: "normal tech become JUNK
and JUNK become normal tech", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - isNonRefundable: true, - allowed() { - return true - }, - requires: "", - effect() { - for (let i = 0, len = tech.tech.length; i < len; i++) { - tech.tech[i].isJunk = !tech.tech[i].isJunk - if (tech.tech[i].isJunk) {} else {} - - if (tech.tech[i].frequency > 0) { - tech.tech[i].frequency = 0 - } else { - tech.tech[i].frequency = 2 - } - } - }, - remove() {} - }, - // { - // name: "pocket dimension", - // description: "rotate tech descriptions into a higher spacial dimension", - // maxCount: 1, - // count: 0, - // frequency: 0, - // isJunk: true, - // isNonRefundable: true, - // allowed() { - // return true - // }, - // requires: "", - // effect() { - // document.getElementById("choose-grid").classList.add("flipX"); - // }, - // remove() {} - // }, - { - name: "random", - link: `random`, - delay: 333, - descriptionFunction() { - const delay = 333 - const loop = () => { - if ((simulation.isChoosing) && m.alive && !build.isExperimentSelection) { - const dmg = Math.floor(27 * Math.random()) * 0.01 - this.text = `+${(dmg*100).toFixed(0).padStart(2, '0')}% damage` - this.damage = 1 + dmg - if (document.getElementById(`damage-JUNK-id${this.id}`)) document.getElementById(`damage-JUNK-id${this.id}`).innerHTML = this.text - setTimeout(() => { - loop() - }, delay); - } - } - setTimeout(() => { - loop() - }, delay); - this.id++ - return `${this.text}` - }, - maxCount: 3, - count: 0, - frequency: 1, - isJunk: true, - allowed() { - return !build.isExperimentSelection - }, - requires: "NOT EXPERIMENT MODE", - damage: 0, - effect() { - tech.damage *= this.damage - }, - remove() { - if (this.count > 0) tech.damage /= this.damage - } - }, - { - name: "boost", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - isNonRefundable: true, - allowed() { - return !build.isExperimentSelection - }, - requires: "NOT EXPERIMENT MODE", - effect() { - powerUps.spawnDelay("boost", this.spawnCount) - }, - remove() {}, - id: 0, - text: "", - delay: 100, - spawnCount: 0, - descriptionFunction() { - let count = 9999 * Math.random() - const loop = () => { - if ((simulation.isChoosing) && m.alive && !build.isExperimentSelection) { //&& (!simulation.isChoosing || this.count === 0) //simulation.paused || - count += 4.5 - const waves = 2 * Math.sin(count * 0.0133) + Math.sin(count * 0.013) + 0.5 * Math.sin(count * 0.031) + 0.33 * Math.sin(count * 0.03) - this.spawnCount = Math.floor(100 * Math.abs(waves)) - this.text = `spawn ${this.spawnCount.toLocaleString(undefined, {minimumIntegerDigits:3})} ${powerUps.orb.boost(1)}
that give +${(powerUps.boost.damage*100).toFixed(0)}% damage for ${(powerUps.boost.duration/60).toFixed(0)} seconds` - if (document.getElementById(`boost-JUNK-id${this.id}`)) document.getElementById(`boost-JUNK-id${this.id}`).innerHTML = this.text - setTimeout(() => { - loop() - }, this.delay); - } - } - setTimeout(() => { - loop() - }, this.delay); - this.id++ - return `${this.text}` - }, - }, - { - name: "placebo", - description: "+777% damage
+777% defense", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - allowed: () => true, - requires: "", - effect() { - if (Math.random() < 0.1) tech.damage *= 8.77 - }, - remove() {} - }, - { - name: "universal healthcare", - description: "make your damage negative", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - allowed: () => true, - requires: "", - effect() { - tech.damage *= -1 - }, - remove() {} - }, - // { - // name: "synchrotron", - // descriptionFunction() { - // return `power ups change into a different flavor after a boss dies` - // }, - // maxCount: 3, - // count: 0, - // frequency: 1, - // frequencyDefault: 1, - // allowed: () => true, - // requires: "", - // effect() { - // }, - // remove() { - // } - // }, - { - name: "return", - description: "return to the introduction level
reduce combat difficulty by 2 levels", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - isNonRefundable: true, - allowed: () => true, - requires: "", - effect() { - level.difficultyDecrease(simulation.difficultyMode * 2) - level.onLevel = 0 - simulation.clearNow = true //end current level - }, - remove() {} - }, - { - name: "panpsychism", - description: "awaken all blocks
blocks have a chance to spawn power ups", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - isNonRefundable: true, - allowed: () => true, - requires: "", - effect() { - setInterval(() => { - for (let i = body.length - 1; i > -1; i--) { - if (!body[i].isNotHoldable) { - Matter.Composite.remove(engine.world, body[i]); - spawn.blockMob(body[i].position.x, body[i].position.y, body[i], 0); - if (!body[i].isAboutToBeRemoved) mob[mob.length - 1].isDropPowerUp = true - body.splice(i, 1); - } - } - }, 6000); - }, - remove() {} - }, - { - name: "meteor shower", - description: "take a shower, but meteors instead of water", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - isNonRefundable: true, - allowed: () => true, - requires: "", - effect() { - setInterval(() => { - - fireBlock = function(xPos, yPos) { - const index = body.length - spawn.bodyRect(xPos, yPos, 20 + 50 * Math.random(), 20 + 50 * Math.random()); - const bodyBullet = body[index] - Matter.Body.setVelocity(bodyBullet, { - x: 5 * (Math.random() - 0.5), - y: 10 * (Math.random() - 0.5) - }); - bodyBullet.isAboutToBeRemoved = true - bodyBullet.collisionFilter.category = cat.body; - bodyBullet.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet - bodyBullet.classType = "body"; - Composite.add(engine.world, bodyBullet); //add to world - setTimeout(() => { //remove block - for (let i = 0; i < body.length; i++) { - if (body[i] === bodyBullet) { - Matter.Composite.remove(engine.world, body[i]); - body.splice(i, 1); - } - } - }, 4000 + Math.floor(9000 * Math.random())); - } - fireBlock(player.position.x + 600 * (Math.random() - 0.5), player.position.y - 500 - 500 * Math.random()); - // for (let i = 0, len = Math.random(); i < len; i++) { - // } - - }, 1000); - }, - remove() {} - }, - { - name: "startle response", - description: `if a threat is nearby, activate a ${powerUps.orb.boost(1)}
and lock your mouse until you press escape`, - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - isNonRefundable: true, - allowed: () => true, - requires: "", - effect() { - // tech.damage *= 1.33 - setInterval(() => { - if (powerUps.boost.endCycle < m.cycle && !simulation.paused && m.alive) { - for (let i = 0; i < mob.length; i++) { - if (mob[i].distanceToPlayer2() < 400000) { //650 - canvas.requestPointerLock(); - powerUps.boost.effect(); - break - } - } - } - }, 2000); - }, - remove() {} - }, - { - name: "closed timelike curve", - description: "spawn 5 field power ups, but every 12 seconds
teleport a second into your future or past", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - isNonRefundable: true, - allowed: () => true, - requires: "", - effect() { - for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "field"); - - function loop() { - if (!simulation.paused && m.alive) { - if (!(simulation.cycle % 720)) { - requestAnimationFrame(() => { - if ((simulation.cycle % 1440) > 720) { //kinda alternate between each option - m.rewind(60) - m.energy += 0.4 //to make up for lost energy - } else { - simulation.timePlayerSkip(60) - } - }); //wrapping in animation frame prevents errors, probably - } - } - requestAnimationFrame(loop); } requestAnimationFrame(loop); - }, - remove() {} + } + requestAnimationFrame(loop); }, - // { - // name: "translate", - // description: "translate n-gon into a random language", - // maxCount: 1, - // count: 0, - // frequency: 0, - // isJunk: true, - // isNonRefundable: true, - // allowed() { - // return true - // }, - // requires: "", - // effect() { - // // generate a container - // const gtElem = document.createElement('div') - // gtElem.id = "gtElem" - // gtElem.style.visibility = 'hidden' // make it invisible - // document.body.append(gtElem) + remove() { } + }, + // { + // name: "translate", + // description: "translate n-gon into a random language", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isJunk: true, + // isNonRefundable: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() { + // // generate a container + // const gtElem = document.createElement('div') + // gtElem.id = "gtElem" + // gtElem.style.visibility = 'hidden' // make it invisible + // document.body.append(gtElem) - // // generate a script to run after creation - // function initGT() { - // // create a new translate element - // new google.translate.TranslateElement({ pageLanguage: 'en', layout: google.translate.TranslateElement.InlineLayout.HORIZONTAL }, 'gtElem') - // // ok now since it's loaded perform a funny hack to make it work - // const langSelect = document.getElementsByClassName("goog-te-combo")[0] - // // select a random language. It takes a second for all langauges to load, so wait a second. - // setTimeout(() => { - // langSelect.selectedIndex = Math.round(langSelect.options.length * Math.random()) - // // simulate a click - // langSelect.dispatchEvent(new Event('change')) - // // now make it go away - // const bar = document.getElementById(':1.container') - // bar.style.display = 'none' - // bar.style.visibility = 'hidden' - // }, 1000) + // // generate a script to run after creation + // function initGT() { + // // create a new translate element + // new google.translate.TranslateElement({ pageLanguage: 'en', layout: google.translate.TranslateElement.InlineLayout.HORIZONTAL }, 'gtElem') + // // ok now since it's loaded perform a funny hack to make it work + // const langSelect = document.getElementsByClassName("goog-te-combo")[0] + // // select a random language. It takes a second for all langauges to load, so wait a second. + // setTimeout(() => { + // langSelect.selectedIndex = Math.round(langSelect.options.length * Math.random()) + // // simulate a click + // langSelect.dispatchEvent(new Event('change')) + // // now make it go away + // const bar = document.getElementById(':1.container') + // bar.style.display = 'none' + // bar.style.visibility = 'hidden' + // }, 1000) - // } + // } - // // add the google translate script - // const translateScript = document.createElement('script') - // translateScript.src = '//translate.google.com/translate_a/element.js?cb=initGT' - // document.body.append(translateScript) - // }, - // remove() {} - // }, - { - name: "discount", - description: "get 3 random JUNKtech for the price of 1!", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - isNonRefundable: true, - allowed: () => true, - requires: "", - effect() { - tech.giveRandomJUNK() - tech.giveRandomJUNK() - tech.giveRandomJUNK() - }, - remove() {} + // // add the google translate script + // const translateScript = document.createElement('script') + // translateScript.src = '//translate.google.com/translate_a/element.js?cb=initGT' + // document.body.append(translateScript) + // }, + // remove() {} + // }, + { + name: "discount", + description: "get 3 random JUNKtech for the price of 1!", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed: () => true, + requires: "", + effect() { + tech.giveRandomJUNK() + tech.giveRandomJUNK() + tech.giveRandomJUNK() }, - // { - // name: "hi", - // description: `spawn to seed 616 `, - // maxCount: 1, - // count: 0, - // frequency: 0, - // isNonRefundable: true, - // isJunk: true, - // allowed() { - // return true - // }, - // requires: "", - // effect() { - // document.getElementById("seed").placeholder = Math.initialSeed = String(616) - // Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed in case the player changed it - // }, - // remove() {} - // }, - { - name: "Higgs phase transition", - description: "instantly spawn 5 tech, but add a chance to
remove everything with a 5 minute half-life", - maxCount: 1, - count: 0, - frequency: 0, - frequencyDefault: 0, - isJunk: true, - isNonRefundable: true, - allowed: () => true, - requires: "", - effect() { - powerUps.spawn(m.pos.x, m.pos.y, "tech"); - powerUps.spawn(m.pos.x + 30, m.pos.y, "tech"); - powerUps.spawn(m.pos.x + 60, m.pos.y, "tech"); - powerUps.spawn(m.pos.x, m.pos.y - 30, "tech"); - powerUps.spawn(m.pos.x + 30, m.pos.y - 60, "tech"); + remove() { } + }, + // { + // name: "hi", + // description: `spawn to seed 616 `, + // maxCount: 1, + // count: 0, + // frequency: 0, + // isNonRefundable: true, + // isJunk: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() { + // document.getElementById("seed").placeholder = Math.initialSeed = String(616) + // Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed in case the player changed it + // }, + // remove() {} + // }, + { + name: "Higgs phase transition", + description: "instantly spawn 5 tech, but add a chance to
remove everything with a 5 minute half-life", + maxCount: 1, + count: 0, + frequency: 0, + frequencyDefault: 0, + isJunk: true, + isNonRefundable: true, + allowed: () => true, + requires: "", + effect() { + powerUps.spawn(m.pos.x, m.pos.y, "tech"); + powerUps.spawn(m.pos.x + 30, m.pos.y, "tech"); + powerUps.spawn(m.pos.x + 60, m.pos.y, "tech"); + powerUps.spawn(m.pos.x, m.pos.y - 30, "tech"); + powerUps.spawn(m.pos.x + 30, m.pos.y - 60, "tech"); - function loop() { - // (1-X)^cycles = chance to be removed //Math.random() < 0.000019 10 min - if (!simulation.paused && m.alive) { - if (Math.random() < 0.000038) { - // m.death(); - simulation.clearMap(); - simulation.draw.setPaths(); - return - } + function loop() { + // (1-X)^cycles = chance to be removed //Math.random() < 0.000019 10 min + if (!simulation.paused && m.alive) { + if (Math.random() < 0.000038) { + // m.death(); + simulation.clearMap(); + simulation.draw.setPaths(); + return } - requestAnimationFrame(loop); } requestAnimationFrame(loop); - }, - remove() {} + } + requestAnimationFrame(loop); }, - { - name: "harvest", - description: "convert all the mobs on this level into ammo", - maxCount: 1, - count: 0, - frequency: 0, - frequencyDefault: 0, - isJunk: true, - isNonRefundable: true, - allowed: () => true, - requires: "", - effect() { - for (let i = 0, len = mob.length; i < len; i++) { - if (mob[i].isDropPowerUp) { - powerUps.directSpawn(mob[i].position.x, mob[i].position.y, "ammo"); - mob[i].death(); - } + remove() { } + }, + { + name: "harvest", + description: "convert all the mobs on this level into ammo", + maxCount: 1, + count: 0, + frequency: 0, + frequencyDefault: 0, + isJunk: true, + isNonRefundable: true, + allowed: () => true, + requires: "", + effect() { + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].isDropPowerUp) { + powerUps.directSpawn(mob[i].position.x, mob[i].position.y, "ammo"); + mob[i].death(); } - // for (let i = powerUp.length - 1; i > -1; i--) { - // if (powerUp[i].name !== "ammo") { - // Matter.Composite.remove(engine.world, powerUp[i]); - // powerUp.splice(i, 1); - // } - // } - }, - remove() {} + } + // for (let i = powerUp.length - 1; i > -1; i--) { + // if (powerUp[i].name !== "ammo") { + // Matter.Composite.remove(engine.world, powerUp[i]); + // powerUp.splice(i, 1); + // } + // } }, - { - name: "brainstorm", - description: "the tech choice menu randomizes
every 0.5 seconds for 10 seconds", - maxCount: 1, - count: 0, - frequency: 0, - frequencyDefault: 0, - isJunk: true, - allowed: () => true, - requires: "", - effect() { - tech.isBrainstorm = true - tech.isBrainstormActive = false - tech.brainStormDelay = 500 - }, - remove() { - tech.isBrainstorm = false - tech.isBrainstormActive = false + remove() { } + }, + { + name: "brainstorm", + description: "the tech choice menu randomizes
every 0.5 seconds for 10 seconds", + maxCount: 1, + count: 0, + frequency: 0, + frequencyDefault: 0, + isJunk: true, + allowed: () => true, + requires: "", + effect() { + tech.isBrainstorm = true + tech.isBrainstormActive = false + tech.brainStormDelay = 500 + }, + remove() { + tech.isBrainstorm = false + tech.isBrainstormActive = false + } + }, + { + name: "catabolysis", + description: `set your maximum health to 1
double your current ammo 10 times`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return !tech.isFallingDamage && !tech.isOverHeal && !tech.isEnergyHealth + }, + requires: "not quenching, tungsten carbide, mass-energy", + effect() { + m.baseHealth = 0.01 + m.setMaxHealth(); + for (let i = 0; i < b.guns.length; i++) b.guns[i].ammo = b.guns[i].ammo * Math.pow(2, 10) + simulation.updateGunHUD(); + }, + remove() { } + }, + { + name: "palantír", + description: `see far away lands`, + maxCount: 1, + count: 0, + frequency: 0, + // isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + m.look = () => { + //always on mouse look + m.angle = Math.atan2( + simulation.mouseInGame.y - m.pos.y, + simulation.mouseInGame.x - m.pos.x + ); + //smoothed mouse look translations + const scale = 2; + m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + m.transX += (m.transSmoothX - m.transX) * m.lookSmoothing; + m.transY += (m.transSmoothY - m.transY) * m.lookSmoothing; } }, - { - name: "catabolysis", - description: `set your maximum health to 1
double your current ammo 10 times`, - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return !tech.isFallingDamage && !tech.isOverHeal && !tech.isEnergyHealth - }, - requires: "not quenching, tungsten carbide, mass-energy", - effect() { - m.baseHealth = 0.01 - m.setMaxHealth(); - for (let i = 0; i < b.guns.length; i++) b.guns[i].ammo = b.guns[i].ammo * Math.pow(2, 10) - simulation.updateGunHUD(); - }, - remove() {} + remove() { + if (this.count) m.look = m.lookDefault + } + }, + { + name: "motion sickness", + description: `disable camera smoothing`, + maxCount: 1, + count: 0, + frequency: 0, + // isNonRefundable: true, + isJunk: true, + allowed() { + return true }, - { - name: "palantír", - description: `see far away lands`, - maxCount: 1, - count: 0, - frequency: 0, - // isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - m.look = () => { - //always on mouse look - m.angle = Math.atan2( - simulation.mouseInGame.y - m.pos.y, - simulation.mouseInGame.x - m.pos.x - ); - //smoothed mouse look translations - const scale = 2; - m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; - m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; - m.transX += (m.transSmoothX - m.transX) * m.lookSmoothing; - m.transY += (m.transSmoothY - m.transY) * m.lookSmoothing; - } - }, - remove() { - if (this.count) m.look = m.lookDefault + requires: "", + effect() { + m.look = () => { + //always on mouse look + m.angle = Math.atan2( + simulation.mouseInGame.y - m.pos.y, + simulation.mouseInGame.x - m.pos.x + ); + //smoothed mouse look translations + const scale = 1.2; + m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + m.transX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + // m.transX += (m.transSmoothX - m.transX) * m.lookSmoothing; + // m.transY += (m.transSmoothY - m.transY) * m.lookSmoothing; } }, - { - name: "motion sickness", - description: `disable camera smoothing`, - maxCount: 1, - count: 0, - frequency: 0, - // isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - m.look = () => { - //always on mouse look - m.angle = Math.atan2( - simulation.mouseInGame.y - m.pos.y, - simulation.mouseInGame.x - m.pos.x - ); - //smoothed mouse look translations - const scale = 1.2; - m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; - m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; - m.transX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; - m.transY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; - // m.transX += (m.transSmoothX - m.transX) * m.lookSmoothing; - // m.transY += (m.transSmoothY - m.transY) * m.lookSmoothing; - } - }, - remove() { - if (this.count) m.look = m.lookDefault - } + remove() { + if (this.count) m.look = m.lookDefault + } + }, + { + name: "facsimile", + description: `inserts a copy of your current level into the level list`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true }, - { - name: "facsimile", - description: `inserts a copy of your current level into the level list`, - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - const index = Math.min(level.levels.length - 1, level.onLevel) - level.levels.splice(index, 0, level.levels[index]); - }, - remove() {} + requires: "", + effect() { + const index = Math.min(level.levels.length - 1, level.onLevel) + level.levels.splice(index, 0, level.levels[index]); }, - { - name: "negative friction", - description: "when you touch walls you speed up instead of slowing down. It's kinda fun.", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - player.friction = -0.4 - }, - remove() { - if (this.count) player.friction = 0.002 - } + remove() { } + }, + { + name: "negative friction", + description: "when you touch walls you speed up instead of slowing down. It's kinda fun.", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return true }, - { - name: "bounce", - description: "you bounce off things. It's annoying, but not that bad.", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - player.restitution = 0.9 - }, - remove() { - if (this.count) player.restitution = 0 - } + requires: "", + effect() { + player.friction = -0.4 }, - { - name: "mouth", - description: "mobs have a non functional mouth", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - mobs.draw = () => { - ctx.lineWidth = 2; - let i = mob.length; - while (i--) { - ctx.beginPath(); - const vertices = mob[i].vertices; - ctx.moveTo(vertices[0].x, vertices[0].y); - for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); - ctx.quadraticCurveTo(mob[i].position.x, mob[i].position.y, vertices[0].x, vertices[0].y); - ctx.fillStyle = mob[i].fill; - ctx.strokeStyle = mob[i].stroke; - ctx.fill(); - ctx.stroke(); - } - } - }, - remove() { - mobs.draw = () => { - ctx.lineWidth = 2; - let i = mob.length; - while (i--) { - ctx.beginPath(); - const vertices = mob[i].vertices; - ctx.moveTo(vertices[0].x, vertices[0].y); - for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); - ctx.lineTo(vertices[0].x, vertices[0].y); - ctx.fillStyle = mob[i].fill; - ctx.strokeStyle = mob[i].stroke; - ctx.fill(); - ctx.stroke(); - } + remove() { + if (this.count) player.friction = 0.002 + } + }, + { + name: "bounce", + description: "you bounce off things. It's annoying, but not that bad.", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + player.restitution = 0.9 + }, + remove() { + if (this.count) player.restitution = 0 + } + }, + { + name: "mouth", + description: "mobs have a non functional mouth", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + mobs.draw = () => { + ctx.lineWidth = 2; + let i = mob.length; + while (i--) { + ctx.beginPath(); + const vertices = mob[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.quadraticCurveTo(mob[i].position.x, mob[i].position.y, vertices[0].x, vertices[0].y); + ctx.fillStyle = mob[i].fill; + ctx.strokeStyle = mob[i].stroke; + ctx.fill(); + ctx.stroke(); } } }, - { - name: "all-stars", - description: "make all mobs look like stars", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - mobs.draw = () => { - ctx.lineWidth = 2; - let i = mob.length; - while (i--) { - ctx.beginPath(); - const vertices = mob[i].vertices; - ctx.moveTo(vertices[0].x, vertices[0].y); - for (let j = 1, len = vertices.length; j < len; ++j) ctx.quadraticCurveTo(mob[i].position.x, mob[i].position.y, vertices[j].x, vertices[j].y); - ctx.quadraticCurveTo(mob[i].position.x, mob[i].position.y, vertices[0].x, vertices[0].y); - ctx.fillStyle = mob[i].fill; - ctx.strokeStyle = mob[i].stroke; - ctx.fill(); - ctx.stroke(); - } + remove() { + mobs.draw = () => { + ctx.lineWidth = 2; + let i = mob.length; + while (i--) { + ctx.beginPath(); + const vertices = mob[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.fillStyle = mob[i].fill; + ctx.strokeStyle = mob[i].stroke; + ctx.fill(); + ctx.stroke(); } - }, - remove() { - mobs.draw = () => { - ctx.lineWidth = 2; - let i = mob.length; - while (i--) { - ctx.beginPath(); - const vertices = mob[i].vertices; - ctx.moveTo(vertices[0].x, vertices[0].y); - for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); - ctx.lineTo(vertices[0].x, vertices[0].y); - ctx.fillStyle = mob[i].fill; - ctx.strokeStyle = mob[i].stroke; - ctx.fill(); - ctx.stroke(); - } + } + } + }, + { + name: "all-stars", + description: "make all mobs look like stars", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + mobs.draw = () => { + ctx.lineWidth = 2; + let i = mob.length; + while (i--) { + ctx.beginPath(); + const vertices = mob[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) ctx.quadraticCurveTo(mob[i].position.x, mob[i].position.y, vertices[j].x, vertices[j].y); + ctx.quadraticCurveTo(mob[i].position.x, mob[i].position.y, vertices[0].x, vertices[0].y); + ctx.fillStyle = mob[i].fill; + ctx.strokeStyle = mob[i].stroke; + ctx.fill(); + ctx.stroke(); } } }, - // draw() { - // ctx.lineWidth = 2; - // let i = mob.length; - // while (i--) { - // ctx.beginPath(); - // const vertices = mob[i].vertices; - // ctx.moveTo(vertices[0].x, vertices[0].y); - // for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); - // ctx.lineTo(vertices[0].x, vertices[0].y); - // ctx.fillStyle = mob[i].fill; - // ctx.strokeStyle = mob[i].stroke; - // ctx.fill(); - // ctx.stroke(); - // } - // }, - { - name: "true colors", - description: `set all power ups to their real world colors`, - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - isNonRefundable: true, - allowed() { - return true - }, - requires: "", - effect() { - // const colors = shuffle(["#f7b", "#0eb", "#467", "#0cf", "hsl(246,100%,77%)", "#26a"]) - const colors = shuffle([powerUps.research.color, powerUps.heal.color, powerUps.ammo.color, powerUps.ammo.color, powerUps.field.color, powerUps.gun.color]) - powerUps.research.color = colors[0] - powerUps.heal.color = colors[1] - powerUps.ammo.color = colors[2] - powerUps.field.color = colors[3] - powerUps.tech.color = colors[4] - powerUps.gun.color = colors[5] - for (let i = 0; i < powerUp.length; i++) { - switch (powerUp[i].name) { - case "research": - powerUp[i].color = colors[0] - break; - case "heal": - powerUp[i].color = colors[1] - break; - case "ammo": - powerUp[i].color = colors[2] - break; - case "field": - powerUp[i].color = colors[3] - break; - case "tech": - powerUp[i].color = colors[4] - break; - case "gun": - powerUp[i].color = colors[5] - break; - } + remove() { + mobs.draw = () => { + ctx.lineWidth = 2; + let i = mob.length; + while (i--) { + ctx.beginPath(); + const vertices = mob[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.fillStyle = mob[i].fill; + ctx.strokeStyle = mob[i].stroke; + ctx.fill(); + ctx.stroke(); + } + } + } + }, + // draw() { + // ctx.lineWidth = 2; + // let i = mob.length; + // while (i--) { + // ctx.beginPath(); + // const vertices = mob[i].vertices; + // ctx.moveTo(vertices[0].x, vertices[0].y); + // for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); + // ctx.lineTo(vertices[0].x, vertices[0].y); + // ctx.fillStyle = mob[i].fill; + // ctx.strokeStyle = mob[i].stroke; + // ctx.fill(); + // ctx.stroke(); + // } + // }, + { + name: "true colors", + description: `set all power ups to their real world colors`, + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed() { + return true + }, + requires: "", + effect() { + // const colors = shuffle(["#f7b", "#0eb", "#467", "#0cf", "hsl(246,100%,77%)", "#26a"]) + const colors = shuffle([powerUps.research.color, powerUps.heal.color, powerUps.ammo.color, powerUps.ammo.color, powerUps.field.color, powerUps.gun.color]) + powerUps.research.color = colors[0] + powerUps.heal.color = colors[1] + powerUps.ammo.color = colors[2] + powerUps.field.color = colors[3] + powerUps.tech.color = colors[4] + powerUps.gun.color = colors[5] + for (let i = 0; i < powerUp.length; i++) { + switch (powerUp[i].name) { + case "research": + powerUp[i].color = colors[0] + break; + case "heal": + powerUp[i].color = colors[1] + break; + case "ammo": + powerUp[i].color = colors[2] + break; + case "field": + powerUp[i].color = colors[3] + break; + case "tech": + powerUp[i].color = colors[4] + break; + case "gun": + powerUp[i].color = colors[5] + break; } - }, - remove() { - // const colors = ["#f7b", "#0eb", "#467", "#0cf", "hsl(246,100%,77%)", "#26a"] //no shuffle - // powerUps.research.color = colors[0] - // powerUps.heal.color = colors[1] - // powerUps.ammo.color = colors[2] - // powerUps.field.color = colors[3] - // powerUps.tech.color = colors[4] - // powerUps.gun.color = colors[5] - // for (let i = 0; i < powerUp.length; i++) { - // switch (powerUp[i].name) { - // case "research": - // powerUp[i].color = colors[0] - // break; - // case "heal": - // powerUp[i].color = colors[1] - // break; - // case "ammo": - // powerUp[i].color = colors[2] - // break; - // case "field": - // powerUp[i].color = colors[3] - // break; - // case "tech": - // powerUp[i].color = colors[4] - // break; - // case "gun": - // powerUp[i].color = colors[5] - // break; - // } - // } } }, - { - name: "emergency broadcasting", - description: "emit 2 sine waveforms at 853 Hz and 960 Hz
lower your volume", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - isNonRefundable: true, - allowed() { - return true - }, - requires: "", - effect: () => { - //setup audio context - function tone(frequency) { - const audioCtx = new(window.AudioContext || window.webkitAudioContext)(); - const oscillator1 = audioCtx.createOscillator(); - const gainNode1 = audioCtx.createGain(); - gainNode1.gain.value = 0.5; //controls volume - oscillator1.connect(gainNode1); - gainNode1.connect(audioCtx.destination); - oscillator1.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' - oscillator1.frequency.value = frequency; // value in hertz - oscillator1.start(); - return audioCtx - } - // let sound = tone(1050) + remove() { + // const colors = ["#f7b", "#0eb", "#467", "#0cf", "hsl(246,100%,77%)", "#26a"] //no shuffle + // powerUps.research.color = colors[0] + // powerUps.heal.color = colors[1] + // powerUps.ammo.color = colors[2] + // powerUps.field.color = colors[3] + // powerUps.tech.color = colors[4] + // powerUps.gun.color = colors[5] + // for (let i = 0; i < powerUp.length; i++) { + // switch (powerUp[i].name) { + // case "research": + // powerUp[i].color = colors[0] + // break; + // case "heal": + // powerUp[i].color = colors[1] + // break; + // case "ammo": + // powerUp[i].color = colors[2] + // break; + // case "field": + // powerUp[i].color = colors[3] + // break; + // case "tech": + // powerUp[i].color = colors[4] + // break; + // case "gun": + // powerUp[i].color = colors[5] + // break; + // } + // } + } + }, + { + name: "emergency broadcasting", + description: "emit 2 sine waveforms at 853 Hz and 960 Hz
lower your volume", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed() { + return true + }, + requires: "", + effect: () => { + //setup audio context + function tone(frequency) { + const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); + const oscillator1 = audioCtx.createOscillator(); + const gainNode1 = audioCtx.createGain(); + gainNode1.gain.value = 0.5; //controls volume + oscillator1.connect(gainNode1); + gainNode1.connect(audioCtx.destination); + oscillator1.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' + oscillator1.frequency.value = frequency; // value in hertz + oscillator1.start(); + return audioCtx + } + // let sound = tone(1050) - function EBS() { - const audioCtx = new(window.AudioContext || window.webkitAudioContext)(); + function EBS() { + const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); - const oscillator1 = audioCtx.createOscillator(); - const gainNode1 = audioCtx.createGain(); - gainNode1.gain.value = 0.3; //controls volume - oscillator1.connect(gainNode1); - gainNode1.connect(audioCtx.destination); - oscillator1.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' - oscillator1.frequency.value = 850; // value in hertz - oscillator1.start(); + const oscillator1 = audioCtx.createOscillator(); + const gainNode1 = audioCtx.createGain(); + gainNode1.gain.value = 0.3; //controls volume + oscillator1.connect(gainNode1); + gainNode1.connect(audioCtx.destination); + oscillator1.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' + oscillator1.frequency.value = 850; // value in hertz + oscillator1.start(); - const oscillator2 = audioCtx.createOscillator(); - const gainNode2 = audioCtx.createGain(); - gainNode2.gain.value = 0.3; //controls volume - oscillator2.connect(gainNode2); - gainNode2.connect(audioCtx.destination); - oscillator2.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' - oscillator2.frequency.value = 957; // value in hertz - oscillator2.start(); - return audioCtx - } - let sound = EBS() + const oscillator2 = audioCtx.createOscillator(); + const gainNode2 = audioCtx.createGain(); + gainNode2.gain.value = 0.3; //controls volume + oscillator2.connect(gainNode2); + gainNode2.connect(audioCtx.destination); + oscillator2.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' + oscillator2.frequency.value = 957; // value in hertz + oscillator2.start(); + return audioCtx + } + let sound = EBS() - delay = 1000 + delay = 1000 + setTimeout(() => { + sound.suspend() + powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); setTimeout(() => { - sound.suspend() - powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); + sound.resume() setTimeout(() => { - sound.resume() + sound.suspend() + powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); setTimeout(() => { - sound.suspend() - powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); + sound.resume() setTimeout(() => { - sound.resume() + sound.suspend() + powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); setTimeout(() => { - sound.suspend() - powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); + sound.resume() setTimeout(() => { - sound.resume() + sound.suspend() + powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); setTimeout(() => { - sound.suspend() - powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); + sound.resume() setTimeout(() => { - sound.resume() + sound.suspend() + powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); setTimeout(() => { - sound.suspend() - powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); + sound.resume() setTimeout(() => { - sound.resume() - setTimeout(() => { - sound.suspend() - sound.close() - powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); - }, delay); + sound.suspend() + sound.close() + powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); }, delay); }, delay); }, delay); @@ -9473,1567 +9472,1568 @@ const tech = { }, delay); }, delay); }, delay); - }, - remove() {} + }, delay); }, - { - name: "automatic", - description: "you can't fire when moving
always fire when at rest", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - allowed() { - return !tech.isFireMoveLock - }, - requires: "not Higgs mechanism", - effect() { - tech.isAlwaysFire = true; + remove() { } + }, + { + name: "automatic", + description: "you can't fire when moving
always fire when at rest", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return !tech.isFireMoveLock + }, + requires: "not Higgs mechanism", + effect() { + tech.isAlwaysFire = true; + b.setFireMethod(); + }, + remove() { + if (tech.isAlwaysFire) { + tech.isAlwaysFire = false b.setFireMethod(); - }, - remove() { - if (tech.isAlwaysFire) { - tech.isAlwaysFire = false - b.setFireMethod(); - } - } - }, - { - name: "hidden variable", - descriptionFunction() { - return `spawn ${powerUps.orb.heal(20)}
but hide your health bar` - }, - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - document.getElementById("health").style.display = "none" - document.getElementById("health-bg").style.display = "none" - document.getElementById("defense-bar").style.display = "none" - for (let i = 0; i < 20; i++) powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); - }, - remove() {} - }, - { - name: "not a bug", - description: "initiate a totally safe game crash for 10 seconds", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - const savedfunction = simulation.drawCircle - simulation.drawCircle = () => { - const a = mob[Infinity].position //crashed the game in a visually interesting way, because of the ctx.translate command is never reverted in the main game loop - } - setTimeout(() => { - simulation.drawCircle = savedfunction - canvas.width = canvas.width //clears the canvas // works on chrome at least - powerUps.spawn(m.pos.x, m.pos.y, "tech"); - }, 10000); - - // for (;;) {} //freezes the tab - }, - remove() {} - }, - { - name: "spinor", - description: "the direction you aim is determined by your position", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return !m.isShipMode - }, - requires: "", - effect() { - m.look = function() { - //always on mouse look - m.angle = (((m.pos.x + m.pos.y) / 100 + Math.PI) % Math.PI * 2) - Math.PI - //smoothed mouse look translations - const scale = 0.8; - m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; - m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; - - m.transX += (m.transSmoothX - m.transX) * 0.07; - m.transY += (m.transSmoothY - m.transY) * 0.07; - } - }, - remove() { - if (this.count) m.look = m.lookDefault - } - }, - { - name: "p-zombie", - description: "set your health to 1
all mobs, not bosses, die and resurrect as zombies", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - m.health = 0.01 //set health to 1 - m.displayHealth(); - for (let i = mob.length - 1; i > -1; i--) { //replace mobs with zombies - if (mob[i].isDropPowerUp && !mob[i].isBoss && mob[i].alive) { - mob[i].isSoonZombie = true - mob[i].death() - } - } - }, - remove() {} - }, - { - name: "decomposers", - description: "after they die mobs leave behind spawns", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return tech.deathSpawns === 0 - }, - requires: "", - effect() { - tech.deathSpawns = 0.2 - }, - remove() { - tech.deathSpawns = 0 - } - }, - { - name: "panopticon", - description: "mobs can always see you", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - setInterval(() => { - for (let i = 0; i < mob.length; i++) { - if (!mob[i].shield && mob[i].isDropPowerUp) { - mob[i].locatePlayer() - mob[i].seePlayer.yes = true; - } - } - }, 1000); //every 1 seconds - }, - remove() {} - }, - // { - // name: "inverted mouse", - // description: "your mouse is scrambled
it's fine, just rotate it 90 degrees", - // maxCount: 1, - // count: 0, - // frequency: 0, - // isExperimentHide: true, - // isNonRefundable: true, - // isJunk: true, - // allowed() { - // return !m.isShipMode - // }, - // requires: "not ship", - // effect() { - // document.body.addEventListener("mousemove", (e) => { - // const ratio = window.innerWidth / window.innerHeight - // simulation.mouse.x = e.clientY * ratio - // simulation.mouse.y = e.clientX / ratio; - // }); - // }, - // remove() { - // // m.look = m.lookDefault - // } - // }, - { - name: "Fourier analysis", - description: "your aiming is now controlled by this equation:
2sin(0.0133t) + sin(0.013t) + 0.5sin(0.031t)+ 0.33sin(0.03t)", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - allowed() { - return !m.isShipMode - }, - requires: "not ship", - effect() { - m.look = () => { - m.angle = 2 * Math.sin(m.cycle * 0.0133) + Math.sin(m.cycle * 0.013) + 0.5 * Math.sin(m.cycle * 0.031) + 0.33 * Math.sin(m.cycle * 0.03) - const scale = 0.8; - simulation.mouse.y - m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; - m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; - m.transX += (m.transSmoothX - m.transX) * 0.07; - m.transY += (m.transSmoothY - m.transY) * 0.07; - } - }, - remove() { - if (this.count) m.look = m.lookDefault - } - }, - { - name: "disintegrated armament", - description: "spawn a gun
remove your active gun", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return b.inventory.length > 0 - }, - requires: "at least 1 gun", - effect() { - if (b.inventory.length > 0) b.removeGun(b.guns[b.activeGun].name) - simulation.makeGunHUD() - powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun"); - }, - remove() {} - }, - { - name: "probability", - description: "increase the frequency
of one random tech by 100", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - let options = []; //find what tech I could get - for (let i = 0, len = tech.tech.length; i < len; i++) { - if ( - tech.tech[i].count < tech.tech[i].maxCount && - tech.tech[i].allowed() && - !tech.tech[i].isJunk && - !tech.tech.isLore - ) { - options.push(i); - } - } - if (options.length) { - const index = options[Math.floor(Math.random() * options.length)] - tech.tech[index].frequency = 100 - } - }, - remove() {} - }, - { - name: "encryption", - description: "secure tech information", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - String.prototype.shuffle = function() { - var a = this.split(""), - n = a.length; - - for (var i = n - 1; i > 0; i--) { - var j = Math.floor(Math.random() * (i + 1)); - var tmp = a[i]; - a[i] = a[j]; - a[j] = tmp; - } - return a.join(""); - } - - for (let i = 0, len = tech.tech.length; i < len; i++) tech.tech[i].name = tech.tech[i].name.shuffle() - }, - remove() {} - }, - { - name: "quantum leap", - description: "become an alternate version of yourself
every 20 seconds", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - setInterval(() => { - m.switchWorlds() - simulation.trails() - }, 20000); //every 30 seconds - }, - remove() {} - }, - { - name: "score", - description: "Add a score to n-gon!", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - setInterval(() => { - let score = Math.ceil(1000 * Math.random() * Math.random() * Math.random() * Math.random() * Math.random()) - simulation.makeTextLog(`simulation.score = ${score.toFixed(0)}`); - }, 10000); //every 10 seconds - }, - remove() {} - }, - { - name: "pop-ups", - description: "sign up to learn endless easy ways to win n-gon
that Landgreen doesn't want you to know!!!1!!", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - setInterval(() => { - alert(`The best combo is ${tech.tech[Math.floor(Math.random() * tech.tech.length)].name} with ${tech.tech[Math.floor(Math.random() * tech.tech.length)].name}!`); - }, 30000); //every 30 seconds - }, - remove() {} - }, - { - name: "music", - description: "add music to n-gon", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - window.open('https://www.youtube.com/watch?v=lEbHeSdmS-k&list=PL9Z5wjoBiPKEDhwCW2RN-VZoCpmhIojdn', '_blank') - }, - remove() {} - }, - { - name: "performance", - description: "display performance stats to n-gon", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - (function() { - var script = document.createElement('script'); - script.onload = function() { - var stats = new Stats(); - document.body.appendChild(stats.dom); - requestAnimationFrame(function loop() { - stats.update(); - requestAnimationFrame(loop) - }); - }; - script.src = 'https://unpkg.com/stats.js@0.17.0/build/stats.min.js'; - document.head.appendChild(script); - })() - //move health to the right - document.getElementById("health").style.left = "86px" - document.getElementById("health-bg").style.left = "86px" - document.getElementById("defense-bar").style.left = "86px" - document.getElementById("damage-bar").style.left = "86px" - }, - remove() {} - }, - { - name: "repartitioning", - description: "set the frequency of finding normal tech to 0
spawn 5 tech", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isJunk) { - tech.tech[i].frequency = 2 - } else { - tech.tech[i].frequency = 0 - } - } - for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x, m.pos.y, "tech"); - }, - remove() {} - }, - { - name: "defragment", - description: "set the frequency of finding JUNKtech to zero", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - for (let i = tech.tech.length - 1; i > 0; i--) { - if (tech.tech[i].isJunk) tech.tech[i].frequency = 0 - } - }, - remove() {} - }, - // { - // name: "lubrication", - // description: "reduce block density and friction for this level", - // maxCount: 9, - // count: 0, - // frequency: 0, - // isNonRefundable: true, - // isExperimentHide: true, - // isJunk: true, - // allowed() { - // return true - // }, - // requires: "", - // effect() { - // for (let i = 0; i < body.length; i++) { - // Matter.Body.setDensity(body[i], 0.0001) // 0.001 is normal - // body[i].friction = 0.01 - // } - // }, - // remove() {} - // }, - { - name: "pitch", - description: "oscillate the pitch of your world", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - setInterval(() => { - if (!simulation.paused) ctx.rotate(0.001 * Math.sin(simulation.cycle * 0.01)) - }, 16); - }, - remove() {} - }, - { - name: "umbra", - description: "produce a blue glow around everything
and probably some simulation lag", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - ctx.shadowColor = '#06f'; - ctx.shadowBlur = 25; - }, - remove() {} - }, - { - name: "lighter", - description: `ctx.globalCompositeOperation = "lighter"`, - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return m.fieldUpgrades[m.fieldMode].name !== "negative mass" - }, - requires: "", - effect() { - ctx.globalCompositeOperation = "lighter"; - }, - remove() {} - }, - { - name: "rewind", - description: "every 10 seconds rewind 2 seconds", - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - setInterval(() => { - m.rewind(120) - m.energy += 0.4 - }, 10000); - // for (let i = 0; i < 24; i++) { - // setTimeout(() => { m.rewind(120) }, i * 5000); - // } - }, - remove() {} - }, - { - name: "undo", - description: "every 4 seconds rewind 1/2 a second", - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - setInterval(() => { - m.rewind(30) - m.energy += 0.2 - }, 4000); - }, - remove() {} - }, - { - name: "energy to mass conversion", - description: "convert your energy into blocks", - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - for (let i = 0, len = 40; i < len; i++) { - setTimeout(() => { - m.energy -= 1 / len - const index = body.length - where = Vector.add(m.pos, { - x: 400 * (Math.random() - 0.5), - y: 400 * (Math.random() - 0.5) - }) - spawn.bodyRect(where.x, where.y, Math.floor(15 + 100 * Math.random()), Math.floor(15 + 100 * Math.random())); - body[index].collisionFilter.category = cat.body; - body[index].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet - body[index].classType = "body"; - Composite.add(engine.world, body[index]); //add to world - }, i * 100); - } - - }, - remove() {} - }, - { - name: "level.nextLevel()", - description: "advance to the next level", - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - level.nextLevel(); - }, - remove() {} - }, - { - name: "reincarnation", - description: "kill all mobs and spawn new ones
(also spawn a few extra mobs for fun)", - maxCount: 3, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - spawn.setSpawnList(); - spawn.setSpawnList(); - for (let i = 0, len = mob.length; i < len; i++) { - if (mob[i].alive && !mob[i].shield && !mob[i].isBadTarget) { - const pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; - spawn[pick](mob[i].position.x, mob[i].position.y); - if (Math.random() < 0.5) spawn[pick](mob[i].position.x, mob[i].position.y); - mob[i].death(); - } - } - }, - remove() {} - }, - { - name: "expert system", - description: "spawn a tech power up
+64% JUNK to tech pool", - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - powerUps.spawn(m.pos.x, m.pos.y, "tech"); - tech.addJunkTechToPool(0.64) - }, - remove() {} - }, - { - name: "energy investment", - description: "every 10 seconds drain your energy
return it doubled 5 seconds later", - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - setInterval(() => { - if (!simulation.paused) { - const energy = m.energy - m.energy = 0 - setTimeout(() => { //return energy - m.energy += 2 * energy - }, 5000); - } - }, 10000); - }, - remove() {} - }, - { - name: "missile launching system", - description: "fire missiles for the next 120 seconds", - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - for (let i = 0; i < 120; i++) { - setTimeout(() => { - const where = { - x: m.pos.x, - y: m.pos.y - 40 - } - b.missile(where, -Math.PI / 2 + 0.2 * (Math.random() - 0.5) * Math.sqrt(tech.missileCount), -2) - }, i * 1000); - } - }, - remove() {} - }, - { - name: "grenade production", - description: "drop a grenade every 2 seconds", - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - setInterval(() => { - if (!simulation.paused && document.visibilityState !== "hidden") { - b.grenade(Vector.add(m.pos, { - x: 10 * (Math.random() - 0.5), - y: 10 * (Math.random() - 0.5) - }), -Math.PI / 2) //fire different angles for each grenade - const who = bullet[bullet.length - 1] - Matter.Body.setVelocity(who, { - x: who.velocity.x * 0.1, - y: who.velocity.y * 0.1 - }); - } - }, 2000); - }, - remove() {} - }, - { - name: "stubs", - description: "no knees or toes are drawn on the player
you can wall climb though", - maxCount: 1, - count: 0, - frequency: 0, - isSkin: true, - isJunk: true, - isNonRefundable: true, - allowed() { - return !m.isShipMode - }, - requires: "", - effect() { - m.skin.stubs() - Matter.Body.scale(player.parts[3], 2, 2); - }, - remove() { - // if (this.count) m.resetSkin(); - } - }, - { - name: "Sleipnir", - description: "grow more legs", - maxCount: 1, - count: 0, - frequency: 0, - isSkin: true, - isJunk: true, - allowed() { - return !m.isShipMode - }, - requires: "", - effect() { - m.skin.Sleipnir() - }, - remove() { - if (this.count) m.resetSkin(); - } - }, - { - name: "diegesis", - description: "indicate fire cooldown
through a rotation of your head", - maxCount: 1, - count: 0, - frequency: 0, - isSkin: true, - isJunk: true, - allowed() { - return !m.isShipMode - }, - requires: "", - effect() { - m.skin.diegesis() - }, - remove() { - if (this.count) m.resetSkin(); - } - }, - { - name: "🐱", - description: "🐈", - maxCount: 1, - count: 0, - frequency: 0, - isSkin: true, - isJunk: true, - allowed() { - return !m.isShipMode - }, - requires: "", - effect() { - m.skin.cat(); - }, - remove() { - if (this.count) m.resetSkin(); - } - }, - { - name: "n-gone", - description: "become invisible to yourself
mobs can still see you", - maxCount: 1, - count: 0, - frequency: 0, - isSkin: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - m.draw = () => {} - }, - remove() { - if (this.count) m.resetSkin(); - } - }, - { - name: "pareidolia", - description: "don't", - maxCount: 1, - count: 0, - frequency: 0, - isSkin: true, - isNonRefundable: true, - isJunk: true, - allowed() { - return !m.isShipMode - }, - requires: "", - effect() { - m.skin.pareidolia() - }, - remove() { - if (this.count) m.resetSkin(); - } - }, - { - name: "posture", - description: "stand a bit taller", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - allowed() { - return !m.isShipMode - }, - requires: "", - effect() { - m.yOffWhen.stand = 70 - }, - remove() { - m.yOffWhen.stand = 49 - } - }, - { - name: "rhythm", - description: "you oscillate up and down", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - isNonRefundable: true, - allowed() { - return !m.isShipMode - }, - requires: "", - effect() { - setInterval(() => { - m.yOffWhen.stand = 53 + 28 * Math.sin(simulation.cycle * 0.2) - if (m.onGround && !m.crouch) m.yOffGoal = m.yOffWhen.stand - }, 100); - }, - remove() {} - }, - { - name: "prism", - description: "you cycle through different colors", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - m.color = { - hue: 0, - sat: 100, - light: 50 - } - setInterval(function() { - m.color.hue++ - m.setFillColors() - }, 10); - }, - remove() {} - }, - // { - // name: "microtransactions", - // description: `when you choose a tech you can
use ${powerUps.orb.research(1)} to buy a free in game skin`, - // maxCount: 1, - // count: 0, - // frequency: 0, - // isJunk: true, - // allowed() { - // return true - // }, - // requires: "", - // effect() { - // tech.isMicroTransactions = true - // }, - // remove() { - // tech.isMicroTransactions = false - // } - // }, - { - name: "ship", - description: "fly around with no legs
reduce combat difficulty by 1 level", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return !m.isShipMode && !m.isAltSkin && m.fieldUpgrades[m.fieldMode].name !== "negative mass" - }, - requires: "", - effect() { - m.isAltSkin = true - m.shipMode() - level.difficultyDecrease(simulation.difficultyMode) - //unlock relativistic rotation - for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].name === "relativistic rotation") tech.tech[i].frequency = 10 - } - }, - remove() {} - }, - { - name: "circular symmetry", - description: "turning the ship rotates the universe instead
+200% damage", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return m.isShipMode - }, - requires: "", - effect() { - tech.damage *= 3 - - m.look = () => { - // const scale = 0; - m.transSmoothX = canvas.width2 - m.pos.x // - (simulation.mouse.x - canvas.width2) * scale; - m.transSmoothY = canvas.height2 - m.pos.y // - (simulation.mouse.y - canvas.height2) * scale; - m.transX += (m.transSmoothX - m.transX) * m.lookSmoothing; - m.transY += (m.transSmoothY - m.transY) * m.lookSmoothing; - ctx.restore(); - ctx.save(); - ctx.translate(canvas.width2, canvas.height2); //center - ctx.rotate(-m.angle) - ctx.translate(-canvas.width2, -canvas.height2); //center - } - }, - remove() {} - }, - { - name: "assimilation", - description: "all your bots are converted to the same random model", - maxCount: 1, - count: 0, - frequency: 0, - isBotTech: true, - isNonRefundable: true, - isJunk: true, - allowed() { - return b.totalBots() > 2 - }, - requires: "at least 3 bots", - effect() { - const total = b.totalBots(); - tech.dynamoBotCount = 0; - tech.nailBotCount = 0; - tech.laserBotCount = 0; - tech.orbitBotCount = 0; - tech.foamBotCount = 0; - tech.boomBotCount = 0; - tech.plasmaBotCount = 0; - tech.missileBotCount = 0; - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType) bullet[i].endCycle = 0 - } - - const bots = [ - () => { - b.nailBot(); - tech.nailBotCount++; - }, - () => { - b.foamBot(); - tech.foamBotCount++; - }, - () => { - b.boomBot(); - tech.boomBotCount++; - }, - () => { - b.laserBot(); - tech.laserBotCount++; - }, - () => { - b.orbitBot(); - tech.orbitBotCount++ - }, - () => { - b.dynamoBot(); - tech.dynamoBotCount++ - } - ] - const index = Math.floor(Math.random() * bots.length) - for (let i = 0; i < total; i++) bots[index]() - }, - remove() {} - }, - { - name: "growth hacking", - description: "increase combat difficulty by 1 level", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - level.difficultyIncrease(simulation.difficultyMode) - }, - remove() {} - }, - { - name: "stun", - description: "stun all mobs for up to 8 seconds", - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - for (let i = 0; i < mob.length; i++) mobs.statusStun(mob[i], 480) - }, - remove() {} - }, - { - name: "translucent", - description: "spawn 3 gun power ups
your bullets and bots are transparent", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - for (let i = 0; i < 3; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun"); - - // //removes guns and ammo - // b.inventory = []; - // b.activeGun = null; - // b.inventoryGun = 0; - // for (let i = 0, len = b.guns.length; i < len; ++i) { - // b.guns[i].have = false; - // if (b.guns[i].ammo !== Infinity) b.guns[i].ammo = 0; - // } - // simulation.makeGunHUD(); //update gun HUD - b.bulletDraw = () => {}; //make bullets invisible - }, - remove() {} - }, - { - name: "re-research", - description: `eject all your ${powerUps.orb.research(1)}`, - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return powerUps.research.count > 3 - }, - requires: "at least 4 research", - effect() { - powerUps.spawnDelay("research", powerUps.research.count); - powerUps.research.count = 0 - }, - remove() {} - }, - { - name: "black hole", - description: `use your energy and ${powerUps.orb.research(4)} to spawn
inside the event horizon of a huge black hole`, - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return powerUps.research.count > 3 - }, - requires: "at least 4 research", - effect() { - m.energy = 0 - spawn.suckerBoss(m.pos.x, m.pos.y - 700) - powerUps.research.changeRerolls(-4) - simulation.makeTextLog(`m.research --
${powerUps.research.count}`) - }, - remove() {} - }, - { - name: "black hole cluster", - description: `spawn 30 nearby black holes`, - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - const unit = { - x: 1, - y: 0 - } - for (let i = 0; i < 30; i++) { - const where = Vector.add(m.pos, Vector.mult(Vector.rotate(unit, Math.random() * 2 * Math.PI), 2000 + 1200 * Math.random())) - spawn.sucker(where.x, where.y, 140) - const who = mob[mob.length - 1] - who.locatePlayer() - // who.damageReduction = 0.2 - } - }, - remove() {} - }, - { - name: "rule 30", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - allowed() { - return !build.isExperimentSelection - }, - requires: "NOT EXPERIMENT MODE", - effect() {}, - remove() {}, - state: [ - [false, false, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, false, false, false, false, false, true, false, false, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, Math.random() > 0.8, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, false, false, false, false, false] - ], - rule(state, a, b, c) { - //30 - if (state[a] && state[b] && state[c]) return false; // TTT => F - if (state[a] && state[b] && !state[c]) return false; // TTF => F - if (state[a] && !state[b] && state[c]) return false; //TFT => F - if (state[a] && !state[b] && !state[c]) return true; //TFF => T - if (!state[a] && state[b] && state[c]) return true; //FTT => T - if (!state[a] && state[b] && !state[c]) return true; //FTF => T - if (!state[a] && !state[b] && state[c]) return true; //FFT => T - if (!state[a] && !state[b] && !state[c]) return false; //FFF => F - }, - id: 0, - researchSpawned: 0, - descriptionFunction() { - const loop = () => { - if ((simulation.paused || simulation.isChoosing) && m.alive && !build.isExperimentSelection) { //&& (!simulation.isChoosing || this.count === 0) - let b = []; //produce next row - b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 1, 0, 1)); //left edge wrap around - for (let i = 1; i < this.state[this.state.length - 1].length - 1; i++) { //apply rule to the rest of the array - b.push(this.rule(this.state[this.state.length - 1], i - 1, i, i + 1)); - } - b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 2, this.state[this.state.length - 1].length - 1, 0)); //right edge wrap around - this.state.push(b) - if (document.getElementById(`cellular-rule-id${this.id}`)) document.getElementById(`cellular-rule-id${this.id}`).innerHTML = this.outputText() //convert to squares and send HTML - if (this.count && this.researchSpawned < 12 && !(this.state.length % 10)) { - this.researchSpawned++ - powerUps.spawn(m.pos.x - 50 + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "research"); - } - setTimeout(() => { - loop() - }, 300 + 5 * this.state.length); - } - } - setTimeout(() => { - loop() - }, 300); - this.id++ - return `${this.outputText()}` - }, - outputText() { - let text = "
"
-                for (let j = 0; j < this.state.length; j++) {
-                    // text += "

" - text += "

" - for (let i = 0; i < this.state[j].length; i++) { - if (this.state[j][i]) { - text += "■" //"☻" //"⬛" //"█" //"■" - } else { - text += " " //"□" //"☺" //"⬜" //"    " //"□" - } - } - text += "

" - } - text += "
" - return text - }, - }, - { - name: "rule 90", - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - allowed() { - return !build.isExperimentSelection - }, - requires: "NOT EXPERIMENT MODE", - effect() {}, - remove() {}, - state: [ - [false, false, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, false, false, false, false, false, true, true, false, false, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, Math.random() > 0.8, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, false, false, false, false] - ], - rule(state, a, b, c) { //90 - if (state[a] && state[b] && state[c]) return false; // TTT => F - if (state[a] && state[b] && !state[c]) return true; // TTF => T - if (state[a] && !state[b] && state[c]) return false; //TFT => F - if (state[a] && !state[b] && !state[c]) return true; //TFF => T - if (!state[a] && state[b] && state[c]) return true; //FTT => T - if (!state[a] && state[b] && !state[c]) return false; //FTF => F - if (!state[a] && !state[b] && state[c]) return true; //FFT => T - if (!state[a] && !state[b] && !state[c]) return false; //FFF => F - }, - id: 90, - researchSpawned: 0, - descriptionFunction() { - const loop = () => { - if ((simulation.paused || simulation.isChoosing) && m.alive && !build.isExperimentSelection) { //&& (!simulation.isChoosing || this.count === 0) - let b = []; //produce next row - b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 1, 0, 1)); //left edge wrap around - for (let i = 1; i < this.state[this.state.length - 1].length - 1; i++) { //apply rule to the rest of the array - b.push(this.rule(this.state[this.state.length - 1], i - 1, i, i + 1)); - } - b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 2, this.state[this.state.length - 1].length - 1, 0)); //right edge wrap around - this.state.push(b) - if (document.getElementById(`cellular-rule-id${this.id}`)) document.getElementById(`cellular-rule-id${this.id}`).innerHTML = this.outputText() //convert to squares and send HTML - if (this.count && this.researchSpawned < 12 && !(this.state.length % 10)) { - this.researchSpawned++ - powerUps.spawn(m.pos.x - 50 + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "research"); - } - setTimeout(() => { - loop() - }, 300 + 5 * this.state.length); - } - } - setTimeout(() => { - loop() - }, 300); - this.id++ - return `${this.outputText()}` - }, - outputText() { - let text = "
"
-                for (let j = 0; j < this.state.length; j++) {
-                    // text += "

" - text += "

" - for (let i = 0; i < this.state[j].length; i++) { - if (this.state[j][i]) { - text += "■" //"☻" //"⬛" //"█" //"■" - } else { - text += " " //"□" //"☺" //"⬜" //"    " //"□" - } - } - text += "

" - } - text += "
" - return text - }, - }, - { - name: "cosmogonic myth", - description: `open a portal to a primordial version of reality
in 5 minutes close the portal, spawn 1 of each power up
`, - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - const urls = ["https://scratch.mit.edu/projects/14005697/fullscreen/", "https://scratch.mit.edu/projects/22573757/fullscreen/", "https://scratch.mit.edu/projects/41429974/fullscreen/", "https://scratch.mit.edu/projects/43690666/fullscreen/", "https://codepen.io/lilgreenland/full/ozXNWZ", "https://codepen.io/lilgreenland/full/wzARJY", "classic/7-1-2017/", "classic/4-15-2018/", "classic/7-11-2019/", "classic/9-8-2019/", "classic/7-15-2020/", "classic/6-1-2021/"] - const choose = urls[Math.floor(Math.random() * urls.length)] - console.log(`opening new tab" ${choose}`) - let tab = window.open(choose, "_blank"); - setTimeout(() => { - tab.close(); - powerUps.spawn(m.pos.x, m.pos.y, "gun"); - setTimeout(() => { - powerUps.spawn(m.pos.x, m.pos.y - 50, "ammo") - }, 250); - setTimeout(() => { - powerUps.spawn(m.pos.x + 50, m.pos.y, "field"); - }, 500); - setTimeout(() => { - powerUps.spawn(m.pos.x + 50, m.pos.y - 50, "heal"); - }, 750); - setTimeout(() => { - powerUps.spawn(m.pos.x - 50, m.pos.y, "tech"); - }, 1000); - setTimeout(() => { - powerUps.spawn(m.pos.x - 50, m.pos.y - 50, "research"); - }, 1250); - }, 1000 * 5 * 60); - }, - remove() {} - }, - { - name: "planetesimals", - description: `play planetesimals (an asteroids-like game)
clear levels in planetesimals to spawn tech
if you die in planetesimals you die in n-gon`, - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - window.open('../../planetesimals/index.html', '_blank') - // powerUps.spawn(m.pos.x, m.pos.y, "tech"); - - // for communicating to other tabs, like planetesimals - // Connection to a broadcast channel - const bc = new BroadcastChannel('planetesimals'); - bc.activated = false - - bc.onmessage = function(ev) { - if (ev.data === 'tech') powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); - if (ev.data === 'death') { - m.death() - bc.close(); //end session - } - if (ev.data === 'ready' && !bc.activated) { - bc.activated = true //prevents n-gon from activating multiple copies of planetesimals - bc.postMessage("activate"); - } - } - }, - remove() {} - }, - { - name: "tinker", - description: "permanently unlock JUNKtech in experiment mode
this effect is stored for future visits", - maxCount: 1, - count: 0, - frequency: 0, - frequencyDefault: 0, - isJunk: true, - isNonRefundable: true, - allowed() { - return !localSettings.isJunkExperiment - }, - requires: "", - effect() { - localSettings.isJunkExperiment = true - if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage - }, - remove() {} - }, - { - name: "NFT", - descriptionFunction() { - return `buy your current game seed: ${Math.initialSeed}
no one is allowed to use your seeds
if they use them they are gonna get in trouble

your seeds: ${localSettings.personalSeeds.join(", ")}` - }, - maxCount: 1, - count: 0, - frequency: 0, - isJunk: true, - isNonRefundable: true, - allowed: () => true, - requires: "", - effect() { - localSettings.personalSeeds.push(Math.initialSeed) - if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage - }, - remove() {} - }, - // { - // name: "rule 90", - // maxCount: 1, - // count: 0, - // frequency: 0, - // isJunk: true, - // allowed() { - // return true - // }, - // requires: "", - // effect() {}, - // remove() {}, - // state: [ - // [false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false] - // ], - // rule(state, a, b, c) { - // if (state[a] && state[b] && state[c]) return false; // TTT => F - // if (state[a] && state[b] && !state[c]) return true; // TTF => T - // if (state[a] && !state[b] && state[c]) return false; //TFT => F - // if (state[a] && !state[b] && !state[c]) return true; //TFF => T - // if (!state[a] && state[b] && state[c]) return true; //FTT => T - // if (!state[a] && state[b] && !state[c]) return false; //FTF => F - // if (!state[a] && !state[b] && state[c]) return true; //FFT => T - // if (!state[a] && !state[b] && !state[c]) return false; //FFF => F - // }, - // id: 0, - // descriptionFunction() { - // const loop = () => { - // if ((simulation.paused || simulation.isChoosing) && m.alive && !build.isExperimentSelection) { //&& (!simulation.isChoosing || this.count === 0) - // let b = []; //produce next row - // b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 1, 0, 1)); //left edge wrap around - // for (let i = 1; i < this.state[this.state.length - 1].length - 1; i++) { //apply rule to the rest of the array - // b.push(this.rule(this.state[this.state.length - 1], i - 1, i, i + 1)); - // } - // b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 2, this.state[this.state.length - 1].length - 1, 0)); //right edge wrap around - // this.state.push(b) - // if (document.getElementById(`cellular-rule-id${this.id}`)) document.getElementById(`cellular-rule-id${this.id}`).innerHTML = this.outputText() //convert to squares and send HTML - // if (this.count && this.state.length < 120 && !(this.state.length % 10)) powerUps.spawn(m.pos.x - 50 + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "research"); - // setTimeout(() => { loop() }, 400); - // } - // } - // setTimeout(() => { loop() }, 400); - // // if (this.id === 0) { - // // for (let i = 0; i < 29; i++) this.state[0][i] = Math.random() < 0.5 //randomize seed - // // } - // this.id++ - // return `${this.outputText()}` - // }, - // outputText() { - // let text = "" - // for (let j = 0; j < this.state.length; j++) { - // text += "

" - // for (let i = 0; i < this.state[j].length; i++) { - // if (this.state[j][i]) { - // text += "⬛" //"█" //"■" - // } else { - // text += "⬜" //"    " //"□" - // } - // } - // text += "

" - // } - // return text - // }, - // }, - - //************************************************** - //************************************************** undefined / lore - //************************************************** tech - //************************************************** - { - name: `undefined`, - description: `this
 `, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - isLore: true, - // isExperimentHide: true, - allowed() { - return !build.isExperimentSelection - }, - requires: "NOT EXPERIMENT MODE", - effect() { - if (localSettings.loreCount > lore.conversation.length - 1) { //reward for people done with lore chapters (or on the final chapter) - for (let i = mob.length - 1; i > -1; i--) { //replace mobs with starters - if (!mob[i].isBoss && mob[i].isDropPowerUp && mob[i].alive) { - spawn.starter(mob[i].position.x, mob[i].position.y) - mob[i].leaveBody = false - mob[i].isDropPowerUp = false - mob[i].death() - - //spawn a random power up - // if (Math.random() < 1 / 5) { - // powerUps.spawn(mob[i].position.x, mob[i].position.y, "research") - // } else - if (Math.random() < 1 / 4) { - powerUps.spawn(mob[i].position.x, mob[i].position.y, "ammo") - } else if (Math.random() < 1 / 3) { - powerUps.spawn(mob[i].position.x, mob[i].position.y, "heal") - } else if (Math.random() < 1 / 2) { - powerUps.spawn(mob[i].position.x, mob[i].position.y, "boost") - } else { - powerUps.spawn(mob[i].position.x, mob[i].position.y, "coupling") - } - } - } - } - - setTimeout(() => { //a short delay, I can't remember why - lore.techCount++ - if (lore.techCount === lore.techGoal) { - // tech.removeLoreTechFromPool(); - this.frequency = 0; - this.description = `null is open at level.final()
 ` - } else { - this.frequency += lore.techGoal * 2 - // for (let i = 0; i < tech.tech.length; i++) { //set name for all unchosen copies of this tech - // if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech[i].description = `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool` - // } - // for (let i = 0, len = 10; i < len; i++) tech.addLoreTechToPool() - this.description = `uncaught error:
${Math.max(0, lore.techGoal - lore.techCount)} more required for access to null` - } - }, 1); - }, - remove() { - lore.techCount = 0; - this.maxCount = lore.techGoal; - this.description = `this
 ` } } + }, + { + name: "hidden variable", + descriptionFunction() { + return `spawn ${powerUps.orb.heal(20)}
but hide your health bar` + }, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + document.getElementById("health").style.display = "none" + document.getElementById("health-bg").style.display = "none" + document.getElementById("defense-bar").style.display = "none" + for (let i = 0; i < 20; i++) powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); + }, + remove() { } + }, + { + name: "not a bug", + description: "initiate a totally safe game crash for 10 seconds", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + const savedfunction = simulation.drawCircle + simulation.drawCircle = () => { + const a = mob[Infinity].position //crashed the game in a visually interesting way, because of the ctx.translate command is never reverted in the main game loop + } + setTimeout(() => { + simulation.drawCircle = savedfunction + canvas.width = canvas.width //clears the canvas // works on chrome at least + powerUps.spawn(m.pos.x, m.pos.y, "tech"); + }, 10000); + + // for (;;) {} //freezes the tab + }, + remove() { } + }, + { + name: "spinor", + description: "the direction you aim is determined by your position", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.look = function () { + //always on mouse look + m.angle = (((m.pos.x + m.pos.y) / 100 + Math.PI) % Math.PI * 2) - Math.PI + //smoothed mouse look translations + const scale = 0.8; + m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + + m.transX += (m.transSmoothX - m.transX) * 0.07; + m.transY += (m.transSmoothY - m.transY) * 0.07; + } + }, + remove() { + if (this.count) m.look = m.lookDefault + } + }, + { + name: "p-zombie", + description: "set your health to 1
all mobs, not bosses, die and resurrect as zombies", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + m.health = 0.01 //set health to 1 + m.displayHealth(); + for (let i = mob.length - 1; i > -1; i--) { //replace mobs with zombies + if (mob[i].isDropPowerUp && !mob[i].isBoss && mob[i].alive) { + mob[i].isSoonZombie = true + mob[i].death() + } + } + }, + remove() { } + }, + { + name: "decomposers", + description: "after they die mobs leave behind spawns", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return tech.deathSpawns === 0 + }, + requires: "", + effect() { + tech.deathSpawns = 0.2 + }, + remove() { + tech.deathSpawns = 0 + } + }, + { + name: "panopticon", + description: "mobs can always see you", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setInterval(() => { + for (let i = 0; i < mob.length; i++) { + if (!mob[i].shield && mob[i].isDropPowerUp) { + mob[i].locatePlayer() + mob[i].seePlayer.yes = true; + } + } + }, 1000); //every 1 seconds + }, + remove() { } + }, + // { + // name: "inverted mouse", + // description: "your mouse is scrambled
it's fine, just rotate it 90 degrees", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isExperimentHide: true, + // isNonRefundable: true, + // isJunk: true, + // allowed() { + // return !m.isShipMode + // }, + // requires: "not ship", + // effect() { + // document.body.addEventListener("mousemove", (e) => { + // const ratio = window.innerWidth / window.innerHeight + // simulation.mouse.x = e.clientY * ratio + // simulation.mouse.y = e.clientX / ratio; + // }); + // }, + // remove() { + // // m.look = m.lookDefault + // } + // }, + { + name: "Fourier analysis", + description: "your aiming is now controlled by this equation:
2sin(0.0133t) + sin(0.013t) + 0.5sin(0.031t)+ 0.33sin(0.03t)", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "not ship", + effect() { + m.look = () => { + m.angle = 2 * Math.sin(m.cycle * 0.0133) + Math.sin(m.cycle * 0.013) + 0.5 * Math.sin(m.cycle * 0.031) + 0.33 * Math.sin(m.cycle * 0.03) + const scale = 0.8; + simulation.mouse.y + m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + m.transX += (m.transSmoothX - m.transX) * 0.07; + m.transY += (m.transSmoothY - m.transY) * 0.07; + } + }, + remove() { + if (this.count) m.look = m.lookDefault + } + }, + { + name: "disintegrated armament", + description: "spawn a gun
remove your active gun", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return b.inventory.length > 0 + }, + requires: "at least 1 gun", + effect() { + if (b.inventory.length > 0) b.removeGun(b.guns[b.activeGun].name) + simulation.makeGunHUD() + powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun"); + }, + remove() { } + }, + { + name: "probability", + description: "increase the frequency
of one random tech by 100", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + let options = []; //find what tech I could get + for (let i = 0, len = tech.tech.length; i < len; i++) { + if ( + tech.tech[i].count < tech.tech[i].maxCount && + tech.tech[i].allowed() && + !tech.tech[i].isJunk && + !tech.tech.isLore + ) { + options.push(i); + } + } + if (options.length) { + const index = options[Math.floor(Math.random() * options.length)] + tech.tech[index].frequency = 100 + } + }, + remove() { } + }, + { + name: "encryption", + description: "secure tech information", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + String.prototype.shuffle = function () { + var a = this.split(""), + n = a.length; + + for (var i = n - 1; i > 0; i--) { + var j = Math.floor(Math.random() * (i + 1)); + var tmp = a[i]; + a[i] = a[j]; + a[j] = tmp; + } + return a.join(""); + } + + for (let i = 0, len = tech.tech.length; i < len; i++) tech.tech[i].name = tech.tech[i].name.shuffle() + }, + remove() { } + }, + { + name: "quantum leap", + description: "become an alternate version of yourself
every 20 seconds", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setInterval(() => { + m.switchWorlds() + simulation.trails() + }, 20000); //every 30 seconds + }, + remove() { } + }, + { + name: "score", + description: "Add a score to n-gon!", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setInterval(() => { + let score = Math.ceil(1000 * Math.random() * Math.random() * Math.random() * Math.random() * Math.random()) + simulation.makeTextLog(`simulation.score = ${score.toFixed(0)}`); + }, 10000); //every 10 seconds + }, + remove() { } + }, + { + name: "pop-ups", + description: "sign up to learn endless easy ways to win n-gon
that Landgreen doesn't want you to know!!!1!!", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setInterval(() => { + alert(`The best combo is ${tech.tech[Math.floor(Math.random() * tech.tech.length)].name} with ${tech.tech[Math.floor(Math.random() * tech.tech.length)].name}!`); + }, 30000); //every 30 seconds + }, + remove() { } + }, + { + name: "music", + description: "add music to n-gon", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + window.open('https://www.youtube.com/watch?v=lEbHeSdmS-k&list=PL9Z5wjoBiPKEDhwCW2RN-VZoCpmhIojdn', '_blank') + }, + remove() { } + }, + { + name: "performance", + description: "display performance stats to n-gon", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + (function () { + var script = document.createElement('script'); + script.onload = function () { + var stats = new Stats(); + document.body.appendChild(stats.dom); + requestAnimationFrame(function loop() { + stats.update(); + requestAnimationFrame(loop) + }); + }; + script.src = 'https://unpkg.com/stats.js@0.17.0/build/stats.min.js'; + document.head.appendChild(script); + })() + //move health to the right + document.getElementById("health").style.left = "86px" + document.getElementById("health-bg").style.left = "86px" + document.getElementById("defense-bar").style.left = "86px" + document.getElementById("damage-bar").style.left = "86px" + }, + remove() { } + }, + { + name: "repartitioning", + description: "set the frequency of finding normal tech to 0
spawn 5 tech", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isJunk) { + tech.tech[i].frequency = 2 + } else { + tech.tech[i].frequency = 0 + } + } + for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x, m.pos.y, "tech"); + }, + remove() { } + }, + { + name: "defragment", + description: "set the frequency of finding JUNKtech to zero", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = tech.tech.length - 1; i > 0; i--) { + if (tech.tech[i].isJunk) tech.tech[i].frequency = 0 + } + }, + remove() { } + }, + // { + // name: "lubrication", + // description: "reduce block density and friction for this level", + // maxCount: 9, + // count: 0, + // frequency: 0, + // isNonRefundable: true, + // isExperimentHide: true, + // isJunk: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() { + // for (let i = 0; i < body.length; i++) { + // Matter.Body.setDensity(body[i], 0.0001) // 0.001 is normal + // body[i].friction = 0.01 + // } + // }, + // remove() {} + // }, + { + name: "pitch", + description: "oscillate the pitch of your world", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setInterval(() => { + if (!simulation.paused) ctx.rotate(0.001 * Math.sin(simulation.cycle * 0.01)) + }, 16); + }, + remove() { } + }, + { + name: "umbra", + description: "produce a blue glow around everything
and probably some simulation lag", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + ctx.shadowColor = '#06f'; + ctx.shadowBlur = 25; + }, + remove() { } + }, + { + name: "lighter", + description: `ctx.globalCompositeOperation = "lighter"`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return m.fieldUpgrades[m.fieldMode].name !== "negative mass" + }, + requires: "", + effect() { + ctx.globalCompositeOperation = "lighter"; + }, + remove() { } + }, + { + name: "rewind", + description: "every 10 seconds rewind 2 seconds", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setInterval(() => { + m.rewind(120) + m.energy += 0.4 + }, 10000); + // for (let i = 0; i < 24; i++) { + // setTimeout(() => { m.rewind(120) }, i * 5000); + // } + }, + remove() { } + }, + { + name: "undo", + description: "every 4 seconds rewind 1/2 a second", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setInterval(() => { + m.rewind(30) + m.energy += 0.2 + }, 4000); + }, + remove() { } + }, + { + name: "energy to mass conversion", + description: "convert your energy into blocks", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0, len = 40; i < len; i++) { + setTimeout(() => { + m.energy -= 1 / len + const index = body.length + where = Vector.add(m.pos, { + x: 400 * (Math.random() - 0.5), + y: 400 * (Math.random() - 0.5) + }) + spawn.bodyRect(where.x, where.y, Math.floor(15 + 100 * Math.random()), Math.floor(15 + 100 * Math.random())); + body[index].collisionFilter.category = cat.body; + body[index].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet + body[index].classType = "body"; + Composite.add(engine.world, body[index]); //add to world + }, i * 100); + } + + }, + remove() { } + }, + { + name: "level.nextLevel()", + description: "advance to the next level", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + level.nextLevel(); + }, + remove() { } + }, + { + name: "reincarnation", + description: "kill all mobs and spawn new ones
(also spawn a few extra mobs for fun)", + maxCount: 3, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + spawn.setSpawnList(); + spawn.setSpawnList(); + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].alive && !mob[i].shield && !mob[i].isBadTarget) { + const pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; + spawn[pick](mob[i].position.x, mob[i].position.y); + if (Math.random() < 0.5) spawn[pick](mob[i].position.x, mob[i].position.y); + mob[i].death(); + } + } + }, + remove() { } + }, + { + name: "expert system", + description: "spawn a tech power up
+64% JUNK to tech pool", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + powerUps.spawn(m.pos.x, m.pos.y, "tech"); + tech.addJunkTechToPool(0.64) + }, + remove() { } + }, + { + name: "energy investment", + description: "every 10 seconds drain your energy
return it doubled 5 seconds later", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setInterval(() => { + if (!simulation.paused) { + const energy = m.energy + m.energy = 0 + setTimeout(() => { //return energy + m.energy += 2 * energy + }, 5000); + } + }, 10000); + }, + remove() { } + }, + { + name: "missile launching system", + description: "fire missiles for the next 120 seconds", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0; i < 120; i++) { + setTimeout(() => { + const where = { + x: m.pos.x, + y: m.pos.y - 40 + } + b.missile(where, -Math.PI / 2 + 0.2 * (Math.random() - 0.5) * Math.sqrt(tech.missileCount), -2) + }, i * 1000); + } + }, + remove() { } + }, + { + name: "grenade production", + description: "drop a grenade every 2 seconds", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setInterval(() => { + if (!simulation.paused && document.visibilityState !== "hidden") { + b.grenade(Vector.add(m.pos, { + x: 10 * (Math.random() - 0.5), + y: 10 * (Math.random() - 0.5) + }), -Math.PI / 2) //fire different angles for each grenade + const who = bullet[bullet.length - 1] + Matter.Body.setVelocity(who, { + x: who.velocity.x * 0.1, + y: who.velocity.y * 0.1 + }); + } + }, 2000); + }, + remove() { } + }, + { + name: "stubs", + description: "no knees or toes are drawn on the player
you can wall climb though", + maxCount: 1, + count: 0, + frequency: 0, + isSkin: true, + isJunk: true, + isNonRefundable: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.skin.stubs() + Matter.Body.scale(player.parts[3], 2, 2); + }, + remove() { + // if (this.count) m.resetSkin(); + } + }, + { + name: "Sleipnir", + description: "grow more legs", + maxCount: 1, + count: 0, + frequency: 0, + isSkin: true, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.skin.Sleipnir() + }, + remove() { + if (this.count) m.resetSkin(); + } + }, + { + name: "diegesis", + description: "indicate fire cooldown
through a rotation of your head", + maxCount: 1, + count: 0, + frequency: 0, + isSkin: true, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.skin.diegesis() + }, + remove() { + if (this.count) m.resetSkin(); + } + }, + { + name: "🐱", + description: "🐈", + maxCount: 1, + count: 0, + frequency: 0, + isSkin: true, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.skin.cat(); + }, + remove() { + if (this.count) m.resetSkin(); + } + }, + { + name: "n-gone", + description: "become invisible to yourself
mobs can still see you", + maxCount: 1, + count: 0, + frequency: 0, + isSkin: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + m.draw = () => { } + }, + remove() { + if (this.count) m.resetSkin(); + } + }, + { + name: "pareidolia", + description: "don't", + maxCount: 1, + count: 0, + frequency: 0, + isSkin: true, + isNonRefundable: true, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.skin.pareidolia() + }, + remove() { + if (this.count) m.resetSkin(); + } + }, + { + name: "posture", + description: "stand a bit taller", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.yOffWhen.stand = 70 + }, + remove() { + m.yOffWhen.stand = 49 + } + }, + { + name: "rhythm", + description: "you oscillate up and down", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + setInterval(() => { + m.yOffWhen.stand = 53 + 28 * Math.sin(simulation.cycle * 0.2) + if (m.onGround && !m.crouch) m.yOffGoal = m.yOffWhen.stand + }, 100); + }, + remove() { } + }, + { + name: "prism", + description: "you cycle through different colors", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + m.color = { + hue: 0, + sat: 100, + light: 50 + } + setInterval(function () { + m.color.hue++ + m.setFillColors() + }, 10); + }, + remove() { } + }, + // { + // name: "microtransactions", + // description: `when you choose a tech you can
use ${powerUps.orb.research(1)} to buy a free in game skin`, + // maxCount: 1, + // count: 0, + // frequency: 0, + // isJunk: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() { + // tech.isMicroTransactions = true + // }, + // remove() { + // tech.isMicroTransactions = false + // } + // }, + { + name: "ship", + description: "fly around with no legs
reduce combat difficulty by 1 level", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return !m.isShipMode && !m.isAltSkin && m.fieldUpgrades[m.fieldMode].name !== "negative mass" + }, + requires: "", + effect() { + m.isAltSkin = true + m.shipMode() + level.difficultyDecrease(simulation.difficultyMode) + //unlock relativistic rotation + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].name === "relativistic rotation") tech.tech[i].frequency = 10 + } + }, + remove() { } + }, + { + name: "circular symmetry", + description: "turning the ship rotates the universe instead
+200% damage", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return m.isShipMode + }, + requires: "", + effect() { + tech.damage *= 3 + + m.look = () => { + // const scale = 0; + m.transSmoothX = canvas.width2 - m.pos.x // - (simulation.mouse.x - canvas.width2) * scale; + m.transSmoothY = canvas.height2 - m.pos.y // - (simulation.mouse.y - canvas.height2) * scale; + m.transX += (m.transSmoothX - m.transX) * m.lookSmoothing; + m.transY += (m.transSmoothY - m.transY) * m.lookSmoothing; + ctx.restore(); + ctx.save(); + ctx.translate(canvas.width2, canvas.height2); //center + ctx.rotate(-m.angle) + ctx.translate(-canvas.width2, -canvas.height2); //center + } + }, + remove() { } + }, + { + name: "assimilation", + description: "all your bots are converted to the same random model", + maxCount: 1, + count: 0, + frequency: 0, + isBotTech: true, + isNonRefundable: true, + isJunk: true, + allowed() { + return b.totalBots() > 2 + }, + requires: "at least 3 bots", + effect() { + const total = b.totalBots(); + tech.dynamoBotCount = 0; + tech.nailBotCount = 0; + tech.laserBotCount = 0; + tech.orbitBotCount = 0; + tech.foamBotCount = 0; + tech.boomBotCount = 0; + tech.plasmaBotCount = 0; + tech.missileBotCount = 0; + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) bullet[i].endCycle = 0 + } + + const bots = [ + () => { + b.nailBot(); + tech.nailBotCount++; + }, + () => { + b.foamBot(); + tech.foamBotCount++; + }, + () => { + b.boomBot(); + tech.boomBotCount++; + }, + () => { + b.laserBot(); + tech.laserBotCount++; + }, + () => { + b.orbitBot(); + tech.orbitBotCount++ + }, + () => { + b.dynamoBot(); + tech.dynamoBotCount++ + } + ] + const index = Math.floor(Math.random() * bots.length) + for (let i = 0; i < total; i++) bots[index]() + }, + remove() { } + }, + { + name: "growth hacking", + description: "increase combat difficulty by 1 level", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + level.difficultyIncrease(simulation.difficultyMode) + }, + remove() { } + }, + { + name: "stun", + description: "stun all mobs for up to 8 seconds", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0; i < mob.length; i++) mobs.statusStun(mob[i], 480) + }, + remove() { } + }, + { + name: "translucent", + description: "spawn 3 gun power ups
your bullets and bots are transparent", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0; i < 3; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun"); + + // //removes guns and ammo + // b.inventory = []; + // b.activeGun = null; + // b.inventoryGun = 0; + // for (let i = 0, len = b.guns.length; i < len; ++i) { + // b.guns[i].have = false; + // if (b.guns[i].ammo !== Infinity) b.guns[i].ammo = 0; + // } + // simulation.makeGunHUD(); //update gun HUD + b.bulletDraw = () => { }; //make bullets invisible + }, + remove() { } + }, + { + name: "re-research", + description: `eject all your ${powerUps.orb.research(1)}`, + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return powerUps.research.count > 3 + }, + requires: "at least 4 research", + effect() { + powerUps.spawnDelay("research", powerUps.research.count); + powerUps.research.count = 0 + }, + remove() { } + }, + { + name: "black hole", + description: `use your energy and ${powerUps.orb.research(4)} to spawn
inside the event horizon of a huge black hole`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return powerUps.research.count > 3 + }, + requires: "at least 4 research", + effect() { + m.energy = 0 + spawn.suckerBoss(m.pos.x, m.pos.y - 700) + powerUps.research.changeRerolls(-4) + simulation.makeTextLog(`m.research --
${powerUps.research.count}`) + }, + remove() { } + }, + { + name: "black hole cluster", + description: `spawn 30 nearby black holes`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + const unit = { + x: 1, + y: 0 + } + for (let i = 0; i < 30; i++) { + const where = Vector.add(m.pos, Vector.mult(Vector.rotate(unit, Math.random() * 2 * Math.PI), 2000 + 1200 * Math.random())) + spawn.sucker(where.x, where.y, 140) + const who = mob[mob.length - 1] + who.locatePlayer() + // who.damageReduction = 0.2 + } + }, + remove() { } + }, + { + name: "rule 30", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return !build.isExperimentSelection + }, + requires: "NOT EXPERIMENT MODE", + effect() { }, + remove() { }, + state: [ + [false, false, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, false, false, false, false, false, true, false, false, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, Math.random() > 0.8, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, false, false, false, false, false] + ], + rule(state, a, b, c) { + //30 + if (state[a] && state[b] && state[c]) return false; // TTT => F + if (state[a] && state[b] && !state[c]) return false; // TTF => F + if (state[a] && !state[b] && state[c]) return false; //TFT => F + if (state[a] && !state[b] && !state[c]) return true; //TFF => T + if (!state[a] && state[b] && state[c]) return true; //FTT => T + if (!state[a] && state[b] && !state[c]) return true; //FTF => T + if (!state[a] && !state[b] && state[c]) return true; //FFT => T + if (!state[a] && !state[b] && !state[c]) return false; //FFF => F + }, + id: 0, + researchSpawned: 0, + descriptionFunction() { + const loop = () => { + if ((simulation.paused || simulation.isChoosing) && m.alive && !build.isExperimentSelection) { //&& (!simulation.isChoosing || this.count === 0) + let b = []; //produce next row + b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 1, 0, 1)); //left edge wrap around + for (let i = 1; i < this.state[this.state.length - 1].length - 1; i++) { //apply rule to the rest of the array + b.push(this.rule(this.state[this.state.length - 1], i - 1, i, i + 1)); + } + b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 2, this.state[this.state.length - 1].length - 1, 0)); //right edge wrap around + this.state.push(b) + if (document.getElementById(`cellular-rule-id${this.id}`)) document.getElementById(`cellular-rule-id${this.id}`).innerHTML = this.outputText() //convert to squares and send HTML + if (this.count && this.researchSpawned < 12 && !(this.state.length % 10)) { + this.researchSpawned++ + powerUps.spawn(m.pos.x - 50 + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "research"); + } + setTimeout(() => { + loop() + }, 300 + 5 * this.state.length); + } + } + setTimeout(() => { + loop() + }, 300); + this.id++ + return `${this.outputText()}` + }, + outputText() { + let text = "
"
+            for (let j = 0; j < this.state.length; j++) {
+                // text += "

" + text += "

" + for (let i = 0; i < this.state[j].length; i++) { + if (this.state[j][i]) { + text += "■" //"☻" //"⬛" //"█" //"■" + } else { + text += " " //"□" //"☺" //"⬜" //"    " //"□" + } + } + text += "

" + } + text += "
" + return text + }, + }, + { + name: "rule 90", + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + allowed() { + return !build.isExperimentSelection + }, + requires: "NOT EXPERIMENT MODE", + effect() { }, + remove() { }, + state: [ + [false, false, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, false, false, false, false, false, true, true, false, false, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, Math.random() > 0.8, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, false, false, false, false] + ], + rule(state, a, b, c) { //90 + if (state[a] && state[b] && state[c]) return false; // TTT => F + if (state[a] && state[b] && !state[c]) return true; // TTF => T + if (state[a] && !state[b] && state[c]) return false; //TFT => F + if (state[a] && !state[b] && !state[c]) return true; //TFF => T + if (!state[a] && state[b] && state[c]) return true; //FTT => T + if (!state[a] && state[b] && !state[c]) return false; //FTF => F + if (!state[a] && !state[b] && state[c]) return true; //FFT => T + if (!state[a] && !state[b] && !state[c]) return false; //FFF => F + }, + id: 90, + researchSpawned: 0, + descriptionFunction() { + const loop = () => { + if ((simulation.paused || simulation.isChoosing) && m.alive && !build.isExperimentSelection) { //&& (!simulation.isChoosing || this.count === 0) + let b = []; //produce next row + b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 1, 0, 1)); //left edge wrap around + for (let i = 1; i < this.state[this.state.length - 1].length - 1; i++) { //apply rule to the rest of the array + b.push(this.rule(this.state[this.state.length - 1], i - 1, i, i + 1)); + } + b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 2, this.state[this.state.length - 1].length - 1, 0)); //right edge wrap around + this.state.push(b) + if (document.getElementById(`cellular-rule-id${this.id}`)) document.getElementById(`cellular-rule-id${this.id}`).innerHTML = this.outputText() //convert to squares and send HTML + if (this.count && this.researchSpawned < 12 && !(this.state.length % 10)) { + this.researchSpawned++ + powerUps.spawn(m.pos.x - 50 + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "research"); + } + setTimeout(() => { + loop() + }, 300 + 5 * this.state.length); + } + } + setTimeout(() => { + loop() + }, 300); + this.id++ + return `${this.outputText()}` + }, + outputText() { + let text = "
"
+            for (let j = 0; j < this.state.length; j++) {
+                // text += "

" + text += "

" + for (let i = 0; i < this.state[j].length; i++) { + if (this.state[j][i]) { + text += "■" //"☻" //"⬛" //"█" //"■" + } else { + text += " " //"□" //"☺" //"⬜" //"    " //"□" + } + } + text += "

" + } + text += "
" + return text + }, + }, + { + name: "cosmogonic myth", + description: `open a portal to a primordial version of reality
in 5 minutes close the portal, spawn 1 of each power up
`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + const urls = ["https://scratch.mit.edu/projects/14005697/fullscreen/", "https://scratch.mit.edu/projects/22573757/fullscreen/", "https://scratch.mit.edu/projects/41429974/fullscreen/", "https://scratch.mit.edu/projects/43690666/fullscreen/", "https://codepen.io/lilgreenland/full/ozXNWZ", "https://codepen.io/lilgreenland/full/wzARJY", "classic/7-1-2017/", "classic/4-15-2018/", "classic/7-11-2019/", "classic/9-8-2019/", "classic/7-15-2020/", "classic/6-1-2021/"] + const choose = urls[Math.floor(Math.random() * urls.length)] + console.log(`opening new tab" ${choose}`) + let tab = window.open(choose, "_blank"); + setTimeout(() => { + tab.close(); + powerUps.spawn(m.pos.x, m.pos.y, "gun"); + setTimeout(() => { + powerUps.spawn(m.pos.x, m.pos.y - 50, "ammo") + }, 250); + setTimeout(() => { + powerUps.spawn(m.pos.x + 50, m.pos.y, "field"); + }, 500); + setTimeout(() => { + powerUps.spawn(m.pos.x + 50, m.pos.y - 50, "heal"); + }, 750); + setTimeout(() => { + powerUps.spawn(m.pos.x - 50, m.pos.y, "tech"); + }, 1000); + setTimeout(() => { + powerUps.spawn(m.pos.x - 50, m.pos.y - 50, "research"); + }, 1250); + }, 1000 * 5 * 60); + }, + remove() { } + }, + { + name: "planetesimals", + description: `play planetesimals (an asteroids-like game)
clear levels in planetesimals to spawn tech
if you die in planetesimals you die in n-gon`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + window.open('../../planetesimals/index.html', '_blank') + // powerUps.spawn(m.pos.x, m.pos.y, "tech"); + + // for communicating to other tabs, like planetesimals + // Connection to a broadcast channel + const bc = new BroadcastChannel('planetesimals'); + bc.activated = false + + bc.onmessage = function (ev) { + if (ev.data === 'tech') powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); + if (ev.data === 'death') { + m.death() + bc.close(); //end session + } + if (ev.data === 'ready' && !bc.activated) { + bc.activated = true //prevents n-gon from activating multiple copies of planetesimals + bc.postMessage("activate"); + } + } + }, + remove() { } + }, + { + name: "tinker", + description: "permanently unlock JUNKtech in experiment mode
this effect is stored for future visits", + maxCount: 1, + count: 0, + frequency: 0, + frequencyDefault: 0, + isJunk: true, + isNonRefundable: true, + allowed() { + return !localSettings.isJunkExperiment + }, + requires: "", + effect() { + localSettings.isJunkExperiment = true + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + }, + remove() { } + }, + { + name: "NFT", + descriptionFunction() { + return `buy your current game seed: ${Math.initialSeed}
no one is allowed to use your seeds
if they use them they are gonna get in trouble

your seeds: ${localSettings.personalSeeds.join(", ")}` + }, + maxCount: 1, + count: 0, + frequency: 0, + isJunk: true, + isNonRefundable: true, + allowed: () => true, + requires: "", + effect() { + localSettings.personalSeeds.push(Math.initialSeed) + if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + }, + remove() { } + }, + // { + // name: "rule 90", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isJunk: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() {}, + // remove() {}, + // state: [ + // [false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false] + // ], + // rule(state, a, b, c) { + // if (state[a] && state[b] && state[c]) return false; // TTT => F + // if (state[a] && state[b] && !state[c]) return true; // TTF => T + // if (state[a] && !state[b] && state[c]) return false; //TFT => F + // if (state[a] && !state[b] && !state[c]) return true; //TFF => T + // if (!state[a] && state[b] && state[c]) return true; //FTT => T + // if (!state[a] && state[b] && !state[c]) return false; //FTF => F + // if (!state[a] && !state[b] && state[c]) return true; //FFT => T + // if (!state[a] && !state[b] && !state[c]) return false; //FFF => F + // }, + // id: 0, + // descriptionFunction() { + // const loop = () => { + // if ((simulation.paused || simulation.isChoosing) && m.alive && !build.isExperimentSelection) { //&& (!simulation.isChoosing || this.count === 0) + // let b = []; //produce next row + // b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 1, 0, 1)); //left edge wrap around + // for (let i = 1; i < this.state[this.state.length - 1].length - 1; i++) { //apply rule to the rest of the array + // b.push(this.rule(this.state[this.state.length - 1], i - 1, i, i + 1)); + // } + // b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 2, this.state[this.state.length - 1].length - 1, 0)); //right edge wrap around + // this.state.push(b) + // if (document.getElementById(`cellular-rule-id${this.id}`)) document.getElementById(`cellular-rule-id${this.id}`).innerHTML = this.outputText() //convert to squares and send HTML + // if (this.count && this.state.length < 120 && !(this.state.length % 10)) powerUps.spawn(m.pos.x - 50 + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "research"); + // setTimeout(() => { loop() }, 400); + // } + // } + // setTimeout(() => { loop() }, 400); + // // if (this.id === 0) { + // // for (let i = 0; i < 29; i++) this.state[0][i] = Math.random() < 0.5 //randomize seed + // // } + // this.id++ + // return `${this.outputText()}` + // }, + // outputText() { + // let text = "" + // for (let j = 0; j < this.state.length; j++) { + // text += "

" + // for (let i = 0; i < this.state[j].length; i++) { + // if (this.state[j][i]) { + // text += "⬛" //"█" //"■" + // } else { + // text += "⬜" //"    " //"□" + // } + // } + // text += "

" + // } + // return text + // }, + // }, + + //************************************************** + //************************************************** undefined / lore + //************************************************** tech + //************************************************** + { + name: `undefined`, + description: `this
 `, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isLore: true, + // isExperimentHide: true, + allowed() { + return !build.isExperimentSelection + }, + requires: "NOT EXPERIMENT MODE", + effect() { + if (localSettings.loreCount > lore.conversation.length - 1) { //reward for people done with lore chapters (or on the final chapter) + for (let i = mob.length - 1; i > -1; i--) { //replace mobs with starters + if (!mob[i].isBoss && mob[i].isDropPowerUp && mob[i].alive) { + spawn.starter(mob[i].position.x, mob[i].position.y) + mob[i].leaveBody = false + mob[i].isDropPowerUp = false + mob[i].death() + + //spawn a random power up + // if (Math.random() < 1 / 5) { + // powerUps.spawn(mob[i].position.x, mob[i].position.y, "research") + // } else + if (Math.random() < 1 / 4) { + powerUps.spawn(mob[i].position.x, mob[i].position.y, "ammo") + } else if (Math.random() < 1 / 3) { + powerUps.spawn(mob[i].position.x, mob[i].position.y, "heal") + } else if (Math.random() < 1 / 2) { + powerUps.spawn(mob[i].position.x, mob[i].position.y, "boost") + } else { + powerUps.spawn(mob[i].position.x, mob[i].position.y, "coupling") + } + } + } + } + + setTimeout(() => { //a short delay, I can't remember why + lore.techCount++ + if (lore.techCount === lore.techGoal) { + // tech.removeLoreTechFromPool(); + this.frequency = 0; + this.description = `null is open at level.final()
 ` + } else { + this.frequency += lore.techGoal * 2 + // for (let i = 0; i < tech.tech.length; i++) { //set name for all unchosen copies of this tech + // if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech[i].description = `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool` + // } + // for (let i = 0, len = 10; i < len; i++) tech.addLoreTechToPool() + this.description = `uncaught error:
${Math.max(0, lore.techGoal - lore.techCount)} more required for access to null` + } + }, 1); + }, + remove() { + lore.techCount = 0; + this.maxCount = lore.techGoal; + this.description = `this
 ` + } + } ], // addLoreTechToPool() { //adds lore tech to tech pool // if (!simulation.isCheating) { diff --git a/style.css b/style.css index 05cee67..5b2150c 100644 --- a/style.css +++ b/style.css @@ -161,7 +161,7 @@ summary { height: 100%; } -#choose-grid { +.choose-grid { position: absolute; top: 50%; left: 50%; @@ -180,9 +180,34 @@ summary { transition: opacity 0.25s linear; overflow: auto; -ms-overflow-style: none; - /* IE and Edge */ scrollbar-width: none; - /* Firefox */ +} + +.choose-grid-no-images { + border-radius: 12px; + border: 10px solid #444; + gap: 10px; + background-color: #444; + /* padding: 10px 1px; */ + + + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + margin: 0px; + z-index: 12; + max-height: 99vh; + font-size: 1.3em; + display: grid; + grid-template-columns: 384px; + align-items: stretch; + visibility: hidden; + opacity: 0; + transition: opacity 0.25s linear; + overflow: auto; + -ms-overflow-style: none; + scrollbar-width: none; } #choose-grid::-webkit-scrollbar { @@ -356,6 +381,7 @@ summary { margin-right: -1px; font-size: 0.92em; min-height: 88px; + /* border-radius: 5px; */ } .cancel-card { @@ -390,19 +416,31 @@ summary { background-color: var(--hover-card-color); } +.research-cancel { + display: flex; + gap: 10px; + line-height: 160%; + /* background-color: var(--card-color); */ + font-size: 1em; +} /* keeps 5 columns at 1440px */ @media (min-width: 1710px) and (max-width: 1950px) { .experiment-grid-module, .choose-grid-module, - .pause-grid-module { + .pause-grid-module, + .research-cancel { line-height: 143%; font-size: 0.68em; } + .research-cancel { + font-size: 0.9em; + } + #experiment-grid, - #choose-grid, + .choose-grid, .pause-grid { grid-template-columns: repeat(auto-fit, 340px); } @@ -426,8 +464,12 @@ summary { font-size: 0.58em; } + .research-cancel { + font-size: 0.8em; + } + #experiment-grid, - #choose-grid, + .choose-grid, .pause-grid { grid-template-columns: repeat(auto-fit, 285px); } @@ -512,7 +554,7 @@ summary { } .experiment-grid-disabled[data-descr]:hover::after { - content: '\a \00a0 \00a0 \00a0 REQUIRES:\a \00a0 \00a0 \00a0 'attr(data-descr); + content: '\a \00a0 \00a0 \00a0 REQUIRES:\a \00a0 \00a0 \00a0 ' attr(data-descr); white-space: pre-wrap; position: absolute; left: 0; @@ -564,7 +606,7 @@ summary { right: 4px; height: 0px; width: 7px; - transition: height 0.15s linear; + transition: height 0.25s linear; opacity: 1; z-index: 2; pointer-events: none; @@ -580,7 +622,7 @@ summary { left: 15px; height: 7px; width: 0px; - transition: width 0.3s linear; + transition: width 0.25s linear; opacity: 1; z-index: 2; pointer-events: none; @@ -588,18 +630,21 @@ summary { border-right: 1.5px solid #777; display: none; } + #health { position: absolute; top: 15px; left: 15px; height: 20px; width: 0px; - transition: width 1s ease-out; z-index: 2; + transition: width 1s ease-out; + z-index: 2; pointer-events: none; background-color: rgb(9, 245, 166); border-right: 2px solid rgb(51, 162, 125); display: none; } + #health-bg { position: absolute; top: 15px; diff --git a/todo.txt b/todo.txt index dd001bb..bb66de3 100644 --- a/todo.txt +++ b/todo.txt @@ -1,13 +1,23 @@ ******************************************************** NEXT PATCH ************************************************** -added defense bar to HUD -new community map commandeer by Desboot +mob damage difficulty setting is lower +recycling now flashes green when it heals + +merged cancel and research bars for single column selection +added some dark grey borders for no images selection mode + +new images with midJourney V5 + spores, pilot wave, standing wave bug fixes *********************************************************** TODO ***************************************************** -switch to prettier formatter or remove beautify +wormhole tech - teleport away mobs with mass below 3 when they get too near the player + short CD, small energy cost, only mobs below a mass + +foam gun could have a knock back effect + maybe with pressure vessel? extend brainstorming animation timers to fps cap? will it be smoother or choppier? @@ -22,59 +32,12 @@ level element - mover, transport extend uncertainty to superballs maybe make aiming them more random? -live updating defense and damage HUD? - makes more sense for defense - for damage many effects only apply to one type of damage so it would show up - update frequency - per s - every cycle? (only if canvas) - don't include difficulty effects - draw in canvas or html? - add border for readability - a number - bars - horizontal - vertical - circles - defense - color: grey, white - scales from 0-1 - wrapped around health bar like armor - what about mass-energy which has no health bar - at defense = 1 outlines health bar fully - health bar - color: keep red (green?) - red is the same as mob health bars - damage - color: red if not used for health - scales from 1-infinity maps to 0->1 - 1-1/(1+damage) -diagetic UI Elements - ammo number? - doesn't text look choppy when camera moves? - health bar could be rendered similarly to energy bar - what about 2 bezier curves on left and right of player head that looks like 1/3 circleRadiusScale - rotate the curves as the head rotates - what about 2 bezier both above player - as the total energy and health increases the curses could asymptotically approach a maximum length as max energy/health goes to infinity - - -foam gun could have a knock back effect - maybe with pressure vessel? - perfect diamagnatism could bounce on mobs, or even map elements? could work like a rocket jump? -field tech: negative mass, wormhole? - tech: do 60% damage when not touching ground - tech: 50% defense when not touching ground - Tech: Von Neuman probes - Drones will consume blocks to replicate themselves it's a little too similar to the drone repair tech, but I kinda like it better. drones that eat blocks and spit out more drones is cool -when gaining ammo have the ammo test quickly count up by Math.floor(1/20x) of the total ammo given - maybe bold, flash the text for a second after - tech: parry - immune to harm for 0.25-0.5 seconds after pressing field button needs a 5 second CD? @@ -95,7 +58,6 @@ tech - after standing wave runs out of energy from blocking, gain a buff aoe damage like railgun push mobs away - level: lock should there be something in the top part of the map? add alt versions of left and right sides @@ -1242,14 +1204,16 @@ if pause is pressed while selecting power ups, display pause menu on top of sele metamaterial cloaking - Scientific photography by Miki Asai, by Bruce Munro molecular assembler - by Laurie Greasley 16-bit Isometric wormhole - by Tim White + pilot wave - nail gun - Screenprint shotgun - blueprint by Dan McPharlin grenades, missiles, explosions - by Victo Ngai - spores - by Ernst Haeckel + spores - turquoise black spores on a white background full color scientific anatomy by Ernst Haeckel drones - tilt-shift photography super balls - By Akari Toriyama wave - sound wave oscilloscope by Paul Catherall, concentric circles by Paul Catherall + Barbara Takenaga's painting depicting a clean sound wave on aoscilloscope device --ar 3:2 --v 5 foam - black blobs Ink doodle harpoon - by Eiichiro Oda mine - by Dan McPharlin