Commit Graph
93 Commits
Author SHA1 Message Date
JailDesigner 37fdcb4751 feat(controls): redefinició de tecles i botons del mando des del menú de servei 2026-04-29 19:54:47 +02:00
JailDesigner 72384c3113 feat(anim): cardinal WEST i flip totalment governat pel motor d'animació 2026-04-29 10:11:27 +02:00
JailDesigner 1d43a77800 feat(anim): suport de flip H/V per frame i per animació 2026-04-29 08:30:08 +02:00
JailDesigner 404db74889 feat(audio): cicle de música per estat (INTRO/PLAYING/OUTRO) amb callback de fi i spawn escalonat 2026-04-28 19:41:38 +02:00
JailDesigner d208ba02df feat(player): estat ATTACKING amb edge-detect, animacions i spawn del whip via bus 2026-04-28 16:20:22 +02:00
JailDesigner d3698a1504 feat(audio): suport d'efectes (eco i reverb) per a sons puntuals
Afig una capa d'efectes oberta a la unitat d'àudio. Els sons puntuals
es poden reproduir processats per un eco o un reverb (Freeverb 8C/4AP)
sense afectar la música ni canviar el camí ràpid existent (playSound
sense efecte segueix sent una sola crida sense cost extra).

  - data/sounds.yaml: catàleg de presets nominats (echo: cave/short,
    reverb: stone_hall/small_room).
  - core/audio/audio_effects: processadors purs que prenen un Ja::Sound
    (U8/S16, mono o estèreo) i tornen PCM S16 amb la cua de l'efecte
    (cap a 800 ms eco, 1500 ms reverb).
  - core/audio/sound_effects_config: Meyers singleton que parseja
    sounds.yaml a EchoParams/ReverbParams indexats per nom; l'API
    pública d'Audio fa el lookup per nom.
  - jail_audio::Engine: nou camí playSoundWith{Echo,Reverb} que empenta
    el buffer ja processat a un canal lliure marcat com a has_effect.
    El cap MAX_EFFECT_CHANNELS limita a 4 sons amb efecte simultanis;
    si està al límit, l'engine retorna -1 i Audio cau a la versió seca.
  - Audio::playSoundWith{Echo,Reverb}(name, preset, group) com a única
    via pública. Si el preset no existeix, fallback dry amb avís.
  - director: load("data/sounds.yaml") abans d'Audio::init.
2026-04-27 08:16:16 +02:00
JailDesigner 3b05704e97 feat(intro): colors del rètol independents + restitueix line_gap_px
Dues claus noves a data/ui.yaml > colors:
  room_text         (relleno, default 232)
  room_text_outline (contorn, default 255)
Abans el relleno reciclava notification_text (compartit amb les
notificacions del HUD) i el contorn estava hardcodejat a 255 dins
renderRoomText. Ara són canals separats: tocar el rètol no afecta
les notificacions i tot viu al YAML.

UiConfig guanya roomText() / roomTextOutline(); Defaults::UI hi posa
els fallbacks; renderRoomText els consumeix.

Drive-by: torna a posar la clau room_intro.line_gap_px: 8 al
data/scenes/game.yaml — s'havia perdut en una edició manual del
fitxer per afinar timings.
2026-04-26 17:02:26 +02:00
JailDesigner af25a31639 feat(intro): font dedicada (CaesarDressing) i separació vertical entre línies
Nova font del rètol de transició de sala (ROOM_INTRO i ROOM_OUTRO):
CaesarDressing-Regular, vista com a fonts.room_text al data/ui.yaml i
exposada per UiConfig::roomTextFont(). Director la preloda al costat
de la del HUD i la del placeholder; GameScene construeix
room_text_font_ paral·lel a pause_font_ i renderRoomText l'usa per a
les dues línies de l'INTRO i el text de l'OUTRO. La pausa segueix
amb la font placeholder antiga — no es barregen.

