mirror of
https://github.com/fallout2-ce/fallout2-ce.git
synced 2026-07-27 16:47:11 -07:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a59d88f9f5 |
@@ -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 }}
|
||||
|
||||
@@ -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: |
|
||||
@@ -328,16 +328,16 @@ jobs:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Visual Studio 2022 Build Tools with v141_xp
|
||||
- name: Install Visual Studio 2019 Build Tools with v141_xp
|
||||
shell: cmd
|
||||
run: |
|
||||
choco install visualstudio2022buildtools -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'"
|
||||
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-v102
|
||||
key: windows-${{ matrix.arch }}-cmake-v3
|
||||
|
||||
- name: Configure
|
||||
shell: cmd
|
||||
@@ -432,4 +432,4 @@ jobs:
|
||||
tag_name: continious
|
||||
draft: false
|
||||
prerelease: true
|
||||
name: Fallout2-CE Continuous Build
|
||||
name: Fallout2-CE Continuous Build
|
||||
@@ -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
|
||||
+1
-12
@@ -286,21 +286,12 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
"src/sfall_metarules.h"
|
||||
"src/sfall_opcodes.cc"
|
||||
"src/sfall_opcodes.h"
|
||||
"src/sfall_script_hooks.cc"
|
||||
"src/sfall_arrays.cc"
|
||||
"src/sfall_arrays.h"
|
||||
"src/sfall_animation.cc"
|
||||
"src/sfall_animation.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"
|
||||
@@ -321,8 +312,7 @@ endif()
|
||||
if(WIN32)
|
||||
target_link_libraries(${EXECUTABLE_NAME}
|
||||
winmm
|
||||
debug libcpmtd
|
||||
optimized libcpmt
|
||||
libcpmt
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -434,7 +424,6 @@ endif()
|
||||
|
||||
add_subdirectory("third_party/fpattern")
|
||||
target_link_libraries(${EXECUTABLE_NAME} fpattern::fpattern)
|
||||
target_link_libraries(${EXECUTABLE_NAME} fpattern_windows::fpattern_windows)
|
||||
|
||||
target_link_libraries(${EXECUTABLE_NAME} ${ZLIB_LIBRARIES})
|
||||
target_include_directories(${EXECUTABLE_NAME} PRIVATE ${ZLIB_INCLUDE_DIRS})
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@
|
||||
"inherits": [
|
||||
"windows-base"
|
||||
],
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"generator": "Visual Studio 16 2019",
|
||||
"architecture": "Win32",
|
||||
"cacheVariables": {
|
||||
"CMAKE_GENERATOR_TOOLSET": "v141_xp",
|
||||
@@ -42,7 +42,7 @@
|
||||
"inherits": [
|
||||
"windows-base"
|
||||
],
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"generator": "Visual Studio 16 2019",
|
||||
"architecture": "x64",
|
||||
"cacheVariables": {
|
||||
"CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded$<$<CONFIG:Debug>:Debug>"
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
# Sfall Compatibility
|
||||
|
||||
This document tracks Fallout 2 CE compatibility with sfall. This is for modders who need to know which Sfall features work in CE.
|
||||
|
||||
For now, this covers opcodes/metarules, and hooks. In the future, it will include other ways of modifying the engine (like ini files), and other Sfall-specific behaviour.
|
||||
|
||||
## Opcodes / Metarules
|
||||
|
||||
See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/) for documentation on specific functions.
|
||||
|
||||
| Group | Opcodes In Group | Compatibility | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| Direct memory access| read_byte,short,int,string<br>write_byte,short,int,string<br>call_offset_vX | đźš« | Not possible. Open an issue if you need functionality not covered by other opcodes. |
|
||||
| Stats | get/set_pc_base_stat<br>get/set_pc_extra_stat<br>get/set_critter_base_stat<br>get/set_critter_extra_stat | âś… | CE uses engine stat helpers here instead of sfall's direct proto-field behavior, so derived-stat update behavior can differ. |
|
||||
| Stats / Alter min/max | get/set_stat_min/max<br>set_pc_stat_min/max<br>set_npc_stat_min/max | not implemented | - |
|
||||
| Skills | get/set_critter_skill_points<br>get/set_available_skill_points<br>set_skill_max<br>set_critter_skill_mod<br>set_base_skill_mod<br>mod_skill_points_per_level | not implemented | - |
|
||||
| Graphics | graphics_funcs_available<br>force_graphics_refresh<br>get_screen_width<br>get_screen_height<br>set_palette | implemented: only get_screen_width, get_screen_height | - |
|
||||
| Shaders | load_shader<br>free_shader<br>activate_shader<br>deactivate_shader<br>set/get_shader_* | đźš« | likely will not implement direct compatibility
|
||||
| Perks and traits | set_perk_image<br>set_perk_*<br>set_pyromaniac_mod<br>apply_heaveho_fix<br>set_swiftlearner_mod<br>set_fake_perk<br>set_fake_trait<br>set_selectable_perk<br>set_perkbox_title<br>hide_real_perks<br>show_real_perks<br>perk_add_mode<br>clear_selectable_perks<br>has_fake_perk<br>has_fake_trait<br>add_trait<br>remove_trait<br>seq_perk_freq | not implemented | - |
|
||||
| Virtual file system | fs_create<br>fs_copy<br>fs_find<br>fs_read/write_*<br>fs_delete<br>fs_size<br>fs_pos<br>fs_seek<br>fs_resize | đźš« | Open an issue if you have a use case for these |
|
||||
| Combat / Knockback | set_weapon_knockback<br>set_target_knockback<br>set_attacker_knockback<br>remove_weapon_knockback<br>remove_target_knockback<br>remove_attacker_knockback | not implemented | - |
|
||||
| Maps and encounters | in_world_map<br>force_encounter<br>force_encounter_with_flags<br>set_map_time_multi<br>get/set_map_enter_position<br>exec_map_update_scripts<br>get/set_terrain_name<br>set_town_title<br>get/set_can_rest_on_map<br>set_rest_heal_time<br>set_rest_mode<br>set_worldmap_heal_time | implemented: in_world_map, force_encounter, force_encounter_with_flags, set_map_time_multi | - |
|
||||
| Maps and encounters / Worldmap | get_world_map_x/y_pos<br>set_world_map_pos | âś… | - |
|
||||
| Audio | eax_available<br>set_eax_environment<br>play_sfall_sound<br>stop_sfall_sound | not implemented | *eax* opcodes will not be implemented |
|
||||
| Combat / Weapons and ammo | get/set_weapon_ammo_pid<br>get/set_weapon_ammo_count | âś… | - |
|
||||
| Sfall / Version | sfall_ver_major<br>sfall_ver_minor<br>sfall_ver_build | âś… | CE currently reports `4.3.4` |
|
||||
| Utility / Math | log, exponent, round, sqrt, abs, sin, cos, tan, arctan, ceil, ^, floor2, div | âś… | - |
|
||||
| Keyboard and mouse | key_pressed<br>tap_key<br>get_mouse_x/y<br>get_mouse_buttons | âś… | - |
|
||||
| Lists | list_begin<br>list_next<br>list_end<br>list_as_array<br>party_member_list | âś… | - |
|
||||
| Explosions | metarule2_explosions<br>set_attack_explosion_pattern<br>set_attack_explosion_art<br>set_attack_explosion_radius<br>set_attack_is_explosion_fire<br>set_explosion_radius<br>set_dynamite_damage<br>set_plastic_damage<br>get_explosion_damage<br>set_explosion_max_targets<br>item_make_explosive | âś… except item_make_explosive | - |
|
||||
| Animations | reg_anim_combat_check<br>reg_anim_destroy<br>reg_anim_animate_and_hide<br>reg_anim_light<br>reg_anim_change_fid<br>reg_anim_take_out<br>reg_anim_turn_towards<br>reg_anim_callback<br>reg_anim_animate_and_move | âś… except reg_anim_callback | - |
|
||||
| Art and appearance | art_exists<br>refresh_pc_art<br>art_cache_clear<br>set_hero_race<br>set_hero_style | implemented: art_exists, refresh_pc_art, art_cache_clear | - |
|
||||
| Tiles and paths | get_tile_fid<br>tile_under_cursor<br>tile_light<br>tile_get_objs<br>tile_refresh_display<br>obj_blocking_tile<br>tile_by_position<br>get_tile_ground_fid<br>get_tile_roof_fid<br>obj_blocking_line<br>path_find_to<br>objects_in_radius | âś… except objects_in_radius | - |
|
||||
| Utility | sprintf<br>typeof<br>atoi<br>atof | âś… | - |
|
||||
| Utility / Strings | string_split<br>substr<br>strlen<br>charcode<br>get_string_pointer<br>string_find<br>string_find_from<br>string_format<br>string_format_array<br>string_replace<br>string_to_case<br>string_compare | âś… except get_string_pointer | `get_string_pointer` is deprecated and intentionally omitted. |
|
||||
| Interface / Tags | show_iface_tag<br>hide_iface_tag<br>is_iface_tag_active<br>set_iface_tag_text<br>add_iface_tag | implemented: all except set_iface_tag_text, add_iface_tag | CE only handles built-in interface tags here; custom tag creation/text is not supported yet. |
|
||||
| Global variables | set_sfall_global<br>get_sfall_global_int<br>get_sfall_global_float | implemented: all except get_sfall_global_float | Current CE storage is int-backed; `set_sfall_global` stores integer values and there is no float getter yet. |
|
||||
| Hooks / Hook functions | init_hook<br>get_sfall_arg<br>get_sfall_args<br>get_sfall_arg_at<br>set_sfall_return<br>set_sfall_arg<br>register_hook<br>register_hook_proc<br>register_hook_proc_spec | âś… except init_hook | See below for implemented hooks. `init_hook` is deprecated and will not be implemented. register_hook_proc and register_hook_proc_spec both add hooks to the *end* of the hook list, instead of beginning and end, respectively. |
|
||||
| Arrays / Array functions | create_array<br>temp_array<br>fix_array<br>get/set_array<br>resize_array<br>free_array<br>scan_array<br>len_array<br>save/load_array<br>array_key<br>arrayexpr | âś… except save_array, load_array | - |
|
||||
| Perks and traits / NPC perks | set_fake_perk_npc<br>set_fake_trait_npc<br>set_selectable_perk_npc<br>has_fake_perk_npc<br>has_fake_trait_npc | not implemented | - |
|
||||
| Global scripts / Global script functions | set_global_script_repeat<br>set_global_script_type<br>available_global_script_types | implemented: all except available_global_script_types | - |
|
||||
| Combat | attack_is_aimed<br>block_combat<br>force_aimed_shots<br>disable_aimed_shots<br>get_attack_type<br>get/set_bodypart_hit_modifier<br>combat_data<br>get/set/reset_critical_table<br>get_last_target<br>get_last_attacker<br>set_critter_burst_disable<br>get/set_critter_current_ap<br>set_spray_settings<br>get/set_combat_free_move | implemented: only get_attack_type, get_bodypart_hit_modifier, combat_data, set_bodypart_hit_modifier | - |
|
||||
| Car | set_car_current_town<br>car_gas_amount<br>set_car_intface_art | implemented: all except set_car_intface_art | - |
|
||||
| Interface / Windows and images | art_frame_data<br>interface_art_draw<br>interface_print<br>draw_image<br>draw_image_scaled<br>get_window_under_mouse<br>create_win<br>get_window_attribute<br>message_box<br>set_window_flag<br>win_fill_color<br>interface_overlay<br>dialog_message<br>get_text_width<br>hide_window<br>show_window | implemented: only message_box, get_text_width, show_window | - |
|
||||
| Interface / Outline | outlined_object<br>get_outline<br>set_outline | implemented: all except get_outline | - |
|
||||
| Interface / Main interface | intface_is_hidden<br>intface_redraw<br>intface_hide<br>intface_show<br>set_quest_failure_value | implemented: only intface_redraw | `intface_redraw` only supports the zero-argument form; the optional-argument path is explicitly unimplemented. |
|
||||
| Interface / Inventory | display_stats<br>inventory_redraw<br>critter_inven_obj2<br>get_current_inven_size<br>item_weight | implemented: only critter_inven_obj2 | - |
|
||||
| Interface / Cursor | get/set_cursor_mode | âś… | - |
|
||||
| Locks | lock_is_jammed<br>unjam_lock<br>set_unjam_locks_time | not implemented | - |
|
||||
| INI settings | modified_ini<br>get_ini_setting<br>get_ini_string<br>get_ini_section<br>get_ini_sections<br>get_ini_config<br>get_ini_config_db<br>set_ini_setting | âś… except modified_ini, get_ini_config, get_ini_config_db | `modified_ini` is intentionally omitted as deprecated. |
|
||||
| Objects and scripts | set_self<br>set_dude_obj<br>real_dude_obj<br>remove_script<br>set_script<br>get_script<br>obj_is_carrying_obj<br>loot_obj<br>dialog_obj<br>obj_under_cursor<br>get_object_data<br>set_object_data<br>get_flags<br>set_flags<br>set_unique_id<br>set_scr_name<br>obj_is_openable<br>get/set_proto_data<br>get_object_ai_data | implemented: set_self, get_script, loot_obj, dialog_obj, obj_under_cursor, get_object_data, get_flags, set_flags, get_proto_data, set_proto_data | - |
|
||||
| Other / Game management | set_movie_path<br>stop_game<br>resume_game<br>mark_movie_played<br>game_loaded<br>get_game_mode<br>get_uptime<br>signal_close_game | implemented: game_loaded, get_game_mode, get_uptime | - |
|
||||
| Other | input_funcs_available<br>get_year<br>set_dm_model<br>set_df_model<br>set_pipboy_available<br>get_kill_counter<br>mod_kill_counter<br>active_hand<br>toggle_active_hand<br>set_pickpocket_max<br>set_hit_chance_max<br>set_xp_mod<br>set_critter_hit_chance_mod<br>set_base_hit_chance_mod<br>inc_npc_level<br>get_npc_level<br>get/set_viewport_x/y<br>set_hp_per_level_mod<br>get/set_unspent_ap_bonus<br>get/set_unspent_ap_perk_bonus<br>hero_select_win<br>create_message_window<br>get_light_level<br>gdialog_get_barter_mod<br>set_inven_ap_cost<br>set_base_pickpocket_mod<br>set_critter_pickpocket_mod<br>message_str_game<br>sneak_success<br>create_spatial<br>unwield_slot<br>get_inven_ap_cost<br>add_g_timer_event<br>add_extra_msg_file<br>get_metarule_table<br>metarule_exist<br>npc_engine_level_up<br>remove_timer_event<br>set_drugs_data<br>spatial_radius | implemented: get_year, active_hand, toggle_active_hand, create_message_window, message_str_game, add_extra_msg_file, metarule_exist | `input_funcs_available`, `nb_create_char` are deprecated in sfall and intentionally absent in CE. `add_extra_msg_file` does not support the explicit `fileNumber` form in CE. |
|
||||
|
||||
## Hooks
|
||||
|
||||
| Hook | ID | Compatibility | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| ToHit | `HOOK_TOHIT` | âś… | - |
|
||||
| AfterHitRoll | `HOOK_AFTERHITROLL` | đźš« | Et tu |
|
||||
| CalcAPCost | `HOOK_CALCAPCOST` | đźš« | - |
|
||||
| DeathAnim1 | `HOOK_DEATHANIM1` | đźš« | Use DEATHANIM2 instead |
|
||||
| DeathAnim2 | `HOOK_DEATHANIM2` | âś… | - |
|
||||
| CombatDamage | `HOOK_COMBATDAMAGE` | âś… | - |
|
||||
| OnDeath | `HOOK_ONDEATH` | đźš« | - |
|
||||
| FindTarget | `HOOK_FINDTARGET` | đźš« | (maybe) |
|
||||
| UseObjOn | `HOOK_USEOBJON` | âś… | - |
|
||||
| UseObj | `HOOK_USEOBJ` | âś… | CE notes an sfall-matching inconsistency around return code `2` behavior between interface contexts. |
|
||||
| RemoveInvenObj | `HOOK_REMOVEINVENOBJ` | đźš« | - |
|
||||
| BarterPrice | `HOOK_BARTERPRICE` | âś… | - |
|
||||
| MoveCost | `HOOK_MOVECOST` | đźš« | - |
|
||||
| ItemDamage | `HOOK_ITEMDAMAGE` | đźš« | - |
|
||||
| AmmoCost | `HOOK_AMMOCOST` | đźš« | Et tu |
|
||||
| KeyPress | `HOOK_KEYPRESS` | âś… | Third hook arg is currently `0`; CE doesn't use VK codes. |
|
||||
| MouseClick | `HOOK_MOUSECLICK` | đźš« | - |
|
||||
| UseSkill | `HOOK_USESKILL` | đźš« | - |
|
||||
| Steal | `HOOK_STEAL` | đźš« | Et tu |
|
||||
| WithinPerception | `HOOK_WITHINPERCEPTION` | đźš« | Et tu |
|
||||
| InventoryMove | `HOOK_INVENTORYMOVE` | âś… | - |
|
||||
| InvenWield | `HOOK_INVENWIELD` | đźš« | - |
|
||||
| AdjustFID | `HOOK_ADJUSTFID` | đźš« | - |
|
||||
| CombatTurn | `HOOK_COMBATTURN` | đźš« | - |
|
||||
| StdProcedure | `HOOK_STDPROCEDURE` | đźš« | Et tu |
|
||||
| StdProcedureEnd | `HOOK_STDPROCEDURE_END` | đźš« | - |
|
||||
| CarTravel | `HOOK_CARTRAVEL` | đźš« | - |
|
||||
| SetGlobalVar | `HOOK_SETGLOBALVAR` | đźš« | - |
|
||||
| RestTimer | `HOOK_RESTTIMER` | đźš« | Et tu |
|
||||
| GameModeChange | `HOOK_GAMEMODECHANGE` | âś… | - |
|
||||
| UseAnimObj | `HOOK_USEANIMOBJ` | đźš« | Et tu; (maybe) |
|
||||
| ExplosiveTimer | `HOOK_EXPLOSIVETIMER` | đźš« | - |
|
||||
| DescriptionObj | `HOOK_DESCRIPTIONOBJ` | đźš« | Et tu |
|
||||
| UseSkillOn | `HOOK_USESKILLON` | đźš« | Et tu |
|
||||
| OnExplosion | `HOOK_ONEXPLOSION` | đźš« | (maybe) |
|
||||
| SubCombatDamage | `HOOK_SUBCOMBATDAMAGE` | đźš« | (maybe) |
|
||||
| SetLighting | `HOOK_SETLIGHTING` | đźš« | Et tu; (maybe) |
|
||||
| Sneak | `HOOK_SNEAK` | đźš« | - |
|
||||
| TargetObject | `HOOK_TARGETOBJECT` | đźš« | (maybe) |
|
||||
| Encounter | `HOOK_ENCOUNTER` | đźš« | Et tu |
|
||||
| AdjustPoison | `HOOK_ADJUSTPOISON` | đźš« | (maybe) |
|
||||
| AdjustRads | `HOOK_ADJUSTRADS` | đźš« | (maybe) |
|
||||
| RollCheck | `HOOK_ROLLCHECK` | đźš« | - |
|
||||
| BestWeapon | `HOOK_BESTWEAPON` | đźš« | - |
|
||||
| CanUseWeapon | `HOOK_CANUSEWEAPON` | đźš« | - |
|
||||
| BuildSfxWeapon | `HOOK_BUILDSFXWEAPON` | đźš« | - |
|
||||
@@ -197,6 +197,11 @@ NumbersInDialogue=0
|
||||
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
[Scripts]
|
||||
|
||||
;Comma-separated list of masked paths to load global scripts from
|
||||
;Only use single backslash \ as the directory separator
|
||||
;Paths outside of scripts folder are supported
|
||||
;GlobalScriptPaths=scripts\gl_*.int,scripts\sfall\gl*.int
|
||||
|
||||
;Uncomment the option to specify an additional directory for ini files used by scripts
|
||||
;The game will search for ini files first relative to this directory and then relative to the root directory if not found
|
||||
;The path length is limited to 61 characters
|
||||
|
||||
@@ -13,23 +13,18 @@ import java.io.OutputStream;
|
||||
public class FileUtils {
|
||||
|
||||
static boolean copyRecursively(ContentResolver contentResolver, DocumentFile src, File dest) {
|
||||
if (dest == null || (!dest.exists() && !dest.mkdirs())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final DocumentFile[] documentFiles = src.listFiles();
|
||||
for (final DocumentFile documentFile : documentFiles) {
|
||||
final String name = documentFile.getName();
|
||||
if (name == null || name.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (documentFile.isFile()) {
|
||||
if (!copyFile(contentResolver, documentFile, new File(dest, name))) {
|
||||
if (!copyFile(contentResolver, documentFile, new File(dest, documentFile.getName()))) {
|
||||
return false;
|
||||
}
|
||||
} else if (documentFile.isDirectory()) {
|
||||
final File subdirectory = new File(dest, name);
|
||||
final File subdirectory = new File(dest, documentFile.getName());
|
||||
if (!subdirectory.exists()) {
|
||||
subdirectory.mkdir();
|
||||
}
|
||||
|
||||
if (!copyRecursively(contentResolver, documentFile, subdirectory)) {
|
||||
return false;
|
||||
}
|
||||
@@ -39,22 +34,18 @@ public class FileUtils {
|
||||
}
|
||||
|
||||
private static boolean copyFile(ContentResolver contentResolver, DocumentFile src, File dest) {
|
||||
final File parent = dest.getParentFile();
|
||||
if (parent != null && !parent.exists() && !parent.mkdirs()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try (final InputStream inputStream = contentResolver.openInputStream(src.getUri());
|
||||
final OutputStream outputStream = new FileOutputStream(dest)) {
|
||||
if (inputStream == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
final InputStream inputStream = contentResolver.openInputStream(src.getUri());
|
||||
final OutputStream outputStream = new FileOutputStream(dest);
|
||||
|
||||
final byte[] buffer = new byte[16384];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
}
|
||||
|
||||
inputStream.close();
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
|
||||
@@ -2,12 +2,10 @@ package com.alexbatalov.fallout2ce;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
@@ -19,18 +17,18 @@ public class ImportActivity extends Activity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
launchImportPicker();
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
startActivityForResult(intent, IMPORT_REQUEST_CODE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent resultData) {
|
||||
if (requestCode == IMPORT_REQUEST_CODE) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
final Uri treeUri = resultData != null ? resultData.getData() : null;
|
||||
final Uri treeUri = resultData.getData();
|
||||
if (treeUri != null) {
|
||||
grantImportPermissions(treeUri, resultData);
|
||||
final DocumentFile treeDocument = DocumentFile.fromTreeUri(this, treeUri);
|
||||
if (treeDocument != null && treeDocument.isDirectory()) {
|
||||
if (treeDocument != null) {
|
||||
copyFiles(treeDocument);
|
||||
return;
|
||||
}
|
||||
@@ -44,57 +42,18 @@ public class ImportActivity extends Activity {
|
||||
}
|
||||
|
||||
private void copyFiles(DocumentFile treeDocument) {
|
||||
final ProgressDialog dialog = createProgressDialog();
|
||||
ProgressDialog dialog = createProgressDialog();
|
||||
dialog.show();
|
||||
|
||||
new Thread(() -> {
|
||||
final ContentResolver contentResolver = getContentResolver();
|
||||
final File externalFilesDir = getExternalFilesDir(null);
|
||||
final boolean success = externalFilesDir != null
|
||||
&& FileUtils.copyRecursively(contentResolver, treeDocument, externalFilesDir);
|
||||
ContentResolver contentResolver = getContentResolver();
|
||||
File externalFilesDir = getExternalFilesDir(null);
|
||||
FileUtils.copyRecursively(contentResolver, treeDocument, externalFilesDir);
|
||||
|
||||
runOnUiThread(() -> {
|
||||
dialog.dismiss();
|
||||
|
||||
if (success) {
|
||||
startMainActivity();
|
||||
} else {
|
||||
Toast.makeText(this, R.string.import_failed, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
finish();
|
||||
});
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void launchImportPicker() {
|
||||
final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
|
||||
|
||||
try {
|
||||
startActivityForResult(intent, IMPORT_REQUEST_CODE);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(this, R.string.import_picker_unavailable, Toast.LENGTH_LONG).show();
|
||||
startMainActivity();
|
||||
dialog.dismiss();
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void grantImportPermissions(Uri treeUri, Intent resultData) {
|
||||
int flags = Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
||||
if (resultData != null) {
|
||||
flags = resultData.getFlags()
|
||||
& (Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
||||
}
|
||||
|
||||
try {
|
||||
getContentResolver().takePersistableUriPermission(treeUri, flags);
|
||||
} catch (SecurityException ignored) {
|
||||
// Some providers do not offer persistable grants. One-shot access is enough for import.
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void startMainActivity() {
|
||||
|
||||
@@ -2,6 +2,4 @@
|
||||
<string name="app_name">Fallout 2</string>
|
||||
<string name="loading_dialog_title">PLEASE STAND BY</string>
|
||||
<string name="loading_dialog_message">Copying files…</string>
|
||||
<string name="import_failed">Failed to import game files. Please pick a valid Fallout 2 folder and try again.</string>
|
||||
<string name="import_picker_unavailable">No compatible folder picker is available on this device.</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
*.int
|
||||
sfall
|
||||
+14
-2
@@ -30,10 +30,22 @@ compile.exe -q -p -l -O2 -d -s -n -I<sfall_headers_id> <script_name.ssl>
|
||||
|
||||
- Install [VSCode Extension](https://marketplace.visualstudio.com/items?itemName=BGforge.bgforge-mls)
|
||||
|
||||
|
||||
|
||||
## Run test script
|
||||
|
||||
1. Move compiled `.int` file into game folder as `scripts/gl_<script_name>.int`
|
||||
1. Move compiled `.int` file into game folder as `data/scripts/gl_<script_name>.int`
|
||||
|
||||
2. Run game, check that game displays message about tests
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
#include "sfall.h"
|
||||
#include "dik.h"
|
||||
#include "define_lite.h"
|
||||
#include "define_extra.h"
|
||||
#include "test_utils.h"
|
||||
|
||||
variable refresh_pc_art_test_done := false;
|
||||
|
||||
procedure run_refresh_pc_art_test begin
|
||||
variable old_gender := get_critter_stat(dude_obj, STAT_gender);
|
||||
variable new_gender := 1 - old_gender;
|
||||
variable dude_pid := obj_pid(dude_obj);
|
||||
variable old_proto_fid := get_proto_data(dude_pid, PROTO_FID);
|
||||
|
||||
set_pc_base_stat(STAT_gender, new_gender);
|
||||
call assertEquals("gender changes immediately", get_critter_stat(dude_obj, STAT_gender), new_gender);
|
||||
call assertEquals("proto fid unchanged before refresh", get_proto_data(dude_pid, PROTO_FID), old_proto_fid);
|
||||
|
||||
refresh_pc_art;
|
||||
|
||||
variable refreshed_proto_fid := get_proto_data(dude_pid, PROTO_FID);
|
||||
call assertNotEquals("refresh_pc_art updates dude proto fid", refreshed_proto_fid, old_proto_fid);
|
||||
|
||||
if (not critter_inven_obj(dude_obj, INVEN_TYPE_WORN)) then begin
|
||||
call assertEquals("refresh_pc_art updates unarmored dude art", obj_art_fid(dude_obj) bwand 0xFFF, refreshed_proto_fid bwand 0xFFF);
|
||||
end
|
||||
|
||||
set_pc_base_stat(STAT_gender, old_gender);
|
||||
refresh_pc_art;
|
||||
call assertEquals("refresh_pc_art restores original proto fid", get_proto_data(dude_pid, PROTO_FID), old_proto_fid);
|
||||
|
||||
refresh_pc_art_test_done := true;
|
||||
call report_test_results("hook_refresh_pc_art");
|
||||
end
|
||||
|
||||
procedure keypress_handler begin
|
||||
variable pressed := get_sfall_arg_at(0);
|
||||
variable key := get_sfall_arg_at(1);
|
||||
|
||||
if (not pressed) then return;
|
||||
if (key != DIK_X) then return;
|
||||
|
||||
if (refresh_pc_art_test_done) then begin
|
||||
display_msg("refresh_pc_art test already ran");
|
||||
return;
|
||||
end
|
||||
|
||||
call run_refresh_pc_art_test;
|
||||
end
|
||||
|
||||
procedure start begin
|
||||
if (not game_loaded) then return;
|
||||
|
||||
display_msg("refresh_pc_art test ready: press X");
|
||||
register_hook_proc(HOOK_KEYPRESS, keypress_handler);
|
||||
end
|
||||
@@ -1,26 +0,0 @@
|
||||
#include "test_utils.h"
|
||||
#include "sfall.h"
|
||||
|
||||
// to test, copy test_extra_msg_file.msg into <game folder>/text/English/game/
|
||||
|
||||
procedure start begin
|
||||
variable auto_id;
|
||||
variable duplicate_auto_id;
|
||||
|
||||
display_msg("Testing add_extra_msg_file...");
|
||||
|
||||
call assertEquals("metarule exists", metarule_exist("add_extra_msg_file"), 1);
|
||||
|
||||
auto_id := add_extra_msg_file("test_extra_msg_file.msg");
|
||||
call assertNotEquals("auto id is not load error", auto_id, -2);
|
||||
call assertTrue("auto id lower bound", auto_id >= 0x3000);
|
||||
call assertTrue("auto id upper bound", auto_id <= 0x3FFF);
|
||||
call assertEquals("auto message", message_str_game(auto_id, 100), "auto extra msg");
|
||||
|
||||
duplicate_auto_id := add_extra_msg_file("TEST_EXTRA_MSG_FILE.MSG");
|
||||
call assertEquals("duplicate file returns existing id", duplicate_auto_id, auto_id);
|
||||
|
||||
call assertEquals("missing file", add_extra_msg_file("missing_extra_msg_file.msg"), -2);
|
||||
|
||||
call report_test_results("add_extra_msg_file");
|
||||
end
|
||||
@@ -164,12 +164,6 @@ procedure array_test_suite begin
|
||||
call assertEquals("saved arrays 3", len_array(list_saved_arrays), len_array(arr2) - 1);
|
||||
*/
|
||||
|
||||
display_msg("Testing nested expressions...");
|
||||
arr := [["one", "two"]];
|
||||
call assertEquals("nested 1", arr[0][1], "two");
|
||||
arr := [["one", "two"], {"three": ["four", -1]}];
|
||||
call assertEquals("nested 2", arr[1].three[0], "four");
|
||||
|
||||
display_msg("All tests finished with "+test_suite_errors+" errors.");
|
||||
|
||||
call report_test_results("arrays");
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,189 @@
|
||||
#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
|
||||
@@ -1,73 +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");
|
||||
call assertEquals("TC1 Size", len_array(result_array), 3);
|
||||
call assertEquals("TC1 Key1", result_array["Key1"], "Value1");
|
||||
call assertEquals("TC1 Key2", result_array["Key2"], "2");
|
||||
call assertEquals("TC1 EmptyValue", result_array["EmptyValue"], "");
|
||||
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");
|
||||
call assertEquals("TC2 get_ini_setting empty int", get_ini_setting("test.ini|ValidSection|EmptyValue"), 0);
|
||||
call assertEquals("TC2 get_ini_string empty string", get_ini_string("test.ini|ValidSection|EmptyValue"), "");
|
||||
|
||||
// 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 missing file", get_ini_setting("notexist.ini|ValidSection|Key2"), -1);
|
||||
call assertEquals("TC3 get_ini_setting missing key", get_ini_setting("test.ini|ValidSection|MissingKey"), -1);
|
||||
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"), -1);
|
||||
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"), -1);
|
||||
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
|
||||
@@ -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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user