1 | #include "layer_tune.h" |
2 | |
3 | #include <game/editor/editor.h> |
4 | |
5 | CLayerTune::CLayerTune(CEditor *pEditor, int w, int h) : |
6 | CLayerTiles(pEditor, w, h) |
7 | { |
8 | str_copy(dst&: m_aName, src: "Tune" ); |
9 | m_Tune = 1; |
10 | |
11 | m_pTuneTile = new CTuneTile[w * h]; |
12 | mem_zero(block: m_pTuneTile, size: (size_t)w * h * sizeof(CTuneTile)); |
13 | } |
14 | |
15 | CLayerTune::CLayerTune(const CLayerTune &Other) : |
16 | CLayerTiles(Other) |
17 | { |
18 | str_copy(dst&: m_aName, src: "Tune copy" ); |
19 | m_Tune = 1; |
20 | |
21 | m_pTuneTile = new CTuneTile[m_Width * m_Height]; |
22 | mem_copy(dest: m_pTuneTile, source: Other.m_pTuneTile, size: (size_t)m_Width * m_Height * sizeof(CTuneTile)); |
23 | } |
24 | |
25 | CLayerTune::~CLayerTune() |
26 | { |
27 | delete[] m_pTuneTile; |
28 | } |
29 | |
30 | void CLayerTune::Resize(int NewW, int NewH) |
31 | { |
32 | // resize Tune data |
33 | CTuneTile *pNewTuneData = new CTuneTile[NewW * NewH]; |
34 | mem_zero(block: pNewTuneData, size: (size_t)NewW * NewH * sizeof(CTuneTile)); |
35 | |
36 | // copy old data |
37 | for(int y = 0; y < minimum(a: NewH, b: m_Height); y++) |
38 | mem_copy(dest: &pNewTuneData[y * NewW], source: &m_pTuneTile[y * m_Width], size: minimum(a: m_Width, b: NewW) * sizeof(CTuneTile)); |
39 | |
40 | // replace old |
41 | delete[] m_pTuneTile; |
42 | m_pTuneTile = pNewTuneData; |
43 | |
44 | // resize tile data |
45 | CLayerTiles::Resize(NewW, NewH); |
46 | |
47 | // resize gamelayer too |
48 | if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH) |
49 | m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH); |
50 | } |
51 | |
52 | void CLayerTune::Shift(int Direction) |
53 | { |
54 | CLayerTiles::Shift(Direction); |
55 | ShiftImpl(pTiles: m_pTuneTile, Direction, ShiftBy: m_pEditor->m_ShiftBy); |
56 | } |
57 | |
58 | bool CLayerTune::IsEmpty(const std::shared_ptr<CLayerTiles> &pLayer) |
59 | { |
60 | for(int y = 0; y < pLayer->m_Height; y++) |
61 | for(int x = 0; x < pLayer->m_Width; x++) |
62 | if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidTuneTile(Index: pLayer->GetTile(x, y).m_Index)) |
63 | return false; |
64 | |
65 | return true; |
66 | } |
67 | |
68 | void CLayerTune::BrushDraw(std::shared_ptr<CLayer> pBrush, float wx, float wy) |
69 | { |
70 | if(m_Readonly) |
71 | return; |
72 | |
73 | std::shared_ptr<CLayerTune> pTuneLayer = std::static_pointer_cast<CLayerTune>(r: pBrush); |
74 | int sx = ConvertX(x: wx); |
75 | int sy = ConvertY(y: wy); |
76 | if(str_comp(a: pTuneLayer->m_aFileName, b: m_pEditor->m_aFileName)) |
77 | { |
78 | m_pEditor->m_TuningNum = pTuneLayer->m_TuningNumber; |
79 | } |
80 | |
81 | bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(pLayer: pTuneLayer); |
82 | |
83 | for(int y = 0; y < pTuneLayer->m_Height; y++) |
84 | for(int x = 0; x < pTuneLayer->m_Width; x++) |
85 | { |
86 | int fx = x + sx; |
87 | int fy = y + sy; |
88 | |
89 | if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) |
90 | continue; |
91 | |
92 | if(!Destructive && GetTile(x: fx, y: fy).m_Index) |
93 | continue; |
94 | |
95 | int Index = fy * m_Width + fx; |
96 | STuneTileStateChange::SData Previous{ |
97 | .m_Number: m_pTuneTile[Index].m_Number, |
98 | .m_Type: m_pTuneTile[Index].m_Type, |
99 | .m_Index: m_pTiles[Index].m_Index}; |
100 | |
101 | if((m_pEditor->m_AllowPlaceUnusedTiles || IsValidTuneTile(Index: pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index)) && pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index != TILE_AIR) |
102 | { |
103 | if(m_pEditor->m_TuningNum != pTuneLayer->m_TuningNumber) |
104 | { |
105 | m_pTuneTile[Index].m_Number = m_pEditor->m_TuningNum; |
106 | } |
107 | else if(pTuneLayer->m_pTuneTile[y * pTuneLayer->m_Width + x].m_Number) |
108 | m_pTuneTile[Index].m_Number = pTuneLayer->m_pTuneTile[y * pTuneLayer->m_Width + x].m_Number; |
109 | else |
110 | { |
111 | if(!m_pEditor->m_TuningNum) |
112 | { |
113 | m_pTuneTile[Index].m_Number = 0; |
114 | m_pTuneTile[Index].m_Type = 0; |
115 | m_pTiles[Index].m_Index = 0; |
116 | continue; |
117 | } |
118 | else |
119 | m_pTuneTile[Index].m_Number = m_pEditor->m_TuningNum; |
120 | } |
121 | |
122 | m_pTuneTile[Index].m_Type = pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index; |
123 | m_pTiles[Index].m_Index = pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index; |
124 | } |
125 | else |
126 | { |
127 | m_pTuneTile[Index].m_Number = 0; |
128 | m_pTuneTile[Index].m_Type = 0; |
129 | m_pTiles[Index].m_Index = 0; |
130 | |
131 | if(pTuneLayer->m_pTiles[y * pTuneLayer->m_Width + x].m_Index != TILE_AIR) |
132 | ShowPreventUnusedTilesWarning(); |
133 | } |
134 | |
135 | STuneTileStateChange::SData Current{ |
136 | .m_Number: m_pTuneTile[Index].m_Number, |
137 | .m_Type: m_pTuneTile[Index].m_Type, |
138 | .m_Index: m_pTiles[Index].m_Index}; |
139 | |
140 | RecordStateChange(x: fx, y: fy, Previous, Current); |
141 | } |
142 | FlagModified(x: sx, y: sy, w: pTuneLayer->m_Width, h: pTuneLayer->m_Height); |
143 | } |
144 | |
145 | void CLayerTune::RecordStateChange(int x, int y, STuneTileStateChange::SData Previous, STuneTileStateChange::SData Current) |
146 | { |
147 | if(!m_History[y][x].m_Changed) |
148 | m_History[y][x] = STuneTileStateChange{.m_Changed: true, .m_Previous: Previous, .m_Current: Current}; |
149 | else |
150 | m_History[y][x].m_Current = Current; |
151 | } |
152 | |
153 | void CLayerTune::BrushFlipX() |
154 | { |
155 | CLayerTiles::BrushFlipX(); |
156 | BrushFlipXImpl(pTiles: m_pTuneTile); |
157 | } |
158 | |
159 | void CLayerTune::BrushFlipY() |
160 | { |
161 | CLayerTiles::BrushFlipY(); |
162 | BrushFlipYImpl(pTiles: m_pTuneTile); |
163 | } |
164 | |
165 | void CLayerTune::BrushRotate(float Amount) |
166 | { |
167 | int Rotation = (round_to_int(f: 360.0f * Amount / (pi * 2)) / 90) % 4; // 0=0°, 1=90°, 2=180°, 3=270° |
168 | if(Rotation < 0) |
169 | Rotation += 4; |
170 | |
171 | if(Rotation == 1 || Rotation == 3) |
172 | { |
173 | // 90° rotation |
174 | CTuneTile *pTempData1 = new CTuneTile[m_Width * m_Height]; |
175 | CTile *pTempData2 = new CTile[m_Width * m_Height]; |
176 | mem_copy(dest: pTempData1, source: m_pTuneTile, size: (size_t)m_Width * m_Height * sizeof(CTuneTile)); |
177 | mem_copy(dest: pTempData2, source: m_pTiles, size: (size_t)m_Width * m_Height * sizeof(CTile)); |
178 | CTuneTile *pDst1 = m_pTuneTile; |
179 | CTile *pDst2 = m_pTiles; |
180 | for(int x = 0; x < m_Width; ++x) |
181 | for(int y = m_Height - 1; y >= 0; --y, ++pDst1, ++pDst2) |
182 | { |
183 | *pDst1 = pTempData1[y * m_Width + x]; |
184 | *pDst2 = pTempData2[y * m_Width + x]; |
185 | } |
186 | |
187 | std::swap(a&: m_Width, b&: m_Height); |
188 | delete[] pTempData1; |
189 | delete[] pTempData2; |
190 | } |
191 | |
192 | if(Rotation == 2 || Rotation == 3) |
193 | { |
194 | BrushFlipX(); |
195 | BrushFlipY(); |
196 | } |
197 | } |
198 | |
199 | void CLayerTune::FillSelection(bool Empty, std::shared_ptr<CLayer> pBrush, CUIRect Rect) |
200 | { |
201 | if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES)) |
202 | return; |
203 | |
204 | Snap(pRect: &Rect); // corrects Rect; no need of <= |
205 | |
206 | int sx = ConvertX(x: Rect.x); |
207 | int sy = ConvertY(y: Rect.y); |
208 | int w = ConvertX(x: Rect.w); |
209 | int h = ConvertY(y: Rect.h); |
210 | |
211 | std::shared_ptr<CLayerTune> pLt = std::static_pointer_cast<CLayerTune>(r: pBrush); |
212 | |
213 | bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLayer: pLt); |
214 | |
215 | for(int y = 0; y < h; y++) |
216 | { |
217 | for(int x = 0; x < w; x++) |
218 | { |
219 | int fx = x + sx; |
220 | int fy = y + sy; |
221 | |
222 | if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) |
223 | continue; |
224 | |
225 | if(!Destructive && GetTile(x: fx, y: fy).m_Index) |
226 | continue; |
227 | |
228 | const int SrcIndex = Empty ? 0 : (y * pLt->m_Width + x % pLt->m_Width) % (pLt->m_Width * pLt->m_Height); |
229 | const int TgtIndex = fy * m_Width + fx; |
230 | |
231 | STuneTileStateChange::SData Previous{ |
232 | .m_Number: m_pTuneTile[TgtIndex].m_Number, |
233 | .m_Type: m_pTuneTile[TgtIndex].m_Type, |
234 | .m_Index: m_pTiles[TgtIndex].m_Index}; |
235 | |
236 | if(Empty || (!m_pEditor->m_AllowPlaceUnusedTiles && !IsValidTuneTile(Index: (pLt->m_pTiles[SrcIndex]).m_Index))) |
237 | { |
238 | m_pTiles[TgtIndex].m_Index = 0; |
239 | m_pTuneTile[TgtIndex].m_Type = 0; |
240 | m_pTuneTile[TgtIndex].m_Number = 0; |
241 | |
242 | if(!Empty) |
243 | ShowPreventUnusedTilesWarning(); |
244 | } |
245 | else |
246 | { |
247 | m_pTiles[TgtIndex] = pLt->m_pTiles[SrcIndex]; |
248 | if(pLt->m_Tune && m_pTiles[TgtIndex].m_Index > 0) |
249 | { |
250 | m_pTuneTile[TgtIndex].m_Type = m_pTiles[fy * m_Width + fx].m_Index; |
251 | |
252 | if((pLt->m_pTuneTile[SrcIndex].m_Number == 0 && m_pEditor->m_TuningNum) || m_pEditor->m_TuningNum != pLt->m_TuningNumber) |
253 | m_pTuneTile[TgtIndex].m_Number = m_pEditor->m_TuningNum; |
254 | else |
255 | m_pTuneTile[TgtIndex].m_Number = pLt->m_pTuneTile[SrcIndex].m_Number; |
256 | } |
257 | } |
258 | |
259 | STuneTileStateChange::SData Current{ |
260 | .m_Number: m_pTuneTile[TgtIndex].m_Number, |
261 | .m_Type: m_pTuneTile[TgtIndex].m_Type, |
262 | .m_Index: m_pTiles[TgtIndex].m_Index}; |
263 | |
264 | RecordStateChange(x: fx, y: fy, Previous, Current); |
265 | } |
266 | } |
267 | |
268 | FlagModified(x: sx, y: sy, w, h); |
269 | } |
270 | |
271 | std::shared_ptr<CLayer> CLayerTune::Duplicate() const |
272 | { |
273 | return std::make_shared<CLayerTune>(args: *this); |
274 | } |
275 | |
276 | const char *CLayerTune::TypeName() const |
277 | { |
278 | return "tune" ; |
279 | } |
280 | |