From 015a9cc4e1de604f261231df2a118e38a92b100f Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Thu, 2 Apr 2026 07:59:30 +0200 Subject: [PATCH] =?UTF-8?q?-=20restaurades=20les=20paletes=20amb=20la=20or?= =?UTF-8?q?denacio=20original=20-=20afegida=20opci=C3=B3=20de=20reordenar?= =?UTF-8?q?=20les=20paletes=20automaticament=20per=20luminositat=20o=20par?= =?UTF-8?q?egut=20a=20la=20paleta=20d'spectrum?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/console/commands.yaml | 4 +- data/locale/ca.yaml | 1 + data/locale/en.yaml | 1 + data/palette/aged-terracotta.pal | 38 ++--- data/palette/antiquity.pal | 38 ++--- data/palette/bubblegum.pal | 38 ++--- data/palette/darkseed.pal | 38 ++--- data/palette/h16da.pal | 38 ++--- data/palette/island-joy.pal | 38 ++--- data/palette/lost-century.pal | 38 ++--- data/palette/na.pal | 38 ++--- data/palette/pico-8.pal | 38 ++--- data/palette/psychic-fibre.pal | 38 ++--- data/palette/ruzx-spectrum-revision-2.pal | 38 ++--- data/palette/ruzx-spectrum.pal | 38 ++--- data/palette/shido-cyberneon.pal | 38 ++--- data/palette/steam-lords.pal | 38 ++--- data/palette/sweetie.pal | 38 ++--- data/palette/vanilla-milkshake.pal | 38 ++--- data/palette/winds-seed-pc98.pal | 38 ++--- source/core/input/global_inputs.cpp | 18 ++- source/core/input/input.cpp | 2 +- source/core/input/input_types.cpp | 2 + source/core/input/input_types.hpp | 1 + source/core/rendering/palette_manager.cpp | 164 +++++++++++++++++++++- source/core/rendering/palette_manager.hpp | 32 ++++- source/core/rendering/screen.cpp | 4 + source/core/rendering/screen.hpp | 3 + source/game/defaults.hpp | 1 + source/game/options.cpp | 21 ++- source/game/options.hpp | 23 +-- source/game/ui/console_commands.cpp | 26 +++- 32 files changed, 595 insertions(+), 354 deletions(-) diff --git a/data/console/commands.yaml b/data/console/commands.yaml index 4314c10..3ab21b5 100644 --- a/data/console/commands.yaml +++ b/data/console/commands.yaml @@ -80,7 +80,9 @@ categories: - keyword: PALETTE handler: cmd_palette description: "Color palette (F5/F6)" - usage: "PALETTE [NEXT|PREV|]" + usage: "PALETTE [NEXT|PREV|SORT [ORIGINAL|LUMINANCE|SPECTRUM]|DEFAULT|]" + completions: + PALETTE SORT: [ORIGINAL, LUMINANCE, SPECTRUM] dynamic_completions: true - name: AUDIO diff --git a/data/locale/ca.yaml b/data/locale/ca.yaml index cc15d09..cb46f7d 100644 --- a/data/locale/ca.yaml +++ b/data/locale/ca.yaml @@ -116,6 +116,7 @@ ui: supersampling_enabled: "SUPERMOSTREIG ACTIVAT" supersampling_disabled: "SUPERMOSTREIG DESACTIVAT" palette: "PALETA" + palette_sort: "ORDENACIÓ PALETA" integer_scale_enabled: "ESCALAT SENCER ACTIVAT" integer_scale_disabled: "ESCALAT SENCER DESACTIVAT" vsync_enabled: "V-SYNC ACTIVAT" diff --git a/data/locale/en.yaml b/data/locale/en.yaml index a98ef2e..8748fc1 100644 --- a/data/locale/en.yaml +++ b/data/locale/en.yaml @@ -116,6 +116,7 @@ ui: supersampling_enabled: "SUPERSAMPLING ON" supersampling_disabled: "SUPERSAMPLING OFF" palette: "PALETTE" + palette_sort: "PALETTE SORT" integer_scale_enabled: "INTEGER SCALE ENABLED" integer_scale_disabled: "INTEGER SCALE DISABLED" vsync_enabled: "V-SYNC ENABLED" diff --git a/data/palette/aged-terracotta.pal b/data/palette/aged-terracotta.pal index 84c1eb9..4b864af 100644 --- a/data/palette/aged-terracotta.pal +++ b/data/palette/aged-terracotta.pal @@ -1,19 +1,19 @@ -JASC-PAL -0100 -16 -52 49 40 -80 73 57 -92 104 82 -108 116 76 -125 130 73 -163 158 85 -202 181 103 -119 63 53 -132 86 64 -160 119 84 -188 153 120 -214 193 157 -234 220 193 -247 240 221 -255 251 237 -255 255 255 +JASC-PAL +0100 +16 +52 49 40 +80 73 57 +92 104 82 +108 116 76 +125 130 73 +163 158 85 +202 181 103 +119 63 53 +132 86 64 +160 119 84 +188 153 120 +214 193 157 +234 220 193 +247 240 221 +255 251 237 +255 255 255 diff --git a/data/palette/antiquity.pal b/data/palette/antiquity.pal index 677cff5..d166df4 100644 --- a/data/palette/antiquity.pal +++ b/data/palette/antiquity.pal @@ -1,19 +1,19 @@ -JASC-PAL -0100 -16 -32 32 32 -45 33 30 -69 41 35 -109 61 41 -177 107 74 -232 159 110 -232 190 130 -93 117 87 -142 146 87 -112 123 136 -138 167 172 -229 93 77 -241 134 108 -210 103 48 -222 154 40 -232 216 165 +JASC-PAL +0100 +16 +32 32 32 +45 33 30 +69 41 35 +109 61 41 +177 107 74 +232 159 110 +232 190 130 +93 117 87 +142 146 87 +112 123 136 +138 167 172 +229 93 77 +241 134 108 +210 103 48 +222 154 40 +232 216 165 diff --git a/data/palette/bubblegum.pal b/data/palette/bubblegum.pal index b49f6e8..893f0bb 100644 --- a/data/palette/bubblegum.pal +++ b/data/palette/bubblegum.pal @@ -1,19 +1,19 @@ -JASC-PAL -0100 -16 -67 0 103 -22 23 26 -127 6 34 -0 40 89 -148 33 106 -35 73 117 -214 36 17 -255 38 116 -0 120 153 -255 132 38 -255 128 164 -104 174 212 -16 210 117 -255 209 0 -191 255 60 -250 253 255 +JASC-PAL +0100 +16 +22 23 26 +127 6 34 +214 36 17 +255 132 38 +255 209 0 +250 253 255 +255 128 164 +255 38 116 +148 33 106 +67 0 103 +35 73 117 +104 174 212 +191 255 60 +16 210 117 +0 120 153 +0 40 89 diff --git a/data/palette/darkseed.pal b/data/palette/darkseed.pal index 8c38a03..7cd1294 100644 --- a/data/palette/darkseed.pal +++ b/data/palette/darkseed.pal @@ -1,19 +1,19 @@ -JASC-PAL -0100 -16 -0 0 0 -0 20 24 -0 32 36 -0 44 56 -20 52 68 -68 52 68 -88 60 72 -108 76 68 -128 96 88 -108 112 108 -136 128 120 -164 148 132 -196 172 156 -216 176 168 -236 212 208 -252 252 252 +JASC-PAL +0100 +16 +0 0 0 +0 20 24 +0 32 36 +0 44 56 +20 52 68 +68 52 68 +88 60 72 +108 76 68 +128 96 88 +108 112 108 +136 128 120 +164 148 132 +196 172 156 +216 176 168 +236 212 208 +252 252 252 diff --git a/data/palette/h16da.pal b/data/palette/h16da.pal index 0ad0367..00e7e63 100644 --- a/data/palette/h16da.pal +++ b/data/palette/h16da.pal @@ -1,19 +1,19 @@ -JASC-PAL -0100 -16 -31 32 37 -46 51 77 -76 82 116 -124 143 178 -128 77 83 -191 125 133 -114 51 76 -192 165 169 -82 103 93 -108 154 154 -108 154 154 -226 217 228 -243 200 147 -229 152 125 -192 165 169 -226 217 228 +JASC-PAL +0100 +16 +226 217 228 +108 154 154 +82 103 93 +55 64 59 +243 200 147 +229 152 125 +203 94 92 +114 51 76 +192 165 169 +191 125 133 +128 77 83 +64 48 56 +124 143 178 +76 82 116 +46 51 77 +31 32 37 diff --git a/data/palette/island-joy.pal b/data/palette/island-joy.pal index 712408e..81f6d53 100644 --- a/data/palette/island-joy.pal +++ b/data/palette/island-joy.pal @@ -1,19 +1,19 @@ -JASC-PAL -0100 -16 -57 52 87 -106 55 113 -201 36 100 -203 77 104 -96 108 129 -30 136 117 -17 173 193 -155 156 130 -91 179 97 -249 146 82 -244 140 182 -247 182 158 -161 229 90 -109 247 193 -247 228 118 -255 255 255 +JASC-PAL +0100 +16 +255 255 255 +109 247 193 +17 173 193 +96 108 129 +57 52 87 +30 136 117 +91 179 97 +161 229 90 +247 228 118 +249 146 82 +203 77 104 +106 55 113 +201 36 100 +244 140 182 +247 182 158 +155 156 130 diff --git a/data/palette/lost-century.pal b/data/palette/lost-century.pal index e32e8e4..f12b5ef 100644 --- a/data/palette/lost-century.pal +++ b/data/palette/lost-century.pal @@ -1,19 +1,19 @@ -JASC-PAL -0100 -16 -75 61 68 -77 69 57 -87 72 82 -121 68 74 -75 114 110 -174 93 64 -119 116 59 -146 116 65 -132 120 117 -199 123 88 -186 145 88 -171 155 142 -179 165 85 -140 171 161 -209 177 135 -210 201 165 +JASC-PAL +0100 +16 +209 177 135 +199 123 88 +174 93 64 +121 68 74 +75 61 68 +186 145 88 +146 116 65 +77 69 57 +119 116 59 +179 165 85 +210 201 165 +140 171 161 +75 114 110 +87 72 82 +132 120 117 +171 155 142 diff --git a/data/palette/na.pal b/data/palette/na.pal index 0a06898..2da3bb5 100644 --- a/data/palette/na.pal +++ b/data/palette/na.pal @@ -1,19 +1,19 @@ -JASC-PAL -0100 -16 -31 14 28 -62 33 55 -23 67 75 -157 48 59 -112 55 127 -88 69 99 -154 99 72 -100 125 52 -52 133 157 -210 100 113 -140 143 174 -228 148 58 -215 155 125 -126 196 193 -192 199 65 -245 237 186 +JASC-PAL +0100 +16 +140 143 174 +88 69 99 +62 33 55 +154 99 72 +215 155 125 +245 237 186 +192 199 65 +100 125 52 +228 148 58 +157 48 59 +210 100 113 +112 55 127 +126 196 193 +52 133 157 +23 67 75 +31 14 28 diff --git a/data/palette/pico-8.pal b/data/palette/pico-8.pal index 9d5c726..2bdfede 100644 --- a/data/palette/pico-8.pal +++ b/data/palette/pico-8.pal @@ -1,19 +1,19 @@ -JASC-PAL -0100 -16 -0 0 0 -29 43 83 -41 173 255 -131 118 156 -255 0 77 -171 82 54 -255 119 168 -194 195 199 -0 228 54 -0 135 81 -95 87 79 -255 241 232 -255 236 39 -255 163 0 -255 204 170 -126 37 83 +JASC-PAL +0100 +16 +0 0 0 +29 43 83 +126 37 83 +0 135 81 +171 82 54 +95 87 79 +194 195 199 +255 241 232 +255 0 77 +255 163 0 +255 236 39 +0 228 54 +41 173 255 +131 118 156 +255 119 168 +255 204 170 diff --git a/data/palette/psychic-fibre.pal b/data/palette/psychic-fibre.pal index baa1486..6702163 100644 --- a/data/palette/psychic-fibre.pal +++ b/data/palette/psychic-fibre.pal @@ -1,19 +1,19 @@ -JASC-PAL -0100 -16 -10 8 25 -49 50 67 -69 59 70 -87 84 117 -130 105 128 -164 111 114 -185 115 113 -205 95 105 -229 76 81 -201 55 73 -144 161 168 -140 147 137 -195 150 145 -236 151 134 -235 171 145 -219 182 167 +JASC-PAL +0100 +16 +10 8 25 +49 50 67 +69 59 70 +87 84 117 +130 105 128 +164 111 114 +185 115 113 +205 95 105 +229 76 81 +201 55 73 +144 161 168 +140 147 137 +195 150 145 +236 151 134 +235 171 145 +219 182 167 diff --git a/data/palette/ruzx-spectrum-revision-2.pal b/data/palette/ruzx-spectrum-revision-2.pal index 8d35aca..d5f5a3f 100644 --- a/data/palette/ruzx-spectrum-revision-2.pal +++ b/data/palette/ruzx-spectrum-revision-2.pal @@ -1,19 +1,19 @@ -JASC-PAL -0100 -16 -17 17 37 -36 34 114 -52 112 190 -150 58 191 -255 94 57 -159 32 98 -255 105 246 -176 201 196 -44 126 75 -160 195 95 -67 152 196 -147 255 229 -210 133 55 -254 245 107 -255 252 241 -82 75 109 +JASC-PAL +0100 +16 +17 17 37 +82 75 109 +176 201 196 +255 252 241 +36 34 114 +52 112 190 +159 32 98 +255 94 57 +150 58 191 +255 105 246 +44 126 75 +160 195 95 +67 152 196 +147 255 229 +210 133 55 +254 245 107 diff --git a/data/palette/ruzx-spectrum.pal b/data/palette/ruzx-spectrum.pal index 045afe3..08fc189 100644 --- a/data/palette/ruzx-spectrum.pal +++ b/data/palette/ruzx-spectrum.pal @@ -1,19 +1,19 @@ -JASC-PAL -0100 -16 -15 11 56 -122 87 22 -40 19 160 -74 107 255 -160 35 17 -237 23 95 -238 20 181 -115 16 147 -39 139 97 -157 255 38 -71 233 223 -27 105 167 -247 229 77 -173 180 183 -249 255 236 -97 106 130 +JASC-PAL +0100 +16 +15 11 56 +97 106 130 +173 180 183 +249 255 236 +40 19 160 +74 107 255 +160 35 17 +237 23 95 +115 16 147 +238 20 181 +39 139 97 +157 255 38 +27 105 167 +71 233 223 +122 87 22 +247 229 77 diff --git a/data/palette/shido-cyberneon.pal b/data/palette/shido-cyberneon.pal index e528589..d2772ba 100644 --- a/data/palette/shido-cyberneon.pal +++ b/data/palette/shido-cyberneon.pal @@ -1,19 +1,19 @@ -JASC-PAL -0100 -16 -0 3 60 -0 82 96 -0 56 132 -42 46 121 -255 0 78 -177 5 133 -172 41 206 -255 92 255 -10 255 82 -0 157 74 -0 247 255 -0 138 197 -78 110 168 -255 255 255 -173 212 250 -96 0 136 +JASC-PAL +0100 +16 +0 3 60 +0 82 96 +0 157 74 +10 255 82 +0 56 132 +0 138 197 +0 247 255 +255 92 255 +172 41 206 +96 0 136 +177 5 133 +255 0 78 +42 46 121 +78 110 168 +173 212 250 +255 255 255 diff --git a/data/palette/steam-lords.pal b/data/palette/steam-lords.pal index dd6e5ca..8e270d2 100644 --- a/data/palette/steam-lords.pal +++ b/data/palette/steam-lords.pal @@ -1,19 +1,19 @@ -JASC-PAL -0100 -16 -23 14 25 -47 33 59 -59 33 55 -33 59 37 -67 58 96 -96 59 58 -79 82 119 -58 96 74 -119 92 79 -79 119 84 -101 115 140 -119 116 79 -124 148 161 -161 159 124 -160 185 186 -192 209 204 +JASC-PAL +0100 +16 +33 59 37 +58 96 74 +79 119 84 +161 159 124 +119 116 79 +119 92 79 +96 59 58 +59 33 55 +23 14 25 +47 33 59 +67 58 96 +79 82 119 +101 115 140 +124 148 161 +160 185 186 +192 209 204 diff --git a/data/palette/sweetie.pal b/data/palette/sweetie.pal index 18ae400..ae04d13 100644 --- a/data/palette/sweetie.pal +++ b/data/palette/sweetie.pal @@ -1,19 +1,19 @@ -JASC-PAL -0100 -16 -26 28 44 -51 60 87 -59 93 201 -41 54 111 -177 62 83 -239 125 87 -93 39 93 -148 176 194 -56 183 100 -37 113 121 -65 166 246 -115 239 247 -255 205 117 -167 240 112 -244 244 244 -86 108 134 +JASC-PAL +0100 +16 +26 28 44 +93 39 93 +177 62 83 +239 125 87 +255 205 117 +167 240 112 +56 183 100 +37 113 121 +41 54 111 +59 93 201 +65 166 246 +115 239 247 +244 244 244 +148 176 194 +86 108 134 +51 60 87 diff --git a/data/palette/vanilla-milkshake.pal b/data/palette/vanilla-milkshake.pal index 83b12c7..07b007f 100644 --- a/data/palette/vanilla-milkshake.pal +++ b/data/palette/vanilla-milkshake.pal @@ -1,19 +1,19 @@ -JASC-PAL -0100 -16 -40 40 46 -108 86 113 -217 200 191 -249 130 132 -176 169 228 -172 204 228 -179 227 218 -254 170 228 -135 168 137 -176 235 147 -233 245 157 -255 230 198 -222 163 139 -255 195 132 -255 247 160 -255 247 228 +JASC-PAL +0100 +16 +40 40 46 +108 86 113 +217 200 191 +249 130 132 +176 169 228 +172 204 228 +179 227 218 +254 170 228 +135 168 137 +176 235 147 +233 245 157 +255 230 198 +222 163 139 +255 195 132 +255 247 160 +255 247 228 diff --git a/data/palette/winds-seed-pc98.pal b/data/palette/winds-seed-pc98.pal index cdafe83..d4d18bc 100644 --- a/data/palette/winds-seed-pc98.pal +++ b/data/palette/winds-seed-pc98.pal @@ -1,19 +1,19 @@ -JASC-PAL -0100 -16 -0 0 0 -50 1 50 -50 50 171 -35 103 239 -254 1 69 -119 70 2 -118 50 118 -239 152 152 -1 137 84 -1 186 152 -152 186 220 -253 253 253 -254 239 69 -186 118 84 -254 205 205 -84 1 103 +JASC-PAL +0100 +16 +0 0 0 +50 1 50 +84 1 103 +118 50 118 +50 50 171 +35 103 239 +152 186 220 +253 253 253 +1 186 152 +1 137 84 +254 239 69 +119 70 2 +186 118 84 +254 1 69 +239 152 152 +254 205 205 diff --git a/source/core/input/global_inputs.cpp b/source/core/input/global_inputs.cpp index f187392..cc8f777 100644 --- a/source/core/input/global_inputs.cpp +++ b/source/core/input/global_inputs.cpp @@ -127,6 +127,11 @@ namespace GlobalInputs { Notifier::get()->show({Locale::get()->get("ui.palette") + " " + toUpper(Screen::get()->getPalettePrettyName())}); // NOLINT(readability-static-accessed-through-instance) } + void handleNextPaletteSortMode() { + Screen::get()->nextPaletteSortMode(); + Notifier::get()->show({Locale::get()->get("ui.palette_sort") + " " + toUpper(Screen::get()->getPaletteSortModeName())}); // NOLINT(readability-static-accessed-through-instance) + } + void handleToggleIntegerScale() { Screen::get()->toggleIntegerScale(); Screen::get()->setVideoMode(Options::video.fullscreen); @@ -172,10 +177,13 @@ namespace GlobalInputs { } } if (Input::get()->checkAction(InputAction::NEXT_PALETTE, Input::DO_NOT_ALLOW_REPEAT)) { - return InputAction::NEXT_PALETTE; + if ((SDL_GetModState() & SDL_KMOD_CTRL) != 0U) { + return InputAction::PREVIOUS_PALETTE; // Ctrl+F5 + } + return InputAction::NEXT_PALETTE; // F5 } - if (Input::get()->checkAction(InputAction::PREVIOUS_PALETTE, Input::DO_NOT_ALLOW_REPEAT)) { - return InputAction::PREVIOUS_PALETTE; + if (Input::get()->checkAction(InputAction::NEXT_PALETTE_SORT, Input::DO_NOT_ALLOW_REPEAT)) { + return InputAction::NEXT_PALETTE_SORT; // F6 } if (Input::get()->checkAction(InputAction::TOGGLE_INTEGER_SCALE, Input::DO_NOT_ALLOW_REPEAT)) { return InputAction::TOGGLE_INTEGER_SCALE; @@ -273,6 +281,10 @@ namespace GlobalInputs { handlePreviousPalette(); break; + case InputAction::NEXT_PALETTE_SORT: + handleNextPaletteSortMode(); + break; + case InputAction::TOGGLE_INTEGER_SCALE: handleToggleIntegerScale(); break; diff --git a/source/core/input/input.cpp b/source/core/input/input.cpp index b350ff9..7c2e949 100644 --- a/source/core/input/input.cpp +++ b/source/core/input/input.cpp @@ -45,7 +45,7 @@ Input::Input(std::string game_controller_db_path) {Action::TOGGLE_FULLSCREEN, KeyState{.scancode = SDL_SCANCODE_F3}}, {Action::TOGGLE_SHADER, KeyState{.scancode = SDL_SCANCODE_F4}}, {Action::NEXT_PALETTE, KeyState{.scancode = SDL_SCANCODE_F5}}, - {Action::PREVIOUS_PALETTE, KeyState{.scancode = SDL_SCANCODE_F6}}, + {Action::NEXT_PALETTE_SORT, KeyState{.scancode = SDL_SCANCODE_F6}}, {Action::TOGGLE_INTEGER_SCALE, KeyState{.scancode = SDL_SCANCODE_F7}}, {Action::TOGGLE_IN_GAME_MUSIC, KeyState{.scancode = SDL_SCANCODE_F8}}, {Action::TOGGLE_BORDER, KeyState{.scancode = SDL_SCANCODE_F9}}, diff --git a/source/core/input/input_types.cpp b/source/core/input/input_types.cpp index 333f3f9..611f9f2 100644 --- a/source/core/input/input_types.cpp +++ b/source/core/input/input_types.cpp @@ -20,6 +20,7 @@ const std::unordered_map ACTION_TO_STRING = { {InputAction::TOGGLE_IN_GAME_MUSIC, "TOGGLE_MUSIC"}, {InputAction::NEXT_PALETTE, "NEXT_PALETTE"}, {InputAction::PREVIOUS_PALETTE, "PREVIOUS_PALETTE"}, + {InputAction::NEXT_PALETTE_SORT, "NEXT_PALETTE_SORT"}, {InputAction::TOGGLE_SHADER, "TOGGLE_POSTFX"}, {InputAction::NEXT_SHADER_PRESET, "NEXT_POSTFX_PRESET"}, {InputAction::TOGGLE_INFO, "TOGGLE_DEBUG"}, @@ -42,6 +43,7 @@ const std::unordered_map STRING_TO_ACTION = { {"TOGGLE_MUSIC", InputAction::TOGGLE_IN_GAME_MUSIC}, {"NEXT_PALETTE", InputAction::NEXT_PALETTE}, {"PREVIOUS_PALETTE", InputAction::PREVIOUS_PALETTE}, + {"NEXT_PALETTE_SORT", InputAction::NEXT_PALETTE_SORT}, {"TOGGLE_POSTFX", InputAction::TOGGLE_SHADER}, {"NEXT_POSTFX_PRESET", InputAction::NEXT_SHADER_PRESET}, {"TOGGLE_DEBUG", InputAction::TOGGLE_INFO}, diff --git a/source/core/input/input_types.hpp b/source/core/input/input_types.hpp index 5a209af..90663c3 100644 --- a/source/core/input/input_types.hpp +++ b/source/core/input/input_types.hpp @@ -31,6 +31,7 @@ enum class InputAction : int { // Acciones de entrada posibles en el juego TOGGLE_IN_GAME_MUSIC, NEXT_PALETTE, PREVIOUS_PALETTE, + NEXT_PALETTE_SORT, TOGGLE_INFO, TOGGLE_CONSOLE, diff --git a/source/core/rendering/palette_manager.cpp b/source/core/rendering/palette_manager.cpp index d82b192..5a948fe 100644 --- a/source/core/rendering/palette_manager.cpp +++ b/source/core/rendering/palette_manager.cpp @@ -2,7 +2,9 @@ #include #include +#include #include +#include #include "core/rendering/surface.hpp" #include "core/resources/resource_cache.hpp" @@ -10,13 +12,136 @@ #include "game/options.hpp" #include "utils/utils.hpp" +// ── Conversión string ↔ PaletteSortMode ────────────────────────────────────── + +auto sortModeFromString(const std::string& str) -> PaletteSortMode { + const std::string lower = toLower(str); + if (lower == "luminance") { return PaletteSortMode::LUMINANCE; } + if (lower == "spectrum") { return PaletteSortMode::SPECTRUM; } + return PaletteSortMode::ORIGINAL; +} + +auto sortModeToString(PaletteSortMode mode) -> std::string { + switch (mode) { + case PaletteSortMode::LUMINANCE: + return "luminance"; + case PaletteSortMode::SPECTRUM: + return "spectrum"; + default: + return "original"; + } +} + +// ── Paleta de referencia ZX Spectrum (16 colores ARGB) ─────────────────────── + +namespace { + // Helpers para extraer componentes RGB de un color ARGB (0xAARRGGBB) + constexpr auto redOf(Uint32 c) -> int { return static_cast((c >> 16) & 0xFF); } + constexpr auto greenOf(Uint32 c) -> int { return static_cast((c >> 8) & 0xFF); } + constexpr auto blueOf(Uint32 c) -> int { return static_cast(c & 0xFF); } + + constexpr auto makeARGB(int r, int g, int b) -> Uint32 { + return (0xFFU << 24) | (static_cast(r) << 16) | (static_cast(g) << 8) | static_cast(b); + } + + // Paleta ZX Spectrum de referencia (misma que en tools/sort_palette/sort_palette.py) + constexpr std::array SPECTRUM_REFERENCE = { + makeARGB(0, 0, 0), + makeARGB(0, 0, 0), + makeARGB(0, 0, 216), + makeARGB(0, 0, 255), + makeARGB(216, 0, 0), + makeARGB(255, 0, 0), + makeARGB(216, 0, 216), + makeARGB(255, 0, 255), + makeARGB(0, 216, 0), + makeARGB(0, 255, 0), + makeARGB(0, 216, 216), + makeARGB(0, 255, 255), + makeARGB(216, 216, 0), + makeARGB(255, 255, 0), + makeARGB(216, 216, 216), + makeARGB(255, 255, 255), + }; + + // Luminancia percibida (ITU-R BT.709) + auto luminance(Uint32 color) -> double { + return 0.2126 * redOf(color) + 0.7152 * greenOf(color) + 0.0722 * blueOf(color); + } + + // Distancia euclídea al cuadrado en espacio RGB (no necesita sqrt para comparar) + auto rgbDistanceSq(Uint32 a, Uint32 b) -> int { + const int dr = redOf(a) - redOf(b); + const int dg = greenOf(a) - greenOf(b); + const int db = blueOf(a) - blueOf(b); + return dr * dr + dg * dg + db * db; + } + + // Cuenta los colores activos en la paleta (los que tienen alpha != 0) + auto countActiveColors(const Palette& palette) -> size_t { + size_t count = 0; + for (const auto& c : palette) { + if (c == 0) { break; } + ++count; + } + return count; + } + + // Ordenar por luminancia + auto sortByLuminance(const Palette& palette) -> Palette { + const size_t n = countActiveColors(palette); + std::vector colors(palette.begin(), palette.begin() + static_cast(n)); + std::sort(colors.begin(), colors.end(), [](Uint32 a, Uint32 b) { + return luminance(a) < luminance(b); + }); + + Palette result{}; + result.fill(0); + std::copy(colors.begin(), colors.end(), result.begin()); + return result; + } + + // Ordenar por similitud con la paleta ZX Spectrum (greedy matching) + auto sortBySpectrum(const Palette& palette) -> Palette { + const size_t n = countActiveColors(palette); + std::vector available(palette.begin(), palette.begin() + static_cast(n)); + std::vector result; + result.reserve(n); + + // Para cada color de referencia del Spectrum, buscar el más cercano disponible + const size_t refs = std::min(n, SPECTRUM_REFERENCE.size()); + for (size_t i = 0; i < refs && !available.empty(); ++i) { + const Uint32 ref = SPECTRUM_REFERENCE[i]; + auto best = std::min_element(available.begin(), available.end(), [ref](Uint32 a, Uint32 b) { + return rgbDistanceSq(a, ref) < rgbDistanceSq(b, ref); + }); + result.push_back(*best); + available.erase(best); + } + + // Si quedan colores sin asignar, añadirlos al final + for (const auto& c : available) { + result.push_back(c); + } + + Palette out{}; + out.fill(0); + std::copy(result.begin(), result.end(), out.begin()); + return out; + } +} // namespace + +// ── PaletteManager ─────────────────────────────────────────────────────────── + PaletteManager::PaletteManager( std::vector raw_paths, const std::string& initial_name, + PaletteSortMode initial_sort_mode, std::shared_ptr game_surface, std::shared_ptr border_surface, OnChangeCallback on_change) : palettes_(std::move(raw_paths)), + sort_mode_(initial_sort_mode), game_surface_(std::move(game_surface)), border_surface_(std::move(border_surface)), on_change_(std::move(on_change)) { @@ -24,7 +149,7 @@ PaletteManager::PaletteManager( // Leer y aplicar paleta inicial directamente desde el archivo // (Resource::Cache aún no está disponible en este punto del ciclo de vida) - const auto initial_palette = readPalFile(palettes_.at(current_)); + const auto initial_palette = sortPalette(readPalFile(palettes_.at(current_)), sort_mode_); game_surface_->setPalette(initial_palette); border_surface_->setPalette(initial_palette); @@ -83,9 +208,31 @@ auto PaletteManager::getPrettyName() const -> std::string { return name; } +void PaletteManager::nextSortMode() { + sort_mode_ = static_cast((static_cast(sort_mode_) + 1) % static_cast(PaletteSortMode::COUNT)); + Options::video.palette_sort = sortModeToString(sort_mode_); + apply(); +} + +void PaletteManager::setSortMode(PaletteSortMode mode) { + sort_mode_ = mode; + Options::video.palette_sort = sortModeToString(sort_mode_); + apply(); +} + +auto PaletteManager::getSortMode() const -> PaletteSortMode { + return sort_mode_; +} + +auto PaletteManager::getSortModeName() const -> std::string { + return sortModeToString(sort_mode_); +} + void PaletteManager::apply() { - game_surface_->loadPalette(Resource::Cache::get()->getPalette(palettes_.at(current_))); - border_surface_->loadPalette(Resource::Cache::get()->getPalette(palettes_.at(current_))); + Palette raw = Resource::Cache::get()->getPalette(palettes_.at(current_)); + Palette sorted = sortPalette(raw, sort_mode_); + game_surface_->loadPalette(sorted); + border_surface_->loadPalette(sorted); Options::video.palette = getCurrentName(); @@ -116,3 +263,14 @@ void PaletteManager::processPathList() { palette = getFileName(palette); } } + +auto PaletteManager::sortPalette(const Palette& palette, PaletteSortMode mode) -> Palette { + switch (mode) { + case PaletteSortMode::LUMINANCE: + return sortByLuminance(palette); + case PaletteSortMode::SPECTRUM: + return sortBySpectrum(palette); + default: + return palette; + } +} diff --git a/source/core/rendering/palette_manager.hpp b/source/core/rendering/palette_manager.hpp index e9872d4..7357eca 100644 --- a/source/core/rendering/palette_manager.hpp +++ b/source/core/rendering/palette_manager.hpp @@ -1,12 +1,30 @@ #pragma once +#include + +#include #include #include #include #include +// Alias de paleta (igual que en surface.hpp; evita incluir todo el header) +using Palette = std::array; + class Surface; +// Modo de ordenación de paletas +enum class PaletteSortMode : int { + ORIGINAL = 0, // Paleta tal cual viene del fichero + LUMINANCE = 1, // Ordenada por luminancia percibida + SPECTRUM = 2, // Reordenada para imitar la paleta ZX Spectrum + COUNT = 3 +}; + +// Conversión string ↔ PaletteSortMode +auto sortModeFromString(const std::string& str) -> PaletteSortMode; +auto sortModeToString(PaletteSortMode mode) -> std::string; + class PaletteManager { public: using OnChangeCallback = std::function; @@ -14,6 +32,7 @@ class PaletteManager { PaletteManager( std::vector raw_paths, const std::string& initial_name, + PaletteSortMode initial_sort_mode, std::shared_ptr game_surface, std::shared_ptr border_surface, OnChangeCallback on_change = nullptr); @@ -25,13 +44,20 @@ class PaletteManager { [[nodiscard]] auto getCurrentName() const -> std::string; // Nombre de la paleta actual (minúsculas, sin .pal) [[nodiscard]] auto getPrettyName() const -> std::string; // Nombre actual con guiones sustituidos por espacios + void nextSortMode(); // Cicla al siguiente modo de ordenación + void setSortMode(PaletteSortMode mode); // Establece un modo de ordenación concreto + [[nodiscard]] auto getSortMode() const -> PaletteSortMode; // Devuelve el modo de ordenación actual + [[nodiscard]] auto getSortModeName() const -> std::string; // Nombre del modo actual ("ORIGINAL", etc.) + private: - void apply(); // Aplica la paleta actual a ambas surfaces - [[nodiscard]] auto findIndex(const std::string& name) const -> size_t; // Localiza paleta por nombre en el vector - void processPathList(); // Extrae nombres de archivo de las rutas completas + void apply(); // Aplica la paleta actual a ambas surfaces + [[nodiscard]] auto findIndex(const std::string& name) const -> size_t; // Localiza paleta por nombre en el vector + void processPathList(); // Extrae nombres de archivo de las rutas completas + static auto sortPalette(const Palette& palette, PaletteSortMode mode) -> Palette; // Reordena una paleta según el modo std::vector palettes_; size_t current_{0}; + PaletteSortMode sort_mode_{PaletteSortMode::ORIGINAL}; std::shared_ptr game_surface_; std::shared_ptr border_surface_; OnChangeCallback on_change_; diff --git a/source/core/rendering/screen.cpp b/source/core/rendering/screen.cpp index e364335..5ec7998 100644 --- a/source/core/rendering/screen.cpp +++ b/source/core/rendering/screen.cpp @@ -85,6 +85,7 @@ Screen::Screen() { palette_manager_ = std::make_unique( Resource::List::get()->getListByType(Resource::List::Type::PALETTE), Options::video.palette, + sortModeFromString(Options::video.palette_sort), game_surface_, border_surface_, [this]() { @@ -449,6 +450,9 @@ auto Screen::setPaletteByName(const std::string& name) -> bool { return palette_ // Devuelve los nombres de paletas disponibles (minúsculas, sin extensión .pal) auto Screen::getPaletteNames() const -> std::vector { return palette_manager_->getNames(); } auto Screen::getPalettePrettyName() const -> std::string { return palette_manager_->getPrettyName(); } +void Screen::nextPaletteSortMode() { palette_manager_->nextSortMode(); } +void Screen::setPaletteSortMode(PaletteSortMode mode) { palette_manager_->setSortMode(mode); } +auto Screen::getPaletteSortModeName() const -> std::string { return palette_manager_->getSortModeName(); } // Limpia la game_surface_ void Screen::clearSurface(Uint8 index) { game_surface_->clear(index); } diff --git a/source/core/rendering/screen.hpp b/source/core/rendering/screen.hpp index 8c60079..20c1c6d 100644 --- a/source/core/rendering/screen.hpp +++ b/source/core/rendering/screen.hpp @@ -59,6 +59,9 @@ class Screen { auto setPaletteByName(const std::string& name) -> bool; // Cambia a paleta por nombre; false si no existe [[nodiscard]] auto getPaletteNames() const -> std::vector; // Nombres disponibles (minúsculas, sin .pal) [[nodiscard]] auto getPalettePrettyName() const -> std::string; // Nombre actual con guiones sustituidos por espacios + void nextPaletteSortMode(); // Cicla al siguiente modo de ordenación de paleta + void setPaletteSortMode(PaletteSortMode mode); // Establece modo de ordenación concreto + [[nodiscard]] auto getPaletteSortModeName() const -> std::string; // Nombre del modo de ordenación actual void toggleShaders(); // Activa/desactiva todos los shaders respetando current_shader void toggleSupersampling(); // Activa/desactiva el supersampling global void reloadPostFX(); // Recarga el shader del preset actual sin toggle diff --git a/source/game/defaults.hpp b/source/game/defaults.hpp index b5af0df..0cce1cc 100644 --- a/source/game/defaults.hpp +++ b/source/game/defaults.hpp @@ -30,6 +30,7 @@ namespace Defaults::Video { constexpr bool INTEGER_SCALE = true; // Escalado entero activado por defecto constexpr bool KEEP_ASPECT = true; // Mantener aspecto activado por defecto constexpr const char* PALETTE_NAME = "zx-spectrum"; // Paleta por defecto + constexpr const char* PALETTE_SORT = "original"; // Modo de ordenación de paleta por defecto constexpr bool LINEAR_UPSCALE = false; // Upscale NEAREST por defecto constexpr int DOWNSCALE_ALGO = 1; // Downscale Lanczos2 por defecto constexpr bool GPU_ACCELERATION = true; // Aceleración GPU activada por defecto diff --git a/source/game/options.cpp b/source/game/options.cpp index b79ab70..6db5636 100644 --- a/source/game/options.cpp +++ b/source/game/options.cpp @@ -288,12 +288,20 @@ namespace Options { // Helper: carga el campo palette; PaletteManager hará el fallback si el nombre no existe void loadPaletteFromYaml(const fkyaml::node& vid) { - if (!vid.contains("palette")) { return; } - try { - auto palette_str = vid["palette"].get_value(); - video.palette = toLower(palette_str); - } catch (...) { - video.palette = Defaults::Video::PALETTE_NAME; + if (vid.contains("palette")) { + try { + auto palette_str = vid["palette"].get_value(); + video.palette = toLower(palette_str); + } catch (...) { + video.palette = Defaults::Video::PALETTE_NAME; + } + } + if (vid.contains("palette_sort")) { + try { + video.palette_sort = toLower(vid["palette_sort"].get_value()); + } catch (...) { + video.palette_sort = Defaults::Video::PALETTE_SORT; + } } } @@ -748,6 +756,7 @@ namespace Options { file << " keep_aspect: " << (video.keep_aspect ? "true" : "false") << "\n"; file << " filter: " << filterToString(video.filter) << " # filter: nearest (pixel perfect) | linear (smooth)\n"; file << " palette: " << video.palette << "\n"; + file << " palette_sort: " << video.palette_sort << "\n"; file << " border:\n"; file << " enabled: " << (video.border.enabled ? "true" : "false") << "\n"; file << " width: " << video.border.width << "\n"; diff --git a/source/game/options.hpp b/source/game/options.hpp index a8d98b3..d94d8d2 100644 --- a/source/game/options.hpp +++ b/source/game/options.hpp @@ -100,17 +100,18 @@ namespace Options { // Estructura para las opciones de video struct Video { - bool fullscreen{Defaults::Video::FULLSCREEN}; // Contiene el valor del modo de pantalla completa - Screen::Filter filter{Defaults::Video::FILTER}; // Filtro usado para el escalado de la imagen - bool vertical_sync{Defaults::Video::VERTICAL_SYNC}; // Indica si se quiere usar vsync o no - bool integer_scale{Defaults::Video::INTEGER_SCALE}; // Indica si el escalado de la imagen ha de ser entero en el modo a pantalla completa - bool keep_aspect{Defaults::Video::KEEP_ASPECT}; // Indica si se ha de mantener la relación de aspecto al poner el modo a pantalla completa - std::string palette{Defaults::Video::PALETTE_NAME}; // Paleta de colores a usar en el juego - std::string info; // Información sobre el modo de vídeo - Border border{}; // Borde de la pantalla - GPU gpu{}; // Opciones de aceleración GPU - Supersampling supersampling{}; // Opciones de supersampling - ShaderConfig shader{}; // Opciones de shader post-procesado + bool fullscreen{Defaults::Video::FULLSCREEN}; // Contiene el valor del modo de pantalla completa + Screen::Filter filter{Defaults::Video::FILTER}; // Filtro usado para el escalado de la imagen + bool vertical_sync{Defaults::Video::VERTICAL_SYNC}; // Indica si se quiere usar vsync o no + bool integer_scale{Defaults::Video::INTEGER_SCALE}; // Indica si el escalado de la imagen ha de ser entero en el modo a pantalla completa + bool keep_aspect{Defaults::Video::KEEP_ASPECT}; // Indica si se ha de mantener la relación de aspecto al poner el modo a pantalla completa + std::string palette{Defaults::Video::PALETTE_NAME}; // Paleta de colores a usar en el juego + std::string palette_sort{Defaults::Video::PALETTE_SORT}; // Modo de ordenación de la paleta (original/luminance/spectrum) + std::string info; // Información sobre el modo de vídeo + Border border{}; // Borde de la pantalla + GPU gpu{}; // Opciones de aceleración GPU + Supersampling supersampling{}; // Opciones de supersampling + ShaderConfig shader{}; // Opciones de shader post-procesado }; // Estructura para las opciones de musica diff --git a/source/game/ui/console_commands.cpp b/source/game/ui/console_commands.cpp index 4117b3c..0563a8c 100644 --- a/source/game/ui/console_commands.cpp +++ b/source/game/ui/console_commands.cpp @@ -333,12 +333,12 @@ static auto cmd_driver(const std::vector& args) -> std::string { return "Driver: " + driver_lower + " (restart)"; } -// PALETTE NEXT/PREV/ +// PALETTE NEXT/PREV/SORT/DEFAULT/ static auto cmd_palette(const std::vector& args) -> std::string { const auto palName = []() -> std::string { return Screen::get()->getPalettePrettyName(); }; - if (args.empty()) { return "usage: palette [next|prev|]"; } + if (args.empty()) { return "usage: palette [next|prev|sort [original|luminance|spectrum]|default|]"; } if (args[0] == "NEXT") { Screen::get()->nextPalette(); return "Palette: " + palName(); @@ -347,6 +347,26 @@ static auto cmd_palette(const std::vector& args) -> std::string { Screen::get()->previousPalette(); return "Palette: " + palName(); } + if (args[0] == "DEFAULT") { + Screen::get()->setPaletteByName(Defaults::Video::PALETTE_NAME); + return "Palette: " + palName(); + } + if (args[0] == "SORT") { + if (args.size() == 1) { + Screen::get()->nextPaletteSortMode(); + return "Palette sort: " + Screen::get()->getPaletteSortModeName(); + } + if (args[1] == "ORIGINAL") { + Screen::get()->setPaletteSortMode(PaletteSortMode::ORIGINAL); + } else if (args[1] == "LUMINANCE") { + Screen::get()->setPaletteSortMode(PaletteSortMode::LUMINANCE); + } else if (args[1] == "SPECTRUM") { + Screen::get()->setPaletteSortMode(PaletteSortMode::SPECTRUM); + } else { + return "Unknown sort mode. Use: original, luminance, spectrum"; + } + return "Palette sort: " + Screen::get()->getPaletteSortModeName(); + } if (!Screen::get()->setPaletteByName(args[0])) { std::string arg_lower = args[0]; std::ranges::transform(arg_lower, arg_lower.begin(), ::tolower); @@ -815,7 +835,7 @@ void CommandRegistry::registerHandlers() { // Proveedores de completions dinámicas // PALETTE: NEXT, PREV + nombres de paletas disponibles (UPPERCASE) dynamic_providers_["PALETTE"] = []() -> std::vector { - std::vector result = {"NEXT", "PREV"}; + std::vector result = {"NEXT", "PREV", "SORT", "DEFAULT"}; if (Screen::get() != nullptr) { for (const auto& name : Screen::get()->getPaletteNames()) { result.push_back(toUpper(name));