1#include "editor.h"
2#include "editor_actions.h"
3
4#include <engine/keys.h>
5
6#include <game/client/gameclient.h>
7#include <game/mapitems.h>
8
9void CEditor::FillGameTiles(EGameTileOp FillTile) const
10{
11 std::shared_ptr<CLayerTiles> pTileLayer = std::static_pointer_cast<CLayerTiles>(r: GetSelectedLayerType(Index: 0, Type: LAYERTYPE_TILES));
12 if(pTileLayer)
13 pTileLayer->FillGameTiles(Fill: FillTile);
14}
15
16bool CEditor::CanFillGameTiles() const
17{
18 std::shared_ptr<CLayerTiles> pTileLayer = std::static_pointer_cast<CLayerTiles>(r: GetSelectedLayerType(Index: 0, Type: LAYERTYPE_TILES));
19 if(pTileLayer)
20 return pTileLayer->CanFillGameTiles();
21 return false;
22}
23
24void CEditor::AddQuadOrSound()
25{
26 std::shared_ptr<CLayer> pLayer = GetSelectedLayer(Index: 0);
27 if(!pLayer)
28 return;
29 if(pLayer->m_Type != LAYERTYPE_QUADS && pLayer->m_Type != LAYERTYPE_SOUNDS)
30 return;
31
32 std::shared_ptr<CLayerGroup> pGroup = GetSelectedGroup();
33
34 float aMapping[4];
35 pGroup->Mapping(pPoints: aMapping);
36 int x = aMapping[0] + (aMapping[2] - aMapping[0]) / 2;
37 int y = aMapping[1] + (aMapping[3] - aMapping[1]) / 2;
38 if(m_Dialog == DIALOG_NONE && CLineInput::GetActiveInput() == nullptr && Input()->KeyPress(Key: KEY_Q) && Input()->ModifierIsPressed())
39 {
40 x += Ui()->MouseWorldX() - (MapView()->GetWorldOffset().x * pGroup->m_ParallaxX / 100) - pGroup->m_OffsetX;
41 y += Ui()->MouseWorldY() - (MapView()->GetWorldOffset().y * pGroup->m_ParallaxY / 100) - pGroup->m_OffsetY;
42 }
43
44 if(pLayer->m_Type == LAYERTYPE_QUADS)
45 m_Map.m_EditorHistory.Execute(pAction: std::make_shared<CEditorActionNewEmptyQuad>(args: &m_Map, args&: m_SelectedGroup, args&: m_vSelectedLayers[0], args&: x, args&: y));
46 else if(pLayer->m_Type == LAYERTYPE_SOUNDS)
47 m_Map.m_EditorHistory.Execute(pAction: std::make_shared<CEditorActionNewEmptySound>(args: &m_Map, args&: m_SelectedGroup, args&: m_vSelectedLayers[0], args&: x, args&: y));
48}
49
50void CEditor::AddGroup()
51{
52 m_Map.NewGroup();
53 m_SelectedGroup = m_Map.m_vpGroups.size() - 1;
54 m_Map.m_EditorHistory.RecordAction(pAction: std::make_shared<CEditorActionGroup>(args: &m_Map, args&: m_SelectedGroup, args: false));
55}
56
57void CEditor::AddSoundLayer()
58{
59 std::shared_ptr<CLayer> pSoundLayer = std::make_shared<CLayerSounds>(args: &m_Map);
60 m_Map.m_vpGroups[m_SelectedGroup]->AddLayer(pLayer: pSoundLayer);
61 int LayerIndex = m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers.size() - 1;
62 SelectLayer(LayerIndex);
63 m_Map.m_vpGroups[m_SelectedGroup]->m_Collapse = false;
64 m_Map.m_EditorHistory.RecordAction(pAction: std::make_shared<CEditorActionAddLayer>(args: &m_Map, args&: m_SelectedGroup, args&: LayerIndex));
65}
66
67void CEditor::AddTileLayer()
68{
69 std::shared_ptr<CLayer> pTileLayer = std::make_shared<CLayerTiles>(args: &m_Map, args&: m_Map.m_pGameLayer->m_Width, args&: m_Map.m_pGameLayer->m_Height);
70 m_Map.m_vpGroups[m_SelectedGroup]->AddLayer(pLayer: pTileLayer);
71 int LayerIndex = m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers.size() - 1;
72 SelectLayer(LayerIndex);
73 m_Map.m_vpGroups[m_SelectedGroup]->m_Collapse = false;
74 m_Map.m_EditorHistory.RecordAction(pAction: std::make_shared<CEditorActionAddLayer>(args: &m_Map, args&: m_SelectedGroup, args&: LayerIndex));
75}
76
77void CEditor::AddQuadsLayer()
78{
79 std::shared_ptr<CLayer> pQuadLayer = std::make_shared<CLayerQuads>(args: &m_Map);
80 m_Map.m_vpGroups[m_SelectedGroup]->AddLayer(pLayer: pQuadLayer);
81 int LayerIndex = m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers.size() - 1;
82 SelectLayer(LayerIndex);
83 m_Map.m_vpGroups[m_SelectedGroup]->m_Collapse = false;
84 m_Map.m_EditorHistory.RecordAction(pAction: std::make_shared<CEditorActionAddLayer>(args: &m_Map, args&: m_SelectedGroup, args&: LayerIndex));
85}
86
87void CEditor::AddSwitchLayer()
88{
89 std::shared_ptr<CLayer> pSwitchLayer = std::make_shared<CLayerSwitch>(args: &m_Map, args&: m_Map.m_pGameLayer->m_Width, args&: m_Map.m_pGameLayer->m_Height);
90 m_Map.MakeSwitchLayer(pLayer: pSwitchLayer);
91 m_Map.m_vpGroups[m_SelectedGroup]->AddLayer(pLayer: pSwitchLayer);
92 int LayerIndex = m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers.size() - 1;
93 SelectLayer(LayerIndex);
94 m_pBrush->Clear();
95 m_Map.m_EditorHistory.RecordAction(pAction: std::make_shared<CEditorActionAddLayer>(args: &m_Map, args&: m_SelectedGroup, args&: LayerIndex));
96}
97
98void CEditor::AddFrontLayer()
99{
100 std::shared_ptr<CLayer> pFrontLayer = std::make_shared<CLayerFront>(args: &m_Map, args&: m_Map.m_pGameLayer->m_Width, args&: m_Map.m_pGameLayer->m_Height);
101 m_Map.MakeFrontLayer(pLayer: pFrontLayer);
102 m_Map.m_vpGroups[m_SelectedGroup]->AddLayer(pLayer: pFrontLayer);
103 int LayerIndex = m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers.size() - 1;
104 SelectLayer(LayerIndex);
105 m_pBrush->Clear();
106 m_Map.m_EditorHistory.RecordAction(pAction: std::make_shared<CEditorActionAddLayer>(args: &m_Map, args&: m_SelectedGroup, args&: LayerIndex));
107}
108
109void CEditor::AddTuneLayer()
110{
111 std::shared_ptr<CLayer> pTuneLayer = std::make_shared<CLayerTune>(args: &m_Map, args&: m_Map.m_pGameLayer->m_Width, args&: m_Map.m_pGameLayer->m_Height);
112 m_Map.MakeTuneLayer(pLayer: pTuneLayer);
113 m_Map.m_vpGroups[m_SelectedGroup]->AddLayer(pLayer: pTuneLayer);
114 int LayerIndex = m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers.size() - 1;
115 SelectLayer(LayerIndex);
116 m_pBrush->Clear();
117 m_Map.m_EditorHistory.RecordAction(pAction: std::make_shared<CEditorActionAddLayer>(args: &m_Map, args&: m_SelectedGroup, args&: LayerIndex));
118}
119
120void CEditor::AddSpeedupLayer()
121{
122 std::shared_ptr<CLayer> pSpeedupLayer = std::make_shared<CLayerSpeedup>(args: &m_Map, args&: m_Map.m_pGameLayer->m_Width, args&: m_Map.m_pGameLayer->m_Height);
123 m_Map.MakeSpeedupLayer(pLayer: pSpeedupLayer);
124 m_Map.m_vpGroups[m_SelectedGroup]->AddLayer(pLayer: pSpeedupLayer);
125 int LayerIndex = m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers.size() - 1;
126 SelectLayer(LayerIndex);
127 m_pBrush->Clear();
128 m_Map.m_EditorHistory.RecordAction(pAction: std::make_shared<CEditorActionAddLayer>(args: &m_Map, args&: m_SelectedGroup, args&: LayerIndex));
129}
130
131void CEditor::AddTeleLayer()
132{
133 std::shared_ptr<CLayer> pTeleLayer = std::make_shared<CLayerTele>(args: &m_Map, args&: m_Map.m_pGameLayer->m_Width, args&: m_Map.m_pGameLayer->m_Height);
134 m_Map.MakeTeleLayer(pLayer: pTeleLayer);
135 m_Map.m_vpGroups[m_SelectedGroup]->AddLayer(pLayer: pTeleLayer);
136 int LayerIndex = m_Map.m_vpGroups[m_SelectedGroup]->m_vpLayers.size() - 1;
137 SelectLayer(LayerIndex);
138 m_pBrush->Clear();
139 m_Map.m_EditorHistory.RecordAction(pAction: std::make_shared<CEditorActionAddLayer>(args: &m_Map, args&: m_SelectedGroup, args&: LayerIndex));
140}
141
142bool CEditor::IsNonGameTileLayerSelected() const
143{
144 std::shared_ptr<CLayer> pLayer = GetSelectedLayer(Index: 0);
145 if(!pLayer)
146 return false;
147 if(pLayer->m_Type != LAYERTYPE_TILES)
148 return false;
149 if(
150 pLayer == m_Map.m_pGameLayer ||
151 pLayer == m_Map.m_pFrontLayer ||
152 pLayer == m_Map.m_pSwitchLayer ||
153 pLayer == m_Map.m_pTeleLayer ||
154 pLayer == m_Map.m_pSpeedupLayer ||
155 pLayer == m_Map.m_pTuneLayer)
156 return false;
157
158 return true;
159}
160
161void CEditor::LayerSelectImage()
162{
163 if(!IsNonGameTileLayerSelected())
164 return;
165
166 std::shared_ptr<CLayer> pLayer = GetSelectedLayer(Index: 0);
167 std::shared_ptr<CLayerTiles> pTiles = std::static_pointer_cast<CLayerTiles>(r: pLayer);
168
169 static SLayerPopupContext s_LayerPopupContext = {};
170 s_LayerPopupContext.m_pEditor = this;
171 Ui()->DoPopupMenu(pId: &s_LayerPopupContext, X: Ui()->MouseX(), Y: Ui()->MouseY(), Width: 150, Height: 300, pContext: &s_LayerPopupContext, pfnFunc: PopupLayer);
172 PopupSelectImageInvoke(Current: pTiles->m_Image, x: Ui()->MouseX(), y: Ui()->MouseY());
173}
174
175void CEditor::MapDetails()
176{
177 const CUIRect *pScreen = Ui()->Screen();
178 m_Map.m_MapInfoTmp.Copy(Source: m_Map.m_MapInfo);
179 static SPopupMenuId s_PopupMapInfoId;
180 constexpr float PopupWidth = 400.0f;
181 constexpr float PopupHeight = 170.0f;
182 Ui()->DoPopupMenu(
183 pId: &s_PopupMapInfoId,
184 X: pScreen->w / 2.0f - PopupWidth / 2.0f,
185 Y: pScreen->h / 2.0f - PopupHeight / 2.0f,
186 Width: PopupWidth,
187 Height: PopupHeight,
188 pContext: this,
189 pfnFunc: PopupMapInfo);
190 Ui()->SetActiveItem(nullptr);
191}
192
193void CEditor::DeleteSelectedLayer()
194{
195 std::shared_ptr<CLayer> pCurrentLayer = GetSelectedLayer(Index: 0);
196 if(!pCurrentLayer)
197 return;
198 if(m_Map.m_pGameLayer == pCurrentLayer)
199 return;
200
201 m_Map.m_EditorHistory.RecordAction(pAction: std::make_shared<CEditorActionDeleteLayer>(args: &m_Map, args&: m_SelectedGroup, args&: m_vSelectedLayers[0]));
202
203 if(pCurrentLayer == m_Map.m_pFrontLayer)
204 m_Map.m_pFrontLayer = nullptr;
205 if(pCurrentLayer == m_Map.m_pTeleLayer)
206 m_Map.m_pTeleLayer = nullptr;
207 if(pCurrentLayer == m_Map.m_pSpeedupLayer)
208 m_Map.m_pSpeedupLayer = nullptr;
209 if(pCurrentLayer == m_Map.m_pSwitchLayer)
210 m_Map.m_pSwitchLayer = nullptr;
211 if(pCurrentLayer == m_Map.m_pTuneLayer)
212 m_Map.m_pTuneLayer = nullptr;
213 m_Map.m_vpGroups[m_SelectedGroup]->DeleteLayer(Index: m_vSelectedLayers[0]);
214
215 SelectPreviousLayer();
216}
217
218void CEditor::TestMapLocally()
219{
220 const char *pFilenameNoMaps = str_startswith(str: m_aFilename, prefix: "maps/");
221 if(!pFilenameNoMaps)
222 {
223 ShowFileDialogError(pFormat: "The map isn't saved in the maps/ folder. It must be saved there to load on the server.");
224 return;
225 }
226
227 char aFilenameNoExt[IO_MAX_PATH_LENGTH];
228 fs_split_file_extension(filename: pFilenameNoMaps, name: aFilenameNoExt, name_size: sizeof(aFilenameNoExt));
229
230 if(Client()->RconAuthed())
231 {
232 if(net_addr_is_local(addr: &Client()->ServerAddress()))
233 {
234 OnClose();
235 g_Config.m_ClEditor = 0;
236 char aMapChange[IO_MAX_PATH_LENGTH + 64];
237 str_format(buffer: aMapChange, buffer_size: sizeof(aMapChange), format: "change_map %s", aFilenameNoExt);
238 Client()->Rcon(pLine: aMapChange);
239 return;
240 }
241 }
242
243 CGameClient *pGameClient = (CGameClient *)Kernel()->RequestInterface<IGameClient>();
244 if(pGameClient->m_LocalServer.IsServerRunning())
245 {
246 m_PopupEventType = CEditor::POPEVENT_RESTART_SERVER;
247 m_PopupEventActivated = true;
248 }
249 else
250 {
251 char aMapChange[IO_MAX_PATH_LENGTH + 64];
252 str_format(buffer: aMapChange, buffer_size: sizeof(aMapChange), format: "change_map %s", aFilenameNoExt);
253 pGameClient->m_LocalServer.RunServer(vpArguments: {"sv_register 0", aMapChange});
254 OnClose();
255 g_Config.m_ClEditor = 0;
256 Client()->Connect(pAddress: "localhost");
257 }
258}
259