diff --git a/CMakeLists.txt b/CMakeLists.txt
index e086d745..6c6b6d9c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -55,10 +55,10 @@ target_include_directories(forgecore_slice PUBLIC
)
# "-dev" until the release is tagged (docs/RELEASE.md step 5 clears it)
-set(FORGE_VERSION_SUFFIX "-rc.2")
+set(FORGE_VERSION_SUFFIX "-dev")
add_compile_definitions(ALBION_VERSION="${PROJECT_VERSION}${FORGE_VERSION_SUFFIX}")
add_library(fableforge STATIC src/terrainexport.cpp src/foliageexport.cpp src/thingsexport.cpp src/stbterrain.cpp src/effects.cpp src/leveledit.cpp src/vanilla_props.cpp src/worldedit.cpp src/overworld.cpp src/stbrelocate.cpp src/lodbake.cpp src/stitch.cpp src/livelink.cpp src/backups.cpp src/navlines.cpp src/worldtiles.cpp
- src/stbcompact.cpp src/presets.cpp src/gtg.cpp src/texturebrowse.cpp src/meshimport.cpp src/modpack.cpp)
+ src/stbcompact.cpp src/presets.cpp src/gtg.cpp src/texturebrowse.cpp src/meshimport.cpp src/modpack.cpp src/skinnedexport.cpp)
target_include_directories(fableforge PUBLIC src)
target_link_libraries(fableforge PUBLIC forgecore_slice)
target_sources(fableforge PRIVATE src/particlepreview.cpp)
@@ -99,6 +99,20 @@ add_test(NAME fableforge_defdecode_tests COMMAND fableforge_defdecode_tests)
add_executable(fableforge_fmplinks_tests tests/test_fmplinks.cpp)
target_link_libraries(fableforge_fmplinks_tests PRIVATE forgecore_slice)
add_test(NAME fableforge_fmplinks_tests COMMAND fableforge_fmplinks_tests)
+add_executable(fableforge_boneconfig_tests tests/test_boneconfig.cpp)
+target_link_libraries(fableforge_boneconfig_tests PRIVATE forgecore_slice)
+add_test(NAME fableforge_boneconfig_tests COMMAND fableforge_boneconfig_tests)
+add_executable(fableforge_creaturepose_tests tests/test_creaturepose.cpp)
+target_link_libraries(fableforge_creaturepose_tests PRIVATE forgecore_slice)
+add_test(NAME fableforge_creaturepose_tests COMMAND fableforge_creaturepose_tests)
+add_executable(fableforge_creatureassembly_tests tests/test_creatureassembly.cpp)
+target_link_libraries(fableforge_creatureassembly_tests PRIVATE forgecore_slice)
+add_test(NAME fableforge_creatureassembly_tests COMMAND fableforge_creatureassembly_tests)
+add_executable(fableforge_characterfit_tests tests/test_characterfit.cpp)
+target_link_libraries(fableforge_characterfit_tests PRIVATE forgecore_slice)
+add_test(NAME fableforge_characterfit_tests COMMAND fableforge_characterfit_tests)
+add_executable(fableforge_skinnedexport_tests tests/test_skinnedexport.cpp)
+target_link_libraries(fableforge_skinnedexport_tests PRIVATE fableforge)
add_executable(fableforge_defrelocate_tests tests/test_defrelocate.cpp)
target_link_libraries(fableforge_defrelocate_tests PRIVATE forgecore_slice)
add_test(NAME fableforge_defrelocate_tests COMMAND fableforge_defrelocate_tests)
@@ -108,6 +122,12 @@ add_test(NAME fableforge_defnamespace_tests COMMAND fableforge_defnamespace_test
add_executable(fableforge_binclone_tests tests/test_binclone.cpp)
target_link_libraries(fableforge_binclone_tests PRIVATE forgecore_slice)
add_test(NAME fableforge_binclone_tests COMMAND fableforge_binclone_tests)
+add_executable(fableforge_creatureclone_tests tests/test_creatureclone.cpp)
+target_link_libraries(fableforge_creatureclone_tests PRIVATE forgecore_slice)
+add_test(NAME fableforge_creatureclone_tests COMMAND fableforge_creatureclone_tests)
+add_executable(fableforge_creaturevisualclone_tests tests/test_creaturevisualclone.cpp)
+target_link_libraries(fableforge_creaturevisualclone_tests PRIVATE fableforge)
+add_test(NAME fableforge_creaturevisualclone_tests COMMAND fableforge_creaturevisualclone_tests)
add_executable(fableforge_binwrite_tests tests/test_binwrite.cpp)
target_link_libraries(fableforge_binwrite_tests PRIVATE forgecore_slice)
add_test(NAME fableforge_binwrite_tests COMMAND fableforge_binwrite_tests)
@@ -122,6 +142,9 @@ target_link_libraries(fableforge_meshbounds_tests PRIVATE forgecore_slice)
add_test(NAME fableforge_meshbounds_tests COMMAND fableforge_meshbounds_tests)
add_executable(fableforge_meshskeleton_tests tests/test_meshskeleton.cpp)
target_link_libraries(fableforge_meshskeleton_tests PRIVATE forgecore_slice)
+add_executable(fableforge_skinnedcompose_tests tests/test_skinnedcompose.cpp)
+target_link_libraries(fableforge_skinnedcompose_tests PRIVATE forgecore_slice)
+add_test(NAME fableforge_skinnedcompose_tests COMMAND fableforge_skinnedcompose_tests)
add_executable(fableforge_animation_tests tests/test_animation.cpp)
target_link_libraries(fableforge_animation_tests PRIVATE forgecore_slice)
add_test(NAME fableforge_animation_tests COMMAND fableforge_animation_tests)
@@ -152,6 +175,9 @@ add_test(NAME fableforge_tests COMMAND fableforge_tests)
add_executable(fableforge_meshinput_tests tests/test_meshinput.cpp)
target_link_libraries(fableforge_meshinput_tests PRIVATE fableforge)
add_test(NAME fableforge_meshinput_tests COMMAND fableforge_meshinput_tests)
+add_executable(fableforge_skinnedinput_tests tests/test_skinnedinput.cpp)
+target_link_libraries(fableforge_skinnedinput_tests PRIVATE fableforge)
+add_test(NAME fableforge_skinnedinput_tests COMMAND fableforge_skinnedinput_tests)
add_executable(fableforge_stage_tests tests/test_stage.cpp)
target_link_libraries(fableforge_stage_tests PRIVATE forgecore_slice)
add_test(NAME fableforge_stage_tests COMMAND fableforge_stage_tests)
@@ -200,6 +226,9 @@ add_test(NAME fableforge_lut_tests COMMAND fableforge_lut_tests)
add_executable(fableforge_unicode_tests tests/test_unicode.cpp)
target_link_libraries(fableforge_unicode_tests PRIVATE forgecore_slice)
add_test(NAME fableforge_unicode_tests COMMAND fableforge_unicode_tests)
+add_executable(fableforge_definitionfield_tests tests/test_definitionfield.cpp)
+target_link_libraries(fableforge_definitionfield_tests PRIVATE fableforge)
+add_test(NAME fableforge_definitionfield_tests COMMAND fableforge_definitionfield_tests)
add_executable(fableforge_dialoguetext_tests tests/test_dialoguetext.cpp)
target_link_libraries(fableforge_dialoguetext_tests PRIVATE forgecore_slice)
@@ -234,6 +263,8 @@ if(WIN32)
${IMGUI_DIR}/imgui_impl_dx11.cpp
)
target_include_directories(albion_imgui PUBLIC ${IMGUI_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/vendor/ImGuizmo)
+ # Keep supplementary Unicode input intact in the GUI and all ImGui consumers.
+ target_compile_definitions(albion_imgui PUBLIC IMGUI_USE_WCHAR32)
target_link_libraries(albion_imgui PUBLIC d3d11 dxgi d3dcompiler dwmapi ole32 shell32 uuid)
add_executable(FableForge WIN32
@@ -323,3 +354,14 @@ add_test(NAME fableforge_setheight_tests COMMAND fableforge_setheight_tests)
add_executable(fableforge_ownedgraph_tests tests/test_ownedgraph.cpp)
target_link_libraries(fableforge_ownedgraph_tests PRIVATE fableforge)
add_test(NAME fableforge_ownedgraph_tests COMMAND fableforge_ownedgraph_tests)
+
+# Long-path opt-in for the shipped executables; Windows honours it when
+# LongPathsEnabled is set. Staged bank writes nest several folders under the install.
+if(WIN32 AND NOT MSVC)
+ enable_language(RC)
+ foreach(target forge FableForge forge-tools)
+ if(TARGET ${target})
+ target_sources(${target} PRIVATE cmake/longpath.rc)
+ endif()
+ endforeach()
+endif()
diff --git a/cmake/longpath.manifest b/cmake/longpath.manifest
new file mode 100644
index 00000000..43ca563f
--- /dev/null
+++ b/cmake/longpath.manifest
@@ -0,0 +1,10 @@
+
+
+
+
+
+ true
+
+
+
diff --git a/cmake/longpath.rc b/cmake/longpath.rc
new file mode 100644
index 00000000..99ceda90
--- /dev/null
+++ b/cmake/longpath.rc
@@ -0,0 +1,2 @@
+// RT_MANIFEST (24), resource 1: the executable's application manifest.
+1 24 "longpath.manifest"
diff --git a/docs/AUTOMATION.md b/docs/AUTOMATION.md
index 5b936fe7..f7dfa17f 100644
--- a/docs/AUTOMATION.md
+++ b/docs/AUTOMATION.md
@@ -501,7 +501,8 @@ Settings persistence is disabled under `--auto` so runs are deterministic.
| `set_entrance` | the map's region entrance (FinalAlbion.gtg, this map's WLD slot) at the view centre, facing the camera (state `entrance` = `x,y,h` or `-`; widget `btn_set_entrance`) |
| `preset_place ` / `preset_save ` | place a preset (shipped `presets/` or the user folder) at the view centre as a selected group; save the selection as a user preset (state `presets` = how many are listed) |
| `select_toggle ` / `select_added` / `copy` / `paste` | Ctrl+click a thing into/out of the selection; select the first thing the Changes list reports as added; copy the selection as a fragment; paste it at the view centre (state `selection_count`) |
-| `place [scriptname]` | place a new thing at the camera focus, on the ground (`CREATURE_*` as the retail AICreature block) |
+| `place [scriptname]` | place a new thing at the camera focus, on the ground, using its compiled class and validated runtime component profile |
+| `place_refused [scriptname]` | require placement to refuse; malformed Creature profiles must leave the document and recovery files unchanged |
| `set place_def ` / `place_at ` | choose the palette definition and place it at viewport-relative pointed terrain coordinates; Shift+click tests the actual input path |
| `snapshot_frame` / `assert_selected_moved_xy` / `assert_selected_frame_same` / `assert_ground_offset_same` | capture a selected frame, then check XY movement, exact frame restoration, or preserved height above terrain |
| `ground_place_carry.txt` | synthetic Shift+click placement, plain and Ctrl+Shift ground drag, Ctrl+D cursor carry, grouped undo, fixed height, lock refusal and Escape cancellation |
@@ -581,7 +582,9 @@ Settings persistence is disabled under `--auto` so runs are deterministic.
| `assert_file_contains ` | the file exists and contains the text (the rest of the line, e.g. `ScriptName UiNamedBarrel;`) |
| `assert_state ` | see `dump_state` for keys |
| `assert_state_min ` / `assert_state_max ` | a numeric state key is at least / at most n (e.g. `texture_thumbs_made`, the Textures list thumbnails decoded so far) |
-| `assert_widget ` | the widget was drawn this frame |
+| `assert_widget ` | the widget has been registered (registrations persist across frames) |
+| `clear_widget_registry` | clear retained widget rectangles; wait for redraw before checking current presence |
+| `assert_widget_absent ` | the widget is not registered; clear the registry and wait for redraw first to test current absence |
| `assert_log ` | some app log line contains the text (background job notes, e.g. `1 stitched`) |
| `world_tab 0\|1`, `world_select