Compare commits
266 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8d4057366e | |||
| 80b58bd174 | |||
| ca268ecb2c | |||
| cf5f865a23 | |||
| 37e4822ed5 | |||
| bf639a2c4a | |||
| 89839be3af | |||
| ec722475b6 | |||
| 32ed3c3271 | |||
| 03bf507893 | |||
| 681b7c388b | |||
| fe0d767521 | |||
| 62c5c4757c | |||
| 70b0b61664 | |||
| 53a4437be9 | |||
| 42c541012c | |||
| 61293bd39c | |||
| d641fefc13 | |||
| 666e5cf62d | |||
| 2565900f90 | |||
| 6b6fa3f111 | |||
| a06097aef5 | |||
| efdc2e0199 | |||
| d6a74468da | |||
| fcf8a87603 | |||
| a8815229a0 | |||
| aa99b2e1d3 | |||
| 63d8300c61 | |||
| 81c3ff9b1a | |||
| a0652e2646 | |||
| df3ac30de6 | |||
| 6ca1329720 | |||
| 129361836f | |||
| cf4921de1e | |||
| ecc6a8679b | |||
| a39f149817 | |||
| 540cc1e08b | |||
| ef34fdb519 | |||
| 9b02f600de | |||
| 0bb07dbd50 | |||
| 0a85ce520b | |||
| 99bc14cf3a | |||
| 527e152297 | |||
| c8d45854ba | |||
| 11472e8be9 | |||
| 69e4adf5b3 | |||
| a2eabd668b | |||
| 6d1273d325 | |||
| e23b39abaa | |||
| 117b1d7b44 | |||
| 6398f8a79e | |||
| 81b9345303 | |||
| 249892716e | |||
| 3e8227a62b | |||
| 1570f860e8 | |||
| d7c4589a55 | |||
| 0a9a1dc7ca | |||
| 42346f66b2 | |||
| 7ac651b736 | |||
| 0bb822ba60 | |||
| 075de8f837 | |||
| 36b5ceba8a | |||
| f5d3cfb5e3 | |||
| 81210f46af | |||
| c14f671a0d | |||
| 33141672ed | |||
| 28083cfea9 | |||
| c9864741f3 | |||
| 5a230efd2b | |||
| 3aea6a98ef | |||
| ba49abcea6 | |||
| 47f5be8340 | |||
| d435185e9e | |||
| dcd450a9b7 | |||
| bfbf692cc0 | |||
| 2714dc1ea1 | |||
| 8604d9c401 | |||
| b9261c3da2 | |||
| ed7176b796 | |||
| 6cac53d20b | |||
| ad3860790c | |||
| bffe81d0b3 | |||
| 6ab08bd22b | |||
| 060c79fc20 | |||
| ac64fde502 | |||
| 7496afa4f8 | |||
| 66955f893a | |||
| 9ee4cb4a26 | |||
| a5cefd6c8b | |||
| d040ea814a | |||
| 03145e4fcd | |||
| 812079004c | |||
| 6224af6178 | |||
| a8d3cdd148 | |||
| 95cc863fde | |||
| cc1562b056 | |||
| 07f3c82444 | |||
| 9976728e04 | |||
| 5922d15b1c | |||
| 248d6dfb92 | |||
| 73cc93a3e4 | |||
| 34259269a4 | |||
| 11217974c6 | |||
| 0e13989f9c | |||
| 57dcf42b5c | |||
| c2c26db5d9 | |||
| b6ee5fcbe9 | |||
| 0e11569397 | |||
| 4b137dac5f | |||
| 1f6d5ebbc9 | |||
| 462baa0a04 | |||
| b74f3c368b | |||
| a05ab09e1b | |||
| 1b0cd6d757 | |||
| b8dea116ee | |||
| e11f1af9ae | |||
| 9c34b26fec | |||
| 9a6d45541f | |||
| f4775775b8 | |||
| a48d744cde | |||
| 16dbe1122e | |||
| b0cb70ffa6 | |||
| 512530c653 | |||
| 982d4cc0f9 | |||
| 3b951b8578 | |||
| 17a4ee8a68 | |||
| c85cda4b48 | |||
| c7ca358295 | |||
| 96296417c6 | |||
| 737076a126 | |||
| c0908cf14f | |||
| 343911f736 | |||
| 281c1aa82a | |||
| 18afce71b0 | |||
| 9599d63f45 | |||
| ce351988b5 | |||
| feb3821565 | |||
| 54eaff26ea | |||
| 5a7ffde45e | |||
| 1c0385ad11 | |||
| 0fe6d88a3a | |||
| 7596d4d721 | |||
| 8333e553e2 | |||
| 6f62cfd466 | |||
| 280b55ab05 | |||
| e02dcdf69d | |||
| b5701fb9b9 | |||
| c569b879f6 | |||
| b36ac50425 | |||
| 029a84a113 | |||
| e6fddd6905 | |||
| b0b69bd780 | |||
| 6de4ac87df | |||
| 502c920656 | |||
| c383fc6b95 | |||
| 4b03c714b4 | |||
| 0d93279a10 | |||
| 510d7bfe95 | |||
| 19d0d2919f | |||
| 42f01c8d45 | |||
| 0474199b5c | |||
| 8a4ce6c658 | |||
| 884034f0bf | |||
| 602ef7c1e1 | |||
| 897ff2bbd2 | |||
| 9ade107e95 | |||
| 1698cfa04b | |||
| 59639c60a3 | |||
| f056f0e131 | |||
| dc90beb696 | |||
| 90da2164f2 | |||
| 9d9e09e80f | |||
| cd51e0cdd7 | |||
| d49869f45c | |||
| 3a541d4b67 | |||
| 7471f9cdbe | |||
| fe9ba9171e | |||
| 6c03e4e293 | |||
| 3f25c9265b | |||
| fa475c754e | |||
| 5e9cf96106 | |||
| a70912d311 | |||
| 67351b8b09 | |||
| e0e0a1ee7d | |||
| fa11122f87 | |||
| ef54463fcc | |||
| 2255481d50 | |||
| 03a7e2e1b1 | |||
| 19bcfebe0d | |||
| 393d89f8c4 | |||
| 9c6286f94a | |||
| fa058f24b4 | |||
| b2420bdc80 | |||
| 23bee9fd62 | |||
| 3ec827d5c1 | |||
| c692fc91af | |||
| a38151bf2c | |||
| b689b3a757 | |||
| e7d68c4f72 | |||
| bc43bdc99c | |||
| e5aa4a3518 | |||
| 4e98093d6c | |||
| 1342fd3ecf | |||
| 3015f39368 | |||
| 37f7ecc1c3 | |||
| bdf2c219ab | |||
| 3488833c2e | |||
| 4821cab724 | |||
| 0851354c8a | |||
| 2f42818a33 | |||
| 12367acb33 | |||
| caa8b06d4f | |||
| 2b63360b95 | |||
| 8dd8d1c312 | |||
| 8ee0f478ad | |||
| 9c6daa4abe | |||
| 01f264e0c2 | |||
| 1bbd586cbb | |||
| 223930c464 | |||
| 40510539ee | |||
| c47113ca29 | |||
| ad9b8586e9 | |||
| 5b151634a5 | |||
| 7627092478 | |||
| b60fe43b06 | |||
| 21aec548af | |||
| beb010cd0f | |||
| b23d05f850 | |||
| e16659d96d | |||
| 4c1020af5f | |||
| a4105d5826 | |||
| ddae4df4ab | |||
| 74a9a9e530 | |||
| 223d214f57 | |||
| a52fd4e70a | |||
| 266c727870 | |||
| 54d230432b | |||
| 9663532e44 | |||
| a4e9123bcc | |||
| 08691ce319 | |||
| d03fd5e43f | |||
| 7ddd8f2894 | |||
| 93a341bbaa | |||
| d2c4538656 | |||
| 8a5c3bc97b | |||
| db57fe6a5e | |||
| 56d798dd12 | |||
| 90942c46b2 | |||
| 1cda2beef7 | |||
| 8754a5d6c9 | |||
| 65f43804b3 | |||
| 9b150257fb | |||
| 32c7883f82 | |||
| 1c73fb7240 | |||
| cce1bb223e | |||
| 624076ae9b | |||
| 8d61a19dbe | |||
| 0264c00ab8 | |||
| e1f22b8218 | |||
| fe4c125474 | |||
| 56d27400ac | |||
| b5681984cd | |||
| a541d0f24a | |||
| 20a41088c6 | |||
| 16ab9cb958 | |||
| 5f9ceb7f5d |
@@ -1,2 +1,3 @@
|
||||
BasedOnStyle: WebKit
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
FixNamespaceComments: true
|
||||
|
||||
@@ -31,6 +31,18 @@ jobs:
|
||||
- name: cppcheck
|
||||
run: cppcheck --std=c++17 src/
|
||||
|
||||
code-format:
|
||||
name: Code format check
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: clang-format
|
||||
run: find src -type f -name \*.cc -o -name \*.h | xargs clang-format --dry-run --Werror
|
||||
|
||||
android:
|
||||
name: Android
|
||||
|
||||
@@ -41,7 +53,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
@@ -75,6 +87,52 @@ jobs:
|
||||
path: os/android/app/build/outputs/apk/debug/app-debug.apk
|
||||
retention-days: 7
|
||||
|
||||
ios:
|
||||
name: iOS
|
||||
|
||||
runs-on: macos-12
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Cache cmake build
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: build
|
||||
key: ios-cmake-v2
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake \
|
||||
-B build \
|
||||
-D CMAKE_TOOLCHAIN_FILE=cmake/toolchain/ios.toolchain.cmake \
|
||||
-D ENABLE_BITCODE=0 \
|
||||
-D PLATFORM=OS64 \
|
||||
-G Xcode \
|
||||
-D CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY='' \
|
||||
# EOL
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cmake \
|
||||
--build build \
|
||||
--config RelWithDebInfo \
|
||||
-j $(sysctl -n hw.physicalcpu) \
|
||||
# EOL
|
||||
|
||||
- name: Pack
|
||||
run: |
|
||||
cd build
|
||||
cpack -C RelWithDebInfo
|
||||
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: fallout2-ce.ipa
|
||||
path: build/fallout2-ce.ipa
|
||||
retention-days: 7
|
||||
|
||||
linux:
|
||||
name: Linux (${{ matrix.arch }})
|
||||
|
||||
@@ -155,28 +213,34 @@ jobs:
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: build
|
||||
key: macos-cmake-v3
|
||||
key: macos-cmake-v4
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake \
|
||||
-B build \
|
||||
-D CMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-G Xcode \
|
||||
-D CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY='' \
|
||||
# EOL
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cmake \
|
||||
--build build \
|
||||
--config RelWithDebInfo \
|
||||
-j $(sysctl -n hw.physicalcpu) \
|
||||
--target package \
|
||||
# EOL
|
||||
|
||||
- name: Pack
|
||||
run: |
|
||||
cd build
|
||||
cpack -C RelWithDebInfo
|
||||
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: fallout2-ce-macos.dmg
|
||||
path: build/fallout2-ce.dmg
|
||||
path: build/Fallout II Community Edition.dmg
|
||||
retention-days: 7
|
||||
|
||||
windows:
|
||||
|
||||
@@ -56,6 +56,48 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
ios:
|
||||
name: iOS
|
||||
|
||||
runs-on: macos-11
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Cache cmake build
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: build
|
||||
key: ios-cmake-v1
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake \
|
||||
-B build \
|
||||
-D CMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-D CMAKE_TOOLCHAIN_FILE=cmake/toolchain/ios.toolchain.cmake \
|
||||
-D ENABLE_BITCODE=0 \
|
||||
-D PLATFORM=OS64 \
|
||||
# EOL
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cmake \
|
||||
--build build \
|
||||
-j $(sysctl -n hw.physicalcpu) \
|
||||
--target package \
|
||||
# EOL
|
||||
|
||||
- name: Upload
|
||||
run: |
|
||||
cd build
|
||||
cp fallout2-ce.zip fallout2-ce-ios.ipa
|
||||
gh release upload ${{ github.ref_name }} fallout2-ce-ios.ipa
|
||||
rm fallout2-ce-ios.ipa
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
linux:
|
||||
name: Linux (${{ matrix.arch }})
|
||||
|
||||
|
||||
+109
-45
@@ -4,9 +4,14 @@ set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
||||
|
||||
set(EXECUTABLE_NAME fallout2-ce)
|
||||
|
||||
if (APPLE)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.11" CACHE STRING "")
|
||||
set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "")
|
||||
if(APPLE)
|
||||
if(IOS)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "11" CACHE STRING "")
|
||||
set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE STRING "")
|
||||
else()
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.11" CACHE STRING "")
|
||||
set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
project(${EXECUTABLE_NAME})
|
||||
@@ -15,7 +20,7 @@ set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||
set(CMAKE_CXX_EXTENSIONS NO)
|
||||
|
||||
if (ANDROID)
|
||||
if(ANDROID)
|
||||
add_library(${EXECUTABLE_NAME} SHARED)
|
||||
else()
|
||||
add_executable(${EXECUTABLE_NAME} WIN32 MACOSX_BUNDLE)
|
||||
@@ -52,8 +57,6 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
"src/combat.h"
|
||||
"src/config.cc"
|
||||
"src/config.h"
|
||||
"src/core.cc"
|
||||
"src/core.h"
|
||||
"src/credits.cc"
|
||||
"src/credits.h"
|
||||
"src/critter.cc"
|
||||
@@ -80,8 +83,6 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
"src/display_monitor.h"
|
||||
"src/draw.cc"
|
||||
"src/draw.h"
|
||||
"src/electronic_registration.cc"
|
||||
"src/electronic_registration.h"
|
||||
"src/elevator.cc"
|
||||
"src/elevator.h"
|
||||
"src/endgame.cc"
|
||||
@@ -104,8 +105,6 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
"src/game_mouse.h"
|
||||
"src/game_movie.cc"
|
||||
"src/game_movie.h"
|
||||
"src/game_palette.cc"
|
||||
"src/game_palette.h"
|
||||
"src/game_sound.cc"
|
||||
"src/game_sound.h"
|
||||
"src/game_vars.h"
|
||||
@@ -115,10 +114,10 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
"src/geometry.h"
|
||||
"src/graph_lib.cc"
|
||||
"src/graph_lib.h"
|
||||
"src/grayscale.cc"
|
||||
"src/grayscale.h"
|
||||
"src/heap.cc"
|
||||
"src/heap.h"
|
||||
"src/input.cc"
|
||||
"src/input.h"
|
||||
"src/interface.cc"
|
||||
"src/interface.h"
|
||||
"src/interpreter_extra.cc"
|
||||
@@ -131,6 +130,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
"src/inventory.h"
|
||||
"src/item.cc"
|
||||
"src/item.h"
|
||||
"src/kb.cc"
|
||||
"src/kb.h"
|
||||
"src/light.cc"
|
||||
"src/light.h"
|
||||
"src/lips.cc"
|
||||
@@ -139,6 +140,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
"src/loadsave.h"
|
||||
"src/main.cc"
|
||||
"src/main.h"
|
||||
"src/mainmenu.cc"
|
||||
"src/mainmenu.h"
|
||||
"src/map_defs.h"
|
||||
"src/map.cc"
|
||||
"src/map.h"
|
||||
@@ -149,10 +152,10 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
"src/memory.h"
|
||||
"src/message.cc"
|
||||
"src/message.h"
|
||||
"src/mmx.cc"
|
||||
"src/mmx.h"
|
||||
"src/mouse_manager.cc"
|
||||
"src/mouse_manager.h"
|
||||
"src/mouse.cc"
|
||||
"src/mouse.h"
|
||||
"src/movie_effect.cc"
|
||||
"src/movie_effect.h"
|
||||
"src/movie_lib.cc"
|
||||
@@ -214,6 +217,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
"src/stat.h"
|
||||
"src/string_parsers.cc"
|
||||
"src/string_parsers.h"
|
||||
"src/svga.cc"
|
||||
"src/svga.h"
|
||||
"src/text_font.cc"
|
||||
"src/text_font.h"
|
||||
"src/text_object.cc"
|
||||
@@ -223,8 +228,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
"src/trait_defs.h"
|
||||
"src/trait.cc"
|
||||
"src/trait.h"
|
||||
"src/trap.cc"
|
||||
"src/trap.h"
|
||||
"src/vcr.cc"
|
||||
"src/vcr.h"
|
||||
"src/version.cc"
|
||||
"src/version.h"
|
||||
"src/widget.cc"
|
||||
@@ -254,19 +259,43 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
"src/platform_compat.h"
|
||||
"src/pointer_registry.cc"
|
||||
"src/pointer_registry.h"
|
||||
"src/preferences.cc"
|
||||
"src/preferences.h"
|
||||
"src/settings.cc"
|
||||
"src/settings.h"
|
||||
"src/sfall_config.cc"
|
||||
"src/sfall_config.h"
|
||||
"src/sfall_global_vars.cc"
|
||||
"src/sfall_global_vars.h"
|
||||
"src/sfall_global_scripts.cc"
|
||||
"src/sfall_global_scripts.h"
|
||||
"src/sfall_ini.cc"
|
||||
"src/sfall_ini.h"
|
||||
"src/sfall_kb_helpers.cc"
|
||||
"src/sfall_kb_helpers.h"
|
||||
"src/sfall_lists.cc"
|
||||
"src/sfall_lists.h"
|
||||
"src/sfall_opcodes.cc"
|
||||
"src/sfall_opcodes.h"
|
||||
"src/sfall_arrays.cc"
|
||||
"src/sfall_arrays.h"
|
||||
"src/touch.cc"
|
||||
"src/touch.h"
|
||||
)
|
||||
|
||||
if(IOS)
|
||||
target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
"src/platform/ios/paths.h"
|
||||
"src/platform/ios/paths.mm"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_compile_definitions(${EXECUTABLE_NAME} PUBLIC
|
||||
_CRT_SECURE_NO_WARNINGS
|
||||
_CRT_NONSTDC_NO_WARNINGS
|
||||
)
|
||||
else()
|
||||
target_compile_definitions(${EXECUTABLE_NAME} PRIVATE
|
||||
$<$<CONFIG:Debug>:_DEBUG>
|
||||
$<$<CONFIG:RelWithDebInfo>:_DEBUG>
|
||||
NOMINMAX
|
||||
WIN32_LEAN_AND_MEAN
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -276,7 +305,7 @@ if(WIN32)
|
||||
)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
if(WIN32)
|
||||
target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
"os/windows/fallout2-ce.ico"
|
||||
"os/windows/fallout2-ce.rc"
|
||||
@@ -284,16 +313,47 @@ if (WIN32)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
target_sources(${EXECUTABLE_NAME} PUBLIC "os/macos/fallout2-ce.icns")
|
||||
set_source_files_properties("os/macos/fallout2-ce.icns" PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
|
||||
if(IOS)
|
||||
set(RESOURCES
|
||||
"os/ios/AppIcon.xcassets"
|
||||
"os/ios/LaunchScreen.storyboard"
|
||||
)
|
||||
|
||||
target_sources(${EXECUTABLE_NAME} PUBLIC ${RESOURCES})
|
||||
set_source_files_properties(${RESOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
|
||||
|
||||
set_target_properties(${EXECUTABLE_NAME} PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/os/ios/Info.plist"
|
||||
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
|
||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.alexbatalov.fallout2-ce"
|
||||
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
||||
)
|
||||
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "${EXECUTABLE_NAME}")
|
||||
set(MACOSX_BUNDLE_DISPLAY_NAME "Fallout 2")
|
||||
else()
|
||||
set(RESOURCES
|
||||
"os/macos/fallout2-ce.icns"
|
||||
)
|
||||
|
||||
target_sources(${EXECUTABLE_NAME} PUBLIC ${RESOURCES})
|
||||
set_source_files_properties(${RESOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
|
||||
|
||||
set_target_properties(${EXECUTABLE_NAME} PROPERTIES
|
||||
OUTPUT_NAME "Fallout II Community Edition"
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/os/macos/Info.plist"
|
||||
XCODE_ATTRIBUTE_EXECUTABLE_NAME "${EXECUTABLE_NAME}"
|
||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.alexbatalov.fallout2-ce"
|
||||
)
|
||||
|
||||
set(MACOSX_BUNDLE_ICON_FILE "fallout2-ce.icns")
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "Fallout II: Community Edition")
|
||||
set(MACOSX_BUNDLE_DISPLAY_NAME "Fallout II")
|
||||
endif()
|
||||
|
||||
set_target_properties(${EXECUTABLE_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/os/macos/Info.plist")
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.alexbatalov.fallout2-ce")
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "${EXECUTABLE_NAME}")
|
||||
set(MACOSX_BUNDLE_ICON_FILE "fallout2-ce.icns")
|
||||
set(MACOSX_BUNDLE_DISPLAY_NAME "Fallout 2")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "1.1.0")
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "1.1.0")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "1.2.0")
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "1.2.0")
|
||||
endif()
|
||||
|
||||
add_subdirectory("third_party/fpattern")
|
||||
@@ -315,23 +375,27 @@ target_link_libraries(${EXECUTABLE_NAME} ${SDL2_LIBRARIES})
|
||||
target_include_directories(${EXECUTABLE_NAME} PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
|
||||
if(APPLE)
|
||||
install(TARGETS ${EXECUTABLE_NAME} DESTINATION .)
|
||||
install(CODE "
|
||||
include(BundleUtilities)
|
||||
fixup_bundle(${CMAKE_BINARY_DIR}/${MACOSX_BUNDLE_BUNDLE_NAME}.app \"\" \"\")
|
||||
"
|
||||
COMPONENT Runtime)
|
||||
if(IOS)
|
||||
install(TARGETS ${EXECUTABLE_NAME} DESTINATION "Payload")
|
||||
|
||||
if (CPACK_BUNDLE_APPLE_CERT_APP)
|
||||
install(CODE "
|
||||
execute_process(COMMAND codesign --deep --force --options runtime --sign \"${CPACK_BUNDLE_APPLE_CERT_APP}\" ${CMAKE_BINARY_DIR}/${MACOSX_BUNDLE_BUNDLE_NAME}.app)
|
||||
"
|
||||
COMPONENT Runtime)
|
||||
set(CPACK_GENERATOR "ZIP")
|
||||
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF)
|
||||
set(CPACK_PACKAGE_FILE_NAME "fallout2-ce")
|
||||
set(CPACK_ARCHIVE_FILE_EXTENSION "ipa")
|
||||
else()
|
||||
install(TARGETS ${EXECUTABLE_NAME} DESTINATION .)
|
||||
|
||||
if(CPACK_BUNDLE_APPLE_CERT_APP)
|
||||
install(CODE "
|
||||
execute_process(COMMAND codesign --deep --force --options runtime --sign \"${CPACK_BUNDLE_APPLE_CERT_APP}\" ${CMAKE_BINARY_DIR}/Fallout II Community Edition.app)
|
||||
"
|
||||
COMPONENT Runtime)
|
||||
endif()
|
||||
|
||||
set(CPACK_GENERATOR "DragNDrop")
|
||||
set(CPACK_DMG_DISABLE_APPLICATIONS_SYMLINK ON)
|
||||
set(CPACK_PACKAGE_FILE_NAME "Fallout II Community Edition")
|
||||
endif()
|
||||
|
||||
set(CPACK_GENERATOR "DragNDrop")
|
||||
set(CPACK_DMG_DISABLE_APPLICATIONS_SYMLINK ON)
|
||||
set(CPACK_PACKAGE_FILE_NAME "fallout2-ce")
|
||||
|
||||
include(CPack)
|
||||
endif()
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
# Fallout 2 Community Edition
|
||||
|
||||
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.
|
||||
|
||||
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) likely work, although there is no complete walkthrough confirmation yet. [Fallout 2 Restoration Project](https://github.com/BGforgeNet/Fallout2_Restoration_Project), [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).
|
||||
|
||||
## Installation
|
||||
|
||||
You must own the game to play. Purchase your copy on [GOG](https://www.gog.com/game/fallout_2) or [Steam](https://store.steampowered.com/app/38410). Download latest release or build from source.
|
||||
You must own the game to play. Purchase your copy on [GOG](https://www.gog.com/game/fallout_2) or [Steam](https://store.steampowered.com/app/38410). Download latest [release](https://github.com/alexbatalov/fallout2-ce/releases) or build from source. You can also check latest [debug](https://github.com/alexbatalov/fallout2-ce/actions) build intended for testers.
|
||||
|
||||
### Windows
|
||||
|
||||
@@ -36,13 +42,53 @@ $ sudo apt install libsdl2-2.0-0
|
||||
|
||||
### Android
|
||||
|
||||
> **NOTE**: Fallout 2 was designed with mouse in mind. There are many controls that require precise cursor positioning, which is not possible with fingers. When playing on Android you'll use fingers to move mouse cursor, not a character, or a map. Double tap to "click" left mouse button in the current cursor position, triple tap to "click" right mouse button. It might feel awkward at first, but it's super handy - you can play with just a thumb. This is not set in stone and might change in the future.
|
||||
> **NOTE**: Fallout 2 was designed with mouse in mind. There are many controls that require precise cursor positioning, which is not possible with fingers. Current control scheme resembles trackpad usage:
|
||||
- One finger moves mouse cursor around.
|
||||
- Tap one finger for left mouse click.
|
||||
- Tap two fingers for right mouse click (switches mouse cursor mode).
|
||||
- Move two fingers to scroll current view (map view, worldmap view, inventory scrollers).
|
||||
|
||||
- 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.
|
||||
> **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 `fallout2-ce.apk` and copy it 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. The game will start automatically.
|
||||
- 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.
|
||||
|
||||
### iOS
|
||||
|
||||
> **NOTE**: See Android note on controls.
|
||||
|
||||
- Download `fallout2-ce.ipa`. Use sideloading applications ([AltStore](https://altstore.io/) or [Sideloadly](https://sideloadly.io/)) to install it to your device. Alternatively you can always build from source with your own signing certificate.
|
||||
|
||||
- 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)).
|
||||
|
||||
## 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 `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.
|
||||
|
||||
The second configuration file is `f2_res.ini`. Use it to change game window size and enable/disable fullscreen mode.
|
||||
|
||||
```ini
|
||||
[MAIN]
|
||||
SCR_WIDTH=1280
|
||||
SCR_HEIGHT=720
|
||||
WINDOWED=1
|
||||
```
|
||||
|
||||
Recommendations:
|
||||
- **Desktops**: Use any size you see fit.
|
||||
- **Tablets**: Set these values to logical resolution of your device, for example iPad Pro 11 is 1668x2388 (pixels), but it's logical resolution is 834x1194 (points).
|
||||
- **Mobile phones**: Set height to 480, calculate width according to your device screen (aspect) ratio, for example Samsung S21 is 20:9 device, so the width should be 480 * 20 / 9 = 1067.
|
||||
|
||||
In time this stuff will receive in-game interface, right now you have to do it manually.
|
||||
|
||||
The third 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. Currently only a small subset of these settings are actually implemented.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,8 +9,8 @@ android {
|
||||
applicationId 'com.alexbatalov.fallout2ce'
|
||||
minSdk 21
|
||||
targetSdk 32
|
||||
versionCode 2
|
||||
versionName '1.1.0'
|
||||
versionCode 3
|
||||
versionName '1.2.0'
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments '-DANDROID_STL=c++_static'
|
||||
|
||||
@@ -15,13 +15,9 @@ import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -37,11 +33,8 @@ import android.view.Display;
|
||||
import android.view.Gravity;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.PointerIcon;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
@@ -51,6 +44,7 @@ import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
@@ -65,6 +59,9 @@ import java.util.Locale;
|
||||
*/
|
||||
public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener {
|
||||
private static final String TAG = "SDL";
|
||||
private static final int SDL_MAJOR_VERSION = 2;
|
||||
private static final int SDL_MINOR_VERSION = 26;
|
||||
private static final int SDL_MICRO_VERSION = 1;
|
||||
/*
|
||||
// Display InputType.SOURCE/CLASS of events and devices
|
||||
//
|
||||
@@ -213,7 +210,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||
// Main components
|
||||
protected static SDLActivity mSingleton;
|
||||
protected static SDLSurface mSurface;
|
||||
protected static View mTextEdit;
|
||||
protected static DummyEdit mTextEdit;
|
||||
protected static boolean mScreenKeyboardShown;
|
||||
protected static ViewGroup mLayout;
|
||||
protected static SDLClipboardHandler mClipboardHandler;
|
||||
@@ -314,6 +311,10 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||
mNextNativeState = NativeState.INIT;
|
||||
mCurrentNativeState = NativeState.INIT;
|
||||
}
|
||||
|
||||
protected SDLSurface createSDLSurface(Context context) {
|
||||
return new SDLSurface(context);
|
||||
}
|
||||
|
||||
// Setup
|
||||
@Override
|
||||
@@ -344,8 +345,18 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||
errorMsgBrokenLib = e.getMessage();
|
||||
}
|
||||
|
||||
if (mBrokenLibraries)
|
||||
{
|
||||
if (!mBrokenLibraries) {
|
||||
String expected_version = String.valueOf(SDL_MAJOR_VERSION) + "." +
|
||||
String.valueOf(SDL_MINOR_VERSION) + "." +
|
||||
String.valueOf(SDL_MICRO_VERSION);
|
||||
String version = nativeGetVersion();
|
||||
if (!version.equals(expected_version)) {
|
||||
mBrokenLibraries = true;
|
||||
errorMsgBrokenLib = "SDL C/Java version mismatch (expected " + expected_version + ", got " + version + ")";
|
||||
}
|
||||
}
|
||||
|
||||
if (mBrokenLibraries) {
|
||||
mSingleton = this;
|
||||
AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
|
||||
dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall."
|
||||
@@ -382,7 +393,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||
mHIDDeviceManager = HIDDeviceManager.acquire(this);
|
||||
|
||||
// Set up the surface
|
||||
mSurface = new SDLSurface(getApplication());
|
||||
mSurface = createSDLSurface(getApplication());
|
||||
|
||||
mLayout = new RelativeLayout(this);
|
||||
mLayout.addView(mSurface);
|
||||
@@ -886,6 +897,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||
}
|
||||
|
||||
// C functions we call
|
||||
public static native String nativeGetVersion();
|
||||
public static native int nativeSetupJNI();
|
||||
public static native int nativeRunMain(String library, String function, Object arguments);
|
||||
public static native void nativeLowMemory();
|
||||
@@ -1220,8 +1232,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||
}
|
||||
|
||||
// This method is called by SDLControllerManager's API 26 Generic Motion Handler.
|
||||
public static View getContentView()
|
||||
{
|
||||
public static View getContentView() {
|
||||
return mLayout;
|
||||
}
|
||||
|
||||
@@ -1292,6 +1303,77 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||
return event.isPrintingKey() || event.getKeyCode() == KeyEvent.KEYCODE_SPACE;
|
||||
}
|
||||
|
||||
public static boolean handleKeyEvent(View v, int keyCode, KeyEvent event, InputConnection ic) {
|
||||
int deviceId = event.getDeviceId();
|
||||
int source = event.getSource();
|
||||
|
||||
if (source == InputDevice.SOURCE_UNKNOWN) {
|
||||
InputDevice device = InputDevice.getDevice(deviceId);
|
||||
if (device != null) {
|
||||
source = device.getSources();
|
||||
}
|
||||
}
|
||||
|
||||
// if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
// Log.v("SDL", "key down: " + keyCode + ", deviceId = " + deviceId + ", source = " + source);
|
||||
// } else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
// Log.v("SDL", "key up: " + keyCode + ", deviceId = " + deviceId + ", source = " + source);
|
||||
// }
|
||||
|
||||
// Dispatch the different events depending on where they come from
|
||||
// Some SOURCE_JOYSTICK, SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
|
||||
// So, we try to process them as JOYSTICK/DPAD/GAMEPAD events first, if that fails we try them as KEYBOARD
|
||||
//
|
||||
// Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
|
||||
// SOURCE_JOYSTICK, while its key events arrive from the keyboard source
|
||||
// So, retrieve the device itself and check all of its sources
|
||||
if (SDLControllerManager.isDeviceSDLJoystick(deviceId)) {
|
||||
// Note that we process events with specific key codes here
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
if (SDLControllerManager.onNativePadDown(deviceId, keyCode) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
if (SDLControllerManager.onNativePadUp(deviceId, keyCode) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((source & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) {
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
if (isTextInputEvent(event)) {
|
||||
if (ic != null) {
|
||||
ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1);
|
||||
} else {
|
||||
SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1);
|
||||
}
|
||||
}
|
||||
onNativeKeyDown(keyCode);
|
||||
return true;
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
onNativeKeyUp(keyCode);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
|
||||
// on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
|
||||
// they are ignored here because sending them as mouse input to SDL is messy
|
||||
if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
|
||||
switch (event.getAction()) {
|
||||
case KeyEvent.ACTION_DOWN:
|
||||
case KeyEvent.ACTION_UP:
|
||||
// mark the event as handled or it will be handled by system
|
||||
// handling KEYCODE_BACK by system will call onBackPressed()
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
@@ -1809,463 +1891,6 @@ class SDLMain implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
SDLSurface. This is what we draw on, so we need to know when it's created
|
||||
in order to do anything useful.
|
||||
|
||||
Because of this, that's where we set up the SDL thread
|
||||
*/
|
||||
class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
|
||||
View.OnKeyListener, View.OnTouchListener, SensorEventListener {
|
||||
|
||||
// Sensors
|
||||
protected SensorManager mSensorManager;
|
||||
protected Display mDisplay;
|
||||
|
||||
// Keep track of the surface size to normalize touch events
|
||||
protected float mWidth, mHeight;
|
||||
|
||||
// Is SurfaceView ready for rendering
|
||||
public boolean mIsSurfaceReady;
|
||||
|
||||
// Startup
|
||||
public SDLSurface(Context context) {
|
||||
super(context);
|
||||
getHolder().addCallback(this);
|
||||
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
setOnKeyListener(this);
|
||||
setOnTouchListener(this);
|
||||
|
||||
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
||||
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
|
||||
|
||||
setOnGenericMotionListener(SDLActivity.getMotionListener());
|
||||
|
||||
// Some arbitrary defaults to avoid a potential division by zero
|
||||
mWidth = 1.0f;
|
||||
mHeight = 1.0f;
|
||||
|
||||
mIsSurfaceReady = false;
|
||||
}
|
||||
|
||||
public void handlePause() {
|
||||
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
|
||||
}
|
||||
|
||||
public void handleResume() {
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
setOnKeyListener(this);
|
||||
setOnTouchListener(this);
|
||||
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
|
||||
}
|
||||
|
||||
public Surface getNativeSurface() {
|
||||
return getHolder().getSurface();
|
||||
}
|
||||
|
||||
// Called when we have a valid drawing surface
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
Log.v("SDL", "surfaceCreated()");
|
||||
SDLActivity.onNativeSurfaceCreated();
|
||||
}
|
||||
|
||||
// Called when we lose the surface
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
Log.v("SDL", "surfaceDestroyed()");
|
||||
|
||||
// Transition to pause, if needed
|
||||
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
|
||||
SDLActivity.handleNativeState();
|
||||
|
||||
mIsSurfaceReady = false;
|
||||
SDLActivity.onNativeSurfaceDestroyed();
|
||||
}
|
||||
|
||||
// Called when the surface is resized
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder,
|
||||
int format, int width, int height) {
|
||||
Log.v("SDL", "surfaceChanged()");
|
||||
|
||||
if (SDLActivity.mSingleton == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
int nDeviceWidth = width;
|
||||
int nDeviceHeight = height;
|
||||
try
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= 17) {
|
||||
DisplayMetrics realMetrics = new DisplayMetrics();
|
||||
mDisplay.getRealMetrics( realMetrics );
|
||||
nDeviceWidth = realMetrics.widthPixels;
|
||||
nDeviceHeight = realMetrics.heightPixels;
|
||||
}
|
||||
} catch(Exception ignored) {
|
||||
}
|
||||
|
||||
synchronized(SDLActivity.getContext()) {
|
||||
// In case we're waiting on a size change after going fullscreen, send a notification.
|
||||
SDLActivity.getContext().notifyAll();
|
||||
}
|
||||
|
||||
Log.v("SDL", "Window size: " + width + "x" + height);
|
||||
Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
|
||||
SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate());
|
||||
SDLActivity.onNativeResize();
|
||||
|
||||
// Prevent a screen distortion glitch,
|
||||
// for instance when the device is in Landscape and a Portrait App is resumed.
|
||||
boolean skip = false;
|
||||
int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
|
||||
|
||||
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
|
||||
if (mWidth > mHeight) {
|
||||
skip = true;
|
||||
}
|
||||
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
|
||||
if (mWidth < mHeight) {
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Special Patch for Square Resolution: Black Berry Passport
|
||||
if (skip) {
|
||||
double min = Math.min(mWidth, mHeight);
|
||||
double max = Math.max(mWidth, mHeight);
|
||||
|
||||
if (max / min < 1.20) {
|
||||
Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
|
||||
skip = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't skip in MultiWindow.
|
||||
if (skip) {
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
if (SDLActivity.mSingleton.isInMultiWindowMode()) {
|
||||
Log.v("SDL", "Don't skip in Multi-Window");
|
||||
skip = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
Log.v("SDL", "Skip .. Surface is not ready.");
|
||||
mIsSurfaceReady = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
|
||||
SDLActivity.onNativeSurfaceChanged();
|
||||
|
||||
/* Surface is ready */
|
||||
mIsSurfaceReady = true;
|
||||
|
||||
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
|
||||
SDLActivity.handleNativeState();
|
||||
}
|
||||
|
||||
// Key events
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
|
||||
int deviceId = event.getDeviceId();
|
||||
int source = event.getSource();
|
||||
|
||||
if (source == InputDevice.SOURCE_UNKNOWN) {
|
||||
InputDevice device = InputDevice.getDevice(deviceId);
|
||||
if (device != null) {
|
||||
source = device.getSources();
|
||||
}
|
||||
}
|
||||
|
||||
// if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
// Log.v("SDL", "key down: " + keyCode + ", deviceId = " + deviceId + ", source = " + source);
|
||||
// } else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
// Log.v("SDL", "key up: " + keyCode + ", deviceId = " + deviceId + ", source = " + source);
|
||||
// }
|
||||
|
||||
// Dispatch the different events depending on where they come from
|
||||
// Some SOURCE_JOYSTICK, SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
|
||||
// So, we try to process them as JOYSTICK/DPAD/GAMEPAD events first, if that fails we try them as KEYBOARD
|
||||
//
|
||||
// Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
|
||||
// SOURCE_JOYSTICK, while its key events arrive from the keyboard source
|
||||
// So, retrieve the device itself and check all of its sources
|
||||
if (SDLControllerManager.isDeviceSDLJoystick(deviceId)) {
|
||||
// Note that we process events with specific key codes here
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
if (SDLControllerManager.onNativePadDown(deviceId, keyCode) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
if (SDLControllerManager.onNativePadUp(deviceId, keyCode) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((source & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) {
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
if (SDLActivity.isTextInputEvent(event)) {
|
||||
SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1);
|
||||
}
|
||||
SDLActivity.onNativeKeyDown(keyCode);
|
||||
return true;
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
SDLActivity.onNativeKeyUp(keyCode);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
|
||||
// on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
|
||||
// they are ignored here because sending them as mouse input to SDL is messy
|
||||
if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
|
||||
switch (event.getAction()) {
|
||||
case KeyEvent.ACTION_DOWN:
|
||||
case KeyEvent.ACTION_UP:
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
SDLActivity.onNativeMouse(MotionEvent.BUTTON_SECONDARY, MotionEvent.ACTION_DOWN, 0, 0, true);
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
SDLActivity.onNativeMouse(MotionEvent.BUTTON_SECONDARY, MotionEvent.ACTION_UP, 0, 0, true);
|
||||
}
|
||||
}
|
||||
|
||||
// mark the event as handled or it will be handled by system
|
||||
// handling KEYCODE_BACK by system will call onBackPressed()
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Touch events
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
/* Ref: http://developer.android.com/training/gestures/multi.html */
|
||||
int touchDevId = event.getDeviceId();
|
||||
final int pointerCount = event.getPointerCount();
|
||||
int action = event.getActionMasked();
|
||||
int pointerFingerId;
|
||||
int i = -1;
|
||||
float x,y,p;
|
||||
|
||||
/*
|
||||
* Prevent id to be -1, since it's used in SDL internal for synthetic events
|
||||
* Appears when using Android emulator, eg:
|
||||
* adb shell input mouse tap 100 100
|
||||
* adb shell input touchscreen tap 100 100
|
||||
*/
|
||||
if (touchDevId < 0) {
|
||||
touchDevId -= 1;
|
||||
}
|
||||
|
||||
// 12290 = Samsung DeX mode desktop mouse
|
||||
// 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN
|
||||
// 0x2 = SOURCE_CLASS_POINTER
|
||||
if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) {
|
||||
int mouseButton = 1;
|
||||
try {
|
||||
Object object = event.getClass().getMethod("getButtonState").invoke(event);
|
||||
if (object != null) {
|
||||
mouseButton = (Integer) object;
|
||||
}
|
||||
} catch(Exception ignored) {
|
||||
}
|
||||
|
||||
// We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
|
||||
// if we are. We'll leverage our existing mouse motion listener
|
||||
SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
|
||||
x = motionListener.getEventX(event);
|
||||
y = motionListener.getEventY(event);
|
||||
|
||||
SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
|
||||
} else {
|
||||
switch(action) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
for (i = 0; i < pointerCount; i++) {
|
||||
pointerFingerId = event.getPointerId(i);
|
||||
x = event.getX(i) / mWidth;
|
||||
y = event.getY(i) / mHeight;
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// Primary pointer up/down, the index is always zero
|
||||
i = 0;
|
||||
/* fallthrough */
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
// Non primary pointer up/down
|
||||
if (i == -1) {
|
||||
i = event.getActionIndex();
|
||||
}
|
||||
|
||||
pointerFingerId = event.getPointerId(i);
|
||||
x = event.getX(i) / mWidth;
|
||||
y = event.getY(i) / mHeight;
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
for (i = 0; i < pointerCount; i++) {
|
||||
pointerFingerId = event.getPointerId(i);
|
||||
x = event.getX(i) / mWidth;
|
||||
y = event.getY(i) / mHeight;
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sensor events
|
||||
public void enableSensor(int sensortype, boolean enabled) {
|
||||
// TODO: This uses getDefaultSensor - what if we have >1 accels?
|
||||
if (enabled) {
|
||||
mSensorManager.registerListener(this,
|
||||
mSensorManager.getDefaultSensor(sensortype),
|
||||
SensorManager.SENSOR_DELAY_GAME, null);
|
||||
} else {
|
||||
mSensorManager.unregisterListener(this,
|
||||
mSensorManager.getDefaultSensor(sensortype));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
|
||||
|
||||
// Since we may have an orientation set, we won't receive onConfigurationChanged events.
|
||||
// We thus should check here.
|
||||
int newOrientation;
|
||||
|
||||
float x, y;
|
||||
switch (mDisplay.getRotation()) {
|
||||
case Surface.ROTATION_90:
|
||||
x = -event.values[1];
|
||||
y = event.values[0];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
|
||||
break;
|
||||
case Surface.ROTATION_270:
|
||||
x = event.values[1];
|
||||
y = -event.values[0];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
|
||||
break;
|
||||
case Surface.ROTATION_180:
|
||||
x = -event.values[0];
|
||||
y = -event.values[1];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
|
||||
break;
|
||||
case Surface.ROTATION_0:
|
||||
default:
|
||||
x = event.values[0];
|
||||
y = event.values[1];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (newOrientation != SDLActivity.mCurrentOrientation) {
|
||||
SDLActivity.mCurrentOrientation = newOrientation;
|
||||
SDLActivity.onNativeOrientationChanged(newOrientation);
|
||||
}
|
||||
|
||||
SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
|
||||
y / SensorManager.GRAVITY_EARTH,
|
||||
event.values[2] / SensorManager.GRAVITY_EARTH);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Captured pointer events for API 26.
|
||||
public boolean onCapturedPointerEvent(MotionEvent event)
|
||||
{
|
||||
int action = event.getActionMasked();
|
||||
|
||||
float x, y;
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
x = event.getX(0);
|
||||
y = event.getY(0);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, true);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_BUTTON_PRESS:
|
||||
case MotionEvent.ACTION_BUTTON_RELEASE:
|
||||
|
||||
// Change our action value to what SDL's code expects.
|
||||
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
|
||||
action = MotionEvent.ACTION_DOWN;
|
||||
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
|
||||
action = MotionEvent.ACTION_UP;
|
||||
}
|
||||
|
||||
x = event.getX(0);
|
||||
y = event.getY(0);
|
||||
int button = event.getButtonState();
|
||||
|
||||
SDLActivity.onNativeMouse(button, action, x, y, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* This is a fake invisible editor view that receives the input and defines the
|
||||
* pan&scan region
|
||||
*/
|
||||
@@ -2286,21 +1911,7 @@ class DummyEdit extends View implements View.OnKeyListener {
|
||||
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
/*
|
||||
* This handles the hardware keyboard input
|
||||
*/
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
if (SDLActivity.isTextInputEvent(event)) {
|
||||
ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1);
|
||||
return true;
|
||||
}
|
||||
SDLActivity.onNativeKeyDown(keyCode);
|
||||
return true;
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
SDLActivity.onNativeKeyUp(keyCode);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return SDLActivity.handleKeyEvent(v, keyCode, event, ic);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -2324,9 +1935,10 @@ class DummyEdit extends View implements View.OnKeyListener {
|
||||
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
||||
ic = new SDLInputConnection(this, true);
|
||||
|
||||
outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
|
||||
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
|
||||
| EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */;
|
||||
outAttrs.inputType = InputType.TYPE_CLASS_TEXT |
|
||||
InputType.TYPE_TEXT_FLAG_MULTI_LINE;
|
||||
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI |
|
||||
EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */;
|
||||
|
||||
return ic;
|
||||
}
|
||||
@@ -2334,9 +1946,17 @@ class DummyEdit extends View implements View.OnKeyListener {
|
||||
|
||||
class SDLInputConnection extends BaseInputConnection {
|
||||
|
||||
protected EditText mEditText;
|
||||
protected String mCommittedText = "";
|
||||
|
||||
public SDLInputConnection(View targetView, boolean fullEditor) {
|
||||
super(targetView, fullEditor);
|
||||
mEditText = new EditText(SDL.getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editable getEditable() {
|
||||
return mEditText.getEditableText();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2359,79 +1979,84 @@ class SDLInputConnection extends BaseInputConnection {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return super.sendKeyEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commitText(CharSequence text, int newCursorPosition) {
|
||||
|
||||
/* Generate backspaces for the text we're going to replace */
|
||||
final Editable content = getEditable();
|
||||
if (content != null) {
|
||||
int a = getComposingSpanStart(content);
|
||||
int b = getComposingSpanEnd(content);
|
||||
if (a == -1 || b == -1) {
|
||||
a = Selection.getSelectionStart(content);
|
||||
b = Selection.getSelectionEnd(content);
|
||||
}
|
||||
if (a < 0) a = 0;
|
||||
if (b < 0) b = 0;
|
||||
if (b < a) {
|
||||
int tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
int backspaces = (b - a);
|
||||
|
||||
for (int i = 0; i < backspaces; i++) {
|
||||
nativeGenerateScancodeForUnichar('\b');
|
||||
}
|
||||
if (!super.commitText(text, newCursorPosition)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
if (c == '\n') {
|
||||
if (SDLActivity.onNativeSoftReturnKey()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
nativeGenerateScancodeForUnichar(c);
|
||||
}
|
||||
|
||||
SDLInputConnection.nativeCommitText(text.toString(), newCursorPosition);
|
||||
|
||||
return super.commitText(text, newCursorPosition);
|
||||
updateText();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setComposingText(CharSequence text, int newCursorPosition) {
|
||||
if (!super.setComposingText(text, newCursorPosition)) {
|
||||
return false;
|
||||
}
|
||||
updateText();
|
||||
return true;
|
||||
}
|
||||
|
||||
nativeSetComposingText(text.toString(), newCursorPosition);
|
||||
@Override
|
||||
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
|
||||
if (!super.deleteSurroundingText(beforeLength, afterLength)) {
|
||||
return false;
|
||||
}
|
||||
updateText();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.setComposingText(text, newCursorPosition);
|
||||
protected void updateText() {
|
||||
final Editable content = getEditable();
|
||||
if (content == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String text = content.toString();
|
||||
int compareLength = Math.min(text.length(), mCommittedText.length());
|
||||
int matchLength, offset;
|
||||
|
||||
/* Backspace over characters that are no longer in the string */
|
||||
for (matchLength = 0; matchLength < compareLength; ) {
|
||||
int codePoint = mCommittedText.codePointAt(matchLength);
|
||||
if (codePoint != text.codePointAt(matchLength)) {
|
||||
break;
|
||||
}
|
||||
matchLength += Character.charCount(codePoint);
|
||||
}
|
||||
/* FIXME: This doesn't handle graphemes, like '🌬️' */
|
||||
for (offset = matchLength; offset < mCommittedText.length(); ) {
|
||||
int codePoint = mCommittedText.codePointAt(offset);
|
||||
nativeGenerateScancodeForUnichar('\b');
|
||||
offset += Character.charCount(codePoint);
|
||||
}
|
||||
|
||||
if (matchLength < text.length()) {
|
||||
String pendingText = text.subSequence(matchLength, text.length()).toString();
|
||||
for (offset = 0; offset < pendingText.length(); ) {
|
||||
int codePoint = pendingText.codePointAt(offset);
|
||||
if (codePoint == '\n') {
|
||||
if (SDLActivity.onNativeSoftReturnKey()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Higher code points don't generate simulated scancodes */
|
||||
if (codePoint < 128) {
|
||||
nativeGenerateScancodeForUnichar((char)codePoint);
|
||||
}
|
||||
offset += Character.charCount(codePoint);
|
||||
}
|
||||
SDLInputConnection.nativeCommitText(pendingText, 0);
|
||||
}
|
||||
mCommittedText = text;
|
||||
}
|
||||
|
||||
public static native void nativeCommitText(String text, int newCursorPosition);
|
||||
|
||||
public native void nativeGenerateScancodeForUnichar(char c);
|
||||
|
||||
public native void nativeSetComposingText(String text, int newCursorPosition);
|
||||
|
||||
@Override
|
||||
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
|
||||
// Workaround to capture backspace key. Ref: http://stackoverflow.com/questions/14560344/android-backspace-in-webview-baseinputconnection
|
||||
// and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
|
||||
if (beforeLength > 0 && afterLength == 0) {
|
||||
// backspace(s)
|
||||
while (beforeLength-- > 0) {
|
||||
nativeGenerateScancodeForUnichar('\b');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.deleteSurroundingText(beforeLength, afterLength);
|
||||
}
|
||||
public static native void nativeGenerateScancodeForUnichar(char c);
|
||||
}
|
||||
|
||||
class SDLClipboardHandler implements
|
||||
|
||||
@@ -0,0 +1,405 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.Build;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
|
||||
/**
|
||||
SDLSurface. This is what we draw on, so we need to know when it's created
|
||||
in order to do anything useful.
|
||||
|
||||
Because of this, that's where we set up the SDL thread
|
||||
*/
|
||||
public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
|
||||
View.OnKeyListener, View.OnTouchListener, SensorEventListener {
|
||||
|
||||
// Sensors
|
||||
protected SensorManager mSensorManager;
|
||||
protected Display mDisplay;
|
||||
|
||||
// Keep track of the surface size to normalize touch events
|
||||
protected float mWidth, mHeight;
|
||||
|
||||
// Is SurfaceView ready for rendering
|
||||
public boolean mIsSurfaceReady;
|
||||
|
||||
// Startup
|
||||
public SDLSurface(Context context) {
|
||||
super(context);
|
||||
getHolder().addCallback(this);
|
||||
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
setOnKeyListener(this);
|
||||
setOnTouchListener(this);
|
||||
|
||||
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
||||
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
|
||||
|
||||
setOnGenericMotionListener(SDLActivity.getMotionListener());
|
||||
|
||||
// Some arbitrary defaults to avoid a potential division by zero
|
||||
mWidth = 1.0f;
|
||||
mHeight = 1.0f;
|
||||
|
||||
mIsSurfaceReady = false;
|
||||
}
|
||||
|
||||
public void handlePause() {
|
||||
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
|
||||
}
|
||||
|
||||
public void handleResume() {
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
setOnKeyListener(this);
|
||||
setOnTouchListener(this);
|
||||
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
|
||||
}
|
||||
|
||||
public Surface getNativeSurface() {
|
||||
return getHolder().getSurface();
|
||||
}
|
||||
|
||||
// Called when we have a valid drawing surface
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
Log.v("SDL", "surfaceCreated()");
|
||||
SDLActivity.onNativeSurfaceCreated();
|
||||
}
|
||||
|
||||
// Called when we lose the surface
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
Log.v("SDL", "surfaceDestroyed()");
|
||||
|
||||
// Transition to pause, if needed
|
||||
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
|
||||
SDLActivity.handleNativeState();
|
||||
|
||||
mIsSurfaceReady = false;
|
||||
SDLActivity.onNativeSurfaceDestroyed();
|
||||
}
|
||||
|
||||
// Called when the surface is resized
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder,
|
||||
int format, int width, int height) {
|
||||
Log.v("SDL", "surfaceChanged()");
|
||||
|
||||
if (SDLActivity.mSingleton == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
int nDeviceWidth = width;
|
||||
int nDeviceHeight = height;
|
||||
try
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= 17) {
|
||||
DisplayMetrics realMetrics = new DisplayMetrics();
|
||||
mDisplay.getRealMetrics( realMetrics );
|
||||
nDeviceWidth = realMetrics.widthPixels;
|
||||
nDeviceHeight = realMetrics.heightPixels;
|
||||
}
|
||||
} catch(Exception ignored) {
|
||||
}
|
||||
|
||||
synchronized(SDLActivity.getContext()) {
|
||||
// In case we're waiting on a size change after going fullscreen, send a notification.
|
||||
SDLActivity.getContext().notifyAll();
|
||||
}
|
||||
|
||||
Log.v("SDL", "Window size: " + width + "x" + height);
|
||||
Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
|
||||
SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate());
|
||||
SDLActivity.onNativeResize();
|
||||
|
||||
// Prevent a screen distortion glitch,
|
||||
// for instance when the device is in Landscape and a Portrait App is resumed.
|
||||
boolean skip = false;
|
||||
int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
|
||||
|
||||
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
|
||||
if (mWidth > mHeight) {
|
||||
skip = true;
|
||||
}
|
||||
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
|
||||
if (mWidth < mHeight) {
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Special Patch for Square Resolution: Black Berry Passport
|
||||
if (skip) {
|
||||
double min = Math.min(mWidth, mHeight);
|
||||
double max = Math.max(mWidth, mHeight);
|
||||
|
||||
if (max / min < 1.20) {
|
||||
Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
|
||||
skip = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't skip in MultiWindow.
|
||||
if (skip) {
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
if (SDLActivity.mSingleton.isInMultiWindowMode()) {
|
||||
Log.v("SDL", "Don't skip in Multi-Window");
|
||||
skip = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
Log.v("SDL", "Skip .. Surface is not ready.");
|
||||
mIsSurfaceReady = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
|
||||
SDLActivity.onNativeSurfaceChanged();
|
||||
|
||||
/* Surface is ready */
|
||||
mIsSurfaceReady = true;
|
||||
|
||||
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
|
||||
SDLActivity.handleNativeState();
|
||||
}
|
||||
|
||||
// Key events
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
return SDLActivity.handleKeyEvent(v, keyCode, event, null);
|
||||
}
|
||||
|
||||
// Touch events
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
/* Ref: http://developer.android.com/training/gestures/multi.html */
|
||||
int touchDevId = event.getDeviceId();
|
||||
final int pointerCount = event.getPointerCount();
|
||||
int action = event.getActionMasked();
|
||||
int pointerFingerId;
|
||||
int i = -1;
|
||||
float x,y,p;
|
||||
|
||||
/*
|
||||
* Prevent id to be -1, since it's used in SDL internal for synthetic events
|
||||
* Appears when using Android emulator, eg:
|
||||
* adb shell input mouse tap 100 100
|
||||
* adb shell input touchscreen tap 100 100
|
||||
*/
|
||||
if (touchDevId < 0) {
|
||||
touchDevId -= 1;
|
||||
}
|
||||
|
||||
// 12290 = Samsung DeX mode desktop mouse
|
||||
// 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN
|
||||
// 0x2 = SOURCE_CLASS_POINTER
|
||||
if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) {
|
||||
int mouseButton = 1;
|
||||
try {
|
||||
Object object = event.getClass().getMethod("getButtonState").invoke(event);
|
||||
if (object != null) {
|
||||
mouseButton = (Integer) object;
|
||||
}
|
||||
} catch(Exception ignored) {
|
||||
}
|
||||
|
||||
// We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
|
||||
// if we are. We'll leverage our existing mouse motion listener
|
||||
SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
|
||||
x = motionListener.getEventX(event);
|
||||
y = motionListener.getEventY(event);
|
||||
|
||||
SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
|
||||
} else {
|
||||
switch(action) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
for (i = 0; i < pointerCount; i++) {
|
||||
pointerFingerId = event.getPointerId(i);
|
||||
x = event.getX(i) / mWidth;
|
||||
y = event.getY(i) / mHeight;
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// Primary pointer up/down, the index is always zero
|
||||
i = 0;
|
||||
/* fallthrough */
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
// Non primary pointer up/down
|
||||
if (i == -1) {
|
||||
i = event.getActionIndex();
|
||||
}
|
||||
|
||||
pointerFingerId = event.getPointerId(i);
|
||||
x = event.getX(i) / mWidth;
|
||||
y = event.getY(i) / mHeight;
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
for (i = 0; i < pointerCount; i++) {
|
||||
pointerFingerId = event.getPointerId(i);
|
||||
x = event.getX(i) / mWidth;
|
||||
y = event.getY(i) / mHeight;
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sensor events
|
||||
public void enableSensor(int sensortype, boolean enabled) {
|
||||
// TODO: This uses getDefaultSensor - what if we have >1 accels?
|
||||
if (enabled) {
|
||||
mSensorManager.registerListener(this,
|
||||
mSensorManager.getDefaultSensor(sensortype),
|
||||
SensorManager.SENSOR_DELAY_GAME, null);
|
||||
} else {
|
||||
mSensorManager.unregisterListener(this,
|
||||
mSensorManager.getDefaultSensor(sensortype));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
|
||||
|
||||
// Since we may have an orientation set, we won't receive onConfigurationChanged events.
|
||||
// We thus should check here.
|
||||
int newOrientation;
|
||||
|
||||
float x, y;
|
||||
switch (mDisplay.getRotation()) {
|
||||
case Surface.ROTATION_90:
|
||||
x = -event.values[1];
|
||||
y = event.values[0];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
|
||||
break;
|
||||
case Surface.ROTATION_270:
|
||||
x = event.values[1];
|
||||
y = -event.values[0];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
|
||||
break;
|
||||
case Surface.ROTATION_180:
|
||||
x = -event.values[0];
|
||||
y = -event.values[1];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
|
||||
break;
|
||||
case Surface.ROTATION_0:
|
||||
default:
|
||||
x = event.values[0];
|
||||
y = event.values[1];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (newOrientation != SDLActivity.mCurrentOrientation) {
|
||||
SDLActivity.mCurrentOrientation = newOrientation;
|
||||
SDLActivity.onNativeOrientationChanged(newOrientation);
|
||||
}
|
||||
|
||||
SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
|
||||
y / SensorManager.GRAVITY_EARTH,
|
||||
event.values[2] / SensorManager.GRAVITY_EARTH);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Captured pointer events for API 26.
|
||||
public boolean onCapturedPointerEvent(MotionEvent event)
|
||||
{
|
||||
int action = event.getActionMasked();
|
||||
|
||||
float x, y;
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
x = event.getX(0);
|
||||
y = event.getY(0);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, true);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_BUTTON_PRESS:
|
||||
case MotionEvent.ACTION_BUTTON_RELEASE:
|
||||
|
||||
// Change our action value to what SDL's code expects.
|
||||
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
|
||||
action = MotionEvent.ACTION_DOWN;
|
||||
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
|
||||
action = MotionEvent.ACTION_UP;
|
||||
}
|
||||
|
||||
x = event.getX(0);
|
||||
y = event.getY(0);
|
||||
int button = event.getButtonState();
|
||||
|
||||
SDLActivity.onNativeMouse(button, action, x, y, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Vendored
+89
-89
@@ -1,89 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1009 KiB |
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "AppIcon.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>${MACOSX_BUNDLE_DISPLAY_NAME}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>11.0</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<string>True</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<true/>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<true/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="834" height="1194"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
+3
-1
@@ -3,7 +3,7 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>${MACOSX_BUNDLE_DISPLAY_NAME}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
@@ -28,6 +28,8 @@
|
||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<string>True</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.role-playing-games</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.11</string>
|
||||
<key>SDL_FILESYSTEM_BASE_DIR_TYPE</key>
|
||||
|
||||
+251
-231
@@ -13,7 +13,6 @@
|
||||
#include "debug.h"
|
||||
#include "display_monitor.h"
|
||||
#include "game.h"
|
||||
#include "game_config.h"
|
||||
#include "game_sound.h"
|
||||
#include "geometry.h"
|
||||
#include "interface.h"
|
||||
@@ -28,6 +27,7 @@
|
||||
#include "proto_types.h"
|
||||
#include "random.h"
|
||||
#include "scripts.h"
|
||||
#include "settings.h"
|
||||
#include "sfall_config.h"
|
||||
#include "skill.h"
|
||||
#include "stat.h"
|
||||
@@ -46,7 +46,7 @@ typedef enum ScienceRepairTargetType {
|
||||
} ScienceRepairTargetType;
|
||||
|
||||
// 0x5106D0
|
||||
static int _action_in_explode = 0;
|
||||
static bool _action_in_explode = false;
|
||||
|
||||
// 0x5106E0
|
||||
static const int gNormalDeathAnimations[DAMAGE_TYPE_COUNT] = {
|
||||
@@ -72,13 +72,13 @@ static const int gMaximumBloodDeathAnimations[DAMAGE_TYPE_COUNT] = {
|
||||
|
||||
static int actionKnockdown(Object* obj, int* anim, int maxDistance, int rotation, int delay);
|
||||
static int _action_blood(Object* obj, int anim, int delay);
|
||||
static int _pick_death(Object* attacker, Object* defender, Object* weapon, int damage, int anim, bool isFallingBack);
|
||||
static int _check_death(Object* obj, int anim, int minViolenceLevel, bool isFallingBack);
|
||||
static int _pick_death(Object* attacker, Object* defender, Object* weapon, int damage, int attackerAnimation, bool hitFromFront);
|
||||
static int _check_death(Object* obj, int anim, int minViolenceLevel, bool hitFromFront);
|
||||
static int _internal_destroy(Object* a1, Object* a2);
|
||||
static void _show_damage_to_object(Object* a1, int damage, int flags, Object* weapon, bool isFallingBack, int knockbackDistance, int knockbackRotation, int a8, Object* a9, int a10);
|
||||
static void _show_damage_to_object(Object* defender, int damage, int flags, Object* weapon, bool hitFromFront, int knockbackDistance, int knockbackRotation, int attackerAnimation, Object* attacker, int delay);
|
||||
static int _show_death(Object* obj, int anim);
|
||||
static int _show_damage_extras(Attack* attack);
|
||||
static void _show_damage(Attack* attack, int a2, int a3);
|
||||
static void _show_damage(Attack* attack, int attackerAnimation, int delay);
|
||||
static int _action_melee(Attack* attack, int a2);
|
||||
static int _action_ranged(Attack* attack, int a2);
|
||||
static int _is_next_to(Object* a1, Object* a2);
|
||||
@@ -87,16 +87,18 @@ static int _action_use_skill_in_combat_error(Object* critter);
|
||||
static int _pick_fall(Object* obj, int anim);
|
||||
static int _report_explosion(Attack* attack, Object* a2);
|
||||
static int _finished_explosion(Object* a1, Object* a2);
|
||||
static int _compute_explosion_damage(int min, int max, Object* a3, int* a4);
|
||||
static int _compute_explosion_damage(int min, int max, Object* defender, int* knockbackDistancePtr);
|
||||
static int _can_talk_to(Object* a1, Object* a2);
|
||||
static int _talk_to(Object* a1, Object* a2);
|
||||
static int _report_dmg(Attack* attack, Object* a2);
|
||||
static int _compute_dmg_damage(int min, int max, Object* obj, int* a4, int damage_type);
|
||||
static int _compute_dmg_damage(int min, int max, Object* obj, int* knockbackDistancePtr, int damageType);
|
||||
|
||||
static int hideProjectile(void* a1, void* a2);
|
||||
|
||||
// 0x410468
|
||||
int actionKnockdown(Object* obj, int* anim, int maxDistance, int rotation, int delay)
|
||||
{
|
||||
if (_critter_flag_check(obj->pid, CRITTER_FLAG_0x4000)) {
|
||||
if (_critter_flag_check(obj->pid, CRITTER_NO_KNOCKBACK)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -120,6 +122,15 @@ int actionKnockdown(Object* obj, int* anim, int maxDistance, int rotation, int d
|
||||
distance--;
|
||||
break;
|
||||
}
|
||||
|
||||
// CE: Fix to prevent critters (including player) cross an exit grid as
|
||||
// a result of knockback. Sfall has similar fix done differently and it
|
||||
// affects the player only. This approach is better since it also
|
||||
// prevents unreachable (=unlootable) corpses on exit grids.
|
||||
if (isExitGridAt(tile, obj->elevation)) {
|
||||
distance--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const char* soundEffectName = sfxBuildCharName(obj, *anim, CHARACTER_SOUND_EFFECT_KNOCKDOWN);
|
||||
@@ -142,10 +153,7 @@ int actionKnockdown(Object* obj, int* anim, int maxDistance, int rotation, int d
|
||||
// 0x410568
|
||||
int _action_blood(Object* obj, int anim, int delay)
|
||||
{
|
||||
|
||||
int violence_level = VIOLENCE_LEVEL_MAXIMUM_BLOOD;
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_VIOLENCE_LEVEL_KEY, &violence_level);
|
||||
if (violence_level == VIOLENCE_LEVEL_NONE) {
|
||||
if (settings.preferences.violence_level == VIOLENCE_LEVEL_NONE) {
|
||||
return anim;
|
||||
}
|
||||
|
||||
@@ -169,7 +177,7 @@ int _action_blood(Object* obj, int anim, int delay)
|
||||
}
|
||||
|
||||
// 0x41060C
|
||||
int _pick_death(Object* attacker, Object* defender, Object* weapon, int damage, int anim, bool isFallingBack)
|
||||
int _pick_death(Object* attacker, Object* defender, Object* weapon, int damage, int attackerAnimation, bool hitFromFront)
|
||||
{
|
||||
int normalViolenceLevelDamageThreshold = 15;
|
||||
int maximumBloodViolenceLevelDamageThreshold = 45;
|
||||
@@ -180,7 +188,7 @@ int _pick_death(Object* attacker, Object* defender, Object* weapon, int damage,
|
||||
normalViolenceLevelDamageThreshold = 5;
|
||||
maximumBloodViolenceLevelDamageThreshold = 15;
|
||||
damageType = DAMAGE_TYPE_FIRE;
|
||||
anim = ANIM_FIRE_SINGLE;
|
||||
attackerAnimation = ANIM_FIRE_SINGLE;
|
||||
}
|
||||
|
||||
if (attacker == gDude && perkHasRank(attacker, PERK_PYROMANIAC) && damageType == DAMAGE_TYPE_FIRE) {
|
||||
@@ -193,11 +201,10 @@ int _pick_death(Object* attacker, Object* defender, Object* weapon, int damage,
|
||||
maximumBloodViolenceLevelDamageThreshold /= 3;
|
||||
}
|
||||
|
||||
int violenceLevel = VIOLENCE_LEVEL_MAXIMUM_BLOOD;
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_VIOLENCE_LEVEL_KEY, &violenceLevel);
|
||||
int violenceLevel = settings.preferences.violence_level;
|
||||
|
||||
if (_critter_flag_check(defender->pid, CRITTER_FLAG_0x1000)) {
|
||||
return _check_death(defender, ANIM_EXPLODED_TO_NOTHING, VIOLENCE_LEVEL_NORMAL, isFallingBack);
|
||||
if (_critter_flag_check(defender->pid, CRITTER_SPECIAL_DEATH)) {
|
||||
return _check_death(defender, ANIM_EXPLODED_TO_NOTHING, VIOLENCE_LEVEL_NORMAL, hitFromFront);
|
||||
}
|
||||
|
||||
bool hasBloodyMess = false;
|
||||
@@ -210,16 +217,16 @@ int _pick_death(Object* attacker, Object* defender, Object* weapon, int damage,
|
||||
// it with bunch of "else" statements.
|
||||
int deathAnim = ANIM_FALL_BACK;
|
||||
|
||||
if ((anim == ANIM_THROW_PUNCH && damageType == DAMAGE_TYPE_NORMAL)
|
||||
|| anim == ANIM_KICK_LEG
|
||||
|| anim == ANIM_THRUST_ANIM
|
||||
|| anim == ANIM_SWING_ANIM
|
||||
|| (anim == ANIM_THROW_ANIM && damageType != DAMAGE_TYPE_EXPLOSION)) {
|
||||
if ((attackerAnimation == ANIM_THROW_PUNCH && damageType == DAMAGE_TYPE_NORMAL)
|
||||
|| attackerAnimation == ANIM_KICK_LEG
|
||||
|| attackerAnimation == ANIM_THRUST_ANIM
|
||||
|| attackerAnimation == ANIM_SWING_ANIM
|
||||
|| (attackerAnimation == ANIM_THROW_ANIM && damageType != DAMAGE_TYPE_EXPLOSION)) {
|
||||
if (violenceLevel == VIOLENCE_LEVEL_MAXIMUM_BLOOD && hasBloodyMess) {
|
||||
deathAnim = ANIM_BIG_HOLE;
|
||||
}
|
||||
} else {
|
||||
if (anim == ANIM_FIRE_SINGLE && damageType == DAMAGE_TYPE_NORMAL) {
|
||||
if (attackerAnimation == ANIM_FIRE_SINGLE && damageType == DAMAGE_TYPE_NORMAL) {
|
||||
if (violenceLevel == VIOLENCE_LEVEL_MAXIMUM_BLOOD) {
|
||||
if (hasBloodyMess || maximumBloodViolenceLevelDamageThreshold <= damage) {
|
||||
deathAnim = ANIM_BIG_HOLE;
|
||||
@@ -229,7 +236,7 @@ int _pick_death(Object* attacker, Object* defender, Object* weapon, int damage,
|
||||
if (violenceLevel > VIOLENCE_LEVEL_MINIMAL && (hasBloodyMess || normalViolenceLevelDamageThreshold <= damage)) {
|
||||
if (violenceLevel > VIOLENCE_LEVEL_NORMAL && (hasBloodyMess || maximumBloodViolenceLevelDamageThreshold <= damage)) {
|
||||
deathAnim = gMaximumBloodDeathAnimations[damageType];
|
||||
if (_check_death(defender, deathAnim, VIOLENCE_LEVEL_MAXIMUM_BLOOD, isFallingBack) != deathAnim) {
|
||||
if (_check_death(defender, deathAnim, VIOLENCE_LEVEL_MAXIMUM_BLOOD, hitFromFront) != deathAnim) {
|
||||
deathAnim = gNormalDeathAnimations[damageType];
|
||||
}
|
||||
} else {
|
||||
@@ -239,28 +246,26 @@ int _pick_death(Object* attacker, Object* defender, Object* weapon, int damage,
|
||||
}
|
||||
}
|
||||
|
||||
if (!isFallingBack && deathAnim == ANIM_FALL_BACK) {
|
||||
if (!hitFromFront && deathAnim == ANIM_FALL_BACK) {
|
||||
deathAnim = ANIM_FALL_FRONT;
|
||||
}
|
||||
|
||||
return _check_death(defender, deathAnim, VIOLENCE_LEVEL_NONE, isFallingBack);
|
||||
return _check_death(defender, deathAnim, VIOLENCE_LEVEL_NONE, hitFromFront);
|
||||
}
|
||||
|
||||
// 0x410814
|
||||
int _check_death(Object* obj, int anim, int minViolenceLevel, bool isFallingBack)
|
||||
int _check_death(Object* obj, int anim, int minViolenceLevel, bool hitFromFront)
|
||||
{
|
||||
int fid;
|
||||
|
||||
int violenceLevel = VIOLENCE_LEVEL_MAXIMUM_BLOOD;
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_VIOLENCE_LEVEL_KEY, &violenceLevel);
|
||||
if (violenceLevel >= minViolenceLevel) {
|
||||
if (settings.preferences.violence_level >= minViolenceLevel) {
|
||||
fid = buildFid(OBJ_TYPE_CRITTER, obj->fid & 0xFFF, anim, (obj->fid & 0xF000) >> 12, obj->rotation + 1);
|
||||
if (artExists(fid)) {
|
||||
return anim;
|
||||
}
|
||||
}
|
||||
|
||||
if (isFallingBack) {
|
||||
if (hitFromFront) {
|
||||
return ANIM_FALL_BACK;
|
||||
}
|
||||
|
||||
@@ -281,50 +286,50 @@ int _internal_destroy(Object* a1, Object* a2)
|
||||
// TODO: Check very carefully, lots of conditions and jumps.
|
||||
//
|
||||
// 0x4108D0
|
||||
void _show_damage_to_object(Object* a1, int damage, int flags, Object* weapon, bool isFallingBack, int knockbackDistance, int knockbackRotation, int a8, Object* a9, int a10)
|
||||
void _show_damage_to_object(Object* defender, int damage, int flags, Object* weapon, bool hitFromFront, int knockbackDistance, int knockbackRotation, int attackerAnimation, Object* attacker, int delay)
|
||||
{
|
||||
int anim;
|
||||
int fid;
|
||||
const char* sfx_name;
|
||||
|
||||
if (_critter_flag_check(a1->pid, CRITTER_FLAG_0x4000)) {
|
||||
if (_critter_flag_check(defender->pid, CRITTER_NO_KNOCKBACK)) {
|
||||
knockbackDistance = 0;
|
||||
}
|
||||
|
||||
anim = FID_ANIM_TYPE(a1->fid);
|
||||
if (!_critter_is_prone(a1)) {
|
||||
anim = FID_ANIM_TYPE(defender->fid);
|
||||
if (!_critter_is_prone(defender)) {
|
||||
if ((flags & DAM_DEAD) != 0) {
|
||||
fid = buildFid(OBJ_TYPE_MISC, 10, 0, 0, 0);
|
||||
if (fid == a9->fid) {
|
||||
anim = _check_death(a1, ANIM_EXPLODED_TO_NOTHING, VIOLENCE_LEVEL_MAXIMUM_BLOOD, isFallingBack);
|
||||
} else if (a9->pid == PROTO_ID_0x20001EB) {
|
||||
anim = _check_death(a1, ANIM_ELECTRIFIED_TO_NOTHING, VIOLENCE_LEVEL_MAXIMUM_BLOOD, isFallingBack);
|
||||
} else if (a9->fid == FID_0x20001F5) {
|
||||
anim = _check_death(a1, a8, VIOLENCE_LEVEL_MAXIMUM_BLOOD, isFallingBack);
|
||||
if (fid == attacker->fid) {
|
||||
anim = _check_death(defender, ANIM_EXPLODED_TO_NOTHING, VIOLENCE_LEVEL_MAXIMUM_BLOOD, hitFromFront);
|
||||
} else if (attacker->pid == PROTO_ID_0x20001EB) {
|
||||
anim = _check_death(defender, ANIM_ELECTRIFIED_TO_NOTHING, VIOLENCE_LEVEL_MAXIMUM_BLOOD, hitFromFront);
|
||||
} else if (attacker->fid == FID_0x20001F5) {
|
||||
anim = _check_death(defender, attackerAnimation, VIOLENCE_LEVEL_MAXIMUM_BLOOD, hitFromFront);
|
||||
} else {
|
||||
anim = _pick_death(a9, a1, weapon, damage, a8, isFallingBack);
|
||||
anim = _pick_death(attacker, defender, weapon, damage, attackerAnimation, hitFromFront);
|
||||
}
|
||||
|
||||
if (anim != ANIM_FIRE_DANCE) {
|
||||
if (knockbackDistance != 0 && (anim == ANIM_FALL_FRONT || anim == ANIM_FALL_BACK)) {
|
||||
actionKnockdown(a1, &anim, knockbackDistance, knockbackRotation, a10);
|
||||
anim = _action_blood(a1, anim, -1);
|
||||
actionKnockdown(defender, &anim, knockbackDistance, knockbackRotation, delay);
|
||||
anim = _action_blood(defender, anim, -1);
|
||||
} else {
|
||||
sfx_name = sfxBuildCharName(a1, anim, CHARACTER_SOUND_EFFECT_DIE);
|
||||
animationRegisterPlaySoundEffect(a1, sfx_name, a10);
|
||||
sfx_name = sfxBuildCharName(defender, anim, CHARACTER_SOUND_EFFECT_DIE);
|
||||
animationRegisterPlaySoundEffect(defender, sfx_name, delay);
|
||||
|
||||
anim = _pick_fall(a1, anim);
|
||||
animationRegisterAnimate(a1, anim, 0);
|
||||
anim = _pick_fall(defender, anim);
|
||||
animationRegisterAnimate(defender, anim, 0);
|
||||
|
||||
if (anim == ANIM_FALL_FRONT || anim == ANIM_FALL_BACK) {
|
||||
anim = _action_blood(a1, anim, -1);
|
||||
anim = _action_blood(defender, anim, -1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fid = buildFid(OBJ_TYPE_CRITTER, a1->fid & 0xFFF, ANIM_FIRE_DANCE, (a1->fid & 0xF000) >> 12, a1->rotation + 1);
|
||||
fid = buildFid(OBJ_TYPE_CRITTER, defender->fid & 0xFFF, ANIM_FIRE_DANCE, (defender->fid & 0xF000) >> 12, defender->rotation + 1);
|
||||
if (artExists(fid)) {
|
||||
sfx_name = sfxBuildCharName(a1, anim, CHARACTER_SOUND_EFFECT_UNUSED);
|
||||
animationRegisterPlaySoundEffect(a1, sfx_name, a10);
|
||||
sfx_name = sfxBuildCharName(defender, anim, CHARACTER_SOUND_EFFECT_UNUSED);
|
||||
animationRegisterPlaySoundEffect(defender, sfx_name, delay);
|
||||
|
||||
// SFALL
|
||||
if (explosionEmitsLight()) {
|
||||
@@ -334,10 +339,10 @@ void _show_damage_to_object(Object* a1, int damage, int flags, Object* weapon, b
|
||||
//
|
||||
// NOTE: Change intensity to 65536 (which is on par with
|
||||
// `anim_set_check_light_fix` Sfall's hack).
|
||||
animationRegisterSetLightIntensity(a1, 2, 65536, 0);
|
||||
animationRegisterSetLightIntensity(defender, 2, 65536, 0);
|
||||
}
|
||||
|
||||
animationRegisterAnimate(a1, anim, 0);
|
||||
animationRegisterAnimate(defender, anim, 0);
|
||||
|
||||
// SFALL
|
||||
if (explosionEmitsLight()) {
|
||||
@@ -349,72 +354,87 @@ void _show_damage_to_object(Object* a1, int damage, int flags, Object* weapon, b
|
||||
// workaround for `anim_set_check_light_fix` hack which
|
||||
// requires two upper bytes to be non-zero to override
|
||||
// default behaviour.
|
||||
animationRegisterSetLightIntensity(a1, 0, 0, -1);
|
||||
animationRegisterSetLightIntensity(defender, 0, 0, -1);
|
||||
}
|
||||
|
||||
int randomDistance = randomBetween(2, 5);
|
||||
int randomRotation = randomBetween(0, 5);
|
||||
|
||||
while (randomDistance > 0) {
|
||||
int tile = tileGetTileInDirection(a1->tile, randomRotation, randomDistance);
|
||||
Object* v35 = NULL;
|
||||
_make_straight_path(a1, a1->tile, tile, NULL, &v35, 4);
|
||||
if (v35 == NULL) {
|
||||
animationRegisterRotateToTile(a1, tile);
|
||||
animationRegisterMoveToTileStraight(a1, tile, a1->elevation, anim, 0);
|
||||
// CE: Fix to prevent critters (including player) to cross
|
||||
// an exit grid as a result of fire dance animation. See
|
||||
// `actionKnockdown` for notes.
|
||||
int rotation = randomRotation;
|
||||
int distance = randomDistance;
|
||||
while (true) {
|
||||
int tile = tileGetTileInDirection(defender->tile, (rotation + randomRotation) % ROTATION_COUNT, distance);
|
||||
if (!isExitGridAt(tile, defender->elevation)) {
|
||||
Object* obstacle = NULL;
|
||||
_make_straight_path(defender, defender->tile, tile, NULL, &obstacle, 4);
|
||||
if (obstacle == NULL) {
|
||||
animationRegisterRotateToTile(defender, tile);
|
||||
animationRegisterMoveToTileStraight(defender, tile, defender->elevation, anim, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (distance > 0) {
|
||||
distance--;
|
||||
} else if (rotation < ROTATION_COUNT) {
|
||||
rotation++;
|
||||
distance = randomDistance;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
randomDistance--;
|
||||
}
|
||||
}
|
||||
|
||||
anim = ANIM_BURNED_TO_NOTHING;
|
||||
sfx_name = sfxBuildCharName(a1, anim, CHARACTER_SOUND_EFFECT_UNUSED);
|
||||
animationRegisterPlaySoundEffect(a1, sfx_name, -1);
|
||||
animationRegisterAnimate(a1, anim, 0);
|
||||
sfx_name = sfxBuildCharName(defender, anim, CHARACTER_SOUND_EFFECT_UNUSED);
|
||||
animationRegisterPlaySoundEffect(defender, sfx_name, -1);
|
||||
animationRegisterAnimate(defender, anim, 0);
|
||||
}
|
||||
} else {
|
||||
if ((flags & (DAM_KNOCKED_OUT | DAM_KNOCKED_DOWN)) != 0) {
|
||||
anim = isFallingBack ? ANIM_FALL_BACK : ANIM_FALL_FRONT;
|
||||
sfx_name = sfxBuildCharName(a1, anim, CHARACTER_SOUND_EFFECT_UNUSED);
|
||||
animationRegisterPlaySoundEffect(a1, sfx_name, a10);
|
||||
anim = hitFromFront ? ANIM_FALL_BACK : ANIM_FALL_FRONT;
|
||||
sfx_name = sfxBuildCharName(defender, anim, CHARACTER_SOUND_EFFECT_UNUSED);
|
||||
animationRegisterPlaySoundEffect(defender, sfx_name, delay);
|
||||
if (knockbackDistance != 0) {
|
||||
actionKnockdown(a1, &anim, knockbackDistance, knockbackRotation, 0);
|
||||
actionKnockdown(defender, &anim, knockbackDistance, knockbackRotation, 0);
|
||||
} else {
|
||||
anim = _pick_fall(a1, anim);
|
||||
animationRegisterAnimate(a1, anim, 0);
|
||||
anim = _pick_fall(defender, anim);
|
||||
animationRegisterAnimate(defender, anim, 0);
|
||||
}
|
||||
} else if ((flags & DAM_ON_FIRE) != 0 && artExists(buildFid(OBJ_TYPE_CRITTER, a1->fid & 0xFFF, ANIM_FIRE_DANCE, (a1->fid & 0xF000) >> 12, a1->rotation + 1))) {
|
||||
animationRegisterAnimate(a1, ANIM_FIRE_DANCE, a10);
|
||||
} else if ((flags & DAM_ON_FIRE) != 0 && artExists(buildFid(OBJ_TYPE_CRITTER, defender->fid & 0xFFF, ANIM_FIRE_DANCE, (defender->fid & 0xF000) >> 12, defender->rotation + 1))) {
|
||||
animationRegisterAnimate(defender, ANIM_FIRE_DANCE, delay);
|
||||
|
||||
fid = buildFid(OBJ_TYPE_CRITTER, a1->fid & 0xFFF, ANIM_STAND, (a1->fid & 0xF000) >> 12, a1->rotation + 1);
|
||||
animationRegisterSetFid(a1, fid, -1);
|
||||
fid = buildFid(OBJ_TYPE_CRITTER, defender->fid & 0xFFF, ANIM_STAND, (defender->fid & 0xF000) >> 12, defender->rotation + 1);
|
||||
animationRegisterSetFid(defender, fid, -1);
|
||||
} else {
|
||||
if (knockbackDistance != 0) {
|
||||
anim = isFallingBack ? ANIM_FALL_BACK : ANIM_FALL_FRONT;
|
||||
actionKnockdown(a1, &anim, knockbackDistance, knockbackRotation, a10);
|
||||
anim = hitFromFront ? ANIM_FALL_BACK : ANIM_FALL_FRONT;
|
||||
actionKnockdown(defender, &anim, knockbackDistance, knockbackRotation, delay);
|
||||
if (anim == ANIM_FALL_BACK) {
|
||||
animationRegisterAnimate(a1, ANIM_BACK_TO_STANDING, -1);
|
||||
animationRegisterAnimate(defender, ANIM_BACK_TO_STANDING, -1);
|
||||
} else {
|
||||
animationRegisterAnimate(a1, ANIM_PRONE_TO_STANDING, -1);
|
||||
animationRegisterAnimate(defender, ANIM_PRONE_TO_STANDING, -1);
|
||||
}
|
||||
} else {
|
||||
if (isFallingBack || !artExists(buildFid(OBJ_TYPE_CRITTER, a1->fid & 0xFFF, ANIM_HIT_FROM_BACK, (a1->fid & 0xF000) >> 12, a1->rotation + 1))) {
|
||||
if (hitFromFront || !artExists(buildFid(OBJ_TYPE_CRITTER, defender->fid & 0xFFF, ANIM_HIT_FROM_BACK, (defender->fid & 0xF000) >> 12, defender->rotation + 1))) {
|
||||
anim = ANIM_HIT_FROM_FRONT;
|
||||
} else {
|
||||
anim = ANIM_HIT_FROM_BACK;
|
||||
}
|
||||
|
||||
sfx_name = sfxBuildCharName(a1, anim, CHARACTER_SOUND_EFFECT_UNUSED);
|
||||
animationRegisterPlaySoundEffect(a1, sfx_name, a10);
|
||||
sfx_name = sfxBuildCharName(defender, anim, CHARACTER_SOUND_EFFECT_UNUSED);
|
||||
animationRegisterPlaySoundEffect(defender, sfx_name, delay);
|
||||
|
||||
animationRegisterAnimate(a1, anim, 0);
|
||||
animationRegisterAnimate(defender, anim, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((flags & DAM_DEAD) != 0 && (a1->data.critter.combat.results & DAM_DEAD) == 0) {
|
||||
anim = _action_blood(a1, anim, a10);
|
||||
if ((flags & DAM_DEAD) != 0 && (defender->data.critter.combat.results & DAM_DEAD) == 0) {
|
||||
anim = _action_blood(defender, anim, delay);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@@ -422,63 +442,63 @@ void _show_damage_to_object(Object* a1, int damage, int flags, Object* weapon, b
|
||||
|
||||
if (weapon != NULL) {
|
||||
if ((flags & DAM_EXPLODE) != 0) {
|
||||
animationRegisterCallbackForced(a1, weapon, (AnimationCallback*)_obj_drop, -1);
|
||||
animationRegisterCallbackForced(defender, weapon, (AnimationCallback*)_obj_drop, -1);
|
||||
fid = buildFid(OBJ_TYPE_MISC, 10, 0, 0, 0);
|
||||
animationRegisterSetFid(weapon, fid, 0);
|
||||
animationRegisterAnimateAndHide(weapon, ANIM_STAND, 0);
|
||||
|
||||
sfx_name = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_HIT, weapon, HIT_MODE_RIGHT_WEAPON_PRIMARY, a1);
|
||||
sfx_name = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_HIT, weapon, HIT_MODE_RIGHT_WEAPON_PRIMARY, defender);
|
||||
animationRegisterPlaySoundEffect(weapon, sfx_name, 0);
|
||||
|
||||
animationRegisterHideObjectForced(weapon);
|
||||
} else if ((flags & DAM_DESTROY) != 0) {
|
||||
animationRegisterCallbackForced(a1, weapon, (AnimationCallback*)_internal_destroy, -1);
|
||||
animationRegisterCallbackForced(defender, weapon, (AnimationCallback*)_internal_destroy, -1);
|
||||
} else if ((flags & DAM_DROP) != 0) {
|
||||
animationRegisterCallbackForced(a1, weapon, (AnimationCallback*)_obj_drop, -1);
|
||||
animationRegisterCallbackForced(defender, weapon, (AnimationCallback*)_obj_drop, -1);
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & DAM_DEAD) != 0) {
|
||||
// TODO: Get rid of casts.
|
||||
animationRegisterCallbackForced(a1, (void*)anim, (AnimationCallback*)_show_death, -1);
|
||||
animationRegisterCallbackForced(defender, (void*)anim, (AnimationCallback*)_show_death, -1);
|
||||
}
|
||||
}
|
||||
|
||||
// 0x410E24
|
||||
int _show_death(Object* obj, int anim)
|
||||
{
|
||||
Rect v7;
|
||||
Rect v8;
|
||||
Rect tempRect;
|
||||
Rect dirtyRect;
|
||||
int fid;
|
||||
|
||||
objectGetRect(obj, &v8);
|
||||
objectGetRect(obj, &dirtyRect);
|
||||
if (anim < 48 && anim > 63) {
|
||||
fid = buildFid(OBJ_TYPE_CRITTER, obj->fid & 0xFFF, anim + 28, (obj->fid & 0xF000) >> 12, obj->rotation + 1);
|
||||
if (objectSetFid(obj, fid, &v7) == 0) {
|
||||
rectUnion(&v8, &v7, &v8);
|
||||
if (objectSetFid(obj, fid, &tempRect) == 0) {
|
||||
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
|
||||
}
|
||||
|
||||
if (objectSetFrame(obj, 0, &v7) == 0) {
|
||||
rectUnion(&v8, &v7, &v8);
|
||||
if (objectSetFrame(obj, 0, &tempRect) == 0) {
|
||||
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
|
||||
}
|
||||
}
|
||||
|
||||
if (_critter_flag_check(obj->pid, CRITTER_FLAG_0x800) == 0) {
|
||||
if (!_critter_flag_check(obj->pid, CRITTER_FLAT)) {
|
||||
obj->flags |= OBJECT_NO_BLOCK;
|
||||
if (_obj_toggle_flat(obj, &v7) == 0) {
|
||||
rectUnion(&v8, &v7, &v8);
|
||||
if (_obj_toggle_flat(obj, &tempRect) == 0) {
|
||||
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
|
||||
}
|
||||
}
|
||||
|
||||
if (objectDisableOutline(obj, &v7) == 0) {
|
||||
rectUnion(&v8, &v7, &v8);
|
||||
if (objectDisableOutline(obj, &tempRect) == 0) {
|
||||
rectUnion(&dirtyRect, &tempRect, &dirtyRect);
|
||||
}
|
||||
|
||||
if (anim >= 30 && anim <= 31 && _critter_flag_check(obj->pid, CRITTER_FLAG_0x1000) == 0 && _critter_flag_check(obj->pid, CRITTER_FLAG_0x40) == 0) {
|
||||
if (anim >= 30 && anim <= 31 && !_critter_flag_check(obj->pid, CRITTER_SPECIAL_DEATH) && !_critter_flag_check(obj->pid, CRITTER_NO_DROP)) {
|
||||
itemDropAll(obj, obj->tile);
|
||||
}
|
||||
|
||||
tileWindowRefreshRect(&v8, obj->elevation);
|
||||
tileWindowRefreshRect(&dirtyRect, obj->elevation);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -486,24 +506,16 @@ int _show_death(Object* obj, int anim)
|
||||
// 0x410FEC
|
||||
int _show_damage_extras(Attack* attack)
|
||||
{
|
||||
int v6;
|
||||
int v8;
|
||||
int v9;
|
||||
|
||||
for (int index = 0; index < attack->extrasLength; index++) {
|
||||
Object* obj = attack->extras[index];
|
||||
if (FID_TYPE(obj->fid) == OBJ_TYPE_CRITTER) {
|
||||
int delta = attack->attacker->rotation - obj->rotation;
|
||||
if (delta < 0) {
|
||||
delta = -delta;
|
||||
}
|
||||
|
||||
v6 = delta != 0 && delta != 1 && delta != 5;
|
||||
// NOTE: Uninline.
|
||||
bool hitFromFront = _is_hit_from_front(attack->attacker, obj);
|
||||
reg_anim_begin(ANIMATION_REQUEST_RESERVED);
|
||||
_register_priority(1);
|
||||
v8 = critterGetAnimationForHitMode(attack->attacker, attack->hitMode);
|
||||
v9 = tileGetRotationTo(attack->attacker->tile, obj->tile);
|
||||
_show_damage_to_object(obj, attack->extrasDamage[index], attack->extrasFlags[index], attack->weapon, v6, attack->extrasKnockback[index], v9, v8, attack->attacker, 0);
|
||||
int attackerAnimation = critterGetAnimationForHitMode(attack->attacker, attack->hitMode);
|
||||
int knockbackRotation = tileGetRotationTo(attack->attacker->tile, obj->tile);
|
||||
_show_damage_to_object(obj, attack->extrasDamage[index], attack->extrasFlags[index], attack->weapon, hitFromFront, attack->extrasKnockback[index], knockbackRotation, attackerAnimation, attack->attacker, 0);
|
||||
reg_anim_end();
|
||||
}
|
||||
}
|
||||
@@ -512,46 +524,35 @@ int _show_damage_extras(Attack* attack)
|
||||
}
|
||||
|
||||
// 0x4110AC
|
||||
void _show_damage(Attack* attack, int a2, int a3)
|
||||
void _show_damage(Attack* attack, int attackerAnimation, int delay)
|
||||
{
|
||||
int v5;
|
||||
int v14;
|
||||
int v17;
|
||||
int v15;
|
||||
|
||||
v5 = a3;
|
||||
for (int index = 0; index < attack->extrasLength; index++) {
|
||||
Object* object = attack->extras[index];
|
||||
if (FID_TYPE(object->fid) == OBJ_TYPE_CRITTER) {
|
||||
reg_anim_26(2, v5);
|
||||
v5 = 0;
|
||||
animationRegisterPing(ANIMATION_REQUEST_RESERVED, delay);
|
||||
delay = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((attack->attackerFlags & DAM_HIT) == 0) {
|
||||
if ((attack->attackerFlags & DAM_CRITICAL) != 0) {
|
||||
_show_damage_to_object(attack->attacker, attack->attackerDamage, attack->attackerFlags, attack->weapon, 1, 0, 0, a2, attack->attacker, -1);
|
||||
_show_damage_to_object(attack->attacker, attack->attackerDamage, attack->attackerFlags, attack->weapon, 1, 0, 0, attackerAnimation, attack->attacker, -1);
|
||||
} else if ((attack->attackerFlags & DAM_BACKWASH) != 0) {
|
||||
_show_damage_to_object(attack->attacker, attack->attackerDamage, attack->attackerFlags, attack->weapon, 1, 0, 0, a2, attack->attacker, -1);
|
||||
_show_damage_to_object(attack->attacker, attack->attackerDamage, attack->attackerFlags, attack->weapon, 1, 0, 0, attackerAnimation, attack->attacker, -1);
|
||||
}
|
||||
} else {
|
||||
if (attack->defender != NULL) {
|
||||
// TODO: Looks very similar to _show_damage_extras.
|
||||
int delta = attack->defender->rotation - attack->attacker->rotation;
|
||||
if (delta < 0) {
|
||||
delta = -delta;
|
||||
}
|
||||
|
||||
v15 = delta != 0 && delta != 1 && delta != 5;
|
||||
// NOTE: Uninline.
|
||||
bool hitFromFront = _is_hit_from_front(attack->defender, attack->attacker);
|
||||
|
||||
if (FID_TYPE(attack->defender->fid) == OBJ_TYPE_CRITTER) {
|
||||
if (attack->attacker->fid == 33554933) {
|
||||
v14 = tileGetRotationTo(attack->attacker->tile, attack->defender->tile);
|
||||
_show_damage_to_object(attack->defender, attack->defenderDamage, attack->defenderFlags, attack->weapon, v15, attack->defenderKnockback, v14, a2, attack->attacker, a3);
|
||||
if (attack->attacker->fid == FID_0x20001F5) {
|
||||
int knockbackRotation = tileGetRotationTo(attack->attacker->tile, attack->defender->tile);
|
||||
_show_damage_to_object(attack->defender, attack->defenderDamage, attack->defenderFlags, attack->weapon, hitFromFront, attack->defenderKnockback, knockbackRotation, attackerAnimation, attack->attacker, delay);
|
||||
} else {
|
||||
v17 = critterGetAnimationForHitMode(attack->attacker, attack->hitMode);
|
||||
v14 = tileGetRotationTo(attack->attacker->tile, attack->defender->tile);
|
||||
_show_damage_to_object(attack->defender, attack->defenderDamage, attack->defenderFlags, attack->weapon, v15, attack->defenderKnockback, v14, v17, attack->attacker, a3);
|
||||
int weaponAnimation = critterGetAnimationForHitMode(attack->attacker, attack->hitMode);
|
||||
int knockbackRotation = tileGetRotationTo(attack->attacker->tile, attack->defender->tile);
|
||||
_show_damage_to_object(attack->defender, attack->defenderDamage, attack->defenderFlags, attack->weapon, hitFromFront, attack->defenderKnockback, knockbackRotation, weaponAnimation, attack->attacker, delay);
|
||||
}
|
||||
} else {
|
||||
tileGetRotationTo(attack->attacker->tile, attack->defender->tile);
|
||||
@@ -560,7 +561,7 @@ void _show_damage(Attack* attack, int a2, int a3)
|
||||
}
|
||||
|
||||
if ((attack->attackerFlags & DAM_DUD) != 0) {
|
||||
_show_damage_to_object(attack->attacker, attack->attackerDamage, attack->attackerFlags, attack->weapon, 1, 0, 0, a2, attack->attacker, -1);
|
||||
_show_damage_to_object(attack->attacker, attack->attackerDamage, attack->attackerFlags, attack->weapon, 1, 0, 0, attackerAnimation, attack->attacker, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -596,10 +597,7 @@ int _action_melee(Attack* attack, int anim)
|
||||
int fid;
|
||||
Art* art;
|
||||
CacheEntry* cache_entry;
|
||||
int v17;
|
||||
int v18;
|
||||
int delta;
|
||||
int flag;
|
||||
int delay;
|
||||
const char* sfx_name;
|
||||
char sfx_name_temp[16];
|
||||
|
||||
@@ -609,21 +607,15 @@ int _action_melee(Attack* attack, int anim)
|
||||
fid = buildFid(OBJ_TYPE_CRITTER, attack->attacker->fid & 0xFFF, anim, (attack->attacker->fid & 0xF000) >> 12, attack->attacker->rotation + 1);
|
||||
art = artLock(fid, &cache_entry);
|
||||
if (art != NULL) {
|
||||
v17 = artGetActionFrame(art);
|
||||
delay = artGetActionFrame(art);
|
||||
} else {
|
||||
v17 = 0;
|
||||
delay = 0;
|
||||
}
|
||||
artUnlock(cache_entry);
|
||||
|
||||
tileGetTileInDirection(attack->attacker->tile, attack->attacker->rotation, 1);
|
||||
animationRegisterRotateToTile(attack->attacker, attack->defender->tile);
|
||||
|
||||
delta = attack->attacker->rotation - attack->defender->rotation;
|
||||
if (delta < 0) {
|
||||
delta = -delta;
|
||||
}
|
||||
flag = delta != 0 && delta != 1 && delta != 5;
|
||||
|
||||
if (anim != ANIM_THROW_PUNCH && anim != ANIM_KICK_LEG) {
|
||||
sfx_name = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_ATTACK, attack->weapon, attack->hitMode, attack->defender);
|
||||
} else {
|
||||
@@ -645,7 +637,7 @@ int _action_melee(Attack* attack, int anim)
|
||||
strcpy(sfx_name_temp, sfx_name);
|
||||
|
||||
animationRegisterAnimate(attack->attacker, anim, 0);
|
||||
animationRegisterPlaySoundEffect(attack->attacker, sfx_name_temp, v17);
|
||||
animationRegisterPlaySoundEffect(attack->attacker, sfx_name_temp, delay);
|
||||
_show_damage(attack, anim, 0);
|
||||
} else {
|
||||
if (attack->defender->data.critter.combat.results & 0x03) {
|
||||
@@ -655,21 +647,21 @@ int _action_melee(Attack* attack, int anim)
|
||||
fid = buildFid(OBJ_TYPE_CRITTER, attack->defender->fid & 0xFFF, ANIM_DODGE_ANIM, (attack->defender->fid & 0xF000) >> 12, attack->defender->rotation + 1);
|
||||
art = artLock(fid, &cache_entry);
|
||||
if (art != NULL) {
|
||||
v18 = artGetActionFrame(art);
|
||||
int dodgeDelay = artGetActionFrame(art);
|
||||
artUnlock(cache_entry);
|
||||
|
||||
if (v18 <= v17) {
|
||||
if (dodgeDelay <= delay) {
|
||||
animationRegisterPlaySoundEffect(attack->attacker, sfx_name_temp, -1);
|
||||
animationRegisterAnimate(attack->attacker, anim, 0);
|
||||
|
||||
sfx_name = sfxBuildCharName(attack->defender, ANIM_DODGE_ANIM, CHARACTER_SOUND_EFFECT_UNUSED);
|
||||
animationRegisterPlaySoundEffect(attack->defender, sfx_name, v17 - v18);
|
||||
animationRegisterPlaySoundEffect(attack->defender, sfx_name, delay - dodgeDelay);
|
||||
animationRegisterAnimate(attack->defender, ANIM_DODGE_ANIM, 0);
|
||||
} else {
|
||||
sfx_name = sfxBuildCharName(attack->defender, ANIM_DODGE_ANIM, CHARACTER_SOUND_EFFECT_UNUSED);
|
||||
animationRegisterPlaySoundEffect(attack->defender, sfx_name, -1);
|
||||
animationRegisterAnimate(attack->defender, ANIM_DODGE_ANIM, 0);
|
||||
animationRegisterPlaySoundEffect(attack->attacker, sfx_name_temp, v18 - v17);
|
||||
animationRegisterPlaySoundEffect(attack->attacker, sfx_name_temp, dodgeDelay - delay);
|
||||
animationRegisterAnimate(attack->attacker, anim, 0);
|
||||
}
|
||||
}
|
||||
@@ -696,14 +688,16 @@ int _action_melee(Attack* attack, int anim)
|
||||
// 0x411600
|
||||
int _action_ranged(Attack* attack, int anim)
|
||||
{
|
||||
Object* neighboors[6];
|
||||
memset(neighboors, 0, sizeof(neighboors));
|
||||
Object* adjacentObjects[ROTATION_COUNT];
|
||||
for (int rotation = 0; rotation < ROTATION_COUNT; rotation++) {
|
||||
adjacentObjects[rotation] = NULL;
|
||||
}
|
||||
|
||||
reg_anim_begin(ANIMATION_REQUEST_RESERVED);
|
||||
_register_priority(1);
|
||||
|
||||
Object* projectile = NULL;
|
||||
Object* v50 = NULL;
|
||||
Object* replacedWeapon = NULL;
|
||||
int weaponFid = -1;
|
||||
|
||||
Proto* weaponProto;
|
||||
@@ -713,7 +707,7 @@ int _action_ranged(Attack* attack, int anim)
|
||||
int fid = buildFid(OBJ_TYPE_CRITTER, attack->attacker->fid & 0xFFF, anim, (attack->attacker->fid & 0xF000) >> 12, attack->attacker->rotation + 1);
|
||||
CacheEntry* artHandle;
|
||||
Art* art = artLock(fid, &artHandle);
|
||||
int actionFrame = (art != NULL) ? artGetActionFrame(art) : 0;
|
||||
int delay = (art != NULL) ? artGetActionFrame(art) : 0;
|
||||
artUnlock(artHandle);
|
||||
|
||||
weaponGetRange(attack->attacker, attack->hitMode);
|
||||
@@ -763,12 +757,12 @@ int _action_ranged(Attack* attack, int anim)
|
||||
interfaceGetItemActions(&leftItemAction, &rightItemAction);
|
||||
|
||||
itemRemove(attack->attacker, weapon, 1);
|
||||
v50 = itemReplace(attack->attacker, weapon, weaponFlags & OBJECT_IN_ANY_HAND);
|
||||
replacedWeapon = itemReplace(attack->attacker, weapon, weaponFlags & OBJECT_IN_ANY_HAND);
|
||||
objectSetFid(projectile, projectileProto->fid, NULL);
|
||||
_cAIPrepWeaponItem(attack->attacker, weapon);
|
||||
|
||||
if (attack->attacker == gDude) {
|
||||
if (v50 == NULL) {
|
||||
if (replacedWeapon == NULL) {
|
||||
if ((weaponFlags & OBJECT_IN_LEFT_HAND) != 0) {
|
||||
leftItemAction = INTERFACE_ITEM_ACTION_DEFAULT;
|
||||
} else if ((weaponFlags & OBJECT_IN_RIGHT_HAND) != 0) {
|
||||
@@ -798,20 +792,20 @@ int _action_ranged(Attack* attack, int anim)
|
||||
int projectileRotation = tileGetRotationTo(attack->attacker->tile, attack->defender->tile);
|
||||
objectSetRotation(projectile, projectileRotation, NULL);
|
||||
|
||||
animationRegisterUnsetFlag(projectile, OBJECT_HIDDEN, actionFrame);
|
||||
animationRegisterUnsetFlag(projectile, OBJECT_HIDDEN, delay);
|
||||
|
||||
const char* sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_AMMO_FLYING, weapon, attack->hitMode, attack->defender);
|
||||
animationRegisterPlaySoundEffect(projectile, sfx, 0);
|
||||
|
||||
int v24;
|
||||
int explosionCenterTile;
|
||||
if ((attack->attackerFlags & DAM_HIT) != 0) {
|
||||
animationRegisterMoveToTileStraight(projectile, attack->defender->tile, attack->defender->elevation, ANIM_WALK, 0);
|
||||
actionFrame = _make_straight_path(projectile, projectileOrigin, attack->defender->tile, NULL, NULL, 32) - 1;
|
||||
v24 = attack->defender->tile;
|
||||
delay = _make_straight_path(projectile, projectileOrigin, attack->defender->tile, NULL, NULL, 32) - 1;
|
||||
explosionCenterTile = attack->defender->tile;
|
||||
} else {
|
||||
animationRegisterMoveToTileStraight(projectile, attack->tile, attack->defender->elevation, ANIM_WALK, 0);
|
||||
actionFrame = 0;
|
||||
v24 = attack->tile;
|
||||
delay = 0;
|
||||
explosionCenterTile = attack->tile;
|
||||
}
|
||||
|
||||
// SFALL
|
||||
@@ -867,11 +861,11 @@ int _action_ranged(Attack* attack, int anim)
|
||||
explosionGetPattern(&startRotation, &endRotation);
|
||||
|
||||
for (int rotation = startRotation; rotation < endRotation; rotation++) {
|
||||
if (objectCreateWithFidPid(&(neighboors[rotation]), explosionFid, -1) != -1) {
|
||||
objectHide(neighboors[rotation], NULL);
|
||||
if (objectCreateWithFidPid(&(adjacentObjects[rotation]), explosionFid, -1) != -1) {
|
||||
objectHide(adjacentObjects[rotation], NULL);
|
||||
|
||||
int v31 = tileGetTileInDirection(v24, rotation, 1);
|
||||
objectSetLocation(neighboors[rotation], v31, projectile->elevation, NULL);
|
||||
int adjacentTile = tileGetTileInDirection(explosionCenterTile, rotation, 1);
|
||||
objectSetLocation(adjacentObjects[rotation], adjacentTile, projectile->elevation, NULL);
|
||||
|
||||
int delay;
|
||||
if (rotation != ROTATION_NE) {
|
||||
@@ -884,8 +878,8 @@ int _action_ranged(Attack* attack, int anim)
|
||||
}
|
||||
}
|
||||
|
||||
animationRegisterUnsetFlag(neighboors[rotation], OBJECT_HIDDEN, delay);
|
||||
animationRegisterAnimateAndHide(neighboors[rotation], ANIM_STAND, 0);
|
||||
animationRegisterUnsetFlag(adjacentObjects[rotation], OBJECT_HIDDEN, delay);
|
||||
animationRegisterAnimateAndHide(adjacentObjects[rotation], ANIM_STAND, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -899,15 +893,15 @@ int _action_ranged(Attack* attack, int anim)
|
||||
|
||||
if (!l56) {
|
||||
const char* sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_HIT, weapon, attack->hitMode, attack->defender);
|
||||
animationRegisterPlaySoundEffect(weapon, sfx, actionFrame);
|
||||
animationRegisterPlaySoundEffect(weapon, sfx, delay);
|
||||
}
|
||||
|
||||
actionFrame = 0;
|
||||
delay = 0;
|
||||
} else {
|
||||
if ((attack->attackerFlags & DAM_HIT) == 0) {
|
||||
Object* defender = attack->defender;
|
||||
if ((defender->data.critter.combat.results & (DAM_KNOCKED_OUT | DAM_KNOCKED_DOWN)) == 0) {
|
||||
animationRegisterAnimate(defender, ANIM_DODGE_ANIM, actionFrame);
|
||||
animationRegisterAnimate(defender, ANIM_DODGE_ANIM, delay);
|
||||
l56 = true;
|
||||
}
|
||||
}
|
||||
@@ -915,7 +909,7 @@ int _action_ranged(Attack* attack, int anim)
|
||||
}
|
||||
}
|
||||
|
||||
_show_damage(attack, anim, actionFrame);
|
||||
_show_damage(attack, anim, delay);
|
||||
|
||||
if ((attack->attackerFlags & DAM_HIT) == 0) {
|
||||
_combatai_msg(attack->defender, attack, AI_MESSAGE_TYPE_MISS, -1);
|
||||
@@ -927,29 +921,38 @@ int _action_ranged(Attack* attack, int anim)
|
||||
|
||||
// SFALL
|
||||
if (projectile != NULL && (isGrenade || damageType == explosionGetDamageType())) {
|
||||
animationRegisterHideObjectForced(projectile);
|
||||
// CE: Use custom callback to hide projectile instead of relying on
|
||||
// `animationRegisterHideObjectForced`. The problem is that completing
|
||||
// `ANIM_KIND_HIDE` removes (frees) object entirely. When this happens
|
||||
// `attack->weapon` becomes a dangling pointer, but this object is
|
||||
// needed to process `damage_p_proc` by scripting engine which can
|
||||
// interrogate weapon's properties (leading to crash on some platforms).
|
||||
// So instead of removing projectile follow a pattern established in
|
||||
// `opDestroyObject` for self-deleting objects (mark it hidden +
|
||||
// no-save).
|
||||
animationRegisterCallbackForced(attack, projectile, hideProjectile, -1);
|
||||
} else if (anim == ANIM_THROW_ANIM && projectile != NULL) {
|
||||
animationRegisterSetFid(projectile, weaponFid, -1);
|
||||
}
|
||||
|
||||
for (int rotation = 0; rotation < ROTATION_COUNT; rotation++) {
|
||||
if (neighboors[rotation] != NULL) {
|
||||
animationRegisterHideObjectForced(neighboors[rotation]);
|
||||
if (adjacentObjects[rotation] != NULL) {
|
||||
animationRegisterHideObjectForced(adjacentObjects[rotation]);
|
||||
}
|
||||
}
|
||||
|
||||
if ((attack->attackerFlags & (DAM_KNOCKED_OUT | DAM_KNOCKED_DOWN | DAM_DEAD)) == 0) {
|
||||
if (anim == ANIM_THROW_ANIM) {
|
||||
bool l9 = false;
|
||||
if (v50 != NULL) {
|
||||
int v38 = weaponGetAnimationCode(v50);
|
||||
if (v38 != 0) {
|
||||
animationRegisterTakeOutWeapon(attack->attacker, v38, -1);
|
||||
l9 = true;
|
||||
bool takeOutAnimationRegistered = false;
|
||||
if (replacedWeapon != NULL) {
|
||||
int weaponAnimationCode = weaponGetAnimationCode(replacedWeapon);
|
||||
if (weaponAnimationCode != 0) {
|
||||
animationRegisterTakeOutWeapon(attack->attacker, weaponAnimationCode, -1);
|
||||
takeOutAnimationRegistered = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!l9) {
|
||||
if (!takeOutAnimationRegistered) {
|
||||
int fid = buildFid(OBJ_TYPE_CRITTER, attack->attacker->fid & 0xFFF, ANIM_STAND, 0, attack->attacker->rotation + 1);
|
||||
animationRegisterSetFid(attack->attacker, fid, -1);
|
||||
}
|
||||
@@ -965,8 +968,8 @@ int _action_ranged(Attack* attack, int anim)
|
||||
}
|
||||
|
||||
for (int rotation = 0; rotation < ROTATION_COUNT; rotation++) {
|
||||
if (neighboors[rotation] != NULL) {
|
||||
objectDestroy(neighboors[rotation], NULL);
|
||||
if (adjacentObjects[rotation] != NULL) {
|
||||
objectDestroy(adjacentObjects[rotation], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1254,6 +1257,11 @@ int _action_loot_container(Object* critter, Object* container)
|
||||
return -1;
|
||||
}
|
||||
|
||||
// SFALL: Fix for trying to loot corpses with the "NoSteal" flag.
|
||||
if (_critter_flag_check(container->pid, CRITTER_NO_STEAL)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (critter == gDude) {
|
||||
int anim = FID_ANIM_TYPE(gDude->fid);
|
||||
if (anim == ANIM_WALK || anim == ANIM_RUNNING) {
|
||||
@@ -1397,7 +1405,7 @@ int actionUseSkill(Object* a1, Object* a2, int skill)
|
||||
|
||||
return -1;
|
||||
case SKILL_SNEAK:
|
||||
dudeToggleState(0);
|
||||
dudeToggleState(DUDE_STATE_SNEAKING);
|
||||
return 0;
|
||||
default:
|
||||
debugPrint("\nskill_use: invalid skill used.");
|
||||
@@ -1439,19 +1447,19 @@ int actionUseSkill(Object* a1, Object* a2, int skill)
|
||||
}
|
||||
|
||||
if (partyMember != NULL) {
|
||||
bool v32 = false;
|
||||
bool isDude = false;
|
||||
if (objectGetDistanceBetween(gDude, a2) <= 1) {
|
||||
v32 = true;
|
||||
isDude = true;
|
||||
}
|
||||
|
||||
char* msg = skillsGetGenericResponse(partyMember, v32);
|
||||
char* msg = skillsGetGenericResponse(partyMember, isDude);
|
||||
|
||||
Rect rect;
|
||||
if (textObjectAdd(partyMember, msg, 101, _colorTable[32747], _colorTable[0], &rect) == 0) {
|
||||
tileWindowRefreshRect(&rect, gElevation);
|
||||
}
|
||||
|
||||
if (v32) {
|
||||
if (isDude) {
|
||||
performer = gDude;
|
||||
partyMember = NULL;
|
||||
}
|
||||
@@ -1563,7 +1571,7 @@ int _pick_fall(Object* obj, int anim)
|
||||
// 0x412CE4
|
||||
bool _action_explode_running()
|
||||
{
|
||||
return _action_in_explode != 0;
|
||||
return _action_in_explode;
|
||||
}
|
||||
|
||||
// action_explode
|
||||
@@ -1587,7 +1595,7 @@ int actionExplode(int tile, int elevation, int minDamage, int maxDamage, Object*
|
||||
}
|
||||
|
||||
objectHide(explosion, NULL);
|
||||
explosion->flags |= OBJECT_TEMPORARY;
|
||||
explosion->flags |= OBJECT_NO_SAVE;
|
||||
|
||||
objectSetLocation(explosion, tile, elevation, NULL);
|
||||
|
||||
@@ -1605,7 +1613,7 @@ int actionExplode(int tile, int elevation, int minDamage, int maxDamage, Object*
|
||||
}
|
||||
|
||||
objectHide(adjacentExplosions[rotation], NULL);
|
||||
adjacentExplosions[rotation]->flags |= OBJECT_TEMPORARY;
|
||||
adjacentExplosions[rotation]->flags |= OBJECT_NO_SAVE;
|
||||
|
||||
int adjacentTile = tileGetTileInDirection(tile, rotation, 1);
|
||||
objectSetLocation(adjacentExplosions[rotation], adjacentTile, elevation, NULL);
|
||||
@@ -1646,7 +1654,7 @@ int actionExplode(int tile, int elevation, int minDamage, int maxDamage, Object*
|
||||
attackComputeDeathFlags(attack);
|
||||
|
||||
if (a6) {
|
||||
_action_in_explode = 1;
|
||||
_action_in_explode = true;
|
||||
|
||||
reg_anim_begin(ANIMATION_REQUEST_RESERVED);
|
||||
_register_priority(1);
|
||||
@@ -1670,7 +1678,7 @@ int actionExplode(int tile, int elevation, int minDamage, int maxDamage, Object*
|
||||
animationRegisterCallbackForced(attack, a5, (AnimationCallback*)_report_explosion, -1);
|
||||
animationRegisterCallbackForced(NULL, NULL, (AnimationCallback*)_finished_explosion, -1);
|
||||
if (reg_anim_end() == -1) {
|
||||
_action_in_explode = 0;
|
||||
_action_in_explode = false;
|
||||
|
||||
objectDestroy(explosion, NULL);
|
||||
|
||||
@@ -1791,31 +1799,30 @@ int _report_explosion(Attack* attack, Object* a2)
|
||||
// 0x4132C0
|
||||
int _finished_explosion(Object* a1, Object* a2)
|
||||
{
|
||||
_action_in_explode = 0;
|
||||
_action_in_explode = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// calculate explosion damage applying threshold and resistances
|
||||
// 0x4132CC
|
||||
int _compute_explosion_damage(int min, int max, Object* a3, int* a4)
|
||||
int _compute_explosion_damage(int min, int max, Object* defender, int* knockbackDistancePtr)
|
||||
{
|
||||
int v5 = randomBetween(min, max);
|
||||
int v7 = v5 - critterGetStat(a3, STAT_DAMAGE_THRESHOLD_EXPLOSION);
|
||||
if (v7 > 0) {
|
||||
v7 -= critterGetStat(a3, STAT_DAMAGE_RESISTANCE_EXPLOSION) * v7 / 100;
|
||||
int damage = randomBetween(min, max) - critterGetStat(defender, STAT_DAMAGE_THRESHOLD_EXPLOSION);
|
||||
if (damage > 0) {
|
||||
damage -= critterGetStat(defender, STAT_DAMAGE_RESISTANCE_EXPLOSION) * damage / 100;
|
||||
}
|
||||
|
||||
if (v7 < 0) {
|
||||
v7 = 0;
|
||||
if (damage < 0) {
|
||||
damage = 0;
|
||||
}
|
||||
|
||||
if (a4 != NULL) {
|
||||
if ((a3->flags & OBJECT_MULTIHEX) == 0) {
|
||||
*a4 = v7 / 10;
|
||||
if (knockbackDistancePtr != NULL) {
|
||||
if ((defender->flags & OBJECT_MULTIHEX) == 0) {
|
||||
*knockbackDistancePtr = damage / 10;
|
||||
}
|
||||
}
|
||||
|
||||
return v7;
|
||||
return damage;
|
||||
}
|
||||
|
||||
// 0x413330
|
||||
@@ -1892,7 +1899,7 @@ void actionDamage(int tile, int elevation, int minDamage, int maxDamage, int dam
|
||||
|
||||
objectHide(attacker, NULL);
|
||||
|
||||
attacker->flags |= OBJECT_TEMPORARY;
|
||||
attacker->flags |= OBJECT_NO_SAVE;
|
||||
|
||||
objectSetLocation(attacker, tile, elevation, NULL);
|
||||
|
||||
@@ -1958,29 +1965,28 @@ int _report_dmg(Attack* attack, Object* a2)
|
||||
// Calculate damage by applying threshold and resistances.
|
||||
//
|
||||
// 0x413660
|
||||
int _compute_dmg_damage(int min, int max, Object* obj, int* a4, int damageType)
|
||||
int _compute_dmg_damage(int min, int max, Object* obj, int* knockbackDistancePtr, int damageType)
|
||||
{
|
||||
if (!_critter_flag_check(obj->pid, CRITTER_FLAG_0x4000)) {
|
||||
a4 = NULL;
|
||||
if (!_critter_flag_check(obj->pid, CRITTER_NO_KNOCKBACK)) {
|
||||
knockbackDistancePtr = NULL;
|
||||
}
|
||||
|
||||
int v8 = randomBetween(min, max);
|
||||
int v10 = v8 - critterGetStat(obj, STAT_DAMAGE_THRESHOLD + damageType);
|
||||
if (v10 > 0) {
|
||||
v10 -= critterGetStat(obj, STAT_DAMAGE_RESISTANCE + damageType) * v10 / 100;
|
||||
int damage = randomBetween(min, max) - critterGetStat(obj, STAT_DAMAGE_THRESHOLD + damageType);
|
||||
if (damage > 0) {
|
||||
damage -= critterGetStat(obj, STAT_DAMAGE_RESISTANCE + damageType) * damage / 100;
|
||||
}
|
||||
|
||||
if (v10 < 0) {
|
||||
v10 = 0;
|
||||
if (damage < 0) {
|
||||
damage = 0;
|
||||
}
|
||||
|
||||
if (a4 != NULL) {
|
||||
if (knockbackDistancePtr != NULL) {
|
||||
if ((obj->flags & OBJECT_MULTIHEX) == 0 && damageType != DAMAGE_TYPE_ELECTRICAL) {
|
||||
*a4 = v10 / 10;
|
||||
*knockbackDistancePtr = damage / 10;
|
||||
}
|
||||
}
|
||||
|
||||
return v10;
|
||||
return damage;
|
||||
}
|
||||
|
||||
// 0x4136EC
|
||||
@@ -2117,4 +2123,18 @@ int _action_can_talk_to(Object* a1, Object* a2)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hideProjectile(void* a1, void* a2)
|
||||
{
|
||||
Object* projectile = reinterpret_cast<Object*>(a2);
|
||||
|
||||
Rect rect;
|
||||
if (objectHide(projectile, &rect) == 0) {
|
||||
tileWindowRefreshRect(&rect, projectile->elevation);
|
||||
}
|
||||
|
||||
projectile->flags |= OBJECT_NO_SAVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
+258
-271
File diff suppressed because it is too large
Load Diff
+10
-10
@@ -10,7 +10,7 @@ typedef enum AnimationRequestOptions {
|
||||
ANIMATION_REQUEST_UNRESERVED = 0x01,
|
||||
ANIMATION_REQUEST_RESERVED = 0x02,
|
||||
ANIMATION_REQUEST_NO_STAND = 0x04,
|
||||
ANIMATION_REQUEST_0x100 = 0x100,
|
||||
ANIMATION_REQUEST_PING = 0x100,
|
||||
ANIMATION_REQUEST_INSIGNIFICANT = 0x200,
|
||||
} AnimationRequestOptions;
|
||||
|
||||
@@ -92,7 +92,7 @@ typedef enum AnimationType {
|
||||
LAST_SF_DEATH_ANIM = ANIM_FALL_FRONT_BLOOD_SF,
|
||||
} AnimationType;
|
||||
|
||||
#define FID_ANIM_TYPE(value) ((value) & 0xFF0000) >> 16
|
||||
#define FID_ANIM_TYPE(value) ((value)&0xFF0000) >> 16
|
||||
|
||||
// Signature of animation callback accepting 2 parameters.
|
||||
typedef int(AnimationCallback)(void* a1, void* a2);
|
||||
@@ -100,12 +100,12 @@ typedef int(AnimationCallback)(void* a1, void* a2);
|
||||
// Signature of animation callback accepting 3 parameters.
|
||||
typedef int(AnimationCallback3)(void* a1, void* a2, void* a3);
|
||||
|
||||
typedef struct STRUCT_530014_28 {
|
||||
typedef struct StraightPathNode {
|
||||
int tile;
|
||||
int elevation;
|
||||
int x;
|
||||
int y;
|
||||
} STRUCT_530014_28;
|
||||
} StraightPathNode;
|
||||
|
||||
typedef Object* PathBuilderCallback(Object* object, int tile, int elevation);
|
||||
|
||||
@@ -142,15 +142,15 @@ int animationRegisterSetLightDistance(Object* owner, int lightDistance, int dela
|
||||
int animationRegisterToggleOutline(Object* object, bool outline, int delay);
|
||||
int animationRegisterPlaySoundEffect(Object* owner, const char* soundEffectName, int delay);
|
||||
int animationRegisterAnimateForever(Object* owner, int anim, int delay);
|
||||
int reg_anim_26(int a1, int a2);
|
||||
int animationRegisterPing(int flags, int delay);
|
||||
int _make_path(Object* object, int from, int to, unsigned char* a4, int a5);
|
||||
int pathfinderFindPath(Object* object, int from, int to, unsigned char* rotations, int a5, PathBuilderCallback* callback);
|
||||
int _make_straight_path(Object* a1, int from, int to, STRUCT_530014_28* pathNodes, Object** a5, int a6);
|
||||
int _make_straight_path_func(Object* a1, int from, int to, STRUCT_530014_28* a4, Object** a5, int a6, Object* (*a7)(Object*, int, int));
|
||||
int _make_straight_path(Object* a1, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6);
|
||||
int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6, PathBuilderCallback* callback);
|
||||
void _object_animate();
|
||||
int _check_move(int* a1);
|
||||
int _dude_move(int a1);
|
||||
int _dude_run(int a1);
|
||||
int _check_move(int* actionPointsPtr);
|
||||
int _dude_move(int actionPoints);
|
||||
int _dude_run(int actionPoints);
|
||||
void _dude_fidget();
|
||||
void _dude_stand(Object* obj, int rotation, int fid);
|
||||
void _dude_standup(Object* a1);
|
||||
|
||||
+116
-102
@@ -8,10 +8,10 @@
|
||||
#include "debug.h"
|
||||
#include "draw.h"
|
||||
#include "game.h"
|
||||
#include "game_config.h"
|
||||
#include "memory.h"
|
||||
#include "object.h"
|
||||
#include "proto.h"
|
||||
#include "settings.h"
|
||||
#include "sfall_config.h"
|
||||
|
||||
namespace fallout {
|
||||
@@ -34,8 +34,10 @@ static int artReadList(const char* path, char** out_arr, int* out_count);
|
||||
static int artCacheGetFileSizeImpl(int a1, int* out_size);
|
||||
static int artCacheReadDataImpl(int a1, int* a2, unsigned char* data);
|
||||
static void artCacheFreeImpl(void* ptr);
|
||||
static int artReadFrameData(unsigned char* data, File* stream, int count);
|
||||
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 paddingForSize(int size);
|
||||
|
||||
// 0x5002D8
|
||||
static char gDefaultJumpsuitMaleFileName[] = "hmjmps";
|
||||
@@ -132,18 +134,14 @@ int artInit()
|
||||
File* stream;
|
||||
char string[200];
|
||||
|
||||
int cacheSize;
|
||||
if (!configGetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_ART_CACHE_SIZE_KEY, &cacheSize)) {
|
||||
cacheSize = 8;
|
||||
}
|
||||
|
||||
int cacheSize = settings.system.art_cache_size;
|
||||
if (!cacheInit(&gArtCache, artCacheGetFileSizeImpl, artCacheReadDataImpl, artCacheFreeImpl, cacheSize << 20)) {
|
||||
debugPrint("cache_init failed in art_init\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char* language;
|
||||
if (configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_LANGUAGE_KEY, &language) && compat_stricmp(language, ENGLISH) != 0) {
|
||||
const char* language = settings.system.language.c_str();
|
||||
if (compat_stricmp(language, ENGLISH) != 0) {
|
||||
strcpy(gArtLanguage, language);
|
||||
gArtLanguageInitialized = true;
|
||||
}
|
||||
@@ -151,28 +149,13 @@ int artInit()
|
||||
bool critterDbSelected = false;
|
||||
for (int objectType = 0; objectType < OBJ_TYPE_COUNT; objectType++) {
|
||||
gArtListDescriptions[objectType].flags = 0;
|
||||
sprintf(path, "%s%s%s\\%s.lst", _cd_path_base, "art\\", gArtListDescriptions[objectType].name, gArtListDescriptions[objectType].name);
|
||||
|
||||
int oldDb;
|
||||
if (objectType == OBJ_TYPE_CRITTER) {
|
||||
oldDb = _db_current();
|
||||
critterDbSelected = true;
|
||||
_db_select(_critter_db_handle);
|
||||
}
|
||||
snprintf(path, sizeof(path), "%s%s%s\\%s.lst", _cd_path_base, "art\\", gArtListDescriptions[objectType].name, gArtListDescriptions[objectType].name);
|
||||
|
||||
if (artReadList(path, &(gArtListDescriptions[objectType].fileNames), &(gArtListDescriptions[objectType].fileNamesLength)) != 0) {
|
||||
debugPrint("art_read_lst failed in art_init\n");
|
||||
if (critterDbSelected) {
|
||||
_db_select(oldDb);
|
||||
}
|
||||
cacheFree(&gArtCache);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (objectType == OBJ_TYPE_CRITTER) {
|
||||
critterDbSelected = false;
|
||||
_db_select(oldDb);
|
||||
}
|
||||
}
|
||||
|
||||
_anon_alias = (int*)internal_malloc(sizeof(*_anon_alias) * gArtListDescriptions[OBJ_TYPE_CRITTER].fileNamesLength);
|
||||
@@ -195,7 +178,7 @@ int artInit()
|
||||
gArtCritterFidShoudRunData[critterIndex] = 0;
|
||||
}
|
||||
|
||||
sprintf(path, "%s%s%s\\%s.lst", _cd_path_base, "art\\", gArtListDescriptions[OBJ_TYPE_CRITTER].name, gArtListDescriptions[OBJ_TYPE_CRITTER].name);
|
||||
snprintf(path, sizeof(path), "%s%s%s\\%s.lst", _cd_path_base, "art\\", gArtListDescriptions[OBJ_TYPE_CRITTER].name, gArtListDescriptions[OBJ_TYPE_CRITTER].name);
|
||||
|
||||
stream = fileOpen(path, "rt");
|
||||
if (stream == NULL) {
|
||||
@@ -231,16 +214,16 @@ int artInit()
|
||||
|
||||
char* critterFileNames = gArtListDescriptions[OBJ_TYPE_CRITTER].fileNames;
|
||||
for (int critterIndex = 0; critterIndex < gArtListDescriptions[OBJ_TYPE_CRITTER].fileNamesLength; critterIndex++) {
|
||||
if (compat_stricmp(critterFileNames, "hmjmps") == 0) {
|
||||
if (compat_stricmp(critterFileNames, jumpsuitMaleFileName) == 0) {
|
||||
_art_vault_person_nums[DUDE_NATIVE_LOOK_JUMPSUIT][GENDER_MALE] = critterIndex;
|
||||
} else if (compat_stricmp(critterFileNames, "hfjmps") == 0) {
|
||||
} else if (compat_stricmp(critterFileNames, jumpsuitFemaleFileName) == 0) {
|
||||
_art_vault_person_nums[DUDE_NATIVE_LOOK_JUMPSUIT][GENDER_FEMALE] = critterIndex;
|
||||
}
|
||||
|
||||
if (compat_stricmp(critterFileNames, "hmwarr") == 0) {
|
||||
if (compat_stricmp(critterFileNames, tribalMaleFileName) == 0) {
|
||||
_art_vault_person_nums[DUDE_NATIVE_LOOK_TRIBAL][GENDER_MALE] = critterIndex;
|
||||
_art_vault_guy_num = critterIndex;
|
||||
} else if (compat_stricmp(critterFileNames, "hfprim") == 0) {
|
||||
} else if (compat_stricmp(critterFileNames, tribalFemaleFileName) == 0) {
|
||||
_art_vault_person_nums[DUDE_NATIVE_LOOK_TRIBAL][GENDER_FEMALE] = critterIndex;
|
||||
}
|
||||
|
||||
@@ -286,7 +269,7 @@ int artInit()
|
||||
return -1;
|
||||
}
|
||||
|
||||
sprintf(path, "%s%s%s\\%s.lst", _cd_path_base, "art\\", gArtListDescriptions[OBJ_TYPE_HEAD].name, gArtListDescriptions[OBJ_TYPE_HEAD].name);
|
||||
snprintf(path, sizeof(path), "%s%s%s\\%s.lst", _cd_path_base, "art\\", gArtListDescriptions[OBJ_TYPE_HEAD].name, gArtListDescriptions[OBJ_TYPE_HEAD].name);
|
||||
|
||||
stream = fileOpen(path, "rt");
|
||||
if (stream == NULL) {
|
||||
@@ -494,7 +477,7 @@ unsigned char* artLockFrameDataReturningSize(int fid, CacheEntry** handlePtr, in
|
||||
{
|
||||
*handlePtr = NULL;
|
||||
|
||||
Art* art;
|
||||
Art* art = NULL;
|
||||
cacheLock(&gArtCache, fid, (void**)&art, handlePtr);
|
||||
|
||||
if (art == NULL) {
|
||||
@@ -657,19 +640,19 @@ char* artBuildFilePath(int fid)
|
||||
return NULL;
|
||||
}
|
||||
if (v10) {
|
||||
sprintf(_art_name, "%s%s%s\\%s%c%c.fr%c", _cd_path_base, "art\\", gArtListDescriptions[1].name, gArtListDescriptions[1].fileNames + v8, v11, v12, v10 + 47);
|
||||
snprintf(_art_name, sizeof(_art_name), "%s%s%s\\%s%c%c.fr%c", _cd_path_base, "art\\", gArtListDescriptions[1].name, gArtListDescriptions[1].fileNames + v8, v11, v12, v10 + 47);
|
||||
} else {
|
||||
sprintf(_art_name, "%s%s%s\\%s%c%c.frm", _cd_path_base, "art\\", gArtListDescriptions[1].name, gArtListDescriptions[1].fileNames + v8, v11, v12);
|
||||
snprintf(_art_name, sizeof(_art_name), "%s%s%s\\%s%c%c.frm", _cd_path_base, "art\\", gArtListDescriptions[1].name, gArtListDescriptions[1].fileNames + v8, v11, v12);
|
||||
}
|
||||
} else if (type == 8) {
|
||||
v9 = _head2[v4];
|
||||
if (v9 == 'f') {
|
||||
sprintf(_art_name, "%s%s%s\\%s%c%c%d.frm", _cd_path_base, "art\\", gArtListDescriptions[8].name, gArtListDescriptions[8].fileNames + v8, _head1[v4], 102, v5);
|
||||
snprintf(_art_name, sizeof(_art_name), "%s%s%s\\%s%c%c%d.frm", _cd_path_base, "art\\", gArtListDescriptions[8].name, gArtListDescriptions[8].fileNames + v8, _head1[v4], 102, v5);
|
||||
} else {
|
||||
sprintf(_art_name, "%s%s%s\\%s%c%c.frm", _cd_path_base, "art\\", gArtListDescriptions[8].name, gArtListDescriptions[8].fileNames + v8, _head1[v4], v9);
|
||||
snprintf(_art_name, sizeof(_art_name), "%s%s%s\\%s%c%c.frm", _cd_path_base, "art\\", gArtListDescriptions[8].name, gArtListDescriptions[8].fileNames + v8, _head1[v4], v9);
|
||||
}
|
||||
} else {
|
||||
sprintf(_art_name, "%s%s%s\\%s", _cd_path_base, "art\\", gArtListDescriptions[type].name, gArtListDescriptions[type].fileNames + v8);
|
||||
snprintf(_art_name, sizeof(_art_name), "%s%s%s\\%s", _cd_path_base, "art\\", gArtListDescriptions[type].name, gArtListDescriptions[type].fileNames + v8);
|
||||
}
|
||||
|
||||
return _art_name;
|
||||
@@ -852,9 +835,9 @@ ArtFrame* artGetFrame(Art* art, int frame, int rotation)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ArtFrame* frm = (ArtFrame*)((unsigned char*)art + sizeof(*art) + art->dataOffsets[rotation]);
|
||||
ArtFrame* frm = (ArtFrame*)((unsigned char*)art + sizeof(*art) + art->dataOffsets[rotation] + art->padding[rotation]);
|
||||
for (int index = 0; index < frame; index++) {
|
||||
frm = (ArtFrame*)((unsigned char*)frm + sizeof(*frm) + frm->size);
|
||||
frm = (ArtFrame*)((unsigned char*)frm + sizeof(*frm) + frm->size + paddingForSize(frm->size));
|
||||
}
|
||||
return frm;
|
||||
}
|
||||
@@ -863,12 +846,6 @@ ArtFrame* artGetFrame(Art* art, int frame, int rotation)
|
||||
bool artExists(int fid)
|
||||
{
|
||||
bool result = false;
|
||||
int oldDb = -1;
|
||||
|
||||
if (FID_TYPE(fid) == OBJ_TYPE_CRITTER) {
|
||||
oldDb = _db_current();
|
||||
_db_select(_critter_db_handle);
|
||||
}
|
||||
|
||||
char* filePath = artBuildFilePath(fid);
|
||||
if (filePath != NULL) {
|
||||
@@ -878,10 +855,6 @@ bool artExists(int fid)
|
||||
}
|
||||
}
|
||||
|
||||
if (oldDb != -1) {
|
||||
_db_select(oldDb);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -891,12 +864,6 @@ bool artExists(int fid)
|
||||
bool _art_fid_valid(int fid)
|
||||
{
|
||||
bool result = false;
|
||||
int oldDb = -1;
|
||||
|
||||
if (FID_TYPE(fid) == OBJ_TYPE_CRITTER) {
|
||||
oldDb = _db_current();
|
||||
_db_select(_critter_db_handle);
|
||||
}
|
||||
|
||||
char* filePath = artBuildFilePath(fid);
|
||||
if (filePath != NULL) {
|
||||
@@ -906,10 +873,6 @@ bool _art_fid_valid(int fid)
|
||||
}
|
||||
}
|
||||
|
||||
if (oldDb != -1) {
|
||||
_db_select(oldDb);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -956,18 +919,12 @@ int artAliasFid(int fid)
|
||||
// 0x419A78
|
||||
static int artCacheGetFileSizeImpl(int fid, int* sizePtr)
|
||||
{
|
||||
int oldDb = -1;
|
||||
int result = -1;
|
||||
|
||||
if (FID_TYPE(fid) == OBJ_TYPE_CRITTER) {
|
||||
oldDb = _db_current();
|
||||
_db_select(_critter_db_handle);
|
||||
}
|
||||
|
||||
char* artFilePath = artBuildFilePath(fid);
|
||||
if (artFilePath != NULL) {
|
||||
int fileSize;
|
||||
bool loaded = false;
|
||||
File* stream = NULL;
|
||||
|
||||
if (gArtLanguageInitialized) {
|
||||
char* pch = strchr(artFilePath, '\\');
|
||||
@@ -976,27 +933,23 @@ static int artCacheGetFileSizeImpl(int fid, int* sizePtr)
|
||||
}
|
||||
|
||||
char localizedPath[COMPAT_MAX_PATH];
|
||||
sprintf(localizedPath, "art\\%s\\%s", gArtLanguage, pch);
|
||||
snprintf(localizedPath, sizeof(localizedPath), "art\\%s\\%s", gArtLanguage, pch);
|
||||
|
||||
if (dbGetFileSize(localizedPath, &fileSize) == 0) {
|
||||
loaded = true;
|
||||
stream = fileOpen(localizedPath, "rb");
|
||||
}
|
||||
|
||||
if (stream == NULL) {
|
||||
stream = fileOpen(artFilePath, "rb");
|
||||
}
|
||||
|
||||
if (stream != NULL) {
|
||||
Art art;
|
||||
if (artReadHeader(&art, stream) == 0) {
|
||||
*sizePtr = artGetDataSize(&art);
|
||||
result = 0;
|
||||
}
|
||||
fileClose(stream);
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
if (dbGetFileSize(artFilePath, &fileSize) == 0) {
|
||||
loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (loaded) {
|
||||
*sizePtr = fileSize;
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (oldDb != -1) {
|
||||
_db_select(oldDb);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -1005,14 +958,8 @@ static int artCacheGetFileSizeImpl(int fid, int* sizePtr)
|
||||
// 0x419B78
|
||||
static int artCacheReadDataImpl(int fid, int* sizePtr, unsigned char* data)
|
||||
{
|
||||
int oldDb = -1;
|
||||
int result = -1;
|
||||
|
||||
if (FID_TYPE(fid) == OBJ_TYPE_CRITTER) {
|
||||
oldDb = _db_current();
|
||||
_db_select(_critter_db_handle);
|
||||
}
|
||||
|
||||
char* artFileName = artBuildFilePath(fid);
|
||||
if (artFileName != NULL) {
|
||||
bool loaded = false;
|
||||
@@ -1023,7 +970,7 @@ static int artCacheReadDataImpl(int fid, int* sizePtr, unsigned char* data)
|
||||
}
|
||||
|
||||
char localizedPath[COMPAT_MAX_PATH];
|
||||
sprintf(localizedPath, "art\\%s\\%s", gArtLanguage, pch);
|
||||
snprintf(localizedPath, sizeof(localizedPath), "art\\%s\\%s", gArtLanguage, pch);
|
||||
|
||||
if (artRead(localizedPath, data) == 0) {
|
||||
loaded = true;
|
||||
@@ -1037,16 +984,11 @@ static int artCacheReadDataImpl(int fid, int* sizePtr, unsigned char* data)
|
||||
}
|
||||
|
||||
if (loaded) {
|
||||
// TODO: Why it adds 74?
|
||||
*sizePtr = ((Art*)data)->field_3A + 74;
|
||||
*sizePtr = artGetDataSize((Art*)data);
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (oldDb != -1) {
|
||||
_db_select(oldDb);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1098,9 +1040,10 @@ out:
|
||||
}
|
||||
|
||||
// 0x419D60
|
||||
static int artReadFrameData(unsigned char* data, File* stream, int count)
|
||||
static int artReadFrameData(unsigned char* data, File* stream, int count, int* paddingPtr)
|
||||
{
|
||||
unsigned char* ptr = data;
|
||||
int padding = 0;
|
||||
for (int index = 0; index < count; index++) {
|
||||
ArtFrame* frame = (ArtFrame*)ptr;
|
||||
|
||||
@@ -1112,8 +1055,12 @@ static int artReadFrameData(unsigned char* data, File* stream, int count)
|
||||
if (fileRead(ptr + sizeof(ArtFrame), frame->size, 1, stream) != 1) return -1;
|
||||
|
||||
ptr += sizeof(ArtFrame) + frame->size;
|
||||
ptr += paddingForSize(frame->size);
|
||||
padding += paddingForSize(frame->size);
|
||||
}
|
||||
|
||||
*paddingPtr = padding;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1127,11 +1074,50 @@ static int artReadHeader(Art* art, File* stream)
|
||||
if (fileReadInt16List(stream, art->xOffsets, ROTATION_COUNT) == -1) return -1;
|
||||
if (fileReadInt16List(stream, art->yOffsets, ROTATION_COUNT) == -1) return -1;
|
||||
if (fileReadInt32List(stream, art->dataOffsets, ROTATION_COUNT) == -1) return -1;
|
||||
if (fileReadInt32(stream, &(art->field_3A)) == -1) return -1;
|
||||
if (fileReadInt32(stream, &(art->dataSize)) == -1) return -1;
|
||||
|
||||
// CE: Fix malformed `frm` files with `dataSize` set to 0 in Nevada.
|
||||
if (art->dataSize == 0) {
|
||||
art->dataSize = fileGetSize(stream);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NOTE: Original function was slightly different, but never used. Basically
|
||||
// it's a memory allocating variant of `artRead` (which reads data into given
|
||||
// buffer). This function is useful to load custom `frm` files since `Art` now
|
||||
// needs more memory then it's on-disk size (due to memory padding).
|
||||
//
|
||||
// 0x419EC0
|
||||
Art* artLoad(const char* path)
|
||||
{
|
||||
File* stream = fileOpen(path, "rb");
|
||||
if (stream == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Art header;
|
||||
if (artReadHeader(&header, stream) != 0) {
|
||||
fileClose(stream);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
fileClose(stream);
|
||||
|
||||
unsigned char* data = reinterpret_cast<unsigned char*>(internal_malloc(artGetDataSize(&header)));
|
||||
if (data == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (artRead(path, data) != 0) {
|
||||
internal_free(data);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return reinterpret_cast<Art*>(data);
|
||||
}
|
||||
|
||||
// 0x419FC0
|
||||
int artRead(const char* path, unsigned char* data)
|
||||
{
|
||||
@@ -1146,9 +1132,16 @@ int artRead(const char* path, unsigned char* data)
|
||||
return -3;
|
||||
}
|
||||
|
||||
int currentPadding = paddingForSize(sizeof(Art));
|
||||
int previousPadding = 0;
|
||||
|
||||
for (int index = 0; index < ROTATION_COUNT; index++) {
|
||||
art->padding[index] = currentPadding;
|
||||
|
||||
if (index == 0 || art->dataOffsets[index - 1] != art->dataOffsets[index]) {
|
||||
if (artReadFrameData(data + sizeof(Art) + art->dataOffsets[index], stream, art->frameCount) != 0) {
|
||||
art->padding[index] += previousPadding;
|
||||
currentPadding += previousPadding;
|
||||
if (artReadFrameData(data + sizeof(Art) + art->dataOffsets[index] + art->padding[index], stream, art->frameCount, &previousPadding) != 0) {
|
||||
fileClose(stream);
|
||||
return -5;
|
||||
}
|
||||
@@ -1176,6 +1169,7 @@ int artWriteFrameData(unsigned char* data, File* stream, int count)
|
||||
if (fileWrite(ptr + sizeof(ArtFrame), frame->size, 1, stream) != 1) return -1;
|
||||
|
||||
ptr += sizeof(ArtFrame) + frame->size;
|
||||
ptr += paddingForSize(frame->size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1193,7 +1187,7 @@ int artWriteHeader(Art* art, File* stream)
|
||||
if (fileWriteInt16List(stream, art->xOffsets, ROTATION_COUNT) == -1) return -1;
|
||||
if (fileWriteInt16List(stream, art->yOffsets, ROTATION_COUNT) == -1) return -1;
|
||||
if (fileWriteInt32List(stream, art->dataOffsets, ROTATION_COUNT) == -1) return -1;
|
||||
if (fileWriteInt32(stream, art->field_3A) == -1) return -1;
|
||||
if (fileWriteInt32(stream, art->dataSize) == -1) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1220,7 +1214,7 @@ int artWrite(const char* path, unsigned char* data)
|
||||
|
||||
for (int index = 0; index < ROTATION_COUNT; index++) {
|
||||
if (index == 0 || art->dataOffsets[index - 1] != art->dataOffsets[index]) {
|
||||
if (artWriteFrameData(data + sizeof(Art) + art->dataOffsets[index], stream, art->frameCount) != 0) {
|
||||
if (artWriteFrameData(data + sizeof(Art) + art->dataOffsets[index] + art->padding[index], stream, art->frameCount) != 0) {
|
||||
fileClose(stream);
|
||||
return -1;
|
||||
}
|
||||
@@ -1231,6 +1225,26 @@ int artWrite(const char* path, unsigned char* data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int artGetDataSize(Art* art)
|
||||
{
|
||||
int dataSize = sizeof(*art) + art->dataSize;
|
||||
|
||||
for (int index = 0; index < ROTATION_COUNT; index++) {
|
||||
if (index == 0 || art->dataOffsets[index - 1] != art->dataOffsets[index]) {
|
||||
// Assume worst case - every frame is unaligned and need
|
||||
// max padding.
|
||||
dataSize += (sizeof(int) - 1) * art->frameCount;
|
||||
}
|
||||
}
|
||||
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
static int paddingForSize(int size)
|
||||
{
|
||||
return (sizeof(int) - size % sizeof(int)) % sizeof(int);
|
||||
}
|
||||
|
||||
FrmImage::FrmImage()
|
||||
{
|
||||
_key = nullptr;
|
||||
|
||||
@@ -67,7 +67,6 @@ typedef enum Background {
|
||||
BACKGROUND_COUNT,
|
||||
} Background;
|
||||
|
||||
#pragma pack(2)
|
||||
typedef struct Art {
|
||||
int field_0;
|
||||
short framesPerSecond;
|
||||
@@ -76,9 +75,9 @@ typedef struct Art {
|
||||
short xOffsets[6];
|
||||
short yOffsets[6];
|
||||
int dataOffsets[6];
|
||||
int field_3A;
|
||||
int padding[6];
|
||||
int dataSize;
|
||||
} Art;
|
||||
#pragma pack()
|
||||
|
||||
typedef struct ArtFrame {
|
||||
short width;
|
||||
@@ -148,6 +147,7 @@ int _art_alias_num(int a1);
|
||||
int artCritterFidShouldRun(int a1);
|
||||
int artAliasFid(int fid);
|
||||
int buildFid(int objectType, int frmId, int animType, int a4, int rotation);
|
||||
Art* artLoad(const char* path);
|
||||
int artRead(const char* path, unsigned char* data);
|
||||
int artWrite(const char* path, unsigned char* data);
|
||||
|
||||
|
||||
+63
-67
@@ -7,76 +7,70 @@
|
||||
#include "db.h"
|
||||
#include "debug.h"
|
||||
#include "memory_manager.h"
|
||||
#include "pointer_registry.h"
|
||||
#include "sound.h"
|
||||
#include "sound_decoder.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
static bool _defaultCompressionFunc(char* filePath);
|
||||
static int audioSoundDecoderReadHandler(int fileHandle, void* buf, unsigned int size);
|
||||
typedef enum AudioFlags {
|
||||
AUDIO_IN_USE = 0x01,
|
||||
AUDIO_COMPRESSED = 0x02,
|
||||
} AudioFileFlags;
|
||||
|
||||
typedef struct Audio {
|
||||
int flags;
|
||||
File* stream;
|
||||
SoundDecoder* soundDecoder;
|
||||
int fileSize;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int position;
|
||||
} Audio;
|
||||
|
||||
static bool defaultCompressionFunc(char* filePath);
|
||||
static int audioSoundDecoderReadHandler(void* data, void* buf, unsigned int size);
|
||||
|
||||
// 0x5108BC
|
||||
static AudioFileIsCompressedProc* _queryCompressedFunc = _defaultCompressionFunc;
|
||||
static AudioQueryCompressedFunc* queryCompressedFunc = defaultCompressionFunc;
|
||||
|
||||
// 0x56CB00
|
||||
static int gAudioListLength;
|
||||
|
||||
// 0x56CB04
|
||||
static AudioFile* gAudioList;
|
||||
static Audio* gAudioList;
|
||||
|
||||
// 0x41A2B0
|
||||
static bool _defaultCompressionFunc(char* filePath)
|
||||
static bool defaultCompressionFunc(char* filePath)
|
||||
{
|
||||
char* pch = strrchr(filePath, '.');
|
||||
if (pch != NULL) {
|
||||
strcpy(pch + 1, "war");
|
||||
strcpy(pch + 1, "raw");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 0x41A2D0
|
||||
static int audioSoundDecoderReadHandler(int fileHandle, void* buffer, unsigned int size)
|
||||
static int audioSoundDecoderReadHandler(void* data, void* buffer, unsigned int size)
|
||||
{
|
||||
return fileRead(buffer, 1, size, (File*)intToPtr(fileHandle));
|
||||
return fileRead(buffer, 1, size, reinterpret_cast<File*>(data));
|
||||
}
|
||||
|
||||
// AudioOpen
|
||||
// 0x41A2EC
|
||||
int audioOpen(const char* fname, int flags, ...)
|
||||
int audioOpen(const char* fname, int* sampleRate)
|
||||
{
|
||||
char path[80];
|
||||
sprintf(path, "%s", fname);
|
||||
snprintf(path, sizeof(path), "%s", fname);
|
||||
|
||||
int compression;
|
||||
if (_queryCompressedFunc(path)) {
|
||||
if (queryCompressedFunc(path)) {
|
||||
compression = 2;
|
||||
} else {
|
||||
compression = 0;
|
||||
}
|
||||
|
||||
char mode[4];
|
||||
memset(mode, 0, 4);
|
||||
|
||||
// NOTE: Original implementation is slightly different, it uses separate
|
||||
// variable to track index where to set 't' and 'b'.
|
||||
char* pm = mode;
|
||||
if (flags & 1) {
|
||||
*pm++ = 'w';
|
||||
} else if (flags & 2) {
|
||||
*pm++ = 'w';
|
||||
*pm++ = '+';
|
||||
} else {
|
||||
*pm++ = 'r';
|
||||
}
|
||||
|
||||
if (flags & 0x100) {
|
||||
*pm++ = 't';
|
||||
} else if (flags & 0x200) {
|
||||
*pm++ = 'b';
|
||||
}
|
||||
|
||||
File* stream = fileOpen(path, mode);
|
||||
File* stream = fileOpen(path, "rb");
|
||||
if (stream == NULL) {
|
||||
debugPrint("AudioOpen: Couldn't open %s for read\n", path);
|
||||
return -1;
|
||||
@@ -84,28 +78,30 @@ int audioOpen(const char* fname, int flags, ...)
|
||||
|
||||
int index;
|
||||
for (index = 0; index < gAudioListLength; index++) {
|
||||
if ((gAudioList[index].flags & AUDIO_FILE_IN_USE) == 0) {
|
||||
if ((gAudioList[index].flags & AUDIO_IN_USE) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index == gAudioListLength) {
|
||||
if (gAudioList != NULL) {
|
||||
gAudioList = (AudioFile*)internal_realloc_safe(gAudioList, sizeof(*gAudioList) * (gAudioListLength + 1), __FILE__, __LINE__); // "..\int\audio.c", 216
|
||||
gAudioList = (Audio*)internal_realloc_safe(gAudioList, sizeof(*gAudioList) * (gAudioListLength + 1), __FILE__, __LINE__); // "..\int\audio.c", 216
|
||||
} else {
|
||||
gAudioList = (AudioFile*)internal_malloc_safe(sizeof(*gAudioList), __FILE__, __LINE__); // "..\int\audio.c", 218
|
||||
gAudioList = (Audio*)internal_malloc_safe(sizeof(*gAudioList), __FILE__, __LINE__); // "..\int\audio.c", 218
|
||||
}
|
||||
gAudioListLength++;
|
||||
}
|
||||
|
||||
AudioFile* audioFile = &(gAudioList[index]);
|
||||
audioFile->flags = AUDIO_FILE_IN_USE;
|
||||
audioFile->fileHandle = ptrToInt(stream);
|
||||
Audio* audioFile = &(gAudioList[index]);
|
||||
audioFile->flags = AUDIO_IN_USE;
|
||||
audioFile->stream = stream;
|
||||
|
||||
if (compression == 2) {
|
||||
audioFile->flags |= AUDIO_FILE_COMPRESSED;
|
||||
audioFile->soundDecoder = soundDecoderInit(audioSoundDecoderReadHandler, audioFile->fileHandle, &(audioFile->field_14), &(audioFile->field_10), &(audioFile->fileSize));
|
||||
audioFile->flags |= AUDIO_COMPRESSED;
|
||||
audioFile->soundDecoder = soundDecoderInit(audioSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize));
|
||||
audioFile->fileSize *= 2;
|
||||
|
||||
*sampleRate = audioFile->sampleRate;
|
||||
} else {
|
||||
audioFile->fileSize = fileGetSize(stream);
|
||||
}
|
||||
@@ -116,30 +112,30 @@ int audioOpen(const char* fname, int flags, ...)
|
||||
}
|
||||
|
||||
// 0x41A50C
|
||||
int audioClose(int fileHandle)
|
||||
int audioClose(int handle)
|
||||
{
|
||||
AudioFile* audioFile = &(gAudioList[fileHandle - 1]);
|
||||
fileClose((File*)intToPtr(audioFile->fileHandle, true));
|
||||
Audio* audioFile = &(gAudioList[handle - 1]);
|
||||
fileClose(audioFile->stream);
|
||||
|
||||
if ((audioFile->flags & AUDIO_FILE_COMPRESSED) != 0) {
|
||||
if ((audioFile->flags & AUDIO_COMPRESSED) != 0) {
|
||||
soundDecoderFree(audioFile->soundDecoder);
|
||||
}
|
||||
|
||||
memset(audioFile, 0, sizeof(AudioFile));
|
||||
memset(audioFile, 0, sizeof(Audio));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x41A574
|
||||
int audioRead(int fileHandle, void* buffer, unsigned int size)
|
||||
int audioRead(int handle, void* buffer, unsigned int size)
|
||||
{
|
||||
AudioFile* audioFile = &(gAudioList[fileHandle - 1]);
|
||||
Audio* audioFile = &(gAudioList[handle - 1]);
|
||||
|
||||
int bytesRead;
|
||||
if ((audioFile->flags & AUDIO_FILE_COMPRESSED) != 0) {
|
||||
if ((audioFile->flags & AUDIO_COMPRESSED) != 0) {
|
||||
bytesRead = soundDecoderDecode(audioFile->soundDecoder, buffer, size);
|
||||
} else {
|
||||
bytesRead = fileRead(buffer, 1, size, (File*)intToPtr(audioFile->fileHandle));
|
||||
bytesRead = fileRead(buffer, 1, size, audioFile->stream);
|
||||
}
|
||||
|
||||
audioFile->position += bytesRead;
|
||||
@@ -148,13 +144,13 @@ int audioRead(int fileHandle, void* buffer, unsigned int size)
|
||||
}
|
||||
|
||||
// 0x41A5E0
|
||||
long audioSeek(int fileHandle, long offset, int origin)
|
||||
long audioSeek(int handle, long offset, int origin)
|
||||
{
|
||||
int pos;
|
||||
unsigned char* buf;
|
||||
int v10;
|
||||
|
||||
AudioFile* audioFile = &(gAudioList[fileHandle - 1]);
|
||||
Audio* audioFile = &(gAudioList[handle - 1]);
|
||||
|
||||
switch (origin) {
|
||||
case SEEK_SET:
|
||||
@@ -170,11 +166,11 @@ long audioSeek(int fileHandle, long offset, int origin)
|
||||
assert(false && "Should be unreachable");
|
||||
}
|
||||
|
||||
if ((audioFile->flags & AUDIO_FILE_COMPRESSED) != 0) {
|
||||
if ((audioFile->flags & AUDIO_COMPRESSED) != 0) {
|
||||
if (pos < audioFile->position) {
|
||||
soundDecoderFree(audioFile->soundDecoder);
|
||||
fileSeek((File*)intToPtr(audioFile->fileHandle), 0, SEEK_SET);
|
||||
audioFile->soundDecoder = soundDecoderInit(audioSoundDecoderReadHandler, audioFile->fileHandle, &(audioFile->field_14), &(audioFile->field_10), &(audioFile->fileSize));
|
||||
fileSeek(audioFile->stream, 0, SEEK_SET);
|
||||
audioFile->soundDecoder = soundDecoderInit(audioSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize));
|
||||
audioFile->position = 0;
|
||||
audioFile->fileSize *= 2;
|
||||
|
||||
@@ -182,11 +178,11 @@ long audioSeek(int fileHandle, long offset, int origin)
|
||||
buf = (unsigned char*)internal_malloc_safe(4096, __FILE__, __LINE__); // "..\int\audio.c", 361
|
||||
while (pos > 4096) {
|
||||
pos -= 4096;
|
||||
audioRead(fileHandle, buf, 4096);
|
||||
audioRead(handle, buf, 4096);
|
||||
}
|
||||
|
||||
if (pos != 0) {
|
||||
audioRead(fileHandle, buf, pos);
|
||||
audioRead(handle, buf, pos);
|
||||
}
|
||||
|
||||
internal_free_safe(buf, __FILE__, __LINE__); // // "..\int\audio.c", 367
|
||||
@@ -196,11 +192,11 @@ long audioSeek(int fileHandle, long offset, int origin)
|
||||
v10 = audioFile->position - pos;
|
||||
while (v10 > 1024) {
|
||||
v10 -= 1024;
|
||||
audioRead(fileHandle, buf, 1024);
|
||||
audioRead(handle, buf, 1024);
|
||||
}
|
||||
|
||||
if (v10 != 0) {
|
||||
audioRead(fileHandle, buf, v10);
|
||||
audioRead(handle, buf, v10);
|
||||
}
|
||||
|
||||
// TODO: Probably leaks memory.
|
||||
@@ -208,21 +204,21 @@ long audioSeek(int fileHandle, long offset, int origin)
|
||||
|
||||
return audioFile->position;
|
||||
} else {
|
||||
return fileSeek((File*)intToPtr(audioFile->fileHandle), offset, origin);
|
||||
return fileSeek(audioFile->stream, offset, origin);
|
||||
}
|
||||
}
|
||||
|
||||
// 0x41A78C
|
||||
long audioGetSize(int fileHandle)
|
||||
long audioGetSize(int handle)
|
||||
{
|
||||
AudioFile* audioFile = &(gAudioList[fileHandle - 1]);
|
||||
Audio* audioFile = &(gAudioList[handle - 1]);
|
||||
return audioFile->fileSize;
|
||||
}
|
||||
|
||||
// 0x41A7A8
|
||||
long audioTell(int fileHandle)
|
||||
long audioTell(int handle)
|
||||
{
|
||||
AudioFile* audioFile = &(gAudioList[fileHandle - 1]);
|
||||
Audio* audioFile = &(gAudioList[handle - 1]);
|
||||
return audioFile->position;
|
||||
}
|
||||
|
||||
@@ -235,9 +231,9 @@ int audioWrite(int handle, const void* buf, unsigned int size)
|
||||
}
|
||||
|
||||
// 0x41A7D4
|
||||
int audioInit(AudioFileIsCompressedProc* isCompressedProc)
|
||||
int audioInit(AudioQueryCompressedFunc* func)
|
||||
{
|
||||
_queryCompressedFunc = isCompressedProc;
|
||||
queryCompressedFunc = func;
|
||||
gAudioList = NULL;
|
||||
gAudioListLength = 0;
|
||||
|
||||
|
||||
+9
-9
@@ -1,18 +1,18 @@
|
||||
#ifndef AUDIO_H
|
||||
#define AUDIO_H
|
||||
|
||||
#include "audio_file.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
int audioOpen(const char* fname, int mode, ...);
|
||||
int audioClose(int fileHandle);
|
||||
int audioRead(int fileHandle, void* buffer, unsigned int size);
|
||||
long audioSeek(int fileHandle, long offset, int origin);
|
||||
long audioGetSize(int fileHandle);
|
||||
long audioTell(int fileHandle);
|
||||
typedef bool(AudioQueryCompressedFunc)(char* filePath);
|
||||
|
||||
int audioOpen(const char* fname, int* sampleRate);
|
||||
int audioClose(int handle);
|
||||
int audioRead(int handle, void* buffer, unsigned int size);
|
||||
long audioSeek(int handle, long offset, int origin);
|
||||
long audioGetSize(int handle);
|
||||
long audioTell(int handle);
|
||||
int audioWrite(int handle, const void* buf, unsigned int size);
|
||||
int audioInit(AudioFileIsCompressedProc* isCompressedProc);
|
||||
int audioInit(AudioQueryCompressedFunc* func);
|
||||
void audioExit();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
+59
-4
@@ -34,6 +34,11 @@ static SDL_AudioSpec gAudioEngineSpec;
|
||||
static SDL_AudioDeviceID gAudioEngineDeviceId = -1;
|
||||
static AudioEngineSoundBuffer gAudioEngineSoundBuffers[AUDIO_ENGINE_SOUND_BUFFERS];
|
||||
|
||||
static bool audioEngineIsInitialized()
|
||||
{
|
||||
return gAudioEngineDeviceId != -1;
|
||||
}
|
||||
|
||||
static bool soundBufferIsValid(int soundBufferIndex)
|
||||
{
|
||||
return soundBufferIndex >= 0 && soundBufferIndex < AUDIO_ENGINE_SOUND_BUFFERS;
|
||||
@@ -113,30 +118,36 @@ bool audioEngineInit()
|
||||
|
||||
void audioEngineExit()
|
||||
{
|
||||
if (gAudioEngineDeviceId != -1) {
|
||||
if (audioEngineIsInitialized()) {
|
||||
SDL_CloseAudioDevice(gAudioEngineDeviceId);
|
||||
gAudioEngineDeviceId = -1;
|
||||
}
|
||||
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
if (SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
}
|
||||
}
|
||||
|
||||
void audioEnginePause()
|
||||
{
|
||||
if (gAudioEngineDeviceId != -1) {
|
||||
if (audioEngineIsInitialized()) {
|
||||
SDL_PauseAudioDevice(gAudioEngineDeviceId, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void audioEngineResume()
|
||||
{
|
||||
if (gAudioEngineDeviceId != -1) {
|
||||
if (audioEngineIsInitialized()) {
|
||||
SDL_PauseAudioDevice(gAudioEngineDeviceId, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int audioEngineCreateSoundBuffer(unsigned int size, int bitsPerSample, int channels, int rate)
|
||||
{
|
||||
if (!audioEngineIsInitialized()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int index = 0; index < AUDIO_ENGINE_SOUND_BUFFERS; index++) {
|
||||
AudioEngineSoundBuffer* soundBuffer = &(gAudioEngineSoundBuffers[index]);
|
||||
std::lock_guard<std::recursive_mutex> lock(soundBuffer->mutex);
|
||||
@@ -162,6 +173,10 @@ int audioEngineCreateSoundBuffer(unsigned int size, int bitsPerSample, int chann
|
||||
|
||||
bool audioEngineSoundBufferRelease(int soundBufferIndex)
|
||||
{
|
||||
if (!audioEngineIsInitialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!soundBufferIsValid(soundBufferIndex)) {
|
||||
return false;
|
||||
}
|
||||
@@ -186,6 +201,10 @@ bool audioEngineSoundBufferRelease(int soundBufferIndex)
|
||||
|
||||
bool audioEngineSoundBufferSetVolume(int soundBufferIndex, int volume)
|
||||
{
|
||||
if (!audioEngineIsInitialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!soundBufferIsValid(soundBufferIndex)) {
|
||||
return false;
|
||||
}
|
||||
@@ -204,6 +223,10 @@ bool audioEngineSoundBufferSetVolume(int soundBufferIndex, int volume)
|
||||
|
||||
bool audioEngineSoundBufferGetVolume(int soundBufferIndex, int* volumePtr)
|
||||
{
|
||||
if (!audioEngineIsInitialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!soundBufferIsValid(soundBufferIndex)) {
|
||||
return false;
|
||||
}
|
||||
@@ -222,6 +245,10 @@ bool audioEngineSoundBufferGetVolume(int soundBufferIndex, int* volumePtr)
|
||||
|
||||
bool audioEngineSoundBufferSetPan(int soundBufferIndex, int pan)
|
||||
{
|
||||
if (!audioEngineIsInitialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!soundBufferIsValid(soundBufferIndex)) {
|
||||
return false;
|
||||
}
|
||||
@@ -241,6 +268,10 @@ bool audioEngineSoundBufferSetPan(int soundBufferIndex, int pan)
|
||||
|
||||
bool audioEngineSoundBufferPlay(int soundBufferIndex, unsigned int flags)
|
||||
{
|
||||
if (!audioEngineIsInitialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!soundBufferIsValid(soundBufferIndex)) {
|
||||
return false;
|
||||
}
|
||||
@@ -263,6 +294,10 @@ bool audioEngineSoundBufferPlay(int soundBufferIndex, unsigned int flags)
|
||||
|
||||
bool audioEngineSoundBufferStop(int soundBufferIndex)
|
||||
{
|
||||
if (!audioEngineIsInitialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!soundBufferIsValid(soundBufferIndex)) {
|
||||
return false;
|
||||
}
|
||||
@@ -281,6 +316,10 @@ bool audioEngineSoundBufferStop(int soundBufferIndex)
|
||||
|
||||
bool audioEngineSoundBufferGetCurrentPosition(int soundBufferIndex, unsigned int* readPosPtr, unsigned int* writePosPtr)
|
||||
{
|
||||
if (!audioEngineIsInitialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!soundBufferIsValid(soundBufferIndex)) {
|
||||
return false;
|
||||
}
|
||||
@@ -312,6 +351,10 @@ bool audioEngineSoundBufferGetCurrentPosition(int soundBufferIndex, unsigned int
|
||||
|
||||
bool audioEngineSoundBufferSetCurrentPosition(int soundBufferIndex, unsigned int pos)
|
||||
{
|
||||
if (!audioEngineIsInitialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!soundBufferIsValid(soundBufferIndex)) {
|
||||
return false;
|
||||
}
|
||||
@@ -330,6 +373,10 @@ bool audioEngineSoundBufferSetCurrentPosition(int soundBufferIndex, unsigned int
|
||||
|
||||
bool audioEngineSoundBufferLock(int soundBufferIndex, unsigned int writePos, unsigned int writeBytes, void** audioPtr1, unsigned int* audioBytes1, void** audioPtr2, unsigned int* audioBytes2, unsigned int flags)
|
||||
{
|
||||
if (!audioEngineIsInitialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!soundBufferIsValid(soundBufferIndex)) {
|
||||
return false;
|
||||
}
|
||||
@@ -387,6 +434,10 @@ bool audioEngineSoundBufferLock(int soundBufferIndex, unsigned int writePos, uns
|
||||
|
||||
bool audioEngineSoundBufferUnlock(int soundBufferIndex, void* audioPtr1, unsigned int audioBytes1, void* audioPtr2, unsigned int audioBytes2)
|
||||
{
|
||||
if (!audioEngineIsInitialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!soundBufferIsValid(soundBufferIndex)) {
|
||||
return false;
|
||||
}
|
||||
@@ -405,6 +456,10 @@ bool audioEngineSoundBufferUnlock(int soundBufferIndex, void* audioPtr1, unsigne
|
||||
|
||||
bool audioEngineSoundBufferGetStatus(int soundBufferIndex, unsigned int* statusPtr)
|
||||
{
|
||||
if (!audioEngineIsInitialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!soundBufferIsValid(soundBufferIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
+52
-56
@@ -7,16 +7,31 @@
|
||||
#include "debug.h"
|
||||
#include "memory_manager.h"
|
||||
#include "platform_compat.h"
|
||||
#include "pointer_registry.h"
|
||||
#include "sound.h"
|
||||
#include "sound_decoder.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
static bool _defaultCompressionFunc__(char* filePath);
|
||||
static int audioFileSoundDecoderReadHandler(int fileHandle, void* buffer, unsigned int size);
|
||||
typedef enum AudioFileFlags {
|
||||
AUDIO_FILE_IN_USE = 0x01,
|
||||
AUDIO_FILE_COMPRESSED = 0x02,
|
||||
} AudioFileFlags;
|
||||
|
||||
typedef struct AudioFile {
|
||||
int flags;
|
||||
FILE* stream;
|
||||
SoundDecoder* soundDecoder;
|
||||
int fileSize;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int position;
|
||||
} AudioFile;
|
||||
|
||||
static bool defaultCompressionFunc(char* filePath);
|
||||
static int audioFileSoundDecoderReadHandler(void* data, void* buffer, unsigned int size);
|
||||
|
||||
// 0x5108C0
|
||||
static AudioFileIsCompressedProc* _queryCompressedFunc_2 = _defaultCompressionFunc__;
|
||||
static AudioFileQueryCompressedFunc* queryCompressedFunc = defaultCompressionFunc;
|
||||
|
||||
// 0x56CB10
|
||||
static AudioFile* gAudioFileList;
|
||||
@@ -25,57 +40,36 @@ static AudioFile* gAudioFileList;
|
||||
static int gAudioFileListLength;
|
||||
|
||||
// 0x41A850
|
||||
static bool _defaultCompressionFunc__(char* filePath)
|
||||
static bool defaultCompressionFunc(char* filePath)
|
||||
{
|
||||
char* pch = strrchr(filePath, '.');
|
||||
if (pch != NULL) {
|
||||
strcpy(pch + 1, "war");
|
||||
strcpy(pch + 1, "raw");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 0x41A870
|
||||
static int audioFileSoundDecoderReadHandler(int fileHandle, void* buffer, unsigned int size)
|
||||
static int audioFileSoundDecoderReadHandler(void* data, void* buffer, unsigned int size)
|
||||
{
|
||||
return fread(buffer, 1, size, (FILE*)intToPtr(fileHandle));
|
||||
return fread(buffer, 1, size, reinterpret_cast<FILE*>(data));
|
||||
}
|
||||
|
||||
// 0x41A88C
|
||||
int audioFileOpen(const char* fname, int flags, ...)
|
||||
int audioFileOpen(const char* fname, int* sampleRate)
|
||||
{
|
||||
char path[COMPAT_MAX_PATH];
|
||||
strcpy(path, fname);
|
||||
|
||||
int compression;
|
||||
if (_queryCompressedFunc_2(path)) {
|
||||
if (queryCompressedFunc(path)) {
|
||||
compression = 2;
|
||||
} else {
|
||||
compression = 0;
|
||||
}
|
||||
|
||||
char mode[4];
|
||||
memset(mode, '\0', 4);
|
||||
|
||||
// NOTE: Original implementation is slightly different, it uses separate
|
||||
// variable to track index where to set 't' and 'b'.
|
||||
char* pm = mode;
|
||||
if (flags & 0x01) {
|
||||
*pm++ = 'w';
|
||||
} else if (flags & 0x02) {
|
||||
*pm++ = 'w';
|
||||
*pm++ = '+';
|
||||
} else {
|
||||
*pm++ = 'r';
|
||||
}
|
||||
|
||||
if (flags & 0x0100) {
|
||||
*pm++ = 't';
|
||||
} else if (flags & 0x0200) {
|
||||
*pm++ = 'b';
|
||||
}
|
||||
|
||||
FILE* stream = compat_fopen(path, mode);
|
||||
FILE* stream = compat_fopen(path, "rb");
|
||||
if (stream == NULL) {
|
||||
return -1;
|
||||
}
|
||||
@@ -98,12 +92,14 @@ int audioFileOpen(const char* fname, int flags, ...)
|
||||
|
||||
AudioFile* audioFile = &(gAudioFileList[index]);
|
||||
audioFile->flags = AUDIO_FILE_IN_USE;
|
||||
audioFile->fileHandle = ptrToInt(stream);
|
||||
audioFile->stream = stream;
|
||||
|
||||
if (compression == 2) {
|
||||
audioFile->flags |= AUDIO_FILE_COMPRESSED;
|
||||
audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->fileHandle, &(audioFile->field_14), &(audioFile->field_10), &(audioFile->fileSize));
|
||||
audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize));
|
||||
audioFile->fileSize *= 2;
|
||||
|
||||
*sampleRate = audioFile->sampleRate;
|
||||
} else {
|
||||
audioFile->fileSize = getFileSize(stream);
|
||||
}
|
||||
@@ -114,10 +110,10 @@ int audioFileOpen(const char* fname, int flags, ...)
|
||||
}
|
||||
|
||||
// 0x41AAA0
|
||||
int audioFileClose(int fileHandle)
|
||||
int audioFileClose(int handle)
|
||||
{
|
||||
AudioFile* audioFile = &(gAudioFileList[fileHandle - 1]);
|
||||
fclose((FILE*)intToPtr(audioFile->fileHandle, true));
|
||||
AudioFile* audioFile = &(gAudioFileList[handle - 1]);
|
||||
fclose(audioFile->stream);
|
||||
|
||||
if ((audioFile->flags & AUDIO_FILE_COMPRESSED) != 0) {
|
||||
soundDecoderFree(audioFile->soundDecoder);
|
||||
@@ -130,16 +126,16 @@ int audioFileClose(int fileHandle)
|
||||
}
|
||||
|
||||
// 0x41AB08
|
||||
int audioFileRead(int fileHandle, void* buffer, unsigned int size)
|
||||
int audioFileRead(int handle, void* buffer, unsigned int size)
|
||||
{
|
||||
|
||||
AudioFile* ptr = &(gAudioFileList[fileHandle - 1]);
|
||||
AudioFile* ptr = &(gAudioFileList[handle - 1]);
|
||||
|
||||
int bytesRead;
|
||||
if ((ptr->flags & AUDIO_FILE_COMPRESSED) != 0) {
|
||||
bytesRead = soundDecoderDecode(ptr->soundDecoder, buffer, size);
|
||||
} else {
|
||||
bytesRead = fread(buffer, 1, size, (FILE*)intToPtr(ptr->fileHandle));
|
||||
bytesRead = fread(buffer, 1, size, ptr->stream);
|
||||
}
|
||||
|
||||
ptr->position += bytesRead;
|
||||
@@ -148,13 +144,13 @@ int audioFileRead(int fileHandle, void* buffer, unsigned int size)
|
||||
}
|
||||
|
||||
// 0x41AB74
|
||||
long audioFileSeek(int fileHandle, long offset, int origin)
|
||||
long audioFileSeek(int handle, long offset, int origin)
|
||||
{
|
||||
void* buf;
|
||||
int remaining;
|
||||
int a4;
|
||||
|
||||
AudioFile* audioFile = &(gAudioFileList[fileHandle - 1]);
|
||||
AudioFile* audioFile = &(gAudioFileList[handle - 1]);
|
||||
|
||||
switch (origin) {
|
||||
case SEEK_SET:
|
||||
@@ -174,20 +170,20 @@ long audioFileSeek(int fileHandle, long offset, int origin)
|
||||
if (a4 <= audioFile->position) {
|
||||
soundDecoderFree(audioFile->soundDecoder);
|
||||
|
||||
fseek((FILE*)intToPtr(audioFile->fileHandle), 0, 0);
|
||||
fseek(audioFile->stream, 0, 0);
|
||||
|
||||
audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->fileHandle, &(audioFile->field_14), &(audioFile->field_10), &(audioFile->fileSize));
|
||||
audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize));
|
||||
audioFile->fileSize *= 2;
|
||||
audioFile->position = 0;
|
||||
|
||||
if (a4) {
|
||||
buf = internal_malloc_safe(4096, __FILE__, __LINE__); // "..\int\audiof.c", 364
|
||||
while (a4 > 4096) {
|
||||
audioFileRead(fileHandle, buf, 4096);
|
||||
audioFileRead(handle, buf, 4096);
|
||||
a4 -= 4096;
|
||||
}
|
||||
if (a4 != 0) {
|
||||
audioFileRead(fileHandle, buf, a4);
|
||||
audioFileRead(handle, buf, a4);
|
||||
}
|
||||
internal_free_safe(buf, __FILE__, __LINE__); // "..\int\audiof.c", 370
|
||||
}
|
||||
@@ -195,46 +191,46 @@ long audioFileSeek(int fileHandle, long offset, int origin)
|
||||
buf = internal_malloc_safe(0x400, __FILE__, __LINE__); // "..\int\audiof.c", 316
|
||||
remaining = audioFile->position - a4;
|
||||
while (remaining > 1024) {
|
||||
audioFileRead(fileHandle, buf, 1024);
|
||||
audioFileRead(handle, buf, 1024);
|
||||
remaining -= 1024;
|
||||
}
|
||||
if (remaining != 0) {
|
||||
audioFileRead(fileHandle, buf, remaining);
|
||||
audioFileRead(handle, buf, remaining);
|
||||
}
|
||||
// TODO: Obiously leaks memory.
|
||||
}
|
||||
return audioFile->position;
|
||||
}
|
||||
|
||||
return fseek((FILE*)intToPtr(audioFile->fileHandle), offset, origin);
|
||||
return fseek(audioFile->stream, offset, origin);
|
||||
}
|
||||
|
||||
// 0x41AD20
|
||||
long audioFileGetSize(int fileHandle)
|
||||
long audioFileGetSize(int handle)
|
||||
{
|
||||
AudioFile* audioFile = &(gAudioFileList[fileHandle - 1]);
|
||||
AudioFile* audioFile = &(gAudioFileList[handle - 1]);
|
||||
return audioFile->fileSize;
|
||||
}
|
||||
|
||||
// 0x41AD3C
|
||||
long audioFileTell(int fileHandle)
|
||||
long audioFileTell(int handle)
|
||||
{
|
||||
AudioFile* audioFile = &(gAudioFileList[fileHandle - 1]);
|
||||
AudioFile* audioFile = &(gAudioFileList[handle - 1]);
|
||||
return audioFile->position;
|
||||
}
|
||||
|
||||
// AudiofWrite
|
||||
// 0x41AD58
|
||||
int audioFileWrite(int fileHandle, const void* buffer, unsigned int size)
|
||||
int audioFileWrite(int handle, const void* buffer, unsigned int size)
|
||||
{
|
||||
debugPrint("AudiofWrite shouldn't be ever called\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x41AD68
|
||||
int audioFileInit(AudioFileIsCompressedProc* isCompressedProc)
|
||||
int audioFileInit(AudioFileQueryCompressedFunc* func)
|
||||
{
|
||||
_queryCompressedFunc_2 = isCompressedProc;
|
||||
queryCompressedFunc = func;
|
||||
gAudioFileList = NULL;
|
||||
gAudioFileListLength = 0;
|
||||
|
||||
|
||||
+7
-24
@@ -1,35 +1,18 @@
|
||||
#ifndef AUDIO_FILE_H
|
||||
#define AUDIO_FILE_H
|
||||
|
||||
#include "sound_decoder.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
typedef enum AudioFileFlags {
|
||||
AUDIO_FILE_IN_USE = 0x01,
|
||||
AUDIO_FILE_COMPRESSED = 0x02,
|
||||
} AudioFileFlags;
|
||||
typedef bool(AudioFileQueryCompressedFunc)(char* filePath);
|
||||
|
||||
typedef struct AudioFile {
|
||||
int flags;
|
||||
int fileHandle;
|
||||
SoundDecoder* soundDecoder;
|
||||
int fileSize;
|
||||
int field_10;
|
||||
int field_14;
|
||||
int position;
|
||||
} AudioFile;
|
||||
|
||||
typedef bool(AudioFileIsCompressedProc)(char* filePath);
|
||||
|
||||
int audioFileOpen(const char* fname, int flags, ...);
|
||||
int audioFileClose(int a1);
|
||||
int audioFileRead(int a1, void* buf, unsigned int size);
|
||||
int audioFileOpen(const char* fname, int* sampleRate);
|
||||
int audioFileClose(int handle);
|
||||
int audioFileRead(int handle, void* buf, unsigned int size);
|
||||
long audioFileSeek(int handle, long offset, int origin);
|
||||
long audioFileGetSize(int a1);
|
||||
long audioFileTell(int a1);
|
||||
long audioFileGetSize(int handle);
|
||||
long audioFileTell(int handle);
|
||||
int audioFileWrite(int handle, const void* buf, unsigned int size);
|
||||
int audioFileInit(AudioFileIsCompressedProc* isCompressedProc);
|
||||
int audioFileInit(AudioFileQueryCompressedFunc* func);
|
||||
void audioFileExit();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
+23
-23
@@ -8,20 +8,22 @@
|
||||
#include "art.h"
|
||||
#include "color.h"
|
||||
#include "config.h"
|
||||
#include "core.h"
|
||||
#include "dbox.h"
|
||||
#include "debug.h"
|
||||
#include "draw.h"
|
||||
#include "game.h"
|
||||
#include "game_config.h"
|
||||
#include "game_mouse.h"
|
||||
#include "game_sound.h"
|
||||
#include "graph_lib.h"
|
||||
#include "input.h"
|
||||
#include "item.h"
|
||||
#include "kb.h"
|
||||
#include "map.h"
|
||||
#include "memory.h"
|
||||
#include "object.h"
|
||||
#include "platform_compat.h"
|
||||
#include "settings.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "window_manager.h"
|
||||
|
||||
@@ -269,12 +271,9 @@ int automapReset()
|
||||
// 0x41B81C
|
||||
void automapExit()
|
||||
{
|
||||
char* masterPatchesPath;
|
||||
if (configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_MASTER_PATCHES_KEY, &masterPatchesPath)) {
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "%s\\%s\\%s", masterPatchesPath, "MAPS", AUTOMAP_DB);
|
||||
compat_remove(path);
|
||||
}
|
||||
char path[COMPAT_MAX_PATH];
|
||||
snprintf(path, sizeof(path), "%s\\%s\\%s", settings.system.master_patches_path.c_str(), "MAPS", AUTOMAP_DB);
|
||||
compat_remove(path);
|
||||
}
|
||||
|
||||
// 0x41B87C
|
||||
@@ -298,6 +297,8 @@ int _automapDisplayMap(int map)
|
||||
// 0x41B8BC
|
||||
void automapShow(bool isInGame, bool isUsingScanner)
|
||||
{
|
||||
ScopedGameMode gm(GameMode::kAutomap);
|
||||
|
||||
int frmIds[AUTOMAP_FRM_COUNT];
|
||||
memcpy(frmIds, gAutomapFrmIds, sizeof(gAutomapFrmIds));
|
||||
|
||||
@@ -322,7 +323,7 @@ void automapShow(bool isInGame, bool isUsingScanner)
|
||||
|
||||
int automapWindowX = (screenGetWidth() - AUTOMAP_WINDOW_WIDTH) / 2;
|
||||
int automapWindowY = (screenGetHeight() - AUTOMAP_WINDOW_HEIGHT) / 2;
|
||||
int window = windowCreate(automapWindowX, automapWindowY, AUTOMAP_WINDOW_WIDTH, AUTOMAP_WINDOW_HEIGHT, color, WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
|
||||
int window = windowCreate(automapWindowX, automapWindowY, AUTOMAP_WINDOW_WIDTH, AUTOMAP_WINDOW_HEIGHT, color, WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
|
||||
|
||||
int scannerBtn = buttonCreate(window,
|
||||
111,
|
||||
@@ -398,11 +399,13 @@ void automapShow(bool isInGame, bool isUsingScanner)
|
||||
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
bool needsRefresh = false;
|
||||
|
||||
// FIXME: There is minor bug in the interface - pressing H/L to toggle
|
||||
// high/low details does not update switch state.
|
||||
int keyCode = _get_input();
|
||||
int keyCode = inputGetInput();
|
||||
switch (keyCode) {
|
||||
case KEY_TAB:
|
||||
case KEY_ESCAPE:
|
||||
@@ -478,6 +481,9 @@ void automapShow(bool isInGame, bool isUsingScanner)
|
||||
automapRenderInMapWindow(window, elevation, frmImages[AUTOMAP_FRM_BACKGROUND].getData(), gAutomapFlags);
|
||||
needsRefresh = false;
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
if (isoWasEnabled) {
|
||||
@@ -703,7 +709,7 @@ int automapSaveCurrent()
|
||||
|
||||
// NOTE: Not sure about the size.
|
||||
char path[256];
|
||||
sprintf(path, "%s\\%s", "MAPS", AUTOMAP_DB);
|
||||
snprintf(path, sizeof(path), "%s\\%s", "MAPS", AUTOMAP_DB);
|
||||
|
||||
File* stream1 = fileOpen(path, "r+b");
|
||||
if (stream1 == NULL) {
|
||||
@@ -734,7 +740,7 @@ int automapSaveCurrent()
|
||||
}
|
||||
|
||||
if (entryOffset != 0) {
|
||||
sprintf(path, "%s\\%s", "MAPS", AUTOMAP_TMP);
|
||||
snprintf(path, sizeof(path), "%s\\%s", "MAPS", AUTOMAP_TMP);
|
||||
|
||||
File* stream2 = fileOpen(path, "wb");
|
||||
if (stream2 == NULL) {
|
||||
@@ -828,15 +834,9 @@ int automapSaveCurrent()
|
||||
internal_free(gAutomapEntry.data);
|
||||
internal_free(gAutomapEntry.compressedData);
|
||||
|
||||
char* masterPatchesPath;
|
||||
if (!configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_MASTER_PATCHES_KEY, &masterPatchesPath)) {
|
||||
debugPrint("\nAUTOMAP: Error reading config info!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// NOTE: Not sure about the size.
|
||||
char automapDbPath[512];
|
||||
sprintf(automapDbPath, "%s\\%s\\%s", masterPatchesPath, "MAPS", AUTOMAP_DB);
|
||||
snprintf(automapDbPath, sizeof(automapDbPath), "%s\\%s\\%s", settings.system.master_patches_path.c_str(), "MAPS", AUTOMAP_DB);
|
||||
if (compat_remove(automapDbPath) != 0) {
|
||||
debugPrint("\nAUTOMAP: Error removing database!\n");
|
||||
return -1;
|
||||
@@ -844,7 +844,7 @@ int automapSaveCurrent()
|
||||
|
||||
// NOTE: Not sure about the size.
|
||||
char automapTmpPath[512];
|
||||
sprintf(automapTmpPath, "%s\\%s\\%s", masterPatchesPath, "MAPS", AUTOMAP_TMP);
|
||||
snprintf(automapTmpPath, sizeof(automapTmpPath), "%s\\%s\\%s", settings.system.master_patches_path.c_str(), "MAPS", AUTOMAP_TMP);
|
||||
if (compat_rename(automapTmpPath, automapDbPath) != 0) {
|
||||
debugPrint("\nAUTOMAP: Error renaming database!\n");
|
||||
return -1;
|
||||
@@ -931,7 +931,7 @@ static int automapLoadEntry(int map, int elevation)
|
||||
gAutomapEntry.compressedData = NULL;
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "%s\\%s", "MAPS", AUTOMAP_DB);
|
||||
snprintf(path, sizeof(path), "%s\\%s", "MAPS", AUTOMAP_DB);
|
||||
|
||||
bool success = true;
|
||||
|
||||
@@ -1106,7 +1106,7 @@ static int automapCreate()
|
||||
memcpy(gAutomapHeader.offsets, _defam, sizeof(_defam));
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "%s\\%s", "MAPS", AUTOMAP_DB);
|
||||
snprintf(path, sizeof(path), "%s\\%s", "MAPS", AUTOMAP_DB);
|
||||
|
||||
File* stream = fileOpen(path, "wb");
|
||||
if (stream == NULL) {
|
||||
@@ -1161,7 +1161,7 @@ static int _copy_file_data(File* stream1, File* stream2, int length)
|
||||
int automapGetHeader(AutomapHeader** automapHeaderPtr)
|
||||
{
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "%s\\%s", "MAPS", AUTOMAP_DB);
|
||||
snprintf(path, sizeof(path), "%s\\%s", "MAPS", AUTOMAP_DB);
|
||||
|
||||
File* stream = fileOpen(path, "rb");
|
||||
if (stream == NULL) {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#include "autorun.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
|
||||
+2
-2
@@ -194,13 +194,13 @@ bool cacheFlush(Cache* cache)
|
||||
}
|
||||
|
||||
// 0x42019C
|
||||
bool cachePrintStats(Cache* cache, char* dest)
|
||||
bool cachePrintStats(Cache* cache, char* dest, size_t size)
|
||||
{
|
||||
if (cache == NULL || dest == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sprintf(dest, "Cache stats are disabled.%s", "\n");
|
||||
snprintf(dest, size, "Cache stats are disabled.%s", "\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,6 +1,8 @@
|
||||
#ifndef CACHE_H
|
||||
#define CACHE_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "heap.h"
|
||||
|
||||
namespace fallout {
|
||||
@@ -66,7 +68,7 @@ bool cacheFree(Cache* cache);
|
||||
bool cacheLock(Cache* cache, int key, void** data, CacheEntry** cacheEntryPtr);
|
||||
bool cacheUnlock(Cache* cache, CacheEntry* cacheEntry);
|
||||
bool cacheFlush(Cache* cache);
|
||||
bool cachePrintStats(Cache* cache, char* dest);
|
||||
bool cachePrintStats(Cache* cache, char* dest, size_t size);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
||||
+209
-144
File diff suppressed because it is too large
Load Diff
+38
-34
@@ -9,24 +9,27 @@
|
||||
#include "art.h"
|
||||
#include "character_editor.h"
|
||||
#include "color.h"
|
||||
#include "core.h"
|
||||
#include "critter.h"
|
||||
#include "db.h"
|
||||
#include "debug.h"
|
||||
#include "draw.h"
|
||||
#include "game.h"
|
||||
#include "game_config.h"
|
||||
#include "game_sound.h"
|
||||
#include "input.h"
|
||||
#include "kb.h"
|
||||
#include "memory.h"
|
||||
#include "message.h"
|
||||
#include "mouse.h"
|
||||
#include "object.h"
|
||||
#include "options.h"
|
||||
#include "palette.h"
|
||||
#include "platform_compat.h"
|
||||
#include "preferences.h"
|
||||
#include "proto.h"
|
||||
#include "settings.h"
|
||||
#include "sfall_config.h"
|
||||
#include "skill.h"
|
||||
#include "stat.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "trait.h"
|
||||
#include "window_manager.h"
|
||||
@@ -160,11 +163,13 @@ int characterSelectorOpen()
|
||||
int rc = 0;
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
if (_game_user_wants_to_quit != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
int keyCode = _get_input();
|
||||
int keyCode = inputGetInput();
|
||||
|
||||
switch (keyCode) {
|
||||
case KEY_MINUS:
|
||||
@@ -234,6 +239,9 @@ int characterSelectorOpen()
|
||||
characterSelectorWindowRefresh();
|
||||
break;
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
paletteFadeTo(gPaletteBlack);
|
||||
@@ -551,7 +559,7 @@ static void characterSelectorWindowFree()
|
||||
static bool characterSelectorWindowRefresh()
|
||||
{
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "%s.gcd", gCustomPremadeCharacterDescriptions[gCurrentPremadeCharacter].fileName);
|
||||
snprintf(path, sizeof(path), "%s.gcd", gCustomPremadeCharacterDescriptions[gCurrentPremadeCharacter].fileName);
|
||||
premadeCharactersLocalizePath(path);
|
||||
|
||||
if (_proto_dude_init(path) == -1) {
|
||||
@@ -629,13 +637,13 @@ static bool characterSelectorWindowRenderStats()
|
||||
value = critterGetStat(gDude, STAT_STRENGTH);
|
||||
str = statGetName(STAT_STRENGTH);
|
||||
|
||||
sprintf(text, "%s %02d", str, value);
|
||||
snprintf(text, sizeof(text), "%s %02d", str, value);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
|
||||
str = statGetValueDescription(value);
|
||||
sprintf(text, " %s", str);
|
||||
snprintf(text, sizeof(text), " %s", str);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
@@ -646,13 +654,13 @@ static bool characterSelectorWindowRenderStats()
|
||||
value = critterGetStat(gDude, STAT_PERCEPTION);
|
||||
str = statGetName(STAT_PERCEPTION);
|
||||
|
||||
sprintf(text, "%s %02d", str, value);
|
||||
snprintf(text, sizeof(text), "%s %02d", str, value);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
|
||||
str = statGetValueDescription(value);
|
||||
sprintf(text, " %s", str);
|
||||
snprintf(text, sizeof(text), " %s", str);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
@@ -661,15 +669,15 @@ static bool characterSelectorWindowRenderStats()
|
||||
y += vh;
|
||||
|
||||
value = critterGetStat(gDude, STAT_ENDURANCE);
|
||||
str = statGetName(STAT_PERCEPTION);
|
||||
str = statGetName(STAT_ENDURANCE);
|
||||
|
||||
sprintf(text, "%s %02d", str, value);
|
||||
snprintf(text, sizeof(text), "%s %02d", str, value);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
|
||||
str = statGetValueDescription(value);
|
||||
sprintf(text, " %s", str);
|
||||
snprintf(text, sizeof(text), " %s", str);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
@@ -680,13 +688,13 @@ static bool characterSelectorWindowRenderStats()
|
||||
value = critterGetStat(gDude, STAT_CHARISMA);
|
||||
str = statGetName(STAT_CHARISMA);
|
||||
|
||||
sprintf(text, "%s %02d", str, value);
|
||||
snprintf(text, sizeof(text), "%s %02d", str, value);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
|
||||
str = statGetValueDescription(value);
|
||||
sprintf(text, " %s", str);
|
||||
snprintf(text, sizeof(text), " %s", str);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
@@ -697,13 +705,13 @@ static bool characterSelectorWindowRenderStats()
|
||||
value = critterGetStat(gDude, STAT_INTELLIGENCE);
|
||||
str = statGetName(STAT_INTELLIGENCE);
|
||||
|
||||
sprintf(text, "%s %02d", str, value);
|
||||
snprintf(text, sizeof(text), "%s %02d", str, value);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
|
||||
str = statGetValueDescription(value);
|
||||
sprintf(text, " %s", str);
|
||||
snprintf(text, sizeof(text), " %s", str);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
@@ -714,13 +722,13 @@ static bool characterSelectorWindowRenderStats()
|
||||
value = critterGetStat(gDude, STAT_AGILITY);
|
||||
str = statGetName(STAT_AGILITY);
|
||||
|
||||
sprintf(text, "%s %02d", str, value);
|
||||
snprintf(text, sizeof(text), "%s %02d", str, value);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
|
||||
str = statGetValueDescription(value);
|
||||
sprintf(text, " %s", str);
|
||||
snprintf(text, sizeof(text), " %s", str);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
@@ -731,13 +739,13 @@ static bool characterSelectorWindowRenderStats()
|
||||
value = critterGetStat(gDude, STAT_LUCK);
|
||||
str = statGetName(STAT_LUCK);
|
||||
|
||||
sprintf(text, "%s %02d", str, value);
|
||||
snprintf(text, sizeof(text), "%s %02d", str, value);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
|
||||
str = statGetValueDescription(value);
|
||||
sprintf(text, " %s", str);
|
||||
snprintf(text, sizeof(text), " %s", str);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
@@ -757,7 +765,7 @@ static bool characterSelectorWindowRenderStats()
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
|
||||
value = critterGetStat(gDude, STAT_MAXIMUM_HIT_POINTS);
|
||||
sprintf(text, " %d/%d", critterGetHitPoints(gDude), value);
|
||||
snprintf(text, sizeof(text), " %d/%d", critterGetHitPoints(gDude), value);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
@@ -772,7 +780,7 @@ static bool characterSelectorWindowRenderStats()
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
|
||||
value = critterGetStat(gDude, STAT_ARMOR_CLASS);
|
||||
sprintf(text, " %d", value);
|
||||
snprintf(text, sizeof(text), " %d", value);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
@@ -790,7 +798,7 @@ static bool characterSelectorWindowRenderStats()
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
|
||||
value = critterGetStat(gDude, STAT_MAXIMUM_ACTION_POINTS);
|
||||
sprintf(text, " %d", value);
|
||||
snprintf(text, sizeof(text), " %d", value);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
@@ -798,14 +806,14 @@ static bool characterSelectorWindowRenderStats()
|
||||
// MELEE DAMAGE
|
||||
y += vh;
|
||||
|
||||
str = statGetName(STAT_ARMOR_CLASS);
|
||||
str = statGetName(STAT_MELEE_DAMAGE);
|
||||
strcpy(text, str);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
|
||||
value = critterGetStat(gDude, STAT_ARMOR_CLASS);
|
||||
sprintf(text, " %d", value);
|
||||
value = critterGetStat(gDude, STAT_MELEE_DAMAGE);
|
||||
snprintf(text, sizeof(text), " %d", value);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
@@ -826,7 +834,7 @@ static bool characterSelectorWindowRenderStats()
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
|
||||
value = skillGetValue(gDude, skills[index]);
|
||||
sprintf(text, " %d%%", value);
|
||||
snprintf(text, sizeof(text), " %d%%", value);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
|
||||
@@ -858,7 +866,7 @@ static bool characterSelectorWindowRenderBio()
|
||||
fontSetCurrent(101);
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "%s.bio", gCustomPremadeCharacterDescriptions[gCurrentPremadeCharacter].fileName);
|
||||
snprintf(path, sizeof(path), "%s.bio", gCustomPremadeCharacterDescriptions[gCurrentPremadeCharacter].fileName);
|
||||
premadeCharactersLocalizePath(path);
|
||||
|
||||
File* stream = fileOpen(path, "rt");
|
||||
@@ -930,7 +938,7 @@ void premadeCharactersInit()
|
||||
continue;
|
||||
}
|
||||
|
||||
sprintf(gCustomPremadeCharacterDescriptions[index].fileName, "premade\\%s", fileNamesString);
|
||||
snprintf(gCustomPremadeCharacterDescriptions[index].fileName, sizeof(gCustomPremadeCharacterDescriptions[index].fileName), "premade\\%s", fileNamesString);
|
||||
|
||||
if (pch != NULL) {
|
||||
*pch = ',';
|
||||
@@ -979,11 +987,7 @@ static void premadeCharactersLocalizePath(char* path)
|
||||
return;
|
||||
}
|
||||
|
||||
char* language;
|
||||
if (!configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_LANGUAGE_KEY, &language)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char* language = settings.system.language.c_str();
|
||||
if (compat_stricmp(language, ENGLISH) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
+64
-63
@@ -5,7 +5,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "core.h"
|
||||
#include "svga.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
@@ -86,13 +86,13 @@ unsigned char* _blendTable[256];
|
||||
unsigned char _mappedColor[256];
|
||||
|
||||
// 0x6738D0
|
||||
unsigned char _colorMixAddTable[65536];
|
||||
Color colorMixAddTable[256][256];
|
||||
|
||||
// 0x6838D0
|
||||
unsigned char _intensityColorTable[65536];
|
||||
Color intensityColorTable[256][256];
|
||||
|
||||
// 0x6938D0
|
||||
unsigned char _colorMixMulTable[65536];
|
||||
Color colorMixMulTable[256][256];
|
||||
|
||||
// 0x6A38D0
|
||||
unsigned char _colorTable[32768];
|
||||
@@ -172,22 +172,19 @@ static void colorPaletteFreeDefaultImpl(void* ptr)
|
||||
}
|
||||
|
||||
// 0x4C72B4
|
||||
int _calculateColor(int a1, int a2)
|
||||
int _calculateColor(int intensity, Color color)
|
||||
{
|
||||
int v1 = (a1 >> 9) + ((a2 & 0xFF) << 8);
|
||||
return _intensityColorTable[v1];
|
||||
return intensityColorTable[color][intensity / 512];
|
||||
}
|
||||
|
||||
// 0x4C72E0
|
||||
int _Color2RGB_(int a1)
|
||||
int Color2RGB(Color c)
|
||||
{
|
||||
int v1, v2, v3;
|
||||
int r = _cmap[3 * c] >> 1;
|
||||
int g = _cmap[3 * c + 1] >> 1;
|
||||
int b = _cmap[3 * c + 2] >> 1;
|
||||
|
||||
v1 = _cmap[3 * a1] >> 1;
|
||||
v2 = _cmap[3 * a1 + 1] >> 1;
|
||||
v3 = _cmap[3 * a1 + 2] >> 1;
|
||||
|
||||
return (((v1 << 5) | v2) << 5) | v3;
|
||||
return (r << 10) | (g << 5) | b;
|
||||
}
|
||||
|
||||
// Performs animated palette transition.
|
||||
@@ -196,6 +193,8 @@ int _Color2RGB_(int a1)
|
||||
void colorPaletteFadeBetween(unsigned char* oldPalette, unsigned char* newPalette, int steps)
|
||||
{
|
||||
for (int step = 0; step < steps; step++) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
unsigned char palette[768];
|
||||
|
||||
for (int index = 0; index < 768; index++) {
|
||||
@@ -209,9 +208,14 @@ void colorPaletteFadeBetween(unsigned char* oldPalette, unsigned char* newPalett
|
||||
}
|
||||
|
||||
_setSystemPalette(palette);
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
sharedFpsLimiter.mark();
|
||||
_setSystemPalette(newPalette);
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
// 0x4C73D4
|
||||
@@ -259,29 +263,28 @@ void _setSystemPaletteEntries(unsigned char* palette, int start, int end)
|
||||
}
|
||||
|
||||
// 0x4C7550
|
||||
static void _setIntensityTableColor(int a1)
|
||||
static void _setIntensityTableColor(int cc)
|
||||
{
|
||||
int v1, v2, v3, v4, v5, v6, v7, v8, v9, v10;
|
||||
|
||||
v5 = 0;
|
||||
v10 = a1 << 8;
|
||||
int shift = 0;
|
||||
|
||||
for (int index = 0; index < 128; index++) {
|
||||
v1 = (_Color2RGB_(a1) & 0x7C00) >> 10;
|
||||
v2 = (_Color2RGB_(a1) & 0x3E0) >> 5;
|
||||
v3 = (_Color2RGB_(a1) & 0x1F);
|
||||
int r = (Color2RGB(cc) & 0x7C00) >> 10;
|
||||
int g = (Color2RGB(cc) & 0x3E0) >> 5;
|
||||
int b = (Color2RGB(cc) & 0x1F);
|
||||
|
||||
v4 = (((v1 * v5) >> 16) << 10) | (((v2 * v5) >> 16) << 5) | ((v3 * v5) >> 16);
|
||||
_intensityColorTable[index + v10] = _colorTable[v4];
|
||||
int darkerR = ((r * shift) >> 16);
|
||||
int darkerG = ((g * shift) >> 16);
|
||||
int darkerB = ((b * shift) >> 16);
|
||||
int darkerColor = (darkerR << 10) | (darkerG << 5) | darkerB;
|
||||
intensityColorTable[cc][index] = _colorTable[darkerColor];
|
||||
|
||||
v6 = v1 + (((0x1F - v1) * v5) >> 16);
|
||||
v7 = v2 + (((0x1F - v2) * v5) >> 16);
|
||||
v8 = v3 + (((0x1F - v3) * v5) >> 16);
|
||||
int lighterR = r + (((0x1F - r) * shift) >> 16);
|
||||
int lighterG = g + (((0x1F - g) * shift) >> 16);
|
||||
int lighterB = b + (((0x1F - b) * shift) >> 16);
|
||||
int lighterColor = (lighterR << 10) | (lighterG << 5) | lighterB;
|
||||
intensityColorTable[cc][128 + index] = _colorTable[lighterColor];
|
||||
|
||||
v9 = (v6 << 10) | (v7 << 5) | v8;
|
||||
_intensityColorTable[0x7F + index + 1 + v10] = _colorTable[v9];
|
||||
|
||||
v5 += 0x200;
|
||||
shift += 512;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,7 +295,7 @@ static void _setIntensityTables()
|
||||
if (_mappedColor[index] != 0) {
|
||||
_setIntensityTableColor(index);
|
||||
} else {
|
||||
memset(_intensityColorTable + index * 256, 0, 256);
|
||||
memset(intensityColorTable[index], 0, 256);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,20 +304,18 @@ static void _setIntensityTables()
|
||||
static void _setMixTableColor(int a1)
|
||||
{
|
||||
int i;
|
||||
int v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19;
|
||||
int v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19;
|
||||
int v20, v21, v22, v23, v24, v25, v26, v27, v28, v29;
|
||||
|
||||
v1 = a1 << 8;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
if (_mappedColor[a1] && _mappedColor[i]) {
|
||||
v2 = (_Color2RGB_(a1) & 0x7C00) >> 10;
|
||||
v3 = (_Color2RGB_(a1) & 0x3E0) >> 5;
|
||||
v4 = (_Color2RGB_(a1) & 0x1F);
|
||||
v2 = (Color2RGB(a1) & 0x7C00) >> 10;
|
||||
v3 = (Color2RGB(a1) & 0x3E0) >> 5;
|
||||
v4 = (Color2RGB(a1) & 0x1F);
|
||||
|
||||
v5 = (_Color2RGB_(i) & 0x7C00) >> 10;
|
||||
v6 = (_Color2RGB_(i) & 0x3E0) >> 5;
|
||||
v7 = (_Color2RGB_(i) & 0x1F);
|
||||
v5 = (Color2RGB(i) & 0x7C00) >> 10;
|
||||
v6 = (Color2RGB(i) & 0x3E0) >> 5;
|
||||
v7 = (Color2RGB(i) & 0x1F);
|
||||
|
||||
v8 = v2 + v5;
|
||||
v9 = v3 + v6;
|
||||
@@ -359,29 +360,29 @@ static void _setMixTableColor(int a1)
|
||||
v12 = _calculateColor(v19, v18);
|
||||
}
|
||||
|
||||
_colorMixAddTable[v1 + i] = v12;
|
||||
colorMixAddTable[a1][i] = v12;
|
||||
|
||||
v20 = (_Color2RGB_(a1) & 0x7C00) >> 10;
|
||||
v21 = (_Color2RGB_(a1) & 0x3E0) >> 5;
|
||||
v22 = (_Color2RGB_(a1) & 0x1F);
|
||||
v20 = (Color2RGB(a1) & 0x7C00) >> 10;
|
||||
v21 = (Color2RGB(a1) & 0x3E0) >> 5;
|
||||
v22 = (Color2RGB(a1) & 0x1F);
|
||||
|
||||
v23 = (_Color2RGB_(i) & 0x7C00) >> 10;
|
||||
v24 = (_Color2RGB_(i) & 0x3E0) >> 5;
|
||||
v25 = (_Color2RGB_(i) & 0x1F);
|
||||
v23 = (Color2RGB(i) & 0x7C00) >> 10;
|
||||
v24 = (Color2RGB(i) & 0x3E0) >> 5;
|
||||
v25 = (Color2RGB(i) & 0x1F);
|
||||
|
||||
v26 = (v20 * v23) >> 5;
|
||||
v27 = (v21 * v24) >> 5;
|
||||
v28 = (v22 * v25) >> 5;
|
||||
|
||||
v29 = (v26 << 10) | (v27 << 5) | v28;
|
||||
_colorMixMulTable[v1 + i] = _colorTable[v29];
|
||||
colorMixMulTable[a1][i] = _colorTable[v29];
|
||||
} else {
|
||||
if (_mappedColor[i]) {
|
||||
_colorMixAddTable[v1 + i] = i;
|
||||
_colorMixMulTable[v1 + i] = i;
|
||||
colorMixAddTable[a1][i] = i;
|
||||
colorMixMulTable[a1][i] = i;
|
||||
} else {
|
||||
_colorMixAddTable[v1 + i] = a1;
|
||||
_colorMixMulTable[v1 + i] = a1;
|
||||
colorMixAddTable[a1][i] = a1;
|
||||
colorMixMulTable[a1][i] = a1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -438,15 +439,15 @@ bool colorPaletteLoad(const char* path)
|
||||
|
||||
// NOTE: The value is "NEWC". Original code uses cmp opcode, not stricmp,
|
||||
// or comparing characters one-by-one.
|
||||
if (type == 0x4E455743) {
|
||||
if (type == 'NEWC') {
|
||||
// NOTE: Uninline.
|
||||
colorPaletteFileRead(fd, _intensityColorTable, 0x10000);
|
||||
colorPaletteFileRead(fd, intensityColorTable, sizeof(intensityColorTable));
|
||||
|
||||
// NOTE: Uninline.
|
||||
colorPaletteFileRead(fd, _colorMixAddTable, 0x10000);
|
||||
colorPaletteFileRead(fd, colorMixAddTable, sizeof(colorMixAddTable));
|
||||
|
||||
// NOTE: Uninline.
|
||||
colorPaletteFileRead(fd, _colorMixMulTable, 0x10000);
|
||||
colorPaletteFileRead(fd, colorMixMulTable, sizeof(colorMixMulTable));
|
||||
} else {
|
||||
_setIntensityTables();
|
||||
|
||||
@@ -479,9 +480,9 @@ static void _buildBlendTable(unsigned char* ptr, unsigned char ch)
|
||||
|
||||
beg = ptr;
|
||||
|
||||
r = (_Color2RGB_(ch) & 0x7C00) >> 10;
|
||||
g = (_Color2RGB_(ch) & 0x3E0) >> 5;
|
||||
b = (_Color2RGB_(ch) & 0x1F);
|
||||
r = (Color2RGB(ch) & 0x7C00) >> 10;
|
||||
g = (Color2RGB(ch) & 0x3E0) >> 5;
|
||||
b = (Color2RGB(ch) & 0x1F);
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
ptr[i] = i;
|
||||
@@ -500,9 +501,9 @@ static void _buildBlendTable(unsigned char* ptr, unsigned char ch)
|
||||
|
||||
for (j = 0; j < 7; j++) {
|
||||
for (i = 0; i < 256; i++) {
|
||||
v12 = (_Color2RGB_(i) & 0x7C00) >> 10;
|
||||
v14 = (_Color2RGB_(i) & 0x3E0) >> 5;
|
||||
v16 = (_Color2RGB_(i) & 0x1F);
|
||||
v12 = (Color2RGB(i) & 0x7C00) >> 10;
|
||||
v14 = (Color2RGB(i) & 0x3E0) >> 5;
|
||||
v16 = (Color2RGB(i) & 0x1F);
|
||||
int index = 0;
|
||||
index |= (r_2 + v12 * v31) / 7 << 10;
|
||||
index |= (g_2 + v14 * v31) / 7 << 5;
|
||||
|
||||
+6
-5
@@ -5,6 +5,7 @@
|
||||
|
||||
namespace fallout {
|
||||
|
||||
typedef unsigned char Color;
|
||||
typedef const char*(ColorFileNameManger)(const char*);
|
||||
typedef void(ColorTransitionCallback)();
|
||||
|
||||
@@ -18,14 +19,14 @@ extern unsigned char _systemCmap[256 * 3];
|
||||
extern unsigned char _currentGammaTable[64];
|
||||
extern unsigned char* _blendTable[256];
|
||||
extern unsigned char _mappedColor[256];
|
||||
extern unsigned char _colorMixAddTable[65536];
|
||||
extern unsigned char _intensityColorTable[65536];
|
||||
extern unsigned char _colorMixMulTable[65536];
|
||||
extern Color colorMixAddTable[256][256];
|
||||
extern Color intensityColorTable[256][256];
|
||||
extern Color colorMixMulTable[256][256];
|
||||
extern unsigned char _colorTable[32768];
|
||||
|
||||
void colorPaletteSetFileIO(ColorPaletteFileOpenProc* openProc, ColorPaletteFileReadProc* readProc, ColorPaletteCloseProc* closeProc);
|
||||
int _calculateColor(int a1, int a2);
|
||||
int _Color2RGB_(int a1);
|
||||
int _calculateColor(int intensity, Color color);
|
||||
int Color2RGB(Color c);
|
||||
void colorPaletteFadeBetween(unsigned char* oldPalette, unsigned char* newPalette, int steps);
|
||||
void colorPaletteSetTransitionCallback(ColorTransitionCallback* callback);
|
||||
void _setSystemPalette(unsigned char* palette);
|
||||
|
||||
+169
-127
@@ -9,7 +9,6 @@
|
||||
#include "art.h"
|
||||
#include "color.h"
|
||||
#include "combat_ai.h"
|
||||
#include "core.h"
|
||||
#include "critter.h"
|
||||
#include "db.h"
|
||||
#include "debug.h"
|
||||
@@ -17,11 +16,12 @@
|
||||
#include "draw.h"
|
||||
#include "elevator.h"
|
||||
#include "game.h"
|
||||
#include "game_config.h"
|
||||
#include "game_mouse.h"
|
||||
#include "game_sound.h"
|
||||
#include "input.h"
|
||||
#include "interface.h"
|
||||
#include "item.h"
|
||||
#include "kb.h"
|
||||
#include "loadsave.h"
|
||||
#include "map.h"
|
||||
#include "memory.h"
|
||||
@@ -35,9 +35,12 @@
|
||||
#include "queue.h"
|
||||
#include "random.h"
|
||||
#include "scripts.h"
|
||||
#include "settings.h"
|
||||
#include "sfall_config.h"
|
||||
#include "sfall_global_scripts.h"
|
||||
#include "skill.h"
|
||||
#include "stat.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "tile.h"
|
||||
#include "trait.h"
|
||||
@@ -118,7 +121,7 @@ static void attackComputeDamage(Attack* attack, int ammoQuantity, int a3);
|
||||
static void _check_for_death(Object* a1, int a2, int* a3);
|
||||
static void _set_new_results(Object* a1, int a2);
|
||||
static void _damage_object(Object* a1, int damage, bool animated, int a4, Object* a5);
|
||||
static void combatCopyDamageAmountDescription(char* dest, Object* critter_obj, int damage);
|
||||
static void combatCopyDamageAmountDescription(char* dest, size_t size, Object* critter_obj, int damage);
|
||||
static void combatAddDamageFlagsDescription(char* a1, int flags, Object* a3);
|
||||
static void _combat_standup(Object* a1);
|
||||
static void _print_tohit(unsigned char* dest, int dest_pitch, int a3);
|
||||
@@ -166,7 +169,7 @@ static bool _combat_call_display = false;
|
||||
// Accuracy modifiers for hit locations.
|
||||
//
|
||||
// 0x510954
|
||||
static const int _hit_location_penalty[HIT_LOCATION_COUNT] = {
|
||||
static int hit_location_penalty_default[HIT_LOCATION_COUNT] = {
|
||||
-40,
|
||||
-30,
|
||||
-30,
|
||||
@@ -178,6 +181,8 @@ static const int _hit_location_penalty[HIT_LOCATION_COUNT] = {
|
||||
0,
|
||||
};
|
||||
|
||||
static int hit_location_penalty[HIT_LOCATION_COUNT];
|
||||
|
||||
// Critical hit tables for every kill type.
|
||||
//
|
||||
// 0x510978
|
||||
@@ -2014,17 +2019,20 @@ int combatInit()
|
||||
return -1;
|
||||
}
|
||||
|
||||
sprintf(path, "%s%s", asc_5186C8, "combat.msg");
|
||||
snprintf(path, sizeof(path), "%s%s", asc_5186C8, "combat.msg");
|
||||
|
||||
if (!messageListLoad(&gCombatMessageList, path)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_COMBAT, &gCombatMessageList);
|
||||
|
||||
// SFALL
|
||||
criticalsInit();
|
||||
burstModInit();
|
||||
unarmedInit();
|
||||
damageModInit();
|
||||
combat_reset_hit_location_penalty();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2054,11 +2062,13 @@ void combatReset()
|
||||
|
||||
// SFALL
|
||||
criticalsReset();
|
||||
combat_reset_hit_location_penalty();
|
||||
}
|
||||
|
||||
// 0x420E14
|
||||
void combatExit()
|
||||
{
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_COMBAT, nullptr);
|
||||
messageListFree(&gCombatMessageList);
|
||||
|
||||
// SFALL
|
||||
@@ -2082,17 +2092,10 @@ int _find_cid(int a1, int cid, Object** critterList, int critterListLength)
|
||||
// 0x420E4C
|
||||
int combatLoad(File* stream)
|
||||
{
|
||||
int v14;
|
||||
int a2;
|
||||
Object* obj;
|
||||
int v24;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
if (fileReadUInt32(stream, &gCombatState) == -1) return -1;
|
||||
|
||||
if (!isInCombat()) {
|
||||
obj = objectFindFirst();
|
||||
Object* obj = objectFindFirst();
|
||||
while (obj != NULL) {
|
||||
if (PID_TYPE(obj->pid) == OBJ_TYPE_CRITTER) {
|
||||
if (obj->data.critter.combat.whoHitMeCid == -1) {
|
||||
@@ -2111,56 +2114,47 @@ int combatLoad(File* stream)
|
||||
if (fileReadInt32(stream, &_list_noncom) == -1) return -1;
|
||||
if (fileReadInt32(stream, &_list_total) == -1) return -1;
|
||||
|
||||
if (objectListCreate(-1, gElevation, 1, &_combat_list) != _list_total) {
|
||||
if (objectListCreate(-1, gElevation, OBJ_TYPE_CRITTER, &_combat_list) != _list_total) {
|
||||
objectListFree(_combat_list);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fileReadInt32(stream, &v24) == -1) return -1;
|
||||
if (fileReadInt32(stream, &(gDude->cid)) == -1) return -1;
|
||||
|
||||
gDude->cid = v24;
|
||||
|
||||
for (i = 0; i < _list_total; i++) {
|
||||
if (_combat_list[i]->data.critter.combat.whoHitMeCid == -1) {
|
||||
_combat_list[i]->data.critter.combat.whoHitMe = NULL;
|
||||
for (int index = 0; index < _list_total; index++) {
|
||||
if (_combat_list[index]->data.critter.combat.whoHitMeCid == -1) {
|
||||
_combat_list[index]->data.critter.combat.whoHitMe = NULL;
|
||||
} else {
|
||||
for (j = 0; j < _list_total; j++) {
|
||||
if (_combat_list[i]->data.critter.combat.whoHitMeCid == _combat_list[j]->cid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j == _list_total) {
|
||||
_combat_list[i]->data.critter.combat.whoHitMe = NULL;
|
||||
// NOTE: Uninline.
|
||||
int found = _find_cid(0, _combat_list[index]->data.critter.combat.whoHitMeCid, _combat_list, _list_total);
|
||||
if (found == _list_total) {
|
||||
_combat_list[index]->data.critter.combat.whoHitMe = NULL;
|
||||
} else {
|
||||
_combat_list[i]->data.critter.combat.whoHitMe = _combat_list[j];
|
||||
_combat_list[index]->data.critter.combat.whoHitMe = _combat_list[found];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < _list_total; i++) {
|
||||
if (fileReadInt32(stream, &v24) == -1) return -1;
|
||||
for (int index = 0; index < _list_total; index++) {
|
||||
int cid;
|
||||
if (fileReadInt32(stream, &cid) == -1) return -1;
|
||||
|
||||
for (j = i; j < _list_total; j++) {
|
||||
if (v24 == _combat_list[j]->cid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j == _list_total) {
|
||||
// NOTE: Uninline.
|
||||
int found = _find_cid(index, cid, _combat_list, _list_total);
|
||||
if (found == _list_total) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
obj = _combat_list[i];
|
||||
_combat_list[i] = _combat_list[j];
|
||||
_combat_list[j] = obj;
|
||||
Object* obj = _combat_list[index];
|
||||
_combat_list[index] = _combat_list[found];
|
||||
_combat_list[found] = obj;
|
||||
}
|
||||
|
||||
for (i = 0; i < _list_total; i++) {
|
||||
_combat_list[i]->cid = i;
|
||||
for (int index = 0; index < _list_total; index++) {
|
||||
_combat_list[index]->cid = index;
|
||||
}
|
||||
|
||||
if (_aiInfoList) {
|
||||
if (_aiInfoList != NULL) {
|
||||
internal_free(_aiInfoList);
|
||||
}
|
||||
|
||||
@@ -2169,39 +2163,42 @@ int combatLoad(File* stream)
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (v14 = 0; v14 < _list_total; v14++) {
|
||||
CombatAiInfo* aiInfo = &(_aiInfoList[v14]);
|
||||
for (int index = 0; index < _list_total; index++) {
|
||||
CombatAiInfo* aiInfo = &(_aiInfoList[index]);
|
||||
|
||||
if (fileReadInt32(stream, &a2) == -1) return -1;
|
||||
int friendlyId;
|
||||
if (fileReadInt32(stream, &friendlyId) == -1) return -1;
|
||||
|
||||
if (a2 == -1) {
|
||||
if (friendlyId == -1) {
|
||||
aiInfo->friendlyDead = NULL;
|
||||
} else {
|
||||
// SFALL: Fix incorrect object type search when loading a game in
|
||||
// combat mode.
|
||||
aiInfo->friendlyDead = objectTypedFindById(a2, OBJ_TYPE_CRITTER);
|
||||
aiInfo->friendlyDead = objectTypedFindById(friendlyId, OBJ_TYPE_CRITTER);
|
||||
if (aiInfo->friendlyDead == NULL) return -1;
|
||||
}
|
||||
|
||||
if (fileReadInt32(stream, &a2) == -1) return -1;
|
||||
int targetId;
|
||||
if (fileReadInt32(stream, &targetId) == -1) return -1;
|
||||
|
||||
if (a2 == -1) {
|
||||
if (targetId == -1) {
|
||||
aiInfo->lastTarget = NULL;
|
||||
} else {
|
||||
// SFALL: Fix incorrect object type search when loading a game in
|
||||
// combat mode.
|
||||
aiInfo->lastTarget = objectTypedFindById(a2, OBJ_TYPE_CRITTER);
|
||||
aiInfo->lastTarget = objectTypedFindById(targetId, OBJ_TYPE_CRITTER);
|
||||
if (aiInfo->lastTarget == NULL) return -1;
|
||||
}
|
||||
|
||||
if (fileReadInt32(stream, &a2) == -1) return -1;
|
||||
int itemId;
|
||||
if (fileReadInt32(stream, &itemId) == -1) return -1;
|
||||
|
||||
if (a2 == -1) {
|
||||
if (itemId == -1) {
|
||||
aiInfo->lastItem = NULL;
|
||||
} else {
|
||||
// SFALL: Fix incorrect object type search when loading a game in
|
||||
// combat mode.
|
||||
aiInfo->lastItem = objectTypedFindById(a2, OBJ_TYPE_ITEM);
|
||||
aiInfo->lastItem = objectTypedFindById(itemId, OBJ_TYPE_ITEM);
|
||||
if (aiInfo->lastItem == NULL) return -1;
|
||||
}
|
||||
|
||||
@@ -2589,7 +2586,7 @@ static void _combat_begin(Object* a1)
|
||||
for (int index = 0; index < _list_total; index++) {
|
||||
Object* critter = _combat_list[index];
|
||||
CritterCombatData* combatData = &(critter->data.critter.combat);
|
||||
combatData->maneuver &= CRITTER_MANEUVER_0x01;
|
||||
combatData->maneuver &= CRITTER_MANEUVER_ENGAGING;
|
||||
combatData->damageLastTurn = 0;
|
||||
combatData->whoHitMe = NULL;
|
||||
combatData->ap = 0;
|
||||
@@ -2651,8 +2648,7 @@ static void _combat_begin_extra(Object* a1)
|
||||
|
||||
_combat_ai_begin(_list_total, _combat_list);
|
||||
|
||||
_combat_highlight = 2;
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_TARGET_HIGHLIGHT_KEY, &_combat_highlight);
|
||||
_combat_highlight = settings.preferences.target_highlight;
|
||||
}
|
||||
|
||||
// NOTE: Inlined.
|
||||
@@ -2812,7 +2808,7 @@ static void _combat_over()
|
||||
interfaceGetItemActions(&leftItemAction, &rightItemAction);
|
||||
interfaceUpdateItems(true, leftItemAction, rightItemAction);
|
||||
|
||||
gDude->data.critter.combat.ap = critterGetStat(gDude, STAT_MAXIMUM_HIT_POINTS);
|
||||
gDude->data.critter.combat.ap = critterGetStat(gDude, STAT_MAXIMUM_ACTION_POINTS);
|
||||
|
||||
interfaceRenderActionPoints(0, 0);
|
||||
|
||||
@@ -2895,7 +2891,7 @@ void _combat_give_exps(int exp_points)
|
||||
return;
|
||||
}
|
||||
|
||||
sprintf(text, v7.text, v9.text, xpGained);
|
||||
snprintf(text, sizeof(text), v7.text, v9.text, xpGained);
|
||||
displayMonitorAddMessage(text);
|
||||
}
|
||||
|
||||
@@ -3041,8 +3037,8 @@ static void _combat_sequence()
|
||||
Object* critter = _combat_list[index];
|
||||
if (critter != gDude) {
|
||||
if ((critter->data.critter.combat.results & DAM_KNOCKED_OUT) != 0
|
||||
|| critter->data.critter.combat.maneuver == CRITTER_MANEUVER_STOP_ATTACKING) {
|
||||
critter->data.critter.combat.maneuver &= ~CRITTER_MANEUVER_0x01;
|
||||
|| critter->data.critter.combat.maneuver == CRITTER_MANEUVER_DISENGAGING) {
|
||||
critter->data.critter.combat.maneuver &= ~CRITTER_MANEUVER_ENGAGING;
|
||||
_list_noncom += 1;
|
||||
|
||||
_combat_list[index] = _combat_list[count - 1];
|
||||
@@ -3115,14 +3111,23 @@ static void combatAttemptEnd()
|
||||
void _combat_turn_run()
|
||||
{
|
||||
while (_combat_turn_running > 0) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
_process_bk();
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4227F4
|
||||
static int _combat_input()
|
||||
{
|
||||
ScopedGameMode gm(GameMode::kPlayerTurn);
|
||||
|
||||
while ((gCombatState & COMBAT_STATE_0x02) != 0) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
if ((gCombatState & COMBAT_STATE_0x08) != 0) {
|
||||
break;
|
||||
}
|
||||
@@ -3139,11 +3144,14 @@ static int _combat_input()
|
||||
break;
|
||||
}
|
||||
|
||||
int keyCode = _get_input();
|
||||
int keyCode = inputGetInput();
|
||||
|
||||
// SFALL: CombatLoopHook.
|
||||
sfall_gl_scr_process_main();
|
||||
|
||||
if (_action_explode_running()) {
|
||||
while (_combat_turn_running > 0) {
|
||||
_process_bk();
|
||||
}
|
||||
// NOTE: Uninline.
|
||||
_combat_turn_run();
|
||||
}
|
||||
|
||||
if (gDude->data.critter.combat.ap <= 0 && _combat_free_move <= 0) {
|
||||
@@ -3160,6 +3168,9 @@ static int _combat_input()
|
||||
_scripts_check_state_in_combat();
|
||||
gameHandleKey(keyCode, true);
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
int v4 = _game_user_wants_to_quit;
|
||||
@@ -3282,9 +3293,8 @@ static int _combat_turn(Object* a1, bool a2)
|
||||
}
|
||||
}
|
||||
|
||||
while (_combat_turn_running > 0) {
|
||||
_process_bk();
|
||||
}
|
||||
// NOTE: Uninline.
|
||||
_combat_turn_run();
|
||||
|
||||
if (a1 == gDude) {
|
||||
gameUiDisable(1);
|
||||
@@ -3357,6 +3367,8 @@ static bool _combat_should_end()
|
||||
// 0x422D2C
|
||||
void _combat(STRUCT_664980* attack)
|
||||
{
|
||||
ScopedGameMode gm(GameMode::kCombat);
|
||||
|
||||
if (attack == NULL
|
||||
|| (attack->attacker == NULL || attack->attacker->elevation == gElevation)
|
||||
|| (attack->defender == NULL || attack->defender->elevation == gElevation)) {
|
||||
@@ -3828,7 +3840,7 @@ static int attackCompute(Attack* attack)
|
||||
roll = _compute_spray(attack, accuracy, &ammoQuantity, &v26, anim);
|
||||
} else {
|
||||
int chance = critterGetStat(attack->attacker, STAT_CRITICAL_CHANCE);
|
||||
roll = randomRoll(accuracy, chance - _hit_location_penalty[attack->defenderHitLocation], NULL);
|
||||
roll = randomRoll(accuracy, chance - hit_location_penalty[attack->defenderHitLocation], NULL);
|
||||
}
|
||||
|
||||
if (roll == ROLL_FAILURE) {
|
||||
@@ -4067,7 +4079,7 @@ void _compute_explosion_on_extras(Attack* attack, int a2, bool isGrenade, int a4
|
||||
static int attackComputeCriticalHit(Attack* attack)
|
||||
{
|
||||
Object* defender = attack->defender;
|
||||
if (defender != NULL && _critter_flag_check(defender->pid, CRITTER_FLAG_0x400)) {
|
||||
if (defender != NULL && _critter_flag_check(defender->pid, CRITTER_INVULNERABLE)) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -4141,7 +4153,7 @@ static int _attackFindInvalidFlags(Object* critter, Object* item)
|
||||
{
|
||||
int flags = 0;
|
||||
|
||||
if (critter != NULL && PID_TYPE(critter->pid) == OBJ_TYPE_CRITTER && _critter_flag_check(critter->pid, CRITTER_FLAG_0x40)) {
|
||||
if (critter != NULL && PID_TYPE(critter->pid) == OBJ_TYPE_CRITTER && _critter_flag_check(critter->pid, CRITTER_NO_DROP)) {
|
||||
flags |= DAM_DROP;
|
||||
}
|
||||
|
||||
@@ -4155,9 +4167,9 @@ static int _attackFindInvalidFlags(Object* critter, Object* item)
|
||||
// 0x4240DC
|
||||
static int attackComputeCriticalFailure(Attack* attack)
|
||||
{
|
||||
attack->attackerFlags |= DAM_HIT;
|
||||
attack->attackerFlags &= ~DAM_HIT;
|
||||
|
||||
if (attack->attacker != NULL && _critter_flag_check(attack->attacker->pid, CRITTER_FLAG_0x400)) {
|
||||
if (attack->attacker != NULL && _critter_flag_check(attack->attacker->pid, CRITTER_INVULNERABLE)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4414,9 +4426,9 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in
|
||||
}
|
||||
|
||||
if (isRangedWeapon) {
|
||||
accuracy += _hit_location_penalty[hitLocation];
|
||||
accuracy += hit_location_penalty[hitLocation];
|
||||
} else {
|
||||
accuracy += _hit_location_penalty[hitLocation] / 2;
|
||||
accuracy += hit_location_penalty[hitLocation] / 2;
|
||||
}
|
||||
|
||||
if (defender != NULL && (defender->flags & OBJECT_MULTIHEX) != 0) {
|
||||
@@ -4455,9 +4467,7 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in
|
||||
}
|
||||
|
||||
if (attacker->data.critter.combat.team != gDude->data.critter.combat.team) {
|
||||
int combatDifficuly = 1;
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_DIFFICULTY_KEY, &combatDifficuly);
|
||||
switch (combatDifficuly) {
|
||||
switch (settings.preferences.combat_difficulty) {
|
||||
case 0:
|
||||
accuracy -= 20;
|
||||
break;
|
||||
@@ -4532,10 +4542,7 @@ static void attackComputeDamage(Attack* attack, int ammoQuantity, int bonusDamag
|
||||
|
||||
int combatDifficultyDamageModifier = 100;
|
||||
if (attack->attacker->data.critter.combat.team != gDude->data.critter.combat.team) {
|
||||
int combatDifficulty = COMBAT_DIFFICULTY_NORMAL;
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_DIFFICULTY_KEY, &combatDifficulty);
|
||||
|
||||
switch (combatDifficulty) {
|
||||
switch (settings.preferences.combat_difficulty) {
|
||||
case COMBAT_DIFFICULTY_EASY:
|
||||
combatDifficultyDamageModifier = 75;
|
||||
break;
|
||||
@@ -4618,7 +4625,7 @@ static void attackComputeDamage(Attack* attack, int ammoQuantity, int bonusDamag
|
||||
&& (critter->flags & OBJECT_MULTIHEX) == 0
|
||||
&& (damageType == DAMAGE_TYPE_EXPLOSION || attack->weapon == NULL || weaponGetAttackTypeForHitMode(attack->weapon, attack->hitMode) == ATTACK_TYPE_MELEE)
|
||||
&& PID_TYPE(critter->pid) == OBJ_TYPE_CRITTER
|
||||
&& _critter_flag_check(critter->pid, CRITTER_FLAG_0x4000) == 0) {
|
||||
&& !_critter_flag_check(critter->pid, CRITTER_NO_KNOCKBACK)) {
|
||||
bool shouldKnockback = true;
|
||||
bool hasStonewall = false;
|
||||
if (critter == gDude) {
|
||||
@@ -4661,7 +4668,7 @@ void _apply_damage(Attack* attack, bool animated)
|
||||
bool attackerIsCritter = attacker != NULL && FID_TYPE(attacker->fid) == OBJ_TYPE_CRITTER;
|
||||
bool v5 = attack->defender != attack->oops;
|
||||
|
||||
if (attackerIsCritter && (attacker->data.critter.combat.results & DAM_DEAD) != 0) {
|
||||
if (attackerIsCritter && (attacker->data.critter.combat.results & DAM_DEAD) == 0) {
|
||||
_set_new_results(attacker, attack->attackerFlags);
|
||||
// TODO: Not sure about "attack->defender == attack->oops".
|
||||
_damage_object(attacker, attack->attackerDamage, animated, attack->defender == attack->oops, attacker);
|
||||
@@ -4749,7 +4756,7 @@ void _apply_damage(Attack* attack, bool animated)
|
||||
// 0x424EE8
|
||||
static void _check_for_death(Object* object, int damage, int* flags)
|
||||
{
|
||||
if (object == NULL || !_critter_flag_check(object->pid, CRITTER_FLAG_0x400)) {
|
||||
if (object == NULL || !_critter_flag_check(object->pid, CRITTER_INVULNERABLE)) {
|
||||
if (object == NULL || PID_TYPE(object->pid) == OBJ_TYPE_CRITTER) {
|
||||
if (damage > 0) {
|
||||
if (critterGetHitPoints(object) - damage <= 0) {
|
||||
@@ -4771,7 +4778,7 @@ static void _set_new_results(Object* critter, int flags)
|
||||
return;
|
||||
}
|
||||
|
||||
if (_critter_flag_check(critter->pid, CRITTER_FLAG_0x400)) {
|
||||
if (_critter_flag_check(critter->pid, CRITTER_INVULNERABLE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4782,6 +4789,9 @@ static void _set_new_results(Object* critter, int flags)
|
||||
if ((flags & DAM_DEAD) != 0) {
|
||||
queueRemoveEvents(critter);
|
||||
} else if ((flags & DAM_KNOCKED_OUT) != 0) {
|
||||
// SFALL: Fix multiple knockout events.
|
||||
queueRemoveEventsByType(critter, EVENT_TYPE_KNOCKOUT);
|
||||
|
||||
int endurance = critterGetStat(critter, STAT_ENDURANCE);
|
||||
queueAddEvent(10 * (35 - 3 * endurance), critter, NULL, EVENT_TYPE_KNOCKOUT);
|
||||
}
|
||||
@@ -4809,7 +4819,7 @@ static void _damage_object(Object* a1, int damage, bool animated, int a4, Object
|
||||
return;
|
||||
}
|
||||
|
||||
if (_critter_flag_check(a1->pid, CRITTER_FLAG_0x400)) {
|
||||
if (_critter_flag_check(a1->pid, CRITTER_INVULNERABLE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4940,7 +4950,7 @@ void _combat_display(Attack* attack)
|
||||
// 708 (female) - Oops! %s was hit instead of you!
|
||||
messageListItem.num = baseMessageId + 8;
|
||||
if (messageListGetItem(&gCombatMessageList, &messageListItem)) {
|
||||
sprintf(text, messageListItem.text, mainCritterName);
|
||||
snprintf(text, sizeof(text), messageListItem.text, mainCritterName);
|
||||
}
|
||||
} else {
|
||||
// 509 (male) - Oops! %s were hit instead of %s!
|
||||
@@ -4948,7 +4958,7 @@ void _combat_display(Attack* attack)
|
||||
const char* name = objectGetName(attack->oops);
|
||||
messageListItem.num = baseMessageId + 9;
|
||||
if (messageListGetItem(&gCombatMessageList, &messageListItem)) {
|
||||
sprintf(text, messageListItem.text, mainCritterName, name);
|
||||
snprintf(text, sizeof(text), messageListItem.text, mainCritterName, name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -4962,7 +4972,7 @@ void _combat_display(Attack* attack)
|
||||
}
|
||||
|
||||
if (messageListGetItem(&gCombatMessageList, &messageListItem)) {
|
||||
sprintf(text, messageListItem.text, you);
|
||||
snprintf(text, sizeof(text), messageListItem.text, you);
|
||||
}
|
||||
} else {
|
||||
const char* name = objectGetName(attack->attacker);
|
||||
@@ -4975,7 +4985,7 @@ void _combat_display(Attack* attack)
|
||||
}
|
||||
|
||||
if (messageListGetItem(&gCombatMessageList, &messageListItem)) {
|
||||
sprintf(text, messageListItem.text, name);
|
||||
snprintf(text, sizeof(text), messageListItem.text, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5010,13 +5020,13 @@ void _combat_display(Attack* attack)
|
||||
|
||||
if (messageListGetItem(&gCombatMessageList, &messageListItem)) {
|
||||
if (attack->defenderDamage <= 1) {
|
||||
sprintf(text, messageListItem.text, mainCritterName);
|
||||
snprintf(text, sizeof(text), messageListItem.text, mainCritterName);
|
||||
} else {
|
||||
sprintf(text, messageListItem.text, mainCritterName, attack->defenderDamage);
|
||||
snprintf(text, sizeof(text), messageListItem.text, mainCritterName, attack->defenderDamage);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
combatCopyDamageAmountDescription(text, v21, attack->defenderDamage);
|
||||
combatCopyDamageAmountDescription(text, sizeof(text), v21, attack->defenderDamage);
|
||||
}
|
||||
} else {
|
||||
const char* hitLocationName = hitLocationGetName(v21, attack->defenderHitLocation);
|
||||
@@ -5055,18 +5065,15 @@ void _combat_display(Attack* attack)
|
||||
|
||||
if (messageListGetItem(&gCombatMessageList, &messageListItem)) {
|
||||
if (attack->defenderDamage <= 1) {
|
||||
sprintf(text, messageListItem.text, mainCritterName, hitLocationName);
|
||||
snprintf(text, sizeof(text), messageListItem.text, mainCritterName, hitLocationName);
|
||||
} else {
|
||||
sprintf(text, messageListItem.text, mainCritterName, hitLocationName, attack->defenderDamage);
|
||||
snprintf(text, sizeof(text), messageListItem.text, mainCritterName, hitLocationName, attack->defenderDamage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int combatMessages = 1;
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_MESSAGES_KEY, &combatMessages);
|
||||
|
||||
if (combatMessages == 1 && (attack->attackerFlags & DAM_CRITICAL) != 0 && attack->criticalMessageId != -1) {
|
||||
if (settings.preferences.combat_messages && (attack->attackerFlags & DAM_CRITICAL) != 0 && attack->criticalMessageId != -1) {
|
||||
messageListItem.num = attack->criticalMessageId;
|
||||
if (messageListGetItem(&gCombatMessageList, &messageListItem)) {
|
||||
strcat(text, messageListItem.text);
|
||||
@@ -5095,7 +5102,7 @@ void _combat_display(Attack* attack)
|
||||
}
|
||||
|
||||
if (messageListGetItem(&gCombatMessageList, &messageListItem)) {
|
||||
sprintf(text, "%s %s", mainCritterName, messageListItem.text);
|
||||
snprintf(text, sizeof(text), "%s %s", mainCritterName, messageListItem.text);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -5133,9 +5140,9 @@ void _combat_display(Attack* attack)
|
||||
|
||||
if (messageListGetItem(&gCombatMessageList, &messageListItem)) {
|
||||
if (attack->attackerDamage <= 1) {
|
||||
sprintf(text, messageListItem.text, mainCritterName);
|
||||
snprintf(text, sizeof(text), messageListItem.text, mainCritterName);
|
||||
} else {
|
||||
sprintf(text, messageListItem.text, mainCritterName, attack->attackerDamage);
|
||||
snprintf(text, sizeof(text), messageListItem.text, mainCritterName, attack->attackerDamage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5148,7 +5155,7 @@ void _combat_display(Attack* attack)
|
||||
|
||||
if ((attack->attackerFlags & DAM_HIT) != 0 || (attack->attackerFlags & DAM_CRITICAL) == 0) {
|
||||
if (attack->attackerDamage > 0) {
|
||||
combatCopyDamageAmountDescription(text, attack->attacker, attack->attackerDamage);
|
||||
combatCopyDamageAmountDescription(text, sizeof(text), attack->attacker, attack->attackerDamage);
|
||||
combatAddDamageFlagsDescription(text, attack->attackerFlags, attack->attacker);
|
||||
strcat(text, ".");
|
||||
displayMonitorAddMessage(text);
|
||||
@@ -5159,7 +5166,7 @@ void _combat_display(Attack* attack)
|
||||
for (int index = 0; index < attack->extrasLength; index++) {
|
||||
Object* critter = attack->extras[index];
|
||||
if ((critter->data.critter.combat.results & DAM_DEAD) == 0) {
|
||||
combatCopyDamageAmountDescription(text, critter, attack->extrasDamage[index]);
|
||||
combatCopyDamageAmountDescription(text, sizeof(text), critter, attack->extrasDamage[index]);
|
||||
combatAddDamageFlagsDescription(text, attack->extrasFlags[index], critter);
|
||||
strcat(text, ".");
|
||||
|
||||
@@ -5169,7 +5176,7 @@ void _combat_display(Attack* attack)
|
||||
}
|
||||
|
||||
// 0x425A9C
|
||||
static void combatCopyDamageAmountDescription(char* dest, Object* critter, int damage)
|
||||
static void combatCopyDamageAmountDescription(char* dest, size_t size, Object* critter, int damage)
|
||||
{
|
||||
MessageListItem messageListItem;
|
||||
char text[40];
|
||||
@@ -5220,9 +5227,9 @@ static void combatCopyDamageAmountDescription(char* dest, Object* critter, int d
|
||||
messageListItem.num = messageId;
|
||||
if (messageListGetItem(&gCombatMessageList, &messageListItem)) {
|
||||
if (damage <= 1) {
|
||||
sprintf(dest, messageListItem.text, name);
|
||||
snprintf(dest, size, messageListItem.text, name);
|
||||
} else {
|
||||
sprintf(dest, messageListItem.text, name, damage);
|
||||
snprintf(dest, size, messageListItem.text, name, damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5293,7 +5300,7 @@ static void combatAddDamageFlagsDescription(char* dest, int flags, Object* critt
|
||||
strcat(dest, messageListItem.text);
|
||||
}
|
||||
|
||||
messageListItem.num = flagsList[flagsListLength - 1];
|
||||
messageListItem.num = num + flagsList[flagsListLength - 1];
|
||||
if (messageListGetItem(&gCombatMessageList, &messageListItem)) {
|
||||
strcat(dest, messageListItem.text);
|
||||
}
|
||||
@@ -5481,7 +5488,7 @@ static int calledShotSelectHitLocation(Object* critter, int* hitLocation, int hi
|
||||
CALLED_SHOT_WINDOW_WIDTH,
|
||||
CALLED_SHOT_WINDOW_HEIGHT,
|
||||
_colorTable[0],
|
||||
WINDOW_FLAG_0x10);
|
||||
WINDOW_MODAL);
|
||||
if (gCalledShotWindow == -1) {
|
||||
return -1;
|
||||
}
|
||||
@@ -5579,7 +5586,9 @@ static int calledShotSelectHitLocation(Object* critter, int* hitLocation, int hi
|
||||
|
||||
int eventCode;
|
||||
while (true) {
|
||||
eventCode = _get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
eventCode = inputGetInput();
|
||||
|
||||
if (eventCode == KEY_ESCAPE) {
|
||||
break;
|
||||
@@ -5592,6 +5601,9 @@ static int calledShotSelectHitLocation(Object* critter, int* hitLocation, int hi
|
||||
if (_game_user_wants_to_quit != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
_gmouse_enable();
|
||||
@@ -5733,7 +5745,7 @@ void _combat_attack_this(Object* a1)
|
||||
messageListItem.num = 100; // You need %d action points.
|
||||
if (messageListGetItem(&gCombatMessageList, &messageListItem)) {
|
||||
int actionPointsRequired = weaponGetActionPointCost(gDude, hitMode, aiming);
|
||||
sprintf(formattedText, messageListItem.text, actionPointsRequired);
|
||||
snprintf(formattedText, sizeof(formattedText), messageListItem.text, actionPointsRequired);
|
||||
displayMonitorAddMessage(formattedText);
|
||||
}
|
||||
return;
|
||||
@@ -5793,9 +5805,7 @@ void _combat_attack_this(Object* a1)
|
||||
// 0x426AA8
|
||||
void _combat_outline_on()
|
||||
{
|
||||
int targetHighlight = TARGET_HIGHLIGHT_TARGETING_ONLY;
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_TARGET_HIGHLIGHT_KEY, &targetHighlight);
|
||||
if (targetHighlight == TARGET_HIGHLIGHT_OFF) {
|
||||
if (settings.preferences.target_highlight == TARGET_HIGHLIGHT_OFF) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5856,8 +5866,7 @@ void _combat_outline_off()
|
||||
// 0x426C64
|
||||
void _combat_highlight_change()
|
||||
{
|
||||
int targetHighlight = 2;
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_TARGET_HIGHLIGHT_KEY, &targetHighlight);
|
||||
int targetHighlight = settings.preferences.target_highlight;
|
||||
if (targetHighlight != _combat_highlight && isInCombat()) {
|
||||
if (targetHighlight != 0) {
|
||||
if (_combat_highlight == 0) {
|
||||
@@ -5909,8 +5918,13 @@ bool _combat_is_shot_blocked(Object* a1, int from, int to, Object* a4, int* a5)
|
||||
}
|
||||
|
||||
if ((obstacle->flags & OBJECT_MULTIHEX) != 0) {
|
||||
int rotation = tileGetRotationTo(current, to);
|
||||
current = tileGetTileInDirection(current, rotation, 1);
|
||||
// SFALL: Fix obtaining the next tile from a multihex object.
|
||||
// This bug does not cause any noticeable error in the function.
|
||||
current = obstacle->tile;
|
||||
if (current != to) {
|
||||
int rotation = tileGetRotationTo(current, to);
|
||||
current = tileGetTileInDirection(current, rotation, 1);
|
||||
}
|
||||
} else {
|
||||
current = obstacle->tile;
|
||||
}
|
||||
@@ -5999,6 +6013,11 @@ void _combatKillCritterOutsideCombat(Object* critter_obj, char* msg)
|
||||
}
|
||||
}
|
||||
|
||||
int combatGetTargetHighlight()
|
||||
{
|
||||
return _combat_highlight;
|
||||
}
|
||||
|
||||
static void criticalsInit()
|
||||
{
|
||||
int mode = 2;
|
||||
@@ -6157,7 +6176,7 @@ static void criticalsInit()
|
||||
for (int killType = 0; killType < KILL_TYPE_COUNT + 1; killType++) {
|
||||
for (int hitLocation = 0; hitLocation < HIT_LOCATION_COUNT; hitLocation++) {
|
||||
for (int effect = 0; effect < CRTICIAL_EFFECT_COUNT; effect++) {
|
||||
sprintf(sectionKey, "c_%02d_%d_%d", killType, hitLocation, effect);
|
||||
snprintf(sectionKey, sizeof(sectionKey), "c_%02d_%d_%d", killType, hitLocation, effect);
|
||||
|
||||
// Update player kill type if needed.
|
||||
int newKillType = killType == KILL_TYPE_COUNT ? SFALL_KILL_TYPE_COUNT : killType;
|
||||
@@ -6177,7 +6196,7 @@ static void criticalsInit()
|
||||
|
||||
// Read Sfall kill types (38) plus one for the player.
|
||||
for (int killType = 0; killType < SFALL_KILL_TYPE_COUNT + 1; killType++) {
|
||||
sprintf(ktSectionKey, "c_%02d", killType);
|
||||
snprintf(ktSectionKey, sizeof(ktSectionKey), "c_%02d", killType);
|
||||
|
||||
int enabled = 0;
|
||||
configGetInt(&criticalsConfig, ktSectionKey, "Enabled", &enabled);
|
||||
@@ -6189,7 +6208,7 @@ static void criticalsInit()
|
||||
if (enabled < 2) {
|
||||
bool hitLocationChanged = false;
|
||||
|
||||
sprintf(key, "Part_%d", hitLocation);
|
||||
snprintf(key, sizeof(key), "Part_%d", hitLocation);
|
||||
configGetBool(&criticalsConfig, ktSectionKey, key, &hitLocationChanged);
|
||||
|
||||
if (!hitLocationChanged) {
|
||||
@@ -6197,12 +6216,12 @@ static void criticalsInit()
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(hitLocationSectionKey, "c_%02d_%d", killType, hitLocation);
|
||||
snprintf(hitLocationSectionKey, sizeof(hitLocationSectionKey), "c_%02d_%d", killType, hitLocation);
|
||||
|
||||
for (int effect = 0; effect < CRTICIAL_EFFECT_COUNT; effect++) {
|
||||
for (int dataMember = 0; dataMember < CRIT_DATA_MEMBER_COUNT; dataMember++) {
|
||||
int value = criticalsGetValue(killType, hitLocation, effect, dataMember);
|
||||
sprintf(key, "e%d_%s", effect, gCritDataMemberKeys[dataMember]);
|
||||
snprintf(key, sizeof(key), "e%d_%s", effect, gCritDataMemberKeys[dataMember]);
|
||||
if (configGetInt(&criticalsConfig, hitLocationSectionKey, key, &value)) {
|
||||
criticalsSetValue(killType, hitLocation, effect, dataMember, value);
|
||||
}
|
||||
@@ -6502,7 +6521,7 @@ static void unarmedInitCustom()
|
||||
}
|
||||
|
||||
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]);
|
||||
sprintf(section, "%d", hitMode);
|
||||
snprintf(section, sizeof(section), "%d", hitMode);
|
||||
|
||||
configGetInt(&unarmedConfig, section, "ReqLevel", &(hitDescription->requiredLevel));
|
||||
configGetInt(&unarmedConfig, section, "SkillLevel", &(hitDescription->requiredSkill));
|
||||
@@ -6515,7 +6534,7 @@ static void unarmedInitCustom()
|
||||
configGetBool(&unarmedConfig, section, "Secondary", &(hitDescription->isSecondary));
|
||||
|
||||
for (int stat = 0; stat < PRIMARY_STAT_COUNT; stat++) {
|
||||
sprintf(statKey, "Stat%d", stat);
|
||||
snprintf(statKey, sizeof(statKey), "Stat%d", stat);
|
||||
configGetInt(&unarmedConfig, section, statKey, &(hitDescription->requiredStats[stat]));
|
||||
}
|
||||
}
|
||||
@@ -6788,4 +6807,27 @@ static void damageModCalculateYaam(DamageCalculationContext* context)
|
||||
}
|
||||
}
|
||||
|
||||
int combat_get_hit_location_penalty(int hit_location)
|
||||
{
|
||||
if (hit_location >= 0 && hit_location < HIT_LOCATION_COUNT) {
|
||||
return hit_location_penalty[hit_location];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void combat_set_hit_location_penalty(int hit_location, int penalty)
|
||||
{
|
||||
if (hit_location >= 0 && hit_location < HIT_LOCATION_COUNT) {
|
||||
hit_location_penalty[hit_location] = penalty;
|
||||
}
|
||||
}
|
||||
|
||||
void combat_reset_hit_location_penalty()
|
||||
{
|
||||
for (int hit_location = 0; hit_location < HIT_LOCATION_COUNT; hit_location++) {
|
||||
hit_location_penalty[hit_location] = hit_location_penalty_default[hit_location];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
@@ -58,6 +58,7 @@ int _combat_explode_scenery(Object* a1, Object* a2);
|
||||
void _combat_delete_critter(Object* obj);
|
||||
void _combatKillCritterOutsideCombat(Object* critter_obj, char* msg);
|
||||
|
||||
int combatGetTargetHighlight();
|
||||
int criticalsGetValue(int killType, int hitLocation, int effect, int dataMember);
|
||||
void criticalsSetValue(int killType, int hitLocation, int effect, int dataMember, int value);
|
||||
void criticalsResetValue(int killType, int hitLocation, int effect, int dataMember);
|
||||
@@ -70,6 +71,9 @@ int unarmedGetKickHitMode(bool isSecondary);
|
||||
bool unarmedIsPenetrating(int hitMode);
|
||||
bool damageModGetBonusHthDamageFix();
|
||||
bool damageModGetDisplayBonusDamage();
|
||||
int combat_get_hit_location_penalty(int hit_location);
|
||||
void combat_set_hit_location_penalty(int hit_location, int penalty);
|
||||
void combat_reset_hit_location_penalty();
|
||||
|
||||
static inline bool isInCombat()
|
||||
{
|
||||
|
||||
+615
-504
File diff suppressed because it is too large
Load Diff
+6
-6
@@ -44,10 +44,10 @@ int aiSetAttackWho(Object* critter, int attackWho);
|
||||
int aiSetChemUse(Object* critter, int chemUse);
|
||||
int aiGetDisposition(Object* obj);
|
||||
int aiSetDisposition(Object* obj, int a2);
|
||||
int _caiSetupTeamCombat(Object* a1, Object* a2);
|
||||
int _caiTeamCombatInit(Object** a1, int a2);
|
||||
int _caiSetupTeamCombat(Object* attackerTeam, Object* defenderTeam);
|
||||
int _caiTeamCombatInit(Object** crittersList, int crittersListLength);
|
||||
void _caiTeamCombatExit();
|
||||
Object* _ai_search_inven_weap(Object* critter, int a2, Object* a3);
|
||||
Object* _ai_search_inven_weap(Object* critter, bool checkRequiredActionPoints, Object* defender);
|
||||
Object* _ai_search_inven_armor(Object* critter);
|
||||
int _cAIPrepWeaponItem(Object* critter, Object* item);
|
||||
void aiAttemptWeaponReload(Object* critter, int animate);
|
||||
@@ -59,10 +59,10 @@ bool _combatai_want_to_join(Object* a1);
|
||||
bool _combatai_want_to_stop(Object* a1);
|
||||
int critterSetTeam(Object* obj, int team);
|
||||
int critterSetAiPacket(Object* object, int aiPacket);
|
||||
int _combatai_msg(Object* a1, Attack* attack, int a3, int a4);
|
||||
int _combatai_msg(Object* critter, Attack* attack, int type, int delay);
|
||||
Object* _combat_ai_random_target(Attack* attack);
|
||||
int _combatai_check_retaliation(Object* a1, Object* a2);
|
||||
bool objectCanHearObject(Object* a1, Object* a2);
|
||||
void _combatai_check_retaliation(Object* a1, Object* a2);
|
||||
bool isWithinPerception(Object* a1, Object* a2);
|
||||
void aiMessageListReloadIfNeeded();
|
||||
void _combatai_notify_onlookers(Object* a1);
|
||||
void _combatai_notify_friends(Object* a1);
|
||||
|
||||
+44
-23
@@ -248,12 +248,14 @@ bool configGetIntList(Config* config, const char* sectionKey, const char* key, i
|
||||
string = pch + 1;
|
||||
}
|
||||
|
||||
if (count <= 1) {
|
||||
// SFALL: Fix getting last item in a list if the list has less than the
|
||||
// requested number of values (for `chem_primary_desire`).
|
||||
if (count > 0) {
|
||||
*arr = atoi(string);
|
||||
return true;
|
||||
count--;
|
||||
}
|
||||
|
||||
return false;
|
||||
return count == 0;
|
||||
}
|
||||
|
||||
// 0x42C160
|
||||
@@ -278,24 +280,28 @@ bool configRead(Config* config, const char* filePath, bool isDb)
|
||||
|
||||
if (isDb) {
|
||||
File* stream = fileOpen(filePath, "rb");
|
||||
if (stream != NULL) {
|
||||
while (fileReadString(string, sizeof(string), stream) != NULL) {
|
||||
configParseLine(config, string);
|
||||
}
|
||||
fileClose(stream);
|
||||
|
||||
// CE: Return `false` if file does not exists in database.
|
||||
if (stream == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (fileReadString(string, sizeof(string), stream) != NULL) {
|
||||
configParseLine(config, string);
|
||||
}
|
||||
fileClose(stream);
|
||||
} else {
|
||||
FILE* stream = compat_fopen(filePath, "rt");
|
||||
if (stream != NULL) {
|
||||
while (fgets(string, sizeof(string), stream) != NULL) {
|
||||
configParseLine(config, string);
|
||||
}
|
||||
|
||||
fclose(stream);
|
||||
// CE: Return `false` if file does not exists on the file system.
|
||||
if (stream == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: This function returns `true` even if the file was not actually
|
||||
// read. I'm pretty sure it's bug.
|
||||
while (compat_fgets(string, sizeof(string), stream) != NULL) {
|
||||
configParseLine(config, string);
|
||||
}
|
||||
fclose(stream);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -379,10 +385,25 @@ static bool configParseLine(Config* config, char* string)
|
||||
*pch = '\0';
|
||||
}
|
||||
|
||||
// Find opening bracket.
|
||||
pch = strchr(string, '[');
|
||||
if (pch != NULL) {
|
||||
char* sectionKey = pch + 1;
|
||||
// CE: Original implementation treats any line with brackets as section key.
|
||||
// The problem can be seen when loading Olympus settings (ddraw.ini), which
|
||||
// contains the following line:
|
||||
//
|
||||
// ```ini
|
||||
// VersionString=Olympus 2207 [Complete].
|
||||
// ```
|
||||
//
|
||||
// It thinks that [Complete] is a start of new section, and puts remaining
|
||||
// keys there.
|
||||
|
||||
// Skip leading whitespace.
|
||||
while (isspace(static_cast<unsigned char>(*string))) {
|
||||
string++;
|
||||
}
|
||||
|
||||
// Check if it's a section key.
|
||||
if (*string == '[') {
|
||||
char* sectionKey = string + 1;
|
||||
|
||||
// Find closing bracket.
|
||||
pch = strchr(sectionKey, ']');
|
||||
@@ -471,7 +492,7 @@ static bool configTrimString(char* string)
|
||||
return false;
|
||||
}
|
||||
|
||||
int length = strlen(string);
|
||||
size_t length = strlen(string);
|
||||
if (length == 0) {
|
||||
return true;
|
||||
}
|
||||
@@ -479,7 +500,7 @@ static bool configTrimString(char* string)
|
||||
// Starting from the end of the string, loop while it's a whitespace and
|
||||
// decrement string length.
|
||||
char* pch = string + length - 1;
|
||||
while (length != 0 && isspace(*pch)) {
|
||||
while (length != 0 && isspace(static_cast<unsigned char>(*pch))) {
|
||||
length--;
|
||||
pch--;
|
||||
}
|
||||
@@ -490,7 +511,7 @@ static bool configTrimString(char* string)
|
||||
// Starting from the beginning of the string loop while it's a whitespace
|
||||
// and decrement string length.
|
||||
pch = string;
|
||||
while (isspace(*pch)) {
|
||||
while (isspace(static_cast<unsigned char>(*pch))) {
|
||||
pch++;
|
||||
length--;
|
||||
}
|
||||
@@ -522,7 +543,7 @@ bool configGetDouble(Config* config, const char* sectionKey, const char* key, do
|
||||
bool configSetDouble(Config* config, const char* sectionKey, const char* key, double value)
|
||||
{
|
||||
char stringValue[32];
|
||||
sprintf(stringValue, "%.6f", value);
|
||||
snprintf(stringValue, sizeof(stringValue), "%.6f", value);
|
||||
|
||||
return configSetString(config, sectionKey, key, stringValue);
|
||||
}
|
||||
|
||||
-703
@@ -1,703 +0,0 @@
|
||||
#ifndef CORE_H
|
||||
#define CORE_H
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "db.h"
|
||||
#include "dinput.h"
|
||||
#include "geometry.h"
|
||||
#include "window.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
#define MOUSE_DEFAULT_CURSOR_WIDTH 8
|
||||
#define MOUSE_DEFAULT_CURSOR_HEIGHT 8
|
||||
#define MOUSE_DEFAULT_CURSOR_SIZE (MOUSE_DEFAULT_CURSOR_WIDTH * MOUSE_DEFAULT_CURSOR_HEIGHT)
|
||||
|
||||
#define MOUSE_STATE_LEFT_BUTTON_DOWN 0x01
|
||||
#define MOUSE_STATE_RIGHT_BUTTON_DOWN 0x02
|
||||
|
||||
#define MOUSE_EVENT_LEFT_BUTTON_DOWN 0x01
|
||||
#define MOUSE_EVENT_RIGHT_BUTTON_DOWN 0x02
|
||||
#define MOUSE_EVENT_LEFT_BUTTON_REPEAT 0x04
|
||||
#define MOUSE_EVENT_RIGHT_BUTTON_REPEAT 0x08
|
||||
#define MOUSE_EVENT_LEFT_BUTTON_UP 0x10
|
||||
#define MOUSE_EVENT_RIGHT_BUTTON_UP 0x20
|
||||
#define MOUSE_EVENT_ANY_BUTTON_DOWN (MOUSE_EVENT_LEFT_BUTTON_DOWN | MOUSE_EVENT_RIGHT_BUTTON_DOWN)
|
||||
#define MOUSE_EVENT_ANY_BUTTON_REPEAT (MOUSE_EVENT_LEFT_BUTTON_REPEAT | MOUSE_EVENT_RIGHT_BUTTON_REPEAT)
|
||||
#define MOUSE_EVENT_ANY_BUTTON_UP (MOUSE_EVENT_LEFT_BUTTON_UP | MOUSE_EVENT_RIGHT_BUTTON_UP)
|
||||
#define MOUSE_EVENT_LEFT_BUTTON_DOWN_REPEAT (MOUSE_EVENT_LEFT_BUTTON_DOWN | MOUSE_EVENT_LEFT_BUTTON_REPEAT)
|
||||
#define MOUSE_EVENT_RIGHT_BUTTON_DOWN_REPEAT (MOUSE_EVENT_RIGHT_BUTTON_DOWN | MOUSE_EVENT_RIGHT_BUTTON_REPEAT)
|
||||
#define MOUSE_EVENT_WHEEL 0x40
|
||||
|
||||
#define BUTTON_REPEAT_TIME 250
|
||||
|
||||
#define KEY_STATE_UP 0
|
||||
#define KEY_STATE_DOWN 1
|
||||
#define KEY_STATE_REPEAT 2
|
||||
|
||||
#define MODIFIER_KEY_STATE_NUM_LOCK 0x01
|
||||
#define MODIFIER_KEY_STATE_CAPS_LOCK 0x02
|
||||
#define MODIFIER_KEY_STATE_SCROLL_LOCK 0x04
|
||||
|
||||
#define KEYBOARD_EVENT_MODIFIER_CAPS_LOCK 0x0001
|
||||
#define KEYBOARD_EVENT_MODIFIER_NUM_LOCK 0x0002
|
||||
#define KEYBOARD_EVENT_MODIFIER_SCROLL_LOCK 0x0004
|
||||
#define KEYBOARD_EVENT_MODIFIER_LEFT_SHIFT 0x0008
|
||||
#define KEYBOARD_EVENT_MODIFIER_RIGHT_SHIFT 0x0010
|
||||
#define KEYBOARD_EVENT_MODIFIER_LEFT_ALT 0x0020
|
||||
#define KEYBOARD_EVENT_MODIFIER_RIGHT_ALT 0x0040
|
||||
#define KEYBOARD_EVENT_MODIFIER_LEFT_CONTROL 0x0080
|
||||
#define KEYBOARD_EVENT_MODIFIER_RIGHT_CONTROL 0x0100
|
||||
#define KEYBOARD_EVENT_MODIFIER_ANY_SHIFT (KEYBOARD_EVENT_MODIFIER_LEFT_SHIFT | KEYBOARD_EVENT_MODIFIER_RIGHT_SHIFT)
|
||||
#define KEYBOARD_EVENT_MODIFIER_ANY_ALT (KEYBOARD_EVENT_MODIFIER_LEFT_ALT | KEYBOARD_EVENT_MODIFIER_RIGHT_ALT)
|
||||
#define KEYBOARD_EVENT_MODIFIER_ANY_CONTROL (KEYBOARD_EVENT_MODIFIER_LEFT_CONTROL | KEYBOARD_EVENT_MODIFIER_RIGHT_CONTROL)
|
||||
|
||||
#define KEY_QUEUE_SIZE 64
|
||||
|
||||
typedef enum Key {
|
||||
KEY_ESCAPE = '\x1b',
|
||||
KEY_TAB = '\x09',
|
||||
KEY_BACKSPACE = '\x08',
|
||||
KEY_RETURN = '\r',
|
||||
|
||||
KEY_SPACE = ' ',
|
||||
KEY_EXCLAMATION = '!',
|
||||
KEY_QUOTE = '"',
|
||||
KEY_NUMBER_SIGN = '#',
|
||||
KEY_DOLLAR = '$',
|
||||
KEY_PERCENT = '%',
|
||||
KEY_AMPERSAND = '&',
|
||||
KEY_SINGLE_QUOTE = '\'',
|
||||
KEY_PAREN_LEFT = '(',
|
||||
KEY_PAREN_RIGHT = ')',
|
||||
KEY_ASTERISK = '*',
|
||||
KEY_PLUS = '+',
|
||||
KEY_COMMA = ',',
|
||||
KEY_MINUS = '-',
|
||||
KEY_DOT = '.',
|
||||
KEY_SLASH = '/',
|
||||
KEY_0 = '0',
|
||||
KEY_1 = '1',
|
||||
KEY_2 = '2',
|
||||
KEY_3 = '3',
|
||||
KEY_4 = '4',
|
||||
KEY_5 = '5',
|
||||
KEY_6 = '6',
|
||||
KEY_7 = '7',
|
||||
KEY_8 = '8',
|
||||
KEY_9 = '9',
|
||||
KEY_COLON = ':',
|
||||
KEY_SEMICOLON = ';',
|
||||
KEY_LESS = '<',
|
||||
KEY_EQUAL = '=',
|
||||
KEY_GREATER = '>',
|
||||
KEY_QUESTION = '?',
|
||||
KEY_AT = '@',
|
||||
KEY_UPPERCASE_A = 'A',
|
||||
KEY_UPPERCASE_B = 'B',
|
||||
KEY_UPPERCASE_C = 'C',
|
||||
KEY_UPPERCASE_D = 'D',
|
||||
KEY_UPPERCASE_E = 'E',
|
||||
KEY_UPPERCASE_F = 'F',
|
||||
KEY_UPPERCASE_G = 'G',
|
||||
KEY_UPPERCASE_H = 'H',
|
||||
KEY_UPPERCASE_I = 'I',
|
||||
KEY_UPPERCASE_J = 'J',
|
||||
KEY_UPPERCASE_K = 'K',
|
||||
KEY_UPPERCASE_L = 'L',
|
||||
KEY_UPPERCASE_M = 'M',
|
||||
KEY_UPPERCASE_N = 'N',
|
||||
KEY_UPPERCASE_O = 'O',
|
||||
KEY_UPPERCASE_P = 'P',
|
||||
KEY_UPPERCASE_Q = 'Q',
|
||||
KEY_UPPERCASE_R = 'R',
|
||||
KEY_UPPERCASE_S = 'S',
|
||||
KEY_UPPERCASE_T = 'T',
|
||||
KEY_UPPERCASE_U = 'U',
|
||||
KEY_UPPERCASE_V = 'V',
|
||||
KEY_UPPERCASE_W = 'W',
|
||||
KEY_UPPERCASE_X = 'X',
|
||||
KEY_UPPERCASE_Y = 'Y',
|
||||
KEY_UPPERCASE_Z = 'Z',
|
||||
|
||||
KEY_BRACKET_LEFT = '[',
|
||||
KEY_BACKSLASH = '\\',
|
||||
KEY_BRACKET_RIGHT = ']',
|
||||
KEY_CARET = '^',
|
||||
KEY_UNDERSCORE = '_',
|
||||
|
||||
KEY_GRAVE = '`',
|
||||
KEY_LOWERCASE_A = 'a',
|
||||
KEY_LOWERCASE_B = 'b',
|
||||
KEY_LOWERCASE_C = 'c',
|
||||
KEY_LOWERCASE_D = 'd',
|
||||
KEY_LOWERCASE_E = 'e',
|
||||
KEY_LOWERCASE_F = 'f',
|
||||
KEY_LOWERCASE_G = 'g',
|
||||
KEY_LOWERCASE_H = 'h',
|
||||
KEY_LOWERCASE_I = 'i',
|
||||
KEY_LOWERCASE_J = 'j',
|
||||
KEY_LOWERCASE_K = 'k',
|
||||
KEY_LOWERCASE_L = 'l',
|
||||
KEY_LOWERCASE_M = 'm',
|
||||
KEY_LOWERCASE_N = 'n',
|
||||
KEY_LOWERCASE_O = 'o',
|
||||
KEY_LOWERCASE_P = 'p',
|
||||
KEY_LOWERCASE_Q = 'q',
|
||||
KEY_LOWERCASE_R = 'r',
|
||||
KEY_LOWERCASE_S = 's',
|
||||
KEY_LOWERCASE_T = 't',
|
||||
KEY_LOWERCASE_U = 'u',
|
||||
KEY_LOWERCASE_V = 'v',
|
||||
KEY_LOWERCASE_W = 'w',
|
||||
KEY_LOWERCASE_X = 'x',
|
||||
KEY_LOWERCASE_Y = 'y',
|
||||
KEY_LOWERCASE_Z = 'z',
|
||||
KEY_BRACE_LEFT = '{',
|
||||
KEY_BAR = '|',
|
||||
KEY_BRACE_RIGHT = '}',
|
||||
KEY_TILDE = '~',
|
||||
KEY_DEL = 127,
|
||||
|
||||
KEY_136 = 136,
|
||||
KEY_146 = 146,
|
||||
KEY_149 = 149,
|
||||
KEY_150 = 150,
|
||||
KEY_151 = 151,
|
||||
KEY_152 = 152,
|
||||
KEY_161 = 161,
|
||||
KEY_163 = 163,
|
||||
KEY_164 = 164,
|
||||
KEY_166 = 166,
|
||||
KEY_168 = 168,
|
||||
KEY_167 = 167,
|
||||
KEY_170 = 170,
|
||||
KEY_172 = 172,
|
||||
KEY_176 = 176,
|
||||
KEY_178 = 178,
|
||||
KEY_179 = 179,
|
||||
KEY_180 = 180,
|
||||
KEY_181 = 181,
|
||||
KEY_186 = 186,
|
||||
KEY_191 = 191,
|
||||
KEY_196 = 196,
|
||||
KEY_199 = 199,
|
||||
KEY_209 = 209,
|
||||
KEY_214 = 214,
|
||||
KEY_215 = 215,
|
||||
KEY_220 = 220,
|
||||
KEY_223 = 223,
|
||||
KEY_224 = 224,
|
||||
KEY_228 = 228,
|
||||
KEY_231 = 231,
|
||||
KEY_232 = 232,
|
||||
KEY_233 = 233,
|
||||
KEY_241 = 241,
|
||||
KEY_246 = 246,
|
||||
KEY_247 = 247,
|
||||
KEY_249 = 249,
|
||||
KEY_252 = 252,
|
||||
|
||||
KEY_ALT_Q = 272,
|
||||
KEY_ALT_W = 273,
|
||||
KEY_ALT_E = 274,
|
||||
KEY_ALT_R = 275,
|
||||
KEY_ALT_T = 276,
|
||||
KEY_ALT_Y = 277,
|
||||
KEY_ALT_U = 278,
|
||||
KEY_ALT_I = 279,
|
||||
KEY_ALT_O = 280,
|
||||
KEY_ALT_P = 281,
|
||||
KEY_ALT_A = 286,
|
||||
KEY_ALT_S = 287,
|
||||
KEY_ALT_D = 288,
|
||||
KEY_ALT_F = 289,
|
||||
KEY_ALT_G = 290,
|
||||
KEY_ALT_H = 291,
|
||||
KEY_ALT_J = 292,
|
||||
KEY_ALT_K = 293,
|
||||
KEY_ALT_L = 294,
|
||||
KEY_ALT_Z = 300,
|
||||
KEY_ALT_X = 301,
|
||||
KEY_ALT_C = 302,
|
||||
KEY_ALT_V = 303,
|
||||
KEY_ALT_B = 304,
|
||||
KEY_ALT_N = 305,
|
||||
KEY_ALT_M = 306,
|
||||
|
||||
KEY_CTRL_Q = 17,
|
||||
KEY_CTRL_W = 23,
|
||||
KEY_CTRL_E = 5,
|
||||
KEY_CTRL_R = 18,
|
||||
KEY_CTRL_T = 20,
|
||||
KEY_CTRL_Y = 25,
|
||||
KEY_CTRL_U = 21,
|
||||
KEY_CTRL_I = 9,
|
||||
KEY_CTRL_O = 15,
|
||||
KEY_CTRL_P = 16,
|
||||
KEY_CTRL_A = 1,
|
||||
KEY_CTRL_S = 19,
|
||||
KEY_CTRL_D = 4,
|
||||
KEY_CTRL_F = 6,
|
||||
KEY_CTRL_G = 7,
|
||||
KEY_CTRL_H = 8,
|
||||
KEY_CTRL_J = 10,
|
||||
KEY_CTRL_K = 11,
|
||||
KEY_CTRL_L = 12,
|
||||
KEY_CTRL_Z = 26,
|
||||
KEY_CTRL_X = 24,
|
||||
KEY_CTRL_C = 3,
|
||||
KEY_CTRL_V = 22,
|
||||
KEY_CTRL_B = 2,
|
||||
KEY_CTRL_N = 14,
|
||||
KEY_CTRL_M = 13,
|
||||
|
||||
KEY_F1 = 315,
|
||||
KEY_F2 = 316,
|
||||
KEY_F3 = 317,
|
||||
KEY_F4 = 318,
|
||||
KEY_F5 = 319,
|
||||
KEY_F6 = 320,
|
||||
KEY_F7 = 321,
|
||||
KEY_F8 = 322,
|
||||
KEY_F9 = 323,
|
||||
KEY_F10 = 324,
|
||||
KEY_F11 = 389,
|
||||
KEY_F12 = 390,
|
||||
|
||||
KEY_SHIFT_F1 = 340,
|
||||
KEY_SHIFT_F2 = 341,
|
||||
KEY_SHIFT_F3 = 342,
|
||||
KEY_SHIFT_F4 = 343,
|
||||
KEY_SHIFT_F5 = 344,
|
||||
KEY_SHIFT_F6 = 345,
|
||||
KEY_SHIFT_F7 = 346,
|
||||
KEY_SHIFT_F8 = 347,
|
||||
KEY_SHIFT_F9 = 348,
|
||||
KEY_SHIFT_F10 = 349,
|
||||
KEY_SHIFT_F11 = 391,
|
||||
KEY_SHIFT_F12 = 392,
|
||||
|
||||
KEY_CTRL_F1 = 350,
|
||||
KEY_CTRL_F2 = 351,
|
||||
KEY_CTRL_F3 = 352,
|
||||
KEY_CTRL_F4 = 353,
|
||||
KEY_CTRL_F5 = 354,
|
||||
KEY_CTRL_F6 = 355,
|
||||
KEY_CTRL_F7 = 356,
|
||||
KEY_CTRL_F8 = 357,
|
||||
KEY_CTRL_F9 = 358,
|
||||
KEY_CTRL_F10 = 359,
|
||||
KEY_CTRL_F11 = 393,
|
||||
KEY_CTRL_F12 = 394,
|
||||
|
||||
KEY_ALT_F1 = 360,
|
||||
KEY_ALT_F2 = 361,
|
||||
KEY_ALT_F3 = 362,
|
||||
KEY_ALT_F4 = 363,
|
||||
KEY_ALT_F5 = 364,
|
||||
KEY_ALT_F6 = 365,
|
||||
KEY_ALT_F7 = 366,
|
||||
KEY_ALT_F8 = 367,
|
||||
KEY_ALT_F9 = 368,
|
||||
KEY_ALT_F10 = 369,
|
||||
KEY_ALT_F11 = 395,
|
||||
KEY_ALT_F12 = 396,
|
||||
|
||||
KEY_HOME = 327,
|
||||
KEY_CTRL_HOME = 375,
|
||||
KEY_ALT_HOME = 407,
|
||||
|
||||
KEY_PAGE_UP = 329,
|
||||
KEY_CTRL_PAGE_UP = 388,
|
||||
KEY_ALT_PAGE_UP = 409,
|
||||
|
||||
KEY_INSERT = 338,
|
||||
KEY_CTRL_INSERT = 402,
|
||||
KEY_ALT_INSERT = 418,
|
||||
|
||||
KEY_DELETE = 339,
|
||||
KEY_CTRL_DELETE = 403,
|
||||
KEY_ALT_DELETE = 419,
|
||||
|
||||
KEY_END = 335,
|
||||
KEY_CTRL_END = 373,
|
||||
KEY_ALT_END = 415,
|
||||
|
||||
KEY_PAGE_DOWN = 337,
|
||||
KEY_ALT_PAGE_DOWN = 417,
|
||||
KEY_CTRL_PAGE_DOWN = 374,
|
||||
|
||||
KEY_ARROW_UP = 328,
|
||||
KEY_CTRL_ARROW_UP = 397,
|
||||
KEY_ALT_ARROW_UP = 408,
|
||||
|
||||
KEY_ARROW_DOWN = 336,
|
||||
KEY_CTRL_ARROW_DOWN = 401,
|
||||
KEY_ALT_ARROW_DOWN = 416,
|
||||
|
||||
KEY_ARROW_LEFT = 331,
|
||||
KEY_CTRL_ARROW_LEFT = 371,
|
||||
KEY_ALT_ARROW_LEFT = 411,
|
||||
|
||||
KEY_ARROW_RIGHT = 333,
|
||||
KEY_CTRL_ARROW_RIGHT = 372,
|
||||
KEY_ALT_ARROW_RIGHT = 413,
|
||||
|
||||
KEY_CTRL_BACKSLASH = 192,
|
||||
|
||||
KEY_NUMBERPAD_5 = 332,
|
||||
KEY_CTRL_NUMBERPAD_5 = 399,
|
||||
KEY_ALT_NUMBERPAD_5 = 9999,
|
||||
|
||||
KEY_FIRST_INPUT_CHARACTER = KEY_SPACE,
|
||||
KEY_LAST_INPUT_CHARACTER = KEY_LOWERCASE_Z,
|
||||
} Key;
|
||||
|
||||
typedef enum KeyboardLayout {
|
||||
KEYBOARD_LAYOUT_QWERTY,
|
||||
KEYBOARD_LAYOUT_FRENCH,
|
||||
KEYBOARD_LAYOUT_GERMAN,
|
||||
KEYBOARD_LAYOUT_ITALIAN,
|
||||
KEYBOARD_LAYOUT_SPANISH,
|
||||
} KeyboardLayout;
|
||||
|
||||
#define VCR_BUFFER_CAPACITY 4096
|
||||
|
||||
typedef enum VcrState {
|
||||
VCR_STATE_RECORDING,
|
||||
VCR_STATE_PLAYING,
|
||||
VCR_STATE_TURNED_OFF,
|
||||
} VcrState;
|
||||
|
||||
#define VCR_STATE_STOP_REQUESTED 0x80000000
|
||||
|
||||
typedef enum VcrTerminationFlags {
|
||||
// Specifies that VCR playback should stop if any key is pressed.
|
||||
VCR_TERMINATE_ON_KEY_PRESS = 0x01,
|
||||
|
||||
// Specifies that VCR playback should stop if mouse is mouved.
|
||||
VCR_TERMINATE_ON_MOUSE_MOVE = 0x02,
|
||||
|
||||
// Specifies that VCR playback should stop if any mouse button is pressed.
|
||||
VCR_TERMINATE_ON_MOUSE_PRESS = 0x04,
|
||||
} VcrTerminationFlags;
|
||||
|
||||
typedef enum VcrPlaybackCompletionReason {
|
||||
VCR_PLAYBACK_COMPLETION_REASON_NONE = 0,
|
||||
|
||||
// Indicates that VCR playback completed normally.
|
||||
VCR_PLAYBACK_COMPLETION_REASON_COMPLETED = 1,
|
||||
|
||||
// Indicates that VCR playback terminated according to termination flags.
|
||||
VCR_PLAYBACK_COMPLETION_REASON_TERMINATED = 2,
|
||||
} VcrPlaybackCompletionReason;
|
||||
|
||||
typedef enum VcrEntryType {
|
||||
VCR_ENTRY_TYPE_NONE = 0,
|
||||
VCR_ENTRY_TYPE_INITIAL_STATE = 1,
|
||||
VCR_ENTRY_TYPE_KEYBOARD_EVENT = 2,
|
||||
VCR_ENTRY_TYPE_MOUSE_EVENT = 3,
|
||||
} VcrEntryType;
|
||||
|
||||
typedef struct STRUCT_6ABF50 {
|
||||
// Time when appropriate key was pressed down or -1 if it's up.
|
||||
int tick;
|
||||
int repeatCount;
|
||||
} STRUCT_6ABF50;
|
||||
|
||||
typedef struct InputEvent {
|
||||
// This is either logical key or input event id, which can be either
|
||||
// character code pressed or some other numbers used throughout the
|
||||
// game interface.
|
||||
int logicalKey;
|
||||
int mouseX;
|
||||
int mouseY;
|
||||
} InputEvent;
|
||||
|
||||
typedef void(IdleFunc)();
|
||||
typedef void(FocusFunc)(bool focus);
|
||||
typedef void(TickerProc)();
|
||||
|
||||
typedef struct TickerListNode {
|
||||
int flags;
|
||||
TickerProc* proc;
|
||||
struct TickerListNode* next;
|
||||
} TickerListNode;
|
||||
|
||||
typedef struct VcrEntry {
|
||||
unsigned int type;
|
||||
unsigned int time;
|
||||
unsigned int counter;
|
||||
union {
|
||||
struct {
|
||||
int mouseX;
|
||||
int mouseY;
|
||||
int keyboardLayout;
|
||||
} initial;
|
||||
struct {
|
||||
short key;
|
||||
} keyboardEvent;
|
||||
struct {
|
||||
int dx;
|
||||
int dy;
|
||||
int buttons;
|
||||
} mouseEvent;
|
||||
};
|
||||
} VcrEntry;
|
||||
|
||||
typedef struct LogicalKeyEntry {
|
||||
short field_0;
|
||||
short unmodified;
|
||||
short shift;
|
||||
short lmenu;
|
||||
short rmenu;
|
||||
short ctrl;
|
||||
} LogicalKeyEntry;
|
||||
|
||||
typedef struct KeyboardEvent {
|
||||
int scanCode;
|
||||
unsigned short modifiers;
|
||||
} KeyboardEvent;
|
||||
|
||||
typedef int(PauseHandler)();
|
||||
typedef int(ScreenshotHandler)(int width, int height, unsigned char* buffer, unsigned char* palette);
|
||||
typedef void(VcrPlaybackCompletionCallback)(int reason);
|
||||
|
||||
extern IdleFunc* _idle_func;
|
||||
extern FocusFunc* _focus_func;
|
||||
extern int gKeyboardKeyRepeatRate;
|
||||
extern int gKeyboardKeyRepeatDelay;
|
||||
extern bool _keyboard_hooked;
|
||||
extern unsigned char gMouseDefaultCursor[MOUSE_DEFAULT_CURSOR_SIZE];
|
||||
extern int _mouse_idling;
|
||||
extern unsigned char* gMouseCursorData;
|
||||
extern unsigned char* _mouse_shape;
|
||||
extern unsigned char* _mouse_fptr;
|
||||
extern double gMouseSensitivity;
|
||||
extern unsigned int _ticker_;
|
||||
extern int gMouseButtonsState;
|
||||
|
||||
extern void (*_update_palette_func)();
|
||||
extern bool gMmxEnabled;
|
||||
extern bool gMmxProbed;
|
||||
|
||||
extern unsigned char _kb_installed;
|
||||
extern bool gKeyboardDisabled;
|
||||
extern bool gKeyboardNumpadDisabled;
|
||||
extern bool gKeyboardNumlockDisabled;
|
||||
extern int gKeyboardEventQueueWriteIndex;
|
||||
extern int gKeyboardEventQueueReadIndex;
|
||||
extern short word_51E2E8;
|
||||
extern int gModifierKeysState;
|
||||
extern int (*_kb_scan_to_ascii)();
|
||||
extern VcrEntry* _vcr_buffer;
|
||||
extern int _vcr_buffer_index;
|
||||
extern unsigned int gVcrState;
|
||||
extern unsigned int _vcr_time;
|
||||
extern unsigned int _vcr_counter;
|
||||
extern unsigned int gVcrTerminateFlags;
|
||||
extern int gVcrPlaybackCompletionReason;
|
||||
extern unsigned int _vcr_start_time;
|
||||
extern int _vcr_registered_atexit;
|
||||
extern File* gVcrFile;
|
||||
extern int _vcr_buffer_end;
|
||||
extern VcrPlaybackCompletionCallback* gVcrPlaybackCompletionCallback;
|
||||
extern unsigned int gVcrRequestedTerminationFlags;
|
||||
extern int gVcrOldKeyboardLayout;
|
||||
|
||||
extern int gNormalizedQwertyKeys[SDL_NUM_SCANCODES];
|
||||
extern InputEvent gInputEventQueue[40];
|
||||
extern STRUCT_6ABF50 _GNW95_key_time_stamps[SDL_NUM_SCANCODES];
|
||||
extern int _input_mx;
|
||||
extern int _input_my;
|
||||
extern bool gPaused;
|
||||
extern int gScreenshotKeyCode;
|
||||
extern int _using_msec_timer;
|
||||
extern int gPauseKeyCode;
|
||||
extern ScreenshotHandler* gScreenshotHandler;
|
||||
extern int gInputEventQueueReadIndex;
|
||||
extern unsigned char* gScreenshotBuffer;
|
||||
extern PauseHandler* gPauseHandler;
|
||||
extern int gInputEventQueueWriteIndex;
|
||||
extern bool gRunLoopDisabled;
|
||||
extern TickerListNode* gTickerListHead;
|
||||
extern unsigned int gTickerLastTimestamp;
|
||||
extern bool gCursorIsHidden;
|
||||
extern int _raw_x;
|
||||
extern int gMouseCursorHeight;
|
||||
extern int _raw_y;
|
||||
extern int _raw_buttons;
|
||||
extern int gMouseCursorY;
|
||||
extern int gMouseCursorX;
|
||||
extern int _mouse_disabled;
|
||||
extern int gMouseEvent;
|
||||
extern unsigned int _mouse_speed;
|
||||
extern int _mouse_curr_frame;
|
||||
extern bool gMouseInitialized;
|
||||
extern int gMouseCursorPitch;
|
||||
extern int gMouseCursorWidth;
|
||||
extern int _mouse_num_frames;
|
||||
extern int _mouse_hoty;
|
||||
extern int _mouse_hotx;
|
||||
extern unsigned int _mouse_idle_start_time;
|
||||
extern WindowDrawingProc2* _mouse_blit_trans;
|
||||
extern WINDOWDRAWINGPROC _mouse_blit;
|
||||
extern unsigned char _mouse_trans;
|
||||
extern int gMouseRightButtonDownTimestamp;
|
||||
extern int gMouseLeftButtonDownTimestamp;
|
||||
extern int gMousePreviousEvent;
|
||||
extern unsigned short gSixteenBppPalette[256];
|
||||
extern Rect _scr_size;
|
||||
extern int gRedMask;
|
||||
extern int gGreenMask;
|
||||
extern int gBlueMask;
|
||||
extern int gBlueShift;
|
||||
extern int gRedShift;
|
||||
extern int gGreenShift;
|
||||
extern void (*_scr_blit)(unsigned char* src, int src_pitch, int a3, int src_x, int src_y, int src_width, int src_height, int dest_x, int dest_y);
|
||||
extern void (*_zero_mem)();
|
||||
extern bool gMmxSupported;
|
||||
extern unsigned char gLastVideoModePalette[268];
|
||||
extern KeyboardEvent gKeyboardEventsQueue[KEY_QUEUE_SIZE];
|
||||
extern LogicalKeyEntry gLogicalKeyEntries[SDL_NUM_SCANCODES];
|
||||
extern unsigned char gPressedPhysicalKeys[SDL_NUM_SCANCODES];
|
||||
extern unsigned int _kb_idle_start_time;
|
||||
extern KeyboardEvent gLastKeyboardEvent;
|
||||
extern int gKeyboardLayout;
|
||||
extern unsigned char gPressedPhysicalKeysCount;
|
||||
extern VcrEntry stru_6AD940;
|
||||
|
||||
extern SDL_Window* gSdlWindow;
|
||||
extern SDL_Surface* gSdlSurface;
|
||||
extern SDL_Renderer* gSdlRenderer;
|
||||
extern SDL_Texture* gSdlTexture;
|
||||
extern SDL_Surface* gSdlTextureSurface;
|
||||
|
||||
int coreInit(int a1);
|
||||
void coreExit();
|
||||
int _get_input();
|
||||
void _process_bk();
|
||||
void enqueueInputEvent(int a1);
|
||||
int dequeueInputEvent();
|
||||
void inputEventQueueReset();
|
||||
void tickersExecute();
|
||||
void tickersAdd(TickerProc* fn);
|
||||
void tickersRemove(TickerProc* fn);
|
||||
void tickersEnable();
|
||||
void tickersDisable();
|
||||
void pauseGame();
|
||||
int pauseHandlerDefaultImpl();
|
||||
void pauseHandlerConfigure(int keyCode, PauseHandler* fn);
|
||||
void takeScreenshot();
|
||||
void screenshotBlitter(unsigned char* src, int src_pitch, int a3, int x, int y, int width, int height, int dest_x, int dest_y);
|
||||
int screenshotHandlerDefaultImpl(int width, int height, unsigned char* data, unsigned char* palette);
|
||||
void screenshotHandlerConfigure(int keyCode, ScreenshotHandler* handler);
|
||||
unsigned int _get_time();
|
||||
void coreDelayProcessingEvents(unsigned int ms);
|
||||
void coreDelay(unsigned int ms);
|
||||
unsigned int getTicksSince(unsigned int a1);
|
||||
unsigned int getTicksBetween(unsigned int a1, unsigned int a2);
|
||||
unsigned int _get_bk_time();
|
||||
void inputSetKeyboardKeyRepeatRate(int value);
|
||||
int inputGetKeyboardKeyRepeatRate();
|
||||
void inputSetKeyboardKeyRepeatDelay(int value);
|
||||
int inputGetKeyboardKeyRepeatDelay();
|
||||
void inputSetFocusFunc(FocusFunc* func);
|
||||
FocusFunc* inputGetFocusFunc();
|
||||
void inputSetIdleFunc(IdleFunc* func);
|
||||
IdleFunc* inputGetIdleFunc();
|
||||
void buildNormalizedQwertyKeys();
|
||||
int _GNW95_input_init();
|
||||
void _GNW95_process_message();
|
||||
void _GNW95_clear_time_stamps();
|
||||
void _GNW95_process_key(KeyboardData* data);
|
||||
void _GNW95_lost_focus();
|
||||
int mouseInit();
|
||||
void mouseFree();
|
||||
void mousePrepareDefaultCursor();
|
||||
int mouseSetFrame(unsigned char* a1, int width, int height, int pitch, int a5, int a6, int a7);
|
||||
void _mouse_anim();
|
||||
void mouseShowCursor();
|
||||
void mouseHideCursor();
|
||||
void _mouse_info();
|
||||
void _mouse_simulate_input(int delta_x, int delta_y, int buttons);
|
||||
bool _mouse_in(int left, int top, int right, int bottom);
|
||||
bool _mouse_click_in(int left, int top, int right, int bottom);
|
||||
void mouseGetRect(Rect* rect);
|
||||
void mouseGetPosition(int* out_x, int* out_y);
|
||||
void _mouse_set_position(int a1, int a2);
|
||||
void _mouse_clip();
|
||||
int mouseGetEvent();
|
||||
bool cursorIsHidden();
|
||||
void _mouse_get_raw_state(int* out_x, int* out_y, int* out_buttons);
|
||||
void mouseSetSensitivity(double value);
|
||||
void mmxSetEnabled(bool a1);
|
||||
int _init_mode_320_200();
|
||||
int _init_mode_320_400();
|
||||
int _init_mode_640_480_16();
|
||||
int _init_mode_640_480();
|
||||
int _init_mode_640_400();
|
||||
int _init_mode_800_600();
|
||||
int _init_mode_1024_768();
|
||||
int _init_mode_1280_1024();
|
||||
void _get_start_mode_();
|
||||
void _zero_vid_mem();
|
||||
int _GNW95_init_mode_ex(int width, int height, int bpp);
|
||||
int _init_vesa_mode(int width, int height);
|
||||
int _GNW95_init_window(int width, int height, bool fullscreen);
|
||||
int getShiftForBitMask(int mask);
|
||||
int directDrawInit(int width, int height, int bpp);
|
||||
void directDrawFree();
|
||||
void directDrawSetPaletteInRange(unsigned char* a1, int a2, int a3);
|
||||
void directDrawSetPalette(unsigned char* palette);
|
||||
unsigned char* directDrawGetPalette();
|
||||
void _GNW95_ShowRect(unsigned char* src, int src_pitch, int a3, int src_x, int src_y, int src_width, int src_height, int dest_x, int dest_y);
|
||||
void _GNW95_MouseShowRect16(unsigned char* src, int srcPitch, int a3, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY);
|
||||
void _GNW95_ShowRect16(unsigned char* src, int srcPitch, int a3, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY);
|
||||
void _GNW95_MouseShowTransRect16(unsigned char* src, int srcPitch, int a3, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, unsigned char keyColor);
|
||||
void _GNW95_zero_vid_mem();
|
||||
int keyboardInit();
|
||||
void keyboardFree();
|
||||
void keyboardReset();
|
||||
int _kb_getch();
|
||||
void keyboardDisable();
|
||||
void keyboardEnable();
|
||||
int keyboardIsDisabled();
|
||||
void keyboardSetLayout(int new_language);
|
||||
int keyboardGetLayout();
|
||||
void _kb_simulate_key(KeyboardData* data);
|
||||
int _kb_next_ascii_English_US();
|
||||
int keyboardDequeueLogicalKeyCode();
|
||||
void keyboardBuildQwertyConfiguration();
|
||||
void keyboardBuildFrenchConfiguration();
|
||||
void keyboardBuildGermanConfiguration();
|
||||
void keyboardBuildItalianConfiguration();
|
||||
void keyboardBuildSpanishConfiguration();
|
||||
void _kb_init_lock_status();
|
||||
int keyboardPeekEvent(int index, KeyboardEvent** keyboardEventPtr);
|
||||
bool vcrRecord(const char* fileName);
|
||||
bool vcrPlay(const char* fileName, unsigned int terminationFlags, VcrPlaybackCompletionCallback* callback);
|
||||
void vcrStop();
|
||||
int vcrGetState();
|
||||
int vcrUpdate();
|
||||
bool vcrInitBuffer();
|
||||
bool vcrFreeBuffer();
|
||||
bool vcrClear();
|
||||
bool vcrDump();
|
||||
bool vcrLoad();
|
||||
bool vcrWriteEntry(VcrEntry* ptr, File* stream);
|
||||
bool vcrReadEntry(VcrEntry* ptr, File* stream);
|
||||
|
||||
int screenGetWidth();
|
||||
int screenGetHeight();
|
||||
int screenGetVisibleHeight();
|
||||
void mouseGetPositionInWindow(int win, int* x, int* y);
|
||||
bool mouseHitTestInWindow(int win, int left, int top, int right, int bottom);
|
||||
void mouseGetWheel(int* x, int* y);
|
||||
void convertMouseWheelToArrowKey(int* keyCodePtr);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* CORE_H */
|
||||
+68
-61
@@ -2,26 +2,28 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "art.h"
|
||||
#include "color.h"
|
||||
#include "core.h"
|
||||
#include "cycle.h"
|
||||
#include "db.h"
|
||||
#include "debug.h"
|
||||
#include "draw.h"
|
||||
#include "game_mouse.h"
|
||||
#include "input.h"
|
||||
#include "memory.h"
|
||||
#include "message.h"
|
||||
#include "mouse.h"
|
||||
#include "palette.h"
|
||||
#include "platform_compat.h"
|
||||
#include "sound.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "window_manager.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
#define CREDITS_WINDOW_WIDTH (640)
|
||||
#define CREDITS_WINDOW_HEIGHT (480)
|
||||
#define CREDITS_WINDOW_SCROLLING_DELAY (38)
|
||||
|
||||
static bool creditsFileParseNextLine(char* dest, int* font, int* color);
|
||||
@@ -63,7 +65,7 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle)
|
||||
soundContinueAll();
|
||||
|
||||
char localizedPath[COMPAT_MAX_PATH];
|
||||
if (_message_make_path(localizedPath, filePath)) {
|
||||
if (_message_make_path(localizedPath, sizeof(localizedPath), filePath)) {
|
||||
gCreditsFile = fileOpen(localizedPath, "rt");
|
||||
if (gCreditsFile != NULL) {
|
||||
soundContinueAll();
|
||||
@@ -76,39 +78,35 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle)
|
||||
mouseShowCursor();
|
||||
}
|
||||
|
||||
int creditsWindowX = (screenGetWidth() - CREDITS_WINDOW_WIDTH) / 2;
|
||||
int creditsWindowY = (screenGetHeight() - CREDITS_WINDOW_HEIGHT) / 2;
|
||||
int window = windowCreate(creditsWindowX, creditsWindowY, CREDITS_WINDOW_WIDTH, CREDITS_WINDOW_HEIGHT, _colorTable[0], 20);
|
||||
int windowWidth = screenGetWidth();
|
||||
int windowHeight = screenGetHeight();
|
||||
int window = windowCreate(0, 0, windowWidth, windowHeight, _colorTable[0], 20);
|
||||
soundContinueAll();
|
||||
if (window != -1) {
|
||||
unsigned char* windowBuffer = windowGetBuffer(window);
|
||||
if (windowBuffer != NULL) {
|
||||
unsigned char* backgroundBuffer = (unsigned char*)internal_malloc(CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT);
|
||||
unsigned char* backgroundBuffer = (unsigned char*)internal_malloc(windowWidth * windowHeight);
|
||||
if (backgroundBuffer) {
|
||||
soundContinueAll();
|
||||
|
||||
memset(backgroundBuffer, _colorTable[0], CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT);
|
||||
memset(backgroundBuffer, _colorTable[0], windowWidth * windowHeight);
|
||||
|
||||
if (backgroundFid != -1) {
|
||||
CacheEntry* backgroundFrmHandle;
|
||||
Art* frm = artLock(backgroundFid, &backgroundFrmHandle);
|
||||
if (frm != NULL) {
|
||||
int width = artGetWidth(frm, 0, 0);
|
||||
int height = artGetHeight(frm, 0, 0);
|
||||
unsigned char* backgroundFrmData = artGetFrameData(frm, 0, 0);
|
||||
blitBufferToBuffer(backgroundFrmData,
|
||||
width,
|
||||
height,
|
||||
width,
|
||||
backgroundBuffer + CREDITS_WINDOW_WIDTH * ((CREDITS_WINDOW_HEIGHT - height) / 2) + (CREDITS_WINDOW_WIDTH - width) / 2,
|
||||
CREDITS_WINDOW_WIDTH);
|
||||
artUnlock(backgroundFrmHandle);
|
||||
FrmImage backgroundFrmImage;
|
||||
if (backgroundFrmImage.lock(backgroundFid)) {
|
||||
blitBufferToBuffer(backgroundFrmImage.getData(),
|
||||
backgroundFrmImage.getWidth(),
|
||||
backgroundFrmImage.getHeight(),
|
||||
backgroundFrmImage.getWidth(),
|
||||
backgroundBuffer + windowWidth * ((windowHeight - backgroundFrmImage.getHeight()) / 2) + (windowWidth - backgroundFrmImage.getWidth()) / 2,
|
||||
windowWidth);
|
||||
backgroundFrmImage.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char* intermediateBuffer = (unsigned char*)internal_malloc(CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT);
|
||||
unsigned char* intermediateBuffer = (unsigned char*)internal_malloc(windowWidth * windowHeight);
|
||||
if (intermediateBuffer != NULL) {
|
||||
memset(intermediateBuffer, 0, CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT);
|
||||
memset(intermediateBuffer, 0, windowWidth * windowHeight);
|
||||
|
||||
fontSetCurrent(gCreditsWindowTitleFont);
|
||||
int titleFontLineHeight = fontGetLineHeight();
|
||||
@@ -116,22 +114,21 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle)
|
||||
fontSetCurrent(gCreditsWindowNameFont);
|
||||
int nameFontLineHeight = fontGetLineHeight();
|
||||
|
||||
int lineHeight = nameFontLineHeight + (titleFontLineHeight >= nameFontLineHeight ? titleFontLineHeight - nameFontLineHeight : 0);
|
||||
int stringBufferSize = CREDITS_WINDOW_WIDTH * lineHeight;
|
||||
int lineHeight = std::max(titleFontLineHeight, nameFontLineHeight);
|
||||
int stringBufferSize = windowWidth * lineHeight;
|
||||
unsigned char* stringBuffer = (unsigned char*)internal_malloc(stringBufferSize);
|
||||
if (stringBuffer != NULL) {
|
||||
blitBufferToBuffer(backgroundBuffer,
|
||||
CREDITS_WINDOW_WIDTH,
|
||||
CREDITS_WINDOW_HEIGHT,
|
||||
CREDITS_WINDOW_WIDTH,
|
||||
windowWidth,
|
||||
windowHeight,
|
||||
windowWidth,
|
||||
windowBuffer,
|
||||
CREDITS_WINDOW_WIDTH);
|
||||
windowWidth);
|
||||
|
||||
windowRefresh(window);
|
||||
|
||||
paletteFadeTo(_cmap);
|
||||
|
||||
unsigned char* v40 = intermediateBuffer + CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT - CREDITS_WINDOW_WIDTH;
|
||||
char str[260];
|
||||
int font;
|
||||
int color;
|
||||
@@ -140,47 +137,52 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle)
|
||||
while (creditsFileParseNextLine(str, &font, &color)) {
|
||||
fontSetCurrent(font);
|
||||
|
||||
int v19 = fontGetStringWidth(str);
|
||||
if (v19 >= CREDITS_WINDOW_WIDTH) {
|
||||
int stringWidth = fontGetStringWidth(str);
|
||||
if (stringWidth >= windowWidth) {
|
||||
continue;
|
||||
}
|
||||
|
||||
memset(stringBuffer, 0, stringBufferSize);
|
||||
fontDrawText(stringBuffer, str, CREDITS_WINDOW_WIDTH, CREDITS_WINDOW_WIDTH, color);
|
||||
fontDrawText(stringBuffer, str, windowWidth, windowWidth, color);
|
||||
|
||||
unsigned char* dest = intermediateBuffer + CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT - CREDITS_WINDOW_WIDTH + (CREDITS_WINDOW_WIDTH - v19) / 2;
|
||||
unsigned char* dest = intermediateBuffer + windowWidth * windowHeight - windowWidth + (windowWidth - stringWidth) / 2;
|
||||
unsigned char* src = stringBuffer;
|
||||
for (int index = 0; index < lineHeight; index++) {
|
||||
if (_get_input() != -1) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
if (inputGetInput() != -1) {
|
||||
stop = true;
|
||||
break;
|
||||
}
|
||||
|
||||
memmove(intermediateBuffer, intermediateBuffer + CREDITS_WINDOW_WIDTH, CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT - CREDITS_WINDOW_WIDTH);
|
||||
memcpy(dest, src, v19);
|
||||
memmove(intermediateBuffer, intermediateBuffer + windowWidth, windowWidth * windowHeight - windowWidth);
|
||||
memcpy(dest, src, stringWidth);
|
||||
|
||||
blitBufferToBuffer(backgroundBuffer,
|
||||
CREDITS_WINDOW_WIDTH,
|
||||
CREDITS_WINDOW_HEIGHT,
|
||||
CREDITS_WINDOW_WIDTH,
|
||||
windowWidth,
|
||||
windowHeight,
|
||||
windowWidth,
|
||||
windowBuffer,
|
||||
CREDITS_WINDOW_WIDTH);
|
||||
windowWidth);
|
||||
|
||||
blitBufferToBufferTrans(intermediateBuffer,
|
||||
CREDITS_WINDOW_WIDTH,
|
||||
CREDITS_WINDOW_HEIGHT,
|
||||
CREDITS_WINDOW_WIDTH,
|
||||
windowWidth,
|
||||
windowHeight,
|
||||
windowWidth,
|
||||
windowBuffer,
|
||||
CREDITS_WINDOW_WIDTH);
|
||||
windowWidth);
|
||||
|
||||
while (getTicksSince(tick) < CREDITS_WINDOW_SCROLLING_DELAY) {
|
||||
}
|
||||
|
||||
tick = _get_time();
|
||||
tick = getTicks();
|
||||
|
||||
windowRefresh(window);
|
||||
|
||||
src += CREDITS_WINDOW_WIDTH;
|
||||
src += windowWidth;
|
||||
|
||||
sharedFpsLimiter.throttle();
|
||||
renderPresent();
|
||||
}
|
||||
|
||||
if (stop) {
|
||||
@@ -189,34 +191,39 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle)
|
||||
}
|
||||
|
||||
if (!stop) {
|
||||
for (int index = 0; index < CREDITS_WINDOW_HEIGHT; index++) {
|
||||
if (_get_input() != -1) {
|
||||
for (int index = 0; index < windowHeight; index++) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
if (inputGetInput() != -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
memmove(intermediateBuffer, intermediateBuffer + CREDITS_WINDOW_WIDTH, CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT - CREDITS_WINDOW_WIDTH);
|
||||
memset(intermediateBuffer + CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT - CREDITS_WINDOW_WIDTH, 0, CREDITS_WINDOW_WIDTH);
|
||||
memmove(intermediateBuffer, intermediateBuffer + windowWidth, windowWidth * windowHeight - windowWidth);
|
||||
memset(intermediateBuffer + windowWidth * windowHeight - windowWidth, 0, windowWidth);
|
||||
|
||||
blitBufferToBuffer(backgroundBuffer,
|
||||
CREDITS_WINDOW_WIDTH,
|
||||
CREDITS_WINDOW_HEIGHT,
|
||||
CREDITS_WINDOW_WIDTH,
|
||||
windowWidth,
|
||||
windowHeight,
|
||||
windowWidth,
|
||||
windowBuffer,
|
||||
CREDITS_WINDOW_WIDTH);
|
||||
windowWidth);
|
||||
|
||||
blitBufferToBufferTrans(intermediateBuffer,
|
||||
CREDITS_WINDOW_WIDTH,
|
||||
CREDITS_WINDOW_HEIGHT,
|
||||
CREDITS_WINDOW_WIDTH,
|
||||
windowWidth,
|
||||
windowHeight,
|
||||
windowWidth,
|
||||
windowBuffer,
|
||||
CREDITS_WINDOW_WIDTH);
|
||||
windowWidth);
|
||||
|
||||
while (getTicksSince(tick) < CREDITS_WINDOW_SCROLLING_DELAY) {
|
||||
}
|
||||
|
||||
tick = _get_time();
|
||||
tick = getTicks();
|
||||
|
||||
windowRefresh(window);
|
||||
|
||||
sharedFpsLimiter.throttle();
|
||||
renderPresent();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-8
@@ -171,13 +171,15 @@ int critterInit()
|
||||
}
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "%sscrname.msg", asc_5186C8);
|
||||
snprintf(path, sizeof(path), "%sscrname.msg", asc_5186C8);
|
||||
|
||||
if (!messageListLoad(&gCritterMessageList, path)) {
|
||||
debugPrint("\nError: Loading critter name message file!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SCRNAME, &gCritterMessageList);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -193,6 +195,7 @@ void critterReset()
|
||||
// 0x42D004
|
||||
void critterExit()
|
||||
{
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SCRNAME, nullptr);
|
||||
messageListFree(&gCritterMessageList);
|
||||
}
|
||||
|
||||
@@ -422,7 +425,7 @@ int critterAdjustRadiation(Object* obj, int amount)
|
||||
}
|
||||
|
||||
if (amount > 0) {
|
||||
proto->critter.data.flags |= CRITTER_FLAG_0x2;
|
||||
proto->critter.data.flags |= CRITTER_RADIATED;
|
||||
}
|
||||
|
||||
if (amount > 0) {
|
||||
@@ -489,7 +492,7 @@ int _critter_check_rads(Object* obj)
|
||||
|
||||
Proto* proto;
|
||||
protoGetProto(obj->pid, &proto);
|
||||
if ((proto->critter.data.flags & CRITTER_FLAG_0x2) == 0) {
|
||||
if ((proto->critter.data.flags & CRITTER_RADIATED) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -530,7 +533,7 @@ int _critter_check_rads(Object* obj)
|
||||
queueAddEvent(GAME_TIME_TICKS_PER_HOUR * randomBetween(4, 18), obj, radiationEvent, EVENT_TYPE_RADIATION);
|
||||
}
|
||||
|
||||
proto->critter.data.flags &= ~(CRITTER_FLAG_0x2);
|
||||
proto->critter.data.flags &= ~CRITTER_RADIATED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -876,7 +879,7 @@ void critterKill(Object* critter, int anim, bool a3)
|
||||
rectUnion(&updatedRect, &tempRect, &updatedRect);
|
||||
}
|
||||
|
||||
if (!_critter_flag_check(critter->pid, CRITTER_FLAG_0x800)) {
|
||||
if (!_critter_flag_check(critter->pid, CRITTER_FLAT)) {
|
||||
critter->flags |= OBJECT_NO_BLOCK;
|
||||
_obj_toggle_flat(critter, &tempRect);
|
||||
}
|
||||
@@ -993,8 +996,8 @@ bool _critter_is_prone(Object* critter)
|
||||
int anim = FID_ANIM_TYPE(critter->fid);
|
||||
|
||||
return (critter->data.critter.combat.results & (DAM_KNOCKED_OUT | DAM_KNOCKED_DOWN)) != 0
|
||||
|| (anim >= FIRST_KNOCKDOWN_AND_DEATH_ANIM && anim <= LAST_KNOCKDOWN_AND_DEATH_ANIM)
|
||||
|| (anim >= FIRST_SF_DEATH_ANIM && anim <= LAST_SF_DEATH_ANIM);
|
||||
|| (anim >= FIRST_KNOCKDOWN_AND_DEATH_ANIM && anim <= LAST_KNOCKDOWN_AND_DEATH_ANIM)
|
||||
|| (anim >= FIRST_SF_DEATH_ANIM && anim <= LAST_SF_DEATH_ANIM);
|
||||
}
|
||||
|
||||
// critter_body_type
|
||||
@@ -1251,7 +1254,7 @@ int knockoutEventProcess(Object* obj, void* data)
|
||||
obj->data.critter.combat.results |= DAM_KNOCKED_DOWN;
|
||||
|
||||
if (isInCombat()) {
|
||||
obj->data.critter.combat.maneuver |= CRITTER_MANEUVER_0x01;
|
||||
obj->data.critter.combat.maneuver |= CRITTER_MANEUVER_ENGAGING;
|
||||
} else {
|
||||
_dude_standup(obj);
|
||||
}
|
||||
|
||||
+97
-106
@@ -1,16 +1,17 @@
|
||||
#include "cycle.h"
|
||||
|
||||
#include "color.h"
|
||||
#include "core.h"
|
||||
#include "game_config.h"
|
||||
#include "input.h"
|
||||
#include "palette.h"
|
||||
#include "settings.h"
|
||||
#include "svga.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
#define COLOR_CYCLE_PERIOD_1 (200U)
|
||||
#define COLOR_CYCLE_PERIOD_2 (142U)
|
||||
#define COLOR_CYCLE_PERIOD_3 (100U)
|
||||
#define COLOR_CYCLE_PERIOD_4 (33U)
|
||||
static constexpr unsigned int kSlowCyclePeriod = 1000 / 5;
|
||||
static constexpr unsigned int kMediumCyclePeriod = 1000 / 7;
|
||||
static constexpr unsigned int kFastCyclePeriod = 1000 / 10;
|
||||
static constexpr unsigned int kVeryFastCyclePeriod = 1000 / 30;
|
||||
|
||||
// 0x51843C
|
||||
static int gColorCycleSpeedFactor = 1;
|
||||
@@ -21,7 +22,7 @@ static int gColorCycleSpeedFactor = 1;
|
||||
// Green.
|
||||
//
|
||||
// 0x518440
|
||||
static unsigned char _slime[12] = {
|
||||
static unsigned char slime[12] = {
|
||||
0, 108, 0,
|
||||
11, 115, 7,
|
||||
27, 123, 15,
|
||||
@@ -31,7 +32,7 @@ static unsigned char _slime[12] = {
|
||||
// Light gray?
|
||||
//
|
||||
// 0x51844C
|
||||
static unsigned char _shoreline[18] = {
|
||||
static unsigned char shoreline[18] = {
|
||||
83, 63, 43,
|
||||
75, 59, 43,
|
||||
67, 55, 39,
|
||||
@@ -43,7 +44,7 @@ static unsigned char _shoreline[18] = {
|
||||
// Orange.
|
||||
//
|
||||
// 0x51845E
|
||||
static unsigned char _fire_slow[15] = {
|
||||
static unsigned char fire_slow[15] = {
|
||||
255, 0, 0,
|
||||
215, 0, 0,
|
||||
147, 43, 11,
|
||||
@@ -54,7 +55,7 @@ static unsigned char _fire_slow[15] = {
|
||||
// Red.
|
||||
//
|
||||
// 0x51846D
|
||||
static unsigned char _fire_fast[15] = {
|
||||
static unsigned char fire_fast[15] = {
|
||||
71, 0, 0,
|
||||
123, 0, 0,
|
||||
179, 0, 0,
|
||||
@@ -65,7 +66,7 @@ static unsigned char _fire_fast[15] = {
|
||||
// Light blue.
|
||||
//
|
||||
// 0x51847C
|
||||
static unsigned char _monitors[15] = {
|
||||
static unsigned char monitors[15] = {
|
||||
107, 107, 111,
|
||||
99, 103, 127,
|
||||
87, 107, 143,
|
||||
@@ -81,38 +82,17 @@ static bool gColorCycleInitialized = false;
|
||||
// 0x518490
|
||||
static bool gColorCycleEnabled = false;
|
||||
|
||||
// 0x518494
|
||||
static int _slime_start = 0;
|
||||
|
||||
// 0x518498
|
||||
static int _shoreline_start = 0;
|
||||
|
||||
// 0x51849C
|
||||
static int _fire_slow_start = 0;
|
||||
|
||||
// 0x5184A0
|
||||
static int _fire_fast_start = 0;
|
||||
|
||||
// 0x5184A4
|
||||
static int _monitors_start = 0;
|
||||
|
||||
// 0x5184A8
|
||||
static unsigned char _bobber_red = 0;
|
||||
|
||||
// 0x5184A9
|
||||
static signed char _bobber_diff = -4;
|
||||
|
||||
// 0x56D7D0
|
||||
static unsigned int gColorCycleTimestamp3;
|
||||
static unsigned int last_cycle_fast;
|
||||
|
||||
// 0x56D7D4
|
||||
static unsigned int gColorCycleTimestamp1;
|
||||
static unsigned int last_cycle_slow;
|
||||
|
||||
// 0x56D7D8
|
||||
static unsigned int gColorCycleTimestamp2;
|
||||
static unsigned int last_cycle_medium;
|
||||
|
||||
// 0x56D7DC
|
||||
static unsigned int gColorCycleTimestamp4;
|
||||
static unsigned int last_cycle_very_fast;
|
||||
|
||||
// 0x42E780
|
||||
void colorCycleInit()
|
||||
@@ -121,33 +101,28 @@ void colorCycleInit()
|
||||
return;
|
||||
}
|
||||
|
||||
bool colorCycling;
|
||||
if (!configGetBool(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_COLOR_CYCLING_KEY, &colorCycling)) {
|
||||
colorCycling = true;
|
||||
}
|
||||
|
||||
if (!colorCycling) {
|
||||
if (!settings.system.color_cycling) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int index = 0; index < 12; index++) {
|
||||
_slime[index] >>= 2;
|
||||
slime[index] >>= 2;
|
||||
}
|
||||
|
||||
for (int index = 0; index < 18; index++) {
|
||||
_shoreline[index] >>= 2;
|
||||
shoreline[index] >>= 2;
|
||||
}
|
||||
|
||||
for (int index = 0; index < 15; index++) {
|
||||
_fire_slow[index] >>= 2;
|
||||
fire_slow[index] >>= 2;
|
||||
}
|
||||
|
||||
for (int index = 0; index < 15; index++) {
|
||||
_fire_fast[index] >>= 2;
|
||||
fire_fast[index] >>= 2;
|
||||
}
|
||||
|
||||
for (int index = 0; index < 15; index++) {
|
||||
_monitors[index] >>= 2;
|
||||
monitors[index] >>= 2;
|
||||
}
|
||||
|
||||
tickersAdd(colorCycleTicker);
|
||||
@@ -155,22 +130,17 @@ void colorCycleInit()
|
||||
gColorCycleInitialized = true;
|
||||
gColorCycleEnabled = true;
|
||||
|
||||
int cycleSpeedFactor;
|
||||
if (!configGetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CYCLE_SPEED_FACTOR_KEY, &cycleSpeedFactor)) {
|
||||
cycleSpeedFactor = 1;
|
||||
}
|
||||
|
||||
cycleSetSpeedFactor(cycleSpeedFactor);
|
||||
cycleSetSpeedFactor(settings.system.cycle_speed_factor);
|
||||
}
|
||||
|
||||
// 0x42E8CC
|
||||
void colorCycleReset()
|
||||
{
|
||||
if (gColorCycleInitialized) {
|
||||
gColorCycleTimestamp1 = 0;
|
||||
gColorCycleTimestamp2 = 0;
|
||||
gColorCycleTimestamp3 = 0;
|
||||
gColorCycleTimestamp4 = 0;
|
||||
last_cycle_slow = 0;
|
||||
last_cycle_medium = 0;
|
||||
last_cycle_fast = 0;
|
||||
last_cycle_very_fast = 0;
|
||||
tickersAdd(colorCycleTicker);
|
||||
gColorCycleEnabled = true;
|
||||
}
|
||||
@@ -208,12 +178,33 @@ bool colorCycleEnabled()
|
||||
void cycleSetSpeedFactor(int value)
|
||||
{
|
||||
gColorCycleSpeedFactor = value;
|
||||
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CYCLE_SPEED_FACTOR_KEY, value);
|
||||
settings.system.cycle_speed_factor = value;
|
||||
}
|
||||
|
||||
// 0x42E97C
|
||||
void colorCycleTicker()
|
||||
{
|
||||
// 0x518494
|
||||
static int slime_start = 0;
|
||||
|
||||
// 0x518498
|
||||
static int shoreline_start = 0;
|
||||
|
||||
// 0x51849C
|
||||
static int fire_slow_start = 0;
|
||||
|
||||
// 0x5184A0
|
||||
static int fire_fast_start = 0;
|
||||
|
||||
// 0x5184A4
|
||||
static int monitors_start = 0;
|
||||
|
||||
// 0x5184A8
|
||||
static unsigned char bobber_red = 0;
|
||||
|
||||
// 0x5184A9
|
||||
static signed char bobber_diff = -4;
|
||||
|
||||
if (!gColorCycleEnabled) {
|
||||
return;
|
||||
}
|
||||
@@ -221,111 +212,111 @@ void colorCycleTicker()
|
||||
bool changed = false;
|
||||
|
||||
unsigned char* palette = _getSystemPalette();
|
||||
unsigned int time = _get_time();
|
||||
unsigned int time = getTicks();
|
||||
|
||||
if (getTicksBetween(time, gColorCycleTimestamp1) >= COLOR_CYCLE_PERIOD_1 * gColorCycleSpeedFactor) {
|
||||
if (getTicksBetween(time, last_cycle_slow) >= kSlowCyclePeriod * gColorCycleSpeedFactor) {
|
||||
changed = true;
|
||||
gColorCycleTimestamp1 = time;
|
||||
last_cycle_slow = time;
|
||||
|
||||
int paletteIndex = 229 * 3;
|
||||
|
||||
for (int index = _slime_start; index < 12; index++) {
|
||||
palette[paletteIndex++] = _slime[index];
|
||||
for (int index = slime_start; index < 12; index++) {
|
||||
palette[paletteIndex++] = slime[index];
|
||||
}
|
||||
|
||||
for (int index = 0; index < _slime_start; index++) {
|
||||
palette[paletteIndex++] = _slime[index];
|
||||
for (int index = 0; index < slime_start; index++) {
|
||||
palette[paletteIndex++] = slime[index];
|
||||
}
|
||||
|
||||
_slime_start -= 3;
|
||||
if (_slime_start < 0) {
|
||||
_slime_start = 9;
|
||||
slime_start -= 3;
|
||||
if (slime_start < 0) {
|
||||
slime_start = 9;
|
||||
}
|
||||
|
||||
paletteIndex = 248 * 3;
|
||||
|
||||
for (int index = _shoreline_start; index < 18; index++) {
|
||||
palette[paletteIndex++] = _shoreline[index];
|
||||
for (int index = shoreline_start; index < 18; index++) {
|
||||
palette[paletteIndex++] = shoreline[index];
|
||||
}
|
||||
|
||||
for (int index = 0; index < _shoreline_start; index++) {
|
||||
palette[paletteIndex++] = _shoreline[index];
|
||||
for (int index = 0; index < shoreline_start; index++) {
|
||||
palette[paletteIndex++] = shoreline[index];
|
||||
}
|
||||
|
||||
_shoreline_start -= 3;
|
||||
if (_shoreline_start < 0) {
|
||||
_shoreline_start = 15;
|
||||
shoreline_start -= 3;
|
||||
if (shoreline_start < 0) {
|
||||
shoreline_start = 15;
|
||||
}
|
||||
|
||||
paletteIndex = 238 * 3;
|
||||
|
||||
for (int index = _fire_slow_start; index < 15; index++) {
|
||||
palette[paletteIndex++] = _fire_slow[index];
|
||||
for (int index = fire_slow_start; index < 15; index++) {
|
||||
palette[paletteIndex++] = fire_slow[index];
|
||||
}
|
||||
|
||||
for (int index = 0; index < _fire_slow_start; index++) {
|
||||
palette[paletteIndex++] = _fire_slow[index];
|
||||
for (int index = 0; index < fire_slow_start; index++) {
|
||||
palette[paletteIndex++] = fire_slow[index];
|
||||
}
|
||||
|
||||
_fire_slow_start -= 3;
|
||||
if (_fire_slow_start < 0) {
|
||||
_fire_slow_start = 12;
|
||||
fire_slow_start -= 3;
|
||||
if (fire_slow_start < 0) {
|
||||
fire_slow_start = 12;
|
||||
}
|
||||
}
|
||||
|
||||
if (getTicksBetween(time, gColorCycleTimestamp2) >= COLOR_CYCLE_PERIOD_2 * gColorCycleSpeedFactor) {
|
||||
if (getTicksBetween(time, last_cycle_medium) >= kMediumCyclePeriod * gColorCycleSpeedFactor) {
|
||||
changed = true;
|
||||
gColorCycleTimestamp2 = time;
|
||||
last_cycle_medium = time;
|
||||
|
||||
int paletteIndex = 243 * 3;
|
||||
|
||||
for (int index = _fire_fast_start; index < 15; index++) {
|
||||
palette[paletteIndex++] = _fire_fast[index];
|
||||
for (int index = fire_fast_start; index < 15; index++) {
|
||||
palette[paletteIndex++] = fire_fast[index];
|
||||
}
|
||||
|
||||
for (int index = 0; index < _fire_fast_start; index++) {
|
||||
palette[paletteIndex++] = _fire_fast[index];
|
||||
for (int index = 0; index < fire_fast_start; index++) {
|
||||
palette[paletteIndex++] = fire_fast[index];
|
||||
}
|
||||
|
||||
_fire_fast_start -= 3;
|
||||
if (_fire_fast_start < 0) {
|
||||
_fire_fast_start = 12;
|
||||
fire_fast_start -= 3;
|
||||
if (fire_fast_start < 0) {
|
||||
fire_fast_start = 12;
|
||||
}
|
||||
}
|
||||
|
||||
if (getTicksBetween(time, gColorCycleTimestamp3) >= COLOR_CYCLE_PERIOD_3 * gColorCycleSpeedFactor) {
|
||||
if (getTicksBetween(time, last_cycle_fast) >= kFastCyclePeriod * gColorCycleSpeedFactor) {
|
||||
changed = true;
|
||||
gColorCycleTimestamp3 = time;
|
||||
last_cycle_fast = time;
|
||||
|
||||
int paletteIndex = 233 * 3;
|
||||
|
||||
for (int index = _monitors_start; index < 15; index++) {
|
||||
palette[paletteIndex++] = _monitors[index];
|
||||
for (int index = monitors_start; index < 15; index++) {
|
||||
palette[paletteIndex++] = monitors[index];
|
||||
}
|
||||
|
||||
for (int index = 0; index < _monitors_start; index++) {
|
||||
palette[paletteIndex++] = _monitors[index];
|
||||
for (int index = 0; index < monitors_start; index++) {
|
||||
palette[paletteIndex++] = monitors[index];
|
||||
}
|
||||
|
||||
_monitors_start -= 3;
|
||||
monitors_start -= 3;
|
||||
|
||||
if (_monitors_start < 0) {
|
||||
_monitors_start = 12;
|
||||
if (monitors_start < 0) {
|
||||
monitors_start = 12;
|
||||
}
|
||||
}
|
||||
|
||||
if (getTicksBetween(time, gColorCycleTimestamp4) >= COLOR_CYCLE_PERIOD_4 * gColorCycleSpeedFactor) {
|
||||
if (getTicksBetween(time, last_cycle_very_fast) >= kVeryFastCyclePeriod * gColorCycleSpeedFactor) {
|
||||
changed = true;
|
||||
gColorCycleTimestamp4 = time;
|
||||
last_cycle_very_fast = time;
|
||||
|
||||
if (_bobber_red == 0 || _bobber_red == 60) {
|
||||
_bobber_diff = -_bobber_diff;
|
||||
if (bobber_red == 0 || bobber_red == 60) {
|
||||
bobber_diff = -bobber_diff;
|
||||
}
|
||||
|
||||
_bobber_red += _bobber_diff;
|
||||
bobber_red += bobber_diff;
|
||||
|
||||
int paletteIndex = 254 * 3;
|
||||
palette[paletteIndex++] = _bobber_red;
|
||||
palette[paletteIndex++] = bobber_red;
|
||||
palette[paletteIndex++] = 0;
|
||||
palette[paletteIndex++] = 0;
|
||||
}
|
||||
|
||||
@@ -63,18 +63,6 @@ int dbOpen(const char* filePath1, int a2, const char* filePath2, int a4)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x4C5D54
|
||||
int _db_select(int dbHandle)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NOTE: Uncollapsed 0x4C5D54.
|
||||
int _db_current()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x4C5D58
|
||||
int _db_total()
|
||||
{
|
||||
@@ -338,7 +326,7 @@ int fileReadInt32(File* stream, int* valuePtr)
|
||||
return -1;
|
||||
}
|
||||
|
||||
*valuePtr = ((value >> 24) & 0xFF) | ((value >> 8) & 0xFF00) | ((value << 8) & 0xFF0000) | ((value << 24) & 0xFF000000);
|
||||
*valuePtr = ((value & 0xFF000000) >> 24) | ((value & 0xFF0000) >> 8) | ((value & 0xFF00) << 8) | ((value & 0xFF) << 24);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -511,7 +499,7 @@ int fileReadInt32List(File* stream, int* arr, int count)
|
||||
|
||||
for (int index = 0; index < count; index++) {
|
||||
int value = arr[index];
|
||||
arr[index] = ((value >> 24) & 0xFF) | ((value >> 8) & 0xFF00) | ((value << 8) & 0xFF0000) | ((value << 24) & 0xFF000000);
|
||||
arr[index] = ((value & 0xFF000000) >> 24) | ((value & 0xFF0000) >> 8) | ((value & 0xFF00) << 8) | ((value & 0xFF) << 24);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -645,17 +633,18 @@ int fileNameListInit(const char* pattern, char*** fileNameListPtr, int a3, int a
|
||||
bool isWildcard = *pattern == '*';
|
||||
|
||||
for (int index = 0; index < fileNamesLength; index += 1) {
|
||||
const char* name = xlist->fileNames[index];
|
||||
char* name = xlist->fileNames[index];
|
||||
char dir[COMPAT_MAX_DIR];
|
||||
char fileName[COMPAT_MAX_FNAME];
|
||||
char extension[COMPAT_MAX_EXT];
|
||||
compat_windows_path_to_native(name);
|
||||
compat_splitpath(name, NULL, dir, fileName, extension);
|
||||
|
||||
if (!isWildcard || *dir == '\0' || strchr(dir, '\\') == NULL) {
|
||||
if (!isWildcard || *dir == '\0' || (strchr(dir, '\\') == NULL && strchr(dir, '/') == NULL)) {
|
||||
// NOTE: Quick and dirty fix to buffer overflow. See RE to
|
||||
// understand the problem.
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "%s%s", fileName, extension);
|
||||
snprintf(path, sizeof(path), "%s%s", fileName, extension);
|
||||
free(xlist->fileNames[length]);
|
||||
xlist->fileNames[length] = compat_strdup(path);
|
||||
length++;
|
||||
@@ -699,14 +688,6 @@ void fileNameListFree(char*** fileNameListPtr, int a2)
|
||||
free(currentFileList);
|
||||
}
|
||||
|
||||
// NOTE: This function does nothing. It was probably used to set memory procs
|
||||
// for building file name list.
|
||||
//
|
||||
// 0x4C68B8
|
||||
void _db_register_mem(MallocProc* mallocProc, StrdupProc* strdupProc, FreeProc* freeProc)
|
||||
{
|
||||
}
|
||||
|
||||
// TODO: Return type should be long.
|
||||
//
|
||||
// 0x4C68BC
|
||||
@@ -727,14 +708,6 @@ void fileSetReadProgressHandler(FileReadProgressHandler* handler, int size)
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: This function is called when fallout2.cfg has "hashing" enabled, but
|
||||
// it does nothing. It's impossible to guess it's name.
|
||||
//
|
||||
// 0x4C68E4
|
||||
void _db_enable_hash_table_()
|
||||
{
|
||||
}
|
||||
|
||||
// 0x4C68E8
|
||||
int _db_list_compare(const void* p1, const void* p2)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "memory_defs.h"
|
||||
#include "xfile.h"
|
||||
|
||||
namespace fallout {
|
||||
@@ -13,8 +12,6 @@ typedef void FileReadProgressHandler();
|
||||
typedef char* StrdupProc(const char* string);
|
||||
|
||||
int dbOpen(const char* filePath1, int a2, const char* filePath2, int a4);
|
||||
int _db_select(int dbHandle);
|
||||
int _db_current();
|
||||
int _db_total();
|
||||
void dbExit();
|
||||
int dbGetFileSize(const char* filePath, int* sizePtr);
|
||||
@@ -63,10 +60,8 @@ int _db_fwriteLongCount(File* stream, int* arr, int count);
|
||||
int fileWriteUInt32List(File* stream, unsigned int* arr, int count);
|
||||
int fileNameListInit(const char* pattern, char*** fileNames, int a3, int a4);
|
||||
void fileNameListFree(char*** fileNames, int a2);
|
||||
void _db_register_mem(MallocProc* mallocProc, StrdupProc* strdupProc, FreeProc* freeProc);
|
||||
int fileGetSize(File* stream);
|
||||
void fileSetReadProgressHandler(FileReadProgressHandler* handler, int size);
|
||||
void _db_enable_hash_table_();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
||||
+102
-50
@@ -8,13 +8,16 @@
|
||||
#include "art.h"
|
||||
#include "character_editor.h"
|
||||
#include "color.h"
|
||||
#include "core.h"
|
||||
#include "debug.h"
|
||||
#include "draw.h"
|
||||
#include "game.h"
|
||||
#include "game_sound.h"
|
||||
#include "input.h"
|
||||
#include "kb.h"
|
||||
#include "message.h"
|
||||
#include "mouse.h"
|
||||
#include "platform_compat.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "window_manager.h"
|
||||
#include "word_wrap.h"
|
||||
@@ -175,7 +178,7 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
|
||||
|
||||
int linesCount = 0;
|
||||
for (int index = 0; index < bodyLength; index++) {
|
||||
// NOTE: Calls [fontGetStringWidth] twice because of [max] macro.
|
||||
// NOTE: Originally there is no `max` macro.
|
||||
maximumLineWidth = std::max(fontGetStringWidth(body[index]), maximumLineWidth);
|
||||
linesCount++;
|
||||
}
|
||||
@@ -203,14 +206,14 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
|
||||
}
|
||||
|
||||
// Maintain original position in original resolution, otherwise center it.
|
||||
if (screenGetWidth() != 640) x = (screenGetWidth() - backgroundFrmImage.getWidth()) / 2;
|
||||
if (screenGetHeight() != 480) y = (screenGetHeight() - backgroundFrmImage.getHeight()) / 2;
|
||||
x += (screenGetWidth() - 640) / 2;
|
||||
y += (screenGetHeight() - 480) / 2;
|
||||
int win = windowCreate(x,
|
||||
y,
|
||||
backgroundFrmImage.getWidth(),
|
||||
backgroundFrmImage.getHeight(),
|
||||
256,
|
||||
WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
|
||||
WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
|
||||
if (win == -1) {
|
||||
fontSetCurrent(savedFont);
|
||||
return -1;
|
||||
@@ -262,7 +265,7 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
|
||||
}
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "%s%s", asc_5186C8, "DBOX.MSG");
|
||||
snprintf(path, sizeof(path), "%s%s", asc_5186C8, "DBOX.MSG");
|
||||
|
||||
if (!messageListLoad(&messageList, path)) {
|
||||
fontSetCurrent(savedFont);
|
||||
@@ -369,7 +372,7 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
|
||||
}
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "%s%s", asc_5186C8, "DBOX.MSG");
|
||||
snprintf(path, sizeof(path), "%s%s", asc_5186C8, "DBOX.MSG");
|
||||
|
||||
if (!messageListLoad(&messageList, path)) {
|
||||
fontSetCurrent(savedFont);
|
||||
@@ -412,81 +415,107 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
|
||||
|
||||
fontSetCurrent(101);
|
||||
|
||||
int v23 = _ytable[dialogType];
|
||||
int nextY = _ytable[dialogType];
|
||||
int maxY = _ytable[dialogType] + _dblines[dialogType] * fontGetLineHeight();
|
||||
|
||||
if ((flags & DIALOG_BOX_NO_VERTICAL_CENTERING) == 0) {
|
||||
int v41 = _dblines[dialogType] * fontGetLineHeight() / 2 + v23;
|
||||
v23 = v41 - ((bodyLength + 1) * fontGetLineHeight() / 2);
|
||||
int numberOfLines = 0;
|
||||
|
||||
if (hasTitle) {
|
||||
numberOfLines++;
|
||||
}
|
||||
|
||||
for (int index = 0; index < bodyLength; index++) {
|
||||
short beginnings[WORD_WRAP_MAX_COUNT];
|
||||
short subLineCount;
|
||||
int maxWidth = backgroundFrmImage.getWidth() - _xtable[dialogType] * 2;
|
||||
if (wordWrap(body[index], maxWidth, beginnings, &subLineCount) == 0) {
|
||||
numberOfLines += subLineCount - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (numberOfLines > _dblines[dialogType]) {
|
||||
numberOfLines = _dblines[dialogType];
|
||||
}
|
||||
|
||||
nextY += (_dblines[dialogType] - numberOfLines) * fontGetLineHeight() / 2;
|
||||
}
|
||||
|
||||
if (hasTitle) {
|
||||
if ((flags & DIALOG_BOX_NO_HORIZONTAL_CENTERING) != 0) {
|
||||
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * v23 + _xtable[dialogType],
|
||||
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * nextY + _xtable[dialogType],
|
||||
title,
|
||||
backgroundFrmImage.getWidth(),
|
||||
backgroundFrmImage.getWidth(),
|
||||
titleColor);
|
||||
} else {
|
||||
int length = fontGetStringWidth(title);
|
||||
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * v23 + (backgroundFrmImage.getWidth() - length) / 2,
|
||||
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * nextY + (backgroundFrmImage.getWidth() - length) / 2,
|
||||
title,
|
||||
backgroundFrmImage.getWidth(),
|
||||
backgroundFrmImage.getWidth(),
|
||||
titleColor);
|
||||
}
|
||||
v23 += fontGetLineHeight();
|
||||
nextY += fontGetLineHeight();
|
||||
}
|
||||
|
||||
for (int v94 = 0; v94 < bodyLength; v94++) {
|
||||
int len = fontGetStringWidth(body[v94]);
|
||||
if (len <= backgroundFrmImage.getWidth() - 26) {
|
||||
for (int index = 0; index < bodyLength && nextY < maxY; index++) {
|
||||
int width = fontGetStringWidth(body[index]);
|
||||
int maxWidth = backgroundFrmImage.getWidth() - _xtable[dialogType] * 2;
|
||||
if (width <= maxWidth) {
|
||||
if ((flags & DIALOG_BOX_NO_HORIZONTAL_CENTERING) != 0) {
|
||||
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * v23 + _xtable[dialogType],
|
||||
body[v94],
|
||||
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * nextY + _xtable[dialogType],
|
||||
body[index],
|
||||
backgroundFrmImage.getWidth(),
|
||||
backgroundFrmImage.getWidth(),
|
||||
bodyColor);
|
||||
} else {
|
||||
int length = fontGetStringWidth(body[v94]);
|
||||
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * v23 + (backgroundFrmImage.getWidth() - length) / 2,
|
||||
body[v94],
|
||||
int length = fontGetStringWidth(body[index]);
|
||||
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * nextY + (backgroundFrmImage.getWidth() - length) / 2,
|
||||
body[index],
|
||||
backgroundFrmImage.getWidth(),
|
||||
backgroundFrmImage.getWidth(),
|
||||
bodyColor);
|
||||
}
|
||||
v23 += fontGetLineHeight();
|
||||
nextY += fontGetLineHeight();
|
||||
} else {
|
||||
short beginnings[WORD_WRAP_MAX_COUNT];
|
||||
short count;
|
||||
if (wordWrap(body[v94], backgroundFrmImage.getWidth() - 26, beginnings, &count) != 0) {
|
||||
if (wordWrap(body[index], maxWidth, beginnings, &count) != 0) {
|
||||
debugPrint("\nError: dialog_out");
|
||||
}
|
||||
|
||||
for (int v48 = 1; v48 < count; v48++) {
|
||||
int v51 = beginnings[v48] - beginnings[v48 - 1];
|
||||
if (v51 >= 260) {
|
||||
v51 = 259;
|
||||
for (int beginningIndex = 1; beginningIndex < count && nextY < maxY; beginningIndex++) {
|
||||
int subLineLength = beginnings[beginningIndex] - beginnings[beginningIndex - 1];
|
||||
if (subLineLength >= 260) {
|
||||
subLineLength = 259;
|
||||
}
|
||||
|
||||
char string[260];
|
||||
strncpy(string, body[v94] + beginnings[v48 - 1], v51);
|
||||
string[v51] = '\0';
|
||||
strncpy(string, body[index] + beginnings[beginningIndex - 1], subLineLength);
|
||||
string[subLineLength] = '\0';
|
||||
|
||||
// Remove trailing space as it affects width calculation.
|
||||
if (subLineLength > 0 && string[subLineLength - 1] == ' ') {
|
||||
string[subLineLength - 1] = '\0';
|
||||
subLineLength -= 1;
|
||||
}
|
||||
|
||||
if ((flags & DIALOG_BOX_NO_HORIZONTAL_CENTERING) != 0) {
|
||||
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * v23 + _xtable[dialogType],
|
||||
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * nextY + _xtable[dialogType],
|
||||
string,
|
||||
backgroundFrmImage.getWidth(),
|
||||
backgroundFrmImage.getWidth(),
|
||||
bodyColor);
|
||||
} else {
|
||||
int length = fontGetStringWidth(string);
|
||||
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * v23 + (backgroundFrmImage.getWidth() - length) / 2,
|
||||
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * nextY + (backgroundFrmImage.getWidth() - length) / 2,
|
||||
string,
|
||||
backgroundFrmImage.getWidth(),
|
||||
backgroundFrmImage.getWidth(),
|
||||
bodyColor);
|
||||
}
|
||||
v23 += fontGetLineHeight();
|
||||
nextY += fontGetLineHeight();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -495,7 +524,9 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
|
||||
|
||||
int rc = -1;
|
||||
while (rc == -1) {
|
||||
int keyCode = _get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
int keyCode = inputGetInput();
|
||||
|
||||
if (keyCode == 500) {
|
||||
rc = 1;
|
||||
@@ -517,6 +548,9 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
|
||||
if (_game_user_wants_to_quit != 0) {
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
windowDestroy(win);
|
||||
@@ -562,9 +596,9 @@ int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLen
|
||||
int backgroundHeight = frmImages[FILE_DIALOG_FRM_BACKGROUND].getHeight();
|
||||
|
||||
// Maintain original position in original resolution, otherwise center it.
|
||||
if (screenGetWidth() != 640) x = (screenGetWidth() - backgroundWidth) / 2;
|
||||
if (screenGetHeight() != 480) y = (screenGetHeight() - backgroundHeight) / 2;
|
||||
int win = windowCreate(x, y, backgroundWidth, backgroundHeight, 256, WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
|
||||
x += (screenGetWidth() - 640) / 2;
|
||||
y += (screenGetHeight() - 480) / 2;
|
||||
int win = windowCreate(x, y, backgroundWidth, backgroundHeight, 256, WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
|
||||
if (win == -1) {
|
||||
return -1;
|
||||
}
|
||||
@@ -581,7 +615,7 @@ int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLen
|
||||
}
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "%s%s", asc_5186C8, "DBOX.MSG");
|
||||
snprintf(path, sizeof(path), "%s%s", asc_5186C8, "DBOX.MSG");
|
||||
|
||||
if (!messageListLoad(&messageList, path)) {
|
||||
windowDestroy(win);
|
||||
@@ -695,8 +729,10 @@ int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLen
|
||||
|
||||
int rc = -1;
|
||||
while (rc == -1) {
|
||||
unsigned int tick = _get_time();
|
||||
int keyCode = _get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
unsigned int tick = getTicks();
|
||||
int keyCode = inputGetInput();
|
||||
int scrollDirection = FILE_DIALOG_SCROLL_DIRECTION_NONE;
|
||||
int scrollCounter = 0;
|
||||
bool isScrolling = false;
|
||||
@@ -805,7 +841,7 @@ int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLen
|
||||
unsigned int scrollDelay = 4;
|
||||
doubleClickSelectedFileIndex = -2;
|
||||
while (1) {
|
||||
unsigned int scrollTick = _get_time();
|
||||
unsigned int scrollTick = getTicks();
|
||||
scrollCounter += 1;
|
||||
if ((!isScrolling && scrollCounter == 1) || (isScrolling && scrollCounter > 14.4)) {
|
||||
isScrolling = true;
|
||||
@@ -857,10 +893,12 @@ int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLen
|
||||
break;
|
||||
}
|
||||
|
||||
int keyCode = _get_input();
|
||||
int keyCode = inputGetInput();
|
||||
if (keyCode == 505 || keyCode == 503) {
|
||||
break;
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
}
|
||||
} else {
|
||||
windowRefresh(win);
|
||||
@@ -878,6 +916,9 @@ int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLen
|
||||
if (_game_user_wants_to_quit) {
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
windowDestroy(win);
|
||||
@@ -921,9 +962,9 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
|
||||
int backgroundHeight = frmImages[FILE_DIALOG_FRM_BACKGROUND].getHeight();
|
||||
|
||||
// Maintain original position in original resolution, otherwise center it.
|
||||
if (screenGetWidth() != 640) x = (screenGetWidth() - backgroundWidth) / 2;
|
||||
if (screenGetHeight() != 480) y = (screenGetHeight() - backgroundHeight) / 2;
|
||||
int win = windowCreate(x, y, backgroundWidth, backgroundHeight, 256, WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
|
||||
x += (screenGetWidth() - 640) / 2;
|
||||
y += (screenGetHeight() - 480) / 2;
|
||||
int win = windowCreate(x, y, backgroundWidth, backgroundHeight, 256, WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
|
||||
if (win == -1) {
|
||||
return -1;
|
||||
}
|
||||
@@ -940,7 +981,7 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
|
||||
}
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "%s%s", asc_5186C8, "DBOX.MSG");
|
||||
snprintf(path, sizeof(path), "%s%s", asc_5186C8, "DBOX.MSG");
|
||||
|
||||
if (!messageListLoad(&messageList, path)) {
|
||||
windowDestroy(win);
|
||||
@@ -1074,6 +1115,8 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
|
||||
|
||||
windowRefresh(win);
|
||||
|
||||
beginTextInput();
|
||||
|
||||
int blinkingCounter = 3;
|
||||
bool blink = false;
|
||||
|
||||
@@ -1082,8 +1125,10 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
|
||||
|
||||
int rc = -1;
|
||||
while (rc == -1) {
|
||||
unsigned int tick = _get_time();
|
||||
int keyCode = _get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
unsigned int tick = getTicks();
|
||||
int keyCode = inputGetInput();
|
||||
int scrollDirection = FILE_DIALOG_SCROLL_DIRECTION_NONE;
|
||||
int scrollCounter = 0;
|
||||
bool isScrolling = false;
|
||||
@@ -1229,7 +1274,7 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
|
||||
unsigned int scrollDelay = 4;
|
||||
doubleClickSelectedFileIndex = -2;
|
||||
while (1) {
|
||||
unsigned int scrollTick = _get_time();
|
||||
unsigned int scrollTick = getTicks();
|
||||
scrollCounter += 1;
|
||||
if ((!isScrolling && scrollCounter == 1) || (isScrolling && scrollCounter > 14.4)) {
|
||||
isScrolling = true;
|
||||
@@ -1298,10 +1343,12 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
|
||||
break;
|
||||
}
|
||||
|
||||
int key = _get_input();
|
||||
int key = inputGetInput();
|
||||
if (key == 505 || key == 503) {
|
||||
break;
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
}
|
||||
} else {
|
||||
blinkingCounter -= 1;
|
||||
@@ -1329,8 +1376,13 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
|
||||
if (_game_user_wants_to_quit != 0) {
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
endTextInput();
|
||||
|
||||
if (rc == 0) {
|
||||
if (fileNameCopyLength != 0) {
|
||||
fileNameCopy[fileNameCopyLength] = '\0';
|
||||
@@ -1364,7 +1416,7 @@ static void fileDialogRenderFileList(unsigned char* buffer, char** fileList, int
|
||||
|
||||
for (int index = 0; index < fileListLength; index++) {
|
||||
int color = index == selectedIndex ? _colorTable[32747] : _colorTable[992];
|
||||
fontDrawText(buffer + pitch * y + FILE_DIALOG_FILE_LIST_X, fileList[pageOffset + index], pitch, pitch, color);
|
||||
fontDrawText(buffer + pitch * y + FILE_DIALOG_FILE_LIST_X, fileList[pageOffset + index], FILE_DIALOG_FILE_LIST_WIDTH, pitch, color);
|
||||
y += lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-12
@@ -5,10 +5,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <SDL.h>
|
||||
|
||||
#include "memory.h"
|
||||
#include "platform_compat.h"
|
||||
@@ -143,18 +140,12 @@ int debugPrint(const char* format, ...)
|
||||
|
||||
if (gDebugPrintProc != NULL) {
|
||||
char string[260];
|
||||
vsprintf(string, format, args);
|
||||
vsnprintf(string, sizeof(string), format, args);
|
||||
|
||||
rc = gDebugPrintProc(string);
|
||||
} else {
|
||||
#ifdef _DEBUG
|
||||
char string[260];
|
||||
vsprintf(string, format, args);
|
||||
#ifdef _WIN32
|
||||
OutputDebugStringA(string);
|
||||
#else
|
||||
printf("%s", string);
|
||||
#endif
|
||||
SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, format, args);
|
||||
#endif
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
+31
-7
@@ -5,6 +5,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <fpattern.h>
|
||||
|
||||
#include "platform_compat.h"
|
||||
@@ -41,6 +43,8 @@ static int dfileReadCharInternal(DFile* stream);
|
||||
static bool dfileReadCompressed(DFile* stream, void* ptr, size_t size);
|
||||
static void dfileUngetCompressed(DFile* stream, int ch);
|
||||
|
||||
static void dfile_normalize_path(char* path);
|
||||
|
||||
// Reads .DAT file contents.
|
||||
//
|
||||
// 0x4E4F58
|
||||
@@ -120,6 +124,9 @@ DBase* dbaseOpen(const char* filePath)
|
||||
|
||||
entry->path[pathLength] = '\0';
|
||||
|
||||
// CE: Normalize entry path.
|
||||
dfile_normalize_path(entry->path);
|
||||
|
||||
if (fread(&(entry->compressed), sizeof(entry->compressed), 1, stream) != 1) {
|
||||
break;
|
||||
}
|
||||
@@ -199,11 +206,18 @@ bool dbaseClose(DBase* dbase)
|
||||
// 0x4E5308
|
||||
bool dbaseFindFirstEntry(DBase* dbase, DFileFindData* findFileData, const char* pattern)
|
||||
{
|
||||
// CE: Normalize pattern to match entries. Underlying `fpattern`
|
||||
// implementation is case-sensitive on non-Windows platforms, so both
|
||||
// pattern and entries should match in case and have native path separators.
|
||||
char normalizedPattern[COMPAT_MAX_PATH];
|
||||
strcpy(normalizedPattern, pattern);
|
||||
dfile_normalize_path(normalizedPattern);
|
||||
|
||||
for (int index = 0; index < dbase->entriesLength; index++) {
|
||||
DBaseEntry* entry = &(dbase->entries[index]);
|
||||
if (fpattern_match(pattern, entry->path)) {
|
||||
if (fpattern_match(normalizedPattern, entry->path)) {
|
||||
strcpy(findFileData->fileName, entry->path);
|
||||
strcpy(findFileData->pattern, pattern);
|
||||
strcpy(findFileData->pattern, normalizedPattern);
|
||||
findFileData->index = index;
|
||||
return true;
|
||||
}
|
||||
@@ -630,7 +644,14 @@ static int dbaseFindEntryByFilePath(const void* a1, const void* a2)
|
||||
// 0x4E5D9C
|
||||
static DFile* dfileOpenInternal(DBase* dbase, const char* filePath, const char* mode, DFile* dfile)
|
||||
{
|
||||
DBaseEntry* entry = (DBaseEntry*)bsearch(filePath, dbase->entries, dbase->entriesLength, sizeof(*dbase->entries), dbaseFindEntryByFilePath);
|
||||
// CE: Normalize path to match entries. Even though
|
||||
// `dbaseFindEntryByFilePath` uses case-insensitive compare, it still needs
|
||||
// native path separators.
|
||||
char normalizedFilePath[COMPAT_MAX_PATH];
|
||||
strcpy(normalizedFilePath, filePath);
|
||||
dfile_normalize_path(normalizedFilePath);
|
||||
|
||||
DBaseEntry* entry = (DBaseEntry*)bsearch(normalizedFilePath, dbase->entries, dbase->entriesLength, sizeof(*dbase->entries), dbaseFindEntryByFilePath);
|
||||
if (entry == NULL) {
|
||||
goto err;
|
||||
}
|
||||
@@ -818,10 +839,7 @@ static bool dfileReadCompressed(DFile* stream, void* ptr, size_t size)
|
||||
|
||||
if (stream->decompressionStream->avail_in == 0) {
|
||||
// No more unprocessed data, request next chunk.
|
||||
size_t bytesToRead = stream->entry->dataSize - stream->compressedBytesRead;
|
||||
if (bytesToRead > DFILE_DECOMPRESSION_BUFFER_SIZE) {
|
||||
bytesToRead = DFILE_DECOMPRESSION_BUFFER_SIZE;
|
||||
}
|
||||
size_t bytesToRead = std::min(DFILE_DECOMPRESSION_BUFFER_SIZE, stream->entry->dataSize - stream->compressedBytesRead);
|
||||
|
||||
if (fread(stream->decompressionBuffer, bytesToRead, 1, stream->stream) != 1) {
|
||||
break;
|
||||
@@ -855,4 +873,10 @@ static void dfileUngetCompressed(DFile* stream, int ch)
|
||||
stream->position--;
|
||||
}
|
||||
|
||||
static void dfile_normalize_path(char* path)
|
||||
{
|
||||
compat_windows_path_to_native(path);
|
||||
compat_strlwr(path);
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
+2
-1
@@ -2,10 +2,11 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "memory_manager.h"
|
||||
#include "mouse.h"
|
||||
#include "movie.h"
|
||||
#include "platform_compat.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "window_manager.h"
|
||||
|
||||
|
||||
+1
-1
@@ -447,7 +447,7 @@ int dictionaryLoad(FILE* stream, Dictionary* dictionary, int a3)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fgets(entry->key, keyLength, stream) == NULL) {
|
||||
if (compat_fgets(entry->key, keyLength + 1, stream) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
+14
-126
@@ -2,30 +2,9 @@
|
||||
|
||||
namespace fallout {
|
||||
|
||||
enum InputType {
|
||||
INPUT_TYPE_MOUSE,
|
||||
INPUT_TYPE_TOUCH,
|
||||
} InputType;
|
||||
|
||||
static int gLastInputType = INPUT_TYPE_MOUSE;
|
||||
|
||||
static int gTouchMouseLastX = 0;
|
||||
static int gTouchMouseLastY = 0;
|
||||
static int gTouchMouseDeltaX = 0;
|
||||
static int gTouchMouseDeltaY = 0;
|
||||
|
||||
static int gTouchFingers = 0;
|
||||
static unsigned int gTouchGestureLastTouchDownTimestamp = 0;
|
||||
static unsigned int gTouchGestureLastTouchUpTimestamp = 0;
|
||||
static int gTouchGestureTaps = 0;
|
||||
static bool gTouchGestureHandled = false;
|
||||
|
||||
static int gMouseWheelDeltaX = 0;
|
||||
static int gMouseWheelDeltaY = 0;
|
||||
|
||||
extern int screenGetWidth();
|
||||
extern int screenGetHeight();
|
||||
|
||||
// 0x4E0400
|
||||
bool directInputInit()
|
||||
{
|
||||
@@ -71,49 +50,22 @@ bool mouseDeviceUnacquire()
|
||||
// 0x4E053C
|
||||
bool mouseDeviceGetData(MouseData* mouseState)
|
||||
{
|
||||
if (gLastInputType == INPUT_TYPE_TOUCH) {
|
||||
mouseState->x = gTouchMouseDeltaX;
|
||||
mouseState->y = gTouchMouseDeltaY;
|
||||
mouseState->buttons[0] = 0;
|
||||
mouseState->buttons[1] = 0;
|
||||
mouseState->wheelX = 0;
|
||||
mouseState->wheelY = 0;
|
||||
gTouchMouseDeltaX = 0;
|
||||
gTouchMouseDeltaY = 0;
|
||||
// CE: This function is sometimes called outside loops calling `get_input`
|
||||
// and subsequently `GNW95_process_message`, so mouse events might not be
|
||||
// handled by SDL yet.
|
||||
//
|
||||
// TODO: Move mouse events processing into `GNW95_process_message` and
|
||||
// update mouse position manually.
|
||||
SDL_PumpEvents();
|
||||
|
||||
if (gTouchFingers == 0) {
|
||||
if (SDL_GetTicks() - gTouchGestureLastTouchUpTimestamp > 150) {
|
||||
if (!gTouchGestureHandled) {
|
||||
if (gTouchGestureTaps == 2) {
|
||||
mouseState->buttons[0] = 1;
|
||||
gTouchGestureHandled = true;
|
||||
} else if (gTouchGestureTaps == 3) {
|
||||
mouseState->buttons[1] = 1;
|
||||
gTouchGestureHandled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (gTouchFingers == 1) {
|
||||
if (SDL_GetTicks() - gTouchGestureLastTouchDownTimestamp > 150) {
|
||||
if (gTouchGestureTaps == 1) {
|
||||
mouseState->buttons[0] = 1;
|
||||
gTouchGestureHandled = true;
|
||||
} else if (gTouchGestureTaps == 2) {
|
||||
mouseState->buttons[1] = 1;
|
||||
gTouchGestureHandled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Uint32 buttons = SDL_GetRelativeMouseState(&(mouseState->x), &(mouseState->y));
|
||||
mouseState->buttons[0] = (buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0;
|
||||
mouseState->buttons[1] = (buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
|
||||
mouseState->wheelX = gMouseWheelDeltaX;
|
||||
mouseState->wheelY = gMouseWheelDeltaY;
|
||||
Uint32 buttons = SDL_GetRelativeMouseState(&(mouseState->x), &(mouseState->y));
|
||||
mouseState->buttons[0] = (buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0;
|
||||
mouseState->buttons[1] = (buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
|
||||
mouseState->wheelX = gMouseWheelDeltaX;
|
||||
mouseState->wheelY = gMouseWheelDeltaY;
|
||||
|
||||
gMouseWheelDeltaX = 0;
|
||||
gMouseWheelDeltaY = 0;
|
||||
}
|
||||
gMouseWheelDeltaX = 0;
|
||||
gMouseWheelDeltaY = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -174,70 +126,6 @@ void handleMouseEvent(SDL_Event* event)
|
||||
gMouseWheelDeltaX += event->wheel.x;
|
||||
gMouseWheelDeltaY += event->wheel.y;
|
||||
}
|
||||
|
||||
if (gLastInputType != INPUT_TYPE_MOUSE) {
|
||||
// Reset touch data.
|
||||
gTouchMouseLastX = 0;
|
||||
gTouchMouseLastY = 0;
|
||||
gTouchMouseDeltaX = 0;
|
||||
gTouchMouseDeltaY = 0;
|
||||
|
||||
gTouchFingers = 0;
|
||||
gTouchGestureLastTouchDownTimestamp = 0;
|
||||
gTouchGestureLastTouchUpTimestamp = 0;
|
||||
gTouchGestureTaps = 0;
|
||||
gTouchGestureHandled = false;
|
||||
|
||||
gLastInputType = INPUT_TYPE_MOUSE;
|
||||
}
|
||||
}
|
||||
|
||||
void handleTouchEvent(SDL_Event* event)
|
||||
{
|
||||
int windowWidth = screenGetWidth();
|
||||
int windowHeight = screenGetHeight();
|
||||
|
||||
if (event->tfinger.type == SDL_FINGERDOWN) {
|
||||
gTouchFingers++;
|
||||
|
||||
gTouchMouseLastX = (int)(event->tfinger.x * windowWidth);
|
||||
gTouchMouseLastY = (int)(event->tfinger.y * windowHeight);
|
||||
gTouchMouseDeltaX = 0;
|
||||
gTouchMouseDeltaY = 0;
|
||||
|
||||
if (event->tfinger.timestamp - gTouchGestureLastTouchDownTimestamp > 250) {
|
||||
gTouchGestureTaps = 0;
|
||||
gTouchGestureHandled = false;
|
||||
}
|
||||
|
||||
gTouchGestureLastTouchDownTimestamp = event->tfinger.timestamp;
|
||||
} else if (event->tfinger.type == SDL_FINGERMOTION) {
|
||||
int prevX = gTouchMouseLastX;
|
||||
int prevY = gTouchMouseLastY;
|
||||
gTouchMouseLastX = (int)(event->tfinger.x * windowWidth);
|
||||
gTouchMouseLastY = (int)(event->tfinger.y * windowHeight);
|
||||
gTouchMouseDeltaX += gTouchMouseLastX - prevX;
|
||||
gTouchMouseDeltaY += gTouchMouseLastY - prevY;
|
||||
} else if (event->tfinger.type == SDL_FINGERUP) {
|
||||
gTouchFingers--;
|
||||
|
||||
int prevX = gTouchMouseLastX;
|
||||
int prevY = gTouchMouseLastY;
|
||||
gTouchMouseLastX = (int)(event->tfinger.x * windowWidth);
|
||||
gTouchMouseLastY = (int)(event->tfinger.y * windowHeight);
|
||||
gTouchMouseDeltaX += gTouchMouseLastX - prevX;
|
||||
gTouchMouseDeltaY += gTouchMouseLastY - prevY;
|
||||
|
||||
gTouchGestureTaps++;
|
||||
gTouchGestureLastTouchUpTimestamp = event->tfinger.timestamp;
|
||||
}
|
||||
|
||||
if (gLastInputType != INPUT_TYPE_TOUCH) {
|
||||
// Reset mouse data.
|
||||
SDL_GetRelativeMouseState(NULL, NULL);
|
||||
|
||||
gLastInputType = INPUT_TYPE_TOUCH;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
+36
-25
@@ -7,14 +7,15 @@
|
||||
#include "art.h"
|
||||
#include "color.h"
|
||||
#include "combat.h"
|
||||
#include "core.h"
|
||||
#include "draw.h"
|
||||
#include "game_mouse.h"
|
||||
#include "game_sound.h"
|
||||
#include "geometry.h"
|
||||
#include "input.h"
|
||||
#include "interface.h"
|
||||
#include "memory.h"
|
||||
#include "sfall_config.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "window_manager.h"
|
||||
|
||||
@@ -29,7 +30,7 @@ namespace fallout {
|
||||
|
||||
#define DISPLAY_MONITOR_X (23)
|
||||
#define DISPLAY_MONITOR_Y (24)
|
||||
#define DISPLAY_MONITOR_WIDTH (167)
|
||||
#define DISPLAY_MONITOR_WIDTH (167 + gInterfaceBarContentOffset)
|
||||
#define DISPLAY_MONITOR_HEIGHT (60)
|
||||
|
||||
#define DISPLAY_MONITOR_HALF_HEIGHT (DISPLAY_MONITOR_HEIGHT / 2)
|
||||
@@ -58,12 +59,7 @@ static bool gDisplayMonitorInitialized = false;
|
||||
// The rectangle that display monitor occupies in the main interface window.
|
||||
//
|
||||
// 0x518510
|
||||
static const Rect gDisplayMonitorRect = {
|
||||
DISPLAY_MONITOR_X,
|
||||
DISPLAY_MONITOR_Y,
|
||||
DISPLAY_MONITOR_X + DISPLAY_MONITOR_WIDTH - 1,
|
||||
DISPLAY_MONITOR_Y + DISPLAY_MONITOR_HEIGHT - 1,
|
||||
};
|
||||
static Rect gDisplayMonitorRect;
|
||||
|
||||
// 0x518520
|
||||
static int gDisplayMonitorScrollDownButton = -1;
|
||||
@@ -105,6 +101,13 @@ static int gConsoleFilePrintCount = 0;
|
||||
int displayMonitorInit()
|
||||
{
|
||||
if (!gDisplayMonitorInitialized) {
|
||||
gDisplayMonitorRect = {
|
||||
DISPLAY_MONITOR_X,
|
||||
DISPLAY_MONITOR_Y,
|
||||
DISPLAY_MONITOR_X + DISPLAY_MONITOR_WIDTH - 1,
|
||||
DISPLAY_MONITOR_Y + DISPLAY_MONITOR_HEIGHT - 1,
|
||||
};
|
||||
|
||||
int oldFont = fontGetCurrent();
|
||||
fontSetCurrent(DISPLAY_MONITOR_FONT);
|
||||
|
||||
@@ -119,25 +122,33 @@ int displayMonitorInit()
|
||||
return -1;
|
||||
}
|
||||
|
||||
CacheEntry* backgroundFrmHandle;
|
||||
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 16, 0, 0, 0);
|
||||
Art* backgroundFrm = artLock(backgroundFid, &backgroundFrmHandle);
|
||||
if (backgroundFrm == NULL) {
|
||||
internal_free(gDisplayMonitorBackgroundFrmData);
|
||||
return -1;
|
||||
if (gInterfaceBarIsCustom) {
|
||||
_intface_full_width = gInterfaceBarWidth;
|
||||
blitBufferToBuffer(customInterfaceBarGetBackgroundImageData() + gInterfaceBarWidth * DISPLAY_MONITOR_Y + DISPLAY_MONITOR_X,
|
||||
DISPLAY_MONITOR_WIDTH,
|
||||
DISPLAY_MONITOR_HEIGHT,
|
||||
gInterfaceBarWidth,
|
||||
gDisplayMonitorBackgroundFrmData,
|
||||
DISPLAY_MONITOR_WIDTH);
|
||||
} else {
|
||||
FrmImage backgroundFrmImage;
|
||||
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 16, 0, 0, 0);
|
||||
if (!backgroundFrmImage.lock(backgroundFid)) {
|
||||
internal_free(gDisplayMonitorBackgroundFrmData);
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned char* backgroundFrmData = backgroundFrmImage.getData();
|
||||
_intface_full_width = backgroundFrmImage.getWidth();
|
||||
|
||||
blitBufferToBuffer(backgroundFrmData + _intface_full_width * DISPLAY_MONITOR_Y + DISPLAY_MONITOR_X,
|
||||
DISPLAY_MONITOR_WIDTH,
|
||||
DISPLAY_MONITOR_HEIGHT,
|
||||
_intface_full_width,
|
||||
gDisplayMonitorBackgroundFrmData,
|
||||
DISPLAY_MONITOR_WIDTH);
|
||||
}
|
||||
|
||||
unsigned char* backgroundFrmData = artGetFrameData(backgroundFrm, 0, 0);
|
||||
_intface_full_width = artGetWidth(backgroundFrm, 0, 0);
|
||||
blitBufferToBuffer(backgroundFrmData + _intface_full_width * DISPLAY_MONITOR_Y + DISPLAY_MONITOR_X,
|
||||
DISPLAY_MONITOR_WIDTH,
|
||||
DISPLAY_MONITOR_HEIGHT,
|
||||
_intface_full_width,
|
||||
gDisplayMonitorBackgroundFrmData,
|
||||
DISPLAY_MONITOR_WIDTH);
|
||||
|
||||
artUnlock(backgroundFrmHandle);
|
||||
|
||||
gDisplayMonitorScrollUpButton = buttonCreate(gInterfaceBarWindow,
|
||||
DISPLAY_MONITOR_X,
|
||||
DISPLAY_MONITOR_Y,
|
||||
|
||||
+59
-49
@@ -3,8 +3,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "color.h"
|
||||
#include "core.h"
|
||||
#include "mmx.h"
|
||||
#include "svga.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
@@ -152,87 +151,69 @@ void bufferDrawRectShadowed(unsigned char* buf, int pitch, int left, int top, in
|
||||
// 0x4D33F0
|
||||
void blitBufferToBufferStretch(unsigned char* src, int srcWidth, int srcHeight, int srcPitch, unsigned char* dest, int destWidth, int destHeight, int destPitch)
|
||||
{
|
||||
int heightRatio = (destHeight << 16) / srcHeight;
|
||||
int widthRatio = (destWidth << 16) / srcWidth;
|
||||
int stepX = (destWidth << 16) / srcWidth;
|
||||
int stepY = (destHeight << 16) / srcHeight;
|
||||
|
||||
int v1 = 0;
|
||||
int v2 = heightRatio;
|
||||
for (int srcY = 0; srcY < srcHeight; srcY += 1) {
|
||||
int v3 = widthRatio;
|
||||
int v4 = (heightRatio * srcY) >> 16;
|
||||
int v5 = v2 >> 16;
|
||||
int v6 = 0;
|
||||
int startDestY = (srcY * stepY) >> 16;
|
||||
int endDestY = ((srcY + 1) * stepY) >> 16;
|
||||
|
||||
unsigned char* c = src + v1;
|
||||
unsigned char* currSrc = src + srcPitch * srcY;
|
||||
for (int srcX = 0; srcX < srcWidth; srcX += 1) {
|
||||
int v7 = v3 >> 16;
|
||||
int v8 = v6 >> 16;
|
||||
int startDestX = (srcX * stepX) >> 16;
|
||||
int endDestX = ((srcX + 1) * stepX) >> 16;
|
||||
|
||||
unsigned char* v9 = dest + destPitch * v4 + v8;
|
||||
for (int destY = v4; destY < v5; destY += 1) {
|
||||
for (int destX = v8; destX < v7; destX += 1) {
|
||||
*v9++ = *c;
|
||||
for (int destY = startDestY; destY < endDestY; destY += 1) {
|
||||
unsigned char* currDest = dest + destPitch * destY + startDestX;
|
||||
for (int destX = startDestX; destX < endDestX; destX += 1) {
|
||||
*currDest++ = *currSrc;
|
||||
}
|
||||
v9 += destPitch;
|
||||
}
|
||||
|
||||
v3 += widthRatio;
|
||||
c++;
|
||||
v6 += widthRatio;
|
||||
currSrc++;
|
||||
}
|
||||
v1 += srcPitch;
|
||||
v2 += heightRatio;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4D3560
|
||||
void blitBufferToBufferStretchTrans(unsigned char* src, int srcWidth, int srcHeight, int srcPitch, unsigned char* dest, int destWidth, int destHeight, int destPitch)
|
||||
{
|
||||
int heightRatio = (destHeight << 16) / srcHeight;
|
||||
int widthRatio = (destWidth << 16) / srcWidth;
|
||||
int stepX = (destWidth << 16) / srcWidth;
|
||||
int stepY = (destHeight << 16) / srcHeight;
|
||||
|
||||
int v1 = 0;
|
||||
int v2 = heightRatio;
|
||||
for (int srcY = 0; srcY < srcHeight; srcY += 1) {
|
||||
int v3 = widthRatio;
|
||||
int v4 = (heightRatio * srcY) >> 16;
|
||||
int v5 = v2 >> 16;
|
||||
int v6 = 0;
|
||||
int startDestY = (srcY * stepY) >> 16;
|
||||
int endDestY = ((srcY + 1) * stepY) >> 16;
|
||||
|
||||
unsigned char* c = src + v1;
|
||||
unsigned char* currSrc = src + srcPitch * srcY;
|
||||
for (int srcX = 0; srcX < srcWidth; srcX += 1) {
|
||||
int v7 = v3 >> 16;
|
||||
int v8 = v6 >> 16;
|
||||
int startDestX = (srcX * stepX) >> 16;
|
||||
int endDestX = ((srcX + 1) * stepX) >> 16;
|
||||
|
||||
if (*c != 0) {
|
||||
unsigned char* v9 = dest + destPitch * v4 + v8;
|
||||
for (int destY = v4; destY < v5; destY += 1) {
|
||||
for (int destX = v8; destX < v7; destX += 1) {
|
||||
*v9++ = *c;
|
||||
if (*currSrc != 0) {
|
||||
for (int destY = startDestY; destY < endDestY; destY += 1) {
|
||||
unsigned char* currDest = dest + destPitch * destY + startDestX;
|
||||
for (int destX = startDestX; destX < endDestX; destX += 1) {
|
||||
*currDest++ = *currSrc;
|
||||
}
|
||||
v9 += destPitch;
|
||||
}
|
||||
}
|
||||
|
||||
v3 += widthRatio;
|
||||
c++;
|
||||
v6 += widthRatio;
|
||||
currSrc++;
|
||||
}
|
||||
v1 += srcPitch;
|
||||
v2 += heightRatio;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4D36D4
|
||||
void blitBufferToBuffer(unsigned char* src, int width, int height, int srcPitch, unsigned char* dest, int destPitch)
|
||||
{
|
||||
mmxBlit(dest, destPitch, src, srcPitch, width, height);
|
||||
srcCopy(dest, destPitch, src, srcPitch, width, height);
|
||||
}
|
||||
|
||||
// 0x4D3704
|
||||
void blitBufferToBufferTrans(unsigned char* src, int width, int height, int srcPitch, unsigned char* dest, int destPitch)
|
||||
{
|
||||
mmxBlitTrans(dest, destPitch, src, srcPitch, width, height);
|
||||
transSrcCopy(dest, destPitch, src, srcPitch, width, height);
|
||||
}
|
||||
|
||||
// 0x4D387C
|
||||
@@ -259,8 +240,8 @@ void _lighten_buf(unsigned char* buf, int width, int height, int pitch)
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
unsigned char p = *buf;
|
||||
*buf++ = _intensityColorTable[(p << 8) + 147];
|
||||
unsigned char color = *buf;
|
||||
*buf++ = intensityColorTable[color][147];
|
||||
}
|
||||
buf += skip;
|
||||
}
|
||||
@@ -329,4 +310,33 @@ void bufferOutline(unsigned char* buf, int width, int height, int pitch, int col
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4E0DB0
|
||||
void srcCopy(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height)
|
||||
{
|
||||
for (int y = 0; y < height; y++) {
|
||||
memcpy(dest, src, width);
|
||||
dest += destPitch;
|
||||
src += srcPitch;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4E0ED5
|
||||
void transSrcCopy(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height)
|
||||
{
|
||||
int destSkip = destPitch - width;
|
||||
int srcSkip = srcPitch - width;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
unsigned char c = *src++;
|
||||
if (c != 0) {
|
||||
*dest = c;
|
||||
}
|
||||
dest++;
|
||||
}
|
||||
src += srcSkip;
|
||||
dest += destSkip;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
@@ -15,6 +15,8 @@ void _buf_texture(unsigned char* buf, int width, int height, int pitch, void* a5
|
||||
void _lighten_buf(unsigned char* buf, int width, int height, int pitch);
|
||||
void _swap_color_buf(unsigned char* buf, int width, int height, int pitch, int color1, int color2);
|
||||
void bufferOutline(unsigned char* buf, int width, int height, int pitch, int a5);
|
||||
void srcCopy(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height);
|
||||
void transSrcCopy(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
#include "electronic_registration.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "game_config.h"
|
||||
#include "platform_compat.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
// 0x440DD0
|
||||
void runElectronicRegistration()
|
||||
{
|
||||
int timesRun = 0;
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_TIMES_RUN_KEY, ×Run);
|
||||
if (timesRun > 0 && timesRun < 5) {
|
||||
#ifdef _WIN32
|
||||
char path[COMPAT_MAX_PATH];
|
||||
if (GetModuleFileNameA(NULL, path, sizeof(path)) != 0) {
|
||||
char* pch = strrchr(path, '\\');
|
||||
if (pch == NULL) {
|
||||
pch = path;
|
||||
}
|
||||
|
||||
strcpy(pch, "\\ereg");
|
||||
|
||||
STARTUPINFOA startupInfo;
|
||||
memset(&startupInfo, 0, sizeof(startupInfo));
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
|
||||
PROCESS_INFORMATION processInfo;
|
||||
|
||||
// FIXME: Leaking processInfo.hProcess and processInfo.hThread:
|
||||
// https://docs.microsoft.com/en-us/cpp/code-quality/c6335.
|
||||
if (CreateProcessA("ereg\\reg32a.exe", NULL, NULL, NULL, FALSE, 0, NULL, path, &startupInfo, &processInfo)) {
|
||||
WaitForSingleObject(processInfo.hProcess, INFINITE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_TIMES_RUN_KEY, timesRun + 1);
|
||||
} else {
|
||||
if (timesRun == 0) {
|
||||
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_TIMES_RUN_KEY, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
@@ -1,10 +0,0 @@
|
||||
#ifndef ELECTRONIC_REGISTRATION_H
|
||||
#define ELECTRONIC_REGISTRATION_H
|
||||
|
||||
namespace fallout {
|
||||
|
||||
void runElectronicRegistration();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* ELECTRONIC_REGISTRATION_H */
|
||||
+22
-10
@@ -6,18 +6,20 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "art.h"
|
||||
#include "core.h"
|
||||
#include "cycle.h"
|
||||
#include "debug.h"
|
||||
#include "draw.h"
|
||||
#include "game_mouse.h"
|
||||
#include "game_sound.h"
|
||||
#include "geometry.h"
|
||||
#include "input.h"
|
||||
#include "interface.h"
|
||||
#include "kb.h"
|
||||
#include "map.h"
|
||||
#include "pipboy.h"
|
||||
#include "scripts.h"
|
||||
#include "sfall_config.h"
|
||||
#include "svga.h"
|
||||
#include "window_manager.h"
|
||||
|
||||
namespace fallout {
|
||||
@@ -392,7 +394,9 @@ int elevatorSelectLevel(int elevator, int* mapPtr, int* elevationPtr, int* tileP
|
||||
bool done = false;
|
||||
int keyCode;
|
||||
while (!done) {
|
||||
keyCode = _get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
keyCode = inputGetInput();
|
||||
if (keyCode == KEY_ESCAPE) {
|
||||
done = true;
|
||||
}
|
||||
@@ -408,6 +412,9 @@ int elevatorSelectLevel(int elevator, int* mapPtr, int* elevationPtr, int* tileP
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
if (keyCode != KEY_ESCAPE) {
|
||||
@@ -432,7 +439,9 @@ int elevatorSelectLevel(int elevator, int* mapPtr, int* elevationPtr, int* tileP
|
||||
float v41 = (float)keyCode * v42;
|
||||
float v44 = (float)(*elevationPtr) * v42;
|
||||
do {
|
||||
unsigned int tick = _get_time();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
unsigned int tick = getTicks();
|
||||
v44 += v43;
|
||||
blitBufferToBuffer(
|
||||
_elevatorFrmImages[ELEVATOR_FRM_GAUGE].getData() + v18 * (int)v44,
|
||||
@@ -446,9 +455,12 @@ int elevatorSelectLevel(int elevator, int* mapPtr, int* elevationPtr, int* tileP
|
||||
|
||||
while (getTicksSince(tick) < delay) {
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
} while ((v43 <= 0.0 || v44 < v41) && (v43 > 0.0 || v44 > v41));
|
||||
|
||||
coreDelayProcessingEvents(200);
|
||||
inputPauseForTocks(200);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,7 +550,7 @@ static int elevatorWindowInit(int elevator)
|
||||
_elevatorBackgroundFrmImage.getWidth(),
|
||||
_elevatorBackgroundFrmImage.getHeight(),
|
||||
256,
|
||||
WINDOW_FLAG_0x10 | WINDOW_FLAG_0x02);
|
||||
WINDOW_MODAL | WINDOW_DONT_MOVE_TOP);
|
||||
if (gElevatorWindow == -1) {
|
||||
_elevatorBackgroundFrmImage.unlock();
|
||||
_elevatorPanelFrmImage.unlock();
|
||||
@@ -649,7 +661,7 @@ void elevatorsInit()
|
||||
char sectionKey[4];
|
||||
char key[32];
|
||||
for (int index = 0; index < ELEVATORS_MAX; index++) {
|
||||
sprintf(sectionKey, "%d", index);
|
||||
snprintf(sectionKey, sizeof(sectionKey), "%d", index);
|
||||
|
||||
if (index >= ELEVATOR_COUNT) {
|
||||
int levels = 0;
|
||||
@@ -661,13 +673,13 @@ void elevatorsInit()
|
||||
configGetInt(&elevatorsConfig, sectionKey, "ButtonsFrm", &(gElevatorBackgrounds[index].panelFrmId));
|
||||
|
||||
for (int level = 0; level < ELEVATOR_LEVEL_MAX; level++) {
|
||||
sprintf(key, "ID%d", level + 1);
|
||||
snprintf(key, sizeof(key), "ID%d", level + 1);
|
||||
configGetInt(&elevatorsConfig, sectionKey, key, &(gElevatorDescriptions[index][level].map));
|
||||
|
||||
sprintf(key, "Elevation%d", level + 1);
|
||||
snprintf(key, sizeof(key), "Elevation%d", level + 1);
|
||||
configGetInt(&elevatorsConfig, sectionKey, key, &(gElevatorDescriptions[index][level].elevation));
|
||||
|
||||
sprintf(key, "Tile%d", level + 1);
|
||||
snprintf(key, sizeof(key), "Tile%d", level + 1);
|
||||
configGetInt(&elevatorsConfig, sectionKey, key, &(gElevatorDescriptions[index][level].tile));
|
||||
}
|
||||
}
|
||||
@@ -677,7 +689,7 @@ void elevatorsInit()
|
||||
// value is then used in the certain places to remap from
|
||||
// requested elevator to the new one.
|
||||
for (int index = 0; index < ELEVATORS_MAX; index++) {
|
||||
sprintf(sectionKey, "%d", index);
|
||||
snprintf(sectionKey, sizeof(sectionKey), "%d", index);
|
||||
|
||||
int type;
|
||||
if (configGetInt(&elevatorsConfig, sectionKey, "Image", &type)) {
|
||||
|
||||
+12
-1
@@ -17,7 +17,18 @@ typedef enum Elevator {
|
||||
ELEVATOR_SIERRA_1,
|
||||
ELEVATOR_SIERRA_2,
|
||||
ELEVATOR_SIERRA_SERVICE,
|
||||
ELEVATOR_COUNT = 24,
|
||||
ELEVATOR_KLAMATH_TOXIC_CAVES,
|
||||
ELEVATOR_14,
|
||||
ELEVATOR_VAULT_CITY,
|
||||
ELEVATOR_VAULT_15_MAIN,
|
||||
ELEVATOR_VAULT_15_SURFACE,
|
||||
ELEVATOR_NAVARRO_NORTHERN,
|
||||
ELEVATOR_NAVARRO_CENTER,
|
||||
ELEVATOR_NAVARRO_LAB,
|
||||
ELEVATOR_NAVARRO_CANTEEN,
|
||||
ELEVATOR_SAN_FRANCISCO_SHI_TEMPLE,
|
||||
ELEVATOR_REDDING_WANAMINGO_MINE,
|
||||
ELEVATOR_COUNT,
|
||||
} Elevator;
|
||||
|
||||
int elevatorSelectLevel(int elevator, int* mapPtr, int* elevationPtr, int* tilePtr);
|
||||
|
||||
+67
-28
@@ -8,7 +8,6 @@
|
||||
|
||||
#include "art.h"
|
||||
#include "color.h"
|
||||
#include "core.h"
|
||||
#include "credits.h"
|
||||
#include "cycle.h"
|
||||
#include "db.h"
|
||||
@@ -16,18 +15,21 @@
|
||||
#include "debug.h"
|
||||
#include "draw.h"
|
||||
#include "game.h"
|
||||
#include "game_config.h"
|
||||
#include "game_mouse.h"
|
||||
#include "game_movie.h"
|
||||
#include "game_sound.h"
|
||||
#include "input.h"
|
||||
#include "map.h"
|
||||
#include "memory.h"
|
||||
#include "mouse.h"
|
||||
#include "object.h"
|
||||
#include "palette.h"
|
||||
#include "pipboy.h"
|
||||
#include "platform_compat.h"
|
||||
#include "random.h"
|
||||
#include "settings.h"
|
||||
#include "stat.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "window_manager.h"
|
||||
#include "word_wrap.h"
|
||||
@@ -81,9 +83,7 @@ static void _endgame_movie_bk_process();
|
||||
static int endgameEndingInit();
|
||||
static void endgameEndingFree();
|
||||
static int endgameDeathEndingValidate(int* percentage);
|
||||
|
||||
// 0x50B00C
|
||||
char _aEnglish_2[] = ENGLISH;
|
||||
static void endgameEndingUpdateOverlay();
|
||||
|
||||
// The number of lines in current subtitles file.
|
||||
//
|
||||
@@ -205,6 +205,8 @@ static unsigned char* gEndgameEndingSlideshowWindowBuffer;
|
||||
// 0x570BF4
|
||||
static int gEndgameEndingSlideshowWindow;
|
||||
|
||||
static int gEndgameEndingOverlay;
|
||||
|
||||
// 0x43F788
|
||||
void endgamePlaySlideshow()
|
||||
{
|
||||
@@ -238,7 +240,7 @@ void endgamePlayMovie()
|
||||
tickersAdd(_endgame_movie_bk_process);
|
||||
backgroundSoundSetEndCallback(_endgame_movie_callback);
|
||||
backgroundSoundLoad("akiss", 12, 14, 15);
|
||||
coreDelayProcessingEvents(3000);
|
||||
inputPauseForTocks(3000);
|
||||
|
||||
// NOTE: Result is ignored. I guess there was some kind of switch for male
|
||||
// vs. female ending, but it was not implemented.
|
||||
@@ -322,6 +324,9 @@ static void endgameEndingRenderPanningScene(int direction, const char* narratorF
|
||||
bufferFill(gEndgameEndingSlideshowWindowBuffer, ENDGAME_ENDING_WINDOW_WIDTH, ENDGAME_ENDING_WINDOW_HEIGHT, ENDGAME_ENDING_WINDOW_WIDTH, _colorTable[0]);
|
||||
endgameEndingLoadPalette(6, 327);
|
||||
|
||||
// CE: Update overlay.
|
||||
endgameEndingUpdateOverlay();
|
||||
|
||||
unsigned char palette[768];
|
||||
memcpy(palette, _cmap, 768);
|
||||
|
||||
@@ -357,6 +362,8 @@ static void endgameEndingRenderPanningScene(int direction, const char* narratorF
|
||||
|
||||
unsigned int since = 0;
|
||||
while (start != end) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
int v12 = 640 - v32;
|
||||
|
||||
// TODO: Complex math, setup scene in debugger.
|
||||
@@ -369,7 +376,7 @@ static void endgameEndingRenderPanningScene(int direction, const char* narratorF
|
||||
|
||||
windowRefresh(gEndgameEndingSlideshowWindow);
|
||||
|
||||
since = _get_time();
|
||||
since = getTicks();
|
||||
|
||||
bool v14;
|
||||
double v31;
|
||||
@@ -409,11 +416,14 @@ static void endgameEndingRenderPanningScene(int direction, const char* narratorF
|
||||
|
||||
soundContinueAll();
|
||||
|
||||
if (_get_input() != -1) {
|
||||
if (inputGetInput() != -1) {
|
||||
// NOTE: Uninline.
|
||||
endgameEndingVoiceOverFree();
|
||||
break;
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
tickersEnable();
|
||||
@@ -425,7 +435,12 @@ static void endgameEndingRenderPanningScene(int direction, const char* narratorF
|
||||
}
|
||||
|
||||
while (mouseGetEvent() != 0) {
|
||||
_get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
inputGetInput();
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,6 +460,9 @@ static void endgameEndingRenderStaticScene(int fid, const char* narratorFileName
|
||||
|
||||
endgameEndingLoadPalette(FID_TYPE(fid), fid & 0xFFF);
|
||||
|
||||
// CE: Update overlay.
|
||||
endgameEndingUpdateOverlay();
|
||||
|
||||
endgameEndingVoiceOverInit(narratorFileName);
|
||||
|
||||
unsigned int delay;
|
||||
@@ -456,17 +474,19 @@ static void endgameEndingRenderStaticScene(int fid, const char* narratorFileName
|
||||
|
||||
paletteFadeTo(_cmap);
|
||||
|
||||
coreDelayProcessingEvents(500);
|
||||
inputPauseForTocks(500);
|
||||
|
||||
// NOTE: Uninline.
|
||||
endgameEndingVoiceOverReset();
|
||||
|
||||
unsigned int referenceTime = _get_time();
|
||||
unsigned int referenceTime = getTicks();
|
||||
tickersDisable();
|
||||
|
||||
int keyCode;
|
||||
while (true) {
|
||||
keyCode = _get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
keyCode = inputGetInput();
|
||||
if (keyCode != -1) {
|
||||
break;
|
||||
}
|
||||
@@ -487,6 +507,9 @@ static void endgameEndingRenderStaticScene(int fid, const char* narratorFileName
|
||||
endgameEndingRefreshSubtitles();
|
||||
windowRefresh(gEndgameEndingSlideshowWindow);
|
||||
soundContinueAll();
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
tickersEnable();
|
||||
@@ -497,13 +520,18 @@ static void endgameEndingRenderStaticScene(int fid, const char* narratorFileName
|
||||
gEndgameEndingVoiceOverSubtitlesLoaded = false;
|
||||
|
||||
if (keyCode == -1) {
|
||||
coreDelayProcessingEvents(500);
|
||||
inputPauseForTocks(500);
|
||||
}
|
||||
|
||||
paletteFadeTo(gPaletteBlack);
|
||||
|
||||
while (mouseGetEvent() != 0) {
|
||||
_get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
inputGetInput();
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,6 +564,13 @@ static int endgameEndingSlideshowWindowInit()
|
||||
|
||||
paletteFadeTo(gPaletteBlack);
|
||||
|
||||
// CE: Every slide has a separate color palette which is incompatible with
|
||||
// main color palette. Setup overlay to hide everything.
|
||||
gEndgameEndingOverlay = windowCreate(0, 0, screenGetWidth(), screenGetHeight(), _colorTable[0], WINDOW_MOVE_ON_TOP);
|
||||
if (gEndgameEndingOverlay == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int windowEndgameEndingX = (screenGetWidth() - ENDGAME_ENDING_WINDOW_WIDTH) / 2;
|
||||
int windowEndgameEndingY = (screenGetHeight() - ENDGAME_ENDING_WINDOW_HEIGHT) / 2;
|
||||
gEndgameEndingSlideshowWindow = windowCreate(windowEndgameEndingX,
|
||||
@@ -543,7 +578,7 @@ static int endgameEndingSlideshowWindowInit()
|
||||
ENDGAME_ENDING_WINDOW_WIDTH,
|
||||
ENDGAME_ENDING_WINDOW_HEIGHT,
|
||||
_colorTable[0],
|
||||
WINDOW_FLAG_0x04);
|
||||
WINDOW_MOVE_ON_TOP);
|
||||
if (gEndgameEndingSlideshowWindow == -1) {
|
||||
return -1;
|
||||
}
|
||||
@@ -557,19 +592,12 @@ static int endgameEndingSlideshowWindowInit()
|
||||
|
||||
speechSetEndCallback(_endgame_voiceover_callback);
|
||||
|
||||
gEndgameEndingSubtitlesEnabled = false;
|
||||
configGetBool(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_SUBTITLES_KEY, &gEndgameEndingSubtitlesEnabled);
|
||||
gEndgameEndingSubtitlesEnabled = settings.preferences.subtitles;
|
||||
if (!gEndgameEndingSubtitlesEnabled) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* language;
|
||||
if (!configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_LANGUAGE_KEY, &language)) {
|
||||
gEndgameEndingSubtitlesEnabled = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sprintf(gEndgameEndingSubtitlesLocalizedPath, "text\\%s\\cuts\\", language);
|
||||
snprintf(gEndgameEndingSubtitlesLocalizedPath, sizeof(gEndgameEndingSubtitlesLocalizedPath), "text\\%s\\cuts\\", settings.system.language.c_str());
|
||||
|
||||
gEndgameEndingSubtitles = (char**)internal_malloc(sizeof(*gEndgameEndingSubtitles) * ENDGAME_ENDING_MAX_SUBTITLES);
|
||||
if (gEndgameEndingSubtitles == NULL) {
|
||||
@@ -611,6 +639,7 @@ static void endgameEndingSlideshowWindowFree()
|
||||
|
||||
speechSetEndCallback(NULL);
|
||||
windowDestroy(gEndgameEndingSlideshowWindow);
|
||||
windowDestroy(gEndgameEndingOverlay);
|
||||
|
||||
if (!_endgame_mouse_state) {
|
||||
mouseHideCursor();
|
||||
@@ -640,7 +669,7 @@ static void endgameEndingVoiceOverInit(const char* fileBaseName)
|
||||
gEndgameEndingVoiceOverSubtitlesLoaded = false;
|
||||
|
||||
// Build speech file path.
|
||||
sprintf(path, "%s%s", "narrator\\", fileBaseName);
|
||||
snprintf(path, sizeof(path), "%s%s", "narrator\\", fileBaseName);
|
||||
|
||||
if (speechLoad(path, 10, 14, 15) != -1) {
|
||||
gEndgameEndingVoiceOverSpeechLoaded = true;
|
||||
@@ -648,7 +677,7 @@ static void endgameEndingVoiceOverInit(const char* fileBaseName)
|
||||
|
||||
if (gEndgameEndingSubtitlesEnabled) {
|
||||
// Build subtitles file path.
|
||||
sprintf(path, "%s%s.txt", gEndgameEndingSubtitlesLocalizedPath, fileBaseName);
|
||||
snprintf(path, sizeof(path), "%s%s.txt", gEndgameEndingSubtitlesLocalizedPath, fileBaseName);
|
||||
|
||||
if (endgameEndingSubtitlesLoad(path) != 0) {
|
||||
return;
|
||||
@@ -687,7 +716,7 @@ static void endgameEndingVoiceOverReset()
|
||||
}
|
||||
|
||||
if (gEndgameEndingVoiceOverSubtitlesLoaded) {
|
||||
gEndgameEndingSubtitlesReferenceTime = _get_time();
|
||||
gEndgameEndingSubtitlesReferenceTime = getTicks();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -718,7 +747,7 @@ static void endgameEndingLoadPalette(int type, int id)
|
||||
|
||||
if (strlen(fileName) <= 8) {
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "%s\\%s.pal", "art\\intrface", fileName);
|
||||
snprintf(path, sizeof(path), "%s\\%s.pal", "art\\intrface", fileName);
|
||||
colorPaletteLoad(path);
|
||||
}
|
||||
}
|
||||
@@ -1200,4 +1229,14 @@ char* endgameDeathEndingGetFileName()
|
||||
return gEndgameDeathEndingFileName;
|
||||
}
|
||||
|
||||
void endgameEndingUpdateOverlay()
|
||||
{
|
||||
bufferFill(windowGetBuffer(gEndgameEndingOverlay),
|
||||
windowGetWidth(gEndgameEndingOverlay),
|
||||
windowGetHeight(gEndgameEndingOverlay),
|
||||
windowGetWidth(gEndgameEndingOverlay),
|
||||
intensityColorTable[_colorTable[0]][0]);
|
||||
windowRefresh(gEndgameEndingOverlay);
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
#define FILE_FIND_H
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
|
||||
+2
-10
@@ -150,16 +150,8 @@ int _gzdecompress_file(const char* existingFilePath, const char* newFilePath)
|
||||
|
||||
static void fileCopy(const char* existingFilePath, const char* newFilePath)
|
||||
{
|
||||
char nativeExistingFilePath[COMPAT_MAX_PATH];
|
||||
strcpy(nativeExistingFilePath, existingFilePath);
|
||||
compat_windows_path_to_native(nativeExistingFilePath);
|
||||
|
||||
char nativeNewFilePath[COMPAT_MAX_PATH];
|
||||
strcpy(nativeNewFilePath, newFilePath);
|
||||
compat_windows_path_to_native(nativeNewFilePath);
|
||||
|
||||
FILE* in = fopen(nativeExistingFilePath, "rb");
|
||||
FILE* out = fopen(nativeNewFilePath, "wb");
|
||||
FILE* in = compat_fopen(existingFilePath, "rb");
|
||||
FILE* out = compat_fopen(newFilePath, "wb");
|
||||
if (in != NULL && out != NULL) {
|
||||
std::vector<unsigned char> buffer(0xFFFF);
|
||||
|
||||
|
||||
+59
-38
@@ -119,64 +119,89 @@ static int interfaceFontLoad(int font_index)
|
||||
InterfaceFontDescriptor* fontDescriptor = &(gInterfaceFontDescriptors[font_index]);
|
||||
|
||||
char path[56];
|
||||
sprintf(path, "font%d.aaf", font_index);
|
||||
snprintf(path, sizeof(path), "font%d.aaf", font_index);
|
||||
|
||||
File* stream = fileOpen(path, "rb");
|
||||
if (stream == NULL) {
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fileSize = fileGetSize(stream);
|
||||
|
||||
int sig;
|
||||
if (fileRead(&sig, 4, 1, stream) != 1) goto err;
|
||||
if (fileRead(&sig, 4, 1, stream) != 1) {
|
||||
fileClose(stream);
|
||||
return -1;
|
||||
}
|
||||
|
||||
interfaceFontByteSwapInt32(&sig);
|
||||
if (sig != 0x41414646) goto err;
|
||||
if (sig != 0x41414646) {
|
||||
fileClose(stream);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fileRead(&(fontDescriptor->maxHeight), 2, 1, stream) != 1) goto err;
|
||||
if (fileRead(&(fontDescriptor->maxHeight), 2, 1, stream) != 1) {
|
||||
fileClose(stream);
|
||||
return -1;
|
||||
}
|
||||
interfaceFontByteSwapInt16(&(fontDescriptor->maxHeight));
|
||||
|
||||
if (fileRead(&(fontDescriptor->letterSpacing), 2, 1, stream) != 1) goto err;
|
||||
if (fileRead(&(fontDescriptor->letterSpacing), 2, 1, stream) != 1) {
|
||||
fileClose(stream);
|
||||
return -1;
|
||||
}
|
||||
interfaceFontByteSwapInt16(&(fontDescriptor->letterSpacing));
|
||||
|
||||
if (fileRead(&(fontDescriptor->wordSpacing), 2, 1, stream) != 1) goto err;
|
||||
if (fileRead(&(fontDescriptor->wordSpacing), 2, 1, stream) != 1) {
|
||||
fileClose(stream);
|
||||
return -1;
|
||||
}
|
||||
interfaceFontByteSwapInt16(&(fontDescriptor->wordSpacing));
|
||||
|
||||
if (fileRead(&(fontDescriptor->lineSpacing), 2, 1, stream) != 1) goto err;
|
||||
if (fileRead(&(fontDescriptor->lineSpacing), 2, 1, stream) != 1) {
|
||||
fileClose(stream);
|
||||
return -1;
|
||||
}
|
||||
interfaceFontByteSwapInt16(&(fontDescriptor->lineSpacing));
|
||||
|
||||
for (int index = 0; index < 256; index++) {
|
||||
InterfaceFontGlyph* glyph = &(fontDescriptor->glyphs[index]);
|
||||
|
||||
if (fileRead(&(glyph->width), 2, 1, stream) != 1) goto err;
|
||||
if (fileRead(&(glyph->width), 2, 1, stream) != 1) {
|
||||
fileClose(stream);
|
||||
return -1;
|
||||
}
|
||||
interfaceFontByteSwapInt16(&(glyph->width));
|
||||
|
||||
if (fileRead(&(glyph->height), 2, 1, stream) != 1) goto err;
|
||||
if (fileRead(&(glyph->height), 2, 1, stream) != 1) {
|
||||
fileClose(stream);
|
||||
return -1;
|
||||
}
|
||||
interfaceFontByteSwapInt16(&(glyph->height));
|
||||
|
||||
if (fileRead(&(glyph->offset), 4, 1, stream) != 1) goto err;
|
||||
if (fileRead(&(glyph->offset), 4, 1, stream) != 1) {
|
||||
fileClose(stream);
|
||||
return -1;
|
||||
}
|
||||
interfaceFontByteSwapInt32(&(glyph->offset));
|
||||
}
|
||||
|
||||
fileSize -= sizeof(InterfaceFontDescriptor);
|
||||
int glyphDataSize = fileSize - 2060;
|
||||
|
||||
fontDescriptor->data = (unsigned char*)internal_malloc_safe(fileSize, __FILE__, __LINE__); // FONTMGR.C, 259
|
||||
if (fontDescriptor->data == NULL) goto err;
|
||||
fontDescriptor->data = (unsigned char*)internal_malloc_safe(glyphDataSize, __FILE__, __LINE__); // FONTMGR.C, 259
|
||||
if (fontDescriptor->data == NULL) {
|
||||
fileClose(stream);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fileRead(fontDescriptor->data, fileSize, 1, stream) != 1) {
|
||||
if (fileRead(fontDescriptor->data, glyphDataSize, 1, stream) != 1) {
|
||||
internal_free_safe(fontDescriptor->data, __FILE__, __LINE__); // FONTMGR.C, 268
|
||||
goto err;
|
||||
fileClose(stream);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fileClose(stream);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
fileClose(stream);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 0x442120
|
||||
@@ -211,26 +236,22 @@ static int interfaceFontGetStringWidthImpl(const char* string)
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* pch = string;
|
||||
int width = 0;
|
||||
int stringWidth = 0;
|
||||
|
||||
while (*pch != '\0') {
|
||||
int v3;
|
||||
int v4;
|
||||
while (*string != '\0') {
|
||||
unsigned char ch = static_cast<unsigned char>(*string++);
|
||||
|
||||
if (*pch == ' ') {
|
||||
v3 = gCurrentInterfaceFontDescriptor->letterSpacing;
|
||||
v4 = gCurrentInterfaceFontDescriptor->wordSpacing;
|
||||
int characterWidth;
|
||||
if (ch == ' ') {
|
||||
characterWidth = gCurrentInterfaceFontDescriptor->wordSpacing;
|
||||
} else {
|
||||
v3 = gCurrentInterfaceFontDescriptor->glyphs[*pch & 0xFF].width;
|
||||
v4 = gCurrentInterfaceFontDescriptor->letterSpacing;
|
||||
characterWidth = gCurrentInterfaceFontDescriptor->glyphs[ch].width;
|
||||
}
|
||||
width += v3 + v4;
|
||||
|
||||
pch++;
|
||||
stringWidth += characterWidth + gCurrentInterfaceFontDescriptor->letterSpacing;
|
||||
}
|
||||
|
||||
return width;
|
||||
return stringWidth;
|
||||
}
|
||||
|
||||
// 0x4421DC
|
||||
@@ -322,13 +343,13 @@ static void interfaceFontDrawImpl(unsigned char* buf, const char* string, int le
|
||||
|
||||
unsigned char* ptr = buf;
|
||||
while (*string != '\0') {
|
||||
char ch = *string++;
|
||||
unsigned char ch = static_cast<unsigned char>(*string++);
|
||||
|
||||
int characterWidth;
|
||||
if (ch == ' ') {
|
||||
characterWidth = gCurrentInterfaceFontDescriptor->wordSpacing;
|
||||
} else {
|
||||
characterWidth = gCurrentInterfaceFontDescriptor->glyphs[ch & 0xFF].width;
|
||||
characterWidth = gCurrentInterfaceFontDescriptor->glyphs[ch].width;
|
||||
}
|
||||
|
||||
unsigned char* end;
|
||||
@@ -343,7 +364,7 @@ static void interfaceFontDrawImpl(unsigned char* buf, const char* string, int le
|
||||
break;
|
||||
}
|
||||
|
||||
InterfaceFontGlyph* glyph = &(gCurrentInterfaceFontDescriptor->glyphs[ch & 0xFF]);
|
||||
InterfaceFontGlyph* glyph = &(gCurrentInterfaceFontDescriptor->glyphs[ch]);
|
||||
unsigned char* glyphDataPtr = gCurrentInterfaceFontDescriptor->data + glyph->offset;
|
||||
|
||||
// Skip blank pixels (difference between font's line height and glyph height).
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
namespace fallout {
|
||||
|
||||
FpsLimiter::FpsLimiter(std::size_t fps)
|
||||
FpsLimiter::FpsLimiter(unsigned int fps)
|
||||
: _fps(fps)
|
||||
, _ticks(0)
|
||||
{
|
||||
|
||||
+3
-5
@@ -1,19 +1,17 @@
|
||||
#ifndef FPS_LIMITER_H
|
||||
#define FPS_LIMITER_H
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace fallout {
|
||||
|
||||
class FpsLimiter {
|
||||
public:
|
||||
FpsLimiter(std::size_t fps = 60);
|
||||
FpsLimiter(unsigned int fps = 60);
|
||||
void mark();
|
||||
void throttle() const;
|
||||
|
||||
private:
|
||||
const std::size_t _fps;
|
||||
std::size_t _ticks;
|
||||
const unsigned int _fps;
|
||||
unsigned int _ticks;
|
||||
};
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
+284
-109
@@ -3,12 +3,6 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h> // access
|
||||
#endif
|
||||
|
||||
#include "actions.h"
|
||||
#include "animation.h"
|
||||
#include "art.h"
|
||||
@@ -18,7 +12,6 @@
|
||||
#include "color.h"
|
||||
#include "combat.h"
|
||||
#include "combat_ai.h"
|
||||
#include "core.h"
|
||||
#include "critter.h"
|
||||
#include "cycle.h"
|
||||
#include "db.h"
|
||||
@@ -26,21 +19,22 @@
|
||||
#include "debug.h"
|
||||
#include "display_monitor.h"
|
||||
#include "draw.h"
|
||||
#include "electronic_registration.h"
|
||||
#include "endgame.h"
|
||||
#include "font_manager.h"
|
||||
#include "game_config.h"
|
||||
#include "game_dialog.h"
|
||||
#include "game_memory.h"
|
||||
#include "game_mouse.h"
|
||||
#include "game_movie.h"
|
||||
#include "game_sound.h"
|
||||
#include "input.h"
|
||||
#include "interface.h"
|
||||
#include "inventory.h"
|
||||
#include "item.h"
|
||||
#include "kb.h"
|
||||
#include "loadsave.h"
|
||||
#include "map.h"
|
||||
#include "memory.h"
|
||||
#include "mouse.h"
|
||||
#include "movie.h"
|
||||
#include "movie_effect.h"
|
||||
#include "object.h"
|
||||
@@ -50,18 +44,25 @@
|
||||
#include "perk.h"
|
||||
#include "pipboy.h"
|
||||
#include "platform_compat.h"
|
||||
#include "preferences.h"
|
||||
#include "proto.h"
|
||||
#include "queue.h"
|
||||
#include "random.h"
|
||||
#include "scripts.h"
|
||||
#include "settings.h"
|
||||
#include "sfall_arrays.h"
|
||||
#include "sfall_config.h"
|
||||
#include "sfall_global_scripts.h"
|
||||
#include "sfall_global_vars.h"
|
||||
#include "sfall_ini.h"
|
||||
#include "sfall_lists.h"
|
||||
#include "skill.h"
|
||||
#include "skilldex.h"
|
||||
#include "stat.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "tile.h"
|
||||
#include "trait.h"
|
||||
#include "trap.h"
|
||||
#include "version.h"
|
||||
#include "window_manager.h"
|
||||
#include "worldmap.h"
|
||||
@@ -92,7 +93,7 @@ static char _aDec11199816543[] = VERSION_BUILD_TIME;
|
||||
static bool gGameUiDisabled = false;
|
||||
|
||||
// 0x5186B8
|
||||
static int _game_state_cur = GAME_STATE_0;
|
||||
static int gGameState = GAME_STATE_0;
|
||||
|
||||
// 0x5186BC
|
||||
static bool gIsMapper = false;
|
||||
@@ -114,15 +115,8 @@ int _game_user_wants_to_quit = 0;
|
||||
// 0x58E940
|
||||
MessageList gMiscMessageList;
|
||||
|
||||
// master.dat loading result
|
||||
//
|
||||
// 0x58E948
|
||||
int _master_db_handle;
|
||||
|
||||
// critter.dat loading result
|
||||
//
|
||||
// 0x58E94C
|
||||
int _critter_db_handle;
|
||||
// CE: Sonora folks like to store objects in global variables.
|
||||
static void** gGameGlobalPointers = nullptr;
|
||||
|
||||
// 0x442580
|
||||
int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4, int argc, char** argv)
|
||||
@@ -137,32 +131,33 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4
|
||||
// override it's file name.
|
||||
sfallConfigInit(argc, argv);
|
||||
|
||||
gameConfigInit(isMapper, argc, argv);
|
||||
settingsInit(isMapper, argc, argv);
|
||||
|
||||
gIsMapper = isMapper;
|
||||
|
||||
if (gameDbInit() == -1) {
|
||||
gameConfigExit(false);
|
||||
settingsExit(false);
|
||||
sfallConfigExit();
|
||||
return -1;
|
||||
}
|
||||
|
||||
runElectronicRegistration();
|
||||
// Message list repository is considered a specialized file manager, so
|
||||
// it should be initialized early in the process.
|
||||
messageListRepositoryInit();
|
||||
|
||||
programWindowSetTitle(windowTitle);
|
||||
_initWindow(1, a4);
|
||||
paletteInit();
|
||||
|
||||
char* language;
|
||||
if (configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_LANGUAGE_KEY, &language)) {
|
||||
if (compat_stricmp(language, FRENCH) == 0) {
|
||||
keyboardSetLayout(KEYBOARD_LAYOUT_FRENCH);
|
||||
} else if (compat_stricmp(language, GERMAN) == 0) {
|
||||
keyboardSetLayout(KEYBOARD_LAYOUT_GERMAN);
|
||||
} else if (compat_stricmp(language, ITALIAN) == 0) {
|
||||
keyboardSetLayout(KEYBOARD_LAYOUT_ITALIAN);
|
||||
} else if (compat_stricmp(language, SPANISH) == 0) {
|
||||
keyboardSetLayout(KEYBOARD_LAYOUT_SPANISH);
|
||||
}
|
||||
const char* language = settings.system.language.c_str();
|
||||
if (compat_stricmp(language, FRENCH) == 0) {
|
||||
keyboardSetLayout(KEYBOARD_LAYOUT_FRENCH);
|
||||
} else if (compat_stricmp(language, GERMAN) == 0) {
|
||||
keyboardSetLayout(KEYBOARD_LAYOUT_GERMAN);
|
||||
} else if (compat_stricmp(language, ITALIAN) == 0) {
|
||||
keyboardSetLayout(KEYBOARD_LAYOUT_ITALIAN);
|
||||
} else if (compat_stricmp(language, SPANISH) == 0) {
|
||||
keyboardSetLayout(KEYBOARD_LAYOUT_SPANISH);
|
||||
}
|
||||
|
||||
// SFALL: Allow to skip splash screen
|
||||
@@ -173,8 +168,6 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4
|
||||
showSplash();
|
||||
}
|
||||
|
||||
_trap_init();
|
||||
|
||||
interfaceFontsInit();
|
||||
fontManagerAdd(&gModernFontManager);
|
||||
fontSetCurrent(font);
|
||||
@@ -315,7 +308,7 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4
|
||||
|
||||
debugPrint(">message_init\t");
|
||||
|
||||
sprintf(path, "%s%s", asc_5186C8, "misc.msg");
|
||||
snprintf(path, sizeof(path), "%s%s", asc_5186C8, "misc.msg");
|
||||
|
||||
if (!messageListLoad(&gMiscMessageList, path)) {
|
||||
debugPrint("Failed on message_load\n");
|
||||
@@ -348,6 +341,32 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4
|
||||
// SFALL
|
||||
premadeCharactersInit();
|
||||
|
||||
if (!sfall_gl_vars_init()) {
|
||||
debugPrint("Failed on sfall_gl_vars_init");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!sfallListsInit()) {
|
||||
debugPrint("Failed on sfallListsInit");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!sfallArraysInit()) {
|
||||
debugPrint("Failed on sfallArraysInit");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!sfall_gl_scr_init()) {
|
||||
debugPrint("Failed on sfall_gl_scr_init");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char* customConfigBasePath;
|
||||
configGetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_INI_CONFIG_FOLDER, &customConfigBasePath);
|
||||
sfall_ini_set_base_path(customConfigBasePath);
|
||||
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_MISC, &gMiscMessageList);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -388,6 +407,13 @@ void gameReset()
|
||||
_game_user_wants_to_quit = 0;
|
||||
automapReset();
|
||||
_init_options_menu();
|
||||
|
||||
// SFALL
|
||||
sfall_gl_vars_reset();
|
||||
sfallListsReset();
|
||||
messageListRepositoryReset();
|
||||
sfallArraysReset();
|
||||
sfall_gl_scr_reset();
|
||||
}
|
||||
|
||||
// 0x442C34
|
||||
@@ -396,9 +422,14 @@ void gameExit()
|
||||
debugPrint("\nGame Exit\n");
|
||||
|
||||
// SFALL
|
||||
sfall_gl_scr_exit();
|
||||
sfallArraysExit();
|
||||
sfallListsExit();
|
||||
sfall_gl_vars_exit();
|
||||
premadeCharactersExit();
|
||||
|
||||
tileDisable();
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_MISC, nullptr);
|
||||
messageListFree(&gMiscMessageList);
|
||||
combatExit();
|
||||
gameDialogExit();
|
||||
@@ -431,10 +462,10 @@ void gameExit()
|
||||
partyMembersExit();
|
||||
endgameDeathEndingExit();
|
||||
interfaceFontsExit();
|
||||
_trap_init();
|
||||
_windowClose();
|
||||
messageListRepositoryExit();
|
||||
dbExit();
|
||||
gameConfigExit(true);
|
||||
settingsExit(true);
|
||||
sfallConfigExit();
|
||||
}
|
||||
|
||||
@@ -442,7 +473,7 @@ void gameExit()
|
||||
int gameHandleKey(int eventCode, bool isInCombatMode)
|
||||
{
|
||||
// NOTE: Uninline.
|
||||
if (_game_state() == GAME_STATE_5) {
|
||||
if (gameGetState() == GAME_STATE_5) {
|
||||
_gdialogSystemEnter();
|
||||
}
|
||||
|
||||
@@ -696,7 +727,7 @@ int gameHandleKey(int eventCode, bool isInCombatMode)
|
||||
}
|
||||
|
||||
if (gIsMapper) {
|
||||
tileSetCenter(gDude->tile, TILE_SET_CENTER_FLAG_0x01);
|
||||
tileSetCenter(gDude->tile, TILE_SET_CENTER_REFRESH_WINDOW);
|
||||
} else {
|
||||
_tile_scroll_to(gDude->tile, 2);
|
||||
}
|
||||
@@ -801,7 +832,7 @@ int gameHandleKey(int eventCode, bool isInCombatMode)
|
||||
MessageList messageList;
|
||||
if (messageListInit(&messageList)) {
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "%s%s", asc_5186C8, "editor.msg");
|
||||
snprintf(path, sizeof(path), "%s%s", asc_5186C8, "editor.msg");
|
||||
|
||||
if (messageListLoad(&messageList, path)) {
|
||||
MessageListItem messageListItem;
|
||||
@@ -810,7 +841,7 @@ int gameHandleKey(int eventCode, bool isInCombatMode)
|
||||
char* time = gameTimeGetTimeString();
|
||||
|
||||
char date[128];
|
||||
sprintf(date, "%s: %d/%d %s", messageListItem.text, day, year, time);
|
||||
snprintf(date, sizeof(date), "%s: %d/%d %s", messageListItem.text, day, year, time);
|
||||
|
||||
displayMonitorAddMessage(date);
|
||||
}
|
||||
@@ -879,7 +910,7 @@ int gameHandleKey(int eventCode, bool isInCombatMode)
|
||||
soundPlayFile("ib1p1xx1");
|
||||
|
||||
char version[VERSION_MAX];
|
||||
versionGetVersion(version);
|
||||
versionGetVersion(version, sizeof(version));
|
||||
displayMonitorAddMessage(version);
|
||||
displayMonitorAddMessage(_aDec11199816543);
|
||||
}
|
||||
@@ -963,9 +994,9 @@ int gameSetGlobalVar(int var, int value)
|
||||
if (diff != 0) {
|
||||
char formattedMessage[80];
|
||||
if (diff > 0) {
|
||||
sprintf(formattedMessage, "You gained %d karma.", diff);
|
||||
snprintf(formattedMessage, sizeof(formattedMessage), "You gained %d karma.", diff);
|
||||
} else {
|
||||
sprintf(formattedMessage, "You lost %d karma.", -diff);
|
||||
snprintf(formattedMessage, sizeof(formattedMessage), "You lost %d karma.", -diff);
|
||||
}
|
||||
displayMonitorAddMessage(formattedMessage);
|
||||
}
|
||||
@@ -981,7 +1012,18 @@ int gameSetGlobalVar(int var, int value)
|
||||
// 0x443CC8
|
||||
static int gameLoadGlobalVars()
|
||||
{
|
||||
return globalVarsRead("data\\vault13.gam", "GAME_GLOBAL_VARS:", &gGameGlobalVarsLength, &gGameGlobalVars);
|
||||
if (globalVarsRead("data\\vault13.gam", "GAME_GLOBAL_VARS:", &gGameGlobalVarsLength, &gGameGlobalVars) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
gGameGlobalPointers = reinterpret_cast<void**>(internal_malloc(sizeof(*gGameGlobalPointers) * gGameGlobalVarsLength));
|
||||
if (gGameGlobalPointers == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(gGameGlobalPointers, 0, sizeof(*gGameGlobalPointers) * gGameGlobalVarsLength);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x443CE8
|
||||
@@ -1044,48 +1086,48 @@ int globalVarsRead(const char* path, const char* section, int* variablesListLeng
|
||||
}
|
||||
|
||||
// 0x443E2C
|
||||
int _game_state()
|
||||
int gameGetState()
|
||||
{
|
||||
return _game_state_cur;
|
||||
return gGameState;
|
||||
}
|
||||
|
||||
// 0x443E34
|
||||
int _game_state_request(int a1)
|
||||
int gameRequestState(int newGameState)
|
||||
{
|
||||
if (a1 == GAME_STATE_0) {
|
||||
a1 = GAME_STATE_1;
|
||||
} else if (a1 == GAME_STATE_2) {
|
||||
a1 = GAME_STATE_3;
|
||||
} else if (a1 == GAME_STATE_4) {
|
||||
a1 = GAME_STATE_5;
|
||||
switch (newGameState) {
|
||||
case GAME_STATE_0:
|
||||
newGameState = GAME_STATE_1;
|
||||
break;
|
||||
case GAME_STATE_2:
|
||||
newGameState = GAME_STATE_3;
|
||||
break;
|
||||
case GAME_STATE_4:
|
||||
newGameState = GAME_STATE_5;
|
||||
break;
|
||||
}
|
||||
|
||||
if (_game_state_cur != GAME_STATE_4 || a1 != GAME_STATE_5) {
|
||||
_game_state_cur = a1;
|
||||
return 0;
|
||||
if (gGameState == GAME_STATE_4 && newGameState == GAME_STATE_5) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
gGameState = newGameState;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x443E90
|
||||
void _game_state_update()
|
||||
void gameUpdateState()
|
||||
{
|
||||
int v0;
|
||||
|
||||
v0 = _game_state_cur;
|
||||
switch (_game_state_cur) {
|
||||
switch (gGameState) {
|
||||
case GAME_STATE_1:
|
||||
v0 = GAME_STATE_0;
|
||||
gGameState = GAME_STATE_0;
|
||||
break;
|
||||
case GAME_STATE_3:
|
||||
v0 = GAME_STATE_2;
|
||||
gGameState = GAME_STATE_2;
|
||||
break;
|
||||
case GAME_STATE_5:
|
||||
v0 = GAME_STATE_4;
|
||||
gGameState = GAME_STATE_4;
|
||||
break;
|
||||
}
|
||||
|
||||
_game_state_cur = v0;
|
||||
}
|
||||
|
||||
// 0x443EF0
|
||||
@@ -1122,11 +1164,18 @@ static void gameFreeGlobalVars()
|
||||
internal_free(gGameGlobalVars);
|
||||
gGameGlobalVars = NULL;
|
||||
}
|
||||
|
||||
if (gGameGlobalPointers != nullptr) {
|
||||
internal_free(gGameGlobalPointers);
|
||||
gGameGlobalPointers = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x443F74
|
||||
static void showHelp()
|
||||
{
|
||||
ScopedGameMode gm(GameMode::kHelp);
|
||||
|
||||
bool isoWasEnabled = isoDisable();
|
||||
gameMouseObjectsHide();
|
||||
|
||||
@@ -1135,9 +1184,13 @@ static void showHelp()
|
||||
bool colorCycleWasEnabled = colorCycleEnabled();
|
||||
colorCycleDisable();
|
||||
|
||||
// CE: Help screen uses separate color palette which is incompatible with
|
||||
// colors in other windows. Setup overlay to hide everything.
|
||||
int overlay = windowCreate(0, 0, screenGetWidth(), screenGetHeight(), 0, WINDOW_HIDDEN | WINDOW_MOVE_ON_TOP);
|
||||
|
||||
int helpWindowX = (screenGetWidth() - HELP_SCREEN_WIDTH) / 2;
|
||||
int helpWindowY = (screenGetHeight() - HELP_SCREEN_HEIGHT) / 2;
|
||||
int win = windowCreate(helpWindowX, helpWindowY, HELP_SCREEN_WIDTH, HELP_SCREEN_HEIGHT, 0, WINDOW_HIDDEN | WINDOW_FLAG_0x04);
|
||||
int win = windowCreate(helpWindowX, helpWindowY, HELP_SCREEN_WIDTH, HELP_SCREEN_HEIGHT, 0, WINDOW_HIDDEN | WINDOW_MOVE_ON_TOP);
|
||||
if (win != -1) {
|
||||
unsigned char* windowBuffer = windowGetBuffer(win);
|
||||
if (windowBuffer != NULL) {
|
||||
@@ -1146,21 +1199,41 @@ static void showHelp()
|
||||
if (backgroundFrmImage.lock(backgroundFid)) {
|
||||
paletteSetEntries(gPaletteBlack);
|
||||
blitBufferToBuffer(backgroundFrmImage.getData(), HELP_SCREEN_WIDTH, HELP_SCREEN_HEIGHT, HELP_SCREEN_WIDTH, windowBuffer, HELP_SCREEN_WIDTH);
|
||||
windowUnhide(win);
|
||||
|
||||
colorPaletteLoad("art\\intrface\\helpscrn.pal");
|
||||
paletteSetEntries(_cmap);
|
||||
|
||||
while (_get_input() == -1 && _game_user_wants_to_quit == 0) {
|
||||
// CE: Fill overlay with darkest color in the palette. It might
|
||||
// not be completely black, but at least it's uniform.
|
||||
bufferFill(windowGetBuffer(overlay),
|
||||
screenGetWidth(),
|
||||
screenGetHeight(),
|
||||
screenGetWidth(),
|
||||
intensityColorTable[_colorTable[0]][0]);
|
||||
|
||||
windowShow(overlay);
|
||||
windowShow(win);
|
||||
|
||||
while (inputGetInput() == -1 && _game_user_wants_to_quit == 0) {
|
||||
sharedFpsLimiter.mark();
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
while (mouseGetEvent() != 0) {
|
||||
_get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
inputGetInput();
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
paletteSetEntries(gPaletteBlack);
|
||||
}
|
||||
}
|
||||
|
||||
windowDestroy(overlay);
|
||||
windowDestroy(win);
|
||||
colorPaletteLoad("color.pal");
|
||||
paletteSetEntries(_cmap);
|
||||
@@ -1235,62 +1308,57 @@ int showQuitConfirmationDialog()
|
||||
// 0x44418C
|
||||
static int gameDbInit()
|
||||
{
|
||||
int hashing;
|
||||
char* main_file_name;
|
||||
char* patch_file_name;
|
||||
const char* main_file_name;
|
||||
const char* patch_file_name;
|
||||
int patch_index;
|
||||
char filename[COMPAT_MAX_PATH];
|
||||
|
||||
hashing = 0;
|
||||
main_file_name = NULL;
|
||||
patch_file_name = NULL;
|
||||
|
||||
if (configGetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_HASHING_KEY, &hashing)) {
|
||||
_db_enable_hash_table_();
|
||||
}
|
||||
|
||||
configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_MASTER_DAT_KEY, &main_file_name);
|
||||
main_file_name = settings.system.master_dat_path.c_str();
|
||||
if (*main_file_name == '\0') {
|
||||
main_file_name = NULL;
|
||||
}
|
||||
|
||||
configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_MASTER_PATCHES_KEY, &patch_file_name);
|
||||
patch_file_name = settings.system.master_patches_path.c_str();
|
||||
if (*patch_file_name == '\0') {
|
||||
patch_file_name = NULL;
|
||||
}
|
||||
|
||||
_master_db_handle = dbOpen(main_file_name, 0, patch_file_name, 1);
|
||||
if (_master_db_handle == -1) {
|
||||
int master_db_handle = dbOpen(main_file_name, 0, patch_file_name, 1);
|
||||
if (master_db_handle == -1) {
|
||||
showMesageBox("Could not find the master datafile. Please make sure the FALLOUT CD is in the drive and that you are running FALLOUT from the directory you installed it to.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CRITTER_DAT_KEY, &main_file_name);
|
||||
main_file_name = settings.system.critter_dat_path.c_str();
|
||||
if (*main_file_name == '\0') {
|
||||
main_file_name = NULL;
|
||||
}
|
||||
|
||||
configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CRITTER_PATCHES_KEY, &patch_file_name);
|
||||
patch_file_name = settings.system.critter_patches_path.c_str();
|
||||
if (*patch_file_name == '\0') {
|
||||
patch_file_name = NULL;
|
||||
}
|
||||
|
||||
_critter_db_handle = dbOpen(main_file_name, 0, patch_file_name, 1);
|
||||
if (_critter_db_handle == -1) {
|
||||
_db_select(_master_db_handle);
|
||||
int critter_db_handle = dbOpen(main_file_name, 0, patch_file_name, 1);
|
||||
if (critter_db_handle == -1) {
|
||||
showMesageBox("Could not find the critter datafile. Please make sure the FALLOUT CD is in the drive and that you are running FALLOUT from the directory you installed it to.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (patch_index = 0; patch_index < 1000; patch_index++) {
|
||||
sprintf(filename, "patch%03d.dat", patch_index);
|
||||
snprintf(filename, sizeof(filename), "patch%03d.dat", patch_index);
|
||||
|
||||
if (access(filename, 0) == 0) {
|
||||
if (compat_access(filename, 0) == 0) {
|
||||
dbOpen(filename, 0, NULL, 1);
|
||||
}
|
||||
}
|
||||
|
||||
_db_select(_master_db_handle);
|
||||
if (compat_access("f2_res.dat", 0) == 0) {
|
||||
dbOpen("f2_res.dat", 0, NULL, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1298,21 +1366,20 @@ static int gameDbInit()
|
||||
// 0x444384
|
||||
static void showSplash()
|
||||
{
|
||||
int splash;
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_SPLASH_KEY, &splash);
|
||||
int splash = settings.system.splash;
|
||||
|
||||
char path[64];
|
||||
char* language;
|
||||
if (configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_LANGUAGE_KEY, &language) && compat_stricmp(language, ENGLISH) != 0) {
|
||||
sprintf(path, "art\\%s\\splash\\", language);
|
||||
const char* language = settings.system.language.c_str();
|
||||
if (compat_stricmp(language, ENGLISH) != 0) {
|
||||
snprintf(path, sizeof(path), "art\\%s\\splash\\", language);
|
||||
} else {
|
||||
sprintf(path, "art\\splash\\");
|
||||
snprintf(path, sizeof(path), "art\\splash\\");
|
||||
}
|
||||
|
||||
File* stream;
|
||||
for (int index = 0; index < SPLASH_COUNT; index++) {
|
||||
char filePath[64];
|
||||
sprintf(filePath, "%ssplash%d.rix", path, splash);
|
||||
snprintf(filePath, sizeof(filePath), "%ssplash%d.rix", path, splash);
|
||||
stream = fileOpen(filePath, "rb");
|
||||
if (stream != NULL) {
|
||||
break;
|
||||
@@ -1329,13 +1396,26 @@ static void showSplash()
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char* palette = (unsigned char*)internal_malloc(768);
|
||||
unsigned char* palette = reinterpret_cast<unsigned char*>(internal_malloc(768));
|
||||
if (palette == NULL) {
|
||||
fileClose(stream);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char* data = (unsigned char*)internal_malloc(SPLASH_WIDTH * SPLASH_HEIGHT);
|
||||
int version;
|
||||
fileReadInt32(stream, &version);
|
||||
if (version != 'RIX3') {
|
||||
fileClose(stream);
|
||||
return;
|
||||
}
|
||||
|
||||
short width;
|
||||
fileRead(&width, sizeof(width), 1, stream);
|
||||
|
||||
short height;
|
||||
fileRead(&height, sizeof(height), 1, stream);
|
||||
|
||||
unsigned char* data = reinterpret_cast<unsigned char*>(internal_malloc(width * height));
|
||||
if (data == NULL) {
|
||||
internal_free(palette);
|
||||
fileClose(stream);
|
||||
@@ -1345,18 +1425,63 @@ static void showSplash()
|
||||
paletteSetEntries(gPaletteBlack);
|
||||
fileSeek(stream, 10, SEEK_SET);
|
||||
fileRead(palette, 1, 768, stream);
|
||||
fileRead(data, 1, SPLASH_WIDTH * SPLASH_HEIGHT, stream);
|
||||
fileRead(data, 1, width * height, stream);
|
||||
fileClose(stream);
|
||||
|
||||
int splashWindowX = (screenGetWidth() - SPLASH_WIDTH) / 2;
|
||||
int splashWindowY = (screenGetHeight() - SPLASH_HEIGHT) / 2;
|
||||
_scr_blit(data, SPLASH_WIDTH, SPLASH_HEIGHT, 0, 0, SPLASH_WIDTH, SPLASH_HEIGHT, splashWindowX, splashWindowY);
|
||||
paletteFadeTo(palette);
|
||||
int size = 0;
|
||||
|
||||
// TODO: Move to settings.
|
||||
Config config;
|
||||
if (configInit(&config)) {
|
||||
if (configRead(&config, "f2_res.ini", false)) {
|
||||
configGetInt(&config, "STATIC_SCREENS", "SPLASH_SCRN_SIZE", &size);
|
||||
}
|
||||
|
||||
configFree(&config);
|
||||
}
|
||||
|
||||
int screenWidth = screenGetWidth();
|
||||
int screenHeight = screenGetHeight();
|
||||
|
||||
if (size != 0 || screenWidth < width || screenHeight < height) {
|
||||
int scaledWidth;
|
||||
int scaledHeight;
|
||||
|
||||
if (size == 2) {
|
||||
scaledWidth = screenWidth;
|
||||
scaledHeight = screenHeight;
|
||||
} else {
|
||||
if (screenHeight * width >= screenWidth * height) {
|
||||
scaledWidth = screenWidth;
|
||||
scaledHeight = screenWidth * height / width;
|
||||
} else {
|
||||
scaledWidth = screenHeight * width / height;
|
||||
scaledHeight = screenHeight;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char* scaled = reinterpret_cast<unsigned char*>(internal_malloc(scaledWidth * scaledHeight));
|
||||
if (scaled != NULL) {
|
||||
blitBufferToBufferStretch(data, width, height, width, scaled, scaledWidth, scaledHeight, scaledWidth);
|
||||
|
||||
int x = screenWidth > scaledWidth ? (screenWidth - scaledWidth) / 2 : 0;
|
||||
int y = screenHeight > scaledHeight ? (screenHeight - scaledHeight) / 2 : 0;
|
||||
_scr_blit(scaled, scaledWidth, scaledHeight, 0, 0, scaledWidth, scaledHeight, x, y);
|
||||
paletteFadeTo(palette);
|
||||
|
||||
internal_free(scaled);
|
||||
}
|
||||
} else {
|
||||
int x = (screenWidth - width) / 2;
|
||||
int y = (screenHeight - height) / 2;
|
||||
_scr_blit(data, width, height, 0, 0, width, height, x, y);
|
||||
paletteFadeTo(palette);
|
||||
}
|
||||
|
||||
internal_free(data);
|
||||
internal_free(palette);
|
||||
|
||||
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_SPLASH_KEY, splash + 1);
|
||||
settings.system.splash = splash + 1;
|
||||
}
|
||||
|
||||
int gameShowDeathDialog(const char* message)
|
||||
@@ -1406,4 +1531,54 @@ int gameShowDeathDialog(const char* message)
|
||||
return rc;
|
||||
}
|
||||
|
||||
void* gameGetGlobalPointer(int var)
|
||||
{
|
||||
if (var < 0 || var >= gGameGlobalVarsLength) {
|
||||
debugPrint("ERROR: attempt to reference global pointer out of range: %d", var);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return gGameGlobalPointers[var];
|
||||
}
|
||||
|
||||
int gameSetGlobalPointer(int var, void* value)
|
||||
{
|
||||
if (var < 0 || var >= gGameGlobalVarsLength) {
|
||||
debugPrint("ERROR: attempt to reference global var out of range: %d", var);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gGameGlobalPointers[var] = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GameMode::currentGameMode = 0;
|
||||
|
||||
void GameMode::enterGameMode(int gameMode)
|
||||
{
|
||||
currentGameMode |= gameMode;
|
||||
}
|
||||
|
||||
void GameMode::exitGameMode(int gameMode)
|
||||
{
|
||||
currentGameMode &= ~gameMode;
|
||||
}
|
||||
|
||||
bool GameMode::isInGameMode(int gameMode)
|
||||
{
|
||||
return (currentGameMode & gameMode) != 0;
|
||||
}
|
||||
|
||||
ScopedGameMode::ScopedGameMode(int gameMode)
|
||||
{
|
||||
this->gameMode = gameMode;
|
||||
GameMode::enterGameMode(gameMode);
|
||||
}
|
||||
|
||||
ScopedGameMode::~ScopedGameMode()
|
||||
{
|
||||
GameMode::exitGameMode(gameMode);
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
+49
-5
@@ -21,8 +21,6 @@ extern const char* asc_5186C8;
|
||||
extern int _game_user_wants_to_quit;
|
||||
|
||||
extern MessageList gMiscMessageList;
|
||||
extern int _master_db_handle;
|
||||
extern int _critter_db_handle;
|
||||
|
||||
int gameInitWithOptions(const char* windowTitle, bool isMapper, int a3, int a4, int argc, char** argv);
|
||||
void gameReset();
|
||||
@@ -34,12 +32,58 @@ bool gameUiIsDisabled();
|
||||
int gameGetGlobalVar(int var);
|
||||
int gameSetGlobalVar(int var, int value);
|
||||
int globalVarsRead(const char* path, const char* section, int* variablesListLengthPtr, int** variablesListPtr);
|
||||
int _game_state();
|
||||
int _game_state_request(int a1);
|
||||
void _game_state_update();
|
||||
int gameGetState();
|
||||
int gameRequestState(int newGameState);
|
||||
void gameUpdateState();
|
||||
int showQuitConfirmationDialog();
|
||||
|
||||
int gameShowDeathDialog(const char* message);
|
||||
void* gameGetGlobalPointer(int var);
|
||||
int gameSetGlobalPointer(int var, void* value);
|
||||
|
||||
class GameMode {
|
||||
public:
|
||||
enum Flags {
|
||||
kWorldmap = 0x1,
|
||||
kDialog = 0x4,
|
||||
kOptions = 0x8,
|
||||
kSaveGame = 0x10,
|
||||
kLoadGame = 0x20,
|
||||
kCombat = 0x40,
|
||||
kPreferences = 0x80,
|
||||
kHelp = 0x100,
|
||||
kEditor = 0x200,
|
||||
kPipboy = 0x400,
|
||||
kPlayerTurn = 0x800,
|
||||
kInventory = 0x1000,
|
||||
kAutomap = 0x2000,
|
||||
kSkilldex = 0x4000,
|
||||
kUseOn = 0x8000,
|
||||
kLoot = 0x10000,
|
||||
kBarter = 0x20000,
|
||||
kHero = 0x40000,
|
||||
kDialogReview = 0x80000,
|
||||
kCounter = 0x100000,
|
||||
kSpecial = 0x80000000,
|
||||
};
|
||||
|
||||
static void enterGameMode(int gameMode);
|
||||
static void exitGameMode(int gameMode);
|
||||
static bool isInGameMode(int gameMode);
|
||||
static int getCurrentGameMode() { return currentGameMode; }
|
||||
|
||||
private:
|
||||
static int currentGameMode;
|
||||
};
|
||||
|
||||
class ScopedGameMode {
|
||||
public:
|
||||
ScopedGameMode(int gameMode);
|
||||
~ScopedGameMode();
|
||||
|
||||
private:
|
||||
int gameMode;
|
||||
};
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
||||
+1
-1
@@ -125,7 +125,7 @@ bool gameConfigInit(bool isMapper, int argc, char** argv)
|
||||
char* ch = strrchr(executable, '\\');
|
||||
if (ch != NULL) {
|
||||
*ch = '\0';
|
||||
sprintf(gGameConfigFilePath, "%s\\%s", executable, GAME_CONFIG_FILE_NAME);
|
||||
snprintf(gGameConfigFilePath, sizeof(gGameConfigFilePath), "%s\\%s", executable, GAME_CONFIG_FILE_NAME);
|
||||
*ch = '\\';
|
||||
} else {
|
||||
strcpy(gGameConfigFilePath, GAME_CONFIG_FILE_NAME);
|
||||
|
||||
+243
-144
@@ -10,7 +10,6 @@
|
||||
#include "color.h"
|
||||
#include "combat.h"
|
||||
#include "combat_ai.h"
|
||||
#include "core.h"
|
||||
#include "critter.h"
|
||||
#include "cycle.h"
|
||||
#include "debug.h"
|
||||
@@ -20,10 +19,13 @@
|
||||
#include "game.h"
|
||||
#include "game_mouse.h"
|
||||
#include "game_sound.h"
|
||||
#include "input.h"
|
||||
#include "interface.h"
|
||||
#include "item.h"
|
||||
#include "kb.h"
|
||||
#include "lips.h"
|
||||
#include "memory.h"
|
||||
#include "mouse.h"
|
||||
#include "object.h"
|
||||
#include "party_member.h"
|
||||
#include "perk.h"
|
||||
@@ -33,6 +35,7 @@
|
||||
#include "sfall_config.h"
|
||||
#include "skill.h"
|
||||
#include "stat.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "text_object.h"
|
||||
#include "tile.h"
|
||||
@@ -619,6 +622,7 @@ static void gameDialogRedButtonsInit();
|
||||
static void gameDialogRedButtonsExit();
|
||||
|
||||
static bool gGameDialogFix;
|
||||
static bool gNumberOptions;
|
||||
|
||||
// gdialog_init
|
||||
// 0x444D1C
|
||||
@@ -628,6 +632,10 @@ int gameDialogInit()
|
||||
gGameDialogFix = true;
|
||||
configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_GAME_DIALOG_FIX_KEY, &gGameDialogFix);
|
||||
|
||||
// SFALL: Use numbers for replies (instead of default knobs).
|
||||
gNumberOptions = false;
|
||||
configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_NUMBERS_IS_DIALOG_KEY, &gNumberOptions);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -658,9 +666,9 @@ bool _gdialogActive()
|
||||
|
||||
// gdialogEnter
|
||||
// 0x444D3C
|
||||
void gameDialogEnter(Object* a1, int a2)
|
||||
void gameDialogEnter(Object* speaker, int a2)
|
||||
{
|
||||
if (a1 == NULL) {
|
||||
if (speaker == NULL) {
|
||||
debugPrint("\nError: gdialogEnter: target was NULL!");
|
||||
return;
|
||||
}
|
||||
@@ -671,14 +679,14 @@ void gameDialogEnter(Object* a1, int a2)
|
||||
return;
|
||||
}
|
||||
|
||||
if (a1->sid == -1) {
|
||||
if (speaker->sid == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (PID_TYPE(a1->pid) != OBJ_TYPE_ITEM && SID_TYPE(a1->sid) != SCRIPT_TYPE_SPATIAL) {
|
||||
if (PID_TYPE(speaker->pid) != OBJ_TYPE_ITEM && SID_TYPE(speaker->sid) != SCRIPT_TYPE_SPATIAL) {
|
||||
MessageListItem messageListItem;
|
||||
|
||||
int rc = _action_can_talk_to(gDude, a1);
|
||||
int rc = _action_can_talk_to(gDude, speaker);
|
||||
if (rc == -1) {
|
||||
// You can't see there.
|
||||
messageListItem.num = 660;
|
||||
@@ -716,17 +724,23 @@ void gameDialogEnter(Object* a1, int a2)
|
||||
isoDisable();
|
||||
|
||||
_dialog_state_fix = 1;
|
||||
gGameDialogSpeaker = a1;
|
||||
gGameDialogSpeakerIsPartyMember = objectIsPartyMember(a1);
|
||||
gGameDialogSpeaker = speaker;
|
||||
gGameDialogSpeakerIsPartyMember = objectIsPartyMember(speaker);
|
||||
|
||||
_dialogue_just_started = 1;
|
||||
|
||||
if (a1->sid != -1) {
|
||||
scriptExecProc(a1->sid, SCRIPT_PROC_TALK);
|
||||
// CE: Obtain and keep SID in a separate variable. This is needed because in
|
||||
// rare circumstates the speaker can destroy itself. So after executing it's
|
||||
// script |speaker| can point to freed memory. Dereferencing such pointer
|
||||
// can lead to crash depending on the environment (confirmed on Android and
|
||||
// MSVC debug builds).
|
||||
int sid = speaker->sid;
|
||||
if (sid != -1) {
|
||||
scriptExecProc(speaker->sid, SCRIPT_PROC_TALK);
|
||||
}
|
||||
|
||||
Script* script;
|
||||
if (scriptGetScript(a1->sid, &script) == -1) {
|
||||
if (scriptGetScript(sid, &script) == -1) {
|
||||
gameMouseObjectsShow();
|
||||
isoEnable();
|
||||
scriptsExecMapUpdateProc();
|
||||
@@ -785,7 +799,7 @@ void gameDialogEnter(Object* a1, int a2)
|
||||
// 0x444FE4
|
||||
void _gdialogSystemEnter()
|
||||
{
|
||||
_game_state_update();
|
||||
gameUpdateState();
|
||||
|
||||
_gdDialogTurnMouseOff = true;
|
||||
|
||||
@@ -801,9 +815,9 @@ void _gdialogSystemEnter()
|
||||
_tile_scroll_to(gGameDialogOldCenterTile, 2);
|
||||
}
|
||||
|
||||
_game_state_request(GAME_STATE_2);
|
||||
gameRequestState(GAME_STATE_2);
|
||||
|
||||
_game_state_update();
|
||||
gameUpdateState();
|
||||
}
|
||||
|
||||
// 0x445050
|
||||
@@ -913,6 +927,8 @@ int _gdialogInitFromScript(int headFid, int reaction)
|
||||
|
||||
_gdDialogWentOff = true;
|
||||
|
||||
GameMode::enterGameMode(GameMode::kDialog);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -938,7 +954,11 @@ int _gdialogExitFromScript()
|
||||
_tile_scroll_to(gGameDialogOldCenterTile, 2);
|
||||
}
|
||||
|
||||
GameMode::exitGameMode(GameMode::kDialog);
|
||||
|
||||
GameMode::enterGameMode(GameMode::kSpecial);
|
||||
_gdDestroyHeadWindow();
|
||||
GameMode::exitGameMode(GameMode::kSpecial);
|
||||
|
||||
// CE: Fix Barter button.
|
||||
gameDialogRedButtonsExit();
|
||||
@@ -1038,7 +1058,7 @@ void gameDialogRenderSupplementaryMessage(char* msg)
|
||||
379,
|
||||
_colorTable[992] | 0x2000000);
|
||||
|
||||
windowUnhide(_gd_replyWin);
|
||||
windowShow(_gd_replyWin);
|
||||
windowRefresh(gGameDialogReplyWindow);
|
||||
}
|
||||
|
||||
@@ -1180,10 +1200,14 @@ void _gdialogUpdatePartyStatus()
|
||||
// NOTE: Uninline.
|
||||
gdHide();
|
||||
|
||||
GameMode::enterGameMode(GameMode::kSpecial);
|
||||
|
||||
_gdialog_window_destroy();
|
||||
|
||||
gGameDialogSpeakerIsPartyMember = isPartyMember;
|
||||
|
||||
GameMode::exitGameMode(GameMode::kSpecial);
|
||||
|
||||
_gdialog_window_create();
|
||||
|
||||
// NOTE: Uninline.
|
||||
@@ -1212,11 +1236,11 @@ static int gdHide()
|
||||
static int gdUnhide()
|
||||
{
|
||||
if (_gd_replyWin != -1) {
|
||||
windowUnhide(_gd_replyWin);
|
||||
windowShow(_gd_replyWin);
|
||||
}
|
||||
|
||||
if (_gd_optionsWin != -1) {
|
||||
windowUnhide(_gd_optionsWin);
|
||||
windowShow(_gd_optionsWin);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1255,7 +1279,13 @@ int gameDialogAddTextOption(int messageListId, const char* text, int reaction)
|
||||
optionEntry->messageId = -4;
|
||||
optionEntry->reaction = reaction;
|
||||
optionEntry->btn = -1;
|
||||
sprintf(optionEntry->text, "%c %s", '\x95', text);
|
||||
|
||||
// SFALL
|
||||
if (gNumberOptions) {
|
||||
snprintf(optionEntry->text, sizeof(optionEntry->text), "%d. %s", gGameDialogOptionEntriesLength + 1, text);
|
||||
} else {
|
||||
snprintf(optionEntry->text, sizeof(optionEntry->text), "%c %s", '\x95', text);
|
||||
}
|
||||
|
||||
gGameDialogOptionEntriesLength++;
|
||||
|
||||
@@ -1284,7 +1314,7 @@ int gameDialogReviewWindowInit(int* win)
|
||||
GAME_DIALOG_REVIEW_WINDOW_WIDTH,
|
||||
GAME_DIALOG_REVIEW_WINDOW_HEIGHT,
|
||||
256,
|
||||
WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
|
||||
WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
|
||||
if (*win == -1) {
|
||||
return -1;
|
||||
}
|
||||
@@ -1421,6 +1451,8 @@ int gameDialogReviewWindowFree(int* win)
|
||||
// 0x445CA0
|
||||
int gameDialogShowReview()
|
||||
{
|
||||
ScopedGameMode gm(GameMode::kDialogReview);
|
||||
|
||||
int win;
|
||||
|
||||
if (gameDialogReviewWindowInit(&win) == -1) {
|
||||
@@ -1433,7 +1465,9 @@ int gameDialogShowReview()
|
||||
gameDialogReviewWindowUpdate(win, v1);
|
||||
|
||||
while (true) {
|
||||
int keyCode = _get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
int keyCode = inputGetInput();
|
||||
if (keyCode == 17 || keyCode == 24 || keyCode == 324) {
|
||||
showQuitConfirmationDialog();
|
||||
}
|
||||
@@ -1458,6 +1492,9 @@ int gameDialogShowReview()
|
||||
v1 = gGameDialogReviewEntriesLength - 1;
|
||||
}
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
if (gameDialogReviewWindowFree(&win) == -1) {
|
||||
@@ -1503,7 +1540,7 @@ void gameDialogReviewWindowUpdate(int win, int origin)
|
||||
GameDialogReviewEntry* dialogReviewEntry = &(gDialogReviewEntries[index]);
|
||||
|
||||
char name[60];
|
||||
sprintf(name, "%s:", objectGetName(gGameDialogSpeaker));
|
||||
snprintf(name, sizeof(name), "%s:", objectGetName(gGameDialogSpeaker));
|
||||
windowDrawText(win, name, 180, 88, y, _colorTable[992] | 0x2000000);
|
||||
entriesRect.top += v20;
|
||||
|
||||
@@ -1536,7 +1573,7 @@ void gameDialogReviewWindowUpdate(int win, int origin)
|
||||
}
|
||||
|
||||
if (dialogReviewEntry->optionMessageListId != -3) {
|
||||
sprintf(name, "%s:", objectGetName(gDude));
|
||||
snprintf(name, sizeof(name), "%s:", objectGetName(gDude));
|
||||
windowDrawText(win, name, 180, 88, y, _colorTable[21140] | 0x2000000);
|
||||
entriesRect.top += v20;
|
||||
|
||||
@@ -1696,7 +1733,7 @@ int _gdProcessInit()
|
||||
GAME_DIALOG_REPLY_WINDOW_WIDTH,
|
||||
GAME_DIALOG_REPLY_WINDOW_HEIGHT,
|
||||
256,
|
||||
WINDOW_FLAG_0x04);
|
||||
WINDOW_MOVE_ON_TOP);
|
||||
if (gGameDialogReplyWindow == -1) {
|
||||
goto err;
|
||||
}
|
||||
@@ -1721,7 +1758,7 @@ int _gdProcessInit()
|
||||
|
||||
optionsWindowX = (screenGetWidth() - GAME_DIALOG_WINDOW_WIDTH) / 2 + GAME_DIALOG_OPTIONS_WINDOW_X;
|
||||
optionsWindowY = (screenGetHeight() - GAME_DIALOG_WINDOW_HEIGHT) / 2 + GAME_DIALOG_OPTIONS_WINDOW_Y;
|
||||
gGameDialogOptionsWindow = windowCreate(optionsWindowX, optionsWindowY, GAME_DIALOG_OPTIONS_WINDOW_WIDTH, GAME_DIALOG_OPTIONS_WINDOW_HEIGHT, 256, WINDOW_FLAG_0x04);
|
||||
gGameDialogOptionsWindow = windowCreate(optionsWindowX, optionsWindowY, GAME_DIALOG_OPTIONS_WINDOW_WIDTH, GAME_DIALOG_OPTIONS_WINDOW_HEIGHT, 256, WINDOW_MOVE_ON_TOP);
|
||||
if (gGameDialogOptionsWindow == -1) {
|
||||
goto err_2;
|
||||
}
|
||||
@@ -1799,7 +1836,7 @@ void gameDialogRenderCaps()
|
||||
|
||||
int caps = itemGetTotalCaps(gDude);
|
||||
char text[20];
|
||||
sprintf(text, "$%d", caps);
|
||||
snprintf(text, sizeof(text), "$%d", caps);
|
||||
|
||||
int width = fontGetStringWidth(text);
|
||||
if (width > 60) {
|
||||
@@ -1830,13 +1867,15 @@ int _gdProcess()
|
||||
_gdReplyTooBig = 1;
|
||||
}
|
||||
|
||||
unsigned int tick = _get_time();
|
||||
unsigned int tick = getTicks();
|
||||
int pageCount = 0;
|
||||
int pageIndex = 0;
|
||||
int pageOffsets[10];
|
||||
pageOffsets[0] = 0;
|
||||
for (;;) {
|
||||
int keyCode = _get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
int keyCode = inputGetInput();
|
||||
|
||||
convertMouseWheelToArrowKey(&keyCode);
|
||||
|
||||
@@ -1855,6 +1894,9 @@ int _gdProcess()
|
||||
} else {
|
||||
if (_dialogue_switch_mode == 3) {
|
||||
_dialogue_state = 4;
|
||||
|
||||
GameMode::exitGameMode(GameMode::kSpecial);
|
||||
|
||||
inventoryOpenTrade(gGameDialogWindow, gGameDialogSpeaker, _peon_table_obj, _barterer_table_obj, gGameDialogBarterModifier);
|
||||
_gdialog_barter_cleanup_tables();
|
||||
|
||||
@@ -1947,7 +1989,7 @@ int _gdProcess()
|
||||
break;
|
||||
}
|
||||
|
||||
tick = _get_time();
|
||||
tick = getTicks();
|
||||
|
||||
if (dword_58F4E0) {
|
||||
v18 = 1;
|
||||
@@ -1958,6 +2000,9 @@ int _gdProcess()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
_gdReenterLevel -= 1;
|
||||
@@ -2236,15 +2281,24 @@ void _gdProcessUpdate()
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sprintf(dialogOptionEntry->text, "%c ", '\x95');
|
||||
strncat(dialogOptionEntry->text, text, 897);
|
||||
// SFALL
|
||||
if (gNumberOptions) {
|
||||
snprintf(dialogOptionEntry->text, sizeof(dialogOptionEntry->text), "%d. %s", index + 1, text);
|
||||
} else {
|
||||
snprintf(dialogOptionEntry->text, sizeof(dialogOptionEntry->text), "%c %s", '\x95', text);
|
||||
}
|
||||
} else if (dialogOptionEntry->messageListId == -1) {
|
||||
if (index == 0) {
|
||||
// Go on
|
||||
messageListItem.num = 655;
|
||||
if (critterGetStat(gDude, STAT_INTELLIGENCE) < 4) {
|
||||
if (messageListGetItem(&gProtoMessageList, &messageListItem)) {
|
||||
strcpy(dialogOptionEntry->text, messageListItem.text);
|
||||
// SFALL
|
||||
if (gNumberOptions) {
|
||||
snprintf(dialogOptionEntry->text, sizeof(dialogOptionEntry->text), "%d. %s", index + 1, messageListItem.text);
|
||||
} else {
|
||||
snprintf(dialogOptionEntry->text, sizeof(dialogOptionEntry->text), "%s", messageListItem.text);
|
||||
}
|
||||
} else {
|
||||
debugPrint("\nError...can't find message!");
|
||||
return;
|
||||
@@ -2252,13 +2306,23 @@ void _gdProcessUpdate()
|
||||
}
|
||||
} else {
|
||||
// TODO: Why only space?
|
||||
strcpy(dialogOptionEntry->text, " ");
|
||||
// SFALL
|
||||
if (gNumberOptions) {
|
||||
snprintf(dialogOptionEntry->text, sizeof(dialogOptionEntry->text), "%d. %s", index + 1, " ");
|
||||
} else {
|
||||
strcpy(dialogOptionEntry->text, " ");
|
||||
}
|
||||
}
|
||||
} else if (dialogOptionEntry->messageListId == -2) {
|
||||
// [Done]
|
||||
messageListItem.num = 650;
|
||||
if (messageListGetItem(&gProtoMessageList, &messageListItem)) {
|
||||
sprintf(dialogOptionEntry->text, "%c %s", '\x95', messageListItem.text);
|
||||
// SFALL
|
||||
if (gNumberOptions) {
|
||||
snprintf(dialogOptionEntry->text, sizeof(dialogOptionEntry->text), "%d. %s", index + 1, messageListItem.text);
|
||||
} else {
|
||||
snprintf(dialogOptionEntry->text, sizeof(dialogOptionEntry->text), "%c %s", '\x95', messageListItem.text);
|
||||
}
|
||||
} else {
|
||||
debugPrint("\nError...can't find message!");
|
||||
return;
|
||||
@@ -2421,7 +2485,7 @@ void _gdSetupFidget(int headFrmId, int reaction)
|
||||
debugPrint("failure!\n");
|
||||
|
||||
char stats[200];
|
||||
cachePrintStats(&gArtCache, stats);
|
||||
cachePrintStats(&gArtCache, stats, sizeof(stats));
|
||||
debugPrint("%s", stats);
|
||||
}
|
||||
}
|
||||
@@ -2474,7 +2538,7 @@ void _gdSetupFidget(int headFrmId, int reaction)
|
||||
debugPrint("failure!\n");
|
||||
|
||||
char stats[200];
|
||||
cachePrintStats(&gArtCache, stats);
|
||||
cachePrintStats(&gArtCache, stats, sizeof(stats));
|
||||
debugPrint("%s", stats);
|
||||
}
|
||||
|
||||
@@ -2497,11 +2561,16 @@ void gameDialogWaitForFidgetToComplete()
|
||||
debugPrint("Waiting for fidget to complete...\n");
|
||||
|
||||
while (artGetFrameCount(gGameDialogFidgetFrm) > gGameDialogFidgetFrmCurrentFrame) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
if (getTicksSince(gGameDialogFidgetLastUpdateTimestamp) >= gGameDialogFidgetUpdateDelay) {
|
||||
gameDialogRenderTalkingHead(gGameDialogFidgetFrm, gGameDialogFidgetFrmCurrentFrame);
|
||||
gGameDialogFidgetLastUpdateTimestamp = _get_time();
|
||||
gGameDialogFidgetLastUpdateTimestamp = getTicks();
|
||||
gGameDialogFidgetFrmCurrentFrame++;
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
gGameDialogFidgetFrmCurrentFrame = 0;
|
||||
@@ -2543,11 +2612,16 @@ void _gdPlayTransition(int anim)
|
||||
int frame = 0;
|
||||
unsigned int time = 0;
|
||||
while (frame < artGetFrameCount(headFrm)) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
if (getTicksSince(time) >= delay) {
|
||||
gameDialogRenderTalkingHead(headFrm, frame);
|
||||
time = _get_time();
|
||||
time = getTicks();
|
||||
frame++;
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
if (artUnlock(headFrmHandle) == -1) {
|
||||
@@ -2721,6 +2795,9 @@ void gameDialogTicker()
|
||||
case 2:
|
||||
_loop_cnt = -1;
|
||||
_dialogue_switch_mode = 3;
|
||||
|
||||
GameMode::enterGameMode(GameMode::kSpecial);
|
||||
|
||||
_gdialog_window_destroy();
|
||||
_gdialog_barter_create_win();
|
||||
break;
|
||||
@@ -2792,7 +2869,7 @@ void gameDialogTicker()
|
||||
_can_start_new_fidget = true;
|
||||
} else {
|
||||
gameDialogRenderTalkingHead(gGameDialogFidgetFrm, gGameDialogFidgetFrmCurrentFrame);
|
||||
gGameDialogFidgetLastUpdateTimestamp = _get_time();
|
||||
gGameDialogFidgetLastUpdateTimestamp = getTicks();
|
||||
gGameDialogFidgetFrmCurrentFrame += 1;
|
||||
}
|
||||
}
|
||||
@@ -2880,6 +2957,8 @@ void _gdialog_scroll_subwin(int win, int a2, unsigned char* a3, unsigned char* a
|
||||
}
|
||||
|
||||
for (; v18 >= 0; v18--) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
soundContinueAll();
|
||||
blitBufferToBuffer(a3,
|
||||
GAME_DIALOG_WINDOW_WIDTH,
|
||||
@@ -2892,9 +2971,12 @@ void _gdialog_scroll_subwin(int win, int a2, unsigned char* a3, unsigned char* a
|
||||
v7 += 10;
|
||||
v9 -= 10 * (GAME_DIALOG_WINDOW_WIDTH);
|
||||
|
||||
tick = _get_time();
|
||||
tick = getTicks();
|
||||
while (getTicksSince(tick) < 33) {
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
} else {
|
||||
rect.right = GAME_DIALOG_WINDOW_WIDTH - 1;
|
||||
@@ -2903,6 +2985,8 @@ void _gdialog_scroll_subwin(int win, int a2, unsigned char* a3, unsigned char* a
|
||||
rect.top = 0;
|
||||
|
||||
for (int index = a6 / 10; index > 0; index--) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
soundContinueAll();
|
||||
|
||||
blitBufferToBuffer(a5,
|
||||
@@ -2927,9 +3011,12 @@ void _gdialog_scroll_subwin(int win, int a2, unsigned char* a3, unsigned char* a
|
||||
|
||||
rect.top += 10;
|
||||
|
||||
tick = _get_time();
|
||||
tick = getTicks();
|
||||
while (getTicksSince(tick) < 33) {
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3134,7 +3221,7 @@ int _gdialog_barter_create_win()
|
||||
GAME_DIALOG_WINDOW_WIDTH,
|
||||
_dialogue_subwin_len,
|
||||
256,
|
||||
WINDOW_FLAG_0x02);
|
||||
WINDOW_DONT_MOVE_TOP);
|
||||
if (gGameDialogWindow == -1) {
|
||||
return -1;
|
||||
}
|
||||
@@ -3166,7 +3253,7 @@ int _gdialog_barter_create_win()
|
||||
_barterer_table_obj->flags |= OBJECT_HIDDEN;
|
||||
|
||||
if (objectCreateWithFidPid(&_barterer_temp_obj, gGameDialogSpeaker->fid, -1) != -1) {
|
||||
_barterer_temp_obj->flags |= OBJECT_HIDDEN | OBJECT_TEMPORARY;
|
||||
_barterer_temp_obj->flags |= OBJECT_HIDDEN | OBJECT_NO_SAVE;
|
||||
_barterer_temp_obj->sid = -1;
|
||||
return 0;
|
||||
}
|
||||
@@ -3288,7 +3375,7 @@ int partyMemberControlWindowInit()
|
||||
GAME_DIALOG_WINDOW_WIDTH,
|
||||
_dialogue_subwin_len,
|
||||
256,
|
||||
WINDOW_FLAG_0x02);
|
||||
WINDOW_DONT_MOVE_TOP);
|
||||
if (gGameDialogWindow == -1) {
|
||||
partyMemberControlWindowFree();
|
||||
return -1;
|
||||
@@ -3464,7 +3551,7 @@ void partyMemberControlWindowUpdate()
|
||||
|
||||
FrmImage backgroundFrmImage;
|
||||
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 390, 0, 0, 0);
|
||||
if (!backgroundFrmImage.lock(backgroundFid)) {
|
||||
if (backgroundFrmImage.lock(backgroundFid)) {
|
||||
int width = backgroundFrmImage.getWidth();
|
||||
unsigned char* buffer = backgroundFrmImage.getData();
|
||||
|
||||
@@ -3490,13 +3577,13 @@ void partyMemberControlWindowUpdate()
|
||||
// Render item in right hand.
|
||||
Object* item2 = critterGetItem2(gGameDialogSpeaker);
|
||||
text = item2 != NULL ? itemGetName(item2) : getmsg(&gProtoMessageList, &messageListItem, 10);
|
||||
sprintf(formattedText, "%s", text);
|
||||
snprintf(formattedText, sizeof(formattedText), "%s", text);
|
||||
fontDrawText(windowBuffer + windowWidth * 20 + 112, formattedText, 110, windowWidth, _colorTable[992]);
|
||||
|
||||
// Render armor.
|
||||
Object* armor = critterGetArmor(gGameDialogSpeaker);
|
||||
text = armor != NULL ? itemGetName(armor) : getmsg(&gProtoMessageList, &messageListItem, 10);
|
||||
sprintf(formattedText, "%s", text);
|
||||
snprintf(formattedText, sizeof(formattedText), "%s", text);
|
||||
fontDrawText(windowBuffer + windowWidth * 49 + 112, formattedText, 110, windowWidth, _colorTable[992]);
|
||||
|
||||
// Render preview.
|
||||
@@ -3514,24 +3601,24 @@ void partyMemberControlWindowUpdate()
|
||||
// Render hit points.
|
||||
int maximumHitPoints = critterGetStat(gGameDialogSpeaker, STAT_MAXIMUM_HIT_POINTS);
|
||||
int hitPoints = critterGetStat(gGameDialogSpeaker, STAT_CURRENT_HIT_POINTS);
|
||||
sprintf(formattedText, "%d/%d", hitPoints, maximumHitPoints);
|
||||
snprintf(formattedText, sizeof(formattedText), "%d/%d", hitPoints, maximumHitPoints);
|
||||
fontDrawText(windowBuffer + windowWidth * 96 + 240, formattedText, 115, windowWidth, _colorTable[992]);
|
||||
|
||||
// Render best skill.
|
||||
int bestSkill = partyMemberGetBestSkill(gGameDialogSpeaker);
|
||||
text = skillGetName(bestSkill);
|
||||
sprintf(formattedText, "%s", text);
|
||||
snprintf(formattedText, sizeof(formattedText), "%s", text);
|
||||
fontDrawText(windowBuffer + windowWidth * 113 + 240, formattedText, 115, windowWidth, _colorTable[992]);
|
||||
|
||||
// Render weight summary.
|
||||
int inventoryWeight = objectGetInventoryWeight(gGameDialogSpeaker);
|
||||
int carryWeight = critterGetStat(gGameDialogSpeaker, STAT_CARRY_WEIGHT);
|
||||
sprintf(formattedText, "%d/%d ", inventoryWeight, carryWeight);
|
||||
snprintf(formattedText, sizeof(formattedText), "%d/%d ", inventoryWeight, carryWeight);
|
||||
fontDrawText(windowBuffer + windowWidth * 131 + 240, formattedText, 115, windowWidth, critterIsEncumbered(gGameDialogSpeaker) ? _colorTable[31744] : _colorTable[992]);
|
||||
|
||||
// Render melee damage.
|
||||
int meleeDamage = critterGetStat(gGameDialogSpeaker, STAT_MELEE_DAMAGE);
|
||||
sprintf(formattedText, "%d", meleeDamage);
|
||||
snprintf(formattedText, sizeof(formattedText), "%d", meleeDamage);
|
||||
fontDrawText(windowBuffer + windowWidth * 148 + 240, formattedText, 115, windowWidth, _colorTable[992]);
|
||||
|
||||
int actionPoints;
|
||||
@@ -3541,7 +3628,7 @@ void partyMemberControlWindowUpdate()
|
||||
actionPoints = critterGetStat(gGameDialogSpeaker, STAT_MAXIMUM_ACTION_POINTS);
|
||||
}
|
||||
int maximumActionPoints = critterGetStat(gGameDialogSpeaker, STAT_MAXIMUM_ACTION_POINTS);
|
||||
sprintf(formattedText, "%d/%d ", actionPoints, maximumActionPoints);
|
||||
snprintf(formattedText, sizeof(formattedText), "%d/%d ", actionPoints, maximumActionPoints);
|
||||
fontDrawText(windowBuffer + windowWidth * 167 + 240, formattedText, 115, windowWidth, _colorTable[992]);
|
||||
|
||||
fontSetCurrent(oldFont);
|
||||
@@ -3585,7 +3672,7 @@ int _gdCanBarter()
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (proto->critter.data.flags & CRITTER_FLAG_0x2) {
|
||||
if (proto->critter.data.flags & CRITTER_BARTER) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -3615,7 +3702,9 @@ void partyMemberControlWindowHandleEvents()
|
||||
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
int keyCode = _get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
int keyCode = inputGetInput();
|
||||
if (keyCode != -1) {
|
||||
if (keyCode == KEY_CTRL_Q || keyCode == KEY_CTRL_X || keyCode == KEY_F10) {
|
||||
showQuitConfirmationDialog();
|
||||
@@ -3674,14 +3763,22 @@ void partyMemberControlWindowHandleEvents()
|
||||
return;
|
||||
}
|
||||
} else if (keyCode == -2) {
|
||||
if (_mouse_click_in(441, 451, 540, 470)) {
|
||||
aiSetDisposition(gGameDialogSpeaker, 0);
|
||||
_dialogue_state = 13;
|
||||
_dialogue_switch_mode = 11;
|
||||
done = true;
|
||||
// CE: Minor improvement - handle on mouse up (just like other
|
||||
// buttons). Also fixed active button area (in original code
|
||||
// it's slightly smaller than the button itself).
|
||||
if ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_UP) != 0) {
|
||||
if (mouseHitTestInWindow(gGameDialogWindow, 438, 156, 438 + 109, 156 + 28)) {
|
||||
aiSetDisposition(gGameDialogSpeaker, 0);
|
||||
_dialogue_state = 13;
|
||||
_dialogue_switch_mode = 11;
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3718,7 +3815,7 @@ int partyMemberCustomizationWindowInit()
|
||||
GAME_DIALOG_WINDOW_WIDTH,
|
||||
_dialogue_subwin_len,
|
||||
256,
|
||||
WINDOW_FLAG_0x02);
|
||||
WINDOW_DONT_MOVE_TOP);
|
||||
if (gGameDialogWindow == -1) {
|
||||
partyMemberCustomizationWindowFree();
|
||||
return -1;
|
||||
@@ -3855,7 +3952,9 @@ void partyMemberCustomizationWindowHandleEvents()
|
||||
{
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
unsigned int keyCode = _get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
unsigned int keyCode = inputGetInput();
|
||||
if (keyCode != -1) {
|
||||
if (keyCode == KEY_CTRL_Q || keyCode == KEY_CTRL_X || keyCode == KEY_F10) {
|
||||
showQuitConfirmationDialog();
|
||||
@@ -3874,6 +3973,9 @@ void partyMemberCustomizationWindowHandleEvents()
|
||||
_dialogue_state = 10;
|
||||
}
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4004,7 +4106,7 @@ int _gdCustomSelect(int a1)
|
||||
|
||||
int selectWindowX = (screenGetWidth() - backgroundFrmWidth) / 2;
|
||||
int selectWindowY = (screenGetHeight() - backgroundFrmHeight) / 2;
|
||||
int win = windowCreate(selectWindowX, selectWindowY, backgroundFrmWidth, backgroundFrmHeight, 256, WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
|
||||
int win = windowCreate(selectWindowX, selectWindowY, backgroundFrmWidth, backgroundFrmHeight, 256, WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
|
||||
if (win == -1) {
|
||||
return -1;
|
||||
}
|
||||
@@ -4057,88 +4159,85 @@ int _gdCustomSelect(int a1)
|
||||
bool done = false;
|
||||
unsigned int v53 = 0;
|
||||
while (!done) {
|
||||
int keyCode = _get_input();
|
||||
if (keyCode == -1) {
|
||||
continue;
|
||||
}
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
if (keyCode == KEY_CTRL_Q || keyCode == KEY_CTRL_X || keyCode == KEY_F10) {
|
||||
showQuitConfirmationDialog();
|
||||
}
|
||||
|
||||
if (_game_user_wants_to_quit != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (keyCode == KEY_RETURN) {
|
||||
STRUCT_5189E4* ptr = &(_custom_settings[a1][value]);
|
||||
_custom_current_selected[a1] = value;
|
||||
_gdCustomUpdateSetting(a1, ptr->value);
|
||||
done = true;
|
||||
} else if (keyCode == KEY_ESCAPE) {
|
||||
done = true;
|
||||
} else if (keyCode == -2) {
|
||||
if ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_UP) == 0) {
|
||||
continue;
|
||||
int keyCode = inputGetInput();
|
||||
if (keyCode != -1) {
|
||||
if (keyCode == KEY_CTRL_Q || keyCode == KEY_CTRL_X || keyCode == KEY_F10) {
|
||||
showQuitConfirmationDialog();
|
||||
}
|
||||
|
||||
// No need to use mouseHitTestInWindow as these values are already
|
||||
// in screen coordinates.
|
||||
if (!_mouse_click_in(minX, minY, maxX, maxY)) {
|
||||
continue;
|
||||
if (_game_user_wants_to_quit != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
int mouseX;
|
||||
int mouseY;
|
||||
mouseGetPosition(&mouseX, &mouseY);
|
||||
if (keyCode == KEY_RETURN) {
|
||||
STRUCT_5189E4* ptr = &(_custom_settings[a1][value]);
|
||||
_custom_current_selected[a1] = value;
|
||||
_gdCustomUpdateSetting(a1, ptr->value);
|
||||
done = true;
|
||||
} else if (keyCode == KEY_ESCAPE) {
|
||||
done = true;
|
||||
} else if (keyCode == -2) {
|
||||
if ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_UP) != 0) {
|
||||
// No need to use mouseHitTestInWindow as these values are already
|
||||
// in screen coordinates.
|
||||
if (_mouse_click_in(minX, minY, maxX, maxY)) {
|
||||
int mouseX;
|
||||
int mouseY;
|
||||
mouseGetPosition(&mouseX, &mouseY);
|
||||
|
||||
int lineHeight = fontGetLineHeight();
|
||||
int newValue = (mouseY - minY) / lineHeight;
|
||||
if (newValue >= 6) {
|
||||
continue;
|
||||
}
|
||||
int lineHeight = fontGetLineHeight();
|
||||
int newValue = (mouseY - minY) / lineHeight;
|
||||
if (newValue < 6) {
|
||||
unsigned int timestamp = getTicks();
|
||||
if (newValue == value) {
|
||||
if (getTicksBetween(timestamp, v53) < 250) {
|
||||
_custom_current_selected[a1] = newValue;
|
||||
_gdCustomUpdateSetting(a1, newValue);
|
||||
done = true;
|
||||
}
|
||||
} else {
|
||||
STRUCT_5189E4* ptr = &(_custom_settings[a1][newValue]);
|
||||
if (ptr->messageId != -1) {
|
||||
bool enabled = false;
|
||||
switch (a1) {
|
||||
case PARTY_MEMBER_CUSTOMIZATION_OPTION_AREA_ATTACK_MODE:
|
||||
enabled = partyMemberSupportsAreaAttackMode(gGameDialogSpeaker, ptr->value);
|
||||
break;
|
||||
case PARTY_MEMBER_CUSTOMIZATION_OPTION_RUN_AWAY_MODE:
|
||||
enabled = partyMemberSupportsRunAwayMode(gGameDialogSpeaker, ptr->value);
|
||||
break;
|
||||
case PARTY_MEMBER_CUSTOMIZATION_OPTION_BEST_WEAPON:
|
||||
enabled = partyMemberSupportsBestWeapon(gGameDialogSpeaker, ptr->value);
|
||||
break;
|
||||
case PARTY_MEMBER_CUSTOMIZATION_OPTION_DISTANCE:
|
||||
enabled = partyMemberSupportsDistance(gGameDialogSpeaker, ptr->value);
|
||||
break;
|
||||
case PARTY_MEMBER_CUSTOMIZATION_OPTION_ATTACK_WHO:
|
||||
enabled = partyMemberSupportsAttackWho(gGameDialogSpeaker, ptr->value);
|
||||
break;
|
||||
case PARTY_MEMBER_CUSTOMIZATION_OPTION_CHEM_USE:
|
||||
enabled = partyMemberSupportsChemUse(gGameDialogSpeaker, ptr->value);
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned int timestamp = _get_time();
|
||||
if (newValue == value) {
|
||||
if (getTicksBetween(timestamp, v53) < 250) {
|
||||
_custom_current_selected[a1] = newValue;
|
||||
_gdCustomUpdateSetting(a1, newValue);
|
||||
done = true;
|
||||
}
|
||||
} else {
|
||||
STRUCT_5189E4* ptr = &(_custom_settings[a1][newValue]);
|
||||
if (ptr->messageId != -1) {
|
||||
bool enabled = false;
|
||||
switch (a1) {
|
||||
case PARTY_MEMBER_CUSTOMIZATION_OPTION_AREA_ATTACK_MODE:
|
||||
enabled = partyMemberSupportsAreaAttackMode(gGameDialogSpeaker, ptr->value);
|
||||
break;
|
||||
case PARTY_MEMBER_CUSTOMIZATION_OPTION_RUN_AWAY_MODE:
|
||||
enabled = partyMemberSupportsRunAwayMode(gGameDialogSpeaker, ptr->value);
|
||||
break;
|
||||
case PARTY_MEMBER_CUSTOMIZATION_OPTION_BEST_WEAPON:
|
||||
enabled = partyMemberSupportsBestWeapon(gGameDialogSpeaker, ptr->value);
|
||||
break;
|
||||
case PARTY_MEMBER_CUSTOMIZATION_OPTION_DISTANCE:
|
||||
enabled = partyMemberSupportsDistance(gGameDialogSpeaker, ptr->value);
|
||||
break;
|
||||
case PARTY_MEMBER_CUSTOMIZATION_OPTION_ATTACK_WHO:
|
||||
enabled = partyMemberSupportsAttackWho(gGameDialogSpeaker, ptr->value);
|
||||
break;
|
||||
case PARTY_MEMBER_CUSTOMIZATION_OPTION_CHEM_USE:
|
||||
enabled = partyMemberSupportsChemUse(gGameDialogSpeaker, ptr->value);
|
||||
break;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
value = newValue;
|
||||
_gdCustomSelectRedraw(windowBuffer, backgroundFrmWidth, a1, newValue);
|
||||
windowRefresh(win);
|
||||
if (enabled) {
|
||||
value = newValue;
|
||||
_gdCustomSelectRedraw(windowBuffer, backgroundFrmWidth, a1, newValue);
|
||||
windowRefresh(win);
|
||||
}
|
||||
}
|
||||
}
|
||||
v53 = timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
v53 = timestamp;
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
windowDestroy(win);
|
||||
@@ -4185,7 +4284,7 @@ void gameDialogBarterButtonUpMouseUp(int btn, int keyCode)
|
||||
|
||||
Proto* proto;
|
||||
protoGetProto(gGameDialogSpeaker->pid, &proto);
|
||||
if (proto->critter.data.flags & CRITTER_FLAG_0x2) {
|
||||
if (proto->critter.data.flags & CRITTER_BARTER) {
|
||||
if (gGameDialogLipSyncStarted) {
|
||||
if (soundIsPlaying(gLipsData.sound)) {
|
||||
gameDialogEndLips();
|
||||
@@ -4241,7 +4340,7 @@ int _gdialog_window_create()
|
||||
|
||||
int dialogSubwindowX = (screenGetWidth() - GAME_DIALOG_WINDOW_WIDTH) / 2;
|
||||
int dialogSubwindowY = (screenGetHeight() - GAME_DIALOG_WINDOW_HEIGHT) / 2 + GAME_DIALOG_WINDOW_HEIGHT - _dialogue_subwin_len;
|
||||
gGameDialogWindow = windowCreate(dialogSubwindowX, dialogSubwindowY, screenWidth, _dialogue_subwin_len, 256, WINDOW_FLAG_0x02);
|
||||
gGameDialogWindow = windowCreate(dialogSubwindowX, dialogSubwindowY, screenWidth, _dialogue_subwin_len, 256, WINDOW_DONT_MOVE_TOP);
|
||||
if (gGameDialogWindow != -1) {
|
||||
|
||||
unsigned char* v10 = windowGetBuffer(gGameDialogWindow);
|
||||
@@ -4362,7 +4461,7 @@ static int talk_to_create_background_window()
|
||||
GAME_DIALOG_WINDOW_WIDTH,
|
||||
GAME_DIALOG_WINDOW_HEIGHT,
|
||||
256,
|
||||
WINDOW_FLAG_0x02);
|
||||
WINDOW_DONT_MOVE_TOP);
|
||||
|
||||
if (gGameDialogBackgroundWindow != -1) {
|
||||
return 0;
|
||||
@@ -4578,9 +4677,9 @@ void gameDialogRenderTalkingHead(Art* headFrm, int frame)
|
||||
void gameDialogHighlightsInit()
|
||||
{
|
||||
for (int color = 0; color < 256; color++) {
|
||||
int r = (_Color2RGB_(color) & 0x7C00) >> 10;
|
||||
int g = (_Color2RGB_(color) & 0x3E0) >> 5;
|
||||
int b = _Color2RGB_(color) & 0x1F;
|
||||
int r = (Color2RGB(color) & 0x7C00) >> 10;
|
||||
int g = (Color2RGB(color) & 0x3E0) >> 5;
|
||||
int b = Color2RGB(color) & 0x1F;
|
||||
_light_GrayTable[color] = ((r + 2 * g + 2 * b) / 10) >> 2;
|
||||
_dark_GrayTable[color] = ((r + g + b) / 10) >> 2;
|
||||
}
|
||||
@@ -4614,15 +4713,15 @@ static void gameDialogHighlightsExit()
|
||||
|
||||
static void gameDialogRedButtonsInit()
|
||||
{
|
||||
// di_rdbt2.frm - dialog red button down
|
||||
int pressedFid = buildFid(OBJ_TYPE_INTERFACE, 96, 0, 0, 0);
|
||||
if (!_redButtonPressedFrmImage.lock(pressedFid)) {
|
||||
// di_rdbt2.frm - dialog red button up
|
||||
int normalFid = buildFid(OBJ_TYPE_INTERFACE, 96, 0, 0, 0);
|
||||
if (!_redButtonNormalFrmImage.lock(normalFid)) {
|
||||
gameDialogRedButtonsExit();
|
||||
}
|
||||
|
||||
// di_rdbt1.frm - dialog red button up
|
||||
int normalFid = buildFid(OBJ_TYPE_INTERFACE, 95, 0, 0, 0);
|
||||
if (!_redButtonNormalFrmImage.lock(normalFid)) {
|
||||
// di_rdbt1.frm - dialog red button down
|
||||
int pressedFid = buildFid(OBJ_TYPE_INTERFACE, 95, 0, 0, 0);
|
||||
if (!_redButtonPressedFrmImage.lock(pressedFid)) {
|
||||
gameDialogRedButtonsExit();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ int gameDialogInit();
|
||||
int gameDialogReset();
|
||||
int gameDialogExit();
|
||||
bool _gdialogActive();
|
||||
void gameDialogEnter(Object* a1, int a2);
|
||||
void gameDialogEnter(Object* speaker, int a2);
|
||||
void _gdialogSystemEnter();
|
||||
void gameDialogStartLips(const char* a1);
|
||||
int gameDialogEnable();
|
||||
|
||||
@@ -16,7 +16,6 @@ static void gameMemoryFree(void* ptr);
|
||||
int gameMemoryInit()
|
||||
{
|
||||
dictionarySetMemoryProcs(internal_malloc, internal_realloc, internal_free);
|
||||
_db_register_mem(internal_malloc, internal_strdup, internal_free);
|
||||
memoryManagerSetProcs(gameMemoryMalloc, gameMemoryRealloc, gameMemoryFree);
|
||||
|
||||
return 0;
|
||||
|
||||
+48
-47
@@ -4,25 +4,30 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "actions.h"
|
||||
#include "animation.h"
|
||||
#include "art.h"
|
||||
#include "color.h"
|
||||
#include "combat.h"
|
||||
#include "core.h"
|
||||
#include "critter.h"
|
||||
#include "draw.h"
|
||||
#include "game.h"
|
||||
#include "game_config.h"
|
||||
#include "game_sound.h"
|
||||
#include "input.h"
|
||||
#include "interface.h"
|
||||
#include "item.h"
|
||||
#include "kb.h"
|
||||
#include "mouse.h"
|
||||
#include "object.h"
|
||||
#include "proto.h"
|
||||
#include "proto_instance.h"
|
||||
#include "settings.h"
|
||||
#include "sfall_config.h"
|
||||
#include "skill.h"
|
||||
#include "skilldex.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "tile.h"
|
||||
#include "window_manager.h"
|
||||
@@ -689,7 +694,7 @@ void gameMouseRefresh()
|
||||
primaryAction = GAME_MOUSE_ACTION_MENU_ITEM_TALK;
|
||||
}
|
||||
} else {
|
||||
if (_critter_flag_check(pointedObject->pid, CRITTER_FLAG_0x20)) {
|
||||
if (_critter_flag_check(pointedObject->pid, CRITTER_NO_STEAL)) {
|
||||
primaryAction = GAME_MOUSE_ACTION_MENU_ITEM_LOOK;
|
||||
} else {
|
||||
primaryAction = GAME_MOUSE_ACTION_MENU_ITEM_USE;
|
||||
@@ -737,9 +742,7 @@ void gameMouseRefresh()
|
||||
if (pointedObject != NULL) {
|
||||
bool pointedObjectIsCritter = FID_TYPE(pointedObject->fid) == OBJ_TYPE_CRITTER;
|
||||
|
||||
int combatLooks = 0;
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_LOOKS_KEY, &combatLooks);
|
||||
if (combatLooks != 0) {
|
||||
if (settings.preferences.combat_looks) {
|
||||
if (_obj_examine(gDude, pointedObject) == -1) {
|
||||
_obj_look_at(gDude, pointedObject);
|
||||
}
|
||||
@@ -749,7 +752,7 @@ void gameMouseRefresh()
|
||||
int accuracy;
|
||||
char formattedAccuracy[8];
|
||||
if (_combat_to_hit(pointedObject, &accuracy)) {
|
||||
sprintf(formattedAccuracy, "%d%%", accuracy);
|
||||
snprintf(formattedAccuracy, sizeof(formattedAccuracy), "%d%%", accuracy);
|
||||
|
||||
if (pointedObjectIsCritter) {
|
||||
if (pointedObject->data.critter.combat.team != 0) {
|
||||
@@ -761,7 +764,7 @@ void gameMouseRefresh()
|
||||
color = _colorTable[17969];
|
||||
}
|
||||
} else {
|
||||
sprintf(formattedAccuracy, " %c ", 'X');
|
||||
snprintf(formattedAccuracy, sizeof(formattedAccuracy), " %c ", 'X');
|
||||
|
||||
if (pointedObjectIsCritter) {
|
||||
if (pointedObject->data.critter.combat.team != 0) {
|
||||
@@ -801,30 +804,25 @@ void gameMouseRefresh()
|
||||
|
||||
char formattedActionPoints[8];
|
||||
int color;
|
||||
int v6 = _make_path(gDude, gDude->tile, gGameMouseHexCursor->tile, NULL, 1);
|
||||
if (v6) {
|
||||
int distance = _make_path(gDude, gDude->tile, gGameMouseHexCursor->tile, NULL, 1);
|
||||
if (distance != 0) {
|
||||
if (!isInCombat()) {
|
||||
formattedActionPoints[0] = '\0';
|
||||
color = _colorTable[31744];
|
||||
} else {
|
||||
int v7 = critterGetMovementPointCostAdjustedForCrippledLegs(gDude, v6);
|
||||
int v8;
|
||||
if (v7 - _combat_free_move >= 0) {
|
||||
v8 = v7 - _combat_free_move;
|
||||
} else {
|
||||
v8 = 0;
|
||||
}
|
||||
int actionPointsMax = critterGetMovementPointCostAdjustedForCrippledLegs(gDude, distance);
|
||||
int actionPointsRequired = std::max(0, actionPointsMax - _combat_free_move);
|
||||
|
||||
if (v8 <= gDude->data.critter.combat.ap) {
|
||||
sprintf(formattedActionPoints, "%d", v8);
|
||||
if (actionPointsRequired <= gDude->data.critter.combat.ap) {
|
||||
snprintf(formattedActionPoints, sizeof(formattedActionPoints), "%d", actionPointsRequired);
|
||||
color = _colorTable[32767];
|
||||
} else {
|
||||
sprintf(formattedActionPoints, "%c", 'X');
|
||||
snprintf(formattedActionPoints, sizeof(formattedActionPoints), "%c", 'X');
|
||||
color = _colorTable[31744];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sprintf(formattedActionPoints, "%c", 'X');
|
||||
snprintf(formattedActionPoints, sizeof(formattedActionPoints), "%c", 'X');
|
||||
color = _colorTable[31744];
|
||||
}
|
||||
|
||||
@@ -935,16 +933,13 @@ void _gmouse_handle_event(int mouseX, int mouseY, int mouseState)
|
||||
actionPoints = -1;
|
||||
}
|
||||
|
||||
bool running;
|
||||
configGetBool(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_RUNNING_KEY, &running);
|
||||
|
||||
if (gPressedPhysicalKeys[SDL_SCANCODE_LSHIFT] || gPressedPhysicalKeys[SDL_SCANCODE_RSHIFT]) {
|
||||
if (running) {
|
||||
if (settings.preferences.running) {
|
||||
_dude_move(actionPoints);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!running) {
|
||||
if (!settings.preferences.running) {
|
||||
_dude_move(actionPoints);
|
||||
return;
|
||||
}
|
||||
@@ -1016,7 +1011,7 @@ void _gmouse_handle_event(int mouseX, int mouseY, int mouseState)
|
||||
_gmouse_3d_hover_test = true;
|
||||
gGameMouseLastY = mouseY;
|
||||
gGameMouseLastX = mouseX;
|
||||
_gmouse_3d_last_move_time = _get_time() - 250;
|
||||
_gmouse_3d_last_move_time = getTicks() - 250;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1093,7 +1088,7 @@ void _gmouse_handle_event(int mouseX, int mouseY, int mouseState)
|
||||
actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_TALK;
|
||||
}
|
||||
} else {
|
||||
if (!_critter_flag_check(v16->pid, CRITTER_FLAG_0x20)) {
|
||||
if (!_critter_flag_check(v16->pid, CRITTER_NO_STEAL)) {
|
||||
actionMenuItems[actionMenuItemsCount++] = GAME_MOUSE_ACTION_MENU_ITEM_USE;
|
||||
}
|
||||
}
|
||||
@@ -1138,7 +1133,9 @@ void _gmouse_handle_event(int mouseX, int mouseY, int mouseState)
|
||||
int v33 = mouseY;
|
||||
int actionIndex = 0;
|
||||
while ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_UP) == 0) {
|
||||
_get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
inputGetInput();
|
||||
|
||||
if (_game_user_wants_to_quit != 0) {
|
||||
actionMenuItems[actionIndex] = 0;
|
||||
@@ -1160,6 +1157,9 @@ void _gmouse_handle_event(int mouseX, int mouseY, int mouseState)
|
||||
}
|
||||
v33 = v47;
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
isoEnable();
|
||||
@@ -1167,7 +1167,7 @@ void _gmouse_handle_event(int mouseX, int mouseY, int mouseState)
|
||||
_gmouse_3d_hover_test = false;
|
||||
gGameMouseLastX = mouseX;
|
||||
gGameMouseLastY = mouseY;
|
||||
_gmouse_3d_last_move_time = _get_time();
|
||||
_gmouse_3d_last_move_time = getTicks();
|
||||
|
||||
_mouse_set_position(mouseX, v33);
|
||||
|
||||
@@ -1273,7 +1273,7 @@ int gameMouseSetCursor(int cursor)
|
||||
bool shouldUpdate = true;
|
||||
int frame = 0;
|
||||
if (cursor >= FIRST_GAME_MOUSE_ANIMATED_CURSOR) {
|
||||
unsigned int tick = _get_time();
|
||||
unsigned int tick = getTicks();
|
||||
|
||||
if ((gGameMouseHexCursor->flags & OBJECT_HIDDEN) == 0) {
|
||||
gameMouseObjectsHide();
|
||||
@@ -1392,7 +1392,7 @@ void gameMouseSetMode(int mode)
|
||||
|
||||
gGameMouseMode = mode;
|
||||
_gmouse_3d_hover_test = false;
|
||||
_gmouse_3d_last_move_time = _get_time();
|
||||
_gmouse_3d_last_move_time = getTicks();
|
||||
|
||||
tileWindowRefreshRect(&rect, gElevation);
|
||||
|
||||
@@ -1560,7 +1560,7 @@ void gameMouseObjectsShow()
|
||||
}
|
||||
|
||||
_gmouse_3d_hover_test = false;
|
||||
_gmouse_3d_last_move_time = _get_time() - 250;
|
||||
_gmouse_3d_last_move_time = getTicks() - 250;
|
||||
}
|
||||
|
||||
// 0x44CE34
|
||||
@@ -1942,10 +1942,7 @@ int gameMouseRenderActionPoints(const char* string, int color)
|
||||
// 0x44D954
|
||||
void gameMouseLoadItemHighlight()
|
||||
{
|
||||
bool itemHighlight;
|
||||
if (configGetBool(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_ITEM_HIGHLIGHT_KEY, &itemHighlight)) {
|
||||
gGameMouseItemHighlightEnabled = itemHighlight;
|
||||
}
|
||||
gGameMouseItemHighlightEnabled = settings.preferences.item_highlight;
|
||||
}
|
||||
|
||||
// 0x44D984
|
||||
@@ -1976,13 +1973,13 @@ int gameMouseObjectsInit()
|
||||
}
|
||||
|
||||
gGameMouseBouncingCursor->flags |= OBJECT_LIGHT_THRU;
|
||||
gGameMouseBouncingCursor->flags |= OBJECT_TEMPORARY;
|
||||
gGameMouseBouncingCursor->flags |= OBJECT_FLAG_0x400;
|
||||
gGameMouseBouncingCursor->flags |= OBJECT_NO_SAVE;
|
||||
gGameMouseBouncingCursor->flags |= OBJECT_NO_REMOVE;
|
||||
gGameMouseBouncingCursor->flags |= OBJECT_SHOOT_THRU;
|
||||
gGameMouseBouncingCursor->flags |= OBJECT_NO_BLOCK;
|
||||
|
||||
gGameMouseHexCursor->flags |= OBJECT_FLAG_0x400;
|
||||
gGameMouseHexCursor->flags |= OBJECT_TEMPORARY;
|
||||
gGameMouseHexCursor->flags |= OBJECT_NO_REMOVE;
|
||||
gGameMouseHexCursor->flags |= OBJECT_NO_SAVE;
|
||||
gGameMouseHexCursor->flags |= OBJECT_LIGHT_THRU;
|
||||
gGameMouseHexCursor->flags |= OBJECT_SHOOT_THRU;
|
||||
gGameMouseHexCursor->flags |= OBJECT_NO_BLOCK;
|
||||
@@ -2024,7 +2021,7 @@ int gameMouseObjectsReset()
|
||||
gGameMouseLastX = -1;
|
||||
gGameMouseLastY = -1;
|
||||
_gmouse_3d_hover_test = false;
|
||||
_gmouse_3d_last_move_time = _get_time();
|
||||
_gmouse_3d_last_move_time = getTicks();
|
||||
gameMouseLoadItemHighlight();
|
||||
|
||||
return 0;
|
||||
@@ -2038,8 +2035,8 @@ void gameMouseObjectsFree()
|
||||
if (gGameMouseObjectsInitialized) {
|
||||
gameMouseActionMenuFree();
|
||||
|
||||
gGameMouseBouncingCursor->flags &= ~OBJECT_TEMPORARY;
|
||||
gGameMouseHexCursor->flags &= ~OBJECT_TEMPORARY;
|
||||
gGameMouseBouncingCursor->flags &= ~OBJECT_NO_SAVE;
|
||||
gGameMouseHexCursor->flags &= ~OBJECT_NO_SAVE;
|
||||
|
||||
objectDestroy(gGameMouseBouncingCursor, NULL);
|
||||
objectDestroy(gGameMouseHexCursor, NULL);
|
||||
@@ -2253,9 +2250,7 @@ int _gmouse_3d_move_to(int x, int y, int elevation, Rect* a4)
|
||||
x1 = -8;
|
||||
y1 = 13;
|
||||
|
||||
char* executable;
|
||||
configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_EXECUTABLE_KEY, &executable);
|
||||
if (compat_stricmp(executable, "mapper") == 0) {
|
||||
if (compat_stricmp(settings.system.executable.c_str(), "mapper") == 0) {
|
||||
if (tileRoofIsVisible()) {
|
||||
if ((gDude->flags & OBJECT_HIDDEN) == 0) {
|
||||
y1 = -83;
|
||||
@@ -2457,4 +2452,10 @@ static void customMouseModeFrmsInit()
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_REPAIR_FRM_KEY, &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_REPAIR]));
|
||||
}
|
||||
|
||||
void gameMouseRefreshImmediately()
|
||||
{
|
||||
gameMouseRefresh();
|
||||
renderPresent();
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
@@ -101,6 +101,8 @@ int gameMouseHighlightActionMenuItemAtIndex(int menuItemIndex);
|
||||
void gameMouseLoadItemHighlight();
|
||||
void _gmouse_remove_item_outline(Object* object);
|
||||
|
||||
void gameMouseRefreshImmediately();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* GAME_MOUSE_H */
|
||||
|
||||
+24
-22
@@ -4,18 +4,21 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "color.h"
|
||||
#include "core.h"
|
||||
#include "cycle.h"
|
||||
#include "debug.h"
|
||||
#include "game.h"
|
||||
#include "game_config.h"
|
||||
#include "game_mouse.h"
|
||||
#include "game_sound.h"
|
||||
#include "input.h"
|
||||
#include "mouse.h"
|
||||
#include "movie.h"
|
||||
#include "movie_effect.h"
|
||||
#include "palette.h"
|
||||
#include "platform_compat.h"
|
||||
#include "settings.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "touch.h"
|
||||
#include "window_manager.h"
|
||||
|
||||
namespace fallout {
|
||||
@@ -141,24 +144,18 @@ int gameMoviePlay(int movie, int flags)
|
||||
const char* movieFileName = gMovieFileNames[movie];
|
||||
debugPrint("\nPlaying movie: %s\n", movieFileName);
|
||||
|
||||
char* language;
|
||||
if (!configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_LANGUAGE_KEY, &language)) {
|
||||
debugPrint("\ngmovie_play() - Error: Unable to determine language!\n");
|
||||
gGameMovieIsPlaying = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* language = settings.system.language.c_str();
|
||||
char movieFilePath[COMPAT_MAX_PATH];
|
||||
int movieFileSize;
|
||||
bool movieFound = false;
|
||||
|
||||
if (compat_stricmp(language, ENGLISH) != 0) {
|
||||
sprintf(movieFilePath, "art\\%s\\cuts\\%s", language, gMovieFileNames[movie]);
|
||||
snprintf(movieFilePath, sizeof(movieFilePath), "art\\%s\\cuts\\%s", language, gMovieFileNames[movie]);
|
||||
movieFound = dbGetFileSize(movieFilePath, &movieFileSize) == 0;
|
||||
}
|
||||
|
||||
if (!movieFound) {
|
||||
sprintf(movieFilePath, "art\\cuts\\%s", gMovieFileNames[movie]);
|
||||
snprintf(movieFilePath, sizeof(movieFilePath), "art\\cuts\\%s", gMovieFileNames[movie]);
|
||||
movieFound = dbGetFileSize(movieFilePath, &movieFileSize) == 0;
|
||||
}
|
||||
|
||||
@@ -180,7 +177,7 @@ int gameMoviePlay(int movie, int flags)
|
||||
GAME_MOVIE_WINDOW_WIDTH,
|
||||
GAME_MOVIE_WINDOW_HEIGHT,
|
||||
0,
|
||||
WINDOW_FLAG_0x10);
|
||||
WINDOW_MODAL);
|
||||
if (win == -1) {
|
||||
gGameMovieIsPlaying = false;
|
||||
return -1;
|
||||
@@ -194,9 +191,8 @@ int gameMoviePlay(int movie, int flags)
|
||||
|
||||
windowRefresh(win);
|
||||
|
||||
bool subtitlesEnabled = false;
|
||||
bool subtitlesEnabled = settings.preferences.subtitles;
|
||||
int v1 = 4;
|
||||
configGetBool(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_SUBTITLES_KEY, &subtitlesEnabled);
|
||||
if (subtitlesEnabled) {
|
||||
char* subtitlesFilePath = gameMovieBuildSubtitlesFilePath(movieFilePath);
|
||||
|
||||
@@ -250,7 +246,12 @@ int gameMoviePlay(int movie, int flags)
|
||||
int v11 = 0;
|
||||
int buttons;
|
||||
do {
|
||||
if (!_moviePlaying() || _game_user_wants_to_quit || _get_input() != -1) {
|
||||
if (!_moviePlaying() || _game_user_wants_to_quit || inputGetInput() != -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
Gesture gesture;
|
||||
if (touch_get_gesture(&gesture) && gesture.state == kEnded) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -281,14 +282,18 @@ int gameMoviePlay(int movie, int flags)
|
||||
|
||||
windowSetFont(oldFont);
|
||||
|
||||
float r = (float)((_Color2RGB_(oldTextColor) & 0x7C00) >> 10) * flt_50352A;
|
||||
float g = (float)((_Color2RGB_(oldTextColor) & 0x3E0) >> 5) * flt_50352A;
|
||||
float b = (float)(_Color2RGB_(oldTextColor) & 0x1F) * flt_50352A;
|
||||
float r = (float)((Color2RGB(oldTextColor) & 0x7C00) >> 10) * flt_50352A;
|
||||
float g = (float)((Color2RGB(oldTextColor) & 0x3E0) >> 5) * flt_50352A;
|
||||
float b = (float)(Color2RGB(oldTextColor) & 0x1F) * flt_50352A;
|
||||
windowSetTextColor(r, g, b);
|
||||
}
|
||||
|
||||
windowDestroy(win);
|
||||
|
||||
// CE: Destroying a window redraws only content it was covering (centered
|
||||
// 640x480). This leads to everything outside this rect to remain black.
|
||||
windowRefreshAll(&_scr_size);
|
||||
|
||||
if ((flags & GAME_MOVIE_PAUSE_MUSIC) != 0) {
|
||||
backgroundSoundResume();
|
||||
}
|
||||
@@ -330,9 +335,6 @@ bool gameMovieIsPlaying()
|
||||
// 0x44EB1C
|
||||
static char* gameMovieBuildSubtitlesFilePath(char* movieFilePath)
|
||||
{
|
||||
char* language;
|
||||
configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_LANGUAGE_KEY, &language);
|
||||
|
||||
char* path = movieFilePath;
|
||||
|
||||
char* separator = strrchr(path, '\\');
|
||||
@@ -340,7 +342,7 @@ static char* gameMovieBuildSubtitlesFilePath(char* movieFilePath)
|
||||
path = separator + 1;
|
||||
}
|
||||
|
||||
sprintf(gGameMovieSubtitlesFilePath, "text\\%s\\cuts\\%s", language, path);
|
||||
snprintf(gGameMovieSubtitlesFilePath, sizeof(gGameMovieSubtitlesFilePath), "text\\%s\\cuts\\%s", settings.system.language.c_str(), path);
|
||||
|
||||
char* pch = strrchr(gGameMovieSubtitlesFilePath, '.');
|
||||
if (*pch != '\0') {
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
#include "game_palette.h"
|
||||
|
||||
#include "color.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
// 0x44EBC0
|
||||
int _HighRGB_(int a1)
|
||||
{
|
||||
// TODO: Some strange bit arithmetic.
|
||||
int v1 = _Color2RGB_(a1);
|
||||
int r = (v1 & 0x7C00) >> 10;
|
||||
int g = (v1 & 0x3E0) >> 5;
|
||||
int b = (v1 & 0x1F);
|
||||
|
||||
int result = g;
|
||||
if (r > result) {
|
||||
result = r;
|
||||
}
|
||||
|
||||
result = result & 0xFF;
|
||||
if (result <= b) {
|
||||
result = b;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
@@ -1,10 +0,0 @@
|
||||
#ifndef GAME_PALETTE_H
|
||||
#define GAME_PALETTE_H
|
||||
|
||||
namespace fallout {
|
||||
|
||||
int _HighRGB_(int a1);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* GAME_PALETTE_H */
|
||||
+55
-66
@@ -6,10 +6,11 @@
|
||||
#include "animation.h"
|
||||
#include "art.h"
|
||||
#include "audio.h"
|
||||
#include "audio_file.h"
|
||||
#include "combat.h"
|
||||
#include "core.h"
|
||||
#include "debug.h"
|
||||
#include "game_config.h"
|
||||
#include "input.h"
|
||||
#include "item.h"
|
||||
#include "map.h"
|
||||
#include "memory.h"
|
||||
@@ -19,8 +20,10 @@
|
||||
#include "proto.h"
|
||||
#include "queue.h"
|
||||
#include "random.h"
|
||||
#include "settings.h"
|
||||
#include "sound_effects_cache.h"
|
||||
#include "stat.h"
|
||||
#include "svga.h"
|
||||
#include "window_manager.h"
|
||||
#include "worldmap.h"
|
||||
|
||||
@@ -154,7 +157,7 @@ static int _gsound_speech_volume_get_set(int volume);
|
||||
static void speechPause();
|
||||
static void speechResume();
|
||||
static void _gsound_bkg_proc();
|
||||
static int gameSoundFileOpen(const char* fname, int access, ...);
|
||||
static int gameSoundFileOpen(const char* fname, int* sampleRate);
|
||||
static long _gsound_write_();
|
||||
static long gameSoundFileTellNotImplemented(int handle);
|
||||
static int gameSoundFileWrite(int handle, const void* buf, unsigned int size);
|
||||
@@ -189,13 +192,11 @@ int gameSoundInit()
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool initialize;
|
||||
configGetBool(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_INITIALIZE_KEY, &initialize);
|
||||
if (!initialize) {
|
||||
if (!settings.sound.initialize) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
configGetBool(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_DEBUG_KEY, &gGameSoundDebugEnabled);
|
||||
gGameSoundDebugEnabled = settings.sound.debug;
|
||||
|
||||
if (gGameSoundDebugEnabled) {
|
||||
debugPrint("Initializing sound system...");
|
||||
@@ -239,8 +240,7 @@ int gameSoundInit()
|
||||
audioFileInit(gameSoundIsCompressed);
|
||||
audioInit(gameSoundIsCompressed);
|
||||
|
||||
int cacheSize;
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_CACHE_SIZE_KEY, &cacheSize);
|
||||
int cacheSize = settings.sound.cache_size;
|
||||
if (cacheSize >= 0x40000) {
|
||||
debugPrint("\n!!! Config file needs adustment. Please remove the ");
|
||||
debugPrint("cache_size line and run fallout again. This will reset ");
|
||||
@@ -265,14 +265,11 @@ int gameSoundInit()
|
||||
gGameSoundInitialized = true;
|
||||
|
||||
// SOUNDS
|
||||
bool sounds = 0;
|
||||
configGetBool(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SOUNDS_KEY, &sounds);
|
||||
|
||||
if (gGameSoundDebugEnabled) {
|
||||
debugPrint("Sounds are ");
|
||||
}
|
||||
|
||||
if (sounds) {
|
||||
if (settings.sound.sounds) {
|
||||
// NOTE: Uninline.
|
||||
soundEffectsEnable();
|
||||
} else {
|
||||
@@ -286,14 +283,11 @@ int gameSoundInit()
|
||||
}
|
||||
|
||||
// MUSIC
|
||||
bool music = 0;
|
||||
configGetBool(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_KEY, &music);
|
||||
|
||||
if (gGameSoundDebugEnabled) {
|
||||
debugPrint("Music is ");
|
||||
}
|
||||
|
||||
if (music) {
|
||||
if (settings.sound.music) {
|
||||
// NOTE: Uninline.
|
||||
backgroundSoundEnable();
|
||||
} else {
|
||||
@@ -307,14 +301,11 @@ int gameSoundInit()
|
||||
}
|
||||
|
||||
// SPEEECH
|
||||
bool speech = 0;
|
||||
configGetBool(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SPEECH_KEY, &speech);
|
||||
|
||||
if (gGameSoundDebugEnabled) {
|
||||
debugPrint("Speech is ");
|
||||
}
|
||||
|
||||
if (speech) {
|
||||
if (settings.sound.speech) {
|
||||
// NOTE: Uninline.
|
||||
speechEnable();
|
||||
} else {
|
||||
@@ -327,16 +318,16 @@ int gameSoundInit()
|
||||
debugPrint("on.\n");
|
||||
}
|
||||
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MASTER_VOLUME_KEY, &gMasterVolume);
|
||||
gMasterVolume = settings.sound.master_volume;
|
||||
gameSoundSetMasterVolume(gMasterVolume);
|
||||
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_VOLUME_KEY, &gMusicVolume);
|
||||
gMusicVolume = settings.sound.music_volume;
|
||||
backgroundSoundSetVolume(gMusicVolume);
|
||||
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SNDFX_VOLUME_KEY, &gSoundEffectsVolume);
|
||||
gSoundEffectsVolume = settings.sound.sndfx_volume;
|
||||
soundEffectsSetVolume(gSoundEffectsVolume);
|
||||
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SPEECH_VOLUME_KEY, &gSpeechVolume);
|
||||
gSpeechVolume = settings.sound.speech_volume;
|
||||
speechSetVolume(gSpeechVolume);
|
||||
|
||||
_gsound_background_fade = 0;
|
||||
@@ -907,7 +898,7 @@ int speechLoad(const char* fname, int a2, int a3, int a4)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (soundSetFileIO(gSpeechSound, &audioOpen, &audioClose, &audioRead, NULL, &audioSeek, &gameSoundFileTellNotImplemented, &audioGetSize)) {
|
||||
if (soundSetFileIO(gSpeechSound, audioOpen, audioClose, audioRead, NULL, audioSeek, gameSoundFileTellNotImplemented, audioGetSize)) {
|
||||
if (gGameSoundDebugEnabled) {
|
||||
debugPrint("failed because file IO could not be set for compression.\n");
|
||||
}
|
||||
@@ -1109,7 +1100,7 @@ Sound* soundEffectLoad(const char* name, Object* object)
|
||||
++_gsound_active_effect_counter;
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "%s%s%s", _sound_sfx_path, name, ".ACM");
|
||||
snprintf(path, sizeof(path), "%s%s%s", _sound_sfx_path, name, ".ACM");
|
||||
|
||||
if (soundLoad(sound, path) == 0) {
|
||||
if (gGameSoundDebugEnabled) {
|
||||
@@ -1132,7 +1123,7 @@ Sound* soundEffectLoad(const char* name, Object* object)
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(path, "%sH%cXXXX%s%s", _sound_sfx_path, v9, name + 6, ".ACM");
|
||||
snprintf(path, sizeof(path), "%sH%cXXXX%s%s", _sound_sfx_path, v9, name + 6, ".ACM");
|
||||
|
||||
if (gGameSoundDebugEnabled) {
|
||||
debugPrint("tyring %s ", path + strlen(_sound_sfx_path));
|
||||
@@ -1147,7 +1138,7 @@ Sound* soundEffectLoad(const char* name, Object* object)
|
||||
}
|
||||
|
||||
if (v9 == 'F') {
|
||||
sprintf(path, "%sHMXXXX%s%s", _sound_sfx_path, name + 6, ".ACM");
|
||||
snprintf(path, sizeof(path), "%sHMXXXX%s%s", _sound_sfx_path, name + 6, ".ACM");
|
||||
|
||||
if (gGameSoundDebugEnabled) {
|
||||
debugPrint("tyring %s ", path + strlen(_sound_sfx_path));
|
||||
@@ -1165,7 +1156,7 @@ Sound* soundEffectLoad(const char* name, Object* object)
|
||||
}
|
||||
|
||||
if (strncmp(name, "MALIEU", 6) == 0 || strncmp(name, "MAMTN2", 6) == 0) {
|
||||
sprintf(path, "%sMAMTNT%s%s", _sound_sfx_path, name + 6, ".ACM");
|
||||
snprintf(path, sizeof(path), "%sMAMTNT%s%s", _sound_sfx_path, name + 6, ".ACM");
|
||||
|
||||
if (gGameSoundDebugEnabled) {
|
||||
debugPrint("tyring %s ", path + strlen(_sound_sfx_path));
|
||||
@@ -1352,7 +1343,7 @@ char* sfxBuildCharName(Object* a1, int anim, int extra)
|
||||
v8 = 'Z';
|
||||
}
|
||||
|
||||
sprintf(_sfx_file_name, "%s%c%c", v7, v8, v9);
|
||||
snprintf(_sfx_file_name, sizeof(_sfx_file_name), "%s%c%c", v7, v8, v9);
|
||||
compat_strupr(_sfx_file_name);
|
||||
return _sfx_file_name;
|
||||
}
|
||||
@@ -1361,7 +1352,7 @@ char* sfxBuildCharName(Object* a1, int anim, int extra)
|
||||
// 0x4516F0
|
||||
char* gameSoundBuildAmbientSoundEffectName(const char* a1)
|
||||
{
|
||||
sprintf(_sfx_file_name, "A%6s%1d", a1, 1);
|
||||
snprintf(_sfx_file_name, sizeof(_sfx_file_name), "A%6s%1d", a1, 1);
|
||||
compat_strupr(_sfx_file_name);
|
||||
return _sfx_file_name;
|
||||
}
|
||||
@@ -1370,7 +1361,7 @@ char* gameSoundBuildAmbientSoundEffectName(const char* a1)
|
||||
// 0x451718
|
||||
char* gameSoundBuildInterfaceName(const char* a1)
|
||||
{
|
||||
sprintf(_sfx_file_name, "N%6s%1d", a1, 1);
|
||||
snprintf(_sfx_file_name, sizeof(_sfx_file_name), "N%6s%1d", a1, 1);
|
||||
compat_strupr(_sfx_file_name);
|
||||
return _sfx_file_name;
|
||||
}
|
||||
@@ -1447,7 +1438,7 @@ char* sfxBuildWeaponName(int effectType, Object* weapon, int hitMode, Object* ta
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(_sfx_file_name, "W%c%c%1d%cXX%1d", effectTypeCode, weaponSoundCode, v6, materialCode, 1);
|
||||
snprintf(_sfx_file_name, sizeof(_sfx_file_name), "W%c%c%1d%cXX%1d", effectTypeCode, weaponSoundCode, v6, materialCode, 1);
|
||||
compat_strupr(_sfx_file_name);
|
||||
return _sfx_file_name;
|
||||
}
|
||||
@@ -1459,7 +1450,7 @@ char* sfxBuildSceneryName(int actionType, int action, const char* name)
|
||||
char actionTypeCode = actionType == SOUND_EFFECT_ACTION_TYPE_PASSIVE ? 'P' : 'A';
|
||||
char actionCode = _snd_lookup_scenery_action[action];
|
||||
|
||||
sprintf(_sfx_file_name, "S%c%c%4s%1d", actionTypeCode, actionCode, name, 1);
|
||||
snprintf(_sfx_file_name, sizeof(_sfx_file_name), "S%c%c%4s%1d", actionTypeCode, actionCode, name, 1);
|
||||
compat_strupr(_sfx_file_name);
|
||||
|
||||
return _sfx_file_name;
|
||||
@@ -1477,11 +1468,11 @@ char* sfxBuildOpenName(Object* object, int action)
|
||||
} else {
|
||||
scenerySoundId = 'A';
|
||||
}
|
||||
sprintf(_sfx_file_name, "S%cDOORS%c", _snd_lookup_scenery_action[action], scenerySoundId);
|
||||
snprintf(_sfx_file_name, sizeof(_sfx_file_name), "S%cDOORS%c", _snd_lookup_scenery_action[action], scenerySoundId);
|
||||
} else {
|
||||
Proto* proto;
|
||||
protoGetProto(object->pid, &proto);
|
||||
sprintf(_sfx_file_name, "I%cCNTNR%c", _snd_lookup_scenery_action[action], proto->item.field_80);
|
||||
snprintf(_sfx_file_name, sizeof(_sfx_file_name), "I%cCNTNR%c", _snd_lookup_scenery_action[action], proto->item.field_80);
|
||||
}
|
||||
compat_strupr(_sfx_file_name);
|
||||
return _sfx_file_name;
|
||||
@@ -1557,12 +1548,8 @@ void _gsound_bkg_proc()
|
||||
}
|
||||
|
||||
// 0x451A08
|
||||
int gameSoundFileOpen(const char* fname, int flags, ...)
|
||||
int gameSoundFileOpen(const char* fname, int* sampleRate)
|
||||
{
|
||||
if ((flags & 2) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
File* stream = fileOpen(fname, "rb");
|
||||
if (stream == NULL) {
|
||||
return -1;
|
||||
@@ -1691,21 +1678,21 @@ void soundEffectCallback(void* userData, int a2)
|
||||
// 0x451ADC
|
||||
int _gsound_background_allocate(Sound** soundPtr, int a2, int a3)
|
||||
{
|
||||
int v5 = 10;
|
||||
int v6 = 0;
|
||||
int soundFlags = SOUND_FLAG_0x02 | SOUND_16BIT;
|
||||
int type = 0;
|
||||
if (a2 == 13) {
|
||||
v6 |= 0x01;
|
||||
type |= SOUND_TYPE_MEMORY;
|
||||
} else if (a2 == 14) {
|
||||
v6 |= 0x02;
|
||||
type |= SOUND_TYPE_STREAMING;
|
||||
}
|
||||
|
||||
if (a3 == 15) {
|
||||
v6 |= 0x04;
|
||||
type |= SOUND_TYPE_FIRE_AND_FORGET;
|
||||
} else if (a3 == 16) {
|
||||
v5 = 42;
|
||||
soundFlags |= SOUND_LOOPING;
|
||||
}
|
||||
|
||||
Sound* sound = soundAllocate(v6, v5);
|
||||
Sound* sound = soundAllocate(type, soundFlags);
|
||||
if (sound == NULL) {
|
||||
return -1;
|
||||
}
|
||||
@@ -1733,7 +1720,7 @@ int gameSoundFindBackgroundSoundPathWithCopy(char* dest, const char* src)
|
||||
}
|
||||
|
||||
char outPath[COMPAT_MAX_PATH];
|
||||
sprintf(outPath, "%s%s%s", _sound_music_path1, src, ".ACM");
|
||||
snprintf(outPath, sizeof(outPath), "%s%s%s", _sound_music_path1, src, ".ACM");
|
||||
if (_gsound_file_exists_f(outPath)) {
|
||||
strncpy(dest, outPath, COMPAT_MAX_PATH);
|
||||
dest[COMPAT_MAX_PATH] = '\0';
|
||||
@@ -1747,7 +1734,7 @@ int gameSoundFindBackgroundSoundPathWithCopy(char* dest, const char* src)
|
||||
gameSoundDeleteOldMusicFile();
|
||||
|
||||
char inPath[COMPAT_MAX_PATH];
|
||||
sprintf(inPath, "%s%s%s", _sound_music_path2, src, ".ACM");
|
||||
snprintf(inPath, sizeof(inPath), "%s%s%s", _sound_music_path2, src, ".ACM");
|
||||
|
||||
FILE* inStream = compat_fopen(inPath, "rb");
|
||||
if (inStream == NULL) {
|
||||
@@ -1834,7 +1821,7 @@ int gameSoundFindBackgroundSoundPath(char* dest, const char* src)
|
||||
debugPrint(" finding background sound ");
|
||||
}
|
||||
|
||||
sprintf(path, "%s%s%s", _sound_music_path1, src, ".ACM");
|
||||
snprintf(path, sizeof(path), "%s%s%s", _sound_music_path1, src, ".ACM");
|
||||
if (_gsound_file_exists_f(path)) {
|
||||
strncpy(dest, path, COMPAT_MAX_PATH);
|
||||
dest[COMPAT_MAX_PATH] = '\0';
|
||||
@@ -1845,7 +1832,7 @@ int gameSoundFindBackgroundSoundPath(char* dest, const char* src)
|
||||
debugPrint("in 2nd path ");
|
||||
}
|
||||
|
||||
sprintf(path, "%s%s%s", _sound_music_path2, src, ".ACM");
|
||||
snprintf(path, sizeof(path), "%s%s%s", _sound_music_path2, src, ".ACM");
|
||||
if (_gsound_file_exists_f(path)) {
|
||||
strncpy(dest, path, COMPAT_MAX_PATH);
|
||||
dest[COMPAT_MAX_PATH] = '\0';
|
||||
@@ -1878,7 +1865,7 @@ int gameSoundFindSpeechSoundPath(char* dest, const char* src)
|
||||
debugPrint(" finding speech sound ");
|
||||
}
|
||||
|
||||
sprintf(path, "%s%s%s", _sound_speech_path, src, ".ACM");
|
||||
snprintf(path, sizeof(path), "%s%s%s", _sound_speech_path, src, ".ACM");
|
||||
|
||||
// Check for existence by getting file size.
|
||||
int fileSize;
|
||||
@@ -1902,7 +1889,7 @@ void gameSoundDeleteOldMusicFile()
|
||||
{
|
||||
if (_background_fname_copied[0] != '\0') {
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "%s%s%s", "sound\\music\\", _background_fname_copied, ".ACM");
|
||||
snprintf(path, sizeof(path), "%s%s%s", "sound\\music\\", _background_fname_copied, ".ACM");
|
||||
if (compat_remove(path)) {
|
||||
if (gGameSoundDebugEnabled) {
|
||||
debugPrint("Deleting old music file failed.\n");
|
||||
@@ -1961,35 +1948,37 @@ int speechPlay()
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: Refactor to use Settings.
|
||||
//
|
||||
// 0x452208
|
||||
int _gsound_get_music_path(char** out_value, const char* key)
|
||||
{
|
||||
size_t v3;
|
||||
char* v4;
|
||||
size_t len;
|
||||
char* copy;
|
||||
char* value;
|
||||
|
||||
configGetString(&gGameConfig, GAME_CONFIG_SOUND_KEY, key, out_value);
|
||||
|
||||
value = *out_value;
|
||||
v3 = strlen(value) + 1;
|
||||
len = strlen(value);
|
||||
|
||||
if (*(value + v3 - 2) == '\\') {
|
||||
if (value[len - 1] == '\\' || value[len - 1] == '/') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
v4 = (char*)internal_malloc(v3 - 1 + 2);
|
||||
if (v4 == NULL) {
|
||||
copy = (char*)internal_malloc(len + 2);
|
||||
if (copy == NULL) {
|
||||
if (gGameSoundDebugEnabled) {
|
||||
debugPrint("Out of memory in gsound_get_music_path.\n");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
strcpy(v4, value);
|
||||
*(v4 + v3) = '\\';
|
||||
*(v4 + v3 + 1) = '\0';
|
||||
strcpy(copy, value);
|
||||
copy[len] = '\\';
|
||||
copy[len + 1] = '\0';
|
||||
|
||||
if (configSetString(&gGameConfig, GAME_CONFIG_SOUND_KEY, key, v4) != 1) {
|
||||
if (configSetString(&gGameConfig, GAME_CONFIG_SOUND_KEY, key, copy) != 1) {
|
||||
if (gGameSoundDebugEnabled) {
|
||||
debugPrint("config_set_string failed in gsound_music_path.\n");
|
||||
}
|
||||
@@ -1998,7 +1987,7 @@ int _gsound_get_music_path(char** out_value, const char* key)
|
||||
}
|
||||
|
||||
if (configGetString(&gGameConfig, GAME_CONFIG_SOUND_KEY, key, out_value)) {
|
||||
internal_free(v4);
|
||||
internal_free(copy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2014,7 +2003,7 @@ Sound* _gsound_get_sound_ready_for_effect()
|
||||
{
|
||||
int rc;
|
||||
|
||||
Sound* sound = soundAllocate(5, 10);
|
||||
Sound* sound = soundAllocate(SOUND_TYPE_MEMORY | SOUND_TYPE_FIRE_AND_FORGET, SOUND_FLAG_0x02 | SOUND_16BIT);
|
||||
if (sound == NULL) {
|
||||
if (gGameSoundDebugEnabled) {
|
||||
debugPrint(" Can't allocate sound for effect. ");
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "color.h"
|
||||
#include "debug.h"
|
||||
#include "memory.h"
|
||||
|
||||
@@ -11,6 +14,9 @@ static void _InitTree();
|
||||
static void _InsertNode(int a1);
|
||||
static void _DeleteNode(int a1);
|
||||
|
||||
// 0x596D90
|
||||
static unsigned char _GreyTable[256];
|
||||
|
||||
// 0x596E90
|
||||
static int* _dad_2;
|
||||
|
||||
@@ -35,6 +41,17 @@ static int _codesize;
|
||||
// 0x596EAC
|
||||
static int _match_position;
|
||||
|
||||
// 0x44EBC0
|
||||
unsigned char HighRGB(unsigned char color)
|
||||
{
|
||||
int rgb = Color2RGB(color);
|
||||
int r = (rgb & 0x7C00) >> 10;
|
||||
int g = (rgb & 0x3E0) >> 5;
|
||||
int b = (rgb & 0x1F);
|
||||
|
||||
return std::max(std::max(r, g), b);
|
||||
}
|
||||
|
||||
// 0x44F250
|
||||
int graphCompress(unsigned char* a1, unsigned char* a2, int a3)
|
||||
{
|
||||
@@ -385,4 +402,37 @@ int graphDecompress(unsigned char* src, unsigned char* dest, int length)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x44FA78
|
||||
void grayscalePaletteUpdate(int a1, int a2)
|
||||
{
|
||||
if (a1 >= 0 && a2 <= 255) {
|
||||
for (int index = a1; index <= a2; index++) {
|
||||
// NOTE: Calls `Color2RGB` many times due to `min` and `max` macro
|
||||
// uses.
|
||||
int v1 = std::max((Color2RGB(index) & 0x7C00) >> 10, std::max((Color2RGB(index) & 0x3E0) >> 5, Color2RGB(index) & 0x1F));
|
||||
int v2 = std::min((Color2RGB(index) & 0x7C00) >> 10, std::min((Color2RGB(index) & 0x3E0) >> 5, Color2RGB(index) & 0x1F));
|
||||
int v3 = v1 + v2;
|
||||
int v4 = (int)((double)v3 * 240.0 / 510.0);
|
||||
|
||||
int paletteIndex = ((v4 & 0xFF) << 10) | ((v4 & 0xFF) << 5) | (v4 & 0xFF);
|
||||
_GreyTable[index] = _colorTable[paletteIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0x44FC40
|
||||
void grayscalePaletteApply(unsigned char* buffer, int width, int height, int pitch)
|
||||
{
|
||||
unsigned char* ptr = buffer;
|
||||
int skip = pitch - width;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
unsigned char c = *ptr;
|
||||
*ptr++ = _GreyTable[c];
|
||||
}
|
||||
ptr += skip;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
|
||||
namespace fallout {
|
||||
|
||||
unsigned char HighRGB(unsigned char color);
|
||||
int graphCompress(unsigned char* a1, unsigned char* a2, int a3);
|
||||
int graphDecompress(unsigned char* a1, unsigned char* a2, int a3);
|
||||
void grayscalePaletteUpdate(int a1, int a2);
|
||||
void grayscalePaletteApply(unsigned char* surface, int width, int height, int pitch);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
#include "grayscale.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "color.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
// 0x596D90
|
||||
static unsigned char _GreyTable[256];
|
||||
|
||||
// 0x44FA78
|
||||
void grayscalePaletteUpdate(int a1, int a2)
|
||||
{
|
||||
if (a1 >= 0 && a2 <= 255) {
|
||||
for (int index = a1; index <= a2; index++) {
|
||||
// NOTE: The only way to explain so much calls to [_Color2RGB_] with
|
||||
// the same repeated pattern is by the use of min/max macros.
|
||||
|
||||
int v1 = std::max((_Color2RGB_(index) & 0x7C00) >> 10, std::max((_Color2RGB_(index) & 0x3E0) >> 5, _Color2RGB_(index) & 0x1F));
|
||||
int v2 = std::min((_Color2RGB_(index) & 0x7C00) >> 10, std::min((_Color2RGB_(index) & 0x3E0) >> 5, _Color2RGB_(index) & 0x1F));
|
||||
int v3 = v1 + v2;
|
||||
int v4 = (int)((double)v3 * 240.0 / 510.0);
|
||||
|
||||
int paletteIndex = ((v4 & 0xFF) << 10) | ((v4 & 0xFF) << 5) | (v4 & 0xFF);
|
||||
_GreyTable[index] = _colorTable[paletteIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0x44FC40
|
||||
void grayscalePaletteApply(unsigned char* buffer, int width, int height, int pitch)
|
||||
{
|
||||
unsigned char* ptr = buffer;
|
||||
int skip = pitch - width;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
unsigned char c = *ptr;
|
||||
*ptr++ = _GreyTable[c];
|
||||
}
|
||||
ptr += skip;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
@@ -1,11 +0,0 @@
|
||||
#ifndef GRAYSCALE_H
|
||||
#define GRAYSCALE_H
|
||||
|
||||
namespace fallout {
|
||||
|
||||
void grayscalePaletteUpdate(int a1, int a2);
|
||||
void grayscalePaletteApply(unsigned char* surface, int width, int height, int pitch);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* GRAYSCALE_H */
|
||||
+6
-4
@@ -73,7 +73,7 @@ typedef struct HeapMoveableExtent {
|
||||
static bool heapInternalsInit();
|
||||
static void heapInternalsFree();
|
||||
static bool heapHandleListInit(Heap* heap);
|
||||
static bool heapPrintStats(Heap* heap, char* dest);
|
||||
static bool heapPrintStats(Heap* heap, char* dest, size_t size);
|
||||
static bool heapFindFreeHandle(Heap* heap, int* handleIndexPtr);
|
||||
static bool heapFindFreeBlock(Heap* heap, int size, void** blockPtr, int a4);
|
||||
static int heapBlockCompareBySize(const void* a1, const void* a2);
|
||||
@@ -307,6 +307,8 @@ bool heapBlockAllocate(Heap* heap, int* handleIndexPtr, int size, int a4)
|
||||
int blockSize;
|
||||
HeapHandle* handle;
|
||||
|
||||
size += 4 - size % 4;
|
||||
|
||||
if (heap == NULL || handleIndexPtr == NULL || size == 0) {
|
||||
goto err;
|
||||
}
|
||||
@@ -599,7 +601,7 @@ bool heapUnlock(Heap* heap, int handleIndex)
|
||||
}
|
||||
|
||||
// 0x4532AC
|
||||
static bool heapPrintStats(Heap* heap, char* dest)
|
||||
static bool heapPrintStats(Heap* heap, char* dest, size_t size)
|
||||
{
|
||||
if (heap == NULL || dest == NULL) {
|
||||
return false;
|
||||
@@ -617,7 +619,7 @@ static bool heapPrintStats(Heap* heap, char* dest)
|
||||
"Total handles: %d\n"
|
||||
"Total heaps: %d";
|
||||
|
||||
sprintf(dest, format,
|
||||
snprintf(dest, size, format,
|
||||
heap->freeBlocks,
|
||||
heap->freeSize,
|
||||
heap->moveableBlocks,
|
||||
@@ -912,7 +914,7 @@ system:
|
||||
|
||||
if (1) {
|
||||
char stats[512];
|
||||
if (heapPrintStats(heap, stats)) {
|
||||
if (heapPrintStats(heap, stats, sizeof(stats))) {
|
||||
debugPrint("\n%s\n", stats);
|
||||
}
|
||||
|
||||
|
||||
+1229
File diff suppressed because it is too large
Load Diff
+52
@@ -0,0 +1,52 @@
|
||||
#ifndef FALLOUT_INPUT_H_
|
||||
#define FALLOUT_INPUT_H_
|
||||
|
||||
namespace fallout {
|
||||
|
||||
typedef void(IdleFunc)();
|
||||
typedef void(FocusFunc)(bool focus);
|
||||
typedef void(TickerProc)();
|
||||
|
||||
typedef int(PauseHandler)();
|
||||
typedef int(ScreenshotHandler)(int width, int height, unsigned char* buffer, unsigned char* palette);
|
||||
|
||||
int inputInit(int a1);
|
||||
void inputExit();
|
||||
int inputGetInput();
|
||||
void _process_bk();
|
||||
void enqueueInputEvent(int a1);
|
||||
void inputEventQueueReset();
|
||||
void tickersExecute();
|
||||
void tickersAdd(TickerProc* fn);
|
||||
void tickersRemove(TickerProc* fn);
|
||||
void tickersEnable();
|
||||
void tickersDisable();
|
||||
void pauseHandlerConfigure(int keyCode, PauseHandler* fn);
|
||||
void takeScreenshot();
|
||||
int screenshotHandlerDefaultImpl(int width, int height, unsigned char* data, unsigned char* palette);
|
||||
void screenshotHandlerConfigure(int keyCode, ScreenshotHandler* handler);
|
||||
unsigned int getTicks();
|
||||
void inputPauseForTocks(unsigned int ms);
|
||||
void inputBlockForTocks(unsigned int ms);
|
||||
unsigned int getTicksSince(unsigned int a1);
|
||||
unsigned int getTicksBetween(unsigned int a1, unsigned int a2);
|
||||
unsigned int _get_bk_time();
|
||||
void inputSetKeyboardKeyRepeatRate(int value);
|
||||
int inputGetKeyboardKeyRepeatRate();
|
||||
void inputSetKeyboardKeyRepeatDelay(int value);
|
||||
int inputGetKeyboardKeyRepeatDelay();
|
||||
void inputSetFocusFunc(FocusFunc* func);
|
||||
FocusFunc* inputGetFocusFunc();
|
||||
void inputSetIdleFunc(IdleFunc* func);
|
||||
IdleFunc* inputGetIdleFunc();
|
||||
int _GNW95_input_init();
|
||||
void _GNW95_process_message();
|
||||
void _GNW95_clear_time_stamps();
|
||||
void _GNW95_lost_focus();
|
||||
|
||||
void beginTextInput();
|
||||
void endTextInput();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* FALLOUT_INPUT_H_ */
|
||||
+332
-95
@@ -8,7 +8,6 @@
|
||||
#include "color.h"
|
||||
#include "combat.h"
|
||||
#include "config.h"
|
||||
#include "core.h"
|
||||
#include "critter.h"
|
||||
#include "cycle.h"
|
||||
#include "debug.h"
|
||||
@@ -16,12 +15,14 @@
|
||||
#include "draw.h"
|
||||
#include "endgame.h"
|
||||
#include "game.h"
|
||||
#include "game_config.h"
|
||||
#include "game_mouse.h"
|
||||
#include "game_sound.h"
|
||||
#include "geometry.h"
|
||||
#include "input.h"
|
||||
#include "item.h"
|
||||
#include "kb.h"
|
||||
#include "memory.h"
|
||||
#include "mouse.h"
|
||||
#include "object.h"
|
||||
#include "platform_compat.h"
|
||||
#include "proto.h"
|
||||
@@ -29,6 +30,7 @@
|
||||
#include "proto_types.h"
|
||||
#include "skill.h"
|
||||
#include "stat.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "tile.h"
|
||||
#include "window_manager.h"
|
||||
@@ -122,6 +124,15 @@ static int indicatorBoxCompareByPosition(const void* a, const void* b);
|
||||
static void indicatorBarRender(int count);
|
||||
static bool indicatorBarAdd(int indicator);
|
||||
|
||||
static void customInterfaceBarInit();
|
||||
static void customInterfaceBarExit();
|
||||
|
||||
static void sidePanelsInit();
|
||||
static void sidePanelsExit();
|
||||
static void sidePanelsHide();
|
||||
static void sidePanelsShow();
|
||||
static void sidePanelsDraw(const char* path, int win, bool isLeading);
|
||||
|
||||
// 0x518F08
|
||||
static bool gInterfaceBarInitialized = false;
|
||||
|
||||
@@ -132,7 +143,7 @@ static bool gInterfaceBarSwapHandsInProgress = false;
|
||||
static bool gInterfaceBarEnabled = false;
|
||||
|
||||
// 0x518F14
|
||||
static bool _intfaceHidden = false;
|
||||
static bool gInterfaceBarHidden = false;
|
||||
|
||||
// 0x518F18
|
||||
static int gInventoryButton = -1;
|
||||
@@ -159,7 +170,7 @@ static int gSingleAttackButton = -1;
|
||||
static int gInterfaceCurrentHand = HAND_LEFT;
|
||||
|
||||
// 0x518F7C
|
||||
static const Rect gInterfaceBarMainActionRect = { 267, 26, 455, 93 };
|
||||
static Rect gInterfaceBarMainActionRect;
|
||||
|
||||
// 0x518F8C
|
||||
static int gChangeHandsButton = -1;
|
||||
@@ -170,7 +181,7 @@ static bool gInterfaceBarEndButtonsIsVisible = false;
|
||||
// Combat mode curtains rect.
|
||||
//
|
||||
// 0x518FA0
|
||||
static const Rect gInterfaceBarEndButtonsRect = { 580, 38, 637, 96 };
|
||||
static Rect gInterfaceBarEndButtonsRect;
|
||||
|
||||
// 0x518FB0
|
||||
static int gEndTurnButton = -1;
|
||||
@@ -179,7 +190,7 @@ static int gEndTurnButton = -1;
|
||||
static int gEndCombatButton = -1;
|
||||
|
||||
// 0x518FD4
|
||||
static const Rect gInterfaceBarActionPointsBarRect = { 316, 14, 406, 19 };
|
||||
static Rect gInterfaceBarActionPointsBarRect;
|
||||
|
||||
// 0x518FE8
|
||||
static IndicatorDescription gIndicatorDescriptions[INDICATOR_COUNT] = {
|
||||
@@ -278,6 +289,16 @@ static FrmImage _greenLightFrmImage;
|
||||
static FrmImage _yellowLightFrmImage;
|
||||
static FrmImage _redLightFrmImage;
|
||||
|
||||
int gInterfaceBarContentOffset = 0;
|
||||
int gInterfaceBarWidth = -1;
|
||||
bool gInterfaceBarIsCustom = false;
|
||||
static Art* gCustomInterfaceBarBackground = nullptr;
|
||||
|
||||
int gInterfaceSidePanelsImageId = 2;
|
||||
bool gInterfaceSidePanelsExtendFromScreenEdge = false;
|
||||
static int gInterfaceSidePanelsLeadingWindow = -1;
|
||||
static int gInterfaceSidePanelsTrailingWindow = -1;
|
||||
|
||||
// intface_init
|
||||
// 0x45D880
|
||||
int interfaceInit()
|
||||
@@ -288,12 +309,18 @@ int interfaceInit()
|
||||
return -1;
|
||||
}
|
||||
|
||||
gInterfaceBarInitialized = 1;
|
||||
customInterfaceBarInit();
|
||||
|
||||
int interfaceBarWindowX = (screenGetWidth() - INTERFACE_BAR_WIDTH) / 2;
|
||||
gInterfaceBarActionPointsBarRect = { 316 + gInterfaceBarContentOffset, 14, 406 + gInterfaceBarContentOffset, 19 };
|
||||
gInterfaceBarEndButtonsRect = { 580 + gInterfaceBarContentOffset, 38, 637 + gInterfaceBarContentOffset, 96 };
|
||||
gInterfaceBarMainActionRect = { 267 + gInterfaceBarContentOffset, 26, 455 + gInterfaceBarContentOffset, 93 };
|
||||
|
||||
gInterfaceBarInitialized = true;
|
||||
|
||||
int interfaceBarWindowX = (screenGetWidth() - gInterfaceBarWidth) / 2;
|
||||
int interfaceBarWindowY = screenGetHeight() - INTERFACE_BAR_HEIGHT;
|
||||
|
||||
gInterfaceBarWindow = windowCreate(interfaceBarWindowX, interfaceBarWindowY, INTERFACE_BAR_WIDTH, INTERFACE_BAR_HEIGHT, _colorTable[0], WINDOW_HIDDEN);
|
||||
gInterfaceBarWindow = windowCreate(interfaceBarWindowX, interfaceBarWindowY, gInterfaceBarWidth, INTERFACE_BAR_HEIGHT, _colorTable[0], WINDOW_HIDDEN);
|
||||
if (gInterfaceBarWindow == -1) {
|
||||
// NOTE: Uninline.
|
||||
return intface_fatal_error(-1);
|
||||
@@ -305,15 +332,18 @@ int interfaceInit()
|
||||
return intface_fatal_error(-1);
|
||||
}
|
||||
|
||||
FrmImage backgroundFrmImage;
|
||||
fid = buildFid(OBJ_TYPE_INTERFACE, 16, 0, 0, 0);
|
||||
if (!backgroundFrmImage.lock(fid)) {
|
||||
// NOTE: Uninline.
|
||||
return intface_fatal_error(-1);
|
||||
}
|
||||
if (gInterfaceBarIsCustom) {
|
||||
blitBufferToBuffer(customInterfaceBarGetBackgroundImageData(), gInterfaceBarWidth, INTERFACE_BAR_HEIGHT - 1, gInterfaceBarWidth, gInterfaceWindowBuffer, gInterfaceBarWidth);
|
||||
} else {
|
||||
FrmImage backgroundFrmImage;
|
||||
fid = buildFid(OBJ_TYPE_INTERFACE, 16, 0, 0, 0);
|
||||
if (!backgroundFrmImage.lock(fid)) {
|
||||
return intface_fatal_error(-1);
|
||||
}
|
||||
|
||||
blitBufferToBuffer(backgroundFrmImage.getData(), INTERFACE_BAR_WIDTH, INTERFACE_BAR_HEIGHT - 1, INTERFACE_BAR_WIDTH, gInterfaceWindowBuffer, 640);
|
||||
backgroundFrmImage.unlock();
|
||||
blitBufferToBuffer(backgroundFrmImage.getData(), gInterfaceBarWidth, INTERFACE_BAR_HEIGHT - 1, gInterfaceBarWidth, gInterfaceWindowBuffer, gInterfaceBarWidth);
|
||||
backgroundFrmImage.unlock();
|
||||
}
|
||||
|
||||
fid = buildFid(OBJ_TYPE_INTERFACE, 47, 0, 0, 0);
|
||||
if (!_inventoryButtonNormalFrmImage.lock(fid)) {
|
||||
@@ -327,7 +357,7 @@ int interfaceInit()
|
||||
return intface_fatal_error(-1);
|
||||
}
|
||||
|
||||
gInventoryButton = buttonCreate(gInterfaceBarWindow, 211, 40, 32, 21, -1, -1, -1, KEY_LOWERCASE_I, _inventoryButtonNormalFrmImage.getData(), _inventoryButtonPressedFrmImage.getData(), NULL, 0);
|
||||
gInventoryButton = buttonCreate(gInterfaceBarWindow, 211 + gInterfaceBarContentOffset, 40, 32, 21, -1, -1, -1, KEY_LOWERCASE_I, _inventoryButtonNormalFrmImage.getData(), _inventoryButtonPressedFrmImage.getData(), NULL, 0);
|
||||
if (gInventoryButton == -1) {
|
||||
// NOTE: Uninline.
|
||||
return intface_fatal_error(-1);
|
||||
@@ -347,7 +377,7 @@ int interfaceInit()
|
||||
return intface_fatal_error(-1);
|
||||
}
|
||||
|
||||
gOptionsButton = buttonCreate(gInterfaceBarWindow, 210, 61, 34, 34, -1, -1, -1, KEY_LOWERCASE_O, _optionsButtonNormalFrmImage.getData(), _optionsButtonPressedFrmImage.getData(), NULL, 0);
|
||||
gOptionsButton = buttonCreate(gInterfaceBarWindow, 210 + gInterfaceBarContentOffset, 61, 34, 34, -1, -1, -1, KEY_LOWERCASE_O, _optionsButtonNormalFrmImage.getData(), _optionsButtonPressedFrmImage.getData(), NULL, 0);
|
||||
if (gOptionsButton == -1) {
|
||||
// NOTE: Uninline.
|
||||
return intface_fatal_error(-1);
|
||||
@@ -373,7 +403,7 @@ int interfaceInit()
|
||||
return intface_fatal_error(-1);
|
||||
}
|
||||
|
||||
gSkilldexButton = buttonCreate(gInterfaceBarWindow, 523, 6, 22, 21, -1, -1, -1, KEY_LOWERCASE_S, _skilldexButtonNormalFrmImage.getData(), _skilldexButtonPressedFrmImage.getData(), NULL, BUTTON_FLAG_TRANSPARENT);
|
||||
gSkilldexButton = buttonCreate(gInterfaceBarWindow, 523 + gInterfaceBarContentOffset, 6, 22, 21, -1, -1, -1, KEY_LOWERCASE_S, _skilldexButtonNormalFrmImage.getData(), _skilldexButtonPressedFrmImage.getData(), NULL, BUTTON_FLAG_TRANSPARENT);
|
||||
if (gSkilldexButton == -1) {
|
||||
// NOTE: Uninline.
|
||||
return intface_fatal_error(-1);
|
||||
@@ -400,7 +430,7 @@ int interfaceInit()
|
||||
return intface_fatal_error(-1);
|
||||
}
|
||||
|
||||
gMapButton = buttonCreate(gInterfaceBarWindow, 526, 39, 41, 19, -1, -1, -1, KEY_TAB, _mapButtonNormalFrmImage.getData(), _mapButtonPressedFrmImage.getData(), NULL, BUTTON_FLAG_TRANSPARENT);
|
||||
gMapButton = buttonCreate(gInterfaceBarWindow, 526 + gInterfaceBarContentOffset, 39, 41, 19, -1, -1, -1, KEY_TAB, _mapButtonNormalFrmImage.getData(), _mapButtonPressedFrmImage.getData(), NULL, BUTTON_FLAG_TRANSPARENT);
|
||||
if (gMapButton == -1) {
|
||||
// NOTE: Uninline.
|
||||
return intface_fatal_error(-1);
|
||||
@@ -421,7 +451,7 @@ int interfaceInit()
|
||||
return intface_fatal_error(-1);
|
||||
}
|
||||
|
||||
gPipboyButton = buttonCreate(gInterfaceBarWindow, 526, 77, 41, 19, -1, -1, -1, KEY_LOWERCASE_P, _pipboyButtonNormalFrmImage.getData(), _pipboyButtonPressedFrmImage.getData(), NULL, 0);
|
||||
gPipboyButton = buttonCreate(gInterfaceBarWindow, 526 + gInterfaceBarContentOffset, 77, 41, 19, -1, -1, -1, KEY_LOWERCASE_P, _pipboyButtonNormalFrmImage.getData(), _pipboyButtonPressedFrmImage.getData(), NULL, 0);
|
||||
if (gPipboyButton == -1) {
|
||||
// NOTE: Uninline.
|
||||
return intface_fatal_error(-1);
|
||||
@@ -442,7 +472,7 @@ int interfaceInit()
|
||||
return intface_fatal_error(-1);
|
||||
}
|
||||
|
||||
gCharacterButton = buttonCreate(gInterfaceBarWindow, 526, 58, 41, 19, -1, -1, -1, KEY_LOWERCASE_C, _characterButtonNormalFrmImage.getData(), _characterButtonPressedFrmImage.getData(), NULL, 0);
|
||||
gCharacterButton = buttonCreate(gInterfaceBarWindow, 526 + gInterfaceBarContentOffset, 58, 41, 19, -1, -1, -1, KEY_LOWERCASE_C, _characterButtonNormalFrmImage.getData(), _characterButtonPressedFrmImage.getData(), NULL, 0);
|
||||
if (gCharacterButton == -1) {
|
||||
// NOTE: Uninline.
|
||||
return intface_fatal_error(-1);
|
||||
@@ -472,7 +502,7 @@ int interfaceInit()
|
||||
memcpy(_itemButtonUp, _itemButtonNormalFrmImage.getData(), sizeof(_itemButtonUp));
|
||||
memcpy(_itemButtonDown, _itemButtonPressedFrmImage.getData(), sizeof(_itemButtonDown));
|
||||
|
||||
gSingleAttackButton = buttonCreate(gInterfaceBarWindow, 267, 26, 188, 67, -1, -1, -1, -20, _itemButtonUp, _itemButtonDown, NULL, BUTTON_FLAG_TRANSPARENT);
|
||||
gSingleAttackButton = buttonCreate(gInterfaceBarWindow, 267 + gInterfaceBarContentOffset, 26, 188, 67, -1, -1, -1, -20, _itemButtonUp, _itemButtonDown, NULL, BUTTON_FLAG_TRANSPARENT);
|
||||
if (gSingleAttackButton == -1) {
|
||||
// NOTE: Uninline.
|
||||
return intface_fatal_error(-1);
|
||||
@@ -500,7 +530,7 @@ int interfaceInit()
|
||||
}
|
||||
|
||||
// Swap hands button
|
||||
gChangeHandsButton = buttonCreate(gInterfaceBarWindow, 218, 6, 22, 21, -1, -1, -1, KEY_LOWERCASE_B, _changeHandsButtonNormalFrmImage.getData(), _changeHandsButtonPressedFrmImage.getData(), NULL, BUTTON_FLAG_TRANSPARENT);
|
||||
gChangeHandsButton = buttonCreate(gInterfaceBarWindow, 218 + gInterfaceBarContentOffset, 6, 22, 21, -1, -1, -1, KEY_LOWERCASE_B, _changeHandsButtonNormalFrmImage.getData(), _changeHandsButtonPressedFrmImage.getData(), NULL, BUTTON_FLAG_TRANSPARENT);
|
||||
if (gChangeHandsButton == -1) {
|
||||
// NOTE: Uninline.
|
||||
return intface_fatal_error(-1);
|
||||
@@ -533,7 +563,7 @@ int interfaceInit()
|
||||
return intface_fatal_error(-1);
|
||||
}
|
||||
|
||||
blitBufferToBuffer(gInterfaceWindowBuffer + 640 * 14 + 316, 90, 5, 640, gInterfaceActionPointsBarBackground, 90);
|
||||
blitBufferToBuffer(gInterfaceWindowBuffer + gInterfaceBarWidth * 14 + 316 + gInterfaceBarContentOffset, 90, 5, gInterfaceBarWidth, gInterfaceActionPointsBarBackground, 90);
|
||||
|
||||
if (indicatorBarInit() == -1) {
|
||||
// NOTE: Uninline.
|
||||
@@ -547,9 +577,12 @@ int interfaceInit()
|
||||
|
||||
displayMonitorInit();
|
||||
|
||||
// SFALL
|
||||
sidePanelsInit();
|
||||
|
||||
gInterfaceBarEnabled = true;
|
||||
gInterfaceBarInitialized = false;
|
||||
_intfaceHidden = 1;
|
||||
gInterfaceBarHidden = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -560,7 +593,7 @@ void interfaceReset()
|
||||
interfaceBarEnable();
|
||||
|
||||
// NOTE: Uninline.
|
||||
intface_hide();
|
||||
interfaceBarHide();
|
||||
|
||||
indicatorBarRefresh();
|
||||
displayMonitorReset();
|
||||
@@ -575,6 +608,9 @@ void interfaceReset()
|
||||
void interfaceFree()
|
||||
{
|
||||
if (gInterfaceBarWindow != -1) {
|
||||
// SFALL
|
||||
sidePanelsExit();
|
||||
|
||||
displayMonitorExit();
|
||||
|
||||
_redLightFrmImage.unlock();
|
||||
@@ -657,6 +693,8 @@ void interfaceFree()
|
||||
}
|
||||
}
|
||||
|
||||
customInterfaceBarExit();
|
||||
|
||||
interfaceBarFree();
|
||||
}
|
||||
|
||||
@@ -669,11 +707,11 @@ int interfaceLoad(File* stream)
|
||||
}
|
||||
}
|
||||
|
||||
int interfaceBarEnabled;
|
||||
if (fileReadInt32(stream, &interfaceBarEnabled) == -1) return -1;
|
||||
bool interfaceBarEnabled;
|
||||
if (fileReadBool(stream, &interfaceBarEnabled) == -1) return -1;
|
||||
|
||||
int v2;
|
||||
if (fileReadInt32(stream, &v2) == -1) return -1;
|
||||
bool interfaceBarHidden;
|
||||
if (fileReadBool(stream, &interfaceBarHidden) == -1) return -1;
|
||||
|
||||
int interfaceCurrentHand;
|
||||
if (fileReadInt32(stream, &interfaceCurrentHand) == -1) return -1;
|
||||
@@ -685,11 +723,11 @@ int interfaceLoad(File* stream)
|
||||
interfaceBarEnable();
|
||||
}
|
||||
|
||||
if (v2) {
|
||||
if (interfaceBarHidden) {
|
||||
// NOTE: Uninline.
|
||||
intface_hide();
|
||||
interfaceBarHide();
|
||||
} else {
|
||||
_intface_show();
|
||||
interfaceBarShow();
|
||||
}
|
||||
|
||||
interfaceRenderHitPoints(false);
|
||||
@@ -725,10 +763,10 @@ int interfaceSave(File* stream)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fileWriteInt32(stream, gInterfaceBarEnabled) == -1) return -1;
|
||||
if (fileWriteInt32(stream, _intfaceHidden) == -1) return -1;
|
||||
if (fileWriteBool(stream, gInterfaceBarEnabled) == -1) return -1;
|
||||
if (fileWriteBool(stream, gInterfaceBarHidden) == -1) return -1;
|
||||
if (fileWriteInt32(stream, gInterfaceCurrentHand) == -1) return -1;
|
||||
if (fileWriteInt32(stream, gInterfaceBarEndButtonsIsVisible) == -1) return -1;
|
||||
if (fileWriteBool(stream, gInterfaceBarEndButtonsIsVisible) == -1) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -736,29 +774,38 @@ int interfaceSave(File* stream)
|
||||
// NOTE: Inlined.
|
||||
//
|
||||
// 0x45E9E0
|
||||
void intface_hide()
|
||||
void interfaceBarHide()
|
||||
{
|
||||
if (gInterfaceBarWindow != -1) {
|
||||
if (!_intfaceHidden) {
|
||||
if (!gInterfaceBarHidden) {
|
||||
windowHide(gInterfaceBarWindow);
|
||||
_intfaceHidden = 1;
|
||||
gInterfaceBarHidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
// SFALL
|
||||
sidePanelsHide();
|
||||
|
||||
indicatorBarRefresh();
|
||||
}
|
||||
|
||||
// 0x45EA10
|
||||
void _intface_show()
|
||||
void interfaceBarShow()
|
||||
{
|
||||
if (gInterfaceBarWindow != -1) {
|
||||
if (_intfaceHidden) {
|
||||
if (gInterfaceBarHidden) {
|
||||
interfaceUpdateItems(false, INTERFACE_ITEM_ACTION_DEFAULT, INTERFACE_ITEM_ACTION_DEFAULT);
|
||||
interfaceRenderHitPoints(false);
|
||||
interfaceRenderArmorClass(false);
|
||||
windowUnhide(gInterfaceBarWindow);
|
||||
_intfaceHidden = false;
|
||||
windowShow(gInterfaceBarWindow);
|
||||
sidePanelsShow();
|
||||
gInterfaceBarHidden = false;
|
||||
}
|
||||
}
|
||||
|
||||
// SFALL
|
||||
sidePanelsShow();
|
||||
|
||||
indicatorBarRefresh();
|
||||
}
|
||||
|
||||
@@ -888,10 +935,10 @@ void interfaceRenderHitPoints(bool animate)
|
||||
if (animate) {
|
||||
int delay = 250 / (abs(gInterfaceLastRenderedHitPoints - hp) + 1);
|
||||
for (int index = 0; index < count; index++) {
|
||||
interfaceRenderCounter(473, 40, v1[index], v1[index + 1], v2[index], delay);
|
||||
interfaceRenderCounter(473 + gInterfaceBarContentOffset, 40, v1[index], v1[index + 1], v2[index], delay);
|
||||
}
|
||||
} else {
|
||||
interfaceRenderCounter(473, 40, gInterfaceLastRenderedHitPoints, hp, color, 0);
|
||||
interfaceRenderCounter(473 + gInterfaceBarContentOffset, 40, gInterfaceLastRenderedHitPoints, hp, color, 0);
|
||||
}
|
||||
|
||||
gInterfaceLastRenderedHitPoints = hp;
|
||||
@@ -910,7 +957,7 @@ void interfaceRenderArmorClass(bool animate)
|
||||
delay = 250 / (abs(gInterfaceLastRenderedArmorClass - armorClass) + 1);
|
||||
}
|
||||
|
||||
interfaceRenderCounter(473, 75, gInterfaceLastRenderedArmorClass, armorClass, 0, delay);
|
||||
interfaceRenderCounter(473 + gInterfaceBarContentOffset, 75, gInterfaceLastRenderedArmorClass, armorClass, 0, delay);
|
||||
|
||||
gInterfaceLastRenderedArmorClass = armorClass;
|
||||
}
|
||||
@@ -924,7 +971,7 @@ void interfaceRenderActionPoints(int actionPointsLeft, int bonusActionPoints)
|
||||
return;
|
||||
}
|
||||
|
||||
blitBufferToBuffer(gInterfaceActionPointsBarBackground, 90, 5, 90, gInterfaceWindowBuffer + 14 * 640 + 316, 640);
|
||||
blitBufferToBuffer(gInterfaceActionPointsBarBackground, 90, 5, 90, gInterfaceWindowBuffer + 14 * gInterfaceBarWidth + gInterfaceBarContentOffset + 316, gInterfaceBarWidth);
|
||||
|
||||
if (actionPointsLeft == -1) {
|
||||
frmData = _redLightFrmImage.getData();
|
||||
@@ -952,11 +999,11 @@ void interfaceRenderActionPoints(int actionPointsLeft, int bonusActionPoints)
|
||||
|
||||
int index;
|
||||
for (index = 0; index < actionPointsLeft; index++) {
|
||||
blitBufferToBuffer(frmData, 5, 5, 5, gInterfaceWindowBuffer + 14 * 640 + 316 + index * 9, 640);
|
||||
blitBufferToBuffer(frmData, 5, 5, 5, gInterfaceWindowBuffer + 14 * gInterfaceBarWidth + 316 + index * 9 + gInterfaceBarContentOffset, gInterfaceBarWidth);
|
||||
}
|
||||
|
||||
for (; index < (actionPointsLeft + bonusActionPoints); index++) {
|
||||
blitBufferToBuffer(_yellowLightFrmImage.getData(), 5, 5, 5, gInterfaceWindowBuffer + 14 * 640 + 316 + index * 9, 640);
|
||||
blitBufferToBuffer(_yellowLightFrmImage.getData(), 5, 5, 5, gInterfaceWindowBuffer + 14 * gInterfaceBarWidth + 316 + gInterfaceBarContentOffset + index * 9, gInterfaceBarWidth);
|
||||
}
|
||||
|
||||
if (!gInterfaceBarInitialized) {
|
||||
@@ -1308,7 +1355,6 @@ int _intface_update_ammo_lights()
|
||||
int ratio = 0;
|
||||
|
||||
if (p->isWeapon != 0) {
|
||||
// calls sub_478674 twice, probably because if min/max kind macro
|
||||
int maximum = ammoGetCapacity(p->item);
|
||||
if (maximum > 0) {
|
||||
int current = ammoGetQuantity(p->item);
|
||||
@@ -1316,7 +1362,6 @@ int _intface_update_ammo_lights()
|
||||
}
|
||||
} else {
|
||||
if (itemGetType(p->item) == ITEM_TYPE_MISC) {
|
||||
// calls sub_4793D0 twice, probably because if min/max kind macro
|
||||
int maximum = miscItemGetMaxCharges(p->item);
|
||||
if (maximum > 0) {
|
||||
int current = miscItemGetCharges(p->item);
|
||||
@@ -1325,7 +1370,7 @@ int _intface_update_ammo_lights()
|
||||
}
|
||||
}
|
||||
|
||||
interfaceUpdateAmmoBar(463, ratio);
|
||||
interfaceUpdateAmmoBar(463 + gInterfaceBarContentOffset, ratio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1356,21 +1401,26 @@ void interfaceBarEndButtonsShow(bool animated)
|
||||
int time = 0;
|
||||
int frame = 0;
|
||||
while (frame < frameCount) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
if (getTicksSince(time) >= delay) {
|
||||
unsigned char* src = artGetFrameData(art, frame, 0);
|
||||
if (src != NULL) {
|
||||
blitBufferToBuffer(src, 57, 58, 57, gInterfaceWindowBuffer + 640 * 38 + 580, 640);
|
||||
blitBufferToBuffer(src, 57, 58, 57, gInterfaceWindowBuffer + gInterfaceBarWidth * 38 + 580 + gInterfaceBarContentOffset, gInterfaceBarWidth);
|
||||
windowRefreshRect(gInterfaceBarWindow, &gInterfaceBarEndButtonsRect);
|
||||
}
|
||||
|
||||
time = _get_time();
|
||||
time = getTicks();
|
||||
frame++;
|
||||
}
|
||||
gameMouseRefresh();
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
} else {
|
||||
unsigned char* src = artGetFrameData(art, frameCount - 1, 0);
|
||||
blitBufferToBuffer(src, 57, 58, 57, gInterfaceWindowBuffer + 640 * 38 + 580, 640);
|
||||
blitBufferToBuffer(src, 57, 58, 57, gInterfaceWindowBuffer + gInterfaceBarWidth * 38 + 580 + gInterfaceBarContentOffset, gInterfaceBarWidth);
|
||||
windowRefreshRect(gInterfaceBarWindow, &gInterfaceBarEndButtonsRect);
|
||||
}
|
||||
|
||||
@@ -1410,23 +1460,28 @@ void interfaceBarEndButtonsHide(bool animated)
|
||||
int frame = artGetFrameCount(art);
|
||||
|
||||
while (frame != 0) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
if (getTicksSince(time) >= delay) {
|
||||
unsigned char* src = artGetFrameData(art, frame - 1, 0);
|
||||
unsigned char* dest = gInterfaceWindowBuffer + 640 * 38 + 580;
|
||||
unsigned char* dest = gInterfaceWindowBuffer + gInterfaceBarWidth * 38 + 580 + gInterfaceBarContentOffset;
|
||||
if (src != NULL) {
|
||||
blitBufferToBuffer(src, 57, 58, 57, dest, 640);
|
||||
blitBufferToBuffer(src, 57, 58, 57, dest, gInterfaceBarWidth);
|
||||
windowRefreshRect(gInterfaceBarWindow, &gInterfaceBarEndButtonsRect);
|
||||
}
|
||||
|
||||
time = _get_time();
|
||||
time = getTicks();
|
||||
frame--;
|
||||
}
|
||||
gameMouseRefresh();
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
} else {
|
||||
unsigned char* dest = gInterfaceWindowBuffer + 640 * 38 + 580;
|
||||
unsigned char* dest = gInterfaceWindowBuffer + gInterfaceBarWidth * 38 + 580 + gInterfaceBarContentOffset;
|
||||
unsigned char* src = artGetFrameData(art, 0, 0);
|
||||
blitBufferToBuffer(src, 57, 58, 57, dest, 640);
|
||||
blitBufferToBuffer(src, 57, 58, 57, dest, gInterfaceBarWidth);
|
||||
windowRefreshRect(gInterfaceBarWindow, &gInterfaceBarEndButtonsRect);
|
||||
}
|
||||
|
||||
@@ -1449,7 +1504,7 @@ void interfaceBarEndButtonsRenderGreenLights()
|
||||
}
|
||||
|
||||
soundPlayFile("icombat2");
|
||||
blitBufferToBufferTrans(lightsFrmImage.getData(), 57, 58, 57, gInterfaceWindowBuffer + 38 * 640 + 580, 640);
|
||||
blitBufferToBufferTrans(lightsFrmImage.getData(), 57, 58, 57, gInterfaceWindowBuffer + 38 * gInterfaceBarWidth + 580 + gInterfaceBarContentOffset, gInterfaceBarWidth);
|
||||
windowRefreshRect(gInterfaceBarWindow, &gInterfaceBarEndButtonsRect);
|
||||
}
|
||||
}
|
||||
@@ -1469,7 +1524,7 @@ void interfaceBarEndButtonsRenderRedLights()
|
||||
}
|
||||
|
||||
soundPlayFile("icombat1");
|
||||
blitBufferToBufferTrans(lightsFrmImage.getData(), 57, 58, 57, gInterfaceWindowBuffer + 38 * 640 + 580, 640);
|
||||
blitBufferToBufferTrans(lightsFrmImage.getData(), 57, 58, 57, gInterfaceWindowBuffer + 38 * gInterfaceBarWidth + 580 + gInterfaceBarContentOffset, gInterfaceBarWidth);
|
||||
windowRefreshRect(gInterfaceBarWindow, &gInterfaceBarEndButtonsRect);
|
||||
}
|
||||
}
|
||||
@@ -1801,11 +1856,16 @@ static void interfaceBarSwapHandsAnimatePutAwayTakeOutSequence(int previousWeapo
|
||||
gameMouseSetCursor(MOUSE_CURSOR_WAIT_WATCH);
|
||||
|
||||
while (gInterfaceBarSwapHandsInProgress) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
if (_game_user_wants_to_quit) {
|
||||
break;
|
||||
}
|
||||
|
||||
_get_input();
|
||||
inputGetInput();
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
gameMouseSetCursor(MOUSE_CURSOR_NONE);
|
||||
@@ -1840,7 +1900,7 @@ static int endTurnButtonInit()
|
||||
return -1;
|
||||
}
|
||||
|
||||
gEndTurnButton = buttonCreate(gInterfaceBarWindow, 590, 43, 38, 22, -1, -1, -1, 32, _endTurnButtonNormalFrmImage.getData(), _endTurnButtonPressedFrmImage.getData(), NULL, 0);
|
||||
gEndTurnButton = buttonCreate(gInterfaceBarWindow, 590 + gInterfaceBarContentOffset, 43, 38, 22, -1, -1, -1, 32, _endTurnButtonNormalFrmImage.getData(), _endTurnButtonPressedFrmImage.getData(), NULL, 0);
|
||||
if (gEndTurnButton == -1) {
|
||||
return -1;
|
||||
}
|
||||
@@ -1892,7 +1952,7 @@ static int endCombatButtonInit()
|
||||
return -1;
|
||||
}
|
||||
|
||||
gEndCombatButton = buttonCreate(gInterfaceBarWindow, 590, 65, 38, 22, -1, -1, -1, 13, _endCombatButtonNormalFrmImage.getData(), _endCombatButtonPressedFrmImage.getData(), NULL, 0);
|
||||
gEndCombatButton = buttonCreate(gInterfaceBarWindow, 590 + gInterfaceBarContentOffset, 65, 38, 22, -1, -1, -1, 13, _endCombatButtonNormalFrmImage.getData(), _endCombatButtonPressedFrmImage.getData(), NULL, 0);
|
||||
if (gEndCombatButton == -1) {
|
||||
return -1;
|
||||
}
|
||||
@@ -1928,19 +1988,19 @@ static void interfaceUpdateAmmoBar(int x, int ratio)
|
||||
ratio -= 1;
|
||||
}
|
||||
|
||||
unsigned char* dest = gInterfaceWindowBuffer + 640 * 26 + x;
|
||||
unsigned char* dest = gInterfaceWindowBuffer + gInterfaceBarWidth * 26 + x;
|
||||
|
||||
for (int index = 70; index > ratio; index--) {
|
||||
*dest = 14;
|
||||
dest += 640;
|
||||
dest += gInterfaceBarWidth;
|
||||
}
|
||||
|
||||
while (ratio > 0) {
|
||||
*dest = 196;
|
||||
dest += 640;
|
||||
dest += gInterfaceBarWidth;
|
||||
|
||||
*dest = 14;
|
||||
dest += 640;
|
||||
dest += gInterfaceBarWidth;
|
||||
|
||||
ratio -= 2;
|
||||
}
|
||||
@@ -1996,7 +2056,7 @@ static void interfaceRenderCounter(int x, int y, int previousValue, int value, i
|
||||
}
|
||||
|
||||
unsigned char* numbers = _numbersFrmImage.getData() + offset;
|
||||
unsigned char* dest = gInterfaceWindowBuffer + 640 * y;
|
||||
unsigned char* dest = gInterfaceWindowBuffer + gInterfaceBarWidth * y;
|
||||
|
||||
unsigned char* downSrc = numbers + 90;
|
||||
unsigned char* upSrc = numbers + 99;
|
||||
@@ -2022,10 +2082,10 @@ static void interfaceRenderCounter(int x, int y, int previousValue, int value, i
|
||||
int tens = (normalizedValue / 10) % 10;
|
||||
int hundreds = normalizedValue / 100;
|
||||
|
||||
blitBufferToBuffer(numbers + 9 * hundreds, 9, 17, 360, hundredsDest, 640);
|
||||
blitBufferToBuffer(numbers + 9 * tens, 9, 17, 360, tensDest, 640);
|
||||
blitBufferToBuffer(numbers + 9 * ones, 9, 17, 360, onesDest, 640);
|
||||
blitBufferToBuffer(normalizedSign >= 0 ? plusSrc : minusSrc, 6, 17, 360, signDest, 640);
|
||||
blitBufferToBuffer(numbers + 9 * hundreds, 9, 17, 360, hundredsDest, gInterfaceBarWidth);
|
||||
blitBufferToBuffer(numbers + 9 * tens, 9, 17, 360, tensDest, gInterfaceBarWidth);
|
||||
blitBufferToBuffer(numbers + 9 * ones, 9, 17, 360, onesDest, gInterfaceBarWidth);
|
||||
blitBufferToBuffer(normalizedSign >= 0 ? plusSrc : minusSrc, 6, 17, 360, signDest, gInterfaceBarWidth);
|
||||
|
||||
if (!gInterfaceBarInitialized) {
|
||||
Rect numbersRect = { x, y, x + 33, y + 17 };
|
||||
@@ -2039,28 +2099,31 @@ static void interfaceRenderCounter(int x, int y, int previousValue, int value, i
|
||||
v49 = 1;
|
||||
}
|
||||
|
||||
blitBufferToBuffer(upSrc, 9, 17, 360, onesDest, 640);
|
||||
blitBufferToBuffer(upSrc, 9, 17, 360, onesDest, gInterfaceBarWidth);
|
||||
_mouse_info();
|
||||
gameMouseRefresh();
|
||||
coreDelay(delay);
|
||||
renderPresent();
|
||||
inputBlockForTocks(delay);
|
||||
windowRefreshRect(gInterfaceBarWindow, &numbersRect);
|
||||
|
||||
ones += v49;
|
||||
|
||||
if (ones > 9 || ones < 0) {
|
||||
blitBufferToBuffer(upSrc, 9, 17, 360, tensDest, 640);
|
||||
blitBufferToBuffer(upSrc, 9, 17, 360, tensDest, gInterfaceBarWidth);
|
||||
_mouse_info();
|
||||
gameMouseRefresh();
|
||||
coreDelay(delay);
|
||||
renderPresent();
|
||||
inputBlockForTocks(delay);
|
||||
windowRefreshRect(gInterfaceBarWindow, &numbersRect);
|
||||
|
||||
tens += v49;
|
||||
ones -= 10 * v49;
|
||||
if (tens == 10 || tens == -1) {
|
||||
blitBufferToBuffer(upSrc, 9, 17, 360, hundredsDest, 640);
|
||||
blitBufferToBuffer(upSrc, 9, 17, 360, hundredsDest, gInterfaceBarWidth);
|
||||
_mouse_info();
|
||||
gameMouseRefresh();
|
||||
coreDelay(delay);
|
||||
renderPresent();
|
||||
inputBlockForTocks(delay);
|
||||
windowRefreshRect(gInterfaceBarWindow, &numbersRect);
|
||||
|
||||
hundreds += v49;
|
||||
@@ -2069,34 +2132,38 @@ static void interfaceRenderCounter(int x, int y, int previousValue, int value, i
|
||||
hundreds -= 10 * v49;
|
||||
}
|
||||
|
||||
blitBufferToBuffer(downSrc, 9, 17, 360, hundredsDest, 640);
|
||||
blitBufferToBuffer(downSrc, 9, 17, 360, hundredsDest, gInterfaceBarWidth);
|
||||
_mouse_info();
|
||||
gameMouseRefresh();
|
||||
coreDelay(delay);
|
||||
renderPresent();
|
||||
inputBlockForTocks(delay);
|
||||
windowRefreshRect(gInterfaceBarWindow, &numbersRect);
|
||||
}
|
||||
|
||||
blitBufferToBuffer(downSrc, 9, 17, 360, tensDest, 640);
|
||||
coreDelay(delay);
|
||||
blitBufferToBuffer(downSrc, 9, 17, 360, tensDest, gInterfaceBarWidth);
|
||||
renderPresent();
|
||||
inputBlockForTocks(delay);
|
||||
windowRefreshRect(gInterfaceBarWindow, &numbersRect);
|
||||
}
|
||||
|
||||
blitBufferToBuffer(downSrc, 9, 17, 360, onesDest, 640);
|
||||
blitBufferToBuffer(downSrc, 9, 17, 360, onesDest, gInterfaceBarWidth);
|
||||
_mouse_info();
|
||||
gameMouseRefresh();
|
||||
coreDelay(delay);
|
||||
renderPresent();
|
||||
inputBlockForTocks(delay);
|
||||
windowRefreshRect(gInterfaceBarWindow, &numbersRect);
|
||||
|
||||
previousValue += change;
|
||||
|
||||
blitBufferToBuffer(numbers + 9 * hundreds, 9, 17, 360, hundredsDest, 640);
|
||||
blitBufferToBuffer(numbers + 9 * tens, 9, 17, 360, tensDest, 640);
|
||||
blitBufferToBuffer(numbers + 9 * ones, 9, 17, 360, onesDest, 640);
|
||||
blitBufferToBuffer(numbers + 9 * hundreds, 9, 17, 360, hundredsDest, gInterfaceBarWidth);
|
||||
blitBufferToBuffer(numbers + 9 * tens, 9, 17, 360, tensDest, gInterfaceBarWidth);
|
||||
blitBufferToBuffer(numbers + 9 * ones, 9, 17, 360, onesDest, gInterfaceBarWidth);
|
||||
|
||||
blitBufferToBuffer(previousValue >= 0 ? plusSrc : minusSrc, 6, 17, 360, signDest, 640);
|
||||
blitBufferToBuffer(previousValue >= 0 ? plusSrc : minusSrc, 6, 17, 360, signDest, gInterfaceBarWidth);
|
||||
_mouse_info();
|
||||
gameMouseRefresh();
|
||||
coreDelay(delay);
|
||||
renderPresent();
|
||||
inputBlockForTocks(delay);
|
||||
windowRefreshRect(gInterfaceBarWindow, &numbersRect);
|
||||
}
|
||||
}
|
||||
@@ -2130,7 +2197,7 @@ static int indicatorBarInit()
|
||||
}
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "%s%s", asc_5186C8, "intrface.msg");
|
||||
snprintf(path, sizeof(path), "%s%s", asc_5186C8, "intrface.msg");
|
||||
|
||||
if (rc != -1) {
|
||||
if (!messageListLoad(&messageList, path)) {
|
||||
@@ -2234,7 +2301,7 @@ static void indicatorBarReset()
|
||||
// 0x4614CC
|
||||
int indicatorBarRefresh()
|
||||
{
|
||||
if (gInterfaceBarWindow != -1 && gIndicatorBarIsVisible && !_intfaceHidden) {
|
||||
if (gInterfaceBarWindow != -1 && gIndicatorBarIsVisible && !gInterfaceBarHidden) {
|
||||
for (int index = 0; index < INDICATOR_SLOTS_COUNT; index++) {
|
||||
gIndicatorSlots[index] = -1;
|
||||
}
|
||||
@@ -2412,4 +2479,174 @@ bool indicatorBarHide()
|
||||
return oldIsVisible;
|
||||
}
|
||||
|
||||
static void customInterfaceBarInit()
|
||||
{
|
||||
gInterfaceBarContentOffset = gInterfaceBarWidth - 640;
|
||||
|
||||
if (gInterfaceBarContentOffset > 0 && screenGetWidth() > 640) {
|
||||
char path[COMPAT_MAX_PATH];
|
||||
snprintf(path, sizeof(path), "art\\intrface\\HR_IFACE_%d.FRM", gInterfaceBarWidth);
|
||||
|
||||
gCustomInterfaceBarBackground = artLoad(path);
|
||||
}
|
||||
|
||||
if (gCustomInterfaceBarBackground != nullptr) {
|
||||
gInterfaceBarIsCustom = true;
|
||||
} else {
|
||||
gInterfaceBarContentOffset = 0;
|
||||
gInterfaceBarWidth = 640;
|
||||
gInterfaceBarIsCustom = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void customInterfaceBarExit()
|
||||
{
|
||||
if (gCustomInterfaceBarBackground != nullptr) {
|
||||
internal_free(gCustomInterfaceBarBackground);
|
||||
gCustomInterfaceBarBackground = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char* customInterfaceBarGetBackgroundImageData()
|
||||
{
|
||||
if (!gInterfaceBarIsCustom) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return artGetFrameData(gCustomInterfaceBarBackground, 0, 0);
|
||||
}
|
||||
|
||||
static void sidePanelsInit()
|
||||
{
|
||||
if (gInterfaceBarMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gInterfaceSidePanelsImageId == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gInterfaceBarWidth >= screenGetWidth()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Rect windowRect;
|
||||
windowGetRect(gInterfaceBarWindow, &windowRect);
|
||||
|
||||
gInterfaceSidePanelsLeadingWindow = windowCreate(0, windowRect.top, windowRect.left, windowRect.bottom - windowRect.top + 1, 0, WINDOW_HIDDEN | WINDOW_DONT_MOVE_TOP);
|
||||
gInterfaceSidePanelsTrailingWindow = windowCreate(windowRect.right + 1, windowRect.top, screenGetWidth() - windowRect.right - 1, windowRect.bottom - windowRect.top + 1, 0, WINDOW_HIDDEN | WINDOW_DONT_MOVE_TOP);
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
snprintf(path, sizeof(path), "art\\intrface\\HR_IFACELFT%d.frm", gInterfaceSidePanelsImageId);
|
||||
sidePanelsDraw(path, gInterfaceSidePanelsLeadingWindow, true);
|
||||
|
||||
snprintf(path, sizeof(path), "art\\intrface\\HR_IFACERHT%d.frm", gInterfaceSidePanelsImageId);
|
||||
sidePanelsDraw(path, gInterfaceSidePanelsTrailingWindow, false);
|
||||
}
|
||||
|
||||
static void sidePanelsExit()
|
||||
{
|
||||
if (gInterfaceSidePanelsTrailingWindow != -1) {
|
||||
windowDestroy(gInterfaceSidePanelsTrailingWindow);
|
||||
gInterfaceSidePanelsTrailingWindow = -1;
|
||||
}
|
||||
|
||||
if (gInterfaceSidePanelsLeadingWindow != -1) {
|
||||
windowDestroy(gInterfaceSidePanelsLeadingWindow);
|
||||
gInterfaceSidePanelsLeadingWindow = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void sidePanelsHide()
|
||||
{
|
||||
if (gInterfaceSidePanelsLeadingWindow != -1) {
|
||||
windowHide(gInterfaceSidePanelsLeadingWindow);
|
||||
}
|
||||
|
||||
if (gInterfaceSidePanelsTrailingWindow != -1) {
|
||||
windowHide(gInterfaceSidePanelsTrailingWindow);
|
||||
}
|
||||
}
|
||||
|
||||
static void sidePanelsShow()
|
||||
{
|
||||
if (gInterfaceSidePanelsLeadingWindow != -1) {
|
||||
windowShow(gInterfaceSidePanelsLeadingWindow);
|
||||
}
|
||||
|
||||
if (gInterfaceSidePanelsTrailingWindow != -1) {
|
||||
windowShow(gInterfaceSidePanelsTrailingWindow);
|
||||
}
|
||||
}
|
||||
|
||||
static void sidePanelsDraw(const char* path, int win, bool isLeading)
|
||||
{
|
||||
Art* image = artLoad(path);
|
||||
if (image == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char* imageData = artGetFrameData(image, 0, 0);
|
||||
|
||||
int imageWidth = artGetWidth(image, 0, 0);
|
||||
int imageHeight = artGetHeight(image, 0, 0);
|
||||
|
||||
int windowWidth = windowGetWidth(win);
|
||||
int windowHeight = windowGetHeight(win);
|
||||
|
||||
int width = std::min(imageWidth, windowWidth);
|
||||
|
||||
if (!gInterfaceSidePanelsExtendFromScreenEdge && isLeading) {
|
||||
imageData += imageWidth - width;
|
||||
}
|
||||
|
||||
if (gInterfaceSidePanelsExtendFromScreenEdge && !isLeading) {
|
||||
imageData += imageWidth - width;
|
||||
}
|
||||
|
||||
blitBufferToBufferStretch(imageData,
|
||||
width,
|
||||
imageHeight,
|
||||
imageWidth,
|
||||
windowGetBuffer(win),
|
||||
windowWidth,
|
||||
windowHeight,
|
||||
windowWidth);
|
||||
|
||||
internal_free(image);
|
||||
}
|
||||
|
||||
// NOTE: Follows Sfall implementation of `GetCurrentAttackMode`. It slightly
|
||||
// differs from `interfaceGetCurrentHitMode` (can return one of `reload` hit
|
||||
// modes, the default is `punch`).
|
||||
//
|
||||
// 0x45EF6C
|
||||
bool interface_get_current_attack_mode(int* hit_mode)
|
||||
{
|
||||
if (gInterfaceBarWindow == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (gInterfaceItemStates[gInterfaceCurrentHand].action) {
|
||||
case INTERFACE_ITEM_ACTION_PRIMARY_AIMING:
|
||||
case INTERFACE_ITEM_ACTION_PRIMARY:
|
||||
*hit_mode = gInterfaceItemStates[gInterfaceCurrentHand].primaryHitMode;
|
||||
break;
|
||||
case INTERFACE_ITEM_ACTION_SECONDARY_AIMING:
|
||||
case INTERFACE_ITEM_ACTION_SECONDARY:
|
||||
*hit_mode = gInterfaceItemStates[gInterfaceCurrentHand].secondaryHitMode;
|
||||
break;
|
||||
case INTERFACE_ITEM_ACTION_RELOAD:
|
||||
*hit_mode = gInterfaceCurrentHand == HAND_LEFT
|
||||
? HIT_MODE_LEFT_WEAPON_RELOAD
|
||||
: HIT_MODE_RIGHT_WEAPON_RELOAD;
|
||||
break;
|
||||
default:
|
||||
*hit_mode = HIT_MODE_PUNCH;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
+10
-2
@@ -33,14 +33,19 @@ typedef enum InterfaceItemAction {
|
||||
|
||||
extern int gInterfaceBarWindow;
|
||||
extern bool gInterfaceBarMode;
|
||||
extern int gInterfaceBarWidth;
|
||||
extern bool gInterfaceBarIsCustom;
|
||||
extern int gInterfaceBarContentOffset;
|
||||
extern int gInterfaceSidePanelsImageId;
|
||||
extern bool gInterfaceSidePanelsExtendFromScreenEdge;
|
||||
|
||||
int interfaceInit();
|
||||
void interfaceReset();
|
||||
void interfaceFree();
|
||||
int interfaceLoad(File* stream);
|
||||
int interfaceSave(File* stream);
|
||||
void intface_hide();
|
||||
void _intface_show();
|
||||
void interfaceBarHide();
|
||||
void interfaceBarShow();
|
||||
void interfaceBarEnable();
|
||||
void interfaceBarDisable();
|
||||
bool interfaceBarEnabled();
|
||||
@@ -64,6 +69,9 @@ void interfaceBarEndButtonsRenderRedLights();
|
||||
int indicatorBarRefresh();
|
||||
bool indicatorBarShow();
|
||||
bool indicatorBarHide();
|
||||
bool interface_get_current_attack_mode(int* hit_mode);
|
||||
|
||||
unsigned char* customInterfaceBarGetBackgroundImageData();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
||||
+196
-98
@@ -7,13 +7,15 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "db.h"
|
||||
#include "debug.h"
|
||||
#include "export.h"
|
||||
#include "input.h"
|
||||
#include "interpreter_lib.h"
|
||||
#include "memory_manager.h"
|
||||
#include "platform_compat.h"
|
||||
#include "sfall_global_scripts.h"
|
||||
#include "svga.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
@@ -42,7 +44,6 @@ static opcode_t programReturnStackPopInt16(Program* program);
|
||||
static int programReturnStackPopInt32(Program* program);
|
||||
static void _detachProgram(Program* program);
|
||||
static void _purgeProgram(Program* program);
|
||||
static void programFree(Program* program);
|
||||
static opcode_t _getOp(Program* program);
|
||||
static void programMarkHeap(Program* program);
|
||||
static void opNoop(Program* program);
|
||||
@@ -86,17 +87,17 @@ static void opLeaveCriticalSection(Program* program);
|
||||
static void opEnterCriticalSection(Program* program);
|
||||
static void opJump(Program* program);
|
||||
static void opCall(Program* program);
|
||||
static void op801F(Program* program);
|
||||
static void op801C(Program* program);
|
||||
static void op801D(Program* program);
|
||||
static void op8020(Program* program);
|
||||
static void op8021(Program* program);
|
||||
static void op8025(Program* program);
|
||||
static void op8026(Program* program);
|
||||
static void op8022(Program* program);
|
||||
static void op8023(Program* program);
|
||||
static void op8024(Program* program);
|
||||
static void op801E(Program* program);
|
||||
static void opPopFlags(Program* program);
|
||||
static void opPopReturn(Program* program);
|
||||
static void opPopExit(Program* program);
|
||||
static void opPopFlagsReturn(Program* program);
|
||||
static void opPopFlagsExit(Program* program);
|
||||
static void opPopFlagsReturnValExit(Program* program);
|
||||
static void opPopFlagsReturnValExitExtern(Program* program);
|
||||
static void opPopFlagsReturnExtern(Program* program);
|
||||
static void opPopFlagsExitExtern(Program* program);
|
||||
static void opPopFlagsReturnValExtern(Program* program);
|
||||
static void opPopAddress(Program* program);
|
||||
static void opAtoD(Program* program);
|
||||
static void opDtoA(Program* program);
|
||||
static void opExitProgram(Program* program);
|
||||
@@ -154,7 +155,7 @@ static int (*_outputFunc)(char*) = _outputStr;
|
||||
static int _cpuBurstSize = 10;
|
||||
|
||||
// 0x59E230
|
||||
static OpcodeHandler* gInterpreterOpcodeHandlers[342];
|
||||
static OpcodeHandler* gInterpreterOpcodeHandlers[OPCODE_MAX_COUNT];
|
||||
|
||||
// 0x59E78C
|
||||
static Program* gInterpreterCurrentProgram;
|
||||
@@ -171,7 +172,7 @@ static int _busy;
|
||||
// 0x4670A0
|
||||
static unsigned int _defaultTimerFunc()
|
||||
{
|
||||
return _get_time();
|
||||
return getTicks();
|
||||
}
|
||||
|
||||
// 0x4670B4
|
||||
@@ -215,7 +216,7 @@ int _interpretOutput(const char* format, ...)
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
int rc = vsprintf(string, format, args);
|
||||
int rc = vsnprintf(string, sizeof(string), format, args);
|
||||
va_end(args);
|
||||
|
||||
debugPrint(string);
|
||||
@@ -252,7 +253,7 @@ static char* programGetCurrentProcedureName(Program* program)
|
||||
|
||||
va_list argptr;
|
||||
va_start(argptr, format);
|
||||
vsprintf(string, format, argptr);
|
||||
vsnprintf(string, sizeof(string), format, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
debugPrint("\nError during execution: %s\n", string);
|
||||
@@ -420,7 +421,7 @@ static void _purgeProgram(Program* program)
|
||||
}
|
||||
|
||||
// 0x467614
|
||||
static void programFree(Program* program)
|
||||
void programFree(Program* program)
|
||||
{
|
||||
// NOTE: Uninline.
|
||||
_detachProgram(program);
|
||||
@@ -465,7 +466,7 @@ Program* programCreateByPath(const char* path)
|
||||
File* stream = fileOpen(path, "rb");
|
||||
if (stream == NULL) {
|
||||
char err[260];
|
||||
sprintf(err, "Couldn't open %s for read\n", path);
|
||||
snprintf(err, sizeof(err), "Couldn't open %s for read\n", path);
|
||||
programFatalError(err);
|
||||
return NULL;
|
||||
}
|
||||
@@ -883,11 +884,11 @@ static void opConditionalOperatorNotEqual(Program* program)
|
||||
strings[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
break;
|
||||
case VALUE_TYPE_FLOAT:
|
||||
sprintf(stringBuffers[0], "%.5f", value[0].floatValue);
|
||||
snprintf(stringBuffers[0], sizeof(stringBuffers[0]), "%.5f", value[0].floatValue);
|
||||
strings[0] = stringBuffers[0];
|
||||
break;
|
||||
case VALUE_TYPE_INT:
|
||||
sprintf(stringBuffers[0], "%d", value[0].integerValue);
|
||||
snprintf(stringBuffers[0], sizeof(stringBuffers[0]), "%d", value[0].integerValue);
|
||||
strings[0] = stringBuffers[0];
|
||||
break;
|
||||
default:
|
||||
@@ -900,7 +901,7 @@ static void opConditionalOperatorNotEqual(Program* program)
|
||||
switch (value[0].opcode) {
|
||||
case VALUE_TYPE_STRING:
|
||||
case VALUE_TYPE_DYNAMIC_STRING:
|
||||
sprintf(stringBuffers[1], "%.5f", value[1].floatValue);
|
||||
snprintf(stringBuffers[1], sizeof(stringBuffers[1]), "%.5f", value[1].floatValue);
|
||||
strings[1] = stringBuffers[1];
|
||||
strings[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
result = strcmp(strings[1], strings[0]) != 0;
|
||||
@@ -919,7 +920,7 @@ static void opConditionalOperatorNotEqual(Program* program)
|
||||
switch (value[0].opcode) {
|
||||
case VALUE_TYPE_STRING:
|
||||
case VALUE_TYPE_DYNAMIC_STRING:
|
||||
sprintf(stringBuffers[1], "%d", value[1].integerValue);
|
||||
snprintf(stringBuffers[1], sizeof(stringBuffers[1]), "%d", value[1].integerValue);
|
||||
strings[1] = stringBuffers[1];
|
||||
strings[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
result = strcmp(strings[1], strings[0]) != 0;
|
||||
@@ -931,7 +932,7 @@ static void opConditionalOperatorNotEqual(Program* program)
|
||||
result = value[1].integerValue != value[0].integerValue;
|
||||
break;
|
||||
case VALUE_TYPE_PTR:
|
||||
result = (intptr_t)(value[1].integerValue) != (intptr_t)(value[0].pointerValue);
|
||||
result = (uintptr_t)(value[1].integerValue) != (uintptr_t)(value[0].pointerValue);
|
||||
break;
|
||||
default:
|
||||
assert(false && "Should be unreachable");
|
||||
@@ -940,7 +941,7 @@ static void opConditionalOperatorNotEqual(Program* program)
|
||||
case VALUE_TYPE_PTR:
|
||||
switch (value[0].opcode) {
|
||||
case VALUE_TYPE_INT:
|
||||
result = (intptr_t)(value[1].pointerValue) != (intptr_t)(value[0].integerValue);
|
||||
result = (uintptr_t)(value[1].pointerValue) != (uintptr_t)(value[0].integerValue);
|
||||
break;
|
||||
case VALUE_TYPE_PTR:
|
||||
result = value[1].pointerValue != value[0].pointerValue;
|
||||
@@ -979,11 +980,11 @@ static void opConditionalOperatorEqual(Program* program)
|
||||
strings[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
break;
|
||||
case VALUE_TYPE_FLOAT:
|
||||
sprintf(stringBuffers[0], "%.5f", value[0].floatValue);
|
||||
snprintf(stringBuffers[0], sizeof(stringBuffers[0]), "%.5f", value[0].floatValue);
|
||||
strings[0] = stringBuffers[0];
|
||||
break;
|
||||
case VALUE_TYPE_INT:
|
||||
sprintf(stringBuffers[0], "%d", value[0].integerValue);
|
||||
snprintf(stringBuffers[0], sizeof(stringBuffers[0]), "%d", value[0].integerValue);
|
||||
strings[0] = stringBuffers[0];
|
||||
break;
|
||||
default:
|
||||
@@ -996,7 +997,7 @@ static void opConditionalOperatorEqual(Program* program)
|
||||
switch (value[0].opcode) {
|
||||
case VALUE_TYPE_STRING:
|
||||
case VALUE_TYPE_DYNAMIC_STRING:
|
||||
sprintf(stringBuffers[1], "%.5f", value[1].floatValue);
|
||||
snprintf(stringBuffers[1], sizeof(stringBuffers[1]), "%.5f", value[1].floatValue);
|
||||
strings[1] = stringBuffers[1];
|
||||
strings[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
result = strcmp(strings[1], strings[0]) == 0;
|
||||
@@ -1015,7 +1016,7 @@ static void opConditionalOperatorEqual(Program* program)
|
||||
switch (value[0].opcode) {
|
||||
case VALUE_TYPE_STRING:
|
||||
case VALUE_TYPE_DYNAMIC_STRING:
|
||||
sprintf(stringBuffers[1], "%d", value[1].integerValue);
|
||||
snprintf(stringBuffers[1], sizeof(stringBuffers[1]), "%d", value[1].integerValue);
|
||||
strings[1] = stringBuffers[1];
|
||||
strings[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
result = strcmp(strings[1], strings[0]) == 0;
|
||||
@@ -1027,7 +1028,7 @@ static void opConditionalOperatorEqual(Program* program)
|
||||
result = value[1].integerValue == value[0].integerValue;
|
||||
break;
|
||||
case VALUE_TYPE_PTR:
|
||||
result = (intptr_t)(value[1].integerValue) == (intptr_t)(value[0].pointerValue);
|
||||
result = (uintptr_t)(value[1].integerValue) == (uintptr_t)(value[0].pointerValue);
|
||||
break;
|
||||
default:
|
||||
assert(false && "Should be unreachable");
|
||||
@@ -1036,7 +1037,7 @@ static void opConditionalOperatorEqual(Program* program)
|
||||
case VALUE_TYPE_PTR:
|
||||
switch (value[0].opcode) {
|
||||
case VALUE_TYPE_INT:
|
||||
result = (intptr_t)(value[1].pointerValue) == (intptr_t)(value[0].integerValue);
|
||||
result = (uintptr_t)(value[1].pointerValue) == (uintptr_t)(value[0].integerValue);
|
||||
break;
|
||||
case VALUE_TYPE_PTR:
|
||||
result = value[1].pointerValue == value[0].pointerValue;
|
||||
@@ -1075,11 +1076,11 @@ static void opConditionalOperatorLessThanEquals(Program* program)
|
||||
strings[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
break;
|
||||
case VALUE_TYPE_FLOAT:
|
||||
sprintf(stringBuffers[0], "%.5f", value[0].floatValue);
|
||||
snprintf(stringBuffers[0], sizeof(stringBuffers[0]), "%.5f", value[0].floatValue);
|
||||
strings[0] = stringBuffers[0];
|
||||
break;
|
||||
case VALUE_TYPE_INT:
|
||||
sprintf(stringBuffers[0], "%d", value[0].integerValue);
|
||||
snprintf(stringBuffers[0], sizeof(stringBuffers[0]), "%d", value[0].integerValue);
|
||||
strings[0] = stringBuffers[0];
|
||||
break;
|
||||
default:
|
||||
@@ -1092,7 +1093,7 @@ static void opConditionalOperatorLessThanEquals(Program* program)
|
||||
switch (value[0].opcode) {
|
||||
case VALUE_TYPE_STRING:
|
||||
case VALUE_TYPE_DYNAMIC_STRING:
|
||||
sprintf(stringBuffers[1], "%.5f", value[1].floatValue);
|
||||
snprintf(stringBuffers[1], sizeof(stringBuffers[1]), "%.5f", value[1].floatValue);
|
||||
strings[1] = stringBuffers[1];
|
||||
strings[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
result = strcmp(strings[1], strings[0]) <= 0;
|
||||
@@ -1111,7 +1112,7 @@ static void opConditionalOperatorLessThanEquals(Program* program)
|
||||
switch (value[0].opcode) {
|
||||
case VALUE_TYPE_STRING:
|
||||
case VALUE_TYPE_DYNAMIC_STRING:
|
||||
sprintf(stringBuffers[1], "%d", value[1].integerValue);
|
||||
snprintf(stringBuffers[1], sizeof(stringBuffers[1]), "%d", value[1].integerValue);
|
||||
strings[1] = stringBuffers[1];
|
||||
strings[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
result = strcmp(strings[1], strings[0]) <= 0;
|
||||
@@ -1126,6 +1127,16 @@ static void opConditionalOperatorLessThanEquals(Program* program)
|
||||
assert(false && "Should be unreachable");
|
||||
}
|
||||
break;
|
||||
// Nevada folks tend to use "object <= 0" to test objects for nulls.
|
||||
case VALUE_TYPE_PTR:
|
||||
switch (value[0].opcode) {
|
||||
case VALUE_TYPE_INT:
|
||||
result = (uintptr_t)value[1].pointerValue <= (uintptr_t)value[0].integerValue;
|
||||
break;
|
||||
default:
|
||||
assert(false && "Should be unreachable");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false && "Should be unreachable");
|
||||
}
|
||||
@@ -1157,11 +1168,11 @@ static void opConditionalOperatorGreaterThanEquals(Program* program)
|
||||
strings[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
break;
|
||||
case VALUE_TYPE_FLOAT:
|
||||
sprintf(stringBuffers[0], "%.5f", value[0].floatValue);
|
||||
snprintf(stringBuffers[0], sizeof(stringBuffers[0]), "%.5f", value[0].floatValue);
|
||||
strings[0] = stringBuffers[0];
|
||||
break;
|
||||
case VALUE_TYPE_INT:
|
||||
sprintf(stringBuffers[0], "%d", value[0].integerValue);
|
||||
snprintf(stringBuffers[0], sizeof(stringBuffers[0]), "%d", value[0].integerValue);
|
||||
strings[0] = stringBuffers[0];
|
||||
break;
|
||||
default:
|
||||
@@ -1174,7 +1185,7 @@ static void opConditionalOperatorGreaterThanEquals(Program* program)
|
||||
switch (value[0].opcode) {
|
||||
case VALUE_TYPE_STRING:
|
||||
case VALUE_TYPE_DYNAMIC_STRING:
|
||||
sprintf(stringBuffers[1], "%.5f", value[1].floatValue);
|
||||
snprintf(stringBuffers[1], sizeof(stringBuffers[1]), "%.5f", value[1].floatValue);
|
||||
strings[1] = stringBuffers[1];
|
||||
strings[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
result = strcmp(strings[1], strings[0]) >= 0;
|
||||
@@ -1193,7 +1204,7 @@ static void opConditionalOperatorGreaterThanEquals(Program* program)
|
||||
switch (value[0].opcode) {
|
||||
case VALUE_TYPE_STRING:
|
||||
case VALUE_TYPE_DYNAMIC_STRING:
|
||||
sprintf(stringBuffers[1], "%d", value[1].integerValue);
|
||||
snprintf(stringBuffers[1], sizeof(stringBuffers[1]), "%d", value[1].integerValue);
|
||||
strings[1] = stringBuffers[1];
|
||||
strings[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
result = strcmp(strings[1], strings[0]) >= 0;
|
||||
@@ -1238,11 +1249,11 @@ static void opConditionalOperatorLessThan(Program* program)
|
||||
str_ptr[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
break;
|
||||
case VALUE_TYPE_FLOAT:
|
||||
sprintf(text[0], "%.5f", value[0].floatValue);
|
||||
snprintf(text[0], sizeof(text[0]), "%.5f", value[0].floatValue);
|
||||
str_ptr[0] = text[0];
|
||||
break;
|
||||
case VALUE_TYPE_INT:
|
||||
sprintf(text[0], "%d", value[0].integerValue);
|
||||
snprintf(text[0], sizeof(text[0]), "%d", value[0].integerValue);
|
||||
str_ptr[0] = text[0];
|
||||
break;
|
||||
default:
|
||||
@@ -1255,7 +1266,7 @@ static void opConditionalOperatorLessThan(Program* program)
|
||||
switch (value[0].opcode) {
|
||||
case VALUE_TYPE_STRING:
|
||||
case VALUE_TYPE_DYNAMIC_STRING:
|
||||
sprintf(text[1], "%.5f", value[1].floatValue);
|
||||
snprintf(text[1], sizeof(text[1]), "%.5f", value[1].floatValue);
|
||||
str_ptr[1] = text[1];
|
||||
str_ptr[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
result = strcmp(str_ptr[1], str_ptr[0]) < 0;
|
||||
@@ -1274,7 +1285,7 @@ static void opConditionalOperatorLessThan(Program* program)
|
||||
switch (value[0].opcode) {
|
||||
case VALUE_TYPE_STRING:
|
||||
case VALUE_TYPE_DYNAMIC_STRING:
|
||||
sprintf(text[1], "%d", value[1].integerValue);
|
||||
snprintf(text[1], sizeof(text[1]), "%d", value[1].integerValue);
|
||||
str_ptr[1] = text[1];
|
||||
str_ptr[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
result = strcmp(str_ptr[1], str_ptr[0]) < 0;
|
||||
@@ -1319,11 +1330,11 @@ static void opConditionalOperatorGreaterThan(Program* program)
|
||||
strings[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
break;
|
||||
case VALUE_TYPE_FLOAT:
|
||||
sprintf(stringBuffers[0], "%.5f", value[0].floatValue);
|
||||
snprintf(stringBuffers[0], sizeof(stringBuffers[0]), "%.5f", value[0].floatValue);
|
||||
strings[0] = stringBuffers[0];
|
||||
break;
|
||||
case VALUE_TYPE_INT:
|
||||
sprintf(stringBuffers[0], "%d", value[0].integerValue);
|
||||
snprintf(stringBuffers[0], sizeof(stringBuffers[0]), "%d", value[0].integerValue);
|
||||
strings[0] = stringBuffers[0];
|
||||
break;
|
||||
default:
|
||||
@@ -1336,7 +1347,7 @@ static void opConditionalOperatorGreaterThan(Program* program)
|
||||
switch (value[0].opcode) {
|
||||
case VALUE_TYPE_STRING:
|
||||
case VALUE_TYPE_DYNAMIC_STRING:
|
||||
sprintf(stringBuffers[1], "%.5f", value[1].floatValue);
|
||||
snprintf(stringBuffers[1], sizeof(stringBuffers[1]), "%.5f", value[1].floatValue);
|
||||
strings[1] = stringBuffers[1];
|
||||
strings[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
result = strcmp(strings[1], strings[0]) > 0;
|
||||
@@ -1355,7 +1366,7 @@ static void opConditionalOperatorGreaterThan(Program* program)
|
||||
switch (value[0].opcode) {
|
||||
case VALUE_TYPE_STRING:
|
||||
case VALUE_TYPE_DYNAMIC_STRING:
|
||||
sprintf(stringBuffers[1], "%d", value[1].integerValue);
|
||||
snprintf(stringBuffers[1], sizeof(stringBuffers[1]), "%d", value[1].integerValue);
|
||||
strings[1] = stringBuffers[1];
|
||||
strings[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
result = strcmp(strings[1], strings[0]) > 0;
|
||||
@@ -1374,7 +1385,7 @@ static void opConditionalOperatorGreaterThan(Program* program)
|
||||
case VALUE_TYPE_PTR:
|
||||
switch (value[0].opcode) {
|
||||
case VALUE_TYPE_INT:
|
||||
result = (intptr_t)value[1].pointerValue > (intptr_t)value[0].integerValue;
|
||||
result = (uintptr_t)value[1].pointerValue > (uintptr_t)value[0].integerValue;
|
||||
break;
|
||||
default:
|
||||
assert(false && "Should be unreachable");
|
||||
@@ -1412,15 +1423,15 @@ static void opAdd(Program* program)
|
||||
break;
|
||||
case VALUE_TYPE_FLOAT:
|
||||
strings[0] = (char*)internal_malloc_safe(80, __FILE__, __LINE__); // "..\\int\\INTRPRET.C", 1011
|
||||
sprintf(strings[0], "%.5f", value[0].floatValue);
|
||||
snprintf(strings[0], 80, "%.5f", value[0].floatValue);
|
||||
break;
|
||||
case VALUE_TYPE_INT:
|
||||
strings[0] = (char*)internal_malloc_safe(80, __FILE__, __LINE__); // "..\\int\\INTRPRET.C", 1007
|
||||
sprintf(strings[0], "%d", value[0].integerValue);
|
||||
snprintf(strings[0], 80, "%d", value[0].integerValue);
|
||||
break;
|
||||
case VALUE_TYPE_PTR:
|
||||
strings[0] = (char*)internal_malloc_safe(80, __FILE__, __LINE__);
|
||||
sprintf(strings[0], "%p", value[0].pointerValue);
|
||||
snprintf(strings[0], 80, "%p", value[0].pointerValue);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1439,7 +1450,7 @@ static void opAdd(Program* program)
|
||||
case VALUE_TYPE_DYNAMIC_STRING:
|
||||
strings[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
tempString = (char*)internal_malloc_safe(strlen(strings[0]) + 80, __FILE__, __LINE__); // "..\\int\\INTRPRET.C", 1039
|
||||
sprintf(tempString, "%.5f", value[1].floatValue);
|
||||
snprintf(tempString, strlen(strings[0]) + 80, "%.5f", value[1].floatValue);
|
||||
strcat(tempString, strings[0]);
|
||||
|
||||
programStackPushString(program, tempString);
|
||||
@@ -1460,7 +1471,7 @@ static void opAdd(Program* program)
|
||||
case VALUE_TYPE_DYNAMIC_STRING:
|
||||
strings[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
tempString = (char*)internal_malloc_safe(strlen(strings[0]) + 80, __FILE__, __LINE__); // "..\\int\\INTRPRET.C", 1070
|
||||
sprintf(tempString, "%d", value[1].integerValue);
|
||||
snprintf(tempString, strlen(strings[0]) + 80, "%d", value[1].integerValue);
|
||||
strcat(tempString, strings[0]);
|
||||
|
||||
programStackPushString(program, tempString);
|
||||
@@ -1480,6 +1491,21 @@ static void opAdd(Program* program)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
// Sonora folks use "object + string" concatenation for debug purposes.
|
||||
case VALUE_TYPE_PTR:
|
||||
switch (value[0].opcode) {
|
||||
case VALUE_TYPE_STRING:
|
||||
case VALUE_TYPE_DYNAMIC_STRING:
|
||||
strings[0] = programGetString(program, value[0].opcode, value[0].integerValue);
|
||||
tempString = (char*)internal_malloc_safe(strlen(strings[0]) + 80, __FILE__, __LINE__);
|
||||
snprintf(tempString, strlen(strings[0]) + 80, "%p", value[1].pointerValue);
|
||||
strcat(tempString, strings[0]);
|
||||
|
||||
programStackPushString(program, tempString);
|
||||
|
||||
internal_free_safe(tempString, __FILE__, __LINE__);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2016,7 +2042,7 @@ static void opCall(Program* program)
|
||||
}
|
||||
|
||||
// 0x46B590
|
||||
static void op801F(Program* program)
|
||||
static void opPopFlags(Program* program)
|
||||
{
|
||||
program->windowId = programStackPopInteger(program);
|
||||
program->checkWaitFunc = (InterpretCheckWaitFunc*)programStackPopPointer(program);
|
||||
@@ -2025,13 +2051,13 @@ static void op801F(Program* program)
|
||||
|
||||
// pop stack 2 -> set program address
|
||||
// 0x46B63C
|
||||
static void op801C(Program* program)
|
||||
static void opPopReturn(Program* program)
|
||||
{
|
||||
program->instructionPointer = programReturnStackPopInteger(program);
|
||||
}
|
||||
|
||||
// 0x46B658
|
||||
static void op801D(Program* program)
|
||||
static void opPopExit(Program* program)
|
||||
{
|
||||
program->instructionPointer = programReturnStackPopInteger(program);
|
||||
|
||||
@@ -2039,37 +2065,37 @@ static void op801D(Program* program)
|
||||
}
|
||||
|
||||
// 0x46B67C
|
||||
static void op8020(Program* program)
|
||||
static void opPopFlagsReturn(Program* program)
|
||||
{
|
||||
op801F(program);
|
||||
opPopFlags(program);
|
||||
program->instructionPointer = programReturnStackPopInteger(program);
|
||||
}
|
||||
|
||||
// 0x46B698
|
||||
static void op8021(Program* program)
|
||||
static void opPopFlagsExit(Program* program)
|
||||
{
|
||||
op801F(program);
|
||||
opPopFlags(program);
|
||||
program->instructionPointer = programReturnStackPopInteger(program);
|
||||
program->flags |= PROGRAM_FLAG_0x40;
|
||||
}
|
||||
|
||||
// 0x46B6BC
|
||||
static void op8025(Program* program)
|
||||
static void opPopFlagsReturnValExit(Program* program)
|
||||
{
|
||||
ProgramValue value = programStackPopValue(program);
|
||||
|
||||
op801F(program);
|
||||
opPopFlags(program);
|
||||
program->instructionPointer = programReturnStackPopInteger(program);
|
||||
program->flags |= PROGRAM_FLAG_0x40;
|
||||
programStackPushValue(program, value);
|
||||
}
|
||||
|
||||
// 0x46B73C
|
||||
static void op8026(Program* program)
|
||||
static void opPopFlagsReturnValExitExtern(Program* program)
|
||||
{
|
||||
ProgramValue value = programStackPopValue(program);
|
||||
|
||||
op801F(program);
|
||||
opPopFlags(program);
|
||||
|
||||
Program* v1 = (Program*)programReturnStackPopPointer(program);
|
||||
v1->checkWaitFunc = (InterpretCheckWaitFunc*)programReturnStackPopPointer(program);
|
||||
@@ -2083,9 +2109,9 @@ static void op8026(Program* program)
|
||||
}
|
||||
|
||||
// 0x46B808
|
||||
static void op8022(Program* program)
|
||||
static void opPopFlagsReturnExtern(Program* program)
|
||||
{
|
||||
op801F(program);
|
||||
opPopFlags(program);
|
||||
|
||||
Program* v1 = (Program*)programReturnStackPopPointer(program);
|
||||
v1->checkWaitFunc = (InterpretCheckWaitFunc*)programReturnStackPopPointer(program);
|
||||
@@ -2095,9 +2121,9 @@ static void op8022(Program* program)
|
||||
}
|
||||
|
||||
// 0x46B86C
|
||||
static void op8023(Program* program)
|
||||
static void opPopFlagsExitExtern(Program* program)
|
||||
{
|
||||
op801F(program);
|
||||
opPopFlags(program);
|
||||
|
||||
Program* v1 = (Program*)programReturnStackPopPointer(program);
|
||||
v1->checkWaitFunc = (InterpretCheckWaitFunc*)programReturnStackPopPointer(program);
|
||||
@@ -2110,11 +2136,11 @@ static void op8023(Program* program)
|
||||
|
||||
// pop value from stack 1 and push it to script popped from stack 2
|
||||
// 0x46B8D8
|
||||
static void op8024(Program* program)
|
||||
static void opPopFlagsReturnValExtern(Program* program)
|
||||
{
|
||||
ProgramValue value = programStackPopValue(program);
|
||||
|
||||
op801F(program);
|
||||
opPopFlags(program);
|
||||
|
||||
Program* v10 = (Program*)programReturnStackPopPointer(program);
|
||||
v10->checkWaitFunc = (InterpretCheckWaitFunc*)programReturnStackPopPointer(program);
|
||||
@@ -2138,7 +2164,7 @@ static void op8024(Program* program)
|
||||
}
|
||||
|
||||
// 0x46BA10
|
||||
static void op801E(Program* program)
|
||||
static void opPopAddress(Program* program)
|
||||
{
|
||||
programReturnStackPopValue(program);
|
||||
}
|
||||
@@ -2242,7 +2268,7 @@ static void opStoreExternalVariable(Program* program)
|
||||
|
||||
if (externalVariableSetValue(program, identifier, value)) {
|
||||
char err[256];
|
||||
sprintf(err, "External variable %s does not exist\n", identifier);
|
||||
snprintf(err, sizeof(err), "External variable %s does not exist\n", identifier);
|
||||
programFatalError(err);
|
||||
}
|
||||
}
|
||||
@@ -2257,7 +2283,7 @@ static void opFetchExternalVariable(Program* program)
|
||||
ProgramValue value;
|
||||
if (externalVariableGetValue(program, identifier, value) != 0) {
|
||||
char err[256];
|
||||
sprintf(err, "External variable %s does not exist\n", identifier);
|
||||
snprintf(err, sizeof(err), "External variable %s does not exist\n", identifier);
|
||||
programFatalError(err);
|
||||
}
|
||||
|
||||
@@ -2277,7 +2303,7 @@ static void opExportProcedure(Program* program)
|
||||
|
||||
if (externalProcedureCreate(program, procedureName, procedureAddress, argumentCount) != 0) {
|
||||
char err[256];
|
||||
sprintf(err, "Error exporting procedure %s", procedureName);
|
||||
snprintf(err, sizeof(err), "Error exporting procedure %s", procedureName);
|
||||
programFatalError(err);
|
||||
}
|
||||
}
|
||||
@@ -2291,7 +2317,7 @@ static void opExportVariable(Program* program)
|
||||
|
||||
if (externalVariableCreate(program, identifier)) {
|
||||
char err[256];
|
||||
sprintf(err, "External variable %s already exists", identifier);
|
||||
snprintf(err, sizeof(err), "External variable %s already exists", identifier);
|
||||
programFatalError(err);
|
||||
}
|
||||
}
|
||||
@@ -2346,7 +2372,7 @@ static void opCallStart(Program* program)
|
||||
program->child = runScript(name);
|
||||
if (program->child == NULL) {
|
||||
char err[260];
|
||||
sprintf(err, "Error spawning child %s", name);
|
||||
snprintf(err, sizeof(err), "Error spawning child %s", name);
|
||||
programFatalError(err);
|
||||
}
|
||||
|
||||
@@ -2370,7 +2396,7 @@ static void opSpawn(Program* program)
|
||||
program->child = runScript(name);
|
||||
if (program->child == NULL) {
|
||||
char err[260];
|
||||
sprintf(err, "Error spawning child %s", name);
|
||||
snprintf(err, sizeof(err), "Error spawning child %s", name);
|
||||
programFatalError(err);
|
||||
}
|
||||
|
||||
@@ -2392,7 +2418,7 @@ static Program* forkProgram(Program* program)
|
||||
|
||||
if (forked == NULL) {
|
||||
char err[256];
|
||||
sprintf(err, "couldn't fork script '%s'", name);
|
||||
snprintf(err, sizeof(err), "couldn't fork script '%s'", name);
|
||||
programFatalError(err);
|
||||
}
|
||||
|
||||
@@ -2446,7 +2472,7 @@ static void opCheckProcedureArgumentCount(Program* program)
|
||||
if (actualArgumentCount != expectedArgumentCount) {
|
||||
const char* identifier = programGetIdentifier(program, stackReadInt32(program->procedures + 4 + 24 * procedureIndex, 0));
|
||||
char err[260];
|
||||
sprintf(err, "Wrong number of args to procedure %s\n", identifier);
|
||||
snprintf(err, sizeof(err), "Wrong number of args to procedure %s\n", identifier);
|
||||
programFatalError(err);
|
||||
}
|
||||
}
|
||||
@@ -2476,7 +2502,7 @@ static void opLookupStringProc(Program* program)
|
||||
}
|
||||
|
||||
char err[260];
|
||||
sprintf(err, "Couldn't find string procedure %s\n", procedureNameToLookup);
|
||||
snprintf(err, sizeof(err), "Couldn't find string procedure %s\n", procedureNameToLookup);
|
||||
programFatalError(err);
|
||||
}
|
||||
|
||||
@@ -2514,17 +2540,17 @@ void interpreterRegisterOpcodeHandlers()
|
||||
interpreterRegisterOpcode(OPCODE_SWAPA, opSwapReturnStack);
|
||||
interpreterRegisterOpcode(OPCODE_POP, opPop);
|
||||
interpreterRegisterOpcode(OPCODE_DUP, opDuplicate);
|
||||
interpreterRegisterOpcode(OPCODE_POP_RETURN, op801C);
|
||||
interpreterRegisterOpcode(OPCODE_POP_EXIT, op801D);
|
||||
interpreterRegisterOpcode(OPCODE_POP_ADDRESS, op801E);
|
||||
interpreterRegisterOpcode(OPCODE_POP_FLAGS, op801F);
|
||||
interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN, op8020);
|
||||
interpreterRegisterOpcode(OPCODE_POP_FLAGS_EXIT, op8021);
|
||||
interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN_EXTERN, op8022);
|
||||
interpreterRegisterOpcode(OPCODE_POP_FLAGS_EXIT_EXTERN, op8023);
|
||||
interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN_VAL_EXTERN, op8024);
|
||||
interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN_VAL_EXIT, op8025);
|
||||
interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN_VAL_EXIT_EXTERN, op8026);
|
||||
interpreterRegisterOpcode(OPCODE_POP_RETURN, opPopReturn);
|
||||
interpreterRegisterOpcode(OPCODE_POP_EXIT, opPopExit);
|
||||
interpreterRegisterOpcode(OPCODE_POP_ADDRESS, opPopAddress);
|
||||
interpreterRegisterOpcode(OPCODE_POP_FLAGS, opPopFlags);
|
||||
interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN, opPopFlagsReturn);
|
||||
interpreterRegisterOpcode(OPCODE_POP_FLAGS_EXIT, opPopFlagsExit);
|
||||
interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN_EXTERN, opPopFlagsReturnExtern);
|
||||
interpreterRegisterOpcode(OPCODE_POP_FLAGS_EXIT_EXTERN, opPopFlagsExitExtern);
|
||||
interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN_VAL_EXTERN, opPopFlagsReturnValExtern);
|
||||
interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN_VAL_EXIT, opPopFlagsReturnValExit);
|
||||
interpreterRegisterOpcode(OPCODE_POP_FLAGS_RETURN_VAL_EXIT_EXTERN, opPopFlagsReturnValExitExtern);
|
||||
interpreterRegisterOpcode(OPCODE_CHECK_PROCEDURE_ARGUMENT_COUNT, opCheckProcedureArgumentCount);
|
||||
interpreterRegisterOpcode(OPCODE_LOOKUP_PROCEDURE_BY_NAME, opLookupStringProc);
|
||||
interpreterRegisterOpcode(OPCODE_POP_BASE, opPopBase);
|
||||
@@ -2641,14 +2667,14 @@ void _interpret(Program* program, int a2)
|
||||
program->flags |= (opcode << 16);
|
||||
|
||||
if (!((opcode >> 8) & 0x80)) {
|
||||
sprintf(err, "Bad opcode %x %c %d.", opcode, opcode, opcode);
|
||||
snprintf(err, sizeof(err), "Bad opcode %x %c %d.", opcode, opcode, opcode);
|
||||
programFatalError(err);
|
||||
}
|
||||
|
||||
unsigned int opcodeIndex = opcode & 0x3FF;
|
||||
OpcodeHandler* handler = gInterpreterOpcodeHandlers[opcodeIndex];
|
||||
if (handler == NULL) {
|
||||
sprintf(err, "Undefined opcode %x.", opcode);
|
||||
snprintf(err, sizeof(err), "Undefined opcode %x.", opcode);
|
||||
programFatalError(err);
|
||||
}
|
||||
|
||||
@@ -2757,11 +2783,11 @@ void _executeProc(Program* program, int procedureIndex)
|
||||
if (externalProgram != NULL) {
|
||||
if (externalProcedureArgumentCount == 0) {
|
||||
} else {
|
||||
sprintf(err, "External procedure cannot take arguments in interrupt context");
|
||||
snprintf(err, sizeof(err), "External procedure cannot take arguments in interrupt context");
|
||||
_interpretOutput(err);
|
||||
}
|
||||
} else {
|
||||
sprintf(err, "External procedure %s not found\n", procedureIdentifier);
|
||||
snprintf(err, sizeof(err), "External procedure %s not found\n", procedureIdentifier);
|
||||
_interpretOutput(err);
|
||||
}
|
||||
|
||||
@@ -2838,11 +2864,11 @@ void _executeProcedure(Program* program, int procedureIndex)
|
||||
_interpret(externalProgram, -1);
|
||||
memcpy(externalProgram->env, env, sizeof(env));
|
||||
} else {
|
||||
sprintf(err, "External procedure cannot take arguments in interrupt context");
|
||||
snprintf(err, sizeof(err), "External procedure cannot take arguments in interrupt context");
|
||||
_interpretOutput(err);
|
||||
}
|
||||
} else {
|
||||
sprintf(err, "External procedure %s not found\n", procedureIdentifier);
|
||||
snprintf(err, sizeof(err), "External procedure %s not found\n", procedureIdentifier);
|
||||
_interpretOutput(err);
|
||||
}
|
||||
} else {
|
||||
@@ -2986,6 +3012,15 @@ Program* runScript(char* name)
|
||||
// 0x46E1EC
|
||||
void _updatePrograms()
|
||||
{
|
||||
// CE: Implementation is different. Sfall inserts global scripts into
|
||||
// program list upon creation, so engine does not diffirentiate between
|
||||
// global and normal scripts. Global scripts in CE are not part of program
|
||||
// list, so we need a separate call to continue execution (usually
|
||||
// non-critical calls scheduled from managed windows). One more thing to
|
||||
// note is that global scripts in CE cannot handle conditional/timed procs
|
||||
// (which are not used anyway).
|
||||
sfall_gl_scr_update(_cpuBurstSize);
|
||||
|
||||
ProgramListNode* curr = gInterpreterProgramListHead;
|
||||
while (curr != NULL) {
|
||||
ProgramListNode* next = curr->next;
|
||||
@@ -3226,7 +3261,7 @@ void* programReturnStackPopPointer(Program* program)
|
||||
return programValue.pointerValue;
|
||||
}
|
||||
|
||||
bool ProgramValue::isEmpty()
|
||||
bool ProgramValue::isEmpty() const
|
||||
{
|
||||
switch (opcode) {
|
||||
case VALUE_TYPE_INT:
|
||||
@@ -3242,4 +3277,67 @@ bool ProgramValue::isEmpty()
|
||||
return true;
|
||||
}
|
||||
|
||||
// Matches Sfall implementation.
|
||||
bool ProgramValue::isInt() const
|
||||
{
|
||||
return opcode == VALUE_TYPE_INT;
|
||||
}
|
||||
|
||||
// Matches Sfall implementation.
|
||||
bool ProgramValue::isFloat() const
|
||||
{
|
||||
return opcode == VALUE_TYPE_FLOAT;
|
||||
}
|
||||
|
||||
// Matches Sfall implementation.
|
||||
float ProgramValue::asFloat() const
|
||||
{
|
||||
switch (opcode) {
|
||||
case VALUE_TYPE_INT:
|
||||
return static_cast<float>(integerValue);
|
||||
case VALUE_TYPE_FLOAT:
|
||||
return floatValue;
|
||||
default:
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
bool ProgramValue::isString() const
|
||||
{
|
||||
return opcode == VALUE_TYPE_STRING || opcode == VALUE_TYPE_DYNAMIC_STRING;
|
||||
}
|
||||
|
||||
ProgramValue::ProgramValue()
|
||||
{
|
||||
opcode = VALUE_TYPE_INT;
|
||||
integerValue = 0;
|
||||
}
|
||||
ProgramValue::ProgramValue(int value)
|
||||
{
|
||||
opcode = VALUE_TYPE_INT;
|
||||
integerValue = value;
|
||||
};
|
||||
ProgramValue::ProgramValue(Object* value)
|
||||
{
|
||||
opcode = VALUE_TYPE_PTR;
|
||||
pointerValue = value;
|
||||
};
|
||||
|
||||
bool ProgramValue::isPointer() const
|
||||
{
|
||||
return opcode == VALUE_TYPE_PTR;
|
||||
}
|
||||
|
||||
int ProgramValue::asInt() const
|
||||
{
|
||||
switch (opcode) {
|
||||
case VALUE_TYPE_INT:
|
||||
return integerValue;
|
||||
case VALUE_TYPE_FLOAT:
|
||||
return static_cast<int>(floatValue);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
+19
-4
@@ -1,6 +1,7 @@
|
||||
#ifndef INTERPRETER_H
|
||||
#define INTERPRETER_H
|
||||
|
||||
#include "object.h"
|
||||
#include <setjmp.h>
|
||||
|
||||
#include <vector>
|
||||
@@ -8,7 +9,9 @@
|
||||
namespace fallout {
|
||||
|
||||
// The maximum number of opcodes.
|
||||
#define OPCODE_MAX_COUNT (342)
|
||||
//
|
||||
// SFALL: Increase number of opcodes.
|
||||
#define OPCODE_MAX_COUNT 768
|
||||
|
||||
typedef enum Opcode {
|
||||
OPCODE_NOOP = 0x8000,
|
||||
@@ -138,7 +141,12 @@ typedef struct Procedure {
|
||||
int field_14;
|
||||
} Procedure;
|
||||
|
||||
typedef struct ProgramValue {
|
||||
class ProgramValue {
|
||||
public:
|
||||
ProgramValue();
|
||||
ProgramValue(int value);
|
||||
ProgramValue(Object* value);
|
||||
|
||||
opcode_t opcode;
|
||||
union {
|
||||
int integerValue;
|
||||
@@ -146,8 +154,14 @@ typedef struct ProgramValue {
|
||||
void* pointerValue;
|
||||
};
|
||||
|
||||
bool isEmpty();
|
||||
} ProgramValue;
|
||||
bool isEmpty() const;
|
||||
bool isInt() const;
|
||||
bool isFloat() const;
|
||||
bool isString() const;
|
||||
float asFloat() const;
|
||||
bool isPointer() const;
|
||||
int asInt() const;
|
||||
};
|
||||
|
||||
typedef std::vector<ProgramValue> ProgramStack;
|
||||
|
||||
@@ -190,6 +204,7 @@ void _interpretOutputFunc(int (*func)(char*));
|
||||
int _interpretOutput(const char* format, ...);
|
||||
[[noreturn]] void programFatalError(const char* str, ...);
|
||||
void _interpretDecStringRef(Program* program, opcode_t a2, int a3);
|
||||
void programFree(Program* program);
|
||||
Program* programCreateByPath(const char* path);
|
||||
char* programGetString(Program* program, opcode_t opcode, int offset);
|
||||
char* programGetIdentifier(Program* program, int offset);
|
||||
|
||||
+235
-197
@@ -10,14 +10,12 @@
|
||||
#include "color.h"
|
||||
#include "combat.h"
|
||||
#include "combat_ai.h"
|
||||
#include "core.h"
|
||||
#include "critter.h"
|
||||
#include "debug.h"
|
||||
#include "dialog.h"
|
||||
#include "display_monitor.h"
|
||||
#include "endgame.h"
|
||||
#include "game.h"
|
||||
#include "game_config.h"
|
||||
#include "game_dialog.h"
|
||||
#include "game_movie.h"
|
||||
#include "game_sound.h"
|
||||
@@ -37,11 +35,15 @@
|
||||
#include "random.h"
|
||||
#include "reaction.h"
|
||||
#include "scripts.h"
|
||||
#include "settings.h"
|
||||
#include "sfall_opcodes.h"
|
||||
#include "skill.h"
|
||||
#include "stat.h"
|
||||
#include "svga.h"
|
||||
#include "text_object.h"
|
||||
#include "tile.h"
|
||||
#include "trait.h"
|
||||
#include "vcr.h"
|
||||
#include "worldmap.h"
|
||||
|
||||
namespace fallout {
|
||||
@@ -338,48 +340,9 @@ static void opTileGetObjectWithPid(Program* program);
|
||||
static void opGetObjectName(Program* program);
|
||||
static void opGetPcStat(Program* program);
|
||||
|
||||
// 0x504B04
|
||||
static char _Error_0[] = "Error";
|
||||
|
||||
// 0x504B0C
|
||||
static char _aCritter[] = "<Critter>";
|
||||
|
||||
// Maps light level to light intensity.
|
||||
//
|
||||
// Middle value is mapped one-to-one which corresponds to 50% light level
|
||||
// (cavern lighting). Light levels above (51-100%) and below (0-49) is
|
||||
// calculated as percentage from two adjacent light values.
|
||||
//
|
||||
// See [opSetLightLevel] for math.
|
||||
//
|
||||
// 0x453F90
|
||||
static const int dword_453F90[3] = {
|
||||
0x4000,
|
||||
0xA000,
|
||||
0x10000,
|
||||
};
|
||||
|
||||
// 0x453F9C
|
||||
static const unsigned short word_453F9C[MOVIE_COUNT] = {
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
};
|
||||
|
||||
// 0x453FC0
|
||||
static Rect stru_453FC0 = { 0, 0, 640, 480 };
|
||||
|
||||
@@ -391,24 +354,6 @@ static const char* _dbg_error_strs[SCRIPT_ERROR_COUNT] = {
|
||||
"follows",
|
||||
};
|
||||
|
||||
// 0x518ED0
|
||||
static const int _ftList[11] = {
|
||||
ANIM_FALL_BACK_BLOOD_SF,
|
||||
ANIM_BIG_HOLE_SF,
|
||||
ANIM_CHARRED_BODY_SF,
|
||||
ANIM_CHUNKS_OF_FLESH_SF,
|
||||
ANIM_FALL_FRONT_BLOOD_SF,
|
||||
ANIM_FALL_BACK_BLOOD_SF,
|
||||
ANIM_DANCING_AUTOFIRE_SF,
|
||||
ANIM_SLICED_IN_HALF_SF,
|
||||
ANIM_EXPLODED_TO_NOTHING_SF,
|
||||
ANIM_FALL_BACK_BLOOD_SF,
|
||||
ANIM_FALL_FRONT_BLOOD_SF,
|
||||
};
|
||||
|
||||
// 0x518EFC
|
||||
static char* _errStr = _Error_0;
|
||||
|
||||
// Last message type during op_float_msg sequential.
|
||||
//
|
||||
// 0x518F00
|
||||
@@ -435,7 +380,7 @@ static void scriptPredefinedError(Program* program, const char* name, int error)
|
||||
{
|
||||
char string[260];
|
||||
|
||||
sprintf(string, "Script Error: %s: op_%s: %s", program->name, name, _dbg_error_strs[error]);
|
||||
snprintf(string, sizeof(string), "Script Error: %s: op_%s: %s", program->name, name, _dbg_error_strs[error]);
|
||||
|
||||
debugPrint(string);
|
||||
}
|
||||
@@ -447,7 +392,7 @@ static void scriptError(const char* format, ...)
|
||||
|
||||
va_list argptr;
|
||||
va_start(argptr, format);
|
||||
vsprintf(string, format, argptr);
|
||||
vsnprintf(string, sizeof(string), format, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
debugPrint(string);
|
||||
@@ -565,7 +510,7 @@ static void opSetMapStart(Program* program)
|
||||
}
|
||||
|
||||
int tile = 200 * y + x;
|
||||
if (tileSetCenter(tile, TILE_SET_CENTER_FLAG_0x01 | TILE_SET_CENTER_FLAG_0x02) != 0) {
|
||||
if (tileSetCenter(tile, TILE_SET_CENTER_REFRESH_WINDOW | TILE_SET_CENTER_FLAG_IGNORE_SCROLL_RESTRICTIONS) != 0) {
|
||||
scriptError("\nScript Error: %s: op_set_map_start: tile_set_center failed", program->name);
|
||||
return;
|
||||
}
|
||||
@@ -585,7 +530,7 @@ static void opOverrideMapStart(Program* program)
|
||||
int x = programStackPopInteger(program);
|
||||
|
||||
char text[60];
|
||||
sprintf(text, "OVERRIDE_MAP_START: x: %d, y: %d", x, y);
|
||||
snprintf(text, sizeof(text), "OVERRIDE_MAP_START: x: %d, y: %d", x, y);
|
||||
debugPrint(text);
|
||||
|
||||
int tile = 200 * y + x;
|
||||
@@ -604,7 +549,7 @@ static void opOverrideMapStart(Program* program)
|
||||
}
|
||||
}
|
||||
|
||||
tileSetCenter(tile, TILE_SET_CENTER_FLAG_0x01);
|
||||
tileSetCenter(tile, TILE_SET_CENTER_REFRESH_WINDOW);
|
||||
tileWindowRefresh();
|
||||
}
|
||||
|
||||
@@ -886,7 +831,7 @@ static void opMoveTo(Program* program)
|
||||
Rect rect;
|
||||
newTile = objectSetLocation(object, tile, elevation, &rect);
|
||||
if (newTile != -1) {
|
||||
tileSetCenter(object->tile, TILE_SET_CENTER_FLAG_0x01);
|
||||
tileSetCenter(object->tile, TILE_SET_CENTER_REFRESH_WINDOW);
|
||||
}
|
||||
|
||||
if (tileLimitingEnabled) {
|
||||
@@ -1049,7 +994,7 @@ static void opDestroyObject(Program* program)
|
||||
|
||||
if (isSelf) {
|
||||
object->sid = -1;
|
||||
object->flags |= (OBJECT_HIDDEN | OBJECT_TEMPORARY);
|
||||
object->flags |= (OBJECT_HIDDEN | OBJECT_NO_SAVE);
|
||||
} else {
|
||||
reg_anim_clear(object);
|
||||
objectDestroy(object, NULL);
|
||||
@@ -1076,10 +1021,7 @@ static void opDisplayMsg(Program* program)
|
||||
char* string = programStackPopString(program);
|
||||
displayMonitorAddMessage(string);
|
||||
|
||||
bool showScriptMessages = false;
|
||||
configGetBool(&gGameConfig, GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_SHOW_SCRIPT_MESSAGES_KEY, &showScriptMessages);
|
||||
|
||||
if (showScriptMessages) {
|
||||
if (settings.debug.show_script_messages) {
|
||||
debugPrint("\n");
|
||||
debugPrint(string);
|
||||
}
|
||||
@@ -1242,16 +1184,20 @@ static void opGetMapVar(Program* program)
|
||||
{
|
||||
int data = programStackPopInteger(program);
|
||||
|
||||
int value = mapGetGlobalVar(data);
|
||||
ProgramValue value;
|
||||
if (mapGetGlobalVar(data, value) == -1) {
|
||||
value.opcode = VALUE_TYPE_INT;
|
||||
value.integerValue = -1;
|
||||
}
|
||||
|
||||
programStackPushInteger(program, value);
|
||||
programStackPushValue(program, value);
|
||||
}
|
||||
|
||||
// set_map_var
|
||||
// 0x4558C8
|
||||
static void opSetMapVar(Program* program)
|
||||
{
|
||||
int value = programStackPopInteger(program);
|
||||
ProgramValue value = programStackPopValue(program);
|
||||
int variable = programStackPopInteger(program);
|
||||
|
||||
mapSetGlobalVar(variable, value);
|
||||
@@ -1261,16 +1207,19 @@ static void opSetMapVar(Program* program)
|
||||
// 0x455950
|
||||
static void opGetGlobalVar(Program* program)
|
||||
{
|
||||
int data = programStackPopInteger(program);
|
||||
int variable = programStackPopInteger(program);
|
||||
|
||||
int value = -1;
|
||||
if (gGameGlobalVarsLength != 0) {
|
||||
value = gameGetGlobalVar(data);
|
||||
void* ptr = gameGetGlobalPointer(variable);
|
||||
if (ptr != nullptr) {
|
||||
programStackPushPointer(program, ptr);
|
||||
} else {
|
||||
programStackPushInteger(program, gameGetGlobalVar(variable));
|
||||
}
|
||||
} else {
|
||||
scriptError("\nScript Error: %s: op_global_var: no global vars found!", program->name);
|
||||
programStackPushInteger(program, -1);
|
||||
}
|
||||
|
||||
programStackPushInteger(program, value);
|
||||
}
|
||||
|
||||
// set_global_var
|
||||
@@ -1278,11 +1227,17 @@ static void opGetGlobalVar(Program* program)
|
||||
// 0x80C6
|
||||
static void opSetGlobalVar(Program* program)
|
||||
{
|
||||
int value = programStackPopInteger(program);
|
||||
ProgramValue value = programStackPopValue(program);
|
||||
int variable = programStackPopInteger(program);
|
||||
|
||||
if (gGameGlobalVarsLength != 0) {
|
||||
gameSetGlobalVar(variable, value);
|
||||
if (value.opcode == VALUE_TYPE_PTR) {
|
||||
gameSetGlobalPointer(variable, value.pointerValue);
|
||||
gameSetGlobalVar(variable, 0);
|
||||
} else {
|
||||
gameSetGlobalPointer(variable, nullptr);
|
||||
gameSetGlobalVar(variable, value.integerValue);
|
||||
}
|
||||
} else {
|
||||
scriptError("\nScript Error: %s: op_set_global_var: no global vars found!", program->name);
|
||||
}
|
||||
@@ -1826,23 +1781,19 @@ static void opObjectCanSeeObject(Program* program)
|
||||
Object* object2 = static_cast<Object*>(programStackPopPointer(program));
|
||||
Object* object1 = static_cast<Object*>(programStackPopPointer(program));
|
||||
|
||||
int result = 0;
|
||||
bool canSee = false;
|
||||
|
||||
if (object1 != NULL && object2 != NULL) {
|
||||
if (object2->tile != -1) {
|
||||
// NOTE: Looks like dead code, I guess these checks were incorporated
|
||||
// into higher level functions, but this code left intact.
|
||||
if (object2 == gDude) {
|
||||
dudeHasState(0);
|
||||
}
|
||||
|
||||
critterGetStat(object1, STAT_PERCEPTION);
|
||||
|
||||
if (objectCanHearObject(object1, object2)) {
|
||||
Object* a5;
|
||||
_make_straight_path(object1, object1->tile, object2->tile, NULL, &a5, 16);
|
||||
if (a5 == object2) {
|
||||
result = 1;
|
||||
if (object2 != nullptr && object1 != nullptr) {
|
||||
// SFALL: Check objects are on the same elevation.
|
||||
// CE: These checks are on par with |opObjectCanHearObject|.
|
||||
if (object2->elevation == object1->elevation) {
|
||||
if (object2->tile != -1 && object1->tile != -1) {
|
||||
if (isWithinPerception(object1, object2)) {
|
||||
Object* obstacle;
|
||||
_make_straight_path(object1, object1->tile, object2->tile, nullptr, &obstacle, 16);
|
||||
if (obstacle == object2) {
|
||||
canSee = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1850,7 +1801,7 @@ static void opObjectCanSeeObject(Program* program)
|
||||
scriptPredefinedError(program, "obj_can_see_obj", SCRIPT_ERROR_OBJECT_IS_NULL);
|
||||
}
|
||||
|
||||
programStackPushInteger(program, result);
|
||||
programStackPushInteger(program, canSee);
|
||||
}
|
||||
|
||||
// attack_complex
|
||||
@@ -1902,8 +1853,8 @@ static void opAttackComplex(Program* program)
|
||||
|
||||
if (isInCombat()) {
|
||||
CritterCombatData* combatData = &(self->data.critter.combat);
|
||||
if ((combatData->maneuver & CRITTER_MANEUVER_0x01) == 0) {
|
||||
combatData->maneuver |= CRITTER_MANEUVER_0x01;
|
||||
if ((combatData->maneuver & CRITTER_MANEUVER_ENGAGING) == 0) {
|
||||
combatData->maneuver |= CRITTER_MANEUVER_ENGAGING;
|
||||
combatData->whoHitMe = target;
|
||||
}
|
||||
} else {
|
||||
@@ -2088,7 +2039,7 @@ static void opMetarule3(Program* program)
|
||||
}
|
||||
break;
|
||||
case METARULE3_TILE_SET_CENTER:
|
||||
result.integerValue = tileSetCenter(param1.integerValue, TILE_SET_CENTER_FLAG_0x01);
|
||||
result.integerValue = tileSetCenter(param1.integerValue, TILE_SET_CENTER_REFRESH_WINDOW);
|
||||
break;
|
||||
case METARULE3_109:
|
||||
result.integerValue = aiGetChemUse(static_cast<Object*>(param1.pointerValue));
|
||||
@@ -2232,7 +2183,7 @@ static void opSetExitGrids(Program* program)
|
||||
|
||||
Object* object = objectFindFirstAtElevation(elevation);
|
||||
while (object != NULL) {
|
||||
if (object->pid >= PROTO_ID_0x5000010 && object->pid <= PROTO_ID_0x5000017) {
|
||||
if (object->pid >= FIRST_EXIT_GRID_PID && object->pid <= LAST_EXIT_GRID_PID) {
|
||||
object->data.misc.map = destinationMap;
|
||||
object->data.misc.tile = destinationTile;
|
||||
object->data.misc.elevation = destinationElevation;
|
||||
@@ -2277,23 +2228,36 @@ static void opCritterHeal(Program* program)
|
||||
// 0x457934
|
||||
static void opSetLightLevel(Program* program)
|
||||
{
|
||||
// Maps light level to light intensity.
|
||||
//
|
||||
// Middle value is mapped one-to-one which corresponds to 50% light level
|
||||
// (cavern lighting). Light levels above (51-100%) and below (0-49%) is
|
||||
// calculated as percentage from two adjacent light values.
|
||||
//
|
||||
// 0x453F90
|
||||
static const int intensities[3] = {
|
||||
LIGHT_INTENSITY_MIN,
|
||||
(LIGHT_INTENSITY_MIN + LIGHT_INTENSITY_MAX) / 2,
|
||||
LIGHT_INTENSITY_MAX,
|
||||
};
|
||||
|
||||
int data = programStackPopInteger(program);
|
||||
|
||||
int lightLevel = data;
|
||||
|
||||
if (data == 50) {
|
||||
lightSetLightLevel(dword_453F90[1], true);
|
||||
lightSetAmbientIntensity(intensities[1], true);
|
||||
return;
|
||||
}
|
||||
|
||||
int lightIntensity;
|
||||
if (data > 50) {
|
||||
lightIntensity = dword_453F90[1] + data * (dword_453F90[2] - dword_453F90[1]) / 100;
|
||||
lightIntensity = intensities[1] + data * (intensities[2] - intensities[1]) / 100;
|
||||
} else {
|
||||
lightIntensity = dword_453F90[0] + data * (dword_453F90[1] - dword_453F90[0]) / 100;
|
||||
lightIntensity = intensities[0] + data * (intensities[1] - intensities[0]) / 100;
|
||||
}
|
||||
|
||||
lightSetLightLevel(lightIntensity, true);
|
||||
lightSetAmbientIntensity(lightIntensity, true);
|
||||
}
|
||||
|
||||
// game_time
|
||||
@@ -2364,11 +2328,8 @@ static void opKillCritter(Program* program)
|
||||
static int _correctDeath(Object* critter, int anim, bool forceBack)
|
||||
{
|
||||
if (anim >= ANIM_BIG_HOLE_SF && anim <= ANIM_FALL_FRONT_BLOOD_SF) {
|
||||
int violenceLevel = VIOLENCE_LEVEL_MAXIMUM_BLOOD;
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_VIOLENCE_LEVEL_KEY, &violenceLevel);
|
||||
|
||||
bool useStandardDeath = false;
|
||||
if (violenceLevel < VIOLENCE_LEVEL_MAXIMUM_BLOOD) {
|
||||
if (settings.preferences.violence_level < VIOLENCE_LEVEL_MAXIMUM_BLOOD) {
|
||||
useStandardDeath = true;
|
||||
} else {
|
||||
int fid = buildFid(OBJ_TYPE_CRITTER, critter->fid & 0xFFF, anim, (critter->fid & 0xF000) >> 12, critter->rotation + 1);
|
||||
@@ -2398,6 +2359,21 @@ static int _correctDeath(Object* critter, int anim, bool forceBack)
|
||||
// 0x457CB4
|
||||
static void opKillCritterType(Program* program)
|
||||
{
|
||||
// 0x518ED0
|
||||
static const int ftList[] = {
|
||||
ANIM_FALL_BACK_BLOOD_SF,
|
||||
ANIM_BIG_HOLE_SF,
|
||||
ANIM_CHARRED_BODY_SF,
|
||||
ANIM_CHUNKS_OF_FLESH_SF,
|
||||
ANIM_FALL_FRONT_BLOOD_SF,
|
||||
ANIM_FALL_BACK_BLOOD_SF,
|
||||
ANIM_DANCING_AUTOFIRE_SF,
|
||||
ANIM_SLICED_IN_HALF_SF,
|
||||
ANIM_EXPLODED_TO_NOTHING_SF,
|
||||
ANIM_FALL_BACK_BLOOD_SF,
|
||||
ANIM_FALL_FRONT_BLOOD_SF,
|
||||
};
|
||||
|
||||
int deathFrame = programStackPopInteger(program);
|
||||
int pid = programStackPopInteger(program);
|
||||
|
||||
@@ -2410,51 +2386,74 @@ static void opKillCritterType(Program* program)
|
||||
|
||||
Object* previousObj = NULL;
|
||||
int count = 0;
|
||||
int v3 = 0;
|
||||
int ftIndex = 0;
|
||||
|
||||
Object* obj = objectFindFirst();
|
||||
while (obj != NULL) {
|
||||
if (FID_ANIM_TYPE(obj->fid) >= ANIM_FALL_BACK_SF) {
|
||||
obj = objectFindNext();
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((obj->flags & OBJECT_HIDDEN) == 0 && obj->pid == pid && !critterIsDead(obj)) {
|
||||
if (obj == previousObj || count > 200) {
|
||||
scriptPredefinedError(program, "kill_critter_type", SCRIPT_ERROR_FOLLOWS);
|
||||
debugPrint(" Infinite loop destroying critters!");
|
||||
program->flags &= ~PROGRAM_FLAG_0x20;
|
||||
return;
|
||||
}
|
||||
|
||||
reg_anim_clear(obj);
|
||||
|
||||
if (deathFrame != 0) {
|
||||
_combat_delete_critter(obj);
|
||||
if (deathFrame == 1) {
|
||||
int anim = _correctDeath(obj, _ftList[v3], 1);
|
||||
critterKill(obj, anim, 1);
|
||||
v3 += 1;
|
||||
if (v3 >= 11) {
|
||||
v3 = 0;
|
||||
}
|
||||
} else {
|
||||
critterKill(obj, ANIM_FALL_BACK_SF, 1);
|
||||
if (FID_ANIM_TYPE(obj->fid) < ANIM_FALL_BACK_SF) {
|
||||
if ((obj->flags & OBJECT_HIDDEN) == 0 && obj->pid == pid && !critterIsDead(obj)) {
|
||||
if (obj == previousObj || count > 200) {
|
||||
scriptPredefinedError(program, "kill_critter_type", SCRIPT_ERROR_FOLLOWS);
|
||||
debugPrint(" Infinite loop destroying critters!");
|
||||
program->flags &= ~PROGRAM_FLAG_0x20;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
||||
reg_anim_clear(obj);
|
||||
|
||||
Rect rect;
|
||||
objectDestroy(obj, &rect);
|
||||
tileWindowRefreshRect(&rect, gElevation);
|
||||
if (deathFrame != 0) {
|
||||
_combat_delete_critter(obj);
|
||||
if (deathFrame == 1) {
|
||||
// Pick next animation from the |ftList|.
|
||||
int anim = _correctDeath(obj, ftList[ftIndex], true);
|
||||
|
||||
// SFALL: Fix for incorrect death animation.
|
||||
// CE: The fix is slightly different. Sfall passes
|
||||
// |false| to |correctDeath| to disambiguate usage
|
||||
// between this function and |opAnim|. On |false| it
|
||||
// simply returns |ANIM_FALL_BACK_SF|. Instead of doing
|
||||
// the same, check for standard death animations
|
||||
// returned from |correctDeath| and convert them to
|
||||
// appropariate single frame cousins.
|
||||
switch (anim) {
|
||||
case ANIM_FALL_BACK:
|
||||
anim = ANIM_FALL_BACK_SF;
|
||||
break;
|
||||
case ANIM_FALL_FRONT:
|
||||
anim = ANIM_FALL_FRONT_SF;
|
||||
break;
|
||||
}
|
||||
|
||||
critterKill(obj, anim, true);
|
||||
|
||||
ftIndex += 1;
|
||||
if (ftIndex >= (sizeof(ftList) / sizeof(ftList[0]))) {
|
||||
ftIndex = 0;
|
||||
}
|
||||
} else if (deathFrame >= FIRST_SF_DEATH_ANIM && deathFrame <= LAST_SF_DEATH_ANIM) {
|
||||
// CE: In some cases user-space scripts randomize death
|
||||
// frame between front/back animations but technically
|
||||
// speaking a scripter can provide any single frame
|
||||
// death animation which original code simply ignores.
|
||||
critterKill(obj, deathFrame, true);
|
||||
} else {
|
||||
critterKill(obj, ANIM_FALL_BACK_SF, true);
|
||||
}
|
||||
} else {
|
||||
reg_anim_clear(obj);
|
||||
|
||||
Rect rect;
|
||||
objectDestroy(obj, &rect);
|
||||
tileWindowRefreshRect(&rect, gElevation);
|
||||
}
|
||||
|
||||
previousObj = obj;
|
||||
count += 1;
|
||||
|
||||
objectFindFirst();
|
||||
|
||||
gMapHeader.lastVisitTime = gameTimeGetTime();
|
||||
}
|
||||
|
||||
previousObj = obj;
|
||||
count += 1;
|
||||
|
||||
objectFindFirst();
|
||||
|
||||
gMapHeader.lastVisitTime = gameTimeGetTime();
|
||||
}
|
||||
|
||||
obj = objectFindNext();
|
||||
@@ -2621,12 +2620,13 @@ static void opObjectCanHearObject(Program* program)
|
||||
|
||||
bool canHear = false;
|
||||
|
||||
// FIXME: This is clearly an error. If any of the object is NULL
|
||||
// dereferencing will crash the game.
|
||||
if (object2 == NULL || object1 == NULL) {
|
||||
// SFALL: Fix broken implementation.
|
||||
// CE: In Sfall this fix is available under "ObjCanHearObjFix" switch and
|
||||
// it's not enabled by default. Probably needs testing.
|
||||
if (object2 != nullptr && object1 != nullptr) {
|
||||
if (object2->elevation == object1->elevation) {
|
||||
if (object2->tile != -1 && object1->tile != -1) {
|
||||
if (objectCanHearObject(object2, object1)) {
|
||||
if (isWithinPerception(object1, object2)) {
|
||||
canHear = true;
|
||||
}
|
||||
}
|
||||
@@ -2698,7 +2698,7 @@ static void opGameDialogSystemEnter(Program* program)
|
||||
return;
|
||||
}
|
||||
|
||||
if (_game_state_request(GAME_STATE_4) == -1) {
|
||||
if (gameRequestState(GAME_STATE_4) == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2980,18 +2980,21 @@ static void opGetProtoData(Program* program)
|
||||
// 0x458E10
|
||||
static void opGetMessageString(Program* program)
|
||||
{
|
||||
// 0x518EFC
|
||||
static char errStr[] = "Error";
|
||||
|
||||
int messageIndex = programStackPopInteger(program);
|
||||
int messageListIndex = programStackPopInteger(program);
|
||||
|
||||
char* string;
|
||||
if (messageIndex >= 1) {
|
||||
if (messageIndex >= 0) {
|
||||
string = _scr_get_msg_str_speech(messageListIndex, messageIndex, 1);
|
||||
if (string == NULL) {
|
||||
debugPrint("\nError: No message file EXISTS!: index %d, line %d", messageListIndex, messageIndex);
|
||||
string = _errStr;
|
||||
string = errStr;
|
||||
}
|
||||
} else {
|
||||
string = _errStr;
|
||||
string = errStr;
|
||||
}
|
||||
|
||||
programStackPushString(program, string);
|
||||
@@ -3095,8 +3098,7 @@ static void _op_inven_cmds(Program* program)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// FIXME: Should be inven_cmds.
|
||||
scriptPredefinedError(program, "anim", SCRIPT_ERROR_OBJECT_IS_NULL);
|
||||
scriptPredefinedError(program, "inven_cmds", SCRIPT_ERROR_OBJECT_IS_NULL);
|
||||
}
|
||||
|
||||
programStackPushPointer(program, item);
|
||||
@@ -3146,7 +3148,7 @@ static void opFloatMessage(Program* program)
|
||||
color = _colorTable[31744];
|
||||
a5 = _colorTable[0];
|
||||
font = 103;
|
||||
tileSetCenter(gDude->tile, TILE_SET_CENTER_FLAG_0x01);
|
||||
tileSetCenter(gDude->tile, TILE_SET_CENTER_REFRESH_WINDOW);
|
||||
break;
|
||||
case FLOATING_MESSAGE_TYPE_NORMAL:
|
||||
case FLOATING_MESSAGE_TYPE_YELLOW:
|
||||
@@ -3283,10 +3285,10 @@ static void opMetarule(Program* program)
|
||||
}
|
||||
break;
|
||||
case METARULE_LANGUAGE_FILTER:
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_LANGUAGE_FILTER_KEY, &result);
|
||||
result = static_cast<int>(settings.preferences.language_filter);
|
||||
break;
|
||||
case METARULE_VIOLENCE_FILTER:
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_VIOLENCE_LEVEL_KEY, &result);
|
||||
result = settings.preferences.violence_level;
|
||||
break;
|
||||
case METARULE_WEAPON_DAMAGE_TYPE:
|
||||
if (1) {
|
||||
@@ -3313,7 +3315,7 @@ static void opMetarule(Program* program)
|
||||
if (PID_TYPE(object->pid) == OBJ_TYPE_CRITTER) {
|
||||
Proto* proto;
|
||||
protoGetProto(object->pid, &proto);
|
||||
if ((proto->critter.data.flags & CRITTER_FLAG_0x2) != 0) {
|
||||
if ((proto->critter.data.flags & CRITTER_BARTER) != 0) {
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
@@ -3475,8 +3477,7 @@ static void opRegAnimAnimate(Program* program)
|
||||
Object* object = static_cast<Object*>(programStackPopPointer(program));
|
||||
|
||||
if (!isInCombat()) {
|
||||
int violenceLevel = VIOLENCE_LEVEL_NONE;
|
||||
if (anim != 20 || object == NULL || object->pid != 0x100002F || (configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_VIOLENCE_LEVEL_KEY, &violenceLevel) && violenceLevel >= 2)) {
|
||||
if (anim != 20 || object == NULL || object->pid != 0x100002F || (settings.preferences.violence_level >= 2)) {
|
||||
if (object != NULL) {
|
||||
animationRegisterAnimate(object, anim, delay);
|
||||
} else {
|
||||
@@ -3575,21 +3576,48 @@ static void opRegAnimObjectRunToTile(Program* program)
|
||||
// 0x45A14C
|
||||
static void opPlayGameMovie(Program* program)
|
||||
{
|
||||
unsigned short flags[MOVIE_COUNT];
|
||||
memcpy(flags, word_453F9C, sizeof(word_453F9C));
|
||||
// 0x453F9C
|
||||
static const unsigned short flags[MOVIE_COUNT] = {
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
|
||||
};
|
||||
|
||||
program->flags |= PROGRAM_FLAG_0x20;
|
||||
|
||||
int data = programStackPopInteger(program);
|
||||
int movie = programStackPopInteger(program);
|
||||
|
||||
// CE: Disable map updates. Needed to stop animation of objects (dude in
|
||||
// particular) when playing movies (the problem can be seen as visual
|
||||
// artifacts when playing endgame oilrig explosion).
|
||||
bool isoWasDisabled = isoDisable();
|
||||
|
||||
gameDialogDisable();
|
||||
|
||||
if (gameMoviePlay(data, word_453F9C[data]) == -1) {
|
||||
debugPrint("\nError playing movie %d!", data);
|
||||
if (gameMoviePlay(movie, flags[movie]) == -1) {
|
||||
debugPrint("\nError playing movie %d!", movie);
|
||||
}
|
||||
|
||||
gameDialogEnable();
|
||||
|
||||
if (isoWasDisabled) {
|
||||
isoEnable();
|
||||
}
|
||||
|
||||
program->flags &= ~PROGRAM_FLAG_0x20;
|
||||
}
|
||||
|
||||
@@ -3628,7 +3656,8 @@ static void opRemoveMultipleObjectsFromInventory(Program* program)
|
||||
Object* owner = static_cast<Object*>(programStackPopPointer(program));
|
||||
|
||||
if (owner == NULL || item == NULL) {
|
||||
// FIXME: Ruined stack.
|
||||
scriptPredefinedError(program, "rm_mult_objs_from_inven", SCRIPT_ERROR_OBJECT_IS_NULL);
|
||||
programStackPushInteger(program, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4003,24 +4032,14 @@ static void _op_gdialog_barter(Program* program)
|
||||
// 0x45B010
|
||||
static void opGetGameDifficulty(Program* program)
|
||||
{
|
||||
int gameDifficulty;
|
||||
if (!configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_GAME_DIFFICULTY_KEY, &gameDifficulty)) {
|
||||
gameDifficulty = GAME_DIFFICULTY_NORMAL;
|
||||
}
|
||||
|
||||
programStackPushInteger(program, gameDifficulty);
|
||||
programStackPushInteger(program, settings.preferences.game_difficulty);
|
||||
}
|
||||
|
||||
// running_burning_guy
|
||||
// 0x45B05C
|
||||
static void opGetRunningBurningGuy(Program* program)
|
||||
{
|
||||
int runningBurningGuy;
|
||||
if (!configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_RUNNING_BURNING_GUY_KEY, &runningBurningGuy)) {
|
||||
runningBurningGuy = 1;
|
||||
}
|
||||
|
||||
programStackPushInteger(program, runningBurningGuy);
|
||||
programStackPushInteger(program, static_cast<int>(settings.preferences.running_burning_guy));
|
||||
}
|
||||
|
||||
// inven_unwield
|
||||
@@ -4421,8 +4440,8 @@ static void opAttackSetup(Program* program)
|
||||
}
|
||||
|
||||
if (isInCombat()) {
|
||||
if ((attacker->data.critter.combat.maneuver & CRITTER_MANEUVER_0x01) == 0) {
|
||||
attacker->data.critter.combat.maneuver |= CRITTER_MANEUVER_0x01;
|
||||
if ((attacker->data.critter.combat.maneuver & CRITTER_MANEUVER_ENGAGING) == 0) {
|
||||
attacker->data.critter.combat.maneuver |= CRITTER_MANEUVER_ENGAGING;
|
||||
attacker->data.critter.combat.whoHitMe = defender;
|
||||
}
|
||||
} else {
|
||||
@@ -4479,7 +4498,7 @@ static void opDestroyMultipleObjects(Program* program)
|
||||
|
||||
if (isSelf) {
|
||||
object->sid = -1;
|
||||
object->flags |= (OBJECT_HIDDEN | OBJECT_TEMPORARY);
|
||||
object->flags |= (OBJECT_HIDDEN | OBJECT_NO_SAVE);
|
||||
} else {
|
||||
reg_anim_clear(object);
|
||||
objectDestroy(object, NULL);
|
||||
@@ -4644,8 +4663,31 @@ static void opGetPartyMember(Program* program)
|
||||
// 0x45C6DC
|
||||
static void opGetRotationToTile(Program* program)
|
||||
{
|
||||
int tile2 = programStackPopInteger(program);
|
||||
int tile1 = programStackPopInteger(program);
|
||||
// CE: There is a bug in Olympus (tgrdqest) - object is passed as one of the
|
||||
// arguments instead of tile. Original game (x86) does not distinguish
|
||||
// between integers and pointers, so one of the tiles is silently ignored
|
||||
// while calculating rotation. As a workaround this opcode now accepts
|
||||
// both integers and objects.
|
||||
ProgramValue value2 = programStackPopValue(program);
|
||||
ProgramValue value1 = programStackPopValue(program);
|
||||
|
||||
int tile2;
|
||||
if (value2.isInt()) {
|
||||
tile2 = value2.integerValue;
|
||||
} else if (value2.isPointer()) {
|
||||
tile2 = static_cast<Object*>(value2.pointerValue)->tile;
|
||||
} else {
|
||||
programFatalError("script error: %s: invalid arg 2 to rotation_to_tile", program->name);
|
||||
}
|
||||
|
||||
int tile1;
|
||||
if (value1.isInt()) {
|
||||
tile1 = value1.integerValue;
|
||||
} else if (value1.isPointer()) {
|
||||
tile1 = static_cast<Object*>(value1.pointerValue)->tile;
|
||||
} else {
|
||||
programFatalError("script error: %s: invalid arg 1 to rotation_to_tile", program->name);
|
||||
}
|
||||
|
||||
int rotation = tileGetRotationTo(tile1, tile2);
|
||||
programStackPushInteger(program, rotation);
|
||||
@@ -4673,12 +4715,7 @@ static void opGameDialogSetBarterMod(Program* program)
|
||||
// 0x45C830
|
||||
static void opGetCombatDifficulty(Program* program)
|
||||
{
|
||||
int combatDifficulty;
|
||||
if (!configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_DIFFICULTY_KEY, &combatDifficulty)) {
|
||||
combatDifficulty = 0;
|
||||
}
|
||||
|
||||
programStackPushInteger(program, combatDifficulty);
|
||||
programStackPushInteger(program, settings.preferences.combat_difficulty);
|
||||
}
|
||||
|
||||
// obj_on_screen
|
||||
@@ -4751,7 +4788,7 @@ static void opTerminateCombat(Program* program)
|
||||
Object* self = scriptGetSelf(program);
|
||||
if (self != NULL) {
|
||||
if (PID_TYPE(self->pid) == OBJ_TYPE_CRITTER) {
|
||||
self->data.critter.combat.maneuver |= CRITTER_MANEUVER_STOP_ATTACKING;
|
||||
self->data.critter.combat.maneuver |= CRITTER_MANEUVER_DISENGAGING;
|
||||
self->data.critter.combat.whoHitMe = NULL;
|
||||
aiInfoSetLastTarget(self, NULL);
|
||||
}
|
||||
@@ -4766,9 +4803,7 @@ static void opDebugMessage(Program* program)
|
||||
char* string = programStackPopString(program);
|
||||
|
||||
if (string != NULL) {
|
||||
bool showScriptMessages = false;
|
||||
configGetBool(&gGameConfig, GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_SHOW_SCRIPT_MESSAGES_KEY, &showScriptMessages);
|
||||
if (showScriptMessages) {
|
||||
if (settings.debug.show_script_messages) {
|
||||
debugPrint("\n");
|
||||
debugPrint(string);
|
||||
}
|
||||
@@ -4782,7 +4817,7 @@ static void opCritterStopAttacking(Program* program)
|
||||
Object* obj = static_cast<Object*>(programStackPopPointer(program));
|
||||
|
||||
if (obj != NULL) {
|
||||
obj->data.critter.combat.maneuver |= CRITTER_MANEUVER_STOP_ATTACKING;
|
||||
obj->data.critter.combat.maneuver |= CRITTER_MANEUVER_DISENGAGING;
|
||||
obj->data.critter.combat.whoHitMe = NULL;
|
||||
aiInfoSetLastTarget(obj, NULL);
|
||||
} else {
|
||||
@@ -4840,6 +4875,7 @@ static void opGetPcStat(Program* program)
|
||||
// 0x45CDD4
|
||||
void _intExtraClose_()
|
||||
{
|
||||
sfallOpcodesExit();
|
||||
}
|
||||
|
||||
// 0x45CDD8
|
||||
@@ -5026,6 +5062,8 @@ void _initIntExtra()
|
||||
interpreterRegisterOpcode(0x8153, opTerminateCombat); // op_terminate_combat
|
||||
interpreterRegisterOpcode(0x8154, opDebugMessage); // op_debug_msg
|
||||
interpreterRegisterOpcode(0x8155, opCritterStopAttacking); // op_critter_stop_attacking
|
||||
|
||||
sfallOpcodesInit();
|
||||
}
|
||||
|
||||
// NOTE: Uncollapsed 0x45D878.
|
||||
|
||||
+51
-33
@@ -1,16 +1,18 @@
|
||||
#include "interpreter_lib.h"
|
||||
|
||||
#include "color.h"
|
||||
#include "core.h"
|
||||
#include "datafile.h"
|
||||
#include "debug.h"
|
||||
#include "dialog.h"
|
||||
#include "input.h"
|
||||
#include "interpreter_extra.h"
|
||||
#include "memory_manager.h"
|
||||
#include "mouse.h"
|
||||
#include "mouse_manager.h"
|
||||
#include "nevs.h"
|
||||
#include "select_file_list.h"
|
||||
#include "sound.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "window.h"
|
||||
#include "window_manager_private.h"
|
||||
@@ -211,16 +213,28 @@ static void opPrint(Program* program)
|
||||
_selectWindowID(program->windowId);
|
||||
|
||||
ProgramValue value = programStackPopValue(program);
|
||||
char string[80];
|
||||
|
||||
// SFALL: Fix broken Print() script function.
|
||||
// CE: Original code uses `interpretOutput` to handle printing. However
|
||||
// this function looks invalid or broken itself. Check `opSelect` - it sets
|
||||
// `outputFunc` to `windowOutput`, but `outputFunc` is never called. I'm not
|
||||
// sure if this fix can be moved into `interpretOutput` because it is also
|
||||
// used in procedure setup functions.
|
||||
//
|
||||
// The fix is slightly different, Sfall fixes strings only, ints and floats
|
||||
// are still passed to `interpretOutput`.
|
||||
switch (value.opcode & VALUE_TYPE_MASK) {
|
||||
case VALUE_TYPE_STRING:
|
||||
_interpretOutput("%s", programGetString(program, value.opcode, value.integerValue));
|
||||
_windowOutput(programGetString(program, value.opcode, value.integerValue));
|
||||
break;
|
||||
case VALUE_TYPE_FLOAT:
|
||||
_interpretOutput("%.5f", value.floatValue);
|
||||
snprintf(string, sizeof(string), "%.5f", value.floatValue);
|
||||
_windowOutput(string);
|
||||
break;
|
||||
case VALUE_TYPE_INT:
|
||||
_interpretOutput("%d", value.integerValue);
|
||||
snprintf(string, sizeof(string), "%d", value.integerValue);
|
||||
_windowOutput(string);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -343,13 +357,13 @@ static void opPrintRect(Program* program)
|
||||
char string[80];
|
||||
switch (value.opcode & VALUE_TYPE_MASK) {
|
||||
case VALUE_TYPE_STRING:
|
||||
sprintf(string, "%s", programGetString(program, value.opcode, value.integerValue));
|
||||
snprintf(string, sizeof(string), "%s", programGetString(program, value.opcode, value.integerValue));
|
||||
break;
|
||||
case VALUE_TYPE_FLOAT:
|
||||
sprintf(string, "%.5f", value.floatValue);
|
||||
snprintf(string, sizeof(string), "%.5f", value.floatValue);
|
||||
break;
|
||||
case VALUE_TYPE_INT:
|
||||
sprintf(string, "%d", value.integerValue);
|
||||
snprintf(string, sizeof(string), "%d", value.integerValue);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -407,12 +421,13 @@ static void _interpretFadePaletteBK(unsigned char* oldPalette, unsigned char* ne
|
||||
int index;
|
||||
unsigned char palette[256 * 3];
|
||||
|
||||
time = _get_time();
|
||||
time = getTicks();
|
||||
previousTime = time;
|
||||
steps = (int)duration;
|
||||
step = 0;
|
||||
delta = 0;
|
||||
|
||||
// TODO: Check if it needs throttling.
|
||||
if (duration != 0.0) {
|
||||
while (step < steps) {
|
||||
if (delta != 0) {
|
||||
@@ -421,6 +436,7 @@ static void _interpretFadePaletteBK(unsigned char* oldPalette, unsigned char* ne
|
||||
}
|
||||
|
||||
_setSystemPalette(palette);
|
||||
renderPresent();
|
||||
|
||||
previousTime = time;
|
||||
step += delta;
|
||||
@@ -430,12 +446,13 @@ static void _interpretFadePaletteBK(unsigned char* oldPalette, unsigned char* ne
|
||||
_process_bk();
|
||||
}
|
||||
|
||||
time = _get_time();
|
||||
time = getTicks();
|
||||
delta = time - previousTime;
|
||||
}
|
||||
}
|
||||
|
||||
_setSystemPalette(newPalette);
|
||||
renderPresent();
|
||||
}
|
||||
|
||||
// NOTE: Unused.
|
||||
@@ -1463,8 +1480,9 @@ static void opSetTextColor(Program* program)
|
||||
}
|
||||
|
||||
for (int arg = 0; arg < 3; arg++) {
|
||||
if (((value[arg].opcode & VALUE_TYPE_MASK) != VALUE_TYPE_FLOAT && (value[arg].opcode & VALUE_TYPE_MASK) != VALUE_TYPE_INT)
|
||||
|| value[arg].floatValue == 0.0) {
|
||||
if ((value[arg].opcode & VALUE_TYPE_MASK) != VALUE_TYPE_FLOAT
|
||||
&& (value[arg].opcode & VALUE_TYPE_MASK) == VALUE_TYPE_INT
|
||||
&& value[arg].integerValue != 0) {
|
||||
programFatalError("Invalid type given to settextcolor");
|
||||
}
|
||||
}
|
||||
@@ -1490,8 +1508,9 @@ static void opSayOptionColor(Program* program)
|
||||
}
|
||||
|
||||
for (int arg = 0; arg < 3; arg++) {
|
||||
if (((value[arg].opcode & VALUE_TYPE_MASK) != VALUE_TYPE_FLOAT && (value[arg].opcode & VALUE_TYPE_MASK) != VALUE_TYPE_INT)
|
||||
|| value[arg].floatValue == 0.0) {
|
||||
if ((value[arg].opcode & VALUE_TYPE_MASK) != VALUE_TYPE_FLOAT
|
||||
&& (value[arg].opcode & VALUE_TYPE_MASK) == VALUE_TYPE_INT
|
||||
&& value[arg].integerValue != 0) {
|
||||
programFatalError("Invalid type given to sayoptioncolor");
|
||||
}
|
||||
}
|
||||
@@ -1517,8 +1536,9 @@ static void opSayReplyColor(Program* program)
|
||||
}
|
||||
|
||||
for (int arg = 0; arg < 3; arg++) {
|
||||
if (((value[arg].opcode & VALUE_TYPE_MASK) != VALUE_TYPE_FLOAT && (value[arg].opcode & VALUE_TYPE_MASK) != VALUE_TYPE_INT)
|
||||
|| value[arg].floatValue == 0.0) {
|
||||
if ((value[arg].opcode & VALUE_TYPE_MASK) != VALUE_TYPE_FLOAT
|
||||
&& (value[arg].opcode & VALUE_TYPE_MASK) == VALUE_TYPE_INT
|
||||
&& value[arg].integerValue != 0) {
|
||||
programFatalError("Invalid type given to sayreplycolor");
|
||||
}
|
||||
}
|
||||
@@ -1544,8 +1564,9 @@ static void opSetHighlightColor(Program* program)
|
||||
}
|
||||
|
||||
for (int arg = 0; arg < 3; arg++) {
|
||||
if (((value[arg].opcode & VALUE_TYPE_MASK) != VALUE_TYPE_FLOAT && (value[arg].opcode & VALUE_TYPE_MASK) != VALUE_TYPE_INT)
|
||||
|| value[arg].floatValue == 0.0) {
|
||||
if ((value[arg].opcode & VALUE_TYPE_MASK) != VALUE_TYPE_FLOAT
|
||||
&& (value[arg].opcode & VALUE_TYPE_MASK) == VALUE_TYPE_INT
|
||||
&& value[arg].integerValue != 0) {
|
||||
programFatalError("Invalid type given to sayreplycolor");
|
||||
}
|
||||
}
|
||||
@@ -1851,32 +1872,29 @@ static int intLibSoundDelete(int value)
|
||||
// 0x466110
|
||||
static int intLibSoundPlay(char* fileName, int mode)
|
||||
{
|
||||
int v3 = 1;
|
||||
int v5 = 0;
|
||||
int type = SOUND_TYPE_MEMORY;
|
||||
int soundFlags = 0;
|
||||
|
||||
if (mode & 0x01) {
|
||||
// looping
|
||||
v5 |= 0x20;
|
||||
soundFlags |= SOUND_LOOPING;
|
||||
} else {
|
||||
v3 = 5;
|
||||
type |= SOUND_TYPE_FIRE_AND_FORGET;
|
||||
}
|
||||
|
||||
if (mode & 0x02) {
|
||||
v5 |= 0x08;
|
||||
soundFlags |= SOUND_16BIT;
|
||||
} else {
|
||||
v5 |= 0x10;
|
||||
soundFlags |= SOUND_8BIT;
|
||||
}
|
||||
|
||||
if (mode & 0x0100) {
|
||||
// memory
|
||||
v3 &= ~0x03;
|
||||
v3 |= 0x01;
|
||||
type &= ~(SOUND_TYPE_MEMORY | SOUND_TYPE_STREAMING);
|
||||
type |= SOUND_TYPE_MEMORY;
|
||||
}
|
||||
|
||||
if (mode & 0x0200) {
|
||||
// streamed
|
||||
v3 &= ~0x03;
|
||||
v3 |= 0x02;
|
||||
type &= ~(SOUND_TYPE_MEMORY | SOUND_TYPE_STREAMING);
|
||||
type |= SOUND_TYPE_STREAMING;
|
||||
}
|
||||
|
||||
int index;
|
||||
@@ -1890,7 +1908,7 @@ static int intLibSoundPlay(char* fileName, int mode)
|
||||
return -1;
|
||||
}
|
||||
|
||||
Sound* sound = gIntLibSounds[index] = soundAllocate(v3, v5);
|
||||
Sound* sound = gIntLibSounds[index] = soundAllocate(type, soundFlags);
|
||||
if (sound == NULL) {
|
||||
return -1;
|
||||
}
|
||||
@@ -1988,7 +2006,7 @@ static int intLibSoundPause(int value)
|
||||
}
|
||||
|
||||
int rc;
|
||||
if (_soundType(sound, 0x01)) {
|
||||
if (_soundType(sound, SOUND_TYPE_MEMORY)) {
|
||||
rc = soundStop(sound);
|
||||
} else {
|
||||
rc = soundPause(sound);
|
||||
@@ -2040,7 +2058,7 @@ static int intLibSoundResume(int value)
|
||||
}
|
||||
|
||||
int rc;
|
||||
if (_soundType(sound, 0x01)) {
|
||||
if (_soundType(sound, SOUND_TYPE_MEMORY)) {
|
||||
rc = soundPlay(sound);
|
||||
} else {
|
||||
rc = soundResume(sound);
|
||||
|
||||
+131
-53
@@ -12,7 +12,6 @@
|
||||
#include "color.h"
|
||||
#include "combat.h"
|
||||
#include "combat_ai.h"
|
||||
#include "core.h"
|
||||
#include "critter.h"
|
||||
#include "dbox.h"
|
||||
#include "debug.h"
|
||||
@@ -23,11 +22,14 @@
|
||||
#include "game_dialog.h"
|
||||
#include "game_mouse.h"
|
||||
#include "game_sound.h"
|
||||
#include "input.h"
|
||||
#include "interface.h"
|
||||
#include "item.h"
|
||||
#include "kb.h"
|
||||
#include "light.h"
|
||||
#include "map.h"
|
||||
#include "message.h"
|
||||
#include "mouse.h"
|
||||
#include "object.h"
|
||||
#include "party_member.h"
|
||||
#include "perk.h"
|
||||
@@ -39,6 +41,7 @@
|
||||
#include "scripts.h"
|
||||
#include "skill.h"
|
||||
#include "stat.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "tile.h"
|
||||
#include "window_manager.h"
|
||||
@@ -521,7 +524,7 @@ static int inventoryMessageListInit()
|
||||
if (!messageListInit(&gInventoryMessageList))
|
||||
return -1;
|
||||
|
||||
sprintf(path, "%s%s", asc_5186C8, "inventry.msg");
|
||||
snprintf(path, sizeof(path), "%s%s", asc_5186C8, "inventry.msg");
|
||||
if (!messageListLoad(&gInventoryMessageList, path))
|
||||
return -1;
|
||||
|
||||
@@ -545,6 +548,8 @@ void inventoryOpen()
|
||||
}
|
||||
}
|
||||
|
||||
ScopedGameMode gm(GameMode::kInventory);
|
||||
|
||||
if (inventoryCommonInit() == -1) {
|
||||
return;
|
||||
}
|
||||
@@ -585,7 +590,9 @@ void inventoryOpen()
|
||||
inventorySetCursor(INVENTORY_WINDOW_CURSOR_HAND);
|
||||
|
||||
for (;;) {
|
||||
int keyCode = _get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
int keyCode = inputGetInput();
|
||||
|
||||
// SFALL: Close with 'I'.
|
||||
if (keyCode == KEY_ESCAPE || keyCode == KEY_UPPERCASE_I || keyCode == KEY_LOWERCASE_I) {
|
||||
@@ -598,7 +605,7 @@ void inventoryOpen()
|
||||
|
||||
_display_body(-1, INVENTORY_WINDOW_TYPE_NORMAL);
|
||||
|
||||
if (_game_state() == GAME_STATE_5) {
|
||||
if (gameGetState() == GAME_STATE_5) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -680,6 +687,9 @@ void inventoryOpen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
_inven_dude = _stack[0];
|
||||
@@ -733,7 +743,7 @@ static bool _setup_inventory(int inventoryWindowType)
|
||||
windowDescription->width,
|
||||
windowDescription->height,
|
||||
257,
|
||||
WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
|
||||
WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
|
||||
gInventoryWindowMaxX = windowDescription->width + inventoryWindowX;
|
||||
gInventoryWindowMaxY = windowDescription->height + inventoryWindowY;
|
||||
|
||||
@@ -1756,14 +1766,14 @@ static void _display_inventory(int a1, int a2, int inventoryWindowType)
|
||||
if (PID_TYPE(object->pid) == OBJ_TYPE_CRITTER) {
|
||||
int carryWeight = critterGetStat(object, STAT_CARRY_WEIGHT);
|
||||
int inventoryWeight = objectGetInventoryWeight(object);
|
||||
sprintf(formattedText, "%d/%d", inventoryWeight, carryWeight);
|
||||
snprintf(formattedText, sizeof(formattedText), "%d/%d", inventoryWeight, carryWeight);
|
||||
|
||||
if (critterIsEncumbered(object)) {
|
||||
color = _colorTable[31744];
|
||||
}
|
||||
} else {
|
||||
int inventoryWeight = objectGetInventoryWeight(object);
|
||||
sprintf(formattedText, "%d", inventoryWeight);
|
||||
snprintf(formattedText, sizeof(formattedText), "%d", inventoryWeight);
|
||||
}
|
||||
|
||||
int width = fontGetStringWidth(formattedText);
|
||||
@@ -1879,7 +1889,7 @@ static void _display_target_inventory(int a1, int a2, Inventory* inventory, int
|
||||
if (PID_TYPE(object->pid) == OBJ_TYPE_CRITTER) {
|
||||
int currentWeight = objectGetInventoryWeight(object);
|
||||
int maxWeight = critterGetStat(object, STAT_CARRY_WEIGHT);
|
||||
sprintf(formattedText, "%d/%d", currentWeight, maxWeight);
|
||||
snprintf(formattedText, sizeof(formattedText), "%d/%d", currentWeight, maxWeight);
|
||||
|
||||
if (critterIsEncumbered(object)) {
|
||||
color = _colorTable[31744];
|
||||
@@ -1888,11 +1898,11 @@ static void _display_target_inventory(int a1, int a2, Inventory* inventory, int
|
||||
if (itemGetType(object) == ITEM_TYPE_CONTAINER) {
|
||||
int currentSize = containerGetTotalSize(object);
|
||||
int maxSize = containerGetMaxSize(object);
|
||||
sprintf(formattedText, "%d/%d", currentSize, maxSize);
|
||||
snprintf(formattedText, sizeof(formattedText), "%d/%d", currentSize, maxSize);
|
||||
}
|
||||
} else {
|
||||
int inventoryWeight = objectGetInventoryWeight(object);
|
||||
sprintf(formattedText, "%d", inventoryWeight);
|
||||
snprintf(formattedText, sizeof(formattedText), "%d", inventoryWeight);
|
||||
}
|
||||
|
||||
int width = fontGetStringWidth(formattedText);
|
||||
@@ -1928,7 +1938,7 @@ static void _display_inventory_info(Object* item, int quantity, unsigned char* d
|
||||
ammoQuantity = 99999;
|
||||
}
|
||||
|
||||
sprintf(formattedText, "x%d", ammoQuantity);
|
||||
snprintf(formattedText, sizeof(formattedText), "x%d", ammoQuantity);
|
||||
draw = true;
|
||||
} else {
|
||||
if (quantity > 1) {
|
||||
@@ -1937,14 +1947,12 @@ static void _display_inventory_info(Object* item, int quantity, unsigned char* d
|
||||
v9 -= 1;
|
||||
}
|
||||
|
||||
// NOTE: Checking for quantity twice probably means inlined function
|
||||
// or some macro expansion.
|
||||
if (quantity > 1) {
|
||||
if (v9 > 99999) {
|
||||
v9 = 99999;
|
||||
}
|
||||
|
||||
sprintf(formattedText, "x%d", v9);
|
||||
snprintf(formattedText, sizeof(formattedText), "x%d", v9);
|
||||
draw = true;
|
||||
}
|
||||
}
|
||||
@@ -2093,7 +2101,7 @@ static void _display_body(int fid, int inventoryWindowType)
|
||||
artUnlock(handle);
|
||||
}
|
||||
|
||||
gInventoryWindowDudeRotationTimestamp = _get_time();
|
||||
gInventoryWindowDudeRotationTimestamp = getTicks();
|
||||
}
|
||||
|
||||
// 0x470A2C
|
||||
@@ -2348,8 +2356,13 @@ static void _inven_pickup(int keyCode, int a2)
|
||||
}
|
||||
|
||||
do {
|
||||
_get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
inputGetInput();
|
||||
_display_body(-1, INVENTORY_WINDOW_TYPE_NORMAL);
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
} while ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_REPEAT) != 0);
|
||||
|
||||
if (itemInventoryFrmImage.isLocked()) {
|
||||
@@ -2605,6 +2618,8 @@ static void _adjust_fid()
|
||||
// 0x4717E4
|
||||
void inventoryOpenUseItemOn(Object* a1)
|
||||
{
|
||||
ScopedGameMode gm(GameMode::kUseOn);
|
||||
|
||||
if (inventoryCommonInit() == -1) {
|
||||
return;
|
||||
}
|
||||
@@ -2613,13 +2628,15 @@ void inventoryOpenUseItemOn(Object* a1)
|
||||
_display_inventory(_stack_offset[_curr_stack], -1, INVENTORY_WINDOW_TYPE_USE_ITEM_ON);
|
||||
inventorySetCursor(INVENTORY_WINDOW_CURSOR_HAND);
|
||||
for (;;) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
if (_game_user_wants_to_quit != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
_display_body(-1, INVENTORY_WINDOW_TYPE_USE_ITEM_ON);
|
||||
|
||||
int keyCode = _get_input();
|
||||
int keyCode = inputGetInput();
|
||||
switch (keyCode) {
|
||||
case KEY_HOME:
|
||||
_stack_offset[_curr_stack] = 0;
|
||||
@@ -2677,7 +2694,9 @@ void inventoryOpenUseItemOn(Object* a1)
|
||||
inventoryWindowOpenContextMenu(keyCode, INVENTORY_WINDOW_TYPE_USE_ITEM_ON);
|
||||
} else {
|
||||
int inventoryItemIndex = _pud->length - (_stack_offset[_curr_stack] + keyCode - 1000 + 1);
|
||||
if (inventoryItemIndex < _pud->length) {
|
||||
// SFALL: Fix crash when clicking on empty space in the inventory list
|
||||
// opened by "Use Inventory Item On" (backpack) action icon
|
||||
if (inventoryItemIndex < _pud->length && inventoryItemIndex >= 0) {
|
||||
InventoryItem* inventoryItem = &(_pud->items[inventoryItemIndex]);
|
||||
if (isInCombat()) {
|
||||
if (gDude->data.critter.combat.ap >= 2) {
|
||||
@@ -2723,6 +2742,9 @@ void inventoryOpenUseItemOn(Object* a1)
|
||||
if (keyCode == KEY_ESCAPE) {
|
||||
break;
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
_exit_inventory(isoWasEnabled);
|
||||
@@ -2887,7 +2909,7 @@ static void inventoryRenderSummary()
|
||||
}
|
||||
|
||||
int value = critterGetStat(_stack[0], stat);
|
||||
sprintf(formattedText, "%d", value);
|
||||
snprintf(formattedText, sizeof(formattedText), "%d", value);
|
||||
fontDrawText(windowBuffer + offset + 24, formattedText, 80, INVENTORY_WINDOW_WIDTH, _colorTable[992]);
|
||||
|
||||
offset += INVENTORY_WINDOW_WIDTH * fontGetLineHeight();
|
||||
@@ -2903,12 +2925,12 @@ static void inventoryRenderSummary()
|
||||
|
||||
if (v57[index] == -1) {
|
||||
int value = critterGetStat(_stack[0], v56[index]);
|
||||
sprintf(formattedText, " %d", value);
|
||||
snprintf(formattedText, sizeof(formattedText), " %d", value);
|
||||
} else {
|
||||
int value1 = critterGetStat(_stack[0], v56[index]);
|
||||
int value2 = critterGetStat(_stack[0], v57[index]);
|
||||
const char* format = index != 0 ? "%d/%d%%" : "%d/%d";
|
||||
sprintf(formattedText, format, value1, value2);
|
||||
snprintf(formattedText, sizeof(formattedText), format, value1, value2);
|
||||
}
|
||||
|
||||
fontDrawText(windowBuffer + offset + 104, formattedText, 80, INVENTORY_WINDOW_WIDTH, _colorTable[992]);
|
||||
@@ -2980,7 +3002,7 @@ static void inventoryRenderSummary()
|
||||
int bonusDamage = unarmedGetDamage(hitMode, &minDamage, &maxDamage);
|
||||
int meleeDamage = critterGetStat(_stack[0], STAT_MELEE_DAMAGE);
|
||||
// TODO: Localize unarmed attack names.
|
||||
sprintf(formattedText, "%s %d-%d",
|
||||
snprintf(formattedText, sizeof(formattedText), "%s %d-%d",
|
||||
messageListItem.text,
|
||||
bonusDamage + minDamage,
|
||||
bonusDamage + meleeDamage + maxDamage);
|
||||
@@ -3062,7 +3084,7 @@ static void inventoryRenderSummary()
|
||||
damageMin += 2 * perkGetRank(gDude, PERK_BONUS_HTH_DAMAGE);
|
||||
}
|
||||
}
|
||||
sprintf(formattedText, "%s %d-%d", messageListItem.text, damageMin, damageMax + meleeDamage);
|
||||
snprintf(formattedText, sizeof(formattedText), "%s %d-%d", messageListItem.text, damageMin, damageMax + meleeDamage);
|
||||
} else {
|
||||
MessageListItem rangeMessageListItem;
|
||||
rangeMessageListItem.num = 16; // Rng:
|
||||
@@ -3079,7 +3101,7 @@ static void inventoryRenderSummary()
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(formattedText, "%s %d-%d %s %d", messageListItem.text, damageMin, damageMax + meleeDamage, rangeMessageListItem.text, range);
|
||||
snprintf(formattedText, sizeof(formattedText), "%s %d-%d %s %d", messageListItem.text, damageMin, damageMax + meleeDamage, rangeMessageListItem.text, range);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3100,17 +3122,17 @@ static void inventoryRenderSummary()
|
||||
const char* ammoName = protoGetName(ammoTypePid);
|
||||
int capacity = ammoGetCapacity(item);
|
||||
int quantity = ammoGetQuantity(item);
|
||||
sprintf(formattedText, "%s %d/%d %s", messageListItem.text, quantity, capacity, ammoName);
|
||||
snprintf(formattedText, sizeof(formattedText), "%s %d/%d %s", messageListItem.text, quantity, capacity, ammoName);
|
||||
} else {
|
||||
int capacity = ammoGetCapacity(item);
|
||||
int quantity = ammoGetQuantity(item);
|
||||
sprintf(formattedText, "%s %d/%d", messageListItem.text, quantity, capacity);
|
||||
snprintf(formattedText, sizeof(formattedText), "%s %d/%d", messageListItem.text, quantity, capacity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int capacity = ammoGetCapacity(item);
|
||||
int quantity = ammoGetQuantity(item);
|
||||
sprintf(formattedText, "%s %d/%d", messageListItem.text, quantity, capacity);
|
||||
snprintf(formattedText, sizeof(formattedText), "%s %d/%d", messageListItem.text, quantity, capacity);
|
||||
}
|
||||
|
||||
fontDrawText(windowBuffer + offset, formattedText, 140, INVENTORY_WINDOW_WIDTH, _colorTable[992]);
|
||||
@@ -3125,7 +3147,7 @@ static void inventoryRenderSummary()
|
||||
if (PID_TYPE(_stack[0]->pid) == OBJ_TYPE_CRITTER) {
|
||||
int carryWeight = critterGetStat(_stack[0], STAT_CARRY_WEIGHT);
|
||||
int inventoryWeight = objectGetInventoryWeight(_stack[0]);
|
||||
sprintf(formattedText, "%s %d/%d", messageListItem.text, inventoryWeight, carryWeight);
|
||||
snprintf(formattedText, sizeof(formattedText), "%s %d/%d", messageListItem.text, inventoryWeight, carryWeight);
|
||||
|
||||
int color = _colorTable[992];
|
||||
if (critterIsEncumbered(_stack[0])) {
|
||||
@@ -3135,7 +3157,7 @@ static void inventoryRenderSummary()
|
||||
fontDrawText(windowBuffer + offset + 15, formattedText, 120, INVENTORY_WINDOW_WIDTH, color);
|
||||
} else {
|
||||
int inventoryWeight = objectGetInventoryWeight(_stack[0]);
|
||||
sprintf(formattedText, "%s %d", messageListItem.text, inventoryWeight);
|
||||
snprintf(formattedText, sizeof(formattedText), "%s %d", messageListItem.text, inventoryWeight);
|
||||
|
||||
fontDrawText(windowBuffer + offset + 30, formattedText, 80, INVENTORY_WINDOW_WIDTH, _colorTable[992]);
|
||||
}
|
||||
@@ -3295,7 +3317,7 @@ int _invenWieldFunc(Object* critter, Object* item, int a3, bool a4)
|
||||
int lightIntensity;
|
||||
int lightDistance;
|
||||
if (critter == gDude) {
|
||||
lightIntensity = LIGHT_LEVEL_MAX;
|
||||
lightIntensity = LIGHT_INTENSITY_MAX;
|
||||
lightDistance = 4;
|
||||
} else {
|
||||
Proto* proto;
|
||||
@@ -3612,7 +3634,7 @@ static void inventoryExamineItem(Object* critter, Object* item)
|
||||
// Clear item description area.
|
||||
FrmImage backgroundFrmImage;
|
||||
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 48, 0, 0, 0);
|
||||
if (backgroundFrmImage.lock(backgroundFid) != NULL) {
|
||||
if (backgroundFrmImage.lock(backgroundFid)) {
|
||||
blitBufferToBuffer(backgroundFrmImage.getData() + INVENTORY_WINDOW_WIDTH * INVENTORY_SUMMARY_Y + INVENTORY_SUMMARY_X,
|
||||
152,
|
||||
188,
|
||||
@@ -3661,7 +3683,7 @@ static void inventoryExamineItem(Object* critter, Object* item)
|
||||
}
|
||||
|
||||
char formattedText[40];
|
||||
sprintf(formattedText, messageListItem.text, weight);
|
||||
snprintf(formattedText, sizeof(formattedText), messageListItem.text, weight);
|
||||
inventoryRenderItemDescription(formattedText);
|
||||
}
|
||||
|
||||
@@ -3684,7 +3706,9 @@ static void inventoryWindowOpenContextMenu(int keyCode, int inventoryWindowType)
|
||||
|
||||
int mouseState;
|
||||
do {
|
||||
_get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
inputGetInput();
|
||||
|
||||
if (inventoryWindowType == INVENTORY_WINDOW_TYPE_NORMAL) {
|
||||
_display_body(-1, INVENTORY_WINDOW_TYPE_NORMAL);
|
||||
@@ -3700,6 +3724,9 @@ static void inventoryWindowOpenContextMenu(int keyCode, int inventoryWindowType)
|
||||
windowRefresh(gInventoryWindow);
|
||||
return;
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
} while ((mouseState & MOUSE_EVENT_LEFT_BUTTON_DOWN_REPEAT) != MOUSE_EVENT_LEFT_BUTTON_DOWN_REPEAT);
|
||||
|
||||
inventorySetCursor(INVENTORY_WINDOW_CURSOR_BLANK);
|
||||
@@ -3804,7 +3831,9 @@ static void inventoryWindowOpenContextMenu(int keyCode, int inventoryWindowType)
|
||||
int menuItemIndex = 0;
|
||||
int previousMouseY = y;
|
||||
while ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_UP) == 0) {
|
||||
_get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
inputGetInput();
|
||||
|
||||
if (inventoryWindowType == INVENTORY_WINDOW_TYPE_NORMAL) {
|
||||
_display_body(-1, INVENTORY_WINDOW_TYPE_NORMAL);
|
||||
@@ -3825,6 +3854,9 @@ static void inventoryWindowOpenContextMenu(int keyCode, int inventoryWindowType)
|
||||
windowRefreshRect(gInventoryWindow, &rect);
|
||||
previousMouseY = y;
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
buttonDestroy(btn);
|
||||
@@ -4016,8 +4048,10 @@ int inventoryOpenLooting(Object* a1, Object* a2)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ScopedGameMode gm(GameMode::kLoot);
|
||||
|
||||
if (FID_TYPE(a2->fid) == OBJ_TYPE_CRITTER) {
|
||||
if (_critter_flag_check(a2->pid, CRITTER_FLAG_0x20)) {
|
||||
if (_critter_flag_check(a2->pid, CRITTER_NO_STEAL)) {
|
||||
// You can't find anything to take from that.
|
||||
messageListItem.num = 50;
|
||||
if (messageListGetItem(&gInventoryMessageList, &messageListItem)) {
|
||||
@@ -4196,6 +4230,8 @@ int inventoryOpenLooting(Object* a1, Object* a2)
|
||||
int stealingXp = 0;
|
||||
int stealingXpBonus = 10;
|
||||
for (;;) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
if (_game_user_wants_to_quit != 0) {
|
||||
break;
|
||||
}
|
||||
@@ -4204,7 +4240,7 @@ int inventoryOpenLooting(Object* a1, Object* a2)
|
||||
break;
|
||||
}
|
||||
|
||||
int keyCode = _get_input();
|
||||
int keyCode = inputGetInput();
|
||||
|
||||
if (keyCode == KEY_CTRL_Q || keyCode == KEY_CTRL_X || keyCode == KEY_F10) {
|
||||
showQuitConfirmationDialog();
|
||||
@@ -4384,6 +4420,9 @@ int inventoryOpenLooting(Object* a1, Object* a2)
|
||||
if (keyCode == KEY_ESCAPE) {
|
||||
break;
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
if (critterCount != 0) {
|
||||
@@ -4425,7 +4464,7 @@ int inventoryOpenLooting(Object* a1, Object* a2)
|
||||
messageListItem.num = 29;
|
||||
if (messageListGetItem(&gInventoryMessageList, &messageListItem)) {
|
||||
char formattedText[200];
|
||||
sprintf(formattedText, messageListItem.text, xpGained);
|
||||
snprintf(formattedText, sizeof(formattedText), messageListItem.text, xpGained);
|
||||
displayMonitorAddMessage(formattedText);
|
||||
}
|
||||
}
|
||||
@@ -4537,7 +4576,12 @@ static int _move_inventory(Object* a1, int a2, Object* a3, bool a4)
|
||||
}
|
||||
|
||||
do {
|
||||
_get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
inputGetInput();
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
} while ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_REPEAT) != 0);
|
||||
|
||||
if (itemInventoryFrmImage.isLocked()) {
|
||||
@@ -4752,7 +4796,12 @@ static void _barter_move_inventory(Object* a1, int quantity, int a3, int a4, Obj
|
||||
}
|
||||
|
||||
do {
|
||||
_get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
inputGetInput();
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
} while ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_REPEAT) != 0);
|
||||
|
||||
if (itemInventoryFrmImage.isLocked()) {
|
||||
@@ -4834,7 +4883,12 @@ static void _barter_move_from_table_inventory(Object* a1, int quantity, int a3,
|
||||
}
|
||||
|
||||
do {
|
||||
_get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
inputGetInput();
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
} while ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_REPEAT) != 0);
|
||||
|
||||
if (itemInventoryFrmImage.isLocked()) {
|
||||
@@ -4907,11 +4961,11 @@ static void inventoryWindowRenderInnerInventories(int win, Object* a2, Object* a
|
||||
|
||||
if (messageListGetItem(&gInventoryMessageList, &messageListItem)) {
|
||||
int weight = objectGetInventoryWeight(a2);
|
||||
sprintf(formattedText, "%s %d", messageListItem.text, weight);
|
||||
snprintf(formattedText, sizeof(formattedText), "%s %d", messageListItem.text, weight);
|
||||
}
|
||||
} else {
|
||||
int cost = objectGetCost(a2);
|
||||
sprintf(formattedText, "$%d", cost);
|
||||
snprintf(formattedText, sizeof(formattedText), "$%d", cost);
|
||||
}
|
||||
|
||||
fontDrawText(windowBuffer + INVENTORY_TRADE_WINDOW_WIDTH * (INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + INVENTORY_TRADE_INNER_LEFT_SCROLLER_Y_PAD) + INVENTORY_TRADE_INNER_LEFT_SCROLLER_X_PAD, formattedText, 80, INVENTORY_TRADE_WINDOW_WIDTH, _colorTable[32767]);
|
||||
@@ -4946,11 +5000,11 @@ static void inventoryWindowRenderInnerInventories(int win, Object* a2, Object* a
|
||||
|
||||
if (messageListGetItem(&gInventoryMessageList, &messageListItem)) {
|
||||
int weight = _barter_compute_value(gDude, _target_stack[0]);
|
||||
sprintf(formattedText, "%s %d", messageListItem.text, weight);
|
||||
snprintf(formattedText, sizeof(formattedText), "%s %d", messageListItem.text, weight);
|
||||
}
|
||||
} else {
|
||||
int cost = _barter_compute_value(gDude, _target_stack[0]);
|
||||
sprintf(formattedText, "$%d", cost);
|
||||
snprintf(formattedText, sizeof(formattedText), "$%d", cost);
|
||||
}
|
||||
|
||||
fontDrawText(windowBuffer + INVENTORY_TRADE_WINDOW_WIDTH * (INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + INVENTORY_TRADE_INNER_RIGHT_SCROLLER_Y_PAD) + INVENTORY_TRADE_INNER_RIGHT_SCROLLER_X_PAD, formattedText, 80, INVENTORY_TRADE_WINDOW_WIDTH, _colorTable[32767]);
|
||||
@@ -4970,6 +5024,8 @@ static void inventoryWindowRenderInnerInventories(int win, Object* a2, Object* a
|
||||
// 0x4757F0
|
||||
void inventoryOpenTrade(int win, Object* a2, Object* a3, Object* a4, int a5)
|
||||
{
|
||||
ScopedGameMode gm(GameMode::kBarter);
|
||||
|
||||
_barter_mod = a5;
|
||||
|
||||
if (inventoryCommonInit() == -1) {
|
||||
@@ -5044,11 +5100,13 @@ void inventoryOpenTrade(int win, Object* a2, Object* a3, Object* a4, int a5)
|
||||
|
||||
int keyCode = -1;
|
||||
for (;;) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
if (keyCode == KEY_ESCAPE || _game_user_wants_to_quit != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
keyCode = _get_input();
|
||||
keyCode = inputGetInput();
|
||||
if (keyCode == KEY_CTRL_Q || keyCode == KEY_CTRL_X || keyCode == KEY_F10) {
|
||||
showQuitConfirmationDialog();
|
||||
}
|
||||
@@ -5266,6 +5324,9 @@ void inventoryOpenTrade(int win, Object* a2, Object* a3, Object* a4, int a5)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
itemMoveAll(a1a, a2);
|
||||
@@ -5515,6 +5576,8 @@ static void _draw_amount(int value, int inventoryWindowType)
|
||||
// 0x47688C
|
||||
static int inventoryQuantitySelect(int inventoryWindowType, Object* item, int max)
|
||||
{
|
||||
ScopedGameMode gm(GameMode::kCounter);
|
||||
|
||||
inventoryQuantityWindowInit(inventoryWindowType, item);
|
||||
|
||||
int value;
|
||||
@@ -5534,7 +5597,9 @@ static int inventoryQuantitySelect(int inventoryWindowType, Object* item, int ma
|
||||
|
||||
bool v5 = false;
|
||||
for (;;) {
|
||||
int keyCode = _get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
int keyCode = inputGetInput();
|
||||
if (keyCode == KEY_ESCAPE) {
|
||||
inventoryQuantityWindowFree(inventoryWindowType);
|
||||
return -1;
|
||||
@@ -5558,21 +5623,26 @@ static int inventoryQuantitySelect(int inventoryWindowType, Object* item, int ma
|
||||
if (value < max) {
|
||||
if (inventoryWindowType == INVENTORY_WINDOW_TYPE_MOVE_ITEMS) {
|
||||
if ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_REPEAT) != 0) {
|
||||
_get_time();
|
||||
getTicks();
|
||||
|
||||
unsigned int delay = 100;
|
||||
while ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_REPEAT) != 0) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
if (value < max) {
|
||||
value++;
|
||||
}
|
||||
|
||||
_draw_amount(value, inventoryWindowType);
|
||||
_get_input();
|
||||
inputGetInput();
|
||||
|
||||
if (delay > 1) {
|
||||
delay--;
|
||||
coreDelayProcessingEvents(delay);
|
||||
inputPauseForTocks(delay);
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
} else {
|
||||
if (value < max) {
|
||||
@@ -5591,21 +5661,26 @@ static int inventoryQuantitySelect(int inventoryWindowType, Object* item, int ma
|
||||
if (value > min) {
|
||||
if (inventoryWindowType == INVENTORY_WINDOW_TYPE_MOVE_ITEMS) {
|
||||
if ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_REPEAT) != 0) {
|
||||
_get_time();
|
||||
getTicks();
|
||||
|
||||
unsigned int delay = 100;
|
||||
while ((mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_REPEAT) != 0) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
if (value > min) {
|
||||
value--;
|
||||
}
|
||||
|
||||
_draw_amount(value, inventoryWindowType);
|
||||
_get_input();
|
||||
inputGetInput();
|
||||
|
||||
if (delay > 1) {
|
||||
delay--;
|
||||
coreDelayProcessingEvents(delay);
|
||||
inputPauseForTocks(delay);
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
} else {
|
||||
if (value > min) {
|
||||
@@ -5645,6 +5720,9 @@ static int inventoryQuantitySelect(int inventoryWindowType, Object* item, int ma
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
inventoryQuantityWindowFree(inventoryWindowType);
|
||||
@@ -5669,7 +5747,7 @@ static int inventoryQuantityWindowInit(int inventoryWindowType, Object* item)
|
||||
int quantityWindowY = screenGetHeight() != 480
|
||||
? (screenGetHeight() - windowDescription->height) / 2
|
||||
: windowDescription->y;
|
||||
_mt_wid = windowCreate(quantityWindowX, quantityWindowY, windowDescription->width, windowDescription->height, 257, WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
|
||||
_mt_wid = windowCreate(quantityWindowX, quantityWindowY, windowDescription->width, windowDescription->height, 257, WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
|
||||
unsigned char* windowBuffer = windowGetBuffer(_mt_wid);
|
||||
|
||||
FrmImage backgroundFrmImage;
|
||||
|
||||
+47
-22
@@ -195,12 +195,14 @@ int itemsInit()
|
||||
}
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "%s%s", asc_5186C8, "item.msg");
|
||||
snprintf(path, sizeof(path), "%s%s", asc_5186C8, "item.msg");
|
||||
|
||||
if (!messageListLoad(&gItemsMessageList, path)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_ITEM, &gItemsMessageList);
|
||||
|
||||
// SFALL
|
||||
booksInit();
|
||||
explosionsInit();
|
||||
@@ -219,6 +221,7 @@ void itemsReset()
|
||||
// 0x477148
|
||||
void itemsExit()
|
||||
{
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_ITEM, nullptr);
|
||||
messageListFree(&gItemsMessageList);
|
||||
|
||||
// SFALL
|
||||
@@ -571,8 +574,8 @@ int itemDropAll(Object* critter, int tile)
|
||||
int frmId = critter->fid & 0xFFF;
|
||||
|
||||
Inventory* inventory = &(critter->data.inventory);
|
||||
for (int index = 0; index < inventory->length; index++) {
|
||||
InventoryItem* inventoryItem = &(inventory->items[index]);
|
||||
while (inventory->length > 0) {
|
||||
InventoryItem* inventoryItem = &(inventory->items[0]);
|
||||
Object* item = inventoryItem->item;
|
||||
if (item->pid == PROTO_ID_MONEY) {
|
||||
if (itemRemove(critter, item, inventoryItem->quantity) != 0) {
|
||||
@@ -602,7 +605,19 @@ int itemDropAll(Object* critter, int tile)
|
||||
}
|
||||
}
|
||||
|
||||
for (int index = 0; index < inventoryItem->quantity; index++) {
|
||||
// This loop is a little bit tricky. `inventoryItem` is a pointer
|
||||
// to the first entry in inventory. It's `quantity` is dynamically
|
||||
// decremented during `itemRemove`. It's `item` is also updated with
|
||||
// a replacement (`itemRemove` creates new Object instance in
|
||||
// inventory).
|
||||
//
|
||||
// Once entire item stack is dropped, the content pointed to by
|
||||
// `inventoryItem` is also updated (see `item_compact`), it points
|
||||
// to the next inventory item. It can also become dangling pointer
|
||||
// (when `inventoryItem` entry is the last in inventory).
|
||||
int quantity = inventoryItem->quantity;
|
||||
for (int it = 0; it < quantity; it++) {
|
||||
item = inventoryItem->item;
|
||||
if (itemRemove(critter, item, 1) != 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -626,7 +641,7 @@ int itemDropAll(Object* critter, int tile)
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x4779F0
|
||||
@@ -1029,11 +1044,15 @@ int itemGetQuantity(Object* obj, Object* item)
|
||||
InventoryItem* inventoryItem = &(inventory->items[index]);
|
||||
if (inventoryItem->item == item) {
|
||||
quantity = inventoryItem->quantity;
|
||||
|
||||
// SFALL: Fix incorrect value being returned if there is a container
|
||||
// item in the inventory.
|
||||
break;
|
||||
} else {
|
||||
if (itemGetType(inventoryItem->item) == ITEM_TYPE_CONTAINER) {
|
||||
quantity = itemGetQuantity(inventoryItem->item, item);
|
||||
if (quantity > 0) {
|
||||
return quantity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1487,7 +1506,7 @@ bool weaponCanBeReloadedWith(Object* weapon, Object* ammo)
|
||||
{
|
||||
if (weapon->pid == PROTO_ID_SOLAR_SCORCHER) {
|
||||
// Check light level to recharge solar scorcher.
|
||||
if (lightGetLightLevel() > 62259) {
|
||||
if (lightGetAmbientIntensity() > LIGHT_INTENSITY_MAX * 0.95) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1577,7 +1596,7 @@ int weaponReload(Object* weapon, Object* ammo)
|
||||
int weaponGetRange(Object* critter, int hitMode)
|
||||
{
|
||||
int range;
|
||||
int v12;
|
||||
int effectiveStrength;
|
||||
|
||||
// NOTE: Uninline.
|
||||
Object* weapon = critterGetWeaponForHitMode(critter, hitMode);
|
||||
@@ -1593,12 +1612,18 @@ int weaponGetRange(Object* critter, int hitMode)
|
||||
|
||||
if (weaponGetAttackTypeForHitMode(weapon, hitMode) == ATTACK_TYPE_THROW) {
|
||||
if (critter == gDude) {
|
||||
v12 = critterGetStat(critter, STAT_STRENGTH) + 2 * perkGetRank(critter, PERK_HEAVE_HO);
|
||||
effectiveStrength = critterGetStat(critter, STAT_STRENGTH) + 2 * perkGetRank(critter, PERK_HEAVE_HO);
|
||||
|
||||
// SFALL: Fix for Heave Ho! increasing effective strength above
|
||||
// 10.
|
||||
if (effectiveStrength > PRIMARY_STAT_MAX) {
|
||||
effectiveStrength = PRIMARY_STAT_MAX;
|
||||
}
|
||||
} else {
|
||||
v12 = critterGetStat(critter, STAT_STRENGTH);
|
||||
effectiveStrength = critterGetStat(critter, STAT_STRENGTH);
|
||||
}
|
||||
|
||||
int maxRange = 3 * v12;
|
||||
int maxRange = 3 * effectiveStrength;
|
||||
if (range >= maxRange) {
|
||||
range = maxRange;
|
||||
}
|
||||
@@ -1607,7 +1632,7 @@ int weaponGetRange(Object* critter, int hitMode)
|
||||
return range;
|
||||
}
|
||||
|
||||
if (_critter_flag_check(critter->pid, CRITTER_FLAG_0x2000)) {
|
||||
if (_critter_flag_check(critter->pid, CRITTER_LONG_LIMBS)) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -2247,7 +2272,7 @@ int _item_m_use_charged_item(Object* critter, Object* miscItem)
|
||||
if (messageListGetItem(&gItemsMessageList, &messageListItem)) {
|
||||
char text[80];
|
||||
const char* itemName = objectGetName(miscItem);
|
||||
sprintf(text, messageListItem.text, itemName);
|
||||
snprintf(text, sizeof(text), messageListItem.text, itemName);
|
||||
displayMonitorAddMessage(text);
|
||||
}
|
||||
}
|
||||
@@ -2293,7 +2318,7 @@ int miscItemTrickleEventProcess(Object* item, void* data)
|
||||
if (messageListGetItem(&gItemsMessageList, &messageListItem)) {
|
||||
char text[80];
|
||||
const char* itemName = objectGetName(item);
|
||||
sprintf(text, messageListItem.text, itemName);
|
||||
snprintf(text, sizeof(text), messageListItem.text, itemName);
|
||||
displayMonitorAddMessage(text);
|
||||
}
|
||||
}
|
||||
@@ -2342,7 +2367,7 @@ int miscItemTurnOn(Object* item)
|
||||
messageListItem.num = 5;
|
||||
if (messageListGetItem(&gItemsMessageList, &messageListItem)) {
|
||||
char* name = objectGetName(item);
|
||||
sprintf(text, messageListItem.text, name);
|
||||
snprintf(text, sizeof(text), messageListItem.text, name);
|
||||
displayMonitorAddMessage(text);
|
||||
}
|
||||
}
|
||||
@@ -2368,7 +2393,7 @@ int miscItemTurnOn(Object* item)
|
||||
messageListItem.num = 6;
|
||||
if (messageListGetItem(&gItemsMessageList, &messageListItem)) {
|
||||
char* name = objectGetName(item);
|
||||
sprintf(text, messageListItem.text, name);
|
||||
snprintf(text, sizeof(text), messageListItem.text, name);
|
||||
displayMonitorAddMessage(text);
|
||||
}
|
||||
|
||||
@@ -2377,7 +2402,7 @@ int miscItemTurnOn(Object* item)
|
||||
messageListItem.num = 8;
|
||||
if (messageListGetItem(&gItemsMessageList, &messageListItem)) {
|
||||
int radiation = critterGetRadiation(critter);
|
||||
sprintf(text, messageListItem.text, radiation);
|
||||
snprintf(text, sizeof(text), messageListItem.text, radiation);
|
||||
displayMonitorAddMessage(text);
|
||||
}
|
||||
}
|
||||
@@ -2416,7 +2441,7 @@ int miscItemTurnOff(Object* item)
|
||||
if (messageListGetItem(&gItemsMessageList, &messageListItem)) {
|
||||
const char* name = objectGetName(item);
|
||||
char text[80];
|
||||
sprintf(text, messageListItem.text, name);
|
||||
snprintf(text, sizeof(text), messageListItem.text, name);
|
||||
displayMonitorAddMessage(text);
|
||||
}
|
||||
}
|
||||
@@ -2660,7 +2685,7 @@ static void _perform_drug_effect(Object* critter, int* stats, int* mods, bool is
|
||||
name = critterGetName(critter);
|
||||
// %s succumbs to the adverse effects of chems.
|
||||
text = getmsg(&gItemsMessageList, &messageListItem, 600);
|
||||
sprintf(v24, text, name);
|
||||
snprintf(v24, sizeof(v24), text, name);
|
||||
_combatKillCritterOutsideCombat(critter, v24);
|
||||
}
|
||||
}
|
||||
@@ -2679,7 +2704,7 @@ static void _perform_drug_effect(Object* critter, int* stats, int* mods, bool is
|
||||
messageListItem.num = after < before ? 2 : 1;
|
||||
if (messageListGetItem(&gItemsMessageList, &messageListItem)) {
|
||||
char* statName = statGetName(stat);
|
||||
sprintf(str, messageListItem.text, after < before ? before - after : after - before, statName);
|
||||
snprintf(str, sizeof(str), messageListItem.text, after < before ? before - after : after - before, statName);
|
||||
displayMonitorAddMessage(str);
|
||||
statsChanged = true;
|
||||
}
|
||||
@@ -2707,7 +2732,7 @@ static void _perform_drug_effect(Object* critter, int* stats, int* mods, bool is
|
||||
name = critterGetName(critter);
|
||||
// %s succumbs to the adverse effects of chems.
|
||||
text = getmsg(&gItemsMessageList, &messageListItem, 600);
|
||||
sprintf(v24, text, name);
|
||||
snprintf(v24, sizeof(v24), text, name);
|
||||
// TODO: Why message is ignored?
|
||||
}
|
||||
}
|
||||
@@ -3299,7 +3324,7 @@ static void booksInitCustom()
|
||||
char sectionKey[4];
|
||||
for (int index = 0; index < bookCount; index++) {
|
||||
// Books numbering starts with 1.
|
||||
sprintf(sectionKey, "%d", index + 1);
|
||||
snprintf(sectionKey, sizeof(sectionKey), "%d", index + 1);
|
||||
|
||||
int bookPid;
|
||||
if (!configGetInt(&booksConfig, sectionKey, "PID", &bookPid)) continue;
|
||||
|
||||
+71
-2978
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,358 @@
|
||||
#ifndef FALLOUT_KB_H_
|
||||
#define FALLOUT_KB_H_
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "dinput.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
#define KEY_STATE_UP 0
|
||||
#define KEY_STATE_DOWN 1
|
||||
#define KEY_STATE_REPEAT 2
|
||||
|
||||
#define MODIFIER_KEY_STATE_NUM_LOCK 0x01
|
||||
#define MODIFIER_KEY_STATE_CAPS_LOCK 0x02
|
||||
#define MODIFIER_KEY_STATE_SCROLL_LOCK 0x04
|
||||
|
||||
#define KEYBOARD_EVENT_MODIFIER_CAPS_LOCK 0x0001
|
||||
#define KEYBOARD_EVENT_MODIFIER_NUM_LOCK 0x0002
|
||||
#define KEYBOARD_EVENT_MODIFIER_SCROLL_LOCK 0x0004
|
||||
#define KEYBOARD_EVENT_MODIFIER_LEFT_SHIFT 0x0008
|
||||
#define KEYBOARD_EVENT_MODIFIER_RIGHT_SHIFT 0x0010
|
||||
#define KEYBOARD_EVENT_MODIFIER_LEFT_ALT 0x0020
|
||||
#define KEYBOARD_EVENT_MODIFIER_RIGHT_ALT 0x0040
|
||||
#define KEYBOARD_EVENT_MODIFIER_LEFT_CONTROL 0x0080
|
||||
#define KEYBOARD_EVENT_MODIFIER_RIGHT_CONTROL 0x0100
|
||||
#define KEYBOARD_EVENT_MODIFIER_ANY_SHIFT (KEYBOARD_EVENT_MODIFIER_LEFT_SHIFT | KEYBOARD_EVENT_MODIFIER_RIGHT_SHIFT)
|
||||
#define KEYBOARD_EVENT_MODIFIER_ANY_ALT (KEYBOARD_EVENT_MODIFIER_LEFT_ALT | KEYBOARD_EVENT_MODIFIER_RIGHT_ALT)
|
||||
#define KEYBOARD_EVENT_MODIFIER_ANY_CONTROL (KEYBOARD_EVENT_MODIFIER_LEFT_CONTROL | KEYBOARD_EVENT_MODIFIER_RIGHT_CONTROL)
|
||||
|
||||
#define KEY_QUEUE_SIZE 64
|
||||
|
||||
typedef enum Key {
|
||||
KEY_ESCAPE = '\x1b',
|
||||
KEY_TAB = '\x09',
|
||||
KEY_BACKSPACE = '\x08',
|
||||
KEY_RETURN = '\r',
|
||||
|
||||
KEY_SPACE = ' ',
|
||||
KEY_EXCLAMATION = '!',
|
||||
KEY_QUOTE = '"',
|
||||
KEY_NUMBER_SIGN = '#',
|
||||
KEY_DOLLAR = '$',
|
||||
KEY_PERCENT = '%',
|
||||
KEY_AMPERSAND = '&',
|
||||
KEY_SINGLE_QUOTE = '\'',
|
||||
KEY_PAREN_LEFT = '(',
|
||||
KEY_PAREN_RIGHT = ')',
|
||||
KEY_ASTERISK = '*',
|
||||
KEY_PLUS = '+',
|
||||
KEY_COMMA = ',',
|
||||
KEY_MINUS = '-',
|
||||
KEY_DOT = '.',
|
||||
KEY_SLASH = '/',
|
||||
KEY_0 = '0',
|
||||
KEY_1 = '1',
|
||||
KEY_2 = '2',
|
||||
KEY_3 = '3',
|
||||
KEY_4 = '4',
|
||||
KEY_5 = '5',
|
||||
KEY_6 = '6',
|
||||
KEY_7 = '7',
|
||||
KEY_8 = '8',
|
||||
KEY_9 = '9',
|
||||
KEY_COLON = ':',
|
||||
KEY_SEMICOLON = ';',
|
||||
KEY_LESS = '<',
|
||||
KEY_EQUAL = '=',
|
||||
KEY_GREATER = '>',
|
||||
KEY_QUESTION = '?',
|
||||
KEY_AT = '@',
|
||||
KEY_UPPERCASE_A = 'A',
|
||||
KEY_UPPERCASE_B = 'B',
|
||||
KEY_UPPERCASE_C = 'C',
|
||||
KEY_UPPERCASE_D = 'D',
|
||||
KEY_UPPERCASE_E = 'E',
|
||||
KEY_UPPERCASE_F = 'F',
|
||||
KEY_UPPERCASE_G = 'G',
|
||||
KEY_UPPERCASE_H = 'H',
|
||||
KEY_UPPERCASE_I = 'I',
|
||||
KEY_UPPERCASE_J = 'J',
|
||||
KEY_UPPERCASE_K = 'K',
|
||||
KEY_UPPERCASE_L = 'L',
|
||||
KEY_UPPERCASE_M = 'M',
|
||||
KEY_UPPERCASE_N = 'N',
|
||||
KEY_UPPERCASE_O = 'O',
|
||||
KEY_UPPERCASE_P = 'P',
|
||||
KEY_UPPERCASE_Q = 'Q',
|
||||
KEY_UPPERCASE_R = 'R',
|
||||
KEY_UPPERCASE_S = 'S',
|
||||
KEY_UPPERCASE_T = 'T',
|
||||
KEY_UPPERCASE_U = 'U',
|
||||
KEY_UPPERCASE_V = 'V',
|
||||
KEY_UPPERCASE_W = 'W',
|
||||
KEY_UPPERCASE_X = 'X',
|
||||
KEY_UPPERCASE_Y = 'Y',
|
||||
KEY_UPPERCASE_Z = 'Z',
|
||||
|
||||
KEY_BRACKET_LEFT = '[',
|
||||
KEY_BACKSLASH = '\\',
|
||||
KEY_BRACKET_RIGHT = ']',
|
||||
KEY_CARET = '^',
|
||||
KEY_UNDERSCORE = '_',
|
||||
|
||||
KEY_GRAVE = '`',
|
||||
KEY_LOWERCASE_A = 'a',
|
||||
KEY_LOWERCASE_B = 'b',
|
||||
KEY_LOWERCASE_C = 'c',
|
||||
KEY_LOWERCASE_D = 'd',
|
||||
KEY_LOWERCASE_E = 'e',
|
||||
KEY_LOWERCASE_F = 'f',
|
||||
KEY_LOWERCASE_G = 'g',
|
||||
KEY_LOWERCASE_H = 'h',
|
||||
KEY_LOWERCASE_I = 'i',
|
||||
KEY_LOWERCASE_J = 'j',
|
||||
KEY_LOWERCASE_K = 'k',
|
||||
KEY_LOWERCASE_L = 'l',
|
||||
KEY_LOWERCASE_M = 'm',
|
||||
KEY_LOWERCASE_N = 'n',
|
||||
KEY_LOWERCASE_O = 'o',
|
||||
KEY_LOWERCASE_P = 'p',
|
||||
KEY_LOWERCASE_Q = 'q',
|
||||
KEY_LOWERCASE_R = 'r',
|
||||
KEY_LOWERCASE_S = 's',
|
||||
KEY_LOWERCASE_T = 't',
|
||||
KEY_LOWERCASE_U = 'u',
|
||||
KEY_LOWERCASE_V = 'v',
|
||||
KEY_LOWERCASE_W = 'w',
|
||||
KEY_LOWERCASE_X = 'x',
|
||||
KEY_LOWERCASE_Y = 'y',
|
||||
KEY_LOWERCASE_Z = 'z',
|
||||
KEY_BRACE_LEFT = '{',
|
||||
KEY_BAR = '|',
|
||||
KEY_BRACE_RIGHT = '}',
|
||||
KEY_TILDE = '~',
|
||||
KEY_DEL = 127,
|
||||
|
||||
KEY_136 = 136,
|
||||
KEY_146 = 146,
|
||||
KEY_149 = 149,
|
||||
KEY_150 = 150,
|
||||
KEY_151 = 151,
|
||||
KEY_152 = 152,
|
||||
KEY_161 = 161,
|
||||
KEY_163 = 163,
|
||||
KEY_164 = 164,
|
||||
KEY_166 = 166,
|
||||
KEY_168 = 168,
|
||||
KEY_167 = 167,
|
||||
KEY_170 = 170,
|
||||
KEY_172 = 172,
|
||||
KEY_176 = 176,
|
||||
KEY_178 = 178,
|
||||
KEY_179 = 179,
|
||||
KEY_180 = 180,
|
||||
KEY_181 = 181,
|
||||
KEY_186 = 186,
|
||||
KEY_191 = 191,
|
||||
KEY_196 = 196,
|
||||
KEY_199 = 199,
|
||||
KEY_209 = 209,
|
||||
KEY_214 = 214,
|
||||
KEY_215 = 215,
|
||||
KEY_220 = 220,
|
||||
KEY_223 = 223,
|
||||
KEY_224 = 224,
|
||||
KEY_228 = 228,
|
||||
KEY_231 = 231,
|
||||
KEY_232 = 232,
|
||||
KEY_233 = 233,
|
||||
KEY_241 = 241,
|
||||
KEY_246 = 246,
|
||||
KEY_247 = 247,
|
||||
KEY_249 = 249,
|
||||
KEY_252 = 252,
|
||||
|
||||
KEY_ALT_Q = 272,
|
||||
KEY_ALT_W = 273,
|
||||
KEY_ALT_E = 274,
|
||||
KEY_ALT_R = 275,
|
||||
KEY_ALT_T = 276,
|
||||
KEY_ALT_Y = 277,
|
||||
KEY_ALT_U = 278,
|
||||
KEY_ALT_I = 279,
|
||||
KEY_ALT_O = 280,
|
||||
KEY_ALT_P = 281,
|
||||
KEY_ALT_A = 286,
|
||||
KEY_ALT_S = 287,
|
||||
KEY_ALT_D = 288,
|
||||
KEY_ALT_F = 289,
|
||||
KEY_ALT_G = 290,
|
||||
KEY_ALT_H = 291,
|
||||
KEY_ALT_J = 292,
|
||||
KEY_ALT_K = 293,
|
||||
KEY_ALT_L = 294,
|
||||
KEY_ALT_Z = 300,
|
||||
KEY_ALT_X = 301,
|
||||
KEY_ALT_C = 302,
|
||||
KEY_ALT_V = 303,
|
||||
KEY_ALT_B = 304,
|
||||
KEY_ALT_N = 305,
|
||||
KEY_ALT_M = 306,
|
||||
|
||||
KEY_CTRL_Q = 17,
|
||||
KEY_CTRL_W = 23,
|
||||
KEY_CTRL_E = 5,
|
||||
KEY_CTRL_R = 18,
|
||||
KEY_CTRL_T = 20,
|
||||
KEY_CTRL_Y = 25,
|
||||
KEY_CTRL_U = 21,
|
||||
KEY_CTRL_I = 9,
|
||||
KEY_CTRL_O = 15,
|
||||
KEY_CTRL_P = 16,
|
||||
KEY_CTRL_A = 1,
|
||||
KEY_CTRL_S = 19,
|
||||
KEY_CTRL_D = 4,
|
||||
KEY_CTRL_F = 6,
|
||||
KEY_CTRL_G = 7,
|
||||
KEY_CTRL_H = 8,
|
||||
KEY_CTRL_J = 10,
|
||||
KEY_CTRL_K = 11,
|
||||
KEY_CTRL_L = 12,
|
||||
KEY_CTRL_Z = 26,
|
||||
KEY_CTRL_X = 24,
|
||||
KEY_CTRL_C = 3,
|
||||
KEY_CTRL_V = 22,
|
||||
KEY_CTRL_B = 2,
|
||||
KEY_CTRL_N = 14,
|
||||
KEY_CTRL_M = 13,
|
||||
|
||||
KEY_F1 = 315,
|
||||
KEY_F2 = 316,
|
||||
KEY_F3 = 317,
|
||||
KEY_F4 = 318,
|
||||
KEY_F5 = 319,
|
||||
KEY_F6 = 320,
|
||||
KEY_F7 = 321,
|
||||
KEY_F8 = 322,
|
||||
KEY_F9 = 323,
|
||||
KEY_F10 = 324,
|
||||
KEY_F11 = 389,
|
||||
KEY_F12 = 390,
|
||||
|
||||
KEY_SHIFT_F1 = 340,
|
||||
KEY_SHIFT_F2 = 341,
|
||||
KEY_SHIFT_F3 = 342,
|
||||
KEY_SHIFT_F4 = 343,
|
||||
KEY_SHIFT_F5 = 344,
|
||||
KEY_SHIFT_F6 = 345,
|
||||
KEY_SHIFT_F7 = 346,
|
||||
KEY_SHIFT_F8 = 347,
|
||||
KEY_SHIFT_F9 = 348,
|
||||
KEY_SHIFT_F10 = 349,
|
||||
KEY_SHIFT_F11 = 391,
|
||||
KEY_SHIFT_F12 = 392,
|
||||
|
||||
KEY_CTRL_F1 = 350,
|
||||
KEY_CTRL_F2 = 351,
|
||||
KEY_CTRL_F3 = 352,
|
||||
KEY_CTRL_F4 = 353,
|
||||
KEY_CTRL_F5 = 354,
|
||||
KEY_CTRL_F6 = 355,
|
||||
KEY_CTRL_F7 = 356,
|
||||
KEY_CTRL_F8 = 357,
|
||||
KEY_CTRL_F9 = 358,
|
||||
KEY_CTRL_F10 = 359,
|
||||
KEY_CTRL_F11 = 393,
|
||||
KEY_CTRL_F12 = 394,
|
||||
|
||||
KEY_ALT_F1 = 360,
|
||||
KEY_ALT_F2 = 361,
|
||||
KEY_ALT_F3 = 362,
|
||||
KEY_ALT_F4 = 363,
|
||||
KEY_ALT_F5 = 364,
|
||||
KEY_ALT_F6 = 365,
|
||||
KEY_ALT_F7 = 366,
|
||||
KEY_ALT_F8 = 367,
|
||||
KEY_ALT_F9 = 368,
|
||||
KEY_ALT_F10 = 369,
|
||||
KEY_ALT_F11 = 395,
|
||||
KEY_ALT_F12 = 396,
|
||||
|
||||
KEY_HOME = 327,
|
||||
KEY_CTRL_HOME = 375,
|
||||
KEY_ALT_HOME = 407,
|
||||
|
||||
KEY_PAGE_UP = 329,
|
||||
KEY_CTRL_PAGE_UP = 388,
|
||||
KEY_ALT_PAGE_UP = 409,
|
||||
|
||||
KEY_INSERT = 338,
|
||||
KEY_CTRL_INSERT = 402,
|
||||
KEY_ALT_INSERT = 418,
|
||||
|
||||
KEY_DELETE = 339,
|
||||
KEY_CTRL_DELETE = 403,
|
||||
KEY_ALT_DELETE = 419,
|
||||
|
||||
KEY_END = 335,
|
||||
KEY_CTRL_END = 373,
|
||||
KEY_ALT_END = 415,
|
||||
|
||||
KEY_PAGE_DOWN = 337,
|
||||
KEY_ALT_PAGE_DOWN = 417,
|
||||
KEY_CTRL_PAGE_DOWN = 374,
|
||||
|
||||
KEY_ARROW_UP = 328,
|
||||
KEY_CTRL_ARROW_UP = 397,
|
||||
KEY_ALT_ARROW_UP = 408,
|
||||
|
||||
KEY_ARROW_DOWN = 336,
|
||||
KEY_CTRL_ARROW_DOWN = 401,
|
||||
KEY_ALT_ARROW_DOWN = 416,
|
||||
|
||||
KEY_ARROW_LEFT = 331,
|
||||
KEY_CTRL_ARROW_LEFT = 371,
|
||||
KEY_ALT_ARROW_LEFT = 411,
|
||||
|
||||
KEY_ARROW_RIGHT = 333,
|
||||
KEY_CTRL_ARROW_RIGHT = 372,
|
||||
KEY_ALT_ARROW_RIGHT = 413,
|
||||
|
||||
KEY_CTRL_BACKSLASH = 192,
|
||||
|
||||
KEY_NUMBERPAD_5 = 332,
|
||||
KEY_CTRL_NUMBERPAD_5 = 399,
|
||||
KEY_ALT_NUMBERPAD_5 = 9999,
|
||||
|
||||
KEY_FIRST_INPUT_CHARACTER = KEY_SPACE,
|
||||
KEY_LAST_INPUT_CHARACTER = KEY_LOWERCASE_Z,
|
||||
} Key;
|
||||
|
||||
typedef enum KeyboardLayout {
|
||||
KEYBOARD_LAYOUT_QWERTY,
|
||||
KEYBOARD_LAYOUT_FRENCH,
|
||||
KEYBOARD_LAYOUT_GERMAN,
|
||||
KEYBOARD_LAYOUT_ITALIAN,
|
||||
KEYBOARD_LAYOUT_SPANISH,
|
||||
} KeyboardLayout;
|
||||
|
||||
extern unsigned char gPressedPhysicalKeys[SDL_NUM_SCANCODES];
|
||||
extern int gKeyboardLayout;
|
||||
extern unsigned char gPressedPhysicalKeysCount;
|
||||
|
||||
int keyboardInit();
|
||||
void keyboardFree();
|
||||
void keyboardReset();
|
||||
int _kb_getch();
|
||||
void keyboardDisable();
|
||||
void keyboardEnable();
|
||||
int keyboardIsDisabled();
|
||||
void keyboardSetLayout(int new_language);
|
||||
int keyboardGetLayout();
|
||||
void _kb_simulate_key(KeyboardData* data);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* FALLOUT_KB_H_ */
|
||||
+38
-39
@@ -1,6 +1,6 @@
|
||||
#include "light.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include "map_defs.h"
|
||||
#include "object.h"
|
||||
@@ -13,50 +13,55 @@ namespace fallout {
|
||||
#define LIGHT_LEVEL_NIGHT_VISION_BONUS (65536 / 5)
|
||||
|
||||
// 0x51923C
|
||||
static int gLightLevel = LIGHT_LEVEL_MAX;
|
||||
static int gAmbientIntensity = LIGHT_INTENSITY_MAX;
|
||||
|
||||
// light intensity per elevation per tile
|
||||
// 0x59E994
|
||||
static int gLightIntensity[ELEVATION_COUNT][HEX_GRID_SIZE];
|
||||
static int gTileIntensity[ELEVATION_COUNT][HEX_GRID_SIZE];
|
||||
|
||||
// 0x47A8F0
|
||||
int lightInit()
|
||||
{
|
||||
lightResetIntensity();
|
||||
lightResetTileIntensity();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x47A8F8
|
||||
int lightGetLightLevel()
|
||||
// 0x47A8F0
|
||||
void lightReset()
|
||||
{
|
||||
return gLightLevel;
|
||||
lightResetTileIntensity();
|
||||
}
|
||||
|
||||
// 0x47A8F0
|
||||
void lightExit()
|
||||
{
|
||||
lightResetTileIntensity();
|
||||
}
|
||||
|
||||
// 0x47A8F8
|
||||
int lightGetAmbientIntensity()
|
||||
{
|
||||
return gAmbientIntensity;
|
||||
}
|
||||
|
||||
// 0x47A908
|
||||
void lightSetLightLevel(int lightLevel, bool shouldUpdateScreen)
|
||||
void lightSetAmbientIntensity(int intensity, bool shouldUpdateScreen)
|
||||
{
|
||||
int normalizedLightLevel = lightLevel + perkGetRank(gDude, PERK_NIGHT_VISION) * LIGHT_LEVEL_NIGHT_VISION_BONUS;
|
||||
int adjustedIntensity = intensity + perkGetRank(gDude, PERK_NIGHT_VISION) * LIGHT_LEVEL_NIGHT_VISION_BONUS;
|
||||
int normalizedIntensity = std::clamp(adjustedIntensity, LIGHT_INTENSITY_MIN, LIGHT_INTENSITY_MAX);
|
||||
|
||||
if (normalizedLightLevel < LIGHT_LEVEL_MIN) {
|
||||
normalizedLightLevel = LIGHT_LEVEL_MIN;
|
||||
}
|
||||
|
||||
if (normalizedLightLevel > LIGHT_LEVEL_MAX) {
|
||||
normalizedLightLevel = LIGHT_LEVEL_MAX;
|
||||
}
|
||||
|
||||
int oldLightLevel = gLightLevel;
|
||||
gLightLevel = normalizedLightLevel;
|
||||
int oldAmbientIntensity = gAmbientIntensity;
|
||||
gAmbientIntensity = normalizedIntensity;
|
||||
|
||||
if (shouldUpdateScreen) {
|
||||
if (oldLightLevel != normalizedLightLevel) {
|
||||
if (oldAmbientIntensity != normalizedIntensity) {
|
||||
tileWindowRefresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Looks strange - it tries to clamp intensity as light level?
|
||||
int _light_get_tile(int elevation, int tile)
|
||||
// 0x47A980
|
||||
int lightGetTileIntensity(int elevation, int tile)
|
||||
{
|
||||
if (!elevationIsValid(elevation)) {
|
||||
return 0;
|
||||
@@ -66,17 +71,11 @@ int _light_get_tile(int elevation, int tile)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result = gLightIntensity[elevation][tile];
|
||||
|
||||
if (result >= 0x10000) {
|
||||
result = 0x10000;
|
||||
}
|
||||
|
||||
return result;
|
||||
return std::min(gTileIntensity[elevation][tile], LIGHT_INTENSITY_MAX);
|
||||
}
|
||||
|
||||
// 0x47A9C4
|
||||
int lightGetIntensity(int elevation, int tile)
|
||||
int lightGetTrueTileIntensity(int elevation, int tile)
|
||||
{
|
||||
if (!elevationIsValid(elevation)) {
|
||||
return 0;
|
||||
@@ -86,11 +85,11 @@ int lightGetIntensity(int elevation, int tile)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return gLightIntensity[elevation][tile];
|
||||
return gTileIntensity[elevation][tile];
|
||||
}
|
||||
|
||||
// 0x47A9EC
|
||||
void lightSetIntensity(int elevation, int tile, int lightIntensity)
|
||||
void lightSetTileIntensity(int elevation, int tile, int intensity)
|
||||
{
|
||||
if (!elevationIsValid(elevation)) {
|
||||
return;
|
||||
@@ -100,11 +99,11 @@ void lightSetIntensity(int elevation, int tile, int lightIntensity)
|
||||
return;
|
||||
}
|
||||
|
||||
gLightIntensity[elevation][tile] = lightIntensity;
|
||||
gTileIntensity[elevation][tile] = intensity;
|
||||
}
|
||||
|
||||
// 0x47AA10
|
||||
void lightIncreaseIntensity(int elevation, int tile, int lightIntensity)
|
||||
void lightIncreaseTileIntensity(int elevation, int tile, int intensity)
|
||||
{
|
||||
if (!elevationIsValid(elevation)) {
|
||||
return;
|
||||
@@ -114,11 +113,11 @@ void lightIncreaseIntensity(int elevation, int tile, int lightIntensity)
|
||||
return;
|
||||
}
|
||||
|
||||
gLightIntensity[elevation][tile] += lightIntensity;
|
||||
gTileIntensity[elevation][tile] += intensity;
|
||||
}
|
||||
|
||||
// 0x47AA48
|
||||
void lightDecreaseIntensity(int elevation, int tile, int lightIntensity)
|
||||
void lightDecreaseTileIntensity(int elevation, int tile, int intensity)
|
||||
{
|
||||
if (!elevationIsValid(elevation)) {
|
||||
return;
|
||||
@@ -128,15 +127,15 @@ void lightDecreaseIntensity(int elevation, int tile, int lightIntensity)
|
||||
return;
|
||||
}
|
||||
|
||||
gLightIntensity[elevation][tile] -= lightIntensity;
|
||||
gTileIntensity[elevation][tile] -= intensity;
|
||||
}
|
||||
|
||||
// 0x47AA84
|
||||
void lightResetIntensity()
|
||||
void lightResetTileIntensity()
|
||||
{
|
||||
for (int elevation = 0; elevation < ELEVATION_COUNT; elevation++) {
|
||||
for (int tile = 0; tile < HEX_GRID_SIZE; tile++) {
|
||||
gLightIntensity[elevation][tile] = 655;
|
||||
gTileIntensity[elevation][tile] = 655;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-10
@@ -3,20 +3,22 @@
|
||||
|
||||
namespace fallout {
|
||||
|
||||
#define LIGHT_LEVEL_MIN (65536 / 4)
|
||||
#define LIGHT_LEVEL_MAX 65536
|
||||
#define LIGHT_INTENSITY_MIN (65536 / 4)
|
||||
#define LIGHT_INTENSITY_MAX 65536
|
||||
|
||||
typedef void AdjustLightIntensityProc(int elevation, int tile, int intensity);
|
||||
|
||||
int lightInit();
|
||||
int lightGetLightLevel();
|
||||
void lightSetLightLevel(int lightLevel, bool shouldUpdateScreen);
|
||||
int _light_get_tile(int elevation, int tile);
|
||||
int lightGetIntensity(int elevation, int tile);
|
||||
void lightSetIntensity(int elevation, int tile, int intensity);
|
||||
void lightIncreaseIntensity(int elevation, int tile, int intensity);
|
||||
void lightDecreaseIntensity(int elevation, int tile, int intensity);
|
||||
void lightResetIntensity();
|
||||
void lightReset();
|
||||
void lightExit();
|
||||
int lightGetAmbientIntensity();
|
||||
void lightSetAmbientIntensity(int intensity, bool shouldUpdateScreen);
|
||||
int lightGetTileIntensity(int elevation, int tile);
|
||||
int lightGetTrueTileIntensity(int elevation, int tile);
|
||||
void lightSetTileIntensity(int elevation, int tile, int intensity);
|
||||
void lightIncreaseTileIntensity(int elevation, int tile, int intensity);
|
||||
void lightDecreaseTileIntensity(int elevation, int tile, int intensity);
|
||||
void lightResetTileIntensity();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
||||
+5
-4
@@ -4,13 +4,14 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "core.h"
|
||||
#include "db.h"
|
||||
#include "debug.h"
|
||||
#include "game_sound.h"
|
||||
#include "input.h"
|
||||
#include "memory.h"
|
||||
#include "platform_compat.h"
|
||||
#include "sound.h"
|
||||
#include "svga.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
@@ -170,7 +171,7 @@ int lipsStart()
|
||||
int speechVolume = speechGetVolume();
|
||||
soundSetVolume(gLipsData.sound, (int)(speechVolume * 0.69));
|
||||
|
||||
_speechStartTime = _get_time();
|
||||
_speechStartTime = getTicks();
|
||||
|
||||
if (soundPlay(gLipsData.sound) != 0) {
|
||||
debugPrint("Failed play!\n");
|
||||
@@ -405,14 +406,14 @@ static int _lips_make_speech()
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
char* v1 = _lips_fix_string(gLipsData.field_50, sizeof(gLipsData.field_50));
|
||||
sprintf(path, "%s%s\\%s.%s", "SOUND\\SPEECH\\", _lips_subdir_name, v1, "ACM");
|
||||
snprintf(path, sizeof(path), "%s%s\\%s.%s", "SOUND\\SPEECH\\", _lips_subdir_name, v1, "ACM");
|
||||
|
||||
if (gLipsData.sound != NULL) {
|
||||
soundDelete(gLipsData.sound);
|
||||
gLipsData.sound = NULL;
|
||||
}
|
||||
|
||||
gLipsData.sound = soundAllocate(1, 8);
|
||||
gLipsData.sound = soundAllocate(SOUND_TYPE_MEMORY, SOUND_16BIT);
|
||||
if (gLipsData.sound == NULL) {
|
||||
debugPrint("\nsoundAllocate falied in lips_make_speech!");
|
||||
return -1;
|
||||
|
||||
+691
-500
File diff suppressed because it is too large
Load Diff
+52
-434
@@ -1,6 +1,5 @@
|
||||
#include "main.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -8,32 +7,35 @@
|
||||
#include "autorun.h"
|
||||
#include "character_selector.h"
|
||||
#include "color.h"
|
||||
#include "core.h"
|
||||
#include "credits.h"
|
||||
#include "cycle.h"
|
||||
#include "db.h"
|
||||
#include "debug.h"
|
||||
#include "draw.h"
|
||||
#include "endgame.h"
|
||||
#include "fps_limiter.h"
|
||||
#include "game.h"
|
||||
#include "game_config.h"
|
||||
#include "game_mouse.h"
|
||||
#include "game_movie.h"
|
||||
#include "game_sound.h"
|
||||
#include "input.h"
|
||||
#include "kb.h"
|
||||
#include "loadsave.h"
|
||||
#include "mainmenu.h"
|
||||
#include "map.h"
|
||||
#include "mouse.h"
|
||||
#include "object.h"
|
||||
#include "options.h"
|
||||
#include "palette.h"
|
||||
#include "platform_compat.h"
|
||||
#include "preferences.h"
|
||||
#include "proto.h"
|
||||
#include "random.h"
|
||||
#include "scripts.h"
|
||||
#include "selfrun.h"
|
||||
#include "settings.h"
|
||||
#include "sfall_config.h"
|
||||
#include "sfall_global_scripts.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "version.h"
|
||||
#include "window.h"
|
||||
#include "window_manager.h"
|
||||
#include "window_manager_private.h"
|
||||
@@ -42,42 +44,16 @@
|
||||
|
||||
namespace fallout {
|
||||
|
||||
#define MAIN_MENU_WINDOW_WIDTH 640
|
||||
#define MAIN_MENU_WINDOW_HEIGHT 480
|
||||
|
||||
#define DEATH_WINDOW_WIDTH 640
|
||||
#define DEATH_WINDOW_HEIGHT 480
|
||||
|
||||
typedef enum MainMenuButton {
|
||||
MAIN_MENU_BUTTON_INTRO,
|
||||
MAIN_MENU_BUTTON_NEW_GAME,
|
||||
MAIN_MENU_BUTTON_LOAD_GAME,
|
||||
MAIN_MENU_BUTTON_OPTIONS,
|
||||
MAIN_MENU_BUTTON_CREDITS,
|
||||
MAIN_MENU_BUTTON_EXIT,
|
||||
MAIN_MENU_BUTTON_COUNT,
|
||||
} MainMenuButton;
|
||||
|
||||
typedef enum MainMenuOption {
|
||||
MAIN_MENU_INTRO,
|
||||
MAIN_MENU_NEW_GAME,
|
||||
MAIN_MENU_LOAD_GAME,
|
||||
MAIN_MENU_SCREENSAVER,
|
||||
MAIN_MENU_TIMEOUT,
|
||||
MAIN_MENU_CREDITS,
|
||||
MAIN_MENU_QUOTES,
|
||||
MAIN_MENU_EXIT,
|
||||
MAIN_MENU_SELFRUN,
|
||||
MAIN_MENU_OPTIONS,
|
||||
} MainMenuOption;
|
||||
|
||||
static bool falloutInit(int argc, char** argv);
|
||||
static int main_reset_system();
|
||||
static void main_exit_system();
|
||||
static int _main_load_new(char* fname);
|
||||
static int main_loadgame_new();
|
||||
static void main_unload_new();
|
||||
static void mainLoop(FpsLimiter& fpsLimiter);
|
||||
static void mainLoop();
|
||||
static void _main_selfrun_exit();
|
||||
static void _main_selfrun_record();
|
||||
static void _main_selfrun_play();
|
||||
@@ -85,14 +61,6 @@ static void showDeath();
|
||||
static void _main_death_voiceover_callback();
|
||||
static int _mainDeathGrabTextFile(const char* fileName, char* dest);
|
||||
static int _mainDeathWordWrap(char* text, int width, short* beginnings, short* count);
|
||||
static int mainMenuWindowInit();
|
||||
static void mainMenuWindowFree();
|
||||
static void mainMenuWindowHide(bool animate);
|
||||
static void mainMenuWindowUnhide(bool animate);
|
||||
static int _main_menu_is_enabled();
|
||||
static int mainMenuWindowHandleEvents(FpsLimiter& fpsLimiter);
|
||||
static int main_menu_fatal_error();
|
||||
static void main_menu_play_sound(const char* fileName);
|
||||
|
||||
// 0x5194C8
|
||||
static char _mainMap[] = "artemple.map";
|
||||
@@ -122,54 +90,9 @@ static bool _main_show_death_scene = false;
|
||||
// 0x5194EC
|
||||
static bool gMainMenuScreensaverCycle = false;
|
||||
|
||||
// 0x5194F0
|
||||
static int gMainMenuWindow = -1;
|
||||
|
||||
// 0x5194F4
|
||||
static unsigned char* gMainMenuWindowBuffer = NULL;
|
||||
|
||||
// 0x519504
|
||||
static bool _in_main_menu = false;
|
||||
|
||||
// 0x519508
|
||||
static bool gMainMenuWindowInitialized = false;
|
||||
|
||||
// 0x51950C
|
||||
static unsigned int gMainMenuScreensaverDelay = 120000;
|
||||
|
||||
// 0x519510
|
||||
static const int gMainMenuButtonKeyBindings[MAIN_MENU_BUTTON_COUNT] = {
|
||||
KEY_LOWERCASE_I, // intro
|
||||
KEY_LOWERCASE_N, // new game
|
||||
KEY_LOWERCASE_L, // load game
|
||||
KEY_LOWERCASE_O, // options
|
||||
KEY_LOWERCASE_C, // credits
|
||||
KEY_LOWERCASE_E, // exit
|
||||
};
|
||||
|
||||
// 0x519528
|
||||
static const int _return_values[MAIN_MENU_BUTTON_COUNT] = {
|
||||
MAIN_MENU_INTRO,
|
||||
MAIN_MENU_NEW_GAME,
|
||||
MAIN_MENU_LOAD_GAME,
|
||||
MAIN_MENU_OPTIONS,
|
||||
MAIN_MENU_CREDITS,
|
||||
MAIN_MENU_EXIT,
|
||||
};
|
||||
|
||||
// 0x614838
|
||||
static bool _main_death_voiceover_done;
|
||||
|
||||
// 0x614840
|
||||
static int gMainMenuButtons[MAIN_MENU_BUTTON_COUNT];
|
||||
|
||||
// 0x614858
|
||||
static bool gMainMenuWindowHidden;
|
||||
|
||||
static FrmImage _mainMenuBackgroundFrmImage;
|
||||
static FrmImage _mainMenuButtonNormalFrmImage;
|
||||
static FrmImage _mainMenuButtonPressedFrmImage;
|
||||
|
||||
// 0x48099C
|
||||
int falloutMain(int argc, char** argv)
|
||||
{
|
||||
@@ -190,8 +113,6 @@ int falloutMain(int argc, char** argv)
|
||||
gameMoviePlay(MOVIE_CREDITS, 0);
|
||||
}
|
||||
|
||||
FpsLimiter fpsLimiter;
|
||||
|
||||
if (mainMenuWindowInit() == 0) {
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
@@ -200,7 +121,7 @@ int falloutMain(int argc, char** argv)
|
||||
mainMenuWindowUnhide(1);
|
||||
|
||||
mouseShowCursor();
|
||||
int mainMenuRc = mainMenuWindowHandleEvents(fpsLimiter);
|
||||
int mainMenuRc = mainMenuWindowHandleEvents();
|
||||
mouseHideCursor();
|
||||
|
||||
switch (mainMenuRc) {
|
||||
@@ -228,7 +149,10 @@ int falloutMain(int argc, char** argv)
|
||||
_main_load_new(mapNameCopy);
|
||||
free(mapNameCopy);
|
||||
|
||||
mainLoop(fpsLimiter);
|
||||
// SFALL: AfterNewGameStartHook.
|
||||
sfall_gl_scr_exec_start_proc();
|
||||
|
||||
mainLoop();
|
||||
paletteFadeTo(gPaletteWhite);
|
||||
|
||||
// NOTE: Uninline.
|
||||
@@ -248,7 +172,7 @@ int falloutMain(int argc, char** argv)
|
||||
break;
|
||||
case MAIN_MENU_LOAD_GAME:
|
||||
if (1) {
|
||||
int win = windowCreate(0, 0, screenGetWidth(), screenGetHeight(), _colorTable[0], WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
|
||||
int win = windowCreate(0, 0, screenGetWidth(), screenGetHeight(), _colorTable[0], WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
|
||||
mainMenuWindowHide(true);
|
||||
mainMenuWindowFree();
|
||||
|
||||
@@ -263,7 +187,7 @@ int falloutMain(int argc, char** argv)
|
||||
} else if (loadGameRc != 0) {
|
||||
windowDestroy(win);
|
||||
win = -1;
|
||||
mainLoop(fpsLimiter);
|
||||
mainLoop();
|
||||
}
|
||||
paletteFadeTo(gPaletteWhite);
|
||||
if (win != -1) {
|
||||
@@ -290,12 +214,8 @@ int falloutMain(int argc, char** argv)
|
||||
_main_selfrun_play();
|
||||
break;
|
||||
case MAIN_MENU_OPTIONS:
|
||||
mainMenuWindowHide(false);
|
||||
mouseShowCursor();
|
||||
showOptionsWithInitialKeyCode(112);
|
||||
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
|
||||
mouseShowCursor();
|
||||
mainMenuWindowUnhide(0);
|
||||
mainMenuWindowHide(true);
|
||||
doPreferences(true);
|
||||
break;
|
||||
case MAIN_MENU_CREDITS:
|
||||
mainMenuWindowHide(true);
|
||||
@@ -380,7 +300,7 @@ static int _main_load_new(char* mapFileName)
|
||||
objectShow(gDude, NULL);
|
||||
mouseHideCursor();
|
||||
|
||||
int win = windowCreate(0, 0, screenGetWidth(), screenGetHeight(), _colorTable[0], WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
|
||||
int win = windowCreate(0, 0, screenGetWidth(), screenGetHeight(), _colorTable[0], WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
|
||||
windowRefresh(win);
|
||||
|
||||
colorPaletteLoad("color.pal");
|
||||
@@ -426,7 +346,7 @@ static void main_unload_new()
|
||||
}
|
||||
|
||||
// 0x480E48
|
||||
static void mainLoop(FpsLimiter& fpsLimiter)
|
||||
static void mainLoop()
|
||||
{
|
||||
bool cursorWasHidden = cursorIsHidden();
|
||||
if (cursorWasHidden) {
|
||||
@@ -438,9 +358,13 @@ static void mainLoop(FpsLimiter& fpsLimiter)
|
||||
scriptsEnable();
|
||||
|
||||
while (_game_user_wants_to_quit == 0) {
|
||||
fpsLimiter.mark();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
int keyCode = inputGetInput();
|
||||
|
||||
// SFALL: MainLoopHook.
|
||||
sfall_gl_scr_process_main();
|
||||
|
||||
int keyCode = _get_input();
|
||||
gameHandleKey(keyCode, false);
|
||||
|
||||
scriptsHandleRequests();
|
||||
@@ -457,7 +381,8 @@ static void mainLoop(FpsLimiter& fpsLimiter)
|
||||
_game_user_wants_to_quit = 2;
|
||||
}
|
||||
|
||||
fpsLimiter.throttle();
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
scriptsDisable();
|
||||
@@ -595,7 +520,7 @@ static void showDeath()
|
||||
DEATH_WINDOW_WIDTH,
|
||||
DEATH_WINDOW_HEIGHT,
|
||||
0,
|
||||
WINDOW_FLAG_0x04);
|
||||
WINDOW_MOVE_ON_TOP);
|
||||
if (win != -1) {
|
||||
do {
|
||||
unsigned char* windowBuffer = windowGetBuffer(win);
|
||||
@@ -611,7 +536,12 @@ static void showDeath()
|
||||
}
|
||||
|
||||
while (mouseGetEvent() != 0) {
|
||||
_get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
inputGetInput();
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
keyboardReset();
|
||||
@@ -622,9 +552,7 @@ static void showDeath()
|
||||
|
||||
const char* deathFileName = endgameDeathEndingGetFileName();
|
||||
|
||||
int subtitles = 0;
|
||||
configGetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_SUBTITLES_KEY, &subtitles);
|
||||
if (subtitles != 0) {
|
||||
if (settings.preferences.subtitles) {
|
||||
char text[512];
|
||||
if (_mainDeathGrabTextFile(deathFileName, text) == 0) {
|
||||
debugPrint("\n((ShowDeath)): %s\n", text);
|
||||
@@ -662,12 +590,17 @@ static void showDeath()
|
||||
|
||||
// SFALL: Fix the playback of the speech sound file for the death
|
||||
// screen.
|
||||
coreDelay(100);
|
||||
inputBlockForTocks(100);
|
||||
|
||||
unsigned int time = _get_time();
|
||||
unsigned int time = getTicks();
|
||||
int keyCode;
|
||||
do {
|
||||
keyCode = _get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
keyCode = inputGetInput();
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
} while (keyCode == -1 && !_main_death_voiceover_done && getTicksSince(time) < delay);
|
||||
|
||||
speechSetEndCallback(NULL);
|
||||
@@ -675,11 +608,16 @@ static void showDeath()
|
||||
speechDelete();
|
||||
|
||||
while (mouseGetEvent() != 0) {
|
||||
_get_input();
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
inputGetInput();
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
if (keyCode == -1) {
|
||||
coreDelayProcessingEvents(500);
|
||||
inputPauseForTocks(500);
|
||||
}
|
||||
|
||||
paletteFadeTo(gPaletteBlack);
|
||||
@@ -713,14 +651,8 @@ static int _mainDeathGrabTextFile(const char* fileName, char* dest)
|
||||
return -1;
|
||||
}
|
||||
|
||||
char* language = NULL;
|
||||
if (!configGetString(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_LANGUAGE_KEY, &language)) {
|
||||
debugPrint("MAIN: Error grabing language for ending. Defaulting to english.\n");
|
||||
language = _aEnglish_2;
|
||||
}
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
sprintf(path, "text\\%s\\cuts\\%s%s", language, p + 1, ".TXT");
|
||||
snprintf(path, sizeof(path), "text\\%s\\cuts\\%s%s", settings.system.language.c_str(), p + 1, ".TXT");
|
||||
|
||||
File* stream = fileOpen(path, "rt");
|
||||
if (stream == NULL) {
|
||||
@@ -786,318 +718,4 @@ static int _mainDeathWordWrap(char* text, int width, short* beginnings, short* c
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x481650
|
||||
static int mainMenuWindowInit()
|
||||
{
|
||||
int fid;
|
||||
MessageListItem msg;
|
||||
int len;
|
||||
|
||||
if (gMainMenuWindowInitialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
colorPaletteLoad("color.pal");
|
||||
|
||||
int mainMenuWindowX = (screenGetWidth() - MAIN_MENU_WINDOW_WIDTH) / 2;
|
||||
int mainMenuWindowY = (screenGetHeight() - MAIN_MENU_WINDOW_HEIGHT) / 2;
|
||||
gMainMenuWindow = windowCreate(mainMenuWindowX,
|
||||
mainMenuWindowY,
|
||||
MAIN_MENU_WINDOW_WIDTH,
|
||||
MAIN_MENU_WINDOW_HEIGHT,
|
||||
0,
|
||||
WINDOW_HIDDEN | WINDOW_FLAG_0x04);
|
||||
if (gMainMenuWindow == -1) {
|
||||
// NOTE: Uninline.
|
||||
return main_menu_fatal_error();
|
||||
}
|
||||
|
||||
gMainMenuWindowBuffer = windowGetBuffer(gMainMenuWindow);
|
||||
|
||||
// mainmenu.frm
|
||||
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 140, 0, 0, 0);
|
||||
if (!_mainMenuBackgroundFrmImage.lock(backgroundFid)) {
|
||||
// NOTE: Uninline.
|
||||
return main_menu_fatal_error();
|
||||
}
|
||||
|
||||
blitBufferToBuffer(_mainMenuBackgroundFrmImage.getData(), 640, 480, 640, gMainMenuWindowBuffer, 640);
|
||||
_mainMenuBackgroundFrmImage.unlock();
|
||||
|
||||
int oldFont = fontGetCurrent();
|
||||
fontSetCurrent(100);
|
||||
|
||||
// SFALL: Allow to change font color/flags of copyright/version text
|
||||
// It's the last byte ('3C' by default) that picks the colour used. The first byte supplies additional flags for this option
|
||||
// 0x010000 - change the color for version string only
|
||||
// 0x020000 - underline text (only for the version string)
|
||||
// 0x040000 - monospace font (only for the version string)
|
||||
int fontSettings = _colorTable[21091], fontSettingsSFall = 0;
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_FONT_COLOR_KEY, &fontSettingsSFall);
|
||||
if (fontSettingsSFall && !(fontSettingsSFall & 0x010000))
|
||||
fontSettings = fontSettingsSFall & 0xFF;
|
||||
|
||||
// SFALL: Allow to move copyright text
|
||||
int offsetX = 0, offsetY = 0;
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_X_KEY, &offsetX);
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_Y_KEY, &offsetY);
|
||||
|
||||
// Copyright.
|
||||
msg.num = 20;
|
||||
if (messageListGetItem(&gMiscMessageList, &msg)) {
|
||||
windowDrawText(gMainMenuWindow, msg.text, 0, offsetX + 15, offsetY + 460, fontSettings | 0x06000000);
|
||||
}
|
||||
|
||||
// SFALL: Make sure font settings are applied when using 0x010000 flag
|
||||
if (fontSettingsSFall)
|
||||
fontSettings = fontSettingsSFall;
|
||||
|
||||
// TODO: Allow to move version text
|
||||
// Version.
|
||||
char version[VERSION_MAX];
|
||||
versionGetVersion(version);
|
||||
len = fontGetStringWidth(version);
|
||||
windowDrawText(gMainMenuWindow, version, 0, 615 - len, 460, fontSettings | 0x06000000);
|
||||
|
||||
// menuup.frm
|
||||
fid = buildFid(OBJ_TYPE_INTERFACE, 299, 0, 0, 0);
|
||||
if (!_mainMenuButtonNormalFrmImage.lock(fid)) {
|
||||
// NOTE: Uninline.
|
||||
return main_menu_fatal_error();
|
||||
}
|
||||
|
||||
// menudown.frm
|
||||
fid = buildFid(OBJ_TYPE_INTERFACE, 300, 0, 0, 0);
|
||||
if (!_mainMenuButtonPressedFrmImage.lock(fid)) {
|
||||
// NOTE: Uninline.
|
||||
return main_menu_fatal_error();
|
||||
}
|
||||
|
||||
for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) {
|
||||
gMainMenuButtons[index] = -1;
|
||||
}
|
||||
|
||||
// SFALL: Allow to move menu buttons
|
||||
offsetX = offsetY = 0;
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_OFFSET_X_KEY, &offsetX);
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_OFFSET_Y_KEY, &offsetY);
|
||||
|
||||
for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) {
|
||||
gMainMenuButtons[index] = buttonCreate(gMainMenuWindow,
|
||||
offsetX + 30,
|
||||
offsetY + 19 + index * 42 - index,
|
||||
26,
|
||||
26,
|
||||
-1,
|
||||
-1,
|
||||
1111,
|
||||
gMainMenuButtonKeyBindings[index],
|
||||
_mainMenuButtonNormalFrmImage.getData(),
|
||||
_mainMenuButtonPressedFrmImage.getData(),
|
||||
0,
|
||||
BUTTON_FLAG_TRANSPARENT);
|
||||
if (gMainMenuButtons[index] == -1) {
|
||||
// NOTE: Uninline.
|
||||
return main_menu_fatal_error();
|
||||
}
|
||||
|
||||
buttonSetMask(gMainMenuButtons[index], _mainMenuButtonNormalFrmImage.getData());
|
||||
}
|
||||
|
||||
fontSetCurrent(104);
|
||||
|
||||
// SFALL: Allow to change font color of buttons
|
||||
fontSettings = _colorTable[21091];
|
||||
fontSettingsSFall = 0;
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_BIG_FONT_COLOR_KEY, &fontSettingsSFall);
|
||||
if (fontSettingsSFall)
|
||||
fontSettings = fontSettingsSFall & 0xFF;
|
||||
|
||||
for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) {
|
||||
msg.num = 9 + index;
|
||||
if (messageListGetItem(&gMiscMessageList, &msg)) {
|
||||
len = fontGetStringWidth(msg.text);
|
||||
fontDrawText(gMainMenuWindowBuffer + offsetX + 640 * (offsetY + 42 * index - index + 20) + 126 - (len / 2), msg.text, 640 - (126 - (len / 2)) - 1, 640, fontSettings);
|
||||
}
|
||||
}
|
||||
|
||||
fontSetCurrent(oldFont);
|
||||
|
||||
gMainMenuWindowInitialized = true;
|
||||
gMainMenuWindowHidden = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x481968
|
||||
static void mainMenuWindowFree()
|
||||
{
|
||||
if (!gMainMenuWindowInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) {
|
||||
// FIXME: Why it tries to free only invalid buttons?
|
||||
if (gMainMenuButtons[index] == -1) {
|
||||
buttonDestroy(gMainMenuButtons[index]);
|
||||
}
|
||||
}
|
||||
|
||||
_mainMenuButtonPressedFrmImage.unlock();
|
||||
_mainMenuButtonNormalFrmImage.unlock();
|
||||
|
||||
if (gMainMenuWindow != -1) {
|
||||
windowDestroy(gMainMenuWindow);
|
||||
}
|
||||
|
||||
gMainMenuWindowInitialized = false;
|
||||
}
|
||||
|
||||
// 0x481A00
|
||||
static void mainMenuWindowHide(bool animate)
|
||||
{
|
||||
if (!gMainMenuWindowInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gMainMenuWindowHidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
soundContinueAll();
|
||||
|
||||
if (animate) {
|
||||
paletteFadeTo(gPaletteBlack);
|
||||
soundContinueAll();
|
||||
}
|
||||
|
||||
windowHide(gMainMenuWindow);
|
||||
|
||||
gMainMenuWindowHidden = true;
|
||||
}
|
||||
|
||||
// 0x481A48
|
||||
static void mainMenuWindowUnhide(bool animate)
|
||||
{
|
||||
if (!gMainMenuWindowInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gMainMenuWindowHidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
windowUnhide(gMainMenuWindow);
|
||||
|
||||
if (animate) {
|
||||
colorPaletteLoad("color.pal");
|
||||
paletteFadeTo(_cmap);
|
||||
}
|
||||
|
||||
gMainMenuWindowHidden = false;
|
||||
}
|
||||
|
||||
// 0x481AA8
|
||||
static int _main_menu_is_enabled()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 0x481AEC
|
||||
static int mainMenuWindowHandleEvents(FpsLimiter& fpsLimiter)
|
||||
{
|
||||
_in_main_menu = true;
|
||||
|
||||
bool oldCursorIsHidden = cursorIsHidden();
|
||||
if (oldCursorIsHidden) {
|
||||
mouseShowCursor();
|
||||
}
|
||||
|
||||
unsigned int tick = _get_time();
|
||||
|
||||
int rc = -1;
|
||||
while (rc == -1) {
|
||||
fpsLimiter.mark();
|
||||
|
||||
int keyCode = _get_input();
|
||||
|
||||
for (int buttonIndex = 0; buttonIndex < MAIN_MENU_BUTTON_COUNT; buttonIndex++) {
|
||||
if (keyCode == gMainMenuButtonKeyBindings[buttonIndex] || keyCode == toupper(gMainMenuButtonKeyBindings[buttonIndex])) {
|
||||
// NOTE: Uninline.
|
||||
main_menu_play_sound("nmselec1");
|
||||
|
||||
rc = _return_values[buttonIndex];
|
||||
|
||||
if (buttonIndex == MAIN_MENU_BUTTON_CREDITS && (gPressedPhysicalKeys[SDL_SCANCODE_RSHIFT] != KEY_STATE_UP || gPressedPhysicalKeys[SDL_SCANCODE_LSHIFT] != KEY_STATE_UP)) {
|
||||
rc = MAIN_MENU_QUOTES;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == -1) {
|
||||
if (keyCode == KEY_CTRL_R) {
|
||||
rc = MAIN_MENU_SELFRUN;
|
||||
continue;
|
||||
} else if (keyCode == KEY_PLUS || keyCode == KEY_EQUAL) {
|
||||
brightnessIncrease();
|
||||
} else if (keyCode == KEY_MINUS || keyCode == KEY_UNDERSCORE) {
|
||||
brightnessDecrease();
|
||||
} else if (keyCode == KEY_UPPERCASE_D || keyCode == KEY_LOWERCASE_D) {
|
||||
rc = MAIN_MENU_SCREENSAVER;
|
||||
continue;
|
||||
} else if (keyCode == 1111) {
|
||||
if (!(mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_REPEAT)) {
|
||||
// NOTE: Uninline.
|
||||
main_menu_play_sound("nmselec0");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyCode == KEY_ESCAPE || _game_user_wants_to_quit == 3) {
|
||||
rc = MAIN_MENU_EXIT;
|
||||
|
||||
// NOTE: Uninline.
|
||||
main_menu_play_sound("nmselec1");
|
||||
break;
|
||||
} else if (_game_user_wants_to_quit == 2) {
|
||||
_game_user_wants_to_quit = 0;
|
||||
} else {
|
||||
if (getTicksSince(tick) >= gMainMenuScreensaverDelay) {
|
||||
rc = MAIN_MENU_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
fpsLimiter.throttle();
|
||||
}
|
||||
|
||||
if (oldCursorIsHidden) {
|
||||
mouseHideCursor();
|
||||
}
|
||||
|
||||
_in_main_menu = false;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
// NOTE: Inlined.
|
||||
//
|
||||
// 0x481C88
|
||||
static int main_menu_fatal_error()
|
||||
{
|
||||
mainMenuWindowFree();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// NOTE: Inlined.
|
||||
//
|
||||
// 0x481C94
|
||||
static void main_menu_play_sound(const char* fileName)
|
||||
{
|
||||
soundPlayFile(fileName);
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
+399
@@ -0,0 +1,399 @@
|
||||
#include "mainmenu.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "art.h"
|
||||
#include "color.h"
|
||||
#include "draw.h"
|
||||
#include "game.h"
|
||||
#include "game_sound.h"
|
||||
#include "input.h"
|
||||
#include "kb.h"
|
||||
#include "mouse.h"
|
||||
#include "palette.h"
|
||||
#include "preferences.h"
|
||||
#include "sfall_config.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "version.h"
|
||||
#include "window_manager.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
#define MAIN_MENU_WINDOW_WIDTH 640
|
||||
#define MAIN_MENU_WINDOW_HEIGHT 480
|
||||
|
||||
typedef enum MainMenuButton {
|
||||
MAIN_MENU_BUTTON_INTRO,
|
||||
MAIN_MENU_BUTTON_NEW_GAME,
|
||||
MAIN_MENU_BUTTON_LOAD_GAME,
|
||||
MAIN_MENU_BUTTON_OPTIONS,
|
||||
MAIN_MENU_BUTTON_CREDITS,
|
||||
MAIN_MENU_BUTTON_EXIT,
|
||||
MAIN_MENU_BUTTON_COUNT,
|
||||
} MainMenuButton;
|
||||
|
||||
static int main_menu_fatal_error();
|
||||
static void main_menu_play_sound(const char* fileName);
|
||||
|
||||
// 0x5194F0
|
||||
static int gMainMenuWindow = -1;
|
||||
|
||||
// 0x5194F4
|
||||
static unsigned char* gMainMenuWindowBuffer = NULL;
|
||||
|
||||
// 0x519504
|
||||
static bool _in_main_menu = false;
|
||||
|
||||
// 0x519508
|
||||
static bool gMainMenuWindowInitialized = false;
|
||||
|
||||
// 0x51950C
|
||||
static unsigned int gMainMenuScreensaverDelay = 120000;
|
||||
|
||||
// 0x519510
|
||||
static const int gMainMenuButtonKeyBindings[MAIN_MENU_BUTTON_COUNT] = {
|
||||
KEY_LOWERCASE_I, // intro
|
||||
KEY_LOWERCASE_N, // new game
|
||||
KEY_LOWERCASE_L, // load game
|
||||
KEY_LOWERCASE_O, // options
|
||||
KEY_LOWERCASE_C, // credits
|
||||
KEY_LOWERCASE_E, // exit
|
||||
};
|
||||
|
||||
// 0x519528
|
||||
static const int _return_values[MAIN_MENU_BUTTON_COUNT] = {
|
||||
MAIN_MENU_INTRO,
|
||||
MAIN_MENU_NEW_GAME,
|
||||
MAIN_MENU_LOAD_GAME,
|
||||
MAIN_MENU_OPTIONS,
|
||||
MAIN_MENU_CREDITS,
|
||||
MAIN_MENU_EXIT,
|
||||
};
|
||||
|
||||
// 0x614840
|
||||
static int gMainMenuButtons[MAIN_MENU_BUTTON_COUNT];
|
||||
|
||||
// 0x614858
|
||||
static bool gMainMenuWindowHidden;
|
||||
|
||||
static FrmImage _mainMenuBackgroundFrmImage;
|
||||
static FrmImage _mainMenuButtonNormalFrmImage;
|
||||
static FrmImage _mainMenuButtonPressedFrmImage;
|
||||
|
||||
// 0x481650
|
||||
int mainMenuWindowInit()
|
||||
{
|
||||
int fid;
|
||||
MessageListItem msg;
|
||||
int len;
|
||||
|
||||
if (gMainMenuWindowInitialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
colorPaletteLoad("color.pal");
|
||||
|
||||
int mainMenuWindowX = (screenGetWidth() - MAIN_MENU_WINDOW_WIDTH) / 2;
|
||||
int mainMenuWindowY = (screenGetHeight() - MAIN_MENU_WINDOW_HEIGHT) / 2;
|
||||
gMainMenuWindow = windowCreate(mainMenuWindowX,
|
||||
mainMenuWindowY,
|
||||
MAIN_MENU_WINDOW_WIDTH,
|
||||
MAIN_MENU_WINDOW_HEIGHT,
|
||||
0,
|
||||
WINDOW_HIDDEN | WINDOW_MOVE_ON_TOP);
|
||||
if (gMainMenuWindow == -1) {
|
||||
// NOTE: Uninline.
|
||||
return main_menu_fatal_error();
|
||||
}
|
||||
|
||||
gMainMenuWindowBuffer = windowGetBuffer(gMainMenuWindow);
|
||||
|
||||
// mainmenu.frm
|
||||
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 140, 0, 0, 0);
|
||||
if (!_mainMenuBackgroundFrmImage.lock(backgroundFid)) {
|
||||
// NOTE: Uninline.
|
||||
return main_menu_fatal_error();
|
||||
}
|
||||
|
||||
blitBufferToBuffer(_mainMenuBackgroundFrmImage.getData(), 640, 480, 640, gMainMenuWindowBuffer, 640);
|
||||
_mainMenuBackgroundFrmImage.unlock();
|
||||
|
||||
int oldFont = fontGetCurrent();
|
||||
fontSetCurrent(100);
|
||||
|
||||
// SFALL: Allow to change font color/flags of copyright/version text
|
||||
// It's the last byte ('3C' by default) that picks the colour used. The first byte supplies additional flags for this option
|
||||
// 0x010000 - change the color for version string only
|
||||
// 0x020000 - underline text (only for the version string)
|
||||
// 0x040000 - monospace font (only for the version string)
|
||||
int fontSettings = _colorTable[21091], fontSettingsSFall = 0;
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_FONT_COLOR_KEY, &fontSettingsSFall);
|
||||
if (fontSettingsSFall && !(fontSettingsSFall & 0x010000))
|
||||
fontSettings = fontSettingsSFall & 0xFF;
|
||||
|
||||
// SFALL: Allow to move copyright text
|
||||
int offsetX = 0, offsetY = 0;
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_X_KEY, &offsetX);
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_Y_KEY, &offsetY);
|
||||
|
||||
// Copyright.
|
||||
msg.num = 20;
|
||||
if (messageListGetItem(&gMiscMessageList, &msg)) {
|
||||
windowDrawText(gMainMenuWindow, msg.text, 0, offsetX + 15, offsetY + 460, fontSettings | 0x06000000);
|
||||
}
|
||||
|
||||
// SFALL: Make sure font settings are applied when using 0x010000 flag
|
||||
if (fontSettingsSFall)
|
||||
fontSettings = fontSettingsSFall;
|
||||
|
||||
// TODO: Allow to move version text
|
||||
// Version.
|
||||
char version[VERSION_MAX];
|
||||
versionGetVersion(version, sizeof(version));
|
||||
len = fontGetStringWidth(version);
|
||||
windowDrawText(gMainMenuWindow, version, 0, 615 - len, 460, fontSettings | 0x06000000);
|
||||
|
||||
// menuup.frm
|
||||
fid = buildFid(OBJ_TYPE_INTERFACE, 299, 0, 0, 0);
|
||||
if (!_mainMenuButtonNormalFrmImage.lock(fid)) {
|
||||
// NOTE: Uninline.
|
||||
return main_menu_fatal_error();
|
||||
}
|
||||
|
||||
// menudown.frm
|
||||
fid = buildFid(OBJ_TYPE_INTERFACE, 300, 0, 0, 0);
|
||||
if (!_mainMenuButtonPressedFrmImage.lock(fid)) {
|
||||
// NOTE: Uninline.
|
||||
return main_menu_fatal_error();
|
||||
}
|
||||
|
||||
for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) {
|
||||
gMainMenuButtons[index] = -1;
|
||||
}
|
||||
|
||||
// SFALL: Allow to move menu buttons
|
||||
offsetX = offsetY = 0;
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_OFFSET_X_KEY, &offsetX);
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_OFFSET_Y_KEY, &offsetY);
|
||||
|
||||
for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) {
|
||||
gMainMenuButtons[index] = buttonCreate(gMainMenuWindow,
|
||||
offsetX + 30,
|
||||
offsetY + 19 + index * 42 - index,
|
||||
26,
|
||||
26,
|
||||
-1,
|
||||
-1,
|
||||
1111,
|
||||
gMainMenuButtonKeyBindings[index],
|
||||
_mainMenuButtonNormalFrmImage.getData(),
|
||||
_mainMenuButtonPressedFrmImage.getData(),
|
||||
0,
|
||||
BUTTON_FLAG_TRANSPARENT);
|
||||
if (gMainMenuButtons[index] == -1) {
|
||||
// NOTE: Uninline.
|
||||
return main_menu_fatal_error();
|
||||
}
|
||||
|
||||
buttonSetMask(gMainMenuButtons[index], _mainMenuButtonNormalFrmImage.getData());
|
||||
}
|
||||
|
||||
fontSetCurrent(104);
|
||||
|
||||
// SFALL: Allow to change font color of buttons
|
||||
fontSettings = _colorTable[21091];
|
||||
fontSettingsSFall = 0;
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_BIG_FONT_COLOR_KEY, &fontSettingsSFall);
|
||||
if (fontSettingsSFall)
|
||||
fontSettings = fontSettingsSFall & 0xFF;
|
||||
|
||||
for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) {
|
||||
msg.num = 9 + index;
|
||||
if (messageListGetItem(&gMiscMessageList, &msg)) {
|
||||
len = fontGetStringWidth(msg.text);
|
||||
fontDrawText(gMainMenuWindowBuffer + offsetX + 640 * (offsetY + 42 * index - index + 20) + 126 - (len / 2), msg.text, 640 - (126 - (len / 2)) - 1, 640, fontSettings);
|
||||
}
|
||||
}
|
||||
|
||||
fontSetCurrent(oldFont);
|
||||
|
||||
gMainMenuWindowInitialized = true;
|
||||
gMainMenuWindowHidden = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x481968
|
||||
void mainMenuWindowFree()
|
||||
{
|
||||
if (!gMainMenuWindowInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) {
|
||||
// FIXME: Why it tries to free only invalid buttons?
|
||||
if (gMainMenuButtons[index] == -1) {
|
||||
buttonDestroy(gMainMenuButtons[index]);
|
||||
}
|
||||
}
|
||||
|
||||
_mainMenuButtonPressedFrmImage.unlock();
|
||||
_mainMenuButtonNormalFrmImage.unlock();
|
||||
|
||||
if (gMainMenuWindow != -1) {
|
||||
windowDestroy(gMainMenuWindow);
|
||||
}
|
||||
|
||||
gMainMenuWindowInitialized = false;
|
||||
}
|
||||
|
||||
// 0x481A00
|
||||
void mainMenuWindowHide(bool animate)
|
||||
{
|
||||
if (!gMainMenuWindowInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gMainMenuWindowHidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
soundContinueAll();
|
||||
|
||||
if (animate) {
|
||||
paletteFadeTo(gPaletteBlack);
|
||||
soundContinueAll();
|
||||
}
|
||||
|
||||
windowHide(gMainMenuWindow);
|
||||
|
||||
gMainMenuWindowHidden = true;
|
||||
}
|
||||
|
||||
// 0x481A48
|
||||
void mainMenuWindowUnhide(bool animate)
|
||||
{
|
||||
if (!gMainMenuWindowInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gMainMenuWindowHidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
windowShow(gMainMenuWindow);
|
||||
|
||||
if (animate) {
|
||||
colorPaletteLoad("color.pal");
|
||||
paletteFadeTo(_cmap);
|
||||
}
|
||||
|
||||
gMainMenuWindowHidden = false;
|
||||
}
|
||||
|
||||
// 0x481AA8
|
||||
int _main_menu_is_enabled()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 0x481AEC
|
||||
int mainMenuWindowHandleEvents()
|
||||
{
|
||||
_in_main_menu = true;
|
||||
|
||||
bool oldCursorIsHidden = cursorIsHidden();
|
||||
if (oldCursorIsHidden) {
|
||||
mouseShowCursor();
|
||||
}
|
||||
|
||||
unsigned int tick = getTicks();
|
||||
|
||||
int rc = -1;
|
||||
while (rc == -1) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
int keyCode = inputGetInput();
|
||||
|
||||
for (int buttonIndex = 0; buttonIndex < MAIN_MENU_BUTTON_COUNT; buttonIndex++) {
|
||||
if (keyCode == gMainMenuButtonKeyBindings[buttonIndex] || keyCode == toupper(gMainMenuButtonKeyBindings[buttonIndex])) {
|
||||
// NOTE: Uninline.
|
||||
main_menu_play_sound("nmselec1");
|
||||
|
||||
rc = _return_values[buttonIndex];
|
||||
|
||||
if (buttonIndex == MAIN_MENU_BUTTON_CREDITS && (gPressedPhysicalKeys[SDL_SCANCODE_RSHIFT] != KEY_STATE_UP || gPressedPhysicalKeys[SDL_SCANCODE_LSHIFT] != KEY_STATE_UP)) {
|
||||
rc = MAIN_MENU_QUOTES;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == -1) {
|
||||
if (keyCode == KEY_CTRL_R) {
|
||||
rc = MAIN_MENU_SELFRUN;
|
||||
continue;
|
||||
} else if (keyCode == KEY_PLUS || keyCode == KEY_EQUAL) {
|
||||
brightnessIncrease();
|
||||
} else if (keyCode == KEY_MINUS || keyCode == KEY_UNDERSCORE) {
|
||||
brightnessDecrease();
|
||||
} else if (keyCode == KEY_UPPERCASE_D || keyCode == KEY_LOWERCASE_D) {
|
||||
rc = MAIN_MENU_SCREENSAVER;
|
||||
continue;
|
||||
} else if (keyCode == 1111) {
|
||||
if (!(mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_REPEAT)) {
|
||||
// NOTE: Uninline.
|
||||
main_menu_play_sound("nmselec0");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyCode == KEY_ESCAPE || _game_user_wants_to_quit == 3) {
|
||||
rc = MAIN_MENU_EXIT;
|
||||
|
||||
// NOTE: Uninline.
|
||||
main_menu_play_sound("nmselec1");
|
||||
break;
|
||||
} else if (_game_user_wants_to_quit == 2) {
|
||||
_game_user_wants_to_quit = 0;
|
||||
} else {
|
||||
if (getTicksSince(tick) >= gMainMenuScreensaverDelay) {
|
||||
rc = MAIN_MENU_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
if (oldCursorIsHidden) {
|
||||
mouseHideCursor();
|
||||
}
|
||||
|
||||
_in_main_menu = false;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
// NOTE: Inlined.
|
||||
//
|
||||
// 0x481C88
|
||||
static int main_menu_fatal_error()
|
||||
{
|
||||
mainMenuWindowFree();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// NOTE: Inlined.
|
||||
//
|
||||
// 0x481C94
|
||||
static void main_menu_play_sound(const char* fileName)
|
||||
{
|
||||
soundPlayFile(fileName);
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef FALLOUT_MAINMENU_H_
|
||||
#define FALLOUT_MAINMENU_H_
|
||||
|
||||
namespace fallout {
|
||||
|
||||
typedef enum MainMenuOption {
|
||||
MAIN_MENU_INTRO,
|
||||
MAIN_MENU_NEW_GAME,
|
||||
MAIN_MENU_LOAD_GAME,
|
||||
MAIN_MENU_SCREENSAVER,
|
||||
MAIN_MENU_TIMEOUT,
|
||||
MAIN_MENU_CREDITS,
|
||||
MAIN_MENU_QUOTES,
|
||||
MAIN_MENU_EXIT,
|
||||
MAIN_MENU_SELFRUN,
|
||||
MAIN_MENU_OPTIONS,
|
||||
} MainMenuOption;
|
||||
|
||||
int mainMenuWindowInit();
|
||||
void mainMenuWindowFree();
|
||||
void mainMenuWindowHide(bool animate);
|
||||
void mainMenuWindowUnhide(bool animate);
|
||||
int _main_menu_is_enabled();
|
||||
int mainMenuWindowHandleEvents();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* FALLOUT_MAINMENU_H_ */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user