/* =========================================================================== IceTech GPL Source Code Copyright (C) 2026 Justin Marshall Modernized Entity Inspector UI pass by Justin / IceBridge workflow. =========================================================================== */ #include "precompiled.h" #pragma hdrstop #include "qe3.h" #include "Radiant.h" #include "GLWidget.h" #include "PropertyList.h" #include "entitydlg.h" #include "PreviewDlg.h" #include "CurveDlg.h" #include "../../models/model_local.h" // for idRenderModelPrt void Select_Ungroup(); // CEntityDlg dialog IMPLEMENT_DYNAMIC(CEntityDlg, CDialog) #ifndef BS_TYPEMASK #define BS_TYPEMASK 0x0000000F #endif static const COLORREF ENTITY_DARK_BG = RGB(17, 19, 23); static const COLORREF ENTITY_DARK_PANEL = RGB(24, 27, 32); static const COLORREF ENTITY_DARK_PANEL_ALT = RGB(29, 33, 39); static const COLORREF ENTITY_DARK_FIELD = RGB(14, 16, 20); static const COLORREF ENTITY_DARK_FIELD_ALT = RGB(18, 21, 26); static const COLORREF ENTITY_DARK_PROP_NAME = RGB(31, 36, 44); static const COLORREF ENTITY_DARK_PROP_NAME_SEL = RGB(54, 76, 105); static const COLORREF ENTITY_DARK_BORDER = RGB(55, 62, 72); static const COLORREF ENTITY_DARK_BORDER_HOT = RGB(87, 166, 255); static const COLORREF ENTITY_DARK_TEXT = RGB(226, 232, 240); static const COLORREF ENTITY_DARK_TEXT_MUTED = RGB(151, 163, 184); static const COLORREF ENTITY_DARK_TEXT_DISABLED = RGB(103, 112, 128); static const COLORREF ENTITY_DARK_ACCENT = RGB(87, 166, 255); static const char* ENTITY_DARK_BUTTON_OLDPROC = "IceTech.Entity.DarkButtonOldProc"; static const char* ENTITY_DARK_PROP_OLDPROC = "IceTech.Entity.DarkPropertyOldProc"; static const char* ENTITY_DARK_PROP_DIVIDER = "IceTech.Entity.DarkPropertyDivider"; static const char* ENTITY_DARK_FIELD_OLDPROC = "IceTech.Entity.DarkFieldOldProc"; static const char* ENTITY_DARK_COMBO_OLDPROC = "IceTech.Entity.DarkComboOldProc"; static void EntityDark_FillRect(HDC hDC, const RECT& rc, COLORREF color) { HBRUSH brush = ::CreateSolidBrush(color); ::FillRect(hDC, &rc, brush); ::DeleteObject(brush); } static void EntityDark_FrameRect(HDC hDC, const RECT& rc, COLORREF color) { HBRUSH brush = ::CreateSolidBrush(color); ::FrameRect(hDC, &rc, brush); ::DeleteObject(brush); } static void EntityDark_DrawButton(HWND hWnd, HDC hDC) { RECT rc; ::GetClientRect(hWnd, &rc); const UINT style = (UINT)::GetWindowLong(hWnd, GWL_STYLE); const UINT type = style & BS_TYPEMASK; const bool isCheck = (type == BS_CHECKBOX || type == BS_AUTOCHECKBOX || type == BS_3STATE || type == BS_AUTO3STATE); const bool enabled = ::IsWindowEnabled(hWnd) ? true : false; const bool pushed = (::SendMessage(hWnd, BM_GETSTATE, 0, 0) & BST_PUSHED) != 0; const bool checked = (::SendMessage(hWnd, BM_GETCHECK, 0, 0) == BST_CHECKED); char text[256]; text[0] = '\0'; ::GetWindowTextA(hWnd, text, sizeof(text)); HFONT font = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0); HFONT oldFont = font ? (HFONT)::SelectObject(hDC, font) : NULL; ::SetBkMode(hDC, TRANSPARENT); ::SetTextColor(hDC, enabled ? ENTITY_DARK_TEXT : ENTITY_DARK_TEXT_DISABLED); if (isCheck) { EntityDark_FillRect(hDC, rc, ENTITY_DARK_PANEL); RECT box = rc; box.left += 3; box.right = box.left + 14; box.top += max(0, (rc.bottom - rc.top - 14) / 2); box.bottom = box.top + 14; EntityDark_FillRect(hDC, box, enabled ? ENTITY_DARK_FIELD : RGB(20, 22, 26)); EntityDark_FrameRect(hDC, box, checked ? ENTITY_DARK_ACCENT : ENTITY_DARK_BORDER); if (checked) { HPEN pen = ::CreatePen(PS_SOLID, 2, enabled ? ENTITY_DARK_ACCENT : ENTITY_DARK_TEXT_DISABLED); HPEN oldPen = (HPEN)::SelectObject(hDC, pen); ::MoveToEx(hDC, box.left + 3, box.top + 7, NULL); ::LineTo(hDC, box.left + 6, box.top + 10); ::LineTo(hDC, box.right - 3, box.top + 3); ::SelectObject(hDC, oldPen); ::DeleteObject(pen); } RECT textRect = rc; textRect.left = box.right + 7; textRect.right -= 2; ::DrawTextA(hDC, text, -1, &textRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); } else { COLORREF face = pushed ? RGB(22, 25, 30) : ENTITY_DARK_PANEL_ALT; EntityDark_FillRect(hDC, rc, face); EntityDark_FrameRect(hDC, rc, enabled ? ENTITY_DARK_BORDER : RGB(39, 44, 52)); RECT inner = rc; ::InflateRect(&inner, -1, -1); if (!pushed) { EntityDark_FrameRect(hDC, inner, RGB(35, 40, 47)); } RECT textRect = rc; if (pushed) { ::OffsetRect(&textRect, 1, 1); } ::DrawTextA(hDC, text, -1, &textRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); } if (oldFont) { ::SelectObject(hDC, oldFont); } } static LRESULT CALLBACK EntityDarkButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { WNDPROC oldProc = (WNDPROC)::GetPropA(hWnd, ENTITY_DARK_BUTTON_OLDPROC); if (!oldProc) { return ::DefWindowProc(hWnd, uMsg, wParam, lParam); } switch (uMsg) { case WM_ERASEBKGND: return 1; case WM_PAINT: { PAINTSTRUCT ps; HDC hDC = ::BeginPaint(hWnd, &ps); EntityDark_DrawButton(hWnd, hDC); ::EndPaint(hWnd, &ps); return 0; } case WM_PRINTCLIENT: EntityDark_DrawButton(hWnd, (HDC)wParam); return 0; case WM_MOUSEMOVE: case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_ENABLE: case WM_SETTEXT: case BM_SETCHECK: case BM_SETSTATE: { LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); ::InvalidateRect(hWnd, NULL, FALSE); return result; } case WM_NCDESTROY: { ::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldProc); ::RemovePropA(hWnd, ENTITY_DARK_BUTTON_OLDPROC); return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); } } return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); } static void EntityDark_SubclassButton(CWnd& wnd) { HWND hWnd = wnd.GetSafeHwnd(); if (!hWnd || ::GetPropA(hWnd, ENTITY_DARK_BUTTON_OLDPROC)) { return; } WNDPROC oldProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC); ::SetPropA(hWnd, ENTITY_DARK_BUTTON_OLDPROC, (HANDLE)oldProc); ::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)EntityDarkButtonProc); ::InvalidateRect(hWnd, NULL, TRUE); } static void EntityDark_ApplyNativeTheme(HWND hWnd) { if (!hWnd) { return; } typedef HRESULT(WINAPI* SetWindowThemeProc)(HWND, LPCWSTR, LPCWSTR); typedef BOOL(WINAPI* AllowDarkModeForWindowProc)(HWND, BOOL); static HMODULE hUxTheme = ::LoadLibraryA("uxtheme.dll"); static SetWindowThemeProc pSetWindowTheme = hUxTheme ? (SetWindowThemeProc)::GetProcAddress(hUxTheme, "SetWindowTheme") : NULL; static AllowDarkModeForWindowProc pAllowDarkModeForWindow = hUxTheme ? (AllowDarkModeForWindowProc)::GetProcAddress(hUxTheme, MAKEINTRESOURCEA(133)) : NULL; if (pAllowDarkModeForWindow) { pAllowDarkModeForWindow(hWnd, TRUE); } if (pSetWindowTheme) { pSetWindowTheme(hWnd, L"DarkMode_Explorer", NULL); } ::SendMessage(hWnd, WM_THEMECHANGED, 0, 0); } static void EntityDark_RemoveClientEdge(HWND hWnd) { if (!hWnd) { return; } LONG exStyle = ::GetWindowLong(hWnd, GWL_EXSTYLE); if (exStyle & WS_EX_CLIENTEDGE) { ::SetWindowLong(hWnd, GWL_EXSTYLE, exStyle & ~WS_EX_CLIENTEDGE); ::SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); } } static void EntityDark_DrawFrame(HWND hWnd, COLORREF color) { HDC hDC = ::GetWindowDC(hWnd); if (!hDC) { return; } RECT rc; ::GetWindowRect(hWnd, &rc); ::OffsetRect(&rc, -rc.left, -rc.top); EntityDark_FrameRect(hDC, rc, color); ::ReleaseDC(hWnd, hDC); } static LRESULT CALLBACK EntityDarkFieldProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { WNDPROC oldProc = (WNDPROC)::GetPropA(hWnd, ENTITY_DARK_FIELD_OLDPROC); if (!oldProc) { return ::DefWindowProc(hWnd, uMsg, wParam, lParam); } switch (uMsg) { case WM_NCPAINT: case WM_PAINT: { LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); EntityDark_DrawFrame(hWnd, (::GetFocus() == hWnd) ? ENTITY_DARK_BORDER_HOT : ENTITY_DARK_BORDER); return result; } case WM_SETFOCUS: case WM_KILLFOCUS: case WM_ENABLE: { LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); ::RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); return result; } case WM_NCDESTROY: { ::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldProc); ::RemovePropA(hWnd, ENTITY_DARK_FIELD_OLDPROC); return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); } } return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); } static void EntityDark_DrawCombo(HWND hWnd, HDC hDC) { RECT rc; ::GetClientRect(hWnd, &rc); const bool enabled = ::IsWindowEnabled(hWnd) ? true : false; const bool focused = (::GetFocus() == hWnd || ::IsChild(hWnd, ::GetFocus())) ? true : false; EntityDark_FillRect(hDC, rc, enabled ? ENTITY_DARK_FIELD : RGB(20, 22, 26)); EntityDark_FrameRect(hDC, rc, focused ? ENTITY_DARK_BORDER_HOT : ENTITY_DARK_BORDER); RECT arrow = rc; arrow.left = arrow.right - 22; EntityDark_FillRect(hDC, arrow, ENTITY_DARK_PANEL_ALT); EntityDark_FrameRect(hDC, arrow, ENTITY_DARK_BORDER); POINT pts[3]; int cx = arrow.left + (arrow.right - arrow.left) / 2; int cy = arrow.top + (arrow.bottom - arrow.top) / 2 + 1; pts[0].x = cx - 4; pts[0].y = cy - 2; pts[1].x = cx + 4; pts[1].y = cy - 2; pts[2].x = cx; pts[2].y = cy + 3; HBRUSH arrowBrush = ::CreateSolidBrush(enabled ? ENTITY_DARK_TEXT : ENTITY_DARK_TEXT_DISABLED); HBRUSH oldBrush = (HBRUSH)::SelectObject(hDC, arrowBrush); HPEN arrowPen = ::CreatePen(PS_SOLID, 1, enabled ? ENTITY_DARK_TEXT : ENTITY_DARK_TEXT_DISABLED); HPEN oldPen = (HPEN)::SelectObject(hDC, arrowPen); ::Polygon(hDC, pts, 3); ::SelectObject(hDC, oldPen); ::DeleteObject(arrowPen); ::SelectObject(hDC, oldBrush); ::DeleteObject(arrowBrush); char text[512]; text[0] = '\0'; int sel = (int)::SendMessage(hWnd, CB_GETCURSEL, 0, 0); if (sel >= 0) { ::SendMessageA(hWnd, CB_GETLBTEXT, (WPARAM)sel, (LPARAM)text); } else { ::GetWindowTextA(hWnd, text, sizeof(text)); } HFONT font = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0); HFONT oldFont = font ? (HFONT)::SelectObject(hDC, font) : NULL; ::SetBkMode(hDC, TRANSPARENT); ::SetTextColor(hDC, enabled ? ENTITY_DARK_TEXT : ENTITY_DARK_TEXT_DISABLED); RECT textRect = rc; textRect.left += 7; textRect.right = arrow.left - 3; ::DrawTextA(hDC, text, -1, &textRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); if (oldFont) { ::SelectObject(hDC, oldFont); } } static LRESULT CALLBACK EntityDarkComboProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { WNDPROC oldProc = (WNDPROC)::GetPropA(hWnd, ENTITY_DARK_COMBO_OLDPROC); if (!oldProc) { return ::DefWindowProc(hWnd, uMsg, wParam, lParam); } switch (uMsg) { case WM_ERASEBKGND: return 1; case WM_PAINT: { UINT comboType = (UINT)::GetWindowLong(hWnd, GWL_STYLE) & 0x00000003; if (comboType == CBS_DROPDOWNLIST) { PAINTSTRUCT ps; HDC hDC = ::BeginPaint(hWnd, &ps); EntityDark_DrawCombo(hWnd, hDC); ::EndPaint(hWnd, &ps); return 0; } LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); EntityDark_DrawFrame(hWnd, (::GetFocus() == hWnd || ::IsChild(hWnd, ::GetFocus())) ? ENTITY_DARK_BORDER_HOT : ENTITY_DARK_BORDER); return result; } case WM_SETFOCUS: case WM_KILLFOCUS: case WM_ENABLE: case WM_SETTEXT: case CB_SETCURSEL: case CB_ADDSTRING: case CB_DELETESTRING: case CB_RESETCONTENT: { LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); ::InvalidateRect(hWnd, NULL, FALSE); return result; } case WM_NCDESTROY: { ::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldProc); ::RemovePropA(hWnd, ENTITY_DARK_COMBO_OLDPROC); return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); } } return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); } static void EntityDark_SubclassField(CWnd& wnd) { HWND hWnd = wnd.GetSafeHwnd(); if (!hWnd || ::GetPropA(hWnd, ENTITY_DARK_FIELD_OLDPROC)) { return; } EntityDark_RemoveClientEdge(hWnd); EntityDark_ApplyNativeTheme(hWnd); WNDPROC oldProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC); ::SetPropA(hWnd, ENTITY_DARK_FIELD_OLDPROC, (HANDLE)oldProc); ::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)EntityDarkFieldProc); ::InvalidateRect(hWnd, NULL, TRUE); } static void EntityDark_SubclassCombo(CWnd& wnd) { HWND hWnd = wnd.GetSafeHwnd(); if (!hWnd || ::GetPropA(hWnd, ENTITY_DARK_COMBO_OLDPROC)) { return; } EntityDark_RemoveClientEdge(hWnd); EntityDark_ApplyNativeTheme(hWnd); WNDPROC oldProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC); ::SetPropA(hWnd, ENTITY_DARK_COMBO_OLDPROC, (HANDLE)oldProc); ::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)EntityDarkComboProc); ::InvalidateRect(hWnd, NULL, TRUE); } static void EntityDark_SetPropertyListRowHeight(HWND hWnd) { if (!hWnd) { return; } // The old resource-sized rows were too tight after the dark-theme font pass. // Give every row enough breathing room so descenders (g, y, p, q) are not clipped at the bottom. ::SendMessage(hWnd, LB_SETITEMHEIGHT, 0, 32); } static void EntityDark_CopyPropText(char* out, int outSize, CPropertyItem* propItem, int index, HWND hWnd, bool wantValue) { if (!out || outSize <= 0) { return; } out[0] = '\0'; if (propItem && propItem != (CPropertyItem*)LB_ERR) { const char* src = wantValue ? propItem->m_curValue : propItem->m_propName; if (src && src[0]) { idStr::Copynz(out, src, outSize); return; } } char text[2048]; text[0] = '\0'; ::SendMessageA(hWnd, LB_GETTEXT, (WPARAM)index, (LPARAM)text); char* tab = strchr(text, '\t'); if (tab) { if (wantValue) { idStr::Copynz(out, tab + 1, outSize); } else { *tab = '\0'; idStr::Copynz(out, text, outSize); } } else if (!wantValue) { idStr::Copynz(out, text, outSize); } } static void EntityDark_DrawPropertyList(HWND hWnd, HDC hDC) { RECT client; ::GetClientRect(hWnd, &client); EntityDark_FillRect(hDC, client, ENTITY_DARK_FIELD); const int count = (int)::SendMessage(hWnd, LB_GETCOUNT, 0, 0); if (count <= 0) { return; } int divider = (int)(INT_PTR)::GetPropA(hWnd, ENTITY_DARK_PROP_DIVIDER); if (divider <= 0) { divider = 170; } if (divider > client.right - 80) { divider = max(80, client.right / 2); } const int topIndex = (int)::SendMessage(hWnd, LB_GETTOPINDEX, 0, 0); const int curSel = (int)::SendMessage(hWnd, LB_GETCURSEL, 0, 0); const bool focused = (::GetFocus() == hWnd || ::IsChild(hWnd, ::GetFocus())) ? true : false; HFONT font = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0); HFONT oldFont = font ? (HFONT)::SelectObject(hDC, font) : NULL; ::SetBkMode(hDC, TRANSPARENT); for (int i = max(0, topIndex); i < count; i++) { RECT item; if ((int)::SendMessage(hWnd, LB_GETITEMRECT, (WPARAM)i, (LPARAM)&item) == LB_ERR) { break; } if (item.top >= client.bottom) { break; } if (item.bottom <= client.top) { continue; } const bool selected = (i == curSel); RECT rowRect = item; rowRect.left = client.left; rowRect.right = client.right; EntityDark_FillRect(hDC, rowRect, selected ? RGB(31, 41, 55) : ENTITY_DARK_FIELD); RECT nameRect = item; nameRect.left = client.left; nameRect.right = min(client.right, divider); EntityDark_FillRect(hDC, nameRect, selected ? ENTITY_DARK_PROP_NAME_SEL : ENTITY_DARK_PROP_NAME); RECT valueRect = item; valueRect.left = min(client.right, divider + 1); valueRect.right = client.right; EntityDark_FillRect(hDC, valueRect, selected ? RGB(30, 43, 59) : ENTITY_DARK_FIELD); CPropertyItem* propItem = (CPropertyItem*)::SendMessage(hWnd, LB_GETITEMDATA, (WPARAM)i, 0); char nameText[1024]; char valueText[2048]; EntityDark_CopyPropText(nameText, sizeof(nameText), propItem, i, hWnd, false); EntityDark_CopyPropText(valueText, sizeof(valueText), propItem, i, hWnd, true); RECT nameTextRect = nameRect; nameTextRect.left += 10; nameTextRect.right -= 10; nameTextRect.top += 3; nameTextRect.bottom -= 1; RECT valueTextRect = valueRect; valueTextRect.left += 12; valueTextRect.right -= 10; valueTextRect.top += 3; valueTextRect.bottom -= 1; ::SetTextColor(hDC, selected ? RGB(255, 255, 255) : ENTITY_DARK_TEXT); ::DrawTextA(hDC, nameText, -1, &nameTextRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); ::SetTextColor(hDC, selected ? RGB(235, 245, 255) : RGB(206, 214, 226)); ::DrawTextA(hDC, valueText, -1, &valueTextRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); // Dark, vertical-only split. Do not draw horizontal row dividers; they were cutting through the value text. RECT dividerRect = item; dividerRect.left = min(client.right - 1, divider); dividerRect.right = dividerRect.left + 1; EntityDark_FillRect(hDC, dividerRect, RGB(42, 48, 58)); } if (focused) { RECT focusRect = client; ::InflateRect(&focusRect, -1, -1); EntityDark_FrameRect(hDC, focusRect, ENTITY_DARK_BORDER_HOT); } else { RECT frameRect = client; ::InflateRect(&frameRect, -1, -1); EntityDark_FrameRect(hDC, frameRect, ENTITY_DARK_BORDER); } if (oldFont) { ::SelectObject(hDC, oldFont); } } static LRESULT CALLBACK EntityDarkPropertyListProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { WNDPROC oldProc = (WNDPROC)::GetPropA(hWnd, ENTITY_DARK_PROP_OLDPROC); if (!oldProc) { return ::DefWindowProc(hWnd, uMsg, wParam, lParam); } switch (uMsg) { case WM_PAINT: { PAINTSTRUCT ps; HDC hDC = ::BeginPaint(hWnd, &ps); EntityDark_DrawPropertyList(hWnd, hDC); ::EndPaint(hWnd, &ps); return 0; } case WM_PRINTCLIENT: EntityDark_DrawPropertyList(hWnd, (HDC)wParam); return 0; case WM_ERASEBKGND: { RECT rc; ::GetClientRect(hWnd, &rc); EntityDark_FillRect((HDC)wParam, rc, ENTITY_DARK_FIELD); return 1; } case WM_VSCROLL: case WM_HSCROLL: case WM_MOUSEWHEEL: case WM_SETFOCUS: case WM_KILLFOCUS: case LB_ADDSTRING: case LB_INSERTSTRING: case LB_DELETESTRING: case LB_RESETCONTENT: case LB_SETCURSEL: { LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); if (uMsg == LB_ADDSTRING || uMsg == LB_INSERTSTRING || uMsg == LB_RESETCONTENT) { EntityDark_SetPropertyListRowHeight(hWnd); } ::InvalidateRect(hWnd, NULL, FALSE); return result; } case WM_NCDESTROY: { ::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldProc); ::RemovePropA(hWnd, ENTITY_DARK_PROP_OLDPROC); ::RemovePropA(hWnd, ENTITY_DARK_PROP_DIVIDER); return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); } } return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); } static void EntityDark_SubclassPropertyList(CWnd& wnd, int divider) { HWND hWnd = wnd.GetSafeHwnd(); if (!hWnd) { return; } ::SetPropA(hWnd, ENTITY_DARK_PROP_DIVIDER, (HANDLE)(INT_PTR)divider); EntityDark_SetPropertyListRowHeight(hWnd); if (::GetPropA(hWnd, ENTITY_DARK_PROP_OLDPROC)) { ::InvalidateRect(hWnd, NULL, TRUE); return; } WNDPROC oldProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC); ::SetPropA(hWnd, ENTITY_DARK_PROP_OLDPROC, (HANDLE)oldProc); ::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)EntityDarkPropertyListProc); ::InvalidateRect(hWnd, NULL, TRUE); } CEntityDlg::CEntityDlg(CWnd* pParent /*=NULL*/) : CDialog(CEntityDlg::IDD, pParent) { editEntity = NULL; multipleEntities = false; currentAnimation = NULL; currentAnimationFrame = 0; dict = NULL; themeInitialized = false; colorBackground = RGB(17, 19, 23); colorPanel = RGB(26, 29, 34); colorPanelAlt = RGB(29, 33, 39); colorBorder = RGB(55, 62, 72); colorText = RGB(226, 232, 240); colorTextMuted = RGB(151, 163, 184); colorAccent = RGB(87, 166, 255); colorEditBg = RGB(14, 16, 20); } CEntityDlg::~CEntityDlg() { DestroyModernTheme(); } void CEntityDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_LIST_KEYVAL, listKeyVal); DDX_Control(pDX, IDC_COMBO_CLASS, comboClass); DDX_Control(pDX, IDC_EDIT_KEY, editKey); DDX_Control(pDX, IDC_EDIT_VAL, editVal); DDX_Control(pDX, IDC_STATIC_TITLE, staticTitle); DDX_Control(pDX, IDC_STATIC_KEY, staticKey); DDX_Control(pDX, IDC_STATIC_VAL, staticVal); DDX_Control(pDX, IDC_BUTTON_BROWSE, btnBrowse); DDX_Control(pDX, IDC_E_135, btn135); DDX_Control(pDX, IDC_E_90, btn90); DDX_Control(pDX, IDC_E_45, btn45); DDX_Control(pDX, IDC_E_180, btn180); DDX_Control(pDX, IDC_E_0, btn360); DDX_Control(pDX, IDC_E_225, btn225); DDX_Control(pDX, IDC_E_270, btn270); DDX_Control(pDX, IDC_E_315, btn315); DDX_Control(pDX, IDC_E_UP, btnUp); DDX_Control(pDX, IDC_E_DOWN, btnDown); DDX_Control(pDX, IDC_BUTTON_MODEL, btnModel); DDX_Control(pDX, IDC_BUTTON_SOUND, btnSound); DDX_Control(pDX, IDC_BUTTON_GUI, btnGui); DDX_Control(pDX, IDC_BUTTON_PARTICLE, btnParticle); DDX_Control(pDX, IDC_BUTTON_SKIN, btnSkin); DDX_Control(pDX, IDC_BUTTON_CURVE, btnCurve); DDX_Control(pDX, IDC_BUTTON_CREATE, btnCreate); DDX_Control(pDX, IDC_LIST_VARS, listVars); DDX_Control(pDX, IDC_ENTITY_ANIMATIONS, cbAnimations); DDX_Control(pDX, IDC_ANIMATION_SLIDER, slFrameSlider); DDX_Control(pDX, IDC_ENTITY_CURRENT_ANIM, staticFrame); DDX_Control(pDX, IDC_ENTITY_PLAY_ANIM, btnPlayAnim); DDX_Control(pDX, IDC_ENTITY_STOP_ANIM, btnStopAnim); } BEGIN_MESSAGE_MAP(CEntityDlg, CDialog) ON_WM_SIZE() ON_WM_CTLCOLOR() ON_WM_ERASEBKGND() ON_WM_PAINT() ON_CBN_SELCHANGE(IDC_COMBO_CLASS, OnCbnSelchangeComboClass) ON_LBN_SELCHANGE(IDC_LIST_KEYVAL, OnLbnSelchangeListkeyval) ON_BN_CLICKED(IDC_E_135, OnBnClickedE135) ON_BN_CLICKED(IDC_E_90, OnBnClickedE90) ON_BN_CLICKED(IDC_E_45, OnBnClickedE45) ON_BN_CLICKED(IDC_E_180, OnBnClickedE180) ON_BN_CLICKED(IDC_E_0, OnBnClickedE0) ON_BN_CLICKED(IDC_E_225, OnBnClickedE225) ON_BN_CLICKED(IDC_E_270, OnBnClickedE270) ON_BN_CLICKED(IDC_E_315, OnBnClickedE315) ON_BN_CLICKED(IDC_E_UP, OnBnClickedEUp) ON_BN_CLICKED(IDC_E_DOWN, OnBnClickedEDown) ON_BN_CLICKED(IDC_BUTTON_MODEL, OnBnClickedButtonModel) ON_BN_CLICKED(IDC_BUTTON_SOUND, OnBnClickedButtonSound) ON_BN_CLICKED(IDC_BUTTON_GUI, OnBnClickedButtonGui) ON_BN_CLICKED(IDC_BUTTON_BROWSE, OnBnClickedButtonBrowse) ON_CBN_DBLCLK(IDC_COMBO_CLASS, OnCbnDblclkComboClass) ON_BN_CLICKED(IDC_BUTTON_CREATE, OnBnClickedButtonCreate) ON_LBN_DBLCLK(IDC_LIST_KEYVAL, OnLbnDblclkListkeyval) ON_LBN_SELCHANGE(IDC_LIST_VARS, OnLbnSelchangeListVars) ON_LBN_DBLCLK(IDC_LIST_VARS, OnLbnDblclkListVars) ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_ANIMATION_SLIDER, OnNMReleasedcaptureSlider1) ON_BN_CLICKED(IDC_BUTTON_PARTICLE, OnBnClickedButtonParticle) ON_BN_CLICKED(IDC_BUTTON_SKIN, OnBnClickedButtonSkin) ON_BN_CLICKED(IDC_BUTTON_CURVE, OnBnClickedButtonCurve) ON_CBN_SELCHANGE(IDC_ENTITY_ANIMATIONS, OnCbnAnimationChange) ON_BN_CLICKED(IDC_ENTITY_PLAY_ANIM, OnBnClickedStartAnimation) ON_BN_CLICKED(IDC_ENTITY_STOP_ANIM, OnBnClickedStopAnimation) ON_WM_TIMER() ON_BN_CLICKED(IDOK, OnOK) END_MESSAGE_MAP() // -------------------------------------------------------------------------- // Modern theme helpers // -------------------------------------------------------------------------- void CEntityDlg::DestroyModernTheme() { if (fontTitle.GetSafeHandle()) { fontTitle.DeleteObject(); } if (fontSection.GetSafeHandle()) { fontSection.DeleteObject(); } if (fontNormal.GetSafeHandle()) { fontNormal.DeleteObject(); } if (fontMono.GetSafeHandle()) { fontMono.DeleteObject(); } if (brushBackground.GetSafeHandle()) { brushBackground.DeleteObject(); } if (brushPanel.GetSafeHandle()) { brushPanel.DeleteObject(); } if (brushPanelAlt.GetSafeHandle()) { brushPanelAlt.DeleteObject(); } if (brushEdit.GetSafeHandle()) { brushEdit.DeleteObject(); } if (brushStatic.GetSafeHandle()) { brushStatic.DeleteObject(); } themeInitialized = false; } void CEntityDlg::InitModernTheme() { if (themeInitialized) { return; } NONCLIENTMETRICS ncm; memset(&ncm, 0, sizeof(ncm)); ncm.cbSize = sizeof(ncm); SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0); LOGFONT lf = ncm.lfMessageFont; strcpy(lf.lfFaceName, "Segoe UI"); lf.lfHeight = -12; lf.lfWeight = FW_NORMAL; fontNormal.CreateFontIndirect(&lf); lf.lfHeight = -17; lf.lfWeight = FW_SEMIBOLD; fontTitle.CreateFontIndirect(&lf); lf.lfHeight = -12; lf.lfWeight = FW_SEMIBOLD; fontSection.CreateFontIndirect(&lf); strcpy(lf.lfFaceName, "Consolas"); lf.lfHeight = -12; lf.lfWeight = FW_NORMAL; fontMono.CreateFontIndirect(&lf); brushBackground.CreateSolidBrush(colorBackground); brushPanel.CreateSolidBrush(colorPanel); brushPanelAlt.CreateSolidBrush(colorPanelAlt); brushEdit.CreateSolidBrush(colorEditBg); brushStatic.CreateSolidBrush(colorPanel); themeInitialized = true; } void CEntityDlg::SetCtrlFont(CWnd& wnd, CFont* font) { if (wnd.GetSafeHwnd() && font && font->GetSafeHandle()) { wnd.SetFont(font, FALSE); } } void CEntityDlg::SetCtrlFont(int id, CFont* font) { CWnd* wnd = GetDlgItem(id); if (wnd && wnd->GetSafeHwnd() && font && font->GetSafeHandle()) { wnd->SetFont(font, FALSE); } } void CEntityDlg::ApplyModernTheme() { InitModernTheme(); SetCtrlFont(staticTitle, &fontTitle); SetCtrlFont(staticKey, &fontSection); SetCtrlFont(staticVal, &fontSection); SetCtrlFont(staticFrame, &fontNormal); SetCtrlFont(comboClass, &fontNormal); SetCtrlFont(cbAnimations, &fontNormal); SetCtrlFont(editKey, &fontMono); SetCtrlFont(editVal, &fontMono); SetCtrlFont(listKeyVal, &fontMono); SetCtrlFont(listVars, &fontNormal); SetCtrlFont(btnCreate, &fontSection); SetCtrlFont(btnBrowse, &fontSection); SetCtrlFont(btnModel, &fontNormal); SetCtrlFont(btnSound, &fontNormal); SetCtrlFont(btnGui, &fontNormal); SetCtrlFont(btnParticle, &fontNormal); SetCtrlFont(btnSkin, &fontNormal); SetCtrlFont(btnCurve, &fontNormal); SetCtrlFont(btn135, &fontNormal); SetCtrlFont(btn90, &fontNormal); SetCtrlFont(btn45, &fontNormal); SetCtrlFont(btn180, &fontNormal); SetCtrlFont(btn360, &fontNormal); SetCtrlFont(btn225, &fontNormal); SetCtrlFont(btn270, &fontNormal); SetCtrlFont(btn315, &fontNormal); SetCtrlFont(btnUp, &fontNormal); SetCtrlFont(btnDown, &fontNormal); SetCtrlFont(btnPlayAnim, &fontNormal); SetCtrlFont(btnStopAnim, &fontNormal); if (editKey.GetSafeHwnd()) { editKey.SetMargins(6, 6); } if (editVal.GetSafeHwnd()) { editVal.SetMargins(6, 6); } EntityDark_SubclassField(editKey); EntityDark_SubclassField(editVal); EntityDark_SubclassField(listKeyVal); EntityDark_SubclassField(listVars); EntityDark_SubclassField(slFrameSlider); EntityDark_SubclassCombo(comboClass); EntityDark_SubclassCombo(cbAnimations); EntityDark_SubclassPropertyList(listKeyVal, 170); EntityDark_SubclassPropertyList(listVars, 170); CWnd* darkButtons[] = { &btnCreate, &btnBrowse, &btnModel, &btnSound, &btnGui, &btnParticle, &btnSkin, &btnCurve, &btn135, &btn90, &btn45, &btn180, &btn360, &btn225, &btn270, &btn315, &btnUp, &btnDown, &btnPlayAnim, &btnStopAnim }; for (int i = 0; i < (int)(sizeof(darkButtons) / sizeof(darkButtons[0])); i++) { if (darkButtons[i] && darkButtons[i]->GetSafeHwnd()) { EntityDark_ApplyNativeTheme(darkButtons[i]->GetSafeHwnd()); EntityDark_SubclassButton(*darkButtons[i]); } } staticTitle.SetWindowText("Entity Inspector"); staticKey.SetWindowText("Key"); staticVal.SetWindowText("Value"); } void CEntityDlg::MoveCtrl(CWnd& wnd, int x, int y, int w, int h, UINT flags) { if (wnd.GetSafeHwnd()) { wnd.SetWindowPos(NULL, x, y, max(1, w), max(1, h), flags | SWP_NOZORDER); } } void CEntityDlg::MoveCtrl(int id, int x, int y, int w, int h, UINT flags) { CWnd* wnd = GetDlgItem(id); if (wnd && wnd->GetSafeHwnd()) { wnd->SetWindowPos(NULL, x, y, max(1, w), max(1, h), flags | SWP_NOZORDER); } } void CEntityDlg::DrawPanel(CDC& dc, const CRect& r, const char* title, bool accent) { CBrush* oldBrush = dc.SelectObject(&brushPanel); CPen borderPen(PS_SOLID, 1, accent ? colorAccent : colorBorder); CPen* oldPen = dc.SelectObject(&borderPen); dc.RoundRect(r.left, r.top, r.right, r.bottom, 8, 8); if (title && title[0]) { CRect titleRect = r; titleRect.left += 10; titleRect.top += 6; titleRect.bottom = titleRect.top + 18; CFont* oldFont = dc.SelectObject(&fontSection); dc.SetBkMode(TRANSPARENT); dc.SetTextColor(accent ? colorAccent : colorTextMuted); dc.DrawText(title, -1, &titleRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); dc.SelectObject(oldFont); } dc.SelectObject(oldPen); dc.SelectObject(oldBrush); } void CEntityDlg::DrawModernBackground(CDC& dc) { CRect rect; GetClientRect(&rect); dc.FillSolidRect(&rect, colorBackground); const int pad = 8; CRect header(rect.left + pad, rect.top + pad, rect.right - pad, rect.top + 42); CRect classPanel(rect.left + pad, header.bottom + pad, rect.right - pad, header.bottom + 52 + pad); int contentTop = classPanel.bottom + pad; int contentBottom = rect.bottom - pad; int width = rect.Width(); if (width >= 500) { const int editorH = 106; const int minToolH = 112; const int availableH = max(1, contentBottom - contentTop); // Give the two property grids the extra vertical space instead of capping them at 420px. // This keeps the edit/tools panels underneath and prevents the list controls from stacking over each other. int listH = max(180, availableH - editorH - minToolH - pad * 3); int leftW = (width - pad * 3) / 2; CRect vars(rect.left + pad, contentTop, rect.left + pad + leftW, contentTop + listH); CRect keyvals(vars.right + pad, contentTop, rect.right - pad, contentTop + listH); CRect editor(rect.left + pad, vars.bottom + pad, rect.right - pad, vars.bottom + editorH); CRect tools(rect.left + pad, editor.bottom + pad, rect.right - pad, contentBottom); DrawPanel(dc, header, "", true); DrawPanel(dc, classPanel, "Class", false); DrawPanel(dc, vars, "Spawn Args / Help", false); DrawPanel(dc, keyvals, "Key / Value Pairs", true); DrawPanel(dc, editor, "Edit Property", false); DrawPanel(dc, tools, "Tools", false); } else { const int editorH = 106; const int minToolH = 112; const int availableH = max(1, contentBottom - contentTop); int h = max(140, (availableH - editorH - minToolH - pad * 3) / 2); CRect vars(rect.left + pad, contentTop, rect.right - pad, contentTop + h); CRect keyvals(rect.left + pad, vars.bottom + pad, rect.right - pad, vars.bottom + h); CRect editor(rect.left + pad, keyvals.bottom + pad, rect.right - pad, keyvals.bottom + editorH); CRect tools(rect.left + pad, editor.bottom + pad, rect.right - pad, contentBottom); DrawPanel(dc, header, "", true); DrawPanel(dc, classPanel, "Class", false); DrawPanel(dc, vars, "Spawn Args / Help", false); DrawPanel(dc, keyvals, "Key / Value Pairs", true); DrawPanel(dc, editor, "Edit Property", false); DrawPanel(dc, tools, "Tools", false); } } BOOL CEntityDlg::OnEraseBkgnd(CDC* pDC) { if (pDC) { DrawModernBackground(*pDC); } return TRUE; } void CEntityDlg::OnPaint() { CPaintDC dc(this); DrawModernBackground(dc); } HBRUSH CEntityDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); if (!themeInitialized) { return hbr; } const int id = pWnd ? pWnd->GetDlgCtrlID() : 0; switch (nCtlColor) { case CTLCOLOR_STATIC: pDC->SetTextColor((id == IDC_STATIC_TITLE) ? colorText : colorTextMuted); pDC->SetBkMode(TRANSPARENT); return (HBRUSH)brushPanel.GetSafeHandle(); case CTLCOLOR_EDIT: pDC->SetTextColor(colorText); pDC->SetBkColor(colorEditBg); return (HBRUSH)brushEdit.GetSafeHandle(); case CTLCOLOR_LISTBOX: pDC->SetTextColor(colorText); pDC->SetBkColor(colorEditBg); return (HBRUSH)brushEdit.GetSafeHandle(); case CTLCOLOR_BTN: pDC->SetTextColor(colorText); pDC->SetBkColor(colorPanelAlt); return (HBRUSH)brushPanelAlt.GetSafeHandle(); case CTLCOLOR_DLG: return (HBRUSH)brushBackground.GetSafeHandle(); } return hbr; } // -------------------------------------------------------------------------- // Dialog init / layout // -------------------------------------------------------------------------- BOOL CEntityDlg::OnInitDialog() { CDialog::OnInitDialog(); InitModernTheme(); listKeyVal.SetUpdateInspectors(true); listKeyVal.SetDivider(170); listVars.SetDivider(170); ::SendMessage(listKeyVal.GetSafeHwnd(), LB_SETITEMHEIGHT, 0, 32); ::SendMessage(listVars.GetSafeHwnd(), LB_SETITEMHEIGHT, 0, 32); staticFrame.SetWindowText("0"); ApplyModernTheme(); ModifyStyleEx(0, WS_EX_CONTROLPARENT); Invalidate(FALSE); return TRUE; } INT_PTR CEntityDlg::OnToolHitTest(CPoint point, TOOLINFO* pTI) const { return CDialog::OnToolHitTest(point, pTI); } void CEntityDlg::OnSize(UINT nType, int cx, int cy) { CDialog::OnSize(nType, cx, cy); if (staticTitle.GetSafeHwnd() == NULL) { return; } const int pad = 8; const int inner = 10; const int headerH = 42; const int classH = 52; const int editorH = 106; const int minToolH = 112; CRect rect; GetClientRect(&rect); int w = max(1, rect.Width()); int h = max(1, rect.Height()); CRect header(rect.left + pad, rect.top + pad, rect.right - pad, rect.top + pad + headerH); MoveCtrl(staticTitle, header.left + 12, header.top + 8, header.Width() - 24, 24); CRect classPanel(rect.left + pad, header.bottom + pad, rect.right - pad, header.bottom + pad + classH); int classY = classPanel.top + 22; int createW = 82; MoveCtrl(comboClass, classPanel.left + inner, classY, classPanel.Width() - inner * 3 - createW, 22); MoveCtrl(btnCreate, classPanel.right - inner - createW, classY, createW, 22); int contentTop = classPanel.bottom + pad; int contentBottom = rect.bottom - pad; if (w >= 500) { const int availableH = max(1, contentBottom - contentTop); // No upper cap: the grids now consume the extra dock/inspector height instead of staying squeezed at the top. int listH = max(180, availableH - editorH - minToolH - pad * 3); int leftW = (w - pad * 3) / 2; CRect varsPanel(rect.left + pad, contentTop, rect.left + pad + leftW, contentTop + listH); CRect keyPanel(varsPanel.right + pad, contentTop, rect.right - pad, contentTop + listH); MoveCtrl(listVars, varsPanel.left + inner, varsPanel.top + 28, varsPanel.Width() - inner * 2, varsPanel.Height() - 38); MoveCtrl(listKeyVal, keyPanel.left + inner, keyPanel.top + 28, keyPanel.Width() - inner * 2, keyPanel.Height() - 38); CRect editPanel(rect.left + pad, varsPanel.bottom + pad, rect.right - pad, varsPanel.bottom + pad + editorH); int labelW = 52; int browseW = 28; int rowH = 22; int rowY = editPanel.top + 30; MoveCtrl(staticKey, editPanel.left + inner, rowY + 2, labelW, rowH); MoveCtrl(editKey, editPanel.left + inner + labelW + 6, rowY, editPanel.Width() - inner * 2 - labelW - 6, rowH); rowY += rowH + 10; MoveCtrl(staticVal, editPanel.left + inner, rowY + 2, labelW, rowH); MoveCtrl(editVal, editPanel.left + inner + labelW + 6, rowY, editPanel.Width() - inner * 2 - labelW - 6 - browseW - 6, rowH); MoveCtrl(btnBrowse, editPanel.right - inner - browseW, rowY, browseW, rowH); CRect toolsPanel(rect.left + pad, editPanel.bottom + pad, rect.right - pad, contentBottom); int toolY = toolsPanel.top + 28; int toolX = toolsPanel.left + inner; int b = 30; MoveCtrl(btn135, toolX, toolY, b, b); MoveCtrl(btn90, toolX + b + 4, toolY, b, b); MoveCtrl(btn45, toolX + (b + 4) * 2, toolY, b, b); MoveCtrl(btn180, toolX, toolY + b + 4, b, b); MoveCtrl(btn360, toolX + (b + 4) * 2, toolY + b + 4, b, b); MoveCtrl(btn225, toolX, toolY + (b + 4) * 2, b, b); MoveCtrl(btn270, toolX + b + 4, toolY + (b + 4) * 2, b, b); MoveCtrl(btn315, toolX + (b + 4) * 2, toolY + (b + 4) * 2, b, b); MoveCtrl(btnUp, toolX + (b + 4) * 3 + 8, toolY + 16, b + 8, b); MoveCtrl(btnDown, toolX + (b + 4) * 3 + 8, toolY + 16 + b + 6, b + 8, b); int mediaX = toolX + 190; int mediaW = 76; int mediaH = 24; MoveCtrl(btnModel, mediaX, toolY, mediaW, mediaH); MoveCtrl(btnSound, mediaX, toolY + mediaH + 6, mediaW, mediaH); MoveCtrl(btnGui, mediaX, toolY + (mediaH + 6) * 2, mediaW, mediaH); MoveCtrl(btnCurve, mediaX + mediaW + 8, toolY, mediaW, mediaH); MoveCtrl(btnSkin, mediaX + mediaW + 8, toolY + mediaH + 6, mediaW, mediaH); MoveCtrl(btnParticle, mediaX + mediaW + 8, toolY + (mediaH + 6) * 2, mediaW, mediaH); int animX = mediaX + (mediaW + 8) * 2 + 18; int animW = max(120, toolsPanel.right - animX - inner); MoveCtrl(cbAnimations, animX, toolY, animW, 24); MoveCtrl(btnPlayAnim, animX, toolY + 32, 54, 24); MoveCtrl(btnStopAnim, animX + 60, toolY + 32, 54, 24); MoveCtrl(slFrameSlider, animX, toolY + 66, max(80, animW - 44), 24); MoveCtrl(staticFrame, animX + animW - 38, toolY + 68, 36, 20); } else { int available = max(1, contentBottom - contentTop); int listH = max(140, (available - editorH - minToolH - pad * 3) / 2); CRect varsPanel(rect.left + pad, contentTop, rect.right - pad, contentTop + listH); CRect keyPanel(rect.left + pad, varsPanel.bottom + pad, rect.right - pad, varsPanel.bottom + pad + listH); MoveCtrl(listVars, varsPanel.left + inner, varsPanel.top + 28, varsPanel.Width() - inner * 2, varsPanel.Height() - 38); MoveCtrl(listKeyVal, keyPanel.left + inner, keyPanel.top + 28, keyPanel.Width() - inner * 2, keyPanel.Height() - 38); CRect editPanel(rect.left + pad, keyPanel.bottom + pad, rect.right - pad, keyPanel.bottom + pad + editorH); int labelW = 48; int browseW = 28; int rowH = 22; int rowY = editPanel.top + 30; MoveCtrl(staticKey, editPanel.left + inner, rowY + 2, labelW, rowH); MoveCtrl(editKey, editPanel.left + inner + labelW + 6, rowY, editPanel.Width() - inner * 2 - labelW - 6, rowH); rowY += rowH + 10; MoveCtrl(staticVal, editPanel.left + inner, rowY + 2, labelW, rowH); MoveCtrl(editVal, editPanel.left + inner + labelW + 6, rowY, editPanel.Width() - inner * 2 - labelW - 6 - browseW - 6, rowH); MoveCtrl(btnBrowse, editPanel.right - inner - browseW, rowY, browseW, rowH); CRect toolsPanel(rect.left + pad, editPanel.bottom + pad, rect.right - pad, contentBottom); int toolY = toolsPanel.top + 28; int toolX = toolsPanel.left + inner; int b = 26; MoveCtrl(btn135, toolX, toolY, b, b); MoveCtrl(btn90, toolX + b + 4, toolY, b, b); MoveCtrl(btn45, toolX + (b + 4) * 2, toolY, b, b); MoveCtrl(btn180, toolX, toolY + b + 4, b, b); MoveCtrl(btn360, toolX + (b + 4) * 2, toolY + b + 4, b, b); MoveCtrl(btn225, toolX, toolY + (b + 4) * 2, b, b); MoveCtrl(btn270, toolX + b + 4, toolY + (b + 4) * 2, b, b); MoveCtrl(btn315, toolX + (b + 4) * 2, toolY + (b + 4) * 2, b, b); MoveCtrl(btnUp, toolX + (b + 4) * 3 + 6, toolY + 12, b + 8, b); MoveCtrl(btnDown, toolX + (b + 4) * 3 + 6, toolY + 12 + b + 6, b + 8, b); int mediaX = toolX + 152; int mediaW = max(58, (toolsPanel.right - mediaX - inner - 8) / 2); int mediaH = 22; MoveCtrl(btnModel, mediaX, toolY, mediaW, mediaH); MoveCtrl(btnSound, mediaX, toolY + mediaH + 5, mediaW, mediaH); MoveCtrl(btnGui, mediaX, toolY + (mediaH + 5) * 2, mediaW, mediaH); MoveCtrl(btnCurve, mediaX + mediaW + 8, toolY, mediaW, mediaH); MoveCtrl(btnSkin, mediaX + mediaW + 8, toolY + mediaH + 5, mediaW, mediaH); MoveCtrl(btnParticle, mediaX + mediaW + 8, toolY + (mediaH + 5) * 2, mediaW, mediaH); int animY = toolY + 88; MoveCtrl(cbAnimations, toolX, animY, toolsPanel.Width() - inner * 2, 22); MoveCtrl(btnPlayAnim, toolX, animY + 28, 54, 22); MoveCtrl(btnStopAnim, toolX + 60, animY + 28, 54, 22); MoveCtrl(slFrameSlider, toolX + 120, animY + 28, toolsPanel.Width() - inner * 2 - 162, 22); MoveCtrl(staticFrame, toolsPanel.right - inner - 36, animY + 30, 36, 18); } Invalidate(FALSE); } // -------------------------------------------------------------------------- // Entity/class/keyval behavior // -------------------------------------------------------------------------- void CEntityDlg::AddClassNames() { comboClass.ResetContent(); for (eclass_t* pec = eclass; pec; pec = pec->next) { comboClass.AddString(pec->name); } } void CEntityDlg::OnCbnSelchangeComboClass() { int index = comboClass.GetCurSel(); if (index != LB_ERR) { CString str; comboClass.GetLBText(index, str); eclass_t* ent = Eclass_ForName(str, false); if (ent) { if (selected_brushes.next == &selected_brushes) { editEntity = world_entity; multipleEntities = false; } else { editEntity = selected_brushes.next->owner; multipleEntities = false; for (brush_t* b = selected_brushes.next->next; b != &selected_brushes; b = b->next) { if (b->owner != editEntity) { multipleEntities = true; break; } } } listVars.ResetContent(); CPropertyItem* pi = new CPropertyItem("Usage:", ent->desc.c_str(), PIT_VAR, ""); listVars.AddPropItem(pi); int c = ent->vars.Num(); for (int i = 0; i < c; i++) { pi = new CPropertyItem(ent->vars[i].name.c_str(), ent->vars[i].desc.c_str(), PIT_VAR, ""); pi->SetData(ent->vars[i].type); listVars.AddPropItem(pi); } listVars.Invalidate(); SetKeyValPairs(); } } } const char* CEntityDlg::TranslateString(const char* buf) { static char buf2[32768]; int l = strlen(buf); char* out = buf2; for (int i = 0; i < l; i++) { if (buf[i] == '\n') { *out++ = '\r'; *out++ = '\n'; } else { *out++ = buf[i]; } } *out++ = 0; return buf2; } void CEntityDlg::UpdateFromListBox() { if (editEntity == NULL) { return; } int c = listKeyVal.GetCount(); for (int i = 0; i < c; i++) { CPropertyItem* pItem = (CPropertyItem*)listKeyVal.GetItemDataPtr(i); if (pItem) { editEntity->epairs.Set(pItem->m_propName, pItem->m_curValue); } } SetKeyValPairs(); } void CEntityDlg::SetKeyValPairs(bool updateAnims) { if (editEntity) { listKeyVal.ResetContent(); int c = editEntity->epairs.GetNumKeyVals(); for (int i = 0; i < c; i++) { const idKeyValue* kv = editEntity->epairs.GetKeyVal(i); CPropertyItem* pi = new CPropertyItem(kv->GetKey().c_str(), kv->GetValue().c_str(), PIT_EDIT, ""); bool found = false; int vc = editEntity->eclass->vars.Num(); for (int j = 0; j < vc; j++) { if (editEntity->eclass->vars[j].name.Icmp(kv->GetKey()) == 0) { switch (editEntity->eclass->vars[j].type) { case EVAR_STRING: case EVAR_INT: case EVAR_FLOAT: pi->m_nItemType = PIT_EDIT; break; case EVAR_BOOL: pi->m_nItemType = PIT_EDIT; break; case EVAR_COLOR: pi->m_nItemType = PIT_COLOR; break; case EVAR_MATERIAL: pi->m_nItemType = PIT_MATERIAL; break; case EVAR_MODEL: pi->m_nItemType = PIT_MODEL; break; case EVAR_GUI: pi->m_nItemType = PIT_GUI; break; case EVAR_SOUND: pi->m_nItemType = PIT_SOUND; break; } found = true; break; } } if (!found) { if (kv->GetKey().Icmp("model") == 0) { pi->m_nItemType = PIT_MODEL; } if (kv->GetKey().Icmp("_color") == 0) { pi->m_nItemType = PIT_COLOR; } if (kv->GetKey().Icmp("gui") == 0) { pi->m_nItemType = PIT_GUI; } if (kv->GetKey().Icmp("gui2") == 0) { pi->m_nItemType = PIT_GUI; } if (kv->GetKey().Icmp("gui3") == 0) { pi->m_nItemType = PIT_GUI; } if (kv->GetKey().Icmp("s_shader") == 0) { pi->m_nItemType = PIT_SOUND; } } listKeyVal.AddPropItem(pi); } if (updateAnims) { int i, num; cbAnimations.ResetContent(); num = gameEdit->ANIM_GetNumAnimsFromEntityDef(&editEntity->eclass->defArgs); for (i = 0; i < num; i++) { cbAnimations.AddString(gameEdit->ANIM_GetAnimNameFromEntityDef(&editEntity->eclass->defArgs, i)); } const idKeyValue* kv = editEntity->epairs.FindKey("anim"); if (kv) { int selIndex = cbAnimations.FindStringExact(0, kv->GetValue().c_str()); if (selIndex != -1) { cbAnimations.SetCurSel(selIndex); OnCbnAnimationChange(); } } } } } void CEntityDlg::UpdateEntitySel(eclass_t* ent) { assert(ent); assert(ent->name); int index = comboClass.FindString(-1, ent->name); if (index != LB_ERR) { comboClass.SetCurSel(index); OnCbnSelchangeComboClass(); } } void CEntityDlg::OnLbnSelchangeListkeyval() { int index = listKeyVal.GetCurSel(); if (index != LB_ERR) { CString str; listKeyVal.GetText(index, str); int i; for (i = 0; str[i] != '\t' && str[i] != '\0'; i++) { } idStr key = str.Left(i); while (str[i] == '\t' && str[i] != '\0') { i++; } idStr val = str.Right(str.GetLength() - i); editKey.SetWindowText(key); editVal.SetWindowText(val); } } static int TabOrder[] = { IDC_COMBO_CLASS, IDC_BUTTON_CREATE, IDC_LIST_KEYVAL, IDC_EDIT_KEY, IDC_EDIT_VAL, IDC_BUTTON_BROWSE, IDC_E_135, IDC_E_90, IDC_E_45, IDC_E_180, IDC_E_0, IDC_E_225, IDC_E_270, IDC_E_315, IDC_E_UP, IDC_E_DOWN, IDC_BUTTON_MODEL, IDC_BUTTON_SOUND, IDC_BUTTON_GUI, IDC_ENTITY_ANIMATIONS }; int TabCount = sizeof(TabOrder) / sizeof(int); void CEntityDlg::DelProp() { CString key; if (editEntity == NULL) { return; } editKey.GetWindowText(key); if (multipleEntities) { for (brush_t* b = selected_brushes.next; b != &selected_brushes; b = b->next) { DeleteKey(b->owner, key); Entity_UpdateCurveData(b->owner); } } else { DeleteKey(editEntity, key); Entity_UpdateCurveData(editEntity); } SetKeyValPairs(); Sys_UpdateWindows(W_ENTITY | W_XY | W_CAMERA); } BOOL CEntityDlg::PreTranslateMessage(MSG* pMsg) { if (pMsg->hwnd == editVal.GetSafeHwnd()) { if (pMsg->message == WM_LBUTTONDOWN) { editVal.SetFocus(); return TRUE; } } if (pMsg->hwnd == editKey.GetSafeHwnd()) { if (pMsg->message == WM_LBUTTONDOWN) { editKey.SetFocus(); return TRUE; } } if (GetFocus() == &editVal || GetFocus() == &editKey) { if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) { AddProp(); return TRUE; } } if (GetFocus() == listKeyVal.GetEditBox()) { if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) { listKeyVal.OnChangeEditBox(); listKeyVal.OnSelchange(); listKeyVal.OnKillfocusEditBox(); AddProp(); SetKeyValPairs(); return TRUE; } } if (GetFocus() == &listKeyVal) { if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_DELETE && editEntity) { DelProp(); return TRUE; } } if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE) { g_pParentWnd->GetCamera()->SetFocus(); Select_Deselect(); return TRUE; } if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) { return TRUE; } if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_TAB) { if (GetFocus()) { int id = GetFocus()->GetDlgCtrlID(); for (int i = 0; i < TabCount; i++) { if (TabOrder[i] == id) { i++; if (i >= TabCount) { i = 0; } CWnd* next = GetDlgItem(TabOrder[i]); if (next) { next->SetFocus(); if (TabOrder[i] == IDC_EDIT_VAL) { editVal.SetSel(0, -1); } return TRUE; } } } } } if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RIGHT && pMsg->hwnd == slFrameSlider.GetSafeHwnd()) { int maxRange = slFrameSlider.GetRangeMax(); if (maxRange <= 0) { return TRUE; } int pos = slFrameSlider.GetPos() + 1; pos = (pos % maxRange); slFrameSlider.SetPos(pos); UpdateFromAnimationFrame(); return TRUE; } if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_LEFT && pMsg->hwnd == slFrameSlider.GetSafeHwnd()) { int maxRange = slFrameSlider.GetRangeMax(); if (maxRange <= 0) { return TRUE; } int pos = slFrameSlider.GetPos() - 1; if (pos < 1) { pos = maxRange; } slFrameSlider.SetPos(pos); UpdateFromAnimationFrame(); return TRUE; } return CDialog::PreTranslateMessage(pMsg); } void CEntityDlg::AddProp() { if (editEntity == NULL) { return; } CString Key, Value; editKey.GetWindowText(Key); editVal.GetWindowText(Value); bool isName = (stricmp(Key, "name") == 0); bool isModel = static_cast((stricmp(Key, "model") == 0 && Value.GetLength() > 0)); bool isOrigin = (idStr::Icmp(Key, "origin") == 0); if (multipleEntities) { for (brush_t* b = selected_brushes.next; b != &selected_brushes; b = b->next) { if (isName) { Entity_SetName(b->owner, Value); } else { if (!((isModel || isOrigin) && (b->owner->eclass->nShowFlags & ECLASS_WORLDSPAWN))) { SetKeyValue(b->owner, Key, Value); } } } } else { if (isName) { Entity_SetName(editEntity, Value); } else { if (!((isModel || isOrigin) && (editEntity->eclass->nShowFlags & ECLASS_WORLDSPAWN))) { SetKeyValue(editEntity, Key, Value); } } if (isModel && !(editEntity->eclass->nShowFlags & ECLASS_WORLDSPAWN)) { idBounds bo; idVec3 mins, maxs; selected_brushes.next->modelHandle = renderModelManager->FindModel(Value); if (dynamic_cast(selected_brushes.next->modelHandle) || dynamic_cast(selected_brushes.next->modelHandle)) { bo.Zero(); bo.ExpandSelf(12.0f); } else { bo = selected_brushes.next->modelHandle->Bounds(NULL); } VectorCopy(bo[0], mins); VectorCopy(bo[1], maxs); VectorAdd(mins, editEntity->origin, mins); VectorAdd(maxs, editEntity->origin, maxs); Brush_RebuildBrush(selected_brushes.next, mins, maxs, false); Brush_Build(selected_brushes.next, false, false, false, true); } } SetKeyValPairs(); Sys_UpdateWindows(W_ALL); } const char* CEntityDlg::AngleKey() { if (editEntity == NULL) { return ""; } if (editEntity->eclass->nShowFlags & ECLASS_MOVER) { return "movedir"; } return "angle"; } void CEntityDlg::OnBnClickedE135() { if (editEntity == NULL) { return; } editKey.SetWindowText(AngleKey()); editVal.SetWindowText("135"); AddProp(); } void CEntityDlg::OnBnClickedE90() { if (editEntity == NULL) { return; } editKey.SetWindowText(AngleKey()); editVal.SetWindowText("90"); AddProp(); } void CEntityDlg::OnBnClickedE45() { if (editEntity == NULL) { return; } editKey.SetWindowText(AngleKey()); editVal.SetWindowText("45"); AddProp(); } void CEntityDlg::OnBnClickedE180() { if (editEntity == NULL) { return; } editKey.SetWindowText(AngleKey()); editVal.SetWindowText("180"); AddProp(); } void CEntityDlg::OnBnClickedE0() { if (editEntity == NULL) { return; } editKey.SetWindowText(AngleKey()); editVal.SetWindowText("0"); AddProp(); } void CEntityDlg::OnBnClickedE225() { if (editEntity == NULL) { return; } editKey.SetWindowText(AngleKey()); editVal.SetWindowText("225"); AddProp(); } void CEntityDlg::OnBnClickedE270() { if (editEntity == NULL) { return; } editKey.SetWindowText(AngleKey()); editVal.SetWindowText("270"); AddProp(); } void CEntityDlg::OnBnClickedE315() { if (editEntity == NULL) { return; } editKey.SetWindowText(AngleKey()); editVal.SetWindowText("315"); AddProp(); } void CEntityDlg::OnBnClickedEUp() { if (editEntity == NULL) { return; } editKey.SetWindowText(AngleKey()); editVal.SetWindowText("-1"); AddProp(); } void CEntityDlg::OnBnClickedEDown() { if (editEntity == NULL) { return; } editKey.SetWindowText(AngleKey()); editVal.SetWindowText("-2"); AddProp(); } // -------------------------------------------------------------------------- // Choosers // -------------------------------------------------------------------------- CPreviewDlg* CEntityDlg::ShowModelChooser() { static CPreviewDlg modelDlg; modelDlg.SetMode(CPreviewDlg::MODELS); modelDlg.SetModal(); if (modelDlg.GetSafeHwnd() == NULL) { modelDlg.Create(MAKEINTRESOURCE(IDD_DIALOG_PREVIEW)); } modelDlg.ShowWindow(SW_SHOW); modelDlg.BringWindowToTop(); while (modelDlg.Waiting()) { } return &modelDlg; } CPreviewDlg* CEntityDlg::ShowParticleChooser() { static CPreviewDlg modelDlg; modelDlg.SetMode(CPreviewDlg::PARTICLES); modelDlg.SetModal(); if (modelDlg.GetSafeHwnd() == NULL) { modelDlg.Create(MAKEINTRESOURCE(IDD_DIALOG_PREVIEW)); } modelDlg.ShowWindow(SW_SHOW); modelDlg.BringWindowToTop(); while (modelDlg.Waiting()) { } return &modelDlg; } CPreviewDlg* CEntityDlg::ShowSkinChooser(entity_t* ent) { static CPreviewDlg modelDlg; modelDlg.SetMode(CPreviewDlg::SKINS); modelDlg.SetModal(); if (modelDlg.GetSafeHwnd() == NULL) { modelDlg.Create(MAKEINTRESOURCE(IDD_DIALOG_PREVIEW)); } modelDlg.RebuildTree((ent) ? ent->epairs.GetString("model") : ""); modelDlg.ShowWindow(SW_SHOW); modelDlg.BringWindowToTop(); while (modelDlg.Waiting()) { } return &modelDlg; } CPreviewDlg* CEntityDlg::ShowGuiChooser() { static CPreviewDlg guiDlg; guiDlg.SetMode(CPreviewDlg::GUIS); guiDlg.SetModal(); if (guiDlg.GetSafeHwnd() == NULL) { guiDlg.Create(MAKEINTRESOURCE(IDD_DIALOG_PREVIEW)); } guiDlg.ShowWindow(SW_SHOW); guiDlg.BringWindowToTop(); while (guiDlg.Waiting()) { } return &guiDlg; } CPreviewDlg* CEntityDlg::ShowSoundChooser() { static CPreviewDlg soundDlg; soundDlg.SetMode(CPreviewDlg::SOUNDS); soundDlg.SetModal(); if (soundDlg.GetSafeHwnd() == NULL) { soundDlg.Create(MAKEINTRESOURCE(IDD_DIALOG_PREVIEW)); } soundDlg.ShowWindow(SW_SHOW); while (soundDlg.Waiting()) { } return &soundDlg; } CPreviewDlg* CEntityDlg::ShowMaterialChooser() { static CPreviewDlg matDlg; matDlg.SetMode(CPreviewDlg::MATERIALS); matDlg.SetModal(); if (matDlg.GetSafeHwnd() == NULL) { matDlg.Create(MAKEINTRESOURCE(IDD_DIALOG_PREVIEW)); } matDlg.ShowWindow(SW_SHOW); matDlg.BringWindowToTop(); while (matDlg.Waiting()) { } return &matDlg; } void CEntityDlg::AssignModel() { OnBnClickedButtonModel(); } void CEntityDlg::OnBnClickedButtonModel() { CPreviewDlg* dlg = ShowModelChooser(); if (dlg->returnCode == IDOK) { editKey.SetWindowText("model"); editVal.SetWindowText(dlg->mediaName); AddProp(); } } void CEntityDlg::OnBnClickedButtonSound() { CPreviewDlg* dlg = ShowSoundChooser(); if (dlg->returnCode == IDOK) { editKey.SetWindowText("s_shader"); editVal.SetWindowText(dlg->mediaName); AddProp(); } } void CEntityDlg::OnBnClickedButtonGui() { CPreviewDlg* dlg = ShowGuiChooser(); if (dlg->returnCode == IDOK) { editKey.SetWindowText("gui"); editVal.SetWindowText(dlg->mediaName); AddProp(); } } void CEntityDlg::OnBnClickedButtonParticle() { CPreviewDlg* dlg = ShowParticleChooser(); if (dlg->returnCode == IDOK) { editKey.SetWindowText("model"); editVal.SetWindowText(dlg->mediaName); AddProp(); } } void CEntityDlg::OnBnClickedButtonSkin() { CPreviewDlg* dlg = ShowSkinChooser(editEntity); if (dlg->returnCode == IDOK) { editKey.SetWindowText("skin"); editVal.SetWindowText(dlg->mediaName); AddProp(); } } void CEntityDlg::OnBnClickedButtonCurve() { CCurveDlg dlg; if (dlg.DoModal() == IDOK) { if (editEntity) { idStr str = "curve_" + dlg.strCurveType; editKey.SetWindowText(str); idVec3 org = editEntity->origin; str = "3 ( "; str += org.ToString(); org.x += 64; str += " "; str += org.ToString(); org.y += 64; str += " "; str += org.ToString(); str += " )"; editVal.SetWindowText(str); AddProp(); Entity_SetCurveData(editEntity); } } } void CEntityDlg::OnBnClickedButtonBrowse() { DelProp(); } void CEntityDlg::OnCbnDblclkComboClass() { } // -------------------------------------------------------------------------- // Entity creation // -------------------------------------------------------------------------- void CEntityDlg::CreateEntity() { entity_t* petNew; bool forceFixed = false; CXYWnd* pWnd = g_pParentWnd->ActiveXY(); if (pWnd) { CRect rctZ; pWnd->GetClientRect(rctZ); if (selected_brushes.next == &selected_brushes) { CreateEntityBrush(g_nSmartX, rctZ.Height() - 1 - g_nSmartY, pWnd); forceFixed = true; } } else { if (selected_brushes.next == &selected_brushes) { MessageBox("You must have a selected brush to create an entity", "info", 0); return; } } int index = comboClass.GetCurSel(); if (index == LB_ERR) { MessageBox("You must have a selected class to create an entity", "info", 0); return; } CString str; comboClass.GetLBText(index, str); if (!stricmp(str, "worldspawn")) { MessageBox("Can't create an entity with worldspawn.", "info", 0); return; } eclass_t* pecNew = Eclass_ForName(str, false); if ((GetAsyncKeyState(VK_CONTROL) & 0x8000)) { extern void Brush_CopyList(brush_t * pFrom, brush_t * pTo); brush_t temp_brushes; temp_brushes.next = &temp_brushes; Brush_CopyList(&selected_brushes, &temp_brushes); Select_Deselect(); brush_t* pBrush = temp_brushes.next; while (pBrush != NULL && pBrush != &temp_brushes) { brush_t* pNext = pBrush->next; Brush_RemoveFromList(pBrush); Brush_AddToList(pBrush, &selected_brushes); pBrush = pNext; petNew = Entity_Create(pecNew, forceFixed); Select_Deselect(); } } else if ((GetAsyncKeyState(VK_SHIFT) & 0x8000)) { Select_Ungroup(); petNew = Entity_Create(pecNew, forceFixed); } else { petNew = Entity_Create(pecNew, forceFixed); } if (petNew == NULL) { MessageBox("Failed to create entity.", "info", 0); return; } if (selected_brushes.next == &selected_brushes) { editEntity = world_entity; } else { editEntity = selected_brushes.next->owner; } SetKeyValPairs(); Select_Deselect(); Select_Brush(editEntity->brushes.onext); Sys_UpdateWindows(W_ALL); } void CEntityDlg::OnBnClickedButtonCreate() { CreateEntity(); } void CEntityDlg::OnLbnDblclkListkeyval() { CString Key, Value; idStr work; editKey.GetWindowText(Key); editVal.GetWindowText(Value); if (stricmp(Key, "script") == 0) { Key = Value; Value = "script/" + Key; if (fileSystem->ReadFile(Value, NULL, NULL) == -1) { sprintf(work, "// Script for %s\n// \n\nvoid main() {\n\n}\n\n", currentmap); fileSystem->WriteFile(Value, work.c_str(), work.Length(), "fs_devpath"); } work = fileSystem->RelativePathToOSPath(Value); WinExec(va("notepad.exe %s", work.c_str()), SW_SHOW); } } void CEntityDlg::OnLbnSelchangeListVars() { } void CEntityDlg::OnLbnDblclkListVars() { if (editEntity == NULL) { return; } int sel = listVars.GetCurSel(); if (sel == LB_ERR) { return; } CPropertyItem* pi = (CPropertyItem*)listVars.GetItemDataPtr(sel); if (pi) { if (editEntity->epairs.FindKey(pi->m_propName) == NULL) { editKey.SetWindowText(pi->m_propName); editVal.SetWindowText(""); editVal.SetFocus(); } } } void CEntityDlg::UpdateKeyVal(const char* key, const char* val) { if (editEntity) { editEntity->epairs.Set(key, val); SetKeyValPairs(); g_pParentWnd->GetCamera()->BuildEntityRenderState(editEntity, true); Entity_UpdateSoundEmitter(editEntity); } } // -------------------------------------------------------------------------- // Animation // -------------------------------------------------------------------------- void CEntityDlg::OnNMReleasedcaptureSlider1(NMHDR* pNMHDR, LRESULT* pResult) { if (!editEntity) { return; } UpdateFromAnimationFrame(); *pResult = 0; } void CEntityDlg::UpdateFromAnimationFrame(bool updateKeyValueDisplay) { if (!editEntity) { return; } int frame = slFrameSlider.GetPos(); editEntity->epairs.SetInt("frame", frame); SetDlgItemText(IDC_ENTITY_CURRENT_ANIM, va("%i", frame)); if (updateKeyValueDisplay) { SetKeyValPairs(); } g_pParentWnd->GetCamera()->BuildEntityRenderState(editEntity, true); Sys_UpdateWindows(W_ALL); } void CEntityDlg::OnCbnAnimationChange() { if (!editEntity) { return; } int sel = cbAnimations.GetCurSel(); CString animName; currentAnimation = NULL; int currFrame = 0; if (sel != -1) { cbAnimations.GetLBText(sel, animName); if (animName.GetLength() > 0) { currFrame = editEntity->epairs.GetInt("frame", "1"); editEntity->epairs.Set("anim", animName.GetBuffer(0)); SetKeyValPairs(false); currentAnimation = gameEdit->ANIM_GetAnimFromEntityDef(editEntity->eclass->name, animName.GetBuffer(0)); currentAnimationFrame = 0; if (currentAnimation) { slFrameSlider.SetRange(1, gameEdit->ANIM_GetNumFrames(currentAnimation), TRUE); slFrameSlider.SetPos(currFrame); currentAnimationFrame = currFrame; } Sys_UpdateWindows(W_ALL); } } } void CEntityDlg::OnBnClickedStartAnimation() { if (!editEntity) { return; } SetTimer(0, 1000 / 24, NULL); } void CEntityDlg::OnBnClickedStopAnimation() { KillTimer(0); } void CEntityDlg::OnTimer(UINT_PTR nIDEvent) { if (!editEntity) { OnBnClickedStopAnimation(); return; } if (currentAnimation) { int numFrames = gameEdit->ANIM_GetNumFrames(currentAnimation); if (numFrames <= 0) { return; } currentAnimationFrame = ((currentAnimationFrame++) % numFrames); editEntity->epairs.SetInt("frame", currentAnimationFrame); slFrameSlider.SetPos(currentAnimationFrame); UpdateFromAnimationFrame(false); Sys_UpdateWindows(W_CAMERA | W_XY); } } // -------------------------------------------------------------------------- // Curves // -------------------------------------------------------------------------- void CEntityDlg::AddCurvePoints() { if (editEntity == NULL || editEntity->curve == NULL) { return; } int c = editEntity->curve->GetNumValues(); idVec3 start; idVec3 end; if (c > 1) { start = editEntity->curve->GetValue(c - 2); end = editEntity->curve->GetValue(c - 1); idVec3 dir = end - start; dir.Normalize(); start = end + 64 * dir; } else if (c > 0) { start = editEntity->curve->GetValue(0); start.x += 64; start.y += 64; } else { start = editEntity->origin; } editEntity->curve->AddValue(editEntity->curve->GetNumValues() * 100, start); if (g_qeglobals.d_select_mode == sel_editpoint) { g_qeglobals.d_select_mode = sel_brush; EditCurvePoints(); } Sys_UpdateWindows(W_CAMERA | W_XY); } void CEntityDlg::EditCurvePoints() { if (editEntity == NULL || editEntity->curve == NULL) { return; } if (g_qeglobals.d_select_mode == sel_editpoint) { g_qeglobals.d_select_mode = sel_brush; return; } g_qeglobals.d_select_mode = sel_editpoint; g_qeglobals.d_numpoints = 0; g_qeglobals.d_num_move_points = 0; int c = editEntity->curve->GetNumValues(); for (int i = 0; i < c; i++) { if (g_qeglobals.d_numpoints < MAX_POINTS - 1) { g_qeglobals.d_points[g_qeglobals.d_numpoints++] = editEntity->curve->GetValue(i); } } Sys_UpdateWindows(W_XY | W_CAMERA); } void CEntityDlg::InsertCurvePoint() { if (editEntity == NULL || editEntity->curve == NULL) { return; } if (g_qeglobals.d_select_mode != sel_editpoint) { return; } if (g_qeglobals.d_num_move_points == 0) { return; } for (int i = 0; i < editEntity->curve->GetNumValues(); i++) { if (PointInMoveList(editEntity->curve->GetValueAddress(i)) >= 0) { if (i == editEntity->curve->GetNumValues() - 1) { AddCurvePoints(); } else { idCurve* newCurve = Entity_MakeCurve(editEntity); if (newCurve == NULL) { return; } for (int j = 0; j < editEntity->curve->GetNumValues(); j++) { if (j == i) { idVec3 start; idVec3 end; if (i > 0) { start = editEntity->curve->GetValue(i - 1); end = editEntity->curve->GetValue(i); start += end; start *= 0.5f; } else { start = editEntity->curve->GetValue(0); if (editEntity->curve->GetNumValues() > 1) { end = start; start = editEntity->curve->GetValue(1); idVec3 dir = end - start; dir.Normalize(); start = end + 64 * dir; } else { end = start; end.x += 64; end.y += 64; } } newCurve->AddValue(newCurve->GetNumValues() * 100, start); } newCurve->AddValue(newCurve->GetNumValues() * 100, editEntity->curve->GetValue(j)); } delete editEntity->curve; editEntity->curve = newCurve; } g_qeglobals.d_num_move_points = 0; break; } } UpdateEntityCurve(); Sys_UpdateWindows(W_XY | W_CAMERA); } void CEntityDlg::DeleteCurvePoint() { if (editEntity == NULL || editEntity->curve == NULL) { return; } if (g_qeglobals.d_select_mode != sel_editpoint) { return; } if (g_qeglobals.d_num_move_points == 0) { return; } for (int i = 0; i < editEntity->curve->GetNumValues(); i++) { if (PointInMoveList(editEntity->curve->GetValueAddress(i)) >= 0) { editEntity->curve->RemoveIndex(i); g_qeglobals.d_num_move_points = 0; break; } } UpdateEntityCurve(); Sys_UpdateWindows(W_XY | W_CAMERA); } void CEntityDlg::UpdateEntityCurve() { if (editEntity == NULL) { return; } Entity_UpdateCurveData(editEntity); if (g_qeglobals.d_select_mode == sel_editpoint) { g_qeglobals.d_numpoints = 0; int c = editEntity->curve->GetNumValues(); for (int i = 0; i < c; i++) { if (g_qeglobals.d_numpoints < MAX_POINTS - 1) { g_qeglobals.d_points[g_qeglobals.d_numpoints++] = editEntity->curve->GetValue(i); } } } Sys_UpdateWindows(W_ENTITY); } void CEntityDlg::SelectCurvePointByRay(const idVec3& org, const idVec3& dir, int buttons) { if (editEntity == NULL) { return; } int besti = -1; float scale = g_pParentWnd->ActiveXY()->Scale(); float bestd = 8 / scale / 2; for (int i = 0; i < g_qeglobals.d_numpoints; i++) { idVec3 temp = g_qeglobals.d_points[i] - org; float d = temp * dir; temp = org + d * dir; temp = g_qeglobals.d_points[i] - temp; d = temp.Length(); if (d <= bestd) { bestd = d; besti = i; } } if (besti == -1) { return; } g_qeglobals.d_num_move_points = 0; assert(besti < editEntity->curve->GetNumValues()); g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = editEntity->curve->GetValueAddress(besti); }