mirror of
https://github.com/fallout2-ce/fallout2-ce.git
synced 2026-07-27 16:47:11 -07:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a59d88f9f5 |
@@ -0,0 +1,189 @@
|
||||
#include "constants.h"
|
||||
#include "headers/define_extra.h" // For sfall functions if not in constants
|
||||
|
||||
procedure start;
|
||||
procedure description_proc; // For visual checks during game
|
||||
|
||||
variable new_tag1_id = -1;
|
||||
variable new_tag2_id = -1;
|
||||
variable max_tags_test_id = -1;
|
||||
|
||||
procedure start begin
|
||||
// Test Predefined Tags
|
||||
debug_msg("Testing Predefined Interface Tags...");
|
||||
|
||||
// SNEAK (0)
|
||||
debug_msg("Activating SNEAK (0)...");
|
||||
show_iface_tag(0);
|
||||
if (is_iface_tag_active(0)) then
|
||||
debug_msg("SNEAK is active. (Visual check needed)");
|
||||
else
|
||||
debug_msg("ERROR: SNEAK failed to activate or is_iface_tag_active(0) failed.");
|
||||
hide_iface_tag(0);
|
||||
if (not is_iface_tag_active(0)) then
|
||||
debug_msg("SNEAK is inactive.");
|
||||
else
|
||||
debug_msg("ERROR: SNEAK failed to deactivate or is_iface_tag_active(0) failed after hide.");
|
||||
|
||||
// POISONED (1) - Test requires making the player poisoned by other means to truly verify show/hide effect on game state
|
||||
// For now, just test is_iface_tag_active if it reflects direct calls or underlying state.
|
||||
// The opcodes show_iface_tag/hide_iface_tag are not meant to directly poison/cure the player for tags 1 and 2.
|
||||
// They are for custom tags or the specific PC flags (0,3,4). is_iface_tag_active *should* work for 1 and 2.
|
||||
debug_msg("Checking POISONED (1) (depends on game state): " + is_iface_tag_active(1));
|
||||
// Similar for RADIATED (2)
|
||||
debug_msg("Checking RADIATED (2) (depends on game state): " + is_iface_tag_active(2));
|
||||
|
||||
|
||||
// LEVEL (3)
|
||||
debug_msg("Activating LEVEL (3)...");
|
||||
show_iface_tag(3);
|
||||
if (is_iface_tag_active(3)) then
|
||||
debug_msg("LEVEL is active. (Visual check needed)");
|
||||
else
|
||||
debug_msg("ERROR: LEVEL failed to activate or is_iface_tag_active(3) failed.");
|
||||
hide_iface_tag(3);
|
||||
if (not is_iface_tag_active(3)) then
|
||||
debug_msg("LEVEL is inactive.");
|
||||
else
|
||||
debug_msg("ERROR: LEVEL failed to deactivate or is_iface_tag_active(3) failed after hide.");
|
||||
|
||||
// ADDICT (4)
|
||||
debug_msg("Activating ADDICT (4)...");
|
||||
show_iface_tag(4);
|
||||
if (is_iface_tag_active(4)) then
|
||||
debug_msg("ADDICT is active. (Visual check needed)");
|
||||
else
|
||||
debug_msg("ERROR: ADDICT failed to activate or is_iface_tag_active(4) failed.");
|
||||
hide_iface_tag(4);
|
||||
if (not is_iface_tag_active(4)) then
|
||||
debug_msg("ADDICT is inactive.");
|
||||
else
|
||||
debug_msg("ERROR: ADDICT failed to deactivate or is_iface_tag_active(4) failed after hide.");
|
||||
|
||||
debug_msg("Predefined tag tests complete.");
|
||||
debug_msg("---");
|
||||
|
||||
// Test Custom Tags
|
||||
debug_msg("Testing Custom Interface Tags...");
|
||||
|
||||
// Add first custom tag
|
||||
new_tag1_id = add_iface_tag;
|
||||
if (new_tag1_id != -1) then
|
||||
debug_msg("Added custom tag 1 with ID: " + new_tag1_id + " (expected 5 if first)");
|
||||
else
|
||||
debug_msg("ERROR: Failed to add custom tag 1.");
|
||||
return; // Stop if first add fails
|
||||
|
||||
// Show it
|
||||
show_iface_tag(new_tag1_id);
|
||||
if (is_iface_tag_active(new_tag1_id)) then
|
||||
debug_msg("Custom tag 1 is active. (Visual check: should show 'NEW' in green)");
|
||||
else
|
||||
debug_msg("ERROR: Custom tag 1 failed to activate or is_iface_tag_active failed.");
|
||||
|
||||
// Set its text and color
|
||||
set_iface_tag_text(new_tag1_id, "HELLO TAG1", 2); // Color 2 (White from issue description's reference)
|
||||
debug_msg("Set text for custom tag 1 to 'HELLO TAG1' (color white). (Visual check needed)");
|
||||
// Verify text length truncation (19 chars)
|
||||
set_iface_tag_text(new_tag1_id, "ThisIsAVeryLongTextStringIndeed", 3); // Color 3 (Yellow)
|
||||
debug_msg("Set text for custom tag 1 to 'ThisIsAVeryLongTextStringIndeed' (color yellow) - should be truncated. (Visual check needed)");
|
||||
|
||||
|
||||
// Add second custom tag
|
||||
new_tag2_id = add_iface_tag;
|
||||
if (new_tag2_id != -1) then
|
||||
debug_msg("Added custom tag 2 with ID: " + new_tag2_id + " (expected 6 if second)");
|
||||
else
|
||||
debug_msg("ERROR: Failed to add custom tag 2.");
|
||||
// Continue to test tag1 hide
|
||||
|
||||
if (new_tag2_id != -1) then
|
||||
show_iface_tag(new_tag2_id);
|
||||
set_iface_tag_text(new_tag2_id, "AWESOME", 6); // Color 6 (GoodColor/Green)
|
||||
debug_msg("Custom tag 2 ('AWESOME', green) should be active. (Visual check needed)");
|
||||
end
|
||||
|
||||
// Hide first custom tag
|
||||
hide_iface_tag(new_tag1_id);
|
||||
if (not is_iface_tag_active(new_tag1_id)) then
|
||||
debug_msg("Custom tag 1 is now inactive.");
|
||||
else
|
||||
debug_msg("ERROR: Custom tag 1 failed to deactivate.");
|
||||
|
||||
if (new_tag2_id != -1 and is_iface_tag_active(new_tag2_id)) then
|
||||
debug_msg("Custom tag 2 should still be active. (Visual check needed)");
|
||||
else if (new_tag2_id != -1) then
|
||||
debug_msg("ERROR: Custom tag 2 became inactive or failed check.");
|
||||
end
|
||||
|
||||
// Test max tags
|
||||
debug_msg("Attempting to add maximum custom tags (up to 126)...");
|
||||
variable i;
|
||||
variable current_tags = 2; // Assuming new_tag1_id and new_tag2_id were successful (IDs 5 and 6)
|
||||
if (new_tag1_id == -1) then current_tags = 0;
|
||||
if (new_tag1_id != -1 and new_tag2_id == -1) then current_tags = 1;
|
||||
|
||||
|
||||
for (i = current_tags; i < 126; i++) begin
|
||||
max_tags_test_id = add_iface_tag;
|
||||
if (max_tags_test_id == -1) then begin
|
||||
debug_msg("ERROR: Failed to add tag at iteration " + i + ". Max tags might be less than 126 or error in add_iface_tag.");
|
||||
break;
|
||||
end
|
||||
// Minimal show/set_text to ensure they are processed
|
||||
show_iface_tag(max_tags_test_id);
|
||||
set_iface_tag_text(max_tags_test_id, "Tag " + max_tags_test_id, 0); // Default color
|
||||
end
|
||||
if (i == 126) then
|
||||
debug_msg("Successfully added up to 126 custom tags (IDs 5 through 130). Last ID: " + max_tags_test_id);
|
||||
else
|
||||
debug_msg("Stopped adding tags at count " + i + ". Last successful ID: " + max_tags_test_id);
|
||||
end
|
||||
|
||||
// Test adding one more tag (should fail if 126 was the limit)
|
||||
max_tags_test_id = add_iface_tag;
|
||||
if (max_tags_test_id == -1) then
|
||||
debug_msg("Correctly failed to add tag beyond limit (127th attempt).");
|
||||
else
|
||||
debug_msg("ERROR: Added tag beyond 126 limit! ID: " + max_tags_test_id);
|
||||
end
|
||||
|
||||
// Test invalid IDs
|
||||
debug_msg("Testing invalid tag IDs...");
|
||||
show_iface_tag(-1); // Invalid
|
||||
show_iface_tag(1000); // Likely invalid (beyond max added)
|
||||
hide_iface_tag(-2);
|
||||
hide_iface_tag(1001);
|
||||
debug_msg("is_iface_tag_active(-3): " + is_iface_tag_active(-3));
|
||||
debug_msg("is_iface_tag_active(1002): " + is_iface_tag_active(1002));
|
||||
set_iface_tag_text(-4, "INVALID", 0);
|
||||
set_iface_tag_text(1003, "INVALID", 0);
|
||||
debug_msg("Invalid ID tests complete (check console for debug prints from C++).");
|
||||
|
||||
debug_msg("---");
|
||||
debug_msg("Interface Tag testing complete. Check console and game screen.");
|
||||
debug_msg("Will keep some tags active for 10 seconds for visual check then clear...");
|
||||
|
||||
// Keep some tags visible for a moment if run in game
|
||||
// This part is for easier visual checking if you load a game with this script.
|
||||
// For automated testing, the debug_msg output is key.
|
||||
if (game_loaded) then begin
|
||||
if (new_tag1_id != -1) then hide_iface_tag(new_tag1_id); // ensure it's hidden from previous test
|
||||
if (new_tag2_id != -1) then show_iface_tag(new_tag2_id); // ensure tag2 is visible
|
||||
set_iface_tag_text(new_tag2_id, "Testing Done!", 4); // Peanut butter color
|
||||
game_time_advance(game_ticks(10)); // Wait 10 seconds
|
||||
if (new_tag2_id != -1) then hide_iface_tag(new_tag2_id);
|
||||
debug_msg("Visual check period ended.");
|
||||
end
|
||||
end
|
||||
|
||||
// This can be used with `debug_map_scripts` or similar to run on map enter
|
||||
// procedure map_enter_p_proc begin call start; end
|
||||
// procedure map_update_p_proc begin call start; end // If you want it to run repeatedly (not recommended for this test script)
|
||||
|
||||
// For `description_proc` usage with `debug(F5)`:
|
||||
// Create a global script that calls `start` from `description_proc`
|
||||
// Example:
|
||||
// procedure description_proc begin
|
||||
// if (game_loaded) then call start;
|
||||
// end
|
||||
+175
-8
@@ -258,6 +258,9 @@ static unsigned char* gInterfaceWindowBuffer;
|
||||
// 0x59D40C
|
||||
static unsigned char gInterfaceActionPointsBarBackground[90 * 5];
|
||||
|
||||
CustomIndicatorBox gCustomIndicatorBoxes[MAX_CUSTOM_INDICATOR_BOXES];
|
||||
static int gActiveCustomTagsCount = 0;
|
||||
|
||||
// Should the game window stretch all the way to the bottom or sit at the top of the interface bar (default)
|
||||
bool gInterfaceBarMode = false;
|
||||
|
||||
@@ -581,6 +584,13 @@ int interfaceInit()
|
||||
// SFALL
|
||||
sidePanelsInit();
|
||||
|
||||
for (int i = 0; i < MAX_CUSTOM_INDICATOR_BOXES; ++i) {
|
||||
gCustomIndicatorBoxes[i].isActive = false;
|
||||
gCustomIndicatorBoxes[i].text[0] = '\0';
|
||||
gCustomIndicatorBoxes[i].color = 0;
|
||||
}
|
||||
gActiveCustomTagsCount = 0;
|
||||
|
||||
gInterfaceBarEnabled = true;
|
||||
gInterfaceBarInitialized = false;
|
||||
gInterfaceBarHidden = true;
|
||||
@@ -697,6 +707,10 @@ void interfaceFree()
|
||||
customInterfaceBarExit();
|
||||
|
||||
interfaceBarFree();
|
||||
// TODO: Consider if a dedicated function to free custom tag resources is needed.
|
||||
// For now, gCustomIndicatorBoxes is a global static array, so no dynamic memory to free.
|
||||
// Resetting active tags count.
|
||||
gActiveCustomTagsCount = 0;
|
||||
}
|
||||
|
||||
// 0x45E860
|
||||
@@ -2362,6 +2376,64 @@ int indicatorBarRefresh()
|
||||
windowRefresh(gIndicatorBarWindow);
|
||||
}
|
||||
|
||||
// START Custom Tag Handling in indicatorBarRefresh
|
||||
int customTagStartSlot = count; // Where to start adding custom tags in gIndicatorSlots
|
||||
for (int i = 0; i < gActiveCustomTagsCount && customTagStartSlot < INDICATOR_SLOTS_COUNT; ++i) {
|
||||
if (gCustomIndicatorBoxes[i].isActive) {
|
||||
// Using a convention: custom tag IDs are INDICATOR_COUNT + i
|
||||
gIndicatorSlots[customTagStartSlot++] = INDICATOR_COUNT + i;
|
||||
}
|
||||
}
|
||||
count = customTagStartSlot; // Update total count of displayed indicators
|
||||
|
||||
if (count > 1 && (gIndicatorSlots[0] < INDICATOR_COUNT || (count > 0 && gIndicatorSlots[0] >= INDICATOR_COUNT && gIndicatorSlots[1] < INDICATOR_COUNT))) { // Only sort if there are standard indicators or a mix
|
||||
// qsort might behave unexpectedly if all elements are custom tags due to how IDs are structured.
|
||||
// Custom tags are already ordered by their addition.
|
||||
// We only need to sort the standard part, or if custom tags are mixed in a way that needs sorting relative to standard ones.
|
||||
// For simplicity, if there are any standard tags, sort the whole array up to the original count.
|
||||
// Custom tags will be appended after this sorted list of standard tags.
|
||||
// This logic might need refinement if standard and custom tags need to interleave based on some criteria.
|
||||
// For now, custom tags are appended after standard ones.
|
||||
int standardIndicatorsCount = 0;
|
||||
for(int i=0; i < count; ++i) {
|
||||
if(gIndicatorSlots[i] < INDICATOR_COUNT) standardIndicatorsCount++;
|
||||
}
|
||||
if(standardIndicatorsCount > 1) {
|
||||
qsort(gIndicatorSlots, standardIndicatorsCount, sizeof(*gIndicatorSlots), indicatorBoxCompareByPosition);
|
||||
}
|
||||
// After sorting standard indicators, append custom ones.
|
||||
// This re-iterates the custom tag addition logic, but ensures they come after sorted standard tags.
|
||||
customTagStartSlot = standardIndicatorsCount;
|
||||
for (int i = 0; i < gActiveCustomTagsCount && customTagStartSlot < INDICATOR_SLOTS_COUNT; ++i) {
|
||||
if (gCustomIndicatorBoxes[i].isActive) {
|
||||
gIndicatorSlots[customTagStartSlot++] = INDICATOR_COUNT + i;
|
||||
}
|
||||
}
|
||||
count = customTagStartSlot;
|
||||
}
|
||||
|
||||
|
||||
if (gIndicatorBarWindow != -1 && count == 0) { // If no indicators (standard or custom), destroy window.
|
||||
windowDestroy(gIndicatorBarWindow);
|
||||
gIndicatorBarWindow = -1;
|
||||
} else if (count > 0) {
|
||||
if (gIndicatorBarWindow == -1) { // Create window if it doesn't exist and there are indicators
|
||||
Rect interfaceBarWindowRect;
|
||||
windowGetRect(gInterfaceBarWindow, &interfaceBarWindowRect);
|
||||
gIndicatorBarWindow = windowCreate(interfaceBarWindowRect.left,
|
||||
screenGetHeight() - INTERFACE_BAR_HEIGHT - INDICATOR_BOX_HEIGHT,
|
||||
(INDICATOR_BOX_WIDTH - INDICATOR_BOX_CONNECTOR_WIDTH) * count, // Adjust width based on actual count
|
||||
INDICATOR_BOX_HEIGHT,
|
||||
_colorTable[0],
|
||||
0);
|
||||
} else { // If window exists, adjust its width if necessary
|
||||
windowResize(gIndicatorBarWindow, (INDICATOR_BOX_WIDTH - INDICATOR_BOX_CONNECTOR_WIDTH) * count, INDICATOR_BOX_HEIGHT);
|
||||
}
|
||||
indicatorBarRender(count); // Render all (standard + custom)
|
||||
windowRefresh(gIndicatorBarWindow);
|
||||
}
|
||||
// END Custom Tag Handling in indicatorBarRefresh
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -2421,17 +2493,62 @@ static void indicatorBarRender(int count)
|
||||
int connectorWidthCompensation = INDICATOR_BOX_CONNECTOR_WIDTH;
|
||||
|
||||
for (int index = 0; index < count; index++) {
|
||||
int indicator = gIndicatorSlots[index];
|
||||
IndicatorDescription* indicatorDescription = &(gIndicatorDescriptions[indicator]);
|
||||
int indicatorId = gIndicatorSlots[index];
|
||||
|
||||
blitBufferToBufferTrans(indicatorDescription->data + connectorWidthCompensation,
|
||||
INDICATOR_BOX_WIDTH - connectorWidthCompensation,
|
||||
INDICATOR_BOX_HEIGHT,
|
||||
INDICATOR_BOX_WIDTH,
|
||||
windowBuffer + x, windowWidth);
|
||||
if (indicatorId >= INDICATOR_COUNT) { // This is a custom tag
|
||||
int customTagIndex = indicatorId - INDICATOR_COUNT;
|
||||
if (customTagIndex < gActiveCustomTagsCount && gCustomIndicatorBoxes[customTagIndex].isActive) {
|
||||
CustomIndicatorBox* customBox = &gCustomIndicatorBoxes[customTagIndex];
|
||||
|
||||
// Prepare a temporary IndicatorDescription-like structure or directly use customBox properties
|
||||
// For simplicity, let's assume a generic background for custom tags for now
|
||||
// and render text directly. A more advanced approach might involve
|
||||
// creating a temporary prerendered box similar to standard indicators.
|
||||
|
||||
// Fallback generic box (e.g. using SNEAK's background, but ideally a new generic one)
|
||||
// This is a placeholder. Proper handling would involve a generic custom box graphic.
|
||||
unsigned char* boxArt = gIndicatorDescriptions[INDICATOR_SNEAK].data; // Placeholder
|
||||
|
||||
// Create a temporary buffer for this custom tag's rendering
|
||||
unsigned char tempBoxData[INDICATOR_BOX_WIDTH * INDICATOR_BOX_HEIGHT];
|
||||
memcpy(tempBoxData, boxArt, INDICATOR_BOX_WIDTH * INDICATOR_BOX_HEIGHT);
|
||||
|
||||
// Render custom text onto this temporary buffer
|
||||
// Need to set font and color appropriately
|
||||
int oldFont = fontGetCurrent();
|
||||
fontSetCurrent(101); // Assuming same font as standard indicators
|
||||
|
||||
// Calculate text position (centering)
|
||||
int textY = (INDICATOR_BOX_HEIGHT - fontGetLineHeight()) / 2; // Adjusted for actual box height
|
||||
int textX = (INDICATOR_BOX_WIDTH - fontGetStringWidth(customBox->text)) / 2;
|
||||
|
||||
// Ensure textX is not negative if text is too long (it should be truncated already, but as a safeguard)
|
||||
if (textX < 0) textX = 0;
|
||||
|
||||
fontDrawText(tempBoxData + INDICATOR_BOX_WIDTH * textY + textX,
|
||||
customBox->text,
|
||||
INDICATOR_BOX_WIDTH, // Max width for text area
|
||||
INDICATOR_BOX_WIDTH, // Stride
|
||||
customBox->color); // Use color from custom tag
|
||||
|
||||
fontSetCurrent(oldFont); // Restore original font
|
||||
|
||||
blitBufferToBufferTrans(tempBoxData + connectorWidthCompensation,
|
||||
INDICATOR_BOX_WIDTH - connectorWidthCompensation,
|
||||
INDICATOR_BOX_HEIGHT,
|
||||
INDICATOR_BOX_WIDTH, // Source buffer stride
|
||||
windowBuffer + x, windowWidth);
|
||||
}
|
||||
} else { // This is a standard indicator
|
||||
IndicatorDescription* indicatorDescription = &(gIndicatorDescriptions[indicatorId]);
|
||||
blitBufferToBufferTrans(indicatorDescription->data + connectorWidthCompensation,
|
||||
INDICATOR_BOX_WIDTH - connectorWidthCompensation,
|
||||
INDICATOR_BOX_HEIGHT,
|
||||
INDICATOR_BOX_WIDTH,
|
||||
windowBuffer + x, windowWidth);
|
||||
}
|
||||
|
||||
connectorWidthCompensation = 0;
|
||||
|
||||
unconnectedIndicatorsWidth += INDICATOR_BOX_WIDTH;
|
||||
x = unconnectedIndicatorsWidth - INDICATOR_BOX_CONNECTOR_WIDTH * connections;
|
||||
connections++;
|
||||
@@ -2653,4 +2770,54 @@ bool interface_get_current_attack_mode(int* hit_mode)
|
||||
return true;
|
||||
}
|
||||
|
||||
int interfaceAddCustomTag() {
|
||||
if (gActiveCustomTagsCount >= MAX_CUSTOM_INDICATOR_BOXES) {
|
||||
return -1; // No slot available
|
||||
}
|
||||
// Find the first non-active slot conceptually, but since we use gActiveCustomTagsCount,
|
||||
// we just append to the current list of active ones.
|
||||
// The actual "slot" in gCustomIndicatorBoxes is gActiveCustomTagsCount.
|
||||
int tagId = gActiveCustomTagsCount;
|
||||
|
||||
gCustomIndicatorBoxes[tagId].isActive = true; // Mark as active conceptually
|
||||
// Default text/color can be set here if desired, e.g.:
|
||||
strncpy(gCustomIndicatorBoxes[tagId].text, "NEW", 19);
|
||||
gCustomIndicatorBoxes[tagId].text[19] = '\0';
|
||||
gCustomIndicatorBoxes[tagId].color = _colorTable[992]; // Default to green, like SNEAK
|
||||
|
||||
gActiveCustomTagsCount++;
|
||||
// Note: indicatorBarRefresh() is not called here;
|
||||
// it's typically called after a batch of changes or by show/hide.
|
||||
return tagId;
|
||||
}
|
||||
|
||||
void interfaceShowCustomTag(int tagId) {
|
||||
if (tagId < 0 || tagId >= gActiveCustomTagsCount) return;
|
||||
gCustomIndicatorBoxes[tagId].isActive = true;
|
||||
indicatorBarRefresh();
|
||||
}
|
||||
|
||||
void interfaceHideCustomTag(int tagId) {
|
||||
if (tagId < 0 || tagId >= gActiveCustomTagsCount) return;
|
||||
gCustomIndicatorBoxes[tagId].isActive = false;
|
||||
indicatorBarRefresh();
|
||||
}
|
||||
|
||||
bool interfaceIsCustomTagActive(int tagId) {
|
||||
if (tagId < 0 || tagId >= gActiveCustomTagsCount) return false;
|
||||
return gCustomIndicatorBoxes[tagId].isActive;
|
||||
}
|
||||
|
||||
void interfaceSetCustomTagText(int tagId, const char* text, int color) {
|
||||
if (tagId < 0 || tagId >= gActiveCustomTagsCount) return;
|
||||
|
||||
strncpy(gCustomIndicatorBoxes[tagId].text, text, 19);
|
||||
gCustomIndicatorBoxes[tagId].text[19] = '\0'; // Ensure null termination
|
||||
gCustomIndicatorBoxes[tagId].color = color;
|
||||
|
||||
if (gCustomIndicatorBoxes[tagId].isActive) {
|
||||
indicatorBarRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
@@ -12,6 +12,22 @@ namespace fallout {
|
||||
#define INTERFACE_BAR_WIDTH 640
|
||||
#define INTERFACE_BAR_HEIGHT 100
|
||||
|
||||
// Minimum radiation amount to display RADIATED indicator.
|
||||
#define RADATION_INDICATOR_THRESHOLD 65
|
||||
// Minimum poison amount to display POISONED indicator.
|
||||
#define POISON_INDICATOR_THRESHOLD 0
|
||||
|
||||
#define MAX_CUSTOM_INDICATOR_BOXES 126
|
||||
|
||||
typedef struct CustomIndicatorBox {
|
||||
char text[20]; // 19 chars + null terminator
|
||||
int color; // Could be an enum or int mapped to color values
|
||||
bool isActive;
|
||||
// Potentially add a specific ID if needed, though array index might suffice
|
||||
} CustomIndicatorBox;
|
||||
|
||||
extern CustomIndicatorBox gCustomIndicatorBoxes[MAX_CUSTOM_INDICATOR_BOXES];
|
||||
|
||||
typedef enum InterfaceItemAction {
|
||||
INTERFACE_ITEM_ACTION_DEFAULT = -1,
|
||||
INTERFACE_ITEM_ACTION_USE,
|
||||
@@ -63,6 +79,12 @@ bool indicatorBarShow();
|
||||
bool indicatorBarHide();
|
||||
bool interface_get_current_attack_mode(int* hit_mode);
|
||||
|
||||
int interfaceAddCustomTag();
|
||||
void interfaceShowCustomTag(int tagId);
|
||||
void interfaceHideCustomTag(int tagId);
|
||||
bool interfaceIsCustomTagActive(int tagId);
|
||||
void interfaceSetCustomTagText(int tagId, const char* text, int color);
|
||||
|
||||
unsigned char* customInterfaceBarGetBackgroundImageData();
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
+28
-2
@@ -56,11 +56,13 @@ static void mf_string_find(Program* program, int args);
|
||||
static void mf_string_to_case(Program* program, int args);
|
||||
static void mf_string_format(Program* program, int args);
|
||||
static void mf_floor2(Program* program, int args);
|
||||
static void mf_add_iface_tag(Program* program, int args);
|
||||
static void mf_set_iface_tag_text(Program* program, int args);
|
||||
|
||||
// ref. https://github.com/sfall-team/sfall/blob/42556141127895c27476cd5242a73739cbb0fade/sfall/Modules/Scripting/Handlers/Metarule.cpp#L72
|
||||
constexpr MetaruleInfo kMetarules[] = {
|
||||
// {"add_extra_msg_file", mf_add_extra_msg_file, 1, 2, -1, {ARG_STRING, ARG_INT}},
|
||||
// {"add_iface_tag", mf_add_iface_tag, 0, 0},
|
||||
{ "add_iface_tag", mf_add_iface_tag, 0, 0},
|
||||
// {"add_g_timer_event", mf_add_g_timer_event, 2, 2, -1, {ARG_INT, ARG_INT}},
|
||||
// {"add_trait", mf_add_trait, 1, 1, -1, {ARG_INT}},
|
||||
// {"art_cache_clear", mf_art_cache_flush, 0, 0},
|
||||
@@ -133,7 +135,7 @@ constexpr MetaruleInfo kMetarules[] = {
|
||||
// {"set_fake_perk_npc", mf_set_fake_perk_npc, 5, 5, -1, {ARG_OBJECT, ARG_STRING, ARG_INT, ARG_INT, ARG_STRING}},
|
||||
// {"set_fake_trait_npc", mf_set_fake_trait_npc, 5, 5, -1, {ARG_OBJECT, ARG_STRING, ARG_INT, ARG_INT, ARG_STRING}},
|
||||
{ "set_flags", mf_set_flags, 2, 2 },
|
||||
// {"set_iface_tag_text", mf_set_iface_tag_text, 3, 3, -1, {ARG_INT, ARG_STRING, ARG_INT}},
|
||||
{ "set_iface_tag_text", mf_set_iface_tag_text, 3, 3 },
|
||||
{ "set_ini_setting", mf_set_ini_setting, 2, 2 },
|
||||
// {"set_map_enter_position", mf_set_map_enter_position, 3, 3, -1, {ARG_INT, ARG_INT, ARG_INT}},
|
||||
// {"set_object_data", mf_set_object_data, 3, 3, -1, {ARG_OBJECT, ARG_INT, ARG_INT}},
|
||||
@@ -492,6 +494,30 @@ void mf_floor2(Program* program, int args)
|
||||
programStackPushInteger(program, static_cast<int>(floor(programValue.asFloat())));
|
||||
}
|
||||
|
||||
static void mf_add_iface_tag(Program* program, int args) {
|
||||
// No arguments are expected from the script for this function.
|
||||
// Ensure args == 0 if strict argument checking is desired.
|
||||
// The metarule registration already specifies minArgs=0 and maxArgs=0.
|
||||
int result = fallout::interfaceAddCustomTag();
|
||||
fallout::programStackPushInteger(program, result);
|
||||
}
|
||||
|
||||
static void mf_set_iface_tag_text(Program* program, int args) {
|
||||
// Expected arguments: color (int), text (string), tag_id (int)
|
||||
// Arguments are popped in reverse order of how they are pushed by the script.
|
||||
// So, if script calls: set_iface_tag_text(tag_id, "my text", 5)
|
||||
// Stack will have: 5 (color), "my text", tag_id
|
||||
|
||||
int color = fallout::programStackPopInteger(program);
|
||||
const char* text = fallout::programStackPopString(program);
|
||||
int tag_id = fallout::programStackPopInteger(program);
|
||||
|
||||
fallout::interfaceSetCustomTagText(tag_id, text, color);
|
||||
|
||||
// Following the pattern of other void metarules like mf_set_flags, push -1.
|
||||
fallout::programStackPushInteger(program, -1);
|
||||
}
|
||||
|
||||
void sprintf_lite(Program* program, int args, const char* infoOpcodeName)
|
||||
{
|
||||
auto format = programStackPopString(program); // Pop the format string
|
||||
|
||||
@@ -1629,6 +1629,172 @@ void sfallOpcodesInit()
|
||||
|
||||
// 0x827d - void register_hook_proc_spec(int hook, procedure proc)
|
||||
// 0x827e - void reg_anim_callback(procedure proc)
|
||||
|
||||
// 0x81dc - void show_iface_tag(int tag)
|
||||
interpreterRegisterOpcode(0x81DC, op_show_iface_tag);
|
||||
// 0x81dd - void hide_iface_tag(int tag)
|
||||
interpreterRegisterOpcode(0x81DD, op_hide_iface_tag);
|
||||
// 0x81de - int is_iface_tag_active(int tag)
|
||||
interpreterRegisterOpcode(0x81DE, op_is_iface_tag_active);
|
||||
}
|
||||
|
||||
static void op_show_iface_tag(fallout::Program* program) {
|
||||
int tag = fallout::programStackPopInteger(program);
|
||||
|
||||
// Tag values based on issue:
|
||||
// 0: SNEAK
|
||||
// 3: LEVEL
|
||||
// 4: ADDICT
|
||||
// 5 to (4 + BoxBarCount) or last custom box: Custom tags
|
||||
|
||||
if (tag == 0) { // SNEAK
|
||||
if (fallout::gDude) {
|
||||
// Assuming critterAddState adds the state if not present.
|
||||
// If it's a toggle or has other behavior, this might need adjustment.
|
||||
fallout::critterAddState(fallout::gDude, fallout::DUDE_STATE_SNEAKING);
|
||||
// indicatorBarRefresh is called by interfaceShowCustomTag for custom tags,
|
||||
// and should also be called after changing states for predefined tags.
|
||||
fallout::indicatorBarRefresh();
|
||||
}
|
||||
} else if (tag == 3) { // LEVEL
|
||||
if (fallout::gDude) {
|
||||
fallout::critterAddState(fallout::gDude, fallout::DUDE_STATE_LEVEL_UP_AVAILABLE);
|
||||
fallout::indicatorBarRefresh();
|
||||
}
|
||||
} else if (tag == 4) { // ADDICT
|
||||
if (fallout::gDude) {
|
||||
fallout::critterAddState(fallout::gDude, fallout::DUDE_STATE_ADDICTED);
|
||||
fallout::indicatorBarRefresh();
|
||||
}
|
||||
} else if (tag >= 5) {
|
||||
// Custom tags start from ID 0 in gCustomIndicatorBoxes.
|
||||
// The script tag ID is 5 + internal_id. So internal_id = tag - 5.
|
||||
fallout::interfaceShowCustomTag(tag - 5); // This already calls indicatorBarRefresh
|
||||
} else {
|
||||
// Log error for unhandled/invalid tags like 1, 2, or negative values.
|
||||
// Note: Negative values for tags are not explicitly handled by current logic
|
||||
// but would fall into this else block.
|
||||
fallout::debugPrint("op_show_iface_tag: Unhandled or invalid tag ID %d", tag);
|
||||
}
|
||||
// No return value for this opcode.
|
||||
}
|
||||
|
||||
static void op_hide_iface_tag(fallout::Program* program) {
|
||||
int tag = fallout::programStackPopInteger(program);
|
||||
|
||||
// Tag values:
|
||||
// 0: SNEAK
|
||||
// 3: LEVEL
|
||||
// 4: ADDICT
|
||||
// 5+: Custom tags
|
||||
|
||||
if (tag == 0) { // SNEAK
|
||||
if (fallout::gDude) {
|
||||
// Assuming critterClearState removes the state.
|
||||
fallout::critterClearState(fallout::gDude, fallout::DUDE_STATE_SNEAKING);
|
||||
fallout::indicatorBarRefresh();
|
||||
}
|
||||
} else if (tag == 3) { // LEVEL
|
||||
if (fallout::gDude) {
|
||||
fallout::critterClearState(fallout::gDude, fallout::DUDE_STATE_LEVEL_UP_AVAILABLE);
|
||||
fallout::indicatorBarRefresh();
|
||||
}
|
||||
} else if (tag == 4) { // ADDICT
|
||||
if (fallout::gDude) {
|
||||
fallout::critterClearState(fallout::gDude, fallout::DUDE_STATE_ADDICTED);
|
||||
fallout::indicatorBarRefresh();
|
||||
}
|
||||
} else if (tag >= 5) {
|
||||
// Custom tags start from ID 0 in gCustomIndicatorBoxes.
|
||||
// The script tag ID is 5 + internal_id. So internal_id = tag - 5.
|
||||
fallout::interfaceHideCustomTag(tag - 5); // This already calls indicatorBarRefresh
|
||||
} else {
|
||||
// Log error for unhandled/invalid tags like 1, 2, or negative values.
|
||||
fallout::debugPrint("op_hide_iface_tag: Unhandled or invalid tag ID %d", tag);
|
||||
}
|
||||
// No return value for this opcode.
|
||||
}
|
||||
|
||||
static void op_hide_iface_tag(fallout::Program* program) {
|
||||
int tag = fallout::programStackPopInteger(program);
|
||||
|
||||
// Tag values:
|
||||
// 0: SNEAK
|
||||
// 3: LEVEL
|
||||
// 4: ADDICT
|
||||
// 5+: Custom tags
|
||||
|
||||
if (tag == 0) { // SNEAK
|
||||
if (fallout::gDude) {
|
||||
// Assuming critterClearState removes the state.
|
||||
fallout::critterClearState(fallout::gDude, fallout::DUDE_STATE_SNEAKING);
|
||||
fallout::indicatorBarRefresh();
|
||||
}
|
||||
} else if (tag == 3) { // LEVEL
|
||||
if (fallout::gDude) {
|
||||
fallout::critterClearState(fallout::gDude, fallout::DUDE_STATE_LEVEL_UP_AVAILABLE);
|
||||
fallout::indicatorBarRefresh();
|
||||
}
|
||||
} else if (tag == 4) { // ADDICT
|
||||
if (fallout::gDude) {
|
||||
fallout::critterClearState(fallout::gDude, fallout::DUDE_STATE_ADDICTED);
|
||||
fallout::indicatorBarRefresh();
|
||||
}
|
||||
} else if (tag >= 5) {
|
||||
// Custom tags start from ID 0 in gCustomIndicatorBoxes.
|
||||
// The script tag ID is 5 + internal_id. So internal_id = tag - 5.
|
||||
fallout::interfaceHideCustomTag(tag - 5); // This already calls indicatorBarRefresh
|
||||
} else {
|
||||
// Log error for unhandled/invalid tags like 1, 2, or negative values.
|
||||
fallout::debugPrint("op_hide_iface_tag: Unhandled or invalid tag ID %d", tag);
|
||||
}
|
||||
// No return value for this opcode.
|
||||
}
|
||||
|
||||
static void op_is_iface_tag_active(fallout::Program* program) {
|
||||
int tag = fallout::programStackPopInteger(program);
|
||||
bool isActive = false;
|
||||
|
||||
// Tag values based on issue:
|
||||
// 0: SNEAK
|
||||
// 1: POISONED
|
||||
// 2: RADIATED
|
||||
// 3: LEVEL
|
||||
// 4: ADDICT
|
||||
// 5+: Custom tags
|
||||
|
||||
if (fallout::gDude) { // Ensure gDude is valid for checks
|
||||
switch (tag) {
|
||||
case 0: // SNEAK
|
||||
isActive = fallout::dudeHasState(fallout::gDude, fallout::DUDE_STATE_SNEAKING);
|
||||
break;
|
||||
case 1: // POISONED
|
||||
isActive = critterGetPoison(fallout::gDude) > fallout::POISON_INDICATOR_THRESHOLD;
|
||||
break;
|
||||
case 2: // RADIATED
|
||||
isActive = critterGetRadiation(fallout::gDude) > fallout::RADATION_INDICATOR_THRESHOLD;
|
||||
break;
|
||||
case 3: // LEVEL
|
||||
isActive = fallout::dudeHasState(fallout::gDude, fallout::DUDE_STATE_LEVEL_UP_AVAILABLE);
|
||||
break;
|
||||
case 4: // ADDICT
|
||||
isActive = fallout::dudeHasState(fallout::gDude, fallout::DUDE_STATE_ADDICTED);
|
||||
break;
|
||||
default:
|
||||
if (tag >= 5) {
|
||||
isActive = fallout::interfaceIsCustomTagActive(tag - 5); // Adjust tag ID
|
||||
} else {
|
||||
fallout::debugPrint("op_is_iface_tag_active: Unhandled or invalid tag ID %d", tag);
|
||||
// isActive remains false for unhandled tags
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
fallout::debugPrint("op_is_iface_tag_active: gDude is null, cannot check tag status for tag ID %d.", tag);
|
||||
// isActive remains false if gDude is null
|
||||
}
|
||||
|
||||
fallout::programStackPushInteger(program, isActive ? 1 : 0);
|
||||
}
|
||||
|
||||
void sfallOpcodesExit()
|
||||
|
||||
Reference in New Issue
Block a user