#include "ui/CSimpleFrame.hpp" #include "event/Event.hpp" #include "event/Input.hpp" #include "gx/Coordinate.hpp" #include "math/Utils.hpp" #include "ui/CBackdropGenerator.hpp" #include "ui/CScriptRegion.hpp" #include "ui/CSimpleFontString.hpp" #include "ui/CSimpleFrameScript.hpp" #include "ui/CSimpleRender.hpp" #include "ui/CSimpleScrollFrame.hpp" #include "ui/CSimpleTitleRegion.hpp" #include "ui/CSimpleTexture.hpp" #include "ui/CSimpleTop.hpp" #include "ui/FrameScript_Object.hpp" #include "ui/FrameXML.hpp" #include "ui/LoadXML.hpp" #include "util/Lua.hpp" #include "util/StringTo.hpp" #include #include #include #include #include int32_t CSimpleFrame::s_metatable; int32_t CSimpleFrame::s_objectType; int32_t CSimpleFrame::GetObjectType() { if (!CSimpleFrame::s_objectType) { CSimpleFrame::s_objectType = ++FrameScript_Object::s_objectTypes; } return CSimpleFrame::s_objectType; } void CSimpleFrame::CreateScriptMetaTable() { lua_State* L = FrameScript_GetContext(); int32_t ref = FrameScript_Object::CreateScriptMetaTable(L, &CSimpleFrame::RegisterScriptMethods); CSimpleFrame::s_metatable = ref; } void CSimpleFrame::RegisterScriptMethods(lua_State* L) { CScriptRegion::RegisterScriptMethods(L); FrameScript_Object::FillScriptMethodTable(L, SimpleFrameMethods, NUM_SIMPLE_FRAME_SCRIPT_METHODS); } CSimpleFrame::CSimpleFrame(CSimpleFrame* parent) : CScriptRegion() { // TODO // other constructor logic this->m_top = CSimpleTop::s_instance; this->m_top->RegisterFrame(this); this->SetParent(parent); this->m_drawenabled[DRAWLAYER_BACKGROUND] = 1; this->m_drawenabled[DRAWLAYER_BACKGROUND_BORDER] = 1; this->m_drawenabled[DRAWLAYER_ARTWORK] = 1; this->m_drawenabled[DRAWLAYER_ARTWORK_OVERLAY] = 1; this->m_drawenabled[DRAWLAYER_HIGHLIGHT] = 0; this->Show(); } CSimpleFrame::~CSimpleFrame() { // TODO this->m_intAC = 3; // TODO } void CSimpleFrame::AddFrameRegion(CSimpleRegion* region, uint32_t drawlayer) { region->SetLayoutScale(this->m_layoutScale, 0); this->m_drawlayers[drawlayer].LinkToTail(region); this->NotifyDrawLayerChanged(drawlayer); } void CSimpleFrame::DisableDrawLayer(uint32_t drawlayer) { this->m_drawenabled[drawlayer] = 0; this->NotifyDrawLayerChanged(drawlayer); } void CSimpleFrame::EnableDrawLayer(uint32_t drawlayer) { this->m_drawenabled[drawlayer] = 1; this->NotifyDrawLayerChanged(drawlayer); } void CSimpleFrame::LoadXML(XMLNode* node, CStatus* status) { const char* inherits = node->GetAttributeByName("inherits"); const char** v68 = &inherits; char inheritName[1024]; if (inherits && *inherits) { do { SStrTokenize(v68, inheritName, 0x400u, " ,", nullptr); if (!*inheritName) { break; } const char* tainted; bool locked; XMLNode* inheritNode = FrameXML_AcquireHashNode(inheritName, tainted, locked); if (inheritNode) { if (locked) { status->Add(STATUS_WARNING, "Recursively inherited node: %s", inheritName); } else { // TODO lua taint management this->LoadXML(inheritNode, status); // TODO lua taint management FrameXML_ReleaseHashNode(inheritName); } } else { status->Add(STATUS_WARNING, "Couldn't find inherited node: %s", inheritName); } } while (*inheritName); } CScriptRegion::LoadXML(node, status); // hidden const char* hidden = node->GetAttributeByName("hidden"); if (hidden && *hidden) { if (StringToBOOL(hidden)) { this->Hide(); } else { this->Show(); } } // toplevel const char* toplevel = node->GetAttributeByName("toplevel"); if (toplevel && *toplevel) { this->SetFrameFlag(0x1, StringToBOOL(toplevel)); } // movable const char* movable = node->GetAttributeByName("movable"); if (movable && *movable) { this->SetFrameFlag(0x100, StringToBOOL(movable)); } // dontSavePosition const char* dontSavePosition = node->GetAttributeByName("dontSavePosition"); if (dontSavePosition && *dontSavePosition) { this->SetFrameFlag(0x80000, StringToBOOL(dontSavePosition)); } // resizable const char* resizable = node->GetAttributeByName("resizable"); if (resizable && *resizable) { this->SetFrameFlag(0x200, StringToBOOL(resizable)); } // frameStrata const char* frameStrata = node->GetAttributeByName("frameStrata"); if (frameStrata && *frameStrata) { FRAME_STRATA strata; if (StringToFrameStrata(frameStrata, strata)) { this->SetFrameStrata(strata); } else { status->Add( STATUS_WARNING, "Frame %s: Unknown frame strata: %s", this->GetName() || "", frameStrata ); } } // frameLevel const char* frameLevel = node->GetAttributeByName("frameLevel"); if (frameLevel && *frameLevel) { int32_t level = SStrToInt(frameLevel); if (level > 0) { this->SetFrameLevel(level, 1); } else { status->Add( STATUS_WARNING, "Frame %s: Unknown frame level: %s", this->GetName() || "", frameLevel ); } } // TODO alpha // enableMouse const char* enableMouse = node->GetAttributeByName("enableMouse"); if (enableMouse && *enableMouse && StringToBOOL(enableMouse)) { this->EnableEvent(SIMPLE_EVENT_MOUSE, -1); } // enableKeyboard const char* enableKeyboard = node->GetAttributeByName("enableKeyboard"); if (enableKeyboard && *enableKeyboard && StringToBOOL(enableKeyboard)) { this->EnableEvent(SIMPLE_EVENT_CHAR, -1); this->EnableEvent(SIMPLE_EVENT_KEY, -1); } // TODO clampedToScreen // TODO protected // depth const char* depth = node->GetAttributeByName("depth"); if (depth && *depth) { float d = SStrToFloat(depth); if (fabs(d - this->m_depth) >= 0.00000023841858) { this->m_depth = d; this->UpdateDepth(false); } } // Children XMLNode* child = node->m_child; while (child) { if (!SStrCmpI(child->GetName(), "TitleRegion", 0x7FFFFFFFu)) { // TODO } if (!SStrCmpI(child->GetName(), "ResizeBounds", 0x7FFFFFFFu)) { // TODO } if (!SStrCmpI(child->GetName(), "Backdrop", 0x7FFFFFFFu)) { this->LoadXML_Backdrop(child, status); } if (!SStrCmpI(child->GetName(), "HitRectInsets", 0x7FFFFFFFu)) { // TODO } if (!SStrCmpI(child->GetName(), "Layers", 0x7FFFFFFFu)) { this->LoadXML_Layers(child, status); } if (!SStrCmpI(child->GetName(), "Attributes", 0x7FFFFFFFu)) { this->LoadXML_Attributes(child, status); } if (!SStrCmpI(child->GetName(), "Scripts", 0x7FFFFFFFu)) { this->LoadXML_Scripts(child, status); } child = child->m_next; } } void CSimpleFrame::PostLoadXML(XMLNode* node, CStatus* status) { this->m_loading = 0; if (this->m_visible) { this->SetDeferredResize(0); CSimpleTitleRegion* titleRegion = this->m_titleRegion; if (titleRegion) { titleRegion->SetDeferredResize(0); } CSimpleRegion* region = this->m_regions.Head(); while (region) { region->SetDeferredResize(0); region = region->m_regionLink.Next(); } } this->PostLoadXML_Frames(node, status); // TODO // Something to do with alpha animations // // int32_t v9 = CSimpleAlphaAnim::GetObjectType(); // this->sub4887F0(v9, &v13, &v14); // // if (v13) { // this->Vfptr26(); // // v10 = LOBYTE(v4->simpleframe_unk3); // HIBYTE(v4->simpleframe_unk3) ^= (HIBYTE(v4->simpleframe_unk3) ^ 2 * v14) & 2; // BYTE2(v4->simpleframe_unk3) = v10 * BYTE1(v4->simpleframe_unk3) / 255; // // auto region = this->m_regions.Head(); // while (region) { // region->Vfptr15(0, 1, v14); // region = region->m_regionLink.Next(); // } // } this->RunOnLoadScript(); if (this->m_visible) { this->RunOnShowScript(); } } void CSimpleFrame::Raise() { this->m_top->RaiseFrame(this, 1); } void CSimpleFrame::RegisterForEvents(int32_t a2) { for (int32_t event = SIMPLE_EVENT_CHAR; event < NUM_SIMPLE_EVENTS; event++) { if (this->m_eventmask & (1 << event)) { this->m_top->RegisterForEvent(this, static_cast(event), a2, -1); } } } void CSimpleFrame::RunOnCharScript(const char* chr) { if (this->m_onChar.luaRef) { auto L = FrameScript_GetContext(); lua_pushstring(L, chr); this->RunScript(this->m_onChar, 1, 0); } } void CSimpleFrame::RunOnEnterScript(int32_t a2) { if (this->m_onEnter.luaRef) { auto L = FrameScript_GetContext(); lua_pushboolean(L, a2); this->RunScript(this->m_onEnter, 1, 0); } } void CSimpleFrame::RunOnHideScript() { if (this->m_onHide.luaRef && !this->m_loading) { this->RunScript(this->m_onHide, 0, 0); } } void CSimpleFrame::RunOnKeyDownScript(const char* key) { if (this->m_onKeyDown.luaRef) { auto L = FrameScript_GetContext(); lua_pushstring(L, key); this->RunScript(this->m_onKeyDown, 1, 0); } } void CSimpleFrame::RunOnKeyUpScript(const char* key) { if (this->m_onKeyUp.luaRef) { auto L = FrameScript_GetContext(); lua_pushstring(L, key); this->RunScript(this->m_onKeyUp, 1, 0); } } void CSimpleFrame::RunOnLeaveScript(int32_t a2) { if (this->m_onLeave.luaRef) { auto L = FrameScript_GetContext(); lua_pushboolean(L, a2); this->RunScript(this->m_onLeave, 1, 0); } } void CSimpleFrame::RunOnLoadScript() { if (this->m_onLoad.luaRef) { this->RunScript(this->m_onLoad, 0, 0); } } void CSimpleFrame::RunOnMouseDownScript(const char* btn) { if (this->m_onMouseDown.luaRef) { auto L = FrameScript_GetContext(); lua_pushstring(L, btn); this->RunScript(this->m_onMouseDown, 1, 0); } } void CSimpleFrame::RunOnMouseUpScript(const char* btn) { if (this->m_onMouseUp.luaRef) { auto L = FrameScript_GetContext(); lua_pushstring(L, btn); this->RunScript(this->m_onMouseUp, 1, 0); } } void CSimpleFrame::RunOnShowScript() { if (this->m_onShow.luaRef && !this->m_loading) { this->RunScript(this->m_onShow, 0, 0); } } void CSimpleFrame::RunOnSizeChangedScript(float width, float height) { if (this->m_onSizeChanged.luaRef) { auto L = FrameScript_GetContext(); float ddcWidth = CoordinateGetAspectCompensation() * 1024.0f * width; float ndcWidth = DDCToNDCWidth(ddcWidth); lua_pushnumber(L, ndcWidth); float ddcHeight = CoordinateGetAspectCompensation() * 1024.0f * height; float ndcHeight = DDCToNDCWidth(ddcHeight); lua_pushnumber(L, ndcHeight); this->RunScript(this->m_onSizeChanged, 2, 0); } } void CSimpleFrame::RunOnUpdateScript(float elapsedSec) { if (this->m_onUpdate.luaRef) { auto L = FrameScript_GetContext(); lua_pushnumber(L, elapsedSec); this->RunScript(this->m_onUpdate, 1, nullptr); } } void CSimpleFrame::PreLoadXML(XMLNode* node, CStatus* status) { const char* name = node->GetAttributeByName("name"); if (name && *name) { this->SetName(name); } const char* id = node->GetAttributeByName("id"); if (id && *id) { int32_t idNum = SStrToInt(id); if (idNum >= 0) { this->m_id = idNum; } } this->m_loading = 1; this->SetDeferredResize(1); CSimpleTitleRegion* titleRegion = this->m_titleRegion; if (titleRegion) { titleRegion->SetDeferredResize(1); } CSimpleRegion* region = this->m_regions.Head(); while (region) { region->SetDeferredResize(1); region = region->m_regionLink.Next(); } } int32_t CSimpleFrame::GetBoundsRect(CRect& bounds) { if (this->IsResizePending()) { this->Resize(1); } CRect rect = { 0.0f, 0.0f, 0.0f, 0.0f }; if (this->GetRect(&rect)) { bounds.minX = rect.minX >= bounds.minX ? bounds.minX : rect.minX; bounds.maxX = rect.maxX <= bounds.maxX ? bounds.maxX : rect.maxX; bounds.minY = rect.minY >= bounds.minY ? bounds.minY : rect.minY; bounds.maxY = rect.maxY <= bounds.maxY ? bounds.maxY : rect.maxY; } for (auto region = this->m_regions.Head(); region; region = this->m_regions.Next(region)) { if (region->IsShown()) { if (region->IsResizePending()) { region->Resize(1); } if (region->GetRect(&rect)) { bounds.minX = rect.minX >= bounds.minX ? bounds.minX : rect.minX; bounds.maxX = rect.maxX <= bounds.maxX ? bounds.maxX : rect.maxX; bounds.minY = rect.minY >= bounds.minY ? bounds.minY : rect.minY; bounds.maxY = rect.maxY <= bounds.maxY ? bounds.maxY : rect.maxY; } } } for (auto child = this->m_children.Head(); child; child = this->m_children.Next(child)) { if (child->frame->m_shown) { child->frame->GetBoundsRect(bounds); } } return bounds.maxY > bounds.minY && bounds.maxX > bounds.minX; } int32_t CSimpleFrame::GetHitRect(CRect& rect) { if (!(this->CLayoutFrame::m_flags & 0x1)) { return 0; } rect = this->m_hitRect; return 1; } FrameScript_Object::ScriptIx* CSimpleFrame::GetScriptByName(const char* name, FrameScript_Object::ScriptData& data) { auto parentScript = FrameScript_Object::GetScriptByName(name, data); if (parentScript) { return parentScript; } if (!SStrCmpI(name, "OnLoad", 0x7FFFFFFFu)) { return &this->m_onLoad; } if (!SStrCmpI(name, "OnSizeChanged", 0x7FFFFFFFu)) { data.wrapper = "return function(self,w,h) %s end"; return &this->m_onSizeChanged; } if (!SStrCmpI(name, "OnUpdate", 0x7FFFFFFFu)) { data.wrapper = "return function(self,elapsed) %s end"; return &this->m_onUpdate; } if (!SStrCmpI(name, "OnShow", 0x7FFFFFFFu)) { return &this->m_onShow; } if (!SStrCmpI(name, "OnHide", 0x7FFFFFFFu)) { return &this->m_onHide; } if (!SStrCmpI(name, "OnEnter", 0x7FFFFFFFu)) { data.wrapper = "return function(self,motion) %s end"; return &this->m_onEnter; } if (!SStrCmpI(name, "OnLeave", 0x7FFFFFFFu)) { data.wrapper = "return function(self,motion) %s end"; return &this->m_onLeave; } if (!SStrCmpI(name, "OnMouseDown", 0x7FFFFFFFu)) { data.wrapper = "return function(self,button) %s end"; return &this->m_onMouseDown; } if (!SStrCmpI(name, "OnMouseUp", 0x7FFFFFFFu)) { data.wrapper = "return function(self,button) %s end"; return &this->m_onMouseUp; } if (!SStrCmpI(name, "OnMouseWheel", 0x7FFFFFFFu)) { data.wrapper = "return function(self,delta) %s end"; return &this->m_onMouseWheel; } if (!SStrCmpI(name, "OnDragStart", 0x7FFFFFFFu)) { data.wrapper = "return function(self,button) %s end"; return &this->m_onDragStart; } if (!SStrCmpI(name, "OnDragStop", 0x7FFFFFFFu)) { return &this->m_onDragStop; } if (!SStrCmpI(name, "OnReceiveDrag", 0x7FFFFFFFu)) { return &this->m_onReceiveDrag; } if (!SStrCmpI(name, "OnChar", 0x7FFFFFFFu)) { data.wrapper = "return function(self,text) %s end"; return &this->m_onChar; } if (!SStrCmpI(name, "OnKeyDown", 0x7FFFFFFFu)) { data.wrapper = "return function(self,key) %s end"; return &this->m_onKeyDown; } if (!SStrCmpI(name, "OnKeyUp", 0x7FFFFFFFu)) { data.wrapper = "return function(self,key) %s end"; return &this->m_onKeyUp; } if (!SStrCmpI(name, "OnAttributeChanged", 0x7FFFFFFFu)) { data.wrapper = "return function(self,name,value) %s end"; return &this->m_onAttributeChange; } if (!SStrCmpI(name, "OnEnable", 0x7FFFFFFFu)) { return &this->m_onEnable; } if (!SStrCmpI(name, "OnDisable", 0x7FFFFFFFu)) { return &this->m_onDisable; } return nullptr; } int32_t CSimpleFrame::GetScriptMetaTable() { return CSimpleFrame::s_metatable; } bool CSimpleFrame::IsA(int32_t type) { return type == CSimpleFrame::s_objectType || type == CScriptRegion::s_objectType || type == CScriptObject::s_objectType; } void CSimpleFrame::EnableEvent(CSimpleEventType eventType, int32_t priority) { if ((1 << eventType) & this->m_eventmask) { return; } if (this->m_visible) { this->m_top->RegisterForEvent(this, eventType, 0, priority); } this->m_eventmask |= 1 << eventType; } void CSimpleFrame::Hide() { if (this->ProtectedFunctionsAllowed()) { this->m_shown = 0; this->HideThis(); } else { // TODO // auto v2 = this->m_top->unk5[8]; // if (v2) { // v2(0); // } } } int32_t CSimpleFrame::HideThis() { if (!this->m_visible) { return 1; } if (!this->m_loading) { this->SetDeferredResize(1); if (this->m_titleRegion) { this->m_titleRegion->SetDeferredResize(1); } } this->m_visible = 0; this->m_top->HideFrame(this, 0); for (auto region = this->m_regions.Head(); region; region = this->m_regions.Link(region)->Next()) { region->HideThis(); } auto child = this->m_children.Head(); while (child) { child->frame->HideThis(); child = this->m_children.Link(child)->Next(); } this->OnLayerHide(); return 1; } void CSimpleFrame::LoadXML_Attributes(XMLNode* node, CStatus* status) { // TODO } void CSimpleFrame::LoadXML_Backdrop(XMLNode* node, CStatus* status) { auto m = SMemAlloc(sizeof(CBackdropGenerator), __FILE__, __LINE__, 0x0); auto backdrop = new (m) CBackdropGenerator(); backdrop->LoadXML(node, status); this->SetBackdrop(backdrop); } void CSimpleFrame::LoadXML_Layers(XMLNode* node, CStatus* status) { if (!node->m_child) { return; } XMLNode* layerNode = node->m_child; while (layerNode) { if (SStrCmpI(layerNode->GetName(), "Layer", 0x7FFFFFFFu)) { const char* layerNodeName = layerNode->GetName(); const char* nodeName = node->GetName(); const char* frameName = this->GetName(); if (!frameName) { frameName = ""; } status->Add(STATUS_WARNING, "Frame %s: Unknown child node in %s element: %s", frameName, nodeName, layerNodeName); } else { int32_t drawlayer = 2; const char* levelStr = layerNode->GetAttributeByName("level"); if (levelStr && *levelStr) { StringToDrawLayer(levelStr, drawlayer); } XMLNode* layerChild; for (layerChild = layerNode->m_child; layerChild; layerChild = layerChild->m_next) { if (SStrCmpI(layerChild->GetName(), "Texture", 0x7FFFFFFFu)) { if (SStrCmpI(layerChild->GetName(), "FontString", 0x7FFFFFFFu)) { const char* layerChildName = layerChild->GetName(); const char* layerNodeName = layerNode->GetName(); const char* frameName = this->GetName(); if (!frameName) { frameName = ""; } status->Add(STATUS_WARNING, "Frame %s: Unknown child node in %s element: %s", frameName, layerNodeName, layerChildName); } else { CSimpleFontString* layerString = LoadXML_String(layerChild, this, status); layerString->SetFrame(this, drawlayer, layerString->m_shown); } } else { CSimpleTexture* layerTexture = LoadXML_Texture(layerChild, this, status); layerTexture->SetFrame(this, drawlayer, layerTexture->m_shown); } } } layerNode = layerNode->m_next; } } void CSimpleFrame::LoadXML_Scripts(XMLNode* root, CStatus* status) { lua_State* L = FrameScript_GetContext(); const char* scriptName; const char* scriptBody; ScriptData scriptData; ScriptIx* script; XMLNode* node = root->m_child; while (node) { scriptName = node->GetName(); scriptBody = node->m_body; scriptData.wrapper = "return function(self) %s end"; script = this->GetScriptByName(scriptName, scriptData); if (script) { if (script->luaRef) { luaL_unref(L, LUA_REGISTRYINDEX, script->luaRef); } if (!node->m_userData && scriptBody && *scriptBody) { char compileName[1024]; SStrPrintf(compileName, 1024, "*:%s", scriptName); void* scriptFunction = SMemAlloc(sizeof(ScriptFunction), __FILE__, __LINE__, 0); if (scriptFunction) { int32_t luaRef = FrameScript_CompileFunction(compileName, scriptData.wrapper, scriptBody, status); scriptFunction = new (scriptFunction) ScriptFunction(); static_cast(scriptFunction)->luaRef = luaRef; } else { scriptFunction = nullptr; } node->m_userData = scriptFunction; } const char* functionLookup = node->GetAttributeByName("function"); // Set script to already defined function with matching name if (functionLookup && *functionLookup) { int32_t luaRef; const char* frameName; lua_pushstring(L, functionLookup); lua_rawget(L, LUA_GLOBALSINDEX); luaRef = luaL_ref(L, LUA_REGISTRYINDEX); if (luaRef == -1) { frameName = this->GetName(); if (!frameName) { frameName = ""; } status->Add(STATUS_WARNING, "Frame %s: Unknown function %s in element %s", frameName, functionLookup, scriptName); script->luaRef = 0; } else { script->luaRef = luaRef; } // Set script to function compiled from body of script element // eg. foo(); } else if (node->m_userData) { lua_rawgeti(L, LUA_REGISTRYINDEX, static_cast(node->m_userData)->luaRef); script->luaRef = luaL_ref(L, LUA_REGISTRYINDEX); // Disable script } else { script->luaRef = 0; } script->unk = nullptr; if (!SStrCmpI(node->GetName(), "OnChar", 0x7FFFFFFFu) && !(this->m_eventmask & 1)) { if (this->m_visible) { this->m_top->RegisterForEvent(this, SIMPLE_EVENT_CHAR, 0, -1); } this->m_eventmask |= 1u; } if ((!SStrCmpI(node->GetName(), "OnKeyDown", 0x7FFFFFFFu) || !SStrCmpI(node->GetName(), "OnKeyUp", 0x7FFFFFFFu)) && !(this->m_eventmask & 2)) { if (this->m_visible) { this->m_top->RegisterForEvent(this, SIMPLE_EVENT_KEY, 0, -1); } this->m_eventmask |= 2u; } if ((!SStrCmpI(node->GetName(), "OnEnter", 0x7FFFFFFFu) || !SStrCmpI(node->GetName(), "OnLeave", 0x7FFFFFFFu) || !SStrCmpI(node->GetName(), "OnMouseDown", 0x7FFFFFFFu) || !SStrCmpI(node->GetName(), "OnMouseUp", 0x7FFFFFFFu) || !SStrCmpI(node->GetName(), "OnDragStart", 0x7FFFFFFFu)) && !(this->m_eventmask & 4)) { if (this->m_visible) { this->m_top->RegisterForEvent(this, SIMPLE_EVENT_MOUSE, 0, -1); } this->m_eventmask |= 4u; } if (!SStrCmpI(node->GetName(), "OnMouseWheel", 0x7FFFFFFFu) && !(this->m_eventmask & 8)) { if (this->m_visible) { this->m_top->RegisterForEvent(this, SIMPLE_EVENT_MOUSEWHEEL, 0, -1); } this->m_eventmask |= 8u; } } else { const char* frameName = this->GetName(); if (!frameName) { frameName = ""; } status->Add(STATUS_WARNING, "Frame %s: Unknown script element %s", frameName, scriptName); } node = node->m_next; } } void CSimpleFrame::LockHighlight(int32_t lock) { if (this->m_highlightLocked == lock) { return; } this->m_highlightLocked = lock; if (lock) { this->EnableDrawLayer(DRAWLAYER_HIGHLIGHT); } else { if (this->m_top->m_mouseFocus != this) { this->DisableDrawLayer(DRAWLAYER_HIGHLIGHT); } } } void CSimpleFrame::NotifyDrawLayerChanged(uint32_t drawlayer) { if (this->m_flags & 0x2000) { this->m_batchDirty |= 1 << drawlayer; } else if (this->m_top && this->m_visible) { this->m_top->NotifyFrameLayerChanged(this, drawlayer); } this->NotifyScrollParent(); } void CSimpleFrame::NotifyScrollParent() { if (!(this->m_flags & 0x2000) || !this->m_visible) { return; } auto parent = this; while (!(parent->m_flags & 0x4000)) { parent = parent->m_parent; } parent = parent->m_parent; if (parent && parent->m_intAC != 3) { static_cast(parent)->m_updateScrollChild = 1; } } void CSimpleFrame::OnFrameRender() { auto batchDirty = this->m_batchDirty; this->m_batchDirty = 0; if (batchDirty) { for (int32_t layer = 0; layer < NUM_SIMPLEFRAME_DRAWLAYERS; layer++) { auto batch = this->m_batch[layer]; if (!batch) { auto mem = SMemAlloc(sizeof(CRenderBatch), __FILE__, __LINE__, 0x0); batch = new (mem) CRenderBatch(); this->m_batch[layer] = batch; } this->m_renderList.Link(batch)->Unlink(); if ((1 << layer) & batchDirty) { batch->Clear(); this->OnFrameRender(batch, layer); batch->Finish(); } if (batch->m_count) { this->m_renderList.LinkToTail(batch); } } } for (int32_t layer = 0; layer < NUM_SIMPLEFRAME_DRAWLAYERS; layer++) { if (this->m_batch[layer]) { CSimpleRender::DrawBatch(this->m_batch[layer]); } } for (auto child = this->m_children.Head(); child; child = this->m_children.Link(child)->Next()) { if (child->frame->m_visible && !(child->frame->m_flags & 0x4000)) { child->frame->OnFrameRender(); } } } void CSimpleFrame::OnFrameRender(CRenderBatch* batch, uint32_t layer) { if (!this->m_drawenabled[layer]) { return; } for (auto region = this->m_drawlayers[layer].Head(); region; region = this->m_drawlayers[layer].Link(region)->Next()) { region->Draw(batch); } } void CSimpleFrame::OnFrameSizeChanged(const CRect& rect) { CLayoutFrame::OnFrameSizeChanged(rect); this->SetHitRect(); if ( fabs(rect.maxX - rect.minX - (this->m_rect.maxX - this->m_rect.minX)) >= WHOA_EPSILON_1 || fabs(rect.maxY - rect.minY - (this->m_rect.maxY - this->m_rect.minY)) >= WHOA_EPSILON_1 ) { if (this->m_backdrop) { this->m_backdrop->Generate(&this->m_rect); } float v4 = 1.0f / this->m_layoutScale; float v6 = v4 * (this->m_rect.maxX - this->m_rect.minX); float v7 = v4 * (this->m_rect.maxY - this->m_rect.minY); this->OnFrameSizeChanged(v6, v7); } this->m_top->NotifyFrameMovedOrResized(this); } void CSimpleFrame::OnFrameSizeChanged(float width, float height) { this->RunOnSizeChangedScript(width, height); this->NotifyScrollParent(); } int32_t CSimpleFrame::OnLayerChar(const CCharEvent& evt) { if (!this->m_visible) { return 0; } if (!this->m_onChar.luaRef) { return 0; } char charBuf[8]; SUniSPutUTF8(evt.ch, charBuf); this->RunOnCharScript(charBuf); return 1; } void CSimpleFrame::OnLayerCursorEnter(int32_t a2) { // TODO // if (this->m_tooltip) { // TODO // } if (!this->m_highlightLocked) { this->m_drawenabled[DRAWLAYER_HIGHLIGHT] = 1; this->NotifyDrawLayerChanged(DRAWLAYER_HIGHLIGHT); } this->RunOnEnterScript(a2); } void CSimpleFrame::OnLayerCursorExit(int32_t a2, int32_t a3) { // TODO // if (this->m_tooltip) { // TODO // } if (!this->m_highlightLocked) { this->m_drawenabled[DRAWLAYER_HIGHLIGHT] = 0; this->NotifyDrawLayerChanged(DRAWLAYER_HIGHLIGHT); } if (a3) { this->m_mouseDown = 0; this->m_dragging = 0; } this->RunOnLeaveScript(a2); } void CSimpleFrame::OnLayerHide() { this->RunOnHideScript(); this->NotifyScrollParent(); } int32_t CSimpleFrame::OnLayerKeyDown(const CKeyEvent& evt) { if (!this->m_visible) { return 0; } if (!this->m_onKeyDown.luaRef && !this->m_onKeyUp.luaRef) { return 0; } const char* keyName = KeyCodeToString(evt.key); this->RunOnKeyDownScript(keyName); return 1; } int32_t CSimpleFrame::OnLayerKeyUp(const CKeyEvent& evt) { if (!this->m_visible || !this->m_onKeyUp.luaRef) { return 0; } const char* keyName = KeyCodeToString(evt.key); this->RunOnKeyUpScript(keyName); return 1; } int32_t CSimpleFrame::OnLayerMouseDown(const CMouseEvent& evt, const char* btn) { if (!btn) { if (this->m_lookForDrag & evt.button) { this->m_mouseDown = 1; this->m_dragging = 0; this->m_dragButton = evt.button; this->m_clickPoint.x = evt.x; this->m_clickPoint.y = evt.y; } btn = GetButtonName(evt.button); } // TODO // const char* old1234 = this->m_top->m_char1234; // this->m_top->m_char1234 = btn; this->RunOnMouseDownScript(btn); // TODO // this->m_top->m_char1234 = old1234; return 0; } int32_t CSimpleFrame::OnLayerMouseUp(const CMouseEvent& evt, const char* btn) { if (!btn) { if (this->m_lookForDrag & evt.button) { this->m_mouseDown = 0; if (this->m_dragging) { // TODO this->m_dragging = 0; return 1; } } btn = GetButtonName(evt.button); } // TODO // const char* old1234 = this->m_top->m_char1234; // this->m_top->m_char1234 = btn; this->RunOnMouseUpScript(btn); // TODO // this->m_top->m_char1234 = old1234; return 0; } void CSimpleFrame::OnLayerShow() { this->RunOnShowScript(); // TODO // this->sub48FEA0(); } int32_t CSimpleFrame::OnLayerTrackUpdate(const CMouseEvent& evt) { C2Vector pt = { evt.x, evt.y }; if (this->m_mouseDown && !this->m_dragging) { // TODO // - check if dragging logic } int32_t hit = this->TestHitRect(pt); if (hit) { // TODO // - stereo logic } return hit; } void CSimpleFrame::OnLayerUpdate(float elapsedSec) { this->RunOnUpdateScript(elapsedSec); this->PreOnAnimUpdate(); for (auto region = this->m_regions.Head(); region; region = this->m_regions.Link(region)->Next()) { region->PreOnAnimUpdate(); } CScriptRegion::OnLayerUpdate(elapsedSec); for (auto region = this->m_regions.Head(); region; region = this->m_regions.Link(region)->Next()) { region->OnLayerUpdate(elapsedSec); } } void CSimpleFrame::OnScreenSizeChanged() { for (auto region = this->m_regions.Head(); region; region = this->m_regions.Link(region)->Next()) { region->OnScreenSizeChanged(); } } void CSimpleFrame::ParentFrame(CSimpleFrame* frame) { SIMPLEFRAMENODE* node = this->m_children.NewNode(2, 0, 0x8); node->frame = frame; } void CSimpleFrame::PostLoadXML_Frames(XMLNode *node, CStatus* status) { // Call this function with all inherited nodes const char* inheritNames = node->GetAttributeByName("inherits"); if (inheritNames && *inheritNames) { char inheritName[1024]; do { SStrTokenize(&inheritNames, inheritName, 1024, " ,", 0); if (!*inheritName) { break; } const char* tainted; bool locked; XMLNode* inheritNode = FrameXML_AcquireHashNode(inheritName, tainted, locked); if (inheritNode && !locked) { // TODO // FrameScript_GetContext(); // v6 = lua_taintexpected++ == -1; // v12 = lua_tainted; // if (!v6 && !lua_taintedclosure) { // lua_tainted = tainted; // } // const char* v7 = lua_gctag; // lua_gctag = tainted; this->PostLoadXML_Frames(inheritNode, status); // TODO // lua_gctag = v7; // if (lua_taintexpected && !lua_taintedclosure) { // lua_tainted = v12; // } // if (--lua_taintexpected <= 0) { // lua_taintexpected = 0; // } FrameXML_ReleaseHashNode(inheritName); } } while (*inheritName); } // Create child frames XMLNode* framesNode = node->GetChildByName("Frames"); if (framesNode) { XMLNode* frameNode; for (frameNode = framesNode->m_child; frameNode; frameNode = frameNode->m_next) { FrameXML_CreateFrame(frameNode, this, status); } } } void CSimpleFrame::PreOnAnimUpdate() { // TODO } void CSimpleFrame::RegisterRegion(CSimpleRegion* region) { STORM_ASSERT(region); this->m_regions.LinkToTail(region); } void CSimpleFrame::RemoveFrameRegion(CSimpleRegion* region, uint32_t drawlayer) { this->m_drawlayers[drawlayer].UnlinkNode(region); this->NotifyDrawLayerChanged(drawlayer); } void CSimpleFrame::SetBackdrop(CBackdropGenerator* backdrop) { if (this->m_backdrop) { delete this->m_backdrop; } if (backdrop) { backdrop->SetOutput(this); } this->m_backdrop = backdrop; } void CSimpleFrame::SetBeingScrolled(int32_t a2, int32_t a3) { auto visible = this->m_visible; if (visible) { this->HideThis(); } if (a2) { this->m_flags |= 0x2000; } else { this->m_flags &= ~0x2000; } if (a3 >= 0) { if (a3) { this->m_flags |= 0x4000; } else { this->m_flags &= ~0x4000; } } if (a2) { this->m_batchDirty |= 0x1F; } for (auto child = this->m_children.Head(); child; this->m_children.Link(child)->Next()) { if (!(child->frame->m_flags & 0x4000)) { child->frame->SetBeingScrolled(a2, -1); } } if (visible) { this->ShowThis(); } } void CSimpleFrame::SetFrameAlpha(uint8_t alpha) { // TODO } void CSimpleFrame::SetFrameFlag(int32_t flag, int32_t on) { if (on) { this->m_flags |= flag; } else { this->m_flags &= ~flag; } } void CSimpleFrame::SetFrameLevel(int32_t level, int32_t shiftChildren) { level = std::max(level, 0); if (this->m_level == level) { return; } int32_t delta = std::min(level - this->m_level, 128); if (this->m_visible) { this->m_top->HideFrame(this, 1); } this->m_level += delta; if (this->m_visible) { this->m_top->ShowFrame(this, 1); } if (shiftChildren) { for (auto child = this->m_children.Head(); child; child = this->m_children.Link(child)->Next()) { child->frame->SetFrameLevel(child->frame->m_level + delta, 1); } } } bool CSimpleFrame::SetFrameScale(float scale, bool a3) { if (scale == 0.0f || (!a3 && AreEqual(scale, this->m_frameScale, WHOA_EPSILON_1))) { return false; } this->m_frameScale = scale; return this->UpdateScale(a3); } void CSimpleFrame::SetFrameStrata(FRAME_STRATA strata) { if (this->m_strata == strata) { return; } if (this->m_visible) { this->m_top->HideFrame(this, 1); } this->m_strata = strata; if (this->m_visible) { this->m_top->ShowFrame(this, 1); } auto child = this->m_children.Head(); while (child) { child->frame->SetFrameStrata(strata); child = this->m_children.Link(child)->Next(); } } void CSimpleFrame::SetHitRect() { this->m_hitRect.minX = this->m_rect.minX + this->m_hitOffset.minX * this->m_layoutScale; this->m_hitRect.maxX = this->m_rect.maxX - this->m_hitOffset.maxX * this->m_layoutScale; this->m_hitRect.minY = this->m_rect.minY + this->m_hitOffset.minY * this->m_layoutScale; this->m_hitRect.maxY = this->m_rect.maxY - this->m_hitOffset.maxY * this->m_layoutScale; } void CSimpleFrame::SetParent(CSimpleFrame* parent) { if (this->m_parent == parent) { return; } if (this->m_parent) { // TODO // this->OnProtectedDetach(this->m_parent); // (this->m_parent->vfptr + 56)(this); } if (this->m_visible) { this->HideThis(); } this->m_parent = parent; if (parent) { this->SetFrameStrata(parent->m_strata); this->SetFrameLevel(this->m_parent->m_level + 1, 1); this->UpdateScale(false); // TODO // alpha stuff? // v7 = LOBYTE(this->m_parent->simpleframe_unk3) * BYTE1(this->m_parent->simpleframe_unk3) / 255; // if ((_BYTE)v7 != BYTE1(this->simpleframe_unk3)) { // BYTE1(this->simpleframe_unk3) = v7; // (this->vfptr + 53)(this); // } this->SetBeingScrolled((this->m_parent->m_flags >> 13) & 1, -1); } else { this->SetFrameStrata(FRAME_STRATA_MEDIUM); this->SetFrameLevel(0, 1); this->UpdateScale(false); // TODO // if (BYTE1(this->simpleframe_unk3) != -1) { // BYTE1(this->simpleframe_unk3) = -1; // (this->vfptr + 53)(v3); // } this->SetBeingScrolled(0, -1); } if (this->m_parent) { this->OnProtectedAttach(this->m_parent); this->m_parent->ParentFrame(this); } if (this->m_shown) { if (!this->m_parent || this->m_parent->m_visible) { this->ShowThis(); } } } void CSimpleFrame::Show() { if (this->ProtectedFunctionsAllowed()) { this->m_shown = 1; this->ShowThis(); } else { // TODO // auto v2 = this->m_top->unk5[8]; // if (v2) { // v2(0); // } } } int32_t CSimpleFrame::ShowThis() { if (!this->m_shown) { return 0; } if (this->m_parent && !this->m_parent->m_visible) { return 0; } if (this->m_visible) { return 1; } if (!this->m_loading) { this->SetDeferredResize(0); if (this->m_titleRegion) { this->m_titleRegion->SetDeferredResize(0); } } this->m_visible = 1; this->m_top->ShowFrame(this, 0); auto region = this->m_regions.Head(); while (region) { region->ShowThis(); region = this->m_regions.Link(region)->Next(); } auto child = this->m_children.Head(); while (child) { child->frame->ShowThis(); child = this->m_children.Link(child)->Next(); } if (this->m_flags & 0x1) { this->m_top->RaiseFrame(this, 1); } this->OnLayerShow(); return 1; } int32_t CSimpleFrame::TestHitRect(const C2Vector& pt) { if (!(static_cast(this)->m_flags & 0x1)) { return 0; } if (!(this->m_flags & 0x2000)) { return this->m_hitRect.IsPointInside(pt); } CSimpleFrame* parent = this->m_parent; while (parent && parent->m_flags & 0x2000) { parent = parent->m_parent; } if (!parent) { return 0; } CRect rect = CRect::Intersection(parent->m_hitRect, this->m_hitRect); return rect.IsPointInside(pt); } void CSimpleFrame::UpdateDepth(bool a2) { // TODO } bool CSimpleFrame::UpdateScale(bool a2) { auto scale = this->m_frameScale; if (this->m_parent) { scale *= this->m_parent->m_layoutScale; } if ((!a2 && AreEqual(scale, this->m_layoutScale, WHOA_EPSILON_1)) || scale == 0.0f) { return false; } this->SetLayoutScale(scale, a2); for (auto region = this->m_regions.Head(); region; region = this->m_regions.Link(region)->Next()) { region->SetLayoutScale(scale, a2); } for (auto child = this->m_children.Head(); child; child = this->m_children.Link(child)->Next()) { child->frame->UpdateScale(false); } return true; } void CSimpleFrame::UnregisterForEvents(int32_t a2) { for (int32_t event = SIMPLE_EVENT_CHAR; event < NUM_SIMPLE_EVENTS; event++) { if ((1 << event) & this->m_eventmask) { this->m_top->UnregisterForEvent(this, static_cast(event), a2); } } } void CSimpleFrame::UnregisterRegion(CSimpleRegion* region) { STORM_ASSERT(region); this->m_regions.UnlinkNode(region); }