From d7de6777bee7f68ef0df0936c7bfac2fe5725586 Mon Sep 17 00:00:00 2001 From: duckietm Date: Tue, 5 Mar 2024 15:30:27 +0100 Subject: [PATCH] Fix: Avatar Editor --- src/App.scss | 4 +- .../images/avatareditor/avatar_shadow.png | Bin 0 -> 1805 bytes src/assets/images/avatareditor/body.png | Bin 0 -> 2199 bytes src/assets/images/avatareditor/choice_bg.png | Bin 0 -> 2998 bytes .../images/avatareditor/color_frame.png | Bin 0 -> 1661 bytes .../avatareditor/color_frame_active.png | Bin 0 -> 1651 bytes src/assets/images/avatareditor/hc_icon.png | Bin 0 -> 1708 bytes src/assets/images/avatareditor/head.png | Bin 0 -> 2824 bytes src/assets/images/avatareditor/legs.png | Bin 0 -> 2277 bytes src/assets/images/avatareditor/randomize.png | Bin 0 -> 2882 bytes .../avatareditor/randomize_transparent.png | Bin 0 -> 1959 bytes .../images/avatareditor/rotation_arrow.png | Bin 0 -> 2818 bytes src/assets/images/avatareditor/shirts.png | Bin 0 -> 2528 bytes .../images/avatareditor/wardrobe_bg.png | Bin 0 -> 1822 bytes .../images/avatareditor/wardrobe_left.png | Bin 0 -> 1637 bytes .../images/avatareditor/wardrobe_right.png | Bin 0 -> 1635 bytes .../images/avatareditor/wardrobe_user_bg.png | Bin 0 -> 1700 bytes src/assets/images/wardrobe/hd.png | Bin 0 -> 225 bytes src/assets/images/wardrobe/head.png | Bin 0 -> 330 bytes src/assets/images/wardrobe/legs.png | Bin 0 -> 231 bytes src/assets/images/wardrobe/torso.png | Bin 0 -> 247 bytes src/assets/images/wardrobe/wardrobe.png | Bin 0 -> 256 bytes src/common/card/NitroCardView.scss | 2 +- .../layout/LayoutGridColorPickerItem.tsx | 75 ++++ .../avatar-editor/AvatarEditorView.scss | 363 +++++++++++++----- .../avatar-editor/AvatarEditorView.tsx | 162 +++++--- .../views/AvatarEditorFigurePreviewView.tsx | 4 +- .../views/AvatarEditorModelView.tsx | 62 +-- .../views/AvatarEditorWardrobeView.tsx | 80 +++- .../AvatarEditorFigureSetItemView.tsx | 14 +- .../figure-set/AvatarEditorFigureSetView.tsx | 22 +- .../AvatarEditorPaletteSetItemView.tsx | 7 +- .../AvatarEditorPaletteSetView.tsx | 23 +- 33 files changed, 605 insertions(+), 213 deletions(-) create mode 100644 src/assets/images/avatareditor/avatar_shadow.png create mode 100644 src/assets/images/avatareditor/body.png create mode 100644 src/assets/images/avatareditor/choice_bg.png create mode 100644 src/assets/images/avatareditor/color_frame.png create mode 100644 src/assets/images/avatareditor/color_frame_active.png create mode 100644 src/assets/images/avatareditor/hc_icon.png create mode 100644 src/assets/images/avatareditor/head.png create mode 100644 src/assets/images/avatareditor/legs.png create mode 100644 src/assets/images/avatareditor/randomize.png create mode 100644 src/assets/images/avatareditor/randomize_transparent.png create mode 100644 src/assets/images/avatareditor/rotation_arrow.png create mode 100644 src/assets/images/avatareditor/shirts.png create mode 100644 src/assets/images/avatareditor/wardrobe_bg.png create mode 100644 src/assets/images/avatareditor/wardrobe_left.png create mode 100644 src/assets/images/avatareditor/wardrobe_right.png create mode 100644 src/assets/images/avatareditor/wardrobe_user_bg.png create mode 100644 src/assets/images/wardrobe/hd.png create mode 100644 src/assets/images/wardrobe/head.png create mode 100644 src/assets/images/wardrobe/legs.png create mode 100644 src/assets/images/wardrobe/torso.png create mode 100644 src/assets/images/wardrobe/wardrobe.png create mode 100644 src/common/layout/LayoutGridColorPickerItem.tsx diff --git a/src/App.scss b/src/App.scss index eaf0c4c..eff22b0 100644 --- a/src/App.scss +++ b/src/App.scss @@ -21,8 +21,8 @@ $toolbar-height: 55px; $achievement-width: 375px; $achievement-height: 405px; -$avatar-editor-width: 746px; -$avatar-editor-height: 445px; +$avatar-editor-width: 520px; +$avatar-editor-height: 553px; $catalog-width: 650px; $catalog-height: 480px; diff --git a/src/assets/images/avatareditor/avatar_shadow.png b/src/assets/images/avatareditor/avatar_shadow.png new file mode 100644 index 0000000000000000000000000000000000000000..33397f11f98b677b53a5d726700cc6bffdc308a0 GIT binary patch literal 1805 zcmcIlTWB0r7@nqSi8U$cLo_~3CRJK=c5XX6J441AcXL_ntZ}=<7^&v$nKQdXcIJ#T zv)N4^QWIKHilU$o8mWYWXp4Q2VuA%PsEAKqAox-%q7_7>K@(pT>Y2S+qz!p-;LJH^ z<~!g2{g?UYx$*31&%V3%Q54ma8B6ELoF;?m+Dq;~e{g(@Ot*Mrj|CLfdv`Q;WpW2L zC~D7Fc78IPG>&KxJ27D50*cifkDw`P@Nmroa0Z2R0TpdmXRcoSkfCi;XC9Re-tbbW zWRK1J=;(Yl59eo~YBGoKrw3~qA#hL#=$cb@1FfbrbzY6EqsJUW*CFAI&J0Bc=}BXp zPGKL>N=#rO&-1jR#tOWZEFhJ?j~01R-~^EqWL6Y3QPu>RZe9$r=bM(6OOG_Ik)6(z z!qC$=u3D|es!|O5MNUvvmE%QD6j_2`gE=<@HP#IVS`29vK;QO48@qJG2nx6o>I~7^ zaKiE0v~JK06bTtu10E;D_{gai&@i@#I!+rMgu}DMN>lF*iG%!{hqxRHaK(pccowUz8P9r0&WqJ2Ffv!J6zOg8j0cl zo3r}6>zYyvPC8w*Y&Ti5X}4+Q5^A3`;qP;AoFEPS$4q)CUt0X_wTJe9LU*SfccQX> z>+14?+xxyfQ0drn`?+}S^5rh$^h^BLcb=KtSUXnko%-U3TJBV3=`&_v<(+3X6DzMw z1f{O=xQRzzKG>H^J$GUR*sC@0_No-Mcmy^e>B}-Hf(%Df?uB zN{#dkO#Hc+%}%{}C3U({|6cWm8rRb>A-p?`a7!>KO*()VuV^fwouFi-ZY^UKnw*ZQwb bcmCe__}``R_1c9(^ux*wXVd2&oL>3|SPe>u literal 0 HcmV?d00001 diff --git a/src/assets/images/avatareditor/body.png b/src/assets/images/avatareditor/body.png new file mode 100644 index 0000000000000000000000000000000000000000..6f81867d15d2fd3c94892fbde5dee1941fe1ab51 GIT binary patch literal 2199 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 W-@Kjt^-=kQXs>PfpFRKDm45*IeGn7? literal 0 HcmV?d00001 diff --git a/src/assets/images/avatareditor/color_frame_active.png b/src/assets/images/avatareditor/color_frame_active.png new file mode 100644 index 0000000000000000000000000000000000000000..41391525237958d1bddfe0281aa63048a05b24cf GIT binary patch literal 1651 zcmcIlJ&)W(7NLqXT9VGKPBVnJX(@+v7^gnCd+SFa$HWKkGy)i8YjhNM_Hw$lx6#LY z+t?*w^}Kv;>T!aEmPno^h7ih* ztA;RYhSY_x$p$oZO*b^Xp%}VnG(5c_R~O*+auRvH_8XNozVgASEHh8jCX)^_v$RB0B`wY@8QQeKd7PCoOJ%``Lbg--fNNbikz_S(T2z7JA=9QP z({vRIrfA_mp6b zj%mVS1sA5*rm_($SutV<_)%}y-elfdPEpD7K4ar$sy3G_vSF%*d^SKhP6hAFLQ{2w zwoyra!1HD(&{A~R*DVh^uR_CvaDfYWArMreLm(N6quGDrg23yhMTt^OyKNtEEovMS zFT^3m(6AK>&4yyRAyN>rETw^M*RY#*gF5D15kKE%ct^xdoL^KdVVHB&q48YPbzq1i z#feN&sTNXP2*?C`5z7XBcgXWoeSE@h;q_{a=yS6EGj1`r>!9m^C3z~J4kzh zb|0_&aC!g#lvhE;Ba{v)@0c18zG$MeEK;I9oUQrZ16^nd=dDdl>#oWdbzi0_r_OoT zz3^@L7w_1EZoAcQeuV$NcW?ElKWBITF`qgCKD)8-be_Pr6;qG~`k3YTr`>l^xj$C~1 W^!rzzxYiQCh3@)B`v>)9d=QPtOrwL-BXg3szhTa+pksWba!k@naZ7qXd2e*fS9 z_y3rGUS6p_HoxnhU6LftSC-4G{4UnF&hF&@bMJq;M{MHdXHrSJ^L}y7R95f1C`mV; z4QgxIn)if*S(rnF`83xJV~&=j{Rg`-!s|4XecA{jSGn-XNktBbt2}Lb(2Gm787%h` zdZ=Hm;r=?diE{9Oyua&kf{b-J!H;C1*~JXRHX0Lj)}Wl=bk*SrdtEU(UCX4bCqV6#g3|W zI-Oj{$g!lMYPM~wP*-&wa0EzuQHHu8O81Q!$~47E5N834WWk7h*3MjoYaIp=#uM5o z9VLpVOzomr)pAe-H3oX#_Ruh#pwnz=lY1HIy)JQD>%~-Er73GCm@aM7DBCx7M)1To zZYQllqy($9MMDltxiD?wDsHljrA>B&I3}+r(Zo(Ep(tZXjj`6aR4Zc^SvPXJ{Gf+$ z5DDIggr<=REu)OO3a^_EU><0&rWuZDI)=UgbqB&B)Z@KCP==-hGZNH$(?C7XsYGdp zB1|h~SK*>^K|maf_{D-{BS3xK26>1H@R3b`NwI0!wx!j5bD)dcmlk@`J z$0SFNMJ;XyA+UXHfFjQOz}M?O;Ag-vts?aE#2B+ZnFRdwpw_jlLKWeRhDIU5umDKj zDu6seMSyIR0-NgCFzR`#BhEvxQ)fwtc&7v*YEU(f8jAd{ni6ZV#AAf#+z|cD*?APp zLCTBQ8=a6tbYrw?1@fTn9E3%;y9yR%rbHP%4z82E>2wZhJ59<-w<-P*ma=-*K?z-K z@Jvt9Uh0kHr)YC~@za(5e-%GUEpDQyLHPhx6%mdq2FVIoCcz4ZE8pHXd6U)hv z<(tWvrV*!3_z=G3=g+S40e-krUaZ~y){!^9oBtBtUV8Vv*U!3t?f&(S4?o|#K#x3r zdgapB_rlVdQ|42z-1Naq)w`~|cxkKf=IOmZd@pbPw)^el=QrNzEtKcZe0BU;{qo$= z*UsB7Y<)3P{X1J~pLBovxVH8C?6>RZe*AN%HyfY0`pvODmuKX{u?y0bbI)De@yiaU cf9keBP8@jkf&Ru(@ja<5Rm&ee^8Dd{0FzWI`v3p{ literal 0 HcmV?d00001 diff --git a/src/assets/images/avatareditor/head.png b/src/assets/images/avatareditor/head.png new file mode 100644 index 0000000000000000000000000000000000000000..76baf40cea6a975184a147aae950fa781acfc3fc GIT binary patch literal 2824 zcmcImdsGuw9*z){A}A;iby21xSVWUdCJA{!KuC~>h6D%|b=_q$nLvPKVlqfTsR%v@ zuCS=E;!3MQ+(OlgqT(9`!72*AmI7TBJT6k-#TSCI6JFc1r}g;H&N+APojc$Ce&6pg zzcVITWT=DP3_A*i;t(DtjwWZ29Im$G$$R6k5>27_2O4zpReLl&n-ffC8bp&HE?+CYS?BCU+*{Y_$n zJY9z|qA>%WszQjb1RiX?m1=El)11XF-W=fZc6SyG}{~bTZ zUXOXB6NvN}Lg0Eij;D;KN;b*@z$`ipcuNpet+nvZwP+eSf{76V6M|&kU>d}xF(EmV zD_}taCTku93m|A1Dj{n@i4e%AfpMi;mHsJEi9`^tH4umv#lpoxkQ7B%tCa#6VzQV@ z2&1uJUyR09K^z)_DO5BiPsL@!JYN=Dp%~Ido)_b2swIGy^TVa9#8HxCEJgwz7vZ8@ zK13EI!log7Hj9R^m24UhQLr!-#N()7?kJm7uO`19B<0tv7F9};5rcSq9;|?9OqfHe zVsa6hf(xr?C>LQWV2)D3Q^FviLR{uuyK27H^ZCWBml71xl z!!QG`BGM2&7L-6n`V-2F@q6+O$f}Qv|FqQqU&W7v7EMI72^iTx8KA`-hNaOACx-FA zIU9QStEMyxCp(>GIo4#!hp{$|X-R55*@U;-JmXL()H~tgAbFUnwD<}h6YAWswtLS0 zF1d{#(}h>x=@k|05N`C&&CZh-X4O?bF(-w@79IM0rK?N9S_;Ui{QjmS$LI4ZA3xh9 zUus1QXuERe`gdkeUnZl`7w9@@8?d73%vX*($2V+ESR+h$%DvlnmNmQb@}ugU%rlj> zGEY0JWwP`G8+Lv9-e=pX{tV}%(++DK7j%A8DDnnC7r*FCPW)!88#ABAI&OR%B5u!?yg`|L*0}8$rH_XAg=<759n981#YST zvF&<9Q0Fg~cpZKnlyBQxEg(=VRfEq%oe%S?4>_(Mq(o2I7jD}9(+AcM-J1ec^uJkCFJeo_?P>3R zaO(w2-nYj4a(=!qeM`N!(xY%kNqI(5L7cH8dw)}F?mY35$v#bzgtyCsoU=bHvkqD` zuGzqU?a}wwVBZM~z*26e$~D9>cKe1$x7Ps62H-BLGeR%Ieu(fey_^s1?K*B-z2A%9 zduy?@J1R;n5sR*5L|2_7Hz&l0*S^VHK&8(8q5)~Jcedv|Fu5K7j=j;;su(!#_kNC$ zQBmKURYSjdH5G}WTC#2L*c+G#t;_SP? zIfzCsOnE1|THU@R!rjg9NN>+-X~gLhz{LCu>rTjBL;BL+&RB9kCS|gwyy|SRIHOoB zkZwCy9G~eh)w=(Xxca2k`W-i4c3pR*bP~nKF1Z}s)G^QgQNEaJSAOz-5*dxo_IgiB zhr8?69)D9woB7d{o__$rF_|=<$fT2X{|*bBZqmPqvvqFJ?xQ@sd!$g}RM%w0{cby? zW|=|%zhB#3d(<>Hv?z$tg?p2}`o68DHtCzm%}*0clI6OY znt~F-ogcW)%B^6dY{I?e9|W_Sl#3n475AUcEw5d1*51R%ny`(kdEdzXcFN0Eu=ZA+ zfASq|yrk5v`DxIR_lKvR=n7E$vCO_LCqI6Jp=@RFsh!i$@vnCFa`Oz*#~S*&FN>@* z&-mDhR(&R`piUFD5=|5C9Wy6SdVL~v5GZT3pIYZO=VANprvAoF|C*OY`1R)h81?QA z+wB$na?2C@Sf}$hr`x!Mu5iBpXR7Rc?upGir?yr^xrE-@JdxP6+Sw^`q50vW4{J+b YGnn4$I8Q0d^3O{N4~Y~X435wK4|feUSO5S3 literal 0 HcmV?d00001 diff --git a/src/assets/images/avatareditor/legs.png b/src/assets/images/avatareditor/legs.png new file mode 100644 index 0000000000000000000000000000000000000000..59076fd599039d6a06c177049c1d2a8815aa8c8c GIT binary patch literal 2277 zcmcImeN+@xA6`sN;5APsBj@bIjFr%b_;gB&98K^0&@pH~WSK?~l?%b~TlP2yNDMD$mB3tu?0rwA_2VVxSb zhMp|ATUGrWNfs9u>x&I~St%fCmSstbAsL212qF-YR1hSjz=VDVD-7_8=vPHq!Zb$U zlnYf0jyD`CC=NF2xw`620m z0l84&VMY;@)Cv8@1U_=sU#NI{Jr#HodZ7=&0!SDgG39s5svL03H}NC#I^vC=OIILJ zWhGaZz5S`m?q|UmgPy_ew*y|3G~Nd^P2nTZ3RGypQQjDWG7>bEODAy#nxo7K6vI(e zAJmR&K>#Wk7+4lWS7;zmyPdO10ToC*v{@}U5~UYKf#aQkXBo4BV4=xO7+E2SU>Spn zFd8XyA}}*FU`@Tc(0QxO7it02&i9qBAoGYL5+jab_(aysIEf@DFcJo6G!X!@X2NNP z08oi$lhZ)=vt=nF`g(x(udJFX0Ws3F0ENWFM8ZT1EMauHC<4$7;$h7&5gMH|0~j0= zc+Mp&K7cAk^nn6M`lSLK`%_K2?3ER}ETEVhw0cJD$cs!dfRYyqe<3+=et6{-vEH)d z0IyZM1?ROiLjezOi~kV4f$8j{Ep|ghdW+=u!2+^NEd~lqEkKbTKzWfjlOF&@w-!IJ z)c>pE!=dHfKq`Q!fs(l94yiSozQmA!&sp!gzci(OIO=rTGSXzxL!?bZ2~jJk3D=J} zouktYYOq;TbIs+xRiDVC@<#1hd|F)g+sd6Uj~sW89&hbx?Hd16_k@}s?q9jzF}kx- zH+9tHkwXrpm1o$-KAX?HvE|E&x%8&Iw_k{Mm;cXw$DW2uuD#U3<`Vh5<@Bp1C)>_! zdtyt7+CKgIO5Kw6_a580bG^4is~bDMm6#Lp%;NbKHx7>f zAc|frR-~LCo_Z>?>zDQPj!h}|t$V7}`&@AAh4|aXjr$^X-Cbqf%j<)e)ucg7qpD_% zu9|!N!08JwHMX2hdWGHgS<6|vVCLqX?`_tZ4yQcLM#t?xckVelttmUYy7c!@SHqpj zA0KXe|N5}+P91&tN=a6oR95=ZlJ@wQ*OPl58Pk10t=zqQALY8}Twaw@vA@Y94(hnn zvuejw&xWEoankziI9j%h2}m06e(S2(@ot?zzclst6q&%X^hcc%3A;SW^T%{x>R z`1yFKXpXmI(NUY{K>X|}Ei3ljogUjUcv$q6t34yu#qa9w?Arb1H?a$^$@eJTZ+u-F zQ+DL?W1|~}z6I>AjlXV)`?hM8Ex~bOQrE@3b#GQx-(KGh=C6G1STb041YqMo`pMmR zy=iG@>(~#raUXtu=G24j#dU^?8BAr`>^ny^IieD}=Uq}>(|r~lRcjqKaq`-08@|e# zRI<7{>w+uqP`2^CoU*pdLmlso@2EP0=O*ndm-O53EiHT1+yS>bj=Zv@bGxnQ{9xb0 z$NsbC>ZA6v%{hZBPj;>j&Fh$-KkIPR+s^hac`o62jUpZoZJhPvQybf;8y{YMe!)mr zbkM)8>L*OzpB2wU0+Nox1PSyqdM1yvA71>hhX*W0tzwTYB`xJ1klC(>#x# oxO{M0_sRcmHnmGDvaZF*o3K!8R>L?Wo@ z5YZ731VwOI8Wluf+z>@jq1A3dL`0-fQLvA=pf+}eZTigV_V{OVQmL=1?swmP_q}>2 zJAGEKBpO*55eNjLmnYW`KilHRVncoWH|+h%CHQHU%rjI$Aj~$^9y)|WxeEvcy>?MR zh%$t?3PPk}2N;#|F$a}chNB6DUG`03Q>IBplLk{dp1~LOG;hCXdRKxnU6^ z&v-fJAHO;PiH}6sC~1W=uv`V<1Y%4H11fQpL;qW3$;5Do6oAGL9fC;v`B~MV2V6rx>`H0+EYk zN|96oXc=L?G+N0a;aVq>5X+`%C5lO-aLXtvSVo~aP_;=-0eQU7L&f50v_k0~gGZUv z`-jAefH)aO@xv6-XgPwp$6ykr^;9qvncgdlmPd_eiXs#&3KQcn1ul#>9ZD7-W6Gx3f>`Aig}(h-cxqL1ss_j9F4wAMiF{)yH_r3lV3?Gc2* zEH={-MahnI0iO)g86cU>XS2x|jp4))u=xTS2v4!Ck&E!R2aftZt5y|?GqTutW1<*? z3_1!BGDARTklAz)w~WbW&=G`*vm+z`MIeDxE{5?=5s6_TMv+N`B;dbl+@w)bIZuk> zp3}AcjI-lec#0Hw@!}@m5Pxj*+B8?o&c?a(u3Rao(F+Yu?ag>J__bA6;~m=FCYNjJ zjpC>RN{$$fd*YvUt{Hj~W&2YTM;&x$yA5k}RQBoTF_DyYUZ!Z{Bn^$`_6+Ctuvy+* zL%)-DR%@U5L?;HOmY(r%`Uvt$ez-KFDqZl{h*Y4Nu}YAdogVr+1=#N=PBGi(lk&@j zzt`Ua!$LM9Zx$0atbb>*6{$47m$&ofjT95ljI040Rpj!O^0EVHa9HkE8x3Q=PR`Yq z9(_M{_o3(BtqG}mgt3E(&80|NPO|GPw z9Zk9k_OtIdjFeV>XpmGJ^xD%6N;D?(e6#zn2s47x4p7z({A#?W;MjNbPaVtED-IWy zhS<=bEAt=p`c*y7stms!E(qJ)zJBhtiqpH++5Iyq)^O|2$hSZESuK9(ZExN>V*EO% zvMDCdU&5*zJgB?$w(Ek0hfmJu;(kXD@xB!&2I6*_7(fdvYOMDw#yXbuP>M7l&@~Xc zw?1z;bXj)jy2HB3hacEKOjlHIc)lQTs9w8~nedcynJ8ji5 zb+MVlWp^HRx0NYpFWz=*Akb6X8`OO)ws_CWH)ndn&z{W`I>kvY)rF_%2%HbWj~en= z9d~cwc(=w{^I6N(MQY2Z+xz7D1OHC!Ui)IuMn`FZ<#hyySp6fhm9?T>95 zvH8bfS)g`cELpShgnyM}l#~7~rWcIbrCA&^t29Kb3 z&0CjPVwVto?&Z&0#&Q$TYV&zEYtZJu*mnOi$oEE;B4_MA+6v$;RMbCHp0OM=N9y@7zneGd+X&&r+ui)~GK z!W_X;^OI`z*Spck603_%zT9<03&S(7vIq_IZ0o0+Oqq3-3;tCYP#e{s5oKI$T?=g! zrS0h5)ivt1vi9k|zP=Ydz~evMU!S?_RwhDTfXBs$XVVNbwy-+|GiHSd#dYThP6N9xWM-y0lJ gt$$o`%Kuk`vN)-GQI(EL`!CnaeKohjb>p_*02yU)CjbBd literal 0 HcmV?d00001 diff --git a/src/assets/images/avatareditor/randomize_transparent.png b/src/assets/images/avatareditor/randomize_transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..382b9e087cdc0ed82b88fa6de388481d387ab56b GIT binary patch literal 1959 zcmcIle{9rL94{CFhk%U4F+l7(4Fl=*$K7$)hLgQ!)yEQu5kUaw%gr^ zU?drg@CPi&j~K*YGDPKw!$B~ZCH zd7tm+`=j5kt*&~0T;7yC48z7%Rs=)nIRlNNF{98ua%*o1dOTuO)LIxec9JtjVDD_3 zjA0L-Qo=QMjqnmL>8cCJx(Hn<)j((rn>#OM0BIStaS_H8EkN`i{DQz0IY2D-3bbI9 z!MIYf+=L64SB0hJ%Op-F<~@tgP4S38g*L!bYC^O4RDc-d<G)Vq)5#w&N2j{C7Ft0E4qd|j6l@u z?Erza4mqJ3IasDO9j~}`1oHqtvW*W2PA(8X6aEo2~1cTLy;avc~SS1Z-IsfiyvO<|5x#u&`NQj z#UN^+6ydl-IgMs0G1Px^Hn?|JQ_8|or*o#cCW{uiHVrjIZK5W;s(Vfx!$z#A43>s# zntt1QI)7x*nDQN$CtaD*^=5lP?`Lga61*^>cgvKs-NktI?O9FJy!}PZ>Cv6XiVqyA zO-i5NXgVn@XgyQc+;uy2rEPji`<_u}A3Og2#$5)0;CdE!zLb;jwjFH(vb4_fdHJLHN3f5o_}L z!7I(ZFFX;6T)l#Ae}D7uJ*~ahcRc;$m~D%w+QNaKceZz>m6?s3n^!*6@o3?R^xma4 zb*<#K<1MXc7G4|>ukKpca&Fy?iS2I>?Ce>3boHtIeb>@Y{dzufxnsfnGUK~g;nlL* zeK*;3{7z46J`=l9SF~$~s&${6e&DUX3wu`nGW$?ze=O2)eXYCtgmLPx4>3I5RrR;l zJF7Fj_*T<3*f(KI_mi%VzgD|LBU=9KET&@-{qnxu8&(&*y5ISgR+d)<_dK^`)j#h% BvKjyY literal 0 HcmV?d00001 diff --git a/src/assets/images/avatareditor/rotation_arrow.png b/src/assets/images/avatareditor/rotation_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..fba2b87bfe428337eabfdcea55447154c99f8862 GIT binary patch literal 2818 zcmcImc~sN*7A`I*47kCFqBtSo2uNwtQd%OFeRC`#ix$+;G)+qnZBv@E$x{#&K}T={ z@qj2Q-~v2H1z{KjL>M;5g(skZ&r!kW2%_SKD7=(y=DaiG@t>D-lKhh2cfb4HCHExB z*T+*|*I1WEqv?Blx%p9dYw9$au1#HUok-T9ZaPY@Ad*I#yI6B-dHb2apwXt@lm-aY z0{%(_Bjh%yh!Em7QF0}PrqLW6qm(EXimL%3E|DsDU|(4U2uMXda4nk;@s+N4h}3Jd z3isda6M$_F#q33(<8r_u3ZV$(xEckbEnJ~g+BQP5nJ3z{xN+d@7+&sp- zQBOQDM6FgL3`S&Rq)jBthEPcuu)V!K17b3mOge?2lhFz_8bw!-X5$QQIEksGO0|?w z02)SANQA3-Afib@$H3RN;-8%Pt>IFQf(JX9{9K$B|s2+GTt-oGR! z1EQ5U!w)Bka21BTN8k#z*|;+iHess_SII^q6=4iqhRZ1!NeP1|Tq#2cH9>|De-p>V z_Cz#F#8ri(YC;u25VG-7`Hr&yOqLB3u;8PZRH5Ou)My$r!QD_b&I75sG3k&k9fks6 zs%Q|x;x2`l2n3Bn`BX26P&N8xU_vAnM}G;F&qur!q#9LVxVIY*q(s?Br6PpOw8d;; z4wudmGFfz6wvb7;$2lo3)du2V?WxuA_Aj0CUS(JTpZ@Y zOq?#_vYB)?*OpBe3Q;>cgo|ytLKX_KZ0*O{R;i@a*MrLb%&Ji(q8QmiHVO;HEUN2m zxpZ5|-j2@2*f4eBz#dXDMur;Bp%(@Hnw>zghrZLbX+iSW%?dRA7f;(n*K_w;JH`FD-1?WC z%7Yb?1P7iJc4${0;~)Wc&OnC1PwPOEK_fUcR8_e~6_Y(V6-eLTVpwr>S$&-O4SE6D z_d)l#YVQ1fv$O+W9@_10^v?mPZ+?idsJzYc+Ki&ywx2I*SJL8JGg4p|{i~pPj@P`* zxbEgD+@Vj=0ri5m-+W&@B9E8P+SULkR~vYiMm{n=_5+_LtgJfl`0YchSYAo0Q{G4Q zj_(C)o#qHu9vhgu;EF@P$<@98nB3=|kzv%Gd4h`kc&WS9pTJAD)vnrN4 zMLhcmH?+6*_5&|}8axd-mbGO!gzaD7Zq+GI^Z9uDN=dNij3qIYop${7y+!gN|65a+ zq!}R&(dQSDtzG#dw2oa|8fFmJlGI%H;_M*z!h?B7-qy@eB^)_ne(|J7??cb{?4*-| z6ERQ3k3{;zvSxJYw<+tC1l#xKB{n7GZkYmB z-F$3Vy#Ly{=Vtd6yw1*8gYz4nygE2|*?7Qr7liEDU=yEQ)pLJ?YeLnnu!ME~hu#}C z*B!8S(e|Bh`aQAy@%5H|VG3Cq>bY6>jr96}MeYpuGr@;XG_`uIxd#~0T(L*ymRz>*YmBHb^IiY7eAS24b!BI#Tsp=x6`wmf-16EojgeVX{9%&9s>#j76S5-X47jo7_^FaMeIq{BLHS#4u& z|GTS7ptAg&>3`bSy)kuKDC+%6c|I`kRgcRwC^EL23v?t^AOHXW literal 0 HcmV?d00001 diff --git a/src/assets/images/avatareditor/shirts.png b/src/assets/images/avatareditor/shirts.png new file mode 100644 index 0000000000000000000000000000000000000000..ff7724701b1489a20a39c5ccd7ad652be5d4a36c GIT binary patch literal 2528 zcmcImdsGv579LPQ5!Q+bR;vsV3yR5PCJA{!Kp8@muU~)ZS{_X(x{H83F)@ zTdLR{#vPI<0Y+*VC{D@woOvji^CdlP8WK20vM(~;IK!l0F5R5=DhmODkgd>1C;M75*{ONJI zAV#!d${Y10q$!O?odAMTQ&SnKEC#7pKro-rhnNV2Aan{rH>7KgXc}E>ST)TcA`F;b zr8BBXEofszWu(a{q)}QYgV5+^Xtjo^L{TY&(oh`)GnlrZrhyX4>!BLW4BB9fNTIw; z>HSq=Lu|T^fMN&(Y0_gvL<*rbu9|j+V>7lolU_X$DULyen$S=%10@Weaivp|M$(`p zeSQJ1~i>Y0-sw61V z*A#yeW+3IpR8&udDyU4)qP?Ksl5ap$UN3%jrT@Q*pGqyJM70Wn8lVu(77k<^G?Rsa z{XjP|vE5JR<$fm=;uCU9P>+Exy51Z;&@I%LQ`tT z)2*9GncV)?YFhZ*ulF>roxAf!MH*$buG;Zx9l$tg%4j=!_1)Gw`FX z%UpKu#9e-WW9P!n-lDhjPW9s|4&V=Tb$oWfbHukSxVo(;cTd2U2S4gOH$`2)2_1Jl zQ@bj&yq$f9ZNCuA%78sAo~}@$z@( z#M__wcay3(zxddo?16v6UBer#U;oX!tWo>~&aS-HxhVNBUSqXq52992&rn_93M}Oh zPH#N5@I4n~+x1@XhMI2-O7hJcFSZR>-{Rh036DOiKERL88(QDex)baz+Ap?xdcg1J zl-NHw|8zxg{7$THZm{UY=LdaC#*F0!-+vXz^+8u3CCusVUW~viyB@#0;L~rPYLeuZ zpZU4@K-@CZ)flmCc>W)IbQ{;@zV+gtoQ+l8C0&)jcPj$?MqfqRe}lMrk?kAx(Tl6w z_@{gUeDOAYn}<~z-DLWHnfUU@r-Pf7rm!U?qxx%md*|BkxNzq=dr4@n%QM%a?Z_iD zc%{JqzBI3UeYnPB!B%*%eZ$Br_oemDoeTH7`EKjWiaipOj^%VK58aM*P@lfo8aj}F zv&kYW^~j`!AVXu`UWwbk$a8MFzT&U+qosZ(A3{2(vc>rUz2k^Er%-m*e*D~`gF~nL zq-%akjK@-_$?xtGqSvvOIOenGYI5iuWBW_(~i+r_aiI zZ*l9xktc`#+|o@X-m&jmbnVW~`vcbI=gYa^;KRMd;p}$zq`m=bUxHg#R9IGN){7TT p-Q(s<1vy@yJly%J_GRDCPH*S_L$~&affcrYFmc3YQB9aM>pwi5$5a3S literal 0 HcmV?d00001 diff --git a/src/assets/images/avatareditor/wardrobe_bg.png b/src/assets/images/avatareditor/wardrobe_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..83fb613280cbdaeea35f6a79a60b462c7bb4019f GIT binary patch literal 1822 zcmcIlZEVzJ9Pi8|fE$t-QI{BMH*|~i`gXm$HXO^mo8BQEb30`aKkRAy++DY}Pi?#1 zZ4m?^5{z#_Mf8I)nwc0Cl!Q-Md$_0jvBtH{YYBpAOsA5)_`W_kSFgg?jq^(Y-_|&(T^>Pf*c6;~>E6~q z2;%ndjZDAauWXk!WQBl^R2V8+4n`BimX4wWv?1t|D$E&nocev@3`H7xoZ2ZWtl}hK z-bjtPuy3q8qm2z|l1_DOCASo1OkhDDkVVV1J-HaCO1v^&2d^25EJ6ICIMo?AB>R;f zGJ#x3MnfE}u`EkQrI5;IV=9!`%_PtA9K-PpC(=A8^PMQ@eO2?0V7b+upg(e z)^ZS*Q=_%LN}_Pem?Cf(F2n{wRe_4KJk+vkXwUB&!CoqQZ%XWCMjgoXLJtkQ8tfW@ zw%<~9rfW4@XV^7Mk?I-)O=w}5hlO!9S56-J$jhT!#8KO>MdQc`7Xlx-8HCJgsd}m` zBrk+`vP}V+VF$eJfu@QHOadRqDO@+6X2UecWw?kOmSiFPFw4s<=F{(69U zBWs{a$BeP8Dn=rLPO|`TbXX8nS`|2n=Jbdb%Z7z$1OSTEH93o13*b&MERchYW9KOH zUo{D2B3D5=&bbiuGiJw8qzn%iZ?y7-^ucE;E7KrLZ6^aQ==M0J1!aaf`tobzCfQq= z&a!9R7#V^A=l=Tb1g`GK^=@qmWb1V5C7+GHY%1Zygim#+r%L6+H@c?D0ARHzb zH08oD|E*bR?}njNh2u#VENep+Kh(xFv@x}dhw%Jw=l9_O{%1PbnTfqT{`wEczakqF zuYPd7_tH|w(d&I*99q9^BE7IJ@qXsm&JTY%ISs04Hp;IH8~BfU%tEe zPU^}1t!Ku*zPIJzM}05A9po>K$OM|RJw_OlxC1~LBDn^%1}xar#Ev%l_5kDU8XY214M?6uiL zT-)NpXN z?ezG`x9jg}+qgKn_t|fEPt2Xj4xW#^^!J7BJLi{vK8M=xJMr|KU8oEGVCk;zs~OUF+5!W6$00 zU4s-MAtazh^pTJ#sF5gg0udxg5lT7=BsvH!4ZPX)ng}JQZLl=uJKmGiJ-{J9W(Z5}_TF<|z#<{`X3y%QwFdgp8{pjt0 zip(TT#N3=^1)y84D_3U)p$A-QF(0LQsQ>ZxJzYy#sNeD;92Hx9oc2#je)D8!NKX!^ z&-ANrXjf(d5M*2uZI(^)Dwu`(oHu~EdX02#4v`0;zNrjq`_ZnpB}%S!Ov|7cW3A(x zF;2QM_wj3*jcp5AHnKd!wgTG=EKjR19qg4X3HEw#*4AJZ>SHO30HNu0YEB(flp|#M zzK^htY}){YQ61-!%#6I+STgi@Ma#61smL|ONMdm)Lmjj(oXCoXHm~YHLCDaI6v#5M za%u^TqO+k{)}Sl7eFRo&y-y^rhQ|d*d%O~dCFR>kJeM0wV~jRui^Fm<_mok@Cp-gK z1;VVxR52D(RAccJKbq~vnE!eYm8y1qlYoH@8n=epp0PtZdZKy`jmx=c{uXkrv3ZZ~$g&5X92 zScb=4tk`0s(}@$`Z`*CB>n+)Clqq~YWb!zxQpJFgw>#~wPpRQ|fWmcvjNpVAvFmkW zyX~kqki6si3%SYPt!I-| zo0nZcsH*l*rz*{y>GkXM3DH|k=Yn=R=0JKz@)xj5ByvhhzBz(OuTWm-Y4R&_bhh}_ zQvbh-uR}}6Bp-2Tph#ElAk}CVi9!F(*?jM@rnH1Zr&H6W$-<&((>w?25}NS$kACZ5NYt#Pv4fo!yU(R2Du<_Ry_p@Ksk6^I9)BE!JJ9qv8K+FW1 literal 0 HcmV?d00001 diff --git a/src/assets/images/avatareditor/wardrobe_right.png b/src/assets/images/avatareditor/wardrobe_right.png new file mode 100644 index 0000000000000000000000000000000000000000..9ddd734416aff7e8ab4d2327aca563460cfc6344 GIT binary patch literal 1635 zcmcIlO^f727;YCo#*qO%timD~Y7`Z%q^gqsNZ9N!PLFnj8He5m$Ae&{Qt6JUp2{BX4dT z9=x)(d1bTFXl(U+-2prwfbr1AgK)of^Zno9@o?IEBX2Ywf2JJw^#@P?0id6v;Zbqq zz3K=R>pW1t)aP*u=tkq}&OGJfSQeTuM^WOU+h2W+v?y@V>rIb%>7E=%y|YYSKRXzT zvtwZgXy=-Cb?yK`EDNs9<4KY`a~Cam9hjG|4Wum~#j%TaOM}{xcc|^DOlobNVnGPe z+P3bKu;WXcyr?n4s6m-Qo0w6DH67a2stbX=EC`)J_vOkOtXwoMiqtWT*=(lIOkHIo zgW9%j5N0rj0fF<=q~LR$ZEjP1@yd&p>bTJ^J4!5tWWJ*} zLb?d@#xSukr9;|sthQsBFA(MsvV?k23jr_qdSDeq;psY1&vW`oUhqW7e%D1HN{^zz zksV1wLRz>#2q#oo%%M`En2)59CL^T%t7cD4ROYDwVs4i84D8@VFUlc#r_~p7UA|Mz zCXu!%JBN$1+Fc~dG|K>0ucNy}Z#|t$+SynF=>^GO!g3WBGoH!a5kz{8@-prvKj$YG zi(fDG|Eu^av|`MYk%R_npwb9RU%&h8 p^EZF^;PQ`;{e3=-Ke_S5Q=8j=Hg8^exBLzC_Yb;Xy!6(4{{WMv{+|E< literal 0 HcmV?d00001 diff --git a/src/assets/images/avatareditor/wardrobe_user_bg.png b/src/assets/images/avatareditor/wardrobe_user_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..239658cbfa9829aa31961ef54aeb3ce44dbfe24a GIT binary patch literal 1700 zcmcIlU5MON6i)pq?P&WTr7S+BX~l|Wa&z;uo3LZu?ySy~W?i-eu4N0ob8|8ic9I*D z?9R-BSbv~att^5df+9ZnA`~eig6Kl~pl{XJA_~5Ul(Kzjp->R-oj;4T%e)wvG8VU7LPSW?m_aEk}@L#%1hpYJj+s$ zCyTmD5km5$Q>>$4styi%M#c#1nvONyQnBu0%hfG;a40-p>Id#(`T0RKepgbOd7ikM z)^4|pZKKH2hNe4?qamzeta5~!b>p0L)HpjhWGF*M(=f?H7R!Q>)Y)oYQn=QB5>Yau zjk7_ac*(R5Ni@BPL{dYb=WP#-q7gdF=hk?Xf!=!(XSHqu+9G6ZHKj1O2629H7|f?5 z-()py^)mIT1}%sirBQ$o|g1sY(Jc)qv`T9Z(2t1A19*(Pb4IX;ZvsIo z7?`Ri3LLeL3AezpCv6&-6nsVYsT;5~B79Q9h%`V;;)Wvst7ew9Sn4sK*W3{E%-MO| zN|^EHbq8O_33z2-wL-Z!b}pe}+Di%*Z5DC*kHdRZZ#HW)xp8RzBgI`Z>o%(&}-#h-iU7awm Sd?o&Ur0U#!`NGlDZ~Oy>`6CMe literal 0 HcmV?d00001 diff --git a/src/assets/images/wardrobe/hd.png b/src/assets/images/wardrobe/hd.png new file mode 100644 index 0000000000000000000000000000000000000000..307d6f799a28c8df46717d9f9c3f7e474c18f1f2 GIT binary patch literal 225 zcmeAS@N?(olHy`uVBq!ia0vp^qChOh!VDzuu06#K5(w}Kadj{70W!ZGEeDbhXL|m5 z6T3!9yyMjMqd-1WNswPKg9C$q&k7)~($mE;q+(9(xzoHY20X5T{+x?n9ys+z*wTyf zg9jsrxr4yp=?#a~a|EV|rx&Kq?0BKAH1FHbKWVb3pZXpDck#^GgzI}4o=)(H3b)Qa zzIfHwX1{x9{)DHNJ!6?6yJ>yxnsv5sLv`=HZMdDfQ#Q%i=}2=hpXTq+=^IURSQX_u UE|es%1v-Mk)78&qol`;+0Az|+#{d8T literal 0 HcmV?d00001 diff --git a/src/assets/images/wardrobe/head.png b/src/assets/images/wardrobe/head.png new file mode 100644 index 0000000000000000000000000000000000000000..977012d216225b6d6ff49e9841a7ff37cbcf94ad GIT binary patch literal 330 zcmV-Q0k!^#P)y$*sf5WsPKgeE=#e$E_1+)g}=GkPJcN-DDf*-Zy_b?|NNp{T^N&|m&d({r5H zQsi`2uTK?G2bTVJ^`Kv1)(`No6%{da%J<~Ckd&Ksq c*Z<7#2}h($07*qoM6N<$g0|p@-v9sr literal 0 HcmV?d00001 diff --git a/src/assets/images/wardrobe/legs.png b/src/assets/images/wardrobe/legs.png new file mode 100644 index 0000000000000000000000000000000000000000..5978a3af7891061e2070aae69b55a831b8ca0c15 GIT binary patch literal 231 zcmeAS@N?(olHy`uVBq!ia0vp^qChOh!VDzuu06#Kq!a^uLR^8gdx4K_*K#1kq;k4J ze$TA3sNGY`6@&7>9WBpvv)H$4haFIksU*lR*ny$#Rg*1{SM2HH7*a83?s;3T1_K_} z2TkH1cp94)2mE;{cW`G_Ta);P3mYveSo5=!wsyQoYWwi<#02%0UCTrkmHEA1TWEZ{ zdhN2y*Mj_J*X&;}U>%XMv3i2Q0uQr`xfVRy&l!YHemc6!OYYN|KgN#^C;xeSI7a#W bj?;W*;(AX{zGB!2w4TA!)z4*}Q$iB}^-)@= literal 0 HcmV?d00001 diff --git a/src/assets/images/wardrobe/torso.png b/src/assets/images/wardrobe/torso.png new file mode 100644 index 0000000000000000000000000000000000000000..a58918ba26e8d9d2ecfa5f5752dd3c34192584de GIT binary patch literal 247 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Ez!VDyX%bUxAlxToYh^u>n50H60A^=Fft0}m6 z{^&weV;fz&O+Y?VNswPKg9C$q&k7)~-_yl0q+-s|3uk!`EAX%;gvTyeAuVD4+)PlC zVeh@b2G$e*tgrlWIm=cl#BOJMlS4^qv&=%V%|4Hc&pq;N13Kvs=B-YY-5_7^?}_~DWd^S8&n**xPGj(N^>bP0l+XkKJWyIg literal 0 HcmV?d00001 diff --git a/src/assets/images/wardrobe/wardrobe.png b/src/assets/images/wardrobe/wardrobe.png new file mode 100644 index 0000000000000000000000000000000000000000..6913da7491b587a4d035a758cc8aba1e4bcb233c GIT binary patch literal 256 zcmeAS@N?(olHy`uVBq!ia0vp^T0ktv!VDx=uuZ%Sq=W)|LR^8gdw~y-JkcC^ZFBz8 zD9@6!2jqc#rjj7PU}J`+A1gS3yxE>Ejv*CuaxdNGZB^iDdwAVXL0aI8jzXS-@Q(im z?1C%))Pz6r)XZf{6MOa0bG`OC%V~ZS1)7##zx(^M#Mgq~J~LmupSLo?^1Wl??qiY@ zESL_933*%h?)Z6l+S3&hH7j}+1-l8RiUdAuJ~e}1%xsN_No!=p(=!=H@!u|1?KL@h zOD#KN@|3gNJ=ZJ7vd=C#^U3d~YOc+cSN7ST*thi<#iSkQcms4MgQu&X%Q~loCIEW) BW7+@! literal 0 HcmV?d00001 diff --git a/src/common/card/NitroCardView.scss b/src/common/card/NitroCardView.scss index a46b39d..3899272 100644 --- a/src/common/card/NitroCardView.scss +++ b/src/common/card/NitroCardView.scss @@ -1,5 +1,5 @@ $nitro-card-header-height: 33px; -$nitro-card-tabs-height: 33px; +$nitro-card-tabs-height: 42px; .nitro-card { resize: both; diff --git a/src/common/layout/LayoutGridColorPickerItem.tsx b/src/common/layout/LayoutGridColorPickerItem.tsx new file mode 100644 index 0000000..cfd7f68 --- /dev/null +++ b/src/common/layout/LayoutGridColorPickerItem.tsx @@ -0,0 +1,75 @@ +import { FC, useMemo } from 'react'; +import { Base } from '../Base'; +import { Column, ColumnProps } from '../Column'; +import { LayoutItemCountView } from './LayoutItemCountView'; +import { LayoutLimitedEditionStyledNumberView } from './limited-edition'; + +export interface LayoutGridColorPickerItemProps extends ColumnProps +{ + itemImage?: string; + itemColor?: string; + itemActive?: boolean; + itemCount?: number; + itemCountMinimum?: number; + itemUniqueSoldout?: boolean; + itemUniqueNumber?: number; + itemUnseen?: boolean; + itemHighlight?: boolean; + disabled?: boolean; +} + +export const LayoutGridColorPickerItem: FC = props => +{ + const { itemImage = undefined, itemColor = undefined, itemActive = false, itemCount = 1, itemCountMinimum = 1, itemUniqueSoldout = false, itemUniqueNumber = -2, itemUnseen = false, itemHighlight = false, disabled = false, center = true, column = true, style = {}, classNames = [], position = 'relative', overflow = 'hidden', children = null, ...rest } = props; + + const getClassNames = useMemo(() => + { + const newClassNames: string[] = [ 'layout-grid-item', 'color-picker-frame' ]; + + if(itemActive) newClassNames.push('active'); + + if(itemUniqueSoldout || (itemUniqueNumber > 0)) newClassNames.push('unique-item'); + + if(itemUniqueSoldout) newClassNames.push('sold-out'); + + if(itemUnseen) newClassNames.push('unseen'); + + if(itemHighlight) newClassNames.push('has-highlight'); + + if(disabled) newClassNames.push('disabled') + + if(itemImage === null) newClassNames.push('icon', 'loading-icon'); + + if(classNames.length) newClassNames.push(...classNames); + + return newClassNames; + }, [ itemActive, itemUniqueSoldout, itemUniqueNumber, itemUnseen, itemHighlight, disabled, itemImage, classNames ]); + + const getStyle = useMemo(() => + { + let newStyle = { ...style }; + + if(itemImage) newStyle.backgroundImage = `url(${ itemImage })`; + + if(itemColor) newStyle.backgroundColor = itemColor; + + if(Object.keys(style).length) newStyle = { ...newStyle, ...style }; + + return newStyle; + }, [ style, itemImage, itemColor ]); + + return ( + + { (itemCount > itemCountMinimum) && + } + { (itemUniqueNumber > 0) && + <> + +
+ +
+ } + { children } +
+ ); +} diff --git a/src/components/avatar-editor/AvatarEditorView.scss b/src/components/avatar-editor/AvatarEditorView.scss index 15f7fb5..160bd17 100644 --- a/src/components/avatar-editor/AvatarEditorView.scss +++ b/src/components/avatar-editor/AvatarEditorView.scss @@ -6,146 +6,146 @@ height: 21px; background-position: -226px -131px; } - + &.arrow-right-icon { width: 28px; height: 21px; background-position: -226px -162px; } - + &.ca-icon { width: 25px; height: 25px; background-position: -226px -61px; - &.selected { + &.selected, &:hover { width: 25px; height: 25px; background-position: -226px -96px; } } - + &.cc-icon { width: 31px; height: 29px; background-position: -145px -5px; - &.selected { + &.selected, &:hover { width: 31px; height: 29px; background-position: -145px -44px; } } - + &.ch-icon { width: 29px; height: 24px; background-position: -186px -39px; - &.selected { + &.selected, &:hover { width: 29px; height: 24px; background-position: -186px -73px; } } - + &.clear-icon { width: 27px; height: 27px; background-position: -145px -157px; } - + &.cp-icon { width: 30px; height: 24px; background-position: -145px -264px; - &.selected { + &.selected, &:hover { width: 30px; height: 24px; background-position: -186px -5px; } } - + &.ea-icon { width: 35px; height: 16px; background-position: -226px -193px; - &.selected { + &.selected, &:hover { width: 35px; height: 16px; background-position: -226px -219px; } } - + &.fa-icon { width: 27px; height: 20px; background-position: -186px -137px; - &.selected { + &.selected, &:hover { width: 27px; height: 20px; background-position: -186px -107px; } } - + &.female-icon { width: 18px; height: 27px; background-position: -186px -202px; - &.selected { + &.selected, &:hover { width: 18px; height: 27px; background-position: -186px -239px; } } - + &.ha-icon { width: 25px; height: 22px; background-position: -226px -245px; - &.selected { + &.selected, &:hover { width: 25px; height: 22px; background-position: -226px -277px; } } - + &.he-icon { width: 31px; height: 27px; background-position: -145px -83px; - &.selected { + &.selected, &:hover { width: 31px; height: 27px; background-position: -145px -120px; } } - + &.hr-icon { width: 29px; height: 25px; background-position: -145px -194px; - &.selected { + &.selected, &:hover { width: 29px; height: 25px; background-position: -145px -229px; } } - + &.lg-icon { width: 19px; height: 20px; background-position: -303px -45px; - &.selected { + &.selected, &:hover { width: 19px; height: 20px; background-position: -303px -75px; @@ -157,54 +157,54 @@ height: 25px; background-position: -186px -167px; } - + &.male-icon { width: 21px; height: 21px; background-position: -186px -276px; - &.selected { + &.selected, &:hover { width: 21px; height: 21px; background-position: -272px -5px; } } - + &.sellable-icon { width: 17px; height: 15px; background-position: -303px -105px; } - + &.sh-icon { width: 37px; height: 10px; background-position: -303px -5px; - &.selected { + &.selected, &:hover { width: 37px; height: 10px; background-position: -303px -25px; } } - + &.spotlight-icon { width: 130px; height: 305px; background-position: -5px -5px; } - + &.wa-icon { width: 36px; height: 18px; background-position: -226px -5px; - &.selected { + &.selected, &:hover { width: 36px; height: 18px; background-position: -226px -33px; @@ -212,18 +212,64 @@ } } -.nitro-avatar-editor-wardrobe-figure-preview { - background-color: $pale-sky; - overflow: hidden; - z-index: 1; +.saved-outfits-title { + color: #a7a6a2; + font-weight: bold; +} + +.saved-outfit-container { + display: flex; + width: 100% !important; + height: 91.5%; .avatar-image { - position: absolute; - bottom: -15px; - margin: 0 auto; + width: 40px !important; z-index: 4; + transform: scale(0.5); } + .avatar-figure { + margin-top: -46px; + margin-left: -9px; + image-rendering: auto !important; + } + + .nitro-avatar-editor-wardrobe-container { + background-color: #cacaca; + border-radius: 0.3rem; + border: solid 1px #000; + height: 386px; + width: 100%; + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-gap: 10px; + padding: 10px 12px 10px 0; + overflow-y: auto; + } + + .avatar-container { + height: 50px; + border-radius: 0.3rem; + background-color: #a7a6a2; + width: 30px; + } + + .saved-outfit-button { + margin-top: -3px; + background-color: transparent; + border: none; + } +} + + +.nitro-avatar-editor-wardrobe-figure-preview { + border-image-source: url(@/assets/images/avatareditor/wardrobe_user_bg.png); + border-image-slice: 4 4 4 4 fill; + border-image-width: 4px 4px 4px 4px; + background-color: transparent; + overflow: hidden; + z-index: 1; + .avatar-shadow { position: absolute; left: 0; @@ -237,19 +283,6 @@ z-index: 2; } - &:after { - position: absolute; - content: ''; - top: 75%; - bottom: 0; - left: 0; - right: 0; - border-radius: 50%; - background-color: $pale-sky; - box-shadow: 0 0 8px 2px rgba($white,.6); - transform: scale(2); - } - .button-container { position: absolute; bottom: 0; @@ -258,8 +291,11 @@ } .nitro-avatar-editor { - width: $avatar-editor-width; height: $avatar-editor-height; + min-width: $avatar-editor-width; + min-height: $avatar-editor-height; + max-width: $avatar-editor-width; + max-height: $avatar-editor-height; .category-item { height: 40px; @@ -268,7 +304,6 @@ .figure-preview-container { position: relative; height: 100%; - background-color: #b69b83; overflow: hidden; z-index: 1; @@ -279,9 +314,17 @@ padding: 0 10px; display: flex; justify-content: space-between; - bottom: 12px; + bottom: 50px; z-index: 5; - + + .arrow-left { + background-image: url(@/assets/images/avatareditor/rotation_arrow.png); + width: 44px; + height: 29px; + margin-left: auto; + margin-right: auto; + } + .icon { cursor: pointer; } @@ -291,46 +334,194 @@ position: absolute; left: 0; right: 0; - bottom: 50px; + bottom: 125px; margin: 0 auto; z-index: 4; } - .avatar-spotlight { - position: absolute; - top: -10px; - left: 0; - right: 0; - margin: 0 auto; - opacity: 0.3; - pointer-events: none; - z-index: 3; - } - .avatar-shadow { position: absolute; left: 0; right: 0; - bottom: 15px; - width: 70px; - height: 30px; + bottom: 88px; + width: 68px; + height: 34px; margin: 0 auto; - border-radius: 100%; - background-color: rgba(0, 0, 0, 0.20); + background: url(@/assets/images/avatareditor/avatar_shadow.png); z-index: 2; } - - &:after { - position: absolute; - content: ''; - top: 75%; - bottom: 0; - left: 0; - right: 0; - border-radius: 50%; - background-color: #a68d76; - box-shadow: 0 0 8px 2px rgba($white,.6); - transform: scale(2); - } } } + +.nitro-avatar-editor.expanded { + max-width: $avatar-editor-width + 264px; + min-width: $avatar-editor-width + 164px; +} + + .choose-clothing { + width: 320px; + } + + .color-picker-frame { + border-image-source: url(@/assets/images/avatareditor/color_frame.png); + border-image-slice: 6 6 6 6 fill; + border-image-width: 6px 6px 6px 6px; + width: 14px; + height: 21px; + border-radius: 4px; + + &.active { + border-image-source: url(@/assets/images/avatareditor/color_frame_active.png); + height: 21px; + margin-top: -2px; + } + + &:hover { + border-image-source: url(@/assets/images/avatareditor/color_frame_active.png); + height: 21px; + margin-top: -2px; + } + } + + .hc-icon { + background-image: url(@/assets/images/avatareditor/hc_icon.png); + height: 9px; + width: 10px; + bottom: 2px; + left: 2px; + } + + .avatar-editor-tabs { + position: relative; + + .tab { + background-position-x: 0; + background-position-y: 0; + width: 34px; + height: 22px; + } + + .hd { + background: url(@/assets/images/wardrobe/hd.png) no-repeat center; + } + + .head { + background: url(@/assets/images/wardrobe/head.png) no-repeat center; + } + + .torso { + background: url(@/assets/images/wardrobe/torso.png) no-repeat center; + } + + .legs { + background: url(@/assets/images/wardrobe/legs.png) no-repeat center; + } + + .tab-wardrobe { + width: 40px; + height: 28px; + background-size: 38px 28px; + background-image: url(@/assets/images/wardrobe/wardrobe.png); + background-repeat: no-repeat; + background-position: center; + filter: contrast(1.2) brightness(1.05); + } + + + .nav-tabs .nav-link { + position: relative; + border-image-source: url(@/assets/images/boxes/card/tabs_avatareditor.png); + border-image-slice: 7 7 7 7 fill; + border-image-width: 7px 7px 7px 7px; + border-image-outset: 0px 0px 0px 0px; + border-image-repeat: repeat repeat; + margin-bottom: -2px; + margin-left: -2px; + + &:hover { + border-image-source: url(@/assets/images/boxes/card/tabs_active.png); + } + } + + + .nav-tabs .nav-link.active { + border-image-source: url(@/assets/images/boxes/card/tabs_active.png); + } + } + + .randomize-container { + bottom: 95px; + left: 330px; + z-index: 2; + } + + .randomize-icon { + background-image: url(@/assets/images/avatareditor/randomize_transparent.png); + height: 33px; + width: 39px; + + &:hover { + background-image: url(@/assets/images/avatareditor/randomize.png); + } + } + + .avatar-wardrobe { + border-image-source: url(@/assets/images/avatareditor/wardrobe_bg.png); + border-image-slice: 6 6 6 6 fill; + border-image-width: 6px 6px 6px 6px; + } + + .avatar-container { + padding: 3px; + } + + .avatar-parts { + border: none !important; + height: 42px; + width: 42px; + background-position: center; + background-repeat: no-repeat; + border-radius: 2rem !important; + overflow: visible !important; + background-color: transparent; + + &:hover { + box-shadow: 0 0 0 3px #dbdad5 !important; + background-color: #cecdc8 !important; + } + + &:active, + &.part-selected { + box-shadow: 0 0 0 3px #c5c3c0 !important; + background-color: #b1b1b1 !important; + } + } + +.avatar-parts-container { + height: 70%; + padding-left: 10px; +} + +.avatar-color-palette-container { + height: 30%; + width: 100%!important; + padding-left: 10px; +} + +.dual-palette { + display: flex !important; + flex-direction: row !important; +} + +.avatar-editor-palette-set-view { + padding-right: 15px !important; + flex-grow: 1; +} + +.clothing-container { + padding-right: 15px !important; +} + +.action-buttons { + gap: 5px; +} diff --git a/src/components/avatar-editor/AvatarEditorView.tsx b/src/components/avatar-editor/AvatarEditorView.tsx index 6b3766e..fac7986 100644 --- a/src/components/avatar-editor/AvatarEditorView.tsx +++ b/src/components/avatar-editor/AvatarEditorView.tsx @@ -1,8 +1,8 @@ -import { AvatarEditorFigureCategory, FigureSetIdsMessageEvent, GetWardrobeMessageComposer, IAvatarFigureContainer, ILinkEventTracker, UserFigureComposer, UserWardrobePageEvent } from '@nitrots/nitro-renderer'; +import { AvatarEditorFigureCategory, FigureSetIdsMessageEvent, GetWardrobeMessageComposer, IAvatarFigureContainer, ILinkEventTracker, SetClothingChangeDataMessageComposer, UserFigureComposer, UserWardrobePageEvent } from '@nitrots/nitro-renderer'; import { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { FaDice, FaTrash, FaUndo } from 'react-icons/fa'; -import { AddEventLinkTracker, AvatarEditorAction, AvatarEditorUtilities, BodyModel, FigureData, generateRandomFigure, GetAvatarRenderManager, GetClubMemberLevel, GetConfiguration, GetSessionDataManager, HeadModel, IAvatarEditorCategoryModel, LegModel, LocalizeText, RemoveLinkEventTracker, SendMessageComposer, TorsoModel } from '../../api'; -import { Button, ButtonGroup, Column, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common'; +import { AddEventLinkTracker, AvatarEditorAction, AvatarEditorUtilities, BodyModel, FigureData, GetAvatarRenderManager, GetClubMemberLevel, GetConfiguration, GetSessionDataManager, HeadModel, IAvatarEditorCategoryModel, LegModel, LocalizeText, RemoveLinkEventTracker, SendMessageComposer, SetLocalStorage, TorsoModel, generateRandomFigure } from '../../api'; +import { Button, ButtonGroup, Column, Flex, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common'; import { useMessageEvent } from '../../hooks'; import { AvatarEditorFigurePreviewView } from './views/AvatarEditorFigurePreviewView'; import { AvatarEditorModelView } from './views/AvatarEditorModelView'; @@ -26,8 +26,19 @@ export const AvatarEditorView: FC<{}> = props => const [ lastGender, setLastGender ] = useState(null); const [ needsReset, setNeedsReset ] = useState(true); const [ isInitalized, setIsInitalized ] = useState(false); - + const [ genderFootballGate, setGenderFootballGate ] = useState(null); + const [ objectFootballGate, setObjectFootballGate ] = useState(null); + + const DEFAULT_MALE_FOOTBALL_GATE = JSON.parse(window.localStorage.getItem('nitro.look.footballgate.M')) || 'ch-3109-92-1408.lg-3116-82-1408.sh-3115-1408-1408'; + const DEFAULT_FEMALE_FOOTBALL_GATE = JSON.parse(window.localStorage.getItem('nitro.look.footballgate.F')) || 'ch-3112-1408-1408.lg-3116-71-1408.sh-3115-1408-1408'; const maxWardrobeSlots = useMemo(() => GetConfiguration('avatar.wardrobe.max.slots', 10), []); + + const onClose = () => + { + setGenderFootballGate(null); + setObjectFootballGate(null); + setIsVisible(false); + } useMessageEvent(FigureSetIdsMessageEvent, event => { @@ -50,7 +61,7 @@ export const AvatarEditorView: FC<{}> = props => i++; } - + for(let [ index, [ look, gender ] ] of parser.looks.entries()) { const container = GetAvatarRenderManager().createFigureContainer(look); @@ -64,7 +75,7 @@ export const AvatarEditorView: FC<{}> = props => const selectCategory = useCallback((name: string) => { if(!categories) return; - + setActiveCategory(categories.get(name)); }, [ categories ]); @@ -72,13 +83,21 @@ export const AvatarEditorView: FC<{}> = props => { const categories = new Map(); - categories.set(AvatarEditorFigureCategory.GENERIC, new BodyModel()); - categories.set(AvatarEditorFigureCategory.HEAD, new HeadModel()); - categories.set(AvatarEditorFigureCategory.TORSO, new TorsoModel()); - categories.set(AvatarEditorFigureCategory.LEGS, new LegModel()); + if (!genderFootballGate) + { + categories.set(AvatarEditorFigureCategory.GENERIC, new BodyModel()); + categories.set(AvatarEditorFigureCategory.HEAD, new HeadModel()); + categories.set(AvatarEditorFigureCategory.TORSO, new TorsoModel()); + categories.set(AvatarEditorFigureCategory.LEGS, new LegModel()); + } + else + { + categories.set(AvatarEditorFigureCategory.TORSO, new TorsoModel()); + categories.set(AvatarEditorFigureCategory.LEGS, new LegModel()); + } setCategories(categories); - }, []); + }, [ genderFootballGate ]); const setupFigures = useCallback(() => { @@ -135,11 +154,12 @@ export const AvatarEditorView: FC<{}> = props => resetCategories(); return; case AvatarEditorAction.ACTION_SAVE: - SendMessageComposer(new UserFigureComposer(figureData.gender, figureData.getFigureString())); - setIsVisible(false); + !genderFootballGate ? SendMessageComposer(new UserFigureComposer(figureData.gender, figureData.getFigureString())) : SendMessageComposer(new SetClothingChangeDataMessageComposer(objectFootballGate, genderFootballGate, figureData.getFigureString())); + SetLocalStorage(`nitro.look.footballgate.${ genderFootballGate }`, figureData.getFigureString()); + onClose(); return; } - }, [ figureData, lastFigure, lastGender, figureSetIds, loadAvatarInEditor, resetCategories ]) + }, [ loadAvatarInEditor, figureData, resetCategories, lastFigure, lastGender, figureSetIds, genderFootballGate, objectFootballGate ]) const setGender = useCallback((gender: string) => { @@ -154,9 +174,12 @@ export const AvatarEditorView: FC<{}> = props => linkReceived: (url: string) => { const parts = url.split('/'); - + + setGenderFootballGate(parts[2] ? parts[2] : null); + setObjectFootballGate(parts[3] ? Number(parts[3]) : null); + if(parts.length < 2) return; - + switch(parts[1]) { case 'show': @@ -185,25 +208,15 @@ export const AvatarEditorView: FC<{}> = props => useEffect(() => { - if(!isWardrobeVisible) return; - - setActiveCategory(null); SendMessageComposer(new GetWardrobeMessageComposer()); - }, [ isWardrobeVisible ]); - - useEffect(() => - { - if(!activeCategory) return; - - setIsWardrobeVisible(false); - }, [ activeCategory ]); + }, []); useEffect(() => { if(!categories) return; - selectCategory(AvatarEditorFigureCategory.GENERIC); - }, [ categories, selectCategory ]); + selectCategory(!genderFootballGate ? AvatarEditorFigureCategory.GENERIC : AvatarEditorFigureCategory.TORSO); + }, [ categories, genderFootballGate, selectCategory ]); useEffect(() => { @@ -248,9 +261,22 @@ export const AvatarEditorView: FC<{}> = props => { if(!isVisible || !isInitalized || !needsReset) return; - loadAvatarInEditor(GetSessionDataManager().figure, GetSessionDataManager().gender); + if (!genderFootballGate) loadAvatarInEditor(GetSessionDataManager().figure, GetSessionDataManager().gender); + if (genderFootballGate) loadAvatarInEditor(genderFootballGate === FigureData.MALE ? DEFAULT_MALE_FOOTBALL_GATE : DEFAULT_FEMALE_FOOTBALL_GATE, genderFootballGate); setNeedsReset(false); - }, [ isVisible, isInitalized, needsReset, loadAvatarInEditor ]); + }, [ isVisible, isInitalized, needsReset, loadAvatarInEditor, genderFootballGate, DEFAULT_MALE_FOOTBALL_GATE, DEFAULT_FEMALE_FOOTBALL_GATE ]); + + useEffect(() => // This is so when you have the look editor open and you change the mode to Boy or Girl + { + if(!isVisible) return; + + return () => + { + setupFigures(); + setIsWardrobeVisible(false); + setNeedsReset(true); + } + }, [ isVisible, genderFootballGate, setupFigures ]); useEffect(() => { @@ -264,50 +290,64 @@ export const AvatarEditorView: FC<{}> = props => if(!isVisible || !figureData) return null; + const avatarEditorClasses = `nitro-avatar-editor no-resize ${ isWardrobeVisible ? 'expanded' : '' }`; + return ( - - setIsVisible(false) } /> - + + + { categories && (categories.size > 0) && Array.from(categories.keys()).map(category => { const isActive = (activeCategory && (activeCategory.name === category)); return ( selectCategory(category) }> - { LocalizeText(`avatareditor.category.${ category }`) } +
); }) } - setIsWardrobeVisible(true) }> - { LocalizeText('avatareditor.category.wardrobe') } - + { (!genderFootballGate) && + setIsWardrobeVisible(!isWardrobeVisible) }> +
+
+ }
- - { (activeCategory && !isWardrobeVisible) && - } - { isWardrobeVisible && - } + + { (activeCategory) && + + } - - - - - - - - - - + + + + + + { (!genderFootballGate) && + + + + + + } + + + + { isWardrobeVisible && + + + + } + diff --git a/src/components/avatar-editor/views/AvatarEditorFigurePreviewView.tsx b/src/components/avatar-editor/views/AvatarEditorFigurePreviewView.tsx index d5715ac..8bde241 100644 --- a/src/components/avatar-editor/views/AvatarEditorFigurePreviewView.tsx +++ b/src/components/avatar-editor/views/AvatarEditorFigurePreviewView.tsx @@ -44,11 +44,9 @@ export const AvatarEditorFigurePreviewView: FC - - rotateFigure(figureData.direction + 1) } /> - rotateFigure(figureData.direction - 1) } /> + rotateFigure(figureData.direction + 1) } /> ); diff --git a/src/components/avatar-editor/views/AvatarEditorModelView.tsx b/src/components/avatar-editor/views/AvatarEditorModelView.tsx index 6eb8fe3..e88512e 100644 --- a/src/components/avatar-editor/views/AvatarEditorModelView.tsx +++ b/src/components/avatar-editor/views/AvatarEditorModelView.tsx @@ -1,9 +1,12 @@ import { Dispatch, FC, SetStateAction, useCallback, useEffect, useState } from 'react'; -import { CategoryData, FigureData, IAvatarEditorCategoryModel } from '../../../api'; -import { Column, Flex, Grid } from '../../../common'; +import { CategoryData, FigureData, IAvatarEditorCategoryModel, LocalizeText } from '../../../api'; +import { Column, Flex, Grid, Text } from '../../../common'; import { AvatarEditorIcon } from './AvatarEditorIcon'; import { AvatarEditorFigureSetView } from './figure-set/AvatarEditorFigureSetView'; import { AvatarEditorPaletteSetView } from './palette-set/AvatarEditorPaletteSetView'; + +const CATEGORY_FOOTBALL_GATE = [ 'ch', 'cp', 'lg', 'sh' ]; + export interface AvatarEditorModelViewProps { model: IAvatarEditorCategoryModel; @@ -13,7 +16,7 @@ export interface AvatarEditorModelViewProps export const AvatarEditorModelView: FC = props => { - const { model = null, gender = null, setGender = null } = props; + const { model = null, gender = null, isFromFootballGate = false, setGender = null } = props; const [ activeCategory, setActiveCategory ] = useState(null); const [ maxPaletteCount, setMaxPaletteCount ] = useState(1); @@ -53,35 +56,48 @@ export const AvatarEditorModelView: FC = props => return ( - - { model.canSetGender && + + + { model.canSetGender && <> - setGender(FigureData.MALE) }> + setGender(FigureData.MALE) }> + { LocalizeText('avatareditor.generic.boy') } - setGender(FigureData.FEMALE) }> + setGender(FigureData.FEMALE) }> + { LocalizeText('avatareditor.generic.girl') } } - { !model.canSetGender && model.categories && (model.categories.size > 0) && Array.from(model.categories.keys()).map(name => - { - const category = model.categories.get(name); + { !model.canSetGender && model.categories && (model.categories.size > 0) && Array.from(model.categories.keys()).map(name => + { + const category = model.categories.get(name); - return ( - selectCategory(name) }> - - - ); - }) } - - - - - - { (maxPaletteCount >= 1) && + return ( +
+ selectCategory(name) }> + { (isFromFootballGate && CATEGORY_FOOTBALL_GATE.includes(category.name)) && + + } + { (!isFromFootballGate) && + + } + +
+ ); + }) } + + + + + + { (maxPaletteCount >= 1) && } - { (maxPaletteCount === 2) && + { (maxPaletteCount === 2) && } +
); diff --git a/src/components/avatar-editor/views/AvatarEditorWardrobeView.tsx b/src/components/avatar-editor/views/AvatarEditorWardrobeView.tsx index 9811ab8..4bd3f1b 100644 --- a/src/components/avatar-editor/views/AvatarEditorWardrobeView.tsx +++ b/src/components/avatar-editor/views/AvatarEditorWardrobeView.tsx @@ -1,8 +1,8 @@ -import { IAvatarFigureContainer, SaveWardrobeOutfitMessageComposer } from '@nitrots/nitro-renderer'; +import { HabboClubLevelEnum, IAvatarFigureContainer, SaveWardrobeOutfitMessageComposer } from '@nitrots/nitro-renderer'; import { Dispatch, FC, SetStateAction, useCallback, useMemo } from 'react'; -import { FigureData, GetAvatarRenderManager, GetClubMemberLevel, GetConfiguration, LocalizeText, SendMessageComposer } from '../../../api'; -import { AutoGrid, Base, Button, Flex, LayoutAvatarImageView, LayoutCurrencyIcon, LayoutGridItem } from '../../../common'; - +import { MdKeyboardArrowLeft, MdKeyboardArrowRight } from 'react-icons/md'; +import { CreateLinkEvent, FigureData, GetAvatarRenderManager, GetClubMemberLevel, GetConfiguration, GetSessionDataManager, LocalizeText, SendMessageComposer } from '../../../api'; +import { Flex, LayoutAvatarImageView, LayoutCurrencyIcon } from '../../../common'; export interface AvatarEditorWardrobeViewProps { figureData: FigureData; @@ -30,6 +30,8 @@ export const AvatarEditorWardrobeView: FC = props { if(!figureData || (index >= savedFigures.length) || (index < 0)) return; + if (GetSessionDataManager().clubLevel === HabboClubLevelEnum.NO_CLUB) return CreateLinkEvent('habboUI/open/hccenter'); + const newFigures = [ ...savedFigures ]; const figure = figureData.getFigureString(); @@ -41,6 +43,22 @@ export const AvatarEditorWardrobeView: FC = props SendMessageComposer(new SaveWardrobeOutfitMessageComposer((index + 1), figure, gender)); }, [ figureData, savedFigures, setSavedFigures ]); + const getClubLevel = useCallback(() => + { + let highestClubLevel = 0; + + savedFigures.forEach(([ figureContainer, gender ]) => + { + if (figureContainer) + { + const clubLevel = GetAvatarRenderManager().getFigureClubLevel(figureContainer, gender); + highestClubLevel = Math.max(highestClubLevel, clubLevel); + } + }); + + return highestClubLevel; + }, [ savedFigures ]); + const figures = useMemo(() => { if(!savedFigures || !savedFigures.length) return []; @@ -54,26 +72,52 @@ export const AvatarEditorWardrobeView: FC = props if(figureContainer) clubLevel = GetAvatarRenderManager().getFigureClubLevel(figureContainer, gender); items.push( - - { figureContainer && - } - - { !hcDisabled && (clubLevel > 0) && } - - - { figureContainer && - } + + + + { figureContainer && ( + + ) } - +
+ { figureContainer && ( + + ) } +
+ ); }); return items; - }, [ savedFigures, hcDisabled, saveFigureAtWardrobeIndex, wearFigureAtIndex ]); + }, [ savedFigures, saveFigureAtWardrobeIndex, wearFigureAtIndex ]); return ( - - { figures } - +
+
+ + { LocalizeText('avatareditor.wardrobe.title') } + + + { !hcDisabled && getClubLevel() > 0 && ( + + ) } + +
+
+
{ figures }
+
+
); + } diff --git a/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetItemView.tsx b/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetItemView.tsx index fd28dc5..e960413 100644 --- a/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetItemView.tsx +++ b/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetItemView.tsx @@ -25,11 +25,13 @@ export const AvatarEditorFigureSetItemView: FC - { !hcDisabled && partItem.isHC && } - { partItem.isClear && } - { partItem.isSellable && } - { children } - +
+ + { !hcDisabled && partItem.isHC && } + { partItem.isClear && } + { partItem.isSellable && } + { children } + +
); } diff --git a/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetView.tsx b/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetView.tsx index 3755731..f6eb6b1 100644 --- a/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetView.tsx +++ b/src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetView.tsx @@ -1,8 +1,14 @@ +import { HabboClubLevelEnum } from '@nitrots/nitro-renderer'; import { Dispatch, FC, SetStateAction, useCallback, useEffect, useRef } from 'react'; -import { AvatarEditorGridPartItem, CategoryData, IAvatarEditorCategoryModel } from '../../../../api'; +import { AvatarEditorGridPartItem, CategoryData, CreateLinkEvent, GetSessionDataManager, IAvatarEditorCategoryModel } from '../../../../api'; import { AutoGrid } from '../../../../common'; import { AvatarEditorFigureSetItemView } from './AvatarEditorFigureSetItemView'; +const TSHIRT_FOOTBALL_GATE = [ 3111, 3110, 3109, 3030, 3114, 266, 265, 262, 3113, 3112, 691, 690, 667 ]; +const NUMBER_BEHIND_FOOTBALL_GATE = [ 3128, 3127, 3126, 3125, 3124, 3123, 3122, 3121, 3120, 3119 ]; +const PANTS_FOOTBALL_GATE = [ 3116, 281, 275, 715, 700, 696, 3006 ]; +const SHOES_FOOTBALL_GATE = [ 3115, 3068, 906 ]; + export interface AvatarEditorFigureSetViewProps { model: IAvatarEditorCategoryModel; @@ -12,7 +18,7 @@ export interface AvatarEditorFigureSetViewProps export const AvatarEditorFigureSetView: FC = props => { - const { model = null, category = null, setMaxPaletteCount = null } = props; + const { model = null, category = null, isFromFootballGate = false, setMaxPaletteCount = null } = props; const elementRef = useRef(null); const selectPart = useCallback((item: AvatarEditorGridPartItem) => @@ -21,6 +27,8 @@ export const AvatarEditorFigureSetView: FC = pro if(index === -1) return; + if (item.isHC && GetSessionDataManager().clubLevel === HabboClubLevelEnum.NO_CLUB) return CreateLinkEvent('habboUI/open/hccenter'); + model.selectPart(category.name, index); const partItem = category.getCurrentPart(); @@ -36,9 +44,11 @@ export const AvatarEditorFigureSetView: FC = pro }, [ model, category ]); return ( - - { (category.parts.length > 0) && category.parts.map((item, index) => - selectPart(item) } />) } - + + { (category.parts.length > 0) && category.parts.map(item => + (!isFromFootballGate || (isFromFootballGate && TSHIRT_FOOTBALL_GATE.includes(item.id) || NUMBER_BEHIND_FOOTBALL_GATE.includes(item.id) || PANTS_FOOTBALL_GATE.includes(item.id) || SHOES_FOOTBALL_GATE.includes(item.id))) && + selectPart(item) } />) + } + ); } diff --git a/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetItemView.tsx b/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetItemView.tsx index 638a9d1..610995d 100644 --- a/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetItemView.tsx +++ b/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetItemView.tsx @@ -1,6 +1,7 @@ import { FC, useEffect, useState } from 'react'; import { AvatarEditorGridColorItem, GetConfiguration } from '../../../../api'; import { LayoutCurrencyIcon, LayoutGridItem, LayoutGridItemProps } from '../../../../common'; +import { LayoutGridColorPickerItem } from '../../../../common/layout/LayoutGridColorPickerItem'; export interface AvatarEditorPaletteSetItemProps extends LayoutGridItemProps { @@ -24,9 +25,9 @@ export const AvatarEditorPaletteSetItem: FC = p }, [ colorItem ]); return ( - - { !hcDisabled && colorItem.isHC && } + + { !hcDisabled && colorItem.isHC && } { children } - + ); } diff --git a/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.tsx b/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.tsx index c55dcb4..3fe0ff9 100644 --- a/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.tsx +++ b/src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.tsx @@ -1,5 +1,6 @@ +import { HabboClubLevelEnum } from '@nitrots/nitro-renderer'; import { FC, useCallback, useEffect, useRef } from 'react'; -import { AvatarEditorGridColorItem, CategoryData, IAvatarEditorCategoryModel } from '../../../../api'; +import { AvatarEditorGridColorItem, CategoryData, CreateLinkEvent, GetSessionDataManager, IAvatarEditorCategoryModel } from '../../../../api'; import { AutoGrid } from '../../../../common'; import { AvatarEditorPaletteSetItem } from './AvatarEditorPaletteSetItemView'; @@ -21,6 +22,8 @@ export const AvatarEditorPaletteSetView: FC = p const index = paletteSet.indexOf(item); if(index === -1) return; + + if (item.isHC && GetSessionDataManager().clubLevel === HabboClubLevelEnum.NO_CLUB) return CreateLinkEvent('habboUI/open/hccenter'); model.selectColor(category.name, index, paletteIndex); }, [ model, category, paletteSet, paletteIndex ]); @@ -33,9 +36,21 @@ export const AvatarEditorPaletteSetView: FC = p }, [ model, category ]); return ( - - { (paletteSet.length > 0) && paletteSet.map((item, index) => - selectColor(item) } />) } + + { paletteSet.length > 0 && + paletteSet.map((item, index) => ( + selectColor(item) } + /> + )) } ); }