Espai vertical configurable: nova clau room_intro.line_gap_px al
data/scenes/game.yaml (default 8 px). El gap es suma al lineHeight
natural del font i es reparteix simètricament cap amunt i cap avall
del CENTER_Y, deixant el bloc de dues línies sempre centrat. Es
poden afinar des del YAML sense recompilar.
2026-04-26 15:40:09 +02:00
JailDesigner 738cde12bb feat(debug): starting_level a debug.yaml
Nova clau opcional al debug.yaml:
    starting_level: { pyramid: 2, room: 3 }
DebugConfig::resolveStartingLevel retorna optional<StartingLevel>; el
Director, abans de crear el GameScene, demana al LevelManager
setStartingPoint(pyramid, room) — que cerca l'entrada al levels.yaml
i hi posiciona l'índex. Si la clau no està (cas habitual) o
(pyramid, room) no existeix, la progressió arrenca a la primera sala
amb un avís a stderr. createDefaultIfMissing documenta la nova clau
sense escriure-la (la deixem opcional).

Drive-by: NOLINT puntual al capture de STARTING_PLAYERS al Director.
És pre-existent i s'ha activat ara perquè el commit toca director.cpp;
la captura és imprescindible en Debug (valor runtime), clang la veu
redundant en Release on és constexpr literal.
2026-04-26 15:07:34 +02:00
JailDesigner 91614fc545 fix(input): mou gamecontrollerdb.txt a l'arrel i el copia al build/ via CMake 2026-04-25 10:36:55 +02:00
JailDesigner c77f90f6b6 feat(input): carrega gamecontrollerdb i corregeix l'ordre d'assignació de mandos 2026-04-25 10:32:09 +02:00
JailDesigner d5a73a7dad chore(tidy): elimina NOLINTs de naming obsolets a gif.cpp 2026-04-24 21:44:23 +02:00
JailDesigner 50aae790b1 refactor(anim): migra AnimatedSprite al format sprite:/frame_ms, animacions en mapa 2026-04-24 21:01:21 +02:00
JailDesigner 996ff0053e fix(input): dedup de mandos, auto-assign per ordre i cycles sense col·lisió 2026-04-24 18:06:24 +02:00
JailDesigner a725b76684 feat(input): assignació mando↔jugador persistida a config.yaml 2026-04-24 15:13:44 +02:00
JailDesigner 4e9d5df872 feat(players): suport del segon jugador amb events amb player_id 2026-04-24 14:33:52 +02:00
JailDesigner 5c70019eb5 feat(game): pausa graduada amb easing i separador al HUD 2026-04-24 11:56:38 +02:00
JailDesigner 8aa4ec3daf build: aïlla la implementació de stb_vorbis en un TU propi
Mateix patró que stb_image_write: jail_audio.cpp defineix
STB_VORBIS_HEADER_ONLY i només veu les declaracions; les definicions
viuen a source/external/stb_vorbis_impl.cpp, ja exclòs del pre-commit.
Això permet retirar també la desactivació global de
clang-analyzer-core.UndefinedBinaryOperatorResult al .clang-tidy, i el
check queda actiu arreu del nostre propi codi.
2026-04-24 11:18:59 +02:00
JailDesigner 6c1c042637 build: aïlla la implementació de stb_image_write en un TU propi
screenshot.cpp ara només inclou les declaracions de stb_image_write.h;
la macro STB_IMAGE_WRITE_IMPLEMENTATION viu a un .cpp nou dins de
source/external/, que ja està exclòs del pre-commit hook (clang-format
i clang-tidy) i cobert pel seu .clang-tidy amb Checks: '-*'. Així
podem retirar la desactivació global de clang-analyzer-optin.portability
.UnixAPI i el check queda actiu arreu del nostre propi codi.
2026-04-24 11:15:32 +02:00
JailDesigner dc8a7723e3 feat(ui): captura F9, tema a data/ui.yaml i retocs estètics
Captura de pantalla amb F9: volca el canvas ARGB a
screenshots/scr_<timestamp>.png via stb_image_write.

