144 Commits

Author SHA1 Message Date
Alexander Batalov
3cca4a81e9 Check back button to RMB emulation 2022-10-02 13:56:49 +03:00
Alexander Batalov
3924b6bba3 Fix back button on Android 2022-09-28 09:19:02 +03:00
Alexander Batalov
a1c1e03da0 Refactor artLockFrameData with FrmImage 2022-09-26 16:09:22 +03:00
Alexander Batalov
ac0a044a32 Refactor artLockFrameData with FrmImage 2022-09-26 15:17:22 +03:00
Alexander Batalov
4aae167bd8 Refactor artLockFrameData with FrmImage 2022-09-26 11:55:06 +03:00
Alexander Batalov
2e5be31ed4 Refactor artLockFrameData with FrmImage 2022-09-26 11:14:11 +03:00
Alexander Batalov
45278f66a5 Refactor artLockFrameData with FrmImage 2022-09-26 10:36:54 +03:00
Alexander Batalov
2205077d36 Refactor artLockFrameData with FrmImage 2022-09-26 10:28:44 +03:00
Alexander Batalov
67f966f7a9 Refactor artLockFrameData with FrmImage 2022-09-24 22:53:25 +03:00
Alexander Batalov
463968d798 Refactor artLockFrameData with FrmImage 2022-09-24 21:29:50 +03:00
Alexander Batalov
109dc6680c Refactor artLockFrameData with FrmImage 2022-09-24 21:22:40 +03:00
Alexander Batalov
23c5f070fa Refactor artLockFrameData with FrmImage 2022-09-24 21:04:32 +03:00
Alexander Batalov
fd9843f8dc Fix type warning 2022-09-24 19:43:27 +03:00
Alexander Batalov
443070226f Refactor artLockFrameData with FrmImage 2022-09-24 19:41:25 +03:00
Alexander Batalov
db63d8a085 Refactor artLockFrameData with FrmImage 2022-09-24 18:15:52 +03:00
Alexander Batalov
f203cfcc83 Refactor artLockFrameData with FrmImage 2022-09-24 17:31:13 +03:00
Alexander Batalov
9ceb490f72 Refactor artLockFrameData with FrmImage 2022-09-24 17:03:50 +03:00
Alexander Batalov
5b7a676b35 Refactor artLockFrameData with FrmImage 2022-09-24 16:51:16 +03:00
Alexander Batalov
e50e26cf87 Refactor artLockFrameData with FrmImage 2022-09-24 16:39:15 +03:00
Alexander Batalov
f59c072d68 Add FrmImage 2022-09-24 16:14:54 +03:00
Alexander Batalov
79c396c1a0 Uninline characterSelectorWindowFatalError 2022-09-24 10:28:30 +03:00
Alexander Batalov
9bb053b3ba Add namespace (#155) 2022-09-23 15:43:44 +03:00
Alexander Batalov
3168c2ec09 Improve some combat functions readability 2022-09-23 14:01:39 +03:00
Alexander Batalov
cb9c72d1db Bump version to 1.1.0 2022-09-23 09:42:47 +03:00
Edgar Miró
7a455dcad5 Update readme (#143) 2022-09-23 09:33:03 +03:00
Alexander Batalov
cacbecb324 Update license 2022-09-22 13:23:09 +03:00
Alexander Batalov
c521dcaf57 Fix storing pointers in map local variables
Closes #152
2022-09-22 12:45:58 +03:00
Alexander Batalov
b89c06008b Add Windows icon 2022-09-22 10:13:28 +03:00
Alexander Batalov
c20e40652d Add Android icon 2022-09-15 22:06:36 +03:00
Alexander Batalov
2fe74627bd Add macOS icon 2022-09-15 20:59:02 +03:00
Alexander Batalov
9864a2551d Uninline mapSetEnteringLocation 2022-09-15 19:59:16 +03:00
Alexander Batalov
dea8c98399 Decompile square_init 2022-09-15 18:25:48 +03:00
Alexander Batalov
108a20ef27 Decompile mapLocalVariablesLoad 2022-09-15 18:22:27 +03:00
Alexander Batalov
0adc65054c Decompile mapLocalVariablesInit 2022-09-15 18:19:50 +03:00
Alexander Batalov
d72a74f6c3 Decompile mapGlobalVariablesLoad 2022-09-15 18:17:16 +03:00
Alexander Batalov
1d05bac7a1 Decompile mapGlobalVariablesInit 2022-09-15 18:16:46 +03:00
Alexander Batalov
980b40dcdd Review mapNewMap 2022-09-15 17:49:40 +03:00
Alexander Batalov
b1a8707fb2 Review isoExit 2022-09-15 17:45:35 +03:00
Alexander Batalov
382de999cc Review isoReset 2022-09-15 17:43:33 +03:00
Alexander Batalov
06618d9e21 Uninline wmAreaSetVisibleState 2022-09-15 14:25:21 +03:00
Alexander Batalov
1b6e29acc0 Reorder includes 2022-09-15 12:38:23 +03:00
Alexander Batalov
e5992779f3 Reconcile worldmap.cc 2022-09-15 11:42:02 +03:00
Alexander Batalov
d7bc8e4176 Rename worldmap.cc 2022-09-15 09:00:11 +03:00
Alexander Batalov
629978d7a6 Reconcile with reference edition 2022-09-01 18:41:37 +03:00
Alexander Batalov
039ad65557 Add getFileSize 2022-09-01 07:37:00 +03:00
Alexander Batalov
26e5104a96 Add compat_strdup 2022-09-01 06:48:26 +03:00
Alexander Batalov
d39276fe3a Update fpattern config 2022-09-01 06:24:01 +03:00
Alexander Batalov
2a17a07784 Fix AI behaviour for Snipe distance preference 2022-08-31 22:02:08 +03:00
Alexander Batalov
56d35bef0d Add more checks for AI to pick secondary hit mode 2022-08-31 20:25:40 +03:00
Alexander Batalov
676098dc5f Fix weapon safety check 2022-08-31 19:56:09 +03:00
Alexander Batalov
857d3902bb Fix AI weapon switching 2022-08-31 19:53:12 +03:00
Alexander Batalov
7803378d82 Fix some warnings 2022-08-31 18:52:01 +03:00
Alexander Batalov
893a116fb4 Fix getting random target 2022-08-31 18:31:23 +03:00
Alexander Batalov
916bf40602 Add CombatBadShot enum 2022-08-31 18:29:46 +03:00
Alexander Batalov
40d6348b09 Fix incorrect AP cost when AI reloads a weapon 2022-08-31 17:41:50 +03:00
Alexander Batalov
1e5047cd48 Improve aiHaveAmmo 2022-08-31 12:05:56 +03:00
Alexander Batalov
7750006127 Fix warnings 2022-08-31 10:11:01 +03:00
Alexander Batalov
1b69c97ce4 Improve aiAttemptWeaponReload 2022-08-31 10:05:00 +03:00
Alexander Batalov
b3431fadf7 Fix some warnings 2022-08-29 23:58:03 +03:00
Alexander Batalov
2a0d0633f1 Fix images for scroll buttons 2022-08-29 23:42:36 +03:00
Alexander Batalov
4ed86be78e Add missing worldmap button sounds 2022-08-29 23:35:58 +03:00
Alexander Batalov
db13317613 Fix default worldmap font 2022-08-29 23:33:03 +03:00
Alexander Batalov
8deb855b12 Fix Pathfinder perk 2022-08-29 17:37:58 +03:00
Alexander Batalov
24277424d2 Fix typo 2022-08-29 16:08:23 +03:00
Alexander Batalov
c593eceaaf Add pip-boy automaps patch 2022-08-29 16:07:34 +03:00
Alexander Batalov
e3a811c83d Add a flashing icon to the Horrigan encounter 2022-08-29 15:14:16 +03:00
Alexander Batalov
89fd83012a Fix quick destinations positioning 2022-08-24 13:11:46 +03:00
Alexander Batalov
e71447a3d3 Use pop-up message box about death from radiation 2022-08-19 20:25:14 +03:00
Alexander Batalov
b89d6dfd72 Fix crash when opening bag on the bartering table 2022-08-19 19:54:39 +03:00
Alexander Batalov
3878be0d09 Fix items disappearing when using bag 2022-08-19 19:42:21 +03:00
Alexander Batalov
21d550ad1e Fix attackDetermineToHit 2022-08-19 18:58:18 +03:00
Alexander Batalov
134ab451e3 Fix item name separator position 2022-08-19 18:07:12 +03:00
Alexander Batalov
25fc67fec1 Add mouse support on Android
Closes #116
2022-08-19 13:36:14 +03:00
Alexander Batalov
a41780caa2 Rename some item functions 2022-08-18 08:41:15 +03:00
Edgar Miró
4eb5e39946 Add loading dialog (#125)
Co-authored-by: Alexander Batalov <alex.batalov@gmail.com>
2022-08-17 09:32:08 +03:00
Alexander Batalov
354b0812c9 Fix missing Android signing configs in forks/PRs 2022-08-17 00:01:20 +03:00
Alexander Batalov
bee34bfdf4 Add items weight summary 2022-08-16 23:06:40 +03:00
Alexander Batalov
d31e367870 Tweak inventory scroller positions 2022-08-16 22:19:23 +03:00
Alexander Batalov
16f0be1a45 Add more inventory UI constants 2022-08-16 21:08:04 +03:00
Alexander Batalov
ebba548206 Use inventory slot size constants 2022-08-16 16:26:21 +03:00
Alexander Batalov
c9fa9a4765 Fix inventory defines misuse 2022-08-16 16:09:13 +03:00
Alexander Batalov
cfca07f7f2 Add idle func 2022-08-16 14:24:57 +03:00
Alexander Batalov
0f60556b73 Decompile some input functions 2022-08-16 14:14:53 +03:00
Nikola Đurinec
a1521049b5 Fix interface button positions (#131) 2022-08-16 11:52:37 +03:00
Alexander Batalov
1128813bf0 Fix testing pointers for nulls in Sonora 2022-08-16 11:35:21 +03:00
Alexander Batalov
1f6339b3b3 Uncollapse _db_select 2022-08-16 11:25:56 +03:00
Alexander Batalov
79f5e00f6e Fix town map hotkeys 2022-08-15 00:16:36 +03:00
Alexander Batalov
957c5af66b Fix pcx extension check 2022-08-14 23:22:37 +03:00
Alexander Batalov
c5c5ecd499 Fix for Jet antidote not being removed 2022-08-14 23:06:11 +03:00
Alexander Batalov
df4382f2e0 Fix Silent Death bonus for critical hits 2022-08-14 22:46:28 +03:00
Alexander Batalov
8700d0c601 Remove unreferenced label 2022-08-14 22:16:48 +03:00
Alexander Batalov
27c5beea01 Fix max items quantity added via scripting 2022-08-14 22:15:13 +03:00
Alexander Batalov
52af5cfc1f Fix object search when loading game in combat mode 2022-08-14 22:07:22 +03:00
Alexander Batalov
5a47f74023 Fix base EMP damage resistance 2022-08-14 21:45:58 +03:00
Alexander Batalov
c35ea77c59 Fix ammo details when examining in barter screen 2022-08-14 20:45:26 +03:00
Alexander Batalov
b10c580d6f Fix displaying xp gained with Swift Learner bonus 2022-08-14 18:46:02 +03:00
Alexander Batalov
7096116296 Fix best weapon calculations 2022-08-14 17:48:59 +03:00
Alexander Batalov
330edde003 Fix radiation bugs 2022-08-14 17:30:45 +03:00
Alexander Batalov
cac96bfc13 Add gender-specific text improvements (#130) 2022-08-14 13:06:51 +03:00
Alexander Batalov
31edb19379 Use English as fallback language 2022-08-14 12:52:13 +03:00
Alexander Batalov
d482f0e610 Add healing items improvements (#29) 2022-08-14 12:42:18 +03:00
Alexander Batalov
c3bffa6777 Increase script message lists capacity 2022-08-12 15:49:43 +03:00
Alexander Batalov
140234f40e Add game dialog fix 2022-08-12 15:03:06 +03:00
Alexander Batalov
13b76287f8 Add science/repair on critters patch 2022-08-12 14:55:36 +03:00
Alexander Batalov
a55feb9301 Add custom mouse mode frms 2022-08-12 14:19:16 +03:00
Alexander Batalov
d86a887cf9 Add damage mod (#29) 2022-08-12 13:54:18 +03:00
Alexander Batalov
5b2a1d13a1 Fix displaying secondary weapon mode stats 2022-08-11 14:30:06 +03:00
Alexander Batalov
1ca08cb97a Fix unarmed attack mode reset when using inventory 2022-08-11 14:12:29 +03:00
Alexander Batalov
ca78b94a7d Fix sprintf warning 2022-08-11 13:41:06 +03:00
Alexander Batalov
dcb53393c1 FIx opFloatMessage string arg handling
Closes #129
2022-08-11 13:34:37 +03:00
Alexander Batalov
4ece7d1188 Workaround for wrong animate_rotation usage
Closes #128
2022-08-11 13:25:43 +03:00
Alexander Batalov
87289a34c0 Add unarmed hits improvements (#29) 2022-08-10 20:46:09 +03:00
Alexander Batalov
2859410d4b Fix character selector not being reset 2022-08-08 12:04:03 +03:00
Alexander Batalov
9d53496521 Fix premade characters data freed too early 2022-08-08 11:34:36 +03:00
Alexander Batalov
4a8d5b13a8 Fix queue clearing 2022-08-08 11:01:52 +03:00
Alexander Batalov
8ba4fa309d Add shortcuts to close game windows 2022-08-07 21:49:53 +03:00
Alexander Batalov
0cab26227a Fix holodisk highlighting 2022-08-07 21:05:42 +03:00
Alexander Batalov
1694c68ebc Fix rest clickability exploit 2022-08-07 19:54:04 +03:00
Alexander Batalov
6c74c461c2 Review pipboyOpen 2022-08-07 19:03:43 +03:00
Alexander Batalov
0ec5306de5 Fix Barter button 2022-08-07 15:52:28 +03:00
Alexander Batalov
ba2ae3c303 Uninline gameDialogHighlightsExit 2022-08-07 15:38:47 +03:00
Alexander Batalov
019dbbb56e Add mouse wheel support 2022-08-07 14:52:44 +03:00
Alexander Batalov
a47918a83c Fix Pipboy screensaver on non-Windows platforms
Closes #115
2022-08-06 22:46:13 +03:00
Alexander Batalov
25e07fb597 Fix flags on non-door objects 2022-08-06 20:33:53 +03:00
Alexander Batalov
43ad927817 Fix dead critters reload weapons when combat ends 2022-08-06 20:12:23 +03:00
Alexander Batalov
d7966cdab2 Fix updating armor class 2022-08-06 20:05:33 +03:00
Alexander Batalov
33bda1612c Fix withdrawal perk crash 2022-08-06 18:45:44 +03:00
Alexander Batalov
f3eef3fe6c Fix negative Skilldex values 2022-08-06 18:21:47 +03:00
Alexander Batalov
d8ae5fbd32 Fix car charging bugs 2022-08-06 17:49:24 +03:00
Alexander Batalov
02e83a1989 Fix Sharshooter bonus 2022-08-06 17:07:07 +03:00
Alexander Batalov
5c8f7c4b00 Fix negate operator 2022-08-06 16:57:43 +03:00
Alexander Batalov
3592a7232c Add town reputation improvements (#29) 2022-08-06 15:50:33 +03:00
Alexander Batalov
ef067cd954 Reconcile with reference edition 2022-08-06 13:48:16 +03:00
Alexander Batalov
17382d7c7e Fix poping uninitialized pointer
Closes #113
2022-08-06 12:46:05 +03:00
Alexander Batalov
5170948588 Add explosions improvements (#29) 2022-08-05 17:43:00 +03:00
Alexander Batalov
3ccb087d20 Add burst mod (#29) 2022-08-03 17:23:24 +03:00
Alexander Batalov
3f9145b745 Remove varargs.h 2022-08-03 16:13:18 +03:00
Alexander Batalov
5b79d8ca79 Add premade characters improvements (#29) 2022-08-03 16:06:44 +03:00
Alexander Batalov
25c1eeee5c Add display monitor improvements (#29) 2022-08-03 13:54:26 +03:00
Alexander Batalov
f5060c301b Add elevators improvements (#29) 2022-08-03 12:34:13 +03:00
Alexander Batalov
8a2ca4b733 Add books improvements (#29) 2022-08-02 21:52:32 +03:00
Alexander Batalov
9610de0a66 Add criticals config defaults 2022-08-02 21:04:58 +03:00
Alexander Batalov
64627ddde9 Fix sprintf_s 2022-08-02 19:46:44 +03:00
Alexander Batalov
63bcb2d009 Add critical hits improvements (#29) 2022-08-02 19:37:31 +03:00
264 changed files with 11057 additions and 6814 deletions

View File

@@ -54,6 +54,7 @@ jobs:
key: android-cmake-v1
- name: Setup signing config
if: env.KEYSTORE_FILE_BASE64 != '' && env.KEYSTORE_PROPERTIES_FILE_BASE64 != ''
run: |
cd os/android
echo "$KEYSTORE_FILE_BASE64" | base64 --decode > debug.keystore

View File

@@ -33,6 +33,7 @@ jobs:
key: android-cmake-v1
- name: Setup signing config
if: env.KEYSTORE_FILE_BASE64 != '' && env.KEYSTORE_PROPERTIES_FILE_BASE64 != ''
run: |
cd os/android
echo "$KEYSTORE_FILE_BASE64" | base64 --decode > release.keystore

View File

@@ -239,8 +239,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
"src/window.h"
"src/word_wrap.cc"
"src/word_wrap.h"
"src/world_map.cc"
"src/world_map.h"
"src/worldmap.cc"
"src/worldmap.h"
"src/xfile.cc"
"src/xfile.h"
)
@@ -276,11 +276,24 @@ if(WIN32)
)
endif()
if (WIN32)
target_sources(${EXECUTABLE_NAME} PUBLIC
"os/windows/fallout2-ce.ico"
"os/windows/fallout2-ce.rc"
)
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")
set_target_properties(${EXECUTABLE_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/os/macos/Info.plist")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.alexbatalov.fallout2-ce")
set(MACOSX_BUNDLE_BUNDLE_NAME "${EXECUTABLE_NAME}")
set(MACOSX_BUNDLE_ICON_FILE "fallout2-ce.icns")
set(MACOSX_BUNDLE_DISPLAY_NAME "Fallout 2")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "1.1.0")
set(MACOSX_BUNDLE_BUNDLE_VERSION "1.1.0")
endif()
add_subdirectory("third_party/fpattern")

51
LICENSE.md Normal file
View File

@@ -0,0 +1,51 @@
# Sustainable Use License
Version 1.0
## Acceptance
By using the software, you agree to all of the terms and conditions below.
## Copyright License
The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license to use, copy, distribute, make available, and prepare derivative works of the software, in each case subject to the limitations below.
## Limitations
You may use or modify the software only for your own internal business purposes or for non-commercial or personal use. You may distribute the software or provide it to others only if you do so free of charge for non-commercial purposes. You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensors trademarks is subject to applicable law.
## Patents
The licensor grants you a license, under any patent claims the licensor can license, or becomes able to license, to make, have made, use, sell, offer for sale, import and have imported the software, in each case subject to the limitations and conditions in this license. This license does not cover any patent claims that you cause to be infringed by modifications or additions to the software. If you or your company make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company.
## Notices
You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms. If you modify the software, you must include in any modified copies of the software a prominent notice stating that you have modified the software.
## No Other Rights
These terms do not imply any licenses other than those expressly granted in these terms.
## Termination
If you use the software in violation of these terms, such use is not licensed, and your license will automatically terminate. If the licensor provides you with a notice of your violation, and you cease all violation of this license no later than 30 days after you receive that notice, your license will be reinstated retroactively. However, if you violate these terms after such reinstatement, any additional violation of these terms will cause your license to terminate automatically and permanently.
## No Liability
As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.
## Definitions
The "licensor" is the entity offering these terms.
The "software" is the software the licensor makes available under these terms, including any portion of it.
"You" refers to the individual or entity agreeing to these terms.
"Your company" is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. Control means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect.
"Your license" is the license granted to you for the software under these terms.
"Use" means anything you do with the software requiring your license.
"Trademark" means trademarks, service marks, and similar rights.

View File

@@ -42,7 +42,7 @@ $ sudo apt install libsdl2-2.0-0
- 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. There is no fancy importing interface (yet), 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. The game will start automatically.
## Contributing
@@ -52,6 +52,6 @@ Integrating Sfall goodies is the top priority. Quality of life updates are OK to
There are literally hundreds if not thousands of fixes and features in sfall. I guess not all of them are needed in Community Edition, but for the sake of compatibility with big mods out there, let's integrate them all.
## Legal & License
## License
See [Fallout 2 Reference Edition](https://github.com/alexbatalov/fallout2-re). Same conditions apply until the source code in this repository is changed significantly.
The source code is this repository is available under the [Sustainable Use License](LICENSE.md).

View File

@@ -9,8 +9,8 @@ android {
applicationId 'com.alexbatalov.fallout2ce'
minSdk 21
targetSdk 32
versionCode 1
versionName '1.0'
versionCode 2
versionName '1.1.0'
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=c++_static'

View File

@@ -58,6 +58,7 @@
An example Java class can be found in README-android.md
-->
<application android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:allowBackup="true"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:hardwareAccelerated="true" >

View File

@@ -0,0 +1,56 @@
package com.alexbatalov.fallout2ce;
import android.content.ContentResolver;
import androidx.documentfile.provider.DocumentFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class FileUtils {
static boolean copyRecursively(ContentResolver contentResolver, DocumentFile src, File dest) {
final DocumentFile[] documentFiles = src.listFiles();
for (final DocumentFile documentFile : documentFiles) {
if (documentFile.isFile()) {
if (!copyFile(contentResolver, documentFile, new File(dest, documentFile.getName()))) {
return false;
}
} else if (documentFile.isDirectory()) {
final File subdirectory = new File(dest, documentFile.getName());
if (!subdirectory.exists()) {
subdirectory.mkdir();
}
if (!copyRecursively(contentResolver, documentFile, subdirectory)) {
return false;
}
}
}
return true;
}
private static boolean copyFile(ContentResolver contentResolver, DocumentFile src, File dest) {
try {
final InputStream inputStream = contentResolver.openInputStream(src.getUri());
final OutputStream outputStream = new FileOutputStream(dest);
final byte[] buffer = new byte[16384];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
inputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
}

View File

@@ -1,6 +1,8 @@
package com.alexbatalov.fallout2ce;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -8,10 +10,6 @@ import android.os.Bundle;
import androidx.documentfile.provider.DocumentFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class ImportActivity extends Activity {
private static final int IMPORT_REQUEST_CODE = 1;
@@ -31,10 +29,8 @@ public class ImportActivity extends Activity {
if (treeUri != null) {
final DocumentFile treeDocument = DocumentFile.fromTreeUri(this, treeUri);
if (treeDocument != null) {
copyRecursively(treeDocument, getExternalFilesDir(null));
final Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
copyFiles(treeDocument);
return;
}
}
}
@@ -45,45 +41,34 @@ public class ImportActivity extends Activity {
}
}
private boolean copyRecursively(DocumentFile src, File dest) {
final DocumentFile[] documentFiles = src.listFiles();
for (final DocumentFile documentFile : documentFiles) {
if (documentFile.isFile()) {
if (!copyFile(documentFile, new File(dest, documentFile.getName()))) {
return false;
}
} else if (documentFile.isDirectory()) {
final File subdirectory = new File(dest, documentFile.getName());
if (!subdirectory.exists()) {
subdirectory.mkdir();
}
private void copyFiles(DocumentFile treeDocument) {
ProgressDialog dialog = createProgressDialog();
dialog.show();
if (!copyRecursively(documentFile, subdirectory)) {
return false;
}
}
}
return true;
new Thread(() -> {
ContentResolver contentResolver = getContentResolver();
File externalFilesDir = getExternalFilesDir(null);
FileUtils.copyRecursively(contentResolver, treeDocument, externalFilesDir);
startMainActivity();
dialog.dismiss();
finish();
}).start();
}
private boolean copyFile(DocumentFile src, File dest) {
try {
final InputStream inputStream = getContentResolver().openInputStream(src.getUri());
final OutputStream outputStream = new FileOutputStream(dest);
private void startMainActivity() {
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
}
final byte[] buffer = new byte[16384];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
private ProgressDialog createProgressDialog() {
ProgressDialog progressDialog = new ProgressDialog(this,
android.R.style.Theme_Material_Light_Dialog);
progressDialog.setTitle(R.string.loading_dialog_title);
progressDialog.setMessage(getString(R.string.loading_dialog_message));
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressDialog.setCancelable(false);
inputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
return progressDialog;
}
}

View File

@@ -2036,6 +2036,14 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
switch (event.getAction()) {
case KeyEvent.ACTION_DOWN:
case KeyEvent.ACTION_UP:
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
SDLActivity.onNativeMouse(MotionEvent.BUTTON_SECONDARY, MotionEvent.ACTION_DOWN, 0, 0, true);
} else if (event.getAction() == KeyEvent.ACTION_UP) {
SDLActivity.onNativeMouse(MotionEvent.BUTTON_SECONDARY, MotionEvent.ACTION_UP, 0, 0, true);
}
}
// mark the event as handled or it will be handled by system
// handling KEYCODE_BACK by system will call onBackPressed()
return true;

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#000000</color>
</resources>

View File

@@ -1,3 +1,5 @@
<resources>
<string name="app_name">Fallout 2</string>
<string name="loading_dialog_title">PLEASE STAND BY</string>
<string name="loading_dialog_message">Copying files…</string>
</resources>

BIN
os/macos/fallout2-ce.icns Normal file

Binary file not shown.

BIN
os/windows/fallout2-ce.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

View File

@@ -0,0 +1 @@
IDI_ICON1 ICON DISCARDABLE "fallout2-ce.ico"

View File

@@ -1,5 +1,8 @@
#include "actions.h"
#include <limits.h>
#include <string.h>
#include "animation.h"
#include "art.h"
#include "color.h"
@@ -25,17 +28,23 @@
#include "proto_types.h"
#include "random.h"
#include "scripts.h"
#include "sfall_config.h"
#include "skill.h"
#include "stat.h"
#include "text_object.h"
#include "tile.h"
#include "trait.h"
#include <limits.h>
#include <string.h>
namespace fallout {
#define MAX_KNOCKDOWN_DISTANCE 20
typedef enum ScienceRepairTargetType {
SCIENCE_REPAIR_TARGET_TYPE_DEFAULT,
SCIENCE_REPAIR_TARGET_TYPE_DUDE,
SCIENCE_REPAIR_TARGET_TYPE_ANYONE,
} ScienceRepairTargetType;
// 0x5106D0
static int _action_in_explode = 0;
@@ -74,6 +83,7 @@ static int _action_melee(Attack* attack, int a2);
static int _action_ranged(Attack* attack, int a2);
static int _is_next_to(Object* a1, Object* a2);
static int _action_climb_ladder(Object* a1, Object* a2);
static int _action_use_skill_in_combat_error(Object* critter);
static int _pick_fall(Object* obj, int anim);
static int _report_explosion(Attack* attack, Object* a2);
static int _finished_explosion(Object* a1, Object* a2);
@@ -316,8 +326,32 @@ void _show_damage_to_object(Object* a1, int damage, int flags, Object* weapon, b
sfx_name = sfxBuildCharName(a1, anim, CHARACTER_SOUND_EFFECT_UNUSED);
animationRegisterPlaySoundEffect(a1, sfx_name, a10);
// SFALL
if (explosionEmitsLight()) {
// 0xFFFF0002:
// - distance: 2
// - intensity: 65535
//
// NOTE: Change intensity to 65536 (which is on par with
// `anim_set_check_light_fix` Sfall's hack).
animationRegisterSetLightIntensity(a1, 2, 65536, 0);
}
animationRegisterAnimate(a1, anim, 0);
// SFALL
if (explosionEmitsLight()) {
// 0x00010000:
// - distance: 0
// - intensity: 1
//
// NOTE: Change intensity to 0. I guess using 1 was a
// workaround for `anim_set_check_light_fix` hack which
// requires two upper bytes to be non-zero to override
// default behaviour.
animationRegisterSetLightIntensity(a1, 0, 0, -1);
}
int randomDistance = randomBetween(2, 5);
int randomRotation = randomBetween(0, 5);
@@ -441,7 +475,7 @@ int _show_death(Object* obj, int anim)
}
if (anim >= 30 && anim <= 31 && _critter_flag_check(obj->pid, CRITTER_FLAG_0x1000) == 0 && _critter_flag_check(obj->pid, CRITTER_FLAG_0x40) == 0) {
_item_drop_all(obj, obj->tile);
itemDropAll(obj, obj->tile);
}
tileWindowRefreshRect(&v8, obj->elevation);
@@ -682,7 +716,7 @@ int _action_ranged(Attack* attack, int anim)
int actionFrame = (art != NULL) ? artGetActionFrame(art) : 0;
artUnlock(artHandle);
_item_w_range(attack->attacker, attack->hitMode);
weaponGetRange(attack->attacker, attack->hitMode);
int damageType = weaponGetDamageType(attack->attacker, attack->weapon);
@@ -692,7 +726,8 @@ int _action_ranged(Attack* attack, int anim)
bool isGrenade = false;
if (anim == ANIM_THROW_ANIM) {
if (damageType == DAMAGE_TYPE_EXPLOSION || damageType == DAMAGE_TYPE_PLASMA || damageType == DAMAGE_TYPE_EMP) {
// SFALL
if (damageType == explosionGetDamageType() || damageType == DAMAGE_TYPE_PLASMA || damageType == DAMAGE_TYPE_EMP) {
isGrenade = true;
}
} else {
@@ -728,7 +763,7 @@ int _action_ranged(Attack* attack, int anim)
interfaceGetItemActions(&leftItemAction, &rightItemAction);
itemRemove(attack->attacker, weapon, 1);
v50 = _item_replace(attack->attacker, weapon, weaponFlags & OBJECT_IN_ANY_HAND);
v50 = itemReplace(attack->attacker, weapon, weaponFlags & OBJECT_IN_ANY_HAND);
objectSetFid(projectile, projectileProto->fid, NULL);
_cAIPrepWeaponItem(attack->attacker, weapon);
@@ -750,7 +785,12 @@ int _action_ranged(Attack* attack, int anim)
objectHide(projectile, NULL);
objectSetLight(projectile, 9, projectile->lightIntensity, NULL);
// SFALL
if (explosionEmitsLight() && projectile->lightIntensity == 0) {
objectSetLight(projectile, projectileProto->item.lightDistance, projectileProto->item.lightIntensity, NULL);
} else {
objectSetLight(projectile, 9, projectile->lightIntensity, NULL);
}
int projectileOrigin = _combat_bullet_start(attack->attacker, attack->defender);
objectSetLocation(projectile, projectileOrigin, attack->attacker->elevation, NULL);
@@ -774,7 +814,8 @@ int _action_ranged(Attack* attack, int anim)
v24 = attack->tile;
}
if (isGrenade || damageType == DAMAGE_TYPE_EXPLOSION) {
// SFALL
if (isGrenade || damageType == explosionGetDamageType()) {
if ((attack->attackerFlags & DAM_DROP) == 0) {
int explosionFrmId;
if (isGrenade) {
@@ -793,6 +834,12 @@ int _action_ranged(Attack* attack, int anim)
explosionFrmId = 10;
}
// SFALL
int explosionFrmIdOverride = explosionGetFrm();
if (explosionFrmIdOverride != -1) {
explosionFrmId = explosionFrmIdOverride;
}
if (isGrenade) {
animationRegisterSetFid(projectile, weaponFid, -1);
}
@@ -803,9 +850,23 @@ int _action_ranged(Attack* attack, int anim)
const char* sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_HIT, weapon, attack->hitMode, attack->defender);
animationRegisterPlaySoundEffect(projectile, sfx, 0);
animationRegisterAnimateAndHide(projectile, ANIM_STAND, 0);
// SFALL
if (explosionEmitsLight()) {
animationRegisterAnimate(projectile, ANIM_STAND, 0);
// 0xFFFF0008
// - distance: 8
// - intensity: 65535
animationRegisterSetLightIntensity(projectile, 8, 65536, 0);
} else {
animationRegisterAnimateAndHide(projectile, ANIM_STAND, 0);
}
for (int rotation = 0; rotation < ROTATION_COUNT; rotation++) {
// SFALL
int startRotation;
int endRotation;
explosionGetPattern(&startRotation, &endRotation);
for (int rotation = startRotation; rotation < endRotation; rotation++) {
if (objectCreateWithFidPid(&(neighboors[rotation]), explosionFid, -1) != -1) {
objectHide(neighboors[rotation], NULL);
@@ -864,7 +925,8 @@ int _action_ranged(Attack* attack, int anim)
}
}
if (projectile != NULL && (isGrenade || damageType == DAMAGE_TYPE_EXPLOSION)) {
// SFALL
if (projectile != NULL && (isGrenade || damageType == explosionGetDamageType())) {
animationRegisterHideObjectForced(projectile);
} else if (anim == ANIM_THROW_ANIM && projectile != NULL) {
animationRegisterSetFid(projectile, weaponFid, -1);
@@ -1230,23 +1292,33 @@ int _action_skill_use(int skill)
return -1;
}
// NOTE: Inlined.
//
// 0x412500
static int _action_use_skill_in_combat_error(Object* critter)
{
MessageListItem messageListItem;
if (critter == gDude) {
messageListItem.num = 902;
if (messageListGetItem(&gProtoMessageList, &messageListItem) == 1) {
displayMonitorAddMessage(messageListItem.text);
}
}
return -1;
}
// skill_use
// 0x41255C
int actionUseSkill(Object* a1, Object* a2, int skill)
{
MessageListItem messageListItem;
switch (skill) {
case SKILL_FIRST_AID:
case SKILL_DOCTOR:
if (isInCombat()) {
if (a1 == gDude) {
messageListItem.num = 902;
if (messageListGetItem(&gProtoMessageList, &messageListItem)) {
displayMonitorAddMessage(messageListItem.text);
}
}
return -1;
// NOTE: Uninline.
return _action_use_skill_in_combat_error(a1);
}
if (PID_TYPE(a2->pid) != OBJ_TYPE_CRITTER) {
@@ -1255,13 +1327,8 @@ int actionUseSkill(Object* a1, Object* a2, int skill)
break;
case SKILL_LOCKPICK:
if (isInCombat()) {
if (a1 == gDude) {
messageListItem.num = 902;
if (messageListGetItem(&gProtoMessageList, &messageListItem)) {
displayMonitorAddMessage(messageListItem.text);
}
}
return -1;
// NOTE: Uninline.
return _action_use_skill_in_combat_error(a1);
}
if (PID_TYPE(a2->pid) != OBJ_TYPE_ITEM && PID_TYPE(a2->pid) != OBJ_TYPE_SCENERY) {
@@ -1271,13 +1338,8 @@ int actionUseSkill(Object* a1, Object* a2, int skill)
break;
case SKILL_STEAL:
if (isInCombat()) {
if (a1 == gDude) {
messageListItem.num = 902;
if (messageListGetItem(&gProtoMessageList, &messageListItem)) {
displayMonitorAddMessage(messageListItem.text);
}
}
return -1;
// NOTE: Uninline.
return _action_use_skill_in_combat_error(a1);
}
if (PID_TYPE(a2->pid) != OBJ_TYPE_ITEM && PID_TYPE(a2->pid) != OBJ_TYPE_CRITTER) {
@@ -1291,13 +1353,8 @@ int actionUseSkill(Object* a1, Object* a2, int skill)
break;
case SKILL_TRAPS:
if (isInCombat()) {
if (a1 == gDude) {
messageListItem.num = 902;
if (messageListGetItem(&gProtoMessageList, &messageListItem)) {
displayMonitorAddMessage(messageListItem.text);
}
}
return -1;
// NOTE: Uninline.
return _action_use_skill_in_combat_error(a1);
}
if (PID_TYPE(a2->pid) == OBJ_TYPE_CRITTER) {
@@ -1308,13 +1365,8 @@ int actionUseSkill(Object* a1, Object* a2, int skill)
case SKILL_SCIENCE:
case SKILL_REPAIR:
if (isInCombat()) {
if (a1 == gDude) {
messageListItem.num = 902;
if (messageListGetItem(&gProtoMessageList, &messageListItem)) {
displayMonitorAddMessage(messageListItem.text);
}
}
return -1;
// NOTE: Uninline.
return _action_use_skill_in_combat_error(a1);
}
if (PID_TYPE(a2->pid) != OBJ_TYPE_CRITTER) {
@@ -1330,6 +1382,19 @@ int actionUseSkill(Object* a1, Object* a2, int skill)
break;
}
// SFALL: Science on critters patch.
if (1) {
int targetType = SCIENCE_REPAIR_TARGET_TYPE_DEFAULT;
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_SCIENCE_REPAIR_TARGET_TYPE_KEY, &targetType);
if (targetType == SCIENCE_REPAIR_TARGET_TYPE_DUDE) {
if (a2 == gDude) {
break;
}
} else if (targetType == SCIENCE_REPAIR_TARGET_TYPE_ANYONE) {
break;
}
}
return -1;
case SKILL_SNEAK:
dudeToggleState(0);
@@ -2051,3 +2116,5 @@ int _action_can_talk_to(Object* a1, Object* a2)
return 0;
}
} // namespace fallout

View File

@@ -4,6 +4,8 @@
#include "combat_defs.h"
#include "obj_types.h"
namespace fallout {
int _action_attack(Attack* attack);
int _action_use_an_item_on_object(Object* a1, Object* a2, Object* a3);
int _action_use_an_object(Object* a1, Object* a2);
@@ -21,4 +23,6 @@ bool actionCheckPush(Object* a1, Object* a2);
int actionPush(Object* a1, Object* a2);
int _action_can_talk_to(Object* a1, Object* a2);
} // namespace fallout
#endif /* ACTIONS_H */

View File

@@ -1,5 +1,8 @@
#include "animation.h"
#include <stdio.h>
#include <string.h>
#include "art.h"
#include "color.h"
#include "combat.h"
@@ -28,8 +31,7 @@
#include "tile.h"
#include "trait.h"
#include <stdio.h>
#include <string.h>
namespace fallout {
#define ANIMATION_SEQUENCE_LIST_CAPACITY 32
#define ANIMATION_DESCRIPTION_LIST_CAPACITY 55
@@ -57,13 +59,17 @@ typedef enum AnimationKind {
ANIM_KIND_SET_FID = 17,
ANIM_KIND_TAKE_OUT_WEAPON = 18,
ANIM_KIND_SET_LIGHT_DISTANCE = 19,
ANIM_KIND_20 = 20,
ANIM_KIND_23 = 23,
ANIM_KIND_MOVE_ON_STAIRS = 20,
ANIM_KIND_CHECK_FALLING = 23,
ANIM_KIND_TOGGLE_OUTLINE = 24,
ANIM_KIND_ANIMATE_FOREVER = 25,
ANIM_KIND_26 = 26,
ANIM_KIND_27 = 27,
ANIM_KIND_NOOP = 28,
// New animation to update both light distance and intensity. Required to
// impement Sfall's explosion light effects without resorting to hackery.
ANIM_KIND_SET_LIGHT_INTENSITY,
} AnimationKind;
typedef enum AnimationSequenceFlags {
@@ -197,6 +203,9 @@ typedef struct AnimationDescription {
// ANIM_KIND_CALLBACK3
void* param3;
// ANIM_KIND_SET_LIGHT_INTENSITY
int lightIntensity;
};
CacheEntry* artCacheKey;
} AnimationDescription;
@@ -247,6 +256,7 @@ typedef struct AnimationSad {
} AnimationSad;
static int _anim_free_slot(int a1);
static int _anim_preload(Object* object, int fid, CacheEntry** cacheEntryPtr);
static void _anim_cleanup();
static int _check_registry(Object* obj);
static int animationRunSequence(int a1);
@@ -266,6 +276,7 @@ static void _object_straight_move(int index);
static int _anim_animate(Object* obj, int anim, int animationSequenceIndex, int flags);
static void _object_anim_compact();
static int actionRotate(Object* obj, int delta, int animationSequenceIndex);
static int _anim_hide(Object* object, int animationSequenceIndex);
static int _anim_change_fid(Object* obj, int animationSequenceIndex, int fid);
static int _check_gravity(int tile, int elevation);
static unsigned int animationComputeTicksPerFrame(Object* object, int fid);
@@ -494,6 +505,22 @@ int reg_anim_end()
return 0;
}
// NOTE: Inlined.
//
// 0x413D6C
static int _anim_preload(Object* object, int fid, CacheEntry** cacheEntryPtr)
{
*cacheEntryPtr = NULL;
if (artLock(fid, cacheEntryPtr) != NULL) {
artUnlock(*cacheEntryPtr);
*cacheEntryPtr = NULL;
return 0;
}
return -1;
}
// 0x413D98
static void _anim_cleanup()
{
@@ -611,14 +638,12 @@ int animationRegisterMoveToObject(Object* owner, Object* destination, int action
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, animationDescription->anim, (owner->fid & 0xF000) >> 12, owner->rotation + 1);
if (artLock(fid, &(animationDescription->artCacheKey)) == NULL) {
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
artUnlock(animationDescription->artCacheKey);
animationDescription->artCacheKey = NULL;
gAnimationDescriptionCurrentIndex++;
return animationRegisterRotateToTile(owner, destination->tile);
@@ -673,15 +698,12 @@ int animationRegisterRunToObject(Object* owner, Object* destination, int actionP
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, animationDescription->anim, (owner->fid & 0xF000) >> 12, owner->rotation + 1);
animationDescription->artCacheKey = NULL;
if (artLock(fid, &(animationDescription->artCacheKey)) == NULL) {
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
artUnlock(animationDescription->artCacheKey);
animationDescription->artCacheKey = NULL;
gAnimationDescriptionCurrentIndex++;
return animationRegisterRotateToTile(owner, destination->tile);
}
@@ -706,17 +728,15 @@ int animationRegisterMoveToTile(Object* owner, int tile, int elevation, int acti
animationDescription->elevation = elevation;
animationDescription->actionPoints = actionPoints;
animationDescription->delay = delay;
animationDescription->artCacheKey = NULL;
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, animationDescription->anim, (owner->fid & 0xF000) >> 12, owner->rotation + 1);
if (artLock(fid, &(animationDescription->artCacheKey)) == NULL) {
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
artUnlock(animationDescription->artCacheKey);
animationDescription->artCacheKey = NULL;
gAnimationDescriptionCurrentIndex++;
return 0;
@@ -774,15 +794,12 @@ int animationRegisterRunToTile(Object* owner, int tile, int elevation, int actio
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, animationDescription->anim, (owner->fid & 0xF000) >> 12, owner->rotation + 1);
animationDescription->artCacheKey = NULL;
if (artLock(fid, &(animationDescription->artCacheKey)) == NULL) {
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
artUnlock(animationDescription->artCacheKey);
animationDescription->artCacheKey = NULL;
gAnimationDescriptionCurrentIndex++;
return 0;
@@ -809,17 +826,15 @@ int animationRegisterMoveToTileStraight(Object* object, int tile, int elevation,
animationDescription->elevation = elevation;
animationDescription->anim = anim;
animationDescription->delay = delay;
animationDescription->artCacheKey = NULL;
int fid = buildFid(FID_TYPE(object->fid), object->fid & 0xFFF, animationDescription->anim, (object->fid & 0xF000) >> 12, object->rotation + 1);
if (artLock(fid, &(animationDescription->artCacheKey)) == NULL) {
// NOTE: Uninline.
if (_anim_preload(object, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
artUnlock(animationDescription->artCacheKey);
animationDescription->artCacheKey = NULL;
gAnimationDescriptionCurrentIndex++;
return 0;
@@ -845,17 +860,15 @@ int animationRegisterMoveToTileStraightAndWaitForComplete(Object* owner, int til
animationDescription->elevation = elevation;
animationDescription->anim = anim;
animationDescription->delay = delay;
animationDescription->artCacheKey = NULL;
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, animationDescription->anim, (owner->fid & 0xF000) >> 12, owner->rotation + 1);
if (artLock(fid, &(animationDescription->artCacheKey)) == NULL) {
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
artUnlock(animationDescription->artCacheKey);
animationDescription->artCacheKey = NULL;
gAnimationDescriptionCurrentIndex++;
return 0;
@@ -875,17 +888,15 @@ int animationRegisterAnimate(Object* owner, int anim, int delay)
animationDescription->owner = owner;
animationDescription->anim = anim;
animationDescription->delay = delay;
animationDescription->artCacheKey = NULL;
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, animationDescription->anim, (owner->fid & 0xF000) >> 12, owner->rotation + 1);
if (artLock(fid, &(animationDescription->artCacheKey)) == NULL) {
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
artUnlock(animationDescription->artCacheKey);
animationDescription->artCacheKey = NULL;
gAnimationDescriptionCurrentIndex++;
return 0;
@@ -908,14 +919,13 @@ int animationRegisterAnimateReversed(Object* owner, int anim, int delay)
animationDescription->artCacheKey = NULL;
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, animationDescription->anim, (owner->fid & 0xF000) >> 12, owner->rotation + 1);
if (artLock(fid, &(animationDescription->artCacheKey)) == NULL) {
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
artUnlock(animationDescription->artCacheKey);
animationDescription->artCacheKey = NULL;
gAnimationDescriptionCurrentIndex++;
return 0;
@@ -938,14 +948,13 @@ int animationRegisterAnimateAndHide(Object* owner, int anim, int delay)
animationDescription->artCacheKey = NULL;
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, anim, (owner->fid & 0xF000) >> 12, owner->rotation + 1);
if (artLock(fid, &(animationDescription->artCacheKey)) == NULL) {
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
artUnlock(animationDescription->artCacheKey);
animationDescription->artCacheKey = NULL;
gAnimationDescriptionCurrentIndex++;
return 0;
@@ -1192,16 +1201,13 @@ int animationRegisterSetFid(Object* owner, int fid, int delay)
animationDescription->owner = owner;
animationDescription->fid = fid;
animationDescription->delay = delay;
animationDescription->artCacheKey = NULL;
if (artLock(fid, &(animationDescription->artCacheKey)) == NULL) {
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
artUnlock(animationDescription->artCacheKey);
animationDescription->artCacheKey = NULL;
gAnimationDescriptionCurrentIndex++;
return 0;
@@ -1229,14 +1235,13 @@ int animationRegisterTakeOutWeapon(Object* owner, int weaponAnimationCode, int d
animationDescription->weaponAnimationCode = weaponAnimationCode;
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, ANIM_TAKE_OUT, weaponAnimationCode, owner->rotation + 1);
if (artLock(fid, &(animationDescription->artCacheKey)) == NULL) {
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
artUnlock(animationDescription->artCacheKey);
animationDescription->artCacheKey = NULL;
gAnimationDescriptionCurrentIndex++;
return 0;
@@ -1332,17 +1337,15 @@ int animationRegisterAnimateForever(Object* owner, int anim, int delay)
animationDescription->owner = owner;
animationDescription->anim = anim;
animationDescription->delay = delay;
animationDescription->artCacheKey = NULL;
int fid = buildFid(FID_TYPE(owner->fid), owner->fid & 0xFFF, anim, (owner->fid & 0xF000) >> 12, owner->rotation + 1);
if (artLock(fid, &(animationDescription->artCacheKey)) == NULL) {
// NOTE: Uninline.
if (_anim_preload(owner, fid, &(animationDescription->artCacheKey)) == -1) {
_anim_cleanup();
return -1;
}
artUnlock(animationDescription->artCacheKey);
animationDescription->artCacheKey = NULL;
gAnimationDescriptionCurrentIndex++;
return 0;
@@ -1431,15 +1434,8 @@ static int animationRunSequence(int animationSequenceIndex)
case ANIM_KIND_ANIMATE_AND_HIDE:
rc = _anim_animate(animationDescription->owner, animationDescription->anim, animationSequenceIndex, ANIM_SAD_HIDE_ON_END);
if (rc == -1) {
Rect rect;
if (objectHide(animationDescription->owner, &rect) == 0) {
tileWindowRefreshRect(&rect, animationDescription->elevation);
}
if (animationSequenceIndex != -1) {
_anim_set_continue(animationSequenceIndex, 0);
}
rc = 0;
// NOTE: Uninline.
rc = _anim_hide(animationDescription->owner, animationSequenceIndex);
}
break;
case ANIM_KIND_ANIMATE_FOREVER:
@@ -1460,13 +1456,8 @@ static int animationRunSequence(int animationSequenceIndex)
rc = actionRotate(animationDescription->owner, -1, animationSequenceIndex);
break;
case ANIM_KIND_HIDE:
if (objectHide(animationDescription->owner, &rect) == 0) {
tileWindowRefreshRect(&rect, animationDescription->owner->elevation);
}
if (animationSequenceIndex != -1) {
_anim_set_continue(animationSequenceIndex, 0);
}
rc = 0;
// NOTE: Uninline.
rc = _anim_hide(animationDescription->owner, animationSequenceIndex);
break;
case ANIM_KIND_CALLBACK:
rc = animationDescription->callback(animationDescription->param1, animationDescription->param2);
@@ -1527,10 +1518,15 @@ static int animationRunSequence(int animationSequenceIndex)
tileWindowRefreshRect(&rect, animationDescription->owner->elevation);
rc = _anim_set_continue(animationSequenceIndex, 0);
break;
case ANIM_KIND_20:
case ANIM_KIND_SET_LIGHT_INTENSITY:
objectSetLight(animationDescription->owner, animationDescription->lightDistance, animationDescription->lightIntensity, &rect);
tileWindowRefreshRect(&rect, animationDescription->owner->elevation);
rc = _anim_set_continue(animationSequenceIndex, 0);
break;
case ANIM_KIND_MOVE_ON_STAIRS:
rc = _anim_move_on_stairs(animationDescription->owner, animationDescription->tile, animationDescription->elevation, animationDescription->anim, animationSequenceIndex);
break;
case ANIM_KIND_23:
case ANIM_KIND_CHECK_FALLING:
rc = _check_for_falling(animationDescription->owner, animationDescription->anim, animationSequenceIndex);
break;
case ANIM_KIND_TOGGLE_OUTLINE:
@@ -2837,9 +2833,8 @@ void _object_animate()
artUnlock(cacheHandle);
if ((sad->flags & ANIM_SAD_HIDE_ON_END) != 0) {
if (objectHide(object, &tempRect) == 0) {
tileWindowRefreshRect(&tempRect, object->elevation);
}
// NOTE: Uninline.
_anim_hide(object, -1);
}
_anim_set_continue(sad->animationSequenceIndex, 1);
@@ -2993,7 +2988,7 @@ int _check_move(int* a1)
bool aiming;
interfaceGetCurrentHitMode(&hitMode, &aiming);
int v6 = _item_mp_cost(gDude, hitMode, aiming);
int v6 = itemGetActionPointCost(gDude, hitMode, aiming);
*a1 = *a1 - v6;
if (*a1 <= 0) {
return -1;
@@ -3250,6 +3245,24 @@ static int actionRotate(Object* obj, int delta, int animationSequenceIndex)
return 0;
}
// NOTE: Inlined.
//
// 0x41862C
static int _anim_hide(Object* object, int animationSequenceIndex)
{
Rect rect;
if (objectHide(object, &rect) == 0) {
tileWindowRefreshRect(&rect, object->elevation);
}
if (animationSequenceIndex != -1) {
_anim_set_continue(animationSequenceIndex, 0);
}
return 0;
}
// 0x418660
static int _anim_change_fid(Object* obj, int animationSequenceIndex, int fid)
{
@@ -3330,3 +3343,26 @@ static unsigned int animationComputeTicksPerFrame(Object* object, int fid)
return 1000 / fps;
}
int animationRegisterSetLightIntensity(Object* owner, int lightDistance, int lightIntensity, int delay)
{
if (_check_registry(owner) == -1) {
_anim_cleanup();
return -1;
}
AnimationSequence* animationSequence = &(gAnimationSequences[gAnimationSequenceCurrentIndex]);
AnimationDescription* animationDescription = &(animationSequence->animations[gAnimationDescriptionCurrentIndex]);
animationDescription->kind = ANIM_KIND_SET_LIGHT_INTENSITY;
animationDescription->artCacheKey = NULL;
animationDescription->owner = owner;
animationDescription->lightDistance = lightDistance;
animationDescription->lightIntensity = lightIntensity;
animationDescription->delay = delay;
gAnimationDescriptionCurrentIndex++;
return 0;
}
} // namespace fallout

View File

@@ -4,6 +4,8 @@
#include "combat_defs.h"
#include "obj_types.h"
namespace fallout {
typedef enum AnimationRequestOptions {
ANIMATION_REQUEST_UNRESERVED = 0x01,
ANIMATION_REQUEST_RESERVED = 0x02,
@@ -154,4 +156,8 @@ void _dude_stand(Object* obj, int rotation, int fid);
void _dude_standup(Object* a1);
void animationStop();
int animationRegisterSetLightIntensity(Object* owner, int lightDistance, int lightIntensity, int delay);
} // namespace fallout
#endif /* ANIMATION_H */

View File

@@ -1,5 +1,9 @@
#include "art.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "animation.h"
#include "debug.h"
#include "draw.h"
@@ -10,9 +14,7 @@
#include "proto.h"
#include "sfall_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
namespace fallout {
typedef struct ArtListDescription {
int flags;
@@ -146,15 +148,31 @@ int artInit()
gArtLanguageInitialized = true;
}
bool critterDbSelected = false;
for (int objectType = 0; objectType < OBJ_TYPE_COUNT; objectType++) {
gArtListDescriptions[objectType].flags = 0;
sprintf(path, "%s%s%s\\%s.lst", _cd_path_base, "art\\", gArtListDescriptions[objectType].name, gArtListDescriptions[objectType].name);
int oldDb;
if (objectType == OBJ_TYPE_CRITTER) {
oldDb = _db_current();
critterDbSelected = true;
_db_select(_critter_db_handle);
}
if (artReadList(path, &(gArtListDescriptions[objectType].fileNames), &(gArtListDescriptions[objectType].fileNamesLength)) != 0) {
debugPrint("art_read_lst failed in art_init\n");
if (critterDbSelected) {
_db_select(oldDb);
}
cacheFree(&gArtCache);
return -1;
}
if (objectType == OBJ_TYPE_CRITTER) {
critterDbSelected = false;
_db_select(oldDb);
}
}
_anon_alias = (int*)internal_malloc(sizeof(*_anon_alias) * gArtListDescriptions[OBJ_TYPE_CRITTER].fileNamesLength);
@@ -698,12 +716,6 @@ static int artReadList(const char* path, char** artListPtr, int* artListSizePtr)
fileClose(stream);
return 0;
err:
fileClose(stream);
return -1;
}
// 0x419760
@@ -854,8 +866,8 @@ bool artExists(int fid)
int oldDb = -1;
if (FID_TYPE(fid) == OBJ_TYPE_CRITTER) {
oldDb = _db_current(1);
_db_current(_critter_db_handle);
oldDb = _db_current();
_db_select(_critter_db_handle);
}
char* filePath = artBuildFilePath(fid);
@@ -867,27 +879,38 @@ bool artExists(int fid)
}
if (oldDb != -1) {
_db_current(oldDb);
_db_select(oldDb);
}
return result;
}
// NOTE: Exactly the same implementation as `artExists`.
//
// 0x419930
bool _art_fid_valid(int fid)
{
// NOTE: Original Code involves calling some unknown function. Check in debugger in mapper.
bool result = false;
int oldDb = -1;
if (FID_TYPE(fid) == OBJ_TYPE_CRITTER) {
oldDb = _db_current();
_db_select(_critter_db_handle);
}
char* filePath = artBuildFilePath(fid);
if (filePath == NULL) {
return false;
if (filePath != NULL) {
int fileSize;
if (dbGetFileSize(filePath, &fileSize) != -1) {
result = true;
}
}
int fileSize;
if (dbGetFileSize(filePath, &fileSize) == -1) {
return false;
if (oldDb != -1) {
_db_select(oldDb);
}
return true;
return result;
}
// 0x419998
@@ -937,8 +960,8 @@ static int artCacheGetFileSizeImpl(int fid, int* sizePtr)
int result = -1;
if (FID_TYPE(fid) == OBJ_TYPE_CRITTER) {
oldDb = _db_current(1);
_db_current(_critter_db_handle);
oldDb = _db_current();
_db_select(_critter_db_handle);
}
char* artFilePath = artBuildFilePath(fid);
@@ -973,7 +996,7 @@ static int artCacheGetFileSizeImpl(int fid, int* sizePtr)
}
if (oldDb != -1) {
_db_current(oldDb);
_db_select(oldDb);
}
return result;
@@ -986,8 +1009,8 @@ static int artCacheReadDataImpl(int fid, int* sizePtr, unsigned char* data)
int result = -1;
if (FID_TYPE(fid) == OBJ_TYPE_CRITTER) {
oldDb = _db_current(1);
_db_current(_critter_db_handle);
oldDb = _db_current();
_db_select(_critter_db_handle);
}
char* artFileName = artBuildFilePath(fid);
@@ -1021,7 +1044,7 @@ static int artCacheReadDataImpl(int fid, int* sizePtr, unsigned char* data)
}
if (oldDb != -1) {
_db_current(oldDb);
_db_select(oldDb);
}
return result;
@@ -1207,3 +1230,43 @@ int artWrite(const char* path, unsigned char* data)
fileClose(stream);
return 0;
}
FrmImage::FrmImage()
{
_key = nullptr;
_data = nullptr;
_width = 0;
_height = 0;
}
FrmImage::~FrmImage()
{
unlock();
}
bool FrmImage::lock(unsigned int fid)
{
if (isLocked()) {
return false;
}
_data = artLockFrameDataReturningSize(fid, &_key, &_width, &_height);
if (!_data) {
return false;
}
return true;
}
void FrmImage::unlock()
{
if (isLocked()) {
artUnlock(_key);
_key = nullptr;
_data = nullptr;
_width = 0;
_height = 0;
}
}
} // namespace fallout

View File

@@ -8,6 +8,8 @@
#include "platform_compat.h"
#include "proto_types.h"
namespace fallout {
typedef enum Head {
HEAD_INVALID,
HEAD_MARCUS,
@@ -149,4 +151,26 @@ int buildFid(int objectType, int frmId, int animType, int a4, int rotation);
int artRead(const char* path, unsigned char* data);
int artWrite(const char* path, unsigned char* data);
class FrmImage {
public:
FrmImage();
~FrmImage();
bool isLocked() const { return _key != nullptr; }
bool lock(unsigned int fid);
void unlock();
int getWidth() const { return _width; }
int getHeight() const { return _height; }
unsigned char* getData() const { return _data; }
private:
CacheEntry* _key;
unsigned char* _data;
int _width;
int _height;
};
} // namespace fallout
#endif

View File

@@ -1,14 +1,16 @@
#include "audio.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "db.h"
#include "debug.h"
#include "memory_manager.h"
#include "pointer_registry.h"
#include "sound.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
namespace fallout {
static bool _defaultCompressionFunc(char* filePath);
static int audioSoundDecoderReadHandler(int fileHandle, void* buf, unsigned int size);
@@ -252,3 +254,5 @@ void audioExit()
gAudioListLength = 0;
gAudioList = NULL;
}
} // namespace fallout

View File

@@ -3,6 +3,8 @@
#include "audio_file.h"
namespace fallout {
int audioOpen(const char* fname, int mode, ...);
int audioClose(int fileHandle);
int audioRead(int fileHandle, void* buffer, unsigned int size);
@@ -13,4 +15,6 @@ int audioWrite(int handle, const void* buf, unsigned int size);
int audioInit(AudioFileIsCompressedProc* isCompressedProc);
void audioExit();
} // namespace fallout
#endif /* AUDIO_H */

View File

@@ -6,6 +6,8 @@
#include <SDL.h>
namespace fallout {
#define AUDIO_ENGINE_SOUND_BUFFERS 8
struct AudioEngineSoundBuffer {
@@ -430,3 +432,5 @@ bool audioEngineSoundBufferGetStatus(int soundBufferIndex, unsigned int* statusP
return true;
}
} // namespace fallout

View File

@@ -1,6 +1,8 @@
#ifndef AUDIO_ENGINE_H
#define AUDIO_ENGINE_H
namespace fallout {
#define AUDIO_ENGINE_SOUND_BUFFER_LOCK_FROM_WRITE_POS 0x00000001
#define AUDIO_ENGINE_SOUND_BUFFER_LOCK_ENTIRE_BUFFER 0x00000002
@@ -26,4 +28,6 @@ bool audioEngineSoundBufferLock(int soundBufferIndex, unsigned int writePos, uns
bool audioEngineSoundBufferUnlock(int soundBufferIndex, void* audioPtr1, unsigned int audioBytes1, void* audioPtr2, unsigned int audioBytes2);
bool audioEngineSoundBufferGetStatus(int soundBufferIndex, unsigned int* status);
} // namespace fallout
#endif /* AUDIO_ENGINE_H */

View File

@@ -1,14 +1,16 @@
#include "audio_file.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "debug.h"
#include "memory_manager.h"
#include "platform_compat.h"
#include "pointer_registry.h"
#include "sound.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
namespace fallout {
static bool _defaultCompressionFunc__(char* filePath);
static int audioFileSoundDecoderReadHandler(int fileHandle, void* buffer, unsigned int size);
@@ -103,7 +105,7 @@ int audioFileOpen(const char* fname, int flags, ...)
audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->fileHandle, &(audioFile->field_14), &(audioFile->field_10), &(audioFile->fileSize));
audioFile->fileSize *= 2;
} else {
audioFile->fileSize = compat_filelength(fileno(stream));
audioFile->fileSize = getFileSize(stream);
}
audioFile->position = 0;
@@ -249,3 +251,5 @@ void audioFileExit()
gAudioFileListLength = 0;
gAudioFileList = NULL;
}
} // namespace fallout

View File

@@ -3,6 +3,8 @@
#include "sound_decoder.h"
namespace fallout {
typedef enum AudioFileFlags {
AUDIO_FILE_IN_USE = 0x01,
AUDIO_FILE_COMPRESSED = 0x02,
@@ -30,4 +32,6 @@ int audioFileWrite(int handle, const void* buf, unsigned int size);
int audioFileInit(AudioFileIsCompressedProc* isCompressedProc);
void audioFileExit();
} // namespace fallout
#endif /* AUDIO_FILE_H */

View File

@@ -1,5 +1,10 @@
#include "automap.h"
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include "art.h"
#include "color.h"
#include "config.h"
@@ -20,10 +25,7 @@
#include "text_font.h"
#include "window_manager.h"
#include <stdio.h>
#include <string.h>
#include <algorithm>
namespace fallout {
#define AUTOMAP_OFFSET_COUNT (AUTOMAP_MAP_COUNT * ELEVATION_COUNT)
@@ -66,7 +68,7 @@ static const int _defam[AUTOMAP_MAP_COUNT][ELEVATION_COUNT] = {
};
// 0x41B560
static const int _displayMapList[AUTOMAP_MAP_COUNT] = {
static int _displayMapList[AUTOMAP_MAP_COUNT] = {
-1,
-1,
-1,
@@ -299,15 +301,10 @@ void automapShow(bool isInGame, bool isUsingScanner)
int frmIds[AUTOMAP_FRM_COUNT];
memcpy(frmIds, gAutomapFrmIds, sizeof(gAutomapFrmIds));
unsigned char* frmData[AUTOMAP_FRM_COUNT];
CacheEntry* frmHandle[AUTOMAP_FRM_COUNT];
FrmImage frmImages[AUTOMAP_FRM_COUNT];
for (int index = 0; index < AUTOMAP_FRM_COUNT; index++) {
int fid = buildFid(OBJ_TYPE_INTERFACE, frmIds[index], 0, 0, 0);
frmData[index] = artLockFrameData(fid, 0, 0, &(frmHandle[index]));
if (frmData[index] == NULL) {
while (--index >= 0) {
artUnlock(frmHandle[index]);
}
if (!frmImages[index].lock(fid)) {
return;
}
}
@@ -327,17 +324,53 @@ void automapShow(bool isInGame, bool isUsingScanner)
int automapWindowY = (screenGetHeight() - AUTOMAP_WINDOW_HEIGHT) / 2;
int window = windowCreate(automapWindowX, automapWindowY, AUTOMAP_WINDOW_WIDTH, AUTOMAP_WINDOW_HEIGHT, color, WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
int scannerBtn = buttonCreate(window, 111, 454, 15, 16, -1, -1, -1, KEY_LOWERCASE_S, frmData[AUTOMAP_FRM_BUTTON_UP], frmData[AUTOMAP_FRM_BUTTON_DOWN], NULL, BUTTON_FLAG_TRANSPARENT);
int scannerBtn = buttonCreate(window,
111,
454,
15,
16,
-1,
-1,
-1,
KEY_LOWERCASE_S,
frmImages[AUTOMAP_FRM_BUTTON_UP].getData(),
frmImages[AUTOMAP_FRM_BUTTON_DOWN].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (scannerBtn != -1) {
buttonSetCallbacks(scannerBtn, _gsound_red_butt_press, _gsound_red_butt_release);
}
int cancelBtn = buttonCreate(window, 277, 454, 15, 16, -1, -1, -1, KEY_ESCAPE, frmData[AUTOMAP_FRM_BUTTON_UP], frmData[AUTOMAP_FRM_BUTTON_DOWN], NULL, BUTTON_FLAG_TRANSPARENT);
int cancelBtn = buttonCreate(window,
277,
454,
15,
16,
-1,
-1,
-1,
KEY_ESCAPE,
frmImages[AUTOMAP_FRM_BUTTON_UP].getData(),
frmImages[AUTOMAP_FRM_BUTTON_DOWN].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (cancelBtn != -1) {
buttonSetCallbacks(cancelBtn, _gsound_red_butt_press, _gsound_red_butt_release);
}
int switchBtn = buttonCreate(window, 457, 340, 42, 74, -1, -1, KEY_LOWERCASE_L, KEY_LOWERCASE_H, frmData[AUTOMAP_FRM_SWITCH_UP], frmData[AUTOMAP_FRM_SWITCH_DOWN], NULL, BUTTON_FLAG_TRANSPARENT | BUTTON_FLAG_0x01);
int switchBtn = buttonCreate(window,
457,
340,
42,
74,
-1,
-1,
KEY_LOWERCASE_L,
KEY_LOWERCASE_H,
frmImages[AUTOMAP_FRM_SWITCH_UP].getData(),
frmImages[AUTOMAP_FRM_SWITCH_DOWN].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT | BUTTON_FLAG_0x01);
if (switchBtn != -1) {
buttonSetCallbacks(switchBtn, _gsound_toggle_butt_press_, _gsound_toggle_butt_press_);
}
@@ -358,7 +391,7 @@ void automapShow(bool isInGame, bool isUsingScanner)
gAutomapFlags |= AUTOMAP_WITH_SCANNER;
}
automapRenderInMapWindow(window, elevation, frmData[AUTOMAP_FRM_BACKGROUND], gAutomapFlags);
automapRenderInMapWindow(window, elevation, frmImages[AUTOMAP_FRM_BACKGROUND].getData(), gAutomapFlags);
bool isoWasEnabled = isoDisable();
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
@@ -442,7 +475,7 @@ void automapShow(bool isInGame, bool isUsingScanner)
}
if (needsRefresh) {
automapRenderInMapWindow(window, elevation, frmData[AUTOMAP_FRM_BACKGROUND], gAutomapFlags);
automapRenderInMapWindow(window, elevation, frmImages[AUTOMAP_FRM_BACKGROUND].getData(), gAutomapFlags);
needsRefresh = false;
}
}
@@ -453,10 +486,6 @@ void automapShow(bool isInGame, bool isUsingScanner)
windowDestroy(window);
fontSetCurrent(oldFont);
for (int index = 0; index < AUTOMAP_FRM_COUNT; index++) {
artUnlock(frmHandle[index]);
}
}
// Renders automap in Map window.
@@ -1153,3 +1182,12 @@ int automapGetHeader(AutomapHeader** automapHeaderPtr)
return 0;
}
void automapSetDisplayMap(int map, bool available)
{
if (map >= 0 && map < AUTOMAP_MAP_COUNT) {
_displayMapList[map] = available ? 0 : -1;
}
}
} // namespace fallout

View File

@@ -4,6 +4,8 @@
#include "db.h"
#include "map_defs.h"
namespace fallout {
#define AUTOMAP_DB ("AUTOMAP.DB")
#define AUTOMAP_TMP ("AUTOMAP.TMP")
@@ -54,4 +56,8 @@ int automapRenderInPipboyWindow(int win, int map, int elevation);
int automapSaveCurrent();
int automapGetHeader(AutomapHeader** automapHeaderPtr);
void automapSetDisplayMap(int map, bool available);
} // namespace fallout
#endif /* AUTOMAP_H */

View File

@@ -11,6 +11,8 @@
static HANDLE gInterplayGenericAutorunMutex;
#endif
namespace fallout {
// 0x4139C0
bool autorunMutexCreate()
{
@@ -34,3 +36,5 @@ void autorunMutexClose()
}
#endif
}
} // namespace fallout

View File

@@ -1,7 +1,11 @@
#ifndef AUTORUN_H
#define AUTORUN_H
namespace fallout {
bool autorunMutexCreate();
void autorunMutexClose();
} // namespace fallout
#endif /* AUTORUN_H */

View File

@@ -1,14 +1,16 @@
#include "cache.h"
#include "debug.h"
#include "memory.h"
#include "sound.h"
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "debug.h"
#include "memory.h"
#include "sound.h"
namespace fallout {
// The initial number of cache entries in new cache.
#define CACHE_ENTRIES_INITIAL_CAPACITY (100)
@@ -613,3 +615,5 @@ static int cacheEntriesCompareByMostRecentHit(const void* a1, const void* a2)
return 0;
}
}
} // namespace fallout

View File

@@ -3,6 +3,8 @@
#include "heap.h"
namespace fallout {
#define INVALID_CACHE_ENTRY ((CacheEntry*)-1)
typedef enum CacheEntryFlags {
@@ -66,4 +68,6 @@ bool cacheUnlock(Cache* cache, CacheEntry* cacheEntry);
bool cacheFlush(Cache* cache);
bool cachePrintStats(Cache* cache, char* dest);
} // namespace fallout
#endif /* CACHE_H */

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,8 @@
#include "db.h"
namespace fallout {
extern int gCharacterEditorRemainingCharacterPoints;
int characterEditorShow(bool isCreationMode);
@@ -13,4 +15,6 @@ int characterEditorSave(File* stream);
int characterEditorLoad(File* stream);
void characterEditorReset();
} // namespace fallout
#endif /* CHARACTER_EDITOR_H */

View File

@@ -1,5 +1,11 @@
#include "character_selector.h"
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include "art.h"
#include "character_editor.h"
#include "color.h"
@@ -9,6 +15,7 @@
#include "debug.h"
#include "draw.h"
#include "game.h"
#include "game_config.h"
#include "game_sound.h"
#include "memory.h"
#include "message.h"
@@ -17,14 +24,14 @@
#include "palette.h"
#include "platform_compat.h"
#include "proto.h"
#include "sfall_config.h"
#include "skill.h"
#include "stat.h"
#include "text_font.h"
#include "trait.h"
#include "window_manager.h"
#include <stdio.h>
#include <string.h>
namespace fallout {
#define CS_WINDOW_WIDTH (640)
#define CS_WINDOW_HEIGHT (480)
@@ -57,12 +64,28 @@
#define CS_WINDOW_SECONDARY_STAT_MID_X (379)
#define CS_WINDOW_BIO_X (438)
typedef enum PremadeCharacter {
PREMADE_CHARACTER_NARG,
PREMADE_CHARACTER_CHITSA,
PREMADE_CHARACTER_MINGUN,
PREMADE_CHARACTER_COUNT,
} PremadeCharacter;
typedef struct PremadeCharacterDescription {
char fileName[20];
int face;
char field_18[20];
} PremadeCharacterDescription;
static bool characterSelectorWindowInit();
static void characterSelectorWindowFree();
static bool characterSelectorWindowRefresh();
static bool characterSelectorWindowRenderFace();
static bool characterSelectorWindowRenderStats();
static bool characterSelectorWindowRenderBio();
static bool characterSelectorWindowFatalError(bool result);
static void premadeCharactersLocalizePath(char* path);
// 0x51C84C
static int gCurrentPremadeCharacter = PREMADE_CHARACTER_NARG;
@@ -75,7 +98,7 @@ static PremadeCharacterDescription gPremadeCharacterDescriptions[PREMADE_CHARACT
};
// 0x51C8D4
static const int gPremadeCharacterCount = PREMADE_CHARACTER_COUNT;
static int gPremadeCharacterCount = PREMADE_CHARACTER_COUNT;
// 0x51C7F8
static int gCharacterSelectorWindow = -1;
@@ -89,92 +112,35 @@ static unsigned char* gCharacterSelectorBackground = NULL;
// 0x51C804
static int gCharacterSelectorWindowPreviousButton = -1;
// 0x51C808
static CacheEntry* gCharacterSelectorWindowPreviousButtonUpFrmHandle = NULL;
// 0x51C80C
static CacheEntry* gCharacterSelectorWindowPreviousButtonDownFrmHandle = NULL;
// 0x51C810
static int gCharacterSelectorWindowNextButton = -1;
// 0x51C814
static CacheEntry* gCharacterSelectorWindowNextButtonUpFrmHandle = NULL;
// 0x51C818
static CacheEntry* gCharacterSelectorWindowNextButtonDownFrmHandle = NULL;
// 0x51C81C
static int gCharacterSelectorWindowTakeButton = -1;
// 0x51C820
static CacheEntry* gCharacterSelectorWindowTakeButtonUpFrmHandle = NULL;
// 0x51C824
static CacheEntry* gCharacterSelectorWindowTakeButtonDownFrmHandle = NULL;
// 0x51C828
static int gCharacterSelectorWindowModifyButton = -1;
// 0x51C82C
static CacheEntry* gCharacterSelectorWindowModifyButtonUpFrmHandle = NULL;
// 0x51C830
static CacheEntry* gCharacterSelectorWindowModifyButtonDownFrmHandle = NULL;
// 0x51C834
static int gCharacterSelectorWindowCreateButton = -1;
// 0x51C838
static CacheEntry* gCharacterSelectorWindowCreateButtonUpFrmHandle = NULL;
// 0x51C83C
static CacheEntry* gCharacterSelectorWindowCreateButtonDownFrmHandle = NULL;
// 0x51C840
static int gCharacterSelectorWindowBackButton = -1;
// 0x51C844
static CacheEntry* gCharacterSelectorWindowBackButtonUpFrmHandle = NULL;
static FrmImage _takeButtonNormalFrmImage;
static FrmImage _takeButtonPressedFrmImage;
static FrmImage _modifyButtonNormalFrmImage;
static FrmImage _modifyButtonPressedFrmImage;
static FrmImage _createButtonNormalFrmImage;
static FrmImage _createButtonPressedFrmImage;
static FrmImage _backButtonNormalFrmImage;
static FrmImage _backButtonPressedFrmImage;
static FrmImage _nextButtonNormalFrmImage;
static FrmImage _nextButtonPressedFrmImage;
static FrmImage _previousButtonNormalFrmImage;
static FrmImage _previousButtonPressedFrmImage;
// 0x51C848
static CacheEntry* gCharacterSelectorWindowBackButtonDownFrmHandle = NULL;
// 0x667764
static unsigned char* gCharacterSelectorWindowTakeButtonUpFrmData;
// 0x667768
static unsigned char* gCharacterSelectorWindowModifyButtonDownFrmData;
// 0x66776C
static unsigned char* gCharacterSelectorWindowBackButtonUpFrmData;
// 0x667770
static unsigned char* gCharacterSelectorWindowCreateButtonUpFrmData;
// 0x667774
static unsigned char* gCharacterSelectorWindowModifyButtonUpFrmData;
// 0x667778
static unsigned char* gCharacterSelectorWindowBackButtonDownFrmData;
// 0x66777C
static unsigned char* gCharacterSelectorWindowCreateButtonDownFrmData;
// 0x667780
static unsigned char* gCharacterSelectorWindowTakeButtonDownFrmData;
// 0x667784
static unsigned char* gCharacterSelectorWindowNextButtonDownFrmData;
// 0x667788
static unsigned char* gCharacterSelectorWindowNextButtonUpFrmData;
// 0x66778C
static unsigned char* gCharacterSelectorWindowPreviousButtonUpFrmData;
// 0x667790
static unsigned char* gCharacterSelectorWindowPreviousButtonDownFrmData;
static std::vector<PremadeCharacterDescription> gCustomPremadeCharacterDescriptions;
// 0x4A71D0
int characterSelectorOpen()
@@ -221,6 +187,8 @@ int characterSelectorOpen()
if (characterEditorShow(1) == 0) {
rc = 2;
done = true;
} else {
characterSelectorWindowRefresh();
}
break;
@@ -229,6 +197,8 @@ int characterSelectorOpen()
if (!characterEditorShow(1)) {
rc = 2;
done = true;
} else {
characterSelectorWindowRefresh();
}
break;
@@ -279,9 +249,6 @@ int characterSelectorOpen()
// 0x4A7468
static bool characterSelectorWindowInit()
{
int backgroundFid;
unsigned char* backgroundFrmData;
if (gCharacterSelectorWindow != -1) {
return false;
}
@@ -290,22 +257,21 @@ static bool characterSelectorWindowInit()
int characterSelectorWindowY = (screenGetHeight() - CS_WINDOW_HEIGHT) / 2;
gCharacterSelectorWindow = windowCreate(characterSelectorWindowX, characterSelectorWindowY, CS_WINDOW_WIDTH, CS_WINDOW_HEIGHT, _colorTable[0], 0);
if (gCharacterSelectorWindow == -1) {
goto err;
return characterSelectorWindowFatalError(false);
}
gCharacterSelectorWindowBuffer = windowGetBuffer(gCharacterSelectorWindow);
if (gCharacterSelectorWindowBuffer == NULL) {
goto err;
return characterSelectorWindowFatalError(false);
}
CacheEntry* backgroundFrmHandle;
backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 174, 0, 0, 0);
backgroundFrmData = artLockFrameData(backgroundFid, 0, 0, &backgroundFrmHandle);
if (backgroundFrmData == NULL) {
goto err;
FrmImage backgroundFrmImage;
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 174, 0, 0, 0);
if (!backgroundFrmImage.lock(backgroundFid)) {
return characterSelectorWindowFatalError(false);
}
blitBufferToBuffer(backgroundFrmData,
blitBufferToBuffer(backgroundFrmImage.getData(),
CS_WINDOW_WIDTH,
CS_WINDOW_HEIGHT,
CS_WINDOW_WIDTH,
@@ -314,30 +280,28 @@ static bool characterSelectorWindowInit()
gCharacterSelectorBackground = (unsigned char*)internal_malloc(CS_WINDOW_BACKGROUND_WIDTH * CS_WINDOW_BACKGROUND_HEIGHT);
if (gCharacterSelectorBackground == NULL)
goto err;
return characterSelectorWindowFatalError(false);
blitBufferToBuffer(backgroundFrmData + CS_WINDOW_WIDTH * CS_WINDOW_BACKGROUND_Y + CS_WINDOW_BACKGROUND_X,
blitBufferToBuffer(backgroundFrmImage.getData() + CS_WINDOW_WIDTH * CS_WINDOW_BACKGROUND_Y + CS_WINDOW_BACKGROUND_X,
CS_WINDOW_BACKGROUND_WIDTH,
CS_WINDOW_BACKGROUND_HEIGHT,
CS_WINDOW_WIDTH,
gCharacterSelectorBackground,
CS_WINDOW_BACKGROUND_WIDTH);
artUnlock(backgroundFrmHandle);
backgroundFrmImage.unlock();
int fid;
// Setup "Previous" button.
fid = buildFid(OBJ_TYPE_INTERFACE, 122, 0, 0, 0);
gCharacterSelectorWindowPreviousButtonUpFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowPreviousButtonUpFrmHandle);
if (gCharacterSelectorWindowPreviousButtonUpFrmData == NULL) {
goto err;
if (!_previousButtonNormalFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
fid = buildFid(OBJ_TYPE_INTERFACE, 123, 0, 0, 0);
gCharacterSelectorWindowPreviousButtonDownFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowPreviousButtonDownFrmHandle);
if (gCharacterSelectorWindowPreviousButtonDownFrmData == NULL) {
goto err;
if (!_previousButtonPressedFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
gCharacterSelectorWindowPreviousButton = buttonCreate(gCharacterSelectorWindow,
@@ -349,27 +313,25 @@ static bool characterSelectorWindowInit()
-1,
-1,
500,
gCharacterSelectorWindowPreviousButtonUpFrmData,
gCharacterSelectorWindowPreviousButtonDownFrmData,
_previousButtonNormalFrmImage.getData(),
_previousButtonPressedFrmImage.getData(),
NULL,
0);
if (gCharacterSelectorWindowPreviousButton == -1) {
goto err;
return characterSelectorWindowFatalError(false);
}
buttonSetCallbacks(gCharacterSelectorWindowPreviousButton, _gsound_med_butt_press, _gsound_med_butt_release);
// Setup "Next" button.
fid = buildFid(OBJ_TYPE_INTERFACE, 124, 0, 0, 0);
gCharacterSelectorWindowNextButtonUpFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowNextButtonUpFrmHandle);
if (gCharacterSelectorWindowNextButtonUpFrmData == NULL) {
goto err;
if (!_nextButtonNormalFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
fid = buildFid(OBJ_TYPE_INTERFACE, 125, 0, 0, 0);
gCharacterSelectorWindowNextButtonDownFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowNextButtonDownFrmHandle);
if (gCharacterSelectorWindowNextButtonDownFrmData == NULL) {
goto err;
if (!_nextButtonPressedFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
gCharacterSelectorWindowNextButton = buttonCreate(gCharacterSelectorWindow,
@@ -381,27 +343,25 @@ static bool characterSelectorWindowInit()
-1,
-1,
501,
gCharacterSelectorWindowNextButtonUpFrmData,
gCharacterSelectorWindowNextButtonDownFrmData,
_nextButtonNormalFrmImage.getData(),
_nextButtonPressedFrmImage.getData(),
NULL,
0);
if (gCharacterSelectorWindowNextButton == -1) {
goto err;
return characterSelectorWindowFatalError(false);
}
buttonSetCallbacks(gCharacterSelectorWindowNextButton, _gsound_med_butt_press, _gsound_med_butt_release);
// Setup "Take" button.
fid = buildFid(OBJ_TYPE_INTERFACE, 8, 0, 0, 0);
gCharacterSelectorWindowTakeButtonUpFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowTakeButtonUpFrmHandle);
if (gCharacterSelectorWindowTakeButtonUpFrmData == NULL) {
goto err;
if (!_takeButtonNormalFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
fid = buildFid(OBJ_TYPE_INTERFACE, 9, 0, 0, 0);
gCharacterSelectorWindowTakeButtonDownFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowTakeButtonDownFrmHandle);
if (gCharacterSelectorWindowTakeButtonDownFrmData == NULL) {
goto err;
if (!_takeButtonPressedFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
gCharacterSelectorWindowTakeButton = buttonCreate(gCharacterSelectorWindow,
@@ -413,26 +373,24 @@ static bool characterSelectorWindowInit()
-1,
-1,
KEY_LOWERCASE_T,
gCharacterSelectorWindowTakeButtonUpFrmData,
gCharacterSelectorWindowTakeButtonDownFrmData,
_takeButtonNormalFrmImage.getData(),
_takeButtonPressedFrmImage.getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (gCharacterSelectorWindowTakeButton == -1) {
goto err;
return characterSelectorWindowFatalError(false);
}
buttonSetCallbacks(gCharacterSelectorWindowTakeButton, _gsound_red_butt_press, _gsound_red_butt_release);
// Setup "Modify" button.
fid = buildFid(OBJ_TYPE_INTERFACE, 8, 0, 0, 0);
gCharacterSelectorWindowModifyButtonUpFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowModifyButtonUpFrmHandle);
if (gCharacterSelectorWindowModifyButtonUpFrmData == NULL)
goto err;
if (!_modifyButtonNormalFrmImage.lock(fid))
return characterSelectorWindowFatalError(false);
fid = buildFid(OBJ_TYPE_INTERFACE, 9, 0, 0, 0);
gCharacterSelectorWindowModifyButtonDownFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowModifyButtonDownFrmHandle);
if (gCharacterSelectorWindowModifyButtonDownFrmData == NULL) {
goto err;
if (!_modifyButtonPressedFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
gCharacterSelectorWindowModifyButton = buttonCreate(gCharacterSelectorWindow,
@@ -444,27 +402,25 @@ static bool characterSelectorWindowInit()
-1,
-1,
KEY_LOWERCASE_M,
gCharacterSelectorWindowModifyButtonUpFrmData,
gCharacterSelectorWindowModifyButtonDownFrmData,
_modifyButtonNormalFrmImage.getData(),
_modifyButtonPressedFrmImage.getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (gCharacterSelectorWindowModifyButton == -1) {
goto err;
return characterSelectorWindowFatalError(false);
}
buttonSetCallbacks(gCharacterSelectorWindowModifyButton, _gsound_red_butt_press, _gsound_red_butt_release);
// Setup "Create" button.
fid = buildFid(OBJ_TYPE_INTERFACE, 8, 0, 0, 0);
gCharacterSelectorWindowCreateButtonUpFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowCreateButtonUpFrmHandle);
if (gCharacterSelectorWindowCreateButtonUpFrmData == NULL) {
goto err;
if (!_createButtonNormalFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
fid = buildFid(OBJ_TYPE_INTERFACE, 9, 0, 0, 0);
gCharacterSelectorWindowCreateButtonDownFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowCreateButtonDownFrmHandle);
if (gCharacterSelectorWindowCreateButtonDownFrmData == NULL) {
goto err;
if (!_createButtonPressedFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
gCharacterSelectorWindowCreateButton = buttonCreate(gCharacterSelectorWindow,
@@ -476,27 +432,25 @@ static bool characterSelectorWindowInit()
-1,
-1,
KEY_LOWERCASE_C,
gCharacterSelectorWindowCreateButtonUpFrmData,
gCharacterSelectorWindowCreateButtonDownFrmData,
_createButtonNormalFrmImage.getData(),
_createButtonPressedFrmImage.getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (gCharacterSelectorWindowCreateButton == -1) {
goto err;
return characterSelectorWindowFatalError(false);
}
buttonSetCallbacks(gCharacterSelectorWindowCreateButton, _gsound_red_butt_press, _gsound_red_butt_release);
// Setup "Back" button.
fid = buildFid(OBJ_TYPE_INTERFACE, 8, 0, 0, 0);
gCharacterSelectorWindowBackButtonUpFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowBackButtonUpFrmHandle);
if (gCharacterSelectorWindowBackButtonUpFrmData == NULL) {
goto err;
if (!_backButtonNormalFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
fid = buildFid(OBJ_TYPE_INTERFACE, 9, 0, 0, 0);
gCharacterSelectorWindowBackButtonDownFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowBackButtonDownFrmHandle);
if (gCharacterSelectorWindowBackButtonDownFrmData == NULL) {
goto err;
if (!_backButtonPressedFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
gCharacterSelectorWindowBackButton = buttonCreate(gCharacterSelectorWindow,
@@ -508,12 +462,12 @@ static bool characterSelectorWindowInit()
-1,
-1,
KEY_ESCAPE,
gCharacterSelectorWindowBackButtonUpFrmData,
gCharacterSelectorWindowBackButtonDownFrmData,
_backButtonNormalFrmImage.getData(),
_backButtonPressedFrmImage.getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (gCharacterSelectorWindowBackButton == -1) {
goto err;
return characterSelectorWindowFatalError(false);
}
buttonSetCallbacks(gCharacterSelectorWindowBackButton, _gsound_red_butt_press, _gsound_red_butt_release);
@@ -523,16 +477,10 @@ static bool characterSelectorWindowInit()
windowRefresh(gCharacterSelectorWindow);
if (!characterSelectorWindowRefresh()) {
goto err;
return characterSelectorWindowFatalError(false);
}
return true;
err:
characterSelectorWindowFree();
return false;
}
// 0x4A7AD4
@@ -547,102 +495,48 @@ static void characterSelectorWindowFree()
gCharacterSelectorWindowPreviousButton = -1;
}
if (gCharacterSelectorWindowPreviousButtonDownFrmData != NULL) {
artUnlock(gCharacterSelectorWindowPreviousButtonDownFrmHandle);
gCharacterSelectorWindowPreviousButtonDownFrmHandle = NULL;
gCharacterSelectorWindowPreviousButtonDownFrmData = NULL;
}
if (gCharacterSelectorWindowPreviousButtonUpFrmData != NULL) {
artUnlock(gCharacterSelectorWindowPreviousButtonUpFrmHandle);
gCharacterSelectorWindowPreviousButtonUpFrmHandle = NULL;
gCharacterSelectorWindowPreviousButtonUpFrmData = NULL;
}
_previousButtonNormalFrmImage.unlock();
_previousButtonPressedFrmImage.unlock();
if (gCharacterSelectorWindowNextButton != -1) {
buttonDestroy(gCharacterSelectorWindowNextButton);
gCharacterSelectorWindowNextButton = -1;
}
if (gCharacterSelectorWindowNextButtonDownFrmData != NULL) {
artUnlock(gCharacterSelectorWindowNextButtonDownFrmHandle);
gCharacterSelectorWindowNextButtonDownFrmHandle = NULL;
gCharacterSelectorWindowNextButtonDownFrmData = NULL;
}
if (gCharacterSelectorWindowNextButtonUpFrmData != NULL) {
artUnlock(gCharacterSelectorWindowNextButtonUpFrmHandle);
gCharacterSelectorWindowNextButtonUpFrmHandle = NULL;
gCharacterSelectorWindowNextButtonUpFrmData = NULL;
}
_nextButtonNormalFrmImage.unlock();
_nextButtonPressedFrmImage.unlock();
if (gCharacterSelectorWindowTakeButton != -1) {
buttonDestroy(gCharacterSelectorWindowTakeButton);
gCharacterSelectorWindowTakeButton = -1;
}
if (gCharacterSelectorWindowTakeButtonDownFrmData != NULL) {
artUnlock(gCharacterSelectorWindowTakeButtonDownFrmHandle);
gCharacterSelectorWindowTakeButtonDownFrmHandle = NULL;
gCharacterSelectorWindowTakeButtonDownFrmData = NULL;
}
if (gCharacterSelectorWindowTakeButtonUpFrmData != NULL) {
artUnlock(gCharacterSelectorWindowTakeButtonUpFrmHandle);
gCharacterSelectorWindowTakeButtonUpFrmHandle = NULL;
gCharacterSelectorWindowTakeButtonUpFrmData = NULL;
}
_takeButtonNormalFrmImage.unlock();
_takeButtonPressedFrmImage.unlock();
if (gCharacterSelectorWindowModifyButton != -1) {
buttonDestroy(gCharacterSelectorWindowModifyButton);
gCharacterSelectorWindowModifyButton = -1;
}
if (gCharacterSelectorWindowModifyButtonDownFrmData != NULL) {
artUnlock(gCharacterSelectorWindowModifyButtonDownFrmHandle);
gCharacterSelectorWindowModifyButtonDownFrmHandle = NULL;
gCharacterSelectorWindowModifyButtonDownFrmData = NULL;
}
if (gCharacterSelectorWindowModifyButtonUpFrmData != NULL) {
artUnlock(gCharacterSelectorWindowModifyButtonUpFrmHandle);
gCharacterSelectorWindowModifyButtonUpFrmHandle = NULL;
gCharacterSelectorWindowModifyButtonUpFrmData = NULL;
}
_modifyButtonNormalFrmImage.unlock();
_modifyButtonPressedFrmImage.unlock();
if (gCharacterSelectorWindowCreateButton != -1) {
buttonDestroy(gCharacterSelectorWindowCreateButton);
gCharacterSelectorWindowCreateButton = -1;
}
if (gCharacterSelectorWindowCreateButtonDownFrmData != NULL) {
artUnlock(gCharacterSelectorWindowCreateButtonDownFrmHandle);
gCharacterSelectorWindowCreateButtonDownFrmHandle = NULL;
gCharacterSelectorWindowCreateButtonDownFrmData = NULL;
}
if (gCharacterSelectorWindowCreateButtonUpFrmData != NULL) {
artUnlock(gCharacterSelectorWindowCreateButtonUpFrmHandle);
gCharacterSelectorWindowCreateButtonUpFrmHandle = NULL;
gCharacterSelectorWindowCreateButtonUpFrmData = NULL;
}
_createButtonNormalFrmImage.unlock();
_createButtonPressedFrmImage.unlock();
if (gCharacterSelectorWindowBackButton != -1) {
buttonDestroy(gCharacterSelectorWindowBackButton);
gCharacterSelectorWindowBackButton = -1;
}
if (gCharacterSelectorWindowBackButtonDownFrmData != NULL) {
artUnlock(gCharacterSelectorWindowBackButtonDownFrmHandle);
gCharacterSelectorWindowBackButtonDownFrmHandle = NULL;
gCharacterSelectorWindowBackButtonDownFrmData = NULL;
}
if (gCharacterSelectorWindowBackButtonUpFrmData != NULL) {
artUnlock(gCharacterSelectorWindowBackButtonUpFrmHandle);
gCharacterSelectorWindowBackButtonUpFrmHandle = NULL;
gCharacterSelectorWindowBackButtonUpFrmData = NULL;
}
_backButtonNormalFrmImage.unlock();
_backButtonPressedFrmImage.unlock();
if (gCharacterSelectorBackground != NULL) {
internal_free(gCharacterSelectorBackground);
@@ -657,7 +551,9 @@ static void characterSelectorWindowFree()
static bool characterSelectorWindowRefresh()
{
char path[COMPAT_MAX_PATH];
sprintf(path, "%s.gcd", gPremadeCharacterDescriptions[gCurrentPremadeCharacter].fileName);
sprintf(path, "%s.gcd", gCustomPremadeCharacterDescriptions[gCurrentPremadeCharacter].fileName);
premadeCharactersLocalizePath(path);
if (_proto_dude_init(path) == -1) {
debugPrint("\n ** Error in dude init! **\n");
return false;
@@ -687,18 +583,17 @@ static bool characterSelectorWindowRenderFace()
{
bool success = false;
CacheEntry* faceFrmHandle;
int faceFid = buildFid(OBJ_TYPE_INTERFACE, gPremadeCharacterDescriptions[gCurrentPremadeCharacter].face, 0, 0, 0);
Art* frm = artLock(faceFid, &faceFrmHandle);
if (frm != NULL) {
unsigned char* data = artGetFrameData(frm, 0, 0);
FrmImage faceFrmImage;
int faceFid = buildFid(OBJ_TYPE_INTERFACE, gCustomPremadeCharacterDescriptions[gCurrentPremadeCharacter].face, 0, 0, 0);
if (faceFrmImage.lock(faceFid)) {
unsigned char* data = faceFrmImage.getData();
if (data != NULL) {
int width = artGetWidth(frm, 0, 0);
int height = artGetHeight(frm, 0, 0);
int width = faceFrmImage.getWidth();
int height = faceFrmImage.getHeight();
blitBufferToBufferTrans(data, width, height, width, (gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * 23 + 27), CS_WINDOW_WIDTH);
success = true;
}
artUnlock(faceFrmHandle);
faceFrmImage.unlock();
}
return success;
@@ -963,7 +858,8 @@ static bool characterSelectorWindowRenderBio()
fontSetCurrent(101);
char path[COMPAT_MAX_PATH];
sprintf(path, "%s.bio", gPremadeCharacterDescriptions[gCurrentPremadeCharacter].fileName);
sprintf(path, "%s.bio", gCustomPremadeCharacterDescriptions[gCurrentPremadeCharacter].fileName);
premadeCharactersLocalizePath(path);
File* stream = fileOpen(path, "rt");
if (stream != NULL) {
@@ -983,3 +879,124 @@ static bool characterSelectorWindowRenderBio()
return true;
}
// NOTE: Inlined.
//
// 0x4A8BD0
static bool characterSelectorWindowFatalError(bool result)
{
characterSelectorWindowFree();
return result;
}
void premadeCharactersInit()
{
char* fileNamesString;
configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_PREMADE_CHARACTERS_FILE_NAMES_KEY, &fileNamesString);
if (fileNamesString != NULL && *fileNamesString == '\0') {
fileNamesString = NULL;
}
char* faceFidsString;
configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_PREMADE_CHARACTERS_FACE_FIDS_KEY, &faceFidsString);
if (faceFidsString != NULL && *faceFidsString == '\0') {
faceFidsString = NULL;
}
if (fileNamesString != NULL && faceFidsString != NULL) {
int fileNamesLength = 0;
for (char* pch = fileNamesString; pch != NULL; pch = strchr(pch + 1, ',')) {
fileNamesLength++;
}
int faceFidsLength = 0;
for (char* pch = faceFidsString; pch != NULL; pch = strchr(pch + 1, ',')) {
faceFidsLength++;
}
int premadeCharactersCount = std::min(fileNamesLength, faceFidsLength);
gCustomPremadeCharacterDescriptions.resize(premadeCharactersCount);
for (int index = 0; index < premadeCharactersCount; index++) {
char* pch;
pch = strchr(fileNamesString, ',');
if (pch != NULL) {
*pch = '\0';
}
if (strlen(fileNamesString) > 11) {
// Sfall fails here.
continue;
}
sprintf(gCustomPremadeCharacterDescriptions[index].fileName, "premade\\%s", fileNamesString);
if (pch != NULL) {
*pch = ',';
}
fileNamesString = pch + 1;
pch = strchr(faceFidsString, ',');
if (pch != NULL) {
*pch = '\0';
}
gCustomPremadeCharacterDescriptions[index].face = atoi(faceFidsString);
if (pch != NULL) {
*pch = ',';
}
faceFidsString = pch + 1;
gCustomPremadeCharacterDescriptions[index].field_18[0] = '\0';
}
}
if (gCustomPremadeCharacterDescriptions.empty()) {
gCustomPremadeCharacterDescriptions.resize(PREMADE_CHARACTER_COUNT);
for (int index = 0; index < PREMADE_CHARACTER_COUNT; index++) {
strcpy(gCustomPremadeCharacterDescriptions[index].fileName, gPremadeCharacterDescriptions[index].fileName);
gCustomPremadeCharacterDescriptions[index].face = gPremadeCharacterDescriptions[index].face;
strcpy(gCustomPremadeCharacterDescriptions[index].field_18, gPremadeCharacterDescriptions[index].field_18);
}
}
gPremadeCharacterCount = gCustomPremadeCharacterDescriptions.size();
}
void premadeCharactersExit()
{
gCustomPremadeCharacterDescriptions.clear();
}
static void premadeCharactersLocalizePath(char* path)
{
if (compat_strnicmp(path, "premade\\", 8) != 0) {
return;
}
char* language;
if (!configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_LANGUAGE_KEY, &language)) {
return;
}
if (compat_stricmp(language, ENGLISH) == 0) {
return;
}
char localizedPath[COMPAT_MAX_PATH];
strncpy(localizedPath, path, 8);
strcpy(localizedPath + 8, language);
strcpy(localizedPath + 8 + strlen(language), path + 7);
int fileSize;
if (dbGetFileSize(localizedPath, &fileSize) == 0) {
strcpy(path, localizedPath);
}
}
} // namespace fallout

View File

@@ -1,19 +1,13 @@
#ifndef CHARACTER_SELECTOR_H
#define CHARACTER_SELECTOR_H
typedef enum PremadeCharacter {
PREMADE_CHARACTER_NARG,
PREMADE_CHARACTER_CHITSA,
PREMADE_CHARACTER_MINGUN,
PREMADE_CHARACTER_COUNT,
} PremadeCharacter;
typedef struct PremadeCharacterDescription {
char fileName[20];
int face;
char field_18[20];
} PremadeCharacterDescription;
namespace fallout {
int characterSelectorOpen();
void premadeCharactersInit();
void premadeCharactersExit();
} // namespace fallout
#endif /* CHARACTER_SELECTOR_H */

View File

@@ -1,12 +1,14 @@
#include "color.h"
#include "core.h"
#include <math.h>
#include <string.h>
#include <algorithm>
#include "core.h"
namespace fallout {
#define COLOR_PALETTE_STACK_CAPACITY 16
typedef struct ColorPaletteStackEntry {
@@ -679,3 +681,5 @@ void _colorsClose()
gColorPaletteStackSize = 0;
}
} // namespace fallout

View File

@@ -3,6 +3,8 @@
#include "memory_defs.h"
namespace fallout {
typedef const char*(ColorFileNameManger)(const char*);
typedef void(ColorTransitionCallback)();
@@ -40,4 +42,6 @@ bool colorPopColorPalette();
bool _initColors();
void _colorsClose();
} // namespace fallout
#endif /* COLOR_H */

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,8 @@
#include "obj_types.h"
#include "proto_types.h"
namespace fallout {
extern int _combatNumTurns;
extern unsigned int gCombatState;
@@ -17,8 +19,8 @@ void combatExit();
int _find_cid(int a1, int a2, Object** a3, int a4);
int combatLoad(File* stream);
int combatSave(File* stream);
bool _combat_safety_invalidate_weapon(Object* a1, Object* a2, int hitMode, Object* a4, int* a5);
bool _combatTestIncidentalHit(Object* a1, Object* a2, Object* a3, Object* a4);
bool _combat_safety_invalidate_weapon(Object* attacker, Object* weapon, int hitMode, Object* defender, int* safeDistancePtr);
bool _combatTestIncidentalHit(Object* attacker, Object* defender, Object* attackerFriend, Object* weapon);
Object* _combat_whose_turn();
void _combat_data_init(Object* obj);
Object* aiInfoGetFriendlyDead(Object* obj);
@@ -56,9 +58,31 @@ int _combat_explode_scenery(Object* a1, Object* a2);
void _combat_delete_critter(Object* obj);
void _combatKillCritterOutsideCombat(Object* critter_obj, char* msg);
int criticalsGetValue(int killType, int hitLocation, int effect, int dataMember);
void criticalsSetValue(int killType, int hitLocation, int effect, int dataMember, int value);
void criticalsResetValue(int killType, int hitLocation, int effect, int dataMember);
int unarmedGetDamage(int hitMode, int* minDamagePtr, int* maxDamagePtr);
int unarmedGetBonusCriticalChance(int hitMode);
int unarmedGetActionPointCost(int hitMode);
bool unarmedIsPenetrating(int hitMode);
int unarmedGetPunchHitMode(bool isSecondary);
int unarmedGetKickHitMode(bool isSecondary);
bool unarmedIsPenetrating(int hitMode);
bool damageModGetBonusHthDamageFix();
bool damageModGetDisplayBonusDamage();
static inline bool isInCombat()
{
return (gCombatState & COMBAT_STATE_0x01) != 0;
}
static inline bool isUnarmedHitMode(int hitMode)
{
return hitMode == HIT_MODE_PUNCH
|| hitMode == HIT_MODE_KICK
|| (hitMode >= FIRST_ADVANCED_UNARMED_HIT_MODE && hitMode <= LAST_ADVANCED_UNARMED_HIT_MODE);
}
} // namespace fallout
#endif /* COMBAT_H */

View File

@@ -1,5 +1,9 @@
#include "combat_ai.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "actions.h"
#include "animation.h"
#include "art.h"
@@ -30,9 +34,7 @@
#include "text_object.h"
#include "tile.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
namespace fallout {
#define AI_PACKET_CHEM_PRIMARY_DESIRE_COUNT (3)
@@ -99,13 +101,16 @@ static void _ai_run_away(Object* a1, Object* a2);
static int _ai_move_away(Object* a1, Object* a2, int a3);
static bool _ai_find_friend(Object* a1, int a2, int a3);
static int _compare_nearer(const void* a1, const void* a2);
static void _ai_sort_list_distance(Object** critterList, int length, Object* origin);
static int _compare_strength(const void* p1, const void* p2);
static void _ai_sort_list_strength(Object** critterList, int length);
static int _compare_weakness(const void* p1, const void* p2);
static void _ai_sort_list_weakness(Object** critterList, int length);
static Object* _ai_find_nearest_team(Object* a1, Object* a2, int a3);
static Object* _ai_find_nearest_team_in_combat(Object* a1, Object* a2, int a3);
static int _ai_find_attackers(Object* a1, Object** a2, Object** a3, Object** a4);
static Object* _ai_danger_source(Object* a1);
static int _ai_have_ammo(Object* critter_obj, Object* weapon_obj, Object** out_ammo_obj);
static bool aiHaveAmmo(Object* critter, Object* weapon, Object** ammoPtr);
static bool _caiHasWeapPrefType(AiPacket* ai, int attackType);
static Object* _ai_best_weapon(Object* a1, Object* a2, Object* a3, Object* a4);
static bool _ai_can_use_weapon(Object* critter, Object* weapon, int hitMode);
@@ -114,9 +119,10 @@ static Object* _ai_search_environ(Object* critter, int itemType);
static Object* _ai_retrieve_object(Object* a1, Object* a2);
static int _ai_pick_hit_mode(Object* a1, Object* a2, Object* a3);
static int _ai_move_steps_closer(Object* a1, Object* a2, int actionPoints, int a4);
static int _ai_move_closer(Object* a1, Object* a2, int a3);
static int _cai_retargetTileFromFriendlyFire(Object* source, Object* target, int* tilePtr);
static int _cai_retargetTileFromFriendlyFireSubFunc(AiRetargetData* aiRetargetData, int tile);
static bool _cai_attackWouldIntersect(Object* a1, Object* a2, Object* a3, int tile, int* distance);
static bool _cai_attackWouldIntersect(Object* attacker, Object* defender, Object* attackerFriend, int tile, int* distance);
static int _ai_switch_weapons(Object* a1, int* hitMode, Object** weapon, Object* a4);
static int _ai_called_shot(Object* a1, Object* a2, int a3);
static int _ai_attack(Object* a1, Object* a2, int a3);
@@ -291,7 +297,7 @@ static char _attack_str[268];
// parse hurt_too_much
static void _parse_hurt_str(char* str, int* valuePtr)
{
int v5, v10;
size_t v5, v10;
char tmp;
int i;
@@ -988,8 +994,7 @@ static int _ai_check_drugs(Object* critter)
}
int drugPid = drug->pid;
if ((drugPid == PROTO_ID_STIMPACK || drugPid == PROTO_ID_SUPER_STIMPACK || drugPid == PROTO_ID_HEALING_POWDER)
&& itemRemove(critter, drug, 1) == 0) {
if (itemIsHealing(drugPid) && itemRemove(critter, drug, 1) == 0) {
if (_item_d_take_drug(critter, drug) == -1) {
itemAdd(critter, drug, 1);
} else {
@@ -1027,8 +1032,7 @@ static int _ai_check_drugs(Object* critter)
}
if (index < AI_PACKET_CHEM_PRIMARY_DESIRE_COUNT) {
if (drugPid != PROTO_ID_STIMPACK && drugPid != PROTO_ID_SUPER_STIMPACK && drugPid != 273
&& itemRemove(critter, drug, 1) == 0) {
if (!itemIsHealing(drugPid) && itemRemove(critter, drug, 1) == 0) {
if (_item_d_take_drug(critter, drug) == -1) {
itemAdd(critter, drug, 1);
} else {
@@ -1238,6 +1242,15 @@ static int _compare_nearer(const void* a1, const void* a2)
}
}
// NOTE: Inlined.
//
// 0x428B74
static void _ai_sort_list_distance(Object** critterList, int length, Object* origin)
{
_combat_obj = origin;
qsort(critterList, length, sizeof(*critterList), _compare_nearer);
}
// qsort compare function - melee then ranged.
//
// 0x428B8C
@@ -1272,6 +1285,14 @@ static int _compare_strength(const void* p1, const void* p2)
return 0;
}
// NOTE: Inlined.
//
// 0x428BD0
static void _ai_sort_list_strength(Object** critterList, int length)
{
qsort(critterList, length, sizeof(*critterList), _compare_strength);
}
// qsort compare unction - ranged then melee
//
// 0x428BE4
@@ -1306,6 +1327,14 @@ static int _compare_weakness(const void* p1, const void* p2)
return 0;
}
// NOTE: Inlined.
//
// 0x428C28
static void _ai_sort_list_weakness(Object** critterList, int length)
{
qsort(critterList, length, sizeof(*critterList), _compare_weakness);
}
// 0x428C3C
static Object* _ai_find_nearest_team(Object* a1, Object* a2, int a3)
{
@@ -1320,8 +1349,8 @@ static Object* _ai_find_nearest_team(Object* a1, Object* a2, int a3)
return NULL;
}
_combat_obj = a1;
qsort(_curr_crit_list, _curr_crit_num, sizeof(*_curr_crit_list), _compare_nearer);
// NOTE: Uninline.
_ai_sort_list_distance(_curr_crit_list, _curr_crit_num, a1);
for (i = 0; i < _curr_crit_num; i++) {
obj = _curr_crit_list[i];
@@ -1346,8 +1375,8 @@ static Object* _ai_find_nearest_team_in_combat(Object* a1, Object* a2, int a3)
int team = a2->data.critter.combat.team;
_combat_obj = a1;
qsort(_curr_crit_list, _curr_crit_num, sizeof(*_curr_crit_list), _compare_nearer);
// NOTE: Uninline.
_ai_sort_list_distance(_curr_crit_list, _curr_crit_num, a1);
for (int index = 0; index < _curr_crit_num; index++) {
Object* obj = _curr_crit_list[index];
@@ -1383,8 +1412,8 @@ static int _ai_find_attackers(Object* a1, Object** a2, Object** a3, Object** a4)
return 0;
}
_combat_obj = a1;
qsort(_curr_crit_list, _curr_crit_num, sizeof(*_curr_crit_list), _compare_nearer);
// NOTE: Uninline.
_ai_sort_list_distance(_curr_crit_list, _curr_crit_num, a1);
int foundTargetCount = 0;
int team = a1->data.critter.combat.team;
@@ -1473,7 +1502,7 @@ static Object* _ai_danger_source(Object* a1)
}
if (pathfinderFindPath(a1, a1->tile, gDude->data.critter.combat.whoHitMe->tile, NULL, 0, _obj_blocking_at) == 0
&& _combat_check_bad_shot(a1, candidate, HIT_MODE_RIGHT_WEAPON_PRIMARY, false) != 0) {
&& _combat_check_bad_shot(a1, candidate, HIT_MODE_RIGHT_WEAPON_PRIMARY, false) != COMBAT_BAD_SHOT_OK) {
debugPrint("\nai_danger_source: %s couldn't attack at target! Picking alternate!", critterGetName(a1));
break;
}
@@ -1523,27 +1552,26 @@ static Object* _ai_danger_source(Object* a1)
}
}
int (*compareProc)(const void*, const void*);
switch (attackWho) {
case ATTACK_WHO_STRONGEST:
compareProc = _compare_strength;
// NOTE: Uninline.
_ai_sort_list_strength(targets, 4);
break;
case ATTACK_WHO_WEAKEST:
compareProc = _compare_weakness;
// NOTE: Uninline.
_ai_sort_list_weakness(targets, 4);
break;
default:
compareProc = _compare_nearer;
_combat_obj = a1;
// NOTE: Uninline.
_ai_sort_list_distance(targets, 4, a1);
break;
}
qsort(targets, 4, sizeof(*targets), compareProc);
for (int index = 0; index < 4; index++) {
Object* candidate = targets[index];
if (candidate != NULL && objectCanHearObject(a1, candidate)) {
if (pathfinderFindPath(a1, a1->tile, candidate->tile, NULL, 0, _obj_blocking_at) != 0
|| _combat_check_bad_shot(a1, candidate, HIT_MODE_RIGHT_WEAPON_PRIMARY, false) == 0) {
|| _combat_check_bad_shot(a1, candidate, HIT_MODE_RIGHT_WEAPON_PRIMARY, false) == COMBAT_BAD_SHOT_OK) {
return candidate;
}
debugPrint("\nai_danger_source: I couldn't get at my target! Picking alternate!");
@@ -1621,44 +1649,41 @@ void _caiTeamCombatExit()
}
// 0x4292D4
static int _ai_have_ammo(Object* critter_obj, Object* weapon_obj, Object** out_ammo_obj)
static bool aiHaveAmmo(Object* critter, Object* weapon, Object** ammoPtr)
{
int v9;
Object* ammo_obj;
if (out_ammo_obj) {
*out_ammo_obj = NULL;
if (ammoPtr != NULL) {
*ammoPtr = NULL;
}
if (weapon_obj->pid == PROTO_ID_SOLAR_SCORCHER) {
if (weapon->pid == PROTO_ID_SOLAR_SCORCHER) {
return lightGetLightLevel() > 62259;
}
v9 = -1;
int inventoryItemIndex = -1;
while (1) {
ammo_obj = _inven_find_type(critter_obj, 4, &v9);
if (ammo_obj == NULL) {
Object* ammo = _inven_find_type(critter, ITEM_TYPE_AMMO, &inventoryItemIndex);
if (ammo == NULL) {
break;
}
if (weaponCanBeReloadedWith(weapon_obj, ammo_obj)) {
if (out_ammo_obj) {
*out_ammo_obj = ammo_obj;
if (weaponCanBeReloadedWith(weapon, ammo)) {
if (ammoPtr != NULL) {
*ammoPtr = ammo;
}
return 1;
return true;
}
if (weaponGetAnimationCode(weapon_obj)) {
if (_item_w_range(critter_obj, 2) < 3) {
_inven_unwield(critter_obj, 1);
if (weaponGetAnimationCode(weapon)) {
if (weaponGetRange(critter, HIT_MODE_RIGHT_WEAPON_PRIMARY) < 3) {
_inven_unwield(critter, HAND_RIGHT);
}
} else {
_inven_unwield(critter_obj, 1);
_inven_unwield(critter, HAND_RIGHT);
}
}
return 0;
return false;
}
// 0x42938C
@@ -1716,17 +1741,18 @@ static Object* _ai_best_weapon(Object* attacker, Object* weapon1, Object* weapon
return NULL;
}
avgDamage1 = (maxDamage - minDamage) / 2;
if (_item_w_area_damage_radius(weapon1, HIT_MODE_RIGHT_WEAPON_PRIMARY) > 0 && defender != NULL) {
// SFALL: Fix avg damage calculation.
avgDamage1 = (maxDamage + minDamage) / 2;
if (weaponGetDamageRadius(weapon1, HIT_MODE_RIGHT_WEAPON_PRIMARY) > 0 && defender != NULL) {
attack.weapon = weapon1;
_compute_explosion_on_extras(&attack, 0, weaponIsGrenade(weapon1), 1);
avgDamage1 *= attack.extrasLength + 1;
}
// TODO: Probably an error, why it takes [weapon2], should likely use
// [weapon1].
if (weaponGetPerk(weapon2) != -1) {
avgDamage1 *= 5;
// SFALL: Fix for the incorrect item being checked.
if (weaponGetPerk(weapon1) != -1) {
// SFALL: Lower weapon score multiplier for having perk.
avgDamage1 *= 2;
}
if (defender != NULL) {
@@ -1735,12 +1761,12 @@ static Object* _ai_best_weapon(Object* attacker, Object* weapon1, Object* weapon
}
}
if (weaponIsNatural(weapon1)) {
if (itemIsHidden(weapon1)) {
return weapon1;
}
} else {
distance = objectGetDistanceBetween(attacker, defender);
if (_item_w_range(attacker, HIT_MODE_PUNCH) >= distance) {
if (weaponGetRange(attacker, HIT_MODE_PUNCH) >= distance) {
attackType1 = ATTACK_TYPE_UNARMED;
}
}
@@ -1760,15 +1786,17 @@ static Object* _ai_best_weapon(Object* attacker, Object* weapon1, Object* weapon
return NULL;
}
avgDamage2 = (maxDamage - minDamage) / 2;
if (_item_w_area_damage_radius(weapon2, HIT_MODE_RIGHT_WEAPON_PRIMARY) > 0 && defender != NULL) {
// SFALL: Fix avg damage calculation.
avgDamage2 = (maxDamage + minDamage) / 2;
if (weaponGetDamageRadius(weapon2, HIT_MODE_RIGHT_WEAPON_PRIMARY) > 0 && defender != NULL) {
attack.weapon = weapon2;
_compute_explosion_on_extras(&attack, 0, weaponIsGrenade(weapon2), 1);
avgDamage2 *= attack.extrasLength + 1;
}
if (weaponGetPerk(weapon2) != -1) {
avgDamage2 *= 5;
// SFALL: Lower weapon score multiplier for having perk.
avgDamage2 *= 2;
}
if (defender != NULL) {
@@ -1777,7 +1805,7 @@ static Object* _ai_best_weapon(Object* attacker, Object* weapon1, Object* weapon
}
}
if (weaponIsNatural(weapon2)) {
if (itemIsHidden(weapon2)) {
return weapon2;
}
} else {
@@ -1785,7 +1813,7 @@ static Object* _ai_best_weapon(Object* attacker, Object* weapon1, Object* weapon
distance = objectGetDistanceBetween(attacker, weapon1);
}
if (_item_w_range(attacker, HIT_MODE_PUNCH) >= distance) {
if (weaponGetRange(attacker, HIT_MODE_PUNCH) >= distance) {
attackType2 = ATTACK_TYPE_UNARMED;
}
}
@@ -1881,7 +1909,7 @@ Object* _ai_search_inven_weap(Object* critter, int a2, Object* a3)
}
if (a2) {
if (weaponGetActionPointCost1(weapon) > critter->data.critter.combat.ap) {
if (weaponGetPrimaryActionPointCost(weapon) > critter->data.critter.combat.ap) {
continue;
}
}
@@ -1892,7 +1920,7 @@ Object* _ai_search_inven_weap(Object* critter, int a2, Object* a3)
if (weaponGetAttackTypeForHitMode(weapon, HIT_MODE_RIGHT_WEAPON_PRIMARY) == ATTACK_TYPE_RANGED) {
if (ammoGetQuantity(weapon) == 0) {
if (!_ai_have_ammo(critter, weapon, NULL)) {
if (!aiHaveAmmo(critter, weapon, NULL)) {
continue;
}
}
@@ -2024,8 +2052,8 @@ static Object* _ai_search_environ(Object* critter, int itemType)
return NULL;
}
_combat_obj = critter;
qsort(objects, count, sizeof(*objects), _compare_nearer);
// NOTE: Uninline.
_ai_sort_list_distance(objects, count, critter);
int perception = critterGetStat(critter, STAT_PERCEPTION) + 5;
Object* item2 = critterGetItem2(critter);
@@ -2158,6 +2186,20 @@ static int _ai_pick_hit_mode(Object* a1, Object* a2, Object* a3)
}
}
// SFALL: Add a check for the weapon range and the AP cost when AI is
// choosing weapon attack modes.
if (useSecondaryMode) {
if (objectGetDistanceBetween(a1, a3) > weaponGetRange(a1, HIT_MODE_RIGHT_WEAPON_SECONDARY)) {
useSecondaryMode = false;
}
}
if (useSecondaryMode) {
if (a1->data.critter.combat.ap < weaponGetActionPointCost(a1, HIT_MODE_RIGHT_WEAPON_SECONDARY, false)) {
useSecondaryMode = false;
}
}
if (useSecondaryMode) {
if (attackType != ATTACK_TYPE_THROW
|| _ai_search_inven_weap(a1, 0, a3) != NULL
@@ -2261,6 +2303,14 @@ static int _ai_move_steps_closer(Object* a1, Object* a2, int actionPoints, int a
return 0;
}
// NOTE: Inlined.
//
// 0x42A1C0
static int _ai_move_closer(Object* a1, Object* a2, int a3)
{
return _ai_move_steps_closer(a1, a2, a1->data.critter.combat.ap, a3);
}
// 0x42A1D4
static int _cai_retargetTileFromFriendlyFire(Object* source, Object* target, int* tilePtr)
{
@@ -2316,9 +2366,8 @@ static int _cai_retargetTileFromFriendlyFire(Object* source, Object* target, int
}
}
_combat_obj = source;
qsort(aiRetargetData.critterList, aiRetargetData.critterCount, sizeof(*aiRetargetData.critterList), _compare_nearer);
// NOTE: Uninline.
_ai_sort_list_distance(aiRetargetData.critterList, aiRetargetData.critterCount, source);
if (_cai_retargetTileFromFriendlyFireSubFunc(&aiRetargetData, *tilePtr) == 0) {
int minDistance = 99999;
@@ -2374,27 +2423,27 @@ static int _cai_retargetTileFromFriendlyFireSubFunc(AiRetargetData* aiRetargetDa
}
// 0x42A518
static bool _cai_attackWouldIntersect(Object* a1, Object* a2, Object* a3, int tile, int* distance)
static bool _cai_attackWouldIntersect(Object* attacker, Object* defender, Object* attackerFriend, int tile, int* distance)
{
int hitMode = HIT_MODE_RIGHT_WEAPON_PRIMARY;
bool aiming = false;
if (a1 == gDude) {
if (attacker == gDude) {
interfaceGetCurrentHitMode(&hitMode, &aiming);
}
Object* v8 = critterGetWeaponForHitMode(a1, hitMode);
if (v8 == NULL) {
Object* weapon = critterGetWeaponForHitMode(attacker, hitMode);
if (weapon == NULL) {
return false;
}
if (_item_w_range(a1, hitMode) < 1) {
if (weaponGetRange(attacker, hitMode) < 1) {
return false;
}
Object* object = NULL;
_make_straight_path_func(a1, a1->tile, a2->tile, NULL, &object, 32, _obj_shoot_blocking_at);
if (object != a3) {
if (!_combatTestIncidentalHit(a1, a2, a3, v8)) {
_make_straight_path_func(attacker, attacker->tile, defender->tile, NULL, &object, 32, _obj_shoot_blocking_at);
if (object != attackerFriend) {
if (!_combatTestIncidentalHit(attacker, defender, attackerFriend, weapon)) {
return false;
}
}
@@ -2415,7 +2464,7 @@ static int _ai_switch_weapons(Object* a1, int* hitMode, Object** weapon, Object*
} else {
Object* v8 = _ai_search_environ(a1, ITEM_TYPE_WEAPON);
if (v8 == NULL) {
if (_item_w_mp_cost(a1, *hitMode, 0) <= a1->data.critter.combat.ap) {
if (weaponGetActionPointCost(a1, *hitMode, 0) <= a1->data.critter.combat.ap) {
return 0;
}
@@ -2432,7 +2481,7 @@ static int _ai_switch_weapons(Object* a1, int* hitMode, Object** weapon, Object*
if (*weapon != NULL) {
_inven_wield(a1, *weapon, 1);
_combat_turn_run();
if (_item_w_mp_cost(a1, *hitMode, 0) <= a1->data.critter.combat.ap) {
if (weaponGetActionPointCost(a1, *hitMode, 0) <= a1->data.critter.combat.ap) {
return 0;
}
}
@@ -2451,8 +2500,8 @@ static int _ai_called_shot(Object* a1, Object* a2, int a3)
v5 = 3;
if (_item_w_mp_cost(a1, a3, 1) <= a1->data.critter.combat.ap) {
if (_item_w_called_shot(a1, a3)) {
if (weaponGetActionPointCost(a1, a3, 1) <= a1->data.critter.combat.ap) {
if (critterCanAim(a1, a3)) {
ai = aiGetPacket(a1);
if (randomBetween(1, ai->called_freq) == 1) {
combat_difficulty = 1;
@@ -2524,13 +2573,17 @@ static int _ai_try_attack(Object* a1, Object* a2)
int actionPoints = a1->data.critter.combat.ap;
int v31 = 0;
int v42 = 0;
if (weapon == NULL) {
if (critterGetBodyType(a2) != BODY_TYPE_BIPED
|| ((a2->fid & 0xF000) >> 12 != 0)
|| !artExists(buildFid(OBJ_TYPE_CRITTER, a1->fid & 0xFFF, ANIM_THROW_PUNCH, 0, a1->rotation + 1))
|| _combat_safety_invalidate_weapon(a1, weapon, HIT_MODE_RIGHT_WEAPON_PRIMARY, a2, &v31)) {
if (weapon != NULL
|| (critterGetBodyType(a2) == BODY_TYPE_BIPED
&& ((a2->fid & 0xF000) >> 12 == 0)
&& artExists(buildFid(OBJ_TYPE_CRITTER, a1->fid & 0xFFF, ANIM_THROW_PUNCH, 0, a1->rotation + 1)))) {
// SFALL: Check the safety of weapons based on the selected attack mode
// instead of always the primary weapon hit mode.
if (_combat_safety_invalidate_weapon(a1, weapon, hitMode, a2, &v31)) {
_ai_switch_weapons(a1, &hitMode, &weapon, a2);
}
} else {
_ai_switch_weapons(a1, &hitMode, &weapon, a2);
}
unsigned char v30[800];
@@ -2542,10 +2595,10 @@ static int _ai_try_attack(Object* a1, Object* a2)
}
int reason = _combat_check_bad_shot(a1, a2, hitMode, false);
if (reason == 1) {
if (reason == COMBAT_BAD_SHOT_NO_AMMO) {
// out of ammo
if (_ai_have_ammo(a1, weapon, &ammo)) {
int v9 = _item_w_reload(weapon, ammo);
if (aiHaveAmmo(a1, weapon, &ammo)) {
int v9 = weaponReload(weapon, ammo);
if (v9 == 0 && ammo != NULL) {
_obj_destroy(ammo);
}
@@ -2556,9 +2609,14 @@ static int _ai_try_attack(Object* a1, Object* a2)
_gsound_play_sfx_file_volume(sfx, volume);
_ai_magic_hands(a1, weapon, 5002);
int actionPoints = a1->data.critter.combat.ap;
if (actionPoints >= 2) {
a1->data.critter.combat.ap = actionPoints - 2;
// SFALL: Fix incorrect AP cost when AI reloads a weapon.
// CE: There is a commented out code which checks
// available action points before performing reload. Not
// sure why it was commented, probably needs additional
// testing.
int actionPointsRequired = weaponGetActionPointCost(a1, HIT_MODE_RIGHT_WEAPON_RELOAD, false);
if (a1->data.critter.combat.ap >= actionPointsRequired) {
a1->data.critter.combat.ap -= actionPointsRequired;
} else {
a1->data.critter.combat.ap = 0;
}
@@ -2568,7 +2626,7 @@ static int _ai_try_attack(Object* a1, Object* a2)
if (ammo != NULL) {
ammo = _ai_retrieve_object(a1, ammo);
if (ammo != NULL) {
int v15 = _item_w_reload(weapon, ammo);
int v15 = weaponReload(weapon, ammo);
if (v15 == 0) {
_obj_destroy(ammo);
}
@@ -2579,9 +2637,14 @@ static int _ai_try_attack(Object* a1, Object* a2)
_gsound_play_sfx_file_volume(sfx, volume);
_ai_magic_hands(a1, weapon, 5002);
int actionPoints = a1->data.critter.combat.ap;
if (actionPoints >= 2) {
a1->data.critter.combat.ap = actionPoints - 2;
// SFALL: Fix incorrect AP cost when AI reloads a
// weapon.
// CE: See note above, probably need to check
// available action points before performing
// reload.
int actionPointsRequired = weaponGetActionPointCost(a1, HIT_MODE_RIGHT_WEAPON_RELOAD, false);
if (a1->data.critter.combat.ap >= actionPointsRequired) {
a1->data.critter.combat.ap -= actionPointsRequired;
} else {
a1->data.critter.combat.ap = 0;
}
@@ -2600,14 +2663,14 @@ static int _ai_try_attack(Object* a1, Object* a2)
_ai_switch_weapons(a1, &hitMode, &weapon, a2);
}
}
} else if (reason == 3 || reason == 6 || reason == 7) {
} else if (reason == COMBAT_BAD_SHOT_NOT_ENOUGH_AP || reason == COMBAT_BAD_SHOT_ARM_CRIPPLED || reason == COMBAT_BAD_SHOT_BOTH_ARMS_CRIPPLED) {
// 3 - not enough action points
// 6 - crippled one arm for two-handed weapon
// 7 - both hands crippled
if (_ai_switch_weapons(a1, &hitMode, &weapon, a2) == -1) {
return -1;
}
} else if (reason == 2) {
} else if (reason == COMBAT_BAD_SHOT_OUT_OF_RANGE) {
// target out of range
int accuracy = _determine_to_hit_no_range(a1, a2, HIT_LOCATION_UNCALLED, hitMode, v30);
if (accuracy < minToHit) {
@@ -2624,19 +2687,20 @@ static int _ai_try_attack(Object* a1, Object* a2)
v38 = 0;
} else {
if (_ai_switch_weapons(a1, &hitMode, &weapon, a2) == -1 || weapon == NULL) {
if (_ai_move_steps_closer(a1, a2, a1->data.critter.combat.ap, v38) == -1) {
// NOTE: Uninline.
if (_ai_move_closer(a1, a2, v38) == -1) {
return -1;
}
}
v38 = 0;
}
} else if (reason == 5) {
} else if (reason == COMBAT_BAD_SHOT_AIM_BLOCKED) {
// aim is blocked
if (_ai_move_steps_closer(a1, a2, a1->data.critter.combat.ap, v38) == -1) {
return -1;
}
v38 = 0;
} else if (reason == 0) {
} else if (reason == COMBAT_BAD_SHOT_OK) {
int accuracy = _determine_to_hit(a1, a2, HIT_LOCATION_UNCALLED, hitMode);
if (v31) {
if (_ai_move_away(a1, a2, v31) == -1) {
@@ -2689,11 +2753,11 @@ static int _ai_try_attack(Object* a1, Object* a2)
}
v38 = 0;
if (_ai_attack(a1, a2, hitMode) == -1 || _item_w_mp_cost(a1, hitMode, 0) > a1->data.critter.combat.ap) {
if (_ai_attack(a1, a2, hitMode) == -1 || weaponGetActionPointCost(a1, hitMode, 0) > a1->data.critter.combat.ap) {
return -1;
}
} else {
if (_ai_attack(a1, a2, hitMode) == -1 || _item_w_mp_cost(a1, hitMode, 0) > a1->data.critter.combat.ap) {
if (_ai_attack(a1, a2, hitMode) == -1 || weaponGetActionPointCost(a1, hitMode, 0) > a1->data.critter.combat.ap) {
return -1;
}
}
@@ -2715,33 +2779,31 @@ int _cAIPrepWeaponItem(Object* critter, Object* item)
}
// 0x42AECC
void _cai_attempt_w_reload(Object* critter_obj, int a2)
void aiAttemptWeaponReload(Object* critter, int animate)
{
Object* weapon_obj;
Object* ammo_obj;
int v5;
int v9;
const char* sfx;
int v10;
weapon_obj = critterGetItem2(critter_obj);
if (weapon_obj == NULL) {
Object* weapon = critterGetItem2(critter);
if (weapon == NULL) {
return;
}
v5 = ammoGetQuantity(weapon_obj);
if (v5 < ammoGetCapacity(weapon_obj) && _ai_have_ammo(critter_obj, weapon_obj, &ammo_obj)) {
v9 = _item_w_reload(weapon_obj, ammo_obj);
if (v9 == 0) {
_obj_destroy(ammo_obj);
}
int ammoQuantity = ammoGetQuantity(weapon);
int ammoCapacity = ammoGetCapacity(weapon);
if (ammoQuantity < ammoCapacity) {
Object* ammo;
if (aiHaveAmmo(critter, weapon, &ammo)) {
int rc = weaponReload(weapon, ammo);
if (rc == 0) {
_obj_destroy(ammo);
}
if (v9 != -1 && objectIsPartyMember(critter_obj)) {
v10 = _gsound_compute_relative_volume(critter_obj);
sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_READY, weapon_obj, HIT_MODE_RIGHT_WEAPON_PRIMARY, NULL);
_gsound_play_sfx_file_volume(sfx, v10);
if (a2) {
_ai_magic_hands(critter_obj, weapon_obj, 5002);
if (rc != -1 && objectIsPartyMember(critter)) {
int volume = _gsound_compute_relative_volume(critter);
const char* sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_READY, weapon, HIT_MODE_RIGHT_WEAPON_PRIMARY, NULL);
_gsound_play_sfx_file_volume(sfx, volume);
if (animate) {
_ai_magic_hands(critter, weapon, 5002);
}
}
}
}
@@ -2798,14 +2860,28 @@ int _cai_perform_distance_prefs(Object* a1, Object* a2)
break;
case DISTANCE_CHARGE:
if (a2 != NULL) {
_ai_move_steps_closer(a1, a2, a1->data.critter.combat.ap, 1);
// NOTE: Uninline.
_ai_move_closer(a1, a2, 1);
}
break;
case DISTANCE_SNIPE:
if (a2 != NULL) {
if (objectGetDistanceBetween(a1, a2) < 10) {
// NOTE: some odd code omitted
_ai_move_away(a1, a2, 10);
// SFALL: Fix AI behavior for "Snipe" distance preference.
int distance = objectGetDistanceBetween(a1, a2);
if (distance < 10) {
int attackCost = weaponGetActionPointCost(a1, HIT_MODE_RIGHT_WEAPON_PRIMARY, false);
int movementPoints = a1->data.critter.combat.ap - attackCost;
if (movementPoints > 0) {
if (movementPoints + distance - 1 < 5) {
int attackerRating = _combatai_rating(a1);
int defenderRating = _combatai_rating(a2);
if (attackerRating < defenderRating) {
_ai_move_away(a1, a2, 10);
}
}
} else {
_ai_move_away(a1, a2, 10);
}
}
}
break;
@@ -3201,7 +3277,7 @@ Object* _combat_ai_random_target(Attack* attack)
// Looks like this function does nothing because it's result is not used. I
// suppose it was planned to use range as a condition below, but it was
// later moved into 0x426614, but remained here.
_item_w_range(attack->attacker, attack->hitMode);
weaponGetRange(attack->attacker, attack->hitMode);
Object* critter = NULL;
@@ -3214,7 +3290,7 @@ Object* _combat_ai_random_target(Attack* attack)
if (obj != attack->attacker
&& obj != attack->defender
&& _can_see(attack->attacker, obj)
&& _combat_check_bad_shot(attack->attacker, obj, attack->hitMode, false)) {
&& _combat_check_bad_shot(attack->attacker, obj, attack->hitMode, false) == COMBAT_BAD_SHOT_OK) {
critter = obj;
break;
}
@@ -3444,3 +3520,5 @@ void _combatai_delete_critter(Object* obj)
}
}
}
} // namespace fallout

View File

@@ -6,6 +6,8 @@
#include "db.h"
#include "obj_types.h"
namespace fallout {
typedef enum AiMessageType {
AI_MESSAGE_TYPE_RUN,
AI_MESSAGE_TYPE_MOVE,
@@ -48,7 +50,7 @@ void _caiTeamCombatExit();
Object* _ai_search_inven_weap(Object* critter, int a2, Object* a3);
Object* _ai_search_inven_armor(Object* critter);
int _cAIPrepWeaponItem(Object* critter, Object* item);
void _cai_attempt_w_reload(Object* critter_obj, int a2);
void aiAttemptWeaponReload(Object* critter, int animate);
void _combat_ai_begin(int a1, void* a2);
void _combat_ai_over();
int _cai_perform_distance_prefs(Object* a1, Object* a2);
@@ -66,4 +68,6 @@ void _combatai_notify_onlookers(Object* a1);
void _combatai_notify_friends(Object* a1);
void _combatai_delete_critter(Object* obj);
} // namespace fallout
#endif /* COMBAT_AI_H */

View File

@@ -1,6 +1,8 @@
#ifndef COMBAT_AI_DEFS_H
#define COMBAT_AI_DEFS_H
namespace fallout {
typedef enum AreaAttackMode {
AREA_ATTACK_MODE_ALWAYS,
AREA_ATTACK_MODE_SOMETIMES,
@@ -79,4 +81,6 @@ typedef enum HurtTooMuch {
HURT_COUNT,
} HurtTooMuch;
} // namespace fallout
#endif /* COMBAT_AI_DEFS_H */

View File

@@ -10,6 +10,8 @@
#define WEAPON_CRITICAL_FAILURE_TYPE_COUNT (7)
#define WEAPON_CRITICAL_FAILURE_EFFECT_COUNT (5)
namespace fallout {
typedef enum CombatState {
COMBAT_STATE_0x01 = 0x01,
COMBAT_STATE_0x02 = 0x02,
@@ -121,25 +123,54 @@ typedef struct Attack {
int extrasKnockback[EXPLOSION_TARGET_COUNT];
} Attack;
typedef enum CriticalHitDescriptionDataMember {
CRIT_DATA_MEMBER_DAMAGE_MULTIPLIER,
CRIT_DATA_MEMBER_FLAGS,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_STAT,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_STAT_MODIFIER,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS,
CRIT_DATA_MEMBER_MESSAGE_ID,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID,
CRIT_DATA_MEMBER_COUNT,
} CriticalHitDescriptionDataMember;
// Provides metadata about critical hit effect.
typedef struct CriticalHitDescription {
int damageMultiplier;
typedef union CriticalHitDescription {
struct {
int damageMultiplier;
// Damage flags that will be applied to defender.
int flags;
// Damage flags that will be applied to defender.
int flags;
// Stat to check to upgrade this critical hit to massive critical hit or
// -1 if there is no massive critical hit.
int massiveCriticalStat;
// Stat to check to upgrade this critical hit to massive critical hit or
// -1 if there is no massive critical hit.
int massiveCriticalStat;
// Bonus/penalty to massive critical stat.
int massiveCriticalStatModifier;
// Bonus/penalty to massive critical stat.
int massiveCriticalStatModifier;
// Additional damage flags if this critical hit become massive critical.
int massiveCriticalFlags;
// Additional damage flags if this critical hit become massive critical.
int massiveCriticalFlags;
int messageId;
int massiveCriticalMessageId;
int messageId;
int massiveCriticalMessageId;
};
// SFALL: Allow indexed access to the data above.
int values[CRIT_DATA_MEMBER_COUNT];
} CriticalHitDescription;
typedef enum CombatBadShot {
COMBAT_BAD_SHOT_OK = 0,
COMBAT_BAD_SHOT_NO_AMMO = 1,
COMBAT_BAD_SHOT_OUT_OF_RANGE = 2,
COMBAT_BAD_SHOT_NOT_ENOUGH_AP = 3,
COMBAT_BAD_SHOT_ALREADY_DEAD = 4,
COMBAT_BAD_SHOT_AIM_BLOCKED = 5,
COMBAT_BAD_SHOT_ARM_CRIPPLED = 6,
COMBAT_BAD_SHOT_BOTH_ARMS_CRIPPLED = 7,
} CombatBadShot;
} // namespace fallout
#endif /* COMBAT_DEFS_H */

View File

@@ -1,9 +1,5 @@
#include "config.h"
#include "db.h"
#include "memory.h"
#include "platform_compat.h"
#include <ctype.h>
#include <errno.h>
#include <limits.h>
@@ -11,6 +7,12 @@
#include <stdlib.h>
#include <string.h>
#include "db.h"
#include "memory.h"
#include "platform_compat.h"
namespace fallout {
#define CONFIG_FILE_MAX_LINE_LENGTH (256)
// The initial number of sections (or key-value) pairs in the config.
@@ -547,3 +549,5 @@ bool configSetBool(Config* config, const char* sectionKey, const char* key, bool
{
return configSetInt(config, sectionKey, key, value ? 1 : 0);
}
} // namespace fallout

View File

@@ -3,6 +3,8 @@
#include "dictionary.h"
namespace fallout {
// A representation of .INI file.
//
// It's implemented as a [Dictionary] whos keys are section names of .INI file,
@@ -31,4 +33,6 @@ bool configSetDouble(Config* config, const char* sectionKey, const char* key, do
bool configGetBool(Config* config, const char* sectionKey, const char* key, bool* valuePtr);
bool configSetBool(Config* config, const char* sectionKey, const char* key, bool value);
} // namespace fallout
#endif /* CONFIG_H */

View File

@@ -1,5 +1,10 @@
#include "core.h"
#include <limits.h>
#include <string.h>
#include <SDL.h>
#include "audio_engine.h"
#include "color.h"
#include "config.h"
@@ -13,15 +18,15 @@
#include "window_manager.h"
#include "window_manager_private.h"
#include <SDL.h>
#include <limits.h>
#include <string.h>
namespace fallout {
// NOT USED.
void (*_idle_func)() = NULL;
static void idleImpl();
// NOT USED.
void (*_focus_func)(int) = NULL;
// 0x51E234
IdleFunc* _idle_func = NULL;
// 0x51E238
FocusFunc* _focus_func = NULL;
// 0x51E23C
int gKeyboardKeyRepeatRate = 80;
@@ -373,6 +378,9 @@ SDL_Renderer* gSdlRenderer = NULL;
SDL_Texture* gSdlTexture = NULL;
SDL_Surface* gSdlTextureSurface = NULL;
static int gMouseWheelX = 0;
static int gMouseWheelY = 0;
// 0x4C8A70
int coreInit(int a1)
{
@@ -408,6 +416,10 @@ int coreInit(int a1)
gTickerListHead = NULL;
gScreenshotKeyCode = KEY_ALT_C;
// SFALL: Set idle function.
// CE: Prevents frying CPU when window is not focused.
inputSetIdleFunc(idleImpl);
return 0;
}
@@ -919,6 +931,70 @@ unsigned int _get_bk_time()
return gTickerLastTimestamp;
}
// NOTE: Unused.
//
// 0x4C9418
void inputSetKeyboardKeyRepeatRate(int value)
{
gKeyboardKeyRepeatRate = value;
}
// NOTE: Unused.
//
// 0x4C9420
int inputGetKeyboardKeyRepeatRate()
{
return gKeyboardKeyRepeatRate;
}
// NOTE: Unused.
//
// 0x4C9428
void inputSetKeyboardKeyRepeatDelay(int value)
{
gKeyboardKeyRepeatDelay = value;
}
// NOTE: Unused.
//
// 0x4C9430
int inputGetKeyboardKeyRepeatDelay()
{
return gKeyboardKeyRepeatDelay;
}
// NOTE: Unused.
//
// 0x4C9438
void inputSetFocusFunc(FocusFunc* func)
{
_focus_func = func;
}
// NOTE: Unused.
//
// 0x4C9440
FocusFunc* inputGetFocusFunc()
{
return _focus_func;
}
// NOTE: Unused.
//
// 0x4C9448
void inputSetIdleFunc(IdleFunc* func)
{
_idle_func = func;
}
// NOTE: Unused.
//
// 0x4C9450
IdleFunc* inputGetIdleFunc()
{
return _idle_func;
}
// 0x4C9490
void buildNormalizedQwertyKeys()
{
@@ -1269,13 +1345,13 @@ void _GNW95_process_message()
case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
// The data is accumulated in SDL itself and will be processed
// in `_mouse_info`.
case SDL_MOUSEWHEEL:
handleMouseEvent(&e);
break;
case SDL_FINGERDOWN:
case SDL_FINGERMOTION:
case SDL_FINGERUP:
handleTouchFingerEvent(&(e.tfinger));
handleTouchEvent(&e);
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
@@ -1369,7 +1445,7 @@ void _GNW95_process_key(KeyboardData* data)
void _GNW95_lost_focus()
{
if (_focus_func != NULL) {
_focus_func(0);
_focus_func(false);
}
while (!gProgramIsActive) {
@@ -1381,7 +1457,7 @@ void _GNW95_lost_focus()
}
if (_focus_func != NULL) {
_focus_func(1);
_focus_func(true);
}
}
@@ -1679,6 +1755,16 @@ void _mouse_info()
}
_mouse_simulate_input(x, y, buttons);
// TODO: Move to `_mouse_simulate_input`.
// TODO: Record wheel event in VCR.
gMouseWheelX = mouseData.wheelX;
gMouseWheelY = mouseData.wheelY;
if (gMouseWheelX != 0 || gMouseWheelY != 0) {
gMouseEvent |= MOUSE_EVENT_WHEEL;
_raw_buttons |= MOUSE_EVENT_WHEEL;
}
}
// 0x4CA698
@@ -4804,3 +4890,33 @@ bool mouseHitTestInWindow(int win, int left, int top, int right, int bottom)
return _mouse_click_in(left, top, right, bottom);
}
void mouseGetWheel(int* x, int* y)
{
*x = gMouseWheelX;
*y = gMouseWheelY;
}
void convertMouseWheelToArrowKey(int* keyCodePtr)
{
if (*keyCodePtr == -1) {
if ((mouseGetEvent() & MOUSE_EVENT_WHEEL) != 0) {
int wheelX;
int wheelY;
mouseGetWheel(&wheelX, &wheelY);
if (wheelY > 0) {
*keyCodePtr = KEY_ARROW_UP;
} else if (wheelY < 0) {
*keyCodePtr = KEY_ARROW_DOWN;
}
}
}
}
static void idleImpl()
{
SDL_Delay(125);
}
} // namespace fallout

View File

@@ -1,12 +1,14 @@
#ifndef CORE_H
#define CORE_H
#include <SDL.h>
#include "db.h"
#include "dinput.h"
#include "geometry.h"
#include "window.h"
#include <SDL.h>
namespace fallout {
#define MOUSE_DEFAULT_CURSOR_WIDTH 8
#define MOUSE_DEFAULT_CURSOR_HEIGHT 8
@@ -26,6 +28,7 @@
#define MOUSE_EVENT_ANY_BUTTON_UP (MOUSE_EVENT_LEFT_BUTTON_UP | MOUSE_EVENT_RIGHT_BUTTON_UP)
#define MOUSE_EVENT_LEFT_BUTTON_DOWN_REPEAT (MOUSE_EVENT_LEFT_BUTTON_DOWN | MOUSE_EVENT_LEFT_BUTTON_REPEAT)
#define MOUSE_EVENT_RIGHT_BUTTON_DOWN_REPEAT (MOUSE_EVENT_RIGHT_BUTTON_DOWN | MOUSE_EVENT_RIGHT_BUTTON_REPEAT)
#define MOUSE_EVENT_WHEEL 0x40
#define BUTTON_REPEAT_TIME 250
@@ -413,7 +416,9 @@ typedef struct InputEvent {
int mouseY;
} InputEvent;
typedef void TickerProc();
typedef void(IdleFunc)();
typedef void(FocusFunc)(bool focus);
typedef void(TickerProc)();
typedef struct TickerListNode {
int flags;
@@ -460,8 +465,8 @@ typedef int(PauseHandler)();
typedef int(ScreenshotHandler)(int width, int height, unsigned char* buffer, unsigned char* palette);
typedef void(VcrPlaybackCompletionCallback)(int reason);
extern void (*_idle_func)();
extern void (*_focus_func)(int);
extern IdleFunc* _idle_func;
extern FocusFunc* _focus_func;
extern int gKeyboardKeyRepeatRate;
extern int gKeyboardKeyRepeatDelay;
extern bool _keyboard_hooked;
@@ -595,6 +600,14 @@ void coreDelay(unsigned int ms);
unsigned int getTicksSince(unsigned int a1);
unsigned int getTicksBetween(unsigned int a1, unsigned int a2);
unsigned int _get_bk_time();
void inputSetKeyboardKeyRepeatRate(int value);
int inputGetKeyboardKeyRepeatRate();
void inputSetKeyboardKeyRepeatDelay(int value);
int inputGetKeyboardKeyRepeatDelay();
void inputSetFocusFunc(FocusFunc* func);
FocusFunc* inputGetFocusFunc();
void inputSetIdleFunc(IdleFunc* func);
IdleFunc* inputGetIdleFunc();
void buildNormalizedQwertyKeys();
int _GNW95_input_init();
void _GNW95_process_message();
@@ -682,5 +695,9 @@ int screenGetHeight();
int screenGetVisibleHeight();
void mouseGetPositionInWindow(int win, int* x, int* y);
bool mouseHitTestInWindow(int win, int left, int top, int right, int bottom);
void mouseGetWheel(int* x, int* y);
void convertMouseWheelToArrowKey(int* keyCodePtr);
} // namespace fallout
#endif /* CORE_H */

View File

@@ -1,5 +1,7 @@
#include "credits.h"
#include <string.h>
#include "art.h"
#include "color.h"
#include "core.h"
@@ -16,7 +18,7 @@
#include "text_font.h"
#include "window_manager.h"
#include <string.h>
namespace fallout {
#define CREDITS_WINDOW_WIDTH (640)
#define CREDITS_WINDOW_HEIGHT (480)
@@ -274,3 +276,5 @@ static bool creditsFileParseNextLine(char* dest, int* font, int* color)
return false;
}
} // namespace fallout

View File

@@ -1,6 +1,10 @@
#ifndef CREDITS_H
#define CREDITS_H
namespace fallout {
void creditsOpen(const char* path, int fid, bool useReversedStyle);
} // namespace fallout
#endif /* CREDITS_H */

View File

@@ -1,5 +1,8 @@
#include "critter.h"
#include <stdio.h>
#include <string.h>
#include "animation.h"
#include "art.h"
#include "character_editor.h"
@@ -26,10 +29,9 @@
#include "stat.h"
#include "tile.h"
#include "trait.h"
#include "world_map.h"
#include "worldmap.h"
#include <stdio.h>
#include <string.h>
namespace fallout {
// Maximum length of dude's name length.
#define DUDE_NAME_MAX_LENGTH (32)
@@ -68,6 +70,7 @@ typedef enum RadiationLevel {
} RadiationLevel;
static int _get_rad_damage_level(Object* obj, void* data);
static int critter_kill_count_clear();
static int _critterClearObjDrugs(Object* obj, void* data);
// 0x50141C
@@ -159,7 +162,8 @@ int critterInit()
{
dudeResetName();
memset(gKillsByType, 0, sizeof(gKillsByType));
// NOTE: Uninline;
critter_kill_count_clear();
if (!messageListInit(&gCritterMessageList)) {
debugPrint("\nError: Initing critter name message file!");
@@ -181,7 +185,9 @@ int critterInit()
void critterReset()
{
dudeResetName();
memset(gKillsByType, 0, sizeof(gKillsByType));
// NOTE: Uninline;
critter_kill_count_clear();
}
// 0x42D004
@@ -568,6 +574,13 @@ void _process_rads(Object* obj, int radiationLevel, bool isHealing)
if (obj == gDude) {
// Radiation level message, higher is worse.
messageListItem.num = 1000 + radiationLevelIndex;
// SFALL: Fix radiation message when removing radiation effects.
if (isHealing) {
// You feel better.
messageListItem.num = 3003;
}
if (messageListGetItem(&gMiscMessageList, &messageListItem)) {
displayMonitorAddMessage(messageListItem.text);
}
@@ -579,15 +592,18 @@ void _process_rads(Object* obj, int radiationLevel, bool isHealing)
critterSetBonusStat(obj, gRadiationEffectStats[effect], value);
}
if ((obj->data.critter.combat.results & DAM_DEAD) == 0) {
// Loop thru effects affecting primary stats. If any of the primary stat
// dropped below minimal value, kill it.
for (int effect = 0; effect < RADIATION_EFFECT_PRIMARY_STAT_COUNT; effect++) {
int base = critterGetBaseStatWithTraitModifier(obj, gRadiationEffectStats[effect]);
int bonus = critterGetBonusStat(obj, gRadiationEffectStats[effect]);
if (base + bonus < PRIMARY_STAT_MIN) {
critterKill(obj, -1, 1);
break;
// SFALL: Prevent death when removing radiation effects.
if (!isHealing) {
if ((obj->data.critter.combat.results & DAM_DEAD) == 0) {
// Loop thru effects affecting primary stats. If any of the primary stat
// dropped below minimal value, kill it.
for (int effect = 0; effect < RADIATION_EFFECT_PRIMARY_STAT_COUNT; effect++) {
int base = critterGetBaseStatWithTraitModifier(obj, gRadiationEffectStats[effect]);
int bonus = critterGetBonusStat(obj, gRadiationEffectStats[effect]);
if (base + bonus < PRIMARY_STAT_MIN) {
critterKill(obj, -1, 1);
break;
}
}
}
}
@@ -597,7 +613,8 @@ void _process_rads(Object* obj, int radiationLevel, bool isHealing)
// You have died from radiation sickness.
messageListItem.num = 1006;
if (messageListGetItem(&gMiscMessageList, &messageListItem)) {
displayMonitorAddMessage(messageListItem.text);
// SFALL: Display a pop-up message box about death from radiation.
gameShowDeathDialog(messageListItem.text);
}
}
}
@@ -669,6 +686,15 @@ int critterGetDamageType(Object* obj)
return proto->critter.data.damageType;
}
// NOTE: Inlined.
//
// 0x42D860
static int critter_kill_count_clear()
{
memset(gKillsByType, 0, sizeof(gKillsByType));
return 0;
}
// 0x42D878
int killsIncByType(int killType)
{
@@ -873,7 +899,7 @@ void critterKill(Object* critter, int anim, bool a3)
_critterClearObj = critter;
_queue_clear_type(EVENT_TYPE_DRUG, _critterClearObjDrugs);
_item_destroy_all_hidden(critter);
itemDestroyAllHidden(critter);
if (a3) {
tileWindowRefreshRect(&updatedRect, elevation);
@@ -1279,7 +1305,7 @@ int _critter_set_who_hit_me(Object* a1, Object* a2)
bool _critter_can_obj_dude_rest()
{
bool v1 = false;
if (!_wmMapCanRestHere(gElevation)) {
if (!wmMapCanRestHere(gElevation)) {
v1 = true;
}
@@ -1366,3 +1392,5 @@ bool _critter_flag_check(int pid, int flag)
protoGetProto(pid, &proto);
return (proto->critter.data.flags & flag) != 0;
}
} // namespace fallout

View File

@@ -5,6 +5,8 @@
#include "obj_types.h"
#include "proto_types.h"
namespace fallout {
typedef enum DudeState {
DUDE_STATE_SNEAKING = 0,
DUDE_STATE_LEVEL_UP_AVAILABLE = 3,
@@ -69,4 +71,6 @@ bool critterIsEncumbered(Object* critter);
bool critterIsFleeing(Object* a1);
bool _critter_flag_check(int pid, int flag);
} // namespace fallout
#endif /* CRITTER_H */

View File

@@ -5,6 +5,8 @@
#include "game_config.h"
#include "palette.h"
namespace fallout {
#define COLOR_CYCLE_PERIOD_1 (200U)
#define COLOR_CYCLE_PERIOD_2 (142U)
#define COLOR_CYCLE_PERIOD_3 (100U)
@@ -332,3 +334,5 @@ void colorCycleTicker()
paletteSetEntriesInRange(palette + 229 * 3, 229, 255);
}
}
} // namespace fallout

View File

@@ -1,6 +1,8 @@
#ifndef CYCLE_H
#define CYCLE_H
namespace fallout {
void colorCycleInit();
void colorCycleReset();
void colorCycleFree();
@@ -10,4 +12,6 @@ bool colorCycleEnabled();
void cycleSetSpeedFactor(int value);
void colorCycleTicker();
} // namespace fallout
#endif /* CYCLE_H */

View File

@@ -8,6 +8,8 @@
#include "pcx.h"
#include "platform_compat.h"
namespace fallout {
// 0x5184AC
DatafileLoader* gDatafileLoader = NULL;
@@ -89,7 +91,7 @@ unsigned char* datafileReadRaw(char* path, int* widthPtr, int* heightPtr)
char* mangledPath = gDatafileNameMangler(path);
char* dot = strrchr(mangledPath, '.');
if (dot != NULL) {
if (compat_stricmp(dot + 1, "pcx")) {
if (compat_stricmp(dot + 1, "pcx") == 0) {
return pcxRead(mangledPath, widthPtr, heightPtr, gDatafilePalette);
}
}
@@ -193,3 +195,5 @@ unsigned char* datafileLoad(char* path, int* sizePtr)
*sizePtr = size;
return data;
}
} // namespace fallout

View File

@@ -1,6 +1,8 @@
#ifndef DATAFILE_H
#define DATAFILE_H
namespace fallout {
typedef unsigned char*(DatafileLoader)(char* path, unsigned char* palette, int* widthPtr, int* heightPtr);
typedef char*(DatafileNameMangler)(char* path);
@@ -21,4 +23,6 @@ void sub_42F024(unsigned char* data, int* widthPtr, int* heightPtr);
unsigned char* datafileGetPalette();
unsigned char* datafileLoad(char* path, int* sizePtr);
} // namespace fallout
#endif /* DATAFILE_H */

View File

@@ -1,12 +1,14 @@
#include "db.h"
#include "platform_compat.h"
#include "xfile.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "platform_compat.h"
#include "xfile.h"
namespace fallout {
typedef struct FileList {
XList xlist;
struct FileList* next;
@@ -61,20 +63,22 @@ int dbOpen(const char* filePath1, int a2, const char* filePath2, int a4)
return 0;
}
// NOTE: This function simply returns 0, but it definitely accept one parameter
// via eax, as seen at every call site. This value is ignored. It's impossible
// to guess it's name.
//
// 0x4C5D54
int _db_current(int a1)
int _db_select(int dbHandle)
{
return 0;
}
// NOTE: Uncollapsed 0x4C5D54.
int _db_current()
{
return 0;
}
// 0x4C5D58
bool _db_total()
int _db_total()
{
return true;
return 0;
}
// 0x4C5D60
@@ -653,7 +657,7 @@ int fileNameListInit(const char* pattern, char*** fileNameListPtr, int a3, int a
char path[COMPAT_MAX_PATH];
sprintf(path, "%s%s", fileName, extension);
free(xlist->fileNames[length]);
xlist->fileNames[length] = strdup(path);
xlist->fileNames[length] = compat_strdup(path);
length++;
}
}
@@ -736,3 +740,5 @@ int _db_list_compare(const void* p1, const void* p2)
{
return compat_stricmp(*(const char**)p1, *(const char**)p2);
}
} // namespace fallout

View File

@@ -1,18 +1,21 @@
#ifndef DB_H
#define DB_H
#include <stddef.h>
#include "memory_defs.h"
#include "xfile.h"
#include <stddef.h>
namespace fallout {
typedef XFile File;
typedef void FileReadProgressHandler();
typedef char* StrdupProc(const char* string);
int dbOpen(const char* filePath1, int a2, const char* filePath2, int a4);
int _db_current(int a1);
bool _db_total();
int _db_select(int dbHandle);
int _db_current();
int _db_total();
void dbExit();
int dbGetFileSize(const char* filePath, int* sizePtr);
int dbGetFileContents(const char* filePath, void* ptr);
@@ -65,4 +68,6 @@ int fileGetSize(File* stream);
void fileSetReadProgressHandler(FileReadProgressHandler* handler, int size);
void _db_enable_hash_table_();
} // namespace fallout
#endif /* DB_H */

View File

@@ -1,5 +1,10 @@
#include "dbox.h"
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include "art.h"
#include "character_editor.h"
#include "color.h"
@@ -14,10 +19,7 @@
#include "window_manager.h"
#include "word_wrap.h"
#include <stdio.h>
#include <string.h>
#include <algorithm>
namespace fallout {
#define FILE_DIALOG_LINE_COUNT 12
@@ -193,81 +195,67 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
: DIALOG_TYPE_MEDIUM;
}
CacheEntry* backgroundHandle;
int backgroundWidth;
int backgroundHeight;
int fid = buildFid(OBJ_TYPE_INTERFACE, gDialogBoxBackgroundFrmIds[dialogType], 0, 0, 0);
unsigned char* background = artLockFrameDataReturningSize(fid, &backgroundHandle, &backgroundWidth, &backgroundHeight);
if (background == NULL) {
FrmImage backgroundFrmImage;
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, gDialogBoxBackgroundFrmIds[dialogType], 0, 0, 0);
if (!backgroundFrmImage.lock(backgroundFid)) {
fontSetCurrent(savedFont);
return -1;
}
// Maintain original position in original resolution, otherwise center it.
if (screenGetWidth() != 640) x = (screenGetWidth() - backgroundWidth) / 2;
if (screenGetHeight() != 480) y = (screenGetHeight() - backgroundHeight) / 2;
int win = windowCreate(x, y, backgroundWidth, backgroundHeight, 256, WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
if (screenGetWidth() != 640) x = (screenGetWidth() - backgroundFrmImage.getWidth()) / 2;
if (screenGetHeight() != 480) y = (screenGetHeight() - backgroundFrmImage.getHeight()) / 2;
int win = windowCreate(x,
y,
backgroundFrmImage.getWidth(),
backgroundFrmImage.getHeight(),
256,
WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
if (win == -1) {
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
return -1;
}
unsigned char* windowBuf = windowGetBuffer(win);
memcpy(windowBuf, background, backgroundWidth * backgroundHeight);
memcpy(windowBuf, backgroundFrmImage.getData(), backgroundFrmImage.getWidth() * backgroundFrmImage.getHeight());
CacheEntry* doneBoxHandle = NULL;
unsigned char* doneBox = NULL;
int doneBoxWidth;
int doneBoxHeight;
CacheEntry* downButtonHandle = NULL;
unsigned char* downButton = NULL;
int downButtonWidth;
int downButtonHeight;
CacheEntry* upButtonHandle = NULL;
unsigned char* upButton = NULL;
FrmImage doneBoxFrmImage;
FrmImage buttonNormalFrmImage;
FrmImage buttonPressedFrmImage;
if ((flags & DIALOG_BOX_0x20) == 0) {
int doneBoxFid = buildFid(OBJ_TYPE_INTERFACE, 209, 0, 0, 0);
doneBox = artLockFrameDataReturningSize(doneBoxFid, &doneBoxHandle, &doneBoxWidth, &doneBoxHeight);
if (doneBox == NULL) {
artUnlock(backgroundHandle);
if (!doneBoxFrmImage.lock(doneBoxFid)) {
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
}
int downButtonFid = buildFid(OBJ_TYPE_INTERFACE, 9, 0, 0, 0);
downButton = artLockFrameDataReturningSize(downButtonFid, &downButtonHandle, &downButtonWidth, &downButtonHeight);
if (downButton == NULL) {
artUnlock(doneBoxHandle);
artUnlock(backgroundHandle);
int pressedFid = buildFid(OBJ_TYPE_INTERFACE, 9, 0, 0, 0);
if (!buttonPressedFrmImage.lock(pressedFid)) {
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
}
int upButtonFid = buildFid(OBJ_TYPE_INTERFACE, 8, 0, 0, 0);
upButton = artLockFrameData(upButtonFid, 0, 0, &upButtonHandle);
if (upButton == NULL) {
artUnlock(downButtonHandle);
artUnlock(doneBoxHandle);
artUnlock(backgroundHandle);
int normalFid = buildFid(OBJ_TYPE_INTERFACE, 8, 0, 0, 0);
if (!buttonNormalFrmImage.lock(normalFid)) {
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
}
int v27 = hasTwoButtons ? _doneX[dialogType] : (backgroundWidth - doneBoxWidth) / 2;
blitBufferToBuffer(doneBox, doneBoxWidth, doneBoxHeight, doneBoxWidth, windowBuf + backgroundWidth * _doneY[dialogType] + v27, backgroundWidth);
int v27 = hasTwoButtons
? _doneX[dialogType]
: (backgroundFrmImage.getWidth() - doneBoxFrmImage.getWidth()) / 2;
blitBufferToBuffer(doneBoxFrmImage.getData(),
doneBoxFrmImage.getWidth(),
doneBoxFrmImage.getHeight(),
doneBoxFrmImage.getWidth(),
windowBuf + backgroundFrmImage.getWidth() * _doneY[dialogType] + v27,
backgroundFrmImage.getWidth());
if (!messageListInit(&messageList)) {
artUnlock(upButtonHandle);
artUnlock(downButtonHandle);
artUnlock(doneBoxHandle);
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
@@ -277,10 +265,6 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
sprintf(path, "%s%s", asc_5186C8, "DBOX.MSG");
if (!messageListLoad(&messageList, path)) {
artUnlock(upButtonHandle);
artUnlock(downButtonHandle);
artUnlock(doneBoxHandle);
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
// FIXME: Window is not removed.
return -1;
@@ -292,10 +276,26 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
// 101 - YES
messageListItem.num = (flags & DIALOG_BOX_YES_NO) == 0 ? 100 : 101;
if (messageListGetItem(&messageList, &messageListItem)) {
fontDrawText(windowBuf + backgroundWidth * (_doneY[dialogType] + 3) + v27 + 35, messageListItem.text, backgroundWidth, backgroundWidth, _colorTable[18979]);
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * (_doneY[dialogType] + 3) + v27 + 35,
messageListItem.text,
backgroundFrmImage.getWidth(),
backgroundFrmImage.getWidth(),
_colorTable[18979]);
}
int btn = buttonCreate(win, v27 + 13, _doneY[dialogType] + 4, downButtonWidth, downButtonHeight, -1, -1, -1, 500, upButton, downButton, NULL, BUTTON_FLAG_TRANSPARENT);
int btn = buttonCreate(win,
v27 + 13,
_doneY[dialogType] + 4,
buttonPressedFrmImage.getWidth(),
buttonPressedFrmImage.getHeight(),
-1,
-1,
-1,
500,
buttonNormalFrmImage.getData(),
buttonPressedFrmImage.getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (btn != -1) {
buttonSetCallbacks(btn, _gsound_red_butt_press, _gsound_red_butt_release);
}
@@ -311,61 +311,58 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
fontSetCurrent(103);
blitBufferToBufferTrans(doneBox,
doneBoxWidth,
doneBoxHeight,
doneBoxWidth,
windowBuf + backgroundWidth * _doneY[dialogType] + _doneX[dialogType] + doneBoxWidth + 24,
backgroundWidth);
blitBufferToBufferTrans(doneBoxFrmImage.getData(),
doneBoxFrmImage.getWidth(),
doneBoxFrmImage.getHeight(),
doneBoxFrmImage.getWidth(),
windowBuf + backgroundFrmImage.getWidth() * _doneY[dialogType] + _doneX[dialogType] + doneBoxFrmImage.getWidth() + 24,
backgroundFrmImage.getWidth());
fontDrawText(windowBuf + backgroundWidth * (_doneY[dialogType] + 3) + _doneX[dialogType] + doneBoxWidth + 59,
a8, backgroundWidth, backgroundWidth, _colorTable[18979]);
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * (_doneY[dialogType] + 3) + _doneX[dialogType] + doneBoxFrmImage.getWidth() + 59,
a8,
backgroundFrmImage.getWidth(),
backgroundFrmImage.getWidth(),
_colorTable[18979]);
int btn = buttonCreate(win,
doneBoxWidth + _doneX[dialogType] + 37,
doneBoxFrmImage.getWidth() + _doneX[dialogType] + 37,
_doneY[dialogType] + 4,
downButtonWidth,
downButtonHeight,
-1, -1, -1, 501, upButton, downButton, 0, BUTTON_FLAG_TRANSPARENT);
buttonPressedFrmImage.getWidth(),
buttonPressedFrmImage.getHeight(),
-1,
-1,
-1,
501,
buttonNormalFrmImage.getData(),
buttonPressedFrmImage.getData(),
0,
BUTTON_FLAG_TRANSPARENT);
if (btn != -1) {
buttonSetCallbacks(btn, _gsound_red_butt_press, _gsound_red_butt_release);
}
} else {
int doneBoxFid = buildFid(OBJ_TYPE_INTERFACE, 209, 0, 0, 0);
unsigned char* doneBox = artLockFrameDataReturningSize(doneBoxFid, &doneBoxHandle, &doneBoxWidth, &doneBoxHeight);
if (doneBox == NULL) {
artUnlock(backgroundHandle);
if (!doneBoxFrmImage.lock(doneBoxFid)) {
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
}
int downButtonFid = buildFid(OBJ_TYPE_INTERFACE, 9, 0, 0, 0);
unsigned char* downButton = artLockFrameDataReturningSize(downButtonFid, &downButtonHandle, &downButtonWidth, &downButtonHeight);
if (downButton == NULL) {
artUnlock(doneBoxHandle);
artUnlock(backgroundHandle);
int pressedFid = buildFid(OBJ_TYPE_INTERFACE, 9, 0, 0, 0);
if (!buttonPressedFrmImage.lock(pressedFid)) {
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
}
int upButtonFid = buildFid(OBJ_TYPE_INTERFACE, 8, 0, 0, 0);
unsigned char* upButton = artLockFrameData(upButtonFid, 0, 0, &upButtonHandle);
if (upButton == NULL) {
artUnlock(downButtonHandle);
artUnlock(doneBoxHandle);
artUnlock(backgroundHandle);
int normalFid = buildFid(OBJ_TYPE_INTERFACE, 8, 0, 0, 0);
if (!buttonNormalFrmImage.lock(normalFid)) {
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
}
if (!messageListInit(&messageList)) {
artUnlock(upButtonHandle);
artUnlock(downButtonHandle);
artUnlock(doneBoxHandle);
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
@@ -375,38 +372,34 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
sprintf(path, "%s%s", asc_5186C8, "DBOX.MSG");
if (!messageListLoad(&messageList, path)) {
artUnlock(upButtonHandle);
artUnlock(downButtonHandle);
artUnlock(doneBoxHandle);
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
}
blitBufferToBufferTrans(doneBox,
doneBoxWidth,
doneBoxHeight,
doneBoxWidth,
windowBuf + backgroundWidth * _doneY[dialogType] + _doneX[dialogType],
backgroundWidth);
blitBufferToBufferTrans(doneBoxFrmImage.getData(),
doneBoxFrmImage.getWidth(),
doneBoxFrmImage.getHeight(),
doneBoxFrmImage.getWidth(),
windowBuf + backgroundFrmImage.getWidth() * _doneY[dialogType] + _doneX[dialogType],
backgroundFrmImage.getWidth());
fontSetCurrent(103);
fontDrawText(windowBuf + backgroundWidth * (_doneY[dialogType] + 3) + _doneX[dialogType] + 35,
a8, backgroundWidth, backgroundWidth, _colorTable[18979]);
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * (_doneY[dialogType] + 3) + _doneX[dialogType] + 35,
a8, backgroundFrmImage.getWidth(), backgroundFrmImage.getWidth(), _colorTable[18979]);
int btn = buttonCreate(win,
_doneX[dialogType] + 13,
_doneY[dialogType] + 4,
downButtonWidth,
downButtonHeight,
buttonPressedFrmImage.getWidth(),
buttonPressedFrmImage.getHeight(),
-1,
-1,
-1,
501,
upButton,
downButton,
buttonNormalFrmImage.getData(),
buttonPressedFrmImage.getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (btn != -1) {
@@ -428,28 +421,44 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
if (hasTitle) {
if ((flags & DIALOG_BOX_NO_HORIZONTAL_CENTERING) != 0) {
fontDrawText(windowBuf + backgroundWidth * v23 + _xtable[dialogType], title, backgroundWidth, backgroundWidth, titleColor);
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * v23 + _xtable[dialogType],
title,
backgroundFrmImage.getWidth(),
backgroundFrmImage.getWidth(),
titleColor);
} else {
int length = fontGetStringWidth(title);
fontDrawText(windowBuf + backgroundWidth * v23 + (backgroundWidth - length) / 2, title, backgroundWidth, backgroundWidth, titleColor);
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * v23 + (backgroundFrmImage.getWidth() - length) / 2,
title,
backgroundFrmImage.getWidth(),
backgroundFrmImage.getWidth(),
titleColor);
}
v23 += fontGetLineHeight();
}
for (int v94 = 0; v94 < bodyLength; v94++) {
int len = fontGetStringWidth(body[v94]);
if (len <= backgroundWidth - 26) {
if (len <= backgroundFrmImage.getWidth() - 26) {
if ((flags & DIALOG_BOX_NO_HORIZONTAL_CENTERING) != 0) {
fontDrawText(windowBuf + backgroundWidth * v23 + _xtable[dialogType], body[v94], backgroundWidth, backgroundWidth, bodyColor);
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * v23 + _xtable[dialogType],
body[v94],
backgroundFrmImage.getWidth(),
backgroundFrmImage.getWidth(),
bodyColor);
} else {
int length = fontGetStringWidth(body[v94]);
fontDrawText(windowBuf + backgroundWidth * v23 + (backgroundWidth - length) / 2, body[v94], backgroundWidth, backgroundWidth, bodyColor);
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * v23 + (backgroundFrmImage.getWidth() - length) / 2,
body[v94],
backgroundFrmImage.getWidth(),
backgroundFrmImage.getWidth(),
bodyColor);
}
v23 += fontGetLineHeight();
} else {
short beginnings[WORD_WRAP_MAX_COUNT];
short count;
if (wordWrap(body[v94], backgroundWidth - 26, beginnings, &count) != 0) {
if (wordWrap(body[v94], backgroundFrmImage.getWidth() - 26, beginnings, &count) != 0) {
debugPrint("\nError: dialog_out");
}
@@ -464,10 +473,18 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
string[v51] = '\0';
if ((flags & DIALOG_BOX_NO_HORIZONTAL_CENTERING) != 0) {
fontDrawText(windowBuf + backgroundWidth * v23 + _xtable[dialogType], string, backgroundWidth, backgroundWidth, bodyColor);
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * v23 + _xtable[dialogType],
string,
backgroundFrmImage.getWidth(),
backgroundFrmImage.getWidth(),
bodyColor);
} else {
int length = fontGetStringWidth(string);
fontDrawText(windowBuf + backgroundWidth * v23 + (backgroundWidth - length) / 2, string, backgroundWidth, backgroundWidth, bodyColor);
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * v23 + (backgroundFrmImage.getWidth() - length) / 2,
string,
backgroundFrmImage.getWidth(),
backgroundFrmImage.getWidth(),
bodyColor);
}
v23 += fontGetLineHeight();
}
@@ -503,13 +520,9 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
}
windowDestroy(win);
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
if (v86) {
artUnlock(doneBoxHandle);
artUnlock(downButtonHandle);
artUnlock(upButtonHandle);
messageListFree(&messageList);
}
@@ -536,48 +549,34 @@ int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLen
}
}
unsigned char* frmBuffers[FILE_DIALOG_FRM_COUNT];
CacheEntry* frmHandles[FILE_DIALOG_FRM_COUNT];
Size frmSizes[FILE_DIALOG_FRM_COUNT];
FrmImage frmImages[FILE_DIALOG_FRM_COUNT];
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
int fid = buildFid(OBJ_TYPE_INTERFACE, gLoadFileDialogFrmIds[index], 0, 0, 0);
frmBuffers[index] = artLockFrameDataReturningSize(fid, &(frmHandles[index]), &(frmSizes[index].width), &(frmSizes[index].height));
if (frmBuffers[index] == NULL) {
while (--index >= 0) {
artUnlock(frmHandles[index]);
}
if (!frmImages[index].lock(fid)) {
return -1;
}
}
int backgroundWidth = frmSizes[FILE_DIALOG_FRM_BACKGROUND].width;
int backgroundHeight = frmSizes[FILE_DIALOG_FRM_BACKGROUND].height;
int backgroundWidth = frmImages[FILE_DIALOG_FRM_BACKGROUND].getWidth();
int backgroundHeight = frmImages[FILE_DIALOG_FRM_BACKGROUND].getHeight();
// Maintain original position in original resolution, otherwise center it.
if (screenGetWidth() != 640) x = (screenGetWidth() - backgroundWidth) / 2;
if (screenGetHeight() != 480) y = (screenGetHeight() - backgroundHeight) / 2;
int win = windowCreate(x, y, backgroundWidth, backgroundHeight, 256, WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
if (win == -1) {
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
artUnlock(frmHandles[index]);
}
return -1;
}
unsigned char* windowBuffer = windowGetBuffer(win);
memcpy(windowBuffer, frmBuffers[FILE_DIALOG_FRM_BACKGROUND], backgroundWidth * backgroundHeight);
memcpy(windowBuffer, frmImages[FILE_DIALOG_FRM_BACKGROUND].getData(), backgroundWidth * backgroundHeight);
MessageList messageList;
MessageListItem messageListItem;
if (!messageListInit(&messageList)) {
windowDestroy(win);
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
artUnlock(frmHandles[index]);
}
return -1;
}
@@ -586,11 +585,6 @@ int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLen
if (!messageListLoad(&messageList, path)) {
windowDestroy(win);
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
artUnlock(frmHandles[index]);
}
return -1;
}
@@ -607,14 +601,14 @@ int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLen
int doneBtn = buttonCreate(win,
LOAD_FILE_DIALOG_DONE_BUTTON_X,
LOAD_FILE_DIALOG_DONE_BUTTON_Y,
frmSizes[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].width,
frmSizes[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].height,
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getWidth(),
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getHeight(),
-1,
-1,
-1,
500,
frmBuffers[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_NORMAL],
frmBuffers[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED],
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_NORMAL].getData(),
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (doneBtn != -1) {
@@ -624,14 +618,14 @@ int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLen
int cancelBtn = buttonCreate(win,
LOAD_FILE_DIALOG_CANCEL_BUTTON_X,
LOAD_FILE_DIALOG_CANCEL_BUTTON_Y,
frmSizes[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].width,
frmSizes[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].height,
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getWidth(),
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getHeight(),
-1,
-1,
-1,
501,
frmBuffers[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_NORMAL],
frmBuffers[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED],
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_NORMAL].getData(),
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (cancelBtn != -1) {
@@ -641,14 +635,14 @@ int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLen
int scrollUpBtn = buttonCreate(win,
FILE_DIALOG_SCROLL_BUTTON_X,
FILE_DIALOG_SCROLL_BUTTON_Y,
frmSizes[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].width,
frmSizes[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].height,
frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].getWidth(),
frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].getHeight(),
-1,
505,
506,
505,
frmBuffers[FILE_DIALOG_FRM_SCROLL_UP_ARROW_NORMAL],
frmBuffers[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED],
frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_NORMAL].getData(),
frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (scrollUpBtn != -1) {
@@ -657,15 +651,15 @@ int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLen
int scrollDownButton = buttonCreate(win,
FILE_DIALOG_SCROLL_BUTTON_X,
FILE_DIALOG_SCROLL_BUTTON_Y + frmSizes[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].height,
frmSizes[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].width,
frmSizes[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].height,
FILE_DIALOG_SCROLL_BUTTON_Y + frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].getHeight(),
frmImages[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].getWidth(),
frmImages[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].getHeight(),
-1,
503,
504,
503,
frmBuffers[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_NORMAL],
frmBuffers[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED],
frmImages[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_NORMAL].getData(),
frmImages[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (scrollUpBtn != -1) {
@@ -707,6 +701,8 @@ int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLen
int scrollCounter = 0;
bool isScrolling = false;
convertMouseWheelToArrowKey(&keyCode);
if (keyCode == 500) {
if (fileListLength != 0) {
strncpy(dest, fileList[selectedFileIndex + pageOffset], 16);
@@ -886,10 +882,6 @@ int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLen
windowDestroy(win);
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
artUnlock(frmHandles[index]);
}
messageListFree(&messageList);
fontSetCurrent(oldFont);
@@ -916,48 +908,34 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
}
}
unsigned char* frmBuffers[FILE_DIALOG_FRM_COUNT];
CacheEntry* frmHandles[FILE_DIALOG_FRM_COUNT];
Size frmSizes[FILE_DIALOG_FRM_COUNT];
FrmImage frmImages[FILE_DIALOG_FRM_COUNT];
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
int fid = buildFid(OBJ_TYPE_INTERFACE, gSaveFileDialogFrmIds[index], 0, 0, 0);
frmBuffers[index] = artLockFrameDataReturningSize(fid, &(frmHandles[index]), &(frmSizes[index].width), &(frmSizes[index].height));
if (frmBuffers[index] == NULL) {
while (--index >= 0) {
artUnlock(frmHandles[index]);
}
if (!frmImages[index].lock(fid)) {
return -1;
}
}
int backgroundWidth = frmSizes[FILE_DIALOG_FRM_BACKGROUND].width;
int backgroundHeight = frmSizes[FILE_DIALOG_FRM_BACKGROUND].height;
int backgroundWidth = frmImages[FILE_DIALOG_FRM_BACKGROUND].getWidth();
int backgroundHeight = frmImages[FILE_DIALOG_FRM_BACKGROUND].getHeight();
// Maintain original position in original resolution, otherwise center it.
if (screenGetWidth() != 640) x = (screenGetWidth() - backgroundWidth) / 2;
if (screenGetHeight() != 480) y = (screenGetHeight() - backgroundHeight) / 2;
int win = windowCreate(x, y, backgroundWidth, backgroundHeight, 256, WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
if (win == -1) {
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
artUnlock(frmHandles[index]);
}
return -1;
}
unsigned char* windowBuffer = windowGetBuffer(win);
memcpy(windowBuffer, frmBuffers[FILE_DIALOG_FRM_BACKGROUND], backgroundWidth * backgroundHeight);
memcpy(windowBuffer, frmImages[FILE_DIALOG_FRM_BACKGROUND].getData(), backgroundWidth * backgroundHeight);
MessageList messageList;
MessageListItem messageListItem;
if (!messageListInit(&messageList)) {
windowDestroy(win);
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
artUnlock(frmHandles[index]);
}
return -1;
}
@@ -966,11 +944,6 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
if (!messageListLoad(&messageList, path)) {
windowDestroy(win);
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
artUnlock(frmHandles[index]);
}
return -1;
}
@@ -987,14 +960,14 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
int doneBtn = buttonCreate(win,
SAVE_FILE_DIALOG_DONE_BUTTON_X,
SAVE_FILE_DIALOG_DONE_BUTTON_Y,
frmSizes[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].width,
frmSizes[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].height,
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getWidth(),
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getHeight(),
-1,
-1,
-1,
500,
frmBuffers[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_NORMAL],
frmBuffers[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED],
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_NORMAL].getData(),
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (doneBtn != -1) {
@@ -1004,14 +977,14 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
int cancelBtn = buttonCreate(win,
SAVE_FILE_DIALOG_CANCEL_BUTTON_X,
SAVE_FILE_DIALOG_CANCEL_BUTTON_Y,
frmSizes[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].width,
frmSizes[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].height,
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getWidth(),
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getHeight(),
-1,
-1,
-1,
501,
frmBuffers[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_NORMAL],
frmBuffers[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED],
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_NORMAL].getData(),
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (cancelBtn != -1) {
@@ -1021,14 +994,14 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
int scrollUpBtn = buttonCreate(win,
FILE_DIALOG_SCROLL_BUTTON_X,
FILE_DIALOG_SCROLL_BUTTON_Y,
frmSizes[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].width,
frmSizes[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].height,
frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].getWidth(),
frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].getHeight(),
-1,
505,
506,
505,
frmBuffers[FILE_DIALOG_FRM_SCROLL_UP_ARROW_NORMAL],
frmBuffers[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED],
frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_NORMAL].getData(),
frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (scrollUpBtn != -1) {
@@ -1037,15 +1010,15 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
int scrollDownButton = buttonCreate(win,
FILE_DIALOG_SCROLL_BUTTON_X,
FILE_DIALOG_SCROLL_BUTTON_Y + frmSizes[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].height,
frmSizes[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].width,
frmSizes[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].height,
FILE_DIALOG_SCROLL_BUTTON_Y + frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].getHeight(),
frmImages[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].getWidth(),
frmImages[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].getHeight(),
-1,
503,
504,
503,
frmBuffers[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_NORMAL],
frmBuffers[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED],
frmImages[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_NORMAL].getData(),
frmImages[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (scrollUpBtn != -1) {
@@ -1090,7 +1063,7 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
char fileNameCopy[32];
strncpy(fileNameCopy, dest, 32);
int fileNameCopyLength = strlen(fileNameCopy);
size_t fileNameCopyLength = strlen(fileNameCopy);
fileNameCopy[fileNameCopyLength + 1] = '\0';
fileNameCopy[fileNameCopyLength] = ' ';
@@ -1115,6 +1088,8 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
int scrollCounter = 0;
bool isScrolling = false;
convertMouseWheelToArrowKey(&keyCode);
if (keyCode == 500) {
rc = 0;
} else if (keyCode == KEY_RETURN) {
@@ -1370,11 +1345,6 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
}
windowDestroy(win);
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
artUnlock(frmHandles[index]);
}
messageListFree(&messageList);
fontSetCurrent(oldFont);
@@ -1399,3 +1369,5 @@ static void fileDialogRenderFileList(unsigned char* buffer, char** fileList, int
}
}
}
} // namespace fallout

View File

@@ -1,6 +1,8 @@
#ifndef DBOX_H
#define DBOX_H
namespace fallout {
typedef enum DialogBoxOptions {
DIALOG_BOX_LARGE = 0x01,
DIALOG_BOX_MEDIUM = 0x02,
@@ -14,4 +16,6 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLength, int x, int y, int flags);
int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLength, int x, int y, int flags);
} // namespace fallout
#endif /* DBOX_H */

View File

@@ -1,9 +1,5 @@
#include "debug.h"
#include "memory.h"
#include "platform_compat.h"
#include "window_manager_private.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -14,12 +10,19 @@
#include <windows.h>
#endif
#include "memory.h"
#include "platform_compat.h"
#include "window_manager_private.h"
namespace fallout {
static int _debug_puts(char* string);
static void _debug_clear();
static int _debug_mono(char* string);
static int _debug_log(char* string);
static int _debug_screen(char* string);
static void _debug_putc(char ch);
static void _debug_putc(int ch);
static void _debug_scroll();
// 0x51DEF8
static FILE* _fd = NULL;
@@ -174,7 +177,28 @@ static int _debug_puts(char* string)
// 0x4C6FAC
static void _debug_clear()
{
// TODO: Something with segments.
char* buffer;
int x;
int y;
buffer = NULL;
if (gDebugPrintProc == _debug_mono) {
buffer = (char*)0xB0000;
} else if (gDebugPrintProc == _debug_screen) {
buffer = (char*)0xB8000;
}
if (buffer != NULL) {
for (y = 0; y < 25; y++) {
for (x = 0; x < 80; x++) {
*buffer++ = ' ';
*buffer++ = 7;
}
}
_cury = 0;
_curx = 0;
}
}
// 0x4C7004
@@ -220,15 +244,72 @@ static int _debug_screen(char* string)
}
// 0x4C709C
static void _debug_putc(char ch)
static void _debug_putc(int ch)
{
// TODO: Something with segments.
char* buffer;
buffer = (char*)0xB0000;
switch (ch) {
case 7:
printf("\x07");
return;
case 8:
if (_curx > 0) {
_curx--;
buffer += 2 * _curx + 2 * 80 * _cury;
*buffer++ = ' ';
*buffer = 7;
}
return;
case 9:
do {
_debug_putc(' ');
} while ((_curx - 1) % 4 != 0);
return;
case 13:
_curx = 0;
return;
default:
buffer += 2 * _curx + 2 * 80 * _cury;
*buffer++ = ch;
*buffer = 7;
_curx++;
if (_curx < 80) {
return;
}
// FALLTHROUGH
case 10:
_curx = 0;
_cury++;
if (_cury > 24) {
_cury = 24;
_debug_scroll();
}
return;
}
}
// 0x4C71AC
void _debug_scroll()
static void _debug_scroll()
{
// TODO: Something with segments.
char* buffer;
int x;
int y;
buffer = (char*)0xB0000;
for (y = 0; y < 24; y++) {
for (x = 0; x < 80 * 2; x++) {
buffer[0] = buffer[80 * 2];
buffer++;
}
}
for (x = 0; x < 80; x++) {
*buffer++ = ' ';
*buffer++ = 7;
}
}
// 0x4C71E8
@@ -238,3 +319,5 @@ void _debug_exit(void)
fclose(_fd);
}
}
} // namespace fallout

View File

@@ -1,6 +1,8 @@
#ifndef DEBUG_H
#define DEBUG_H
namespace fallout {
typedef int(DebugPrintProc)(char* string);
void _GNW_debug_init();
@@ -12,4 +14,6 @@ void _debug_register_func(DebugPrintProc* proc);
int debugPrint(const char* format, ...);
void _debug_exit(void);
} // namespace fallout
#endif /* DEBUG_H */

View File

@@ -1,14 +1,16 @@
#include "dfile.h"
#include "platform_compat.h"
#include <fpattern.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fpattern.h>
#include "platform_compat.h"
namespace fallout {
// The size of decompression buffer for reading compressed [DFile]s.
#define DFILE_DECOMPRESSION_BUFFER_SIZE (0x400)
@@ -61,7 +63,7 @@ DBase* dbaseOpen(const char* filePath)
// Get file size, and reposition stream to read footer, which contains two
// 32-bits ints.
int fileSize = compat_filelength(fileno(stream));
int fileSize = getFileSize(stream);
if (fseek(stream, fileSize - sizeof(int) * 2, SEEK_SET) != 0) {
goto err;
}
@@ -141,7 +143,7 @@ DBase* dbaseOpen(const char* filePath)
goto err;
}
dbase->path = strdup(filePath);
dbase->path = compat_strdup(filePath);
dbase->dataOffset = fileSize - dbaseDataSize;
fclose(stream);
@@ -852,3 +854,5 @@ static void dfileUngetCompressed(DFile* stream, int ch)
stream->flags |= DFILE_HAS_COMPRESSED_UNGETC;
stream->position--;
}
} // namespace fallout

View File

@@ -1,10 +1,13 @@
#ifndef DFILE_H
#define DFILE_H
#include <stdio.h>
#include <zlib.h>
#include "platform_compat.h"
#include <stdio.h>
#include <zlib.h>
namespace fallout {
typedef struct DBase DBase;
typedef struct DBaseEntry DBaseEntry;
@@ -127,4 +130,6 @@ long dfileTell(DFile* stream);
void dfileRewind(DFile* stream);
int dfileEof(DFile* stream);
} // namespace fallout
#endif /* DFILE_H */

View File

@@ -1,14 +1,15 @@
#include "dialog.h"
#include <string.h>
#include "core.h"
#include "memory_manager.h"
#include "movie.h"
#include "platform_compat.h"
#include "text_font.h"
#include "widget.h"
#include "window_manager.h"
#include <string.h>
namespace fallout {
// 0x501623
const float flt_501623 = 31.0;
@@ -209,7 +210,7 @@ void _replyAddOption(const char* a1, const char* a2, int a3)
v18->field_C[v17].string = NULL;
}
v18->field_C[v17].field_18 = widgetGetFont();
v18->field_C[v17].field_18 = windowGetFont();
v18->field_C[v17].field_1A = word_56DB60;
v18->field_C[v17].field_14 = a3;
}
@@ -236,7 +237,7 @@ void _replyAddOptionProc(const char* a1, int a2, int a3)
v5->field_C[v13].proc = a2;
v5->field_C[v13].field_18 = widgetGetFont();
v5->field_C[v13].field_18 = windowGetFont();
v5->field_C[v13].field_1A = word_56DB60;
v5->field_C[v13].field_14 = a3;
}
@@ -388,8 +389,8 @@ void _drawStr(int win, char* str, int font, int width, int height, int left, int
int old_font;
Rect rect;
old_font = widgetGetFont();
widgetSetFont(font);
old_font = windowGetFont();
windowSetFont(font);
_printStr(win, str, width, height, left, top, a8, a9, a10);
@@ -398,7 +399,7 @@ void _drawStr(int win, char* str, int font, int width, int height, int left, int
rect.right = width + left;
rect.bottom = height + top;
windowRefreshRect(win, &rect);
widgetSetFont(old_font);
windowSetFont(old_font);
}
// 0x430D40
@@ -751,3 +752,5 @@ int _dialogGetMediaFlag()
{
return _mediaFlag;
}
} // namespace fallout

View File

@@ -3,6 +3,8 @@
#include "interpreter.h"
namespace fallout {
typedef void DialogFunc1(int win);
typedef void DialogFunc2(int win);
@@ -131,4 +133,6 @@ void _dialogRegisterWinDrawCallbacks(DialogFunc1* a1, DialogFunc2* a2);
int _dialogToggleMediaFlag(int a1);
int _dialogGetMediaFlag();
} // namespace fallout
#endif /* DIALOG_H */

View File

@@ -1,11 +1,13 @@
#include "dictionary.h"
#include "platform_compat.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "platform_compat.h"
namespace fallout {
// NOTE: I guess this marker is used as a type discriminator for implementing
// nested dictionaries. That's why every dictionary-related function starts
// with a check for this value.
@@ -552,3 +554,5 @@ void dictionarySetMemoryProcs(MallocProc* mallocProc, ReallocProc* reallocProc,
gDictionaryFreeProc = dictionaryFreeDefaultImpl;
}
}
} // namespace fallout

View File

@@ -1,9 +1,11 @@
#ifndef DICTIONARY_H
#define DICTIONARY_H
#include <stdio.h>
#include "memory_defs.h"
#include <stdio.h>
namespace fallout {
typedef int(DictionaryReadProc)(FILE* stream, void* buffer, unsigned int size, int a4);
typedef int(DictionaryWriteProc)(FILE* stream, void* buffer, unsigned int size, int a4);
@@ -63,4 +65,6 @@ int dictionaryWriteHeader(FILE* stream, Dictionary* dictionary);
int dictionaryWrite(FILE* stream, Dictionary* dictionary, int a3);
void dictionarySetMemoryProcs(MallocProc* mallocProc, ReallocProc* reallocProc, FreeProc* freeProc);
} // namespace fallout
#endif /* DICTIONARY_H */

View File

@@ -1,5 +1,14 @@
#include "dinput.h"
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;
@@ -11,6 +20,9 @@ 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();
@@ -59,42 +71,49 @@ bool mouseDeviceUnacquire()
// 0x4E053C
bool mouseDeviceGetData(MouseData* mouseState)
{
#if __ANDROID__
mouseState->x = gTouchMouseDeltaX;
mouseState->y = gTouchMouseDeltaY;
mouseState->buttons[0] = 0;
mouseState->buttons[1] = 0;
gTouchMouseDeltaX = 0;
gTouchMouseDeltaY = 0;
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;
if (gTouchFingers == 0) {
if (SDL_GetTicks() - gTouchGestureLastTouchUpTimestamp > 150) {
if (!gTouchGestureHandled) {
if (gTouchGestureTaps == 2) {
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 == 3) {
} else if (gTouchGestureTaps == 2) {
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;
gMouseWheelDeltaX = 0;
gMouseWheelDeltaY = 0;
}
#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;
#endif
return true;
}
@@ -146,43 +165,79 @@ void keyboardDeviceFree()
{
}
void handleTouchFingerEvent(SDL_TouchFingerEvent* event)
void handleMouseEvent(SDL_Event* event)
{
// Mouse movement and buttons are accumulated in SDL itself and will be
// processed later in `mouseDeviceGetData` via `SDL_GetRelativeMouseState`.
if (event->type == SDL_MOUSEWHEEL) {
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->type == SDL_FINGERDOWN) {
if (event->tfinger.type == SDL_FINGERDOWN) {
gTouchFingers++;
gTouchMouseLastX = (int)(event->x * windowWidth);
gTouchMouseLastY = (int)(event->y * windowHeight);
gTouchMouseLastX = (int)(event->tfinger.x * windowWidth);
gTouchMouseLastY = (int)(event->tfinger.y * windowHeight);
gTouchMouseDeltaX = 0;
gTouchMouseDeltaY = 0;
if (event->timestamp - gTouchGestureLastTouchDownTimestamp > 250) {
if (event->tfinger.timestamp - gTouchGestureLastTouchDownTimestamp > 250) {
gTouchGestureTaps = 0;
gTouchGestureHandled = false;
}
gTouchGestureLastTouchDownTimestamp = event->timestamp;
} else if (event->type == SDL_FINGERMOTION) {
gTouchGestureLastTouchDownTimestamp = event->tfinger.timestamp;
} else if (event->tfinger.type == SDL_FINGERMOTION) {
int prevX = gTouchMouseLastX;
int prevY = gTouchMouseLastY;
gTouchMouseLastX = (int)(event->x * windowWidth);
gTouchMouseLastY = (int)(event->y * windowHeight);
gTouchMouseLastX = (int)(event->tfinger.x * windowWidth);
gTouchMouseLastY = (int)(event->tfinger.y * windowHeight);
gTouchMouseDeltaX += gTouchMouseLastX - prevX;
gTouchMouseDeltaY += gTouchMouseLastY - prevY;
} else if (event->type == SDL_FINGERUP) {
} else if (event->tfinger.type == SDL_FINGERUP) {
gTouchFingers--;
int prevX = gTouchMouseLastX;
int prevY = gTouchMouseLastY;
gTouchMouseLastX = (int)(event->x * windowWidth);
gTouchMouseLastY = (int)(event->y * windowHeight);
gTouchMouseLastX = (int)(event->tfinger.x * windowWidth);
gTouchMouseLastY = (int)(event->tfinger.y * windowHeight);
gTouchMouseDeltaX += gTouchMouseLastX - prevX;
gTouchMouseDeltaY += gTouchMouseLastY - prevY;
gTouchGestureTaps++;
gTouchGestureLastTouchUpTimestamp = event->timestamp;
gTouchGestureLastTouchUpTimestamp = event->tfinger.timestamp;
}
if (gLastInputType != INPUT_TYPE_TOUCH) {
// Reset mouse data.
SDL_GetRelativeMouseState(NULL, NULL);
gLastInputType = INPUT_TYPE_TOUCH;
}
}
} // namespace fallout

View File

@@ -3,10 +3,14 @@
#include <SDL.h>
namespace fallout {
typedef struct MouseData {
int x;
int y;
unsigned char buttons[2];
int wheelX;
int wheelY;
} MouseData;
typedef struct KeyboardData {
@@ -28,6 +32,9 @@ void mouseDeviceFree();
bool keyboardDeviceInit();
void keyboardDeviceFree();
void handleTouchFingerEvent(SDL_TouchFingerEvent* event);
void handleMouseEvent(SDL_Event* event);
void handleTouchEvent(SDL_Event* event);
} // namespace fallout
#endif /* DINPUT_H */

View File

@@ -1,5 +1,9 @@
#include "display_monitor.h"
#include <string.h>
#include <fstream>
#include "art.h"
#include "color.h"
#include "combat.h"
@@ -10,10 +14,11 @@
#include "geometry.h"
#include "interface.h"
#include "memory.h"
#include "sfall_config.h"
#include "text_font.h"
#include "window_manager.h"
#include <string.h>
namespace fallout {
// The maximum number of lines display monitor can hold. Once this value
// is reached earlier messages are thrown away.
@@ -33,6 +38,7 @@
#define DISPLAY_MONITOR_BEEP_DELAY (500U)
static void display_clear();
static void displayMonitorRefresh();
static void displayMonitorScrollUpOnMouseDown(int btn, int keyCode);
static void displayMonitorScrollDownOnMouseDown(int btn, int keyCode);
@@ -40,6 +46,12 @@ static void displayMonitorScrollUpOnMouseEnter(int btn, int keyCode);
static void displayMonitorScrollDownOnMouseEnter(int btn, int keyCode);
static void displayMonitorOnMouseExit(int btn, int keyCode);
static void consoleFileInit();
static void consoleFileReset();
static void consoleFileExit();
static void consoleFileAddMessage(const char* message);
static void consoleFileFlush();
// 0x51850C
static bool gDisplayMonitorInitialized = false;
@@ -86,6 +98,9 @@ static int _disp_start;
// 0x56FB58
static unsigned int gDisplayMonitorLastBeepTimestamp;
static std::ofstream gConsoleFileStream;
static int gConsoleFilePrintCount = 0;
// 0x431610
int displayMonitorInit()
{
@@ -168,14 +183,11 @@ int displayMonitorInit()
gDisplayMonitorEnabled = true;
gDisplayMonitorInitialized = true;
for (int index = 0; index < gDisplayMonitorLinesCapacity; index++) {
gDisplayMonitorLines[index][0] = '\0';
}
// NOTE: Uninline.
display_clear();
_disp_start = 0;
_disp_curr = 0;
displayMonitorRefresh();
// SFALL
consoleFileInit();
}
return 0;
@@ -184,15 +196,12 @@ int displayMonitorInit()
// 0x431800
int displayMonitorReset()
{
if (gDisplayMonitorInitialized) {
for (int index = 0; index < gDisplayMonitorLinesCapacity; index++) {
gDisplayMonitorLines[index][0] = '\0';
}
// NOTE: Uninline.
display_clear();
// SFALL
consoleFileReset();
_disp_start = 0;
_disp_curr = 0;
displayMonitorRefresh();
}
return 0;
}
@@ -200,6 +209,9 @@ int displayMonitorReset()
void displayMonitorExit()
{
if (gDisplayMonitorInitialized) {
// SFALL
consoleFileExit();
internal_free(gDisplayMonitorBackgroundFrmData);
gDisplayMonitorInitialized = false;
}
@@ -212,6 +224,9 @@ void displayMonitorAddMessage(char* str)
return;
}
// SFALL
consoleFileAddMessage(str);
int oldFont = fontGetCurrent();
fontSetCurrent(DISPLAY_MONITOR_FONT);
@@ -295,6 +310,24 @@ void displayMonitorAddMessage(char* str)
displayMonitorRefresh();
}
// NOTE: Inlined.
//
// 0x431A2C
static void display_clear()
{
int index;
if (gDisplayMonitorInitialized) {
for (index = 0; index < gDisplayMonitorLinesCapacity; index++) {
gDisplayMonitorLines[index][0] = '\0';
}
_disp_start = 0;
_disp_curr = 0;
displayMonitorRefresh();
}
}
// 0x431A78
static void displayMonitorRefresh()
{
@@ -389,3 +422,53 @@ void displayMonitorEnable()
gDisplayMonitorEnabled = true;
}
}
static void consoleFileInit()
{
char* consoleFilePath;
configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_CONSOLE_OUTPUT_FILE_KEY, &consoleFilePath);
if (consoleFilePath != NULL && *consoleFilePath == '\0') {
consoleFilePath = NULL;
}
if (consoleFilePath != NULL) {
gConsoleFileStream.open(consoleFilePath);
}
}
static void consoleFileReset()
{
if (gConsoleFileStream.is_open()) {
gConsoleFilePrintCount = 0;
gConsoleFileStream.flush();
}
}
static void consoleFileExit()
{
if (gConsoleFileStream.is_open()) {
gConsoleFileStream.close();
}
}
static void consoleFileAddMessage(const char* message)
{
if (gConsoleFileStream.is_open()) {
gConsoleFileStream << message << '\n';
gConsoleFilePrintCount++;
if (gConsoleFilePrintCount >= 20) {
consoleFileFlush();
}
}
}
static void consoleFileFlush()
{
if (gConsoleFileStream.is_open()) {
gConsoleFilePrintCount = 0;
gConsoleFileStream.flush();
}
}
} // namespace fallout

View File

@@ -1,6 +1,8 @@
#ifndef DISPLAY_MONITOR_H
#define DISPLAY_MONITOR_H
namespace fallout {
int displayMonitorInit();
int displayMonitorReset();
void displayMonitorExit();
@@ -8,4 +10,6 @@ void displayMonitorAddMessage(char* string);
void displayMonitorDisable();
void displayMonitorEnable();
} // namespace fallout
#endif /* DISPLAY_MONITOR_H */

View File

@@ -1,10 +1,12 @@
#include "draw.h"
#include <string.h>
#include "color.h"
#include "core.h"
#include "mmx.h"
#include <string.h>
namespace fallout {
// 0x4D2FC0
void bufferDrawLine(unsigned char* buf, int pitch, int x1, int y1, int x2, int y2, int color)
@@ -326,3 +328,5 @@ void bufferOutline(unsigned char* buf, int width, int height, int pitch, int col
}
}
}
} // namespace fallout

View File

@@ -1,6 +1,8 @@
#ifndef DRAW_H
#define DRAW_H
namespace fallout {
void bufferDrawLine(unsigned char* buf, int pitch, int left, int top, int right, int bottom, int color);
void bufferDrawRect(unsigned char* buf, int a2, int a3, int a4, int a5, int a6, int a7);
void bufferDrawRectShadowed(unsigned char* buf, int a2, int a3, int a4, int a5, int a6, int a7, int a8);
@@ -14,4 +16,6 @@ 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);
} // namespace fallout
#endif /* DRAW_H */

View File

@@ -1,13 +1,15 @@
#include "electronic_registration.h"
#include "game_config.h"
#include "platform_compat.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include "game_config.h"
#include "platform_compat.h"
namespace fallout {
// 0x440DD0
void runElectronicRegistration()
{
@@ -45,3 +47,5 @@ void runElectronicRegistration()
}
}
}
} // namespace fallout

View File

@@ -1,6 +1,10 @@
#ifndef ELECTRONIC_REGISTRATION_H
#define ELECTRONIC_REGISTRATION_H
namespace fallout {
void runElectronicRegistration();
} // namespace fallout
#endif /* ELECTRONIC_REGISTRATION_H */

View File

@@ -1,5 +1,10 @@
#include "elevator.h"
#include <ctype.h>
#include <string.h>
#include <algorithm>
#include "art.h"
#include "core.h"
#include "cycle.h"
@@ -12,19 +17,17 @@
#include "map.h"
#include "pipboy.h"
#include "scripts.h"
#include "sfall_config.h"
#include "window_manager.h"
#include <ctype.h>
#include <string.h>
namespace fallout {
// The maximum number of elevator levels.
#define ELEVATOR_LEVEL_MAX (4)
// NOTE: There are two variables which hold background data used in the
// elevator window - [gElevatorBackgroundFrmData] and [gElevatorPanelFrmData].
// For unknown reason they are using -1 to denote that they are not set
// (instead of using NULL).
#define ELEVATOR_BACKGROUND_NULL ((unsigned char*)(-1))
// Max number of elevators that can be loaded from elevators.ini. This limit is
// emposed by Sfall.
#define ELEVATORS_MAX 50
typedef enum ElevatorFrm {
ELEVATOR_FRM_BUTTON_DOWN,
@@ -56,7 +59,7 @@ static const int gElevatorFrmIds[ELEVATOR_FRM_COUNT] = {
};
// 0x43E95C
static const ElevatorBackground gElevatorBackgrounds[ELEVATOR_COUNT] = {
static ElevatorBackground gElevatorBackgrounds[ELEVATORS_MAX] = {
{ 143, -1 },
{ 143, 150 },
{ 144, -1 },
@@ -86,7 +89,7 @@ static const ElevatorBackground gElevatorBackgrounds[ELEVATOR_COUNT] = {
// Number of levels for eleveators.
//
// 0x43EA1C
static const int gElevatorLevels[ELEVATOR_COUNT] = {
static int gElevatorLevels[ELEVATORS_MAX] = {
4,
2,
3,
@@ -114,7 +117,7 @@ static const int gElevatorLevels[ELEVATOR_COUNT] = {
};
// 0x43EA7C
static const ElevatorDescription gElevatorDescriptions[ELEVATOR_COUNT][ELEVATOR_LEVEL_MAX] = {
static ElevatorDescription gElevatorDescriptions[ELEVATORS_MAX][ELEVATOR_LEVEL_MAX] = {
{
{ 14, 0, 18940 },
{ 14, 1, 18936 },
@@ -264,7 +267,7 @@ static const ElevatorDescription gElevatorDescriptions[ELEVATOR_COUNT][ELEVATOR_
// NOTE: These values are also used as key bindings.
//
// 0x43EEFC
static const char gElevatorLevelLabels[ELEVATOR_COUNT][ELEVATOR_LEVEL_MAX] = {
static char gElevatorLevelLabels[ELEVATORS_MAX][ELEVATOR_LEVEL_MAX] = {
{ '1', '2', '3', '4' },
{ 'G', '1', '\0', '\0' },
{ '1', '2', '3', '\0' },
@@ -313,57 +316,29 @@ static const char* gElevatorSoundEffects[ELEVATOR_LEVEL_MAX - 1][ELEVATOR_LEVEL_
},
};
// 0x570A2C
static Size gElevatorFrmSizes[ELEVATOR_FRM_COUNT];
// 0x570A44
static int gElevatorBackgroundFrmWidth;
// 0x570A48
static int gElevatorBackgroundFrmHeight;
// 0x570A4C
static int gElevatorPanelFrmWidth;
// 0x570A50
static int gElevatorPanelFrmHeight;
// 0x570A54
static int gElevatorWindow;
// 0x570A58
static CacheEntry* gElevatorFrmHandles[ELEVATOR_FRM_COUNT];
// 0x570A64
static CacheEntry* gElevatorBackgroundFrmHandle;
// 0x570A68
static CacheEntry* gElevatorPanelFrmHandle;
// 0x570A6C
static unsigned char* gElevatorWindowBuffer;
// 0x570A70
static bool gElevatorWindowIsoWasEnabled;
// 0x570A74
static unsigned char* gElevatorFrmData[ELEVATOR_FRM_COUNT];
// 0x570A80
static unsigned char* gElevatorBackgroundFrmData;
// 0x570A84
static unsigned char* gElevatorPanelFrmData;
static FrmImage _elevatorFrmImages[ELEVATOR_FRM_COUNT];
static FrmImage _elevatorBackgroundFrmImage;
static FrmImage _elevatorPanelFrmImage;
// Presents elevator dialog for player to pick a desired level.
//
// 0x43EF5C
int elevatorSelectLevel(int elevator, int* mapPtr, int* elevationPtr, int* tilePtr)
{
if (elevator < 0 || elevator >= ELEVATOR_COUNT) {
if (elevator < 0 || elevator >= ELEVATORS_MAX) {
return -1;
}
// SFALL
if (elevatorWindowInit(elevator) == -1) {
return -1;
}
@@ -403,15 +378,15 @@ int elevatorSelectLevel(int elevator, int* mapPtr, int* elevationPtr, int* tileP
debugPrint("\n the start elev level %d\n", *elevationPtr);
int v18 = (gElevatorFrmSizes[ELEVATOR_FRM_GAUGE].width * gElevatorFrmSizes[ELEVATOR_FRM_GAUGE].height) / 13;
int v18 = (_elevatorFrmImages[ELEVATOR_FRM_GAUGE].getWidth() * _elevatorFrmImages[ELEVATOR_FRM_GAUGE].getHeight()) / 13;
float v42 = 12.0f / (float)(gElevatorLevels[elevator] - 1);
blitBufferToBuffer(
gElevatorFrmData[ELEVATOR_FRM_GAUGE] + v18 * (int)((float)(*elevationPtr) * v42),
gElevatorFrmSizes[ELEVATOR_FRM_GAUGE].width,
gElevatorFrmSizes[ELEVATOR_FRM_GAUGE].height / 13,
gElevatorFrmSizes[ELEVATOR_FRM_GAUGE].width,
gElevatorWindowBuffer + gElevatorBackgroundFrmWidth * 41 + 121,
gElevatorBackgroundFrmWidth);
_elevatorFrmImages[ELEVATOR_FRM_GAUGE].getData() + v18 * (int)((float)(*elevationPtr) * v42),
_elevatorFrmImages[ELEVATOR_FRM_GAUGE].getWidth(),
_elevatorFrmImages[ELEVATOR_FRM_GAUGE].getHeight() / 13,
_elevatorFrmImages[ELEVATOR_FRM_GAUGE].getWidth(),
gElevatorWindowBuffer + _elevatorBackgroundFrmImage.getWidth() * 41 + 121,
_elevatorBackgroundFrmImage.getWidth());
windowRefresh(gElevatorWindow);
bool done = false;
@@ -460,12 +435,12 @@ int elevatorSelectLevel(int elevator, int* mapPtr, int* elevationPtr, int* tileP
unsigned int tick = _get_time();
v44 += v43;
blitBufferToBuffer(
gElevatorFrmData[ELEVATOR_FRM_GAUGE] + v18 * (int)v44,
gElevatorFrmSizes[ELEVATOR_FRM_GAUGE].width,
gElevatorFrmSizes[ELEVATOR_FRM_GAUGE].height / 13,
gElevatorFrmSizes[ELEVATOR_FRM_GAUGE].width,
gElevatorWindowBuffer + gElevatorBackgroundFrmWidth * 41 + 121,
gElevatorBackgroundFrmWidth);
_elevatorFrmImages[ELEVATOR_FRM_GAUGE].getData() + v18 * (int)v44,
_elevatorFrmImages[ELEVATOR_FRM_GAUGE].getWidth(),
_elevatorFrmImages[ELEVATOR_FRM_GAUGE].getHeight() / 13,
_elevatorFrmImages[ELEVATOR_FRM_GAUGE].getWidth(),
gElevatorWindowBuffer + _elevatorBackgroundFrmImage.getWidth() * 41 + 121,
_elevatorBackgroundFrmImage.getWidth());
windowRefresh(gElevatorWindow);
@@ -504,15 +479,14 @@ static int elevatorWindowInit(int elevator)
int index;
for (index = 0; index < ELEVATOR_FRM_COUNT; index++) {
int fid = buildFid(OBJ_TYPE_INTERFACE, gElevatorFrmIds[index], 0, 0, 0);
gElevatorFrmData[index] = artLockFrameDataReturningSize(fid, &(gElevatorFrmHandles[index]), &(gElevatorFrmSizes[index].width), &(gElevatorFrmSizes[index].height));
if (gElevatorFrmData[index] == NULL) {
if (!_elevatorFrmImages[index].lock(fid)) {
break;
}
}
if (index != ELEVATOR_FRM_COUNT) {
for (int reversedIndex = index - 1; reversedIndex >= 0; reversedIndex--) {
artUnlock(gElevatorFrmHandles[reversedIndex]);
_elevatorFrmImages[reversedIndex].unlock();
}
if (gElevatorWindowIsoWasEnabled) {
@@ -524,39 +498,27 @@ static int elevatorWindowInit(int elevator)
return -1;
}
gElevatorPanelFrmData = ELEVATOR_BACKGROUND_NULL;
gElevatorBackgroundFrmData = ELEVATOR_BACKGROUND_NULL;
const ElevatorBackground* elevatorBackground = &(gElevatorBackgrounds[elevator]);
bool backgroundsLoaded = true;
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, elevatorBackground->backgroundFrmId, 0, 0, 0);
gElevatorBackgroundFrmData = artLockFrameDataReturningSize(backgroundFid, &gElevatorBackgroundFrmHandle, &gElevatorBackgroundFrmWidth, &gElevatorBackgroundFrmHeight);
if (gElevatorBackgroundFrmData != NULL) {
if (_elevatorBackgroundFrmImage.lock(backgroundFid)) {
if (elevatorBackground->panelFrmId != -1) {
int panelFid = buildFid(OBJ_TYPE_INTERFACE, elevatorBackground->panelFrmId, 0, 0, 0);
gElevatorPanelFrmData = artLockFrameDataReturningSize(panelFid, &gElevatorPanelFrmHandle, &gElevatorPanelFrmWidth, &gElevatorPanelFrmHeight);
if (gElevatorPanelFrmData == NULL) {
gElevatorPanelFrmData = ELEVATOR_BACKGROUND_NULL;
if (!_elevatorPanelFrmImage.lock(panelFid)) {
backgroundsLoaded = false;
}
}
} else {
gElevatorBackgroundFrmData = ELEVATOR_BACKGROUND_NULL;
backgroundsLoaded = false;
}
if (!backgroundsLoaded) {
if (gElevatorBackgroundFrmData != ELEVATOR_BACKGROUND_NULL) {
artUnlock(gElevatorBackgroundFrmHandle);
}
if (gElevatorPanelFrmData != ELEVATOR_BACKGROUND_NULL) {
artUnlock(gElevatorPanelFrmHandle);
}
_elevatorBackgroundFrmImage.unlock();
_elevatorPanelFrmImage.unlock();
for (int index = 0; index < ELEVATOR_FRM_COUNT; index++) {
artUnlock(gElevatorFrmHandles[index]);
_elevatorFrmImages[index].unlock();
}
if (gElevatorWindowIsoWasEnabled) {
@@ -568,26 +530,21 @@ static int elevatorWindowInit(int elevator)
return -1;
}
int elevatorWindowX = (screenGetWidth() - gElevatorBackgroundFrmWidth) / 2;
int elevatorWindowY = (screenGetHeight() - INTERFACE_BAR_HEIGHT - 1 - gElevatorBackgroundFrmHeight) / 2;
int elevatorWindowX = (screenGetWidth() - _elevatorBackgroundFrmImage.getWidth()) / 2;
int elevatorWindowY = (screenGetHeight() - INTERFACE_BAR_HEIGHT - 1 - _elevatorBackgroundFrmImage.getHeight()) / 2;
gElevatorWindow = windowCreate(
elevatorWindowX,
elevatorWindowY,
gElevatorBackgroundFrmWidth,
gElevatorBackgroundFrmHeight,
_elevatorBackgroundFrmImage.getWidth(),
_elevatorBackgroundFrmImage.getHeight(),
256,
WINDOW_FLAG_0x10 | WINDOW_FLAG_0x02);
if (gElevatorWindow == -1) {
if (gElevatorBackgroundFrmData != ELEVATOR_BACKGROUND_NULL) {
artUnlock(gElevatorBackgroundFrmHandle);
}
if (gElevatorPanelFrmData != ELEVATOR_BACKGROUND_NULL) {
artUnlock(gElevatorPanelFrmHandle);
}
_elevatorBackgroundFrmImage.unlock();
_elevatorPanelFrmImage.unlock();
for (int index = 0; index < ELEVATOR_FRM_COUNT; index++) {
artUnlock(gElevatorFrmHandles[index]);
_elevatorFrmImages[index].unlock();
}
if (gElevatorWindowIsoWasEnabled) {
@@ -600,15 +557,15 @@ static int elevatorWindowInit(int elevator)
}
gElevatorWindowBuffer = windowGetBuffer(gElevatorWindow);
memcpy(gElevatorWindowBuffer, (unsigned char*)gElevatorBackgroundFrmData, gElevatorBackgroundFrmWidth * gElevatorBackgroundFrmHeight);
memcpy(gElevatorWindowBuffer, _elevatorBackgroundFrmImage.getData(), _elevatorBackgroundFrmImage.getWidth() * _elevatorBackgroundFrmImage.getHeight());
if (gElevatorPanelFrmData != ELEVATOR_BACKGROUND_NULL) {
blitBufferToBuffer((unsigned char*)gElevatorPanelFrmData,
gElevatorPanelFrmWidth,
gElevatorPanelFrmHeight,
gElevatorPanelFrmWidth,
gElevatorWindowBuffer + gElevatorBackgroundFrmWidth * (gElevatorBackgroundFrmHeight - gElevatorPanelFrmHeight),
gElevatorBackgroundFrmWidth);
if (_elevatorPanelFrmImage.isLocked()) {
blitBufferToBuffer(_elevatorPanelFrmImage.getData(),
_elevatorPanelFrmImage.getWidth(),
_elevatorPanelFrmImage.getHeight(),
_elevatorPanelFrmImage.getWidth(),
gElevatorWindowBuffer + _elevatorBackgroundFrmImage.getWidth() * (_elevatorBackgroundFrmImage.getHeight() - _elevatorPanelFrmImage.getHeight()),
_elevatorBackgroundFrmImage.getWidth());
}
int y = 40;
@@ -616,14 +573,14 @@ static int elevatorWindowInit(int elevator)
int btn = buttonCreate(gElevatorWindow,
13,
y,
gElevatorFrmSizes[ELEVATOR_FRM_BUTTON_DOWN].width,
gElevatorFrmSizes[ELEVATOR_FRM_BUTTON_DOWN].height,
_elevatorFrmImages[ELEVATOR_FRM_BUTTON_DOWN].getWidth(),
_elevatorFrmImages[ELEVATOR_FRM_BUTTON_DOWN].getHeight(),
-1,
-1,
-1,
500 + level,
gElevatorFrmData[ELEVATOR_FRM_BUTTON_UP],
gElevatorFrmData[ELEVATOR_FRM_BUTTON_DOWN],
_elevatorFrmImages[ELEVATOR_FRM_BUTTON_UP].getData(),
_elevatorFrmImages[ELEVATOR_FRM_BUTTON_DOWN].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (btn != -1) {
@@ -640,16 +597,11 @@ static void elevatorWindowFree()
{
windowDestroy(gElevatorWindow);
if (gElevatorBackgroundFrmData != ELEVATOR_BACKGROUND_NULL) {
artUnlock(gElevatorBackgroundFrmHandle);
}
if (gElevatorPanelFrmData != ELEVATOR_BACKGROUND_NULL) {
artUnlock(gElevatorPanelFrmHandle);
}
_elevatorBackgroundFrmImage.unlock();
_elevatorPanelFrmImage.unlock();
for (int index = 0; index < ELEVATOR_FRM_COUNT; index++) {
artUnlock(gElevatorFrmHandles[index]);
_elevatorFrmImages[index].unlock();
}
scriptsEnable();
@@ -681,3 +633,67 @@ static int elevatorGetLevelFromKeyCode(int elevator, int keyCode)
}
return 0;
}
void elevatorsInit()
{
char* elevatorsFileName;
configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_ELEVATORS_FILE_KEY, &elevatorsFileName);
if (elevatorsFileName != NULL && *elevatorsFileName == '\0') {
elevatorsFileName = NULL;
}
if (elevatorsFileName != NULL) {
Config elevatorsConfig;
if (configInit(&elevatorsConfig)) {
if (configRead(&elevatorsConfig, elevatorsFileName, false)) {
char sectionKey[4];
char key[32];
for (int index = 0; index < ELEVATORS_MAX; index++) {
sprintf(sectionKey, "%d", index);
if (index >= ELEVATOR_COUNT) {
int levels = 0;
configGetInt(&elevatorsConfig, sectionKey, "ButtonCount", &levels);
gElevatorLevels[index] = std::clamp(levels, 2, ELEVATOR_LEVEL_MAX);
}
configGetInt(&elevatorsConfig, sectionKey, "MainFrm", &(gElevatorBackgrounds[index].backgroundFrmId));
configGetInt(&elevatorsConfig, sectionKey, "ButtonsFrm", &(gElevatorBackgrounds[index].panelFrmId));
for (int level = 0; level < ELEVATOR_LEVEL_MAX; level++) {
sprintf(key, "ID%d", level + 1);
configGetInt(&elevatorsConfig, sectionKey, key, &(gElevatorDescriptions[index][level].map));
sprintf(key, "Elevation%d", level + 1);
configGetInt(&elevatorsConfig, sectionKey, key, &(gElevatorDescriptions[index][level].elevation));
sprintf(key, "Tile%d", level + 1);
configGetInt(&elevatorsConfig, sectionKey, key, &(gElevatorDescriptions[index][level].tile));
}
}
// NOTE: Sfall implementation is slightly different. It uses one
// loop and stores `type` value in a separate lookup table. This
// value is then used in the certain places to remap from
// requested elevator to the new one.
for (int index = 0; index < ELEVATORS_MAX; index++) {
sprintf(sectionKey, "%d", index);
int type;
if (configGetInt(&elevatorsConfig, sectionKey, "Image", &type)) {
type = std::clamp(type, 0, ELEVATORS_MAX - 1);
if (index != type) {
memcpy(&(gElevatorBackgrounds[index]), &(gElevatorBackgrounds[type]), sizeof(*gElevatorBackgrounds));
memcpy(&(gElevatorLevels[index]), &(gElevatorLevels[type]), sizeof(*gElevatorLevels));
memcpy(&(gElevatorLevelLabels[index]), &(gElevatorLevelLabels[type]), sizeof(*gElevatorLevelLabels));
}
}
}
}
configFree(&elevatorsConfig);
}
}
}
} // namespace fallout

View File

@@ -1,6 +1,8 @@
#ifndef ELEVATOR_H
#define ELEVATOR_H
namespace fallout {
typedef enum Elevator {
ELEVATOR_BROTHERHOOD_OF_STEEL_MAIN,
ELEVATOR_BROTHERHOOD_OF_STEEL_SURFACE,
@@ -20,4 +22,8 @@ typedef enum Elevator {
int elevatorSelectLevel(int elevator, int* mapPtr, int* elevationPtr, int* tilePtr);
void elevatorsInit();
} // namespace fallout
#endif /* ELEVATOR_H */

View File

@@ -1,5 +1,11 @@
#include "endgame.h"
#include <ctype.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "art.h"
#include "color.h"
#include "core.h"
@@ -25,13 +31,9 @@
#include "text_font.h"
#include "window_manager.h"
#include "word_wrap.h"
#include "world_map.h"
#include "worldmap.h"
#include <ctype.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
namespace fallout {
// The maximum number of subtitle lines per slide.
#define ENDGAME_ENDING_MAX_SUBTITLES (50)
@@ -661,7 +663,7 @@ static void endgameEndingVoiceOverInit(const char* fileBaseName)
unsigned int timing = 0;
for (int index = 0; index < gEndgameEndingSubtitlesLength; index++) {
double charactersCount = strlen(gEndgameEndingSubtitles[index]);
double charactersCount = static_cast<double>(strlen(gEndgameEndingSubtitles[index]));
// NOTE: There is floating point math at 0x4402E6 used to add
// timing.
timing += (unsigned int)trunc(charactersCount * durationPerCharacter * 1000.0);
@@ -758,7 +760,7 @@ static int endgameEndingSubtitlesLoad(const char* filePath)
if (gEndgameEndingSubtitlesLength < ENDGAME_ENDING_MAX_SUBTITLES) {
gEndgameEndingSubtitles[gEndgameEndingSubtitlesLength] = internal_strdup(pch + 1);
gEndgameEndingSubtitlesLength++;
gEndgameEndingSubtitlesCharactersCount += strlen(pch + 1);
gEndgameEndingSubtitlesCharactersCount += static_cast<int>(strlen(pch + 1));
}
}
}
@@ -861,7 +863,7 @@ static int endgameEndingInit()
const char* delim = " \t,";
EndgameEnding entry;
EndgameEnding* entries;
int narrator_file_len;
size_t narratorFileNameLength;
if (gEndgameEndings != NULL) {
internal_free(gEndgameEndings);
@@ -913,9 +915,9 @@ static int endgameEndingInit()
strcpy(entry.voiceOverBaseName, tok);
narrator_file_len = strlen(entry.voiceOverBaseName);
if (isspace(entry.voiceOverBaseName[narrator_file_len - 1])) {
entry.voiceOverBaseName[narrator_file_len - 1] = '\0';
narratorFileNameLength = strlen(entry.voiceOverBaseName);
if (isspace(entry.voiceOverBaseName[narratorFileNameLength - 1])) {
entry.voiceOverBaseName[narratorFileNameLength - 1] = '\0';
}
tok = strtok(NULL, delim);
@@ -971,7 +973,7 @@ int endgameDeathEndingInit()
char* tok;
EndgameDeathEnding entry;
EndgameDeathEnding* entries;
int narrator_file_len;
size_t narratorFileNameLength;
strcpy(gEndgameDeathEndingFileName, "narrator\\nar_5");
@@ -1038,13 +1040,13 @@ int endgameDeathEndingInit()
}
// this code is slightly different from the original, but does the same thing
narrator_file_len = strlen(tok);
strncpy(entry.voiceOverBaseName, tok, narrator_file_len);
narratorFileNameLength = strlen(tok);
strncpy(entry.voiceOverBaseName, tok, narratorFileNameLength);
entry.enabled = false;
if (isspace(entry.voiceOverBaseName[narrator_file_len - 1])) {
entry.voiceOverBaseName[narrator_file_len - 1] = '\0';
if (isspace(entry.voiceOverBaseName[narratorFileNameLength - 1])) {
entry.voiceOverBaseName[narratorFileNameLength - 1] = '\0';
}
entries = (EndgameDeathEnding*)internal_realloc(gEndgameDeathEndings, sizeof(*entries) * (gEndgameDeathEndingsLength + 1));
@@ -1158,13 +1160,13 @@ static int endgameDeathEndingValidate(int* percentage)
}
if (deathEnding->worldAreaKnown != -1) {
if (!_wmAreaIsKnown(deathEnding->worldAreaKnown)) {
if (!wmAreaIsKnown(deathEnding->worldAreaKnown)) {
continue;
}
}
if (deathEnding->worldAreaNotKnown != -1) {
if (_wmAreaIsKnown(deathEnding->worldAreaNotKnown)) {
if (wmAreaIsKnown(deathEnding->worldAreaNotKnown)) {
continue;
}
}
@@ -1197,3 +1199,5 @@ char* endgameDeathEndingGetFileName()
return gEndgameDeathEndingFileName;
}
} // namespace fallout

View File

@@ -1,6 +1,8 @@
#ifndef ENDGAME_H
#define ENDGAME_H
namespace fallout {
typedef enum EndgameDeathEndingReason {
// Dude died.
ENDGAME_DEATH_ENDING_REASON_DEATH = 0,
@@ -18,4 +20,6 @@ int endgameDeathEndingExit();
void endgameSetupDeathEnding(int reason);
char* endgameDeathEndingGetFileName();
} // namespace fallout
#endif /* ENDGAME_H */

View File

@@ -1,11 +1,13 @@
#include "export.h"
#include <ctype.h>
#include <string.h>
#include "interpreter_lib.h"
#include "memory_manager.h"
#include "platform_compat.h"
#include <ctype.h>
#include <string.h>
namespace fallout {
typedef struct ExternalVariable {
char name[32];
@@ -343,3 +345,5 @@ void _exportClearAllVariables()
}
}
}
} // namespace fallout

View File

@@ -3,6 +3,8 @@
#include "interpreter.h"
namespace fallout {
int externalVariableSetValue(Program* program, const char* identifier, ProgramValue& value);
int externalVariableGetValue(Program* program, const char* name, ProgramValue& value);
int externalVariableCreate(Program* program, const char* identifier);
@@ -12,4 +14,6 @@ Program* externalProcedureGetProgram(const char* identifier, int* addressPtr, in
int externalProcedureCreate(Program* program, const char* identifier, int address, int argumentCount);
void _exportClearAllVariables();
} // namespace fallout
#endif /* EXPORT_H */

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