Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11a909b58d | ||
|
|
17cf6e0636 | ||
|
|
7d0aca506a | ||
|
|
7cd7b4dc4f | ||
|
|
6d4f606eeb | ||
|
|
1ccd444aad | ||
|
|
3bfca35f3a | ||
|
|
3e5217066f | ||
|
|
4da50571eb | ||
|
|
d4593d0686 | ||
|
|
a53cf4b797 | ||
|
|
bcbf990a5f | ||
|
|
27cf2f032d | ||
|
|
6f6aa182f7 | ||
|
|
b65eeb7aaa | ||
|
|
37fdcb4751 | ||
|
|
4739354875 | ||
|
|
2879cbdb17 | ||
|
|
38070b33aa | ||
|
|
9f0a566ffb | ||
|
|
6397f9d4eb | ||
|
|
72384c3113 | ||
|
|
9dc7ff0da3 | ||
|
|
1d43a77800 | ||
|
|
39eda56c9d | ||
|
|
a351f920ba | ||
|
|
72642dc10e | ||
|
|
404db74889 | ||
|
|
91f664c4d5 | ||
|
|
ce55aeeadc | ||
|
|
8f66961c7b | ||
|
|
121558b1fd | ||
|
|
df60709e6d | ||
|
|
d208ba02df | ||
|
|
5db3bb0bd0 | ||
|
|
9ad21eb5bb | ||
|
|
4752eb8ad7 | ||
|
|
887b8f3aa3 | ||
|
|
2f3643f09c | ||
|
|
cc529afeb2 | ||
|
|
15a7c4e18a | ||
|
|
7d725f99f4 | ||
|
|
e9ef6565a7 | ||
|
|
47bb92144e | ||
|
|
d3698a1504 | ||
|
|
42062dca55 | ||
|
|
5502191725 | ||
|
|
8e2865efbd | ||
|
|
99eb42b319 | ||
|
|
8243e95ef1 | ||
|
|
0752df0f62 | ||
|
|
98303f7ce8 | ||
|
|
7982df922b | ||
|
|
8da18a6a86 | ||
|
|
3b05704e97 | ||
|
|
00594d9eb2 | ||
|
|
af25a31639 | ||
|
|
f52e7f4682 | ||
|
|
e2f6d326aa | ||
|
|
b18bd4d021 | ||
|
|
738cde12bb | ||
|
|
b147590a39 | ||
|
|
d52cc1c509 | ||
|
|
9d4f92c98b | ||
|
|
f51fe3d820 | ||
|
|
fe17c35c67 | ||
|
|
8fd06887d1 | ||
|
|
580c13e854 | ||
|
|
645db217fa | ||
|
|
cd961cf718 | ||
|
|
c0214b233b | ||
|
|
d054d1b246 | ||
|
|
cd5bb94223 | ||
|
|
178ce486c2 | ||
|
|
811c6c482d | ||
|
|
59cd9b524c | ||
|
|
28e55a0615 | ||
|
|
5c5cd1d5da | ||
|
|
377e5d0b97 | ||
|
|
648a5932f8 | ||
|
|
b54f3fc622 | ||
|
|
63bb41425a | ||
|
|
9a2c677153 | ||
|
|
4a9a037cd8 | ||
|
|
67b06c0a10 | ||
|
|
368f9d2d6a | ||
|
|
651db50a93 | ||
|
|
decf9f5d90 | ||
|
|
f921f6bf2b | ||
|
|
abf52be7e0 | ||
|
|
3e379ce129 | ||
|
|
c38c80753a | ||
|
|
4667e0abf9 | ||
|
|
7f78f556f3 | ||
|
|
e8407ce35e | ||
|
|
188f035333 | ||
|
|
3646e6a4aa | ||
|
|
f3f5fa5e92 | ||
|
|
3a750822f6 | ||
|
|
06ff11d563 | ||
|
|
f0a0515f28 | ||
|
|
4758d5d8a4 | ||
|
|
64390f8ad7 | ||
|
|
c4caa9021e | ||
|
|
a64f33fe43 | ||
|
|
cd50626b88 | ||
|
|
7b09a076b2 | ||
|
|
91614fc545 | ||
|
|
c77f90f6b6 | ||
|
|
4f1a3260a5 | ||
|
|
1af43ef052 | ||
|
|
d5a73a7dad | ||
|
|
578916bc58 | ||
|
|
ae8b9d8fe6 | ||
|
|
50aae790b1 | ||
|
|
ea0f6d98a9 | ||
|
|
291cbdc957 | ||
|
|
858e444842 | ||
|
|
511f8d5b09 | ||
|
|
e22f3240da | ||
|
|
1cc2e28b1b | ||
|
|
820b9f2d21 | ||
|
|
f3977170b7 | ||
|
|
996ff0053e | ||
|
|
e5b8d7731b | ||
|
|
a725b76684 | ||
|
|
4e9d5df872 | ||
|
|
74dad71cc2 | ||
|
|
5c70019eb5 | ||
|
|
8aa4ec3daf | ||
|
|
6c1c042637 | ||
|
|
dc8a7723e3 | ||
|
|
82b229222f | ||
|
|
67ecb704d2 | ||
|
|
936adf1405 | ||
|
|
0d914cc6dd | ||
|
|
1117264758 | ||
|
|
d0503ff8b1 | ||
|
|
e3cf2eb773 | ||
|
|
0f56ce7567 | ||
|
|
87b08a7c7c | ||
|
|
00eb696859 | ||
|
|
48ff1839bc | ||
|
|
01145464ed | ||
|
|
f30bb1fb0f | ||
|
|
05cdcfa660 | ||
|
|
a612a8255f | ||
|
|
329bf692e6 | ||
|
|
219f8033eb | ||
|
|
6d8423764d | ||
|
|
459d18b49c | ||
|
|
243ba06351 | ||
|
|
4da05b3d76 | ||
|
|
aeed0e01c2 | ||
|
|
9d256b7f2d | ||
|
|
5312abc761 | ||
|
|
4dd2b6d2a9 | ||
|
|
d5abc6343e | ||
|
|
a2b01ba9d5 | ||
|
|
a230351b79 | ||
|
|
7c35ffaa74 | ||
|
|
be9309d342 | ||
|
|
591b4b58e5 | ||
|
|
fb40d0effb | ||
|
|
350625dec0 | ||
|
|
e4a8b4cd3e | ||
|
|
eb07e7e564 | ||
|
|
03f1c5f926 | ||
|
|
6800786194 | ||
|
|
9c9b6028e7 | ||
|
|
2cb21211ef | ||
|
|
87a9f098b5 | ||
|
|
d31483124e | ||
|
|
2883df57da | ||
|
|
510ab55a33 | ||
|
|
a7e681a347 | ||
|
|
b19a6e25da | ||
|
|
d9f6fb45f8 | ||
|
|
64e0178ee9 | ||
|
|
a3b03b87be | ||
|
|
32dcf264c4 | ||
|
|
a3592d6c73 |
No files matched your search
@@ -8,12 +8,7 @@ Checks:
|
||||
- -bugprone-integer-division
|
||||
- -bugprone-easily-swappable-parameters
|
||||
- -bugprone-narrowing-conversions
|
||||
- -modernize-avoid-c-arrays,-warnings-as-errors
|
||||
# Fals positiu recurrent en codi C de tercers (source/external/stb_vorbis.c).
|
||||
# HeaderFilterRegex no aplica a clang-analyzer-*, així que cal desactivar
|
||||
# explícitament. El check inferència sobre variables locals de legacy C
|
||||
# genera reports que no reflecteixen bugs reals.
|
||||
- -clang-analyzer-core.UndefinedBinaryOperatorResult
|
||||
- -modernize-avoid-c-arrays
|
||||
|
||||
WarningsAsErrors: '*'
|
||||
# Headers nostres (excloem source/external/ que conté dependències de tercers no editables)
|
||||
@@ -24,61 +19,89 @@ CheckOptions:
|
||||
# bugprone-empty-catch: aceptar catches vacíos marcados con @INTENTIONAL en un comentario
|
||||
- { key: bugprone-empty-catch.IgnoreCatchWithKeywords, value: '@INTENTIONAL' }
|
||||
|
||||
# Variables locales en snake_case
|
||||
# =====================================================================
|
||||
# CONSTANTES → UPPER_CASE (compile-time y runtime, en cualquier scope)
|
||||
# =====================================================================
|
||||
# Todo lo que sea const o constexpr se identifica visualmente en UPPER_CASE,
|
||||
# sin importar si es global, local, miembro o static.
|
||||
|
||||
# constexpr en cualquier scope (globales y locales)
|
||||
- { key: readability-identifier-naming.ConstexprVariableCase, value: UPPER_CASE }
|
||||
|
||||
# Constantes globales (const no-constexpr)
|
||||
- { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE }
|
||||
|
||||
# Constantes locales (const en función)
|
||||
- { key: readability-identifier-naming.LocalConstantCase, value: UPPER_CASE }
|
||||
|
||||
# Static const a nivel de archivo/namespace
|
||||
- { key: readability-identifier-naming.StaticConstantCase, value: UPPER_CASE }
|
||||
|
||||
# Miembros static const/constexpr de clase (p.ej. static constexpr int MAX = 100;)
|
||||
- { key: readability-identifier-naming.ClassConstantCase, value: UPPER_CASE }
|
||||
|
||||
# Miembros const no-static de clase (p.ej. const int limit;)
|
||||
- { key: readability-identifier-naming.ConstantMemberCase, value: UPPER_CASE }
|
||||
|
||||
# Valores de enums
|
||||
- { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE }
|
||||
|
||||
# NOTA: Los parámetros const NO se tratan como constantes aquí.
|
||||
# Un parámetro sigue siendo un parámetro aunque sea const → hereda ParameterCase.
|
||||
|
||||
# =====================================================================
|
||||
# VARIABLES NO-CONST
|
||||
# =====================================================================
|
||||
|
||||
# Variables locales
|
||||
- { key: readability-identifier-naming.VariableCase, value: lower_case }
|
||||
- { key: readability-identifier-naming.LocalVariableCase, value: lower_case }
|
||||
|
||||
# Miembros privados en snake_case con sufijo _
|
||||
- { key: readability-identifier-naming.PrivateMemberCase, value: lower_case }
|
||||
- { key: readability-identifier-naming.PrivateMemberSuffix, value: _ }
|
||||
# Parámetros de función
|
||||
- { key: readability-identifier-naming.ParameterCase, value: lower_case }
|
||||
|
||||
# Miembros protegidos en snake_case con sufijo _
|
||||
- { key: readability-identifier-naming.ProtectedMemberCase, value: lower_case }
|
||||
- { key: readability-identifier-naming.ProtectedMemberSuffix, value: _ }
|
||||
|
||||
# Miembros públicos en snake_case (sin sufijo)
|
||||
- { key: readability-identifier-naming.PublicMemberCase, value: lower_case }
|
||||
|
||||
# Namespaces en CamelCase
|
||||
- { key: readability-identifier-naming.NamespaceCase, value: CamelCase }
|
||||
|
||||
# Variables estáticas privadas como miembros privados
|
||||
# Variables estáticas no-const (static locales, static file-scope,
|
||||
# y static members no-const de clase como el instance_ de un Singleton).
|
||||
# Sufijo _ para marcar que tienen storage estático.
|
||||
- { key: readability-identifier-naming.StaticVariableCase, value: lower_case }
|
||||
- { key: readability-identifier-naming.StaticVariableSuffix, value: _ }
|
||||
|
||||
# Constantes estáticas sin sufijo
|
||||
- { key: readability-identifier-naming.StaticConstantCase, value: UPPER_CASE }
|
||||
# =====================================================================
|
||||
# MIEMBROS DE CLASE NO-CONST
|
||||
# =====================================================================
|
||||
# Privados: snake_case con sufijo _
|
||||
- { key: readability-identifier-naming.PrivateMemberCase, value: lower_case }
|
||||
- { key: readability-identifier-naming.PrivateMemberSuffix, value: _ }
|
||||
|
||||
# Constantes globales en UPPER_CASE
|
||||
- { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE }
|
||||
# Protegidos: snake_case con sufijo _
|
||||
- { key: readability-identifier-naming.ProtectedMemberCase, value: lower_case }
|
||||
- { key: readability-identifier-naming.ProtectedMemberSuffix, value: _ }
|
||||
|
||||
# Variables constexpr globales en UPPER_CASE
|
||||
- { key: readability-identifier-naming.ConstexprVariableCase, value: UPPER_CASE }
|
||||
# Públicos: snake_case sin sufijo
|
||||
- { key: readability-identifier-naming.PublicMemberCase, value: lower_case }
|
||||
|
||||
# Constantes locales en UPPER_CASE
|
||||
- { key: readability-identifier-naming.LocalConstantCase, value: UPPER_CASE }
|
||||
|
||||
# Constexpr miembros en UPPER_CASE (sin sufijo)
|
||||
- { key: readability-identifier-naming.ConstexprMemberCase, value: UPPER_CASE }
|
||||
|
||||
# Constexpr miembros privados/protegidos con sufijo _
|
||||
- { key: readability-identifier-naming.ConstexprMethodCase, value: UPPER_CASE }
|
||||
|
||||
# Clases, structs y enums en CamelCase
|
||||
# =====================================================================
|
||||
# TIPOS
|
||||
# =====================================================================
|
||||
- { key: readability-identifier-naming.ClassCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.StructCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.EnumCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.UnionCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.TypeAliasCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.TypedefCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.TemplateParameterCase, value: CamelCase }
|
||||
|
||||
# Valores de enums en UPPER_CASE
|
||||
- { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE }
|
||||
# Namespaces
|
||||
- { key: readability-identifier-naming.NamespaceCase, value: CamelCase }
|
||||
|
||||
# Métodos en camelBack (sin sufijos)
|
||||
# =====================================================================
|
||||
# FUNCIONES Y MÉTODOS (incluyendo constexpr)
|
||||
# =====================================================================
|
||||
# Un método/función constexpr es un invocable, no una constante → camelBack.
|
||||
- { key: readability-identifier-naming.FunctionCase, value: camelBack }
|
||||
- { key: readability-identifier-naming.ConstexprFunctionCase, value: camelBack }
|
||||
- { key: readability-identifier-naming.MethodCase, value: camelBack }
|
||||
- { key: readability-identifier-naming.PrivateMethodCase, value: camelBack }
|
||||
- { key: readability-identifier-naming.ProtectedMethodCase, value: camelBack }
|
||||
- { key: readability-identifier-naming.PublicMethodCase, value: camelBack }
|
||||
|
||||
# Funciones en camelBack
|
||||
- { key: readability-identifier-naming.FunctionCase, value: camelBack }
|
||||
|
||||
# Parámetros en lower_case
|
||||
- { key: readability-identifier-naming.ParameterCase, value: lower_case }
|
||||
- { key: readability-identifier-naming.ConstexprMethodCase, value: camelBack }
|
||||
@@ -33,7 +33,10 @@ configure_file(${CMAKE_SOURCE_DIR}/source/version.h.in ${CMAKE_BINARY_DIR}/versi
|
||||
# Sources comuns del sistema d'entitats: s'afegeixen tant a l'executable
|
||||
# principal com al smoke test. Mantenir-los en variable separada evita
|
||||
# duplicació entre els dos targets.
|
||||
set(ENTITY_SOURCES
|
||||
# Entitats sense dependència directa amb Player.cpp: les enllacen tant
|
||||
# l'executable principal com el test_entities_smoke (que evita arrossegar
|
||||
# l'stack del jugador i les seues dependències pesades).
|
||||
set(ENTITY_SOURCES_BASE
|
||||
source/game/entities/entity.cpp
|
||||
source/game/entities/animation_component.cpp
|
||||
source/game/entities/event_bus.cpp
|
||||
@@ -47,6 +50,15 @@ set(ENTITY_SOURCES
|
||||
source/game/entities/concrete/rolling_rock.cpp
|
||||
source/game/entities/concrete/wall_trap.cpp
|
||||
source/game/entities/concrete/floor_trap.cpp
|
||||
source/game/entities/concrete/hole.cpp
|
||||
source/game/entities/concrete/dust.cpp
|
||||
)
|
||||
|
||||
# Entitats que criden directament símbols de Player (com WhipAttack via
|
||||
# `enterHurt`/`getBaseBox`): només van a l'executable principal.
|
||||
set(ENTITY_SOURCES
|
||||
${ENTITY_SOURCES_BASE}
|
||||
source/game/entities/concrete/whip_attack.cpp
|
||||
)
|
||||
|
||||
set(APP_SOURCES
|
||||
@@ -54,33 +66,63 @@ set(APP_SOURCES
|
||||
source/core/system/director.cpp
|
||||
source/core/audio/audio.cpp
|
||||
source/core/audio/audio_adapter.cpp
|
||||
source/core/audio/audio_effects.cpp
|
||||
source/core/audio/jail_audio.cpp
|
||||
source/core/audio/sound_effects_config.cpp
|
||||
source/core/locale/locale.cpp
|
||||
source/core/ui/ui_config.cpp
|
||||
source/core/input/key_config.cpp
|
||||
source/core/input/global_inputs.cpp
|
||||
source/core/input/input_state.cpp
|
||||
source/core/input/gamepad_manager.cpp
|
||||
source/core/input/gamepad_bindings.cpp
|
||||
source/core/input/define_buttons.cpp
|
||||
source/core/input/mouse.cpp
|
||||
source/core/rendering/screen.cpp
|
||||
source/core/rendering/canvas.cpp
|
||||
source/core/rendering/notifier.cpp
|
||||
source/core/rendering/render_info.cpp
|
||||
source/core/rendering/gif.cpp
|
||||
source/core/rendering/screenshot.cpp
|
||||
source/external/stb_image_write_impl.cpp
|
||||
source/external/stb_vorbis_impl.cpp
|
||||
source/core/rendering/surface.cpp
|
||||
source/core/rendering/text.cpp
|
||||
source/core/rendering/sprite/sprite.cpp
|
||||
source/core/rendering/sprite/moving_sprite.cpp
|
||||
source/core/rendering/sprite/animated_sprite.cpp
|
||||
source/core/rendering/sprite/animated_sprite_parser.cpp
|
||||
source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp
|
||||
source/core/rendering/sdl3gpu/viewport.cpp
|
||||
source/core/rendering/sdl3gpu/pipelines/crtpi_pipeline.cpp
|
||||
source/core/resources/resource_helper.cpp
|
||||
source/core/resources/resource_cache.cpp
|
||||
source/core/resources/resource_pack.cpp
|
||||
source/core/resources/palette_loader.cpp
|
||||
source/utils/easing.cpp
|
||||
source/utils/utils.cpp
|
||||
source/game/debug_config.cpp
|
||||
source/game/options.cpp
|
||||
source/game/options_apply.cpp
|
||||
source/game/ui/service_menu.cpp
|
||||
source/game/ui/hud.cpp
|
||||
source/game/scenes/placeholder_input.cpp
|
||||
source/game/scenes/logo_scene.cpp
|
||||
source/game/scenes/title_scene.cpp
|
||||
source/game/scenes/intro_scene.cpp
|
||||
source/game/scenes/game_scene.cpp
|
||||
source/game/scenes/room_fsm.cpp
|
||||
source/game/entities/player.cpp
|
||||
source/game/map/map.cpp
|
||||
source/game/map/tilemap.cpp
|
||||
source/game/map/tilemap_writer.cpp
|
||||
source/game/map/paint_layer.cpp
|
||||
source/game/levels/level_manager.cpp
|
||||
# Mòdul editor (només activa contingut en Debug via `#ifdef _DEBUG`).
|
||||
# En Release els fitxers compilen a object buit i l'enllaçador no
|
||||
# arrossega cap símbol del joc final.
|
||||
source/game/editor/editor.cpp
|
||||
source/game/editor/editor_config.cpp
|
||||
${ENTITY_SOURCES}
|
||||
# Fitxer només del binari principal: `loadFromLevel` depèn de
|
||||
# Resource::Cache (→ Surface → GIF → àudio). Separat per no arrossegar
|
||||
@@ -139,6 +181,15 @@ endif()
|
||||
|
||||
add_executable(${PROJECT_NAME} ${APP_SOURCES})
|
||||
|
||||
# Silencia warnings de codi de tercers (mateixa filosofia que el
|
||||
# .clang-tidy de source/external/). Només afecta aquests TUs concrets;
|
||||
# la resta del codi continua compilant amb -Wall -Wextra -Wpedantic.
|
||||
set_source_files_properties(
|
||||
source/external/stb_image_write_impl.cpp
|
||||
source/external/stb_vorbis_impl.cpp
|
||||
PROPERTIES COMPILE_OPTIONS "-Wno-missing-field-initializers;-Wno-deprecated-declarations"
|
||||
)
|
||||
|
||||
if(NOT APPLE AND GLSLC_EXE)
|
||||
add_dependencies(${PROJECT_NAME} shaders)
|
||||
endif()
|
||||
@@ -150,19 +201,63 @@ target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE SDL3::SDL3)
|
||||
|
||||
# Copia recursius de la carpeta data/ al costat del binari per que les rutes
|
||||
# relatives ("data/player/player.gif") funcionen sense desplegament.
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
"${CMAKE_SOURCE_DIR}/data"
|
||||
"$<TARGET_FILE_DIR:${PROJECT_NAME}>/data"
|
||||
COMMENT "Copying data/ next to the binary"
|
||||
# --- EINA STANDALONE: pack_resources ---
|
||||
# Executable auxiliar que empaqueta `data/` a `build/resources.pack`.
|
||||
# EXCLUDE_FROM_ALL: només es compila quan algun target en depèn (ho fa
|
||||
# `resource_pack`). Build manual: `cmake --build build --target pack_resources`.
|
||||
add_executable(pack_resources EXCLUDE_FROM_ALL
|
||||
tools/pack_resources/pack_resources.cpp
|
||||
source/core/resources/resource_pack.cpp
|
||||
)
|
||||
target_include_directories(pack_resources PRIVATE "${CMAKE_SOURCE_DIR}/source")
|
||||
target_compile_options(pack_resources PRIVATE -Wall -Wextra -Wpedantic)
|
||||
|
||||
# --- REGENERACIÓ AUTOMÀTICA DE build/resources.pack ---
|
||||
# A cada build re-empaquetem data/ si algun fitxer dins ha canviat. Evita
|
||||
# debugar amb un pack obsolet. CONFIGURE_DEPENDS força CMake a re-globbar
|
||||
# a la pròxima invocació (recull fitxers nous afegits a data/).
|
||||
file(GLOB_RECURSE DATA_FILES CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/data/*")
|
||||
set(RESOURCE_PACK "${CMAKE_BINARY_DIR}/resources.pack")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${RESOURCE_PACK}
|
||||
COMMAND $<TARGET_FILE:pack_resources>
|
||||
"${CMAKE_SOURCE_DIR}/data"
|
||||
"${RESOURCE_PACK}"
|
||||
DEPENDS pack_resources ${DATA_FILES}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMENT "Empaquetant data/ → build/resources.pack"
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(resource_pack ALL DEPENDS ${RESOURCE_PACK})
|
||||
add_dependencies(${PROJECT_NAME} resource_pack)
|
||||
|
||||
# --- CÒPIA DE gamecontrollerdb.txt AL COSTAT DEL BINARI ---
|
||||
# SDL_AddGamepadMappingsFromFile només llegeix del filesystem real (no del
|
||||
# pack), així que el fitxer ha de viure al directori del binari. Es copia
|
||||
# només si existeix per no fallar la build d'algú que encara no ha fet
|
||||
# `make controllerdb`.
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/gamecontrollerdb.txt")
|
||||
set(CONTROLLER_DB "${CMAKE_BINARY_DIR}/gamecontrollerdb.txt")
|
||||
add_custom_command(
|
||||
OUTPUT ${CONTROLLER_DB}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CMAKE_SOURCE_DIR}/gamecontrollerdb.txt"
|
||||
"${CONTROLLER_DB}"
|
||||
DEPENDS "${CMAKE_SOURCE_DIR}/gamecontrollerdb.txt"
|
||||
COMMENT "Copiant gamecontrollerdb.txt → build/"
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(controller_db ALL DEPENDS ${CONTROLLER_DB})
|
||||
add_dependencies(${PROJECT_NAME} controller_db)
|
||||
endif()
|
||||
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||
$<$<CONFIG:Debug>:_DEBUG>
|
||||
$<$<CONFIG:Release>:RELEASE_BUILD>
|
||||
# En Debug el fallback a disc apunta al source tree (sense re-packejar).
|
||||
AEE_SOURCE_DIR="${CMAKE_SOURCE_DIR}/"
|
||||
)
|
||||
|
||||
# --- STATIC ANALYSIS / FORMAT TARGETS ---
|
||||
@@ -255,8 +350,11 @@ option(BUILD_TESTS "Build smoke tests" ON)
|
||||
if(BUILD_TESTS)
|
||||
add_executable(test_entities_smoke
|
||||
tests/test_entities_smoke.cpp
|
||||
tests/hole_player_stubs.cpp
|
||||
source/core/resources/resource_helper.cpp
|
||||
source/core/resources/resource_pack.cpp
|
||||
source/game/map/map.cpp
|
||||
${ENTITY_SOURCES}
|
||||
${ENTITY_SOURCES_BASE}
|
||||
)
|
||||
target_include_directories(test_entities_smoke PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/source"
|
||||
@@ -268,4 +366,106 @@ if(BUILD_TESTS)
|
||||
add_test(NAME entities_smoke
|
||||
COMMAND test_entities_smoke
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
|
||||
# Smoke del WhipAttack. Inclou tot l'stack d'entitats + whip_attack.cpp.
|
||||
# `whip_attack_player_stubs.cpp` proveeix símbols mínims de Player per
|
||||
# satisfer el linker sense arrossegar audio/gamepad/screen — els tests
|
||||
# del whip s'enfoquen a hitbox + Mummy, no a friendly fire.
|
||||
add_executable(test_whip_attack
|
||||
tests/test_whip_attack.cpp
|
||||
tests/whip_attack_player_stubs.cpp
|
||||
tests/hole_player_stubs.cpp
|
||||
source/core/resources/resource_helper.cpp
|
||||
source/core/resources/resource_pack.cpp
|
||||
source/game/map/map.cpp
|
||||
${ENTITY_SOURCES_BASE}
|
||||
source/game/entities/concrete/whip_attack.cpp
|
||||
)
|
||||
target_include_directories(test_whip_attack PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/source"
|
||||
"${CMAKE_BINARY_DIR}"
|
||||
)
|
||||
target_link_libraries(test_whip_attack PRIVATE SDL3::SDL3)
|
||||
target_compile_options(test_whip_attack PRIVATE -Wall -Wextra -Wpedantic)
|
||||
add_test(NAME whip_attack
|
||||
COMMAND test_whip_attack
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
|
||||
# Smoke del flux d'escenes i del parser de debug.yaml. Stand-alone:
|
||||
# només enllaça els helpers purs (debug_config, placeholder_input,
|
||||
# key_config) perquè la integració completa dels placeholders arrossegaria
|
||||
# tot l'stack SDL (Screen/Notifier/RenderInfo), coberta pel smoke xvfb.
|
||||
add_executable(test_scene_flow
|
||||
tests/test_scene_flow.cpp
|
||||
source/game/debug_config.cpp
|
||||
source/game/scenes/placeholder_input.cpp
|
||||
source/core/input/key_config.cpp
|
||||
source/core/resources/resource_helper.cpp
|
||||
source/core/resources/resource_pack.cpp
|
||||
)
|
||||
target_include_directories(test_scene_flow PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/source"
|
||||
"${CMAKE_BINARY_DIR}"
|
||||
)
|
||||
target_link_libraries(test_scene_flow PRIVATE SDL3::SDL3)
|
||||
target_compile_options(test_scene_flow PRIVATE -Wall -Wextra -Wpedantic)
|
||||
add_test(NAME scene_flow
|
||||
COMMAND test_scene_flow
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
|
||||
# Smoke del writer surgical del tilemap (escriu el bloc `data:` sense
|
||||
# tocar la resta del fitxer). Pura manipulació de text, zero
|
||||
# dependències: no cal ni SDL ni el Resource::Cache.
|
||||
add_executable(test_tilemap_writer
|
||||
tests/test_tilemap_writer.cpp
|
||||
source/game/map/tilemap_writer.cpp
|
||||
)
|
||||
target_include_directories(test_tilemap_writer PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/source"
|
||||
)
|
||||
target_compile_options(test_tilemap_writer PRIVATE -Wall -Wextra -Wpedantic)
|
||||
add_test(NAME tilemap_writer
|
||||
COMMAND test_tilemap_writer
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
|
||||
# Smoke de la FSM de sala (RoomFsm). Cobreix loadConfig (timings i
|
||||
# corbes des de YAML), advance (transicions entre estats), i
|
||||
# formatRoomTitle (locale + substitució `{n}`). Stand-alone: no
|
||||
# arrossega Audio/Screen/Player/EntityManager — només els helpers
|
||||
# purs de la FSM, Locale i UiConfig.
|
||||
add_executable(test_game_fsm
|
||||
tests/test_game_fsm.cpp
|
||||
source/game/scenes/room_fsm.cpp
|
||||
source/core/locale/locale.cpp
|
||||
source/core/ui/ui_config.cpp
|
||||
source/core/resources/resource_helper.cpp
|
||||
source/core/resources/resource_pack.cpp
|
||||
source/utils/easing.cpp
|
||||
)
|
||||
target_include_directories(test_game_fsm PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/source"
|
||||
"${CMAKE_BINARY_DIR}"
|
||||
)
|
||||
target_link_libraries(test_game_fsm PRIVATE SDL3::SDL3)
|
||||
target_compile_options(test_game_fsm PRIVATE -Wall -Wextra -Wpedantic)
|
||||
add_test(NAME game_fsm
|
||||
COMMAND test_game_fsm
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
|
||||
# Smoke del parser d'animacions amb suport de flip H/V. Stand-alone:
|
||||
# només enllaça `animated_sprite_parser.cpp`, que és la part del motor
|
||||
# que NO depèn de Resource::Cache ni de Surface — el parser és pure
|
||||
# YAML-in / dades-out, així que no cal arrossegar tot l'stack.
|
||||
add_executable(test_animated_sprite_flip
|
||||
tests/test_animated_sprite_flip.cpp
|
||||
source/core/rendering/sprite/animated_sprite_parser.cpp
|
||||
)
|
||||
target_include_directories(test_animated_sprite_flip PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/source"
|
||||
)
|
||||
target_link_libraries(test_animated_sprite_flip PRIVATE SDL3::SDL3)
|
||||
target_compile_options(test_animated_sprite_flip PRIVATE -Wall -Wextra -Wpedantic)
|
||||
add_test(NAME animated_sprite_flip
|
||||
COMMAND test_animated_sprite_flip
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
endif()
|
||||
@@ -1,35 +1,79 @@
|
||||
PROJECT := aee_2026
|
||||
BUILDDIR := build
|
||||
JOBS ?= $(shell nproc 2>/dev/null || echo 4)
|
||||
|
||||
# VERSION (única font de veritat: source/utils/defines.hpp)
|
||||
VERSION := v$(shell grep 'constexpr const char\* VERSION' source/utils/defines.hpp | sed -E 's/.*VERSION = "([^"]+)".*/\1/')
|
||||
# Detecció de plataforma. En Windows CMake defaulteja a "NMake Makefiles"
|
||||
# (que requereix Visual Studio); forcem MinGW Makefiles per usar el g++ de
|
||||
# MinGW. nproc tampoc existeix en cmd.exe → fem servir NUMBER_OF_PROCESSORS.
|
||||
ifeq ($(OS),Windows_NT)
|
||||
# Si Make corre dins MSYS2/Git Bash/MinGW, $(shell ...) usa sh.exe i
|
||||
# "NUL" NO és dispositiu — un redirect "2>NUL" crearia un fitxer literal
|
||||
# anomenat NUL al cwd (i en Windows es complica esborrar-lo). Detectem
|
||||
# MSYSTEM per usar /dev/null en aquests entorns; només cmd.exe pur necessita NUL.
|
||||
ifneq ($(MSYSTEM),)
|
||||
NULDEV := /dev/null
|
||||
else
|
||||
NULDEV := NUL
|
||||
endif
|
||||
JOBS ?= $(NUMBER_OF_PROCESSORS)
|
||||
# Si Ninja està al PATH, l'usem (més ràpid i paral·lelitza millor).
|
||||
# Si no, caiem a MinGW Makefiles. Sense aquesta selecció CMake
|
||||
# defaultejaria a "NMake Makefiles", que requereix Visual Studio.
|
||||
HAS_NINJA := $(shell ninja --version 2>$(NULDEV))
|
||||
ifneq ($(HAS_NINJA),)
|
||||
CMAKE_GEN := -G "Ninja"
|
||||
else
|
||||
CMAKE_GEN := -G "MinGW Makefiles"
|
||||
endif
|
||||
else
|
||||
NULDEV := /dev/null
|
||||
JOBS ?= $(shell nproc 2>/dev/null || echo 4)
|
||||
CMAKE_GEN :=
|
||||
endif
|
||||
|
||||
# VERSION (única font de veritat: source/utils/defines.hpp).
|
||||
# Lazy (=): només es calcula quan s'invoca un target que la usa
|
||||
# (show-version/help). Evita executar grep/sed des de cmd.exe en `make debug`.
|
||||
VERSION = v$(shell grep 'constexpr const char\* VERSION' source/utils/defines.hpp | sed -E 's/.*VERSION = "([^"]+)".*/\1/')
|
||||
|
||||
# GIT_HASH calculat al host i passat a CMake (evita problemes si CMake corre en
|
||||
# entorns sense accés a git, p.ex. Docker/emscripten).
|
||||
GIT_HASH := $(shell git rev-parse --short=7 HEAD 2>/dev/null)
|
||||
GIT_HASH := $(shell git rev-parse --short=7 HEAD 2>$(NULDEV))
|
||||
ifeq ($(GIT_HASH),)
|
||||
GIT_HASH := unknown
|
||||
endif
|
||||
|
||||
CMAKE_DEFS := -DGIT_HASH=$(GIT_HASH)
|
||||
|
||||
.PHONY: all configure build run clean rebuild release show-version format format-check tidy tidy-fix cppcheck hooks-install
|
||||
.PHONY: all debug release run run-debug clean rebuild show-version pack controllerdb format format-check tidy tidy-fix cppcheck hooks-install help
|
||||
|
||||
all: build
|
||||
# ==============================================================================
|
||||
# COMPILACIÓ
|
||||
# ==============================================================================
|
||||
# Convenció alineada amb ../aee i ../projecte_2026:
|
||||
# make → Release (binari d'ús normal)
|
||||
# make debug → Debug (flux complet + `debug.yaml` per triar escena inicial)
|
||||
# make release → Release + empaquetat per a distribució (TODO, pendent)
|
||||
# ==============================================================================
|
||||
|
||||
configure:
|
||||
@cmake -S . -B $(BUILDDIR) -DCMAKE_BUILD_TYPE=Debug $(CMAKE_DEFS)
|
||||
|
||||
build: configure
|
||||
all:
|
||||
@cmake -S . -B $(BUILDDIR) $(CMAKE_GEN) -DCMAKE_BUILD_TYPE=Release $(CMAKE_DEFS)
|
||||
@cmake --build $(BUILDDIR) -j$(JOBS)
|
||||
|
||||
run: build
|
||||
debug:
|
||||
@cmake -S . -B $(BUILDDIR) $(CMAKE_GEN) -DCMAKE_BUILD_TYPE=Debug $(CMAKE_DEFS)
|
||||
@cmake --build $(BUILDDIR) -j$(JOBS)
|
||||
|
||||
run: all
|
||||
@./$(BUILDDIR)/$(PROJECT)
|
||||
|
||||
release:
|
||||
@cmake -S . -B $(BUILDDIR) -DCMAKE_BUILD_TYPE=Release $(CMAKE_DEFS)
|
||||
@cmake --build $(BUILDDIR) -j$(JOBS)
|
||||
run-debug: debug
|
||||
@./$(BUILDDIR)/$(PROJECT)
|
||||
|
||||
# Release + empaquetat per distribució (zip/dmg/tar.gz). De moment només
|
||||
# compila amb CMAKE_BUILD_TYPE=Release; l'empaquetat OS-specific està
|
||||
# pendent (veure Makefile de ../aee com a referència).
|
||||
release: all
|
||||
@echo "TODO: empaquetat per a distribució (zip/dmg/tar.gz) — pendent"
|
||||
|
||||
show-version:
|
||||
@echo "$(PROJECT) $(VERSION) ($(GIT_HASH))"
|
||||
@@ -37,24 +81,46 @@ show-version:
|
||||
clean:
|
||||
@rm -rf $(BUILDDIR)
|
||||
|
||||
rebuild: clean build
|
||||
rebuild: clean all
|
||||
|
||||
# Empaqueta data/ a $(BUILDDIR)/resources.pack (format AE26). Força un rebuild
|
||||
# del pack encara que res no hagi canviat dins data/. L'empaquetat també es fa
|
||||
# automàticament a cada `make`/`make debug` via el target CMake `resource_pack`.
|
||||
pack:
|
||||
@cmake -S . -B $(BUILDDIR) $(CMAKE_GEN) -DCMAKE_BUILD_TYPE=Release $(CMAKE_DEFS)
|
||||
@cmake --build $(BUILDDIR) --target pack_resources
|
||||
@./$(BUILDDIR)/pack_resources data $(BUILDDIR)/resources.pack
|
||||
|
||||
# Descàrrega de la base de dades de mappings de mandos. SDL només pot
|
||||
# obrir-la des del filesystem (no del pack), per això viu a l'arrel del
|
||||
# projecte i CMake la copia al costat del binari a cada build.
|
||||
controllerdb:
|
||||
@echo "Descarregant gamecontrollerdb.txt..."
|
||||
@curl -fsSL https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/master/gamecontrollerdb.txt \
|
||||
-o gamecontrollerdb.txt
|
||||
@echo "gamecontrollerdb.txt actualitzat"
|
||||
|
||||
# ==============================================================================
|
||||
# CODE QUALITY (delegados a cmake)
|
||||
# ==============================================================================
|
||||
format: configure
|
||||
format:
|
||||
@cmake -S . -B $(BUILDDIR) $(CMAKE_GEN) -DCMAKE_BUILD_TYPE=Release $(CMAKE_DEFS)
|
||||
@cmake --build $(BUILDDIR) --target format
|
||||
|
||||
format-check: configure
|
||||
format-check:
|
||||
@cmake -S . -B $(BUILDDIR) $(CMAKE_GEN) -DCMAKE_BUILD_TYPE=Release $(CMAKE_DEFS)
|
||||
@cmake --build $(BUILDDIR) --target format-check
|
||||
|
||||
tidy: configure
|
||||
tidy:
|
||||
@cmake -S . -B $(BUILDDIR) $(CMAKE_GEN) -DCMAKE_BUILD_TYPE=Release $(CMAKE_DEFS)
|
||||
@cmake --build $(BUILDDIR) --target tidy
|
||||
|
||||
tidy-fix: configure
|
||||
tidy-fix:
|
||||
@cmake -S . -B $(BUILDDIR) $(CMAKE_GEN) -DCMAKE_BUILD_TYPE=Release $(CMAKE_DEFS)
|
||||
@cmake --build $(BUILDDIR) --target tidy-fix
|
||||
|
||||
cppcheck: configure
|
||||
cppcheck:
|
||||
@cmake -S . -B $(BUILDDIR) $(CMAKE_GEN) -DCMAKE_BUILD_TYPE=Release $(CMAKE_DEFS)
|
||||
@cmake --build $(BUILDDIR) --target cppcheck
|
||||
|
||||
# ==============================================================================
|
||||
@@ -63,3 +129,39 @@ cppcheck: configure
|
||||
hooks-install:
|
||||
@git config core.hooksPath .githooks
|
||||
@echo "Git hooks activats: $(shell pwd)/.githooks"
|
||||
|
||||
# ==============================================================================
|
||||
# AJUDA
|
||||
# ==============================================================================
|
||||
help:
|
||||
@echo "Makefile per a $(PROJECT)"
|
||||
@echo ""
|
||||
@echo " Compilació:"
|
||||
@echo " make - Compilar amb cmake (Release)"
|
||||
@echo " make debug - Compilar amb cmake (Debug)"
|
||||
@echo ""
|
||||
@echo " Execució:"
|
||||
@echo " make run - Compilar (Release) i executar"
|
||||
@echo " make run-debug - Compilar (Debug) i executar"
|
||||
@echo ""
|
||||
@echo " Release:"
|
||||
@echo " make release - Release + empaquetat (TODO pendent)"
|
||||
@echo ""
|
||||
@echo " Recursos:"
|
||||
@echo " make pack - Empaquetar data/ a $(BUILDDIR)/resources.pack (format AE26)"
|
||||
@echo " make controllerdb - Descarregar gamecontrollerdb.txt actualitzat (arrel)"
|
||||
@echo ""
|
||||
@echo " Qualitat de codi:"
|
||||
@echo " make format - Formatar codi amb clang-format"
|
||||
@echo " make format-check - Verificar format sense modificar"
|
||||
@echo " make tidy - Anàlisi estàtic amb clang-tidy"
|
||||
@echo " make tidy-fix - Anàlisi estàtic amb auto-fix"
|
||||
@echo " make cppcheck - Anàlisi estàtic amb cppcheck"
|
||||
@echo ""
|
||||
@echo " Altres:"
|
||||
@echo " make clean - Esborrar carpeta $(BUILDDIR)/"
|
||||
@echo " make rebuild - clean + all"
|
||||
@echo " make show-version - Mostrar versió"
|
||||
@echo " make hooks-install - Activar git hooks del projecte"
|
||||
@echo ""
|
||||
@echo " Versió actual: $(VERSION) ($(GIT_HASH))"
|
||||
@@ -0,0 +1,13 @@
|
||||
# Tema visual de l'editor de tilemap (només Debug). Carregat per
|
||||
# EditorConfig a l'arrencada; les claus absents cauen als valors compilats
|
||||
# a Defaults::Editor (veure source/utils/defaults.hpp).
|
||||
|
||||
picker:
|
||||
margin: 8 # px entre canvas i caixa del picker
|
||||
spacing: 1 # px de separació entre tiles dins del picker
|
||||
|
||||
# Colors ARGB en hex. Accepta enter (0xFF2244FF) o cadena ("#FF2244FF").
|
||||
colors:
|
||||
border: "0xFF2244FF" # vora 1 px que envolta canvas i picker
|
||||
picker_bg: "0xFF101824" # fons del picker
|
||||
picker_hover: "0xFFFFFFFF" # vora 1 px del tile sota el cursor
|
||||
@@ -4,6 +4,18 @@ sprite:
|
||||
path: door.gif
|
||||
size: [48, 32]
|
||||
tile_size: [3, 2]
|
||||
collision:
|
||||
# Caixa de col·lisió: 16×24 centrada al sprite 48×32. Coincideix amb el
|
||||
# tile central transitable (els 16 px del passadís de sortida) i es
|
||||
# restringeix a la franja del mur trasser (24 px d'alçada). Així
|
||||
# `checkPlayerCollisions` només dispara `onPlayerContact` quan el
|
||||
# jugador entra al forat real, no als marcs flanquejants ni als 8 px
|
||||
# superiors que queden tapats pel HUD.
|
||||
size: [16, 24]
|
||||
offset: [16, 8]
|
||||
# Porta = mur infranquejable. Igual que el sarc, alçada efectiva
|
||||
# infinita per impedir que un jugador la salte per damunt.
|
||||
height: 999
|
||||
flags:
|
||||
collides: true
|
||||
interactable: true
|
||||
|
||||
|
After Width: | Height: | Size: 306 B |
@@ -0,0 +1,22 @@
|
||||
name: dust
|
||||
type: effect
|
||||
sprite:
|
||||
path: dust.gif
|
||||
size: [40, 16]
|
||||
|
||||
# El polvet és purament visual: no col·lisiona, no interactua, no fa
|
||||
# dany. Només viu mentre dura l'animació triada (`jump` o `land`) i
|
||||
# desapareix per `markDead` quan acaba.
|
||||
flags:
|
||||
collides: false
|
||||
interactable: false
|
||||
|
||||
# Dues animacions, no-loop: una per a cada moment del salt. El spawner
|
||||
# (GameScene, en resposta a events del bus) instancia la variant
|
||||
# apropiada via `setVariant` abans del primer tick.
|
||||
# jump: frames 0..6 (7 frames) → reproduït al despegue.
|
||||
# land: frames 7..12 (6 frames) → reproduït a l'aterratge.
|
||||
# fps: 10 = 100 ms per frame.
|
||||
animations:
|
||||
jump: { frames: [0, 1, 2, 3, 4, 5, 6], fps: 15, loop: false }
|
||||
land: { frames: [7, 8, 9, 10, 11, 12], fps: 15, loop: false }
|
||||
@@ -4,6 +4,11 @@ sprite:
|
||||
path: floor_trap.gif
|
||||
size: [16, 16]
|
||||
tile_size: [1, 1]
|
||||
collision:
|
||||
# Trampa al sòl: alçada 0 → qualsevol salt l'esquiva. (Avui amb
|
||||
# `collides: false` la trampa no entra al filtre de contacte, però
|
||||
# declarem el camp per quan es cablege la detecció via àrea.)
|
||||
height: 0
|
||||
flags:
|
||||
collides: false
|
||||
interactable: false
|
||||
|
||||
|
After Width: | Height: | Size: 182 B |
@@ -0,0 +1,26 @@
|
||||
name: hole
|
||||
type: static_hazard
|
||||
sprite:
|
||||
path: hole.gif
|
||||
size: [16, 16]
|
||||
tile_size: [1, 1]
|
||||
collision:
|
||||
# Forat: ocupa el tile sencer (16×16). `height: 0` deixa que un salt
|
||||
# AIRBORNE el creui sense disparar el contacte (el filtre d'altura de
|
||||
# `checkPlayerCollisions` el descarta quan `jump_z_ > height`). El
|
||||
# bucle de contacte admet l'entitat per `flags.triggers_on_overlap`,
|
||||
# encara que `flags.collides` sigui false.
|
||||
size: [16, 16]
|
||||
height: 0
|
||||
flags:
|
||||
collides: false
|
||||
interactable: false
|
||||
# Avui el loader no llig el bloc `flags:` (és declaratiu); el flag
|
||||
# real es fixa al constructor C++ de `Hole`. Mantenim el camp per
|
||||
# coherència amb la resta de plantilles i per quan es cablege la
|
||||
# lectura genèrica de flags.
|
||||
triggers_on_overlap: true
|
||||
animations:
|
||||
idle: { frames: [0], fps: 1, loop: true }
|
||||
stats:
|
||||
damage: 0
|
||||
|
Before Width: | Height: | Size: 833 B After Width: | Height: | Size: 1.2 KiB |
@@ -2,25 +2,58 @@ name: mummy
|
||||
type: dynamic_hazard
|
||||
sprite:
|
||||
path: mummy.gif
|
||||
size: [12, 24]
|
||||
size: [16, 32]
|
||||
tile_size: [1, 1]
|
||||
# La momia usa falsa perspectiva: el sprite fa 24 px d'alt però la caixa
|
||||
# AABB només ocupa els 12×12 de baix (els "peus"). L'spawn `position` del
|
||||
# map és el tile dels peus: el sprite es centra horitzontalment i la
|
||||
# seua vora inferior s'alinea amb la vora inferior del tile.
|
||||
# Variant 16×32 de la momia: el sprite fa 16×32 (dos 16×16: cap i peus)
|
||||
# i la caja de col·lisió amb el mapa és 16×16, coincident amb el
|
||||
# rectangle-base 16×16 dels peus. Com que `size` i `base_size` són
|
||||
# iguals, el padding és zero i la caja s'alinea exactament amb la
|
||||
# silueta inferior del sprite; amb `anchor: feet` ocupa la cel·la
|
||||
# 16×16 del corredor on naix.
|
||||
anchor: feet
|
||||
collision:
|
||||
size: [12, 12]
|
||||
size: [16, 16]
|
||||
base_size: [16, 16]
|
||||
# Alçada del cos en píxels per al filtre de col·lisió per salt: un
|
||||
# jugador amb jump_z > height no col·lisiona amb la momia (la salta
|
||||
# per damunt). Amb impulse=240 + gravity=600 el pic ronda els 48 px,
|
||||
# així que 24 deixa marge per esquivar amb un salt no perfecte.
|
||||
height: 24
|
||||
flags:
|
||||
collides: true
|
||||
interactable: false
|
||||
# Strip de 12 frames en graella 4×3 (mateix esquema que el jugador):
|
||||
# 0..3 → walk_south (cara avall)
|
||||
# 4..7 → walk_north (cara amunt)
|
||||
# 8..11 → walk_east (lateral cap a l'est)
|
||||
# La variant `walk_west` reaprofita els frames de l'est amb `flip_h: true`:
|
||||
# el motor d'animació aplica el flip al sprite, així el codi de la mòmia
|
||||
# no decideix flip per direcció (només selecciona quina animació mostrar).
|
||||
# La momia no té variants `stand_*` perquè mai no para.
|
||||
animations:
|
||||
idle: { frames: [0], fps: 1, loop: true }
|
||||
walk: { frames: [0], fps: 8, loop: true }
|
||||
walk_south: { frames: [0, 1, 2, 3], fps: 8, loop: true }
|
||||
walk_north: { frames: [4, 5, 6, 7], fps: 8, loop: true }
|
||||
walk_east: { frames: [8, 9, 10, 11], fps: 8, loop: true }
|
||||
walk_west: { frames: [8, 9, 10, 11], fps: 8, loop: true, flip_h: true }
|
||||
ai:
|
||||
type: corridor_wanderer
|
||||
direction_choice: random
|
||||
initial_direction: right
|
||||
initial_direction: east
|
||||
stats:
|
||||
speed: 40
|
||||
speed: 30
|
||||
damage: 1
|
||||
# Quan true, el contacte amb el jugador no aplica `hurt` ni knockback:
|
||||
# mata d'un sol toc i dispara el cicle DYING → respawn. El path `hurt`
|
||||
# del jugador segueix funcionant per a futurs hazards no-letals (avui
|
||||
# no n'hi ha cap que el dispari).
|
||||
kills_outright: true
|
||||
|
||||
# Estat HURT: post-fuetada, fugint en línia recta. Si la direcció
|
||||
# preferida (la del whip) està bloquejada, prova perpendiculars; si totes
|
||||
# ho estan, queda quieta tot el `duration_ms` i després torna a ROAMING.
|
||||
# `speed` opcional: si no apareix, s'usa `stats.speed`. El `whippable`
|
||||
# habilita la reacció al látigo (false ⇒ el whip la ignora).
|
||||
hurt:
|
||||
duration_ms: 600
|
||||
speed: 60
|
||||
whippable: true
|
||||
@@ -4,6 +4,11 @@ sprite:
|
||||
path: rolling_rock.gif
|
||||
size: [16, 16]
|
||||
tile_size: [1, 1]
|
||||
collision:
|
||||
# `size`/`base_size` no calen per a la roca (tot el sprite és cos),
|
||||
# però sí declarem `height` per al filtre de salt: 16 px → fàcil
|
||||
# d'esquivar amb un salt curt.
|
||||
height: 16
|
||||
flags:
|
||||
collides: true
|
||||
interactable: false
|
||||
|
||||
@@ -7,6 +7,13 @@ sprite:
|
||||
# són "alçada" en falsa perspectiva, sense col·lisió. Perímetre ortogonal
|
||||
# de 8 tiles (3 N + 3 S + 1 O + 1 E).
|
||||
tile_size: [3, 1]
|
||||
collision:
|
||||
# Sarcòfag = obstacle massís: alçada efectiva infinita (999) per al
|
||||
# filtre del salt. Un jugador no el pot saltar per damunt encara que
|
||||
# el salt arribe al punt àlgid. La caixa de col·lisió real (logical
|
||||
# 48×16) la calcula `placeSarcophagus` a partir de la geometria del
|
||||
# mapa, no des d'aquest YAML.
|
||||
height: 999
|
||||
flags:
|
||||
collides: true
|
||||
interactable: true
|
||||
|
||||
@@ -4,6 +4,10 @@ sprite:
|
||||
path: torch.gif
|
||||
size: [16, 16]
|
||||
tile_size: [1, 1]
|
||||
collision:
|
||||
# Decoratiu, no col·lisiona. Camp `height` declarat per coherència
|
||||
# amb la resta d'entitats; no té efecte mentre `collides: false`.
|
||||
height: 0
|
||||
flags:
|
||||
collides: false
|
||||
interactable: false
|
||||
|
||||
@@ -4,6 +4,11 @@ sprite:
|
||||
path: wall_trap.gif
|
||||
size: [16, 16]
|
||||
tile_size: [1, 1]
|
||||
collision:
|
||||
# Trampa de paret: ix d'una vora alta i no es pot saltar per damunt.
|
||||
# 999 = infranquejable. Igual que floor_trap, el filtre només té efecte
|
||||
# quan la detecció de contacte hi entre.
|
||||
height: 999
|
||||
flags:
|
||||
collides: false
|
||||
interactable: false
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
name: whip_attack
|
||||
|
||||
# Quatre sprites distints, un per cardinal. El sistema d'animació actual
|
||||
# només permet UNA mida de frame per surface, i els sprites del látigo
|
||||
# tenen aspect ratios distints segons direcció. Per això són 4 fitxers
|
||||
# en lloc d'una sola sheet compartida amb flips.
|
||||
#
|
||||
# Mides (= `draw_rect` del WhipAttack, que NO es retalla per
|
||||
# bloquejadors):
|
||||
# E/W → 80×32 px per frame: 16 px de wind-up darrere del jugador
|
||||
# + 16 px de solapament + 48 px d'atac
|
||||
# frontal. Cobreix tota l'altura del jugador.
|
||||
# N/S → 16×96 px per frame: 48 px d'atac + 32 px solapant amb el jugador
|
||||
# + 16 px de wind-up "darrere".
|
||||
#
|
||||
# La caja de DANY és independent i més petita (48×16 / 16×48), pegada
|
||||
# al borde del jugador i retallada per murs/sarcs. La calcula
|
||||
# `WhipAttack::configure` a partir del `owner_box` del payload.
|
||||
sprites:
|
||||
east: { path: whip_east.gif, size: [80, 32] }
|
||||
west: { path: whip_west.gif, size: [80, 32] }
|
||||
north: { path: whip_north.gif, size: [16, 96] }
|
||||
south: { path: whip_south.gif, size: [16, 96] }
|
||||
|
||||
# Animació compartida entre les 4 direccions: la seqüència de frames i les
|
||||
# durades són idèntiques; només canvia quina surface/mida es clipa.
|
||||
# - `frames` : índex de tile dins l'spritesheet (graella deduïda).
|
||||
# - `frame_ms` : durada uniforme per frame; 50 ms × 4 ≈ 200 ms swing.
|
||||
# - `loop: false` : el látigo viu una animació i mor (`markDead`).
|
||||
# - `active_frames` : valors dins de `frames` durant els quals la
|
||||
# hitbox aplica dany. Wind-up (0) i recovery (3) no
|
||||
# danyen — només els frames "actius" 1 i 2.
|
||||
animations:
|
||||
swing:
|
||||
frames: [0, 1, 2, 3]
|
||||
frame_ms: 50
|
||||
loop: false
|
||||
active_frames: [1, 2]
|
||||
|
After Width: | Height: | Size: 313 B |
|
After Width: | Height: | Size: 378 B |
|
After Width: | Height: | Size: 369 B |
|
After Width: | Height: | Size: 313 B |
@@ -0,0 +1,132 @@
|
||||
# Font: CaesarDressing-Regular — generado desde CaesarDressing-Regular.ttf size 24
|
||||
# Generado con font_gen.py
|
||||
|
||||
box_width 18
|
||||
box_height 28
|
||||
columns 15
|
||||
|
||||
# codepoint_decimal ancho_visual
|
||||
32 8 # U+0020
|
||||
33 5 # !
|
||||
34 6 # "
|
||||
35 14 # #
|
||||
36 7 # $
|
||||
37 17 # %
|
||||
38 9 # &
|
||||
39 3 # '
|
||||
40 10 # (
|
||||
41 8 # )
|
||||
42 10 # *
|
||||
43 11 # +
|
||||
44 5 # ,
|
||||
45 10 # -
|
||||
46 3 # .
|
||||
47 9 # /
|
||||
48 12 # 0
|
||||
49 6 # 1
|
||||
50 10 # 2
|
||||
51 9 # 3
|
||||
52 12 # 4
|
||||
53 11 # 5
|
||||
54 13 # 6
|
||||
55 11 # 7
|
||||
56 13 # 8
|
||||
57 13 # 9
|
||||
58 5 # :
|
||||
59 4 # ;
|
||||
60 11 # <
|
||||
61 11 # =
|
||||
62 11 # >
|
||||
63 11 # ?
|
||||
64 14 # @
|
||||
65 14 # A
|
||||
66 12 # B
|
||||
67 11 # C
|
||||
68 12 # D
|
||||
69 10 # E
|
||||
70 10 # F
|
||||
71 15 # G
|
||||
72 12 # H
|
||||
73 9 # I
|
||||
74 9 # J
|
||||
75 13 # K
|
||||
76 10 # L
|
||||
77 18 # M
|
||||
78 12 # N
|
||||
79 13 # O
|
||||
80 12 # P
|
||||
81 12 # Q
|
||||
82 13 # R
|
||||
83 9 # S
|
||||
84 12 # T
|
||||
85 11 # U
|
||||
86 12 # V
|
||||
87 17 # W
|
||||
88 11 # X
|
||||
89 12 # Y
|
||||
90 10 # Z
|
||||
91 8 # [
|
||||
92 8 # \
|
||||
93 7 # ]
|
||||
94 11 # ^
|
||||
95 13 # _
|
||||
96 7 # `
|
||||
97 13 # a
|
||||
98 12 # b
|
||||
99 11 # c
|
||||
100 12 # d
|
||||
101 10 # e
|
||||
102 10 # f
|
||||
103 15 # g
|
||||
104 12 # h
|
||||
105 5 # i
|
||||
106 9 # j
|
||||
107 13 # k
|
||||
108 10 # l
|
||||
109 18 # m
|
||||
110 12 # n
|
||||
111 13 # o
|
||||
112 12 # p
|
||||
113 12 # q
|
||||
114 12 # r
|
||||
115 10 # s
|
||||
116 12 # t
|
||||
117 11 # u
|
||||
118 12 # v
|
||||
119 17 # w
|
||||
120 11 # x
|
||||
121 12 # y
|
||||
122 10 # z
|
||||
123 9 # {
|
||||
124 5 # |
|
||||
125 9 # }
|
||||
126 14 # ~
|
||||
192 14 # À
|
||||
193 14 # Á
|
||||
200 10 # È
|
||||
201 10 # É
|
||||
204 9 # Ì
|
||||
205 9 # Í
|
||||
210 13 # Ò
|
||||
211 13 # Ó
|
||||
217 11 # Ù
|
||||
218 11 # Ú
|
||||
209 12 # Ñ
|
||||
199 11 # Ç
|
||||
224 13 # à
|
||||
225 13 # á
|
||||
232 10 # è
|
||||
233 11 # é
|
||||
236 7 # ì
|
||||
237 8 # í
|
||||
242 13 # ò
|
||||
243 13 # ó
|
||||
249 11 # ù
|
||||
250 11 # ú
|
||||
241 12 # ñ
|
||||
231 11 # ç
|
||||
161 5 # ¡
|
||||
191 10 # ¿
|
||||
171 11 # «
|
||||
187 11 # »
|
||||
183 3 # ·
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1,132 @@
|
||||
# Font: ari-w9500-condensed-display — generado desde ari-w9500-condensed-display.ttf size 11
|
||||
# Generado con tools/font_gen/font_gen.py
|
||||
|
||||
box_width 12
|
||||
box_height 11
|
||||
columns 15
|
||||
|
||||
# codepoint_decimal ancho_visual
|
||||
32 3 # U+0020
|
||||
33 3 # !
|
||||
34 6 # "
|
||||
35 9 # #
|
||||
36 7 # $
|
||||
37 11 # %
|
||||
38 8 # &
|
||||
39 3 # '
|
||||
40 5 # (
|
||||
41 5 # )
|
||||
42 5 # *
|
||||
43 7 # +
|
||||
44 4 # ,
|
||||
45 6 # -
|
||||
46 3 # .
|
||||
47 6 # /
|
||||
48 7 # 0
|
||||
49 6 # 1
|
||||
50 7 # 2
|
||||
51 7 # 3
|
||||
52 8 # 4
|
||||
53 7 # 5
|
||||
54 7 # 6
|
||||
55 7 # 7
|
||||
56 7 # 8
|
||||
57 7 # 9
|
||||
58 3 # :
|
||||
59 4 # ;
|
||||
60 7 # <
|
||||
61 7 # =
|
||||
62 7 # >
|
||||
63 7 # ?
|
||||
64 12 # @
|
||||
65 8 # A
|
||||
66 7 # B
|
||||
67 8 # C
|
||||
68 8 # D
|
||||
69 7 # E
|
||||
70 7 # F
|
||||
71 8 # G
|
||||
72 7 # H
|
||||
73 3 # I
|
||||
74 6 # J
|
||||
75 8 # K
|
||||
76 7 # L
|
||||
77 9 # M
|
||||
78 8 # N
|
||||
79 9 # O
|
||||
80 7 # P
|
||||
81 9 # Q
|
||||
82 7 # R
|
||||
83 7 # S
|
||||
84 7 # T
|
||||
85 7 # U
|
||||
86 7 # V
|
||||
87 10 # W
|
||||
88 8 # X
|
||||
89 7 # Y
|
||||
90 8 # Z
|
||||
91 5 # [
|
||||
92 6 # \
|
||||
93 5 # ]
|
||||
94 6 # ^
|
||||
95 7 # _
|
||||
96 5 # `
|
||||
97 7 # a
|
||||
98 7 # b
|
||||
99 7 # c
|
||||
100 7 # d
|
||||
101 7 # e
|
||||
102 6 # f
|
||||
103 7 # g
|
||||
104 7 # h
|
||||
105 3 # i
|
||||
106 3 # j
|
||||
107 7 # k
|
||||
108 3 # l
|
||||
109 9 # m
|
||||
110 7 # n
|
||||
111 7 # o
|
||||
112 7 # p
|
||||
113 7 # q
|
||||
114 6 # r
|
||||
115 6 # s
|
||||
116 6 # t
|
||||
117 7 # u
|
||||
118 7 # v
|
||||
119 8 # w
|
||||
120 7 # x
|
||||
121 7 # y
|
||||
122 7 # z
|
||||
123 6 # {
|
||||
124 3 # |
|
||||
125 6 # }
|
||||
126 8 # ~
|
||||
192 8 # À
|
||||
193 8 # Á
|
||||
200 7 # È
|
||||
201 7 # É
|
||||
204 3 # Ì
|
||||
205 3 # Í
|
||||
210 9 # Ò
|
||||
211 9 # Ó
|
||||
217 7 # Ù
|
||||
218 7 # Ú
|
||||
209 8 # Ñ
|
||||
199 8 # Ç
|
||||
224 7 # à
|
||||
225 7 # á
|
||||
232 7 # è
|
||||
233 7 # é
|
||||
236 4 # ì
|
||||
237 3 # í
|
||||
242 7 # ò
|
||||
243 7 # ó
|
||||
249 7 # ù
|
||||
250 7 # ú
|
||||
241 7 # ñ
|
||||
231 7 # ç
|
||||
161 3 # ¡
|
||||
191 7 # ¿
|
||||
171 10 # «
|
||||
187 10 # »
|
||||
183 3 # ·
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,132 @@
|
||||
# Font: ari-w9500-display — generado desde ari-w9500-display.ttf size 11
|
||||
# Generado con tools/font_gen/font_gen.py
|
||||
|
||||
box_width 12
|
||||
box_height 11
|
||||
columns 15
|
||||
|
||||
# codepoint_decimal ancho_visual
|
||||
32 3 # U+0020
|
||||
33 3 # !
|
||||
34 6 # "
|
||||
35 9 # #
|
||||
36 8 # $
|
||||
37 12 # %
|
||||
38 9 # &
|
||||
39 3 # '
|
||||
40 5 # (
|
||||
41 5 # )
|
||||
42 5 # *
|
||||
43 7 # +
|
||||
44 4 # ,
|
||||
45 6 # -
|
||||
46 3 # .
|
||||
47 6 # /
|
||||
48 8 # 0
|
||||
49 6 # 1
|
||||
50 8 # 2
|
||||
51 8 # 3
|
||||
52 8 # 4
|
||||
53 8 # 5
|
||||
54 8 # 6
|
||||
55 8 # 7
|
||||
56 8 # 8
|
||||
57 8 # 9
|
||||
58 3 # :
|
||||
59 4 # ;
|
||||
60 7 # <
|
||||
61 7 # =
|
||||
62 7 # >
|
||||
63 8 # ?
|
||||
64 12 # @
|
||||
65 9 # A
|
||||
66 8 # B
|
||||
67 9 # C
|
||||
68 9 # D
|
||||
69 8 # E
|
||||
70 8 # F
|
||||
71 9 # G
|
||||
72 8 # H
|
||||
73 3 # I
|
||||
74 7 # J
|
||||
75 9 # K
|
||||
76 8 # L
|
||||
77 10 # M
|
||||
78 9 # N
|
||||
79 10 # O
|
||||
80 8 # P
|
||||
81 10 # Q
|
||||
82 8 # R
|
||||
83 8 # S
|
||||
84 9 # T
|
||||
85 8 # U
|
||||
86 9 # V
|
||||
87 12 # W
|
||||
88 9 # X
|
||||
89 9 # Y
|
||||
90 9 # Z
|
||||
91 5 # [
|
||||
92 6 # \
|
||||
93 5 # ]
|
||||
94 7 # ^
|
||||
95 7 # _
|
||||
96 5 # `
|
||||
97 8 # a
|
||||
98 8 # b
|
||||
99 8 # c
|
||||
100 8 # d
|
||||
101 8 # e
|
||||
102 6 # f
|
||||
103 8 # g
|
||||
104 8 # h
|
||||
105 3 # i
|
||||
106 4 # j
|
||||
107 8 # k
|
||||
108 3 # l
|
||||
109 11 # m
|
||||
110 8 # n
|
||||
111 8 # o
|
||||
112 8 # p
|
||||
113 8 # q
|
||||
114 6 # r
|
||||
115 7 # s
|
||||
116 6 # t
|
||||
117 8 # u
|
||||
118 8 # v
|
||||
119 9 # w
|
||||
120 8 # x
|
||||
121 8 # y
|
||||
122 8 # z
|
||||
123 6 # {
|
||||
124 3 # |
|
||||
125 6 # }
|
||||
126 9 # ~
|
||||
192 9 # À
|
||||
193 9 # Á
|
||||
200 8 # È
|
||||
201 8 # É
|
||||
204 3 # Ì
|
||||
205 3 # Í
|
||||
210 10 # Ò
|
||||
211 10 # Ó
|
||||
217 8 # Ù
|
||||
218 8 # Ú
|
||||
209 9 # Ñ
|
||||
199 9 # Ç
|
||||
224 8 # à
|
||||
225 8 # á
|
||||
232 8 # è
|
||||
233 8 # é
|
||||
236 4 # ì
|
||||
237 3 # í
|
||||
242 8 # ò
|
||||
243 8 # ó
|
||||
249 8 # ù
|
||||
250 8 # ú
|
||||
241 8 # ñ
|
||||
231 8 # ç
|
||||
161 3 # ¡
|
||||
191 8 # ¿
|
||||
171 10 # «
|
||||
187 10 # »
|
||||
183 3 # ·
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -29,12 +29,33 @@ keys:
|
||||
- id: toggle_vsync
|
||||
code: "F8"
|
||||
desc: "Activa / desactiva VSync"
|
||||
- id: screenshot
|
||||
code: "F9"
|
||||
desc: "Captura de pantalla (PNG)"
|
||||
- id: toggle_render_info
|
||||
code: "F12"
|
||||
code: "F10"
|
||||
desc: "Cicle de l'overlay de debug: apagat / dalt / baix"
|
||||
- id: toggle_render_time
|
||||
- id: pause
|
||||
code: "F11"
|
||||
desc: "Mostra o amaga el temps de joc dins de l'overlay"
|
||||
desc: "Pausa el joc (o botó START del mando)"
|
||||
- id: menu_toggle
|
||||
code: "F12"
|
||||
desc: "Obri / tanca el menú de servei"
|
||||
- id: editor_toggle
|
||||
code: "9"
|
||||
desc: "Entra/surt del mode editor (només Debug)"
|
||||
- id: editor_picker
|
||||
code: "T"
|
||||
desc: "Obri/tanca el tilepicker dins de l'editor"
|
||||
- id: prev_room
|
||||
code: "["
|
||||
desc: "Salta a la sala anterior del levels.yaml (només Debug)"
|
||||
- id: next_room
|
||||
code: "]"
|
||||
desc: "Salta a la sala següent del levels.yaml (només Debug)"
|
||||
- id: open_door
|
||||
code: "Keypad 1"
|
||||
desc: "Força l'obertura de la porta de la sala (només Debug)"
|
||||
- id: exit
|
||||
code: "Escape"
|
||||
desc: "Eixir (confirmar amb segona pulsació)"
|
||||
@@ -3,20 +3,113 @@
|
||||
|
||||
notification:
|
||||
test: "notificacio de prova"
|
||||
gamepad_connected: "connectat"
|
||||
gamepad_disconnected: "desconnectat"
|
||||
|
||||
input:
|
||||
zoom: "zoom"
|
||||
fullscreen_on: "pantalla completa"
|
||||
fullscreen_off: "mode finestra"
|
||||
shader_on: "shaders activats"
|
||||
shader_off: "shaders desactivats"
|
||||
vsync_on: "vsync activat"
|
||||
vsync_off: "vsync desactivat"
|
||||
shader: "shader"
|
||||
preset: "preset"
|
||||
scaling_disabled: "escalat: sense"
|
||||
scaling_stretch: "escalat: stretch"
|
||||
scaling_letterbox: "escalat: letterbox"
|
||||
scaling_overscan: "escalat: overscan"
|
||||
scaling_integer: "escalat: enter"
|
||||
press_again_exit: "prem una altra volta per eixir"
|
||||
zoom: "Zoom"
|
||||
fullscreen_on: "Pantalla completa"
|
||||
fullscreen_off: "Mode finestra"
|
||||
shader_on: "Shaders activats"
|
||||
shader_off: "Shaders desactivats"
|
||||
vsync_on: "VSync activat"
|
||||
vsync_off: "VSync desactivat"
|
||||
screenshot: "Captura"
|
||||
shader: "Shader"
|
||||
preset: "Preset"
|
||||
scaling_disabled: "Escalat sense"
|
||||
scaling_stretch: "Escalat stretch"
|
||||
scaling_letterbox: "Escalat letterbox"
|
||||
scaling_overscan: "Escalat overscan"
|
||||
scaling_integer: "Escalat enter"
|
||||
press_again_exit: "Prem una altra volta per eixir"
|
||||
paused: "Pausa"
|
||||
editor_on: "Editor ON"
|
||||
editor_off: "Editor OFF"
|
||||
editor_save_error: "Editor: error guardant el mapa"
|
||||
|
||||
hud:
|
||||
p1: "P1"
|
||||
p2: "P2"
|
||||
lives: "Vides"
|
||||
treasures: "Tres"
|
||||
pyramid: "Piramide"
|
||||
room: "Sala"
|
||||
|
||||
# Textos de la transició d'entrada/sortida de sala (FSM de GameScene).
|
||||
# Tant l'INTRO com l'OUTRO es renderitzen com a dues línies separades
|
||||
# (cadascuna amb la seua animació). L'INTRO accepta els placeholders
|
||||
# `{p}` (piràmide) i `{r}` (sala); l'OUTRO no en té cap — són les
|
||||
# dues paraules del rètol "fase completada".
|
||||
game:
|
||||
room_intro_pyramid: "Piramide {p}"
|
||||
room_intro_room: "Sala {r}"
|
||||
room_complete_line1: "Fase"
|
||||
room_complete_line2: "Completada"
|
||||
|
||||
menu:
|
||||
titles:
|
||||
root: "Menú de servei"
|
||||
video: "Vídeo"
|
||||
audio: "Àudio"
|
||||
controls: "Controls"
|
||||
keyboard: "Teclat"
|
||||
gamepad: "Comandament"
|
||||
system: "Sistema"
|
||||
labels:
|
||||
player_1: "Jugador 1"
|
||||
player_2: "Jugador 2"
|
||||
gamepad_unavailable: "No disponible"
|
||||
items:
|
||||
video_submenu: "Vídeo"
|
||||
audio_submenu: "Àudio"
|
||||
controls_submenu: "Controls"
|
||||
keyboard_submenu: "Teclat"
|
||||
gamepad_submenu: "Comandament"
|
||||
system_submenu: "Sistema"
|
||||
move_up: "Amunt"
|
||||
move_down: "Avall"
|
||||
move_left: "Esquerra"
|
||||
move_right: "Dreta"
|
||||
jump: "Saltar"
|
||||
attack: "Atacar"
|
||||
service: "Opcions"
|
||||
gamepad_p1: "Comandament P1"
|
||||
gamepad_p2: "Comandament P2"
|
||||
define_buttons_p1: "Definir botons P1"
|
||||
define_buttons_p2: "Definir botons P2"
|
||||
zoom: "Zoom"
|
||||
screen: "Pantalla completa"
|
||||
vsync: "VSync"
|
||||
scaling_mode: "Escalat"
|
||||
shader: "Shader"
|
||||
shader_type: "Tipus de shader"
|
||||
preset: "Preset"
|
||||
render_info: "Info de render"
|
||||
uptime: "Mostra temps"
|
||||
master_enable: "Àudio"
|
||||
master_volume: "Volum general"
|
||||
music: "Música"
|
||||
music_volume: "Volum música"
|
||||
sounds: "Sons"
|
||||
sounds_volume: "Volum sons"
|
||||
restart: "Reiniciar"
|
||||
exit_game: "Eixir del joc"
|
||||
values:
|
||||
on: "Actiu"
|
||||
off: "Inactiu"
|
||||
submenu_arrow: ">"
|
||||
scaling_disabled: "Sense"
|
||||
scaling_stretch: "Stretch"
|
||||
scaling_letterbox: "Letterbox"
|
||||
scaling_overscan: "Overscan"
|
||||
scaling_integer: "Enter"
|
||||
render_info_off: "Apagat"
|
||||
render_info_top: "Dalt"
|
||||
render_info_bottom: "Baix"
|
||||
gamepad_none: "Cap"
|
||||
gamepad_disconnected: "Desconnectat"
|
||||
press_key: "Polsa una tecla"
|
||||
press_button: "Polsa un botó"
|
||||
define_buttons_complete: "Configuració completa"
|
||||
define_buttons_no_pad: "Cap mando assignat"
|
||||
@@ -3,20 +3,112 @@
|
||||
|
||||
notification:
|
||||
test: "test notification"
|
||||
gamepad_connected: "connected"
|
||||
gamepad_disconnected: "disconnected"
|
||||
|
||||
input:
|
||||
zoom: "zoom"
|
||||
fullscreen_on: "fullscreen"
|
||||
fullscreen_off: "windowed"
|
||||
shader_on: "shaders on"
|
||||
shader_off: "shaders off"
|
||||
vsync_on: "vsync on"
|
||||
vsync_off: "vsync off"
|
||||
shader: "shader"
|
||||
preset: "preset"
|
||||
scaling_disabled: "scaling: off"
|
||||
scaling_stretch: "scaling: stretch"
|
||||
scaling_letterbox: "scaling: letterbox"
|
||||
scaling_overscan: "scaling: overscan"
|
||||
scaling_integer: "scaling: integer"
|
||||
press_again_exit: "press again to exit"
|
||||
zoom: "Zoom"
|
||||
fullscreen_on: "Fullscreen"
|
||||
fullscreen_off: "Windowed"
|
||||
shader_on: "Shaders on"
|
||||
shader_off: "Shaders off"
|
||||
vsync_on: "VSync on"
|
||||
vsync_off: "VSync off"
|
||||
screenshot: "Screenshot"
|
||||
shader: "Shader"
|
||||
preset: "Preset"
|
||||
scaling_disabled: "Scaling off"
|
||||
scaling_stretch: "Scaling stretch"
|
||||
scaling_letterbox: "Scaling letterbox"
|
||||
scaling_overscan: "Scaling overscan"
|
||||
scaling_integer: "Scaling integer"
|
||||
press_again_exit: "Press again to exit"
|
||||
paused: "Paused"
|
||||
editor_on: "Editor ON"
|
||||
editor_off: "Editor OFF"
|
||||
editor_save_error: "Editor: map save failed"
|
||||
|
||||
hud:
|
||||
p1: "P1"
|
||||
p2: "P2"
|
||||
lives: "Lives"
|
||||
treasures: "Tre"
|
||||
pyramid: "Pyramid"
|
||||
room: "Room"
|
||||
|
||||
# Room transition texts (GameScene FSM intro/outro). Both INTRO and
|
||||
# OUTRO render as two independent lines with their own animation.
|
||||
# INTRO uses `{p}` (pyramid) and `{r}` (room) placeholders; OUTRO has
|
||||
# no placeholders — it's the two words of the "stage complete" label.
|
||||
game:
|
||||
room_intro_pyramid: "Pyramid {p}"
|
||||
room_intro_room: "Room {r}"
|
||||
room_complete_line1: "Stage"
|
||||
room_complete_line2: "Complete"
|
||||
|
||||
menu:
|
||||
titles:
|
||||
root: "Service menu"
|
||||
video: "Video"
|
||||
audio: "Audio"
|
||||
controls: "Controls"
|
||||
keyboard: "Keyboard"
|
||||
gamepad: "Gamepad"
|
||||
system: "System"
|
||||
labels:
|
||||
player_1: "Player 1"
|
||||
player_2: "Player 2"
|
||||
gamepad_unavailable: "Not available"
|
||||
items:
|
||||
video_submenu: "Video"
|
||||
audio_submenu: "Audio"
|
||||
controls_submenu: "Controls"
|
||||
keyboard_submenu: "Keyboard"
|
||||
gamepad_submenu: "Gamepad"
|
||||
system_submenu: "System"
|
||||
move_up: "Up"
|
||||
move_down: "Down"
|
||||
move_left: "Left"
|
||||
move_right: "Right"
|
||||
jump: "Jump"
|
||||
attack: "Attack"
|
||||
service: "Options"
|
||||
gamepad_p1: "Gamepad P1"
|
||||
gamepad_p2: "Gamepad P2"
|
||||
define_buttons_p1: "Define buttons P1"
|
||||
define_buttons_p2: "Define buttons P2"
|
||||
zoom: "Zoom"
|
||||
screen: "Fullscreen"
|
||||
vsync: "VSync"
|
||||
scaling_mode: "Scaling"
|
||||
shader: "Shader"
|
||||
shader_type: "Shader type"
|
||||
preset: "Preset"
|
||||
render_info: "Render info"
|
||||
uptime: "Show time"
|
||||
master_enable: "Audio"
|
||||
master_volume: "Master volume"
|
||||
music: "Music"
|
||||
music_volume: "Music volume"
|
||||
sounds: "Sounds"
|
||||
sounds_volume: "Sound volume"
|
||||
restart: "Restart"
|
||||
exit_game: "Exit game"
|
||||
values:
|
||||
on: "On"
|
||||
off: "Off"
|
||||
submenu_arrow: ">"
|
||||
scaling_disabled: "Off"
|
||||
scaling_stretch: "Stretch"
|
||||
scaling_letterbox: "Letterbox"
|
||||
scaling_overscan: "Overscan"
|
||||
scaling_integer: "Integer"
|
||||
render_info_off: "Off"
|
||||
render_info_top: "Top"
|
||||
render_info_bottom: "Bottom"
|
||||
gamepad_none: "None"
|
||||
gamepad_disconnected: "Disconnected"
|
||||
press_key: "Press a key"
|
||||
press_button: "Press a button"
|
||||
define_buttons_complete: "Configuration complete"
|
||||
define_buttons_no_pad: "No gamepad assigned"
|
||||
@@ -0,0 +1,45 @@
|
||||
# Índex ordenat de les 30 sales del joc (5 piràmides × 6 sales). El
|
||||
# LevelManager carrega aquesta llista a l'arrencada i la fa servir com a
|
||||
# font de veritat per a `currentPath()`, `advance()`, `setIndex()` i la
|
||||
# resta de consultes que li facin la GameScene, l'Editor i els salts de
|
||||
# debug. Per reordenar la progressió, canvia l'ordre d'aquesta seqüència
|
||||
# (no cal renombrar fitxers). Per afegir una sala, crea el YAML i posa
|
||||
# una entrada nova; els camps `pyramid` i `room` són purament informatius
|
||||
# (apareixen als logs i al text d'intro de sala) — l'únic camp que
|
||||
# realment carrega l'engine és `file`.
|
||||
#
|
||||
# Convenció de nomenclatura del fitxer: `data/maps/p<N>/r<M>.yaml` per
|
||||
# dins la piràmide N, sala M. La carpeta `p<N>/` agruparà més endavant
|
||||
# les metadades comunes de la piràmide (tileset, paleta, música).
|
||||
|
||||
sequence:
|
||||
- { pyramid: 1, room: 1, file: data/maps/p1/r1.yaml }
|
||||
- { pyramid: 1, room: 2, file: data/maps/p1/r2.yaml }
|
||||
- { pyramid: 1, room: 3, file: data/maps/p1/r3.yaml }
|
||||
- { pyramid: 1, room: 4, file: data/maps/p1/r4.yaml }
|
||||
- { pyramid: 1, room: 5, file: data/maps/p1/r5.yaml }
|
||||
- { pyramid: 1, room: 6, file: data/maps/p1/r6.yaml }
|
||||
- { pyramid: 2, room: 1, file: data/maps/p2/r1.yaml }
|
||||
- { pyramid: 2, room: 2, file: data/maps/p2/r2.yaml }
|
||||
- { pyramid: 2, room: 3, file: data/maps/p2/r3.yaml }
|
||||
- { pyramid: 2, room: 4, file: data/maps/p2/r4.yaml }
|
||||
- { pyramid: 2, room: 5, file: data/maps/p2/r5.yaml }
|
||||
- { pyramid: 2, room: 6, file: data/maps/p2/r6.yaml }
|
||||
- { pyramid: 3, room: 1, file: data/maps/p3/r1.yaml }
|
||||
- { pyramid: 3, room: 2, file: data/maps/p3/r2.yaml }
|
||||
- { pyramid: 3, room: 3, file: data/maps/p3/r3.yaml }
|
||||
- { pyramid: 3, room: 4, file: data/maps/p3/r4.yaml }
|
||||
- { pyramid: 3, room: 5, file: data/maps/p3/r5.yaml }
|
||||
- { pyramid: 3, room: 6, file: data/maps/p3/r6.yaml }
|
||||
- { pyramid: 4, room: 1, file: data/maps/p4/r1.yaml }
|
||||
- { pyramid: 4, room: 2, file: data/maps/p4/r2.yaml }
|
||||
- { pyramid: 4, room: 3, file: data/maps/p4/r3.yaml }
|
||||
- { pyramid: 4, room: 4, file: data/maps/p4/r4.yaml }
|
||||
- { pyramid: 4, room: 5, file: data/maps/p4/r5.yaml }
|
||||
- { pyramid: 4, room: 6, file: data/maps/p4/r6.yaml }
|
||||
- { pyramid: 5, room: 1, file: data/maps/p5/r1.yaml }
|
||||
- { pyramid: 5, room: 2, file: data/maps/p5/r2.yaml }
|
||||
- { pyramid: 5, room: 3, file: data/maps/p5/r3.yaml }
|
||||
- { pyramid: 5, room: 4, file: data/maps/p5/r4.yaml }
|
||||
- { pyramid: 5, room: 5, file: data/maps/p5/r5.yaml }
|
||||
- { pyramid: 5, room: 6, file: data/maps/p5/r6.yaml }
|
||||
@@ -0,0 +1,152 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_1.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 1
|
||||
room: 1
|
||||
tileset: data/tilesets/pyramid_1.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
|
||||
- type: door
|
||||
|
||||
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
|
||||
- type: hole
|
||||
position: [11, 5]
|
||||
- type: hole
|
||||
position: [4, 7]
|
||||
- type: hole
|
||||
position: [8, 11]
|
||||
- type: hole
|
||||
position: [15, 11]
|
||||
- type: hole
|
||||
position: [15, 5]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_1.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 1
|
||||
room: 2
|
||||
tileset: data/tilesets/pyramid_1.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_1.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 1
|
||||
room: 3
|
||||
tileset: data/tilesets/pyramid_1.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_1.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 1
|
||||
room: 4
|
||||
tileset: data/tilesets/pyramid_1.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_1.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 1
|
||||
room: 5
|
||||
tileset: data/tilesets/pyramid_1.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_1.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 1
|
||||
room: 6
|
||||
tileset: data/tilesets/pyramid_1.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_2.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 2
|
||||
room: 1
|
||||
tileset: data/tilesets/pyramid_2.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_2.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 2
|
||||
room: 2
|
||||
tileset: data/tilesets/pyramid_2.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_2.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 2
|
||||
room: 3
|
||||
tileset: data/tilesets/pyramid_2.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_2.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 2
|
||||
room: 4
|
||||
tileset: data/tilesets/pyramid_2.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_2.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 2
|
||||
room: 5
|
||||
tileset: data/tilesets/pyramid_2.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_2.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 2
|
||||
room: 6
|
||||
tileset: data/tilesets/pyramid_2.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_3.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 3
|
||||
room: 1
|
||||
tileset: data/tilesets/pyramid_3.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_3.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 3
|
||||
room: 2
|
||||
tileset: data/tilesets/pyramid_3.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_3.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 3
|
||||
room: 3
|
||||
tileset: data/tilesets/pyramid_3.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_3.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 3
|
||||
room: 4
|
||||
tileset: data/tilesets/pyramid_3.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_3.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 3
|
||||
room: 5
|
||||
tileset: data/tilesets/pyramid_3.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_3.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 3
|
||||
room: 6
|
||||
tileset: data/tilesets/pyramid_3.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_4.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 4
|
||||
room: 1
|
||||
tileset: data/tilesets/pyramid_4.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_4.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 4
|
||||
room: 2
|
||||
tileset: data/tilesets/pyramid_4.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_4.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 4
|
||||
room: 3
|
||||
tileset: data/tilesets/pyramid_4.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_4.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 4
|
||||
room: 4
|
||||
tileset: data/tilesets/pyramid_4.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_4.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 4
|
||||
room: 5
|
||||
tileset: data/tilesets/pyramid_4.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_4.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 4
|
||||
room: 6
|
||||
tileset: data/tilesets/pyramid_4.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_5.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 5
|
||||
room: 1
|
||||
tileset: data/tilesets/pyramid_5.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_5.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 5
|
||||
room: 2
|
||||
tileset: data/tilesets/pyramid_5.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_5.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 5
|
||||
room: 3
|
||||
tileset: data/tilesets/pyramid_5.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_5.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 5
|
||||
room: 4
|
||||
tileset: data/tilesets/pyramid_5.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_5.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 5
|
||||
room: 5
|
||||
tileset: data/tilesets/pyramid_5.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Mapa de la piràmide: graella 21×15 de tiles 16×16 sobre pyramid_5.gif.
|
||||
# Overscan horitzontal: 8 px per banda (les parets laterals ocupen 2 tiles
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
pyramid: 5
|
||||
room: 6
|
||||
tileset: data/tilesets/pyramid_5.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
# vivien com a `static constexpr` a map.hpp; ara cada piràmide pot definir
|
||||
# la seua pròpia forma sense recompilar.
|
||||
geometry:
|
||||
grid_cols: 4
|
||||
grid_rows: 5
|
||||
sarc_w: 48
|
||||
sarc_h: 16
|
||||
pitch_x: 64
|
||||
pitch_y: 32
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
data:
|
||||
- [11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13]
|
||||
- [11,30,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,31,13]
|
||||
- [11,13,27,28,15,22,28,28,28,28,28,28,28,28,28,28,15,28, 6,11,13]
|
||||
- [11,13, 8, 8,16, 8,17, 8,10, 8,17, 8, 8, 8,17, 8, 8, 8, 8,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 9, 2, 8,10, 8, 8, 8, 8,10, 8, 8,26, 8, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8, 8,26, 8, 8, 9, 8, 8, 8, 8, 8,10, 8,16, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2,16, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13,17, 8,10, 8, 8,10, 2, 8, 8,26, 8, 8, 8, 8,10, 8,17,11,13]
|
||||
- [11,13, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8,11,13]
|
||||
- [11,13, 8,10,16,16,17, 8, 8, 8,17, 8, 9, 8,17, 8, 2, 8, 8,11,13]
|
||||
- [11,38, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,39,13]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
# - sarcophagus → `sarc_position: [sarc_col, sarc_row]` en la graella de
|
||||
# sarcòfags (4×5). EntityManager calcula el rect píxel via
|
||||
# Map::getSarcRect i l'ancoratge de tile per al perímetre.
|
||||
# - la resta (torch, door, mummy, rolling_rock, wall_trap, floor_trap)
|
||||
# → `position: [tile_col, tile_row]` en coordenades del tilemap
|
||||
# (origen `[-8, 0]` per defecte). Pixel = tile*16 + tilemap_origin.
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
position: [4, 2]
|
||||
- type: torch
|
||||
position: [8, 2]
|
||||
- type: torch
|
||||
position: [12, 2]
|
||||
- type: torch
|
||||
position: [16, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 0]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 1]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 2]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 3]
|
||||
- type: sarcophagus
|
||||
sarc_position: [0, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [1, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [2, 4]
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -3,18 +3,30 @@
|
||||
# en el tilemap però només 1.5 visibles al canvas, així que la textura es
|
||||
# dibuixa amb un origen de x=-8). Vertical no cal overscan: 15·16 = 240.
|
||||
#
|
||||
# Símbols del grid:
|
||||
# W = paret (tile 99 del tileset, o (3,3) en 0-indexed)
|
||||
# F = sòl (llista = un tile aleatori entre aquests per cada casella)
|
||||
# . = cap tile (els sarcòfags seran actors, ací deixem transparent)
|
||||
#
|
||||
# Índexs lineals: els tiles van d'esquerra a dreta, de dalt a baix. Amb un
|
||||
# tileset de 512×256 i tiles de 16×16 hi ha 32 tiles per fila.
|
||||
# Format del tilemap:
|
||||
# `data` és una llista de files; cada fila és una llista d'índexs del
|
||||
# tileset (lineal esq→dre, dalt→baix). Amb un tileset de 512×256 i tiles
|
||||
# de 16×16 hi ha 32 tiles per fila. Un valor negatiu (p.ex. -1) deixa
|
||||
# la cel·la buida (transparent), útil per caselles que ocuparà un actor.
|
||||
|
||||
tileset: data/tilesets/tomb.gif
|
||||
tile_size: 16
|
||||
origin: [-8, 0]
|
||||
|
||||
# Índex de tile amb què cada jugador deixa rastre en trepitjar un sòl
|
||||
# pintable. Un índex per jugador perquè a multijugador local el recorregut
|
||||
# es distingeix a simple vista. Provisional fins que l'art del pintat
|
||||
# estigui definit; canviar aquí l'índex és tot el que cal. Omet el camp
|
||||
# per desactivar el pintat per a aquest jugador.
|
||||
player1_paint_tile: 0
|
||||
player2_paint_tile: 1
|
||||
|
||||
# Física del contacte entre jugadors. Quan un jugador està quiet i l'altre
|
||||
# el topa, el segon l'empenta a aquesta fracció de la velocitat normal (i
|
||||
# ell mateix es ralentitza per mantenir el grup). En head-on, ambdós
|
||||
# s'anul·len (sense push, sense moviment).
|
||||
push_factor: 0.30
|
||||
|
||||
# Geometria lògica de la piràmide (separada del render visual del tileset).
|
||||
# La graella de sarcòfags, el pitch entre ells i les amplades dels murs
|
||||
# decideixen els límits del jugador i les seues caixes de col·lisió. Abans
|
||||
@@ -30,39 +42,29 @@ geometry:
|
||||
hud_h: 24
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
# Columna central del tile transitable de la porta. La porta es renderitza
|
||||
# com a sprite 48×32 (3 tiles d'amplada) centrat sobre aquesta columna; els
|
||||
# 16 px centrals (just aquesta columna) són el "forat" del mur trasser per
|
||||
# on el jugador surt de la sala. Les 2 columnes flanquejants segueixen sent
|
||||
# mur visible. El spawn dels jugadors també s'alinea amb aquesta columna.
|
||||
door_col: 10
|
||||
|
||||
legend:
|
||||
W: 292
|
||||
X: 169
|
||||
Y: 1
|
||||
Z: 202
|
||||
T: 200
|
||||
R: 262
|
||||
G: 263
|
||||
H: 230
|
||||
I: 231
|
||||
J: 233
|
||||
A: 2
|
||||
B: 34
|
||||
C: 33
|
||||
D: 32
|
||||
|
||||
data: |
|
||||
YYYYYYYYYYYYYYYYYYYYY
|
||||
YHJJJJJJJJJJJJJJJJJIY
|
||||
YZWWWWWWWWWWWWWWWWWTY
|
||||
YZDDADDBBDDADDDDBDDTY
|
||||
YZDAAADAAADAAABAAADTY
|
||||
YZDDDCCDDDDDADDDBDDTY
|
||||
YZDAAADAAAAAAADAAADTY
|
||||
YZDDCCDDDDDADDBDDDDTY
|
||||
YZDAAADAAADAAADAAADTY
|
||||
YZDDDDCCDDDDADDDDDDTY
|
||||
YZDAAADAAADAAADAAADTY
|
||||
YZDDDABDDDDADCDDBDDTY
|
||||
YZDAAADAAADAAADAAADTY
|
||||
YZDDBBDDADDDBCDDDDDTY
|
||||
YRXXXXXXXXXXXXXXXXXGY
|
||||
data:
|
||||
- [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||
- [ 1, 91,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105, 92, 1]
|
||||
- [ 1, 98,113,113, 95,113, 94,113,113,113,113,113,113,113, 94,113,102,113,113, 96, 1]
|
||||
- [ 1, 98, 8, 8, 6, 8, 17, 8, 10, 8, 17, 8, 8, 8, 17, 8, 8, 8, 8, 96, 1]
|
||||
- [ 1, 98, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 96, 1]
|
||||
- [ 1, 98, 17, 8, 9, 2, 8, 10, 8, 8, 8, 8, 10, 8, 8, 26, 8, 8, 17, 96, 1]
|
||||
- [ 1, 98, 8, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 96, 1]
|
||||
- [ 1, 98, 17, 8, 8, 26, 8, 8, 9, 8, 8, 8, 8, 8, 10, 8, 6, 8, 17, 96, 1]
|
||||
- [ 1, 98, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 96, 1]
|
||||
- [ 1, 98, 17, 8, 10, 8, 8, 8, 2, 8, 8, 8, 9, 8, 8, 8, 10, 8, 17, 96, 1]
|
||||
- [ 1, 98, 8, 2, 2, 2, 8, 2, 2, 2, 6, 2, 2, 2, 8, 2, 2, 2, 8, 96, 1]
|
||||
- [ 1, 98, 17, 8, 10, 8, 8, 10, 2, 8, 8, 26, 8, 8, 8, 8, 10, 8, 17, 96, 1]
|
||||
- [ 1, 98, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 2, 2, 2, 8, 96, 1]
|
||||
- [ 1, 98, 8, 10, 6, 6, 17, 8, 8, 8, 17, 8, 9, 8, 17, 8, 2, 8, 8, 96, 1]
|
||||
- [ 1, 99, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,100, 1]
|
||||
|
||||
# Spawns d'entitats per a aquest nivell. Es llegeixen des de
|
||||
# EntityManager::loadFromLevel al GameScene. Convencions:
|
||||
@@ -75,7 +77,7 @@ data: |
|
||||
#
|
||||
# Dos sarcòfags en cantonades oposades → perímetres aïllats (cap tile en
|
||||
# comú). La porta va al centre inferior, on el tilemap té els tiles
|
||||
# d'obertura (R/G). La momia al corredor central. L'antorxa al costat
|
||||
# d'obertura (262/263). La momia al corredor central. L'antorxa al costat
|
||||
# superior esquerre per verificar render d'una entitat decorativa.
|
||||
entities:
|
||||
- type: torch
|
||||
@@ -127,6 +129,10 @@ entities:
|
||||
- type: sarcophagus
|
||||
sarc_position: [3, 4]
|
||||
- type: door
|
||||
position: [9, 1]
|
||||
# Sense `position`: la porta sempre viu al mur trasser. La columna
|
||||
# transitable la dicta `geometry.door_col`; el render i la caixa
|
||||
# de col·lisió es deriven d'aquesta dada al loadFromLevel.
|
||||
- type: mummy
|
||||
position: [10, 7]
|
||||
- type: mummy
|
||||
position: [7, 9]
|
||||
@@ -2,6 +2,8 @@
|
||||
# per a poder carregar Map sense errors, però el seu propòsit real són
|
||||
# els spawns de sota: l'smoke verifica que EntityLoader::readSpawns els
|
||||
# retorna correctament i que EntityManager pot instanciar-los i wire-ar-los.
|
||||
#
|
||||
# Format del tilemap: vegeu pyramid.yaml.
|
||||
|
||||
tileset: data/tilesets/tomb.gif
|
||||
tile_size: 16
|
||||
@@ -18,18 +20,14 @@ geometry:
|
||||
back_wall_h: 24
|
||||
side_wall_w: 24
|
||||
|
||||
legend:
|
||||
Y: 1
|
||||
X: 169
|
||||
|
||||
data: |
|
||||
YYYYYYYYYYYYYY
|
||||
YXXXXXXXXXXXXY
|
||||
YXXXXXXXXXXXXY
|
||||
YXXXXXXXXXXXXY
|
||||
YXXXXXXXXXXXXY
|
||||
YXXXXXXXXXXXXY
|
||||
YYYYYYYYYYYYYY
|
||||
data:
|
||||
- [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||
- [ 1,169,169,169,169,169,169,169,169,169,169,169,169, 1]
|
||||
- [ 1,169,169,169,169,169,169,169,169,169,169,169,169, 1]
|
||||
- [ 1,169,169,169,169,169,169,169,169,169,169,169,169, 1]
|
||||
- [ 1,169,169,169,169,169,169,169,169,169,169,169,169, 1]
|
||||
- [ 1,169,169,169,169,169,169,169,169,169,169,169,169, 1]
|
||||
- [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||
|
||||
entities:
|
||||
- type: torch
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
JASC-PAL
|
||||
0100
|
||||
256
|
||||
0 0 0
|
||||
36 0 0
|
||||
72 0 0
|
||||
109 0 0
|
||||
145 0 0
|
||||
182 0 0
|
||||
218 0 0
|
||||
255 0 0
|
||||
0 36 0
|
||||
36 36 0
|
||||
72 36 0
|
||||
109 36 0
|
||||
145 36 0
|
||||
182 36 0
|
||||
218 36 0
|
||||
255 36 0
|
||||
0 72 0
|
||||
36 72 0
|
||||
72 72 0
|
||||
109 72 0
|
||||
145 72 0
|
||||
182 72 0
|
||||
218 72 0
|
||||
255 72 0
|
||||
0 109 0
|
||||
36 109 0
|
||||
72 109 0
|
||||
109 109 0
|
||||
145 109 0
|
||||
182 109 0
|
||||
218 109 0
|
||||
255 109 0
|
||||
0 145 0
|
||||
36 145 0
|
||||
72 145 0
|
||||
109 145 0
|
||||
145 145 0
|
||||
182 145 0
|
||||
218 145 0
|
||||
255 145 0
|
||||
0 182 0
|
||||
36 182 0
|
||||
72 182 0
|
||||
109 182 0
|
||||
145 182 0
|
||||
182 182 0
|
||||
218 182 0
|
||||
255 182 0
|
||||
0 218 0
|
||||
36 218 0
|
||||
72 218 0
|
||||
109 218 0
|
||||
145 218 0
|
||||
182 218 0
|
||||
218 218 0
|
||||
255 218 0
|
||||
0 255 0
|
||||
36 255 0
|
||||
72 255 0
|
||||
109 255 0
|
||||
145 255 0
|
||||
182 255 0
|
||||
218 255 0
|
||||
255 255 0
|
||||
0 0 85
|
||||
36 0 85
|
||||
72 0 85
|
||||
109 0 85
|
||||
145 0 85
|
||||
182 0 85
|
||||
218 0 85
|
||||
255 0 85
|
||||
0 36 85
|
||||
36 36 85
|
||||
72 36 85
|
||||
109 36 85
|
||||
145 36 85
|
||||
182 36 85
|
||||
218 36 85
|
||||
255 36 85
|
||||
0 72 85
|
||||
36 72 85
|
||||
72 72 85
|
||||
109 72 85
|
||||
145 72 85
|
||||
182 72 85
|
||||
218 72 85
|
||||
255 72 85
|
||||
0 109 85
|
||||
36 109 85
|
||||
72 109 85
|
||||
109 109 85
|
||||
145 109 85
|
||||
182 109 85
|
||||
218 109 85
|
||||
255 109 85
|
||||
0 145 85
|
||||
36 145 85
|
||||
72 145 85
|
||||
109 145 85
|
||||
145 145 85
|
||||
182 145 85
|
||||
218 145 85
|
||||
255 145 85
|
||||
0 182 85
|
||||
36 182 85
|
||||
72 182 85
|
||||
109 182 85
|
||||
145 182 85
|
||||
182 182 85
|
||||
218 182 85
|
||||
255 182 85
|
||||
0 218 85
|
||||
36 218 85
|
||||
72 218 85
|
||||
109 218 85
|
||||
145 218 85
|
||||
182 218 85
|
||||
218 218 85
|
||||
255 218 85
|
||||
0 255 85
|
||||
36 255 85
|
||||
72 255 85
|
||||
109 255 85
|
||||
145 255 85
|
||||
182 255 85
|
||||
218 255 85
|
||||
255 255 85
|
||||
0 0 170
|
||||
36 0 170
|
||||
72 0 170
|
||||
109 0 170
|
||||
145 0 170
|
||||
182 0 170
|
||||
218 0 170
|
||||
255 0 170
|
||||
0 36 170
|
||||
36 36 170
|
||||
72 36 170
|
||||
109 36 170
|
||||
145 36 170
|
||||
182 36 170
|
||||
218 36 170
|
||||
255 36 170
|
||||
0 72 170
|
||||
36 72 170
|
||||
72 72 170
|
||||
109 72 170
|
||||
145 72 170
|
||||
182 72 170
|
||||
218 72 170
|
||||
255 72 170
|
||||
0 109 170
|
||||
36 109 170
|
||||
72 109 170
|
||||
109 109 170
|
||||
145 109 170
|
||||
182 109 170
|
||||
218 109 170
|
||||
255 109 170
|
||||
0 145 170
|
||||
36 145 170
|
||||
72 145 170
|
||||
109 145 170
|
||||
145 145 170
|
||||
182 145 170
|
||||
218 145 170
|
||||
255 145 170
|
||||
0 182 170
|
||||
36 182 170
|
||||
72 182 170
|
||||
109 182 170
|
||||
145 182 170
|
||||
182 182 170
|
||||
218 182 170
|
||||
255 182 170
|
||||
0 218 170
|
||||
36 218 170
|
||||
72 218 170
|
||||
109 218 170
|
||||
145 218 170
|
||||
182 218 170
|
||||
218 218 170
|
||||
255 218 170
|
||||
0 255 170
|
||||
36 255 170
|
||||
72 255 170
|
||||
109 255 170
|
||||
145 255 170
|
||||
182 255 170
|
||||
218 255 170
|
||||
255 255 170
|
||||
0 0 255
|
||||
36 0 255
|
||||
72 0 255
|
||||
109 0 255
|
||||
145 0 255
|
||||
182 0 255
|
||||
218 0 255
|
||||
255 0 255
|
||||
0 36 255
|
||||
36 36 255
|
||||
72 36 255
|
||||
109 36 255
|
||||
145 36 255
|
||||
182 36 255
|
||||
218 36 255
|
||||
255 36 255
|
||||
0 72 255
|
||||
36 72 255
|
||||
72 72 255
|
||||
109 72 255
|
||||
145 72 255
|
||||
182 72 255
|
||||
218 72 255
|
||||
255 72 255
|
||||
0 109 255
|
||||
36 109 255
|
||||
72 109 255
|
||||
109 109 255
|
||||
145 109 255
|
||||
182 109 255
|
||||
218 109 255
|
||||
255 109 255
|
||||
0 145 255
|
||||
36 145 255
|
||||
72 145 255
|
||||
109 145 255
|
||||
145 145 255
|
||||
182 145 255
|
||||
218 145 255
|
||||
255 145 255
|
||||
0 182 255
|
||||
36 182 255
|
||||
72 182 255
|
||||
109 182 255
|
||||
145 182 255
|
||||
182 182 255
|
||||
218 182 255
|
||||
255 182 255
|
||||
0 218 255
|
||||
36 218 255
|
||||
72 218 255
|
||||
109 218 255
|
||||
145 218 255
|
||||
182 218 255
|
||||
218 218 255
|
||||
255 218 255
|
||||
0 255 255
|
||||
36 255 255
|
||||
72 255 255
|
||||
109 255 255
|
||||
145 255 255
|
||||
182 255 255
|
||||
218 255 255
|
||||
255 255 255
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,133 @@
|
||||
sprite:
|
||||
path: morcus.gif
|
||||
size: [16, 32]
|
||||
|
||||
# Geometria de col·lisió. `size` és la caixa contra murs/sarcs;
|
||||
# `hit_size`, la caixa contra altres actors (més petita); `height`,
|
||||
# l'alçada que un salt ha de superar per passar per damunt.
|
||||
collision:
|
||||
size: 16
|
||||
base_size: 16
|
||||
hit_size: 8
|
||||
height: 12
|
||||
|
||||
animations:
|
||||
# Les variants `*_west` reaprofiten els frames de l'est i declaren
|
||||
# `flip_h: true` perquè el motor d'animació apliqui el flip al sprite —
|
||||
# el codi del joc no toca mai `flip_` directament.
|
||||
stand_south: { frames: [0] }
|
||||
stand_north: { frames: [4] }
|
||||
stand_east: { frames: [8] }
|
||||
stand_west: { frames: [8], flip_h: true }
|
||||
|
||||
jump_south: { frames: [12] }
|
||||
jump_north: { frames: [13] }
|
||||
jump_east: { frames: [14] }
|
||||
jump_west: { frames: [14], flip_h: true }
|
||||
|
||||
stun_south: { frames: [16] }
|
||||
stun_north: { frames: [17] }
|
||||
stun_east: { frames: [18] }
|
||||
stun_west: { frames: [18], flip_h: true }
|
||||
|
||||
walk_south: { frames: [0, 1, 2, 3], frame_ms: 100 }
|
||||
walk_north: { frames: [4, 5, 6, 7], frame_ms: 100 }
|
||||
walk_east: { frames: [8, 9, 10, 11], frame_ms: 100 }
|
||||
walk_west: { frames: [8, 9, 10, 11], frame_ms: 100, flip_h: true }
|
||||
|
||||
hurt_south: { frames: [0, 1, 2, 3], frame_ms: 100 }
|
||||
hurt_north: { frames: [4, 5, 6, 7], frame_ms: 100 }
|
||||
hurt_east: { frames: [8, 9, 10, 11], frame_ms: 100 }
|
||||
hurt_west: { frames: [8, 9, 10, 11], frame_ms: 100, flip_h: true }
|
||||
|
||||
attack_south: { frames: [20, 21, 22, 23], frame_ms: 50 }
|
||||
attack_north: { frames: [24, 25, 26, 27], frame_ms: 50 }
|
||||
attack_east: { frames: [28, 29, 30, 31], frame_ms: 50 }
|
||||
attack_west: { frames: [28, 29, 30, 31], frame_ms: 50, flip_h: true }
|
||||
|
||||
recover_south: { frames: [23] }
|
||||
recover_north: { frames: [27] }
|
||||
recover_east: { frames: [31] }
|
||||
recover_west: { frames: [31], flip_h: true }
|
||||
|
||||
# Animació de mort: 3 voltes horàries començant pel sud (south →
|
||||
# west → north → east, repetit 3×). Reaprofita els frames stand_*; el
|
||||
# west és l'east amb `flip_h: true` declarat per-frame (mapping form,
|
||||
# no hereta del nivell d'anim). `frame_ms` per-frame creix linealment
|
||||
# per donar la sensació de desaccelerar fins a aturar-se. `loop: false`
|
||||
# → es queda al darrer frame i marca completed.
|
||||
death_spinning:
|
||||
frames:
|
||||
- 0
|
||||
- { index: 8, flip_h: true }
|
||||
- 4
|
||||
- 8
|
||||
- 0
|
||||
- { index: 8, flip_h: true }
|
||||
- 4
|
||||
- 8
|
||||
- 0
|
||||
- { index: 8, flip_h: true }
|
||||
- 4
|
||||
- 8
|
||||
frame_ms: [60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280]
|
||||
loop: false
|
||||
|
||||
# Mort per caiguda a un agujero (DeathCause::HOLE). `loop: false` perquè
|
||||
# `animationIsCompleted()` dispari el respawn timer determinísticament.
|
||||
death_hole:
|
||||
frames: [15]
|
||||
frame_ms: 60
|
||||
loop: false
|
||||
|
||||
# Inèrcia de frenada en soltar la direcció. `threshold_ms` filtra
|
||||
# tocs curts (passets d'un tile sense rellisquera).
|
||||
inertia:
|
||||
brake_time_ms: 150
|
||||
threshold_ms: 300
|
||||
|
||||
# Gir de 180°: durant `duration_ms` el facing es fixa a la direcció
|
||||
# intermèdia indicada (purament visual).
|
||||
turn:
|
||||
duration_ms: 80
|
||||
transitions:
|
||||
west_to_east: south
|
||||
east_to_west: north
|
||||
north_to_south: east
|
||||
south_to_north: west
|
||||
|
||||
# Salt vertical (px de z, no toca la posició al món).
|
||||
# `shadow_y_offset`: puja la sombra per compensar la franja
|
||||
# transparent sota els peus del sprite.
|
||||
jump:
|
||||
impulse: 200
|
||||
gravity: 600
|
||||
landing_stun_ms: 400
|
||||
shadow_y_offset: 3
|
||||
sound: jump.wav
|
||||
land_sound: land.wav
|
||||
|
||||
# Projecció post-impacte: surt cap a la direcció amb més recorregut
|
||||
# lliure i frena fins a 0 en `brake_time_ms`.
|
||||
hurt:
|
||||
speed: 120
|
||||
brake_time_ms: 500
|
||||
sound: hurt.wav
|
||||
|
||||
# Atac amb làtigo. `range_tiles` és la longitud abans de retallar
|
||||
# contra murs/sarcs; `cooldown_ms` allarga ATTACKING amb una
|
||||
# recuperació en què la hitbox ja no fa dany.
|
||||
whip:
|
||||
range_tiles: 3
|
||||
swing_ms: 200
|
||||
cooldown_ms: 200
|
||||
damage: 1
|
||||
sound: whip.wav
|
||||
|
||||
# Mort + respawn. `respawn_delay_ms` és la pausa entre la fi de
|
||||
# `death_spinning` i la reaparició al spawn. `lives` és el comptador
|
||||
# inicial d'intents (estil Mario): cada respawn el decrementa en 1, pot
|
||||
# anar a negatius (no hi ha game over).
|
||||
death:
|
||||
respawn_delay_ms: 500
|
||||
lives: 3
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1,37 +0,0 @@
|
||||
# player animation
|
||||
tileSetFile: player.gif
|
||||
frameWidth: 12
|
||||
frameHeight: 24
|
||||
|
||||
# Geometria de col·lisió del jugador (independent de l'animació):
|
||||
# size → costat del quadrat de col·lisió (16×16)
|
||||
# base_size → costat del quadrat visual inferior del sprite (els "peus");
|
||||
# s'usa per centrar la caixa de col·lisió sobre ell.
|
||||
collision:
|
||||
size: 16
|
||||
base_size: 12
|
||||
|
||||
animations:
|
||||
- name: stand_down
|
||||
frames: [0]
|
||||
|
||||
- name: stand_up
|
||||
frames: [4]
|
||||
|
||||
- name: stand
|
||||
frames: [8]
|
||||
|
||||
- name: walk_down
|
||||
speed: 0.07
|
||||
loopFrom: 0
|
||||
frames: [0, 1, 2, 3]
|
||||
|
||||
- name: walk_up
|
||||
speed: 0.07
|
||||
loopFrom: 0
|
||||
frames: [4, 5, 6, 7]
|
||||
|
||||
- name: walk
|
||||
speed: 0.07
|
||||
loopFrom: 0
|
||||
frames: [8, 9, 10, 11]
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,133 @@
|
||||
sprite:
|
||||
path: sam.gif
|
||||
size: [16, 32]
|
||||
|
||||
# Geometria de col·lisió. `size` és la caixa contra murs/sarcs;
|
||||
# `hit_size`, la caixa contra altres actors (més petita); `height`,
|
||||
# l'alçada que un salt ha de superar per passar per damunt.
|
||||
collision:
|
||||
size: 16
|
||||
base_size: 16
|
||||
hit_size: 8
|
||||
height: 12
|
||||
|
||||
animations:
|
||||
# Les variants `*_west` reaprofiten els frames de l'est i declaren
|
||||
# `flip_h: true` perquè el motor d'animació apliqui el flip al sprite —
|
||||
# el codi del joc no toca mai `flip_` directament.
|
||||
stand_south: { frames: [0] }
|
||||
stand_north: { frames: [4] }
|
||||
stand_east: { frames: [8] }
|
||||
stand_west: { frames: [8], flip_h: true }
|
||||
|
||||
jump_south: { frames: [12] }
|
||||
jump_north: { frames: [13] }
|
||||
jump_east: { frames: [14] }
|
||||
jump_west: { frames: [14], flip_h: true }
|
||||
|
||||
stun_south: { frames: [16] }
|
||||
stun_north: { frames: [17] }
|
||||
stun_east: { frames: [18] }
|
||||
stun_west: { frames: [18], flip_h: true }
|
||||
|
||||
walk_south: { frames: [0, 1, 2, 3], frame_ms: 100 }
|
||||
walk_north: { frames: [4, 5, 6, 7], frame_ms: 100 }
|
||||
walk_east: { frames: [8, 9, 10, 11], frame_ms: 100 }
|
||||
walk_west: { frames: [8, 9, 10, 11], frame_ms: 100, flip_h: true }
|
||||
|
||||
hurt_south: { frames: [0, 1, 2, 3], frame_ms: 100 }
|
||||
hurt_north: { frames: [4, 5, 6, 7], frame_ms: 100 }
|
||||
hurt_east: { frames: [8, 9, 10, 11], frame_ms: 100 }
|
||||
hurt_west: { frames: [8, 9, 10, 11], frame_ms: 100, flip_h: true }
|
||||
|
||||
attack_south: { frames: [20, 21, 22, 23], frame_ms: 50 }
|
||||
attack_north: { frames: [24, 25, 26, 27], frame_ms: 50 }
|
||||
attack_east: { frames: [28, 29, 30, 31], frame_ms: 50 }
|
||||
attack_west: { frames: [28, 29, 30, 31], frame_ms: 50, flip_h: true }
|
||||
|
||||
recover_south: { frames: [23] }
|
||||
recover_north: { frames: [27] }
|
||||
recover_east: { frames: [31] }
|
||||
recover_west: { frames: [31], flip_h: true }
|
||||
|
||||
# Animació de mort: 3 voltes horàries començant pel sud (south →
|
||||
# west → north → east, repetit 3×). Reaprofita els frames stand_*; el
|
||||
# west és l'east amb `flip_h: true` declarat per-frame (mapping form,
|
||||
# no hereta del nivell d'anim). `frame_ms` per-frame creix linealment
|
||||
# per donar la sensació de desaccelerar fins a aturar-se. `loop: false`
|
||||
# → es queda al darrer frame i marca completed.
|
||||
death_spinning:
|
||||
frames:
|
||||
- 0
|
||||
- { index: 8, flip_h: true }
|
||||
- 4
|
||||
- 8
|
||||
- 0
|
||||
- { index: 8, flip_h: true }
|
||||
- 4
|
||||
- 8
|
||||
- 0
|
||||
- { index: 8, flip_h: true }
|
||||
- 4
|
||||
- 8
|
||||
frame_ms: [60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280]
|
||||
loop: false
|
||||
|
||||
# Mort per caiguda a un agujero (DeathCause::HOLE). `loop: false` perquè
|
||||
# `animationIsCompleted()` dispari el respawn timer determinísticament.
|
||||
death_hole:
|
||||
frames: [15]
|
||||
frame_ms: 60
|
||||
loop: false
|
||||
|
||||
# Inèrcia de frenada en soltar la direcció. `threshold_ms` filtra
|
||||
# tocs curts (passets d'un tile sense rellisquera).
|
||||
inertia:
|
||||
brake_time_ms: 150
|
||||
threshold_ms: 300
|
||||
|
||||
# Gir de 180°: durant `duration_ms` el facing es fixa a la direcció
|
||||
# intermèdia indicada (purament visual).
|
||||
turn:
|
||||
duration_ms: 80
|
||||
transitions:
|
||||
west_to_east: south
|
||||
east_to_west: north
|
||||
north_to_south: east
|
||||
south_to_north: west
|
||||
|
||||
# Salt vertical (px de z, no toca la posició al món).
|
||||
# `shadow_y_offset`: puja la sombra per compensar la franja
|
||||
# transparent sota els peus del sprite.
|
||||
jump:
|
||||
impulse: 200
|
||||
gravity: 600
|
||||
landing_stun_ms: 400
|
||||
shadow_y_offset: 3
|
||||
sound: jump.wav
|
||||
land_sound: land.wav
|
||||
|
||||
# Projecció post-impacte: surt cap a la direcció amb més recorregut
|
||||
# lliure i frena fins a 0 en `brake_time_ms`.
|
||||
hurt:
|
||||
speed: 120
|
||||
brake_time_ms: 500
|
||||
sound: hurt.wav
|
||||
|
||||
# Atac amb làtigo. `range_tiles` és la longitud abans de retallar
|
||||
# contra murs/sarcs; `cooldown_ms` allarga ATTACKING amb una
|
||||
# recuperació en què la hitbox ja no fa dany.
|
||||
whip:
|
||||
range_tiles: 3
|
||||
swing_ms: 200
|
||||
cooldown_ms: 200
|
||||
damage: 1
|
||||
sound: whip.wav
|
||||
|
||||
# Mort + respawn. `respawn_delay_ms` és la pausa entre la fi de
|
||||
# `death_spinning` i la reaparició al spawn. `lives` és el comptador
|
||||
# inicial d'intents (estil Mario): cada respawn el decrementa en 1, pot
|
||||
# anar a negatius (no hi ha game over).
|
||||
death:
|
||||
respawn_delay_ms: 500
|
||||
lives: 3
|
||||
@@ -0,0 +1,48 @@
|
||||
# Configuració de la GameScene: timings i corbes de la FSM de sala
|
||||
# (ROOM_INTRO i ROOM_OUTRO). Tots els valors són ms i tota la geometria
|
||||
# i font del text es reutilitzen del tema UI (data/ui.yaml > pause +
|
||||
# notification). Si manca el fitxer o alguna clau, GameScene cau als
|
||||
# fallbacks compilats; veure `readSceneConfig` a game_scene.cpp.
|
||||
|
||||
room_intro:
|
||||
fade_in_ms: 500 # negre opac → món visible
|
||||
text_delay_ms: 500 # espera amb el món net abans de sortir el text
|
||||
text_enter_ms: 400 # text esquerra → centre
|
||||
text_hold_ms: 2000 # text quiet al centre
|
||||
text_exit_ms: 400 # text centre → fora per l'esquerra
|
||||
line_gap_px: -4 # px extra entre les dues línies (piràmide / sala)
|
||||
# Delays d'spawn aeri **mesurats des de la fi de la música d'INTRO**
|
||||
# (callback `Audio::setOnMusicEnded`). 0 = cau al moment exacte que
|
||||
# `room_intro.ogg` acaba; valors > 0 escalonen els jugadors. En mode
|
||||
# single-player s'usa només `player_spawn_delay_ms`.
|
||||
player_spawn_delay_ms: 0
|
||||
player2_spawn_delay_ms: 300
|
||||
# Preset de reverb (catàleg a `data/sounds.yaml`) aplicat al primer
|
||||
# LAND de cada jugador durant la cinemàtica d'entrada. La sala dona
|
||||
# "més detall" sonor a l'INTRO; els salts del joc i els respawns
|
||||
# post-mort cauen al so sec. Buit o absent = sense reverb.
|
||||
land_reverb: stone_hall
|
||||
|
||||
room_outro:
|
||||
text_enter_ms: 400
|
||||
text_hold_ms: 2000
|
||||
text_exit_ms: 400
|
||||
fade_out_ms: 500 # món visible → negre opac
|
||||
line_gap_px: -4 # px extra entre les dues línies (fase / completada)
|
||||
black_hold_ms: 500 # pausa en negre abans d'entrar a la pròxima sala
|
||||
|
||||
text:
|
||||
# Noms d'easing tal com els resol UiConfig::curveByName (vegeu
|
||||
# `data/ui.yaml`). El text entra suau i surt accelerant.
|
||||
enter_curve: outCubic
|
||||
exit_curve: inCubic
|
||||
|
||||
# Panel translúcid pintat sota el rètol de transició (INTRO i OUTRO).
|
||||
# Va centrat verticalment al centre del playfield i ocupa tota
|
||||
# l'amplada del canvas. Creix d'altura amb la mateixa enter_curve del
|
||||
# text i decreix amb l'exit_curve. `color` són tres canals RGB (0..255);
|
||||
# `alpha` és la transparència del blend (0 = invisible, 255 = opac).
|
||||
text_panel:
|
||||
color: [36, 182, 170]
|
||||
alpha: 128
|
||||
height_px: 32
|
||||
@@ -0,0 +1,37 @@
|
||||
# Presets d'efectes per a sons puntuals. Les crides
|
||||
# Audio::playSoundWithEcho(name, preset, group) i
|
||||
# Audio::playSoundWithReverb(name, preset, group) referencien aquests
|
||||
# presets pel seu nom (camp `preset`). Si el nom no existeix, el so es
|
||||
# reprodueix sec amb un avís a stderr.
|
||||
#
|
||||
# Camps:
|
||||
# echo:
|
||||
# delay_ms → temps fins al primer rebot, en mil·lisegons.
|
||||
# feedback → reinjecció (0..0.95). >0.7 sona "metàl·lic".
|
||||
# wet → mescla del senyal humit (0..1). 1 = només eco.
|
||||
# reverb:
|
||||
# room_size → mida percebuda de la sala (0..1).
|
||||
# damping → atenuació de freqüències altes en cada rebot (0..1).
|
||||
# wet → mescla humida (0..1).
|
||||
#
|
||||
# Els processadors limiten la cua a 800 ms (echo) i 1500 ms (reverb).
|
||||
|
||||
echo:
|
||||
cave:
|
||||
delay_ms: 220
|
||||
feedback: 0.45
|
||||
wet: 0.35
|
||||
short:
|
||||
delay_ms: 80
|
||||
feedback: 0.30
|
||||
wet: 0.25
|
||||
|
||||
reverb:
|
||||
stone_hall:
|
||||
room_size: 0.78
|
||||
damping: 0.50
|
||||
wet: 0.40
|
||||
small_room:
|
||||
room_size: 0.40
|
||||
damping: 0.70
|
||||
wet: 0.30
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 5.1 KiB |
@@ -0,0 +1,77 @@
|
||||
# Tema de la UI: índexs de paleta, timings, fonts i flags visuals. Tota
|
||||
# clau absent cau al valor compilat a Defaults::UI (veure defaults.hpp).
|
||||
|
||||
colors:
|
||||
render_info_text: 58
|
||||
render_info_shadow: 16
|
||||
canvas_bg: 72
|
||||
notification_text: 232
|
||||
notification_bg: 64
|
||||
exit_text: 71
|
||||
exit_bg: 2
|
||||
menu_bg: 2
|
||||
menu_border: 232
|
||||
menu_title: 237
|
||||
menu_label: 58
|
||||
menu_value: 232
|
||||
menu_cursor: 71
|
||||
menu_disabled: 16
|
||||
hud_bg: 9
|
||||
hud_label: 58
|
||||
hud_value: 187
|
||||
hud_flash: 237
|
||||
# Rètol de transició de sala (ROOM_INTRO i ROOM_OUTRO). Independents
|
||||
# de notification_* perquè la fase i les notificacions són dos canals
|
||||
# visuals distints: ací pots canviar el color del rètol sense tocar
|
||||
# els missatges del HUD.
|
||||
room_text: 160
|
||||
room_text_outline: 253
|
||||
|
||||
timings:
|
||||
notification_ms: 4000
|
||||
exit_ms: 4000
|
||||
|
||||
fonts:
|
||||
hud:
|
||||
name: "8bithud"
|
||||
gif: "data/fonts/8bithud.gif"
|
||||
fnt: "data/fonts/8bithud.fnt"
|
||||
kerning: 1
|
||||
placeholder:
|
||||
name: "ari-w9500-condensed-display"
|
||||
gif: "data/fonts/ari-w9500-condensed-display.gif"
|
||||
fnt: "data/fonts/ari-w9500-condensed-display.fnt"
|
||||
kerning: 1
|
||||
# Font del rètol de transició de sala (ROOM_INTRO i ROOM_OUTRO).
|
||||
room_text:
|
||||
name: "CaesarDressing-Regular"
|
||||
gif: "data/fonts/CaesarDressing-Regular.gif"
|
||||
fnt: "data/fonts/CaesarDressing-Regular.fnt"
|
||||
kerning: 1
|
||||
menu_title:
|
||||
name: "ari-w9500-display"
|
||||
gif: "data/fonts/ari-w9500-display.gif"
|
||||
fnt: "data/fonts/ari-w9500-display.fnt"
|
||||
kerning: 0
|
||||
menu_items:
|
||||
name: "ari-w9500-condensed-display"
|
||||
gif: "data/fonts/ari-w9500-condensed-display.gif"
|
||||
fnt: "data/fonts/ari-w9500-condensed-display.fnt"
|
||||
kerning: 0
|
||||
|
||||
hud:
|
||||
bg_enabled: false
|
||||
entry_ms: 800 # durada de l'animació d'entrada dels tres blocs
|
||||
flash_ms: 250 # durada del flash quan un jugador rep un hit
|
||||
shake_ms: 300 # durada del shake que acompanya el flash
|
||||
|
||||
# Pausa graduada de la GameScene: el món no para d'un colp, s'atura amb
|
||||
# easing i torna a arrancar amb una altra corba. Els timings es poden
|
||||
# afinar independentment, i les corbes permeten estils diferents entrant
|
||||
# i sortint (p.ex. frenar suau i arrancar seca). Corbes disponibles:
|
||||
# linear, outQuad, inQuad, inOutQuad, outCubic, inCubic.
|
||||
pause:
|
||||
enter_ms: 400
|
||||
exit_ms: 250
|
||||
enter_curve: outCubic
|
||||
exit_curve: inCubic
|
||||
@@ -0,0 +1,10 @@
|
||||
# `docs/`
|
||||
|
||||
| Fitxer | Tipus | Estat |
|
||||
|--------|-------|-------|
|
||||
| [audit-core-audio.md](audit-core-audio.md) | Auditoria de `source/core/audio/` | Arxivada (tancada amb `refactor/audio-engine-class`) |
|
||||
| [audit-core-rendering.md](audit-core-rendering.md) | Auditoria de `source/core/rendering/` | Arxivada (tots els ítems DONE/WONTFIX/DEFERRED documentats) |
|
||||
| [audit-game.md](audit-game.md) | Auditoria de `source/game/` | Arxivada (tancada amb `refactor/options-class`) |
|
||||
| [convencions.md](convencions.md) | Convencions del repo (commits, `debug.yaml`) | Viu |
|
||||
| [entities-system.md](entities-system.md) | Disseny del sistema d'entitats (factory, bus, EntityManager, hazards) | Viu |
|
||||
| [player-animations.md](player-animations.md) | Notes d'animacions direccionals i `stand` automàtic del Player | Viu |
|
||||
@@ -3,7 +3,7 @@
|
||||
Rama: `audit/game`.
|
||||
Cobertura: `source/game/{entities,map,scenes,ui}` + `source/game/options.{hpp,cpp}` (12 fitxers, ~1.486 línies).
|
||||
|
||||
Estado global: **28 DONE, 2 DONE (parcial), 5 WONTFIX, 4 DEFERRED** sobre 39 ítems (el top 5 compta dins dels seus IDs individuals). Cada canvi té un commit aïllat i s'ha provat amb `SDL_VIDEODRIVER=dummy` + `SIGINT` (el log de `Game start` a `Bye!` és idèntic entre commits; la branca `config.yaml` de backup verificada manualment amb un fitxer corrupte). Els 4 DEFERRED (COUP-02, COUP-03, CONV-02 resta, PATCH-01) van a la futura branca `refactor/options-class`.
|
||||
Estado global: **32 DONE, 1 DONE (parcial), 5 WONTFIX, 0 DEFERRED** sobre 39 ítems (el top 5 compta dins dels seus IDs individuals). Cada canvi té un commit aïllat i s'ha provat amb `SDL_VIDEODRIVER=dummy` + `SIGINT` (el log de `Game start` a `Bye!` és idèntic entre commits; la branca `config.yaml` de backup verificada manualment amb un fitxer corrupte). Els 4 DEFERRED inicials (COUP-02, COUP-03, CONV-02 part Options, PATCH-01) s'han tancat a la branca `refactor/options-class` (registre de commits al final del fitxer); l'únic ítem que queda en DONE (parcial) és INCONS-01, intencionadament — la nota n'explica el motiu.
|
||||
|
||||
Estados posibles:
|
||||
- `PENDING` — identificat, sense tocar
|
||||
@@ -69,14 +69,14 @@ Els IDs es mantenen estables una vegada assignats. Cada entrada apunta al fitxer
|
||||
|----|--------------|----------|-----------|-------|-------|
|
||||
| COUP-01 | `core/input/global_inputs.cpp:15` | `core/` inclou `game/ui/notification_presets.hpp` | Crítico | DONE | `notification_presets.hpp` mogut a `core/rendering/` (al costat del `Notifier` que consumeix). `global_inputs.cpp` i el comentari de `notifier.hpp` actualitzats |
|
||||
| COUP-02 | `player.cpp` + `Map::PLAYFIELD_*`, `PITCH_*`, `GRID_*`, `collides` | `Player` coneix la geometria interna del mapa | Importante | DEFERRED | Player només llegeix globals de `Map::`, no muta res. Injectar `MapInfo&` té sentit quan hi haja múltiples mapes simultanis o un sistema tipus ECS. Mentre hi haja un Map actiu global, l'acoblament és acceptable. Es refacta amb COUP-03 a `refactor/options-class` |
|
||||
| COUP-03 | `options.hpp:96-105` | Globals `inline` mutats lliurement des de `core/input/global_inputs.cpp` | Importante | DEFERRED | Encapsular tots els globals darrere setters + observers per Screen/Audio/RenderInfo és un refactor ample (equivalent a COUP-01 de l'audit d'àudio, que va anar a una branca pròpia). Es tractarà a `refactor/options-class` perquè toca tots els callers de core/input. Es fa en branca separada |
|
||||
| COUP-03 | `options.hpp:96-105` | Globals `inline` mutats lliurement des de `core/input/global_inputs.cpp` | Importante | DONE | Estat mutable mogut a anonymous namespace dins `options.cpp`; API pública són getters const-ref (`window()`, `video()`, `audio()`, `locale()`, `postfxPresets()`, `crtpiPresets()`) + setters específics (`setWindowZoom`, `setShaderEnabled`, `setActiveShaderName`, ...) que muten i persisteixen via `saveToFile()`. Els setters NO notifiquen els subsistemes: el caller (`global_inputs.cpp`) crida després `Screen::setWindowZoom`, `reapplyShader()`, etc. La part "observers" del audit es deixa WONTFIX (veure CONV-02): notificar des d'Options invertiria la direcció `Director→Screen/Audio` i Options aprenria del layout dels subsistemes |
|
||||
| COUP-04 | `tilemap.cpp:117-138` | Clip+blit directe al canvas ARGB que podria viure a `Surface` | Menor | WONTFIX | Únic caller en tot el projecte. Promoure a `Surface::blitInto` ara afegiria una API genèrica per a un ús únic (YAGNI). Si un segon client apareix, es mourea en aquell moment |
|
||||
|
||||
## 5. Inconsistències internes
|
||||
|
||||
| ID | Fitxer:línia | Troballa | Severitat | Estat | Notes |
|
||||
|----|--------------|----------|-----------|-------|-------|
|
||||
| INCONS-01 | `game/*` | 3 estils de mòdul: namespace+inline (Options), classe all-static (Map), OOP (Player/Tilemap), namespace+constexpr (UiColor/Notification) | Importante | DONE (parcial) | Resolt "classe all-static" amb PATCH-07 (Map → namespace). Queden 3 estils diferenciats per responsabilitat: `namespace` per a estat compartit (Options, Map), classes per a entitats amb estat (Player, Tilemap), `namespace+constexpr` per a alias/presets (UiColor, Notification). La distinció ara és intencional, no fortuïta |
|
||||
| INCONS-01 | `game/*` | 3 estils de mòdul: namespace+inline (Options), classe all-static (Map), OOP (Player/Tilemap), namespace+constexpr (UiColor/Notification) | Importante | DONE (parcial) | Resolt "classe all-static" amb PATCH-07 (Map → namespace). Queden 3 estils diferenciats per responsabilitat: `namespace` per a estat compartit (Options, Map), classes per a entitats amb estat (Player, Tilemap), `namespace+constexpr` per a alias/presets (UiColor, Notification). La distinció ara és intencional, no fortuïta. NOTA (post `refactor/options-class`): "encapsular" en COUP-03 vol dir ocultar l'estat mutable darrere de l'API del namespace (anonymous namespace + getters/setters), no convertir Options a classe — el patró `namespace per a estat compartit` queda preservat |
|
||||
| INCONS-02 | `options.cpp:372,477,530,579,607` | `std::cerr` en comptes de `std::cout` (convenció del projecte) | Importante | DONE | Totes les ocurrències (10, incloent les afegides a PATCH-04) substituïdes per `std::cout`. Test manual: amb config.yaml trencat el missatge de backup va per stdout |
|
||||
| INCONS-03 | `options.cpp` vs `tilemap.cpp:77-78` | Diferent política en YAML absent: `contains()` vs accés directe que llança | Menor | WONTFIX | Les dues polítiques són correctes per al seu cas: `Tilemap`/`Map::loadFrom` tenen fitxers de definició (asset obligatori → llançar); `Options` parseja preferències de l'usuari (camp absent → manté default). No és inconsistència sinó dos contractes diferents |
|
||||
| INCONS-04 | `options.cpp`, `player.cpp` | Locals ALL-CAPS (`CONTENT`, `IT`, `DT`, `OLD_X`) barrejats amb `snake_case` | Menor | WONTFIX | El `.clang-tidy` del projecte exigeix `LocalConstantCase = UPPER_CASE` i `VariableCase = lower_case` — els ALL-CAPS són per a `const`/`constexpr` i els snake_case per a mutables. La barreja es correcta i està dictada pel linter |
|
||||
@@ -97,6 +97,16 @@ Els IDs es mantenen estables una vegada assignats. Cada entrada apunta al fitxer
|
||||
| ID | Fitxer:línia | Troballa | Severitat | Estat | Notes |
|
||||
|----|--------------|----------|-----------|-------|-------|
|
||||
| CONV-01 | `player.*`, `map.*`, `ui/*`, `game_scene.cpp` | Speed, caixa de col·lisió, geometria, índexs paleta, rutes d'assets, nom del track: tot cablejat | Crítico | DONE | Tapat per HC-01/02/03/04/05/06: speed a Defaults::Player, col·lisió a player.yaml, geometria a pyramid.yaml, índexs paleta a Defaults::UI, rutes a Defaults::Pyramid + Config |
|
||||
| CONV-02 | `map.*`, `options.*` | Interfaces+callbacks: `Map` simulated class, `Options` globals sense observers | Importante | DONE (parcial) | Map resolt amb PATCH-07 (namespace). La part d'Options/observers va amb COUP-03 (DEFERRED a `refactor/options-class`) |
|
||||
| CONV-02 | `map.*`, `options.*` | Interfaces+callbacks: `Map` simulated class, `Options` globals sense observers | Importante | DONE | Map resolt amb PATCH-07 (namespace). Options resolt amb COUP-03: estat mutable encapsulat darrere de l'API del namespace (anonymous ns + getters/setters). La part "observers" (notificacions automàtiques a Screen/Audio) queda WONTFIX justificat: invertiria la direcció de dependència `Director→Screen/Audio` que el projecte manté intencionadament |
|
||||
| CONV-03 | `options.cpp` | `std::cerr` viola la regla "std::cout only" | Importante | DONE | Resolt amb INCONS-02 |
|
||||
| CONV-04 | `player.cpp:27` | `SDL_GetKeyboardState` directe en `Player::update` (fuga de SDL al gameplay) | Menor | WONTFIX | `SDL_GetKeyboardState` és l'accés correcte per a state-based polling a 60Hz; `EventBuffer` és per a esdeveniments discrets. La fuga és cosmètica — abstraure-la no aportaria valor |
|
||||
|
||||
---
|
||||
|
||||
## Registro de commits — `refactor/options-class`
|
||||
|
||||
Branca per a tancar els 4 ítems DEFERRED de `audit/game`. Base: `main` (32dcf26).
|
||||
|
||||
- `a3b03b8` — COUP-03 + CONV-02: estat mutable d'`Options` mogut a anonymous ns + API de getters/setters; `global_inputs.cpp` i `director.cpp` adaptats. CONV-02 (part Options) tanca al pas. La part "observers" del audit es deixa WONTFIX (invertiria `Director→Screen/Audio`)
|
||||
- `64e0178` — COUP-02: `Map::Info` plana injectada al `Player` (només Player; momia/roca/loadlevel resten amb `Map::*` global, com diu la nota original del audit)
|
||||
- `d9f6fb4` — PATCH-01: `tryMove(dx, dy, CollisionFn)` a `MovingSprite` + hook virtual `collisionBox()` a `Sprite`; `Player::update` deixa de fer `update + revertir + reaplicar` i passa a `animate(dt) + tryMove`
|
||||
@@ -0,0 +1,65 @@
|
||||
# Convencions del repo
|
||||
|
||||
## Missatges de commit
|
||||
|
||||
- En **valencià**, frases curtes i compactes.
|
||||
- **Sense firma**: res de `Co-Authored-By:`, `Generated with Claude Code`, ni cap altra firma automàtica.
|
||||
- Un commit per unitat lògica. Si el canvi afecta àrees separades (feature + docs + test), separar en commits diferents.
|
||||
- Prefix opcional estil convencional (`feat:`, `fix:`, `refactor:`, `docs:`, `test:`) quan ajude a la llegibilitat.
|
||||
|
||||
Exemple correcte:
|
||||
|
||||
```
|
||||
feat(scenes): afegir placeholders LogoScene/TitleScene/IntroScene
|
||||
|
||||
Tres escenes de 5 s cadascuna que precedeixen GameScene. Cada classe
|
||||
té el seu fitxer perquè divergiran quan es desenvolupe el contingut
|
||||
real (logo animat, menú de títol, cutscene).
|
||||
```
|
||||
|
||||
## `debug.yaml` (ajust de dev, no versionat)
|
||||
|
||||
Fitxer personal de desenvolupament, **fora del repo**. Viu a la mateixa carpeta
|
||||
de sistema que `config.yaml`:
|
||||
|
||||
- Linux (XDG): `$XDG_CONFIG_HOME/jailgames/aee_2026/debug.yaml`
|
||||
- Per defecte: `~/.config/jailgames/aee_2026/debug.yaml`
|
||||
|
||||
### Comportament segons build
|
||||
|
||||
Convenció alineada amb `../aee` i `../projecte_2026`:
|
||||
|
||||
- **Release** (`make` — binari d'ús normal): `debug.yaml` s'ignora completament.
|
||||
El joc arranca sempre a `LOGO` i recorre el flux sencer
|
||||
`LOGO → TITLE → INTRO → GAME`, tal com ho veurà el jugador final.
|
||||
- **Debug** (`make debug`): es llig `debug.yaml`. Si no existeix, es crea
|
||||
automàticament amb `start_scene: GAME` perquè el dia a dia de gameplay no
|
||||
arrossegui els 15 s de placeholders. Editant-lo a mà, es pot triar el punt
|
||||
d'entrada.
|
||||
- `make release` fa el Release + empaquetat per a distribució (zip/dmg/tar.gz);
|
||||
l'empaquetat concret està pendent d'implementar.
|
||||
|
||||
### Format
|
||||
|
||||
```yaml
|
||||
start_scene: GAME # valors vàlids: LOGO, TITLE, INTRO, GAME
|
||||
```
|
||||
|
||||
- `start_scene: LOGO` → arranca a `LogoScene` i recorre `LOGO → TITLE → INTRO → GAME`.
|
||||
- `start_scene: TITLE` → arranca a `TitleScene` i recorre `TITLE → INTRO → GAME`.
|
||||
- `start_scene: INTRO` → arranca a `IntroScene` i recorre `INTRO → GAME`.
|
||||
- `start_scene: GAME` → arranca directament a `GameScene` (valor que s'escriu
|
||||
per defecte en la creació automàtica).
|
||||
|
||||
### Comportament davant d'errors (build debug)
|
||||
|
||||
- Fitxer buit, valor desconegut o YAML invàlid → `GAME` amb warning a stderr.
|
||||
El fitxer no es reescriu automàticament: es respecta l'edició manual per si
|
||||
l'usuari ha teclejat alguna cosa intencionadament.
|
||||
|
||||
### Durada dels placeholders
|
||||
|
||||
Cada escena placeholder (LOGO, TITLE, INTRO) mostra el seu nom i un compte
|
||||
regressiu de 5 segons al centre de la pantalla. Es pot saltar amb qualsevol
|
||||
tecla excepte les registrades a `data/input/keys.yaml` (F1–F12 i Escape),
|
||||
que mantenen el seu comportament de hotkey global.
|
||||
@@ -248,10 +248,102 @@ Tanca els hooks d'IA i col·lisió que quedaven apuntalats a les fases 1–3.
|
||||
- **Smoke tests**: T12 (CollisionBox), T13 (Mummy::step), T11 (sarc YAML),
|
||||
T10 (torch anim). Total 14/14.
|
||||
|
||||
## 7. Estat global
|
||||
## 7. Fase 5 — Convenció de caixa "peus" com el jugador + animacions direccionals (DONE)
|
||||
|
||||
- **Andamiatge, integració, render animat, falsa perspectiva, moviment**: DONE. Els 26 actors del `pyramid.yaml` (4 torches, 20 sarcs, 1 door, 1 mummy) es carreguen, animen, es col·loquen amb l'anchor correcte, col·lisionen segons caixa lògica i el z-order interleava per peus.
|
||||
- **Pendents majors**: ENT-HAZ-01 (sistema de vides), ENT-DOOR-01 (transició d'escena), ENT-IA-02 (bias IA), PER-01 (perímetre amb blocked_predicate quan els sarcs siguin veïns).
|
||||
Alinea la momia amb la convenció de col·lisió i animació del Player, i soluciona regressions de navegació derivades del canvi de caixa.
|
||||
|
||||
### Caixa concèntrica amb el rectangle-base
|
||||
|
||||
- **`collision.base_size: [bw, bh]`** (nou camp YAML, opcional). Si apareix, la caja no s'alinea a la vora inferior del sprite sinó que queda **concèntrica amb el rectangle-base `bw×bh` dels peus** (els `bh` píxels inferiors del sprite). Fórmula: `off_y = sprite_h − (base_h + H) / 2`. Sense `base_size`, comportament antic (alineada a la vora inferior del sprite).
|
||||
- **`anchor: feet` ara alinea la caja, no el sprite**. Si l'entitat té `CollisionBox`, el loader col·loca `pixel_rect_` perquè la caja quedi exactament al tile. Sense caja, fallback a la convenció antiga (sprite centrat horitzontalment + vora inferior al tile). Per la momia: sprite 12×24 a (tile_x+2, tile_y−10); caja 16×16 a (tile_x, tile_y) — omple el tile sencer com fa el jugador.
|
||||
- **`mummy.yaml`**: `collision: { size: [16, 16], base_size: [12, 12] }`. Abans era `[12, 12]` alineada a baix; visualment quedava "enganxada al mur inferior" dins dels corredors horitzontals.
|
||||
|
||||
### Snap-to-edge per girar al playfield
|
||||
|
||||
La caja 16×16 té exactament l'amplada del corredor (16 px). Amb el moviment contínu `speed × delta_time` (~0.66 px/frame), la momia s'aturava a una posició subpíxel dins de l'última cel·la (p. ex. `x=279.87` en lloc de `x=280`) i qualsevol gir perpendicular col·lidia amb el sarc adjacent al limit. [`Mummy::snapToPlayfieldEdge`](../source/game/entities/concrete/mummy.cpp) redueix el `dx/dy` fins a la vora exacta quan el candidat sortiria del playfield; el gir posterior ja parteix d'una posició alineada amb el lane.
|
||||
|
||||
### Animacions direccionals
|
||||
|
||||
- **`mummy.gif` a 12 frames** (graella 12×1) amb el mateix esquema que el jugador: 0–3 walk_down, 4–7 walk_up, 8–11 walk lateral. Sense variants `stand_*` (mai no para).
|
||||
- **`Mummy` té tres `AnimationComponent`** privats + un helper `changeDirection(dx, dy)` (únic punt d'entrada per canviar direcció) que selecciona l'animació i aplica el flip: `dx ≠ 0` → lateral amb flip horitzontal segons signe; `dy < 0` → up; `dy > 0` → down.
|
||||
- **`Entity::flip_` a la base** + `getFlip()`. `renderEntity` el passa a `Surface::render`, que ja suportava `SDL_FlipMode`. Les entitats estàtiques queden a `SDL_FLIP_NONE` automàticament.
|
||||
|
||||
### Pipeline de build: sincronització de `data/`
|
||||
|
||||
Canvi a [CMakeLists.txt](../CMakeLists.txt): la còpia de `data/` al build era un `add_custom_command(POST_BUILD)`, que només es dispara quan el binari es relinkeja. Si s'editava només un YAML o un GIF sense tocar codi, `build/data/` quedava obsolet. Ara és un `add_custom_target(sync_data ALL ...)` amb dependència del target principal — els custom targets sense BYPRODUCTS es consideren sempre out-of-date, així que la còpia s'executa a cada `cmake --build`.
|
||||
|
||||
### Refactors de suport
|
||||
|
||||
- `Mummy::step` partit: `snapToPlayfieldEdge`, `collectDirectionPools`, `applyChoice` — per mantenir la complexitat cognitiva sota el llindar de clang-tidy (< 25).
|
||||
|
||||
## 8. Fase 6 — Pintat de tiles (DONE)
|
||||
|
||||
Introdueix la mecànica central: el jugador deixa rastre al terra en trepitjar-lo, i els sarcòfags compten tiles **pintats** (no tiles *trepitjats*). Font única de veritat del pintat: una nova capa `PaintLayer` escolta el moviment del Player, filtra pintabilitat i re-publica `tile_painted`, que és el que ara observen els sarcs.
|
||||
|
||||
### Cadena d'events resultant
|
||||
|
||||
```
|
||||
Player canvi de tile
|
||||
→ bus: on_tile_entered { col, row, paint_tile_index }
|
||||
→ PaintLayer: filtra Map::isPaintable + "primer pinta guanya"
|
||||
→ buffer ARGB + bus: tile_painted { col, row }
|
||||
→ EntityManager fan-out → Sarcophagus::notifyTilePainted
|
||||
→ perímetre complet → OPENING → OPEN → sarcophagus_opened
|
||||
→ tots oberts → Door::open
|
||||
```
|
||||
|
||||
Dos events encadenats perquè el *pintat* no és la *presència*: es filtra per pintabilitat i per "ja pintat", i el re-publish garanteix que els sarcs no vegin falsos positius (p. ex. el jugador lliscant contra un mur, o un tile ja marcat per l'altre jugador).
|
||||
|
||||
### Canvis per fitxer
|
||||
|
||||
| Path | Canvi |
|
||||
|---|---|
|
||||
| [event_bus.hpp](../source/game/entities/event_bus.hpp) | Nou `struct PaintedTile { col, row, tile_index }` al `Payload` variant + overload `publish`. |
|
||||
| [map.hpp](../source/game/map/map.hpp) / [.cpp](../source/game/map/map.cpp) | `Map::isPaintable(col, row)` → `true` si el tile cau dins del playfield i no s'encavalca cap sarc. |
|
||||
| [map/paint_layer.hpp](../source/game/map/paint_layer.hpp) / [.cpp](../source/game/map/paint_layer.cpp) | Classe nova. Subscriu `on_tile_entered`, marca `painted_[row][col]`, blita el tile al buffer ARGB propi, publica `tile_painted`. `render()` anàleg a `Tilemap::render`. |
|
||||
| [player.hpp](../source/game/entities/player.hpp) / [.cpp](../source/game/entities/player.cpp) | Camp `paint_tile_index_` al ctor (default -1). El Player publica `PaintedTile` amb el seu índex en lloc de `TilePos`. |
|
||||
| [entity.hpp](../source/game/entities/entity.hpp) / [.cpp](../source/game/entities/entity.cpp) | Rename `notifyTileEntered` → `notifyTilePainted`. |
|
||||
| [concrete/sarcophagus.hpp](../source/game/entities/concrete/sarcophagus.hpp) / [.cpp](../source/game/entities/concrete/sarcophagus.cpp) | Mateix rename. La lògica del `visited_` és idèntica — només canvia qui li truca el hook. |
|
||||
| [entity_manager.hpp](../source/game/entities/entity_manager.hpp) / [.cpp](../source/game/entities/entity_manager.cpp) | La subscripció canvia d'`on_tile_entered` → `tile_painted`. Fan-out renombrat a `notifyPlayerTilePainted`. |
|
||||
| [game_scene.hpp](../source/game/scenes/game_scene.hpp) / [.cpp](../source/game/scenes/game_scene.cpp) | Membre `std::unique_ptr<PaintLayer> paint_layer_` (declarat després d'`entity_manager_` per garantir ordre invers de destrucció). Helper local `readOptionalInt` llegeix `player1_paint_tile` del YAML del mapa i el passa al Player. `render()` crida `paint_layer_->render()` entre `tilemap_.render()` i els actors; s'amaga al mode editor. |
|
||||
| [data/maps/pyramid.yaml](../data/maps/pyramid.yaml) | Camps nous `player1_paint_tile: 2` i `player2_paint_tile: 2` (índex provisional, canviable sense recompilar). |
|
||||
| [tests/test_entities_smoke.cpp](../tests/test_entities_smoke.cpp) | T9 adaptat: publica `tile_painted` directament al bus per aïllar el fan-out d'EntityManager de la resta de la cadena. |
|
||||
|
||||
### Decisions de disseny
|
||||
|
||||
- **Centralitzat vs per-sarc**: l'estat "pintat" viu al `PaintLayer`, no dins cada sarc. Els sarcs només apunten els tiles del seu perímetre que han estat notificats pintats. Aquesta separació deixa preparat per a items-en-sarc (obrir quan `N` items trobats, no quan tots oberts) i per a altres productors de pintat futurs (enemics, trampes).
|
||||
- **"Primer pinta guanya"**: `PaintLayer::tryPaint` retorna false si el tile ja està pintat. No se sobreescriu amb l'índex d'un altre jugador. Reduïm fonts de decisió inconsistent; el rastre visual és estable.
|
||||
- **Paint tile per jugador**: l'índex l'aporta el YAML del mapa (`player1_paint_tile`, `player2_paint_tile`), no el YAML del jugador. Raó: depèn del tileset del mapa — cada nivell pot oferir rastres diferents; canviar de mapa canvia l'índex vàlid.
|
||||
- **Render al mode editor**: desactivat. L'editor edita el tilemap base; la capa de pintat és d'estat de joc, no de dades del nivell. Quan `editor_.active()`, `SKIP_ACTORS=true` i el `paint_layer_->render()` se salta amb la resta d'actors.
|
||||
- **Resolució**: 16×16 reutilitzant `Map::tile_size` i l'origen del tilemap. El `PaintLayer` emmagatzema `tile_size_` com a camp propi per facilitar una eventual migració a 8×8 (graella 2× més fina) sense tocar la interfície.
|
||||
- **Persistència**: runtime only. No hi ha camp `painted:` al YAML. Entrar al `GameScene` reseteja el pintat.
|
||||
- **No cal `is_blocked` al `sarc->place(...)`** per a aquest nivell: l'`outer_corridor` del `pyramid.yaml` és exactament 1 tile, cosa que garanteix que el perímetre de qualsevol sarc (fins i tot els de cantonades) cau íntegrament dins del playfield i és pintable. Quedaria rellevant si el disseny futur posa sarcs sense passadís al voltant — cobert per PER-01.
|
||||
|
||||
### Verificació
|
||||
|
||||
- `make debug` net, sense warnings ni de clang-tidy ni de cppcheck als fitxers nous.
|
||||
- `./build/test_entities_smoke`: **ALL OK** (14/14). T9 adaptat (publica `tile_painted` en lloc d'`on_tile_entered`); la resta de tests del sistema d'entitats passen sense tocar.
|
||||
- `timeout 5 xvfb-run ./build/aee_2026`: arrenca, log `PaintLayer: 21x15 tiles (336x240 px), tileset data/tilesets/tomb.gif` després de `EntityManager: 27 entitats carregades`, surt amb `Bye!` sense crash.
|
||||
|
||||
### Pendent derivat
|
||||
|
||||
| ID | Descripció | Estat |
|
||||
|---|---|---|
|
||||
| PAINT-01 | **Tile de pintat definitiu** quan l'art estigui madur. Ara `2` provisional al `pyramid.yaml`. Canviar l'índex no demana recompilar. | DEFERRED |
|
||||
| PAINT-02 | **Resolució 8×8** si es vol més granularitat. El `PaintLayer` porta el seu propi `tile_size_`; cal propagar-lo al ctor per a no lligar-lo a `Tilemap::getTileSize()`. També cal decidir com es mapen els perímetres dels sarcs a sub-caselles. | DEFERRED |
|
||||
| PAINT-03 | **Smoke unit del `PaintLayer`**: "primer pinta guanya", filtre d'`isPaintable`, publicació de `tile_painted`. Ara la cadena està coberta end-to-end via T9 (fan-out del bus) i via l'smoke d'arrencada, però el filtre i la deduplicació no tenen test directe. | DEFERRED |
|
||||
| PAINT-04 | **Player 2 cablejat al `GameScene`**: la lectura del YAML i el ctor ja suporten un segon jugador amb `paint_tile_index_` propi; només cal instanciar-lo amb Controls distints (p. ex. WASD) i afegir-lo al vector. | DEFERRED |
|
||||
|
||||
### Memòria durable afectada
|
||||
|
||||
INF-02 queda parcialment cobert: el `PaintedTile` ja porta `tile_index` (per jugador), que és una forma d'identificar qui pinta sense haver de propagar un `player_id` explícit. Si en el futur cal saber *qui* (p. ex. per puntuació individual), caldrà afegir el camp; el canvi és local al struct.
|
||||
|
||||
---
|
||||
|
||||
## 9. Estat global
|
||||
|
||||
- **Andamiatge, integració, render animat, falsa perspectiva, moviment, animació direccional de momia, pintat de tiles**: DONE. El `pyramid.yaml` actual (4 torches, 20 sarcs, 1 door, 2 mummies) es carrega, anima, col·loca amb l'anchor correcte, col·lisiona segons caixa lògica, el z-order interleava per peus, la momia gira visualment a cada intersecció, i el jugador deixa rastre al terra.
|
||||
- **Pendents majors**: ENT-HAZ-01 (sistema de vides), ENT-DOOR-01 (transició d'escena), ENT-IA-02 (bias IA), PER-01 (perímetre amb blocked_predicate quan els sarcs siguin veïns), PAINT-03 (test unit del PaintLayer), PAINT-04 (Player 2 cablejat).
|
||||
- **Smoke tests**: 14/14 passant; regla durable de cobrir amb smoke tota lògica no trivial respectada a totes les fases.
|
||||
|
||||
El document és viu: cada tasca nova hauria d'afegir-s'hi (entrada amb ID, descripció, estat) al fer-la o ajornar-la.
|
||||
@@ -0,0 +1,85 @@
|
||||
# Animacions i direccionalitat del jugador
|
||||
|
||||
Documenta els afegits d'aquesta sessió a `source/game/entities/player.*`: sis
|
||||
animacions direccionals amb `facing_` persistent, regla de flip només per al
|
||||
lateral i transició automàtica a `stand` quan la col·lisió bloqueja el
|
||||
moviment. Tot el canvi és local al `Player`; no s'ha tocat l'EntityManager
|
||||
ni el bus.
|
||||
|
||||
## 1. YAML — sis animacions
|
||||
|
||||
El fitxer `data/player/player.yaml` defineix ara sis animacions, agrupades
|
||||
en tres files de 4 frames sobre `player.gif` (convenció habitual: d'esquerra
|
||||
a dreta i de dalt a baix):
|
||||
|
||||
| Nom | Frames | Descripció |
|
||||
|---------------|------------|----------------------------|
|
||||
| `stand_down` | `[0]` | parat mirant cap avall |
|
||||
| `walk_down` | `[0..3]` | caminant cap avall |
|
||||
| `stand_up` | `[4]` | parat mirant cap amunt |
|
||||
| `walk_up` | `[4..7]` | caminant cap amunt |
|
||||
| `stand` | `[8]` | parat lateral |
|
||||
| `walk` | `[8..11]` | caminant lateral |
|
||||
|
||||
Les laterals (`stand` i `walk`) ja existien; les variants `_up` i `_down`
|
||||
són noves i permeten distingir visualment la direcció de moviment.
|
||||
|
||||
## 2. `Facing` persistent
|
||||
|
||||
S'ha afegit l'enum privat `Facing { SIDE, UP, DOWN }` amb el membre
|
||||
`facing_{SIDE}`. La tria es fa a cada `update`, amb prevalença lateral
|
||||
per si un dia es permetessin diagonals:
|
||||
|
||||
- `vx != 0` → `SIDE` (inclou diagonals).
|
||||
- `vx == 0 && vy < 0` → `UP`.
|
||||
- `vx == 0 && vy > 0` → `DOWN`.
|
||||
- Cap input → es conserva l'últim `facing_` perquè l'animació `stand_*`
|
||||
coincideixi amb l'última direcció caminada.
|
||||
|
||||
## 3. Regla de flip
|
||||
|
||||
- `SIDE`: el sprite es flipeja horitzontalment si `vx < 0`, i es restaura
|
||||
a `FLIP_NONE` si `vx > 0`. El sprite original mira a la dreta.
|
||||
- `UP` / `DOWN`: el flip es força explícitament a `FLIP_NONE`. Els sprites
|
||||
frontals/dorsals no s'han d'espillar només perquè l'últim lateral havia
|
||||
estat cap a l'esquerra.
|
||||
|
||||
## 4. `stand` automàtic a la col·lisió
|
||||
|
||||
Quan el jugador prem una tecla però la col·lisió (mapa o sarcòfag) li
|
||||
impedeix avançar, l'animació `walk*` s'atura i es força la variant
|
||||
`stand_*` corresponent al facing. L'algorisme:
|
||||
|
||||
1. Al principi del frame es captura `FRAME_START_X` / `FRAME_START_Y`.
|
||||
2. S'aplica snap al passadís perpendicular si hi ha pur moviment H o V.
|
||||
3. `animate(delta_time)` avança el frame de l'animació actual sense
|
||||
tocar la posició.
|
||||
4. `tryMove(vx*dt, vy*dt, collidesFn)` (a `MovingSprite`) aplica el
|
||||
moviment per eix: si la `collisionBox()` resultant col·lisiona —
|
||||
el Player la sobreescriu retornant `getBaseBox()` —, l'eix es
|
||||
reverteix. Així el jugador llisca contra murs/sarcòfags en lloc de
|
||||
bloquejar-se en sec.
|
||||
5. Després de `clampToPlayfield`, es compara la posició final amb el
|
||||
valor capturat al pas 1. Si és idèntica, `MOVED = false` i es tria
|
||||
`stand` / `stand_up` / `stand_down` segons `facing_`. Si hi ha
|
||||
hagut moviment real (incloent-hi snap), s'aplica la variant
|
||||
`walk*` del mateix facing.
|
||||
|
||||
D'aquesta manera, xocar contra un mur o un sarcòfag fa que el jugador es
|
||||
quede immòbil en compte d'animar el caminat "en el lloc". El facing
|
||||
segueix reflectint la intenció (on vol anar), no el resultat, perquè
|
||||
l'`stand_up` d'empentar-se contra un mur ha de mostrar el personatge
|
||||
mirant amunt.
|
||||
|
||||
## 5. Estat
|
||||
|
||||
- DONE: sis animacions al YAML, `facing_` persistent, flip condicionat,
|
||||
`stand` automàtic a la col·lisió.
|
||||
- Build i smoke tests (13/13) verdes. (Aquesta nota deia "14/14" abans;
|
||||
el comptatge real al `tests/test_entities_smoke.cpp` són 13 funcions
|
||||
`test*` invocades des de `main()` — corregit a `refactor/options-class`.)
|
||||
- Sense smoke test dedicat: la lògica està entrellaçada amb teclat i
|
||||
`Map::collides`; l'smoke actual de `Player` no cobreix teclat simulat.
|
||||
- Pendent (ajornat): si algun dia es vol feedback auditiu de wall-bump
|
||||
(so en xocar), la condició natural és `!MOVED && (vx != 0 || vy != 0)`
|
||||
al final d'`update`.
|
||||
@@ -0,0 +1,472 @@
|
||||
# Pla d'implementació — Menú de servei
|
||||
|
||||
Pla per a portar el menú de servei de `../aee` (`source/core/rendering/menu.cpp`)
|
||||
a `aee_2026`, adaptat a l'arquitectura actual (Scenes + Director instanciat +
|
||||
`Options` amb setters persistents + `Screen` ARGB + `Text` bitmap). Document de
|
||||
referència per a implementar-ho en una sessió futura; no s'ha escrit cap codi
|
||||
encara.
|
||||
|
||||
Objectiu visual: caixa centrada amb vora i títol, navegable amb fletxes +
|
||||
Retorn + Retrocés, oberta i tancada amb F12 (hotkey `menu_toggle`), retolada amb
|
||||
la font `ari-w9500-display`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Referència a `../aee`
|
||||
|
||||
Tot el que segueix és la base; la còpia mental serveix per a no reinventar res:
|
||||
|
||||
- [source/core/rendering/menu.hpp](../../aee/source/core/rendering/menu.hpp) — API
|
||||
pública (`init/destroy/toggle/close/isOpen/isVisible/render/handleKey`).
|
||||
- [source/core/rendering/menu.cpp](../../aee/source/core/rendering/menu.cpp) —
|
||||
implementació completa (676 línies). Conté:
|
||||
- Struct `Item` i `Page` amb `ItemKind::{Toggle, Cycle, IntRange, Submenu,
|
||||
KeyBind, Action}`.
|
||||
- Animació d'obertura/tancament (`OPEN_SPEED`, `CLOSE_SPEED`,
|
||||
`HEIGHT_RATE`) i transició entre pàgines (`pushPage/popPage`).
|
||||
- Dibuix manual sobre `Uint32* pixel_data` amb `blendRect/fillRect/drawBorder`.
|
||||
- `buildRoot/buildVideo/buildAudio/buildControls/buildGame/buildSystem`.
|
||||
- Claus de `locale/ca.yaml` sota `menu.{titles, items, values}` — es poden
|
||||
reutilitzar gairebé literalment.
|
||||
|
||||
---
|
||||
|
||||
## 2. Diferències d'arquitectura rellevants
|
||||
|
||||
| Punt | `../aee` | `aee_2026` |
|
||||
| ----------------- | ---------------------------------------------------- | -------------------------------------------------------------------------- |
|
||||
| Overlay | `Overlay::render()` dibuixa Menú + Notifier al buffer | No hi ha Overlay. Cada Scene crida `Notifier::render()` i `RenderInfo::render()` |
|
||||
| Director | Singleton `Director::get()` amb `requestQuit/requestRestart` | Classe instanciada; sortida via `SDL_EVENT_QUIT` a `EventBuffer` |
|
||||
| `Options` | Membres `inline` mutats directament | Getters const-ref + setters que persisteixen automàticament |
|
||||
| Setter → subsistema | Dins del mètode (ex. `Screen::toggleVSync`) | El caller crida `Options::setX(v)` **i** `Screen::setX(v)` per separat |
|
||||
| Remapping tecles | `KeyConfig::scancodePtr(id)` retorna mutable | `KeyConfig` actual no té `scancodePtr` — veure limitació a §5 |
|
||||
| Render | Pixel-level (blend manual sobre `Uint32*`) | `Screen::fillRect(argb)` + `Text::writeDX`; ARGB cru via `getCanvasArgb()` |
|
||||
| Font | `8bithud.fnt` via `Text` | `Text` via `Resource::Cache` (paletat sobre `ui`) |
|
||||
|
||||
Conseqüències per al disseny:
|
||||
|
||||
1. El menú ha d'integrar-se en el pipeline de render de cada Scene, no via
|
||||
Overlay. Pot ser tan simple com una línia al final de cada `render()`
|
||||
abans de `Screen::get().present()`, al costat de Notifier/RenderInfo.
|
||||
2. El menú intercepta input **abans** que `GlobalInputs::update()` processi
|
||||
les hotkeys (si és obert, només F12 i les fletxes arriben al menú; la
|
||||
resta d'esdeveniments es consumeixen perquè no activin F1–F11).
|
||||
3. Els setters de `Options::` ja persisteixen. El menú crida
|
||||
`Options::setX(v); Screen::setX(v);` com fa `global_inputs.cpp`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Ubicació i mòduls nous
|
||||
|
||||
```
|
||||
source/game/ui/service_menu.hpp (nou)
|
||||
source/game/ui/service_menu.cpp (nou)
|
||||
data/fonts/ari-w9500-display.{fnt,gif} (ja existeixen)
|
||||
data/locale/ca.yaml (afegir clau arrel `menu:`)
|
||||
data/locale/en.yaml (afegir clau arrel `menu:`)
|
||||
data/input/keys.yaml (canviar `toggle_render_info` → `menu_toggle` en F12, o moure F12 a menu i reubicar render_info)
|
||||
```
|
||||
|
||||
Raonament:
|
||||
|
||||
- Viu a `game/ui/` perquè el contingut de les pàgines toca `Options::` i
|
||||
`Screen::` del joc; no és un primitiu de renderització com `Text` o
|
||||
`Notifier`.
|
||||
- Exposa l'API com a **singleton** (`ServiceMenu::get()`) per coherència amb
|
||||
`Notifier` i `RenderInfo`.
|
||||
- `init()` rep `std::shared_ptr<Text>` + `Palette ui_palette`, igual que
|
||||
`Notifier::init()`. El Director construeix la font display i la injecta.
|
||||
|
||||
Esquelet de l'API:
|
||||
|
||||
```cpp
|
||||
// source/game/ui/service_menu.hpp
|
||||
class ServiceMenu {
|
||||
public:
|
||||
static auto get() -> ServiceMenu&;
|
||||
void init(std::shared_ptr<Text> font, const Palette& ui_palette);
|
||||
|
||||
[[nodiscard]] auto isOpen() const -> bool; // accepta input
|
||||
[[nodiscard]] auto isVisible() const -> bool; // dibuixa caixa (també tancant-se)
|
||||
|
||||
void toggle();
|
||||
void close();
|
||||
|
||||
void update(int delta_ms); // avança animacions
|
||||
void render(); // dibuixa sobre el canvas de Screen
|
||||
|
||||
// Retorna true si el menú ha consumit l'esdeveniment (tecla empassada).
|
||||
auto handleEvent(const SDL_Event& ev) -> bool;
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Hotkey i integració amb `GlobalInputs`
|
||||
|
||||
Decisió: F12 passa a ser `menu_toggle` i el cicle de l'overlay d'info es mou
|
||||
a una altra tecla (per ex. F10) o es manté a F12 i s'obre el menú amb una
|
||||
altra (per ex. F9). Opció recomanada:
|
||||
|
||||
- `menu_toggle` → **F9** (tecla nova, no conflictiva).
|
||||
- `toggle_render_info` es queda a F12 (hàbit actual).
|
||||
|
||||
Motius: evita trencar la memòria muscular existent i deixa F12 — una tecla
|
||||
familiar per a debug — lliure per a la seua funció actual.
|
||||
|
||||
Afegir a `data/input/keys.yaml`:
|
||||
|
||||
```yaml
|
||||
- id: menu_toggle
|
||||
code: "F9"
|
||||
desc: "Obri/tanca el menú de servei"
|
||||
```
|
||||
|
||||
Flux d'input (escena GameScene::update):
|
||||
|
||||
```cpp
|
||||
// Abans de GlobalInputs::update, donar prioritat al menú.
|
||||
for (auto it = EventBuffer::events.begin(); it != EventBuffer::events.end();) {
|
||||
if (ServiceMenu::get().isOpen() && ServiceMenu::get().handleEvent(*it)) {
|
||||
it = EventBuffer::events.erase(it);
|
||||
} else if (it->type == SDL_EVENT_KEY_DOWN &&
|
||||
it->key.scancode == KeyConfig::get().scancode("menu_toggle")) {
|
||||
ServiceMenu::get().toggle();
|
||||
it = EventBuffer::events.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
GlobalInputs::update(delta_ms);
|
||||
```
|
||||
|
||||
- Mentre el menú està obert, les hotkeys (F1–F8, F11, F12) queden bloquejades
|
||||
perquè els KEY_DOWN els consumeix el menú.
|
||||
- Escape dins del menú ↔ close (o cancel·lació de captura de tecla, si es
|
||||
porta el KeyBind — veure §5).
|
||||
- F9 sempre passa el toggle (encara que el menú estiga obert).
|
||||
|
||||
Alternativa més neta a mig termini: un `InputRouter` centralitzat al Director
|
||||
amb prioritats (Menu → GlobalInputs → Scene). De moment, cadascuna de les
|
||||
Scenes (`LogoScene`, `TitleScene`, `IntroScene`, `GameScene`) crida el bloc
|
||||
d'input-del-menú al principi de `update()`, igual que ja fan amb
|
||||
`GlobalInputs::update`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Contingut de les pàgines (adaptat a `Options` actuals)
|
||||
|
||||
### Root
|
||||
|
||||
- Vídeo → submenú
|
||||
- Àudio → submenú
|
||||
- Sistema → submenú
|
||||
|
||||
_No hi ha Controls ni Game:_ `Options::` actual no exposa remapping de tecles
|
||||
de joc (les `KeysGame` són constants de `Defaults::`), ni opcions "de joc"
|
||||
equivalents a `use_new_logo` / `show_preload`. Si en el futur apareixen, es
|
||||
pot afegir sense reescriure res.
|
||||
|
||||
### Vídeo
|
||||
|
||||
Items, cadascun mapa el setter+notificador com `global_inputs.cpp`:
|
||||
|
||||
| Label | Tipus | Origen | Handler |
|
||||
| --------------------- | -------- | ------------------------------------ | ---------------------------------------------------------------------------------------- |
|
||||
| `menu.items.zoom` | IntRange | `Options::window().zoom` | `Options::setWindowZoom(v); Screen::get().setWindowZoom(v)` (rang 1–6, clamp) |
|
||||
| `menu.items.screen` | Toggle | `Options::window().fullscreen` | `Options::setWindowFullscreen(v); Screen::get().setFullscreen(v)` |
|
||||
| `menu.items.vsync` | Toggle | `Options::video().vsync` | `Options::setVideoVSync(v); Screen::get().setVSync(v)` |
|
||||
| `menu.items.scaling_mode` | Cycle | `Options::video().scaling_mode` | `Options::setVideoScalingMode(m); Screen::get().setScalingMode(m)` |
|
||||
| `menu.items.shader` | Toggle | `Options::video().shader.enabled` | `Options::setShaderEnabled(v)` + `reapplyShader()` (copiar helper de `global_inputs.cpp`)|
|
||||
| `menu.items.shader_type` | Cycle | `Options::video().shader.current_shader` | `Options::setActiveShaderName("postfx"/"crtpi")` + `reapplyShader()` |
|
||||
| `menu.items.preset` | Cycle | preset actiu segons shader | `Options::setPostFXPreset(s)` o `setCrtPiPreset(s)` + `reapplyShader()` |
|
||||
| `menu.items.render_info` | Cycle | `Options::video().render_info_position` | `Options::setVideoRenderInfoPosition(p)` |
|
||||
| `menu.items.uptime` | Toggle | `Options::video().render_info_show_time` | `Options::setVideoRenderInfoShowTime(v)` (sols visible si render_info ≠ OFF) |
|
||||
|
||||
Claus `visible` (oculta quan no apliquen):
|
||||
|
||||
- `shader_type`, `preset` → només si `shader.enabled == true`.
|
||||
- `uptime` → només si `render_info_position != OFF`.
|
||||
|
||||
### Àudio
|
||||
|
||||
| Label | Tipus | Origen | Handler |
|
||||
| ---------------------------- | -------- | ---------------------------------- | ------------------------------------------------------------------------------------ |
|
||||
| `menu.items.master_enable` | Toggle | `Options::audio().enabled` | Caldrà afegir `Options::setAudioEnabled(v)` (no hi és avui) + `Audio::applyConfig()` |
|
||||
| `menu.items.master_volume` | IntRange | `Options::audio().volume` | `Options::setAudioVolume(v)` (afegir) + `Audio::applyConfig()` |
|
||||
| `menu.items.music` | Toggle | `Options::audio().music.enabled` | `Options::setAudioMusicEnabled(v)` (afegir) + `Audio::applyConfig()` |
|
||||
| `menu.items.music_volume` | IntRange | `Options::audio().music.volume` | `Options::setAudioMusicVolume(v)` (afegir) + `Audio::applyConfig()` |
|
||||
| `menu.items.sounds` | Toggle | `Options::audio().sound.enabled` | `Options::setAudioSoundEnabled(v)` (afegir) + `Audio::applyConfig()` |
|
||||
| `menu.items.sounds_volume` | IntRange | `Options::audio().sound.volume` | `Options::setAudioSoundVolume(v)` (afegir) + `Audio::applyConfig()` |
|
||||
|
||||
**Dependència prèvia:** `Options::` no té setters d'àudio. Cal afegir-los
|
||||
seguint el mateix patró que `setVideoVSync` (muten namespace intern +
|
||||
`saveToFile()`) i exposar `Audio::applyConfig(Audio::Config)` o
|
||||
`Audio::setVolumes(...)` — depenent del que ja existeixi a `core/audio/`.
|
||||
|
||||
Les escales de volum són 0.0–1.0 amb pas 0.05 (5 %), com a `../aee`.
|
||||
|
||||
### Sistema
|
||||
|
||||
Subtítol: `v<VERSION> (<GIT_HASH>)` — usar el `version.h` generat per CMake
|
||||
(ja existeix a `source/version.h.in`).
|
||||
|
||||
| Label | Tipus | Handler |
|
||||
| --------------------- | ------ | -------------------------------------------------------------------------------- |
|
||||
| `menu.items.exit_game`| Action | Injectar un `SDL_EVENT_QUIT` a `EventBuffer::events` perquè `Director::processEvents` el vegi |
|
||||
|
||||
_Sense `Restart`_ de moment: Director no té un pas de reinici en calent, i
|
||||
simular-lo a partir del director actual implica destruir l'escena i
|
||||
reconstruir el flux — fora d'abast d'aquest menú. Deixar-ho com a TODO si
|
||||
es necessita.
|
||||
|
||||
### Limitació: pàgina Controls diferida
|
||||
|
||||
El menú de `../aee` té una pàgina Controls (`buildControls`) que permet
|
||||
remappar `up/down/left/right` i la tecla de menú. A `aee_2026`:
|
||||
|
||||
- Les tecles de joc són `Defaults::KeysGame::{UP,DOWN,LEFT,RIGHT}` — constants
|
||||
`constexpr`, no mutables.
|
||||
- `KeyConfig` actual no exposa `scancodePtr(id)`.
|
||||
|
||||
Opcions, de menys a més invasiu:
|
||||
|
||||
1. **No portar Controls** en aquesta iteració (recomanat per a la primera
|
||||
versió).
|
||||
2. Migrar `KeysGame` a `Options::` + afegir `KeyConfig::scancodePtr(id)` per
|
||||
a hotkeys. Requereix tocar `Player::Controls`, el config.yaml i keys.yaml.
|
||||
|
||||
Si s'opta per (2), la pàgina Controls s'afegeix exactament igual que a
|
||||
`../aee`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Renderitzat
|
||||
|
||||
Dos camins segons nivell d'ambició:
|
||||
|
||||
### A. Simple — sòlid opac (MVP)
|
||||
|
||||
- Caixa centrada: `Screen::get().fillRect(x, y, w, h, ui_palette_[idx_fons])`.
|
||||
- Vora: 4 `fillRect` d'1 px amb color `ui_palette_[idx_vora]`.
|
||||
- Text: `Text::writeDX(CENTER_FLAG | COLOR_FLAG | SHADOW_FLAG, ...)`.
|
||||
- Cap blend; el joc queda totalment ocult darrere de la caixa (equivalent a
|
||||
`../aee` però amb `BG_ALPHA = 255`).
|
||||
|
||||
Afegir dos índexs nous a `utils/defaults.hpp → UI::`:
|
||||
|
||||
```cpp
|
||||
constexpr Uint8 MENU_BG = /* triar paleta sega-cube */;
|
||||
constexpr Uint8 MENU_BORDER = /* triar */;
|
||||
constexpr Uint8 MENU_TITLE = /* blanc */;
|
||||
constexpr Uint8 MENU_LABEL = /* gris clar */;
|
||||
constexpr Uint8 MENU_VALUE = /* cian/groc */;
|
||||
constexpr Uint8 MENU_CURSOR = /* destacat */;
|
||||
constexpr Uint8 MENU_EMPTY = /* gris */;
|
||||
```
|
||||
|
||||
### B. Semi-transparent (opcional, iteració 2)
|
||||
|
||||
Calca el `blendRect` de `../aee`: expandir `Screen::` amb un `blendRect(x,
|
||||
y, w, h, argb, alpha)` que opere sobre `canvas_.data()`. El canvas és
|
||||
`std::uint32_t*` format ABGR (equivalent a ARGB little-endian). Amb això, el
|
||||
fons del menú pot ser `alpha=220` i es veu el joc apagat per darrere.
|
||||
|
||||
Mida fixa recomanada per al MVP: `BOX_W = 220`, altura dinàmica (igual que
|
||||
`../aee`). Canvas lògic d'`aee_2026` és `GameCanvas::WIDTH`/`HEIGHT` —
|
||||
confirmar mida real (mateixos 320×200?) a `utils/defines.hpp` abans de
|
||||
hardcoded.
|
||||
|
||||
---
|
||||
|
||||
## 7. Font `ari-w9500-display`
|
||||
|
||||
Ja és al disc i ja té el metadatfnt corresponent
|
||||
([data/fonts/ari-w9500-display.fnt](../data/fonts/ari-w9500-display.fnt),
|
||||
11 px d'altura, 12 px de box). És paletable sobre `ui` com les altres
|
||||
fonts bitmap.
|
||||
|
||||
Al `Director::Director()`, afegir després del bloc de fonts HUD/Placeholder:
|
||||
|
||||
```cpp
|
||||
constexpr const char* MENU_FONT_NAME = "ari-w9500-display";
|
||||
constexpr const char* MENU_FONT_GIF = "data/fonts/ari-w9500-display.gif";
|
||||
constexpr const char* MENU_FONT_FNT = "data/fonts/ari-w9500-display.fnt";
|
||||
|
||||
const std::string MENU_FONT_GIF_KEY = std::string(MENU_FONT_NAME) + ".gif";
|
||||
Resource::Cache::get().loadSurface(MENU_FONT_GIF_KEY, MENU_FONT_GIF);
|
||||
Resource::Cache::get().getSurface(MENU_FONT_GIF_KEY)->setPalette(
|
||||
Resource::Cache::get().getPalette("ui"));
|
||||
auto menu_font_surface = Resource::Cache::get().getSurface(MENU_FONT_GIF_KEY);
|
||||
auto menu_text = std::make_shared<Text>(menu_font_surface, MENU_FONT_FNT);
|
||||
|
||||
ServiceMenu::get().init(menu_text, ui_palette);
|
||||
```
|
||||
|
||||
Centralitzar les constants a `Defaults::UI::Font::MENU_{NAME,GIF,FNT}` perquè
|
||||
encaixi amb el memo `project_ui_fonts.md` ("tot a `Defaults::UI::Font`").
|
||||
|
||||
---
|
||||
|
||||
## 8. Locale
|
||||
|
||||
Copiar les claus de `../aee/data/locale/ca.yaml` sota `menu:` (titles, items,
|
||||
values) a **ca.yaml** i **en.yaml**. El primer commit pot incloure només `ca`;
|
||||
`en` val la pena fer-lo a la vegada perquè és traducció directa.
|
||||
|
||||
Valors a eliminar de la còpia (no apliquen ací):
|
||||
|
||||
- `menu.items.{use_new_logo, show_title_credits, show_preload}` (no hi ha
|
||||
pàgina Game).
|
||||
- `menu.items.{move_up, move_down, move_left, move_right, menu_key,
|
||||
restart, aspect_4_3, texture_filter, internal_resolution, supersampling}`
|
||||
(no portats a l'MVP — veure §5 i les decisions de pàgina Vídeo).
|
||||
|
||||
Claus noves que no tenia `../aee` — cap per a l'MVP; si s'afegeixen pàgines
|
||||
en futures iteracions, traduir a mesura.
|
||||
|
||||
---
|
||||
|
||||
## 9. Fases d'implementació
|
||||
|
||||
Cada fase és un commit. Ordre perquè cada pas compila i s'executa.
|
||||
|
||||
### Fase 1 — setters d'àudio a `Options::`
|
||||
|
||||
_Preparatori; sense això la pàgina Àudio no pot mutar res._
|
||||
|
||||
- Afegir `setAudioEnabled`, `setAudioVolume`, `setAudioMusicEnabled`,
|
||||
`setAudioMusicVolume`, `setAudioSoundEnabled`, `setAudioSoundVolume` a
|
||||
`options.hpp/.cpp`. Patró exacte de `setVideoVSync`: muten estat intern +
|
||||
`saveToFile()`.
|
||||
- Exposar un mètode de reconfiguració en calent a `core/audio/`
|
||||
(`Audio::applyConfig` o similar) si no hi és ja.
|
||||
- Smoke test: `xvfb-run` 5 s, joc arrenca i música sona.
|
||||
|
||||
### Fase 2 — hotkey i esquelet `ServiceMenu`
|
||||
|
||||
- Afegir `menu_toggle` a `data/input/keys.yaml` amb F9.
|
||||
- Crear `service_menu.{hpp,cpp}` amb API mínima: `init`, `toggle`, `close`,
|
||||
`isOpen`, `isVisible`, `update`, `render` (render de caixa buida amb
|
||||
títol fix "Opcions"), `handleEvent` (només F9, Escape, Backspace).
|
||||
- Al Director: construir font `ari-w9500-display`, inicialitzar
|
||||
`ServiceMenu::get()`.
|
||||
- A `GameScene::update()`: bloc d'input abans de `GlobalInputs::update`
|
||||
(veure §4). A `GameScene::render()`: `ServiceMenu::get().render()` abans
|
||||
de `Screen::present()`. **No** replicar encara a Logo/Title/Intro — el
|
||||
menú només té sentit quan l'usuari juga.
|
||||
- Smoke test: F9 obri caixa centrada, Escape/F9 la tanca.
|
||||
|
||||
### Fase 3 — framework de pàgines
|
||||
|
||||
- Portar `Item`, `Page`, `ItemKind`, `stack_`, `pushPage`/`popPage`,
|
||||
`nextVisibleCursor`, `isVisible(Item)`, `handleKey` de `../aee` (quasi
|
||||
literalment).
|
||||
- Portar `renderPageContent` simplificat (sense transicions ni clipping
|
||||
avançat al principi): render de títol, línia, items amb cursor i valor.
|
||||
- Afegir les claus `menu.titles.root` i `menu.values.*` a `ca.yaml`/`en.yaml`.
|
||||
- Implementar `buildRoot()` amb tres entrades Submenu buides (es naveguen
|
||||
però totes porten a pàgines sense items visibles, text "(Buit)").
|
||||
- Smoke test: navegació amunt/avall, enter a submenú, backspace torna.
|
||||
|
||||
### Fase 4 — Pàgina Vídeo
|
||||
|
||||
- Implementar `buildVideo()` amb els items de §5.
|
||||
- Copiar el helper `reapplyShader()` de `global_inputs.cpp` a un lloc
|
||||
compartit (p. ex. `game/options_apply.{hpp,cpp}`) perquè el cridin
|
||||
tant `global_inputs` com el menú, sense duplicar la lògica.
|
||||
- Afegir claus `menu.items.{zoom,screen,vsync,scaling_mode,shader,shader_type,preset,render_info,uptime}`.
|
||||
- Smoke test: obrir menú, toggle vsync, canvi d'escala, comprovar que la
|
||||
notificació NO apareix (és el menú qui fa el canvi, no les hotkeys).
|
||||
|
||||
### Fase 5 — Pàgina Àudio
|
||||
|
||||
- `buildAudio()` amb els 6 items. Helpers `volPct`/`stepVolume` copiats de
|
||||
`../aee`.
|
||||
- Claus `menu.items.{master_enable,master_volume,music,music_volume,sounds,sounds_volume}`.
|
||||
- Smoke test: baixar volum de música, pujar volum de sons.
|
||||
|
||||
### Fase 6 — Pàgina Sistema
|
||||
|
||||
- `buildSystem()` amb subtítol de versió i `exit_game` que injecta
|
||||
`SDL_EVENT_QUIT`.
|
||||
- Smoke test: obrir sistema, exit_game tanca el joc net.
|
||||
|
||||
### Fase 7 (opcional) — polit
|
||||
|
||||
- Animació d'obertura/tancament (porta les constants `OPEN_SPEED`,
|
||||
`CLOSE_SPEED`, `HEIGHT_RATE`, `TRANSITION_SPEED` de `../aee`).
|
||||
- `blendRect` a `Screen::` per a fons semi-transparent.
|
||||
- Replicar el bloc d'input del menú a `Title/Intro/Logo` si s'acorda que el
|
||||
menú hi és accessible també.
|
||||
- Traduccions EN si no es van fer abans.
|
||||
|
||||
---
|
||||
|
||||
## 10. Tests
|
||||
|
||||
- **Smoke (xvfb-run 5 s)** després de cada fase — memòria `feedback_smoke_test_mandatory`.
|
||||
- Unitaris amb Catch2 si el framework està muntat (veure `tests/` actual):
|
||||
- `ServiceMenu::toggle()` idempotent després de `close()`.
|
||||
- `nextVisibleCursor` salta items ocults.
|
||||
- `handleEvent` retorna `true` per a KEY_DOWN quan està obert, `false`
|
||||
quan està tancat.
|
||||
- Manual:
|
||||
- Obrir F9 → tancar F9 → obrir de nou (no leak d'estat).
|
||||
- Entrar a Vídeo → Shader off → verificar que `shader_type` i `preset`
|
||||
desapareixen (anim o salt).
|
||||
- Tancar el joc, re-obrir, verificar que `config.yaml` conserva els
|
||||
canvis (els fa `Options::setX`).
|
||||
|
||||
---
|
||||
|
||||
## 11. Riscos i punts oberts
|
||||
|
||||
- **Input routing als altres Scenes.** Si l'usuari vol obrir el menú
|
||||
durant el LOGO/TITLE/INTRO, cal replicar el bloc d'input. Decisió: no.
|
||||
Motiu: són placeholders amb 5 s de durada; obrir un menú allà és
|
||||
overkill. Anotar-ho a la capçalera de `service_menu.hpp`.
|
||||
- **`EventBuffer::events.erase(it)`** invalida els iteradors — mà amb el
|
||||
loop. Patró amb `it = events.erase(it)` ↔ `++it` és segur.
|
||||
- **Blend rect vs. GPU pipeline.** Si el shader PostFX reprocessa el
|
||||
canvas, el menú ha d'estar pintat *abans* del shader (i ja ho està,
|
||||
perquè el canvas ARGB va al shader). Res a fer; tot passa per
|
||||
`Screen::present()` al final.
|
||||
- **Captura de tecla (`KeyBind`).** No s'implementa aquesta iteració
|
||||
(§5). Si es porta, l'event router ha de distingir "menú obert i
|
||||
capturant" — el menú consumeix **tota** tecla, no només Enter/Backspace/etc.
|
||||
- **`ari-w9500-display` és paletada.** Ha de compartir la paleta `ui` amb
|
||||
la resta d'overlays. Verificar que el GIF només té la tinta a l'índex
|
||||
`Text::INK_COLOR_INDEX = 1`; si no, el font_gen l'ha de regenerar (veure
|
||||
memòria `reference_font_generator`).
|
||||
- **F9 lliure?** Abans d'agafar F9 per al `menu_toggle`, confirmar que
|
||||
cap altra hotkey hi és mapejada en builds futures.
|
||||
|
||||
---
|
||||
|
||||
## 12. Resum d'arxius a tocar
|
||||
|
||||
**Nous:**
|
||||
|
||||
- `source/game/ui/service_menu.hpp`
|
||||
- `source/game/ui/service_menu.cpp`
|
||||
- (opcional) `source/game/options_apply.{hpp,cpp}` per compartir
|
||||
`reapplyShader` amb `global_inputs.cpp`.
|
||||
|
||||
**Modificats:**
|
||||
|
||||
- `source/utils/defaults.hpp` — afegir índexs `UI::MENU_*` + `UI::Font::MENU_*`.
|
||||
- `source/game/options.hpp/.cpp` — setters d'àudio (fase 1).
|
||||
- `source/core/system/director.cpp` — carregar font menú + `ServiceMenu::get().init(...)`.
|
||||
- `source/game/scenes/game_scene.cpp` — bloc d'input del menú a `update`,
|
||||
render del menú a `render` abans de `present`.
|
||||
- `data/input/keys.yaml` — afegir `menu_toggle: F9`.
|
||||
- `data/locale/ca.yaml`, `data/locale/en.yaml` — secció `menu:`.
|
||||
- `CMakeLists.txt` o `source/CMakeLists.txt` — afegir el nou `service_menu.cpp`.
|
||||
|
||||
**Sense tocar per a l'MVP:**
|
||||
|
||||
- `source/core/input/key_config.*` (no cal `scancodePtr`).
|
||||
- `source/core/audio/*` si ja exposa configuració en calent; si no, mínima
|
||||
adició.
|
||||
- LogoScene/TitleScene/IntroScene.
|
||||
@@ -5,9 +5,10 @@
|
||||
#include <algorithm> // Per a clamp
|
||||
#include <cstdio> // Per a std::fprintf
|
||||
|
||||
#include "core/audio/audio_adapter.hpp" // Per a AudioResource::getMusic/getSound
|
||||
#include "core/audio/jail_audio.hpp" // Per a Ja::* (motor jailgames)
|
||||
#include "utils/defaults.hpp" // Per a Defaults::Audio::FREQUENCY
|
||||
#include "core/audio/audio_adapter.hpp" // Per a AudioResource::getMusic/getSound
|
||||
#include "core/audio/jail_audio.hpp" // Per a Ja::* (motor jailgames)
|
||||
#include "core/audio/sound_effects_config.hpp" // Per a SoundEffectsConfig
|
||||
#include "utils/defaults.hpp" // Per a Defaults::Audio::FREQUENCY
|
||||
|
||||
// Invariant compile-time: tots els valors d'Audio::Group han de cabre als slots
|
||||
// de volum per grup que manté l'engine. Si s'afegeix una nova entrada a Group
|
||||
@@ -128,6 +129,47 @@ void Audio::playSound(Ja::Sound* sound, Group group) {
|
||||
}
|
||||
}
|
||||
|
||||
// Reprodueix un so processat per un eco definit a sounds.yaml. Si el preset no
|
||||
// existeix o l'engine retorna -1 (cap de canals d'efecte plé), cau a playSound
|
||||
// sec — l'usuari sent el so encara que la cua no s'apliqui.
|
||||
// NOLINTNEXTLINE(readability-make-member-function-const) — muta estat via engine_
|
||||
void Audio::playSoundWithEcho(const std::string& name, const std::string& preset_name, Group group) {
|
||||
if (!sound_enabled_) { return; }
|
||||
auto* sound = AudioResource::getSound(name);
|
||||
if (sound == nullptr) { return; }
|
||||
|
||||
const auto* params = SoundEffectsConfig::get().findEcho(preset_name);
|
||||
if (params == nullptr) {
|
||||
std::fprintf(stderr, "Audio: preset d'eco '%s' desconegut — so '%s' es reprodueix sec\n", preset_name.c_str(), name.c_str());
|
||||
engine_->playSound(sound, 0, static_cast<int>(group));
|
||||
return;
|
||||
}
|
||||
|
||||
if (engine_->playSoundWithEcho(sound, *params, static_cast<int>(group)) < 0) {
|
||||
engine_->playSound(sound, 0, static_cast<int>(group));
|
||||
}
|
||||
}
|
||||
|
||||
// Reprodueix un so processat per un reverb definit a sounds.yaml. Mateix
|
||||
// fallback que playSoundWithEcho.
|
||||
// NOLINTNEXTLINE(readability-make-member-function-const) — muta estat via engine_
|
||||
void Audio::playSoundWithReverb(const std::string& name, const std::string& preset_name, Group group) {
|
||||
if (!sound_enabled_) { return; }
|
||||
auto* sound = AudioResource::getSound(name);
|
||||
if (sound == nullptr) { return; }
|
||||
|
||||
const auto* params = SoundEffectsConfig::get().findReverb(preset_name);
|
||||
if (params == nullptr) {
|
||||
std::fprintf(stderr, "Audio: preset de reverb '%s' desconegut — so '%s' es reprodueix sec\n", preset_name.c_str(), name.c_str());
|
||||
engine_->playSound(sound, 0, static_cast<int>(group));
|
||||
return;
|
||||
}
|
||||
|
||||
if (engine_->playSoundWithReverb(sound, *params, static_cast<int>(group)) < 0) {
|
||||
engine_->playSound(sound, 0, static_cast<int>(group));
|
||||
}
|
||||
}
|
||||
|
||||
// Atura tots els sons
|
||||
// NOLINTNEXTLINE(readability-make-member-function-const) — muta estat via engine_; unique_ptr::operator-> no propaga const
|
||||
void Audio::stopAllSounds() {
|
||||
|
||||
@@ -80,7 +80,13 @@ class Audio {
|
||||
// --- Control de sons ---
|
||||
void playSound(const std::string& name, Group group = Group::GAME); // Reproduir so puntual per nom (muta globals de Ja)
|
||||
void playSound(Ja::Sound* sound, Group group = Group::GAME); // Reproduir so puntual per punter (muta globals de Ja)
|
||||
void stopAllSounds(); // Aturar tots els sons (muta globals de Ja)
|
||||
// Reprodueix un so processat per un efecte definit a data/sounds.yaml
|
||||
// (preset_name busca a SoundEffectsConfig). Si el preset no existeix
|
||||
// o el motor està al cap de canals amb efecte, fa fallback a playSound
|
||||
// sec — l'usuari sent el so igualment, sense la cua.
|
||||
void playSoundWithEcho(const std::string& name, const std::string& preset_name, Group group = Group::GAME);
|
||||
void playSoundWithReverb(const std::string& name, const std::string& preset_name, Group group = Group::GAME);
|
||||
void stopAllSounds(); // Aturar tots els sons (muta globals de Ja)
|
||||
|
||||
// --- Control de volum (API interna: float 0.0..1.0) ---
|
||||
void setSoundVolume(float volume, Group group = Group::ALL); // Ajusta el volum dels efectes
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
#include "core/audio/audio_effects.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "core/audio/jail_audio.hpp"
|
||||
|
||||
namespace AudioEffects {
|
||||
|
||||
namespace {
|
||||
|
||||
// --- Caps de cua ---
|
||||
constexpr float ECHO_TAIL_MS = 800.0F;
|
||||
constexpr float REVERB_TAIL_MS = 1500.0F;
|
||||
|
||||
// --- Constants Freeverb ---
|
||||
// Delays de comb i allpass tunats per a 44.1 kHz; els reescalem per
|
||||
// freqüència real de la font.
|
||||
constexpr int COMB_REFERENCE_RATE = 44100;
|
||||
constexpr std::array<int, 8> COMB_DELAYS_L = {1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617};
|
||||
constexpr std::array<int, 4> ALLPASS_DELAYS_L = {556, 441, 341, 225};
|
||||
constexpr int STEREO_SPREAD = 23;
|
||||
|
||||
// Mapeig de Schroeder/Dattorro/Freeverb estàndard.
|
||||
constexpr float FIXED_GAIN = 0.015F;
|
||||
constexpr float SCALE_ROOM = 0.28F;
|
||||
constexpr float OFFSET_ROOM = 0.7F;
|
||||
constexpr float SCALE_DAMP = 0.4F;
|
||||
|
||||
// --- Decodificació a float -1..1 ---
|
||||
// Suporta U8/S16, mono/estèreo. Mono es duplica a L i R (la cadena
|
||||
// d'efectes treballa sempre amb dos canals per simplicitat).
|
||||
auto decodeToStereoFloat(const Ja::Sound& src, std::vector<float>& left, std::vector<float>& right) -> bool {
|
||||
const auto& spec = src.spec;
|
||||
const Uint8* buf = src.buffer.get();
|
||||
if (buf == nullptr || src.length == 0) { return false; }
|
||||
|
||||
int bytes_per_sample = 0;
|
||||
if (spec.format == SDL_AUDIO_S16) {
|
||||
bytes_per_sample = 2;
|
||||
} else if (spec.format == SDL_AUDIO_U8) {
|
||||
bytes_per_sample = 1;
|
||||
} else {
|
||||
std::cerr << "AudioEffects: format de so no suportat (només U8 o S16)\n";
|
||||
return false;
|
||||
}
|
||||
if (spec.channels < 1 || spec.channels > 2) {
|
||||
std::cerr << "AudioEffects: el so ha de ser mono o estèreo\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::size_t TOTAL_FRAMES = src.length / static_cast<std::size_t>(bytes_per_sample * spec.channels);
|
||||
left.resize(TOTAL_FRAMES);
|
||||
right.resize(TOTAL_FRAMES);
|
||||
|
||||
for (std::size_t i = 0; i < TOTAL_FRAMES; ++i) {
|
||||
float sample_l = 0.0F;
|
||||
float sample_r = 0.0F;
|
||||
if (spec.format == SDL_AUDIO_S16) {
|
||||
const auto* p = reinterpret_cast<const std::int16_t*>(buf + (i * spec.channels * 2));
|
||||
sample_l = static_cast<float>(p[0]) / 32768.0F;
|
||||
sample_r = (spec.channels == 2) ? static_cast<float>(p[1]) / 32768.0F : sample_l;
|
||||
} else { // U8
|
||||
const Uint8* p = buf + (i * spec.channels);
|
||||
sample_l = (static_cast<float>(p[0]) - 128.0F) / 128.0F;
|
||||
sample_r = (spec.channels == 2) ? (static_cast<float>(p[1]) - 128.0F) / 128.0F : sample_l;
|
||||
}
|
||||
left[i] = sample_l;
|
||||
right[i] = sample_r;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Empaqueta dos canals float (-1..1) a S16 entrellaçat.
|
||||
void encodeStereoS16(const std::vector<float>& left, const std::vector<float>& right, std::vector<std::uint8_t>& out) {
|
||||
const std::size_t LEN = left.size();
|
||||
out.resize(LEN * 2 * sizeof(std::int16_t));
|
||||
auto* dst = reinterpret_cast<std::int16_t*>(out.data());
|
||||
for (std::size_t i = 0; i < LEN; ++i) {
|
||||
const float L = std::clamp(left[i], -1.0F, 1.0F);
|
||||
const float R = std::clamp(right[i], -1.0F, 1.0F);
|
||||
dst[(i * 2) + 0] = static_cast<std::int16_t>(std::lround(L * 32767.0F));
|
||||
dst[(i * 2) + 1] = static_cast<std::int16_t>(std::lround(R * 32767.0F));
|
||||
}
|
||||
}
|
||||
|
||||
// Reescala un delay de la taula de Freeverb per a la freqüència real.
|
||||
auto scaledDelay(int reference_delay, int rate) -> int {
|
||||
const long SCALED = std::lround(static_cast<double>(reference_delay) * static_cast<double>(rate) / static_cast<double>(COMB_REFERENCE_RATE));
|
||||
return std::max(1, static_cast<int>(SCALED));
|
||||
}
|
||||
|
||||
// --- Filtres bàsics ---
|
||||
struct Comb {
|
||||
std::vector<float> buf;
|
||||
std::size_t idx{0};
|
||||
float feedback{0.0F};
|
||||
float damp1{0.0F};
|
||||
float damp2{1.0F};
|
||||
float store{0.0F};
|
||||
|
||||
void init(int delay, float fb, float damping) {
|
||||
buf.assign(static_cast<std::size_t>(delay), 0.0F);
|
||||
idx = 0;
|
||||
feedback = fb;
|
||||
damp1 = damping;
|
||||
damp2 = 1.0F - damping;
|
||||
store = 0.0F;
|
||||
}
|
||||
auto tick(float in) -> float {
|
||||
const float OUT = buf[idx];
|
||||
store = (OUT * damp2) + (store * damp1);
|
||||
buf[idx] = in + (store * feedback);
|
||||
idx = (idx + 1) % buf.size();
|
||||
return OUT;
|
||||
}
|
||||
};
|
||||
|
||||
struct Allpass {
|
||||
std::vector<float> buf;
|
||||
std::size_t idx{0};
|
||||
|
||||
void init(int delay) {
|
||||
buf.assign(static_cast<std::size_t>(delay), 0.0F);
|
||||
idx = 0;
|
||||
}
|
||||
auto tick(float in) -> float {
|
||||
const float BUFOUT = buf[idx];
|
||||
const float OUT = -in + BUFOUT;
|
||||
buf[idx] = in + (BUFOUT * 0.5F);
|
||||
idx = (idx + 1) % buf.size();
|
||||
return OUT;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
auto applyEcho(const Ja::Sound& src, const Ja::EchoParams& params) -> std::optional<ProcessedSound> {
|
||||
std::vector<float> left;
|
||||
std::vector<float> right;
|
||||
if (!decodeToStereoFloat(src, left, right)) { return std::nullopt; }
|
||||
|
||||
const int RATE = src.spec.freq;
|
||||
const int DELAY_SAMPLES = std::max(1, static_cast<int>(std::lround(params.delay_ms * 0.001F * static_cast<float>(RATE))));
|
||||
const auto TAIL_SAMPLES = static_cast<std::size_t>(std::lround(ECHO_TAIL_MS * 0.001F * static_cast<float>(RATE)));
|
||||
|
||||
const float FEEDBACK = std::clamp(params.feedback, 0.0F, 0.95F);
|
||||
const float WET = std::clamp(params.wet, 0.0F, 1.0F);
|
||||
const float DRY = 1.0F - WET;
|
||||
|
||||
const std::size_t INPUT_LEN = left.size();
|
||||
const std::size_t TOTAL_LEN = INPUT_LEN + TAIL_SAMPLES;
|
||||
|
||||
std::vector<float> ring_l(static_cast<std::size_t>(DELAY_SAMPLES), 0.0F);
|
||||
std::vector<float> ring_r(static_cast<std::size_t>(DELAY_SAMPLES), 0.0F);
|
||||
std::size_t cursor = 0;
|
||||
|
||||
std::vector<float> out_l(TOTAL_LEN);
|
||||
std::vector<float> out_r(TOTAL_LEN);
|
||||
|
||||
for (std::size_t i = 0; i < TOTAL_LEN; ++i) {
|
||||
const float IN_L = (i < INPUT_LEN) ? left[i] : 0.0F;
|
||||
const float IN_R = (i < INPUT_LEN) ? right[i] : 0.0F;
|
||||
|
||||
const float DELAYED_L = ring_l[cursor];
|
||||
const float DELAYED_R = ring_r[cursor];
|
||||
|
||||
out_l[i] = (DRY * IN_L) + (WET * DELAYED_L);
|
||||
out_r[i] = (DRY * IN_R) + (WET * DELAYED_R);
|
||||
|
||||
ring_l[cursor] = IN_L + (DELAYED_L * FEEDBACK);
|
||||
ring_r[cursor] = IN_R + (DELAYED_R * FEEDBACK);
|
||||
cursor = (cursor + 1) % static_cast<std::size_t>(DELAY_SAMPLES);
|
||||
}
|
||||
|
||||
ProcessedSound result;
|
||||
result.spec = SDL_AudioSpec{.format = SDL_AUDIO_S16, .channels = 2, .freq = RATE};
|
||||
encodeStereoS16(out_l, out_r, result.bytes);
|
||||
return result;
|
||||
}
|
||||
|
||||
auto applyReverb(const Ja::Sound& src, const Ja::ReverbParams& params) -> std::optional<ProcessedSound> {
|
||||
std::vector<float> left;
|
||||
std::vector<float> right;
|
||||
if (!decodeToStereoFloat(src, left, right)) { return std::nullopt; }
|
||||
|
||||
const int RATE = src.spec.freq;
|
||||
const auto TAIL_SAMPLES = static_cast<std::size_t>(std::lround(REVERB_TAIL_MS * 0.001F * static_cast<float>(RATE)));
|
||||
|
||||
const float ROOM_SIZE = std::clamp(params.room_size, 0.0F, 1.0F);
|
||||
const float DAMPING = std::clamp(params.damping, 0.0F, 1.0F);
|
||||
const float WET = std::clamp(params.wet, 0.0F, 1.0F);
|
||||
const float DRY = 1.0F - WET;
|
||||
|
||||
const float FEEDBACK = (ROOM_SIZE * SCALE_ROOM) + OFFSET_ROOM; // 0.7..0.98
|
||||
const float DAMP1 = DAMPING * SCALE_DAMP; // 0..0.4
|
||||
|
||||
// Inicialitza els 8 comb filters per cada canal i els 4 allpass.
|
||||
std::array<Comb, 8> comb_l;
|
||||
std::array<Comb, 8> comb_r;
|
||||
for (std::size_t i = 0; i < COMB_DELAYS_L.size(); ++i) {
|
||||
comb_l[i].init(scaledDelay(COMB_DELAYS_L[i], RATE), FEEDBACK, DAMP1);
|
||||
comb_r[i].init(scaledDelay(COMB_DELAYS_L[i] + STEREO_SPREAD, RATE), FEEDBACK, DAMP1);
|
||||
}
|
||||
std::array<Allpass, 4> allpass_l;
|
||||
std::array<Allpass, 4> allpass_r;
|
||||
for (std::size_t i = 0; i < ALLPASS_DELAYS_L.size(); ++i) {
|
||||
allpass_l[i].init(scaledDelay(ALLPASS_DELAYS_L[i], RATE));
|
||||
allpass_r[i].init(scaledDelay(ALLPASS_DELAYS_L[i] + STEREO_SPREAD, RATE));
|
||||
}
|
||||
|
||||
const std::size_t INPUT_LEN = left.size();
|
||||
const std::size_t TOTAL_LEN = INPUT_LEN + TAIL_SAMPLES;
|
||||
std::vector<float> out_l(TOTAL_LEN);
|
||||
std::vector<float> out_r(TOTAL_LEN);
|
||||
|
||||
for (std::size_t i = 0; i < TOTAL_LEN; ++i) {
|
||||
const float IN_L = (i < INPUT_LEN) ? left[i] : 0.0F;
|
||||
const float IN_R = (i < INPUT_LEN) ? right[i] : 0.0F;
|
||||
const float MONO_INPUT = (IN_L + IN_R) * FIXED_GAIN;
|
||||
|
||||
// 8 comb filters en paral·lel, sumats.
|
||||
float wet_l = 0.0F;
|
||||
float wet_r = 0.0F;
|
||||
for (std::size_t k = 0; k < comb_l.size(); ++k) {
|
||||
wet_l += comb_l[k].tick(MONO_INPUT);
|
||||
wet_r += comb_r[k].tick(MONO_INPUT);
|
||||
}
|
||||
// 4 allpass en sèrie.
|
||||
for (std::size_t k = 0; k < allpass_l.size(); ++k) {
|
||||
wet_l = allpass_l[k].tick(wet_l);
|
||||
wet_r = allpass_r[k].tick(wet_r);
|
||||
}
|
||||
|
||||
out_l[i] = (DRY * IN_L) + (WET * wet_l);
|
||||
out_r[i] = (DRY * IN_R) + (WET * wet_r);
|
||||
}
|
||||
|
||||
ProcessedSound result;
|
||||
result.spec = SDL_AudioSpec{.format = SDL_AUDIO_S16, .channels = 2, .freq = RATE};
|
||||
encodeStereoS16(out_l, out_r, result.bytes);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace AudioEffects
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
// Forward-declaració per no incloure jail_audio.hpp (cicle d'inclusió: aquest
|
||||
// header viu sota els params declarats a jail_audio.hpp, i alhora jail_audio
|
||||
// usa applyEcho/applyReverb).
|
||||
namespace Ja {
|
||||
struct Sound;
|
||||
struct EchoParams;
|
||||
struct ReverbParams;
|
||||
} // namespace Ja
|
||||
|
||||
// Processadors d'efectes per a sons puntuals. Reben un Ja::Sound (qualsevol
|
||||
// format suportat pel decodificador WAV: U8/S16, mono o estèreo) i tornen un
|
||||
// buffer PCM en S16 + el seu spec, llest per empenyer a un SDL_AudioStream.
|
||||
//
|
||||
// El buffer de sortida inclou la cua (decay) generada per l'efecte: per al
|
||||
// reverb, fins a 1500 ms; per a l'eco, fins a 800 ms. Aquests caps eviten
|
||||
// allargar indefinidament la reproducció quan els paràmetres reinjecten molt.
|
||||
//
|
||||
// Si el format del so d'origen no es pot processar, retornen std::nullopt
|
||||
// (el caller ha de fer fallback a reproducció seca).
|
||||
namespace AudioEffects {
|
||||
|
||||
struct ProcessedSound {
|
||||
std::vector<std::uint8_t> bytes; // PCM S16 entrellaçat (LRLRLR... si stereo)
|
||||
SDL_AudioSpec spec; // Format/canals/freqüència del buffer
|
||||
};
|
||||
|
||||
[[nodiscard]] auto applyEcho(const Ja::Sound& src, const Ja::EchoParams& params) -> std::optional<ProcessedSound>;
|
||||
[[nodiscard]] auto applyReverb(const Ja::Sound& src, const Ja::ReverbParams& params) -> std::optional<ProcessedSound>;
|
||||
|
||||
} // namespace AudioEffects
|
||||
@@ -4,10 +4,15 @@
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
// Implementació completa de stb_vorbis en aquest TU. HEADER_ONLY es queda
|
||||
// indefinit aquí perquè el .c aporti les definicions. La resta del projecte
|
||||
// només veu el forward-decl de jail_audio.hpp.
|
||||
#include "core/audio/audio_effects.hpp"
|
||||
|
||||
// Només declaracions de stb_vorbis: STB_VORBIS_HEADER_ONLY omet el bloc
|
||||
// d'implementació. Les definicions les aporta source/external/stb_vorbis_impl.cpp
|
||||
// (TU aïllat perquè clang-analyzer no dispari fals positius al nostre codi).
|
||||
#define STB_VORBIS_HEADER_ONLY
|
||||
// clang-format off
|
||||
// NOLINTNEXTLINE(bugprone-suspicious-include)
|
||||
#include "external/stb_vorbis.c"
|
||||
@@ -396,13 +401,17 @@ namespace Ja {
|
||||
}
|
||||
|
||||
void Engine::stopChannel(const int channel) {
|
||||
forEachTargetChannel(channel, [](Channel& ch) {
|
||||
forEachTargetChannel(channel, [this](Channel& ch) {
|
||||
if (ch.state != ChannelState::FREE) {
|
||||
if (ch.stream != nullptr) { SDL_DestroyAudioStream(ch.stream); }
|
||||
ch.stream = nullptr;
|
||||
ch.state = ChannelState::FREE;
|
||||
ch.pos = 0;
|
||||
ch.sound = nullptr;
|
||||
if (ch.has_effect) {
|
||||
ch.has_effect = false;
|
||||
if (effect_channels_active_ > 0) { --effect_channels_active_; }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -437,6 +446,55 @@ namespace Ja {
|
||||
}
|
||||
}
|
||||
|
||||
auto Engine::playProcessedOnFreeChannel(const std::vector<std::uint8_t>& bytes, const SDL_AudioSpec& spec, const int group) -> int {
|
||||
// El cap de canals amb efecte es valida abans de reservar slot —
|
||||
// així evitem crear i destruir un stream només per descartar el play.
|
||||
if (effect_channels_active_ >= MAX_EFFECT_CHANNELS) { return -1; }
|
||||
|
||||
int channel = 0;
|
||||
while (channel < MAX_SIMULTANEOUS_CHANNELS && channels_[channel].state != ChannelState::FREE) { ++channel; }
|
||||
if (channel == MAX_SIMULTANEOUS_CHANNELS) { channel = 0; }
|
||||
|
||||
stopChannel(channel);
|
||||
|
||||
// El stream es crea contra l'spec del buffer processat (S16, ...)
|
||||
// perquè SDL faci el resampling cap a audio_spec_ del device.
|
||||
channels_[channel].stream = SDL_CreateAudioStream(&spec, &audio_spec_);
|
||||
if (channels_[channel].stream == nullptr) {
|
||||
std::fprintf(stderr, "Ja::Engine::playProcessedOnFreeChannel: Failed to create audio stream!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
channels_[channel].sound = nullptr; // El buffer no és propietat de cap Ja::Sound.
|
||||
channels_[channel].times = 0;
|
||||
channels_[channel].pos = 0;
|
||||
const int CLAMPED_GROUP = (group >= 0 && group < MAX_GROUPS) ? group : 0;
|
||||
channels_[channel].group = CLAMPED_GROUP;
|
||||
channels_[channel].state = ChannelState::PLAYING;
|
||||
channels_[channel].has_effect = true;
|
||||
++effect_channels_active_;
|
||||
|
||||
SDL_PutAudioStreamData(channels_[channel].stream, bytes.data(), static_cast<int>(bytes.size()));
|
||||
SDL_SetAudioStreamGain(channels_[channel].stream, sound_volume_[CLAMPED_GROUP]);
|
||||
SDL_BindAudioStream(sdl_audio_device_, channels_[channel].stream);
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
auto Engine::playSoundWithEcho(const Sound* sound, const EchoParams& params, const int group) -> int {
|
||||
if (sound == nullptr) { return -1; }
|
||||
auto processed = AudioEffects::applyEcho(*sound, params);
|
||||
if (!processed) { return -1; }
|
||||
return playProcessedOnFreeChannel(processed->bytes, processed->spec, group);
|
||||
}
|
||||
|
||||
auto Engine::playSoundWithReverb(const Sound* sound, const ReverbParams& params, const int group) -> int {
|
||||
if (sound == nullptr) { return -1; }
|
||||
auto processed = AudioEffects::applyReverb(*sound, params);
|
||||
if (!processed) { return -1; }
|
||||
return playProcessedOnFreeChannel(processed->bytes, processed->spec, group);
|
||||
}
|
||||
|
||||
// --- Factories i destructors (permanents) ---
|
||||
|
||||
auto loadMusic(const Uint8* buffer, Uint32 length) -> Music* {
|
||||
|
||||