mirror of
https://github.com/fallout2-ce/fallout2-ce.git
synced 2026-07-27 16:47:11 -07:00
Compare commits
130
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a470bdc6af | ||
|
|
22ba94eca3 | ||
|
|
7e49ed8545 | ||
|
|
3249b7dc07 | ||
|
|
f291f52a61 | ||
|
|
4bc2ed5d0d | ||
|
|
b6a522a065 | ||
|
|
bbb01398ef | ||
|
|
fb5e1f9c07 | ||
|
|
625928265e | ||
|
|
a6ccac2a58 | ||
|
|
19e72a5b6d | ||
|
|
44f5477ee5 | ||
|
|
8d0587566f | ||
|
|
0ed3725124 | ||
|
|
86787e2c97 | ||
|
|
f60b773ed4 | ||
|
|
b6384ce8e9 | ||
|
|
15aa1db286 | ||
|
|
763169f33e | ||
|
|
58b811f2ae | ||
|
|
bfcfaa9540 | ||
|
|
491806686b | ||
|
|
245705c468 | ||
|
|
43c0b79f3f | ||
|
|
216710f86c | ||
|
|
a478bad7fa | ||
|
|
9a3ec752dd | ||
|
|
f940766379 | ||
|
|
87e1d1f11b | ||
|
|
5d17e0ef89 | ||
|
|
2bf08228c5 | ||
|
|
571b43410d | ||
|
|
f0e6002df9 | ||
|
|
d593c481f2 | ||
|
|
76f3d82679 | ||
|
|
0e1270a606 | ||
|
|
55a32a00ab | ||
|
|
7b9b545baa | ||
|
|
a96edd9f0c | ||
|
|
55c528f346 | ||
|
|
5c28dc798f | ||
|
|
7ef6943588 | ||
|
|
40bbd6c02a | ||
|
|
45c491c11f | ||
|
|
18c05d438e | ||
|
|
c3a501a644 | ||
|
|
2767a399c5 | ||
|
|
1237d8561d | ||
|
|
89b6812711 | ||
|
|
339146c5b3 | ||
|
|
3949c5d7c1 | ||
|
|
84a64035e8 | ||
|
|
164be5105a | ||
|
|
d532e4c398 | ||
|
|
950c12dbf7 | ||
|
|
2be9b7db65 | ||
|
|
138ce8be9b | ||
|
|
96b9323a97 | ||
|
|
7c6d339e3d | ||
|
|
2ec8240820 | ||
|
|
ca72573c14 | ||
|
|
9974a23c1e | ||
|
|
cd6cce3f34 | ||
|
|
361b9a0b30 | ||
|
|
7c7cb87f65 | ||
|
|
20bcb2360c | ||
|
|
75dbc98321 | ||
|
|
d792cfa32e | ||
|
|
e81a07dcaf | ||
|
|
0d78119680 | ||
|
|
79d0fcfcaa | ||
|
|
2d5e69d740 | ||
|
|
b5cf3ee148 | ||
|
|
a571a985c6 | ||
|
|
6ccba74836 | ||
|
|
30c3af01f5 | ||
|
|
fd25944a9e | ||
|
|
41c5d77f89 | ||
|
|
75513058a5 | ||
|
|
0cf55eca75 | ||
|
|
e6cbdd5466 | ||
|
|
90f634b285 | ||
|
|
666ee6f41c | ||
|
|
4342aefa5d | ||
|
|
7224c7d7fb | ||
|
|
dff7529707 | ||
|
|
2435b33480 | ||
|
|
c4652d799a | ||
|
|
5140b00ccf | ||
|
|
f1ba068d05 | ||
|
|
01db7078c7 | ||
|
|
64fd857da6 | ||
|
|
555652d9c3 | ||
|
|
8c35203b7d | ||
|
|
0303a00299 | ||
|
|
dd16e374ee | ||
|
|
ce4495589b | ||
|
|
af350914ef | ||
|
|
8dd2afc4fa | ||
|
|
240aa8a212 | ||
|
|
c58b5eac1d | ||
|
|
0bd8a76b05 | ||
|
|
853266b380 | ||
|
|
0adc9c9e77 | ||
|
|
150e734955 | ||
|
|
d0ba0151d1 | ||
|
|
67c70661a6 | ||
|
|
9c4efeb59d | ||
|
|
8bd87be687 | ||
|
|
8101c78e78 | ||
|
|
9b5737a14e | ||
|
|
3d4c073856 | ||
|
|
f868ac0e1a | ||
|
|
cd6ca76988 | ||
|
|
5d3cbd567a | ||
|
|
70ef1d4009 | ||
|
|
3acf0ac6c4 | ||
|
|
a73c336ead | ||
|
|
d46aea53bd | ||
|
|
41a7c97e69 | ||
|
|
2ca921c525 | ||
|
|
e676089f83 | ||
|
|
e119aa4c4d | ||
|
|
ea66287551 | ||
|
|
db07e95f5b | ||
|
|
acd0413626 | ||
|
|
196ad2267f | ||
|
|
22a864a0c9 | ||
|
|
252d7fe83e |
@@ -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
|
||||
|
||||
@@ -76,6 +86,10 @@ jobs:
|
||||
SITE_BASE: ${{ steps.set-path.outputs.SITE_BASE }}
|
||||
F2_GAME_VERSION: Game=${{github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Place hash file
|
||||
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository)
|
||||
run: echo "$GITHUB_SHA" > fallout2-ce-ems/dist/site_hash.txt
|
||||
|
||||
- name: Install rsync (required for the next step)
|
||||
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository)
|
||||
run: |
|
||||
@@ -91,10 +105,37 @@ jobs:
|
||||
clean: false
|
||||
single-commit: true
|
||||
|
||||
- name: Comment preview URL on PR
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
|
||||
|
||||
|
||||
- name: Check that site is deployed
|
||||
if: github.event_name == 'push' || (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 }}/"
|
||||
URL="$SITE_ORIGIN""$SITE_BASE""site_hash.txt"
|
||||
echo "Expecting $GITHUB_SHA from $URL"
|
||||
for i in `seq 1 80`; do
|
||||
got="$(curl -fsSL --max-time 10 "$URL" | tr -d '\r\n' || true)"
|
||||
if [ "$got" = "$GITHUB_SHA" ]; then
|
||||
echo "âś… Match on attempt $i: ${got}"
|
||||
exit 0
|
||||
fi
|
||||
echo "Received $got, waiting"
|
||||
sleep 5
|
||||
done
|
||||
exit 1
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SITE_ORIGIN: https://${{ github.repository_owner }}.github.io
|
||||
SITE_BASE: ${{ steps.set-path.outputs.SITE_BASE }}
|
||||
|
||||
- name: Update deployment status
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
|
||||
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 }}/
|
||||
|
||||
|
||||
@@ -87,7 +87,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 +128,7 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: out
|
||||
key: ios-cmake-v100
|
||||
key: ios-cmake-v101
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
@@ -183,7 +183,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 +288,7 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: out
|
||||
key: macos-cmake-v100
|
||||
key: macos-cmake-v101
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
@@ -337,7 +337,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -126,6 +126,17 @@ IFACE_BAR_SIDES_ORI=0
|
||||
;if IFACE_BAR_WIDTH=640 - Interface bar will remain at it's original width.
|
||||
;if IFACE_BAR_WIDTH=800 - Interface bar will use 800pix wide asset from f2_res.dat.
|
||||
;IFACE_BAR_WIDTH=640
|
||||
|
||||
[STATIC_SCREENS]
|
||||
;if SPLASH_SCRN_SIZE=0 - Splash screen shows at original size if it fits, otherwise scales down while preserving aspect ratio.
|
||||
;if SPLASH_SCRN_SIZE=1 - Splash screen scales to fit the screen while preserving aspect ratio.
|
||||
;if SPLASH_SCRN_SIZE=2 - Splash screen stretches to fill the entire screen.
|
||||
SPLASH_SCRN_SIZE=0
|
||||
|
||||
[MAPS]
|
||||
;if IGNORE_MAP_EDGES=0 - Hi-Res map scroll edges are enabled.
|
||||
;if IGNORE_MAP_EDGES=1 - Hi-Res map scroll edges are ignored.
|
||||
IGNORE_MAP_EDGES=0
|
||||
```
|
||||
|
||||
Recommendations:
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
+12
-8
@@ -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;
|
||||
}
|
||||
@@ -3841,14 +3845,14 @@ static int attackCompute(Attack* attack)
|
||||
}
|
||||
|
||||
int attackType = weaponGetAttackTypeForHitMode(attack->weapon, attack->hitMode);
|
||||
int ammoQuantity = 1;
|
||||
int roundsHitMainTarget = 1;
|
||||
int damageMultiplier = 2;
|
||||
int v26 = 1;
|
||||
int roundsSpent = 1;
|
||||
|
||||
int roll;
|
||||
|
||||
if (anim == ANIM_FIRE_BURST || anim == ANIM_FIRE_CONTINUOUS) {
|
||||
roll = _compute_spray(attack, accuracy, &ammoQuantity, &v26, anim);
|
||||
roll = _compute_spray(attack, accuracy, &roundsHitMainTarget, &roundsSpent, anim);
|
||||
} else {
|
||||
int chance = critterGetStat(attack->attacker, STAT_CRITICAL_CHANCE);
|
||||
roll = randomRoll(accuracy, chance - hit_location_penalty[attack->defenderHitLocation], nullptr);
|
||||
@@ -3886,7 +3890,7 @@ static int attackCompute(Attack* attack)
|
||||
}
|
||||
|
||||
if (attackType == ATTACK_TYPE_RANGED) {
|
||||
attack->ammoQuantity = v26;
|
||||
attack->ammoQuantity = roundsSpent;
|
||||
|
||||
if (roll == ROLL_SUCCESS && attack->attacker == gDude) {
|
||||
if (perkGetRank(gDude, PERK_SNIPER) != 0) {
|
||||
@@ -3924,7 +3928,7 @@ static int attackCompute(Attack* attack)
|
||||
case ROLL_SUCCESS:
|
||||
attack->attackerFlags |= DAM_HIT;
|
||||
attackComputeEnhancedKnockout(attack);
|
||||
attackComputeDamage(attack, ammoQuantity, damageMultiplier);
|
||||
attackComputeDamage(attack, roundsHitMainTarget, damageMultiplier);
|
||||
break;
|
||||
case ROLL_FAILURE:
|
||||
if (attackType == ATTACK_TYPE_RANGED || attackType == ATTACK_TYPE_THROW) {
|
||||
|
||||
+19
-17
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
Reference in New Issue
Block a user