Compare commits

..
Author SHA1 Message Date
google-labs-jules[bot] 6c42bb2630 Here's the rewritten message:
Add opcodes for getting world map position

I've implemented `get_world_map_x_pos` (0x8173) and `get_world_map_y_pos` (0x8174) sfall opcodes. These functions allow your scripts to retrieve the player's current X and Y coordinates on the world map.

The implementation mirrors the existing `set_world_map_pos` functionality by calling the underlying `wmGetPartyWorldPos` engine function.

I've added a new test script `sfall_testing/gl_test_worldmap.ssl` which includes cases for:
- Basic retrieval of X and Y coordinates.
- Retrieval after multiple position sets.
- Retrieval of zero coordinates.
- Retrieval of negative coordinates (assuming engine support).

Note: I wasn't able to automatically register the new test script in CMakeLists.txt as the specified variable was not found. I also encountered an issue that prevented me from verifying the test execution.
2025-06-09 06:26:28 +00:00
99 changed files with 657 additions and 12399 deletions
+6 -47
View File
@@ -14,16 +14,6 @@ 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
@@ -86,10 +76,6 @@ 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: |
@@ -105,37 +91,10 @@ jobs:
clean: false
single-commit: true
- 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: |
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:
SITE_ORIGIN: https://${{ github.repository_owner }}.github.io
SITE_BASE: ${{ steps.set-path.outputs.SITE_BASE }}
- name: Update deployment status
- name: Comment preview URL on PR
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 }}/
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 }}
+5 -5
View File
@@ -87,7 +87,7 @@ jobs:
uses: actions/cache@v4
with:
path: os/android/app/.cxx
key: android-cmake-v101
key: android-cmake-v2
- 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-v101
key: ios-cmake-v5
- name: Configure
run: |
@@ -183,7 +183,7 @@ jobs:
uses: actions/cache@v4
with:
path: out
key: linux-${{ matrix.arch }}-cmake-v101
key: linux-${{ matrix.arch }}-cmake-v3
- name: Configure
run: |
@@ -288,7 +288,7 @@ jobs:
uses: actions/cache@v4
with:
path: out
key: macos-cmake-v101
key: macos-cmake-v6
- name: Configure
run: |
@@ -337,7 +337,7 @@ jobs:
uses: actions/cache@v4
with:
path: out
key: windows-${{ matrix.arch }}-cmake-v101
key: windows-${{ matrix.arch }}-cmake-v3
- name: Configure
shell: cmd
+1 -10
View File
@@ -12,7 +12,6 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: write
deployments: write
steps:
- name: Checkout gh-pages branch
uses: actions/checkout@v4
@@ -44,12 +43,4 @@ jobs:
else
git commit --amend -m "đźš® Remove preview for closed PR #${{ github.event.pull_request.number }}"
git push --force
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
fi
-6
View File
@@ -288,16 +288,10 @@ 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 -12
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. **Note**: You must use "Offline backup game installer", not the main game installer.
- Or if you're a Terminal user and have Homebrew installed you can extract the needed files from the GoG installer:
```console
$ brew install innoextract
@@ -126,17 +126,6 @@ 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:
-33
View File
@@ -1,33 +0,0 @@
#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
-69
View File
@@ -1,69 +0,0 @@
#include "lib.arrays.h" // For arrays_equal, len_array, etc.
#include "test_utils.h" // For assertEquals, VALTYPE_STR, report_test_results
// To test: copy test.ini into the game folder before running
procedure ini_test_suite begin
variable result_array, expected_array, key_type, value_type, count, key_val_pair;
display_msg("--- Testing ini functions");
// Test Case 1: Valid file, valid section
result_array := get_ini_section("test.ini", "ValidSection");
expected_array := {"Key1": "Value1", "Key2": "2"};
call assertEquals("TC1 Size", len_array(result_array), 2);
call assertEquals("TC1 Content", arrays_equal(result_array, expected_array), true);
count := 0;
foreach key_val_pair in result_array begin
count += 1;
call assertEquals("TC1 Key Type " + count, typeof(key_val_pair[0]), VALTYPE_STR);
call assertEquals("TC1 Value Type " + count, typeof(key_val_pair[1]), VALTYPE_STR);
end
// basic fetchers
call assertEquals("TC2 Specific Key1 Value", result_array["Key1"], "Value1");
call assertEquals("TC2 get_ini_setting string", get_ini_setting("test.ini|ValidSection|Key1"), 0);
call assertEquals("TC2 get_ini_string string", get_ini_string("test.ini|ValidSection|Key1"), "Value1");
call assertEquals("TC2 get_ini_setting int", get_ini_setting("test.ini|ValidSection|Key2"), 2);
call assertEquals("TC2 get_ini_string int", get_ini_string("test.ini|ValidSection|Key2"), "2");
// INI file not found
result_array := get_ini_section("nonexistent.ini", "AnySection");
call assertEquals("TC3 Size", len_array(result_array), 0);
call assertEquals("TC3 Is Map", array_key(result_array, -1), 1); // Should still be an associative array
call assertEquals("TC3 get_ini_setting", get_ini_setting("notexist.ini|ValidSection|Key2"), 0);
call assertEquals("TC3 get_ini_string", get_ini_string("notexist.ini|ValidSection|Key2"), "");
// Valid file, section not found
result_array := get_ini_section("test.ini", "NonExistentSection");
call assertEquals("TC4 Size", len_array(result_array), 0);
call assertEquals("TC4 Is Map", array_key(result_array, -1), 1);
call assertEquals("TC4 get_ini_setting", get_ini_setting("test.ini|NonExistentSection|Key2"), 0);
call assertEquals("TC4 get_ini_string", get_ini_string("test.ini|NonExistentSection|Key2"), "");
// Valid file, empty section
result_array := get_ini_section("test.ini", "EmptySection");
call assertEquals("TC5 Size", len_array(result_array), 0);
call assertEquals("TC5 Is Map", array_key(result_array, -1), 1);
call assertEquals("TC5 get_ini_setting", get_ini_setting("test.ini|EmptySection|Key2"), 0);
call assertEquals("TC5 get_ini_string", get_ini_string("test.ini|EmptySection|Key2"), "");
// set ini setting
set_ini_setting("test_set.ini|ValidSection|Key3", "OldValue");
call assertEquals("TC6 set_ini_setting 1", get_ini_string("test_set.ini|ValidSection|Key3"), "OldValue");
set_ini_setting("test_set.ini|ValidSection|Key3", "NewValue");
call assertEquals("TC6 set_ini_setting 2", get_ini_string("test_set.ini|ValidSection|Key3"), "NewValue");
set_ini_setting("test_set.ini|ValidSection|Key3", 2);
call assertEquals("TC6 set_ini_setting int", get_ini_setting("test_set.ini|ValidSection|Key3"), 2);
// get_init_sections
result_array := get_ini_sections("test.ini");
call assertEquals("TC7 get_ini_sections size", len_array(result_array), 3);
call assertTrue("TC7 get_ini_sections", scan_array(result_array, "TestMisc") >= 0);
call report_test_results("ini");
end
procedure start begin
call ini_test_suite();
end
-14
View File
@@ -1,14 +0,0 @@
#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
@@ -1,30 +0,0 @@
#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
-7
View File
@@ -1,7 +0,0 @@
#include "sfall.h"
procedure start begin
// this isn't a unit test, just a way to easily test different values manually
display_msg("Setting world map mult");
set_map_time_multi(1.0);
end
-16
View File
@@ -1,16 +0,0 @@
[ValidSection]
Key1=Value1
Key2=2
[SectionWithSpaces]
LeadingSpaceKey = LeadingSpaceValue
TrailingSpaceKey = TrailingSpaceValue
Both Sides Space Key = Both Sides Space Value
SimpleKey=SimpleValue
[EmptySection]
; This is TestMisc, it's not empty but won't be directly tested for content here.
[TestMisc]
Alpha=Beta
Gamma=Delta
-22
View File
@@ -7,28 +7,6 @@ variable test_suite_errors := 0;
variable test_suite_verbose := false;
variable test_suite_assertions := 0;
procedure assertTrue(variable desc, variable a) begin
test_suite_assertions++;
if (not a) then begin
display_msg("Assertion failed \""+desc+"\": is not true");
test_suite_errors ++;
end else if (test_suite_verbose) then begin
display_msg("Assert \""+desc+"\" ok");
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++;
+18 -5
View File
@@ -1032,7 +1032,9 @@ int _action_climb_ladder(Object* critter, Object* ladder)
reg_anim_begin(animationRequestOptions);
int tile = tileGetTileInDirection(ladder->tile, ROTATION_SE, 1);
if (actionPoints != -1 || objectWithinWalkDistance(critter, ladder)) {
int walkDistance = 5;
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_WALK_DISTANCE, &walkDistance);
if (actionPoints != -1 || objectGetDistanceBetween(critter, ladder) < walkDistance) {
animationRegisterMoveToTile(critter, tile, ladder->elevation, actionPoints, 0);
} else {
animationRegisterRunToTile(critter, tile, ladder->elevation, actionPoints, 0);
@@ -1099,7 +1101,9 @@ int _action_use_an_item_on_object(Object* user, Object* targetObj, Object* item)
reg_anim_begin(animationRequestOptions);
if (actionPoints != -1 || objectWithinWalkDistance(user, targetObj)) {
int walkDistance = 5;
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_WALK_DISTANCE, &walkDistance);
if (actionPoints != -1 || objectGetDistanceBetween(user, targetObj) < walkDistance) {
animationRegisterMoveToObject(user, targetObj, actionPoints, 0);
} else {
animationRegisterRunToObject(user, targetObj, -1, 0);
@@ -1169,12 +1173,15 @@ 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 (!objectWithinWalkDistance(critter, item)) {
if (objectGetDistanceBetween(critter, item) >= walkDistance) {
animationRegisterRunToObject(critter, item, -1, 0);
} else {
animationRegisterMoveToObject(critter, item, -1, 0);
@@ -1274,13 +1281,16 @@ 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 (objectWithinWalkDistance(critter, container)) {
if (objectGetDistanceBetween(critter, container) < walkDistance) {
animationRegisterMoveToObject(critter, container, -1, 0);
} else {
animationRegisterRunToObject(critter, container, -1, 0);
@@ -1478,13 +1488,16 @@ 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 (!objectWithinWalkDistance(performer, target)) {
if (objectGetDistanceBetween(performer, target) >= walkDistance) {
animationRegisterRunToObject(performer, target, -1, 0);
} else {
animationRegisterMoveToObject(performer, target, -1, 0);
+4 -6
View File
@@ -1713,18 +1713,16 @@ static bool canUseDoor(Object* critter, Object* door)
}
// 0x415EE8
int _make_path(Object* object, int from, int to, unsigned char* rotations, int requireEmptyDest)
int _make_path(Object* object, int from, int to, unsigned char* rotations, int a5)
{
return pathfinderFindPath(object, from, to, rotations, requireEmptyDest, _obj_blocking_at);
return pathfinderFindPath(object, from, to, rotations, a5, _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 requireEmptyDest, PathBuilderCallback* callback)
int pathfinderFindPath(Object* object, int from, int to, unsigned char* rotations, int a5, PathBuilderCallback* callback)
{
if (requireEmptyDest) {
if (a5) {
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* rotations, int requireEmptyDest);
int pathfinderFindPath(Object* object, int from, int to, unsigned char* rotations, int requireEmptyDest, PathBuilderCallback* callback);
int _make_path(Object* object, int from, int to, unsigned char* a4, int a5);
int pathfinderFindPath(Object* object, int from, int to, unsigned char* rotations, int a5, PathBuilderCallback* callback);
int _make_straight_path(Object* 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();
+1 -14
View File
@@ -1,6 +1,5 @@
#include "art.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -620,7 +619,7 @@ char* artBuildFilePath(int fid)
v2 = fid;
v10 = FID_ROTATION(fid);
v10 = (fid & 0x70000000) >> 28;
v1 = artAliasFid(fid);
if (v1 != -1) {
@@ -703,13 +702,8 @@ 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;
@@ -1012,13 +1006,6 @@ 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);
+1 -2
View File
@@ -324,8 +324,7 @@ void automapShow(bool isInGame, bool isUsingScanner)
int automapWindowX = (screenGetWidth() - AUTOMAP_WINDOW_WIDTH) / 2;
int automapWindowY = (screenGetHeight() - AUTOMAP_WINDOW_HEIGHT) / 2;
// 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 window = windowCreate(automapWindowX, automapWindowY, AUTOMAP_WINDOW_WIDTH, AUTOMAP_WINDOW_HEIGHT, color, WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
int scannerBtn = buttonCreate(window,
111,
+8 -12
View File
@@ -36,7 +36,6 @@
#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"
@@ -2621,7 +2620,7 @@ static void _combat_begin(Object* attacker)
100,
FID_ANIM_TYPE(v1->fid),
(v1->fid & 0xF000) >> 12,
FID_ROTATION(v1->fid));
(v1->fid & 0x70000000) >> 28);
reg_anim_clear(v1);
reg_anim_begin(ANIMATION_REQUEST_RESERVED);
@@ -2633,7 +2632,6 @@ static void _combat_begin(Object* attacker)
_process_bk();
}
}
sfallOnCombatStart();
}
}
@@ -2790,7 +2788,7 @@ static void _combat_over()
99,
FID_ANIM_TYPE(critter->fid),
(critter->fid & 0xF000) >> 12,
FID_ROTATION(critter->fid));
(critter->fid & 0x70000000) >> 28);
reg_anim_clear(critter);
reg_anim_begin(ANIMATION_REQUEST_RESERVED);
animationRegisterAnimate(critter, ANIM_UP_STAIRS_RIGHT, -1);
@@ -2843,8 +2841,6 @@ static void _combat_over()
queueRemoveEventsByType(gDude, EVENT_TYPE_KNOCKOUT);
knockoutEventProcess(gDude, nullptr);
}
sfallOnCombatEnd();
}
// 0x422194
@@ -3494,7 +3490,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, FID_ROTATION(attacker->fid));
int fid = buildFid(OBJ_TYPE_CRITTER, attacker->fid & 0xFFF, ANIM_KICK_LEG, (attacker->fid & 0xF000) >> 12, (attacker->fid & 0x70000000) >> 28);
if (artExists(fid)) {
hitMode = HIT_MODE_KICK;
}
@@ -3845,14 +3841,14 @@ static int attackCompute(Attack* attack)
}
int attackType = weaponGetAttackTypeForHitMode(attack->weapon, attack->hitMode);
int roundsHitMainTarget = 1;
int ammoQuantity = 1;
int damageMultiplier = 2;
int roundsSpent = 1;
int v26 = 1;
int roll;
if (anim == ANIM_FIRE_BURST || anim == ANIM_FIRE_CONTINUOUS) {
roll = _compute_spray(attack, accuracy, &roundsHitMainTarget, &roundsSpent, anim);
roll = _compute_spray(attack, accuracy, &ammoQuantity, &v26, anim);
} else {
int chance = critterGetStat(attack->attacker, STAT_CRITICAL_CHANCE);
roll = randomRoll(accuracy, chance - hit_location_penalty[attack->defenderHitLocation], nullptr);
@@ -3890,7 +3886,7 @@ static int attackCompute(Attack* attack)
}
if (attackType == ATTACK_TYPE_RANGED) {
attack->ammoQuantity = roundsSpent;
attack->ammoQuantity = v26;
if (roll == ROLL_SUCCESS && attack->attacker == gDude) {
if (perkGetRank(gDude, PERK_SNIPER) != 0) {
@@ -3928,7 +3924,7 @@ static int attackCompute(Attack* attack)
case ROLL_SUCCESS:
attack->attackerFlags |= DAM_HIT;
attackComputeEnhancedKnockout(attack);
attackComputeDamage(attack, roundsHitMainTarget, damageMultiplier);
attackComputeDamage(attack, ammoQuantity, damageMultiplier);
break;
case ROLL_FAILURE:
if (attackType == ATTACK_TYPE_RANGED || attackType == ATTACK_TYPE_THROW) {
+17 -19
View File
@@ -792,15 +792,15 @@ char* killTypeGetDescription(int killType)
}
}
// 0x42D9F4 _critter_heal_hours heals critters based on the number of elapsed hours
int _critter_heal_hours(Object* critter, int hours)
// 0x42D9F4
int _critter_heal_hours(Object* critter, int a2)
{
if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) {
return -1;
}
if (critter->data.critter.hp < critterGetStat(critter, STAT_MAXIMUM_HIT_POINTS)) {
critterAdjustHitPoints(critter, 14 * (hours / 3));
critterAdjustHitPoints(critter, 14 * (a2 / 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 refreshRect)
void critterKill(Object* critter, int anim, bool a3)
{
if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) {
return;
@@ -904,7 +904,7 @@ void critterKill(Object* critter, int anim, bool refreshRect)
itemDestroyAllHidden(critter);
if (refreshRect) {
if (a3) {
tileWindowRefreshRect(&updatedRect, elevation);
}
@@ -1282,23 +1282,21 @@ int _critter_wake_clear(Object* obj, void* data)
}
// 0x42E4C0
// 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)
int _critter_set_who_hit_me(Object* a1, Object* a2)
{
if (critter == nullptr) {
if (a1 == nullptr) {
return -1;
}
if (hitMe != nullptr && FID_TYPE(hitMe->fid) != OBJ_TYPE_CRITTER) {
if (a2 != nullptr && FID_TYPE(a2->fid) != OBJ_TYPE_CRITTER) {
return -1;
}
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);
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);
}
}
}
@@ -1348,7 +1346,7 @@ bool _critter_can_obj_dude_rest()
}
// 0x42E62C
int critterGetMovementPointCostAdjustedForCrippledLegs(Object* critter, int distance)
int critterGetMovementPointCostAdjustedForCrippledLegs(Object* critter, int actionPoints)
{
if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) {
return 0;
@@ -1356,11 +1354,11 @@ int critterGetMovementPointCostAdjustedForCrippledLegs(Object* critter, int dist
int flags = critter->data.critter.combat.results;
if ((flags & DAM_CRIP_LEG_LEFT) != 0 && (flags & DAM_CRIP_LEG_RIGHT) != 0) {
return 8 * distance;
return 8 * actionPoints;
} else if ((flags & DAM_CRIP_LEG_ANY) != 0) {
return 4 * distance;
return 4 * actionPoints;
} else {
return distance;
return actionPoints;
}
}
+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 hours);
void critterKill(Object* critter, int anim, bool refreshRect);
int _critter_heal_hours(Object* obj, int a2);
void critterKill(Object* critter, int anim, bool a3);
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* critter, Object* hitMe);
int _critter_set_who_hit_me(Object* a1, Object* a2);
bool _critter_can_obj_dude_rest();
int critterGetMovementPointCostAdjustedForCrippledLegs(Object* critter, int distance);
int critterGetMovementPointCostAdjustedForCrippledLegs(Object* critter, int a2);
bool critterIsEncumbered(Object* critter);
bool critterIsFleeing(Object* critter);
bool critterIsFleeing(Object* a1);
bool _critter_flag_check(int pid, int flag);
void critter_flag_set(int pid, int flag);
void critter_flag_unset(int pid, int flag);

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