Tot el tema d'UI (colors, timings, fonts, flag hud.bg_enabled)
passa a data/ui.yaml via UiConfig; els valors de Defaults::UI
queden com a fallbacks si el fitxer falta o li manca alguna clau.

Retocs estètics: paleta "uzebox" per a la UI, strings del HUD
(P1/P2/V/T/PIRAMIDE/SALA) traduïts a locale, i fons del marcador
opcional amb backdrop continu de franja per evitar el trencament
visual durant el shake o l'animació d'entrada dels panells.
2026-04-24 10:58:02 +02:00
JailDesigner 329bf692e6 feat(editor): Screen::windowToCanvas() per a input de ratòli 2026-04-23 11:58:58 +02:00
JailDesigner 459d18b49c feat(game): pausa amb F11/START i contorn NSEO al Text 2026-04-23 09:47:59 +02:00
JailDesigner 4da05b3d76 chore(release): v0.21.0 — empaquetat de recursos (AE26) i neteja de fonts
- Nou format de pack AE26 v1 (XOR + djb2), eina `pack_resources`,
  regeneració automàtica de `build/resources.pack` a cada build.
- `Resource::Helper` amb init/shutdown, fallback a disc només en Debug
  (apunta al source tree); Release exigeix pack present.
- Callers de YAML migrats a `loadFile`+deserialize des de buffer.
- Arrel neta: el pack viu a `build/`, eliminat `sync_data`.
- Placeholder font -> `ari-w9500-condensed-display`; esborrades variants
  bold/condensed no usades a `data/fonts/`.
2026-04-23 08:51:21 +02:00
JailDesigner 5312abc761 chore(release): v0.20.0 — notificacions en case frase i renom de presets 2026-04-22 22:05:18 +02:00
JailDesigner 4dd2b6d2a9 feat(mouse): amaga el cursor automàticament després de 3s sense moviment 2026-04-22 21:34:05 +02:00
JailDesigner d5abc6343e fix(gamepad): no retallar el nom del mand a 25 caràcters 2026-04-22 21:20:53 +02:00
JailDesigner 7c35ffaa74 feat(gamepad): detecció i notificacions hot-plug amb GamepadManager 2026-04-22 19:39:16 +02:00
JailDesigner be9309d342 feat(notifier): suport multilinia amb wrap automàtic per espais 2026-04-22 19:33:18 +02:00
JailDesigner fb40d0effb feat(menu): submenú Controls i bloqueig de polling amb menú obert 2026-04-22 19:05:36 +02:00
JailDesigner 350625dec0 feat(menu): primera versió del menú de servei 2026-04-22 18:15:00 +02:00
JailDesignerandClaude Opus 4.7 eb07e7e564 feat(fonts): deinterlace GIF i font ari-w9500 per placeholders
Fix: gif.cpp no desfeia l'interlace, trinxava qualsevol GIF entrellaçat
(tota la sortida de Pillow). Afegeix Defaults::UI::Font per centralitzar
noms/rutes i mou data/font → data/fonts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 13:45:06 +02:00
JailDesigner 6800786194 fix(resources): resoldre rutes de data/ respecte a SDL_GetBasePath
Abans el binari només arrencava si la CWD era la que contenia data/
(típicament build/). Executat des de qualsevol altra carpeta no trobava
keys.yaml, paletes, etc. i es moria al carregar el primer recurs.

Patró seguit de ../aee: Resource::Helper guarda un data root absolut
i resolve(rel) el prefixa davant de rutes no absolutes. Director fa
setDataRoot(SDL_GetBasePath()) com a primera acció del ctor, així tots
els loaders posteriors hi passen sense canvis al call site.

Migrats a Resource::Helper::resolve els loaders que obrien std::ifstream
directament amb el path rebut: Locale, KeyConfig, Player, Map, Tilemap
i EntityLoader. Els que ja usaven Resource::Helper::loadFile
(Resource::Cache, PaletteLoader) hereten el fix automàticament.

