mirror of
https://github.com/fallout2-ce/fallout2-ce.git
synced 2026-07-27 16:47:11 -07:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b37a9b2dc | ||
|
|
efd8a4a525 | ||
|
|
4c09ffe9e6 | ||
|
|
1026c79ac5 | ||
|
|
3849b4ea17 | ||
|
|
6cbe68aef9 | ||
|
|
066595f683 |
+12
@@ -400,3 +400,15 @@ FodyWeavers.xsd
|
||||
# versioning header
|
||||
src/platform/git_version.h
|
||||
CMakeUserPresets.json
|
||||
|
||||
# Local configs for AI agents
|
||||
.opencode
|
||||
.agents
|
||||
.aider*
|
||||
.gemini
|
||||
.claude
|
||||
.mcp.json
|
||||
AGENTS.md
|
||||
CLAUDE.md
|
||||
GEMINI.md
|
||||
opencode.json
|
||||
+30
-19
@@ -20,6 +20,10 @@ set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||
set(CMAKE_CXX_EXTENSIONS NO)
|
||||
|
||||
if(MSVC)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/EHsc>)
|
||||
endif()
|
||||
|
||||
option(FALLOUT_VENDORED "Use vendored third-party libraries" ON)
|
||||
|
||||
if(ANDROID)
|
||||
@@ -448,27 +452,34 @@ target_include_directories(${EXECUTABLE_NAME} PRIVATE ${ZLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(${EXECUTABLE_NAME} ${SDL2_LIBRARIES})
|
||||
target_include_directories(${EXECUTABLE_NAME} PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
|
||||
add_executable(fallout2-dat
|
||||
"tools/dat_tool.cc"
|
||||
"src/dfile.cc"
|
||||
"src/platform_compat.cc"
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
set_target_properties(fallout2-dat PROPERTIES
|
||||
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
|
||||
XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO"
|
||||
if((NOT ANDROID) AND (NOT IOS) AND (NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten"))
|
||||
add_executable(fallout2-dat
|
||||
"tools/dat_tool.cc"
|
||||
"tools/dat_archive.cc"
|
||||
"tools/fo1_dat_lzss.cc"
|
||||
"src/dfile.cc"
|
||||
"src/platform_compat.cc"
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(fallout2-dat
|
||||
fpattern_windows::fpattern_windows
|
||||
${ZLIB_LIBRARIES}
|
||||
${SDL2_LIBRARIES}
|
||||
)
|
||||
target_include_directories(fallout2-dat PRIVATE "src")
|
||||
target_include_directories(fallout2-dat PRIVATE ${ZLIB_INCLUDE_DIRS})
|
||||
target_include_directories(fallout2-dat PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
if(APPLE)
|
||||
set_target_properties(fallout2-dat PROPERTIES
|
||||
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
|
||||
XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO"
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(fallout2-dat
|
||||
fpattern_windows::fpattern_windows
|
||||
${ZLIB_LIBRARIES}
|
||||
${SDL2_LIBRARIES}
|
||||
)
|
||||
if(WIN32)
|
||||
target_link_libraries(fallout2-dat winmm)
|
||||
endif()
|
||||
target_include_directories(fallout2-dat PRIVATE "src")
|
||||
target_include_directories(fallout2-dat PRIVATE ${ZLIB_INCLUDE_DIRS})
|
||||
target_include_directories(fallout2-dat PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
if(IOS)
|
||||
|
||||
+51
-5
@@ -2,13 +2,59 @@
|
||||
|
||||
This file is currently a shell to be filled in over time. For now, it only captures project-specific maintenance notes that are easy to miss.
|
||||
|
||||
For current sfall compatibility status and the remaining work needed to close gaps, see [SFALL_COMPATIBILITY.md](SFALL_COMPATIBILITY.md).
|
||||
|
||||
## Building
|
||||
|
||||
The project uses CMake, so building mostly comes down to picking the right preset.
|
||||
|
||||
### macOS
|
||||
|
||||
- Install the Command Line Tools if needed: `xcode-select --install`
|
||||
- Install the common build dependencies: `brew install cmake ninja`
|
||||
- Configure and build:
|
||||
|
||||
```sh
|
||||
cmake -S . -B out/build/macos -G Ninja -DCMAKE_BUILD_TYPE=Debug
|
||||
cmake --build out/build/macos --target fallout2-ce
|
||||
```
|
||||
|
||||
### Linux
|
||||
|
||||
Install SDL and a toolchain:
|
||||
|
||||
- Debian / Ubuntu:
|
||||
```sh
|
||||
sudo apt install build-essential cmake ninja-build libsdl2-dev zlib1g-dev
|
||||
```
|
||||
- Fedora:
|
||||
```sh
|
||||
sudo dnf install gcc-c++ cmake ninja-build SDL2-devel zlib-devel
|
||||
```
|
||||
- Arch Linux:
|
||||
```sh
|
||||
sudo pacman -S base-devel cmake ninja sdl2 zlib
|
||||
```
|
||||
|
||||
Then build:
|
||||
```
|
||||
cmake -S . -B out/build/linux-x64-debug -G Ninja -DCMAKE_BUILD_TYPE=Debug
|
||||
cmake --build out/build/linux-x64-debug --target fallout2-ce
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
TODO: add Visual Studio build instructions. This will likely need a Visual Studio generator or preset plus the MSVC toolchain and Windows SDK.
|
||||
|
||||
## `.dat` CLI Tool
|
||||
|
||||
There is a small command-line archive tool in this repo for inspecting Fallout `.dat` files using the same reader implementation as the game.
|
||||
There is a small command-line archive tool in this repo for inspecting Fallout `.dat` files. It supports both Fallout 2 and Fallout 1 archives.
|
||||
|
||||
Build it with `make TARGET=fallout2-dat`.
|
||||
From the repo root, build it with your normal CMake workflow, targeting `fallout2-dat`:
|
||||
|
||||
The executable is written to your selected `BUILD_DIR`, so the default path is `out/build/local-debug-arm64/fallout2-dat` on this macOS setup, but other platforms or custom build directories will differ.
|
||||
`cmake --build out/build/<preset-name> --target fallout2-dat`
|
||||
|
||||
The executable is written to your selected build directory, so the exact path will vary by preset and configuration.
|
||||
|
||||
The current tool is read-only. Available commands:
|
||||
|
||||
@@ -18,9 +64,9 @@ The current tool is read-only. Available commands:
|
||||
4. `./<BUILD_DIR>/fallout2-dat <archive.dat> cat <entry>`
|
||||
|
||||
Use `--lower` with `extract` when you want every extracted file and directory name forced to lowercase.
|
||||
For example, from `/Applications/Fallout2Codex` you can run:
|
||||
For example:
|
||||
|
||||
`/Users/klaas/game/fallout2-ce/out/build/local-debug-arm64/fallout2-dat master.dat extract --lower /tmp/fallout2-dat-lower data\\*`
|
||||
`fallout2-dat master.dat extract --lower /tmp/fallout2-dat-lower data\\*`
|
||||
|
||||
## Updating SDL
|
||||
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
|
||||
Fallout 2 Community Edition is a fully working re-implementation of Fallout 2, with the same original gameplay, engine bugfixes, and some quality of life improvements, that works (mostly) hassle-free on multiple platforms. This is a fork of the original project, which isn't getting regular updates.
|
||||
|
||||
Popular Fallout 2 total conversion mods are partially supported. Original versions of Nevada and Sonora (that do not rely on extended features provided by Sfall) work. [Fallout 2 Restoration Project](https://github.com/BGforgeNet/Fallout2_Restoration_Project), [Fallout Et Tu](https://github.com/rotators/Fo1in2) and [Olympus 2207](https://olympus2207.com) are not yet supported. Other mods (particularly Resurrection and Yesterday) are not tested.
|
||||
Popular Fallout 2 total conversion mods are partially supported. Original versions of Nevada and Sonora (that do not rely on extended features provided by Sfall) work. [Fallout 2 Restoration Project](https://github.com/BGforgeNet/Fallout2_Restoration_Project) is supported (in Beta). [Fallout Et Tu](https://github.com/rotators/Fo1in2) and [Olympus 2207](https://olympus2207.com) are not yet supported. Other mods (particularly Resurrection and Yesterday) are not tested.
|
||||
|
||||
There is also [Fallout Community Edition](https://github.com/alexbatalov/fallout1-ce) (not affiliated with this fork).
|
||||
|
||||
For build and contributor notes, see [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
|
||||
## Installation
|
||||
|
||||
You must own the game to play. Purchase your copy on [GOG](https://www.gog.com/game/fallout_2), [Epic Games](https://store.epicgames.com/p/fallout-2) or [Steam](https://store.steampowered.com/app/38410). Download latest [release](https://github.com/fallout2-ce/fallout2-ce/releases) or build from source.
|
||||
You must own the game to play. Purchase your copy on [GOG](https://www.gog.com/game/fallout_2), [Epic Games](https://store.epicgames.com/p/fallout-2) or [Steam](https://store.steampowered.com/app/38410). Download latest [release](https://github.com/fallout2-ce/fallout2-ce/releases) or build from source.
|
||||
|
||||
### Windows
|
||||
|
||||
@@ -22,17 +24,10 @@ Download and copy `fallout2-ce.exe` to your `Fallout2` folder. It serves as a dr
|
||||
|
||||
```console
|
||||
$ sudo apt install innoextract
|
||||
$ innoextract ~/Downloads/setup_fallout2_2.1.0.18.exe -I app
|
||||
$ mv app Fallout2
|
||||
$ innoextract ~/Downloads/setup_fallout_2_1.02_gog_v1_\(77792\).exe -d Fallout2
|
||||
```
|
||||
|
||||
- Download and copy `fallout2-ce` to this folder.
|
||||
|
||||
- Install [SDL2](https://libsdl.org/download-2.0.php):
|
||||
|
||||
```console
|
||||
$ sudo apt install libsdl2-2.0-0
|
||||
```
|
||||
- Download the Linux release archive, extract `fallout2-ce`, and copy it into this folder.
|
||||
|
||||
- Run `./fallout2-ce`.
|
||||
|
||||
@@ -48,13 +43,13 @@ $ sudo apt install libsdl2-2.0-0
|
||||
|
||||
```console
|
||||
$ brew install innoextract
|
||||
$ innoextract ~/Downloads/setup_fallout2_2.1.0.18.exe -I app
|
||||
$ mv app /Applications/Fallout2
|
||||
$ innoextract ~/Downloads/setup_fallout_2_1.02_gog_v1_\(77792\).exe -d fallout2
|
||||
$ mv fallout2 /Applications/Fallout2
|
||||
```
|
||||
|
||||
- Download and copy `fallout2-ce.app` to this folder.
|
||||
- [Download](https://github.com/fallout2-ce/fallout2-ce/releases) and copy `Fallout II Community Edition.app` to this folder.
|
||||
|
||||
- Run `fallout2-ce.app`.
|
||||
- Run `Fallout II Community Edition.app`.
|
||||
|
||||
### Android
|
||||
|
||||
@@ -141,11 +136,9 @@ For a sample ddraw.ini configuration file, containing all currently working sett
|
||||
|
||||
## 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.
|
||||
Integrating Sfall goodies is the top priority. Quality of life updates are OK too. In any case open up an issue with your suggestion or to notify other people that something is being worked on.
|
||||
|
||||
### Integrating Sfall
|
||||
|
||||
There are literally hundreds if not thousands of fixes and features in sfall. I guess not all of them are needed in Community Edition, but for the sake of compatibility with big mods out there, let's integrate them all.
|
||||
For current sfall compatibility status and the remaining work needed to close gaps, see [SFALL_COMPATIBILITY.md](SFALL_COMPATIBILITY.md).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
|
||||
| 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 | - |
|
||||
| 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 | - |
|
||||
| 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. |
|
||||
| Interface / Tags | show_iface_tag<br>hide_iface_tag<br>is_iface_tag_active<br>set_iface_tag_text<br>add_iface_tag | âś… except set_iface_tag_text, add_iface_tag | CE only handles built-in interface tags here; custom tag creation/text is not supported yet. |
|
||||
@@ -61,7 +61,7 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
|
||||
| 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 | âś… 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: get_attack_type, get_bodypart_hit_modifier, combat_data, set_bodypart_hit_modifier, get_critter_current_ap, set_critter_current_ap, get_combat_free_move, set_combat_free_move | - |
|
||||
| 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: get_attack_type, get/set_bodypart_hit_modifier, combat_data, get/set_critter_current_ap, get/set_combat_free_move | - |
|
||||
| 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 / Outline | outlined_object<br>get_outline<br>set_outline | âś… | - |
|
||||
@@ -72,7 +72,7 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
|
||||
| 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 | âś… except 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>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, obj_is_openable, get_proto_data, set_proto_data | - |
|
||||
| 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>get_unspent_ap_bonus<br>gdialog_get_barter_mod<br>set_unspent_ap_bonus<br>get/set_unspent_ap_perk_bonus<br>set_inven_ap_cost<br>set_base_pickpocket_mod<br>set_critter_pickpocket_mod<br>get_inven_ap_cost<br>set_drugs_data<br>get_kill_counter<br>mod_kill_counter<br>set_pipboy_available | implemented: gdialog_get_barter_mod | - |
|
||||
| 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/<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 | - |
|
||||
| NPCs | inc_npc_level<br>get_npc_level<br>npc_engine_level_up | not implemented | - |
|
||||
| Other | get_year<br>set_dm/df_model<br>active_hand<br>toggle_active_hand<br>get/set_viewport_x/y<br>hero_select_win<br>get_light_level<br>message_str_game<br>sneak_success<br>create_spatial<br>unwield_slot<br>add_g_timer_event<br>add_extra_msg_file<br>get_metarule_table<br>metarule_exist<br>remove_timer_event<br>spatial_radius | implemented: get_year, active_hand, toggle_active_hand, get_light_level, 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. |
|
||||
|
||||
|
||||
Binary file not shown.
@@ -96,6 +96,8 @@ display_bonus_damage=1
|
||||
display_karma_changes=0
|
||||
;Set to one to hide areas outside map bounds when using higher than 640x480 resolution, and to zero to disable
|
||||
enable_high_resolution_stencil=1
|
||||
;Set to 1 to extend the action points bar to show up to 16 AP instead of 10 (requires iface_apbar_e.frm)
|
||||
extend_ap_bar=0
|
||||
iface_bar_mode=0
|
||||
iface_bar_side_art=2
|
||||
iface_bar_sides_ori=0
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
#include "sfall.h"
|
||||
#include "define_extra.h"
|
||||
#include "test_utils.h"
|
||||
|
||||
#define TEST_ITEM_PID 40
|
||||
|
||||
variable blocker_obj := 0;
|
||||
variable radius_obj := 0;
|
||||
|
||||
procedure cleanup begin
|
||||
if (blocker_obj) then begin
|
||||
destroy_object(blocker_obj);
|
||||
blocker_obj := 0;
|
||||
end
|
||||
if (radius_obj) then begin
|
||||
destroy_object(radius_obj);
|
||||
radius_obj := 0;
|
||||
end
|
||||
end
|
||||
|
||||
procedure start begin
|
||||
@@ -45,6 +53,26 @@ procedure start begin
|
||||
call assertEquals("one-step path length", len_array(path), 1);
|
||||
if (len_array(path) == 1) then
|
||||
call assertEquals("one-step path direction", get_array(path, 0), step_dir);
|
||||
|
||||
radius_obj := create_object_sid(TEST_ITEM_PID, step_tile, elev, -1);
|
||||
call assertNotEquals("spawned radius item", radius_obj, 0);
|
||||
if (radius_obj) then begin
|
||||
objs := sfall_func3("objects_in_radius", dude_tile, 1, elev);
|
||||
call assertTrue("objects_in_radius all contains dude", scan_array(objs, dude_obj) != -1);
|
||||
call assertTrue("objects_in_radius all contains item", scan_array(objs, radius_obj) != -1);
|
||||
|
||||
objs := sfall_func3("objects_in_radius", dude_tile, 0, elev);
|
||||
call assertTrue("objects_in_radius radius 0 contains dude", scan_array(objs, dude_obj) != -1);
|
||||
call assertEquals("objects_in_radius radius 0 excludes adjacent item", scan_array(objs, radius_obj), -1);
|
||||
|
||||
objs := objects_in_radius(dude_tile, 1, elev, OBJ_TYPE_ITEM);
|
||||
call assertTrue("objects_in_radius item filter contains item", scan_array(objs, radius_obj) != -1);
|
||||
call assertEquals("objects_in_radius item filter excludes dude", scan_array(objs, dude_obj), -1);
|
||||
|
||||
objs := objects_in_radius(dude_tile, 1, elev, OBJ_TYPE_CRITTER);
|
||||
call assertTrue("objects_in_radius critter filter contains dude", scan_array(objs, dude_obj) != -1);
|
||||
call assertEquals("objects_in_radius critter filter excludes item", scan_array(objs, radius_obj), -1);
|
||||
end
|
||||
end
|
||||
|
||||
if (block_tile != -1) then begin
|
||||
|
||||
+11
-2
@@ -11,7 +11,7 @@
|
||||
#include "draw.h"
|
||||
#include "game.h"
|
||||
#include "memory.h"
|
||||
#include "object.h"
|
||||
#include "obj_types.h"
|
||||
#include "proto.h"
|
||||
#include "settings.h"
|
||||
|
||||
@@ -821,7 +821,7 @@ unsigned char* artGetFrameData(Art* art, int frame, int direction)
|
||||
}
|
||||
|
||||
// 0x419880
|
||||
ArtFrame* artGetFrame(Art* art, int frame, int rotation)
|
||||
ArtFrame* artGetFrame(const Art* art, int frame, int rotation)
|
||||
{
|
||||
if (rotation < 0 || rotation >= 6) {
|
||||
return nullptr;
|
||||
@@ -842,6 +842,15 @@ ArtFrame* artGetFrame(Art* art, int frame, int rotation)
|
||||
return frm;
|
||||
}
|
||||
|
||||
ConstBuffer2D artGetFrameBuffer(const Art* art, int frame, int direction)
|
||||
{
|
||||
ArtFrame* frm = artGetFrame(art, frame, direction);
|
||||
if (frm == nullptr) {
|
||||
return { nullptr, 0, 0 };
|
||||
}
|
||||
return { reinterpret_cast<unsigned char*>(frm) + sizeof(*frm), frm->width, frm->height };
|
||||
}
|
||||
|
||||
// 0x4198C8
|
||||
bool artExists(int fid)
|
||||
{
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
#define ART_H
|
||||
|
||||
#include "cache.h"
|
||||
#include "db.h"
|
||||
#include "heap.h"
|
||||
#include "obj_types.h"
|
||||
#include "platform_compat.h"
|
||||
#include "draw.h"
|
||||
#include "memory.h"
|
||||
#include "proto_types.h"
|
||||
|
||||
namespace fallout {
|
||||
@@ -141,7 +139,8 @@ int artGetSize(Art* art, int frame, int direction, int* out_width, int* out_heig
|
||||
int artGetFrameOffsets(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);
|
||||
ArtFrame* artGetFrame(Art* art, int frame, int direction);
|
||||
ArtFrame* artGetFrame(const Art* art, int frame, int direction);
|
||||
ConstBuffer2D artGetFrameBuffer(const Art* art, int frame, int direction);
|
||||
bool artExists(int fid);
|
||||
bool _art_fid_valid(int fid);
|
||||
int _art_alias_num(int index);
|
||||
@@ -165,6 +164,8 @@ public:
|
||||
int getHeight() const { return _height; }
|
||||
unsigned char* getData() const { return _data; }
|
||||
|
||||
ConstBuffer2D getBuffer() const { return { _data, _width, _height }; };
|
||||
|
||||
private:
|
||||
CacheEntry* _key;
|
||||
unsigned char* _data;
|
||||
@@ -172,6 +173,8 @@ private:
|
||||
int _height;
|
||||
};
|
||||
|
||||
using ArtPtr = InternalPtr<Art>;
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif
|
||||
|
||||
@@ -331,26 +331,6 @@ bool configRead(Config* config, const char* filePath, bool isDb)
|
||||
configParseLine(config, string);
|
||||
}
|
||||
fileClose(stream);
|
||||
|
||||
// Build patch file path by inserting "#patch" before the extension.
|
||||
char patchPath[COMPAT_MAX_PATH];
|
||||
const char* dot = strrchr(filePath, '.');
|
||||
if (dot != nullptr) {
|
||||
snprintf(patchPath, sizeof(patchPath), "%.*s#patch%s", (int)(dot - filePath), filePath, dot);
|
||||
} else {
|
||||
snprintf(patchPath, sizeof(patchPath), "%s#patch", filePath);
|
||||
}
|
||||
|
||||
struct PatchContext {
|
||||
Config* config;
|
||||
char string[CONFIG_FILE_MAX_LINE_LENGTH];
|
||||
} patchCtx = { config };
|
||||
|
||||
xfileOpenEachReverse(patchPath, "rb", [](XFile* file, void* ctx) {
|
||||
auto* pc = static_cast<PatchContext*>(ctx);
|
||||
while (fileReadString(pc->string, sizeof(pc->string), file) != nullptr) {
|
||||
configParseLine(pc->config, pc->string);
|
||||
} }, &patchCtx);
|
||||
} else {
|
||||
FILE* stream = compat_fopen(filePath, "rt");
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ void contentConfigInit()
|
||||
}
|
||||
|
||||
configRead(&gContentConfig, kConfigPath, true);
|
||||
// Patch config allows to override only certain fields, without replacing the whole file.
|
||||
// TODO: remove this after config patching by mods is implemented inside configRead
|
||||
configRead(&gContentConfig, kConfigPatchPath, true);
|
||||
}
|
||||
|
||||
void contentConfigExit()
|
||||
|
||||
+55
-1
@@ -216,6 +216,60 @@ void blitBufferToBufferTrans(unsigned char* src, int width, int height, int srcP
|
||||
transSrcCopy(dest, destPitch, src, srcPitch, width, height);
|
||||
}
|
||||
|
||||
void blitBuffer2D(ConstBuffer2D src, Buffer2D dst, int dstX, int dstY)
|
||||
{
|
||||
blitBuffer2D(src, 0, 0, src.width, src.height, dst, dstX, dstY);
|
||||
}
|
||||
|
||||
void blitBuffer2D(ConstBuffer2D src, int srcX, int srcY, int width, int height,
|
||||
Buffer2D dst, int dstX, int dstY)
|
||||
{
|
||||
// Clip source region to src bounds.
|
||||
if (srcX < 0) {
|
||||
width += srcX;
|
||||
dstX -= srcX;
|
||||
srcX = 0;
|
||||
}
|
||||
if (srcY < 0) {
|
||||
height += srcY;
|
||||
dstY -= srcY;
|
||||
srcY = 0;
|
||||
}
|
||||
if (srcX + width > src.width) {
|
||||
width = src.width - srcX;
|
||||
}
|
||||
if (srcY + height > src.height) {
|
||||
height = src.height - srcY;
|
||||
}
|
||||
|
||||
// Clip destination region to dst bounds.
|
||||
if (dstX < 0) {
|
||||
srcX -= dstX;
|
||||
width += dstX;
|
||||
dstX = 0;
|
||||
}
|
||||
if (dstY < 0) {
|
||||
srcY -= dstY;
|
||||
height += dstY;
|
||||
dstY = 0;
|
||||
}
|
||||
if (dstX + width > dst.width) {
|
||||
width = dst.width - dstX;
|
||||
}
|
||||
if (dstY + height > dst.height) {
|
||||
height = dst.height - dstY;
|
||||
}
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
srcCopy(
|
||||
dst.data + dstY * dst.width + dstX, dst.width,
|
||||
src.data + srcY * src.width + srcX, src.width,
|
||||
width, height);
|
||||
}
|
||||
|
||||
// 0x4D387C
|
||||
void bufferFill(unsigned char* buf, int width, int height, int pitch, int value)
|
||||
{
|
||||
@@ -321,7 +375,7 @@ void bufferOutline(unsigned char* buf, int width, int height, int pitch, int col
|
||||
}
|
||||
|
||||
// 0x4E0DB0
|
||||
void srcCopy(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height)
|
||||
void srcCopy(unsigned char* dest, int destPitch, const unsigned char* src, int srcPitch, int width, int height)
|
||||
{
|
||||
for (int y = 0; y < height; y++) {
|
||||
memcpy(dest, src, width);
|
||||
|
||||
+40
-1
@@ -1,8 +1,47 @@
|
||||
#ifndef DRAW_H
|
||||
#define DRAW_H
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace fallout {
|
||||
|
||||
template <typename T>
|
||||
struct Buffer2DBase {
|
||||
T* data = nullptr;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
Buffer2DBase() = default;
|
||||
Buffer2DBase(T* data, int width, int height)
|
||||
: data(data)
|
||||
, width(width)
|
||||
, height(height)
|
||||
{
|
||||
}
|
||||
|
||||
// Implicit conversion Buffer2D -> ConstBuffer2D.
|
||||
template <typename U, typename = std::enable_if_t<std::is_same_v<const U, T>>>
|
||||
Buffer2DBase(const Buffer2DBase<U>& other)
|
||||
: data(other.data)
|
||||
, width(other.width)
|
||||
, height(other.height)
|
||||
{
|
||||
}
|
||||
|
||||
explicit operator bool() const { return data != nullptr; }
|
||||
};
|
||||
|
||||
using Buffer2D = Buffer2DBase<unsigned char>;
|
||||
using ConstBuffer2D = Buffer2DBase<const unsigned char>;
|
||||
|
||||
// Copies src into dst at (dstX, dstY). Clips to dst bounds; no-op if fully outside.
|
||||
void blitBuffer2D(ConstBuffer2D src, Buffer2D dst, int dstX = 0, int dstY = 0);
|
||||
|
||||
// Copies a region of src (starting at srcX, srcY with given width/height) into
|
||||
// dst at (dstX, dstY). Clips to both src and dst bounds.
|
||||
void blitBuffer2D(ConstBuffer2D src, int srcX, int srcY, int width, int height,
|
||||
Buffer2D dst, int dstX = 0, int dstY = 0);
|
||||
|
||||
void bufferDrawLine(unsigned char* buf, int pitch, int left, int top, int right, int bottom, int color);
|
||||
void bufferDrawRect(unsigned char* buf, int pitch, int left, int top, int right, int bottom, int color);
|
||||
void bufferDrawRectShadowed(unsigned char* buf, int pitch, int left, int top, int right, int bottom, int ltColor, int rbColor);
|
||||
@@ -16,7 +55,7 @@ void _buf_texture(unsigned char* buf, int width, int height, int pitch, void* te
|
||||
void _lighten_buf(unsigned char* buf, int width, int height, int pitch);
|
||||
void _swap_color_buf(unsigned char* buf, int width, int height, int pitch, int color1, int color2);
|
||||
void bufferOutline(unsigned char* buf, int width, int height, int pitch, int color);
|
||||
void srcCopy(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height);
|
||||
void srcCopy(unsigned char* dest, int destPitch, const unsigned char* src, int srcPitch, int width, int height);
|
||||
void transSrcCopy(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
@@ -26,8 +26,8 @@ namespace {
|
||||
|
||||
static bool gameConfigHasKey(Config* config, const char* section, const char* key);
|
||||
static bool gameConfigNeedsF2ResMigration(Config* gameConfig);
|
||||
static bool gameConfigMigrateStringKey(Config* legacyConfig, Config* gameConfig, const F2ResMigrationEntry& entry);
|
||||
static bool gameConfigMigrateScaleKey(Config* legacyConfig, Config* gameConfig);
|
||||
static bool gameConfigMigrateStringKey(Config* legacyConfig, Config* gameConfig, Config* targetConfig, const F2ResMigrationEntry& entry);
|
||||
static bool gameConfigMigrateScaleKey(Config* legacyConfig, Config* gameConfig, Config* targetConfig);
|
||||
|
||||
static constexpr F2ResMigrationEntry kF2ResMigrationEntries[] = {
|
||||
{ "MAIN", "SCR_WIDTH", GAME_CONFIG_SCREEN_KEY, GAME_CONFIG_RESOLUTION_X_KEY },
|
||||
@@ -54,9 +54,9 @@ namespace {
|
||||
return !gameConfigHasKey(gameConfig, GAME_CONFIG_SCREEN_KEY, GAME_CONFIG_RESOLUTION_X_KEY);
|
||||
}
|
||||
|
||||
static bool gameConfigMigrateStringKey(Config* legacyConfig, Config* gameConfig, const F2ResMigrationEntry& entry)
|
||||
static bool gameConfigMigrateStringKey(Config* legacyConfig, Config* gameConfig, Config* targetConfig, const F2ResMigrationEntry& entry)
|
||||
{
|
||||
assert(legacyConfig != nullptr && gameConfig != nullptr);
|
||||
assert(legacyConfig != nullptr && gameConfig != nullptr && targetConfig != nullptr);
|
||||
|
||||
if (gameConfigHasKey(gameConfig, entry.targetSection, entry.targetKey)) {
|
||||
return false;
|
||||
@@ -67,12 +67,12 @@ namespace {
|
||||
return false;
|
||||
}
|
||||
|
||||
return configSetString(gameConfig, entry.targetSection, entry.targetKey, value);
|
||||
return configSetString(targetConfig, entry.targetSection, entry.targetKey, value);
|
||||
}
|
||||
|
||||
static bool gameConfigMigrateScaleKey(Config* legacyConfig, Config* gameConfig)
|
||||
static bool gameConfigMigrateScaleKey(Config* legacyConfig, Config* gameConfig, Config* targetConfig)
|
||||
{
|
||||
assert(legacyConfig != nullptr && gameConfig != nullptr);
|
||||
assert(legacyConfig != nullptr && gameConfig != nullptr && targetConfig != nullptr);
|
||||
|
||||
if (gameConfigHasKey(gameConfig, GAME_CONFIG_SCREEN_KEY, GAME_CONFIG_SCALE_KEY)) {
|
||||
return false;
|
||||
@@ -83,7 +83,7 @@ namespace {
|
||||
return false;
|
||||
}
|
||||
|
||||
return configSetInt(gameConfig, GAME_CONFIG_SCREEN_KEY, GAME_CONFIG_SCALE_KEY, value + 1);
|
||||
return configSetInt(targetConfig, GAME_CONFIG_SCREEN_KEY, GAME_CONFIG_SCALE_KEY, value + 1);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -97,7 +97,15 @@ bool gameConfigMigrateFromF2Res(const char* gameConfigFilePath, Config* gameConf
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!gameConfigNeedsF2ResMigration(gameConfig)) {
|
||||
Config fileConfig;
|
||||
if (!configInit(&fileConfig)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
configRead(&fileConfig, gameConfigFilePath, false);
|
||||
|
||||
if (!gameConfigNeedsF2ResMigration(&fileConfig)) {
|
||||
configFree(&fileConfig);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -109,23 +117,25 @@ bool gameConfigMigrateFromF2Res(const char* gameConfigFilePath, Config* gameConf
|
||||
|
||||
Config legacyConfig;
|
||||
if (!configInit(&legacyConfig)) {
|
||||
configFree(&fileConfig);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool migrated = false;
|
||||
if (configRead(&legacyConfig, f2ResFilePath, false)) {
|
||||
for (const auto& entry : kF2ResMigrationEntries) {
|
||||
if (gameConfigMigrateStringKey(&legacyConfig, gameConfig, entry)) {
|
||||
if (gameConfigMigrateStringKey(&legacyConfig, &fileConfig, gameConfig, entry)) {
|
||||
migrated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (gameConfigMigrateScaleKey(&legacyConfig, gameConfig)) {
|
||||
if (gameConfigMigrateScaleKey(&legacyConfig, &fileConfig, gameConfig)) {
|
||||
migrated = true;
|
||||
}
|
||||
}
|
||||
|
||||
configFree(&legacyConfig);
|
||||
configFree(&fileConfig);
|
||||
return migrated;
|
||||
}
|
||||
|
||||
|
||||
+97
-26
@@ -128,6 +128,8 @@ static bool indicatorBarAdd(int indicator);
|
||||
static void customInterfaceBarInit();
|
||||
static void customInterfaceBarExit();
|
||||
|
||||
static void extendedApBarInitToWindow();
|
||||
|
||||
static void sidePanelsInit();
|
||||
static void sidePanelsExit();
|
||||
static void sidePanelsHide();
|
||||
@@ -190,9 +192,6 @@ static int gEndTurnButton = -1;
|
||||
// 0x518FBC
|
||||
static int gEndCombatButton = -1;
|
||||
|
||||
// 0x518FD4
|
||||
static Rect gInterfaceBarActionPointsBarRect;
|
||||
|
||||
// 0x518FE8
|
||||
static IndicatorDescription gIndicatorDescriptions[INDICATOR_COUNT] = {
|
||||
{ 102, true, nullptr }, // ADDICT
|
||||
@@ -249,14 +248,34 @@ static unsigned char _itemButtonUp[INTERFACE_ITEM_ACTION_BUTTON_WIDTH * INTERFAC
|
||||
// 0x59D3F4
|
||||
static unsigned char* gInterfaceWindowBuffer;
|
||||
|
||||
// A slice of main interface background containing 10 shadowed action point
|
||||
// Rectangle within Interface Bar window covering the Action Points bar.
|
||||
// 0x518FD4
|
||||
static Rect apBarRect;
|
||||
|
||||
// Width and height of AP bulbs.
|
||||
constexpr int kApBarBulbSize = 5;
|
||||
|
||||
// Horizontal margin between AP bulbs.
|
||||
constexpr int kApBarBulbMargin = 4;
|
||||
|
||||
constexpr int kApBarMaxBulbs = 16;
|
||||
constexpr int kApBarMaxWidth = (kApBarBulbSize + kApBarBulbMargin) * kApBarMaxBulbs;
|
||||
|
||||
// A slice of main interface background containing up to 16 shadowed action point
|
||||
// dots. In combat mode individual colored dots are rendered on top of this
|
||||
// background.
|
||||
//
|
||||
// This buffer is initialized once and does not change throughout the game.
|
||||
//
|
||||
// 0x59D40C
|
||||
static unsigned char gInterfaceActionPointsBarBackground[90 * 5];
|
||||
static unsigned char apBarBackgroundData[kApBarMaxWidth * kApBarBulbSize];
|
||||
|
||||
static int apBarMaxAP;
|
||||
static int apBarWidth;
|
||||
|
||||
// AP bar offset relative to the content area of the Interface Bar window.
|
||||
static int apBarXOffset;
|
||||
constexpr int kApBarYOffset = 14;
|
||||
|
||||
static FrmImage _inventoryButtonNormalFrmImage;
|
||||
static FrmImage _inventoryButtonPressedFrmImage;
|
||||
@@ -287,15 +306,26 @@ static FrmImage _greenLightFrmImage;
|
||||
static FrmImage _yellowLightFrmImage;
|
||||
static FrmImage _redLightFrmImage;
|
||||
|
||||
// X offset of interface bar content (used when widescreen version of iface bar is used).
|
||||
// TODO: this seems like a bad solution, maybe better to separate message box and other content into two separate windows and avoid this offset?
|
||||
int gInterfaceBarContentOffset = 0;
|
||||
int gInterfaceBarWidth = 800; // will fall back to 640 if screen width is too narrow or asset is absent
|
||||
bool gInterfaceBarIsCustom = false;
|
||||
static Art* gCustomInterfaceBarBackground = nullptr;
|
||||
|
||||
bool gInterfaceSidePanelsExtendFromScreenEdge = false;
|
||||
static int gInterfaceSidePanelsLeadingWindow = -1;
|
||||
static int gInterfaceSidePanelsTrailingWindow = -1;
|
||||
|
||||
static Buffer2D interfaceWindowBuf2D()
|
||||
{
|
||||
return { gInterfaceWindowBuffer, gInterfaceBarWidth, INTERFACE_BAR_HEIGHT };
|
||||
}
|
||||
|
||||
static Buffer2D apBarBackgroundBuf2D()
|
||||
{
|
||||
return { apBarBackgroundData, apBarWidth, kApBarBulbSize };
|
||||
}
|
||||
|
||||
// intface_init
|
||||
// 0x45D880
|
||||
int interfaceInit()
|
||||
@@ -308,7 +338,6 @@ int interfaceInit()
|
||||
|
||||
customInterfaceBarInit();
|
||||
|
||||
gInterfaceBarActionPointsBarRect = { 316 + gInterfaceBarContentOffset, 14, 406 + gInterfaceBarContentOffset, 19 };
|
||||
gInterfaceBarEndButtonsRect = { 580 + gInterfaceBarContentOffset, 38, 637 + gInterfaceBarContentOffset, 96 };
|
||||
gInterfaceBarMainActionRect = { 267 + gInterfaceBarContentOffset, 26, 455 + gInterfaceBarContentOffset, 93 };
|
||||
|
||||
@@ -329,6 +358,7 @@ int interfaceInit()
|
||||
return intface_fatal_error(-1);
|
||||
}
|
||||
|
||||
// Blit interface bar FRM into static window buffer.
|
||||
if (gInterfaceBarIsCustom) {
|
||||
blitBufferToBuffer(customInterfaceBarGetBackgroundImageData(), gInterfaceBarWidth, INTERFACE_BAR_HEIGHT - 1, gInterfaceBarWidth, gInterfaceWindowBuffer, gInterfaceBarWidth);
|
||||
} else {
|
||||
@@ -342,6 +372,8 @@ int interfaceInit()
|
||||
backgroundFrmImage.unlock();
|
||||
}
|
||||
|
||||
extendedApBarInitToWindow();
|
||||
|
||||
fid = buildFid(OBJ_TYPE_INTERFACE, 47, 0, 0, 0);
|
||||
if (!_inventoryButtonNormalFrmImage.lock(fid)) {
|
||||
// NOTE: Uninline.
|
||||
@@ -560,8 +592,6 @@ int interfaceInit()
|
||||
return intface_fatal_error(-1);
|
||||
}
|
||||
|
||||
blitBufferToBuffer(gInterfaceWindowBuffer + gInterfaceBarWidth * 14 + 316 + gInterfaceBarContentOffset, 90, 5, gInterfaceBarWidth, gInterfaceActionPointsBarBackground, 90);
|
||||
|
||||
if (indicatorBarInit() == -1) {
|
||||
// NOTE: Uninline.
|
||||
return intface_fatal_error(-1);
|
||||
@@ -970,49 +1000,49 @@ void interfaceRenderArmorClass(bool animate)
|
||||
// 0x45EE0C
|
||||
void interfaceRenderActionPoints(int actionPointsLeft, int bonusActionPoints)
|
||||
{
|
||||
unsigned char* frmData;
|
||||
ConstBuffer2D bulbFrmBuf {};
|
||||
|
||||
if (gInterfaceBarWindow == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
blitBufferToBuffer(gInterfaceActionPointsBarBackground, 90, 5, 90, gInterfaceWindowBuffer + 14 * gInterfaceBarWidth + gInterfaceBarContentOffset + 316, gInterfaceBarWidth);
|
||||
blitBuffer2D(apBarBackgroundBuf2D(), interfaceWindowBuf2D(), gInterfaceBarContentOffset + apBarXOffset, kApBarYOffset);
|
||||
|
||||
if (actionPointsLeft == -1) {
|
||||
frmData = _redLightFrmImage.getData();
|
||||
actionPointsLeft = 10;
|
||||
bulbFrmBuf = _redLightFrmImage.getBuffer();
|
||||
actionPointsLeft = apBarMaxAP;
|
||||
bonusActionPoints = 0;
|
||||
} else {
|
||||
frmData = _greenLightFrmImage.getData();
|
||||
bulbFrmBuf = _greenLightFrmImage.getBuffer();
|
||||
|
||||
if (actionPointsLeft < 0) {
|
||||
actionPointsLeft = 0;
|
||||
}
|
||||
|
||||
if (actionPointsLeft > 10) {
|
||||
actionPointsLeft = 10;
|
||||
if (actionPointsLeft > apBarMaxAP) {
|
||||
actionPointsLeft = apBarMaxAP;
|
||||
}
|
||||
|
||||
if (bonusActionPoints >= 0) {
|
||||
if (actionPointsLeft + bonusActionPoints > 10) {
|
||||
bonusActionPoints = 10 - actionPointsLeft;
|
||||
if (actionPointsLeft + bonusActionPoints > apBarMaxAP) {
|
||||
bonusActionPoints = apBarMaxAP - actionPointsLeft;
|
||||
}
|
||||
} else {
|
||||
bonusActionPoints = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int index;
|
||||
for (index = 0; index < actionPointsLeft; index++) {
|
||||
blitBufferToBuffer(frmData, 5, 5, 5, gInterfaceWindowBuffer + 14 * gInterfaceBarWidth + 316 + index * 9 + gInterfaceBarContentOffset, gInterfaceBarWidth);
|
||||
}
|
||||
|
||||
for (; index < (actionPointsLeft + bonusActionPoints); index++) {
|
||||
blitBufferToBuffer(_yellowLightFrmImage.getData(), 5, 5, 5, gInterfaceWindowBuffer + 14 * gInterfaceBarWidth + 316 + gInterfaceBarContentOffset + index * 9, gInterfaceBarWidth);
|
||||
int numBulbs = actionPointsLeft + bonusActionPoints;
|
||||
for (int index = 0; index < numBulbs; index++) {
|
||||
constexpr int bulbXOffset = kApBarBulbSize + kApBarBulbMargin;
|
||||
auto frmBuf = index < actionPointsLeft
|
||||
? bulbFrmBuf
|
||||
: _yellowLightFrmImage.getBuffer();
|
||||
blitBuffer2D(frmBuf, 0, 0, kApBarBulbSize, kApBarBulbSize, interfaceWindowBuf2D(), gInterfaceBarContentOffset + apBarXOffset + index * bulbXOffset, kApBarYOffset);
|
||||
}
|
||||
|
||||
if (!gInterfaceBarInitialized) {
|
||||
windowRefreshRect(gInterfaceBarWindow, &gInterfaceBarActionPointsBarRect);
|
||||
windowRefreshRect(gInterfaceBarWindow, &apBarRect);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2496,6 +2526,47 @@ static void customInterfaceBarExit()
|
||||
}
|
||||
}
|
||||
|
||||
// Inits AP bar offsets based on custom AP bar setting and blits the correct AP graphic into the window buffer. Must be called after main panel FRM art is blitted.
|
||||
static void extendedApBarInitToWindow()
|
||||
{
|
||||
constexpr int apBarXOffsetOriginal = 316;
|
||||
constexpr int apBarWidthOriginal = 90;
|
||||
constexpr int apBarMaxAPOriginal = 10;
|
||||
|
||||
static_assert(apBarWidthOriginal <= kApBarMaxWidth);
|
||||
static_assert(apBarMaxAPOriginal <= kApBarMaxBulbs);
|
||||
|
||||
if (settings.ui.extend_ap_bar) {
|
||||
apBarMaxAP = kApBarMaxBulbs;
|
||||
apBarWidth = kApBarMaxWidth;
|
||||
// Shift X offset left according to the extra AP bar width.
|
||||
apBarXOffset = apBarXOffsetOriginal - (kApBarMaxWidth - apBarWidthOriginal) / 2;
|
||||
} else {
|
||||
apBarMaxAP = apBarMaxAPOriginal;
|
||||
apBarWidth = apBarWidthOriginal;
|
||||
apBarXOffset = apBarXOffsetOriginal;
|
||||
}
|
||||
apBarRect = { gInterfaceBarContentOffset + apBarXOffset, kApBarYOffset, gInterfaceBarContentOffset + apBarXOffset + apBarWidth - 1, kApBarYOffset + kApBarBulbSize - 1 };
|
||||
|
||||
Buffer2D ifaceBarBuf = interfaceWindowBuf2D();
|
||||
|
||||
// Blit extended AP bar art into static window buffer, overwriting pixels from the interface bar FRM.
|
||||
if (settings.ui.extend_ap_bar) {
|
||||
if (ArtPtr apBarArt { artLoad("art\\intrface\\iface_apbar_e.frm") }) {
|
||||
if (auto apBarFrmBuf = artGetFrameBuffer(apBarArt.get(), 0, 0)) {
|
||||
int apBarBgXOffset = apBarXOffset - 23;
|
||||
constexpr int apBarBgYOffset = kApBarYOffset - 4;
|
||||
|
||||
blitBuffer2D(apBarFrmBuf, ifaceBarBuf, gInterfaceBarContentOffset + apBarBgXOffset, apBarBgYOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Blit the thin bar of pixels covering the bulbs (that change color) into static apBarBackgroundData buffer.
|
||||
Buffer2D abBarBgBuf = apBarBackgroundBuf2D();
|
||||
blitBuffer2D(ifaceBarBuf, gInterfaceBarContentOffset + apBarXOffset, kApBarYOffset, abBarBgBuf.width, abBarBgBuf.height, abBarBgBuf);
|
||||
}
|
||||
|
||||
unsigned char* customInterfaceBarGetBackgroundImageData()
|
||||
{
|
||||
if (!gInterfaceBarIsCustom) {
|
||||
|
||||
@@ -34,7 +34,6 @@ extern bool gInterfaceBarMode;
|
||||
extern int gInterfaceBarWidth;
|
||||
extern bool gInterfaceBarIsCustom;
|
||||
extern int gInterfaceBarContentOffset;
|
||||
extern bool gInterfaceSidePanelsExtendFromScreenEdge;
|
||||
|
||||
int interfaceInit();
|
||||
void interfaceReset();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "memory_defs.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
|
||||
+51
-1
@@ -1,7 +1,8 @@
|
||||
#ifndef MEMORY_H
|
||||
#define MEMORY_H
|
||||
|
||||
#include "memory_defs.h"
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
namespace fallout {
|
||||
|
||||
@@ -11,6 +12,55 @@ void* internal_realloc(void* ptr, size_t size);
|
||||
void internal_free(void* ptr);
|
||||
void mem_check();
|
||||
|
||||
// Owning smart pointer for objects allocated with internal_malloc.
|
||||
template <typename T>
|
||||
class InternalPtr {
|
||||
public:
|
||||
static_assert(std::is_trivially_destructible_v<T>);
|
||||
|
||||
InternalPtr() = default;
|
||||
explicit InternalPtr(T* ptr)
|
||||
: _ptr(ptr)
|
||||
{
|
||||
}
|
||||
~InternalPtr() { reset(); }
|
||||
|
||||
InternalPtr(const InternalPtr&) = delete;
|
||||
InternalPtr& operator=(const InternalPtr&) = delete;
|
||||
|
||||
InternalPtr(InternalPtr&& other) noexcept
|
||||
: _ptr(other._ptr)
|
||||
{
|
||||
other._ptr = nullptr;
|
||||
}
|
||||
|
||||
InternalPtr& operator=(InternalPtr&& other) noexcept
|
||||
{
|
||||
if (this != &other) {
|
||||
reset();
|
||||
_ptr = other._ptr;
|
||||
other._ptr = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
if (_ptr != nullptr) {
|
||||
internal_free(_ptr);
|
||||
_ptr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
T* get() const { return _ptr; }
|
||||
T* operator->() const { return _ptr; }
|
||||
T& operator*() const { return *_ptr; }
|
||||
explicit operator bool() const { return _ptr != nullptr; }
|
||||
|
||||
private:
|
||||
T* _ptr = nullptr;
|
||||
};
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
#endif /* MEMORY_H */
|
||||
|
||||
+1
-1
@@ -556,7 +556,7 @@ static void* ioRead(int size)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mve_read_func(io_handle, buf, size) < 1) {
|
||||
if (!mve_read_func(io_handle, buf, size)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user