diff --git a/.gitignore b/.gitignore index c45c9ef..2dbf80a 100644 --- a/.gitignore +++ b/.gitignore @@ -105,3 +105,6 @@ TestResults*.xml # Coplay AI assistant (resolved via manifest git URL) /Packages/Coplay/ /Packages/com.coplaydev.coplay/ + +# Unity test runner output folder +/TestResults/ diff --git a/Assets/Editor/SpeakerDatabaseSetup.cs b/Assets/Editor/SpeakerDatabaseSetup.cs index 27f627e..e599ddf 100644 --- a/Assets/Editor/SpeakerDatabaseSetup.cs +++ b/Assets/Editor/SpeakerDatabaseSetup.cs @@ -13,8 +13,8 @@ namespace AjedrezPurgatorio.EditorTools /// Unity -batchmode -projectPath . -executeMethod /// AjedrezPurgatorio.EditorTools.SpeakerDatabaseSetup.PopulateAndAssign /// - /// Nota: 'muerte' y 'ricardo' no tienen PNG de retrato todavía; se registran - /// con portrait nulo para evitar warnings y facilitar añadir arte después. + /// Nota: 'ricardo' no tiene PNG de retrato todavía; se registra con + /// portrait nulo para evitar warnings y facilitar añadir arte después. /// public static class SpeakerDatabaseSetup { @@ -24,7 +24,7 @@ namespace AjedrezPurgatorio.EditorTools private static readonly (string speaker, string portrait)[] Entries = { ("narrator", "narrator"), - ("muerte", null), + ("muerte", "muerte"), ("ricardo", null), ("alfil", "alfil"), ("caballo", "caballo"), diff --git a/Assets/Game/Audio/Music.meta b/Assets/Game/Audio/Music.meta deleted file mode 100644 index 1328dc3..0000000 --- a/Assets/Game/Audio/Music.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: be210525ade96b143bbf7a4b90d5e258 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Game/Audio/SFX/capture.wav b/Assets/Game/Audio/SFX/capture.wav deleted file mode 100644 index e406091..0000000 Binary files a/Assets/Game/Audio/SFX/capture.wav and /dev/null differ diff --git a/Assets/Game/Audio/SFX/capture.wav.meta b/Assets/Game/Audio/SFX/capture.wav.meta deleted file mode 100644 index 94cbd25..0000000 --- a/Assets/Game/Audio/SFX/capture.wav.meta +++ /dev/null @@ -1,23 +0,0 @@ -fileFormatVersion: 2 -guid: bbee09e29e03488a87056057de3333d3 -AudioImporter: - externalObjects: {{}} - serializedVersion: 7 - defaultSettings: - serializedVersion: 2 - loadType: 0 - sampleRateSetting: 0 - normalizedSampleRate: 44100 - compressionFormat: 1 - quality: 1 - conversionMode: 0 - platformSettingOverrides: {{}} - forceToMono: 0 - normalize: 1 - preloadAudioData: 1 - loadInBackground: 0 - ambisonic: 0 - 3D: 1 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Game/Audio/SFX/check.wav b/Assets/Game/Audio/SFX/check.wav deleted file mode 100644 index 9cf90ae..0000000 Binary files a/Assets/Game/Audio/SFX/check.wav and /dev/null differ diff --git a/Assets/Game/Audio/SFX/check.wav.meta b/Assets/Game/Audio/SFX/check.wav.meta deleted file mode 100644 index 0726fb9..0000000 --- a/Assets/Game/Audio/SFX/check.wav.meta +++ /dev/null @@ -1,23 +0,0 @@ -fileFormatVersion: 2 -guid: 39c30d32bdb442ee844d9c2204244d96 -AudioImporter: - externalObjects: {{}} - serializedVersion: 7 - defaultSettings: - serializedVersion: 2 - loadType: 0 - sampleRateSetting: 0 - normalizedSampleRate: 44100 - compressionFormat: 1 - quality: 1 - conversionMode: 0 - platformSettingOverrides: {{}} - forceToMono: 0 - normalize: 1 - preloadAudioData: 1 - loadInBackground: 0 - ambisonic: 0 - 3D: 1 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Game/Audio/SFX/checkmate.wav b/Assets/Game/Audio/SFX/checkmate.wav deleted file mode 100644 index 9701c60..0000000 Binary files a/Assets/Game/Audio/SFX/checkmate.wav and /dev/null differ diff --git a/Assets/Game/Audio/SFX/checkmate.wav.meta b/Assets/Game/Audio/SFX/checkmate.wav.meta deleted file mode 100644 index f1dec66..0000000 --- a/Assets/Game/Audio/SFX/checkmate.wav.meta +++ /dev/null @@ -1,23 +0,0 @@ -fileFormatVersion: 2 -guid: 4d305733468c4b7199722466abeeace9 -AudioImporter: - externalObjects: {{}} - serializedVersion: 7 - defaultSettings: - serializedVersion: 2 - loadType: 0 - sampleRateSetting: 0 - normalizedSampleRate: 44100 - compressionFormat: 1 - quality: 1 - conversionMode: 0 - platformSettingOverrides: {{}} - forceToMono: 0 - normalize: 1 - preloadAudioData: 1 - loadInBackground: 0 - ambisonic: 0 - 3D: 1 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Game/Audio/SFX/invalid.wav b/Assets/Game/Audio/SFX/invalid.wav deleted file mode 100644 index eeed3db..0000000 Binary files a/Assets/Game/Audio/SFX/invalid.wav and /dev/null differ diff --git a/Assets/Game/Audio/SFX/invalid.wav.meta b/Assets/Game/Audio/SFX/invalid.wav.meta deleted file mode 100644 index fe0b6ba..0000000 --- a/Assets/Game/Audio/SFX/invalid.wav.meta +++ /dev/null @@ -1,23 +0,0 @@ -fileFormatVersion: 2 -guid: 146caa30b3a549eb8fa8d1fbd296635d -AudioImporter: - externalObjects: {{}} - serializedVersion: 7 - defaultSettings: - serializedVersion: 2 - loadType: 0 - sampleRateSetting: 0 - normalizedSampleRate: 44100 - compressionFormat: 1 - quality: 1 - conversionMode: 0 - platformSettingOverrides: {{}} - forceToMono: 0 - normalize: 1 - preloadAudioData: 1 - loadInBackground: 0 - ambisonic: 0 - 3D: 1 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Game/Audio/SFX/move.wav b/Assets/Game/Audio/SFX/move.wav deleted file mode 100644 index e080caa..0000000 Binary files a/Assets/Game/Audio/SFX/move.wav and /dev/null differ diff --git a/Assets/Game/Audio/SFX/move.wav.meta b/Assets/Game/Audio/SFX/move.wav.meta deleted file mode 100644 index d279031..0000000 --- a/Assets/Game/Audio/SFX/move.wav.meta +++ /dev/null @@ -1,23 +0,0 @@ -fileFormatVersion: 2 -guid: bd7eca68231b48f198532c2a8f8b8ed3 -AudioImporter: - externalObjects: {{}} - serializedVersion: 7 - defaultSettings: - serializedVersion: 2 - loadType: 0 - sampleRateSetting: 0 - normalizedSampleRate: 44100 - compressionFormat: 1 - quality: 1 - conversionMode: 0 - platformSettingOverrides: {{}} - forceToMono: 0 - normalize: 1 - preloadAudioData: 1 - loadInBackground: 0 - ambisonic: 0 - 3D: 1 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Game/Audio/SFX/promotion.wav b/Assets/Game/Audio/SFX/promotion.wav deleted file mode 100644 index f1a8848..0000000 Binary files a/Assets/Game/Audio/SFX/promotion.wav and /dev/null differ diff --git a/Assets/Game/Audio/SFX/promotion.wav.meta b/Assets/Game/Audio/SFX/promotion.wav.meta deleted file mode 100644 index bb3508a..0000000 --- a/Assets/Game/Audio/SFX/promotion.wav.meta +++ /dev/null @@ -1,23 +0,0 @@ -fileFormatVersion: 2 -guid: f7347cab1bf24c4a93f0594fab7b84d8 -AudioImporter: - externalObjects: {{}} - serializedVersion: 7 - defaultSettings: - serializedVersion: 2 - loadType: 0 - sampleRateSetting: 0 - normalizedSampleRate: 44100 - compressionFormat: 1 - quality: 1 - conversionMode: 0 - platformSettingOverrides: {{}} - forceToMono: 0 - normalize: 1 - preloadAudioData: 1 - loadInBackground: 0 - ambisonic: 0 - 3D: 1 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Game/Data/Resources/Dialogues/muerte_quotes.json b/Assets/Game/Data/Resources/Dialogues/muerte_quotes.json new file mode 100644 index 0000000..da89fb3 --- /dev/null +++ b/Assets/Game/Data/Resources/Dialogues/muerte_quotes.json @@ -0,0 +1,26 @@ +{ + "intro": [ + "Mate perfecto... para el tablero. Aquí abajo, la partida sigue.", + "Llegaste al final del tablero. Bienvenido al principio del mío.", + "Todos los caminos del ajedrez terminan frente a mí. Tú llegaste caminando.", + "Tu reina no me mateó a mí. Solo despejó la mesa." + ], + "milestone": [ + "El Purgatorio tiene hambre. Cada turno, una casilla menos.", + "El suelo que pisas ya fue. Apúrate.", + "No te apresuro. El vacío lo hace por mí.", + "Cada casilla que cae fue una partida más. Como la tuya." + ], + "playerWin": [ + "...Bien jugado. El tablero es tuyo. Por ahora.", + "Me capturas hoy. Mañana seré tu siguiente jaque.", + "Toma tu victoria. Yo me quedo con el tiempo.", + "Nadie me había alcanzado sin perder algo primero. Vuelve pronto." + ], + "playerLost": [ + "Te dije que nadie gana aquí. Solo algunos posponen.", + "Tu pieza descansa. ¿Quieres negociar por ella? Los dados no mienten.", + "El vacío también juega. Y siempre va conmigo.", + "Otra partida termina. Otra alma aprende." + ] +} diff --git a/Assets/Game/Data/Resources/Dialogues/muerte_quotes.json.meta b/Assets/Game/Data/Resources/Dialogues/muerte_quotes.json.meta new file mode 100644 index 0000000..7860114 --- /dev/null +++ b/Assets/Game/Data/Resources/Dialogues/muerte_quotes.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f3b9c74e28d14a6b8e50a1c9d7f26b34 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Game/Audio/SFX.meta b/Assets/Game/Data/Resources/Pieces.meta similarity index 76% rename from Assets/Game/Audio/SFX.meta rename to Assets/Game/Data/Resources/Pieces.meta index 07ce356..b44fb9b 100644 --- a/Assets/Game/Audio/SFX.meta +++ b/Assets/Game/Data/Resources/Pieces.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5b071f4614a3499c9929e8f8bbaa0091 +guid: 8a4d2c91b7e34f6095c1d2e8a6b3f740 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Assets/Game/Data/Resources/Pieces/la_muerte.png b/Assets/Game/Data/Resources/Pieces/la_muerte.png new file mode 100644 index 0000000..513f5aa Binary files /dev/null and b/Assets/Game/Data/Resources/Pieces/la_muerte.png differ diff --git a/Assets/Game/Data/Resources/Pieces/la_muerte.png.meta b/Assets/Game/Data/Resources/Pieces/la_muerte.png.meta new file mode 100644 index 0000000..75b6416 --- /dev/null +++ b/Assets/Game/Data/Resources/Pieces/la_muerte.png.meta @@ -0,0 +1,156 @@ +fileFormatVersion: 2 +guid: e1167710624d445aa3b7dea5b24b42a5 +TextureImporter: + internalIDToNameTable: + - first: + 213: 6587412096331147520 + second: la_muerte_0 + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: + - serializedVersion: 2 + name: la_muerte_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1024 + height: 1024 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: 009ede666603b6b50800000000000000 + internalID: 6587412096331147520 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + la_muerte_0: 6587412096331147520 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Game/Data/Resources/Portraits/muerte.png b/Assets/Game/Data/Resources/Portraits/muerte.png new file mode 100644 index 0000000..d0d8d37 Binary files /dev/null and b/Assets/Game/Data/Resources/Portraits/muerte.png differ diff --git a/Assets/Game/Data/Resources/Portraits/muerte.png.meta b/Assets/Game/Data/Resources/Portraits/muerte.png.meta new file mode 100644 index 0000000..9b66b14 --- /dev/null +++ b/Assets/Game/Data/Resources/Portraits/muerte.png.meta @@ -0,0 +1,156 @@ +fileFormatVersion: 2 +guid: c1e6e27e3c8e40129f502081e37ff22d +TextureImporter: + internalIDToNameTable: + - first: + 213: 7419852336102758402 + second: muerte_0 + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: + - serializedVersion: 2 + name: muerte_0 + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 256 + height: 256 + alignment: 0 + pivot: {x: 0, y: 0} + border: {x: 0, y: 0, z: 0, w: 0} + customData: + outline: [] + physicsShape: [] + tessellationDetail: -1 + bones: [] + spriteID: c39a1f4d82b60e718000000000000000 + internalID: 7419852336102758402 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: + muerte_0: 7419852336102758402 + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Game/Data/Resources/SpeakerDatabase.asset b/Assets/Game/Data/Resources/SpeakerDatabase.asset index 8f8de32..3afe358 100644 --- a/Assets/Game/Data/Resources/SpeakerDatabase.asset +++ b/Assets/Game/Data/Resources/SpeakerDatabase.asset @@ -16,7 +16,7 @@ MonoBehaviour: - speakerName: narrator portrait: {fileID: 5364273658523971450, guid: 737c4034b70db6d47978d8d288f51f59, type: 3} - speakerName: muerte - portrait: {fileID: 0} + portrait: {fileID: 7419852336102758402, guid: c1e6e27e3c8e40129f502081e37ff22d, type: 3} - speakerName: ricardo portrait: {fileID: 0} - speakerName: alfil diff --git a/Assets/Game/Scripts/Data/CampaignState.cs b/Assets/Game/Scripts/Data/CampaignState.cs index d0c63c9..714a179 100644 --- a/Assets/Game/Scripts/Data/CampaignState.cs +++ b/Assets/Game/Scripts/Data/CampaignState.cs @@ -65,9 +65,9 @@ public class CampaignState : ScriptableObject public void StartChapter(string chapterId) { _currentChapterPiecesLost = 0; - - // Inicializar piezas vivas para el capítulo - // TODO: Poblar esto desde el board state de GameManager + + // Las piezas vivas se sincronizan desde el tablero vía SyncFromBoard(), + // llamada por CampaignManager tras SetupBoard(). Debug.Log($"[CampaignState] Iniciando capítulo: {chapterId}"); } diff --git a/Assets/Game/Scripts/Data/SpeakerDatabase.cs b/Assets/Game/Scripts/Data/SpeakerDatabase.cs index 9c2708c..759319d 100644 --- a/Assets/Game/Scripts/Data/SpeakerDatabase.cs +++ b/Assets/Game/Scripts/Data/SpeakerDatabase.cs @@ -61,6 +61,27 @@ public class SpeakerDatabase : ScriptableObject return null; } + /// + /// Obtiene el portrait de un speaker sin loguear warning si falta. + /// Usado por sistemas runtime (PortraitRegistry) que comprueban existencia. + /// + public bool TryGetPortrait(string speakerName, out Sprite portrait) + { + portrait = null; + + if (_speakerLookup == null) + { + BuildLookup(); + } + + if (string.IsNullOrEmpty(speakerName)) + { + return false; + } + + return _speakerLookup.TryGetValue(speakerName.ToLower(), out portrait) && portrait != null; + } + /// /// Verifica si existe un speaker en la base de datos. /// diff --git a/Assets/Game/Scripts/Mono/Core/PortraitRegistry.cs b/Assets/Game/Scripts/Mono/Core/PortraitRegistry.cs index 2f4a397..f17dc8a 100644 --- a/Assets/Game/Scripts/Mono/Core/PortraitRegistry.cs +++ b/Assets/Game/Scripts/Mono/Core/PortraitRegistry.cs @@ -30,6 +30,7 @@ public class PortraitRegistry : MonoBehaviour Sprite[] sprites = Resources.LoadAll(_resourcesPortraitsFolder); int registered = 0; + int filled = 0; foreach (var sprite in sprites) { @@ -42,9 +43,15 @@ public class PortraitRegistry : MonoBehaviour database.RegisterPortrait(sprite.name, sprite); registered++; } + else if (!database.TryGetPortrait(sprite.name, out Sprite current) || current == null) + { + // Speaker ya registrado en el asset pero sin arte: rellenarlo. + database.RegisterPortrait(sprite.name, sprite); + filled++; + } } _registered = true; - Debug.Log($"[PortraitRegistry] {registered} retrato(s) registrado(s) desde Resources/{_resourcesPortraitsFolder}."); + Debug.Log($"[PortraitRegistry] {registered} retrato(s) registrado(s), {filled} hueco(s) rellenado(s) desde Resources/{_resourcesPortraitsFolder}."); } } diff --git a/Assets/Game/Scripts/Mono/Purgatory/Duel/MuerteQuotes.cs b/Assets/Game/Scripts/Mono/Purgatory/Duel/MuerteQuotes.cs index 500cc08..00ad7b3 100644 --- a/Assets/Game/Scripts/Mono/Purgatory/Duel/MuerteQuotes.cs +++ b/Assets/Game/Scripts/Mono/Purgatory/Duel/MuerteQuotes.cs @@ -1,12 +1,26 @@ -using System.Collections.Generic; +using System; using UnityEngine; /// /// Frases de La Muerte durante el Duelo del Purgatorio. /// Voz solemne según purgatorio-cosmology.md ("la Muerte", máscara formal). +/// Los pools se cargan de Resources/Dialogues/muerte_quotes.json (data-driven +/// según purgatorio-final.md); las entradas ausentes o un JSON faltante/inválido +/// caen a los pools por defecto embebidos. /// public static class MuerteQuotes { + [Serializable] + private class QuotePools + { + public string[] intro = Array.Empty(); + public string[] milestone = Array.Empty(); + public string[] playerWin = Array.Empty(); + public string[] playerLost = Array.Empty(); + } + + private const string ResourcePath = "Dialogues/muerte_quotes"; + private static readonly string[] Intro = { "Mate perfecto... para el tablero. Aquí abajo, la partida sigue.", @@ -39,15 +53,56 @@ public static class MuerteQuotes "Otra partida termina. Otra alma aprende." }; - public static string GetIntro() => Pick(Intro); - public static string GetMilestone() => Pick(Milestone); - public static string GetPlayerWin() => Pick(PlayerWin); - public static string GetPlayerLost() => Pick(PlayerLost); + private static QuotePools _pools; + private static bool _loaded; + + public static string GetIntro() => Pick(Resolve(p => p.intro, Intro)); + public static string GetMilestone() => Pick(Resolve(p => p.milestone, Milestone)); + public static string GetPlayerWin() => Pick(Resolve(p => p.playerWin, PlayerWin)); + public static string GetPlayerLost() => Pick(Resolve(p => p.playerLost, PlayerLost)); + + /// + /// Descarta la caché del JSON (útil en tests o hot-reload de contenido). + /// + public static void Reload() + { + _loaded = false; + } + + private static string[] Resolve(Func selector, string[] fallback) + { + EnsureLoaded(); + string[] fromJson = selector(_pools); + return fromJson != null && fromJson.Length > 0 ? fromJson : fallback; + } + + private static void EnsureLoaded() + { + if (_loaded) + return; + + _loaded = true; + _pools = new QuotePools(); + + TextAsset asset = Resources.Load(ResourcePath); + if (asset == null) + return; // Sin JSON: los getters usan los pools embebidos. + + try + { + JsonUtility.FromJsonOverwrite(asset.text, _pools); + } + catch (Exception ex) + { + Debug.LogError($"[MuerteQuotes] JSON inválido en {ResourcePath}: {ex.Message}. Usando frases por defecto."); + _pools = new QuotePools(); + } + } private static string Pick(string[] pool) { if (pool == null || pool.Length == 0) return string.Empty; - return pool[Random.Range(0, pool.Length)]; + return pool[UnityEngine.Random.Range(0, pool.Length)]; } } diff --git a/Assets/Game/Scripts/Mono/Purgatory/Duel/PurgatoryDuelManager.cs b/Assets/Game/Scripts/Mono/Purgatory/Duel/PurgatoryDuelManager.cs index b14a14d..313c9e5 100644 --- a/Assets/Game/Scripts/Mono/Purgatory/Duel/PurgatoryDuelManager.cs +++ b/Assets/Game/Scripts/Mono/Purgatory/Duel/PurgatoryDuelManager.cs @@ -175,12 +175,32 @@ public class PurgatoryDuelManager : MonoBehaviour boss.hasMoved = true; board[spawnPos.x, spawnPos.y] = boss; - foreach (var spriteRenderer in bossObj.GetComponentsInChildren(true)) - spriteRenderer.color = new Color(0.55f, 0.07f, 0.07f); + ApplyBossAppearance(bossObj); return boss; } + /// + /// Arte de La Muerte: usa el sprite propio (Resources/Pieces/la_muerte) + /// si existe; si no, tiñe el Rey negro en rojo oscuro como placeholder. + /// + private static void ApplyBossAppearance(GameObject bossObj) + { + SpriteRenderer[] renderers = bossObj.GetComponentsInChildren(true); + + Sprite muerteSprite = Resources.Load("Pieces/la_muerte"); + if (muerteSprite != null) + { + foreach (var spriteRenderer in renderers) + spriteRenderer.sprite = muerteSprite; + + return; + } + + foreach (var spriteRenderer in renderers) + spriteRenderer.color = new Color(0.55f, 0.07f, 0.07f); + } + private static Vector2Int PickBossSpawn(Piece[,] board) { foreach (Vector2Int candidate in new[] diff --git a/Assets/Scenes/Chapter2.unity b/Assets/Scenes/Chapter2.unity index 5210746..d517876 100644 --- a/Assets/Scenes/Chapter2.unity +++ b/Assets/Scenes/Chapter2.unity @@ -7717,7 +7717,7 @@ MonoBehaviour: _speakerNameText: {fileID: 1622539956} _speakerPortrait: {fileID: 991001013} _continueIndicator: {fileID: 1699531460} - _speakerDatabase: {fileID: 0} + _speakerDatabase: {fileID: 11400000, guid: e35f84464de24b4cb0885058c4cdea56, type: 2} _blinkSpeed: 0.5 _advanceDialogueAction: {fileID: 0} --- !u!1 &321429398 diff --git a/Assets/Scenes/Chapter3.unity b/Assets/Scenes/Chapter3.unity index c585af4..c0eb007 100644 --- a/Assets/Scenes/Chapter3.unity +++ b/Assets/Scenes/Chapter3.unity @@ -7234,7 +7234,7 @@ MonoBehaviour: _speakerNameText: {fileID: 1622539956} _speakerPortrait: {fileID: 991001013} _continueIndicator: {fileID: 1699531460} - _speakerDatabase: {fileID: 0} + _speakerDatabase: {fileID: 11400000, guid: e35f84464de24b4cb0885058c4cdea56, type: 2} _blinkSpeed: 0.5 _advanceDialogueAction: {fileID: 0} --- !u!1 &321429398 diff --git a/production/roadmap.md b/production/roadmap.md index d2f8198..345ab03 100644 --- a/production/roadmap.md +++ b/production/roadmap.md @@ -107,3 +107,20 @@ Verificación de los 7 bloqueantes listados arriba: **Pendiente tras esta auditoría**: correr los menús Tools > Purgatory UI en Unity (Ch1, Ch2, Ch3), guardar escenas, verificar consola sin errores de compilación, y commitear. + +--- + +## Actualización 2026-08-24 — Duelo del Purgatorio + La Muerte como personaje + +Commit base: `f7d5606` (Duelo del Purgatorio completo + SFX cableados + menú de opciones + migración +de tests). Sobre esa base: + +| Ítem | Estado | +|------|--------| +| Commit del trabajo pendiente | RESUELTO — `f7d5606` | +| La Muerte como speaker | RESUELTO — retrato `Portraits/muerte.png` (256×256, flat) cableado en SpeakerDatabase.asset con GUID/fileID deterministas; `PortraitRegistry` ahora rellena portraits null de speakers existentes; frases data-driven en `Dialogues/muerte_quotes.json` con fallback embebido | +| Arte del Jefe (La Muerte) | RESUELTO — sprite propio `Resources/Pieces/la_muerte.png` (1024×1024, PPU 100); `SpawnBoss` lo intercambia con fallback al Rey tintado | +| Limpieza menor | RESUELTO — TODO obsoleto en `CampaignState.StartChapter` eliminado (SyncFromBoard ya cubre), `/TestResults/` a .gitignore, duplicado `Assets/Game/Audio/` (metas YAML corruptos, sin referencias) eliminado | + +**Verificación**: Unity 6000.3.13f1 batch mode — assets importados, `SpeakerDatabaseSetup` OK +(12 speakers, DialogueUIs actualizados en Ch2/Ch3), EditMode 200/200 pasados.