Els fitxers de system_folder_ (config.yaml, debug.yaml, postfx.yaml,
crtpi.yaml) es passen com a rutes absolutes i resolve() les deixa
intactes, així no es veuen afectats.

Test targets (test_entities_smoke, test_scene_flow) enllacen ara
resource_helper.cpp perquè el seu codi passa per Helper::resolve. Els
tests corren amb CWD = repo root, on les rutes "data/..." funcionen
amb data root buit sense canviar cap assert.

Verificat executant el binari des de build/, /tmp i / — tots tres
troben els recursos i arrenquen correctament, tant en debug com en
release (release sempre arranca a LOGO).
2026-04-22 11:29:51 +02:00
JailDesigner 87a9f098b5 feat(debug): selector d'escena inicial via debug.yaml
Director encadena LOGO → TITLE → INTRO → GAME amb factories injectades
al ctor de cada placeholder (sense que cap escena conegui el tipus de
la següent). L'escena d'arrencada surt de `debug.yaml` en debug build i
és sempre LOGO en release.

Comportament:
- Release: `debug.yaml` s'ignora; arranca sempre a LOGO.
- Debug: es llig `debug.yaml`. Si no existeix, es crea automàticament
  amb `start_scene: GAME` per no arrossegar 15 s de placeholders al dia
  a dia de gameplay. Valor invàlid o YAML corrupte → warning + GAME,
  però el fitxer no es reescriu (respectem l'edició manual).

`debug.yaml` viu a `$XDG_CONFIG_HOME/jailgames/aee_2026/`, al costat
de `config.yaml`, i queda fora del repo (ajust per màquina de dev).
2026-04-22 10:33:36 +02:00
JailDesigner d31483124e feat(scenes): afegir placeholders LogoScene/TitleScene/IntroScene
Tres escenes de 5 s cadascuna, activables al flux que precedeix GameScene.
Classes separades (sense base comuna) perquè divergiran quan es
desenvolupi contingut real.

Avancen per timeout o per qualsevol tecla no reservada. Les tecles
reservades (F1-F12, Escape) són les declarades a data/input/keys.yaml i
mantenen el seu comportament habitual via GlobalInputs. La
classificació viu a Placeholder::isReservedScancode, i KeyConfig exposa
bindings() per enumerar-les sense duplicar la llista.

Encara no integrats al Director: això es fa al següent commit quan
s'afegeix el selector d'escena inicial.
2026-04-22 10:31:48 +02:00
JailDesignerandClaude Opus 4.7 d9f6fb45f8 refactor(sprite): PATCH-01 add tryMove + virtual collisionBox to base
- Sprite: nou hook virtual `collisionBox() -> SDL_FRect` (default
  `pos_`). Subclasses amb falsa perspectiva el sobreescriuen amb una
  sub-caixa més petita.
- MovingSprite: nou `tryMove(dx, dy, CollisionFn)` que aplica eix a
  eix i reverteix l'eix que col·lisiona; deixa lliscar el sprite
  contra obstacles.
- Player: override `collisionBox()` retornant `getBaseBox()` i
  `update()` reescrita per fer `animate(dt) + tryMove(vx*dt, vy*dt,
  collidesFn)` en lloc del patró antic `update + revertir + reaplicar`.

El comportament observable és idèntic: la integració `v*dt` parteix
de la mateixa posició (post-snap), els revertits per col·lisió són
els mateixos i el `MOVED` que controla l'animació stand/walk segueix
comparant amb `FRAME_START_X/Y`.

Smoke tests 13/13 OK; log Game start → Bye! sota xvfb-run idèntic al
baseline. clang-tidy i cppcheck nets sobre els fitxers modificats.

Refs: docs/audit-game.md (PATCH-01).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 08:53:11 +02:00
JailDesignerandClaude Opus 4.7 a3b03b87be refactor(game): COUP-03 + CONV-02 encapsulate Options state behind setters
L'estat mutable d'Options (window, video, audio, locale, presets, paths)
es mou a un anonymous namespace dins options.cpp. L'API pública del
namespace passa a ser:

- Getters const-ref: window(), video(), audio(), locale(),
  configVersion(), postfxPresets(), crtpiPresets().
- Setters específics que muten i persisteixen via saveToFile():
  setWindowZoom, setWindowFullscreen, setVideoVSync,
  setVideoScalingMode, setVideoRenderInfoPosition,
  setVideoRenderInfoShowTime, setShaderEnabled, setActiveShaderName,
  setPostFXPreset, setCrtPiPreset.

Els setters NO notifiquen els subsistemes: el caller (global_inputs.cpp)
crida explícitament Screen::setWindowZoom, reapplyShader, etc. La part
"observers" del audit es deixa WONTFIX justificat — notificar des
d'Options invertiria la direcció Director→Screen/Audio i Options
aprenria del layout dels subsistemes.

CONV-02 tanca al pas: la part Options dels "globals sense observers"
queda resolta amb la nova encapsulació. Map::* (l'altra meitat de
CONV-02) ja s'havia tancat amb PATCH-07.

Smoke tests 13/13 OK; log Game start → Bye! sota xvfb-run +
SDL_VIDEODRIVER=dummy + SIGINT idèntic al baseline de main.
clang-tidy i cppcheck nets sobre els fitxers modificats.

Refs: docs/audit-game.md (COUP-03, CONV-02, INCONS-01).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 08:37:59 +02:00
JailDesigner 984cc820c0 postfx: eliminat l'uniform oversample i vestigis de supersampling/Lanczos mai implementats 2026-04-21 08:09:23 +02:00
JailDesigner c9e6261cf1 postfx: scanlines analítiques amb smoothstep a l'estil crtpi, exposant forma per preset YAML 2026-04-20 20:57:14 +02:00
JailDesigner beb6a4aad1 HC-04+HC-05: índexs de paleta UI centralitzats a Defaults::UI
Afegit `Defaults::UI::` amb els 5 índexs (RENDER_INFO_TEXT/SHADOW,
CANVAS_BG, NOTIFICATION_TEXT/BG, EXIT_TEXT/BG) i 2 durades
(NOTIFICATION_TIME_MS, EXIT_TIME_MS). `ui_palette.hpp` i
`notification_presets.hpp` queden com a alias: canviar el tema global
és tocar un sol bloc. Equivalent a tenir un "tema" (vindria d'un YAML el
dia que calga).
2026-04-20 18:07:31 +02:00
JailDesigner ab7a79dc50 COUP-01: notification_presets.hpp mogut de game/ui/ a core/rendering/
El fitxer definia presets del Notifier (que ja viu a core/rendering/), però
estava a game/ i era consumit únicament per core/input/global_inputs.cpp
— una dependència invertida core→game. El mogueu al costat del component
que consumeix; el comentari de `notifier.hpp` hi apunta. `game/ui/` es
queda només amb `ui_palette.hpp`.
2026-04-20 17:35:03 +02:00
JailDesignerandClaude Opus 4.7 2ec6fd23b6 feat(core/audio): music-ended callback (CONV-03)
Ja::Engine exposa setOnMusicEnded(std::function<void()>). Quan
updateCurrentMusic detecta que la pista actual ha acabat de drenar
(times == 0 && stream buit) s'atura primer via stopMusic i després
s'invoca el callback — així un eventual playMusic des del callback pot
començar amb l'engine en estat net.

Audio proxeia amb setOnMusicEnded. Contracte documentat: s'executa al
mateix thread que Audio::update (render loop); no fer operacions
blocants.

Cap consumidor actualment; només exposa l'API per quan calgui.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 13:31:47 +02:00
JailDesignerandClaude Opus 4.7 4e0dcfe739 refactor(core/audio): remove engine enable flags (INCONS-05)
Ja::Engine tenia music_enabled_ i sound_enabled_ com a membres amb el
seu propi enableMusic/enableSound setter, i disseminava checks
`if (!music_enabled_)` / `if (!sound_enabled_)` per gairebé totes les
operacions. Després del commit 3, ningú crida aquests setters: Audio
gestiona el gating per gain (effectiveVolume retorna 0 quan
music_enabled_/sound_enabled_ d'Audio són false). L'usuari ha confirmat
que vol mantenir aquesta semàntica (silenciar per gain, no parar).

Es retira tot el camí mort:

  - Membres music_enabled_ / sound_enabled_ eliminats.
  - Mètodes Engine::enableMusic(bool) / Engine::enableSound(bool)
    eliminats.
  - Checks `if (!music_enabled_)` / `if (!sound_enabled_)` eliminats de
    playMusic, pauseMusic, resumeMusic, fadeOutMusic, crossfadeMusic,
    updateCurrentMusic, playSound, playSoundOnChannel, pauseChannel,
    resumeChannel, updateSoundChannels.
  - Enum MusicState::DISABLED eliminat (inabastable sense el flag).
  - Enum ChannelState::INVALID i SOUND_DISABLED eliminats (no usats
    després de DEAD-04).
  - Audio::getMusicState treu el case DISABLED del switch.

Audio::enableMusic/enableSound i els seus flags music_enabled_/
sound_enabled_ d'Audio romanen — són ells els qui porten la decisió al
motor via setXxxVolume(config_.X_volume) → effectiveVolume → gain.
Tanca la meitat restant d'INCONS-05.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 13:29:00 +02:00
JailDesignerandClaude Opus 4.7 961a59c460 refactor(core/audio): Audio owns the engine (migration)
Audio guanya un std::unique_ptr<Ja::Engine> engine_ com a membre privat.
Es construeix a initSDLAudio amb els defaults de Defaults::Audio; es
destrueix sol al ~Audio (cap Ja::quit() explícit).

Totes les crides Ja::funcióN(...) d'audio.cpp passen a engine_->funcióN:
  - playMusicInternal: crossfadeMusic/stopMusic/playMusic
  - pauseMusic, resumeMusic, stopMusic, fadeOutMusic
  - playSound x2, stopAllSounds
  - setSoundVolume, setMusicVolume
  - getMusicState (via instance->engine_ — és static però pot arribar-hi)

Les free functions forwarder del commit 2 (Ja::init/quit/update/playMusic/
pauseMusic/resumeMusic/stopMusic/fadeOutMusic/crossfadeMusic/
getMusicState/setMusicVolume/enableMusic i els equivalents de so) es
retiren del .hpp i del .cpp: ja no les crida ningú.

Queden com a free functions permanents (les usa Resource::Cache):
loadMusic (x2), loadSound, deleteMusic, deleteSound. Aquests darrers
consulten Engine::active() per notificar el motor abans d'alliberar.

audio.hpp forward-declara també `class Ja::Engine` perquè engine_ és
un std::unique_ptr i el dtor es defineix out-of-line al .cpp (= default)
— així no cal incloure jail_audio.hpp al header.

Els NOLINT(readability-make-member-function-const) d'audio.cpp es
mantenen amb raó actualitzada: std::unique_ptr::operator-> retorna T*
(no const T*), per tant clang-tidy veu un mètode d'Audio que podria ser
const encara que muti l'Engine via el punter. La mutació és real; el
NOLINT segueix sent honest.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 13:25:03 +02:00
JailDesignerandClaude Opus 4.7 1d124e967d refactor(core/audio): introduce Ja::Engine class (COUP-01, CONV-02)
Encapsula l'estat del motor a Ja::Engine — classe amb RAII (ctor obre
SDL_AudioDevice, dtor el tanca) i `assert` al ctor contra instàncies
concurrents. Tots els globals del namespace anònim del commit 1
(current_music, channels[], audio_spec, music_volume, sound_volume[],
music_enabled, sound_enabled, sdl_audio_device, outgoing_music,
incoming_fade) es converteixen en membres privats amb sufix `_`. Els
helpers que toquen estat (stealCurrentIntoOutgoing,
updateOutgoing/IncomingFade, updateCurrent*, forEachTargetChannel)
passen a mètodes privats. Els stateless (feedMusicChunk, pumpMusic,
preFillOutgoing, fadeProgress) es queden al namespace anònim del .cpp.

Engine::active() static accessor: registrat al ctor, desregistrat al
dtor. Els deleters Ja::deleteMusic/deleteSound consulten Engine::active()
per parar canals/música abans d'alliberar recursos; si active() és
nullptr (shutdown-order invertit), fan cleanup local silenciós. Això
resol CONV-02 (engine ja és classe) i tanca COUP-01.

Compat amb els callers: les free functions Ja::init/quit/update,
Ja::playMusic, Ja::pauseMusic, Ja::resumeMusic, Ja::stopMusic,
Ja::fadeOutMusic, Ja::crossfadeMusic, Ja::getMusicState,
Ja::setMusicVolume, Ja::enableMusic, Ja::playSound, Ja::playSoundOnChannel,
Ja::pauseChannel, Ja::resumeChannel, Ja::stopChannel, Ja::setSoundVolume
i Ja::enableSound es mantenen com a thin forwarders contra Engine::active().
audio.cpp no canvia en aquest commit; les seves crides Ja:: continuen
funcionant. Al commit 3 Audio agafarà ownership directe via
std::unique_ptr<Engine> i aquests forwarders desapareixeran.

Ja::init() crea un std::unique_ptr<Engine> propietari dins d'un namespace
anònim del .cpp (encara no és Audio qui posseeix — això és el commit 3).
Ja::loadMusic/loadSound/deleteMusic/deleteSound es queden com a free
functions permanents (no depenen del motor per construir).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 13:21:01 +02:00
JailDesignerandClaude Opus 4.7 bc53174f2b refactor(core/audio): split jail_audio.hpp/.cpp (COUP-02, PATCH-03)
Preparatori per COUP-01. No canvia comportament: només mou les
definicions inline de jail_audio.hpp a un jail_audio.cpp nou, deixant
el .hpp amb declaracions + tipus + constants.

Efectes col·laterals que es tanquen gratis:

  - COUP-02: jail_audio deixa de ser header-only. Abans cada TU que
    l'incloïa compilava 650 línies inline (amb stb_vorbis.c en mode
    HEADER_ONLY). Ara el .hpp té ~130 línies de declaracions.
  - PATCH-03: #include "external/stb_vorbis.c" + #undef L/C/R/PLAYBACK_*
    viu només al final de jail_audio.cpp. audio.cpp perd el workaround
    dels undefs.

