Compare commits

..
Author SHA1 Message Date
phobos2077 b8cf5afd3c Inventory filter POC 2026-05-01 22:49:15 +02:00
phobos2077 61b9b38e97 Merge branch 'f2ce-main' into feature/game-config-from-exe
# Conflicts:
#	README.md
2026-05-01 20:39:03 +02:00
phobos2077 ec79058ab8 Rename fallout2.cfg in repo 2026-05-01 20:07:02 +02:00
phobos2077 88b322e40b Review fixes 2026-05-01 20:05:15 +02:00
Mike Klaas 7d1c8e7eea Bundle ce.dat into release packages, and update README (#425) 2026-05-01 09:59:46 -07:00
Vlad K a7de228978 frm2png fix unable to convert most FRM's (#426)
* frm2png: fix incorrect byte order detection logic and size check

* Remove unnecessary little endian support

* Add frame size check matching engine logic

* Zero frame size condition reverted

* Tiny fix
2026-05-01 16:49:51 +00:00
github-actions[bot] d873cd0ec3 chore: auto-format with clang-format 2026-05-01 16:02:33 +00:00
Vlad K 6c728ddc3d Migrate once, overwrite values 2026-05-01 17:59:16 +02:00
Vlad K 3cdab90b2d debug log buffering to catch early logs, const char* for debug fns 2026-05-01 17:28:09 +02:00
Vlad K 18f7e425b4 game cfg follows EXE name and auto-migrates from fallout2.cfg 2026-05-01 17:25:42 +02:00
Vlad Kandgithub-actions[bot] 3aa6c12244 New concepts for easier UI coding (using game_dialog.cc as a test bed) (#424)
* game_dialog.cc: code debloat, simpler partial cleanup via RAII, fix dialog button during scroll anim

* game_dialog.cc: get rid of awkward button FRM locking using new buttonCreateWithFrm

* chore: auto-format with clang-format

* Code review fixes

* chore: auto-format with clang-format

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-05-01 12:09:50 +02:00
Mike Klaas afdd110d60 2 column inventory and loot (#413)
Also, fix jesse container bug
2026-04-30 20:35:40 -07:00
Mike Klaas 2f491fa6d5 HOOK_EXPLOSIVETIMER (#423) 2026-04-29 20:37:43 -07:00
tectiv3 4382ec3305 iOS/iPad: touch gestures, HUD tap-through, and quick-actions toolbar (#377)
* iOS: add touch gestures, HUD tap-through, quick-actions toolbar

Makes fallout2-ce playable on iPad without a keyboard, while leaving
non-iOS builds unchanged (all additions are TARGET_OS_IOS-guarded).

Touch gestures (src/mouse.cc):
- 3-finger swipe down → ESC
- 4-finger long-press → F6 (quicksave)
- 3-finger long-press → hold LShift (FO2tweaks highlighting). Uses
  SDL_PushEvent so sfall's SDL_GetKeyboardState-based key_pressed()
  sees the held key, not just the engine's own kb state.

HUD ergonomics (src/mouse.cc):
- Taps on interface-bar buttons inject the button's keyCode directly
  so the cursor stays put. Taps on belt chrome are consumed rather
  than teleporting the cursor to inert pixels.
- Two-finger taps on belt buttons → right-click handler.

Gameplay cursor (src/game_mouse.cc):
- On iPad, keep GAME_MOUSE_MODE_MOVE in relative (trackpad) mode so
  screen taps don't teleport the cursor during combat. UI screens
  that explicitly enable touchscreen mode are unaffected.

Quick-actions toolbar (src/platform/ios/quick_toolbar.{cc,h}):
- Optional iOS-only HUD strip above the belt with skill shortcuts
  and end-turn. Header provides no-op stubs for non-iOS builds so
  interface.cc can call it unconditionally.
- game.cc refactor: skilldex result handling factored into
  gameHandleSkilldexResult() so the toolbar and the keyboard
  shortcut share one path.

README: iPad controls section.

* iOS: add quick toolbar visibility preference

Adds a ui.quick_toolbar_visible config flag (default true)

* iOS: skip HUD tap-through when interface bar is hidden

When the interface bar window is hidden (e.g. on the world map),
windowGetRect still returns its rect, causing taps in that screen
area to be consumed as overHud even though there's nothing to interact
with. Check WINDOW_HIDDEN before treating the rect as active HUD.

Addresses Copilot review comment on PR #377.

* iOS: fix dialog options near bottom unclickable on iPad

Enable touchscreen mode for dialog UI so taps reach dialog option
buttons directly. The HUD tap interception in mouse.cc was consuming
taps that landed inside the interface bar rect, even when a dialog
window overlapped that region. Dialog options near the bottom of the
screen were silently swallowed by the belt chrome catch-all.

Skip HUD tap interception when touchscreen mode is active, since a UI
screen (dialog, inventory, skilldex, etc.) owns input in that state.
Add touch_get_touchscreen_mode() getter to support the check.

* iOS: add pipboy touch exception

* Apply fixes from review

* Change quick toolbar default visibility to false

* Add touch mode to character selector

update readme and simplify switch condition
2026-04-28 23:20:15 -07:00
Mike Klaas b012e62e32 ce-frm2png tool (#419) 2026-04-28 22:55:29 -07:00
Mike Klaas 0f69c50df0 Implement HOOK_AMMOCOST (#421) 2026-04-28 22:36:50 -07:00
Mike Klaasandgithub-actions[bot] 1e387beebd Implement HOOK_RESTTIMER (#417)
* Implement HOOK_RESTTIMER

Et tu + other mods use this

* PR comments + missing doc for ce-dat-tool

* chore: auto-format with clang-format

* document timer wrap

* cleanups

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-26 21:32:49 -07:00
c8a905ea14 Expanded 4-slot barter UI from sfall + support FrmImage locking cached art by file name (#414)
* Expanded barter window from sfall

* Refactor: FrmImage class to lock based on file name, refactor code from last commit

* Soft limit on named art cache size

* Support localized paths for art loaded by name

* Fix visual issue from original sfall implementation where barter window were scrolling down incorrectly + bufToBuf const correctness

* Fix build error and extra destructor call when adding NamedCacheEntry

* Review fixes

* Update src/art.cc

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Review issues fixed, window made transparent to remove black area below the overlap during scroll down

* chore: auto-format with clang-format

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-26 00:03:14 +02:00
Mike Klaas 6255f49d6a Avoid recursive sfall tap_key hooks (#415)
* Avoid recursive sfall tap_key hooks

* PR cleanup
2026-04-25 14:08:59 -07:00
Mike Klaas c3fa7107f9 Rename fallout2-dat to ce-dat-tool (#416)
This is a generic tool that works with FO1 and FO2 archives
2026-04-25 10:53:43 -07:00
Mike Klaas 2b078f48a9 Implement HOOK_AFTERHITROLL (#411)
* Implement HOOK_AFTERHITROLL
2026-04-25 00:38:04 +00:00
Vlad K 10ebd28a4d Fix f2_res migration never working (#410)
* Fix f2_res migration never working

* Remove config checker usage in game_config.cc
2026-04-24 13:05:50 +00:00
77 changed files with 4074 additions and 1546 deletions
+6 -1
View File
@@ -6,10 +6,15 @@ on:
push:
branches: [main]
permissions:
contents: read
jobs:
format:
runs-on: ubuntu-latest
if: github.actor != 'github-actions[bot]' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -39,4 +44,4 @@ jobs:
echo "No formatting changes"
fi
+6 -2
View File
@@ -8,11 +8,16 @@ on:
- opened
- synchronize
permissions:
contents: read
jobs:
build_webassembly_version:
name: Build webassembly version
runs-on: ubuntu-latest
permissions: write-all
permissions:
contents: write
deployments: write
steps:
- name: start deployment
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
@@ -138,4 +143,3 @@ jobs:
deployment_id: ${{ steps.deployment.outputs.deployment_id }}
ref: ${{ github.event.pull_request.head.sha }}
env_url: https://${{ github.repository_owner }}.github.io/fallout2-ce/PR-${{ github.event.pull_request.number }}/
+101 -20
View File
@@ -9,6 +9,9 @@ on:
- opened
- synchronize
permissions:
contents: read
defaults:
run:
shell: bash
@@ -35,6 +38,9 @@ jobs:
name: Code format check
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
steps:
- name: Clone
@@ -71,6 +77,7 @@ jobs:
name: Android
runs-on: ubuntu-latest
needs: [ce-dat]
steps:
- name: Clone
@@ -104,21 +111,38 @@ jobs:
cd os/android
./gradlew assembleDebug
- name: Change apk file name
- name: Download ce.dat
uses: actions/download-artifact@v4
with:
name: ce-dat
path: out/dist
- name: Package
run: |
mv os/android/app/build/outputs/apk/debug/app-debug.apk os/android/app/build/outputs/apk/debug/fallout2-ce.apk
mkdir -p out/dist/fallout2-ce-android-debug
cp os/android/app/build/outputs/apk/debug/app-debug.apk out/dist/fallout2-ce-android-debug/fallout2-ce.apk
cp out/dist/ce.dat out/dist/fallout2-ce-android-debug/ce.dat
cd out/dist
zip -r fallout2-ce-android-debug.zip fallout2-ce-android-debug
- name: Upload
uses: actions/upload-artifact@v4
with:
name: fallout2-ce-debug.apk
path: os/android/app/build/outputs/apk/debug/fallout2-ce.apk
name: fallout2-ce-android-debug
path: out/dist/fallout2-ce-android-debug.zip
retention-days: 7
ce-dat:
name: Build ce.dat
uses: ./.github/workflows/reusable-build-ce-dat.yml
with:
preset: linux-x64-debug
ios:
name: iOS
runs-on: macos-14
needs: [ce-dat]
steps:
- name: Clone
@@ -143,17 +167,32 @@ jobs:
cd out/build/ios
cpack -C Debug
- name: Download ce.dat
uses: actions/download-artifact@v4
with:
name: ce-dat
path: out/dist
- name: Package
run: |
mkdir -p out/dist/fallout2-ce-ios
cp out/build/ios/fallout2-ce.ipa out/dist/fallout2-ce-ios/fallout2-ce-ios.ipa
cp out/dist/ce.dat out/dist/fallout2-ce-ios/ce.dat
cd out/dist
zip -r fallout2-ce-ios.zip fallout2-ce-ios
- name: Upload
uses: actions/upload-artifact@v4
with:
name: fallout2-ce.ipa
path: out/build/ios/fallout2-ce.ipa
name: fallout2-ce-ios
path: out/dist/fallout2-ce-ios.zip
retention-days: 7
linux:
name: Linux (${{ matrix.arch }})
runs-on: ubuntu-22.04
needs: [ce-dat]
strategy:
fail-fast: false
@@ -193,21 +232,31 @@ jobs:
run: |
cmake --build --preset linux-${{ matrix.arch }}-debug
- name: Change executable file name
- name: Download ce.dat
uses: actions/download-artifact@v4
with:
name: ce-dat
path: out/dist
- name: Package
run: |
mv out/build/linux-${{ matrix.arch }}-debug/fallout2-ce out/build/linux-${{ matrix.arch }}-debug/fallout2-ce-linux-${{ matrix.arch }}
mkdir -p out/dist/fallout2-ce-linux-${{ matrix.arch }}
cp out/build/linux-${{ matrix.arch }}-debug/fallout2-ce out/dist/fallout2-ce-linux-${{ matrix.arch }}/fallout2-ce
cp out/dist/ce.dat out/dist/fallout2-ce-linux-${{ matrix.arch }}/ce.dat
tar -czvf out/dist/fallout2-ce-linux-${{ matrix.arch }}.tar.gz -C out/dist fallout2-ce-linux-${{ matrix.arch }}
- name: Upload
uses: actions/upload-artifact@v4
with:
name: fallout2-ce-linux-${{ matrix.arch }}
path: out/build/linux-${{ matrix.arch }}-debug/fallout2-ce-linux-${{ matrix.arch }}
path: out/dist/fallout2-ce-linux-${{ matrix.arch }}.tar.gz
retention-days: 7
linux-armhf:
name: Linux (armhf)
runs-on: ubuntu-22.04-arm
needs: [ce-dat]
steps:
- name: Clone
@@ -233,21 +282,31 @@ jobs:
- name: Build
run: cmake --build build
- name: Change executable file name
- name: Download ce.dat
uses: actions/download-artifact@v4
with:
name: ce-dat
path: out/dist
- name: Package
run: |
mv build/fallout2-ce build/fallout2-ce-linux-armhf
mkdir -p out/dist/fallout2-ce-linux-armhf
cp build/fallout2-ce out/dist/fallout2-ce-linux-armhf/fallout2-ce
cp out/dist/ce.dat out/dist/fallout2-ce-linux-armhf/ce.dat
tar -czvf out/dist/fallout2-ce-linux-armhf.tar.gz -C out/dist fallout2-ce-linux-armhf
- name: Upload
uses: actions/upload-artifact@v4
with:
name: fallout2-ce-linux-armhf
path: build/fallout2-ce-linux-armhf
path: out/dist/fallout2-ce-linux-armhf.tar.gz
retention-days: 7
linux-arm64:
name: Linux (arm64)
runs-on: ubuntu-22.04-arm
needs: [ce-dat]
steps:
- name: Clone
@@ -264,15 +323,24 @@ jobs:
- name: Build
run: cmake --build build
- name: Change executable file name
- name: Download ce.dat
uses: actions/download-artifact@v4
with:
name: ce-dat
path: out/dist
- name: Package
run: |
mv build/fallout2-ce build/fallout2-ce-linux-arm64
mkdir -p out/dist/fallout2-ce-linux-arm64
cp build/fallout2-ce out/dist/fallout2-ce-linux-arm64/fallout2-ce
cp out/dist/ce.dat out/dist/fallout2-ce-linux-arm64/ce.dat
tar -czvf out/dist/fallout2-ce-linux-arm64.tar.gz -C out/dist fallout2-ce-linux-arm64
- name: Upload
uses: actions/upload-artifact@v4
with:
name: fallout2-ce-linux-arm64
path: build/fallout2-ce-linux-arm64
path: out/dist/fallout2-ce-linux-arm64.tar.gz
retention-days: 7
macos:
@@ -306,7 +374,7 @@ jobs:
- name: Upload
uses: actions/upload-artifact@v4
with:
name: fallout2-ce-macos.dmg
name: fallout2-ce-macos
path: out/build/macos/Fallout II Community Edition.dmg
retention-days: 7
@@ -314,6 +382,7 @@ jobs:
name: Windows (${{ matrix.arch }})
runs-on: windows-2025
needs: [ce-dat]
strategy:
fail-fast: false
@@ -351,15 +420,25 @@ jobs:
call "%VS_PATH%\Common7\Tools\VsDevCmd.bat" ^
&& cmake --build --preset windows-${{ matrix.arch }}-release
- name: Change executable file name
- name: Download ce.dat
uses: actions/download-artifact@v4
with:
name: ce-dat
path: out/dist
- name: Package
run: |
mv out/build/windows-${{ matrix.arch }}/RelWithDebInfo/fallout2-ce.exe out/build/windows-${{ matrix.arch }}/RelWithDebInfo/fallout2-ce-${{ matrix.arch }}.exe
mkdir -p out/dist/fallout2-ce-windows-${{ matrix.arch }}
cp out/build/windows-${{ matrix.arch }}/RelWithDebInfo/fallout2-ce.exe out/dist/fallout2-ce-windows-${{ matrix.arch }}/fallout2-ce.exe
cp out/dist/ce.dat out/dist/fallout2-ce-windows-${{ matrix.arch }}/ce.dat
cd out/dist
7z a fallout2-ce-windows-${{ matrix.arch }}.zip fallout2-ce-windows-${{ matrix.arch }}
- name: Upload
uses: actions/upload-artifact@v4
with:
name: fallout2-ce-windows-${{ matrix.arch }}
path: out/build/windows-${{ matrix.arch }}/RelWithDebInfo/fallout2-ce-${{ matrix.arch }}.exe
path: out/dist/fallout2-ce-windows-${{ matrix.arch }}.zip
retention-days: 7
@@ -367,7 +446,8 @@ jobs:
name: Release Continuous build
runs-on: ubuntu-latest
needs: [android, ios, linux, linux-armhf, linux-arm64, macos, windows]
permissions: write-all
permissions:
contents: write
steps:
- name: Fetch artifacts
if: ${{ always() && github.ref == 'refs/heads/main' && github.event_name == 'push' }}
@@ -387,6 +467,7 @@ jobs:
- name: Move unpacked artifacts to root folder
if: ${{ always() && github.ref == 'refs/heads/main' && github.event_name == 'push' }}
run: |
rm -rf artifacts/ce-dat
ls -Rlah .
find .
cd artifacts/
@@ -5,6 +5,9 @@ on:
types:
- closed
permissions:
contents: read
jobs:
delete_pr_preview:
name: Delete PR preview from gh-pages
+70 -12
View File
@@ -5,15 +5,27 @@ on:
types:
- published
permissions:
contents: read
defaults:
run:
shell: bash
jobs:
ce-dat:
name: Build ce.dat
uses: ./.github/workflows/reusable-build-ce-dat.yml
with:
preset: linux-x64-release
android:
name: Android
runs-on: ubuntu-latest
needs: [ce-dat]
permissions:
contents: write
steps:
- name: Clone
@@ -47,12 +59,21 @@ jobs:
cd os/android
./gradlew assembleRelease
- name: Download ce.dat
uses: actions/download-artifact@v4
with:
name: ce-dat
path: out/dist
- name: Upload
run: |
cd os/android/app/build/outputs/apk/release
cp app-release.apk fallout2-ce-android.apk
gh release upload ${{ github.event.release.tag_name }} fallout2-ce-android.apk
rm fallout2-ce-android.apk
mkdir -p out/dist/fallout2-ce-android
cp os/android/app/build/outputs/apk/release/app-release.apk out/dist/fallout2-ce-android/fallout2-ce-android.apk
cp out/dist/ce.dat out/dist/fallout2-ce-android/ce.dat
cd out/dist
zip -r fallout2-ce-android.zip fallout2-ce-android
gh release upload ${{ github.event.release.tag_name }} fallout2-ce-android.zip
rm fallout2-ce-android.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -60,6 +81,9 @@ jobs:
name: iOS
runs-on: macos-14
needs: [ce-dat]
permissions:
contents: write
steps:
- name: Clone
@@ -95,12 +119,21 @@ jobs:
cd build
cpack -C RelWithDebInfo
- name: Download ce.dat
uses: actions/download-artifact@v4
with:
name: ce-dat
path: out/dist
- name: Upload
run: |
cd build
cp fallout2-ce.ipa fallout2-ce-ios.ipa
gh release upload ${{ github.event.release.tag_name }} fallout2-ce-ios.ipa
rm fallout2-ce-ios.ipa
mkdir -p out/dist/fallout2-ce-ios
cp build/fallout2-ce.ipa out/dist/fallout2-ce-ios/fallout2-ce-ios.ipa
cp out/dist/ce.dat out/dist/fallout2-ce-ios/ce.dat
cd out/dist
zip -r fallout2-ce-ios.zip fallout2-ce-ios
gh release upload ${{ github.event.release.tag_name }} fallout2-ce-ios.zip
rm fallout2-ce-ios.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -108,6 +141,9 @@ jobs:
name: Linux (${{ matrix.arch }})
runs-on: ubuntu-20.04
needs: [ce-dat]
permissions:
contents: write
strategy:
fail-fast: false
@@ -164,10 +200,18 @@ jobs:
-j $(nproc) \
# EOL
- name: Download ce.dat
uses: actions/download-artifact@v4
with:
name: ce-dat
path: out/dist
- name: Upload
run: |
cd build
tar -czvf fallout2-ce-linux-${{ matrix.arch }}.tar.gz fallout2-ce
mkdir -p out/dist/fallout2-ce-linux-${{ matrix.arch }}
cp build/fallout2-ce out/dist/fallout2-ce-linux-${{ matrix.arch }}/fallout2-ce
cp out/dist/ce.dat out/dist/fallout2-ce-linux-${{ matrix.arch }}/ce.dat
tar -czvf fallout2-ce-linux-${{ matrix.arch }}.tar.gz -C out/dist fallout2-ce-linux-${{ matrix.arch }}
gh release upload ${{ github.event.release.tag_name }} fallout2-ce-linux-${{ matrix.arch }}.tar.gz
rm fallout2-ce-linux-${{ matrix.arch }}.tar.gz
env:
@@ -177,6 +221,8 @@ jobs:
name: macOS
runs-on: macos-11
permissions:
contents: write
steps:
- name: Clone
@@ -244,6 +290,9 @@ jobs:
name: Windows (${{ matrix.arch }})
runs-on: windows-2025
needs: [ce-dat]
permissions:
contents: write
strategy:
fail-fast: false
@@ -281,10 +330,19 @@ jobs:
call "%VS_PATH%\Common7\Tools\VsDevCmd.bat" ^
&& cmake --build --preset windows-${{ matrix.arch }}-release
- name: Download ce.dat
uses: actions/download-artifact@v4
with:
name: ce-dat
path: out/dist
- name: Upload
run: |
cd out/build/windows-${{ matrix.arch }}/RelWithDebInfo
7z a fallout2-ce-windows-${{ matrix.arch }}.zip fallout2-ce.exe
mkdir -p out/dist/fallout2-ce-windows-${{ matrix.arch }}
cp out/build/windows-${{ matrix.arch }}/RelWithDebInfo/fallout2-ce.exe out/dist/fallout2-ce-windows-${{ matrix.arch }}/fallout2-ce.exe
cp out/dist/ce.dat out/dist/fallout2-ce-windows-${{ matrix.arch }}/ce.dat
cd out/dist
7z a fallout2-ce-windows-${{ matrix.arch }}.zip fallout2-ce-windows-${{ matrix.arch }}
gh release upload ${{ github.event.release.tag_name }} fallout2-ce-windows-${{ matrix.arch }}.zip
rm fallout2-ce-windows-${{ matrix.arch }}.zip
env:
@@ -0,0 +1,49 @@
name: Reusable Build ce.dat
on:
workflow_call:
inputs:
preset:
required: true
type: string
permissions:
contents: read
defaults:
run:
shell: bash
jobs:
build-ce-dat:
name: Build ce.dat
runs-on: ubuntu-22.04
steps:
- name: Clone
uses: actions/checkout@v4
- name: Dependencies
run: |
sudo apt update
sudo apt install libsdl2-dev zlib1g-dev
- name: Configure
run: |
cmake --preset ${{ inputs.preset }}
- name: Build ce-dat-tool
run: |
cmake --build --preset ${{ inputs.preset }} --target ce-dat-tool
- name: Create ce.dat
run: |
mkdir -p out/dist
out/build/${{ inputs.preset }}/ce-dat-tool create files/ce.dat out/dist/ce.dat
- name: Upload ce.dat artifact
uses: actions/upload-artifact@v4
with:
name: ce-dat
path: out/dist/ce.dat
retention-days: 7
+53 -7
View File
@@ -315,6 +315,8 @@ if(IOS)
target_sources(${EXECUTABLE_NAME} PUBLIC
"src/platform/ios/paths.h"
"src/platform/ios/paths.mm"
"src/platform/ios/quick_toolbar.h"
"src/platform/ios/quick_toolbar.cc"
)
endif()
@@ -367,6 +369,8 @@ if(APPLE)
"os/macos/fallout2-ce.icns"
)
target_link_libraries(${EXECUTABLE_NAME} "-framework CoreFoundation")
target_sources(${EXECUTABLE_NAME} PUBLIC ${RESOURCES})
set_source_files_properties(${RESOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
@@ -453,7 +457,7 @@ target_link_libraries(${EXECUTABLE_NAME} ${SDL2_LIBRARIES})
target_include_directories(${EXECUTABLE_NAME} PRIVATE ${SDL2_INCLUDE_DIRS})
if((NOT ANDROID) AND (NOT IOS) AND (NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten"))
add_executable(fallout2-dat
add_executable(ce-dat-tool
"tools/dat_tool.cc"
"tools/dat_archive.cc"
"tools/fo1_dat_lzss.cc"
@@ -462,23 +466,65 @@ if((NOT ANDROID) AND (NOT IOS) AND (NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten"))
)
if(APPLE)
set_target_properties(fallout2-dat PROPERTIES
set_target_properties(ce-dat-tool PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO"
)
endif()
target_link_libraries(fallout2-dat
target_link_libraries(ce-dat-tool
fpattern_windows::fpattern_windows
${ZLIB_LIBRARIES}
${SDL2_LIBRARIES}
)
if(WIN32)
target_link_libraries(fallout2-dat winmm)
target_link_libraries(ce-dat-tool winmm)
endif()
target_include_directories(ce-dat-tool PRIVATE "src")
target_include_directories(ce-dat-tool PRIVATE ${ZLIB_INCLUDE_DIRS})
target_include_directories(ce-dat-tool PRIVATE ${SDL2_INCLUDE_DIRS})
add_executable(ce-frm2png
"tools/ce_frm2png.cc"
"src/platform_compat.cc"
"third_party/lodepng/lodepng.cpp"
)
if(APPLE)
set_target_properties(ce-frm2png PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO"
)
endif()
target_include_directories(ce-frm2png PRIVATE "src")
target_include_directories(ce-frm2png PRIVATE "third_party/lodepng")
target_include_directories(ce-frm2png PRIVATE ${SDL2_INCLUDE_DIRS})
target_include_directories(ce-frm2png PRIVATE ${ZLIB_INCLUDE_DIRS})
target_link_libraries(ce-frm2png ${SDL2_LIBRARIES})
target_link_libraries(ce-frm2png ${ZLIB_LIBRARIES})
if(APPLE)
file(GLOB_RECURSE CE_DAT_INPUTS CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/files/ce.dat/*")
set(CE_DAT_RESOURCE_PATH "${CMAKE_CURRENT_BINARY_DIR}/ce.dat")
add_custom_command(
OUTPUT "${CE_DAT_RESOURCE_PATH}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}"
COMMAND $<TARGET_FILE:ce-dat-tool> create "${CMAKE_SOURCE_DIR}/files/ce.dat" "${CE_DAT_RESOURCE_PATH}"
DEPENDS ce-dat-tool ${CE_DAT_INPUTS}
COMMENT "Generating ce.dat for app bundle"
VERBATIM
)
add_custom_target(ce-dat-resource DEPENDS "${CE_DAT_RESOURCE_PATH}")
add_dependencies(${EXECUTABLE_NAME} ce-dat-resource)
target_sources(${EXECUTABLE_NAME} PRIVATE "${CE_DAT_RESOURCE_PATH}")
set_source_files_properties("${CE_DAT_RESOURCE_PATH}" PROPERTIES
GENERATED TRUE
MACOSX_PACKAGE_LOCATION "Resources")
endif()
target_include_directories(fallout2-dat PRIVATE "src")
target_include_directories(fallout2-dat PRIVATE ${ZLIB_INCLUDE_DIRS})
target_include_directories(fallout2-dat PRIVATE ${SDL2_INCLUDE_DIRS})
endif()
if(APPLE)
+37 -8
View File
@@ -55,23 +55,52 @@ TODO: add Visual Studio build instructions. This will likely need a Visual Studi
There is a small command-line archive tool in this repo for inspecting Fallout `.dat` files. It supports both Fallout 2 and Fallout 1 archives.
From the repo root, build it with your normal CMake workflow, targeting `fallout2-dat`:
From the repo root, build it with your normal CMake workflow, targeting `ce-dat-tool`:
`cmake --build out/build/<preset-name> --target fallout2-dat`
`cmake --build out/build/<preset-name> --target ce-dat-tool`
The executable is written to your selected build directory, so the exact path will vary by preset and configuration.
The current tool is read-only. Available commands:
Available commands:
1. `./<BUILD_DIR>/fallout2-dat <archive.dat> list [pattern]`
2. `./<BUILD_DIR>/fallout2-dat <archive.dat> info [pattern]`
3. `./<BUILD_DIR>/fallout2-dat <archive.dat> extract [--lower] <output-dir> [pattern]`
4. `./<BUILD_DIR>/fallout2-dat <archive.dat> cat <entry>`
1. `./<BUILD_DIR>/ce-dat-tool <archive.dat> list [pattern]`
2. `ce-dat-tool <archive.dat> info [pattern]`
3. `ce-dat-tool <archive.dat> extract [--lower] <output-dir> [pattern]`
4. `ce-dat-tool <archive.dat> cat <entry>`
5. `ce-dat-tool create <input-dir> <archive.dat>`
Use `--lower` with `extract` when you want every extracted file and directory name forced to lowercase.
For example:
`fallout2-dat master.dat extract --lower /tmp/fallout2-dat-lower data\\*`
`ce-dat-tool master.dat extract --lower /tmp/ce-dat-tool-lower data\\*`
## `ce-frm2png` CLI Tool
There is also a small FRM conversion tool in this repo for exporting Fallout `.frm` art to `.png`.
From the repo root, build it with your normal CMake workflow, targeting `ce-frm2png`:
`cmake --build out/build/<preset-name> --target ce-frm2png`
The executable is written to your selected build directory, so the exact path will vary by preset and configuration.
Basic usage:
1. `./<BUILD_DIR>/ce-frm2png <input.frm> [output.png]`
2. `./<BUILD_DIR>/ce-frm2png <input.frm> [output.png] --palette <path-to-color.pal>`
3. `./<BUILD_DIR>/ce-frm2png <input.frm> [output.png] --frame <index> --direction <index>`
4. `./<BUILD_DIR>/ce-frm2png - [output.png|-] --palette <path-to-color.pal>`
`-` can be used to take input from stdin (e.g. from `ce-dat-tool cat` or write to stdout).
If `--palette` isn't specified, it will pick up `color.pal` in the .frm's directory or cwd.
Notes:
- The tool expects a raw `color.pal` file. It does not read palettes directly from `master.dat`, so extract `color.pal` first if needed.
- A typical extraction flow is: `ce-dat-tool master.dat extract --lower . 'color.pal'`
- By default palette index `0` becomes transparent in the output PNG. Pass `--opaque` to keep it opaque.
- The current implementation writes RGBA PNGs. It does not preserve the original Fallout palette indices as a palette-indexed PNG.
## Updating SDL
+43 -24
View File
@@ -1,20 +1,22 @@
# Fallout 2 Community Edition
# Fallout 2 Community Engine
Fallout 2 Community Edition is a fully working re-implementation of Fallout 2, with the same original gameplay, engine bugfixes, and some quality of life improvements, that works (mostly) hassle-free on multiple platforms. This is a fork of the original project, which isn't getting regular updates.
Fallout 2 Community Engine is a fully working re-implementation of the Fallout 2 engine, optimized for a hassle-free experience on multiple platforms. It provides high resolution support, quality-of-life improvements, and dozens of bug fixes.
This is a fork of the original Fallout2: CE project, which is no longer getting regular updates.
Popular Fallout 2 total conversion mods are partially supported. Original versions of Nevada and Sonora (that do not rely on extended features provided by Sfall) work. [Fallout 2 Restoration Project](https://github.com/BGforgeNet/Fallout2_Restoration_Project) is supported (in Beta). [Fallout Et Tu](https://github.com/rotators/Fo1in2) and [Olympus 2207](https://olympus2207.com) are not yet supported. Other mods (particularly Resurrection and Yesterday) are not tested.
There is also [Fallout Community Edition](https://github.com/alexbatalov/fallout1-ce) (not affiliated with this fork).
Fallout2: CE has broad (though not total) compatibility with [Sfall](https://github.com/sfall-team/sfall) scripting extensions. Many traditional Fallout mods work out of the box.
For build and contributor notes, see [CONTRIBUTING.md](CONTRIBUTING.md).
There is also [Fallout 1 Community Edition](https://github.com/alexbatalov/fallout1-ce) (not affiliated with this fork).
## Installation
You must own the game to play. Purchase your copy on [GOG](https://www.gog.com/game/fallout_2), [Epic Games](https://store.epicgames.com/p/fallout-2) or [Steam](https://store.steampowered.com/app/38410). Download latest [release](https://github.com/fallout2-ce/fallout2-ce/releases) or build from source.
You *must* own the game to play. Purchase your copy on [GOG](https://www.gog.com/game/fallout_2), [Epic Games](https://store.epicgames.com/p/fallout-2) or [Steam](https://store.steampowered.com/app/38410). Download latest [release](https://github.com/fallout2-ce/fallout2-ce/releases) or build from source.
### Windows
Download and copy `fallout2-ce.exe` to your `Fallout2` folder. It serves as a drop-in replacement for `fallout2.exe`.
[Download](https://github.com/fallout2-ce/fallout2-ce/releases) and unzip into your `Fallout2` folder. Launch the game using `fallout2-ce.exe`.
### Linux
@@ -27,7 +29,7 @@ $ sudo apt install innoextract
$ innoextract ~/Downloads/setup_fallout_2_1.02_gog_v1_\(77792\).exe -d Fallout2
```
- Download the Linux release archive, extract `fallout2-ce`, and copy it into this folder.
- Download the Linux release archive, extract `fallout2-ce` and `ce.dat`, and copy them into this folder.
- Run `./fallout2-ce`.
@@ -61,9 +63,11 @@ $ mv fallout2 /Applications/Fallout2
> **NOTE**: From Android standpoint release and debug builds are different apps. Both apps require their own copy of game assets and have their own savegames. This is intentional. As a gamer just stick with release version and check for updates.
- Use Windows installation as a base - it contains data assets needed to play. Copy `Fallout2` folder to your device, for example to `Downloads`. You need `master.dat`, `critter.dat`, `patch000.dat`, and `data` folder. Watch for file names - keep (or make) them lowercased (see [Configuration](#configuration)).
- Download the Android [release](https://github.com/fallout2-ce/fallout2-ce/releases), which contains an .apk file and `ce.dat`.
- Download `fallout2-ce.apk` and copy it to your device. Open it with file explorer, follow instructions (install from unknown source).
- Use Windows installation as a base - it contains data assets needed to play. Copy `Fallout2` folder to your device, for example to `Downloads`. You need `master.dat`, `critter.dat`, `patch000.dat`, and `data` folder. Watch for file names - keep (or make) them lowercased (see [Configuration](#configuration)). Copy `ce.dat` into this folder.
- Copy `fallout2-ce.apk` from the release to your device. Open it with file explorer, follow instructions (install from unknown source).
- When you run the game for the first time it will immediately present file picker. Select the folder from the first step. Wait until this data is copied. A loading dialog will appear, just wait for about 30 seconds. If you're installing total conversion mod or localized version with a large number of unpacked resources in `data` folder it can take up to 20 minutes. Once copied, the game will start automatically.
@@ -75,7 +79,21 @@ $ mv fallout2 /Applications/Fallout2
- Run the game once. You'll see error message saying "Couldn't find/load text fonts". This step is needed for iOS to expose the game via File Sharing feature.
- Use Finder (macOS Catalina and later) or iTunes (Windows and macOS Mojave or earlier) to copy `master.dat`, `critter.dat`, `patch000.dat`, and `data` folder to "Fallout 2" app ([how-to](https://support.apple.com/HT210598)). Watch for file names - keep (or make) them lowercased (see [Configuration](#configuration)).
- Use Finder (macOS Catalina and later) or iTunes (Windows and macOS Mojave or earlier) to copy `master.dat`, `critter.dat`, `patch000.dat`, `ce.dat`, and `data` folder to "Fallout 2" app ([how-to](https://support.apple.com/HT210598)). Watch for file names - keep (or make) them lowercased (see [Configuration](#configuration)).
-
**Controls on iPad:**
- One-finger tap → left click; hold → left button held.
- Two-finger tap → right click (cycles cursor mode between walk and attack).
- One-finger drag → scroll map / windows.
- Two-finger drag → mouse wheel.
- **Three-finger swipe down → ESC** (opens options / main menu).
- **Four-finger long press → F6** (quicksave). Hold four fingers on the screen for about half a second.
- **Three-finger long press → hold Shift** (highlights interactable objects — containers, doors, items — while held).
- Taps on the HUD (interface bar / end-turn / end-combat buttons) act as direct touches so small buttons are easy to hit without aiming.
- A Bluetooth keyboard works for everything else (numbered dialog choices, load game, etc.).
### Browser
@@ -87,13 +105,13 @@ docker run --rm -v $(pwd):/src emscripten/emsdk:3.1.74 sh -c 'git config --globa
## Configuration
The main configuration file is `fallout2.cfg`. There are several important settings you might need to adjust for your installation. Depending on your Fallout distribution main game assets `master.dat`, `critter.dat`, `patch000.dat`, and `data` folder might be either all lowercased, or all uppercased. You can either update `master_dat`, `critter_dat`, `master_patches` and `critter_patches` settings to match your file names, or rename files to match entries in your `fallout2.cfg`.
The main configuration file is `fallout2-ce.cfg`. There are several important settings you might need to adjust for your installation. Depending on your Fallout distribution main game assets `master.dat`, `critter.dat`, `patch000.dat`, and `data` folder might be either all lowercased, or all uppercased. You can either update `master_dat`, `critter_dat`, `master_patches` and `critter_patches` settings to match your file names, or rename files to match entries in your `fallout2-ce.cfg`.
The `sound` folder (with `music` folder inside) might be located either in `data` folder, or be in the Fallout folder. Update `music_path1` setting to match your hierarchy, usually it's `data/sound/music/` or `sound/music/`. Make sure it matches your path exactly (so it might be `SOUND/MUSIC/` if you've installed Fallout from CD). Music files themselves (with `ACM` extension) should be all uppercased, regardless of `sound` and `music` folders.
Additional settings for screen resolution, UI customization, and map options are now integrated into the main `fallout2.cfg` file (previously part of `f2_res.ini` from Mash's HRP). When Fallout 2 CE starts, if it detects an existing `f2_res.ini` file, it automatically migrates these settings into `fallout2.cfg`. After migration, `fallout2.cfg` becomes the single source of truth for this configuration.
Additional settings for screen resolution, UI customization, and map options are now integrated into the main `fallout2-ce.cfg` file (previously part of `f2_res.ini` from Mash's HRP). When Fallout 2 CE starts, if it detects an existing `f2_res.ini` file, it automatically migrates these settings into main config. After that, `fallout2-ce.cfg` becomes the single source of truth for this configuration.
The following settings can be configured in `fallout2.cfg` under the `[screen]` and `[ui]` sections:
Here are some important settings in `fallout2-ce.cfg` under the `[screen]` and `[ui]` sections. See [the example config](https://github.com/fallout2-ce/fallout2-ce/tree/refs/heads/main/files/ce.cfg) for a full list of settings.
```ini
[screen]
@@ -103,12 +121,12 @@ windowed=1 ; 0 = fullscreen
scale=2 ; 1 = original scale, 2 = 2x scale, etc. (e.g. at scale 2 and screen resolution 1920x1080, in-game resolution will be 960x540, thus every pixel is twice as wide and tall)
[ui]
iface_bar_mode=0 ; 0 = interface bar below game window, 1 = interface bar overlaps game window
iface_bar_width=800 ; Width of interface bar (640 = original, 800 = extended)
iface_bar_side_art=2 ; Interface bar side art style (0=black, 1=metal grey, 2=leather, 3-8=alternative styles)
iface_bar_sides_ori=0 ; Side graphics orientation (0=extend from bar to edges, 1=extend from edges to bar)
;Set to 1 to expand the barter/trade window vertically, adding a 4th item slot per side (requires ce.dat)
expand_barter_window=1
;Maximum number of columns shown in the main inventory and loot/steal windows (valid range: 1..2)
inventory_columns=2
splash_screen_size=1 ; Splash screen scaling (0=original size, 1=fit preserving aspect, 2=stretch to fill)
ignore_map_edges=0 ; Hi-res map scroll edges (0=enabled, 1=ignored)
quick_toolbar_visible=0 ; Skills quick access toolbar visibility (iOS only) (0=hidden, 1=visible)
```
**Recommendations:**
@@ -119,23 +137,24 @@ ignore_map_edges=0 ; Hi-res map scroll edges (0=enabled, 1=ignored)
In time this stuff will receive in-game interface, right now you have to do it manually. To see all currently working fallout2.cfg settings, just run the game once and quit. It will be automatically updated with defaults for every supported setting.
*Note*: Use of the IFACE_BAR settings requires the `f2_res.dat` file, which contains graphical assets. Various versions are available, but one compatible with the above settings can be found here: [f2_res.dat](https://github.com/fallout2-ce/fallout2-ce/raw/refs/heads/main/files/f2_res.dat)
The second configuration file is `ddraw.ini` (part of Sfall). There are dozens of options that adjust or override engine behaviour and gameplay mechanics. This file is intended for modders and advanced users.
For a sample ddraw.ini configuration file, containing all currently working settings use this link: [ddraw.ini](https://raw.githubusercontent.com/fallout2-ce/fallout2-ce/refs/heads/main/files/ddraw.ini)
## Quality of life benefits over vanilla Fallout
* High resolution support
* Expanded 2-column inventory
* Expanded 4-row barter screen
* Expanded AP bar
* Increased pathfinding nodes 5x for more accurate pathfinding
* Ctrl-click to quickly move items when bartering, looting, or stealing
* _a_ to select "all" when selecting item quantity
* _a_ to `Take All` when looting
* When bartering, caps default to the right amount to balance the trade (if possible)
* Music continues playing between maps (requires config)
* Auto open doors (requires config)
* Music continues playing between maps
* Auto open doors
## Contributing
For build instructions and contributor notes, see [CONTRIBUTING.md](CONTRIBUTING.md).
Integrating Sfall goodies is the top priority. Quality of life updates are OK too. In any case open up an issue with your suggestion or to notify other people that something is being worked on.
For current sfall compatibility status and the remaining work needed to close gaps, see [SFALL_COMPATIBILITY.md](SFALL_COMPATIBILITY.md).
+5 -5
View File
@@ -10,7 +10,7 @@ Settings previously read from `ddraw.ini` have been moved into standard CE confi
Most settings that control game behavior (premade characters, extra message files, combat tweaks, worldmap, etc.) have been moved into [`<DAT>/config/game.cfg`](files/ce.dat/config/game.cfg), which is a content-mod config file intended to be overridden by mods. See that file for the full list with descriptions.
The following settings were moved into [`fallout2.cfg`](files/fallout2.cfg) instead:
The following settings were moved into [`fallout2-ce.cfg`](files/ce.cfg) instead:
| ddraw.ini section | ddraw.ini key | fallout2.cfg section | fallout2.cfg key |
| --- | --- | --- | --- |
@@ -81,7 +81,7 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
| Hook | ID | Compatibility | Notes |
| --- | --- | --- | --- |
| ToHit | `HOOK_TOHIT` | âś… | - |
| AfterHitRoll | `HOOK_AFTERHITROLL` | đźš« | Et tu |
| AfterHitRoll | `HOOK_AFTERHITROLL` | âś… | Overriding `defender` leaves a lot of attack variables in previous state (e.g. distance, ->oops, roundsHitMainTarget) |
| CalcAPCost | `HOOK_CALCAPCOST` | âś… | - |
| DeathAnim1 | `HOOK_DEATHANIM1` | đźš« | Use DEATHANIM2 instead |
| DeathAnim2 | `HOOK_DEATHANIM2` | âś… | - |
@@ -94,7 +94,7 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
| BarterPrice | `HOOK_BARTERPRICE` | âś… | - |
| MoveCost | `HOOK_MOVECOST` | đźš« | - |
| ItemDamage | `HOOK_ITEMDAMAGE` | đźš« | - |
| AmmoCost | `HOOK_AMMOCOST` | đźš« | Et tu |
| AmmoCost | `HOOK_AMMOCOST` | âś… | Requires `check_weapon_ammo_cost=1` if you want pre-attack ammo validation to respect per-shot/per-round overrides. |
| KeyPress | `HOOK_KEYPRESS` | âś… | Third hook arg is currently `0`; CE doesn't use VK codes. |
| MouseClick | `HOOK_MOUSECLICK` | âś… | - |
| UseSkill | `HOOK_USESKILL` | đźš« | - |
@@ -108,10 +108,10 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
| StdProcedureEnd | `HOOK_STDPROCEDURE_END` | âś… | - |
| CarTravel | `HOOK_CARTRAVEL` | đźš« | - |
| SetGlobalVar | `HOOK_SETGLOBALVAR` | đźš« | - |
| RestTimer | `HOOK_RESTTIMER` | đźš« | Et tu |
| RestTimer | `HOOK_RESTTIMER` | âś… | CE is slightly more strict: only `ret0 == 1` interrupts. Ticks wrap every 6.8y; do not rely on them for absolute game time. |
| GameModeChange | `HOOK_GAMEMODECHANGE` | âś… | - |
| UseAnimObj | `HOOK_USEANIMOBJ` | đźš« | Et tu; (maybe) |
| ExplosiveTimer | `HOOK_EXPLOSIVETIMER` | đźš« | - |
| ExplosiveTimer | `HOOK_EXPLOSIVETIMER` | âś… | - |
| DescriptionObj | `HOOK_DESCRIPTIONOBJ` | đźš« | Et tu |
| UseSkillOn | `HOOK_USESKILLON` | đźš« | Et tu |
| OnExplosion | `HOOK_ONEXPLOSION` | đźš« | (maybe) |
+5
View File
@@ -98,6 +98,8 @@ display_karma_changes=0
enable_high_resolution_stencil=1
;Set to 1 to extend the action points bar to show up to 16 AP instead of 10 (requires iface_apbar_e.frm)
extend_ap_bar=0
;Set to 1 to expand the barter/trade window vertically, adding a 4th item slot per side (requires barter_e.frm, trade_e.frm, and a screen height of at least 528 pixels)
expand_barter_window=0
iface_bar_mode=0
iface_bar_side_art=2
iface_bar_sides_ori=0
@@ -109,6 +111,9 @@ numbers_in_dialogue=0
;Set to 2 to also skip the splash screen
skip_opening_movies=0
splash_screen_size=1
;Maximum number of columns shown in the main inventory and loot/steal windows (valid range: 1..2)
;2-column Loot/steal window requires minimum screen width of 673px
inventory_columns=2
[qol]
; Automatically open doors that are unlocked and has no script attached.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3
View File
@@ -74,6 +74,9 @@ bonus_hth_damage_fix=1
remove_critical_time_limits=0
; Set to 1 to allow science and repair to be used on the player, or 2 for all critters (rather than only brahmin/robots).
science_on_critters=0
; Set to 1 to make pre-attack ammo checks respect weapon ammo cost and HOOK_AMMOCOST type 1 overrides.
; Set to 0 to preserve vanilla behavior where one loaded round is enough to attempt the attack.
check_weapon_ammo_cost=1
; Set to 1 to enable modified burst bullet distribution (spray mod).
burst_enabled=0
; Ratio of bullets going to the center group vs. left/right in burst attacks. Multiplier is capped at divisor.
+74
View File
@@ -0,0 +1,74 @@
#include "sfall.h"
#include "dik.h"
#include "define_lite.h"
#include "define_extra.h"
#include "lib.arrays.h"
variable afterhitroll_mode := 0;
procedure afterhitroll_mode_name begin
if (afterhitroll_mode == 1) then return "force_hit";
if (afterhitroll_mode == 2) then return "force_eyes";
if (afterhitroll_mode == 3) then return "retarget_self";
return "log_only";
end
procedure afterhitroll_handler begin
variable
args := get_sfall_args,
roll := args[0],
attacker := args[1],
target := args[2],
bodypart := args[3],
hitChance := args[4],
attackerName := "<null>",
targetName := "<null>";
if (attacker) then attackerName := obj_name(attacker);
if (target) then targetName := obj_name(target);
display_msg(string_format("afterhitroll mode=%s roll=%d chance=%d %s -> %s body=%d",
afterhitroll_mode_name,
roll,
hitChance,
attackerName,
targetName,
bodypart));
if (attacker != dude_obj) then return;
if (afterhitroll_mode == 1) then begin
set_sfall_return(ROLL_SUCCESS);
end else if (afterhitroll_mode == 2) then begin
set_sfall_return(roll);
set_sfall_return(BODY_HIT_EYES);
end else if (afterhitroll_mode == 3) then begin
set_sfall_return(ROLL_SUCCESS);
set_sfall_return(BODY_HIT_TORSO);
set_sfall_return(dude_obj);
end
end
procedure keypress_handler begin
variable
pressed := get_sfall_arg_at(0),
key := get_sfall_arg_at(1);
if (not pressed) then return;
if (key != DIK_J) then return;
afterhitroll_mode := afterhitroll_mode + 1;
if (afterhitroll_mode > 3) then afterhitroll_mode := 0;
display_msg(string_format1("afterhitroll mode -> %s", afterhitroll_mode_name));
end
procedure start begin
if (not game_loaded) then return;
display_msg("afterhitroll manual test ready: press J to cycle log_only / force_hit / force_eyes / retarget_self");
display_msg("attack something in combat and watch hook logs plus the overridden result");
register_hook_proc(HOOK_KEYPRESS, keypress_handler);
register_hook_proc(HOOK_AFTERHITROLL, afterhitroll_handler);
end
+78
View File
@@ -0,0 +1,78 @@
#include "sfall.h"
#include "dik.h"
#include "lib.arrays.h"
#include "lib.obj.h"
variable ammocost_mode := 0;
procedure ammocost_mode_name begin
if (ammocost_mode == 1) then return "double-single";
if (ammocost_mode == 2) then return "burst-x2";
if (ammocost_mode == 3) then return "free";
return "engine";
end
procedure ammocost_item_name(variable item) begin
if (item == 0) then return "<null>";
return obj_name(item);
end
procedure ammocost_handler begin
variable
args := get_sfall_args,
weapon := args[0],
rounds := args[1],
defaultCost := args[2],
hookType := args[3],
weaponPid := 0;
if (weapon) then weaponPid := obj_pid(weapon);
display_msg(string_format6("ammocost mode=%s type=%d rounds=%d default=%d weapon=%s pid=%d",
ammocost_mode_name,
hookType,
rounds,
defaultCost,
ammocost_item_name(weapon),
weaponPid));
if (ammocost_mode == 1) then begin
if (hookType == 0 or hookType == 1) then
set_sfall_return(defaultCost + 1);
end
if (ammocost_mode == 2) then begin
if (hookType == 1 or hookType == 3) then
set_sfall_return(rounds * 2);
if (hookType == 2) then
set_sfall_return(2);
end
if (ammocost_mode == 3) then
set_sfall_return(0);
end
procedure keypress_handler begin
variable
pressed := get_sfall_arg_at(0),
key := get_sfall_arg_at(1);
if (not pressed) then return;
if (key != DIK_H) then return;
ammocost_mode := ammocost_mode + 1;
if (ammocost_mode > 3) then ammocost_mode := 0;
display_msg(string_format1("ammocost mode -> %s", ammocost_mode_name));
intface_redraw;
end
procedure start begin
if (not game_loaded) then return;
display_msg("ammocost manual test ready: press H to cycle engine/double-single/burst-x2/free");
display_msg("check single-shot ammo use, burst ammo use, and pre-attack ammo validation with CheckWeaponAmmoCost");
register_hook_proc(HOOK_KEYPRESS, keypress_handler);
register_hook_proc(HOOK_AMMOCOST, ammocost_handler);
end
+68
View File
@@ -0,0 +1,68 @@
#include "sfall.h"
#include "dik.h"
variable mode := 0;
variable hook_calls := 0;
procedure resttimer_mode_name(variable value) begin
if (value == 0) then return "observe";
if (value == 1) then return "interrupt";
if (value == 2) then return "invalid_ret";
if (value == 3) then return "ignore_esc";
return "unknown";
end
procedure log_mode begin
display_msg(string_format2("resttimer mode=%d (%s)", mode, resttimer_mode_name(mode)));
end
procedure resttimer_handler begin
variable
time_ticks := get_sfall_arg_at(0),
event_type := get_sfall_arg_at(1),
rest_hours := get_sfall_arg_at(2),
rest_minutes := get_sfall_arg_at(3);
hook_calls += 1;
if (hook_calls <= 3 or event_type != 0) then begin
display_msg(string_format4("resttimer call=%d event=%d duration=%d:%02d", hook_calls, event_type, rest_hours, rest_minutes));
display_msg(string_format1("resttimer game_time=%d", time_ticks));
end
if (mode == 1 and event_type == 0) then begin
display_msg("resttimer interrupting normal rest");
set_sfall_return(1);
end else if (mode == 2 and event_type == 0) then begin
display_msg("resttimer returning invalid value 7");
set_sfall_return(7);
end else if (mode == 3 and event_type == -1) then begin
display_msg("resttimer ignoring ESC");
set_sfall_return(0);
end
end
procedure keypress_handler begin
variable
pressed := get_sfall_arg_at(0),
key := get_sfall_arg_at(1);
if (not pressed) then return;
if (key != DIK_X) then return;
mode := mode + 1;
if (mode > 3) then mode := 0;
hook_calls := 0;
call log_mode;
end
procedure start begin
if (not game_loaded) then return;
display_msg("resttimer manual test ready: open Pip-Boy alarm clock and rest");
display_msg("press X to cycle mode: observe, interrupt, invalid_ret, ignore_esc");
call log_mode;
register_hook_proc(HOOK_KEYPRESS, keypress_handler);
register_hook_proc(HOOK_RESTTIMER, resttimer_handler);
end
+227 -30
View File
@@ -11,10 +11,14 @@
#include "draw.h"
#include "game.h"
#include "memory.h"
#include "obj_types.h"
#include "proto.h"
#include "settings.h"
#include <algorithm>
#include <memory>
#include <unordered_map>
#include <vector>
namespace fallout {
typedef struct ArtListDescription {
@@ -37,7 +41,7 @@ static int artCacheReadDataImpl(int fid, int* sizePtr, unsigned char* data);
static void artCacheFreeImpl(void* ptr);
static int artReadFrameData(unsigned char* data, File* stream, int count, int* paddingPtr);
static int artReadHeader(Art* art, File* stream);
static int artGetDataSize(Art* art);
static int artGetDataSize(const Art* art);
static int paddingForSize(int size);
// 0x5002D8
@@ -128,6 +132,11 @@ static int* _anon_alias;
// 0x56CAF0
static int* gArtCritterFidShoudRunData;
static std::unordered_map<std::string, std::shared_ptr<NamedCacheEntry>> gNamedArtCache;
constexpr int kNamedCacheMaxBytes = 32 * 1024 * 1024; // 32MB soft limit
static unsigned int gNamedArtCacheMruCounter = 0;
static int gNamedArtCacheCurrentBytes = 0;
// 0x418840
int artInit()
{
@@ -925,6 +934,22 @@ int artAliasFid(int fid)
return -1;
}
static bool artGetLocalizedPath(const char* basePath, const char** outPath)
{
static char localizedPath[COMPAT_MAX_PATH];
if (!gArtLanguageInitialized) {
return false;
}
const char* pch = strchr(basePath, '\\');
if (pch == nullptr) {
pch = basePath;
}
snprintf(localizedPath, sizeof(localizedPath), "art\\%s\\%s", gArtLanguage, pch);
*outPath = localizedPath;
return true;
}
// 0x419A78
static int artCacheGetFileSizeImpl(int fid, int* sizePtr)
{
@@ -932,21 +957,11 @@ static int artCacheGetFileSizeImpl(int fid, int* sizePtr)
char* artFilePath = artBuildFilePath(fid);
if (artFilePath != nullptr) {
bool loaded = false;
File* stream = nullptr;
if (gArtLanguageInitialized) {
char* pch = strchr(artFilePath, '\\');
if (pch == nullptr) {
pch = artFilePath;
}
char localizedPath[COMPAT_MAX_PATH];
snprintf(localizedPath, sizeof(localizedPath), "art\\%s\\%s", gArtLanguage, pch);
const char* localizedPath;
if (artGetLocalizedPath(artFilePath, &localizedPath)) {
stream = fileOpen(localizedPath, "rb");
}
if (stream == nullptr) {
stream = fileOpen(artFilePath, "rb");
}
@@ -972,15 +987,8 @@ static int artCacheReadDataImpl(int fid, int* sizePtr, unsigned char* data)
char* artFileName = artBuildFilePath(fid);
if (artFileName != nullptr) {
bool loaded = false;
if (gArtLanguageInitialized) {
char* pch = strchr(artFileName, '\\');
if (pch == nullptr) {
pch = artFileName;
}
char localizedPath[COMPAT_MAX_PATH];
snprintf(localizedPath, sizeof(localizedPath), "art\\%s\\%s", gArtLanguage, pch);
const char* localizedPath;
if (artGetLocalizedPath(artFileName, &localizedPath)) {
if (artRead(localizedPath, data) == 0) {
loaded = true;
}
@@ -1117,6 +1125,16 @@ Art* artLoad(const char* path)
return reinterpret_cast<Art*>(data);
}
static Art* artLoadLocalized(const char* path)
{
const char* localizedPath;
Art* result = artGetLocalizedPath(path, &localizedPath)
? artLoad(localizedPath)
: nullptr;
return result != nullptr ? result : artLoad(path);
}
// 0x419FC0
int artRead(const char* path, unsigned char* data)
{
@@ -1224,7 +1242,7 @@ int artWrite(const char* path, unsigned char* data)
return 0;
}
static int artGetDataSize(Art* art)
static int artGetDataSize(const Art* art)
{
int dataSize = sizeof(*art) + art->dataSize;
@@ -1244,6 +1262,115 @@ static int paddingForSize(int size)
return (sizeof(int) - size % sizeof(int)) % sizeof(int);
}
class NamedCacheEntry {
public:
explicit NamedCacheEntry(ArtPtr&& art);
const Art* art() const { return _art.get(); }
unsigned char* frameData(int frame, int direction, int& outWidth, int& outHeight) const;
unsigned int mru = 0;
private:
ArtPtr _art;
};
NamedCacheEntry::NamedCacheEntry(ArtPtr&& art)
: _art(std::move(art))
{
}
unsigned char* NamedCacheEntry::frameData(int frame, int direction, int& outWidth, int& outHeight) const
{
unsigned char* data = artGetFrameData(_art.get(), frame, direction);
if (!data) {
outWidth = 0;
outHeight = 0;
return nullptr;
}
outWidth = artGetWidth(_art.get(), frame, direction);
outHeight = artGetHeight(_art.get(), frame, direction);
return data;
}
std::shared_ptr<NamedCacheEntry> artLockNamedFrameData(const char* path)
{
auto it = gNamedArtCache.find(path);
if (it != gNamedArtCache.end()) {
it->second->mru = ++gNamedArtCacheMruCounter;
return it->second;
}
Art* art = artLoadLocalized(path);
if (!art) return nullptr;
if (gNamedArtCacheMruCounter == UINT_MAX) {
// This looks complicated, but it should happen rarely and needed to preserve mru order.
std::vector<NamedCacheEntry*> sorted;
sorted.reserve(gNamedArtCache.size());
for (auto& [key, e] : gNamedArtCache) {
sorted.push_back(e.get());
}
std::sort(sorted.begin(), sorted.end(), [](auto* a, auto* b) { return a->mru < b->mru; });
unsigned int mru = 0;
for (auto* e : sorted) {
e->mru = mru++;
}
gNamedArtCacheMruCounter = mru;
}
auto entry = std::make_shared<NamedCacheEntry>(ArtPtr(art));
entry->mru = ++gNamedArtCacheMruCounter;
gNamedArtCacheCurrentBytes += artGetDataSize(art);
// Evict LRU entries if over soft limit (post-insertion)
while (gNamedArtCacheCurrentBytes > kNamedCacheMaxBytes) {
unsigned int lowestMru = UINT_MAX;
auto evictIt = gNamedArtCache.end();
for (auto iter = gNamedArtCache.begin(); iter != gNamedArtCache.end(); ++iter) {
if (iter->second.use_count() == 1 && iter->second->mru < lowestMru) {
lowestMru = iter->second->mru;
evictIt = iter;
}
}
if (evictIt == gNamedArtCache.end()) {
break;
}
gNamedArtCacheCurrentBytes -= artGetDataSize(evictIt->second->art());
gNamedArtCache.erase(evictIt);
}
return gNamedArtCache.emplace(path, std::move(entry)).first->second;
}
FrmId::FrmId(ObjectType objType, int frmId)
: _fid(buildFid(objType, frmId, 0, 0, 0))
{
assert(objType >= 0 && objType < OBJ_TYPE_COUNT);
}
FrmId::FrmId(ObjectType objType, const char* path)
: _objectType(objType)
, _path(path)
{
assert(objType >= 0 && objType < OBJ_TYPE_COUNT);
}
FrmId::FrmId(const char* path)
: _path(path)
{
}
ObjectType FrmId::objectType() const
{
assert(hasObjectType());
return static_cast<ObjectType>(_objectType);
}
FrmImage::FrmImage()
{
_key = nullptr;
@@ -1257,6 +1384,44 @@ FrmImage::~FrmImage()
unlock();
}
FrmImage::FrmImage(FrmImage&& other) noexcept
: _namedKey(std::move(other._namedKey))
, _key(other._key)
, _data(other._data)
, _width(other._width)
, _height(other._height)
{
other.resetInternal();
}
FrmImage& FrmImage::operator=(FrmImage&& other) noexcept
{
if (this != &other) {
unlock();
_namedKey = std::move(other._namedKey);
_key = other._key;
_data = other._data;
_width = other._width;
_height = other._height;
other.resetInternal();
}
return *this;
}
bool FrmImage::lock(const FrmId& frmId)
{
if (frmId.fid() >= 0) {
return lock(frmId.fid());
}
if (frmId.filePath() != nullptr) {
return frmId.hasObjectType()
? lock(frmId.objectType(), frmId.filePath())
: lock(frmId.filePath());
}
return false;
}
bool FrmImage::lock(unsigned int fid)
{
if (isLocked()) {
@@ -1271,15 +1436,47 @@ bool FrmImage::lock(unsigned int fid)
return true;
}
void FrmImage::unlock()
bool FrmImage::lock(const char* frmPath)
{
if (isLocked()) {
artUnlock(_key);
_key = nullptr;
_data = nullptr;
_width = 0;
_height = 0;
return false;
}
_namedKey = artLockNamedFrameData(frmPath);
if (!_namedKey) return false;
_data = _namedKey->frameData(0, 0, _width, _height);
if (_data == nullptr) {
_namedKey = nullptr;
return false;
}
return true;
}
bool FrmImage::lock(ObjectType objType, const char* frmRelativePath)
{
if (objType < OBJ_TYPE_ITEM || objType >= OBJ_TYPE_COUNT) {
return false;
}
snprintf(_art_name, sizeof(_art_name), "%s%s%s\\%s", _cd_path_base, "art\\", gArtListDescriptions[objType].name, frmRelativePath);
return lock(_art_name);
}
void FrmImage::unlock()
{
if (_key != nullptr) {
artUnlock(_key);
}
resetInternal();
}
void FrmImage::resetInternal()
{
_namedKey = nullptr;
_key = nullptr;
_data = nullptr;
_width = 0;
_height = 0;
}
} // namespace fallout

Some files were not shown because too many files have changed in this diff Show More