Commit Graph
97 Commits
Author SHA1 Message Date
JailDesigner 32dcf264c4 feat(entities): caja-peus concèntrica i animacions direccionals a la momia, còpia de data/ sempre sincronitzada; v0.19.0 2026-04-21 23:21:32 +02:00
JailDesigner a3592d6c73 feat(player): animacions direccionals i stand automàtic en col·lisió; v0.18.0 v0.18.0 2026-04-21 20:41:49 +02:00
JailDesigner 13ae4a51a4 chore: ignora el lockfile intern del harness a .claude/ v0.17.0 2026-04-21 20:18:35 +02:00
JailDesigner ee8dc25181 feat(entities): fase 4 - moviment de la momia amb col·lisió, z-order per peus i contacte amb el jugador 2026-04-21 20:14:47 +02:00
JailDesigner e6b612d164 feat(entities): bloc collision i anchor:feet per entitats amb falsa perspectiva 2026-04-21 19:39:02 +02:00
JailDesigner 1af5ef861f feat(entities): clipping per frame, falsa perspectiva del sarcòfag i piràmide amb 20 sarcs v0.16.0 2026-04-21 19:11:02 +02:00
JailDesigner 7191a08618 feat(entities): integració del sistema d'entitats al GameScene 2026-04-21 13:55:21 +02:00
JailDesigner 96b71b0f65 feat(entities): andamiatge del sistema d'entitats (classes, factory, bus, YAML, placeholders, smoke tests) 2026-04-21 12:35:19 +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 4dc2654a66 docs/audit-game.md: PATCH-01 a DEFERRED i recompte global corregit
PATCH-01 (col·lisió per eix a Player::update) queda per a la branca
`refactor/options-class` junt amb COUP-02/03, ja que tryMove a
AnimatedSprite toca core/rendering/sprite/ i val la pena agrupar-ho
amb el refactor de MapInfo/Options. Recomptes: 28 DONE + 2 DONE
(parcial) + 5 WONTFIX + 4 DEFERRED.
2026-04-20 18:24:01 +02:00
JailDesigner 07a6e4d5d8 Auditoria de game/: neteja general aplicada (rama audit/game) 2026-04-20 18:19:59 +02:00
JailDesigner 331f3fc85c docs/audit-game.md: estat global i top 5 sincronitzat al tancament 2026-04-20 18:17:02 +02:00
JailDesigner 1643811d86 INCONS-05 + triage final d'auditoria game/
- INCONS-05: `buildScreenConfig` passa a return amb designated
  initializers, consistent amb `buildAudioConfig`.
- INCONS-01: marcat DONE (parcial) — queden 3 estils intencionals
  (namespace per estat compartit, classes per entitats, constexpr per
  alias), ja no barreja fortuïta.
- INCONS-03/04, PATCH-03, COUP-04, CONV-04: WONTFIX amb raó
  documentada (contractes diferents, linter ho dicta, o sobre-abstracció
  per a un únic caller).
- COUP-02/03, CONV-02 (parcial): DEFERRED a `refactor/options-class`
  (equivalent a COUP-01 de l'audit d'àudio).
- CONV-01: DONE per HC-01/02/03/04/05/06.
2026-04-20 18:16:05 +02:00
JailDesigner ba5cddd427 PATCH-07: Map passa de class all-static a namespace
Map no té estat d'instància (tots els membres són inline globals) i el
ctor ja s'havia eliminat a HC-03. Convertit a `namespace Map`: callers
no canvien perquè la sintaxi `Map::grid_cols`/`Map::collides(...)` és
idèntica. Pattern consistent amb `Options`, que és l'altre mòdul sense
estat al game/.
2026-04-20 18:12:27 +02:00
JailDesigner f64bb5e267 PATCH-05+PATCH-06+DEAD-05: neteja defenses i sort cerimonial a GameScene
- PATCH-05: `if (Audio::get() != nullptr)` eliminat; Director::init
  crida Audio::init abans de GameScene, comentari ho documenta. Si mai
  salta, és un bug real que val detectar.
- PATCH-06/DEAD-05: short-circuit per `players_.size() <= 1` — no vector
  temporal ni sort amb un sol jugador. La branca multi-jugador queda
  preparada per quan n'hi haja >1 o sarcs-sprites.