Estat intern (current_music, channels[], audio_spec, music_volume,
sound_volume[], music_enabled, sound_enabled, sdl_audio_device,
outgoing_music, incoming_fade) baixa al .cpp dins d'un namespace
anònim (internal linkage). Els helpers privats (feedMusicChunk,
pumpMusic, preFillOutgoing, fadeProgress, update*Fade, updateCurrent*,
stealCurrentIntoOutgoing, forEachTargetChannel) també viuen a
l'anonymous namespace.

Ja::Music i Ja::Sound són públics; el .hpp forward-declara
`struct stb_vorbis` perquè els callers només els agafen per punter.
NOLINT(readability-identifier-naming) al forward-decl perquè el nom
l'imposa stb_vorbis.

jail_audio.cpp s'afegeix a APP_SOURCES al CMakeLists.txt.

.clang-tidy: desactivat clang-analyzer-core.UndefinedBinaryOperatorResult
globalment. Fals positiu conegut a external/stb_vorbis.c que abans no es
disparava (no hi havia TU que cridés directament stb_vorbis_seek_start
des del mateix fitxer on la funció està inlined); ara que jail_audio.cpp
sí que fa aquestes crides directes, el path d'anàlisi passa pel .c i el
check dispara. HeaderFilterRegex no aplica a clang-analyzer-*.

