mirror of
https://github.com/fallout2-ce/fallout2-ce.git
synced 2026-07-27 16:47:11 -07:00
Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e3e94ffd9 | ||
|
|
25e006cffd | ||
|
|
3edd00d85f | ||
|
|
2bfa10411c | ||
|
|
a9eddc1705 | ||
|
|
26d7b632fc | ||
|
|
c6818f0849 | ||
|
|
84cac43da1 | ||
|
|
261b54c603 | ||
|
|
46b89abbd8 | ||
|
|
1ee64988bc | ||
|
|
c78ba885d7 | ||
|
|
8c98a15ef6 |
@@ -81,12 +81,14 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
|
||||
| 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 / 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 | âś… except get_ini_config, get_ini_config_db | `modified_ini` is intentionally omitted as deprecated. |
|
||||
| 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 | - |
|
||||
| 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 | - |
|
||||
| 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. |
|
||||
| Hero Appearange | set_dm/df_model<br>hero_select_win | 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. |
|
||||
|
||||
## Hooks
|
||||
|
||||
@@ -104,8 +106,8 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
|
||||
| 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` | đźš« | - |
|
||||
| ItemDamage | `HOOK_ITEMDAMAGE` | âś… | - |
|
||||
| MoveCost | `HOOK_MOVECOST` | âś… | - |
|
||||
| AmmoCost | `HOOK_AMMOCOST` | âś… | Requires `check_weapon_ammo_cost=1` if you want pre-attack ammo validation to respect per-shot/per-round overrides. |
|
||||
| KeyPress | `HOOK_KEYPRESS` | âś… | Third hook arg is currently `0`; CE doesn't use VK codes. |
|
||||
| MouseClick | `HOOK_MOUSECLICK` | âś… | - |
|
||||
|
||||
@@ -65,6 +65,12 @@ procedure ini_test_suite begin
|
||||
call assertEquals("TC7 get_ini_sections size", len_array(result_array), 3);
|
||||
call assertTrue("TC7 get_ini_sections", scan_array(result_array, "TestMisc") >= 0);
|
||||
|
||||
// get_ini_config
|
||||
result_array := get_ini_config_db("config\\config_from_db.cfg");
|
||||
call assertEquals("TC8 get_ini_config_db size", len_array(result_array), 2);
|
||||
call assertTrue("TC8 get_ini_config ValidSection", result_array.ValidSection != 0);
|
||||
call assertEquals("TC8 get_ini_config_db ValidValue", result_array.ValidSection.ValidKey, "ValidValue");
|
||||
|
||||
call report_test_results("ini");
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "sfall.h"
|
||||
#include "dik.h"
|
||||
#include "define_lite.h"
|
||||
#include "define_extra.h"
|
||||
#include "test_utils.h"
|
||||
|
||||
procedure run_unwield_slot_test(variable slot, variable slotName) begin
|
||||
variable equippedItem := critter_inven_obj2(dude_obj, slot);
|
||||
|
||||
if (equippedItem == 0) then begin
|
||||
display_msg("unwield_slot " + slotName + ": slot is empty");
|
||||
return;
|
||||
end
|
||||
|
||||
unwield_slot(dude_obj, slot);
|
||||
|
||||
call assertTrue("unwield_slot clears " + slotName + " slot", critter_inven_obj2(dude_obj, slot) == 0);
|
||||
call report_test_results("unwield_slot");
|
||||
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_J) then begin
|
||||
call run_unwield_slot_test(INVEN_TYPE_WORN, "armor");
|
||||
end else if (key == DIK_K) then begin
|
||||
call run_unwield_slot_test(INVEN_TYPE_RIGHT_HAND, "right");
|
||||
end else if (key == DIK_L) then begin
|
||||
call run_unwield_slot_test(INVEN_TYPE_LEFT_HAND, "left");
|
||||
end
|
||||
end
|
||||
|
||||
procedure start begin
|
||||
if (not game_loaded) then return;
|
||||
|
||||
call assertEquals("unwield_slot metarule exists", metarule_exist("unwield_slot"), 1);
|
||||
call assertEquals("critter_inven_obj2 metarule exists", metarule_exist("critter_inven_obj2"), 1);
|
||||
display_msg("unwield_slot manual test ready: open inventory or barter, then press J armor, K right, L left");
|
||||
register_hook_proc(HOOK_KEYPRESS, keypress_handler);
|
||||
end
|
||||
@@ -0,0 +1,98 @@
|
||||
#include "sfall.h"
|
||||
#include "dik.h"
|
||||
#include "lib.arrays.h"
|
||||
#include "../test_utils.h"
|
||||
|
||||
variable itemdamage_mode := 0;
|
||||
variable itemdamage_seen := 0;
|
||||
variable itemdamage_last_min := 0;
|
||||
variable itemdamage_last_max := 0;
|
||||
variable itemdamage_last_hit_mode := 0;
|
||||
variable itemdamage_last_is_melee_weapon := 0;
|
||||
variable itemdamage_last_weapon_pid := -1;
|
||||
variable itemdamage_last_weapon_name := "<none>";
|
||||
|
||||
procedure itemdamage_mode_name begin
|
||||
if (itemdamage_mode == 1) then return "fixed-42";
|
||||
if (itemdamage_mode == 2) then return "range-30-30";
|
||||
return "engine";
|
||||
end
|
||||
|
||||
procedure itemdamage_weapon_name(variable weapon) begin
|
||||
if (weapon == 0) then return "<unarmed>";
|
||||
return obj_name(weapon);
|
||||
end
|
||||
|
||||
procedure itemdamage_handler begin
|
||||
variable
|
||||
args := get_sfall_args,
|
||||
weapon := args[2];
|
||||
|
||||
call assertEquals("itemdamage arg count", len_array(args), 6);
|
||||
call assertTrue("itemdamage min <= max", args[0] <= args[1]);
|
||||
call assertTrue("itemdamage melee flag", args[5] == 0 or args[5] == 1);
|
||||
|
||||
itemdamage_seen := itemdamage_seen + 1;
|
||||
itemdamage_last_min := args[0];
|
||||
itemdamage_last_max := args[1];
|
||||
itemdamage_last_hit_mode := args[4];
|
||||
itemdamage_last_is_melee_weapon := args[5];
|
||||
itemdamage_last_weapon_name := itemdamage_weapon_name(weapon);
|
||||
|
||||
if (weapon == 0) then
|
||||
itemdamage_last_weapon_pid := -1;
|
||||
else
|
||||
itemdamage_last_weapon_pid := obj_pid(weapon);
|
||||
|
||||
display_msg(string_format6("itemdamage mode=%s min=%d max=%d weapon=%s hit=%d melee=%d",
|
||||
itemdamage_mode_name,
|
||||
args[0],
|
||||
args[1],
|
||||
itemdamage_last_weapon_name,
|
||||
itemdamage_last_hit_mode,
|
||||
itemdamage_last_is_melee_weapon));
|
||||
|
||||
if (itemdamage_mode == 1) then begin
|
||||
set_sfall_return(42);
|
||||
end else if (itemdamage_mode == 2) then begin
|
||||
set_sfall_return(30);
|
||||
set_sfall_return(30);
|
||||
end
|
||||
end
|
||||
|
||||
procedure keypress_handler begin
|
||||
variable
|
||||
pressed := get_sfall_arg_at(0),
|
||||
key := get_sfall_arg_at(1);
|
||||
|
||||
if (not pressed) then return;
|
||||
|
||||
if (key == DIK_J) then begin
|
||||
itemdamage_mode := itemdamage_mode + 1;
|
||||
if (itemdamage_mode > 2) then itemdamage_mode := 0;
|
||||
display_msg(string_format1("itemdamage mode -> %s", itemdamage_mode_name));
|
||||
end else if (key == DIK_K) then begin
|
||||
display_msg(string_format7("itemdamage summary mode=%s seen=%d last=%d-%d weapon=%s pid=%d melee=%d",
|
||||
itemdamage_mode_name,
|
||||
itemdamage_seen,
|
||||
itemdamage_last_min,
|
||||
itemdamage_last_max,
|
||||
itemdamage_last_weapon_name,
|
||||
itemdamage_last_weapon_pid,
|
||||
itemdamage_last_is_melee_weapon));
|
||||
call assertTrue("itemdamage observed", itemdamage_seen > 0);
|
||||
call report_test_results("itemdamage");
|
||||
end
|
||||
end
|
||||
|
||||
procedure start begin
|
||||
if (not game_loaded) then return;
|
||||
|
||||
display_msg("itemdamage manual test ready:");
|
||||
display_msg("press J to cycle engine/fixed-42/range-30-30");
|
||||
display_msg("attack with fists, melee weapons, and ranged weapons");
|
||||
display_msg("press K to report after HOOK_ITEMDAMAGE fires");
|
||||
|
||||
register_hook_proc(HOOK_KEYPRESS, keypress_handler);
|
||||
register_hook_proc(HOOK_ITEMDAMAGE, itemdamage_handler);
|
||||
end
|
||||
@@ -0,0 +1,70 @@
|
||||
#include "sfall.h"
|
||||
#include "dik.h"
|
||||
|
||||
variable movecost_mode := 0;
|
||||
|
||||
procedure movecost_mode_name(variable mode) begin
|
||||
variable name := "unknown";
|
||||
|
||||
if (mode == 0) then name := "engine";
|
||||
else if (mode == 1) then name := "plus_per_hex";
|
||||
else if (mode == 2) then name := "double";
|
||||
|
||||
return name;
|
||||
end
|
||||
|
||||
procedure cycle_movecost_mode begin
|
||||
if (movecost_mode == 0) then movecost_mode := 1;
|
||||
else if (movecost_mode == 1) then movecost_mode := 2;
|
||||
else movecost_mode := 0;
|
||||
|
||||
display_msg(string_format1("movecost mode=%s", movecost_mode_name(movecost_mode)));
|
||||
end
|
||||
|
||||
procedure movecost_handler begin
|
||||
variable
|
||||
args := get_sfall_args,
|
||||
critter := args[0],
|
||||
distance := args[1],
|
||||
defaultCost := args[2],
|
||||
modeName := movecost_mode_name(movecost_mode),
|
||||
newCost := defaultCost;
|
||||
|
||||
if (critter != dude_obj) then return;
|
||||
|
||||
display_msg(string_format3("movecost dist=%d default=%d mode=%s",
|
||||
distance,
|
||||
defaultCost,
|
||||
modeName));
|
||||
|
||||
if (movecost_mode == 1) then begin
|
||||
newCost := defaultCost + distance;
|
||||
end else if (movecost_mode == 2) then begin
|
||||
newCost := defaultCost * 2;
|
||||
end else begin
|
||||
return;
|
||||
end
|
||||
|
||||
set_sfall_return(newCost);
|
||||
end
|
||||
|
||||
procedure keypress_handler begin
|
||||
variable
|
||||
pressed := get_sfall_arg_at(0),
|
||||
key := get_sfall_arg_at(1);
|
||||
|
||||
if (not pressed) then return;
|
||||
if (key != DIK_M) then return;
|
||||
|
||||
call cycle_movecost_mode;
|
||||
end
|
||||
|
||||
procedure start begin
|
||||
if (not game_loaded) then return;
|
||||
|
||||
display_msg("movecost manual test ready: enter combat, hover a tile, then move");
|
||||
display_msg("press M to cycle hook override: engine -> plus_per_hex -> double");
|
||||
|
||||
register_hook_proc(HOOK_KEYPRESS, keypress_handler);
|
||||
register_hook_proc(HOOK_MOVECOST, movecost_handler);
|
||||
end
|
||||
@@ -0,0 +1,7 @@
|
||||
[ValidSection]
|
||||
ValidKey=ValidValue
|
||||
Key2=2
|
||||
|
||||
[Section2]
|
||||
Key1=AA
|
||||
Key2=BB
|
||||
@@ -0,0 +1,2 @@
|
||||
A test mod for sfall scripting features testing.
|
||||
Add it to your mods_order.txt
|
||||
@@ -0,0 +1 @@
|
||||
Put compiled scripts in here
|
||||
+30
-7
@@ -112,6 +112,8 @@ typedef enum EditorFolder {
|
||||
EDITOR_FOLDER_KILLS,
|
||||
} EditorFolder;
|
||||
|
||||
static void characterEditorMessageListReset();
|
||||
|
||||
enum {
|
||||
EDITOR_DERIVED_STAT_ARMOR_CLASS,
|
||||
EDITOR_DERIVED_STAT_ACTION_POINTS,
|
||||
@@ -587,6 +589,12 @@ static int gCharacterEditorOptionalTraitBtns[TRAIT_COUNT];
|
||||
// 0x5700E8 mesg
|
||||
static MessageListItem gCharacterEditorMessageListItem;
|
||||
|
||||
static void characterEditorMessageListReset()
|
||||
{
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_EDITOR, nullptr);
|
||||
messageListFree(&gCharacterEditorMessageList);
|
||||
}
|
||||
|
||||
// 0x5700F8 old_str1
|
||||
static char gCharacterEditorCardTitle[48];
|
||||
|
||||
@@ -1284,18 +1292,35 @@ static int characterEditorWindowInit()
|
||||
if (!messageListLoad(&gCharacterEditorMessageList, path)) {
|
||||
return -1;
|
||||
}
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_EDITOR, &gCharacterEditorMessageList);
|
||||
|
||||
fid = buildFid(OBJ_TYPE_INTERFACE, (gCharacterEditorIsCreationMode ? 169 : 177), 0, 0, 0);
|
||||
if (!_editorBackgroundFrmImage.lock(fid)) {
|
||||
messageListFree(&gCharacterEditorMessageList);
|
||||
characterEditorMessageListReset();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (karmaInit() == -1) {
|
||||
_editorBackgroundFrmImage.unlock();
|
||||
characterEditorMessageListReset();
|
||||
if (gCharacterEditorIsoWasEnabled) {
|
||||
isoEnable();
|
||||
}
|
||||
colorCycleEnable();
|
||||
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (genericReputationInit() == -1) {
|
||||
karmaFree();
|
||||
_editorBackgroundFrmImage.unlock();
|
||||
|
||||
characterEditorMessageListReset();
|
||||
if (gCharacterEditorIsoWasEnabled) {
|
||||
isoEnable();
|
||||
}
|
||||
colorCycleEnable();
|
||||
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1318,11 +1343,9 @@ static int characterEditorWindowInit()
|
||||
while (--i >= 0) {
|
||||
_editorFrmImages[i].unlock();
|
||||
}
|
||||
return -1;
|
||||
|
||||
_editorBackgroundFrmImage.unlock();
|
||||
|
||||
messageListFree(&gCharacterEditorMessageList);
|
||||
characterEditorMessageListReset();
|
||||
|
||||
if (gCharacterEditorIsoWasEnabled) {
|
||||
isoEnable();
|
||||
@@ -1360,7 +1383,7 @@ static int characterEditorWindowInit()
|
||||
|
||||
_editorBackgroundFrmImage.unlock();
|
||||
|
||||
messageListFree(&gCharacterEditorMessageList);
|
||||
characterEditorMessageListReset();
|
||||
if (gCharacterEditorIsoWasEnabled) {
|
||||
isoEnable();
|
||||
}
|
||||
@@ -1389,7 +1412,7 @@ static int characterEditorWindowInit()
|
||||
|
||||
_editorBackgroundFrmImage.unlock();
|
||||
|
||||
messageListFree(&gCharacterEditorMessageList);
|
||||
characterEditorMessageListReset();
|
||||
if (gCharacterEditorIsoWasEnabled) {
|
||||
isoEnable();
|
||||
}
|
||||
@@ -1869,7 +1892,7 @@ static void characterEditorWindowFree()
|
||||
// SFALL: Custom town reputation.
|
||||
customTownReputationFree();
|
||||
|
||||
messageListFree(&gCharacterEditorMessageList);
|
||||
characterEditorMessageListReset();
|
||||
|
||||
interfaceBarRefresh();
|
||||
|
||||
|
||||
+6
-3
@@ -1359,13 +1359,16 @@ int critterGetMovementPointCostAdjustedForCrippledLegs(Object* critter, int dist
|
||||
}
|
||||
|
||||
int flags = critter->data.critter.combat.results;
|
||||
int actionPoints = 0;
|
||||
if ((flags & DAM_CRIP_LEG_LEFT) != 0 && (flags & DAM_CRIP_LEG_RIGHT) != 0) {
|
||||
return 8 * distance;
|
||||
actionPoints = 8 * distance;
|
||||
} else if ((flags & DAM_CRIP_LEG_ANY) != 0) {
|
||||
return 4 * distance;
|
||||
actionPoints = 4 * distance;
|
||||
} else {
|
||||
return distance;
|
||||
actionPoints = distance;
|
||||
}
|
||||
|
||||
return scriptHooks_MoveCost(critter, distance, actionPoints);
|
||||
}
|
||||
|
||||
// 0x42E66C critterIsOverloaded
|
||||
|
||||
@@ -448,6 +448,7 @@ void gameReset()
|
||||
scriptHooksReset();
|
||||
sfallArraysReset();
|
||||
sfall_gl_scr_reset();
|
||||
sfall_ini_cache_clear();
|
||||
sfallOnGameReset();
|
||||
gGameLoaded = false;
|
||||
}
|
||||
|
||||
+12
-1
@@ -86,6 +86,8 @@ typedef enum GameDialogReviewWindowButtonFrm {
|
||||
GAME_DIALOG_REVIEW_WINDOW_BUTTON_FRM_COUNT,
|
||||
} GameDialogReviewWindowButtonFrm;
|
||||
|
||||
static void partyMemberCustomizationMessageListReset();
|
||||
|
||||
typedef enum GameDialogReaction {
|
||||
GAME_DIALOG_REACTION_GOOD = 49,
|
||||
GAME_DIALOG_REACTION_NEUTRAL = 50,
|
||||
@@ -583,6 +585,12 @@ static int gGameDialogReviewWindowOldFont;
|
||||
// 0x58F470 gdialog_buttons
|
||||
static int _gdialog_buttons[9];
|
||||
|
||||
static void partyMemberCustomizationMessageListReset()
|
||||
{
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_CUSTOM, nullptr);
|
||||
messageListFree(&gCustomMessageList);
|
||||
}
|
||||
|
||||
// 0x58F4C8 oldFont
|
||||
static int _oldFont;
|
||||
|
||||
@@ -3966,10 +3974,12 @@ int partyMemberCustomizationWindowInit()
|
||||
if (!messageListLoad(&gCustomMessageList, "game\\custom.msg")) {
|
||||
return -1;
|
||||
}
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_CUSTOM, &gCustomMessageList);
|
||||
|
||||
FrmImage backgroundFrmImage;
|
||||
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 391, 0, 0, 0);
|
||||
if (!backgroundFrmImage.lock(backgroundFid)) {
|
||||
partyMemberCustomizationMessageListReset();
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -4081,6 +4091,7 @@ int partyMemberCustomizationWindowInit()
|
||||
void partyMemberCustomizationWindowFree()
|
||||
{
|
||||
if (gGameDialogWindow == -1) {
|
||||
partyMemberCustomizationMessageListReset();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4118,7 +4129,7 @@ void partyMemberCustomizationWindowFree()
|
||||
windowDestroy(gGameDialogWindow);
|
||||
gGameDialogWindow = -1;
|
||||
|
||||
messageListFree(&gCustomMessageList);
|
||||
partyMemberCustomizationMessageListReset();
|
||||
}
|
||||
|
||||
// 0x449B3C
|
||||
|
||||
@@ -151,7 +151,7 @@ typedef enum OpRegAnimFunc {
|
||||
static void scriptPredefinedError(Program* program, const char* name, int error);
|
||||
static void scriptError(const char* format, ...);
|
||||
static int tileIsVisible(int tile);
|
||||
static int _correctFidForRemovedItem(Object* critter, Object* item, int flags);
|
||||
int correctFidForRemovedItem(Object* critter, Object* item, int flags);
|
||||
static void opGiveExpPoints(Program* program);
|
||||
static void opScrReturn(Program* program);
|
||||
static void opPlaySfx(Program* program);
|
||||
@@ -407,7 +407,7 @@ static int tileIsVisible(int tile)
|
||||
}
|
||||
|
||||
// 0x45409C correctFidForRemovedItem
|
||||
static int _correctFidForRemovedItem(Object* critter, Object* item, int flags)
|
||||
int correctFidForRemovedItem(Object* critter, Object* item, int flags)
|
||||
{
|
||||
InvenSlot invenSlot = InvenSlot::Armor;
|
||||
if ((flags & OBJECT_IN_RIGHT_HAND) != 0) {
|
||||
@@ -1683,7 +1683,7 @@ static void opRemoveObjectFromInventory(Program* program)
|
||||
tileWindowRefreshRect(&rect, item->elevation);
|
||||
|
||||
if (updateFlags) {
|
||||
_correctFidForRemovedItem(owner, item, flags);
|
||||
correctFidForRemovedItem(owner, item, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4641,7 +4641,7 @@ static void opMoveObjectInventoryToObject(Program* program)
|
||||
flags |= OBJECT_IN_RIGHT_HAND;
|
||||
}
|
||||
|
||||
_correctFidForRemovedItem(object1, oldWeapon, flags);
|
||||
correctFidForRemovedItem(object1, oldWeapon, flags);
|
||||
}
|
||||
|
||||
itemMoveAll(object1, object2);
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
#define INTERPRETER_EXTRA_H
|
||||
|
||||
#include "interpreter.h"
|
||||
#include "obj_types.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
enum class InvenSlot {
|
||||
enum class InvenSlot : int {
|
||||
Armor = 0, // INVEN_TYPE_WORN
|
||||
RightHand = 1, // INVEN_TYPE_RIGHT_HAND
|
||||
LeftHand = 2, // INVEN_TYPE_LEFT_HAND
|
||||
@@ -13,6 +14,8 @@ enum class InvenSlot {
|
||||
|
||||
constexpr int kInvenSlotInvCount = -2;
|
||||
|
||||
int correctFidForRemovedItem(Object* critter, Object* item, int flags);
|
||||
|
||||
void _intExtraClose_();
|
||||
void _initIntExtra();
|
||||
void intExtraUpdate();
|
||||
|
||||
+138
-3
@@ -937,6 +937,7 @@ static int inventoryMessageListInit()
|
||||
if (!messageListLoad(&gInventoryMessageList, path))
|
||||
return -1;
|
||||
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_INVENTORY, &gInventoryMessageList);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -944,6 +945,7 @@ static int inventoryMessageListInit()
|
||||
// 0x46E7A0
|
||||
static int inventoryMessageListFree()
|
||||
{
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_INVENTORY, nullptr);
|
||||
messageListFree(&gInventoryMessageList);
|
||||
return 0;
|
||||
}
|
||||
@@ -3643,7 +3645,6 @@ int inventoryUnequipFunc(Object* critter, int hand, bool animate)
|
||||
{
|
||||
int activeHand;
|
||||
Object* item;
|
||||
int fid;
|
||||
|
||||
if (critter == gDude) {
|
||||
activeHand = interfaceGetCurrentHand();
|
||||
@@ -3678,13 +3679,13 @@ int inventoryUnequipFunc(Object* critter, int hand, bool animate)
|
||||
|
||||
animationRegisterAnimate(critter, ANIM_PUT_AWAY, 0);
|
||||
|
||||
fid = buildFid(OBJ_TYPE_CRITTER, critter->fid & 0xFFF, 0, 0, critter->rotation + 1);
|
||||
int fid = buildFid(OBJ_TYPE_CRITTER, critter->fid & 0xFFF, 0, 0, critter->rotation + 1);
|
||||
animationRegisterSetFid(critter, fid, -1);
|
||||
|
||||
return reg_anim_end();
|
||||
}
|
||||
|
||||
fid = buildFid(OBJ_TYPE_CRITTER, critter->fid & 0xFFF, 0, 0, critter->rotation + 1);
|
||||
int fid = buildFid(OBJ_TYPE_CRITTER, critter->fid & 0xFFF, 0, 0, critter->rotation + 1);
|
||||
_dude_stand(critter, critter->rotation, fid);
|
||||
}
|
||||
|
||||
@@ -6344,4 +6345,138 @@ Object* inventoryGetTargetObject()
|
||||
return _target_stack[_target_curr_stack];
|
||||
}
|
||||
|
||||
int inventoryUnwieldSlot(Object* critter, InvenSlot slot)
|
||||
{
|
||||
if (critter == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool isDude = critter == gDude;
|
||||
bool uiModeActive = GameMode::isInGameMode(GameMode::kInventory)
|
||||
|| GameMode::isInGameMode(GameMode::kUseOn)
|
||||
|| GameMode::isInGameMode(GameMode::kLoot)
|
||||
|| GameMode::isInGameMode(GameMode::kBarter);
|
||||
bool update = false;
|
||||
|
||||
if (slot != InvenSlot::Armor && !uiModeActive) {
|
||||
int rc = inventoryUnequipFunc(critter, slot == InvenSlot::LeftHand ? HAND_LEFT : HAND_RIGHT, false);
|
||||
if (rc == 0) {
|
||||
update = isDude;
|
||||
}
|
||||
|
||||
if (update) {
|
||||
interfaceUpdateItems(false, INTERFACE_ITEM_ACTION_DEFAULT, INTERFACE_ITEM_ACTION_DEFAULT);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool forceAdd = false;
|
||||
Object* item = nullptr;
|
||||
Object** itemSlot = nullptr;
|
||||
if (slot != InvenSlot::Armor) {
|
||||
if (!isDude || critter != _inven_dude) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (slot == InvenSlot::RightHand) {
|
||||
item = gInventoryRightHandItem;
|
||||
itemSlot = &gInventoryRightHandItem;
|
||||
} else {
|
||||
item = gInventoryLeftHandItem;
|
||||
itemSlot = &gInventoryLeftHandItem;
|
||||
}
|
||||
|
||||
if (item != nullptr) {
|
||||
if (!scriptHooks_InvenWield(critter, item, slot, 0, 0, false)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int oldFlags = item->flags;
|
||||
bool clearBothHandSlots = gInventoryLeftHandItem == item && gInventoryRightHandItem == item;
|
||||
if (clearBothHandSlots) {
|
||||
gInventoryLeftHandItem = nullptr;
|
||||
gInventoryRightHandItem = nullptr;
|
||||
} else {
|
||||
*itemSlot = nullptr;
|
||||
}
|
||||
item->flags &= ~OBJECT_IN_ANY_HAND;
|
||||
|
||||
if (itemAdd(critter, item, 1) != 0) {
|
||||
// item will be in an invalid state, but this can only happen on malloc failure
|
||||
return -1;
|
||||
}
|
||||
|
||||
update = true;
|
||||
}
|
||||
} else {
|
||||
if (isDude && critter == _inven_dude) {
|
||||
item = gInventoryArmor;
|
||||
}
|
||||
|
||||
if (item == nullptr) {
|
||||
item = critterGetArmor(critter);
|
||||
} else {
|
||||
gInventoryArmor = nullptr;
|
||||
forceAdd = true;
|
||||
}
|
||||
|
||||
if (item != nullptr) {
|
||||
item->flags &= ~OBJECT_WORN;
|
||||
if (correctFidForRemovedItem(critter, item, OBJECT_WORN) != 0) {
|
||||
if (forceAdd) {
|
||||
gInventoryArmor = item;
|
||||
item->flags |= OBJECT_WORN;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (forceAdd && itemAdd(critter, item, 1) != 0) {
|
||||
// improper cleanup, but this can only happen on malloc failure
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (isDude) {
|
||||
interfaceRenderArmorClass(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!uiModeActive) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
InventoryWindowType inventoryWindowType = INVENTORY_WINDOW_TYPE_NORMAL;
|
||||
if (GameMode::isInGameMode(GameMode::kBarter)) {
|
||||
inventoryWindowType = INVENTORY_WINDOW_TYPE_TRADE;
|
||||
} else if (GameMode::isInGameMode(GameMode::kLoot)) {
|
||||
inventoryWindowType = INVENTORY_WINDOW_TYPE_LOOT;
|
||||
} else if (GameMode::isInGameMode(GameMode::kUseOn)) {
|
||||
inventoryWindowType = INVENTORY_WINDOW_TYPE_USE_ITEM_ON;
|
||||
}
|
||||
|
||||
if (inventoryWindowType == INVENTORY_WINDOW_TYPE_NORMAL) {
|
||||
inventoryRenderSummary();
|
||||
}
|
||||
|
||||
int targetFid = -1;
|
||||
if (inventoryWindowType == INVENTORY_WINDOW_TYPE_LOOT
|
||||
|| inventoryWindowType == INVENTORY_WINDOW_TYPE_TRADE) {
|
||||
targetFid = _target_stack[_target_curr_stack]->fid;
|
||||
_display_target_inventory(_target_stack_offset[_target_curr_stack], -1, _target_pud, inventoryWindowType);
|
||||
}
|
||||
|
||||
_adjust_fid();
|
||||
_display_inventory(_stack_offset[_curr_stack], -1, inventoryWindowType);
|
||||
_display_body(targetFid, inventoryWindowType);
|
||||
if (inventoryWindowType == INVENTORY_WINDOW_TYPE_TRADE) {
|
||||
barterDisplayTables(gInventoryBarterBackgroundWindow, gPlayerTableObj, gBartererTableObj, -1);
|
||||
}
|
||||
|
||||
if (update) {
|
||||
interfaceUpdateItems(false, INTERFACE_ITEM_ACTION_DEFAULT, INTERFACE_ITEM_ACTION_DEFAULT);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
namespace fallout {
|
||||
|
||||
enum class InvenSlot : int;
|
||||
|
||||
#define INVENTORY_SLOT_WIDTH 64
|
||||
#define INVENTORY_SLOT_HEIGHT 48
|
||||
|
||||
@@ -61,6 +63,7 @@ int inventoryOpenStealing(Object* thief, Object* target);
|
||||
void barterProcessUI(int win, Object* barterer, Object* playerTable, Object* bartererTable, int barterMod);
|
||||
int inventorySetTimer(Object* item);
|
||||
Object* inventoryGetTargetObject();
|
||||
int inventoryUnwieldSlot(Object* critter, InvenSlot slot);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
||||
+8
-3
@@ -320,8 +320,7 @@ int itemAttemptAdd(Object* owner, Object* itemToAdd, int quantity)
|
||||
return itemAdd(owner, itemToAdd, quantity);
|
||||
}
|
||||
|
||||
// item_add_force
|
||||
// 0x4772B8
|
||||
// 0x4772B8 item_add_force
|
||||
int itemAdd(Object* owner, Object* itemToAdd, int quantity)
|
||||
{
|
||||
if (quantity < 1) {
|
||||
@@ -1275,6 +1274,7 @@ int weaponGetDamage(Object* critter, int hitMode)
|
||||
int maxDamage = 0;
|
||||
int meleeDamage = 0;
|
||||
int bonusDamage = 0;
|
||||
bool isMeleeWeaponAttack = false;
|
||||
|
||||
// NOTE: Uninline.
|
||||
Object* weapon = critterGetWeaponForHitMode(critter, hitMode);
|
||||
@@ -1286,6 +1286,7 @@ int weaponGetDamage(Object* critter, int hitMode)
|
||||
int attackType = weaponGetAttackTypeForHitMode(weapon, hitMode);
|
||||
if (attackType == ATTACK_TYPE_MELEE || attackType == ATTACK_TYPE_UNARMED) {
|
||||
meleeDamage = critterGetStat(critter, STAT_MELEE_DAMAGE);
|
||||
isMeleeWeaponAttack = attackType == ATTACK_TYPE_MELEE;
|
||||
|
||||
// SFALL: Bonus HtH Damage fix.
|
||||
if (damageModGetBonusHthDamageFix()) {
|
||||
@@ -1311,7 +1312,11 @@ int weaponGetDamage(Object* critter, int hitMode)
|
||||
}
|
||||
}
|
||||
|
||||
return randomBetween(bonusDamage + minDamage, bonusDamage + meleeDamage + maxDamage);
|
||||
minDamage += bonusDamage;
|
||||
maxDamage += bonusDamage + meleeDamage;
|
||||
|
||||
scriptHooks_ItemDamage(weapon, critter, hitMode, isMeleeWeaponAttack, &minDamage, &maxDamage);
|
||||
return randomBetween(minDamage, maxDamage);
|
||||
}
|
||||
|
||||
// 0x478570
|
||||
|
||||
+18
-6
@@ -120,6 +120,8 @@ typedef enum LoadSaveScrollDirection {
|
||||
LOAD_SAVE_SCROLL_DIRECTION_DOWN,
|
||||
} LoadSaveScrollDirection;
|
||||
|
||||
static void loadSaveMessageListReset();
|
||||
|
||||
typedef struct LoadSaveSlotData {
|
||||
char signature[24];
|
||||
short versionMinor;
|
||||
@@ -330,6 +332,12 @@ static char _str1[COMPAT_MAX_PATH];
|
||||
// 0x6145FC str
|
||||
static char _str[COMPAT_MAX_PATH];
|
||||
|
||||
static void loadSaveMessageListReset()
|
||||
{
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_LSGAME, nullptr);
|
||||
messageListFree(&gLoadSaveMessageList);
|
||||
}
|
||||
|
||||
// 0x614700 lsgbuf
|
||||
static unsigned char* gLoadSaveWindowBuffer;
|
||||
|
||||
@@ -459,6 +467,7 @@ int lsgSaveGame(int mode)
|
||||
if (!messageListLoad(&gLoadSaveMessageList, path)) {
|
||||
return -1;
|
||||
}
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_LSGAME, &gLoadSaveMessageList);
|
||||
|
||||
_snapshotBuf = nullptr;
|
||||
int v6 = _QuickSnapShot();
|
||||
@@ -476,6 +485,7 @@ int lsgSaveGame(int mode)
|
||||
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
|
||||
|
||||
if (v6 != -1) {
|
||||
loadSaveMessageListReset();
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -491,7 +501,7 @@ int lsgSaveGame(int mode)
|
||||
};
|
||||
showDialogBox(_str0, body, 1, 169, 116, _colorTable[32328], nullptr, _colorTable[32328], DIALOG_BOX_LARGE);
|
||||
|
||||
messageListFree(&gLoadSaveMessageList);
|
||||
loadSaveMessageListReset();
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -1092,6 +1102,7 @@ int lsgLoadGame(int mode)
|
||||
if (!messageListLoad(&gLoadSaveMessageList, path)) {
|
||||
return -1;
|
||||
}
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_LSGAME, &gLoadSaveMessageList);
|
||||
|
||||
if (window != -1) {
|
||||
windowDestroy(window);
|
||||
@@ -1103,7 +1114,7 @@ int lsgLoadGame(int mode)
|
||||
strcpy(_str1, getmsg(&gLoadSaveMessageList, &messageListItem, 135));
|
||||
showDialogBox(_str0, body, 1, 169, 116, _colorTable[32328], nullptr, _colorTable[32328], DIALOG_BOX_LARGE);
|
||||
|
||||
messageListFree(&gLoadSaveMessageList);
|
||||
loadSaveMessageListReset();
|
||||
mapNewMap();
|
||||
_game_user_wants_to_quit = GAME_QUIT_REQUEST_MAIN_MENU;
|
||||
|
||||
@@ -1597,10 +1608,11 @@ static int lsgWindowInit(int windowType)
|
||||
if (!messageListLoad(&gLoadSaveMessageList, _str)) {
|
||||
return -1;
|
||||
}
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_LSGAME, &gLoadSaveMessageList);
|
||||
|
||||
_snapshot = (unsigned char*)internal_malloc(61632);
|
||||
if (_snapshot == nullptr) {
|
||||
messageListFree(&gLoadSaveMessageList);
|
||||
loadSaveMessageListReset();
|
||||
fontSetCurrent(gLoadSaveWindowOldFont);
|
||||
return -1;
|
||||
}
|
||||
@@ -1652,7 +1664,7 @@ static int lsgWindowInit(int windowType)
|
||||
_loadsaveFrmImages[index].unlock();
|
||||
}
|
||||
internal_free(_snapshot);
|
||||
messageListFree(&gLoadSaveMessageList);
|
||||
loadSaveMessageListReset();
|
||||
fontSetCurrent(gLoadSaveWindowOldFont);
|
||||
|
||||
if (windowType != LOAD_SAVE_WINDOW_TYPE_LOAD_GAME_FROM_MAIN_MENU) {
|
||||
@@ -1678,7 +1690,7 @@ static int lsgWindowInit(int windowType)
|
||||
if (gLoadSaveWindow == -1) {
|
||||
// FIXME: Leaking frms.
|
||||
internal_free(_snapshot);
|
||||
messageListFree(&gLoadSaveMessageList);
|
||||
loadSaveMessageListReset();
|
||||
fontSetCurrent(gLoadSaveWindowOldFont);
|
||||
|
||||
if (windowType != LOAD_SAVE_WINDOW_TYPE_LOAD_GAME_FROM_MAIN_MENU) {
|
||||
@@ -1816,7 +1828,7 @@ static int lsgWindowFree(int windowType)
|
||||
|
||||
windowDestroy(gLoadSaveWindow);
|
||||
fontSetCurrent(gLoadSaveWindowOldFont);
|
||||
messageListFree(&gLoadSaveMessageList);
|
||||
loadSaveMessageListReset();
|
||||
|
||||
for (int index = 0; index < LOAD_SAVE_FRM_COUNT; index++) {
|
||||
_loadsaveFrmImages[index].unlock();
|
||||
|
||||
+13
-13
@@ -53,7 +53,7 @@ static int mapLoad(File* stream);
|
||||
static int _map_age_dead_critters();
|
||||
static void _map_fix_critter_combat_data();
|
||||
static int _map_save_file(File* stream);
|
||||
int _map_save();
|
||||
int _map_save(bool isInGame);
|
||||
static void mapMakeMapsDirectory();
|
||||
static void isoWindowRefreshRect(Rect* rect);
|
||||
static void isoWindowRefreshRectGame(Rect* rect);
|
||||
@@ -310,7 +310,7 @@ void mapInit()
|
||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_MAP, &gMapMessageList);
|
||||
}
|
||||
|
||||
// 0x482084
|
||||
// 0x482084 map_enable_bk_processes
|
||||
void mapExit()
|
||||
{
|
||||
windowHide(gIsoWindow);
|
||||
@@ -338,7 +338,7 @@ void isoEnable()
|
||||
}
|
||||
}
|
||||
|
||||
// 0x482104
|
||||
// 0x482104 map_disable_bk_processes
|
||||
bool isoDisable()
|
||||
{
|
||||
if (!gIsoEnabled) {
|
||||
@@ -356,14 +356,13 @@ bool isoDisable()
|
||||
return true;
|
||||
}
|
||||
|
||||
// 0x482148
|
||||
// 0x482148 map_bk_processes_are_disabled
|
||||
bool isoIsDisabled()
|
||||
{
|
||||
return gIsoEnabled == false;
|
||||
}
|
||||
|
||||
// map_set_elevation
|
||||
// 0x482158
|
||||
// 0x482158 map_set_elevation
|
||||
int mapSetElevation(int elevation)
|
||||
{
|
||||
if (!elevationIsValid(elevation)) {
|
||||
@@ -727,10 +726,9 @@ const char* mapBuildDataSavePath(const char* relativePath)
|
||||
{
|
||||
static char path[COMPAT_MAX_PATH];
|
||||
|
||||
// Save root: the validated mapper dev_path when set, otherwise the master patches path.
|
||||
// Save root: the validated mapper dev_path when set (mapper only), otherwise the master patches path.
|
||||
const std::string& devPath = settings.mapper.dev_path;
|
||||
const char* root = !devPath.empty() ? devPath.c_str() : settings.system.master_patches_path.c_str();
|
||||
|
||||
const char* root = (settings.system.executableIsMapper() && !devPath.empty()) ? devPath.c_str() : settings.system.master_patches_path.c_str();
|
||||
// Join root + relativePath, tolerating a trailing separator on the root.
|
||||
size_t rootLen = strlen(root);
|
||||
bool rootHasSeparator = rootLen > 0 && (root[rootLen - 1] == '\\' || root[rootLen - 1] == '/');
|
||||
@@ -1377,7 +1375,7 @@ static void _map_fix_critter_combat_data()
|
||||
|
||||
// map_save
|
||||
// 0x483850
|
||||
int _map_save()
|
||||
int _map_save(bool isInGame)
|
||||
{
|
||||
int rc = -1;
|
||||
if (gMapHeader.name[0] != '\0') {
|
||||
@@ -1393,8 +1391,10 @@ int _map_save()
|
||||
if (rc == 0) {
|
||||
debugPrint("%s saved.", gMapHeader.name);
|
||||
|
||||
// Write the edge (.EDG) sidecar alongside the map.
|
||||
mapEdgeSave(gMapHeader.name);
|
||||
if (!isInGame) {
|
||||
// Write the edge (.EDG) alongside the map.
|
||||
mapEdgeSave(gMapHeader.name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debugPrint("\nError: map_save: map header corrupt!");
|
||||
@@ -1528,7 +1528,7 @@ int _map_save_in_game(bool isLeavingMap)
|
||||
|
||||
strcpy(name, gMapHeader.name);
|
||||
_strmfe(gMapHeader.name, name, "SAV");
|
||||
if (_map_save() == -1) {
|
||||
if (_map_save(true) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ int mapGetLoadedAreaId();
|
||||
int mapSetTransition(MapTransition* transition);
|
||||
int mapHandleTransition();
|
||||
int _map_save_in_game(bool isLeavingMap);
|
||||
int _map_save();
|
||||
int _map_save(bool isInGame = false);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user