Added "interface_overlay" script function

* for creating an additional overlay on the top of the interface window.
(document WIP)
This commit is contained in:
NovaRain
2020-12-23 11:46:17 +08:00
parent 2bc94e8036
commit 9d5bfaa059
9 changed files with 263 additions and 49 deletions
+3
View File
@@ -355,6 +355,9 @@
#define obj_under_cursor(onlyCritter, includeDude) sfall_func2("obj_under_cursor", onlyCritter, includeDude)
#define objects_in_radius(tile, radius, elev, type) sfall_func4("objects_in_radius", tile, radius, elev, type)
#define outlined_object sfall_func0("outlined_object")
#define overlay_create(winType) sfall_func2("interface_overlay", winType, 1)
#define overlay_clear(winType) sfall_func2("interface_overlay", winType, 2)
#define overlay_clear_rectangle(winType, x, y, w, h) sfall_func6("interface_overlay", winType, 2, x, y, w, h)
#define real_dude_obj sfall_func0("real_dude_obj")
#define remove_all_timer_events sfall_func0("remove_timer_event")
#define remove_timer_event(fixedParam) sfall_func1("remove_timer_event", fixedParam)
+13 -13
View File
@@ -738,28 +738,28 @@ struct ScriptListInfoItem {
//for holding window info
struct Window {
long wID;
long wID; // window position in the _window_index array
long flags;
union {
RECT wRect;
BoundRect rect;
};
long width;
long height;
long clearColour;
long randX;
long randY;
BYTE *surface; // bytes frame data ref to palette
long *buttonsList;
long buttonT1; // buttonptr?
long buttonT2;
long *menuBar;
void (__cdecl *drawFunc)(BYTE* src, long width, long height, long src_width, BYTE* dst, long dst_width); // trans_buf_to_buf_
long width;
long height;
long clearColour;
long randX; // not used by engine
long* randY; // used by sfall for additional surfaces
BYTE* surface; // bytes frame data ref to palette
long* buttonsList;
long buttonT1; // buttonptr?
long buttonT2;
long* menuBar;
void (__cdecl *drawFunc)(BYTE* src, long width, long height, long src_width, BYTE* dst, long dst_width); // trans_buf_to_buf_
};
struct sWindow {
char name[32];
long wID;
long wID; // window position in the _window_index array
long width;
long height;
long region1;
+39 -2
View File
@@ -32,6 +32,24 @@ extern IDirect3DDevice9* d3d9Device;
extern IDirectDrawSurface* primaryDDSurface;
extern bool DeviceLost;
/*
static void DDSurfaceToDXTexture(BYTE* src, long width, long height, long src_width, DWORD* dst, long dst_width) {
if (width <= 0 || height <= 0) return;
dst_width /= 4;
size_t s_pitch = src_width - width;
size_t d_pitch = dst_width - width;
while (height--) {
while (width--) {
*dst++ = *src++ << 24; // write palette color index to texture alpha channel
}
dst += d_pitch;
src += s_pitch;
}
}
*/
class Graphics : public Module {
public:
const char* name() { return "Graphics"; }
@@ -66,13 +84,32 @@ public:
static void RefreshGraphics();
static __forceinline void UpdateDDSurface(BYTE* surface, int width, int height, int widthFrom, RECT* rect) {
long x = rect->left;
long y = rect->top;
if (Graphics::mode == 0) {
__asm {
xor eax, eax;
push y;
push x;
push height;
push width;
push eax; // yFrom
push eax; // xFrom
push eax; // heightFrom
push widthFrom;
push surface;
call ds:[FO_VAR_scr_blit]; // GNW95_ShowRect_(int from, int widthFrom, int heightFrom, int xFrom, int yFrom, int width, int height, int x, int y)
add esp, 9*4;
}
return;
}
if (!DeviceLost) {
DDSURFACEDESC desc;
RECT lockRect = { rect->left, rect->top, rect->right + 1, rect->bottom + 1 };
RECT lockRect = { x, y, rect->right + 1, rect->bottom + 1 };
primaryDDSurface->Lock(&lockRect, &desc, 0, 0);
if (Graphics::GPUBlt == 0) desc.lpSurface = (BYTE*)desc.lpSurface + (desc.lPitch * rect->top) + rect->left;
if (Graphics::GPUBlt == 0) desc.lpSurface = (BYTE*)desc.lpSurface + (desc.lPitch * y) + x;
fo::func::buf_to_buf(surface, width, height, widthFrom, (BYTE*)desc.lpSurface, desc.lPitch);
primaryDDSurface->Unlock(desc.lpSurface);
+50 -3
View File
@@ -29,6 +29,8 @@
#include "..\..\HookScripts\InventoryHs.h"
#include "..\..\SubModules\GameRender.h"
#include "Interface.h"
namespace sfall
@@ -582,7 +584,7 @@ void mf_draw_image_scaled(OpcodeContext& ctx) {
ctx.setReturn(DrawImage(ctx, true));
}
static long InterfaceDrawImage(OpcodeContext& ctx, fo::Window* interfaceWin) {
static long InterfaceDrawImage(OpcodeContext& ctx, fo::Window* ifaceWin) {
const char* file = nullptr;
bool useShift = false;
long direction = -1, w = -1, h = -1;
@@ -628,12 +630,14 @@ static long InterfaceDrawImage(OpcodeContext& ctx, fo::Window* interfaceWin) {
int width = (w >= 0) ? w : framePtr->width;
int height = (h >= 0) ? h : framePtr->height;
BYTE* surface = (ifaceWin->randY) ? GameRender::GetOverlaySurface(ifaceWin) : ifaceWin->surface;
fo::func::trans_cscale(((frmPtr->id == 'PCX') ? frmPtr->pixelData : framePtr->data), framePtr->width, framePtr->height, framePtr->width,
interfaceWin->surface + (y * interfaceWin->width) + x, width, height, interfaceWin->width
surface + (y * ifaceWin->width) + x, width, height, ifaceWin->width
);
if (!(ctx.arg(0).rawValue() & 0x1000000)) {
fo::func::GNW_win_refresh(interfaceWin, &interfaceWin->rect, 0);
fo::func::GNW_win_refresh(ifaceWin, &ifaceWin->rect, 0);
}
FreeArtFile(frmPtr);
@@ -785,12 +789,21 @@ void mf_interface_print(OpcodeContext& ctx) { // same as vanilla PrintRect
__asm call fo::funcoffs::windowGetTextColor_; // set from SetTextColor
__asm mov byte ptr color, al;
}
BYTE* surface;
if (win->randY) { // if a surface was created, the engine will draw on it
surface = win->surface;
win->surface = GameRender::GetOverlaySurface(win); // replace the surface for the windowWrapLineWithSpacing_ function
}
if (color & 0x10000) { // shadow (textshadow)
fo::func::windowWrapLineWithSpacing(win->wID, text, width, maxHeight, x, y, 0x201000F, 0, 0);
color ^= 0x10000;
}
ctx.setReturn(fo::func::windowWrapLineWithSpacing(win->wID, text, width, maxHeight, x, y, color, 0, 0)); // returns count of lines printed
if (win->randY) win->surface = surface;
// no redraw (textdirect)
if (!(color & 0x1000000)) fo::func::GNW_win_refresh(win, &win->rect, 0);
}
@@ -814,5 +827,39 @@ void mf_win_fill_color(OpcodeContext& ctx) {
}
}
void mf_interface_overlay(OpcodeContext& ctx) {
fo::Window* win = nullptr;
long winType = ctx.arg(0).rawValue();
if (ctx.arg(1).rawValue()) {
win = Interface::GetWindow(winType);
if (!win || (int)win == -1) return;
}
switch (ctx.arg(1).rawValue()) {
case 1:
GameRender::CreateOverlaySurface(win, winType);
break;
case 2: // clear
if (ctx.numArgs() > 2) {
long w = ctx.arg(4).rawValue();
long h = ctx.arg(5).rawValue();
if (w <= 0 || h <= 0) return;
long x = ctx.arg(2).rawValue();
long y = ctx.arg(3).rawValue();
if (x < 0 || y < 0) return;
Rectangle rect = { x, y, w, h };
GameRender::ClearOverlay(win, rect);
} else {
GameRender::ClearOverlay(win);
}
break;
//case 0: // unused (reserved)
// GameRender::DestroyOverlaySurface(winType);
}
}
}
}
@@ -121,5 +121,7 @@ void mf_interface_print(OpcodeContext&);
void mf_win_fill_color(OpcodeContext&);
void mf_interface_overlay(OpcodeContext&);
}
}
@@ -102,6 +102,7 @@ static const SfallMetarule metarules[] = {
{"has_fake_trait_npc", mf_has_fake_trait_npc, 2, 2, 0, {ARG_OBJECT, ARG_STRING}},
{"hide_window", mf_hide_window, 0, 1, -1, {ARG_STRING}},
{"interface_art_draw", mf_interface_art_draw, 4, 6, -1, {ARG_INT, ARG_INTSTR, ARG_INT, ARG_INT, ARG_INT, ARG_INT}},
{"interface_overlay", mf_interface_overlay, 2, 6, -1, {ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT}},
{"interface_print", mf_interface_print, 5, 6, -1, {ARG_STRING, ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT}},
{"intface_hide", mf_intface_hide, 0, 0},
{"intface_is_hidden", mf_intface_is_hidden, 0, 0},
+142 -31
View File
@@ -25,10 +25,125 @@
namespace sfall
{
class OverlaySurface
{
private:
long size = 0;
long surfWidth;
long allocSize;
BYTE* surface = nullptr;
public:
//long winType = -1;
BYTE* Surface() { return surface; }
void CreateSurface(fo::Window* win, long winType) {
//this->winType = winType;
this->surfWidth = win->width;
this->size = win->height * win->width;
if (surface != nullptr) {
if (size <= allocSize) {
std::memset(surface, 0, size);
return;
}
delete[] surface;
}
this->allocSize = size;
surface = new BYTE[size]();
}
void ClearSurface() {
if (surface != nullptr) std::memset(surface, 0, size);
}
void ClearSurface(Rectangle &rect) {
if (surface != nullptr) {
if (rect.width > surfWidth || rect.height > (size / surfWidth)) return; // going beyond the surface size
BYTE* surf = surface + (surfWidth * rect.y) + rect.x;
size_t sizeD = rect.width >> 2;
size_t sizeB = rect.width & 3;
size_t stride = sizeD << 2;
long height = rect.height;
while (height--) {
if (sizeD) {
__stosd((DWORD*)surf, 0, sizeD);
surf += stride;
}
if (sizeB) {
__stosb(surf, 0, sizeB);
surf += sizeB;
}
};
}
}
/*void DestroySurface() {
delete[] surface;
surface = nullptr;
}*/
~OverlaySurface() {
delete[] surface;
}
} overlaySurfaces[5];
static long indexPosition = 0;
void GameRender::CreateOverlaySurface(fo::Window* win, long winType) {
overlaySurfaces[indexPosition].CreateSurface(win, winType);
win->randY = reinterpret_cast<long*>(&overlaySurfaces[indexPosition]);
if (++indexPosition == 5) indexPosition = 0;
};
BYTE* GameRender::GetOverlaySurface(fo::Window* win) {
return reinterpret_cast<OverlaySurface*>(win->randY)->Surface();
};
void GameRender::ClearOverlay(fo::Window* win) {
if (win->randY) reinterpret_cast<OverlaySurface*>(win->randY)->ClearSurface();
};
void GameRender::ClearOverlay(fo::Window* win, Rectangle &rect) {
if (win->randY) reinterpret_cast<OverlaySurface*>(win->randY)->ClearSurface(rect);
};
/*void GameRender::DestroyOverlaySurface(long winType) {
for (size_t i = 0; i < 5; i++) {
if (overlaySurfaces[i].winType == winType) {
overlaySurfaces[i].DestroySurface();
overlaySurfaces[i].winType = -1;
//break;
}
}
};*/
static BYTE* GetBuffer() {
return (BYTE*)*(DWORD*)FO_VAR_screen_buffer;
}
static void Draw(fo::Window* win, BYTE* surface, long width, long height, long widthFrom, BYTE* toBuffer, long toWidth, RECT &rect, RECT* updateRect) {
auto drawFunc = (win->flags & fo::WinFlags::Transparent && win->wID) ? fo::func::trans_buf_to_buf : fo::func::buf_to_buf;
if (toBuffer) {
drawFunc(surface, width, height, widthFrom, &toBuffer[rect.left - updateRect->left] + ((rect.top - updateRect->top) * toWidth), toWidth);
} else {
drawFunc(surface, width, height, widthFrom, &GetBuffer()[rect.left] + (rect.top * toWidth), toWidth); // copy to buffer instead of DD surface (buffering)
}
if (!win->randY) return;
surface = &GameRender::GetOverlaySurface(win)[rect.left - win->rect.x] + ((rect.top - win->rect.y) * win->width);
if (toBuffer) {
fo::func::trans_buf_to_buf(surface, width, height, widthFrom, &toBuffer[rect.left - updateRect->left] + ((rect.top - updateRect->top) * toWidth), toWidth);
} else {
fo::func::trans_buf_to_buf(surface, width, height, widthFrom, &GetBuffer()[rect.left] + (rect.top * toWidth), toWidth);
}
}
static void __fastcall sf_GNW_win_refresh(fo::Window* win, RECT* updateRect, BYTE* toBuffer) {
if (win->flags & fo::WinFlags::Hidden) return;
fo::RectList* rects;
@@ -74,9 +189,9 @@ static void __fastcall sf_GNW_win_refresh(fo::Window* win, RECT* updateRect, BYT
Graphics::UpdateDDSurface(&GetBuffer()[rects->wRect.left - updateRect->left] + (rects->wRect.top - updateRect->top) * w, wRect, hRect, w, &rects->wRect);
fo::RectList* next = rects->nextRect;
fo::sf_rect_free(rects);
rects = next;
fo::RectList* free = rects;
rects = rects->nextRect;
fo::sf_rect_free(free);
}
}
return;
@@ -89,18 +204,17 @@ static void __fastcall sf_GNW_win_refresh(fo::Window* win, RECT* updateRect, BYT
rects->rect = { updateRect->left, updateRect->top, updateRect->right, updateRect->bottom };
rects->nextRect = nullptr;
RECT &rect = rects->wRect;
/*
If the border of the updateRect rectangle is located outside the window, then assign to rects->rect the border of the window rectangle
Otherwise, rects->rect contains the borders from the update rectangle (updateRect)
*/
if (win->wRect.left >= rect.left) rect.left = win->wRect.left;
if (win->wRect.top >= rect.top) rect.top = win->wRect.top;
if (win->wRect.right <= rect.right) rect.right = win->wRect.right;
if (win->wRect.bottom <= rect.bottom) rect.bottom = win->wRect.bottom;
if (rects->wRect.left < win->wRect.left) rects->wRect.left = win->wRect.left;
if (rects->wRect.top < win->wRect.top) rects->wRect.top = win->wRect.top;
if (rects->wRect.right > win->wRect.right) rects->wRect.right = win->wRect.right;
if (rects->wRect.bottom > win->wRect.bottom) rects->wRect.bottom = win->wRect.bottom;
if (rect.right < rect.left || rect.bottom < rect.top) {
if (rects->wRect.right < rects->wRect.left || rects->wRect.bottom < rects->wRect.top) {
fo::sf_rect_free(rects);
return;
}
@@ -112,9 +226,8 @@ static void __fastcall sf_GNW_win_refresh(fo::Window* win, RECT* updateRect, BYT
fo::RectList* currRect = rects;
while (currRect) {
RECT &crect = currRect->wRect;
int width = (crect.right - crect.left) + 1; // for current rectangle
int height = (crect.bottom - crect.top) + 1;; // for current rectangle
int width = (currRect->wRect.right - currRect->wRect.left) + 1; // for current rectangle
int height = (currRect->wRect.bottom - currRect->wRect.top) + 1;; // for current rectangle
BYTE* surface;
if (win->wID > 0) {
@@ -123,20 +236,14 @@ static void __fastcall sf_GNW_win_refresh(fo::Window* win, RECT* updateRect, BYT
mov edx, currRect;
call fo::funcoffs::GNW_button_refresh_;
}
surface = &win->surface[crect.left - win->rect.x] + ((crect.top - win->rect.y) * win->width);
surface = &win->surface[currRect->wRect.left - win->rect.x] + ((currRect->wRect.top - win->rect.y) * win->width);
} else {
surface = new BYTE[height * width](); // black background
widthFrom = width; // replace with rectangle
}
auto drawFunc = (win->flags & fo::WinFlags::Transparent && win->wID) ? fo::func::trans_buf_to_buf : fo::func::buf_to_buf;
if (toBuffer) {
drawFunc(surface, width, height, widthFrom, &toBuffer[crect.left - updateRect->left] + ((crect.top - updateRect->top) * toWidth), toWidth);
} else {
// copy to buffer instead of DD surface (buffering)
drawFunc(surface, width, height, widthFrom, &GetBuffer()[crect.left] + (crect.top * toWidth), toWidth);
//Graphics::UpdateDDSurface(surface, width, height, widthFrom, crect);
}
Draw(win, surface, width, height, widthFrom, toBuffer, toWidth, currRect->wRect, updateRect);
if (win->wID == 0) delete[] surface;
currRect = currRect->nextRect;
@@ -201,17 +308,21 @@ void GameRender::init() {
MakeJump(0x4D3704, fo::func::trans_buf_to_buf); // trans_buf_to_buf_
// Enable support for transparent interface windows
SafeWrite16(0x4D5D46, 0x9090); // win_init_ (create screen_buffer)
SafeWriteBatch<WORD>(0x9090, {
0x4D5D46, // win_init_ (create screen_buffer)
0x4D75E6 // win_clip_ (remove _buffering checking)
});
SafeWrite8(0x42F869, fo::WinFlags::MoveOnTop | fo::WinFlags::OwnerFlag); // addWindow_ (remove Transparent flag)
if (Graphics::mode) {
// custom implementation of the GNW_win_refresh function
MakeJump(0x4D6FD9, GNW_win_refresh_hack, 1);
SafeWrite16(0x4D75E6, 0x9090); // win_clip_ (remove _buffering checking)
SafeWrite32(0x4C8FD1, FO_VAR_screen_buffer); // replace screendump_buf
} else { // for default or HRP graphics mode
SafeWrite8(0x4D5DAB, 0x1D); // ecx > ebx (enable _buffering)
BlockCall(0x431076); // dialogMessage_
}
// Custom implementation of the GNW_win_refresh function
MakeJump(0x4D6FD9, GNW_win_refresh_hack, 1);
// Replace _screendump_buf with _screen_buffer for creating screenshots
SafeWriteBatch<DWORD>(FO_VAR_screen_buffer, {0x4C8FD1, 0x4C900D});
// Disable unused code for the RandX and RandY window structure fields (these fields can now be used for other purposes)
SafeWrite32(0x4D630C, 0x9090C031); // xor eax, eax
SafeWrite8(0x4D6310, 0x90);
BlockCall(0x4D6319);
}
}
+6
View File
@@ -24,6 +24,12 @@ namespace sfall
class GameRender {
public:
static void init();
static void CreateOverlaySurface(fo::Window* win, long winType);
// static void DestroyOverlaySurface(long winType);
static void ClearOverlay(fo::Window* win);
static void ClearOverlay(fo::Window* win, Rectangle &rect);
static BYTE* GetOverlaySurface(fo::Window* win);
};
}
+7
View File
@@ -80,6 +80,13 @@ namespace sfall
#define pushadc __asm push eax __asm push edx __asm push ecx
#define popadc __asm pop ecx __asm pop edx __asm pop eax
struct Rectangle {
long x, y, width, height;
long right() { return x + width; }
long bottom() { return y + height; }
};
// Gets the integer value from given INI file.
int iniGetInt(const char* section, const char* setting, int defaultValue, const char* iniFile);