Preparació per COUP-01 (introduir Ja::Engine al commit següent).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 13:11:15 +02:00
JailDesignerandClaude Opus 4.7 0229468cc5 docs(core/audio): unificar comentaris a català (INCONS-03)
Els comentaris d'audio.hpp i audio.cpp barrejaven castellà i català —
"Reproduce la música", "Detener", "Estado de la música"... conviven amb
"Reproduir", "Aturar", "Estat". Unifico tot a català, incloent-hi:

  - Comentaris d'includes ("Para X" → "Per a X").
  - Secció headers ("// --- Configuración general ---" → "// --- Configuració
    general ---").
  - Descripcions de mètodes i membres.
  - Comentaris de l'enum Group/MusicState.

Els comentaris que ja estaven en català (els afegits a la mateixa
auditoria i els preexistents) no es toquen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 12:28:17 +02:00
JailDesignerandClaude Opus 4.7 d03e9fffdc refactor(core/audio): eliminar 4 sobrecàrregues enable/disable no-arg (INCONS-05/DEAD-05)
enableSound()/disableSound()/enableMusic()/disableMusic() eren inline
thunks que només cridaven la versió amb bool. Cap caller del projecte
les invoca. Es deixa només enableSound(bool) + toggleSound() (i
l'equivalent per música) com a API única.

Queda pendent la divergència semàntica amb Ja::enableSound/enableMusic
(Audio silencia per gain, Ja pararia els canals). No es resol aquí
perquè és una decisió de disseny que lliga amb l'encapsulament de
Ja::* (COUP-01).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 12:24:55 +02:00
JailDesignerandClaude Opus 4.7 3089177e69 refactor(core/audio): eliminar 5 funcions mortes a Ja:: (DEAD-01..04, PATCH-04)
Verificat amb grep que cap caller dins de source/ invoca:
  - Ja::setMusicPosition (stub no-op)
  - Ja::getMusicPosition (stub que retornava 0)
  - Ja::getMusicFilename (accessor a Music::filename, el camp s'accedeix
    directament des d'Audio)
  - Ja::getChannelState (getter sense consumidor públic)
  - Ja::setVolume (combinació master — Audio fa el gate per canal)

Es manté loadMusic(buffer, length, filename) perquè Resource::Cache
l'utilitza per registrar el path dins del Ja::Music.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 12:22:41 +02:00
JailDesignerandClaude Opus 4.7 355fcfb9de refactor(core/audio): iostream → cstdio per als missatges d'error (COUP-03)
jail_audio.hpp és un header inline que arrossegava <iostream> a cada
TU que l'incloïa, només per a std::cout de missatges d'error. <iostream>
és un include pesat i aquí no aporta res: estem fent logging simple.

Passem a <cstdio> + std::fprintf(stderr, ...) als dos fitxers (jail_audio
i audio.cpp). Missatges prefixats "Ja::funció:" / "Audio:" seguint la
convenció "Module: message" que ja fan servir director.cpp, options.cpp,
resource_cache.cpp, etc.

Efecte col·lateral: stderr és l'stream correcte per als errors (abans
anaven a stdout).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 12:20:08 +02:00
JailDesignerandClaude Opus 4.7 a917f6660a fix(core/audio): recuperar el nom del Ja::Music::filename (PATCH-02)
L'overload playMusic(Ja::Music*, ...) buidava music_.name a cegues, així
que getCurrentMusicName() mentia després d'aquest camí tot i que el
Ja::Music que li passaves podia portar filename (Ja::loadMusic té un
overload que l'accepta).

Ara copiem music->filename — si el caller va usar l'overload amb
filename, el nom es conserva; si no, queda buit i el contracte és
explícit al comentari.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 12:17:32 +02:00