ddnet/src/game/editor/popups.cpp

2323 lines
75 KiB
C++
Raw Normal View History

2010-11-20 10:37:14 +00:00
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/color.h>
#include <engine/console.h>
2010-05-29 07:25:38 +00:00
#include <engine/graphics.h>
#include <engine/input.h>
#include <engine/keys.h>
2022-05-29 16:33:38 +00:00
#include <engine/shared/config.h>
#include <engine/storage.h>
#include <limits>
#include <game/client/ui_scrollregion.h>
2011-04-19 08:34:51 +00:00
#include "editor.h"
2008-01-17 23:09:49 +00:00
// popup menu handling
static struct
{
2010-05-29 07:25:38 +00:00
CUIRect m_Rect;
void *m_pId;
2020-02-28 15:25:27 +00:00
int (*m_pfnFunc)(CEditor *pEditor, CUIRect Rect, void *pContext);
2010-05-29 07:25:38 +00:00
int m_IsMenu;
2020-02-28 15:25:27 +00:00
void *m_pContext;
2010-05-29 07:25:38 +00:00
} s_UiPopups[8];
2008-01-17 23:09:49 +00:00
2010-05-29 07:25:38 +00:00
static int g_UiNumPopups = 0;
2008-01-17 23:09:49 +00:00
2020-02-28 15:25:27 +00:00
void CEditor::UiInvokePopupMenu(void *pID, int Flags, float x, float y, float Width, float Height, int (*pfnFunc)(CEditor *pEditor, CUIRect Rect, void *pContext), void *pContext)
2008-01-17 23:09:49 +00:00
{
2015-05-31 15:34:01 +00:00
if(g_UiNumPopups > 7)
return;
2023-03-27 14:19:35 +00:00
const float Margin = 5.0f;
if(x + Width > UI()->Screen()->w - Margin)
x = maximum<float>(x - Width, Margin);
if(y + Height > UI()->Screen()->h - Margin)
y = maximum<float>(y - Height, Margin);
s_UiPopups[g_UiNumPopups].m_pId = pID;
2010-05-29 07:25:38 +00:00
s_UiPopups[g_UiNumPopups].m_IsMenu = Flags;
s_UiPopups[g_UiNumPopups].m_Rect.x = x;
s_UiPopups[g_UiNumPopups].m_Rect.y = y;
s_UiPopups[g_UiNumPopups].m_Rect.w = Width;
s_UiPopups[g_UiNumPopups].m_Rect.h = Height;
2010-05-29 07:25:38 +00:00
s_UiPopups[g_UiNumPopups].m_pfnFunc = pfnFunc;
2020-02-28 15:25:27 +00:00
s_UiPopups[g_UiNumPopups].m_pContext = pContext;
2010-05-29 07:25:38 +00:00
g_UiNumPopups++;
2008-01-17 23:09:49 +00:00
}
2010-05-29 07:25:38 +00:00
void CEditor::UiDoPopupMenu()
2008-01-17 23:09:49 +00:00
{
2010-05-29 07:25:38 +00:00
for(int i = 0; i < g_UiNumPopups; i++)
2008-01-17 23:09:49 +00:00
{
2010-05-29 07:25:38 +00:00
bool Inside = UI()->MouseInside(&s_UiPopups[i].m_Rect);
UI()->SetHotItem(&s_UiPopups[i].m_pId);
2020-09-24 16:52:31 +00:00
if(Inside)
m_MouseInsidePopup = true;
if(UI()->CheckActiveItem(&s_UiPopups[i].m_pId))
2008-01-17 23:09:49 +00:00
{
2009-10-27 14:38:53 +00:00
if(!UI()->MouseButton(0))
2008-01-17 23:09:49 +00:00
{
2010-05-29 07:25:38 +00:00
if(!Inside)
{
2010-05-29 07:25:38 +00:00
g_UiNumPopups--;
m_PopupEventWasActivated = false;
}
UI()->SetActiveItem(nullptr);
2008-01-17 23:09:49 +00:00
}
}
2010-05-29 07:25:38 +00:00
else if(UI()->HotItem() == &s_UiPopups[i].m_pId)
2008-01-17 23:09:49 +00:00
{
2009-10-27 14:38:53 +00:00
if(UI()->MouseButton(0))
2010-05-29 07:25:38 +00:00
UI()->SetActiveItem(&s_UiPopups[i].m_pId);
2008-01-17 23:09:49 +00:00
}
int Corners = IGraphics::CORNER_ALL;
2010-05-29 07:25:38 +00:00
if(s_UiPopups[i].m_IsMenu)
Corners = IGraphics::CORNER_R | IGraphics::CORNER_B;
2010-05-29 07:25:38 +00:00
CUIRect r = s_UiPopups[i].m_Rect;
r.Draw(ColorRGBA(0.5f, 0.5f, 0.5f, 0.75f), Corners, 3.0f);
2009-10-27 14:38:53 +00:00
r.Margin(1.0f, &r);
r.Draw(ColorRGBA(0, 0, 0, 0.75f), Corners, 3.0f);
2009-10-27 14:38:53 +00:00
r.Margin(4.0f, &r);
if(s_UiPopups[i].m_pfnFunc(this, r, s_UiPopups[i].m_pContext) || UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE))
UiClosePopupMenus(1);
2008-01-17 23:09:49 +00:00
}
}
void CEditor::UiClosePopupMenus(int Menus)
{
if(Menus <= 0)
Menus = g_UiNumPopups;
if(Menus <= 0)
return;
m_LockMouse = false;
UI()->SetActiveItem(nullptr);
g_UiNumPopups = maximum(0, g_UiNumPopups - Menus);
m_PopupEventWasActivated = false;
}
bool CEditor::UiPopupExists(void *pid)
{
for(int i = 0; i < g_UiNumPopups; i++)
{
if(s_UiPopups[i].m_pId == pid)
return true;
}
return false;
}
2008-01-17 23:09:49 +00:00
bool CEditor::UiPopupOpen()
{
return g_UiNumPopups > 0;
}
int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View, void *pContext)
{
static int s_NewMapButton = 0;
static int s_SaveButton = 0;
static int s_SaveAsButton = 0;
static int s_SaveCopyButton = 0;
static int s_OpenButton = 0;
static int s_OpenCurrentMapButton = 0;
static int s_AppendButton = 0;
static int s_ExitButton = 0;
CUIRect Slot;
View.HSplitTop(12.0f, &Slot, &View);
if(pEditor->DoButton_MenuItem(&s_NewMapButton, "New", 0, &Slot, 0, "Creates a new map (ctrl+n)"))
{
if(pEditor->HasUnsavedData())
{
pEditor->m_PopupEventType = POPEVENT_NEW;
pEditor->m_PopupEventActivated = true;
}
else
{
pEditor->Reset();
pEditor->m_aFileName[0] = 0;
}
return 1;
}
2023-04-01 21:17:37 +00:00
View.HSplitTop(10.0f, nullptr, &View);
View.HSplitTop(12.0f, &Slot, &View);
if(pEditor->DoButton_MenuItem(&s_OpenButton, "Load", 0, &Slot, 0, "Opens a map for editing (ctrl+l)"))
{
if(pEditor->HasUnsavedData())
{
pEditor->m_PopupEventType = POPEVENT_LOAD;
pEditor->m_PopupEventActivated = true;
}
else
pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Load map", "Load", "maps", "", pEditor->CallbackOpenMap, pEditor);
return 1;
}
2023-04-01 21:17:37 +00:00
View.HSplitTop(2.0f, nullptr, &View);
View.HSplitTop(12.0f, &Slot, &View);
if(pEditor->DoButton_MenuItem(&s_OpenCurrentMapButton, "Load Current Map", 0, &Slot, 0, "Opens the current in game map for editing (ctrl+alt+l)"))
{
if(pEditor->HasUnsavedData())
{
pEditor->m_PopupEventType = POPEVENT_LOADCURRENT;
pEditor->m_PopupEventActivated = true;
}
else
{
pEditor->LoadCurrentMap();
}
return 1;
}
2023-04-01 21:17:37 +00:00
View.HSplitTop(10.0f, nullptr, &View);
View.HSplitTop(12.0f, &Slot, &View);
if(pEditor->DoButton_MenuItem(&s_AppendButton, "Append", 0, &Slot, 0, "Opens a map and adds everything from that map to the current one (ctrl+a)"))
{
pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Append map", "Append", "maps", "", pEditor->CallbackAppendMap, pEditor);
return 1;
}
2023-04-01 21:17:37 +00:00
View.HSplitTop(10.0f, nullptr, &View);
View.HSplitTop(12.0f, &Slot, &View);
if(pEditor->DoButton_MenuItem(&s_SaveButton, "Save", 0, &Slot, 0, "Saves the current map (ctrl+s)"))
{
if(pEditor->m_aFileName[0] && pEditor->m_ValidSaveFilename)
{
str_copy(pEditor->m_aFileSaveName, pEditor->m_aFileName, sizeof(pEditor->m_aFileSaveName));
pEditor->m_PopupEventType = POPEVENT_SAVE;
pEditor->m_PopupEventActivated = true;
}
else
pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", pEditor->CallbackSaveMap, pEditor);
return 1;
}
2023-04-01 21:17:37 +00:00
View.HSplitTop(2.0f, nullptr, &View);
View.HSplitTop(12.0f, &Slot, &View);
if(pEditor->DoButton_MenuItem(&s_SaveAsButton, "Save As", 0, &Slot, 0, "Saves the current map under a new name (ctrl+shift+s)"))
{
pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", pEditor->CallbackSaveMap, pEditor);
return 1;
}
2023-04-01 21:17:37 +00:00
View.HSplitTop(2.0f, nullptr, &View);
View.HSplitTop(12.0f, &Slot, &View);
if(pEditor->DoButton_MenuItem(&s_SaveCopyButton, "Save Copy", 0, &Slot, 0, "Saves a copy of the current map under a new name (ctrl+shift+alt+s)"))
{
pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", pEditor->CallbackSaveCopyMap, pEditor);
return 1;
}
2023-04-01 21:17:37 +00:00
View.HSplitTop(10.0f, nullptr, &View);
View.HSplitTop(12.0f, &Slot, &View);
if(pEditor->DoButton_MenuItem(&s_ExitButton, "Exit", 0, &Slot, 0, "Exits from the editor"))
{
if(pEditor->HasUnsavedData())
{
pEditor->m_PopupEventType = POPEVENT_EXIT;
pEditor->m_PopupEventActivated = true;
}
else
g_Config.m_ClEditor = 0;
return 1;
}
return 0;
}
int CEditor::PopupMenuTools(CEditor *pEditor, CUIRect View, void *pContext)
{
CUIRect Slot;
View.HSplitTop(12.0f, &Slot, &View);
static int s_RemoveUnusedEnvelopesButton = 0;
static SConfirmPopupContext s_ConfirmPopupContext;
if(pEditor->DoButton_MenuItem(&s_RemoveUnusedEnvelopesButton, "Remove unused envelopes", 0, &Slot, 0, "Removes all unused envelopes from the map"))
{
s_ConfirmPopupContext.Reset();
str_copy(s_ConfirmPopupContext.m_aMessage, "Are you sure that you want to remove all unused envelopes from this map?");
pEditor->ShowPopupConfirm(Slot.x + Slot.w, Slot.y, &s_ConfirmPopupContext);
}
if(s_ConfirmPopupContext.m_Result == SConfirmPopupContext::CONFIRMED)
pEditor->RemoveUnusedEnvelopes();
if(s_ConfirmPopupContext.m_Result != SConfirmPopupContext::UNSET)
{
s_ConfirmPopupContext.Reset();
return 1;
}
return 0;
}
2020-02-28 15:25:27 +00:00
int CEditor::PopupGroup(CEditor *pEditor, CUIRect View, void *pContext)
2008-01-17 23:09:49 +00:00
{
// remove group button
2010-05-29 07:25:38 +00:00
CUIRect Button;
View.HSplitBottom(12.0f, &View, &Button);
static int s_DeleteButton = 0;
// don't allow deletion of game group
if(pEditor->m_Map.m_pGameGroup != pEditor->GetSelectedGroup())
2008-01-17 23:09:49 +00:00
{
2011-03-20 16:04:03 +00:00
if(pEditor->DoButton_Editor(&s_DeleteButton, "Delete group", 0, &Button, 0, "Delete group"))
{
pEditor->m_Map.DeleteGroup(pEditor->m_SelectedGroup);
pEditor->m_SelectedGroup = maximum(0, pEditor->m_SelectedGroup - 1);
return 1;
}
}
else
{
2014-12-01 19:35:30 +00:00
if(pEditor->DoButton_Editor(&s_DeleteButton, "Clean up game tiles", 0, &Button, 0, "Removes game tiles that aren't based on a layer"))
{
// gather all tile layers
2022-06-15 17:34:41 +00:00
std::vector<CLayerTiles *> vpLayers;
for(auto &pLayer : pEditor->m_Map.m_pGameGroup->m_vpLayers)
{
if(pLayer != pEditor->m_Map.m_pGameLayer && pLayer->m_Type == LAYERTYPE_TILES)
2022-06-15 17:34:41 +00:00
vpLayers.push_back(static_cast<CLayerTiles *>(pLayer));
}
// search for unneeded game tiles
CLayerTiles *pGameLayer = pEditor->m_Map.m_pGameLayer;
for(int y = 0; y < pGameLayer->m_Height; ++y)
2023-04-01 21:18:27 +00:00
{
for(int x = 0; x < pGameLayer->m_Width; ++x)
{
if(pGameLayer->m_pTiles[y * pGameLayer->m_Width + x].m_Index > static_cast<unsigned char>(TILE_NOHOOK))
continue;
bool Found = false;
2022-06-15 17:34:41 +00:00
for(const auto &pLayer : vpLayers)
{
if(x < pLayer->m_Width && y < pLayer->m_Height && pLayer->m_pTiles[y * pLayer->m_Width + x].m_Index)
{
Found = true;
break;
}
}
if(!Found)
{
pGameLayer->m_pTiles[y * pGameLayer->m_Width + x].m_Index = TILE_AIR;
pEditor->m_Map.m_Modified = true;
}
}
2023-04-01 21:18:27 +00:00
}
return 1;
}
2008-01-17 23:09:49 +00:00
}
if(pEditor->GetSelectedGroup()->m_GameGroup && !pEditor->m_Map.m_pTeleLayer)
{
// new tele layer
2023-04-01 21:18:27 +00:00
View.HSplitBottom(5.0f, &View, nullptr);
View.HSplitBottom(12.0f, &View, &Button);
2019-11-22 11:28:34 +00:00
static int s_NewTeleLayerButton = 0;
if(pEditor->DoButton_Editor(&s_NewTeleLayerButton, "Add tele layer", 0, &Button, 0, "Creates a new tele layer"))
{
CLayer *pTeleLayer = new CLayerTele(pEditor->m_Map.m_pGameLayer->m_Width, pEditor->m_Map.m_pGameLayer->m_Height);
pEditor->m_Map.MakeTeleLayer(pTeleLayer);
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->AddLayer(pTeleLayer);
pEditor->SelectLayer(pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_vpLayers.size() - 1);
pEditor->m_Brush.Clear();
return 1;
}
}
if(pEditor->GetSelectedGroup()->m_GameGroup && !pEditor->m_Map.m_pSpeedupLayer)
{
// new speedup layer
2023-04-01 21:18:27 +00:00
View.HSplitBottom(5.0f, &View, nullptr);
View.HSplitBottom(12.0f, &View, &Button);
2019-11-22 11:28:34 +00:00
static int s_NewSpeedupLayerButton = 0;
if(pEditor->DoButton_Editor(&s_NewSpeedupLayerButton, "Add speedup layer", 0, &Button, 0, "Creates a new speedup layer"))
{
CLayer *pSpeedupLayer = new CLayerSpeedup(pEditor->m_Map.m_pGameLayer->m_Width, pEditor->m_Map.m_pGameLayer->m_Height);
pEditor->m_Map.MakeSpeedupLayer(pSpeedupLayer);
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->AddLayer(pSpeedupLayer);
pEditor->SelectLayer(pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_vpLayers.size() - 1);
pEditor->m_Brush.Clear();
return 1;
}
}
2015-07-09 00:08:14 +00:00
if(pEditor->GetSelectedGroup()->m_GameGroup && !pEditor->m_Map.m_pTuneLayer)
{
// new tune layer
2023-04-01 21:18:27 +00:00
View.HSplitBottom(5.0f, &View, nullptr);
View.HSplitBottom(12.0f, &View, &Button);
static int s_NewTuneLayerButton = 0;
if(pEditor->DoButton_Editor(&s_NewTuneLayerButton, "Add tune layer", 0, &Button, 0, "Creates a new tuning layer"))
{
CLayer *pTuneLayer = new CLayerTune(pEditor->m_Map.m_pGameLayer->m_Width, pEditor->m_Map.m_pGameLayer->m_Height);
pEditor->m_Map.MakeTuneLayer(pTuneLayer);
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->AddLayer(pTuneLayer);
pEditor->SelectLayer(pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_vpLayers.size() - 1);
pEditor->m_Brush.Clear();
return 1;
}
}
if(pEditor->GetSelectedGroup()->m_GameGroup && !pEditor->m_Map.m_pFrontLayer)
{
2019-11-22 11:28:34 +00:00
// new front layer
2023-04-01 21:18:27 +00:00
View.HSplitBottom(5.0f, &View, nullptr);
View.HSplitBottom(12.0f, &View, &Button);
static int s_NewFrontLayerButton = 0;
2014-12-01 19:35:30 +00:00
if(pEditor->DoButton_Editor(&s_NewFrontLayerButton, "Add front layer", 0, &Button, 0, "Creates a new item layer"))
{
CLayer *pFrontLayer = new CLayerFront(pEditor->m_Map.m_pGameLayer->m_Width, pEditor->m_Map.m_pGameLayer->m_Height);
pEditor->m_Map.MakeFrontLayer(pFrontLayer);
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->AddLayer(pFrontLayer);
pEditor->SelectLayer(pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_vpLayers.size() - 1);
pEditor->m_Brush.Clear();
return 1;
}
}
if(pEditor->GetSelectedGroup()->m_GameGroup && !pEditor->m_Map.m_pSwitchLayer)
{
// new Switch layer
2023-04-01 21:18:27 +00:00
View.HSplitBottom(5.0f, &View, nullptr);
View.HSplitBottom(12.0f, &View, &Button);
static int s_NewSwitchLayerButton = 0;
2014-12-01 19:35:30 +00:00
if(pEditor->DoButton_Editor(&s_NewSwitchLayerButton, "Add switch layer", 0, &Button, 0, "Creates a new switch layer"))
{
CLayer *pSwitchLayer = new CLayerSwitch(pEditor->m_Map.m_pGameLayer->m_Width, pEditor->m_Map.m_pGameLayer->m_Height);
pEditor->m_Map.MakeSwitchLayer(pSwitchLayer);
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->AddLayer(pSwitchLayer);
pEditor->SelectLayer(pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_vpLayers.size() - 1);
pEditor->m_Brush.Clear();
return 1;
}
}
2014-10-08 15:33:06 +00:00
// new quad layer
2023-04-01 21:18:27 +00:00
View.HSplitBottom(5.0f, &View, nullptr);
2010-05-29 07:25:38 +00:00
View.HSplitBottom(12.0f, &View, &Button);
static int s_NewQuadLayerButton = 0;
2011-03-20 16:04:03 +00:00
if(pEditor->DoButton_Editor(&s_NewQuadLayerButton, "Add quads layer", 0, &Button, 0, "Creates a new quad layer"))
2008-01-17 23:09:49 +00:00
{
CLayer *pQuadLayer = new CLayerQuads;
pQuadLayer->m_pEditor = pEditor;
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->AddLayer(pQuadLayer);
pEditor->SelectLayer(pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_vpLayers.size() - 1);
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_Collapse = false;
2008-01-17 23:09:49 +00:00
return 1;
}
2014-10-08 15:33:06 +00:00
// new tile layer
2023-04-01 21:18:27 +00:00
View.HSplitBottom(5.0f, &View, nullptr);
2010-05-29 07:25:38 +00:00
View.HSplitBottom(12.0f, &View, &Button);
static int s_NewTileLayerButton = 0;
2011-03-20 16:04:03 +00:00
if(pEditor->DoButton_Editor(&s_NewTileLayerButton, "Add tile layer", 0, &Button, 0, "Creates a new tile layer"))
2008-01-17 23:09:49 +00:00
{
CLayer *pTileLayer = new CLayerTiles(pEditor->m_Map.m_pGameLayer->m_Width, pEditor->m_Map.m_pGameLayer->m_Height);
pTileLayer->m_pEditor = pEditor;
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->AddLayer(pTileLayer);
pEditor->SelectLayer(pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_vpLayers.size() - 1);
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_Collapse = false;
2008-01-17 23:09:49 +00:00
return 1;
}
2014-10-08 15:33:06 +00:00
// new sound layer
2023-04-01 21:18:27 +00:00
View.HSplitBottom(5.0f, &View, nullptr);
2014-10-08 15:33:06 +00:00
View.HSplitBottom(12.0f, &View, &Button);
static int s_NewSoundLayerButton = 0;
if(pEditor->DoButton_Editor(&s_NewSoundLayerButton, "Add sound layer", 0, &Button, 0, "Creates a new sound layer"))
{
CLayer *pSoundLayer = new CLayerSounds;
pSoundLayer->m_pEditor = pEditor;
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->AddLayer(pSoundLayer);
pEditor->SelectLayer(pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_vpLayers.size() - 1);
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_Collapse = false;
2014-10-08 15:33:06 +00:00
return 1;
}
2011-07-12 01:14:46 +00:00
// group name
if(!pEditor->GetSelectedGroup()->m_GameGroup)
{
2023-04-01 21:18:27 +00:00
View.HSplitBottom(5.0f, &View, nullptr);
2011-07-12 01:14:46 +00:00
View.HSplitBottom(12.0f, &View, &Button);
2022-03-11 16:34:48 +00:00
pEditor->UI()->DoLabel(&Button, "Name:", 10.0f, TEXTALIGN_LEFT);
Button.VSplitLeft(40.0f, nullptr, &Button);
static float s_Name = 0;
if(pEditor->DoEditBox(&s_Name, &Button, pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_aName, sizeof(pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_aName), 10.0f, &s_Name))
2011-07-20 20:18:31 +00:00
pEditor->m_Map.m_Modified = true;
2011-07-12 01:14:46 +00:00
}
2008-01-17 23:09:49 +00:00
enum
{
PROP_ORDER = 0,
2008-01-17 23:09:49 +00:00
PROP_POS_X,
PROP_POS_Y,
PROP_PARA_X,
PROP_PARA_Y,
PROP_CUSTOM_ZOOM,
PROP_PARA_ZOOM,
2008-03-29 11:44:03 +00:00
PROP_USE_CLIPPING,
PROP_CLIP_X,
PROP_CLIP_Y,
PROP_CLIP_W,
PROP_CLIP_H,
2008-01-17 23:09:49 +00:00
NUM_PROPS,
};
2010-05-29 07:25:38 +00:00
CProperty aProps[] = {
{"Order", pEditor->m_SelectedGroup, PROPTYPE_INT_STEP, 0, (int)pEditor->m_Map.m_vpGroups.size() - 1},
{"Pos X", -pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_OffsetX, PROPTYPE_INT_SCROLL, -1000000, 1000000},
{"Pos Y", -pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_OffsetY, PROPTYPE_INT_SCROLL, -1000000, 1000000},
{"Para X", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxX, PROPTYPE_INT_SCROLL, -1000000, 1000000},
{"Para Y", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxY, PROPTYPE_INT_SCROLL, -1000000, 1000000},
{"Custom Zoom", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_CustomParallaxZoom, PROPTYPE_BOOL, 0, 1},
{"Para Zoom", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxZoom, PROPTYPE_INT_SCROLL, -1000000, 1000000},
{"Use Clipping", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_UseClipping, PROPTYPE_BOOL, 0, 1},
{"Clip X", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ClipX, PROPTYPE_INT_SCROLL, -1000000, 1000000},
{"Clip Y", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ClipY, PROPTYPE_INT_SCROLL, -1000000, 1000000},
{"Clip W", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ClipW, PROPTYPE_INT_SCROLL, 0, 1000000},
{"Clip H", pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ClipH, PROPTYPE_INT_SCROLL, 0, 1000000},
{nullptr},
2008-01-17 23:09:49 +00:00
};
2023-04-01 21:18:27 +00:00
// cut the properties that aren't needed
2010-05-29 07:25:38 +00:00
if(pEditor->GetSelectedGroup()->m_GameGroup)
aProps[PROP_POS_X].m_pName = nullptr;
2023-04-01 21:18:27 +00:00
static int s_aIds[NUM_PROPS] = {0};
int NewVal = 0;
2010-05-29 07:25:38 +00:00
int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal);
if(Prop != -1)
2023-04-01 21:18:27 +00:00
{
pEditor->m_Map.m_Modified = true;
2023-04-01 21:18:27 +00:00
}
2010-05-29 07:25:38 +00:00
if(Prop == PROP_ORDER)
2023-04-01 21:18:27 +00:00
{
2010-05-29 07:25:38 +00:00
pEditor->m_SelectedGroup = pEditor->m_Map.SwapGroups(pEditor->m_SelectedGroup, NewVal);
2023-04-01 21:18:27 +00:00
}
2008-01-17 23:09:49 +00:00
// these can not be changed on the game group
2010-05-29 07:25:38 +00:00
if(!pEditor->GetSelectedGroup()->m_GameGroup)
2008-01-17 23:09:49 +00:00
{
if(Prop == PROP_PARA_X)
2023-04-01 21:18:27 +00:00
{
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxX = NewVal;
2023-04-01 21:18:27 +00:00
}
else if(Prop == PROP_PARA_Y)
2023-04-01 21:18:27 +00:00
{
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxY = NewVal;
2023-04-01 21:18:27 +00:00
}
else if(Prop == PROP_CUSTOM_ZOOM)
2023-04-01 21:18:27 +00:00
{
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_CustomParallaxZoom = NewVal;
2023-04-01 21:18:27 +00:00
}
else if(Prop == PROP_PARA_ZOOM)
{
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_CustomParallaxZoom = 1;
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ParallaxZoom = NewVal;
}
else if(Prop == PROP_POS_X)
2023-04-01 21:18:27 +00:00
{
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_OffsetX = -NewVal;
2023-04-01 21:18:27 +00:00
}
else if(Prop == PROP_POS_Y)
2023-04-01 21:18:27 +00:00
{
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_OffsetY = -NewVal;
2023-04-01 21:18:27 +00:00
}
else if(Prop == PROP_USE_CLIPPING)
2023-04-01 21:18:27 +00:00
{
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_UseClipping = NewVal;
2023-04-01 21:18:27 +00:00
}
else if(Prop == PROP_CLIP_X)
2023-04-01 21:18:27 +00:00
{
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ClipX = NewVal;
2023-04-01 21:18:27 +00:00
}
else if(Prop == PROP_CLIP_Y)
2023-04-01 21:18:27 +00:00
{
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ClipY = NewVal;
2023-04-01 21:18:27 +00:00
}
else if(Prop == PROP_CLIP_W)
2023-04-01 21:18:27 +00:00
{
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ClipW = NewVal;
2023-04-01 21:18:27 +00:00
}
else if(Prop == PROP_CLIP_H)
2023-04-01 21:18:27 +00:00
{
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->m_ClipH = NewVal;
2023-04-01 21:18:27 +00:00
}
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->OnEdited();
2008-01-17 23:09:49 +00:00
}
2008-01-17 23:09:49 +00:00
return 0;
}
2020-02-28 15:25:27 +00:00
int CEditor::PopupLayer(CEditor *pEditor, CUIRect View, void *pContext)
2008-01-17 23:09:49 +00:00
{
2020-02-28 15:25:27 +00:00
CLayerPopupContext *pPopup = (CLayerPopupContext *)pContext;
CLayerGroup *pCurrentGroup = pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup];
CLayer *pCurrentLayer = pEditor->GetSelectedLayer(0);
if(pPopup->m_vpLayers.size() > 1)
{
return CLayerTiles::RenderCommonProperties(pPopup->m_CommonPropState, pEditor, &View, pPopup->m_vpLayers);
}
const bool EntitiesLayer = pCurrentLayer->IsEntitiesLayer();
// delete button
if(pEditor->m_Map.m_pGameLayer != pCurrentLayer) // entities layers except the game layer can be deleted
{
CUIRect DeleteButton;
View.HSplitBottom(12.0f, &View, &DeleteButton);
static int s_DeleteButton = 0;
if(pEditor->DoButton_Editor(&s_DeleteButton, "Delete layer", 0, &DeleteButton, 0, "Deletes the layer"))
{
if(pCurrentLayer == pEditor->m_Map.m_pFrontLayer)
pEditor->m_Map.m_pFrontLayer = nullptr;
if(pCurrentLayer == pEditor->m_Map.m_pTeleLayer)
pEditor->m_Map.m_pTeleLayer = nullptr;
if(pCurrentLayer == pEditor->m_Map.m_pSpeedupLayer)
pEditor->m_Map.m_pSpeedupLayer = nullptr;
if(pCurrentLayer == pEditor->m_Map.m_pSwitchLayer)
pEditor->m_Map.m_pSwitchLayer = nullptr;
if(pCurrentLayer == pEditor->m_Map.m_pTuneLayer)
pEditor->m_Map.m_pTuneLayer = nullptr;
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->DeleteLayer(pEditor->m_vSelectedLayers[0]);
return 1;
}
}
// duplicate button
if(!EntitiesLayer) // entities layers cannot be duplicated
2008-01-17 23:09:49 +00:00
{
CUIRect DuplicateButton;
View.HSplitBottom(4.0f, &View, nullptr);
View.HSplitBottom(12.0f, &View, &DuplicateButton);
static int s_DuplicationButton = 0;
if(pEditor->DoButton_Editor(&s_DuplicationButton, "Duplicate layer", 0, &DuplicateButton, 0, "Duplicates the layer"))
{
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->DuplicateLayer(pEditor->m_vSelectedLayers[0]);
return 1;
}
2008-01-17 23:09:49 +00:00
}
2011-07-12 01:14:46 +00:00
// layer name
if(!EntitiesLayer) // name cannot be changed for entities layers
2011-07-12 01:14:46 +00:00
{
CUIRect Label, EditBox;
View.HSplitBottom(5.0f, &View, nullptr);
View.HSplitBottom(12.0f, &View, &Label);
Label.VSplitLeft(40.0f, &Label, &EditBox);
pEditor->UI()->DoLabel(&Label, "Name:", 10.0f, TEXTALIGN_LEFT);
static float s_Name = 0;
if(pEditor->DoEditBox(&s_Name, &EditBox, pCurrentLayer->m_aName, sizeof(pCurrentLayer->m_aName), 10.0f, &s_Name))
2011-07-20 20:18:31 +00:00
pEditor->m_Map.m_Modified = true;
2011-07-12 01:14:46 +00:00
}
// spacing if any button was rendered
if(!EntitiesLayer || pEditor->m_Map.m_pGameLayer != pCurrentLayer)
View.HSplitBottom(10.0f, &View, nullptr);
2008-01-17 23:09:49 +00:00
enum
{
PROP_GROUP = 0,
2008-01-17 23:09:49 +00:00
PROP_ORDER,
PROP_HQ,
2008-01-17 23:09:49 +00:00
NUM_PROPS,
};
2010-05-29 07:25:38 +00:00
CProperty aProps[] = {
{"Group", pEditor->m_SelectedGroup, PROPTYPE_INT_STEP, 0, (int)pEditor->m_Map.m_vpGroups.size() - 1},
{"Order", pEditor->m_vSelectedLayers[0], PROPTYPE_INT_STEP, 0, (int)pCurrentGroup->m_vpLayers.size() - 1},
{"Detail", pCurrentLayer->m_Flags & LAYERFLAG_DETAIL, PROPTYPE_BOOL, 0, 1},
{nullptr},
2008-01-17 23:09:49 +00:00
};
2010-05-29 07:25:38 +00:00
// don't use Group and Detail from the selection if this is an entities layer
if(EntitiesLayer)
2010-05-29 07:25:38 +00:00
{
aProps[0].m_Type = PROPTYPE_NULL;
aProps[2].m_Type = PROPTYPE_NULL;
}
2010-05-29 07:25:38 +00:00
static int s_aIds[NUM_PROPS] = {0};
int NewVal = 0;
int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal);
if(Prop != -1)
2023-04-01 21:19:04 +00:00
{
pEditor->m_Map.m_Modified = true;
2023-04-01 21:19:04 +00:00
}
2010-05-29 07:25:38 +00:00
if(Prop == PROP_ORDER)
2023-04-01 21:19:04 +00:00
{
pEditor->SelectLayer(pCurrentGroup->SwapLayers(pEditor->m_vSelectedLayers[0], NewVal));
2023-04-01 21:19:04 +00:00
}
else if(Prop == PROP_GROUP)
2008-01-17 23:09:49 +00:00
{
if(NewVal >= 0 && (size_t)NewVal < pEditor->m_Map.m_vpGroups.size())
2008-01-17 23:09:49 +00:00
{
auto Position = std::find(pCurrentGroup->m_vpLayers.begin(), pCurrentGroup->m_vpLayers.end(), pCurrentLayer);
if(Position != pCurrentGroup->m_vpLayers.end())
pCurrentGroup->m_vpLayers.erase(Position);
pEditor->m_Map.m_vpGroups[NewVal]->m_vpLayers.push_back(pCurrentLayer);
2010-05-29 07:25:38 +00:00
pEditor->m_SelectedGroup = NewVal;
pEditor->SelectLayer(pEditor->m_Map.m_vpGroups[NewVal]->m_vpLayers.size() - 1);
2008-01-17 23:09:49 +00:00
}
}
else if(Prop == PROP_HQ)
{
2010-05-29 07:25:38 +00:00
pCurrentLayer->m_Flags &= ~LAYERFLAG_DETAIL;
if(NewVal)
pCurrentLayer->m_Flags |= LAYERFLAG_DETAIL;
}
2010-05-29 07:25:38 +00:00
return pCurrentLayer->RenderProperties(&View);
2008-01-17 23:09:49 +00:00
}
2020-02-28 15:25:27 +00:00
int CEditor::PopupQuad(CEditor *pEditor, CUIRect View, void *pContext)
2008-01-17 23:09:49 +00:00
{
2022-06-15 17:34:41 +00:00
std::vector<CQuad *> vpQuads = pEditor->GetSelectedQuads();
CQuad *pCurrentQuad = vpQuads[pEditor->m_SelectedQuadIndex];
2023-04-02 09:43:17 +00:00
CLayerQuads *pLayer = static_cast<CLayerQuads *>(pEditor->GetSelectedLayerType(0, LAYERTYPE_QUADS));
2008-01-17 23:09:49 +00:00
2010-05-29 07:25:38 +00:00
CUIRect Button;
2008-03-02 15:59:52 +00:00
// delete button
2010-05-29 07:25:38 +00:00
View.HSplitBottom(12.0f, &View, &Button);
static int s_DeleteButton = 0;
2011-03-20 16:04:03 +00:00
if(pEditor->DoButton_Editor(&s_DeleteButton, "Delete", 0, &Button, 0, "Deletes the current quad"))
2008-03-02 15:59:52 +00:00
{
2010-05-29 07:25:38 +00:00
if(pLayer)
2008-03-02 15:59:52 +00:00
{
pEditor->m_Map.m_Modified = true;
2018-08-13 09:11:56 +00:00
pEditor->DeleteSelectedQuads();
2008-03-02 15:59:52 +00:00
}
return 1;
}
// aspect ratio button
2023-04-02 09:43:17 +00:00
View.HSplitBottom(10.0f, &View, nullptr);
2011-03-29 10:08:46 +00:00
View.HSplitBottom(12.0f, &View, &Button);
if(pLayer && pLayer->m_Image >= 0 && (size_t)pLayer->m_Image < pEditor->m_Map.m_vpImages.size())
{
static int s_AspectRatioButton = 0;
2011-03-20 16:04:03 +00:00
if(pEditor->DoButton_Editor(&s_AspectRatioButton, "Aspect ratio", 0, &Button, 0, "Resizes the current Quad based on the aspect ratio of the image"))
{
2022-06-15 17:34:41 +00:00
for(auto &pQuad : vpQuads)
{
int Top = pQuad->m_aPoints[0].y;
int Left = pQuad->m_aPoints[0].x;
int Right = pQuad->m_aPoints[0].x;
2018-08-13 09:11:56 +00:00
for(int k = 1; k < 4; k++)
{
if(pQuad->m_aPoints[k].y < Top)
Top = pQuad->m_aPoints[k].y;
if(pQuad->m_aPoints[k].x < Left)
Left = pQuad->m_aPoints[k].x;
if(pQuad->m_aPoints[k].x > Right)
Right = pQuad->m_aPoints[k].x;
2018-08-13 09:11:56 +00:00
}
2023-04-02 09:43:17 +00:00
const int Height = (Right - Left) * pEditor->m_Map.m_vpImages[pLayer->m_Image]->m_Height / pEditor->m_Map.m_vpImages[pLayer->m_Image]->m_Width;
2018-08-13 09:11:56 +00:00
pQuad->m_aPoints[0].x = Left;
pQuad->m_aPoints[0].y = Top;
pQuad->m_aPoints[1].x = Right;
pQuad->m_aPoints[1].y = Top;
pQuad->m_aPoints[2].x = Left;
pQuad->m_aPoints[2].y = Top + Height;
pQuad->m_aPoints[3].x = Right;
pQuad->m_aPoints[3].y = Top + Height;
2018-08-13 09:11:56 +00:00
pEditor->m_Map.m_Modified = true;
}
return 1;
}
2011-03-29 10:08:46 +00:00
}
// align button
2023-04-02 09:43:17 +00:00
View.HSplitBottom(6.0f, &View, nullptr);
2011-03-29 10:08:46 +00:00
View.HSplitBottom(12.0f, &View, &Button);
static int s_AlignButton = 0;
if(pEditor->DoButton_Editor(&s_AlignButton, "Align", 0, &Button, 0, "Aligns coordinates of the quad points"))
{
2022-06-15 17:34:41 +00:00
for(auto &pQuad : vpQuads)
2011-03-29 10:08:46 +00:00
{
2018-08-13 09:11:56 +00:00
for(int k = 1; k < 4; k++)
{
pQuad->m_aPoints[k].x = 1000.0f * (pQuad->m_aPoints[k].x / 1000);
pQuad->m_aPoints[k].y = 1000.0f * (pQuad->m_aPoints[k].y / 1000);
2018-08-13 09:11:56 +00:00
}
pEditor->m_Map.m_Modified = true;
2011-03-29 10:08:46 +00:00
}
return 1;
}
// square button
2023-04-02 09:43:17 +00:00
View.HSplitBottom(6.0f, &View, nullptr);
2010-05-29 07:25:38 +00:00
View.HSplitBottom(12.0f, &View, &Button);
static int s_Button = 0;
2011-03-20 16:04:03 +00:00
if(pEditor->DoButton_Editor(&s_Button, "Square", 0, &Button, 0, "Squares the current quad"))
2008-01-17 23:09:49 +00:00
{
2022-06-15 17:34:41 +00:00
for(auto &pQuad : vpQuads)
2008-01-17 23:09:49 +00:00
{
int Top = pQuad->m_aPoints[0].y;
int Left = pQuad->m_aPoints[0].x;
int Bottom = pQuad->m_aPoints[0].y;
int Right = pQuad->m_aPoints[0].x;
2018-08-13 09:11:56 +00:00
for(int k = 1; k < 4; k++)
{
if(pQuad->m_aPoints[k].y < Top)
Top = pQuad->m_aPoints[k].y;
if(pQuad->m_aPoints[k].x < Left)
Left = pQuad->m_aPoints[k].x;
if(pQuad->m_aPoints[k].y > Bottom)
Bottom = pQuad->m_aPoints[k].y;
if(pQuad->m_aPoints[k].x > Right)
Right = pQuad->m_aPoints[k].x;
2018-08-13 09:11:56 +00:00
}
pQuad->m_aPoints[0].x = Left;
pQuad->m_aPoints[0].y = Top;
pQuad->m_aPoints[1].x = Right;
pQuad->m_aPoints[1].y = Top;
pQuad->m_aPoints[2].x = Left;
pQuad->m_aPoints[2].y = Bottom;
pQuad->m_aPoints[3].x = Right;
pQuad->m_aPoints[3].y = Bottom;
2018-08-13 09:11:56 +00:00
pEditor->m_Map.m_Modified = true;
}
2008-01-17 23:09:49 +00:00
return 1;
}
2022-02-26 17:49:06 +00:00
// slice button
2023-04-02 09:43:17 +00:00
View.HSplitBottom(6.0f, &View, nullptr);
2022-02-26 17:49:06 +00:00
View.HSplitBottom(12.0f, &View, &Button);
static int s_SliceButton = 0;
if(pEditor->DoButton_Editor(&s_SliceButton, "Slice", 0, &Button, 0, "Enables quad knife mode"))
{
pEditor->m_QuadKnifeCount = 0;
pEditor->m_QuadKnifeActive = true;
return 1;
}
2008-01-17 23:09:49 +00:00
enum
{
PROP_ORDER = 0,
PROP_POS_X,
2011-03-29 10:08:46 +00:00
PROP_POS_Y,
PROP_POS_ENV,
2008-01-17 23:09:49 +00:00
PROP_POS_ENV_OFFSET,
PROP_COLOR_ENV,
PROP_COLOR_ENV_OFFSET,
NUM_PROPS,
};
2023-04-02 09:43:17 +00:00
const int NumQuads = pLayer ? (int)pLayer->m_vQuads.size() : 0;
2010-05-29 07:25:38 +00:00
CProperty aProps[] = {
{"Order", pEditor->m_vSelectedQuads[pEditor->m_SelectedQuadIndex], PROPTYPE_INT_STEP, 0, NumQuads},
{"Pos X", fx2i(pCurrentQuad->m_aPoints[4].x), PROPTYPE_INT_SCROLL, -1000000, 1000000},
{"Pos Y", fx2i(pCurrentQuad->m_aPoints[4].y), PROPTYPE_INT_SCROLL, -1000000, 1000000},
2020-09-22 12:47:21 +00:00
{"Pos. Env", pCurrentQuad->m_PosEnv + 1, PROPTYPE_ENVELOPE, 0, 0},
2018-08-13 09:11:56 +00:00
{"Pos. TO", pCurrentQuad->m_PosEnvOffset, PROPTYPE_INT_SCROLL, -1000000, 1000000},
2020-09-22 12:47:21 +00:00
{"Color Env", pCurrentQuad->m_ColorEnv + 1, PROPTYPE_ENVELOPE, 0, 0},
2018-08-13 09:11:56 +00:00
{"Color TO", pCurrentQuad->m_ColorEnvOffset, PROPTYPE_INT_SCROLL, -1000000, 1000000},
{nullptr},
2008-01-17 23:09:49 +00:00
};
2010-05-29 07:25:38 +00:00
static int s_aIds[NUM_PROPS] = {0};
int NewVal = 0;
int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal);
if(Prop != -1)
2023-04-02 09:43:17 +00:00
{
pEditor->m_Map.m_Modified = true;
2023-04-02 09:43:17 +00:00
}
2023-04-02 09:43:17 +00:00
const float OffsetX = i2fx(NewVal) - pCurrentQuad->m_aPoints[4].x;
const float OffsetY = i2fx(NewVal) - pCurrentQuad->m_aPoints[4].y;
2018-08-13 09:11:56 +00:00
if(Prop == PROP_ORDER && pLayer)
{
2023-04-02 09:43:17 +00:00
const int QuadIndex = pLayer->SwapQuads(pEditor->m_vSelectedQuads[pEditor->m_SelectedQuadIndex], NewVal);
pEditor->m_vSelectedQuads[pEditor->m_SelectedQuadIndex] = QuadIndex;
}
2022-06-15 17:34:41 +00:00
for(auto &pQuad : vpQuads)
{
2018-08-13 09:11:56 +00:00
if(Prop == PROP_POS_X)
{
for(auto &Point : pQuad->m_aPoints)
2020-10-26 14:14:07 +00:00
Point.x += OffsetX;
}
2023-04-02 09:43:17 +00:00
else if(Prop == PROP_POS_Y)
{
for(auto &Point : pQuad->m_aPoints)
2020-10-26 14:14:07 +00:00
Point.y += OffsetY;
2018-08-13 09:11:56 +00:00
}
2023-04-02 09:43:17 +00:00
else if(Prop == PROP_POS_ENV)
2018-08-13 09:11:56 +00:00
{
int Index = clamp(NewVal - 1, -1, (int)pEditor->m_Map.m_vpEnvelopes.size() - 1);
int StepDirection = Index < pQuad->m_PosEnv ? -1 : 1;
if(StepDirection != 0)
2018-08-13 09:11:56 +00:00
{
for(; Index >= -1 && Index < (int)pEditor->m_Map.m_vpEnvelopes.size(); Index += StepDirection)
2023-04-02 09:43:17 +00:00
{
if(Index == -1 || pEditor->m_Map.m_vpEnvelopes[Index]->m_Channels == 3)
2018-08-13 09:11:56 +00:00
{
pQuad->m_PosEnv = Index;
2018-08-13 09:11:56 +00:00
break;
}
2023-04-02 09:43:17 +00:00
}
2018-08-13 09:11:56 +00:00
}
}
2023-04-02 09:43:17 +00:00
else if(Prop == PROP_POS_ENV_OFFSET)
{
pQuad->m_PosEnvOffset = NewVal;
2023-04-02 09:43:17 +00:00
}
else if(Prop == PROP_COLOR_ENV)
2018-08-13 09:11:56 +00:00
{
int Index = clamp(NewVal - 1, -1, (int)pEditor->m_Map.m_vpEnvelopes.size() - 1);
int StepDirection = Index < pQuad->m_ColorEnv ? -1 : 1;
if(StepDirection != 0)
2018-08-13 09:11:56 +00:00
{
for(; Index >= -1 && Index < (int)pEditor->m_Map.m_vpEnvelopes.size(); Index += StepDirection)
2023-04-02 09:43:17 +00:00
{
if(Index == -1 || pEditor->m_Map.m_vpEnvelopes[Index]->m_Channels == 4)
2018-08-13 09:11:56 +00:00
{
pQuad->m_ColorEnv = Index;
2018-08-13 09:11:56 +00:00
break;
}
2023-04-02 09:43:17 +00:00
}
2018-08-13 09:11:56 +00:00
}
}
2023-04-02 09:43:17 +00:00
else if(Prop == PROP_COLOR_ENV_OFFSET)
{
pQuad->m_ColorEnvOffset = NewVal;
2023-04-02 09:43:17 +00:00
}
}
2008-01-17 23:09:49 +00:00
return 0;
}
2020-02-28 15:25:27 +00:00
int CEditor::PopupSource(CEditor *pEditor, CUIRect View, void *pContext)
{
CSoundSource *pSource = pEditor->GetSelectedSource();
CUIRect Button;
// delete button
View.HSplitBottom(12.0f, &View, &Button);
static int s_DeleteButton = 0;
if(pEditor->DoButton_Editor(&s_DeleteButton, "Delete", 0, &Button, 0, "Deletes the current source"))
{
CLayerSounds *pLayer = (CLayerSounds *)pEditor->GetSelectedLayerType(0, LAYERTYPE_SOUNDS);
if(pLayer)
{
pEditor->m_Map.m_Modified = true;
pLayer->m_vSources.erase(pLayer->m_vSources.begin() + pEditor->m_SelectedSource);
pEditor->m_SelectedSource--;
}
return 1;
}
// Sound shape button
CUIRect ShapeButton;
View.HSplitBottom(3.0f, &View, nullptr);
View.HSplitBottom(12.0f, &View, &ShapeButton);
2023-04-01 21:19:30 +00:00
static const char *s_apShapeNames[CSoundShape::NUM_SHAPES] = {
"Rectangle",
"Circle"};
pSource->m_Shape.m_Type = pSource->m_Shape.m_Type % CSoundShape::NUM_SHAPES; // prevent out of array errors
2014-11-30 10:33:44 +00:00
2023-04-01 21:19:30 +00:00
static int s_ShapeTypeButton = 0;
if(pEditor->DoButton_Editor(&s_ShapeTypeButton, s_apShapeNames[pSource->m_Shape.m_Type], 0, &ShapeButton, 0, "Change shape"))
{
pSource->m_Shape.m_Type = (pSource->m_Shape.m_Type + 1) % CSoundShape::NUM_SHAPES;
// set default values
switch(pSource->m_Shape.m_Type)
{
case CSoundShape::SHAPE_CIRCLE:
{
pSource->m_Shape.m_Circle.m_Radius = 1000.0f;
break;
}
case CSoundShape::SHAPE_RECTANGLE:
{
pSource->m_Shape.m_Rectangle.m_Width = f2fx(1000.0f);
pSource->m_Shape.m_Rectangle.m_Height = f2fx(800.0f);
break;
}
}
}
2015-07-09 00:08:14 +00:00
enum
{
PROP_POS_X = 0,
PROP_POS_Y,
PROP_LOOP,
PROP_PAN,
PROP_TIME_DELAY,
PROP_FALLOFF,
PROP_POS_ENV,
PROP_POS_ENV_OFFSET,
2014-10-11 14:05:36 +00:00
PROP_SOUND_ENV,
PROP_SOUND_ENV_OFFSET,
NUM_PROPS,
};
CProperty aProps[] = {
2020-09-22 13:42:20 +00:00
{"Pos X", pSource->m_Position.x / 1000, PROPTYPE_INT_SCROLL, -1000000, 1000000},
{"Pos Y", pSource->m_Position.y / 1000, PROPTYPE_INT_SCROLL, -1000000, 1000000},
{"Loop", pSource->m_Loop, PROPTYPE_BOOL, 0, 1},
{"Pan", pSource->m_Pan, PROPTYPE_BOOL, 0, 1},
{"Delay", pSource->m_TimeDelay, PROPTYPE_INT_SCROLL, 0, 1000000},
{"Falloff", pSource->m_Falloff, PROPTYPE_INT_SCROLL, 0, 255},
2020-09-22 13:42:20 +00:00
{"Pos. Env", pSource->m_PosEnv + 1, PROPTYPE_ENVELOPE, 0, 0},
{"Pos. TO", pSource->m_PosEnvOffset, PROPTYPE_INT_SCROLL, -1000000, 1000000},
2020-09-22 13:42:20 +00:00
{"Sound Env", pSource->m_SoundEnv + 1, PROPTYPE_ENVELOPE, 0, 0},
2020-09-22 12:50:04 +00:00
{"Sound. TO", pSource->m_SoundEnvOffset, PROPTYPE_INT_SCROLL, -1000000, 1000000},
{nullptr},
};
static int s_aIds[NUM_PROPS] = {0};
int NewVal = 0;
int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal);
if(Prop != -1)
2023-04-01 21:19:30 +00:00
{
pEditor->m_Map.m_Modified = true;
2023-04-01 21:19:30 +00:00
}
if(Prop == PROP_POS_X)
2023-04-01 21:19:30 +00:00
{
pSource->m_Position.x = NewVal * 1000;
2023-04-01 21:19:30 +00:00
}
else if(Prop == PROP_POS_Y)
{
pSource->m_Position.y = NewVal * 1000;
2023-04-01 21:19:30 +00:00
}
else if(Prop == PROP_LOOP)
{
pSource->m_Loop = NewVal;
2023-04-01 21:19:30 +00:00
}
else if(Prop == PROP_PAN)
{
pSource->m_Pan = NewVal;
2023-04-01 21:19:30 +00:00
}
else if(Prop == PROP_TIME_DELAY)
{
pSource->m_TimeDelay = NewVal;
2023-04-01 21:19:30 +00:00
}
else if(Prop == PROP_FALLOFF)
{
pSource->m_Falloff = NewVal;
2023-04-01 21:19:30 +00:00
}
else if(Prop == PROP_POS_ENV)
{
int Index = clamp(NewVal - 1, -1, (int)pEditor->m_Map.m_vpEnvelopes.size() - 1);
2023-04-01 21:19:30 +00:00
const int StepDirection = Index < pSource->m_PosEnv ? -1 : 1;
for(; Index >= -1 && Index < (int)pEditor->m_Map.m_vpEnvelopes.size(); Index += StepDirection)
{
2023-04-01 21:19:30 +00:00
if(Index == -1 || pEditor->m_Map.m_vpEnvelopes[Index]->m_Channels == 3)
{
pSource->m_PosEnv = Index;
break;
}
}
}
2023-04-01 21:19:30 +00:00
else if(Prop == PROP_POS_ENV_OFFSET)
{
pSource->m_PosEnvOffset = NewVal;
2023-04-01 21:19:30 +00:00
}
else if(Prop == PROP_SOUND_ENV)
2014-10-11 14:05:36 +00:00
{
int Index = clamp(NewVal - 1, -1, (int)pEditor->m_Map.m_vpEnvelopes.size() - 1);
2023-04-01 21:19:30 +00:00
const int StepDirection = Index < pSource->m_SoundEnv ? -1 : 1;
for(; Index >= -1 && Index < (int)pEditor->m_Map.m_vpEnvelopes.size(); Index += StepDirection)
2014-10-11 14:05:36 +00:00
{
2023-04-01 21:19:30 +00:00
if(Index == -1 || pEditor->m_Map.m_vpEnvelopes[Index]->m_Channels == 1)
{
pSource->m_SoundEnv = Index;
break;
}
2014-10-11 14:05:36 +00:00
}
}
2023-04-01 21:19:30 +00:00
else if(Prop == PROP_SOUND_ENV_OFFSET)
{
pSource->m_SoundEnvOffset = NewVal;
2023-04-01 21:19:30 +00:00
}
// source shape properties
switch(pSource->m_Shape.m_Type)
{
case CSoundShape::SHAPE_CIRCLE:
{
enum
{
PROP_CIRCLE_RADIUS = 0,
NUM_CIRCLE_PROPS,
};
2015-07-09 00:08:14 +00:00
CProperty aCircleProps[] = {
{"Radius", pSource->m_Shape.m_Circle.m_Radius, PROPTYPE_INT_SCROLL, 0, 1000000},
{nullptr},
};
static int s_aCircleIds[NUM_CIRCLE_PROPS] = {0};
NewVal = 0;
Prop = pEditor->DoProperties(&View, aCircleProps, s_aCircleIds, &NewVal);
if(Prop != -1)
2023-04-01 21:19:30 +00:00
{
pEditor->m_Map.m_Modified = true;
2023-04-01 21:19:30 +00:00
}
if(Prop == PROP_CIRCLE_RADIUS)
2023-04-01 21:19:30 +00:00
{
pSource->m_Shape.m_Circle.m_Radius = NewVal;
2023-04-01 21:19:30 +00:00
}
break;
}
2015-07-09 00:08:14 +00:00
case CSoundShape::SHAPE_RECTANGLE:
{
enum
{
PROP_RECTANGLE_WIDTH = 0,
PROP_RECTANGLE_HEIGHT,
NUM_RECTANGLE_PROPS,
};
2015-07-09 00:08:14 +00:00
CProperty aRectangleProps[] = {
{"Width", pSource->m_Shape.m_Rectangle.m_Width / 1024, PROPTYPE_INT_SCROLL, 0, 1000000},
{"Height", pSource->m_Shape.m_Rectangle.m_Height / 1024, PROPTYPE_INT_SCROLL, 0, 1000000},
{nullptr},
};
static int s_aRectangleIds[NUM_RECTANGLE_PROPS] = {0};
NewVal = 0;
Prop = pEditor->DoProperties(&View, aRectangleProps, s_aRectangleIds, &NewVal);
if(Prop != -1)
2023-04-01 21:19:30 +00:00
{
pEditor->m_Map.m_Modified = true;
2023-04-01 21:19:30 +00:00
}
if(Prop == PROP_RECTANGLE_WIDTH)
2023-04-01 21:19:30 +00:00
{
pSource->m_Shape.m_Rectangle.m_Width = NewVal * 1024;
2023-04-01 21:19:30 +00:00
}
else if(Prop == PROP_RECTANGLE_HEIGHT)
{
pSource->m_Shape.m_Rectangle.m_Height = NewVal * 1024;
2023-04-01 21:19:30 +00:00
}
break;
}
}
return 0;
}
2020-02-28 15:25:27 +00:00
int CEditor::PopupPoint(CEditor *pEditor, CUIRect View, void *pContext)
2008-01-17 23:09:49 +00:00
{
2022-06-15 17:34:41 +00:00
std::vector<CQuad *> vpQuads = pEditor->GetSelectedQuads();
CQuad *pCurrentQuad = vpQuads[pEditor->m_SelectedQuadIndex];
2008-01-17 23:09:49 +00:00
enum
{
PROP_POS_X = 0,
2011-03-29 10:08:46 +00:00
PROP_POS_Y,
PROP_COLOR,
PROP_TEX_U,
PROP_TEX_V,
2008-01-17 23:09:49 +00:00
NUM_PROPS,
};
2010-05-29 07:25:38 +00:00
int Color = 0;
Color |= pCurrentQuad->m_aColors[pEditor->m_SelectedQuadPoint].r << 24;
Color |= pCurrentQuad->m_aColors[pEditor->m_SelectedQuadPoint].g << 16;
Color |= pCurrentQuad->m_aColors[pEditor->m_SelectedQuadPoint].b << 8;
2018-08-13 09:11:56 +00:00
Color |= pCurrentQuad->m_aColors[pEditor->m_SelectedQuadPoint].a;
2023-04-01 21:19:48 +00:00
const int X = fx2i(pCurrentQuad->m_aPoints[pEditor->m_SelectedQuadPoint].x);
const int Y = fx2i(pCurrentQuad->m_aPoints[pEditor->m_SelectedQuadPoint].y);
const int TextureU = fx2f(pCurrentQuad->m_aTexcoords[pEditor->m_SelectedQuadPoint].x) * 1024;
const int TextureV = fx2f(pCurrentQuad->m_aTexcoords[pEditor->m_SelectedQuadPoint].y) * 1024;
2010-05-29 07:25:38 +00:00
CProperty aProps[] = {
2023-04-01 21:19:48 +00:00
{"Pos X", X, PROPTYPE_INT_SCROLL, -1000000, 1000000},
{"Pos Y", Y, PROPTYPE_INT_SCROLL, -1000000, 1000000},
{"Color", Color, PROPTYPE_COLOR, 0, 0},
2023-04-01 21:19:48 +00:00
{"Tex U", TextureU, PROPTYPE_INT_SCROLL, -1000000, 1000000},
{"Tex V", TextureV, PROPTYPE_INT_SCROLL, -1000000, 1000000},
{nullptr},
2008-01-17 23:09:49 +00:00
};
2010-05-29 07:25:38 +00:00
static int s_aIds[NUM_PROPS] = {0};
int NewVal = 0;
2011-03-29 10:08:46 +00:00
int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal);
if(Prop != -1)
2023-04-01 21:19:48 +00:00
{
2011-03-29 10:08:46 +00:00
pEditor->m_Map.m_Modified = true;
2023-04-01 21:19:48 +00:00
}
2011-03-29 10:08:46 +00:00
2022-06-15 17:34:41 +00:00
for(auto &pQuad : vpQuads)
2008-01-17 23:09:49 +00:00
{
2018-08-13 09:11:56 +00:00
if(Prop == PROP_POS_X)
{
for(int v = 0; v < 4; v++)
2020-09-22 12:47:21 +00:00
if(pEditor->m_SelectedPoints & (1 << v))
2023-04-01 21:19:48 +00:00
pQuad->m_aPoints[v].x = i2fx(fx2i(pQuad->m_aPoints[v].x) + NewVal - X);
2018-08-13 09:11:56 +00:00
}
2023-04-01 21:19:48 +00:00
else if(Prop == PROP_POS_Y)
2008-01-17 23:09:49 +00:00
{
2018-08-13 09:11:56 +00:00
for(int v = 0; v < 4; v++)
2020-09-22 12:47:21 +00:00
if(pEditor->m_SelectedPoints & (1 << v))
2023-04-01 21:19:48 +00:00
pQuad->m_aPoints[v].y = i2fx(fx2i(pQuad->m_aPoints[v].y) + NewVal - Y);
2018-08-13 09:11:56 +00:00
}
2023-04-01 21:19:48 +00:00
else if(Prop == PROP_COLOR)
2018-08-13 09:11:56 +00:00
{
for(int v = 0; v < 4; v++)
2008-01-17 23:09:49 +00:00
{
if(pEditor->m_SelectedPoints & (1 << v))
2018-08-13 09:11:56 +00:00
{
pQuad->m_aColors[v].r = (NewVal >> 24) & 0xff;
pQuad->m_aColors[v].g = (NewVal >> 16) & 0xff;
pQuad->m_aColors[v].b = (NewVal >> 8) & 0xff;
pQuad->m_aColors[v].a = NewVal & 0xff;
2018-08-13 09:11:56 +00:00
}
2008-01-17 23:09:49 +00:00
}
}
2023-04-01 21:19:48 +00:00
else if(Prop == PROP_TEX_U)
{
for(int v = 0; v < 4; v++)
2020-09-22 12:47:21 +00:00
if(pEditor->m_SelectedPoints & (1 << v))
2023-04-01 21:19:48 +00:00
pQuad->m_aTexcoords[v].x = f2fx(fx2f(pQuad->m_aTexcoords[v].x) + (NewVal - TextureU) / 1024.0f);
}
2023-04-01 21:19:48 +00:00
else if(Prop == PROP_TEX_V)
{
for(int v = 0; v < 4; v++)
2020-09-22 12:47:21 +00:00
if(pEditor->m_SelectedPoints & (1 << v))
2023-04-01 21:19:48 +00:00
pQuad->m_aTexcoords[v].y = f2fx(fx2f(pQuad->m_aTexcoords[v].y) + (NewVal - TextureV) / 1024.0f);
}
2008-01-17 23:09:49 +00:00
}
return 0;
2008-01-17 23:09:49 +00:00
}
static int gs_ModifyIndexDeletedIndex;
static void ModifyIndexDeleted(int *pIndex)
{
if(*pIndex == gs_ModifyIndexDeletedIndex)
*pIndex = -1;
else if(*pIndex > gs_ModifyIndexDeletedIndex)
*pIndex = *pIndex - 1;
}
int CEditor::PopupImage(CEditor *pEditor, CUIRect View, void *pContext)
{
static int s_ReaddButton = 0;
static int s_ReplaceButton = 0;
static int s_RemoveButton = 0;
CUIRect Slot;
View.HSplitTop(12.0f, &Slot, &View);
CEditorImage *pImg = pEditor->m_Map.m_vpImages[pEditor->m_SelectedImage];
static int s_ExternalButton = 0;
if(pImg->m_External)
{
if(pEditor->DoButton_MenuItem(&s_ExternalButton, "Embed", 0, &Slot, 0, "Embeds the image into the map file."))
{
pImg->m_External = 0;
return 1;
}
View.HSplitTop(5.0f, nullptr, &View);
View.HSplitTop(12.0f, &Slot, &View);
}
else if(pEditor->IsVanillaImage(pImg->m_aName))
{
if(pEditor->DoButton_MenuItem(&s_ExternalButton, "Make external", 0, &Slot, 0, "Removes the image from the map file."))
{
pImg->m_External = 1;
return 1;
}
View.HSplitTop(5.0f, nullptr, &View);
View.HSplitTop(12.0f, &Slot, &View);
}
static SSelectionPopupContext s_SelectionPopupContext;
if(pEditor->DoButton_MenuItem(&s_ReaddButton, "Readd", 0, &Slot, 0, "Reloads the image from the mapres folder"))
{
char aFilename[IO_MAX_PATH_LENGTH];
str_format(aFilename, sizeof(aFilename), "%s.png", pImg->m_aName);
s_SelectionPopupContext.Reset();
pEditor->Storage()->FindFiles(aFilename, "mapres", IStorage::TYPE_ALL, &s_SelectionPopupContext.m_Entries);
if(s_SelectionPopupContext.m_Entries.empty())
{
pEditor->ShowFileDialogError("Error: could not find image '%s' in the mapres folder.", aFilename);
}
else if(s_SelectionPopupContext.m_Entries.size() == 1)
{
s_SelectionPopupContext.m_pSelection = &*s_SelectionPopupContext.m_Entries.begin();
}
else
{
str_copy(s_SelectionPopupContext.m_aMessage, "Select the wanted image:");
pEditor->ShowPopupSelection(pEditor->UI()->MouseX(), pEditor->UI()->MouseY(), &s_SelectionPopupContext);
}
}
if(s_SelectionPopupContext.m_pSelection != nullptr)
{
bool WasExternal = pImg->m_External;
ReplaceImage(s_SelectionPopupContext.m_pSelection->c_str(), IStorage::TYPE_ALL, pEditor);
pImg->m_External = WasExternal;
s_SelectionPopupContext.Reset();
return 1;
}
View.HSplitTop(5.0f, nullptr, &View);
View.HSplitTop(12.0f, &Slot, &View);
if(pEditor->DoButton_MenuItem(&s_ReplaceButton, "Replace", 0, &Slot, 0, "Replaces the image with a new one"))
{
pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Replace Image", "Replace", "mapres", "", ReplaceImage, pEditor);
return 1;
}
View.HSplitTop(5.0f, nullptr, &View);
View.HSplitTop(12.0f, &Slot, &View);
if(pEditor->DoButton_MenuItem(&s_RemoveButton, "Remove", 0, &Slot, 0, "Removes the image from the map"))
{
delete pImg;
pEditor->m_Map.m_vpImages.erase(pEditor->m_Map.m_vpImages.begin() + pEditor->m_SelectedImage);
gs_ModifyIndexDeletedIndex = pEditor->m_SelectedImage;
pEditor->m_Map.ModifyImageIndex(ModifyIndexDeleted);
return 1;
}
return 0;
}
int CEditor::PopupSound(CEditor *pEditor, CUIRect View, void *pContext)
{
static int s_ReaddButton = 0;
static int s_ReplaceButton = 0;
static int s_RemoveButton = 0;
CUIRect Slot;
View.HSplitTop(12.0f, &Slot, &View);
CEditorSound *pSound = pEditor->m_Map.m_vpSounds[pEditor->m_SelectedSound];
static SSelectionPopupContext s_SelectionPopupContext;
if(pEditor->DoButton_MenuItem(&s_ReaddButton, "Readd", 0, &Slot, 0, "Reloads the sound from the mapres folder"))
{
char aFilename[IO_MAX_PATH_LENGTH];
str_format(aFilename, sizeof(aFilename), "%s.opus", pSound->m_aName);
s_SelectionPopupContext.Reset();
pEditor->Storage()->FindFiles(aFilename, "mapres", IStorage::TYPE_ALL, &s_SelectionPopupContext.m_Entries);
if(s_SelectionPopupContext.m_Entries.empty())
{
pEditor->ShowFileDialogError("Error: could not find sound '%s' in the mapres folder.", aFilename);
}
else if(s_SelectionPopupContext.m_Entries.size() == 1)
{
s_SelectionPopupContext.m_pSelection = &*s_SelectionPopupContext.m_Entries.begin();
}
else
{
str_copy(s_SelectionPopupContext.m_aMessage, "Select the wanted sound:");
pEditor->ShowPopupSelection(pEditor->UI()->MouseX(), pEditor->UI()->MouseY(), &s_SelectionPopupContext);
}
}
if(s_SelectionPopupContext.m_pSelection != nullptr)
{
ReplaceSound(s_SelectionPopupContext.m_pSelection->c_str(), IStorage::TYPE_ALL, pEditor);
s_SelectionPopupContext.Reset();
return 1;
}
View.HSplitTop(5.0f, nullptr, &View);
View.HSplitTop(12.0f, &Slot, &View);
if(pEditor->DoButton_MenuItem(&s_ReplaceButton, "Replace", 0, &Slot, 0, "Replaces the sound with a new one"))
{
pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_SOUND, "Replace sound", "Replace", "mapres", "", ReplaceSound, pEditor);
return 1;
}
View.HSplitTop(5.0f, nullptr, &View);
View.HSplitTop(12.0f, &Slot, &View);
if(pEditor->DoButton_MenuItem(&s_RemoveButton, "Remove", 0, &Slot, 0, "Removes the sound from the map"))
{
delete pSound;
pEditor->m_Map.m_vpSounds.erase(pEditor->m_Map.m_vpSounds.begin() + pEditor->m_SelectedSound);
gs_ModifyIndexDeletedIndex = pEditor->m_SelectedSound;
pEditor->m_Map.ModifySoundIndex(ModifyIndexDeleted);
return 1;
}
return 0;
}
2020-02-28 15:25:27 +00:00
int CEditor::PopupNewFolder(CEditor *pEditor, CUIRect View, void *pContext)
{
CUIRect Label, ButtonBar, Button;
View.Margin(10.0f, &View);
View.HSplitBottom(20.0f, &View, &ButtonBar);
// title
View.HSplitTop(20.0f, &Label, &View);
pEditor->UI()->DoLabel(&Label, "Create new folder", 20.0f, TEXTALIGN_CENTER);
View.HSplitTop(10.0f, nullptr, &View);
// folder name
View.HSplitTop(20.0f, &Label, &View);
pEditor->UI()->DoLabel(&Label, "Name:", 10.0f, TEXTALIGN_LEFT);
Label.VSplitLeft(50.0f, nullptr, &Button);
Button.HMargin(2.0f, &Button);
static float s_FolderBox = 0;
pEditor->DoEditBox(&s_FolderBox, &Button, pEditor->m_aFileDialogNewFolderName, sizeof(pEditor->m_aFileDialogNewFolderName), 12.0f, &s_FolderBox);
// button bar
ButtonBar.VSplitLeft(110.0f, &Button, &ButtonBar);
static int s_CancelButton = 0;
if(pEditor->DoButton_Editor(&s_CancelButton, "Cancel", 0, &Button, 0, nullptr))
return 1;
ButtonBar.VSplitRight(110.0f, &ButtonBar, &Button);
static int s_CreateButton = 0;
if(pEditor->DoButton_Editor(&s_CreateButton, "Create", 0, &Button, 0, nullptr) || pEditor->UI()->ConsumeHotkey(CUI::HOTKEY_ENTER))
{
// create the folder
if(pEditor->m_aFileDialogNewFolderName[0])
{
char aBuf[IO_MAX_PATH_LENGTH];
str_format(aBuf, sizeof(aBuf), "%s/%s", pEditor->m_pFileDialogPath, pEditor->m_aFileDialogNewFolderName);
if(pEditor->Storage()->CreateFolder(aBuf, IStorage::TYPE_SAVE))
{
pEditor->FilelistPopulate(IStorage::TYPE_SAVE);
return 1;
}
else
{
pEditor->ShowFileDialogError("Failed to create the folder '%s'.", aBuf);
}
}
}
return 0;
}
2008-01-17 23:09:49 +00:00
2020-02-28 15:25:27 +00:00
int CEditor::PopupMapInfo(CEditor *pEditor, CUIRect View, void *pContext)
2011-07-12 21:31:39 +00:00
{
CUIRect Label, ButtonBar, Button;
View.Margin(10.0f, &View);
2011-07-12 21:31:39 +00:00
View.HSplitBottom(20.0f, &View, &ButtonBar);
// title
View.HSplitTop(20.0f, &Label, &View);
pEditor->UI()->DoLabel(&Label, "Map details", 20.0f, TEXTALIGN_CENTER);
View.HSplitTop(10.0f, nullptr, &View);
2011-07-12 21:31:39 +00:00
// author box
View.HSplitTop(20.0f, &Label, &View);
pEditor->UI()->DoLabel(&Label, "Author:", 10.0f, TEXTALIGN_LEFT);
Label.VSplitLeft(60.0f, nullptr, &Button);
Button.HMargin(3.0f, &Button);
2011-07-12 21:31:39 +00:00
static float s_AuthorBox = 0;
pEditor->DoEditBox(&s_AuthorBox, &Button, pEditor->m_Map.m_MapInfo.m_aAuthorTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aAuthorTmp), 10.0f, &s_AuthorBox);
// version box
View.HSplitTop(20.0f, &Label, &View);
pEditor->UI()->DoLabel(&Label, "Version:", 10.0f, TEXTALIGN_LEFT);
Label.VSplitLeft(60.0f, nullptr, &Button);
Button.HMargin(3.0f, &Button);
2011-07-12 21:31:39 +00:00
static float s_VersionBox = 0;
pEditor->DoEditBox(&s_VersionBox, &Button, pEditor->m_Map.m_MapInfo.m_aVersionTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aVersionTmp), 10.0f, &s_VersionBox);
// credits box
View.HSplitTop(20.0f, &Label, &View);
pEditor->UI()->DoLabel(&Label, "Credits:", 10.0f, TEXTALIGN_LEFT);
Label.VSplitLeft(60.0f, nullptr, &Button);
Button.HMargin(3.0f, &Button);
2011-07-12 21:31:39 +00:00
static float s_CreditsBox = 0;
pEditor->DoEditBox(&s_CreditsBox, &Button, pEditor->m_Map.m_MapInfo.m_aCreditsTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aCreditsTmp), 10.0f, &s_CreditsBox);
// license box
View.HSplitTop(20.0f, &Label, &View);
pEditor->UI()->DoLabel(&Label, "License:", 10.0f, TEXTALIGN_LEFT);
Label.VSplitLeft(60.0f, nullptr, &Button);
Button.HMargin(3.0f, &Button);
2011-07-12 21:31:39 +00:00
static float s_LicenseBox = 0;
pEditor->DoEditBox(&s_LicenseBox, &Button, pEditor->m_Map.m_MapInfo.m_aLicenseTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aLicenseTmp), 10.0f, &s_LicenseBox);
// button bar
ButtonBar.VSplitLeft(110.0f, &Label, &ButtonBar);
static int s_CancelButton = 0;
if(pEditor->DoButton_Editor(&s_CancelButton, "Cancel", 0, &Label, 0, nullptr))
2011-07-12 21:31:39 +00:00
return 1;
ButtonBar.VSplitRight(110.0f, &ButtonBar, &Label);
static int s_ConfirmButton = 0;
if(pEditor->DoButton_Editor(&s_ConfirmButton, "Confirm", 0, &Label, 0, nullptr))
{
str_copy(pEditor->m_Map.m_MapInfo.m_aAuthor, pEditor->m_Map.m_MapInfo.m_aAuthorTmp);
str_copy(pEditor->m_Map.m_MapInfo.m_aVersion, pEditor->m_Map.m_MapInfo.m_aVersionTmp);
str_copy(pEditor->m_Map.m_MapInfo.m_aCredits, pEditor->m_Map.m_MapInfo.m_aCreditsTmp);
str_copy(pEditor->m_Map.m_MapInfo.m_aLicense, pEditor->m_Map.m_MapInfo.m_aLicenseTmp);
2011-07-12 21:31:39 +00:00
return 1;
}
2011-07-12 21:31:39 +00:00
return 0;
}
2008-01-17 23:09:49 +00:00
2020-02-28 15:25:27 +00:00
int CEditor::PopupEvent(CEditor *pEditor, CUIRect View, void *pContext)
{
CUIRect Label, ButtonBar;
// title
View.HSplitTop(10.0f, nullptr, &View);
View.HSplitTop(30.0f, &Label, &View);
if(pEditor->m_PopupEventType == POPEVENT_EXIT)
pEditor->UI()->DoLabel(&Label, "Exit the editor", 20.0f, TEXTALIGN_CENTER);
else if(pEditor->m_PopupEventType == POPEVENT_LOAD)
pEditor->UI()->DoLabel(&Label, "Load map", 20.0f, TEXTALIGN_CENTER);
else if(pEditor->m_PopupEventType == POPEVENT_NEW)
pEditor->UI()->DoLabel(&Label, "New map", 20.0f, TEXTALIGN_CENTER);
else if(pEditor->m_PopupEventType == POPEVENT_SAVE)
pEditor->UI()->DoLabel(&Label, "Save map", 20.0f, TEXTALIGN_CENTER);
else if(pEditor->m_PopupEventType == POPEVENT_LARGELAYER)
pEditor->UI()->DoLabel(&Label, "Large layer", 20.0f, TEXTALIGN_CENTER);
else if(pEditor->m_PopupEventType == POPEVENT_PREVENTUNUSEDTILES)
pEditor->UI()->DoLabel(&Label, "Unused tiles disabled", 20.0f, TEXTALIGN_CENTER);
2020-06-20 23:14:36 +00:00
else if(pEditor->m_PopupEventType == POPEVENT_IMAGEDIV16)
pEditor->UI()->DoLabel(&Label, "Image width/height", 20.0f, TEXTALIGN_CENTER);
else if(pEditor->m_PopupEventType == POPEVENT_IMAGE_MAX)
pEditor->UI()->DoLabel(&Label, "Max images", 20.0f, TEXTALIGN_CENTER);
else if(pEditor->m_PopupEventType == POPEVENT_PLACE_BORDER_TILES)
pEditor->UI()->DoLabel(&Label, "Place border tiles", 20.0f, TEXTALIGN_CENTER);
View.HSplitBottom(10.0f, &View, nullptr);
View.HSplitBottom(20.0f, &View, &ButtonBar);
// notification text
View.HSplitTop(30.0f, nullptr, &View);
View.VMargin(40.0f, &View);
View.HSplitTop(20.0f, &Label, &View);
2022-03-11 16:34:48 +00:00
SLabelProperties Props;
Props.m_MaxWidth = Label.w - 10.0f;
if(pEditor->m_PopupEventType == POPEVENT_EXIT)
2022-03-11 16:34:48 +00:00
pEditor->UI()->DoLabel(&Label, "The map contains unsaved data, you might want to save it before you exit the editor.\nContinue anyway?", 10.0f, TEXTALIGN_LEFT, Props);
else if((pEditor->m_PopupEventType == POPEVENT_LOAD) || (pEditor->m_PopupEventType == POPEVENT_LOADCURRENT))
2022-03-11 16:34:48 +00:00
pEditor->UI()->DoLabel(&Label, "The map contains unsaved data, you might want to save it before you load a new map.\nContinue anyway?", 10.0f, TEXTALIGN_LEFT, Props);
else if(pEditor->m_PopupEventType == POPEVENT_NEW)
2022-03-11 16:34:48 +00:00
pEditor->UI()->DoLabel(&Label, "The map contains unsaved data, you might want to save it before you create a new map.\nContinue anyway?", 10.0f, TEXTALIGN_LEFT, Props);
else if(pEditor->m_PopupEventType == POPEVENT_SAVE)
pEditor->UI()->DoLabel(&Label, "The file already exists.\nDo you want to overwrite the map?", 10.0f, TEXTALIGN_LEFT);
else if(pEditor->m_PopupEventType == POPEVENT_LARGELAYER)
2022-03-11 16:34:48 +00:00
pEditor->UI()->DoLabel(&Label, "You are trying to set the height or width of a layer to more than 1000 tiles. This is actually possible, but only rarely necessary. It may cause the editor to work slower, larger file size as well as higher memory usage for client and server.", 10.0f, TEXTALIGN_LEFT, Props);
else if(pEditor->m_PopupEventType == POPEVENT_PREVENTUNUSEDTILES)
2022-03-11 16:34:48 +00:00
pEditor->UI()->DoLabel(&Label, "Unused tiles can't be placed by default because they could get a use later and then destroy your map.\nActivate the 'Unused' switch to be able to place every tile.", 10.0f, TEXTALIGN_LEFT, Props);
2020-06-20 23:14:36 +00:00
else if(pEditor->m_PopupEventType == POPEVENT_IMAGEDIV16)
2022-03-11 16:34:48 +00:00
pEditor->UI()->DoLabel(&Label, "The width or height of this image is not divisible by 16. This is required for images used in tile layers.", 10.0f, TEXTALIGN_LEFT, Props);
else if(pEditor->m_PopupEventType == POPEVENT_IMAGE_MAX)
2022-03-11 16:34:48 +00:00
pEditor->UI()->DoLabel(&Label, "The client only allows a maximum of 64 images.", 10.0f, TEXTALIGN_LEFT, Props);
else if(pEditor->m_PopupEventType == POPEVENT_PLACE_BORDER_TILES)
2022-03-11 16:34:48 +00:00
pEditor->UI()->DoLabel(&Label, "This is going to overwrite any existing tiles around the edges of the layer.\nContinue?", 10.0f, TEXTALIGN_LEFT, Props);
// button bar
ButtonBar.VSplitLeft(30.0f, nullptr, &ButtonBar);
ButtonBar.VSplitLeft(110.0f, &Label, &ButtonBar);
static int s_OkButton = 0;
if(pEditor->DoButton_Editor(&s_OkButton, "Ok", 0, &Label, 0, nullptr) || pEditor->Input()->KeyPress(KEY_RETURN) || pEditor->Input()->KeyPress(KEY_KP_ENTER))
{
if(pEditor->m_PopupEventType == POPEVENT_EXIT)
g_Config.m_ClEditor = 0;
else if(pEditor->m_PopupEventType == POPEVENT_LOAD)
2022-01-22 16:34:23 +00:00
pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Load map", "Load", "maps", "", CEditor::CallbackOpenMap, pEditor);
2016-08-23 01:08:36 +00:00
else if(pEditor->m_PopupEventType == POPEVENT_LOADCURRENT)
pEditor->LoadCurrentMap();
else if(pEditor->m_PopupEventType == POPEVENT_NEW)
{
pEditor->Reset();
pEditor->m_aFileName[0] = 0;
}
else if(pEditor->m_PopupEventType == POPEVENT_SAVE)
{
if(!CallbackSaveMap(pEditor->m_aFileSaveName, IStorage::TYPE_SAVE, pEditor))
return 0; // don't close this popup on error, because it would close the error popup instead
}
else if(pEditor->m_PopupEventType == POPEVENT_PLACE_BORDER_TILES)
pEditor->PlaceBorderTiles();
pEditor->m_PopupEventWasActivated = false;
return 1;
}
ButtonBar.VSplitRight(30.0f, &ButtonBar, nullptr);
ButtonBar.VSplitRight(110.0f, &ButtonBar, &Label);
if(pEditor->m_PopupEventType != POPEVENT_LARGELAYER && pEditor->m_PopupEventType != POPEVENT_PREVENTUNUSEDTILES && pEditor->m_PopupEventType != POPEVENT_IMAGEDIV16 && pEditor->m_PopupEventType != POPEVENT_IMAGE_MAX)
{
static int s_AbortButton = 0;
if(pEditor->DoButton_Editor(&s_AbortButton, "Abort", 0, &Label, 0, nullptr))
{
pEditor->m_PopupEventWasActivated = false;
return 1;
}
}
return 0;
}
2008-01-17 23:09:49 +00:00
2010-05-29 07:25:38 +00:00
static int g_SelectImageSelected = -100;
static int g_SelectImageCurrent = -100;
2008-01-17 23:09:49 +00:00
2020-02-28 15:25:27 +00:00
int CEditor::PopupSelectImage(CEditor *pEditor, CUIRect View, void *pContext)
2008-01-17 23:09:49 +00:00
{
2010-05-29 07:25:38 +00:00
CUIRect ButtonBar, ImageView;
View.VSplitLeft(150.0f, &ButtonBar, &View);
2010-05-29 07:25:38 +00:00
View.Margin(10.0f, &ImageView);
2010-05-29 07:25:38 +00:00
int ShowImage = g_SelectImageCurrent;
const float ButtonHeight = 12.0f;
const float ButtonMargin = 2.0f;
static CScrollRegion s_ScrollRegion;
vec2 ScrollOffset(0.0f, 0.0f);
CScrollRegionParams ScrollParams;
ScrollParams.m_ScrollbarWidth = 10.0f;
ScrollParams.m_ScrollbarMargin = 3.0f;
ScrollParams.m_ScrollUnit = (ButtonHeight + ButtonMargin) * 5;
s_ScrollRegion.Begin(&ButtonBar, &ScrollOffset, &ScrollParams);
ButtonBar.y += ScrollOffset.y;
for(int i = -1; i < (int)pEditor->m_Map.m_vpImages.size(); i++)
2008-01-17 23:09:49 +00:00
{
2010-05-29 07:25:38 +00:00
CUIRect Button;
ButtonBar.HSplitTop(ButtonMargin, nullptr, &ButtonBar);
ButtonBar.HSplitTop(ButtonHeight, &Button, &ButtonBar);
if(s_ScrollRegion.AddRect(Button))
2008-01-17 23:09:49 +00:00
{
if(pEditor->UI()->MouseInside(&Button))
ShowImage = i;
static int s_NoneButton = 0;
if(pEditor->DoButton_MenuItem(i == -1 ? (void *)&s_NoneButton : &pEditor->m_Map.m_vpImages[i], i == -1 ? "None" : pEditor->m_Map.m_vpImages[i]->m_aName, i == g_SelectImageCurrent, &Button))
g_SelectImageSelected = i;
2008-01-17 23:09:49 +00:00
}
}
s_ScrollRegion.End();
if(ShowImage >= 0 && (size_t)ShowImage < pEditor->m_Map.m_vpImages.size())
{
if(ImageView.h < ImageView.w)
ImageView.w = ImageView.h;
else
ImageView.h = ImageView.w;
float Max = (float)(maximum(pEditor->m_Map.m_vpImages[ShowImage]->m_Width, pEditor->m_Map.m_vpImages[ShowImage]->m_Height));
ImageView.w *= pEditor->m_Map.m_vpImages[ShowImage]->m_Width / Max;
ImageView.h *= pEditor->m_Map.m_vpImages[ShowImage]->m_Height / Max;
pEditor->Graphics()->TextureSet(pEditor->m_Map.m_vpImages[ShowImage]->m_Texture);
pEditor->Graphics()->BlendNormal();
pEditor->Graphics()->WrapClamp();
pEditor->Graphics()->QuadsBegin();
IGraphics::CQuadItem QuadItem(ImageView.x, ImageView.y, ImageView.w, ImageView.h);
pEditor->Graphics()->QuadsDrawTL(&QuadItem, 1);
pEditor->Graphics()->QuadsEnd();
pEditor->Graphics()->WrapNormal();
}
2008-01-17 23:09:49 +00:00
return 0;
}
2010-05-29 07:25:38 +00:00
void CEditor::PopupSelectImageInvoke(int Current, float x, float y)
2008-01-17 23:09:49 +00:00
{
2010-05-29 07:25:38 +00:00
static int s_SelectImagePopupId = 0;
g_SelectImageSelected = -100;
g_SelectImageCurrent = Current;
UiInvokePopupMenu(&s_SelectImagePopupId, 0, x, y, 450, 300, PopupSelectImage);
2008-01-17 23:09:49 +00:00
}
2010-05-29 07:25:38 +00:00
int CEditor::PopupSelectImageResult()
2008-01-17 23:09:49 +00:00
{
2010-05-29 07:25:38 +00:00
if(g_SelectImageSelected == -100)
2008-01-17 23:09:49 +00:00
return -100;
2010-05-29 07:25:38 +00:00
g_SelectImageCurrent = g_SelectImageSelected;
g_SelectImageSelected = -100;
return g_SelectImageCurrent;
2008-01-17 23:09:49 +00:00
}
2014-10-08 15:33:06 +00:00
static int g_SelectSoundSelected = -100;
static int g_SelectSoundCurrent = -100;
2020-02-28 15:25:27 +00:00
int CEditor::PopupSelectSound(CEditor *pEditor, CUIRect View, void *pContext)
2014-10-08 15:33:06 +00:00
{
const float ButtonHeight = 12.0f;
const float ButtonMargin = 2.0f;
2014-10-08 15:33:06 +00:00
static CScrollRegion s_ScrollRegion;
vec2 ScrollOffset(0.0f, 0.0f);
CScrollRegionParams ScrollParams;
ScrollParams.m_ScrollbarWidth = 10.0f;
ScrollParams.m_ScrollbarMargin = 3.0f;
ScrollParams.m_ScrollUnit = (ButtonHeight + ButtonMargin) * 5;
s_ScrollRegion.Begin(&View, &ScrollOffset, &ScrollParams);
View.y += ScrollOffset.y;
2014-10-08 15:33:06 +00:00
for(int i = -1; i < (int)pEditor->m_Map.m_vpSounds.size(); i++)
2014-10-08 15:33:06 +00:00
{
CUIRect Button;
View.HSplitTop(ButtonMargin, nullptr, &View);
View.HSplitTop(ButtonHeight, &Button, &View);
if(s_ScrollRegion.AddRect(Button))
2014-10-08 15:33:06 +00:00
{
static int s_NoneButton = 0;
if(pEditor->DoButton_MenuItem(i == -1 ? (void *)&s_NoneButton : &pEditor->m_Map.m_vpSounds[i], i == -1 ? "None" : pEditor->m_Map.m_vpSounds[i]->m_aName, i == g_SelectSoundCurrent, &Button))
2014-10-08 15:33:06 +00:00
g_SelectSoundSelected = i;
}
}
s_ScrollRegion.End();
2014-10-08 15:33:06 +00:00
return 0;
}
void CEditor::PopupSelectSoundInvoke(int Current, float x, float y)
{
static int s_SelectSoundPopupId = 0;
g_SelectSoundSelected = -100;
g_SelectSoundCurrent = Current;
UiInvokePopupMenu(&s_SelectSoundPopupId, 0, x, y, 150, 300, PopupSelectSound);
2014-10-08 15:33:06 +00:00
}
int CEditor::PopupSelectSoundResult()
{
if(g_SelectSoundSelected == -100)
return -100;
g_SelectSoundCurrent = g_SelectSoundSelected;
g_SelectSoundSelected = -100;
return g_SelectSoundCurrent;
2015-07-09 00:08:14 +00:00
}
2014-10-08 15:33:06 +00:00
static int s_GametileOpSelected = -1;
2008-01-17 23:09:49 +00:00
2020-02-28 15:25:27 +00:00
int CEditor::PopupSelectGametileOp(CEditor *pEditor, CUIRect View, void *pContext)
{
static const char *s_apButtonNames[] = {
2022-01-08 11:30:51 +00:00
"Air",
"Hookable",
"Death",
"Unhookable",
"Hookthrough",
"Freeze",
"Unfreeze",
"Deep Freeze",
"Deep Unfreeze",
"Blue Check-Tele",
"Red Check-Tele",
"Live Freeze",
"Live Unfreeze",
};
static unsigned s_NumButtons = std::size(s_apButtonNames);
CUIRect Button;
for(unsigned i = 0; i < s_NumButtons; ++i)
{
View.HSplitTop(2.0f, nullptr, &View);
View.HSplitTop(12.0f, &Button, &View);
if(pEditor->DoButton_Editor(&s_apButtonNames[i], s_apButtonNames[i], 0, &Button, 0, nullptr))
s_GametileOpSelected = i;
}
2008-01-17 23:09:49 +00:00
return 0;
}
2008-01-17 23:09:49 +00:00
void CEditor::PopupSelectGametileOpInvoke(float x, float y)
{
static int s_SelectGametileOpPopupId = 0;
s_GametileOpSelected = -1;
UiInvokePopupMenu(&s_SelectGametileOpPopupId, 0, x, y, 120.0f, 189.0f, PopupSelectGametileOp);
}
2008-01-17 23:09:49 +00:00
int CEditor::PopupSelectGameTileOpResult()
{
if(s_GametileOpSelected < 0)
return -1;
int Result = s_GametileOpSelected;
s_GametileOpSelected = -1;
return Result;
}
2008-01-17 23:09:49 +00:00
static int s_AutoMapConfigSelected = -100;
static int s_AutoMapConfigCurrent = -100;
2020-02-28 15:25:27 +00:00
int CEditor::PopupSelectConfigAutoMap(CEditor *pEditor, CUIRect View, void *pContext)
{
2020-09-20 12:05:39 +00:00
CLayerTiles *pLayer = static_cast<CLayerTiles *>(pEditor->GetSelectedLayer(0));
CAutoMapper *pAutoMapper = &pEditor->m_Map.m_vpImages[pLayer->m_Image]->m_AutoMapper;
2011-08-11 08:59:14 +00:00
const float ButtonHeight = 12.0f;
2020-09-27 15:20:41 +00:00
const float ButtonMargin = 2.0f;
static CScrollRegion s_ScrollRegion;
vec2 ScrollOffset(0.0f, 0.0f);
CScrollRegionParams ScrollParams;
ScrollParams.m_ScrollbarWidth = 10.0f;
ScrollParams.m_ScrollbarMargin = 3.0f;
ScrollParams.m_ScrollUnit = (ButtonHeight + ButtonMargin) * 5;
s_ScrollRegion.Begin(&View, &ScrollOffset, &ScrollParams);
View.y += ScrollOffset.y;
for(int i = -1; i < pAutoMapper->ConfigNamesNum(); i++)
{
CUIRect Button;
View.HSplitTop(ButtonMargin, nullptr, &View);
View.HSplitTop(ButtonHeight, &Button, &View);
if(s_ScrollRegion.AddRect(Button))
{
static int s_NoneButton = 0;
if(pEditor->DoButton_MenuItem(i == -1 ? (void *)&s_NoneButton : pAutoMapper->GetConfigName(i), i == -1 ? "None" : pAutoMapper->GetConfigName(i), i == s_AutoMapConfigCurrent, &Button))
s_AutoMapConfigSelected = i;
}
}
s_ScrollRegion.End();
return 0;
}
void CEditor::PopupSelectConfigAutoMapInvoke(int Current, float x, float y)
{
static int s_AutoMapConfigSelectID = 0;
s_AutoMapConfigSelected = -100;
s_AutoMapConfigCurrent = Current;
2020-09-20 12:05:39 +00:00
CLayerTiles *pLayer = static_cast<CLayerTiles *>(GetSelectedLayer(0));
const int ItemCount = minimum(m_Map.m_vpImages[pLayer->m_Image]->m_AutoMapper.ConfigNamesNum(), 10);
// Width for buttons is 120, 15 is the scrollbar width, 2 is the margin between both.
UiInvokePopupMenu(&s_AutoMapConfigSelectID, 0, x, y, 120.0f + 15.0f + 2.0f, 26.0f + 14.0f * ItemCount, PopupSelectConfigAutoMap);
}
int CEditor::PopupSelectConfigAutoMapResult()
{
if(s_AutoMapConfigSelected == -100)
return -100;
2011-08-11 08:59:14 +00:00
s_AutoMapConfigCurrent = s_AutoMapConfigSelected;
s_AutoMapConfigSelected = -100;
return s_AutoMapConfigCurrent;
}
// DDRace
2020-02-28 15:25:27 +00:00
int CEditor::PopupTele(CEditor *pEditor, CUIRect View, void *pContext)
{
2020-05-20 18:57:19 +00:00
static int s_PreviousNumber = -1;
2020-05-15 22:42:11 +00:00
2020-05-17 00:56:35 +00:00
CUIRect NumberPicker;
2020-05-15 22:42:11 +00:00
CUIRect FindEmptySlot;
2020-05-17 00:56:35 +00:00
View.VSplitRight(15.f, &NumberPicker, &FindEmptySlot);
NumberPicker.VSplitRight(2.f, &NumberPicker, nullptr);
FindEmptySlot.HSplitTop(13.0f, &FindEmptySlot, nullptr);
FindEmptySlot.HMargin(1.0f, &FindEmptySlot);
2020-05-15 22:42:11 +00:00
2020-05-17 00:56:35 +00:00
// find empty number button
{
2020-05-19 16:17:02 +00:00
static int s_EmptySlotPid = 0;
if(pEditor->DoButton_Editor(&s_EmptySlotPid, "F", 0, &FindEmptySlot, 0, "[ctrl+f] Find empty slot") || (pEditor->Input()->ModifierIsPressed() && pEditor->Input()->KeyPress(KEY_F)))
2020-05-15 22:42:11 +00:00
{
2020-05-17 00:56:35 +00:00
int number = -1;
for(int i = 1; i <= 255; i++)
2020-05-15 22:42:11 +00:00
{
2020-05-17 00:56:35 +00:00
if(!pEditor->m_Map.m_pTeleLayer->ContainsElementWithId(i))
{
number = i;
break;
}
2020-05-15 22:42:11 +00:00
}
2020-05-17 00:56:35 +00:00
if(number != -1)
{
pEditor->m_TeleNumber = number;
}
2020-05-15 22:42:11 +00:00
}
}
2020-05-17 00:56:35 +00:00
// number picker
{
2020-05-19 16:17:02 +00:00
static ColorRGBA s_Color = ColorRGBA(0.5f, 1, 0.5f, 0.5f);
2008-01-17 23:09:49 +00:00
2020-05-17 00:56:35 +00:00
enum
{
PROP_TELE = 0,
NUM_PROPS,
};
CProperty aProps[] = {
{"Number", pEditor->m_TeleNumber, PROPTYPE_INT_STEP, 1, 255},
{nullptr},
2020-05-17 00:56:35 +00:00
};
2020-05-17 00:56:35 +00:00
static int s_aIds[NUM_PROPS] = {0};
static int NewVal = 0;
2020-05-19 16:17:02 +00:00
int Prop = pEditor->DoProperties(&NumberPicker, aProps, s_aIds, &NewVal, s_Color);
2020-05-17 00:56:35 +00:00
if(Prop == PROP_TELE)
{
pEditor->m_TeleNumber = (NewVal - 1 + 255) % 255 + 1;
2020-05-17 00:56:35 +00:00
}
2020-05-20 18:57:19 +00:00
if(s_PreviousNumber == 1 || s_PreviousNumber != pEditor->m_TeleNumber)
2020-05-17 00:56:35 +00:00
{
s_Color = pEditor->m_Map.m_pTeleLayer->ContainsElementWithId(pEditor->m_TeleNumber) ? ColorRGBA(1, 0.5f, 0.5f, 0.5f) : ColorRGBA(0.5f, 1, 0.5f, 0.5f);
2020-05-17 00:56:35 +00:00
}
2020-05-15 22:42:11 +00:00
}
2020-05-20 18:57:19 +00:00
s_PreviousNumber = pEditor->m_TeleNumber;
return 0;
}
2020-02-28 15:25:27 +00:00
int CEditor::PopupSpeedup(CEditor *pEditor, CUIRect View, void *pContext)
{
enum
{
PROP_FORCE = 0,
PROP_MAXSPEED,
PROP_ANGLE,
NUM_PROPS
};
CProperty aProps[] = {
{"Force", pEditor->m_SpeedupForce, PROPTYPE_INT_STEP, 1, 255},
2016-06-02 13:31:12 +00:00
{"Max Speed", pEditor->m_SpeedupMaxSpeed, PROPTYPE_INT_STEP, 0, 255},
{"Angle", pEditor->m_SpeedupAngle, PROPTYPE_ANGLE_SCROLL, 0, 359},
{nullptr},
};
static int s_aIds[NUM_PROPS] = {0};
int NewVal = 0;
int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal);
if(Prop == PROP_FORCE)
pEditor->m_SpeedupForce = clamp(NewVal, 1, 255);
if(Prop == PROP_MAXSPEED)
pEditor->m_SpeedupMaxSpeed = clamp(NewVal, 0, 255);
if(Prop == PROP_ANGLE)
pEditor->m_SpeedupAngle = clamp(NewVal, 0, 359);
return 0;
}
2008-01-17 23:09:49 +00:00
2020-02-28 15:25:27 +00:00
int CEditor::PopupSwitch(CEditor *pEditor, CUIRect View, void *pContext)
{
2020-05-20 18:57:19 +00:00
static int s_PreviousNumber = -1;
2008-01-17 23:09:49 +00:00
2020-05-17 00:56:35 +00:00
CUIRect NumberPicker;
CUIRect FindEmptySlot;
View.VSplitRight(15.f, &NumberPicker, &FindEmptySlot);
NumberPicker.VSplitRight(2.f, &NumberPicker, nullptr);
FindEmptySlot.HSplitTop(13.0f, &FindEmptySlot, nullptr);
FindEmptySlot.HMargin(1.0f, &FindEmptySlot);
2020-05-17 00:56:35 +00:00
// find empty number button
{
2020-05-19 16:17:02 +00:00
static int s_EmptySlotPid = 0;
if(pEditor->DoButton_Editor(&s_EmptySlotPid, "F", 0, &FindEmptySlot, 0, "[ctrl+f] Find empty slot") || (pEditor->Input()->ModifierIsPressed() && pEditor->Input()->KeyPress(KEY_F)))
{
2020-05-17 00:56:35 +00:00
int number = -1;
for(int i = 1; i <= 255; i++)
{
2020-05-17 00:56:35 +00:00
if(!pEditor->m_Map.m_pSwitchLayer->ContainsElementWithId(i))
{
2020-05-17 00:56:35 +00:00
number = i;
break;
}
}
2020-05-17 00:56:35 +00:00
if(number != -1)
{
pEditor->m_SwitchNum = number;
}
}
2020-05-17 00:56:35 +00:00
}
2020-05-17 00:56:35 +00:00
// number picker
{
2020-05-19 16:17:02 +00:00
static ColorRGBA s_Color = ColorRGBA(1, 1, 1, 0.5f);
2020-05-17 00:56:35 +00:00
enum
{
PROP_SwitchNumber = 0,
PROP_SwitchDelay,
NUM_PROPS,
};
CProperty aProps[] = {
{"Number", pEditor->m_SwitchNum, PROPTYPE_INT_STEP, 0, 255},
2020-05-17 00:56:35 +00:00
{"Delay", pEditor->m_SwitchDelay, PROPTYPE_INT_STEP, 0, 255},
{nullptr},
};
2020-05-17 00:56:35 +00:00
static int s_aIds[NUM_PROPS] = {0};
int NewVal = 0;
2020-05-19 16:17:02 +00:00
int Prop = pEditor->DoProperties(&NumberPicker, aProps, s_aIds, &NewVal, s_Color);
2020-05-17 00:56:35 +00:00
if(Prop == PROP_SwitchNumber)
{
pEditor->m_SwitchNum = (NewVal + 256) % 256;
2020-05-17 00:56:35 +00:00
}
else if(Prop == PROP_SwitchDelay)
{
pEditor->m_SwitchDelay = (NewVal + 256) % 256;
}
2020-05-20 18:57:19 +00:00
if(s_PreviousNumber == 1 || s_PreviousNumber != pEditor->m_SwitchNum)
2020-05-17 00:56:35 +00:00
{
s_Color = pEditor->m_Map.m_pSwitchLayer->ContainsElementWithId(pEditor->m_SwitchNum) ? ColorRGBA(1, 0.5f, 0.5f, 0.5f) : ColorRGBA(0.5f, 1, 0.5f, 0.5f);
2020-05-17 00:56:35 +00:00
}
}
2020-05-20 18:57:19 +00:00
s_PreviousNumber = pEditor->m_SwitchNum;
return 0;
}
2020-02-28 15:25:27 +00:00
int CEditor::PopupTune(CEditor *pEditor, CUIRect View, void *pContext)
{
enum
{
PROP_TUNE = 0,
NUM_PROPS,
};
CProperty aProps[] = {
{"Zone", pEditor->m_TuningNum, PROPTYPE_INT_STEP, 1, 255},
{nullptr},
};
static int s_aIds[NUM_PROPS] = {0};
int NewVal = 0;
int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal);
if(Prop == PROP_TUNE)
pEditor->m_TuningNum = (NewVal - 1 + 255) % 255 + 1;
return 0;
}
2022-08-20 18:47:46 +00:00
int CEditor::PopupGoto(CEditor *pEditor, CUIRect View, void *pContext)
{
static ColorRGBA s_Color = ColorRGBA(1, 1, 1, 0.5f);
enum
{
PROP_CoordX = 0,
PROP_CoordY,
NUM_PROPS,
};
CProperty aProps[] = {
{"X", pEditor->m_GotoX, PROPTYPE_INT_STEP, std::numeric_limits<int>::min(), std::numeric_limits<int>::max()},
{"Y", pEditor->m_GotoY, PROPTYPE_INT_STEP, std::numeric_limits<int>::min(), std::numeric_limits<int>::max()},
2022-08-20 18:47:46 +00:00
{nullptr},
};
static int s_aIds[NUM_PROPS] = {0};
int NewVal = 0;
int Prop = pEditor->DoProperties(&View, aProps, s_aIds, &NewVal, s_Color);
2022-08-20 18:47:46 +00:00
if(Prop == PROP_CoordX)
{
pEditor->m_GotoX = NewVal;
}
else if(Prop == PROP_CoordY)
{
pEditor->m_GotoY = NewVal;
}
CUIRect Button;
View.HSplitBottom(12.0f, &View, &Button);
static int s_Button;
if(pEditor->DoButton_Editor(&s_Button, "Go", 0, &Button, 0, ""))
{
pEditor->Goto(pEditor->m_GotoX + 0.5f, pEditor->m_GotoY + 0.5f);
}
return 0;
}
2020-02-28 15:25:27 +00:00
int CEditor::PopupColorPicker(CEditor *pEditor, CUIRect View, void *pContext)
{
CUIRect SVPicker, HuePicker;
View.VSplitRight(20.0f, &SVPicker, &HuePicker);
HuePicker.VSplitLeft(4.0f, nullptr, &HuePicker);
pEditor->Graphics()->TextureClear();
pEditor->Graphics()->QuadsBegin();
// base: white - hue
2022-01-22 16:34:23 +00:00
ColorHSVA hsv = CEditor::ms_PickerColor;
IGraphics::CColorVertex aColors[4];
2019-04-26 12:06:32 +00:00
ColorRGBA c = color_cast<ColorRGBA>(ColorHSVA(hsv.x, 0.0f, 1.0f));
aColors[0] = IGraphics::CColorVertex(0, c.r, c.g, c.b, 1.0f);
2019-04-26 12:06:32 +00:00
c = color_cast<ColorRGBA>(ColorHSVA(hsv.x, 1.0f, 1.0f));
aColors[1] = IGraphics::CColorVertex(1, c.r, c.g, c.b, 1.0f);
2019-04-26 12:06:32 +00:00
c = color_cast<ColorRGBA>(ColorHSVA(hsv.x, 1.0f, 1.0f));
aColors[2] = IGraphics::CColorVertex(2, c.r, c.g, c.b, 1.0f);
2019-04-26 12:06:32 +00:00
c = color_cast<ColorRGBA>(ColorHSVA(hsv.x, 0.0f, 1.0f));
aColors[3] = IGraphics::CColorVertex(3, c.r, c.g, c.b, 1.0f);
pEditor->Graphics()->SetColorVertex(aColors, 4);
IGraphics::CQuadItem QuadItem(SVPicker.x, SVPicker.y, SVPicker.w, SVPicker.h);
pEditor->Graphics()->QuadsDrawTL(&QuadItem, 1);
// base: transparent - black
aColors[0] = IGraphics::CColorVertex(0, 0.0f, 0.0f, 0.0f, 0.0f);
aColors[1] = IGraphics::CColorVertex(1, 0.0f, 0.0f, 0.0f, 0.0f);
aColors[2] = IGraphics::CColorVertex(2, 0.0f, 0.0f, 0.0f, 1.0f);
aColors[3] = IGraphics::CColorVertex(3, 0.0f, 0.0f, 0.0f, 1.0f);
pEditor->Graphics()->SetColorVertex(aColors, 4);
pEditor->Graphics()->QuadsDrawTL(&QuadItem, 1);
pEditor->Graphics()->QuadsEnd();
// marker
vec2 Marker = vec2(hsv.y, (1.0f - hsv.z)) * vec2(SVPicker.w, SVPicker.h);
pEditor->Graphics()->QuadsBegin();
pEditor->Graphics()->SetColor(0.5f, 0.5f, 0.5f, 1.0f);
IGraphics::CQuadItem aMarker[2];
aMarker[0] = IGraphics::CQuadItem(SVPicker.x + Marker.x, SVPicker.y + Marker.y - 5.0f * pEditor->UI()->PixelSize(), pEditor->UI()->PixelSize(), 11.0f * pEditor->UI()->PixelSize());
aMarker[1] = IGraphics::CQuadItem(SVPicker.x + Marker.x - 5.0f * pEditor->UI()->PixelSize(), SVPicker.y + Marker.y, 11.0f * pEditor->UI()->PixelSize(), pEditor->UI()->PixelSize());
pEditor->Graphics()->QuadsDrawTL(aMarker, 2);
pEditor->Graphics()->QuadsEnd();
// logic
float X, Y;
2022-01-22 16:34:23 +00:00
if(pEditor->UI()->DoPickerLogic(&CEditor::ms_SVPicker, &SVPicker, &X, &Y))
{
hsv.y = X / SVPicker.w;
hsv.z = 1.0f - Y / SVPicker.h;
}
// hue slider
static const float s_aColorIndices[7][3] = {
{1.0f, 0.0f, 0.0f}, // red
{1.0f, 0.0f, 1.0f}, // magenta
{0.0f, 0.0f, 1.0f}, // blue
{0.0f, 1.0f, 1.0f}, // cyan
{0.0f, 1.0f, 0.0f}, // green
{1.0f, 1.0f, 0.0f}, // yellow
{1.0f, 0.0f, 0.0f} // red
};
pEditor->Graphics()->QuadsBegin();
ColorRGBA ColorTop, ColorBottom;
float Offset = HuePicker.h / 6.0f;
2015-08-17 18:19:27 +00:00
for(int j = 0; j < 6; j++)
{
ColorTop = ColorRGBA(s_aColorIndices[j][0], s_aColorIndices[j][1], s_aColorIndices[j][2], 1.0f);
ColorBottom = ColorRGBA(s_aColorIndices[j + 1][0], s_aColorIndices[j + 1][1], s_aColorIndices[j + 1][2], 1.0f);
aColors[0] = IGraphics::CColorVertex(0, ColorTop.r, ColorTop.g, ColorTop.b, ColorTop.a);
aColors[1] = IGraphics::CColorVertex(1, ColorTop.r, ColorTop.g, ColorTop.b, ColorTop.a);
aColors[2] = IGraphics::CColorVertex(2, ColorBottom.r, ColorBottom.g, ColorBottom.b, ColorBottom.a);
aColors[3] = IGraphics::CColorVertex(3, ColorBottom.r, ColorBottom.g, ColorBottom.b, ColorBottom.a);
pEditor->Graphics()->SetColorVertex(aColors, 4);
QuadItem = IGraphics::CQuadItem(HuePicker.x, HuePicker.y + Offset * j, HuePicker.w, Offset);
pEditor->Graphics()->QuadsDrawTL(&QuadItem, 1);
}
// marker
pEditor->Graphics()->SetColor(0.5f, 0.5f, 0.5f, 1.0f);
IGraphics::CQuadItem QuadItemMarker(HuePicker.x, HuePicker.y + (1.0f - hsv.x) * HuePicker.h, HuePicker.w, pEditor->UI()->PixelSize());
pEditor->Graphics()->QuadsDrawTL(&QuadItemMarker, 1);
pEditor->Graphics()->QuadsEnd();
2022-01-22 16:34:23 +00:00
if(pEditor->UI()->DoPickerLogic(&CEditor::ms_HuePicker, &HuePicker, &X, &Y))
{
hsv.x = 1.0f - Y / HuePicker.h;
}
2022-01-22 16:34:23 +00:00
CEditor::ms_PickerColor = hsv;
return 0;
}
2020-02-28 15:25:27 +00:00
int CEditor::PopupEntities(CEditor *pEditor, CUIRect View, void *pContext)
{
for(int i = 0; i < (int)pEditor->m_vSelectEntitiesFiles.size(); i++)
{
CUIRect Button;
View.HSplitTop(14.0f, &Button, &View);
const char *pName = pEditor->m_vSelectEntitiesFiles[i].c_str();
if(pEditor->DoButton_MenuItem(pName, pName, pEditor->m_vSelectEntitiesFiles[i] == pEditor->m_SelectEntitiesImage, &Button))
{
if(pEditor->m_vSelectEntitiesFiles[i] != pEditor->m_SelectEntitiesImage)
{
pEditor->m_SelectEntitiesImage = pEditor->m_vSelectEntitiesFiles[i];
pEditor->m_AllowPlaceUnusedTiles = pEditor->m_SelectEntitiesImage == "DDNet" ? 0 : -1;
pEditor->m_PreventUnusedTilesWasWarned = false;
char aBuf[512];
str_format(aBuf, sizeof(aBuf), "editor/entities/%s.png", pName);
if(pEditor->m_EntitiesTexture.IsValid())
pEditor->Graphics()->UnloadTexture(&pEditor->m_EntitiesTexture);
int TextureLoadFlag = pEditor->GetTextureUsageFlag();
pEditor->m_EntitiesTexture = pEditor->Graphics()->LoadTexture(aBuf, IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
g_UiNumPopups--;
}
}
}
return 0;
2020-05-19 16:17:02 +00:00
}
void CEditor::SMessagePopupContext::DefaultColor(ITextRender *pTextRender)
{
m_TextColor = pTextRender->DefaultTextColor();
}
void CEditor::SMessagePopupContext::ErrorColor()
{
m_TextColor = ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f);
}
int CEditor::PopupMessage(CEditor *pEditor, CUIRect View, void *pContext)
{
SMessagePopupContext *pMessagePopup = static_cast<SMessagePopupContext *>(pContext);
CTextCursor Cursor;
pEditor->TextRender()->SetCursor(&Cursor, View.x, View.y, SMessagePopupContext::POPUP_FONT_SIZE, TEXTFLAG_RENDER);
Cursor.m_LineWidth = View.w;
pEditor->TextRender()->TextColor(pMessagePopup->m_TextColor);
pEditor->TextRender()->TextEx(&Cursor, pMessagePopup->m_aMessage, -1);
pEditor->TextRender()->TextColor(pEditor->TextRender()->DefaultTextColor());
return 0;
}
CEditor::SConfirmPopupContext::SConfirmPopupContext()
{
Reset();
}
void CEditor::SConfirmPopupContext::Reset()
{
m_Result = SConfirmPopupContext::UNSET;
}
void CEditor::ShowPopupConfirm(float X, float Y, SConfirmPopupContext *pContext)
{
const float TextWidth = minimum(TextRender()->TextWidth(SConfirmPopupContext::POPUP_FONT_SIZE, pContext->m_aMessage, -1, -1.0f), SConfirmPopupContext::POPUP_MAX_WIDTH);
const int LineCount = TextRender()->TextLineCount(SConfirmPopupContext::POPUP_FONT_SIZE, pContext->m_aMessage, TextWidth);
const float PopupHeight = LineCount * SConfirmPopupContext::POPUP_FONT_SIZE + SConfirmPopupContext::POPUP_BUTTON_HEIGHT + SConfirmPopupContext::POPUP_BUTTON_SPACING + 10.0f;
pContext->m_Result = SConfirmPopupContext::UNSET;
UiInvokePopupMenu(pContext, 0, X, Y, TextWidth + 10.0f, PopupHeight, PopupConfirm, pContext);
}
int CEditor::PopupConfirm(CEditor *pEditor, CUIRect View, void *pContext)
{
SConfirmPopupContext *pConfirmPopup = static_cast<SConfirmPopupContext *>(pContext);
CUIRect Label, ButtonBar, CancelButton, ConfirmButton;
View.HSplitBottom(SConfirmPopupContext::POPUP_BUTTON_HEIGHT, &Label, &ButtonBar);
ButtonBar.VSplitMid(&CancelButton, &ConfirmButton, SConfirmPopupContext::POPUP_BUTTON_SPACING);
CTextCursor Cursor;
pEditor->TextRender()->SetCursor(&Cursor, Label.x, Label.y, SConfirmPopupContext::POPUP_FONT_SIZE, TEXTFLAG_RENDER);
Cursor.m_LineWidth = Label.w;
pEditor->TextRender()->TextEx(&Cursor, pConfirmPopup->m_aMessage, -1);
static int s_CancelButton = 0;
if(pEditor->DoButton_Editor(&s_CancelButton, "Cancel", 0, &CancelButton, 0, nullptr))
{
pConfirmPopup->m_Result = SConfirmPopupContext::CANCELED;
return 1;
}
static int s_ConfirmButton = 0;
if(pEditor->DoButton_Editor(&s_ConfirmButton, "Confirm", 0, &ConfirmButton, 0, nullptr))
{
pConfirmPopup->m_Result = SConfirmPopupContext::CONFIRMED;
return 1;
}
return 0;
}
void CEditor::ShowPopupMessage(float X, float Y, SMessagePopupContext *pContext)
{
const float TextWidth = minimum(TextRender()->TextWidth(SMessagePopupContext::POPUP_FONT_SIZE, pContext->m_aMessage, -1, -1.0f), SMessagePopupContext::POPUP_MAX_WIDTH);
const int LineCount = TextRender()->TextLineCount(SMessagePopupContext::POPUP_FONT_SIZE, pContext->m_aMessage, TextWidth);
UiInvokePopupMenu(pContext, 0, X, Y, TextWidth + 10.0f, LineCount * SMessagePopupContext::POPUP_FONT_SIZE + 10.0f, PopupMessage, pContext);
}
CEditor::SSelectionPopupContext::SSelectionPopupContext()
{
Reset();
}
void CEditor::SSelectionPopupContext::Reset()
{
m_pSelection = nullptr;
m_Entries.clear();
}
int CEditor::PopupSelection(CEditor *pEditor, CUIRect View, void *pContext)
{
SSelectionPopupContext *pSelectionPopup = static_cast<SSelectionPopupContext *>(pContext);
CUIRect Slot;
const int LineCount = pEditor->TextRender()->TextLineCount(SSelectionPopupContext::POPUP_FONT_SIZE, pSelectionPopup->m_aMessage, SSelectionPopupContext::POPUP_MAX_WIDTH);
View.HSplitTop(LineCount * SSelectionPopupContext::POPUP_FONT_SIZE, &Slot, &View);
CTextCursor Cursor;
pEditor->TextRender()->SetCursor(&Cursor, Slot.x, Slot.y, SSelectionPopupContext::POPUP_FONT_SIZE, TEXTFLAG_RENDER);
Cursor.m_LineWidth = Slot.w;
pEditor->TextRender()->TextEx(&Cursor, pSelectionPopup->m_aMessage, -1);
for(const auto &Entry : pSelectionPopup->m_Entries)
{
View.HSplitTop(SSelectionPopupContext::POPUP_ENTRY_SPACING, nullptr, &View);
View.HSplitTop(SSelectionPopupContext::POPUP_ENTRY_HEIGHT, &Slot, &View);
if(pEditor->DoButton_MenuItem(&Entry, Entry.c_str(), 0, &Slot, 0, nullptr))
pSelectionPopup->m_pSelection = &Entry;
}
return pSelectionPopup->m_pSelection == nullptr ? 0 : 1;
}
void CEditor::ShowPopupSelection(float X, float Y, SSelectionPopupContext *pContext)
{
const int LineCount = TextRender()->TextLineCount(SSelectionPopupContext::POPUP_FONT_SIZE, pContext->m_aMessage, SSelectionPopupContext::POPUP_MAX_WIDTH);
const float PopupHeight = LineCount * SSelectionPopupContext::POPUP_FONT_SIZE + pContext->m_Entries.size() * (SSelectionPopupContext::POPUP_ENTRY_HEIGHT + SSelectionPopupContext::POPUP_ENTRY_SPACING) + 10.0f;
pContext->m_pSelection = nullptr;
UiInvokePopupMenu(pContext, 0, X, Y, SSelectionPopupContext::POPUP_MAX_WIDTH + 10.0f, PopupHeight, PopupSelection, pContext);
}