From 02c2df50ac580f79ca549b74cdc48a08a5a29d9b Mon Sep 17 00:00:00 2001 From: NovaRain Date: Thu, 9 May 2019 22:22:02 +0800 Subject: [PATCH] Added "draw_image" and "draw_image_scaled" script functions. --- artifacts/ddraw.ini | 2 +- artifacts/scripting/headers/sfall.h | 2 + artifacts/scripting/sfall function notes.txt | 11 +++ sfall/FalloutEngine/Functions.cpp | 2 + sfall/Modules/BugFixes.cpp | 6 +- .../Modules/Scripting/Handlers/Interface.cpp | 67 +++++++++++++++++++ sfall/Modules/Scripting/Handlers/Interface.h | 4 ++ sfall/Modules/Scripting/Handlers/Metarule.cpp | 6 +- 8 files changed, 94 insertions(+), 6 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 9c76261b..392de3b3 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -630,7 +630,7 @@ DontTurnOffSneakIfYouRun=0 ;Set to 0 to disable switching. (Default is 3) UseWalkDistance=3 -;Set to 1 to fix the bug that unable to sell used geiger counters or stealth boys +;Set to 1 to fix the bug of being unable to sell used geiger counters or stealth boys CanSellUsedGeiger=1 ;Set to 1 to fix the issue with being able to charge the car by using cells on other scenary/critters diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index a3789051..9fe36a3b 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -249,6 +249,8 @@ #define dialog_message(text) sfall_func1("dialog_message", text) #define dialog_obj sfall_func0("dialog_obj") #define display_stats sfall_func0("display_stats") +#define draw_image(frmFile, frame, x, y, transparent) sfall_func5("draw_image", frmFile, frame, x, y, transparent) +#define draw_image_scaled(frmFile, frame, x, y, w, h) sfall_func6("draw_image_scaled", frmFile, frame, x, y, w, h) #define exec_map_update_scripts sfall_func0("exec_map_update_scripts") #define floor2(value) sfall_func1("floor2", value) #define get_can_rest_on_map(map, elev) sfall_func2("get_can_rest_on_map", map, elev) diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index b6f1579c..4fa24564 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -587,6 +587,17 @@ Some utility/math functions are available: - there is also a unique ID number range for the player and party members from 18000 to 83535 - to assign a new ID number generated by the engine to the object (i.e. unassign a unique ID), call the function with two arguments and pass -1 for the flag argument +> void sfall_func5("draw_image", string/int pathFile/artId, int frame, int x, int y, bool transparent) +> void sfall_func6("draw_image_scaled", string/int pathFile/artId, int frame, int x, int y, int width, int height) +- displays the specified FRM image in the active window created by vanilla CreateWin or sfall's create_win script function +- pathFile/artId: path to the FRM file (e.g. "art\\inven\\5mmap.frm"), or its FRM ID number (e.g. 117440550, see specification of the FID format) +Optional arguments: +- frame: frame number, the first frame starts from zero +- x/y: offset relative to the top-left corner of the window +- width/height: image size, used to scale the image when displaying it +- transparent: pass true to display an image with transparent background +- NOTE: calling the functions without creating a window first will crash the game + ------------------------ ------ MORE INFO ------- ------------------------ diff --git a/sfall/FalloutEngine/Functions.cpp b/sfall/FalloutEngine/Functions.cpp index 8e3e473c..14d87ebd 100644 --- a/sfall/FalloutEngine/Functions.cpp +++ b/sfall/FalloutEngine/Functions.cpp @@ -417,6 +417,7 @@ void __fastcall DrawWinLine(int winRef, DWORD startXPos, DWORD endXPos, DWORD st } } +// draws an image to the buffer without scaling and with possible transparency void __fastcall windowDisplayBuf(long x, long width, long y, long height, void* data, long isTrans) { __asm { push height; @@ -430,6 +431,7 @@ void __fastcall windowDisplayBuf(long x, long width, long y, long height, void* } } +// draws an image in the window and scales it to fit the window void __fastcall displayInWindow(long w_here, long width, long height, void* data) { __asm { mov ebx, height; diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index 4ad28cf1..63b9b127 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -2320,9 +2320,9 @@ void BugFixes::init() MakeCall(0x4130E5, action_explode_hack1); dlogr(" Done", DL_INIT); - // Fix for unable to sell used geiger counters or stealth boys + // Fix for being unable to sell used geiger counters or stealth boys if (GetConfigInt("Misc", "CanSellUsedGeiger", 1)) { - dlog("Applying fix for unable to sell used geiger counters or stealth boys.", DL_INIT); + dlog("Applying fix for being unable to sell used geiger counters or stealth boys.", DL_INIT); SafeWrite8(0x478115, 0xBA); SafeWrite8(0x478138, 0xBA); MakeJump(0x474D22, barter_attempt_transaction_hack); @@ -2487,7 +2487,7 @@ void BugFixes::init() SafeWrite8(0x4C1015, 0x90); HookCall(0x4C1042, wmSetupRandomEncounter_hook); - // Fix for unable to sell/give items in the barter screen when the player/party member is overloaded + // Fix for being unable to sell/give items in the barter screen when the player/party member is overloaded HookCalls(barter_attempt_transaction_hook_weight, {0x474C73, 0x474CCA}); // Fix for the underline position in the inventory display window when the item name is longer than one line diff --git a/sfall/Modules/Scripting/Handlers/Interface.cpp b/sfall/Modules/Scripting/Handlers/Interface.cpp index 1db980f0..6c23343b 100644 --- a/sfall/Modules/Scripting/Handlers/Interface.cpp +++ b/sfall/Modules/Scripting/Handlers/Interface.cpp @@ -398,5 +398,72 @@ void sf_create_win(OpcodeContext& ctx) { } } +static void DrawImage(OpcodeContext& ctx, bool isScaled) { + long rotation = 0; + const char* file = nullptr; + if (ctx.arg(0).isInt()) { // art id + long fid = ctx.arg(0).rawValue(); + if (fid == -1) return; + long _fid = fid & 0xFFFFFFF; + file = fo::func::art_get_name(_fid); // .frm + if (_fid >> 24 == fo::OBJ_TYPE_CRITTER) { + rotation = (fid >> 28); + DWORD sz; + if (rotation && fo::func::db_file_exist(file, &sz)) { + file = fo::func::art_get_name(fid); // .fr# + } + } + } else { + file = ctx.arg(0).strValue(); // path to frm file + } + fo::FrmFile* frmPtr = nullptr; + if (fo::func::load_frame(file, &frmPtr)) { + ctx.printOpcodeError("%s() - can't open the file '%s'.", ctx.getMetaruleName(), file); + return; + } + fo::FrmFrameData* framePtr = (fo::FrmFrameData*)&frmPtr->width; + if (rotation > 0 && rotation < 6) { + BYTE* offsOriFrame = (BYTE*)framePtr; + offsOriFrame += frmPtr->oriFrameOffset[rotation]; + framePtr = (fo::FrmFrameData*)offsOriFrame; + } + int frameno = ctx.arg(1).rawValue(); + if (frameno > 0) { + int maxFrames = frmPtr->frames - 1; + if (frameno > maxFrames) frameno = maxFrames; + while (frameno-- > 0) { + BYTE* offsFrame = (BYTE*)framePtr; + offsFrame += framePtr->size + (sizeof(fo::FrmFrameData) - 1); + framePtr = (fo::FrmFrameData*)offsFrame; + } + } + if (isScaled && ctx.numArgs() < 3) { + fo::func::displayInWindow(framePtr->width, framePtr->width, framePtr->height, framePtr->data); // scaled to window size + } else { + int x = ctx.arg(2).rawValue(), y = ctx.arg(3).rawValue(); + if (isScaled) { // draw to scale + long s_width = (ctx.numArgs() > 4) ? ctx.arg(4).rawValue() : framePtr->width; + long s_height = (ctx.numArgs() > 5) ? ctx.arg(5).rawValue() : framePtr->height; + long w_width = fo::func::windowWidth(); + long xy_pos = (x * w_width) + y; + fo::func::trans_cscale(framePtr->width, framePtr->height, s_width, s_height, xy_pos, w_width, framePtr->data); // custom scaling + } else { + fo::func::windowDisplayBuf(x + frmPtr->xshift[rotation], framePtr->width, y + frmPtr->yshift[rotation], framePtr->height, framePtr->data, ctx.arg(4).rawValue()); + } + } + __asm { + mov eax, frmPtr; + call fo::funcoffs::mem_free_; + } +} + +void sf_draw_image(OpcodeContext& ctx) { + DrawImage(ctx, false); +} + +void sf_draw_image_scaled(OpcodeContext& ctx) { + DrawImage(ctx, true); +} + } } diff --git a/sfall/Modules/Scripting/Handlers/Interface.h b/sfall/Modules/Scripting/Handlers/Interface.h index 260463c6..90f00eb3 100644 --- a/sfall/Modules/Scripting/Handlers/Interface.h +++ b/sfall/Modules/Scripting/Handlers/Interface.h @@ -99,5 +99,9 @@ void sf_dialog_message(OpcodeContext&); void sf_create_win(OpcodeContext&); +void sf_draw_image(OpcodeContext&); + +void sf_draw_image_scaled(OpcodeContext&); + } } diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index 78d74b78..4f65b559 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -54,7 +54,7 @@ static MetaruleTableType metaruleTable; { name, handler, minArgs, maxArgs, {arg1, arg2, ...} } - name - name of function that will be used in scripts, - handler - pointer to handler function (see examples below), - - minArgs/maxArgs - minimum and maximum number of arguments allowed for this function + - minArgs/maxArgs - minimum and maximum number of arguments allowed for this function (max 6) - arg1, arg2, ... - argument types for automatic validation */ static const SfallMetarule metarules[] = { @@ -66,7 +66,9 @@ static const SfallMetarule metarules[] = { {"critter_inven_obj2", sf_critter_inven_obj2, 2, 2, {ARG_OBJECT, ARG_INT}}, {"dialog_message", sf_dialog_message, 1, 1, {ARG_STRING}}, {"dialog_obj", sf_get_dialog_object, 0, 0}, - {"display_stats", sf_display_stats, 0, 0}, + {"display_stats", sf_display_stats, 0, 0}, // refresh + {"draw_image", sf_draw_image, 1, 5, {ARG_INTSTR, ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, + {"draw_image_scaled", sf_draw_image_scaled, 1, 6, {ARG_INTSTR, ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT}}, {"exec_map_update_scripts", sf_exec_map_update_scripts, 0, 0}, {"floor2", sf_floor2, 1, 1, {ARG_NUMBER}}, {"get_can_rest_on_map", sf_get_rest_on_map, 2, 2, {ARG_INT, ARG_INT}},