2026-04-20 18:10:17 +02:00
JailDesigner 6673168ea0 HC-08: alpha-mask amb nom a Tilemap::render
`0xFF000000U` passa a `constexpr std::uint32_t ALPHA_MASK` local amb
comentari. No s'ha mogut a defines.hpp perquè només s'usa dins de
Tilemap::render; si apareix a altres llocs es podrà centralitzar.
2026-04-20 18:08:22 +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 df059a6fcb DUP-05: clampToPlayfield() extret de Player::update
Els 4 `const auto` MIN_/MAX_ i els dos `std::clamp` es mouen a un mètode
privat `clampToPlayfield`. Separa la responsabilitat de mantenir la
caixa dins del playfield de la integració de moviment.
2026-04-20 18:05:08 +02:00
JailDesigner cb2364490f DUP-04: readField<T> plantilla a options.cpp
Les 7 funcions parse* i els dos parse*Preset repetien el patró
`if (node.contains("x")) { x = node["x"].get_value<T>(); }`. Centralitzat
en `readField(node, key, dst)` a l'anonymous namespace.
2026-04-20 18:03:31 +02:00
JailDesigner 982a1788f1 DUP-03+HC-07: enum↔string per taula a options.cpp
Tres parelles de `*ToString`/`*FromString` substituïdes per tres taules
`constexpr std::array<pair<Enum, string_view>, N>` i dos helpers plantilla
(`enumToString`, `enumFromString`). La primera entrada de cada taula és
el fallback quan l'enum és desconegut o el text no casa: així els valors
"letterbox"/"nearest"/"off" només apareixen una vegada. Resol també HC-07.
2026-04-20 18:01:19 +02:00
JailDesigner 206ba07a6f DUP-02: snapToHorizontalLane/VerticalLane unificats en snapToLane
Les dues funcions compartien l'algorisme complet — només canvien l'eix
actiu (getPosY/setPosY vs getPosX/setPosX) i els paràmetres del Map.
`snapToLane` pren base, pitch, max_idx i dos member-fn pointers i és
cridat pels dos wrappers amb els valors corresponents.
2026-04-20 17:59:05 +02:00
JailDesigner 53bf3a1ff2 DEAD-01..04: retirat el render primitiu del Map
`renderBackground`, `renderSarcsRow`, `getSarcRowBottomY` i els índexs de
paleta `HUD_BG/WALL/FLOOR/SARC` no tenien callers: el Tilemap s'encarrega
del render visual i els sarcs passaran a ser actors sprite. `getSarcRect`
es queda (la usa `collides`). Includes orfes de `screen.hpp`,
`resource_cache.hpp` i `defines.hpp` eliminats. DEAD-04 (ctor Map)
ja es va resoldre durant HC-03.
2026-04-20 17:57:12 +02:00
JailDesigner ea725c2177 INCONS-02+CONV-03: std::cerr → std::cout a options.cpp
Les 10 ocurrències de std::cerr a options.cpp (incloent les de PATCH-04)
passen a std::cout per complir la convenció del projecte ("std::cout
only, no SDL_Log"). Prova manual: amb config.yaml trencat, els missatges
de backup i reescriptura es veuen per stdout.
2026-04-20 17:54:51 +02:00
JailDesigner 505e819af1 HC-06: rutes d'assets de GameScene a Defaults::Pyramid + Config
Afegits `Defaults::Pyramid::{MAP_YAML, PLAYER_SPRITE, PLAYER_YAML,
MUSIC_TRACK}` a utils/defaults.hpp. `GameScene` exposa `Config` i
`defaultConfig()`; el ctor accepta Config (default = Defaults::Pyramid)
i usa els camps enlloc dels literals. `Player` passa a rebre el anim_key
per constructor en lloc de cablejar "player.yaml". Cache key = basename
(convenció preexistent). Cada piràmide futura serà un Config distint.
2026-04-20 17:52:53 +02:00
JailDesigner e84d8cc535 HC-02: mides de sprite/col·lisió del jugador al player.yaml
Afegit bloc `collision:` (size 16, base_size 12) al YAML del player; les
`static constexpr` de `player.hpp` passen a `inline static int` (snake_case)
i es calculen al `Player::loadFrom()`. `sprite_w/sprite_h` es llegeixen
dels mateixos frameWidth/frameHeight que ja hi viuen. GameScene crida el
load abans del spawn (mateix patró que `Map::loadFrom`). Permet ajustar
la caixa del jugador sense recompilar.
2026-04-20 17:46:43 +02:00
JailDesigner da4f78fd0c HC-01+PATCH-02: SPEED_PX_PER_S a Defaults::Player
La velocitat base del jugador (60 px/s) passa de `constexpr` local a
`player.cpp` a `Defaults::Player::SPEED_PX_PER_S`, seguint el patró usat
a l'audit d'àudio. `player.cpp` manté un alias local per llegibilitat.
Elimina el comentari TODO sobre sprint/slow-zones que PATCH-02 volia fora.
2026-04-20 17:43:32 +02:00
JailDesigner db2e5d66e0 PATCH-04: backup <path>.bak abans de reescriure config/presets per fallada
Helper `backupFile(path)` que copia el YAML a `<path>.bak` si existeix.
Es crida a `loadFromFile` (versió mismatch i catch) i a
`loadPresetsFromFile<T>` (manca `presets:`, llista buida, catch).
La primera execució no genera .bak. Cada reescriptura forçada registra el
motiu per stderr (INCONS-02 migrarà a std::cout).

Prova manual: config.yaml amb contingut \"===broken===\" genera
config.yaml.bak amb el contingut previ i un nou config.yaml amb defaults.
2026-04-20 17:40:39 +02:00
JailDesigner 505c20dabc DUP-01: pipeline unificat de presets PostFX i CrtPi a options.cpp
Afegits `loadPresetsFromFile<T>` i `savePresetsToFile<T>` a l'anonymous
namespace; els quatre entry-points passen a ser one-liners que injecten
parse/write/defaults per tipus. Abans eren dues còpies del mateix codi
diferenciades només pel tipus concret de preset. Els YAML regenerats són
byte-a-byte idèntics als anteriors (el formatter i els comentaris es
preserven).
2026-04-20 17:37:58 +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
JailDesigner e6c7f0dcfb HC-03: geometria del mapa traslladada de map.hpp al YAML de la piràmide
El bloc `geometry:` de pyramid.yaml porta les 9 constants base (grid, sarc,
pitch, HUD, murs); els derivats (corridor, board, origin, playfield) es
recalculen al `Map::loadFrom()`. `constexpr` passa a `const` on calia i
els membres estàtics adopten snake_case. Desbloqueja la iteració
"pirámide a pirámide" sense recompilar.
2026-04-20 17:32:56 +02:00
JailDesigner b895475716 Auditoria game/: llista completa de 38 troballes en docs/audit-game.md
Inventari de duplicació, hardcoding, parxes, acoplament, inconsistències,
codi mort i violacions de convencions. Tots els ítems PENDING; els commits
següents en tancaran un a un.
2026-04-20 17:25:04 +02:00
JailDesigner bad71ec4a9 Refactor core/audio: Ja::Engine encapsula l'estat del motor (rama refactor/audio-engine-class)
Tanca els DEFERRED de l'auditoria: COUP-01 (engine sense globals),
COUP-02 (header-only → .hpp/.cpp), PATCH-03 (stb_vorbis macros aïllades),
CONV-02 (engine ja és classe), CONV-03 (callback music-ended),
INCONS-05 complet (silenciar per gain, sense flags de motor).

5 commits atòmics amb build + xvfb entre cadascun. Detalls: plan a
/home/sergio/.claude/plans/crea-un-plan-para-twinkling-bubble.md i
docs/audit-core-audio.md actualitzat.

Únic DEFERRED restant de l'auditoria: PATCH-05 (voice-stealing policy)
— és feature, no deute.
2026-04-20 13:39:50 +02:00
JailDesigner 17301aff66 Auditoria de core/audio: neteja general aplicada (rama audit/core-audio)
17 ítems DONE (HC-01..HC-05 exc. HC-04 WONTFIX, DUP-01..DUP-05, PATCH-01/02/04,
INCONS-01/02/03/05-parcial, COUP-03, DEAD-01..05) + 1 parcial (INCONS-05).

Detalls i resolucions: docs/audit-core-audio.md.
2026-04-20 13:37:45 +02:00
JailDesignerandClaude Opus 4.7 11473588de docs(audit): tancar COUP-01/02, PATCH-03, CONV-02/03, INCONS-05 complet
Actualitza audit-core-audio.md amb els estats finals després del refactor
a refactor/audio-engine-class:

  - COUP-01 (encapsular Ja::* globals): DONE — Ja::Engine classe RAII
  - COUP-02 (header-only): DONE — jail_audio.cpp nou amb stb_vorbis.c
  - PATCH-03 (#undef macros): DONE — migrats a jail_audio.cpp
  - CONV-02 (engine no és classe): DONE — resolt amb COUP-01
  - CONV-03 (callback music-ended): DONE — setOnMusicEnded
  - INCONS-05 (enable/disable divergent): DONE complet — Audio silencia
    per gain, flags de motor eliminats

Queda un sol ítem DEFERRED: PATCH-05 (voice-stealing policy), que és una
feature, no deute.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 13:33:59 +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 5610814c9f docs(audit): tancar audit-core-audio.md amb estat final
17 ítems DONE (+1 parcial INCONS-05), 4 WONTFIX (HC-04, PATCH-03,
COUP-04, COUP-05, INCONS-04), 4 DEFERRED (COUP-01/02, PATCH-05,
CONV-02/03). CONV-01 passa a DONE parcial — la part del motor reutilizable
no es mou a Defaults per disseny.

Tots els DEFERRED pengen del refactor COUP-01 (encapsular Ja::* globals
en una classe), que tancarà també COUP-02, PATCH-03, PATCH-05, CONV-02,
CONV-03 i la meitat restant d'INCONS-05 (la divergència Ja::enable* vs
Audio::enable*).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 12:29:37 +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
JailDesignerandClaude Opus 4.7 66c5801725 refactor(core/audio): Ja::* és l'única font de veritat de MusicState (INCONS-02)
music_.state era una cache d'estat que podia divergir del real —
precisament per això existia getRealMusicState() com a "consulta honesta".
Es descarta la cache:

  - music_.state deixa d'existir; el struct privat Music només manté
    name i loop.
  - getMusicState() passa a ser static i consulta Ja::getMusicState(),
    projectant INVALID/DISABLED al STOPPED que exposa Audio::MusicState.
  - getRealMusicState() desapareix (getMusicState ja fa la consulta real).
  - update() ja no necessita el bloc de sincronització.
  - playMusicInternal/pauseMusic/resumeMusic/stopMusic/fadeOutMusic
    llegeixen getMusicState() en comptes de music_.state i deixen de
    reassignar-lo — Ja ja ho fa.

Externament: cap caller usa getMusicState/getRealMusicState, així que
canviar a static és segur (Audio::get()->getMusicState() continua
funcionant).

pauseMusic/resumeMusic/stopMusic es marquen amb NOLINT per
readability-make-member-function-const pel mateix motiu que INCONS-01
(muten globals de Ja::* que clang-tidy no veu).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 12:15:49 +02:00
JailDesignerandClaude Opus 4.7 964ff104ce refactor(core/audio): quitar const que mentia en 6 mètodes (INCONS-01)
playSound (x2), stopAllSounds, fadeOutMusic, setSoundVolume i
setMusicVolume es declaraven const però mutaven l'estat global de Ja::*
(channels[], outgoing_music, music_volume, sound_volume[]). El const no
protegia res: el compilador no detectava les mutacions perquè anaven a
globals, no a membres, i podia induir a error a qui llegia el header.

Els callers externs van via Audio* (no Audio const*), així que el canvi
és source-compatible.

Clang-tidy falla amb readability-make-member-function-const perquè
tampoc veu la mutació — és exactament el símptoma que motiva el
hallazgo. S'afegeix NOLINT localitzat a cada mètode amb la raó. Quan
s'executi COUP-01 (encapsular Ja::* en classe membre) els NOLINT
cauran sols perquè la mutació serà visible al compilador.

effectiveVolume es queda const — és un càlcul pur sobre els membres.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 12:10:44 +02:00
JailDesignerandClaude Opus 4.7 6a1ed37242 refactor(core/audio): template forEachTargetChannel (DUP-05)
pauseChannel, resumeChannel i stopChannel repetien tres vegades la
cadena "if channel == -1 forEach(channels) else if 0<=channel<MAX
apply()". S'extreu a forEachTargetChannel(channel, fn) i cada funció es
queda només amb la lògica per canal — pausar, reprendre, destruir.

Substitueix codi gairebé idèntic triplicat per una declaració de per
què fa el que fa cada funció.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 12:07:07 +02:00
JailDesignerandClaude Opus 4.7 dee00a8de7 refactor(core/audio): helper stealCurrentIntoOutgoing (DUP-04)
fadeOutMusic i crossfadeMusic repetien el bloc "destruir outgoing
anterior + preFillOutgoing + robar stream + reset state+vorbis". Es
treu a stealCurrentIntoOutgoing(duration_ms) i els dos callers deleguen.

Com a efecte secundari, homogeneïtza la neteja: abans fadeOutMusic no
posava outgoing.fade.active = false en destruir l'anterior i
crossfadeMusic sí. L'helper fa sempre les dues coses. El comportament
observable no canvia perquè la fade s'assigna completament just després,
però elimina una inconsistència latent.

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