ddnet/src/game/editor/editor.cpp

7169 lines
214 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 <algorithm>
#include <base/color.h>
#include <base/system.h>
2007-05-22 15:03:32 +00:00
2014-01-19 03:02:01 +00:00
#if defined(CONF_FAMILY_UNIX)
#include <pthread.h>
#endif
2010-05-29 07:25:38 +00:00
#include <engine/client.h>
#include <engine/console.h>
#include <engine/gfx/image_manipulation.h>
2010-05-29 07:25:38 +00:00
#include <engine/graphics.h>
#include <engine/input.h>
#include <engine/keys.h>
#include <engine/shared/config.h>
2010-05-29 07:25:38 +00:00
#include <engine/storage.h>
#include <engine/textrender.h>
2007-05-22 15:03:32 +00:00
2020-05-15 14:29:09 +00:00
#include <game/client/components/camera.h>
#include <game/client/components/menu_background.h>
#include <game/client/gameclient.h>
2010-05-29 07:25:38 +00:00
#include <game/client/render.h>
#include <game/client/ui.h>
#include <game/client/ui_listbox.h>
#include <game/client/ui_scrollregion.h>
#include <game/generated/client_data.h>
#include <game/localization.h>
2007-05-22 15:03:32 +00:00
#include "auto_map.h"
2011-04-19 08:34:51 +00:00
#include "editor.h"
#include <limits>
static const char *VANILLA_IMAGES[] = {
"bg_cloud1",
"bg_cloud2",
"bg_cloud3",
"desert_doodads",
"desert_main",
"desert_mountains",
"desert_mountains2",
"desert_sun",
"generic_deathtiles",
"generic_unhookable",
"grass_doodads",
"grass_main",
"jungle_background",
"jungle_deathtiles",
"jungle_doodads",
"jungle_main",
"jungle_midground",
"jungle_unhookables",
"moon",
"mountains",
"snow",
"stars",
"sun",
"winter_doodads",
"winter_main",
"winter_mountains",
"winter_mountains2",
"winter_mountains3"};
static bool IsVanillaImage(const char *pImage)
{
2022-01-23 17:56:37 +00:00
return std::any_of(std::begin(VANILLA_IMAGES), std::end(VANILLA_IMAGES), [pImage](const char *pVanillaImage) { return str_comp(pImage, pVanillaImage) == 0; });
}
const void *CEditor::ms_pUiGotContext;
2007-05-22 15:03:32 +00:00
2019-04-26 12:06:32 +00:00
ColorHSVA CEditor::ms_PickerColor;
int CEditor::ms_SVPicker;
int CEditor::ms_HuePicker;
2009-10-27 14:38:53 +00:00
enum
{
BUTTON_CONTEXT = 1,
2009-10-27 14:38:53 +00:00
};
2008-01-13 11:43:43 +00:00
2010-05-29 07:25:38 +00:00
CEditorImage::~CEditorImage()
2009-10-27 14:38:53 +00:00
{
m_pEditor->Graphics()->UnloadTexture(&m_Texture);
free(m_pData);
m_pData = nullptr;
2009-10-27 14:38:53 +00:00
}
2014-10-11 11:38:45 +00:00
CEditorSound::~CEditorSound()
{
m_pEditor->Sound()->UnloadSample(m_SoundID);
free(m_pData);
m_pData = nullptr;
2014-10-11 11:38:45 +00:00
}
2010-05-29 07:25:38 +00:00
CLayerGroup::CLayerGroup()
2007-05-22 15:03:32 +00:00
{
m_vpLayers.clear();
2011-07-12 01:14:46 +00:00
m_aName[0] = 0;
2010-05-29 07:25:38 +00:00
m_Visible = true;
m_Collapse = false;
2010-05-29 07:25:38 +00:00
m_GameGroup = false;
m_OffsetX = 0;
m_OffsetY = 0;
m_ParallaxX = 100;
m_ParallaxY = 100;
m_CustomParallaxZoom = 0;
m_ParallaxZoom = 100;
2010-05-29 07:25:38 +00:00
m_UseClipping = 0;
m_ClipX = 0;
m_ClipY = 0;
m_ClipW = 0;
m_ClipH = 0;
2008-01-12 12:27:55 +00:00
}
2007-05-22 15:03:32 +00:00
template<typename T>
2022-06-15 17:34:41 +00:00
static void DeleteAll(std::vector<T> &vList)
{
Declare variables as `const` when possible According to cppcheck's `constVariable` error: ``` src\engine\client\backend\opengl\opengl_sl.cpp:74:43: style: Variable 'Define' can be declared as reference to const [constVariable] for(CGLSLCompiler::SGLSLCompilerDefine &Define : pCompiler->m_vDefines) ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:2149:12: style: Variable 'GraphicThreadCommandBuffer' can be declared as reference to const [constVariable] auto &GraphicThreadCommandBuffer = m_vvThreadDrawCommandBuffers[i + 1][m_CurImageIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:3192:9: style: Variable 'BufferObject' can be declared as reference to const [constVariable] auto &BufferObject = m_vBufferObjects[BufferObjectIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:3200:10: style: Variable 'DescrSet' can be declared as reference to const [constVariable] auto &DescrSet = m_vTextures[State.m_Texture].m_VKStandard3DTexturedDescrSet; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:3810:13: style: Variable 'Mode' can be declared as reference to const [constVariable] for(auto &Mode : vPresentModeList) ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:3818:13: style: Variable 'Mode' can be declared as reference to const [constVariable] for(auto &Mode : vPresentModeList) ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6511:10: style: Variable 'DescrSet' can be declared as reference to const [constVariable] auto &DescrSet = m_vTextures[pCommand->m_State.m_Texture].m_aVKStandardTexturedDescrSets[AddressModeIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6555:10: style: Variable 'DescrSet' can be declared as reference to const [constVariable] auto &DescrSet = m_vTextures[pCommand->m_State.m_Texture].m_VKStandard3DTexturedDescrSet; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6660:9: style: Variable 'MemBlock' can be declared as reference to const [constVariable] auto &MemBlock = m_vBufferObjects[BufferIndex].m_BufferObject.m_Mem; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6799:9: style: Variable 'BufferObject' can be declared as reference to const [constVariable] auto &BufferObject = m_vBufferObjects[BufferObjectIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6808:10: style: Variable 'DescrSet' can be declared as reference to const [constVariable] auto &DescrSet = m_vTextures[pCommand->m_State.m_Texture].m_aVKStandardTexturedDescrSets[AddressModeIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6902:9: style: Variable 'BufferObject' can be declared as reference to const [constVariable] auto &BufferObject = m_vBufferObjects[BufferObjectIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6907:9: style: Variable 'TextTextureDescr' can be declared as reference to const [constVariable] auto &TextTextureDescr = m_vTextures[pCommand->m_TextTextureIndex].m_VKTextDescrSet; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6961:9: style: Variable 'BufferObject' can be declared as reference to const [constVariable] auto &BufferObject = m_vBufferObjects[BufferObjectIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6970:10: style: Variable 'DescrSet' can be declared as reference to const [constVariable] auto &DescrSet = m_vTextures[State.m_Texture].m_aVKStandardTexturedDescrSets[AddressModeIndex]; ^ src\game\client\components\hud.cpp:178:8: style: Variable 'aFlagCarrier' can be declared as const array [constVariable] int aFlagCarrier[2] = { ^ src\game\client\components\hud.cpp:519:16: style: Variable 's_aTextWidth' can be declared as const array [constVariable] static float s_aTextWidth[5] = {s_TextWidth0, s_TextWidth00, s_TextWidth000, s_TextWidth0000, s_TextWidth00000}; ^ src\game\client\components\killmessages.cpp:305:30: style: Variable 'Client' can be declared as reference to const [constVariable] CGameClient::CClientData &Client = GameClient()->m_aClients[m_aKillmsgs[r].m_KillerID]; ^ src\game\client\components\killmessages.cpp:314:30: style: Variable 'Client' can be declared as reference to const [constVariable] CGameClient::CClientData &Client = GameClient()->m_aClients[m_aKillmsgs[r].m_VictimID]; ^ src\game\client\components\menus_ingame.cpp:243:12: style: Variable 'pInfoByName' can be declared as reference to const [constVariable] for(auto &pInfoByName : m_pClient->m_Snap.m_apInfoByName) ^ src\game\client\components\menus_ingame.cpp:530:12: style: Variable 'pInfoByName' can be declared as reference to const [constVariable] for(auto &pInfoByName : m_pClient->m_Snap.m_apInfoByName) ^ src\game\client\components\players.cpp:767:44: style: Variable 'CharacterInfo' can be declared as reference to const [constVariable] CGameClient::CSnapState::CCharacterInfo &CharacterInfo = m_pClient->m_Snap.m_aCharacters[i]; ^ src\game\client\components\spectator.cpp:122:27: style: Variable 'Snap' can be declared as reference to const [constVariable] CGameClient::CSnapState &Snap = pSelf->m_pClient->m_Snap; ^ src\game\client\components\spectator.cpp:221:12: style: Variable 'pInfo' can be declared as reference to const [constVariable] for(auto &pInfo : m_pClient->m_Snap.m_apInfoByDDTeamName) ^ src\game\client\gameclient.cpp:2220:15: style: Variable 'OwnClientData' can be declared as reference to const [constVariable] CClientData &OwnClientData = m_aClients[ownID]; ^ src\game\client\gameclient.cpp:2227:16: style: Variable 'cData' can be declared as reference to const [constVariable] CClientData &cData = m_aClients[i]; ^ src\game\client\prediction\entities\character.cpp:397:11: style: Variable 'aSpreading' can be declared as const array [constVariable] float aSpreading[] = {-0.185f, -0.070f, 0, 0.070f, 0.185f}; ^ src\game\client\prediction\entities\laser.cpp:53:9: style: Variable 'HitPos' can be declared as reference to const [constVariable] vec2 &HitPos = pHit->Core()->m_Pos; ^ src\game\editor\auto_map.cpp:507:18: style: Variable 'Index' can be declared as reference to const [constVariable] for(auto &Index : pRule->m_vIndexList) ^ src\game\editor\auto_map.cpp:518:18: style: Variable 'Index' can be declared as reference to const [constVariable] for(auto &Index : pRule->m_vIndexList) ^ src\game\editor\editor.cpp:118:12: style: Variable 'Item' can be declared as reference to const [constVariable] for(auto &Item : vList) ^ src\game\editor\editor.cpp:2983:11: style: Variable 'aAspects' can be declared as const array [constVariable] float aAspects[] = {4.0f / 3.0f, 16.0f / 10.0f, 5.0f / 4.0f, 16.0f / 9.0f}; ^ src\game\editor\editor.cpp:3141:15: style: Variable 's_aShift' can be declared as const array [constVariable] static int s_aShift[] = {24, 16, 8, 0}; ^ src\engine\server\server.cpp:2807:14: style: Variable 'Client' can be declared as reference to const [constVariable] for(auto &Client : m_aClients) ^ src\engine\server\sql_string_helpers.cpp:51:6: style: Variable 'aTimes' can be declared as const array [constVariable] int aTimes[7] = ^ src\test\secure_random.cpp:24:6: style: Variable 'BOUNDS' can be declared as const array [constVariable] int BOUNDS[] = {2, 3, 4, 5, 10, 100, 127, 128, 129}; ^ ```
2022-11-13 15:29:13 +00:00
for(const auto &pItem : vList)
delete pItem;
2022-06-15 17:34:41 +00:00
vList.clear();
}
2010-05-29 07:25:38 +00:00
CLayerGroup::~CLayerGroup()
2007-05-22 15:03:32 +00:00
{
DeleteAll(m_vpLayers);
2007-05-22 15:03:32 +00:00
}
2010-05-29 07:25:38 +00:00
void CLayerGroup::Convert(CUIRect *pRect)
2007-05-22 15:03:32 +00:00
{
2010-05-29 07:25:38 +00:00
pRect->x += m_OffsetX;
pRect->y += m_OffsetY;
2007-05-22 15:03:32 +00:00
}
2010-05-29 07:25:38 +00:00
void CLayerGroup::Mapping(float *pPoints)
2007-05-22 15:03:32 +00:00
{
float ParallaxZoom = m_pMap->m_pEditor->m_PreviewZoom ? m_ParallaxZoom : 100.0f;
m_pMap->m_pEditor->RenderTools()->MapScreenToWorld(
2010-05-29 07:25:38 +00:00
m_pMap->m_pEditor->m_WorldOffsetX, m_pMap->m_pEditor->m_WorldOffsetY,
m_ParallaxX, m_ParallaxY, ParallaxZoom, m_OffsetX, m_OffsetY,
2010-05-29 07:25:38 +00:00
m_pMap->m_pEditor->Graphics()->ScreenAspect(), m_pMap->m_pEditor->m_WorldZoom, pPoints);
pPoints[0] += m_pMap->m_pEditor->m_EditorOffsetX;
pPoints[1] += m_pMap->m_pEditor->m_EditorOffsetY;
pPoints[2] += m_pMap->m_pEditor->m_EditorOffsetX;
pPoints[3] += m_pMap->m_pEditor->m_EditorOffsetY;
2007-05-22 15:03:32 +00:00
}
2010-05-29 07:25:38 +00:00
void CLayerGroup::MapScreen()
2007-05-22 15:03:32 +00:00
{
2010-05-29 07:25:38 +00:00
float aPoints[4];
Mapping(aPoints);
m_pMap->m_pEditor->Graphics()->MapScreen(aPoints[0], aPoints[1], aPoints[2], aPoints[3]);
2007-05-22 15:03:32 +00:00
}
2010-05-29 07:25:38 +00:00
void CLayerGroup::Render()
2007-05-22 15:03:32 +00:00
{
2010-05-29 07:25:38 +00:00
MapScreen();
IGraphics *pGraphics = m_pMap->m_pEditor->Graphics();
if(m_UseClipping)
{
float aPoints[4];
m_pMap->m_pGameGroup->Mapping(aPoints);
float x0 = (m_ClipX - aPoints[0]) / (aPoints[2] - aPoints[0]);
float y0 = (m_ClipY - aPoints[1]) / (aPoints[3] - aPoints[1]);
float x1 = ((m_ClipX + m_ClipW) - aPoints[0]) / (aPoints[2] - aPoints[0]);
float y1 = ((m_ClipY + m_ClipH) - aPoints[1]) / (aPoints[3] - aPoints[1]);
2010-05-29 07:25:38 +00:00
pGraphics->ClipEnable((int)(x0 * pGraphics->ScreenWidth()), (int)(y0 * pGraphics->ScreenHeight()),
(int)((x1 - x0) * pGraphics->ScreenWidth()), (int)((y1 - y0) * pGraphics->ScreenHeight()));
2008-03-29 11:44:03 +00:00
}
2010-05-29 07:25:38 +00:00
for(auto &pLayer : m_vpLayers)
2008-01-12 12:27:55 +00:00
{
if(pLayer->m_Visible)
{
if(pLayer->m_Type == LAYERTYPE_TILES)
{
CLayerTiles *pTiles = static_cast<CLayerTiles *>(pLayer);
if(pTiles->m_Game || pTiles->m_Front || pTiles->m_Tele || pTiles->m_Speedup || pTiles->m_Tune || pTiles->m_Switch)
continue;
}
if(m_pMap->m_pEditor->m_ShowDetail || !(pLayer->m_Flags & LAYERFLAG_DETAIL))
pLayer->Render();
}
}
2018-10-02 01:52:01 +00:00
for(auto &pLayer : m_vpLayers)
{
if(pLayer->m_Visible && pLayer->m_Type == LAYERTYPE_TILES && pLayer != m_pMap->m_pGameLayer && pLayer != m_pMap->m_pFrontLayer && pLayer != m_pMap->m_pTeleLayer && pLayer != m_pMap->m_pSpeedupLayer && pLayer != m_pMap->m_pSwitchLayer && pLayer != m_pMap->m_pTuneLayer)
{
CLayerTiles *pTiles = static_cast<CLayerTiles *>(pLayer);
if(pTiles->m_Game || pTiles->m_Front || pTiles->m_Tele || pTiles->m_Speedup || pTiles->m_Tune || pTiles->m_Switch)
{
pLayer->Render();
}
}
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
if(m_UseClipping)
pGraphics->ClipDisable();
2008-01-12 12:27:55 +00:00
}
void CLayerGroup::AddLayer(CLayer *pLayer)
{
2020-05-17 14:11:39 +00:00
m_pMap->m_Modified = true;
m_vpLayers.push_back(pLayer);
}
2010-05-29 07:25:38 +00:00
void CLayerGroup::DeleteLayer(int Index)
2007-05-22 15:03:32 +00:00
{
if(Index < 0 || Index >= (int)m_vpLayers.size())
return;
delete m_vpLayers[Index];
m_vpLayers.erase(m_vpLayers.begin() + Index);
m_pMap->m_Modified = true;
2010-05-29 07:25:38 +00:00
}
2007-05-22 15:03:32 +00:00
void CLayerGroup::DuplicateLayer(int Index)
{
if(Index < 0 || Index >= (int)m_vpLayers.size())
return;
auto *pDup = m_vpLayers[Index]->Duplicate();
m_vpLayers.insert(m_vpLayers.begin() + Index + 1, pDup);
m_pMap->m_Modified = true;
}
void CLayerGroup::GetSize(float *pWidth, float *pHeight) const
2007-05-22 15:03:32 +00:00
{
*pWidth = 0;
*pHeight = 0;
for(const auto &pLayer : m_vpLayers)
2008-01-12 12:27:55 +00:00
{
float lw, lh;
pLayer->GetSize(&lw, &lh);
*pWidth = maximum(*pWidth, lw);
*pHeight = maximum(*pHeight, lh);
2008-01-12 12:27:55 +00:00
}
2007-05-22 15:03:32 +00:00
}
2010-05-29 07:25:38 +00:00
int CLayerGroup::SwapLayers(int Index0, int Index1)
2007-05-22 15:03:32 +00:00
{
if(Index0 < 0 || Index0 >= (int)m_vpLayers.size())
return Index0;
if(Index1 < 0 || Index1 >= (int)m_vpLayers.size())
return Index0;
if(Index0 == Index1)
return Index0;
m_pMap->m_Modified = true;
std::swap(m_vpLayers[Index0], m_vpLayers[Index1]);
2010-05-29 07:25:38 +00:00
return Index1;
2008-03-29 11:44:03 +00:00
}
2010-05-29 07:25:38 +00:00
void CEditorImage::AnalyseTileFlags()
2008-03-29 11:44:03 +00:00
{
2010-05-29 07:25:38 +00:00
mem_zero(m_aTileFlags, sizeof(m_aTileFlags));
int tw = m_Width / 16; // tilesizes
int th = m_Height / 16;
if(tw == th && m_Format == CImageInfo::FORMAT_RGBA)
2011-08-11 08:59:14 +00:00
{
2010-05-29 07:25:38 +00:00
unsigned char *pPixelData = (unsigned char *)m_pData;
int TileID = 0;
2008-09-07 08:30:49 +00:00
for(int ty = 0; ty < 16; ty++)
for(int tx = 0; tx < 16; tx++, TileID++)
2008-09-07 08:30:49 +00:00
{
2010-05-29 07:25:38 +00:00
bool Opaque = true;
2008-09-07 08:30:49 +00:00
for(int x = 0; x < tw; x++)
for(int y = 0; y < th; y++)
2008-03-29 11:44:03 +00:00
{
int p = (ty * tw + y) * m_Width + tx * tw + x;
if(pPixelData[p * 4 + 3] < 250)
2008-09-07 08:30:49 +00:00
{
2010-05-29 07:25:38 +00:00
Opaque = false;
2008-09-07 08:30:49 +00:00
break;
}
2008-03-29 11:44:03 +00:00
}
2010-05-29 07:25:38 +00:00
if(Opaque)
m_aTileFlags[TileID] |= TILEFLAG_OPAQUE;
2008-09-07 08:30:49 +00:00
}
}
2008-03-29 11:44:03 +00:00
}
2008-01-12 12:27:55 +00:00
void CEditor::EnvelopeEval(int TimeOffsetMillis, int Env, ColorRGBA &Channels, void *pUser)
{
CEditor *pThis = (CEditor *)pUser;
if(Env < 0 || Env >= (int)pThis->m_Map.m_vpEnvelopes.size())
{
Channels = ColorRGBA();
return;
}
CEnvelope *pEnv = pThis->m_Map.m_vpEnvelopes[Env];
float t = pThis->m_AnimateTime;
t *= pThis->m_AnimateSpeed;
t += (TimeOffsetMillis / 1000.0f);
pEnv->Eval(t, Channels);
}
2008-01-12 12:27:55 +00:00
/********************************************************
OTHER
*********************************************************/
2008-01-13 22:03:32 +00:00
bool CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, float *pOffset, bool Hidden, int Corners, const char *pToolTip)
{
2021-12-03 18:46:31 +00:00
if(UI()->LastActiveItem() == pID)
m_EditBoxActive = 2;
UpdateTooltip(pID, pRect, pToolTip);
return UI()->DoEditBox(pID, pRect, pStr, StrSize, FontSize, pOffset, Hidden, Corners);
}
bool CEditor::DoClearableEditBox(void *pID, void *pClearID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, float *pOffset, bool Hidden, int Corners, const char *pToolTip)
2008-09-23 13:28:57 +00:00
{
if(UI()->LastActiveItem() == pID)
m_EditBoxActive = 2;
UpdateTooltip(pID, pRect, pToolTip);
return UI()->DoClearableEditBox(pID, pClearID, pRect, pStr, StrSize, FontSize, pOffset, Hidden, Corners);
2008-09-23 13:28:57 +00:00
}
2008-01-12 12:27:55 +00:00
ColorRGBA CEditor::GetButtonColor(const void *pID, int Checked)
2007-05-22 15:03:32 +00:00
{
2010-05-29 07:25:38 +00:00
if(Checked < 0)
return ColorRGBA(0, 0, 0, 0.5f);
2010-05-29 07:25:38 +00:00
switch(Checked)
{
2015-06-28 13:02:48 +00:00
case 7: // selected + game layers
if(UI()->HotItem() == pID)
return ColorRGBA(1, 0, 0, 0.4f);
return ColorRGBA(1, 0, 0, 0.2f);
2015-06-28 13:02:48 +00:00
case 6: // game layers
if(UI()->HotItem() == pID)
return ColorRGBA(1, 1, 1, 0.4f);
return ColorRGBA(1, 1, 1, 0.2f);
2015-06-28 13:02:48 +00:00
case 5: // selected + image/sound should be embedded
if(UI()->HotItem() == pID)
return ColorRGBA(1, 0, 0, 0.75f);
return ColorRGBA(1, 0, 0, 0.5f);
case 4: // image/sound should be embedded
if(UI()->HotItem() == pID)
return ColorRGBA(1, 0, 0, 1.0f);
return ColorRGBA(1, 0, 0, 0.875f);
case 3: // selected + unused image/sound
if(UI()->HotItem() == pID)
return ColorRGBA(1, 0, 1, 0.75f);
return ColorRGBA(1, 0, 1, 0.5f);
case 2: // unused image/sound
if(UI()->HotItem() == pID)
return ColorRGBA(0, 0, 1, 0.75f);
return ColorRGBA(0, 0, 1, 0.5f);
case 1: // selected
if(UI()->HotItem() == pID)
return ColorRGBA(1, 0, 0, 0.75f);
return ColorRGBA(1, 0, 0, 0.5f);
2010-05-29 07:25:38 +00:00
default: // regular
if(UI()->HotItem() == pID)
return ColorRGBA(1, 1, 1, 0.75f);
return ColorRGBA(1, 1, 1, 0.5f);
}
2007-05-22 15:03:32 +00:00
}
void CEditor::UpdateTooltip(const void *pID, const CUIRect *pRect, const char *pToolTip)
{
if((UI()->MouseInside(pRect) && m_pTooltip) || (UI()->HotItem() == pID && pToolTip))
m_pTooltip = pToolTip;
}
2010-05-29 07:25:38 +00:00
int CEditor::DoButton_Editor_Common(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
2007-05-22 15:03:32 +00:00
{
2009-10-27 14:38:53 +00:00
if(UI()->MouseInside(pRect))
{
if(Flags & BUTTON_CONTEXT)
2010-05-29 07:25:38 +00:00
ms_pUiGotContext = pID;
2009-10-27 14:38:53 +00:00
}
2010-05-29 07:25:38 +00:00
UpdateTooltip(pID, pRect, pToolTip);
return UI()->DoButtonLogic(pID, Checked, pRect);
2009-10-27 14:38:53 +00:00
}
2020-10-06 10:25:10 +00:00
int CEditor::DoButton_Editor(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int AlignVert)
2009-10-27 14:38:53 +00:00
{
pRect->Draw(GetButtonColor(pID, Checked), IGraphics::CORNER_ALL, 3.0f);
CUIRect NewRect = *pRect;
2022-03-11 16:34:48 +00:00
SLabelProperties Props;
Props.m_AlignVertically = AlignVert;
UI()->DoLabel(&NewRect, pText, 10.f, TEXTALIGN_CENTER, Props);
Checked %= 2;
2009-10-27 14:38:53 +00:00
return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
2007-05-22 15:03:32 +00:00
}
2019-04-26 22:11:15 +00:00
int CEditor::DoButton_Env(const void *pID, const char *pText, int Checked, const CUIRect *pRect, const char *pToolTip, ColorRGBA BaseColor)
2015-07-24 22:04:12 +00:00
{
2017-03-21 10:24:44 +00:00
float Bright = Checked ? 1.0f : 0.5f;
float Alpha = UI()->HotItem() == pID ? 1.0f : 0.75f;
ColorRGBA Color = ColorRGBA(BaseColor.r * Bright, BaseColor.g * Bright, BaseColor.b * Bright, Alpha);
2015-07-24 22:04:12 +00:00
pRect->Draw(Color, IGraphics::CORNER_ALL, 3.0f);
2022-03-11 16:34:48 +00:00
UI()->DoLabel(pRect, pText, 10.f, TEXTALIGN_CENTER);
2015-07-24 22:04:12 +00:00
Checked %= 2;
return DoButton_Editor_Common(pID, pText, Checked, pRect, 0, pToolTip);
}
2010-05-29 07:25:38 +00:00
int CEditor::DoButton_File(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
2008-01-13 16:30:30 +00:00
{
if(Checked)
pRect->Draw(GetButtonColor(pID, Checked), IGraphics::CORNER_ALL, 3.0f);
2010-05-29 07:25:38 +00:00
2009-10-27 14:38:53 +00:00
CUIRect t = *pRect;
t.VMargin(5.0f, &t);
2022-03-11 16:34:48 +00:00
UI()->DoLabel(&t, pText, 10, TEXTALIGN_LEFT);
2009-10-27 14:38:53 +00:00
return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
2008-01-13 16:30:30 +00:00
}
2010-05-29 07:25:38 +00:00
int CEditor::DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
2008-01-13 22:03:32 +00:00
{
2009-10-27 14:38:53 +00:00
CUIRect r = *pRect;
r.Draw(ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f), IGraphics::CORNER_T, 3.0f);
2008-01-13 22:03:32 +00:00
2009-10-27 14:38:53 +00:00
r = *pRect;
r.VMargin(5.0f, &r);
2022-03-11 16:34:48 +00:00
UI()->DoLabel(&r, pText, 10, TEXTALIGN_LEFT);
2009-10-27 14:38:53 +00:00
return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
2008-01-13 22:03:32 +00:00
}
2010-05-29 07:25:38 +00:00
int CEditor::DoButton_MenuItem(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
2008-01-13 22:03:32 +00:00
{
2009-10-27 14:38:53 +00:00
if(UI()->HotItem() == pID || Checked)
pRect->Draw(GetButtonColor(pID, Checked), IGraphics::CORNER_ALL, 3.0f);
2010-05-29 07:25:38 +00:00
2009-10-27 14:38:53 +00:00
CUIRect t = *pRect;
t.VMargin(5.0f, &t);
2022-03-11 16:34:48 +00:00
UI()->DoLabel(&t, pText, 10, TEXTALIGN_LEFT);
return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
2008-01-13 22:03:32 +00:00
}
2010-05-29 07:25:38 +00:00
int CEditor::DoButton_Tab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
2007-05-22 15:03:32 +00:00
{
pRect->Draw(GetButtonColor(pID, Checked), IGraphics::CORNER_T, 5.0f);
2011-08-11 08:59:14 +00:00
CUIRect NewRect = *pRect;
2022-03-11 16:34:48 +00:00
UI()->DoLabel(&NewRect, pText, 10, TEXTALIGN_CENTER);
2009-10-27 14:38:53 +00:00
return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
2007-05-22 15:03:32 +00:00
}
2020-10-06 10:25:10 +00:00
int CEditor::DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners, float FontSize, int AlignVert)
2007-05-22 15:03:32 +00:00
{
pRect->Draw(GetButtonColor(pID, Checked), Corners, 3.0f);
2011-08-11 08:59:14 +00:00
CUIRect NewRect = *pRect;
2022-03-11 16:34:48 +00:00
SLabelProperties Props;
Props.m_AlignVertically = AlignVert;
UI()->DoLabel(&NewRect, pText, FontSize, TEXTALIGN_CENTER, Props);
2009-10-27 14:38:53 +00:00
return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
2007-05-22 15:03:32 +00:00
}
int CEditor::DoButton_FontIcon(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners, float FontSize, int AlignVert)
{
pRect->Draw(GetButtonColor(pID, Checked), Corners, 3.0f);
CUIRect NewRect = *pRect;
SLabelProperties Props;
Props.m_AlignVertically = AlignVert;
TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_ICON_FONT));
UI()->DoLabel(&NewRect, pText, FontSize, TEXTALIGN_CENTER, Props);
TextRender()->SetCurFont(nullptr);
return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
}
2010-05-29 07:25:38 +00:00
int CEditor::DoButton_ButtonInc(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
2007-05-22 15:03:32 +00:00
{
pRect->Draw(GetButtonColor(pID, Checked), IGraphics::CORNER_R, 3.0f);
2022-03-11 16:34:48 +00:00
UI()->DoLabel(pRect, pText ? pText : "+", 10, TEXTALIGN_CENTER);
2009-10-27 14:38:53 +00:00
return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
2007-05-22 15:03:32 +00:00
}
2010-05-29 07:25:38 +00:00
int CEditor::DoButton_ButtonDec(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
2007-05-22 15:03:32 +00:00
{
pRect->Draw(GetButtonColor(pID, Checked), IGraphics::CORNER_L, 3.0f);
2022-03-11 16:34:48 +00:00
UI()->DoLabel(pRect, pText ? pText : "-", 10, TEXTALIGN_CENTER);
2009-10-27 14:38:53 +00:00
return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
2007-05-22 15:03:32 +00:00
}
2019-04-26 12:06:32 +00:00
int CEditor::DoButton_ColorPicker(const void *pID, const CUIRect *pRect, ColorRGBA *pColor, const char *pToolTip)
{
pRect->Draw(*pColor, 0, 0.0f);
return DoButton_Editor_Common(pID, nullptr, 0, pRect, 0, pToolTip);
}
2023-03-15 17:15:43 +00:00
int CEditor::DoButton_DraggableEx(const void *pID, const char *pText, int Checked, const CUIRect *pRect, bool *pClicked, bool *pAbrupted, int Flags, const char *pToolTip, int Corners, float FontSize, int AlignVert)
{
pRect->Draw(GetButtonColor(pID, Checked), Corners, 3.0f);
SLabelProperties Props;
Props.m_AlignVertically = AlignVert;
UI()->DoLabel(pRect, pText, FontSize, TEXTALIGN_CENTER, Props);
if(UI()->MouseInside(pRect))
{
if(Flags & BUTTON_CONTEXT)
ms_pUiGotContext = pID;
}
UpdateTooltip(pID, pRect, pToolTip);
return UI()->DoDraggableButtonLogic(pID, Checked, pRect, pClicked, pAbrupted);
}
2011-07-10 20:16:16 +00:00
void CEditor::RenderGrid(CLayerGroup *pGroup)
{
if(!m_GridActive)
return;
float aGroupPoints[4];
2011-08-11 08:59:14 +00:00
pGroup->Mapping(aGroupPoints);
2011-07-10 20:16:16 +00:00
float w = UI()->Screen()->w;
float h = UI()->Screen()->h;
int LineDistance = GetLineDistance();
int XOffset = aGroupPoints[0] / LineDistance;
int YOffset = aGroupPoints[1] / LineDistance;
2011-07-10 20:16:16 +00:00
int XGridOffset = XOffset % m_GridFactor;
int YGridOffset = YOffset % m_GridFactor;
Graphics()->TextureClear();
2011-07-10 20:16:16 +00:00
Graphics()->LinesBegin();
for(int i = 0; i < (int)w; i++)
{
if((i + YGridOffset) % m_GridFactor == 0)
2011-07-10 20:16:16 +00:00
Graphics()->SetColor(1.0f, 0.3f, 0.3f, 0.3f);
else
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.15f);
IGraphics::CLineItem Line = IGraphics::CLineItem(LineDistance * XOffset, LineDistance * i + LineDistance * YOffset, w + aGroupPoints[2], LineDistance * i + LineDistance * YOffset);
2011-07-10 20:16:16 +00:00
Graphics()->LinesDraw(&Line, 1);
if((i + XGridOffset) % m_GridFactor == 0)
2011-07-10 20:16:16 +00:00
Graphics()->SetColor(1.0f, 0.3f, 0.3f, 0.3f);
else
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.15f);
Line = IGraphics::CLineItem(LineDistance * i + LineDistance * XOffset, LineDistance * YOffset, LineDistance * i + LineDistance * XOffset, h + aGroupPoints[3]);
2011-07-10 20:16:16 +00:00
Graphics()->LinesDraw(&Line, 1);
}
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
Graphics()->LinesEnd();
}
void CEditor::SnapToGrid(float &x, float &y)
{
const int GridDistance = GetLineDistance() * m_GridFactor;
x = (int)((x + (x >= 0 ? 1.0f : -1.0f) * GridDistance / 2) / GridDistance) * GridDistance;
y = (int)((y + (y >= 0 ? 1.0f : -1.0f) * GridDistance / 2) / GridDistance) * GridDistance;
}
void CEditor::RenderBackground(CUIRect View, IGraphics::CTextureHandle Texture, float Size, float Brightness)
2007-05-22 15:03:32 +00:00
{
2010-05-29 07:25:38 +00:00
Graphics()->TextureSet(Texture);
2009-10-27 14:38:53 +00:00
Graphics()->BlendNormal();
Graphics()->QuadsBegin();
2010-05-29 07:25:38 +00:00
Graphics()->SetColor(Brightness, Brightness, Brightness, 1.0f);
Graphics()->QuadsSetSubset(0, 0, View.w / Size, View.h / Size);
2010-05-29 07:25:38 +00:00
IGraphics::CQuadItem QuadItem(View.x, View.y, View.w, View.h);
Graphics()->QuadsDrawTL(&QuadItem, 1);
2009-10-27 14:38:53 +00:00
Graphics()->QuadsEnd();
2007-05-22 15:03:32 +00:00
}
int CEditor::UiDoValueSelector(void *pID, CUIRect *pRect, const char *pLabel, int Current, int Min, int Max, int Step, float Scale, const char *pToolTip, bool IsDegree, bool IsHex, int Corners, ColorRGBA *pColor, bool ShowValue)
{
2015-03-30 09:44:32 +00:00
// logic
static float s_Value;
2021-09-13 09:47:47 +00:00
static char s_aNumStr[64];
2015-03-30 09:44:32 +00:00
static bool s_TextMode = false;
static void *s_pLastTextpID = pID;
const bool Inside = UI()->MouseInside(pRect);
2015-03-30 09:44:32 +00:00
if(UI()->MouseButton(1) && UI()->HotItem() == pID)
{
s_pLastTextpID = pID;
2015-03-30 09:44:32 +00:00
s_TextMode = true;
m_LockMouse = false;
2017-03-21 10:24:44 +00:00
if(IsHex)
2021-09-13 09:47:47 +00:00
str_format(s_aNumStr, sizeof(s_aNumStr), "%06X", Current);
else
2021-09-13 09:47:47 +00:00
str_format(s_aNumStr, sizeof(s_aNumStr), "%d", Current);
2015-03-30 09:44:32 +00:00
}
if(UI()->CheckActiveItem(pID))
{
if(!UI()->MouseButton(0))
{
m_LockMouse = false;
UI()->SetActiveItem(nullptr);
s_TextMode = false;
}
}
if(s_TextMode && s_pLastTextpID == pID)
2015-03-30 09:44:32 +00:00
{
m_pTooltip = "Type your number";
static float s_NumberBoxID = 0;
2021-09-13 09:47:47 +00:00
DoEditBox(&s_NumberBoxID, pRect, s_aNumStr, sizeof(s_aNumStr), 10.0f, &s_NumberBoxID, false, Corners);
2015-03-30 09:44:32 +00:00
UI()->SetActiveItem(&s_NumberBoxID);
if(Input()->KeyIsPressed(KEY_RETURN) || Input()->KeyIsPressed(KEY_KP_ENTER) ||
((UI()->MouseButton(1) || UI()->MouseButton(0)) && !Inside))
2015-03-30 09:44:32 +00:00
{
2017-03-21 10:24:44 +00:00
if(IsHex)
2021-09-13 09:47:47 +00:00
Current = clamp(str_toint_base(s_aNumStr, 16), Min, Max);
else
2021-09-13 09:47:47 +00:00
Current = clamp(str_toint(s_aNumStr), Min, Max);
m_LockMouse = false;
UI()->SetActiveItem(nullptr);
2015-03-30 09:44:32 +00:00
s_TextMode = false;
}
if(Input()->KeyIsPressed(KEY_ESCAPE))
2015-03-30 09:44:32 +00:00
{
m_LockMouse = false;
UI()->SetActiveItem(nullptr);
2015-03-30 09:44:32 +00:00
s_TextMode = false;
}
}
else
{
if(UI()->CheckActiveItem(pID))
2015-03-30 09:44:32 +00:00
{
if(UI()->MouseButton(0))
2015-03-30 09:44:32 +00:00
{
if(Input()->ShiftIsPressed())
s_Value += m_MouseDeltaX * 0.05f;
2015-03-30 09:44:32 +00:00
else
s_Value += m_MouseDeltaX;
2011-08-11 08:59:14 +00:00
if(absolute(s_Value) >= Scale)
2015-03-30 09:44:32 +00:00
{
int Count = (int)(s_Value / Scale);
s_Value = std::fmod(s_Value, Scale);
Current += Step * Count;
2015-03-30 09:44:32 +00:00
Current = clamp(Current, Min, Max);
// Constrain to discrete steps
if(Count > 0)
Current = Current / Step * Step;
else
Current = std::ceil(Current / (float)Step) * Step;
2015-03-30 09:44:32 +00:00
}
}
if(pToolTip && !s_TextMode)
m_pTooltip = pToolTip;
}
else if(UI()->HotItem() == pID)
{
if(UI()->MouseButton(0))
{
m_LockMouse = true;
2015-03-30 09:44:32 +00:00
s_Value = 0;
UI()->SetActiveItem(pID);
}
if(pToolTip && !s_TextMode)
m_pTooltip = pToolTip;
}
if(Inside)
UI()->SetHotItem(pID);
// render
char aBuf[128];
2015-07-08 11:38:21 +00:00
if(pLabel[0] != '\0')
{
if(ShowValue)
str_format(aBuf, sizeof(aBuf), "%s %d", pLabel, Current);
else
str_copy(aBuf, pLabel);
}
2017-03-21 10:24:44 +00:00
else if(IsDegree)
str_format(aBuf, sizeof(aBuf), "%d°", Current);
2017-03-21 10:24:44 +00:00
else if(IsHex)
str_format(aBuf, sizeof(aBuf), "#%06X", Current);
2015-07-08 11:38:21 +00:00
else
str_format(aBuf, sizeof(aBuf), "%d", Current);
pRect->Draw(pColor ? *pColor : GetButtonColor(pID, 0), Corners, 5.0f);
2022-03-11 16:34:48 +00:00
UI()->DoLabel(pRect, aBuf, 10, TEXTALIGN_CENTER);
2015-03-30 09:44:32 +00:00
}
2015-07-09 00:08:14 +00:00
2010-05-29 07:25:38 +00:00
return Current;
2007-05-22 15:03:32 +00:00
}
CLayerGroup *CEditor::GetSelectedGroup() const
2007-05-22 15:03:32 +00:00
{
if(m_SelectedGroup >= 0 && m_SelectedGroup < (int)m_Map.m_vpGroups.size())
return m_Map.m_vpGroups[m_SelectedGroup];
return nullptr;
2007-05-22 15:03:32 +00:00
}
CLayer *CEditor::GetSelectedLayer(int Index) const
2007-05-22 15:03:32 +00:00
{
2010-05-29 07:25:38 +00:00
CLayerGroup *pGroup = GetSelectedGroup();
if(!pGroup)
return nullptr;
2018-10-02 01:52:01 +00:00
if(Index < 0 || Index >= (int)m_vSelectedLayers.size())
return nullptr;
2018-10-02 01:52:01 +00:00
int LayerIndex = m_vSelectedLayers[Index];
2008-01-12 12:27:55 +00:00
if(LayerIndex >= 0 && LayerIndex < (int)m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers.size())
return pGroup->m_vpLayers[LayerIndex];
return nullptr;
2007-05-22 15:03:32 +00:00
}
CLayer *CEditor::GetSelectedLayerType(int Index, int Type) const
2007-05-22 15:03:32 +00:00
{
2010-05-29 07:25:38 +00:00
CLayer *p = GetSelectedLayer(Index);
if(p && p->m_Type == Type)
2008-01-12 12:27:55 +00:00
return p;
return nullptr;
2007-05-22 15:03:32 +00:00
}
std::vector<CQuad *> CEditor::GetSelectedQuads()
2008-01-12 12:27:55 +00:00
{
CLayerQuads *pQuadLayer = (CLayerQuads *)GetSelectedLayerType(0, LAYERTYPE_QUADS);
std::vector<CQuad *> vpQuads;
if(!pQuadLayer)
return vpQuads;
vpQuads.resize(m_vSelectedQuads.size());
for(int i = 0; i < (int)m_vSelectedQuads.size(); ++i)
vpQuads[i] = &pQuadLayer->m_vQuads[m_vSelectedQuads[i]];
return vpQuads;
2008-01-12 12:27:55 +00:00
}
2007-12-02 17:55:45 +00:00
CSoundSource *CEditor::GetSelectedSource()
{
CLayerSounds *pSounds = (CLayerSounds *)GetSelectedLayerType(0, LAYERTYPE_SOUNDS);
if(!pSounds)
return nullptr;
if(m_SelectedSource >= 0 && m_SelectedSource < (int)pSounds->m_vSources.size())
return &pSounds->m_vSources[m_SelectedSource];
return nullptr;
}
void CEditor::SelectLayer(int LayerIndex, int GroupIndex)
{
if(GroupIndex != -1)
m_SelectedGroup = GroupIndex;
m_vSelectedLayers.clear();
m_vSelectedQuads.clear();
2022-02-26 17:49:06 +00:00
AddSelectedLayer(LayerIndex);
}
void CEditor::AddSelectedLayer(int LayerIndex)
{
m_vSelectedLayers.push_back(LayerIndex);
2022-02-26 17:49:06 +00:00
m_QuadKnifeActive = false;
}
2018-08-13 09:11:56 +00:00
void CEditor::SelectQuad(int Index)
{
m_vSelectedQuads.clear();
m_vSelectedQuads.push_back(Index);
2018-08-13 09:11:56 +00:00
}
void CEditor::DeleteSelectedQuads()
{
CLayerQuads *pLayer = (CLayerQuads *)GetSelectedLayerType(0, LAYERTYPE_QUADS);
if(!pLayer)
return;
for(int i = 0; i < (int)m_vSelectedQuads.size(); ++i)
2018-08-13 09:11:56 +00:00
{
pLayer->m_vQuads.erase(pLayer->m_vQuads.begin() + m_vSelectedQuads[i]);
for(int j = i + 1; j < (int)m_vSelectedQuads.size(); ++j)
if(m_vSelectedQuads[j] > m_vSelectedQuads[i])
m_vSelectedQuads[j]--;
2018-08-13 09:11:56 +00:00
m_vSelectedQuads.erase(m_vSelectedQuads.begin() + i);
i--;
2018-08-13 09:11:56 +00:00
}
2018-08-13 14:46:53 +00:00
}
bool CEditor::IsQuadSelected(int Index) const
2018-08-13 14:46:53 +00:00
{
return FindSelectedQuadIndex(Index) >= 0;
2018-08-13 14:46:53 +00:00
}
int CEditor::FindSelectedQuadIndex(int Index) const
2018-08-13 14:46:53 +00:00
{
for(size_t i = 0; i < m_vSelectedQuads.size(); ++i)
if(m_vSelectedQuads[i] == Index)
2018-08-13 14:46:53 +00:00
return i;
return -1;
2018-08-13 09:11:56 +00:00
}
void CEditor::CallbackOpenMap(const char *pFileName, int StorageType, void *pUser)
2010-09-12 11:15:59 +00:00
{
CEditor *pEditor = (CEditor *)pUser;
if(pEditor->Load(pFileName, StorageType))
2010-06-02 16:12:32 +00:00
{
pEditor->m_ValidSaveFilename = StorageType == IStorage::TYPE_SAVE && pEditor->m_pFileDialogPath == pEditor->m_aFileDialogCurrentFolder;
2010-09-12 11:15:59 +00:00
pEditor->m_Dialog = DIALOG_NONE;
}
2010-06-02 16:12:32 +00:00
}
void CEditor::CallbackAppendMap(const char *pFileName, int StorageType, void *pUser)
2010-06-02 16:12:32 +00:00
{
CEditor *pEditor = (CEditor *)pUser;
if(!pEditor->Append(pFileName, StorageType))
2010-06-02 16:12:32 +00:00
pEditor->m_aFileName[0] = 0;
2011-08-11 08:59:14 +00:00
2010-09-12 11:15:59 +00:00
pEditor->m_Dialog = DIALOG_NONE;
2010-06-02 16:12:32 +00:00
}
void CEditor::CallbackSaveMap(const char *pFileName, int StorageType, void *pUser)
{
CEditor *pEditor = static_cast<CEditor *>(pUser);
char aBuf[1024];
// add map extension
if(!str_endswith(pFileName, ".map"))
{
str_format(aBuf, sizeof(aBuf), "%s.map", pFileName);
pFileName = aBuf;
}
if(pEditor->Save(pFileName))
{
str_copy(pEditor->m_aFileName, pFileName, sizeof(pEditor->m_aFileName));
pEditor->m_ValidSaveFilename = StorageType == IStorage::TYPE_SAVE && pEditor->m_pFileDialogPath == pEditor->m_aFileDialogCurrentFolder;
pEditor->m_Map.m_Modified = false;
}
2011-08-11 08:59:14 +00:00
2010-09-12 11:15:59 +00:00
pEditor->m_Dialog = DIALOG_NONE;
}
2007-12-02 17:55:45 +00:00
void CEditor::CallbackSaveCopyMap(const char *pFileName, int StorageType, void *pUser)
{
CEditor *pEditor = static_cast<CEditor *>(pUser);
char aBuf[1024];
// add map extension
if(!str_endswith(pFileName, ".map"))
{
str_format(aBuf, sizeof(aBuf), "%s.map", pFileName);
pFileName = aBuf;
}
if(pEditor->Save(pFileName))
{
pEditor->m_Map.m_Modified = false;
}
pEditor->m_Dialog = DIALOG_NONE;
}
static int EntitiesListdirCallback(const char *pName, int IsDir, int StorageType, void *pUser)
{
CEditor *pEditor = (CEditor *)pUser;
if(!IsDir && str_endswith(pName, ".png"))
{
std::string Name = pName;
pEditor->m_vSelectEntitiesFiles.push_back(Name.substr(0, Name.length() - 4));
}
return 0;
}
2010-05-29 07:25:38 +00:00
void CEditor::DoToolbar(CUIRect ToolBar)
2007-05-22 15:03:32 +00:00
{
const bool ModPressed = Input()->ModifierIsPressed();
const bool ShiftPressed = Input()->ShiftIsPressed();
2020-05-15 16:14:39 +00:00
2010-05-29 07:25:38 +00:00
CUIRect TB_Top, TB_Bottom;
CUIRect Button;
2011-08-11 08:59:14 +00:00
2022-01-26 19:23:50 +00:00
ToolBar.HSplitMid(&TB_Top, &TB_Bottom, 5.0f);
2020-05-17 01:39:23 +00:00
// top line buttons
{
2020-05-17 01:39:23 +00:00
// detail button
TB_Top.VSplitLeft(40.0f, &Button, &TB_Top);
static int s_HqButton = 0;
if(DoButton_Editor(&s_HqButton, "HD", m_ShowDetail, &Button, 0, "[ctrl+h] Toggle High Detail") ||
(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && Input()->KeyPress(KEY_H) && ModPressed))
2020-05-17 01:39:23 +00:00
{
m_ShowDetail = !m_ShowDetail;
}
TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top);
2010-05-29 07:25:38 +00:00
2020-05-17 01:39:23 +00:00
// animation button
TB_Top.VSplitLeft(40.0f, &Button, &TB_Top);
static int s_AnimateButton = 0;
if(DoButton_Editor(&s_AnimateButton, "Anim", m_Animate, &Button, 0, "[ctrl+m] Toggle animation") ||
(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && Input()->KeyPress(KEY_M) && ModPressed))
2020-05-17 01:39:23 +00:00
{
m_AnimateStart = time_get();
m_Animate = !m_Animate;
}
2008-01-13 13:42:59 +00:00
TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top);
2008-01-13 13:42:59 +00:00
2020-05-17 01:39:23 +00:00
// proof button
TB_Top.VSplitLeft(40.0f, &Button, &TB_Top);
static int s_ProofButton = 0;
if(DoButton_Editor(&s_ProofButton, "Proof", m_ProofBorders, &Button, 0, "[ctrl+p] Toggles proof borders. These borders represent what a player maximum can see.") ||
(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && Input()->KeyPress(KEY_P) && ModPressed))
2020-05-17 01:39:23 +00:00
{
m_ProofBorders = !m_ProofBorders && !m_MenuProofBorders;
}
TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top);
// menu proof button
TB_Top.VSplitLeft(60.0f, &Button, &TB_Top);
static int s_MenuProofButton = 0;
if(DoButton_Editor(&s_MenuProofButton, "Proof Menu", m_MenuProofBorders, &Button, 0, "Toggles menu proof borders. These borders represent what will be shown in the menu."))
{
m_MenuProofBorders = !m_MenuProofBorders && !m_ProofBorders;
2020-05-17 01:39:23 +00:00
}
2008-01-13 13:42:59 +00:00
TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top);
// zoom button
TB_Top.VSplitLeft(40.0f, &Button, &TB_Top);
static int s_ZoomButton = 0;
if(DoButton_Editor(&s_ZoomButton, "Zoom", m_PreviewZoom, &Button, 0, "Toggles preview of how layers will be zoomed in-game"))
{
m_PreviewZoom = !m_PreviewZoom;
}
TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top);
2020-05-17 01:39:23 +00:00
// grid button
TB_Top.VSplitLeft(40.0f, &Button, &TB_Top);
static int s_GridButton = 0;
if(DoButton_Editor(&s_GridButton, "Grid", m_GridActive, &Button, 0, "[ctrl+g] Toggle Grid") ||
(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && Input()->KeyPress(KEY_G) && ModPressed && !ShiftPressed))
2020-05-17 01:39:23 +00:00
{
m_GridActive = !m_GridActive;
}
2016-05-01 00:34:06 +00:00
TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top);
2016-05-01 00:34:06 +00:00
2020-05-17 01:39:23 +00:00
// tile info button
TB_Top.VSplitLeft(40.0f, &Button, &TB_Top);
static int s_TileInfoButton = 0;
if(DoButton_Editor(&s_TileInfoButton, "Info", m_ShowTileInfo, &Button, 0, "[ctrl+i] Show tile information") ||
(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && Input()->KeyPress(KEY_I) && ModPressed && !ShiftPressed))
2020-05-17 01:39:23 +00:00
{
m_ShowTileInfo = !m_ShowTileInfo;
m_ShowEnvelopePreview = SHOWENV_NONE;
2020-05-17 01:39:23 +00:00
}
TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top);
2020-05-17 01:39:23 +00:00
// allow place unused tiles button
TB_Top.VSplitLeft(40.0f, &Button, &TB_Top);
static int s_AllowPlaceUnusedTilesButton = 0;
if(DoButton_Editor(&s_AllowPlaceUnusedTilesButton, "Unused", m_AllowPlaceUnusedTiles, &Button, 0, "[ctrl+u] Allow placing unused tiles") ||
(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && Input()->KeyPress(KEY_U) && ModPressed))
2020-05-17 01:39:23 +00:00
{
m_AllowPlaceUnusedTiles = !m_AllowPlaceUnusedTiles;
}
TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top);
2020-05-17 01:39:23 +00:00
TB_Top.VSplitLeft(40.0f, &Button, &TB_Top);
static int s_ColorBrushButton = 0;
if(DoButton_Editor(&s_ColorBrushButton, "Color", m_BrushColorEnabled, &Button, 0, "Toggle brush coloring"))
{
m_BrushColorEnabled = !m_BrushColorEnabled;
}
TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top);
2020-05-17 01:39:23 +00:00
TB_Top.VSplitLeft(45.0f, &Button, &TB_Top);
2021-05-17 07:11:36 +00:00
static int s_EntitiesButtonID = 0;
if(DoButton_Editor(&s_EntitiesButtonID, "Entities", 0, &Button, 0, "Choose game layer entities image for different gametypes"))
{
m_vSelectEntitiesFiles.clear();
2020-05-17 01:39:23 +00:00
Storage()->ListDirectory(IStorage::TYPE_ALL, "editor/entities", EntitiesListdirCallback, this);
std::sort(m_vSelectEntitiesFiles.begin(), m_vSelectEntitiesFiles.end());
2020-05-17 01:39:23 +00:00
static int s_EntitiesPopupID = 0;
UiInvokePopupMenu(&s_EntitiesPopupID, 0, Button.x, Button.y + 18.0f,
250, m_vSelectEntitiesFiles.size() * 14 + 10, PopupEntities);
2020-05-17 01:39:23 +00:00
}
TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top);
2010-05-29 07:25:38 +00:00
2020-05-17 01:39:23 +00:00
// zoom group
TB_Top.VSplitLeft(20.0f, &Button, &TB_Top);
2020-05-17 01:39:23 +00:00
static int s_ZoomOutButton = 0;
if(DoButton_FontIcon(&s_ZoomOutButton, "-", 0, &Button, 0, "[NumPad-] Zoom out", IGraphics::CORNER_L))
{
ChangeZoom(50.0f);
}
2010-05-29 07:25:38 +00:00
TB_Top.VSplitLeft(25.0f, &Button, &TB_Top);
2020-05-17 01:39:23 +00:00
static int s_ZoomNormalButton = 0;
if(DoButton_FontIcon(&s_ZoomNormalButton, "\xEF\x80\x82", 0, &Button, 0, "[NumPad*] Zoom to normal and remove editor offset", 0))
2020-05-17 01:39:23 +00:00
{
m_EditorOffsetX = 0;
m_EditorOffsetY = 0;
SetZoom(100.0f);
2020-05-17 01:39:23 +00:00
}
2010-05-29 07:25:38 +00:00
TB_Top.VSplitLeft(20.0f, &Button, &TB_Top);
2020-05-17 01:39:23 +00:00
static int s_ZoomInButton = 0;
if(DoButton_FontIcon(&s_ZoomInButton, "+", 0, &Button, 0, "[NumPad+] Zoom in", IGraphics::CORNER_R))
{
ChangeZoom(-50.0f);
}
2010-05-29 07:25:38 +00:00
TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top);
2010-05-29 07:25:38 +00:00
2020-05-17 01:39:23 +00:00
// brush manipulation
{
int Enabled = m_Brush.IsEmpty() ? -1 : 0;
2010-05-29 07:25:38 +00:00
2020-05-17 01:39:23 +00:00
// flip buttons
TB_Top.VSplitLeft(25.0f, &Button, &TB_Top);
2020-05-17 01:39:23 +00:00
static int s_FlipXButton = 0;
if(DoButton_FontIcon(&s_FlipXButton, "\xEF\x8C\xB7", Enabled, &Button, 0, "[N] Flip brush horizontal", IGraphics::CORNER_L) || (Input()->KeyPress(KEY_N) && m_Dialog == DIALOG_NONE && m_EditBoxActive == 0))
2020-05-17 01:39:23 +00:00
{
for(auto &pLayer : m_Brush.m_vpLayers)
pLayer->BrushFlipX();
2020-05-17 01:39:23 +00:00
}
2010-05-29 07:25:38 +00:00
TB_Top.VSplitLeft(25.0f, &Button, &TB_Top);
2020-05-17 01:39:23 +00:00
static int s_FlipyButton = 0;
if(DoButton_FontIcon(&s_FlipyButton, "\xEF\x81\xBD", Enabled, &Button, 0, "[M] Flip brush vertical", IGraphics::CORNER_R) || (Input()->KeyPress(KEY_M) && m_Dialog == DIALOG_NONE && m_EditBoxActive == 0))
2020-05-17 01:39:23 +00:00
{
for(auto &pLayer : m_Brush.m_vpLayers)
pLayer->BrushFlipY();
2020-05-17 01:39:23 +00:00
}
TB_Top.VSplitLeft(5.0f, nullptr, &TB_Top);
2008-01-12 12:27:55 +00:00
2020-05-17 01:39:23 +00:00
// rotate buttons
TB_Top.VSplitLeft(25.0f, &Button, &TB_Top);
2020-05-17 01:39:23 +00:00
static int s_RotationAmount = 90;
bool TileLayer = false;
// check for tile layers in brush selection
for(auto &pLayer : m_Brush.m_vpLayers)
if(pLayer->m_Type == LAYERTYPE_TILES)
2020-05-17 01:39:23 +00:00
{
TileLayer = true;
s_RotationAmount = maximum(90, (s_RotationAmount / 90) * 90);
2020-05-17 01:39:23 +00:00
break;
}
2008-01-12 12:27:55 +00:00
2020-05-17 01:39:23 +00:00
static int s_CcwButton = 0;
if(DoButton_FontIcon(&s_CcwButton, "\xEF\x8B\xAA", Enabled, &Button, 0, "[R] Rotates the brush counter clockwise", IGraphics::CORNER_ALL) || (Input()->KeyPress(KEY_R) && m_Dialog == DIALOG_NONE && m_EditBoxActive == 0))
2020-05-17 01:39:23 +00:00
{
for(auto &pLayer : m_Brush.m_vpLayers)
pLayer->BrushRotate(-s_RotationAmount / 360.0f * pi * 2);
2020-05-17 01:39:23 +00:00
}
2008-01-12 12:27:55 +00:00
2020-05-17 01:39:23 +00:00
TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
s_RotationAmount = UiDoValueSelector(&s_RotationAmount, &Button, "", s_RotationAmount, TileLayer ? 90 : 1, 359, TileLayer ? 90 : 1, TileLayer ? 10.0f : 2.0f, "Rotation of the brush in degrees. Use left mouse button to drag and change the value. Hold shift to be more precise.", true);
TB_Top.VSplitLeft(25.0f, &Button, &TB_Top);
2020-05-17 01:39:23 +00:00
static int s_CwButton = 0;
if(DoButton_FontIcon(&s_CwButton, "\xEF\x8B\xB9", Enabled, &Button, 0, "[T] Rotates the brush clockwise", IGraphics::CORNER_ALL) || (Input()->KeyPress(KEY_T) && m_Dialog == DIALOG_NONE && m_EditBoxActive == 0))
2020-05-17 01:39:23 +00:00
{
for(auto &pLayer : m_Brush.m_vpLayers)
pLayer->BrushRotate(s_RotationAmount / 360.0f * pi * 2);
2020-05-17 01:39:23 +00:00
}
2010-05-29 07:25:38 +00:00
2020-05-17 01:39:23 +00:00
TB_Top.VSplitLeft(5.0f, &Button, &TB_Top);
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
2020-05-17 01:39:23 +00:00
// animation speed
if(m_Animate)
2008-01-12 12:27:55 +00:00
{
TB_Top.VSplitLeft(20.0f, &Button, &TB_Top);
2020-05-17 01:39:23 +00:00
static int s_AnimSlowerButton = 0;
if(DoButton_FontIcon(&s_AnimSlowerButton, "-", 0, &Button, 0, "Decrease animation speed", IGraphics::CORNER_L))
{
2020-05-17 01:39:23 +00:00
if(m_AnimateSpeed > 0.5f)
m_AnimateSpeed -= 0.5f;
}
2010-05-29 07:25:38 +00:00
TB_Top.VSplitLeft(25.0f, &Button, &TB_Top);
static int s_AnimNormalButton = 0;
if(DoButton_FontIcon(&s_AnimNormalButton, "\xEF\x85\x84", 0, &Button, 0, "Normal animation speed", 0))
m_AnimateSpeed = 1.0f;
TB_Top.VSplitLeft(20.0f, &Button, &TB_Top);
static int s_AnimFasterButton = 0;
if(DoButton_FontIcon(&s_AnimFasterButton, "+", 0, &Button, 0, "Increase animation speed", IGraphics::CORNER_R))
m_AnimateSpeed += 0.5f;
2020-05-17 01:39:23 +00:00
TB_Top.VSplitLeft(5.0f, &Button, &TB_Top);
}
2010-05-29 07:25:38 +00:00
2020-05-17 01:39:23 +00:00
// grid zoom
if(m_GridActive)
{
TB_Top.VSplitLeft(20.0f, &Button, &TB_Top);
2020-05-17 01:39:23 +00:00
static int s_GridIncreaseButton = 0;
if(DoButton_FontIcon(&s_GridIncreaseButton, "-", 0, &Button, 0, "Decrease grid", IGraphics::CORNER_L))
2020-05-17 01:39:23 +00:00
{
if(m_GridFactor > 1)
m_GridFactor--;
}
2008-01-12 12:27:55 +00:00
TB_Top.VSplitLeft(25.0f, &Button, &TB_Top);
2020-05-17 01:39:23 +00:00
static int s_GridNormalButton = 0;
if(DoButton_FontIcon(&s_GridNormalButton, "\xEF\xA1\x8C", 0, &Button, 0, "Normal grid", 0))
2020-05-17 01:39:23 +00:00
m_GridFactor = 1;
TB_Top.VSplitLeft(20.0f, &Button, &TB_Top);
2020-05-17 01:39:23 +00:00
static int s_GridDecreaseButton = 0;
if(DoButton_FontIcon(&s_GridDecreaseButton, "+", 0, &Button, 0, "Increase grid", IGraphics::CORNER_R))
2020-05-17 01:39:23 +00:00
{
if(m_GridFactor < 15)
m_GridFactor++;
}
TB_Top.VSplitLeft(5.0f, &Button, &TB_Top);
2020-05-15 16:51:14 +00:00
}
2008-01-12 12:27:55 +00:00
}
2011-08-11 08:59:14 +00:00
2020-05-17 01:39:23 +00:00
// Bottom line buttons
2010-05-29 07:25:38 +00:00
{
2020-05-17 01:39:23 +00:00
// refocus button
2010-05-29 07:25:38 +00:00
{
2020-05-17 01:39:23 +00:00
TB_Bottom.VSplitLeft(45.0f, &Button, &TB_Bottom);
static int s_RefocusButton = 0;
int FocusButtonChecked;
if(m_MenuProofBorders)
{
if(distance(m_vMenuBackgroundPositions[m_CurrentMenuProofIndex], vec2(m_WorldOffsetX, m_WorldOffsetY)) < 0.0001f)
FocusButtonChecked = -1;
else
FocusButtonChecked = 1;
}
else
{
if(m_WorldOffsetX == 0 && m_WorldOffsetY == 0)
FocusButtonChecked = -1;
else
FocusButtonChecked = 1;
}
if(DoButton_Editor(&s_RefocusButton, "Refocus", FocusButtonChecked, &Button, 0, "[HOME] Restore map focus") || (m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && Input()->KeyPress(KEY_HOME)))
2020-05-15 16:51:14 +00:00
{
if(m_MenuProofBorders)
{
m_WorldOffsetX = m_vMenuBackgroundPositions[m_CurrentMenuProofIndex].x;
m_WorldOffsetY = m_vMenuBackgroundPositions[m_CurrentMenuProofIndex].y;
}
else
{
m_WorldOffsetX = 0;
m_WorldOffsetY = 0;
}
2020-05-15 16:51:14 +00:00
}
TB_Bottom.VSplitLeft(5.0f, nullptr, &TB_Bottom);
2010-05-29 07:25:38 +00:00
}
2016-05-01 00:34:06 +00:00
2022-08-20 18:47:46 +00:00
// goto xy button
{
TB_Bottom.VSplitLeft(50.0, &Button, &TB_Bottom);
static int s_GotoButton = 0;
if(DoButton_Editor(&s_GotoButton, "Goto XY", 0, &Button, 0, "Go to a specified coordinate point on the map"))
{
static int s_ModifierPopupID = 0;
if(!UiPopupExists(&s_ModifierPopupID))
{
UiInvokePopupMenu(&s_ModifierPopupID, 0, Button.x, Button.y + Button.h, 120, 52, PopupGoto);
}
}
TB_Bottom.VSplitLeft(5.0f, nullptr, &TB_Bottom);
}
2020-05-17 01:39:23 +00:00
// tile manipulation
{
2020-05-17 01:39:23 +00:00
CLayerTiles *pT = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES);
if(pT && !pT->m_Tele && !pT->m_Speedup && !pT->m_Switch && !pT->m_Front && !pT->m_Tune)
{
static int s_BorderBut = 0;
2020-05-17 01:39:23 +00:00
TB_Bottom.VSplitLeft(60.0f, &Button, &TB_Bottom);
if(DoButton_Ex(&s_BorderBut, "Border", 0, &Button, 0, "Place tiles in a 2-tile wide border at the edges of the layer", IGraphics::CORNER_ALL))
2020-05-15 16:51:14 +00:00
{
m_PopupEventType = POPEVENT_PLACE_BORDER_TILES;
m_PopupEventActivated = true;
}
2020-05-17 01:39:23 +00:00
TB_Bottom.VSplitLeft(5.0f, &Button, &TB_Bottom);
}
2020-05-15 16:51:14 +00:00
2020-05-17 01:39:23 +00:00
// do tele/tune/switch/speedup button
{
CLayerTiles *pS = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES);
if(pS)
{
const char *pButtonName = nullptr;
int (*pfnPopupFunc)(CEditor * pEditor, CUIRect View, void *pContext) = nullptr;
int Rows = 0;
2020-05-17 01:39:23 +00:00
if(pS == m_Map.m_pSwitchLayer)
{
pButtonName = "Switch";
pfnPopupFunc = PopupSwitch;
Rows = 2;
2020-05-17 01:39:23 +00:00
}
else if(pS == m_Map.m_pSpeedupLayer)
{
pButtonName = "Speedup";
pfnPopupFunc = PopupSpeedup;
Rows = 3;
2020-05-17 01:39:23 +00:00
}
else if(pS == m_Map.m_pTuneLayer)
{
pButtonName = "Tune";
pfnPopupFunc = PopupTune;
Rows = 1;
2020-05-17 01:39:23 +00:00
}
else if(pS == m_Map.m_pTeleLayer)
{
pButtonName = "Tele";
pfnPopupFunc = PopupTele;
Rows = 1;
2020-05-17 01:39:23 +00:00
}
if(pButtonName != nullptr)
2020-05-15 16:51:14 +00:00
{
2020-05-17 01:39:23 +00:00
static char aBuf[64];
str_format(aBuf, sizeof(aBuf), "[ctrl+t] %s", pButtonName);
2020-05-17 01:39:23 +00:00
TB_Bottom.VSplitLeft(60.0f, &Button, &TB_Bottom);
static int s_ModifierButton = 0;
if(DoButton_Ex(&s_ModifierButton, pButtonName, 0, &Button, 0, aBuf, IGraphics::CORNER_ALL) || (m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && ModPressed && Input()->KeyPress(KEY_T)))
{
2020-05-17 01:39:23 +00:00
static int s_ModifierPopupID = 0;
if(!UiPopupExists(&s_ModifierPopupID))
{
UiInvokePopupMenu(&s_ModifierPopupID, 0, Button.x, Button.y + Button.h, 120, 10.0f + Rows * 13.0f, pfnPopupFunc);
2020-05-17 01:39:23 +00:00
}
}
TB_Bottom.VSplitLeft(5.0f, nullptr, &TB_Bottom);
2020-05-15 16:51:14 +00:00
}
}
}
}
2020-05-17 01:39:23 +00:00
// do add quad/sound button
CLayer *pLayer = GetSelectedLayer(0);
if(pLayer && (pLayer->m_Type == LAYERTYPE_QUADS || pLayer->m_Type == LAYERTYPE_SOUNDS))
{
2020-05-17 01:39:23 +00:00
TB_Bottom.VSplitLeft(60.0f, &Button, &TB_Bottom);
2020-05-19 16:17:02 +00:00
bool Invoked = false;
2020-05-17 01:39:23 +00:00
static int s_AddItemButton = 0;
2016-05-01 00:34:06 +00:00
2020-05-17 01:39:23 +00:00
if(pLayer->m_Type == LAYERTYPE_QUADS)
2020-05-15 16:51:14 +00:00
{
2020-05-19 16:17:02 +00:00
Invoked = DoButton_Editor(&s_AddItemButton, "Add Quad", 0, &Button, 0, "[ctrl+q] Add a new quad") ||
(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && Input()->KeyPress(KEY_Q) && ModPressed);
2020-05-17 01:39:23 +00:00
}
else if(pLayer->m_Type == LAYERTYPE_SOUNDS)
{
2020-05-19 16:17:02 +00:00
Invoked = DoButton_Editor(&s_AddItemButton, "Add Sound", 0, &Button, 0, "[ctrl+q] Add a new sound source") ||
(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && Input()->KeyPress(KEY_Q) && ModPressed);
2020-05-15 16:51:14 +00:00
}
2020-05-19 16:17:02 +00:00
if(Invoked)
2020-05-15 16:51:14 +00:00
{
2020-05-17 01:39:23 +00:00
CLayerGroup *pGroup = GetSelectedGroup();
float aMapping[4];
pGroup->Mapping(aMapping);
int x = aMapping[0] + (aMapping[2] - aMapping[0]) / 2;
int y = aMapping[1] + (aMapping[3] - aMapping[1]) / 2;
if(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && Input()->KeyPress(KEY_Q) && ModPressed)
2016-05-01 00:34:06 +00:00
{
x += UI()->MouseWorldX() - (m_WorldOffsetX * pGroup->m_ParallaxX / 100) - pGroup->m_OffsetX;
y += UI()->MouseWorldY() - (m_WorldOffsetY * pGroup->m_ParallaxY / 100) - pGroup->m_OffsetY;
2016-05-01 00:34:06 +00:00
}
2020-05-15 16:51:14 +00:00
2020-05-17 01:39:23 +00:00
if(pLayer->m_Type == LAYERTYPE_QUADS)
{
CLayerQuads *pLayerQuads = (CLayerQuads *)pLayer;
2020-05-17 01:39:23 +00:00
int Width = 64;
int Height = 64;
if(pLayerQuads->m_Image >= 0)
2020-05-17 01:39:23 +00:00
{
Width = m_Map.m_vpImages[pLayerQuads->m_Image]->m_Width;
Height = m_Map.m_vpImages[pLayerQuads->m_Image]->m_Height;
2020-05-17 01:39:23 +00:00
}
pLayerQuads->NewQuad(x, y, Width, Height);
2020-05-17 01:39:23 +00:00
}
else if(pLayer->m_Type == LAYERTYPE_SOUNDS)
{
CLayerSounds *pLayerSounds = (CLayerSounds *)pLayer;
pLayerSounds->NewSource(x, y);
2020-05-17 01:39:23 +00:00
}
2016-05-01 00:34:06 +00:00
}
2020-05-17 01:39:23 +00:00
TB_Bottom.VSplitLeft(5.0f, &Button, &TB_Bottom);
2016-05-01 00:34:06 +00:00
}
2020-05-17 01:39:23 +00:00
// Brush draw mode button
{
TB_Bottom.VSplitLeft(65.0f, &Button, &TB_Bottom);
static int s_BrushDrawModeButton = 0;
if(DoButton_Editor(&s_BrushDrawModeButton, "Destructive", m_BrushDrawDestructive, &Button, 0, "[ctrl+d] Toggle brush draw mode") ||
(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && Input()->KeyPress(KEY_D) && ModPressed && !ShiftPressed))
2020-05-17 01:39:23 +00:00
m_BrushDrawDestructive = !m_BrushDrawDestructive;
TB_Bottom.VSplitLeft(5.0f, &Button, &TB_Bottom);
}
// Hex values button
if(m_ShowTileInfo)
{
TB_Bottom.VSplitLeft(65.0f, &Button, &TB_Bottom);
static int s_TileInfoHexButton = 0;
if(DoButton_Editor(&s_TileInfoHexButton, "Hex Values", m_ShowTileHexInfo, &Button, 0, "[ctrl+shift+i] Show a tile's hex value") ||
(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && Input()->KeyPress(KEY_I) && ModPressed && ShiftPressed))
m_ShowTileHexInfo = !m_ShowTileHexInfo;
}
2020-05-15 16:51:14 +00:00
}
2007-05-22 15:03:32 +00:00
}
static void Rotate(const CPoint *pCenter, CPoint *pPoint, float Rotation)
2007-07-15 13:25:10 +00:00
{
2010-05-29 07:25:38 +00:00
int x = pPoint->x - pCenter->x;
int y = pPoint->y - pCenter->y;
pPoint->x = (int)(x * std::cos(Rotation) - y * std::sin(Rotation) + pCenter->x);
pPoint->y = (int)(x * std::sin(Rotation) + y * std::cos(Rotation) + pCenter->y);
2007-07-15 13:25:10 +00:00
}
void CEditor::DoSoundSource(CSoundSource *pSource, int Index)
{
enum
{
OP_NONE = 0,
OP_MOVE,
OP_CONTEXT_MENU,
};
void *pID = &pSource->m_Position;
static int s_Operation = OP_NONE;
float wx = UI()->MouseWorldX();
float wy = UI()->MouseWorldY();
float CenterX = fx2f(pSource->m_Position.x);
float CenterY = fx2f(pSource->m_Position.y);
float dx = (CenterX - wx) / m_MouseWScale;
float dy = (CenterY - wy) / m_MouseWScale;
if(dx * dx + dy * dy < 50)
UI()->SetHotItem(pID);
const bool IgnoreGrid = Input()->AltIsPressed();
if(UI()->CheckActiveItem(pID))
{
if(m_MouseDeltaWx * m_MouseDeltaWx + m_MouseDeltaWy * m_MouseDeltaWy > 0.0f)
{
if(s_Operation == OP_MOVE)
{
float x = wx;
float y = wy;
if(m_GridActive && !IgnoreGrid)
SnapToGrid(x, y);
pSource->m_Position.x = f2fx(x);
pSource->m_Position.y = f2fx(y);
}
}
if(s_Operation == OP_CONTEXT_MENU)
{
if(!UI()->MouseButton(1))
{
if(m_vSelectedLayers.size() == 1)
{
static int s_SourcePopupID = 0;
2020-01-23 20:24:48 +00:00
UiInvokePopupMenu(&s_SourcePopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 200, PopupSource);
m_LockMouse = false;
}
s_Operation = OP_NONE;
UI()->SetActiveItem(nullptr);
}
}
else
{
if(!UI()->MouseButton(0))
{
m_LockMouse = false;
s_Operation = OP_NONE;
UI()->SetActiveItem(nullptr);
}
}
Graphics()->SetColor(1, 1, 1, 1);
}
else if(UI()->HotItem() == pID)
{
ms_pUiGotContext = pID;
Graphics()->SetColor(1, 1, 1, 1);
m_pTooltip = "Left mouse button to move. Hold alt to ignore grid.";
if(UI()->MouseButton(0))
{
s_Operation = OP_MOVE;
UI()->SetActiveItem(pID);
m_SelectedSource = Index;
}
if(UI()->MouseButton(1))
{
m_SelectedSource = Index;
s_Operation = OP_CONTEXT_MENU;
UI()->SetActiveItem(pID);
}
}
else
{
Graphics()->SetColor(0, 1, 0, 1);
}
IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f * m_MouseWScale, 5.0f * m_MouseWScale);
Graphics()->QuadsDraw(&QuadItem, 1);
}
void CEditor::DoQuad(CQuad *pQuad, int Index)
2007-05-22 15:03:32 +00:00
{
2008-01-12 12:27:55 +00:00
enum
{
OP_NONE = 0,
2008-01-12 12:27:55 +00:00
OP_MOVE_ALL,
OP_MOVE_PIVOT,
OP_ROTATE,
2008-01-17 23:09:49 +00:00
OP_CONTEXT_MENU,
OP_DELETE,
2008-01-12 12:27:55 +00:00
};
2010-05-29 07:25:38 +00:00
2008-01-12 12:27:55 +00:00
// some basic values
void *pID = &pQuad->m_aPoints[4]; // use pivot addr as id
2022-06-15 17:34:41 +00:00
static std::vector<std::vector<CPoint>> s_vvRotatePoints;
2010-05-29 07:25:38 +00:00
static int s_Operation = OP_NONE;
static float s_RotateAngle = 0;
2009-10-27 14:38:53 +00:00
float wx = UI()->MouseWorldX();
float wy = UI()->MouseWorldY();
2010-05-29 07:25:38 +00:00
2008-01-12 12:27:55 +00:00
// get pivot
float CenterX = fx2f(pQuad->m_aPoints[4].x);
float CenterY = fx2f(pQuad->m_aPoints[4].y);
2008-01-12 12:27:55 +00:00
float dx = (CenterX - wx) / m_MouseWScale;
float dy = (CenterY - wy) / m_MouseWScale;
if(dx * dx + dy * dy < 50)
UI()->SetHotItem(pID);
2008-01-12 12:27:55 +00:00
const bool IgnoreGrid = Input()->AltIsPressed();
2010-05-29 07:25:38 +00:00
// draw selection background
2018-08-13 09:11:56 +00:00
if(IsQuadSelected(Index))
2007-05-22 15:03:32 +00:00
{
Graphics()->SetColor(0, 0, 0, 1);
IGraphics::CQuadItem QuadItem(CenterX, CenterY, 7.0f * m_MouseWScale, 7.0f * m_MouseWScale);
2010-05-29 07:25:38 +00:00
Graphics()->QuadsDraw(&QuadItem, 1);
2007-05-22 15:03:32 +00:00
}
2010-05-29 07:25:38 +00:00
if(UI()->CheckActiveItem(pID))
2007-05-22 15:03:32 +00:00
{
if(m_MouseDeltaWx * m_MouseDeltaWx + m_MouseDeltaWy * m_MouseDeltaWy > 0.0f)
2008-01-12 12:27:55 +00:00
{
// check if we only should move pivot
if(s_Operation == OP_MOVE_PIVOT)
2008-01-12 12:27:55 +00:00
{
float x = wx;
float y = wy;
if(m_GridActive && !IgnoreGrid)
SnapToGrid(x, y);
pQuad->m_aPoints[4].x = f2fx(x);
pQuad->m_aPoints[4].y = f2fx(y);
2011-07-10 20:16:16 +00:00
}
else if(s_Operation == OP_MOVE_ALL)
2008-01-12 12:27:55 +00:00
{
// move all points including pivot
float x = wx;
float y = wy;
if(m_GridActive && !IgnoreGrid)
SnapToGrid(x, y);
2011-08-11 08:59:14 +00:00
int OffsetX = f2fx(x) - pQuad->m_aPoints[4].x;
int OffsetY = f2fx(y) - pQuad->m_aPoints[4].y;
2011-08-11 08:59:14 +00:00
CLayerQuads *pLayer = (CLayerQuads *)GetSelectedLayerType(0, LAYERTYPE_QUADS);
for(auto &Selected : m_vSelectedQuads)
2011-07-10 20:16:16 +00:00
{
CQuad *pCurrentQuad = &pLayer->m_vQuads[Selected];
for(auto &Point : pCurrentQuad->m_aPoints)
{
Point.x += OffsetX;
Point.y += OffsetY;
}
2011-07-10 20:16:16 +00:00
}
2008-01-12 12:27:55 +00:00
}
else if(s_Operation == OP_ROTATE)
2008-01-12 12:27:55 +00:00
{
2018-08-13 09:11:56 +00:00
CLayerQuads *pLayer = (CLayerQuads *)GetSelectedLayerType(0, LAYERTYPE_QUADS);
for(size_t i = 0; i < m_vSelectedQuads.size(); ++i)
{
CQuad *pCurrentQuad = &pLayer->m_vQuads[m_vSelectedQuads[i]];
2018-08-13 09:11:56 +00:00
for(int v = 0; v < 4; v++)
{
2022-06-15 17:34:41 +00:00
pCurrentQuad->m_aPoints[v] = s_vvRotatePoints[i][v];
2018-08-13 09:11:56 +00:00
Rotate(&pCurrentQuad->m_aPoints[4], &pCurrentQuad->m_aPoints[v], s_RotateAngle);
}
}
2008-01-12 12:27:55 +00:00
}
2007-05-22 15:03:32 +00:00
}
2010-05-29 07:25:38 +00:00
s_RotateAngle += (m_MouseDeltaX)*0.002f;
2010-05-29 07:25:38 +00:00
if(s_Operation == OP_CONTEXT_MENU)
2007-05-22 15:03:32 +00:00
{
2009-10-27 14:38:53 +00:00
if(!UI()->MouseButton(1))
2008-01-17 23:09:49 +00:00
{
if(m_vSelectedLayers.size() == 1)
{
2018-08-13 09:11:56 +00:00
m_SelectedQuadIndex = FindSelectedQuadIndex(Index);
static int s_QuadPopupID = 0;
2022-02-26 17:49:06 +00:00
UiInvokePopupMenu(&s_QuadPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 198, PopupQuad);
m_LockMouse = false;
}
2010-05-29 07:25:38 +00:00
s_Operation = OP_NONE;
UI()->SetActiveItem(nullptr);
2008-01-17 23:09:49 +00:00
}
2007-05-22 15:03:32 +00:00
}
else if(s_Operation == OP_DELETE)
{
if(!UI()->MouseButton(1))
{
if(m_vSelectedLayers.size() == 1)
{
m_LockMouse = false;
m_Map.m_Modified = true;
2018-08-13 09:11:56 +00:00
DeleteSelectedQuads();
}
s_Operation = OP_NONE;
UI()->SetActiveItem(nullptr);
}
}
2008-01-17 23:09:49 +00:00
else
{
2009-10-27 14:38:53 +00:00
if(!UI()->MouseButton(0))
2008-01-17 23:09:49 +00:00
{
m_LockMouse = false;
2010-05-29 07:25:38 +00:00
s_Operation = OP_NONE;
UI()->SetActiveItem(nullptr);
2008-01-17 23:09:49 +00:00
}
2010-05-29 07:25:38 +00:00
}
2008-01-12 12:27:55 +00:00
Graphics()->SetColor(1, 1, 1, 1);
2008-01-12 12:27:55 +00:00
}
else if(UI()->HotItem() == pID)
2008-01-12 12:27:55 +00:00
{
ms_pUiGotContext = pID;
2010-05-29 07:25:38 +00:00
Graphics()->SetColor(1, 1, 1, 1);
m_pTooltip = "Left mouse button to move. Hold shift to move pivot. Hold ctrl to rotate. Hold alt to ignore grid. Hold shift and right click to delete.";
2010-05-29 07:25:38 +00:00
2009-10-27 14:38:53 +00:00
if(UI()->MouseButton(0))
2008-01-12 12:27:55 +00:00
{
if(Input()->ShiftIsPressed())
2018-08-13 09:11:56 +00:00
{
2010-05-29 07:25:38 +00:00
s_Operation = OP_MOVE_PIVOT;
2018-08-13 09:11:56 +00:00
SelectQuad(Index);
}
else if(Input()->ModifierIsPressed())
2008-01-12 12:27:55 +00:00
{
m_LockMouse = true;
2010-05-29 07:25:38 +00:00
s_Operation = OP_ROTATE;
s_RotateAngle = 0;
2018-10-02 01:52:01 +00:00
2018-08-13 09:11:56 +00:00
if(!IsQuadSelected(Index))
SelectQuad(Index);
2018-10-02 01:52:01 +00:00
2018-08-13 09:11:56 +00:00
CLayerQuads *pLayer = (CLayerQuads *)GetSelectedLayerType(0, LAYERTYPE_QUADS);
2022-06-15 17:34:41 +00:00
s_vvRotatePoints.clear();
s_vvRotatePoints.resize(m_vSelectedQuads.size());
for(size_t i = 0; i < m_vSelectedQuads.size(); ++i)
2018-08-13 09:11:56 +00:00
{
CQuad *pCurrentQuad = &pLayer->m_vQuads[m_vSelectedQuads[i]];
2018-08-13 09:11:56 +00:00
2022-06-15 17:34:41 +00:00
s_vvRotatePoints[i].resize(4);
s_vvRotatePoints[i][0] = pCurrentQuad->m_aPoints[0];
s_vvRotatePoints[i][1] = pCurrentQuad->m_aPoints[1];
s_vvRotatePoints[i][2] = pCurrentQuad->m_aPoints[2];
s_vvRotatePoints[i][3] = pCurrentQuad->m_aPoints[3];
2018-08-13 09:11:56 +00:00
}
2008-01-12 12:27:55 +00:00
}
else
2018-08-13 09:11:56 +00:00
{
2010-05-29 07:25:38 +00:00
s_Operation = OP_MOVE_ALL;
2018-10-02 01:52:01 +00:00
2018-08-13 09:11:56 +00:00
if(!IsQuadSelected(Index))
SelectQuad(Index);
}
2010-05-29 07:25:38 +00:00
UI()->SetActiveItem(pID);
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
2009-10-27 14:38:53 +00:00
if(UI()->MouseButton(1))
2008-01-17 23:09:49 +00:00
{
if(Input()->ShiftIsPressed())
{
s_Operation = OP_DELETE;
2018-08-13 09:11:56 +00:00
if(!IsQuadSelected(Index))
SelectQuad(Index);
UI()->SetActiveItem(pID);
}
else
{
s_Operation = OP_CONTEXT_MENU;
2018-08-13 09:11:56 +00:00
if(!IsQuadSelected(Index))
SelectQuad(Index);
UI()->SetActiveItem(pID);
}
2008-01-17 23:09:49 +00:00
}
2007-05-22 15:03:32 +00:00
}
2008-01-12 12:27:55 +00:00
else
Graphics()->SetColor(0, 1, 0, 1);
2008-01-12 12:27:55 +00:00
IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f * m_MouseWScale, 5.0f * m_MouseWScale);
2010-05-29 07:25:38 +00:00
Graphics()->QuadsDraw(&QuadItem, 1);
2008-01-12 12:27:55 +00:00
}
void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V)
2008-01-12 12:27:55 +00:00
{
void *pID = &pQuad->m_aPoints[V];
2008-01-12 12:27:55 +00:00
2009-10-27 14:38:53 +00:00
float wx = UI()->MouseWorldX();
float wy = UI()->MouseWorldY();
2010-05-29 07:25:38 +00:00
float px = fx2f(pQuad->m_aPoints[V].x);
float py = fx2f(pQuad->m_aPoints[V].y);
2010-05-29 07:25:38 +00:00
float dx = (px - wx) / m_MouseWScale;
float dy = (py - wy) / m_MouseWScale;
if(dx * dx + dy * dy < 50)
UI()->SetHotItem(pID);
2008-01-12 12:27:55 +00:00
2010-05-29 07:25:38 +00:00
// draw selection background
if(IsQuadSelected(QuadIndex) && m_SelectedPoints & (1 << V))
2008-01-12 12:27:55 +00:00
{
Graphics()->SetColor(0, 0, 0, 1);
IGraphics::CQuadItem QuadItem(px, py, 7.0f * m_MouseWScale, 7.0f * m_MouseWScale);
2010-05-29 07:25:38 +00:00
Graphics()->QuadsDraw(&QuadItem, 1);
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
2008-01-12 12:27:55 +00:00
enum
{
OP_NONE = 0,
2008-01-12 12:27:55 +00:00
OP_MOVEPOINT,
2008-01-17 23:09:49 +00:00
OP_MOVEUV,
OP_CONTEXT_MENU
2008-01-12 12:27:55 +00:00
};
2010-05-29 07:25:38 +00:00
static bool s_Moved;
static int s_Operation = OP_NONE;
const bool IgnoreGrid = Input()->AltIsPressed();
if(UI()->CheckActiveItem(pID))
2007-05-22 15:03:32 +00:00
{
2010-05-29 07:25:38 +00:00
if(!s_Moved)
2007-05-22 15:03:32 +00:00
{
if(m_MouseDeltaWx * m_MouseDeltaWx + m_MouseDeltaWy * m_MouseDeltaWy > 0.0f)
2010-05-29 07:25:38 +00:00
s_Moved = true;
2007-05-22 15:03:32 +00:00
}
2010-05-29 07:25:38 +00:00
if(s_Moved)
2007-05-22 15:03:32 +00:00
{
2010-05-29 07:25:38 +00:00
if(s_Operation == OP_MOVEPOINT)
2007-05-22 15:03:32 +00:00
{
float x = wx;
float y = wy;
if(m_GridActive && !IgnoreGrid)
SnapToGrid(x, y);
2011-07-10 20:16:16 +00:00
int OffsetX = f2fx(x) - pQuad->m_aPoints[V].x;
int OffsetY = f2fx(y) - pQuad->m_aPoints[V].y;
2018-08-13 09:11:56 +00:00
CLayerQuads *pLayer = (CLayerQuads *)GetSelectedLayerType(0, LAYERTYPE_QUADS);
for(auto &Selected : m_vSelectedQuads)
2011-07-10 20:16:16 +00:00
{
CQuad *pCurrentQuad = &pLayer->m_vQuads[Selected];
for(int m = 0; m < 4; m++)
2018-08-13 09:11:56 +00:00
{
if(m_SelectedPoints & (1 << m))
{
pCurrentQuad->m_aPoints[m].x += OffsetX;
pCurrentQuad->m_aPoints[m].y += OffsetY;
}
2018-08-13 09:11:56 +00:00
}
2011-07-10 20:16:16 +00:00
}
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
else if(s_Operation == OP_MOVEUV)
2008-01-12 12:27:55 +00:00
{
2018-08-13 09:11:56 +00:00
CLayerQuads *pLayer = (CLayerQuads *)GetSelectedLayerType(0, LAYERTYPE_QUADS);
for(auto &Selected : m_vSelectedQuads)
2018-08-13 09:11:56 +00:00
{
CQuad *pCurrentQuad = &pLayer->m_vQuads[Selected];
2018-08-13 09:11:56 +00:00
for(int m = 0; m < 4; m++)
if(m_SelectedPoints & (1 << m))
2018-08-13 09:11:56 +00:00
{
// 0,2;1,3 - line x
// 0,1;2,3 - line y
pCurrentQuad->m_aTexcoords[m].x += f2fx(m_MouseDeltaWx * 0.001f);
pCurrentQuad->m_aTexcoords[(m + 2) % 4].x += f2fx(m_MouseDeltaWx * 0.001f);
2011-08-11 08:59:14 +00:00
pCurrentQuad->m_aTexcoords[m].y += f2fx(m_MouseDeltaWy * 0.001f);
pCurrentQuad->m_aTexcoords[m ^ 1].y += f2fx(m_MouseDeltaWy * 0.001f);
2018-08-13 09:11:56 +00:00
}
}
2007-05-22 15:03:32 +00:00
}
}
2010-05-29 07:25:38 +00:00
if(s_Operation == OP_CONTEXT_MENU)
2007-05-22 15:03:32 +00:00
{
2009-10-27 14:38:53 +00:00
if(!UI()->MouseButton(1))
2007-05-22 15:03:32 +00:00
{
if(m_vSelectedLayers.size() == 1)
{
2018-08-13 09:11:56 +00:00
m_SelectedQuadPoint = V;
m_SelectedQuadIndex = FindSelectedQuadIndex(QuadIndex);
static int s_PointPopupID = 0;
UiInvokePopupMenu(&s_PointPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 150, PopupPoint);
}
UI()->SetActiveItem(nullptr);
2008-01-17 23:09:49 +00:00
}
}
else
{
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(!s_Moved)
2008-01-17 23:09:49 +00:00
{
if(Input()->ShiftIsPressed())
m_SelectedPoints ^= 1 << V;
2008-01-17 23:09:49 +00:00
else
m_SelectedPoints = 1 << V;
2008-01-17 23:09:49 +00:00
}
2014-01-19 17:06:56 +00:00
m_LockMouse = false;
UI()->SetActiveItem(nullptr);
2007-05-22 15:03:32 +00:00
}
}
2008-01-12 12:27:55 +00:00
Graphics()->SetColor(1, 1, 1, 1);
2007-05-22 15:03:32 +00:00
}
else if(UI()->HotItem() == pID)
2008-01-12 12:27:55 +00:00
{
ms_pUiGotContext = pID;
2010-05-29 07:25:38 +00:00
Graphics()->SetColor(1, 1, 1, 1);
m_pTooltip = "Left mouse button to move. Hold shift to move the texture. Hold alt to ignore grid.";
2010-05-29 07:25:38 +00:00
2009-10-27 14:38:53 +00:00
if(UI()->MouseButton(0))
2008-01-12 12:27:55 +00:00
{
UI()->SetActiveItem(pID);
2010-05-29 07:25:38 +00:00
s_Moved = false;
if(Input()->ShiftIsPressed())
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
s_Operation = OP_MOVEUV;
m_LockMouse = true;
2008-01-12 12:27:55 +00:00
}
else
2010-05-29 07:25:38 +00:00
s_Operation = OP_MOVEPOINT;
if(!(m_SelectedPoints & (1 << V)))
2008-01-12 12:27:55 +00:00
{
if(Input()->ShiftIsPressed())
m_SelectedPoints |= 1 << V;
2008-01-12 12:27:55 +00:00
else
m_SelectedPoints = 1 << V;
2011-11-26 00:44:24 +00:00
s_Moved = true;
2008-01-12 12:27:55 +00:00
}
2018-10-02 01:52:01 +00:00
2018-08-13 09:11:56 +00:00
if(!IsQuadSelected(QuadIndex))
SelectQuad(QuadIndex);
2008-01-17 23:09:49 +00:00
}
2009-10-27 14:38:53 +00:00
else if(UI()->MouseButton(1))
2008-01-17 23:09:49 +00:00
{
2010-05-29 07:25:38 +00:00
s_Operation = OP_CONTEXT_MENU;
2018-08-13 09:11:56 +00:00
if(!IsQuadSelected(QuadIndex))
SelectQuad(QuadIndex);
UI()->SetActiveItem(pID);
if(!(m_SelectedPoints & (1 << V)))
{
if(Input()->ShiftIsPressed())
m_SelectedPoints |= 1 << V;
else
m_SelectedPoints = 1 << V;
s_Moved = true;
}
2008-01-12 12:27:55 +00:00
}
}
else
Graphics()->SetColor(1, 0, 0, 1);
2010-05-29 07:25:38 +00:00
IGraphics::CQuadItem QuadItem(px, py, 5.0f * m_MouseWScale, 5.0f * m_MouseWScale);
2010-05-29 07:25:38 +00:00
Graphics()->QuadsDraw(&QuadItem, 1);
2007-05-22 15:03:32 +00:00
}
2022-02-26 17:49:06 +00:00
float CEditor::TriangleArea(vec2 A, vec2 B, vec2 C)
{
return absolute(((B.x - A.x) * (C.y - A.y) - (C.x - A.x) * (B.y - A.y)) * 0.5f);
2022-02-26 17:49:06 +00:00
}
bool CEditor::IsInTriangle(vec2 Point, vec2 A, vec2 B, vec2 C)
{
// Normalize to increase precision
2022-02-26 23:44:34 +00:00
vec2 Min(minimum(A.x, B.x, C.x), minimum(A.y, B.y, C.y));
vec2 Max(maximum(A.x, B.x, C.x), maximum(A.y, B.y, C.y));
2022-02-26 17:49:06 +00:00
vec2 Size(Max.x - Min.x, Max.y - Min.y);
2022-02-26 23:44:34 +00:00
if(Size.x < 0.0000001f || Size.y < 0.0000001f)
2022-02-26 17:49:06 +00:00
return false;
vec2 Normal(1.f / Size.x, 1.f / Size.y);
A = (A - Min) * Normal;
B = (B - Min) * Normal;
C = (C - Min) * Normal;
Point = (Point - Min) * Normal;
float Area = TriangleArea(A, B, C);
return Area > 0.f && absolute(TriangleArea(Point, A, B) + TriangleArea(Point, B, C) + TriangleArea(Point, C, A) - Area) < 0.000001f;
2022-02-26 17:49:06 +00:00
}
void CEditor::DoQuadKnife(int QuadIndex)
{
CLayerQuads *pLayer = (CLayerQuads *)GetSelectedLayerType(0, LAYERTYPE_QUADS);
CQuad *pQuad = &pLayer->m_vQuads[QuadIndex];
2022-02-26 17:49:06 +00:00
const bool IgnoreGrid = Input()->AltIsPressed();
float SnapRadius = 4.f * m_MouseWScale;
2022-02-26 17:49:06 +00:00
vec2 Mouse = vec2(UI()->MouseWorldX(), UI()->MouseWorldY());
vec2 Point = Mouse;
vec2 v[4] = {
vec2(fx2f(pQuad->m_aPoints[0].x), fx2f(pQuad->m_aPoints[0].y)),
vec2(fx2f(pQuad->m_aPoints[1].x), fx2f(pQuad->m_aPoints[1].y)),
vec2(fx2f(pQuad->m_aPoints[3].x), fx2f(pQuad->m_aPoints[3].y)),
vec2(fx2f(pQuad->m_aPoints[2].x), fx2f(pQuad->m_aPoints[2].y))};
m_pTooltip = "Left click inside the quad to select an area to slice. Hold alt to ignore grid. Right click to leave knife mode";
if(UI()->MouseButtonClicked(1))
{
m_QuadKnifeActive = false;
return;
}
2022-03-01 10:33:53 +00:00
2022-02-26 17:49:06 +00:00
// Handle snapping
if(m_GridActive && !IgnoreGrid)
{
float CellSize = (float)GetLineDistance();
vec2 OnGrid = vec2(std::round(Mouse.x / CellSize) * CellSize, std::round(Mouse.y / CellSize) * CellSize);
2022-02-26 17:49:06 +00:00
2022-03-01 10:33:53 +00:00
if(IsInTriangle(OnGrid, v[0], v[1], v[2]) || IsInTriangle(OnGrid, v[0], v[3], v[2]))
2022-02-26 17:49:06 +00:00
Point = OnGrid;
else
{
float MinDistance = -1.f;
for(int i = 0; i < 4; i++)
{
int j = (i + 1) % 4;
vec2 Min(minimum(v[i].x, v[j].x), minimum(v[i].y, v[j].y));
vec2 Max(maximum(v[i].x, v[j].x), maximum(v[i].y, v[j].y));
2022-02-26 23:44:34 +00:00
if(in_range(OnGrid.y, Min.y, Max.y) && Max.y - Min.y > 0.0000001f)
2022-02-26 17:49:06 +00:00
{
vec2 OnEdge(v[i].x + (OnGrid.y - v[i].y) / (v[j].y - v[i].y) * (v[j].x - v[i].x), OnGrid.y);
float Distance = absolute(OnGrid.x - OnEdge.x);
2022-02-26 17:49:06 +00:00
if(Distance < CellSize && (Distance < MinDistance || MinDistance < 0.f))
{
MinDistance = Distance;
Point = OnEdge;
}
}
2022-03-01 10:33:53 +00:00
2022-02-26 23:44:34 +00:00
if(in_range(OnGrid.x, Min.x, Max.x) && Max.x - Min.x > 0.0000001f)
2022-02-26 17:49:06 +00:00
{
vec2 OnEdge(OnGrid.x, v[i].y + (OnGrid.x - v[i].x) / (v[j].x - v[i].x) * (v[j].y - v[i].y));
float Distance = absolute(OnGrid.y - OnEdge.y);
2022-02-26 17:49:06 +00:00
if(Distance < CellSize && (Distance < MinDistance || MinDistance < 0.f))
{
MinDistance = Distance;
Point = OnEdge;
}
}
}
}
}
else
{
float MinDistance = -1.f;
// Try snapping to corners
2022-03-01 10:33:53 +00:00
for(const auto &x : v)
2022-02-26 17:49:06 +00:00
{
2022-03-01 10:33:53 +00:00
float Distance = distance(Mouse, x);
2022-02-26 17:49:06 +00:00
if(Distance <= SnapRadius && (Distance < MinDistance || MinDistance < 0.f))
{
MinDistance = Distance;
2022-03-01 10:33:53 +00:00
Point = x;
2022-02-26 17:49:06 +00:00
}
}
2022-03-01 10:33:53 +00:00
if(MinDistance < 0.f)
2022-02-26 17:49:06 +00:00
{
// Try snapping to edges
for(int i = 0; i < 4; i++)
{
int j = (i + 1) % 4;
vec2 s(v[j] - v[i]);
float t = ((Mouse.x - v[i].x) * s.x + (Mouse.y - v[i].y) * s.y) / (s.x * s.x + s.y * s.y);
if(in_range(t, 0.f, 1.f))
{
vec2 OnEdge = vec2((v[i].x + t * s.x), (v[i].y + t * s.y));
float Distance = distance(Mouse, OnEdge);
if(Distance <= SnapRadius && (Distance < MinDistance || MinDistance < 0.f))
{
MinDistance = Distance;
Point = OnEdge;
}
}
}
}
}
bool ValidPosition = IsInTriangle(Point, v[0], v[1], v[2]) || IsInTriangle(Point, v[0], v[3], v[2]);
2022-03-01 10:33:53 +00:00
if(UI()->MouseButtonClicked(0) && ValidPosition)
2022-02-26 23:44:34 +00:00
{
m_aQuadKnifePoints[m_QuadKnifeCount] = Point;
m_QuadKnifeCount++;
}
2022-02-26 17:49:06 +00:00
if(m_QuadKnifeCount == 4)
{
if(IsInTriangle(m_aQuadKnifePoints[3], m_aQuadKnifePoints[0], m_aQuadKnifePoints[1], m_aQuadKnifePoints[2]) ||
IsInTriangle(m_aQuadKnifePoints[1], m_aQuadKnifePoints[0], m_aQuadKnifePoints[2], m_aQuadKnifePoints[3]))
{
// Fix concave order
2022-05-15 17:25:36 +00:00
std::swap(m_aQuadKnifePoints[0], m_aQuadKnifePoints[3]);
std::swap(m_aQuadKnifePoints[1], m_aQuadKnifePoints[2]);
2022-02-26 17:49:06 +00:00
}
2022-05-15 17:25:36 +00:00
std::swap(m_aQuadKnifePoints[2], m_aQuadKnifePoints[3]);
2022-02-26 17:49:06 +00:00
CQuad *pResult = pLayer->NewQuad(64, 64, 64, 64);
pQuad = &pLayer->m_vQuads[QuadIndex];
2022-02-26 17:49:06 +00:00
for(int i = 0; i < 4; i++)
{
int t = IsInTriangle(m_aQuadKnifePoints[i], v[0], v[3], v[2]) ? 2 : 1;
2022-03-01 10:33:53 +00:00
2022-02-26 17:49:06 +00:00
vec2 A = vec2(fx2f(pQuad->m_aPoints[0].x), fx2f(pQuad->m_aPoints[0].y));
vec2 B = vec2(fx2f(pQuad->m_aPoints[3].x), fx2f(pQuad->m_aPoints[3].y));
vec2 C = vec2(fx2f(pQuad->m_aPoints[t].x), fx2f(pQuad->m_aPoints[t].y));
float TriArea = TriangleArea(A, B, C);
float WeightA = TriangleArea(m_aQuadKnifePoints[i], B, C) / TriArea;
float WeightB = TriangleArea(m_aQuadKnifePoints[i], C, A) / TriArea;
float WeightC = TriangleArea(m_aQuadKnifePoints[i], A, B) / TriArea;
pResult->m_aColors[i].r = (int)std::round(pQuad->m_aColors[0].r * WeightA + pQuad->m_aColors[3].r * WeightB + pQuad->m_aColors[t].r * WeightC);
pResult->m_aColors[i].g = (int)std::round(pQuad->m_aColors[0].g * WeightA + pQuad->m_aColors[3].g * WeightB + pQuad->m_aColors[t].g * WeightC);
pResult->m_aColors[i].b = (int)std::round(pQuad->m_aColors[0].b * WeightA + pQuad->m_aColors[3].b * WeightB + pQuad->m_aColors[t].b * WeightC);
pResult->m_aColors[i].a = (int)std::round(pQuad->m_aColors[0].a * WeightA + pQuad->m_aColors[3].a * WeightB + pQuad->m_aColors[t].a * WeightC);
2022-02-26 17:49:06 +00:00
pResult->m_aTexcoords[i].x = (int)std::round(pQuad->m_aTexcoords[0].x * WeightA + pQuad->m_aTexcoords[3].x * WeightB + pQuad->m_aTexcoords[t].x * WeightC);
pResult->m_aTexcoords[i].y = (int)std::round(pQuad->m_aTexcoords[0].y * WeightA + pQuad->m_aTexcoords[3].y * WeightB + pQuad->m_aTexcoords[t].y * WeightC);
2022-02-26 17:49:06 +00:00
pResult->m_aPoints[i].x = f2fx(m_aQuadKnifePoints[i].x);
pResult->m_aPoints[i].y = f2fx(m_aQuadKnifePoints[i].y);
}
pResult->m_aPoints[4].x = ((pResult->m_aPoints[0].x + pResult->m_aPoints[3].x) / 2 + (pResult->m_aPoints[1].x + pResult->m_aPoints[2].x) / 2) / 2;
pResult->m_aPoints[4].y = ((pResult->m_aPoints[0].y + pResult->m_aPoints[3].y) / 2 + (pResult->m_aPoints[1].y + pResult->m_aPoints[2].y) / 2) / 2;
m_QuadKnifeCount = 0;
}
// Render
2022-02-28 22:45:57 +00:00
Graphics()->TextureClear();
2022-03-01 10:33:53 +00:00
Graphics()->LinesBegin();
2022-02-28 22:45:57 +00:00
2022-02-26 17:49:06 +00:00
IGraphics::CLineItem aEdges[4] = {
IGraphics::CLineItem(v[0].x, v[0].y, v[1].x, v[1].y),
IGraphics::CLineItem(v[1].x, v[1].y, v[2].x, v[2].y),
IGraphics::CLineItem(v[2].x, v[2].y, v[3].x, v[3].y),
IGraphics::CLineItem(v[3].x, v[3].y, v[0].x, v[0].y)};
2022-02-28 22:45:57 +00:00
Graphics()->SetColor(1.f, 0.5f, 0.f, 1.f);
Graphics()->LinesDraw(aEdges, 4);
2022-02-26 17:49:06 +00:00
IGraphics::CLineItem aLines[4];
int LineCount = maximum(m_QuadKnifeCount - 1, 0);
for(int i = 0; i < LineCount; i++)
aLines[i] = IGraphics::CLineItem(m_aQuadKnifePoints[i].x, m_aQuadKnifePoints[i].y, m_aQuadKnifePoints[i + 1].x, m_aQuadKnifePoints[i + 1].y);
2022-02-28 22:45:57 +00:00
Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
Graphics()->LinesDraw(aLines, LineCount);
2022-02-26 17:49:06 +00:00
if(ValidPosition)
{
if(m_QuadKnifeCount > 0)
2022-02-26 23:44:34 +00:00
{
2022-02-28 22:45:57 +00:00
IGraphics::CLineItem LineCurrent(Point.x, Point.y, m_aQuadKnifePoints[m_QuadKnifeCount - 1].x, m_aQuadKnifePoints[m_QuadKnifeCount - 1].y);
Graphics()->LinesDraw(&LineCurrent, 1);
2022-02-26 23:44:34 +00:00
}
2022-02-26 17:49:06 +00:00
2022-02-28 22:45:57 +00:00
if(m_QuadKnifeCount == 3)
2022-02-26 23:44:34 +00:00
{
2022-02-28 22:45:57 +00:00
IGraphics::CLineItem LineClose(Point.x, Point.y, m_aQuadKnifePoints[0].x, m_aQuadKnifePoints[0].y);
Graphics()->LinesDraw(&LineClose, 1);
2022-02-26 23:44:34 +00:00
}
2022-02-26 17:49:06 +00:00
}
Graphics()->LinesEnd();
Graphics()->QuadsBegin();
2022-02-28 22:45:57 +00:00
IGraphics::CQuadItem aMarkers[4];
for(int i = 0; i < m_QuadKnifeCount; i++)
aMarkers[i] = IGraphics::CQuadItem(m_aQuadKnifePoints[i].x, m_aQuadKnifePoints[i].y, 5.f * m_MouseWScale, 5.f * m_MouseWScale);
2022-02-28 22:45:57 +00:00
2022-02-26 17:49:06 +00:00
Graphics()->SetColor(0.f, 0.f, 1.f, 1.f);
2022-02-28 22:45:57 +00:00
Graphics()->QuadsDraw(aMarkers, m_QuadKnifeCount);
if(ValidPosition)
{
IGraphics::CQuadItem MarkerCurrent(Point.x, Point.y, 5.f * m_MouseWScale, 5.f * m_MouseWScale);
2022-02-28 22:45:57 +00:00
Graphics()->QuadsDraw(&MarkerCurrent, 1);
}
2022-03-01 10:33:53 +00:00
2022-02-26 17:49:06 +00:00
Graphics()->QuadsEnd();
}
void CEditor::DoQuadEnvelopes(const std::vector<CQuad> &vQuads, IGraphics::CTextureHandle Texture)
2011-08-15 18:12:31 +00:00
{
size_t Num = vQuads.size();
CEnvelope **apEnvelope = new CEnvelope *[Num];
mem_zero(apEnvelope, sizeof(CEnvelope *) * Num); // NOLINT(bugprone-sizeof-expression)
for(size_t i = 0; i < Num; i++)
{
if((m_ShowEnvelopePreview == SHOWENV_SELECTED && vQuads[i].m_PosEnv == m_SelectedEnvelope) || m_ShowEnvelopePreview == SHOWENV_ALL)
if(vQuads[i].m_PosEnv >= 0 && vQuads[i].m_PosEnv < (int)m_Map.m_vpEnvelopes.size())
apEnvelope[i] = m_Map.m_vpEnvelopes[vQuads[i].m_PosEnv];
}
2011-08-15 18:12:31 +00:00
//Draw Lines
Graphics()->TextureClear();
2011-08-15 18:12:31 +00:00
Graphics()->LinesBegin();
Graphics()->SetColor(80.0f / 255, 150.0f / 255, 230.f / 255, 0.5f);
for(size_t j = 0; j < Num; j++)
{
if(!apEnvelope[j])
continue;
//QuadParams
const CPoint *pPoints = vQuads[j].m_aPoints;
for(size_t i = 0; i < apEnvelope[j]->m_vPoints.size() - 1; i++)
2011-08-15 18:12:31 +00:00
{
float OffsetX = fx2f(apEnvelope[j]->m_vPoints[i].m_aValues[0]);
float OffsetY = fx2f(apEnvelope[j]->m_vPoints[i].m_aValues[1]);
vec2 Pos0 = vec2(fx2f(pPoints[4].x) + OffsetX, fx2f(pPoints[4].y) + OffsetY);
2011-08-15 18:12:31 +00:00
OffsetX = fx2f(apEnvelope[j]->m_vPoints[i + 1].m_aValues[0]);
OffsetY = fx2f(apEnvelope[j]->m_vPoints[i + 1].m_aValues[1]);
vec2 Pos1 = vec2(fx2f(pPoints[4].x) + OffsetX, fx2f(pPoints[4].y) + OffsetY);
2011-08-15 18:12:31 +00:00
IGraphics::CLineItem Line = IGraphics::CLineItem(Pos0.x, Pos0.y, Pos1.x, Pos1.y);
Graphics()->LinesDraw(&Line, 1);
}
}
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
2011-08-15 18:12:31 +00:00
Graphics()->LinesEnd();
//Draw Quads
Graphics()->TextureSet(Texture);
Graphics()->QuadsBegin();
2015-07-09 00:08:14 +00:00
for(size_t j = 0; j < Num; j++)
{
if(!apEnvelope[j])
continue;
//QuadParams
const CPoint *pPoints = vQuads[j].m_aPoints;
for(size_t i = 0; i < apEnvelope[j]->m_vPoints.size(); i++)
2011-08-15 18:12:31 +00:00
{
//Calc Env Position
float OffsetX = fx2f(apEnvelope[j]->m_vPoints[i].m_aValues[0]);
float OffsetY = fx2f(apEnvelope[j]->m_vPoints[i].m_aValues[1]);
float Rot = fx2f(apEnvelope[j]->m_vPoints[i].m_aValues[2]) / 360.0f * pi * 2;
//Set Colours
float Alpha = (m_SelectedQuadEnvelope == vQuads[j].m_PosEnv && m_SelectedEnvelopePoint == (int)i) ? 0.65f : 0.35f;
IGraphics::CColorVertex aArray[4] = {
IGraphics::CColorVertex(0, vQuads[j].m_aColors[0].r, vQuads[j].m_aColors[0].g, vQuads[j].m_aColors[0].b, Alpha),
IGraphics::CColorVertex(1, vQuads[j].m_aColors[1].r, vQuads[j].m_aColors[1].g, vQuads[j].m_aColors[1].b, Alpha),
IGraphics::CColorVertex(2, vQuads[j].m_aColors[2].r, vQuads[j].m_aColors[2].g, vQuads[j].m_aColors[2].b, Alpha),
IGraphics::CColorVertex(3, vQuads[j].m_aColors[3].r, vQuads[j].m_aColors[3].g, vQuads[j].m_aColors[3].b, Alpha)};
Graphics()->SetColorVertex(aArray, 4);
//Rotation
if(Rot != 0)
{
static CPoint aRotated[4];
aRotated[0] = vQuads[j].m_aPoints[0];
aRotated[1] = vQuads[j].m_aPoints[1];
aRotated[2] = vQuads[j].m_aPoints[2];
aRotated[3] = vQuads[j].m_aPoints[3];
pPoints = aRotated;
Rotate(&vQuads[j].m_aPoints[4], &aRotated[0], Rot);
Rotate(&vQuads[j].m_aPoints[4], &aRotated[1], Rot);
Rotate(&vQuads[j].m_aPoints[4], &aRotated[2], Rot);
Rotate(&vQuads[j].m_aPoints[4], &aRotated[3], Rot);
}
//Set Texture Coords
Graphics()->QuadsSetSubsetFree(
fx2f(vQuads[j].m_aTexcoords[0].x), fx2f(vQuads[j].m_aTexcoords[0].y),
fx2f(vQuads[j].m_aTexcoords[1].x), fx2f(vQuads[j].m_aTexcoords[1].y),
fx2f(vQuads[j].m_aTexcoords[2].x), fx2f(vQuads[j].m_aTexcoords[2].y),
fx2f(vQuads[j].m_aTexcoords[3].x), fx2f(vQuads[j].m_aTexcoords[3].y));
//Set Quad Coords & Draw
IGraphics::CFreeformItem Freeform(
fx2f(pPoints[0].x) + OffsetX, fx2f(pPoints[0].y) + OffsetY,
fx2f(pPoints[1].x) + OffsetX, fx2f(pPoints[1].y) + OffsetY,
fx2f(pPoints[2].x) + OffsetX, fx2f(pPoints[2].y) + OffsetY,
fx2f(pPoints[3].x) + OffsetX, fx2f(pPoints[3].y) + OffsetY);
Graphics()->QuadsDrawFreeform(&Freeform, 1);
2011-08-15 18:12:31 +00:00
}
}
Graphics()->QuadsEnd();
Graphics()->TextureClear();
Graphics()->QuadsBegin();
2011-08-15 18:12:31 +00:00
// Draw QuadPoints
for(size_t j = 0; j < Num; j++)
{
if(!apEnvelope[j])
continue;
for(size_t i = 0; i < apEnvelope[j]->m_vPoints.size(); i++)
DoQuadEnvPoint(&vQuads[j], j, i);
2011-08-15 18:12:31 +00:00
}
Graphics()->QuadsEnd();
delete[] apEnvelope;
2011-08-14 14:31:48 +00:00
}
2011-08-15 18:12:31 +00:00
void CEditor::DoQuadEnvPoint(const CQuad *pQuad, int QIndex, int PIndex)
2011-08-14 14:31:48 +00:00
{
enum
{
OP_NONE = 0,
2011-08-15 18:12:31 +00:00
OP_MOVE,
OP_ROTATE,
2011-08-14 14:31:48 +00:00
};
// some basic values
static float s_LastWx = 0;
static float s_LastWy = 0;
2011-08-14 14:31:48 +00:00
static int s_Operation = OP_NONE;
float wx = UI()->MouseWorldX();
2011-08-15 18:12:31 +00:00
float wy = UI()->MouseWorldY();
CEnvelope *pEnvelope = m_Map.m_vpEnvelopes[pQuad->m_PosEnv];
void *pID = &pEnvelope->m_vPoints[PIndex];
static int s_CurQIndex = -1;
2011-08-14 14:31:48 +00:00
// get pivot
float CenterX = fx2f(pQuad->m_aPoints[4].x) + fx2f(pEnvelope->m_vPoints[PIndex].m_aValues[0]);
float CenterY = fx2f(pQuad->m_aPoints[4].y) + fx2f(pEnvelope->m_vPoints[PIndex].m_aValues[1]);
2011-08-14 14:31:48 +00:00
float dx = (CenterX - wx) / m_MouseWScale;
float dy = (CenterY - wy) / m_MouseWScale;
if(dx * dx + dy * dy < 50.0f && UI()->CheckActiveItem(nullptr))
{
2011-08-14 14:31:48 +00:00
UI()->SetHotItem(pID);
s_CurQIndex = QIndex;
}
2011-08-14 14:31:48 +00:00
const bool IgnoreGrid = Input()->AltIsPressed();
if(UI()->CheckActiveItem(pID) && s_CurQIndex == QIndex)
2011-08-14 14:31:48 +00:00
{
2011-08-15 18:12:31 +00:00
if(s_Operation == OP_MOVE)
2011-08-14 14:31:48 +00:00
{
if(m_GridActive && !IgnoreGrid)
2011-08-15 18:12:31 +00:00
{
float x = wx;
float y = wy;
SnapToGrid(x, y);
pEnvelope->m_vPoints[PIndex].m_aValues[0] = f2fx(x) - pQuad->m_aPoints[4].x;
pEnvelope->m_vPoints[PIndex].m_aValues[1] = f2fx(y) - pQuad->m_aPoints[4].y;
2011-08-15 18:12:31 +00:00
}
else
{
pEnvelope->m_vPoints[PIndex].m_aValues[0] += f2fx(wx - s_LastWx);
pEnvelope->m_vPoints[PIndex].m_aValues[1] += f2fx(wy - s_LastWy);
2011-08-15 18:12:31 +00:00
}
2011-08-14 14:31:48 +00:00
}
2011-08-15 18:12:31 +00:00
else if(s_Operation == OP_ROTATE)
pEnvelope->m_vPoints[PIndex].m_aValues[2] += 10 * m_MouseDeltaX;
2011-08-14 14:31:48 +00:00
s_LastWx = wx;
s_LastWy = wy;
2011-08-15 18:12:31 +00:00
if(!UI()->MouseButton(0))
{
m_LockMouse = false;
2011-08-15 18:12:31 +00:00
s_Operation = OP_NONE;
UI()->SetActiveItem(nullptr);
2011-08-14 14:31:48 +00:00
}
2011-08-15 18:12:31 +00:00
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
2011-08-14 14:31:48 +00:00
}
else if(UI()->HotItem() == pID && s_CurQIndex == QIndex)
2011-08-14 14:31:48 +00:00
{
ms_pUiGotContext = pID;
2011-08-15 18:12:31 +00:00
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
m_pTooltip = "Left mouse button to move. Hold ctrl to rotate. Hold alt to ignore grid.";
2011-08-14 14:31:48 +00:00
if(UI()->MouseButton(0))
{
if(Input()->ModifierIsPressed())
2011-08-15 18:12:31 +00:00
{
m_LockMouse = true;
2011-08-15 18:12:31 +00:00
s_Operation = OP_ROTATE;
2018-08-13 09:11:56 +00:00
SelectQuad(QIndex);
2011-08-15 18:12:31 +00:00
}
else
2018-08-13 09:11:56 +00:00
{
2011-08-15 18:12:31 +00:00
s_Operation = OP_MOVE;
2018-08-13 09:11:56 +00:00
SelectQuad(QIndex);
}
2011-08-15 18:12:31 +00:00
m_SelectedEnvelopePoint = PIndex;
m_SelectedQuadEnvelope = pQuad->m_PosEnv;
2011-08-14 14:31:48 +00:00
UI()->SetActiveItem(pID);
s_LastWx = wx;
s_LastWy = wy;
2011-08-15 18:12:31 +00:00
}
else
{
m_SelectedEnvelopePoint = -1;
m_SelectedQuadEnvelope = -1;
2011-08-14 14:31:48 +00:00
}
}
2011-08-15 18:12:31 +00:00
else
Graphics()->SetColor(0.0f, 1.0f, 0.0f, 1.0f);
2011-08-14 14:31:48 +00:00
IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f * m_MouseWScale, 5.0f * m_MouseWScale);
2011-08-14 14:31:48 +00:00
Graphics()->QuadsDraw(&QuadItem, 1);
}
2018-04-04 19:41:14 +00:00
void CEditor::DoMapEditor(CUIRect View)
2007-05-22 15:03:32 +00:00
{
2008-01-12 12:27:55 +00:00
// render all good stuff
if(!m_ShowPicker)
2008-01-12 12:27:55 +00:00
{
if(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && Input()->ShiftIsPressed() && !Input()->ModifierIsPressed() && Input()->KeyPress(KEY_G))
{
const bool AnyHidden =
!m_Map.m_pGameLayer->m_Visible ||
(m_Map.m_pFrontLayer && !m_Map.m_pFrontLayer->m_Visible) ||
(m_Map.m_pTeleLayer && !m_Map.m_pTeleLayer->m_Visible) ||
(m_Map.m_pSpeedupLayer && !m_Map.m_pSpeedupLayer->m_Visible) ||
(m_Map.m_pTuneLayer && !m_Map.m_pTuneLayer->m_Visible) ||
(m_Map.m_pSwitchLayer && !m_Map.m_pSwitchLayer->m_Visible);
m_Map.m_pGameLayer->m_Visible = AnyHidden;
if(m_Map.m_pFrontLayer)
m_Map.m_pFrontLayer->m_Visible = AnyHidden;
if(m_Map.m_pTeleLayer)
m_Map.m_pTeleLayer->m_Visible = AnyHidden;
if(m_Map.m_pSpeedupLayer)
m_Map.m_pSpeedupLayer->m_Visible = AnyHidden;
if(m_Map.m_pTuneLayer)
m_Map.m_pTuneLayer->m_Visible = AnyHidden;
if(m_Map.m_pSwitchLayer)
m_Map.m_pSwitchLayer->m_Visible = AnyHidden;
}
for(auto &pGroup : m_Map.m_vpGroups)
{
if(pGroup->m_Visible)
pGroup->Render();
2008-01-12 12:27:55 +00:00
}
// render the game, tele, speedup, front, tune and switch above everything else
if(m_Map.m_pGameGroup->m_Visible)
{
m_Map.m_pGameGroup->MapScreen();
for(auto &pLayer : m_Map.m_pGameGroup->m_vpLayers)
{
if(
pLayer->m_Visible &&
(pLayer == m_Map.m_pGameLayer ||
pLayer == m_Map.m_pFrontLayer ||
pLayer == m_Map.m_pTeleLayer ||
pLayer == m_Map.m_pSpeedupLayer ||
pLayer == m_Map.m_pSwitchLayer ||
pLayer == m_Map.m_pTuneLayer))
pLayer->Render();
}
}
CLayerTiles *pT = static_cast<CLayerTiles *>(GetSelectedLayerType(0, LAYERTYPE_TILES));
if(m_ShowTileInfo && pT && pT->m_Visible && m_Zoom <= 300.0f)
{
GetSelectedGroup()->MapScreen();
pT->ShowInfo();
}
2008-01-12 12:27:55 +00:00
}
else
{
// fix aspect ratio of the image in the picker
2019-04-26 19:36:49 +00:00
float Max = minimum(View.w, View.h);
View.w = View.h = Max;
}
2008-01-12 12:27:55 +00:00
static void *s_pEditorID = (void *)&s_pEditorID;
const bool Inside = !m_GuiActive || UI()->MouseInside(&View);
2008-01-12 12:27:55 +00:00
2008-03-29 11:44:03 +00:00
// fetch mouse position
2009-10-27 14:38:53 +00:00
float wx = UI()->MouseWorldX();
float wy = UI()->MouseWorldY();
2014-08-22 13:40:23 +00:00
float mx = UI()->MouseX();
float my = UI()->MouseY();
2010-05-29 07:25:38 +00:00
static float s_StartWx = 0;
static float s_StartWy = 0;
2008-01-12 12:27:55 +00:00
enum
2007-05-22 15:03:32 +00:00
{
OP_NONE = 0,
2008-01-12 12:27:55 +00:00
OP_BRUSH_GRAB,
OP_BRUSH_DRAW,
2010-05-29 07:25:38 +00:00
OP_BRUSH_PAINT,
2008-01-12 12:27:55 +00:00
OP_PAN_WORLD,
OP_PAN_EDITOR,
};
// remap the screen so it can display the whole tileset
if(m_ShowPicker)
2010-05-29 07:25:38 +00:00
{
CUIRect Screen = *UI()->Screen();
2022-03-21 06:10:44 +00:00
float Size = 32.0f * 16.0f;
float w = Size * (Screen.w / View.w);
float h = Size * (Screen.h / View.h);
float x = -(View.x / Screen.w) * w;
float y = -(View.y / Screen.h) * h;
wx = x + w * mx / Screen.w;
wy = y + h * my / Screen.h;
CLayerTiles *pTileLayer = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES);
if(pTileLayer)
2008-01-12 12:27:55 +00:00
{
Graphics()->MapScreen(x, y, x + w, y + h);
m_TilesetPicker.m_Image = pTileLayer->m_Image;
m_TilesetPicker.m_Texture = pTileLayer->m_Texture;
if(m_BrushColorEnabled)
{
m_TilesetPicker.m_Color = pTileLayer->m_Color;
m_TilesetPicker.m_Color.a = 255;
}
else
{
m_TilesetPicker.m_Color = {255, 255, 255, 255};
}
2018-08-19 17:05:42 +00:00
m_TilesetPicker.m_Game = pTileLayer->m_Game;
m_TilesetPicker.m_Tele = pTileLayer->m_Tele;
m_TilesetPicker.m_Speedup = pTileLayer->m_Speedup;
m_TilesetPicker.m_Front = pTileLayer->m_Front;
m_TilesetPicker.m_Switch = pTileLayer->m_Switch;
m_TilesetPicker.m_Tune = pTileLayer->m_Tune;
2018-08-19 17:05:42 +00:00
m_TilesetPicker.Render(true);
if(m_ShowTileInfo)
m_TilesetPicker.ShowInfo();
2008-01-12 12:27:55 +00:00
}
else
{
CLayerQuads *pQuadLayer = (CLayerQuads *)GetSelectedLayerType(0, LAYERTYPE_QUADS);
if(pQuadLayer)
{
m_QuadsetPicker.m_Image = pQuadLayer->m_Image;
m_QuadsetPicker.m_vQuads[0].m_aPoints[0].x = f2fx(View.x);
m_QuadsetPicker.m_vQuads[0].m_aPoints[0].y = f2fx(View.y);
m_QuadsetPicker.m_vQuads[0].m_aPoints[1].x = f2fx((View.x + View.w));
m_QuadsetPicker.m_vQuads[0].m_aPoints[1].y = f2fx(View.y);
m_QuadsetPicker.m_vQuads[0].m_aPoints[2].x = f2fx(View.x);
m_QuadsetPicker.m_vQuads[0].m_aPoints[2].y = f2fx((View.y + View.h));
m_QuadsetPicker.m_vQuads[0].m_aPoints[3].x = f2fx((View.x + View.w));
m_QuadsetPicker.m_vQuads[0].m_aPoints[3].y = f2fx((View.y + View.h));
m_QuadsetPicker.m_vQuads[0].m_aPoints[4].x = f2fx((View.x + View.w / 2));
m_QuadsetPicker.m_vQuads[0].m_aPoints[4].y = f2fx((View.y + View.h / 2));
m_QuadsetPicker.Render();
}
}
2007-05-22 15:03:32 +00:00
}
2010-05-29 07:25:38 +00:00
static int s_Operation = OP_NONE;
2008-01-12 12:27:55 +00:00
// draw layer borders
CLayer *apEditLayers[128];
size_t NumEditLayers = 0;
2010-05-29 07:25:38 +00:00
if(m_ShowPicker && GetSelectedLayer(0) && GetSelectedLayer(0)->m_Type == LAYERTYPE_TILES)
2007-05-22 15:03:32 +00:00
{
apEditLayers[0] = &m_TilesetPicker;
2010-05-29 07:25:38 +00:00
NumEditLayers++;
2007-05-22 15:03:32 +00:00
}
else if(m_ShowPicker)
{
apEditLayers[0] = &m_QuadsetPicker;
NumEditLayers++;
}
2007-05-22 15:03:32 +00:00
else
2008-01-12 12:27:55 +00:00
{
2022-10-25 16:51:56 +00:00
// pick a type of layers to edit, preferring Tiles layers.
int EditingType = -1;
for(size_t i = 0; i < m_vSelectedLayers.size(); i++)
{
CLayer *pLayer = GetSelectedLayer(i);
if(pLayer && (EditingType == -1 || pLayer->m_Type == LAYERTYPE_TILES))
{
EditingType = pLayer->m_Type;
if(EditingType == LAYERTYPE_TILES)
break;
}
}
for(size_t i = 0; i < m_vSelectedLayers.size() && NumEditLayers < 128; i++)
{
apEditLayers[NumEditLayers] = GetSelectedLayerType(i, EditingType);
if(apEditLayers[NumEditLayers])
{
NumEditLayers++;
}
}
2008-01-12 12:27:55 +00:00
CLayerGroup *pGroup = GetSelectedGroup();
if(pGroup)
2008-01-12 12:27:55 +00:00
{
pGroup->MapScreen();
2010-05-29 07:25:38 +00:00
RenderGrid(pGroup);
2011-07-10 20:16:16 +00:00
for(size_t i = 0; i < NumEditLayers; i++)
2008-01-12 12:27:55 +00:00
{
if(apEditLayers[i]->m_Type != LAYERTYPE_TILES)
2008-01-12 12:27:55 +00:00
continue;
2010-05-29 07:25:38 +00:00
2008-01-12 12:27:55 +00:00
float w, h;
apEditLayers[i]->GetSize(&w, &h);
2008-01-12 12:27:55 +00:00
2010-05-29 07:25:38 +00:00
IGraphics::CLineItem Array[4] = {
IGraphics::CLineItem(0, 0, w, 0),
IGraphics::CLineItem(w, 0, w, h),
IGraphics::CLineItem(w, h, 0, h),
IGraphics::CLineItem(0, h, 0, 0)};
Graphics()->TextureClear();
2009-10-27 14:38:53 +00:00
Graphics()->LinesBegin();
2010-05-29 07:25:38 +00:00
Graphics()->LinesDraw(Array, 4);
2009-10-27 14:38:53 +00:00
Graphics()->LinesEnd();
2008-01-12 12:27:55 +00:00
}
}
}
2010-05-29 07:25:38 +00:00
if(Inside)
2008-01-12 12:27:55 +00:00
{
UI()->SetHotItem(s_pEditorID);
2010-05-29 07:25:38 +00:00
2008-01-12 12:27:55 +00:00
// do global operations like pan and zoom
if(UI()->CheckActiveItem(nullptr) && (UI()->MouseButton(0) || UI()->MouseButton(2)))
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
s_StartWx = wx;
s_StartWy = wy;
if(Input()->ModifierIsPressed() || UI()->MouseButton(2))
2008-01-12 12:27:55 +00:00
{
if(Input()->ShiftIsPressed())
2010-05-29 07:25:38 +00:00
s_Operation = OP_PAN_EDITOR;
2008-01-12 12:27:55 +00:00
else
2010-05-29 07:25:38 +00:00
s_Operation = OP_PAN_WORLD;
UI()->SetActiveItem(s_pEditorID);
2008-01-12 12:27:55 +00:00
}
else
s_Operation = OP_NONE;
2008-01-12 12:27:55 +00:00
}
// brush editing
if(UI()->HotItem() == s_pEditorID)
2008-01-12 12:27:55 +00:00
{
int Layer = NUM_LAYERS;
if(m_ShowPicker)
{
CLayer *pLayer = GetSelectedLayer(0);
if(pLayer == m_Map.m_pGameLayer)
Layer = LAYER_GAME;
else if(pLayer == m_Map.m_pFrontLayer)
Layer = LAYER_FRONT;
else if(pLayer == m_Map.m_pSwitchLayer)
Layer = LAYER_SWITCH;
else if(pLayer == m_Map.m_pTeleLayer)
Layer = LAYER_TELE;
else if(pLayer == m_Map.m_pSpeedupLayer)
Layer = LAYER_SPEEDUP;
else if(pLayer == m_Map.m_pTuneLayer)
Layer = LAYER_TUNE;
}
if(m_ShowPicker && Layer != NUM_LAYERS)
{
if(m_SelectEntitiesImage == "DDNet")
m_pTooltip = Explain(EXPLANATION_DDNET, (int)wx / 32 + (int)wy / 32 * 16, Layer);
else if(m_SelectEntitiesImage == "FNG")
m_pTooltip = Explain(EXPLANATION_FNG, (int)wx / 32 + (int)wy / 32 * 16, Layer);
2021-08-28 17:51:38 +00:00
else if(m_SelectEntitiesImage == "Vanilla")
m_pTooltip = Explain(EXPLANATION_VANILLA, (int)wx / 32 + (int)wy / 32 * 16, Layer);
}
else if(m_Brush.IsEmpty())
m_pTooltip = "Use left mouse button to drag and create a brush. Hold shift to select multiple quads. Use ctrl+right mouse to select layer.";
2008-01-12 12:27:55 +00:00
else
2011-03-20 16:04:03 +00:00
m_pTooltip = "Use left mouse button to paint with the brush. Right button clears the brush.";
2008-01-12 12:27:55 +00:00
if(UI()->CheckActiveItem(s_pEditorID))
2008-01-12 12:27:55 +00:00
{
2009-10-27 14:38:53 +00:00
CUIRect r;
2010-05-29 07:25:38 +00:00
r.x = s_StartWx;
r.y = s_StartWy;
r.w = wx - s_StartWx;
r.h = wy - s_StartWy;
2008-01-12 12:27:55 +00:00
if(r.w < 0)
{
r.x += r.w;
r.w = -r.w;
}
if(r.h < 0)
{
r.y += r.h;
r.h = -r.h;
}
2010-05-29 07:25:38 +00:00
if(s_Operation == OP_BRUSH_DRAW)
{
if(!m_Brush.IsEmpty())
2008-01-12 12:27:55 +00:00
{
// draw with brush
for(size_t k = 0; k < NumEditLayers; k++)
2008-01-12 12:27:55 +00:00
{
size_t BrushIndex = k % m_Brush.m_vpLayers.size();
if(apEditLayers[k]->m_Type == m_Brush.m_vpLayers[BrushIndex]->m_Type)
{
if(apEditLayers[k]->m_Type == LAYERTYPE_TILES)
{
CLayerTiles *pLayer = (CLayerTiles *)apEditLayers[k];
CLayerTiles *pBrushLayer = (CLayerTiles *)m_Brush.m_vpLayers[BrushIndex];
if(pLayer->m_Tele <= pBrushLayer->m_Tele && pLayer->m_Speedup <= pBrushLayer->m_Speedup && pLayer->m_Front <= pBrushLayer->m_Front && pLayer->m_Game <= pBrushLayer->m_Game && pLayer->m_Switch <= pBrushLayer->m_Switch && pLayer->m_Tune <= pBrushLayer->m_Tune)
pLayer->BrushDraw(pBrushLayer, wx, wy);
}
else
{
apEditLayers[k]->BrushDraw(m_Brush.m_vpLayers[BrushIndex], wx, wy);
}
}
2008-01-12 12:27:55 +00:00
}
}
}
2010-05-29 07:25:38 +00:00
else if(s_Operation == OP_BRUSH_GRAB)
2008-01-12 12:27:55 +00:00
{
2009-10-27 14:38:53 +00:00
if(!UI()->MouseButton(0))
2008-01-12 12:27:55 +00:00
{
if(Input()->ShiftIsPressed())
2018-08-13 09:11:56 +00:00
{
CLayerQuads *pQuadLayer = (CLayerQuads *)GetSelectedLayerType(0, LAYERTYPE_QUADS);
if(pQuadLayer)
2018-08-13 09:11:56 +00:00
{
for(size_t i = 0; i < pQuadLayer->m_vQuads.size(); i++)
2018-08-13 09:11:56 +00:00
{
CQuad *pQuad = &pQuadLayer->m_vQuads[i];
float px = fx2f(pQuad->m_aPoints[4].x);
float py = fx2f(pQuad->m_aPoints[4].y);
2010-05-29 07:25:38 +00:00
if(px > r.x && px < r.x + r.w && py > r.y && py < r.y + r.h)
2018-08-13 09:11:56 +00:00
if(!IsQuadSelected(i))
m_vSelectedQuads.push_back(i);
2018-08-13 09:11:56 +00:00
}
}
}
else
{
// grab brush
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "grabbing %f %f %f %f", r.x, r.y, r.w, r.h);
2018-08-13 09:11:56 +00:00
Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "editor", aBuf);
// TODO: do all layers
int Grabs = 0;
for(size_t k = 0; k < NumEditLayers; k++)
Grabs += apEditLayers[k]->BrushGrab(&m_Brush, r);
2018-08-13 09:11:56 +00:00
if(Grabs == 0)
m_Brush.Clear();
for(auto &pLayer : m_Brush.m_vpLayers)
pLayer->m_BrushRefCount = 1;
2018-10-02 01:52:01 +00:00
m_vSelectedQuads.clear();
2018-08-13 09:11:56 +00:00
m_SelectedPoints = 0;
}
2008-01-12 12:27:55 +00:00
}
else
{
for(size_t k = 0; k < NumEditLayers; k++)
apEditLayers[k]->BrushSelecting(r);
2021-09-22 19:48:55 +00:00
UI()->MapScreen();
2008-01-12 12:27:55 +00:00
}
}
2010-05-29 07:25:38 +00:00
else if(s_Operation == OP_BRUSH_PAINT)
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
if(!UI()->MouseButton(0))
{
for(size_t k = 0; k < NumEditLayers; k++)
{
size_t BrushIndex = k;
if(m_Brush.m_vpLayers.size() != NumEditLayers)
BrushIndex = 0;
CLayer *pBrush = m_Brush.IsEmpty() ? nullptr : m_Brush.m_vpLayers[BrushIndex];
apEditLayers[k]->FillSelection(m_Brush.IsEmpty(), pBrush, r);
}
2010-05-29 07:25:38 +00:00
}
2008-01-12 12:27:55 +00:00
else
{
for(size_t k = 0; k < NumEditLayers; k++)
apEditLayers[k]->BrushSelecting(r);
2021-09-22 19:48:55 +00:00
UI()->MapScreen();
2010-05-29 07:25:38 +00:00
}
}
}
else
{
2015-07-22 22:43:09 +00:00
if(UI()->MouseButton(1))
2018-10-02 01:52:01 +00:00
{
for(auto &pLayer : m_Brush.m_vpLayers)
2018-10-02 01:52:01 +00:00
{
if(pLayer->m_BrushRefCount == 1)
delete pLayer;
2018-10-02 01:52:01 +00:00
}
2010-05-29 07:25:38 +00:00
m_Brush.Clear();
2018-10-02 01:52:01 +00:00
}
2010-05-29 07:25:38 +00:00
2022-02-26 17:49:06 +00:00
if(UI()->MouseButton(0) && s_Operation == OP_NONE && !m_QuadKnifeActive)
2010-05-29 07:25:38 +00:00
{
UI()->SetActiveItem(s_pEditorID);
2010-05-29 07:25:38 +00:00
if(m_Brush.IsEmpty())
s_Operation = OP_BRUSH_GRAB;
else
{
s_Operation = OP_BRUSH_DRAW;
for(size_t k = 0; k < NumEditLayers; k++)
2008-01-12 12:27:55 +00:00
{
size_t BrushIndex = k;
if(m_Brush.m_vpLayers.size() != NumEditLayers)
BrushIndex = 0;
if(apEditLayers[k]->m_Type == m_Brush.m_vpLayers[BrushIndex]->m_Type)
apEditLayers[k]->BrushPlace(m_Brush.m_vpLayers[BrushIndex], wx, wy);
2008-01-12 12:27:55 +00:00
}
}
2011-08-11 08:59:14 +00:00
CLayerTiles *pLayer = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES);
if(Input()->ShiftIsPressed() && pLayer)
2011-08-11 08:59:14 +00:00
s_Operation = OP_BRUSH_PAINT;
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
if(!m_Brush.IsEmpty())
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
m_Brush.m_OffsetX = -(int)wx;
m_Brush.m_OffsetY = -(int)wy;
for(const auto &pLayer : m_Brush.m_vpLayers)
2008-01-12 12:27:55 +00:00
{
if(pLayer->m_Type == LAYERTYPE_TILES)
2008-01-12 12:27:55 +00:00
{
m_Brush.m_OffsetX = -(int)(wx / 32.0f) * 32;
m_Brush.m_OffsetY = -(int)(wy / 32.0f) * 32;
2008-01-12 12:27:55 +00:00
break;
}
}
2010-05-29 07:25:38 +00:00
CLayerGroup *pGroup = GetSelectedGroup();
if(!m_ShowPicker && pGroup)
2010-06-25 16:56:58 +00:00
{
m_Brush.m_OffsetX += pGroup->m_OffsetX;
m_Brush.m_OffsetY += pGroup->m_OffsetY;
m_Brush.m_ParallaxX = pGroup->m_ParallaxX;
m_Brush.m_ParallaxY = pGroup->m_ParallaxY;
m_Brush.m_ParallaxZoom = pGroup->m_ParallaxZoom;
2010-06-25 16:56:58 +00:00
m_Brush.Render();
float w, h;
m_Brush.GetSize(&w, &h);
IGraphics::CLineItem Array[4] = {
IGraphics::CLineItem(0, 0, w, 0),
IGraphics::CLineItem(w, 0, w, h),
IGraphics::CLineItem(w, h, 0, h),
IGraphics::CLineItem(0, h, 0, 0)};
Graphics()->TextureClear();
2010-06-25 16:56:58 +00:00
Graphics()->LinesBegin();
Graphics()->LinesDraw(Array, 4);
Graphics()->LinesEnd();
}
2008-01-12 12:27:55 +00:00
}
}
}
2010-05-29 07:25:38 +00:00
// quad & sound editing
2008-01-12 12:27:55 +00:00
{
if(!m_ShowPicker && m_Brush.IsEmpty())
2008-01-12 12:27:55 +00:00
{
2008-01-13 22:03:32 +00:00
// fetch layers
CLayerGroup *pGroup = GetSelectedGroup();
if(pGroup)
pGroup->MapScreen();
2010-05-29 07:25:38 +00:00
for(size_t k = 0; k < NumEditLayers; k++)
2008-01-12 12:27:55 +00:00
{
if(apEditLayers[k]->m_Type == LAYERTYPE_QUADS)
2008-01-12 12:27:55 +00:00
{
CLayerQuads *pLayer = (CLayerQuads *)apEditLayers[k];
2010-05-29 07:25:38 +00:00
if(m_ShowEnvelopePreview == SHOWENV_NONE)
m_ShowEnvelopePreview = SHOWENV_ALL;
2010-05-29 07:25:38 +00:00
2022-02-26 17:49:06 +00:00
if(m_QuadKnifeActive)
DoQuadKnife(m_vSelectedQuads[m_SelectedQuadIndex]);
2022-02-26 17:49:06 +00:00
else
2008-01-13 22:03:32 +00:00
{
2022-02-26 17:49:06 +00:00
Graphics()->TextureClear();
Graphics()->QuadsBegin();
for(size_t i = 0; i < pLayer->m_vQuads.size(); i++)
2022-02-26 17:49:06 +00:00
{
for(int v = 0; v < 4; v++)
DoQuadPoint(&pLayer->m_vQuads[i], i, v);
2010-05-29 07:25:38 +00:00
DoQuad(&pLayer->m_vQuads[i], i);
2022-02-26 17:49:06 +00:00
}
Graphics()->QuadsEnd();
2008-01-13 22:03:32 +00:00
}
2008-01-12 12:27:55 +00:00
}
if(apEditLayers[k]->m_Type == LAYERTYPE_SOUNDS)
{
CLayerSounds *pLayer = (CLayerSounds *)apEditLayers[k];
Graphics()->TextureClear();
Graphics()->QuadsBegin();
for(size_t i = 0; i < pLayer->m_vSources.size(); i++)
{
DoSoundSource(&pLayer->m_vSources[i], i);
}
Graphics()->QuadsEnd();
}
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
2021-09-22 19:48:55 +00:00
UI()->MapScreen();
2010-05-29 07:25:38 +00:00
}
}
2010-05-29 07:25:38 +00:00
// menu proof selection
if(m_MenuProofBorders && !m_ShowPicker)
{
ResetMenuBackgroundPositions();
for(int i = 0; i < (int)m_vMenuBackgroundPositions.size(); i++)
{
vec2 Pos = m_vMenuBackgroundPositions[i];
2023-03-18 06:53:18 +00:00
if(Pos == vec2(0, 0))
continue;
Pos += vec2(m_WorldOffsetX, m_WorldOffsetY) - m_vMenuBackgroundPositions[m_CurrentMenuProofIndex];
Pos.y -= 3.0f;
vec2 MousePos(m_MouseWorldNoParaX, m_MouseWorldNoParaY);
if(distance(Pos, MousePos) <= 20.0f)
{
UI()->SetHotItem(&m_vMenuBackgroundPositions[i]);
2023-03-18 06:53:18 +00:00
if(i != m_CurrentMenuProofIndex && UI()->CheckActiveItem(&m_vMenuBackgroundPositions[i]))
{
if(!UI()->MouseButton(0))
{
m_CurrentMenuProofIndex = i;
m_WorldOffsetX = m_vMenuBackgroundPositions[i].x;
m_WorldOffsetY = m_vMenuBackgroundPositions[i].y;
UI()->SetActiveItem(nullptr);
}
}
else if(UI()->HotItem() == &m_vMenuBackgroundPositions[i])
{
2023-03-18 06:53:18 +00:00
char aNumBuf[8];
if(i < (TILE_TIME_CHECKPOINT_LAST - TILE_TIME_CHECKPOINT_FIRST))
str_format(aNumBuf, sizeof(aNumBuf), "#%d", i + 1);
else
aNumBuf[0] = '\0';
if(i == m_CurrentMenuProofIndex)
str_format(m_aMenuBackgroundTooltip, sizeof(m_aMenuBackgroundTooltip), "Current proof position is %s %s", m_vpMenuBackgroundPositionNames[i], aNumBuf);
else
str_format(m_aMenuBackgroundTooltip, sizeof(m_aMenuBackgroundTooltip), "Switch proof position to %s %s", m_vpMenuBackgroundPositionNames[i], aNumBuf);
2023-03-18 06:53:18 +00:00
m_pTooltip = m_aMenuBackgroundTooltip;
if(UI()->MouseButton(0))
UI()->SetActiveItem(&m_vMenuBackgroundPositions[i]);
}
}
}
}
// do panning
if(UI()->CheckActiveItem(s_pEditorID))
{
if(s_Operation == OP_PAN_WORLD)
2008-01-12 12:27:55 +00:00
{
m_WorldOffsetX -= m_MouseDeltaX * m_MouseWScale;
m_WorldOffsetY -= m_MouseDeltaY * m_MouseWScale;
}
else if(s_Operation == OP_PAN_EDITOR)
{
m_EditorOffsetX -= m_MouseDeltaX * m_MouseWScale;
m_EditorOffsetY -= m_MouseDeltaY * m_MouseWScale;
}
2008-01-12 12:27:55 +00:00
// release mouse
if(!UI()->MouseButton(0))
{
s_Operation = OP_NONE;
UI()->SetActiveItem(nullptr);
2008-01-13 22:03:32 +00:00
}
2008-01-12 12:27:55 +00:00
}
if(!Input()->ShiftIsPressed() && !Input()->ModifierIsPressed() && m_Dialog == DIALOG_NONE && m_EditBoxActive == 0)
2021-11-06 15:44:40 +00:00
{
float PanSpeed = 64.0f;
if(Input()->KeyPress(KEY_A))
m_WorldOffsetX -= PanSpeed * m_MouseWScale;
2021-11-06 15:44:40 +00:00
else if(Input()->KeyPress(KEY_D))
m_WorldOffsetX += PanSpeed * m_MouseWScale;
2021-11-06 15:44:40 +00:00
if(Input()->KeyPress(KEY_W))
m_WorldOffsetY -= PanSpeed * m_MouseWScale;
2021-11-06 15:44:40 +00:00
else if(Input()->KeyPress(KEY_S))
m_WorldOffsetY += PanSpeed * m_MouseWScale;
2021-11-06 15:44:40 +00:00
}
2008-01-12 12:27:55 +00:00
}
else if(UI()->CheckActiveItem(s_pEditorID))
{
// release mouse
if(!UI()->MouseButton(0))
{
s_Operation = OP_NONE;
UI()->SetActiveItem(nullptr);
}
}
2010-05-29 07:25:38 +00:00
if(!m_ShowPicker && GetSelectedGroup() && GetSelectedGroup()->m_UseClipping)
2008-03-29 11:44:03 +00:00
{
CLayerGroup *pGameGroup = m_Map.m_pGameGroup;
pGameGroup->MapScreen();
2011-08-11 08:59:14 +00:00
Graphics()->TextureClear();
2009-10-27 14:38:53 +00:00
Graphics()->LinesBegin();
CUIRect r;
r.x = GetSelectedGroup()->m_ClipX;
r.y = GetSelectedGroup()->m_ClipY;
r.w = GetSelectedGroup()->m_ClipW;
r.h = GetSelectedGroup()->m_ClipH;
IGraphics::CLineItem Array[4] = {
IGraphics::CLineItem(r.x, r.y, r.x + r.w, r.y),
IGraphics::CLineItem(r.x + r.w, r.y, r.x + r.w, r.y + r.h),
IGraphics::CLineItem(r.x + r.w, r.y + r.h, r.x, r.y + r.h),
IGraphics::CLineItem(r.x, r.y + r.h, r.x, r.y)};
Graphics()->SetColor(1, 0, 0, 1);
Graphics()->LinesDraw(Array, 4);
2010-05-29 07:25:38 +00:00
2009-10-27 14:38:53 +00:00
Graphics()->LinesEnd();
2008-03-29 11:44:03 +00:00
}
2008-01-12 12:27:55 +00:00
2010-05-29 07:25:38 +00:00
// render screen sizes
if((m_ProofBorders || m_MenuProofBorders) && !m_ShowPicker)
2008-01-12 12:27:55 +00:00
{
CLayerGroup *pGameGroup = m_Map.m_pGameGroup;
pGameGroup->MapScreen();
2010-05-29 07:25:38 +00:00
Graphics()->TextureClear();
2009-10-27 14:38:53 +00:00
Graphics()->LinesBegin();
2010-05-29 07:25:38 +00:00
2016-07-18 06:55:35 +00:00
// possible screen sizes (white border)
2010-05-29 07:25:38 +00:00
float aLastPoints[4];
float Start = 1.0f; //9.0f/16.0f;
float End = 16.0f / 9.0f;
2010-05-29 07:25:38 +00:00
const int NumSteps = 20;
for(int i = 0; i <= NumSteps; i++)
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
float aPoints[4];
float Aspect = Start + (End - Start) * (i / (float)NumSteps);
2010-05-29 07:25:38 +00:00
float Zoom = m_MenuProofBorders ? 0.7f : 1.0f;
RenderTools()->MapScreenToWorld(
2010-05-29 07:25:38 +00:00
m_WorldOffsetX, m_WorldOffsetY,
100.0f, 100.0f, 100.0f, 0.0f, 0.0f, Aspect, Zoom, aPoints);
2010-05-29 07:25:38 +00:00
2008-01-12 12:27:55 +00:00
if(i == 0)
{
2010-05-29 07:25:38 +00:00
IGraphics::CLineItem Array[2] = {
IGraphics::CLineItem(aPoints[0], aPoints[1], aPoints[2], aPoints[1]),
IGraphics::CLineItem(aPoints[0], aPoints[3], aPoints[2], aPoints[3])};
Graphics()->LinesDraw(Array, 2);
2008-01-12 12:27:55 +00:00
}
if(i != 0)
{
2010-05-29 07:25:38 +00:00
IGraphics::CLineItem Array[4] = {
IGraphics::CLineItem(aPoints[0], aPoints[1], aLastPoints[0], aLastPoints[1]),
IGraphics::CLineItem(aPoints[2], aPoints[1], aLastPoints[2], aLastPoints[1]),
IGraphics::CLineItem(aPoints[0], aPoints[3], aLastPoints[0], aLastPoints[3]),
IGraphics::CLineItem(aPoints[2], aPoints[3], aLastPoints[2], aLastPoints[3])};
Graphics()->LinesDraw(Array, 4);
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
if(i == NumSteps)
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
IGraphics::CLineItem Array[2] = {
IGraphics::CLineItem(aPoints[0], aPoints[1], aPoints[0], aPoints[3]),
IGraphics::CLineItem(aPoints[2], aPoints[1], aPoints[2], aPoints[3])};
Graphics()->LinesDraw(Array, 2);
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
mem_copy(aLastPoints, aPoints, sizeof(aPoints));
2008-01-12 12:27:55 +00:00
}
2016-07-18 06:55:35 +00:00
// two screen sizes (green and red border)
2008-01-12 12:27:55 +00:00
{
Graphics()->SetColor(1, 0, 0, 1);
2008-01-17 23:09:49 +00:00
for(int i = 0; i < 2; i++)
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
float aPoints[4];
Declare variables as `const` when possible According to cppcheck's `constVariable` error: ``` src\engine\client\backend\opengl\opengl_sl.cpp:74:43: style: Variable 'Define' can be declared as reference to const [constVariable] for(CGLSLCompiler::SGLSLCompilerDefine &Define : pCompiler->m_vDefines) ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:2149:12: style: Variable 'GraphicThreadCommandBuffer' can be declared as reference to const [constVariable] auto &GraphicThreadCommandBuffer = m_vvThreadDrawCommandBuffers[i + 1][m_CurImageIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:3192:9: style: Variable 'BufferObject' can be declared as reference to const [constVariable] auto &BufferObject = m_vBufferObjects[BufferObjectIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:3200:10: style: Variable 'DescrSet' can be declared as reference to const [constVariable] auto &DescrSet = m_vTextures[State.m_Texture].m_VKStandard3DTexturedDescrSet; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:3810:13: style: Variable 'Mode' can be declared as reference to const [constVariable] for(auto &Mode : vPresentModeList) ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:3818:13: style: Variable 'Mode' can be declared as reference to const [constVariable] for(auto &Mode : vPresentModeList) ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6511:10: style: Variable 'DescrSet' can be declared as reference to const [constVariable] auto &DescrSet = m_vTextures[pCommand->m_State.m_Texture].m_aVKStandardTexturedDescrSets[AddressModeIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6555:10: style: Variable 'DescrSet' can be declared as reference to const [constVariable] auto &DescrSet = m_vTextures[pCommand->m_State.m_Texture].m_VKStandard3DTexturedDescrSet; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6660:9: style: Variable 'MemBlock' can be declared as reference to const [constVariable] auto &MemBlock = m_vBufferObjects[BufferIndex].m_BufferObject.m_Mem; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6799:9: style: Variable 'BufferObject' can be declared as reference to const [constVariable] auto &BufferObject = m_vBufferObjects[BufferObjectIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6808:10: style: Variable 'DescrSet' can be declared as reference to const [constVariable] auto &DescrSet = m_vTextures[pCommand->m_State.m_Texture].m_aVKStandardTexturedDescrSets[AddressModeIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6902:9: style: Variable 'BufferObject' can be declared as reference to const [constVariable] auto &BufferObject = m_vBufferObjects[BufferObjectIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6907:9: style: Variable 'TextTextureDescr' can be declared as reference to const [constVariable] auto &TextTextureDescr = m_vTextures[pCommand->m_TextTextureIndex].m_VKTextDescrSet; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6961:9: style: Variable 'BufferObject' can be declared as reference to const [constVariable] auto &BufferObject = m_vBufferObjects[BufferObjectIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6970:10: style: Variable 'DescrSet' can be declared as reference to const [constVariable] auto &DescrSet = m_vTextures[State.m_Texture].m_aVKStandardTexturedDescrSets[AddressModeIndex]; ^ src\game\client\components\hud.cpp:178:8: style: Variable 'aFlagCarrier' can be declared as const array [constVariable] int aFlagCarrier[2] = { ^ src\game\client\components\hud.cpp:519:16: style: Variable 's_aTextWidth' can be declared as const array [constVariable] static float s_aTextWidth[5] = {s_TextWidth0, s_TextWidth00, s_TextWidth000, s_TextWidth0000, s_TextWidth00000}; ^ src\game\client\components\killmessages.cpp:305:30: style: Variable 'Client' can be declared as reference to const [constVariable] CGameClient::CClientData &Client = GameClient()->m_aClients[m_aKillmsgs[r].m_KillerID]; ^ src\game\client\components\killmessages.cpp:314:30: style: Variable 'Client' can be declared as reference to const [constVariable] CGameClient::CClientData &Client = GameClient()->m_aClients[m_aKillmsgs[r].m_VictimID]; ^ src\game\client\components\menus_ingame.cpp:243:12: style: Variable 'pInfoByName' can be declared as reference to const [constVariable] for(auto &pInfoByName : m_pClient->m_Snap.m_apInfoByName) ^ src\game\client\components\menus_ingame.cpp:530:12: style: Variable 'pInfoByName' can be declared as reference to const [constVariable] for(auto &pInfoByName : m_pClient->m_Snap.m_apInfoByName) ^ src\game\client\components\players.cpp:767:44: style: Variable 'CharacterInfo' can be declared as reference to const [constVariable] CGameClient::CSnapState::CCharacterInfo &CharacterInfo = m_pClient->m_Snap.m_aCharacters[i]; ^ src\game\client\components\spectator.cpp:122:27: style: Variable 'Snap' can be declared as reference to const [constVariable] CGameClient::CSnapState &Snap = pSelf->m_pClient->m_Snap; ^ src\game\client\components\spectator.cpp:221:12: style: Variable 'pInfo' can be declared as reference to const [constVariable] for(auto &pInfo : m_pClient->m_Snap.m_apInfoByDDTeamName) ^ src\game\client\gameclient.cpp:2220:15: style: Variable 'OwnClientData' can be declared as reference to const [constVariable] CClientData &OwnClientData = m_aClients[ownID]; ^ src\game\client\gameclient.cpp:2227:16: style: Variable 'cData' can be declared as reference to const [constVariable] CClientData &cData = m_aClients[i]; ^ src\game\client\prediction\entities\character.cpp:397:11: style: Variable 'aSpreading' can be declared as const array [constVariable] float aSpreading[] = {-0.185f, -0.070f, 0, 0.070f, 0.185f}; ^ src\game\client\prediction\entities\laser.cpp:53:9: style: Variable 'HitPos' can be declared as reference to const [constVariable] vec2 &HitPos = pHit->Core()->m_Pos; ^ src\game\editor\auto_map.cpp:507:18: style: Variable 'Index' can be declared as reference to const [constVariable] for(auto &Index : pRule->m_vIndexList) ^ src\game\editor\auto_map.cpp:518:18: style: Variable 'Index' can be declared as reference to const [constVariable] for(auto &Index : pRule->m_vIndexList) ^ src\game\editor\editor.cpp:118:12: style: Variable 'Item' can be declared as reference to const [constVariable] for(auto &Item : vList) ^ src\game\editor\editor.cpp:2983:11: style: Variable 'aAspects' can be declared as const array [constVariable] float aAspects[] = {4.0f / 3.0f, 16.0f / 10.0f, 5.0f / 4.0f, 16.0f / 9.0f}; ^ src\game\editor\editor.cpp:3141:15: style: Variable 's_aShift' can be declared as const array [constVariable] static int s_aShift[] = {24, 16, 8, 0}; ^ src\engine\server\server.cpp:2807:14: style: Variable 'Client' can be declared as reference to const [constVariable] for(auto &Client : m_aClients) ^ src\engine\server\sql_string_helpers.cpp:51:6: style: Variable 'aTimes' can be declared as const array [constVariable] int aTimes[7] = ^ src\test\secure_random.cpp:24:6: style: Variable 'BOUNDS' can be declared as const array [constVariable] int BOUNDS[] = {2, 3, 4, 5, 10, 100, 127, 128, 129}; ^ ```
2022-11-13 15:29:13 +00:00
const float aAspects[] = {4.0f / 3.0f, 16.0f / 10.0f, 5.0f / 4.0f, 16.0f / 9.0f};
2010-05-29 07:25:38 +00:00
float Aspect = aAspects[i];
float Zoom = m_MenuProofBorders ? 0.7f : 1.0f;
RenderTools()->MapScreenToWorld(
2010-05-29 07:25:38 +00:00
m_WorldOffsetX, m_WorldOffsetY,
100.0f, 100.0f, 100.0f, 0.0f, 0.0f, Aspect, Zoom, aPoints);
2010-05-29 07:25:38 +00:00
2009-10-27 14:38:53 +00:00
CUIRect r;
2010-05-29 07:25:38 +00:00
r.x = aPoints[0];
r.y = aPoints[1];
r.w = aPoints[2] - aPoints[0];
r.h = aPoints[3] - aPoints[1];
2010-05-29 07:25:38 +00:00
IGraphics::CLineItem Array[4] = {
IGraphics::CLineItem(r.x, r.y, r.x + r.w, r.y),
IGraphics::CLineItem(r.x + r.w, r.y, r.x + r.w, r.y + r.h),
IGraphics::CLineItem(r.x + r.w, r.y + r.h, r.x, r.y + r.h),
IGraphics::CLineItem(r.x, r.y + r.h, r.x, r.y)};
2010-05-29 07:25:38 +00:00
Graphics()->LinesDraw(Array, 4);
Graphics()->SetColor(0, 1, 0, 1);
2008-01-12 12:27:55 +00:00
}
}
2009-10-27 14:38:53 +00:00
Graphics()->LinesEnd();
2016-07-18 06:55:35 +00:00
// tee position (blue circle) and other screen positions
2016-07-18 06:55:35 +00:00
{
Graphics()->TextureClear();
2016-07-18 06:55:35 +00:00
Graphics()->QuadsBegin();
Graphics()->SetColor(0, 0, 1, 0.3f);
Graphics()->DrawCircle(m_WorldOffsetX, m_WorldOffsetY - 3.0f, 20.0f, 32);
if(m_MenuProofBorders)
{
Graphics()->SetColor(0, 1, 0, 0.3f);
for(int i = 0; i < (int)m_vMenuBackgroundPositions.size(); i++)
{
vec2 Pos = m_vMenuBackgroundPositions[i];
if(i == m_CurrentMenuProofIndex || Pos == vec2(0, 0))
continue;
Pos += vec2(m_WorldOffsetX, m_WorldOffsetY) - m_vMenuBackgroundPositions[m_CurrentMenuProofIndex];
Graphics()->DrawCircle(Pos.x, Pos.y - 3.0f, 20.0f, 32);
}
}
2016-07-18 06:55:35 +00:00
Graphics()->QuadsEnd();
}
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
if(!m_ShowPicker && m_ShowTileInfo && m_ShowEnvelopePreview != SHOWENV_NONE && GetSelectedLayer(0) && GetSelectedLayer(0)->m_Type == LAYERTYPE_QUADS)
2011-08-15 18:12:31 +00:00
{
GetSelectedGroup()->MapScreen();
CLayerQuads *pLayer = (CLayerQuads *)GetSelectedLayer(0);
IGraphics::CTextureHandle Texture;
if(pLayer->m_Image >= 0 && pLayer->m_Image < (int)m_Map.m_vpImages.size())
Texture = m_Map.m_vpImages[pLayer->m_Image]->m_Texture;
2011-08-15 18:12:31 +00:00
DoQuadEnvelopes(pLayer->m_vQuads, Texture);
m_ShowEnvelopePreview = SHOWENV_NONE;
2014-01-19 03:02:01 +00:00
}
2011-08-14 14:31:48 +00:00
2021-09-22 19:48:55 +00:00
UI()->MapScreen();
2007-05-22 15:03:32 +00:00
}
float CEditor::ScaleFontSize(char *pText, int TextSize, float FontSize, int Width)
{
while(TextRender()->TextWidth(FontSize, pText, -1, -1.0f) > Width)
{
if(FontSize > 6.0f)
{
FontSize--;
}
else
{
pText[str_length(pText) - 4] = '\0';
2020-12-31 09:03:15 +00:00
str_append(pText, "", TextSize);
}
}
return FontSize;
}
int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIDs, int *pNewVal, ColorRGBA Color)
2007-05-22 15:03:32 +00:00
{
2010-05-29 07:25:38 +00:00
int Change = -1;
2008-01-12 12:27:55 +00:00
2010-05-29 07:25:38 +00:00
for(int i = 0; pProps[i].m_pName; i++)
2007-05-22 15:03:32 +00:00
{
2010-05-29 07:25:38 +00:00
CUIRect Slot;
pToolBox->HSplitTop(13.0f, &Slot, pToolBox);
CUIRect Label, Shifter;
Slot.VSplitMid(&Label, &Shifter);
Shifter.HMargin(1.0f, &Shifter);
2022-03-11 16:34:48 +00:00
UI()->DoLabel(&Label, pProps[i].m_pName, 10.0f, TEXTALIGN_LEFT);
2010-05-29 07:25:38 +00:00
if(pProps[i].m_Type == PROPTYPE_INT_STEP)
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
CUIRect Inc, Dec;
char aBuf[64];
Shifter.VSplitRight(10.0f, &Shifter, &Inc);
Shifter.VSplitLeft(10.0f, &Dec, &Shifter);
str_format(aBuf, sizeof(aBuf), "%d", pProps[i].m_Value);
int NewValue = UiDoValueSelector((char *)&pIDs[i], &Shifter, "", pProps[i].m_Value, pProps[i].m_Min, pProps[i].m_Max, 1, 1.0f, "Use left mouse button to drag and change the value. Hold shift to be more precise. Rightclick to edit as text.", false, false, 0, &Color);
if(NewValue != pProps[i].m_Value)
2016-06-02 11:50:06 +00:00
{
*pNewVal = NewValue;
Change = i;
}
if(DoButton_ButtonDec((char *)&pIDs[i] + 1, nullptr, 0, &Dec, 0, "Decrease"))
2008-01-12 12:27:55 +00:00
{
*pNewVal = pProps[i].m_Value - 1;
2010-05-29 07:25:38 +00:00
Change = i;
2008-01-12 12:27:55 +00:00
}
if(DoButton_ButtonInc(((char *)&pIDs[i]) + 2, nullptr, 0, &Inc, 0, "Increase"))
2008-01-12 12:27:55 +00:00
{
*pNewVal = pProps[i].m_Value + 1;
2010-05-29 07:25:38 +00:00
Change = i;
2008-01-12 12:27:55 +00:00
}
}
2010-05-29 07:25:38 +00:00
else if(pProps[i].m_Type == PROPTYPE_BOOL)
{
2010-05-29 07:25:38 +00:00
CUIRect No, Yes;
Shifter.VSplitMid(&No, &Yes);
2011-03-20 16:04:03 +00:00
if(DoButton_ButtonDec(&pIDs[i], "No", !pProps[i].m_Value, &No, 0, ""))
{
2010-05-29 07:25:38 +00:00
*pNewVal = 0;
Change = i;
}
if(DoButton_ButtonInc(((char *)&pIDs[i]) + 1, "Yes", pProps[i].m_Value, &Yes, 0, ""))
{
2010-05-29 07:25:38 +00:00
*pNewVal = 1;
Change = i;
}
2010-05-29 07:25:38 +00:00
}
else if(pProps[i].m_Type == PROPTYPE_INT_SCROLL)
2008-01-12 12:27:55 +00:00
{
2015-03-30 09:44:32 +00:00
int NewValue = UiDoValueSelector(&pIDs[i], &Shifter, "", pProps[i].m_Value, pProps[i].m_Min, pProps[i].m_Max, 1, 1.0f, "Use left mouse button to drag and change the value. Hold shift to be more precise. Rightclick to edit as text.");
2010-05-29 07:25:38 +00:00
if(NewValue != pProps[i].m_Value)
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
*pNewVal = NewValue;
Change = i;
2008-01-12 12:27:55 +00:00
}
}
else if(pProps[i].m_Type == PROPTYPE_ANGLE_SCROLL)
{
2016-06-02 13:31:12 +00:00
CUIRect Inc, Dec;
Shifter.VSplitRight(10.0f, &Shifter, &Inc);
Shifter.VSplitLeft(10.0f, &Dec, &Shifter);
const bool Shift = Input()->ShiftIsPressed();
int Step = Shift ? 1 : 45;
2015-07-15 00:34:50 +00:00
int Value = pProps[i].m_Value;
int NewValue = UiDoValueSelector(&pIDs[i], &Shifter, "", Value, pProps[i].m_Min, pProps[i].m_Max, Shift ? 1 : 45, Shift ? 1.0f : 10.0f, "Use left mouse button to drag and change the value. Hold shift to be more precise. Rightclick to edit as text.", false, false, 0);
if(DoButton_ButtonDec(&pIDs[i] + 1, nullptr, 0, &Dec, 0, "Decrease"))
2016-06-02 13:31:12 +00:00
{
NewValue = (std::ceil((pProps[i].m_Value / (float)Step)) - 1) * Step;
if(NewValue < 0)
NewValue += 360;
2016-06-02 13:31:12 +00:00
}
if(DoButton_ButtonInc(&pIDs[i] + 2, nullptr, 0, &Inc, 0, "Increase"))
NewValue = (pProps[i].m_Value + Step) / Step * Step;
if(NewValue != pProps[i].m_Value)
2016-06-02 13:31:12 +00:00
{
*pNewVal = NewValue % 360;
2016-06-02 13:31:12 +00:00
Change = i;
}
}
2010-05-29 07:25:38 +00:00
else if(pProps[i].m_Type == PROPTYPE_COLOR)
2008-01-12 12:27:55 +00:00
{
static const char *s_apTexts[4] = {"R", "G", "B", "A"};
Declare variables as `const` when possible According to cppcheck's `constVariable` error: ``` src\engine\client\backend\opengl\opengl_sl.cpp:74:43: style: Variable 'Define' can be declared as reference to const [constVariable] for(CGLSLCompiler::SGLSLCompilerDefine &Define : pCompiler->m_vDefines) ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:2149:12: style: Variable 'GraphicThreadCommandBuffer' can be declared as reference to const [constVariable] auto &GraphicThreadCommandBuffer = m_vvThreadDrawCommandBuffers[i + 1][m_CurImageIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:3192:9: style: Variable 'BufferObject' can be declared as reference to const [constVariable] auto &BufferObject = m_vBufferObjects[BufferObjectIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:3200:10: style: Variable 'DescrSet' can be declared as reference to const [constVariable] auto &DescrSet = m_vTextures[State.m_Texture].m_VKStandard3DTexturedDescrSet; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:3810:13: style: Variable 'Mode' can be declared as reference to const [constVariable] for(auto &Mode : vPresentModeList) ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:3818:13: style: Variable 'Mode' can be declared as reference to const [constVariable] for(auto &Mode : vPresentModeList) ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6511:10: style: Variable 'DescrSet' can be declared as reference to const [constVariable] auto &DescrSet = m_vTextures[pCommand->m_State.m_Texture].m_aVKStandardTexturedDescrSets[AddressModeIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6555:10: style: Variable 'DescrSet' can be declared as reference to const [constVariable] auto &DescrSet = m_vTextures[pCommand->m_State.m_Texture].m_VKStandard3DTexturedDescrSet; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6660:9: style: Variable 'MemBlock' can be declared as reference to const [constVariable] auto &MemBlock = m_vBufferObjects[BufferIndex].m_BufferObject.m_Mem; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6799:9: style: Variable 'BufferObject' can be declared as reference to const [constVariable] auto &BufferObject = m_vBufferObjects[BufferObjectIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6808:10: style: Variable 'DescrSet' can be declared as reference to const [constVariable] auto &DescrSet = m_vTextures[pCommand->m_State.m_Texture].m_aVKStandardTexturedDescrSets[AddressModeIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6902:9: style: Variable 'BufferObject' can be declared as reference to const [constVariable] auto &BufferObject = m_vBufferObjects[BufferObjectIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6907:9: style: Variable 'TextTextureDescr' can be declared as reference to const [constVariable] auto &TextTextureDescr = m_vTextures[pCommand->m_TextTextureIndex].m_VKTextDescrSet; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6961:9: style: Variable 'BufferObject' can be declared as reference to const [constVariable] auto &BufferObject = m_vBufferObjects[BufferObjectIndex]; ^ src\engine\client\backend\vulkan\backend_vulkan.cpp:6970:10: style: Variable 'DescrSet' can be declared as reference to const [constVariable] auto &DescrSet = m_vTextures[State.m_Texture].m_aVKStandardTexturedDescrSets[AddressModeIndex]; ^ src\game\client\components\hud.cpp:178:8: style: Variable 'aFlagCarrier' can be declared as const array [constVariable] int aFlagCarrier[2] = { ^ src\game\client\components\hud.cpp:519:16: style: Variable 's_aTextWidth' can be declared as const array [constVariable] static float s_aTextWidth[5] = {s_TextWidth0, s_TextWidth00, s_TextWidth000, s_TextWidth0000, s_TextWidth00000}; ^ src\game\client\components\killmessages.cpp:305:30: style: Variable 'Client' can be declared as reference to const [constVariable] CGameClient::CClientData &Client = GameClient()->m_aClients[m_aKillmsgs[r].m_KillerID]; ^ src\game\client\components\killmessages.cpp:314:30: style: Variable 'Client' can be declared as reference to const [constVariable] CGameClient::CClientData &Client = GameClient()->m_aClients[m_aKillmsgs[r].m_VictimID]; ^ src\game\client\components\menus_ingame.cpp:243:12: style: Variable 'pInfoByName' can be declared as reference to const [constVariable] for(auto &pInfoByName : m_pClient->m_Snap.m_apInfoByName) ^ src\game\client\components\menus_ingame.cpp:530:12: style: Variable 'pInfoByName' can be declared as reference to const [constVariable] for(auto &pInfoByName : m_pClient->m_Snap.m_apInfoByName) ^ src\game\client\components\players.cpp:767:44: style: Variable 'CharacterInfo' can be declared as reference to const [constVariable] CGameClient::CSnapState::CCharacterInfo &CharacterInfo = m_pClient->m_Snap.m_aCharacters[i]; ^ src\game\client\components\spectator.cpp:122:27: style: Variable 'Snap' can be declared as reference to const [constVariable] CGameClient::CSnapState &Snap = pSelf->m_pClient->m_Snap; ^ src\game\client\components\spectator.cpp:221:12: style: Variable 'pInfo' can be declared as reference to const [constVariable] for(auto &pInfo : m_pClient->m_Snap.m_apInfoByDDTeamName) ^ src\game\client\gameclient.cpp:2220:15: style: Variable 'OwnClientData' can be declared as reference to const [constVariable] CClientData &OwnClientData = m_aClients[ownID]; ^ src\game\client\gameclient.cpp:2227:16: style: Variable 'cData' can be declared as reference to const [constVariable] CClientData &cData = m_aClients[i]; ^ src\game\client\prediction\entities\character.cpp:397:11: style: Variable 'aSpreading' can be declared as const array [constVariable] float aSpreading[] = {-0.185f, -0.070f, 0, 0.070f, 0.185f}; ^ src\game\client\prediction\entities\laser.cpp:53:9: style: Variable 'HitPos' can be declared as reference to const [constVariable] vec2 &HitPos = pHit->Core()->m_Pos; ^ src\game\editor\auto_map.cpp:507:18: style: Variable 'Index' can be declared as reference to const [constVariable] for(auto &Index : pRule->m_vIndexList) ^ src\game\editor\auto_map.cpp:518:18: style: Variable 'Index' can be declared as reference to const [constVariable] for(auto &Index : pRule->m_vIndexList) ^ src\game\editor\editor.cpp:118:12: style: Variable 'Item' can be declared as reference to const [constVariable] for(auto &Item : vList) ^ src\game\editor\editor.cpp:2983:11: style: Variable 'aAspects' can be declared as const array [constVariable] float aAspects[] = {4.0f / 3.0f, 16.0f / 10.0f, 5.0f / 4.0f, 16.0f / 9.0f}; ^ src\game\editor\editor.cpp:3141:15: style: Variable 's_aShift' can be declared as const array [constVariable] static int s_aShift[] = {24, 16, 8, 0}; ^ src\engine\server\server.cpp:2807:14: style: Variable 'Client' can be declared as reference to const [constVariable] for(auto &Client : m_aClients) ^ src\engine\server\sql_string_helpers.cpp:51:6: style: Variable 'aTimes' can be declared as const array [constVariable] int aTimes[7] = ^ src\test\secure_random.cpp:24:6: style: Variable 'BOUNDS' can be declared as const array [constVariable] int BOUNDS[] = {2, 3, 4, 5, 10, 100, 127, 128, 129}; ^ ```
2022-11-13 15:29:13 +00:00
static const int s_aShift[] = {24, 16, 8, 0};
2015-08-22 15:50:34 +00:00
int NewColor = 0;
// extra space
CUIRect ColorBox, ColorSlots;
pToolBox->HSplitTop(3.0f * 13.0f, &Slot, pToolBox);
Slot.VSplitMid(&ColorBox, &ColorSlots);
ColorBox.HMargin(1.0f, &ColorBox);
ColorBox.VMargin(6.0f, &ColorBox);
2010-05-29 07:25:38 +00:00
2008-01-12 12:27:55 +00:00
for(int c = 0; c < 4; c++)
{
int v = (pProps[i].m_Value >> s_aShift[c]) & 0xff;
NewColor |= UiDoValueSelector(((char *)&pIDs[i]) + c, &Shifter, s_apTexts[c], v, 0, 255, 1, 1.0f, "Use left mouse button to drag and change the color value. Hold shift to be more precise. Rightclick to edit as text.") << s_aShift[c];
2008-01-12 12:27:55 +00:00
if(c != 3)
{
ColorSlots.HSplitTop(13.0f, &Shifter, &ColorSlots);
2010-05-29 07:25:38 +00:00
Shifter.HMargin(1.0f, &Shifter);
2008-01-12 12:27:55 +00:00
}
}
2010-05-29 07:25:38 +00:00
// hex
pToolBox->HSplitTop(13.0f, &Slot, pToolBox);
Slot.VSplitMid(nullptr, &Shifter);
Shifter.HMargin(1.0f, &Shifter);
int NewColorHex = pProps[i].m_Value & 0xff;
NewColorHex |= UiDoValueSelector(((char *)&pIDs[i] - 1), &Shifter, "", (pProps[i].m_Value >> 8) & 0xFFFFFF, 0, 0xFFFFFF, 1, 1.0f, "Use left mouse button to drag and change the color value. Hold shift to be more precise. Rightclick to edit as text.", false, true) << 8;
// color picker
ColorRGBA ColorPick = ColorRGBA(
((pProps[i].m_Value >> s_aShift[0]) & 0xff) / 255.0f,
((pProps[i].m_Value >> s_aShift[1]) & 0xff) / 255.0f,
((pProps[i].m_Value >> s_aShift[2]) & 0xff) / 255.0f,
1.0f);
2017-02-21 16:10:08 +00:00
static int s_ColorPicker, s_ColorPickerID;
if(DoButton_ColorPicker(&s_ColorPicker, &ColorBox, &ColorPick))
{
2022-04-04 18:51:19 +00:00
ms_PickerColor = color_cast<ColorHSVA>(ColorPick);
UiInvokePopupMenu(&s_ColorPickerID, 0, UI()->MouseX(), UI()->MouseY(), 180, 150, PopupColorPicker);
}
if(UI()->HotItem() == &ms_SVPicker || UI()->HotItem() == &ms_HuePicker)
{
2019-04-26 12:06:32 +00:00
ColorRGBA c = color_cast<ColorRGBA>(ms_PickerColor);
NewColor = ((int)(c.r * 255.0f) & 0xff) << 24 | ((int)(c.g * 255.0f) & 0xff) << 16 | ((int)(c.b * 255.0f) & 0xff) << 8 | (pProps[i].m_Value & 0xff);
}
//
2010-05-29 07:25:38 +00:00
if(NewColor != pProps[i].m_Value)
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
*pNewVal = NewColor;
Change = i;
2008-01-12 12:27:55 +00:00
}
else if(NewColorHex != pProps[i].m_Value)
{
*pNewVal = NewColorHex;
Change = i;
}
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
else if(pProps[i].m_Type == PROPTYPE_IMAGE)
2008-01-13 22:37:58 +00:00
{
2010-05-29 07:25:38 +00:00
char aBuf[64];
if(pProps[i].m_Value < 0)
2011-03-20 16:04:03 +00:00
str_copy(aBuf, "None", sizeof(aBuf));
2008-01-17 23:09:49 +00:00
else
str_copy(aBuf, m_Map.m_vpImages[pProps[i].m_Value]->m_aName);
2010-05-29 07:25:38 +00:00
float FontSize = ScaleFontSize(aBuf, sizeof(aBuf), 10.0f, Shifter.w);
if(DoButton_Ex(&pIDs[i], aBuf, 0, &Shifter, 0, nullptr, IGraphics::CORNER_ALL, FontSize))
2010-05-29 07:25:38 +00:00
PopupSelectImageInvoke(pProps[i].m_Value, UI()->MouseX(), UI()->MouseY());
int r = PopupSelectImageResult();
2008-01-17 23:09:49 +00:00
if(r >= -1)
2008-01-13 22:37:58 +00:00
{
2010-05-29 07:25:38 +00:00
*pNewVal = r;
Change = i;
2008-01-13 22:37:58 +00:00
}
}
else if(pProps[i].m_Type == PROPTYPE_SHIFT)
{
CUIRect Left, Right, Up, Down;
2022-01-26 19:19:44 +00:00
Shifter.VSplitMid(&Left, &Up, 2.0f);
Left.VSplitLeft(10.0f, &Left, &Shifter);
Shifter.VSplitRight(10.0f, &Shifter, &Right);
Shifter.Draw(ColorRGBA(1, 1, 1, 0.5f), 0, 0.0f);
2022-03-11 16:34:48 +00:00
UI()->DoLabel(&Shifter, "X", 10.0f, TEXTALIGN_CENTER);
Up.VSplitLeft(10.0f, &Up, &Shifter);
Shifter.VSplitRight(10.0f, &Shifter, &Down);
Shifter.Draw(ColorRGBA(1, 1, 1, 0.5f), 0, 0.0f);
2022-03-11 16:34:48 +00:00
UI()->DoLabel(&Shifter, "Y", 10.0f, TEXTALIGN_CENTER);
2011-03-20 16:04:03 +00:00
if(DoButton_ButtonDec(&pIDs[i], "-", 0, &Left, 0, "Left"))
{
*pNewVal = DIRECTION_LEFT;
Change = i;
}
if(DoButton_ButtonInc(((char *)&pIDs[i]) + 3, "+", 0, &Right, 0, "Right"))
{
*pNewVal = DIRECTION_RIGHT;
Change = i;
}
if(DoButton_ButtonDec(((char *)&pIDs[i]) + 1, "-", 0, &Up, 0, "Up"))
{
*pNewVal = DIRECTION_UP;
Change = i;
}
if(DoButton_ButtonInc(((char *)&pIDs[i]) + 2, "+", 0, &Down, 0, "Down"))
{
*pNewVal = DIRECTION_DOWN;
Change = i;
}
}
2014-10-08 15:33:06 +00:00
else if(pProps[i].m_Type == PROPTYPE_SOUND)
{
char aBuf[64];
if(pProps[i].m_Value < 0)
str_copy(aBuf, "None", sizeof(aBuf));
else
str_copy(aBuf, m_Map.m_vpSounds[pProps[i].m_Value]->m_aName);
2014-10-08 15:33:06 +00:00
float FontSize = ScaleFontSize(aBuf, sizeof(aBuf), 10.0f, Shifter.w);
if(DoButton_Ex(&pIDs[i], aBuf, 0, &Shifter, 0, nullptr, IGraphics::CORNER_ALL, FontSize))
2014-10-08 15:33:06 +00:00
PopupSelectSoundInvoke(pProps[i].m_Value, UI()->MouseX(), UI()->MouseY());
int r = PopupSelectSoundResult();
if(r >= -1)
{
*pNewVal = r;
Change = i;
}
}
else if(pProps[i].m_Type == PROPTYPE_AUTOMAPPER)
{
char aBuf[64];
if(pProps[i].m_Value < 0 || pProps[i].m_Min < 0 || pProps[i].m_Min >= (int)m_Map.m_vpImages.size())
str_copy(aBuf, "None", sizeof(aBuf));
else
str_copy(aBuf, m_Map.m_vpImages[pProps[i].m_Min]->m_AutoMapper.GetConfigName(pProps[i].m_Value));
float FontSize = ScaleFontSize(aBuf, sizeof(aBuf), 10.0f, Shifter.w);
if(DoButton_Ex(&pIDs[i], aBuf, 0, &Shifter, 0, nullptr, IGraphics::CORNER_ALL, FontSize))
PopupSelectConfigAutoMapInvoke(pProps[i].m_Value, UI()->MouseX(), UI()->MouseY());
int r = PopupSelectConfigAutoMapResult();
if(r >= -1)
{
*pNewVal = r;
Change = i;
}
}
2020-01-23 19:26:06 +00:00
else if(pProps[i].m_Type == PROPTYPE_ENVELOPE)
{
CUIRect Inc, Dec;
char aBuf[8];
2020-01-23 20:24:48 +00:00
int CurValue = pProps[i].m_Value;
2020-01-23 19:26:06 +00:00
Shifter.VSplitRight(10.0f, &Shifter, &Inc);
Shifter.VSplitLeft(10.0f, &Dec, &Shifter);
2020-01-23 20:24:48 +00:00
if(CurValue <= 0)
str_copy(aBuf, "None:", sizeof(aBuf));
else if(m_Map.m_vpEnvelopes[CurValue - 1]->m_aName[0])
{
str_format(aBuf, sizeof(aBuf), "%s:", m_Map.m_vpEnvelopes[CurValue - 1]->m_aName);
if(!str_endswith(aBuf, ":"))
{
aBuf[sizeof(aBuf) - 2] = ':';
aBuf[sizeof(aBuf) - 1] = '\0';
}
}
2020-01-23 19:48:42 +00:00
else
aBuf[0] = '\0';
2020-01-23 19:26:06 +00:00
int NewVal = UiDoValueSelector((char *)&pIDs[i], &Shifter, aBuf, CurValue, 0, m_Map.m_vpEnvelopes.size(), 1, 1.0f, "Set Envelope", false, false, IGraphics::CORNER_NONE);
if(NewVal != CurValue)
{
*pNewVal = NewVal;
Change = i;
}
2020-01-23 19:26:06 +00:00
if(DoButton_ButtonDec((char *)&pIDs[i] + 1, nullptr, 0, &Dec, 0, "Previous Envelope"))
2020-01-23 19:26:06 +00:00
{
*pNewVal = pProps[i].m_Value - 1;
2020-01-23 19:26:06 +00:00
Change = i;
}
if(DoButton_ButtonInc(((char *)&pIDs[i]) + 2, nullptr, 0, &Inc, 0, "Next Envelope"))
2020-01-23 19:26:06 +00:00
{
*pNewVal = pProps[i].m_Value + 1;
2020-01-23 19:26:06 +00:00
Change = i;
}
}
2007-05-22 15:03:32 +00:00
}
2008-03-21 00:13:55 +00:00
2010-05-29 07:25:38 +00:00
return Change;
2007-05-22 15:03:32 +00:00
}
void CEditor::RenderLayers(CUIRect LayersBox)
2007-05-22 15:03:32 +00:00
{
const float RowHeight = 12.0f;
2010-05-29 07:25:38 +00:00
char aBuf[64];
2007-12-02 17:55:45 +00:00
2023-03-15 17:15:43 +00:00
CUIRect UnscrolledLayersBox = LayersBox;
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 = RowHeight * 5.0f;
s_ScrollRegion.Begin(&LayersBox, &ScrollOffset, &ScrollParams);
LayersBox.y += ScrollOffset.y;
2023-03-15 17:15:43 +00:00
enum
{
OP_NONE = 0,
OP_CLICK,
OP_LAYER_DRAG,
OP_GROUP_DRAG
};
static int s_Operation = OP_NONE;
static const void *s_pDraggedButton = 0;
static float s_InitialMouseY = 0;
static float s_InitialCutHeight = 0;
int GroupAfterDraggedLayer = -1;
int LayerAfterDraggedLayer = -1;
bool DraggedPositionFound = false;
bool MoveLayers = false;
bool MoveGroup = false;
bool StartDragLayer = false;
bool StartDragGroup = false;
std::vector<int> vButtonsPerGroup;
vButtonsPerGroup.reserve(m_Map.m_vpGroups.size());
for(CLayerGroup *pGroup : m_Map.m_vpGroups)
{
vButtonsPerGroup.push_back(pGroup->m_vpLayers.size() + 1);
}
if(!UI()->CheckActiveItem(s_pDraggedButton))
s_Operation = OP_NONE;
if(s_Operation == OP_LAYER_DRAG || s_Operation == OP_GROUP_DRAG)
{
float MinDraggableValue = UnscrolledLayersBox.y;
float MaxDraggableValue = MinDraggableValue;
for(int NumButtons : vButtonsPerGroup)
{
MaxDraggableValue += NumButtons * (RowHeight + 2.0f) + 5.0f;
}
MaxDraggableValue += ScrollOffset.y;
if(s_Operation == OP_GROUP_DRAG)
{
MaxDraggableValue -= vButtonsPerGroup[m_SelectedGroup] * (RowHeight + 2.0f) + 5.0f;
}
else if(s_Operation == OP_LAYER_DRAG)
{
MinDraggableValue += RowHeight + 2.0f;
MaxDraggableValue -= m_vSelectedLayers.size() * (RowHeight + 2.0f) + 5.0f;
}
UnscrolledLayersBox.HSplitTop(s_InitialCutHeight, nullptr, &UnscrolledLayersBox);
UnscrolledLayersBox.y -= s_InitialMouseY - UI()->MouseY();
UnscrolledLayersBox.y = clamp(UnscrolledLayersBox.y, MinDraggableValue, MaxDraggableValue);
UnscrolledLayersBox.w = LayersBox.w;
}
const bool ScrollToSelection = SelectLayerByTile();
2010-05-29 07:25:38 +00:00
// render layers
2022-08-13 17:02:55 +00:00
for(int g = 0; g < (int)m_Map.m_vpGroups.size(); g++)
2007-12-02 17:55:45 +00:00
{
2023-03-15 17:15:43 +00:00
if(s_Operation == OP_LAYER_DRAG && g > 0 && !DraggedPositionFound && UI()->MouseY() < LayersBox.y + RowHeight / 2)
{
DraggedPositionFound = true;
GroupAfterDraggedLayer = g;
LayerAfterDraggedLayer = m_Map.m_vpGroups[g - 1]->m_vpLayers.size();
CUIRect Slot;
LayersBox.HSplitTop(m_vSelectedLayers.size() * (RowHeight + 2.0f), &Slot, &LayersBox);
s_ScrollRegion.AddRect(Slot);
}
CUIRect Slot, VisibleToggle;
2023-03-15 17:15:43 +00:00
if(s_Operation == OP_GROUP_DRAG)
{
if(g == m_SelectedGroup)
{
UnscrolledLayersBox.HSplitTop(RowHeight, &Slot, &UnscrolledLayersBox);
UnscrolledLayersBox.HSplitTop(2.0f, nullptr, &UnscrolledLayersBox);
}
else if(!DraggedPositionFound && UI()->MouseY() < LayersBox.y + RowHeight * vButtonsPerGroup[g] / 2 + 3.0f)
{
DraggedPositionFound = true;
GroupAfterDraggedLayer = g;
CUIRect TmpSlot;
if(m_Map.m_vpGroups[m_SelectedGroup]->m_Collapse)
LayersBox.HSplitTop(RowHeight + 7.0f, &TmpSlot, &LayersBox);
else
LayersBox.HSplitTop(vButtonsPerGroup[m_SelectedGroup] * (RowHeight + 2.0f) + 5.0f, &TmpSlot, &LayersBox);
s_ScrollRegion.AddRect(TmpSlot, false);
}
}
if(s_Operation != OP_GROUP_DRAG || g != m_SelectedGroup)
{
LayersBox.HSplitTop(RowHeight, &Slot, &LayersBox);
CUIRect TmpRect;
LayersBox.HSplitTop(2.0f, &TmpRect, &LayersBox);
s_ScrollRegion.AddRect(TmpRect);
}
if(s_ScrollRegion.AddRect(Slot))
2022-08-13 17:02:55 +00:00
{
Slot.VSplitLeft(15.0f, &VisibleToggle, &Slot);
if(DoButton_FontIcon(&m_Map.m_vpGroups[g]->m_Visible, m_Map.m_vpGroups[g]->m_Visible ? "\xEF\x81\xAE" : "\xEF\x81\xB0", m_Map.m_vpGroups[g]->m_Collapse ? 1 : 0, &VisibleToggle, 0, "Toggle group visibility", IGraphics::CORNER_L, 8.0f, 0))
2022-08-13 17:02:55 +00:00
m_Map.m_vpGroups[g]->m_Visible = !m_Map.m_vpGroups[g]->m_Visible;
2022-08-13 17:02:55 +00:00
str_format(aBuf, sizeof(aBuf), "#%d %s", g, m_Map.m_vpGroups[g]->m_aName);
float FontSize = 10.0f;
while(TextRender()->TextWidth(FontSize, aBuf, -1, -1.0f) > Slot.w && FontSize >= 7.0f)
2022-08-13 17:02:55 +00:00
FontSize--;
2023-03-15 17:15:43 +00:00
bool Clicked;
bool Abrupted;
if(int Result = DoButton_DraggableEx(&m_Map.m_vpGroups[g], aBuf, g == m_SelectedGroup, &Slot, &Clicked, &Abrupted,
2022-08-13 17:02:55 +00:00
BUTTON_CONTEXT, m_Map.m_vpGroups[g]->m_Collapse ? "Select group. Shift click to select all layers. Double click to expand." : "Select group. Shift click to select all layers. Double click to collapse.", IGraphics::CORNER_R, FontSize))
2008-01-12 12:27:55 +00:00
{
2023-03-15 17:15:43 +00:00
if(s_Operation == OP_NONE)
{
s_InitialMouseY = UI()->MouseY();
s_InitialCutHeight = s_InitialMouseY - UnscrolledLayersBox.y;
s_Operation = OP_CLICK;
2010-05-29 07:25:38 +00:00
2023-03-15 17:15:43 +00:00
if(g != m_SelectedGroup)
SelectLayer(0, g);
}
if(Abrupted)
s_Operation = OP_NONE;
if(s_Operation == OP_CLICK)
{
2023-03-15 17:15:43 +00:00
if(absolute(UI()->MouseY() - s_InitialMouseY) > 5)
StartDragGroup = true;
s_pDraggedButton = &m_Map.m_vpGroups[g];
}
if(s_Operation == OP_CLICK && Clicked)
{
if(g != m_SelectedGroup)
SelectLayer(0, g);
if(Input()->ShiftIsPressed() && m_SelectedGroup == g)
{
2023-03-15 17:15:43 +00:00
m_vSelectedLayers.clear();
for(size_t i = 0; i < m_Map.m_vpGroups[g]->m_vpLayers.size(); i++)
{
AddSelectedLayer(i);
}
}
2010-05-29 07:25:38 +00:00
2023-03-15 17:15:43 +00:00
static int s_GroupPopupId = 0;
if(Result == 2)
UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 145, 256, PopupGroup);
2023-03-15 17:15:43 +00:00
if(!m_Map.m_vpGroups[g]->m_vpLayers.empty() && Input()->MouseDoubleClick())
m_Map.m_vpGroups[g]->m_Collapse ^= 1;
s_Operation = OP_NONE;
}
if(s_Operation == OP_GROUP_DRAG && Clicked)
MoveGroup = true;
}
2022-08-13 17:02:55 +00:00
}
2022-08-13 17:02:55 +00:00
for(int i = 0; i < (int)m_Map.m_vpGroups[g]->m_vpLayers.size(); i++)
{
if(m_Map.m_vpGroups[g]->m_Collapse)
2022-08-13 17:02:55 +00:00
continue;
bool IsLayerSelected = false;
if(m_SelectedGroup == g)
{
for(const auto &Selected : m_vSelectedLayers)
{
if(Selected == i)
{
IsLayerSelected = true;
break;
}
}
2022-08-13 17:02:55 +00:00
}
2023-03-15 17:15:43 +00:00
if(s_Operation == OP_GROUP_DRAG && g == m_SelectedGroup)
{
UnscrolledLayersBox.HSplitTop(RowHeight + 2.0f, &Slot, &UnscrolledLayersBox);
}
else if(s_Operation == OP_LAYER_DRAG)
{
if(IsLayerSelected)
{
UnscrolledLayersBox.HSplitTop(RowHeight + 2.0f, &Slot, &UnscrolledLayersBox);
}
else
{
if(!DraggedPositionFound && UI()->MouseY() < LayersBox.y + RowHeight / 2)
{
DraggedPositionFound = true;
GroupAfterDraggedLayer = g + 1;
LayerAfterDraggedLayer = i;
for(size_t j = 0; j < m_vSelectedLayers.size(); j++)
{
LayersBox.HSplitTop(RowHeight + 2.0f, nullptr, &LayersBox);
s_ScrollRegion.AddRect(Slot);
}
}
LayersBox.HSplitTop(RowHeight + 2.0f, &Slot, &LayersBox);
if(!s_ScrollRegion.AddRect(Slot, ScrollToSelection && IsLayerSelected))
continue;
}
}
else
{
LayersBox.HSplitTop(RowHeight + 2.0f, &Slot, &LayersBox);
if(!s_ScrollRegion.AddRect(Slot, ScrollToSelection && IsLayerSelected))
continue;
}
Slot.HSplitTop(RowHeight, &Slot, nullptr);
CUIRect Button;
Slot.VSplitLeft(12.0f, nullptr, &Slot);
Slot.VSplitLeft(15.0f, &VisibleToggle, &Button);
2008-01-12 12:27:55 +00:00
if(DoButton_FontIcon(&m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Visible, m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Visible ? "\xEF\x81\xAE" : "\xEF\x81\xB0", 0, &VisibleToggle, 0, "Toggle layer visibility", IGraphics::CORNER_L, 8.0f, 0))
2022-08-13 17:02:55 +00:00
m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Visible = !m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Visible;
2008-01-12 12:27:55 +00:00
2022-08-13 17:02:55 +00:00
if(m_Map.m_vpGroups[g]->m_vpLayers[i]->m_aName[0])
str_copy(aBuf, m_Map.m_vpGroups[g]->m_vpLayers[i]->m_aName, sizeof(aBuf));
else
{
if(m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Type == LAYERTYPE_TILES)
{
2022-08-13 17:02:55 +00:00
CLayerTiles *pTiles = (CLayerTiles *)m_Map.m_vpGroups[g]->m_vpLayers[i];
str_copy(aBuf, pTiles->m_Image >= 0 ? m_Map.m_vpImages[pTiles->m_Image]->m_aName : "Tiles", sizeof(aBuf));
}
2022-08-13 17:02:55 +00:00
else if(m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Type == LAYERTYPE_QUADS)
{
CLayerQuads *pQuads = (CLayerQuads *)m_Map.m_vpGroups[g]->m_vpLayers[i];
str_copy(aBuf, pQuads->m_Image >= 0 ? m_Map.m_vpImages[pQuads->m_Image]->m_aName : "Quads", sizeof(aBuf));
}
else if(m_Map.m_vpGroups[g]->m_vpLayers[i]->m_Type == LAYERTYPE_SOUNDS)
{
CLayerSounds *pSounds = (CLayerSounds *)m_Map.m_vpGroups[g]->m_vpLayers[i];
str_copy(aBuf, pSounds->m_Sound >= 0 ? m_Map.m_vpSounds[pSounds->m_Sound]->m_aName : "Sounds", sizeof(aBuf));
}
if(str_length(aBuf) > 11)
str_format(aBuf, sizeof(aBuf), "%.8s...", aBuf);
}
2011-07-12 01:14:46 +00:00
2022-08-13 17:02:55 +00:00
float FontSize = 10.0f;
while(TextRender()->TextWidth(FontSize, aBuf, -1, -1.0f) > Button.w && FontSize >= 7.0f)
2022-08-13 17:02:55 +00:00
FontSize--;
2018-10-02 01:52:01 +00:00
int Checked = IsLayerSelected ? 1 : 0;
2022-08-13 17:02:55 +00:00
if(m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pGameLayer ||
m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pFrontLayer ||
m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pSwitchLayer ||
m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pTuneLayer ||
m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pSpeedupLayer ||
m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pTeleLayer)
{
Checked += 6;
}
2023-03-15 17:15:43 +00:00
bool Clicked;
bool Abrupted;
if(int Result = DoButton_DraggableEx(m_Map.m_vpGroups[g]->m_vpLayers[i], aBuf, Checked, &Button, &Clicked, &Abrupted,
2022-08-13 17:02:55 +00:00
BUTTON_CONTEXT, "Select layer. Shift click to select multiple.", IGraphics::CORNER_R, FontSize))
{
2023-03-15 17:15:43 +00:00
if(s_Operation == OP_NONE)
2008-01-12 12:27:55 +00:00
{
2023-03-15 17:15:43 +00:00
s_InitialMouseY = UI()->MouseY();
s_InitialCutHeight = s_InitialMouseY - UnscrolledLayersBox.y;
s_Operation = OP_CLICK;
if(!Input()->ShiftIsPressed() && !IsLayerSelected)
2020-02-28 15:25:27 +00:00
{
2022-08-13 17:02:55 +00:00
SelectLayer(i, g);
}
}
2023-03-15 17:15:43 +00:00
if(Abrupted)
s_Operation = OP_NONE;
if(s_Operation == OP_CLICK)
2022-08-13 17:02:55 +00:00
{
2023-03-15 17:15:43 +00:00
if(absolute(UI()->MouseY() - s_InitialMouseY) > 5)
2022-08-13 17:02:55 +00:00
{
2023-03-15 17:15:43 +00:00
bool EntitiesLayerSelected = false;
for(int k : m_vSelectedLayers)
{
if(m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers[k]->IsEntitiesLayer())
EntitiesLayerSelected = true;
}
if(!EntitiesLayerSelected)
StartDragLayer = true;
2022-08-13 17:02:55 +00:00
}
2023-03-15 17:15:43 +00:00
s_pDraggedButton = m_Map.m_vpGroups[g]->m_vpLayers[i];
}
if(s_Operation == OP_CLICK && Clicked)
{
static CLayerPopupContext s_LayerPopupContext = {};
if(Result == 1)
2022-08-13 17:02:55 +00:00
{
2023-03-15 17:15:43 +00:00
if(Input()->ShiftIsPressed() && m_SelectedGroup == g)
2020-02-28 15:25:27 +00:00
{
2023-03-15 17:15:43 +00:00
auto Position = std::find(m_vSelectedLayers.begin(), m_vSelectedLayers.end(), i);
if(Position != m_vSelectedLayers.end())
m_vSelectedLayers.erase(Position);
2022-08-13 17:02:55 +00:00
else
2023-03-15 17:15:43 +00:00
AddSelectedLayer(i);
}
else if(!Input()->ShiftIsPressed())
{
SelectLayer(i, g);
2020-02-28 15:25:27 +00:00
}
2023-03-15 17:15:43 +00:00
}
else if(Result == 2)
{
if(!IsLayerSelected)
{
SelectLayer(i, g);
}
if(m_vSelectedLayers.size() > 1)
{
bool AllTile = true;
for(size_t j = 0; AllTile && j < m_vSelectedLayers.size(); j++)
{
if(m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers[m_vSelectedLayers[j]]->m_Type == LAYERTYPE_TILES)
s_LayerPopupContext.m_vpLayers.push_back((CLayerTiles *)m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers[m_vSelectedLayers[j]]);
else
AllTile = false;
}
2020-02-28 15:25:27 +00:00
2023-03-15 17:15:43 +00:00
// Don't allow editing if all selected layers are tile layers
if(!AllTile)
s_LayerPopupContext.m_vpLayers.clear();
}
else
2022-08-13 17:02:55 +00:00
s_LayerPopupContext.m_vpLayers.clear();
2023-03-15 17:15:43 +00:00
UiInvokePopupMenu(&s_LayerPopupContext, 0, UI()->MouseX(), UI()->MouseY(), 120, 320, PopupLayer, &s_LayerPopupContext);
2020-02-28 15:25:27 +00:00
}
2010-05-29 07:25:38 +00:00
2023-03-15 17:15:43 +00:00
s_Operation = OP_NONE;
}
if(s_Operation == OP_LAYER_DRAG && Clicked)
{
MoveLayers = true;
2022-08-13 17:02:55 +00:00
}
2008-01-12 12:27:55 +00:00
}
2007-12-02 17:55:45 +00:00
}
2023-03-15 17:15:43 +00:00
if(s_Operation != OP_GROUP_DRAG || g != m_SelectedGroup)
{
LayersBox.HSplitTop(5.0f, &Slot, &LayersBox);
s_ScrollRegion.AddRect(Slot);
}
}
if(!DraggedPositionFound && s_Operation == OP_LAYER_DRAG)
{
GroupAfterDraggedLayer = m_Map.m_vpGroups.size();
LayerAfterDraggedLayer = m_Map.m_vpGroups[GroupAfterDraggedLayer - 1]->m_vpLayers.size();
CUIRect TmpSlot;
LayersBox.HSplitTop(m_vSelectedLayers.size() * (RowHeight + 2.0f), &TmpSlot, &LayersBox);
s_ScrollRegion.AddRect(TmpSlot);
}
if(!DraggedPositionFound && s_Operation == OP_GROUP_DRAG)
{
GroupAfterDraggedLayer = m_Map.m_vpGroups.size();
CUIRect TmpSlot;
if(m_Map.m_vpGroups[m_SelectedGroup]->m_Collapse)
LayersBox.HSplitTop(RowHeight + 7.0f, &TmpSlot, &LayersBox);
else
LayersBox.HSplitTop(vButtonsPerGroup[m_SelectedGroup] * (RowHeight + 2.0f) + 5.0f, &TmpSlot, &LayersBox);
s_ScrollRegion.AddRect(TmpSlot, false);
}
if(MoveLayers && 1 <= GroupAfterDraggedLayer && GroupAfterDraggedLayer <= (int)m_Map.m_vpGroups.size())
{
std::vector<CLayer *> &vpNewGroupLayers = m_Map.m_vpGroups[GroupAfterDraggedLayer - 1]->m_vpLayers;
if(0 <= LayerAfterDraggedLayer && LayerAfterDraggedLayer <= (int)vpNewGroupLayers.size())
{
std::vector<CLayer *> vpSelectedLayers;
std::vector<CLayer *> &vpSelectedGroupLayers = m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers;
CLayer *pNextLayer = nullptr;
if(LayerAfterDraggedLayer < (int)vpNewGroupLayers.size())
pNextLayer = vpNewGroupLayers[LayerAfterDraggedLayer];
std::sort(m_vSelectedLayers.begin(), m_vSelectedLayers.end(), std::greater<>());
for(int k : m_vSelectedLayers)
{
vpSelectedLayers.insert(vpSelectedLayers.begin(), vpSelectedGroupLayers[k]);
}
for(int k : m_vSelectedLayers)
{
vpSelectedGroupLayers.erase(vpSelectedGroupLayers.begin() + k);
}
auto InsertPosition = std::find(vpNewGroupLayers.begin(), vpNewGroupLayers.end(), pNextLayer);
vpNewGroupLayers.insert(InsertPosition, vpSelectedLayers.begin(), vpSelectedLayers.end());
m_SelectedGroup = GroupAfterDraggedLayer - 1;
m_vSelectedLayers.clear();
m_vSelectedQuads.clear();
m_Map.m_Modified = true;
}
}
if(MoveGroup && 0 <= GroupAfterDraggedLayer && GroupAfterDraggedLayer <= (int)m_Map.m_vpGroups.size())
{
CLayerGroup *pSelectedGroup = m_Map.m_vpGroups[m_SelectedGroup];
CLayerGroup *pNextGroup = nullptr;
if(GroupAfterDraggedLayer < (int)m_Map.m_vpGroups.size())
pNextGroup = m_Map.m_vpGroups[GroupAfterDraggedLayer];
m_Map.m_vpGroups.erase(m_Map.m_vpGroups.begin() + m_SelectedGroup);
auto InsertPosition = std::find(m_Map.m_vpGroups.begin(), m_Map.m_vpGroups.end(), pNextGroup);
m_Map.m_vpGroups.insert(InsertPosition, pSelectedGroup);
m_SelectedGroup = GroupAfterDraggedLayer == 0 ? 0 : GroupAfterDraggedLayer - 1;
m_Map.m_Modified = true;
}
if(MoveLayers || MoveGroup)
s_Operation = OP_NONE;
if(StartDragLayer)
s_Operation = OP_LAYER_DRAG;
if(StartDragGroup)
s_Operation = OP_GROUP_DRAG;
if(s_Operation == OP_LAYER_DRAG || s_Operation == OP_GROUP_DRAG)
{
s_ScrollRegion.DoEdgeScrolling();
UI()->SetActiveItem(s_pDraggedButton);
2007-12-02 17:55:45 +00:00
}
2010-05-29 07:25:38 +00:00
2023-03-15 17:15:43 +00:00
if(Input()->KeyPress(KEY_DOWN) && m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && s_Operation == OP_NONE)
{
if(Input()->ShiftIsPressed())
{
if(m_vSelectedLayers[m_vSelectedLayers.size() - 1] < (int)m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers.size() - 1)
AddSelectedLayer(m_vSelectedLayers[m_vSelectedLayers.size() - 1] + 1);
}
else
{
int CurrentLayer = 0;
for(const auto &Selected : m_vSelectedLayers)
CurrentLayer = maximum(Selected, CurrentLayer);
SelectLayer(CurrentLayer);
if(m_vSelectedLayers[0] < (int)m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers.size() - 1)
{
SelectLayer(m_vSelectedLayers[0] + 1);
}
else
{
for(size_t Group = m_SelectedGroup + 1; Group < m_Map.m_vpGroups.size(); Group++)
{
if(!m_Map.m_vpGroups[Group]->m_vpLayers.empty())
{
SelectLayer(0, Group);
break;
}
}
}
}
}
2023-03-15 17:15:43 +00:00
if(Input()->KeyPress(KEY_UP) && m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && s_Operation == OP_NONE)
{
if(Input()->ShiftIsPressed())
{
if(m_vSelectedLayers[m_vSelectedLayers.size() - 1] > 0)
AddSelectedLayer(m_vSelectedLayers[m_vSelectedLayers.size() - 1] - 1);
}
else
{
int CurrentLayer = std::numeric_limits<int>::max();
for(const auto &Selected : m_vSelectedLayers)
CurrentLayer = minimum(Selected, CurrentLayer);
SelectLayer(CurrentLayer);
if(m_vSelectedLayers[0] > 0)
{
SelectLayer(m_vSelectedLayers[0] - 1);
}
else
{
for(int Group = m_SelectedGroup - 1; Group >= 0; Group--)
{
if(!m_Map.m_vpGroups[Group]->m_vpLayers.empty())
{
SelectLayer(m_Map.m_vpGroups[Group]->m_vpLayers.size() - 1, Group);
break;
}
}
}
}
}
CUIRect AddGroupButton;
LayersBox.HSplitTop(RowHeight + 1.0f, &AddGroupButton, &LayersBox);
if(s_ScrollRegion.AddRect(AddGroupButton))
2007-12-02 17:55:45 +00:00
{
AddGroupButton.HSplitTop(RowHeight, &AddGroupButton, 0);
static int s_AddGroupButton = 0;
if(DoButton_Editor(&s_AddGroupButton, "Add group", 0, &AddGroupButton, IGraphics::CORNER_R, "Adds a new group"))
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
m_Map.NewGroup();
m_SelectedGroup = m_Map.m_vpGroups.size() - 1;
2007-12-02 17:55:45 +00:00
}
}
s_ScrollRegion.End();
}
bool CEditor::SelectLayerByTile()
{
// ctrl+rightclick a map index to select the layer that has a tile there
static bool s_CtrlClick = false;
static int s_Selected = 0;
int MatchedGroup = -1;
int MatchedLayer = -1;
int Matches = 0;
bool IsFound = false;
if(UI()->MouseButton(1) && Input()->ModifierIsPressed())
{
if(s_CtrlClick)
return false;
s_CtrlClick = true;
for(size_t g = 0; g < m_Map.m_vpGroups.size(); g++)
{
for(size_t l = 0; l < m_Map.m_vpGroups[g]->m_vpLayers.size(); l++)
{
if(IsFound)
continue;
if(m_Map.m_vpGroups[g]->m_vpLayers[l]->m_Type != LAYERTYPE_TILES)
continue;
CLayerTiles *pTiles = (CLayerTiles *)m_Map.m_vpGroups[g]->m_vpLayers[l];
int x = (int)UI()->MouseWorldX() / 32 + m_Map.m_vpGroups[g]->m_OffsetX;
int y = (int)UI()->MouseWorldY() / 32 + m_Map.m_vpGroups[g]->m_OffsetY;
if(x < 0 || x >= pTiles->m_Width)
continue;
if(y < 0 || y >= pTiles->m_Height)
continue;
CTile Tile = pTiles->GetTile(x, y);
if(Tile.m_Index)
{
if(MatchedGroup == -1)
{
MatchedGroup = g;
MatchedLayer = l;
}
if(++Matches > s_Selected)
{
s_Selected++;
MatchedGroup = g;
MatchedLayer = l;
IsFound = true;
}
}
}
}
if(MatchedGroup != -1 && MatchedLayer != -1)
{
if(!IsFound)
s_Selected = 1;
SelectLayer(MatchedLayer, MatchedGroup);
return true;
}
}
else
s_CtrlClick = false;
return false;
2008-01-12 12:27:55 +00:00
}
void CEditor::ReplaceImage(const char *pFileName, int StorageType, void *pUser)
2008-01-19 12:32:08 +00:00
{
2010-05-29 07:25:38 +00:00
CEditor *pEditor = (CEditor *)pUser;
CEditorImage ImgInfo(pEditor);
if(!pEditor->Graphics()->LoadPNG(&ImgInfo, pFileName, StorageType))
2008-01-19 12:32:08 +00:00
return;
2010-05-29 07:25:38 +00:00
CEditorImage *pImg = pEditor->m_Map.m_vpImages[pEditor->m_SelectedImage];
pEditor->Graphics()->UnloadTexture(&(pImg->m_Texture));
free(pImg->m_pData);
pImg->m_pData = nullptr;
2010-05-29 07:25:38 +00:00
*pImg = ImgInfo;
IStorage::StripPathAndExtension(pFileName, pImg->m_aName, sizeof(pImg->m_aName));
2019-07-17 01:00:09 +00:00
pImg->m_External = IsVanillaImage(pImg->m_aName);
if(!pImg->m_External && g_Config.m_ClEditorDilate == 1 && pImg->m_Format == CImageInfo::FORMAT_RGBA)
{
int ColorChannelCount = 0;
if(ImgInfo.m_Format == CImageInfo::FORMAT_RGBA)
ColorChannelCount = 4;
DilateImage((unsigned char *)ImgInfo.m_pData, ImgInfo.m_Width, ImgInfo.m_Height, ColorChannelCount);
}
pImg->m_AutoMapper.Load(pImg->m_aName);
int TextureLoadFlag = pEditor->Graphics()->HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE;
if(ImgInfo.m_Width % 16 != 0 || ImgInfo.m_Height % 16 != 0)
TextureLoadFlag = 0;
pImg->m_Texture = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, TextureLoadFlag, pFileName);
ImgInfo.m_pData = nullptr;
2010-06-02 16:12:32 +00:00
pEditor->SortImages();
for(size_t i = 0; i < pEditor->m_Map.m_vpImages.size(); ++i)
{
if(!str_comp(pEditor->m_Map.m_vpImages[i]->m_aName, pImg->m_aName))
2011-08-11 08:59:14 +00:00
pEditor->m_SelectedImage = i;
}
pEditor->m_Dialog = DIALOG_NONE;
2008-01-19 12:32:08 +00:00
}
void CEditor::AddImage(const char *pFileName, int StorageType, void *pUser)
2008-01-13 16:30:30 +00:00
{
2010-05-29 07:25:38 +00:00
CEditor *pEditor = (CEditor *)pUser;
CEditorImage ImgInfo(pEditor);
if(!pEditor->Graphics()->LoadPNG(&ImgInfo, pFileName, StorageType))
2008-01-13 16:30:30 +00:00
return;
// check if we have that image already
char aBuf[128];
IStorage::StripPathAndExtension(pFileName, aBuf, sizeof(aBuf));
for(const auto &pImage : pEditor->m_Map.m_vpImages)
2010-05-29 07:25:38 +00:00
{
if(!str_comp(pImage->m_aName, aBuf))
2011-08-11 08:59:14 +00:00
return;
2010-05-29 07:25:38 +00:00
}
if(pEditor->m_Map.m_vpImages.size() >= 64) // hard limit for teeworlds
{
pEditor->m_PopupEventType = pEditor->POPEVENT_IMAGE_MAX;
pEditor->m_PopupEventActivated = true;
return;
}
CEditorImage *pImg = new CEditorImage(pEditor);
*pImg = ImgInfo;
pImg->m_External = IsVanillaImage(aBuf);
if(!pImg->m_External && g_Config.m_ClEditorDilate == 1 && pImg->m_Format == CImageInfo::FORMAT_RGBA)
{
int ColorChannelCount = 0;
if(ImgInfo.m_Format == CImageInfo::FORMAT_RGBA)
ColorChannelCount = 4;
DilateImage((unsigned char *)ImgInfo.m_pData, ImgInfo.m_Width, ImgInfo.m_Height, ColorChannelCount);
}
int TextureLoadFlag = pEditor->Graphics()->HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE;
if(ImgInfo.m_Width % 16 != 0 || ImgInfo.m_Height % 16 != 0)
TextureLoadFlag = 0;
pImg->m_Texture = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, TextureLoadFlag, pFileName);
ImgInfo.m_pData = nullptr;
str_copy(pImg->m_aName, aBuf, sizeof(pImg->m_aName));
pImg->m_AutoMapper.Load(pImg->m_aName);
pEditor->m_Map.m_vpImages.push_back(pImg);
2010-06-02 16:12:32 +00:00
pEditor->SortImages();
if(pEditor->m_SelectedImage >= 0 && (size_t)pEditor->m_SelectedImage < pEditor->m_Map.m_vpImages.size())
{
for(int i = 0; i <= pEditor->m_SelectedImage; ++i)
if(!str_comp(pEditor->m_Map.m_vpImages[i]->m_aName, aBuf))
{
pEditor->m_SelectedImage++;
break;
}
}
pEditor->m_Dialog = DIALOG_NONE;
2008-01-13 16:30:30 +00:00
}
void CEditor::AddSound(const char *pFileName, int StorageType, void *pUser)
{
CEditor *pEditor = (CEditor *)pUser;
2014-10-24 20:13:46 +00:00
// check if we have that sound already
char aBuf[128];
IStorage::StripPathAndExtension(pFileName, aBuf, sizeof(aBuf));
for(const auto &pSound : pEditor->m_Map.m_vpSounds)
{
if(!str_comp(pSound->m_aName, aBuf))
return;
}
2014-10-11 12:50:16 +00:00
// load external
void *pData;
unsigned DataSize;
if(!pEditor->Storage()->ReadFile(pFileName, StorageType, &pData, &DataSize))
2014-10-24 23:07:24 +00:00
{
dbg_msg("sound/opus", "failed to open file. filename='%s'", pFileName);
2014-10-11 12:50:16 +00:00
return;
2014-10-24 23:07:24 +00:00
}
2014-10-11 12:50:16 +00:00
// load sound
int SoundId = pEditor->Sound()->LoadOpusFromMem(pData, (unsigned)DataSize, true);
if(SoundId == -1)
{
free(pData);
return;
}
// add sound
2014-10-11 11:38:45 +00:00
CEditorSound *pSound = new CEditorSound(pEditor);
pSound->m_SoundID = SoundId;
pSound->m_DataSize = DataSize;
2014-10-11 12:50:16 +00:00
pSound->m_pData = pData;
str_copy(pSound->m_aName, aBuf, sizeof(pSound->m_aName));
pEditor->m_Map.m_vpSounds.push_back(pSound);
if(pEditor->m_SelectedSound >= 0 && (size_t)pEditor->m_SelectedSound < pEditor->m_Map.m_vpSounds.size())
{
for(int i = 0; i <= pEditor->m_SelectedSound; ++i)
if(!str_comp(pEditor->m_Map.m_vpSounds[i]->m_aName, aBuf))
{
pEditor->m_SelectedSound++;
break;
}
}
2015-07-09 00:08:14 +00:00
pEditor->m_Dialog = DIALOG_NONE;
}
2014-11-09 13:58:28 +00:00
void CEditor::ReplaceSound(const char *pFileName, int StorageType, void *pUser)
{
CEditor *pEditor = (CEditor *)pUser;
2015-07-09 00:08:14 +00:00
2014-11-09 13:58:28 +00:00
// load external
void *pData;
unsigned DataSize;
if(!pEditor->Storage()->ReadFile(pFileName, StorageType, &pData, &DataSize))
2014-11-09 13:58:28 +00:00
{
dbg_msg("sound/opus", "failed to open file. filename='%s'", pFileName);
return;
}
CEditorSound *pSound = pEditor->m_Map.m_vpSounds[pEditor->m_SelectedSound];
2014-11-09 13:58:28 +00:00
// unload sample
pEditor->Sound()->UnloadSample(pSound->m_SoundID);
free(pSound->m_pData);
pSound->m_pData = nullptr;
2014-11-09 13:58:28 +00:00
// replace sound
IStorage::StripPathAndExtension(pFileName, pSound->m_aName, sizeof(pSound->m_aName));
pSound->m_SoundID = pEditor->Sound()->LoadOpusFromMem(pData, (unsigned)DataSize, true);
2014-11-09 13:58:28 +00:00
pSound->m_pData = pData;
pSound->m_DataSize = DataSize;
pEditor->m_Dialog = DIALOG_NONE;
}
2010-05-29 07:25:38 +00:00
static int gs_ModifyIndexDeletedIndex;
static void ModifyIndexDeleted(int *pIndex)
2008-01-19 12:32:08 +00:00
{
2010-05-29 07:25:38 +00:00
if(*pIndex == gs_ModifyIndexDeletedIndex)
*pIndex = -1;
else if(*pIndex > gs_ModifyIndexDeletedIndex)
*pIndex = *pIndex - 1;
2008-01-19 12:32:08 +00:00
}
2020-02-28 15:25:27 +00:00
int CEditor::PopupImage(CEditor *pEditor, CUIRect View, void *pContext)
2008-01-13 22:03:32 +00:00
{
static int s_ReaddButton = 0;
2010-05-29 07:25:38 +00:00
static int s_ReplaceButton = 0;
static int s_RemoveButton = 0;
2008-01-13 22:03:32 +00:00
2010-05-29 07:25:38 +00:00
CUIRect Slot;
View.HSplitTop(12.0f, &Slot, &View);
CEditorImage *pImg = pEditor->m_Map.m_vpImages[pEditor->m_SelectedImage];
2010-05-29 07:25:38 +00:00
static int s_ExternalButton = 0;
if(pImg->m_External)
{
2011-03-20 16:04:03 +00:00
if(pEditor->DoButton_MenuItem(&s_ExternalButton, "Embed", 0, &Slot, 0, "Embeds the image into the map file."))
{
2010-05-29 07:25:38 +00:00
pImg->m_External = 0;
return 1;
}
View.HSplitTop(5.0f, nullptr, &View);
View.HSplitTop(12.0f, &Slot, &View);
}
else if(IsVanillaImage(pImg->m_aName))
2010-05-29 07:25:38 +00:00
{
2011-03-20 16:04:03 +00:00
if(pEditor->DoButton_MenuItem(&s_ExternalButton, "Make external", 0, &Slot, 0, "Removes the image from the map file."))
{
2010-05-29 07:25:38 +00:00
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())
{
static SMessagePopupContext s_MessagePopupContext;
s_MessagePopupContext.ErrorColor();
str_format(s_MessagePopupContext.m_aMessage, sizeof(s_MessagePopupContext.m_aMessage), "Error: could not find image '%s' in the mapres folder.", aFilename);
pEditor->ShowPopupMessage(pEditor->UI()->MouseX(), pEditor->UI()->MouseY(), &s_MessagePopupContext);
}
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);
2011-03-20 16:04:03 +00:00
if(pEditor->DoButton_MenuItem(&s_ReplaceButton, "Replace", 0, &Slot, 0, "Replaces the image with a new one"))
2008-01-13 22:03:32 +00:00
{
2011-03-20 16:04:03 +00:00
pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Replace Image", "Replace", "mapres", "", ReplaceImage, pEditor);
2008-01-13 22:03:32 +00:00
return 1;
}
View.HSplitTop(5.0f, nullptr, &View);
2010-05-29 07:25:38 +00:00
View.HSplitTop(12.0f, &Slot, &View);
2011-03-20 16:04:03 +00:00
if(pEditor->DoButton_MenuItem(&s_RemoveButton, "Remove", 0, &Slot, 0, "Removes the image from the map"))
2008-01-13 22:03:32 +00:00
{
2010-05-29 07:25:38 +00:00
delete pImg;
pEditor->m_Map.m_vpImages.erase(pEditor->m_Map.m_vpImages.begin() + pEditor->m_SelectedImage);
2010-05-29 07:25:38 +00:00
gs_ModifyIndexDeletedIndex = pEditor->m_SelectedImage;
pEditor->m_Map.ModifyImageIndex(ModifyIndexDeleted);
2008-01-13 22:03:32 +00:00
return 1;
}
return 0;
}
2020-02-28 15:25:27 +00:00
int CEditor::PopupSound(CEditor *pEditor, CUIRect View, void *pContext)
{
static int s_ReaddButton = 0;
2014-11-09 13:58:28 +00:00
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())
{
static SMessagePopupContext s_MessagePopupContext;
s_MessagePopupContext.ErrorColor();
str_format(s_MessagePopupContext.m_aMessage, sizeof(s_MessagePopupContext.m_aMessage), "Error: could not find sound '%s' in the mapres folder.", aFilename);
pEditor->ShowPopupMessage(pEditor->UI()->MouseX(), pEditor->UI()->MouseY(), &s_MessagePopupContext);
}
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"))
{
2014-11-09 13:58:28 +00:00
pEditor->InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_SOUND, "Replace sound", "Replace", "mapres", "", ReplaceSound, pEditor);
return 1;
2014-11-09 13:58:28 +00:00
}
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;
2014-10-11 11:38:45 +00:00
pEditor->m_Map.ModifySoundIndex(ModifyIndexDeleted);
return 1;
}
return 0;
}
void CEditor::SelectGameLayer()
{
for(size_t g = 0; g < m_Map.m_vpGroups.size(); g++)
{
for(size_t i = 0; i < m_Map.m_vpGroups[g]->m_vpLayers.size(); i++)
{
if(m_Map.m_vpGroups[g]->m_vpLayers[i] == m_Map.m_pGameLayer)
{
SelectLayer(i, g);
return;
}
}
}
}
2020-10-09 20:49:52 +00:00
static bool ImageNameLess(const CEditorImage *const &a, const CEditorImage *const &b)
2010-06-02 16:12:32 +00:00
{
2020-10-09 20:49:52 +00:00
return str_comp(a->m_aName, b->m_aName) < 0;
2010-06-02 16:12:32 +00:00
}
static int *gs_pSortedIndex = nullptr;
2010-06-02 16:12:32 +00:00
static void ModifySortedIndex(int *pIndex)
{
2020-10-09 20:49:52 +00:00
if(*pIndex >= 0)
2010-06-02 16:12:32 +00:00
*pIndex = gs_pSortedIndex[*pIndex];
}
void CEditor::SortImages()
{
if(!std::is_sorted(m_Map.m_vpImages.begin(), m_Map.m_vpImages.end(), ImageNameLess))
2010-06-02 16:12:32 +00:00
{
std::vector<CEditorImage *> vpTemp = m_Map.m_vpImages;
gs_pSortedIndex = new int[vpTemp.size()];
2010-06-02 16:12:32 +00:00
std::sort(m_Map.m_vpImages.begin(), m_Map.m_vpImages.end(), ImageNameLess);
for(size_t OldIndex = 0; OldIndex < vpTemp.size(); OldIndex++)
2020-10-09 20:49:52 +00:00
{
for(size_t NewIndex = 0; NewIndex < m_Map.m_vpImages.size(); NewIndex++)
2020-10-09 20:49:52 +00:00
{
if(vpTemp[OldIndex] == m_Map.m_vpImages[NewIndex])
2020-10-09 20:49:52 +00:00
{
2010-06-02 16:12:32 +00:00
gs_pSortedIndex[OldIndex] = NewIndex;
2020-10-09 20:49:52 +00:00
break;
}
}
}
2010-06-02 16:12:32 +00:00
m_Map.ModifyImageIndex(ModifySortedIndex);
delete[] gs_pSortedIndex;
gs_pSortedIndex = nullptr;
2010-06-02 16:12:32 +00:00
}
}
2011-08-11 08:59:14 +00:00
void CEditor::RenderImagesList(CUIRect ToolBox)
2008-01-12 12:27:55 +00:00
{
const float RowHeight = 12.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 = RowHeight * 5;
s_ScrollRegion.Begin(&ToolBox, &ScrollOffset, &ScrollParams);
ToolBox.y += ScrollOffset.y;
bool ScrollToSelection = false;
if(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && !m_Map.m_vpImages.empty())
{
if(Input()->KeyPress(KEY_DOWN))
{
int OldImage = m_SelectedImage;
m_SelectedImage = clamp(m_SelectedImage, 0, (int)m_Map.m_vpImages.size() - 1);
for(size_t i = m_SelectedImage + 1; i < m_Map.m_vpImages.size(); i++)
{
if(m_Map.m_vpImages[i]->m_External == m_Map.m_vpImages[m_SelectedImage]->m_External)
{
m_SelectedImage = i;
break;
}
}
if(m_SelectedImage == OldImage && !m_Map.m_vpImages[m_SelectedImage]->m_External)
{
for(size_t i = 0; i < m_Map.m_vpImages.size(); i++)
{
if(m_Map.m_vpImages[i]->m_External)
{
m_SelectedImage = i;
break;
}
}
}
ScrollToSelection = OldImage != m_SelectedImage;
}
else if(Input()->KeyPress(KEY_UP))
{
int OldImage = m_SelectedImage;
m_SelectedImage = clamp(m_SelectedImage, 0, (int)m_Map.m_vpImages.size() - 1);
for(int i = m_SelectedImage - 1; i >= 0; i--)
{
if(m_Map.m_vpImages[i]->m_External == m_Map.m_vpImages[m_SelectedImage]->m_External)
{
m_SelectedImage = i;
break;
}
}
if(m_SelectedImage == OldImage && m_Map.m_vpImages[m_SelectedImage]->m_External)
{
for(int i = (int)m_Map.m_vpImages.size() - 1; i >= 0; i--)
{
if(!m_Map.m_vpImages[i]->m_External)
{
m_SelectedImage = i;
break;
}
}
}
ScrollToSelection = OldImage != m_SelectedImage;
}
}
for(int e = 0; e < 2; e++) // two passes, first embedded, then external
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
CUIRect Slot;
ToolBox.HSplitTop(RowHeight + 3.0f, &Slot, &ToolBox);
if(s_ScrollRegion.AddRect(Slot))
UI()->DoLabel(&Slot, e == 0 ? "Embedded" : "External", 12.0f, TEXTALIGN_CENTER);
2010-05-29 07:25:38 +00:00
for(int i = 0; i < (int)m_Map.m_vpImages.size(); i++)
2008-01-13 22:03:32 +00:00
{
if((e && !m_Map.m_vpImages[i]->m_External) ||
(!e && m_Map.m_vpImages[i]->m_External))
{
continue;
}
2010-05-29 07:25:38 +00:00
ToolBox.HSplitTop(RowHeight + 2.0f, &Slot, &ToolBox);
int Selected = m_SelectedImage == i;
if(!s_ScrollRegion.AddRect(Slot, Selected && ScrollToSelection))
continue;
Slot.HSplitTop(RowHeight, &Slot, nullptr);
const bool ImageUsed = std::any_of(m_Map.m_vpGroups.cbegin(), m_Map.m_vpGroups.cend(), [i](const auto &pGroup) {
return std::any_of(pGroup->m_vpLayers.cbegin(), pGroup->m_vpLayers.cend(), [i](const auto &pLayer) {
if(pLayer->m_Type == LAYERTYPE_QUADS)
return static_cast<CLayerQuads *>(pLayer)->m_Image == i;
else if(pLayer->m_Type == LAYERTYPE_TILES)
return static_cast<CLayerTiles *>(pLayer)->m_Image == i;
return false;
});
});
if(!ImageUsed)
Selected += 2; // Image is unused
if(Selected < 2 && e == 1)
{
if(!IsVanillaImage(m_Map.m_vpImages[i]->m_aName))
{
Selected += 4; // Image should be embedded
}
}
float FontSize = 10.0f;
while(TextRender()->TextWidth(FontSize, m_Map.m_vpImages[i]->m_aName, -1, -1.0f) > Slot.w)
FontSize--;
if(int Result = DoButton_Ex(&m_Map.m_vpImages[i], m_Map.m_vpImages[i]->m_aName, Selected, &Slot,
BUTTON_CONTEXT, "Select image.", IGraphics::CORNER_ALL, FontSize))
{
2010-05-29 07:25:38 +00:00
m_SelectedImage = i;
static int s_PopupImageID = 0;
2010-05-29 07:25:38 +00:00
if(Result == 2)
{
CEditorImage *pImg = m_Map.m_vpImages[m_SelectedImage];
int Height;
if(pImg->m_External || IsVanillaImage(pImg->m_aName))
Height = 73;
else
Height = 56;
UiInvokePopupMenu(&s_PopupImageID, 0, UI()->MouseX(), UI()->MouseY(), 120, Height, PopupImage);
}
}
2007-12-02 17:55:45 +00:00
}
// separator
ToolBox.HSplitTop(5.0f, &Slot, &ToolBox);
if(s_ScrollRegion.AddRect(Slot))
{
IGraphics::CLineItem LineItem(Slot.x, Slot.y + Slot.h / 2, Slot.x + Slot.w, Slot.y + Slot.h / 2);
Graphics()->TextureClear();
Graphics()->LinesBegin();
Graphics()->LinesDraw(&LineItem, 1);
Graphics()->LinesEnd();
}
}
// new image
static int s_AddImageButton = 0;
CUIRect AddImageButton;
ToolBox.HSplitTop(5.0f + RowHeight + 1.0f, &AddImageButton, &ToolBox);
if(s_ScrollRegion.AddRect(AddImageButton))
{
AddImageButton.HSplitTop(5.0f, nullptr, &AddImageButton);
AddImageButton.HSplitTop(RowHeight, &AddImageButton, nullptr);
if(DoButton_Editor(&s_AddImageButton, "Add", 0, &AddImageButton, 0, "Load a new image to use in the map"))
InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Add Image", "Add", "mapres", "", AddImage, this);
}
s_ScrollRegion.End();
}
2010-06-14 01:18:44 +00:00
void CEditor::RenderSelectedImage(CUIRect View)
{
if(m_SelectedImage < 0 || (size_t)m_SelectedImage >= m_Map.m_vpImages.size())
return;
2008-01-13 16:30:30 +00:00
View.Margin(10.0f, &View);
if(View.h < View.w)
View.w = View.h;
else
View.h = View.w;
float Max = maximum<float>(m_Map.m_vpImages[m_SelectedImage]->m_Width, m_Map.m_vpImages[m_SelectedImage]->m_Height);
View.w *= m_Map.m_vpImages[m_SelectedImage]->m_Width / Max;
View.h *= m_Map.m_vpImages[m_SelectedImage]->m_Height / Max;
Graphics()->TextureSet(m_Map.m_vpImages[m_SelectedImage]->m_Texture);
Graphics()->BlendNormal();
Graphics()->WrapClamp();
Graphics()->QuadsBegin();
IGraphics::CQuadItem QuadItem(View.x, View.y, View.w, View.h);
Graphics()->QuadsDrawTL(&QuadItem, 1);
Graphics()->QuadsEnd();
Graphics()->WrapNormal();
2010-05-29 07:25:38 +00:00
}
void CEditor::RenderSounds(CUIRect ToolBox)
{
const float RowHeight = 12.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 = RowHeight * 5;
s_ScrollRegion.Begin(&ToolBox, &ScrollOffset, &ScrollParams);
ToolBox.y += ScrollOffset.y;
bool ScrollToSelection = false;
if(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && !m_Map.m_vpSounds.empty())
{
if(Input()->KeyPress(KEY_DOWN))
{
m_SelectedSound = (m_SelectedSound + 1) % m_Map.m_vpSounds.size();
ScrollToSelection = true;
}
else if(Input()->KeyPress(KEY_UP))
{
m_SelectedSound = (m_SelectedSound + m_Map.m_vpSounds.size() - 1) % m_Map.m_vpSounds.size();
ScrollToSelection = true;
}
}
CUIRect Slot;
ToolBox.HSplitTop(RowHeight + 3.0f, &Slot, &ToolBox);
if(s_ScrollRegion.AddRect(Slot))
UI()->DoLabel(&Slot, "Embedded", 12.0f, TEXTALIGN_CENTER);
for(int i = 0; i < (int)m_Map.m_vpSounds.size(); i++)
{
ToolBox.HSplitTop(RowHeight + 2.0f, &Slot, &ToolBox);
int Selected = m_SelectedSound == i;
if(!s_ScrollRegion.AddRect(Slot, Selected && ScrollToSelection))
continue;
Slot.HSplitTop(RowHeight, &Slot, nullptr);
const bool SoundUsed = std::any_of(m_Map.m_vpGroups.cbegin(), m_Map.m_vpGroups.cend(), [i](const auto &pGroup) {
return std::any_of(pGroup->m_vpLayers.cbegin(), pGroup->m_vpLayers.cend(), [i](const auto &pLayer) {
if(pLayer->m_Type == LAYERTYPE_SOUNDS)
return static_cast<CLayerSounds *>(pLayer)->m_Sound == i;
return false;
});
});
if(!SoundUsed)
Selected += 2; // Sound is unused
float FontSize = 10.0f;
while(TextRender()->TextWidth(FontSize, m_Map.m_vpSounds[i]->m_aName, -1, -1.0f) > Slot.w)
FontSize--;
if(int Result = DoButton_Ex(&m_Map.m_vpSounds[i], m_Map.m_vpSounds[i]->m_aName, Selected, &Slot,
BUTTON_CONTEXT, "Select sound.", IGraphics::CORNER_ALL, FontSize))
{
m_SelectedSound = i;
static int s_PopupSoundID = 0;
if(Result == 2)
UiInvokePopupMenu(&s_PopupSoundID, 0, UI()->MouseX(), UI()->MouseY(), 120, 56, PopupSound);
}
}
// separator
ToolBox.HSplitTop(5.0f, &Slot, &ToolBox);
if(s_ScrollRegion.AddRect(Slot))
{
IGraphics::CLineItem LineItem(Slot.x, Slot.y + Slot.h / 2, Slot.x + Slot.w, Slot.y + Slot.h / 2);
Graphics()->TextureClear();
Graphics()->LinesBegin();
Graphics()->LinesDraw(&LineItem, 1);
Graphics()->LinesEnd();
}
// new sound
static int s_AddSoundButton = 0;
CUIRect AddSoundButton;
ToolBox.HSplitTop(5.0f + RowHeight + 1.0f, &AddSoundButton, &ToolBox);
if(s_ScrollRegion.AddRect(AddSoundButton))
{
AddSoundButton.HSplitTop(5.0f, nullptr, &AddSoundButton);
AddSoundButton.HSplitTop(RowHeight, &AddSoundButton, nullptr);
if(DoButton_Editor(&s_AddSoundButton, "Add", 0, &AddSoundButton, 0, "Load a new sound to use in the map"))
InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_SOUND, "Add Sound", "Add", "mapres", "", AddSound, this);
}
s_ScrollRegion.End();
}
2023-03-12 17:54:57 +00:00
static int EditorListdirCallback(const CFsFileInfo *pInfo, int IsDir, int StorageType, void *pUser)
2008-01-12 12:27:55 +00:00
{
CEditor *pEditor = (CEditor *)pUser;
2023-03-12 17:54:57 +00:00
if((pInfo->m_pName[0] == '.' && (pInfo->m_pName[1] == 0 ||
(pInfo->m_pName[1] == '.' && pInfo->m_pName[2] == 0 && (!str_comp(pEditor->m_pFileDialogPath, "maps") || !str_comp(pEditor->m_pFileDialogPath, "mapres"))))) ||
(!IsDir && ((pEditor->m_FileDialogFileType == CEditor::FILETYPE_MAP && !str_endswith(pInfo->m_pName, ".map")) ||
(pEditor->m_FileDialogFileType == CEditor::FILETYPE_IMG && !str_endswith(pInfo->m_pName, ".png")) ||
(pEditor->m_FileDialogFileType == CEditor::FILETYPE_SOUND && !str_endswith(pInfo->m_pName, ".opus")))))
return 0;
2010-05-29 07:25:38 +00:00
2010-10-06 21:07:35 +00:00
CEditor::CFilelistItem Item;
2023-03-12 17:54:57 +00:00
str_copy(Item.m_aFilename, pInfo->m_pName, sizeof(Item.m_aFilename));
2010-10-06 21:07:35 +00:00
if(IsDir)
2023-03-12 17:54:57 +00:00
str_format(Item.m_aName, sizeof(Item.m_aName), "%s/", pInfo->m_pName);
2010-10-06 21:07:35 +00:00
else
2014-10-11 11:38:45 +00:00
{
2020-10-26 11:56:21 +00:00
int LenEnding = pEditor->m_FileDialogFileType == CEditor::FILETYPE_SOUND ? 5 : 4;
2023-03-12 17:54:57 +00:00
str_truncate(Item.m_aName, sizeof(Item.m_aName), pInfo->m_pName, str_length(pInfo->m_pName) - LenEnding);
2014-10-11 11:38:45 +00:00
}
2010-10-06 21:07:35 +00:00
Item.m_IsDir = IsDir != 0;
Item.m_IsLink = false;
2010-10-06 21:07:35 +00:00
Item.m_StorageType = StorageType;
2023-03-12 17:54:57 +00:00
Item.m_TimeModified = pInfo->m_TimeModified;
pEditor->m_vCompleteFileList.push_back(Item);
return 0;
}
void CEditor::SortFilteredFileList()
{
if(m_SortByFilename == 1)
{
2023-03-12 18:41:58 +00:00
std::sort(m_vpFilteredFileList.begin(), m_vpFilteredFileList.end(), CEditor::CompareFilenameAscending);
}
else
{
2023-03-12 18:41:58 +00:00
std::sort(m_vpFilteredFileList.begin(), m_vpFilteredFileList.end(), CEditor::CompareFilenameDescending);
}
if(m_SortByTimeModified == 1)
{
2023-03-12 18:41:58 +00:00
std::stable_sort(m_vpFilteredFileList.begin(), m_vpFilteredFileList.end(), CEditor::CompareTimeModifiedAscending);
}
else if(m_SortByTimeModified == -1)
{
2023-03-12 18:41:58 +00:00
std::stable_sort(m_vpFilteredFileList.begin(), m_vpFilteredFileList.end(), CEditor::CompareTimeModifiedDescending);
}
}
2010-05-29 07:25:38 +00:00
void CEditor::RenderFileDialog()
2008-01-12 12:27:55 +00:00
{
// GUI coordsys
2021-09-22 19:48:55 +00:00
UI()->MapScreen();
2010-05-29 07:25:38 +00:00
CUIRect View = *UI()->Screen();
2014-03-28 21:47:57 +00:00
CUIRect Preview;
float Width = View.w, Height = View.h;
View.Draw(ColorRGBA(0, 0, 0, 0.25f), 0, 0);
2010-05-29 07:25:38 +00:00
View.VMargin(150.0f, &View);
View.HMargin(50.0f, &View);
View.Draw(ColorRGBA(0, 0, 0, 0.75f), IGraphics::CORNER_ALL, 5.0f);
2010-05-29 07:25:38 +00:00
View.Margin(10.0f, &View);
CUIRect Title, FileBox, FileBoxLabel, ButtonBar, PathBox;
2010-05-29 07:25:38 +00:00
View.HSplitTop(18.0f, &Title, &View);
View.HSplitTop(5.0f, nullptr, &View); // some spacing
2010-05-29 07:25:38 +00:00
View.HSplitBottom(14.0f, &View, &ButtonBar);
View.HSplitBottom(10.0f, &View, nullptr); // some spacing
View.HSplitBottom(14.0f, &View, &PathBox);
View.HSplitBottom(5.0f, &View, nullptr); // some spacing
2010-05-29 07:25:38 +00:00
View.HSplitBottom(14.0f, &View, &FileBox);
FileBox.VSplitLeft(55.0f, &FileBoxLabel, &FileBox);
View.HSplitBottom(10.0f, &View, nullptr); // some spacing
if(m_FileDialogFileType == CEditor::FILETYPE_IMG)
2014-03-28 21:47:57 +00:00
View.VSplitMid(&View, &Preview);
2010-05-29 07:25:38 +00:00
2008-01-13 16:30:30 +00:00
// title
CUIRect ButtonTimeModified, ButtonFileName;
Title.VSplitRight(10.0f, &Title, nullptr);
Title.VSplitRight(90.0f, &Title, &ButtonTimeModified);
Title.VSplitRight(10.0f, &Title, nullptr);
Title.VSplitRight(90.0f, &Title, &ButtonFileName);
Title.VSplitRight(10.0f, &Title, nullptr);
2023-03-12 18:57:46 +00:00
const char *aSortIndicator[3] = {"", "", ""};
static int s_ButtonTimeModified = 0;
char aBufLabelButtonTimeModified[64];
2023-03-12 18:41:58 +00:00
str_format(aBufLabelButtonTimeModified, sizeof(aBufLabelButtonTimeModified), "Time modified %s", aSortIndicator[m_SortByTimeModified + 1]);
if(DoButton_Editor(&s_ButtonTimeModified, aBufLabelButtonTimeModified, 0, &ButtonTimeModified, 0, "Sort by time modified"))
{
if(m_SortByTimeModified == 1)
{
m_SortByTimeModified = -1;
}
else if(m_SortByTimeModified == -1)
{
m_SortByTimeModified = 0;
}
else
{
m_SortByTimeModified = 1;
}
2023-03-14 16:17:24 +00:00
RefreshFilteredFileList();
}
static int s_ButtonFileName = 0;
char aBufLabelButtonFilename[64];
2023-03-12 18:41:58 +00:00
str_format(aBufLabelButtonFilename, sizeof(aBufLabelButtonFilename), "Filename %s", aSortIndicator[m_SortByFilename + 1]);
if(DoButton_Editor(&s_ButtonFileName, aBufLabelButtonFilename, 0, &ButtonFileName, 0, "Sort by file name"))
{
if(m_SortByFilename == 1)
{
m_SortByFilename = -1;
2023-03-14 16:17:24 +00:00
m_SortByTimeModified = 0;
}
else
{
m_SortByFilename = 1;
2023-03-14 16:17:24 +00:00
m_SortByTimeModified = 0;
}
2023-03-14 16:17:24 +00:00
RefreshFilteredFileList();
}
Title.Draw(ColorRGBA(1, 1, 1, 0.25f), IGraphics::CORNER_ALL, 4.0f);
2010-05-29 07:25:38 +00:00
Title.VMargin(10.0f, &Title);
2022-03-11 16:34:48 +00:00
UI()->DoLabel(&Title, m_pFileDialogTitle, 12.0f, TEXTALIGN_LEFT);
2010-05-29 07:25:38 +00:00
// pathbox
char aPath[IO_MAX_PATH_LENGTH], aBuf[128 + IO_MAX_PATH_LENGTH];
if(m_FilesSelectedIndex != -1)
Storage()->GetCompletePath(m_vpFilteredFileList[m_FilesSelectedIndex]->m_StorageType, m_pFileDialogPath, aPath, sizeof(aPath));
else
aPath[0] = 0;
str_format(aBuf, sizeof(aBuf), "Current path: %s", aPath);
2022-03-11 16:34:48 +00:00
UI()->DoLabel(&PathBox, aBuf, 10.0f, TEXTALIGN_LEFT);
2017-02-21 16:10:08 +00:00
// filebox
static CListBox s_ListBox;
static bool s_ListBoxUsed = false;
s_ListBoxUsed = !UiPopupOpen();
if(m_FileDialogStorageType == IStorage::TYPE_SAVE)
{
2022-03-11 16:34:48 +00:00
UI()->DoLabel(&FileBoxLabel, "Filename:", 10.0f, TEXTALIGN_LEFT);
static float s_FileBoxID = 0;
2011-07-12 01:14:46 +00:00
if(DoEditBox(&s_FileBoxID, &FileBox, m_aFileDialogFileName, sizeof(m_aFileDialogFileName), 10.0f, &s_FileBoxID))
{
// remove '/' and '\'
for(int i = 0; m_aFileDialogFileName[i]; ++i)
{
if(m_aFileDialogFileName[i] == '/' || m_aFileDialogFileName[i] == '\\')
{
str_copy(&m_aFileDialogFileName[i], &m_aFileDialogFileName[i + 1], (int)(sizeof(m_aFileDialogFileName)) - i);
--i;
}
}
m_FilesSelectedIndex = -1;
m_aFilesSelectedName[0] = '\0';
// find first valid entry, if it exists
for(size_t i = 0; i < m_vpFilteredFileList.size(); i++)
{
if(str_comp_nocase(m_vpFilteredFileList[i]->m_aName, m_aFileDialogFileName) == 0)
{
m_FilesSelectedIndex = i;
str_copy(m_aFilesSelectedName, m_vpFilteredFileList[i]->m_aName);
break;
}
}
if(m_FilesSelectedIndex >= 0)
s_ListBox.ScrollToSelected();
}
if(m_FileDialogOpening)
UI()->SetActiveItem(&s_FileBoxID);
}
2016-08-20 23:47:39 +00:00
else
{
// render search bar
FileBox.VSplitRight(250, &FileBox, nullptr);
2016-08-20 23:47:39 +00:00
CUIRect ClearBox;
FileBox.VSplitRight(15, &FileBox, &ClearBox);
2017-02-21 16:10:08 +00:00
2022-03-11 16:34:48 +00:00
UI()->DoLabel(&FileBoxLabel, "Search:", 10.0f, TEXTALIGN_LEFT);
static float s_SearchBoxID = 0;
bool SearchUpdated = DoEditBox(&s_SearchBoxID, &FileBox, m_aFileDialogFilterString, sizeof(m_aFileDialogFilterString), 10.0f, &s_SearchBoxID, false, IGraphics::CORNER_L);
if(m_FileDialogOpening)
UI()->SetActiveItem(&s_SearchBoxID);
2017-02-21 16:10:08 +00:00
// clear search button
2016-08-20 23:47:39 +00:00
{
static int s_ClearButton = 0;
ClearBox.Draw(ColorRGBA(1, 1, 1, 0.33f * UI()->ButtonColorMul(&s_ClearButton)), IGraphics::CORNER_R, 3.0f);
UI()->DoLabel(&ClearBox, "×", 10.0f, TEXTALIGN_CENTER);
if(UI()->DoButtonLogic(&s_ClearButton, 0, &ClearBox))
2016-08-20 23:47:39 +00:00
{
SearchUpdated = true;
m_aFileDialogFilterString[0] = 0;
2016-08-20 23:47:39 +00:00
UI()->SetActiveItem(&s_SearchBoxID);
}
}
if(SearchUpdated)
{
RefreshFilteredFileList();
if(m_vpFilteredFileList.empty())
{
m_FilesSelectedIndex = -1;
}
else if(m_FilesSelectedIndex == -1 || (m_aFileDialogFilterString[0] && !str_find_nocase(m_vpFilteredFileList[m_FilesSelectedIndex]->m_aName, m_aFileDialogFilterString)))
{
// we need to refresh selection
m_FilesSelectedIndex = -1;
for(size_t i = 0; i < m_vpFilteredFileList.size(); i++)
{
if(str_find_nocase(m_vpFilteredFileList[i]->m_aName, m_aFileDialogFilterString))
{
m_FilesSelectedIndex = i;
break;
}
}
if(m_FilesSelectedIndex == -1)
{
// select first item
m_FilesSelectedIndex = 0;
}
}
if(m_FilesSelectedIndex >= 0)
str_copy(m_aFilesSelectedName, m_vpFilteredFileList[m_FilesSelectedIndex]->m_aName);
else
m_aFilesSelectedName[0] = '\0';
if(m_FilesSelectedIndex >= 0 && !m_vpFilteredFileList[m_FilesSelectedIndex]->m_IsDir)
str_copy(m_aFileDialogFileName, m_vpFilteredFileList[m_FilesSelectedIndex]->m_aFilename);
else
m_aFileDialogFileName[0] = '\0';
s_ListBox.ScrollToSelected();
}
}
m_FileDialogOpening = false;
2014-03-28 21:47:57 +00:00
if(m_FilesSelectedIndex > -1)
{
if(m_FileDialogFileType == CEditor::FILETYPE_IMG && !m_PreviewImageIsLoaded && m_FilesSelectedIndex > -1)
2014-03-28 21:47:57 +00:00
{
if(str_endswith(m_vpFilteredFileList[m_FilesSelectedIndex]->m_aFilename, ".png"))
2014-03-28 21:47:57 +00:00
{
char aBuffer[IO_MAX_PATH_LENGTH];
str_format(aBuffer, sizeof(aBuffer), "%s/%s", m_pFileDialogPath, m_vpFilteredFileList[m_FilesSelectedIndex]->m_aFilename);
if(Graphics()->LoadPNG(&m_FilePreviewImageInfo, aBuffer, IStorage::TYPE_ALL))
{
Graphics()->UnloadTexture(&m_FilePreviewImage);
2021-05-21 12:57:55 +00:00
m_FilePreviewImage = Graphics()->LoadTextureRaw(m_FilePreviewImageInfo.m_Width, m_FilePreviewImageInfo.m_Height, m_FilePreviewImageInfo.m_Format, m_FilePreviewImageInfo.m_pData, m_FilePreviewImageInfo.m_Format, 0);
Graphics()->FreePNG(&m_FilePreviewImageInfo);
m_PreviewImageIsLoaded = true;
}
2014-03-28 21:47:57 +00:00
}
}
if(m_FileDialogFileType == CEditor::FILETYPE_IMG && m_PreviewImageIsLoaded)
2014-03-28 21:47:57 +00:00
{
int w = m_FilePreviewImageInfo.m_Width;
int h = m_FilePreviewImageInfo.m_Height;
if(m_FilePreviewImageInfo.m_Width > Preview.w) // NOLINT(clang-analyzer-core.UndefinedBinaryOperatorResult)
2014-03-28 21:47:57 +00:00
{
h = m_FilePreviewImageInfo.m_Height * Preview.w / m_FilePreviewImageInfo.m_Width;
w = Preview.w;
}
if(h > Preview.h)
2014-03-28 21:47:57 +00:00
{
2022-05-28 00:15:42 +00:00
w = w * Preview.h / h;
2014-03-28 21:47:57 +00:00
h = Preview.h;
}
Graphics()->TextureSet(m_FilePreviewImage);
Graphics()->BlendNormal();
Graphics()->QuadsBegin();
IGraphics::CQuadItem QuadItem(Preview.x, Preview.y, w, h);
Graphics()->QuadsDrawTL(&QuadItem, 1);
Graphics()->QuadsEnd();
}
}
s_ListBox.DoStart(15.0f, m_vpFilteredFileList.size(), 1, 5, m_FilesSelectedIndex, &View, false, &s_ListBoxUsed);
for(size_t i = 0; i < m_vpFilteredFileList.size(); i++)
{
const CListboxItem Item = s_ListBox.DoNextItem(m_vpFilteredFileList[i], m_FilesSelectedIndex >= 0 && (size_t)m_FilesSelectedIndex == i, &s_ListBoxUsed);
if(!Item.m_Visible)
continue;
CUIRect Button, FileIcon, TimeModified;
Item.m_Rect.VSplitLeft(Item.m_Rect.h, &FileIcon, &Button);
Button.VSplitLeft(5.0f, nullptr, &Button);
Button.VSplitRight(100.0f, &Button, &TimeModified);
2010-05-29 07:25:38 +00:00
const char *pIconType;
if(!m_vpFilteredFileList[i]->m_IsDir)
{
switch(m_FileDialogFileType)
{
case FILETYPE_MAP:
pIconType = "\xEF\x89\xB9";
break;
case FILETYPE_IMG:
pIconType = "\xEF\x80\xBE";
break;
case FILETYPE_SOUND:
pIconType = "\xEF\x80\x81";
break;
default:
pIconType = "";
}
}
else
{
if(str_comp(m_vpFilteredFileList[i]->m_aFilename, "..") == 0)
pIconType = "\xEF\xA0\x82";
else
pIconType = "\xEF\x81\xBB";
}
2010-05-29 07:25:38 +00:00
TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_ICON_FONT));
UI()->DoLabel(&FileIcon, pIconType, 12.0f, TEXTALIGN_LEFT);
TextRender()->SetCurFont(nullptr);
2010-05-29 07:25:38 +00:00
char aBufTimeModified[64];
2023-03-12 17:54:57 +00:00
str_timestamp_ex(m_vpFilteredFileList[i]->m_TimeModified, aBufTimeModified, sizeof(aBufTimeModified), "%d.%m.%Y %H:%M");
SLabelProperties Props;
Props.m_AlignVertically = 0;
UI()->DoLabel(&Button, m_vpFilteredFileList[i]->m_aName, 10.0f, TEXTALIGN_LEFT, Props);
2023-03-12 18:41:58 +00:00
if(!m_vpFilteredFileList[i]->m_IsLink && str_comp(m_vpFilteredFileList[i]->m_aFilename, "..") != 0)
UI()->DoLabel(&TimeModified, aBufTimeModified, 10.0f, TEXTALIGN_RIGHT, Props);
}
2010-05-29 07:25:38 +00:00
const int NewSelection = s_ListBox.DoEnd();
if(NewSelection != m_FilesSelectedIndex)
{
m_FilesSelectedIndex = NewSelection;
str_copy(m_aFilesSelectedName, m_vpFilteredFileList[m_FilesSelectedIndex]->m_aName);
if(!m_vpFilteredFileList[m_FilesSelectedIndex]->m_IsDir)
str_copy(m_aFileDialogFileName, m_vpFilteredFileList[m_FilesSelectedIndex]->m_aFilename);
else
m_aFileDialogFileName[0] = '\0';
m_PreviewImageIsLoaded = false;
}
2010-05-29 07:25:38 +00:00
2008-01-13 16:30:30 +00:00
// the buttons
2010-05-29 07:25:38 +00:00
static int s_OkButton = 0;
static int s_CancelButton = 0;
static int s_RefreshButton = 0;
static int s_NewFolderButton = 0;
2011-07-12 21:31:39 +00:00
static int s_MapInfoButton = 0;
2008-01-13 16:30:30 +00:00
2010-05-29 07:25:38 +00:00
CUIRect Button;
ButtonBar.VSplitRight(50.0f, &ButtonBar, &Button);
bool IsDir = m_FilesSelectedIndex >= 0 && m_vpFilteredFileList[m_FilesSelectedIndex]->m_IsDir;
if(DoButton_Editor(&s_OkButton, IsDir ? "Open" : m_pFileDialogButtonText, 0, &Button, 0, nullptr) || s_ListBox.WasItemActivated())
2008-01-13 16:30:30 +00:00
{
if(IsDir) // folder
{
m_aFileDialogFilterString[0] = '\0';
if(str_comp(m_vpFilteredFileList[m_FilesSelectedIndex]->m_aFilename, "..") == 0) // parent folder
{
if(fs_parent_dir(m_pFileDialogPath))
m_pFileDialogPath = m_aFileDialogCurrentFolder; // leave the link
}
else // sub folder
{
if(m_vpFilteredFileList[m_FilesSelectedIndex]->m_IsLink)
{
m_pFileDialogPath = m_aFileDialogCurrentLink; // follow the link
str_copy(m_aFileDialogCurrentLink, m_vpFilteredFileList[m_FilesSelectedIndex]->m_aFilename, sizeof(m_aFileDialogCurrentLink));
}
else
{
char aTemp[IO_MAX_PATH_LENGTH];
str_copy(aTemp, m_pFileDialogPath, sizeof(aTemp));
str_format(m_pFileDialogPath, IO_MAX_PATH_LENGTH, "%s/%s", aTemp, m_vpFilteredFileList[m_FilesSelectedIndex]->m_aFilename);
}
}
FilelistPopulate(!str_comp(m_pFileDialogPath, "maps") || !str_comp(m_pFileDialogPath, "mapres") ? m_FileDialogStorageType :
m_vpFilteredFileList[m_FilesSelectedIndex]->m_StorageType);
if(m_FilesSelectedIndex >= 0 && !m_vpFilteredFileList[m_FilesSelectedIndex]->m_IsDir)
str_copy(m_aFileDialogFileName, m_vpFilteredFileList[m_FilesSelectedIndex]->m_aFilename, sizeof(m_aFileDialogFileName));
else
m_aFileDialogFileName[0] = 0;
}
else // file
{
str_format(m_aFileSaveName, sizeof(m_aFileSaveName), "%s/%s", m_pFileDialogPath, m_aFileDialogFileName);
if(!str_comp(m_pFileDialogButtonText, "Save"))
{
IOHANDLE File = Storage()->OpenFile(m_aFileSaveName, IOFLAG_READ, IStorage::TYPE_SAVE);
if(File)
{
io_close(File);
m_PopupEventType = POPEVENT_SAVE;
m_PopupEventActivated = true;
}
else if(m_pfnFileDialogFunc)
m_pfnFileDialogFunc(m_aFileSaveName, m_FilesSelectedIndex >= 0 ? m_vpFilteredFileList[m_FilesSelectedIndex]->m_StorageType : m_FileDialogStorageType, m_pFileDialogUser);
}
else if(m_pfnFileDialogFunc)
m_pfnFileDialogFunc(m_aFileSaveName, m_FilesSelectedIndex >= 0 ? m_vpFilteredFileList[m_FilesSelectedIndex]->m_StorageType : m_FileDialogStorageType, m_pFileDialogUser);
}
2008-01-13 16:30:30 +00:00
}
2010-05-29 07:25:38 +00:00
ButtonBar.VSplitRight(40.0f, &ButtonBar, &Button);
ButtonBar.VSplitRight(50.0f, &ButtonBar, &Button);
if(DoButton_Editor(&s_CancelButton, "Cancel", 0, &Button, 0, nullptr) || (s_ListBoxUsed && UI()->ConsumeHotkey(CUI::HOTKEY_ESCAPE)))
2010-05-29 07:25:38 +00:00
m_Dialog = DIALOG_NONE;
if(m_FileDialogStorageType == IStorage::TYPE_SAVE)
{
ButtonBar.VSplitLeft(70.0f, &Button, &ButtonBar);
if(DoButton_Editor(&s_NewFolderButton, "New folder", 0, &Button, 0, nullptr))
{
2021-12-03 17:01:37 +00:00
m_aFileDialogNewFolderName[0] = 0;
m_aFileDialogErrString[0] = 0;
static int s_NewFolderPopupID = 0;
UiInvokePopupMenu(&s_NewFolderPopupID, 0, Width / 2.0f - 200.0f, Height / 2.0f - 100.0f, 400.0f, 200.0f, PopupNewFolder);
UI()->SetActiveItem(nullptr);
}
}
2011-07-12 21:31:39 +00:00
ButtonBar.VSplitRight(40.0f, &ButtonBar, &Button);
ButtonBar.VSplitRight(50.0f, &ButtonBar, &Button);
if(DoButton_Editor(&s_RefreshButton, "Refresh", 0, &Button, 0, nullptr) || (s_ListBoxUsed && (Input()->KeyIsPressed(KEY_F5) || (Input()->ModifierIsPressed() && Input()->KeyIsPressed(KEY_R)))))
FilelistPopulate(m_FileDialogLastPopulatedStorageType, true);
2011-07-12 21:31:39 +00:00
if(m_FileDialogStorageType == IStorage::TYPE_SAVE)
{
ButtonBar.VSplitLeft(40.0f, nullptr, &ButtonBar);
2011-07-12 21:31:39 +00:00
ButtonBar.VSplitLeft(70.0f, &Button, &ButtonBar);
if(DoButton_Editor(&s_MapInfoButton, "Map details", 0, &Button, 0, nullptr))
2011-07-12 21:31:39 +00:00
{
str_copy(m_Map.m_MapInfo.m_aAuthorTmp, m_Map.m_MapInfo.m_aAuthor, sizeof(m_Map.m_MapInfo.m_aAuthorTmp));
str_copy(m_Map.m_MapInfo.m_aVersionTmp, m_Map.m_MapInfo.m_aVersion, sizeof(m_Map.m_MapInfo.m_aVersionTmp));
str_copy(m_Map.m_MapInfo.m_aCreditsTmp, m_Map.m_MapInfo.m_aCredits, sizeof(m_Map.m_MapInfo.m_aCreditsTmp));
str_copy(m_Map.m_MapInfo.m_aLicenseTmp, m_Map.m_MapInfo.m_aLicense, sizeof(m_Map.m_MapInfo.m_aLicenseTmp));
static int s_MapInfoPopupID = 0;
UiInvokePopupMenu(&s_MapInfoPopupID, 0, Width / 2.0f - 200.0f, Height / 2.0f - 100.0f, 400.0f, 200.0f, PopupMapInfo);
UI()->SetActiveItem(nullptr);
2011-07-12 21:31:39 +00:00
}
}
2008-01-13 16:30:30 +00:00
}
void CEditor::RefreshFilteredFileList()
{
m_vpFilteredFileList.clear();
for(const CFilelistItem &Item : m_vCompleteFileList)
{
if(!m_aFileDialogFilterString[0] || str_find_nocase(Item.m_aName, m_aFileDialogFilterString))
{
m_vpFilteredFileList.push_back(&Item);
}
}
2023-03-14 16:17:24 +00:00
SortFilteredFileList();
if(!m_vpFilteredFileList.empty())
{
if(m_aFilesSelectedName[0])
{
for(size_t i = 0; i < m_vpFilteredFileList.size(); i++)
{
if(m_aFilesSelectedName[0] && str_comp(m_vpFilteredFileList[i]->m_aName, m_aFilesSelectedName) == 0)
{
m_FilesSelectedIndex = i;
break;
}
}
}
m_FilesSelectedIndex = clamp<int>(m_FilesSelectedIndex, 0, m_vpFilteredFileList.size() - 1);
str_copy(m_aFilesSelectedName, m_vpFilteredFileList[m_FilesSelectedIndex]->m_aName);
}
else
{
m_FilesSelectedIndex = -1;
m_aFilesSelectedName[0] = '\0';
}
}
void CEditor::FilelistPopulate(int StorageType, bool KeepSelection)
2010-09-12 11:15:59 +00:00
{
m_FileDialogLastPopulatedStorageType = StorageType;
m_vCompleteFileList.clear();
if(m_FileDialogStorageType != IStorage::TYPE_SAVE && !str_comp(m_pFileDialogPath, "maps"))
{
CFilelistItem Item;
str_copy(Item.m_aFilename, "downloadedmaps", sizeof(Item.m_aFilename));
str_copy(Item.m_aName, "downloadedmaps/", sizeof(Item.m_aName));
Item.m_IsDir = true;
Item.m_IsLink = true;
Item.m_StorageType = IStorage::TYPE_SAVE;
m_vCompleteFileList.push_back(Item);
}
Storage()->ListDirectoryInfo(StorageType, m_pFileDialogPath, EditorListdirCallback, this);
RefreshFilteredFileList();
if(!KeepSelection)
{
m_FilesSelectedIndex = m_vpFilteredFileList.empty() ? -1 : 0;
if(m_FilesSelectedIndex >= 0)
str_copy(m_aFilesSelectedName, m_vpFilteredFileList[m_FilesSelectedIndex]->m_aName);
else
m_aFilesSelectedName[0] = '\0';
}
m_PreviewImageIsLoaded = false;
2010-09-12 11:15:59 +00:00
}
void CEditor::InvokeFileDialog(int StorageType, int FileType, const char *pTitle, const char *pButtonText,
2010-05-29 07:25:38 +00:00
const char *pBasePath, const char *pDefaultName,
void (*pfnFunc)(const char *pFileName, int StorageType, void *pUser), void *pUser)
2008-01-13 16:30:30 +00:00
{
UiClosePopupMenus();
2010-10-06 21:07:35 +00:00
m_FileDialogStorageType = StorageType;
2010-09-12 11:15:59 +00:00
m_pFileDialogTitle = pTitle;
m_pFileDialogButtonText = pButtonText;
m_pfnFileDialogFunc = pfnFunc;
m_pFileDialogUser = pUser;
m_aFileDialogFileName[0] = 0;
m_aFileDialogFilterString[0] = 0;
m_aFileDialogCurrentFolder[0] = 0;
m_aFileDialogCurrentLink[0] = 0;
m_pFileDialogPath = m_aFileDialogCurrentFolder;
m_FileDialogFileType = FileType;
m_PreviewImageIsLoaded = false;
m_FileDialogOpening = true;
2010-05-29 07:25:38 +00:00
if(pDefaultName)
2010-09-12 11:15:59 +00:00
str_copy(m_aFileDialogFileName, pDefaultName, sizeof(m_aFileDialogFileName));
2010-05-29 07:25:38 +00:00
if(pBasePath)
str_copy(m_aFileDialogCurrentFolder, pBasePath, sizeof(m_aFileDialogCurrentFolder));
2011-08-11 08:59:14 +00:00
FilelistPopulate(m_FileDialogStorageType);
2010-05-29 07:25:38 +00:00
m_FileDialogOpening = true;
2010-05-29 07:25:38 +00:00
m_Dialog = DIALOG_FILE;
2007-12-02 17:55:45 +00:00
}
2010-05-29 07:25:38 +00:00
void CEditor::RenderModebar(CUIRect View)
2007-12-02 17:55:45 +00:00
{
2010-05-29 07:25:38 +00:00
CUIRect Button;
2007-12-02 17:55:45 +00:00
2008-01-12 12:27:55 +00:00
// mode buttons
2007-12-02 17:55:45 +00:00
{
2010-05-29 07:25:38 +00:00
View.VSplitLeft(65.0f, &Button, &View);
Button.HSplitTop(30.0f, nullptr, &Button);
2010-05-29 07:25:38 +00:00
static int s_Button = 0;
const char *pButName = "";
if(m_Mode == MODE_LAYERS)
pButName = "Layers";
else if(m_Mode == MODE_IMAGES)
pButName = "Images";
else if(m_Mode == MODE_SOUNDS)
pButName = "Sounds";
2018-02-04 15:00:47 +00:00
int MouseButton = DoButton_Tab(&s_Button, pButName, 0, &Button, 0, "Switch between images, sounds and layers management.");
if(MouseButton == 2 || (Input()->KeyPress(KEY_LEFT) && m_Dialog == DIALOG_NONE && m_EditBoxActive == 0))
{
if(m_Mode == MODE_LAYERS)
m_Mode = MODE_SOUNDS;
else if(m_Mode == MODE_IMAGES)
m_Mode = MODE_LAYERS;
else
m_Mode = MODE_IMAGES;
}
else if(MouseButton == 1 || (Input()->KeyPress(KEY_RIGHT) && m_Dialog == DIALOG_NONE && m_EditBoxActive == 0))
2010-05-29 07:25:38 +00:00
{
if(m_Mode == MODE_LAYERS)
2011-08-11 08:59:14 +00:00
m_Mode = MODE_IMAGES;
else if(m_Mode == MODE_IMAGES)
m_Mode = MODE_SOUNDS;
2011-08-11 08:59:14 +00:00
else
m_Mode = MODE_LAYERS;
2010-05-29 07:25:38 +00:00
}
2007-12-02 17:55:45 +00:00
}
2008-01-12 12:27:55 +00:00
View.VSplitLeft(5.0f, nullptr, &View);
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
void CEditor::RenderStatusbar(CUIRect View)
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
CUIRect Button;
View.VSplitRight(60.0f, &View, &Button);
static int s_EnvelopeButton = 0;
int MouseButton = DoButton_Editor(&s_EnvelopeButton, "Envelopes", m_ShowEnvelopeEditor, &Button, 0, "Toggles the envelope editor.");
if(MouseButton == 2)
m_ShowEnvelopeEditor = (m_ShowEnvelopeEditor + 3) % 4;
else if(MouseButton == 1)
m_ShowEnvelopeEditor = (m_ShowEnvelopeEditor + 1) % 4;
if(MouseButton)
{
m_ShowServerSettingsEditor = false;
}
View.VSplitRight(100.0f, &View, &Button);
Button.VSplitRight(10.0f, &Button, nullptr);
static int s_SettingsButton = 0;
if(DoButton_Editor(&s_SettingsButton, "Server settings", m_ShowServerSettingsEditor, &Button, 0, "Toggles the server settings editor."))
{
m_ShowEnvelopeEditor = 0;
m_ShowServerSettingsEditor ^= 1;
}
2010-05-29 07:25:38 +00:00
if(m_pTooltip)
2008-01-17 23:09:49 +00:00
{
char aBuf[512];
2010-05-29 07:25:38 +00:00
if(ms_pUiGotContext && ms_pUiGotContext == UI()->HotItem())
2011-03-20 16:04:03 +00:00
str_format(aBuf, sizeof(aBuf), "%s Right click for context menu.", m_pTooltip);
2008-01-17 23:09:49 +00:00
else
str_copy(aBuf, m_pTooltip, sizeof(aBuf));
float FontSize = ScaleFontSize(aBuf, sizeof(aBuf), 10.0f, View.w);
2022-03-11 16:34:48 +00:00
SLabelProperties Props;
Props.m_MaxWidth = View.w;
UI()->DoLabel(&View, aBuf, FontSize, TEXTALIGN_LEFT, Props);
2008-01-17 23:09:49 +00:00
}
2008-01-12 12:27:55 +00:00
}
bool CEditor::IsEnvelopeUsed(int EnvelopeIndex) const
2018-10-14 13:53:14 +00:00
{
for(const auto &pGroup : m_Map.m_vpGroups)
2018-10-14 13:53:14 +00:00
{
for(const auto &pLayer : pGroup->m_vpLayers)
2018-10-14 13:53:14 +00:00
{
if(pLayer->m_Type == LAYERTYPE_QUADS)
2018-10-14 13:53:14 +00:00
{
CLayerQuads *pLayerQuads = (CLayerQuads *)pLayer;
for(const auto &Quad : pLayerQuads->m_vQuads)
2018-10-14 13:53:14 +00:00
{
if(Quad.m_PosEnv == EnvelopeIndex || Quad.m_ColorEnv == EnvelopeIndex)
2018-10-14 13:53:14 +00:00
{
return true;
}
}
}
else if(pLayer->m_Type == LAYERTYPE_SOUNDS)
2018-10-14 13:53:14 +00:00
{
CLayerSounds *pLayerSounds = (CLayerSounds *)pLayer;
for(const auto &Source : pLayerSounds->m_vSources)
2018-10-14 13:53:14 +00:00
{
if(Source.m_PosEnv == EnvelopeIndex || Source.m_SoundEnv == EnvelopeIndex)
2018-10-14 13:53:14 +00:00
{
return true;
}
}
}
else if(pLayer->m_Type == LAYERTYPE_TILES)
2018-10-14 13:53:14 +00:00
{
CLayerTiles *pLayerTiles = (CLayerTiles *)pLayer;
if(pLayerTiles->m_ColorEnv == EnvelopeIndex)
2018-10-14 13:53:14 +00:00
return true;
}
}
}
return false;
}
void CEditor::RemoveUnusedEnvelopes()
{
for(size_t Envelope = 0; Envelope < m_Map.m_vpEnvelopes.size();)
{
if(IsEnvelopeUsed(Envelope))
{
++Envelope;
}
else
{
m_Map.DeleteEnvelope(Envelope);
}
}
}
2010-05-29 07:25:38 +00:00
void CEditor::RenderEnvelopeEditor(CUIRect View)
2008-01-12 12:27:55 +00:00
{
if(m_SelectedEnvelope < 0)
m_SelectedEnvelope = 0;
if(m_SelectedEnvelope >= (int)m_Map.m_vpEnvelopes.size())
m_SelectedEnvelope = m_Map.m_vpEnvelopes.size() - 1;
2008-01-12 12:27:55 +00:00
CEnvelope *pEnvelope = nullptr;
if(m_SelectedEnvelope >= 0 && m_SelectedEnvelope < (int)m_Map.m_vpEnvelopes.size())
pEnvelope = m_Map.m_vpEnvelopes[m_SelectedEnvelope];
2008-01-12 12:27:55 +00:00
2010-05-29 07:25:38 +00:00
CUIRect ToolBar, CurveBar, ColorBar;
View.HSplitTop(15.0f, &ToolBar, &View);
View.HSplitTop(15.0f, &CurveBar, &View);
ToolBar.Margin(2.0f, &ToolBar);
CurveBar.Margin(2.0f, &CurveBar);
2008-01-12 12:27:55 +00:00
bool CurrentEnvelopeSwitched = false;
2008-01-12 12:27:55 +00:00
// do the toolbar
2007-12-02 17:55:45 +00:00
{
2010-05-29 07:25:38 +00:00
CUIRect Button;
CEnvelope *pNewEnv = nullptr;
2010-05-29 07:25:38 +00:00
2014-10-11 14:05:36 +00:00
ToolBar.VSplitRight(50.0f, &ToolBar, &Button);
static int s_NewSoundButton = 0;
2014-10-24 20:13:46 +00:00
if(DoButton_Editor(&s_NewSoundButton, "Sound+", 0, &Button, 0, "Creates a new sound envelope"))
2014-10-11 14:05:36 +00:00
{
m_Map.m_Modified = true;
pNewEnv = m_Map.NewEnvelope(1);
2014-10-11 14:05:36 +00:00
}
ToolBar.VSplitRight(5.0f, &ToolBar, nullptr);
2010-05-29 07:25:38 +00:00
ToolBar.VSplitRight(50.0f, &ToolBar, &Button);
static int s_New4dButton = 0;
2011-03-20 16:04:03 +00:00
if(DoButton_Editor(&s_New4dButton, "Color+", 0, &Button, 0, "Creates a new color envelope"))
{
m_Map.m_Modified = true;
2010-05-29 07:25:38 +00:00
pNewEnv = m_Map.NewEnvelope(4);
}
2010-05-29 07:25:38 +00:00
ToolBar.VSplitRight(5.0f, &ToolBar, nullptr);
2010-05-29 07:25:38 +00:00
ToolBar.VSplitRight(50.0f, &ToolBar, &Button);
static int s_New2dButton = 0;
2014-10-24 20:15:55 +00:00
if(DoButton_Editor(&s_New2dButton, "Pos.+", 0, &Button, 0, "Creates a new position envelope"))
{
m_Map.m_Modified = true;
2010-05-29 07:25:38 +00:00
pNewEnv = m_Map.NewEnvelope(3);
}
2011-01-06 03:46:10 +00:00
if(m_SelectedEnvelope >= 0)
{
// Delete button
ToolBar.VSplitRight(10.0f, &ToolBar, nullptr);
ToolBar.VSplitRight(25.0f, &ToolBar, &Button);
static int s_DeleteButton = 0;
if(DoButton_Editor(&s_DeleteButton, "", 0, &Button, 0, "Delete this envelope"))
{
m_Map.DeleteEnvelope(m_SelectedEnvelope);
if(m_SelectedEnvelope >= (int)m_Map.m_vpEnvelopes.size())
m_SelectedEnvelope = m_Map.m_vpEnvelopes.size() - 1;
pEnvelope = m_SelectedEnvelope >= 0 ? m_Map.m_vpEnvelopes[m_SelectedEnvelope] : nullptr;
}
// Move right button
ToolBar.VSplitRight(5.0f, &ToolBar, nullptr);
ToolBar.VSplitRight(25.0f, &ToolBar, &Button);
static int s_MoveRightButton = 0;
if(DoButton_Ex(&s_MoveRightButton, "", 0, &Button, 0, "Move this envelope to the right", IGraphics::CORNER_R))
{
m_Map.SwapEnvelopes(m_SelectedEnvelope, m_SelectedEnvelope + 1);
m_SelectedEnvelope = clamp<int>(m_SelectedEnvelope + 1, 0, m_Map.m_vpEnvelopes.size() - 1);
pEnvelope = m_Map.m_vpEnvelopes[m_SelectedEnvelope];
}
// Move left button
ToolBar.VSplitRight(25.0f, &ToolBar, &Button);
static int s_MoveLeftButton = 0;
if(DoButton_Ex(&s_MoveLeftButton, "", 0, &Button, 0, "Move this envelope to the left", IGraphics::CORNER_L))
{
m_Map.SwapEnvelopes(m_SelectedEnvelope - 1, m_SelectedEnvelope);
m_SelectedEnvelope = clamp<int>(m_SelectedEnvelope - 1, 0, m_Map.m_vpEnvelopes.size() - 1);
pEnvelope = m_Map.m_vpEnvelopes[m_SelectedEnvelope];
}
// Margin on the right side
ToolBar.VSplitRight(7.0f, &ToolBar, nullptr);
}
2010-05-29 07:25:38 +00:00
if(pNewEnv) // add the default points
2007-12-02 17:55:45 +00:00
{
2010-05-29 07:25:38 +00:00
if(pNewEnv->m_Channels == 4)
2008-01-12 12:27:55 +00:00
{
pNewEnv->AddPoint(0, f2fx(1.0f), f2fx(1.0f), f2fx(1.0f), f2fx(1.0f));
pNewEnv->AddPoint(1000, f2fx(1.0f), f2fx(1.0f), f2fx(1.0f), f2fx(1.0f));
2008-01-12 12:27:55 +00:00
}
else
{
2010-05-29 07:25:38 +00:00
pNewEnv->AddPoint(0, 0);
pNewEnv->AddPoint(1000, 0);
2008-01-12 12:27:55 +00:00
}
}
2010-05-29 07:25:38 +00:00
CUIRect Shifter, Inc, Dec;
ToolBar.VSplitLeft(60.0f, &Shifter, &ToolBar);
Shifter.VSplitRight(15.0f, &Shifter, &Inc);
Shifter.VSplitLeft(15.0f, &Dec, &Shifter);
2023-01-24 20:16:11 +00:00
char aBuf[64];
str_format(aBuf, sizeof(aBuf), "%d/%d", m_SelectedEnvelope + 1, (int)m_Map.m_vpEnvelopes.size());
2018-10-09 10:41:52 +00:00
ColorRGBA EnvColor = ColorRGBA(1, 1, 1, 0.5f);
if(!m_Map.m_vpEnvelopes.empty())
2018-10-09 10:41:52 +00:00
{
2018-10-14 13:53:14 +00:00
EnvColor = IsEnvelopeUsed(m_SelectedEnvelope) ?
ColorRGBA(1, 0.7f, 0.7f, 0.5f) :
ColorRGBA(0.7f, 1, 0.7f, 0.5f);
2018-10-09 10:41:52 +00:00
}
static int s_EnvelopeSelector = 0;
int NewValue = UiDoValueSelector(&s_EnvelopeSelector, &Shifter, aBuf, m_SelectedEnvelope + 1, 1, m_Map.m_vpEnvelopes.size(), 1, 1.0f, "Select Envelope", false, false, IGraphics::CORNER_NONE, &EnvColor, false);
if(NewValue - 1 != m_SelectedEnvelope)
{
m_SelectedEnvelope = NewValue - 1;
CurrentEnvelopeSwitched = true;
}
2010-05-29 07:25:38 +00:00
static int s_PrevButton = 0;
if(DoButton_ButtonDec(&s_PrevButton, nullptr, 0, &Dec, 0, "Previous Envelope"))
{
2010-05-29 07:25:38 +00:00
m_SelectedEnvelope--;
if(m_SelectedEnvelope < 0)
m_SelectedEnvelope = m_Map.m_vpEnvelopes.size() - 1;
CurrentEnvelopeSwitched = true;
}
2010-05-29 07:25:38 +00:00
static int s_NextButton = 0;
if(DoButton_ButtonInc(&s_NextButton, nullptr, 0, &Inc, 0, "Next Envelope"))
{
2010-05-29 07:25:38 +00:00
m_SelectedEnvelope++;
if(m_SelectedEnvelope >= (int)m_Map.m_vpEnvelopes.size())
m_SelectedEnvelope = 0;
CurrentEnvelopeSwitched = true;
}
2010-05-29 07:25:38 +00:00
if(pEnvelope)
2008-01-12 12:27:55 +00:00
{
ToolBar.VSplitLeft(15.0f, nullptr, &ToolBar);
ToolBar.VSplitLeft(40.0f, &Button, &ToolBar);
UI()->DoLabel(&Button, "Name:", 10.0f, TEXTALIGN_RIGHT);
2010-05-29 07:25:38 +00:00
ToolBar.VSplitLeft(3.0f, nullptr, &ToolBar);
ToolBar.VSplitLeft(ToolBar.w > ToolBar.h * 40 ? 80.0f : 60.0f, &Button, &ToolBar);
2010-05-29 07:25:38 +00:00
2011-07-12 01:14:46 +00:00
static float s_NameBox = 0;
if(DoEditBox(&s_NameBox, &Button, pEnvelope->m_aName, sizeof(pEnvelope->m_aName), 10.0f, &s_NameBox, false, IGraphics::CORNER_ALL, "The name of the selected envelope"))
2014-01-19 03:02:01 +00:00
{
m_Map.m_Modified = true;
2014-01-19 03:02:01 +00:00
}
2007-12-02 17:55:45 +00:00
}
}
2010-05-29 07:25:38 +00:00
bool ShowColorBar = false;
if(pEnvelope && pEnvelope->m_Channels == 4)
{
ShowColorBar = true;
View.HSplitTop(20.0f, &ColorBar, &View);
ColorBar.Margin(2.0f, &ColorBar);
RenderBackground(ColorBar, m_CheckerTexture, 16.0f, 1.0f);
}
RenderBackground(View, m_CheckerTexture, 32.0f, 0.1f);
2010-05-29 07:25:38 +00:00
if(pEnvelope)
2007-12-02 17:55:45 +00:00
{
2022-06-15 17:34:41 +00:00
static std::vector<int> s_vSelection;
static int s_EnvelopeEditorID = 0;
2010-05-29 07:25:38 +00:00
static int s_ActiveChannels = 0xf;
ColorRGBA aColors[] = {ColorRGBA(1, 0.2f, 0.2f), ColorRGBA(0.2f, 1, 0.2f), ColorRGBA(0.2f, 0.2f, 1), ColorRGBA(1, 1, 0.2f)};
2015-07-24 22:04:12 +00:00
Remove redundant conditions (always either true or false) According to cppcheck's `knownConditionTrueFalse` error: ``` src\base\system.cpp:1776:11: style: The comparison 'bytes == 0' is always true. [knownConditionTrueFalse] if(bytes == 0 && sock->ipv4sock >= 0) ^ src\base\system.cpp:1742:14: note: 'bytes' is assigned value '0' here. int bytes = 0; ^ src\base\system.cpp:1776:11: note: The comparison 'bytes == 0' is always true. if(bytes == 0 && sock->ipv4sock >= 0) ^ src\game\editor\io.cpp:887:33: style: Condition 'pSoundsItem->m_Version>=1' is always true [knownConditionTrueFalse] if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:874:33: note: Assuming that condition 'pSoundsItem->m_Version<1' is not redundant if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION) ^ src\game\editor\io.cpp:887:33: note: Condition 'pSoundsItem->m_Version>=1' is always true if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:914:33: style: Condition 'pSoundsItem->m_Version>=1' is always true [knownConditionTrueFalse] if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:901:33: note: Assuming that condition 'pSoundsItem->m_Version<1' is not redundant if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION) ^ src\game\editor\io.cpp:914:33: note: Condition 'pSoundsItem->m_Version>=1' is always true if(pSoundsItem->m_Version >= 1) ^ src\engine\client\backend\opengl\backend_opengl.cpp:207:68: style: Condition '*pStr=='\0'' is always false [knownConditionTrueFalse] else if(LastWasNumber && (*pStr == '.' || *pStr == ' ' || *pStr == '\0')) ^ src\game\client\components\maplayers.cpp:851:110: style: Condition 'DoTextureCoords' is always true [knownConditionTrueFalse] mem_copy_special(pUploadData + sizeof(vec2), pTmpTileTexCoords, sizeof(vec3), vtmpTiles.size() * 4, (DoTextureCoords ? (sizeof(vec2)) : 0)); ^ src\game\client\components\maplayers.cpp:849:11: note: Assuming that condition 'DoTextureCoords' is not redundant if(DoTextureCoords) ^ src\game\client\components\maplayers.cpp:851:110: note: Condition 'DoTextureCoords' is always true mem_copy_special(pUploadData + sizeof(vec2), pTmpTileTexCoords, sizeof(vec3), vtmpTiles.size() * 4, (DoTextureCoords ? (sizeof(vec2)) : 0)); ^ src\game\client\prediction\gameworld.cpp:567:5: style: Condition 'm_pParent' is always true [knownConditionTrueFalse] if(m_pParent && m_pParent->m_pChild && m_pParent->m_pChild != this) ^ src\game\client\components\menu_background.cpp:271:7: style: Condition 'NeedImageLoading' is always true [knownConditionTrueFalse] if(NeedImageLoading) ^ src\game\client\components\menu_background.cpp:268:23: note: Assignment 'NeedImageLoading=true', assigned value is 1 NeedImageLoading = true; ^ src\game\client\components\menu_background.cpp:271:7: note: Condition 'NeedImageLoading' is always true if(NeedImageLoading) ^ src\game\editor\editor.cpp:4991:6: style: Condition 'pEnvelope' is always true [knownConditionTrueFalse] if(pEnvelope) ^ ```
2022-10-30 12:52:20 +00:00
CUIRect Button;
2010-05-29 07:25:38 +00:00
Remove redundant conditions (always either true or false) According to cppcheck's `knownConditionTrueFalse` error: ``` src\base\system.cpp:1776:11: style: The comparison 'bytes == 0' is always true. [knownConditionTrueFalse] if(bytes == 0 && sock->ipv4sock >= 0) ^ src\base\system.cpp:1742:14: note: 'bytes' is assigned value '0' here. int bytes = 0; ^ src\base\system.cpp:1776:11: note: The comparison 'bytes == 0' is always true. if(bytes == 0 && sock->ipv4sock >= 0) ^ src\game\editor\io.cpp:887:33: style: Condition 'pSoundsItem->m_Version>=1' is always true [knownConditionTrueFalse] if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:874:33: note: Assuming that condition 'pSoundsItem->m_Version<1' is not redundant if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION) ^ src\game\editor\io.cpp:887:33: note: Condition 'pSoundsItem->m_Version>=1' is always true if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:914:33: style: Condition 'pSoundsItem->m_Version>=1' is always true [knownConditionTrueFalse] if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:901:33: note: Assuming that condition 'pSoundsItem->m_Version<1' is not redundant if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION) ^ src\game\editor\io.cpp:914:33: note: Condition 'pSoundsItem->m_Version>=1' is always true if(pSoundsItem->m_Version >= 1) ^ src\engine\client\backend\opengl\backend_opengl.cpp:207:68: style: Condition '*pStr=='\0'' is always false [knownConditionTrueFalse] else if(LastWasNumber && (*pStr == '.' || *pStr == ' ' || *pStr == '\0')) ^ src\game\client\components\maplayers.cpp:851:110: style: Condition 'DoTextureCoords' is always true [knownConditionTrueFalse] mem_copy_special(pUploadData + sizeof(vec2), pTmpTileTexCoords, sizeof(vec3), vtmpTiles.size() * 4, (DoTextureCoords ? (sizeof(vec2)) : 0)); ^ src\game\client\components\maplayers.cpp:849:11: note: Assuming that condition 'DoTextureCoords' is not redundant if(DoTextureCoords) ^ src\game\client\components\maplayers.cpp:851:110: note: Condition 'DoTextureCoords' is always true mem_copy_special(pUploadData + sizeof(vec2), pTmpTileTexCoords, sizeof(vec3), vtmpTiles.size() * 4, (DoTextureCoords ? (sizeof(vec2)) : 0)); ^ src\game\client\prediction\gameworld.cpp:567:5: style: Condition 'm_pParent' is always true [knownConditionTrueFalse] if(m_pParent && m_pParent->m_pChild && m_pParent->m_pChild != this) ^ src\game\client\components\menu_background.cpp:271:7: style: Condition 'NeedImageLoading' is always true [knownConditionTrueFalse] if(NeedImageLoading) ^ src\game\client\components\menu_background.cpp:268:23: note: Assignment 'NeedImageLoading=true', assigned value is 1 NeedImageLoading = true; ^ src\game\client\components\menu_background.cpp:271:7: note: Condition 'NeedImageLoading' is always true if(NeedImageLoading) ^ src\game\editor\editor.cpp:4991:6: style: Condition 'pEnvelope' is always true [knownConditionTrueFalse] if(pEnvelope) ^ ```
2022-10-30 12:52:20 +00:00
ToolBar.VSplitLeft(15.0f, &Button, &ToolBar);
2023-01-24 19:11:33 +00:00
static const char *s_aapNames[4][CEnvPoint::MAX_CHANNELS] = {
Remove redundant conditions (always either true or false) According to cppcheck's `knownConditionTrueFalse` error: ``` src\base\system.cpp:1776:11: style: The comparison 'bytes == 0' is always true. [knownConditionTrueFalse] if(bytes == 0 && sock->ipv4sock >= 0) ^ src\base\system.cpp:1742:14: note: 'bytes' is assigned value '0' here. int bytes = 0; ^ src\base\system.cpp:1776:11: note: The comparison 'bytes == 0' is always true. if(bytes == 0 && sock->ipv4sock >= 0) ^ src\game\editor\io.cpp:887:33: style: Condition 'pSoundsItem->m_Version>=1' is always true [knownConditionTrueFalse] if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:874:33: note: Assuming that condition 'pSoundsItem->m_Version<1' is not redundant if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION) ^ src\game\editor\io.cpp:887:33: note: Condition 'pSoundsItem->m_Version>=1' is always true if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:914:33: style: Condition 'pSoundsItem->m_Version>=1' is always true [knownConditionTrueFalse] if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:901:33: note: Assuming that condition 'pSoundsItem->m_Version<1' is not redundant if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION) ^ src\game\editor\io.cpp:914:33: note: Condition 'pSoundsItem->m_Version>=1' is always true if(pSoundsItem->m_Version >= 1) ^ src\engine\client\backend\opengl\backend_opengl.cpp:207:68: style: Condition '*pStr=='\0'' is always false [knownConditionTrueFalse] else if(LastWasNumber && (*pStr == '.' || *pStr == ' ' || *pStr == '\0')) ^ src\game\client\components\maplayers.cpp:851:110: style: Condition 'DoTextureCoords' is always true [knownConditionTrueFalse] mem_copy_special(pUploadData + sizeof(vec2), pTmpTileTexCoords, sizeof(vec3), vtmpTiles.size() * 4, (DoTextureCoords ? (sizeof(vec2)) : 0)); ^ src\game\client\components\maplayers.cpp:849:11: note: Assuming that condition 'DoTextureCoords' is not redundant if(DoTextureCoords) ^ src\game\client\components\maplayers.cpp:851:110: note: Condition 'DoTextureCoords' is always true mem_copy_special(pUploadData + sizeof(vec2), pTmpTileTexCoords, sizeof(vec3), vtmpTiles.size() * 4, (DoTextureCoords ? (sizeof(vec2)) : 0)); ^ src\game\client\prediction\gameworld.cpp:567:5: style: Condition 'm_pParent' is always true [knownConditionTrueFalse] if(m_pParent && m_pParent->m_pChild && m_pParent->m_pChild != this) ^ src\game\client\components\menu_background.cpp:271:7: style: Condition 'NeedImageLoading' is always true [knownConditionTrueFalse] if(NeedImageLoading) ^ src\game\client\components\menu_background.cpp:268:23: note: Assignment 'NeedImageLoading=true', assigned value is 1 NeedImageLoading = true; ^ src\game\client\components\menu_background.cpp:271:7: note: Condition 'NeedImageLoading' is always true if(NeedImageLoading) ^ src\game\editor\editor.cpp:4991:6: style: Condition 'pEnvelope' is always true [knownConditionTrueFalse] if(pEnvelope) ^ ```
2022-10-30 12:52:20 +00:00
{"V", "", "", ""},
{"", "", "", ""},
{"X", "Y", "R", ""},
{"R", "G", "B", "A"},
};
2010-05-29 07:25:38 +00:00
2023-01-24 19:11:33 +00:00
static const char *s_aapDescriptions[4][CEnvPoint::MAX_CHANNELS] = {
Remove redundant conditions (always either true or false) According to cppcheck's `knownConditionTrueFalse` error: ``` src\base\system.cpp:1776:11: style: The comparison 'bytes == 0' is always true. [knownConditionTrueFalse] if(bytes == 0 && sock->ipv4sock >= 0) ^ src\base\system.cpp:1742:14: note: 'bytes' is assigned value '0' here. int bytes = 0; ^ src\base\system.cpp:1776:11: note: The comparison 'bytes == 0' is always true. if(bytes == 0 && sock->ipv4sock >= 0) ^ src\game\editor\io.cpp:887:33: style: Condition 'pSoundsItem->m_Version>=1' is always true [knownConditionTrueFalse] if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:874:33: note: Assuming that condition 'pSoundsItem->m_Version<1' is not redundant if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION) ^ src\game\editor\io.cpp:887:33: note: Condition 'pSoundsItem->m_Version>=1' is always true if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:914:33: style: Condition 'pSoundsItem->m_Version>=1' is always true [knownConditionTrueFalse] if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:901:33: note: Assuming that condition 'pSoundsItem->m_Version<1' is not redundant if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION) ^ src\game\editor\io.cpp:914:33: note: Condition 'pSoundsItem->m_Version>=1' is always true if(pSoundsItem->m_Version >= 1) ^ src\engine\client\backend\opengl\backend_opengl.cpp:207:68: style: Condition '*pStr=='\0'' is always false [knownConditionTrueFalse] else if(LastWasNumber && (*pStr == '.' || *pStr == ' ' || *pStr == '\0')) ^ src\game\client\components\maplayers.cpp:851:110: style: Condition 'DoTextureCoords' is always true [knownConditionTrueFalse] mem_copy_special(pUploadData + sizeof(vec2), pTmpTileTexCoords, sizeof(vec3), vtmpTiles.size() * 4, (DoTextureCoords ? (sizeof(vec2)) : 0)); ^ src\game\client\components\maplayers.cpp:849:11: note: Assuming that condition 'DoTextureCoords' is not redundant if(DoTextureCoords) ^ src\game\client\components\maplayers.cpp:851:110: note: Condition 'DoTextureCoords' is always true mem_copy_special(pUploadData + sizeof(vec2), pTmpTileTexCoords, sizeof(vec3), vtmpTiles.size() * 4, (DoTextureCoords ? (sizeof(vec2)) : 0)); ^ src\game\client\prediction\gameworld.cpp:567:5: style: Condition 'm_pParent' is always true [knownConditionTrueFalse] if(m_pParent && m_pParent->m_pChild && m_pParent->m_pChild != this) ^ src\game\client\components\menu_background.cpp:271:7: style: Condition 'NeedImageLoading' is always true [knownConditionTrueFalse] if(NeedImageLoading) ^ src\game\client\components\menu_background.cpp:268:23: note: Assignment 'NeedImageLoading=true', assigned value is 1 NeedImageLoading = true; ^ src\game\client\components\menu_background.cpp:271:7: note: Condition 'NeedImageLoading' is always true if(NeedImageLoading) ^ src\game\editor\editor.cpp:4991:6: style: Condition 'pEnvelope' is always true [knownConditionTrueFalse] if(pEnvelope) ^ ```
2022-10-30 12:52:20 +00:00
{"Volume of the envelope", "", "", ""},
{"", "", "", ""},
{"X-axis of the envelope", "Y-axis of the envelope", "Rotation of the envelope", ""},
{"Red value of the envelope", "Green value of the envelope", "Blue value of the envelope", "Alpha value of the envelope"},
};
2010-05-29 07:25:38 +00:00
2023-01-24 19:11:33 +00:00
static int s_aChannelButtons[CEnvPoint::MAX_CHANNELS] = {0};
Remove redundant conditions (always either true or false) According to cppcheck's `knownConditionTrueFalse` error: ``` src\base\system.cpp:1776:11: style: The comparison 'bytes == 0' is always true. [knownConditionTrueFalse] if(bytes == 0 && sock->ipv4sock >= 0) ^ src\base\system.cpp:1742:14: note: 'bytes' is assigned value '0' here. int bytes = 0; ^ src\base\system.cpp:1776:11: note: The comparison 'bytes == 0' is always true. if(bytes == 0 && sock->ipv4sock >= 0) ^ src\game\editor\io.cpp:887:33: style: Condition 'pSoundsItem->m_Version>=1' is always true [knownConditionTrueFalse] if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:874:33: note: Assuming that condition 'pSoundsItem->m_Version<1' is not redundant if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION) ^ src\game\editor\io.cpp:887:33: note: Condition 'pSoundsItem->m_Version>=1' is always true if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:914:33: style: Condition 'pSoundsItem->m_Version>=1' is always true [knownConditionTrueFalse] if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:901:33: note: Assuming that condition 'pSoundsItem->m_Version<1' is not redundant if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION) ^ src\game\editor\io.cpp:914:33: note: Condition 'pSoundsItem->m_Version>=1' is always true if(pSoundsItem->m_Version >= 1) ^ src\engine\client\backend\opengl\backend_opengl.cpp:207:68: style: Condition '*pStr=='\0'' is always false [knownConditionTrueFalse] else if(LastWasNumber && (*pStr == '.' || *pStr == ' ' || *pStr == '\0')) ^ src\game\client\components\maplayers.cpp:851:110: style: Condition 'DoTextureCoords' is always true [knownConditionTrueFalse] mem_copy_special(pUploadData + sizeof(vec2), pTmpTileTexCoords, sizeof(vec3), vtmpTiles.size() * 4, (DoTextureCoords ? (sizeof(vec2)) : 0)); ^ src\game\client\components\maplayers.cpp:849:11: note: Assuming that condition 'DoTextureCoords' is not redundant if(DoTextureCoords) ^ src\game\client\components\maplayers.cpp:851:110: note: Condition 'DoTextureCoords' is always true mem_copy_special(pUploadData + sizeof(vec2), pTmpTileTexCoords, sizeof(vec3), vtmpTiles.size() * 4, (DoTextureCoords ? (sizeof(vec2)) : 0)); ^ src\game\client\prediction\gameworld.cpp:567:5: style: Condition 'm_pParent' is always true [knownConditionTrueFalse] if(m_pParent && m_pParent->m_pChild && m_pParent->m_pChild != this) ^ src\game\client\components\menu_background.cpp:271:7: style: Condition 'NeedImageLoading' is always true [knownConditionTrueFalse] if(NeedImageLoading) ^ src\game\client\components\menu_background.cpp:268:23: note: Assignment 'NeedImageLoading=true', assigned value is 1 NeedImageLoading = true; ^ src\game\client\components\menu_background.cpp:271:7: note: Condition 'NeedImageLoading' is always true if(NeedImageLoading) ^ src\game\editor\editor.cpp:4991:6: style: Condition 'pEnvelope' is always true [knownConditionTrueFalse] if(pEnvelope) ^ ```
2022-10-30 12:52:20 +00:00
int Bit = 1;
for(int i = 0; i < CEnvPoint::MAX_CHANNELS; i++, Bit <<= 1)
Remove redundant conditions (always either true or false) According to cppcheck's `knownConditionTrueFalse` error: ``` src\base\system.cpp:1776:11: style: The comparison 'bytes == 0' is always true. [knownConditionTrueFalse] if(bytes == 0 && sock->ipv4sock >= 0) ^ src\base\system.cpp:1742:14: note: 'bytes' is assigned value '0' here. int bytes = 0; ^ src\base\system.cpp:1776:11: note: The comparison 'bytes == 0' is always true. if(bytes == 0 && sock->ipv4sock >= 0) ^ src\game\editor\io.cpp:887:33: style: Condition 'pSoundsItem->m_Version>=1' is always true [knownConditionTrueFalse] if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:874:33: note: Assuming that condition 'pSoundsItem->m_Version<1' is not redundant if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION) ^ src\game\editor\io.cpp:887:33: note: Condition 'pSoundsItem->m_Version>=1' is always true if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:914:33: style: Condition 'pSoundsItem->m_Version>=1' is always true [knownConditionTrueFalse] if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:901:33: note: Assuming that condition 'pSoundsItem->m_Version<1' is not redundant if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION) ^ src\game\editor\io.cpp:914:33: note: Condition 'pSoundsItem->m_Version>=1' is always true if(pSoundsItem->m_Version >= 1) ^ src\engine\client\backend\opengl\backend_opengl.cpp:207:68: style: Condition '*pStr=='\0'' is always false [knownConditionTrueFalse] else if(LastWasNumber && (*pStr == '.' || *pStr == ' ' || *pStr == '\0')) ^ src\game\client\components\maplayers.cpp:851:110: style: Condition 'DoTextureCoords' is always true [knownConditionTrueFalse] mem_copy_special(pUploadData + sizeof(vec2), pTmpTileTexCoords, sizeof(vec3), vtmpTiles.size() * 4, (DoTextureCoords ? (sizeof(vec2)) : 0)); ^ src\game\client\components\maplayers.cpp:849:11: note: Assuming that condition 'DoTextureCoords' is not redundant if(DoTextureCoords) ^ src\game\client\components\maplayers.cpp:851:110: note: Condition 'DoTextureCoords' is always true mem_copy_special(pUploadData + sizeof(vec2), pTmpTileTexCoords, sizeof(vec3), vtmpTiles.size() * 4, (DoTextureCoords ? (sizeof(vec2)) : 0)); ^ src\game\client\prediction\gameworld.cpp:567:5: style: Condition 'm_pParent' is always true [knownConditionTrueFalse] if(m_pParent && m_pParent->m_pChild && m_pParent->m_pChild != this) ^ src\game\client\components\menu_background.cpp:271:7: style: Condition 'NeedImageLoading' is always true [knownConditionTrueFalse] if(NeedImageLoading) ^ src\game\client\components\menu_background.cpp:268:23: note: Assignment 'NeedImageLoading=true', assigned value is 1 NeedImageLoading = true; ^ src\game\client\components\menu_background.cpp:271:7: note: Condition 'NeedImageLoading' is always true if(NeedImageLoading) ^ src\game\editor\editor.cpp:4991:6: style: Condition 'pEnvelope' is always true [knownConditionTrueFalse] if(pEnvelope) ^ ```
2022-10-30 12:52:20 +00:00
{
ToolBar.VSplitLeft(15.0f, &Button, &ToolBar);
if(i < pEnvelope->m_Channels)
{
if(DoButton_Env(&s_aChannelButtons[i], s_aapNames[pEnvelope->m_Channels - 1][i], s_ActiveChannels & Bit, &Button, s_aapDescriptions[pEnvelope->m_Channels - 1][i], aColors[i]))
s_ActiveChannels ^= Bit;
}
2010-05-29 07:25:38 +00:00
}
Remove redundant conditions (always either true or false) According to cppcheck's `knownConditionTrueFalse` error: ``` src\base\system.cpp:1776:11: style: The comparison 'bytes == 0' is always true. [knownConditionTrueFalse] if(bytes == 0 && sock->ipv4sock >= 0) ^ src\base\system.cpp:1742:14: note: 'bytes' is assigned value '0' here. int bytes = 0; ^ src\base\system.cpp:1776:11: note: The comparison 'bytes == 0' is always true. if(bytes == 0 && sock->ipv4sock >= 0) ^ src\game\editor\io.cpp:887:33: style: Condition 'pSoundsItem->m_Version>=1' is always true [knownConditionTrueFalse] if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:874:33: note: Assuming that condition 'pSoundsItem->m_Version<1' is not redundant if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION) ^ src\game\editor\io.cpp:887:33: note: Condition 'pSoundsItem->m_Version>=1' is always true if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:914:33: style: Condition 'pSoundsItem->m_Version>=1' is always true [knownConditionTrueFalse] if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:901:33: note: Assuming that condition 'pSoundsItem->m_Version<1' is not redundant if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION) ^ src\game\editor\io.cpp:914:33: note: Condition 'pSoundsItem->m_Version>=1' is always true if(pSoundsItem->m_Version >= 1) ^ src\engine\client\backend\opengl\backend_opengl.cpp:207:68: style: Condition '*pStr=='\0'' is always false [knownConditionTrueFalse] else if(LastWasNumber && (*pStr == '.' || *pStr == ' ' || *pStr == '\0')) ^ src\game\client\components\maplayers.cpp:851:110: style: Condition 'DoTextureCoords' is always true [knownConditionTrueFalse] mem_copy_special(pUploadData + sizeof(vec2), pTmpTileTexCoords, sizeof(vec3), vtmpTiles.size() * 4, (DoTextureCoords ? (sizeof(vec2)) : 0)); ^ src\game\client\components\maplayers.cpp:849:11: note: Assuming that condition 'DoTextureCoords' is not redundant if(DoTextureCoords) ^ src\game\client\components\maplayers.cpp:851:110: note: Condition 'DoTextureCoords' is always true mem_copy_special(pUploadData + sizeof(vec2), pTmpTileTexCoords, sizeof(vec3), vtmpTiles.size() * 4, (DoTextureCoords ? (sizeof(vec2)) : 0)); ^ src\game\client\prediction\gameworld.cpp:567:5: style: Condition 'm_pParent' is always true [knownConditionTrueFalse] if(m_pParent && m_pParent->m_pChild && m_pParent->m_pChild != this) ^ src\game\client\components\menu_background.cpp:271:7: style: Condition 'NeedImageLoading' is always true [knownConditionTrueFalse] if(NeedImageLoading) ^ src\game\client\components\menu_background.cpp:268:23: note: Assignment 'NeedImageLoading=true', assigned value is 1 NeedImageLoading = true; ^ src\game\client\components\menu_background.cpp:271:7: note: Condition 'NeedImageLoading' is always true if(NeedImageLoading) ^ src\game\editor\editor.cpp:4991:6: style: Condition 'pEnvelope' is always true [knownConditionTrueFalse] if(pEnvelope) ^ ```
2022-10-30 12:52:20 +00:00
// sync checkbox
ToolBar.VSplitLeft(15.0f, nullptr, &ToolBar);
Remove redundant conditions (always either true or false) According to cppcheck's `knownConditionTrueFalse` error: ``` src\base\system.cpp:1776:11: style: The comparison 'bytes == 0' is always true. [knownConditionTrueFalse] if(bytes == 0 && sock->ipv4sock >= 0) ^ src\base\system.cpp:1742:14: note: 'bytes' is assigned value '0' here. int bytes = 0; ^ src\base\system.cpp:1776:11: note: The comparison 'bytes == 0' is always true. if(bytes == 0 && sock->ipv4sock >= 0) ^ src\game\editor\io.cpp:887:33: style: Condition 'pSoundsItem->m_Version>=1' is always true [knownConditionTrueFalse] if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:874:33: note: Assuming that condition 'pSoundsItem->m_Version<1' is not redundant if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION) ^ src\game\editor\io.cpp:887:33: note: Condition 'pSoundsItem->m_Version>=1' is always true if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:914:33: style: Condition 'pSoundsItem->m_Version>=1' is always true [knownConditionTrueFalse] if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:901:33: note: Assuming that condition 'pSoundsItem->m_Version<1' is not redundant if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION) ^ src\game\editor\io.cpp:914:33: note: Condition 'pSoundsItem->m_Version>=1' is always true if(pSoundsItem->m_Version >= 1) ^ src\engine\client\backend\opengl\backend_opengl.cpp:207:68: style: Condition '*pStr=='\0'' is always false [knownConditionTrueFalse] else if(LastWasNumber && (*pStr == '.' || *pStr == ' ' || *pStr == '\0')) ^ src\game\client\components\maplayers.cpp:851:110: style: Condition 'DoTextureCoords' is always true [knownConditionTrueFalse] mem_copy_special(pUploadData + sizeof(vec2), pTmpTileTexCoords, sizeof(vec3), vtmpTiles.size() * 4, (DoTextureCoords ? (sizeof(vec2)) : 0)); ^ src\game\client\components\maplayers.cpp:849:11: note: Assuming that condition 'DoTextureCoords' is not redundant if(DoTextureCoords) ^ src\game\client\components\maplayers.cpp:851:110: note: Condition 'DoTextureCoords' is always true mem_copy_special(pUploadData + sizeof(vec2), pTmpTileTexCoords, sizeof(vec3), vtmpTiles.size() * 4, (DoTextureCoords ? (sizeof(vec2)) : 0)); ^ src\game\client\prediction\gameworld.cpp:567:5: style: Condition 'm_pParent' is always true [knownConditionTrueFalse] if(m_pParent && m_pParent->m_pChild && m_pParent->m_pChild != this) ^ src\game\client\components\menu_background.cpp:271:7: style: Condition 'NeedImageLoading' is always true [knownConditionTrueFalse] if(NeedImageLoading) ^ src\game\client\components\menu_background.cpp:268:23: note: Assignment 'NeedImageLoading=true', assigned value is 1 NeedImageLoading = true; ^ src\game\client\components\menu_background.cpp:271:7: note: Condition 'NeedImageLoading' is always true if(NeedImageLoading) ^ src\game\editor\editor.cpp:4991:6: style: Condition 'pEnvelope' is always true [knownConditionTrueFalse] if(pEnvelope) ^ ```
2022-10-30 12:52:20 +00:00
ToolBar.VSplitLeft(12.0f, &Button, &ToolBar);
static int s_SyncButton;
if(DoButton_Editor(&s_SyncButton, pEnvelope->m_Synchronized ? "X" : "", 0, &Button, 0, "Synchronize envelope animation to game time (restarts when you touch the start line)"))
pEnvelope->m_Synchronized = !pEnvelope->m_Synchronized;
ToolBar.VSplitLeft(4.0f, nullptr, &ToolBar);
ToolBar.VSplitLeft(40.0f, &Button, &ToolBar);
UI()->DoLabel(&Button, "Sync.", 10.0f, TEXTALIGN_LEFT);
Remove redundant conditions (always either true or false) According to cppcheck's `knownConditionTrueFalse` error: ``` src\base\system.cpp:1776:11: style: The comparison 'bytes == 0' is always true. [knownConditionTrueFalse] if(bytes == 0 && sock->ipv4sock >= 0) ^ src\base\system.cpp:1742:14: note: 'bytes' is assigned value '0' here. int bytes = 0; ^ src\base\system.cpp:1776:11: note: The comparison 'bytes == 0' is always true. if(bytes == 0 && sock->ipv4sock >= 0) ^ src\game\editor\io.cpp:887:33: style: Condition 'pSoundsItem->m_Version>=1' is always true [knownConditionTrueFalse] if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:874:33: note: Assuming that condition 'pSoundsItem->m_Version<1' is not redundant if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION) ^ src\game\editor\io.cpp:887:33: note: Condition 'pSoundsItem->m_Version>=1' is always true if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:914:33: style: Condition 'pSoundsItem->m_Version>=1' is always true [knownConditionTrueFalse] if(pSoundsItem->m_Version >= 1) ^ src\game\editor\io.cpp:901:33: note: Assuming that condition 'pSoundsItem->m_Version<1' is not redundant if(pSoundsItem->m_Version < 1 || pSoundsItem->m_Version > CMapItemLayerSounds::CURRENT_VERSION) ^ src\game\editor\io.cpp:914:33: note: Condition 'pSoundsItem->m_Version>=1' is always true if(pSoundsItem->m_Version >= 1) ^ src\engine\client\backend\opengl\backend_opengl.cpp:207:68: style: Condition '*pStr=='\0'' is always false [knownConditionTrueFalse] else if(LastWasNumber && (*pStr == '.' || *pStr == ' ' || *pStr == '\0')) ^ src\game\client\components\maplayers.cpp:851:110: style: Condition 'DoTextureCoords' is always true [knownConditionTrueFalse] mem_copy_special(pUploadData + sizeof(vec2), pTmpTileTexCoords, sizeof(vec3), vtmpTiles.size() * 4, (DoTextureCoords ? (sizeof(vec2)) : 0)); ^ src\game\client\components\maplayers.cpp:849:11: note: Assuming that condition 'DoTextureCoords' is not redundant if(DoTextureCoords) ^ src\game\client\components\maplayers.cpp:851:110: note: Condition 'DoTextureCoords' is always true mem_copy_special(pUploadData + sizeof(vec2), pTmpTileTexCoords, sizeof(vec3), vtmpTiles.size() * 4, (DoTextureCoords ? (sizeof(vec2)) : 0)); ^ src\game\client\prediction\gameworld.cpp:567:5: style: Condition 'm_pParent' is always true [knownConditionTrueFalse] if(m_pParent && m_pParent->m_pChild && m_pParent->m_pChild != this) ^ src\game\client\components\menu_background.cpp:271:7: style: Condition 'NeedImageLoading' is always true [knownConditionTrueFalse] if(NeedImageLoading) ^ src\game\client\components\menu_background.cpp:268:23: note: Assignment 'NeedImageLoading=true', assigned value is 1 NeedImageLoading = true; ^ src\game\client\components\menu_background.cpp:271:7: note: Condition 'NeedImageLoading' is always true if(NeedImageLoading) ^ src\game\editor\editor.cpp:4991:6: style: Condition 'pEnvelope' is always true [knownConditionTrueFalse] if(pEnvelope) ^ ```
2022-10-30 12:52:20 +00:00
2010-05-29 07:25:38 +00:00
float EndTime = pEnvelope->EndTime();
if(EndTime < 1)
EndTime = 1;
pEnvelope->FindTopBottom(s_ActiveChannels);
float Top = pEnvelope->m_Top;
float Bottom = pEnvelope->m_Bottom;
if(Top < 1)
Top = 1;
if(Bottom >= 0)
Bottom = 0;
float TimeScale = EndTime / View.w;
float ValueScale = (Top - Bottom) / View.h;
2010-05-29 07:25:38 +00:00
if(UI()->MouseInside(&View))
2022-06-15 17:34:41 +00:00
UI()->SetHotItem(&s_EnvelopeEditorID);
2010-05-29 07:25:38 +00:00
2022-06-15 17:34:41 +00:00
if(UI()->HotItem() == &s_EnvelopeEditorID)
2008-01-12 12:27:55 +00:00
{
// do stuff
Remove redundant null-checks According to cppcheck's `nullPointerRedundantCheck` check: ``` src\game\client\components\menus_settings.cpp:729:8: warning: Either the condition 'pSkinToBeSelected==0' is redundant or there is possible null pointer dereference: pSkinToBeSelected. [nullPointerRedundantCheck] if((pSkinToBeSelected->GetName()[0] == 'x' && pSkinToBeSelected->GetName()[1] == '_')) ^ src\game\client\components\menus_settings.cpp:732:25: note: Assuming that condition 'pSkinToBeSelected==0' is not redundant if(pSkinToBeSelected == 0) ^ src\game\client\components\menus_settings.cpp:729:8: note: Null pointer dereference if((pSkinToBeSelected->GetName()[0] == 'x' && pSkinToBeSelected->GetName()[1] == '_')) ^ src\game\editor\editor.cpp:5034:19: warning: Either the condition 'pEnvelope' is redundant or there is possible null pointer dereference: pEnvelope. [nullPointerRedundantCheck] float EndTime = pEnvelope->EndTime(); ^ src\game\editor\editor.cpp:5056:7: note: Assuming that condition 'pEnvelope' is not redundant if(pEnvelope) ^ src\game\editor\editor.cpp:5034:19: note: Null pointer dereference float EndTime = pEnvelope->EndTime(); ^ src\game\editor\editor.cpp:5038:3: warning: Either the condition 'pEnvelope' is redundant or there is possible null pointer dereference: pEnvelope. [nullPointerRedundantCheck] pEnvelope->FindTopBottom(s_ActiveChannels); ^ src\game\editor\editor.cpp:5056:7: note: Assuming that condition 'pEnvelope' is not redundant if(pEnvelope) ^ src\game\editor\editor.cpp:5038:3: note: Null pointer dereference pEnvelope->FindTopBottom(s_ActiveChannels); ^ src\game\editor\editor.cpp:5039:15: warning: Either the condition 'pEnvelope' is redundant or there is possible null pointer dereference: pEnvelope. [nullPointerRedundantCheck] float Top = pEnvelope->m_Top; ^ src\game\editor\editor.cpp:5056:7: note: Assuming that condition 'pEnvelope' is not redundant if(pEnvelope) ^ src\game\editor\editor.cpp:5039:15: note: Null pointer dereference float Top = pEnvelope->m_Top; ^ src\game\editor\editor.cpp:5040:18: warning: Either the condition 'pEnvelope' is redundant or there is possible null pointer dereference: pEnvelope. [nullPointerRedundantCheck] float Bottom = pEnvelope->m_Bottom; ^ src\game\editor\editor.cpp:5056:7: note: Assuming that condition 'pEnvelope' is not redundant if(pEnvelope) ^ src\game\editor\editor.cpp:5040:18: note: Null pointer dereference float Bottom = pEnvelope->m_Bottom; ^ src\game\editor\editor.cpp:5081:23: warning: Either the condition 'pEnvelope' is redundant or there is possible null pointer dereference: pEnvelope. [nullPointerRedundantCheck] for(int c = 0; c < pEnvelope->m_Channels; c++) ^ src\game\editor\editor.cpp:5056:7: note: Assuming that condition 'pEnvelope' is not redundant if(pEnvelope) ^ src\game\editor\editor.cpp:5081:23: note: Null pointer dereference for(int c = 0; c < pEnvelope->m_Channels; c++) ^ ```
2022-10-30 12:57:12 +00:00
if(UI()->MouseButtonClicked(1))
2008-01-12 12:27:55 +00:00
{
Remove redundant null-checks According to cppcheck's `nullPointerRedundantCheck` check: ``` src\game\client\components\menus_settings.cpp:729:8: warning: Either the condition 'pSkinToBeSelected==0' is redundant or there is possible null pointer dereference: pSkinToBeSelected. [nullPointerRedundantCheck] if((pSkinToBeSelected->GetName()[0] == 'x' && pSkinToBeSelected->GetName()[1] == '_')) ^ src\game\client\components\menus_settings.cpp:732:25: note: Assuming that condition 'pSkinToBeSelected==0' is not redundant if(pSkinToBeSelected == 0) ^ src\game\client\components\menus_settings.cpp:729:8: note: Null pointer dereference if((pSkinToBeSelected->GetName()[0] == 'x' && pSkinToBeSelected->GetName()[1] == '_')) ^ src\game\editor\editor.cpp:5034:19: warning: Either the condition 'pEnvelope' is redundant or there is possible null pointer dereference: pEnvelope. [nullPointerRedundantCheck] float EndTime = pEnvelope->EndTime(); ^ src\game\editor\editor.cpp:5056:7: note: Assuming that condition 'pEnvelope' is not redundant if(pEnvelope) ^ src\game\editor\editor.cpp:5034:19: note: Null pointer dereference float EndTime = pEnvelope->EndTime(); ^ src\game\editor\editor.cpp:5038:3: warning: Either the condition 'pEnvelope' is redundant or there is possible null pointer dereference: pEnvelope. [nullPointerRedundantCheck] pEnvelope->FindTopBottom(s_ActiveChannels); ^ src\game\editor\editor.cpp:5056:7: note: Assuming that condition 'pEnvelope' is not redundant if(pEnvelope) ^ src\game\editor\editor.cpp:5038:3: note: Null pointer dereference pEnvelope->FindTopBottom(s_ActiveChannels); ^ src\game\editor\editor.cpp:5039:15: warning: Either the condition 'pEnvelope' is redundant or there is possible null pointer dereference: pEnvelope. [nullPointerRedundantCheck] float Top = pEnvelope->m_Top; ^ src\game\editor\editor.cpp:5056:7: note: Assuming that condition 'pEnvelope' is not redundant if(pEnvelope) ^ src\game\editor\editor.cpp:5039:15: note: Null pointer dereference float Top = pEnvelope->m_Top; ^ src\game\editor\editor.cpp:5040:18: warning: Either the condition 'pEnvelope' is redundant or there is possible null pointer dereference: pEnvelope. [nullPointerRedundantCheck] float Bottom = pEnvelope->m_Bottom; ^ src\game\editor\editor.cpp:5056:7: note: Assuming that condition 'pEnvelope' is not redundant if(pEnvelope) ^ src\game\editor\editor.cpp:5040:18: note: Null pointer dereference float Bottom = pEnvelope->m_Bottom; ^ src\game\editor\editor.cpp:5081:23: warning: Either the condition 'pEnvelope' is redundant or there is possible null pointer dereference: pEnvelope. [nullPointerRedundantCheck] for(int c = 0; c < pEnvelope->m_Channels; c++) ^ src\game\editor\editor.cpp:5056:7: note: Assuming that condition 'pEnvelope' is not redundant if(pEnvelope) ^ src\game\editor\editor.cpp:5081:23: note: Null pointer dereference for(int c = 0; c < pEnvelope->m_Channels; c++) ^ ```
2022-10-30 12:57:12 +00:00
// add point
int Time = (int)(((UI()->MouseX() - View.x) * TimeScale) * 1000.0f);
ColorRGBA Channels;
pEnvelope->Eval(Time / 1000.0f, Channels);
pEnvelope->AddPoint(Time,
f2fx(Channels.r), f2fx(Channels.g),
f2fx(Channels.b), f2fx(Channels.a));
m_Map.m_Modified = true;
2008-01-12 12:27:55 +00:00
}
Remove redundant null-checks According to cppcheck's `nullPointerRedundantCheck` check: ``` src\game\client\components\menus_settings.cpp:729:8: warning: Either the condition 'pSkinToBeSelected==0' is redundant or there is possible null pointer dereference: pSkinToBeSelected. [nullPointerRedundantCheck] if((pSkinToBeSelected->GetName()[0] == 'x' && pSkinToBeSelected->GetName()[1] == '_')) ^ src\game\client\components\menus_settings.cpp:732:25: note: Assuming that condition 'pSkinToBeSelected==0' is not redundant if(pSkinToBeSelected == 0) ^ src\game\client\components\menus_settings.cpp:729:8: note: Null pointer dereference if((pSkinToBeSelected->GetName()[0] == 'x' && pSkinToBeSelected->GetName()[1] == '_')) ^ src\game\editor\editor.cpp:5034:19: warning: Either the condition 'pEnvelope' is redundant or there is possible null pointer dereference: pEnvelope. [nullPointerRedundantCheck] float EndTime = pEnvelope->EndTime(); ^ src\game\editor\editor.cpp:5056:7: note: Assuming that condition 'pEnvelope' is not redundant if(pEnvelope) ^ src\game\editor\editor.cpp:5034:19: note: Null pointer dereference float EndTime = pEnvelope->EndTime(); ^ src\game\editor\editor.cpp:5038:3: warning: Either the condition 'pEnvelope' is redundant or there is possible null pointer dereference: pEnvelope. [nullPointerRedundantCheck] pEnvelope->FindTopBottom(s_ActiveChannels); ^ src\game\editor\editor.cpp:5056:7: note: Assuming that condition 'pEnvelope' is not redundant if(pEnvelope) ^ src\game\editor\editor.cpp:5038:3: note: Null pointer dereference pEnvelope->FindTopBottom(s_ActiveChannels); ^ src\game\editor\editor.cpp:5039:15: warning: Either the condition 'pEnvelope' is redundant or there is possible null pointer dereference: pEnvelope. [nullPointerRedundantCheck] float Top = pEnvelope->m_Top; ^ src\game\editor\editor.cpp:5056:7: note: Assuming that condition 'pEnvelope' is not redundant if(pEnvelope) ^ src\game\editor\editor.cpp:5039:15: note: Null pointer dereference float Top = pEnvelope->m_Top; ^ src\game\editor\editor.cpp:5040:18: warning: Either the condition 'pEnvelope' is redundant or there is possible null pointer dereference: pEnvelope. [nullPointerRedundantCheck] float Bottom = pEnvelope->m_Bottom; ^ src\game\editor\editor.cpp:5056:7: note: Assuming that condition 'pEnvelope' is not redundant if(pEnvelope) ^ src\game\editor\editor.cpp:5040:18: note: Null pointer dereference float Bottom = pEnvelope->m_Bottom; ^ src\game\editor\editor.cpp:5081:23: warning: Either the condition 'pEnvelope' is redundant or there is possible null pointer dereference: pEnvelope. [nullPointerRedundantCheck] for(int c = 0; c < pEnvelope->m_Channels; c++) ^ src\game\editor\editor.cpp:5056:7: note: Assuming that condition 'pEnvelope' is not redundant if(pEnvelope) ^ src\game\editor\editor.cpp:5081:23: note: Null pointer dereference for(int c = 0; c < pEnvelope->m_Channels; c++) ^ ```
2022-10-30 12:57:12 +00:00
m_ShowEnvelopePreview = SHOWENV_SELECTED;
m_pTooltip = "Press right mouse button to create a new point";
2008-01-12 12:27:55 +00:00
}
// render lines
{
2010-05-29 07:25:38 +00:00
UI()->ClipEnable(&View);
Graphics()->TextureClear();
2009-10-27 14:38:53 +00:00
Graphics()->LinesBegin();
2010-05-29 07:25:38 +00:00
for(int c = 0; c < pEnvelope->m_Channels; c++)
2008-01-12 12:27:55 +00:00
{
if(s_ActiveChannels & (1 << c))
Graphics()->SetColor(aColors[c].r, aColors[c].g, aColors[c].b, 1);
2008-01-12 12:27:55 +00:00
else
Graphics()->SetColor(aColors[c].r * 0.5f, aColors[c].g * 0.5f, aColors[c].b * 0.5f, 1);
2010-05-29 07:25:38 +00:00
float PrevX = 0;
ColorRGBA Channels;
pEnvelope->Eval(0.000001f, Channels);
float PrevValue = Channels[c];
2010-05-29 07:25:38 +00:00
int Steps = (int)((View.w / UI()->Screen()->w) * Graphics()->ScreenWidth());
2010-05-29 07:25:38 +00:00
for(int i = 1; i <= Steps; i++)
2008-01-12 12:27:55 +00:00
{
float a = i / (float)Steps;
pEnvelope->Eval(a * EndTime, Channels);
float v = Channels[c];
v = (v - Bottom) / (Top - Bottom);
2010-05-29 07:25:38 +00:00
IGraphics::CLineItem LineItem(View.x + PrevX * View.w, View.y + View.h - PrevValue * View.h, View.x + a * View.w, View.y + View.h - v * View.h);
2010-05-29 07:25:38 +00:00
Graphics()->LinesDraw(&LineItem, 1);
PrevX = a;
PrevValue = v;
2008-01-12 12:27:55 +00:00
}
}
2009-10-27 14:38:53 +00:00
Graphics()->LinesEnd();
UI()->ClipDisable();
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
2008-01-12 12:27:55 +00:00
// render curve options
2007-12-02 17:55:45 +00:00
{
for(int i = 0; i < (int)pEnvelope->m_vPoints.size() - 1; i++)
2008-01-12 12:27:55 +00:00
{
float t0 = pEnvelope->m_vPoints[i].m_Time / 1000.0f / EndTime;
float t1 = pEnvelope->m_vPoints[i + 1].m_Time / 1000.0f / EndTime;
2008-01-12 12:27:55 +00:00
2009-10-27 14:38:53 +00:00
CUIRect v;
v.x = CurveBar.x + (t0 + (t1 - t0) * 0.5f) * CurveBar.w;
2010-05-29 07:25:38 +00:00
v.y = CurveBar.y;
v.h = CurveBar.h;
v.w = CurveBar.h;
v.x -= v.w / 2;
void *pID = &pEnvelope->m_vPoints[i].m_Curvetype;
const char *apTypeName[] = {
"N", "L", "S", "F", "M"};
const char *pTypeName = "Invalid";
if(0 <= pEnvelope->m_vPoints[i].m_Curvetype && pEnvelope->m_vPoints[i].m_Curvetype < (int)std::size(apTypeName))
pTypeName = apTypeName[pEnvelope->m_vPoints[i].m_Curvetype];
if(DoButton_Editor(pID, pTypeName, 0, &v, 0, "Switch curve type"))
pEnvelope->m_vPoints[i].m_Curvetype = (pEnvelope->m_vPoints[i].m_Curvetype + 1) % NUM_CURVETYPES;
2008-01-12 12:27:55 +00:00
}
}
2010-05-29 07:25:38 +00:00
2008-01-12 12:27:55 +00:00
// render colorbar
2010-05-29 07:25:38 +00:00
if(ShowColorBar)
2008-01-12 12:27:55 +00:00
{
Graphics()->TextureClear();
2009-10-27 14:38:53 +00:00
Graphics()->QuadsBegin();
for(int i = 0; i < (int)pEnvelope->m_vPoints.size() - 1; i++)
2008-01-12 12:27:55 +00:00
{
float r0 = fx2f(pEnvelope->m_vPoints[i].m_aValues[0]);
float g0 = fx2f(pEnvelope->m_vPoints[i].m_aValues[1]);
float b0 = fx2f(pEnvelope->m_vPoints[i].m_aValues[2]);
float a0 = fx2f(pEnvelope->m_vPoints[i].m_aValues[3]);
float r1 = fx2f(pEnvelope->m_vPoints[i + 1].m_aValues[0]);
float g1 = fx2f(pEnvelope->m_vPoints[i + 1].m_aValues[1]);
float b1 = fx2f(pEnvelope->m_vPoints[i + 1].m_aValues[2]);
float a1 = fx2f(pEnvelope->m_vPoints[i + 1].m_aValues[3]);
2010-05-29 07:25:38 +00:00
IGraphics::CColorVertex Array[4] = {IGraphics::CColorVertex(0, r0, g0, b0, a0),
IGraphics::CColorVertex(1, r1, g1, b1, a1),
IGraphics::CColorVertex(2, r1, g1, b1, a1),
IGraphics::CColorVertex(3, r0, g0, b0, a0)};
2010-05-29 07:25:38 +00:00
Graphics()->SetColorVertex(Array, 4);
float x0 = pEnvelope->m_vPoints[i].m_Time / 1000.0f / EndTime;
float x1 = pEnvelope->m_vPoints[i + 1].m_Time / 1000.0f / EndTime;
2010-05-29 07:25:38 +00:00
2023-01-24 17:09:56 +00:00
IGraphics::CQuadItem QuadItem(ColorBar.x + x0 * ColorBar.w, ColorBar.y, (x1 - x0) * ColorBar.w, ColorBar.h);
2010-05-29 07:25:38 +00:00
Graphics()->QuadsDrawTL(&QuadItem, 1);
2008-01-12 12:27:55 +00:00
}
2009-10-27 14:38:53 +00:00
Graphics()->QuadsEnd();
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
2008-01-12 12:27:55 +00:00
// render handles
2015-03-30 03:06:08 +00:00
2015-03-30 09:44:32 +00:00
// keep track of last Env
static void *s_pID = nullptr;
2015-03-30 03:06:08 +00:00
2015-03-30 09:44:32 +00:00
// chars for textinput
static char s_aStrCurTime[32] = "0.000";
static char s_aStrCurValue[32] = "0.000";
2015-03-30 03:06:08 +00:00
if(CurrentEnvelopeSwitched)
{
s_pID = nullptr;
// update displayed text
str_copy(s_aStrCurTime, "0.000");
str_copy(s_aStrCurValue, "0.000");
}
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
int CurrentValue = 0, CurrentTime = 0;
Graphics()->TextureClear();
2009-10-27 14:38:53 +00:00
Graphics()->QuadsBegin();
2010-05-29 07:25:38 +00:00
for(int c = 0; c < pEnvelope->m_Channels; c++)
2008-01-12 12:27:55 +00:00
{
if(!(s_ActiveChannels & (1 << c)))
2008-01-12 12:27:55 +00:00
continue;
2010-05-29 07:25:38 +00:00
for(size_t i = 0; i < pEnvelope->m_vPoints.size(); i++)
2008-01-12 12:27:55 +00:00
{
float x0 = pEnvelope->m_vPoints[i].m_Time / 1000.0f / EndTime;
float y0 = (fx2f(pEnvelope->m_vPoints[i].m_aValues[c]) - Bottom) / (Top - Bottom);
2010-05-29 07:25:38 +00:00
CUIRect Final;
Final.x = View.x + x0 * View.w;
Final.y = View.y + View.h - y0 * View.h;
2010-05-29 07:25:38 +00:00
Final.x -= 2.0f;
Final.y -= 2.0f;
Final.w = 4.0f;
Final.h = 4.0f;
void *pID = &pEnvelope->m_vPoints[i].m_aValues[c];
2008-01-12 12:27:55 +00:00
2010-05-29 07:25:38 +00:00
if(UI()->MouseInside(&Final))
UI()->SetHotItem(pID);
2010-05-29 07:25:38 +00:00
float ColorMod = 1.0f;
if(UI()->CheckActiveItem(pID))
2008-01-12 12:27:55 +00:00
{
2009-10-27 14:38:53 +00:00
if(!UI()->MouseButton(0))
2008-01-12 12:27:55 +00:00
{
2011-08-15 18:12:31 +00:00
m_SelectedQuadEnvelope = -1;
m_SelectedEnvelopePoint = -1;
2011-08-14 14:31:48 +00:00
UI()->SetActiveItem(nullptr);
2008-01-12 12:27:55 +00:00
}
else
2011-08-11 08:59:14 +00:00
{
if(Input()->ShiftIsPressed())
2008-01-12 12:27:55 +00:00
{
if(i != 0)
{
if(Input()->ModifierIsPressed())
pEnvelope->m_vPoints[i].m_Time += (int)((m_MouseDeltaX));
2010-07-18 11:44:30 +00:00
else
pEnvelope->m_vPoints[i].m_Time += (int)((m_MouseDeltaX * TimeScale) * 1000.0f);
if(pEnvelope->m_vPoints[i].m_Time < pEnvelope->m_vPoints[i - 1].m_Time)
pEnvelope->m_vPoints[i].m_Time = pEnvelope->m_vPoints[i - 1].m_Time + 1;
if(i + 1 != pEnvelope->m_vPoints.size() && pEnvelope->m_vPoints[i].m_Time > pEnvelope->m_vPoints[i + 1].m_Time)
pEnvelope->m_vPoints[i].m_Time = pEnvelope->m_vPoints[i + 1].m_Time - 1;
2008-01-12 12:27:55 +00:00
}
}
else
{
if(Input()->ModifierIsPressed())
pEnvelope->m_vPoints[i].m_aValues[c] -= f2fx(m_MouseDeltaY * 0.001f);
else
pEnvelope->m_vPoints[i].m_aValues[c] -= f2fx(m_MouseDeltaY * ValueScale);
}
2011-08-15 18:12:31 +00:00
m_SelectedQuadEnvelope = m_SelectedEnvelope;
m_ShowEnvelopePreview = SHOWENV_SELECTED;
2011-08-15 18:12:31 +00:00
m_SelectedEnvelopePoint = i;
2015-03-30 09:44:32 +00:00
m_Map.m_Modified = true;
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
ColorMod = 100.0f;
Graphics()->SetColor(1, 1, 1, 1);
2008-01-12 12:27:55 +00:00
}
else if(UI()->HotItem() == pID)
2008-01-12 12:27:55 +00:00
{
2009-10-27 14:38:53 +00:00
if(UI()->MouseButton(0))
2008-01-12 12:27:55 +00:00
{
2022-06-15 17:34:41 +00:00
s_vSelection.clear();
s_vSelection.push_back(i);
UI()->SetActiveItem(pID);
2015-03-30 09:44:32 +00:00
// track it
s_pID = pID;
2008-01-12 12:27:55 +00:00
}
// remove point
2015-03-30 09:44:32 +00:00
if(UI()->MouseButtonClicked(1))
{
if(s_pID == pID)
{
s_pID = nullptr;
// update displayed text
str_copy(s_aStrCurTime, "0.000");
str_copy(s_aStrCurValue, "0.000");
}
pEnvelope->m_vPoints.erase(pEnvelope->m_vPoints.begin() + i);
m_Map.m_Modified = true;
}
2010-05-29 07:25:38 +00:00
m_ShowEnvelopePreview = SHOWENV_SELECTED;
2010-05-29 07:25:38 +00:00
ColorMod = 100.0f;
Graphics()->SetColor(1, 0.75f, 0.75f, 1);
2018-02-04 15:00:47 +00:00
m_pTooltip = "Left mouse to drag. Hold ctrl to be more precise. Hold shift to alter time point as well. Right click to delete.";
2008-01-12 12:27:55 +00:00
}
if(pID == s_pID && (Input()->KeyIsPressed(KEY_RETURN) || Input()->KeyIsPressed(KEY_KP_ENTER)))
2015-03-30 09:44:32 +00:00
{
if(i != 0)
{
pEnvelope->m_vPoints[i].m_Time = str_tofloat(s_aStrCurTime) * 1000.0f;
2015-03-30 03:06:08 +00:00
if(pEnvelope->m_vPoints[i].m_Time < pEnvelope->m_vPoints[i - 1].m_Time)
pEnvelope->m_vPoints[i].m_Time = pEnvelope->m_vPoints[i - 1].m_Time + 1;
if(i + 1 != pEnvelope->m_vPoints.size() && pEnvelope->m_vPoints[i].m_Time > pEnvelope->m_vPoints[i + 1].m_Time)
pEnvelope->m_vPoints[i].m_Time = pEnvelope->m_vPoints[i + 1].m_Time - 1;
2015-03-30 09:44:32 +00:00
}
else
pEnvelope->m_vPoints[i].m_Time = 0.0f;
2015-03-30 03:06:08 +00:00
str_format(s_aStrCurTime, sizeof(s_aStrCurTime), "%.3f", pEnvelope->m_vPoints[i].m_Time / 1000.0f);
pEnvelope->m_vPoints[i].m_aValues[c] = f2fx(str_tofloat(s_aStrCurValue));
2015-03-30 09:44:32 +00:00
}
2015-03-30 03:06:08 +00:00
2022-05-30 20:44:12 +00:00
if(UI()->CheckActiveItem(pID))
2015-03-30 09:44:32 +00:00
{
CurrentTime = pEnvelope->m_vPoints[i].m_Time;
CurrentValue = pEnvelope->m_vPoints[i].m_aValues[c];
2015-03-30 03:06:08 +00:00
2015-03-30 09:44:32 +00:00
// update displayed text
str_format(s_aStrCurTime, sizeof(s_aStrCurTime), "%.3f", CurrentTime / 1000.0f);
2015-03-30 09:44:32 +00:00
str_format(s_aStrCurValue, sizeof(s_aStrCurValue), "%.3f", fx2f(CurrentValue));
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
if(m_SelectedQuadEnvelope == m_SelectedEnvelope && m_SelectedEnvelopePoint == (int)i)
2011-08-15 18:12:31 +00:00
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
else
Graphics()->SetColor(aColors[c].r * ColorMod, aColors[c].g * ColorMod, aColors[c].b * ColorMod, 1.0f);
2010-05-29 07:25:38 +00:00
IGraphics::CQuadItem QuadItem(Final.x, Final.y, Final.w, Final.h);
Graphics()->QuadsDrawTL(&QuadItem, 1);
2008-01-12 12:27:55 +00:00
}
}
2009-10-27 14:38:53 +00:00
Graphics()->QuadsEnd();
2008-01-12 12:27:55 +00:00
2015-03-30 09:44:32 +00:00
CUIRect ToolBar1;
CUIRect ToolBar2;
ToolBar.VSplitMid(&ToolBar1, &ToolBar2);
ToolBar1.VSplitRight(3.0f, &ToolBar1, nullptr);
ToolBar2.VSplitRight(3.0f, &ToolBar2, nullptr);
if(ToolBar.w > ToolBar.h * 21)
{
CUIRect Label1;
CUIRect Label2;
ToolBar1.VSplitMid(&Label1, &ToolBar1);
ToolBar2.VSplitMid(&Label2, &ToolBar2);
Label1.VSplitRight(3.0f, &Label1, nullptr);
Label2.VSplitRight(3.0f, &Label2, nullptr);
UI()->DoLabel(&Label1, "Value:", 10.0f, TEXTALIGN_RIGHT);
UI()->DoLabel(&Label2, "Time (in s):", 10.0f, TEXTALIGN_RIGHT);
}
2015-03-30 03:06:08 +00:00
static float s_ValNumber = 0;
DoEditBox(&s_ValNumber, &ToolBar1, s_aStrCurValue, sizeof(s_aStrCurValue), 10.0f, &s_ValNumber, false, IGraphics::CORNER_ALL, "The value of the selected envelope point");
static float s_TimeNumber = 0;
DoEditBox(&s_TimeNumber, &ToolBar2, s_aStrCurTime, sizeof(s_aStrCurTime), 10.0f, &s_TimeNumber, false, IGraphics::CORNER_ALL, "The time of the selected envelope point");
2007-12-02 17:55:45 +00:00
}
}
2008-01-12 12:27:55 +00:00
}
void CEditor::RenderServerSettingsEditor(CUIRect View, bool ShowServerSettingsEditorLast)
{
static int s_CommandSelectedIndex = -1;
CUIRect ToolBar;
View.HSplitTop(20.0f, &ToolBar, &View);
ToolBar.Margin(2.0f, &ToolBar);
// do the toolbar
{
CUIRect Button;
// command line
ToolBar.VSplitLeft(5.0f, nullptr, &Button);
UI()->DoLabel(&Button, "Command:", 12.0f, TEXTALIGN_LEFT);
Button.VSplitLeft(70.0f, nullptr, &Button);
Button.VSplitLeft(180.0f, &Button, nullptr);
static int s_ClearButton = 0;
DoClearableEditBox(&m_CommandBox, &s_ClearButton, &Button, m_aSettingsCommand, sizeof(m_aSettingsCommand), 12.0f, &m_CommandBox, false, IGraphics::CORNER_ALL);
if(!ShowServerSettingsEditorLast) // Just activated
UI()->SetActiveItem(&m_CommandBox);
// buttons
ToolBar.VSplitRight(50.0f, &ToolBar, &Button);
static int s_AddButton = 0;
if(DoButton_Editor(&s_AddButton, "Add", 0, &Button, 0, "Add a command to command list.") || ((Input()->KeyPress(KEY_RETURN) || Input()->KeyPress(KEY_KP_ENTER)) && UI()->LastActiveItem() == &m_CommandBox && m_Dialog == DIALOG_NONE))
{
if(m_aSettingsCommand[0] != 0 && str_find(m_aSettingsCommand, " "))
{
bool Found = false;
for(const auto &Setting : m_Map.m_vSettings)
if(!str_comp(Setting.m_aCommand, m_aSettingsCommand))
{
Found = true;
break;
}
if(!Found)
{
CEditorMap::CSetting Setting;
str_copy(Setting.m_aCommand, m_aSettingsCommand, sizeof(Setting.m_aCommand));
m_Map.m_vSettings.push_back(Setting);
s_CommandSelectedIndex = m_Map.m_vSettings.size() - 1;
}
}
UI()->SetActiveItem(&m_CommandBox);
}
if(!m_Map.m_vSettings.empty() && s_CommandSelectedIndex >= 0 && (size_t)s_CommandSelectedIndex < m_Map.m_vSettings.size())
{
ToolBar.VSplitRight(50.0f, &ToolBar, &Button);
Button.VSplitRight(5.0f, &Button, nullptr);
static int s_ModButton = 0;
if(DoButton_Editor(&s_ModButton, "Mod", 0, &Button, 0, "Modify a command from the command list.") || (Input()->KeyPress(KEY_M) && UI()->LastActiveItem() != &m_CommandBox && m_Dialog == DIALOG_NONE))
{
if(str_comp(m_Map.m_vSettings[s_CommandSelectedIndex].m_aCommand, m_aSettingsCommand) != 0 && m_aSettingsCommand[0] != 0 && str_find(m_aSettingsCommand, " "))
{
bool Found = false;
int i;
for(i = 0; i < (int)m_Map.m_vSettings.size(); i++)
if(i != s_CommandSelectedIndex && !str_comp(m_Map.m_vSettings[i].m_aCommand, m_aSettingsCommand))
{
Found = true;
break;
}
if(Found)
{
m_Map.m_vSettings.erase(m_Map.m_vSettings.begin() + s_CommandSelectedIndex);
s_CommandSelectedIndex = i > s_CommandSelectedIndex ? i - 1 : i;
}
else
{
str_copy(m_Map.m_vSettings[s_CommandSelectedIndex].m_aCommand, m_aSettingsCommand, sizeof(m_Map.m_vSettings[s_CommandSelectedIndex].m_aCommand));
}
}
UI()->SetActiveItem(&m_CommandBox);
}
ToolBar.VSplitRight(50.0f, &ToolBar, &Button);
Button.VSplitRight(5.0f, &Button, nullptr);
static int s_DelButton = 0;
if(DoButton_Editor(&s_DelButton, "Del", 0, &Button, 0, "Delete a command from the command list.") || (Input()->KeyPress(KEY_DELETE) && UI()->LastActiveItem() != &m_CommandBox && m_Dialog == DIALOG_NONE))
{
m_Map.m_vSettings.erase(m_Map.m_vSettings.begin() + s_CommandSelectedIndex);
if(s_CommandSelectedIndex >= (int)m_Map.m_vSettings.size())
s_CommandSelectedIndex = m_Map.m_vSettings.size() - 1;
if(s_CommandSelectedIndex >= 0)
str_copy(m_aSettingsCommand, m_Map.m_vSettings[s_CommandSelectedIndex].m_aCommand, sizeof(m_aSettingsCommand));
UI()->SetActiveItem(&m_CommandBox);
}
ToolBar.VSplitRight(25.0f, &ToolBar, &Button);
Button.VSplitRight(5.0f, &Button, nullptr);
static int s_DownButton = 0;
if(s_CommandSelectedIndex < (int)m_Map.m_vSettings.size() - 1 && DoButton_Editor(&s_DownButton, "", 0, &Button, 0, "Move command down"))
{
std::swap(m_Map.m_vSettings[s_CommandSelectedIndex], m_Map.m_vSettings[s_CommandSelectedIndex + 1]);
s_CommandSelectedIndex++;
}
ToolBar.VSplitRight(25.0f, &ToolBar, &Button);
Button.VSplitRight(5.0f, &Button, nullptr);
static int s_UpButton = 0;
if(s_CommandSelectedIndex > 0 && DoButton_Editor(&s_UpButton, "", 0, &Button, 0, "Move command up"))
{
std::swap(m_Map.m_vSettings[s_CommandSelectedIndex], m_Map.m_vSettings[s_CommandSelectedIndex - 1]);
s_CommandSelectedIndex--;
}
}
}
View.HSplitTop(2.0f, nullptr, &View);
RenderBackground(View, m_CheckerTexture, 32.0f, 0.1f);
CUIRect ListBox;
View.Margin(1.0f, &ListBox);
const float ButtonHeight = 15.0f;
const float ButtonMargin = 2.0f;
static CScrollRegion s_ScrollRegion;
vec2 ScrollOffset(0.0f, 0.0f);
CScrollRegionParams ScrollParams;
ScrollParams.m_ScrollUnit = (ButtonHeight + ButtonMargin) * 5.0f;
s_ScrollRegion.Begin(&ListBox, &ScrollOffset, &ScrollParams);
ListBox.y += ScrollOffset.y;
for(size_t i = 0; i < m_Map.m_vSettings.size(); i++)
{
CUIRect Button;
ListBox.HSplitTop(ButtonHeight, &Button, &ListBox);
ListBox.HSplitTop(ButtonMargin, nullptr, &ListBox);
Button.VSplitLeft(5.0f, nullptr, &Button);
if(s_ScrollRegion.AddRect(Button))
{
if(DoButton_MenuItem(&m_Map.m_vSettings[i], m_Map.m_vSettings[i].m_aCommand, s_CommandSelectedIndex >= 0 && (size_t)s_CommandSelectedIndex == i, &Button, 0, nullptr))
{
s_CommandSelectedIndex = i;
str_copy(m_aSettingsCommand, m_Map.m_vSettings[i].m_aCommand);
UI()->SetActiveItem(&m_CommandBox);
}
}
}
s_ScrollRegion.End();
}
2020-02-28 15:25:27 +00:00
int CEditor::PopupMenuFile(CEditor *pEditor, CUIRect View, void *pContext)
2008-01-13 13:42:59 +00:00
{
2010-05-29 07:25:38 +00:00
static int s_NewMapButton = 0;
static int s_SaveButton = 0;
static int s_SaveAsButton = 0;
static int s_SaveCopyButton = 0;
2010-05-29 07:25:38 +00:00
static int s_OpenButton = 0;
2016-08-23 01:08:36 +00:00
static int s_OpenCurrentMapButton = 0;
2010-05-29 07:25:38 +00:00
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)"))
2010-05-29 07:25:38 +00:00
{
if(pEditor->HasUnsavedData())
{
pEditor->m_PopupEventType = POPEVENT_NEW;
pEditor->m_PopupEventActivated = true;
}
else
{
pEditor->Reset();
pEditor->m_aFileName[0] = 0;
}
2008-01-13 22:03:32 +00:00
return 1;
}
2008-01-13 13:42:59 +00:00
2010-05-29 07:25:38 +00:00
View.HSplitTop(10.0f, &Slot, &View);
View.HSplitTop(12.0f, &Slot, &View);
if(pEditor->DoButton_MenuItem(&s_OpenButton, "Load", 0, &Slot, 0, "Opens a map for editing (ctrl+l)"))
2008-01-13 22:03:32 +00:00
{
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);
2008-01-13 22:03:32 +00:00
return 1;
}
2008-01-13 13:42:59 +00:00
2016-08-23 01:08:36 +00:00
View.HSplitTop(2.0f, &Slot, &View);
View.HSplitTop(12.0f, &Slot, &View);
2020-05-15 15:05:45 +00:00
if(pEditor->DoButton_MenuItem(&s_OpenCurrentMapButton, "Load Current Map", 0, &Slot, 0, "Opens the current in game map for editing (ctrl+alt+l)"))
2016-08-21 19:14:41 +00:00
{
if(pEditor->HasUnsavedData())
{
2016-08-23 01:08:36 +00:00
pEditor->m_PopupEventType = POPEVENT_LOADCURRENT;
2016-08-21 19:14:41 +00:00
pEditor->m_PopupEventActivated = true;
}
else
2016-08-23 01:08:36 +00:00
{
pEditor->LoadCurrentMap();
}
2016-08-21 19:14:41 +00:00
return 1;
}
2008-01-13 13:42:59 +00:00
2010-05-29 07:25:38 +00:00
View.HSplitTop(10.0f, &Slot, &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;
}
2010-05-29 07:25:38 +00:00
View.HSplitTop(10.0f, &Slot, &View);
View.HSplitTop(12.0f, &Slot, &View);
if(pEditor->DoButton_MenuItem(&s_SaveButton, "Save", 0, &Slot, 0, "Saves the current map (ctrl+s)"))
2008-01-13 22:03:32 +00:00
{
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;
}
2010-05-29 07:25:38 +00:00
else
pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", pEditor->CallbackSaveMap, pEditor);
2008-01-13 22:03:32 +00:00
return 1;
}
2008-01-13 13:42:59 +00:00
2010-05-29 07:25:38 +00:00
View.HSplitTop(2.0f, &Slot, &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)"))
2008-01-13 22:03:32 +00:00
{
pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", pEditor->CallbackSaveMap, pEditor);
2008-01-13 22:03:32 +00:00
return 1;
}
2010-05-29 07:25:38 +00:00
View.HSplitTop(2.0f, &Slot, &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;
}
2010-05-29 07:25:38 +00:00
View.HSplitTop(10.0f, &Slot, &View);
View.HSplitTop(12.0f, &Slot, &View);
2011-03-20 16:04:03 +00:00
if(pEditor->DoButton_MenuItem(&s_ExitButton, "Exit", 0, &Slot, 0, "Exits from the editor"))
2009-05-31 09:44:20 +00:00
{
if(pEditor->HasUnsavedData())
{
pEditor->m_PopupEventType = POPEVENT_EXIT;
pEditor->m_PopupEventActivated = true;
}
else
g_Config.m_ClEditor = 0;
2009-05-31 09:44:20 +00:00
return 1;
2010-05-29 07:25:38 +00:00
}
2008-01-13 22:03:32 +00:00
return 0;
2008-01-13 13:42:59 +00:00
}
2008-01-13 22:03:32 +00:00
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;
}
2010-05-29 07:25:38 +00:00
void CEditor::RenderMenubar(CUIRect MenuBar)
2008-01-13 22:03:32 +00:00
{
CUIRect FileButton;
static int s_FileButton = 0;
MenuBar.VSplitLeft(60.0f, &FileButton, &MenuBar);
if(DoButton_Menu(&s_FileButton, "File", 0, &FileButton, 0, nullptr))
UiInvokePopupMenu(&s_FileButton, 1, FileButton.x, FileButton.y + FileButton.h - 1.0f, 120.0f, 152.0f, PopupMenuFile, this);
MenuBar.VSplitLeft(5.0f, nullptr, &MenuBar);
CUIRect ToolsButton;
static int s_ToolsButton = 0;
MenuBar.VSplitLeft(60.0f, &ToolsButton, &MenuBar);
if(DoButton_Menu(&s_ToolsButton, "Tools", 0, &ToolsButton, 0, nullptr))
UiInvokePopupMenu(&s_ToolsButton, 1, ToolsButton.x, ToolsButton.y + ToolsButton.h - 1.0f, 200.0f, 22.0f, PopupMenuTools, this);
2008-01-13 22:03:32 +00:00
CUIRect Info, Close;
MenuBar.VSplitLeft(5.0f, nullptr, &MenuBar);
MenuBar.VSplitRight(20.0f, &MenuBar, &Close);
Close.VSplitLeft(5.0f, nullptr, &Close);
MenuBar.VSplitLeft(MenuBar.w * 0.75f, &MenuBar, &Info);
char aBuf[128];
2011-03-20 16:04:03 +00:00
str_format(aBuf, sizeof(aBuf), "File: %s", m_aFileName);
2022-03-11 16:34:48 +00:00
UI()->DoLabel(&MenuBar, aBuf, 10.0f, TEXTALIGN_LEFT);
2017-09-03 06:53:54 +00:00
char aTimeStr[6];
2017-09-03 06:48:21 +00:00
str_timestamp_format(aTimeStr, sizeof(aTimeStr), "%H:%M");
str_format(aBuf, sizeof(aBuf), "X: %i, Y: %i, Z: %.1f, A: %.1f, G: %i %s", (int)UI()->MouseWorldX() / 32, (int)UI()->MouseWorldY() / 32, m_Zoom, m_AnimateSpeed, m_GridFactor, aTimeStr);
2022-03-11 16:34:48 +00:00
UI()->DoLabel(&Info, aBuf, 10.0f, TEXTALIGN_RIGHT);
static int s_CloseButton = 0;
2020-10-06 10:25:10 +00:00
if(DoButton_Editor(&s_CloseButton, "×", 0, &Close, 0, "Exits from the editor", 0) || (m_Dialog == DIALOG_NONE && !UiPopupOpen() && !m_PopupEventActivated && Input()->KeyPress(KEY_ESCAPE)))
g_Config.m_ClEditor = 0;
2008-01-13 22:03:32 +00:00
}
2008-01-13 13:42:59 +00:00
2010-05-29 07:25:38 +00:00
void CEditor::Render()
2008-01-12 12:27:55 +00:00
{
// basic start
2022-03-20 17:04:00 +00:00
Graphics()->Clear(0.0f, 0.0f, 0.0f);
2010-05-29 07:25:38 +00:00
CUIRect View = *UI()->Screen();
2021-09-22 19:48:55 +00:00
UI()->MapScreen();
2010-05-29 07:25:38 +00:00
float Width = View.w;
float Height = View.h;
2008-01-12 12:27:55 +00:00
// reset tip
m_pTooltip = nullptr;
2010-05-29 07:25:38 +00:00
if(m_EditBoxActive)
--m_EditBoxActive;
2008-01-12 12:27:55 +00:00
// render checker
RenderBackground(View, m_CheckerTexture, 32.0f, 1.0f);
2010-05-29 07:25:38 +00:00
CUIRect MenuBar, CModeBar, ToolBar, StatusBar, ExtraEditor, ToolBox;
m_ShowPicker = Input()->KeyIsPressed(KEY_SPACE) && m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && UI()->LastActiveItem() != &m_CommandBox && m_vSelectedLayers.size() == 1;
2010-05-29 07:25:38 +00:00
if(m_GuiActive)
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
View.HSplitTop(16.0f, &MenuBar, &View);
View.HSplitTop(53.0f, &ToolBar, &View);
View.VSplitLeft(100.0f, &ToolBox, &View);
2010-05-29 07:25:38 +00:00
View.HSplitBottom(16.0f, &View, &StatusBar);
if(m_ShowEnvelopeEditor && !m_ShowPicker)
2008-03-29 11:44:03 +00:00
{
2017-03-21 10:24:44 +00:00
float Size = 125.0f;
2010-05-29 07:25:38 +00:00
if(m_ShowEnvelopeEditor == 2)
2017-03-21 10:24:44 +00:00
Size *= 2.0f;
2010-05-29 07:25:38 +00:00
else if(m_ShowEnvelopeEditor == 3)
2017-03-21 10:24:44 +00:00
Size *= 3.0f;
View.HSplitBottom(Size, &View, &ExtraEditor);
2008-03-29 11:44:03 +00:00
}
if(m_ShowServerSettingsEditor && !m_ShowPicker)
View.HSplitBottom(250.0f, &View, &ExtraEditor);
2008-03-29 11:44:03 +00:00
}
else
{
// hack to get keyboard inputs from toolbar even when GUI is not active
ToolBar.x = -100;
ToolBar.y = -100;
ToolBar.w = 50;
ToolBar.h = 50;
}
2010-05-29 07:25:38 +00:00
2008-03-29 11:44:03 +00:00
// a little hack for now
2010-05-29 07:25:38 +00:00
if(m_Mode == MODE_LAYERS)
2018-04-04 19:41:14 +00:00
DoMapEditor(View);
2010-05-29 07:25:38 +00:00
// do zooming
if(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0)
{
if(Input()->KeyPress(KEY_KP_MINUS))
ChangeZoom(50.0f);
if(Input()->KeyPress(KEY_KP_PLUS))
ChangeZoom(-50.0f);
if(Input()->KeyPress(KEY_KP_MULTIPLY))
{
m_EditorOffsetX = 0;
m_EditorOffsetY = 0;
SetZoom(100.0f);
}
}
2018-10-02 01:52:01 +00:00
for(int i = KEY_1; i <= KEY_0; i++)
{
if(m_Dialog != DIALOG_NONE || m_EditBoxActive != 0)
break;
if(Input()->KeyPress(i))
{
int Slot = i - KEY_1;
if(Input()->ModifierIsPressed() && !m_Brush.IsEmpty())
2018-10-02 01:52:01 +00:00
{
dbg_msg("editor", "saving current brush to %d", Slot);
if(m_apSavedBrushes[Slot])
{
CLayerGroup *pPrev = m_apSavedBrushes[Slot];
for(auto &pLayer : pPrev->m_vpLayers)
2018-10-02 01:52:01 +00:00
{
if(pLayer->m_BrushRefCount == 1)
delete pLayer;
2018-10-02 01:52:01 +00:00
else
pLayer->m_BrushRefCount--;
2018-10-02 01:52:01 +00:00
}
}
delete m_apSavedBrushes[Slot];
m_apSavedBrushes[Slot] = new CLayerGroup(m_Brush);
for(auto &pLayer : m_apSavedBrushes[Slot]->m_vpLayers)
pLayer->m_BrushRefCount++;
2018-10-02 01:52:01 +00:00
}
else if(m_apSavedBrushes[Slot])
{
dbg_msg("editor", "loading brush from slot %d", Slot);
CLayerGroup *pNew = m_apSavedBrushes[Slot];
for(auto &pLayer : pNew->m_vpLayers)
pLayer->m_BrushRefCount++;
2018-10-02 01:52:01 +00:00
m_Brush = *pNew;
}
}
}
float Brightness = 0.25f;
2010-05-29 07:25:38 +00:00
if(m_GuiActive)
2008-03-29 11:44:03 +00:00
{
RenderBackground(MenuBar, m_BackgroundTexture, 128.0f, Brightness * 0);
2010-05-29 07:25:38 +00:00
MenuBar.Margin(2.0f, &MenuBar);
2008-03-29 11:44:03 +00:00
RenderBackground(ToolBox, m_BackgroundTexture, 128.0f, Brightness);
2010-05-29 07:25:38 +00:00
ToolBox.Margin(2.0f, &ToolBox);
RenderBackground(ToolBar, m_BackgroundTexture, 128.0f, Brightness);
2010-05-29 07:25:38 +00:00
ToolBar.Margin(2.0f, &ToolBar);
ToolBar.VSplitLeft(100.0f, &CModeBar, &ToolBar);
RenderBackground(StatusBar, m_BackgroundTexture, 128.0f, Brightness);
2010-05-29 07:25:38 +00:00
StatusBar.Margin(2.0f, &StatusBar);
}
2007-05-22 15:03:32 +00:00
2019-04-05 23:15:02 +00:00
// show mentions
if(m_GuiActive && m_Mentions)
{
char aBuf[16];
if(m_Mentions == 1)
{
2019-04-06 11:46:47 +00:00
str_copy(aBuf, Localize("1 new mention"), sizeof(aBuf));
2019-04-05 23:15:02 +00:00
}
else if(m_Mentions <= 9)
{
2019-04-06 11:46:47 +00:00
str_format(aBuf, sizeof(aBuf), Localize("%d new mentions"), m_Mentions);
2019-04-05 23:15:02 +00:00
}
else
{
2019-04-06 11:46:47 +00:00
str_copy(aBuf, Localize("9+ new mentions"), sizeof(aBuf));
2019-04-05 23:15:02 +00:00
}
TextRender()->TextColor(1.0f, 0.0f, 0.0f, 1.0f);
TextRender()->Text(5.0f, 27.0f, 10.0f, aBuf, -1.0f);
2019-04-05 23:15:02 +00:00
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
}
// do the toolbar
if(m_Mode == MODE_LAYERS)
DoToolbar(ToolBar);
2010-05-29 07:25:38 +00:00
if(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0)
{
const bool ModPressed = Input()->ModifierIsPressed();
const bool ShiftPressed = Input()->ShiftIsPressed();
const bool AltPressed = Input()->AltIsPressed();
// ctrl+n to create new map
if(Input()->KeyPress(KEY_N) && ModPressed)
{
if(HasUnsavedData())
{
if(!m_PopupEventWasActivated)
{
m_PopupEventType = POPEVENT_NEW;
m_PopupEventActivated = true;
}
}
else
{
Reset();
m_aFileName[0] = 0;
}
}
// ctrl+a to append map
if(Input()->KeyPress(KEY_A) && ModPressed)
{
InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Append map", "Append", "maps", "", CallbackAppendMap, this);
}
// ctrl+o or ctrl+l to open
if((Input()->KeyPress(KEY_O) || Input()->KeyPress(KEY_L)) && ModPressed)
{
if(ShiftPressed)
{
if(HasUnsavedData())
{
if(!m_PopupEventWasActivated)
{
m_PopupEventType = POPEVENT_LOADCURRENT;
m_PopupEventActivated = true;
}
}
else
{
LoadCurrentMap();
}
}
else
{
if(HasUnsavedData())
{
if(!m_PopupEventWasActivated)
{
m_PopupEventType = POPEVENT_LOAD;
m_PopupEventActivated = true;
}
}
else
{
InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Load map", "Load", "maps", "", CallbackOpenMap, this);
}
}
}
// ctrl+shift+alt+s to save as
if(Input()->KeyPress(KEY_S) && ModPressed && ShiftPressed && AltPressed)
InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", CallbackSaveCopyMap, this);
// ctrl+shift+s to save as
else if(Input()->KeyPress(KEY_S) && ModPressed && ShiftPressed)
InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", CallbackSaveMap, this);
// ctrl+s to save
else if(Input()->KeyPress(KEY_S) && ModPressed)
{
if(m_aFileName[0] && m_ValidSaveFilename)
{
if(!m_PopupEventWasActivated)
{
str_copy(m_aFileSaveName, m_aFileName, sizeof(m_aFileSaveName));
CallbackSaveMap(m_aFileSaveName, IStorage::TYPE_SAVE, this);
}
}
else
InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", CallbackSaveMap, this);
}
}
if(m_GuiActive)
{
2019-03-18 01:34:24 +00:00
if(!m_ShowPicker)
2008-01-12 12:27:55 +00:00
{
2019-03-18 01:34:24 +00:00
if(m_ShowEnvelopeEditor || m_ShowServerSettingsEditor)
{
RenderBackground(ExtraEditor, m_BackgroundTexture, 128.0f, Brightness);
2019-03-18 01:34:24 +00:00
ExtraEditor.Margin(2.0f, &ExtraEditor);
}
2014-01-19 03:02:01 +00:00
}
2010-05-29 07:25:38 +00:00
2018-04-04 19:41:14 +00:00
if(m_Mode == MODE_LAYERS)
RenderLayers(ToolBox);
2018-04-04 19:41:14 +00:00
else if(m_Mode == MODE_IMAGES)
{
RenderImagesList(ToolBox);
RenderSelectedImage(View);
}
2018-04-04 19:41:14 +00:00
else if(m_Mode == MODE_SOUNDS)
RenderSounds(ToolBox);
2018-04-04 19:41:14 +00:00
}
2008-03-29 11:44:03 +00:00
2021-09-22 19:48:55 +00:00
UI()->MapScreen();
2008-01-12 12:27:55 +00:00
2010-05-29 07:25:38 +00:00
if(m_GuiActive)
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
RenderMenubar(MenuBar);
RenderModebar(CModeBar);
if(!m_ShowPicker)
{
if(m_ShowEnvelopeEditor)
RenderEnvelopeEditor(ExtraEditor);
static bool s_ShowServerSettingsEditorLast = false;
if(m_ShowServerSettingsEditor)
{
RenderServerSettingsEditor(ExtraEditor, s_ShowServerSettingsEditorLast);
}
s_ShowServerSettingsEditorLast = m_ShowServerSettingsEditor;
}
2008-01-12 12:27:55 +00:00
}
2008-01-13 16:30:30 +00:00
2010-05-29 07:25:38 +00:00
if(m_Dialog == DIALOG_FILE)
2008-01-13 22:03:32 +00:00
{
2010-05-29 07:25:38 +00:00
static int s_NullUiTarget = 0;
UI()->SetHotItem(&s_NullUiTarget);
RenderFileDialog();
2008-01-13 22:03:32 +00:00
}
if(m_PopupEventActivated)
{
static int s_PopupID = 0;
UiInvokePopupMenu(&s_PopupID, 0, Width / 2.0f - 200.0f, Height / 2.0f - 100.0f, 400.0f, 200.0f, PopupEvent);
m_PopupEventActivated = false;
m_PopupEventWasActivated = true;
}
UiDoPopupMenu();
2010-05-29 07:25:38 +00:00
if(m_Dialog == DIALOG_NONE && !m_MouseInsidePopup && (!m_GuiActive || UI()->MouseInside(&View)))
2020-09-24 16:52:31 +00:00
{
if(Input()->KeyPress(KEY_MOUSE_WHEEL_DOWN))
ChangeZoom(20.0f);
if(Input()->KeyPress(KEY_MOUSE_WHEEL_UP))
ChangeZoom(-20.0f);
2020-09-24 16:52:31 +00:00
}
UpdateZoom();
2020-09-24 16:52:31 +00:00
2010-05-29 07:25:38 +00:00
if(m_GuiActive)
RenderStatusbar(StatusBar);
//
2010-05-29 07:25:38 +00:00
if(g_Config.m_EdShowkeys)
{
2021-09-22 19:48:55 +00:00
UI()->MapScreen();
2010-05-29 07:25:38 +00:00
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, View.x + 10, View.y + View.h - 24 - 10, 24.0f, TEXTFLAG_RENDER);
2010-05-29 07:25:38 +00:00
int NKeys = 0;
for(int i = 0; i < KEY_LAST; i++)
{
if(Input()->KeyIsPressed(i))
{
2010-05-29 07:25:38 +00:00
if(NKeys)
TextRender()->TextEx(&Cursor, " + ", -1);
TextRender()->TextEx(&Cursor, Input()->KeyName(i), -1);
NKeys++;
}
}
}
2010-05-29 07:25:38 +00:00
if(m_ShowMousePointer)
2008-09-01 18:38:08 +00:00
{
// render butt ugly mouse cursor
2009-10-27 14:38:53 +00:00
float mx = UI()->MouseX();
float my = UI()->MouseY();
2020-09-15 14:18:12 +00:00
Graphics()->WrapClamp();
Graphics()->TextureSet(m_CursorTexture);
2009-10-27 14:38:53 +00:00
Graphics()->QuadsBegin();
2010-05-29 07:25:38 +00:00
if(ms_pUiGotContext == UI()->HotItem())
Graphics()->SetColor(1, 0, 0, 1);
IGraphics::CQuadItem QuadItem(mx, my, 16.0f, 16.0f);
2010-05-29 07:25:38 +00:00
Graphics()->QuadsDrawTL(&QuadItem, 1);
2009-10-27 14:38:53 +00:00
Graphics()->QuadsEnd();
2020-09-15 14:18:12 +00:00
Graphics()->WrapNormal();
2008-09-01 18:38:08 +00:00
}
2020-09-24 16:52:31 +00:00
m_MouseInsidePopup = false;
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
void CEditor::Reset(bool CreateDefault)
2007-05-22 15:03:32 +00:00
{
2010-05-29 07:25:38 +00:00
m_Map.Clean();
2008-01-12 12:27:55 +00:00
2014-01-19 03:02:01 +00:00
//delete undo file
char aBuffer[1024];
m_pStorage->GetCompletePath(IStorage::TYPE_SAVE, "editor/", aBuffer, sizeof(aBuffer));
2018-10-02 01:52:01 +00:00
mem_zero(m_apSavedBrushes, sizeof m_apSavedBrushes);
2008-01-12 12:27:55 +00:00
// create default layers
2010-05-29 07:25:38 +00:00
if(CreateDefault)
{
m_EditorWasUsedBefore = true;
m_Map.CreateDefault(GetEntitiesTexture());
}
2010-05-29 07:25:38 +00:00
SelectGameLayer();
m_vSelectedQuads.clear();
2010-05-29 07:25:38 +00:00
m_SelectedPoints = 0;
m_SelectedEnvelope = 0;
m_SelectedImage = 0;
m_SelectedSound = 0;
m_SelectedSource = -1;
2011-08-11 08:59:14 +00:00
2010-06-09 23:09:51 +00:00
m_WorldOffsetX = 0;
m_WorldOffsetY = 0;
m_EditorOffsetX = 0.0f;
m_EditorOffsetY = 0.0f;
2011-08-11 08:59:14 +00:00
m_Zoom = 200.0f;
m_Zooming = false;
2010-06-09 23:09:51 +00:00
m_WorldZoom = 1.0f;
m_MouseDeltaX = 0;
m_MouseDeltaY = 0;
m_MouseDeltaWx = 0;
m_MouseDeltaWy = 0;
m_Map.m_Modified = false;
2011-08-14 14:31:48 +00:00
m_ShowEnvelopePreview = SHOWENV_NONE;
2017-02-21 16:10:08 +00:00
m_ShiftBy = 1;
2007-05-22 15:03:32 +00:00
}
int CEditor::GetLineDistance() const
2011-07-10 20:16:16 +00:00
{
if(m_Zoom <= 100.0f)
return 16;
else if(m_Zoom <= 250.0f)
return 32;
else if(m_Zoom <= 450.0f)
return 64;
else if(m_Zoom <= 850.0f)
return 128;
else if(m_Zoom <= 1550.0f)
return 256;
else
return 512;
}
void CEditor::SetZoom(float Target)
{
Target = clamp(Target, MinZoomLevel(), MaxZoomLevel());
const float Now = Client()->GlobalTime();
float Current = m_Zoom;
float Derivative = 0.0f;
if(m_Zooming)
{
const float Progress = ZoomProgress(Now);
Current = m_ZoomSmoothing.Evaluate(Progress);
Derivative = m_ZoomSmoothing.Derivative(Progress);
}
m_ZoomSmoothingTarget = Target;
m_ZoomSmoothing = CCubicBezier::With(Current, Derivative, 0.0f, m_ZoomSmoothingTarget);
m_ZoomSmoothingStart = Now;
m_ZoomSmoothingEnd = Now + g_Config.m_EdSmoothZoomTime / 1000.0f;
m_Zooming = true;
}
void CEditor::ChangeZoom(float Amount)
{
const float CurrentTarget = m_Zooming ? m_ZoomSmoothingTarget : m_Zoom;
SetZoom(CurrentTarget + Amount);
2011-07-10 20:16:16 +00:00
}
void CEditor::ZoomMouseTarget(float ZoomFactor)
{
// zoom to the current mouse position
// get absolute mouse position
float aPoints[4];
RenderTools()->MapScreenToWorld(
m_WorldOffsetX, m_WorldOffsetY,
100.0f, 100.0f, 100.0f, 0.0f, 0.0f, Graphics()->ScreenAspect(), m_WorldZoom, aPoints);
float WorldWidth = aPoints[2] - aPoints[0];
float WorldHeight = aPoints[3] - aPoints[1];
float Mwx = aPoints[0] + WorldWidth * (UI()->MouseX() / UI()->Screen()->w);
float Mwy = aPoints[1] + WorldHeight * (UI()->MouseY() / UI()->Screen()->h);
// adjust camera
m_WorldOffsetX += (Mwx - m_WorldOffsetX) * (1.0f - ZoomFactor);
m_WorldOffsetY += (Mwy - m_WorldOffsetY) * (1.0f - ZoomFactor);
}
void CEditor::UpdateZoom()
{
if(m_Zooming)
{
const float Time = Client()->GlobalTime();
const float OldLevel = m_Zoom;
if(Time >= m_ZoomSmoothingEnd)
{
m_Zoom = m_ZoomSmoothingTarget;
m_Zooming = false;
}
else
{
m_Zoom = m_ZoomSmoothing.Evaluate(ZoomProgress(Time));
if((OldLevel < m_ZoomSmoothingTarget && m_Zoom > m_ZoomSmoothingTarget) || (OldLevel > m_ZoomSmoothingTarget && m_Zoom < m_ZoomSmoothingTarget))
{
m_Zoom = m_ZoomSmoothingTarget;
m_Zooming = false;
}
}
m_Zoom = clamp(m_Zoom, MinZoomLevel(), MaxZoomLevel());
if(g_Config.m_EdZoomTarget)
ZoomMouseTarget(m_Zoom / OldLevel);
}
m_WorldZoom = m_Zoom / 100.0f;
}
float CEditor::MinZoomLevel() const
{
return 10.0f;
}
float CEditor::MaxZoomLevel() const
{
return g_Config.m_EdLimitMaxZoomLevel ? 2000.0f : std::numeric_limits<float>::max();
}
float CEditor::ZoomProgress(float CurrentTime) const
{
return (CurrentTime - m_ZoomSmoothingStart) / (m_ZoomSmoothingEnd - m_ZoomSmoothingStart);
}
2022-08-20 18:47:46 +00:00
void CEditor::Goto(float X, float Y)
{
m_WorldOffsetX = X * 32;
m_WorldOffsetY = Y * 32;
}
void CEditorMap::DeleteEnvelope(int Index)
{
if(Index < 0 || Index >= (int)m_vpEnvelopes.size())
return;
m_Modified = true;
VisitEnvelopeReferences([Index](int &ElementIndex) {
if(ElementIndex == Index)
ElementIndex = -1;
else if(ElementIndex > Index)
ElementIndex--;
});
m_vpEnvelopes.erase(m_vpEnvelopes.begin() + Index);
}
void CEditorMap::SwapEnvelopes(int Index0, int Index1)
{
if(Index0 < 0 || Index0 >= (int)m_vpEnvelopes.size())
return;
if(Index1 < 0 || Index1 >= (int)m_vpEnvelopes.size())
return;
if(Index0 == Index1)
return;
m_Modified = true;
VisitEnvelopeReferences([Index0, Index1](int &ElementIndex) {
if(ElementIndex == Index0)
ElementIndex = Index1;
else if(ElementIndex == Index1)
ElementIndex = Index0;
});
std::swap(m_vpEnvelopes[Index0], m_vpEnvelopes[Index1]);
}
template<typename F>
void CEditorMap::VisitEnvelopeReferences(F &&Visitor)
{
for(auto &pGroup : m_vpGroups)
{
for(auto &pLayer : pGroup->m_vpLayers)
{
if(pLayer->m_Type == LAYERTYPE_QUADS)
{
CLayerQuads *pLayerQuads = static_cast<CLayerQuads *>(pLayer);
for(auto &Quad : pLayerQuads->m_vQuads)
{
Visitor(Quad.m_PosEnv);
Visitor(Quad.m_ColorEnv);
}
}
else if(pLayer->m_Type == LAYERTYPE_TILES)
{
CLayerTiles *pLayerTiles = static_cast<CLayerTiles *>(pLayer);
Visitor(pLayerTiles->m_ColorEnv);
}
else if(pLayer->m_Type == LAYERTYPE_SOUNDS)
{
CLayerSounds *pLayerSounds = static_cast<CLayerSounds *>(pLayer);
for(auto &Source : pLayerSounds->m_vSources)
{
Visitor(Source.m_PosEnv);
Visitor(Source.m_SoundEnv);
}
}
}
}
}
2010-05-29 07:25:38 +00:00
void CEditorMap::MakeGameLayer(CLayer *pLayer)
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
m_pGameLayer = (CLayerGame *)pLayer;
m_pGameLayer->m_pEditor = m_pEditor;
m_pGameLayer->m_Texture = m_pEditor->GetEntitiesTexture();
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
void CEditorMap::MakeGameGroup(CLayerGroup *pGroup)
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
m_pGameGroup = pGroup;
m_pGameGroup->m_GameGroup = true;
2011-07-12 01:14:46 +00:00
str_copy(m_pGameGroup->m_aName, "Game", sizeof(m_pGameGroup->m_aName));
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
void CEditorMap::Clean()
{
for(auto &pGroup : m_vpGroups)
{
DeleteAll(pGroup->m_vpLayers);
}
DeleteAll(m_vpGroups);
DeleteAll(m_vpEnvelopes);
DeleteAll(m_vpImages);
DeleteAll(m_vpSounds);
2010-05-29 07:25:38 +00:00
2011-07-12 21:31:39 +00:00
m_MapInfo.Reset();
m_vSettings.clear();
m_pGameLayer = nullptr;
m_pGameGroup = nullptr;
m_Modified = false;
m_pTeleLayer = nullptr;
m_pSpeedupLayer = nullptr;
m_pFrontLayer = nullptr;
m_pSwitchLayer = nullptr;
m_pTuneLayer = nullptr;
}
void CEditorMap::CreateDefault(IGraphics::CTextureHandle EntitiesTexture)
{
// add background
CLayerGroup *pGroup = NewGroup();
pGroup->m_ParallaxX = 0;
pGroup->m_ParallaxY = 0;
pGroup->m_CustomParallaxZoom = 0;
pGroup->m_ParallaxZoom = 0;
CLayerQuads *pLayer = new CLayerQuads;
pLayer->m_pEditor = m_pEditor;
CQuad *pQuad = pLayer->NewQuad(0, 0, 1600, 1200);
pQuad->m_aColors[0].r = pQuad->m_aColors[1].r = 94;
pQuad->m_aColors[0].g = pQuad->m_aColors[1].g = 132;
pQuad->m_aColors[0].b = pQuad->m_aColors[1].b = 174;
pQuad->m_aColors[2].r = pQuad->m_aColors[3].r = 204;
pQuad->m_aColors[2].g = pQuad->m_aColors[3].g = 232;
pQuad->m_aColors[2].b = pQuad->m_aColors[3].b = 255;
pGroup->AddLayer(pLayer);
// add game layer and reset front, tele, speedup, tune and switch layer pointers
2010-05-29 07:25:38 +00:00
MakeGameGroup(NewGroup());
MakeGameLayer(new CLayerGame(50, 50));
m_pGameGroup->AddLayer(m_pGameLayer);
m_pFrontLayer = nullptr;
m_pTeleLayer = nullptr;
m_pSpeedupLayer = nullptr;
m_pSwitchLayer = nullptr;
m_pTuneLayer = nullptr;
}
int CEditor::GetTextureUsageFlag()
{
return Graphics()->HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE;
}
IGraphics::CTextureHandle CEditor::GetFrontTexture()
{
int TextureLoadFlag = GetTextureUsageFlag();
if(!m_FrontTexture.IsValid())
m_FrontTexture = Graphics()->LoadTexture("editor/front.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
return m_FrontTexture;
}
IGraphics::CTextureHandle CEditor::GetTeleTexture()
{
int TextureLoadFlag = GetTextureUsageFlag();
if(!m_TeleTexture.IsValid())
m_TeleTexture = Graphics()->LoadTexture("editor/tele.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
return m_TeleTexture;
}
IGraphics::CTextureHandle CEditor::GetSpeedupTexture()
{
int TextureLoadFlag = GetTextureUsageFlag();
if(!m_SpeedupTexture.IsValid())
m_SpeedupTexture = Graphics()->LoadTexture("editor/speedup.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
return m_SpeedupTexture;
}
IGraphics::CTextureHandle CEditor::GetSwitchTexture()
{
int TextureLoadFlag = GetTextureUsageFlag();
if(!m_SwitchTexture.IsValid())
m_SwitchTexture = Graphics()->LoadTexture("editor/switch.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
return m_SwitchTexture;
}
IGraphics::CTextureHandle CEditor::GetTuneTexture()
{
int TextureLoadFlag = GetTextureUsageFlag();
if(!m_TuneTexture.IsValid())
m_TuneTexture = Graphics()->LoadTexture("editor/tune.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
return m_TuneTexture;
}
IGraphics::CTextureHandle CEditor::GetEntitiesTexture()
{
int TextureLoadFlag = GetTextureUsageFlag();
if(!m_EntitiesTexture.IsValid())
m_EntitiesTexture = Graphics()->LoadTexture("editor/entities/DDNet.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
return m_EntitiesTexture;
}
2010-05-29 07:25:38 +00:00
void CEditor::Init()
2007-05-22 15:03:32 +00:00
{
2010-05-29 07:25:38 +00:00
m_pInput = Kernel()->RequestInterface<IInput>();
m_pClient = Kernel()->RequestInterface<IClient>();
m_pConfig = Kernel()->RequestInterface<IConfigManager>()->Values();
m_pConsole = Kernel()->RequestInterface<IConsole>();
2010-05-29 07:25:38 +00:00
m_pGraphics = Kernel()->RequestInterface<IGraphics>();
m_pTextRender = Kernel()->RequestInterface<ITextRender>();
2010-09-12 11:15:59 +00:00
m_pStorage = Kernel()->RequestInterface<IStorage>();
m_pSound = Kernel()->RequestInterface<ISound>();
m_UI.Init(Kernel());
m_RenderTools.Init(m_pGraphics, m_pTextRender);
2010-05-29 07:25:38 +00:00
m_Map.m_pEditor = this;
m_CheckerTexture = Graphics()->LoadTexture("editor/checker.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
m_BackgroundTexture = Graphics()->LoadTexture("editor/background.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
m_CursorTexture = Graphics()->LoadTexture("editor/cursor.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
2010-05-29 07:25:38 +00:00
m_TilesetPicker.m_pEditor = this;
m_TilesetPicker.MakePalette();
m_TilesetPicker.m_Readonly = true;
m_QuadsetPicker.m_pEditor = this;
m_QuadsetPicker.NewQuad(0, 0, 64, 64);
m_QuadsetPicker.m_Readonly = true;
2010-05-29 07:25:38 +00:00
m_Brush.m_pMap = &m_Map;
Reset(false);
m_Map.m_Modified = false;
ms_PickerColor = ColorHSVA(1.0f, 0.0f, 0.0f);
2023-03-18 06:53:18 +00:00
ResetMenuBackgroundPositions();
m_vpMenuBackgroundPositionNames.resize(CMenuBackground::NUM_POS);
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_START] = "start";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_BROWSER_INTERNET] = "browser(internet)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_BROWSER_LAN] = "browser(lan)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_DEMOS] = "demos";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_NEWS] = "news";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_BROWSER_FAVORITES] = "favorites";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_LANGUAGE] = "settings(language)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_GENERAL] = "settings(general)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_PLAYER] = "settings(player)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_TEE] = "settings(tee)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_APPEARANCE] = "settings(appearance)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_CONTROLS] = "settings(controls)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_GRAPHICS] = "settings(graphics)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_SOUND] = "settings(sound)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_DDNET] = "settings(ddnet)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_ASSETS] = "settings(assets)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_BROWSER_CUSTOM0] = "custom(ddnet)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_BROWSER_CUSTOM1] = "custom(kog)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_BROWSER_CUSTOM2] = "custom(3)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_BROWSER_CUSTOM3] = "custom(4)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_RESERVED0] = "reserved settings(1)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_RESERVED0] = "reserved settings(2)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_RESERVED0] = "reserved(1)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_RESERVED1] = "reserved(2)";
m_vpMenuBackgroundPositionNames[CMenuBackground::POS_RESERVED2] = "reserved(3)";
2007-12-02 17:55:45 +00:00
}
void CEditor::PlaceBorderTiles()
2007-12-02 17:55:45 +00:00
{
2011-08-11 08:59:14 +00:00
CLayerTiles *pT = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES);
for(int i = 0; i < pT->m_Width * 2; ++i)
2011-08-11 08:59:14 +00:00
pT->m_pTiles[i].m_Index = 1;
for(int i = 0; i < pT->m_Width * pT->m_Height; ++i)
2011-08-11 08:59:14 +00:00
{
if(i % pT->m_Width < 2 || i % pT->m_Width > pT->m_Width - 3)
2011-08-11 08:59:14 +00:00
pT->m_pTiles[i].m_Index = 1;
}
for(int i = (pT->m_Width * (pT->m_Height - 2)); i < pT->m_Width * pT->m_Height; ++i)
2011-08-11 08:59:14 +00:00
pT->m_pTiles[i].m_Index = 1;
2010-05-29 07:25:38 +00:00
}
void CEditor::OnUpdate()
2010-05-29 07:25:38 +00:00
{
CUIElementBase::Init(UI()); // update static pointer because game and editor use separate UI
if(!m_EditorWasUsedBefore)
{
m_EditorWasUsedBefore = true;
Reset();
}
for(int i = 0; i < Input()->NumEvents(); i++)
UI()->OnInput(Input()->GetEvent(i));
2022-06-16 19:01:23 +00:00
// handle cursor movement
2021-09-15 21:52:09 +00:00
{
2022-06-16 19:01:51 +00:00
static float s_MouseX = 0.0f;
static float s_MouseY = 0.0f;
float MouseRelX = 0.0f, MouseRelY = 0.0f;
IInput::ECursorType CursorType = Input()->CursorRelative(&MouseRelX, &MouseRelY);
if(CursorType != IInput::CURSOR_NONE)
UI()->ConvertMouseMove(&MouseRelX, &MouseRelY, CursorType);
UI()->ResetMouseSlow();
2007-12-02 17:55:45 +00:00
m_MouseDeltaX += MouseRelX;
m_MouseDeltaY += MouseRelY;
if(!m_LockMouse)
{
s_MouseX = clamp<float>(s_MouseX + MouseRelX, 0.0f, Graphics()->WindowWidth());
s_MouseY = clamp<float>(s_MouseY + MouseRelY, 0.0f, Graphics()->WindowHeight());
}
2021-09-15 21:52:09 +00:00
// update positions for ui, but only update ui when rendering
m_MouseX = UI()->Screen()->w * ((float)s_MouseX / Graphics()->WindowWidth());
m_MouseY = UI()->Screen()->h * ((float)s_MouseY / Graphics()->WindowHeight());
2010-05-29 07:25:38 +00:00
2008-01-12 12:27:55 +00:00
// fix correct world x and y
CLayerGroup *pGroup = GetSelectedGroup();
if(pGroup)
2008-01-12 12:27:55 +00:00
{
2010-05-29 07:25:38 +00:00
float aPoints[4];
pGroup->Mapping(aPoints);
2010-05-29 07:25:38 +00:00
float WorldWidth = aPoints[2] - aPoints[0];
float WorldHeight = aPoints[3] - aPoints[1];
2010-05-29 07:25:38 +00:00
m_MouseWScale = WorldWidth / Graphics()->WindowWidth();
m_MouseWorldX = aPoints[0] + WorldWidth * (s_MouseX / Graphics()->WindowWidth());
m_MouseWorldY = aPoints[1] + WorldHeight * (s_MouseY / Graphics()->WindowHeight());
m_MouseDeltaWx = m_MouseDeltaX * (WorldWidth / Graphics()->WindowWidth());
m_MouseDeltaWy = m_MouseDeltaY * (WorldHeight / Graphics()->WindowHeight());
}
else
{
m_MouseWorldX = 0.0f;
m_MouseWorldY = 0.0f;
2008-01-12 12:27:55 +00:00
}
for(CLayerGroup *pGameGroup : m_Map.m_vpGroups)
{
if(!pGameGroup->m_GameGroup)
continue;
float aPoints[4];
pGameGroup->Mapping(aPoints);
float WorldWidth = aPoints[2] - aPoints[0];
float WorldHeight = aPoints[3] - aPoints[1];
m_MouseWorldNoParaX = aPoints[0] + WorldWidth * (s_MouseX / Graphics()->WindowWidth());
m_MouseWorldNoParaY = aPoints[1] + WorldHeight * (s_MouseY / Graphics()->WindowHeight());
}
2007-05-22 15:03:32 +00:00
}
}
2010-05-29 07:25:38 +00:00
void CEditor::OnRender()
{
2008-01-12 12:27:55 +00:00
// toggle gui
if(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && Input()->KeyPress(KEY_TAB))
2010-05-29 07:25:38 +00:00
m_GuiActive = !m_GuiActive;
2007-12-02 17:55:45 +00:00
if(Input()->KeyPress(KEY_F10))
2010-05-29 07:25:38 +00:00
m_ShowMousePointer = false;
if(m_Animate)
m_AnimateTime = (time_get() - m_AnimateStart) / (float)time_freq();
else
m_AnimateTime = 0;
ms_pUiGotContext = nullptr;
UI()->StartCheck();
UI()->Update(m_MouseX, m_MouseY, m_MouseWorldX, m_MouseWorldY);
2010-05-29 07:25:38 +00:00
Render();
m_MouseDeltaX = 0.0f;
m_MouseDeltaY = 0.0f;
m_MouseDeltaWx = 0.0f;
m_MouseDeltaWy = 0.0f;
if(Input()->KeyPress(KEY_F10))
2008-09-01 18:38:08 +00:00
{
Graphics()->TakeScreenshot(nullptr);
2010-05-29 07:25:38 +00:00
m_ShowMousePointer = true;
2008-09-01 18:38:08 +00:00
}
2010-05-29 07:25:38 +00:00
UI()->FinishCheck();
UI()->ClearHotkeys();
Input()->Clear();
2007-05-22 15:03:32 +00:00
}
2020-05-15 14:29:09 +00:00
void CEditor::LoadCurrentMap()
{
Load(m_pClient->GetCurrentMapPath(), IStorage::TYPE_ALL);
m_ValidSaveFilename = true;
CGameClient *pGameClient = (CGameClient *)Kernel()->RequestInterface<IGameClient>();
2021-07-12 09:43:56 +00:00
vec2 Center = pGameClient->m_Camera.m_Center;
2020-05-15 14:29:09 +00:00
2020-05-19 16:08:27 +00:00
m_WorldOffsetX = Center.x;
m_WorldOffsetY = Center.y;
2020-05-15 14:29:09 +00:00
}
2010-06-22 00:07:52 +00:00
IEditor *CreateEditor() { return new CEditor; }
void CEditor::ResetMenuBackgroundPositions()
{
std::array<vec2, CMenuBackground::NUM_POS> aBackgroundPositions = GenerateMenuBackgroundPositions();
m_vMenuBackgroundPositions.assign(aBackgroundPositions.begin(), aBackgroundPositions.end());
CLayerGame *pLayer = m_Map.m_pGameLayer;
if(pLayer)
{
for(int y = 0; y < pLayer->m_Height; ++y)
{
for(int x = 0; x < pLayer->m_Width; ++x)
{
CTile Tile = pLayer->GetTile(x, y);
if(Tile.m_Index >= TILE_TIME_CHECKPOINT_FIRST && Tile.m_Index <= TILE_TIME_CHECKPOINT_LAST)
{
int ArrayIndex = clamp<int>((Tile.m_Index - TILE_TIME_CHECKPOINT_FIRST), 0, CMenuBackground::NUM_POS);
m_vMenuBackgroundPositions[ArrayIndex] = vec2(x * 32.0f + 16.0f, y * 32.0f + 16.0f);
}
x += Tile.m_Skip;
}
}
}
for(size_t i = 0; i < m_vMenuBackgroundPositions.size(); i++)
{
2023-03-18 06:53:18 +00:00
for(size_t j = i + 1; j < m_vMenuBackgroundPositions.size(); j++)
{
if(i != j && distance(m_vMenuBackgroundPositions[i], m_vMenuBackgroundPositions[j]) < 0.001f)
m_vMenuBackgroundPositions[j] = vec2(0, 0);
}
}
}
// DDRace
void CEditorMap::MakeTeleLayer(CLayer *pLayer)
{
m_pTeleLayer = (CLayerTele *)pLayer;
m_pTeleLayer->m_pEditor = m_pEditor;
m_pTeleLayer->m_Texture = m_pEditor->GetTeleTexture();
}
void CEditorMap::MakeSpeedupLayer(CLayer *pLayer)
{
m_pSpeedupLayer = (CLayerSpeedup *)pLayer;
m_pSpeedupLayer->m_pEditor = m_pEditor;
m_pSpeedupLayer->m_Texture = m_pEditor->GetSpeedupTexture();
}
void CEditorMap::MakeFrontLayer(CLayer *pLayer)
{
m_pFrontLayer = (CLayerFront *)pLayer;
m_pFrontLayer->m_pEditor = m_pEditor;
m_pFrontLayer->m_Texture = m_pEditor->GetFrontTexture();
}
void CEditorMap::MakeSwitchLayer(CLayer *pLayer)
{
m_pSwitchLayer = (CLayerSwitch *)pLayer;
m_pSwitchLayer->m_pEditor = m_pEditor;
m_pSwitchLayer->m_Texture = m_pEditor->GetSwitchTexture();
}
void CEditorMap::MakeTuneLayer(CLayer *pLayer)
{
m_pTuneLayer = (CLayerTune *)pLayer;
m_pTuneLayer->m_pEditor = m_pEditor;
m_pTuneLayer->m_Texture = m_pEditor->GetTuneTexture();
}