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 | |
|---|---|---|---|
|
|
0b649c416c | ||
|
|
1a50bebff1 | ||
|
|
ab2a09610d | ||
|
|
cef24823f4 | ||
|
|
63d79e4bfd | ||
|
|
e42d8021c1 | ||
|
|
8254c758fe |
+79
-4
@@ -38,7 +38,9 @@ endif()
|
||||
# Git Info
|
||||
include("cmake/gitver.cmake")
|
||||
|
||||
target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
# Shared engine sources — used by both game and mapper targets.
|
||||
# Excludes main.cc/main.h (game-only entry logic).
|
||||
set(FALLOUT_ENGINE_SOURCES
|
||||
"src/actions.cc"
|
||||
"src/actions.h"
|
||||
"src/animation.cc"
|
||||
@@ -154,8 +156,6 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
"src/lips.h"
|
||||
"src/loadsave.cc"
|
||||
"src/loadsave.h"
|
||||
"src/main.cc"
|
||||
"src/main.h"
|
||||
"src/mainmenu.cc"
|
||||
"src/mainmenu.h"
|
||||
"src/map_defs.h"
|
||||
@@ -268,7 +268,7 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
"src/xfile.h"
|
||||
)
|
||||
|
||||
target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
set(FALLOUT_PLATFORM_SOURCES
|
||||
"src/audio_engine.cc"
|
||||
"src/audio_engine.h"
|
||||
"src/delay.cc"
|
||||
@@ -316,6 +316,14 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
"third_party/lodepng/lodepng.cpp"
|
||||
)
|
||||
|
||||
target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||
${FALLOUT_ENGINE_SOURCES}
|
||||
"src/main.cc"
|
||||
"src/main.h"
|
||||
)
|
||||
|
||||
target_sources(${EXECUTABLE_NAME} PUBLIC ${FALLOUT_PLATFORM_SOURCES})
|
||||
|
||||
target_include_directories(${EXECUTABLE_NAME} PUBLIC "third_party/lodepng")
|
||||
|
||||
set(FALLOUT_HAVE_STB_VORBIS OFF)
|
||||
@@ -561,6 +569,73 @@ if((NOT ANDROID) AND (NOT IOS) AND (NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten"))
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if((NOT ANDROID) AND (NOT IOS) AND (NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten"))
|
||||
if(WIN32)
|
||||
add_executable(mapper-ce WIN32)
|
||||
else()
|
||||
add_executable(mapper-ce)
|
||||
endif()
|
||||
|
||||
target_sources(mapper-ce PUBLIC
|
||||
${FALLOUT_ENGINE_SOURCES}
|
||||
${FALLOUT_PLATFORM_SOURCES}
|
||||
"src/mapper/map_func.cc"
|
||||
"src/mapper/map_func.h"
|
||||
"src/mapper/mapper.cc"
|
||||
"src/mapper/mapper.h"
|
||||
"src/mapper/mp_instance.cc"
|
||||
"src/mapper/mp_instance.h"
|
||||
"src/mapper/mp_proto.cc"
|
||||
"src/mapper/mp_proto.h"
|
||||
"src/mapper/mp_scrpt.cc"
|
||||
"src/mapper/mp_scrpt.h"
|
||||
"src/mapper/mp_targt.cc"
|
||||
"src/mapper/mp_targt.h"
|
||||
"src/mapper/mp_text.cc"
|
||||
"src/mapper/mp_text.h"
|
||||
)
|
||||
|
||||
target_compile_definitions(mapper-ce PUBLIC FALLOUT_MAPPER)
|
||||
|
||||
if(WIN32)
|
||||
target_compile_definitions(mapper-ce PUBLIC
|
||||
_CRT_SECURE_NO_WARNINGS
|
||||
_CRT_NONSTDC_NO_WARNINGS
|
||||
NOMINMAX
|
||||
WIN32_LEAN_AND_MEAN
|
||||
_STATIC_CPPLIB
|
||||
)
|
||||
target_link_libraries(mapper-ce
|
||||
winmm
|
||||
debug libcpmtd
|
||||
optimized libcpmt
|
||||
)
|
||||
target_sources(mapper-ce PUBLIC
|
||||
"os/windows/fallout2-ce.rc"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
target_link_libraries(mapper-ce "-framework CoreFoundation")
|
||||
set_target_properties(mapper-ce PROPERTIES
|
||||
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
|
||||
XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO"
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(mapper-ce PUBLIC "third_party/lodepng")
|
||||
target_include_directories(mapper-ce PRIVATE "src")
|
||||
target_include_directories(mapper-ce PRIVATE ${ZLIB_INCLUDE_DIRS})
|
||||
target_include_directories(mapper-ce PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
|
||||
target_link_libraries(mapper-ce
|
||||
fpattern::fpattern
|
||||
fpattern_windows::fpattern_windows
|
||||
${ZLIB_LIBRARIES}
|
||||
${SDL2_LIBRARIES}
|
||||
)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
if(IOS)
|
||||
install(TARGETS ${EXECUTABLE_NAME} DESTINATION "Payload")
|
||||
|
||||
@@ -98,11 +98,11 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
|
||||
| KeyPress | `HOOK_KEYPRESS` | ✅ | Third hook arg is currently `0`; CE doesn't use VK codes. |
|
||||
| MouseClick | `HOOK_MOUSECLICK` | ✅ | - |
|
||||
| UseSkill | `HOOK_USESKILL` | 🚫 | - |
|
||||
| Steal | `HOOK_STEAL` | 🚫 | Et tu |
|
||||
| Steal | `HOOK_STEAL` | ✅ | - |
|
||||
| WithinPerception | `HOOK_WITHINPERCEPTION` | ✅ | - |
|
||||
| InventoryMove | `HOOK_INVENTORYMOVE` | ✅ | - |
|
||||
| InvenWield | `HOOK_INVENWIELD` | 🚫 | - |
|
||||
| AdjustFID | `HOOK_ADJUSTFID` | 🚫 | - |
|
||||
| InvenWield | `HOOK_INVENWIELD` | ✅ | - |
|
||||
| AdjustFID | `HOOK_ADJUSTFID` | ✅ | Second hook arg currently matches the first because CE has no internal FID modifiers like Hero Appearance. |
|
||||
| CombatTurn | `HOOK_COMBATTURN` | ✅ | - |
|
||||
| StdProcedure | `HOOK_STDPROCEDURE` | ✅ | - |
|
||||
| StdProcedureEnd | `HOOK_STDPROCEDURE_END` | ✅ | - |
|
||||
@@ -124,5 +124,5 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
|
||||
| AdjustRads | `HOOK_ADJUSTRADS` | 🚫 | (maybe) |
|
||||
| RollCheck | `HOOK_ROLLCHECK` | 🚫 | - |
|
||||
| BestWeapon | `HOOK_BESTWEAPON` | 🚫 | - |
|
||||
| CanUseWeapon | `HOOK_CANUSEWEAPON` | 🚫 | - |
|
||||
| CanUseWeapon | `HOOK_CANUSEWEAPON` | ✅ | - |
|
||||
| BuildSfxWeapon | `HOOK_BUILDSFXWEAPON` | 🚫 | - |
|
||||
|
||||
@@ -22,6 +22,10 @@ save_text_maps=0
|
||||
show_pid_numbers=0
|
||||
sort_script_list=0
|
||||
use_art_not_protos=0
|
||||
; Allows to load a certain map right away after loading the mapper.
|
||||
map=denbus2
|
||||
; Allows to disable grid item picker and use a simpler list-based picker.
|
||||
use_grid_item_picker=1
|
||||
|
||||
[preferences]
|
||||
brightness=1.000000
|
||||
@@ -94,6 +98,8 @@ auto_quick_save=3
|
||||
display_bonus_damage=1
|
||||
;Set to 1 to get notification of karma changes in the notification window
|
||||
display_karma_changes=0
|
||||
;Set to 1 to use the hi-res dialog border/background at resolutions above 640x480. Requires art\intrface\HR_ALLTLK.FRM (for example from f2_res.dat)
|
||||
enable_dialog_border=1
|
||||
;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)
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
#include "sfall.h"
|
||||
#include "dik.h"
|
||||
#include "lib.arrays.h"
|
||||
|
||||
variable steal_mode := 0;
|
||||
|
||||
procedure steal_mode_name(variable mode) begin
|
||||
if (mode == 1) then return "force_success";
|
||||
if (mode == 2) then return "caught_fail";
|
||||
if (mode == 3) then return "silent_fail";
|
||||
if (mode == 4) then return "xp_override";
|
||||
return "vanilla";
|
||||
end
|
||||
|
||||
procedure steal_handler begin
|
||||
variable
|
||||
args := get_sfall_args,
|
||||
thief := args[0],
|
||||
target := args[1],
|
||||
item := args[2],
|
||||
is_planting := args[3],
|
||||
quantity := args[4],
|
||||
thief_name := "<null>",
|
||||
target_name := "<null>",
|
||||
item_name := "<null>";
|
||||
|
||||
if (thief) then thief_name := obj_name(thief);
|
||||
if (target) then target_name := obj_name(target);
|
||||
if (item) then item_name := obj_name(item);
|
||||
|
||||
display_msg(string_format6("steal mode=%s thief=%s target=%s item=%s planting=%d qty=%d",
|
||||
steal_mode_name(steal_mode),
|
||||
thief_name,
|
||||
target_name,
|
||||
item_name,
|
||||
is_planting,
|
||||
quantity));
|
||||
display_msg(string_format1("steal args=%s", debug_array_str(args)));
|
||||
|
||||
if (thief != dude_obj) then return;
|
||||
|
||||
if (steal_mode == 1) then begin
|
||||
display_msg("steal forcing success");
|
||||
display_msg(sprintf(mstr_skill(571 + is_planting * 2), item_name));
|
||||
set_sfall_return(1);
|
||||
end else if (steal_mode == 2) then begin
|
||||
display_msg("steal forcing caught failure");
|
||||
display_msg(sprintf(mstr_skill(570 + is_planting * 2), item_name));
|
||||
set_sfall_return(0);
|
||||
end else if (steal_mode == 3) then begin
|
||||
display_msg("steal forcing silent failure");
|
||||
set_sfall_return(2);
|
||||
end else if (steal_mode == 4) then begin
|
||||
display_msg("steal forcing success with xp override 77");
|
||||
display_msg(sprintf(mstr_skill(571 + is_planting * 2), item_name));
|
||||
set_sfall_return(1);
|
||||
set_sfall_return(77);
|
||||
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;
|
||||
|
||||
steal_mode += 1;
|
||||
if (steal_mode > 4) then steal_mode := 0;
|
||||
|
||||
display_msg(string_format1("steal mode -> %s", steal_mode_name(steal_mode)));
|
||||
end
|
||||
|
||||
procedure start begin
|
||||
if (not game_loaded) then return;
|
||||
|
||||
display_msg("steal manual test ready: press X to cycle vanilla / force_success / caught_fail / silent_fail / xp_override");
|
||||
display_msg("open a steal screen and move an item in either direction; mode 4 should award 77 XP for a successful action");
|
||||
|
||||
register_hook_proc(HOOK_KEYPRESS, keypress_handler);
|
||||
register_hook_proc(HOOK_STEAL, steal_handler);
|
||||
end
|
||||
+42
@@ -355,6 +355,14 @@ int artIsObjectTypeHidden(int objectType)
|
||||
return objectType >= OBJ_TYPE_ITEM && objectType < OBJ_TYPE_COUNT ? gArtListDescriptions[objectType].flags & 1 : 0;
|
||||
}
|
||||
|
||||
// 0x409DF0
|
||||
void artToggleObjectTypeHidden(int objectType)
|
||||
{
|
||||
if (objectType >= 0 && objectType < OBJ_TYPE_COUNT) {
|
||||
gArtListDescriptions[objectType].flags ^= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x418F7C
|
||||
int artGetFidgetCount(int headFid)
|
||||
{
|
||||
@@ -444,6 +452,40 @@ int art_list_str(int fid, char* name)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int artListIndex(int objectType, const char* name)
|
||||
{
|
||||
if (objectType < 0 || objectType >= OBJ_TYPE_COUNT) return -1;
|
||||
if (gArtListDescriptions[objectType].fileNames == nullptr) return -1;
|
||||
|
||||
char upperName[13] = { 0 };
|
||||
strncpy(upperName, name, 12);
|
||||
upperName[12] = '\0';
|
||||
compat_strupr(upperName);
|
||||
|
||||
int length = gArtListDescriptions[objectType].fileNamesLength;
|
||||
const char* fileNames = gArtListDescriptions[objectType].fileNames;
|
||||
|
||||
for (int index = 0; index < length; index++) {
|
||||
const char* entry = fileNames + index * 13;
|
||||
|
||||
char upperEntry[13];
|
||||
strncpy(upperEntry, entry, 12);
|
||||
upperEntry[12] = '\0';
|
||||
compat_strupr(upperEntry);
|
||||
|
||||
char* p = upperEntry;
|
||||
while (*p && ((*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == '_'))
|
||||
p++;
|
||||
*p = '\0';
|
||||
|
||||
if (strcmp(upperEntry, upperName) == 0) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 0x419160
|
||||
Art* artLock(int fid, CacheEntry** handlePtr)
|
||||
{
|
||||
|
||||
@@ -122,6 +122,7 @@ void artReset();
|
||||
void artExit();
|
||||
char* artGetObjectTypeName(int objectType);
|
||||
int artIsObjectTypeHidden(int objectType);
|
||||
void artToggleObjectTypeHidden(int objectType);
|
||||
int artGetFidgetCount(int headFid);
|
||||
void artRender(int fid, unsigned char* dest, int width, int height, int pitch);
|
||||
int art_list_str(int fid, char* name);
|
||||
@@ -150,6 +151,7 @@ int _art_alias_num(int index);
|
||||
int artCritterFidShouldRun(int fid);
|
||||
int artAliasFid(int fid);
|
||||
int buildFid(int objectType, int frmId, int animType, int weaponCode, int rotation);
|
||||
int artListIndex(int objectType, const char* name);
|
||||
Art* artLoad(const char* path);
|
||||
int artRead(const char* path, unsigned char* data);
|
||||
int artWrite(const char* path, unsigned char* data);
|
||||
|
||||
@@ -264,7 +264,6 @@ int audioOpen(const char* fname, AudioFileInfo* info, bool* isMemoryBackedPtr)
|
||||
|
||||
if (info != nullptr) {
|
||||
info->sampleRate = audioFile->sampleRate;
|
||||
info->channels = audioFile->channels;
|
||||
info->bitsPerSample = audioFile->bitsPerSample;
|
||||
}
|
||||
if (isMemoryBackedPtr != nullptr) {
|
||||
|
||||
@@ -100,7 +100,6 @@ int audioFileOpen(const char* fname, AudioFileInfo* info, bool* isMemoryBackedPt
|
||||
audioFile->fileSize *= 2;
|
||||
if (info != nullptr) {
|
||||
info->sampleRate = audioFile->sampleRate;
|
||||
info->channels = audioFile->channels;
|
||||
info->bitsPerSample = 16;
|
||||
}
|
||||
if (isMemoryBackedPtr != nullptr) {
|
||||
|
||||
+24
-14
@@ -1972,31 +1972,41 @@ static Object* _ai_best_weapon(Object* attacker, Object* weapon1, Object* weapon
|
||||
// 0x4298EC
|
||||
static bool _ai_can_use_weapon(Object* critter, Object* weapon, int hitMode)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
int damageFlags = critter->data.critter.combat.results;
|
||||
if ((damageFlags & DAM_CRIP_ARM_LEFT) != 0 && (damageFlags & DAM_CRIP_ARM_RIGHT) != 0) {
|
||||
return false;
|
||||
result = false;
|
||||
}
|
||||
|
||||
if ((damageFlags & DAM_CRIP_ARM_ANY) != 0 && weaponIsTwoHanded(weapon)) {
|
||||
return false;
|
||||
if (result && (damageFlags & DAM_CRIP_ARM_ANY) != 0 && weaponIsTwoHanded(weapon)) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
int rotation = critter->rotation + 1;
|
||||
int animationCode = weaponGetAnimationCode(weapon);
|
||||
int weaponAnimationCode = weaponGetAnimationForHitMode(weapon, hitMode);
|
||||
int fid = buildFid(OBJ_TYPE_CRITTER, critter->fid & 0xFFF, weaponAnimationCode, animationCode, rotation);
|
||||
if (!artExists(fid)) {
|
||||
return false;
|
||||
if (result) {
|
||||
int rotation = critter->rotation + 1;
|
||||
int animationCode = weaponGetAnimationCode(weapon);
|
||||
int weaponAnimationCode = weaponGetAnimationForHitMode(weapon, hitMode);
|
||||
int fid = buildFid(OBJ_TYPE_CRITTER, critter->fid & 0xFFF, weaponAnimationCode, animationCode, rotation);
|
||||
if (!artExists(fid)) {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
int skill = weaponGetSkillForHitMode(weapon, hitMode);
|
||||
AiPacket* ai = aiGetPacket(critter);
|
||||
if (skillGetValue(critter, skill) < ai->min_to_hit) {
|
||||
return false;
|
||||
if (result) {
|
||||
int skill = weaponGetSkillForHitMode(weapon, hitMode);
|
||||
if (skillGetValue(critter, skill) < ai->min_to_hit) {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
int attackType = weaponGetAttackTypeForHitMode(weapon, HIT_MODE_RIGHT_WEAPON_PRIMARY);
|
||||
return _caiHasWeapPrefType(ai, attackType) != 0;
|
||||
if (result) {
|
||||
int attackType = weaponGetAttackTypeForHitMode(weapon, HIT_MODE_RIGHT_WEAPON_PRIMARY);
|
||||
result = _caiHasWeapPrefType(ai, attackType) != 0;
|
||||
}
|
||||
|
||||
return scriptHooks_CanUseWeapon(result, critter, weapon, hitMode);
|
||||
}
|
||||
|
||||
// 0x4299A0
|
||||
|
||||
@@ -29,34 +29,82 @@ long fileTell(File* stream);
|
||||
void fileRewind(File* stream);
|
||||
int fileEof(File* stream);
|
||||
int fileReadUInt8(File* stream, unsigned char* valuePtr);
|
||||
|
||||
// Reads a 16-bit big-endian integer from stream and stores it in host byte order.
|
||||
int fileReadInt16(File* stream, short* valuePtr);
|
||||
|
||||
// Reads a 16-bit big-endian unsigned integer from stream and stores it in host byte order.
|
||||
int fileReadUInt16(File* stream, unsigned short* valuePtr);
|
||||
|
||||
// Reads a 32-bit big-endian integer from stream and stores it in host byte order.
|
||||
int fileReadInt32(File* stream, int* valuePtr);
|
||||
|
||||
// Reads a 32-bit big-endian unsigned integer from stream and stores it in host byte order.
|
||||
int fileReadUInt32(File* stream, unsigned int* valuePtr);
|
||||
|
||||
// Reads a 32-bit big-endian integer from stream and stores it in host byte order (alias).
|
||||
int _db_freadInt(File* stream, int* valuePtr);
|
||||
|
||||
// Reads a 32-bit big-endian floating-point value from stream and stores it in host byte order.
|
||||
int fileReadFloat(File* stream, float* valuePtr);
|
||||
|
||||
// Reads a 32-bit big-endian integer from stream and stores the boolean equivalent in host byte order.
|
||||
int fileReadBool(File* stream, bool* valuePtr);
|
||||
int fileWriteUInt8(File* stream, unsigned char value);
|
||||
|
||||
// Writes a 16-bit integer to stream in big-endian byte order.
|
||||
int fileWriteInt16(File* stream, short value);
|
||||
|
||||
// Writes a 16-bit unsigned integer to stream in big-endian byte order.
|
||||
int fileWriteUInt16(File* stream, unsigned short value);
|
||||
|
||||
// Writes a 32-bit integer to stream in big-endian byte order.
|
||||
int fileWriteInt32(File* stream, int value);
|
||||
|
||||
// Writes a 32-bit integer to stream in big-endian byte order (alias).
|
||||
int _db_fwriteLong(File* stream, int value);
|
||||
|
||||
// Writes a 32-bit unsigned integer to stream in big-endian byte order.
|
||||
int fileWriteUInt32(File* stream, unsigned int value);
|
||||
|
||||
// Writes a 32-bit floating-point value to stream in big-endian byte order.
|
||||
int fileWriteFloat(File* stream, float value);
|
||||
|
||||
// Writes a boolean value to stream as a 32-bit big-endian integer (1 or 0).
|
||||
int fileWriteBool(File* stream, bool value);
|
||||
int fileReadUInt8List(File* stream, unsigned char* arr, int count);
|
||||
int fileReadFixedLengthString(File* stream, char* string, int length);
|
||||
|
||||
// Reads a list of 16-bit big-endian integers from stream into arr (in host byte order).
|
||||
int fileReadInt16List(File* stream, short* arr, int count);
|
||||
|
||||
// Reads a list of 16-bit big-endian unsigned integers from stream into arr (in host byte order).
|
||||
int fileReadUInt16List(File* stream, unsigned short* arr, int count);
|
||||
|
||||
// Reads a list of 32-bit big-endian integers from stream into arr (in host byte order).
|
||||
int fileReadInt32List(File* stream, int* arr, int count);
|
||||
|
||||
// Reads a list of 32-bit big-endian integers from stream into arr (in host byte order, alias).
|
||||
int _db_freadIntCount(File* stream, int* arr, int count);
|
||||
|
||||
// Reads a list of 32-bit big-endian unsigned integers from stream into arr (in host byte order).
|
||||
int fileReadUInt32List(File* stream, unsigned int* arr, int count);
|
||||
int fileWriteUInt8List(File* stream, unsigned char* arr, int count);
|
||||
int fileWriteFixedLengthString(File* stream, char* string, int length);
|
||||
|
||||
// Writes a list of 16-bit integers to stream in big-endian byte order.
|
||||
int fileWriteInt16List(File* stream, short* arr, int count);
|
||||
|
||||
// Writes a list of 16-bit unsigned integers to stream in big-endian byte order.
|
||||
int fileWriteUInt16List(File* stream, unsigned short* arr, int count);
|
||||
|
||||
// Writes a list of 32-bit integers to stream in big-endian byte order.
|
||||
int fileWriteInt32List(File* stream, int* arr, int count);
|
||||
|
||||
// Writes a list of 32-bit integers to stream in big-endian byte order (alias).
|
||||
int _db_fwriteLongCount(File* stream, int* arr, int count);
|
||||
|
||||
// Writes a list of 32-bit unsigned integers to stream in big-endian byte order.
|
||||
int fileWriteUInt32List(File* stream, unsigned int* arr, int count);
|
||||
int fileNameListInit(const char* pattern, char*** fileNames);
|
||||
void fileNameListFree(char*** fileNames, int unused);
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
namespace fallout {
|
||||
|
||||
// The size of decompression buffer for reading compressed [DFile]s.
|
||||
#define DFILE_DECOMPRESSION_BUFFER_SIZE (0x400)
|
||||
#define DFILE_DECOMPRESSION_BUFFER_SIZE (0x1000)
|
||||
|
||||
// Specifies that [DFile] has unget character.
|
||||
//
|
||||
|
||||
+85
-29
@@ -279,6 +279,8 @@ static int gGameDialogBackgroundWindow = -1;
|
||||
// 0x518744
|
||||
static int gGameDialogWindow = -1;
|
||||
|
||||
static bool gameDialogUseHrArt = false;
|
||||
|
||||
// 0x518748
|
||||
static Rect _backgrndRects[8] = {
|
||||
{ 126, 14, 152, 40 },
|
||||
@@ -291,6 +293,37 @@ static Rect _backgrndRects[8] = {
|
||||
{ 504, 40, 514, 188 },
|
||||
};
|
||||
|
||||
static bool gameDialogShouldUseHrArt()
|
||||
{
|
||||
return settings.ui.enable_dialog_border
|
||||
&& (screenGetWidth() > GAME_DIALOG_WINDOW_WIDTH || screenGetHeight() > GAME_DIALOG_WINDOW_HEIGHT);
|
||||
}
|
||||
|
||||
static int gameDialogHrArtYOffset()
|
||||
{
|
||||
return gameDialogUseHrArt ? 5 : 0;
|
||||
}
|
||||
|
||||
static Rect gameDialogGetBackgroundRect(int index)
|
||||
{
|
||||
Rect rect = _backgrndRects[index];
|
||||
int yOffset = gameDialogHrArtYOffset();
|
||||
rect.top += yOffset;
|
||||
rect.bottom += yOffset;
|
||||
return rect;
|
||||
}
|
||||
|
||||
static int gameDialogGetBackgroundWindowY()
|
||||
{
|
||||
// center onplay area if large enough, else center on screen
|
||||
int visibleHeight = screenGetVisibleHeight();
|
||||
if (visibleHeight >= GAME_DIALOG_WINDOW_HEIGHT) {
|
||||
return (visibleHeight - GAME_DIALOG_WINDOW_HEIGHT) / 2;
|
||||
}
|
||||
|
||||
return (screenGetHeight() - GAME_DIALOG_WINDOW_HEIGHT) / 2;
|
||||
}
|
||||
|
||||
// 0x5187C8
|
||||
static bool _talk_need_to_center = true;
|
||||
|
||||
@@ -2494,31 +2527,35 @@ int _gdCreateHeadWindow()
|
||||
int windowWidth = GAME_DIALOG_WINDOW_WIDTH;
|
||||
|
||||
// NOTE: Uninline.
|
||||
talk_to_create_background_window();
|
||||
gameDialogWindowRenderBackground();
|
||||
if (talk_to_create_background_window() == -1 || gameDialogWindowRenderBackground() == -1) {
|
||||
_gdDestroyHeadWindow();
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned char* buf = windowGetBuffer(gGameDialogBackgroundWindow);
|
||||
ConstBuffer2D backgroundBuf = windowGetBuffer2D(gGameDialogBackgroundWindow);
|
||||
|
||||
for (int index = 0; index < 8; index++) {
|
||||
soundContinueAll();
|
||||
|
||||
Rect* rect = &(_backgrndRects[index]);
|
||||
int width = rect->right - rect->left;
|
||||
int height = rect->bottom - rect->top;
|
||||
Rect rect = gameDialogGetBackgroundRect(index);
|
||||
int width = rect.right - rect.left;
|
||||
int height = rect.bottom - rect.top;
|
||||
_backgrndBufs[index] = (unsigned char*)internal_malloc(width * height);
|
||||
if (_backgrndBufs[index] == nullptr) {
|
||||
_gdDestroyHeadWindow();
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned char* src = buf;
|
||||
src += windowWidth * rect->top + rect->left;
|
||||
|
||||
blitBufferToBuffer(src, width, height, windowWidth, _backgrndBufs[index], width);
|
||||
Buffer2D savedBackgroundBuf { _backgrndBufs[index], width, height };
|
||||
blitBuffer2D(backgroundBuf, rect.left, rect.top, width, height, savedBackgroundBuf);
|
||||
}
|
||||
|
||||
_gdialog_window_create();
|
||||
if (_gdialog_window_create() == -1) {
|
||||
_gdDestroyHeadWindow();
|
||||
return -1;
|
||||
}
|
||||
|
||||
gGameDialogDisplayBuffer = windowGetBuffer(gGameDialogBackgroundWindow) + windowWidth * 14 + 126;
|
||||
gGameDialogDisplayBuffer = windowGetBuffer(gGameDialogBackgroundWindow) + windowWidth * (14 + gameDialogHrArtYOffset()) + 126;
|
||||
|
||||
// TODO: jnz at 0x447275 without cmp or test, not sure what that means.
|
||||
if (false) {
|
||||
@@ -2547,10 +2584,12 @@ void _gdDestroyHeadWindow()
|
||||
gGameDialogBackgroundWindow = -1;
|
||||
}
|
||||
|
||||
gameDialogUseHrArt = false;
|
||||
gExpandedBarterEnabled = false;
|
||||
|
||||
for (int index = 0; index < 8; index++) {
|
||||
internal_free(_backgrndBufs[index]);
|
||||
_backgrndBufs[index] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4551,13 +4590,14 @@ static const char* expandedBarterFrmName()
|
||||
// 0x44AAD8
|
||||
static int talk_to_create_background_window()
|
||||
{
|
||||
gameDialogUseHrArt = false;
|
||||
|
||||
gExpandedBarterEnabled = settings.ui.expand_barter_window
|
||||
&& screenGetHeight() >= GAME_DIALOG_WINDOW_HEIGHT + kExpandedBarterExtraHeight
|
||||
&& FrmImage().lock(OBJ_TYPE_INTERFACE, expandedBarterFrmName());
|
||||
|
||||
int backgroundWindowX = (screenGetWidth() - GAME_DIALOG_WINDOW_WIDTH) / 2;
|
||||
int effectiveBgHeight = GAME_DIALOG_WINDOW_HEIGHT + (gExpandedBarterEnabled ? kExpandedBarterExtraHeight : 0);
|
||||
int backgroundWindowY = (screenGetHeight() - effectiveBgHeight) / 2;
|
||||
int backgroundWindowY = gameDialogGetBackgroundWindowY();
|
||||
|
||||
gGameDialogBackgroundWindow = windowCreate(backgroundWindowX,
|
||||
backgroundWindowY,
|
||||
@@ -4577,15 +4617,29 @@ static int talk_to_create_background_window()
|
||||
int gameDialogWindowRenderBackground()
|
||||
{
|
||||
FrmImage backgroundFrmImage;
|
||||
// alltlk.frm - dialog screen background
|
||||
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 103, 0, 0, 0);
|
||||
if (!backgroundFrmImage.lock(backgroundFid)) {
|
||||
return -1;
|
||||
|
||||
if (gameDialogShouldUseHrArt()) {
|
||||
if (backgroundFrmImage.lock(OBJ_TYPE_INTERFACE, "HR_ALLTLK.frm")
|
||||
&& backgroundFrmImage.getWidth() >= GAME_DIALOG_WINDOW_WIDTH
|
||||
&& backgroundFrmImage.getHeight() >= GAME_DIALOG_WINDOW_HEIGHT) {
|
||||
gameDialogUseHrArt = true;
|
||||
} else {
|
||||
backgroundFrmImage.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
int windowWidth = GAME_DIALOG_WINDOW_WIDTH;
|
||||
unsigned char* windowBuffer = windowGetBuffer(gGameDialogBackgroundWindow);
|
||||
blitBufferToBuffer(backgroundFrmImage.getData(), windowWidth, 480, windowWidth, windowBuffer, windowWidth);
|
||||
if (!backgroundFrmImage.isLocked()) {
|
||||
// alltlk.frm - dialog screen background
|
||||
FrmId backgroundFid(OBJ_TYPE_INTERFACE, 103);
|
||||
if (!backgroundFrmImage.lock(backgroundFid)) {
|
||||
return -1;
|
||||
}
|
||||
gameDialogUseHrArt = false;
|
||||
}
|
||||
|
||||
ConstBuffer2D backgroundFrmBuf = backgroundFrmImage.getBuffer();
|
||||
Buffer2D windowBuf = windowGetBuffer2D(gGameDialogBackgroundWindow);
|
||||
blitBuffer2D(backgroundFrmBuf, 0, 0, GAME_DIALOG_WINDOW_WIDTH, GAME_DIALOG_WINDOW_HEIGHT, windowBuf);
|
||||
|
||||
if (!_dialogue_just_started) {
|
||||
windowRefresh(gGameDialogBackgroundWindow);
|
||||
@@ -4734,11 +4788,13 @@ void gameDialogRenderTalkingHead(Art* headFrm, int frame)
|
||||
GAME_DIALOG_WINDOW_WIDTH);
|
||||
}
|
||||
|
||||
int yOffset = gameDialogHrArtYOffset();
|
||||
|
||||
Rect headRect;
|
||||
headRect.left = 126;
|
||||
headRect.top = 14;
|
||||
headRect.top = 14 + yOffset;
|
||||
headRect.right = 514;
|
||||
headRect.bottom = 214;
|
||||
headRect.bottom = 214 + yOffset;
|
||||
|
||||
unsigned char* dest = windowGetBuffer(gGameDialogBackgroundWindow);
|
||||
|
||||
@@ -4748,7 +4804,7 @@ void gameDialogRenderTalkingHead(Art* headFrm, int frame)
|
||||
_upperHighlightFrmImage.getWidth(),
|
||||
dest,
|
||||
426,
|
||||
15,
|
||||
15 + yOffset,
|
||||
GAME_DIALOG_WINDOW_WIDTH,
|
||||
_light_BlendTable,
|
||||
_light_GrayTable);
|
||||
@@ -4759,20 +4815,20 @@ void gameDialogRenderTalkingHead(Art* headFrm, int frame)
|
||||
_lowerHighlightFrmImage.getWidth(),
|
||||
dest,
|
||||
129,
|
||||
214 - _lowerHighlightFrmImage.getHeight() - 2,
|
||||
214 + yOffset - _lowerHighlightFrmImage.getHeight() - 2,
|
||||
GAME_DIALOG_WINDOW_WIDTH,
|
||||
_dark_BlendTable,
|
||||
_dark_GrayTable);
|
||||
|
||||
for (int index = 0; index < 8; ++index) {
|
||||
Rect* rect = &(_backgrndRects[index]);
|
||||
int width = rect->right - rect->left;
|
||||
Rect rect = gameDialogGetBackgroundRect(index);
|
||||
int width = rect.right - rect.left;
|
||||
|
||||
blitBufferToBufferTrans(_backgrndBufs[index],
|
||||
width,
|
||||
rect->bottom - rect->top,
|
||||
rect.bottom - rect.top,
|
||||
width,
|
||||
dest + GAME_DIALOG_WINDOW_WIDTH * rect->top + rect->left,
|
||||
dest + GAME_DIALOG_WINDOW_WIDTH * rect.top + rect.left,
|
||||
GAME_DIALOG_WINDOW_WIDTH);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -2279,7 +2279,7 @@ int _gmouse_3d_move_to(int x, int y, int elevation, Rect* rect)
|
||||
int fid = gGameMouseBouncingCursor->fid;
|
||||
if (FID_TYPE(fid) == OBJ_TYPE_TILE) {
|
||||
int squareTile = squareTileFromScreenXY(x, y, elevation);
|
||||
if (squareTile == -1) {
|
||||
if (squareTile != -1) {
|
||||
tile = HEX_GRID_WIDTH * (2 * (squareTile / SQUARE_GRID_WIDTH) + 1) + 2 * (squareTile % SQUARE_GRID_WIDTH) + 1;
|
||||
x1 = -8;
|
||||
y1 = 13;
|
||||
|
||||
+151
-4
@@ -1,12 +1,13 @@
|
||||
#include "graph_lib.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "color.h"
|
||||
#include "db.h"
|
||||
#include "debug.h"
|
||||
#include "memory.h"
|
||||
#include "palette.h"
|
||||
|
||||
namespace fallout {
|
||||
|
||||
@@ -53,10 +54,156 @@ unsigned char HighRGB(unsigned char color)
|
||||
}
|
||||
|
||||
// 0x44ED98
|
||||
int load_lbm_to_buf(const char* path, unsigned char* buffer, int a3, int a4, int a5, int a6, int a7)
|
||||
// Loads LBM image file into a dstBuffer.
|
||||
// Returns image width on success, -1 on error.
|
||||
int load_lbm_to_buf(const char* path, unsigned char* dstBuffer, int xMin, int yMin, int xMax, int yMax)
|
||||
{
|
||||
// TODO: Incomplete.
|
||||
File* stream = fileOpen(path, "rb");
|
||||
if (stream == nullptr) {
|
||||
debugPrint("load_lbm_to_buf(%s): fileOpen failed\n", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned char form[4];
|
||||
unsigned int formSize;
|
||||
unsigned char ilbm[4];
|
||||
if (fileRead(form, 1, 4, stream) != 4
|
||||
|| memcmp(form, "FORM", 4) != 0
|
||||
|| fileReadUInt32(stream, &formSize) == -1
|
||||
|| fileRead(ilbm, 1, 4, stream) != 4) {
|
||||
debugPrint("load_lbm_to_buf(%s): incorrect LBM header\n", path);
|
||||
fileClose(stream);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int imgWidth = 0, imgHeight = 0;
|
||||
|
||||
// NOTE: The original ASM's sliding-window scanner consumes the BMHD
|
||||
// chunk size as a 4-byte skip, reads only the 2-byte width, and then
|
||||
// lets the scanner drift through the remaining BMHD fields (height,
|
||||
// nPlanes, masking, comp, etc.) while searching for the next tag.
|
||||
// Height and compression are therefore never stored by the original.
|
||||
// The BODY decoder always runs as PackBits unconditionally.
|
||||
|
||||
// paletteRemap[i] = system-palette index that best matches LBM colour i.
|
||||
// Built from CMAP via colorTable. Index 0 is populated but never used
|
||||
// (pixel value 0 is always output as 0 — the transparent colour).
|
||||
unsigned char paletteRemap[256] = {};
|
||||
|
||||
while (true) {
|
||||
unsigned char chunkType[4];
|
||||
unsigned int chunkSize;
|
||||
if (fileRead(chunkType, 1, 4, stream) != 4
|
||||
|| fileReadUInt32(stream, &chunkSize) == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned int aligned = (chunkSize + 1) & ~1u;
|
||||
|
||||
if (memcmp(chunkType, "BMHD", 4) == 0) {
|
||||
// Original reads only w (2 bytes); h is read here to advance the
|
||||
// file position but its value is intentionally discarded.
|
||||
unsigned short w, h;
|
||||
if (fileReadUInt16(stream, &w) == -1
|
||||
|| fileReadUInt16(stream, &h) == -1) break;
|
||||
imgWidth = w;
|
||||
imgHeight = h;
|
||||
|
||||
// Skip every remaining BMHD field — the original never reads them.
|
||||
unsigned int bmhdRemaining = aligned - 4; // already consumed w + h
|
||||
if (bmhdRemaining > 0) fileSeek(stream, bmhdRemaining, SEEK_CUR);
|
||||
|
||||
} else if (memcmp(chunkType, "CMAP", 4) == 0) {
|
||||
// The game DOES use its own palette, but it reconciles the two
|
||||
// by mapping each LBM color through colorTable (RGB555 → nearest
|
||||
// system index), storing the result in paletteRemap[].
|
||||
int entries = static_cast<int>(std::min(chunkSize / 3u, 256u));
|
||||
for (int i = 0; i < entries; i++) {
|
||||
unsigned char r, g, b;
|
||||
if (fileReadUInt8(stream, &r) == -1
|
||||
|| fileReadUInt8(stream, &g) == -1
|
||||
|| fileReadUInt8(stream, &b) == -1) {
|
||||
fileClose(stream);
|
||||
return -1;
|
||||
}
|
||||
int rgb555 = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
|
||||
paletteRemap[i] = _colorTable[rgb555];
|
||||
}
|
||||
unsigned int cmapRemaining = aligned - static_cast<unsigned int>(entries * 3);
|
||||
if (cmapRemaining > 0) fileSeek(stream, cmapRemaining, SEEK_CUR);
|
||||
|
||||
} else if (memcmp(chunkType, "BODY", 4) == 0) {
|
||||
// Decompression, palette remapping, and clipped output in a single pass with no intermediate pixel buffer.
|
||||
// Output rules:
|
||||
// pixel == 0 -> write 0 unconditionally (transparent)
|
||||
// pixel != 0 -> write paletteRemap[pixel]
|
||||
//
|
||||
// The run-length case pre-remaps the pixel value once before the repeat loop.
|
||||
|
||||
unsigned char* dst = dstBuffer;
|
||||
int col = 0; // current column - wraps at imgWidth
|
||||
int row = 0; // current row - increments on wrap
|
||||
|
||||
// Advance the pixel cursor by one position.
|
||||
auto advance = [&]() {
|
||||
if (++col == imgWidth) {
|
||||
col = 0;
|
||||
++row;
|
||||
}
|
||||
};
|
||||
|
||||
// Emit one pixel, respecting the clip rectangle.
|
||||
// Used for literal-run pixels where the remap must occur per byte.
|
||||
auto emit = [&](unsigned char raw) {
|
||||
if (col >= xMin && col <= xMax && row >= yMin && row <= yMax)
|
||||
*dst++ = (raw == 0) ? 0 : paletteRemap[raw];
|
||||
advance();
|
||||
};
|
||||
|
||||
// PackBits RLE — always, regardless of BMHD compression field.
|
||||
bool eof = false;
|
||||
unsigned char ctrl;
|
||||
while (!eof && fileReadUInt8(stream, &ctrl) != -1) {
|
||||
if (ctrl < 0x80) {
|
||||
// Literal run: the next (ctrl + 1) bytes are distinct pixels.
|
||||
int count = ctrl + 1;
|
||||
for (int i = 0; i < count && !eof; i++) {
|
||||
unsigned char px;
|
||||
if (fileReadUInt8(stream, &px) == -1)
|
||||
eof = true;
|
||||
else
|
||||
emit(px);
|
||||
}
|
||||
} else {
|
||||
// Run: repeat the next single pixel (257 − ctrl) times.
|
||||
int count = 257 - static_cast<int>(ctrl);
|
||||
unsigned char px;
|
||||
if (fileReadUInt8(stream, &px) == -1) {
|
||||
eof = true;
|
||||
} else {
|
||||
// Remap once for the whole run (mirrors var_10 in the ASM).
|
||||
unsigned char mapped = (px == 0) ? 0 : paletteRemap[px];
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (col >= xMin && col <= xMax && row >= yMin && row <= yMax)
|
||||
*dst++ = mapped;
|
||||
advance();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reaching the end of BODY data (normally or via EOF) is the
|
||||
// success path.
|
||||
fileClose(stream);
|
||||
debugPrint("load_lbm_to_buf: loaded %dx%d OK\n", imgWidth, imgHeight);
|
||||
return imgWidth;
|
||||
|
||||
} else {
|
||||
if (aligned > 0) fileSeek(stream, aligned, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
fileClose(stream);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
namespace fallout {
|
||||
|
||||
unsigned char HighRGB(unsigned char color);
|
||||
int load_lbm_to_buf(const char* path, unsigned char* buffer, int a3, int a4, int a5, int a6, int a7);
|
||||
int load_lbm_to_buf(const char* path, unsigned char* dstBuffer, int xMin, int yMin, int xMax, int yMax);
|
||||
int graphCompress(unsigned char* a1, unsigned char* a2, int a3);
|
||||
int graphDecompress(unsigned char* a1, unsigned char* a2, int a3);
|
||||
void grayscalePaletteUpdate(int a1, int a2);
|
||||
|
||||
@@ -969,6 +969,7 @@ static void buildNormalizedQwertyKeys()
|
||||
keys[SDL_SCANCODE_RIGHT] = SDL_SCANCODE_RIGHT;
|
||||
keys[SDL_SCANCODE_END] = SDL_SCANCODE_END;
|
||||
keys[SDL_SCANCODE_DOWN] = SDL_SCANCODE_DOWN;
|
||||
keys[SDL_SCANCODE_PAGEUP] = SDL_SCANCODE_PAGEUP;
|
||||
keys[SDL_SCANCODE_PAGEDOWN] = SDL_SCANCODE_PAGEDOWN;
|
||||
keys[SDL_SCANCODE_INSERT] = SDL_SCANCODE_INSERT;
|
||||
keys[SDL_SCANCODE_DELETE] = SDL_SCANCODE_DELETE;
|
||||
|
||||
@@ -3298,6 +3298,9 @@ bool ProgramValue::isEmpty() const
|
||||
switch (opcode) {
|
||||
case VALUE_TYPE_INT:
|
||||
case VALUE_TYPE_STRING:
|
||||
case VALUE_TYPE_DYNAMIC_STRING:
|
||||
// XXX: this matches the engine, but for VALUE_TYPE_STRING, 0 is valid string index, so this
|
||||
// would be wrong in that case.
|
||||
return integerValue == 0;
|
||||
case VALUE_TYPE_FLOAT:
|
||||
return floatValue == 0.0;
|
||||
|
||||
+45
-20
@@ -36,7 +36,9 @@
|
||||
#include "reaction.h"
|
||||
#include "scripts.h"
|
||||
#include "settings.h"
|
||||
#include "sfall_config.h"
|
||||
#include "sfall_opcodes.h"
|
||||
#include "sfall_script_hooks.h"
|
||||
#include "skill.h"
|
||||
#include "stat.h"
|
||||
#include "svga.h"
|
||||
@@ -121,13 +123,6 @@ typedef enum CritterState {
|
||||
CRITTER_STATE_PRONE = 0x02,
|
||||
} CritterState;
|
||||
|
||||
enum {
|
||||
INVEN_TYPE_WORN = 0,
|
||||
INVEN_TYPE_RIGHT_HAND = 1,
|
||||
INVEN_TYPE_LEFT_HAND = 2,
|
||||
INVEN_TYPE_INV_COUNT = -2,
|
||||
};
|
||||
|
||||
typedef enum FloatingMessageType {
|
||||
FLOATING_MESSAGE_TYPE_WARNING = -2,
|
||||
FLOATING_MESSAGE_TYPE_COLOR_SEQUENCE = -1,
|
||||
@@ -414,6 +409,15 @@ static int tileIsVisible(int tile)
|
||||
// 0x45409C
|
||||
static int _correctFidForRemovedItem(Object* critter, Object* item, int flags)
|
||||
{
|
||||
InvenSlot invenSlot = InvenSlot::Armor;
|
||||
if ((flags & OBJECT_IN_RIGHT_HAND) != 0) {
|
||||
invenSlot = InvenSlot::RightHand;
|
||||
} else if ((flags & OBJECT_IN_LEFT_HAND) != 0) {
|
||||
invenSlot = InvenSlot::LeftHand;
|
||||
}
|
||||
|
||||
scriptHooks_InvenWield(critter, item, invenSlot, 0, 1);
|
||||
|
||||
if (critter == gDude) {
|
||||
bool animated = !gameUiIsDisabled();
|
||||
interfaceUpdateItems(animated, INTERFACE_ITEM_ACTION_DEFAULT, INTERFACE_ITEM_ACTION_DEFAULT);
|
||||
@@ -3015,11 +3019,16 @@ static void opCritterGetInventoryObject(Program* program)
|
||||
Object* critter = static_cast<Object*>(programStackPopPointer(program));
|
||||
|
||||
if (PID_TYPE(critter->pid) == OBJ_TYPE_CRITTER) {
|
||||
switch (type) {
|
||||
case INVEN_TYPE_WORN:
|
||||
if (type == kInvenSlotInvCount) {
|
||||
programStackPushInteger(program, critter->data.inventory.length);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (static_cast<InvenSlot>(type)) {
|
||||
case InvenSlot::Armor:
|
||||
programStackPushPointer(program, critterGetArmor(critter));
|
||||
break;
|
||||
case INVEN_TYPE_RIGHT_HAND:
|
||||
case InvenSlot::RightHand:
|
||||
if (critter == gDude) {
|
||||
if (interfaceGetCurrentHand() != HAND_LEFT) {
|
||||
programStackPushPointer(program, critterGetItem2(critter));
|
||||
@@ -3030,7 +3039,7 @@ static void opCritterGetInventoryObject(Program* program)
|
||||
programStackPushPointer(program, critterGetItem2(critter));
|
||||
}
|
||||
break;
|
||||
case INVEN_TYPE_LEFT_HAND:
|
||||
case InvenSlot::LeftHand:
|
||||
if (critter == gDude) {
|
||||
if (interfaceGetCurrentHand() == HAND_LEFT) {
|
||||
programStackPushPointer(program, critterGetItem1(critter));
|
||||
@@ -3041,9 +3050,6 @@ static void opCritterGetInventoryObject(Program* program)
|
||||
programStackPushPointer(program, critterGetItem1(critter));
|
||||
}
|
||||
break;
|
||||
case INVEN_TYPE_INV_COUNT:
|
||||
programStackPushInteger(program, critter->data.inventory.length);
|
||||
break;
|
||||
default:
|
||||
scriptError("script error: %s: Error in critter_inven_obj -- wrong type!", program->name);
|
||||
programStackPushInteger(program, 0);
|
||||
@@ -4599,24 +4605,43 @@ static void opMoveObjectInventoryToObject(Program* program)
|
||||
}
|
||||
|
||||
Object* oldArmor = nullptr;
|
||||
Object* item2 = nullptr;
|
||||
Object* oldWeapon = nullptr;
|
||||
if (object1 == gDude) {
|
||||
oldArmor = critterGetArmor(object1);
|
||||
if (interfaceGetCurrentHand() == HAND_RIGHT) {
|
||||
oldWeapon = critterGetItem2(object1);
|
||||
} else {
|
||||
oldWeapon = critterGetItem1(object1);
|
||||
}
|
||||
} else {
|
||||
item2 = critterGetItem2(object1);
|
||||
oldWeapon = critterGetItem2(object1);
|
||||
}
|
||||
|
||||
if (object1 != gDude && item2 != nullptr) {
|
||||
if (object1 == gDude) {
|
||||
if (oldWeapon != nullptr) {
|
||||
InvenSlot invenSlot = interfaceGetCurrentHand() == HAND_RIGHT
|
||||
? InvenSlot::RightHand
|
||||
: InvenSlot::LeftHand;
|
||||
scriptHooks_InvenWield(object1, oldWeapon, invenSlot, 0, 1);
|
||||
}
|
||||
|
||||
if (oldArmor != nullptr) {
|
||||
scriptHooks_InvenWield(object1, oldArmor, InvenSlot::Armor, 0, 1);
|
||||
}
|
||||
} else if (oldWeapon != nullptr) {
|
||||
// CE intentionally reports the NPC weapon's actual hand slot here.
|
||||
// Sfall's op_move_obj_inven_to_obj HOOK_INVENWIELD path passes
|
||||
// InvenSlot::Armor unconditionally for NPC weapons in this case.
|
||||
int flags = 0;
|
||||
if ((item2->flags & OBJECT_IN_LEFT_HAND) != 0) {
|
||||
if ((oldWeapon->flags & OBJECT_IN_LEFT_HAND) != 0) {
|
||||
flags |= OBJECT_IN_LEFT_HAND;
|
||||
}
|
||||
|
||||
if ((item2->flags & OBJECT_IN_RIGHT_HAND) != 0) {
|
||||
if ((oldWeapon->flags & OBJECT_IN_RIGHT_HAND) != 0) {
|
||||
flags |= OBJECT_IN_RIGHT_HAND;
|
||||
}
|
||||
|
||||
_correctFidForRemovedItem(object1, item2, flags);
|
||||
_correctFidForRemovedItem(object1, oldWeapon, flags);
|
||||
}
|
||||
|
||||
itemMoveAll(object1, object2);
|
||||
|
||||
@@ -5,6 +5,14 @@
|
||||
|
||||
namespace fallout {
|
||||
|
||||
enum class InvenSlot {
|
||||
Armor = 0, // INVEN_TYPE_WORN
|
||||
RightHand = 1, // INVEN_TYPE_RIGHT_HAND
|
||||
LeftHand = 2, // INVEN_TYPE_LEFT_HAND
|
||||
};
|
||||
|
||||
constexpr int kInvenSlotInvCount = -2;
|
||||
|
||||
void _intExtraClose_();
|
||||
void _initIntExtra();
|
||||
void intExtraUpdate();
|
||||
|
||||
+132
-42
@@ -26,6 +26,7 @@
|
||||
#include "game_sound.h"
|
||||
#include "input.h"
|
||||
#include "interface.h"
|
||||
#include "interpreter_extra.h"
|
||||
#include "item.h"
|
||||
#include "kb.h"
|
||||
#include "light.h"
|
||||
@@ -131,6 +132,9 @@ constexpr int kTradeSlotCount = 3;
|
||||
#define INVENTORY_PC_BODY_VIEW_MAX_X (INVENTORY_PC_BODY_VIEW_X + INVENTORY_BODY_VIEW_WIDTH)
|
||||
#define INVENTORY_PC_BODY_VIEW_MAX_Y (INVENTORY_PC_BODY_VIEW_Y + INVENTORY_BODY_VIEW_HEIGHT)
|
||||
|
||||
#define INVENTORY_BARTER_LEFT_BODY_VIEW_X 18
|
||||
#define INVENTORY_BARTER_LEFT_BODY_VIEW_Y 25
|
||||
|
||||
#define INVENTORY_LOOT_RIGHT_BODY_VIEW_X 422
|
||||
#define INVENTORY_LOOT_RIGHT_BODY_VIEW_Y 35
|
||||
|
||||
@@ -166,7 +170,6 @@ constexpr int kTradeSlotCount = 3;
|
||||
#define INVENTORY_LOOT_RIGHT_SCROLLER_Y_PAD (INVENTORY_LOOT_RIGHT_SCROLLER_Y + INVENTORY_SLOT_PADDING)
|
||||
|
||||
#define INVENTORY_LOOT_CRITTER_TOGGLE_Y (INVENTORY_LOOT_LEFT_BODY_VIEW_Y + INVENTORY_BODY_VIEW_HEIGHT + 24)
|
||||
|
||||
#define INVENTORY_TRADE_LEFT_SCROLLER_X_PAD (INVENTORY_TRADE_LEFT_SCROLLER_Y + INVENTORY_SLOT_PADDING)
|
||||
#define INVENTORY_TRADE_LEFT_SCROLLER_Y_PAD (INVENTORY_TRADE_LEFT_SCROLLER_Y + INVENTORY_SLOT_PADDING)
|
||||
|
||||
@@ -323,10 +326,11 @@ static void _adjust_fid();
|
||||
static void inventoryRenderSummary();
|
||||
static int _inven_from_button(int keyCode, Object** outItem, Object*** outItemSlot, Object** outOwner);
|
||||
static void inventoryRenderItemDescription(const char* string);
|
||||
static void inventoryDrawCenteredText(unsigned char* buffer, int pitch, int width, int x, int y, const char* text, int color);
|
||||
static void inventoryExamineItem(Object* critter, Object* item);
|
||||
static void inventorySetLootLeftPaneCritter(Object* critter, Object* target);
|
||||
static void inventorySetLeftPaneCritter(Object* critter, Object* target, int inventoryWindowType);
|
||||
static void inventoryWindowOpenContextMenu(int eventCode, int inventoryWindowType);
|
||||
static InventoryMoveResult _move_inventory(Object* item, int slotIndex, Object* targetObj, bool isPlanting);
|
||||
static InventoryMoveResult _move_inventory(Object* item, int slotIndex, Object* targetObj, bool isPlanting, int* stealXpOverridePtr);
|
||||
static std::pair<int, int> barterComputeTablesValue(Object* dude, Object* npc, bool offerButton = false);
|
||||
static std::pair<int, int> barterComputeTablesWeight(Object* dude, Object* npc);
|
||||
static int barterAttemptTransaction(Object* dude, Object* offerTable, Object* npc, Object* barterTable);
|
||||
@@ -357,6 +361,7 @@ static int inventoryLootGetSlotY(int slotIndex);
|
||||
static bool inventoryLootMouseHitTestScroller(bool targetInventory);
|
||||
static int inventoryComputeAlignedMaxOffset(int length, int visibleSlots, int scrollStep);
|
||||
static int inventoryGetCenteredWindowY(int windowHeight);
|
||||
static void inventoryDisplayLeftPaneCompanionName(unsigned char* windowBuffer, int windowPitch, const Rect& rect, int index);
|
||||
|
||||
// 0x46E6D0
|
||||
static const int gSummaryStats[7] = {
|
||||
@@ -844,6 +849,13 @@ void inventoryResetDude()
|
||||
_inven_pid = 0x1000000;
|
||||
}
|
||||
|
||||
// inven_set_dude
|
||||
void inventorySetDude(Object* obj, int pid)
|
||||
{
|
||||
_inven_dude = obj;
|
||||
_inven_pid = pid;
|
||||
}
|
||||
|
||||
// TODO(CE): move to more generic location
|
||||
int inventoryComputeCritterFid(Object* critter, int basePid, Object* rightHandItem, Object* leftHandItem, Object* armor, int activeHand, int anim, int rotation)
|
||||
{
|
||||
@@ -1392,7 +1404,7 @@ static bool _setup_inventory(int inventoryWindowType)
|
||||
|
||||
// Invisible button representing left character.
|
||||
buttonCreateAction(gInventoryBarterBackgroundWindow,
|
||||
15, 25, INVENTORY_BODY_VIEW_WIDTH, INVENTORY_BODY_VIEW_HEIGHT, 2500);
|
||||
INVENTORY_BARTER_LEFT_BODY_VIEW_X, INVENTORY_BARTER_LEFT_BODY_VIEW_Y, INVENTORY_BODY_VIEW_WIDTH, INVENTORY_BODY_VIEW_HEIGHT, 2500);
|
||||
|
||||
// Invisible button representing right character.
|
||||
buttonCreateAction(gInventoryBarterBackgroundWindow,
|
||||
@@ -1858,10 +1870,8 @@ static void _display_inventory(int stackOffset, int dragSlotIndex, int inventory
|
||||
snprintf(formattedText, sizeof(formattedText), "%d", inventoryWeight);
|
||||
}
|
||||
|
||||
int width = fontGetStringWidth(formattedText);
|
||||
int x = inventoryLootLayout.leftScrollerX + inventoryLootLayout.scrollerWidth / 2 - width / 2;
|
||||
int y = inventoryLootLayout.leftScrollerY + inventoryLootLayout.scrollerHeight + 2;
|
||||
fontDrawText(windowBuffer + pitch * y + x, formattedText, width, pitch, color);
|
||||
inventoryDrawCenteredText(windowBuffer, pitch, inventoryLootLayout.scrollerWidth, inventoryLootLayout.leftScrollerX, y, formattedText, color);
|
||||
|
||||
fontSetCurrent(oldFont);
|
||||
}
|
||||
@@ -1987,10 +1997,8 @@ static void _display_target_inventory(int stackOffset, int dragSlotIndex, Invent
|
||||
snprintf(formattedText, sizeof(formattedText), "%d", inventoryWeight);
|
||||
}
|
||||
|
||||
int width = fontGetStringWidth(formattedText);
|
||||
int x = inventoryLootLayout.rightScrollerX + inventoryLootLayout.scrollerWidth / 2 - width / 2;
|
||||
int y = inventoryLootLayout.rightScrollerY + inventoryLootLayout.scrollerHeight + 2;
|
||||
fontDrawText(windowBuffer + pitch * y + x, formattedText, width, pitch, color);
|
||||
inventoryDrawCenteredText(windowBuffer, pitch, inventoryLootLayout.scrollerWidth, inventoryLootLayout.rightScrollerX, y, formattedText, color);
|
||||
|
||||
fontSetCurrent(oldFont);
|
||||
}
|
||||
@@ -2113,8 +2121,8 @@ static void _display_body(int fid, int inventoryWindowType)
|
||||
rect.left = 560;
|
||||
rect.top = 25;
|
||||
} else {
|
||||
rect.left = 15;
|
||||
rect.top = 25;
|
||||
rect.left = INVENTORY_BARTER_LEFT_BODY_VIEW_X;
|
||||
rect.top = INVENTORY_BARTER_LEFT_BODY_VIEW_Y;
|
||||
}
|
||||
|
||||
rect.right = rect.left + INVENTORY_BODY_VIEW_WIDTH - 1;
|
||||
@@ -2135,6 +2143,8 @@ static void _display_body(int fid, int inventoryWindowType)
|
||||
windowBuffer + windowPitch * (rect.top + (INVENTORY_BODY_VIEW_HEIGHT - frameHeight) / 2) + (INVENTORY_BODY_VIEW_WIDTH - frameWidth) / 2 + rect.left,
|
||||
windowPitch);
|
||||
|
||||
inventoryDisplayLeftPaneCompanionName(windowBuffer, windowPitch, rect, index);
|
||||
|
||||
win = gInventoryBarterBackgroundWindow;
|
||||
} else {
|
||||
unsigned char* windowBuffer = windowGetBuffer(gInventoryWindow);
|
||||
@@ -2189,19 +2199,8 @@ static void _display_body(int fid, int inventoryWindowType)
|
||||
windowBuffer + windowPitch * (rect.top + (INVENTORY_BODY_VIEW_HEIGHT - frameHeight) / 2) + (INVENTORY_BODY_VIEW_WIDTH - frameWidth) / 2 + rect.left,
|
||||
windowPitch);
|
||||
|
||||
// Draw party member name at bottom of left avatar in loot mode.
|
||||
if (index == 0 && inventoryWindowType == INVENTORY_WINDOW_TYPE_LOOT && _stack[0] != gDude) {
|
||||
const char* name = critterGetName(_stack[0]);
|
||||
if (name != nullptr) {
|
||||
// TODO: clean up text rendering
|
||||
int oldFont = fontGetCurrent();
|
||||
fontSetCurrent(101);
|
||||
int nameWidth = std::min(fontGetStringWidth(name), INVENTORY_BODY_VIEW_WIDTH);
|
||||
int nameX = rect.left + INVENTORY_BODY_VIEW_WIDTH / 2 - nameWidth / 2;
|
||||
int nameY = rect.bottom - fontGetLineHeight() - 1;
|
||||
fontDrawText(windowBuffer + windowPitch * nameY + nameX, name, INVENTORY_BODY_VIEW_WIDTH, windowPitch, _colorTable[992]);
|
||||
fontSetCurrent(oldFont);
|
||||
}
|
||||
if (inventoryWindowType == INVENTORY_WINDOW_TYPE_LOOT) {
|
||||
inventoryDisplayLeftPaneCompanionName(windowBuffer, windowPitch, rect, index);
|
||||
}
|
||||
|
||||
win = gInventoryWindow;
|
||||
@@ -2703,7 +2702,7 @@ void adjustCritterStatsOnArmorChange(Object* critter, Object* oldArmor, Object*
|
||||
// 0x4716E8
|
||||
static void _adjust_fid()
|
||||
{
|
||||
gInventoryWindowDudeFid = inventoryComputeCritterFid(_inven_dude,
|
||||
int fid = inventoryComputeCritterFid(_inven_dude,
|
||||
_inven_pid,
|
||||
gInventoryRightHandItem,
|
||||
gInventoryLeftHandItem,
|
||||
@@ -2711,6 +2710,7 @@ static void _adjust_fid()
|
||||
interfaceGetCurrentHand(),
|
||||
0,
|
||||
0);
|
||||
gInventoryWindowDudeFid = scriptHooks_AdjustFid(fid, fid);
|
||||
}
|
||||
|
||||
// 0x4717E4
|
||||
@@ -2988,12 +2988,14 @@ static void inventoryRestoreEquippedFromGlobals(Object* critter)
|
||||
gInventoryArmor = nullptr;
|
||||
}
|
||||
|
||||
static void inventorySetLootLeftPaneCritter(Object* critter, Object* target)
|
||||
static void inventorySetLeftPaneCritter(Object* critter, Object* target, int inventoryWindowType)
|
||||
{
|
||||
assert(critter != nullptr);
|
||||
assert(target != nullptr);
|
||||
|
||||
inventoryRestoreEquippedFromGlobals(_inven_dude);
|
||||
// _inven_dude can point at a nested container while browsing bags, but
|
||||
// equipped items always need to be restored onto the owning critter.
|
||||
inventoryRestoreEquippedFromGlobals(_stack[0]);
|
||||
inventoryStripEquippedToGlobals(critter);
|
||||
_inven_dude = critter;
|
||||
_curr_stack = 0;
|
||||
@@ -3014,8 +3016,8 @@ static void inventorySetLootLeftPaneCritter(Object* critter, Object* target)
|
||||
|
||||
gInventoryWindowDudeFid = buildFid(OBJ_TYPE_CRITTER, critter->fid & 0xFFF, 0, animationCode, 0);
|
||||
gInventoryWindowDudeRotationTimestamp = 0;
|
||||
_display_inventory(0, -1, INVENTORY_WINDOW_TYPE_LOOT);
|
||||
_display_body(target->fid, INVENTORY_WINDOW_TYPE_LOOT);
|
||||
_display_inventory(0, -1, inventoryWindowType);
|
||||
_display_body(target->fid, inventoryWindowType);
|
||||
}
|
||||
|
||||
// 0x471CA0
|
||||
@@ -3456,13 +3458,21 @@ int inventoryEquip(Object* critter, Object* item, int hand)
|
||||
// 0x472768
|
||||
int inventoryEquipFunc(Object* critter, Object* item, int handIndex, bool animate)
|
||||
{
|
||||
int itemType = itemGetType(item);
|
||||
|
||||
InvenSlot invenSlot = itemType == ITEM_TYPE_ARMOR
|
||||
? InvenSlot::Armor
|
||||
: (handIndex == HAND_RIGHT ? InvenSlot::RightHand : InvenSlot::LeftHand);
|
||||
if (!scriptHooks_InvenWield(critter, item, invenSlot, 1, 0)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (animate) {
|
||||
if (!isoIsDisabled()) {
|
||||
reg_anim_begin(ANIMATION_REQUEST_RESERVED);
|
||||
}
|
||||
}
|
||||
|
||||
int itemType = itemGetType(item);
|
||||
if (itemType == ITEM_TYPE_ARMOR) {
|
||||
Object* armor = critterGetArmor(critter);
|
||||
if (armor != nullptr) {
|
||||
@@ -3620,6 +3630,14 @@ int inventoryUnequipFunc(Object* critter, int hand, bool animate)
|
||||
item = critterGetItem1(critter);
|
||||
}
|
||||
|
||||
// Notify scripts before mutating the OBJECT_IN_ANY_HAND flag..
|
||||
if (item != nullptr) {
|
||||
InvenSlot invenSlot = hand == HAND_RIGHT ? InvenSlot::RightHand : InvenSlot::LeftHand;
|
||||
if (!scriptHooks_InvenWield(critter, item, invenSlot, 0, 0)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (item) {
|
||||
item->flags &= ~OBJECT_IN_ANY_HAND;
|
||||
}
|
||||
@@ -3736,6 +3754,39 @@ static int _inven_from_button(int keyCode, Object** outItem, Object*** outItemSl
|
||||
return quantity;
|
||||
}
|
||||
|
||||
static void inventoryDrawCenteredText(unsigned char* buffer, int pitch, int width, int x, int y, const char* text, int color)
|
||||
{
|
||||
if (text == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
int oldFont = fontGetCurrent();
|
||||
fontSetCurrent(101);
|
||||
int textWidth = std::min(fontGetStringWidth(text), width);
|
||||
int textX = x + width / 2 - textWidth / 2;
|
||||
fontDrawText(buffer + pitch * y + textX, text, width, pitch, color);
|
||||
fontSetCurrent(oldFont);
|
||||
}
|
||||
|
||||
static void inventoryDisplayLeftPaneCompanionName(unsigned char* windowBuffer, int windowPitch, const Rect& rect, int index)
|
||||
{
|
||||
if (index != 0 || _stack[0] == gDude) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char* name = critterGetName(_stack[0]);
|
||||
if (name == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
int oldFont = fontGetCurrent();
|
||||
fontSetCurrent(101);
|
||||
int nameY = rect.bottom - fontGetLineHeight() - 2;
|
||||
fontSetCurrent(oldFont);
|
||||
|
||||
inventoryDrawCenteredText(windowBuffer, windowPitch, INVENTORY_BODY_VIEW_WIDTH, rect.left, nameY, name, _colorTable[992]);
|
||||
}
|
||||
|
||||
// Displays item description.
|
||||
//
|
||||
// inven_display_msg
|
||||
@@ -4486,12 +4537,12 @@ int inventoryOpenLooting(Object* looter, Object* target)
|
||||
} else if (keyCode == KEY_ARROW_LEFT) {
|
||||
if (partyTargets.size() > 1) {
|
||||
partyTargetIndex = (partyTargetIndex > 0) ? partyTargetIndex - 1 : (int)partyTargets.size() - 1;
|
||||
inventorySetLootLeftPaneCritter(partyTargets[partyTargetIndex], target);
|
||||
inventorySetLeftPaneCritter(partyTargets[partyTargetIndex], target, INVENTORY_WINDOW_TYPE_LOOT);
|
||||
}
|
||||
} else if (keyCode == KEY_ARROW_RIGHT) {
|
||||
if (partyTargets.size() > 1) {
|
||||
partyTargetIndex = (partyTargetIndex < (int)partyTargets.size() - 1) ? partyTargetIndex + 1 : 0;
|
||||
inventorySetLootLeftPaneCritter(partyTargets[partyTargetIndex], target);
|
||||
inventorySetLeftPaneCritter(partyTargets[partyTargetIndex], target, INVENTORY_WINDOW_TYPE_LOOT);
|
||||
}
|
||||
|
||||
} else if (keyCode == KEY_ARROW_UP) {
|
||||
@@ -4571,11 +4622,12 @@ int inventoryOpenLooting(Object* looter, Object* target)
|
||||
_gStealSize += itemGetSize(_stack[_curr_stack]);
|
||||
|
||||
InventoryItem* inventoryItem = &(_pud->items[_pud->length - (slotIndex + _stack_offset[_curr_stack] + 1)]);
|
||||
InventoryMoveResult rc = _move_inventory(inventoryItem->item, slotIndex, _target_stack[_target_curr_stack], true);
|
||||
int stealXpOverride = -1;
|
||||
InventoryMoveResult rc = _move_inventory(inventoryItem->item, slotIndex, _target_stack[_target_curr_stack], true, &stealXpOverride);
|
||||
if (rc == INVENTORY_MOVE_RESULT_CAUGHT_STEALING) {
|
||||
isCaughtStealing = true;
|
||||
} else if (rc == INVENTORY_MOVE_RESULT_SUCCESS) {
|
||||
stealingXp += stealingXpBonus;
|
||||
stealingXp += stealXpOverride >= 0 ? stealXpOverride : stealingXpBonus;
|
||||
stealingXpBonus += 10;
|
||||
}
|
||||
|
||||
@@ -4595,11 +4647,12 @@ int inventoryOpenLooting(Object* looter, Object* target)
|
||||
_gStealSize += itemGetSize(_stack[_curr_stack]);
|
||||
|
||||
InventoryItem* inventoryItem = &(_target_pud->items[_target_pud->length - (slotIndex + _target_stack_offset[_target_curr_stack] + 1)]);
|
||||
InventoryMoveResult rc = _move_inventory(inventoryItem->item, slotIndex, _target_stack[_target_curr_stack], false);
|
||||
int stealXpOverride = -1;
|
||||
InventoryMoveResult rc = _move_inventory(inventoryItem->item, slotIndex, _target_stack[_target_curr_stack], false, &stealXpOverride);
|
||||
if (rc == INVENTORY_MOVE_RESULT_CAUGHT_STEALING) {
|
||||
isCaughtStealing = true;
|
||||
} else if (rc == INVENTORY_MOVE_RESULT_SUCCESS) {
|
||||
stealingXp += stealingXpBonus;
|
||||
stealingXp += stealXpOverride >= 0 ? stealXpOverride : stealingXpBonus;
|
||||
stealingXpBonus += 10;
|
||||
}
|
||||
|
||||
@@ -4713,8 +4766,11 @@ int inventoryOpenStealing(Object* thief, Object* target)
|
||||
|
||||
// 0x474708
|
||||
// note: this is looting and stealing, not the inventory screen
|
||||
static InventoryMoveResult _move_inventory(Object* item, int slotIndex, Object* targetObj, bool isPlanting)
|
||||
static InventoryMoveResult _move_inventory(Object* item, int slotIndex, Object* targetObj, bool isPlanting, int* stealXpOverridePtr)
|
||||
{
|
||||
assert(stealXpOverridePtr != nullptr);
|
||||
*stealXpOverridePtr = -1;
|
||||
|
||||
bool needRefresh = true;
|
||||
|
||||
Rect rect;
|
||||
@@ -4776,13 +4832,17 @@ static InventoryMoveResult _move_inventory(Object* item, int slotIndex, Object*
|
||||
}
|
||||
|
||||
if (quantityToMove != -1) {
|
||||
bool skipMove = false;
|
||||
if (_gIsSteal && _inven_dude == gDude) {
|
||||
if (skillsPerformStealing(_inven_dude, targetObj, item, true) == 0) {
|
||||
SkillStealResult stealResult = skillsPerformStealing(_inven_dude, targetObj, item, quantityToMove, true, stealXpOverridePtr);
|
||||
if (stealResult == SkillStealResult::caught) {
|
||||
result = INVENTORY_MOVE_RESULT_CAUGHT_STEALING;
|
||||
} else if (stealResult == SkillStealResult::fail) {
|
||||
skipMove = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (result != INVENTORY_MOVE_RESULT_CAUGHT_STEALING) {
|
||||
if (!skipMove && result != INVENTORY_MOVE_RESULT_CAUGHT_STEALING) {
|
||||
if (itemMove(_inven_dude, targetObj, item, quantityToMove) != -1) {
|
||||
result = INVENTORY_MOVE_RESULT_SUCCESS;
|
||||
} else {
|
||||
@@ -4803,13 +4863,17 @@ static InventoryMoveResult _move_inventory(Object* item, int slotIndex, Object*
|
||||
}
|
||||
|
||||
if (quantityToMove != -1) {
|
||||
bool skipMove = false;
|
||||
if (_gIsSteal && _inven_dude == gDude) {
|
||||
if (skillsPerformStealing(_inven_dude, targetObj, item, false) == 0) {
|
||||
SkillStealResult stealResult = skillsPerformStealing(_inven_dude, targetObj, item, quantityToMove, false, stealXpOverridePtr);
|
||||
if (stealResult == SkillStealResult::caught) {
|
||||
result = INVENTORY_MOVE_RESULT_CAUGHT_STEALING;
|
||||
} else if (stealResult == SkillStealResult::fail) {
|
||||
skipMove = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (result != INVENTORY_MOVE_RESULT_CAUGHT_STEALING) {
|
||||
if (!skipMove && result != INVENTORY_MOVE_RESULT_CAUGHT_STEALING) {
|
||||
if (itemMove(targetObj, _inven_dude, item, quantityToMove) == 0) {
|
||||
if ((item->flags & OBJECT_IN_RIGHT_HAND) != 0) {
|
||||
targetObj->fid = buildFid(FID_TYPE(targetObj->fid), targetObj->fid & 0xFFF, FID_ANIM_TYPE(targetObj->fid), 0, targetObj->rotation + 1);
|
||||
@@ -5294,7 +5358,21 @@ void barterProcessUI(int win, Object* barterer, Object* playerTable, Object* bar
|
||||
_target_stack[0] = barterer;
|
||||
_target_stack_offset[0] = 0;
|
||||
|
||||
Object* const playerObj = _inven_dude;
|
||||
int savedDudeFid = gInventoryWindowDudeFid;
|
||||
|
||||
std::vector<Object*> partyTargets = { _inven_dude };
|
||||
if (settings.qol.party_loot_and_barter) {
|
||||
for (Object* pm : get_all_party_members_objects(false)) {
|
||||
if (pm != gDude && pm != barterer) {
|
||||
partyTargets.push_back(pm);
|
||||
}
|
||||
}
|
||||
}
|
||||
int partyTargetIndex = 0;
|
||||
|
||||
bool isoWasEnabled = _setup_inventory(INVENTORY_WINDOW_TYPE_TRADE);
|
||||
|
||||
_display_target_inventory(_target_stack_offset[_target_curr_stack], -1, _target_pud, INVENTORY_WINDOW_TYPE_TRADE);
|
||||
_display_inventory(_stack_offset[0], -1, INVENTORY_WINDOW_TYPE_TRADE);
|
||||
_display_body(barterer->fid, INVENTORY_WINDOW_TYPE_TRADE);
|
||||
@@ -5403,6 +5481,16 @@ void barterProcessUI(int win, Object* barterer, Object* playerTable, Object* bar
|
||||
_display_target_inventory(_target_stack_offset[_target_curr_stack], -1, _target_pud, INVENTORY_WINDOW_TYPE_TRADE);
|
||||
windowRefresh(gInventoryWindow);
|
||||
}
|
||||
} else if (keyCode == KEY_ARROW_LEFT) {
|
||||
if (partyTargets.size() > 1) {
|
||||
partyTargetIndex = (partyTargetIndex > 0) ? partyTargetIndex - 1 : (int)partyTargets.size() - 1;
|
||||
inventorySetLeftPaneCritter(partyTargets[partyTargetIndex], barterer, INVENTORY_WINDOW_TYPE_TRADE);
|
||||
}
|
||||
} else if (keyCode == KEY_ARROW_RIGHT) {
|
||||
if (partyTargets.size() > 1) {
|
||||
partyTargetIndex = (partyTargetIndex < (int)partyTargets.size() - 1) ? partyTargetIndex + 1 : 0;
|
||||
inventorySetLeftPaneCritter(partyTargets[partyTargetIndex], barterer, INVENTORY_WINDOW_TYPE_TRADE);
|
||||
}
|
||||
} else if (keyCode >= 2500 && keyCode <= 2501) {
|
||||
_container_exit(keyCode, INVENTORY_WINDOW_TYPE_TRADE);
|
||||
} else {
|
||||
@@ -5572,7 +5660,9 @@ void barterProcessUI(int win, Object* barterer, Object* playerTable, Object* bar
|
||||
itemAdd(barterer, item1, 1);
|
||||
}
|
||||
|
||||
gInventoryWindowDudeFid = savedDudeFid;
|
||||
_exit_inventory(isoWasEnabled);
|
||||
_inven_dude = playerObj;
|
||||
|
||||
// NOTE: Uninline.
|
||||
inventoryCommonFree();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user