11 Commits

Author SHA1 Message Date
4435bc4942 arreglos en makefile de macos 2026-05-03 18:07:13 +02:00
4a4485c6f8 bugfixes 2026-04-18 18:16:41 +02:00
d09bb1cf6b actualitzat changelog 2026-04-18 17:57:05 +02:00
b1f9e57f36 fix: color de fonde dels sliders de 050505 a 000000 2026-04-18 15:20:25 +02:00
f7875baa2d refactor: fase 6 — Rule of 5 a Mapa i ModuleGame (no-copiables, no-movibles)
- Mapa té un JD8_Surface fondo propi que s'allibera al destructor: una
  còpia accidental provocaria double-free. Ara els 4 copy/move ops estan
  = delete.
- ModuleGame ja era no-copiable implícitament per tindre unique_ptr
  members, però els = delete expliciten la intenció i protegeixen
  davant refactors futurs que afegeixquen tipus copiables.

Fi de la modernització RAII per fases (1–6).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:03:51 +02:00
c6e37af7d1 refactor: fase 5 — singletons a std::unique_ptr (elimina new/delete manual)
5 singletons afectats: Audio, Screen, Director, Resource::Cache, Resource::List.

- static T* instance → static std::unique_ptr<T> instance
- init(): new T() adoptat immediatament per unique_ptr (ownership RAII)
- destroy(): instance.reset() (sense delete manual)
- get(): retorna instance.get()
- Destructors moguts a public perquè std::default_delete hi pugui accedir
  (ctors privats + copy/move deleted → encapsulació efectiva mantinguda)

