mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-19 06:28:19 +00:00
Merge #5749
5749: Editor: added the possibility to duplicate layers r=def- a=archimede67 <!-- What is the motivation for the changes of this pull request --> Suggested by Pulsar a few years ago, I finally had the motivation to do it. We can now duplicate any non-special layer (tiles, quads and sounds) thanks to a button just above the delete button. ![image](https://user-images.githubusercontent.com/13364635/185260070-cd5b4c8f-5827-457c-b505-176751003dbc.png) For each duplicated layers, all the necessary properties are duplicated, for example name, color, flags, etc. For a tile layer, all the placed tiles are copied. Same goes for a sound layer, all sources are copied. And for the quads layer, all the quads are duplicated. ## Checklist - [x] Tested the change ingame - [x] Provided screenshots if it is a visual change - [ ] Tested in combination with possibly related configuration options - [ ] Written a unit test (especially base/) or added coverage to integration test - [x] Considered possible null pointers and out of bounds array indexing - [x] Changed no physics that affect existing maps - [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional) Co-authored-by: Corantin H <archi0670@gmail.com>
This commit is contained in:
commit
6a5d99daf1
|
@ -212,6 +212,17 @@ void CLayerGroup::DeleteLayer(int Index)
|
|||
m_pMap->m_Modified = true;
|
||||
}
|
||||
|
||||
void CLayerGroup::DuplicateLayer(int Index)
|
||||
{
|
||||
if(Index < 0 || Index >= (int)m_vpLayers.size())
|
||||
return;
|
||||
|
||||
auto *pDup = m_vpLayers[Index]->Duplicate();
|
||||
m_vpLayers.insert(m_vpLayers.begin() + Index + 1, pDup);
|
||||
|
||||
m_pMap->m_Modified = true;
|
||||
}
|
||||
|
||||
void CLayerGroup::GetSize(float *pWidth, float *pHeight) const
|
||||
{
|
||||
*pWidth = 0;
|
||||
|
@ -745,6 +756,11 @@ int CEditor::FindSelectedQuadIndex(int Index) const
|
|||
return -1;
|
||||
}
|
||||
|
||||
bool CEditor::IsSpecialLayer(const CLayer *pLayer) const
|
||||
{
|
||||
return m_Map.m_pGameLayer == pLayer || m_Map.m_pTeleLayer == pLayer || m_Map.m_pSpeedupLayer == pLayer || m_Map.m_pFrontLayer == pLayer || m_Map.m_pSwitchLayer == pLayer || m_Map.m_pTuneLayer == pLayer;
|
||||
}
|
||||
|
||||
void CEditor::CallbackOpenMap(const char *pFileName, int StorageType, void *pUser)
|
||||
{
|
||||
CEditor *pEditor = (CEditor *)pUser;
|
||||
|
@ -3480,7 +3496,7 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect View)
|
|||
else
|
||||
s_LayerPopupContext.m_vpLayers.clear();
|
||||
|
||||
UiInvokePopupMenu(&s_LayerPopupContext, 0, UI()->MouseX(), UI()->MouseY(), 120, 300, PopupLayer, &s_LayerPopupContext);
|
||||
UiInvokePopupMenu(&s_LayerPopupContext, 0, UI()->MouseX(), UI()->MouseY(), 120, 320, PopupLayer, &s_LayerPopupContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -127,6 +127,17 @@ public:
|
|||
m_BrushRefCount = 0;
|
||||
}
|
||||
|
||||
CLayer(const CLayer &Other)
|
||||
{
|
||||
str_copy(m_aName, Other.m_aName, sizeof(m_aName));
|
||||
m_Flags = Other.m_Flags;
|
||||
m_pEditor = Other.m_pEditor;
|
||||
m_Type = Other.m_Type;
|
||||
m_BrushRefCount = 0;
|
||||
m_Visible = true;
|
||||
m_Readonly = false;
|
||||
}
|
||||
|
||||
virtual ~CLayer()
|
||||
{
|
||||
}
|
||||
|
@ -147,6 +158,8 @@ public:
|
|||
virtual void ModifyEnvelopeIndex(INDEX_MODIFY_FUNC pfnFunc) {}
|
||||
virtual void ModifySoundIndex(INDEX_MODIFY_FUNC pfnFunc) {}
|
||||
|
||||
virtual CLayer *Duplicate() const = 0;
|
||||
|
||||
virtual void GetSize(float *pWidth, float *pHeight)
|
||||
{
|
||||
*pWidth = 0;
|
||||
|
@ -159,7 +172,6 @@ public:
|
|||
|
||||
bool m_Readonly;
|
||||
bool m_Visible;
|
||||
|
||||
int m_BrushRefCount;
|
||||
};
|
||||
|
||||
|
@ -198,6 +210,7 @@ public:
|
|||
void GetSize(float *pWidth, float *pHeight) const;
|
||||
|
||||
void DeleteLayer(int Index);
|
||||
void DuplicateLayer(int Index);
|
||||
int SwapLayers(int Index0, int Index1);
|
||||
|
||||
bool IsEmpty() const
|
||||
|
@ -554,6 +567,7 @@ protected:
|
|||
|
||||
public:
|
||||
CLayerTiles(int w, int h);
|
||||
CLayerTiles(const CLayerTiles &Other);
|
||||
~CLayerTiles();
|
||||
|
||||
virtual CTile GetTile(int x, int y);
|
||||
|
@ -580,6 +594,8 @@ public:
|
|||
void BrushFlipY() override;
|
||||
void BrushRotate(float Amount) override;
|
||||
|
||||
CLayer *Duplicate() const override;
|
||||
|
||||
virtual void ShowInfo();
|
||||
int RenderProperties(CUIRect *pToolbox) override;
|
||||
|
||||
|
@ -637,6 +653,7 @@ class CLayerQuads : public CLayer
|
|||
{
|
||||
public:
|
||||
CLayerQuads();
|
||||
CLayerQuads(const CLayerQuads &Other);
|
||||
~CLayerQuads();
|
||||
|
||||
void Render(bool QuadPicker = false) override;
|
||||
|
@ -655,6 +672,7 @@ public:
|
|||
void ModifyEnvelopeIndex(INDEX_MODIFY_FUNC pfnFunc) override;
|
||||
|
||||
void GetSize(float *pWidth, float *pHeight) override;
|
||||
CLayer *Duplicate() const override;
|
||||
|
||||
int m_Image;
|
||||
std::vector<CQuad> m_vQuads;
|
||||
|
@ -842,6 +860,7 @@ public:
|
|||
void DeleteSelectedQuads();
|
||||
bool IsQuadSelected(int Index) const;
|
||||
int FindSelectedQuadIndex(int Index) const;
|
||||
bool IsSpecialLayer(const CLayer *pLayer) const;
|
||||
|
||||
float ScaleFontSize(char *pText, int TextSize, float FontSize, int Width);
|
||||
int DoProperties(CUIRect *pToolbox, CProperty *pProps, int *pIDs, int *pNewVal, ColorRGBA Color = ColorRGBA(1, 1, 1, 0.5f));
|
||||
|
@ -1318,6 +1337,7 @@ class CLayerSounds : public CLayer
|
|||
{
|
||||
public:
|
||||
CLayerSounds();
|
||||
CLayerSounds(const CLayerSounds &Other);
|
||||
~CLayerSounds();
|
||||
|
||||
void Render(bool Tileset = false) override;
|
||||
|
@ -1332,6 +1352,8 @@ public:
|
|||
void ModifyEnvelopeIndex(INDEX_MODIFY_FUNC pfnFunc) override;
|
||||
void ModifySoundIndex(INDEX_MODIFY_FUNC pfnFunc) override;
|
||||
|
||||
CLayer *Duplicate() const override;
|
||||
|
||||
int m_Sound;
|
||||
std::vector<CSoundSource> m_vSources;
|
||||
};
|
||||
|
|
|
@ -14,6 +14,13 @@ CLayerQuads::CLayerQuads()
|
|||
m_Image = -1;
|
||||
}
|
||||
|
||||
CLayerQuads::CLayerQuads(const CLayerQuads &Other) :
|
||||
CLayer(Other)
|
||||
{
|
||||
m_Image = Other.m_Image;
|
||||
m_vQuads = Other.m_vQuads;
|
||||
}
|
||||
|
||||
CLayerQuads::~CLayerQuads() = default;
|
||||
|
||||
void CLayerQuads::Render(bool QuadPicker)
|
||||
|
@ -255,3 +262,8 @@ void CLayerQuads::ModifyEnvelopeIndex(INDEX_MODIFY_FUNC Func)
|
|||
Func(&Quad.m_ColorEnv);
|
||||
}
|
||||
}
|
||||
|
||||
CLayer *CLayerQuads::Duplicate() const
|
||||
{
|
||||
return new CLayerQuads(*this);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,13 @@ CLayerSounds::CLayerSounds()
|
|||
m_Sound = -1;
|
||||
}
|
||||
|
||||
CLayerSounds::CLayerSounds(const CLayerSounds &Other) :
|
||||
CLayer(Other)
|
||||
{
|
||||
m_Sound = Other.m_Sound;
|
||||
m_vSources = Other.m_vSources;
|
||||
}
|
||||
|
||||
CLayerSounds::~CLayerSounds() = default;
|
||||
|
||||
void CLayerSounds::Render(bool Tileset)
|
||||
|
@ -219,3 +226,8 @@ void CLayerSounds::ModifyEnvelopeIndex(INDEX_MODIFY_FUNC Func)
|
|||
Func(&Source.m_PosEnv);
|
||||
}
|
||||
}
|
||||
|
||||
CLayer *CLayerSounds::Duplicate() const
|
||||
{
|
||||
return new CLayerSounds(*this);
|
||||
}
|
||||
|
|
|
@ -40,6 +40,33 @@ CLayerTiles::CLayerTiles(int w, int h)
|
|||
mem_zero(m_pTiles, (size_t)m_Width * m_Height * sizeof(CTile));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
CLayerTiles::~CLayerTiles()
|
||||
{
|
||||
delete[] m_pTiles;
|
||||
|
@ -571,6 +598,11 @@ void CLayerTiles::BrushRotate(float Amount)
|
|||
}
|
||||
}
|
||||
|
||||
CLayer *CLayerTiles::Duplicate() const
|
||||
{
|
||||
return new CLayerTiles(*this);
|
||||
}
|
||||
|
||||
void CLayerTiles::Resize(int NewW, int NewH)
|
||||
{
|
||||
CTile *pNewData = new CTile[NewW * NewH];
|
||||
|
|
|
@ -397,6 +397,21 @@ int CEditor::PopupLayer(CEditor *pEditor, CUIRect View, void *pContext)
|
|||
return CLayerTiles::RenderCommonProperties(pPopup->m_CommonPropState, pEditor, &View, pPopup->m_vpLayers);
|
||||
}
|
||||
|
||||
// duplicate layer button
|
||||
CUIRect DupButton;
|
||||
static int s_DuplicationButton = 0;
|
||||
View.HSplitBottom(4.0f, &View, nullptr);
|
||||
View.HSplitBottom(12.0f, &View, &DupButton);
|
||||
|
||||
if(!pEditor->IsSpecialLayer(pEditor->GetSelectedLayer(0)))
|
||||
{
|
||||
if(pEditor->DoButton_Editor(&s_DuplicationButton, "Duplicate layer", 0, &DupButton, 0, "Duplicates the layer"))
|
||||
{
|
||||
pEditor->m_Map.m_vpGroups[pEditor->m_SelectedGroup]->DuplicateLayer(pEditor->m_vSelectedLayers[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// don't allow deletion of game layer
|
||||
if(pEditor->m_Map.m_pGameLayer != pEditor->GetSelectedLayer(0) &&
|
||||
pEditor->DoButton_Editor(&s_DeleteButton, "Delete layer", 0, &Button, 0, "Deletes the layer"))
|
||||
|
|
Loading…
Reference in a new issue