Compare commits

..
Author SHA1 Message Date
Mike Klaas 3a9433c0f1 Bump versions for v1.4.0 release 2026-04-01 20:42:11 -07:00
13 changed files with 111 additions and 343 deletions
+2 -2
View File
@@ -373,8 +373,8 @@ if(APPLE)
endif()
set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.alexbatalov.fallout2-ce")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "1.3.0")
set(MACOSX_BUNDLE_BUNDLE_VERSION "1.3.0")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "1.4.0")
set(MACOSX_BUNDLE_BUNDLE_VERSION "1.4.0")
endif()
if((NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten"))
+3 -3
View File
@@ -61,7 +61,7 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
| CalcAPCost | `HOOK_CALCAPCOST` | đźš« | - |
| DeathAnim1 | `HOOK_DEATHANIM1` | đźš« | Use DEATHANIM2 instead |
| DeathAnim2 | `HOOK_DEATHANIM2` | âś… | - |
| CombatDamage | `HOOK_COMBATDAMAGE` | âś… | - |
| CombatDamage | `HOOK_COMBATDAMAGE` | âś… | CE passes the raw `Attack*` as the final mixed argument, matching sfall's shape. |
| OnDeath | `HOOK_ONDEATH` | đźš« | - |
| FindTarget | `HOOK_FINDTARGET` | đźš« | (maybe) |
| UseObjOn | `HOOK_USEOBJON` | âś… | - |
@@ -71,12 +71,12 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
| MoveCost | `HOOK_MOVECOST` | đźš« | - |
| ItemDamage | `HOOK_ITEMDAMAGE` | đźš« | - |
| AmmoCost | `HOOK_AMMOCOST` | đźš« | Et tu |
| KeyPress | `HOOK_KEYPRESS` | âś… | Third hook arg is currently `0`; CE doesn't use VK codes. |
| KeyPress | `HOOK_KEYPRESS` | âś… | Third hook arg is currently `0`; CE notes that sfall used VK codes there. |
| MouseClick | `HOOK_MOUSECLICK` | đźš« | - |
| UseSkill | `HOOK_USESKILL` | đźš« | - |
| Steal | `HOOK_STEAL` | đźš« | Et tu |
| WithinPerception | `HOOK_WITHINPERCEPTION` | đźš« | Et tu |
| InventoryMove | `HOOK_INVENTORYMOVE` | âś… | - |
| InventoryMove | `HOOK_INVENTORYMOVE` | đźš« | Et tu |
| InvenWield | `HOOK_INVENWIELD` | đźš« | - |
| AdjustFID | `HOOK_ADJUSTFID` | đźš« | - |
| CombatTurn | `HOOK_COMBATTURN` | đźš« | - |
+2 -2
View File
@@ -9,8 +9,8 @@ android {
applicationId 'com.alexbatalov.fallout2ce'
minSdk 21
targetSdk 32
versionCode 4
versionName '1.3.0'
versionCode 5
versionName '1.4.0'
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=c++_static'
+2 -2
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.alexbatalov.fallout2ce"
android:versionCode="1"
android:versionName="1.0.0"
android:versionCode="5"
android:versionName="1.4.0"
android:installLocation="auto">
<!-- OpenGL ES 2.0 -->
@@ -13,23 +13,18 @@ import java.io.OutputStream;
public class FileUtils {
static boolean copyRecursively(ContentResolver contentResolver, DocumentFile src, File dest) {
if (dest == null || (!dest.exists() && !dest.mkdirs())) {
return false;
}
final DocumentFile[] documentFiles = src.listFiles();
for (final DocumentFile documentFile : documentFiles) {
final String name = documentFile.getName();
if (name == null || name.isEmpty()) {
return false;
}
if (documentFile.isFile()) {
if (!copyFile(contentResolver, documentFile, new File(dest, name))) {
if (!copyFile(contentResolver, documentFile, new File(dest, documentFile.getName()))) {
return false;
}
} else if (documentFile.isDirectory()) {
final File subdirectory = new File(dest, name);
final File subdirectory = new File(dest, documentFile.getName());
if (!subdirectory.exists()) {
subdirectory.mkdir();
}
if (!copyRecursively(contentResolver, documentFile, subdirectory)) {
return false;
}
@@ -39,22 +34,18 @@ public class FileUtils {
}
private static boolean copyFile(ContentResolver contentResolver, DocumentFile src, File dest) {
final File parent = dest.getParentFile();
if (parent != null && !parent.exists() && !parent.mkdirs()) {
return false;
}
try (final InputStream inputStream = contentResolver.openInputStream(src.getUri());
final OutputStream outputStream = new FileOutputStream(dest)) {
if (inputStream == null) {
return false;
}
try {
final InputStream inputStream = contentResolver.openInputStream(src.getUri());
final OutputStream outputStream = new FileOutputStream(dest);
final byte[] buffer = new byte[16384];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
inputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
return false;
@@ -2,12 +2,10 @@ package com.alexbatalov.fallout2ce;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.widget.Toast;
import androidx.documentfile.provider.DocumentFile;
@@ -19,18 +17,18 @@ public class ImportActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
launchImportPicker();
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, IMPORT_REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if (requestCode == IMPORT_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
final Uri treeUri = resultData != null ? resultData.getData() : null;
final Uri treeUri = resultData.getData();
if (treeUri != null) {
grantImportPermissions(treeUri, resultData);
final DocumentFile treeDocument = DocumentFile.fromTreeUri(this, treeUri);
if (treeDocument != null && treeDocument.isDirectory()) {
if (treeDocument != null) {
copyFiles(treeDocument);
return;
}
@@ -44,57 +42,18 @@ public class ImportActivity extends Activity {
}
private void copyFiles(DocumentFile treeDocument) {
final ProgressDialog dialog = createProgressDialog();
ProgressDialog dialog = createProgressDialog();
dialog.show();
new Thread(() -> {
final ContentResolver contentResolver = getContentResolver();
final File externalFilesDir = getExternalFilesDir(null);
final boolean success = externalFilesDir != null
&& FileUtils.copyRecursively(contentResolver, treeDocument, externalFilesDir);
ContentResolver contentResolver = getContentResolver();
File externalFilesDir = getExternalFilesDir(null);
FileUtils.copyRecursively(contentResolver, treeDocument, externalFilesDir);
runOnUiThread(() -> {
dialog.dismiss();
if (success) {
startMainActivity();
} else {
Toast.makeText(this, R.string.import_failed, Toast.LENGTH_LONG).show();
}
finish();
});
}).start();
}
private void launchImportPicker() {
final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
try {
startActivityForResult(intent, IMPORT_REQUEST_CODE);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, R.string.import_picker_unavailable, Toast.LENGTH_LONG).show();
startMainActivity();
dialog.dismiss();
finish();
}
}
private void grantImportPermissions(Uri treeUri, Intent resultData) {
int flags = Intent.FLAG_GRANT_READ_URI_PERMISSION;
if (resultData != null) {
flags = resultData.getFlags()
& (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
}
try {
getContentResolver().takePersistableUriPermission(treeUri, flags);
} catch (SecurityException ignored) {
// Some providers do not offer persistable grants. One-shot access is enough for import.
}
}).start();
}
private void startMainActivity() {
@@ -2,6 +2,4 @@
<string name="app_name">Fallout 2</string>
<string name="loading_dialog_title">PLEASE STAND BY</string>
<string name="loading_dialog_message">Copying files…</string>
<string name="import_failed">Failed to import game files. Please pick a valid Fallout 2 folder and try again.</string>
<string name="import_picker_unavailable">No compatible folder picker is available on this device.</string>
</resources>
-57
View File
@@ -1,57 +0,0 @@
#include "sfall.h"
#include "dik.h"
#include "lib.arrays.h"
variable inventorymove_blocking := false;
procedure inventorymove_handler begin
variable
args := get_sfall_args,
action := args[0],
item := args[1],
arg2 := args[2],
action_name := "unknown";
if (action == 0) then action_name := "main_backpack";
else if (action == 1) then action_name := "left_hand";
else if (action == 2) then action_name := "right_hand";
else if (action == 3) then action_name := "armor_slot";
else if (action == 4) then action_name := "weapon_reload";
else if (action == 5) then action_name := "container";
else if (action == 6) then action_name := "ground";
else if (action == 7) then action_name := "pickup";
else if (action == 8) then action_name := "character_portrait";
display_msg(string_format2("inventorymove %s args=%s", action_name, debug_array_str(args)));
if (item) then
display_msg(string_format2("inventorymove item=%s pid=%d", obj_name(item), obj_pid(item)));
if (arg2) then
display_msg(string_format2("inventorymove arg2=%s pid=%d", obj_name(arg2), obj_pid(arg2)));
display_msg(string_format1("inventorymove blocking=%d", inventorymove_blocking));
if (inventorymove_blocking) then begin
display_msg("inventorymove blocked");
set_sfall_return(0);
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_X) then return;
inventorymove_blocking := not inventorymove_blocking;
display_msg(string_format1("inventorymove blocking %d", inventorymove_blocking));
end
procedure start begin
if (not game_loaded) then return;
display_msg("inventorymove manual test ready: press X to toggle blocking");
register_hook_proc(HOOK_KEYPRESS, keypress_handler);
register_hook_proc(HOOK_INVENTORYMOVE, inventorymove_handler);
end
+25 -85
View File
@@ -247,12 +247,6 @@ typedef enum InventoryMoveResult {
INVENTORY_MOVE_RESULT_SUCCESS,
} InventoryMoveResult;
typedef enum InventoryAmmoMoveResult {
INVENTORY_AMMO_MOVE_RESULT_FAILED = -1,
INVENTORY_AMMO_MOVE_RESULT_SUCCESS = 0,
INVENTORY_AMMO_MOVE_RESULT_BLOCKED = 1,
} InventoryAmmoMoveResult;
static int inventoryMessageListInit();
static int inventoryMessageListFree();
static bool _setup_inventory(int inventoryWindowType);
@@ -286,7 +280,7 @@ static void barterDisplayTables(int win, Object* leftTable, Object* rightTable,
static void _container_enter(int keyCode, int inventoryWindowType);
static void _container_exit(int keyCode, int inventoryWindowType);
static int _drop_into_container(Object* container, Object* item, int sourceIndex, Object** itemSlot, int quantity);
static InventoryAmmoMoveResult _drop_ammo_into_weapon(Object* weapon, Object* ammo, Object** ammoItemSlot, int quantity, int keyCode);
static int _drop_ammo_into_weapon(Object* weapon, Object* ammo, Object** ammoItemSlot, int quantity, int keyCode);
static void _draw_amount(int value, int inventoryWindowType);
static int inventoryQuantitySelect(int inventoryWindowType, Object* item, int maximum, int defaultValue = 1);
static int inventoryQuantityWindowInit(int inventoryWindowType, Object* item);
@@ -2446,18 +2440,14 @@ static void _inven_pickup(int buttonCode, int indexOffset)
itemIndex = 0;
}
} else {
if (_drop_ammo_into_weapon(targetItem, item, itemSlot, count, buttonCode) == INVENTORY_AMMO_MOVE_RESULT_SUCCESS) {
if (_drop_ammo_into_weapon(targetItem, item, itemSlot, count, buttonCode) == 0) {
itemIndex = 0;
}
}
}
}
if (immediate || itemIndex == -1) {
if (!scriptHooks_InventoryMove(HOOK_INVENTORYMOVE_MAIN_BACKPACK, item, nullptr)) {
goto inventory_move_done;
}
if (immediate || pickUpFromSlot) {
// TODO: Holy shit, needs refactoring.
*itemSlot = nullptr;
if (itemAdd(_inven_dude, item, 1)) {
@@ -2475,38 +2465,30 @@ static void _inven_pickup(int buttonCode, int indexOffset)
// default to first empty hand, or left hand if both are full
bool left = gInventoryLeftHandItem == nullptr || gInventoryRightHandItem != nullptr;
if (left) {
_switch_hand(item, &gInventoryLeftHandItem, itemSlot, itemIndex);
_switch_hand(item, &gInventoryLeftHandItem, itemSlot, buttonCode);
} else {
_switch_hand(item, &gInventoryRightHandItem, itemSlot, itemIndex);
_switch_hand(item, &gInventoryRightHandItem, itemSlot, buttonCode);
}
// drop in left hand slot
} else if (mouseHitTestInWindow(gInventoryWindow, INVENTORY_LEFT_HAND_SLOT_X, INVENTORY_LEFT_HAND_SLOT_Y, INVENTORY_LEFT_HAND_SLOT_MAX_X, INVENTORY_LEFT_HAND_SLOT_MAX_Y)) {
if (gInventoryLeftHandItem != nullptr && itemGetType(gInventoryLeftHandItem) == ITEM_TYPE_CONTAINER && gInventoryLeftHandItem != item) {
_drop_into_container(gInventoryLeftHandItem, item, itemIndex, itemSlot, count);
} else if (gInventoryLeftHandItem == nullptr) {
_switch_hand(item, &gInventoryLeftHandItem, itemSlot, itemIndex);
} else if (_drop_ammo_into_weapon(gInventoryLeftHandItem, item, itemSlot, count, buttonCode) == INVENTORY_AMMO_MOVE_RESULT_FAILED) {
_switch_hand(item, &gInventoryLeftHandItem, itemSlot, itemIndex);
} else if (gInventoryLeftHandItem == nullptr || _drop_ammo_into_weapon(gInventoryLeftHandItem, item, itemSlot, count, buttonCode)) {
_switch_hand(item, &gInventoryLeftHandItem, itemSlot, buttonCode);
}
// drop in right hand slot
} else if (mouseHitTestInWindow(gInventoryWindow, INVENTORY_RIGHT_HAND_SLOT_X, INVENTORY_RIGHT_HAND_SLOT_Y, INVENTORY_RIGHT_HAND_SLOT_MAX_X, INVENTORY_RIGHT_HAND_SLOT_MAX_Y)) {
if (gInventoryRightHandItem != nullptr && itemGetType(gInventoryRightHandItem) == ITEM_TYPE_CONTAINER && gInventoryRightHandItem != item) {
_drop_into_container(gInventoryRightHandItem, item, itemIndex, itemSlot, count);
} else if (gInventoryRightHandItem == nullptr) {
_switch_hand(item, &gInventoryRightHandItem, itemSlot, itemIndex);
} else if (_drop_ammo_into_weapon(gInventoryRightHandItem, item, itemSlot, count, buttonCode) == INVENTORY_AMMO_MOVE_RESULT_FAILED) {
} else if (gInventoryRightHandItem == nullptr || _drop_ammo_into_weapon(gInventoryRightHandItem, item, itemSlot, count, buttonCode)) {
_switch_hand(item, &gInventoryRightHandItem, itemSlot, itemIndex);
}
} else if ((immediate && itemGetType(item) == ITEM_TYPE_ARMOR) || mouseHitTestInWindow(gInventoryWindow, INVENTORY_ARMOR_SLOT_X, INVENTORY_ARMOR_SLOT_Y, INVENTORY_ARMOR_SLOT_MAX_X, INVENTORY_ARMOR_SLOT_MAX_Y)) {
if (itemGetType(item) == ITEM_TYPE_ARMOR) {
Object* currentArmor = gInventoryArmor;
if (!scriptHooks_InventoryMove(HOOK_INVENTORYMOVE_ARMOR_SLOT, item, currentArmor)) {
goto inventory_move_done;
}
int itemAddResult = 0;
if (itemIndex != -1) {
itemRemove(_inven_dude, item, 1);
@@ -2536,19 +2518,13 @@ static void _inven_pickup(int buttonCode, int indexOffset)
}
}
} else if (mouseHitTestInWindow(gInventoryWindow, INVENTORY_PC_BODY_VIEW_X, INVENTORY_PC_BODY_VIEW_Y, INVENTORY_PC_BODY_VIEW_MAX_X, INVENTORY_PC_BODY_VIEW_MAX_Y)) {
if (_curr_stack == 0) {
// Call the hook when dropping item on the PC portrait when not in a container. Return value is irrelevant.
if (!scriptHooks_InventoryMove(HOOK_INVENTORYMOVE_CHARACTER_PORTRAIT, item, nullptr)) {
goto inventory_move_done;
}
} else {
if (_curr_stack != 0) {
// If we are looking inside nested inventory (such as backpack item), we see this item in the PC Body View instead of the player.
// So we drop item into it.
_drop_into_container(_stack[_curr_stack - 1], item, itemIndex, itemSlot, count);
}
}
inventory_move_done:
_adjust_fid();
inventoryRenderSummary();
_display_inventory(indexOffset, -1, INVENTORY_WINDOW_TYPE_NORMAL);
@@ -2574,16 +2550,7 @@ static void _switch_hand(Object* sourceItem, Object** targetSlot, Object** sourc
if (itemGetType(*targetSlot) == ITEM_TYPE_WEAPON && itemGetType(sourceItem) == ITEM_TYPE_AMMO) {
return;
}
}
HookInventoryMoveType targetSlotType = targetSlot == &gInventoryLeftHandItem
? HOOK_INVENTORYMOVE_LEFT_HAND
: HOOK_INVENTORYMOVE_RIGHT_HAND;
if (!scriptHooks_InventoryMove(targetSlotType, sourceItem, *targetSlot)) {
return;
}
if (*targetSlot != nullptr) {
if (sourceSlot != nullptr && (sourceSlot != &gInventoryArmor || itemGetType(*targetSlot) == ITEM_TYPE_ARMOR)) {
if (sourceSlot == &gInventoryArmor) {
adjustCritterStatsOnArmorChange(_stack[0], gInventoryArmor, *targetSlot);
@@ -3966,14 +3933,8 @@ static void inventoryWindowOpenContextMenu(int keyCode, int inventoryWindowType)
int actionMenuItem = actionMenuItems[menuItemIndex];
switch (actionMenuItem) {
case GAME_MOUSE_ACTION_MENU_ITEM_DROP: {
bool inventoryMoveAlreadyChecked = false;
case GAME_MOUSE_ACTION_MENU_ITEM_DROP:
if (itemSlot != nullptr) {
if (!scriptHooks_InventoryMove(HOOK_INVENTORYMOVE_GROUND, item, nullptr)) {
break;
}
inventoryMoveAlreadyChecked = true;
if (itemSlot == &gInventoryArmor) {
adjustCritterStatsOnArmorChange(_stack[0], item, nullptr);
}
@@ -3991,20 +3952,14 @@ static void inventoryWindowOpenContextMenu(int keyCode, int inventoryWindowType)
if (quantity > 0) {
if (quantity == 1) {
if (inventoryMoveAlreadyChecked || scriptHooks_InventoryMove(HOOK_INVENTORYMOVE_GROUND, item, nullptr)) {
itemSetMoney(item, 1);
objectDrop(owner, item);
}
itemSetMoney(item, 1);
objectDrop(owner, item);
} else {
if (itemRemove(owner, item, quantity - 1) == 0) {
Object* item2;
if (_inven_from_button(keyCode, &item2, &itemSlot, &owner) != 0) {
if (scriptHooks_InventoryMove(HOOK_INVENTORYMOVE_GROUND, item2, nullptr)) {
itemSetMoney(item2, quantity);
objectDrop(owner, item2);
} else {
itemAdd(owner, item, quantity - 1);
}
itemSetMoney(item2, quantity);
objectDrop(owner, item2);
} else {
itemAdd(owner, item, quantity - 1);
}
@@ -4012,29 +3967,22 @@ static void inventoryWindowOpenContextMenu(int keyCode, int inventoryWindowType)
}
}
} else if (explosiveIsActiveExplosive(item->pid)) {
if (inventoryMoveAlreadyChecked || scriptHooks_InventoryMove(HOOK_INVENTORYMOVE_GROUND, item, nullptr)) {
_dropped_explosive = 1;
objectDrop(owner, item);
}
_dropped_explosive = 1;
objectDrop(owner, item);
} else {
if (quantity > 1) {
quantity = inventoryQuantitySelect(INVENTORY_WINDOW_TYPE_MOVE_ITEMS, item, quantity);
for (int index = 0; index < quantity; index++) {
if (_inven_from_button(keyCode, &item, &itemSlot, &owner) != 0) {
if (scriptHooks_InventoryMove(HOOK_INVENTORYMOVE_GROUND, item, nullptr)) {
objectDrop(owner, item);
}
objectDrop(owner, item);
}
}
} else {
if (inventoryMoveAlreadyChecked || scriptHooks_InventoryMove(HOOK_INVENTORYMOVE_GROUND, item, nullptr)) {
objectDrop(owner, item);
}
objectDrop(owner, item);
}
}
break;
}
case GAME_MOUSE_ACTION_MENU_ITEM_LOOK:
if (inventoryWindowType != INVENTORY_WINDOW_TYPE_NORMAL) {
objectExamineFunc(_stack[0], item, gInventoryPrintItemDescriptionHandler);
@@ -5539,10 +5487,6 @@ static void _container_exit(int keyCode, int inventoryWindowType)
// 0x476464
static int _drop_into_container(Object* container, Object* item, int sourceIndex, Object** itemSlot, int quantity)
{
if (!scriptHooks_InventoryMove(HOOK_INVENTORYMOVE_CONTAINER, item, container)) {
return -1;
}
int quantityToMove;
if (quantity > 1) {
quantityToMove = inventoryQuantitySelect(INVENTORY_WINDOW_TYPE_MOVE_ITEMS, item, quantity);
@@ -5581,22 +5525,18 @@ static int _drop_into_container(Object* container, Object* item, int sourceIndex
}
// 0x47650C
static InventoryAmmoMoveResult _drop_ammo_into_weapon(Object* weapon, Object* ammo, Object** ammoItemSlot, int quantity, int keyCode)
static int _drop_ammo_into_weapon(Object* weapon, Object* ammo, Object** ammoItemSlot, int quantity, int keyCode)
{
if (itemGetType(weapon) != ITEM_TYPE_WEAPON) {
return INVENTORY_AMMO_MOVE_RESULT_FAILED;
return -1;
}
if (itemGetType(ammo) != ITEM_TYPE_AMMO) {
return INVENTORY_AMMO_MOVE_RESULT_FAILED;
return -1;
}
if (!weaponCanBeReloadedWith(weapon, ammo)) {
return INVENTORY_AMMO_MOVE_RESULT_FAILED;
}
if (!scriptHooks_InventoryMove(HOOK_INVENTORYMOVE_WEAPON_RELOAD, ammo, weapon)) {
return INVENTORY_AMMO_MOVE_RESULT_BLOCKED;
return -1;
}
int quantityToMove;
@@ -5607,7 +5547,7 @@ static InventoryAmmoMoveResult _drop_ammo_into_weapon(Object* weapon, Object* am
}
if (quantityToMove == -1) {
return INVENTORY_AMMO_MOVE_RESULT_FAILED;
return -1;
}
Object* sourceItem = ammo;
@@ -5640,13 +5580,13 @@ static InventoryAmmoMoveResult _drop_ammo_into_weapon(Object* weapon, Object* am
}
if (!isReloaded) {
return INVENTORY_AMMO_MOVE_RESULT_FAILED;
return -1;
}
const char* sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_READY, weapon, HIT_MODE_RIGHT_WEAPON_PRIMARY, nullptr);
soundPlayFile(sfx);
return INVENTORY_AMMO_MOVE_RESULT_SUCCESS;
return 0;
}
// 0x47664C
-4
View File
@@ -570,10 +570,6 @@ int objectPickup(Object* critter, Object* item)
{
bool overriden = false;
if (critter == gDude && !scriptHooks_InventoryMove(HOOK_INVENTORYMOVE_PICKUP, item, nullptr)) {
return 0;
}
if (item->sid != -1) {
scriptSetObjects(item->sid, critter, item);
scriptExecProc(item->sid, SCRIPT_PROC_PICKUP);
-30
View File
@@ -164,36 +164,6 @@ void scriptHooks_GameModeChange(int exit, int previousGameMode)
ScriptHookCall(HOOK_GAMEMODECHANGE, 0, { exit, previousGameMode }).call();
}
/*
Runs before moving items between inventory slots in dude interface. You can override the action.
int arg0 - Target slot:
0 - main backpack
1 - left hand
2 - right hand
3 - armor slot
4 - weapon, when reloading it by dropping ammo
5 - container, like bag/backpack
6 - dropping on the ground
7 - picking up item
8 - dropping item on the character portrait
Item arg1 - Item being moved
Item arg2 - Item being replaced, weapon being reloaded, or container being filled (can be 0)
int ret0 - Override setting (-1 - use engine handler, any other value - prevent relocation)
*/
bool scriptHooks_InventoryMove(HookInventoryMoveType actionType, Object* item, Object* targetItem)
{
ScriptHookCall hook(HOOK_INVENTORYMOVE, 1, { actionType, item, targetItem });
hook.call();
if (hook.numReturnValues() <= 0) {
return true;
}
return hook.getReturnValueAt(0).asInt() == -1;
}
/*
Runs when Fallout is calculating the chances of an attack striking a target.
Runs after the hit chance is fully calculated normally, including applying the 95% cap.
-13
View File
@@ -154,18 +154,6 @@ typedef enum {
HOOK_COUNT,
} HookType;
typedef enum {
HOOK_INVENTORYMOVE_MAIN_BACKPACK = 0,
HOOK_INVENTORYMOVE_LEFT_HAND = 1,
HOOK_INVENTORYMOVE_RIGHT_HAND = 2,
HOOK_INVENTORYMOVE_ARMOR_SLOT = 3,
HOOK_INVENTORYMOVE_WEAPON_RELOAD = 4,
HOOK_INVENTORYMOVE_CONTAINER = 5,
HOOK_INVENTORYMOVE_GROUND = 6,
HOOK_INVENTORYMOVE_PICKUP = 7,
HOOK_INVENTORYMOVE_CHARACTER_PORTRAIT = 8,
} HookInventoryMoveType;
constexpr size_t HOOKS_MAX_ARGUMENTS = 16;
constexpr size_t HOOKS_MAX_RETURN_VALUES = 8;
@@ -244,7 +232,6 @@ void scriptHooksReset();
void scriptHooksExit();
void scriptHooks_GameModeChange(int exit, int previousGameMode);
bool scriptHooks_InventoryMove(HookInventoryMoveType actionType, Object* item, Object* targetItem);
int scriptHooks_ToHit(Object* attacker, Object* defender, int tile, int hitMode, int hitLocation, int hitChance, int hitChanceUncapped, bool useDistance);
void scriptHooks_DeathAnim(Object* attacker, Object* defender, Object* weapon, int damage, int* anim);
int scriptHooks_UseItem(Object* user, Object* objUsed);
+54 -70
View File
@@ -71,11 +71,6 @@ namespace fallout {
#define WM_WINDOW_DIAL_X (532)
#define WM_WINDOW_DIAL_Y (48)
#define WM_TOWN_LIST_X (501)
#define WM_TOWN_LIST_Y (135)
#define WM_TOWN_LIST_WIDTH (119)
#define WM_TOWN_LIST_HEIGHT (178)
#define WM_TOWN_LIST_SCROLL_UP_X (480)
#define WM_TOWN_LIST_SCROLL_UP_Y (137)
@@ -98,9 +93,6 @@ namespace fallout {
#define WM_TOWN_WORLD_SWITCH_X (519)
#define WM_TOWN_WORLD_SWITCH_Y (439)
#define WM_TOWN_LIST_VISIBLE_SLOT_COUNT (7)
#define WM_TOWN_LIST_SLOT_HEIGHT (27)
#define WM_TILE_WIDTH (350)
#define WM_TILE_HEIGHT (300)
@@ -826,7 +818,7 @@ static int wmRndOriginalCenterTile;
static Config* pConfigCfg;
// 0x672FD8
static int wmTownMapSubButtonIds[WM_TOWN_LIST_VISIBLE_SLOT_COUNT];
static int wmTownMapSubButtonIds[7];
// 0x672FF4
static Encounter* wmEncBaseTypeList;
@@ -3270,11 +3262,11 @@ static int wmWorldMapFunc(int a1)
// NOTE: Uninline.
wmInterfaceScroll(1, 0, nullptr);
} else if (keyCode == KEY_CTRL_ARROW_UP) {
wmInterfaceScrollTabsStart(-WM_TOWN_LIST_SLOT_HEIGHT);
wmInterfaceScrollTabsStart(-27);
} else if (keyCode == KEY_CTRL_ARROW_DOWN) {
wmInterfaceScrollTabsStart(WM_TOWN_LIST_SLOT_HEIGHT);
wmInterfaceScrollTabsStart(27);
} else if (keyCode >= KEY_CTRL_F1 && keyCode <= KEY_CTRL_F7) {
int quickDestinationIndex = wmGenData.tabsOffsetY / WM_TOWN_LIST_SLOT_HEIGHT + (keyCode - KEY_CTRL_F1);
int quickDestinationIndex = wmGenData.tabsOffsetY / 27 + (keyCode - KEY_CTRL_F1);
if (quickDestinationIndex < wmLabelCount) {
int areaIdx = wmLabelList[quickDestinationIndex];
CityInfo* city = &(wmAreaInfoList[areaIdx]);
@@ -3302,13 +3294,9 @@ static int wmWorldMapFunc(int a1)
if (mouseHitTestInWindow(wmBkWin, WM_VIEW_X, WM_VIEW_Y, WM_VIEW_WIDTH + WM_VIEW_X, WM_VIEW_HEIGHT + WM_VIEW_Y)) {
wmInterfaceScrollPixel(20, 20, wheelX, -wheelY, nullptr, true);
} else if (mouseHitTestInWindow(wmBkWin,
WM_TOWN_LIST_X,
WM_TOWN_LIST_Y,
WM_TOWN_LIST_X + WM_TOWN_LIST_WIDTH,
WM_TOWN_LIST_Y + WM_TOWN_LIST_HEIGHT)) {
} else if (mouseHitTestInWindow(wmBkWin, 501, 135, 501 + 119, 135 + 178)) {
if (wheelY != 0) {
wmInterfaceScrollTabsStart(wheelY > 0 ? WM_TOWN_LIST_SLOT_HEIGHT : -WM_TOWN_LIST_SLOT_HEIGHT);
wmInterfaceScrollTabsStart(wheelY > 0 ? 27 : -27);
}
}
}
@@ -4441,14 +4429,11 @@ static void wmPartyWalkingStep()
// 0x4C219C
static void wmInterfaceScrollTabsStart(int delta)
{
int tabsScrollMaxOffsetY = std::max(0,
wmGenData.tabsBackgroundFrmImage.getHeight() - (WM_TOWN_LIST_HEIGHT + 52));
// SFALL: Fix world map cities list scrolling bug that might leave buttons
// in the disabled state.
if (delta >= 0) {
if (wmGenData.tabsOffsetY < tabsScrollMaxOffsetY) {
wmGenData.oldTabsOffsetY = std::min(wmGenData.tabsOffsetY + delta, tabsScrollMaxOffsetY);
if (wmGenData.tabsOffsetY < wmGenData.tabsBackgroundFrmImage.getHeight() - 230) {
wmGenData.oldTabsOffsetY = std::min(wmGenData.tabsOffsetY + delta, wmGenData.tabsBackgroundFrmImage.getHeight() - 230);
wmGenData.tabsScrollingDelta = delta;
}
} else {
@@ -4462,7 +4447,7 @@ static void wmInterfaceScrollTabsStart(int delta)
return;
}
for (int index = 0; index < WM_TOWN_LIST_VISIBLE_SLOT_COUNT; index++) {
for (int index = 0; index < 7; index++) {
buttonDisable(wmTownMapSubButtonIds[index]);
}
@@ -4474,7 +4459,7 @@ static void wmInterfaceScrollTabsStop()
{
wmGenData.tabsScrollingDelta = 0;
for (int index = 0; index < WM_TOWN_LIST_VISIBLE_SLOT_COUNT; index++) {
for (int index = 0; index < 7; index++) {
buttonEnable(wmTownMapSubButtonIds[index]);
}
}
@@ -4686,10 +4671,10 @@ static int wmInterfaceInit()
buttonSetCallbacks(switchBtn, _gsound_red_butt_press, _gsound_red_butt_release);
}
for (int index = 0; index < WM_TOWN_LIST_VISIBLE_SLOT_COUNT; index++) {
for (int index = 0; index < 7; index++) {
wmTownMapSubButtonIds[index] = buttonCreate(wmBkWin,
508,
138 + WM_TOWN_LIST_SLOT_HEIGHT * index,
138 + 27 * index,
wmGenData.redButtonNormalFrmImage.getWidth(),
wmGenData.redButtonNormalFrmImage.getHeight(),
-1,
@@ -6092,7 +6077,7 @@ static int wmTownMapFunc(int* mapIdxPtr)
}
if (keyCode >= KEY_CTRL_F1 && keyCode <= KEY_CTRL_F7) {
int quickDestinationIndex = wmGenData.tabsOffsetY / WM_TOWN_LIST_SLOT_HEIGHT + keyCode - KEY_CTRL_F1;
int quickDestinationIndex = wmGenData.tabsOffsetY / 27 + keyCode - KEY_CTRL_F1;
if (quickDestinationIndex < wmLabelCount) {
int areaIdx = wmLabelList[quickDestinationIndex];
CityInfo* city = &(wmAreaInfoList[areaIdx]);
@@ -6115,9 +6100,9 @@ static int wmTownMapFunc(int* mapIdxPtr)
}
} else {
if (keyCode == KEY_CTRL_ARROW_UP) {
wmInterfaceScrollTabsStart(-WM_TOWN_LIST_SLOT_HEIGHT);
wmInterfaceScrollTabsStart(-27);
} else if (keyCode == KEY_CTRL_ARROW_DOWN) {
wmInterfaceScrollTabsStart(WM_TOWN_LIST_SLOT_HEIGHT);
wmInterfaceScrollTabsStart(27);
} else if (keyCode == 2069) {
if (wmTownMapRefresh() == -1) {
return -1;
@@ -6531,64 +6516,63 @@ static void wmInterfaceRefreshCarFuel()
// 0x4C52B0
static int wmRefreshTabs()
{
unsigned char* firstTabBottomDest;
unsigned char* firstTabDest;
int firstVisibleLabelIndex;
unsigned char* v30;
unsigned char* v0;
int v31;
CityInfo* city;
int firstTabVisibleHeight;
unsigned char* firstTabSrc;
unsigned char* firstTabClampedDest;
int lastLabelIndexToDraw;
unsigned char* nextTabDest;
int v10;
unsigned char* v11;
unsigned char* v12;
int v32;
unsigned char* v13;
FrmImage labelFrm;
// CE: Skip first empty tab (original code does this in the
// `wmInterfaceInit`).
unsigned char* src = wmGenData.tabsBackgroundFrmImage.getData() + wmGenData.tabsBackgroundFrmImage.getWidth() * WM_TOWN_LIST_SLOT_HEIGHT;
unsigned char* src = wmGenData.tabsBackgroundFrmImage.getData() + wmGenData.tabsBackgroundFrmImage.getWidth() * 27;
blitBufferToBufferTrans(src + wmGenData.tabsBackgroundFrmImage.getWidth() * wmGenData.tabsOffsetY + 9,
WM_TOWN_LIST_WIDTH,
WM_TOWN_LIST_HEIGHT,
119,
178,
wmGenData.tabsBackgroundFrmImage.getWidth(),
wmBkWinBuf + WM_WINDOW_WIDTH * WM_TOWN_LIST_Y + WM_TOWN_LIST_X,
wmBkWinBuf + WM_WINDOW_WIDTH * 135 + 501,
WM_WINDOW_WIDTH);
int tabsOffsetWithinSlot = wmGenData.tabsOffsetY % WM_TOWN_LIST_SLOT_HEIGHT;
firstTabBottomDest = wmBkWinBuf + WM_WINDOW_WIDTH * 138 + 530;
firstTabDest = firstTabBottomDest - WM_WINDOW_WIDTH * tabsOffsetWithinSlot;
firstVisibleLabelIndex = wmGenData.tabsOffsetY / WM_TOWN_LIST_SLOT_HEIGHT;
v30 = wmBkWinBuf + WM_WINDOW_WIDTH * 138 + 530;
v0 = wmBkWinBuf + WM_WINDOW_WIDTH * 138 + 530 - WM_WINDOW_WIDTH * (wmGenData.tabsOffsetY % 27);
v31 = wmGenData.tabsOffsetY / 27;
if (firstVisibleLabelIndex < wmLabelCount) {
city = &(wmAreaInfoList[wmLabelList[firstVisibleLabelIndex]]);
if (v31 < wmLabelCount) {
city = &(wmAreaInfoList[wmLabelList[v31]]);
if (city->labelFid != -1) {
if (!labelFrm.lock(city->labelFid)) {
return -1;
}
firstTabVisibleHeight = labelFrm.getHeight() - tabsOffsetWithinSlot;
firstTabSrc = labelFrm.getData() + labelFrm.getWidth() * tabsOffsetWithinSlot;
v10 = labelFrm.getHeight() - wmGenData.tabsOffsetY % 27;
v11 = labelFrm.getData() + labelFrm.getWidth() * (wmGenData.tabsOffsetY % 27);
firstTabClampedDest = firstTabDest;
if (firstTabDest < firstTabBottomDest - WM_WINDOW_WIDTH) {
firstTabClampedDest = firstTabBottomDest - WM_WINDOW_WIDTH;
v12 = v0;
if (v0 < v30 - WM_WINDOW_WIDTH) {
v12 = v30 - WM_WINDOW_WIDTH;
}
blitBufferToBuffer(firstTabSrc,
blitBufferToBuffer(v11,
labelFrm.getWidth(),
firstTabVisibleHeight,
v10,
labelFrm.getWidth(),
firstTabClampedDest,
v12,
WM_WINDOW_WIDTH);
labelFrm.unlock();
}
}
nextTabDest = firstTabDest + WM_WINDOW_WIDTH * WM_TOWN_LIST_SLOT_HEIGHT;
lastLabelIndexToDraw = firstVisibleLabelIndex + (WM_TOWN_LIST_VISIBLE_SLOT_COUNT - 1);
v13 = v0 + WM_WINDOW_WIDTH * 27;
v32 = v31 + 6;
for (int labelIndex = firstVisibleLabelIndex + 1; labelIndex < lastLabelIndexToDraw; labelIndex++) {
if (labelIndex < wmLabelCount) {
city = &(wmAreaInfoList[wmLabelList[labelIndex]]);
for (int v14 = v31 + 1; v14 < v32; v14++) {
if (v14 < wmLabelCount) {
city = &(wmAreaInfoList[wmLabelList[v14]]);
if (city->labelFid != -1) {
if (!labelFrm.lock(city->labelFid)) {
return -1;
@@ -6598,17 +6582,17 @@ static int wmRefreshTabs()
labelFrm.getWidth(),
labelFrm.getHeight(),
labelFrm.getWidth(),
nextTabDest,
v13,
WM_WINDOW_WIDTH);
labelFrm.unlock();
}
}
nextTabDest += WM_WINDOW_WIDTH * WM_TOWN_LIST_SLOT_HEIGHT;
v13 += WM_WINDOW_WIDTH * 27;
}
if (lastLabelIndexToDraw < wmLabelCount) {
city = &(wmAreaInfoList[wmLabelList[lastLabelIndexToDraw]]);
if (v31 + 6 < wmLabelCount) {
city = &(wmAreaInfoList[wmLabelList[v31 + 6]]);
if (city->labelFid != -1) {
if (!labelFrm.lock(city->labelFid)) {
return -1;
@@ -6618,7 +6602,7 @@ static int wmRefreshTabs()
labelFrm.getWidth(),
labelFrm.getHeight() - 5,
labelFrm.getWidth(),
nextTabDest,
v13,
WM_WINDOW_WIDTH);
labelFrm.unlock();
@@ -6626,10 +6610,10 @@ static int wmRefreshTabs()
}
blitBufferToBufferTrans(wmGenData.tabsBorderFrmImage.getData(),
WM_TOWN_LIST_WIDTH,
WM_TOWN_LIST_HEIGHT,
WM_TOWN_LIST_WIDTH,
wmBkWinBuf + WM_WINDOW_WIDTH * WM_TOWN_LIST_Y + WM_TOWN_LIST_X,
119,
178,
119,
wmBkWinBuf + WM_WINDOW_WIDTH * 135 + 501,
WM_WINDOW_WIDTH);
return 0;