Ordre de destrucció preservat: SDL_AppQuit segueix cridant destroy() en
l'ordre invers a init() — la RAII automàtica no s'activa fins al final
del programa (LIFO de variables static).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:02:01 +02:00
5e57034a38 refactor: fase 4 — llista enllaçada de Momia a std::vector<unique_ptr>
Eliminada completament la recursivitat per next-pointer:
- Momia::next, clear(), insertar() desapareixen
- update()/draw() no recursen: operen només sobre la instància pròpia
- ModuleGame::momies: Momia* (head de llista) → std::vector<std::unique_ptr<Momia>>
  - Destructor simplificat (vector s'autodestrueix)
  - Draw: range-for sobre el vector
  - Update: std::erase_if + decrement sincronitzat de info::ctx.momies
  - Cheat "alone": momies.clear()
  - iniciarMomies i nova_momia: emplace_back(std::make_unique<Momia>(...))

Zero new/delete manuals al cicle de vida de les momies.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 13:56:05 +02:00
2a8fbbb095 refactor: fase 3 — Text::bitmap_ a std::vector<Uint8>
- bitmap_: Uint8* (owning, free'd al destructor) → std::vector<Uint8>
- loadBitmap copia des del buffer de LoadGif i fa free(pixels) de
  l'intermedi (gif.h usa malloc internament)
- ~Text() eliminat: regla 0 aplicada (vector es destrueix sol)
- Les 4 comprovacions !bitmap_ → bitmap_.empty()

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 13:44:07 +02:00
53e93ef697 refactor: fase 2 — elimina malloc/free a jdraw8 i paletes d'escenes
- JD8_Init/Quit: new[]/delete[] per a screen, main_palette, pixel_data
- JD8_NewSurface/FreeSurface: new Uint8[64000]{}/delete[]
- JD8_LoadPalette: uniforme — sempre retorna `new Color[256]`, copiant del
  LoadPalette extern al path no-cached (l'intermedi raw es frees amb free()
  perquè gif.h el malloca)
- JD8_SetScreenPalette: delete[] la paleta reemplaçada
- slides/secreta/menu/banner/mort scenes: std::free/std::malloc → delete[]/new Color[256]

Ownership uniforme: tot el cicle de vida de surface/palette usa new[]/delete[].

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 13:42:31 +02:00
e7aa2463b4 refactor: fase 1 — cleanup mecànic de baix risc (NULL→nullptr, typedef→using, explicit, enum class local)
- jdraw8.hpp: typedef → using (JD8_Surface, JD8_Palette)
- jdraw8.cpp: NULL → nullptr, C-casts → static_cast/reinterpret_cast, anon enum FadeType → enum class
- momia.cpp: NULL → nullptr
- bola/mapa/marcador/momia/engendro: explicit als constructors

Zero canvis de lògica ni ownership. Primera fase de la modernització RAII.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 13:37:48 +02:00
27f8b0ae36 cppcheck 2026-04-18 13:22:13 +02:00
52 changed files with 417 additions and 340 deletions

View File

@@ -0,0 +1 @@
{"sessionId":"7b0c9c32-3dd4-48a3-ba06-c2303dc08243","pid":123890,"acquiredAt":1776510185734}

View File

@@ -1,6 +1,88 @@
# Changelog
Tots els canvis de la reconstrucció moderna (C++/SDL3) d'**Aventures en Egipte**, des de l'inici del port fins a la v1.1.
Tots els canvis de la reconstrucció moderna (C++/SDL3) d'**Aventures en Egipte**.
## [1.2] — 2026-04-18
Versió de modernització profunda: desapareix el model *threads estil emulador* i tot el runtime passa a un sol fil tick-based compatible amb emscripten. Zero regressions de gameplay.
### Afegit
#### Arquitectura: capa `scenes::` tick-based
- Infraestructura `scenes::` ([source/scenes/](source/scenes/)): `Scene`, `SceneRegistry`, `Timeline`, `SpriteMover`, `FrameAnimator`, `PaletteFade`, `SurfaceHandle`, helper `playMusic` (`4436f7f`)
- **MortScene** substitueix `doMort()` (`d86cb21`)
- **BannerScene** substitueix `doBanner()` per piràmides 25 (`2cb38ff`)
- **MenuScene** substitueix `doMenu()` + fix `JI_Update` al loop (`8720e77`)
- **IntroNewLogoScene** substitueix `doIntroNewLogo()` (`ad38fc0`)
- **SlidesScene** amb wipe suau per easing (`605c273`)
- **CreditsScene** amb scroll vertical + parallax condicional (`829d743`)
- **SecretaScene** amb swap `tomba1→tomba2` i red pulse animat (`6063b1c`)
- **IntroScene** amb revelat *JAILGAMES* lletra a lletra + cicle de paleta (`e18b732`)
- **IntroSpritesScene** com a sub-escena amb 3 variants aleatòries (`d343e71`)
- **ModuleGame** migrat a `scenes::Scene` amb fases `FadingIn`/`FadingOut` (`4e18f83`)
- Pla de migració documentat a [docs/scenes-migration-plan.md](docs/scenes-migration-plan.md) (`6125277`)
#### Resource pack
- Sistema d'empaquetat d'assets `resources.pack` (format **AEE1**, XOR-xifrat) estil *coffee_crisis* (`b2d5f5a`, `4244bca`)
- Classe `ResourcePack` + namespace `ResourceHelper` + eina CLI standalone `pack_resources` (target `make pack`)
- Cablejat a tots els callsites de recursos via `ResourceHelper::loadFile`
- Scaffold `.jrf` llegat eliminat completament de `jfile.cpp`
- Releases natius depenen del pack i l'usen obligatòriament (sense fallback); WASM i Debug mantenen fallback
- Normalització de `resource::cache` per a `Audio` (`94aa69c`)
#### Build WebAssembly
- Build WASM via Docker (`emscripten/emsdk:latest`) amb desplegament a maverick (`make wasm`)
- SDL3 compilat des de font via `FetchContent`; shaders omesos; `sdl3gpu_shader.cpp` exclòs
- Events de canvas d'emscripten (`1c11a30`)
- Fix de mandos en emscripten Android (`d3bdd9b`)
- Defaults específics d'emscripten (`7f26b8d`)
- Internal resolution configurable (`e8b0b12`, `16a3f5b`)
#### Menú i UI
- **Menú de sistema** amb versió i opció de tancar/reiniciar (`e0f9b60`)
- Animació de tancar el menú (`5956d87`)
- Items ocultables condicionalment en funció d'altres items (`a3fc111`)
- Tots els valors d'escala que exposa SDL3 (`52431ad`)
- `debug.yaml` separat de `config.yaml` (`fe41919`)
### Canviat
#### Runtime: sense fibers, sense threads, sense mutex
- **Fase 1** — jail i game a C++ idiomàtic: RAII, `info::ctx` com a singleton `inline`, cheats arreglats (`scancode→ASCII`) (`7f85b50`)
- **Fase 2** — fades de `jd8` a màquina d'estats + helper `wait_frame_or_skip` a les cinemàtiques (`80fa7b4`)
- **Fase 3** — `jail_audio` header-only amb streaming real (`stb_vorbis_open_memory` + `JA_PumpMusic`), sense `SDL_AddTimer` (`801a8ad`)
- **Fase 4+5** — fibers cooperatius substitueixen el game thread, sense mutex ni `cv` (`1507a1c`)
- **Step B.1** — fades de `ModuleGame` tick-based amb `scenes::PaletteFade` (`4e18f83`)
- **Step B.2** — **eliminació total del fiber**: `Director` posseeix l'escena (`current_scene_`, `game_state_`), `JD8_Flip` sense yield, `fiber.{hpp,cpp}` esborrats (`96a3cf9`)
- **Step 10** — `ModuleSequence` eliminat; dispatch via `SceneRegistry::tryCreate()` i `game_state_ == 0/1` directe des del `Director`
- Main loop via **SDL3 Callback API** (`SDL_MAIN_USE_CALLBACKS`): `SDL_AppInit`/`Iterate`/`Event`/`Quit`, compatible amb emscripten
#### RAII i neteja de memòria
- **Fase 1** — cleanup mecànic: `NULL→nullptr`, `typedef→using`, `explicit`, `enum class` local (`e7aa246`)
- **Fase 2** — elimina `malloc`/`free` a `jdraw8` i paletes d'escenes (`53e93ef`)
- **Fase 3** — `Text::bitmap_` a `std::vector<Uint8>` (`2a8fbbb`)
- **Fase 4** — llista enllaçada de Momia a `std::vector<std::unique_ptr>` (`5e57034`)
- **Fase 5** — singletons a `std::unique_ptr` (elimina `new`/`delete` manual) (`c6e37af`)
- **Fase 6** — Rule of 5 a `Mapa` i `ModuleGame` (no-copiables, no-movibles) (`f7875ba`)
- `file_getfilebuffer``file_readfile` retornant `std::vector<char>` — elimina 3 leaks silenciosos (paleta + música gameplay + música cinemàtica) (`b3ff620`)
- `JA_Music_t` RAII amb `vector<Uint8>`/`string`, elimina overload i camps morts (`f9346ad`)
- `JA_Sound_t` RAII amb `unique_ptr + SDLFreeDeleter`, elimina `JA_NewSound` (`550e3e0`)
#### Build i tooling
- Unificats `.clang-format` i `.clang-tidy`, amb exclusió de `external/` i `spv/` via dummies (`7409c79`)
- `cppcheck` integrat amb suppress list (`27f8b0a`, `2e1a82f`)
- `make`/`cmake` estandarditzats amb la resta de projectes JailGames (`9d86137`)
- Fitxers de música renombrats a noms temàtics (`417699d`)
- Carpeta `data/` reordenada (`083a57d`)
### Arreglat
- Shaders ON/OFF no afectaven a CRT-Pi (`a36662a`)
- Logo nou de la intro tornava a descentrar-se (`52369be`, `5cda8fc`)
- Color de fons dels sliders de `0x050505` a `0x000000` (`b1f9e57`)
- Diversos detalls menors (`6394e9a`, `0cd09f6`)
---
## [1.1] — 2026-04-05
@@ -64,4 +146,5 @@ Versió que fa coincidir la numeració amb la del joc original del 2000.
---
[1.1]: https://gitea/aee/compare/9e0ab87...HEAD
[1.2]: https://gitea/aee/compare/486f00b...HEAD
[1.1]: https://gitea/aee/compare/9e0ab87...486f00b

View File

@@ -184,12 +184,28 @@ _windows_release: pack
_macos_release: pack
@echo "Creando release para macOS - Version: $(VERSION)"
# Verificar e instalar create-dmg si es necesario
@which create-dmg > /dev/null || (echo "Instalando create-dmg..." && brew install create-dmg)
# Compila la versión para procesadores Intel con cmake
@cmake -S . -B build/intel -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DMACOS_BUNDLE=ON -DGIT_HASH=$(GIT_HASH)
@cmake --build build/intel
# Verifica dependencias necesarias (create-dmg). Si falta, intenta instalarla
# con brew; si brew tampoco está, indica el comando exacto al usuario.
@command -v create-dmg >/dev/null 2>&1 || { \
echo ""; \
echo "============================================"; \
echo " Falta la dependencia: create-dmg"; \
echo "============================================"; \
if command -v brew >/dev/null 2>&1; then \
echo " Instalando con: brew install create-dmg"; \
brew install create-dmg || { \
echo ""; \
echo " ERROR: 'brew install create-dmg' ha fallado."; \
echo " Ejecuta el comando manualmente y vuelve a probar."; \
exit 1; \
}; \
else \
echo " Homebrew no está instalado."; \
echo " Instálalo desde https://brew.sh y luego ejecuta:"; \
echo " brew install create-dmg"; \
exit 1; \
fi; \
}
# Elimina datos de compilaciones anteriores
$(RMDIR) "$(RELEASE_FOLDER)"
@@ -218,14 +234,20 @@ _macos_release: pack
sed -i '' '/<key>CFBundleShortVersionString<\/key>/{n;s|<string>.*</string>|<string>'"$$RAW_VERSION"'</string>|;}' "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Info.plist"; \
sed -i '' '/<key>CFBundleVersion<\/key>/{n;s|<string>.*</string>|<string>'"$$RAW_VERSION"'</string>|;}' "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Info.plist"
# Copia el ejecutable Intel al bundle
cp "$(TARGET_FILE)" "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)"
# Firma la aplicación
codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app"
# Empaqueta el .dmg de la versión Intel con create-dmg
@echo "Creando DMG Intel con iconos de 96x96..."
# Compila y empaqueta la versión Intel (best-effort: si falla, se omite el
# DMG Intel y continúa con la build de Apple Silicon).
@echo ""
@echo "============================================"
@echo " Compilando version Intel (x86_64)"
@echo "============================================"
@if cmake -S . -B build/intel -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 \
-DMACOS_BUNDLE=ON -DGIT_HASH=$(GIT_HASH) \
&& cmake --build build/intel; then \
cp "$(TARGET_FILE)" "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)"; \
codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app"; \
echo "Creando DMG Intel con iconos de 96x96..."; \
create-dmg \
--volname "$(APP_NAME)" \
--window-pos 200 120 \
@@ -238,10 +260,23 @@ _macos_release: pack
--app-drop-link 115 102 \
--hide-extension "$(APP_NAME).app" \
"$(MACOS_INTEL_RELEASE)" \
"$(RELEASE_FOLDER)" || true
@echo "Release Intel creado: $(MACOS_INTEL_RELEASE)"
"$(RELEASE_FOLDER)" || true; \
echo "Release Intel creado: $(MACOS_INTEL_RELEASE)"; \
else \
echo ""; \
echo "============================================"; \
echo " WARNING: la build Intel ha fallado."; \
echo " Se omite el DMG Intel y se continúa con"; \
echo " la build de Apple Silicon."; \
echo "============================================"; \
echo ""; \
fi
# Compila la versión para procesadores Apple Silicon con cmake
@echo ""
@echo "============================================"
@echo " Compilando version Apple Silicon (arm64)"
@echo "============================================"
@cmake -S . -B build/arm -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=arm64 -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 -DMACOS_BUNDLE=ON -DGIT_HASH=$(GIT_HASH)
@cmake --build build/arm
cp "$(TARGET_FILE)" "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -21,6 +21,7 @@ menu:
exit_game: "Eixir del joc"
use_new_logo: "Logo nou"
show_title_credits: "Crèdits del port"
show_preload: "Barra de precàrrega"
zoom: "Zoom"
screen: "Pantalla"
shader: "Shader"

View File

@@ -24,19 +24,16 @@
#include "game/options.hpp" // Para Options::audio
// Singleton
Audio* Audio::instance = nullptr;
std::unique_ptr<Audio> Audio::instance;
// Inicializa la instancia única del singleton
void Audio::init() { Audio::instance = new Audio(); }
void Audio::init() { Audio::instance = std::unique_ptr<Audio>(new Audio()); }
// Libera la instancia
void Audio::destroy() {
delete Audio::instance;
Audio::instance = nullptr;
}
void Audio::destroy() { Audio::instance.reset(); }
// Obtiene la instancia
auto Audio::get() -> Audio* { return Audio::instance; }
auto Audio::get() -> Audio* { return Audio::instance.get(); }
// Constructor
Audio::Audio() { initSDLAudio(); }
@@ -172,21 +169,19 @@ auto Audio::getRealMusicState() -> MusicState {
// Establece el volumen de los sonidos (float 0.0..1.0)
void Audio::setSoundVolume(float sound_volume, Group group) const {
if (sound_enabled_) {
sound_volume = std::clamp(sound_volume, MIN_VOLUME, MAX_VOLUME);
const float CONVERTED_VOLUME = sound_volume * Options::audio.volume;
const bool active = enabled_ && sound_enabled_;
const float CONVERTED_VOLUME = active ? sound_volume * Options::audio.volume : 0.0F;
JA_SetSoundVolume(CONVERTED_VOLUME, static_cast<int>(group));
}
}
// Establece el volumen de la música (float 0.0..1.0)
void Audio::setMusicVolume(float music_volume) const {
if (music_enabled_) {
music_volume = std::clamp(music_volume, MIN_VOLUME, MAX_VOLUME);
const float CONVERTED_VOLUME = music_volume * Options::audio.volume;
const bool active = enabled_ && music_enabled_;
const float CONVERTED_VOLUME = active ? music_volume * Options::audio.volume : 0.0F;
JA_SetMusicVolume(CONVERTED_VOLUME);
}
}
// Aplica la configuración
void Audio::applySettings() {

View File

@@ -1,6 +1,7 @@
#pragma once
#include <cstdint> // Para int8_t, uint8_t
#include <memory> // Para std::unique_ptr
#include <string> // Para string
#include <utility> // Para move
@@ -35,6 +36,7 @@ class Audio {
static void init(); // Inicializa el objeto Audio
static void destroy(); // Libera el objeto Audio
static auto get() -> Audio*; // Obtiene el puntero al objeto Audio
~Audio(); // Destructor (públic per a std::unique_ptr)
Audio(const Audio&) = delete; // Evitar copia
auto operator=(const Audio&) -> Audio& = delete; // Evitar asignación
@@ -101,11 +103,10 @@ class Audio {
// --- Métodos ---
Audio(); // Constructor privado
~Audio(); // Destructor privado
void initSDLAudio(); // Inicializa SDL Audio
// --- Variables miembro ---
static Audio* instance; // Instancia única de Audio
static std::unique_ptr<Audio> instance; // Instancia única de Audio
Music music_; // Estado de la música
bool enabled_{true}; // Estado general del audio

View File

@@ -265,13 +265,13 @@ inline JA_Music_t* JA_LoadMusic(const Uint8* buffer, Uint32 length) {
auto* music = new JA_Music_t();
music->ogg_data.assign(buffer, buffer + length);
int error = 0;
int vorbis_error = 0;
music->vorbis = stb_vorbis_open_memory(music->ogg_data.data(),
static_cast<int>(length),
&error,
&vorbis_error,
nullptr);
if (!music->vorbis) {
std::cout << "JA_LoadMusic: stb_vorbis_open_memory failed (error " << error << ")" << '\n';
std::cout << "JA_LoadMusic: stb_vorbis_open_memory failed (error " << vorbis_error << ")" << '\n';
delete music;
return nullptr;
}

View File

@@ -19,20 +19,23 @@
#pragma GCC diagnostic pop
#endif
JD8_Surface screen = NULL;
JD8_Palette main_palette = NULL;
Uint32* pixel_data = NULL;
JD8_Surface screen = nullptr;
JD8_Palette main_palette = nullptr;
Uint32* pixel_data = nullptr;
void JD8_Init() {
screen = (JD8_Surface)calloc(1, 64000);
main_palette = (JD8_Palette)calloc(1, 768);
pixel_data = (Uint32*)calloc(1, 320 * 200 * 4);
screen = new Uint8[64000]{};
main_palette = new Color[256]{};
pixel_data = new Uint32[320 * 200]{};
}
void JD8_Quit() {
if (screen != NULL) free(screen);
if (main_palette != NULL) free(main_palette);
if (pixel_data != NULL) free(pixel_data);
delete[] screen;
delete[] main_palette;
delete[] pixel_data;
screen = nullptr;
main_palette = nullptr;
pixel_data = nullptr;
}
void JD8_ClearScreen(Uint8 color) {
@@ -40,9 +43,7 @@ void JD8_ClearScreen(Uint8 color) {
}
JD8_Surface JD8_NewSurface() {
JD8_Surface surface = (JD8_Surface)malloc(64000);
memset(surface, 0, 64000);
return surface;
return new Uint8[64000]{};
}
// Helper intern: deriva el basename d'una ruta per a buscar al Cache.
@@ -71,7 +72,7 @@ JD8_Surface JD8_LoadSurface(const char* file) {
auto buffer = ResourceHelper::loadFile(file);
unsigned short w, h;
Uint8* pixels = LoadGif(buffer.data(), &w, &h);
if (pixels == NULL) {
if (pixels == nullptr) {
printf("Unable to load bitmap: %s\n", SDL_GetError());
exit(1);
}
@@ -82,27 +83,31 @@ JD8_Surface JD8_LoadSurface(const char* file) {
}
JD8_Palette JD8_LoadPalette(const char* file) {
// Sempre retorna un buffer de 256 colors reservat amb `new Color[256]`
// — el caller és responsable d'alliberar-lo amb `delete[]` (o lliurar-ne
// l'ownership a `JD8_SetScreenPalette`).
JD8_Palette palette = new Color[256];
if (Resource::Cache::get() != nullptr) {
try {
const auto& cached = Resource::Cache::get()->getPaletteBytes(jd8_basename(file));
// Reservem un buffer 768 bytes (256 * RGB) que el caller ha
// d'alliberar amb free() — mateixa convenció que el LoadPalette
// original (retornava un malloc).
JD8_Palette palette = (JD8_Palette)malloc(768);
memcpy(palette, cached.data(), 768);
return palette;
} catch (const std::exception&) {
// No està al cache.
// No està al cache — fallback a lectura + LoadPalette.
}
}
auto buffer = ResourceHelper::loadFile(file);
return (JD8_Palette)LoadPalette(buffer.data());
Uint8* raw = LoadPalette(buffer.data()); // external malloc
memcpy(palette, raw, 768);
free(raw);
return palette;
}
void JD8_SetScreenPalette(JD8_Palette palette) {
if (main_palette == palette) return;
if (main_palette != NULL) free(main_palette);
delete[] main_palette;
main_palette = palette;
}
@@ -218,7 +223,7 @@ Uint32* JD8_GetFramebuffer() {
}
void JD8_FreeSurface(JD8_Surface surface) {
free(surface);
delete[] surface;
}
Uint8 JD8_GetPixel(JD8_Surface surface, int x, int y) {
@@ -240,26 +245,26 @@ void JD8_SetPaletteColor(Uint8 index, Uint8 r, Uint8 g, Uint8 b) {
// el caller decideix quan fer Flip.
namespace {
enum FadeType {
FADE_NONE = 0,
FADE_OUT,
FADE_TO_PAL,
enum class FadeType {
None = 0,
Out,
ToPal,
};
constexpr int FADE_STEPS = 32;
FadeType fade_type = FADE_NONE;
FadeType fade_type = FadeType::None;
Color fade_target[256];
int fade_step = 0;
void apply_fade_step() {
if (fade_type == FADE_OUT) {
if (fade_type == FadeType::Out) {
for (int i = 0; i < 256; i++) {
main_palette[i].r = main_palette[i].r >= 8 ? main_palette[i].r - 8 : 0;
main_palette[i].g = main_palette[i].g >= 8 ? main_palette[i].g - 8 : 0;
main_palette[i].b = main_palette[i].b >= 8 ? main_palette[i].b - 8 : 0;
}
} else if (fade_type == FADE_TO_PAL) {
} else if (fade_type == FadeType::ToPal) {
for (int i = 0; i < 256; i++) {
main_palette[i].r = main_palette[i].r <= int(fade_target[i].r) - 8
? main_palette[i].r + 8
@@ -277,28 +282,28 @@ namespace {
} // namespace
void JD8_FadeStartOut() {
fade_type = FADE_OUT;
fade_type = FadeType::Out;
fade_step = 0;
}
void JD8_FadeStartToPal(JD8_Palette pal) {
fade_type = FADE_TO_PAL;
fade_type = FadeType::ToPal;
memcpy(fade_target, pal, sizeof(Color) * 256);
fade_step = 0;
}
bool JD8_FadeIsActive() {
return fade_type != FADE_NONE;
return fade_type != FadeType::None;
}
bool JD8_FadeTickStep() {
if (fade_type == FADE_NONE) return true;
if (fade_type == FadeType::None) return true;
apply_fade_step();
fade_step++;
if (fade_step >= FADE_STEPS) {
fade_type = FADE_NONE;
fade_type = FadeType::None;
return true;
}
return false;

View File

@@ -7,8 +7,8 @@ struct Color {
Uint8 b;
};
typedef Uint8* JD8_Surface;
typedef Color* JD8_Palette;
using JD8_Surface = Uint8*;
using JD8_Palette = Color*;
void JD8_Init();

View File

@@ -70,21 +70,26 @@ void file_setconfigfolder(const char* foldername) {
if (!homedir) homedir = "/tmp";
config_folder = std::string(homedir) + "/Library/Application Support/" + foldername;
#elif __linux__
// Nota emscripten: `__linux__` també està definit, però `getpwuid` no
// troba cap /etc/passwd al MEMFS i retorna nullptr. Amb els fallbacks
// HOME → /tmp evitem crashejar al primer arranque dins del navegador.
// La config no persistirà entre recàrregues de la pàgina (MEMFS és
// volàtil); caldria IDBFS si volguéssem persistència a web.
// Nota emscripten: `__linux__` també està definit, però `getpwuid` pot
// retornar nullptr (sense /etc/passwd al MEMFS) o un passwd amb pw_dir
// buit. Amb els fallbacks HOME → /tmp evitem crashejar al primer
// arranque dins del navegador. La config no persistirà entre recàrregues
// (MEMFS és volàtil); caldria IDBFS si volguéssem persistència a web.
struct passwd* pw = getpwuid(getuid());
const char* homedir = (pw && pw->pw_dir) ? pw->pw_dir : nullptr;
if (!homedir) homedir = getenv("HOME");
if (!homedir) homedir = "/tmp";
const char* homedir = (pw && pw->pw_dir && pw->pw_dir[0]) ? pw->pw_dir : nullptr;
if (!homedir || !homedir[0]) homedir = getenv("HOME");
if (!homedir || !homedir[0]) homedir = "/tmp";
config_folder = std::string(homedir) + "/.config/" + foldername;
#endif
if (!config_folder.empty()) {
std::filesystem::create_directories(config_folder);
if (config_folder.empty()) {
config_folder = "/tmp/jailgames_config";
}
std::error_code ec;
std::filesystem::create_directories(config_folder, ec);
// A emscripten/MEMFS create_directories pot fallar (p.ex. parent
// read-only o libc++ amb path empty-check estricte). La config és
// volàtil al navegador de totes formes: ignorem l'error i continuem.
}
const char* file_getconfigfolder() {

View File

@@ -271,6 +271,8 @@ namespace Menu {
p.items.push_back({Locale::get("menu.items.show_title_credits"), ItemKind::Toggle, [] { return yesNo(Options::game.show_title_credits); }, [](int) { Options::game.show_title_credits = !Options::game.show_title_credits; }, nullptr});
p.items.push_back({Locale::get("menu.items.show_preload"), ItemKind::Toggle, [] { return yesNo(Options::game.show_preload); }, [](int) { Options::game.show_preload = !Options::game.show_preload; }, nullptr});
return p;
}

View File

@@ -55,19 +55,18 @@ namespace {
} // namespace
#endif // __EMSCRIPTEN__
Screen* Screen::instance_ = nullptr;
std::unique_ptr<Screen> Screen::instance_;
void Screen::init() {
instance_ = new Screen();
instance_ = std::unique_ptr<Screen>(new Screen());
}
void Screen::destroy() {
delete instance_;
instance_ = nullptr;
instance_.reset();
}
auto Screen::get() -> Screen* {
return instance_;
return instance_.get();
}
Screen::Screen() {

View File

@@ -13,6 +13,8 @@ class Screen {
static void destroy();
static auto get() -> Screen*;
~Screen(); // públic per a std::unique_ptr
// Presentació — rep el buffer ARGB de 320x200 de JD8
void present(Uint32* pixel_data);
@@ -62,7 +64,6 @@ class Screen {
private:
Screen();
~Screen();
void adjustWindowSize();
void calculateMaxZoom();
@@ -70,7 +71,7 @@ class Screen {
void applyFallbackPresentation(); // Logical presentation + scale mode per al path SDL_Renderer
void ensureFallbackInternalTexture(); // Recrea internal_texture_sdl_ si cal (fallback path)
static Screen* instance_;
static std::unique_ptr<Screen> instance_;
SDL_Window* window_{nullptr};
SDL_Renderer* renderer_{nullptr};

View File

@@ -19,10 +19,6 @@ Text::Text(const char* fnt_file, const char* gif_file) {
loadFont(fnt_file);
}
Text::~Text() {
if (bitmap_) free(bitmap_);
}
// --- UTF-8 ---
auto Text::nextCodepoint(const char*& ptr) -> uint32_t {
@@ -80,27 +76,27 @@ void Text::loadFont(const char* fnt_file) {
// Elimina comentaris inline
auto comment_pos = line.find('#');
if (comment_pos != std::string::npos) {
line = line.substr(0, comment_pos);
line.resize(comment_pos);
}
// Parseja directives
if (line.find("box_width") == 0) {
if (line.starts_with("box_width")) {
sscanf(line.c_str(), "box_width %d", &box_width_);
continue;
}
if (line.find("box_height") == 0) {
if (line.starts_with("box_height")) {
sscanf(line.c_str(), "box_height %d", &box_height_);
continue;
}
if (line.find("columns") == 0) {
if (line.starts_with("columns")) {
sscanf(line.c_str(), "columns %d", &columns_);
continue;
}
if (line.find("cell_spacing") == 0) {
if (line.starts_with("cell_spacing")) {
sscanf(line.c_str(), "cell_spacing %d", &cell_spacing_);
continue;
}
if (line.find("row_spacing") == 0) {
if (line.starts_with("row_spacing")) {
sscanf(line.c_str(), "row_spacing %d", &row_spacing_);
continue;
}
@@ -146,7 +142,8 @@ void Text::loadBitmap(const char* gif_file) {
bitmap_width_ = w;
bitmap_height_ = h;
bitmap_ = pixels;
bitmap_.assign(pixels, pixels + (static_cast<size_t>(w) * h));
free(pixels); // LoadGif usa malloc internament
std::cout << "Text: bitmap loaded " << w << "x" << h << '\n';
}
@@ -154,7 +151,7 @@ void Text::loadBitmap(const char* gif_file) {
// --- Renderitzat ---
void Text::draw(Uint32* pixel_data, int x, int y, const char* text, Uint32 color) const {
if (!bitmap_ || !pixel_data) return;
if (bitmap_.empty() || !pixel_data) return;
const char* ptr = text;
int cursor_x = x;
@@ -207,7 +204,7 @@ void Text::drawCentered(Uint32* pixel_data, int y, const char* text, Uint32 colo
}
void Text::drawClipped(Uint32* pixel_data, int x, int y, const char* text, Uint32 color, int clip_x_min, int clip_x_max, int clip_y_min, int clip_y_max) const {
if (!bitmap_ || !pixel_data) return;
if (bitmap_.empty() || !pixel_data) return;
// Descart ràpid si el glifo sencer cau fora verticalment
if (y + box_height_ <= clip_y_min || y >= clip_y_max) return;
@@ -262,7 +259,7 @@ void Text::drawClipped(Uint32* pixel_data, int x, int y, const char* text, Uint3
}
void Text::drawMono(Uint32* pixel_data, int x, int y, const char* text, Uint32 color, int cell_w) const {
if (!bitmap_ || !pixel_data) return;
if (bitmap_.empty() || !pixel_data) return;
const char* ptr = text;
int cursor_x = x;
@@ -308,7 +305,7 @@ void Text::drawMono(Uint32* pixel_data, int x, int y, const char* text, Uint32 c
}
void Text::drawMonoDigits(Uint32* pixel_data, int x, int y, const char* text, Uint32 color, int digit_cell_w) const {
if (!bitmap_ || !pixel_data) return;
if (bitmap_.empty() || !pixel_data) return;
const char* ptr = text;
int cursor_x = x;

View File

@@ -5,11 +5,11 @@
#include <cstdint>
#include <string>
#include <unordered_map>
#include <vector>
class Text {
public:
Text(const char* fnt_file, const char* gif_file);
~Text();
// Pinta texto sobre un buffer ARGB de 320x200
void draw(Uint32* pixel_data, int x, int y, const char* text, Uint32 color) const;
@@ -46,7 +46,7 @@ class Text {
int cell_spacing_{0};
int row_spacing_{0};
Uint8* bitmap_{nullptr}; // píxels 8-bit del GIF de la font
std::vector<Uint8> bitmap_; // píxels 8-bit del GIF de la font
int bitmap_width_{0};
int bitmap_height_{0};

View File

@@ -20,14 +20,11 @@ extern unsigned char* LoadPalette(unsigned char* data);
namespace Resource {
Cache* Cache::instance = nullptr;
std::unique_ptr<Cache> Cache::instance;
void Cache::init() { instance = new Cache(); }
void Cache::destroy() {
delete instance;
instance = nullptr;
}
auto Cache::get() -> Cache* { return instance; }
void Cache::init() { instance = std::unique_ptr<Cache>(new Cache()); }
void Cache::destroy() { instance.reset(); }
auto Cache::get() -> Cache* { return instance.get(); }
namespace {
auto basename(const std::string& path) -> std::string {

View File

@@ -3,6 +3,7 @@
#include <SDL3/SDL.h>
#include <cstddef>
#include <memory>
#include <string>
#include <vector>
@@ -20,6 +21,7 @@ namespace Resource {
static void destroy();
static auto get() -> Cache*;
~Cache() = default;
Cache(const Cache&) = delete;
auto operator=(const Cache&) -> Cache& = delete;
@@ -39,7 +41,6 @@ namespace Resource {
private:
Cache() = default;
~Cache() = default;
enum class LoadStage {
MUSICS,
@@ -66,7 +67,7 @@ namespace Resource {
int loaded_count_{0};
std::string current_loading_name_;
static Cache* instance;
static std::unique_ptr<Cache> instance;
};
} // namespace Resource

View File

@@ -9,19 +9,16 @@
namespace Resource {
List* List::instance = nullptr;
std::unique_ptr<List> List::instance;
void List::init(const std::string& yaml_path) {
instance = new List();
instance = std::unique_ptr<List>(new List());
instance->loadFromYaml(yaml_path);
}
void List::destroy() {
delete instance;
instance = nullptr;
}
void List::destroy() { instance.reset(); }
auto List::get() -> List* { return instance; }
auto List::get() -> List* { return instance.get(); }
void List::loadFromYaml(const std::string& yaml_path) {
auto bytes = ResourceHelper::loadFile(yaml_path);

View File

@@ -1,5 +1,6 @@
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
@@ -25,6 +26,7 @@ namespace Resource {
static void destroy();
static auto get() -> List*;
~List() = default;
List(const List&) = delete;
auto operator=(const List&) -> List& = delete;
@@ -44,7 +46,6 @@ namespace Resource {
};
List() = default;
~List() = default;
void loadFromYaml(const std::string& yaml_path);
void loadFromString(const std::string& yaml_content);
@@ -55,7 +56,7 @@ namespace Resource {
std::unordered_map<std::string, Item> file_list_;
static List* instance;
static std::unique_ptr<List> instance;
};
} // namespace Resource

View File

@@ -35,7 +35,7 @@
// Cheats del joc original — declarats a jinput.cpp
extern void JI_moveCheats(Uint8 new_key);
Director* Director::instance_ = nullptr;
std::unique_ptr<Director> Director::instance_;
Director::~Director() = default;
@@ -79,7 +79,7 @@ std::unique_ptr<scenes::Scene> Director::createNextScene() {
}
void Director::init() {
instance_ = new Director();
instance_ = std::unique_ptr<Director>(new Director());
Gamepad::init();
// Registre d'escenes. Cada entrada = un state_key (`num_piramide`)
@@ -116,12 +116,11 @@ void Director::init() {
void Director::destroy() {
Gamepad::destroy();
delete instance_;
instance_ = nullptr;
instance_.reset();
}
auto Director::get() -> Director* {
return instance_;
return instance_.get();
}
void Director::togglePause() {

View File

@@ -52,11 +52,13 @@ class Director {
void togglePause();
auto isPaused() const -> bool { return paused_; }
private:
Director() = default;
public:
~Director();
static Director* instance_;
private:
Director() = default;
static std::unique_ptr<Director> instance_;
void pollAllEvents(); // drenatge amb SDL_PollEvent, només per al bucle natiu

View File

@@ -6,9 +6,9 @@
class Bola : public Sprite {
public:
Bola(JD8_Surface gfx, Prota* sam);
explicit Bola(JD8_Surface gfx, Prota* sam);
void draw();
void draw() override;
void update();
protected:

View File

@@ -46,4 +46,5 @@ namespace Defaults::Game {
constexpr int DINERS_INICIAL = 0;
constexpr bool USE_NEW_LOGO = true;
constexpr bool SHOW_TITLE_CREDITS = true;
constexpr bool SHOW_PRELOAD = false;
} // namespace Defaults::Game

View File

@@ -29,10 +29,6 @@ Engendro::Engendro(JD8_Surface gfx, Uint16 x, Uint16 y)
this->cycles_per_frame = 30;
}
void Engendro::draw() {
Sprite::draw();
}
bool Engendro::update() {
bool mort = false;

View File

@@ -4,9 +4,8 @@
class Engendro : public Sprite {
public:
Engendro(JD8_Surface gfx, Uint16 x, Uint16 y);
explicit Engendro(JD8_Surface gfx, Uint16 x, Uint16 y);
void draw();
bool update();
protected:

View File

@@ -27,9 +27,14 @@ struct Vertex {
class Mapa {
public:
Mapa(JD8_Surface gfx, Prota* sam);
explicit Mapa(JD8_Surface gfx, Prota* sam);
~Mapa(void);
Mapa(const Mapa&) = delete;
Mapa& operator=(const Mapa&) = delete;
Mapa(Mapa&&) = delete;
Mapa& operator=(Mapa&&) = delete;
void draw();
void update();
bool novaMomia();

View File

@@ -6,7 +6,7 @@
class Marcador {
public:
Marcador(JD8_Surface gfx, Prota* sam);
explicit Marcador(JD8_Surface gfx, Prota* sam);
~Marcador(void);
void draw();

View File

@@ -1,5 +1,7 @@
#include "game/modulegame.hpp"
#include <algorithm>
#include "core/audio/audio.hpp"
#include "core/jail/jdraw8.hpp"
#include "core/jail/jgame.hpp"
@@ -9,29 +11,17 @@ ModuleGame::ModuleGame() {
this->gfx = JD8_LoadSurface(info::ctx.pepe_activat ? "gfx/frames2.gif" : "gfx/frames.gif");
JG_SetUpdateTicks(10);
this->sam = new Prota(this->gfx);
this->mapa = new Mapa(this->gfx, this->sam);
this->marcador = new Marcador(this->gfx, this->sam);
this->sam = std::make_unique<Prota>(this->gfx);
this->mapa = std::make_unique<Mapa>(this->gfx, this->sam.get());
this->marcador = std::make_unique<Marcador>(this->gfx, this->sam.get());
if (info::ctx.num_piramide == 2) {
this->bola = new Bola(this->gfx, this->sam);
} else {
this->bola = nullptr;
this->bola = std::make_unique<Bola>(this->gfx, this->sam.get());
}
this->momies = nullptr;
this->iniciarMomies();
}
ModuleGame::~ModuleGame() {
if (this->bola != nullptr) delete this->bola;
if (this->momies != nullptr) {
this->momies->clear();
delete this->momies;
}
delete this->marcador;
delete this->mapa;
delete this->sam;
JD8_FreeSurface(this->gfx);
}
@@ -122,8 +112,8 @@ void ModuleGame::Draw() {
this->mapa->draw();
this->marcador->draw();
this->sam->draw();
if (this->momies != nullptr) this->momies->draw();
if (this->bola != nullptr) this->bola->draw();
for (auto& m : this->momies) m->draw();
if (this->bola) this->bola->draw();
}
void ModuleGame::Update() {
@@ -131,33 +121,20 @@ void ModuleGame::Update() {
JI_Update();
this->final_ = this->sam->update();
if (this->momies != nullptr && this->momies->update()) {
Momia* seguent = this->momies->next;
delete this->momies;
this->momies = seguent;
info::ctx.momies--;
}
if (this->bola != nullptr) this->bola->update();
const auto erased = std::erase_if(this->momies, [](auto& m) { return m->update(); });
info::ctx.momies -= static_cast<int>(erased);
if (this->bola) this->bola->update();
this->mapa->update();
if (this->mapa->novaMomia()) {
if (this->momies != nullptr) {
this->momies->insertar(new Momia(this->gfx, true, 0, 0, this->sam));
this->momies.emplace_back(std::make_unique<Momia>(this->gfx, true, 0, 0, this->sam.get()));
info::ctx.momies++;
} else {
this->momies = new Momia(this->gfx, true, 0, 0, this->sam);
info::ctx.momies++;
}
}
if (JI_CheatActivated("reviu")) info::ctx.vida = 5;
if (JI_CheatActivated("alone")) {
if (this->momies != nullptr) {
this->momies->clear();
delete this->momies;
this->momies = nullptr;
this->momies.clear();
info::ctx.momies = 0;
}
}
if (JI_CheatActivated("obert")) {
for (int i = 0; i < 16; i++) {
this->mapa->tombes[i].costat[0] = true;
@@ -186,11 +163,7 @@ void ModuleGame::iniciarMomies() {
int y = 170;
bool dimonis = info::ctx.num_piramide == 6;
for (int i = 0; i < info::ctx.momies; i++) {
if (this->momies == nullptr) {
this->momies = new Momia(this->gfx, dimonis, x, y, this->sam);
} else {
this->momies->insertar(new Momia(this->gfx, dimonis, x, y, this->sam));
}
this->momies.emplace_back(std::make_unique<Momia>(this->gfx, dimonis, x, y, this->sam.get()));
x += 65;
if (x == 345) {
x = 20;

View File

@@ -1,5 +1,8 @@
#pragma once
#include <memory>
#include <vector>
#include "game/bola.hpp"
#include "game/info.hpp"
#include "game/mapa.hpp"
@@ -28,6 +31,11 @@ class ModuleGame : public scenes::Scene {
ModuleGame();
~ModuleGame() override;
ModuleGame(const ModuleGame&) = delete;
ModuleGame& operator=(const ModuleGame&) = delete;
ModuleGame(ModuleGame&&) = delete;
ModuleGame& operator=(ModuleGame&&) = delete;
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return phase_ == Phase::Done; }
@@ -52,9 +60,9 @@ class ModuleGame : public scenes::Scene {
Uint8 final_{0};
JD8_Surface gfx{nullptr};
Mapa* mapa{nullptr};
Prota* sam{nullptr};
Marcador* marcador{nullptr};
Momia* momies{nullptr};
Bola* bola{nullptr};
std::unique_ptr<Mapa> mapa;
std::unique_ptr<Prota> sam;
std::unique_ptr<Marcador> marcador;
std::vector<std::unique_ptr<Momia>> momies;
std::unique_ptr<Bola> bola;
};

View File

@@ -10,15 +10,15 @@ Momia::Momia(JD8_Surface gfx, bool dimoni, Uint16 x, Uint16 y, Prota* sam)
this->sam = sam;
entitat.frames.reserve(20);
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 5; x++) {
for (int row = 0; row < 4; row++) {
for (int col = 0; col < 5; col++) {
Frame f;
f.w = 15;
f.h = 15;
if (info::ctx.num_piramide == 4) f.h -= 5;
f.x = (x * 15) + 75;
f.x = (col * 15) + 75;
if (this->dimoni) f.x += 75;
f.y = 20 + (y * 15);
f.y = 20 + (row * 15);
entitat.frames.push_back(f);
}
}
@@ -40,7 +40,6 @@ Momia::Momia(JD8_Surface gfx, bool dimoni, Uint16 x, Uint16 y, Prota* sam)
this->cur_frame = 0;
this->o = rand() % 4;
this->cycles_per_frame = 4;
this->next = NULL;
if (this->dimoni) {
if (x == 0) {
@@ -57,22 +56,15 @@ Momia::Momia(JD8_Surface gfx, bool dimoni, Uint16 x, Uint16 y, Prota* sam)
} else {
this->y = y;
}
this->engendro = new Engendro(gfx, this->x, this->y);
this->engendro = std::make_unique<Engendro>(gfx, this->x, this->y);
} else {
this->engendro = NULL;
this->x = x;
this->y = y;
}
}
void Momia::clear() {
if (this->next != NULL) this->next->clear();
if (this->engendro != NULL) delete this->engendro;
delete this->next;
}
void Momia::draw() {
if (this->engendro != NULL) {
if (this->engendro) {
this->engendro->draw();
} else {
Sprite::draw();
@@ -85,18 +77,18 @@ void Momia::draw() {
}
}
}
if (this->next != NULL) this->next->draw();
}
bool Momia::update() {
bool morta = false;
if (this->engendro != NULL) {
if (this->engendro) {
if (this->engendro->update()) {
delete this->engendro;
this->engendro = NULL;
this->engendro.reset();
}
} else {
return morta;
}
if (this->sam->o < 4 && (this->dimoni || info::ctx.num_piramide == 5 || JG_GetCycleCounter() % 2 == 0)) {
if ((this->x - 20) % 65 == 0 && (this->y - 30) % 35 == 0) {
if (this->dimoni) {
@@ -156,24 +148,6 @@ bool Momia::update() {
}
}
}
}
if (this->next != NULL) {
if (this->next->update()) {
Momia* seguent = this->next->next;
delete this->next;
this->next = seguent;
info::ctx.momies--;
}
}
return morta;
}
void Momia::insertar(Momia* momia) {
if (this->next != NULL) {
this->next->insertar(momia);
} else {
this->next = momia;
}
}

View File

@@ -1,5 +1,7 @@
#pragma once
#include <memory>
#include "game/engendro.hpp"
#include "game/info.hpp"
#include "game/prota.hpp"
@@ -7,17 +9,14 @@
class Momia : public Sprite {
public:
Momia(JD8_Surface gfx, bool dimoni, Uint16 x, Uint16 y, Prota* sam);
explicit Momia(JD8_Surface gfx, bool dimoni, Uint16 x, Uint16 y, Prota* sam);
void clear();
void draw();
void draw() override;
bool update();
void insertar(Momia* momia);
bool dimoni;
Momia* next;
protected:
Prota* sam;
Engendro* engendro;
std::unique_ptr<Engendro> engendro;
};

View File

@@ -219,6 +219,8 @@ namespace Options {
game.use_new_logo = node["use_new_logo"].get_value<bool>();
if (node.contains("show_title_credits"))
game.show_title_credits = node["show_title_credits"].get_value<bool>();
if (node.contains("show_preload"))
game.show_preload = node["show_preload"].get_value<bool>();
}
// Carrega les opcions des del fitxer configurat
@@ -368,6 +370,7 @@ namespace Options {
file << "game:\n";
file << " use_new_logo: " << (game.use_new_logo ? "true" : "false") << "\n";
file << " show_title_credits: " << (game.show_title_credits ? "true" : "false") << "\n";
file << " show_preload: " << (game.show_preload ? "true" : "false") << "\n";
file << "\n";
// CONTROLS — només moviment del jugador. Les tecles d'UI viuen a

View File

@@ -90,6 +90,7 @@ namespace Options {
int diners_inicial{Defaults::Game::DINERS_INICIAL};
bool use_new_logo{Defaults::Game::USE_NEW_LOGO};
bool show_title_credits{Defaults::Game::SHOW_TITLE_CREDITS};
bool show_preload{Defaults::Game::SHOW_PRELOAD};
};
// Preset PostFX

View File

@@ -5,9 +5,9 @@
class Prota : public Sprite {
public:
Prota(JD8_Surface gfx);
explicit Prota(JD8_Surface gfx);
void draw();
void draw() override;
Uint8 update();
Uint8 frame_pejades;

View File

@@ -22,10 +22,10 @@ struct Entitat {
class Sprite {
public:
Sprite(JD8_Surface gfx);
explicit Sprite(JD8_Surface gfx);
virtual ~Sprite() = default;
void draw();
virtual void draw();
Entitat entitat;
Uint8 cur_frame = 0;

View File

@@ -74,7 +74,7 @@ SDL_AppResult SDL_AppInit(void** /*appstate*/, int /*argc*/, char* /*argv*/[]) {
#ifdef __EMSCRIPTEN__
// MEMFS no persistix entre recàrregues: força valors sensats per a web.
Options::window.fullscreen = false;
Options::window.zoom = 1;
Options::window.zoom = 3;
Options::video.aspect_ratio_4_3 = true;
Options::video.scaling_mode = Options::ScalingMode::INTEGER;
Options::video.texture_filter = Options::TextureFilter::LINEAR;

View File

@@ -32,7 +32,7 @@ namespace scenes {
// PaletteFade copia internament amb memcpy; alliberem la paleta temporal.
JD8_Palette pal = JD8_LoadPalette("gfx/ffase.gif");
fade_.startFadeTo(pal);
std::free(pal);
delete[] pal;
phase_ = Phase::FadingIn;
remaining_ms_ = 5000;

View File

@@ -2,6 +2,7 @@
#include "core/jail/jdraw8.hpp"
#include "core/resources/resource_cache.hpp"
#include "game/options.hpp"
namespace scenes {
@@ -37,6 +38,8 @@ namespace scenes {
void BootLoaderScene::render() const {
JD8_ClearScreen(BG_COLOR);
if (!Options::game.show_preload) return;
const float pct = Resource::Cache::get()->getProgress();
const int filled = static_cast<int>(static_cast<float>(BAR_W) * pct);

View File

@@ -16,7 +16,6 @@ namespace scenes {
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return done_; }
int nextState() const override { return 1; } // 1 → SceneRegistry::tryCreate(num_piramide=255 → intro)
private:
void render() const;

View File

@@ -29,7 +29,6 @@ namespace scenes {
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return phase_ == Phase::Done; }
int nextState() const override { return 1; }
private:
enum class Phase { Rolling,

View File

@@ -37,7 +37,6 @@ namespace scenes {
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return phase_ == Phase::Done; }
int nextState() const override { return 1; }
private:
enum class Phase {

View File

@@ -38,7 +38,6 @@ namespace scenes {
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return phase_ == Phase::Done; }
int nextState() const override { return 1; }
private:
enum class Phase {

View File

@@ -29,7 +29,6 @@ namespace scenes {
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return done_; }
int nextState() const override { return 1; }
private:
SurfaceHandle gfx_;

View File

@@ -21,7 +21,7 @@ namespace scenes {
JD8_Palette pal = JD8_LoadPalette("gfx/menu2.gif");
fade_.startFadeTo(pal);
std::free(pal);
delete[] pal;
phase_ = Phase::FadingIn;
}

View File

@@ -27,7 +27,6 @@ namespace scenes {
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return phase_ == Phase::Done; }
int nextState() const override { return 1; }
private:
enum class Phase { FadingIn,

View File

@@ -22,7 +22,7 @@ namespace scenes {
// la paleta temporal immediatament.
JD8_Palette pal = JD8_LoadPalette("gfx/gameover.gif");
fade_.startFadeTo(pal);
std::free(pal);
delete[] pal;
phase_ = Phase::FadingIn;
remaining_ms_ = 10000;

View File

@@ -19,7 +19,6 @@ namespace scenes {
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return phase_ == Phase::Done; }
int nextState() const override { return 1; }
private:
enum class Phase { FadingIn,

View File

@@ -37,7 +37,7 @@ namespace {
namespace scenes {
SecretaScene::~SecretaScene() {
if (pal_aux_) std::free(pal_aux_);
delete[] pal_aux_;
// pal_active_ NO s'allibera: propietat de main_palette via SetScreenPalette.
}
@@ -51,7 +51,7 @@ namespace scenes {
gfx_ = SurfaceHandle("gfx/tomba1.gif");
pal_aux_ = JD8_LoadPalette("gfx/tomba1.gif");
pal_active_ = static_cast<JD8_Palette>(std::malloc(768));
pal_active_ = new Color[256];
std::memcpy(pal_active_, pal_aux_, 768);
phase_ = Phase::InitialFadeOut;
@@ -62,7 +62,7 @@ namespace scenes {
JD8_ClearScreen(255);
gfx_.reset("gfx/tomba2.gif");
std::free(pal_aux_);
delete[] pal_aux_;
pal_aux_ = JD8_LoadPalette("gfx/tomba2.gif");
// pal_active_ continua sent el mateix buffer: només actualitzem
// el seu contingut. main_palette ja apunta ací.

View File

@@ -31,7 +31,7 @@ namespace {
namespace scenes {
SlidesScene::~SlidesScene() {
if (pal_aux_) std::free(pal_aux_);
delete[] pal_aux_;
// pal_active_ NO s'allibera: propietat de main_palette via SetScreenPalette.
}
@@ -54,7 +54,7 @@ namespace scenes {
// main_palette després del SetScreenPalette — modificar-la modifica
// main_palette directament. `pal_aux_` es manté intacte per a poder
// restaurar després de cada fade-out intermedi.
pal_active_ = static_cast<JD8_Palette>(std::malloc(768));
pal_active_ = new Color[256];
std::memcpy(pal_active_, pal_aux_, 768);
JD8_SetScreenPalette(pal_active_);