Compare commits

..
Author SHA1 Message Date
cambragol 3822e25201 Update inventory.cc
Added a comment regarding the button flaw on Inventory, Use On and Loot screens.

Also noted that scaling and flipping the main menu button is a temp solution/stop-gap measure.
2025-05-25 02:21:55 +09:00
Mike Klaas 2ba6cbee3a Merge branch 'Splash-and-Help-stretching' into Inventory-Buttons-Fix 2025-05-21 09:02:35 -07:00
cambragol cb90129ebd Merge branch 'main' into Inventory-Buttons-Fix 2025-05-21 09:16:34 +09:00
cambragol 13d5fd2e83 Update inventory.cc
flipped buttons to match top right light source
2025-05-21 08:39:17 +09:00
cambragol 7c3cbbac05 Update inventory.cc 2025-05-19 21:52:43 +09:00
cambragol a5a317ab40 Update inventory.cc 2025-05-19 21:42:52 +09:00
cambragol 372483a5ca Update game.cc
synchronize some settings
2025-05-18 17:00:52 +09:00
cambragol d46549dd11 Update game.cc 2025-05-18 16:43:26 +09:00
cambragol c6970e35de Update draw.h 2025-05-18 16:40:13 +09:00
cambragol 68e7d3e903 Update draw.cc 2025-05-18 16:39:23 +09:00
47 changed files with 680 additions and 2670 deletions
-42
View File
@@ -1,42 +0,0 @@
name: Auto Format with Clang
on:
pull_request:
types: [opened, synchronize]
push:
branches: [main]
jobs:
format:
runs-on: ubuntu-latest
if: github.actor != 'github-actions[bot]' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref || github.ref_name }}
token: ${{ secrets.PAT_FOR_ACTIONS }}
- name: Run clang-format via Docker
run: |
docker run --rm \
-v ${{ github.workspace }}:/app --workdir /app \
--user $(id -u):$(id -g) silkeh/clang:18 \
bash -c 'find src -type f \( -name "*.cc" -o -name "*.h" \) -print0 | xargs -0 clang-format -i'
# https://github.com/actions/checkout?tab=readme-ov-file#push-a-commit-to-a-pr-using-the-built-in-token
- name: Check and commit changes if needed
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
if ! git diff --quiet; then
git diff
git commit -am "chore: auto-format with clang-format"
git push
else
echo "No formatting changes"
fi
-1
View File
@@ -89,7 +89,6 @@ jobs:
folder: fallout2-ce-ems/dist
target-folder: ${{ steps.set-path.outputs.DEST_DIR }}
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
+4 -13
View File
@@ -313,7 +313,7 @@ jobs:
windows:
name: Windows (${{ matrix.arch }})
runs-on: windows-2025
runs-on: windows-2019
strategy:
fail-fast: false
@@ -328,28 +328,19 @@ jobs:
- name: Clone
uses: actions/checkout@v4
- name: Install Visual Studio 2019 Build Tools with v141_xp
shell: cmd
run: |
choco install visualstudio2019buildtools -y --package-parameters="'--add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.v141.x86.x64 --add Microsoft.VisualStudio.Component.VC.v141.xp --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows10SDK.10240 --add Microsoft.VisualStudio.ComponentGroup.NativeDesktop.WinXP --add Microsoft.VisualStudio.Component.WinXP'"
- name: Cache cmake build
uses: actions/cache@v4
with:
path: out
key: windows-${{ matrix.arch }}-cmake-v3
key: windows-${{ matrix.arch }}-cmake-v2
- name: Configure
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\VsDevCmd.bat" ^
&& cmake --preset windows-${{ matrix.arch }}
cmake --preset windows-${{ matrix.arch }}
- name: Build
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\VsDevCmd.bat" ^
&& cmake --build --preset windows-${{ matrix.arch }}-release
cmake --build --preset windows-${{ matrix.arch }}-release
- name: Change executable file name
run: |
+2 -15
View File
@@ -29,18 +29,5 @@ jobs:
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
echo "Current branch: $CURRENT_BRANCH"
if [ "$CURRENT_BRANCH" != "gh-pages" ]; then
echo "❌ Refusing to force-push to non-gh-pages branch: $CURRENT_BRANCH"
exit 1
fi
if git diff --cached --quiet; then
echo "No changes to commit"
else
git commit --amend -m "đźš® Remove preview for closed PR #${{ github.event.pull_request.number }}"
git push --force
fi
git commit -am "đźš® Remove preview for closed PR #${{ github.event.pull_request.number }}"
git push
+10 -12
View File
@@ -243,7 +243,7 @@ jobs:
windows:
name: Windows (${{ matrix.arch }})
runs-on: windows-2025
runs-on: windows-2019
strategy:
fail-fast: false
@@ -258,11 +258,6 @@ jobs:
- name: Clone
uses: actions/checkout@v4
- name: Install Visual Studio 2019 Build Tools with v141_xp
shell: cmd
run: |
choco install visualstudio2019buildtools -y --package-parameters="'--add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.v141.x86.x64 --add Microsoft.VisualStudio.Component.VC.v141.xp --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows10SDK.10240 --add Microsoft.VisualStudio.ComponentGroup.NativeDesktop.WinXP --add Microsoft.VisualStudio.Component.WinXP'"
- name: Cache cmake build
uses: actions/cache@v4
with:
@@ -270,16 +265,19 @@ jobs:
key: windows-${{ matrix.arch }}-cmake-v1
- name: Configure
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\VsDevCmd.bat" ^
&& cmake -B build -G "Visual Studio 16 2019" -A ${{ matrix.generator-platform }}
cmake \
-B build \
-G "Visual Studio 16 2019" \
-A ${{ matrix.generator-platform }} \
# EOL
- name: Build
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\VsDevCmd.bat" ^
&& cmake --build build --config RelWithDebInfo
cmake \
--build build \
--config RelWithDebInfo \
# EOL
- name: Upload
run: |
+1 -12
View File
@@ -125,7 +125,7 @@ IFACE_BAR_SIDES_ORI=0
;This will increase the width of the interface bar expanding the area used to display text.
;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
IFACE_BAR_WIDTH=640
```
Recommendations:
@@ -141,17 +141,6 @@ The third configuration file is `ddraw.ini` (part of Sfall). There are dozens of
For a sample ddraw.ini configuration file, containing all currently working settings use this link: [ddraw.ini](https://raw.githubusercontent.com/fallout2-ce/fallout2-ce/refs/heads/main/files/ddraw.ini)
## Quality of life benefits over vanilla Fallout
* High resolution support
* Increased pathfinding nodes 5x for more accurate pathfinding
* Ctrl-click to quickly move items when bartering, looting, or stealing
* _a_ to select "all" when selecting item quantity
* _a_ to `Take All` when looting
* When bartering, caps default to the right amount to balance the trade (if possible)
* Music continues playing between maps (requires config)
* Auto open doors (requires config)
## Contributing
Integrating Sfall goodies is the top priority. Quality of life updates are OK too. Please no large scale refactorings at this time as we need to reconcile changes from Reference Edition, which will make this process slow and error-prone. In any case open up an issue with your suggestion or to notify other people that something is being worked on.
+1 -2
View File
@@ -4,8 +4,7 @@
[Main]
;Set to one to hide areas outside map bounds when using higher than 640x480 resolution, and to zero to disable
;EnableHighResolutionStencil=1
;HiResMode=1
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
[Misc]
-7
View File
@@ -1,7 +0,0 @@
#!/bin/bash
docker run --rm \
-v $(pwd):/app --workdir /app \
--user $(id -u):$(id -g) silkeh/clang:18 \
bash -c 'find src -type f -name \*.cc -o -name \*.h | xargs clang-format -i'
-1
View File
@@ -1 +0,0 @@
*.int
-51
View File
@@ -1,51 +0,0 @@
# Testing sFall scripting features
## Basic info
This folder contains scripts which can be used for sFall opcodes.
## Building
Get latest `compile.exe` from [modders pack](https://sourceforge.net/projects/sfall/files/Modders%20pack/) or from [repository](https://github.com/sfall-team/sslc) and run it this way:
```sh
compile.exe -q -p -l -O2 -d -s -n -I<sfall_headers_id> <script_name.ssl>
```
(sfall headers can be found in modderspack)
### Build script using sFall Script Editor
- Get "Fallout sFall Script Editor v4.1.7.RC1", for example from https://nuclear-grot.ucoz.net/forum/12-20-1
- Run script editor, add "sfall_headers" into "Location folder of headers files" in the options
- Open any script here and compile it
- This shoud create compiled script <file.int>
### Build script using VSCode extension
- Install [VSCode Extension](https://marketplace.visualstudio.com/items?itemName=BGforge.bgforge-mls)
## Run test script
1. Move compiled `.int` file into game folder as `data/scripts/gl_<script_name>.int`
2. Change `ddraw.ini` and add this section:
```ini
[Scripts]
GlobalScriptPaths=data/scripts/gl*.int
```
(or add new path using comma as separator)
Note that on non-Windows it have to be `/` as folder separator
3. Run game, check that game displays message about tests
-181
View File
@@ -1,181 +0,0 @@
#include "lib.arrays.h"
#include "test_utils.h"
// #include "define_lite.h"
#define ARRAY_MAX_STRING (255)
#define ARRAY_MAX_SIZE (100000)
procedure array_test_suite begin
variable arr, i, arr2, map, k, v, s;
test_suite_errors := 0;
display_msg("Testing arrays_equal functions...");
call assertEquals("array_equals 1", arrays_equal([9, 7, 2, 1], [9, 7, 2, 1]), true);
call assertEquals("array_equals 2", arrays_equal([9, 7, 0, 1], [9, 7, 2, 1]), false);
call assertEquals("array_equals 3", arrays_equal([1, 1, 1, 1], [1, 1, 1]), false);
call assertEquals("array_equals 4", arrays_equal([], []), true);
call assertEquals("array_equals 5", arrays_equal({}, []), false);
call assertEquals("array_equals 6", arrays_equal({1: 1.0, 2: 2.0}, {1: 1.000, 2: 2.000}), true);
call assertEquals("array_equals 7", arrays_equal({"name": "John", "dept": 15.20}, {"name": "John"}), false);
display_msg("Testing utility functions...");
call assertEquals("strlen", strlen("testing"), 7);
call assertEquals("substr 1", substr("testing", 4, 2), "in");
call assertEquals("substr 2", substr("testing", 1, -1), "estin");
call assertEquals("substr 3", substr("testing", -5, 5), "sting");
call assertEquals("typeof 1", typeof(1), VALTYPE_INT);
call assertEquals("typeof 2", typeof(1.0), VALTYPE_FLOAT);
call assertEquals("typeof 3", typeof("1.0"), VALTYPE_STR);
// Basic array functionality
display_msg("Testing basic arrays functionality...");
arr := create_array(5,0);
call assertEquals("array size", len_array(arr), 5);
arr[0]:=100;
call assertEquals("get array", get_array(arr, 0), 100);
arr[1]:=5.555;
call assertEquals("get array float", get_array(arr, 1), 5.555);
arr[2]:="hello";
call assertEquals("get array str", get_array(arr, 2), "hello");
call assertEquals("get array invalid index", get_array(arr, -1), 0);
call assertEquals("get array invalid index", get_array(arr, 6), 0);
resize_array(arr, 77);
call assertEquals("list resize", len_array(arr), 77);
resize_array(arr, ARRAY_MAX_SIZE + 100);
call assertEquals("resize max", len_array(arr), ARRAY_MAX_SIZE);
free_array(arr);
call assertEquals("not exists check", len_array(arr), -1);
arr := create_array(ARRAY_MAX_SIZE + 100, 0);
call assertEquals("create max", len_array(arr), ARRAY_MAX_SIZE);
free_array(arr);
arr := [6, 1, 10, "one", 10, "two", 5, "three"];
arr[0] := "wtf";
//display_msg(debug_array_str(arr));
call assertEquals("scan list", scan_array(arr, 10), 2);
call assertEquals("scan list 2", scan_array(arr, "two"), 5);
call assertEquals("array_key", array_key(arr, 5), 5);
call assertEquals("array_key out of range", array_key(arr, 9), 0);
call assertEquals("is list", array_key(arr, -1), 0);
call assertEquals("get array as substr", get_array("NiCe", 1), "i");
arr := [78, 12, 99, 1, -5];
sort_array(arr);
call assertEquals("sort ASC", arrays_equal(arr, [-5, 1, 12, 78, 99]), true);
arr := ["Albert", "John", "Mike", "David"];
sort_array_reverse(arr);
call assertEquals("sort DESC", arrays_equal(arr, ["Mike", "John", "David", "Albert"]), true);
reverse_array(arr);
call assertEquals("reverse list", arrays_equal(arr, ["Albert", "David", "John", "Mike"]), true);
shuffle_array(arr);
call assertEquals("shuffle list 1", arrays_equal(arr, ["Albert", "David", "John", "Mike"]), false);
call assertEquals("shuffle list 2", len_array(arr), 4);
if (test_suite_verbose) then display_array(arr);
// some additional stuff
call assertEquals("string_split", get_array(string_split("this+is+good", "+"), 2), "good");
call assertEquals("string_split 2", len_array(string_split("advice", "")), 6);
s := "";
for (i := 0; i < ARRAY_MAX_STRING+30; i+=10) begin
s += "Verbosity.";
end
arr[0] := s;
call assertEquals("array max string", strlen(arr[0]), ARRAY_MAX_STRING-1);
// ASSOC ARRAYS TEST
display_msg("Testing associative arrays...");
arr := create_array(-1,0);
call assertEquals("is map", array_key(arr, -1), 1);
call assertEquals("exists check", len_array(arr), 0);
arr["123"] := 100;
call assertEquals("set/get str=>int", arr["123"], 100);
arr["123"] := 50;
call assertEquals("overwrite 1", arr["123"], 50);
call assertEquals("overwrite 2", len_array(arr), 1);
arr[-1] := "wtf";
call assertEquals("set/get int=>str", arr[-1], "wtf");
arr[3.14] := 0.00001;
call assertEquals("set/get float=>float", arr[3.14], 0.00001);
arr["fourth"] := "elem";
call assertEquals("map size", len_array(arr), 4);
arr[-1] := 0;
call assertEquals("unset key: length", len_array(arr), 3);
call assertEquals("unset key: hashmap", arr[3.14], 0.00001);
call assertEquals("key not exist", arr[777], 0);
free_array(arr);
call assertEquals("not exists check", len_array(arr), -1);
arr := {6:5, 1.001:0.5, 10:"A", 5:0, "wtf":1.1, 10:0.0001, "some":"What"}; // 7 here
// 10:"A" will be overwritten by 10:0.0001
call assertEquals("assoc array expr", len_array(arr), 6); // 6 actual
call assertEquals("scan map 1", scan_array(arr, 1.1), "wtf");
call assertEquals("scan map 2", scan_array(arr, 5), 6);
call assertEquals("scan map 3", scan_array(arr, "What"), "some");
call assertEquals("array_key 1", array_key(arr, 0), 6);
call assertEquals("array_key 2", array_key(arr, 4), "wtf");
call assertEquals("array_key 3", array_key(arr, 1), 1.001);
resize_array(arr, 2);
call assertEquals("map resize 1", len_array(arr), 2);
call assertEquals("map resize 2", arrays_equal(arr, {6:5, 1.001:0.5}), true);
resize_array(arr, 0);
call assertEquals("map clear", len_array(arr), 0);
display_msg("Testing foreach...");
s := "ar";
arr := [6, 10, 0.5, "wtf"];
foreach v in arr begin
s += v;
end
call assertEquals("foreach 1", s, "ar6100.50000wtf");
s := "ar2=";
arr := {"name": "John", "hp": 25, 0: 5.5};
foreach k: v in arr begin
s += k+":"+v+";";
end
call assertEquals("foreach 2", s, "ar2=name:John;hp:25;0:5.50000;");
/*
display_msg("Testing save/load...");
arr := [2,1];
arr2 := {1:2};
save_array("myarray", arr);
call assertEquals("load 1", load_array("myarray"), arr);
save_array("myarray", arr2);
call assertEquals("load 2", load_array("myarray"), arr2);
free_array(arr2);
call assertEquals("not exists check", len_array(arr2), -1);
call assertEquals("load fail", load_array("myarray"), 0);
save_array(0.1, arr);
call assertEquals("save as float", load_array(0.1), arr);
arr2 := list_saved_arrays; // list of array names
//display_msg(debug_array_str(arr2));
call assertNotEquals("saved arrays 1", scan_array(arr2, 0.1), -1);
call assertEquals("saved arrays 2", scan_array(arr2, "myarray"), -1);
save_array(0, arr);
call assertEquals("unsave array 1", load_array(0.1), 0);
call assertEquals("unsave array 2", len_array(arr), 2);
call assertEquals("saved arrays 3", len_array(list_saved_arrays), len_array(arr2) - 1);
*/
display_msg("All tests finished with "+test_suite_errors+" errors.");
call report_test_results("arrays");
end
procedure start begin
display_msg("Starting tests");
call array_test_suite();
end
-189
View File
@@ -1,189 +0,0 @@
#include "constants.h"
#include "headers/define_extra.h" // For sfall functions if not in constants
procedure start;
procedure description_proc; // For visual checks during game
variable new_tag1_id = -1;
variable new_tag2_id = -1;
variable max_tags_test_id = -1;
procedure start begin
// Test Predefined Tags
debug_msg("Testing Predefined Interface Tags...");
// SNEAK (0)
debug_msg("Activating SNEAK (0)...");
show_iface_tag(0);
if (is_iface_tag_active(0)) then
debug_msg("SNEAK is active. (Visual check needed)");
else
debug_msg("ERROR: SNEAK failed to activate or is_iface_tag_active(0) failed.");
hide_iface_tag(0);
if (not is_iface_tag_active(0)) then
debug_msg("SNEAK is inactive.");
else
debug_msg("ERROR: SNEAK failed to deactivate or is_iface_tag_active(0) failed after hide.");
// POISONED (1) - Test requires making the player poisoned by other means to truly verify show/hide effect on game state
// For now, just test is_iface_tag_active if it reflects direct calls or underlying state.
// The opcodes show_iface_tag/hide_iface_tag are not meant to directly poison/cure the player for tags 1 and 2.
// They are for custom tags or the specific PC flags (0,3,4). is_iface_tag_active *should* work for 1 and 2.
debug_msg("Checking POISONED (1) (depends on game state): " + is_iface_tag_active(1));
// Similar for RADIATED (2)
debug_msg("Checking RADIATED (2) (depends on game state): " + is_iface_tag_active(2));
// LEVEL (3)
debug_msg("Activating LEVEL (3)...");
show_iface_tag(3);
if (is_iface_tag_active(3)) then
debug_msg("LEVEL is active. (Visual check needed)");
else
debug_msg("ERROR: LEVEL failed to activate or is_iface_tag_active(3) failed.");
hide_iface_tag(3);
if (not is_iface_tag_active(3)) then
debug_msg("LEVEL is inactive.");
else
debug_msg("ERROR: LEVEL failed to deactivate or is_iface_tag_active(3) failed after hide.");
// ADDICT (4)
debug_msg("Activating ADDICT (4)...");
show_iface_tag(4);
if (is_iface_tag_active(4)) then
debug_msg("ADDICT is active. (Visual check needed)");
else
debug_msg("ERROR: ADDICT failed to activate or is_iface_tag_active(4) failed.");
hide_iface_tag(4);
if (not is_iface_tag_active(4)) then
debug_msg("ADDICT is inactive.");
else
debug_msg("ERROR: ADDICT failed to deactivate or is_iface_tag_active(4) failed after hide.");
debug_msg("Predefined tag tests complete.");
debug_msg("---");
// Test Custom Tags
debug_msg("Testing Custom Interface Tags...");
// Add first custom tag
new_tag1_id = add_iface_tag;
if (new_tag1_id != -1) then
debug_msg("Added custom tag 1 with ID: " + new_tag1_id + " (expected 5 if first)");
else
debug_msg("ERROR: Failed to add custom tag 1.");
return; // Stop if first add fails
// Show it
show_iface_tag(new_tag1_id);
if (is_iface_tag_active(new_tag1_id)) then
debug_msg("Custom tag 1 is active. (Visual check: should show 'NEW' in green)");
else
debug_msg("ERROR: Custom tag 1 failed to activate or is_iface_tag_active failed.");
// Set its text and color
set_iface_tag_text(new_tag1_id, "HELLO TAG1", 2); // Color 2 (White from issue description's reference)
debug_msg("Set text for custom tag 1 to 'HELLO TAG1' (color white). (Visual check needed)");
// Verify text length truncation (19 chars)
set_iface_tag_text(new_tag1_id, "ThisIsAVeryLongTextStringIndeed", 3); // Color 3 (Yellow)
debug_msg("Set text for custom tag 1 to 'ThisIsAVeryLongTextStringIndeed' (color yellow) - should be truncated. (Visual check needed)");
// Add second custom tag
new_tag2_id = add_iface_tag;
if (new_tag2_id != -1) then
debug_msg("Added custom tag 2 with ID: " + new_tag2_id + " (expected 6 if second)");
else
debug_msg("ERROR: Failed to add custom tag 2.");
// Continue to test tag1 hide
if (new_tag2_id != -1) then
show_iface_tag(new_tag2_id);
set_iface_tag_text(new_tag2_id, "AWESOME", 6); // Color 6 (GoodColor/Green)
debug_msg("Custom tag 2 ('AWESOME', green) should be active. (Visual check needed)");
end
// Hide first custom tag
hide_iface_tag(new_tag1_id);
if (not is_iface_tag_active(new_tag1_id)) then
debug_msg("Custom tag 1 is now inactive.");
else
debug_msg("ERROR: Custom tag 1 failed to deactivate.");
if (new_tag2_id != -1 and is_iface_tag_active(new_tag2_id)) then
debug_msg("Custom tag 2 should still be active. (Visual check needed)");
else if (new_tag2_id != -1) then
debug_msg("ERROR: Custom tag 2 became inactive or failed check.");
end
// Test max tags
debug_msg("Attempting to add maximum custom tags (up to 126)...");
variable i;
variable current_tags = 2; // Assuming new_tag1_id and new_tag2_id were successful (IDs 5 and 6)
if (new_tag1_id == -1) then current_tags = 0;
if (new_tag1_id != -1 and new_tag2_id == -1) then current_tags = 1;
for (i = current_tags; i < 126; i++) begin
max_tags_test_id = add_iface_tag;
if (max_tags_test_id == -1) then begin
debug_msg("ERROR: Failed to add tag at iteration " + i + ". Max tags might be less than 126 or error in add_iface_tag.");
break;
end
// Minimal show/set_text to ensure they are processed
show_iface_tag(max_tags_test_id);
set_iface_tag_text(max_tags_test_id, "Tag " + max_tags_test_id, 0); // Default color
end
if (i == 126) then
debug_msg("Successfully added up to 126 custom tags (IDs 5 through 130). Last ID: " + max_tags_test_id);
else
debug_msg("Stopped adding tags at count " + i + ". Last successful ID: " + max_tags_test_id);
end
// Test adding one more tag (should fail if 126 was the limit)
max_tags_test_id = add_iface_tag;
if (max_tags_test_id == -1) then
debug_msg("Correctly failed to add tag beyond limit (127th attempt).");
else
debug_msg("ERROR: Added tag beyond 126 limit! ID: " + max_tags_test_id);
end
// Test invalid IDs
debug_msg("Testing invalid tag IDs...");
show_iface_tag(-1); // Invalid
show_iface_tag(1000); // Likely invalid (beyond max added)
hide_iface_tag(-2);
hide_iface_tag(1001);
debug_msg("is_iface_tag_active(-3): " + is_iface_tag_active(-3));
debug_msg("is_iface_tag_active(1002): " + is_iface_tag_active(1002));
set_iface_tag_text(-4, "INVALID", 0);
set_iface_tag_text(1003, "INVALID", 0);
debug_msg("Invalid ID tests complete (check console for debug prints from C++).");
debug_msg("---");
debug_msg("Interface Tag testing complete. Check console and game screen.");
debug_msg("Will keep some tags active for 10 seconds for visual check then clear...");
// Keep some tags visible for a moment if run in game
// This part is for easier visual checking if you load a game with this script.
// For automated testing, the debug_msg output is key.
if (game_loaded) then begin
if (new_tag1_id != -1) then hide_iface_tag(new_tag1_id); // ensure it's hidden from previous test
if (new_tag2_id != -1) then show_iface_tag(new_tag2_id); // ensure tag2 is visible
set_iface_tag_text(new_tag2_id, "Testing Done!", 4); // Peanut butter color
game_time_advance(game_ticks(10)); // Wait 10 seconds
if (new_tag2_id != -1) then hide_iface_tag(new_tag2_id);
debug_msg("Visual check period ended.");
end
end
// This can be used with `debug_map_scripts` or similar to run on map enter
// procedure map_enter_p_proc begin call start; end
// procedure map_update_p_proc begin call start; end // If you want it to run repeatedly (not recommended for this test script)
// For `description_proc` usage with `debug(F5)`:
// Create a global script that calls `start` from `description_proc`
// Example:
// procedure description_proc begin
// if (game_loaded) then call start;
// end
-71
View File
@@ -1,71 +0,0 @@
#include "test_utils.h"
#include "sfall.h"
procedure start begin
display_msg("Testing math functions...");
call assertFloat("sin(0)", sin(0), 0.0);
call assertFloat("sin(PI/2)", sin(1.5708), 1.0);
call assertFloat("sin(PI)", sin(3.1416), 0.0);
call assertFloat("sin(-PI/2)", sin(-1.5708), -1.0);
call assertFloat("cos(0)", cos(0), 1.0);
call assertFloat("cos(PI/2)", cos(1.5708), 0.0);
call assertFloat("cos(PI)", cos(3.1416), -1.0);
call assertFloat("cos(-PI)", cos(-3.1416), -1.0);
call assertFloat("tan(0)", tan(0), 0.0);
call assertFloat("tan(PI/4)", tan(0.7854), 1.0);
call assertFloat("tan(-PI/4)", tan(-0.7854), -1.0);
call assertFloat("arctan(0, 1)", arctan(0, 1), 0.0);
call assertFloat("arctan(1, 1)", arctan(1, 1), 0.7854);
call assertFloat("arctan(-1, 1)", arctan(-1, 1), -0.7854);
call assertFloat("arctan(1, 0)", arctan(1, 0), 1.5708);
call assertFloat("log(1)", log(1), 0.0);
call assertFloat("log(e)", log(2.7183), 1.0);
call assertFloat("log(10)", log(10), 2.3026);
call assertFloat("exponent(0)", exponent(0), 1.0);
call assertFloat("exponent(1)", exponent(1), 2.7183);
call assertFloat("exponent(2)", exponent(2), 7.3891);
call assertFloat("exponent(-1)", exponent(-1), 0.3679);
call assertEquals("ceil(2.1)", ceil(2.1), 3);
call assertEquals("ceil(2.0)", ceil(2.0), 2);
call assertEquals("ceil(-2.1)", ceil(-2.1), -2);
call assertEquals("ceil(-2.9)", ceil(-2.9), -2);
call assertEquals("floor2(2.1)", floor2(2.1), 2);
call assertEquals("floor2(2.9)", floor2(2.9), 2);
call assertEquals("floor2(-2.1)", floor2(-2.1), -3);
call assertEquals("floor2(-2.9)", floor2(-2.9), -3);
call assertEquals("2^3", 2^3, 8);
call assertEquals("4^2", 4^2, 16);
call assertEquals("3^0", 3^0, 1);
call assertFloat("2^-1", 2^-1, 0); // note: int truncation
call assertFloat("2^-1", 2^-1.0, 0.5);
call assertFloat("4^0.5", 4^0.5, 2.0);
call assertFloat("8^(1/3)", 8^(1.0/3.0), 2.0);
call assertFloat("2.5^2", 2.5^2, 6.25);
call assertEquals("round(2.3)", round(2.3), 2);
call assertEquals("round(2.5)", round(2.5), 3);
call assertEquals("round(2.7)", round(2.7), 3);
call assertEquals("round(-2.3)", round(-2.3), -2);
call assertEquals("round(-2.5)", round(-2.5), -3);
call assertEquals("round(-2.7)", round(-2.7), -3);
call assertEquals("round(0.0)", round(0.0), 0);
call assertEquals("round(1.0)", round(1.0), 1);
call assertEquals("round(<int>)", round(1), 1);
call assertFloat("sin(arctan(1, 1))", sin(arctan(1, 1)), 0.7071);
call assertFloat("cos(arctan(1, 1))", cos(arctan(1, 1)), 0.7071);
call assertFloat("log(ceil(2.7))", log(ceil(2.7)), 1.0986);
call assertFloat("exponent(log(5))", exponent(log(5)), 5.0);
call report_test_results("math");
end
-15
View File
@@ -1,15 +0,0 @@
#include "test_utils.h"
#include "sfall.h"
procedure start begin
// TODO: move this to a more suitable place, once we have more functions implemented
// These are in the "Other" category in the sfall documentation
display_msg("Testing misc functions...");
variable hand := active_hand;
toggle_active_hand;
call assertEquals("active_hand toggled", active_hand, 1 - hand);
toggle_active_hand;
call assertEquals("active_hand toggled", active_hand, hand);
call report_test_results("misc");
end
-82
View File
@@ -1,82 +0,0 @@
#include "test_utils.h"
#include "sfall.h"
#define REPEAT_64 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
#define REPEAT_512 REPEAT_64 REPEAT_64 REPEAT_64 REPEAT_64 REPEAT_64 REPEAT_64 REPEAT_64 REPEAT_64
#define REPEAT_1024 REPEAT_512 REPEAT_512
procedure start begin
display_msg("Testing sprintf...");
call assertEquals("sprintf hello world", sprintf("Hello %s", "World"), "Hello World");
call assertEquals("sprintf empty string", sprintf("", 123), "");
call assertEquals("sprintf number string", sprintf("%d", 123), "123");
call assertEquals("sprintf number inside string", sprintf("Hello %d !", 123), "Hello 123 !");
call assertEquals("sprintf ascii code", sprintf("Hello %c !", 123), "Hello { !");
call assertEquals("sprintf float string", sprintf("Hello %f !", 3.4), "Hello 3.400000 !");
call assertEquals("sprintf float string", sprintf("Hello %d !", 10/4), "Hello 2 !");
call assertEquals("sprintf two strings", sprintf("Hello %s %s", "World"), "Hello World World");
call assertEquals("Long string", sprintf(REPEAT_1024"x", 1), "Error");
call assertEquals("Not so long", sprintf(REPEAT_1024, 1), REPEAT_1024);
call assertEquals("string_format1 hello world", string_format1("Hello %s!", "World"), "Hello World!");
call assertEquals("string_format1 auto hello world", string_format("Hello %s!", "World"), "Hello World!");
call assertEquals("string_format2 hello world", string_format2("Hello %s%s!", "Wo","rld"), "Hello World!");
call assertEquals("string_format2 auto hello world", string_format("Hello %s%s!", "Wo","rld"), "Hello World!");
call assertEquals("string_format3 hello world", string_format3("Hell%s %s%s!", "o", "Wo","rld"), "Hello World!");
call assertEquals("string_format3 auto hello world", string_format("Hell%s %s%s!", "o", "Wo","rld"), "Hello World!");
call assertEquals("string_format4 hello world", string_format4("Hel%s%s %s%s!", "l", "o", "Wo","rld"), "Hello World!");
call assertEquals("string_format4 auto hello world", string_format("Hel%s%s %s%s!", "l", "o", "Wo","rld"), "Hello World!");
call assertEquals("string_format5 hello world", string_format5("He%s%s%s %s%s!", "l", "l", "o", "Wo","rld"), "Hello World!");
call assertEquals("string_format5 auto hello world", string_format("He%s%s%s %s%s!", "l", "l", "o", "Wo","rld"), "Hello World!");
call assertEquals("string_format6 hello world", string_format6("H%s%s%s%s %s%s!", "e", "l", "l", "o", "Wo","rld"), "Hello World!");
call assertEquals("string_format6 auto hello world", string_format("H%s%s%s%s %s%s!", "e", "l", "l", "o", "Wo","rld"), "Hello World!");
call assertEquals("string_format7 hello world", string_format7("%s%s%s%s%s %s%s!", "H", "e", "l", "l", "o", "Wo","rld"), "Hello World!");
call assertEquals("string_format7 auto hello world", string_format("%s%s%s%s%s %s%s!", "H", "e", "l", "l", "o", "Wo","rld"), "Hello World!");
call assertEquals("formatted string length 1024", strlen(sprintf(REPEAT_1024, 1)), 1024);
call assertEquals("formatted string length 5119", strlen(
string_format7(
"%s%s%s%s%s%s%s",
REPEAT_1024,
REPEAT_1024,
REPEAT_1024,
REPEAT_1024,
REPEAT_1024,
REPEAT_1024,
REPEAT_1024)), 5119);
call assertEquals("string_tolower", string_tolower("miXeD CaSe"), "mixed case");
call assertEquals("string_toupper", string_toupper("miXeD CaSe"), "MIXED CASE");
call assertEquals("string_find ok", string_find("Hello World", "World"), 6);
call assertEquals("string_find missing", string_find("Hello World", "Zebra"), -1);
call assertEquals("string_find_from", string_find_from("Hello World", "o", 6), 7);
call assertEquals("string_compare ok", string_compare("Hello World", "HeLlO WoRld"), 1);
call assertEquals("string_compare neq", string_compare("Hello World", "HeLlO WoRld!"), 0);
call assertEquals("string_compare_locale", string_compare_locale("Hello World", "HeLlO WoRld", 866), 1); // TODO: would be nice to compare cyrillic
call assertEquals("charcode", charcode("A"), 65);
call assertEquals("charcode empty", charcode(""), 0);
call report_test_results("sprintf");
end
-162
View File
@@ -1,162 +0,0 @@
#include "define_lite.h"
#include "sfall.h"
#include "test_utils.h"
#include "command_lite.h"
procedure test_stat_pc(variable stat_id) begin
display_msg("Stat " + stat_id + " = " +
get_pc_base_stat(stat_id) + " + " +
get_pc_extra_stat(stat_id));
call assertNotEquals("Not interesting stat, base stat is zero", get_pc_base_stat(stat_id), 0);
variable old_base_stat := get_pc_base_stat(stat_id);
variable old_extra_stat := get_pc_extra_stat(stat_id);
set_pc_base_stat(stat_id, old_base_stat + 2);
set_pc_extra_stat(stat_id, old_extra_stat + 4);
// display_msg("Stat after update " + stat_id + " = " +
// get_pc_base_stat(stat_id) + " + " +
// get_pc_extra_stat(stat_id));
if (stat_id > STAT_lu) then begin
call assertNotEquals("Base stat was updated while it shouldn't",
get_pc_base_stat(stat_id), old_base_stat + 2);
end else begin
call assertEquals("Base stat updated",
get_pc_base_stat(stat_id), old_base_stat + 2);
end
set_pc_base_stat(stat_id, old_base_stat);
set_pc_extra_stat(stat_id, old_extra_stat);
// display_msg("Stat after reset " + stat_id + " = " +
// get_pc_base_stat(stat_id) + " + " +
// get_pc_extra_stat(stat_id));
call assertEquals("Base stat reset",
get_pc_base_stat(stat_id), old_base_stat);
call assertEquals("Extra stat reset",
get_pc_extra_stat(stat_id), old_extra_stat);
end
procedure test_stat_critter(variable critter, variable stat_id) begin
display_msg("Stat " + stat_id + " = " +
get_critter_base_stat(critter, stat_id) + " + " +
get_critter_extra_stat(critter, stat_id));
call assertNotEquals("Not interesting stat, base stat is zero",
get_critter_base_stat(critter, stat_id), 0);
variable old_base_stat := get_critter_base_stat(critter, stat_id);
variable old_extra_stat := get_critter_extra_stat(critter, stat_id);
set_critter_base_stat(critter, stat_id, old_base_stat + 2);
set_critter_extra_stat(critter, stat_id, old_extra_stat + 4);
// display_msg("Stat after update " + stat_id + " = " +
// get_critter_base_stat(critter, stat_id) + " + " +
// get_critter_extra_stat(critter, stat_id));
if (stat_id > STAT_lu) then begin
call assertNotEquals("Base stat was updated while it shouldn't",
get_critter_base_stat(critter, stat_id), old_base_stat + 2);
end else begin
call assertEquals("Base stat updated",
get_critter_base_stat(critter, stat_id), old_base_stat + 2);
end
set_critter_base_stat(critter, stat_id, old_base_stat);
set_critter_extra_stat(critter, stat_id, old_extra_stat);
// display_msg("Stat after reset " + stat_id + " = " +
// get_critter_base_stat(critter, stat_id) + " + " +
// get_critter_extra_stat(critter, stat_id));
call assertEquals("Base stat reset",
get_critter_base_stat(critter, stat_id), old_base_stat);
call assertEquals("Extra stat reset",
get_critter_extra_stat(critter, stat_id), old_extra_stat);
end
procedure test_derived_base_stat_critter(variable critter) begin
display_msg("Testing derived stats on " + obj_name(critter));
variable old_agility;
old_agility := get_critter_base_stat(critter, STAT_ag);
call assertNotEquals("Initial agility is 10", old_agility, 10);
call assertNotEquals("Action points is 10 while agility is not 10", get_critter_base_stat(critter, STAT_max_move_points), 10);
set_critter_base_stat(critter, STAT_ag, 10);
call assertEquals("Action points updated to 10",
get_critter_base_stat(critter, STAT_max_move_points), 10);
set_critter_base_stat(critter, STAT_ag, old_agility);
call assertNotEquals("Action points updated from 10",
get_critter_base_stat(critter, STAT_max_move_points), 10);
end
procedure start begin
display_msg("Testing stats...");
test_suite_errors := 0;
call test_stat_pc(STAT_max_hit_points);
call test_stat_pc(STAT_lu);
variable critter;
variable closest_critter := dude_obj;
foreach critter in list_as_array(LIST_CRITTERS) begin
if (is_critter_dead(critter)) then begin
continue;
end
if tile_distance(dude_tile, tile_num(critter)) == 0 then begin
continue;
end
if (tile_distance(dude_tile, tile_num(critter)) <
tile_distance(dude_tile, tile_num(closest_critter))) or
(closest_critter == dude_obj)
then begin
closest_critter := critter;
end
end
// display_msg("Closest " + obj_name(closest_critter) + " distance: " +
// tile_distance(dude_tile, tile_num(closest_critter)));
call test_stat_critter(dude_obj, STAT_max_hit_points);
call test_stat_critter(dude_obj, STAT_lu);
if closest_critter != dude_obj then begin
display_msg("Testing stats for " + obj_name(closest_critter));
call test_stat_critter(closest_critter, STAT_max_hit_points);
call test_stat_critter(closest_critter, STAT_lu);
end else begin
display_msg("FAIL: No other critters found to test stats");
test_suite_errors++;
end
if
get_critter_base_stat(dude_obj, STAT_lu) ==
get_critter_base_stat(closest_critter, STAT_lu)
and
get_critter_extra_stat(dude_obj, STAT_max_hit_points) ==
get_critter_extra_stat(closest_critter, STAT_max_hit_points) then begin
display_msg("FAIL: Very sus eq values for dude and closest critter");
test_suite_errors++;
end
call test_derived_base_stat_critter(dude_obj);
call test_derived_base_stat_critter(closest_critter);
call report_test_results("stats");
// set_pc_base_stat(STAT_lu, 10);
// set_pc_extra_stat(STAT_max_hit_points, 500);
end
-112
View File
@@ -1,112 +0,0 @@
#ifndef TEST_UTILS_H
#define TEST_UTILS_H
#include "define_lite.h"
variable test_suite_errors := 0;
variable test_suite_verbose := false;
variable test_suite_assertions := 0;
procedure assertEquals(variable desc, variable a, variable b) begin
test_suite_assertions++;
if (a != b or typeof(a) != typeof(b)) then begin
display_msg("FAIL \""+desc+"\": "+a+" != "+b);
test_suite_errors ++;
end else if (test_suite_verbose) then begin
display_msg("ok \""+desc+"\"");
end
end
procedure assertNotEquals(variable desc, variable a, variable b) begin
test_suite_assertions++;
if (a == b) then begin
display_msg("FAIL \""+desc+"\": "+a+" == "+b);
test_suite_errors ++;
end else if (test_suite_verbose) then begin
display_msg("ok \""+desc+"\"");
end
end
procedure assertFloat(variable desc, variable a, variable b, variable tolerance = 0.001) begin
test_suite_assertions++;
variable diff := abs(a - b);
if (diff > tolerance) then begin
display_msg("FAIL \""+desc+"\": "+a+" != "+b+" (diff: "+diff+")");
test_suite_errors ++;
end else if (test_suite_verbose) then begin
display_msg("ok \""+desc+"\"");
end
end
procedure report_test_results(variable desc) begin
display_msg("DONE " + desc + " " +
(test_suite_assertions-test_suite_errors) + "/" + test_suite_assertions + "");
display_msg("================");
#define GL_VAR_TESTING_INFO_ARRAY_ID "TestArra"
#define TEST_ARR_IDENTIFIER_KEY "__testing_info_array__"
variable arr_id = get_sfall_global_int(GL_VAR_TESTING_INFO_ARRAY_ID);
if arr_id != 0 then begin
// Let's check that it is actually our testing array
if array_key(arr_id, -1) == 0 then begin
// It is not even associative array, so it is not our testing array
arr_id = 0;
end else begin
if arr_id[TEST_ARR_IDENTIFIER_KEY] != 1 then begin
// It is associative array, but it is not our testing array
arr_id = 0;
end
end
end
if arr_id == 0 then begin
arr_id = create_array(-1, 0);
arr_id[TEST_ARR_IDENTIFIER_KEY] = 1;
set_sfall_global(GL_VAR_TESTING_INFO_ARRAY_ID, arr_id);
end
#define TEST_SUITES "Test suites"
#define TEST_ASSERTIONS_TOTAL "Tests assertings total"
#define TEST_ASSERTIONS_FAILED "Tests assertings failed"
if arr_id[TEST_SUITES] == 0 then begin
arr_id[TEST_SUITES] = create_array(-1, 0);
end
//set_sfall_global("test_suite_errors", test_suite_errors);
arr_id[TEST_ASSERTIONS_TOTAL] += test_suite_assertions;
arr_id[TEST_ASSERTIONS_FAILED] += test_suite_errors;
arr_id[TEST_SUITES][desc] += 1;
variable total_suites_names = "";
variable total_suites_count = 0;
variable key, item;
foreach (key: item in arr_id[TEST_SUITES]) begin
total_suites_count += item;
if (total_suites_names != "") then begin
total_suites_names += ", ";
end
total_suites_names += key;
end
variable assertions_stats_str = (arr_id[TEST_ASSERTIONS_TOTAL]-arr_id[TEST_ASSERTIONS_FAILED]) +
"/" + arr_id[TEST_ASSERTIONS_TOTAL];
if (arr_id[TEST_ASSERTIONS_FAILED] == 0) then begin
float_msg(dude_obj, "Tested " + total_suites_count + " cases: " + total_suites_names + ". " +
assertions_stats_str + " assertions passed!", FLOAT_MSG_GREEN);
end else begin
float_msg(dude_obj, "Tested " + total_suites_count + " cases: " + total_suites_names + ". " +
assertions_stats_str + " assertions passed", FLOAT_MSG_RED);
end
end
#endif
-4
View File
@@ -3046,10 +3046,6 @@ void _dude_fidget()
return;
}
if (GameMode::getCurrentGameMode() == GameMode::kWorldmap) {
return;
}
if (isInCombat()) {
return;
}
+3 -3
View File
@@ -1769,7 +1769,7 @@ static int characterEditorWindowInit()
btn = buttonCreate(
gCharacterEditorWindow,
344, // one pixel right shift to align with background
343,
454,
_editorFrmImages[EDITOR_GRAPHIC_LITTLE_RED_BUTTON_UP].getWidth(),
_editorFrmImages[EDITOR_GRAPHIC_LITTLE_RED_BUTTON_UP].getHeight(),
@@ -1787,7 +1787,7 @@ static int characterEditorWindowInit()
btn = buttonCreate(
gCharacterEditorWindow,
553, // one pixel right shift to align with background
552,
454,
_editorFrmImages[EDITOR_GRAPHIC_LITTLE_RED_BUTTON_UP].getWidth(),
_editorFrmImages[EDITOR_GRAPHIC_LITTLE_RED_BUTTON_UP].getHeight(),
@@ -1805,7 +1805,7 @@ static int characterEditorWindowInit()
btn = buttonCreate(
gCharacterEditorWindow,
456, // one pixel right shift to align with background
455,
454,
_editorFrmImages[23].getWidth(),
_editorFrmImages[23].getHeight(),
+51
View File
@@ -1,6 +1,8 @@
#include "draw.h"
#include <cstring>
#include <string.h>
#include <vector>
#include "color.h"
#include "svga.h"
@@ -339,4 +341,53 @@ void transSrcCopy(unsigned char* dest, int destPitch, unsigned char* src, int sr
}
}
void blitBufferToBufferStretchAndFixEdges(
unsigned char* src, int srcW, int srcH, int srcPitch,
unsigned char* dst, int dstW, int dstH, int dstPitch,
int numStates)
{
// Temp buffer for over-stretched result (per state)
const int stretchW = dstW + 1;
const int stretchH = dstH + 1;
const int stretchPitch = stretchW;
std::vector<unsigned char> tempBuffer(stretchW * stretchH * numStates);
// Stretch all states to slightly larger temp buffer
blitBufferToBufferStretch(
src, srcW, srcH * numStates, srcPitch,
tempBuffer.data(), stretchW, stretchH * numStates, stretchPitch);
// Now copy only the top-left dstW x dstH portion per state into the final buffer
for (int state = 0; state < numStates; ++state) {
unsigned char* tempFrame = tempBuffer.data() + stretchW * stretchH * state;
unsigned char* finalFrame = dst + dstW * dstH * state;
for (int y = 0; y < dstH; ++y) {
memcpy(
finalFrame + y * dstW,
tempFrame + y * stretchPitch,
dstW);
}
}
}
void calculateScaledSize(int srcWidth, int srcHeight, int targetWidth, int targetHeight, int mode, int& outWidth, int& outHeight)
{
if (mode == 2) {
// Fullscreen stretch
outWidth = targetWidth;
outHeight = targetHeight;
} else {
// Maintain aspect ratio
if (targetHeight * srcWidth >= targetWidth * srcHeight) {
outWidth = targetWidth;
outHeight = (targetWidth * srcHeight) / srcWidth;
} else {
outWidth = (targetHeight * srcWidth) / srcHeight;
outHeight = targetHeight;
}
}
}
} // namespace fallout

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