Compare commits

..
Author SHA1 Message Date
github-actions[bot] 07939225c6 chore: auto-format with clang-format 2026-06-23 05:27:36 +00:00
Mike Klaas b10526330c SFALL_COMPAT 2026-06-22 22:26:13 -07:00
Mike Klaas 4c1cad7122 scriptWindow opcodes 2026-06-22 22:26:02 -07:00
Mike Klaas b76ff8257e fix item_weight 2026-06-22 22:24:46 -07:00
Mike Klaas 6edbd70d60 Let FrmImage reference single-frames from multi-frame frms, and support pcx 2026-06-22 22:04:33 -07:00
Mike Klaas 15917dccb3 inventory_refresh, display_stats 2026-06-22 20:56:19 -07:00
Mike Klaas 33aae49066 character editor display_stats 2026-06-22 20:46:55 -07:00
Mike Klaas bfd41bb40c opcode registration 2026-06-22 20:44:36 -07:00
Mike Klaas 0ccaef5ef0 get windows 2026-06-22 20:34:11 -07:00
36 changed files with 1513 additions and 1501 deletions
+4 -4
View File
@@ -144,13 +144,13 @@ In time this stuff will receive in-game interface, right now you have to do it m
* Expanded 4-row barter screen
* Expanded AP bar
* Party members can loot and barter in place of PC
* Increased pathfinding nodes 5x for more accurate pathfinding, and use accurate path length for walk vs run
* Increased pathfinding nodes 5x for more accurate pathfinding
* Ctrl-click to quickly move items when bartering, looting, or stealing
* _a_ to select "all" when selecting item quantity, and for `Take All` when looting
* When bartering, caps default to the right amount to balance the trade
* _a_ to select "all" when selecting item quantity
* _a_ to `Take All` when looting
* When bartering, caps default to the right amount to balance the trade (if possible)
* Music continues playing between maps
* Auto open doors
* Integrated "HELP" menu
* 44.1 kHz stereo sound/music supported, in .ogg and .wav format as well as legacy .acm
* Last used save slot is remembered
* Item/Corpse/Container/Critter highlighting (configure using [mods/sfall-mods.ini](https://github.com/sfall-team/sfall/blob/master/artifacts/config_files/sfall-mods.ini))
+1 -1
View File
@@ -75,7 +75,7 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
| Global scripts / Global script functions | set_global_script_repeat<br>set_global_script_type<br>available_global_script_types | âś… except available_global_script_types | - |
| Combat | attack_is_aimed<br>block_combat<br>force_aimed_shots<br>disable_aimed_shots<br>get_attack_type<br>get/set_bodypart_hit_modifier<br>combat_data<br>get/set/reset_critical_table<br>get_last_target<br>get_last_attacker<br>set_critter_burst_disable<br>get/set_critter_current_ap<br>set_spray_settings<br>get/set_combat_free_move | âś… except block_combat, force_aimed_shots, disable_aimed_shots, get_last_target, get_last_attacker, set_spray_settings | - |
| Car | set_car_current_town<br>car_gas_amount<br>set_car_intface_art | implemented: all except set_car_intface_art | - |
| Interface / Windows and images | art_frame_data<br>interface_art_draw<br>interface_print<br>draw_image<br>draw_image_scaled<br>get_window_under_mouse<br>create_win<br>get_window_attribute<br>message_box<br>set_window_flag<br>win_fill_color<br>interface_overlay<br>dialog_message<br>get_text_width<br>hide_window<br>show_window<br>create_message_window | implemented: get_window_under_mouse, create_win, get_window_attribute, message_box, set_window_flag, dialog_message, get_text_width, hide_window, show_window, create_message_window | - |
| Interface / Windows and images | art_frame_data<br>interface_art_draw<br>interface_print<br>draw_image<br>draw_image_scaled<br>get_window_under_mouse<br>create_win<br>get_window_attribute<br>message_box<br>set_window_flag<br>win_fill_color<br>interface_overlay<br>dialog_message<br>get_text_width<br>hide_window<br>show_window<br>create_message_window | âś… except interface_print, draw_image, draw_image_scaled, interface_overlay | - |
| Interface / Outline | outlined_object<br>get_outline<br>set_outline | âś… | - |
| Interface / Main interface | intface_is_hidden<br>intface_redraw<br>intface_hide<br>intface_show<br>set_quest_failure_value | implemented: only intface_redraw | `intface_redraw` only supports the zero-argument form; the optional-argument path is explicitly unimplemented. |
| Interface / Inventory | display_stats<br>inventory_redraw<br>critter_inven_obj2<br>get_current_inven_size<br>item_weight | âś… except get_current_inven_size | - |
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

-1
View File
@@ -1 +0,0 @@
{0}{}{HELP}
-1
View File
@@ -1 +0,0 @@
{0}{}{AIDE}
-1
View File
@@ -1 +0,0 @@
{0}{}{HILFE}
-1
View File
@@ -1 +0,0 @@
{0}{}{AIUTO}
-1
View File
@@ -1 +0,0 @@
{0}{}{POMOC}
-1
View File
@@ -1 +0,0 @@
{0}{}{AJUDA}
-1
View File
@@ -1 +0,0 @@
{0}{}{ПОМОЩЬ}
-1
View File
@@ -1 +0,0 @@
{0}{}{AYUDA}
+1 -10
View File
@@ -39,16 +39,7 @@ IFACE_BAR_SIDES_ORI=0
;if IFACE_BAR_WIDTH=800 - Interface bar will use 800pix wide asset from f2_res.dat. Note
IFACE_BAR_WIDTH=640
; Controls ammo display in the HP/AC cluster.
; 0 = stock 1-pixel ammo bar only.
; 1 = numeric current/max ammo counter only.
; 2 = HRP 2-pixel ammo bar only.
; 3 = hidden, no stock bar and no numeric counter.
; 4 = stock 1-pixel ammo bar plus numeric counter.
; 5 = HRP 2-pixel ammo bar plus numeric counter.
IFACE_AMMO_COUNTER=0
;##################################################################################################################
[STATIC_SCREENS]
-2
View File
@@ -130,8 +130,6 @@ inventory_columns=2
;Set to 1 to scale only the main menu background image to fit the screen while maintaining aspect ratio.
;Set to 2 to also scale the main menu button panel, red buttons, and button labels with the menu art.
main_menu_scale_mode=1
;Set to 1 to show a Help option in the in-game options menu which opens the F1 help screen.
in_game_menu_help=1
[qol]
+141 -79
View File
@@ -10,6 +10,7 @@
#include "animation.h"
#include "content_config.h"
#include "datafile.h"
#include "debug.h"
#include "draw.h"
#include "game.h"
@@ -524,34 +525,6 @@ unsigned char* artLockFrameData(int fid, int frame, int direction, CacheEntry**
return nullptr;
}
// 0x4191CC
unsigned char* artLockFrameDataReturningSize(int fid, CacheEntry** handlePtr, int* widthPtr, int* heightPtr)
{
*handlePtr = nullptr;
Art* art = nullptr;
cacheLock(&gArtCache, fid, (void**)&art, handlePtr);
if (art == nullptr) {
return nullptr;
}
// NOTE: Uninline.
*widthPtr = artGetWidth(art, 0, 0);
if (*widthPtr == -1) {
return nullptr;
}
// NOTE: Uninline.
*heightPtr = artGetHeight(art, 0, 0);
if (*heightPtr == -1) {
return nullptr;
}
// NOTE: Uninline.
return artGetFrameData(art, 0, 0);
}
// 0x419260
int artUnlock(CacheEntry* handle)
{
@@ -1221,6 +1194,51 @@ static bool artIndexedPngHasSupportedTransparency(const LodePNGColorMode& color)
return true;
}
static Art* artAllocateSingleFrame(int width, int height, unsigned char** frameDataPtr)
{
if (width <= 0 || height <= 0 || width > SHRT_MAX || height > SHRT_MAX) {
return nullptr;
}
size_t pixelCount = static_cast<size_t>(width) * static_cast<size_t>(height);
if (pixelCount > kMaxNamedPngPixels || pixelCount > INT_MAX) {
return nullptr;
}
Art header = {};
header.version = 4;
header.framesPerSecond = 10;
header.actionFrame = 0;
header.frameCount = 1;
header.dataSize = sizeof(ArtFrame) + static_cast<int>(pixelCount);
int currentPadding = paddingForSize(sizeof(Art));
for (int rotation = 0; rotation < ROTATION_COUNT; rotation++) {
header.dataOffsets[rotation] = 0;
header.padding[rotation] = currentPadding;
}
int dataSize = artGetDataSize(&header);
unsigned char* data = reinterpret_cast<unsigned char*>(internal_malloc(dataSize));
if (data == nullptr) {
return nullptr;
}
memset(data, 0, dataSize);
Art* art = reinterpret_cast<Art*>(data);
*art = header;
ArtFrame* frame = reinterpret_cast<ArtFrame*>(data + sizeof(Art) + art->padding[0]);
frame->width = static_cast<short>(width);
frame->height = static_cast<short>(height);
frame->size = static_cast<int>(pixelCount);
frame->x = 0;
frame->y = 0;
*frameDataPtr = reinterpret_cast<unsigned char*>(frame) + sizeof(ArtFrame);
return art;
}
static Art* artLoadIndexedPng(const char* path)
{
std::vector<unsigned char> encoded;
@@ -1255,47 +1273,44 @@ static Art* artLoadIndexedPng(const char* path)
return nullptr;
}
size_t pixelCount = static_cast<size_t>(width) * static_cast<size_t>(height);
Art header = {};
header.version = 4;
header.framesPerSecond = 10;
header.actionFrame = 0;
header.frameCount = 1;
header.dataSize = sizeof(ArtFrame) + static_cast<int>(pixelCount);
int currentPadding = paddingForSize(sizeof(Art));
for (int rotation = 0; rotation < ROTATION_COUNT; rotation++) {
header.dataOffsets[rotation] = 0;
header.padding[rotation] = currentPadding;
}
int dataSize = artGetDataSize(&header);
unsigned char* data = reinterpret_cast<unsigned char*>(internal_malloc(dataSize));
if (data == nullptr) {
unsigned char* frameData = nullptr;
Art* art = artAllocateSingleFrame(static_cast<int>(width), static_cast<int>(height), &frameData);
if (art == nullptr) {
return nullptr;
}
memset(data, 0, dataSize);
Art* art = reinterpret_cast<Art*>(data);
*art = header;
ArtFrame* frame = reinterpret_cast<ArtFrame*>(data + sizeof(Art) + art->padding[0]);
frame->width = static_cast<short>(width);
frame->height = static_cast<short>(height);
frame->size = static_cast<int>(pixelCount);
frame->x = 0;
frame->y = 0;
if (!artUnpackIndexedPngPixels(indexedData, width, height, state.info_png.color.bitdepth, reinterpret_cast<unsigned char*>(frame) + sizeof(ArtFrame))) {
if (!artUnpackIndexedPngPixels(indexedData, width, height, state.info_png.color.bitdepth, frameData)) {
debugPrint("ART: failed to read indexed PNG pixels: %s\n", path);
internal_free(data);
internal_free(art);
return nullptr;
}
return art;
}
static Art* artLoadPcx(const char* path)
{
char mutablePath[COMPAT_MAX_PATH];
strncpy(mutablePath, path, sizeof(mutablePath));
mutablePath[sizeof(mutablePath) - 1] = '\0';
int width = 0;
int height = 0;
unsigned char* pcxData = datafileRead(mutablePath, &width, &height);
if (pcxData == nullptr) {
return nullptr;
}
unsigned char* frameData = nullptr;
Art* art = artAllocateSingleFrame(width, height, &frameData);
if (art != nullptr) {
memcpy(frameData, pcxData, static_cast<size_t>(width) * static_cast<size_t>(height));
}
internal_free(pcxData);
return art;
}
static Art* artLoadFrm(const char* path)
{
File* stream = fileOpen(path, "rb");
@@ -1340,6 +1355,10 @@ Art* artLoad(const char* path)
return artLoadIndexedPng(path);
}
if (artPathHasExtension(path, ".pcx")) {
return artLoadPcx(path);
}
Art* art = artLoadFrm(path);
if (art != nullptr) {
return art;
@@ -1491,7 +1510,7 @@ public:
const Art* art() const { return _art.get(); }
unsigned char* frameData(int frame, int direction, int& outWidth, int& outHeight) const;
ArtFrame* frame(int frame, int direction) const;
unsigned int mru = 0;
@@ -1504,17 +1523,9 @@ NamedCacheEntry::NamedCacheEntry(ArtPtr&& art)
{
}
unsigned char* NamedCacheEntry::frameData(int frame, int direction, int& outWidth, int& outHeight) const
ArtFrame* NamedCacheEntry::frame(int frame, int direction) const
{
unsigned char* data = artGetFrameData(_art.get(), frame, direction);
if (!data) {
outWidth = 0;
outHeight = 0;
return nullptr;
}
outWidth = artGetWidth(_art.get(), frame, direction);
outHeight = artGetHeight(_art.get(), frame, direction);
return data;
return artGetFrame(_art.get(), frame, direction);
}
std::shared_ptr<NamedCacheEntry> artLockNamedFrameData(const char* path)
@@ -1597,9 +1608,12 @@ ObjectType FrmId::objectType() const
FrmImage::FrmImage()
{
_key = nullptr;
_frame = nullptr;
_data = nullptr;
_width = 0;
_height = 0;
_xOffset = 0;
_yOffset = 0;
}
FrmImage::~FrmImage()
@@ -1610,9 +1624,12 @@ FrmImage::~FrmImage()
FrmImage::FrmImage(FrmImage&& other) noexcept
: _namedKey(std::move(other._namedKey))
, _key(other._key)
, _frame(other._frame)
, _data(other._data)
, _width(other._width)
, _height(other._height)
, _xOffset(other._xOffset)
, _yOffset(other._yOffset)
{
other.resetInternal();
}
@@ -1623,9 +1640,12 @@ FrmImage& FrmImage::operator=(FrmImage&& other) noexcept
unlock();
_namedKey = std::move(other._namedKey);
_key = other._key;
_frame = other._frame;
_data = other._data;
_width = other._width;
_height = other._height;
_xOffset = other._xOffset;
_yOffset = other._yOffset;
other.resetInternal();
}
@@ -1633,33 +1653,60 @@ FrmImage& FrmImage::operator=(FrmImage&& other) noexcept
}
bool FrmImage::lock(const FrmId& frmId)
{
return lock(frmId, 0, 0);
}
bool FrmImage::lock(const FrmId& frmId, int frame, int direction)
{
if (frmId.fid() >= 0) {
return lock(frmId.fid());
return lock(frmId.fid(), frame, direction);
}
if (frmId.filePath() != nullptr) {
return frmId.hasObjectType()
? lock(frmId.objectType(), frmId.filePath())
: lock(frmId.filePath());
? lock(frmId.objectType(), frmId.filePath(), frame, direction)
: lock(frmId.filePath(), frame, direction);
}
return false;
}
bool FrmImage::lock(unsigned int fid)
{
return lock(fid, 0, 0);
}
bool FrmImage::lock(unsigned int fid, int frame, int direction)
{
if (isLocked()) {
return false;
}
_data = artLockFrameDataReturningSize(fid, &_key, &_width, &_height);
if (!_data) {
Art* art = artLock(fid, &_key);
if (art == nullptr) {
return false;
}
_frame = artGetFrame(art, frame, direction);
if (_frame == nullptr) {
unlock();
return false;
}
_data = reinterpret_cast<unsigned char*>(_frame) + sizeof(ArtFrame);
_width = _frame->width;
_height = _frame->height;
_xOffset = _frame->x;
_yOffset = _frame->y;
return true;
}
bool FrmImage::lock(const char* frmPath)
{
return lock(frmPath, 0, 0);
}
bool FrmImage::lock(const char* frmPath, int frame, int direction)
{
if (isLocked()) {
return false;
@@ -1668,21 +1715,33 @@ bool FrmImage::lock(const char* frmPath)
_namedKey = artLockNamedFrameData(frmPath);
if (!_namedKey) return false;
_data = _namedKey->frameData(0, 0, _width, _height);
if (_data == nullptr) {
_namedKey = nullptr;
_frame = _namedKey->frame(frame, direction);
if (_frame == nullptr) {
unlock();
return false;
}
_data = reinterpret_cast<unsigned char*>(_frame) + sizeof(ArtFrame);
_width = _frame->width;
_height = _frame->height;
_xOffset = _frame->x;
_yOffset = _frame->y;
return true;
}
bool FrmImage::lock(ObjectType objType, const char* frmRelativePath)
{
return lock(objType, frmRelativePath, 0, 0);
}
bool FrmImage::lock(ObjectType objType, const char* frmRelativePath, int frame, int direction)
{
if (objType < OBJ_TYPE_ITEM || objType >= OBJ_TYPE_COUNT) {
return false;
}
snprintf(_art_name, sizeof(_art_name), "%s%s%s\\%s", _cd_path_base, "art\\", gArtListDescriptions[objType].name, frmRelativePath);
return lock(_art_name);
return lock(_art_name, frame, direction);
}
void FrmImage::unlock()
@@ -1697,9 +1756,12 @@ void FrmImage::resetInternal()
{
_namedKey = nullptr;
_key = nullptr;
_frame = nullptr;
_data = nullptr;
_width = 0;
_height = 0;
_xOffset = 0;
_yOffset = 0;
}
} // namespace fallout
+11 -3
View File
@@ -128,7 +128,6 @@ void artRender(int fid, unsigned char* dest, int width, int height, int pitch);
int art_list_str(int fid, char* name);
Art* artLock(int fid, CacheEntry** cache_entry);
unsigned char* artLockFrameData(int fid, int frame, int direction, CacheEntry** out_cache_entry);
unsigned char* artLockFrameDataReturningSize(int fid, CacheEntry** out_cache_entry, int* widthPtr, int* heightPtr);
int artUnlock(CacheEntry* cache_entry);
int artCacheFlush();
int artCopyFileName(int objectType, int id, char* dest);
@@ -185,8 +184,8 @@ private:
const char* _path = nullptr;
};
// A helper for using RAII to read single-frame FRM's.
// lock/unlock use the art-cache instead of just loading/unloading directly.
// RAII helper for locking one selected frame from FID-backed or path-backed art.
// lock/unlock use caches instead of just loading/unloading directly.
class FrmImage {
public:
FrmImage();
@@ -201,13 +200,19 @@ public:
bool isLocked() const { return _key != nullptr || _namedKey; }
bool lock(const FrmId& frmId);
bool lock(const FrmId& frmId, int frame, int direction);
bool lock(unsigned int fid);
bool lock(unsigned int fid, int frame, int direction);
bool lock(const char* frmPath);
bool lock(const char* frmPath, int frame, int direction);
bool lock(ObjectType objType, const char* frmRelativePath);
bool lock(ObjectType objType, const char* frmRelativePath, int frame, int direction);
void unlock();
int getWidth() const { return _width; }
int getHeight() const { return _height; }
int getXOffset() const { return _xOffset; }
int getYOffset() const { return _yOffset; }
// Returns FRM frame data if locked, nullptr otherwise.
unsigned char* getData() const { return _data; }
@@ -218,9 +223,12 @@ private:
std::shared_ptr<NamedCacheEntry> _namedKey;
CacheEntry* _key = nullptr;
ArtFrame* _frame = nullptr;
unsigned char* _data = nullptr;
int _width = 0;
int _height = 0;
int _xOffset = 0;
int _yOffset = 0;
};
} // namespace fallout
+10 -13
View File
@@ -663,7 +663,7 @@ static int gCharacterEditorPrimaryStatMinusBtns[7];
static unsigned char* gCharacterEditorWindowBuffer;
// 0x57060C edit_win
static int gCharacterEditorWindow = -1;
static int gCharacterEditorWindow;
// + stats buttons
//
@@ -1436,27 +1436,24 @@ static int characterEditorWindowInit()
str = getmsg(&gCharacterEditorMessageList, &gCharacterEditorMessageListItem, 111);
strcpy(kills, str);
int tabLabelY = 5;
int inactiveTabLabelY = tabLabelY + 1;
// perks selected
len = fontGetStringWidth(perks);
fontDrawText(
gCharacterEditorFrmCopy[46] + tabLabelY * _editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth() + 61 - len / 2,
gCharacterEditorFrmCopy[46] + 5 * _editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth() + 61 - len / 2,
perks,
_editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth(),
_editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth(),
_colorTable[18979]);
len = fontGetStringWidth(karma);
fontDrawText(gCharacterEditorFrmCopy[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED] + inactiveTabLabelY * _editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth() + 159 - len / 2,
fontDrawText(gCharacterEditorFrmCopy[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED] + 5 * _editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth() + 159 - len / 2,
karma,
_editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth(),
_editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth(),
_colorTable[14723]);
len = fontGetStringWidth(kills);
fontDrawText(gCharacterEditorFrmCopy[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED] + inactiveTabLabelY * _editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth() + 257 - len / 2,
fontDrawText(gCharacterEditorFrmCopy[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED] + 5 * _editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth() + 257 - len / 2,
kills,
_editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth(),
_editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth(),
@@ -1464,21 +1461,21 @@ static int characterEditorWindowInit()
// karma selected
len = fontGetStringWidth(perks);
fontDrawText(gCharacterEditorFrmCopy[EDITOR_GRAPHIC_KARMA_FOLDER_SELECTED] + inactiveTabLabelY * _editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth() + 61 - len / 2,
fontDrawText(gCharacterEditorFrmCopy[EDITOR_GRAPHIC_KARMA_FOLDER_SELECTED] + 5 * _editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth() + 61 - len / 2,
perks,
_editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth(),
_editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth(),
_colorTable[14723]);
len = fontGetStringWidth(karma);
fontDrawText(gCharacterEditorFrmCopy[EDITOR_GRAPHIC_KARMA_FOLDER_SELECTED] + tabLabelY * _editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth() + 159 - len / 2,
fontDrawText(gCharacterEditorFrmCopy[EDITOR_GRAPHIC_KARMA_FOLDER_SELECTED] + 5 * _editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth() + 159 - len / 2,
karma,
_editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth(),
_editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth(),
_colorTable[18979]);
len = fontGetStringWidth(kills);
fontDrawText(gCharacterEditorFrmCopy[EDITOR_GRAPHIC_KARMA_FOLDER_SELECTED] + inactiveTabLabelY * _editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth() + 257 - len / 2,
fontDrawText(gCharacterEditorFrmCopy[EDITOR_GRAPHIC_KARMA_FOLDER_SELECTED] + 5 * _editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth() + 257 - len / 2,
kills,
_editorFrmImages[46].getWidth(),
_editorFrmImages[46].getWidth(),
@@ -1486,21 +1483,21 @@ static int characterEditorWindowInit()
// kills selected
len = fontGetStringWidth(perks);
fontDrawText(gCharacterEditorFrmCopy[EDITOR_GRAPHIC_KILLS_FOLDER_SELECTED] + inactiveTabLabelY * _editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth() + 61 - len / 2,
fontDrawText(gCharacterEditorFrmCopy[EDITOR_GRAPHIC_KILLS_FOLDER_SELECTED] + 5 * _editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth() + 61 - len / 2,
perks,
_editorFrmImages[46].getWidth(),
_editorFrmImages[46].getWidth(),
_colorTable[14723]);
len = fontGetStringWidth(karma);
fontDrawText(gCharacterEditorFrmCopy[EDITOR_GRAPHIC_KILLS_FOLDER_SELECTED] + inactiveTabLabelY * _editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth() + 159 - len / 2,
fontDrawText(gCharacterEditorFrmCopy[EDITOR_GRAPHIC_KILLS_FOLDER_SELECTED] + 5 * _editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth() + 159 - len / 2,
karma,
_editorFrmImages[46].getWidth(),
_editorFrmImages[46].getWidth(),
_colorTable[14723]);
len = fontGetStringWidth(kills);
fontDrawText(gCharacterEditorFrmCopy[EDITOR_GRAPHIC_KILLS_FOLDER_SELECTED] + tabLabelY * _editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth() + 257 - len / 2,
fontDrawText(gCharacterEditorFrmCopy[EDITOR_GRAPHIC_KILLS_FOLDER_SELECTED] + 5 * _editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth() + 257 - len / 2,
kills,
_editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth(),
_editorFrmImages[EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED].getWidth(),
+64 -53
View File
@@ -3537,7 +3537,7 @@ void attackInit(Attack* attack, Object* attacker, Object* defender, int hitMode,
attack->defenderFlags = 0;
attack->defenderKnockback = 0;
attack->extrasLength = 0;
attack->intendedTarget = defender;
attack->oops = defender;
}
// 0x422F3C
@@ -4336,7 +4336,7 @@ static int attackComputeCriticalFailure(Attack* attack)
int rounds = attackType == ATTACK_TYPE_RANGED ? attack->ammoQuantity : 1;
attackComputeDamage(attack, rounds, 2);
} else {
attack->defender = attack->intendedTarget;
attack->defender = attack->oops;
}
if (attack->defender != nullptr) {
@@ -4758,28 +4758,31 @@ void _apply_damage(Attack* attack, bool animated)
{
Object* attacker = attack->attacker;
bool attackerIsCritter = attacker != nullptr && FID_TYPE(attacker->fid) == OBJ_TYPE_CRITTER;
bool hitUnintendedTarget = attack->defender != attack->intendedTarget;
bool v5 = attack->defender != attack->oops;
if (attackerIsCritter && (attacker->data.critter.combat.results & DAM_DEAD) == 0) {
_set_new_results(attacker, attack->attackerFlags);
_damage_object(attacker, attack->attackerDamage, animated, hitUnintendedTarget, attacker);
// TODO: Not sure about "attack->defender == attack->oops".
_damage_object(attacker, attack->attackerDamage, animated, attack->defender == attack->oops, attacker);
}
// bystanders might flee
if (attack->intendedTarget != nullptr && hitUnintendedTarget) {
_combatai_notify_onlookers(attack->intendedTarget);
Object* v7 = attack->oops;
if (v7 != nullptr && v7 != attack->defender) {
_combatai_notify_onlookers(v7);
}
Object* defender = attack->defender;
bool defenderIsCritter = defender != nullptr && FID_TYPE(defender->fid) == OBJ_TYPE_CRITTER;
if (!defenderIsCritter && !hitUnintendedTarget) {
bool shouldRunDamageProc = !objectIsPartyMember(attack->defender) || !objectIsPartyMember(attack->attacker);
if (shouldRunDamageProc) {
if (defender != nullptr && defender->sid != -1) {
scriptSetFixedParam(defender->sid, attack->attackerDamage);
scriptSetObjects(defender->sid, attack->attacker, attack->weapon);
scriptExecProc(defender->sid, SCRIPT_PROC_DAMAGE);
if (!defenderIsCritter && !v5) {
bool v9 = objectIsPartyMember(attack->defender) && objectIsPartyMember(attack->attacker) ? false : true;
if (v9) {
if (defender != nullptr) {
if (defender->sid != -1) {
scriptSetFixedParam(defender->sid, attack->attackerDamage);
scriptSetObjects(defender->sid, attack->attacker, attack->weapon);
scriptExecProc(defender->sid, SCRIPT_PROC_DAMAGE);
}
}
}
}
@@ -4787,17 +4790,24 @@ void _apply_damage(Attack* attack, bool animated)
if (defenderIsCritter && (defender->data.critter.combat.results & DAM_DEAD) == 0) {
_set_new_results(defender, attack->defenderFlags);
if ((defender->data.critter.combat.results & (DAM_DEAD | DAM_KNOCKED_OUT)) != 0) {
if (!hitUnintendedTarget || defender != gDude) {
critterSetWhoHitMe(defender, attack->attacker);
if (defenderIsCritter) {
if (defenderIsCritter) {
if ((defender->data.critter.combat.results & (DAM_DEAD | DAM_KNOCKED_OUT)) != 0) {
if (!v5 || defender != gDude) {
critterSetWhoHitMe(defender, attack->attacker);
}
} else if (defender == attack->oops || defender->data.critter.combat.team != attack->attacker->data.critter.combat.team) {
_combatai_check_retaliation(defender, attack->attacker);
}
}
} else if (attackerIsCritter && (defender == attack->intendedTarget || defender->data.critter.combat.team != attack->attacker->data.critter.combat.team)) {
_combatai_check_retaliation(defender, attack->attacker);
}
scriptSetObjects(defender->sid, attack->attacker, attack->weapon);
_damage_object(defender, attack->defenderDamage, animated, hitUnintendedTarget, attacker);
_combatai_notify_onlookers(defender);
_damage_object(defender, attack->defenderDamage, animated, attack->defender != attack->oops, attacker);
if (defenderIsCritter) {
_combatai_notify_onlookers(defender);
}
if (attack->defenderDamage >= 0 && (attack->attackerFlags & DAM_HIT) != 0) {
scriptSetObjects(attack->attacker->sid, nullptr, attack->defender);
@@ -4814,13 +4824,14 @@ void _apply_damage(Attack* attack, bool animated)
if (defenderIsCritter) {
if ((obj->data.critter.combat.results & (DAM_DEAD | DAM_KNOCKED_OUT)) != 0) {
critterSetWhoHitMe(obj, attack->attacker);
} else if (attackerIsCritter && obj->data.critter.combat.team != attack->attacker->data.critter.combat.team) {
} else if (obj->data.critter.combat.team != attack->attacker->data.critter.combat.team) {
_combatai_check_retaliation(obj, attack->attacker);
}
}
scriptSetObjects(obj->sid, attack->attacker, attack->weapon);
_damage_object(obj, attack->extrasDamage[index], animated, hitUnintendedTarget, attack->attacker);
// TODO: Not sure about defender == oops.
_damage_object(obj, attack->extrasDamage[index], animated, attack->defender == attack->oops, attack->attacker);
_combatai_notify_onlookers(obj);
if (attack->extrasDamage[index] >= 0) {
@@ -4889,18 +4900,18 @@ static void _set_new_results(Object* critter, int flags)
}
}
// 0x425020 damage_object
static void _damage_object(Object* target, int damage, bool animated, int hitUnintendedTarget, Object* damageSource)
// 0x425020
static void _damage_object(Object* a1, int damage, bool animated, int a4, Object* a5)
{
if (target == nullptr) {
if (a1 == nullptr) {
return;
}
if (FID_TYPE(target->fid) != OBJ_TYPE_CRITTER) {
if (FID_TYPE(a1->fid) != OBJ_TYPE_CRITTER) {
return;
}
if (critterFlagCheck(target->pid, CRITTER_INVULNERABLE)) {
if (critterFlagCheck(a1->pid, CRITTER_INVULNERABLE)) {
return;
}
@@ -4908,50 +4919,50 @@ static void _damage_object(Object* target, int damage, bool animated, int hitUni
return;
}
critterAdjustHitPoints(target, -damage);
critterAdjustHitPoints(a1, -damage);
if (target == gDude) {
if (a1 == gDude) {
interfaceRenderHitPoints(animated);
}
target->data.critter.combat.damageLastTurn += damage;
a1->data.critter.combat.damageLastTurn += damage;
if (!hitUnintendedTarget) {
if (!a4) {
// TODO: Not sure about this one.
if (!objectIsPartyMember(target) || !objectIsPartyMember(damageSource)) {
scriptSetFixedParam(target->sid, damage);
scriptExecProc(target->sid, SCRIPT_PROC_DAMAGE);
if (!objectIsPartyMember(a1) || !objectIsPartyMember(a5)) {
scriptSetFixedParam(a1->sid, damage);
scriptExecProc(a1->sid, SCRIPT_PROC_DAMAGE);
}
}
if ((target->data.critter.combat.results & DAM_DEAD) != 0) {
scriptSetObjects(target->sid, target->data.critter.combat.whoHitMe, nullptr);
scriptExecProc(target->sid, SCRIPT_PROC_DESTROY);
itemDestroyAllHidden(target);
if ((a1->data.critter.combat.results & DAM_DEAD) != 0) {
scriptSetObjects(a1->sid, a1->data.critter.combat.whoHitMe, nullptr);
scriptExecProc(a1->sid, SCRIPT_PROC_DESTROY);
itemDestroyAllHidden(a1);
if (target != gDude) {
Object* whoHitMe = target->data.critter.combat.whoHitMe;
if (a1 != gDude) {
Object* whoHitMe = a1->data.critter.combat.whoHitMe;
if (whoHitMe == gDude || (whoHitMe != nullptr && whoHitMe->data.critter.combat.team == gDude->data.critter.combat.team)) {
bool scriptOverrides = false;
Script* scr;
if (scriptGetScript(target->sid, &scr) != -1) {
if (scriptGetScript(a1->sid, &scr) != -1) {
scriptOverrides = scr->scriptOverrides;
}
if (!scriptOverrides) {
_combat_exps += critterGetExp(target);
killsIncByType(critterGetKillType(target));
_combat_exps += critterGetExp(a1);
killsIncByType(critterGetKillType(a1));
}
}
}
if (target->sid != -1) {
scriptRemove(target->sid);
target->sid = -1;
if (a1->sid != -1) {
scriptRemove(a1->sid);
a1->sid = -1;
}
partyMemberRemove(target);
scriptHooks_OnDeath(target);
partyMemberRemove(a1);
scriptHooks_OnDeath(a1);
}
}
@@ -5023,11 +5034,11 @@ void _combat_display(Attack* attack)
char text[280];
if (attack->defender != nullptr
&& attack->intendedTarget != nullptr
&& attack->defender != attack->intendedTarget
&& attack->oops != nullptr
&& attack->defender != attack->oops
&& (attack->attackerFlags & DAM_HIT) != 0) {
if (FID_TYPE(attack->defender->fid) == OBJ_TYPE_CRITTER) {
if (attack->intendedTarget == gDude) {
if (attack->oops == gDude) {
// 608 (male) - Oops! %s was hit instead of you!
// 708 (female) - Oops! %s was hit instead of you!
messageListItem.num = baseMessageId + 8;
@@ -5037,7 +5048,7 @@ void _combat_display(Attack* attack)
} else {
// 509 (male) - Oops! %s were hit instead of %s!
// 559 (female) - Oops! %s were hit instead of %s!
const char* name = objectGetName(attack->intendedTarget);
const char* name = objectGetName(attack->oops);
messageListItem.num = baseMessageId + 9;
if (messageListGetItem(&gCombatMessageList, &messageListItem)) {
snprintf(text, sizeof(text), messageListItem.text, mainCritterName, name);
+2 -2
View File
@@ -103,7 +103,7 @@ typedef struct Attack {
Object* attacker;
int hitMode;
Object* weapon;
int attackHitLocation; // UNUSED?
int attackHitLocation;
int attackerDamage;
int attackerFlags;
int ammoQuantity;
@@ -114,7 +114,7 @@ typedef struct Attack {
int defenderDamage;
int defenderFlags;
int defenderKnockback;
Object* intendedTarget; // mainTarget
Object* oops;
int extrasLength;
Object* extras[EXPLOSION_TARGET_COUNT];
int extrasHitLocation[EXPLOSION_TARGET_COUNT];
+4 -14
View File
@@ -89,6 +89,7 @@ namespace fallout {
static int gameTakeScreenshot(int width, int height, unsigned char* buffer, unsigned char* palette);
static void gameFreeGlobalVars();
static bool tryLoadBaseCEModAtPath(const char* path, bool* found, bool* openFailed);
static void showHelp();
static int gameDbInit();
static void showSplash();
@@ -1185,21 +1186,12 @@ static void gameFreeGlobalVars()
}
// 0x443F74
void showHelp()
static void showHelp()
{
ScopedGameMode gm(GameMode::kHelp);
bool isoWasEnabled = isoDisable();
bool gameMouseWasVisible;
if (isoWasEnabled) {
gameMouseWasVisible = gameMouseObjectsIsVisible();
} else {
gameMouseWasVisible = false;
}
if (gameMouseWasVisible) {
gameMouseObjectsHide();
}
gameMouseObjectsHide();
gameMouseSetCursor(MOUSE_CURSOR_NONE);
@@ -1265,9 +1257,7 @@ void showHelp()
colorCycleEnable();
}
if (gameMouseWasVisible) {
gameMouseObjectsShow();
}
gameMouseObjectsShow();
if (isoWasEnabled) {
isoEnable();
-1
View File
@@ -50,7 +50,6 @@ int showQuitConfirmationDialog();
int gameLoadGlobalVars();
int gameShowDeathDialog(const char* message);
void gameHandleSkilldexResult(int rc);
void showHelp();
void* gameGetGlobalPointer(int var);
int gameSetGlobalPointer(int var, void* value);

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