Compare commits
73 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 |
@@ -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
|
||||
@@ -78,7 +90,7 @@ jobs:
|
||||
ios:
|
||||
name: iOS
|
||||
|
||||
runs-on: macos-11
|
||||
runs-on: macos-12
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
@@ -88,30 +100,31 @@ jobs:
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: build
|
||||
key: ios-cmake-v1
|
||||
key: ios-cmake-v2
|
||||
|
||||
- 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 \
|
||||
-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
|
||||
|
||||
# TODO: Should be a part of packaging.
|
||||
- name: Prepare for uploading
|
||||
- name: Pack
|
||||
run: |
|
||||
cp build/fallout2-ce.zip build/fallout2-ce.ipa
|
||||
cd build
|
||||
cpack -C RelWithDebInfo
|
||||
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
@@ -200,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:
|
||||
|
||||
+57
-29
@@ -4,7 +4,7 @@ set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
||||
|
||||
set(EXECUTABLE_NAME fallout2-ce)
|
||||
|
||||
if (APPLE)
|
||||
if(APPLE)
|
||||
if(IOS)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "11" CACHE STRING "")
|
||||
set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE STRING "")
|
||||
@@ -20,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)
|
||||
@@ -140,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"
|
||||
@@ -150,8 +152,6 @@ 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"
|
||||
@@ -259,16 +259,28 @@ 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)
|
||||
@@ -282,11 +294,8 @@ 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()
|
||||
|
||||
@@ -296,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"
|
||||
@@ -304,22 +313,45 @@ 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)
|
||||
target_sources(${EXECUTABLE_NAME} PUBLIC "os/ios/LaunchScreen.storyboard")
|
||||
set_source_files_properties("os/ios/LaunchScreen.storyboard" PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
|
||||
set_target_properties(${EXECUTABLE_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/os/ios/Info.plist")
|
||||
set_target_properties(${EXECUTABLE_NAME} PROPERTIES XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2")
|
||||
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_target_properties(${EXECUTABLE_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/os/macos/Info.plist")
|
||||
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(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.2.0")
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "1.2.0")
|
||||
endif()
|
||||
@@ -349,24 +381,20 @@ if(APPLE)
|
||||
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 .)
|
||||
install(CODE "
|
||||
include(BundleUtilities)
|
||||
fixup_bundle(${CMAKE_BINARY_DIR}/${MACOSX_BUNDLE_BUNDLE_NAME}.app \"\" \"\")
|
||||
"
|
||||
COMPONENT Runtime)
|
||||
|
||||
if (CPACK_BUNDLE_APPLE_CERT_APP)
|
||||
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)
|
||||
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 "fallout2-ce")
|
||||
set(CPACK_PACKAGE_FILE_NAME "Fallout II Community Edition")
|
||||
endif()
|
||||
|
||||
include(CPack)
|
||||
|
||||
@@ -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,19 @@ $ 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
|
||||
|
||||
@@ -52,7 +64,31 @@ $ sudo apt install libsdl2-2.0-0
|
||||
|
||||
- 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)).
|
||||
- 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
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,6 @@
|
||||
<string>${MACOSX_BUNDLE_DISPLAY_NAME}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
@@ -22,8 +20,6 @@
|
||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.role-playing-games</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>11.0</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
@@ -49,8 +45,6 @@
|
||||
<array>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
+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>
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+40
-1
@@ -477,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) {
|
||||
@@ -1076,9 +1076,48 @@ static int artReadHeader(Art* art, File* stream)
|
||||
if (fileReadInt32List(stream, art->dataOffsets, ROTATION_COUNT) == -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)
|
||||
{
|
||||
|
||||
@@ -147,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);
|
||||
|
||||
|
||||
+13
-33
@@ -7,7 +7,6 @@
|
||||
#include "db.h"
|
||||
#include "debug.h"
|
||||
#include "memory_manager.h"
|
||||
#include "pointer_registry.h"
|
||||
#include "sound.h"
|
||||
#include "sound_decoder.h"
|
||||
|
||||
@@ -20,7 +19,7 @@ typedef enum AudioFlags {
|
||||
|
||||
typedef struct Audio {
|
||||
int flags;
|
||||
int stream;
|
||||
File* stream;
|
||||
SoundDecoder* soundDecoder;
|
||||
int fileSize;
|
||||
int sampleRate;
|
||||
@@ -29,7 +28,7 @@ typedef struct Audio {
|
||||
} Audio;
|
||||
|
||||
static bool defaultCompressionFunc(char* filePath);
|
||||
static int audioSoundDecoderReadHandler(int fileHandle, void* buf, unsigned int size);
|
||||
static int audioSoundDecoderReadHandler(void* data, void* buf, unsigned int size);
|
||||
|
||||
// 0x5108BC
|
||||
static AudioQueryCompressedFunc* queryCompressedFunc = defaultCompressionFunc;
|
||||
@@ -52,14 +51,14 @@ static bool defaultCompressionFunc(char* filePath)
|
||||
}
|
||||
|
||||
// 0x41A2D0
|
||||
static int audioSoundDecoderReadHandler(int handle, void* buffer, unsigned int size)
|
||||
static int audioSoundDecoderReadHandler(void* data, void* buffer, unsigned int size)
|
||||
{
|
||||
return fileRead(buffer, 1, size, (File*)intToPtr(handle));
|
||||
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];
|
||||
snprintf(path, sizeof(path), "%s", fname);
|
||||
@@ -71,28 +70,7 @@ int audioOpen(const char* fname, int flags, ...)
|
||||
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;
|
||||
@@ -116,12 +94,14 @@ int audioOpen(const char* fname, int flags, ...)
|
||||
|
||||
Audio* audioFile = &(gAudioList[index]);
|
||||
audioFile->flags = AUDIO_IN_USE;
|
||||
audioFile->stream = ptrToInt(stream);
|
||||
audioFile->stream = stream;
|
||||
|
||||
if (compression == 2) {
|
||||
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);
|
||||
}
|
||||
@@ -135,7 +115,7 @@ int audioOpen(const char* fname, int flags, ...)
|
||||
int audioClose(int handle)
|
||||
{
|
||||
Audio* audioFile = &(gAudioList[handle - 1]);
|
||||
fileClose((File*)intToPtr(audioFile->stream, true));
|
||||
fileClose(audioFile->stream);
|
||||
|
||||
if ((audioFile->flags & AUDIO_COMPRESSED) != 0) {
|
||||
soundDecoderFree(audioFile->soundDecoder);
|
||||
@@ -155,7 +135,7 @@ int audioRead(int handle, void* buffer, unsigned int size)
|
||||
if ((audioFile->flags & AUDIO_COMPRESSED) != 0) {
|
||||
bytesRead = soundDecoderDecode(audioFile->soundDecoder, buffer, size);
|
||||
} else {
|
||||
bytesRead = fileRead(buffer, 1, size, (File*)intToPtr(audioFile->stream));
|
||||
bytesRead = fileRead(buffer, 1, size, audioFile->stream);
|
||||
}
|
||||
|
||||
audioFile->position += bytesRead;
|
||||
@@ -189,7 +169,7 @@ long audioSeek(int handle, long offset, int origin)
|
||||
if ((audioFile->flags & AUDIO_COMPRESSED) != 0) {
|
||||
if (pos < audioFile->position) {
|
||||
soundDecoderFree(audioFile->soundDecoder);
|
||||
fileSeek((File*)intToPtr(audioFile->stream), 0, SEEK_SET);
|
||||
fileSeek(audioFile->stream, 0, SEEK_SET);
|
||||
audioFile->soundDecoder = soundDecoderInit(audioSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize));
|
||||
audioFile->position = 0;
|
||||
audioFile->fileSize *= 2;
|
||||
@@ -224,7 +204,7 @@ long audioSeek(int handle, long offset, int origin)
|
||||
|
||||
return audioFile->position;
|
||||
} else {
|
||||
return fileSeek((File*)intToPtr(audioFile->stream), offset, origin);
|
||||
return fileSeek(audioFile->stream, offset, origin);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ namespace fallout {
|
||||
|
||||
typedef bool(AudioQueryCompressedFunc)(char* filePath);
|
||||
|
||||
int audioOpen(const char* fname, int mode, ...);
|
||||
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);
|
||||
|
||||
+13
-33
@@ -7,7 +7,6 @@
|
||||
#include "debug.h"
|
||||
#include "memory_manager.h"
|
||||
#include "platform_compat.h"
|
||||
#include "pointer_registry.h"
|
||||
#include "sound.h"
|
||||
#include "sound_decoder.h"
|
||||
|
||||
@@ -20,7 +19,7 @@ typedef enum AudioFileFlags {
|
||||
|
||||
typedef struct AudioFile {
|
||||
int flags;
|
||||
int stream;
|
||||
FILE* stream;
|
||||
SoundDecoder* soundDecoder;
|
||||
int fileSize;
|
||||
int sampleRate;
|
||||
@@ -29,7 +28,7 @@ typedef struct AudioFile {
|
||||
} AudioFile;
|
||||
|
||||
static bool defaultCompressionFunc(char* filePath);
|
||||
static int audioFileSoundDecoderReadHandler(int handle, void* buffer, unsigned int size);
|
||||
static int audioFileSoundDecoderReadHandler(void* data, void* buffer, unsigned int size);
|
||||
|
||||
// 0x5108C0
|
||||
static AudioFileQueryCompressedFunc* queryCompressedFunc = defaultCompressionFunc;
|
||||
@@ -52,13 +51,13 @@ static bool defaultCompressionFunc(char* filePath)
|
||||
}
|
||||
|
||||
// 0x41A870
|
||||
static int audioFileSoundDecoderReadHandler(int handle, void* buffer, unsigned int size)
|
||||
static int audioFileSoundDecoderReadHandler(void* data, void* buffer, unsigned int size)
|
||||
{
|
||||
return fread(buffer, 1, size, (FILE*)intToPtr(handle));
|
||||
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);
|
||||
@@ -70,28 +69,7 @@ int audioFileOpen(const char* fname, int flags, ...)
|
||||
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;
|
||||
}
|
||||
@@ -114,12 +92,14 @@ int audioFileOpen(const char* fname, int flags, ...)
|
||||
|
||||
AudioFile* audioFile = &(gAudioFileList[index]);
|
||||
audioFile->flags = AUDIO_FILE_IN_USE;
|
||||
audioFile->stream = ptrToInt(stream);
|
||||
audioFile->stream = stream;
|
||||
|
||||
if (compression == 2) {
|
||||
audioFile->flags |= AUDIO_FILE_COMPRESSED;
|
||||
audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize));
|
||||
audioFile->fileSize *= 2;
|
||||
|
||||
*sampleRate = audioFile->sampleRate;
|
||||
} else {
|
||||
audioFile->fileSize = getFileSize(stream);
|
||||
}
|
||||
@@ -133,7 +113,7 @@ int audioFileOpen(const char* fname, int flags, ...)
|
||||
int audioFileClose(int handle)
|
||||
{
|
||||
AudioFile* audioFile = &(gAudioFileList[handle - 1]);
|
||||
fclose((FILE*)intToPtr(audioFile->stream, true));
|
||||
fclose(audioFile->stream);
|
||||
|
||||
if ((audioFile->flags & AUDIO_FILE_COMPRESSED) != 0) {
|
||||
soundDecoderFree(audioFile->soundDecoder);
|
||||
@@ -155,7 +135,7 @@ int audioFileRead(int handle, void* buffer, unsigned int size)
|
||||
if ((ptr->flags & AUDIO_FILE_COMPRESSED) != 0) {
|
||||
bytesRead = soundDecoderDecode(ptr->soundDecoder, buffer, size);
|
||||
} else {
|
||||
bytesRead = fread(buffer, 1, size, (FILE*)intToPtr(ptr->stream));
|
||||
bytesRead = fread(buffer, 1, size, ptr->stream);
|
||||
}
|
||||
|
||||
ptr->position += bytesRead;
|
||||
@@ -190,7 +170,7 @@ long audioFileSeek(int handle, long offset, int origin)
|
||||
if (a4 <= audioFile->position) {
|
||||
soundDecoderFree(audioFile->soundDecoder);
|
||||
|
||||
fseek((FILE*)intToPtr(audioFile->stream), 0, 0);
|
||||
fseek(audioFile->stream, 0, 0);
|
||||
|
||||
audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize));
|
||||
audioFile->fileSize *= 2;
|
||||
@@ -222,7 +202,7 @@ long audioFileSeek(int handle, long offset, int origin)
|
||||
return audioFile->position;
|
||||
}
|
||||
|
||||
return fseek((FILE*)intToPtr(audioFile->stream), offset, origin);
|
||||
return fseek(audioFile->stream, offset, origin);
|
||||
}
|
||||
|
||||
// 0x41AD20
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ namespace fallout {
|
||||
|
||||
typedef bool(AudioFileQueryCompressedFunc)(char* filePath);
|
||||
|
||||
int audioFileOpen(const char* fname, int flags, ...);
|
||||
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);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#include "autorun.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
#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"
|
||||
@@ -806,13 +806,13 @@ 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);
|
||||
value = critterGetStat(gDude, STAT_MELEE_DAMAGE);
|
||||
snprintf(text, sizeof(text), " %d", value);
|
||||
|
||||
length = fontGetStringWidth(text);
|
||||
|
||||
+37
-5
@@ -37,6 +37,7 @@
|
||||
#include "scripts.h"
|
||||
#include "settings.h"
|
||||
#include "sfall_config.h"
|
||||
#include "sfall_global_scripts.h"
|
||||
#include "skill.h"
|
||||
#include "stat.h"
|
||||
#include "svga.h"
|
||||
@@ -168,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,
|
||||
@@ -180,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
|
||||
@@ -2029,6 +2032,7 @@ int combatInit()
|
||||
burstModInit();
|
||||
unarmedInit();
|
||||
damageModInit();
|
||||
combat_reset_hit_location_penalty();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2058,6 +2062,7 @@ void combatReset()
|
||||
|
||||
// SFALL
|
||||
criticalsReset();
|
||||
combat_reset_hit_location_penalty();
|
||||
}
|
||||
|
||||
// 0x420E14
|
||||
@@ -2803,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);
|
||||
|
||||
@@ -3140,6 +3145,10 @@ static int _combat_input()
|
||||
}
|
||||
|
||||
int keyCode = inputGetInput();
|
||||
|
||||
// SFALL: CombatLoopHook.
|
||||
sfall_gl_scr_process_main();
|
||||
|
||||
if (_action_explode_running()) {
|
||||
// NOTE: Uninline.
|
||||
_combat_turn_run();
|
||||
@@ -3831,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) {
|
||||
@@ -4417,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) {
|
||||
@@ -6798,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
|
||||
|
||||
@@ -71,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()
|
||||
{
|
||||
|
||||
+19
-15
@@ -280,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;
|
||||
@@ -393,7 +397,7 @@ static bool configParseLine(Config* config, char* string)
|
||||
// keys there.
|
||||
|
||||
// Skip leading whitespace.
|
||||
while (isspace(*string)) {
|
||||
while (isspace(static_cast<unsigned char>(*string))) {
|
||||
string++;
|
||||
}
|
||||
|
||||
@@ -496,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--;
|
||||
}
|
||||
@@ -507,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--;
|
||||
}
|
||||
|
||||
+2
-2
@@ -996,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
|
||||
|
||||
+6
-6
@@ -206,8 +206,8 @@ 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(),
|
||||
@@ -596,8 +596,8 @@ 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;
|
||||
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;
|
||||
@@ -962,8 +962,8 @@ 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;
|
||||
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;
|
||||
|
||||
+28
-3
@@ -43,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
|
||||
@@ -122,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;
|
||||
}
|
||||
@@ -201,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;
|
||||
}
|
||||
@@ -632,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;
|
||||
}
|
||||
@@ -854,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
|
||||
|
||||
+1
-1
@@ -447,7 +447,7 @@ int dictionaryLoad(FILE* stream, Dictionary* dictionary, int a3)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fgets(entry->key, keyLength + 1, 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
|
||||
|
||||
+31
-3
@@ -3,7 +3,6 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "color.h"
|
||||
#include "mmx.h"
|
||||
#include "svga.h"
|
||||
|
||||
namespace fallout {
|
||||
@@ -208,13 +207,13 @@ void blitBufferToBufferStretchTrans(unsigned char* src, int srcWidth, int srcHei
|
||||
// 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
|
||||
@@ -311,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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
+73
-12
@@ -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"
|
||||
@@ -50,13 +44,17 @@
|
||||
#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"
|
||||
@@ -117,6 +115,9 @@ int _game_user_wants_to_quit = 0;
|
||||
// 0x58E940
|
||||
MessageList gMiscMessageList;
|
||||
|
||||
// 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)
|
||||
{
|
||||
@@ -340,8 +341,8 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4
|
||||
// SFALL
|
||||
premadeCharactersInit();
|
||||
|
||||
if (!sfallGlobalVarsInit()) {
|
||||
debugPrint("Failed on sfallGlobalVarsInit");
|
||||
if (!sfall_gl_vars_init()) {
|
||||
debugPrint("Failed on sfall_gl_vars_init");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -350,6 +351,20 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4
|
||||
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;
|
||||
@@ -394,9 +409,11 @@ void gameReset()
|
||||
_init_options_menu();
|
||||
|
||||
// SFALL
|
||||
sfallGlobalVarsReset();
|
||||
sfall_gl_vars_reset();
|
||||
sfallListsReset();
|
||||
messageListRepositoryReset();
|
||||
sfallArraysReset();
|
||||
sfall_gl_scr_reset();
|
||||
}
|
||||
|
||||
// 0x442C34
|
||||
@@ -405,8 +422,10 @@ void gameExit()
|
||||
debugPrint("\nGame Exit\n");
|
||||
|
||||
// SFALL
|
||||
sfall_gl_scr_exit();
|
||||
sfallArraysExit();
|
||||
sfallListsExit();
|
||||
sfallGlobalVarsExit();
|
||||
sfall_gl_vars_exit();
|
||||
premadeCharactersExit();
|
||||
|
||||
tileDisable();
|
||||
@@ -993,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
|
||||
@@ -1134,6 +1164,11 @@ static void gameFreeGlobalVars()
|
||||
internal_free(gGameGlobalVars);
|
||||
gGameGlobalVars = NULL;
|
||||
}
|
||||
|
||||
if (gGameGlobalPointers != nullptr) {
|
||||
internal_free(gGameGlobalPointers);
|
||||
gGameGlobalPointers = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x443F74
|
||||
@@ -1316,11 +1351,15 @@ static int gameDbInit()
|
||||
for (patch_index = 0; patch_index < 1000; 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);
|
||||
}
|
||||
}
|
||||
|
||||
if (compat_access("f2_res.dat", 0) == 0) {
|
||||
dbOpen("f2_res.dat", 0, NULL, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1492,6 +1531,28 @@ 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)
|
||||
|
||||
@@ -38,6 +38,8 @@ void gameUpdateState();
|
||||
int showQuitConfirmationDialog();
|
||||
|
||||
int gameShowDeathDialog(const char* message);
|
||||
void* gameGetGlobalPointer(int var);
|
||||
int gameSetGlobalPointer(int var, void* value);
|
||||
|
||||
class GameMode {
|
||||
public:
|
||||
|
||||
@@ -2612,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 = getTicks();
|
||||
frame++;
|
||||
}
|
||||
|
||||
renderPresent();
|
||||
sharedFpsLimiter.throttle();
|
||||
}
|
||||
|
||||
if (artUnlock(headFrmHandle) == -1) {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "settings.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "touch.h"
|
||||
#include "window_manager.h"
|
||||
|
||||
namespace fallout {
|
||||
@@ -249,6 +250,11 @@ int gameMoviePlay(int movie, int flags)
|
||||
break;
|
||||
}
|
||||
|
||||
Gesture gesture;
|
||||
if (touch_get_gesture(&gesture) && gesture.state == kEnded) {
|
||||
break;
|
||||
}
|
||||
|
||||
int x;
|
||||
int y;
|
||||
_mouse_get_raw_state(&x, &y, &buttons);
|
||||
|
||||
+3
-7
@@ -157,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);
|
||||
@@ -898,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");
|
||||
}
|
||||
@@ -1548,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;
|
||||
|
||||
+20
-13
@@ -11,6 +11,7 @@
|
||||
#include "mouse.h"
|
||||
#include "svga.h"
|
||||
#include "text_font.h"
|
||||
#include "touch.h"
|
||||
#include "vcr.h"
|
||||
#include "win32.h"
|
||||
|
||||
@@ -1026,24 +1027,24 @@ static void buildNormalizedQwertyKeys()
|
||||
keys[SDL_SCANCODE_F13] = -1;
|
||||
keys[SDL_SCANCODE_F14] = -1;
|
||||
keys[SDL_SCANCODE_F15] = -1;
|
||||
//keys[DIK_KANA] = -1;
|
||||
//keys[DIK_CONVERT] = -1;
|
||||
//keys[DIK_NOCONVERT] = -1;
|
||||
//keys[DIK_YEN] = -1;
|
||||
// keys[DIK_KANA] = -1;
|
||||
// keys[DIK_CONVERT] = -1;
|
||||
// keys[DIK_NOCONVERT] = -1;
|
||||
// keys[DIK_YEN] = -1;
|
||||
keys[SDL_SCANCODE_KP_EQUALS] = -1;
|
||||
//keys[DIK_PREVTRACK] = -1;
|
||||
//keys[DIK_AT] = -1;
|
||||
//keys[DIK_COLON] = -1;
|
||||
//keys[DIK_UNDERLINE] = -1;
|
||||
//keys[DIK_KANJI] = -1;
|
||||
// keys[DIK_PREVTRACK] = -1;
|
||||
// keys[DIK_AT] = -1;
|
||||
// keys[DIK_COLON] = -1;
|
||||
// keys[DIK_UNDERLINE] = -1;
|
||||
// keys[DIK_KANJI] = -1;
|
||||
keys[SDL_SCANCODE_STOP] = -1;
|
||||
//keys[DIK_AX] = -1;
|
||||
//keys[DIK_UNLABELED] = -1;
|
||||
// keys[DIK_AX] = -1;
|
||||
// keys[DIK_UNLABELED] = -1;
|
||||
keys[SDL_SCANCODE_KP_ENTER] = SDL_SCANCODE_KP_ENTER;
|
||||
keys[SDL_SCANCODE_RCTRL] = SDL_SCANCODE_RCTRL;
|
||||
keys[SDL_SCANCODE_KP_COMMA] = -1;
|
||||
keys[SDL_SCANCODE_KP_DIVIDE] = SDL_SCANCODE_KP_DIVIDE;
|
||||
//keys[DIK_SYSRQ] = 84;
|
||||
// keys[DIK_SYSRQ] = 84;
|
||||
keys[SDL_SCANCODE_RALT] = SDL_SCANCODE_RALT;
|
||||
keys[SDL_SCANCODE_HOME] = SDL_SCANCODE_HOME;
|
||||
keys[SDL_SCANCODE_UP] = SDL_SCANCODE_UP;
|
||||
@@ -1084,9 +1085,13 @@ void _GNW95_process_message()
|
||||
handleMouseEvent(&e);
|
||||
break;
|
||||
case SDL_FINGERDOWN:
|
||||
touch_handle_start(&(e.tfinger));
|
||||
break;
|
||||
case SDL_FINGERMOTION:
|
||||
touch_handle_move(&(e.tfinger));
|
||||
break;
|
||||
case SDL_FINGERUP:
|
||||
handleTouchEvent(&e);
|
||||
touch_handle_end(&(e.tfinger));
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
@@ -1121,6 +1126,8 @@ void _GNW95_process_message()
|
||||
}
|
||||
}
|
||||
|
||||
touch_process_gesture();
|
||||
|
||||
if (gProgramIsActive && !keyboardIsDisabled()) {
|
||||
// NOTE: Uninline
|
||||
int tick = getTicks();
|
||||
|
||||
+44
-25
@@ -2483,30 +2483,26 @@ static void customInterfaceBarInit()
|
||||
{
|
||||
gInterfaceBarContentOffset = gInterfaceBarWidth - 640;
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
snprintf(path, sizeof(path), "art\\intrface\\HR_IFACE_%d.FRM", gInterfaceBarWidth);
|
||||
if (gInterfaceBarContentOffset > 0 && screenGetWidth() > 640) {
|
||||
char path[COMPAT_MAX_PATH];
|
||||
snprintf(path, sizeof(path), "art\\intrface\\HR_IFACE_%d.FRM", gInterfaceBarWidth);
|
||||
|
||||
int size;
|
||||
if (dbGetFileSize(path, &size) != 0 || gInterfaceBarContentOffset <= 0 || screenGetWidth() <= 640) {
|
||||
gCustomInterfaceBarBackground = artLoad(path);
|
||||
}
|
||||
|
||||
if (gCustomInterfaceBarBackground != nullptr) {
|
||||
gInterfaceBarIsCustom = true;
|
||||
} else {
|
||||
gInterfaceBarContentOffset = 0;
|
||||
gInterfaceBarWidth = 640;
|
||||
gInterfaceBarIsCustom = false;
|
||||
} else {
|
||||
gInterfaceBarIsCustom = true;
|
||||
|
||||
gCustomInterfaceBarBackground = (Art*)(malloc(size));
|
||||
if (artRead(path, (unsigned char*)gCustomInterfaceBarBackground) != 0) {
|
||||
gInterfaceBarIsCustom = false;
|
||||
free(gCustomInterfaceBarBackground);
|
||||
gCustomInterfaceBarBackground = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void customInterfaceBarExit()
|
||||
{
|
||||
if (gCustomInterfaceBarBackground != nullptr) {
|
||||
free(gCustomInterfaceBarBackground);
|
||||
internal_free(gCustomInterfaceBarBackground);
|
||||
gCustomInterfaceBarBackground = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -2585,21 +2581,11 @@ static void sidePanelsShow()
|
||||
|
||||
static void sidePanelsDraw(const char* path, int win, bool isLeading)
|
||||
{
|
||||
int size;
|
||||
if (dbGetFileSize(path, &size) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Art* image = reinterpret_cast<Art*>(internal_malloc(size));
|
||||
Art* image = artLoad(path);
|
||||
if (image == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (artRead(path, reinterpret_cast<unsigned char*>(image)) != 0) {
|
||||
internal_free(image);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char* imageData = artGetFrameData(image, 0, 0);
|
||||
|
||||
int imageWidth = artGetWidth(image, 0, 0);
|
||||
@@ -2630,4 +2616,37 @@ static void sidePanelsDraw(const char* path, int win, bool isLeading)
|
||||
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
|
||||
|
||||
@@ -69,6 +69,7 @@ void interfaceBarEndButtonsRenderRedLights();
|
||||
int indicatorBarRefresh();
|
||||
bool indicatorBarShow();
|
||||
bool indicatorBarHide();
|
||||
bool interface_get_current_attack_mode(int* hit_mode);
|
||||
|
||||
unsigned char* customInterfaceBarGetBackgroundImageData();
|
||||
|
||||
|
||||
+136
-49
@@ -14,6 +14,7 @@
|
||||
#include "interpreter_lib.h"
|
||||
#include "memory_manager.h"
|
||||
#include "platform_compat.h"
|
||||
#include "sfall_global_scripts.h"
|
||||
#include "svga.h"
|
||||
|
||||
namespace fallout {
|
||||
@@ -43,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);
|
||||
@@ -87,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);
|
||||
@@ -421,7 +421,7 @@ static void _purgeProgram(Program* program)
|
||||
}
|
||||
|
||||
// 0x467614
|
||||
static void programFree(Program* program)
|
||||
void programFree(Program* program)
|
||||
{
|
||||
// NOTE: Uninline.
|
||||
_detachProgram(program);
|
||||
@@ -932,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");
|
||||
@@ -941,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;
|
||||
@@ -1028,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");
|
||||
@@ -1037,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;
|
||||
@@ -1131,7 +1131,7 @@ static void opConditionalOperatorLessThanEquals(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");
|
||||
@@ -1385,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");
|
||||
@@ -1491,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2027,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);
|
||||
@@ -2036,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);
|
||||
|
||||
@@ -2050,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);
|
||||
@@ -2094,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);
|
||||
@@ -2106,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);
|
||||
@@ -2121,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);
|
||||
@@ -2149,7 +2164,7 @@ static void op8024(Program* program)
|
||||
}
|
||||
|
||||
// 0x46BA10
|
||||
static void op801E(Program* program)
|
||||
static void opPopAddress(Program* program)
|
||||
{
|
||||
programReturnStackPopValue(program);
|
||||
}
|
||||
@@ -2525,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);
|
||||
@@ -2997,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;
|
||||
@@ -3237,7 +3261,7 @@ void* programReturnStackPopPointer(Program* program)
|
||||
return programValue.pointerValue;
|
||||
}
|
||||
|
||||
bool ProgramValue::isEmpty()
|
||||
bool ProgramValue::isEmpty() const
|
||||
{
|
||||
switch (opcode) {
|
||||
case VALUE_TYPE_INT:
|
||||
@@ -3253,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
|
||||
|
||||
+16
-3
@@ -1,6 +1,7 @@
|
||||
#ifndef INTERPRETER_H
|
||||
#define INTERPRETER_H
|
||||
|
||||
#include "object.h"
|
||||
#include <setjmp.h>
|
||||
|
||||
#include <vector>
|
||||
@@ -140,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;
|
||||
@@ -148,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;
|
||||
|
||||
@@ -192,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);
|
||||
|
||||
+45
-13
@@ -1207,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
|
||||
@@ -1224,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);
|
||||
}
|
||||
@@ -2978,7 +2987,7 @@ static void opGetMessageString(Program* 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);
|
||||
@@ -3089,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);
|
||||
@@ -3648,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;
|
||||
}
|
||||
|
||||
@@ -4654,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);
|
||||
|
||||
+3
-1
@@ -2694,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) {
|
||||
|
||||
+16
-4
@@ -574,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) {
|
||||
@@ -605,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;
|
||||
}
|
||||
@@ -629,7 +641,7 @@ int itemDropAll(Object* critter, int tile)
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x4779F0
|
||||
|
||||
@@ -1400,11 +1400,11 @@ static void keyboardBuildFrenchConfiguration()
|
||||
gLogicalKeyEntries[SDL_SCANCODE_BACKSLASH].rmenu = -1;
|
||||
gLogicalKeyEntries[SDL_SCANCODE_BACKSLASH].ctrl = -1;
|
||||
|
||||
//gLogicalKeyEntries[DIK_OEM_102].unmodified = KEY_LESS;
|
||||
//gLogicalKeyEntries[DIK_OEM_102].shift = KEY_GREATER;
|
||||
//gLogicalKeyEntries[DIK_OEM_102].lmenu = -1;
|
||||
//gLogicalKeyEntries[DIK_OEM_102].rmenu = -1;
|
||||
//gLogicalKeyEntries[DIK_OEM_102].ctrl = -1;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].unmodified = KEY_LESS;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].shift = KEY_GREATER;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].lmenu = -1;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].rmenu = -1;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].ctrl = -1;
|
||||
|
||||
switch (gKeyboardLayout) {
|
||||
case KEYBOARD_LAYOUT_QWERTY:
|
||||
@@ -1583,11 +1583,11 @@ static void keyboardBuildGermanConfiguration()
|
||||
gLogicalKeyEntries[SDL_SCANCODE_BACKSLASH].rmenu = -1;
|
||||
gLogicalKeyEntries[SDL_SCANCODE_BACKSLASH].ctrl = -1;
|
||||
|
||||
//gLogicalKeyEntries[DIK_OEM_102].unmodified = KEY_LESS;
|
||||
//gLogicalKeyEntries[DIK_OEM_102].shift = KEY_GREATER;
|
||||
//gLogicalKeyEntries[DIK_OEM_102].lmenu = -1;
|
||||
//gLogicalKeyEntries[DIK_OEM_102].rmenu = KEY_166;
|
||||
//gLogicalKeyEntries[DIK_OEM_102].ctrl = -1;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].unmodified = KEY_LESS;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].shift = KEY_GREATER;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].lmenu = -1;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].rmenu = KEY_166;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].ctrl = -1;
|
||||
|
||||
switch (gKeyboardLayout) {
|
||||
case KEYBOARD_LAYOUT_FRENCH:
|
||||
@@ -1684,11 +1684,11 @@ static void keyboardBuildItalianConfiguration()
|
||||
gLogicalKeyEntries[SDL_SCANCODE_GRAVE].rmenu = -1;
|
||||
gLogicalKeyEntries[SDL_SCANCODE_GRAVE].ctrl = -1;
|
||||
|
||||
//gLogicalKeyEntries[DIK_OEM_102].unmodified = KEY_LESS;
|
||||
//gLogicalKeyEntries[DIK_OEM_102].shift = KEY_GREATER;
|
||||
//gLogicalKeyEntries[DIK_OEM_102].lmenu = -1;
|
||||
//gLogicalKeyEntries[DIK_OEM_102].rmenu = -1;
|
||||
//gLogicalKeyEntries[DIK_OEM_102].ctrl = -1;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].unmodified = KEY_LESS;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].shift = KEY_GREATER;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].lmenu = -1;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].rmenu = -1;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].ctrl = -1;
|
||||
|
||||
gLogicalKeyEntries[SDL_SCANCODE_1].unmodified = KEY_1;
|
||||
gLogicalKeyEntries[SDL_SCANCODE_1].shift = KEY_EXCLAMATION;
|
||||
@@ -1896,11 +1896,11 @@ static void keyboardBuildSpanishConfiguration()
|
||||
gLogicalKeyEntries[SDL_SCANCODE_RIGHTBRACKET].rmenu = KEY_BRACKET_RIGHT;
|
||||
gLogicalKeyEntries[SDL_SCANCODE_RIGHTBRACKET].ctrl = -1;
|
||||
|
||||
//gLogicalKeyEntries[DIK_OEM_102].unmodified = KEY_LESS;
|
||||
//gLogicalKeyEntries[DIK_OEM_102].shift = KEY_GREATER;
|
||||
//gLogicalKeyEntries[DIK_OEM_102].lmenu = -1;
|
||||
//gLogicalKeyEntries[DIK_OEM_102].rmenu = -1;
|
||||
//gLogicalKeyEntries[DIK_OEM_102].ctrl = -1;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].unmodified = KEY_LESS;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].shift = KEY_GREATER;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].lmenu = -1;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].rmenu = -1;
|
||||
// gLogicalKeyEntries[DIK_OEM_102].ctrl = -1;
|
||||
|
||||
gLogicalKeyEntries[SDL_SCANCODE_SEMICOLON].unmodified = KEY_241;
|
||||
gLogicalKeyEntries[SDL_SCANCODE_SEMICOLON].shift = KEY_209;
|
||||
|
||||
+91
-1
@@ -35,16 +35,18 @@
|
||||
#include "message.h"
|
||||
#include "mouse.h"
|
||||
#include "object.h"
|
||||
#include "options.h"
|
||||
#include "party_member.h"
|
||||
#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_global_scripts.h"
|
||||
#include "sfall_global_vars.h"
|
||||
#include "skill.h"
|
||||
#include "stat.h"
|
||||
#include "svga.h"
|
||||
@@ -1587,6 +1589,73 @@ static int lsgPerformSaveGame()
|
||||
|
||||
fileClose(_flptr);
|
||||
|
||||
// SFALL: Save sfallgv.sav.
|
||||
snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1);
|
||||
strcat(_gmpath, "sfallgv.sav");
|
||||
|
||||
_flptr = fileOpen(_gmpath, "wb");
|
||||
if (_flptr != NULL) {
|
||||
do {
|
||||
if (!sfall_gl_vars_save(_flptr)) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error saving global vars **\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: For now fill remaining sections with zeros to that Sfall
|
||||
// can successfully read our global vars and skip the rest.
|
||||
|
||||
int nextObjectId = 0;
|
||||
if (fileWrite(&nextObjectId, sizeof(nextObjectId), 1, _flptr) != 1) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error saving next object id **\n");
|
||||
break;
|
||||
}
|
||||
|
||||
int addedYears = 0;
|
||||
if (fileWrite(&addedYears, sizeof(addedYears), 1, _flptr) != 1) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error saving added years **\n");
|
||||
break;
|
||||
}
|
||||
|
||||
int fakeTraitsCount = 0;
|
||||
if (fileWrite(&fakeTraitsCount, sizeof(fakeTraitsCount), 1, _flptr) != 1) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error saving fake traits **\n");
|
||||
break;
|
||||
}
|
||||
|
||||
int fakePerksCount = 0;
|
||||
if (fileWrite(&fakePerksCount, sizeof(fakePerksCount), 1, _flptr) != 1) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error saving fake perks **\n");
|
||||
break;
|
||||
}
|
||||
|
||||
int fakeSelectablePerksCount = 0;
|
||||
if (fileWrite(&fakeSelectablePerksCount, sizeof(fakeSelectablePerksCount), 1, _flptr) != 1) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error saving fake selectable perks **\n");
|
||||
break;
|
||||
}
|
||||
|
||||
int arraysCountOld = 0;
|
||||
if (fileWrite(&arraysCountOld, sizeof(arraysCountOld), 1, _flptr) != 1) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error saving arrays (old fmt) **\n");
|
||||
break;
|
||||
}
|
||||
|
||||
int arraysCountNew = 0;
|
||||
if (fileWrite(&arraysCountNew, sizeof(arraysCountNew), 1, _flptr) != 1) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error saving arrays (new fmt) **\n");
|
||||
break;
|
||||
}
|
||||
|
||||
int drugPidsCount = 0;
|
||||
if (fileWrite(&drugPidsCount, sizeof(drugPidsCount), 1, _flptr) != 1) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error saving drug pids **\n");
|
||||
break;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
fileClose(_flptr);
|
||||
}
|
||||
|
||||
snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1);
|
||||
_MapDirErase(_gmpath, "BAK");
|
||||
|
||||
@@ -1662,6 +1731,24 @@ static int lsgLoadGameInSlot(int slot)
|
||||
debugPrint("LOADSAVE: Total load data read: %ld bytes.\n", fileTell(_flptr));
|
||||
fileClose(_flptr);
|
||||
|
||||
// SFALL: Load sfallgv.sav.
|
||||
snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1);
|
||||
strcat(_gmpath, "sfallgv.sav");
|
||||
|
||||
_flptr = fileOpen(_gmpath, "rb");
|
||||
if (_flptr != NULL) {
|
||||
do {
|
||||
if (!sfall_gl_vars_load(_flptr)) {
|
||||
debugPrint("LOADSAVE (SFALL): ** Error loading global vars **\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: For now silently ignore remaining sections.
|
||||
} while (0);
|
||||
|
||||
fileClose(_flptr);
|
||||
}
|
||||
|
||||
snprintf(_str, sizeof(_str), "%s\\", "MAPS");
|
||||
_MapDirErase(_str, "BAK");
|
||||
_proto_dude_update_gender();
|
||||
@@ -1676,6 +1763,9 @@ static int lsgLoadGameInSlot(int slot)
|
||||
|
||||
_loadingGame = 0;
|
||||
|
||||
// SFALL: Start global scripts.
|
||||
sfall_gl_scr_exec_start_proc();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+12
-403
@@ -1,6 +1,5 @@
|
||||
#include "main.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -21,21 +20,22 @@
|
||||
#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"
|
||||
@@ -44,35 +44,9 @@
|
||||
|
||||
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();
|
||||
@@ -87,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();
|
||||
static int main_menu_fatal_error();
|
||||
static void main_menu_play_sound(const char* fileName);
|
||||
|
||||
// 0x5194C8
|
||||
static char _mainMap[] = "artemple.map";
|
||||
@@ -124,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)
|
||||
{
|
||||
@@ -228,6 +149,9 @@ int falloutMain(int argc, char** argv)
|
||||
_main_load_new(mapNameCopy);
|
||||
free(mapNameCopy);
|
||||
|
||||
// SFALL: AfterNewGameStartHook.
|
||||
sfall_gl_scr_exec_start_proc();
|
||||
|
||||
mainLoop();
|
||||
paletteFadeTo(gPaletteWhite);
|
||||
|
||||
@@ -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);
|
||||
@@ -441,6 +361,10 @@ static void mainLoop()
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
int keyCode = inputGetInput();
|
||||
|
||||
// SFALL: MainLoopHook.
|
||||
sfall_gl_scr_process_main();
|
||||
|
||||
gameHandleKey(keyCode, false);
|
||||
|
||||
scriptsHandleRequests();
|
||||
@@ -794,319 +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_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
|
||||
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;
|
||||
}
|
||||
|
||||
windowShow(gMainMenuWindow);
|
||||
|
||||
if (animate) {
|
||||
colorPaletteLoad("color.pal");
|
||||
paletteFadeTo(_cmap);
|
||||
}
|
||||
|
||||
gMainMenuWindowHidden = false;
|
||||
}
|
||||
|
||||
// 0x481AA8
|
||||
static int _main_menu_is_enabled()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 0x481AEC
|
||||
static 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
|
||||
|
||||
+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_ */
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
#include "mmx.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "svga.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
// Return `true` if CPU supports MMX.
|
||||
//
|
||||
// 0x4E08A0
|
||||
bool mmxIsSupported()
|
||||
{
|
||||
return SDL_HasMMX() == SDL_TRUE;
|
||||
}
|
||||
|
||||
// 0x4E0DB0
|
||||
void mmxBlit(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height)
|
||||
{
|
||||
if (gMmxEnabled) {
|
||||
// TODO: Blit with MMX.
|
||||
gMmxEnabled = false;
|
||||
mmxBlit(dest, destPitch, src, srcPitch, width, height);
|
||||
gMmxEnabled = true;
|
||||
} else {
|
||||
for (int y = 0; y < height; y++) {
|
||||
memcpy(dest, src, width);
|
||||
dest += destPitch;
|
||||
src += srcPitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4E0ED5
|
||||
void mmxBlitTrans(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height)
|
||||
{
|
||||
if (gMmxEnabled) {
|
||||
// TODO: Blit with MMX.
|
||||
gMmxEnabled = false;
|
||||
mmxBlitTrans(dest, destPitch, src, srcPitch, width, height);
|
||||
gMmxEnabled = true;
|
||||
} else {
|
||||
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
|
||||
@@ -1,12 +0,0 @@
|
||||
#ifndef MMX_H
|
||||
#define MMX_H
|
||||
|
||||
namespace fallout {
|
||||
|
||||
bool mmxIsSupported();
|
||||
void mmxBlit(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height);
|
||||
void mmxBlitTrans(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* MMX_H */
|
||||
+60
-6
@@ -6,6 +6,7 @@
|
||||
#include "kb.h"
|
||||
#include "memory.h"
|
||||
#include "svga.h"
|
||||
#include "touch.h"
|
||||
#include "vcr.h"
|
||||
|
||||
namespace fallout {
|
||||
@@ -54,7 +55,7 @@ static unsigned char* _mouse_fptr = NULL;
|
||||
static double gMouseSensitivity = 1.0;
|
||||
|
||||
// 0x51E2AC
|
||||
static int gMouseButtonsState = 0;
|
||||
static int last_buttons = 0;
|
||||
|
||||
// 0x6AC790
|
||||
static bool gCursorIsHidden;
|
||||
@@ -381,6 +382,54 @@ void _mouse_info()
|
||||
return;
|
||||
}
|
||||
|
||||
Gesture gesture;
|
||||
if (touch_get_gesture(&gesture)) {
|
||||
static int prevx;
|
||||
static int prevy;
|
||||
|
||||
switch (gesture.type) {
|
||||
case kTap:
|
||||
if (gesture.numberOfTouches == 1) {
|
||||
_mouse_simulate_input(0, 0, MOUSE_STATE_LEFT_BUTTON_DOWN);
|
||||
} else if (gesture.numberOfTouches == 2) {
|
||||
_mouse_simulate_input(0, 0, MOUSE_STATE_RIGHT_BUTTON_DOWN);
|
||||
}
|
||||
break;
|
||||
case kLongPress:
|
||||
case kPan:
|
||||
if (gesture.state == kBegan) {
|
||||
prevx = gesture.x;
|
||||
prevy = gesture.y;
|
||||
}
|
||||
|
||||
if (gesture.type == kLongPress) {
|
||||
if (gesture.numberOfTouches == 1) {
|
||||
_mouse_simulate_input(gesture.x - prevx, gesture.y - prevy, MOUSE_STATE_LEFT_BUTTON_DOWN);
|
||||
} else if (gesture.numberOfTouches == 2) {
|
||||
_mouse_simulate_input(gesture.x - prevx, gesture.y - prevy, MOUSE_STATE_RIGHT_BUTTON_DOWN);
|
||||
}
|
||||
} else if (gesture.type == kPan) {
|
||||
if (gesture.numberOfTouches == 1) {
|
||||
_mouse_simulate_input(gesture.x - prevx, gesture.y - prevy, 0);
|
||||
} else if (gesture.numberOfTouches == 2) {
|
||||
gMouseWheelX = (prevx - gesture.x) / 2;
|
||||
gMouseWheelY = (gesture.y - prevy) / 2;
|
||||
|
||||
if (gMouseWheelX != 0 || gMouseWheelY != 0) {
|
||||
gMouseEvent |= MOUSE_EVENT_WHEEL;
|
||||
_raw_buttons |= MOUSE_EVENT_WHEEL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prevx = gesture.x;
|
||||
prevy = gesture.y;
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int buttons = 0;
|
||||
@@ -415,7 +464,7 @@ void _mouse_info()
|
||||
}
|
||||
x = 0;
|
||||
y = 0;
|
||||
buttons = gMouseButtonsState;
|
||||
buttons = last_buttons;
|
||||
}
|
||||
|
||||
_mouse_simulate_input(x, y, buttons);
|
||||
@@ -447,7 +496,7 @@ void _mouse_simulate_input(int delta_x, int delta_y, int buttons)
|
||||
return;
|
||||
}
|
||||
|
||||
if (delta_x || delta_y || buttons != gMouseButtonsState) {
|
||||
if (delta_x || delta_y || buttons != last_buttons) {
|
||||
if (gVcrState == 0) {
|
||||
if (_vcr_buffer_index == VCR_BUFFER_CAPACITY - 1) {
|
||||
vcrDump();
|
||||
@@ -464,13 +513,13 @@ void _mouse_simulate_input(int delta_x, int delta_y, int buttons)
|
||||
_vcr_buffer_index++;
|
||||
}
|
||||
} else {
|
||||
if (gMouseButtonsState == 0) {
|
||||
if (last_buttons == 0) {
|
||||
if (!_mouse_idling) {
|
||||
_mouse_idle_start_time = getTicks();
|
||||
_mouse_idling = 1;
|
||||
}
|
||||
|
||||
gMouseButtonsState = 0;
|
||||
last_buttons = 0;
|
||||
_raw_buttons = 0;
|
||||
gMouseEvent = 0;
|
||||
|
||||
@@ -479,7 +528,7 @@ void _mouse_simulate_input(int delta_x, int delta_y, int buttons)
|
||||
}
|
||||
|
||||
_mouse_idling = 0;
|
||||
gMouseButtonsState = buttons;
|
||||
last_buttons = buttons;
|
||||
previousEvent = gMouseEvent;
|
||||
gMouseEvent = 0;
|
||||
|
||||
@@ -703,4 +752,9 @@ void convertMouseWheelToArrowKey(int* keyCodePtr)
|
||||
}
|
||||
}
|
||||
|
||||
int mouse_get_last_buttons()
|
||||
{
|
||||
return last_buttons;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
@@ -52,6 +52,7 @@ 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);
|
||||
int mouse_get_last_buttons();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ enum {
|
||||
OBJ_TYPE_COUNT,
|
||||
};
|
||||
|
||||
#define FID_TYPE(value) ((value) & 0xF000000) >> 24
|
||||
#define FID_TYPE(value) ((value)&0xF000000) >> 24
|
||||
#define PID_TYPE(value) (value) >> 24
|
||||
#define SID_TYPE(value) (value) >> 24
|
||||
|
||||
|
||||
+22
-21
@@ -2133,27 +2133,18 @@ Object* objectFindFirst()
|
||||
{
|
||||
gObjectFindElevation = 0;
|
||||
|
||||
ObjectListNode* objectListNode;
|
||||
for (gObjectFindTile = 0; gObjectFindTile < HEX_GRID_SIZE; gObjectFindTile++) {
|
||||
objectListNode = gObjectListHeadByTile[gObjectFindTile];
|
||||
if (objectListNode) {
|
||||
break;
|
||||
ObjectListNode* objectListNode = gObjectListHeadByTile[gObjectFindTile];
|
||||
while (objectListNode != NULL) {
|
||||
Object* object = objectListNode->obj;
|
||||
if (!artIsObjectTypeHidden(FID_TYPE(object->fid))) {
|
||||
gObjectFindLastObjectListNode = objectListNode;
|
||||
return object;
|
||||
}
|
||||
objectListNode = objectListNode->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (gObjectFindTile == HEX_GRID_SIZE) {
|
||||
gObjectFindLastObjectListNode = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (objectListNode != NULL) {
|
||||
if (artIsObjectTypeHidden(FID_TYPE(objectListNode->obj->fid)) == 0) {
|
||||
gObjectFindLastObjectListNode = objectListNode;
|
||||
return objectListNode->obj;
|
||||
}
|
||||
objectListNode = objectListNode->next;
|
||||
}
|
||||
|
||||
gObjectFindLastObjectListNode = NULL;
|
||||
return NULL;
|
||||
}
|
||||
@@ -2167,9 +2158,14 @@ Object* objectFindNext()
|
||||
|
||||
ObjectListNode* objectListNode = gObjectFindLastObjectListNode->next;
|
||||
|
||||
while (gObjectFindTile < HEX_GRID_SIZE) {
|
||||
while (true) {
|
||||
if (objectListNode == NULL) {
|
||||
objectListNode = gObjectListHeadByTile[gObjectFindTile++];
|
||||
gObjectFindTile++;
|
||||
if (gObjectFindTile >= HEX_GRID_SIZE) {
|
||||
break;
|
||||
}
|
||||
|
||||
objectListNode = gObjectListHeadByTile[gObjectFindTile];
|
||||
}
|
||||
|
||||
while (objectListNode != NULL) {
|
||||
@@ -2219,9 +2215,14 @@ Object* objectFindNextAtElevation()
|
||||
|
||||
ObjectListNode* objectListNode = gObjectFindLastObjectListNode->next;
|
||||
|
||||
while (gObjectFindTile < HEX_GRID_SIZE) {
|
||||
while (true) {
|
||||
if (objectListNode == NULL) {
|
||||
objectListNode = gObjectListHeadByTile[gObjectFindTile++];
|
||||
gObjectFindTile++;
|
||||
if (gObjectFindTile >= HEX_GRID_SIZE) {
|
||||
break;
|
||||
}
|
||||
|
||||
objectListNode = gObjectListHeadByTile[gObjectFindTile];
|
||||
}
|
||||
|
||||
while (objectListNode != NULL) {
|
||||
|
||||
+19
-1566
File diff suppressed because it is too large
Load Diff
@@ -1,37 +1,11 @@
|
||||
#ifndef OPTIONS_H
|
||||
#define OPTIONS_H
|
||||
|
||||
#include "db.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
extern int gPreferencesSoundEffectsVolume1;
|
||||
extern int gPreferencesSubtitles1;
|
||||
extern int gPreferencesLanguageFilter1;
|
||||
extern int gPreferencesSpeechVolume1;
|
||||
extern int gPreferencesMasterVolume1;
|
||||
extern int gPreferencesPlayerSpeedup1;
|
||||
extern int gPreferencesCombatTaunts1;
|
||||
extern int gPreferencesMusicVolume1;
|
||||
extern int gPreferencesRunning1;
|
||||
extern int gPreferencesCombatSpeed1;
|
||||
extern int gPreferencesItemHighlight1;
|
||||
extern int gPreferencesCombatMessages1;
|
||||
extern int gPreferencesTargetHighlight1;
|
||||
extern int gPreferencesCombatDifficulty1;
|
||||
extern int gPreferencesViolenceLevel1;
|
||||
extern int gPreferencesGameDifficulty1;
|
||||
extern int gPreferencesCombatLooks1;
|
||||
|
||||
int showOptions();
|
||||
int showOptionsWithInitialKeyCode(int initialKeyCode);
|
||||
int showPause(bool a1);
|
||||
int _init_options_menu();
|
||||
int preferencesSave(File* stream);
|
||||
int preferencesLoad(File* stream);
|
||||
void brightnessIncrease();
|
||||
void brightnessDecrease();
|
||||
int _do_options();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
||||
+93
-87
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "animation.h"
|
||||
#include "color.h"
|
||||
#include "combat.h"
|
||||
#include "combat_ai.h"
|
||||
#include "combat_ai_defs.h"
|
||||
#include "config.h"
|
||||
@@ -63,24 +64,24 @@ typedef struct STRU_519DBC {
|
||||
int field_8; // early what?
|
||||
} STRU_519DBC;
|
||||
|
||||
typedef struct STRUCT_519DA8 {
|
||||
typedef struct PartyMemberListItem {
|
||||
Object* object;
|
||||
Script* script;
|
||||
int* vars;
|
||||
struct STRUCT_519DA8* next;
|
||||
} STRUCT_519DA8;
|
||||
struct PartyMemberListItem* next;
|
||||
} PartyMemberListItem;
|
||||
|
||||
static int partyMemberGetDescription(Object* object, PartyMemberDescription** partyMemberDescriptionPtr);
|
||||
static void partyMemberDescriptionInit(PartyMemberDescription* partyMemberDescription);
|
||||
static int _partyMemberPrepLoadInstance(STRUCT_519DA8* a1);
|
||||
static int _partyMemberRecoverLoadInstance(STRUCT_519DA8* a1);
|
||||
static int _partyMemberPrepLoadInstance(PartyMemberListItem* a1);
|
||||
static int _partyMemberRecoverLoadInstance(PartyMemberListItem* a1);
|
||||
static int _partyMemberNewObjID();
|
||||
static int _partyMemberNewObjIDRecurseFind(Object* object, int objectId);
|
||||
static int _partyMemberPrepItemSave(Object* object);
|
||||
static int _partyMemberItemSave(Object* object);
|
||||
static int _partyMemberItemRecover(STRUCT_519DA8* a1);
|
||||
static int _partyMemberItemRecover(PartyMemberListItem* a1);
|
||||
static int _partyMemberClearItemList();
|
||||
static int _partyFixMultipleMembers();
|
||||
static int partyFixMultipleMembers();
|
||||
static int _partyMemberCopyLevelInfo(Object* object, int a2);
|
||||
|
||||
// 0x519D9C
|
||||
@@ -90,12 +91,12 @@ int gPartyMemberDescriptionsLength = 0;
|
||||
int* gPartyMemberPids = NULL;
|
||||
|
||||
//
|
||||
static STRUCT_519DA8* _itemSaveListHead = NULL;
|
||||
static PartyMemberListItem* _itemSaveListHead = NULL;
|
||||
|
||||
// List of party members, it's length is [gPartyMemberDescriptionsLength] + 20.
|
||||
//
|
||||
// 0x519DA8
|
||||
static STRUCT_519DA8* gPartyMembers = NULL;
|
||||
PartyMemberListItem* gPartyMembers = NULL;
|
||||
|
||||
// Number of critters added to party.
|
||||
//
|
||||
@@ -149,7 +150,7 @@ int partyMembersInit()
|
||||
|
||||
memset(gPartyMemberPids, 0, sizeof(*gPartyMemberPids) * gPartyMemberDescriptionsLength);
|
||||
|
||||
gPartyMembers = (STRUCT_519DA8*)internal_malloc(sizeof(*gPartyMembers) * (gPartyMemberDescriptionsLength + 20));
|
||||
gPartyMembers = (PartyMemberListItem*)internal_malloc(sizeof(*gPartyMembers) * (gPartyMemberDescriptionsLength + 20));
|
||||
if (gPartyMembers == NULL) {
|
||||
goto err;
|
||||
}
|
||||
@@ -378,7 +379,7 @@ int partyMemberAdd(Object* object)
|
||||
}
|
||||
|
||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||
if (partyMember->object == object || partyMember->object->pid == object->pid) {
|
||||
return 0;
|
||||
}
|
||||
@@ -389,7 +390,7 @@ int partyMemberAdd(Object* object)
|
||||
return -1;
|
||||
}
|
||||
|
||||
STRUCT_519DA8* partyMember = &(gPartyMembers[gPartyMembersLength]);
|
||||
PartyMemberListItem* partyMember = &(gPartyMembers[gPartyMembersLength]);
|
||||
partyMember->object = object;
|
||||
partyMember->script = NULL;
|
||||
partyMember->vars = NULL;
|
||||
@@ -434,7 +435,7 @@ int partyMemberRemove(Object* object)
|
||||
|
||||
int index;
|
||||
for (index = 1; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||
if (partyMember->object == object) {
|
||||
break;
|
||||
}
|
||||
@@ -479,7 +480,7 @@ int _partyMemberPrepSave()
|
||||
_partyStatePrepped = 1;
|
||||
|
||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* ptr = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* ptr = &(gPartyMembers[index]);
|
||||
|
||||
if (index > 0) {
|
||||
ptr->object->flags &= ~(OBJECT_NO_REMOVE | OBJECT_NO_SAVE);
|
||||
@@ -498,7 +499,7 @@ int _partyMemberPrepSave()
|
||||
int _partyMemberUnPrepSave()
|
||||
{
|
||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* ptr = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* ptr = &(gPartyMembers[index]);
|
||||
|
||||
if (index > 0) {
|
||||
ptr->object->flags |= (OBJECT_NO_REMOVE | OBJECT_NO_SAVE);
|
||||
@@ -522,7 +523,7 @@ int partyMembersSave(File* stream)
|
||||
if (fileWriteInt32(stream, _partyMemberItemCount) == -1) return -1;
|
||||
|
||||
for (int index = 1; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||
if (fileWriteInt32(stream, partyMember->object->id) == -1) return -1;
|
||||
}
|
||||
|
||||
@@ -546,7 +547,7 @@ int _partyMemberPrepLoad()
|
||||
_partyStatePrepped = 1;
|
||||
|
||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* ptr_519DA8 = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* ptr_519DA8 = &(gPartyMembers[index]);
|
||||
if (_partyMemberPrepLoadInstance(ptr_519DA8) != 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -557,7 +558,7 @@ int _partyMemberPrepLoad()
|
||||
|
||||
// partyMemberPrepLoadInstance
|
||||
// 0x49480C
|
||||
static int _partyMemberPrepLoadInstance(STRUCT_519DA8* a1)
|
||||
static int _partyMemberPrepLoadInstance(PartyMemberListItem* a1)
|
||||
{
|
||||
Object* obj = a1->object;
|
||||
|
||||
@@ -642,7 +643,7 @@ int _partyMemberRecoverLoad()
|
||||
debugPrint("[Party Member %d]: %s\n", index, critterGetName(gPartyMembers[index].object));
|
||||
}
|
||||
|
||||
STRUCT_519DA8* v6 = _itemSaveListHead;
|
||||
PartyMemberListItem* v6 = _itemSaveListHead;
|
||||
while (v6 != NULL) {
|
||||
_itemSaveListHead = v6->next;
|
||||
|
||||
@@ -655,7 +656,7 @@ int _partyMemberRecoverLoad()
|
||||
_partyStatePrepped = 0;
|
||||
|
||||
if (!_isLoadingGame()) {
|
||||
_partyFixMultipleMembers();
|
||||
partyFixMultipleMembers();
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -663,7 +664,7 @@ int _partyMemberRecoverLoad()
|
||||
|
||||
// partyMemberRecoverLoadInstance
|
||||
// 0x494A88
|
||||
static int _partyMemberRecoverLoadInstance(STRUCT_519DA8* a1)
|
||||
static int _partyMemberRecoverLoadInstance(PartyMemberListItem* a1)
|
||||
{
|
||||
if (a1->script == NULL) {
|
||||
showMesageBox("\n Error!: partyMemberRecoverLoadInstance: No script!");
|
||||
@@ -759,7 +760,7 @@ int partyMembersLoad(File* stream)
|
||||
}
|
||||
}
|
||||
|
||||
_partyFixMultipleMembers();
|
||||
partyFixMultipleMembers();
|
||||
|
||||
for (int index = 1; index < gPartyMemberDescriptionsLength; index++) {
|
||||
STRU_519DBC* ptr_519DBC = &(_partyMemberLevelUpInfoList[index]);
|
||||
@@ -800,7 +801,7 @@ int _partyMemberSyncPosition()
|
||||
int n = 0;
|
||||
int distance = 2;
|
||||
for (int index = 1; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||
Object* partyMemberObj = partyMember->object;
|
||||
if ((partyMemberObj->flags & OBJECT_HIDDEN) == 0 && PID_TYPE(partyMemberObj->pid) == OBJ_TYPE_CRITTER) {
|
||||
int rotation;
|
||||
@@ -832,7 +833,7 @@ int _partyMemberRestingHeal(int a1)
|
||||
}
|
||||
|
||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||
if (PID_TYPE(partyMember->object->pid) == OBJ_TYPE_CRITTER) {
|
||||
int healingRate = critterGetStat(partyMember->object, STAT_HEALING_RATE);
|
||||
critterAdjustHitPoints(partyMember->object, v1 * healingRate);
|
||||
@@ -859,7 +860,7 @@ Object* partyMemberFindByPid(int pid)
|
||||
bool _isPotentialPartyMember(Object* object)
|
||||
{
|
||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||
if (partyMember->object->pid == gPartyMemberPids[index]) {
|
||||
return true;
|
||||
}
|
||||
@@ -975,7 +976,7 @@ static int _partyMemberNewObjIDRecurseFind(Object* obj, int objectId)
|
||||
int _partyMemberPrepItemSaveAll()
|
||||
{
|
||||
for (int partyMemberIndex = 0; partyMemberIndex < gPartyMembersLength; partyMemberIndex++) {
|
||||
STRUCT_519DA8* partyMember = &(gPartyMembers[partyMemberIndex]);
|
||||
PartyMemberListItem* partyMember = &(gPartyMembers[partyMemberIndex]);
|
||||
|
||||
Inventory* inventory = &(partyMember->object->data.inventory);
|
||||
for (int inventoryItemIndex = 0; inventoryItemIndex < inventory->length; inventoryItemIndex++) {
|
||||
@@ -1024,7 +1025,7 @@ static int _partyMemberItemSave(Object* object)
|
||||
object->id = script->field_1C;
|
||||
}
|
||||
|
||||
STRUCT_519DA8* node = (STRUCT_519DA8*)internal_malloc(sizeof(*node));
|
||||
PartyMemberListItem* node = (PartyMemberListItem*)internal_malloc(sizeof(*node));
|
||||
if (node == NULL) {
|
||||
showMesageBox("\n Error!: partyMemberItemSave: Out of memory!");
|
||||
exit(1);
|
||||
@@ -1052,7 +1053,7 @@ static int _partyMemberItemSave(Object* object)
|
||||
node->vars = NULL;
|
||||
}
|
||||
|
||||
STRUCT_519DA8* temp = _itemSaveListHead;
|
||||
PartyMemberListItem* temp = _itemSaveListHead;
|
||||
_itemSaveListHead = node;
|
||||
node->next = temp;
|
||||
}
|
||||
@@ -1068,7 +1069,7 @@ static int _partyMemberItemSave(Object* object)
|
||||
|
||||
// partyMemberItemRecover
|
||||
// 0x495388
|
||||
static int _partyMemberItemRecover(STRUCT_519DA8* a1)
|
||||
static int _partyMemberItemRecover(PartyMemberListItem* a1)
|
||||
{
|
||||
int sid = -1;
|
||||
if (scriptAdd(&sid, SCRIPT_TYPE_ITEM) == -1) {
|
||||
@@ -1107,7 +1108,7 @@ static int _partyMemberItemRecover(STRUCT_519DA8* a1)
|
||||
static int _partyMemberClearItemList()
|
||||
{
|
||||
while (_itemSaveListHead != NULL) {
|
||||
STRUCT_519DA8* node = _itemSaveListHead;
|
||||
PartyMemberListItem* node = _itemSaveListHead;
|
||||
_itemSaveListHead = _itemSaveListHead->next;
|
||||
|
||||
if (node->script != NULL) {
|
||||
@@ -1196,16 +1197,14 @@ int partyGetBestSkillValue(int skill)
|
||||
}
|
||||
|
||||
// 0x495620
|
||||
static int _partyFixMultipleMembers()
|
||||
static int partyFixMultipleMembers()
|
||||
{
|
||||
debugPrint("\n\n\n[Party Members]:");
|
||||
|
||||
// NOTE: Original code is slightly different (uses two nested loops).
|
||||
int critterCount = 0;
|
||||
for (Object* obj = objectFindFirst(); obj != NULL; obj = objectFindNext()) {
|
||||
if (PID_TYPE(obj->pid) == OBJ_TYPE_CRITTER) {
|
||||
critterCount++;
|
||||
}
|
||||
|
||||
Object* obj = objectFindFirst();
|
||||
while (obj != NULL) {
|
||||
bool isPartyMember = false;
|
||||
for (int index = 1; index < gPartyMemberDescriptionsLength; index++) {
|
||||
if (obj->pid == gPartyMemberPids[index]) {
|
||||
@@ -1214,65 +1213,56 @@ static int _partyFixMultipleMembers()
|
||||
}
|
||||
}
|
||||
|
||||
if (!isPartyMember) {
|
||||
continue;
|
||||
}
|
||||
if (isPartyMember) {
|
||||
debugPrint("\n PM: %s", critterGetName(obj));
|
||||
|
||||
debugPrint("\n PM: %s", critterGetName(obj));
|
||||
|
||||
bool v19 = false;
|
||||
if (obj->sid == -1) {
|
||||
v19 = true;
|
||||
} else {
|
||||
Object* v7 = NULL;
|
||||
for (int i = 0; i < gPartyMembersLength; i++) {
|
||||
if (obj->pid == gPartyMembers[i].object->pid) {
|
||||
v7 = gPartyMembers[i].object;
|
||||
break;
|
||||
bool remove = false;
|
||||
if (obj->sid == -1) {
|
||||
remove = true;
|
||||
} else {
|
||||
// NOTE: Uninline.
|
||||
Object* partyMember = partyMemberFindByPid(obj->pid);
|
||||
if (partyMember != NULL && partyMember != obj) {
|
||||
if (partyMember->sid == obj->sid) {
|
||||
obj->sid = -1;
|
||||
}
|
||||
remove = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (v7 != NULL && obj != v7) {
|
||||
if (v7->sid == obj->sid) {
|
||||
obj->sid = -1;
|
||||
if (remove) {
|
||||
// NOTE: Uninline.
|
||||
if (obj != partyMemberFindByPid(obj->pid)) {
|
||||
debugPrint("\nDestroying evil critter doppleganger!");
|
||||
|
||||
if (obj->sid != -1) {
|
||||
scriptRemove(obj->sid);
|
||||
obj->sid = -1;
|
||||
} else {
|
||||
if (queueRemoveEventsByType(obj, EVENT_TYPE_SCRIPT) == -1) {
|
||||
debugPrint("\nERROR Removing Timed Events on FIX remove!!\n");
|
||||
}
|
||||
}
|
||||
|
||||
_combat_delete_critter(obj);
|
||||
|
||||
objectDestroy(obj, NULL);
|
||||
|
||||
// Start over.
|
||||
critterCount = 0;
|
||||
obj = objectFindFirst();
|
||||
continue;
|
||||
} else {
|
||||
debugPrint("\nError: Attempting to destroy evil critter doppleganger FAILED!");
|
||||
}
|
||||
v19 = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!v19) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object* v10 = NULL;
|
||||
for (int i = 0; i < gPartyMembersLength; i++) {
|
||||
if (obj->pid == gPartyMembers[i].object->pid) {
|
||||
v10 = gPartyMembers[i].object;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Probably wrong.
|
||||
if (obj == v10) {
|
||||
debugPrint("\nError: Attempting to destroy evil critter doppleganger FAILED!");
|
||||
continue;
|
||||
}
|
||||
|
||||
debugPrint("\nDestroying evil critter doppleganger!");
|
||||
|
||||
if (obj->sid != -1) {
|
||||
scriptRemove(obj->sid);
|
||||
obj->sid = -1;
|
||||
} else {
|
||||
if (queueRemoveEventsByType(obj, EVENT_TYPE_SCRIPT) == -1) {
|
||||
debugPrint("\nERROR Removing Timed Events on FIX remove!!\n");
|
||||
}
|
||||
}
|
||||
|
||||
objectDestroy(obj, NULL);
|
||||
obj = objectFindNext();
|
||||
}
|
||||
|
||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||
|
||||
Script* script;
|
||||
if (scriptGetScript(partyMember->object->sid, &script) != -1) {
|
||||
@@ -1464,7 +1454,7 @@ bool partyMemberSupportsChemUse(Object* object, int chemUse)
|
||||
int _partyMemberIncLevels()
|
||||
{
|
||||
int i;
|
||||
STRUCT_519DA8* ptr;
|
||||
PartyMemberListItem* ptr;
|
||||
Object* obj;
|
||||
PartyMemberDescription* party_member;
|
||||
const char* name;
|
||||
@@ -1632,7 +1622,7 @@ static int _partyMemberCopyLevelInfo(Object* critter, int a2)
|
||||
bool partyIsAnyoneCanBeHealedByRest()
|
||||
{
|
||||
for (int index = 1; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* ptr = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* ptr = &(gPartyMembers[index]);
|
||||
Object* object = ptr->object;
|
||||
|
||||
if (PID_TYPE(object->pid) != OBJ_TYPE_CRITTER) continue;
|
||||
@@ -1659,7 +1649,7 @@ int partyGetMaxWoundToHealByRest()
|
||||
int maxWound = 0;
|
||||
|
||||
for (int index = 1; index < gPartyMembersLength; index++) {
|
||||
STRUCT_519DA8* ptr = &(gPartyMembers[index]);
|
||||
PartyMemberListItem* ptr = &(gPartyMembers[index]);
|
||||
Object* object = ptr->object;
|
||||
|
||||
if (PID_TYPE(object->pid) != OBJ_TYPE_CRITTER) continue;
|
||||
@@ -1680,4 +1670,20 @@ int partyGetMaxWoundToHealByRest()
|
||||
return maxWound;
|
||||
}
|
||||
|
||||
std::vector<Object*> get_all_party_members_objects(bool include_hidden)
|
||||
{
|
||||
std::vector<Object*> value;
|
||||
value.reserve(gPartyMembersLength);
|
||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||
auto object = gPartyMembers[index].object;
|
||||
if (include_hidden
|
||||
|| (PID_TYPE(object->pid) == OBJ_TYPE_CRITTER
|
||||
&& !critterIsDead(object)
|
||||
&& (object->flags & OBJECT_HIDDEN) == 0)) {
|
||||
value.push_back(object);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#ifndef PARTY_MEMBER_H
|
||||
#define PARTY_MEMBER_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "db.h"
|
||||
#include "obj_types.h"
|
||||
#include "scripts.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
@@ -42,6 +45,7 @@ bool partyMemberSupportsChemUse(Object* object, int chemUse);
|
||||
int _partyMemberIncLevels();
|
||||
bool partyIsAnyoneCanBeHealedByRest();
|
||||
int partyGetMaxWoundToHealByRest();
|
||||
std::vector<Object*> get_all_party_members_objects(bool include_hidden);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
||||
+100
-17
@@ -3,8 +3,6 @@
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
@@ -14,6 +12,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
@@ -187,21 +186,6 @@ void compat_makepath(char* path, const char* drive, const char* dir, const char*
|
||||
#endif
|
||||
}
|
||||
|
||||
int compat_read(int fileHandle, void* buf, unsigned int size)
|
||||
{
|
||||
return read(fileHandle, buf, size);
|
||||
}
|
||||
|
||||
int compat_write(int fileHandle, const void* buf, unsigned int size)
|
||||
{
|
||||
return write(fileHandle, buf, size);
|
||||
}
|
||||
|
||||
long compat_lseek(int fileHandle, long offset, int origin)
|
||||
{
|
||||
return lseek(fileHandle, offset, origin);
|
||||
}
|
||||
|
||||
long compat_tell(int fd)
|
||||
{
|
||||
return lseek(fd, 0, SEEK_CUR);
|
||||
@@ -221,6 +205,7 @@ int compat_mkdir(const char* path)
|
||||
char nativePath[COMPAT_MAX_PATH];
|
||||
strcpy(nativePath, path);
|
||||
compat_windows_path_to_native(nativePath);
|
||||
compat_resolve_path(nativePath);
|
||||
|
||||
#ifdef _WIN32
|
||||
return mkdir(nativePath);
|
||||
@@ -245,6 +230,7 @@ FILE* compat_fopen(const char* path, const char* mode)
|
||||
char nativePath[COMPAT_MAX_PATH];
|
||||
strcpy(nativePath, path);
|
||||
compat_windows_path_to_native(nativePath);
|
||||
compat_resolve_path(nativePath);
|
||||
return fopen(nativePath, mode);
|
||||
}
|
||||
|
||||
@@ -253,14 +239,46 @@ gzFile compat_gzopen(const char* path, const char* mode)
|
||||
char nativePath[COMPAT_MAX_PATH];
|
||||
strcpy(nativePath, path);
|
||||
compat_windows_path_to_native(nativePath);
|
||||
compat_resolve_path(nativePath);
|
||||
return gzopen(nativePath, mode);
|
||||
}
|
||||
|
||||
char* compat_fgets(char* buffer, int maxCount, FILE* stream)
|
||||
{
|
||||
buffer = fgets(buffer, maxCount, stream);
|
||||
|
||||
if (buffer != NULL) {
|
||||
size_t len = strlen(buffer);
|
||||
if (len >= 2 && buffer[len - 1] == '\n' && buffer[len - 2] == '\r') {
|
||||
buffer[len - 2] = '\n';
|
||||
buffer[len - 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char* compat_gzgets(gzFile stream, char* buffer, int maxCount)
|
||||
{
|
||||
buffer = gzgets(stream, buffer, maxCount);
|
||||
|
||||
if (buffer != NULL) {
|
||||
size_t len = strlen(buffer);
|
||||
if (len >= 2 && buffer[len - 1] == '\n' && buffer[len - 2] == '\r') {
|
||||
buffer[len - 2] = '\n';
|
||||
buffer[len - 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int compat_remove(const char* path)
|
||||
{
|
||||
char nativePath[COMPAT_MAX_PATH];
|
||||
strcpy(nativePath, path);
|
||||
compat_windows_path_to_native(nativePath);
|
||||
compat_resolve_path(nativePath);
|
||||
return remove(nativePath);
|
||||
}
|
||||
|
||||
@@ -269,10 +287,12 @@ int compat_rename(const char* oldFileName, const char* newFileName)
|
||||
char nativeOldFileName[COMPAT_MAX_PATH];
|
||||
strcpy(nativeOldFileName, oldFileName);
|
||||
compat_windows_path_to_native(nativeOldFileName);
|
||||
compat_resolve_path(nativeOldFileName);
|
||||
|
||||
char nativeNewFileName[COMPAT_MAX_PATH];
|
||||
strcpy(nativeNewFileName, newFileName);
|
||||
compat_windows_path_to_native(nativeNewFileName);
|
||||
compat_resolve_path(nativeNewFileName);
|
||||
|
||||
return rename(nativeOldFileName, nativeNewFileName);
|
||||
}
|
||||
@@ -290,6 +310,69 @@ void compat_windows_path_to_native(char* path)
|
||||
#endif
|
||||
}
|
||||
|
||||
void compat_resolve_path(char* path)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
char* pch = path;
|
||||
|
||||
DIR* dir;
|
||||
if (pch[0] == '/') {
|
||||
dir = opendir("/");
|
||||
pch++;
|
||||
} else {
|
||||
dir = opendir(".");
|
||||
}
|
||||
|
||||
while (dir != NULL) {
|
||||
char* sep = strchr(pch, '/');
|
||||
size_t length;
|
||||
if (sep != NULL) {
|
||||
length = sep - pch;
|
||||
} else {
|
||||
length = strlen(pch);
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
|
||||
struct dirent* entry = readdir(dir);
|
||||
while (entry != NULL) {
|
||||
if (strlen(entry->d_name) == length && compat_strnicmp(pch, entry->d_name, length) == 0) {
|
||||
strncpy(pch, entry->d_name, length);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
entry = readdir(dir);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
dir = NULL;
|
||||
|
||||
if (!found) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (sep == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
*sep = '\0';
|
||||
dir = opendir(path);
|
||||
*sep = '/';
|
||||
|
||||
pch = sep + 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int compat_access(const char* path, int mode)
|
||||
{
|
||||
char nativePath[COMPAT_MAX_PATH];
|
||||
strcpy(nativePath, path);
|
||||
compat_windows_path_to_native(nativePath);
|
||||
compat_resolve_path(nativePath);
|
||||
return access(nativePath, mode);
|
||||
}
|
||||
|
||||
char* compat_strdup(const char* string)
|
||||
{
|
||||
return SDL_strdup(string);
|
||||
|
||||
@@ -29,18 +29,19 @@ char* compat_strlwr(char* string);
|
||||
char* compat_itoa(int value, char* buffer, int radix);
|
||||
void compat_splitpath(const char* path, char* drive, char* dir, char* fname, char* ext);
|
||||
void compat_makepath(char* path, const char* drive, const char* dir, const char* fname, const char* ext);
|
||||
int compat_read(int fileHandle, void* buf, unsigned int size);
|
||||
int compat_write(int fileHandle, const void* buf, unsigned int size);
|
||||
long compat_lseek(int fileHandle, long offset, int origin);
|
||||
long compat_tell(int fileHandle);
|
||||
long compat_filelength(int fd);
|
||||
int compat_mkdir(const char* path);
|
||||
unsigned int compat_timeGetTime();
|
||||
FILE* compat_fopen(const char* path, const char* mode);
|
||||
gzFile compat_gzopen(const char* path, const char* mode);
|
||||
char* compat_fgets(char* buffer, int maxCount, FILE* stream);
|
||||
char* compat_gzgets(gzFile stream, char* buffer, int maxCount);
|
||||
int compat_remove(const char* path);
|
||||
int compat_rename(const char* oldFileName, const char* newFileName);
|
||||
void compat_windows_path_to_native(char* path);
|
||||
void compat_resolve_path(char* path);
|
||||
int compat_access(const char* path, int mode);
|
||||
char* compat_strdup(const char* string);
|
||||
long getFileSize(FILE* stream);
|
||||
|
||||
|
||||
+1586
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
||||
#ifndef FALLOUT_PREFERENCES_H_
|
||||
#define FALLOUT_PREFERENCES_H_
|
||||
|
||||
#include "db.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
int preferencesInit();
|
||||
int doPreferences(bool animated);
|
||||
int preferencesSave(File* stream);
|
||||
int preferencesLoad(File* stream);
|
||||
void brightnessIncrease();
|
||||
void brightnessDecrease();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* FALLOUT_PREFERENCES_H_ */
|
||||
+8
-4
@@ -249,6 +249,12 @@ int _proto_list_str(int pid, char* proto_path)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x49E984
|
||||
size_t proto_size(int type)
|
||||
{
|
||||
return type >= 0 && type < OBJ_TYPE_COUNT ? _proto_sizes[type] : 0;
|
||||
}
|
||||
|
||||
// 0x49E99C
|
||||
bool _proto_action_can_use(int pid)
|
||||
{
|
||||
@@ -1704,12 +1710,10 @@ static int _proto_load_pid(int pid, Proto** protoPtr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// allocate memory for proto of given type and adds it to proto cache
|
||||
// 0x4A1D98
|
||||
static int _proto_find_free_subnode(int type, Proto** protoPtr)
|
||||
{
|
||||
size_t size = (type >= 0 && type < 11) ? _proto_sizes[type] : 0;
|
||||
|
||||
Proto* proto = (Proto*)internal_malloc(size);
|
||||
Proto* proto = (Proto*)internal_malloc(proto_size(type));
|
||||
*protoPtr = proto;
|
||||
if (proto == NULL) {
|
||||
return -1;
|
||||
|
||||
@@ -104,6 +104,7 @@ extern char* _proto_none_str;
|
||||
|
||||
void _proto_make_path(char* path, int pid);
|
||||
int _proto_list_str(int pid, char* proto_path);
|
||||
size_t proto_size(int type);
|
||||
bool _proto_action_can_use(int pid);
|
||||
bool _proto_action_can_use_on(int pid);
|
||||
bool _proto_action_can_talk_to(int pid);
|
||||
|
||||
@@ -1766,7 +1766,7 @@ int _obj_use_door(Object* a1, Object* a2, int a3)
|
||||
animationRegisterCallback(a2, a2, (AnimationCallback*)_set_door_state_open, -1);
|
||||
}
|
||||
|
||||
const char* sfx = sfxBuildOpenName(a2, SCENERY_SOUND_EFFECT_CLOSED);
|
||||
const char* sfx = sfxBuildOpenName(a2, SCENERY_SOUND_EFFECT_OPEN);
|
||||
animationRegisterPlaySoundEffect(a2, sfx, -1);
|
||||
|
||||
animationRegisterAnimate(a2, ANIM_STAND, 0);
|
||||
|
||||
+45
-26
@@ -29,6 +29,9 @@
|
||||
#include "proto.h"
|
||||
#include "proto_instance.h"
|
||||
#include "queue.h"
|
||||
#include "sfall_arrays.h"
|
||||
#include "sfall_config.h"
|
||||
#include "sfall_global_scripts.h"
|
||||
#include "stat.h"
|
||||
#include "svga.h"
|
||||
#include "tile.h"
|
||||
@@ -143,7 +146,7 @@ static const int gGameTimeDaysPerMonth[12] = {
|
||||
};
|
||||
|
||||
// 0x51C758
|
||||
static const char* gScriptProcNames[28] = {
|
||||
const char* gScriptProcNames[SCRIPT_PROC_COUNT] = {
|
||||
"no_p_proc",
|
||||
"start",
|
||||
"spatial_p_proc",
|
||||
@@ -265,6 +268,15 @@ static bool _set;
|
||||
// 0x667750
|
||||
static char _tempStr1[20];
|
||||
|
||||
static int gStartYear;
|
||||
static int gStartMonth;
|
||||
static int gStartDay;
|
||||
|
||||
static int gMovieTimerArtimer1;
|
||||
static int gMovieTimerArtimer2;
|
||||
static int gMovieTimerArtimer3;
|
||||
static int gMovieTimerArtimer4;
|
||||
|
||||
// TODO: Make unsigned.
|
||||
//
|
||||
// Returns game time in ticks (1/10 second).
|
||||
@@ -278,9 +290,9 @@ int gameTimeGetTime()
|
||||
// 0x4A3338
|
||||
void gameTimeGetDate(int* monthPtr, int* dayPtr, int* yearPtr)
|
||||
{
|
||||
int year = (gGameTime / GAME_TIME_TICKS_PER_DAY + 24) / 365 + 2241;
|
||||
int month = 6;
|
||||
int day = (gGameTime / GAME_TIME_TICKS_PER_DAY + 24) % 365;
|
||||
int year = (gGameTime / GAME_TIME_TICKS_PER_DAY + gStartDay) / 365 + gStartYear;
|
||||
int month = gStartMonth;
|
||||
int day = (gGameTime / GAME_TIME_TICKS_PER_DAY + gStartDay) % 365;
|
||||
|
||||
while (1) {
|
||||
int daysInMonth = gGameTimeDaysPerMonth[month];
|
||||
@@ -439,7 +451,7 @@ int _scriptsCheckGameEvents(int* moviePtr, int window)
|
||||
movieFlags = GAME_MOVIE_FADE_IN | GAME_MOVIE_STOP_MUSIC;
|
||||
endgame = true;
|
||||
} else {
|
||||
if (day >= 360 || gameGetGlobalVar(GVAR_FALLOUT_2) >= 3) {
|
||||
if (day >= gMovieTimerArtimer4 || gameGetGlobalVar(GVAR_FALLOUT_2) >= 3) {
|
||||
movie = MOVIE_ARTIMER4;
|
||||
if (!gameMovieIsSeen(MOVIE_ARTIMER4)) {
|
||||
adjustRep = true;
|
||||
@@ -447,13 +459,13 @@ int _scriptsCheckGameEvents(int* moviePtr, int window)
|
||||
wmAreaSetVisibleState(CITY_DESTROYED_ARROYO, 1, 1);
|
||||
wmAreaMarkVisitedState(CITY_DESTROYED_ARROYO, 2);
|
||||
}
|
||||
} else if (day >= 270 && gameGetGlobalVar(GVAR_FALLOUT_2) != 3) {
|
||||
} else if (day >= gMovieTimerArtimer3 && gameGetGlobalVar(GVAR_FALLOUT_2) != 3) {
|
||||
adjustRep = true;
|
||||
movie = MOVIE_ARTIMER3;
|
||||
} else if (day >= 180 && gameGetGlobalVar(GVAR_FALLOUT_2) != 3) {
|
||||
} else if (day >= gMovieTimerArtimer2 && gameGetGlobalVar(GVAR_FALLOUT_2) != 3) {
|
||||
adjustRep = true;
|
||||
movie = MOVIE_ARTIMER2;
|
||||
} else if (day >= 90 && gameGetGlobalVar(GVAR_FALLOUT_2) != 3) {
|
||||
} else if (day >= gMovieTimerArtimer1 && gameGetGlobalVar(GVAR_FALLOUT_2) != 3) {
|
||||
adjustRep = true;
|
||||
movie = MOVIE_ARTIMER1;
|
||||
}
|
||||
@@ -1017,6 +1029,8 @@ int scriptsHandleRequests()
|
||||
inventoryOpenStealing(gScriptsRequestedStealingBy, gScriptsRequestedStealingFrom);
|
||||
}
|
||||
|
||||
DeleteAllTempArrays();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1522,6 +1536,15 @@ int scriptsInit()
|
||||
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SCRIPT, &gScrMessageList);
|
||||
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_START_YEAR, &gStartYear);
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_START_MONTH, &gStartMonth);
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_START_DAY, &gStartDay);
|
||||
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER1, &gMovieTimerArtimer1);
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER2, &gMovieTimerArtimer2);
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER3, &gMovieTimerArtimer3);
|
||||
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER4, &gMovieTimerArtimer4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2337,37 +2360,30 @@ int _scr_remove_all()
|
||||
_queue_clear_type(EVENT_TYPE_SCRIPT, NULL);
|
||||
_scr_message_free();
|
||||
|
||||
for (int scrType = 0; scrType < SCRIPT_TYPE_COUNT; scrType++) {
|
||||
ScriptList* scriptList = &(gScriptLists[scrType]);
|
||||
for (int scriptType = 0; scriptType < SCRIPT_TYPE_COUNT; scriptType++) {
|
||||
ScriptList* scriptList = &(gScriptLists[scriptType]);
|
||||
|
||||
// TODO: Super odd way to remove scripts. The problem is that [scrRemove]
|
||||
// does relocate scripts between extents, so current extent may become
|
||||
// empty. In addition there is a 0x10 flag on the script that is not
|
||||
// removed. Find a way to refactor this.
|
||||
ScriptListExtent* scriptListExtent = scriptList->head;
|
||||
while (scriptListExtent != NULL) {
|
||||
ScriptListExtent* next = NULL;
|
||||
for (int scriptIndex = 0; scriptIndex < scriptListExtent->length;) {
|
||||
Script* script = &(scriptListExtent->scripts[scriptIndex]);
|
||||
int index = 0;
|
||||
while (scriptListExtent != NULL && index < scriptListExtent->length) {
|
||||
Script* script = &(scriptListExtent->scripts[index]);
|
||||
|
||||
if ((script->flags & SCRIPT_FLAG_0x10) != 0) {
|
||||
scriptIndex++;
|
||||
index++;
|
||||
} else {
|
||||
if (scriptIndex != 0 || scriptListExtent->length != 1) {
|
||||
if (index == 0 && scriptListExtent->length == 1) {
|
||||
scriptListExtent = scriptListExtent->next;
|
||||
scriptRemove(script->sid);
|
||||
} else {
|
||||
next = scriptListExtent->next;
|
||||
scriptRemove(script->sid);
|
||||
|
||||
// CE: Current extent is freed in |scriptRemove|. Break
|
||||
// to prevent next iteration which needs to dereference
|
||||
// extent to obtain it's length.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scriptListExtent = next;
|
||||
if (scriptListExtent != NULL) {
|
||||
scriptListExtent = scriptListExtent->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2574,6 +2590,9 @@ void scriptsExecMapUpdateProc()
|
||||
// 0x4A67EC
|
||||
void scriptsExecMapUpdateScripts(int proc)
|
||||
{
|
||||
// SFALL: Run global scripts.
|
||||
sfall_gl_scr_exec_map_update_scripts(proc);
|
||||
|
||||
_scr_SpatialsEnabled = false;
|
||||
|
||||
int fixedParam = 0;
|
||||
|
||||
@@ -145,6 +145,8 @@ typedef struct Script {
|
||||
int field_DC;
|
||||
} Script;
|
||||
|
||||
extern const char* gScriptProcNames[SCRIPT_PROC_COUNT];
|
||||
|
||||
int gameTimeGetTime();
|
||||
void gameTimeGetDate(int* monthPtr, int* dayPtr, int* yearPtr);
|
||||
int gameTimeGetHour();
|
||||
|
||||
@@ -0,0 +1,697 @@
|
||||
#include "sfall_arrays.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "interpreter.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
static constexpr ArrayId kInitialArrayId = 1;
|
||||
|
||||
#define ARRAY_MAX_STRING (255) // maximum length of string to be stored as array key or value
|
||||
#define ARRAY_MAX_SIZE (100000) // maximum number of array elements,
|
||||
|
||||
// special actions for arrays using array_resize operator
|
||||
#define ARRAY_ACTION_SORT (-2)
|
||||
#define ARRAY_ACTION_RSORT (-3)
|
||||
#define ARRAY_ACTION_REVERSE (-4)
|
||||
#define ARRAY_ACTION_SHUFFLE (-5)
|
||||
|
||||
template <class T, typename Compare>
|
||||
static void ListSort(std::vector<T>& arr, int type, Compare cmp)
|
||||
{
|
||||
switch (type) {
|
||||
case ARRAY_ACTION_SORT: // sort ascending
|
||||
std::sort(arr.begin(), arr.end(), cmp);
|
||||
break;
|
||||
case ARRAY_ACTION_RSORT: // sort descending
|
||||
std::sort(arr.rbegin(), arr.rend(), cmp);
|
||||
break;
|
||||
case ARRAY_ACTION_REVERSE: // reverse elements
|
||||
std::reverse(arr.rbegin(), arr.rend());
|
||||
break;
|
||||
case ARRAY_ACTION_SHUFFLE: // shuffle elements
|
||||
std::random_device rd;
|
||||
std::mt19937 g(rd());
|
||||
std::shuffle(arr.begin(), arr.end(), g);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
enum class ArrayElementType {
|
||||
INT,
|
||||
FLOAT,
|
||||
STRING,
|
||||
POINTER
|
||||
};
|
||||
|
||||
/**
|
||||
* This is mostly the same as ProgramValue but it owns strings.
|
||||
*
|
||||
* This is done because when we pop dynamic string element from
|
||||
* the stack we decrease ref count for this string and it memory
|
||||
* can be freed.
|
||||
*
|
||||
* In theory arrays can be shared between programs so we also
|
||||
* have to copy static strings.
|
||||
*
|
||||
*/
|
||||
class ArrayElement {
|
||||
public:
|
||||
ArrayElement()
|
||||
: type(ArrayElementType::INT)
|
||||
, value({ 0 })
|
||||
{
|
||||
}
|
||||
|
||||
ArrayElement(const ArrayElement& other) = delete;
|
||||
|
||||
ArrayElement& operator=(const ArrayElement& rhs) = delete;
|
||||
|
||||
ArrayElement(ArrayElement&& other) noexcept
|
||||
: type(ArrayElementType::INT)
|
||||
, value({ 0 })
|
||||
{
|
||||
std::swap(type, other.type);
|
||||
std::swap(value, other.value);
|
||||
}
|
||||
|
||||
ArrayElement& operator=(ArrayElement&& other) noexcept
|
||||
{
|
||||
std::swap(type, other.type);
|
||||
std::swap(value, other.value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ArrayElement(ProgramValue programValue, Program* program)
|
||||
{
|
||||
switch (programValue.opcode) {
|
||||
case VALUE_TYPE_INT:
|
||||
type = ArrayElementType::INT;
|
||||
value.integerValue = programValue.integerValue;
|
||||
break;
|
||||
case VALUE_TYPE_FLOAT:
|
||||
type = ArrayElementType::FLOAT;
|
||||
value.floatValue = programValue.floatValue;
|
||||
break;
|
||||
case VALUE_TYPE_PTR:
|
||||
type = ArrayElementType::POINTER;
|
||||
value.pointerValue = programValue.pointerValue;
|
||||
break;
|
||||
case VALUE_TYPE_STRING:
|
||||
case VALUE_TYPE_DYNAMIC_STRING:
|
||||
setString(programGetString(program, programValue.opcode, programValue.integerValue), -1);
|
||||
break;
|
||||
default:
|
||||
type = ArrayElementType::INT;
|
||||
value.integerValue = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayElement(const char* str)
|
||||
{
|
||||
setString(str, -1);
|
||||
}
|
||||
|
||||
ArrayElement(const char* str, size_t sLen)
|
||||
{
|
||||
setString(str, sLen);
|
||||
}
|
||||
|
||||
ProgramValue toValue(Program* program) const
|
||||
{
|
||||
ProgramValue out;
|
||||
switch (type) {
|
||||
case ArrayElementType::INT:
|
||||
out.opcode = VALUE_TYPE_INT;
|
||||
out.integerValue = value.integerValue;
|
||||
break;
|
||||
case ArrayElementType::FLOAT:
|
||||
out.opcode = VALUE_TYPE_FLOAT;
|
||||
out.floatValue = value.floatValue;
|
||||
break;
|
||||
case ArrayElementType::POINTER:
|
||||
out.opcode = VALUE_TYPE_PTR;
|
||||
out.pointerValue = value.pointerValue;
|
||||
break;
|
||||
case ArrayElementType::STRING:
|
||||
out.opcode = VALUE_TYPE_DYNAMIC_STRING;
|
||||
out.integerValue = programPushString(program, value.stringValue);
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool operator<(ArrayElement const& other) const
|
||||
{
|
||||
if (type != other.type) {
|
||||
return type < other.type;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case ArrayElementType::INT:
|
||||
return value.integerValue < other.value.integerValue;
|
||||
case ArrayElementType::FLOAT:
|
||||
return value.floatValue < other.value.floatValue;
|
||||
case ArrayElementType::POINTER:
|
||||
return value.pointerValue < other.value.pointerValue;
|
||||
case ArrayElementType::STRING:
|
||||
return strcmp(value.stringValue, other.value.stringValue) < 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(ArrayElement const& other) const
|
||||
{
|
||||
if (type != other.type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case ArrayElementType::INT:
|
||||
return value.integerValue == other.value.integerValue;
|
||||
case ArrayElementType::FLOAT:
|
||||
return value.floatValue == other.value.floatValue;
|
||||
case ArrayElementType::POINTER:
|
||||
return value.pointerValue == other.value.pointerValue;
|
||||
case ArrayElementType::STRING:
|
||||
return strcmp(value.stringValue, other.value.stringValue) == 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
~ArrayElement()
|
||||
{
|
||||
if (type == ArrayElementType::STRING) {
|
||||
free(value.stringValue);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void setString(const char* str, size_t sLen)
|
||||
{
|
||||
type = ArrayElementType::STRING;
|
||||
|
||||
if (sLen == -1) {
|
||||
sLen = strlen(str);
|
||||
}
|
||||
|
||||
if (sLen >= ARRAY_MAX_STRING) {
|
||||
sLen = ARRAY_MAX_STRING - 1; // memory safety
|
||||
}
|
||||
|
||||
value.stringValue = (char*)malloc(sLen + 1);
|
||||
memcpy(value.stringValue, str, sLen);
|
||||
value.stringValue[sLen] = '\0';
|
||||
}
|
||||
|
||||
ArrayElementType type;
|
||||
union {
|
||||
int integerValue;
|
||||
float floatValue;
|
||||
char* stringValue;
|
||||
void* pointerValue;
|
||||
} value;
|
||||
};
|
||||
|
||||
class SFallArray {
|
||||
public:
|
||||
SFallArray(unsigned int flags)
|
||||
: flags(flags)
|
||||
{
|
||||
}
|
||||
virtual ~SFallArray() = default;
|
||||
virtual ProgramValue GetArrayKey(int index, Program* program) = 0;
|
||||
virtual ProgramValue GetArray(const ProgramValue& key, Program* program) = 0;
|
||||
virtual void SetArray(const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program) = 0;
|
||||
virtual void SetArray(const ProgramValue& key, ArrayElement&& val, bool allowUnset) = 0;
|
||||
virtual void ResizeArray(int newLen) = 0;
|
||||
virtual ProgramValue ScanArray(const ProgramValue& value, Program* program) = 0;
|
||||
virtual int size() = 0;
|
||||
|
||||
bool isReadOnly() const
|
||||
{
|
||||
return (flags & SFALL_ARRAYFLAG_CONSTVAL) != 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
class SFallArrayList : public SFallArray {
|
||||
public:
|
||||
SFallArrayList() = delete;
|
||||
|
||||
SFallArrayList(unsigned int len, unsigned int flags)
|
||||
: SFallArray(flags)
|
||||
{
|
||||
values.resize(len);
|
||||
}
|
||||
|
||||
int size()
|
||||
{
|
||||
return static_cast<int>(values.size());
|
||||
}
|
||||
|
||||
ProgramValue GetArrayKey(int index, Program* program)
|
||||
{
|
||||
if (index < -1 || index > size()) {
|
||||
return ProgramValue(0);
|
||||
}
|
||||
|
||||
if (index == -1) { // special index to indicate if array is associative
|
||||
return ProgramValue(0);
|
||||
}
|
||||
|
||||
return ProgramValue(index);
|
||||
}
|
||||
|
||||
ProgramValue GetArray(const ProgramValue& key, Program* program)
|
||||
{
|
||||
auto index = key.asInt();
|
||||
if (index < 0 || index >= size()) {
|
||||
return ProgramValue(0);
|
||||
}
|
||||
|
||||
return values[index].toValue(program);
|
||||
}
|
||||
|
||||
void SetArray(const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program)
|
||||
{
|
||||
SetArray(key, ArrayElement { val, program }, allowUnset);
|
||||
}
|
||||
|
||||
void SetArray(const ProgramValue& key, ArrayElement&& val, bool allowUnset)
|
||||
{
|
||||
if (key.isInt()) {
|
||||
auto index = key.asInt();
|
||||
if (index >= 0 && index < size()) {
|
||||
std::swap(values[index], val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResizeArray(int newLen)
|
||||
{
|
||||
if (newLen == -1 || size() == newLen) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newLen == 0) {
|
||||
values.clear();
|
||||
} else if (newLen > 0) {
|
||||
if (newLen > ARRAY_MAX_SIZE) {
|
||||
newLen = ARRAY_MAX_SIZE; // safety
|
||||
}
|
||||
|
||||
values.resize(newLen);
|
||||
} else if (newLen >= ARRAY_ACTION_SHUFFLE) {
|
||||
ListSort(values, newLen, std::less<ArrayElement>());
|
||||
}
|
||||
}
|
||||
|
||||
ProgramValue ScanArray(const ProgramValue& value, Program* program)
|
||||
{
|
||||
auto element = ArrayElement { value, program };
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (element == values[i]) {
|
||||
return ProgramValue(i);
|
||||
}
|
||||
}
|
||||
return ProgramValue(-1);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<ArrayElement> values;
|
||||
};
|
||||
|
||||
class SFallArrayAssoc : public SFallArray {
|
||||
public:
|
||||
SFallArrayAssoc() = delete;
|
||||
|
||||
SFallArrayAssoc(unsigned int flags)
|
||||
: SFallArray(flags)
|
||||
{
|
||||
}
|
||||
|
||||
int size()
|
||||
{
|
||||
return static_cast<int>(pairs.size());
|
||||
}
|
||||
|
||||
ProgramValue GetArrayKey(int index, Program* program)
|
||||
{
|
||||
if (index < -1 || index > size()) {
|
||||
return ProgramValue(0);
|
||||
}
|
||||
|
||||
if (index == -1) { // special index to indicate if array is associative
|
||||
return ProgramValue(1);
|
||||
}
|
||||
|
||||
return pairs[index].key.toValue(program);
|
||||
}
|
||||
|
||||
ProgramValue GetArray(const ProgramValue& key, Program* program)
|
||||
{
|
||||
auto keyEl = ArrayElement { key, program };
|
||||
auto it = std::find_if(pairs.begin(), pairs.end(), [&keyEl](const KeyValuePair& pair) -> bool {
|
||||
return pair.key == keyEl;
|
||||
});
|
||||
|
||||
if (it == pairs.end()) {
|
||||
return ProgramValue(0);
|
||||
}
|
||||
|
||||
auto index = it - pairs.begin();
|
||||
return pairs[index].value.toValue(program);
|
||||
}
|
||||
|
||||
void SetArray(const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program)
|
||||
{
|
||||
auto keyEl = ArrayElement { key, program };
|
||||
auto it = std::find_if(pairs.begin(), pairs.end(), [&keyEl](const KeyValuePair& pair) -> bool {
|
||||
return pair.key == keyEl;
|
||||
});
|
||||
|
||||
if (it != pairs.end() && isReadOnly()) {
|
||||
// don't update value of key
|
||||
return;
|
||||
}
|
||||
|
||||
if (allowUnset && !isReadOnly() && val.isInt() && val.asInt() == 0) {
|
||||
// after assigning zero to a key, no need to store it, because "get_array" returns 0 for non-existent keys: try unset
|
||||
if (it != pairs.end()) {
|
||||
pairs.erase(it);
|
||||
}
|
||||
} else {
|
||||
if (it == pairs.end()) {
|
||||
// size check
|
||||
if (size() >= ARRAY_MAX_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
pairs.push_back(KeyValuePair { std::move(keyEl), ArrayElement { val, program } });
|
||||
} else {
|
||||
auto index = it - pairs.begin();
|
||||
pairs[index].value = ArrayElement { val, program };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetArray(const ProgramValue& key, ArrayElement&& val, bool allowUnset)
|
||||
{
|
||||
assert(false && "This method is not used for associative arrays thus it is not implemented");
|
||||
}
|
||||
|
||||
void ResizeArray(int newLen)
|
||||
{
|
||||
if (newLen == -1 || size() == newLen) {
|
||||
return;
|
||||
}
|
||||
|
||||
// only allow to reduce number of elements (adding range of elements is meaningless for maps)
|
||||
if (newLen >= 0 && newLen < size()) {
|
||||
pairs.resize(newLen);
|
||||
} else if (newLen < 0) {
|
||||
if (newLen < (ARRAY_ACTION_SHUFFLE - 2)) return;
|
||||
MapSort(newLen);
|
||||
}
|
||||
}
|
||||
|
||||
ProgramValue ScanArray(const ProgramValue& value, Program* program)
|
||||
{
|
||||
auto valueEl = ArrayElement { value, program };
|
||||
auto it = std::find_if(pairs.begin(), pairs.end(), [&valueEl](const KeyValuePair& pair) {
|
||||
return pair.value == valueEl;
|
||||
});
|
||||
|
||||
if (it == pairs.end()) {
|
||||
return ProgramValue(-1);
|
||||
}
|
||||
|
||||
return it->key.toValue(program);
|
||||
}
|
||||
|
||||
private:
|
||||
struct KeyValuePair {
|
||||
ArrayElement key;
|
||||
ArrayElement value;
|
||||
};
|
||||
|
||||
void MapSort(int type)
|
||||
{
|
||||
bool sortByValue = false;
|
||||
if (type < ARRAY_ACTION_SHUFFLE) {
|
||||
type += 4;
|
||||
sortByValue = true;
|
||||
}
|
||||
|
||||
if (sortByValue) {
|
||||
ListSort(pairs, type, [](const KeyValuePair& a, const KeyValuePair& b) -> bool {
|
||||
return a.value < b.value;
|
||||
});
|
||||
} else {
|
||||
ListSort(pairs, type, [](const KeyValuePair& a, const KeyValuePair& b) -> bool {
|
||||
return a.key < b.key;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<KeyValuePair> pairs;
|
||||
};
|
||||
|
||||
struct SfallArraysState {
|
||||
std::unordered_map<ArrayId, std::unique_ptr<SFallArray>> arrays;
|
||||
std::unordered_set<ArrayId> temporaryArrayIds;
|
||||
int nextArrayId = kInitialArrayId;
|
||||
};
|
||||
|
||||
static SfallArraysState* _state = nullptr;
|
||||
|
||||
bool sfallArraysInit()
|
||||
{
|
||||
_state = new (std::nothrow) SfallArraysState();
|
||||
if (_state == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void sfallArraysReset()
|
||||
{
|
||||
if (_state != nullptr) {
|
||||
_state->arrays.clear();
|
||||
_state->temporaryArrayIds.clear();
|
||||
_state->nextArrayId = kInitialArrayId;
|
||||
}
|
||||
}
|
||||
|
||||
void sfallArraysExit()
|
||||
{
|
||||
if (_state != nullptr) {
|
||||
delete _state;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayId CreateArray(int len, unsigned int flags)
|
||||
{
|
||||
flags = (flags & ~1); // reset 1 bit
|
||||
|
||||
if (len < 0) {
|
||||
flags |= SFALL_ARRAYFLAG_ASSOC;
|
||||
} else if (len > ARRAY_MAX_SIZE) {
|
||||
len = ARRAY_MAX_SIZE; // safecheck
|
||||
}
|
||||
|
||||
ArrayId arrayId = _state->nextArrayId++;
|
||||
|
||||
if (flags & SFALL_ARRAYFLAG_ASSOC) {
|
||||
_state->arrays.emplace(std::make_pair(arrayId, std::make_unique<SFallArrayAssoc>(flags)));
|
||||
} else {
|
||||
_state->arrays.emplace(std::make_pair(arrayId, std::make_unique<SFallArrayList>(len, flags)));
|
||||
}
|
||||
|
||||
return arrayId;
|
||||
}
|
||||
|
||||
ArrayId CreateTempArray(int len, unsigned int flags)
|
||||
{
|
||||
ArrayId arrayId = CreateArray(len, flags);
|
||||
_state->temporaryArrayIds.insert(arrayId);
|
||||
return arrayId;
|
||||
}
|
||||
|
||||
SFallArray* get_array_by_id(ArrayId arrayId)
|
||||
{
|
||||
auto it = _state->arrays.find(arrayId);
|
||||
if (it == _state->arrays.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
ProgramValue GetArrayKey(ArrayId arrayId, int index, Program* program)
|
||||
{
|
||||
auto arr = get_array_by_id(arrayId);
|
||||
if (arr == nullptr) {
|
||||
return ProgramValue(0);
|
||||
}
|
||||
|
||||
return arr->GetArrayKey(index, program);
|
||||
}
|
||||
|
||||
int LenArray(ArrayId arrayId)
|
||||
{
|
||||
auto arr = get_array_by_id(arrayId);
|
||||
if (arr == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return arr->size();
|
||||
}
|
||||
|
||||
ProgramValue GetArray(ArrayId arrayId, const ProgramValue& key, Program* program)
|
||||
{
|
||||
auto arr = get_array_by_id(arrayId);
|
||||
if (arr == nullptr) {
|
||||
return ProgramValue(0);
|
||||
}
|
||||
|
||||
return arr->GetArray(key, program);
|
||||
}
|
||||
|
||||
void SetArray(ArrayId arrayId, const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program)
|
||||
{
|
||||
auto arr = get_array_by_id(arrayId);
|
||||
if (arr == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
arr->SetArray(key, val, allowUnset, program);
|
||||
}
|
||||
|
||||
void FreeArray(ArrayId arrayId)
|
||||
{
|
||||
// TODO: remove from saved_arrays
|
||||
_state->arrays.erase(arrayId);
|
||||
}
|
||||
|
||||
void FixArray(ArrayId arrayId)
|
||||
{
|
||||
_state->temporaryArrayIds.erase(arrayId);
|
||||
}
|
||||
|
||||
void DeleteAllTempArrays()
|
||||
{
|
||||
for (auto it = _state->temporaryArrayIds.begin(); it != _state->temporaryArrayIds.end(); ++it) {
|
||||
FreeArray(*it);
|
||||
}
|
||||
_state->temporaryArrayIds.clear();
|
||||
}
|
||||
|
||||
void ResizeArray(ArrayId arrayId, int newLen)
|
||||
{
|
||||
auto arr = get_array_by_id(arrayId);
|
||||
if (arr == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
arr->ResizeArray(newLen);
|
||||
}
|
||||
|
||||
int StackArray(const ProgramValue& key, const ProgramValue& val, Program* program)
|
||||
{
|
||||
// CE: Sfall uses eponymous global variable which is always the id of the
|
||||
// last created array.
|
||||
ArrayId stackArrayId = _state->nextArrayId - 1;
|
||||
|
||||
auto arr = get_array_by_id(stackArrayId);
|
||||
if (arr == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto size = arr->size();
|
||||
if (size >= ARRAY_MAX_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (key.asInt() >= size) {
|
||||
arr->ResizeArray(size + 1);
|
||||
}
|
||||
|
||||
SetArray(stackArrayId, key, val, false, program);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ProgramValue ScanArray(ArrayId arrayId, const ProgramValue& val, Program* program)
|
||||
{
|
||||
auto arr = get_array_by_id(arrayId);
|
||||
if (arr == nullptr) {
|
||||
return ProgramValue(-1);
|
||||
}
|
||||
|
||||
return arr->ScanArray(val, program);
|
||||
}
|
||||
|
||||
ArrayId StringSplit(const char* str, const char* split)
|
||||
{
|
||||
size_t splitLen = strlen(split);
|
||||
|
||||
ArrayId arrayId = CreateTempArray(0, 0);
|
||||
auto arr = get_array_by_id(arrayId);
|
||||
|
||||
if (splitLen == 0) {
|
||||
int count = static_cast<int>(strlen(str));
|
||||
|
||||
arr->ResizeArray(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
arr->SetArray(ProgramValue { i }, ArrayElement { &str[i], 1 }, false);
|
||||
}
|
||||
} else {
|
||||
int count = 1;
|
||||
const char* ptr = str;
|
||||
while (true) {
|
||||
const char* newptr = strstr(ptr, split);
|
||||
if (newptr == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
count++;
|
||||
ptr = newptr + splitLen;
|
||||
}
|
||||
arr->ResizeArray(count);
|
||||
|
||||
count = 0;
|
||||
ptr = str;
|
||||
while (true) {
|
||||
const char* newptr = strstr(ptr, split);
|
||||
size_t len = (newptr != nullptr) ? newptr - ptr : strlen(ptr);
|
||||
|
||||
arr->SetArray(ProgramValue { count }, ArrayElement { ptr, len }, false);
|
||||
|
||||
if (newptr == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
count++;
|
||||
ptr = newptr + splitLen;
|
||||
}
|
||||
}
|
||||
return arrayId;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef FALLOUT_SFALL_ARRAYS_H_
|
||||
#define FALLOUT_SFALL_ARRAYS_H_
|
||||
|
||||
#include "interpreter.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
#define SFALL_ARRAYFLAG_ASSOC (1) // is map
|
||||
#define SFALL_ARRAYFLAG_CONSTVAL (2) // don't update value of key if the key exists in map
|
||||
#define SFALL_ARRAYFLAG_RESERVED (4)
|
||||
|
||||
using ArrayId = unsigned int;
|
||||
|
||||
bool sfallArraysInit();
|
||||
void sfallArraysReset();
|
||||
void sfallArraysExit();
|
||||
ArrayId CreateArray(int len, unsigned int flags);
|
||||
ArrayId CreateTempArray(int len, unsigned int flags);
|
||||
ProgramValue GetArrayKey(ArrayId arrayId, int index, Program* program);
|
||||
int LenArray(ArrayId arrayId);
|
||||
ProgramValue GetArray(ArrayId arrayId, const ProgramValue& key, Program* program);
|
||||
void SetArray(ArrayId arrayId, const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program);
|
||||
void FreeArray(ArrayId arrayId);
|
||||
void FixArray(ArrayId id);
|
||||
void ResizeArray(ArrayId arrayId, int newLen);
|
||||
void DeleteAllTempArrays();
|
||||
int StackArray(const ProgramValue& key, const ProgramValue& val, Program* program);
|
||||
ProgramValue ScanArray(ArrayId arrayId, const ProgramValue& val, Program* program);
|
||||
|
||||
ArrayId StringSplit(const char* str, const char* split);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* FALLOUT_SFALL_ARRAYS_H_ */
|
||||
@@ -27,6 +27,9 @@ bool sfallConfigInit(int argc, char** argv)
|
||||
configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DUDE_NATIVE_LOOK_JUMPSUIT_FEMALE_KEY, "");
|
||||
configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_MALE_KEY, "");
|
||||
configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_FEMALE_KEY, "");
|
||||
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_START_YEAR, 2241);
|
||||
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_START_MONTH, 6);
|
||||
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_START_DAY, 24);
|
||||
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_BIG_FONT_COLOR_KEY, 0);
|
||||
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_X_KEY, 0);
|
||||
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_Y_KEY, 0);
|
||||
@@ -50,6 +53,13 @@ bool sfallConfigInit(int argc, char** argv)
|
||||
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_TARGET_MULTIPLIER_KEY, SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_MULTIPLIER);
|
||||
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_TARGET_DIVISOR_KEY, SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_DIVISOR);
|
||||
configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_EXTRA_MESSAGE_LISTS_KEY, "");
|
||||
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER1, 90);
|
||||
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER2, 180);
|
||||
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER3, 270);
|
||||
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER4, 360);
|
||||
|
||||
configSetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_INI_CONFIG_FOLDER, "");
|
||||
configSetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_GLOBAL_SCRIPT_PATHS, "");
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
char* executable = argv[0];
|
||||
|
||||
@@ -8,11 +8,15 @@ namespace fallout {
|
||||
#define SFALL_CONFIG_FILE_NAME "ddraw.ini"
|
||||
|
||||
#define SFALL_CONFIG_MISC_KEY "Misc"
|
||||
#define SFALL_CONFIG_SCRIPTS_KEY "Scripts"
|
||||
|
||||
#define SFALL_CONFIG_DUDE_NATIVE_LOOK_JUMPSUIT_MALE_KEY "MaleDefaultModel"
|
||||
#define SFALL_CONFIG_DUDE_NATIVE_LOOK_JUMPSUIT_FEMALE_KEY "FemaleDefaultModel"
|
||||
#define SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_MALE_KEY "MaleStartModel"
|
||||
#define SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_FEMALE_KEY "FemaleStartModel"
|
||||
#define SFALL_CONFIG_START_YEAR "StartYear"
|
||||
#define SFALL_CONFIG_START_MONTH "StartMonth"
|
||||
#define SFALL_CONFIG_START_DAY "StartDay"
|
||||
#define SFALL_CONFIG_MAIN_MENU_BIG_FONT_COLOR_KEY "MainMenuBigFontColour"
|
||||
#define SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_X_KEY "MainMenuCreditsOffsetX"
|
||||
#define SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_Y_KEY "MainMenuCreditsOffsetY"
|
||||
@@ -42,6 +46,10 @@ namespace fallout {
|
||||
#define SFALL_CONFIG_PLASTIC_EXPLOSIVE_MIN_DAMAGE_KEY "PlasticExplosive_DmgMin"
|
||||
#define SFALL_CONFIG_PLASTIC_EXPLOSIVE_MAX_DAMAGE_KEY "PlasticExplosive_DmgMax"
|
||||
#define SFALL_CONFIG_EXPLOSION_EMITS_LIGHT_KEY "ExplosionsEmitLight"
|
||||
#define SFALL_CONFIG_MOVIE_TIMER_ARTIMER1 "MovieTimer_artimer1"
|
||||
#define SFALL_CONFIG_MOVIE_TIMER_ARTIMER2 "MovieTimer_artimer2"
|
||||
#define SFALL_CONFIG_MOVIE_TIMER_ARTIMER3 "MovieTimer_artimer3"
|
||||
#define SFALL_CONFIG_MOVIE_TIMER_ARTIMER4 "MovieTimer_artimer4"
|
||||
#define SFALL_CONFIG_CITY_REPUTATION_LIST_KEY "CityRepsList"
|
||||
#define SFALL_CONFIG_UNARMED_FILE_KEY "UnarmedFile"
|
||||
#define SFALL_CONFIG_DAMAGE_MOD_FORMULA_KEY "DamageFormula"
|
||||
@@ -61,6 +69,8 @@ namespace fallout {
|
||||
#define SFALL_CONFIG_TOWN_MAP_HOTKEYS_FIX_KEY "TownMapHotkeysFix"
|
||||
#define SFALL_CONFIG_EXTRA_MESSAGE_LISTS_KEY "ExtraGameMsgFileList"
|
||||
#define SFALL_CONFIG_NUMBERS_IS_DIALOG_KEY "NumbersInDialogue"
|
||||
#define SFALL_CONFIG_INI_CONFIG_FOLDER "IniConfigFolder"
|
||||
#define SFALL_CONFIG_GLOBAL_SCRIPT_PATHS "GlobalScriptPaths"
|
||||
|
||||
#define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER 1
|
||||
#define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
#include "sfall_global_scripts.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db.h"
|
||||
#include "input.h"
|
||||
#include "platform_compat.h"
|
||||
#include "scripts.h"
|
||||
#include "sfall_config.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
struct GlobalScript {
|
||||
Program* program = nullptr;
|
||||
int procs[SCRIPT_PROC_COUNT] = { 0 };
|
||||
int repeat = 0;
|
||||
int count = 0;
|
||||
int mode = 0;
|
||||
bool once = true;
|
||||
};
|
||||
|
||||
struct GlobalScriptsState {
|
||||
std::vector<std::string> paths;
|
||||
std::vector<GlobalScript> globalScripts;
|
||||
};
|
||||
|
||||
static GlobalScriptsState* state = nullptr;
|
||||
|
||||
bool sfall_gl_scr_init()
|
||||
{
|
||||
state = new (std::nothrow) GlobalScriptsState();
|
||||
if (state == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char* paths;
|
||||
configGetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_GLOBAL_SCRIPT_PATHS, &paths);
|
||||
|
||||
char* curr = paths;
|
||||
while (curr != nullptr && *curr != '\0') {
|
||||
char* end = strchr(curr, ',');
|
||||
if (end != nullptr) {
|
||||
*end = '\0';
|
||||
}
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
strcpy(path, curr);
|
||||
|
||||
char* fname = strrchr(path, '\\');
|
||||
if (fname != nullptr) {
|
||||
fname += 1;
|
||||
} else {
|
||||
fname = path;
|
||||
}
|
||||
|
||||
char** files;
|
||||
int filesLength = fileNameListInit(curr, &files, 0, 0);
|
||||
if (filesLength != 0) {
|
||||
for (int index = 0; index < filesLength; index++) {
|
||||
strcpy(fname, files[index]);
|
||||
state->paths.push_back(std::string { path });
|
||||
}
|
||||
|
||||
fileNameListFree(&files, 0);
|
||||
}
|
||||
|
||||
if (end != nullptr) {
|
||||
*end = ',';
|
||||
curr = end + 1;
|
||||
} else {
|
||||
curr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(state->paths.begin(), state->paths.end());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void sfall_gl_scr_reset()
|
||||
{
|
||||
if (state != nullptr) {
|
||||
sfall_gl_scr_remove_all();
|
||||
}
|
||||
}
|
||||
|
||||
void sfall_gl_scr_exit()
|
||||
{
|
||||
if (state != nullptr) {
|
||||
sfall_gl_scr_remove_all();
|
||||
|
||||
delete state;
|
||||
state = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void sfall_gl_scr_exec_start_proc()
|
||||
{
|
||||
for (auto& path : state->paths) {
|
||||
Program* program = programCreateByPath(path.c_str());
|
||||
if (program != nullptr) {
|
||||
GlobalScript scr;
|
||||
scr.program = program;
|
||||
|
||||
for (int action = 0; action < SCRIPT_PROC_COUNT; action++) {
|
||||
scr.procs[action] = programFindProcedure(program, gScriptProcNames[action]);
|
||||
}
|
||||
|
||||
state->globalScripts.push_back(std::move(scr));
|
||||
|
||||
_interpret(program, -1);
|
||||
}
|
||||
}
|
||||
|
||||
tickersAdd(sfall_gl_scr_process_input);
|
||||
}
|
||||
|
||||
void sfall_gl_scr_remove_all()
|
||||
{
|
||||
tickersRemove(sfall_gl_scr_process_input);
|
||||
|
||||
for (auto& scr : state->globalScripts) {
|
||||
programFree(scr.program);
|
||||
}
|
||||
|
||||
state->globalScripts.clear();
|
||||
}
|
||||
|
||||
void sfall_gl_scr_exec_map_update_scripts(int action)
|
||||
{
|
||||
for (auto& scr : state->globalScripts) {
|
||||
if (scr.mode == 0 || scr.mode == 3) {
|
||||
if (scr.procs[action] != -1) {
|
||||
_executeProcedure(scr.program, scr.procs[action]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sfall_gl_scr_process_simple(int mode1, int mode2)
|
||||
{
|
||||
for (auto& scr : state->globalScripts) {
|
||||
if (scr.repeat != 0 && (scr.mode == mode1 || scr.mode == mode2)) {
|
||||
scr.count++;
|
||||
if (scr.count >= scr.repeat) {
|
||||
_executeProcedure(scr.program, scr.procs[SCRIPT_PROC_START]);
|
||||
scr.count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sfall_gl_scr_process_main()
|
||||
{
|
||||
sfall_gl_scr_process_simple(0, 3);
|
||||
}
|
||||
|
||||
void sfall_gl_scr_process_input()
|
||||
{
|
||||
sfall_gl_scr_process_simple(1, 1);
|
||||
}
|
||||
|
||||
void sfall_gl_scr_process_worldmap()
|
||||
{
|
||||
sfall_gl_scr_process_simple(2, 3);
|
||||
}
|
||||
|
||||
static GlobalScript* sfall_gl_scr_map_program_to_scr(Program* program)
|
||||
{
|
||||
auto it = std::find_if(state->globalScripts.begin(),
|
||||
state->globalScripts.end(),
|
||||
[&program](const GlobalScript& scr) {
|
||||
return scr.program == program;
|
||||
});
|
||||
return it != state->globalScripts.end() ? &(*it) : nullptr;
|
||||
}
|
||||
|
||||
void sfall_gl_scr_set_repeat(Program* program, int frames)
|
||||
{
|
||||
GlobalScript* scr = sfall_gl_scr_map_program_to_scr(program);
|
||||
if (scr != nullptr) {
|
||||
scr->repeat = frames;
|
||||
}
|
||||
}
|
||||
|
||||
void sfall_gl_scr_set_type(Program* program, int type)
|
||||
{
|
||||
if (type < 0 || type > 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
GlobalScript* scr = sfall_gl_scr_map_program_to_scr(program);
|
||||
if (scr != nullptr) {
|
||||
scr->mode = type;
|
||||
}
|
||||
}
|
||||
|
||||
bool sfall_gl_scr_is_loaded(Program* program)
|
||||
{
|
||||
GlobalScript* scr = sfall_gl_scr_map_program_to_scr(program);
|
||||
if (scr != nullptr) {
|
||||
if (scr->once) {
|
||||
scr->once = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not a global script.
|
||||
return true;
|
||||
}
|
||||
|
||||
void sfall_gl_scr_update(int burstSize)
|
||||
{
|
||||
for (auto& scr : state->globalScripts) {
|
||||
_interpret(scr.program, burstSize);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef FALLOUT_SFALL_GLOBAL_SCRIPTS_H_
|
||||
#define FALLOUT_SFALL_GLOBAL_SCRIPTS_H_
|
||||
|
||||
#include "interpreter.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
bool sfall_gl_scr_init();
|
||||
void sfall_gl_scr_reset();
|
||||
void sfall_gl_scr_exit();
|
||||
void sfall_gl_scr_exec_start_proc();
|
||||
void sfall_gl_scr_remove_all();
|
||||
void sfall_gl_scr_exec_map_update_scripts(int action);
|
||||
void sfall_gl_scr_process_main();
|
||||
void sfall_gl_scr_process_input();
|
||||
void sfall_gl_scr_process_worldmap();
|
||||
void sfall_gl_scr_set_repeat(Program* program, int frames);
|
||||
void sfall_gl_scr_set_type(Program* program, int type);
|
||||
bool sfall_gl_scr_is_loaded(Program* program);
|
||||
void sfall_gl_scr_update(int burstSize);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* FALLOUT_SFALL_GLOBAL_SCRIPTS_H_ */
|
||||
+83
-28
@@ -10,72 +10,127 @@ struct SfallGlobalVarsState {
|
||||
std::unordered_map<uint64_t, int> vars;
|
||||
};
|
||||
|
||||
static bool sfallGlobalVarsStore(uint64_t key, int value);
|
||||
static bool sfallGlobalVarsFetch(uint64_t key, int& value);
|
||||
#pragma pack(push)
|
||||
#pragma pack(8)
|
||||
|
||||
static SfallGlobalVarsState* _state;
|
||||
/// Matches Sfall's `GlobalVar` to maintain binary compatibility.
|
||||
struct GlobalVarEntry {
|
||||
uint64_t key;
|
||||
int32_t value;
|
||||
int32_t unused;
|
||||
};
|
||||
|
||||
bool sfallGlobalVarsInit()
|
||||
#pragma pack(pop)
|
||||
|
||||
static bool sfall_gl_vars_store(uint64_t key, int value);
|
||||
static bool sfall_gl_vars_fetch(uint64_t key, int& value);
|
||||
|
||||
static SfallGlobalVarsState* sfall_gl_vars_state = nullptr;
|
||||
|
||||
bool sfall_gl_vars_init()
|
||||
{
|
||||
_state = new (std::nothrow) SfallGlobalVarsState();
|
||||
if (_state == nullptr) {
|
||||
sfall_gl_vars_state = new (std::nothrow) SfallGlobalVarsState();
|
||||
if (sfall_gl_vars_state == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void sfallGlobalVarsReset()
|
||||
void sfall_gl_vars_reset()
|
||||
{
|
||||
_state->vars.clear();
|
||||
sfall_gl_vars_state->vars.clear();
|
||||
}
|
||||
|
||||
void sfallGlobalVarsExit()
|
||||
void sfall_gl_vars_exit()
|
||||
{
|
||||
if (_state != nullptr) {
|
||||
delete _state;
|
||||
_state = nullptr;
|
||||
if (sfall_gl_vars_state != nullptr) {
|
||||
delete sfall_gl_vars_state;
|
||||
sfall_gl_vars_state = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool sfallGlobalVarsStore(const char* key, int value)
|
||||
bool sfall_gl_vars_save(File* stream)
|
||||
{
|
||||
int count = static_cast<int>(sfall_gl_vars_state->vars.size());
|
||||
if (fileWrite(&count, sizeof(count), 1, stream) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GlobalVarEntry entry = { 0 };
|
||||
for (auto& pair : sfall_gl_vars_state->vars) {
|
||||
entry.key = pair.first;
|
||||
entry.value = pair.second;
|
||||
|
||||
if (fileWrite(&entry, sizeof(entry), 1, stream) != 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sfall_gl_vars_load(File* stream)
|
||||
{
|
||||
int count;
|
||||
if (fileRead(&count, sizeof(count), 1, stream) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sfall_gl_vars_state->vars.reserve(count);
|
||||
|
||||
GlobalVarEntry entry;
|
||||
while (count > 0) {
|
||||
if (fileRead(&entry, sizeof(entry), 1, stream) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sfall_gl_vars_state->vars[entry.key] = static_cast<int>(entry.value);
|
||||
|
||||
count--;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sfall_gl_vars_store(const char* key, int value)
|
||||
{
|
||||
if (strlen(key) != 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t numericKey = *(reinterpret_cast<const uint64_t*>(key));
|
||||
return sfallGlobalVarsStore(numericKey, value);
|
||||
return sfall_gl_vars_store(numericKey, value);
|
||||
}
|
||||
|
||||
bool sfallGlobalVarsStore(int key, int value)
|
||||
bool sfall_gl_vars_store(int key, int value)
|
||||
{
|
||||
return sfallGlobalVarsStore(static_cast<uint64_t>(key), value);
|
||||
return sfall_gl_vars_store(static_cast<uint64_t>(key), value);
|
||||
}
|
||||
|
||||
bool sfallGlobalVarsFetch(const char* key, int& value)
|
||||
bool sfall_gl_vars_fetch(const char* key, int& value)
|
||||
{
|
||||
if (strlen(key) != 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t numericKey = *(reinterpret_cast<const uint64_t*>(key));
|
||||
return sfallGlobalVarsFetch(numericKey, value);
|
||||
return sfall_gl_vars_fetch(numericKey, value);
|
||||
}
|
||||
|
||||
bool sfallGlobalVarsFetch(int key, int& value)
|
||||
bool sfall_gl_vars_fetch(int key, int& value)
|
||||
{
|
||||
return sfallGlobalVarsFetch(static_cast<uint64_t>(key), value);
|
||||
return sfall_gl_vars_fetch(static_cast<uint64_t>(key), value);
|
||||
}
|
||||
|
||||
static bool sfallGlobalVarsStore(uint64_t key, int value)
|
||||
static bool sfall_gl_vars_store(uint64_t key, int value)
|
||||
{
|
||||
auto it = _state->vars.find(key);
|
||||
if (it == _state->vars.end()) {
|
||||
_state->vars.emplace(key, value);
|
||||
auto it = sfall_gl_vars_state->vars.find(key);
|
||||
if (it == sfall_gl_vars_state->vars.end()) {
|
||||
sfall_gl_vars_state->vars.emplace(key, value);
|
||||
} else {
|
||||
if (value == 0) {
|
||||
_state->vars.erase(it);
|
||||
sfall_gl_vars_state->vars.erase(it);
|
||||
} else {
|
||||
it->second = value;
|
||||
}
|
||||
@@ -84,10 +139,10 @@ static bool sfallGlobalVarsStore(uint64_t key, int value)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sfallGlobalVarsFetch(uint64_t key, int& value)
|
||||
static bool sfall_gl_vars_fetch(uint64_t key, int& value)
|
||||
{
|
||||
auto it = _state->vars.find(key);
|
||||
if (it == _state->vars.end()) {
|
||||
auto it = sfall_gl_vars_state->vars.find(key);
|
||||
if (it == sfall_gl_vars_state->vars.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+11
-7
@@ -1,15 +1,19 @@
|
||||
#ifndef FALLOUT_SFALL_GLOBAL_VARS_H_
|
||||
#define FALLOUT_SFALL_GLOBAL_VARS_H_
|
||||
|
||||
#include "db.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
bool sfallGlobalVarsInit();
|
||||
void sfallGlobalVarsReset();
|
||||
void sfallGlobalVarsExit();
|
||||
bool sfallGlobalVarsStore(const char* key, int value);
|
||||
bool sfallGlobalVarsStore(int key, int value);
|
||||
bool sfallGlobalVarsFetch(const char* key, int& value);
|
||||
bool sfallGlobalVarsFetch(int key, int& value);
|
||||
bool sfall_gl_vars_init();
|
||||
void sfall_gl_vars_reset();
|
||||
void sfall_gl_vars_exit();
|
||||
bool sfall_gl_vars_save(File* stream);
|
||||
bool sfall_gl_vars_load(File* stream);
|
||||
bool sfall_gl_vars_store(const char* key, int value);
|
||||
bool sfall_gl_vars_store(int key, int value);
|
||||
bool sfall_gl_vars_fetch(const char* key, int& value);
|
||||
bool sfall_gl_vars_fetch(int key, int& value);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
#include "sfall_ini.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "config.h"
|
||||
#include "platform_compat.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
/// The max length of `fileName` chunk in the triplet.
|
||||
static constexpr size_t kFileNameMaxSize = 63;
|
||||
|
||||
/// The max length of `section` chunk in the triplet.
|
||||
static constexpr size_t kSectionMaxSize = 32;
|
||||
|
||||
/// Special .ini file names which are accessed without adding base path.
|
||||
static constexpr const char* kSystemConfigFileNames[] = {
|
||||
"ddraw.ini",
|
||||
"f2_res.ini",
|
||||
};
|
||||
|
||||
static char basePath[COMPAT_MAX_PATH];
|
||||
|
||||
/// Parses "fileName|section|key" triplet into parts. `fileName` and `section`
|
||||
/// chunks are copied into appropriate variables. Returns the pointer to `key`,
|
||||
/// or `nullptr` on any error.
|
||||
static const char* parse_ini_triplet(const char* triplet, char* fileName, char* section)
|
||||
{
|
||||
const char* fileNameSectionSep = strchr(triplet, '|');
|
||||
if (fileNameSectionSep == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t fileNameLength = fileNameSectionSep - triplet;
|
||||
if (fileNameLength > kFileNameMaxSize) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* sectionKeySep = strchr(fileNameSectionSep + 1, '|');
|
||||
if (sectionKeySep == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t sectionLength = sectionKeySep - fileNameSectionSep - 1;
|
||||
if (sectionLength > kSectionMaxSize) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
strncpy(fileName, triplet, fileNameLength);
|
||||
fileName[fileNameLength] = '\0';
|
||||
|
||||
strncpy(section, fileNameSectionSep + 1, sectionLength);
|
||||
section[sectionLength] = '\0';
|
||||
|
||||
return sectionKeySep + 1;
|
||||
}
|
||||
|
||||
/// Returns `true` if given `fileName` is a special system .ini file name.
|
||||
static bool is_system_file_name(const char* fileName)
|
||||
{
|
||||
for (auto& systemFileName : kSystemConfigFileNames) {
|
||||
if (compat_stricmp(systemFileName, fileName) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void sfall_ini_set_base_path(const char* path)
|
||||
{
|
||||
if (path != nullptr) {
|
||||
strcpy(basePath, path);
|
||||
|
||||
size_t length = strlen(basePath);
|
||||
if (length > 0) {
|
||||
if (basePath[length - 1] == '\\' || basePath[length - 1] == '/') {
|
||||
basePath[length - 1] = '\0';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
basePath[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
bool sfall_ini_get_int(const char* triplet, int* value)
|
||||
{
|
||||
char string[20];
|
||||
if (!sfall_ini_get_string(triplet, string, sizeof(string))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*value = atol(string);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sfall_ini_get_string(const char* triplet, char* value, size_t size)
|
||||
{
|
||||
char fileName[kFileNameMaxSize];
|
||||
char section[kSectionMaxSize];
|
||||
|
||||
const char* key = parse_ini_triplet(triplet, fileName, section);
|
||||
if (key == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Config config;
|
||||
if (!configInit(&config)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char path[COMPAT_MAX_PATH];
|
||||
bool loaded = false;
|
||||
|
||||
if (basePath[0] != '\0' && !is_system_file_name(fileName)) {
|
||||
// Attempt to load requested file in base directory.
|
||||
snprintf(path, sizeof(path), "%s\\%s", basePath, fileName);
|
||||
loaded = configRead(&config, path, false);
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
// There was no base path set, requested file is a system config, or
|
||||
// non-system config file was not found the base path - attempt to load
|
||||
// from current working directory.
|
||||
strcpy(path, fileName);
|
||||
loaded = configRead(&config, path, false);
|
||||
}
|
||||
|
||||
// NOTE: Sfall's `GetIniSetting` returns error code (-1) only when it cannot
|
||||
// parse triplet. Otherwise the default for string settings is empty string.
|
||||
value[0] = '\0';
|
||||
|
||||
if (loaded) {
|
||||
char* stringValue;
|
||||
if (configGetString(&config, section, key, &stringValue)) {
|
||||
strncpy(value, stringValue, size - 1);
|
||||
value[size - 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
configFree(&config);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef FALLOUT_SFALL_INI_H_
|
||||
#define FALLOUT_SFALL_INI_H_
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace fallout {
|
||||
|
||||
/// Sets base directory to lookup .ini files.
|
||||
void sfall_ini_set_base_path(const char* path);
|
||||
|
||||
/// Reads integer key identified by "fileName|section|key" triplet into `value`.
|
||||
bool sfall_ini_get_int(const char* triplet, int* value);
|
||||
|
||||
/// Reads string key identified by "fileName|section|key" triplet into `value`.
|
||||
bool sfall_ini_get_string(const char* triplet, char* value, size_t size);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* FALLOUT_SFALL_INI_H_ */
|
||||
@@ -0,0 +1,297 @@
|
||||
#include "sfall_kb_helpers.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
/// Maps DirectInput DIK constants to SDL scancodes.
|
||||
static constexpr SDL_Scancode kDiks[256] = {
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_ESCAPE, // DIK_ESCAPE
|
||||
SDL_SCANCODE_1, // DIK_1
|
||||
SDL_SCANCODE_2, // DIK_2
|
||||
SDL_SCANCODE_3, // DIK_3
|
||||
SDL_SCANCODE_4, // DIK_4
|
||||
SDL_SCANCODE_5, // DIK_5
|
||||
SDL_SCANCODE_6, // DIK_6
|
||||
SDL_SCANCODE_7, // DIK_7
|
||||
SDL_SCANCODE_8, // DIK_8
|
||||
SDL_SCANCODE_9, // DIK_9
|
||||
SDL_SCANCODE_0, // DIK_0
|
||||
SDL_SCANCODE_MINUS, // DIK_MINUS
|
||||
SDL_SCANCODE_EQUALS, // DIK_EQUALS
|
||||
SDL_SCANCODE_BACKSPACE, // DIK_BACK
|
||||
SDL_SCANCODE_TAB, // DIK_TAB
|
||||
SDL_SCANCODE_Q, // DIK_Q
|
||||
SDL_SCANCODE_W, // DIK_W
|
||||
SDL_SCANCODE_E, // DIK_E
|
||||
SDL_SCANCODE_R, // DIK_R
|
||||
SDL_SCANCODE_T, // DIK_T
|
||||
SDL_SCANCODE_Y, // DIK_Y
|
||||
SDL_SCANCODE_U, // DIK_U
|
||||
SDL_SCANCODE_I, // DIK_I
|
||||
SDL_SCANCODE_O, // DIK_O
|
||||
SDL_SCANCODE_P, // DIK_P
|
||||
SDL_SCANCODE_LEFTBRACKET, // DIK_LBRACKET
|
||||
SDL_SCANCODE_RIGHTBRACKET, // DIK_RBRACKET
|
||||
SDL_SCANCODE_RETURN, // DIK_RETURN
|
||||
SDL_SCANCODE_LCTRL, // DIK_LCONTROL
|
||||
SDL_SCANCODE_A, // DIK_A
|
||||
SDL_SCANCODE_S, // DIK_S
|
||||
SDL_SCANCODE_D, // DIK_D
|
||||
SDL_SCANCODE_F, // DIK_F
|
||||
SDL_SCANCODE_G, // DIK_G
|
||||
SDL_SCANCODE_H, // DIK_H
|
||||
SDL_SCANCODE_J, // DIK_J
|
||||
SDL_SCANCODE_K, // DIK_K
|
||||
SDL_SCANCODE_L, // DIK_L
|
||||
SDL_SCANCODE_SEMICOLON, // DIK_SEMICOLON
|
||||
SDL_SCANCODE_APOSTROPHE, // DIK_APOSTROPHE
|
||||
SDL_SCANCODE_GRAVE, // DIK_GRAVE
|
||||
SDL_SCANCODE_LSHIFT, // DIK_LSHIFT
|
||||
SDL_SCANCODE_BACKSLASH, // DIK_BACKSLASH
|
||||
SDL_SCANCODE_Z, // DIK_Z
|
||||
SDL_SCANCODE_X, // DIK_X
|
||||
SDL_SCANCODE_C, // DIK_C
|
||||
SDL_SCANCODE_V, // DIK_V
|
||||
SDL_SCANCODE_B, // DIK_B
|
||||
SDL_SCANCODE_N, // DIK_N
|
||||
SDL_SCANCODE_M, // DIK_M
|
||||
SDL_SCANCODE_COMMA, // DIK_COMMA
|
||||
SDL_SCANCODE_PERIOD, // DIK_PERIOD
|
||||
SDL_SCANCODE_SLASH, // DIK_SLASH
|
||||
SDL_SCANCODE_RSHIFT, // DIK_RSHIFT
|
||||
SDL_SCANCODE_KP_MULTIPLY, // DIK_MULTIPLY
|
||||
SDL_SCANCODE_LALT, // DIK_LMENU
|
||||
SDL_SCANCODE_SPACE, // DIK_SPACE
|
||||
SDL_SCANCODE_CAPSLOCK, // DIK_CAPITAL
|
||||
SDL_SCANCODE_F1, // DIK_F1
|
||||
SDL_SCANCODE_F2, // DIK_F2
|
||||
SDL_SCANCODE_F3, // DIK_F3
|
||||
SDL_SCANCODE_F4, // DIK_F4
|
||||
SDL_SCANCODE_F5, // DIK_F5
|
||||
SDL_SCANCODE_F6, // DIK_F6
|
||||
SDL_SCANCODE_F7, // DIK_F7
|
||||
SDL_SCANCODE_F8, // DIK_F8
|
||||
SDL_SCANCODE_F9, // DIK_F9
|
||||
SDL_SCANCODE_F10, // DIK_F10
|
||||
SDL_SCANCODE_NUMLOCKCLEAR, // DIK_NUMLOCK
|
||||
SDL_SCANCODE_SCROLLLOCK, // DIK_SCROLL
|
||||
SDL_SCANCODE_KP_7, // DIK_NUMPAD7
|
||||
SDL_SCANCODE_KP_8, // DIK_NUMPAD8
|
||||
SDL_SCANCODE_KP_9, // DIK_NUMPAD9
|
||||
SDL_SCANCODE_KP_MINUS, // DIK_SUBTRACT
|
||||
SDL_SCANCODE_KP_4, // DIK_NUMPAD4
|
||||
SDL_SCANCODE_KP_5, // DIK_NUMPAD5
|
||||
SDL_SCANCODE_KP_6, // DIK_NUMPAD6
|
||||
SDL_SCANCODE_KP_PLUS, // DIK_ADD
|
||||
SDL_SCANCODE_KP_1, // DIK_NUMPAD1
|
||||
SDL_SCANCODE_KP_2, // DIK_NUMPAD2
|
||||
SDL_SCANCODE_KP_3, // DIK_NUMPAD3
|
||||
SDL_SCANCODE_KP_0, // DIK_NUMPAD0
|
||||
SDL_SCANCODE_KP_PERIOD, // DIK_DECIMAL
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_F11, // DIK_F11
|
||||
SDL_SCANCODE_F12, // DIK_F12
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_KP_EQUALS, // DIK_NUMPADEQUALS
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, // DIK_AT
|
||||
SDL_SCANCODE_UNKNOWN, // DIK_COLON
|
||||
SDL_SCANCODE_UNKNOWN, // DIK_UNDERLINE
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, // DIK_STOP
|
||||
SDL_SCANCODE_UNKNOWN, // DIK_AX
|
||||
SDL_SCANCODE_UNKNOWN, // DIK_UNLABELED
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_KP_ENTER, // DIK_NUMPADENTER
|
||||
SDL_SCANCODE_RCTRL, // DIK_RCONTROL
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_KP_COMMA, // DIK_NUMPADCOMMA
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_SLASH, // DIK_DIVIDE
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_SYSREQ, // DIK_SYSRQ
|
||||
SDL_SCANCODE_RALT, // DIK_RMENU
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_HOME, // DIK_HOME
|
||||
SDL_SCANCODE_UP, // DIK_UP
|
||||
SDL_SCANCODE_PAGEUP, // DIK_PRIOR
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_LEFT, // DIK_LEFT
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_RIGHT, // DIK_RIGHT
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_END, // DIK_END
|
||||
SDL_SCANCODE_DOWN, // DIK_DOWN
|
||||
SDL_SCANCODE_PAGEDOWN, // DIK_NEXT
|
||||
SDL_SCANCODE_INSERT, // DIK_INSERT
|
||||
SDL_SCANCODE_DELETE, // DIK_DELETE
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_LGUI, // DIK_LWIN
|
||||
SDL_SCANCODE_RGUI, // DIK_RWIN
|
||||
SDL_SCANCODE_APPLICATION, // DIK_APPS
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN,
|
||||
};
|
||||
|
||||
/// Translates Sfall key code (DIK or VK constant) to SDL scancode.
|
||||
static SDL_Scancode get_scancode_from_key(int key)
|
||||
{
|
||||
return kDiks[key & 0xFF];
|
||||
}
|
||||
|
||||
bool sfall_kb_is_key_pressed(int key)
|
||||
{
|
||||
SDL_Scancode scancode = get_scancode_from_key(key);
|
||||
if (scancode == SDL_SCANCODE_UNKNOWN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Uint8* state = SDL_GetKeyboardState(nullptr);
|
||||
return state[scancode] != 0;
|
||||
}
|
||||
|
||||
void sfall_kb_press_key(int key)
|
||||
{
|
||||
SDL_Scancode scancode = get_scancode_from_key(key);
|
||||
if (scancode == SDL_SCANCODE_UNKNOWN) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Event event;
|
||||
event.key.keysym.scancode = scancode;
|
||||
|
||||
event.type = SDL_KEYDOWN;
|
||||
SDL_PushEvent(&event);
|
||||
|
||||
event.type = SDL_KEYUP;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef FALLOUT_SFALL_KB_HELPERS_H_
|
||||
#define FALLOUT_SFALL_KB_HELPERS_H_
|
||||
|
||||
/// Returns `true` if given key is pressed.
|
||||
bool sfall_kb_is_key_pressed(int key);
|
||||
|
||||
/// Simulates pressing `key`.
|
||||
void sfall_kb_press_key(int key);
|
||||
|
||||
#endif /* FALLOUT_SFALL_KB_HELPERS_H_ */
|
||||
+625
-5
@@ -1,19 +1,35 @@
|
||||
#include "sfall_opcodes.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "animation.h"
|
||||
#include "art.h"
|
||||
#include "color.h"
|
||||
#include "combat.h"
|
||||
#include "dbox.h"
|
||||
#include "debug.h"
|
||||
#include "game.h"
|
||||
#include "input.h"
|
||||
#include "interface.h"
|
||||
#include "interpreter.h"
|
||||
#include "item.h"
|
||||
#include "memory.h"
|
||||
#include "message.h"
|
||||
#include "mouse.h"
|
||||
#include "object.h"
|
||||
#include "party_member.h"
|
||||
#include "proto.h"
|
||||
#include "scripts.h"
|
||||
#include "sfall_arrays.h"
|
||||
#include "sfall_global_scripts.h"
|
||||
#include "sfall_global_vars.h"
|
||||
#include "sfall_ini.h"
|
||||
#include "sfall_kb_helpers.h"
|
||||
#include "sfall_lists.h"
|
||||
#include "stat.h"
|
||||
#include "svga.h"
|
||||
#include "tile.h"
|
||||
#include "worldmap.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
@@ -39,6 +55,17 @@ static void opReadByte(Program* program)
|
||||
programStackPushInteger(program, value);
|
||||
}
|
||||
|
||||
// set_pc_base_stat
|
||||
static void op_set_pc_base_stat(Program* program)
|
||||
{
|
||||
// CE: Implementation is different. Sfall changes value directly on the
|
||||
// dude's proto, without calling |critterSetBaseStat|. This function has
|
||||
// important call to update derived stats, which is not present in Sfall.
|
||||
int value = programStackPopInteger(program);
|
||||
int stat = programStackPopInteger(program);
|
||||
critterSetBaseStat(gDude, stat, value);
|
||||
}
|
||||
|
||||
// set_pc_extra_stat
|
||||
static void opSetPcBonusStat(Program* program)
|
||||
{
|
||||
@@ -50,6 +77,16 @@ static void opSetPcBonusStat(Program* program)
|
||||
critterSetBonusStat(gDude, stat, value);
|
||||
}
|
||||
|
||||
// get_pc_base_stat
|
||||
static void op_get_pc_base_stat(Program* program)
|
||||
{
|
||||
// CE: Implementation is different. Sfall obtains value directly from
|
||||
// dude's proto. This can have unforeseen consequences when dealing with
|
||||
// current stats.
|
||||
int stat = programStackPopInteger(program);
|
||||
programStackPushInteger(program, critterGetBaseStat(gDude, stat));
|
||||
}
|
||||
|
||||
// get_pc_extra_stat
|
||||
static void opGetPcBonusStat(Program* program)
|
||||
{
|
||||
@@ -58,12 +95,70 @@ static void opGetPcBonusStat(Program* program)
|
||||
programStackPushInteger(program, value);
|
||||
}
|
||||
|
||||
// tap_key
|
||||
static void op_tap_key(Program* program)
|
||||
{
|
||||
int key = programStackPopInteger(program);
|
||||
sfall_kb_press_key(key);
|
||||
}
|
||||
|
||||
// get_year
|
||||
static void op_get_year(Program* program)
|
||||
{
|
||||
int year;
|
||||
gameTimeGetDate(nullptr, nullptr, &year);
|
||||
programStackPushInteger(program, year);
|
||||
}
|
||||
|
||||
// game_loaded
|
||||
static void op_game_loaded(Program* program)
|
||||
{
|
||||
bool loaded = sfall_gl_scr_is_loaded(program);
|
||||
programStackPushInteger(program, loaded ? 1 : 0);
|
||||
}
|
||||
|
||||
// set_global_script_repeat
|
||||
static void op_set_global_script_repeat(Program* program)
|
||||
{
|
||||
int frames = programStackPopInteger(program);
|
||||
sfall_gl_scr_set_repeat(program, frames);
|
||||
}
|
||||
|
||||
// key_pressed
|
||||
static void op_key_pressed(Program* program)
|
||||
{
|
||||
int key = programStackPopInteger(program);
|
||||
bool pressed = sfall_kb_is_key_pressed(key);
|
||||
programStackPushInteger(program, pressed ? 1 : 0);
|
||||
}
|
||||
|
||||
// in_world_map
|
||||
static void op_in_world_map(Program* program)
|
||||
{
|
||||
programStackPushInteger(program, GameMode::isInGameMode(GameMode::kWorldmap) ? 1 : 0);
|
||||
}
|
||||
|
||||
// set_world_map_pos
|
||||
static void op_set_world_map_pos(Program* program)
|
||||
{
|
||||
int y = programStackPopInteger(program);
|
||||
int x = programStackPopInteger(program);
|
||||
wmSetPartyWorldPos(x, y);
|
||||
}
|
||||
|
||||
// active_hand
|
||||
static void opGetCurrentHand(Program* program)
|
||||
{
|
||||
programStackPushInteger(program, interfaceGetCurrentHand());
|
||||
}
|
||||
|
||||
// set_global_script_type
|
||||
static void op_set_global_script_type(Program* program)
|
||||
{
|
||||
int type = programStackPopInteger(program);
|
||||
sfall_gl_scr_set_type(program, type);
|
||||
}
|
||||
|
||||
// set_sfall_global
|
||||
static void opSetGlobalVar(Program* program)
|
||||
{
|
||||
@@ -72,9 +167,9 @@ static void opSetGlobalVar(Program* program)
|
||||
|
||||
if ((variable.opcode & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) {
|
||||
const char* key = programGetString(program, variable.opcode, variable.integerValue);
|
||||
sfallGlobalVarsStore(key, value.integerValue);
|
||||
sfall_gl_vars_store(key, value.integerValue);
|
||||
} else if (variable.opcode == VALUE_TYPE_INT) {
|
||||
sfallGlobalVarsStore(variable.integerValue, value.integerValue);
|
||||
sfall_gl_vars_store(variable.integerValue, value.integerValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,20 +181,143 @@ static void opGetGlobalInt(Program* program)
|
||||
int value = 0;
|
||||
if ((variable.opcode & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) {
|
||||
const char* key = programGetString(program, variable.opcode, variable.integerValue);
|
||||
sfallGlobalVarsFetch(key, value);
|
||||
sfall_gl_vars_fetch(key, value);
|
||||
} else if (variable.opcode == VALUE_TYPE_INT) {
|
||||
sfallGlobalVarsFetch(variable.integerValue, value);
|
||||
sfall_gl_vars_fetch(variable.integerValue, value);
|
||||
}
|
||||
|
||||
programStackPushInteger(program, value);
|
||||
}
|
||||
|
||||
// get_ini_setting
|
||||
static void op_get_ini_setting(Program* program)
|
||||
{
|
||||
const char* string = programStackPopString(program);
|
||||
|
||||
int value;
|
||||
if (sfall_ini_get_int(string, &value)) {
|
||||
programStackPushInteger(program, value);
|
||||
} else {
|
||||
programStackPushInteger(program, -1);
|
||||
}
|
||||
}
|
||||
|
||||
// get_game_mode
|
||||
static void opGetGameMode(Program* program)
|
||||
{
|
||||
programStackPushInteger(program, GameMode::getCurrentGameMode());
|
||||
}
|
||||
|
||||
// get_uptime
|
||||
static void op_get_uptime(Program* program)
|
||||
{
|
||||
programStackPushInteger(program, getTicks());
|
||||
}
|
||||
|
||||
// set_car_current_town
|
||||
static void op_set_car_current_town(Program* program)
|
||||
{
|
||||
int area = programStackPopInteger(program);
|
||||
wmCarSetCurrentArea(area);
|
||||
}
|
||||
|
||||
// get_bodypart_hit_modifier
|
||||
static void op_get_bodypart_hit_modifier(Program* program)
|
||||
{
|
||||
int hit_location = programStackPopInteger(program);
|
||||
programStackPushInteger(program, combat_get_hit_location_penalty(hit_location));
|
||||
}
|
||||
|
||||
// set_bodypart_hit_modifier
|
||||
static void op_set_bodypart_hit_modifier(Program* program)
|
||||
{
|
||||
int penalty = programStackPopInteger(program);
|
||||
int hit_location = programStackPopInteger(program);
|
||||
combat_set_hit_location_penalty(hit_location, penalty);
|
||||
}
|
||||
|
||||
// get_ini_string
|
||||
static void op_get_ini_string(Program* program)
|
||||
{
|
||||
const char* string = programStackPopString(program);
|
||||
|
||||
char value[256];
|
||||
if (sfall_ini_get_string(string, value, sizeof(value))) {
|
||||
programStackPushString(program, value);
|
||||
} else {
|
||||
programStackPushInteger(program, -1);
|
||||
}
|
||||
}
|
||||
|
||||
// sqrt
|
||||
static void op_sqrt(Program* program)
|
||||
{
|
||||
ProgramValue programValue = programStackPopValue(program);
|
||||
programStackPushFloat(program, sqrtf(programValue.asFloat()));
|
||||
}
|
||||
|
||||
// abs
|
||||
static void op_abs(Program* program)
|
||||
{
|
||||
ProgramValue programValue = programStackPopValue(program);
|
||||
|
||||
if (programValue.isInt()) {
|
||||
programStackPushInteger(program, abs(programValue.integerValue));
|
||||
} else {
|
||||
programStackPushFloat(program, abs(programValue.asFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
// get_proto_data
|
||||
static void op_get_proto_data(Program* program)
|
||||
{
|
||||
size_t offset = static_cast<size_t>(programStackPopInteger(program));
|
||||
int pid = programStackPopInteger(program);
|
||||
|
||||
Proto* proto;
|
||||
if (protoGetProto(pid, &proto) != 0) {
|
||||
debugPrint("op_get_proto_data: bad proto %d", pid);
|
||||
programStackPushInteger(program, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
// CE: Make sure the requested offset is within memory bounds and is
|
||||
// properly aligned.
|
||||
if (offset + sizeof(int) > proto_size(PID_TYPE(pid)) || offset % sizeof(int) != 0) {
|
||||
debugPrint("op_get_proto_data: bad offset %d", offset);
|
||||
programStackPushInteger(program, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
int value = *reinterpret_cast<int*>(reinterpret_cast<unsigned char*>(proto) + offset);
|
||||
programStackPushInteger(program, value);
|
||||
}
|
||||
|
||||
// set_proto_data
|
||||
static void op_set_proto_data(Program* program)
|
||||
{
|
||||
int value = programStackPopInteger(program);
|
||||
size_t offset = static_cast<size_t>(programStackPopInteger(program));
|
||||
int pid = programStackPopInteger(program);
|
||||
|
||||
Proto* proto;
|
||||
if (protoGetProto(pid, &proto) != 0) {
|
||||
debugPrint("op_set_proto_data: bad proto %d", pid);
|
||||
programStackPushInteger(program, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
// CE: Make sure the requested offset is within memory bounds and is
|
||||
// properly aligned.
|
||||
if (offset + sizeof(int) > proto_size(PID_TYPE(pid)) || offset % sizeof(int) != 0) {
|
||||
debugPrint("op_set_proto_data: bad offset %d", offset);
|
||||
programStackPushInteger(program, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
*reinterpret_cast<int*>(reinterpret_cast<unsigned char*>(proto) + offset) = value;
|
||||
}
|
||||
|
||||
// list_begin
|
||||
static void opListBegin(Program* program)
|
||||
{
|
||||
@@ -251,6 +469,14 @@ static void opGetMouseY(Program* program)
|
||||
programStackPushInteger(program, y);
|
||||
}
|
||||
|
||||
// get_mouse_buttons
|
||||
static void op_get_mouse_buttons(Program* program)
|
||||
{
|
||||
// CE: Implementation is slightly different - it does not handle middle
|
||||
// mouse button.
|
||||
programStackPushInteger(program, mouse_get_last_buttons());
|
||||
}
|
||||
|
||||
// get_screen_width
|
||||
static void opGetScreenWidth(Program* program)
|
||||
{
|
||||
@@ -263,6 +489,58 @@ static void opGetScreenHeight(Program* program)
|
||||
programStackPushInteger(program, screenGetHeight());
|
||||
}
|
||||
|
||||
// create_message_window
|
||||
static void op_create_message_window(Program* program)
|
||||
{
|
||||
static bool showing = false;
|
||||
|
||||
if (showing) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char* string = programStackPopString(program);
|
||||
if (string == nullptr || string[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
char* copy = internal_strdup(string);
|
||||
|
||||
const char* body[4];
|
||||
int count = 0;
|
||||
|
||||
char* pch = strchr(copy, '\n');
|
||||
while (pch != nullptr && count < 4) {
|
||||
*pch = '\0';
|
||||
body[count++] = pch + 1;
|
||||
pch = strchr(pch + 1, '\n');
|
||||
}
|
||||
|
||||
showing = true;
|
||||
showDialogBox(copy,
|
||||
body,
|
||||
count,
|
||||
192,
|
||||
116,
|
||||
_colorTable[32328],
|
||||
nullptr,
|
||||
_colorTable[32328],
|
||||
DIALOG_BOX_LARGE);
|
||||
showing = false;
|
||||
|
||||
internal_free(copy);
|
||||
}
|
||||
|
||||
// get_attack_type
|
||||
static void op_get_attack_type(Program* program)
|
||||
{
|
||||
int hit_mode;
|
||||
if (interface_get_current_attack_mode(&hit_mode)) {
|
||||
programStackPushInteger(program, hit_mode);
|
||||
} else {
|
||||
programStackPushInteger(program, -1);
|
||||
}
|
||||
}
|
||||
|
||||
// atoi
|
||||
static void opParseInt(Program* program)
|
||||
{
|
||||
@@ -270,6 +548,71 @@ static void opParseInt(Program* program)
|
||||
programStackPushInteger(program, static_cast<int>(strtol(string, nullptr, 0)));
|
||||
}
|
||||
|
||||
// atof
|
||||
static void op_atof(Program* program)
|
||||
{
|
||||
const char* string = programStackPopString(program);
|
||||
programStackPushFloat(program, static_cast<float>(atof(string)));
|
||||
}
|
||||
|
||||
// tile_under_cursor
|
||||
static void op_tile_under_cursor(Program* program)
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
mouseGetPosition(&x, &y);
|
||||
|
||||
int tile = tileFromScreenXY(x, y, gElevation);
|
||||
programStackPushInteger(program, tile);
|
||||
}
|
||||
|
||||
// substr
|
||||
static void opSubstr(Program* program)
|
||||
{
|
||||
auto length = programStackPopInteger(program);
|
||||
auto startPos = programStackPopInteger(program);
|
||||
const char* str = programStackPopString(program);
|
||||
|
||||
char buf[5120] = { 0 };
|
||||
|
||||
int len = strlen(str);
|
||||
|
||||
if (startPos < 0) {
|
||||
startPos += len; // start from end
|
||||
if (startPos < 0) {
|
||||
startPos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (length < 0) {
|
||||
length += len - startPos; // cutoff at end
|
||||
if (length == 0) {
|
||||
programStackPushString(program, buf);
|
||||
return;
|
||||
}
|
||||
length = abs(length); // length can't be negative
|
||||
}
|
||||
|
||||
// check position
|
||||
if (startPos >= len) {
|
||||
// start position is out of string length, return empty string
|
||||
programStackPushString(program, buf);
|
||||
return;
|
||||
}
|
||||
|
||||
if (length == 0 || length + startPos > len) {
|
||||
length = len - startPos; // set the correct length, the length of characters goes beyond the end of the string
|
||||
}
|
||||
|
||||
if (length > sizeof(buf) - 1) {
|
||||
length = sizeof(buf) - 1;
|
||||
}
|
||||
|
||||
memcpy(buf, &str[startPos], length);
|
||||
buf[length] = '\0';
|
||||
programStackPushString(program, buf);
|
||||
}
|
||||
|
||||
// strlen
|
||||
static void opGetStringLength(Program* program)
|
||||
{
|
||||
@@ -277,6 +620,22 @@ static void opGetStringLength(Program* program)
|
||||
programStackPushInteger(program, static_cast<int>(strlen(string)));
|
||||
}
|
||||
|
||||
// pow (^)
|
||||
static void op_power(Program* program)
|
||||
{
|
||||
ProgramValue expValue = programStackPopValue(program);
|
||||
ProgramValue baseValue = programStackPopValue(program);
|
||||
|
||||
// CE: Implementation is slightly different, check.
|
||||
float result = powf(baseValue.asFloat(), expValue.asFloat());
|
||||
|
||||
if (baseValue.isInt() && expValue.isInt()) {
|
||||
programStackPushInteger(program, static_cast<int>(result));
|
||||
} else {
|
||||
programStackPushFloat(program, result);
|
||||
}
|
||||
}
|
||||
|
||||
// message_str_game
|
||||
static void opGetMessage(Program* program)
|
||||
{
|
||||
@@ -286,6 +645,146 @@ static void opGetMessage(Program* program)
|
||||
programStackPushString(program, text);
|
||||
}
|
||||
|
||||
// array_key
|
||||
static void opGetArrayKey(Program* program)
|
||||
{
|
||||
auto index = programStackPopInteger(program);
|
||||
auto arrayId = programStackPopInteger(program);
|
||||
auto value = GetArrayKey(arrayId, index, program);
|
||||
programStackPushValue(program, value);
|
||||
}
|
||||
|
||||
// create_array
|
||||
static void opCreateArray(Program* program)
|
||||
{
|
||||
auto flags = programStackPopInteger(program);
|
||||
auto len = programStackPopInteger(program);
|
||||
auto arrayId = CreateArray(len, flags);
|
||||
programStackPushInteger(program, arrayId);
|
||||
}
|
||||
|
||||
// temp_array
|
||||
static void opTempArray(Program* program)
|
||||
{
|
||||
auto flags = programStackPopInteger(program);
|
||||
auto len = programStackPopInteger(program);
|
||||
auto arrayId = CreateTempArray(len, flags);
|
||||
programStackPushInteger(program, arrayId);
|
||||
}
|
||||
|
||||
// fix_array
|
||||
static void opFixArray(Program* program)
|
||||
{
|
||||
auto arrayId = programStackPopInteger(program);
|
||||
FixArray(arrayId);
|
||||
}
|
||||
|
||||
// string_split
|
||||
static void opStringSplit(Program* program)
|
||||
{
|
||||
auto split = programStackPopString(program);
|
||||
auto str = programStackPopString(program);
|
||||
auto arrayId = StringSplit(str, split);
|
||||
programStackPushInteger(program, arrayId);
|
||||
}
|
||||
|
||||
// set_array
|
||||
static void opSetArray(Program* program)
|
||||
{
|
||||
auto value = programStackPopValue(program);
|
||||
auto key = programStackPopValue(program);
|
||||
auto arrayId = programStackPopInteger(program);
|
||||
SetArray(arrayId, key, value, true, program);
|
||||
}
|
||||
|
||||
// arrayexpr
|
||||
static void opStackArray(Program* program)
|
||||
{
|
||||
auto value = programStackPopValue(program);
|
||||
auto key = programStackPopValue(program);
|
||||
auto returnValue = StackArray(key, value, program);
|
||||
programStackPushInteger(program, returnValue);
|
||||
}
|
||||
|
||||
// scan_array
|
||||
static void opScanArray(Program* program)
|
||||
{
|
||||
auto value = programStackPopValue(program);
|
||||
auto arrayId = programStackPopInteger(program);
|
||||
auto returnValue = ScanArray(arrayId, value, program);
|
||||
programStackPushValue(program, returnValue);
|
||||
}
|
||||
|
||||
// get_array
|
||||
static void opGetArray(Program* program)
|
||||
{
|
||||
auto key = programStackPopValue(program);
|
||||
auto arrayId = programStackPopValue(program);
|
||||
|
||||
if (arrayId.isInt()) {
|
||||
auto value = GetArray(arrayId.integerValue, key, program);
|
||||
programStackPushValue(program, value);
|
||||
} else if (arrayId.isString() && key.isInt()) {
|
||||
auto pos = key.asInt();
|
||||
auto str = programGetString(program, arrayId.opcode, arrayId.integerValue);
|
||||
|
||||
char buf[2] = { 0 };
|
||||
if (pos < strlen(str)) {
|
||||
buf[0] = str[pos];
|
||||
programStackPushString(program, buf);
|
||||
} else {
|
||||
programStackPushString(program, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// free_array
|
||||
static void opFreeArray(Program* program)
|
||||
{
|
||||
auto arrayId = programStackPopInteger(program);
|
||||
FreeArray(arrayId);
|
||||
}
|
||||
|
||||
// len_array
|
||||
static void opLenArray(Program* program)
|
||||
{
|
||||
auto arrayId = programStackPopInteger(program);
|
||||
programStackPushInteger(program, LenArray(arrayId));
|
||||
}
|
||||
|
||||
// resize_array
|
||||
static void opResizeArray(Program* program)
|
||||
{
|
||||
auto newLen = programStackPopInteger(program);
|
||||
auto arrayId = programStackPopInteger(program);
|
||||
ResizeArray(arrayId, newLen);
|
||||
}
|
||||
|
||||
// party_member_list
|
||||
static void opPartyMemberList(Program* program)
|
||||
{
|
||||
auto includeHidden = programStackPopInteger(program);
|
||||
auto objects = get_all_party_members_objects(includeHidden);
|
||||
auto arrayId = CreateTempArray(objects.size(), SFALL_ARRAYFLAG_RESERVED);
|
||||
for (int i = 0; i < LenArray(arrayId); i++) {
|
||||
SetArray(arrayId, ProgramValue { i }, ProgramValue { objects[i] }, false, program);
|
||||
}
|
||||
programStackPushInteger(program, arrayId);
|
||||
}
|
||||
|
||||
// type_of
|
||||
static void opTypeOf(Program* program)
|
||||
{
|
||||
auto value = programStackPopValue(program);
|
||||
if (value.isInt()) {
|
||||
programStackPushInteger(program, 1);
|
||||
} else if (value.isFloat()) {
|
||||
programStackPushInteger(program, 2);
|
||||
} else {
|
||||
programStackPushInteger(program, 3);
|
||||
};
|
||||
}
|
||||
|
||||
// round
|
||||
static void opRound(Program* program)
|
||||
{
|
||||
@@ -298,6 +797,61 @@ static void opRound(Program* program)
|
||||
programStackPushInteger(program, integerValue);
|
||||
}
|
||||
|
||||
enum BlockType {
|
||||
BLOCKING_TYPE_BLOCK,
|
||||
BLOCKING_TYPE_SHOOT,
|
||||
BLOCKING_TYPE_AI,
|
||||
BLOCKING_TYPE_SIGHT,
|
||||
BLOCKING_TYPE_SCROLL,
|
||||
};
|
||||
|
||||
PathBuilderCallback* get_blocking_func(int type)
|
||||
{
|
||||
switch (type) {
|
||||
case BLOCKING_TYPE_SHOOT:
|
||||
return _obj_shoot_blocking_at;
|
||||
case BLOCKING_TYPE_AI:
|
||||
return _obj_ai_blocking_at;
|
||||
case BLOCKING_TYPE_SIGHT:
|
||||
return _obj_sight_blocking_at;
|
||||
default:
|
||||
return _obj_blocking_at;
|
||||
}
|
||||
}
|
||||
|
||||
// obj_blocking_line
|
||||
static void op_make_straight_path(Program* program)
|
||||
{
|
||||
int type = programStackPopInteger(program);
|
||||
int dest = programStackPopInteger(program);
|
||||
Object* object = static_cast<Object*>(programStackPopPointer(program));
|
||||
|
||||
int flags = type == BLOCKING_TYPE_SHOOT ? 32 : 0;
|
||||
|
||||
Object* obstacle = nullptr;
|
||||
_make_straight_path_func(object, object->tile, dest, nullptr, &obstacle, flags, get_blocking_func(type));
|
||||
programStackPushPointer(program, obstacle);
|
||||
}
|
||||
|
||||
// obj_blocking_tile
|
||||
static void op_obj_blocking_at(Program* program)
|
||||
{
|
||||
int type = programStackPopInteger(program);
|
||||
int elevation = programStackPopInteger(program);
|
||||
int tile = programStackPopInteger(program);
|
||||
|
||||
PathBuilderCallback* func = get_blocking_func(type);
|
||||
Object* obstacle = func(NULL, tile, elevation);
|
||||
if (obstacle != NULL) {
|
||||
if (type == BLOCKING_TYPE_SHOOT) {
|
||||
if ((obstacle->flags & OBJECT_SHOOT_THRU) != 0) {
|
||||
obstacle = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
programStackPushPointer(program, obstacle);
|
||||
}
|
||||
|
||||
// art_exists
|
||||
static void opArtExists(Program* program)
|
||||
{
|
||||
@@ -305,15 +859,57 @@ static void opArtExists(Program* program)
|
||||
programStackPushInteger(program, artExists(fid));
|
||||
}
|
||||
|
||||
// div (/)
|
||||
static void op_div(Program* program)
|
||||
{
|
||||
ProgramValue divisorValue = programStackPopValue(program);
|
||||
ProgramValue dividendValue = programStackPopValue(program);
|
||||
|
||||
if (divisorValue.integerValue == 0) {
|
||||
debugPrint("Division by zero");
|
||||
|
||||
// TODO: Looks like execution is not halted in Sfall's div, check.
|
||||
programStackPushInteger(program, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dividendValue.isFloat() || divisorValue.isFloat()) {
|
||||
programStackPushFloat(program, dividendValue.asFloat() / divisorValue.asFloat());
|
||||
} else {
|
||||
// Unsigned divison.
|
||||
programStackPushInteger(program, static_cast<unsigned int>(dividendValue.integerValue) / static_cast<unsigned int>(divisorValue.integerValue));
|
||||
}
|
||||
}
|
||||
|
||||
void sfallOpcodesInit()
|
||||
{
|
||||
interpreterRegisterOpcode(0x8156, opReadByte);
|
||||
interpreterRegisterOpcode(0x815A, op_set_pc_base_stat);
|
||||
interpreterRegisterOpcode(0x815B, opSetPcBonusStat);
|
||||
interpreterRegisterOpcode(0x815C, op_get_pc_base_stat);
|
||||
interpreterRegisterOpcode(0x815D, opGetPcBonusStat);
|
||||
interpreterRegisterOpcode(0x8162, op_tap_key);
|
||||
interpreterRegisterOpcode(0x8163, op_get_year);
|
||||
interpreterRegisterOpcode(0x8164, op_game_loaded);
|
||||
interpreterRegisterOpcode(0x816A, op_set_global_script_repeat);
|
||||
interpreterRegisterOpcode(0x816C, op_key_pressed);
|
||||
interpreterRegisterOpcode(0x8170, op_in_world_map);
|
||||
interpreterRegisterOpcode(0x8172, op_set_world_map_pos);
|
||||
interpreterRegisterOpcode(0x8193, opGetCurrentHand);
|
||||
interpreterRegisterOpcode(0x819B, op_set_global_script_type);
|
||||
interpreterRegisterOpcode(0x819D, opSetGlobalVar);
|
||||
interpreterRegisterOpcode(0x819E, opGetGlobalInt);
|
||||
interpreterRegisterOpcode(0x81AC, op_get_ini_setting);
|
||||
interpreterRegisterOpcode(0x81AF, opGetGameMode);
|
||||
interpreterRegisterOpcode(0x81B3, op_get_uptime);
|
||||
interpreterRegisterOpcode(0x81B6, op_set_car_current_town);
|
||||
interpreterRegisterOpcode(0x81DF, op_get_bodypart_hit_modifier);
|
||||
interpreterRegisterOpcode(0x81E0, op_set_bodypart_hit_modifier);
|
||||
interpreterRegisterOpcode(0x81EB, op_get_ini_string);
|
||||
interpreterRegisterOpcode(0x81EC, op_sqrt);
|
||||
interpreterRegisterOpcode(0x81ED, op_abs);
|
||||
interpreterRegisterOpcode(0x8204, op_get_proto_data);
|
||||
interpreterRegisterOpcode(0x8205, op_set_proto_data);
|
||||
interpreterRegisterOpcode(0x820D, opListBegin);
|
||||
interpreterRegisterOpcode(0x820E, opListNext);
|
||||
interpreterRegisterOpcode(0x820F, opListEnd);
|
||||
@@ -326,13 +922,37 @@ void sfallOpcodesInit()
|
||||
interpreterRegisterOpcode(0x821A, opSetWeaponAmmoCount);
|
||||
interpreterRegisterOpcode(0x821C, opGetMouseX);
|
||||
interpreterRegisterOpcode(0x821D, opGetMouseY);
|
||||
interpreterRegisterOpcode(0x821E, op_get_mouse_buttons);
|
||||
interpreterRegisterOpcode(0x8220, opGetScreenWidth);
|
||||
interpreterRegisterOpcode(0x8221, opGetScreenHeight);
|
||||
interpreterRegisterOpcode(0x8224, op_create_message_window);
|
||||
interpreterRegisterOpcode(0x8228, op_get_attack_type);
|
||||
interpreterRegisterOpcode(0x822D, opCreateArray);
|
||||
interpreterRegisterOpcode(0x822E, opSetArray);
|
||||
interpreterRegisterOpcode(0x822F, opGetArray);
|
||||
interpreterRegisterOpcode(0x8230, opFreeArray);
|
||||
interpreterRegisterOpcode(0x8231, opLenArray);
|
||||
interpreterRegisterOpcode(0x8232, opResizeArray);
|
||||
interpreterRegisterOpcode(0x8233, opTempArray);
|
||||
interpreterRegisterOpcode(0x8234, opFixArray);
|
||||
interpreterRegisterOpcode(0x8235, opStringSplit);
|
||||
interpreterRegisterOpcode(0x8237, opParseInt);
|
||||
interpreterRegisterOpcode(0x8238, op_atof);
|
||||
interpreterRegisterOpcode(0x8239, opScanArray);
|
||||
interpreterRegisterOpcode(0x824B, op_tile_under_cursor);
|
||||
interpreterRegisterOpcode(0x824E, opSubstr);
|
||||
interpreterRegisterOpcode(0x824F, opGetStringLength);
|
||||
interpreterRegisterOpcode(0x826B, opGetMessage);
|
||||
interpreterRegisterOpcode(0x8253, opTypeOf);
|
||||
interpreterRegisterOpcode(0x8256, opGetArrayKey);
|
||||
interpreterRegisterOpcode(0x8257, opStackArray);
|
||||
interpreterRegisterOpcode(0x8263, op_power);
|
||||
interpreterRegisterOpcode(0x8267, opRound);
|
||||
interpreterRegisterOpcode(0x826B, opGetMessage);
|
||||
interpreterRegisterOpcode(0x826E, op_make_straight_path);
|
||||
interpreterRegisterOpcode(0x826F, op_obj_blocking_at);
|
||||
interpreterRegisterOpcode(0x8271, opPartyMemberList);
|
||||
interpreterRegisterOpcode(0x8274, opArtExists);
|
||||
interpreterRegisterOpcode(0x827F, op_div);
|
||||
}
|
||||
|
||||
void sfallOpcodesExit()
|
||||
|
||||
+73
-9
@@ -1,5 +1,6 @@
|
||||
#include "sound.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
@@ -8,7 +9,6 @@
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
@@ -45,6 +45,13 @@ typedef struct FadeSound {
|
||||
static void* soundMallocProcDefaultImpl(size_t size);
|
||||
static void* soundReallocProcDefaultImpl(void* ptr, size_t size);
|
||||
static void soundFreeProcDefaultImpl(void* ptr);
|
||||
static long soundFileSize(int fileHandle);
|
||||
static long soundTellData(int fileHandle);
|
||||
static int soundWriteData(int fileHandle, const void* buf, unsigned int size);
|
||||
static int soundReadData(int fileHandle, void* buf, unsigned int size);
|
||||
static int soundOpenData(const char* filePath, int* sampleRate);
|
||||
static long soundSeekData(int fileHandle, long offset, int origin);
|
||||
static int soundCloseData(int fileHandle);
|
||||
static char* soundFileManglerDefaultImpl(char* fname);
|
||||
static void _refreshSoundBuffers(Sound* sound);
|
||||
static int _preloadBuffers(Sound* sound);
|
||||
@@ -77,13 +84,13 @@ static FreeProc* gSoundFreeProc = soundFreeProcDefaultImpl;
|
||||
|
||||
// 0x51D494
|
||||
static SoundFileIO gSoundDefaultFileIO = {
|
||||
open,
|
||||
close,
|
||||
compat_read,
|
||||
compat_write,
|
||||
compat_lseek,
|
||||
compat_tell,
|
||||
compat_filelength,
|
||||
soundOpenData,
|
||||
soundCloseData,
|
||||
soundReadData,
|
||||
soundWriteData,
|
||||
soundSeekData,
|
||||
soundTellData,
|
||||
soundFileSize,
|
||||
-1,
|
||||
};
|
||||
|
||||
@@ -184,6 +191,63 @@ void soundSetMemoryProcs(MallocProc* mallocProc, ReallocProc* reallocProc, FreeP
|
||||
gSoundFreeProc = freeProc;
|
||||
}
|
||||
|
||||
// 0x4AC71C
|
||||
static long soundFileSize(int fileHandle)
|
||||
{
|
||||
long pos;
|
||||
long size;
|
||||
|
||||
pos = compat_tell(fileHandle);
|
||||
size = lseek(fileHandle, 0, SEEK_END);
|
||||
lseek(fileHandle, pos, SEEK_SET);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
// 0x4AC750
|
||||
static long soundTellData(int fileHandle)
|
||||
{
|
||||
return compat_tell(fileHandle);
|
||||
}
|
||||
|
||||
// 0x4AC758
|
||||
static int soundWriteData(int fileHandle, const void* buf, unsigned int size)
|
||||
{
|
||||
return write(fileHandle, buf, size);
|
||||
}
|
||||
|
||||
// 0x4AC760
|
||||
static int soundReadData(int fileHandle, void* buf, unsigned int size)
|
||||
{
|
||||
return read(fileHandle, buf, size);
|
||||
}
|
||||
|
||||
// 0x4AC768
|
||||
static int soundOpenData(const char* filePath, int* sampleRate)
|
||||
{
|
||||
int flags;
|
||||
|
||||
#ifdef _WIN32
|
||||
flags = _O_RDONLY | _O_BINARY;
|
||||
#else
|
||||
flags = O_RDONLY;
|
||||
#endif
|
||||
|
||||
return open(filePath, flags);
|
||||
}
|
||||
|
||||
// 0x4AC774
|
||||
static long soundSeekData(int fileHandle, long offset, int origin)
|
||||
{
|
||||
return lseek(fileHandle, offset, origin);
|
||||
}
|
||||
|
||||
// 0x4AC77C
|
||||
static int soundCloseData(int fileHandle)
|
||||
{
|
||||
return close(fileHandle);
|
||||
}
|
||||
|
||||
// 0x4AC78C
|
||||
char* soundFileManglerDefaultImpl(char* fname)
|
||||
{
|
||||
@@ -552,7 +616,7 @@ int soundLoad(Sound* sound, char* filePath)
|
||||
return gSoundLastError;
|
||||
}
|
||||
|
||||
sound->io.fd = sound->io.open(gSoundFileNameMangler(filePath), 0x0200);
|
||||
sound->io.fd = sound->io.open(gSoundFileNameMangler(filePath), &(sound->rate));
|
||||
if (sound->io.fd == -1) {
|
||||
gSoundLastError = SOUND_FILE_NOT_FOUND;
|
||||
return gSoundLastError;
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ typedef enum SoundError {
|
||||
SOUND_ERR_COUNT,
|
||||
} SoundError;
|
||||
|
||||
typedef int SoundOpenProc(const char* filePath, int flags, ...);
|
||||
typedef int SoundOpenProc(const char* filePath, int* sampleRate);
|
||||
typedef int SoundCloseProc(int fileHandle);
|
||||
typedef int SoundReadProc(int fileHandle, void* buf, unsigned int size);
|
||||
typedef int SoundWriteProc(int fileHandle, const void* buf, unsigned int size);
|
||||
|
||||
+144
-110
@@ -17,68 +17,70 @@ namespace fallout {
|
||||
|
||||
typedef int (*ReadBandFunc)(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
|
||||
static bool soundDecoderPrepare(SoundDecoder* a1, SoundDecoderReadProc* readProc, int fileHandle);
|
||||
static unsigned char soundDecoderReadNextChunk(SoundDecoder* a1);
|
||||
static void _init_pack_tables();
|
||||
static int _ReadBand_Fail_(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int _ReadBand_Fmt0_(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int _ReadBand_Fmt3_16_(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int _ReadBand_Fmt17_(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int _ReadBand_Fmt18_(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int _ReadBand_Fmt19_(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int _ReadBand_Fmt20_(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int _ReadBand_Fmt21_(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int _ReadBand_Fmt22_(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int _ReadBand_Fmt23_(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int _ReadBand_Fmt24_(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int _ReadBand_Fmt26_(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int _ReadBand_Fmt27_(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int _ReadBand_Fmt29_(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int _ReadBands_(SoundDecoder* ptr);
|
||||
static void _untransform_subband0(unsigned char* a1, unsigned char* a2, int a3, int a4);
|
||||
static void _untransform_subband(unsigned char* a1, unsigned char* a2, int a3, int a4);
|
||||
static void _untransform_all(SoundDecoder* a1);
|
||||
static bool soundDecoderPrepare(SoundDecoder* soundDecoder, SoundDecoderReadProc* readProc, void* data);
|
||||
static unsigned char soundDecoderReadNextChunk(SoundDecoder* soundDecoder);
|
||||
static void init_pack_tables();
|
||||
static int ReadBand_Fail(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int ReadBand_Fmt0(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int ReadBand_Fmt3_16(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int ReadBand_Fmt17(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int ReadBand_Fmt18(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int ReadBand_Fmt19(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int ReadBand_Fmt20(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int ReadBand_Fmt21(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int ReadBand_Fmt22(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int ReadBand_Fmt23(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int ReadBand_Fmt24(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int ReadBand_Fmt26(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int ReadBand_Fmt27(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static int ReadBand_Fmt29(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
static bool ReadBands(SoundDecoder* soundDecoder);
|
||||
static void untransform_subband0(unsigned char* a1, unsigned char* a2, int a3, int a4);
|
||||
static void untransform_subband(unsigned char* a1, unsigned char* a2, int a3, int a4);
|
||||
static void untransform_all(SoundDecoder* soundDecoder);
|
||||
static bool soundDecoderFill(SoundDecoder* soundDecoder);
|
||||
|
||||
static inline void soundDecoderRequireBits(SoundDecoder* soundDecoder, int bits);
|
||||
static inline void soundDecoderDropBits(SoundDecoder* soundDecoder, int bits);
|
||||
static int ReadBand_Fmt31(SoundDecoder* soundDecoder, int offset, int bits);
|
||||
|
||||
// 0x51E328
|
||||
static int gSoundDecodersCount = 0;
|
||||
|
||||
// 0x51E330
|
||||
static ReadBandFunc _ReadBand_tbl[32] = {
|
||||
_ReadBand_Fmt0_,
|
||||
_ReadBand_Fail_,
|
||||
_ReadBand_Fail_,
|
||||
_ReadBand_Fmt3_16_,
|
||||
_ReadBand_Fmt3_16_,
|
||||
_ReadBand_Fmt3_16_,
|
||||
_ReadBand_Fmt3_16_,
|
||||
_ReadBand_Fmt3_16_,
|
||||
_ReadBand_Fmt3_16_,
|
||||
_ReadBand_Fmt3_16_,
|
||||
_ReadBand_Fmt3_16_,
|
||||
_ReadBand_Fmt3_16_,
|
||||
_ReadBand_Fmt3_16_,
|
||||
_ReadBand_Fmt3_16_,
|
||||
_ReadBand_Fmt3_16_,
|
||||
_ReadBand_Fmt3_16_,
|
||||
_ReadBand_Fmt3_16_,
|
||||
_ReadBand_Fmt17_,
|
||||
_ReadBand_Fmt18_,
|
||||
_ReadBand_Fmt19_,
|
||||
_ReadBand_Fmt20_,
|
||||
_ReadBand_Fmt21_,
|
||||
_ReadBand_Fmt22_,
|
||||
_ReadBand_Fmt23_,
|
||||
_ReadBand_Fmt24_,
|
||||
_ReadBand_Fail_,
|
||||
_ReadBand_Fmt26_,
|
||||
_ReadBand_Fmt27_,
|
||||
_ReadBand_Fail_,
|
||||
_ReadBand_Fmt29_,
|
||||
_ReadBand_Fail_,
|
||||
_ReadBand_Fail_,
|
||||
ReadBand_Fmt0,
|
||||
ReadBand_Fail,
|
||||
ReadBand_Fail,
|
||||
ReadBand_Fmt3_16,
|
||||
ReadBand_Fmt3_16,
|
||||
ReadBand_Fmt3_16,
|
||||
ReadBand_Fmt3_16,
|
||||
ReadBand_Fmt3_16,
|
||||
ReadBand_Fmt3_16,
|
||||
ReadBand_Fmt3_16,
|
||||
ReadBand_Fmt3_16,
|
||||
ReadBand_Fmt3_16,
|
||||
ReadBand_Fmt3_16,
|
||||
ReadBand_Fmt3_16,
|
||||
ReadBand_Fmt3_16,
|
||||
ReadBand_Fmt3_16,
|
||||
ReadBand_Fmt3_16,
|
||||
ReadBand_Fmt17,
|
||||
ReadBand_Fmt18,
|
||||
ReadBand_Fmt19,
|
||||
ReadBand_Fmt20,
|
||||
ReadBand_Fmt21,
|
||||
ReadBand_Fmt22,
|
||||
ReadBand_Fmt23,
|
||||
ReadBand_Fmt24,
|
||||
ReadBand_Fail,
|
||||
ReadBand_Fmt26,
|
||||
ReadBand_Fmt27,
|
||||
ReadBand_Fail,
|
||||
ReadBand_Fmt29,
|
||||
ReadBand_Fail,
|
||||
ReadBand_Fmt31,
|
||||
};
|
||||
|
||||
// 0x6AD960
|
||||
@@ -97,10 +99,10 @@ static unsigned char* _AudioDecoder_scale0;
|
||||
static unsigned char* _AudioDecoder_scale_tbl;
|
||||
|
||||
// 0x4D3BB0
|
||||
static bool soundDecoderPrepare(SoundDecoder* soundDecoder, SoundDecoderReadProc* readProc, int fileHandle)
|
||||
static bool soundDecoderPrepare(SoundDecoder* soundDecoder, SoundDecoderReadProc* readProc, void* data)
|
||||
{
|
||||
soundDecoder->readProc = readProc;
|
||||
soundDecoder->fd = fileHandle;
|
||||
soundDecoder->data = data;
|
||||
|
||||
soundDecoder->bufferIn = (unsigned char*)malloc(SOUND_DECODER_IN_BUFFER_SIZE);
|
||||
if (soundDecoder->bufferIn == NULL) {
|
||||
@@ -116,7 +118,7 @@ static bool soundDecoderPrepare(SoundDecoder* soundDecoder, SoundDecoderReadProc
|
||||
// 0x4D3BE0
|
||||
static unsigned char soundDecoderReadNextChunk(SoundDecoder* soundDecoder)
|
||||
{
|
||||
soundDecoder->remainingInSize = soundDecoder->readProc(soundDecoder->fd, soundDecoder->bufferIn, soundDecoder->bufferInSize);
|
||||
soundDecoder->remainingInSize = soundDecoder->readProc(soundDecoder->data, soundDecoder->bufferIn, soundDecoder->bufferInSize);
|
||||
if (soundDecoder->remainingInSize == 0) {
|
||||
memset(soundDecoder->bufferIn, 0, soundDecoder->bufferInSize);
|
||||
soundDecoder->remainingInSize = soundDecoder->bufferInSize;
|
||||
@@ -128,7 +130,7 @@ static unsigned char soundDecoderReadNextChunk(SoundDecoder* soundDecoder)
|
||||
}
|
||||
|
||||
// 0x4D3C78
|
||||
static void _init_pack_tables()
|
||||
static void init_pack_tables()
|
||||
{
|
||||
// 0x51E32C
|
||||
static bool inited = false;
|
||||
@@ -167,13 +169,13 @@ static void _init_pack_tables()
|
||||
}
|
||||
|
||||
// 0x4D3D9C
|
||||
static int _ReadBand_Fail_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
static int ReadBand_Fail(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x4D3DA0
|
||||
static int _ReadBand_Fmt0_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
static int ReadBand_Fmt0(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
{
|
||||
int* p = (int*)soundDecoder->samples;
|
||||
p += offset;
|
||||
@@ -189,7 +191,7 @@ static int _ReadBand_Fmt0_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
}
|
||||
|
||||
// 0x4D3DC8
|
||||
static int _ReadBand_Fmt3_16_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
static int ReadBand_Fmt3_16(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
{
|
||||
int value;
|
||||
int v14;
|
||||
@@ -218,7 +220,7 @@ static int _ReadBand_Fmt3_16_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
}
|
||||
|
||||
// 0x4D3E90
|
||||
static int _ReadBand_Fmt17_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
static int ReadBand_Fmt17(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
{
|
||||
short* base = (short*)_AudioDecoder_scale0;
|
||||
|
||||
@@ -272,7 +274,7 @@ static int _ReadBand_Fmt17_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
}
|
||||
|
||||
// 0x4D3F98
|
||||
static int _ReadBand_Fmt18_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
static int ReadBand_Fmt18(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
{
|
||||
short* base = (short*)_AudioDecoder_scale0;
|
||||
|
||||
@@ -310,7 +312,7 @@ static int _ReadBand_Fmt18_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
}
|
||||
|
||||
// 0x4D4068
|
||||
static int _ReadBand_Fmt19_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
static int ReadBand_Fmt19(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
{
|
||||
short* base = (short*)_AudioDecoder_scale0;
|
||||
base -= 1;
|
||||
@@ -348,7 +350,7 @@ static int _ReadBand_Fmt19_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
}
|
||||
|
||||
// 0x4D4158
|
||||
static int _ReadBand_Fmt20_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
static int ReadBand_Fmt20(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
{
|
||||
short* base = (short*)_AudioDecoder_scale0;
|
||||
|
||||
@@ -411,7 +413,7 @@ static int _ReadBand_Fmt20_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
}
|
||||
|
||||
// 0x4D4254
|
||||
static int _ReadBand_Fmt21_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
static int ReadBand_Fmt21(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
{
|
||||
short* base = (short*)_AudioDecoder_scale0;
|
||||
|
||||
@@ -458,7 +460,7 @@ static int _ReadBand_Fmt21_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
}
|
||||
|
||||
// 0x4D4338
|
||||
static int _ReadBand_Fmt22_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
static int ReadBand_Fmt22(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
{
|
||||
short* base = (short*)_AudioDecoder_scale0;
|
||||
base -= 2;
|
||||
@@ -500,7 +502,7 @@ static int _ReadBand_Fmt22_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
}
|
||||
|
||||
// 0x4D4434
|
||||
static int _ReadBand_Fmt23_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
static int ReadBand_Fmt23(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
{
|
||||
short* base = (short*)_AudioDecoder_scale0;
|
||||
|
||||
@@ -569,7 +571,7 @@ static int _ReadBand_Fmt23_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
}
|
||||
|
||||
// 0x4D4584
|
||||
static int _ReadBand_Fmt24_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
static int ReadBand_Fmt24(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
{
|
||||
short* base = (short*)_AudioDecoder_scale0;
|
||||
|
||||
@@ -623,7 +625,7 @@ static int _ReadBand_Fmt24_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
}
|
||||
|
||||
// 0x4D4698
|
||||
static int _ReadBand_Fmt26_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
static int ReadBand_Fmt26(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
{
|
||||
short* base = (short*)_AudioDecoder_scale0;
|
||||
|
||||
@@ -679,7 +681,7 @@ static int _ReadBand_Fmt26_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
}
|
||||
|
||||
// 0x4D47A4
|
||||
static int _ReadBand_Fmt27_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
static int ReadBand_Fmt27(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
{
|
||||
short* base = (short*)_AudioDecoder_scale0;
|
||||
|
||||
@@ -719,7 +721,7 @@ static int _ReadBand_Fmt27_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
}
|
||||
|
||||
// 0x4D4870
|
||||
static int _ReadBand_Fmt29_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
static int ReadBand_Fmt29(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
{
|
||||
short* base = (short*)_AudioDecoder_scale0;
|
||||
|
||||
@@ -751,7 +753,7 @@ static int _ReadBand_Fmt29_(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
}
|
||||
|
||||
// 0x4D493C
|
||||
static int _ReadBands_(SoundDecoder* soundDecoder)
|
||||
static bool ReadBands(SoundDecoder* soundDecoder)
|
||||
{
|
||||
int v9;
|
||||
int v15;
|
||||
@@ -788,7 +790,7 @@ static int _ReadBands_(SoundDecoder* soundDecoder)
|
||||
v21 -= v15;
|
||||
}
|
||||
|
||||
_init_pack_tables();
|
||||
init_pack_tables();
|
||||
|
||||
for (int index = 0; index < soundDecoder->subbands; index++) {
|
||||
soundDecoderRequireBits(soundDecoder, 5);
|
||||
@@ -797,14 +799,14 @@ static int _ReadBands_(SoundDecoder* soundDecoder)
|
||||
|
||||
fn = _ReadBand_tbl[bits];
|
||||
if (!fn(soundDecoder, index, bits)) {
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 0x4D4ADC
|
||||
static void _untransform_subband0(unsigned char* a1, unsigned char* a2, int a3, int a4)
|
||||
static void untransform_subband0(unsigned char* a1, unsigned char* a2, int a3, int a4)
|
||||
{
|
||||
short* p;
|
||||
|
||||
@@ -902,7 +904,7 @@ static void _untransform_subband0(unsigned char* a1, unsigned char* a2, int a3,
|
||||
}
|
||||
|
||||
// 0x4D4D1C
|
||||
static void _untransform_subband(unsigned char* a1, unsigned char* a2, int a3, int a4)
|
||||
static void untransform_subband(unsigned char* a1, unsigned char* a2, int a3, int a4)
|
||||
{
|
||||
int v13;
|
||||
int* v14;
|
||||
@@ -993,7 +995,7 @@ static void _untransform_subband(unsigned char* a1, unsigned char* a2, int a3, i
|
||||
}
|
||||
|
||||
// 0x4D4E80
|
||||
static void _untransform_all(SoundDecoder* soundDecoder)
|
||||
static void untransform_all(SoundDecoder* soundDecoder)
|
||||
{
|
||||
int v8;
|
||||
unsigned char* ptr;
|
||||
@@ -1019,7 +1021,7 @@ static void _untransform_all(SoundDecoder* soundDecoder)
|
||||
|
||||
v4 *= 2;
|
||||
|
||||
_untransform_subband0(soundDecoder->prev_samples, ptr, v3, v4);
|
||||
untransform_subband0(soundDecoder->prev_samples, ptr, v3, v4);
|
||||
|
||||
v5 = (int*)ptr;
|
||||
for (v6 = 0; v6 < v4; v6++) {
|
||||
@@ -1034,7 +1036,7 @@ static void _untransform_all(SoundDecoder* soundDecoder)
|
||||
if (v3 == 0) {
|
||||
break;
|
||||
}
|
||||
_untransform_subband(j, ptr, v3, v4);
|
||||
untransform_subband(j, ptr, v3, v4);
|
||||
j += 8 * v3;
|
||||
}
|
||||
|
||||
@@ -1043,53 +1045,67 @@ static void _untransform_all(SoundDecoder* soundDecoder)
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Inlined.
|
||||
//
|
||||
// 0x4D4F58
|
||||
static bool soundDecoderFill(SoundDecoder* soundDecoder)
|
||||
{
|
||||
// CE: Implementation is slightly different. `ReadBands` now handles new
|
||||
// Fmt31 used in some Russian localizations. The appropriate handler acts as
|
||||
// both decoder and transformer, so there is no need to untransform bands
|
||||
// once again. This approach assumes band 31 is never used by standard acms
|
||||
// and mods.
|
||||
if (ReadBands(soundDecoder)) {
|
||||
untransform_all(soundDecoder);
|
||||
}
|
||||
|
||||
soundDecoder->file_cnt -= soundDecoder->total_samples;
|
||||
soundDecoder->samp_ptr = soundDecoder->samples;
|
||||
soundDecoder->samp_cnt = soundDecoder->total_samples;
|
||||
|
||||
if (soundDecoder->file_cnt < 0) {
|
||||
soundDecoder->samp_cnt += soundDecoder->file_cnt;
|
||||
soundDecoder->file_cnt = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 0x4D4FA0
|
||||
size_t soundDecoderDecode(SoundDecoder* soundDecoder, void* buffer, size_t size)
|
||||
{
|
||||
unsigned char* dest;
|
||||
unsigned char* v5;
|
||||
int v6;
|
||||
int v4;
|
||||
unsigned char* samp_ptr;
|
||||
int samp_cnt;
|
||||
|
||||
dest = (unsigned char*)buffer;
|
||||
v4 = 0;
|
||||
v5 = soundDecoder->samp_ptr;
|
||||
v6 = soundDecoder->samp_cnt;
|
||||
samp_ptr = soundDecoder->samp_ptr;
|
||||
samp_cnt = soundDecoder->samp_cnt;
|
||||
|
||||
size_t bytesRead;
|
||||
for (bytesRead = 0; bytesRead < size; bytesRead += 2) {
|
||||
if (!v6) {
|
||||
if (!soundDecoder->file_cnt) {
|
||||
if (samp_cnt == 0) {
|
||||
if (soundDecoder->file_cnt == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!_ReadBands_(soundDecoder)) {
|
||||
// NOTE: Uninline.
|
||||
if (!soundDecoderFill(soundDecoder)) {
|
||||
break;
|
||||
}
|
||||
|
||||
_untransform_all(soundDecoder);
|
||||
|
||||
soundDecoder->file_cnt -= soundDecoder->total_samples;
|
||||
soundDecoder->samp_ptr = soundDecoder->samples;
|
||||
soundDecoder->samp_cnt = soundDecoder->total_samples;
|
||||
|
||||
if (soundDecoder->file_cnt < 0) {
|
||||
soundDecoder->samp_cnt += soundDecoder->file_cnt;
|
||||
soundDecoder->file_cnt = 0;
|
||||
}
|
||||
|
||||
v5 = soundDecoder->samp_ptr;
|
||||
v6 = soundDecoder->samp_cnt;
|
||||
samp_ptr = soundDecoder->samp_ptr;
|
||||
samp_cnt = soundDecoder->samp_cnt;
|
||||
}
|
||||
|
||||
int v13 = *(int*)v5;
|
||||
v5 += 4;
|
||||
*(unsigned short*)(dest + bytesRead) = (v13 >> soundDecoder->levels) & 0xFFFF;
|
||||
v6--;
|
||||
int sample = *(int*)samp_ptr;
|
||||
samp_ptr += 4;
|
||||
*(unsigned short*)(dest + bytesRead) = (sample >> soundDecoder->levels) & 0xFFFF;
|
||||
samp_cnt--;
|
||||
}
|
||||
|
||||
soundDecoder->samp_ptr = v5;
|
||||
soundDecoder->samp_cnt = v6;
|
||||
soundDecoder->samp_ptr = samp_ptr;
|
||||
soundDecoder->samp_cnt = samp_cnt;
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
@@ -1122,7 +1138,7 @@ void soundDecoderFree(SoundDecoder* soundDecoder)
|
||||
}
|
||||
|
||||
// 0x4D50A8
|
||||
SoundDecoder* soundDecoderInit(SoundDecoderReadProc* readProc, int fileHandle, int* channelsPtr, int* sampleRatePtr, int* sampleCountPtr)
|
||||
SoundDecoder* soundDecoderInit(SoundDecoderReadProc* readProc, void* data, int* channelsPtr, int* sampleRatePtr, int* sampleCountPtr)
|
||||
{
|
||||
int v14;
|
||||
int v20;
|
||||
@@ -1137,7 +1153,7 @@ SoundDecoder* soundDecoderInit(SoundDecoderReadProc* readProc, int fileHandle, i
|
||||
|
||||
gSoundDecodersCount++;
|
||||
|
||||
if (!soundDecoderPrepare(soundDecoder, readProc, fileHandle)) {
|
||||
if (!soundDecoderPrepare(soundDecoder, readProc, data)) {
|
||||
goto L66;
|
||||
}
|
||||
|
||||
@@ -1259,4 +1275,22 @@ static inline void soundDecoderDropBits(SoundDecoder* soundDecoder, int bits)
|
||||
soundDecoder->bits -= bits;
|
||||
}
|
||||
|
||||
static int ReadBand_Fmt31(SoundDecoder* soundDecoder, int offset, int bits)
|
||||
{
|
||||
int* samples = (int*)soundDecoder->samples;
|
||||
|
||||
int remaining_samples = soundDecoder->total_samples;
|
||||
while (remaining_samples != 0) {
|
||||
soundDecoderRequireBits(soundDecoder, 16);
|
||||
int value = soundDecoder->hold & 0xFFFF;
|
||||
soundDecoderDropBits(soundDecoder, 16);
|
||||
|
||||
*samples++ = (value << 16) >> (16 - soundDecoder->levels);
|
||||
|
||||
remaining_samples--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
+3
-3
@@ -5,11 +5,11 @@
|
||||
|
||||
namespace fallout {
|
||||
|
||||
typedef int(SoundDecoderReadProc)(int fileHandle, void* buffer, unsigned int size);
|
||||
typedef int(SoundDecoderReadProc)(void* data, void* buffer, unsigned int size);
|
||||
|
||||
typedef struct SoundDecoder {
|
||||
SoundDecoderReadProc* readProc;
|
||||
int fd;
|
||||
void* data;
|
||||
unsigned char* bufferIn;
|
||||
size_t bufferInSize;
|
||||
|
||||
@@ -41,7 +41,7 @@ typedef struct SoundDecoder {
|
||||
|
||||
size_t soundDecoderDecode(SoundDecoder* soundDecoder, void* buffer, size_t size);
|
||||
void soundDecoderFree(SoundDecoder* soundDecoder);
|
||||
SoundDecoder* soundDecoderInit(SoundDecoderReadProc* readProc, int fileHandle, int* channelsPtr, int* sampleRatePtr, int* sampleCountPtr);
|
||||
SoundDecoder* soundDecoderInit(SoundDecoderReadProc* readProc, void* data, int* channelsPtr, int* sampleRatePtr, int* sampleCountPtr);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ static void soundEffectsCacheFreeHandles();
|
||||
static int soundEffectsCreate(int* handlePtr, int id, void* data, CacheEntry* cacheHandle);
|
||||
static bool soundEffectsIsValidHandle(int a1);
|
||||
static int soundEffectsCacheFileReadCompressed(int handle, void* buf, unsigned int size);
|
||||
static int _sfxc_ad_reader(int handle, void* buf, unsigned int size);
|
||||
static int soundEffectsCacheSoundDecoderReadHandler(void* data, void* buf, unsigned int size);
|
||||
|
||||
// 0x50DE04
|
||||
static const char* off_50DE04 = "";
|
||||
@@ -154,7 +154,7 @@ void soundEffectsCacheFlush()
|
||||
|
||||
// sfxc_cached_open
|
||||
// 0x4A915C
|
||||
int soundEffectsCacheFileOpen(const char* fname, int mode, ...)
|
||||
int soundEffectsCacheFileOpen(const char* fname, int* sampleRate)
|
||||
{
|
||||
if (_sfxc_files_open >= SOUND_EFFECTS_MAX_COUNT) {
|
||||
return -1;
|
||||
@@ -476,7 +476,7 @@ static int soundEffectsCacheFileReadCompressed(int handle, void* buf, unsigned i
|
||||
int channels;
|
||||
int sampleRate;
|
||||
int sampleCount;
|
||||
SoundDecoder* soundDecoder = soundDecoderInit(_sfxc_ad_reader, handle, &channels, &sampleRate, &sampleCount);
|
||||
SoundDecoder* soundDecoder = soundDecoderInit(soundEffectsCacheSoundDecoderReadHandler, &handle, &channels, &sampleRate, &sampleCount);
|
||||
|
||||
if (soundEffect->position != 0) {
|
||||
void* temp = internal_malloc(soundEffect->position);
|
||||
@@ -505,12 +505,13 @@ static int soundEffectsCacheFileReadCompressed(int handle, void* buf, unsigned i
|
||||
}
|
||||
|
||||
// 0x4A9774
|
||||
static int _sfxc_ad_reader(int handle, void* buf, unsigned int size)
|
||||
static int soundEffectsCacheSoundDecoderReadHandler(void* data, void* buf, unsigned int size)
|
||||
{
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handle = *reinterpret_cast<int*>(data);
|
||||
SoundEffect* soundEffect = &(gSoundEffects[handle]);
|
||||
|
||||
unsigned int bytesToRead = soundEffect->fileSize - soundEffect->dataPosition;
|
||||
|
||||
@@ -11,7 +11,7 @@ int soundEffectsCacheInit(int cache_size, const char* effectsPath);
|
||||
void soundEffectsCacheExit();
|
||||
int soundEffectsCacheInitialized();
|
||||
void soundEffectsCacheFlush();
|
||||
int soundEffectsCacheFileOpen(const char* fname, int mode, ...);
|
||||
int soundEffectsCacheFileOpen(const char* fname, int* sampleRate);
|
||||
int soundEffectsCacheFileClose(int handle);
|
||||
int soundEffectsCacheFileRead(int handle, void* buf, unsigned int size);
|
||||
int soundEffectsCacheFileWrite(int handle, const void* buf, unsigned int size);
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "debug.h"
|
||||
#include "memory.h"
|
||||
#include "platform_compat.h"
|
||||
#include "pointer_registry.h"
|
||||
#include "sound_decoder.h"
|
||||
|
||||
namespace fallout {
|
||||
@@ -28,7 +27,7 @@ static int soundEffectsListCopyFileNames(char** fileNameList);
|
||||
static int soundEffectsListPopulateFileSizes();
|
||||
static int soundEffectsListSort();
|
||||
static int soundEffectsListCompareByName(const void* a1, const void* a2);
|
||||
static int _sfxl_ad_reader(int fileHandle, void* buf, unsigned int size);
|
||||
static int soundEffectsListSoundDecoderReadHandler(void* data, void* buf, unsigned int size);
|
||||
|
||||
// 0x51C8F8
|
||||
static bool gSoundEffectsListInitialized = false;
|
||||
@@ -424,16 +423,13 @@ static int soundEffectsListPopulateFileSizes()
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fileHandle = ptrToInt((void*)stream);
|
||||
|
||||
int channels;
|
||||
int sampleRate;
|
||||
int sampleCount;
|
||||
SoundDecoder* soundDecoder = soundDecoderInit(_sfxl_ad_reader, fileHandle, &channels, &sampleRate, &sampleCount);
|
||||
SoundDecoder* soundDecoder = soundDecoderInit(soundEffectsListSoundDecoderReadHandler, stream, &channels, &sampleRate, &sampleCount);
|
||||
entry->dataSize = 2 * sampleCount;
|
||||
soundDecoderFree(soundDecoder);
|
||||
fileClose(stream);
|
||||
intToPtr(fileHandle, true);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -468,9 +464,9 @@ static int soundEffectsListCompareByName(const void* a1, const void* a2)
|
||||
}
|
||||
|
||||
// read via xfile
|
||||
static int _sfxl_ad_reader(int fileHandle, void* buf, unsigned int size)
|
||||
static int soundEffectsListSoundDecoderReadHandler(void* data, void* buf, unsigned int size)
|
||||
{
|
||||
return fileRead(buf, 1, size, (File*)intToPtr(fileHandle));
|
||||
return fileRead(buf, 1, size, reinterpret_cast<File*>(data));
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
@@ -208,6 +208,10 @@ int strParseIntWithKey(char** stringPtr, const char* key, int* valuePtr, const c
|
||||
*(str + v4) = tmp2;
|
||||
*(str + v2) = tmp1;
|
||||
|
||||
if (**stringPtr == ',') {
|
||||
*stringPtr = *stringPtr + 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
-25
@@ -9,7 +9,6 @@
|
||||
#include "draw.h"
|
||||
#include "interface.h"
|
||||
#include "memory.h"
|
||||
#include "mmx.h"
|
||||
#include "mouse.h"
|
||||
#include "win32.h"
|
||||
#include "window_manager.h"
|
||||
@@ -20,9 +19,6 @@ namespace fallout {
|
||||
static bool createRenderer(int width, int height);
|
||||
static void destroyRenderer();
|
||||
|
||||
// 0x51E2C8
|
||||
bool gMmxEnabled = true;
|
||||
|
||||
// screen rect
|
||||
Rect _scr_size;
|
||||
|
||||
@@ -41,25 +37,6 @@ SDL_Surface* gSdlTextureSurface = NULL;
|
||||
// TODO: Remove once migration to update-render cycle is completed.
|
||||
FpsLimiter sharedFpsLimiter;
|
||||
|
||||
// 0x4CACD0
|
||||
void mmxSetEnabled(bool a1)
|
||||
{
|
||||
// 0x51E2CC
|
||||
static bool probed = false;
|
||||
|
||||
// 0x6ACA20
|
||||
static bool supported;
|
||||
|
||||
if (!probed) {
|
||||
supported = mmxIsSupported();
|
||||
probed = true;
|
||||
}
|
||||
|
||||
if (supported) {
|
||||
gMmxEnabled = a1;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4CAD08
|
||||
int _init_mode_320_200()
|
||||
{
|
||||
@@ -165,8 +142,6 @@ int _GNW95_init_mode_ex(int width, int height, int bpp)
|
||||
_scr_size.right = width - 1;
|
||||
_scr_size.bottom = height - 1;
|
||||
|
||||
mmxSetEnabled(true);
|
||||
|
||||
_mouse_blit_trans = NULL;
|
||||
_scr_blit = _GNW95_ShowRect;
|
||||
_zero_mem = _GNW95_zero_vid_mem;
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
|
||||
namespace fallout {
|
||||
|
||||
extern bool gMmxEnabled;
|
||||
|
||||
extern Rect _scr_size;
|
||||
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)();
|
||||
@@ -21,7 +19,6 @@ extern SDL_Texture* gSdlTexture;
|
||||
extern SDL_Surface* gSdlTextureSurface;
|
||||
extern FpsLimiter sharedFpsLimiter;
|
||||
|
||||
void mmxSetEnabled(bool a1);
|
||||
int _init_mode_320_200();
|
||||
int _init_mode_320_400();
|
||||
int _init_mode_640_480_16();
|
||||
|
||||
+40
-45
@@ -5,6 +5,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <stack>
|
||||
|
||||
#include "art.h"
|
||||
#include "color.h"
|
||||
@@ -49,11 +50,16 @@ typedef struct UpsideDownTriangle {
|
||||
int field_8;
|
||||
} UpsideDownTriangle;
|
||||
|
||||
struct roof_fill_task {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
static void tileSetBorder(int windowWidth, int windowHeight, int hexGridWidth, int hexGridHeight);
|
||||
static void tileRefreshMapper(Rect* rect, int elevation);
|
||||
static void tileRefreshGame(Rect* rect, int elevation);
|
||||
static void roof_fill_on(int x, int y, int elevation);
|
||||
static void roof_fill_off(int x, int y, int elevation);
|
||||
static void roof_fill_push_task_if_in_bounds(std::stack<roof_fill_task>& tasks_stack, int x, int y);
|
||||
static void roof_fill_off_process_task(std::stack<roof_fill_task>& tasks_stack, int elevation, bool on);
|
||||
static void tileRenderRoof(int fid, int x, int y, Rect* rect, int light);
|
||||
static void _draw_grid(int tile, int elevation, Rect* rect);
|
||||
static void tileRenderFloor(int fid, int x, int y, Rect* rect);
|
||||
@@ -1258,27 +1264,39 @@ void tileRenderRoofsInRect(Rect* rect, int elevation)
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4B22D0
|
||||
static void roof_fill_on(int x, int y, int elevation)
|
||||
static void roof_fill_push_task_if_in_bounds(std::stack<roof_fill_task>& tasks_stack, int x, int y)
|
||||
{
|
||||
if (x >= 0 && x < gSquareGridWidth && y >= 0 && y < gSquareGridHeight) {
|
||||
int squareTileIndex = gSquareGridWidth * y + x;
|
||||
int squareTile = gTileSquares[elevation]->field_0[squareTileIndex];
|
||||
int roof = (squareTile >> 16) & 0xFFFF;
|
||||
tasks_stack.push(roof_fill_task { x, y });
|
||||
};
|
||||
};
|
||||
|
||||
int id = roof & 0xFFF;
|
||||
if (buildFid(OBJ_TYPE_TILE, id, 0, 0, 0) != buildFid(OBJ_TYPE_TILE, 1, 0, 0, 0)) {
|
||||
int flag = (roof & 0xF000) >> 12;
|
||||
if ((flag & 0x01) != 0) {
|
||||
static void roof_fill_off_process_task(std::stack<roof_fill_task>& tasks_stack, int elevation, bool on)
|
||||
{
|
||||
auto [x, y] = tasks_stack.top();
|
||||
tasks_stack.pop();
|
||||
|
||||
int squareTileIndex = gSquareGridWidth * y + x;
|
||||
int squareTile = gTileSquares[elevation]->field_0[squareTileIndex];
|
||||
int roof = (squareTile >> 16) & 0xFFFF;
|
||||
|
||||
int id = roof & 0xFFF;
|
||||
if (buildFid(OBJ_TYPE_TILE, id, 0, 0, 0) != buildFid(OBJ_TYPE_TILE, 1, 0, 0, 0)) {
|
||||
int flag = (roof & 0xF000) >> 12;
|
||||
|
||||
if (on ? ((flag & 0x01) != 0) : ((flag & 0x03) == 0)) {
|
||||
if (on) {
|
||||
flag &= ~0x01;
|
||||
|
||||
gTileSquares[elevation]->field_0[squareTileIndex] = (squareTile & 0xFFFF) | (((flag << 12) | id) << 16);
|
||||
|
||||
roof_fill_on(x - 1, y, elevation);
|
||||
roof_fill_on(x + 1, y, elevation);
|
||||
roof_fill_on(x, y - 1, elevation);
|
||||
roof_fill_on(x, y + 1, elevation);
|
||||
} else {
|
||||
flag |= 0x01;
|
||||
}
|
||||
|
||||
gTileSquares[elevation]->field_0[squareTileIndex] = (squareTile & 0xFFFF) | (((flag << 12) | id) << 16);
|
||||
|
||||
roof_fill_push_task_if_in_bounds(tasks_stack, x - 1, y);
|
||||
roof_fill_push_task_if_in_bounds(tasks_stack, x + 1, y);
|
||||
roof_fill_push_task_if_in_bounds(tasks_stack, x, y - 1);
|
||||
roof_fill_push_task_if_in_bounds(tasks_stack, x, y + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1286,35 +1304,12 @@ static void roof_fill_on(int x, int y, int elevation)
|
||||
// 0x4B23D4
|
||||
void tile_fill_roof(int x, int y, int elevation, bool on)
|
||||
{
|
||||
if (on) {
|
||||
roof_fill_on(x, y, elevation);
|
||||
} else {
|
||||
roof_fill_off(x, y, elevation);
|
||||
}
|
||||
}
|
||||
std::stack<roof_fill_task> tasks_stack;
|
||||
|
||||
// 0x4B23DC
|
||||
static void roof_fill_off(int x, int y, int elevation)
|
||||
{
|
||||
if (x >= 0 && x < gSquareGridWidth && y >= 0 && y < gSquareGridHeight) {
|
||||
int squareTileIndex = gSquareGridWidth * y + x;
|
||||
int squareTile = gTileSquares[elevation]->field_0[squareTileIndex];
|
||||
int roof = (squareTile >> 16) & 0xFFFF;
|
||||
roof_fill_push_task_if_in_bounds(tasks_stack, x, y);
|
||||
|
||||
int id = roof & 0xFFF;
|
||||
if (buildFid(OBJ_TYPE_TILE, id, 0, 0, 0) != buildFid(OBJ_TYPE_TILE, 1, 0, 0, 0)) {
|
||||
int flag = (roof & 0xF000) >> 12;
|
||||
if ((flag & 0x03) == 0) {
|
||||
flag |= 0x01;
|
||||
|
||||
gTileSquares[elevation]->field_0[squareTileIndex] = (squareTile & 0xFFFF) | (((flag << 12) | id) << 16);
|
||||
|
||||
roof_fill_off(x - 1, y, elevation);
|
||||
roof_fill_off(x + 1, y, elevation);
|
||||
roof_fill_off(x, y - 1, elevation);
|
||||
roof_fill_off(x, y + 1, elevation);
|
||||
}
|
||||
}
|
||||
while (!tasks_stack.empty()) {
|
||||
roof_fill_off_process_task(tasks_stack, elevation, on);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+290
@@ -0,0 +1,290 @@
|
||||
#include "touch.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stack>
|
||||
|
||||
#include "svga.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
#define TOUCH_PHASE_BEGAN 0
|
||||
#define TOUCH_PHASE_MOVED 1
|
||||
#define TOUCH_PHASE_ENDED 2
|
||||
|
||||
#define MAX_TOUCHES 10
|
||||
|
||||
#define TAP_MAXIMUM_DURATION 75
|
||||
#define PAN_MINIMUM_MOVEMENT 4
|
||||
#define LONG_PRESS_MINIMUM_DURATION 500
|
||||
|
||||
struct TouchLocation {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
struct Touch {
|
||||
bool used;
|
||||
SDL_FingerID fingerId;
|
||||
TouchLocation startLocation;
|
||||
Uint32 startTimestamp;
|
||||
TouchLocation currentLocation;
|
||||
Uint32 currentTimestamp;
|
||||
int phase;
|
||||
};
|
||||
|
||||
static Touch touches[MAX_TOUCHES];
|
||||
static Gesture currentGesture;
|
||||
static std::stack<Gesture> gestureEventsQueue;
|
||||
|
||||
static int find_touch(SDL_FingerID fingerId)
|
||||
{
|
||||
for (int index = 0; index < MAX_TOUCHES; index++) {
|
||||
if (touches[index].fingerId == fingerId) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int find_unused_touch_index()
|
||||
{
|
||||
for (int index = 0; index < MAX_TOUCHES; index++) {
|
||||
if (!touches[index].used) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static TouchLocation touch_get_start_location_centroid(int* indexes, int length)
|
||||
{
|
||||
TouchLocation centroid;
|
||||
centroid.x = 0;
|
||||
centroid.y = 0;
|
||||
for (int index = 0; index < length; index++) {
|
||||
centroid.x += touches[indexes[index]].startLocation.x;
|
||||
centroid.y += touches[indexes[index]].startLocation.y;
|
||||
}
|
||||
centroid.x /= length;
|
||||
centroid.y /= length;
|
||||
return centroid;
|
||||
}
|
||||
|
||||
static TouchLocation touch_get_current_location_centroid(int* indexes, int length)
|
||||
{
|
||||
TouchLocation centroid;
|
||||
centroid.x = 0;
|
||||
centroid.y = 0;
|
||||
for (int index = 0; index < length; index++) {
|
||||
centroid.x += touches[indexes[index]].currentLocation.x;
|
||||
centroid.y += touches[indexes[index]].currentLocation.y;
|
||||
}
|
||||
centroid.x /= length;
|
||||
centroid.y /= length;
|
||||
return centroid;
|
||||
}
|
||||
|
||||
void touch_handle_start(SDL_TouchFingerEvent* event)
|
||||
{
|
||||
// On iOS `fingerId` is an address of underlying `UITouch` object. When
|
||||
// `touchesBegan` is called this object might be reused, but with
|
||||
// incresed `tapCount` (which is ignored in this implementation).
|
||||
int index = find_touch(event->fingerId);
|
||||
if (index == -1) {
|
||||
index = find_unused_touch_index();
|
||||
}
|
||||
|
||||
if (index != -1) {
|
||||
Touch* touch = &(touches[index]);
|
||||
touch->used = true;
|
||||
touch->fingerId = event->fingerId;
|
||||
touch->startTimestamp = event->timestamp;
|
||||
touch->startLocation.x = static_cast<int>(event->x * screenGetWidth());
|
||||
touch->startLocation.y = static_cast<int>(event->y * screenGetHeight());
|
||||
touch->currentTimestamp = touch->startTimestamp;
|
||||
touch->currentLocation = touch->startLocation;
|
||||
touch->phase = TOUCH_PHASE_BEGAN;
|
||||
}
|
||||
}
|
||||
|
||||
void touch_handle_move(SDL_TouchFingerEvent* event)
|
||||
{
|
||||
int index = find_touch(event->fingerId);
|
||||
if (index != -1) {
|
||||
Touch* touch = &(touches[index]);
|
||||
touch->currentTimestamp = event->timestamp;
|
||||
touch->currentLocation.x = static_cast<int>(event->x * screenGetWidth());
|
||||
touch->currentLocation.y = static_cast<int>(event->y * screenGetHeight());
|
||||
touch->phase = TOUCH_PHASE_MOVED;
|
||||
}
|
||||
}
|
||||
|
||||
void touch_handle_end(SDL_TouchFingerEvent* event)
|
||||
{
|
||||
int index = find_touch(event->fingerId);
|
||||
if (index != -1) {
|
||||
Touch* touch = &(touches[index]);
|
||||
touch->currentTimestamp = event->timestamp;
|
||||
touch->currentLocation.x = static_cast<int>(event->x * screenGetWidth());
|
||||
touch->currentLocation.y = static_cast<int>(event->y * screenGetHeight());
|
||||
touch->phase = TOUCH_PHASE_ENDED;
|
||||
}
|
||||
}
|
||||
|
||||
void touch_process_gesture()
|
||||
{
|
||||
Uint32 sequenceStartTimestamp = -1;
|
||||
int sequenceStartIndex = -1;
|
||||
|
||||
// Find start of sequence (earliest touch).
|
||||
for (int index = 0; index < MAX_TOUCHES; index++) {
|
||||
if (touches[index].used) {
|
||||
if (sequenceStartTimestamp > touches[index].startTimestamp) {
|
||||
sequenceStartTimestamp = touches[index].startTimestamp;
|
||||
sequenceStartIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sequenceStartIndex == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Uint32 sequenceEndTimestamp = -1;
|
||||
if (touches[sequenceStartIndex].phase == TOUCH_PHASE_ENDED) {
|
||||
sequenceEndTimestamp = touches[sequenceStartIndex].currentTimestamp;
|
||||
|
||||
// Find end timestamp of sequence.
|
||||
for (int index = 0; index < MAX_TOUCHES; index++) {
|
||||
if (touches[index].used
|
||||
&& touches[index].startTimestamp >= sequenceStartTimestamp
|
||||
&& touches[index].startTimestamp <= sequenceEndTimestamp) {
|
||||
if (touches[index].phase == TOUCH_PHASE_ENDED) {
|
||||
if (sequenceEndTimestamp < touches[index].currentTimestamp) {
|
||||
sequenceEndTimestamp = touches[index].currentTimestamp;
|
||||
|
||||
// Start over since we can have fingers missed.
|
||||
index = -1;
|
||||
}
|
||||
} else {
|
||||
// Sequence is current.
|
||||
sequenceEndTimestamp = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int active[MAX_TOUCHES];
|
||||
int activeCount = 0;
|
||||
|
||||
int ended[MAX_TOUCHES];
|
||||
int endedCount = 0;
|
||||
|
||||
// Split participating fingers into two buckets - active fingers (currently
|
||||
// on screen) and ended (lifted up).
|
||||
for (int index = 0; index < MAX_TOUCHES; index++) {
|
||||
if (touches[index].used
|
||||
&& touches[index].currentTimestamp >= sequenceStartTimestamp
|
||||
&& touches[index].currentTimestamp <= sequenceEndTimestamp) {
|
||||
if (touches[index].phase == TOUCH_PHASE_ENDED) {
|
||||
ended[endedCount++] = index;
|
||||
} else {
|
||||
active[activeCount++] = index;
|
||||
}
|
||||
|
||||
// If this sequence is over, unmark participating finger as used.
|
||||
if (sequenceEndTimestamp != -1) {
|
||||
touches[index].used = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentGesture.type == kPan || currentGesture.type == kLongPress) {
|
||||
if (currentGesture.state != kEnded) {
|
||||
// For continuous gestures we want number of fingers to remain the
|
||||
// same as it was when gesture was recognized.
|
||||
if (activeCount == currentGesture.numberOfTouches && endedCount == 0) {
|
||||
TouchLocation centroid = touch_get_current_location_centroid(active, activeCount);
|
||||
currentGesture.state = kChanged;
|
||||
currentGesture.x = centroid.x;
|
||||
currentGesture.y = centroid.y;
|
||||
gestureEventsQueue.push(currentGesture);
|
||||
} else {
|
||||
currentGesture.state = kEnded;
|
||||
gestureEventsQueue.push(currentGesture);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset continuous gesture if when current sequence is over.
|
||||
if (currentGesture.state == kEnded && sequenceEndTimestamp != -1) {
|
||||
currentGesture.type = kUnrecognized;
|
||||
}
|
||||
} else {
|
||||
if (activeCount == 0 && endedCount != 0) {
|
||||
// For taps we need all participating fingers to be both started
|
||||
// and ended simultaneously (within predefined threshold).
|
||||
Uint32 startEarliestTimestamp = -1;
|
||||
Uint32 startLatestTimestamp = 0;
|
||||
Uint32 endEarliestTimestamp = -1;
|
||||
Uint32 endLatestTimestamp = 0;
|
||||
|
||||
for (int index = 0; index < endedCount; index++) {
|
||||
startEarliestTimestamp = std::min(startEarliestTimestamp, touches[ended[index]].startTimestamp);
|
||||
startLatestTimestamp = std::max(startLatestTimestamp, touches[ended[index]].startTimestamp);
|
||||
endEarliestTimestamp = std::min(endEarliestTimestamp, touches[ended[index]].currentTimestamp);
|
||||
endLatestTimestamp = std::max(endLatestTimestamp, touches[ended[index]].currentTimestamp);
|
||||
}
|
||||
|
||||
if (startLatestTimestamp - startEarliestTimestamp <= TAP_MAXIMUM_DURATION
|
||||
&& endLatestTimestamp - endEarliestTimestamp <= TAP_MAXIMUM_DURATION) {
|
||||
TouchLocation currentCentroid = touch_get_current_location_centroid(ended, endedCount);
|
||||
|
||||
currentGesture.type = kTap;
|
||||
currentGesture.state = kEnded;
|
||||
currentGesture.numberOfTouches = endedCount;
|
||||
currentGesture.x = currentCentroid.x;
|
||||
currentGesture.y = currentCentroid.y;
|
||||
gestureEventsQueue.push(currentGesture);
|
||||
|
||||
// Reset tap gesture immediately.
|
||||
currentGesture.type = kUnrecognized;
|
||||
}
|
||||
} else if (activeCount != 0 && endedCount == 0) {
|
||||
TouchLocation startCentroid = touch_get_start_location_centroid(active, activeCount);
|
||||
TouchLocation currentCentroid = touch_get_current_location_centroid(active, activeCount);
|
||||
|
||||
// Disambiguate between pan and long press.
|
||||
if (abs(currentCentroid.x - startCentroid.x) >= PAN_MINIMUM_MOVEMENT
|
||||
|| abs(currentCentroid.y - startCentroid.y) >= PAN_MINIMUM_MOVEMENT) {
|
||||
currentGesture.type = kPan;
|
||||
currentGesture.state = kBegan;
|
||||
currentGesture.numberOfTouches = activeCount;
|
||||
currentGesture.x = currentCentroid.x;
|
||||
currentGesture.y = currentCentroid.y;
|
||||
gestureEventsQueue.push(currentGesture);
|
||||
} else if (SDL_GetTicks() - touches[active[0]].startTimestamp >= LONG_PRESS_MINIMUM_DURATION) {
|
||||
currentGesture.type = kLongPress;
|
||||
currentGesture.state = kBegan;
|
||||
currentGesture.numberOfTouches = activeCount;
|
||||
currentGesture.x = currentCentroid.x;
|
||||
currentGesture.y = currentCentroid.y;
|
||||
gestureEventsQueue.push(currentGesture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool touch_get_gesture(Gesture* gesture)
|
||||
{
|
||||
if (gestureEventsQueue.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*gesture = gestureEventsQueue.top();
|
||||
gestureEventsQueue.pop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
#ifndef FALLOUT_TOUCH_H_
|
||||
#define FALLOUT_TOUCH_H_
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
namespace fallout {
|
||||
|
||||
enum GestureType {
|
||||
kUnrecognized,
|
||||
kTap,
|
||||
kLongPress,
|
||||
kPan,
|
||||
};
|
||||
|
||||
enum GestureState {
|
||||
kPossible,
|
||||
kBegan,
|
||||
kChanged,
|
||||
kEnded,
|
||||
};
|
||||
|
||||
struct Gesture {
|
||||
GestureType type;
|
||||
GestureState state;
|
||||
int numberOfTouches;
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
void touch_handle_start(SDL_TouchFingerEvent* event);
|
||||
void touch_handle_move(SDL_TouchFingerEvent* event);
|
||||
void touch_handle_end(SDL_TouchFingerEvent* event);
|
||||
void touch_process_gesture();
|
||||
bool touch_get_gesture(Gesture* gesture);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* FALLOUT_TOUCH_H_ */
|
||||
+2
-2
@@ -265,12 +265,12 @@ int traitGetStatModifier(int stat)
|
||||
break;
|
||||
case STAT_RADIATION_RESISTANCE:
|
||||
if (traitIsSelected(TRAIT_FAST_METABOLISM)) {
|
||||
modifier -= -critterGetBaseStat(gDude, STAT_RADIATION_RESISTANCE);
|
||||
modifier -= critterGetBaseStat(gDude, STAT_RADIATION_RESISTANCE);
|
||||
}
|
||||
break;
|
||||
case STAT_POISON_RESISTANCE:
|
||||
if (traitIsSelected(TRAIT_FAST_METABOLISM)) {
|
||||
modifier -= -critterGetBaseStat(gDude, STAT_POISON_RESISTANCE);
|
||||
modifier -= critterGetBaseStat(gDude, STAT_POISON_RESISTANCE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
+1
-1
@@ -438,4 +438,4 @@ bool vcrReadEntry(VcrEntry* vcrEntry, File* stream)
|
||||
return false;
|
||||
}
|
||||
|
||||
} // fallout
|
||||
} // namespace fallout
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
#define WIN32_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
|
||||
+148
-152
@@ -25,8 +25,8 @@ namespace fallout {
|
||||
|
||||
#define MAX_WINDOW_COUNT (50)
|
||||
|
||||
// The maximum number of radio groups.
|
||||
#define RADIO_GROUP_LIST_CAPACITY (64)
|
||||
// The maximum number of button groups.
|
||||
#define BUTTON_GROUP_LIST_CAPACITY (64)
|
||||
|
||||
static void windowFree(int win);
|
||||
static void _win_buffering(bool a1);
|
||||
@@ -39,13 +39,13 @@ static int paletteOpenFileImpl(const char* path, int flags);
|
||||
static int paletteReadFileImpl(int fd, void* buf, size_t count);
|
||||
static int paletteCloseFileImpl(int fd);
|
||||
static Button* buttonCreateInternal(int win, int x, int y, int width, int height, int mouseEnterEventCode, int mouseExitEventCode, int mouseDownEventCode, int mouseUpEventCode, int flags, unsigned char* up, unsigned char* dn, unsigned char* hover);
|
||||
static int _GNW_check_buttons(Window* window, int* out_a2);
|
||||
static int _GNW_check_buttons(Window* window, int* keyCodePtr);
|
||||
static bool _button_under_mouse(Button* button, Rect* rect);
|
||||
static void buttonFree(Button* ptr);
|
||||
static int button_new_id();
|
||||
static int _win_group_check_buttons(int a1, int* a2, int a3, void (*a4)(int));
|
||||
static int _win_group_check_buttons(int buttonCount, int* btns, int maxChecked, RadioButtonCallback* func);
|
||||
static int _button_check_group(Button* button);
|
||||
static void _button_draw(Button* button, Window* window, unsigned char* data, int a4, Rect* a5, int a6);
|
||||
static void _button_draw(Button* button, Window* window, unsigned char* data, bool draw, Rect* bound, bool sound);
|
||||
static void _GNW_button_refresh(Window* window, Rect* rect);
|
||||
|
||||
// 0x50FA30
|
||||
@@ -112,7 +112,7 @@ static int _doing_refresh_all;
|
||||
static void* _GNW_texture;
|
||||
|
||||
// 0x6ADF40
|
||||
static RadioGroup gRadioGroups[RADIO_GROUP_LIST_CAPACITY];
|
||||
static ButtonGroup gButtonGroups[BUTTON_GROUP_LIST_CAPACITY];
|
||||
|
||||
// 0x4D5C30
|
||||
int windowManagerInit(VideoSystemInitProc* videoSystemInitProc, VideoSystemExitProc* videoSystemExitProc, int a3)
|
||||
@@ -504,15 +504,12 @@ void windowDrawBorder(int win)
|
||||
}
|
||||
|
||||
// 0x4D684C
|
||||
void windowDrawText(int win, const char* str, int a3, int x, int y, int a6)
|
||||
void windowDrawText(int win, const char* str, int width, int x, int y, int color)
|
||||
{
|
||||
int v7;
|
||||
int v14;
|
||||
unsigned char* buf;
|
||||
int v27;
|
||||
int textColor;
|
||||
|
||||
Window* window = windowGetWindow(win);
|
||||
v7 = a3;
|
||||
|
||||
if (!gWindowSystemInitialized) {
|
||||
return;
|
||||
@@ -522,52 +519,51 @@ void windowDrawText(int win, const char* str, int a3, int x, int y, int a6)
|
||||
return;
|
||||
}
|
||||
|
||||
if (a3 == 0) {
|
||||
if (a6 & 0x040000) {
|
||||
v7 = fontGetMonospacedStringWidth(str);
|
||||
if (width == 0) {
|
||||
if (color & 0x040000) {
|
||||
width = fontGetMonospacedStringWidth(str);
|
||||
} else {
|
||||
v7 = fontGetStringWidth(str);
|
||||
width = fontGetStringWidth(str);
|
||||
}
|
||||
}
|
||||
|
||||
if (v7 + x > window->width) {
|
||||
if (!(a6 & 0x04000000)) {
|
||||
if (width + x > window->width) {
|
||||
if (!(color & 0x04000000)) {
|
||||
return;
|
||||
}
|
||||
|
||||
v7 = window->width - x;
|
||||
width = window->width - x;
|
||||
}
|
||||
|
||||
buf = window->buffer + x + y * window->width;
|
||||
|
||||
v14 = fontGetLineHeight();
|
||||
if (v14 + y > window->height) {
|
||||
if (fontGetLineHeight() + y > window->height) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(a6 & 0x02000000)) {
|
||||
if (!(color & 0x02000000)) {
|
||||
if (window->color == 256 && _GNW_texture != NULL) {
|
||||
_buf_texture(buf, v7, fontGetLineHeight(), window->width, _GNW_texture, window->tx + x, window->ty + y);
|
||||
_buf_texture(buf, width, fontGetLineHeight(), window->width, _GNW_texture, window->tx + x, window->ty + y);
|
||||
} else {
|
||||
bufferFill(buf, v7, fontGetLineHeight(), window->width, window->color);
|
||||
bufferFill(buf, width, fontGetLineHeight(), window->width, window->color);
|
||||
}
|
||||
}
|
||||
|
||||
if (a6 & 0xFF00) {
|
||||
int t = (a6 & 0xFF00) >> 8;
|
||||
v27 = (a6 & ~0xFFFF) | _colorTable[_GNW_wcolor[t]];
|
||||
if (color & 0xFF00) {
|
||||
int t = (color & 0xFF00) >> 8;
|
||||
textColor = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[t]];
|
||||
} else {
|
||||
v27 = a6;
|
||||
textColor = color;
|
||||
}
|
||||
|
||||
fontDrawText(buf, str, v7, window->width, v27);
|
||||
fontDrawText(buf, str, width, window->width, textColor);
|
||||
|
||||
if (a6 & 0x01000000) {
|
||||
if (color & 0x01000000) {
|
||||
// TODO: Check.
|
||||
Rect rect;
|
||||
rect.left = window->rect.left + x;
|
||||
rect.top = window->rect.top + y;
|
||||
rect.right = rect.left + v7;
|
||||
rect.right = rect.left + width;
|
||||
rect.bottom = rect.top + fontGetLineHeight();
|
||||
_GNW_win_refresh(window, &rect, NULL);
|
||||
}
|
||||
@@ -628,7 +624,7 @@ void windowDrawRect(int win, int left, int top, int right, int bottom, int color
|
||||
}
|
||||
|
||||
// 0x4D6CC8
|
||||
void windowFill(int win, int x, int y, int width, int height, int a6)
|
||||
void windowFill(int win, int x, int y, int width, int height, int color)
|
||||
{
|
||||
Window* window = windowGetWindow(win);
|
||||
|
||||
@@ -640,19 +636,19 @@ void windowFill(int win, int x, int y, int width, int height, int a6)
|
||||
return;
|
||||
}
|
||||
|
||||
if (a6 == 256) {
|
||||
if (color == 256) {
|
||||
if (_GNW_texture != NULL) {
|
||||
_buf_texture(window->buffer + window->width * y + x, width, height, window->width, _GNW_texture, x + window->tx, y + window->ty);
|
||||
} else {
|
||||
a6 = _colorTable[_GNW_wcolor[0]] & 0xFF;
|
||||
color = _colorTable[_GNW_wcolor[0]] & 0xFF;
|
||||
}
|
||||
} else if ((a6 & 0xFF00) != 0) {
|
||||
int v1 = (a6 & 0xFF00) >> 8;
|
||||
a6 = (a6 & ~0xFFFF) | _colorTable[_GNW_wcolor[v1]];
|
||||
} else if ((color & 0xFF00) != 0) {
|
||||
int v1 = (color & 0xFF00) >> 8;
|
||||
color = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[v1]];
|
||||
}
|
||||
|
||||
if (a6 < 256) {
|
||||
bufferFill(window->buffer + window->width * y + x, width, height, window->width, a6);
|
||||
if (color < 256) {
|
||||
bufferFill(window->buffer + window->width * y + x, width, height, window->width, color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1367,12 +1363,12 @@ int buttonCreate(int win, int x, int y, int width, int height, int mouseEnterEve
|
||||
return -1;
|
||||
}
|
||||
|
||||
Button* button = buttonCreateInternal(win, x, y, width, height, mouseEnterEventCode, mouseExitEventCode, mouseDownEventCode, mouseUpEventCode, flags | BUTTON_FLAG_0x010000, up, dn, hover);
|
||||
Button* button = buttonCreateInternal(win, x, y, width, height, mouseEnterEventCode, mouseExitEventCode, mouseDownEventCode, mouseUpEventCode, flags | BUTTON_FLAG_GRAPHIC, up, dn, hover);
|
||||
if (button == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
_button_draw(button, window, button->normalImage, 0, NULL, 0);
|
||||
_button_draw(button, window, button->normalImage, false, NULL, false);
|
||||
|
||||
return button->id;
|
||||
}
|
||||
@@ -1469,7 +1465,7 @@ int _win_register_text_button(int win, int x, int y, int mouseEnterEventCode, in
|
||||
return -1;
|
||||
}
|
||||
|
||||
_button_draw(button, window, button->normalImage, 0, NULL, 0);
|
||||
_button_draw(button, window, button->normalImage, false, NULL, false);
|
||||
|
||||
return button->id;
|
||||
}
|
||||
@@ -1494,7 +1490,7 @@ int _win_register_button_disable(int btn, unsigned char* up, unsigned char* down
|
||||
}
|
||||
|
||||
// 0x4D86A8
|
||||
int _win_register_button_image(int btn, unsigned char* up, unsigned char* down, unsigned char* hover, int a5)
|
||||
int _win_register_button_image(int btn, unsigned char* up, unsigned char* down, unsigned char* hover, bool draw)
|
||||
{
|
||||
if (!gWindowSystemInitialized) {
|
||||
return -1;
|
||||
@@ -1510,7 +1506,7 @@ int _win_register_button_image(int btn, unsigned char* up, unsigned char* down,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(button->flags & BUTTON_FLAG_0x010000)) {
|
||||
if (!(button->flags & BUTTON_FLAG_GRAPHIC)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1527,7 +1523,7 @@ int _win_register_button_image(int btn, unsigned char* up, unsigned char* down,
|
||||
button->pressedImage = down;
|
||||
button->hoverImage = hover;
|
||||
|
||||
_button_draw(button, window, button->currentImage, a5, NULL, 0);
|
||||
_button_draw(button, window, button->currentImage, draw, NULL, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1590,7 +1586,7 @@ int buttonSetRightMouseCallbacks(int btn, int rightMouseDownEventCode, int right
|
||||
// These callbacks can be triggered several times during tracking if mouse leaves button's rectangle without releasing mouse buttons.
|
||||
//
|
||||
// 0x4D87F8
|
||||
int buttonSetCallbacks(int btn, ButtonCallback* onPressed, ButtonCallback* onUnpressed)
|
||||
int buttonSetCallbacks(int btn, ButtonCallback* pressSoundFunc, ButtonCallback* releaseSoundFunc)
|
||||
{
|
||||
if (!gWindowSystemInitialized) {
|
||||
return -1;
|
||||
@@ -1601,8 +1597,8 @@ int buttonSetCallbacks(int btn, ButtonCallback* onPressed, ButtonCallback* onUnp
|
||||
return -1;
|
||||
}
|
||||
|
||||
button->onPressed = onPressed;
|
||||
button->onUnpressed = onUnpressed;
|
||||
button->pressSoundFunc = pressSoundFunc;
|
||||
button->releaseSoundFunc = releaseSoundFunc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1676,9 +1672,9 @@ Button* buttonCreateInternal(int win, int x, int y, int width, int height, int m
|
||||
button->leftMouseUpProc = NULL;
|
||||
button->rightMouseDownProc = NULL;
|
||||
button->rightMouseUpProc = NULL;
|
||||
button->onPressed = NULL;
|
||||
button->onUnpressed = NULL;
|
||||
button->radioGroup = NULL;
|
||||
button->pressSoundFunc = NULL;
|
||||
button->releaseSoundFunc = NULL;
|
||||
button->buttonGroup = NULL;
|
||||
button->prev = NULL;
|
||||
|
||||
button->next = window->buttonListHead;
|
||||
@@ -1702,7 +1698,7 @@ bool _win_button_down(int btn)
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((button->flags & BUTTON_FLAG_0x01) != 0 && (button->flags & BUTTON_FLAG_0x020000) != 0) {
|
||||
if ((button->flags & BUTTON_FLAG_0x01) != 0 && (button->flags & BUTTON_FLAG_CHECKED) != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1751,10 +1747,10 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
|
||||
*keyCodePtr = prevHoveredButton->mouseExitEventCode;
|
||||
}
|
||||
|
||||
if ((prevHoveredButton->flags & BUTTON_FLAG_0x01) && (prevHoveredButton->flags & BUTTON_FLAG_0x020000)) {
|
||||
_button_draw(prevHoveredButton, window, prevHoveredButton->pressedImage, 1, NULL, 1);
|
||||
if ((prevHoveredButton->flags & BUTTON_FLAG_0x01) && (prevHoveredButton->flags & BUTTON_FLAG_CHECKED)) {
|
||||
_button_draw(prevHoveredButton, window, prevHoveredButton->pressedImage, true, NULL, true);
|
||||
} else {
|
||||
_button_draw(prevHoveredButton, window, prevHoveredButton->normalImage, 1, NULL, 1);
|
||||
_button_draw(prevHoveredButton, window, prevHoveredButton->normalImage, true, NULL, true);
|
||||
}
|
||||
|
||||
window->hoveredButton = NULL;
|
||||
@@ -1778,10 +1774,10 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
|
||||
*keyCodePtr = prevClickedButton->mouseEnterEventCode;
|
||||
}
|
||||
|
||||
if ((prevClickedButton->flags & BUTTON_FLAG_0x01) && (prevClickedButton->flags & BUTTON_FLAG_0x020000)) {
|
||||
_button_draw(prevClickedButton, window, prevClickedButton->pressedImage, 1, NULL, 1);
|
||||
if ((prevClickedButton->flags & BUTTON_FLAG_0x01) && (prevClickedButton->flags & BUTTON_FLAG_CHECKED)) {
|
||||
_button_draw(prevClickedButton, window, prevClickedButton->pressedImage, true, NULL, true);
|
||||
} else {
|
||||
_button_draw(prevClickedButton, window, prevClickedButton->normalImage, 1, NULL, 1);
|
||||
_button_draw(prevClickedButton, window, prevClickedButton->normalImage, true, NULL, true);
|
||||
}
|
||||
|
||||
window->hoveredButton = prevClickedButton;
|
||||
@@ -1812,10 +1808,10 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
|
||||
*keyCodePtr = v28->mouseExitEventCode;
|
||||
}
|
||||
|
||||
if ((v28->flags & BUTTON_FLAG_0x01) && (v28->flags & BUTTON_FLAG_0x020000)) {
|
||||
_button_draw(v28, v26, v28->pressedImage, 1, NULL, 1);
|
||||
if ((v28->flags & BUTTON_FLAG_0x01) && (v28->flags & BUTTON_FLAG_CHECKED)) {
|
||||
_button_draw(v28, v26, v28->pressedImage, true, NULL, true);
|
||||
} else {
|
||||
_button_draw(v28, v26, v28->normalImage, 1, NULL, 1);
|
||||
_button_draw(v28, v26, v28->normalImage, true, NULL, true);
|
||||
}
|
||||
|
||||
v26->clickedButton = NULL;
|
||||
@@ -1857,10 +1853,10 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
|
||||
|
||||
if ((button->flags & BUTTON_FLAG_0x01) != 0) {
|
||||
if ((button->flags & BUTTON_FLAG_0x02) != 0) {
|
||||
if ((button->flags & BUTTON_FLAG_0x020000) != 0) {
|
||||
if ((button->flags & BUTTON_FLAG_CHECKED) != 0) {
|
||||
if (!(button->flags & BUTTON_FLAG_0x04)) {
|
||||
if (button->radioGroup != NULL) {
|
||||
button->radioGroup->field_4--;
|
||||
if (button->buttonGroup != NULL) {
|
||||
button->buttonGroup->currChecked--;
|
||||
}
|
||||
|
||||
if ((mouseEvent & MOUSE_EVENT_LEFT_BUTTON_DOWN) != 0) {
|
||||
@@ -1871,7 +1867,7 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
|
||||
cb = button->rightMouseUpProc;
|
||||
}
|
||||
|
||||
button->flags &= ~BUTTON_FLAG_0x020000;
|
||||
button->flags &= ~BUTTON_FLAG_CHECKED;
|
||||
}
|
||||
} else {
|
||||
if (_button_check_group(button) == -1) {
|
||||
@@ -1887,7 +1883,7 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
|
||||
cb = button->rightMouseDownProc;
|
||||
}
|
||||
|
||||
button->flags |= BUTTON_FLAG_0x020000;
|
||||
button->flags |= BUTTON_FLAG_CHECKED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1905,7 +1901,7 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
|
||||
}
|
||||
}
|
||||
|
||||
_button_draw(button, window, button->pressedImage, 1, NULL, 1);
|
||||
_button_draw(button, window, button->pressedImage, true, NULL, true);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1916,10 +1912,10 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
|
||||
|
||||
if (v49->flags & BUTTON_FLAG_0x01) {
|
||||
if (!(v49->flags & BUTTON_FLAG_0x02)) {
|
||||
if (v49->flags & BUTTON_FLAG_0x020000) {
|
||||
if (v49->flags & BUTTON_FLAG_CHECKED) {
|
||||
if (!(v49->flags & BUTTON_FLAG_0x04)) {
|
||||
if (v49->radioGroup != NULL) {
|
||||
v49->radioGroup->field_4--;
|
||||
if (v49->buttonGroup != NULL) {
|
||||
v49->buttonGroup->currChecked--;
|
||||
}
|
||||
|
||||
if ((mouseEvent & MOUSE_EVENT_LEFT_BUTTON_UP) != 0) {
|
||||
@@ -1930,12 +1926,12 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
|
||||
cb = button->rightMouseUpProc;
|
||||
}
|
||||
|
||||
button->flags &= ~BUTTON_FLAG_0x020000;
|
||||
button->flags &= ~BUTTON_FLAG_CHECKED;
|
||||
}
|
||||
} else {
|
||||
if (_button_check_group(v49) == -1) {
|
||||
button = NULL;
|
||||
_button_draw(v49, window, v49->normalImage, 1, NULL, 1);
|
||||
_button_draw(v49, window, v49->normalImage, true, NULL, true);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1947,13 +1943,13 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
|
||||
cb = v49->rightMouseDownProc;
|
||||
}
|
||||
|
||||
v49->flags |= BUTTON_FLAG_0x020000;
|
||||
v49->flags |= BUTTON_FLAG_CHECKED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (v49->flags & BUTTON_FLAG_0x020000) {
|
||||
if (v49->radioGroup != NULL) {
|
||||
v49->radioGroup->field_4--;
|
||||
if (v49->flags & BUTTON_FLAG_CHECKED) {
|
||||
if (v49->buttonGroup != NULL) {
|
||||
v49->buttonGroup->currChecked--;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1967,9 +1963,9 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
|
||||
}
|
||||
|
||||
if (button->hoverImage != NULL) {
|
||||
_button_draw(button, window, button->hoverImage, 1, NULL, 1);
|
||||
_button_draw(button, window, button->hoverImage, true, NULL, true);
|
||||
} else {
|
||||
_button_draw(button, window, button->normalImage, 1, NULL, 1);
|
||||
_button_draw(button, window, button->normalImage, true, NULL, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1982,7 +1978,7 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
|
||||
cb = button->mouseEnterProc;
|
||||
}
|
||||
|
||||
_button_draw(button, window, button->hoverImage, 1, NULL, 1);
|
||||
_button_draw(button, window, button->hoverImage, true, NULL, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1995,7 +1991,7 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
|
||||
&& (mouseEvent & MOUSE_EVENT_ANY_BUTTON_DOWN) != 0
|
||||
&& (mouseEvent & MOUSE_EVENT_ANY_BUTTON_REPEAT) == 0) {
|
||||
_win_drag(window->id);
|
||||
_button_draw(button, window, button->normalImage, 1, NULL, 1);
|
||||
_button_draw(button, window, button->normalImage, true, NULL, true);
|
||||
}
|
||||
} else if ((window->flags & WINDOW_FLAG_0x80) != 0) {
|
||||
v25 |= mouseEvent << 8;
|
||||
@@ -2023,13 +2019,13 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr)
|
||||
*keyCodePtr = prevHoveredButton->mouseExitEventCode;
|
||||
|
||||
unsigned char* data;
|
||||
if ((prevHoveredButton->flags & BUTTON_FLAG_0x01) && (prevHoveredButton->flags & BUTTON_FLAG_0x020000)) {
|
||||
if ((prevHoveredButton->flags & BUTTON_FLAG_0x01) && (prevHoveredButton->flags & BUTTON_FLAG_CHECKED)) {
|
||||
data = prevHoveredButton->pressedImage;
|
||||
} else {
|
||||
data = prevHoveredButton->normalImage;
|
||||
}
|
||||
|
||||
_button_draw(prevHoveredButton, window, data, 1, NULL, 1);
|
||||
_button_draw(prevHoveredButton, window, data, true, NULL, true);
|
||||
|
||||
window->hoveredButton = NULL;
|
||||
}
|
||||
@@ -2142,7 +2138,7 @@ int buttonDestroy(int btn)
|
||||
// 0x4D9374
|
||||
void buttonFree(Button* button)
|
||||
{
|
||||
if ((button->flags & BUTTON_FLAG_0x010000) == 0) {
|
||||
if ((button->flags & BUTTON_FLAG_GRAPHIC) == 0) {
|
||||
if (button->normalImage != NULL) {
|
||||
internal_free(button->normalImage);
|
||||
}
|
||||
@@ -2168,15 +2164,15 @@ void buttonFree(Button* button)
|
||||
}
|
||||
}
|
||||
|
||||
RadioGroup* radioGroup = button->radioGroup;
|
||||
if (radioGroup != NULL) {
|
||||
for (int index = 0; index < radioGroup->buttonsLength; index++) {
|
||||
if (button == radioGroup->buttons[index]) {
|
||||
for (; index < radioGroup->buttonsLength - 1; index++) {
|
||||
radioGroup->buttons[index] = radioGroup->buttons[index + 1];
|
||||
ButtonGroup* buttonGroup = button->buttonGroup;
|
||||
if (buttonGroup != NULL) {
|
||||
for (int index = 0; index < buttonGroup->buttonsLength; index++) {
|
||||
if (button == buttonGroup->buttons[index]) {
|
||||
for (; index < buttonGroup->buttonsLength - 1; index++) {
|
||||
buttonGroup->buttons[index] = buttonGroup->buttons[index + 1];
|
||||
}
|
||||
|
||||
radioGroup->buttonsLength--;
|
||||
buttonGroup->buttonsLength--;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -2216,7 +2212,7 @@ int buttonEnable(int btn)
|
||||
|
||||
if ((button->flags & BUTTON_FLAG_DISABLED) != 0) {
|
||||
button->flags &= ~BUTTON_FLAG_DISABLED;
|
||||
_button_draw(button, window, button->currentImage, 1, NULL, 0);
|
||||
_button_draw(button, window, button->currentImage, true, NULL, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -2238,7 +2234,7 @@ int buttonDisable(int btn)
|
||||
if ((button->flags & BUTTON_FLAG_DISABLED) == 0) {
|
||||
button->flags |= BUTTON_FLAG_DISABLED;
|
||||
|
||||
_button_draw(button, window, button->currentImage, 1, NULL, 0);
|
||||
_button_draw(button, window, button->currentImage, true, NULL, false);
|
||||
|
||||
if (button == window->hoveredButton) {
|
||||
if (window->hoveredButton->mouseExitEventCode != -1) {
|
||||
@@ -2252,7 +2248,7 @@ int buttonDisable(int btn)
|
||||
}
|
||||
|
||||
// 0x4D9554
|
||||
int _win_set_button_rest_state(int btn, bool a2, int a3)
|
||||
int _win_set_button_rest_state(int btn, bool checked, int flags)
|
||||
{
|
||||
if (!gWindowSystemInitialized) {
|
||||
return -1;
|
||||
@@ -2267,30 +2263,30 @@ int _win_set_button_rest_state(int btn, bool a2, int a3)
|
||||
if ((button->flags & BUTTON_FLAG_0x01) != 0) {
|
||||
int keyCode = -1;
|
||||
|
||||
if ((button->flags & BUTTON_FLAG_0x020000) != 0) {
|
||||
if (!a2) {
|
||||
button->flags &= ~BUTTON_FLAG_0x020000;
|
||||
if ((button->flags & BUTTON_FLAG_CHECKED) != 0) {
|
||||
if (!checked) {
|
||||
button->flags &= ~BUTTON_FLAG_CHECKED;
|
||||
|
||||
if ((a3 & 0x02) == 0) {
|
||||
_button_draw(button, window, button->normalImage, 1, NULL, 0);
|
||||
if ((flags & 0x02) == 0) {
|
||||
_button_draw(button, window, button->normalImage, true, NULL, false);
|
||||
}
|
||||
|
||||
if (button->radioGroup != NULL) {
|
||||
button->radioGroup->field_4--;
|
||||
if (button->buttonGroup != NULL) {
|
||||
button->buttonGroup->currChecked--;
|
||||
}
|
||||
|
||||
keyCode = button->leftMouseUpEventCode;
|
||||
}
|
||||
} else {
|
||||
if (a2) {
|
||||
button->flags |= BUTTON_FLAG_0x020000;
|
||||
if (checked) {
|
||||
button->flags |= BUTTON_FLAG_CHECKED;
|
||||
|
||||
if ((a3 & 0x02) == 0) {
|
||||
_button_draw(button, window, button->pressedImage, 1, NULL, 0);
|
||||
if ((flags & 0x02) == 0) {
|
||||
_button_draw(button, window, button->pressedImage, true, NULL, false);
|
||||
}
|
||||
|
||||
if (button->radioGroup != NULL) {
|
||||
button->radioGroup->field_4++;
|
||||
if (button->buttonGroup != NULL) {
|
||||
button->buttonGroup->currChecked++;
|
||||
}
|
||||
|
||||
keyCode = button->lefMouseDownEventCode;
|
||||
@@ -2298,7 +2294,7 @@ int _win_set_button_rest_state(int btn, bool a2, int a3)
|
||||
}
|
||||
|
||||
if (keyCode != -1) {
|
||||
if ((a3 & 0x01) != 0) {
|
||||
if ((flags & 0x01) != 0) {
|
||||
enqueueInputEvent(keyCode);
|
||||
}
|
||||
}
|
||||
@@ -2308,20 +2304,20 @@ int _win_set_button_rest_state(int btn, bool a2, int a3)
|
||||
}
|
||||
|
||||
// 0x4D962C
|
||||
int _win_group_check_buttons(int buttonCount, int* btns, int a3, void (*a4)(int))
|
||||
int _win_group_check_buttons(int buttonCount, int* btns, int maxChecked, RadioButtonCallback* func)
|
||||
{
|
||||
if (!gWindowSystemInitialized) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buttonCount >= RADIO_GROUP_BUTTON_LIST_CAPACITY) {
|
||||
if (buttonCount >= BUTTON_GROUP_BUTTON_LIST_CAPACITY) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int groupIndex = 0; groupIndex < RADIO_GROUP_LIST_CAPACITY; groupIndex++) {
|
||||
RadioGroup* radioGroup = &(gRadioGroups[groupIndex]);
|
||||
if (radioGroup->buttonsLength == 0) {
|
||||
radioGroup->field_4 = 0;
|
||||
for (int groupIndex = 0; groupIndex < BUTTON_GROUP_LIST_CAPACITY; groupIndex++) {
|
||||
ButtonGroup* buttonGroup = &(gButtonGroups[groupIndex]);
|
||||
if (buttonGroup->buttonsLength == 0) {
|
||||
buttonGroup->currChecked = 0;
|
||||
|
||||
for (int buttonIndex = 0; buttonIndex < buttonCount; buttonIndex++) {
|
||||
Button* button = buttonGetButton(btns[buttonIndex], NULL);
|
||||
@@ -2329,18 +2325,18 @@ int _win_group_check_buttons(int buttonCount, int* btns, int a3, void (*a4)(int)
|
||||
return -1;
|
||||
}
|
||||
|
||||
radioGroup->buttons[buttonIndex] = button;
|
||||
buttonGroup->buttons[buttonIndex] = button;
|
||||
|
||||
button->radioGroup = radioGroup;
|
||||
button->buttonGroup = buttonGroup;
|
||||
|
||||
if ((button->flags & BUTTON_FLAG_0x020000) != 0) {
|
||||
radioGroup->field_4++;
|
||||
if ((button->flags & BUTTON_FLAG_CHECKED) != 0) {
|
||||
buttonGroup->currChecked++;
|
||||
}
|
||||
}
|
||||
|
||||
radioGroup->buttonsLength = buttonCount;
|
||||
radioGroup->field_0 = a3;
|
||||
radioGroup->field_8 = a4;
|
||||
buttonGroup->buttonsLength = buttonCount;
|
||||
buttonGroup->maxChecked = maxChecked;
|
||||
buttonGroup->func = func;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -2360,11 +2356,11 @@ int _win_group_radio_buttons(int count, int* btns)
|
||||
}
|
||||
|
||||
Button* button = buttonGetButton(btns[0], NULL);
|
||||
RadioGroup* radioGroup = button->radioGroup;
|
||||
ButtonGroup* buttonGroup = button->buttonGroup;
|
||||
|
||||
for (int index = 0; index < radioGroup->buttonsLength; index++) {
|
||||
Button* v1 = radioGroup->buttons[index];
|
||||
v1->flags |= BUTTON_FLAG_0x040000;
|
||||
for (int index = 0; index < buttonGroup->buttonsLength; index++) {
|
||||
Button* v1 = buttonGroup->buttons[index];
|
||||
v1->flags |= BUTTON_FLAG_RADIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -2373,20 +2369,20 @@ int _win_group_radio_buttons(int count, int* btns)
|
||||
// 0x4D9744
|
||||
int _button_check_group(Button* button)
|
||||
{
|
||||
if (button->radioGroup == NULL) {
|
||||
if (button->buttonGroup == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((button->flags & BUTTON_FLAG_0x040000) != 0) {
|
||||
if (button->radioGroup->field_4 > 0) {
|
||||
for (int index = 0; index < button->radioGroup->buttonsLength; index++) {
|
||||
Button* v1 = button->radioGroup->buttons[index];
|
||||
if ((v1->flags & BUTTON_FLAG_0x020000) != 0) {
|
||||
v1->flags &= ~BUTTON_FLAG_0x020000;
|
||||
if ((button->flags & BUTTON_FLAG_RADIO) != 0) {
|
||||
if (button->buttonGroup->currChecked > 0) {
|
||||
for (int index = 0; index < button->buttonGroup->buttonsLength; index++) {
|
||||
Button* v1 = button->buttonGroup->buttons[index];
|
||||
if ((v1->flags & BUTTON_FLAG_CHECKED) != 0) {
|
||||
v1->flags &= ~BUTTON_FLAG_CHECKED;
|
||||
|
||||
Window* window;
|
||||
buttonGetButton(v1->id, &window);
|
||||
_button_draw(v1, window, v1->normalImage, 1, NULL, 1);
|
||||
_button_draw(v1, window, v1->normalImage, true, NULL, true);
|
||||
|
||||
if (v1->leftMouseUpProc != NULL) {
|
||||
v1->leftMouseUpProc(v1->id, v1->leftMouseUpEventCode);
|
||||
@@ -2395,30 +2391,30 @@ int _button_check_group(Button* button)
|
||||
}
|
||||
}
|
||||
|
||||
if ((button->flags & BUTTON_FLAG_0x020000) == 0) {
|
||||
button->radioGroup->field_4++;
|
||||
if ((button->flags & BUTTON_FLAG_CHECKED) == 0) {
|
||||
button->buttonGroup->currChecked++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (button->radioGroup->field_4 < button->radioGroup->field_0) {
|
||||
if ((button->flags & BUTTON_FLAG_0x020000) == 0) {
|
||||
button->radioGroup->field_4++;
|
||||
if (button->buttonGroup->currChecked < button->buttonGroup->maxChecked) {
|
||||
if ((button->flags & BUTTON_FLAG_CHECKED) == 0) {
|
||||
button->buttonGroup->currChecked++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (button->radioGroup->field_8 != NULL) {
|
||||
button->radioGroup->field_8(button->id);
|
||||
if (button->buttonGroup->func != NULL) {
|
||||
button->buttonGroup->func(button->id);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 0x4D9808
|
||||
void _button_draw(Button* button, Window* window, unsigned char* data, int a4, Rect* a5, int a6)
|
||||
void _button_draw(Button* button, Window* window, unsigned char* data, bool draw, Rect* bound, bool sound)
|
||||
{
|
||||
unsigned char* previousImage = NULL;
|
||||
if (data != NULL) {
|
||||
@@ -2427,8 +2423,8 @@ void _button_draw(Button* button, Window* window, unsigned char* data, int a4, R
|
||||
rectOffset(&v2, window->rect.left, window->rect.top);
|
||||
|
||||
Rect v3;
|
||||
if (a5 != NULL) {
|
||||
if (rectIntersection(&v2, a5, &v2) == -1) {
|
||||
if (bound != NULL) {
|
||||
if (rectIntersection(&v2, bound, &v2) == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2438,7 +2434,7 @@ void _button_draw(Button* button, Window* window, unsigned char* data, int a4, R
|
||||
rectCopy(&v3, &(button->rect));
|
||||
}
|
||||
|
||||
if (data == button->normalImage && (button->flags & BUTTON_FLAG_0x020000)) {
|
||||
if (data == button->normalImage && (button->flags & BUTTON_FLAG_CHECKED)) {
|
||||
data = button->pressedImage;
|
||||
}
|
||||
|
||||
@@ -2461,7 +2457,7 @@ void _button_draw(Button* button, Window* window, unsigned char* data, int a4, R
|
||||
}
|
||||
|
||||
if (data) {
|
||||
if (a4 == 0) {
|
||||
if (!draw) {
|
||||
int width = button->rect.right - button->rect.left + 1;
|
||||
if ((button->flags & BUTTON_FLAG_TRANSPARENT) != 0) {
|
||||
blitBufferToBufferTrans(
|
||||
@@ -2485,18 +2481,18 @@ void _button_draw(Button* button, Window* window, unsigned char* data, int a4, R
|
||||
previousImage = button->currentImage;
|
||||
button->currentImage = data;
|
||||
|
||||
if (a4 != 0) {
|
||||
if (draw) {
|
||||
_GNW_win_refresh(window, &v2, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (a6) {
|
||||
if (sound) {
|
||||
if (previousImage != data) {
|
||||
if (data == button->pressedImage && button->onPressed != NULL) {
|
||||
button->onPressed(button->id, button->lefMouseDownEventCode);
|
||||
} else if (data == button->normalImage && button->onUnpressed != NULL) {
|
||||
button->onUnpressed(button->id, button->leftMouseUpEventCode);
|
||||
if (data == button->pressedImage && button->pressSoundFunc != NULL) {
|
||||
button->pressSoundFunc(button->id, button->lefMouseDownEventCode);
|
||||
} else if (data == button->normalImage && button->releaseSoundFunc != NULL) {
|
||||
button->releaseSoundFunc(button->id, button->leftMouseUpEventCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2513,7 +2509,7 @@ void _GNW_button_refresh(Window* window, Rect* rect)
|
||||
}
|
||||
|
||||
while (button != NULL) {
|
||||
_button_draw(button, window, button->currentImage, 0, rect, 0);
|
||||
_button_draw(button, window, button->currentImage, false, rect, false);
|
||||
button = button->prev;
|
||||
}
|
||||
}
|
||||
@@ -2531,7 +2527,7 @@ int _win_button_press_and_release(int btn)
|
||||
return -1;
|
||||
}
|
||||
|
||||
_button_draw(button, window, button->pressedImage, 1, NULL, 1);
|
||||
_button_draw(button, window, button->pressedImage, true, NULL, true);
|
||||
|
||||
if (button->leftMouseDownProc != NULL) {
|
||||
button->leftMouseDownProc(btn, button->lefMouseDownEventCode);
|
||||
@@ -2545,7 +2541,7 @@ int _win_button_press_and_release(int btn)
|
||||
}
|
||||
}
|
||||
|
||||
_button_draw(button, window, button->normalImage, 1, NULL, 1);
|
||||
_button_draw(button, window, button->normalImage, true, NULL, true);
|
||||
|
||||
if (button->leftMouseUpProc != NULL) {
|
||||
button->leftMouseUpProc(btn, button->leftMouseUpEventCode);
|
||||
|
||||
+23
-22
@@ -8,7 +8,7 @@
|
||||
namespace fallout {
|
||||
|
||||
// The maximum number of buttons in one radio group.
|
||||
#define RADIO_GROUP_BUTTON_LIST_CAPACITY (64)
|
||||
#define BUTTON_GROUP_BUTTON_LIST_CAPACITY (64)
|
||||
|
||||
typedef enum WindowManagerErr {
|
||||
WINDOW_MANAGER_OK = 0,
|
||||
@@ -55,9 +55,9 @@ typedef enum ButtonFlags {
|
||||
BUTTON_FLAG_0x10 = 0x10,
|
||||
BUTTON_FLAG_TRANSPARENT = 0x20,
|
||||
BUTTON_FLAG_0x40 = 0x40,
|
||||
BUTTON_FLAG_0x010000 = 0x010000,
|
||||
BUTTON_FLAG_0x020000 = 0x020000,
|
||||
BUTTON_FLAG_0x040000 = 0x040000,
|
||||
BUTTON_FLAG_GRAPHIC = 0x010000,
|
||||
BUTTON_FLAG_CHECKED = 0x020000,
|
||||
BUTTON_FLAG_RADIO = 0x040000,
|
||||
BUTTON_FLAG_RIGHT_MOUSE_BUTTON_CONFIGURED = 0x080000,
|
||||
} ButtonFlags;
|
||||
|
||||
@@ -66,8 +66,8 @@ typedef struct MenuPulldown {
|
||||
int keyCode;
|
||||
int itemsLength;
|
||||
char** items;
|
||||
int field_1C;
|
||||
int field_20;
|
||||
int foregroundColor;
|
||||
int backgroundColor;
|
||||
} MenuPulldown;
|
||||
|
||||
typedef struct MenuBar {
|
||||
@@ -75,14 +75,14 @@ typedef struct MenuBar {
|
||||
Rect rect;
|
||||
int pulldownsLength;
|
||||
MenuPulldown pulldowns[15];
|
||||
int borderColor;
|
||||
int foregroundColor;
|
||||
int backgroundColor;
|
||||
} MenuBar;
|
||||
|
||||
typedef void WindowBlitProc(unsigned char* src, int width, int height, int srcPitch, unsigned char* dest, int destPitch);
|
||||
|
||||
typedef struct Button Button;
|
||||
typedef struct RadioGroup RadioGroup;
|
||||
typedef struct ButtonGroup ButtonGroup;
|
||||
|
||||
typedef struct Window {
|
||||
int id;
|
||||
@@ -102,6 +102,7 @@ typedef struct Window {
|
||||
} Window;
|
||||
|
||||
typedef void ButtonCallback(int btn, int keyCode);
|
||||
typedef void RadioButtonCallback(int btn);
|
||||
|
||||
typedef struct Button {
|
||||
int id;
|
||||
@@ -127,20 +128,20 @@ typedef struct Button {
|
||||
ButtonCallback* leftMouseUpProc;
|
||||
ButtonCallback* rightMouseDownProc;
|
||||
ButtonCallback* rightMouseUpProc;
|
||||
ButtonCallback* onPressed;
|
||||
ButtonCallback* onUnpressed;
|
||||
RadioGroup* radioGroup;
|
||||
ButtonCallback* pressSoundFunc;
|
||||
ButtonCallback* releaseSoundFunc;
|
||||
ButtonGroup* buttonGroup;
|
||||
Button* prev;
|
||||
Button* next;
|
||||
} Button;
|
||||
|
||||
typedef struct RadioGroup {
|
||||
int field_0;
|
||||
int field_4;
|
||||
void (*field_8)(int);
|
||||
typedef struct ButtonGroup {
|
||||
int maxChecked;
|
||||
int currChecked;
|
||||
RadioButtonCallback* func;
|
||||
int buttonsLength;
|
||||
Button* buttons[RADIO_GROUP_BUTTON_LIST_CAPACITY];
|
||||
} RadioGroup;
|
||||
Button* buttons[BUTTON_GROUP_BUTTON_LIST_CAPACITY];
|
||||
} ButtonGroup;
|
||||
|
||||
typedef int(VideoSystemInitProc)();
|
||||
typedef void(VideoSystemExitProc)();
|
||||
@@ -157,7 +158,7 @@ void windowDrawText(int win, const char* str, int a3, int x, int y, int a6);
|
||||
void _win_text(int win, char** fileNameList, int fileNameListLength, int maxWidth, int x, int y, int flags);
|
||||
void windowDrawLine(int win, int left, int top, int right, int bottom, int color);
|
||||
void windowDrawRect(int win, int left, int top, int right, int bottom, int color);
|
||||
void windowFill(int win, int x, int y, int width, int height, int a6);
|
||||
void windowFill(int win, int x, int y, int width, int height, int color);
|
||||
void windowShow(int win);
|
||||
void windowHide(int win);
|
||||
void windowRefresh(int win);
|
||||
@@ -178,10 +179,10 @@ bool showMesageBox(const char* str);
|
||||
int buttonCreate(int win, int x, int y, int width, int height, int mouseEnterEventCode, int mouseExitEventCode, int mouseDownEventCode, int mouseUpEventCode, unsigned char* up, unsigned char* dn, unsigned char* hover, int flags);
|
||||
int _win_register_text_button(int win, int x, int y, int mouseEnterEventCode, int mouseExitEventCode, int mouseDownEventCode, int mouseUpEventCode, const char* title, int flags);
|
||||
int _win_register_button_disable(int btn, unsigned char* up, unsigned char* down, unsigned char* hover);
|
||||
int _win_register_button_image(int btn, unsigned char* up, unsigned char* down, unsigned char* hover, int a5);
|
||||
int _win_register_button_image(int btn, unsigned char* up, unsigned char* down, unsigned char* hover, bool draw);
|
||||
int buttonSetMouseCallbacks(int btn, ButtonCallback* mouseEnterProc, ButtonCallback* mouseExitProc, ButtonCallback* mouseDownProc, ButtonCallback* mouseUpProc);
|
||||
int buttonSetRightMouseCallbacks(int btn, int rightMouseDownEventCode, int rightMouseUpEventCode, ButtonCallback* rightMouseDownProc, ButtonCallback* rightMouseUpProc);
|
||||
int buttonSetCallbacks(int btn, ButtonCallback* onPressed, ButtonCallback* onUnpressed);
|
||||
int buttonSetCallbacks(int btn, ButtonCallback* pressSoundFunc, ButtonCallback* releaseSoundFunc);
|
||||
int buttonSetMask(int btn, unsigned char* mask);
|
||||
bool _win_button_down(int btn);
|
||||
int buttonGetWindowId(int btn);
|
||||
@@ -189,8 +190,8 @@ int _win_last_button_winID();
|
||||
int buttonDestroy(int btn);
|
||||
int buttonEnable(int btn);
|
||||
int buttonDisable(int btn);
|
||||
int _win_set_button_rest_state(int btn, bool a2, int a3);
|
||||
int _win_group_radio_buttons(int a1, int* a2);
|
||||
int _win_set_button_rest_state(int btn, bool checked, int flags);
|
||||
int _win_group_radio_buttons(int buttonCount, int* btns);
|
||||
int _win_button_press_and_release(int btn);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
@@ -79,13 +79,13 @@ static int _currx;
|
||||
char gProgramWindowTitle[256];
|
||||
|
||||
// 0x4DA6C0
|
||||
int _win_list_select(const char* title, char** fileList, int fileListLength, ListSelectionHandler* callback, int x, int y, int a7)
|
||||
int _win_list_select(const char* title, char** fileList, int fileListLength, ListSelectionHandler* callback, int x, int y, int color)
|
||||
{
|
||||
return _win_list_select_at(title, fileList, fileListLength, callback, x, y, a7, 0);
|
||||
return _win_list_select_at(title, fileList, fileListLength, callback, x, y, color, 0);
|
||||
}
|
||||
|
||||
// 0x4DA70C
|
||||
int _win_list_select_at(const char* title, char** items, int itemsLength, ListSelectionHandler* callback, int x, int y, int a7, int a8)
|
||||
int _win_list_select_at(const char* title, char** items, int itemsLength, ListSelectionHandler* callback, int x, int y, int color, int start)
|
||||
{
|
||||
if (!gWindowSystemInitialized) {
|
||||
return -1;
|
||||
@@ -170,8 +170,8 @@ int _win_list_select_at(const char* title, char** items, int itemsLength, ListSe
|
||||
windowWidth,
|
||||
_colorTable[_GNW_wcolor[0]]);
|
||||
|
||||
int scrollOffset = a8;
|
||||
if (a8 < 0 || a8 >= itemsLength) {
|
||||
int scrollOffset = start;
|
||||
if (start < 0 || start >= itemsLength) {
|
||||
scrollOffset = 0;
|
||||
}
|
||||
|
||||
@@ -189,14 +189,13 @@ int _win_list_select_at(const char* title, char** items, int itemsLength, ListSe
|
||||
selectedItemIndex = 0;
|
||||
}
|
||||
|
||||
char** itemsTO = items + a8;
|
||||
_win_text(win,
|
||||
items + a8,
|
||||
items + start,
|
||||
itemsLength < listViewCapacity ? itemsLength : listViewCapacity,
|
||||
listViewWidth,
|
||||
listViewX,
|
||||
listViewY,
|
||||
a7 | 0x2000000);
|
||||
color | 0x2000000);
|
||||
|
||||
_lighten_buf(listViewBuffer + windowWidth * selectedItemIndex * fontGetLineHeight(),
|
||||
listViewWidth,
|
||||
@@ -452,7 +451,7 @@ int _win_list_select_at(const char* title, char** items, int itemsLength, ListSe
|
||||
listViewWidth,
|
||||
listViewX,
|
||||
listViewY,
|
||||
a7 | 0x2000000);
|
||||
color | 0x2000000);
|
||||
|
||||
_lighten_buf(listViewBuffer + windowWidth * selectedItemIndex * fontGetLineHeight(),
|
||||
listViewWidth,
|
||||
@@ -500,19 +499,19 @@ int _win_list_select_at(const char* title, char** items, int itemsLength, ListSe
|
||||
windowWidth,
|
||||
_colorTable[_GNW_wcolor[0]]);
|
||||
|
||||
int color;
|
||||
if ((a7 & 0xFF00) != 0) {
|
||||
int colorIndex = (a7 & 0xFF) - 1;
|
||||
color = (a7 & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]];
|
||||
int textColor;
|
||||
if ((color & 0xFF00) != 0) {
|
||||
int colorIndex = (color & 0xFF) - 1;
|
||||
textColor = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]];
|
||||
} else {
|
||||
color = a7;
|
||||
textColor = color;
|
||||
}
|
||||
|
||||
fontDrawText(listViewBuffer + windowWidth * previousSelectedItemIndex * fontGetLineHeight(),
|
||||
items[scrollOffset + previousSelectedItemIndex],
|
||||
windowWidth,
|
||||
windowWidth,
|
||||
color);
|
||||
textColor);
|
||||
|
||||
_GNW_win_refresh(window, &itemRect, NULL);
|
||||
}
|
||||
@@ -619,7 +618,7 @@ int _win_get_str(char* dest, int length, const char* title, int x, int y)
|
||||
}
|
||||
|
||||
// 0x4DBA98
|
||||
int _win_msg(const char* string, int x, int y, int flags)
|
||||
int _win_msg(const char* string, int x, int y, int color)
|
||||
{
|
||||
if (!gWindowSystemInitialized) {
|
||||
return -1;
|
||||
@@ -644,16 +643,16 @@ int _win_msg(const char* string, int x, int y, int flags)
|
||||
Window* window = windowGetWindow(win);
|
||||
unsigned char* windowBuffer = window->buffer;
|
||||
|
||||
int color;
|
||||
if ((flags & 0xFF00) != 0) {
|
||||
int index = (flags & 0xFF) - 1;
|
||||
color = _colorTable[_GNW_wcolor[index]];
|
||||
color |= flags & ~0xFFFF;
|
||||
int textColor;
|
||||
if ((color & 0xFF00) != 0) {
|
||||
int index = (color & 0xFF) - 1;
|
||||
textColor = _colorTable[_GNW_wcolor[index]];
|
||||
textColor |= color & ~0xFFFF;
|
||||
} else {
|
||||
color = flags;
|
||||
textColor = color;
|
||||
}
|
||||
|
||||
fontDrawText(windowBuffer + windowWidth * 8 + 16, string, windowWidth, windowWidth, color);
|
||||
fontDrawText(windowBuffer + windowWidth * 8 + 16, string, windowWidth, windowWidth, textColor);
|
||||
|
||||
_win_register_text_button(win,
|
||||
windowWidth / 2 - 32,
|
||||
@@ -679,23 +678,23 @@ int _win_msg(const char* string, int x, int y, int flags)
|
||||
}
|
||||
|
||||
// 0x4DBBC4
|
||||
int _win_pull_down(char** items, int itemsLength, int x, int y, int a5)
|
||||
int _win_pull_down(char** items, int itemsLength, int x, int y, int color)
|
||||
{
|
||||
if (!gWindowSystemInitialized) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Rect rect;
|
||||
int win = _create_pull_down(items, itemsLength, x, y, a5, _colorTable[_GNW_wcolor[0]], &rect);
|
||||
int win = _create_pull_down(items, itemsLength, x, y, color, _colorTable[_GNW_wcolor[0]], &rect);
|
||||
if (win == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sub_4DBD04(win, &rect, items, itemsLength, a5, _colorTable[_GNW_wcolor[0]], NULL, -1);
|
||||
return process_pull_down(win, &rect, items, itemsLength, color, _colorTable[_GNW_wcolor[0]], NULL, -1);
|
||||
}
|
||||
|
||||
// 0x4DBC34
|
||||
int _create_pull_down(char** stringList, int stringListLength, int x, int y, int a5, int a6, Rect* rect)
|
||||
int _create_pull_down(char** stringList, int stringListLength, int x, int y, int foregroundColor, int backgroundColor, Rect* rect)
|
||||
{
|
||||
int windowHeight = stringListLength * fontGetLineHeight() + 16;
|
||||
int windowWidth = _win_width_needed(stringList, stringListLength) + 4;
|
||||
@@ -703,14 +702,14 @@ int _create_pull_down(char** stringList, int stringListLength, int x, int y, int
|
||||
return -1;
|
||||
}
|
||||
|
||||
int win = windowCreate(x, y, windowWidth, windowHeight, a6, WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
|
||||
int win = windowCreate(x, y, windowWidth, windowHeight, backgroundColor, WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
|
||||
if (win == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
_win_text(win, stringList, stringListLength, windowWidth - 4, 2, 8, a5);
|
||||
_win_text(win, stringList, stringListLength, windowWidth - 4, 2, 8, foregroundColor);
|
||||
windowDrawRect(win, 0, 0, windowWidth - 1, windowHeight - 1, _colorTable[0]);
|
||||
windowDrawRect(win, 1, 1, windowWidth - 2, windowHeight - 2, a5);
|
||||
windowDrawRect(win, 1, 1, windowWidth - 2, windowHeight - 2, foregroundColor);
|
||||
windowRefresh(win);
|
||||
windowGetRect(win, rect);
|
||||
|
||||
@@ -841,7 +840,7 @@ void _win_debug_delete(int btn, int keyCode)
|
||||
}
|
||||
|
||||
// 0x4DC674
|
||||
int _win_register_menu_bar(int win, int x, int y, int width, int height, int borderColor, int backgroundColor)
|
||||
int _win_register_menu_bar(int win, int x, int y, int width, int height, int foregroundColor, int backgroundColor)
|
||||
{
|
||||
Window* window = windowGetWindow(win);
|
||||
|
||||
@@ -878,17 +877,17 @@ int _win_register_menu_bar(int win, int x, int y, int width, int height, int bor
|
||||
menuBar->rect.right = right - 1;
|
||||
menuBar->rect.bottom = bottom - 1;
|
||||
menuBar->pulldownsLength = 0;
|
||||
menuBar->borderColor = borderColor;
|
||||
menuBar->foregroundColor = foregroundColor;
|
||||
menuBar->backgroundColor = backgroundColor;
|
||||
|
||||
windowFill(win, x, y, width, height, backgroundColor);
|
||||
windowDrawRect(win, x, y, right - 1, bottom - 1, borderColor);
|
||||
windowDrawRect(win, x, y, right - 1, bottom - 1, foregroundColor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0x4DC768
|
||||
int _win_register_menu_pulldown(int win, int x, char* title, int keyCode, int itemsLength, char** items, int a7, int a8)
|
||||
int _win_register_menu_pulldown(int win, int x, char* title, int keyCode, int itemsLength, char** items, int foregroundColor, int backgroundColor)
|
||||
{
|
||||
Window* window = windowGetWindow(win);
|
||||
|
||||
@@ -928,7 +927,7 @@ int _win_register_menu_pulldown(int win, int x, char* title, int keyCode, int it
|
||||
return -1;
|
||||
}
|
||||
|
||||
windowDrawText(win, title, 0, titleX, titleY, window->menuBar->borderColor | 0x2000000);
|
||||
windowDrawText(win, title, 0, titleX, titleY, window->menuBar->foregroundColor | 0x2000000);
|
||||
|
||||
MenuPulldown* pulldown = &(window->menuBar->pulldowns[window->menuBar->pulldownsLength]);
|
||||
pulldown->rect.left = titleX;
|
||||
@@ -938,8 +937,8 @@ int _win_register_menu_pulldown(int win, int x, char* title, int keyCode, int it
|
||||
pulldown->keyCode = keyCode;
|
||||
pulldown->itemsLength = itemsLength;
|
||||
pulldown->items = items;
|
||||
pulldown->field_1C = a7;
|
||||
pulldown->field_20 = a8;
|
||||
pulldown->foregroundColor = foregroundColor;
|
||||
pulldown->backgroundColor = backgroundColor;
|
||||
|
||||
window->menuBar->pulldownsLength++;
|
||||
|
||||
@@ -1120,7 +1119,7 @@ int _win_input_str(int win, char* dest, int maxLength, int x, int y, int textCol
|
||||
}
|
||||
|
||||
// 0x4DBD04
|
||||
int sub_4DBD04(int win, Rect* rect, char** items, int itemsLength, int a5, int a6, MenuBar* menuBar, int pulldownIndex)
|
||||
int process_pull_down(int win, Rect* rect, char** items, int itemsLength, int foregroundColor, int backgroundColor, MenuBar* menuBar, int pulldownIndex)
|
||||
{
|
||||
// TODO: Incomplete.
|
||||
return -1;
|
||||
@@ -1143,15 +1142,15 @@ int _GNW_process_menu(MenuBar* menuBar, int pulldownIndex)
|
||||
pulldown->itemsLength,
|
||||
pulldown->rect.left,
|
||||
menuBar->rect.bottom + 1,
|
||||
pulldown->field_1C,
|
||||
pulldown->field_20,
|
||||
pulldown->foregroundColor,
|
||||
pulldown->backgroundColor,
|
||||
&rect);
|
||||
if (win == -1) {
|
||||
_curr_menu = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
keyCode = sub_4DBD04(win, &rect, pulldown->items, pulldown->itemsLength, pulldown->field_1C, pulldown->field_20, menuBar, pulldownIndex);
|
||||
keyCode = process_pull_down(win, &rect, pulldown->items, pulldown->itemsLength, pulldown->foregroundColor, pulldown->backgroundColor, menuBar, pulldownIndex);
|
||||
if (keyCode < -1) {
|
||||
pulldownIndex = -2 - keyCode;
|
||||
}
|
||||
@@ -1168,20 +1167,20 @@ int _GNW_process_menu(MenuBar* menuBar, int pulldownIndex)
|
||||
return keyCode;
|
||||
}
|
||||
|
||||
// Calculates max length of string needed to represent a1 or a2.
|
||||
// Calculates max length of string needed to represent `value` or `value2`.
|
||||
//
|
||||
// 0x4DD03C
|
||||
size_t _calc_max_field_chars_wcursor(int a1, int a2)
|
||||
size_t _calc_max_field_chars_wcursor(int value1, int value2)
|
||||
{
|
||||
char* str = (char*)internal_malloc(17);
|
||||
if (str == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(str, 17, "%d", a1);
|
||||
snprintf(str, 17, "%d", value1);
|
||||
size_t len1 = strlen(str);
|
||||
|
||||
snprintf(str, 17, "%d", a2);
|
||||
snprintf(str, 17, "%d", value2);
|
||||
size_t len2 = strlen(str);
|
||||
|
||||
internal_free(str);
|
||||
@@ -1272,7 +1271,7 @@ void _tm_kill_msg()
|
||||
}
|
||||
|
||||
// 0x4DD744
|
||||
void _tm_kill_out_of_order(int a1)
|
||||
void _tm_kill_out_of_order(int queueIndex)
|
||||
{
|
||||
int v7;
|
||||
int v6;
|
||||
@@ -1281,16 +1280,16 @@ void _tm_kill_out_of_order(int a1)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_tm_index_active(a1)) {
|
||||
if (!_tm_index_active(queueIndex)) {
|
||||
return;
|
||||
}
|
||||
|
||||
windowDestroy(_tm_queue[a1].field_4);
|
||||
windowDestroy(_tm_queue[queueIndex].field_4);
|
||||
|
||||
_tm_location[_tm_queue[a1].field_8].field_0 = 0;
|
||||
_tm_location[_tm_queue[queueIndex].field_8].field_0 = 0;
|
||||
|
||||
if (a1 != _tm_kill) {
|
||||
v6 = a1;
|
||||
if (queueIndex != _tm_kill) {
|
||||
v6 = queueIndex;
|
||||
do {
|
||||
v7 = v6 - 1;
|
||||
if (v7 < 0) {
|
||||
@@ -1317,35 +1316,35 @@ void _tm_kill_out_of_order(int a1)
|
||||
void _tm_click_response(int btn)
|
||||
{
|
||||
int win;
|
||||
int v3;
|
||||
int queueIndex;
|
||||
|
||||
if (_tm_kill == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
win = buttonGetWindowId(btn);
|
||||
v3 = _tm_kill;
|
||||
while (win != _tm_queue[v3].field_4) {
|
||||
v3++;
|
||||
if (v3 == 5) {
|
||||
v3 = 0;
|
||||
queueIndex = _tm_kill;
|
||||
while (win != _tm_queue[queueIndex].field_4) {
|
||||
queueIndex++;
|
||||
if (queueIndex == 5) {
|
||||
queueIndex = 0;
|
||||
}
|
||||
|
||||
if (v3 == _tm_kill || !_tm_index_active(v3))
|
||||
if (queueIndex == _tm_kill || !_tm_index_active(queueIndex))
|
||||
return;
|
||||
}
|
||||
|
||||
_tm_kill_out_of_order(v3);
|
||||
_tm_kill_out_of_order(queueIndex);
|
||||
}
|
||||
|
||||
// 0x4DD870
|
||||
int _tm_index_active(int a1)
|
||||
int _tm_index_active(int queueIndex)
|
||||
{
|
||||
if (_tm_kill != _tm_add) {
|
||||
if (_tm_kill >= _tm_add) {
|
||||
if (a1 >= _tm_add && a1 < _tm_kill)
|
||||
if (queueIndex >= _tm_add && queueIndex < _tm_kill)
|
||||
return 0;
|
||||
} else if (a1 < _tm_kill || a1 >= _tm_add) {
|
||||
} else if (queueIndex < _tm_kill || queueIndex >= _tm_add) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,30 +13,30 @@ typedef void(ListSelectionHandler)(char** items, int index);
|
||||
|
||||
extern char gProgramWindowTitle[256];
|
||||
|
||||
int _win_list_select(const char* title, char** fileList, int fileListLength, ListSelectionHandler* callback, int x, int y, int a7);
|
||||
int _win_list_select_at(const char* title, char** items, int itemsLength, ListSelectionHandler* callback, int x, int y, int a7, int a8);
|
||||
int _win_list_select(const char* title, char** fileList, int fileListLength, ListSelectionHandler* callback, int x, int y, int color);
|
||||
int _win_list_select_at(const char* title, char** items, int itemsLength, ListSelectionHandler* callback, int x, int y, int color, int start);
|
||||
int _win_get_str(char* dest, int length, const char* title, int x, int y);
|
||||
int _win_msg(const char* string, int x, int y, int flags);
|
||||
int _win_pull_down(char** items, int itemsLength, int x, int y, int a5);
|
||||
int _create_pull_down(char** stringList, int stringListLength, int x, int y, int a5, int a6, Rect* rect);
|
||||
int _win_msg(const char* string, int x, int y, int color);
|
||||
int _win_pull_down(char** items, int itemsLength, int x, int y, int color);
|
||||
int _create_pull_down(char** stringList, int stringListLength, int x, int y, int foregroundColor, int backgroundColor, Rect* rect);
|
||||
int _win_debug(char* string);
|
||||
void _win_debug_delete(int btn, int keyCode);
|
||||
int _win_register_menu_bar(int win, int x, int y, int width, int height, int borderColor, int backgroundColor);
|
||||
int _win_register_menu_pulldown(int win, int x, char* title, int keyCode, int itemsLength, char** items, int a7, int a8);
|
||||
int _win_register_menu_bar(int win, int x, int y, int width, int height, int foregroundColor, int backgroundColor);
|
||||
int _win_register_menu_pulldown(int win, int x, char* title, int keyCode, int itemsLength, char** items, int foregroundColor, int backgroundColor);
|
||||
void _win_delete_menu_bar(int win);
|
||||
int _find_first_letter(int ch, char** stringList, int stringListLength);
|
||||
int _win_width_needed(char** fileNameList, int fileNameListLength);
|
||||
int _win_input_str(int win, char* dest, int maxLength, int x, int y, int textColor, int backgroundColor);
|
||||
int sub_4DBD04(int win, Rect* rect, char** items, int itemsLength, int a5, int a6, MenuBar* menuBar, int pulldownIndex);
|
||||
int process_pull_down(int win, Rect* rect, char** items, int itemsLength, int a5, int a6, MenuBar* menuBar, int pulldownIndex);
|
||||
int _GNW_process_menu(MenuBar* menuBar, int pulldownIndex);
|
||||
size_t _calc_max_field_chars_wcursor(int a1, int a2);
|
||||
size_t _calc_max_field_chars_wcursor(int value1, int value2);
|
||||
void _GNW_intr_init();
|
||||
void _GNW_intr_exit();
|
||||
void _tm_watch_msgs();
|
||||
void _tm_kill_msg();
|
||||
void _tm_kill_out_of_order(int a1);
|
||||
void _tm_kill_out_of_order(int queueIndex);
|
||||
void _tm_click_response(int btn);
|
||||
int _tm_index_active(int a1);
|
||||
int _tm_index_active(int queueIndex);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user