Compare commits

...
Author SHA1 Message Date
Vasilii Rogin b55fb81f2d Remove conditions 2025-09-08 22:18:54 +03:00
Vasilii Rogin 9d4405d955 Add artifacts as deployment 2025-09-08 22:16:35 +03:00
Mike Klaas 571b43410d Fix crash during encounter (#254)
Initial report: https://github.com/BGforgeNet/Fallout2_Restoration_Project/issues/350, first fixed in sfall.

h/t @NovaRain for the fix!

Fixes #253
2025-08-26 08:36:43 -07:00
APAmk2 f0e6002df9 Merge pull request #252. SFall callbacks implementation. 2025-08-18 21:07:58 +04:00
APAMk2 d593c481f2 Clang-format fix & proper variable name 2025-08-18 21:01:26 +04:00
APAMk2 76f3d82679 Clang-format fixes 2025-08-17 23:26:49 +04:00
APAMk2 0e1270a606 Renamed sfall_behaviours to sfall_callbacks 2025-08-17 23:17:00 +04:00
APAMk2 55a32a00ab SFall callbacks implementation (EXPERIMENTAL) 2025-08-17 20:55:09 +04:00
Vasilii Rogin 7b9b545baa Merge pull request #251 from fallout2-ce/minor-more-deobfus
Yet more deobfuscation
2025-08-17 18:42:10 +03:00
Mike Klaas a96edd9f0c one more 2025-08-17 07:36:45 -07:00
Mike Klaas 55c528f346 fixes 2025-08-17 07:22:45 -07:00
Mike Klaas 5c28dc798f Yet more deobfuscation
Focusing on the files that are "almost" fully deobfuscated.  As usual, no semantic changes
2025-08-17 07:17:45 -07:00
APAmk2 7ef6943588 Merge pull request #249 from APAmk2/png-screenshots
PNG Screenshots implementation
2025-08-17 18:07:41 +04:00
Mike Klaas 40bbd6c02a Minor deobfuscation of function parameters (#250)
No semantic changes, just chipping away at removing placeholder function parameters
2025-08-17 06:56:21 -07:00
APAMk2 45c491c11f Added lodepng license file 2025-08-17 17:02:50 +04:00
APAMk2 18c05d438e Fixed clang-format error
Want to believe
2025-08-17 16:58:51 +04:00
APAMk2 c3a501a644 Changed PngScreenshots option to ScreenshotsFormat for new formats in future 2025-08-17 16:54:09 +04:00
APAMk2 2767a399c5 Moved lodepng to third_party folder 2025-08-17 16:39:31 +04:00
APAMk2 1237d8561d Use std::vector, instead of c-style array 2025-08-17 16:07:04 +04:00
APAMk2 89b6812711 Typo fix 2025-08-17 15:50:51 +04:00
APAMk2 339146c5b3 PNG Screenshots implementation 2025-08-17 15:30:06 +04:00
Vasilii Rogin 3949c5d7c1 Merge pull request #248 from fallout2-ce/use-precise-distance-for-walking
Use path length instead of tile distance for determining walk vs run
2025-08-17 10:16:49 +03:00
github-actions[bot] 84a64035e8 chore: auto-format with clang-format 2025-08-16 11:41:39 +00:00
Mike Klaas 164be5105a Use path length instead of tile distance for determining walk vs run
Fallout tries to be smart about walking vs running: if an object you're interacting with is "close" (<5 tiles), the PC will walk, otherwise, they'll run.

The problem with this is that the distance is calculated "as the crow flies" and doesn't take into account the path length.  So not rarely, your character will end up slowly walking a long distance to interact with an object.

This PR fixes that by using path length instead of tile distance.  This is slightly less performant, but extremely unlikely to ever be noticable.  Regardless, I used actual pathfinding only when the tile distance is short, so it is very unlikely to generate long paths (which, in any case, can happen just by trying to move to that tile).

To test, load the given save file.  Interacting with things on the other side of the wall usually triggers annoying walk behaviour.  E.g.:
* Looting the shelves on the other side of the wall
* Using Steal on the shelves / on an NPC
* Dropping an item on one side then picking it up from the other side.
2025-08-16 04:36:02 -07:00
Vasilii Rogin d532e4c398 Merge pull request #244 from fallout2-ce/fix_uncompressed_database_seek
Fix uncompressed database seek
2025-08-06 08:44:28 +03:00
Vasilii Rogin 950c12dbf7 Merge pull request #245 from fallout2-ce/fix_indexes_in_file_list_init
Fix off-by-one error in duplicates cleanup
2025-08-06 08:41:57 +03:00
Vasilii Rogin 2be9b7db65 Fix off-by-one error in duplicates cleanup 2025-08-05 22:43:27 +03:00
github-actions[bot] 138ce8be9b chore: auto-format with clang-format 2025-08-05 19:14:10 +00:00
Vasilii Rogin 96b9323a97 Add assertion about elements count 2025-08-05 22:09:27 +03:00
Vasilii Rogin 7c6d339e3d Do not initialize zlib on non-compressed files 2025-08-05 22:04:40 +03:00
Vasilii Rogin 2ec8240820 Merge pull request #239 from fallout2-ce/fix_gMapGlobalPointers_resize
Resize gMapGlobalPointers if inconsistency between .map and .gam file
2025-08-05 17:57:45 +03:00
Vasilii Rogin ca72573c14 Merge branch 'main' into fix_gMapGlobalPointers_resize 2025-08-05 09:47:15 +03:00
Vasilii Rogin 9974a23c1e Merge pull request #243 from fallout2-ce/fix-deployments-cleanup
Fix deployments cleanup
2025-08-05 08:39:47 +03:00
Vasilii Rogin cd6cce3f34 Update ci-delete-pr-preview.yml 2025-08-05 08:25:26 +03:00
Vasilii Rogin 361b9a0b30 Merge pull request #242 from fallout2-ce/try_to_use_github_deploy_field
Use deployment action instead of comment
2025-08-05 08:04:39 +03:00
Vasilii Rogin 7c7cb87f65 Merge pull request #238 from fallout2-ce/fix_memory_overlap_with_sound
Do not copy the same string, prevent sanitizer crash
2025-08-05 08:02:29 +03:00
Vasilii Rogin 20bcb2360c Fix env, add prune 2025-08-04 20:48:12 +03:00
Vasilii Rogin 75dbc98321 Add debug: true 2025-08-04 20:38:46 +03:00
Vasilii Rogin d792cfa32e add status 2025-08-04 20:38:16 +03:00
Vasilii Rogin e81a07dcaf Add ref, add deployment_id 2025-08-04 20:33:28 +03:00
Vasilii Rogin 0d78119680 Use deployment action instead of comment 2025-08-04 20:27:47 +03:00
github-actions[bot] 79d0fcfcaa chore: auto-format with clang-format 2025-08-04 16:57:36 +00:00
Vasilii Rogin 2d5e69d740 Resize gMapGlobalPointers if inconsistency between .map and .gam file 2025-08-04 19:51:31 +03:00
Vasilii Rogin b5cf3ee148 Do not copy the same string 2025-08-04 19:16:54 +03:00
Mike Klaas a571a985c6 Use FID_ROTATION macro to remove manual bit shifting (#236)
* Use `FID_ROTATION` macro to remove manual bit shifting

This is a mechanical change.

Note: this uncovered what I'm almost certain is a bug in the interpreter code.  I can fix it based on code inspection... this is how it's used everywhere else.  I'm not confident about reproducing it in game though, so open to thoughts on how to preceed here.

* Fix bugs
2025-07-30 21:27:01 -07:00
cambragol 6ccba74836 Merge pull request #230 from fallout2-ce/cambragol-patch-1
Full Transparency Fix
2025-07-23 11:22:25 +09:00
cambragol 30c3af01f5 Merge pull request #231 from fallout2-ce/cambragol-patch-2
Button Fix for Loadsave.cc
2025-07-23 11:22:01 +09:00
Vasilii Rogin fd25944a9e Merge pull request #235 from fallout2-ce/deobfus-iextra
Deobfuscate one function in interpreter_extra.cc
2025-07-22 21:01:48 +03:00
Vasilii Rogin 41c5d77f89 Merge branch 'main' into deobfus-iextra 2025-07-22 20:55:18 +03:00
cambragol 75513058a5 Merge branch 'main' into cambragol-patch-1 2025-07-23 02:07:04 +09:00
cambragol 0cf55eca75 Merge branch 'main' into cambragol-patch-2 2025-07-23 02:06:51 +09:00
Vasilii Rogin e6cbdd5466 Merge pull request #234 from fallout2-ce/test-cache-bust
Test ci cache busting
2025-07-22 18:48:47 +03:00
github-actions[bot] 90f634b285 chore: auto-format with clang-format 2025-07-22 05:23:02 +00:00
Mike Klaas 666ee6f41c Deobfuscate one function in interpreter_extra.cc
Introduces FID_WEAPON_CODE and FID_ROTATION.  In future PRs we can use these to eliminate a lot of inlined bit shifts
2025-07-21 22:21:06 -07:00
Mike Klaas 4342aefa5d Test ci cache busting 2025-07-21 21:55:00 -07:00
cambragol 7224c7d7fb Button Fix for Loadsave.cc
Fixes two missing button sounds for loadsave
2025-07-20 19:02:56 +09:00
cambragol dff7529707 Full Transparency Fix
Fix to restore full transparency. Without this, transparent windows leave a 'trail' within their frame.
2025-07-20 18:51:55 +09:00
Mike Klaas 2435b33480 Fix missing cursor on the right (mirrored) context menu (#227)
* Fix missing cursor on the right (mirrored) context menu

Fixes #226

* Additional deobfuscation + missing artUnlock)
2025-07-13 20:30:37 -07:00
Vasilii Rogin c4652d799a Merge pull request #220 from fallout2-ce/message-box
`message_box` metarule
2025-07-12 10:46:28 +03:00
Mike Klaas 5140b00ccf Implement show/hide_iface_tag,is_iface_tag_active for vanilla tags (#217)
* Implement `show/hide_iface_tag,is_iface_tag_active` for vanilla tags

This is all that is used by RPU (absent partycontrol mod)
2025-07-10 21:39:28 -07:00
Vasilii Rogin f1ba068d05 Merge pull request #198 from fallout2-ce/add_sfall_metarule_exists_metarule
Add opcode_exists metarule
2025-07-10 19:44:59 +03:00
Mike Klaas 01db7078c7 Decrypt some of tile.cc/h (#211)
Mostly functional changes, but did un-inline one function that Alex indicated likely should be uninlined.
2025-07-10 19:34:18 +03:00
Mike Klaas 64fd857da6 Update README.md for clearer OS X instructions (#223) 2025-06-29 20:47:48 -03:00
cambragol 555652d9c3 Merge pull request #216 from fallout2-ce/window-transparent
Window transparent
2025-06-25 16:11:02 +09:00
cambragol 8c35203b7d Merge branch 'main' into window-transparent 2025-06-24 17:33:06 +09:00
cambragol 0303a00299 Merge pull request #219 from fallout2-ce/trade-and-barter-glitch-fix
Trade/Barter background fix
2025-06-24 14:48:16 +09:00
cambragol dd16e374ee Update main.cc
fixed spelling typo
2025-06-24 14:40:27 +09:00
cambragol ce4495589b Update window_manager.cc
removed unneeded comments
2025-06-24 14:39:36 +09:00
github-actions[bot] af350914ef chore: auto-format with clang-format 2025-06-24 05:14:13 +00:00
Mike Klaas 8dd2afc4fa message_box metarule
This is used by Et Tu and RPU.

Also: some decyption of the underlying dialog box code
2025-06-23 22:11:51 -07:00
github-actions[bot] 240aa8a212 chore: auto-format with clang-format 2025-06-23 22:14:36 +00:00
cambragol c58b5eac1d Update window_manager.cc
Removed unneeded if/else switch.
2025-06-24 07:14:09 +09:00
cambragol 0bd8a76b05 Update inventory.cc
Fix background blits positions for moved inventory items
2025-06-23 22:13:23 +09:00
cambragol 853266b380 Merge branch 'main' into window-transparent 2025-06-23 20:55:44 +09:00
Mike Klaas 0adc9c9e77 Prevent pop-in by drawing city circles/names to temp buffer (#178)
* Draw city/name to a temp buffer to prevent pop-in
2025-06-22 22:48:08 -07:00
github-actions[bot] 150e734955 chore: auto-format with clang-format 2025-06-23 05:27:35 +00:00
cambragol d0ba0151d1 Update window_manager.cc
initialised _buffering as false, and put the switch to true in the if statement for the flag from the game init.
2025-06-23 14:27:07 +09:00
cambragol 67c70661a6 Update window_manager.cc
made _buffering conditional on the gameInitWithOptions flag for allocating _screen_buffer
2025-06-22 12:56:20 +09:00
cambragol 9c4efeb59d Merge branch 'main' into window-transparent 2025-06-22 11:18:20 +09:00
cambragol 8bd87be687 Update window_manager.cc
removed unused bool
2025-06-21 13:51:54 +09:00
cambragol 8101c78e78 Update window_manager.cc
cleaning up from testing code
2025-06-21 13:46:20 +09:00
cambragol 9b5737a14e Update automap.cc
Added WINDOW_TRANSPARENT | WINDOW_DRAGGABLE_BY_BACKGROUND temporarily for testing. The automap window default texture has transparency built in.
2025-06-21 11:31:58 +09:00
cambragol 3d4c073856 Update window_manager.cc
Set _buffering to true, add refresh for transparent windows
2025-06-21 10:19:33 +09:00
cambragol f868ac0e1a Update main.cc
Change game ini options to turn on _screen_buffer allocation
2025-06-21 10:09:19 +09:00
github-actions[bot] cd6ca76988 chore: auto-format with clang-format 2025-06-07 15:27:27 +00:00
Vasilii Rogin 5d3cbd567a Add test for OP_WRITE_INT opcode 2025-06-07 18:24:56 +03:00
Vasilii Rogin 70ef1d4009 Add opcode_exists metarule 2025-06-07 18:21:50 +03:00
Vasilii Rogin 3acf0ac6c4 Add assert for opcode_exists 2025-06-07 18:12:43 +03:00
Vasilii Rogin a73c336ead Add tests for metarule_exist/opcode_exists 2025-06-07 18:08:47 +03:00
Vasilii Rogin d46aea53bd Add initial test 2025-06-07 17:59:18 +03:00
71 changed files with 10429 additions and 357 deletions
+21 -6
View File
@@ -14,6 +14,16 @@ jobs:
runs-on: ubuntu-latest
permissions: write-all
steps:
- name: start deployment
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
uses: bobheadxi/deployments@v1
id: deployment
with:
ref: ${{ github.event.pull_request.head.sha }}
step: start
debug: true
env: preview-${{ github.event.pull_request.number }}
- name: Clone
uses: actions/checkout@v4
@@ -91,10 +101,15 @@ jobs:
clean: false
single-commit: true
- name: Comment preview URL on PR
- name: Update deployment status
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
run: |
gh pr comment ${{ github.event.pull_request.number }} \
--body "🚀 WebAssembly PR Preview for ${{github.event.pull_request.head.sha || github.sha }} is available at: https://${{ github.repository_owner }}.github.io/fallout2-ce/PR-${{ github.event.pull_request.number }}/"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: bobheadxi/deployments@v1
with:
step: finish
status: success
debug: true
env: ${{ steps.deployment.outputs.env }}
deployment_id: ${{ steps.deployment.outputs.deployment_id }}
ref: ${{ github.event.pull_request.head.sha }}
env_url: https://${{ github.repository_owner }}.github.io/fallout2-ce/PR-${{ github.event.pull_request.number }}/
+45 -5
View File
@@ -14,6 +14,27 @@ defaults:
shell: bash
jobs:
mark_deployment_as_started:
name: Mark deployment as started
runs-on: ubuntu-latest
permissions:
deployments: write
contents: read
outputs:
deployment_id: ${{ steps.start.outputs.deployment_id }}
env: ${{ steps.start.outputs.env }}
steps:
- name: start deployment
id: start
uses: bobheadxi/deployments@v1
with:
ref: ${{ github.event.pull_request.head.sha }}
step: start
debug: true
env: artifacts-${{ github.event.pull_request.number }}
static-analysis:
name: Static analysis
@@ -87,7 +108,7 @@ jobs:
uses: actions/cache@v4
with:
path: os/android/app/.cxx
key: android-cmake-v100
key: android-cmake-v101
- name: Setup signing config
if: env.KEYSTORE_FILE_BASE64 != '' && env.KEYSTORE_PROPERTIES_FILE_BASE64 != ''
@@ -128,7 +149,7 @@ jobs:
uses: actions/cache@v4
with:
path: out
key: ios-cmake-v100
key: ios-cmake-v101
- name: Configure
run: |
@@ -183,7 +204,7 @@ jobs:
uses: actions/cache@v4
with:
path: out
key: linux-${{ matrix.arch }}-cmake-v100
key: linux-${{ matrix.arch }}-cmake-v101
- name: Configure
run: |
@@ -288,7 +309,7 @@ jobs:
uses: actions/cache@v4
with:
path: out
key: macos-cmake-v100
key: macos-cmake-v101
- name: Configure
run: |
@@ -337,7 +358,7 @@ jobs:
uses: actions/cache@v4
with:
path: out
key: windows-${{ matrix.arch }}-cmake-v100
key: windows-${{ matrix.arch }}-cmake-v101
- name: Configure
shell: cmd
@@ -362,6 +383,25 @@ jobs:
path: out/build/windows-${{ matrix.arch }}/RelWithDebInfo/fallout2-ce-${{ matrix.arch }}.exe
retention-days: 7
mark_deployment_as_completed:
name: Mark deployment as completed
runs-on: ubuntu-latest
permissions:
deployments: write
contents: read
needs: [mark_deployment_as_started, android, ios, linux, linux-armhf, linux-arm64, macos, windows]
steps:
- name: Update deployment status
uses: bobheadxi/deployments@v1
with:
step: finish
status: success
debug: true
ref: ${{ github.event.pull_request.head.sha }}
env: ${{ needs.mark_deployment_as_started.outputs.env }}
deployment_id: ${{ needs.mark_deployment_as_started.outputs.deployment_id }}
env_url: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
release:
name: Release Continuous build
+10 -1
View File
@@ -12,6 +12,7 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: write
deployments: write
steps:
- name: Checkout gh-pages branch
uses: actions/checkout@v4
@@ -43,4 +44,12 @@ jobs:
else
git commit --amend -m "đźš® Remove preview for closed PR #${{ github.event.pull_request.number }}"
git push --force
fi
fi
- name: mark environment as deactivated
uses: bobheadxi/deployments@v1
with:
step: deactivate-env
env: preview-${{ github.event.pull_request.number }}
desc: Environment was pruned
debug: true
+5
View File
@@ -288,11 +288,16 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
"src/sfall_opcodes.h"
"src/sfall_arrays.cc"
"src/sfall_arrays.h"
"src/sfall_callbacks.cc"
"src/sfall_callbacks.h"
"src/touch.cc"
"src/touch.h"
"src/scan_unimplemented.cc"
"third_party/lodepng/lodepng.cpp"
)
target_include_directories(${EXECUTABLE_NAME} PUBLIC "third_party/lodepng")
if(IOS)
target_sources(${EXECUTABLE_NAME} PUBLIC
"src/platform/ios/paths.h"
+1 -1
View File
@@ -44,7 +44,7 @@ $ sudo apt install libsdl2-2.0-0
- Alternatively you can use Fallout 2 from Macplay/The Omni Group as a base - you need to extract game assets from the original bundle. Mount CD/DMG, right click `Fallout 2` -> `Show Package Contents`, navigate to `Contents/Resources`. Copy `GameData` folder somewhere, for example `/Applications/Fallout2`.
- Or if you're a Terminal user and have Homebrew installed you can extract the needed files from the GoG installer:
- Or if you're a Terminal user and have Homebrew installed you can extract the needed files from the GoG installer. **Note**: You must use "Offline backup game installer", not the main game installer.
```console
$ brew install innoextract
+33
View File
@@ -0,0 +1,33 @@
#include "test_utils.h"
#include "sfall.h"
procedure start begin
display_msg("Testing iface_tag functions...");
// assumes no iface tags active in the save game
// SNEAK
show_iface_tag(0);
call assertTrue("show_iface_tag(SNEAK)", is_iface_tag_active(0));
hide_iface_tag(0);
call assertFalse("hide_iface_tag(SNEAK)", is_iface_tag_active(0));
// LEVEL
show_iface_tag(3);
call assertTrue("show_iface_tag(LEVEL)", is_iface_tag_active(3));
hide_iface_tag(3);
call assertFalse("hide_iface_tag(LEVEL)", is_iface_tag_active(3));
// ADDICTED
show_iface_tag(4);
call assertTrue("show_iface_tag(ADDICTED)", is_iface_tag_active(4));
hide_iface_tag(4);
call assertFalse("hide_iface_tag(ADDICTED)", is_iface_tag_active(4));
// POISONED
call assertFalse("hide_iface_tag(POISONED)", is_iface_tag_active(1));
// RADIATED
call assertFalse("hide_iface_tag(RADIATED)", is_iface_tag_active(2));
call report_test_results("iface_tag");
end
+14
View File
@@ -0,0 +1,14 @@
#include "sfall.h"
// this is the basic message_box function used by RPU and Et Tu
#define message_box_warning(text) message_box4(text, 0x01, 134, 145)
procedure start begin
// I don't see a practice way to auto test message_box, so this is just a way to do manual testing
variable ret = message_box1("Basic Dialog\nwith multiple lines\nand such.");
display_msg("chose: " + ret);
message_box2("Basic Dialog\nwith default flags.", -1); // -1 means use default flags
message_box2("Basic Dialog\nsmall.", 0x02);
message_box_warning("Missing config values\n(just testing).");
end
+30
View File
@@ -0,0 +1,30 @@
#include "test_utils.h"
#include "sfall.h"
procedure start begin
display_msg("Testing metarule_exist functions...");
call assertEquals("string_format", metarule_exist("string_format"), 1);
call assertEquals("metarule_exist", metarule_exist("metarule_exist"), 1);
// This will fail on sFall
call assertEquals("opcode_exists", metarule_exist("opcode_exists"), 1);
if metarule_exist("opcode_exists") then begin // And example if how to use it
#define opcode_exists(opcode) sfall_func1("opcode_exists", opcode)
#define OP_GET_UPTIME 0x81B3
#define OP_NOOP 0x8000
#define OP_WRITE_INT 0x81D1
call assertEquals("opcode_exists OP_GET_UPTIME", opcode_exists(OP_GET_UPTIME), 1);
call assertEquals("opcode_exists OP_NOOP", opcode_exists(OP_NOOP), 1);
// Probably will never be implemented on fallout2-ce
call assertEquals("opcode_exists OP_WRITE_INT", opcode_exists(OP_WRITE_INT), 0);
call assertEquals("opcode_exists 0xFFFF", opcode_exists(0xFFFF), 0);
end else begin
display_msg("Opcode exists metarule is not available, skipping tests.");
end
call report_test_results("metarule_exist");
end
+11
View File
@@ -18,6 +18,17 @@ procedure assertTrue(variable desc, variable a) begin
end
end
procedure assertFalse(variable desc, variable a) begin
test_suite_assertions++;
if (a) then begin
display_msg("Assertion failed \""+desc+"\": is not false");
test_suite_errors ++;
end else if (test_suite_verbose) then begin
display_msg("Assert \""+desc+"\" ok");
end
end
procedure assertEquals(variable desc, variable a, variable b) begin
test_suite_assertions++;
+5 -18
View File
@@ -1032,9 +1032,7 @@ int _action_climb_ladder(Object* critter, Object* ladder)
reg_anim_begin(animationRequestOptions);
int tile = tileGetTileInDirection(ladder->tile, ROTATION_SE, 1);
int walkDistance = 5;
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_WALK_DISTANCE, &walkDistance);
if (actionPoints != -1 || objectGetDistanceBetween(critter, ladder) < walkDistance) {
if (actionPoints != -1 || objectWithinWalkDistance(critter, ladder)) {
animationRegisterMoveToTile(critter, tile, ladder->elevation, actionPoints, 0);
} else {
animationRegisterRunToTile(critter, tile, ladder->elevation, actionPoints, 0);
@@ -1101,9 +1099,7 @@ int _action_use_an_item_on_object(Object* user, Object* targetObj, Object* item)
reg_anim_begin(animationRequestOptions);
int walkDistance = 5;
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_WALK_DISTANCE, &walkDistance);
if (actionPoints != -1 || objectGetDistanceBetween(user, targetObj) < walkDistance) {
if (actionPoints != -1 || objectWithinWalkDistance(user, targetObj)) {
animationRegisterMoveToObject(user, targetObj, actionPoints, 0);
} else {
animationRegisterRunToObject(user, targetObj, -1, 0);
@@ -1173,15 +1169,12 @@ int actionPickUp(Object* critter, Object* item)
}
}
int walkDistance = 5;
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_WALK_DISTANCE, &walkDistance);
if (isInCombat()) {
reg_anim_begin(ANIMATION_REQUEST_RESERVED);
animationRegisterMoveToObject(critter, item, critter->data.critter.combat.ap, 0);
} else {
reg_anim_begin(critter == gDude ? ANIMATION_REQUEST_RESERVED : ANIMATION_REQUEST_UNRESERVED);
if (objectGetDistanceBetween(critter, item) >= walkDistance) {
if (!objectWithinWalkDistance(critter, item)) {
animationRegisterRunToObject(critter, item, -1, 0);
} else {
animationRegisterMoveToObject(critter, item, -1, 0);
@@ -1281,16 +1274,13 @@ int _action_loot_container(Object* critter, Object* container)
}
}
int walkDistance = 5;
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_WALK_DISTANCE, &walkDistance);
if (isInCombat()) {
reg_anim_begin(ANIMATION_REQUEST_RESERVED);
animationRegisterMoveToObject(critter, container, critter->data.critter.combat.ap, 0);
} else {
reg_anim_begin(critter == gDude ? ANIMATION_REQUEST_RESERVED : ANIMATION_REQUEST_UNRESERVED);
if (objectGetDistanceBetween(critter, container) < walkDistance) {
if (objectWithinWalkDistance(critter, container)) {
animationRegisterMoveToObject(critter, container, -1, 0);
} else {
animationRegisterRunToObject(critter, container, -1, 0);
@@ -1488,16 +1478,13 @@ int actionUseSkill(Object* user, Object* target, int skill)
}
}
int walkDistance = 5;
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_WALK_DISTANCE, &walkDistance);
if (isInCombat()) {
reg_anim_begin(ANIMATION_REQUEST_RESERVED);
animationRegisterMoveToObject(performer, target, performer->data.critter.combat.ap, 0);
} else {
reg_anim_begin(user == gDude ? ANIMATION_REQUEST_RESERVED : ANIMATION_REQUEST_UNRESERVED);
if (target != gDude) {
if (objectGetDistanceBetween(performer, target) >= walkDistance) {
if (!objectWithinWalkDistance(performer, target)) {
animationRegisterRunToObject(performer, target, -1, 0);
} else {
animationRegisterMoveToObject(performer, target, -1, 0);
+6 -4
View File
@@ -1713,16 +1713,18 @@ static bool canUseDoor(Object* critter, Object* door)
}
// 0x415EE8
int _make_path(Object* object, int from, int to, unsigned char* rotations, int a5)
int _make_path(Object* object, int from, int to, unsigned char* rotations, int requireEmptyDest)
{
return pathfinderFindPath(object, from, to, rotations, a5, _obj_blocking_at);
return pathfinderFindPath(object, from, to, rotations, requireEmptyDest, _obj_blocking_at);
}
// TODO: move pathfinding into another unit
// If requireEmptyDest == 1, will not return a path if the destination tile is blocked (according to `callback`).
// Returns path length, or 0 if no path is available.
// 0x415EFC
int pathfinderFindPath(Object* object, int from, int to, unsigned char* rotations, int a5, PathBuilderCallback* callback)
int pathfinderFindPath(Object* object, int from, int to, unsigned char* rotations, int requireEmptyDest, PathBuilderCallback* callback)
{
if (a5) {
if (requireEmptyDest) {
if (callback(object, to, object->elevation) != nullptr) {
return 0;
}
+2 -2
View File
@@ -143,8 +143,8 @@ int animationRegisterToggleOutline(Object* object, bool outline, int delay);
int animationRegisterPlaySoundEffect(Object* owner, const char* soundEffectName, int delay);
int animationRegisterAnimateForever(Object* owner, int anim, int delay);
int animationRegisterPing(int flags, int delay);
int _make_path(Object* object, int from, int to, unsigned char* a4, int a5);
int pathfinderFindPath(Object* object, int from, int to, unsigned char* rotations, int a5, PathBuilderCallback* callback);
int _make_path(Object* object, int from, int to, unsigned char* rotations, int requireEmptyDest);
int pathfinderFindPath(Object* object, int from, int to, unsigned char* rotations, int requireEmptyDest, PathBuilderCallback* callback);
int _make_straight_path(Object* object, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6);
int _make_straight_path_func(Object* object, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6, PathBuilderCallback* callback);
void _object_animate();
+14 -1
View File
@@ -1,5 +1,6 @@
#include "art.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -619,7 +620,7 @@ char* artBuildFilePath(int fid)
v2 = fid;
v10 = (fid & 0x70000000) >> 28;
v10 = FID_ROTATION(fid);
v1 = artAliasFid(fid);
if (v1 != -1) {
@@ -702,8 +703,13 @@ static int artReadList(const char* path, char** artListPtr, int* artListSizePtr)
artList[12] = '\0';
artList += 13;
count--;
}
// Sanity check. There was a bug with uncompressed database file seek
assert(count == 0);
fileClose(stream);
return 0;
@@ -1006,6 +1012,13 @@ static void artCacheFreeImpl(void* ptr)
internal_free(ptr);
}
/* FID Structure:
3 bits for rotation
4 bits for object type
8 bits for animation type
4 bits for weapon code
12 bits for frame ID
*/
static int buildFidInternal(unsigned short frmId, unsigned char weaponCode, unsigned char animType, unsigned char objectType, unsigned char rotation)
{
return ((rotation << 28) & 0x70000000) | (objectType << 24) | ((animType << 16) & 0xFF0000) | ((weaponCode << 12) & 0xF000) | (frmId & 0xFFF);
+2 -1
View File
@@ -324,7 +324,8 @@ void automapShow(bool isInGame, bool isUsingScanner)
int automapWindowX = (screenGetWidth() - AUTOMAP_WINDOW_WIDTH) / 2;
int automapWindowY = (screenGetHeight() - AUTOMAP_WINDOW_HEIGHT) / 2;
int window = windowCreate(automapWindowX, automapWindowY, AUTOMAP_WINDOW_WIDTH, AUTOMAP_WINDOW_HEIGHT, color, WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
// adding WINDOW_TRANSPARENT and WINDOW_DRAGGABLE_BY_BACKGROUND for testing temporarily
int window = windowCreate(automapWindowX, automapWindowY, AUTOMAP_WINDOW_WIDTH, AUTOMAP_WINDOW_HEIGHT, color, WINDOW_MODAL | WINDOW_MOVE_ON_TOP | WINDOW_TRANSPARENT | WINDOW_DRAGGABLE_BY_BACKGROUND);
int scannerBtn = buttonCreate(window,
111,
+7 -3
View File
@@ -36,6 +36,7 @@
#include "random.h"
#include "scripts.h"
#include "settings.h"
#include "sfall_callbacks.h"
#include "sfall_config.h"
#include "sfall_global_scripts.h"
#include "skill.h"
@@ -2620,7 +2621,7 @@ static void _combat_begin(Object* attacker)
100,
FID_ANIM_TYPE(v1->fid),
(v1->fid & 0xF000) >> 12,
(v1->fid & 0x70000000) >> 28);
FID_ROTATION(v1->fid));
reg_anim_clear(v1);
reg_anim_begin(ANIMATION_REQUEST_RESERVED);
@@ -2632,6 +2633,7 @@ static void _combat_begin(Object* attacker)
_process_bk();
}
}
sfallOnCombatStart();
}
}
@@ -2788,7 +2790,7 @@ static void _combat_over()
99,
FID_ANIM_TYPE(critter->fid),
(critter->fid & 0xF000) >> 12,
(critter->fid & 0x70000000) >> 28);
FID_ROTATION(critter->fid));
reg_anim_clear(critter);
reg_anim_begin(ANIMATION_REQUEST_RESERVED);
animationRegisterAnimate(critter, ANIM_UP_STAIRS_RIGHT, -1);
@@ -2841,6 +2843,8 @@ static void _combat_over()
queueRemoveEventsByType(gDude, EVENT_TYPE_KNOCKOUT);
knockoutEventProcess(gDude, nullptr);
}
sfallOnCombatEnd();
}
// 0x422194
@@ -3490,7 +3494,7 @@ void attackInit(Attack* attack, Object* attacker, Object* defender, int hitMode,
int _combat_attack(Object* attacker, Object* defender, int hitMode, int hitLocation)
{
if (attacker != gDude && hitMode == HIT_MODE_PUNCH && randomBetween(1, 4) == 1) {
int fid = buildFid(OBJ_TYPE_CRITTER, attacker->fid & 0xFFF, ANIM_KICK_LEG, (attacker->fid & 0xF000) >> 12, (attacker->fid & 0x70000000) >> 28);
int fid = buildFid(OBJ_TYPE_CRITTER, attacker->fid & 0xFFF, ANIM_KICK_LEG, (attacker->fid & 0xF000) >> 12, FID_ROTATION(attacker->fid));
if (artExists(fid)) {
hitMode = HIT_MODE_KICK;
}
+19 -17
View File
@@ -792,15 +792,15 @@ char* killTypeGetDescription(int killType)
}
}
// 0x42D9F4
int _critter_heal_hours(Object* critter, int a2)
// 0x42D9F4 _critter_heal_hours heals critters based on the number of elapsed hours
int _critter_heal_hours(Object* critter, int hours)
{
if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) {
return -1;
}
if (critter->data.critter.hp < critterGetStat(critter, STAT_MAXIMUM_HIT_POINTS)) {
critterAdjustHitPoints(critter, 14 * (a2 / 3));
critterAdjustHitPoints(critter, 14 * (hours / 3));
}
critter->data.critter.combat.maneuver &= ~CRITTER_MANUEVER_FLEEING;
@@ -815,7 +815,7 @@ static int _critterClearObjDrugs(Object* obj, void* data)
}
// 0x42DA64
void critterKill(Object* critter, int anim, bool a3)
void critterKill(Object* critter, int anim, bool refreshRect)
{
if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) {
return;
@@ -904,7 +904,7 @@ void critterKill(Object* critter, int anim, bool a3)
itemDestroyAllHidden(critter);
if (a3) {
if (refreshRect) {
tileWindowRefreshRect(&updatedRect, elevation);
}
@@ -1282,21 +1282,23 @@ int _critter_wake_clear(Object* obj, void* data)
}
// 0x42E4C0
int _critter_set_who_hit_me(Object* a1, Object* a2)
// note: this is sometimes called with (attacker, defender) and sometimes with (defender, attacker).
// `hitMe` may be nullptr
int _critter_set_who_hit_me(Object* critter, Object* hitMe)
{
if (a1 == nullptr) {
if (critter == nullptr) {
return -1;
}
if (a2 != nullptr && FID_TYPE(a2->fid) != OBJ_TYPE_CRITTER) {
if (hitMe != nullptr && FID_TYPE(hitMe->fid) != OBJ_TYPE_CRITTER) {
return -1;
}
if (PID_TYPE(a1->pid) == OBJ_TYPE_CRITTER) {
if (a2 == nullptr || a1->data.critter.combat.team != a2->data.critter.combat.team || (statRoll(a1, STAT_INTELLIGENCE, -1, nullptr) < 2 && (!objectIsPartyMember(a1) || !objectIsPartyMember(a2)))) {
a1->data.critter.combat.whoHitMe = a2;
if (a2 == gDude) {
reactionSetValue(a1, -3);
if (PID_TYPE(critter->pid) == OBJ_TYPE_CRITTER) {
if (hitMe == nullptr || critter->data.critter.combat.team != hitMe->data.critter.combat.team || (statRoll(critter, STAT_INTELLIGENCE, -1, nullptr) < 2 && (!objectIsPartyMember(critter) || !objectIsPartyMember(hitMe)))) {
critter->data.critter.combat.whoHitMe = hitMe;
if (hitMe == gDude) {
reactionSetValue(critter, -3);
}
}
}
@@ -1346,7 +1348,7 @@ bool _critter_can_obj_dude_rest()
}
// 0x42E62C
int critterGetMovementPointCostAdjustedForCrippledLegs(Object* critter, int actionPoints)
int critterGetMovementPointCostAdjustedForCrippledLegs(Object* critter, int distance)
{
if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) {
return 0;
@@ -1354,11 +1356,11 @@ int critterGetMovementPointCostAdjustedForCrippledLegs(Object* critter, int acti
int flags = critter->data.critter.combat.results;
if ((flags & DAM_CRIP_LEG_LEFT) != 0 && (flags & DAM_CRIP_LEG_RIGHT) != 0) {
return 8 * actionPoints;
return 8 * distance;
} else if ((flags & DAM_CRIP_LEG_ANY) != 0) {
return 4 * actionPoints;
return 4 * distance;
} else {
return actionPoints;
return distance;
}
}
+5 -5
View File
@@ -43,8 +43,8 @@ int killsSave(File* stream);
int critterGetKillType(Object* critter);
char* killTypeGetName(int killType);
char* killTypeGetDescription(int killType);
int _critter_heal_hours(Object* obj, int a2);
void critterKill(Object* critter, int anim, bool a3);
int _critter_heal_hours(Object* obj, int hours);
void critterKill(Object* critter, int anim, bool refreshRect);
int critterGetExp(Object* critter);
bool critterIsActive(Object* critter);
bool critterIsDead(Object* critter);
@@ -64,11 +64,11 @@ int _critter_sneak_clear(Object* obj, void* data);
bool dudeIsSneaking();
int knockoutEventProcess(Object* obj, void* data);
int _critter_wake_clear(Object* obj, void* data);
int _critter_set_who_hit_me(Object* a1, Object* a2);
int _critter_set_who_hit_me(Object* critter, Object* hitMe);
bool _critter_can_obj_dude_rest();
int critterGetMovementPointCostAdjustedForCrippledLegs(Object* critter, int a2);
int critterGetMovementPointCostAdjustedForCrippledLegs(Object* critter, int distance);
bool critterIsEncumbered(Object* critter);
bool critterIsFleeing(Object* a1);
bool critterIsFleeing(Object* critter);
bool _critter_flag_check(int pid, int flag);
void critter_flag_set(int pid, int flag);
void critter_flag_unset(int pid, int flag);
+11 -3
View File
@@ -621,12 +621,20 @@ int fileNameListInit(const char* pattern, char*** fileNameListPtr, int a3, int a
int fileNamesLength = xlist->fileNamesLength;
for (int index = 0; index < fileNamesLength - 1; index++) {
if (compat_stricmp(xlist->fileNames[index], xlist->fileNames[index + 1]) == 0) {
char* temp = xlist->fileNames[index + 1];
memmove(&(xlist->fileNames[index + 1]), &(xlist->fileNames[index + 2]), sizeof(*xlist->fileNames) * (xlist->fileNamesLength - index - 1));
// If the current item is equal to the next one, then remove it.
// Example 1:
// fileNamesLength = 6
// 0 1 2 3 4 5
// [a, b, c1, c2, d, e]
// duplicate found at index=2
// move items 3,4,5 to 2,3,4 and move duplicate to the end
// [a, b, c2, d, e, c1]
char* temp = xlist->fileNames[index];
memmove(&(xlist->fileNames[index]), &(xlist->fileNames[index + 1]), sizeof(*xlist->fileNames) * (xlist->fileNamesLength - index - 1));
xlist->fileNames[xlist->fileNamesLength - 1] = temp;
fileNamesLength--;
index--;
index--; // Repeat the same index if there are more duplicates.
}
}
+10 -9
View File
@@ -154,10 +154,11 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
MessageListItem messageListItem;
int savedFont = fontGetCurrent();
bool v86 = false;
bool initializedButtons = false;
bool hasTwoButtons = false;
if (a8 != nullptr) {
// TODO: a8 seems to be a way to customize "NO", but is unused.
hasTwoButtons = true;
}
@@ -169,7 +170,7 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
if ((flags & DIALOG_BOX_YES_NO) != 0) {
hasTwoButtons = true;
flags |= DIALOG_BOX_LARGE;
flags &= ~DIALOG_BOX_0x20;
flags &= ~DIALOG_BOX_NO_BUTTONS;
}
int maximumLineWidth = 0;
@@ -227,7 +228,7 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
FrmImage buttonNormalFrmImage;
FrmImage buttonPressedFrmImage;
if ((flags & DIALOG_BOX_0x20) == 0) {
if ((flags & DIALOG_BOX_NO_BUTTONS) == 0) {
int doneBoxFid = buildFid(OBJ_TYPE_INTERFACE, 209, 0, 0, 0);
if (!doneBoxFrmImage.lock(doneBoxFid)) {
fontSetCurrent(savedFont);
@@ -304,13 +305,13 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
buttonSetCallbacks(btn, _gsound_red_butt_press, _gsound_red_butt_release);
}
v86 = true;
initializedButtons = true;
}
if (hasTwoButtons && dialogType == DIALOG_TYPE_LARGE) {
if (v86) {
if (initializedButtons) {
if ((flags & DIALOG_BOX_YES_NO) != 0) {
a8 = getmsg(&messageList, &messageListItem, 102);
a8 = getmsg(&messageList, &messageListItem, 102); // 102 - NO
}
fontSetCurrent(103);
@@ -410,7 +411,7 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
buttonSetCallbacks(btn, _gsound_red_butt_press, _gsound_red_butt_release);
}
v86 = true;
initializedButtons = true;
}
}
@@ -537,7 +538,7 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
} else if (keyCode == KEY_ESCAPE || keyCode == 501) {
rc = 0;
} else {
if ((flags & 0x10) != 0) {
if ((flags & DIALOG_BOX_YES_NO) != 0) {
if (keyCode == KEY_UPPERCASE_Y || keyCode == KEY_LOWERCASE_Y) {
rc = 1;
} else if (keyCode == KEY_UPPERCASE_N || keyCode == KEY_LOWERCASE_N) {
@@ -557,7 +558,7 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
windowDestroy(win);
fontSetCurrent(savedFont);
if (v86) {
if (initializedButtons) {
messageListFree(&messageList);
}
+5 -5
View File
@@ -4,12 +4,12 @@
namespace fallout {
typedef enum DialogBoxOptions {
DIALOG_BOX_LARGE = 0x01,
DIALOG_BOX_MEDIUM = 0x02,
DIALOG_BOX_NO_HORIZONTAL_CENTERING = 0x04,
DIALOG_BOX_NO_VERTICAL_CENTERING = 0x08,
DIALOG_BOX_LARGE = 0x01, // sfall calls this NORMAL
DIALOG_BOX_MEDIUM = 0x02, // sfall: SMALL
DIALOG_BOX_NO_HORIZONTAL_CENTERING = 0x04, // sfall: ALIGN_LEFT
DIALOG_BOX_NO_VERTICAL_CENTERING = 0x08, // sfall: ALIGN_TOP
DIALOG_BOX_YES_NO = 0x10,
DIALOG_BOX_0x20 = 0x20,
DIALOG_BOX_NO_BUTTONS = 0x20, // sfall: CLEAN
} DialogBoxOptions;
int showDialogBox(const char* title, const char** body, int bodyLength, int x, int y, int titleColor, const char* a8, int bodyColor, int flags);

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