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. */
|
2010-05-29 07:25:38 +00:00
|
|
|
#include <base/math.h>
|
2008-08-14 17:19:13 +00:00
|
|
|
|
2011-02-18 10:41:27 +00:00
|
|
|
#include <engine/client.h>
|
2010-05-29 07:25:38 +00:00
|
|
|
#include <engine/graphics.h>
|
|
|
|
#include <engine/textrender.h>
|
2009-10-27 14:38:53 +00:00
|
|
|
|
2011-04-19 08:34:51 +00:00
|
|
|
#include "editor.h"
|
2020-09-26 19:41:58 +00:00
|
|
|
#include <game/client/render.h>
|
2010-06-05 21:36:52 +00:00
|
|
|
|
2011-04-09 06:41:31 +00:00
|
|
|
#include <engine/input.h>
|
|
|
|
#include <engine/keys.h>
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
CLayerTiles::CLayerTiles(int w, int h)
|
2008-01-12 12:27:55 +00:00
|
|
|
{
|
2010-05-29 07:25:38 +00:00
|
|
|
m_Type = LAYERTYPE_TILES;
|
2019-03-24 01:44:33 +00:00
|
|
|
m_aName[0] = '\0';
|
2010-05-29 07:25:38 +00:00
|
|
|
m_Width = w;
|
|
|
|
m_Height = h;
|
2023-03-10 16:18:24 +00:00
|
|
|
m_Texture.Invalidate();
|
2010-05-29 07:25:38 +00:00
|
|
|
m_Image = -1;
|
|
|
|
m_Game = 0;
|
2011-01-04 10:38:14 +00:00
|
|
|
m_Color.r = 255;
|
|
|
|
m_Color.g = 255;
|
|
|
|
m_Color.b = 255;
|
|
|
|
m_Color.a = 255;
|
2011-07-18 10:05:12 +00:00
|
|
|
m_ColorEnv = -1;
|
|
|
|
m_ColorEnvOffset = 0;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
m_Tele = 0;
|
|
|
|
m_Speedup = 0;
|
|
|
|
m_Front = 0;
|
|
|
|
m_Switch = 0;
|
2014-03-12 22:34:30 +00:00
|
|
|
m_Tune = 0;
|
2018-10-03 16:16:58 +00:00
|
|
|
m_AutoMapperConfig = -1;
|
|
|
|
m_Seed = 0;
|
|
|
|
m_AutoAutoMap = false;
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pTiles = new CTile[m_Width * m_Height];
|
2020-10-05 17:03:14 +00:00
|
|
|
mem_zero(m_pTiles, (size_t)m_Width * m_Height * sizeof(CTile));
|
2008-01-12 12:27:55 +00:00
|
|
|
}
|
|
|
|
|
2022-08-17 23:07:16 +00:00
|
|
|
CLayerTiles::CLayerTiles(const CLayerTiles &Other) :
|
|
|
|
CLayer(Other)
|
|
|
|
{
|
|
|
|
m_Width = Other.m_Width;
|
|
|
|
m_Height = Other.m_Height;
|
|
|
|
m_pTiles = new CTile[m_Width * m_Height];
|
|
|
|
mem_copy(m_pTiles, Other.m_pTiles, (size_t)m_Width * m_Height * sizeof(CTile));
|
|
|
|
|
|
|
|
m_Image = Other.m_Image;
|
|
|
|
m_Texture = Other.m_Texture;
|
|
|
|
m_Game = Other.m_Game;
|
|
|
|
m_Color = Other.m_Color;
|
|
|
|
m_ColorEnv = Other.m_ColorEnv;
|
|
|
|
m_ColorEnvOffset = Other.m_ColorEnvOffset;
|
|
|
|
|
|
|
|
m_AutoMapperConfig = Other.m_AutoMapperConfig;
|
|
|
|
m_Seed = Other.m_Seed;
|
|
|
|
m_AutoAutoMap = Other.m_AutoAutoMap;
|
|
|
|
m_Tele = Other.m_Tele;
|
|
|
|
m_Speedup = Other.m_Speedup;
|
|
|
|
m_Front = Other.m_Front;
|
|
|
|
m_Switch = Other.m_Switch;
|
|
|
|
m_Tune = Other.m_Tune;
|
|
|
|
|
|
|
|
mem_copy(m_aFileName, Other.m_aFileName, IO_MAX_PATH_LENGTH);
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
CLayerTiles::~CLayerTiles()
|
2008-01-12 12:27:55 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
delete[] m_pTiles;
|
2008-01-12 12:27:55 +00:00
|
|
|
}
|
|
|
|
|
2016-04-30 23:47:29 +00:00
|
|
|
CTile CLayerTiles::GetTile(int x, int y)
|
2015-11-14 23:00:43 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
return m_pTiles[y * m_Width + x];
|
2015-11-14 23:00:43 +00:00
|
|
|
}
|
|
|
|
|
2023-07-10 19:27:51 +00:00
|
|
|
void CLayerTiles::SetTile(int x, int y, CTile Tile)
|
2015-11-14 23:00:43 +00:00
|
|
|
{
|
2023-07-10 19:27:51 +00:00
|
|
|
m_pTiles[y * m_Width + x] = Tile;
|
2015-11-14 23:00:43 +00:00
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CLayerTiles::PrepareForSave()
|
2008-03-29 11:44:03 +00:00
|
|
|
{
|
2010-05-29 07:25:38 +00:00
|
|
|
for(int y = 0; y < m_Height; y++)
|
|
|
|
for(int x = 0; x < m_Width; x++)
|
2022-12-19 17:28:08 +00:00
|
|
|
m_pTiles[y * m_Width + x].m_Flags &= TILEFLAG_XFLIP | TILEFLAG_YFLIP | TILEFLAG_ROTATE;
|
2008-03-29 11:44:03 +00:00
|
|
|
|
2011-01-04 10:38:14 +00:00
|
|
|
if(m_Image != -1 && m_Color.a == 255)
|
2008-03-29 11:44:03 +00:00
|
|
|
{
|
2010-05-29 07:25:38 +00:00
|
|
|
for(int y = 0; y < m_Height; y++)
|
|
|
|
for(int x = 0; x < m_Width; x++)
|
2022-06-11 19:38:18 +00:00
|
|
|
m_pTiles[y * m_Width + x].m_Flags |= m_pEditor->m_Map.m_vpImages[m_Image]->m_aTileFlags[m_pTiles[y * m_Width + x].m_Index];
|
2008-03-29 11:44:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-11 08:50:15 +00:00
|
|
|
void CLayerTiles::ExtractTiles(CTile *pSavedTiles)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
while(i < m_Width * m_Height)
|
|
|
|
{
|
|
|
|
for(unsigned Counter = 0; Counter <= pSavedTiles->m_Skip && i < m_Width * m_Height; Counter++)
|
|
|
|
{
|
|
|
|
m_pTiles[i] = *pSavedTiles;
|
|
|
|
m_pTiles[i++].m_Skip = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
pSavedTiles++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CLayerTiles::MakePalette()
|
2008-01-12 12:27:55 +00:00
|
|
|
{
|
2010-05-29 07:25:38 +00:00
|
|
|
for(int y = 0; y < m_Height; y++)
|
|
|
|
for(int x = 0; x < m_Width; x++)
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pTiles[y * m_Width + x].m_Index = y * 16 + x;
|
2008-01-12 12:27:55 +00:00
|
|
|
}
|
|
|
|
|
2018-08-19 17:05:42 +00:00
|
|
|
void CLayerTiles::Render(bool Tileset)
|
2008-01-12 12:27:55 +00:00
|
|
|
{
|
2022-06-11 19:38:18 +00:00
|
|
|
if(m_Image >= 0 && (size_t)m_Image < m_pEditor->m_Map.m_vpImages.size())
|
|
|
|
m_Texture = m_pEditor->m_Map.m_vpImages[m_Image]->m_Texture;
|
2012-08-12 10:41:50 +00:00
|
|
|
Graphics()->TextureSet(m_Texture);
|
2020-09-26 19:41:58 +00:00
|
|
|
ColorRGBA Color = ColorRGBA(m_Color.r / 255.0f, m_Color.g / 255.0f, m_Color.b / 255.0f, m_Color.a / 255.0f);
|
2012-08-13 10:57:40 +00:00
|
|
|
Graphics()->BlendNone();
|
|
|
|
m_pEditor->RenderTools()->RenderTilemap(m_pTiles, m_Width, m_Height, 32.0f, Color, LAYERRENDERFLAG_OPAQUE,
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pEditor->EnvelopeEval, m_pEditor, m_ColorEnv, m_ColorEnvOffset);
|
2012-08-13 10:57:40 +00:00
|
|
|
Graphics()->BlendNormal();
|
|
|
|
m_pEditor->RenderTools()->RenderTilemap(m_pTiles, m_Width, m_Height, 32.0f, Color, LAYERRENDERFLAG_TRANSPARENT,
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pEditor->EnvelopeEval, m_pEditor, m_ColorEnv, m_ColorEnvOffset);
|
2011-04-09 06:41:31 +00:00
|
|
|
|
|
|
|
// Render DDRace Layers
|
2018-08-19 17:05:42 +00:00
|
|
|
if(!Tileset)
|
|
|
|
{
|
|
|
|
if(m_Tele)
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pEditor->RenderTools()->RenderTeleOverlay(((CLayerTele *)this)->m_pTeleTile, m_Width, m_Height, 32.0f);
|
2018-08-19 17:05:42 +00:00
|
|
|
if(m_Speedup)
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pEditor->RenderTools()->RenderSpeedupOverlay(((CLayerSpeedup *)this)->m_pSpeedupTile, m_Width, m_Height, 32.0f);
|
2018-08-19 17:05:42 +00:00
|
|
|
if(m_Switch)
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pEditor->RenderTools()->RenderSwitchOverlay(((CLayerSwitch *)this)->m_pSwitchTile, m_Width, m_Height, 32.0f);
|
2018-08-19 17:05:42 +00:00
|
|
|
if(m_Tune)
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pEditor->RenderTools()->RenderTuneOverlay(((CLayerTune *)this)->m_pTuneTile, m_Width, m_Height, 32.0f);
|
2018-08-19 17:05:42 +00:00
|
|
|
}
|
2008-01-12 12:27:55 +00:00
|
|
|
}
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
int CLayerTiles::ConvertX(float x) const { return (int)(x / 32.0f); }
|
|
|
|
int CLayerTiles::ConvertY(float y) const { return (int)(y / 32.0f); }
|
2008-01-12 12:27:55 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CLayerTiles::Convert(CUIRect Rect, RECTi *pOut)
|
2008-01-12 12:27:55 +00:00
|
|
|
{
|
2010-05-29 07:25:38 +00:00
|
|
|
pOut->x = ConvertX(Rect.x);
|
|
|
|
pOut->y = ConvertY(Rect.y);
|
2020-09-26 19:41:58 +00:00
|
|
|
pOut->w = ConvertX(Rect.x + Rect.w + 31) - pOut->x;
|
|
|
|
pOut->h = ConvertY(Rect.y + Rect.h + 31) - pOut->y;
|
2008-01-12 12:27:55 +00:00
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CLayerTiles::Snap(CUIRect *pRect)
|
2008-01-12 12:27:55 +00:00
|
|
|
{
|
2010-05-29 07:25:38 +00:00
|
|
|
RECTi Out;
|
|
|
|
Convert(*pRect, &Out);
|
2020-09-26 19:41:58 +00:00
|
|
|
pRect->x = Out.x * 32.0f;
|
|
|
|
pRect->y = Out.y * 32.0f;
|
|
|
|
pRect->w = Out.w * 32.0f;
|
|
|
|
pRect->h = Out.h * 32.0f;
|
2008-01-12 12:27:55 +00:00
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CLayerTiles::Clamp(RECTi *pRect)
|
2008-01-12 12:27:55 +00:00
|
|
|
{
|
2010-05-29 07:25:38 +00:00
|
|
|
if(pRect->x < 0)
|
2008-01-12 12:27:55 +00:00
|
|
|
{
|
2010-05-29 07:25:38 +00:00
|
|
|
pRect->w += pRect->x;
|
|
|
|
pRect->x = 0;
|
2008-01-12 12:27:55 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(pRect->y < 0)
|
2008-01-12 12:27:55 +00:00
|
|
|
{
|
2010-05-29 07:25:38 +00:00
|
|
|
pRect->h += pRect->y;
|
|
|
|
pRect->y = 0;
|
2008-01-12 12:27:55 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
if(pRect->x + pRect->w > m_Width)
|
2010-05-29 07:25:38 +00:00
|
|
|
pRect->w = m_Width - pRect->x;
|
2008-01-12 12:27:55 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
if(pRect->y + pRect->h > m_Height)
|
2010-05-29 07:25:38 +00:00
|
|
|
pRect->h = m_Height - pRect->y;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(pRect->h < 0)
|
|
|
|
pRect->h = 0;
|
|
|
|
if(pRect->w < 0)
|
|
|
|
pRect->w = 0;
|
2008-01-12 12:27:55 +00:00
|
|
|
}
|
|
|
|
|
2023-01-17 21:39:31 +00:00
|
|
|
bool CLayerTiles::IsEntitiesLayer() const
|
|
|
|
{
|
|
|
|
return m_pEditor->m_Map.m_pGameLayer == this || m_pEditor->m_Map.m_pTeleLayer == this || m_pEditor->m_Map.m_pSpeedupLayer == this || m_pEditor->m_Map.m_pFrontLayer == this || m_pEditor->m_Map.m_pSwitchLayer == this || m_pEditor->m_Map.m_pTuneLayer == this;
|
|
|
|
}
|
|
|
|
|
2019-03-26 18:15:24 +00:00
|
|
|
bool CLayerTiles::IsEmpty(CLayerTiles *pLayer)
|
|
|
|
{
|
|
|
|
for(int y = 0; y < pLayer->m_Height; y++)
|
|
|
|
for(int x = 0; x < pLayer->m_Width; x++)
|
|
|
|
{
|
|
|
|
int Index = pLayer->GetTile(x, y).m_Index;
|
|
|
|
if(Index)
|
|
|
|
{
|
|
|
|
if(pLayer->m_Game)
|
|
|
|
{
|
|
|
|
if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidGameTile(Index))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if(pLayer->m_Front)
|
|
|
|
{
|
|
|
|
if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidFrontTile(Index))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CLayerTiles::BrushSelecting(CUIRect Rect)
|
2008-01-12 12:27:55 +00:00
|
|
|
{
|
2012-08-12 10:41:50 +00:00
|
|
|
Graphics()->TextureClear();
|
2010-05-29 07:25:38 +00:00
|
|
|
m_pEditor->Graphics()->QuadsBegin();
|
|
|
|
m_pEditor->Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.4f);
|
|
|
|
Snap(&Rect);
|
|
|
|
IGraphics::CQuadItem QuadItem(Rect.x, Rect.y, Rect.w, Rect.h);
|
|
|
|
m_pEditor->Graphics()->QuadsDrawTL(&QuadItem, 1);
|
|
|
|
m_pEditor->Graphics()->QuadsEnd();
|
|
|
|
char aBuf[16];
|
|
|
|
str_format(aBuf, sizeof(aBuf), "%d,%d", ConvertX(Rect.w), ConvertY(Rect.h));
|
2023-02-17 17:13:20 +00:00
|
|
|
TextRender()->Text(Rect.x + 3.0f, Rect.y + 3.0f, m_pEditor->m_ShowPicker ? 15.0f : 15.0f * m_pEditor->m_WorldZoom, aBuf, -1.0f);
|
2008-01-12 12:27:55 +00:00
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
int CLayerTiles::BrushGrab(CLayerGroup *pBrush, CUIRect Rect)
|
2008-01-12 12:27:55 +00:00
|
|
|
{
|
|
|
|
RECTi r;
|
2010-05-29 07:25:38 +00:00
|
|
|
Convert(Rect, &r);
|
|
|
|
Clamp(&r);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2008-01-12 12:27:55 +00:00
|
|
|
if(!r.w || !r.h)
|
|
|
|
return 0;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2008-01-12 12:27:55 +00:00
|
|
|
// create new layers
|
2018-08-19 17:05:42 +00:00
|
|
|
if(this->m_Tele)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
|
|
|
CLayerTele *pGrabbed = new CLayerTele(r.w, r.h);
|
|
|
|
pGrabbed->m_pEditor = m_pEditor;
|
2012-08-12 10:41:50 +00:00
|
|
|
pGrabbed->m_Texture = m_Texture;
|
2010-09-30 21:31:19 +00:00
|
|
|
pGrabbed->m_Image = m_Image;
|
|
|
|
pGrabbed->m_Game = m_Game;
|
2020-09-26 19:41:58 +00:00
|
|
|
if(m_pEditor->m_BrushColorEnabled)
|
2018-09-11 08:35:02 +00:00
|
|
|
{
|
|
|
|
pGrabbed->m_Color = m_Color;
|
|
|
|
pGrabbed->m_Color.a = 255;
|
|
|
|
}
|
2010-09-30 21:31:19 +00:00
|
|
|
|
|
|
|
pBrush->AddLayer(pGrabbed);
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
// copy the tiles
|
|
|
|
for(int y = 0; y < r.h; y++)
|
|
|
|
for(int x = 0; x < r.w; x++)
|
2020-09-26 19:41:58 +00:00
|
|
|
pGrabbed->m_pTiles[y * pGrabbed->m_Width + x] = GetTile(r.x + x, r.y + y);
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
// copy the tele data
|
2016-04-30 18:11:26 +00:00
|
|
|
if(!m_pEditor->Input()->KeyIsPressed(KEY_SPACE))
|
2010-09-30 21:31:19 +00:00
|
|
|
for(int y = 0; y < r.h; y++)
|
|
|
|
for(int x = 0; x < r.w; x++)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
pGrabbed->m_pTeleTile[y * pGrabbed->m_Width + x] = ((CLayerTele *)this)->m_pTeleTile[(r.y + y) * m_Width + (r.x + x)];
|
2023-03-03 17:20:08 +00:00
|
|
|
if(IsValidTeleTile(pGrabbed->m_pTeleTile[y * pGrabbed->m_Width + x].m_Type) && IsTeleTileNumberUsed(pGrabbed->m_pTeleTile[y * pGrabbed->m_Width + x].m_Type))
|
2020-05-15 22:42:11 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pEditor->m_TeleNumber = pGrabbed->m_pTeleTile[y * pGrabbed->m_Width + x].m_Number;
|
2020-05-15 22:42:11 +00:00
|
|
|
}
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
2010-12-07 15:51:59 +00:00
|
|
|
pGrabbed->m_TeleNum = m_pEditor->m_TeleNumber;
|
2023-06-13 15:44:30 +00:00
|
|
|
str_copy(pGrabbed->m_aFileName, m_pEditor->m_aFileName);
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
2018-08-19 17:05:42 +00:00
|
|
|
else if(this->m_Speedup)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
|
|
|
CLayerSpeedup *pGrabbed = new CLayerSpeedup(r.w, r.h);
|
|
|
|
pGrabbed->m_pEditor = m_pEditor;
|
2012-08-12 10:41:50 +00:00
|
|
|
pGrabbed->m_Texture = m_Texture;
|
2010-09-30 21:31:19 +00:00
|
|
|
pGrabbed->m_Image = m_Image;
|
|
|
|
pGrabbed->m_Game = m_Game;
|
2020-09-26 19:41:58 +00:00
|
|
|
if(m_pEditor->m_BrushColorEnabled)
|
2018-09-11 08:35:02 +00:00
|
|
|
{
|
|
|
|
pGrabbed->m_Color = m_Color;
|
|
|
|
pGrabbed->m_Color.a = 255;
|
|
|
|
}
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
pBrush->AddLayer(pGrabbed);
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
// copy the tiles
|
|
|
|
for(int y = 0; y < r.h; y++)
|
|
|
|
for(int x = 0; x < r.w; x++)
|
2020-09-26 19:41:58 +00:00
|
|
|
pGrabbed->m_pTiles[y * pGrabbed->m_Width + x] = GetTile(r.x + x, r.y + y);
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
// copy the speedup data
|
2016-04-30 18:11:26 +00:00
|
|
|
if(!m_pEditor->Input()->KeyIsPressed(KEY_SPACE))
|
2010-09-30 21:31:19 +00:00
|
|
|
for(int y = 0; y < r.h; y++)
|
|
|
|
for(int x = 0; x < r.w; x++)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
pGrabbed->m_pSpeedupTile[y * pGrabbed->m_Width + x] = ((CLayerSpeedup *)this)->m_pSpeedupTile[(r.y + y) * m_Width + (r.x + x)];
|
2023-03-03 17:20:08 +00:00
|
|
|
if(IsValidSpeedupTile(pGrabbed->m_pSpeedupTile[y * pGrabbed->m_Width + x].m_Type))
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pEditor->m_SpeedupAngle = pGrabbed->m_pSpeedupTile[y * pGrabbed->m_Width + x].m_Angle;
|
|
|
|
m_pEditor->m_SpeedupForce = pGrabbed->m_pSpeedupTile[y * pGrabbed->m_Width + x].m_Force;
|
|
|
|
m_pEditor->m_SpeedupMaxSpeed = pGrabbed->m_pSpeedupTile[y * pGrabbed->m_Width + x].m_MaxSpeed;
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
}
|
2010-12-04 11:54:53 +00:00
|
|
|
pGrabbed->m_SpeedupForce = m_pEditor->m_SpeedupForce;
|
|
|
|
pGrabbed->m_SpeedupMaxSpeed = m_pEditor->m_SpeedupMaxSpeed;
|
|
|
|
pGrabbed->m_SpeedupAngle = m_pEditor->m_SpeedupAngle;
|
2023-06-13 15:44:30 +00:00
|
|
|
str_copy(pGrabbed->m_aFileName, m_pEditor->m_aFileName);
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
2018-08-19 17:05:42 +00:00
|
|
|
else if(this->m_Switch)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
|
|
|
CLayerSwitch *pGrabbed = new CLayerSwitch(r.w, r.h);
|
|
|
|
pGrabbed->m_pEditor = m_pEditor;
|
2012-08-12 10:41:50 +00:00
|
|
|
pGrabbed->m_Texture = m_Texture;
|
2010-09-30 21:31:19 +00:00
|
|
|
pGrabbed->m_Image = m_Image;
|
|
|
|
pGrabbed->m_Game = m_Game;
|
2020-09-26 19:41:58 +00:00
|
|
|
if(m_pEditor->m_BrushColorEnabled)
|
2018-09-11 08:35:02 +00:00
|
|
|
{
|
|
|
|
pGrabbed->m_Color = m_Color;
|
|
|
|
pGrabbed->m_Color.a = 255;
|
|
|
|
}
|
2010-09-30 21:31:19 +00:00
|
|
|
|
|
|
|
pBrush->AddLayer(pGrabbed);
|
|
|
|
|
|
|
|
// copy the tiles
|
|
|
|
for(int y = 0; y < r.h; y++)
|
|
|
|
for(int x = 0; x < r.w; x++)
|
2020-09-26 19:41:58 +00:00
|
|
|
pGrabbed->m_pTiles[y * pGrabbed->m_Width + x] = GetTile(r.x + x, r.y + y);
|
2010-09-30 21:31:19 +00:00
|
|
|
|
|
|
|
// copy the switch data
|
2016-04-30 18:11:26 +00:00
|
|
|
if(!m_pEditor->Input()->KeyIsPressed(KEY_SPACE))
|
2010-09-30 21:31:19 +00:00
|
|
|
for(int y = 0; y < r.h; y++)
|
|
|
|
for(int x = 0; x < r.w; x++)
|
|
|
|
{
|
2020-09-14 14:00:32 +00:00
|
|
|
pGrabbed->m_pSwitchTile[y * pGrabbed->m_Width + x] = ((CLayerSwitch *)this)->m_pSwitchTile[(r.y + y) * m_Width + (r.x + x)];
|
2023-03-03 17:20:08 +00:00
|
|
|
if(IsValidSwitchTile(pGrabbed->m_pSwitchTile[y * pGrabbed->m_Width + x].m_Type))
|
2010-11-22 20:43:22 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pEditor->m_SwitchNum = pGrabbed->m_pSwitchTile[y * pGrabbed->m_Width + x].m_Number;
|
|
|
|
m_pEditor->m_SwitchDelay = pGrabbed->m_pSwitchTile[y * pGrabbed->m_Width + x].m_Delay;
|
2010-11-22 20:43:22 +00:00
|
|
|
}
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
2010-12-07 15:51:59 +00:00
|
|
|
pGrabbed->m_SwitchNumber = m_pEditor->m_SwitchNum;
|
2010-12-04 11:54:53 +00:00
|
|
|
pGrabbed->m_SwitchDelay = m_pEditor->m_SwitchDelay;
|
2023-06-13 15:44:30 +00:00
|
|
|
str_copy(pGrabbed->m_aFileName, m_pEditor->m_aFileName);
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
2015-07-09 00:08:14 +00:00
|
|
|
|
2018-08-19 17:05:42 +00:00
|
|
|
else if(this->m_Tune)
|
2015-11-14 23:00:43 +00:00
|
|
|
{
|
2014-03-12 22:34:30 +00:00
|
|
|
CLayerTune *pGrabbed = new CLayerTune(r.w, r.h);
|
|
|
|
pGrabbed->m_pEditor = m_pEditor;
|
2012-08-12 10:41:50 +00:00
|
|
|
pGrabbed->m_Texture = m_Texture;
|
2014-03-12 22:34:30 +00:00
|
|
|
pGrabbed->m_Image = m_Image;
|
|
|
|
pGrabbed->m_Game = m_Game;
|
2020-09-26 19:41:58 +00:00
|
|
|
if(m_pEditor->m_BrushColorEnabled)
|
2018-09-11 08:35:02 +00:00
|
|
|
{
|
|
|
|
pGrabbed->m_Color = m_Color;
|
|
|
|
pGrabbed->m_Color.a = 255;
|
|
|
|
}
|
2018-08-09 10:07:49 +00:00
|
|
|
|
2014-03-12 22:34:30 +00:00
|
|
|
pBrush->AddLayer(pGrabbed);
|
2015-07-09 00:08:14 +00:00
|
|
|
|
2014-03-12 22:34:30 +00:00
|
|
|
// copy the tiles
|
2015-11-14 23:00:43 +00:00
|
|
|
for(int y = 0; y < r.h; y++)
|
|
|
|
for(int x = 0; x < r.w; x++)
|
2020-09-26 19:41:58 +00:00
|
|
|
pGrabbed->m_pTiles[y * pGrabbed->m_Width + x] = GetTile(r.x + x, r.y + y);
|
2014-03-12 22:34:30 +00:00
|
|
|
|
|
|
|
// copy the tiles
|
2016-04-30 18:11:26 +00:00
|
|
|
if(!m_pEditor->Input()->KeyIsPressed(KEY_SPACE))
|
2015-11-14 23:00:43 +00:00
|
|
|
for(int y = 0; y < r.h; y++)
|
|
|
|
for(int x = 0; x < r.w; x++)
|
|
|
|
{
|
2020-09-14 14:00:32 +00:00
|
|
|
pGrabbed->m_pTuneTile[y * pGrabbed->m_Width + x] = ((CLayerTune *)this)->m_pTuneTile[(r.y + y) * m_Width + (r.x + x)];
|
2023-03-03 17:20:08 +00:00
|
|
|
if(IsValidTuneTile(pGrabbed->m_pTuneTile[y * pGrabbed->m_Width + x].m_Type))
|
2015-11-14 23:00:43 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pEditor->m_TuningNum = pGrabbed->m_pTuneTile[y * pGrabbed->m_Width + x].m_Number;
|
2015-11-14 23:00:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
pGrabbed->m_TuningNumber = m_pEditor->m_TuningNum;
|
2023-06-13 15:44:30 +00:00
|
|
|
str_copy(pGrabbed->m_aFileName, m_pEditor->m_aFileName);
|
2015-11-14 23:00:43 +00:00
|
|
|
}
|
2018-08-19 17:05:42 +00:00
|
|
|
else if(this->m_Front)
|
2011-08-13 00:12:40 +00:00
|
|
|
{
|
2015-11-14 23:00:43 +00:00
|
|
|
CLayerFront *pGrabbed = new CLayerFront(r.w, r.h);
|
|
|
|
pGrabbed->m_pEditor = m_pEditor;
|
2012-08-12 10:41:50 +00:00
|
|
|
pGrabbed->m_Texture = m_Texture;
|
2015-11-14 23:00:43 +00:00
|
|
|
pGrabbed->m_Image = m_Image;
|
|
|
|
pGrabbed->m_Game = m_Game;
|
2020-09-26 19:41:58 +00:00
|
|
|
if(m_pEditor->m_BrushColorEnabled)
|
2018-09-11 08:35:02 +00:00
|
|
|
{
|
|
|
|
pGrabbed->m_Color = m_Color;
|
|
|
|
pGrabbed->m_Color.a = 255;
|
|
|
|
}
|
2018-08-09 10:07:49 +00:00
|
|
|
|
2015-11-14 23:00:43 +00:00
|
|
|
pBrush->AddLayer(pGrabbed);
|
|
|
|
|
|
|
|
// copy the tiles
|
|
|
|
for(int y = 0; y < r.h; y++)
|
|
|
|
for(int x = 0; x < r.w; x++)
|
2020-09-26 19:41:58 +00:00
|
|
|
pGrabbed->m_pTiles[y * pGrabbed->m_Width + x] = GetTile(r.x + x, r.y + y);
|
2023-06-13 15:44:30 +00:00
|
|
|
str_copy(pGrabbed->m_aFileName, m_pEditor->m_aFileName);
|
2011-08-13 00:12:40 +00:00
|
|
|
}
|
2010-09-30 21:31:19 +00:00
|
|
|
else
|
|
|
|
{
|
2015-11-14 23:00:43 +00:00
|
|
|
CLayerTiles *pGrabbed = new CLayerTiles(r.w, r.h);
|
|
|
|
pGrabbed->m_pEditor = m_pEditor;
|
2012-08-12 10:41:50 +00:00
|
|
|
pGrabbed->m_Texture = m_Texture;
|
2015-11-14 23:00:43 +00:00
|
|
|
pGrabbed->m_Image = m_Image;
|
|
|
|
pGrabbed->m_Game = m_Game;
|
2020-09-26 19:41:58 +00:00
|
|
|
if(m_pEditor->m_BrushColorEnabled)
|
2018-09-11 08:35:02 +00:00
|
|
|
{
|
|
|
|
pGrabbed->m_Color = m_Color;
|
|
|
|
pGrabbed->m_Color.a = 255;
|
|
|
|
}
|
2018-08-09 10:07:49 +00:00
|
|
|
|
2015-11-14 23:00:43 +00:00
|
|
|
pBrush->AddLayer(pGrabbed);
|
|
|
|
|
|
|
|
// copy the tiles
|
|
|
|
for(int y = 0; y < r.h; y++)
|
|
|
|
for(int x = 0; x < r.w; x++)
|
2020-09-26 19:41:58 +00:00
|
|
|
pGrabbed->m_pTiles[y * pGrabbed->m_Width + x] = GetTile(r.x + x, r.y + y);
|
2023-06-13 15:44:30 +00:00
|
|
|
str_copy(pGrabbed->m_aFileName, m_pEditor->m_aFileName);
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2008-01-12 12:27:55 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CLayerTiles::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect)
|
2008-01-12 12:27:55 +00:00
|
|
|
{
|
2018-10-30 17:56:46 +00:00
|
|
|
if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES))
|
2018-09-26 12:53:25 +00:00
|
|
|
return;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2012-01-12 21:03:55 +00:00
|
|
|
Snap(&Rect);
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
int sx = ConvertX(Rect.x);
|
|
|
|
int sy = ConvertY(Rect.y);
|
|
|
|
int w = ConvertX(Rect.w);
|
|
|
|
int h = ConvertY(Rect.h);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
CLayerTiles *pLt = static_cast<CLayerTiles *>(pBrush);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2019-04-30 12:24:13 +00:00
|
|
|
bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLt);
|
2019-03-26 18:15:24 +00:00
|
|
|
|
2012-01-12 21:03:55 +00:00
|
|
|
for(int y = 0; y < h; y++)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2012-01-12 21:03:55 +00:00
|
|
|
for(int x = 0; x < w; x++)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int fx = x + sx;
|
|
|
|
int fy = y + sy;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height)
|
|
|
|
continue;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2019-04-11 19:18:48 +00:00
|
|
|
bool HasTile = GetTile(fx, fy).m_Index;
|
Fix buffer-overflow in editor on shift-clicking brush
1. Open any map, including an empty one.
2. Select a brush, e.g. size 2x2.
3. Shift click to repeat the brush over a larger area, e.g. 10x10.
4. This causes a buffer-overflow / crash with ASAN:
```
=================================================================
==4826==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6060000f5040 at pc 0x55db7d0aa743 bp 0x7fffe4e191f0 sp 0x7fffe4e191e0
READ of size 4 at 0x6060000f5040 thread T0
0 0x55db7d0aa742 in CLayerTiles::GetTile(int, int) src/game/editor/layer_tiles.cpp:50
1 0x55db7d0d23e1 in CLayerTiles::FillSelection(bool, CLayer*, CUIRect) src/game/editor/layer_tiles.cpp:437
2 0x55db7cf196e9 in CEditor::DoMapEditor(CUIRect) src/game/editor/editor.cpp:2641
3 0x55db7cfa7755 in CEditor::Render() src/game/editor/editor.cpp:5747
4 0x55db7cfd2a56 in CEditor::OnRender() src/game/editor/editor.cpp:6437
5 0x55db7c23e02d in CClient::Run() src/engine/client/client.cpp:3374
6 0x55db7c2a9f7b in main src/engine/client/client.cpp:4762
0x6060000f5040 is located 0 bytes to the right of 64-byte region [0x6060000f5000,0x6060000f5040)
allocated by thread T0 here:
0 0x7f9b21db5787 in operator new[](unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cc:107
1 0x55db7d0a9a86 in CLayerTiles::CLayerTiles(int, int) src/game/editor/layer_tiles.cpp:39
2 0x55db7d0cf0ed in CLayerTiles::BrushGrab(CLayerGroup*, CUIRect) src/game/editor/layer_tiles.cpp:387
3 0x55db7cf18191 in CEditor::DoMapEditor(CUIRect) src/game/editor/editor.cpp:2612
4 0x55db7cfa7755 in CEditor::Render() src/game/editor/editor.cpp:5747
5 0x55db7cfd2a56 in CEditor::OnRender() src/game/editor/editor.cpp:6437
6 0x55db7c23e02d in CClient::Run() src/engine/client/client.cpp:3374
SUMMARY: AddressSanitizer: heap-buffer-overflow src/game/editor/layer_tiles.cpp:50 in CLayerTiles::GetTile(int, int)
Shadow bytes around the buggy address:
0x0c0c800169b0: 00 00 00 fa fa fa fa fa 00 00 00 00 00 00 00 fa
0x0c0c800169c0: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
0x0c0c800169d0: fd fd fd fd fd fd fd fa fa fa fa fa fd fd fd fd
0x0c0c800169e0: fd fd fd fd fa fa fa fa fd fd fd fd fd fd fd fa
0x0c0c800169f0: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
=>0x0c0c80016a00: 00 00 00 00 00 00 00 00[fa]fa fa fa fd fd fd fd
0x0c0c80016a10: fd fd fd fa fa fa fa fa fd fd fd fd fd fd fd fa
0x0c0c80016a20: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
0x0c0c80016a30: fd fd fd fd fd fd fd fa fa fa fa fa fd fd fd fd
0x0c0c80016a40: fd fd fd fa fa fa fa fa fd fd fd fd fd fd fd fa
0x0c0c80016a50: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==4826==ABORTING
```
2022-08-04 21:39:40 +00:00
|
|
|
if(!Empty && pLt->GetTile(x % pLt->m_Width, y % pLt->m_Height).m_Index == TILE_THROUGH_CUT)
|
2019-04-11 19:18:48 +00:00
|
|
|
{
|
|
|
|
if(m_Game && m_pEditor->m_Map.m_pFrontLayer)
|
|
|
|
{
|
|
|
|
HasTile = HasTile || m_pEditor->m_Map.m_pFrontLayer->GetTile(fx, fy).m_Index;
|
|
|
|
}
|
|
|
|
else if(m_Front)
|
|
|
|
{
|
|
|
|
HasTile = HasTile || m_pEditor->m_Map.m_pGameLayer->GetTile(fx, fy).m_Index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!Destructive && HasTile)
|
2019-03-26 18:15:24 +00:00
|
|
|
continue;
|
|
|
|
|
2023-07-10 19:28:27 +00:00
|
|
|
SetTile(fx, fy, Empty ? CTile{TILE_AIR} : pLt->m_pTiles[(y * pLt->m_Width + x % pLt->m_Width) % (pLt->m_Width * pLt->m_Height)]);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
2018-10-04 13:26:41 +00:00
|
|
|
FlagModified(sx, sy, w, h);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CLayerTiles::BrushDraw(CLayer *pBrush, float wx, float wy)
|
|
|
|
{
|
|
|
|
if(m_Readonly)
|
2008-03-20 23:59:41 +00:00
|
|
|
return;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2008-01-12 12:27:55 +00:00
|
|
|
//
|
2022-06-30 22:36:32 +00:00
|
|
|
CLayerTiles *pTileLayer = (CLayerTiles *)pBrush;
|
2010-05-29 07:25:38 +00:00
|
|
|
int sx = ConvertX(wx);
|
|
|
|
int sy = ConvertY(wy);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(pTileLayer);
|
2019-03-26 18:15:24 +00:00
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
for(int y = 0; y < pTileLayer->m_Height; y++)
|
|
|
|
for(int x = 0; x < pTileLayer->m_Width; x++)
|
2008-01-12 12:27:55 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int fx = x + sx;
|
|
|
|
int fy = y + sy;
|
2019-03-26 18:15:24 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height)
|
2008-01-12 12:27:55 +00:00
|
|
|
continue;
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2019-04-11 19:18:48 +00:00
|
|
|
bool HasTile = GetTile(fx, fy).m_Index;
|
2022-06-30 22:36:32 +00:00
|
|
|
if(pTileLayer->GetTile(x, y).m_Index == TILE_THROUGH_CUT)
|
2019-04-11 19:18:48 +00:00
|
|
|
{
|
|
|
|
if(m_Game && m_pEditor->m_Map.m_pFrontLayer)
|
|
|
|
{
|
|
|
|
HasTile = HasTile || m_pEditor->m_Map.m_pFrontLayer->GetTile(fx, fy).m_Index;
|
|
|
|
}
|
|
|
|
else if(m_Front)
|
|
|
|
{
|
|
|
|
HasTile = HasTile || m_pEditor->m_Map.m_pGameLayer->GetTile(fx, fy).m_Index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!Destructive && HasTile)
|
2019-03-26 18:15:24 +00:00
|
|
|
continue;
|
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
SetTile(fx, fy, pTileLayer->GetTile(x, y));
|
2008-01-12 12:27:55 +00:00
|
|
|
}
|
2019-03-26 18:15:24 +00:00
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
FlagModified(sx, sy, pTileLayer->m_Width, pTileLayer->m_Height);
|
2008-01-12 12:27:55 +00:00
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CLayerTiles::BrushFlipX()
|
2008-01-12 12:27:55 +00:00
|
|
|
{
|
2022-06-05 20:21:24 +00:00
|
|
|
BrushFlipXImpl(m_pTiles);
|
2008-01-12 12:27:55 +00:00
|
|
|
|
2020-01-17 17:22:41 +00:00
|
|
|
if(m_Tele || m_Speedup || m_Tune)
|
2019-03-21 21:50:22 +00:00
|
|
|
return;
|
|
|
|
|
2020-01-17 17:22:41 +00:00
|
|
|
bool Rotate = !(m_Game || m_Front || m_Switch) || m_pEditor->m_AllowPlaceUnusedTiles;
|
2014-10-01 16:55:53 +00:00
|
|
|
for(int y = 0; y < m_Height; y++)
|
|
|
|
for(int x = 0; x < m_Width; x++)
|
2020-09-26 19:41:58 +00:00
|
|
|
if(!Rotate && !IsRotatableTile(m_pTiles[y * m_Width + x].m_Index))
|
|
|
|
m_pTiles[y * m_Width + x].m_Flags = 0;
|
2019-03-21 21:50:22 +00:00
|
|
|
else
|
2022-12-19 17:28:08 +00:00
|
|
|
m_pTiles[y * m_Width + x].m_Flags ^= (m_pTiles[y * m_Width + x].m_Flags & TILEFLAG_ROTATE) ? TILEFLAG_YFLIP : TILEFLAG_XFLIP;
|
2008-01-12 12:27:55 +00:00
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CLayerTiles::BrushFlipY()
|
2008-01-12 12:27:55 +00:00
|
|
|
{
|
2022-06-05 20:21:24 +00:00
|
|
|
BrushFlipYImpl(m_pTiles);
|
2008-01-12 12:27:55 +00:00
|
|
|
|
2020-01-17 17:22:41 +00:00
|
|
|
if(m_Tele || m_Speedup || m_Tune)
|
2019-03-21 21:50:22 +00:00
|
|
|
return;
|
|
|
|
|
2020-01-17 17:22:41 +00:00
|
|
|
bool Rotate = !(m_Game || m_Front || m_Switch) || m_pEditor->m_AllowPlaceUnusedTiles;
|
2014-10-01 16:55:53 +00:00
|
|
|
for(int y = 0; y < m_Height; y++)
|
|
|
|
for(int x = 0; x < m_Width; x++)
|
2020-09-26 19:41:58 +00:00
|
|
|
if(!Rotate && !IsRotatableTile(m_pTiles[y * m_Width + x].m_Index))
|
|
|
|
m_pTiles[y * m_Width + x].m_Flags = 0;
|
2019-03-21 21:50:22 +00:00
|
|
|
else
|
2022-12-19 17:28:08 +00:00
|
|
|
m_pTiles[y * m_Width + x].m_Flags ^= (m_pTiles[y * m_Width + x].m_Flags & TILEFLAG_ROTATE) ? TILEFLAG_XFLIP : TILEFLAG_YFLIP;
|
2010-09-05 17:01:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CLayerTiles::BrushRotate(float Amount)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int Rotation = (round_to_int(360.0f * Amount / (pi * 2)) / 90) % 4; // 0=0°, 1=90°, 2=180°, 3=270°
|
2010-09-05 17:01:01 +00:00
|
|
|
if(Rotation < 0)
|
2020-09-26 19:41:58 +00:00
|
|
|
Rotation += 4;
|
2010-09-05 17:01:01 +00:00
|
|
|
|
|
|
|
if(Rotation == 1 || Rotation == 3)
|
|
|
|
{
|
2014-03-19 09:26:38 +00:00
|
|
|
// 90° rotation
|
2020-09-26 19:41:58 +00:00
|
|
|
CTile *pTempData = new CTile[m_Width * m_Height];
|
2020-10-05 17:03:14 +00:00
|
|
|
mem_copy(pTempData, m_pTiles, (size_t)m_Width * m_Height * sizeof(CTile));
|
2010-09-05 17:01:01 +00:00
|
|
|
CTile *pDst = m_pTiles;
|
2019-03-21 21:50:22 +00:00
|
|
|
bool Rotate = !(m_Game || m_Front) || m_pEditor->m_AllowPlaceUnusedTiles;
|
2010-09-05 17:01:01 +00:00
|
|
|
for(int x = 0; x < m_Width; ++x)
|
2020-09-26 19:41:58 +00:00
|
|
|
for(int y = m_Height - 1; y >= 0; --y, ++pDst)
|
2010-09-05 17:01:01 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
*pDst = pTempData[y * m_Width + x];
|
2019-03-21 21:50:22 +00:00
|
|
|
if(!Rotate && !IsRotatableTile(pDst->m_Index))
|
|
|
|
pDst->m_Flags = 0;
|
|
|
|
else
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
if(pDst->m_Flags & TILEFLAG_ROTATE)
|
2022-12-19 17:28:08 +00:00
|
|
|
pDst->m_Flags ^= (TILEFLAG_YFLIP | TILEFLAG_XFLIP);
|
2019-03-21 21:50:22 +00:00
|
|
|
pDst->m_Flags ^= TILEFLAG_ROTATE;
|
|
|
|
}
|
2010-09-05 17:01:01 +00:00
|
|
|
}
|
|
|
|
|
2022-05-15 18:44:05 +00:00
|
|
|
std::swap(m_Width, m_Height);
|
2010-09-05 17:01:01 +00:00
|
|
|
delete[] pTempData;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(Rotation == 2 || Rotation == 3)
|
|
|
|
{
|
|
|
|
BrushFlipX();
|
|
|
|
BrushFlipY();
|
|
|
|
}
|
2008-01-12 12:27:55 +00:00
|
|
|
}
|
|
|
|
|
2022-08-17 23:07:16 +00:00
|
|
|
CLayer *CLayerTiles::Duplicate() const
|
|
|
|
{
|
|
|
|
return new CLayerTiles(*this);
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CLayerTiles::Resize(int NewW, int NewH)
|
2008-01-12 12:27:55 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
CTile *pNewData = new CTile[NewW * NewH];
|
2020-10-05 17:03:14 +00:00
|
|
|
mem_zero(pNewData, (size_t)NewW * NewH * sizeof(CTile));
|
2008-01-12 12:27:55 +00:00
|
|
|
|
2011-04-13 18:37:12 +00:00
|
|
|
// copy old data
|
2019-04-26 19:36:49 +00:00
|
|
|
for(int y = 0; y < minimum(NewH, m_Height); y++)
|
2020-09-26 19:41:58 +00:00
|
|
|
mem_copy(&pNewData[y * NewW], &m_pTiles[y * m_Width], minimum(m_Width, NewW) * sizeof(CTile));
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2008-01-12 12:27:55 +00:00
|
|
|
// replace old
|
2020-09-26 19:41:58 +00:00
|
|
|
delete[] m_pTiles;
|
2010-05-29 07:25:38 +00:00
|
|
|
m_pTiles = pNewData;
|
|
|
|
m_Width = NewW;
|
|
|
|
m_Height = NewH;
|
2010-09-30 21:31:19 +00:00
|
|
|
|
|
|
|
// resize tele layer if available
|
|
|
|
if(m_Game && m_pEditor->m_Map.m_pTeleLayer && (m_pEditor->m_Map.m_pTeleLayer->m_Width != NewW || m_pEditor->m_Map.m_pTeleLayer->m_Height != NewH))
|
|
|
|
m_pEditor->m_Map.m_pTeleLayer->Resize(NewW, NewH);
|
|
|
|
|
|
|
|
// resize speedup layer if available
|
|
|
|
if(m_Game && m_pEditor->m_Map.m_pSpeedupLayer && (m_pEditor->m_Map.m_pSpeedupLayer->m_Width != NewW || m_pEditor->m_Map.m_pSpeedupLayer->m_Height != NewH))
|
|
|
|
m_pEditor->m_Map.m_pSpeedupLayer->Resize(NewW, NewH);
|
|
|
|
|
|
|
|
// resize front layer
|
|
|
|
if(m_Game && m_pEditor->m_Map.m_pFrontLayer && (m_pEditor->m_Map.m_pFrontLayer->m_Width != NewW || m_pEditor->m_Map.m_pFrontLayer->m_Height != NewH))
|
|
|
|
m_pEditor->m_Map.m_pFrontLayer->Resize(NewW, NewH);
|
|
|
|
|
|
|
|
// resize switch layer if available
|
|
|
|
if(m_Game && m_pEditor->m_Map.m_pSwitchLayer && (m_pEditor->m_Map.m_pSwitchLayer->m_Width != NewW || m_pEditor->m_Map.m_pSwitchLayer->m_Height != NewH))
|
|
|
|
m_pEditor->m_Map.m_pSwitchLayer->Resize(NewW, NewH);
|
2015-07-09 00:08:14 +00:00
|
|
|
|
2014-03-12 22:34:30 +00:00
|
|
|
// resize tune layer if available
|
|
|
|
if(m_Game && m_pEditor->m_Map.m_pTuneLayer && (m_pEditor->m_Map.m_pTuneLayer->m_Width != NewW || m_pEditor->m_Map.m_pTuneLayer->m_Height != NewH))
|
2019-03-12 01:35:29 +00:00
|
|
|
m_pEditor->m_Map.m_pTuneLayer->Resize(NewW, NewH);
|
2008-01-12 12:27:55 +00:00
|
|
|
}
|
|
|
|
|
2010-10-09 16:38:23 +00:00
|
|
|
void CLayerTiles::Shift(int Direction)
|
|
|
|
{
|
2022-06-05 20:03:16 +00:00
|
|
|
ShiftImpl(m_pTiles, Direction, m_pEditor->m_ShiftBy);
|
2010-10-09 16:38:23 +00:00
|
|
|
}
|
2008-01-12 12:27:55 +00:00
|
|
|
|
2011-02-18 10:41:27 +00:00
|
|
|
void CLayerTiles::ShowInfo()
|
|
|
|
{
|
|
|
|
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
|
|
|
|
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
|
|
|
|
Graphics()->TextureSet(m_pEditor->Client()->GetDebugFont());
|
2012-10-14 12:04:48 +00:00
|
|
|
Graphics()->QuadsBegin();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
int StartY = maximum(0, (int)(ScreenY0 / 32.0f) - 1);
|
|
|
|
int StartX = maximum(0, (int)(ScreenX0 / 32.0f) - 1);
|
|
|
|
int EndY = minimum((int)(ScreenY1 / 32.0f) + 1, m_Height);
|
|
|
|
int EndX = minimum((int)(ScreenX1 / 32.0f) + 1, m_Width);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-02-18 10:41:27 +00:00
|
|
|
for(int y = StartY; y < EndY; y++)
|
|
|
|
for(int x = StartX; x < EndX; x++)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int c = x + y * m_Width;
|
2011-02-18 10:41:27 +00:00
|
|
|
if(m_pTiles[c].m_Index)
|
|
|
|
{
|
|
|
|
char aBuf[64];
|
2023-05-23 18:16:42 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), m_pEditor->m_ShowTileInfo == CEditor::SHOW_TILE_HEXADECIMAL ? "%02X" : "%i", m_pTiles[c].m_Index);
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pEditor->Graphics()->QuadsText(x * 32, y * 32, 16.0f, aBuf);
|
2011-02-18 10:41:27 +00:00
|
|
|
|
2022-12-19 17:28:08 +00:00
|
|
|
char aFlags[4] = {m_pTiles[c].m_Flags & TILEFLAG_XFLIP ? 'X' : ' ',
|
|
|
|
m_pTiles[c].m_Flags & TILEFLAG_YFLIP ? 'Y' : ' ',
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pTiles[c].m_Flags & TILEFLAG_ROTATE ? 'R' : ' ',
|
|
|
|
0};
|
|
|
|
m_pEditor->Graphics()->QuadsText(x * 32, y * 32 + 16, 16.0f, aFlags);
|
2011-02-18 10:41:27 +00:00
|
|
|
}
|
|
|
|
x += m_pTiles[c].m_Skip;
|
|
|
|
}
|
2011-06-02 17:16:22 +00:00
|
|
|
|
2012-10-14 12:04:48 +00:00
|
|
|
Graphics()->QuadsEnd();
|
2011-06-02 17:16:22 +00:00
|
|
|
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
|
2011-02-18 10:41:27 +00:00
|
|
|
}
|
|
|
|
|
2023-03-27 15:22:17 +00:00
|
|
|
CUI::EPopupMenuFunctionResult CLayerTiles::RenderProperties(CUIRect *pToolBox)
|
2008-01-12 12:27:55 +00:00
|
|
|
{
|
2010-05-29 07:25:38 +00:00
|
|
|
CUIRect Button;
|
2011-08-11 08:59:14 +00:00
|
|
|
|
2023-01-17 21:39:31 +00:00
|
|
|
const bool EntitiesLayer = IsEntitiesLayer();
|
2010-12-15 11:49:36 +00:00
|
|
|
|
2022-06-11 19:38:18 +00:00
|
|
|
CLayerGroup *pGroup = m_pEditor->m_Map.m_vpGroups[m_pEditor->m_SelectedGroup];
|
2019-03-12 01:35:29 +00:00
|
|
|
|
|
|
|
// Game tiles can only be constructed if the layer is relative to the game layer
|
2023-01-17 21:39:31 +00:00
|
|
|
if(!EntitiesLayer && !(pGroup->m_OffsetX % 32) && !(pGroup->m_OffsetY % 32) && pGroup->m_ParallaxX == 100 && pGroup->m_ParallaxY == 100)
|
2008-09-07 08:30:49 +00:00
|
|
|
{
|
2011-07-08 23:09:06 +00:00
|
|
|
pToolBox->HSplitBottom(12.0f, pToolBox, &Button);
|
2023-04-01 21:16:55 +00:00
|
|
|
static int s_GameTilesButton = 0;
|
|
|
|
if(m_pEditor->DoButton_Editor(&s_GameTilesButton, "Game tiles", 0, &Button, 0, "Constructs game tiles from this layer"))
|
2011-02-12 18:10:45 +00:00
|
|
|
m_pEditor->PopupSelectGametileOpInvoke(m_pEditor->UI()->MouseX(), m_pEditor->UI()->MouseY());
|
|
|
|
|
|
|
|
int Result = m_pEditor->PopupSelectGameTileOpResult();
|
2011-02-13 05:35:13 +00:00
|
|
|
switch(Result)
|
2016-05-04 21:37:02 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
case 4:
|
|
|
|
Result = TILE_THROUGH_CUT;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
Result = TILE_FREEZE;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
Result = TILE_UNFREEZE;
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
Result = TILE_DFREEZE;
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
Result = TILE_DUNFREEZE;
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
Result = TILE_TELECHECKIN;
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
Result = TILE_TELECHECKINEVIL;
|
2022-01-07 15:53:40 +00:00
|
|
|
break;
|
|
|
|
case 11:
|
|
|
|
Result = TILE_LFREEZE;
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
Result = TILE_LUNFREEZE;
|
|
|
|
break;
|
2020-09-26 19:41:58 +00:00
|
|
|
default:
|
|
|
|
break;
|
2010-12-15 11:49:36 +00:00
|
|
|
}
|
2011-02-12 18:10:45 +00:00
|
|
|
if(Result > -1)
|
2010-12-15 11:49:36 +00:00
|
|
|
{
|
2023-04-01 21:16:55 +00:00
|
|
|
const int OffsetX = -pGroup->m_OffsetX / 32;
|
|
|
|
const int OffsetY = -pGroup->m_OffsetY / 32;
|
2019-03-12 01:35:29 +00:00
|
|
|
|
|
|
|
if(Result != TILE_TELECHECKIN && Result != TILE_TELECHECKINEVIL)
|
2014-02-12 20:44:05 +00:00
|
|
|
{
|
2019-03-12 01:35:29 +00:00
|
|
|
CLayerTiles *pGLayer = m_pEditor->m_Map.m_pGameLayer;
|
|
|
|
|
|
|
|
if(pGLayer->m_Width < m_Width + OffsetX || pGLayer->m_Height < m_Height + OffsetY)
|
|
|
|
{
|
2023-04-01 21:16:55 +00:00
|
|
|
const int NewW = pGLayer->m_Width < m_Width + OffsetX ? m_Width + OffsetX : pGLayer->m_Width;
|
|
|
|
const int NewH = pGLayer->m_Height < m_Height + OffsetY ? m_Height + OffsetY : pGLayer->m_Height;
|
2019-03-12 01:35:29 +00:00
|
|
|
pGLayer->Resize(NewW, NewH);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(int y = OffsetY < 0 ? -OffsetY : 0; y < m_Height; y++)
|
2023-04-01 21:16:55 +00:00
|
|
|
{
|
2019-03-12 01:35:29 +00:00
|
|
|
for(int x = OffsetX < 0 ? -OffsetX : 0; x < m_Width; x++)
|
2023-04-01 21:16:55 +00:00
|
|
|
{
|
2019-03-12 01:35:29 +00:00
|
|
|
if(GetTile(x, y).m_Index)
|
|
|
|
{
|
2023-04-01 21:16:55 +00:00
|
|
|
const CTile ResultTile = {(unsigned char)Result};
|
2019-03-12 01:35:29 +00:00
|
|
|
pGLayer->SetTile(x + OffsetX, y + OffsetY, ResultTile);
|
2016-05-01 15:44:02 +00:00
|
|
|
}
|
2023-04-01 21:16:55 +00:00
|
|
|
}
|
|
|
|
}
|
2014-02-12 20:44:05 +00:00
|
|
|
}
|
2019-03-12 01:35:29 +00:00
|
|
|
else
|
2014-02-12 20:44:05 +00:00
|
|
|
{
|
2019-03-12 01:35:29 +00:00
|
|
|
if(!m_pEditor->m_Map.m_pTeleLayer)
|
|
|
|
{
|
|
|
|
CLayer *pLayer = new CLayerTele(m_Width, m_Height);
|
|
|
|
m_pEditor->m_Map.MakeTeleLayer(pLayer);
|
|
|
|
m_pEditor->m_Map.m_pGameGroup->AddLayer(pLayer);
|
|
|
|
}
|
2014-02-12 20:44:05 +00:00
|
|
|
|
2019-03-12 01:35:29 +00:00
|
|
|
CLayerTele *pTLayer = m_pEditor->m_Map.m_pTeleLayer;
|
|
|
|
|
|
|
|
if(pTLayer->m_Width < m_Width + OffsetX || pTLayer->m_Height < m_Height + OffsetY)
|
|
|
|
{
|
|
|
|
int NewW = pTLayer->m_Width < m_Width + OffsetX ? m_Width + OffsetX : pTLayer->m_Width;
|
|
|
|
int NewH = pTLayer->m_Height < m_Height + OffsetY ? m_Height + OffsetY : pTLayer->m_Height;
|
|
|
|
pTLayer->Resize(NewW, NewH);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(int y = OffsetY < 0 ? -OffsetY : 0; y < m_Height; y++)
|
2023-04-01 21:16:55 +00:00
|
|
|
{
|
2019-03-12 01:35:29 +00:00
|
|
|
for(int x = OffsetX < 0 ? -OffsetX : 0; x < m_Width; x++)
|
2023-04-01 21:16:55 +00:00
|
|
|
{
|
2019-03-12 01:35:29 +00:00
|
|
|
if(GetTile(x, y).m_Index)
|
2014-02-12 20:44:05 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
pTLayer->m_pTiles[(y + OffsetY) * pTLayer->m_Width + x + OffsetX].m_Index = TILE_AIR + Result;
|
|
|
|
pTLayer->m_pTeleTile[(y + OffsetY) * pTLayer->m_Width + x + OffsetX].m_Number = 1;
|
|
|
|
pTLayer->m_pTeleTile[(y + OffsetY) * pTLayer->m_Width + x + OffsetX].m_Type = TILE_AIR + Result;
|
2014-02-12 20:44:05 +00:00
|
|
|
}
|
2023-04-01 21:16:55 +00:00
|
|
|
}
|
|
|
|
}
|
2014-02-12 20:44:05 +00:00
|
|
|
}
|
2010-12-15 11:49:36 +00:00
|
|
|
}
|
2008-01-13 22:03:32 +00:00
|
|
|
}
|
2019-03-12 01:35:29 +00:00
|
|
|
|
2018-10-03 16:16:58 +00:00
|
|
|
if(m_pEditor->m_Map.m_pGameLayer != this)
|
|
|
|
{
|
2023-04-01 21:16:55 +00:00
|
|
|
if(m_Image >= 0 && (size_t)m_Image < m_pEditor->m_Map.m_vpImages.size() && m_pEditor->m_Map.m_vpImages[m_Image]->m_AutoMapper.IsLoaded() && m_AutoMapperConfig != -1)
|
2018-10-03 16:16:58 +00:00
|
|
|
{
|
2022-06-13 16:28:13 +00:00
|
|
|
pToolBox->HSplitBottom(2.0f, pToolBox, nullptr);
|
2018-10-03 16:16:58 +00:00
|
|
|
pToolBox->HSplitBottom(12.0f, pToolBox, &Button);
|
2020-09-26 19:41:58 +00:00
|
|
|
if(m_Seed != 0)
|
|
|
|
{
|
2018-10-03 16:16:58 +00:00
|
|
|
CUIRect ButtonAuto;
|
|
|
|
Button.VSplitRight(16.0f, &Button, &ButtonAuto);
|
2022-06-13 16:28:13 +00:00
|
|
|
Button.VSplitRight(2.0f, &Button, nullptr);
|
2023-04-01 21:16:55 +00:00
|
|
|
static int s_AutoMapperButtonAuto = 0;
|
2018-10-03 16:16:58 +00:00
|
|
|
if(m_pEditor->DoButton_Editor(&s_AutoMapperButtonAuto, "A", m_AutoAutoMap, &ButtonAuto, 0, "Automatically run automap after modifications."))
|
|
|
|
{
|
|
|
|
m_AutoAutoMap = !m_AutoAutoMap;
|
2018-10-04 13:26:41 +00:00
|
|
|
FlagModified(0, 0, m_Width, m_Height);
|
2018-10-03 16:16:58 +00:00
|
|
|
}
|
|
|
|
}
|
2023-04-01 21:16:55 +00:00
|
|
|
|
|
|
|
static int s_AutoMapperButton = 0;
|
2018-10-03 16:16:58 +00:00
|
|
|
if(m_pEditor->DoButton_Editor(&s_AutoMapperButton, "Automap", 0, &Button, 0, "Run the automapper"))
|
|
|
|
{
|
2022-06-11 19:38:18 +00:00
|
|
|
m_pEditor->m_Map.m_vpImages[m_Image]->m_AutoMapper.Proceed(this, m_AutoMapperConfig, m_Seed);
|
2023-03-27 15:22:17 +00:00
|
|
|
return CUI::POPUP_CLOSE_CURRENT;
|
2018-10-03 16:16:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2008-01-12 12:27:55 +00:00
|
|
|
enum
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
PROP_WIDTH = 0,
|
2008-01-12 12:27:55 +00:00
|
|
|
PROP_HEIGHT,
|
2010-10-09 16:38:23 +00:00
|
|
|
PROP_SHIFT,
|
2014-05-31 23:32:54 +00:00
|
|
|
PROP_SHIFT_BY,
|
2008-03-21 00:13:55 +00:00
|
|
|
PROP_IMAGE,
|
2011-01-04 10:38:14 +00:00
|
|
|
PROP_COLOR,
|
2011-07-18 10:05:12 +00:00
|
|
|
PROP_COLOR_ENV,
|
|
|
|
PROP_COLOR_ENV_OFFSET,
|
2018-10-03 16:16:58 +00:00
|
|
|
PROP_AUTOMAPPER,
|
|
|
|
PROP_SEED,
|
2008-01-12 12:27:55 +00:00
|
|
|
NUM_PROPS,
|
|
|
|
};
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-01-04 10:38:14 +00:00
|
|
|
int Color = 0;
|
2020-09-26 19:41:58 +00:00
|
|
|
Color |= m_Color.r << 24;
|
|
|
|
Color |= m_Color.g << 16;
|
|
|
|
Color |= m_Color.b << 8;
|
2011-01-04 10:38:14 +00:00
|
|
|
Color |= m_Color.a;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
CProperty aProps[] = {
|
2019-04-17 19:48:23 +00:00
|
|
|
{"Width", m_Width, PROPTYPE_INT_SCROLL, 1, 100000},
|
|
|
|
{"Height", m_Height, PROPTYPE_INT_SCROLL, 1, 100000},
|
2011-03-20 16:04:03 +00:00
|
|
|
{"Shift", 0, PROPTYPE_SHIFT, 0, 0},
|
2019-04-17 19:48:23 +00:00
|
|
|
{"Shift by", m_pEditor->m_ShiftBy, PROPTYPE_INT_SCROLL, 1, 100000},
|
2020-05-30 07:08:59 +00:00
|
|
|
{"Image", m_Image, PROPTYPE_IMAGE, 0, 0},
|
2011-03-20 16:04:03 +00:00
|
|
|
{"Color", Color, PROPTYPE_COLOR, 0, 0},
|
2020-09-26 19:41:58 +00:00
|
|
|
{"Color Env", m_ColorEnv + 1, PROPTYPE_ENVELOPE, 0, 0},
|
2011-07-18 10:05:12 +00:00
|
|
|
{"Color TO", m_ColorEnvOffset, PROPTYPE_INT_SCROLL, -1000000, 1000000},
|
2018-10-03 16:16:58 +00:00
|
|
|
{"Auto Rule", m_AutoMapperConfig, PROPTYPE_AUTOMAPPER, m_Image, 0},
|
|
|
|
{"Seed", m_Seed, PROPTYPE_INT_SCROLL, 0, 1000000000},
|
2022-06-13 16:28:13 +00:00
|
|
|
{nullptr},
|
2008-01-12 12:27:55 +00:00
|
|
|
};
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2023-01-17 21:39:31 +00:00
|
|
|
if(EntitiesLayer) // remove the image and color properties if this is a game layer
|
2011-01-04 10:38:14 +00:00
|
|
|
{
|
2022-06-13 16:28:13 +00:00
|
|
|
aProps[PROP_IMAGE].m_pName = nullptr;
|
|
|
|
aProps[PROP_COLOR].m_pName = nullptr;
|
|
|
|
aProps[PROP_AUTOMAPPER].m_pName = nullptr;
|
2018-10-03 16:16:58 +00:00
|
|
|
}
|
|
|
|
if(m_Image == -1)
|
|
|
|
{
|
2022-06-13 16:28:13 +00:00
|
|
|
aProps[PROP_AUTOMAPPER].m_pName = nullptr;
|
|
|
|
aProps[PROP_SEED].m_pName = nullptr;
|
2011-01-04 10:38:14 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
static int s_aIds[NUM_PROPS] = {0};
|
|
|
|
int NewVal = 0;
|
2011-03-21 23:31:42 +00:00
|
|
|
int Prop = m_pEditor->DoProperties(pToolBox, aProps, s_aIds, &NewVal);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(Prop == PROP_WIDTH && NewVal > 1)
|
2016-04-27 15:49:14 +00:00
|
|
|
{
|
|
|
|
if(NewVal > 1000 && !m_pEditor->m_LargeLayerWasWarned)
|
|
|
|
{
|
2023-04-01 11:55:50 +00:00
|
|
|
m_pEditor->m_PopupEventType = CEditor::POPEVENT_LARGELAYER;
|
2016-04-27 15:49:14 +00:00
|
|
|
m_pEditor->m_PopupEventActivated = true;
|
|
|
|
m_pEditor->m_LargeLayerWasWarned = true;
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
Resize(NewVal, m_Height);
|
2016-04-27 15:49:14 +00:00
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
else if(Prop == PROP_HEIGHT && NewVal > 1)
|
2016-04-27 15:49:14 +00:00
|
|
|
{
|
|
|
|
if(NewVal > 1000 && !m_pEditor->m_LargeLayerWasWarned)
|
|
|
|
{
|
2023-04-01 11:55:50 +00:00
|
|
|
m_pEditor->m_PopupEventType = CEditor::POPEVENT_LARGELAYER;
|
2016-04-27 15:49:14 +00:00
|
|
|
m_pEditor->m_PopupEventActivated = true;
|
|
|
|
m_pEditor->m_LargeLayerWasWarned = true;
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
Resize(m_Width, NewVal);
|
2016-04-27 15:49:14 +00:00
|
|
|
}
|
2010-10-09 16:38:23 +00:00
|
|
|
else if(Prop == PROP_SHIFT)
|
2023-04-01 21:16:55 +00:00
|
|
|
{
|
2010-10-09 16:38:23 +00:00
|
|
|
Shift(NewVal);
|
2023-04-01 21:16:55 +00:00
|
|
|
}
|
2014-05-31 23:32:54 +00:00
|
|
|
else if(Prop == PROP_SHIFT_BY)
|
2023-04-01 21:16:55 +00:00
|
|
|
{
|
2014-05-31 23:32:54 +00:00
|
|
|
m_pEditor->m_ShiftBy = NewVal;
|
2023-04-01 21:16:55 +00:00
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
else if(Prop == PROP_IMAGE)
|
2008-09-07 08:30:49 +00:00
|
|
|
{
|
2020-05-28 08:09:27 +00:00
|
|
|
m_Image = NewVal;
|
2020-09-26 19:41:58 +00:00
|
|
|
if(NewVal == -1)
|
2008-09-07 08:30:49 +00:00
|
|
|
{
|
2016-07-02 08:33:37 +00:00
|
|
|
m_Texture.Invalidate();
|
2020-05-30 07:08:59 +00:00
|
|
|
m_Image = -1;
|
2008-09-07 08:30:49 +00:00
|
|
|
}
|
|
|
|
else
|
2018-10-03 16:16:58 +00:00
|
|
|
{
|
2022-06-11 19:38:18 +00:00
|
|
|
m_Image = NewVal % m_pEditor->m_Map.m_vpImages.size();
|
2018-10-03 16:16:58 +00:00
|
|
|
m_AutoMapperConfig = -1;
|
2020-06-20 23:14:36 +00:00
|
|
|
|
2022-06-11 19:38:18 +00:00
|
|
|
if(m_pEditor->m_Map.m_vpImages[m_Image]->m_Width % 16 != 0 || m_pEditor->m_Map.m_vpImages[m_Image]->m_Height % 16 != 0)
|
2020-06-20 23:14:36 +00:00
|
|
|
{
|
2023-04-01 11:55:50 +00:00
|
|
|
m_pEditor->m_PopupEventType = CEditor::POPEVENT_IMAGEDIV16;
|
2020-06-20 23:14:36 +00:00
|
|
|
m_pEditor->m_PopupEventActivated = true;
|
2020-09-21 03:57:54 +00:00
|
|
|
|
2023-03-10 16:18:24 +00:00
|
|
|
m_Texture.Invalidate();
|
2020-09-21 03:57:54 +00:00
|
|
|
m_Image = -1;
|
2020-06-20 23:14:36 +00:00
|
|
|
}
|
2018-10-03 16:16:58 +00:00
|
|
|
}
|
2008-09-07 08:30:49 +00:00
|
|
|
}
|
2011-01-04 10:38:14 +00:00
|
|
|
else if(Prop == PROP_COLOR)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
m_Color.r = (NewVal >> 24) & 0xff;
|
|
|
|
m_Color.g = (NewVal >> 16) & 0xff;
|
|
|
|
m_Color.b = (NewVal >> 8) & 0xff;
|
|
|
|
m_Color.a = NewVal & 0xff;
|
2011-01-04 10:38:14 +00:00
|
|
|
}
|
2023-04-01 21:16:55 +00:00
|
|
|
else if(Prop == PROP_COLOR_ENV)
|
2011-08-06 20:30:18 +00:00
|
|
|
{
|
2022-06-11 19:38:18 +00:00
|
|
|
int Index = clamp(NewVal - 1, -1, (int)m_pEditor->m_Map.m_vpEnvelopes.size() - 1);
|
2023-04-01 21:16:55 +00:00
|
|
|
const int Step = (Index - m_ColorEnv) % 2;
|
2011-08-06 20:30:18 +00:00
|
|
|
if(Step != 0)
|
|
|
|
{
|
2022-06-11 19:38:18 +00:00
|
|
|
for(; Index >= -1 && Index < (int)m_pEditor->m_Map.m_vpEnvelopes.size(); Index += Step)
|
2023-04-01 21:16:55 +00:00
|
|
|
{
|
2022-06-11 19:38:18 +00:00
|
|
|
if(Index == -1 || m_pEditor->m_Map.m_vpEnvelopes[Index]->m_Channels == 4)
|
2011-08-06 20:30:18 +00:00
|
|
|
{
|
|
|
|
m_ColorEnv = Index;
|
|
|
|
break;
|
|
|
|
}
|
2023-04-01 21:16:55 +00:00
|
|
|
}
|
2011-08-06 20:30:18 +00:00
|
|
|
}
|
|
|
|
}
|
2023-04-01 21:16:55 +00:00
|
|
|
else if(Prop == PROP_COLOR_ENV_OFFSET)
|
|
|
|
{
|
2011-07-18 10:05:12 +00:00
|
|
|
m_ColorEnvOffset = NewVal;
|
2023-04-01 21:16:55 +00:00
|
|
|
}
|
2018-10-03 16:16:58 +00:00
|
|
|
else if(Prop == PROP_SEED)
|
2023-04-01 21:16:55 +00:00
|
|
|
{
|
2018-10-03 16:16:58 +00:00
|
|
|
m_Seed = NewVal;
|
2023-04-01 21:16:55 +00:00
|
|
|
}
|
2018-10-03 16:16:58 +00:00
|
|
|
else if(Prop == PROP_AUTOMAPPER)
|
|
|
|
{
|
2022-06-11 19:38:18 +00:00
|
|
|
if(m_Image >= 0 && m_pEditor->m_Map.m_vpImages[m_Image]->m_AutoMapper.ConfigNamesNum() > 0 && NewVal >= 0)
|
|
|
|
m_AutoMapperConfig = NewVal % m_pEditor->m_Map.m_vpImages[m_Image]->m_AutoMapper.ConfigNamesNum();
|
2018-10-03 16:16:58 +00:00
|
|
|
else
|
|
|
|
m_AutoMapperConfig = -1;
|
|
|
|
}
|
2023-04-01 21:16:55 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
if(Prop != -1)
|
|
|
|
{
|
2018-10-04 13:26:41 +00:00
|
|
|
FlagModified(0, 0, m_Width, m_Height);
|
2018-10-03 16:16:58 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2023-03-27 15:22:17 +00:00
|
|
|
return CUI::POPUP_KEEP_OPEN;
|
2008-01-12 12:27:55 +00:00
|
|
|
}
|
2008-01-19 12:32:08 +00:00
|
|
|
|
2023-03-27 15:22:17 +00:00
|
|
|
CUI::EPopupMenuFunctionResult CLayerTiles::RenderCommonProperties(SCommonPropState &State, CEditor *pEditor, CUIRect *pToolbox, std::vector<CLayerTiles *> &vpLayers)
|
2020-02-27 13:53:12 +00:00
|
|
|
{
|
2022-06-27 21:09:31 +00:00
|
|
|
if(State.m_Modified)
|
2020-02-28 15:25:27 +00:00
|
|
|
{
|
|
|
|
CUIRect Commit;
|
|
|
|
pToolbox->HSplitBottom(20.0f, pToolbox, &Commit);
|
|
|
|
static int s_CommitButton = 0;
|
|
|
|
if(pEditor->DoButton_Editor(&s_CommitButton, "Commit", 0, &Commit, 0, "Applies the changes"))
|
|
|
|
{
|
2022-06-11 19:38:18 +00:00
|
|
|
for(auto &pLayer : vpLayers)
|
2022-05-24 09:24:33 +00:00
|
|
|
{
|
2022-06-27 21:18:58 +00:00
|
|
|
if((State.m_Modified & SCommonPropState::MODIFIED_SIZE) != 0)
|
|
|
|
pLayer->Resize(State.m_Width, State.m_Height);
|
2020-02-28 15:25:27 +00:00
|
|
|
|
2022-06-27 21:18:58 +00:00
|
|
|
if((State.m_Modified & SCommonPropState::MODIFIED_COLOR) != 0)
|
|
|
|
{
|
|
|
|
pLayer->m_Color.r = (State.m_Color >> 24) & 0xff;
|
|
|
|
pLayer->m_Color.g = (State.m_Color >> 16) & 0xff;
|
|
|
|
pLayer->m_Color.b = (State.m_Color >> 8) & 0xff;
|
|
|
|
pLayer->m_Color.a = State.m_Color & 0xff;
|
|
|
|
}
|
2020-02-28 15:25:27 +00:00
|
|
|
|
|
|
|
pLayer->FlagModified(0, 0, pLayer->m_Width, pLayer->m_Height);
|
2022-05-24 09:24:33 +00:00
|
|
|
}
|
2022-06-27 21:18:58 +00:00
|
|
|
State.m_Modified = 0;
|
2020-02-28 15:25:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-06-11 19:38:18 +00:00
|
|
|
for(auto &pLayer : vpLayers)
|
2022-05-24 09:24:33 +00:00
|
|
|
{
|
2022-06-27 21:09:31 +00:00
|
|
|
if(pLayer->m_Width > State.m_Width)
|
|
|
|
State.m_Width = pLayer->m_Width;
|
|
|
|
if(pLayer->m_Height > State.m_Height)
|
|
|
|
State.m_Height = pLayer->m_Height;
|
2022-05-24 09:24:33 +00:00
|
|
|
}
|
2020-02-28 15:25:27 +00:00
|
|
|
}
|
|
|
|
|
2020-02-27 15:02:36 +00:00
|
|
|
{
|
|
|
|
CUIRect Warning;
|
|
|
|
pToolbox->HSplitTop(13.0f, &Warning, pToolbox);
|
|
|
|
Warning.HMargin(0.5f, &Warning);
|
|
|
|
|
|
|
|
pEditor->TextRender()->TextColor(ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f));
|
2022-03-11 16:34:48 +00:00
|
|
|
SLabelProperties Props;
|
|
|
|
Props.m_MaxWidth = Warning.w;
|
2023-04-09 19:24:06 +00:00
|
|
|
pEditor->UI()->DoLabel(&Warning, "Editing multiple layers", 9.0f, TEXTALIGN_ML, Props);
|
2020-02-27 15:02:36 +00:00
|
|
|
pEditor->TextRender()->TextColor(ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
|
2022-06-13 16:28:13 +00:00
|
|
|
pToolbox->HSplitTop(2.0f, nullptr, pToolbox);
|
2020-02-27 15:02:36 +00:00
|
|
|
}
|
|
|
|
|
2020-02-27 13:53:12 +00:00
|
|
|
enum
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
PROP_WIDTH = 0,
|
2020-02-27 13:53:12 +00:00
|
|
|
PROP_HEIGHT,
|
|
|
|
PROP_SHIFT,
|
2020-02-28 12:33:32 +00:00
|
|
|
PROP_SHIFT_BY,
|
2020-02-27 15:02:36 +00:00
|
|
|
PROP_COLOR,
|
2020-02-27 13:53:12 +00:00
|
|
|
NUM_PROPS,
|
|
|
|
};
|
|
|
|
|
|
|
|
CProperty aProps[] = {
|
2022-06-27 21:09:31 +00:00
|
|
|
{"Width", State.m_Width, PROPTYPE_INT_SCROLL, 1, 100000},
|
|
|
|
{"Height", State.m_Height, PROPTYPE_INT_SCROLL, 1, 100000},
|
2020-02-27 13:53:12 +00:00
|
|
|
{"Shift", 0, PROPTYPE_SHIFT, 0, 0},
|
2020-02-28 12:33:32 +00:00
|
|
|
{"Shift by", pEditor->m_ShiftBy, PROPTYPE_INT_SCROLL, 1, 100000},
|
2022-06-27 21:09:31 +00:00
|
|
|
{"Color", State.m_Color, PROPTYPE_COLOR, 0, 0},
|
2022-06-13 16:28:13 +00:00
|
|
|
{nullptr},
|
2020-02-27 13:53:12 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int s_aIds[NUM_PROPS] = {0};
|
|
|
|
int NewVal = 0;
|
|
|
|
int Prop = pEditor->DoProperties(pToolbox, aProps, s_aIds, &NewVal);
|
|
|
|
|
|
|
|
if(Prop == PROP_WIDTH && NewVal > 1)
|
|
|
|
{
|
|
|
|
if(NewVal > 1000 && !pEditor->m_LargeLayerWasWarned)
|
|
|
|
{
|
|
|
|
pEditor->m_PopupEventType = pEditor->POPEVENT_LARGELAYER;
|
|
|
|
pEditor->m_PopupEventActivated = true;
|
|
|
|
pEditor->m_LargeLayerWasWarned = true;
|
|
|
|
}
|
2022-06-27 21:09:31 +00:00
|
|
|
State.m_Width = NewVal;
|
2020-02-27 13:53:12 +00:00
|
|
|
}
|
|
|
|
else if(Prop == PROP_HEIGHT && NewVal > 1)
|
|
|
|
{
|
|
|
|
if(NewVal > 1000 && !pEditor->m_LargeLayerWasWarned)
|
|
|
|
{
|
|
|
|
pEditor->m_PopupEventType = pEditor->POPEVENT_LARGELAYER;
|
|
|
|
pEditor->m_PopupEventActivated = true;
|
|
|
|
pEditor->m_LargeLayerWasWarned = true;
|
|
|
|
}
|
2022-06-27 21:09:31 +00:00
|
|
|
State.m_Height = NewVal;
|
2020-02-27 13:53:12 +00:00
|
|
|
}
|
|
|
|
else if(Prop == PROP_SHIFT)
|
|
|
|
{
|
2022-06-11 19:38:18 +00:00
|
|
|
for(auto &pLayer : vpLayers)
|
2020-02-27 13:53:12 +00:00
|
|
|
pLayer->Shift(NewVal);
|
|
|
|
}
|
2020-02-28 12:33:32 +00:00
|
|
|
else if(Prop == PROP_SHIFT_BY)
|
2023-04-01 21:16:55 +00:00
|
|
|
{
|
2020-02-28 12:33:32 +00:00
|
|
|
pEditor->m_ShiftBy = NewVal;
|
2023-04-01 21:16:55 +00:00
|
|
|
}
|
2020-02-27 15:02:36 +00:00
|
|
|
else if(Prop == PROP_COLOR)
|
|
|
|
{
|
2022-06-27 21:09:31 +00:00
|
|
|
State.m_Color = NewVal;
|
2020-02-27 15:02:36 +00:00
|
|
|
}
|
2020-02-27 13:53:12 +00:00
|
|
|
|
2022-06-27 21:18:58 +00:00
|
|
|
if(Prop == PROP_WIDTH || Prop == PROP_HEIGHT)
|
2023-04-01 21:16:55 +00:00
|
|
|
{
|
2022-06-27 21:18:58 +00:00
|
|
|
State.m_Modified |= SCommonPropState::MODIFIED_SIZE;
|
2023-04-01 21:16:55 +00:00
|
|
|
}
|
2022-06-27 21:18:58 +00:00
|
|
|
else if(Prop == PROP_COLOR)
|
2023-04-01 21:16:55 +00:00
|
|
|
{
|
2022-06-27 21:18:58 +00:00
|
|
|
State.m_Modified |= SCommonPropState::MODIFIED_COLOR;
|
2023-04-01 21:16:55 +00:00
|
|
|
}
|
2020-02-27 13:53:12 +00:00
|
|
|
|
2023-03-27 15:22:17 +00:00
|
|
|
return CUI::POPUP_KEEP_OPEN;
|
2020-02-27 13:53:12 +00:00
|
|
|
}
|
|
|
|
|
2019-12-21 13:35:09 +00:00
|
|
|
void CLayerTiles::FlagModified(int x, int y, int w, int h)
|
|
|
|
{
|
Autosave copy of current editor map periodically to `auto` folder
A copy of the map currently open in the editor is saved every 10 minutes to the `maps/auto` folder (interval configurable, see below). The automatically saved map uses the filename of the original map with an additional timestamp. Per map name 10 autosaves are kept in the `auto` folder before old autosaves will be deleted (number configurable, see below).
Add config variable `ed_autosave_interval` (0 - 240, default 10) to configure the interval in minutes at which a copy of the current editor map is automatically saved to the 'auto' folder.
Add config variable `ed_autosave_max` (0 - 1000, default 10) to configure the maximum number of autosaves that are kept per map name (0 = no limit).
Autosaving will not take place in the 5 seconds immediately after the map was last modified by the user, to avoid interrupting the user with the autosave.
This will only delay autosaving for up to 1 minute though, so autosaves are not prevented entirely, should the user continuously edit the map.
When the editor is reopened after being closed for more than 10 seconds, the autosave timer will be adjusted to compensate for the time that was not spent on editing in the editor.
When the map is saved manually by the user the autosave file is also updated, if it's outdated by at least half of the configured autosave interval. This ensures that autosaves are always available as a periodic backup of the map.
When a copy of the current map is saved, this does not update the autosave and will also no longer reset the modified state. The modified state should reflect whether changes have been made that are not saved to the current map file. As saving a copy does not update the current file, the modified state should not be reset in this case.
Closes #6693.
2023-06-23 15:39:05 +00:00
|
|
|
m_pEditor->m_Map.OnModify();
|
2021-02-19 14:43:39 +00:00
|
|
|
if(m_Seed != 0 && m_AutoMapperConfig != -1 && m_AutoAutoMap && m_Image >= 0)
|
2020-09-26 19:41:58 +00:00
|
|
|
{
|
2022-06-11 19:38:18 +00:00
|
|
|
m_pEditor->m_Map.m_vpImages[m_Image]->m_AutoMapper.ProceedLocalized(this, m_AutoMapperConfig, m_Seed, x, y, w, h);
|
2018-10-03 16:16:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CLayerTiles::ModifyImageIndex(INDEX_MODIFY_FUNC Func)
|
2008-01-19 12:32:08 +00:00
|
|
|
{
|
2022-07-31 19:39:18 +00:00
|
|
|
const auto ImgBefore = m_Image;
|
2010-05-29 07:25:38 +00:00
|
|
|
Func(&m_Image);
|
2022-07-31 19:39:18 +00:00
|
|
|
if(m_Image != ImgBefore)
|
|
|
|
m_Texture.Invalidate();
|
2008-01-19 12:32:08 +00:00
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CLayerTiles::ModifyEnvelopeIndex(INDEX_MODIFY_FUNC Func)
|
2008-01-19 12:32:08 +00:00
|
|
|
{
|
2020-01-23 17:30:55 +00:00
|
|
|
Func(&m_ColorEnv);
|
2008-01-19 12:32:08 +00:00
|
|
|
}
|
2010-09-30 21:31:19 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
CLayerTele::CLayerTele(int w, int h) :
|
|
|
|
CLayerTiles(w, h)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2023-06-13 15:44:30 +00:00
|
|
|
str_copy(m_aName, "Tele");
|
2010-09-30 21:31:19 +00:00
|
|
|
m_Tele = 1;
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pTeleTile = new CTeleTile[w * h];
|
2020-10-05 17:03:14 +00:00
|
|
|
mem_zero(m_pTeleTile, (size_t)w * h * sizeof(CTeleTile));
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CLayerTele::~CLayerTele()
|
|
|
|
{
|
|
|
|
delete[] m_pTeleTile;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CLayerTele::Resize(int NewW, int NewH)
|
|
|
|
{
|
|
|
|
// resize tele data
|
2020-09-26 19:41:58 +00:00
|
|
|
CTeleTile *pNewTeleData = new CTeleTile[NewW * NewH];
|
2020-10-05 17:03:14 +00:00
|
|
|
mem_zero(pNewTeleData, (size_t)NewW * NewH * sizeof(CTeleTile));
|
2010-09-30 21:31:19 +00:00
|
|
|
|
2011-04-09 06:41:31 +00:00
|
|
|
// copy old data
|
2019-04-26 19:36:49 +00:00
|
|
|
for(int y = 0; y < minimum(NewH, m_Height); y++)
|
2020-09-26 19:41:58 +00:00
|
|
|
mem_copy(&pNewTeleData[y * NewW], &m_pTeleTile[y * m_Width], minimum(m_Width, NewW) * sizeof(CTeleTile));
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
// replace old
|
2020-09-26 19:41:58 +00:00
|
|
|
delete[] m_pTeleTile;
|
2010-09-30 21:31:19 +00:00
|
|
|
m_pTeleTile = pNewTeleData;
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
// resize tile data
|
|
|
|
CLayerTiles::Resize(NewW, NewH);
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
// resize gamelayer too
|
|
|
|
if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH)
|
|
|
|
m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH);
|
|
|
|
}
|
|
|
|
|
2010-10-20 13:56:29 +00:00
|
|
|
void CLayerTele::Shift(int Direction)
|
|
|
|
{
|
|
|
|
CLayerTiles::Shift(Direction);
|
2022-06-05 20:03:16 +00:00
|
|
|
ShiftImpl(m_pTeleTile, Direction, m_pEditor->m_ShiftBy);
|
2010-10-20 13:56:29 +00:00
|
|
|
}
|
|
|
|
|
2019-03-26 18:15:24 +00:00
|
|
|
bool CLayerTele::IsEmpty(CLayerTiles *pLayer)
|
|
|
|
{
|
|
|
|
for(int y = 0; y < pLayer->m_Height; y++)
|
|
|
|
for(int x = 0; x < pLayer->m_Width; x++)
|
2021-09-01 09:43:32 +00:00
|
|
|
if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidTeleTile(pLayer->GetTile(x, y).m_Index))
|
2019-03-26 18:15:24 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
void CLayerTele::BrushDraw(CLayer *pBrush, float wx, float wy)
|
|
|
|
{
|
2011-08-26 16:46:27 +00:00
|
|
|
if(m_Readonly)
|
|
|
|
return;
|
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
CLayerTele *pTeleLayer = (CLayerTele *)pBrush;
|
2010-09-30 21:31:19 +00:00
|
|
|
int sx = ConvertX(wx);
|
|
|
|
int sy = ConvertY(wy);
|
2022-06-30 22:36:32 +00:00
|
|
|
if(str_comp(pTeleLayer->m_aFileName, m_pEditor->m_aFileName))
|
|
|
|
m_pEditor->m_TeleNumber = pTeleLayer->m_TeleNum;
|
2010-12-19 14:10:42 +00:00
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(pTeleLayer);
|
2019-03-26 18:15:24 +00:00
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
for(int y = 0; y < pTeleLayer->m_Height; y++)
|
|
|
|
for(int x = 0; x < pTeleLayer->m_Width; x++)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int fx = x + sx;
|
|
|
|
int fy = y + sy;
|
2019-03-26 18:15:24 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height)
|
2010-09-30 21:31:19 +00:00
|
|
|
continue;
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2019-03-26 18:15:24 +00:00
|
|
|
if(!Destructive && GetTile(fx, fy).m_Index)
|
|
|
|
continue;
|
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
if((m_pEditor->m_AllowPlaceUnusedTiles || IsValidTeleTile(pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index)) && pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index != TILE_AIR)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2023-03-03 17:20:55 +00:00
|
|
|
if(!IsTeleTileNumberUsed(pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index))
|
|
|
|
{
|
|
|
|
// Tele tile number is unused. Set a known value which is not 0,
|
|
|
|
// as tiles with number 0 would be ignored by previous versions.
|
|
|
|
m_pTeleTile[fy * m_Width + fx].m_Number = 255;
|
|
|
|
}
|
|
|
|
else if(m_pEditor->m_TeleNumber != pTeleLayer->m_TeleNum)
|
2010-12-04 11:54:53 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pTeleTile[fy * m_Width + fx].m_Number = m_pEditor->m_TeleNumber;
|
2010-12-04 11:54:53 +00:00
|
|
|
}
|
2022-06-30 22:36:32 +00:00
|
|
|
else if(pTeleLayer->m_pTeleTile[y * pTeleLayer->m_Width + x].m_Number)
|
2023-03-03 17:20:55 +00:00
|
|
|
{
|
2022-06-30 22:36:32 +00:00
|
|
|
m_pTeleTile[fy * m_Width + fx].m_Number = pTeleLayer->m_pTeleTile[y * pTeleLayer->m_Width + x].m_Number;
|
2023-03-03 17:20:55 +00:00
|
|
|
}
|
2010-12-04 11:54:53 +00:00
|
|
|
else
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2010-12-07 15:51:59 +00:00
|
|
|
if(!m_pEditor->m_TeleNumber)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pTeleTile[fy * m_Width + fx].m_Number = 0;
|
|
|
|
m_pTeleTile[fy * m_Width + fx].m_Type = 0;
|
|
|
|
m_pTiles[fy * m_Width + fx].m_Index = 0;
|
2010-09-30 21:31:19 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pTeleTile[fy * m_Width + fx].m_Number = m_pEditor->m_TeleNumber;
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
m_pTeleTile[fy * m_Width + fx].m_Type = pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index;
|
|
|
|
m_pTiles[fy * m_Width + fx].m_Index = pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index;
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pTeleTile[fy * m_Width + fx].m_Number = 0;
|
|
|
|
m_pTeleTile[fy * m_Width + fx].m_Type = 0;
|
|
|
|
m_pTiles[fy * m_Width + fx].m_Index = 0;
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
}
|
2022-06-30 22:36:32 +00:00
|
|
|
FlagModified(sx, sy, pTeleLayer->m_Width, pTeleLayer->m_Height);
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
|
2011-04-17 15:59:02 +00:00
|
|
|
void CLayerTele::BrushFlipX()
|
|
|
|
{
|
|
|
|
CLayerTiles::BrushFlipX();
|
2022-06-05 20:21:24 +00:00
|
|
|
BrushFlipXImpl(m_pTeleTile);
|
2011-04-17 15:59:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CLayerTele::BrushFlipY()
|
|
|
|
{
|
|
|
|
CLayerTiles::BrushFlipY();
|
2022-06-05 20:21:24 +00:00
|
|
|
BrushFlipYImpl(m_pTeleTile);
|
2011-04-17 15:59:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CLayerTele::BrushRotate(float Amount)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int Rotation = (round_to_int(360.0f * Amount / (pi * 2)) / 90) % 4; // 0=0°, 1=90°, 2=180°, 3=270°
|
2011-04-17 15:59:02 +00:00
|
|
|
if(Rotation < 0)
|
2020-09-26 19:41:58 +00:00
|
|
|
Rotation += 4;
|
2011-04-17 15:59:02 +00:00
|
|
|
|
|
|
|
if(Rotation == 1 || Rotation == 3)
|
|
|
|
{
|
2014-03-19 09:26:38 +00:00
|
|
|
// 90° rotation
|
2020-09-26 19:41:58 +00:00
|
|
|
CTeleTile *pTempData1 = new CTeleTile[m_Width * m_Height];
|
|
|
|
CTile *pTempData2 = new CTile[m_Width * m_Height];
|
2020-10-05 17:03:14 +00:00
|
|
|
mem_copy(pTempData1, m_pTeleTile, (size_t)m_Width * m_Height * sizeof(CTeleTile));
|
|
|
|
mem_copy(pTempData2, m_pTiles, (size_t)m_Width * m_Height * sizeof(CTile));
|
2011-04-17 15:59:02 +00:00
|
|
|
CTeleTile *pDst1 = m_pTeleTile;
|
|
|
|
CTile *pDst2 = m_pTiles;
|
|
|
|
for(int x = 0; x < m_Width; ++x)
|
2020-09-26 19:41:58 +00:00
|
|
|
for(int y = m_Height - 1; y >= 0; --y, ++pDst1, ++pDst2)
|
2011-04-17 15:59:02 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
*pDst1 = pTempData1[y * m_Width + x];
|
|
|
|
*pDst2 = pTempData2[y * m_Width + x];
|
2011-04-17 15:59:02 +00:00
|
|
|
}
|
|
|
|
|
2022-05-15 18:44:05 +00:00
|
|
|
std::swap(m_Width, m_Height);
|
2011-04-17 15:59:02 +00:00
|
|
|
delete[] pTempData1;
|
|
|
|
delete[] pTempData2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(Rotation == 2 || Rotation == 3)
|
|
|
|
{
|
|
|
|
BrushFlipX();
|
|
|
|
BrushFlipY();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
void CLayerTele::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect)
|
|
|
|
{
|
2018-10-30 17:56:46 +00:00
|
|
|
if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES))
|
2018-09-26 12:53:25 +00:00
|
|
|
return;
|
2015-07-09 00:08:14 +00:00
|
|
|
|
2014-03-12 22:34:30 +00:00
|
|
|
Snap(&Rect); // corrects Rect; no need of <=
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2014-03-19 09:26:38 +00:00
|
|
|
Snap(&Rect);
|
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
int sx = ConvertX(Rect.x);
|
|
|
|
int sy = ConvertY(Rect.y);
|
|
|
|
int w = ConvertX(Rect.w);
|
|
|
|
int h = ConvertY(Rect.h);
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
CLayerTele *pLt = static_cast<CLayerTele *>(pBrush);
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2019-04-30 12:24:13 +00:00
|
|
|
bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLt);
|
2019-03-26 18:15:24 +00:00
|
|
|
|
2014-03-19 09:26:38 +00:00
|
|
|
for(int y = 0; y < h; y++)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2014-03-19 09:26:38 +00:00
|
|
|
for(int x = 0; x < w; x++)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int fx = x + sx;
|
|
|
|
int fy = y + sy;
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height)
|
|
|
|
continue;
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2019-03-26 18:15:24 +00:00
|
|
|
if(!Destructive && GetTile(fx, fy).m_Index)
|
|
|
|
continue;
|
|
|
|
|
2023-07-07 17:07:33 +00:00
|
|
|
const int SrcIndex = Empty ? 0 : (y * pLt->m_Width + x % pLt->m_Width) % (pLt->m_Width * pLt->m_Height);
|
2023-05-20 20:16:23 +00:00
|
|
|
const int TgtIndex = fy * m_Width + fx;
|
|
|
|
|
|
|
|
if(Empty || (!m_pEditor->m_AllowPlaceUnusedTiles && !IsValidTeleTile((pLt->m_pTiles[SrcIndex]).m_Index)))
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pTiles[TgtIndex].m_Index = 0;
|
|
|
|
m_pTeleTile[TgtIndex].m_Type = 0;
|
|
|
|
m_pTeleTile[TgtIndex].m_Number = 0;
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
2011-02-13 05:35:13 +00:00
|
|
|
else
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pTiles[TgtIndex] = pLt->m_pTiles[SrcIndex];
|
|
|
|
if(pLt->m_Tele && m_pTiles[TgtIndex].m_Index > 0)
|
2011-02-13 05:35:13 +00:00
|
|
|
{
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pTeleTile[TgtIndex].m_Type = m_pTiles[TgtIndex].m_Index;
|
|
|
|
|
2023-05-21 09:59:08 +00:00
|
|
|
if(!IsTeleTileNumberUsed(m_pTeleTile[TgtIndex].m_Type))
|
|
|
|
{
|
|
|
|
// Tele tile number is unused. Set a known value which is not 0,
|
|
|
|
// as tiles with number 0 would be ignored by previous versions.
|
|
|
|
m_pTeleTile[TgtIndex].m_Number = 255;
|
|
|
|
}
|
|
|
|
else if((pLt->m_pTeleTile[SrcIndex].m_Number == 0 && m_pEditor->m_TeleNumber) || m_pEditor->m_TeleNumber != pLt->m_TeleNum)
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pTeleTile[TgtIndex].m_Number = m_pEditor->m_TeleNumber;
|
2010-12-07 15:51:59 +00:00
|
|
|
else
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pTeleTile[TgtIndex].m_Number = pLt->m_pTeleTile[SrcIndex].m_Number;
|
2011-02-13 05:35:13 +00:00
|
|
|
}
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-04 13:26:41 +00:00
|
|
|
FlagModified(sx, sy, w, h);
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
|
2020-05-15 22:42:11 +00:00
|
|
|
bool CLayerTele::ContainsElementWithId(int Id)
|
|
|
|
{
|
|
|
|
for(int y = 0; y < m_Height; ++y)
|
|
|
|
{
|
|
|
|
for(int x = 0; x < m_Width; ++x)
|
|
|
|
{
|
2023-03-03 16:57:25 +00:00
|
|
|
if(IsTeleTileNumberUsed(m_pTeleTile[y * m_Width + x].m_Type) && m_pTeleTile[y * m_Width + x].m_Number == Id)
|
2020-05-15 22:42:11 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
CLayerSpeedup::CLayerSpeedup(int w, int h) :
|
|
|
|
CLayerTiles(w, h)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2023-06-13 15:44:30 +00:00
|
|
|
str_copy(m_aName, "Speedup");
|
2010-09-30 21:31:19 +00:00
|
|
|
m_Speedup = 1;
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pSpeedupTile = new CSpeedupTile[w * h];
|
2020-10-05 17:03:14 +00:00
|
|
|
mem_zero(m_pSpeedupTile, (size_t)w * h * sizeof(CSpeedupTile));
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CLayerSpeedup::~CLayerSpeedup()
|
|
|
|
{
|
|
|
|
delete[] m_pSpeedupTile;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CLayerSpeedup::Resize(int NewW, int NewH)
|
|
|
|
{
|
|
|
|
// resize speedup data
|
2020-09-26 19:41:58 +00:00
|
|
|
CSpeedupTile *pNewSpeedupData = new CSpeedupTile[NewW * NewH];
|
2020-10-05 17:03:14 +00:00
|
|
|
mem_zero(pNewSpeedupData, (size_t)NewW * NewH * sizeof(CSpeedupTile));
|
2010-09-30 21:31:19 +00:00
|
|
|
|
2011-04-09 06:41:31 +00:00
|
|
|
// copy old data
|
2019-04-26 19:36:49 +00:00
|
|
|
for(int y = 0; y < minimum(NewH, m_Height); y++)
|
2020-09-26 19:41:58 +00:00
|
|
|
mem_copy(&pNewSpeedupData[y * NewW], &m_pSpeedupTile[y * m_Width], minimum(m_Width, NewW) * sizeof(CSpeedupTile));
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
// replace old
|
2020-09-26 19:41:58 +00:00
|
|
|
delete[] m_pSpeedupTile;
|
2010-09-30 21:31:19 +00:00
|
|
|
m_pSpeedupTile = pNewSpeedupData;
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
// resize tile data
|
|
|
|
CLayerTiles::Resize(NewW, NewH);
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
// resize gamelayer too
|
|
|
|
if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH)
|
|
|
|
m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH);
|
|
|
|
}
|
|
|
|
|
2010-10-20 13:56:29 +00:00
|
|
|
void CLayerSpeedup::Shift(int Direction)
|
|
|
|
{
|
|
|
|
CLayerTiles::Shift(Direction);
|
2022-06-05 20:03:16 +00:00
|
|
|
ShiftImpl(m_pSpeedupTile, Direction, m_pEditor->m_ShiftBy);
|
2010-10-20 13:56:29 +00:00
|
|
|
}
|
|
|
|
|
2019-03-26 18:15:24 +00:00
|
|
|
bool CLayerSpeedup::IsEmpty(CLayerTiles *pLayer)
|
|
|
|
{
|
|
|
|
for(int y = 0; y < pLayer->m_Height; y++)
|
|
|
|
for(int x = 0; x < pLayer->m_Width; x++)
|
2021-09-01 09:43:32 +00:00
|
|
|
if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidSpeedupTile(pLayer->GetTile(x, y).m_Index))
|
2019-03-26 18:15:24 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
void CLayerSpeedup::BrushDraw(CLayer *pBrush, float wx, float wy)
|
|
|
|
{
|
2011-08-26 16:46:27 +00:00
|
|
|
if(m_Readonly)
|
|
|
|
return;
|
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
CLayerSpeedup *pSpeedupLayer = (CLayerSpeedup *)pBrush;
|
2010-09-30 21:31:19 +00:00
|
|
|
int sx = ConvertX(wx);
|
|
|
|
int sy = ConvertY(wy);
|
2022-06-30 22:36:32 +00:00
|
|
|
if(str_comp(pSpeedupLayer->m_aFileName, m_pEditor->m_aFileName))
|
2010-12-19 14:10:42 +00:00
|
|
|
{
|
2022-06-30 22:36:32 +00:00
|
|
|
m_pEditor->m_SpeedupAngle = pSpeedupLayer->m_SpeedupAngle;
|
|
|
|
m_pEditor->m_SpeedupForce = pSpeedupLayer->m_SpeedupForce;
|
|
|
|
m_pEditor->m_SpeedupMaxSpeed = pSpeedupLayer->m_SpeedupMaxSpeed;
|
2010-12-19 14:10:42 +00:00
|
|
|
}
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(pSpeedupLayer);
|
2019-03-26 18:15:24 +00:00
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
for(int y = 0; y < pSpeedupLayer->m_Height; y++)
|
|
|
|
for(int x = 0; x < pSpeedupLayer->m_Width; x++)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int fx = x + sx;
|
|
|
|
int fy = y + sy;
|
2019-03-26 18:15:24 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height)
|
2010-09-30 21:31:19 +00:00
|
|
|
continue;
|
2010-12-04 11:54:53 +00:00
|
|
|
|
2019-03-26 18:15:24 +00:00
|
|
|
if(!Destructive && GetTile(fx, fy).m_Index)
|
|
|
|
continue;
|
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
if((m_pEditor->m_AllowPlaceUnusedTiles || IsValidSpeedupTile(pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index)) && pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index != TILE_AIR)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2022-06-30 22:36:32 +00:00
|
|
|
if(m_pEditor->m_SpeedupAngle != pSpeedupLayer->m_SpeedupAngle || m_pEditor->m_SpeedupForce != pSpeedupLayer->m_SpeedupForce || m_pEditor->m_SpeedupMaxSpeed != pSpeedupLayer->m_SpeedupMaxSpeed)
|
2010-12-04 11:54:53 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_Force = m_pEditor->m_SpeedupForce;
|
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_MaxSpeed = m_pEditor->m_SpeedupMaxSpeed;
|
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_Angle = m_pEditor->m_SpeedupAngle;
|
2022-06-30 22:36:32 +00:00
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_Type = pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index;
|
|
|
|
m_pTiles[fy * m_Width + fx].m_Index = pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index;
|
2010-12-04 11:54:53 +00:00
|
|
|
}
|
2022-06-30 22:36:32 +00:00
|
|
|
else if(pSpeedupLayer->m_pSpeedupTile[y * pSpeedupLayer->m_Width + x].m_Force)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2022-06-30 22:36:32 +00:00
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_Force = pSpeedupLayer->m_pSpeedupTile[y * pSpeedupLayer->m_Width + x].m_Force;
|
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_Angle = pSpeedupLayer->m_pSpeedupTile[y * pSpeedupLayer->m_Width + x].m_Angle;
|
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_MaxSpeed = pSpeedupLayer->m_pSpeedupTile[y * pSpeedupLayer->m_Width + x].m_MaxSpeed;
|
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_Type = pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index;
|
|
|
|
m_pTiles[fy * m_Width + fx].m_Index = pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index;
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
2010-12-04 11:54:53 +00:00
|
|
|
else if(m_pEditor->m_SpeedupForce)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_Force = m_pEditor->m_SpeedupForce;
|
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_MaxSpeed = m_pEditor->m_SpeedupMaxSpeed;
|
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_Angle = m_pEditor->m_SpeedupAngle;
|
2022-06-30 22:36:32 +00:00
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_Type = pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index;
|
|
|
|
m_pTiles[fy * m_Width + fx].m_Index = pSpeedupLayer->m_pTiles[y * pSpeedupLayer->m_Width + x].m_Index;
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-09-10 22:42:42 +00:00
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_Force = 0;
|
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_MaxSpeed = 0;
|
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_Angle = 0;
|
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_Type = 0;
|
|
|
|
m_pTiles[fy * m_Width + fx].m_Index = 0;
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-09-10 22:42:42 +00:00
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_Force = 0;
|
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_MaxSpeed = 0;
|
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_Angle = 0;
|
|
|
|
m_pSpeedupTile[fy * m_Width + fx].m_Type = 0;
|
|
|
|
m_pTiles[fy * m_Width + fx].m_Index = 0;
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
}
|
2022-06-30 22:36:32 +00:00
|
|
|
FlagModified(sx, sy, pSpeedupLayer->m_Width, pSpeedupLayer->m_Height);
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
|
2011-04-17 15:59:02 +00:00
|
|
|
void CLayerSpeedup::BrushFlipX()
|
|
|
|
{
|
|
|
|
CLayerTiles::BrushFlipX();
|
2022-06-05 20:21:24 +00:00
|
|
|
BrushFlipXImpl(m_pSpeedupTile);
|
2011-04-17 15:59:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CLayerSpeedup::BrushFlipY()
|
|
|
|
{
|
|
|
|
CLayerTiles::BrushFlipY();
|
2022-06-05 20:21:24 +00:00
|
|
|
BrushFlipYImpl(m_pSpeedupTile);
|
2011-04-17 15:59:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CLayerSpeedup::BrushRotate(float Amount)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int Rotation = (round_to_int(360.0f * Amount / (pi * 2)) / 90) % 4; // 0=0°, 1=90°, 2=180°, 3=270°
|
2011-04-17 15:59:02 +00:00
|
|
|
if(Rotation < 0)
|
2020-09-26 19:41:58 +00:00
|
|
|
Rotation += 4;
|
2011-04-17 15:59:02 +00:00
|
|
|
|
|
|
|
if(Rotation == 1 || Rotation == 3)
|
|
|
|
{
|
2014-03-19 09:26:38 +00:00
|
|
|
// 90° rotation
|
2020-09-26 19:41:58 +00:00
|
|
|
CSpeedupTile *pTempData1 = new CSpeedupTile[m_Width * m_Height];
|
|
|
|
CTile *pTempData2 = new CTile[m_Width * m_Height];
|
2020-10-05 17:03:14 +00:00
|
|
|
mem_copy(pTempData1, m_pSpeedupTile, (size_t)m_Width * m_Height * sizeof(CSpeedupTile));
|
|
|
|
mem_copy(pTempData2, m_pTiles, (size_t)m_Width * m_Height * sizeof(CTile));
|
2011-04-17 15:59:02 +00:00
|
|
|
CSpeedupTile *pDst1 = m_pSpeedupTile;
|
|
|
|
CTile *pDst2 = m_pTiles;
|
|
|
|
for(int x = 0; x < m_Width; ++x)
|
2020-09-26 19:41:58 +00:00
|
|
|
for(int y = m_Height - 1; y >= 0; --y, ++pDst1, ++pDst2)
|
2011-04-17 15:59:02 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
*pDst1 = pTempData1[y * m_Width + x];
|
|
|
|
*pDst2 = pTempData2[y * m_Width + x];
|
2011-04-17 15:59:02 +00:00
|
|
|
}
|
|
|
|
|
2022-05-15 18:44:05 +00:00
|
|
|
std::swap(m_Width, m_Height);
|
2011-04-17 15:59:02 +00:00
|
|
|
delete[] pTempData1;
|
|
|
|
delete[] pTempData2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(Rotation == 2 || Rotation == 3)
|
|
|
|
{
|
|
|
|
BrushFlipX();
|
|
|
|
BrushFlipY();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
void CLayerSpeedup::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect)
|
|
|
|
{
|
2018-10-30 17:56:46 +00:00
|
|
|
if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES))
|
2018-09-26 12:53:25 +00:00
|
|
|
return;
|
2015-07-09 00:08:14 +00:00
|
|
|
|
2014-03-12 22:34:30 +00:00
|
|
|
Snap(&Rect); // corrects Rect; no need of <=
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2014-03-19 09:26:38 +00:00
|
|
|
Snap(&Rect);
|
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
int sx = ConvertX(Rect.x);
|
|
|
|
int sy = ConvertY(Rect.y);
|
|
|
|
int w = ConvertX(Rect.w);
|
|
|
|
int h = ConvertY(Rect.h);
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
CLayerSpeedup *pLt = static_cast<CLayerSpeedup *>(pBrush);
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2019-04-30 12:24:13 +00:00
|
|
|
bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLt);
|
2019-03-26 18:15:24 +00:00
|
|
|
|
2014-03-19 09:26:38 +00:00
|
|
|
for(int y = 0; y < h; y++)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2014-03-19 09:26:38 +00:00
|
|
|
for(int x = 0; x < w; x++)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int fx = x + sx;
|
|
|
|
int fy = y + sy;
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height)
|
|
|
|
continue;
|
2011-04-09 06:41:31 +00:00
|
|
|
|
2019-03-26 18:15:24 +00:00
|
|
|
if(!Destructive && GetTile(fx, fy).m_Index)
|
|
|
|
continue;
|
|
|
|
|
2023-07-07 17:07:33 +00:00
|
|
|
const int SrcIndex = Empty ? 0 : (y * pLt->m_Width + x % pLt->m_Width) % (pLt->m_Width * pLt->m_Height);
|
2023-05-20 20:16:23 +00:00
|
|
|
const int TgtIndex = fy * m_Width + fx;
|
|
|
|
|
|
|
|
if(Empty || (!m_pEditor->m_AllowPlaceUnusedTiles && !IsValidSpeedupTile((pLt->m_pTiles[SrcIndex]).m_Index))) // no speed up tile chosen: reset
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pTiles[TgtIndex].m_Index = 0;
|
|
|
|
m_pSpeedupTile[TgtIndex].m_Force = 0;
|
|
|
|
m_pSpeedupTile[TgtIndex].m_Angle = 0;
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
2011-02-13 05:35:13 +00:00
|
|
|
else
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pTiles[TgtIndex] = pLt->m_pTiles[SrcIndex];
|
|
|
|
if(pLt->m_Speedup && m_pTiles[TgtIndex].m_Index > 0)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pSpeedupTile[TgtIndex].m_Type = m_pTiles[TgtIndex].m_Index;
|
|
|
|
|
|
|
|
if((pLt->m_pSpeedupTile[SrcIndex].m_Force == 0 && m_pEditor->m_SpeedupForce) || m_pEditor->m_SpeedupForce != pLt->m_SpeedupForce)
|
|
|
|
m_pSpeedupTile[TgtIndex].m_Force = m_pEditor->m_SpeedupForce;
|
2010-12-07 15:51:59 +00:00
|
|
|
else
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pSpeedupTile[TgtIndex].m_Force = pLt->m_pSpeedupTile[SrcIndex].m_Force;
|
|
|
|
|
|
|
|
if((pLt->m_pSpeedupTile[SrcIndex].m_Angle == 0 && m_pEditor->m_SpeedupAngle) || m_pEditor->m_SpeedupAngle != pLt->m_SpeedupAngle)
|
|
|
|
m_pSpeedupTile[TgtIndex].m_Angle = m_pEditor->m_SpeedupAngle;
|
2010-12-07 15:51:59 +00:00
|
|
|
else
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pSpeedupTile[TgtIndex].m_Angle = pLt->m_pSpeedupTile[SrcIndex].m_Angle;
|
|
|
|
|
|
|
|
if((pLt->m_pSpeedupTile[SrcIndex].m_MaxSpeed == 0 && m_pEditor->m_SpeedupMaxSpeed) || m_pEditor->m_SpeedupMaxSpeed != pLt->m_SpeedupMaxSpeed)
|
|
|
|
m_pSpeedupTile[TgtIndex].m_MaxSpeed = m_pEditor->m_SpeedupMaxSpeed;
|
2010-12-07 15:51:59 +00:00
|
|
|
else
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pSpeedupTile[TgtIndex].m_MaxSpeed = pLt->m_pSpeedupTile[SrcIndex].m_MaxSpeed;
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-04 13:26:41 +00:00
|
|
|
FlagModified(sx, sy, w, h);
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
CLayerFront::CLayerFront(int w, int h) :
|
|
|
|
CLayerTiles(w, h)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2023-06-13 15:44:30 +00:00
|
|
|
str_copy(m_aName, "Front");
|
2010-09-30 21:31:19 +00:00
|
|
|
m_Front = 1;
|
|
|
|
}
|
|
|
|
|
2023-07-10 19:27:51 +00:00
|
|
|
void CLayerFront::SetTile(int x, int y, CTile Tile)
|
2015-11-14 23:00:43 +00:00
|
|
|
{
|
2023-07-10 19:27:51 +00:00
|
|
|
if(Tile.m_Index == TILE_THROUGH_CUT)
|
2020-09-26 19:41:58 +00:00
|
|
|
{
|
2016-04-30 23:47:29 +00:00
|
|
|
CTile nohook = {TILE_NOHOOK};
|
2020-11-04 18:27:22 +00:00
|
|
|
m_pEditor->m_Map.m_pGameLayer->CLayerTiles::SetTile(x, y, nohook); // NOLINT(bugprone-parent-virtual-call)
|
2020-09-26 19:41:58 +00:00
|
|
|
}
|
2023-07-10 19:27:51 +00:00
|
|
|
else if(Tile.m_Index == TILE_AIR && CLayerTiles::GetTile(x, y).m_Index == TILE_THROUGH_CUT)
|
2020-09-26 19:41:58 +00:00
|
|
|
{
|
2016-04-30 23:47:29 +00:00
|
|
|
CTile air = {TILE_AIR};
|
2020-11-04 18:27:22 +00:00
|
|
|
m_pEditor->m_Map.m_pGameLayer->CLayerTiles::SetTile(x, y, air); // NOLINT(bugprone-parent-virtual-call)
|
2016-04-30 23:47:29 +00:00
|
|
|
}
|
2023-07-10 19:27:51 +00:00
|
|
|
if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidFrontTile(Tile.m_Index))
|
2020-09-26 19:41:58 +00:00
|
|
|
{
|
2023-07-10 19:27:51 +00:00
|
|
|
CLayerTiles::SetTile(x, y, Tile);
|
2020-09-26 19:41:58 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-04-30 23:47:29 +00:00
|
|
|
CTile air = {TILE_AIR};
|
|
|
|
CLayerTiles::SetTile(x, y, air);
|
2020-09-26 19:41:58 +00:00
|
|
|
if(!m_pEditor->m_PreventUnusedTilesWasWarned)
|
|
|
|
{
|
2023-04-01 11:55:50 +00:00
|
|
|
m_pEditor->m_PopupEventType = CEditor::POPEVENT_PREVENTUNUSEDTILES;
|
2016-05-01 16:08:07 +00:00
|
|
|
m_pEditor->m_PopupEventActivated = true;
|
|
|
|
m_pEditor->m_PreventUnusedTilesWasWarned = true;
|
|
|
|
}
|
2016-04-29 14:53:19 +00:00
|
|
|
}
|
2015-11-14 23:00:43 +00:00
|
|
|
}
|
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
void CLayerFront::Resize(int NewW, int NewH)
|
|
|
|
{
|
|
|
|
// resize tile data
|
|
|
|
CLayerTiles::Resize(NewW, NewH);
|
|
|
|
|
|
|
|
// resize gamelayer too
|
|
|
|
if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH)
|
|
|
|
m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH);
|
|
|
|
}
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
CLayerSwitch::CLayerSwitch(int w, int h) :
|
|
|
|
CLayerTiles(w, h)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2023-06-13 15:44:30 +00:00
|
|
|
str_copy(m_aName, "Switch");
|
2010-09-30 21:31:19 +00:00
|
|
|
m_Switch = 1;
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pSwitchTile = new CSwitchTile[w * h];
|
2020-10-05 17:03:14 +00:00
|
|
|
mem_zero(m_pSwitchTile, (size_t)w * h * sizeof(CSwitchTile));
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CLayerSwitch::~CLayerSwitch()
|
|
|
|
{
|
|
|
|
delete[] m_pSwitchTile;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CLayerSwitch::Resize(int NewW, int NewH)
|
|
|
|
{
|
|
|
|
// resize switch data
|
2020-09-26 19:41:58 +00:00
|
|
|
CSwitchTile *pNewSwitchData = new CSwitchTile[NewW * NewH];
|
2020-10-05 17:03:14 +00:00
|
|
|
mem_zero(pNewSwitchData, (size_t)NewW * NewH * sizeof(CSwitchTile));
|
2010-09-30 21:31:19 +00:00
|
|
|
|
|
|
|
// copy old data
|
2019-04-26 19:36:49 +00:00
|
|
|
for(int y = 0; y < minimum(NewH, m_Height); y++)
|
2020-09-26 19:41:58 +00:00
|
|
|
mem_copy(&pNewSwitchData[y * NewW], &m_pSwitchTile[y * m_Width], minimum(m_Width, NewW) * sizeof(CSwitchTile));
|
2010-09-30 21:31:19 +00:00
|
|
|
|
|
|
|
// replace old
|
2020-09-26 19:41:58 +00:00
|
|
|
delete[] m_pSwitchTile;
|
2010-09-30 21:31:19 +00:00
|
|
|
m_pSwitchTile = pNewSwitchData;
|
|
|
|
|
|
|
|
// resize tile data
|
|
|
|
CLayerTiles::Resize(NewW, NewH);
|
|
|
|
|
|
|
|
// resize gamelayer too
|
|
|
|
if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH)
|
|
|
|
m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH);
|
|
|
|
}
|
|
|
|
|
2010-10-22 15:02:11 +00:00
|
|
|
void CLayerSwitch::Shift(int Direction)
|
|
|
|
{
|
|
|
|
CLayerTiles::Shift(Direction);
|
2022-06-05 20:03:16 +00:00
|
|
|
ShiftImpl(m_pSwitchTile, Direction, m_pEditor->m_ShiftBy);
|
2010-10-22 15:02:11 +00:00
|
|
|
}
|
|
|
|
|
2019-03-26 18:15:24 +00:00
|
|
|
bool CLayerSwitch::IsEmpty(CLayerTiles *pLayer)
|
|
|
|
{
|
|
|
|
for(int y = 0; y < pLayer->m_Height; y++)
|
|
|
|
for(int x = 0; x < pLayer->m_Width; x++)
|
2021-09-01 09:43:32 +00:00
|
|
|
if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidSwitchTile(pLayer->GetTile(x, y).m_Index))
|
2019-03-26 18:15:24 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
void CLayerSwitch::BrushDraw(CLayer *pBrush, float wx, float wy)
|
|
|
|
{
|
2011-08-26 16:46:27 +00:00
|
|
|
if(m_Readonly)
|
|
|
|
return;
|
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
CLayerSwitch *pSwitchLayer = (CLayerSwitch *)pBrush;
|
2010-09-30 21:31:19 +00:00
|
|
|
int sx = ConvertX(wx);
|
|
|
|
int sy = ConvertY(wy);
|
2022-06-30 22:36:32 +00:00
|
|
|
if(str_comp(pSwitchLayer->m_aFileName, m_pEditor->m_aFileName))
|
2010-12-19 14:10:42 +00:00
|
|
|
{
|
2022-06-30 22:36:32 +00:00
|
|
|
m_pEditor->m_SwitchNum = pSwitchLayer->m_SwitchNumber;
|
|
|
|
m_pEditor->m_SwitchDelay = pSwitchLayer->m_SwitchDelay;
|
2010-12-19 14:10:42 +00:00
|
|
|
}
|
2010-09-30 21:31:19 +00:00
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(pSwitchLayer);
|
2019-03-26 18:15:24 +00:00
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
for(int y = 0; y < pSwitchLayer->m_Height; y++)
|
|
|
|
for(int x = 0; x < pSwitchLayer->m_Width; x++)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int fx = x + sx;
|
|
|
|
int fy = y + sy;
|
2019-03-26 18:15:24 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height)
|
2010-09-30 21:31:19 +00:00
|
|
|
continue;
|
|
|
|
|
2019-03-26 18:15:24 +00:00
|
|
|
if(!Destructive && GetTile(fx, fy).m_Index)
|
|
|
|
continue;
|
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
if((m_pEditor->m_AllowPlaceUnusedTiles || IsValidSwitchTile(pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index)) && pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index != TILE_AIR)
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2022-06-30 22:36:32 +00:00
|
|
|
if(m_pEditor->m_SwitchNum != pSwitchLayer->m_SwitchNumber || m_pEditor->m_SwitchDelay != pSwitchLayer->m_SwitchDelay)
|
2010-12-04 11:54:53 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pSwitchTile[fy * m_Width + fx].m_Number = m_pEditor->m_SwitchNum;
|
|
|
|
m_pSwitchTile[fy * m_Width + fx].m_Delay = m_pEditor->m_SwitchDelay;
|
2010-12-04 11:54:53 +00:00
|
|
|
}
|
2022-06-30 22:36:32 +00:00
|
|
|
else if(pSwitchLayer->m_pSwitchTile[y * pSwitchLayer->m_Width + x].m_Number)
|
2010-12-04 11:54:53 +00:00
|
|
|
{
|
2022-06-30 22:36:32 +00:00
|
|
|
m_pSwitchTile[fy * m_Width + fx].m_Number = pSwitchLayer->m_pSwitchTile[y * pSwitchLayer->m_Width + x].m_Number;
|
|
|
|
m_pSwitchTile[fy * m_Width + fx].m_Delay = pSwitchLayer->m_pSwitchTile[y * pSwitchLayer->m_Width + x].m_Delay;
|
2010-12-04 11:54:53 +00:00
|
|
|
}
|
|
|
|
else
|
2010-09-30 21:31:19 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pSwitchTile[fy * m_Width + fx].m_Number = m_pEditor->m_SwitchNum;
|
|
|
|
m_pSwitchTile[fy * m_Width + fx].m_Delay = m_pEditor->m_SwitchDelay;
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
m_pSwitchTile[fy * m_Width + fx].m_Type = pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index;
|
|
|
|
m_pSwitchTile[fy * m_Width + fx].m_Flags = pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Flags;
|
|
|
|
m_pTiles[fy * m_Width + fx].m_Index = pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index;
|
|
|
|
m_pTiles[fy * m_Width + fx].m_Flags = pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Flags;
|
2023-03-04 11:22:18 +00:00
|
|
|
|
|
|
|
if(!IsSwitchTileFlagsUsed(pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index))
|
|
|
|
{
|
|
|
|
m_pSwitchTile[fy * m_Width + fx].m_Flags = 0;
|
|
|
|
}
|
|
|
|
if(!IsSwitchTileNumberUsed(pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index))
|
|
|
|
{
|
|
|
|
m_pSwitchTile[fy * m_Width + fx].m_Number = 0;
|
|
|
|
}
|
|
|
|
if(!IsSwitchTileDelayUsed(pSwitchLayer->m_pTiles[y * pSwitchLayer->m_Width + x].m_Index))
|
|
|
|
{
|
|
|
|
m_pSwitchTile[fy * m_Width + fx].m_Delay = 0;
|
|
|
|
}
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pSwitchTile[fy * m_Width + fx].m_Number = 0;
|
|
|
|
m_pSwitchTile[fy * m_Width + fx].m_Type = 0;
|
|
|
|
m_pSwitchTile[fy * m_Width + fx].m_Flags = 0;
|
|
|
|
m_pSwitchTile[fy * m_Width + fx].m_Delay = 0;
|
|
|
|
m_pTiles[fy * m_Width + fx].m_Index = 0;
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
}
|
2022-06-30 22:36:32 +00:00
|
|
|
FlagModified(sx, sy, pSwitchLayer->m_Width, pSwitchLayer->m_Height);
|
2010-09-30 21:31:19 +00:00
|
|
|
}
|
|
|
|
|
2019-03-21 21:50:22 +00:00
|
|
|
void CLayerSwitch::BrushFlipX()
|
|
|
|
{
|
|
|
|
CLayerTiles::BrushFlipX();
|
2022-06-05 20:21:24 +00:00
|
|
|
BrushFlipXImpl(m_pSwitchTile);
|
2019-03-21 21:50:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CLayerSwitch::BrushFlipY()
|
|
|
|
{
|
|
|
|
CLayerTiles::BrushFlipY();
|
2022-06-05 20:21:24 +00:00
|
|
|
BrushFlipYImpl(m_pSwitchTile);
|
2019-03-21 21:50:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CLayerSwitch::BrushRotate(float Amount)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int Rotation = (round_to_int(360.0f * Amount / (pi * 2)) / 90) % 4; // 0=0°, 1=90°, 2=180°, 3=270°
|
2019-03-21 21:50:22 +00:00
|
|
|
if(Rotation < 0)
|
2020-09-26 19:41:58 +00:00
|
|
|
Rotation += 4;
|
2019-03-21 21:50:22 +00:00
|
|
|
|
|
|
|
if(Rotation == 1 || Rotation == 3)
|
|
|
|
{
|
|
|
|
// 90° rotation
|
2020-09-26 19:41:58 +00:00
|
|
|
CSwitchTile *pTempData1 = new CSwitchTile[m_Width * m_Height];
|
|
|
|
CTile *pTempData2 = new CTile[m_Width * m_Height];
|
2020-10-05 17:03:14 +00:00
|
|
|
mem_copy(pTempData1, m_pSwitchTile, (size_t)m_Width * m_Height * sizeof(CSwitchTile));
|
|
|
|
mem_copy(pTempData2, m_pTiles, (size_t)m_Width * m_Height * sizeof(CTile));
|
2019-03-21 21:50:22 +00:00
|
|
|
CSwitchTile *pDst1 = m_pSwitchTile;
|
|
|
|
CTile *pDst2 = m_pTiles;
|
|
|
|
for(int x = 0; x < m_Width; ++x)
|
2020-09-26 19:41:58 +00:00
|
|
|
for(int y = m_Height - 1; y >= 0; --y, ++pDst1, ++pDst2)
|
2019-03-21 21:50:22 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
*pDst1 = pTempData1[y * m_Width + x];
|
|
|
|
*pDst2 = pTempData2[y * m_Width + x];
|
2020-01-17 17:22:41 +00:00
|
|
|
if(IsRotatableTile(pDst2->m_Index))
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
if(pDst2->m_Flags & TILEFLAG_ROTATE)
|
2022-12-19 17:28:08 +00:00
|
|
|
pDst2->m_Flags ^= (TILEFLAG_YFLIP | TILEFLAG_XFLIP);
|
2020-01-17 17:22:41 +00:00
|
|
|
pDst2->m_Flags ^= TILEFLAG_ROTATE;
|
|
|
|
}
|
2019-03-21 21:50:22 +00:00
|
|
|
}
|
|
|
|
|
2022-05-15 18:44:05 +00:00
|
|
|
std::swap(m_Width, m_Height);
|
2019-03-21 21:50:22 +00:00
|
|
|
delete[] pTempData1;
|
|
|
|
delete[] pTempData2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(Rotation == 2 || Rotation == 3)
|
|
|
|
{
|
|
|
|
BrushFlipX();
|
|
|
|
BrushFlipY();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-30 21:31:19 +00:00
|
|
|
void CLayerSwitch::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect)
|
2010-11-23 09:26:54 +00:00
|
|
|
{
|
2018-10-30 17:56:46 +00:00
|
|
|
if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES))
|
2018-09-26 12:53:25 +00:00
|
|
|
return;
|
2015-07-09 00:08:14 +00:00
|
|
|
|
2014-03-12 22:34:30 +00:00
|
|
|
Snap(&Rect); // corrects Rect; no need of <=
|
2010-11-23 09:26:54 +00:00
|
|
|
|
2014-03-19 09:26:38 +00:00
|
|
|
Snap(&Rect);
|
|
|
|
|
2010-11-23 09:26:54 +00:00
|
|
|
int sx = ConvertX(Rect.x);
|
|
|
|
int sy = ConvertY(Rect.y);
|
|
|
|
int w = ConvertX(Rect.w);
|
|
|
|
int h = ConvertY(Rect.h);
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
CLayerSwitch *pLt = static_cast<CLayerSwitch *>(pBrush);
|
2010-11-23 09:26:54 +00:00
|
|
|
|
2019-04-30 12:24:13 +00:00
|
|
|
bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLt);
|
2019-03-26 18:15:24 +00:00
|
|
|
|
2014-03-19 09:26:38 +00:00
|
|
|
for(int y = 0; y < h; y++)
|
2010-11-23 09:26:54 +00:00
|
|
|
{
|
2014-03-19 09:26:38 +00:00
|
|
|
for(int x = 0; x < w; x++)
|
2010-11-23 09:26:54 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int fx = x + sx;
|
|
|
|
int fy = y + sy;
|
2010-11-23 09:26:54 +00:00
|
|
|
|
|
|
|
if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height)
|
|
|
|
continue;
|
|
|
|
|
2019-03-26 18:15:24 +00:00
|
|
|
if(!Destructive && GetTile(fx, fy).m_Index)
|
|
|
|
continue;
|
|
|
|
|
2023-07-07 17:07:33 +00:00
|
|
|
const int SrcIndex = Empty ? 0 : (y * pLt->m_Width + x % pLt->m_Width) % (pLt->m_Width * pLt->m_Height);
|
2023-05-20 20:16:23 +00:00
|
|
|
const int TgtIndex = fy * m_Width + fx;
|
|
|
|
|
|
|
|
if(Empty || (!m_pEditor->m_AllowPlaceUnusedTiles && !IsValidSwitchTile((pLt->m_pTiles[SrcIndex]).m_Index)))
|
2010-11-23 09:26:54 +00:00
|
|
|
{
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pTiles[TgtIndex].m_Index = 0;
|
|
|
|
m_pSwitchTile[TgtIndex].m_Type = 0;
|
|
|
|
m_pSwitchTile[TgtIndex].m_Number = 0;
|
|
|
|
m_pSwitchTile[TgtIndex].m_Delay = 0;
|
2010-11-23 09:26:54 +00:00
|
|
|
}
|
2018-09-26 12:53:25 +00:00
|
|
|
else
|
2010-11-23 09:26:54 +00:00
|
|
|
{
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pTiles[TgtIndex] = pLt->m_pTiles[SrcIndex];
|
|
|
|
m_pSwitchTile[TgtIndex].m_Type = m_pTiles[TgtIndex].m_Index;
|
2023-05-21 09:39:56 +00:00
|
|
|
if(pLt->m_Switch && m_pTiles[TgtIndex].m_Index > 0)
|
2011-02-13 05:35:13 +00:00
|
|
|
{
|
2023-05-21 09:59:08 +00:00
|
|
|
if(!IsSwitchTileNumberUsed(m_pSwitchTile[TgtIndex].m_Type))
|
|
|
|
m_pSwitchTile[TgtIndex].m_Number = 0;
|
|
|
|
else if(pLt->m_pSwitchTile[SrcIndex].m_Number == 0 || m_pEditor->m_SwitchNum != pLt->m_SwitchNumber)
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pSwitchTile[TgtIndex].m_Number = m_pEditor->m_SwitchNum;
|
2010-12-07 15:51:59 +00:00
|
|
|
else
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pSwitchTile[TgtIndex].m_Number = pLt->m_pSwitchTile[SrcIndex].m_Number;
|
|
|
|
|
2023-05-21 09:59:08 +00:00
|
|
|
if(!IsSwitchTileDelayUsed(m_pSwitchTile[TgtIndex].m_Type))
|
|
|
|
m_pSwitchTile[TgtIndex].m_Delay = 0;
|
|
|
|
else if(pLt->m_pSwitchTile[SrcIndex].m_Delay == 0 || m_pEditor->m_SwitchDelay != pLt->m_SwitchDelay)
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pSwitchTile[TgtIndex].m_Delay = m_pEditor->m_SwitchDelay;
|
2010-12-07 15:51:59 +00:00
|
|
|
else
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pSwitchTile[TgtIndex].m_Delay = pLt->m_pSwitchTile[SrcIndex].m_Delay;
|
|
|
|
|
2023-05-21 09:59:08 +00:00
|
|
|
if(!IsSwitchTileFlagsUsed(m_pSwitchTile[TgtIndex].m_Type))
|
|
|
|
m_pSwitchTile[TgtIndex].m_Flags = 0;
|
|
|
|
else
|
|
|
|
m_pSwitchTile[TgtIndex].m_Flags = pLt->m_pSwitchTile[SrcIndex].m_Flags;
|
2011-02-13 05:35:13 +00:00
|
|
|
}
|
2010-11-23 09:26:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-04 13:26:41 +00:00
|
|
|
FlagModified(sx, sy, w, h);
|
2010-11-23 09:26:54 +00:00
|
|
|
}
|
2014-03-12 22:34:30 +00:00
|
|
|
|
2020-05-17 00:56:35 +00:00
|
|
|
bool CLayerSwitch::ContainsElementWithId(int Id)
|
|
|
|
{
|
|
|
|
for(int y = 0; y < m_Height; ++y)
|
|
|
|
{
|
|
|
|
for(int x = 0; x < m_Width; ++x)
|
|
|
|
{
|
2023-03-04 11:18:04 +00:00
|
|
|
if(IsSwitchTileNumberUsed(m_pSwitchTile[y * m_Width + x].m_Type) && m_pSwitchTile[y * m_Width + x].m_Number == Id)
|
2020-05-17 00:56:35 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-03-12 22:34:30 +00:00
|
|
|
//------------------------------------------------------
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
CLayerTune::CLayerTune(int w, int h) :
|
|
|
|
CLayerTiles(w, h)
|
2014-03-12 22:34:30 +00:00
|
|
|
{
|
2023-06-13 15:44:30 +00:00
|
|
|
str_copy(m_aName, "Tune");
|
2014-03-12 22:34:30 +00:00
|
|
|
m_Tune = 1;
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pTuneTile = new CTuneTile[w * h];
|
2020-10-05 17:03:14 +00:00
|
|
|
mem_zero(m_pTuneTile, (size_t)w * h * sizeof(CTuneTile));
|
2014-03-12 22:34:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CLayerTune::~CLayerTune()
|
|
|
|
{
|
|
|
|
delete[] m_pTuneTile;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CLayerTune::Resize(int NewW, int NewH)
|
|
|
|
{
|
|
|
|
// resize Tune data
|
2020-09-26 19:41:58 +00:00
|
|
|
CTuneTile *pNewTuneData = new CTuneTile[NewW * NewH];
|
2020-10-05 17:03:14 +00:00
|
|
|
mem_zero(pNewTuneData, (size_t)NewW * NewH * sizeof(CTuneTile));
|
2014-03-12 22:34:30 +00:00
|
|
|
|
|
|
|
// copy old data
|
2019-04-26 19:36:49 +00:00
|
|
|
for(int y = 0; y < minimum(NewH, m_Height); y++)
|
2020-09-26 19:41:58 +00:00
|
|
|
mem_copy(&pNewTuneData[y * NewW], &m_pTuneTile[y * m_Width], minimum(m_Width, NewW) * sizeof(CTuneTile));
|
2014-03-12 22:34:30 +00:00
|
|
|
|
|
|
|
// replace old
|
2020-09-26 19:41:58 +00:00
|
|
|
delete[] m_pTuneTile;
|
2014-03-12 22:34:30 +00:00
|
|
|
m_pTuneTile = pNewTuneData;
|
|
|
|
|
|
|
|
// resize tile data
|
|
|
|
CLayerTiles::Resize(NewW, NewH);
|
|
|
|
|
|
|
|
// resize gamelayer too
|
|
|
|
if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH)
|
|
|
|
m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CLayerTune::Shift(int Direction)
|
|
|
|
{
|
|
|
|
CLayerTiles::Shift(Direction);
|
2022-06-05 20:03:16 +00:00
|
|
|
ShiftImpl(m_pTuneTile, Direction, m_pEditor->m_ShiftBy);
|
2014-03-12 22:34:30 +00:00
|
|
|
}
|
|
|
|
|
2019-03-26 18:15:24 +00:00
|
|
|
bool CLayerTune::IsEmpty(CLayerTiles *pLayer)
|
|
|
|
{
|
|
|
|
for(int y = 0; y < pLayer->m_Height; y++)
|
|
|
|
for(int x = 0; x < pLayer->m_Width; x++)
|
2021-09-01 09:43:32 +00:00
|
|
|
if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidTuneTile(pLayer->GetTile(x, y).m_Index))
|
2019-03-26 18:15:24 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-03-12 22:34:30 +00:00
|
|
|
void CLayerTune::BrushDraw(CLayer *pBrush, float wx, float wy)
|
|
|
|
{
|
|
|
|
if(m_Readonly)
|
|
|
|
return;
|
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
CLayerTune *pTuneLayer = (CLayerTune *)pBrush;
|
2014-03-12 22:34:30 +00:00
|
|
|
int sx = ConvertX(wx);
|
|
|
|
int sy = ConvertY(wy);
|
2022-06-30 22:36:32 +00:00
|
|
|
if(str_comp(pTuneLayer->m_aFileName, m_pEditor->m_aFileName))
|
2014-03-12 22:34:30 +00:00
|
|
|
{
|
2022-06-30 22:36:32 +00:00
|
|
|
m_pEditor->m_TuningNum = pTuneLayer->m_TuningNumber;
|
2014-03-12 22:34:30 +00:00
|
|
|
}
|
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(pTuneLayer);
|
2019-03-26 18:15:24 +00:00
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
for(int y = 0; y < pTuneLayer->m_Height; y++)
|
|
|
|
for(int x = 0; x < pTuneLayer->m_Width; x++)
|
2020-09-26 19:41:58 +00:00
|
|
|
{
|
|
|
|
int fx = x + sx;
|
|
|
|
int fy = y + sy;
|
2019-03-26 18:15:24 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height)
|
|
|
|
continue;
|
2014-03-12 22:34:30 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
if(!Destructive && GetTile(fx, fy).m_Index)
|
|
|
|
continue;
|
2019-03-26 18:15:24 +00:00
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
if((m_pEditor->m_AllowPlaceUnusedTiles || IsValidTuneTile(pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index)) && pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index != TILE_AIR)
|
2020-09-26 19:41:58 +00:00
|
|
|
{
|
2022-06-30 22:36:32 +00:00
|
|
|
if(m_pEditor->m_TuningNum != pTuneLayer->m_TuningNumber)
|
2014-03-12 22:34:30 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pTuneTile[fy * m_Width + fx].m_Number = m_pEditor->m_TuningNum;
|
2014-03-12 22:34:30 +00:00
|
|
|
}
|
2022-06-30 22:36:32 +00:00
|
|
|
else if(pTuneLayer->m_pTuneTile[y * pTuneLayer->m_Width + x].m_Number)
|
|
|
|
m_pTuneTile[fy * m_Width + fx].m_Number = pTuneLayer->m_pTuneTile[y * pTuneLayer->m_Width + x].m_Number;
|
2014-03-12 22:34:30 +00:00
|
|
|
else
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
if(!m_pEditor->m_TuningNum)
|
|
|
|
{
|
|
|
|
m_pTuneTile[fy * m_Width + fx].m_Number = 0;
|
|
|
|
m_pTuneTile[fy * m_Width + fx].m_Type = 0;
|
|
|
|
m_pTiles[fy * m_Width + fx].m_Index = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_pTuneTile[fy * m_Width + fx].m_Number = m_pEditor->m_TuningNum;
|
2014-03-12 22:34:30 +00:00
|
|
|
}
|
2020-09-26 19:41:58 +00:00
|
|
|
|
2022-06-30 22:36:32 +00:00
|
|
|
m_pTuneTile[fy * m_Width + fx].m_Type = pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index;
|
|
|
|
m_pTiles[fy * m_Width + fx].m_Index = pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index;
|
2014-03-12 22:34:30 +00:00
|
|
|
}
|
2020-09-26 19:41:58 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
m_pTuneTile[fy * m_Width + fx].m_Number = 0;
|
|
|
|
m_pTuneTile[fy * m_Width + fx].m_Type = 0;
|
|
|
|
m_pTiles[fy * m_Width + fx].m_Index = 0;
|
|
|
|
}
|
|
|
|
}
|
2022-06-30 22:36:32 +00:00
|
|
|
FlagModified(sx, sy, pTuneLayer->m_Width, pTuneLayer->m_Height);
|
2014-03-12 22:34:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CLayerTune::BrushFlipX()
|
|
|
|
{
|
|
|
|
CLayerTiles::BrushFlipX();
|
2022-06-05 20:21:24 +00:00
|
|
|
BrushFlipXImpl(m_pTuneTile);
|
2014-03-12 22:34:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CLayerTune::BrushFlipY()
|
|
|
|
{
|
|
|
|
CLayerTiles::BrushFlipY();
|
2022-06-05 20:21:24 +00:00
|
|
|
BrushFlipYImpl(m_pTuneTile);
|
2014-03-12 22:34:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CLayerTune::BrushRotate(float Amount)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int Rotation = (round_to_int(360.0f * Amount / (pi * 2)) / 90) % 4; // 0=0°, 1=90°, 2=180°, 3=270°
|
2014-03-12 22:34:30 +00:00
|
|
|
if(Rotation < 0)
|
2020-09-26 19:41:58 +00:00
|
|
|
Rotation += 4;
|
2014-03-12 22:34:30 +00:00
|
|
|
|
|
|
|
if(Rotation == 1 || Rotation == 3)
|
|
|
|
{
|
2014-03-23 16:42:00 +00:00
|
|
|
// 90° rotation
|
2020-09-26 19:41:58 +00:00
|
|
|
CTuneTile *pTempData1 = new CTuneTile[m_Width * m_Height];
|
|
|
|
CTile *pTempData2 = new CTile[m_Width * m_Height];
|
2020-10-05 17:03:14 +00:00
|
|
|
mem_copy(pTempData1, m_pTuneTile, (size_t)m_Width * m_Height * sizeof(CTuneTile));
|
|
|
|
mem_copy(pTempData2, m_pTiles, (size_t)m_Width * m_Height * sizeof(CTile));
|
2014-03-12 22:34:30 +00:00
|
|
|
CTuneTile *pDst1 = m_pTuneTile;
|
|
|
|
CTile *pDst2 = m_pTiles;
|
|
|
|
for(int x = 0; x < m_Width; ++x)
|
2020-09-26 19:41:58 +00:00
|
|
|
for(int y = m_Height - 1; y >= 0; --y, ++pDst1, ++pDst2)
|
2014-03-12 22:34:30 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
*pDst1 = pTempData1[y * m_Width + x];
|
|
|
|
*pDst2 = pTempData2[y * m_Width + x];
|
2014-03-12 22:34:30 +00:00
|
|
|
}
|
|
|
|
|
2022-05-15 18:44:05 +00:00
|
|
|
std::swap(m_Width, m_Height);
|
2014-03-12 22:34:30 +00:00
|
|
|
delete[] pTempData1;
|
|
|
|
delete[] pTempData2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(Rotation == 2 || Rotation == 3)
|
|
|
|
{
|
|
|
|
BrushFlipX();
|
|
|
|
BrushFlipY();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CLayerTune::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect)
|
|
|
|
{
|
2018-10-30 17:56:46 +00:00
|
|
|
if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES))
|
2018-09-26 12:53:25 +00:00
|
|
|
return;
|
2015-07-09 00:08:14 +00:00
|
|
|
|
2014-03-12 22:34:30 +00:00
|
|
|
Snap(&Rect); // corrects Rect; no need of <=
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
int sx = ConvertX(Rect.x);
|
|
|
|
int sy = ConvertY(Rect.y);
|
|
|
|
int w = ConvertX(Rect.w);
|
|
|
|
int h = ConvertY(Rect.h);
|
2014-03-12 22:34:30 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
CLayerTune *pLt = static_cast<CLayerTune *>(pBrush);
|
2014-03-12 22:34:30 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLt);
|
2019-03-26 18:15:24 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
for(int y = 0; y < h; y++)
|
|
|
|
{
|
|
|
|
for(int x = 0; x < w; x++)
|
2014-03-12 22:34:30 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int fx = x + sx;
|
|
|
|
int fy = y + sy;
|
2014-03-12 22:34:30 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height)
|
|
|
|
continue;
|
2014-03-12 22:34:30 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
if(!Destructive && GetTile(fx, fy).m_Index)
|
|
|
|
continue;
|
2019-03-26 18:15:24 +00:00
|
|
|
|
2023-07-07 17:07:33 +00:00
|
|
|
const int SrcIndex = Empty ? 0 : (y * pLt->m_Width + x % pLt->m_Width) % (pLt->m_Width * pLt->m_Height);
|
2023-05-20 20:16:23 +00:00
|
|
|
const int TgtIndex = fy * m_Width + fx;
|
|
|
|
|
|
|
|
if(Empty || (!m_pEditor->m_AllowPlaceUnusedTiles && !IsValidTuneTile((pLt->m_pTiles[SrcIndex]).m_Index)))
|
2020-09-26 19:41:58 +00:00
|
|
|
{
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pTiles[TgtIndex].m_Index = 0;
|
|
|
|
m_pTuneTile[TgtIndex].m_Type = 0;
|
|
|
|
m_pTuneTile[TgtIndex].m_Number = 0;
|
2020-09-26 19:41:58 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pTiles[TgtIndex] = pLt->m_pTiles[SrcIndex];
|
|
|
|
if(pLt->m_Tune && m_pTiles[TgtIndex].m_Index > 0)
|
2014-03-12 22:34:30 +00:00
|
|
|
{
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pTuneTile[TgtIndex].m_Type = m_pTiles[fy * m_Width + fx].m_Index;
|
|
|
|
|
|
|
|
if((pLt->m_pTuneTile[SrcIndex].m_Number == 0 && m_pEditor->m_TuningNum) || m_pEditor->m_TuningNum != pLt->m_TuningNumber)
|
|
|
|
m_pTuneTile[TgtIndex].m_Number = m_pEditor->m_TuningNum;
|
2020-09-26 19:41:58 +00:00
|
|
|
else
|
2023-05-20 20:16:23 +00:00
|
|
|
m_pTuneTile[TgtIndex].m_Number = pLt->m_pTuneTile[SrcIndex].m_Number;
|
2014-03-12 22:34:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-26 19:41:58 +00:00
|
|
|
}
|
2018-10-04 13:26:41 +00:00
|
|
|
|
|
|
|
FlagModified(sx, sy, w, h);
|
2014-03-23 16:42:00 +00:00
|
|
|
}
|