From 8fbe4532fc03d921f8a27123590de1f5d96f66be Mon Sep 17 00:00:00 2001 From: hero Date: Fri, 23 Aug 2024 03:39:04 +0200 Subject: [PATCH] completely reworked menu --- README.md | 2 +- example-mod/src/mod.cpp | 12 +- resource/arrow-left-solid.png | Bin 0 -> 391 bytes resource/arrows-up-down-left-right-solid.png | Bin 0 -> 717 bytes resource/gear-solid.png | Bin 0 -> 724 bytes resource/icon.png | Bin 0 -> 1804 bytes resource/list-solid.png | Bin 0 -> 283 bytes resource/resource.h | 349 +++++++++++++++++ src/core/huds/huds.h | 2 + src/core/menu/framework.cpp | 377 +++++++++++++------ src/core/menu/framework.h | 42 ++- src/core/menu/gui.cpp | 84 +++-- src/core/shared/menu.cpp | 7 +- src/core/shared/render.cpp | 126 +++++-- src/photon.vcxproj | 1 + src/sdk/menu.h | 3 +- src/sdk/photon.h | 1 + src/sdk/render.h | 8 +- src/util/util.cpp | 6 + src/util/util.h | 1 + tests/photon.tests.vcxproj | 1 + 21 files changed, 843 insertions(+), 179 deletions(-) create mode 100644 resource/arrow-left-solid.png create mode 100644 resource/arrows-up-down-left-right-solid.png create mode 100644 resource/gear-solid.png create mode 100644 resource/icon.png create mode 100644 resource/list-solid.png create mode 100644 resource/resource.h diff --git a/README.md b/README.md index a8c0972..6d753b5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Photon [![CI](https://github.com/hero622/photon/actions/workflows/CI.yml/badge.svg)](https://github.com/hero622/photon/actions/workflows/CI.yml) [![C++](https://img.shields.io/badge/language-C%2B%2B-f34b7d)](https://en.wikipedia.org/wiki/C%2B%2B) [![Portal 2](https://img.shields.io/badge/game-Portal%202-blue)](https://store.steampowered.com/app/620/Portal_2/) [![Platform](https://img.shields.io/badge/platform-Windows%20%26%20Linux-green)](https://en.wikipedia.org/wiki/Cross-platform_software) +# Photon [![CI](https://github.com/hero622/photon/actions/workflows/CI.yml/badge.svg)](https://github.com/hero622/photon/actions/workflows/CI.yml) [![C++](https://img.shields.io/badge/language-C%2B%2B-f34b7d)](https://en.wikipedia.org/wiki/C%2B%2B) [![Portal 2](https://img.shields.io/badge/game-Portal%202-blue)](https://store.steampowered.com/app/620/Portal_2/) [![Platform](https://img.shields.io/badge/platform-Windows%20%26%20Linux-green)](https://en.wikipedia.org/wiki/Cross-platform_software) **Photon** is a mod loader for Portal 2. diff --git a/example-mod/src/mod.cpp b/example-mod/src/mod.cpp index 8079ff4..7a0fb02 100644 --- a/example-mod/src/mod.cpp +++ b/example-mod/src/mod.cpp @@ -55,14 +55,15 @@ void c_photon_mod::on_event( const char* msg ) { photon_api::mod_info_t c_photon_mod::get_info( ) { photon_api::mod_info_t info; - info.name = "example mod"; + info.name = "Example Mod"; + info.author = "hero"; info.version = "0.0.1"; return info; } void c_photon_mod::paint_menu( ) { - static bool example_checkbox_val; - photon->menu->checkbox( example_checkbox_val, "example checkbox" ); + static bool example_toggle_val; + photon->menu->toggle( example_toggle_val, "example toggle" ); static int example_slider_val; photon->menu->slider( example_slider_val, 0, 100, "example slider" ); @@ -70,6 +71,11 @@ void c_photon_mod::paint_menu( ) { static float example_sliderf_val; photon->menu->sliderf( example_sliderf_val, 0.f, 10.f, "example sliderf" ); + static color_t example_colorpicker_val; + photon->menu->colorpicker( example_colorpicker_val, "example colorpicker" ); + + photon->menu->separator( "example separator" ); + static std::size_t example_combo_val; const char* example_combo_items[] = { "value 1", "value 2", "value 3" }; photon->menu->combo( example_combo_val, example_combo_items, ARRAY_LEN( example_combo_items ), "example combo" ); diff --git a/resource/arrow-left-solid.png b/resource/arrow-left-solid.png new file mode 100644 index 0000000000000000000000000000000000000000..5a8cd970afe9a35df8bebf17e581e672fe5fb5e1 GIT binary patch literal 391 zcmV;20eJq2P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0USw0K~z{r?U%7m z!cY{3d)ZhKN7(S4^bvH{CWOI}FyQ2C7!zLrUO*>1!ft>EfSB@~+tUPUYa-#a4);sG z_V)H9|DUvJ(kSp((lounH=N>yZr1~W4|vFn-m-_VILka@jU64agOIXtVWuN?5K4nbulHdx=!31P(&uX?LK l2)#!6HG9IYZU;^ziuU55cD;cvQ>Op`002ovPDHLkV1krRpw|EZ literal 0 HcmV?d00001 diff --git a/resource/arrows-up-down-left-right-solid.png b/resource/arrows-up-down-left-right-solid.png new file mode 100644 index 0000000000000000000000000000000000000000..014a4f4e0a62c8442ca4ad40a7489c57b7e84e39 GIT binary patch literal 717 zcmV;;0y6!HP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0%A!-K~z{r#h2So z8!-@uvw#EwwNfOgcmo~?)TUAczG7YaLlJVPXJkNCf{x%D;Ij z)yJ+9`nynm_KB$m`3dJnvus;Zz6D!WT_aom@8O*IX4NJ23aEkuNGJIq!pyxBj>smsm~l{F+L7+00C{ZGZASVa{g86%l_->9U(P9HF!Ebzr7Wee za(zqTRl2t{=Slh`dHoC+z(|?xs=RPU%wSyQ--Ut|_^b?DK!C)wwFLwxTd-V~QofeI z1cc-TApt}n#ewy|AdCZ+049)gCa-N7yg^(_Yu2PjHXP;W04tIp8%HzuLrmg4EFs72 znUZ}ejtQ2{TRLxBymr{~wg>WcYWYbtI$kz^OIH%d=mfH}y!i@jT6Kc#;TA>BUR2a@ z>g55dYdio@oqM@Ky#+B^qQA(BHP#U^@mN_B9;x#jaYG_2Cb$FeP{-Vi<*g=tm*TUZ z)9z3Tx2U^ybHl8p_*R2wOX11V0P!<*3u79ufI}Shd8D_0x@tyEU(5NQck`*Hj+=a# zq<4MW$ib{nvu>RSPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0%=J^K~z{r#gwx#ygjxm-^VJk0CNd(NDDoH_G0QfakXm+%FC#vN1fOPsZ|gM&%lU>R>Q zv()~>&Bt*=GnJsRaZ8V4z}i?&<1rr=qOmhRj3e~{E?QefOinSpz6rd8*YG?JcJ$xJ zZ49AB`4Fe<+QWDOuj5^uP=6N;3}b$QE#m^NyT8)-iMR2c`)dy^)M1-;4?1tS;d;1f z(>C0eq2nygD|D~C26pgVmSs!rML36bO)fOoWd#|({sirNJg$yJep}n?wN1T&P5iE& z1RmowYR^l>i@FaYmQw~&w{>_e}E%+ z$cJ5Mt2KRCiE_%erS3iC!yNkGpcY6K!L6nopIS=yBBVpxa7CjL^miK)j9~u63ZY-A z)vmWD6^DL#9?dzTPw^tr;Hm3MM-%rlxp>0d>;LOG-bNkA%WfO$ zT&9^s&G^RsY0?{lxmwk zKoy)JRJh(lSA5bAq6%CUx+-{2slO-eCLyj`dK9T=!82>Ch{@R{^#NA!j}HskKz&o9 zNqvAX_|b=jd|-8dwfJD*e>{t-oIc^EsaS{1jHN}=X#4|Qh)t0000Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!2kdb!2!6DYwZ942BAqrK~!i%?U`9@ zR8<(qzdK8}neKLB)ppth8lehx!>7CBZow@%1^-fc7XQs>C!H~>9`Q>BIJw509&Uept zk&H6RC?iXx>-ueAyYchd6@q959U#{zN{eiUO^e|CBQSNK0KCAmbV)I+*U|t68fFsQ1R)b9{)QP~vmSZl{FQTnt*d;5~ zj64TQ0aqag!X(+y0U-HVRy-=UyLC4i?0AzsV_lmvbjOw zZ?a)xaPuqzo4}TEDBR$9yXlzH@@;=aEAfh|4{EAoSOU3Y6W(IqfHjGJT{e=Mo=JGrIZ<+21MU-0|7mwa->weuP!+24K zY#|x7`((uCmk}8gA2R5(du2-TO2|H-PdUB1*az_|?8bs(AZ+UjU?w$93ws=jt>IkIe*I(T18x4&LA{`?D_ox68L6># zD7A7ar&`9`D?}}*kg&Tb8qISBv|Q(8DBCs>bt-V$j)r)fs{nL_tA_)yQ^romhm7@~ zLGFfm3mgN-QO=ERj!=(n@RefQ%oYcOFgUJud`^V`Gh2>m*Xa^zZ2v^}z0-f*$~v-IEAp z64o-I5597|nC*<>3^t`Gdt8dbO;cK$8l794?_4FSynb_)FW@GM{u-5443_0@72 zeRKl`!@i$2vXoHsW#lEmJ%)|(`pRATstA4YNW(yQLS|s}C7}`qf)UsN!%I`_`&2Ku z+Tu4c_RXXIPWV*M!gxxS5nf+;+vCM2>Gd^TGWz7DjXv5|cR7oac9P7{2b^Pfg@_7PO# zwL50LCYIG;n* zVx5jDUis`YzWTM`V+8WdbO|&Qh%q;w1iY?t@8C1@95QES_HX_>(Qi7!w#?6GEA~9* ubh7$9>v2W<0KxDxY(^Pnl+iUMk$(W-3z3pOx1y5(0000!lvI6;>1s;*b3=DjSL74G){)!Z!;08|@$B+p3WQmUrf?mdd{{N5X@Gx|);Hj6C zl)UeF(}XpFok90KL(ocxKm147nbZ{K@s_x-ws12#l*Tx8NgrA)QNudxf3M)H&$k_T z7ENT-UTd X|Akc(OC_cP-Ok|Y>gTe~DWM4fXWdud literal 0 HcmV?d00001 diff --git a/resource/resource.h b/resource/resource.h new file mode 100644 index 0000000..807b9ca --- /dev/null +++ b/resource/resource.h @@ -0,0 +1,349 @@ +#pragma once + +namespace resource { + namespace icons { + unsigned char photon[ 1804 ] = { + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x1E, 0x3F, 0x88, 0xB1, 0x00, 0x00, 0x00, + 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xAE, 0xCE, 0x1C, 0xE9, 0x00, 0x00, + 0x00, 0x04, 0x67, 0x41, 0x4D, 0x41, 0x00, 0x00, 0xB1, 0x8F, 0x0B, 0xFC, + 0x61, 0x05, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, + 0x0E, 0xC1, 0x00, 0x00, 0x0E, 0xC1, 0x01, 0xB8, 0x91, 0x6B, 0xED, 0x00, + 0x00, 0x06, 0xA1, 0x49, 0x44, 0x41, 0x54, 0x68, 0x43, 0xED, 0x99, 0x59, + 0x6C, 0x54, 0x55, 0x18, 0xC7, 0xBF, 0x3B, 0x4B, 0xB7, 0x99, 0xEE, 0x76, + 0x61, 0xD5, 0x76, 0xDA, 0x04, 0x1A, 0xA1, 0x0A, 0x75, 0xC3, 0x98, 0xA2, + 0x44, 0xC1, 0x80, 0x88, 0x4A, 0xD0, 0x28, 0x31, 0x6A, 0x5C, 0x82, 0x09, + 0xA1, 0x2A, 0x1A, 0x63, 0x7C, 0xF0, 0xC9, 0x07, 0x31, 0x46, 0x13, 0xDF, + 0x0C, 0xD1, 0x17, 0x13, 0x03, 0x51, 0x94, 0x58, 0xAC, 0xF6, 0x85, 0xF8, + 0x62, 0x14, 0x41, 0x10, 0x4B, 0x6B, 0x17, 0xB5, 0x2D, 0x52, 0x3A, 0x74, + 0x9F, 0xE9, 0x9D, 0xCE, 0xCC, 0x9D, 0xB9, 0xFE, 0xFF, 0xF5, 0x4E, 0x53, + 0x6F, 0x67, 0xA6, 0xCB, 0xDC, 0xC1, 0x90, 0xCC, 0x3F, 0xF9, 0xE5, 0x63, + 0xCE, 0x3D, 0x3D, 0xE7, 0xFB, 0xCE, 0x77, 0xCF, 0x76, 0x91, 0x8C, 0x32, + 0xCA, 0x28, 0x23, 0x4B, 0xA4, 0xEB, 0xFA, 0x6D, 0x60, 0xBB, 0xF1, 0xF3, + 0xDA, 0x15, 0x82, 0x68, 0x05, 0x1D, 0x20, 0xD7, 0x28, 0x4A, 0x8B, 0x6C, + 0x86, 0x4D, 0x8B, 0xE0, 0xFC, 0x23, 0x30, 0x75, 0xA0, 0x00, 0xBC, 0xC0, + 0xB2, 0x74, 0x49, 0x31, 0xAC, 0xE5, 0x42, 0x10, 0x74, 0xFE, 0x6B, 0xE0, + 0x03, 0x41, 0x70, 0x03, 0xD8, 0xA5, 0x28, 0x4A, 0x1F, 0xAC, 0xE5, 0x4A, + 0x67, 0x46, 0xF6, 0x01, 0x37, 0xF8, 0x19, 0x9C, 0x01, 0x01, 0xF0, 0x0A, + 0x48, 0x8B, 0xD2, 0x12, 0x08, 0xB2, 0x51, 0x03, 0xF3, 0x04, 0x38, 0x0D, + 0x7E, 0x35, 0x60, 0x40, 0xF7, 0xE1, 0xD9, 0xED, 0xB0, 0x96, 0x2B, 0x5D, + 0x19, 0x39, 0x08, 0xC6, 0x41, 0x2C, 0x08, 0x72, 0x1E, 0xF0, 0xB5, 0x7A, + 0x15, 0x58, 0x2E, 0xCB, 0x03, 0xC1, 0x88, 0x6F, 0x86, 0x69, 0x04, 0xA7, + 0xC0, 0x6F, 0xE0, 0x2F, 0xC0, 0x00, 0x2E, 0x00, 0x66, 0x65, 0x2D, 0xEA, + 0xEC, 0x81, 0xB5, 0x54, 0x96, 0x06, 0x02, 0x07, 0x9D, 0x30, 0xAF, 0x81, + 0x1E, 0xC0, 0x0C, 0x74, 0x60, 0x72, 0x87, 0x41, 0x14, 0xFF, 0xEE, 0x04, + 0x6D, 0x06, 0xFB, 0x51, 0x37, 0x1F, 0xD6, 0x32, 0x59, 0x9D, 0x91, 0xBD, + 0x60, 0x35, 0xE0, 0xDC, 0x60, 0x36, 0x06, 0xC1, 0xB4, 0x10, 0xCC, 0x10, + 0x0C, 0xCB, 0x38, 0xF1, 0xB9, 0x08, 0x70, 0x31, 0xB0, 0x4C, 0x96, 0x05, + 0x82, 0x11, 0x2E, 0x87, 0xA1, 0x73, 0xE7, 0x00, 0x1D, 0xEE, 0x32, 0x32, + 0x31, 0x5B, 0x7F, 0x02, 0x66, 0x84, 0xC1, 0xEC, 0xC5, 0xDF, 0x78, 0x58, + 0x68, 0x85, 0xAC, 0xCC, 0xC8, 0x01, 0xC0, 0x7D, 0xE9, 0x17, 0xD0, 0x86, + 0x20, 0xC6, 0x58, 0x38, 0x5B, 0x28, 0xE3, 0x9E, 0xC2, 0xB9, 0xC2, 0x60, + 0x27, 0x80, 0x65, 0xCB, 0xB1, 0x25, 0x81, 0x60, 0x64, 0xD7, 0xC3, 0xEC, + 0x02, 0x9C, 0xE0, 0x1C, 0x71, 0x8E, 0x7C, 0x22, 0xF5, 0x03, 0xD6, 0x61, + 0xDD, 0xBB, 0xF1, 0xB7, 0x77, 0xB1, 0x30, 0x55, 0x59, 0x95, 0x11, 0x4E, + 0xF0, 0xCB, 0x80, 0xCB, 0xEC, 0x05, 0x8C, 0x3C, 0x37, 0xBF, 0xB8, 0xC2, + 0x33, 0xEE, 0xF2, 0x1D, 0x80, 0xAF, 0x1F, 0x03, 0x3E, 0x88, 0x60, 0x52, + 0xF6, 0x23, 0xE5, 0x06, 0xE0, 0xC4, 0x0E, 0x98, 0x8D, 0x80, 0x4B, 0x2B, + 0x47, 0xFA, 0x6F, 0x30, 0x9F, 0x18, 0x34, 0xEB, 0x72, 0x51, 0xE0, 0xE6, + 0xF9, 0x38, 0x48, 0x49, 0x29, 0x05, 0x82, 0x20, 0xF2, 0x60, 0x5E, 0x06, + 0xED, 0x80, 0x23, 0xCC, 0xE5, 0x56, 0x83, 0x4D, 0x2A, 0xD4, 0x89, 0xC0, + 0xC4, 0x96, 0x63, 0x2E, 0xD3, 0x2F, 0xA2, 0xAD, 0x62, 0xD8, 0x25, 0x2B, + 0xD5, 0x8C, 0x3C, 0x07, 0x4A, 0x01, 0x57, 0x21, 0x06, 0xC2, 0x25, 0x76, + 0x41, 0x42, 0x30, 0x23, 0x30, 0x9C, 0xF4, 0xCC, 0x4A, 0x56, 0x34, 0x12, + 0xDD, 0xCF, 0xF2, 0x85, 0xA8, 0xBA, 0xB2, 0x7F, 0xCE, 0x61, 0x77, 0xC9, + 0xA7, 0x5F, 0x8C, 0x20, 0xF7, 0x8B, 0xAF, 0x00, 0x57, 0xA9, 0x2F, 0x41, + 0x6B, 0xB2, 0xB9, 0x41, 0xE1, 0x6F, 0xB2, 0x61, 0x62, 0x70, 0xF3, 0x2C, + 0x02, 0x9B, 0xC0, 0x96, 0x70, 0x28, 0xDC, 0xE0, 0x78, 0xBB, 0xE9, 0x98, + 0xD2, 0xF2, 0x6D, 0x7F, 0x44, 0x2B, 0xF1, 0x8A, 0xAA, 0x0F, 0x6A, 0xAA, + 0x63, 0x58, 0x02, 0xB9, 0x63, 0x13, 0xDE, 0x62, 0xDF, 0x80, 0x38, 0xA6, + 0xEA, 0xE5, 0xC8, 0x14, 0xEA, 0xC6, 0x55, 0x2A, 0x81, 0xBC, 0x0F, 0xD3, + 0x00, 0x3E, 0x07, 0x27, 0x01, 0x37, 0xBF, 0x12, 0x70, 0x1D, 0xE0, 0x9E, + 0x52, 0x06, 0x98, 0x2D, 0x3A, 0xEB, 0x02, 0x59, 0x80, 0xFD, 0x85, 0x01, + 0x03, 0xE6, 0xF2, 0x3B, 0xFC, 0x6F, 0x99, 0xEE, 0x09, 0x45, 0x94, 0x5B, + 0x1D, 0x17, 0x8F, 0xDE, 0x6C, 0xEB, 0x7E, 0xDD, 0x26, 0xC3, 0x78, 0x51, + 0x86, 0x6C, 0x21, 0x19, 0xB5, 0xFB, 0x64, 0xC4, 0xE6, 0x97, 0x11, 0x19, + 0x13, 0x1F, 0x32, 0xE8, 0xB3, 0x7B, 0x65, 0x4A, 0xF1, 0x4A, 0xC8, 0x3E, + 0xA0, 0x4F, 0x39, 0xBD, 0xBA, 0xD8, 0x07, 0xF1, 0x22, 0x5F, 0x51, 0x42, + 0x8E, 0xD1, 0x05, 0x07, 0x02, 0xC7, 0x0B, 0x61, 0x56, 0x80, 0x95, 0x80, + 0x27, 0xD8, 0x26, 0xA0, 0x02, 0x6E, 0x7A, 0x39, 0x80, 0xCF, 0xE9, 0xAC, + 0x59, 0x7E, 0xC0, 0x03, 0x24, 0xF7, 0x95, 0x51, 0xC0, 0x57, 0x8A, 0xF0, + 0x35, 0x24, 0x28, 0xD3, 0xCB, 0xB4, 0xB0, 0xBE, 0x4E, 0x71, 0x04, 0xEF, + 0xF8, 0x5E, 0x79, 0x6F, 0xC8, 0x2B, 0x2D, 0xFE, 0x1A, 0x99, 0x2C, 0xAA, + 0x8C, 0x04, 0xF3, 0x0B, 0xD4, 0xA8, 0x3B, 0xCF, 0xA7, 0xE7, 0x22, 0xA4, + 0x2C, 0x19, 0x8A, 0x3A, 0x11, 0xBA, 0x22, 0xDE, 0xE9, 0x16, 0x34, 0x19, + 0xB1, 0x07, 0x65, 0x4C, 0x0F, 0xC8, 0xB8, 0x43, 0x8D, 0x1B, 0x08, 0x9C, + 0xA6, 0x63, 0x1C, 0x51, 0x3A, 0xBD, 0x1C, 0x70, 0x54, 0xE9, 0x30, 0x47, + 0x70, 0x00, 0x1C, 0x02, 0x9C, 0xD4, 0xCC, 0x04, 0x37, 0x38, 0xBE, 0x26, + 0xAC, 0xB7, 0x0C, 0x54, 0x1A, 0x30, 0x33, 0x3C, 0x4F, 0xF1, 0x99, 0x0E, + 0xB8, 0xEC, 0xC6, 0x82, 0x62, 0x20, 0x6C, 0xEB, 0x0A, 0x60, 0x26, 0x35, + 0x4D, 0x0B, 0x7B, 0x1C, 0x8A, 0xAD, 0xE1, 0x5C, 0xD0, 0x7E, 0xFD, 0x63, + 0xE7, 0x03, 0x6D, 0xFE, 0xD2, 0x41, 0xBD, 0xA0, 0xAC, 0x2B, 0x5C, 0x9E, + 0xDF, 0x19, 0xA9, 0xB1, 0x75, 0x28, 0xB5, 0x72, 0x29, 0xA7, 0x5A, 0xC6, + 0xDC, 0x2B, 0x44, 0x2D, 0xA8, 0x90, 0xB0, 0xBB, 0x28, 0x1A, 0xCD, 0x73, + 0x05, 0xB4, 0x5C, 0xE7, 0x64, 0x28, 0xDB, 0x36, 0x11, 0x75, 0x2A, 0x70, + 0xDA, 0x8E, 0x86, 0x78, 0x9B, 0xAB, 0x00, 0x74, 0x84, 0xAB, 0x87, 0x03, + 0xB0, 0x53, 0xC6, 0x4E, 0xC7, 0x87, 0x8C, 0xF5, 0x9F, 0x41, 0xEE, 0x86, + 0x79, 0x0B, 0x1C, 0x03, 0xC7, 0x51, 0xCE, 0x8D, 0x6D, 0x8E, 0x50, 0x8F, + 0x6D, 0xF0, 0x95, 0x62, 0xDB, 0xCC, 0x16, 0x07, 0x83, 0x30, 0x38, 0xAE, + 0x76, 0x7C, 0x4E, 0x85, 0x00, 0x33, 0x5B, 0x8E, 0x68, 0xD7, 0xE9, 0x12, + 0x69, 0x6C, 0xFF, 0x44, 0xED, 0x69, 0x3D, 0xAC, 0xF6, 0x9D, 0xD9, 0x54, + 0x91, 0xDF, 0x53, 0x2B, 0xAE, 0x91, 0xD5, 0x92, 0xED, 0x2F, 0x57, 0x6D, + 0x91, 0x52, 0x6F, 0xD0, 0x51, 0x7C, 0x51, 0x2D, 0x70, 0x75, 0x86, 0x96, + 0x29, 0x5D, 0x52, 0x25, 0x97, 0xB2, 0xAA, 0xC5, 0x97, 0xE7, 0x11, 0xD5, + 0xCD, 0x40, 0xEE, 0x41, 0x23, 0x5C, 0x0E, 0xF9, 0xCE, 0x72, 0x84, 0x46, + 0xE0, 0x1C, 0x1B, 0x9E, 0x23, 0xD4, 0x65, 0x90, 0xCD, 0x80, 0xBB, 0x33, + 0x03, 0xF9, 0xCE, 0x58, 0x7D, 0x96, 0x2C, 0x23, 0x60, 0x0E, 0x66, 0x29, + 0xF2, 0xB6, 0x25, 0xA2, 0xE8, 0xDB, 0xED, 0x97, 0x7D, 0xB7, 0xC8, 0xFD, + 0x9F, 0x1E, 0x95, 0xB6, 0xB0, 0x6B, 0x22, 0xAF, 0x32, 0xE7, 0xF7, 0x92, + 0x2A, 0xED, 0xA7, 0x1A, 0x4F, 0xF4, 0x54, 0x5D, 0x49, 0x6E, 0x5F, 0x95, + 0x94, 0x8C, 0xAE, 0x94, 0x7C, 0xB5, 0x5C, 0x9C, 0x81, 0xB2, 0x89, 0x60, + 0xA8, 0x68, 0x78, 0x52, 0x0A, 0x7A, 0xFD, 0x8B, 0x9A, 0xEC, 0xE8, 0xF4, + 0x4D, 0x98, 0x87, 0xC0, 0x11, 0xD0, 0x8C, 0x20, 0xB8, 0xE4, 0x5A, 0x26, + 0xB4, 0xCF, 0x0F, 0x15, 0xFC, 0x74, 0xB4, 0x1B, 0x23, 0x7B, 0x62, 0x4C, + 0x51, 0xDE, 0x29, 0x5D, 0xFD, 0xC6, 0x7A, 0x09, 0x38, 0x6F, 0x92, 0x50, + 0xF6, 0x5A, 0xD1, 0x0A, 0x8B, 0xC7, 0xA5, 0x4C, 0xEB, 0x2A, 0xF6, 0xF8, + 0x7F, 0xF4, 0x54, 0x6B, 0xA7, 0xD7, 0x14, 0xB9, 0xFB, 0x3C, 0x52, 0xEA, + 0x5B, 0x2E, 0xC5, 0x8B, 0x99, 0xEC, 0x6B, 0x60, 0x8E, 0x82, 0x1F, 0x00, + 0x97, 0xDD, 0x93, 0x08, 0x64, 0x12, 0xD6, 0x32, 0xA1, 0x0F, 0xBE, 0x72, + 0xBC, 0x98, 0xED, 0x8C, 0xE2, 0x0C, 0x36, 0xA5, 0x28, 0x7B, 0x5C, 0x8A, + 0xC2, 0x4D, 0x53, 0x5A, 0x9A, 0x1A, 0x9D, 0x5B, 0x9B, 0xEF, 0x5C, 0x25, + 0xAA, 0xBD, 0x5E, 0x54, 0xC7, 0x46, 0x89, 0xE6, 0xAC, 0x92, 0xA0, 0x3B, + 0xCB, 0x67, 0x5F, 0x31, 0xDE, 0x51, 0xE8, 0xF1, 0x2D, 0x26, 0x90, 0xC3, + 0x30, 0x3C, 0x4E, 0x30, 0x1B, 0x27, 0x10, 0x44, 0xB2, 0x83, 0xE1, 0x92, + 0x85, 0x7E, 0xF8, 0xB5, 0x65, 0x3A, 0x2B, 0xA0, 0x17, 0xFD, 0x3C, 0xC5, + 0x72, 0xB3, 0x26, 0x36, 0x3D, 0x5F, 0xE2, 0x1A, 0x28, 0xAC, 0xB3, 0x05, + 0x72, 0x1A, 0x90, 0xAD, 0x1B, 0x8D, 0xE2, 0xE4, 0x42, 0xE3, 0xF7, 0x82, + 0x76, 0xF0, 0x2E, 0xD8, 0x0C, 0xB8, 0x12, 0xA5, 0x45, 0x6C, 0x1B, 0x34, + 0x82, 0x43, 0x80, 0x7D, 0x6E, 0x35, 0x1E, 0x25, 0xD4, 0xD9, 0x7D, 0x07, + 0xB2, 0xE6, 0xCD, 0x08, 0x1A, 0xE2, 0x2E, 0x7C, 0x1C, 0x70, 0x57, 0xFD, + 0x02, 0xB4, 0x60, 0x94, 0x66, 0x6E, 0x7E, 0xE9, 0x10, 0xFA, 0xE4, 0x0A, + 0xBA, 0x0D, 0x3C, 0x0C, 0xF8, 0x85, 0x72, 0x07, 0xFA, 0xE4, 0xEA, 0x96, + 0x50, 0x0B, 0x39, 0x6B, 0x31, 0xB5, 0xDC, 0x23, 0x62, 0xE7, 0x29, 0xAE, + 0x6C, 0xE9, 0x16, 0xFB, 0xE0, 0xDC, 0x60, 0x9F, 0xDC, 0x12, 0x9E, 0x06, + 0x49, 0x95, 0x34, 0x10, 0x8C, 0x0C, 0x1B, 0xE1, 0xC1, 0xF0, 0x2C, 0x60, + 0x10, 0xDD, 0x18, 0x19, 0xF3, 0xF5, 0xD5, 0x72, 0x19, 0x7D, 0x74, 0x03, + 0x06, 0xC3, 0xBE, 0x9F, 0x35, 0xB2, 0x94, 0x50, 0xF3, 0x65, 0xE4, 0x25, + 0xC0, 0x3D, 0x86, 0x8D, 0xF1, 0xFA, 0xCA, 0x5D, 0xF9, 0xAA, 0x08, 0x7D, + 0xF1, 0x48, 0xC3, 0x40, 0x78, 0x42, 0x66, 0x60, 0xF4, 0x25, 0xA1, 0x12, + 0x06, 0x82, 0x11, 0xD8, 0x00, 0xC3, 0x4B, 0x53, 0xEC, 0xFB, 0x54, 0x2F, + 0xB8, 0xDA, 0xE2, 0x37, 0x31, 0xF6, 0xCD, 0xA3, 0xFE, 0x4E, 0xF8, 0x54, + 0xCF, 0xC2, 0x78, 0x4A, 0x96, 0x11, 0x5E, 0x5F, 0x79, 0xDB, 0xE3, 0xC5, + 0x27, 0xE9, 0xF5, 0x35, 0x5D, 0x32, 0xFA, 0xE4, 0xA5, 0x8D, 0x3E, 0xF0, + 0xA8, 0x44, 0x9F, 0xE2, 0x2A, 0x6E, 0x20, 0x88, 0x9C, 0xAB, 0x05, 0x3F, + 0x28, 0xF0, 0xFA, 0x1A, 0x6B, 0xE4, 0xFF, 0x12, 0x07, 0x93, 0x59, 0xA1, + 0x2F, 0x1B, 0xE0, 0xDB, 0x03, 0x2C, 0x34, 0x6B, 0x4E, 0x20, 0xA8, 0xC8, + 0x43, 0x1D, 0x3F, 0xED, 0xC4, 0xAE, 0xA1, 0x3D, 0x18, 0x99, 0x79, 0xAF, + 0xAF, 0xE9, 0x92, 0xD1, 0x77, 0x17, 0xA0, 0x2F, 0xCC, 0x4E, 0x13, 0x7C, + 0x9C, 0xF3, 0x9F, 0x46, 0x73, 0xF6, 0x11, 0x54, 0xE2, 0xB7, 0x26, 0x7E, + 0x31, 0xE4, 0x31, 0x84, 0xF3, 0x83, 0xDF, 0x6D, 0xCD, 0x6B, 0x38, 0x8F, + 0xE5, 0xF1, 0x94, 0xA8, 0xDC, 0xAC, 0x64, 0xF5, 0xCC, 0xCF, 0xF8, 0x9B, + 0xD7, 0x0A, 0xEE, 0xF8, 0xBC, 0x07, 0x3D, 0x08, 0x3E, 0x42, 0x80, 0x1F, + 0xC2, 0xCE, 0xE8, 0x3F, 0x81, 0x20, 0x88, 0x2A, 0x98, 0x6F, 0x00, 0x8F, + 0x1F, 0x4C, 0x27, 0xEF, 0x0C, 0x5C, 0x31, 0xCC, 0x01, 0xB3, 0x71, 0x96, + 0x2D, 0xC6, 0x21, 0x2A, 0x59, 0x7D, 0xCA, 0xFC, 0x3C, 0xF6, 0x9B, 0x96, + 0x27, 0x64, 0xDE, 0x91, 0xF8, 0xCA, 0xF3, 0x9E, 0xB4, 0x0D, 0xC1, 0xCC, + 0x7C, 0xB1, 0x31, 0x07, 0xF2, 0x19, 0xCC, 0xA3, 0xE0, 0x0F, 0x90, 0xEC, + 0x40, 0x38, 0x9F, 0x43, 0xD4, 0x62, 0x9D, 0x8E, 0x29, 0x5E, 0xF9, 0xEC, + 0x32, 0xBE, 0xFA, 0xB5, 0xE0, 0x63, 0x04, 0xF2, 0xCC, 0x74, 0x09, 0x34, + 0x13, 0x88, 0x31, 0x37, 0x9E, 0x04, 0xBC, 0xAE, 0x72, 0xEF, 0xE0, 0x33, + 0xF3, 0x1C, 0x32, 0x67, 0x66, 0xF6, 0x6F, 0xFE, 0x3B, 0xD1, 0x6F, 0x3A, + 0xC2, 0xB6, 0xCC, 0xCF, 0x67, 0x2B, 0xF6, 0x3C, 0xE6, 0x74, 0xB2, 0xFA, + 0x3C, 0xEB, 0x71, 0x45, 0xFB, 0x00, 0xC1, 0xF0, 0x33, 0x6C, 0x46, 0x19, + 0x65, 0x94, 0xD1, 0x35, 0x25, 0x91, 0x7F, 0x00, 0xE0, 0x0B, 0x91, 0x92, + 0x3E, 0xB7, 0xA2, 0x93, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, + 0xAE, 0x42, 0x60, 0x82 + }; + + unsigned char list[ 283 ] = { + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x73, 0x7A, 0x7A, 0xF4, 0x00, 0x00, 0x00, + 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xAE, 0xCE, 0x1C, 0xE9, 0x00, 0x00, + 0x00, 0x04, 0x67, 0x41, 0x4D, 0x41, 0x00, 0x00, 0xB1, 0x8F, 0x0B, 0xFC, + 0x61, 0x05, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, + 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01, 0xC7, 0x6F, 0xA8, 0x64, 0x00, + 0x00, 0x00, 0xB0, 0x49, 0x44, 0x41, 0x54, 0x58, 0x47, 0x63, 0x18, 0xF1, + 0x80, 0x11, 0x4A, 0x33, 0xFC, 0xFF, 0xFF, 0x5F, 0x08, 0x48, 0x31, 0x43, + 0x78, 0x0C, 0x7F, 0x19, 0x19, 0x19, 0xDF, 0x41, 0xD9, 0x34, 0x05, 0x60, + 0x07, 0x00, 0x2D, 0xEF, 0x00, 0x52, 0xA9, 0x40, 0xFC, 0x0F, 0xC4, 0x07, + 0x02, 0x26, 0x20, 0x9E, 0x0D, 0x74, 0x44, 0x05, 0x84, 0x0B, 0x01, 0x40, + 0x75, 0x5C, 0x40, 0x8A, 0x1B, 0xC2, 0xA3, 0x18, 0x7C, 0x05, 0x9A, 0xFF, + 0x8D, 0x11, 0xEA, 0xF3, 0xDB, 0x40, 0x0C, 0xA2, 0x91, 0x01, 0x28, 0x04, + 0x54, 0x61, 0x21, 0x01, 0x54, 0x17, 0x0C, 0xA4, 0x7A, 0x80, 0x98, 0x17, + 0x88, 0x61, 0x0E, 0x25, 0x17, 0x80, 0x3C, 0xF8, 0x19, 0x88, 0x8B, 0x41, + 0x06, 0x8B, 0x02, 0xF1, 0x6B, 0x20, 0x46, 0x07, 0x20, 0x31, 0x51, 0x90, + 0x6A, 0x20, 0xCD, 0x05, 0xC4, 0x0F, 0x40, 0x82, 0x54, 0x06, 0xCF, 0x41, + 0x2E, 0x19, 0x50, 0x30, 0xE0, 0x51, 0x30, 0xF0, 0x89, 0x10, 0xCA, 0x01, + 0x19, 0x3E, 0x20, 0xD9, 0x70, 0x14, 0x8C, 0x96, 0x84, 0xA3, 0x25, 0xE1, + 0x68, 0x49, 0x38, 0x5A, 0x12, 0x8E, 0x82, 0xD1, 0x92, 0x70, 0xB4, 0x24, + 0x1C, 0x2D, 0x09, 0x47, 0x4B, 0xC2, 0x91, 0x0E, 0x18, 0x18, 0x00, 0x9C, + 0xFF, 0x13, 0x25, 0x61, 0x75, 0x18, 0x95, 0x00, 0x00, 0x00, 0x00, 0x49, + 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 + }; + + unsigned char gear[ 724 ] = { + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x73, 0x7A, 0x7A, 0xF4, 0x00, 0x00, 0x00, + 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xAE, 0xCE, 0x1C, 0xE9, 0x00, 0x00, + 0x00, 0x04, 0x67, 0x41, 0x4D, 0x41, 0x00, 0x00, 0xB1, 0x8F, 0x0B, 0xFC, + 0x61, 0x05, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, + 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01, 0xC7, 0x6F, 0xA8, 0x64, 0x00, + 0x00, 0x02, 0x69, 0x49, 0x44, 0x41, 0x54, 0x58, 0x47, 0xC5, 0x96, 0x4F, + 0x4B, 0x55, 0x41, 0x18, 0x87, 0xEF, 0x31, 0xB0, 0x4C, 0x09, 0xDA, 0x64, + 0x82, 0xCB, 0xD4, 0x5B, 0xD0, 0xAE, 0x45, 0xED, 0x72, 0x67, 0x7E, 0x1B, + 0xDB, 0xF4, 0x01, 0x82, 0x5A, 0xD4, 0x2A, 0xF7, 0x12, 0x45, 0x7F, 0x20, + 0x83, 0xFA, 0x08, 0xAD, 0x2A, 0xE8, 0xCF, 0x42, 0xD3, 0x36, 0x45, 0x44, + 0x51, 0x66, 0x14, 0x12, 0x28, 0x0A, 0x71, 0x7B, 0x7E, 0x33, 0xEF, 0xD5, + 0xEB, 0xB9, 0xE7, 0x9C, 0x99, 0xB9, 0x5C, 0x4F, 0x0F, 0x3C, 0xCC, 0xEB, + 0xCC, 0x7B, 0xCE, 0x9C, 0x7B, 0x9C, 0x39, 0xF3, 0x36, 0x52, 0x69, 0xB5, + 0x5A, 0x97, 0xF0, 0x05, 0x7E, 0xC6, 0x1D, 0x53, 0xF1, 0x4B, 0x9C, 0xB5, + 0xB4, 0x83, 0x83, 0x49, 0xDE, 0x60, 0x19, 0x6F, 0x31, 0xB3, 0xD4, 0xFE, + 0xC3, 0xCD, 0xC7, 0x71, 0x43, 0x33, 0x95, 0xA0, 0xB1, 0x71, 0x4B, 0x8F, + 0x62, 0xC0, 0xDA, 0x58, 0x4E, 0xE3, 0x31, 0x1F, 0x16, 0xA2, 0xB1, 0x33, + 0x3E, 0x8C, 0x23, 0xF5, 0x01, 0x2E, 0x5A, 0x5B, 0x45, 0x4C, 0x4E, 0x31, + 0xBC, 0xBE, 0x09, 0xBC, 0x83, 0xD7, 0xF0, 0x3C, 0x0E, 0x76, 0xF4, 0xDF, + 0xC7, 0x6D, 0x0C, 0xA1, 0x45, 0xF9, 0x10, 0xA7, 0xEC, 0xDA, 0xC3, 0x78, + 0x01, 0xAF, 0xE3, 0x5D, 0x9C, 0x50, 0x7F, 0x17, 0x0C, 0x0C, 0x63, 0x7E, + 0x81, 0x2D, 0xE3, 0x02, 0xAE, 0xBB, 0xBF, 0xD2, 0xF8, 0x89, 0xB7, 0xF1, + 0x9D, 0xFB, 0x6B, 0x0F, 0x2D, 0xD4, 0x61, 0x9B, 0x76, 0x0F, 0x3A, 0x6F, + 0xB8, 0xE1, 0x7A, 0xB8, 0x69, 0xD3, 0x36, 0xDC, 0x96, 0xA1, 0xE3, 0x2C, + 0xCD, 0x2B, 0x74, 0xAF, 0xBC, 0x06, 0x76, 0xF0, 0x5C, 0x96, 0x65, 0x4B, + 0xED, 0x45, 0x38, 0x87, 0x75, 0x4D, 0x2E, 0x34, 0xD7, 0x65, 0x05, 0x19, + 0xBF, 0xFE, 0x04, 0xED, 0x7B, 0x3C, 0xAE, 0x8E, 0x44, 0x7E, 0x5B, 0xDB, + 0xEB, 0xB5, 0x4D, 0xBD, 0x81, 0x4D, 0xFC, 0xAE, 0x9E, 0x04, 0x1E, 0xE3, + 0x34, 0x6A, 0xCF, 0x4B, 0xC5, 0x8B, 0x98, 0xC2, 0x37, 0xD4, 0xDC, 0x6E, + 0x0D, 0x4C, 0xE2, 0x2A, 0xC6, 0x70, 0xC5, 0x5D, 0x54, 0x80, 0xC6, 0x7C, + 0x4A, 0x10, 0xCD, 0x35, 0x69, 0x97, 0x79, 0xE8, 0x18, 0x43, 0x1D, 0x32, + 0x55, 0x3C, 0xB2, 0xF4, 0x52, 0xC8, 0x59, 0xF4, 0xA9, 0xA5, 0x3C, 0xC7, + 0x31, 0x4B, 0xDF, 0x8F, 0x06, 0xF0, 0x87, 0xB2, 0x4A, 0x08, 0x7E, 0xE5, + 0x94, 0xE3, 0x53, 0x0B, 0x59, 0xC3, 0x93, 0x96, 0xEA, 0xC8, 0x7F, 0x8A, + 0xB7, 0xF0, 0xAF, 0x0F, 0xBB, 0xD0, 0xA2, 0xD1, 0x62, 0x0D, 0xA1, 0x9C, + 0xF6, 0xE2, 0xCC, 0xA3, 0x7B, 0x6B, 0x8E, 0x5D, 0x52, 0xCF, 0x82, 0xBE, + 0x93, 0x7F, 0x80, 0x23, 0x78, 0xC8, 0x87, 0x5D, 0x68, 0xAB, 0x35, 0x7D, + 0x58, 0x89, 0x72, 0xCA, 0xB6, 0xA5, 0xEE, 0x3D, 0xE4, 0xC3, 0x1C, 0xFA, + 0xDF, 0xA0, 0x16, 0x48, 0x15, 0xC1, 0xAD, 0xA6, 0x1C, 0x9F, 0x5A, 0x4A, + 0xF7, 0x22, 0xA4, 0x43, 0xDB, 0x70, 0x45, 0xA3, 0x11, 0xF4, 0x77, 0x1B, + 0x12, 0x8C, 0x60, 0xFE, 0xC4, 0x0A, 0xA1, 0x5F, 0xA9, 0xD5, 0xAE, 0xB7, + 0x26, 0x15, 0x87, 0x7E, 0x79, 0x1E, 0xCD, 0x39, 0xA2, 0x4F, 0xF1, 0x28, + 0xCF, 0xB1, 0x8A, 0x75, 0x7F, 0x8A, 0x7F, 0x61, 0x73, 0x80, 0x13, 0x69, + 0x8D, 0xE0, 0xA9, 0xEB, 0x4A, 0x47, 0x13, 0xF7, 0x32, 0xB9, 0x78, 0xC2, + 0xDC, 0xEB, 0xFF, 0xEB, 0x38, 0xDE, 0x46, 0x1D, 0xC7, 0xCB, 0x6E, 0x1B, + 0xEA, 0x5C, 0xA6, 0x99, 0x57, 0x5C, 0x13, 0xB7, 0x34, 0xB9, 0xC5, 0x1E, + 0xDE, 0x82, 0x4A, 0x32, 0x95, 0x4B, 0x9D, 0xB4, 0x4B, 0x32, 0x95, 0x57, + 0xA9, 0xA8, 0x8C, 0xD3, 0xB5, 0xBA, 0x47, 0x27, 0xAF, 0xF1, 0xA8, 0x4D, + 0xBB, 0x1F, 0x06, 0x54, 0x7C, 0xAA, 0x70, 0x54, 0x01, 0xA9, 0x42, 0xB2, + 0x5D, 0x94, 0x6A, 0x9B, 0x3E, 0x40, 0x15, 0x9C, 0x21, 0x54, 0xB8, 0xDE, + 0x43, 0x57, 0x7C, 0xD2, 0x0E, 0xA2, 0x0A, 0x5C, 0x15, 0xBA, 0x2A, 0x78, + 0x4F, 0xA9, 0xBF, 0x27, 0xEC, 0x26, 0x21, 0xAE, 0x5A, 0x7A, 0x14, 0xA9, + 0x67, 0xC1, 0x33, 0x6B, 0xAB, 0x88, 0xC9, 0xD9, 0x25, 0xF5, 0x01, 0x56, + 0xF0, 0x8F, 0x0F, 0x0B, 0xD9, 0x40, 0x7D, 0x53, 0xA2, 0x49, 0x7D, 0x80, + 0x2F, 0xF8, 0xD1, 0x87, 0x85, 0x7C, 0x60, 0x75, 0x7F, 0xB5, 0xF8, 0x60, + 0xE0, 0x7F, 0x3C, 0x8B, 0xAA, 0x9C, 0x3E, 0xE1, 0xA6, 0xA9, 0x58, 0x87, + 0xCC, 0x8C, 0xA5, 0x45, 0xD2, 0x68, 0xFC, 0x03, 0x5C, 0xE4, 0xE5, 0x8C, + 0xB3, 0x6F, 0xA9, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, + 0xAE, 0x42, 0x60, 0x82 + }; + + unsigned char left_arrow[ 391 ] = { + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x73, 0x7A, 0x7A, 0xF4, 0x00, 0x00, 0x00, + 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xAE, 0xCE, 0x1C, 0xE9, 0x00, 0x00, + 0x00, 0x04, 0x67, 0x41, 0x4D, 0x41, 0x00, 0x00, 0xB1, 0x8F, 0x0B, 0xFC, + 0x61, 0x05, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, + 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01, 0xC7, 0x6F, 0xA8, 0x64, 0x00, + 0x00, 0x01, 0x1C, 0x49, 0x44, 0x41, 0x54, 0x58, 0x47, 0xED, 0x97, 0xB1, + 0x4E, 0xC2, 0x50, 0x14, 0x86, 0x7B, 0xD9, 0x58, 0x11, 0x47, 0xD8, 0xF0, + 0x9D, 0xF4, 0x11, 0x74, 0xD6, 0x26, 0x84, 0xC1, 0x91, 0x30, 0xE0, 0xE4, + 0x6B, 0x18, 0x13, 0x5F, 0x00, 0x5E, 0x40, 0x27, 0x3B, 0xC2, 0x6E, 0x80, + 0x07, 0x80, 0x98, 0xF2, 0x9D, 0xDB, 0xD3, 0x04, 0x6A, 0x6B, 0x22, 0xE1, + 0xB4, 0x0E, 0xF7, 0x4B, 0xBE, 0xF6, 0xF6, 0xF6, 0x24, 0xFF, 0x9F, 0xB4, + 0x69, 0xD2, 0x28, 0xF0, 0x57, 0xD2, 0x34, 0xBD, 0xC1, 0x37, 0x9C, 0xE2, + 0x85, 0x6E, 0xD7, 0x03, 0x81, 0x0F, 0x78, 0xC8, 0x8B, 0xDE, 0xB2, 0x87, + 0xB0, 0x38, 0xCB, 0x3C, 0x62, 0x8D, 0x1D, 0x1D, 0xB1, 0x83, 0x90, 0xB2, + 0x70, 0x61, 0xA6, 0x23, 0x76, 0x10, 0x52, 0x15, 0xBE, 0xC0, 0x81, 0x8E, + 0xD9, 0x40, 0xC0, 0x6F, 0xE1, 0x57, 0x3A, 0x66, 0x03, 0x01, 0x21, 0xBC, + 0x48, 0xA3, 0xE1, 0x4B, 0x6C, 0x2C, 0x3C, 0xC1, 0xBE, 0x8E, 0x9D, 0x1D, + 0x27, 0x07, 0x02, 0x62, 0x4E, 0x63, 0x59, 0x17, 0xD8, 0xE1, 0x10, 0xDF, + 0xB1, 0x2D, 0x1B, 0x67, 0x42, 0x72, 0x17, 0xCE, 0xB9, 0xC4, 0x11, 0x7E, + 0xCB, 0xC5, 0xB3, 0xDF, 0xFE, 0x49, 0x8A, 0xBE, 0xA4, 0x01, 0xDF, 0x78, + 0x27, 0x05, 0xBE, 0x58, 0x5C, 0xFA, 0xAD, 0xFA, 0x59, 0xB5, 0x74, 0xD1, + 0x18, 0x52, 0xE0, 0x31, 0x5B, 0x96, 0x22, 0x8F, 0xC0, 0x0A, 0x79, 0x04, + 0xA3, 0xFC, 0x25, 0xBC, 0xE7, 0x34, 0x91, 0x75, 0x81, 0x2D, 0xCA, 0x4B, + 0xF8, 0x81, 0x26, 0x2F, 0x61, 0x76, 0x09, 0x52, 0x02, 0xCB, 0xF8, 0xC4, + 0x9E, 0x8E, 0xD9, 0x42, 0x50, 0x55, 0x09, 0xFB, 0xAF, 0x60, 0x0E, 0x41, + 0xA1, 0x84, 0x87, 0xA0, 0xAA, 0x12, 0x4B, 0x0C, 0x25, 0x84, 0x7F, 0x51, + 0x62, 0x8E, 0xFE, 0x5B, 0x62, 0x0E, 0x41, 0x65, 0x25, 0x36, 0x58, 0xDF, + 0xCF, 0x09, 0x61, 0xC5, 0x12, 0xAF, 0x7A, 0xAB, 0x3E, 0x08, 0xBD, 0x46, + 0xF9, 0x35, 0x7B, 0xC2, 0xAE, 0x6E, 0x07, 0x4E, 0x24, 0x8A, 0xF6, 0xE2, + 0xA0, 0x76, 0xBD, 0x81, 0x2E, 0x53, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x49, + 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 + }; + + unsigned char arrows[ 717 ] = { + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x73, 0x7A, 0x7A, 0xF4, 0x00, 0x00, 0x00, + 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xAE, 0xCE, 0x1C, 0xE9, 0x00, 0x00, + 0x00, 0x04, 0x67, 0x41, 0x4D, 0x41, 0x00, 0x00, 0xB1, 0x8F, 0x0B, 0xFC, + 0x61, 0x05, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, + 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01, 0xC7, 0x6F, 0xA8, 0x64, 0x00, + 0x00, 0x02, 0x62, 0x49, 0x44, 0x41, 0x54, 0x58, 0x47, 0xC5, 0x97, 0xDB, + 0x4E, 0x1B, 0x31, 0x10, 0x86, 0xB3, 0x80, 0x04, 0x02, 0xB5, 0x52, 0x24, + 0xA8, 0x78, 0x03, 0x1E, 0x08, 0xD4, 0xA6, 0x52, 0x03, 0xE5, 0x01, 0xE8, + 0x3B, 0x71, 0xB8, 0xE0, 0x85, 0xE8, 0x5D, 0x29, 0xDC, 0x10, 0x09, 0x21, + 0x71, 0xD5, 0x0A, 0xC4, 0x21, 0xFD, 0xBF, 0xDD, 0xF1, 0xCA, 0x76, 0x26, + 0xE9, 0x2A, 0xEC, 0x86, 0x4F, 0xFA, 0x63, 0x7B, 0xEC, 0xF5, 0xF8, 0x30, + 0xB1, 0x77, 0x7B, 0xF3, 0x32, 0x1E, 0x8F, 0xBF, 0x48, 0x23, 0xE9, 0x86, + 0xBC, 0x99, 0x17, 0x83, 0x1C, 0x0E, 0xA5, 0x9C, 0xA1, 0x55, 0x77, 0x0B, + 0x8E, 0xA4, 0xD7, 0xD2, 0x65, 0x0A, 0xB6, 0x6E, 0x07, 0x81, 0x03, 0x73, + 0x34, 0x8D, 0xEE, 0x06, 0xA1, 0x8E, 0x07, 0xA5, 0x8B, 0x66, 0x0C, 0xEC, + 0xB1, 0x76, 0x50, 0x87, 0x1B, 0xD2, 0x75, 0xD9, 0x75, 0x33, 0x68, 0xBB, + 0x61, 0x8F, 0xCF, 0x64, 0xC9, 0xD2, 0x79, 0x78, 0xB1, 0x14, 0xE2, 0x7C, + 0x37, 0x68, 0x46, 0x7B, 0x36, 0xB3, 0x5F, 0xD2, 0x91, 0xC4, 0xDF, 0x2F, + 0x40, 0x1E, 0x1B, 0x75, 0xB4, 0xD9, 0xB3, 0xC7, 0xDA, 0x45, 0x1D, 0xB3, + 0x15, 0x6B, 0xD2, 0x47, 0xE9, 0x56, 0x0A, 0x90, 0xC7, 0x46, 0x5D, 0xA3, + 0xA5, 0x0F, 0xB8, 0x5B, 0x40, 0x27, 0x5E, 0x47, 0x45, 0x51, 0xFC, 0x91, + 0x1E, 0x94, 0x5D, 0xAD, 0x2C, 0x09, 0xAB, 0xD4, 0xD1, 0xC6, 0xCA, 0x35, + 0xEA, 0x6B, 0x1D, 0x59, 0x31, 0x61, 0x62, 0x00, 0x6A, 0x48, 0x04, 0xFF, + 0x44, 0xCA, 0xBF, 0x79, 0x29, 0xD5, 0xC7, 0xAE, 0x12, 0xFA, 0xBB, 0x50, + 0x7E, 0xF6, 0x89, 0xA9, 0x06, 0xF9, 0x09, 0xE7, 0x46, 0xB3, 0x6C, 0x5B, + 0x52, 0xBE, 0x05, 0x5B, 0x56, 0x5D, 0x23, 0x5B, 0xFE, 0xEF, 0xE1, 0x9C, + 0xF8, 0x66, 0xD5, 0x25, 0xF5, 0x0A, 0xA8, 0x82, 0x03, 0xE4, 0xB8, 0x2A, + 0xD5, 0x3C, 0x4B, 0x5E, 0x84, 0x63, 0xCF, 0xF1, 0x6C, 0x3C, 0x1B, 0x3F, + 0x5F, 0x48, 0x27, 0xF9, 0x20, 0xC2, 0xCC, 0xBD, 0x13, 0x8E, 0xC8, 0x26, + 0xB8, 0x98, 0x71, 0x50, 0x5F, 0xDA, 0x91, 0xEE, 0xA4, 0x00, 0x79, 0x6C, + 0xD4, 0x6D, 0x46, 0xFA, 0x20, 0xFD, 0x90, 0x72, 0xEA, 0x95, 0x28, 0x94, + 0x61, 0x5F, 0xCE, 0x29, 0x64, 0x30, 0xF2, 0x5B, 0x69, 0xA5, 0x2C, 0xA5, + 0xB0, 0x72, 0x7D, 0x4B, 0xE1, 0x55, 0xBA, 0xB7, 0x34, 0xE7, 0x49, 0xFA, + 0x24, 0x79, 0xFD, 0x0C, 0x18, 0xC0, 0x48, 0x99, 0xED, 0xAA, 0xBC, 0x70, + 0x46, 0xCC, 0x60, 0x5C, 0xE5, 0xDF, 0x85, 0x82, 0x15, 0xF8, 0xAC, 0x0C, + 0x5B, 0x40, 0x80, 0xC4, 0xB4, 0xB5, 0x05, 0x04, 0x27, 0x5B, 0xB0, 0x5C, + 0x96, 0x52, 0xBE, 0x96, 0xBF, 0x04, 0x84, 0xE4, 0x05, 0x21, 0x01, 0x44, + 0x20, 0xC5, 0x81, 0xF5, 0xBF, 0x20, 0x8C, 0x03, 0x96, 0x00, 0x26, 0x90, + 0x73, 0x26, 0xAF, 0x6D, 0x19, 0xBC, 0x41, 0x5C, 0x4A, 0x6B, 0xD6, 0xA4, + 0x46, 0x36, 0x1C, 0xE5, 0xE7, 0x00, 0x2B, 0x92, 0x20, 0x1B, 0x47, 0x33, + 0xF7, 0x43, 0x4C, 0xE2, 0x3C, 0x2C, 0x21, 0xC7, 0xEC, 0x99, 0x92, 0x7D, + 0x29, 0x8E, 0x09, 0x96, 0xCD, 0x5B, 0x3A, 0x6F, 0x5B, 0xBC, 0x76, 0xD8, + 0xF2, 0xB6, 0x07, 0xF2, 0x75, 0x6A, 0xF9, 0x49, 0x34, 0x3A, 0x5E, 0x36, + 0x7F, 0x4B, 0x57, 0x12, 0xC7, 0xE8, 0x04, 0xB2, 0xB3, 0xBC, 0xF9, 0x0A, + 0x6C, 0x5A, 0x75, 0x82, 0xEC, 0xE1, 0x16, 0x45, 0xCD, 0x5E, 0x54, 0xD4, + 0x70, 0xEA, 0xE5, 0x01, 0xAA, 0x6B, 0x3C, 0x00, 0x50, 0x9D, 0x7B, 0xB9, + 0x41, 0xBD, 0x05, 0x31, 0x5A, 0xA2, 0xBF, 0xC8, 0x8A, 0x35, 0xD6, 0x11, + 0x31, 0xF1, 0x58, 0x59, 0x12, 0x1E, 0xA9, 0xF3, 0x1C, 0x71, 0x43, 0x22, + 0x2B, 0x26, 0xB8, 0x03, 0xF0, 0x50, 0xC7, 0xDC, 0x8C, 0xE5, 0xAD, 0x26, + 0x7D, 0x97, 0xE2, 0xB3, 0x9F, 0xD3, 0xEE, 0x50, 0x0A, 0xB7, 0xA8, 0xBB, + 0x75, 0x73, 0xC3, 0xAC, 0xA4, 0xF8, 0x56, 0x83, 0x67, 0x4B, 0xE1, 0xC9, + 0xD2, 0x00, 0xF1, 0x33, 0x75, 0x0B, 0x63, 0x1A, 0xAF, 0x80, 0x43, 0x1C, + 0xF5, 0x79, 0xA4, 0xB7, 0x7F, 0xBA, 0x6A, 0x46, 0x4D, 0x5F, 0xCB, 0xF9, + 0x9F, 0x77, 0xF3, 0xA9, 0xA6, 0x8E, 0x9B, 0x7C, 0x98, 0xA4, 0x77, 0x7D, + 0xDB, 0xC8, 0xC1, 0xAC, 0x4F, 0xB3, 0x6E, 0x9D, 0x07, 0xE4, 0x28, 0x7F, + 0x75, 0x83, 0xC5, 0x38, 0x0F, 0xC8, 0x61, 0x0B, 0x9F, 0xE7, 0xBD, 0xDE, + 0x3F, 0xFC, 0xB6, 0x0E, 0xC9, 0xC2, 0x67, 0xAD, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 + }; + } // namespace icons +} // namespace resource diff --git a/src/core/huds/huds.h b/src/core/huds/huds.h index cc93240..3657172 100644 --- a/src/core/huds/huds.h +++ b/src/core/huds/huds.h @@ -10,6 +10,8 @@ namespace huds { inline int safezone_x = 8; inline int safezone_y = 8; + inline bool edit = false; + void paint( ); void paint_ui( ); } // namespace huds diff --git a/src/core/menu/framework.cpp b/src/core/menu/framework.cpp index b2d34ea..4eccf0e 100644 --- a/src/core/menu/framework.cpp +++ b/src/core/menu/framework.cpp @@ -6,33 +6,85 @@ #include +void gui::framework::set_theme( bool dark ) { + if ( !dark ) { + colors::bg = color_t( 255, 255, 255, 255 ); + colors::fg = color_t( 200, 200, 200, 255 ); + colors::text = color_t( 0, 0, 0, 200 ); + colors::accent = color_t( 66, 128, 244, 255 ); + colors::disabled = color_t( 0, 0, 0, 64 ); + } else { + colors::bg = color_t( 21, 21, 21, 255 ); + colors::fg = color_t( 59, 59, 59, 255 ); + colors::text = color_t( 255, 255, 255, 64 ); + colors::accent = color_t( 66, 128, 244, 255 ); + colors::disabled = color_t( 255, 255, 255, 2 ); + } +} + void gui::framework::begin( vec2_t pos, vec2_t size ) { cur_menu = menu_t( ); cur_menu.pos = pos; cur_menu.size = size; - cur_menu.cursor = vec2_t( 12, 12 ); + cur_menu.cursor = vec2_t( 8, 8 ); // block input to other controls when a dropdown is open cur_menu.block_input = !cur_dropdown.id.empty( ); - photon->render->draw_outlined_rect( cur_menu.pos.x, cur_menu.pos.y, cur_menu.size.x, cur_menu.size.y, colors::dark ); - photon->render->draw_filled_rect( cur_menu.pos.x + 1, cur_menu.pos.y + 1, cur_menu.size.x - 2, cur_menu.size.y - 2, colors::bg ); + photon->render->draw_rounded_rect( cur_menu.pos.x, cur_menu.pos.y, cur_menu.size.x, cur_menu.size.y, colors::bg, 8 ); interfaces::surface->enable_clipping = true; - interfaces::surface->set_clip_rect( cur_menu.pos.x + 1, cur_menu.pos.y + 1, cur_menu.pos.x + cur_menu.size.x - 1, cur_menu.pos.y + cur_menu.size.y - 1 ); + interfaces::surface->set_clip_rect( cur_menu.pos.x, cur_menu.pos.y, cur_menu.pos.x + cur_menu.size.x, cur_menu.pos.y + cur_menu.size.y ); } void gui::framework::end( ) { + // draw scrollbar only if the content cant fit in to the page + const auto max_y = cur_menu.cursor.y - cur_menu.size.y; + if ( max_y > 0 ) { + const auto cur_pos = vec2_t( cur_menu.pos.x + cur_menu.size.x - 14, cur_menu.pos.y + 8 ); + + const auto height = max( cur_menu.size.y - max_y, 16 ); + const auto size = vec2_t( 8, cur_menu.size.y - height - 16 ); + + bool hover = !cur_menu.block_input && photon->input->is_cursor_in_area( cur_pos.x, cur_pos.y, cur_pos.x + size.x, cur_pos.y + cur_menu.size.y - 16 ); + bool clicking = photon->input->get_key_held( mouse_left ); + +#define SCROLLBAR_ID "photon scrollbar" + + static int grab_pos{ }; + + float value = ( float ) scroll_offset / max_y; + + if ( hover && clicking && cur_slider.empty( ) ) { + cur_slider = SCROLLBAR_ID; + grab_pos = photon->input->get_cursor_position( ).y - cur_pos.y - value * size.y; + } else if ( !clicking && !cur_slider.empty( ) ) + cur_slider.clear( ); + + if ( cur_slider == SCROLLBAR_ID ) { + value = ( photon->input->get_cursor_position( ).y - cur_pos.y - grab_pos ) / size.y; + scroll_offset = std::clamp( value * max_y, 0.f, max_y ); + value = ( float ) scroll_offset / max_y; + } + + photon->render->draw_rounded_rect( cur_pos.x, cur_pos.y + value * size.y, 8, height, cur_slider == SCROLLBAR_ID ? colors::accent : colors::fg, 4 ); + +#undef SCROLLBAR_ID + } else + scroll_offset = 0; // reset scroll offset if content can fit in page + // draw dropdowns - { - auto cur_pos = cur_menu.pos + cur_dropdown.pos; + if ( !cur_dropdown.id.empty( ) ) { + auto cur_pos = cur_dropdown.pos; + + const auto size = vec2_t( 140, 24 ); - const auto size = vec2_t( 160, 26 ); + cur_pos.y += size.y; - photon->render->draw_outlined_rect( cur_pos.x, cur_pos.y, size.x, cur_dropdown.items.size( ) * size.y, colors::dark ); - photon->render->draw_filled_rect( cur_pos.x + 1, cur_pos.y + 1, size.x - 2, cur_dropdown.items.size( ) * size.y - 2, colors::black ); + photon->render->draw_rounded_rect( cur_pos.x, cur_pos.y, size.x, cur_dropdown.items.size( ) * size.y, colors::accent, 8 ); + photon->render->draw_rounded_rect( cur_pos.x + 1, cur_pos.y + 1, size.x - 2, cur_dropdown.items.size( ) * size.y - 2, colors::bg, 8 ); for ( std::size_t i = 0; i < cur_dropdown.items.size( ); ++i ) { const auto& item = cur_dropdown.items[ i ]; @@ -46,15 +98,15 @@ void gui::framework::end( ) { cur_dropdown.done = true; } - photon->render->draw_text( cur_pos.x + 8, cur_pos.y + 2, fonts::normal, hover ? colors::white : colors::dark, false, item.c_str( ) ); + photon->render->draw_text( cur_pos.x + 8, cur_pos.y + 4, fonts::smaller, hover ? colors::accent : colors::text, false, util::to_upper( item ).c_str( ) ); } else { if ( clicking ) cur_dropdown.value ^= ( 1 << i ); - photon->render->draw_text( cur_pos.x + 8, cur_pos.y + 2, fonts::normal, cur_dropdown.value & ( 1 << i ) ? colors::white : colors::dark, false, item.c_str( ) ); + photon->render->draw_text( cur_pos.x + 8, cur_pos.y + 4, fonts::smaller, cur_dropdown.value & ( 1 << i ) ? colors::accent : colors::text, false, util::to_upper( item ).c_str( ) ); } - cur_pos.y += 26; + cur_pos.y += size.y; } } @@ -62,100 +114,140 @@ void gui::framework::end( ) { interfaces::surface->enable_clipping = false; } -bool gui::framework::tab( int& selected, vec2_t pos, vec2_t size, const std::string& title ) { +bool gui::framework::tab( int& selected, vec2_t pos, vec2_t size, const std::string& label, bool texture ) { bool hover = photon->input->is_cursor_in_area( pos.x, pos.y, pos.x + size.x, pos.y + size.y ); bool active = ( hover && photon->input->get_key_press( mouse_left ) ) || selected == cur_menu.tab_count; - if ( active ) + if ( active ) { selected = cur_menu.tab_count; - interfaces::surface->set_clip_rect( 0, 0, photon->render->get_screen_size( ).x, photon->render->get_screen_size( ).y ); + cur_menu.cursor = vec2_t( 8, 8 ); + } - photon->render->draw_outlined_rect( pos.x, pos.y, size.x, size.y, active ? colors::white : hover ? colors::dark - : colors::darker ); - photon->render->draw_filled_rect( pos.x + 1, pos.y + 1, size.x - 2, size.y - 2, colors::bg ); + interfaces::surface->set_clip_rect( 0, 0, photon->render->get_screen_size( ).x, photon->render->get_screen_size( ).y ); - const auto text_size = photon->render->get_text_size( fonts::title, title.c_str( ) ); + photon->render->draw_filled_rect( pos.x, pos.y, size.x, size.y, hover ? colors::accent : colors::bg ); + if ( hover ) + photon->render->draw_outlined_rect( pos.x - 1, pos.y - 1, size.x + 2, size.y + 2, colors::bg, 3 ); - photon->render->draw_text( pos.x + size.x / 2, pos.y + size.y / 2 - text_size.y / 2, fonts::title, colors::white, true, title.c_str( ) ); + if ( !texture ) { + const auto text_size = photon->render->get_text_size( fonts::title, util::to_upper( label ).c_str( ) ); + photon->render->draw_text( pos.x + size.x / 2, pos.y + size.y / 2 - text_size.y / 2, fonts::title, colors::text, true, util::to_upper( label ).c_str( ) ); + } else { + // yep these numbers are hardcoded idc + photon->render->draw_texture( pos.x + 12, pos.y + 12, 32, 32, label.c_str( ), colors::text ); + } interfaces::surface->set_clip_rect( cur_menu.pos.x + 1, cur_menu.pos.y + 1, cur_menu.pos.x + cur_menu.size.x - 1, cur_menu.pos.y + cur_menu.size.y - 1 ); ++cur_menu.tab_count; - // fix the weird positioning for 1 frame - if ( active ) - cur_menu.cursor = vec2_t( 12, 12 ); - return active; } -bool gui::framework::mod( mods::mod_info_t* info ) { - const auto cur_pos = cur_menu.pos + cur_menu.cursor; +bool gui::framework::mod( mods::mod_info_t& info ) { + const auto size = vec2_t( 232, 158 ); - const auto size = vec2_t( 220, 128 ); + if ( cur_menu.mod_count % 3 == 0 ) { + cur_menu.cursor.x = 8; + } else { + cur_menu.cursor.x += size.x; + cur_menu.cursor.y -= 80 + 36 + 40 + 8; + } - bool hover = photon->input->is_cursor_in_area( cur_pos.x, cur_pos.y, cur_pos.x + size.x, cur_pos.y + size.y ); + auto cur_pos = cur_menu.pos + cur_menu.cursor; + cur_pos.y -= scroll_offset; - photon->render->draw_outlined_rect( cur_pos.x, cur_pos.y, size.x, size.y, hover ? colors::dark : colors::darker ); - photon->render->draw_filled_rect( cur_pos.x + 1, cur_pos.y + 1, size.x - 2, size.y - 2, colors::bg ); + photon->render->draw_filled_rect( cur_pos.x, cur_pos.y, size.x, size.y, info.is_loaded ? colors::green : colors::fg ); - const auto mod_info = info->ptr->get_info( ); + const auto mod_info = info.ptr->get_info( ); - photon->render->draw_text( cur_pos.x + size.x / 2, cur_pos.y + 8, fonts::title, colors::white, true, mod_info.name ); - photon->render->draw_text( cur_pos.x + size.x / 2, cur_pos.y + 36, fonts::normal, colors::dark, true, mod_info.version ); + photon->render->draw_text( cur_pos.x + size.x / 2 + 2, cur_pos.y + 16 + 2, fonts::normal, colors::gray, true, mod_info.name ); + photon->render->draw_text( cur_pos.x + size.x / 2, cur_pos.y + 16, fonts::normal, colors::white, true, mod_info.name ); - cur_menu.cursor += vec2_t( 8, 64 ); + photon->render->draw_text( cur_pos.x + size.x / 2, cur_pos.y + 40, fonts::smaller, colors::text, true, util::ssprintf( "by %s", mod_info.author ).c_str( ) ); - bool result = false; + cur_menu.cursor += vec2_t( 8, 80 ); - if ( !info->is_loaded ) { - if ( button( vec2_t( size.x - 16, 26 ), "enable" ) ) - mods::enable( info ); + if ( !info.is_loaded ) { + if ( button( { size.x - 16, 28 }, "ENABLE", true, fonts::smaller, colors::green ) ) + mods::enable( &info ); } else { - if ( button( vec2_t( size.x - 16, 26 ), "disable" ) ) - mods::disable( info ); - - result = button( vec2_t( size.x - 16, 26 ), "settings" ); + if ( button( { size.x - 16, 28 }, "DISABLE", true, fonts::smaller, colors::red ) ) + mods::disable( &info ); } + bool result = button( { size.x - 16, 32 }, "OPTIONS", info.is_loaded ); + ++cur_menu.mod_count; - if ( cur_menu.mod_count % 3 == 0 ) { - cur_menu.cursor.x = 12; - cur_menu.cursor.y += size.y + 8; - } else - cur_menu.cursor.x += size.x + 8; + cur_menu.cursor.y += 8; return result; } -bool gui::framework::button( vec2_t size, const std::string& label ) { - const auto cur_pos = cur_menu.pos + cur_menu.cursor; +void gui::framework::split( int width ) { + cur_menu.cursor.x += width + 8; + cur_menu.cursor.y = 8; +} + +bool gui::framework::icon_button( vec2_t size, const std::string& texture ) { + auto cur_pos = cur_menu.pos + cur_menu.cursor; + cur_pos.y -= scroll_offset; bool hover = !cur_menu.block_input && photon->input->is_cursor_in_area( cur_pos.x, cur_pos.y, cur_pos.x + size.x, cur_pos.y + size.y ); - bool clicking = hover && photon->input->get_key_held( mouse_left ); + bool clicking = hover && photon->input->get_key_press( mouse_left ); + + photon->render->draw_filled_rect( cur_pos.x, cur_pos.y, size.x, size.y, hover ? colors::accent : colors::bg ); + if ( hover ) + photon->render->draw_outlined_rect( cur_pos.x - 1, cur_pos.y - 1, size.x + 2, size.y + 2, colors::bg, 3 ); + + photon->render->draw_texture( cur_pos.x + 12, cur_pos.y + 12, 32, 32, texture.c_str( ), colors::text ); - photon->render->draw_outlined_rect( cur_pos.x, cur_pos.y, size.x, size.y, hover ? clicking ? colors::white : colors::dark : colors::darker ); - photon->render->draw_filled_rect( cur_pos.x + 1, cur_pos.y + 1, size.x - 2, size.y - 2, colors::bg ); + cur_menu.cursor.y += size.y + 8; - const auto text_size = photon->render->get_text_size( fonts::normal, label.c_str( ) ); + return clicking; +} + +bool gui::framework::button( vec2_t size, const std::string& label, bool enabled, h_font font, color_t color ) { + auto cur_pos = cur_menu.pos + cur_menu.cursor; + cur_pos.y -= scroll_offset; + + bool hover = enabled && !cur_menu.block_input && photon->input->is_cursor_in_area( cur_pos.x, cur_pos.y, cur_pos.x + size.x, cur_pos.y + size.y ); + bool clicking = hover && photon->input->get_key_press( mouse_left ); - photon->render->draw_text( cur_pos.x + size.x / 2, cur_pos.y + size.y / 2 - text_size.y / 2, fonts::normal, colors::white, true, label.c_str( ) ); + photon->render->draw_filled_rect( cur_pos.x, cur_pos.y, size.x, size.y, hover ? color : colors::bg ); + if ( hover ) + photon->render->draw_outlined_rect( cur_pos.x - 1, cur_pos.y - 1, size.x + 2, size.y + 2, colors::bg, 3 ); - cur_menu.cursor.y += size.y + 4; + const auto text_size = photon->render->get_text_size( font, util::to_upper( label ).c_str( ) ); + + photon->render->draw_text( cur_pos.x + size.x / 2, cur_pos.y + size.y / 2 - text_size.y / 2, font, colors::text, true, util::to_upper( label ).c_str( ) ); + + if ( !enabled ) + photon->render->draw_filled_rect( cur_pos.x, cur_pos.y, size.x, size.y, colors::disabled ); + + cur_menu.cursor.y += size.y + 8; + + return clicking; +} - if ( hover && photon->input->get_key_press( mouse_left ) ) - return true; +static inline vec2_t align_right( vec2_t pos, vec2_t size ) { + auto pos_x = gui::framework::cur_menu.pos.x + gui::framework::cur_menu.size.x - size.x - 20; - return false; + return vec2_t( pos_x, pos.y ); } -bool gui::framework::checkbox( bool& val, const std::string& label ) { - const auto cur_pos = cur_menu.pos + cur_menu.cursor; +bool gui::framework::toggle( bool& val, const std::string& label ) { + auto cur_pos = cur_menu.pos + cur_menu.cursor; + cur_pos.y -= scroll_offset; + + const auto size = vec2_t( 40, 20 ); + const int radius = size.y / 2 - 2; + const int radius_half = radius / 2; - const auto size = vec2_t( 20, 20 ); + auto cur_pos2 = align_right( cur_pos, size ); - bool hover = !cur_menu.block_input && photon->input->is_cursor_in_area( cur_pos.x, cur_pos.y, cur_pos.x + size.x, cur_pos.y + size.y ); + bool hover = !cur_menu.block_input && photon->input->is_cursor_in_area( cur_pos2.x, cur_pos2.y, cur_pos2.x + size.x, cur_pos2.y + size.y ); bool result = false; if ( hover && photon->input->get_key_press( mouse_left ) ) { @@ -163,82 +255,123 @@ bool gui::framework::checkbox( bool& val, const std::string& label ) { result = true; } - photon->render->draw_outlined_rect( cur_pos.x, cur_pos.y, size.x, size.y, val ? colors::white : colors::dark ); - photon->render->draw_filled_rect( cur_pos.x + 3, cur_pos.y + 3, size.x - 6, size.y - 6, val ? colors::white : color_t( 0, 0, 0, 0 ) ); + photon->render->draw_text( cur_pos.x, cur_pos.y, fonts::normal, colors::text, false, util::to_upper( label ).c_str( ) ); + + photon->render->draw_rounded_rect( cur_pos2.x, cur_pos2.y, size.x, size.y, val ? colors::accent : colors::fg, 10 ); - photon->render->draw_text( cur_pos.x + size.x + 4, cur_pos.y - 2, fonts::normal, colors::white, false, label.c_str( ) ); + cur_pos2 += vec2_t( radius, radius ); + cur_pos2.x += val * size.x / 2; - cur_menu.cursor.y += size.y + 4; + photon->render->draw_circle( cur_pos2.x + 2, cur_pos2.y + 2, radius, colors::white ); + + cur_menu.cursor.y += size.y + 16; return result; } void gui::framework::slider( int& val, int min, int max, const std::string& label ) { auto cur_pos = cur_menu.pos + cur_menu.cursor; + cur_pos.y -= scroll_offset; + + const auto size = vec2_t( 200, 20 ); + const int radius = size.y / 2; + const int radius_half = radius / 2; - const auto size = vec2_t( 160, 18 ); - const auto text_size = photon->render->get_text_size( fonts::normal, label.c_str( ) ); + auto cur_pos2 = align_right( cur_pos, size ); - bool hover = !cur_menu.block_input && photon->input->is_cursor_in_area( cur_pos.x + 3, cur_pos.y + text_size.y + 3, cur_pos.x + size.x - 3, cur_pos.y + text_size.y + size.y - 3 ); - bool clicking = hover && photon->input->get_key_held( mouse_left ); + bool hover = !cur_menu.block_input && photon->input->is_cursor_in_area( cur_pos2.x, cur_pos2.y, cur_pos2.x + size.x, cur_pos2.y + size.y ); + bool clicking = photon->input->get_key_held( mouse_left ); + + if ( hover && clicking && cur_slider.empty( ) ) + cur_slider = label; + else if ( !clicking && !cur_slider.empty( ) ) + cur_slider.clear( ); float value = ( float ) val / ( max - min ); - if ( clicking ) { - value = ( photon->input->get_cursor_position( ).x - ( cur_pos.x + 3 ) ) / ( size.x - 6 ); - val = value * ( max - min ); + if ( cur_slider == label ) { + value = ( photon->input->get_cursor_position( ).x - cur_pos2.x ) / size.x; + val = std::clamp( value * ( max - min ), ( float ) min, ( float ) max ); value = ( float ) val / ( max - min ); } - photon->render->draw_text( cur_pos.x, cur_pos.y, fonts::normal, colors::white, false, label.c_str( ) ); + photon->render->draw_text( cur_pos.x, cur_pos.y, fonts::normal, colors::text, false, util::to_upper( label ).c_str( ) ); + + cur_pos2.y += 6; - cur_pos.y += text_size.y; + photon->render->draw_rounded_rect( cur_pos2.x, cur_pos2.y + 1, size.x, size.y - 13, colors::fg, 4 ); + photon->render->draw_rounded_rect( cur_pos2.x, cur_pos2.y + 1, value * size.x, size.y - 13, colors::accent, 4 ); - photon->render->draw_outlined_rect( cur_pos.x, cur_pos.y, size.x, size.y, clicking ? colors::white : colors::dark ); - photon->render->draw_filled_rect( cur_pos.x + 3, cur_pos.y + 3, value * ( size.x - 6 ), size.y - 6, colors::white ); + cur_pos2 += vec2_t( radius_half, radius_half ); + cur_pos2.x += value * ( size.x - radius ); - photon->render->draw_text( cur_pos.x + size.x + 4, cur_pos.y - 3, fonts::normal, colors::white, false, util::ssprintf( "%d", val ).c_str( ) ); + photon->render->draw_circle( cur_pos2.x, cur_pos2.y, radius, colors::accent ); + photon->render->draw_circle( cur_pos2.x, cur_pos2.y, radius - 4, colors::white ); - cur_menu.cursor.y += size.y + text_size.y + 4; + photon->render->draw_text( cur_pos2.x, cur_pos2.y - 26, fonts::smaller, colors::text, true, util::ssprintf( "%d", val ).c_str( ) ); + + cur_menu.cursor.y += size.y + 16; } void gui::framework::sliderf( float& val, float min, float max, const std::string& label ) { auto cur_pos = cur_menu.pos + cur_menu.cursor; + cur_pos.y -= scroll_offset; + + const auto size = vec2_t( 200, 20 ); + const int radius = size.y / 2; + const int radius_half = radius / 2; - const auto size = vec2_t( 160, 18 ); - const auto text_size = photon->render->get_text_size( fonts::normal, label.c_str( ) ); + auto cur_pos2 = align_right( cur_pos, size ); - bool hover = !cur_menu.block_input && photon->input->is_cursor_in_area( cur_pos.x + 3, cur_pos.y + text_size.y + 3, cur_pos.x + size.x - 3, cur_pos.y + text_size.y + size.y - 3 ); - bool clicking = hover && photon->input->get_key_held( mouse_left ); + bool hover = !cur_menu.block_input && photon->input->is_cursor_in_area( cur_pos2.x, cur_pos2.y, cur_pos2.x + size.x, cur_pos2.y + size.y ); + bool clicking = photon->input->get_key_held( mouse_left ); + + if ( hover && clicking && cur_slider.empty( ) ) + cur_slider = label; + else if ( !clicking && !cur_slider.empty( ) ) + cur_slider.clear( ); float value = val / ( max - min ); - if ( clicking ) { - value = ( photon->input->get_cursor_position( ).x - ( cur_pos.x + 3 ) ) / ( size.x - 6 ); - val = value * ( max - min ); + if ( cur_slider == label ) { + value = ( photon->input->get_cursor_position( ).x - cur_pos2.x ) / size.x; + val = std::clamp( value * ( max - min ), min, max ); + value = val / ( max - min ); } - photon->render->draw_text( cur_pos.x, cur_pos.y, fonts::normal, colors::white, false, label.c_str( ) ); + photon->render->draw_text( cur_pos.x, cur_pos.y, fonts::normal, colors::text, false, util::to_upper( label ).c_str( ) ); + + cur_pos2.y += 6; + + photon->render->draw_rounded_rect( cur_pos2.x, cur_pos2.y + 1, size.x, size.y - 13, colors::fg, 4 ); + photon->render->draw_rounded_rect( cur_pos2.x, cur_pos2.y + 1, value * size.x, size.y - 13, colors::accent, 4 ); + + cur_pos2 += vec2_t( radius_half, radius_half ); + cur_pos2.x += value * ( size.x - radius ); - cur_pos.y += text_size.y; + photon->render->draw_circle( cur_pos2.x, cur_pos2.y, radius, colors::accent ); + photon->render->draw_circle( cur_pos2.x, cur_pos2.y, radius - 4, colors::white ); - photon->render->draw_outlined_rect( cur_pos.x, cur_pos.y, size.x, size.y, clicking ? colors::white : colors::dark ); - photon->render->draw_filled_rect( cur_pos.x + 3, cur_pos.y + 3, value * ( size.x - 6 ), size.y - 6, colors::white ); + photon->render->draw_text( cur_pos2.x, cur_pos2.y - 26, fonts::smaller, colors::text, true, util::ssprintf( "%.1f", val ).c_str( ) ); - photon->render->draw_text( cur_pos.x + size.x + 4, cur_pos.y - 3, fonts::normal, colors::white, false, util::ssprintf( "%.1f", val ).c_str( ) ); + cur_menu.cursor.y += size.y + 16; +} - cur_menu.cursor.y += size.y + text_size.y + 4; +void gui::framework::colorpicker( color_t& val, const std::string& label ) { } void gui::framework::combo( std::size_t& val, const std::vector< std::string >& items, const std::string& label ) { auto cur_pos = cur_menu.pos + cur_menu.cursor; + cur_pos.y -= scroll_offset; + + const auto size = vec2_t( 140, 24 ); - const auto size = vec2_t( 160, 26 ); - const auto text_size = photon->render->get_text_size( fonts::normal, label.c_str( ) ); + auto cur_pos2 = align_right( cur_pos, size ); + cur_pos2.y -= 2; bool open = cur_dropdown.id == label; - bool hover = ( !cur_menu.block_input || open ) && photon->input->is_cursor_in_area( cur_pos.x + 3, cur_pos.y + text_size.y + 3, cur_pos.x + size.x - 3, cur_pos.y + text_size.y + size.y - 3 ); + bool hover = ( !cur_menu.block_input || open ) && photon->input->is_cursor_in_area( cur_pos2.x, cur_pos2.y, cur_pos2.x + size.x, cur_pos2.y + size.y ); bool clicking = hover && photon->input->get_key_press( mouse_left ); if ( clicking ) { @@ -246,7 +379,7 @@ void gui::framework::combo( std::size_t& val, const std::vector< std::string >& cur_dropdown = dropdown_t( ); else { cur_dropdown.id = label; - cur_dropdown.pos = vec2_t( cur_menu.cursor.x, cur_menu.cursor.y + size.y + text_size.y ); + cur_dropdown.pos = cur_pos2; cur_dropdown.items = items; cur_dropdown.value = val; cur_dropdown.multiselect = false; @@ -261,26 +394,28 @@ void gui::framework::combo( std::size_t& val, const std::vector< std::string >& cur_dropdown = dropdown_t( ); } - photon->render->draw_text( cur_pos.x, cur_pos.y, fonts::normal, colors::white, false, label.c_str( ) ); - - cur_pos.y += text_size.y; + photon->render->draw_text( cur_pos.x, cur_pos.y, fonts::normal, colors::text, false, util::to_upper( label ).c_str( ) ); - photon->render->draw_outlined_rect( cur_pos.x, cur_pos.y, size.x, size.y, open ? colors::white : colors::dark ); + photon->render->draw_rounded_rect( cur_pos2.x, cur_pos2.y, size.x, size.y, open ? colors::accent : colors::fg, 8 ); + photon->render->draw_rounded_rect( cur_pos2.x + 1, cur_pos2.y + 1, size.x - 2, size.y - 2, colors::bg, 8 ); - photon->render->draw_text( cur_pos.x + 8, cur_pos.y + 2, fonts::normal, colors::white, false, items[ val ].c_str( ) ); + photon->render->draw_text( cur_pos2.x + 8, cur_pos2.y + 4, fonts::smaller, colors::text, false, util::to_upper( items[ val ] ).c_str( ) ); - cur_menu.cursor.y += size.y + text_size.y + 4; + cur_menu.cursor.y += size.y + 16; } void gui::framework::multicombo( std::size_t& val, const std::vector< std::string >& items, const std::string& label ) { auto cur_pos = cur_menu.pos + cur_menu.cursor; + cur_pos.y -= scroll_offset; - const auto size = vec2_t( 160, 26 ); - const auto text_size = photon->render->get_text_size( fonts::normal, label.c_str( ) ); + const auto size = vec2_t( 140, 24 ); + + auto cur_pos2 = align_right( cur_pos, size ); + cur_pos2.y -= 2; bool open = cur_dropdown.id == label; - bool hover = ( !cur_menu.block_input || open ) && photon->input->is_cursor_in_area( cur_pos.x + 3, cur_pos.y + text_size.y + 3, cur_pos.x + size.x - 3, cur_pos.y + text_size.y + size.y - 3 ); + bool hover = ( !cur_menu.block_input || open ) && photon->input->is_cursor_in_area( cur_pos2.x, cur_pos2.y, cur_pos2.x + size.x, cur_pos2.y + size.y ); bool clicking = hover && photon->input->get_key_press( mouse_left ); if ( clicking ) { @@ -290,7 +425,7 @@ void gui::framework::multicombo( std::size_t& val, const std::vector< std::strin cur_dropdown = dropdown_t( ); } else { cur_dropdown.id = label; - cur_dropdown.pos = vec2_t( cur_menu.cursor.x, cur_menu.cursor.y + size.y + text_size.y ); + cur_dropdown.pos = cur_pos2; cur_dropdown.items = items; cur_dropdown.value = val; cur_dropdown.multiselect = true; @@ -302,11 +437,10 @@ void gui::framework::multicombo( std::size_t& val, const std::vector< std::strin if ( open ) val = cur_dropdown.value; - photon->render->draw_text( cur_pos.x, cur_pos.y, fonts::normal, colors::white, false, label.c_str( ) ); - - cur_pos.y += text_size.y; + photon->render->draw_text( cur_pos.x, cur_pos.y, fonts::normal, colors::text, false, util::to_upper( label ).c_str( ) ); - photon->render->draw_outlined_rect( cur_pos.x, cur_pos.y, size.x, size.y, open ? colors::white : colors::dark ); + photon->render->draw_rounded_rect( cur_pos2.x, cur_pos2.y, size.x, size.y, open ? colors::accent : colors::fg, 8 ); + photon->render->draw_rounded_rect( cur_pos2.x + 1, cur_pos2.y + 1, size.x - 2, size.y - 2, colors::bg, 8 ); std::string display_text; bool comma = false; @@ -318,9 +452,24 @@ void gui::framework::multicombo( std::size_t& val, const std::vector< std::strin } } - interfaces::surface->set_clip_rect( cur_pos.x + 8, cur_pos.y, cur_pos.x + size.x - 8, cur_pos.y + size.y ); - photon->render->draw_text( cur_pos.x + 8, cur_pos.y + 2, fonts::normal, colors::white, false, display_text.c_str( ) ); - interfaces::surface->set_clip_rect( cur_menu.pos.x + 1, cur_menu.pos.y + 1, cur_menu.pos.x + cur_menu.size.x - 1, cur_menu.pos.y + cur_menu.size.y - 1 ); + interfaces::surface->set_clip_rect( cur_pos2.x + 8, cur_pos2.y, cur_pos2.x + size.x - 8, cur_pos2.y + size.y ); + photon->render->draw_text( cur_pos2.x + 8, cur_pos2.y + 4, fonts::smaller, colors::text, false, util::to_upper( display_text ).c_str( ) ); + interfaces::surface->set_clip_rect( cur_menu.pos.x, cur_menu.pos.y, cur_menu.pos.x + cur_menu.size.x, cur_menu.pos.y + cur_menu.size.y ); + + cur_menu.cursor.y += size.y + 16; +} + +void gui::framework::separator( const std::string& label ) { + auto cur_pos = cur_menu.pos + cur_menu.cursor; + cur_pos.y -= scroll_offset; + + const auto size = vec2_t( cur_menu.size.x - cur_menu.cursor.x - 20, 2 ); + + const auto text_size = photon->render->get_text_size( fonts::normal, util::to_upper( label ).c_str( ) ); + + photon->render->draw_text( cur_pos.x, cur_pos.y, fonts::normal, colors::text, false, util::to_upper( label ).c_str( ) ); + + photon->render->draw_filled_rect( cur_pos.x, cur_pos.y + text_size.y, size.x, size.y, colors::text ); - cur_menu.cursor.y += size.y + text_size.y + 4; + cur_menu.cursor.y += size.y + text_size.y + 8; } diff --git a/src/core/menu/framework.h b/src/core/menu/framework.h index 42b8454..784384c 100644 --- a/src/core/menu/framework.h +++ b/src/core/menu/framework.h @@ -8,17 +8,25 @@ namespace gui { namespace framework { namespace colors { - inline color_t bg = color_t( 0, 0, 0, 200 ); - inline color_t black = color_t( 0, 0, 0, 255 ); - inline color_t white = color_t( 255, 255, 255, 255 ); - inline color_t dark = color_t( 64, 64, 64, 255 ); - inline color_t darker = color_t( 32, 32, 32, 255 ); + // constant colors + inline color_t white = color_t( 255, 255, 255 ); + inline color_t gray = color_t( 64, 64, 64 ); + inline color_t green = color_t( 36, 140, 45 ); + inline color_t red = color_t( 168, 43, 43 ); + + // theme colors + inline color_t bg; + inline color_t fg; + inline color_t text; + inline color_t accent; + inline color_t disabled; } // namespace colors namespace fonts { + inline h_font smaller; inline h_font normal; inline h_font title; - inline h_font icon; + inline h_font bigtitle; } // namespace fonts struct menu_t { @@ -47,16 +55,30 @@ namespace gui { }; inline dropdown_t cur_dropdown; + struct colorpicker_t { + + }; + inline colorpicker_t cur_colorpicker; + + inline std::string cur_slider; // FIXME: make this somehow be only for current menu but still persist + inline int scroll_offset; // ditto + + void set_theme( bool dark = false ); + void begin( vec2_t pos, vec2_t size ); void end( ); - bool tab( int& selected, vec2_t pos, vec2_t size, const std::string& title ); - bool mod( mods::mod_info_t* info ); + bool tab( int& selected, vec2_t pos, vec2_t size, const std::string& label, bool use_texture = false ); + bool mod( mods::mod_info_t& info ); + void split( int width ); + bool icon_button( vec2_t size, const std::string& texture ); - bool button( vec2_t size, const std::string& label ); - bool checkbox( bool& val, const std::string& label ); + bool button( vec2_t size, const std::string& label, bool enabled = true, h_font font = fonts::normal, color_t color = colors::accent ); + bool toggle( bool& val, const std::string& label ); void slider( int& val, int min, int max, const std::string& label ); void sliderf( float& val, float min, float max, const std::string& label ); + void colorpicker( color_t& val, const std::string& label ); void combo( std::size_t& val, const std::vector< std::string >& items, const std::string& label ); void multicombo( std::size_t& val, const std::vector< std::string >& items, const std::string& label ); + void separator( const std::string& label ); } // namespace framework } // namespace gui diff --git a/src/core/menu/gui.cpp b/src/core/menu/gui.cpp index 2ad6bf4..665459f 100644 --- a/src/core/menu/gui.cpp +++ b/src/core/menu/gui.cpp @@ -1,5 +1,6 @@ #include "gui.h" +#include "../resource/resource.h" #include "core/convars/convars.h" #include "core/huds/huds.h" #include "core/interfaces/interfaces.h" @@ -12,7 +13,7 @@ SIGNAL_CALLBACK( void, __rescall, lock_cursor ) { static void* input_ctx = interfaces::engine_client->get_input_context( 0 ); - if ( gui::open ) { + if ( gui::open || huds::edit ) { interfaces::surface->unlock_cursor( ); interfaces::input_stack_system->set_cursor_visible( input_ctx, true ); @@ -35,13 +36,21 @@ SIGNAL_CALLBACK( void, __rescall, paint, paint_mode_t, mode ) { photon->common->post_event( &plugin, "paint" ); - if ( photon->input->get_key_press( key_insert ) ) - gui::open = !gui::open; + if ( photon->input->get_key_press( key_insert ) ) { + gui::open = !gui::open; + huds::edit = false; + } + + if ( photon->input->get_key_press( key_escape ) ) { + gui::open = true; + huds::edit = false; + } - if ( gui::open ) { + if ( gui::open ) gui::paint( ); + + if ( huds::edit ) huds::paint_ui( ); - } } interfaces::surface->finish_drawing( ); @@ -49,7 +58,7 @@ SIGNAL_CALLBACK( void, __rescall, paint, paint_mode_t, mode ) { // block input to the game when photon's menu is open, only works in game, not in the menu SIGNAL_CALLBACK( int, __rescall, in_key_event, int, eventcode, button_code_t, keynum, const char*, current_binding ) { - if ( gui::open ) + if ( gui::open || huds::edit ) return 0; return original( ecx, eventcode, keynum, current_binding ); @@ -57,7 +66,7 @@ SIGNAL_CALLBACK( int, __rescall, in_key_event, int, eventcode, button_code_t, ke // block input to the menu, vgui has its own input system for some reason, so we have to hook another function SIGNAL_CALLBACK( void, __rescall, update_button_state, const int*, event ) { - if ( gui::open ) { + if ( gui::open || huds::edit ) { /* * so we cant actually just return here because theres other * functions calling SetKeyCodeState and SetMouseCodeState, @@ -74,14 +83,24 @@ SIGNAL_CALLBACK( void, __rescall, update_button_state, const int*, event ) { } bool gui::initialize( ) { + framework::set_theme( false ); + + photon->render->create_font( framework::fonts::smaller, "D-DIN EXP", 16, false, fontflag_antialias ); + photon->render->create_font( framework::fonts::normal, "D-DIN EXP", 20, true, fontflag_antialias ); + photon->render->create_font( framework::fonts::title, "D-DIN EXP", 24, true, fontflag_antialias ); + photon->render->create_font( framework::fonts::bigtitle, "D-DIN EXP", 32, true, fontflag_antialias ); + + photon->render->load_texture( "photon_icon", resource::icons::photon, 50, 50, sizeof( resource::icons::photon ) ); + photon->render->load_texture( "photon_list", resource::icons::list, 32, 32, sizeof( resource::icons::list ) ); + photon->render->load_texture( "photon_gear", resource::icons::gear, 32, 32, sizeof( resource::icons::gear ) ); + photon->render->load_texture( "photon_left_arrow", resource::icons::left_arrow, 32, 32, sizeof( resource::icons::left_arrow ) ); + photon->render->load_texture( "photon_arrows", resource::icons::arrows, 32, 32, sizeof( resource::icons::arrows ) ); + photon->signal->get( "lock_cursor" )->add_callback( &lock_cursor_cbk ); photon->signal->get( "paint" )->add_callback( &paint_cbk ); photon->signal->get( "in_key_event" )->add_callback( &in_key_event_cbk ); photon->signal->get( "update_button_state" )->add_callback( &update_button_state_cbk ); - photon->render->create_font( framework::fonts::normal, "Segoe UI Light", 22, false, fontflag_antialias ); - photon->render->create_font( framework::fonts::title, "Segoe UI Light", 30, false, fontflag_antialias ); - dx9::initialize( ); return true; @@ -90,41 +109,64 @@ bool gui::initialize( ) { void gui::uninitialize( ) { dx9::uninitialize( ); + photon->render->destruct_font( framework::fonts::bigtitle ); photon->render->destruct_font( framework::fonts::title ); photon->render->destruct_font( framework::fonts::normal ); + photon->render->destruct_font( framework::fonts::smaller ); } void gui::paint( ) { const auto screen_size = photon->render->get_screen_size( ); + const auto screen_half = screen_size / 2; - const int tab_height = 50; - const auto menu_size = vec2_t( 700, 0.32f * screen_size.y ); - const auto menu_pos = vec2_t( screen_size.x / 2 - menu_size.x / 2, screen_size.y / 2 + tab_height / 2 + 8 ); + constexpr int tab_height = 56; + const auto menu_size = vec2_t( 740, 0.4f * screen_size.y ); + const auto menu_pos = vec2_t( screen_half.x - menu_size.x / 2, screen_half.y + tab_height / 2 + 12 ); + + static int tab = 1; - static int tab = 1; static photon_api::i_photon_mod* cur_mod; + // draw title + const auto title_size = photon->render->get_text_size( framework::fonts::bigtitle, "PHOTON" ); + photon->render->draw_texture( screen_half.x - title_size.x / 2 - 25 - 4, screen_half.y - 90, 50, 50, "photon_icon" ); + photon->render->draw_text( screen_half.x + 25 + 4, screen_half.y - 80, framework::fonts::bigtitle, framework::colors::white, true, "PHOTON" ); + framework::begin( menu_pos, menu_size ); - if ( framework::tab( tab, vec2_t( screen_size.x / 2 - 90 - 130 - 6, screen_size.y / 2 - tab_height / 2 ), vec2_t( 130, tab_height ), "profiles" ) ) { + if ( framework::tab( tab, { screen_half.x - 100 - tab_height - 8, screen_half.y - tab_height / 2 }, { tab_height, tab_height }, "photon_list", true ) ) { } - if ( framework::tab( tab, vec2_t( screen_size.x / 2 - 90, screen_size.y / 2 - tab_height / 2 ), vec2_t( 180, tab_height ), "modules" ) ) { + if ( framework::tab( tab, { screen_half.x - 100, screen_half.y - tab_height / 2 }, { 200, tab_height }, "MODS" ) ) { if ( !cur_mod ) { for ( auto& mod : mods::mod_list ) { - if ( framework::mod( &mod.second ) ) + if ( framework::mod( mod.second ) ) cur_mod = mod.second.ptr; } } else { - if ( framework::button( vec2_t( 80, 30 ), "< back" ) ) + if ( framework::icon_button( { 56, 56 }, "photon_left_arrow" ) ) cur_mod = nullptr; - if ( cur_mod ) + if ( framework::icon_button( { 56, 56 }, "photon_arrows" ) ) { + gui::open = false; + huds::edit = true; + } + + framework::split( 56 ); + + if ( cur_mod ) { + framework::separator( util::ssprintf( "%s settings", cur_mod->get_info( ).name ).c_str( ) ); + cur_mod->paint_menu( ); + } } } - if ( framework::tab( tab, vec2_t( screen_size.x / 2 + 90 + 6, screen_size.y / 2 - tab_height / 2 ), vec2_t( 130, tab_height ), "settings" ) ) { + if ( framework::tab( tab, { screen_half.x + 100 + 8, screen_half.y - tab_height / 2 }, { tab_height, tab_height }, "photon_gear", true ) ) { + static bool dark_mode = false; + if ( framework::toggle( dark_mode, "dark mode" ) ) + framework::set_theme( dark_mode ); + static bool fast_loads = true; - if ( framework::checkbox( fast_loads, "fast loads" ) ) + if ( framework::toggle( fast_loads, "fast loads" ) ) convars::set_fast_loads( fast_loads ); framework::slider( huds::safezone_x, 0, 32, "hud safezone x" ); diff --git a/src/core/shared/menu.cpp b/src/core/shared/menu.cpp index fadc210..a6e35ed 100644 --- a/src/core/shared/menu.cpp +++ b/src/core/shared/menu.cpp @@ -9,8 +9,8 @@ bool c_menu::button( vec2_t size, const char* label ) { return gui::framework::button( size, label ); } -bool c_menu::checkbox( bool& val, const char* label ) { - return gui::framework::checkbox( val, label ); +bool c_menu::toggle( bool& val, const char* label ) { + return gui::framework::toggle( val, label ); } void c_menu::slider( int& val, int min, int max, const char* label ) { return gui::framework::slider( val, min, max, label ); @@ -36,3 +36,6 @@ void c_menu::multicombo( std::size_t& val, const char* items[], std::size_t item return gui::framework::multicombo( val, items_vector, label ); } +void c_menu::separator( const char* label ) { + return gui::framework::separator( label ); +} diff --git a/src/core/shared/render.cpp b/src/core/shared/render.cpp index 7c56bf1..85a0f12 100644 --- a/src/core/shared/render.cpp +++ b/src/core/shared/render.cpp @@ -1,6 +1,11 @@ #include "core/interfaces/interfaces.h" #include "sdk/photon.h" +#include +#include + +static std::unordered_map< std::string, int > texture_ids; + void c_render::draw_filled_rect( int x, int y, int w, int h, color_t color ) { interfaces::surface->draw_set_color( color.r, color.g, color.b, color.a ); interfaces::surface->draw_filled_rect( x, y, x + w, y + h ); @@ -18,25 +23,55 @@ void c_render::draw_line( int x, int y, int w, int h, color_t color ) { interfaces::surface->draw_line( x, y, x + w, y + h ); } -bool c_render::create_font( h_font& font, const char* font_name, int size, bool bold, int flags ) { - font = interfaces::surface->create_font( ); - return interfaces::surface->set_font_glyph_set( font, font_name, size, bold ? 800 : 0, 0, 0, flags ); +void c_render::draw_polygon( int n, vertex_t* vertices, color_t color ) { + // create plain white texture + static int plain_tex = interfaces::surface->create_new_texture_id( true ); + static uint8_t rgba[ 4 ] = { 255, 255, 255, 255 }; + interfaces::surface->draw_set_texture_rgba( plain_tex, rgba, 1, 1 ); + + interfaces::surface->draw_set_texture( plain_tex ); + interfaces::surface->draw_set_color( color.r, color.g, color.b, color.a ); + + interfaces::surface->draw_textured_polygon( n, vertices ); } -void c_render::destruct_font( h_font font ) { - /* - * seems like the game doesnt have a way of destructing a specific font, - * so we have to do it ourselves - */ +void c_render::draw_rounded_rect( int x, int y, int w, int h, color_t color, int rounding ) { + if ( rounding < 2 ) + return; - for ( const auto& font_range : interfaces::font_manager->font_amalgams[ font ].fonts ) { - const int idx = interfaces::font_manager->win32_fonts.find( font_range.win32_font ); + const int n_verts = rounding * 4; - interfaces::mem_alloc->free( interfaces::font_manager->win32_fonts[ idx ] ); - interfaces::font_manager->win32_fonts.remove( idx ); + vertex_t* verts = new vertex_t[ 4 * n_verts ]; + for ( int i = 0; i < 4; ++i ) { + int _x = x + ( i < 2 ? w - rounding : rounding ); + int _y = y + ( i % 3 ? h - rounding : rounding ); + + float ang = 90.f * i; + + for ( int j = 0; j < n_verts; j++ ) { + float rad = math::deg_to_rad( ang + ( j / ( float ) ( n_verts - 1 ) ) * 90.f ); + + verts[ i * n_verts + j ] = vertex_t( vec2_t( _x + rounding * sin( rad ), _y - rounding * cos( rad ) ) ); + } } - interfaces::font_manager->font_amalgams.remove( font ); + draw_polygon( 4 * rounding * 4, verts, color ); + + delete verts; +} + +void c_render::draw_circle( int x, int y, int radius, color_t color ) { + vertex_t verts[ 360 ]; + for ( int i = 0; i < 360; ++i ) { + auto& pos = verts[ i ].position; + + float rad = math::deg_to_rad( i ); + + pos.x = x + cos( rad ) * radius; + pos.y = y + sin( rad ) * radius; + } + + draw_polygon( 360, verts, color ); } void c_render::draw_text( int x, int y, h_font font, color_t color, bool center, const char* text ) { @@ -51,21 +86,18 @@ void c_render::draw_text( int x, int y, h_font font, color_t color, bool center, interfaces::surface->draw_colored_text( font, text_x, text_y, color.r, color.g, color.b, color.a, text ); } -vec2_t c_render::get_text_size( h_font font, const char* text ) { - int text_width, text_height; - - std::string stxt = text; - interfaces::surface->get_text_size( font, std::wstring( stxt.begin( ), stxt.end( ) ).c_str( ), text_width, text_height ); - - return vec2_t( text_width, text_height ); -} - void c_render::draw_texture( int x, int y, int w, int h, const char* texture, color_t color ) { // check if texture already exists int id = interfaces::surface->draw_get_texture_id( texture ); - if ( !id ) { - id = interfaces::surface->create_new_texture_id( true ); - interfaces::surface->draw_set_texture_file( id, texture, false, true ); + if ( id < 1 ) { + // check if its a user-loaded texture + if ( texture_ids.contains( texture ) ) + id = texture_ids[ texture ]; + else { + // doesnt exist at all, fallback to loading it from game + id = interfaces::surface->create_new_texture_id( true ); + interfaces::surface->draw_set_texture_file( id, texture, false, true ); + } } interfaces::surface->draw_set_texture( id ); @@ -73,6 +105,50 @@ void c_render::draw_texture( int x, int y, int w, int h, const char* texture, co interfaces::surface->draw_textured_rect( x, y, x + w, y + h ); } +void c_render::load_texture( const char* name, const uint8_t* png, int w, int h, size_t size ) { + std::vector< uint8_t > rgba; + + auto _w = std::uint32_t( w ); + auto _h = std::uint32_t( h ); + + lodepng::decode( rgba, _w, _h, png, size ); + + int id = interfaces::surface->create_new_texture_id( true ); + interfaces::surface->draw_set_texture_rgba( id, rgba.data( ), w, h ); + + texture_ids.insert( std::make_pair( name, id ) ); +} + +bool c_render::create_font( h_font& font, const char* font_name, int size, bool bold, int flags ) { + font = interfaces::surface->create_font( ); + return interfaces::surface->set_font_glyph_set( font, font_name, size, bold ? 700 : 400, 0, 1, flags ); +} + +void c_render::destruct_font( h_font font ) { + /* + * seems like the game doesnt have a way of destructing a specific font, + * so we have to do it ourselves + */ + + for ( const auto& font_range : interfaces::font_manager->font_amalgams[ font ].fonts ) { + const int idx = interfaces::font_manager->win32_fonts.find( font_range.win32_font ); + + interfaces::mem_alloc->free( interfaces::font_manager->win32_fonts[ idx ] ); + interfaces::font_manager->win32_fonts.remove( idx ); + } + + interfaces::font_manager->font_amalgams.remove( font ); +} + +vec2_t c_render::get_text_size( h_font font, const char* text ) { + int text_width, text_height; + + std::string stxt = text; + interfaces::surface->get_text_size( font, std::wstring( stxt.begin( ), stxt.end( ) ).c_str( ), text_width, text_height ); + + return vec2_t( text_width, text_height ); +} + vec2_t c_render::get_screen_size( ) { int w, h; diff --git a/src/photon.vcxproj b/src/photon.vcxproj index b91cc7a..2c066e0 100644 --- a/src/photon.vcxproj +++ b/src/photon.vcxproj @@ -11,6 +11,7 @@ <_WildCardClCompile Include="..\vendor\subhook\subhook.c" /> + <_WildCardClCompile Include="..\vendor\lodepng\lodepng.cpp" /> <_WildCardClCompile Include=".\**\*.cpp" /> <_WildCardClInclude Include=".\**\*.h" /> diff --git a/src/sdk/menu.h b/src/sdk/menu.h index e3041f0..3a5aa69 100644 --- a/src/sdk/menu.h +++ b/src/sdk/menu.h @@ -7,10 +7,11 @@ class c_menu { public: virtual bool button( vec2_t size, const char* label ); // Returns true on button click. - virtual bool checkbox( bool& val, const char* label ); // Checkbox using boolean value. + virtual bool toggle( bool& val, const char* label ); // Toggle using boolean value. virtual void slider( int& val, int min, int max, const char* label ); // Slider using integer values. virtual void sliderf( float& val, float min, float max, const char* label ); // Slider using float values. virtual void colorpicker( color_t& val, const char* label ); // Colorpicker using a 4 value datatype (R, G, B, A). virtual void combo( std::size_t& val, const char* items[], std::size_t items_count, const char* label ); // Value is the index of the selected element. virtual void multicombo( std::size_t& val, const char* items[], std::size_t items_count, const char* label ); // Value is a bitmask containing selected elements. + virtual void separator( const char* label ); // Separator with a label on it. }; diff --git a/src/sdk/photon.h b/src/sdk/photon.h index e7cc97a..cd663ac 100644 --- a/src/sdk/photon.h +++ b/src/sdk/photon.h @@ -24,6 +24,7 @@ namespace photon_api { struct mod_info_t { const char* name; // Name of mod. + const char* author; // Author of mod. const char* version; // Version of mod using semantic versioning. }; diff --git a/src/sdk/render.h b/src/sdk/render.h index 693c04c..57fb8f7 100644 --- a/src/sdk/render.h +++ b/src/sdk/render.h @@ -9,11 +9,15 @@ class c_render { virtual void draw_filled_rect( int x, int y, int w, int h, color_t color ); virtual void draw_outlined_rect( int x, int y, int w, int h, color_t color, int stroke_width = 1 ); virtual void draw_line( int x, int y, int w, int h, color_t color ); + virtual void draw_polygon( int n, vertex_t* vertices, color_t color ); + virtual void draw_rounded_rect( int x, int y, int w, int h, color_t color, int rounding ); + virtual void draw_circle( int x, int y, int radius, color_t color ); + virtual void draw_text( int x, int y, h_font font, color_t color, bool center, const char* text ); + virtual void draw_texture( int x, int y, int w, int h, const char* texture, color_t color = color_t( 255, 255, 255, 255 ) ); + virtual void load_texture( const char* name, const uint8_t* png, int w, int h, size_t size ); virtual bool create_font( h_font& font, const char* font_name, int size, bool bold, int flags ); virtual void destruct_font( h_font font ); - virtual void draw_text( int x, int y, h_font font, color_t color, bool center, const char* text ); virtual vec2_t get_text_size( h_font font, const char* text ); - virtual void draw_texture( int x, int y, int w, int h, const char* texture, color_t color = color_t( 255, 255, 255, 255 ) ); virtual vec2_t get_screen_size( ); virtual h_font get_font( unsigned long id ); virtual vec2_t normalize( vec2_t vec ); // Translate screen X, Y pixel position to a normal value between (0, 1). diff --git a/src/util/util.cpp b/src/util/util.cpp index b99599f..91a46bb 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -3,6 +3,7 @@ #include "memory.h" #include "string.h" +#include #include #include #include @@ -172,3 +173,8 @@ bool util::replace( std::string& str, const std::string& from, const std::string str.replace( start_pos, from.length( ), to ); return true; } + +std::string util::to_upper( std::string str ) { + std::transform( str.begin( ), str.end( ), str.begin( ), ::toupper ); + return str; +} diff --git a/src/util/util.h b/src/util/util.h index 8fbb2cc..81f0bf1 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -85,4 +85,5 @@ namespace util { std::string ssprintf( const char* fmt, ... ); bool replace( std::string& str, const std::string& from, const std::string& to ); + std::string to_upper( std::string str ); } // namespace util diff --git a/tests/photon.tests.vcxproj b/tests/photon.tests.vcxproj index 314d886..8a68e40 100644 --- a/tests/photon.tests.vcxproj +++ b/tests/photon.tests.vcxproj @@ -104,6 +104,7 @@ <_WildCardClCompile Include="..\vendor\subhook\subhook.c" /> + <_WildCardClCompile Include="..\vendor\lodepng\lodepng.cpp" /> <_WildCardClCompile Include="..\src\**\*.cpp" /> <_WildCardClCompile Include=".\**\*.cpp" /> <_WildCardClInclude Include=".\**\*.h" />