Compare commits

...
Author SHA1 Message Date
jirik2077 33926467b2 remove the false guards 2026-07-16 06:04:16 +02:00
Mike Klaas c6e7c5997f more debugging 2026-07-14 22:35:44 -07:00
Mike Klaas b224eae3f7 remove heuristic logging 2026-07-14 22:35:43 -07:00
Mike Klaas 7d3766c5de Two crash fixes (#552)
* Sfall fix for > 160 maps for automap code (RPU triggers this)
* Logic fix for action menu, where actionIndex was incremented unconditionally and could go out of bounds.  This was a regression from vanilla
2026-07-14 22:35:43 -07:00
jirik2077 1b453fad7f Support for EPA gender change operation (#542)
* support for EPA gender change operation

* fix unintented return
2026-07-14 22:35:43 -07:00
Mike Klaas c81f065920 Implement Sfall npc level cap (10) (#545)
Also, fix off-by-one error that can cause proto corruption
2026-07-14 22:35:43 -07:00
Mike Klaas cd46f79b98 Implement real_dude_obj (#537)
This is used by scripts which are party-control aware.  Implementing this means they won't crash and should still work properly
2026-07-14 22:35:43 -07:00
jirik2077 0f25e3df3b Fix division by zero in objectExamineFunc (#541) 2026-07-14 22:35:43 -07:00
Mike Klaas 1265f3287c Increase animation cap (Sfall) (#539)
Fixes #538
2026-07-14 22:35:43 -07:00
jirik2077 8397c6d245 fix healing revision op_tokenize (#536) 2026-07-14 22:35:43 -07:00
Mike Klaas 2e4774c37d Normalize ini paths that start with \ to game root (#501)
* Normalize ini paths that start with `\` to game root

Sfall does this, and we were missing it. https://github.com/sfall-team/sfall/blob/master/sfall/Modules/Scripting/Handlers/IniFiles.cpp#L61-L78

An example is InventoryFilter, which starts its ini path with `\`: https://github.com/rotators/InventoryFilter/blob/master/source/gl_InvenFilter.ssl#L19

* Normalize cache key
2026-07-14 22:35:43 -07:00
Mike Klaas 4c2fbd0cc2 Fix possible double-free in animation_free (#513)
* Fix possible double-free in animation_free

Port of roughly: https://github.com/cambragol/fission-ce/commit/7fafbb11c7daf5fe7f5ae05b92a3bffaaaa7b5a4

Not sure how to trigger it in CE, but seems plausibly possible
2026-07-14 22:35:43 -07:00
Mike Klaas f58023bc2f Fix "Uncontrolled format string" Code Scanning results (#512)
* Fix "Uncontrolled format string" Code Scanning results

https://github.com/fallout2-ce/fallout2-ce/security/code-scanning

* PR comments

* fix missing early return in programExecuteProcedureAsync path
2026-07-14 22:35:43 -07:00
Mike Klaas eb3d0a28e6 Party member equipment management (#528)
Add equipment slots to the companion loot screen.

Items can be equipped directly from player or companion inventory. This also allows switching between party members so you can manage all companion's inventories easily. Does not allow equipping items that would otherwise be disallowed.

Also:

    Calls HOOK_UNWIELD when adding/removing equipment, meaning mods like npc armor work
    Uses HOOK_CANUSE for weapon use checks

Ctrl-click to equip is not implemented since it already transfers items between the two inventories. I tried making it work only for equippable items (and otherwise transfer), but that felt awkward and random. Ctrl-click unequip is implemented.
2026-07-14 22:35:43 -07:00
Mike Klaas cba4a7b711 Support Sfall extra animation codes (#535)
Fixes #534
2026-07-14 22:35:43 -07:00
Mike Klaas 733232c20d Fix blue/green channel mixup in PCX reading (#500) 2026-07-14 22:35:43 -07:00
jirik2077andgithub-actions[bot] 0542cdb3ae Handle unimplemented fs_copy/fs_create/fs_find more gracefully in scripts (#530)
* handle unimplemented fs_copy/fs_create/fs_find more gracefully from the script point of view

* chore: auto-format with clang-format

* moved to sfall_opcodes

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-14 22:35:43 -07:00
Mike Klaas 52a664f6df Add offset tracking to --scan-unimplemented (#517)
* Add offset tracking to --scan-unimplemented

* Add a bunch of missing opcodes
* Hardcode a list of implemented hooks to avoid filtering a long list of hooks that are already done
* Add tracking of specific offsets accessed in unsafe scripting so we can plan replacements

Sample output

```
OFFSET write_int (0x81d1 - 0x1d1 - 465):
  0x5190f8 (5345528):
    - ./mods/InventoryFilter.dat/scripts/gl_InvenFilter_debug.int
    - ./mods/InventoryFilter.dat/scripts/release_gl_InvenFilter.int
  0x59e95c (5892444):
    - ./mods/InventoryFilter.dat/scripts/gl_InvenFilter_debug.int
    - ./mods/InventoryFilter.dat/scripts/release_gl_InvenFilter.int
```

Then we can corrlated it with source:

```
// 0x5190F8 curr_rot
static int gInventoryWindowDudeRotation = 0;
```

* PR feedback
2026-07-14 22:35:43 -07:00
github-actions[bot] 488cf0b8e4 chore: auto-format with clang-format 2026-07-09 16:36:27 +00:00
Mike Klaas 0e5e6ce7fa Extra heap safety 2026-07-09 13:35:58 -03:00
Mike Klaas 40e7a2167b highlighting logging 2026-07-09 09:57:31 -03:00
jirik2077 16143aef74 art/cache debug info 2026-07-09 06:45:17 +02:00
jirik2077 723a8e4abb an extra heap related debugPrint 2026-07-09 06:26:20 +02:00
Mike Klaas bf8a001924 minor fix 2026-07-09 00:10:58 -03:00
Mike Klaas 212b86751c Minor heap fixes and debugging
While investigating https://github.com/fallout2-ce/fallout2-ce/issues/529, I found a few bugs to fix. Nothing that would cause corruption, though.  I added some basic safety checks here that might be useful to narrow down the crash
2026-07-08 23:45:30 -03:00
Mike Klaas 24199e916d Grow ability for ce-dat-tool to extract a list of files (#506)
* Grow ability for `ce-dat-tool` to extract a list of files

This is needed for Et Tu installation, which selectively extracts files from fo1's master.dat.  (It doesn't work if everything is extracted)

* cache entry list to avoid O(n) cost when extracting many files
2026-07-04 14:58:44 -07:00
Mike Klaas 6ee5d04f7c Deobfuscate COMBAT_STATE_* bitmask (#524) 2026-07-04 14:58:16 -07:00
jirik2077 a380a3c9e1 Merge pull request #525 from fallout2-ce/stackable_empty_weapons_fix
QoL Stackable empty weapons regardless of the previous ammo type.
2026-07-04 16:39:39 +02:00
jirik2077 c79bf5eb23 move before the ammo/money handling 2026-07-04 06:57:34 +02:00
jirik2077 7290bb4262 added the sameFlags also for empty weapon check 2026-07-04 06:32:15 +02:00
jirik2077 9a84b67ed3 comment typo fix 2026-07-03 22:54:20 +02:00
jirik2077 949458a559 empty weapons of the same type are considered the same even the ammo was originally different 2026-07-03 22:18:08 +02:00
Mike Klaas 2183c3914c Fix Goris robe/disrobe (#492)
Currently it isn't playing at all, since we're not doing renderPresent() during the animation loop.

This fixes that.  Also, it optimizes the time the animation takes by starting it before the interface combat start/end animation.  This saves half a second or so.

* Remove hidden items when trading with Goris

Also, fix the fix to allow looting with Goris
2026-07-02 19:41:16 -07:00
Mike Klaas c7e187d275 Add art drawing metarules (#507)
interface_art_draw
draw_image{_scaled}
art_frame_data
2026-07-02 19:28:14 -07:00
Mike Klaas 2c495a46ca Update gl_highlighting from sfall (#518)
* Update gl_highlighting from sfall

Pulls in NovaRain's improvement to distinguish empty containers here: https://github.com/sfall-team/sfall/commit/9fa679293269fde377cda1fc97c4668bce741af2x

* Pick up sfall fix: https://github.com/sfall-team/sfall/commit/ca3df3347d2776b83bd6edb480b8b4845731af63#diff-c2e89ad914e1b203c22d4003fb6949828e299bf9fbe46ad7ccd5a2476c053b4d
2026-07-02 06:53:05 -07:00
Mike Klaas 2ed355f8ca Fix initialized variable comparisons and fileReadInt32 (#519)
* Fix initialized variable comparisons and fileReadInt32

Also pulled in an Sfall fix for this function

Fixes https://github.com/fallout2-ce/fallout2-ce/issues/516

* approximate bytes read in xfileRead
2026-07-02 06:52:26 -07:00
Mike Klaas f3caac0bda Refactor inventory.cc (#491)
This adds major simplification and deduplication to inventory.cc, including the start of an "InventoryScroller" class that can manage inventory events without duplicate handling everywhere.  This is needed as we add more complexity to the page.
2026-06-29 21:38:38 -07:00
Mike Klaas fec5df8981 Fix missing argument to format string (#511)
* Add program->name to scriptError

This is @jirik2077 's fix for a segfault, and I agree with the fix.

Fixes #509
2026-06-29 20:41:50 -07:00
Mike Klaas 4715e0b349 Add in-game HELP menu (#498)
Also: fix alignment of skilldex, menu, charset buttons to look visibly depressed.
2026-06-28 22:23:07 -07:00
Mike Klaas 8ce4361365 Windows opcodes (#505)
* create_win / hide_window
* get_window_attributes
* get_window_under_mouse
* set_window_flag
* dialog_message
* display_stats
2026-06-28 15:18:06 -07:00
Mike Klaas 2447593e9c Fix crash due to corrupted whoHitMe (#493)
* Fix crash due to corrupted `whoHitMe`

It's possible for some object's `whoHitMe` to contain `-1`.  It's supposed to be resolved to `nullptr`, but doesn't reliably happen in all cases, esp outside of combat.  The main fix here is to normalize to nullptr when loading the object, instead of just map load, or combat paths.

At some point I thought the problem might have been attacker being a non-critter.  Added some guards against that regardless.  Also, found several bugs and messiness in `apply_damage` which are now fixed.

Fixes https://github.com/alexbatalov/fallout2-ce/issues/368
2026-06-24 20:36:55 -07:00
78 changed files with 4066 additions and 1671 deletions
+3 -2
View File
@@ -66,8 +66,9 @@ Available commands:
1. `./<BUILD_DIR>/ce-dat-tool <archive.dat> list [pattern]`
2. `ce-dat-tool <archive.dat> info [pattern]`
3. `ce-dat-tool <archive.dat> extract [--lower] <output-dir> [pattern]`
4. `ce-dat-tool <archive.dat> cat <entry>`
5. `ce-dat-tool create <input-dir> <archive.dat>`
4. `ce-dat-tool <archive.dat> extract [--lower] (--file-list|--files-from) <list-file> <output-dir>`
5. `ce-dat-tool <archive.dat> cat <entry>`
6. `ce-dat-tool create <input-dir> <archive.dat>`
Use `--lower` with `extract` when you want every extracted file and directory name forced to lowercase.
For example:
+4 -4
View File
@@ -144,13 +144,13 @@ In time this stuff will receive in-game interface, right now you have to do it m
* Expanded 4-row barter screen
* Expanded AP bar
* Party members can loot and barter in place of PC
* Increased pathfinding nodes 5x for more accurate pathfinding
* Increased pathfinding nodes 5x for more accurate pathfinding, and use accurate path length for walk vs run
* 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)
* _a_ to select "all" when selecting item quantity, and for `Take All` when looting
* When bartering, caps default to the right amount to balance the trade
* Music continues playing between maps
* Auto open doors
* Integrated "HELP" menu
* 44.1 kHz stereo sound/music supported, in .ogg and .wav format as well as legacy .acm
* Last used save slot is remembered
* Item/Corpse/Container/Critter highlighting (configure using [mods/sfall-mods.ini](https://github.com/sfall-team/sfall/blob/master/artifacts/config_files/sfall-mods.ini))
+6 -6
View File
@@ -53,7 +53,7 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
| Perks and traits | set_perk_image<br>set_perk_*<br>set_pyromaniac_mod<br>apply_heaveho_fix<br>set_swiftlearner_mod<br>has/set_fake_perk<br>has/set_fake_trait<br>set_selectable_perk<br>set_perkbox_title<br>show/hide_real_perks<br>perk_add_mode<br>clear_selectable_perks<br>add/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 | in_world_map<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 | play_sfall_sound<br>stop_sfall_sound | âś… | `play_sfall_sound` currently supports `.acm`, `.wav`, `.ogg` formats, and can load from `.dat` archives. `.mp3` is not yet supported. |
| Combat / Weapons and ammo | get/set_weapon_ammo_pid<br>get/set_weapon_ammo_count | âś… | - |
@@ -63,7 +63,7 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
| Lists | list_begin<br>list_next<br>list_end<br>list_as_array<br>party_member_list | âś… | - |
| Explosions | 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 | - |
| Art and appearance | art_exists<br>art_frame_data<br>refresh_pc_art<br>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 | âś… | - |
| 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 | âś… | `get_string_pointer` is deprecated and intentionally omitted. |
@@ -75,18 +75,18 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
| Global scripts / Global script functions | set_global_script_repeat<br>set_global_script_type<br>available_global_script_types | âś… 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 | âś… except block_combat, force_aimed_shots, disable_aimed_shots, get_last_target, get_last_attacker, set_spray_settings | - |
| 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<br>create_message_window | implemented: only message_box, get_text_width, show_window, create_message_window | - |
| Interface / Windows and images | interface_art_draw<br>interface_print<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<br>create_message_window | âś… except interface_print, interface_overlay | - |
| Interface / Outline | outlined_object<br>get_outline<br>set_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: critter_inven_obj2, item_weight | - |
| Interface / Inventory | display_stats<br>inventory_redraw<br>critter_inven_obj2<br>get_current_inven_size<br>item_weight | âś… except get_current_inven_size | - |
| Interface / Cursor | get/set_cursor_mode | âś… | - |
| Locks | lock_is_jammed<br>unjam_lock<br>set_unjam_locks_time | not implemented | - |
| INI settings | 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 | âś… | `modified_ini` is intentionally omitted as deprecated. |
| Objects and scripts | set_self<br>set_dude_obj<br>real_dude_obj<br>remove_script<br>get/set_script<br>obj_is_carrying_obj<br>loot_obj<br>dialog_obj<br>obj_under_cursor<br>get/set_object_data<br>get/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/set/remove_script, obj_is_carrying_obj, loot_obj, dialog_obj, obj_under_cursor, get_object_data, get_flags, set_flags, set_unique_id, obj_is_openable, get_proto_data, set_proto_data | - |
| Objects and scripts | set_self<br>set_dude_obj<br>real_dude_obj<br>remove_script<br>get/set_script<br>obj_is_carrying_obj<br>loot_obj<br>dialog_obj<br>obj_under_cursor<br>get/set_object_data<br>get/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, real_dude_obj, get/set/remove_script, obj_is_carrying_obj, loot_obj, dialog_obj, obj_under_cursor, get_object_data, get_flags, set_flags, set_unique_id, obj_is_openable, get_proto_data, set_proto_data | get_object_data assumes raw memory offsets which may return different values in CE |
| Other / Game management | set_movie_path<br>stop/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, signal_close_game | - |
| Gameplay tweaks | 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>set_hp_per_level_mod<br>gdialog_get_barter_mod<br>get/set_unspent_ap_bonus<br>get/set_unspent_ap_perk_bonus<br>set_base_pickpocket_mod<br>set_critter_pickpocket_mod<br>get/set_inven_ap_cost<br>set_drugs_data<br>get_kill_counter<br>mod_kill_counter<br>set_pipboy_available | implemented: gdialog_get_barter_mod, get/set_unspent_ap{_perk}_bonus, get/set_inven_ap_cost | - |
| NPCs | inc_npc_level<br>get_npc_level<br>npc_engine_level_up | not implemented | - |
| Hero Appearange | set_dm/df_model<br>hero_select_win | not implemented | - |
| Hero Appearance | set_dm/df_model<br>hero_select_win<br>set_hero_race<br>set_hero_style | not implemented | - |
| Events | add_g_timer_event<br>remove_timer_event<br>create_spatial<br>spatial_radius | not implemented | - |
| Other | get_year<br>active_hand<br>toggle_active_hand<br>get/set_viewport_x/y<br>get_light_level<br>message_str_game<br>sneak_success<br>unwield_slot<br>add_extra_msg_file<br>get_metarule_table<br>metarule_exist<br> | âś… except get/set_viewport_x/y, sneak_success | `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. |
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.
+1
View File
@@ -0,0 +1 @@
{0}{}{HELP}
+1
View File
@@ -0,0 +1 @@
{0}{}{AIDE}
+1
View File
@@ -0,0 +1 @@
{0}{}{HILFE}
+1
View File
@@ -0,0 +1 @@
{0}{}{AIUTO}
+1
View File
@@ -0,0 +1 @@
{0}{}{POMOC}
+1
View File
@@ -0,0 +1 @@
{0}{}{AJUDA}
+1
View File
@@ -0,0 +1 @@
{0}{}{ПОМОЩЬ}
+1
View File
@@ -0,0 +1 @@
{0}{}{AYUDA}
+2
View File
@@ -130,6 +130,8 @@ inventory_columns=2
;Set to 1 to scale only the main menu background image to fit the screen while maintaining aspect ratio.
;Set to 2 to also scale the main menu button panel, red buttons, and button labels with the menu art.
main_menu_scale_mode=1
;Set to 1 to show a Help option in the in-game options menu which opens the F1 help screen.
in_game_menu_help=1
[qol]
+10
View File
@@ -1,5 +1,6 @@
#include "lib.arrays.h" // For arrays_equal, len_array, etc.
#include "test_utils.h" // For assertEquals, VALTYPE_STR, report_test_results
#include "sfall.h"
// To test: copy test.ini into the game folder before running
procedure ini_test_suite begin
@@ -29,6 +30,15 @@ procedure ini_test_suite begin
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"), "");
// Regression: sfall resolves leading-backslash paths relative to the game root.
result_array := get_ini_section("\\test.ini", "ValidSection");
call assertEquals("TC2B Size", len_array(result_array), 3);
call assertEquals("TC2B Key1", result_array["Key1"], "Value1");
call assertEquals("TC2B get_ini_setting int", get_ini_setting("\\test.ini|ValidSection|Key2"), 2);
call assertEquals("TC2B get_ini_string string", get_ini_string("\\test.ini|ValidSection|Key1"), "Value1");
result_array := get_ini_sections("\\test.ini");
call assertEquals("TC2B get_ini_sections size", len_array(result_array), 3);
// INI file not found
result_array := get_ini_section("nonexistent.ini", "AnySection");
call assertEquals("TC3 Size", len_array(result_array), 0);
+42 -12
View File
@@ -1,5 +1,8 @@
#include "animation.h"
#include <algorithm>
#include <array>
#include <cstddef>
#include <stdio.h>
#include <string.h>
@@ -37,9 +40,13 @@
namespace fallout {
#define ANIMATION_SEQUENCE_LIST_CAPACITY 32
// sfall exposes this as [Misc] AnimationsAtOnceLimit. Keep CE aligned with
// sfall's default 64 total / 52 non-reserved slots for now; this can become
// configurable later.
#define ANIMATION_SEQUENCE_LIST_CAPACITY 64
#define ANIMATION_DESCRIPTION_LIST_CAPACITY 55
#define ANIMATION_SAD_LIST_CAPACITY 24
#define ANIMATION_SAD_LIST_CAPACITY 64
#define ANIMATION_NON_RESERVED_LIST_CAPACITY 52
#define ANIMATION_SEQUENCE_FORCED 0x01
@@ -75,6 +82,11 @@ typedef enum AnimationKind {
ANIM_KIND_SET_LIGHT_INTENSITY,
} AnimationKind;
static bool animationKindIsCallback(int kind)
{
return kind == ANIM_KIND_CALLBACK || kind == ANIM_KIND_CALLBACK3;
}
typedef enum AnimationSequenceFlags {
// Specifies that the animation sequence has high priority, it cannot be
// cleared.
@@ -333,7 +345,7 @@ static AnimationSad gAnimationSads[ANIMATION_SAD_LIST_CAPACITY];
static PathNode gClosedPathNodeList[PATH_NODE_CAPACITY];
// 0x54CC14 anim_set
static AnimationSequence gAnimationSequences[32];
static AnimationSequence gAnimationSequences[ANIMATION_SEQUENCE_LIST_CAPACITY];
// 0x561814 seen_tile
static unsigned char gPathfinderProcessedTiles[5000];
@@ -437,7 +449,7 @@ static int _anim_free_slot(int requestOptions)
}
return -1;
} else if ((requestOptions & ANIMATION_REQUEST_RESERVED) != 0 || v2 < 20) {
} else if ((requestOptions & ANIMATION_REQUEST_RESERVED) != 0 || v2 < ANIMATION_NON_RESERVED_LIST_CAPACITY) {
return v1;
}
@@ -472,7 +484,7 @@ int reg_anim_clear(Object* a1)
int animationDescriptionIndex;
for (animationDescriptionIndex = 0; animationDescriptionIndex < animationSequence->length; animationDescriptionIndex++) {
AnimationDescription* animationDescription = &(animationSequence->animations[animationDescriptionIndex]);
if (a1 != animationDescription->owner || animationDescription->kind == 11) {
if (a1 != animationDescription->owner || animationKindIsCallback(animationDescription->kind)) {
continue;
}
@@ -586,7 +598,7 @@ static int _check_registry(Object* obj)
if (animationSequenceIndex != gAnimationSequenceCurrentIndex && animationSequence->step != ANIM_COMPLETE) {
for (int animationDescriptionIndex = 0; animationDescriptionIndex < animationSequence->length; animationDescriptionIndex++) {
AnimationDescription* animationDescription = &(animationSequence->animations[animationDescriptionIndex]);
if (obj == animationDescription->owner && animationDescription->kind != 11) {
if (obj == animationDescription->owner && !animationKindIsCallback(animationDescription->kind)) {
if ((animationSequence->flags & ANIM_SEQ_INSIGNIFICANT) == 0) {
return -1;
}
@@ -618,7 +630,7 @@ int animationIsBusy(Object* a1)
continue;
}
if (animationDescription->kind == ANIM_KIND_CALLBACK) {
if (animationKindIsCallback(animationDescription->kind)) {
continue;
}
@@ -1608,12 +1620,25 @@ static int _anim_set_end(int animationSequenceIndex)
}
}
std::array<Object*, ANIMATION_DESCRIPTION_LIST_CAPACITY> destroyedOwners = {};
std::size_t destroyedOwnersLength = 0;
auto ownerWasDestroyed = [&destroyedOwners, &destroyedOwnersLength](Object* owner) {
return std::find(destroyedOwners.begin(), destroyedOwners.begin() + destroyedOwnersLength, owner) != destroyedOwners.begin() + destroyedOwnersLength;
};
for (i = 0; i < animationSequence->length; i++) {
animationDescription = &(animationSequence->animations[i]);
if (animationDescription->kind == ANIM_KIND_HIDE && ((i < animationSequence->animationIndex) || (animationDescription->extendedFlags & ANIMATION_SEQUENCE_FORCED))) {
Object* owner = animationDescription->owner;
if (ownerWasDestroyed(owner)) {
continue;
}
destroyedOwners[destroyedOwnersLength++] = owner;
Rect rect;
int elevation = animationDescription->owner->elevation;
objectDestroy(animationDescription->owner, &rect);
int elevation = owner->elevation;
objectDestroy(owner, &rect);
tileWindowRefreshRect(&rect, elevation);
}
}
@@ -1622,18 +1647,23 @@ static int _anim_set_end(int animationSequenceIndex)
animationDescription = &(animationSequence->animations[i]);
if (animationDescription->artCacheKey) {
artUnlock(animationDescription->artCacheKey);
animationDescription->artCacheKey = nullptr;
}
if (animationDescription->kind != 11 && animationDescription->kind != 12) {
if (!animationKindIsCallback(animationDescription->kind)) {
// TODO: Check.
if (animationDescription->kind != ANIM_KIND_PING) {
Object* owner = animationDescription->owner;
if (ownerWasDestroyed(owner)) {
continue;
}
if (FID_TYPE(owner->fid) == OBJ_TYPE_CRITTER) {
int j = 0;
for (; j < i; j++) {
AnimationDescription* ad = &(animationSequence->animations[j]);
if (owner == ad->owner) {
if (ad->kind != ANIM_KIND_CALLBACK && ad->kind != ANIM_KIND_CALLBACK3) {
if (!animationKindIsCallback(ad->kind)) {
break;
}
}
@@ -3345,7 +3375,7 @@ static unsigned int animationComputeTicksPerFrame(Object* object, int fid)
}
}
return 1000 / fps;
return std::max(1000 / fps, 1);
}
int animationRegisterSetLightIntensity(Object* owner, int lightDistance, int lightIntensity, int delay)
+256 -108
View File
File diff suppressed because it is too large Load Diff
+18 -4
View File
@@ -100,6 +100,11 @@ typedef enum WeaponAnimation {
WEAPON_ANIMATION_LASER_RIFLE, // k
WEAPON_ANIMATION_MINIGUN, // l
WEAPON_ANIMATION_LAUNCHER, // m
WEAPON_ANIMATION_SFALL_S, // s
WEAPON_ANIMATION_SFALL_O, // o
WEAPON_ANIMATION_SFALL_P, // p
WEAPON_ANIMATION_SFALL_Q, // q
WEAPON_ANIMATION_SFALL_T, // t
WEAPON_ANIMATION_COUNT,
} WeaponAnimation;
@@ -128,7 +133,6 @@ void artRender(int fid, unsigned char* dest, int width, int height, int pitch);
int art_list_str(int fid, char* name);
Art* artLock(int fid, CacheEntry** cache_entry);
unsigned char* artLockFrameData(int fid, int frame, int direction, CacheEntry** out_cache_entry);
unsigned char* artLockFrameDataReturningSize(int fid, CacheEntry** out_cache_entry, int* widthPtr, int* heightPtr);
int artUnlock(CacheEntry* cache_entry);
int artCacheFlush();
int artCopyFileName(int objectType, int id, char* dest);
@@ -140,9 +144,10 @@ int artGetFrameCount(Art* art);
int artGetWidth(Art* art, int frame, int direction);
int artGetHeight(Art* art, int frame, int direction);
int artGetSize(Art* art, int frame, int direction, int* out_width, int* out_height);
int artGetFrameOffsets(Art* art, int frame, int direction, int* xPtr, int* yPtr);
int artGetFrameOffsets(const Art* art, int frame, int direction, int* xPtr, int* yPtr);
int artGetRotationOffsets(Art* art, int rotation, int* out_offset_x, int* out_offset_y);
unsigned char* artGetFrameData(Art* art, int frame, int direction);
unsigned char* artGetFrameData(const Art* art, int frame, int direction, int* widthPtr, int* heightPtr, int* xOffsetPtr, int* yOffsetPtr);
ArtFrame* artGetFrame(const Art* art, int frame, int direction);
ConstBuffer2D artGetFrameBuffer(const Art* art, int frame, int direction);
bool artExists(int fid);
@@ -185,8 +190,8 @@ private:
const char* _path = nullptr;
};
// A helper for using RAII to read single-frame FRM's.
// lock/unlock use the art-cache instead of just loading/unloading directly.
// RAII helper for locking one selected frame from FID-backed or path-backed art.
// lock/unlock use caches instead of just loading/unloading directly.
class FrmImage {
public:
FrmImage();
@@ -201,13 +206,19 @@ public:
bool isLocked() const { return _key != nullptr || _namedKey; }
bool lock(const FrmId& frmId);
bool lock(const FrmId& frmId, int frame, int direction);
bool lock(unsigned int fid);
bool lock(unsigned int fid, int frame, int direction);
bool lock(const char* frmPath);
bool lock(const char* frmPath, int frame, int direction);
bool lock(ObjectType objType, const char* frmRelativePath);
bool lock(ObjectType objType, const char* frmRelativePath, int frame, int direction);
void unlock();
int getWidth() const { return _width; }
int getHeight() const { return _height; }
int getXOffset() const { return _xOffset; }
int getYOffset() const { return _yOffset; }
// Returns FRM frame data if locked, nullptr otherwise.
unsigned char* getData() const { return _data; }
@@ -215,12 +226,15 @@ public:
private:
void resetInternal();
bool setFrame(const Art* art, int frame, int direction);
std::shared_ptr<NamedCacheEntry> _namedKey;
CacheEntry* _key = nullptr;
unsigned char* _data = nullptr;
int _width = 0;
int _height = 0;
int _xOffset = 0;
int _yOffset = 0;
};
} // namespace fallout
+27 -3
View File
@@ -46,6 +46,13 @@ static void _decode_map_data(int elevation);
static int automapCreate();
static int _copy_file_data(File* stream1, File* stream2, int length);
static int gAutomapWindow = -1;
static bool automapEntryIsValid(int map, int elevation)
{
return map >= 0 && map < AUTOMAP_MAP_COUNT && elevationIsValid(elevation);
}
typedef enum AutomapFrm {
AUTOMAP_FRM_BACKGROUND,
AUTOMAP_FRM_BUTTON_UP,
@@ -291,6 +298,10 @@ int automapSave(File* stream)
// 0x41B8B4 automapDisplayMap
int _automapDisplayMap(int map)
{
if (map < 0 || map >= AUTOMAP_MAP_COUNT) {
return -1;
}
return _displayMapList[map];
}
@@ -326,6 +337,7 @@ void automapShow(bool isInGame, bool isUsingScanner)
int automapWindowY = (screenGetHeight() - AUTOMAP_WINDOW_HEIGHT) / 2;
// adding WINDOW_TRANSPARENT and WINDOW_DRAGGABLE_BY_BACKGROUND for testing temporarily
int window = windowCreate(automapWindowX, automapWindowY, AUTOMAP_WINDOW_WIDTH, AUTOMAP_WINDOW_HEIGHT, color, WINDOW_MODAL | WINDOW_MOVE_ON_TOP | WINDOW_TRANSPARENT | WINDOW_DRAGGABLE_BY_BACKGROUND);
gAutomapWindow = window;
int scannerBtn = buttonCreate(window,
111,
@@ -493,10 +505,16 @@ void automapShow(bool isInGame, bool isUsingScanner)
}
windowDestroy(window);
gAutomapWindow = -1;
fontSetCurrent(oldFont);
touch_set_touchscreen_mode(false);
}
int automapGetWindow()
{
return windowGetWindow(gAutomapWindow) != nullptr ? gAutomapWindow : -1;
}
// Renders automap in Map window.
//
// 0x41BD1C draw_top_down_map
@@ -534,13 +552,13 @@ static void automapRenderInMapWindow(int window, int elevation, unsigned char* b
continue;
}
if (object->pid == PROTO_ID_0x2000031) {
if (object->pid == PROTO_ID_EXIT_GRID_MAP_MARKER) {
objectColor = _colorTable[32328];
} else if (objectType == OBJ_TYPE_WALL) {
objectColor = _colorTable[992];
} else if (objectType == OBJ_TYPE_SCENERY
&& (flags & AUTOMAP_WTH_HIGH_DETAILS) != 0
&& object->pid != PROTO_ID_0x2000158) {
&& object->pid != PROTO_ID_BLOCK_HEX_AUTO_INVISO) {
objectColor = _colorTable[480];
} else if (object == gDude) {
objectColor = _colorTable[31744];
@@ -687,6 +705,9 @@ int automapSaveCurrent()
{
int map = mapGetCurrentMap();
int elevation = gElevation;
if (!automapEntryIsValid(map, elevation)) {
return 0;
}
int entryOffset = gAutomapHeader.offsets[map][elevation];
if (entryOffset < 0) {
@@ -932,6 +953,9 @@ err:
static int automapLoadEntry(int map, int elevation)
{
gAutomapEntry.compressedData = nullptr;
if (!automapEntryIsValid(map, elevation)) {
return -1;
}
char path[COMPAT_MAX_PATH];
snprintf(path, sizeof(path), "%s\\%s", "MAPS", AUTOMAP_DB);
@@ -1081,7 +1105,7 @@ static void _decode_map_data(int elevation)
int contentType;
int objectType = FID_TYPE(object->fid);
if (objectType == OBJ_TYPE_SCENERY && object->pid != PROTO_ID_0x2000158) {
if (objectType == OBJ_TYPE_SCENERY && object->pid != PROTO_ID_BLOCK_HEX_AUTO_INVISO) {
contentType = 2;
} else if (objectType == OBJ_TYPE_WALL) {
contentType = 1;

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