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