1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#include <engine/demo.h>
4#include <engine/graphics.h>
5#include <engine/keys.h>
6#include <engine/serverbrowser.h>
7#include <engine/shared/config.h>
8#include <engine/storage.h>
9
10#include <game/client/gameclient.h>
11#include <game/client/render.h>
12
13#include <game/layers.h>
14#include <game/mapitems.h>
15#include <game/mapitems_ex.h>
16
17#include <game/client/components/camera.h>
18#include <game/client/components/mapimages.h>
19#include <game/localization.h>
20
21#include "maplayers.h"
22
23#include <chrono>
24
25using namespace std::chrono_literals;
26
27CMapLayers::CMapLayers(int t, bool OnlineOnly)
28{
29 m_Type = t;
30 m_pLayers = 0;
31 m_CurrentLocalTick = 0;
32 m_LastLocalTick = 0;
33 m_EnvelopeUpdate = false;
34 m_OnlineOnly = OnlineOnly;
35}
36
37void CMapLayers::OnInit()
38{
39 m_pLayers = Layers();
40 m_pImages = &m_pClient->m_MapImages;
41}
42
43CCamera *CMapLayers::GetCurCamera()
44{
45 return &m_pClient->m_Camera;
46}
47
48void CMapLayers::EnvelopeUpdate()
49{
50 if(Client()->State() == IClient::STATE_DEMOPLAYBACK)
51 {
52 const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo();
53 m_CurrentLocalTick = pInfo->m_CurrentTick;
54 m_LastLocalTick = pInfo->m_CurrentTick;
55 m_EnvelopeUpdate = true;
56 }
57}
58
59void CMapLayers::EnvelopeEval(int TimeOffsetMillis, int Env, ColorRGBA &Result, size_t Channels, void *pUser)
60{
61 CMapLayers *pThis = (CMapLayers *)pUser;
62
63 int EnvStart, EnvNum;
64 pThis->m_pLayers->Map()->GetType(Type: MAPITEMTYPE_ENVELOPE, pStart: &EnvStart, pNum: &EnvNum);
65 if(Env < 0 || Env >= EnvNum)
66 return;
67
68 const CMapItemEnvelope *pItem = (CMapItemEnvelope *)pThis->m_pLayers->Map()->GetItem(Index: EnvStart + Env);
69 if(pItem->m_Channels <= 0)
70 return;
71 Channels = minimum<size_t>(a: Channels, b: pItem->m_Channels, c: CEnvPoint::MAX_CHANNELS);
72
73 CMapBasedEnvelopePointAccess EnvelopePoints(pThis->m_pLayers->Map());
74 EnvelopePoints.SetPointsRange(StartPoint: pItem->m_StartPoint, NumPoints: pItem->m_NumPoints);
75 if(EnvelopePoints.NumPoints() == 0)
76 return;
77
78 const auto TickToNanoSeconds = std::chrono::nanoseconds(1s) / (int64_t)pThis->Client()->GameTickSpeed();
79
80 static std::chrono::nanoseconds s_Time{0};
81 static auto s_LastLocalTime = time_get_nanoseconds();
82 if(pThis->Client()->State() == IClient::STATE_DEMOPLAYBACK)
83 {
84 const IDemoPlayer::CInfo *pInfo = pThis->DemoPlayer()->BaseInfo();
85
86 if(!pInfo->m_Paused || pThis->m_EnvelopeUpdate)
87 {
88 if(pThis->m_CurrentLocalTick != pInfo->m_CurrentTick)
89 {
90 pThis->m_LastLocalTick = pThis->m_CurrentLocalTick;
91 pThis->m_CurrentLocalTick = pInfo->m_CurrentTick;
92 }
93 if(pItem->m_Version < 2 || pItem->m_Synchronized)
94 {
95 if(pThis->m_pClient->m_Snap.m_pGameInfoObj)
96 {
97 // get the lerp of the current tick and prev
98 int MinTick = pThis->Client()->PrevGameTick(Conn: g_Config.m_ClDummy) - pThis->m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick;
99 int CurTick = pThis->Client()->GameTick(Conn: g_Config.m_ClDummy) - pThis->m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick;
100 s_Time = std::chrono::nanoseconds((int64_t)(mix<double>(
101 a: 0,
102 b: (CurTick - MinTick),
103 amount: (double)pThis->Client()->IntraGameTick(Conn: g_Config.m_ClDummy)) *
104 TickToNanoSeconds.count())) +
105 MinTick * TickToNanoSeconds;
106 }
107 }
108 else
109 {
110 int MinTick = pThis->m_LastLocalTick;
111 s_Time = std::chrono::nanoseconds((int64_t)(mix<double>(a: 0,
112 b: pThis->m_CurrentLocalTick - MinTick,
113 amount: (double)pThis->Client()->IntraGameTick(Conn: g_Config.m_ClDummy)) *
114 TickToNanoSeconds.count())) +
115 MinTick * TickToNanoSeconds;
116 }
117 }
118 CRenderTools::RenderEvalEnvelope(pPoints: &EnvelopePoints, TimeNanos: s_Time + (int64_t)TimeOffsetMillis * std::chrono::nanoseconds(1ms), Result, Channels);
119 }
120 else
121 {
122 if(pThis->m_OnlineOnly && (pItem->m_Version < 2 || pItem->m_Synchronized))
123 {
124 if(pThis->m_pClient->m_Snap.m_pGameInfoObj) // && !(pThis->m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED))
125 {
126 // get the lerp of the current tick and prev
127 int MinTick = pThis->Client()->PrevGameTick(Conn: g_Config.m_ClDummy) - pThis->m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick;
128 int CurTick = pThis->Client()->GameTick(Conn: g_Config.m_ClDummy) - pThis->m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick;
129 s_Time = std::chrono::nanoseconds((int64_t)(mix<double>(
130 a: 0,
131 b: (CurTick - MinTick),
132 amount: (double)pThis->Client()->IntraGameTick(Conn: g_Config.m_ClDummy)) *
133 TickToNanoSeconds.count())) +
134 MinTick * TickToNanoSeconds;
135 }
136 }
137 else
138 {
139 auto CurTime = time_get_nanoseconds();
140 s_Time += CurTime - s_LastLocalTime;
141 s_LastLocalTime = CurTime;
142 }
143 CRenderTools::RenderEvalEnvelope(pPoints: &EnvelopePoints, TimeNanos: s_Time + std::chrono::nanoseconds(std::chrono::milliseconds(TimeOffsetMillis)), Result, Channels);
144 }
145}
146
147void FillTmpTile(SGraphicTile *pTmpTile, SGraphicTileTexureCoords *pTmpTex, unsigned char Flags, unsigned char Index, int x, int y, const ivec2 &Offset, int Scale, CMapItemGroup *pGroup)
148{
149 if(pTmpTex)
150 {
151 unsigned char x0 = 0;
152 unsigned char y0 = 0;
153 unsigned char x1 = x0 + 1;
154 unsigned char y1 = y0;
155 unsigned char x2 = x0 + 1;
156 unsigned char y2 = y0 + 1;
157 unsigned char x3 = x0;
158 unsigned char y3 = y0 + 1;
159
160 if(Flags & TILEFLAG_XFLIP)
161 {
162 x0 = x2;
163 x1 = x3;
164 x2 = x3;
165 x3 = x0;
166 }
167
168 if(Flags & TILEFLAG_YFLIP)
169 {
170 y0 = y3;
171 y2 = y1;
172 y3 = y1;
173 y1 = y0;
174 }
175
176 if(Flags & TILEFLAG_ROTATE)
177 {
178 unsigned char Tmp = x0;
179 x0 = x3;
180 x3 = x2;
181 x2 = x1;
182 x1 = Tmp;
183 Tmp = y0;
184 y0 = y3;
185 y3 = y2;
186 y2 = y1;
187 y1 = Tmp;
188 }
189
190 pTmpTex->m_TexCoordTopLeft.x = x0;
191 pTmpTex->m_TexCoordTopLeft.y = y0;
192 pTmpTex->m_TexCoordBottomLeft.x = x3;
193 pTmpTex->m_TexCoordBottomLeft.y = y3;
194 pTmpTex->m_TexCoordTopRight.x = x1;
195 pTmpTex->m_TexCoordTopRight.y = y1;
196 pTmpTex->m_TexCoordBottomRight.x = x2;
197 pTmpTex->m_TexCoordBottomRight.y = y2;
198
199 pTmpTex->m_TexCoordTopLeft.z = Index;
200 pTmpTex->m_TexCoordBottomLeft.z = Index;
201 pTmpTex->m_TexCoordTopRight.z = Index;
202 pTmpTex->m_TexCoordBottomRight.z = Index;
203
204 bool HasRotation = (Flags & TILEFLAG_ROTATE) != 0;
205 pTmpTex->m_TexCoordTopLeft.w = HasRotation;
206 pTmpTex->m_TexCoordBottomLeft.w = HasRotation;
207 pTmpTex->m_TexCoordTopRight.w = HasRotation;
208 pTmpTex->m_TexCoordBottomRight.w = HasRotation;
209 }
210
211 pTmpTile->m_TopLeft.x = x * Scale + Offset.x;
212 pTmpTile->m_TopLeft.y = y * Scale + Offset.y;
213 pTmpTile->m_BottomLeft.x = x * Scale + Offset.x;
214 pTmpTile->m_BottomLeft.y = y * Scale + Scale + Offset.y;
215 pTmpTile->m_TopRight.x = x * Scale + Scale + Offset.x;
216 pTmpTile->m_TopRight.y = y * Scale + Offset.y;
217 pTmpTile->m_BottomRight.x = x * Scale + Scale + Offset.x;
218 pTmpTile->m_BottomRight.y = y * Scale + Scale + Offset.y;
219}
220
221void FillTmpTileSpeedup(SGraphicTile *pTmpTile, SGraphicTileTexureCoords *pTmpTex, unsigned char Flags, unsigned char Index, int x, int y, const ivec2 &Offset, int Scale, CMapItemGroup *pGroup, short AngleRotate)
222{
223 int Angle = AngleRotate % 360;
224 FillTmpTile(pTmpTile, pTmpTex, Flags: Angle >= 270 ? ROTATION_270 : (Angle >= 180 ? ROTATION_180 : (Angle >= 90 ? ROTATION_90 : 0)), Index: AngleRotate % 90, x, y, Offset, Scale, pGroup);
225}
226
227bool CMapLayers::STileLayerVisuals::Init(unsigned int Width, unsigned int Height)
228{
229 m_Width = Width;
230 m_Height = Height;
231 if(Width == 0 || Height == 0)
232 return false;
233 if constexpr(sizeof(unsigned int) >= sizeof(ptrdiff_t))
234 if(Width >= std::numeric_limits<std::ptrdiff_t>::max() || Height >= std::numeric_limits<std::ptrdiff_t>::max())
235 return false;
236
237 m_pTilesOfLayer = new CMapLayers::STileLayerVisuals::STileVisual[Height * Width];
238
239 m_vBorderTop.resize(new_size: Width);
240 m_vBorderBottom.resize(new_size: Width);
241
242 m_vBorderLeft.resize(new_size: Height);
243 m_vBorderRight.resize(new_size: Height);
244 return true;
245}
246
247CMapLayers::STileLayerVisuals::~STileLayerVisuals()
248{
249 delete[] m_pTilesOfLayer;
250
251 m_pTilesOfLayer = NULL;
252}
253
254bool AddTile(std::vector<SGraphicTile> &vTmpTiles, std::vector<SGraphicTileTexureCoords> &vTmpTileTexCoords, unsigned char Index, unsigned char Flags, int x, int y, CMapItemGroup *pGroup, bool DoTextureCoords, bool FillSpeedup = false, int AngleRotate = -1, const ivec2 &Offset = ivec2{0, 0}, int Scale = 32)
255{
256 if(Index)
257 {
258 vTmpTiles.emplace_back();
259 SGraphicTile &Tile = vTmpTiles.back();
260 SGraphicTileTexureCoords *pTileTex = NULL;
261 if(DoTextureCoords)
262 {
263 vTmpTileTexCoords.emplace_back();
264 SGraphicTileTexureCoords &TileTex = vTmpTileTexCoords.back();
265 pTileTex = &TileTex;
266 }
267 if(FillSpeedup)
268 FillTmpTileSpeedup(pTmpTile: &Tile, pTmpTex: pTileTex, Flags, Index: 0, x, y, Offset, Scale, pGroup, AngleRotate);
269 else
270 FillTmpTile(pTmpTile: &Tile, pTmpTex: pTileTex, Flags, Index, x, y, Offset, Scale, pGroup);
271
272 return true;
273 }
274 return false;
275}
276
277struct STmpQuadVertexTextured
278{
279 float m_X, m_Y, m_CenterX, m_CenterY;
280 unsigned char m_R, m_G, m_B, m_A;
281 float m_U, m_V;
282};
283
284struct STmpQuadVertex
285{
286 float m_X, m_Y, m_CenterX, m_CenterY;
287 unsigned char m_R, m_G, m_B, m_A;
288};
289
290struct STmpQuad
291{
292 STmpQuadVertex m_aVertices[4];
293};
294
295struct STmpQuadTextured
296{
297 STmpQuadVertexTextured m_aVertices[4];
298};
299
300void mem_copy_special(void *pDest, void *pSource, size_t Size, size_t Count, size_t Steps)
301{
302 size_t CurStep = 0;
303 for(size_t i = 0; i < Count; ++i)
304 {
305 mem_copy(dest: ((char *)pDest) + CurStep + i * Size, source: ((char *)pSource) + i * Size, size: Size);
306 CurStep += Steps;
307 }
308}
309
310CMapLayers::~CMapLayers()
311{
312 //clear everything and destroy all buffers
313 if(!m_vpTileLayerVisuals.empty())
314 {
315 int s = m_vpTileLayerVisuals.size();
316 for(int i = 0; i < s; ++i)
317 {
318 delete m_vpTileLayerVisuals[i];
319 }
320 }
321 if(!m_vpQuadLayerVisuals.empty())
322 {
323 int s = m_vpQuadLayerVisuals.size();
324 for(int i = 0; i < s; ++i)
325 {
326 delete m_vpQuadLayerVisuals[i];
327 }
328 }
329}
330
331void CMapLayers::OnMapLoad()
332{
333 if(!Graphics()->IsTileBufferingEnabled() && !Graphics()->IsQuadBufferingEnabled())
334 return;
335
336 const char *pConnectCaption = GameClient()->DemoPlayer()->IsPlaying() ? Localize(pStr: "Preparing demo playback") : Localize(pStr: "Connected");
337 const char *pLoadMapContent = Localize(pStr: "Uploading map data to GPU");
338
339 auto CurTime = time_get_nanoseconds();
340 auto &&RenderLoading = [&]() {
341 if(CanRenderMenuBackground())
342 GameClient()->m_Menus.RenderLoading(pCaption: pConnectCaption, pContent: pLoadMapContent, IncreaseCounter: 0, RenderLoadingBar: false);
343 else if(time_get_nanoseconds() - CurTime > 500ms)
344 GameClient()->m_Menus.RenderLoading(pCaption: pConnectCaption, pContent: pLoadMapContent, IncreaseCounter: 0, RenderLoadingBar: false, RenderMenuBackgroundMap: false);
345 };
346
347 //clear everything and destroy all buffers
348 if(!m_vpTileLayerVisuals.empty())
349 {
350 int s = m_vpTileLayerVisuals.size();
351 for(int i = 0; i < s; ++i)
352 {
353 Graphics()->DeleteBufferContainer(ContainerIndex&: m_vpTileLayerVisuals[i]->m_BufferContainerIndex, DestroyAllBO: true);
354 delete m_vpTileLayerVisuals[i];
355 }
356 m_vpTileLayerVisuals.clear();
357 }
358 if(!m_vpQuadLayerVisuals.empty())
359 {
360 int s = m_vpQuadLayerVisuals.size();
361 for(int i = 0; i < s; ++i)
362 {
363 Graphics()->DeleteBufferContainer(ContainerIndex&: m_vpQuadLayerVisuals[i]->m_BufferContainerIndex, DestroyAllBO: true);
364 delete m_vpQuadLayerVisuals[i];
365 }
366 m_vpQuadLayerVisuals.clear();
367
368 RenderLoading();
369 }
370
371 bool PassedGameLayer = false;
372 //prepare all visuals for all tile layers
373 std::vector<SGraphicTile> vtmpTiles;
374 std::vector<SGraphicTileTexureCoords> vtmpTileTexCoords;
375 std::vector<SGraphicTile> vtmpBorderTopTiles;
376 std::vector<SGraphicTileTexureCoords> vtmpBorderTopTilesTexCoords;
377 std::vector<SGraphicTile> vtmpBorderLeftTiles;
378 std::vector<SGraphicTileTexureCoords> vtmpBorderLeftTilesTexCoords;
379 std::vector<SGraphicTile> vtmpBorderRightTiles;
380 std::vector<SGraphicTileTexureCoords> vtmpBorderRightTilesTexCoords;
381 std::vector<SGraphicTile> vtmpBorderBottomTiles;
382 std::vector<SGraphicTileTexureCoords> vtmpBorderBottomTilesTexCoords;
383 std::vector<SGraphicTile> vtmpBorderCorners;
384 std::vector<SGraphicTileTexureCoords> vtmpBorderCornersTexCoords;
385
386 std::vector<STmpQuad> vtmpQuads;
387 std::vector<STmpQuadTextured> vtmpQuadsTextured;
388
389 for(int g = 0; g < m_pLayers->NumGroups(); g++)
390 {
391 CMapItemGroup *pGroup = m_pLayers->GetGroup(Index: g);
392 if(!pGroup)
393 {
394 dbg_msg(sys: "maplayers", fmt: "error group was null, group number = %d, total groups = %d", g, m_pLayers->NumGroups());
395 dbg_msg(sys: "maplayers", fmt: "this is here to prevent a crash but the source of this is unknown, please report this for it to get fixed");
396 dbg_msg(sys: "maplayers", fmt: "we need mapname and crc and the map that caused this if possible, and anymore info you think is relevant");
397 continue;
398 }
399
400 for(int l = 0; l < pGroup->m_NumLayers; l++)
401 {
402 CMapItemLayer *pLayer = m_pLayers->GetLayer(Index: pGroup->m_StartLayer + l);
403 bool IsFrontLayer = false;
404 bool IsSwitchLayer = false;
405 bool IsTeleLayer = false;
406 bool IsSpeedupLayer = false;
407 bool IsTuneLayer = false;
408 bool IsGameLayer = false;
409 bool IsEntityLayer = false;
410
411 if(pLayer == (CMapItemLayer *)m_pLayers->GameLayer())
412 {
413 IsGameLayer = true;
414 IsEntityLayer = true;
415 PassedGameLayer = true;
416 }
417
418 if(pLayer == (CMapItemLayer *)m_pLayers->FrontLayer())
419 IsEntityLayer = IsFrontLayer = true;
420
421 if(pLayer == (CMapItemLayer *)m_pLayers->SwitchLayer())
422 IsEntityLayer = IsSwitchLayer = true;
423
424 if(pLayer == (CMapItemLayer *)m_pLayers->TeleLayer())
425 IsEntityLayer = IsTeleLayer = true;
426
427 if(pLayer == (CMapItemLayer *)m_pLayers->SpeedupLayer())
428 IsEntityLayer = IsSpeedupLayer = true;
429
430 if(pLayer == (CMapItemLayer *)m_pLayers->TuneLayer())
431 IsEntityLayer = IsTuneLayer = true;
432
433 if(m_Type <= TYPE_BACKGROUND_FORCE)
434 {
435 if(PassedGameLayer)
436 return;
437 }
438 else if(m_Type == TYPE_FOREGROUND)
439 {
440 if(!PassedGameLayer)
441 continue;
442 }
443
444 if(pLayer->m_Type == LAYERTYPE_TILES && Graphics()->IsTileBufferingEnabled())
445 {
446 bool DoTextureCoords = false;
447 CMapItemLayerTilemap *pTMap = (CMapItemLayerTilemap *)pLayer;
448 if(pTMap->m_Image == -1)
449 {
450 if(IsEntityLayer)
451 DoTextureCoords = true;
452 }
453 else
454 DoTextureCoords = true;
455
456 int DataIndex = 0;
457 unsigned int TileSize = 0;
458 int OverlayCount = 0;
459 if(IsFrontLayer)
460 {
461 DataIndex = pTMap->m_Front;
462 TileSize = sizeof(CTile);
463 }
464 else if(IsSwitchLayer)
465 {
466 DataIndex = pTMap->m_Switch;
467 TileSize = sizeof(CSwitchTile);
468 OverlayCount = 2;
469 }
470 else if(IsTeleLayer)
471 {
472 DataIndex = pTMap->m_Tele;
473 TileSize = sizeof(CTeleTile);
474 OverlayCount = 1;
475 }
476 else if(IsSpeedupLayer)
477 {
478 DataIndex = pTMap->m_Speedup;
479 TileSize = sizeof(CSpeedupTile);
480 OverlayCount = 2;
481 }
482 else if(IsTuneLayer)
483 {
484 DataIndex = pTMap->m_Tune;
485 TileSize = sizeof(CTuneTile);
486 }
487 else
488 {
489 DataIndex = pTMap->m_Data;
490 TileSize = sizeof(CTile);
491 }
492 unsigned int Size = m_pLayers->Map()->GetDataSize(Index: DataIndex);
493 void *pTiles = m_pLayers->Map()->GetData(Index: DataIndex);
494
495 if(Size >= pTMap->m_Width * pTMap->m_Height * TileSize)
496 {
497 int CurOverlay = 0;
498 while(CurOverlay < OverlayCount + 1)
499 {
500 // We can later just count the tile layers to get the idx in the vector
501 m_vpTileLayerVisuals.push_back(x: new STileLayerVisuals());
502 STileLayerVisuals &Visuals = *m_vpTileLayerVisuals.back();
503 if(!Visuals.Init(Width: pTMap->m_Width, Height: pTMap->m_Height))
504 {
505 ++CurOverlay;
506 continue;
507 }
508 Visuals.m_IsTextured = DoTextureCoords;
509
510 vtmpTiles.clear();
511 vtmpTileTexCoords.clear();
512
513 vtmpBorderTopTiles.clear();
514 vtmpBorderLeftTiles.clear();
515 vtmpBorderRightTiles.clear();
516 vtmpBorderBottomTiles.clear();
517 vtmpBorderCorners.clear();
518 vtmpBorderTopTilesTexCoords.clear();
519 vtmpBorderLeftTilesTexCoords.clear();
520 vtmpBorderRightTilesTexCoords.clear();
521 vtmpBorderBottomTilesTexCoords.clear();
522 vtmpBorderCornersTexCoords.clear();
523
524 if(!DoTextureCoords)
525 {
526 vtmpTiles.reserve(n: (size_t)pTMap->m_Width * pTMap->m_Height);
527 vtmpBorderTopTiles.reserve(n: (size_t)pTMap->m_Width);
528 vtmpBorderBottomTiles.reserve(n: (size_t)pTMap->m_Width);
529 vtmpBorderLeftTiles.reserve(n: (size_t)pTMap->m_Height);
530 vtmpBorderRightTiles.reserve(n: (size_t)pTMap->m_Height);
531 vtmpBorderCorners.reserve(n: (size_t)4);
532 }
533 else
534 {
535 vtmpTileTexCoords.reserve(n: (size_t)pTMap->m_Width * pTMap->m_Height);
536 vtmpBorderTopTilesTexCoords.reserve(n: (size_t)pTMap->m_Width);
537 vtmpBorderBottomTilesTexCoords.reserve(n: (size_t)pTMap->m_Width);
538 vtmpBorderLeftTilesTexCoords.reserve(n: (size_t)pTMap->m_Height);
539 vtmpBorderRightTilesTexCoords.reserve(n: (size_t)pTMap->m_Height);
540 vtmpBorderCornersTexCoords.reserve(n: (size_t)4);
541 }
542
543 int x = 0;
544 int y = 0;
545 for(y = 0; y < pTMap->m_Height; ++y)
546 {
547 for(x = 0; x < pTMap->m_Width; ++x)
548 {
549 unsigned char Index = 0;
550 unsigned char Flags = 0;
551 int AngleRotate = -1;
552 if(IsEntityLayer)
553 {
554 if(IsGameLayer)
555 {
556 Index = ((CTile *)pTiles)[y * pTMap->m_Width + x].m_Index;
557 Flags = ((CTile *)pTiles)[y * pTMap->m_Width + x].m_Flags;
558 }
559 if(IsFrontLayer)
560 {
561 Index = ((CTile *)pTiles)[y * pTMap->m_Width + x].m_Index;
562 Flags = ((CTile *)pTiles)[y * pTMap->m_Width + x].m_Flags;
563 }
564 if(IsSwitchLayer)
565 {
566 Flags = 0;
567 Index = ((CSwitchTile *)pTiles)[y * pTMap->m_Width + x].m_Type;
568 if(CurOverlay == 0)
569 {
570 Flags = ((CSwitchTile *)pTiles)[y * pTMap->m_Width + x].m_Flags;
571 if(Index == TILE_SWITCHTIMEDOPEN)
572 Index = 8;
573 }
574 else if(CurOverlay == 1)
575 Index = ((CSwitchTile *)pTiles)[y * pTMap->m_Width + x].m_Number;
576 else if(CurOverlay == 2)
577 Index = ((CSwitchTile *)pTiles)[y * pTMap->m_Width + x].m_Delay;
578 }
579 if(IsTeleLayer)
580 {
581 Index = ((CTeleTile *)pTiles)[y * pTMap->m_Width + x].m_Type;
582 Flags = 0;
583 if(CurOverlay == 1)
584 {
585 if(IsTeleTileNumberUsedAny(Index))
586 Index = ((CTeleTile *)pTiles)[y * pTMap->m_Width + x].m_Number;
587 else
588 Index = 0;
589 }
590 }
591 if(IsSpeedupLayer)
592 {
593 Index = ((CSpeedupTile *)pTiles)[y * pTMap->m_Width + x].m_Type;
594 Flags = 0;
595 AngleRotate = ((CSpeedupTile *)pTiles)[y * pTMap->m_Width + x].m_Angle;
596 if(((CSpeedupTile *)pTiles)[y * pTMap->m_Width + x].m_Force == 0)
597 Index = 0;
598 else if(CurOverlay == 1)
599 Index = ((CSpeedupTile *)pTiles)[y * pTMap->m_Width + x].m_Force;
600 else if(CurOverlay == 2)
601 Index = ((CSpeedupTile *)pTiles)[y * pTMap->m_Width + x].m_MaxSpeed;
602 }
603 if(IsTuneLayer)
604 {
605 Index = ((CTuneTile *)pTiles)[y * pTMap->m_Width + x].m_Type;
606 Flags = 0;
607 }
608 }
609 else
610 {
611 Index = ((CTile *)pTiles)[y * pTMap->m_Width + x].m_Index;
612 Flags = ((CTile *)pTiles)[y * pTMap->m_Width + x].m_Flags;
613 }
614
615 //the amount of tiles handled before this tile
616 int TilesHandledCount = vtmpTiles.size();
617 Visuals.m_pTilesOfLayer[y * pTMap->m_Width + x].SetIndexBufferByteOffset((offset_ptr32)(TilesHandledCount));
618
619 bool AddAsSpeedup = false;
620 if(IsSpeedupLayer && CurOverlay == 0)
621 AddAsSpeedup = true;
622
623 if(AddTile(vTmpTiles&: vtmpTiles, vTmpTileTexCoords&: vtmpTileTexCoords, Index, Flags, x, y, pGroup, DoTextureCoords, FillSpeedup: AddAsSpeedup, AngleRotate))
624 Visuals.m_pTilesOfLayer[y * pTMap->m_Width + x].Draw(SetDraw: true);
625
626 //do the border tiles
627 if(x == 0)
628 {
629 if(y == 0)
630 {
631 Visuals.m_BorderTopLeft.SetIndexBufferByteOffset((offset_ptr32)(vtmpBorderCorners.size()));
632 if(AddTile(vTmpTiles&: vtmpBorderCorners, vTmpTileTexCoords&: vtmpBorderCornersTexCoords, Index, Flags, x: 0, y: 0, pGroup, DoTextureCoords, FillSpeedup: AddAsSpeedup, AngleRotate, Offset: ivec2{-32, -32}))
633 Visuals.m_BorderTopLeft.Draw(SetDraw: true);
634 }
635 else if(y == pTMap->m_Height - 1)
636 {
637 Visuals.m_BorderBottomLeft.SetIndexBufferByteOffset((offset_ptr32)(vtmpBorderCorners.size()));
638 if(AddTile(vTmpTiles&: vtmpBorderCorners, vTmpTileTexCoords&: vtmpBorderCornersTexCoords, Index, Flags, x: 0, y: 0, pGroup, DoTextureCoords, FillSpeedup: AddAsSpeedup, AngleRotate, Offset: ivec2{-32, 0}))
639 Visuals.m_BorderBottomLeft.Draw(SetDraw: true);
640 }
641 Visuals.m_vBorderLeft[y].SetIndexBufferByteOffset((offset_ptr32)(vtmpBorderLeftTiles.size()));
642 if(AddTile(vTmpTiles&: vtmpBorderLeftTiles, vTmpTileTexCoords&: vtmpBorderLeftTilesTexCoords, Index, Flags, x: 0, y, pGroup, DoTextureCoords, FillSpeedup: AddAsSpeedup, AngleRotate, Offset: ivec2{-32, 0}))
643 Visuals.m_vBorderLeft[y].Draw(SetDraw: true);
644 }
645 else if(x == pTMap->m_Width - 1)
646 {
647 if(y == 0)
648 {
649 Visuals.m_BorderTopRight.SetIndexBufferByteOffset((offset_ptr32)(vtmpBorderCorners.size()));
650 if(AddTile(vTmpTiles&: vtmpBorderCorners, vTmpTileTexCoords&: vtmpBorderCornersTexCoords, Index, Flags, x: 0, y: 0, pGroup, DoTextureCoords, FillSpeedup: AddAsSpeedup, AngleRotate, Offset: ivec2{0, -32}))
651 Visuals.m_BorderTopRight.Draw(SetDraw: true);
652 }
653 else if(y == pTMap->m_Height - 1)
654 {
655 Visuals.m_BorderBottomRight.SetIndexBufferByteOffset((offset_ptr32)(vtmpBorderCorners.size()));
656 if(AddTile(vTmpTiles&: vtmpBorderCorners, vTmpTileTexCoords&: vtmpBorderCornersTexCoords, Index, Flags, x: 0, y: 0, pGroup, DoTextureCoords, FillSpeedup: AddAsSpeedup, AngleRotate, Offset: ivec2{0, 0}))
657 Visuals.m_BorderBottomRight.Draw(SetDraw: true);
658 }
659 Visuals.m_vBorderRight[y].SetIndexBufferByteOffset((offset_ptr32)(vtmpBorderRightTiles.size()));
660 if(AddTile(vTmpTiles&: vtmpBorderRightTiles, vTmpTileTexCoords&: vtmpBorderRightTilesTexCoords, Index, Flags, x: 0, y, pGroup, DoTextureCoords, FillSpeedup: AddAsSpeedup, AngleRotate, Offset: ivec2{0, 0}))
661 Visuals.m_vBorderRight[y].Draw(SetDraw: true);
662 }
663 if(y == 0)
664 {
665 Visuals.m_vBorderTop[x].SetIndexBufferByteOffset((offset_ptr32)(vtmpBorderTopTiles.size()));
666 if(AddTile(vTmpTiles&: vtmpBorderTopTiles, vTmpTileTexCoords&: vtmpBorderTopTilesTexCoords, Index, Flags, x, y: 0, pGroup, DoTextureCoords, FillSpeedup: AddAsSpeedup, AngleRotate, Offset: ivec2{0, -32}))
667 Visuals.m_vBorderTop[x].Draw(SetDraw: true);
668 }
669 else if(y == pTMap->m_Height - 1)
670 {
671 Visuals.m_vBorderBottom[x].SetIndexBufferByteOffset((offset_ptr32)(vtmpBorderBottomTiles.size()));
672 if(AddTile(vTmpTiles&: vtmpBorderBottomTiles, vTmpTileTexCoords&: vtmpBorderBottomTilesTexCoords, Index, Flags, x, y: 0, pGroup, DoTextureCoords, FillSpeedup: AddAsSpeedup, AngleRotate, Offset: ivec2{0, 0}))
673 Visuals.m_vBorderBottom[x].Draw(SetDraw: true);
674 }
675 }
676 }
677
678 //append one kill tile to the gamelayer
679 if(IsGameLayer)
680 {
681 Visuals.m_BorderKillTile.SetIndexBufferByteOffset((offset_ptr32)(vtmpTiles.size()));
682 if(AddTile(vTmpTiles&: vtmpTiles, vTmpTileTexCoords&: vtmpTileTexCoords, Index: TILE_DEATH, Flags: 0, x: 0, y: 0, pGroup, DoTextureCoords))
683 Visuals.m_BorderKillTile.Draw(SetDraw: true);
684 }
685
686 //add the border corners, then the borders and fix their byte offsets
687 int TilesHandledCount = vtmpTiles.size();
688 Visuals.m_BorderTopLeft.AddIndexBufferByteOffset(IndexBufferByteOff: TilesHandledCount);
689 Visuals.m_BorderTopRight.AddIndexBufferByteOffset(IndexBufferByteOff: TilesHandledCount);
690 Visuals.m_BorderBottomLeft.AddIndexBufferByteOffset(IndexBufferByteOff: TilesHandledCount);
691 Visuals.m_BorderBottomRight.AddIndexBufferByteOffset(IndexBufferByteOff: TilesHandledCount);
692 //add the Corners to the tiles
693 vtmpTiles.insert(position: vtmpTiles.end(), first: vtmpBorderCorners.begin(), last: vtmpBorderCorners.end());
694 vtmpTileTexCoords.insert(position: vtmpTileTexCoords.end(), first: vtmpBorderCornersTexCoords.begin(), last: vtmpBorderCornersTexCoords.end());
695
696 //now the borders
697 TilesHandledCount = vtmpTiles.size();
698 if(pTMap->m_Width > 0)
699 {
700 for(int i = 0; i < pTMap->m_Width; ++i)
701 {
702 Visuals.m_vBorderTop[i].AddIndexBufferByteOffset(IndexBufferByteOff: TilesHandledCount);
703 }
704 }
705 vtmpTiles.insert(position: vtmpTiles.end(), first: vtmpBorderTopTiles.begin(), last: vtmpBorderTopTiles.end());
706 vtmpTileTexCoords.insert(position: vtmpTileTexCoords.end(), first: vtmpBorderTopTilesTexCoords.begin(), last: vtmpBorderTopTilesTexCoords.end());
707
708 TilesHandledCount = vtmpTiles.size();
709 if(pTMap->m_Width > 0)
710 {
711 for(int i = 0; i < pTMap->m_Width; ++i)
712 {
713 Visuals.m_vBorderBottom[i].AddIndexBufferByteOffset(IndexBufferByteOff: TilesHandledCount);
714 }
715 }
716 vtmpTiles.insert(position: vtmpTiles.end(), first: vtmpBorderBottomTiles.begin(), last: vtmpBorderBottomTiles.end());
717 vtmpTileTexCoords.insert(position: vtmpTileTexCoords.end(), first: vtmpBorderBottomTilesTexCoords.begin(), last: vtmpBorderBottomTilesTexCoords.end());
718
719 TilesHandledCount = vtmpTiles.size();
720 if(pTMap->m_Height > 0)
721 {
722 for(int i = 0; i < pTMap->m_Height; ++i)
723 {
724 Visuals.m_vBorderLeft[i].AddIndexBufferByteOffset(IndexBufferByteOff: TilesHandledCount);
725 }
726 }
727 vtmpTiles.insert(position: vtmpTiles.end(), first: vtmpBorderLeftTiles.begin(), last: vtmpBorderLeftTiles.end());
728 vtmpTileTexCoords.insert(position: vtmpTileTexCoords.end(), first: vtmpBorderLeftTilesTexCoords.begin(), last: vtmpBorderLeftTilesTexCoords.end());
729
730 TilesHandledCount = vtmpTiles.size();
731 if(pTMap->m_Height > 0)
732 {
733 for(int i = 0; i < pTMap->m_Height; ++i)
734 {
735 Visuals.m_vBorderRight[i].AddIndexBufferByteOffset(IndexBufferByteOff: TilesHandledCount);
736 }
737 }
738 vtmpTiles.insert(position: vtmpTiles.end(), first: vtmpBorderRightTiles.begin(), last: vtmpBorderRightTiles.end());
739 vtmpTileTexCoords.insert(position: vtmpTileTexCoords.end(), first: vtmpBorderRightTilesTexCoords.begin(), last: vtmpBorderRightTilesTexCoords.end());
740
741 //setup params
742 float *pTmpTiles = vtmpTiles.empty() ? NULL : (float *)vtmpTiles.data();
743 unsigned char *pTmpTileTexCoords = vtmpTileTexCoords.empty() ? NULL : (unsigned char *)vtmpTileTexCoords.data();
744
745 Visuals.m_BufferContainerIndex = -1;
746 size_t UploadDataSize = vtmpTileTexCoords.size() * sizeof(SGraphicTileTexureCoords) + vtmpTiles.size() * sizeof(SGraphicTile);
747 if(UploadDataSize > 0)
748 {
749 char *pUploadData = (char *)malloc(size: sizeof(char) * UploadDataSize);
750
751 mem_copy_special(pDest: pUploadData, pSource: pTmpTiles, Size: sizeof(vec2), Count: vtmpTiles.size() * 4, Steps: (DoTextureCoords ? sizeof(ubvec4) : 0));
752 if(DoTextureCoords)
753 {
754 mem_copy_special(pDest: pUploadData + sizeof(vec2), pSource: pTmpTileTexCoords, Size: sizeof(ubvec4), Count: vtmpTiles.size() * 4, Steps: sizeof(vec2));
755 }
756
757 // first create the buffer object
758 int BufferObjectIndex = Graphics()->CreateBufferObject(UploadDataSize, pUploadData, CreateFlags: 0, IsMovedPointer: true);
759
760 // then create the buffer container
761 SBufferContainerInfo ContainerInfo;
762 ContainerInfo.m_Stride = (DoTextureCoords ? (sizeof(float) * 2 + sizeof(ubvec4)) : 0);
763 ContainerInfo.m_VertBufferBindingIndex = BufferObjectIndex;
764 ContainerInfo.m_vAttributes.emplace_back();
765 SBufferContainerInfo::SAttribute *pAttr = &ContainerInfo.m_vAttributes.back();
766 pAttr->m_DataTypeCount = 2;
767 pAttr->m_Type = GRAPHICS_TYPE_FLOAT;
768 pAttr->m_Normalized = false;
769 pAttr->m_pOffset = 0;
770 pAttr->m_FuncType = 0;
771 if(DoTextureCoords)
772 {
773 ContainerInfo.m_vAttributes.emplace_back();
774 pAttr = &ContainerInfo.m_vAttributes.back();
775 pAttr->m_DataTypeCount = 4;
776 pAttr->m_Type = GRAPHICS_TYPE_UNSIGNED_BYTE;
777 pAttr->m_Normalized = false;
778 pAttr->m_pOffset = (void *)(sizeof(vec2));
779 pAttr->m_FuncType = 1;
780 }
781
782 Visuals.m_BufferContainerIndex = Graphics()->CreateBufferContainer(pContainerInfo: &ContainerInfo);
783 // and finally inform the backend how many indices are required
784 Graphics()->IndicesNumRequiredNotify(RequiredIndicesCount: vtmpTiles.size() * 6);
785
786 RenderLoading();
787 }
788
789 ++CurOverlay;
790 }
791 }
792 }
793 else if(pLayer->m_Type == LAYERTYPE_QUADS && Graphics()->IsQuadBufferingEnabled())
794 {
795 CMapItemLayerQuads *pQLayer = (CMapItemLayerQuads *)pLayer;
796
797 m_vpQuadLayerVisuals.push_back(x: new SQuadLayerVisuals());
798 SQuadLayerVisuals *pQLayerVisuals = m_vpQuadLayerVisuals.back();
799
800 bool Textured = (pQLayer->m_Image != -1);
801
802 vtmpQuads.clear();
803 vtmpQuadsTextured.clear();
804
805 if(Textured)
806 vtmpQuadsTextured.resize(new_size: pQLayer->m_NumQuads);
807 else
808 vtmpQuads.resize(new_size: pQLayer->m_NumQuads);
809
810 CQuad *pQuads = (CQuad *)m_pLayers->Map()->GetDataSwapped(Index: pQLayer->m_Data);
811 for(int i = 0; i < pQLayer->m_NumQuads; ++i)
812 {
813 CQuad *pQuad = &pQuads[i];
814 for(int j = 0; j < 4; ++j)
815 {
816 int QuadIdX = j;
817 if(j == 2)
818 QuadIdX = 3;
819 else if(j == 3)
820 QuadIdX = 2;
821 if(!Textured)
822 {
823 // ignore the conversion for the position coordinates
824 vtmpQuads[i].m_aVertices[j].m_X = (pQuad->m_aPoints[QuadIdX].x);
825 vtmpQuads[i].m_aVertices[j].m_Y = (pQuad->m_aPoints[QuadIdX].y);
826 vtmpQuads[i].m_aVertices[j].m_CenterX = (pQuad->m_aPoints[4].x);
827 vtmpQuads[i].m_aVertices[j].m_CenterY = (pQuad->m_aPoints[4].y);
828 vtmpQuads[i].m_aVertices[j].m_R = (unsigned char)pQuad->m_aColors[QuadIdX].r;
829 vtmpQuads[i].m_aVertices[j].m_G = (unsigned char)pQuad->m_aColors[QuadIdX].g;
830 vtmpQuads[i].m_aVertices[j].m_B = (unsigned char)pQuad->m_aColors[QuadIdX].b;
831 vtmpQuads[i].m_aVertices[j].m_A = (unsigned char)pQuad->m_aColors[QuadIdX].a;
832 }
833 else
834 {
835 // ignore the conversion for the position coordinates
836 vtmpQuadsTextured[i].m_aVertices[j].m_X = (pQuad->m_aPoints[QuadIdX].x);
837 vtmpQuadsTextured[i].m_aVertices[j].m_Y = (pQuad->m_aPoints[QuadIdX].y);
838 vtmpQuadsTextured[i].m_aVertices[j].m_CenterX = (pQuad->m_aPoints[4].x);
839 vtmpQuadsTextured[i].m_aVertices[j].m_CenterY = (pQuad->m_aPoints[4].y);
840 vtmpQuadsTextured[i].m_aVertices[j].m_U = fx2f(v: pQuad->m_aTexcoords[QuadIdX].x);
841 vtmpQuadsTextured[i].m_aVertices[j].m_V = fx2f(v: pQuad->m_aTexcoords[QuadIdX].y);
842 vtmpQuadsTextured[i].m_aVertices[j].m_R = (unsigned char)pQuad->m_aColors[QuadIdX].r;
843 vtmpQuadsTextured[i].m_aVertices[j].m_G = (unsigned char)pQuad->m_aColors[QuadIdX].g;
844 vtmpQuadsTextured[i].m_aVertices[j].m_B = (unsigned char)pQuad->m_aColors[QuadIdX].b;
845 vtmpQuadsTextured[i].m_aVertices[j].m_A = (unsigned char)pQuad->m_aColors[QuadIdX].a;
846 }
847 }
848 }
849
850 size_t UploadDataSize = 0;
851 if(Textured)
852 UploadDataSize = vtmpQuadsTextured.size() * sizeof(STmpQuadTextured);
853 else
854 UploadDataSize = vtmpQuads.size() * sizeof(STmpQuad);
855
856 if(UploadDataSize > 0)
857 {
858 void *pUploadData = NULL;
859 if(Textured)
860 pUploadData = vtmpQuadsTextured.data();
861 else
862 pUploadData = vtmpQuads.data();
863 // create the buffer object
864 int BufferObjectIndex = Graphics()->CreateBufferObject(UploadDataSize, pUploadData, CreateFlags: 0);
865 // then create the buffer container
866 SBufferContainerInfo ContainerInfo;
867 ContainerInfo.m_Stride = (Textured ? (sizeof(STmpQuadTextured) / 4) : (sizeof(STmpQuad) / 4));
868 ContainerInfo.m_VertBufferBindingIndex = BufferObjectIndex;
869 ContainerInfo.m_vAttributes.emplace_back();
870 SBufferContainerInfo::SAttribute *pAttr = &ContainerInfo.m_vAttributes.back();
871 pAttr->m_DataTypeCount = 4;
872 pAttr->m_Type = GRAPHICS_TYPE_FLOAT;
873 pAttr->m_Normalized = false;
874 pAttr->m_pOffset = 0;
875 pAttr->m_FuncType = 0;
876 ContainerInfo.m_vAttributes.emplace_back();
877 pAttr = &ContainerInfo.m_vAttributes.back();
878 pAttr->m_DataTypeCount = 4;
879 pAttr->m_Type = GRAPHICS_TYPE_UNSIGNED_BYTE;
880 pAttr->m_Normalized = true;
881 pAttr->m_pOffset = (void *)(sizeof(float) * 4);
882 pAttr->m_FuncType = 0;
883 if(Textured)
884 {
885 ContainerInfo.m_vAttributes.emplace_back();
886 pAttr = &ContainerInfo.m_vAttributes.back();
887 pAttr->m_DataTypeCount = 2;
888 pAttr->m_Type = GRAPHICS_TYPE_FLOAT;
889 pAttr->m_Normalized = false;
890 pAttr->m_pOffset = (void *)(sizeof(float) * 4 + sizeof(unsigned char) * 4);
891 pAttr->m_FuncType = 0;
892 }
893
894 pQLayerVisuals->m_BufferContainerIndex = Graphics()->CreateBufferContainer(pContainerInfo: &ContainerInfo);
895 // and finally inform the backend how many indices are required
896 Graphics()->IndicesNumRequiredNotify(RequiredIndicesCount: pQLayer->m_NumQuads * 6);
897
898 RenderLoading();
899 }
900 }
901 }
902 }
903}
904
905void CMapLayers::RenderTileLayer(int LayerIndex, const ColorRGBA &Color, CMapItemLayerTilemap *pTileLayer, CMapItemGroup *pGroup)
906{
907 STileLayerVisuals &Visuals = *m_vpTileLayerVisuals[LayerIndex];
908 if(Visuals.m_BufferContainerIndex == -1)
909 return; //no visuals were created
910
911 float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
912 Graphics()->GetScreen(pTopLeftX: &ScreenX0, pTopLeftY: &ScreenY0, pBottomRightX: &ScreenX1, pBottomRightY: &ScreenY1);
913
914 int BorderX0, BorderY0, BorderX1, BorderY1;
915 bool DrawBorder = false;
916
917 int Y0 = BorderY0 = std::floor(x: ScreenY0 / 32);
918 int X0 = BorderX0 = std::floor(x: ScreenX0 / 32);
919 int Y1 = BorderY1 = std::ceil(x: ScreenY1 / 32);
920 int X1 = BorderX1 = std::ceil(x: ScreenX1 / 32);
921
922 if(X0 < 0)
923 {
924 X0 = 0;
925 DrawBorder = true;
926 }
927 if(Y0 < 0)
928 {
929 Y0 = 0;
930 DrawBorder = true;
931 }
932 if(X1 > pTileLayer->m_Width)
933 {
934 X1 = pTileLayer->m_Width;
935 DrawBorder = true;
936 }
937 if(Y1 > pTileLayer->m_Height)
938 {
939 Y1 = pTileLayer->m_Height;
940 DrawBorder = true;
941 }
942
943 bool DrawLayer = true;
944 if(X1 <= 0)
945 DrawLayer = false;
946 if(Y1 <= 0)
947 DrawLayer = false;
948 if(X0 >= pTileLayer->m_Width)
949 DrawLayer = false;
950 if(Y0 >= pTileLayer->m_Height)
951 DrawLayer = false;
952
953 if(DrawLayer)
954 {
955 //create the indice buffers we want to draw -- reuse them
956 static std::vector<char *> s_vpIndexOffsets;
957 static std::vector<unsigned int> s_vDrawCounts;
958
959 s_vpIndexOffsets.clear();
960 s_vDrawCounts.clear();
961
962 unsigned long long Reserve = absolute(a: Y1 - Y0) + 1;
963 s_vpIndexOffsets.reserve(n: Reserve);
964 s_vDrawCounts.reserve(n: Reserve);
965
966 for(int y = Y0; y < Y1; ++y)
967 {
968 if(X0 > X1)
969 continue;
970 int XR = X1 - 1;
971
972 dbg_assert(Visuals.m_pTilesOfLayer[y * pTileLayer->m_Width + XR].IndexBufferByteOffset() >= Visuals.m_pTilesOfLayer[y * pTileLayer->m_Width + X0].IndexBufferByteOffset(), "Tile count wrong.");
973
974 unsigned int NumVertices = ((Visuals.m_pTilesOfLayer[y * pTileLayer->m_Width + XR].IndexBufferByteOffset() - Visuals.m_pTilesOfLayer[y * pTileLayer->m_Width + X0].IndexBufferByteOffset()) / sizeof(unsigned int)) + (Visuals.m_pTilesOfLayer[y * pTileLayer->m_Width + XR].DoDraw() ? 6lu : 0lu);
975
976 if(NumVertices)
977 {
978 s_vpIndexOffsets.push_back(x: (offset_ptr_size)Visuals.m_pTilesOfLayer[y * pTileLayer->m_Width + X0].IndexBufferByteOffset());
979 s_vDrawCounts.push_back(x: NumVertices);
980 }
981 }
982
983 int DrawCount = s_vpIndexOffsets.size();
984 if(DrawCount != 0)
985 {
986 Graphics()->RenderTileLayer(BufferContainerIndex: Visuals.m_BufferContainerIndex, Color, pOffsets: s_vpIndexOffsets.data(), pIndicedVertexDrawNum: s_vDrawCounts.data(), NumIndicesOffset: DrawCount);
987 }
988 }
989
990 if(DrawBorder)
991 RenderTileBorder(LayerIndex, Color, pTileLayer, pGroup, BorderX0, BorderY0, BorderX1, BorderY1);
992}
993
994void CMapLayers::RenderTileBorder(int LayerIndex, const ColorRGBA &Color, CMapItemLayerTilemap *pTileLayer, CMapItemGroup *pGroup, int BorderX0, int BorderY0, int BorderX1, int BorderY1)
995{
996 STileLayerVisuals &Visuals = *m_vpTileLayerVisuals[LayerIndex];
997
998 int Y0 = BorderY0;
999 int X0 = BorderX0;
1000 int Y1 = BorderY1;
1001 int X1 = BorderX1;
1002
1003 if(X0 < 0)
1004 X0 = 0;
1005 if(Y0 < 0)
1006 Y0 = 0;
1007 if(X1 > pTileLayer->m_Width)
1008 X1 = pTileLayer->m_Width;
1009 if(Y1 > pTileLayer->m_Height)
1010 Y1 = pTileLayer->m_Height;
1011
1012 // corners
1013 if(BorderX0 < 0)
1014 {
1015 // Draw corners on left side
1016 if(BorderY0 < 0)
1017 {
1018 if(Visuals.m_BorderTopLeft.DoDraw())
1019 {
1020 vec2 Offset;
1021 Offset.x = 0;
1022 Offset.y = 0;
1023 vec2 Scale;
1024 Scale.x = absolute(a: BorderX0);
1025 Scale.y = absolute(a: BorderY0);
1026
1027 Graphics()->RenderBorderTiles(BufferContainerIndex: Visuals.m_BufferContainerIndex, Color, pIndexBufferOffset: (offset_ptr_size)Visuals.m_BorderTopLeft.IndexBufferByteOffset(), Offset, Scale, DrawNum: 1);
1028 }
1029 }
1030 if(BorderY1 > pTileLayer->m_Height)
1031 {
1032 if(Visuals.m_BorderBottomLeft.DoDraw())
1033 {
1034 vec2 Offset;
1035 Offset.x = 0;
1036 Offset.y = pTileLayer->m_Height * 32.0f;
1037 vec2 Scale;
1038 Scale.x = absolute(a: BorderX0);
1039 Scale.y = BorderY1 - pTileLayer->m_Height;
1040
1041 Graphics()->RenderBorderTiles(BufferContainerIndex: Visuals.m_BufferContainerIndex, Color, pIndexBufferOffset: (offset_ptr_size)Visuals.m_BorderBottomLeft.IndexBufferByteOffset(), Offset, Scale, DrawNum: 1);
1042 }
1043 }
1044 }
1045 if(BorderX1 > pTileLayer->m_Width)
1046 {
1047 // Draw corners on right side
1048 if(BorderY0 < 0)
1049 {
1050 if(Visuals.m_BorderTopRight.DoDraw())
1051 {
1052 vec2 Offset;
1053 Offset.x = pTileLayer->m_Width * 32.0f;
1054 Offset.y = 0;
1055 vec2 Scale;
1056 Scale.x = BorderX1 - pTileLayer->m_Width;
1057 Scale.y = absolute(a: BorderY0);
1058
1059 Graphics()->RenderBorderTiles(BufferContainerIndex: Visuals.m_BufferContainerIndex, Color, pIndexBufferOffset: (offset_ptr_size)Visuals.m_BorderTopRight.IndexBufferByteOffset(), Offset, Scale, DrawNum: 1);
1060 }
1061 }
1062 if(BorderY1 > pTileLayer->m_Height)
1063 {
1064 if(Visuals.m_BorderBottomRight.DoDraw())
1065 {
1066 vec2 Offset;
1067 Offset.x = pTileLayer->m_Width * 32.0f;
1068 Offset.y = pTileLayer->m_Height * 32.0f;
1069 vec2 Scale;
1070 Scale.x = BorderX1 - pTileLayer->m_Width;
1071 Scale.y = BorderY1 - pTileLayer->m_Height;
1072
1073 Graphics()->RenderBorderTiles(BufferContainerIndex: Visuals.m_BufferContainerIndex, Color, pIndexBufferOffset: (offset_ptr_size)Visuals.m_BorderBottomRight.IndexBufferByteOffset(), Offset, Scale, DrawNum: 1);
1074 }
1075 }
1076 }
1077
1078 if(BorderX1 > pTileLayer->m_Width)
1079 {
1080 // Draw right border
1081 if(Y0 < pTileLayer->m_Height && Y1 > 0)
1082 {
1083 int YB = Y1 - 1;
1084 unsigned int DrawNum = ((Visuals.m_vBorderRight[YB].IndexBufferByteOffset() - Visuals.m_vBorderRight[Y0].IndexBufferByteOffset()) / (sizeof(unsigned int) * 6)) + (Visuals.m_vBorderRight[YB].DoDraw() ? 1lu : 0lu);
1085 offset_ptr_size pOffset = (offset_ptr_size)Visuals.m_vBorderRight[Y0].IndexBufferByteOffset();
1086 vec2 Offset;
1087 Offset.x = 32.f * pTileLayer->m_Width;
1088 Offset.y = 0.f;
1089 vec2 Scale;
1090 Scale.x = BorderX1 - pTileLayer->m_Width;
1091 Scale.y = 1.f;
1092 Graphics()->RenderBorderTiles(BufferContainerIndex: Visuals.m_BufferContainerIndex, Color, pIndexBufferOffset: pOffset, Offset, Scale, DrawNum);
1093 }
1094 }
1095 if(BorderX0 < 0)
1096 {
1097 // Draw left border
1098 if(Y0 < pTileLayer->m_Height && Y1 > 0)
1099 {
1100 int YB = Y1 - 1;
1101 unsigned int DrawNum = ((Visuals.m_vBorderLeft[YB].IndexBufferByteOffset() - Visuals.m_vBorderLeft[Y0].IndexBufferByteOffset()) / (sizeof(unsigned int) * 6)) + (Visuals.m_vBorderLeft[YB].DoDraw() ? 1lu : 0lu);
1102 offset_ptr_size pOffset = (offset_ptr_size)Visuals.m_vBorderLeft[Y0].IndexBufferByteOffset();
1103 vec2 Offset;
1104 Offset.x = 0;
1105 Offset.y = 0;
1106 vec2 Scale;
1107 Scale.x = absolute(a: BorderX0);
1108 Scale.y = 1;
1109 Graphics()->RenderBorderTiles(BufferContainerIndex: Visuals.m_BufferContainerIndex, Color, pIndexBufferOffset: pOffset, Offset, Scale, DrawNum);
1110 }
1111 }
1112 if(BorderY0 < 0)
1113 {
1114 // Draw top border
1115 if(X0 < pTileLayer->m_Width && X1 > 0)
1116 {
1117 int XR = X1 - 1;
1118 unsigned int DrawNum = ((Visuals.m_vBorderTop[XR].IndexBufferByteOffset() - Visuals.m_vBorderTop[X0].IndexBufferByteOffset()) / (sizeof(unsigned int) * 6)) + (Visuals.m_vBorderTop[XR].DoDraw() ? 1lu : 0lu);
1119 offset_ptr_size pOffset = (offset_ptr_size)Visuals.m_vBorderTop[X0].IndexBufferByteOffset();
1120 vec2 Offset;
1121 Offset.x = 0.f;
1122 Offset.y = 0;
1123 vec2 Scale;
1124 Scale.x = 1;
1125 Scale.y = absolute(a: BorderY0);
1126 Graphics()->RenderBorderTiles(BufferContainerIndex: Visuals.m_BufferContainerIndex, Color, pIndexBufferOffset: pOffset, Offset, Scale, DrawNum);
1127 }
1128 }
1129 if(BorderY1 > pTileLayer->m_Height)
1130 {
1131 // Draw bottom border
1132 if(X0 < pTileLayer->m_Width && X1 > 0)
1133 {
1134 int XR = X1 - 1;
1135 unsigned int DrawNum = ((Visuals.m_vBorderBottom[XR].IndexBufferByteOffset() - Visuals.m_vBorderBottom[X0].IndexBufferByteOffset()) / (sizeof(unsigned int) * 6)) + (Visuals.m_vBorderBottom[XR].DoDraw() ? 1lu : 0lu);
1136 offset_ptr_size pOffset = (offset_ptr_size)Visuals.m_vBorderBottom[X0].IndexBufferByteOffset();
1137 vec2 Offset;
1138 Offset.x = 0.f;
1139 Offset.y = 32.f * pTileLayer->m_Height;
1140 vec2 Scale;
1141 Scale.x = 1;
1142 Scale.y = BorderY1 - pTileLayer->m_Height;
1143 Graphics()->RenderBorderTiles(BufferContainerIndex: Visuals.m_BufferContainerIndex, Color, pIndexBufferOffset: pOffset, Offset, Scale, DrawNum);
1144 }
1145 }
1146}
1147
1148void CMapLayers::RenderKillTileBorder(int LayerIndex, const ColorRGBA &Color, CMapItemLayerTilemap *pTileLayer, CMapItemGroup *pGroup)
1149{
1150 STileLayerVisuals &Visuals = *m_vpTileLayerVisuals[LayerIndex];
1151 if(Visuals.m_BufferContainerIndex == -1)
1152 return; //no visuals were created
1153
1154 float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
1155 Graphics()->GetScreen(pTopLeftX: &ScreenX0, pTopLeftY: &ScreenY0, pBottomRightX: &ScreenX1, pBottomRightY: &ScreenY1);
1156
1157 bool DrawBorder = false;
1158
1159 int BorderY0 = std::floor(x: ScreenY0 / 32);
1160 int BorderX0 = std::floor(x: ScreenX0 / 32);
1161 int BorderY1 = std::ceil(x: ScreenY1 / 32);
1162 int BorderX1 = std::ceil(x: ScreenX1 / 32);
1163
1164 if(BorderX0 < -201)
1165 DrawBorder = true;
1166 if(BorderY0 < -201)
1167 DrawBorder = true;
1168 if(BorderX1 > pTileLayer->m_Width + 201)
1169 DrawBorder = true;
1170 if(BorderY1 > pTileLayer->m_Height + 201)
1171 DrawBorder = true;
1172
1173 if(!DrawBorder)
1174 return;
1175 if(!Visuals.m_BorderKillTile.DoDraw())
1176 return;
1177
1178 if(BorderX0 < -300)
1179 BorderX0 = -300;
1180 if(BorderY0 < -300)
1181 BorderY0 = -300;
1182 if(BorderX1 >= pTileLayer->m_Width + 300)
1183 BorderX1 = pTileLayer->m_Width + 299;
1184 if(BorderY1 >= pTileLayer->m_Height + 300)
1185 BorderY1 = pTileLayer->m_Height + 299;
1186
1187 if(BorderX1 < -300)
1188 BorderX1 = -300;
1189 if(BorderY1 < -300)
1190 BorderY1 = -300;
1191 if(BorderX0 >= pTileLayer->m_Width + 300)
1192 BorderX0 = pTileLayer->m_Width + 299;
1193 if(BorderY0 >= pTileLayer->m_Height + 300)
1194 BorderY0 = pTileLayer->m_Height + 299;
1195
1196 // Draw left kill tile border
1197 if(BorderX0 < -201)
1198 {
1199 unsigned int DrawNum = 1;
1200 offset_ptr_size pOffset = (offset_ptr_size)Visuals.m_BorderKillTile.IndexBufferByteOffset();
1201 vec2 Offset;
1202 Offset.x = 32.f * BorderX0;
1203 Offset.y = 32.f * BorderY0;
1204 vec2 Scale;
1205 Scale.x = -201 - BorderX0;
1206 Scale.y = BorderY1 - BorderY0;
1207 Graphics()->RenderBorderTiles(BufferContainerIndex: Visuals.m_BufferContainerIndex, Color, pIndexBufferOffset: pOffset, Offset, Scale, DrawNum);
1208 }
1209 // Draw top kill tile border
1210 if(BorderY0 < -201)
1211 {
1212 unsigned int DrawNum = 1;
1213 offset_ptr_size pOffset = (offset_ptr_size)Visuals.m_BorderKillTile.IndexBufferByteOffset();
1214 vec2 Offset;
1215 Offset.x = maximum(a: BorderX0, b: -201) * 32.0f;
1216 Offset.y = 32.f * BorderY0;
1217 vec2 Scale;
1218 Scale.x = minimum(a: BorderX1, b: pTileLayer->m_Width + 201) - maximum(a: BorderX0, b: -201);
1219 Scale.y = -201 - BorderY0;
1220 Graphics()->RenderBorderTiles(BufferContainerIndex: Visuals.m_BufferContainerIndex, Color, pIndexBufferOffset: pOffset, Offset, Scale, DrawNum);
1221 }
1222 // Draw right kill tile border
1223 if(BorderX1 > pTileLayer->m_Width + 201)
1224 {
1225 unsigned int DrawNum = 1;
1226 offset_ptr_size pOffset = (offset_ptr_size)Visuals.m_BorderKillTile.IndexBufferByteOffset();
1227 vec2 Offset;
1228 Offset.x = 32.0f * (pTileLayer->m_Width + 201);
1229 Offset.y = 32.0f * BorderY0;
1230 vec2 Scale;
1231 Scale.x = BorderX1 - (pTileLayer->m_Width + 201);
1232 Scale.y = BorderY1 - BorderY0;
1233 Graphics()->RenderBorderTiles(BufferContainerIndex: Visuals.m_BufferContainerIndex, Color, pIndexBufferOffset: pOffset, Offset, Scale, DrawNum);
1234 }
1235 // Draw bottom kill tile border
1236 if(BorderY1 > pTileLayer->m_Height + 201)
1237 {
1238 unsigned int DrawNum = 1;
1239 offset_ptr_size pOffset = (offset_ptr_size)Visuals.m_BorderKillTile.IndexBufferByteOffset();
1240 vec2 Offset;
1241 Offset.x = maximum(a: BorderX0, b: -201) * 32.0f;
1242 Offset.y = 32.0f * (pTileLayer->m_Height + 201);
1243 vec2 Scale;
1244 Scale.x = minimum(a: BorderX1, b: pTileLayer->m_Width + 201) - maximum(a: BorderX0, b: -201);
1245 Scale.y = BorderY1 - (pTileLayer->m_Height + 201);
1246 Graphics()->RenderBorderTiles(BufferContainerIndex: Visuals.m_BufferContainerIndex, Color, pIndexBufferOffset: pOffset, Offset, Scale, DrawNum);
1247 }
1248}
1249
1250void CMapLayers::RenderQuadLayer(int LayerIndex, CMapItemLayerQuads *pQuadLayer, CMapItemGroup *pGroup, bool Force)
1251{
1252 SQuadLayerVisuals &Visuals = *m_vpQuadLayerVisuals[LayerIndex];
1253 if(Visuals.m_BufferContainerIndex == -1)
1254 return; //no visuals were created
1255
1256 if(!Force && (!g_Config.m_ClShowQuads || g_Config.m_ClOverlayEntities == 100))
1257 return;
1258
1259 CQuad *pQuads = (CQuad *)m_pLayers->Map()->GetDataSwapped(Index: pQuadLayer->m_Data);
1260
1261 static std::vector<SQuadRenderInfo> s_vQuadRenderInfo;
1262
1263 s_vQuadRenderInfo.resize(new_size: pQuadLayer->m_NumQuads);
1264 size_t QuadsRenderCount = 0;
1265 size_t CurQuadOffset = 0;
1266 for(int i = 0; i < pQuadLayer->m_NumQuads; ++i)
1267 {
1268 CQuad *pQuad = &pQuads[i];
1269
1270 ColorRGBA Color = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
1271 EnvelopeEval(TimeOffsetMillis: pQuad->m_ColorEnvOffset, Env: pQuad->m_ColorEnv, Result&: Color, Channels: 4, pUser: this);
1272
1273 const bool IsFullyTransparent = Color.a <= 0.0f;
1274 const bool NeedsFlush = QuadsRenderCount == gs_GraphicsMaxQuadsRenderCount || IsFullyTransparent;
1275
1276 if(NeedsFlush)
1277 {
1278 // render quads of the current offset directly(cancel batching)
1279 Graphics()->RenderQuadLayer(BufferContainerIndex: Visuals.m_BufferContainerIndex, pQuadInfo: s_vQuadRenderInfo.data(), QuadNum: QuadsRenderCount, QuadOffset: CurQuadOffset);
1280 QuadsRenderCount = 0;
1281 CurQuadOffset = i;
1282 if(IsFullyTransparent)
1283 {
1284 // since this quad is ignored, the offset is the next quad
1285 ++CurQuadOffset;
1286 }
1287 }
1288
1289 if(!IsFullyTransparent)
1290 {
1291 ColorRGBA Position = ColorRGBA(0.0f, 0.0f, 0.0f, 0.0f);
1292 EnvelopeEval(TimeOffsetMillis: pQuad->m_PosEnvOffset, Env: pQuad->m_PosEnv, Result&: Position, Channels: 3, pUser: this);
1293
1294 SQuadRenderInfo &QInfo = s_vQuadRenderInfo[QuadsRenderCount++];
1295 QInfo.m_Color = Color;
1296 QInfo.m_Offsets.x = Position.r;
1297 QInfo.m_Offsets.y = Position.g;
1298 QInfo.m_Rotation = Position.b / 180.0f * pi;
1299 }
1300 }
1301 Graphics()->RenderQuadLayer(BufferContainerIndex: Visuals.m_BufferContainerIndex, pQuadInfo: s_vQuadRenderInfo.data(), QuadNum: QuadsRenderCount, QuadOffset: CurQuadOffset);
1302}
1303
1304void CMapLayers::LayersOfGroupCount(CMapItemGroup *pGroup, int &TileLayerCount, int &QuadLayerCount, bool &PassedGameLayer)
1305{
1306 int TileLayerCounter = 0;
1307 int QuadLayerCounter = 0;
1308 for(int l = 0; l < pGroup->m_NumLayers; l++)
1309 {
1310 CMapItemLayer *pLayer = m_pLayers->GetLayer(Index: pGroup->m_StartLayer + l);
1311 bool IsFrontLayer = false;
1312 bool IsSwitchLayer = false;
1313 bool IsTeleLayer = false;
1314 bool IsSpeedupLayer = false;
1315 bool IsTuneLayer = false;
1316
1317 if(pLayer == (CMapItemLayer *)m_pLayers->GameLayer())
1318 {
1319 PassedGameLayer = true;
1320 }
1321
1322 if(pLayer == (CMapItemLayer *)m_pLayers->FrontLayer())
1323 IsFrontLayer = true;
1324
1325 if(pLayer == (CMapItemLayer *)m_pLayers->SwitchLayer())
1326 IsSwitchLayer = true;
1327
1328 if(pLayer == (CMapItemLayer *)m_pLayers->TeleLayer())
1329 IsTeleLayer = true;
1330
1331 if(pLayer == (CMapItemLayer *)m_pLayers->SpeedupLayer())
1332 IsSpeedupLayer = true;
1333
1334 if(pLayer == (CMapItemLayer *)m_pLayers->TuneLayer())
1335 IsTuneLayer = true;
1336
1337 if(m_Type <= TYPE_BACKGROUND_FORCE)
1338 {
1339 if(PassedGameLayer)
1340 break;
1341 }
1342 else if(m_Type == TYPE_FOREGROUND)
1343 {
1344 if(!PassedGameLayer)
1345 continue;
1346 }
1347
1348 if(pLayer->m_Type == LAYERTYPE_TILES)
1349 {
1350 CMapItemLayerTilemap *pTMap = (CMapItemLayerTilemap *)pLayer;
1351 int DataIndex = 0;
1352 unsigned int TileSize = 0;
1353 int TileLayerAndOverlayCount = 0;
1354 if(IsFrontLayer)
1355 {
1356 DataIndex = pTMap->m_Front;
1357 TileSize = sizeof(CTile);
1358 TileLayerAndOverlayCount = 1;
1359 }
1360 else if(IsSwitchLayer)
1361 {
1362 DataIndex = pTMap->m_Switch;
1363 TileSize = sizeof(CSwitchTile);
1364 TileLayerAndOverlayCount = 3;
1365 }
1366 else if(IsTeleLayer)
1367 {
1368 DataIndex = pTMap->m_Tele;
1369 TileSize = sizeof(CTeleTile);
1370 TileLayerAndOverlayCount = 2;
1371 }
1372 else if(IsSpeedupLayer)
1373 {
1374 DataIndex = pTMap->m_Speedup;
1375 TileSize = sizeof(CSpeedupTile);
1376 TileLayerAndOverlayCount = 3;
1377 }
1378 else if(IsTuneLayer)
1379 {
1380 DataIndex = pTMap->m_Tune;
1381 TileSize = sizeof(CTuneTile);
1382 TileLayerAndOverlayCount = 1;
1383 }
1384 else
1385 {
1386 DataIndex = pTMap->m_Data;
1387 TileSize = sizeof(CTile);
1388 TileLayerAndOverlayCount = 1;
1389 }
1390
1391 unsigned int Size = m_pLayers->Map()->GetDataSize(Index: DataIndex);
1392 if(Size >= pTMap->m_Width * pTMap->m_Height * TileSize)
1393 {
1394 TileLayerCounter += TileLayerAndOverlayCount;
1395 }
1396 }
1397 else if(pLayer->m_Type == LAYERTYPE_QUADS)
1398 {
1399 ++QuadLayerCounter;
1400 }
1401 }
1402
1403 TileLayerCount += TileLayerCounter;
1404 QuadLayerCount += QuadLayerCounter;
1405}
1406
1407void CMapLayers::OnRender()
1408{
1409 if(m_OnlineOnly && Client()->State() != IClient::STATE_ONLINE && Client()->State() != IClient::STATE_DEMOPLAYBACK)
1410 return;
1411
1412 CUIRect Screen;
1413 Graphics()->GetScreen(pTopLeftX: &Screen.x, pTopLeftY: &Screen.y, pBottomRightX: &Screen.w, pBottomRightY: &Screen.h);
1414
1415 vec2 Center = GetCurCamera()->m_Center;
1416
1417 bool PassedGameLayer = false;
1418 int TileLayerCounter = 0;
1419 int QuadLayerCounter = 0;
1420
1421 for(int g = 0; g < m_pLayers->NumGroups(); g++)
1422 {
1423 CMapItemGroup *pGroup = m_pLayers->GetGroup(Index: g);
1424
1425 if(!pGroup)
1426 {
1427 dbg_msg(sys: "maplayers", fmt: "error group was null, group number = %d, total groups = %d", g, m_pLayers->NumGroups());
1428 dbg_msg(sys: "maplayers", fmt: "this is here to prevent a crash but the source of this is unknown, please report this for it to get fixed");
1429 dbg_msg(sys: "maplayers", fmt: "we need mapname and crc and the map that caused this if possible, and anymore info you think is relevant");
1430 continue;
1431 }
1432
1433 if((!g_Config.m_GfxNoclip || m_Type == TYPE_FULL_DESIGN) && pGroup->m_Version >= 2 && pGroup->m_UseClipping)
1434 {
1435 // set clipping
1436 float aPoints[4];
1437 RenderTools()->MapScreenToGroup(CenterX: Center.x, CenterY: Center.y, pGroup: m_pLayers->GameGroup(), Zoom: GetCurCamera()->m_Zoom);
1438 Graphics()->GetScreen(pTopLeftX: &aPoints[0], pTopLeftY: &aPoints[1], pBottomRightX: &aPoints[2], pBottomRightY: &aPoints[3]);
1439 float x0 = (pGroup->m_ClipX - aPoints[0]) / (aPoints[2] - aPoints[0]);
1440 float y0 = (pGroup->m_ClipY - aPoints[1]) / (aPoints[3] - aPoints[1]);
1441 float x1 = ((pGroup->m_ClipX + pGroup->m_ClipW) - aPoints[0]) / (aPoints[2] - aPoints[0]);
1442 float y1 = ((pGroup->m_ClipY + pGroup->m_ClipH) - aPoints[1]) / (aPoints[3] - aPoints[1]);
1443
1444 if(x1 < 0.0f || x0 > 1.0f || y1 < 0.0f || y0 > 1.0f)
1445 {
1446 //check tile layer count of this group
1447 LayersOfGroupCount(pGroup, TileLayerCount&: TileLayerCounter, QuadLayerCount&: QuadLayerCounter, PassedGameLayer);
1448 continue;
1449 }
1450
1451 Graphics()->ClipEnable(x: (int)(x0 * Graphics()->ScreenWidth()), y: (int)(y0 * Graphics()->ScreenHeight()),
1452 w: (int)((x1 - x0) * Graphics()->ScreenWidth()), h: (int)((y1 - y0) * Graphics()->ScreenHeight()));
1453 }
1454
1455 RenderTools()->MapScreenToGroup(CenterX: Center.x, CenterY: Center.y, pGroup, Zoom: GetCurCamera()->m_Zoom);
1456
1457 for(int l = 0; l < pGroup->m_NumLayers; l++)
1458 {
1459 CMapItemLayer *pLayer = m_pLayers->GetLayer(Index: pGroup->m_StartLayer + l);
1460 bool Render = false;
1461 bool IsGameLayer = false;
1462 bool IsFrontLayer = false;
1463 bool IsSwitchLayer = false;
1464 bool IsTeleLayer = false;
1465 bool IsSpeedupLayer = false;
1466 bool IsTuneLayer = false;
1467 bool IsEntityLayer = false;
1468
1469 if(pLayer == (CMapItemLayer *)m_pLayers->GameLayer())
1470 {
1471 IsEntityLayer = IsGameLayer = true;
1472 PassedGameLayer = true;
1473 }
1474
1475 if(pLayer == (CMapItemLayer *)m_pLayers->FrontLayer())
1476 IsEntityLayer = IsFrontLayer = true;
1477
1478 if(pLayer == (CMapItemLayer *)m_pLayers->SwitchLayer())
1479 IsEntityLayer = IsSwitchLayer = true;
1480
1481 if(pLayer == (CMapItemLayer *)m_pLayers->TeleLayer())
1482 IsEntityLayer = IsTeleLayer = true;
1483
1484 if(pLayer == (CMapItemLayer *)m_pLayers->SpeedupLayer())
1485 IsEntityLayer = IsSpeedupLayer = true;
1486
1487 if(pLayer == (CMapItemLayer *)m_pLayers->TuneLayer())
1488 IsEntityLayer = IsTuneLayer = true;
1489
1490 if(m_Type == -1)
1491 Render = true;
1492 else if(m_Type <= TYPE_BACKGROUND_FORCE)
1493 {
1494 if(PassedGameLayer)
1495 return;
1496 Render = true;
1497
1498 if(m_Type == TYPE_BACKGROUND_FORCE)
1499 {
1500 if(pLayer->m_Type == LAYERTYPE_TILES && !g_Config.m_ClBackgroundShowTilesLayers)
1501 continue;
1502 }
1503 }
1504 else if(m_Type == TYPE_FOREGROUND)
1505 {
1506 if(PassedGameLayer && !IsGameLayer)
1507 Render = true;
1508 }
1509 else if(m_Type == TYPE_FULL_DESIGN)
1510 {
1511 if(!IsGameLayer)
1512 Render = true;
1513 }
1514
1515 if(Render && pLayer->m_Type == LAYERTYPE_TILES && Input()->ModifierIsPressed() && Input()->ShiftIsPressed() && Input()->KeyPress(Key: KEY_KP_0))
1516 {
1517 CMapItemLayerTilemap *pTMap = (CMapItemLayerTilemap *)pLayer;
1518 CTile *pTiles = (CTile *)m_pLayers->Map()->GetData(Index: pTMap->m_Data);
1519 CServerInfo CurrentServerInfo;
1520 Client()->GetServerInfo(pServerInfo: &CurrentServerInfo);
1521 char aFilename[IO_MAX_PATH_LENGTH];
1522 str_format(buffer: aFilename, buffer_size: sizeof(aFilename), format: "dumps/tilelayer_dump_%s-%d-%d-%dx%d.txt", CurrentServerInfo.m_aMap, g, l, pTMap->m_Width, pTMap->m_Height);
1523 IOHANDLE File = Storage()->OpenFile(pFilename: aFilename, Flags: IOFLAG_WRITE, Type: IStorage::TYPE_SAVE);
1524 if(File)
1525 {
1526 for(int y = 0; y < pTMap->m_Height; y++)
1527 {
1528 for(int x = 0; x < pTMap->m_Width; x++)
1529 io_write(io: File, buffer: &(pTiles[y * pTMap->m_Width + x].m_Index), size: sizeof(pTiles[y * pTMap->m_Width + x].m_Index));
1530 io_write_newline(io: File);
1531 }
1532 io_close(io: File);
1533 }
1534 }
1535
1536 if((Render || IsGameLayer) && pLayer->m_Type == LAYERTYPE_TILES)
1537 {
1538 CMapItemLayerTilemap *pTMap = (CMapItemLayerTilemap *)pLayer;
1539 int DataIndex = 0;
1540 unsigned int TileSize = 0;
1541 int TileLayerAndOverlayCount = 0;
1542 if(IsFrontLayer)
1543 {
1544 DataIndex = pTMap->m_Front;
1545 TileSize = sizeof(CTile);
1546 TileLayerAndOverlayCount = 1;
1547 }
1548 else if(IsSwitchLayer)
1549 {
1550 DataIndex = pTMap->m_Switch;
1551 TileSize = sizeof(CSwitchTile);
1552 TileLayerAndOverlayCount = 3;
1553 }
1554 else if(IsTeleLayer)
1555 {
1556 DataIndex = pTMap->m_Tele;
1557 TileSize = sizeof(CTeleTile);
1558 TileLayerAndOverlayCount = 2;
1559 }
1560 else if(IsSpeedupLayer)
1561 {
1562 DataIndex = pTMap->m_Speedup;
1563 TileSize = sizeof(CSpeedupTile);
1564 TileLayerAndOverlayCount = 3;
1565 }
1566 else if(IsTuneLayer)
1567 {
1568 DataIndex = pTMap->m_Tune;
1569 TileSize = sizeof(CTuneTile);
1570 TileLayerAndOverlayCount = 1;
1571 }
1572 else
1573 {
1574 DataIndex = pTMap->m_Data;
1575 TileSize = sizeof(CTile);
1576 TileLayerAndOverlayCount = 1;
1577 }
1578
1579 unsigned int Size = m_pLayers->Map()->GetDataSize(Index: DataIndex);
1580 if(Size >= pTMap->m_Width * pTMap->m_Height * TileSize)
1581 {
1582 TileLayerCounter += TileLayerAndOverlayCount;
1583 }
1584 }
1585 else if(Render && pLayer->m_Type == LAYERTYPE_QUADS)
1586 {
1587 ++QuadLayerCounter;
1588 }
1589
1590 // skip rendering if detail layers if not wanted, or is entity layer and we are a background map
1591 if((pLayer->m_Flags & LAYERFLAG_DETAIL && (!g_Config.m_GfxHighDetail && !(m_Type == TYPE_FULL_DESIGN)) && !IsGameLayer) || (m_Type == TYPE_BACKGROUND_FORCE && IsEntityLayer) || (m_Type == TYPE_FULL_DESIGN && IsEntityLayer))
1592 continue;
1593
1594 int EntityOverlayVal = g_Config.m_ClOverlayEntities;
1595 if(m_Type == TYPE_FULL_DESIGN)
1596 EntityOverlayVal = 0;
1597
1598 if((Render && EntityOverlayVal < 100 && !IsGameLayer && !IsFrontLayer && !IsSwitchLayer && !IsTeleLayer && !IsSpeedupLayer && !IsTuneLayer) || (EntityOverlayVal && IsGameLayer) || (m_Type == TYPE_BACKGROUND_FORCE))
1599 {
1600 if(pLayer->m_Type == LAYERTYPE_TILES)
1601 {
1602 CMapItemLayerTilemap *pTMap = (CMapItemLayerTilemap *)pLayer;
1603 if(pTMap->m_Image < 0 || pTMap->m_Image >= m_pImages->Num())
1604 {
1605 if(!IsGameLayer)
1606 Graphics()->TextureClear();
1607 else
1608 Graphics()->TextureSet(Texture: m_pImages->GetEntities(EntityLayerType: MAP_IMAGE_ENTITY_LAYER_TYPE_ALL_EXCEPT_SWITCH));
1609 }
1610 else
1611 Graphics()->TextureSet(Texture: m_pImages->Get(Index: pTMap->m_Image));
1612
1613 CTile *pTiles = (CTile *)m_pLayers->Map()->GetData(Index: pTMap->m_Data);
1614 unsigned int Size = m_pLayers->Map()->GetDataSize(Index: pTMap->m_Data);
1615
1616 if(Size >= (size_t)pTMap->m_Width * pTMap->m_Height * sizeof(CTile))
1617 {
1618 ColorRGBA Color = IsGameLayer ? ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f) : ColorRGBA(pTMap->m_Color.r / 255.0f, pTMap->m_Color.g / 255.0f, pTMap->m_Color.b / 255.0f, pTMap->m_Color.a / 255.0f);
1619 if(IsGameLayer && EntityOverlayVal)
1620 Color.a *= EntityOverlayVal / 100.0f;
1621 else if(!IsGameLayer && EntityOverlayVal && m_Type != TYPE_BACKGROUND_FORCE)
1622 Color.a *= (100 - EntityOverlayVal) / 100.0f;
1623
1624 if(!IsGameLayer)
1625 {
1626 ColorRGBA ColorEnv = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
1627 EnvelopeEval(TimeOffsetMillis: pTMap->m_ColorEnvOffset, Env: pTMap->m_ColorEnv, Result&: ColorEnv, Channels: 4, pUser: this);
1628 Color = Color.Multiply(Other: ColorEnv);
1629 }
1630
1631 if(!Graphics()->IsTileBufferingEnabled())
1632 {
1633 Graphics()->BlendNone();
1634 RenderTools()->RenderTilemap(pTiles, w: pTMap->m_Width, h: pTMap->m_Height, Scale: 32.0f, Color, RenderFlags: TILERENDERFLAG_EXTEND | LAYERRENDERFLAG_OPAQUE);
1635 Graphics()->BlendNormal();
1636
1637 // draw kill tiles outside the entity clipping rectangle
1638 if(IsGameLayer)
1639 {
1640 // slow blinking to hint that it's not a part of the map
1641 double Seconds = time_get() / (double)time_freq();
1642 ColorRGBA ColorHint = ColorRGBA(1.0f, 1.0f, 1.0f, 0.3 + 0.7 * (1 + std::sin(x: 2 * (double)pi * Seconds / 3)) / 2);
1643 RenderTools()->RenderTileRectangle(RectX: -201, RectY: -201, RectW: pTMap->m_Width + 402, RectH: pTMap->m_Height + 402,
1644 IndexIn: 0, IndexOut: TILE_DEATH, // display air inside, death outside
1645 Scale: 32.0f, Color: Color.Multiply(Other: ColorHint), RenderFlags: TILERENDERFLAG_EXTEND | LAYERRENDERFLAG_TRANSPARENT);
1646 }
1647
1648 RenderTools()->RenderTilemap(pTiles, w: pTMap->m_Width, h: pTMap->m_Height, Scale: 32.0f, Color, RenderFlags: TILERENDERFLAG_EXTEND | LAYERRENDERFLAG_TRANSPARENT);
1649 }
1650 else
1651 {
1652 Graphics()->BlendNormal();
1653 // draw kill tiles outside the entity clipping rectangle
1654 if(IsGameLayer)
1655 {
1656 // slow blinking to hint that it's not a part of the map
1657 double Seconds = time_get() / (double)time_freq();
1658 ColorRGBA ColorHint = ColorRGBA(1.0f, 1.0f, 1.0f, 0.3 + 0.7 * (1.0 + std::sin(x: 2 * (double)pi * Seconds / 3)) / 2);
1659 RenderKillTileBorder(LayerIndex: TileLayerCounter - 1, Color: Color.Multiply(Other: ColorHint), pTileLayer: pTMap, pGroup);
1660 }
1661 RenderTileLayer(LayerIndex: TileLayerCounter - 1, Color, pTileLayer: pTMap, pGroup);
1662 }
1663 }
1664 }
1665 else if(pLayer->m_Type == LAYERTYPE_QUADS)
1666 {
1667 CMapItemLayerQuads *pQLayer = (CMapItemLayerQuads *)pLayer;
1668 if(pQLayer->m_Image < 0 || pQLayer->m_Image >= m_pImages->Num())
1669 Graphics()->TextureClear();
1670 else
1671 Graphics()->TextureSet(Texture: m_pImages->Get(Index: pQLayer->m_Image));
1672
1673 CQuad *pQuads = (CQuad *)m_pLayers->Map()->GetDataSwapped(Index: pQLayer->m_Data);
1674 if(m_Type == TYPE_BACKGROUND_FORCE || m_Type == TYPE_FULL_DESIGN)
1675 {
1676 if(g_Config.m_ClShowQuads || m_Type == TYPE_FULL_DESIGN)
1677 {
1678 if(!Graphics()->IsQuadBufferingEnabled())
1679 {
1680 Graphics()->BlendNormal();
1681 RenderTools()->ForceRenderQuads(pQuads, NumQuads: pQLayer->m_NumQuads, Flags: LAYERRENDERFLAG_TRANSPARENT, pfnEval: EnvelopeEval, pUser: this, Alpha: 1.f);
1682 }
1683 else
1684 {
1685 RenderQuadLayer(LayerIndex: QuadLayerCounter - 1, pQuadLayer: pQLayer, pGroup, Force: true);
1686 }
1687 }
1688 }
1689 else
1690 {
1691 if(!Graphics()->IsQuadBufferingEnabled())
1692 {
1693 Graphics()->BlendNormal();
1694 RenderTools()->RenderQuads(pQuads, NumQuads: pQLayer->m_NumQuads, Flags: LAYERRENDERFLAG_TRANSPARENT, pfnEval: EnvelopeEval, pUser: this);
1695 }
1696 else
1697 {
1698 RenderQuadLayer(LayerIndex: QuadLayerCounter - 1, pQuadLayer: pQLayer, pGroup, Force: false);
1699 }
1700 }
1701 }
1702 }
1703 else if(Render && EntityOverlayVal && IsFrontLayer)
1704 {
1705 CMapItemLayerTilemap *pTMap = (CMapItemLayerTilemap *)pLayer;
1706 Graphics()->TextureSet(Texture: m_pImages->GetEntities(EntityLayerType: MAP_IMAGE_ENTITY_LAYER_TYPE_ALL_EXCEPT_SWITCH));
1707
1708 CTile *pFrontTiles = (CTile *)m_pLayers->Map()->GetData(Index: pTMap->m_Front);
1709 unsigned int Size = m_pLayers->Map()->GetDataSize(Index: pTMap->m_Front);
1710
1711 if(Size >= (size_t)pTMap->m_Width * pTMap->m_Height * sizeof(CTile))
1712 {
1713 const ColorRGBA Color = ColorRGBA(1.0f, 1.0f, 1.0f, EntityOverlayVal / 100.0f);
1714 if(!Graphics()->IsTileBufferingEnabled())
1715 {
1716 Graphics()->BlendNone();
1717 RenderTools()->RenderTilemap(pTiles: pFrontTiles, w: pTMap->m_Width, h: pTMap->m_Height, Scale: 32.0f, Color, RenderFlags: TILERENDERFLAG_EXTEND | LAYERRENDERFLAG_OPAQUE);
1718 Graphics()->BlendNormal();
1719 RenderTools()->RenderTilemap(pTiles: pFrontTiles, w: pTMap->m_Width, h: pTMap->m_Height, Scale: 32.0f, Color, RenderFlags: TILERENDERFLAG_EXTEND | LAYERRENDERFLAG_TRANSPARENT);
1720 }
1721 else
1722 {
1723 Graphics()->BlendNormal();
1724 RenderTileLayer(LayerIndex: TileLayerCounter - 1, Color, pTileLayer: pTMap, pGroup);
1725 }
1726 }
1727 }
1728 else if(Render && EntityOverlayVal && IsSwitchLayer)
1729 {
1730 CMapItemLayerTilemap *pTMap = (CMapItemLayerTilemap *)pLayer;
1731 Graphics()->TextureSet(Texture: m_pImages->GetEntities(EntityLayerType: MAP_IMAGE_ENTITY_LAYER_TYPE_SWITCH));
1732
1733 CSwitchTile *pSwitchTiles = (CSwitchTile *)m_pLayers->Map()->GetData(Index: pTMap->m_Switch);
1734 unsigned int Size = m_pLayers->Map()->GetDataSize(Index: pTMap->m_Switch);
1735
1736 if(Size >= (size_t)pTMap->m_Width * pTMap->m_Height * sizeof(CSwitchTile))
1737 {
1738 const ColorRGBA Color = ColorRGBA(1.0f, 1.0f, 1.0f, EntityOverlayVal / 100.0f);
1739 if(!Graphics()->IsTileBufferingEnabled())
1740 {
1741 Graphics()->BlendNone();
1742 RenderTools()->RenderSwitchmap(pSwitch: pSwitchTiles, w: pTMap->m_Width, h: pTMap->m_Height, Scale: 32.0f, Color, RenderFlags: TILERENDERFLAG_EXTEND | LAYERRENDERFLAG_OPAQUE);
1743 Graphics()->BlendNormal();
1744 RenderTools()->RenderSwitchmap(pSwitch: pSwitchTiles, w: pTMap->m_Width, h: pTMap->m_Height, Scale: 32.0f, Color, RenderFlags: TILERENDERFLAG_EXTEND | LAYERRENDERFLAG_TRANSPARENT);
1745 RenderTools()->RenderSwitchOverlay(pSwitch: pSwitchTiles, w: pTMap->m_Width, h: pTMap->m_Height, Scale: 32.0f, Alpha: EntityOverlayVal / 100.0f);
1746 }
1747 else
1748 {
1749 Graphics()->BlendNormal();
1750 RenderTileLayer(LayerIndex: TileLayerCounter - 3, Color, pTileLayer: pTMap, pGroup);
1751 if(g_Config.m_ClTextEntities)
1752 {
1753 Graphics()->TextureSet(Texture: m_pImages->GetOverlayBottom());
1754 RenderTileLayer(LayerIndex: TileLayerCounter - 2, Color, pTileLayer: pTMap, pGroup);
1755 Graphics()->TextureSet(Texture: m_pImages->GetOverlayTop());
1756 RenderTileLayer(LayerIndex: TileLayerCounter - 1, Color, pTileLayer: pTMap, pGroup);
1757 }
1758 }
1759 }
1760 }
1761 else if(Render && EntityOverlayVal && IsTeleLayer)
1762 {
1763 CMapItemLayerTilemap *pTMap = (CMapItemLayerTilemap *)pLayer;
1764 Graphics()->TextureSet(Texture: m_pImages->GetEntities(EntityLayerType: MAP_IMAGE_ENTITY_LAYER_TYPE_ALL_EXCEPT_SWITCH));
1765
1766 CTeleTile *pTeleTiles = (CTeleTile *)m_pLayers->Map()->GetData(Index: pTMap->m_Tele);
1767 unsigned int Size = m_pLayers->Map()->GetDataSize(Index: pTMap->m_Tele);
1768
1769 if(Size >= (size_t)pTMap->m_Width * pTMap->m_Height * sizeof(CTeleTile))
1770 {
1771 const ColorRGBA Color = ColorRGBA(1.0f, 1.0f, 1.0f, EntityOverlayVal / 100.0f);
1772 if(!Graphics()->IsTileBufferingEnabled())
1773 {
1774 Graphics()->BlendNone();
1775 RenderTools()->RenderTelemap(pTele: pTeleTiles, w: pTMap->m_Width, h: pTMap->m_Height, Scale: 32.0f, Color, RenderFlags: TILERENDERFLAG_EXTEND | LAYERRENDERFLAG_OPAQUE);
1776 Graphics()->BlendNormal();
1777 RenderTools()->RenderTelemap(pTele: pTeleTiles, w: pTMap->m_Width, h: pTMap->m_Height, Scale: 32.0f, Color, RenderFlags: TILERENDERFLAG_EXTEND | LAYERRENDERFLAG_TRANSPARENT);
1778 RenderTools()->RenderTeleOverlay(pTele: pTeleTiles, w: pTMap->m_Width, h: pTMap->m_Height, Scale: 32.0f, Alpha: EntityOverlayVal / 100.0f);
1779 }
1780 else
1781 {
1782 Graphics()->BlendNormal();
1783 RenderTileLayer(LayerIndex: TileLayerCounter - 2, Color, pTileLayer: pTMap, pGroup);
1784 if(g_Config.m_ClTextEntities)
1785 {
1786 Graphics()->TextureSet(Texture: m_pImages->GetOverlayCenter());
1787 RenderTileLayer(LayerIndex: TileLayerCounter - 1, Color, pTileLayer: pTMap, pGroup);
1788 }
1789 }
1790 }
1791 }
1792 else if(Render && EntityOverlayVal && IsSpeedupLayer)
1793 {
1794 CMapItemLayerTilemap *pTMap = (CMapItemLayerTilemap *)pLayer;
1795 Graphics()->TextureSet(Texture: m_pImages->GetEntities(EntityLayerType: MAP_IMAGE_ENTITY_LAYER_TYPE_ALL_EXCEPT_SWITCH));
1796
1797 CSpeedupTile *pSpeedupTiles = (CSpeedupTile *)m_pLayers->Map()->GetData(Index: pTMap->m_Speedup);
1798 unsigned int Size = m_pLayers->Map()->GetDataSize(Index: pTMap->m_Speedup);
1799
1800 if(Size >= (size_t)pTMap->m_Width * pTMap->m_Height * sizeof(CSpeedupTile))
1801 {
1802 const ColorRGBA Color = ColorRGBA(1.0f, 1.0f, 1.0f, EntityOverlayVal / 100.0f);
1803 if(!Graphics()->IsTileBufferingEnabled())
1804 {
1805 Graphics()->BlendNone();
1806 RenderTools()->RenderSpeedupmap(pSpeedup: pSpeedupTiles, w: pTMap->m_Width, h: pTMap->m_Height, Scale: 32.0f, Color, RenderFlags: TILERENDERFLAG_EXTEND | LAYERRENDERFLAG_OPAQUE);
1807 Graphics()->BlendNormal();
1808 RenderTools()->RenderSpeedupmap(pSpeedup: pSpeedupTiles, w: pTMap->m_Width, h: pTMap->m_Height, Scale: 32.0f, Color, RenderFlags: TILERENDERFLAG_EXTEND | LAYERRENDERFLAG_TRANSPARENT);
1809 RenderTools()->RenderSpeedupOverlay(pSpeedup: pSpeedupTiles, w: pTMap->m_Width, h: pTMap->m_Height, Scale: 32.0f, Alpha: EntityOverlayVal / 100.0f);
1810 }
1811 else
1812 {
1813 Graphics()->BlendNormal();
1814
1815 // draw arrow -- clamp to the edge of the arrow image
1816 Graphics()->WrapClamp();
1817 Graphics()->TextureSet(Texture: m_pImages->GetSpeedupArrow());
1818 RenderTileLayer(LayerIndex: TileLayerCounter - 3, Color, pTileLayer: pTMap, pGroup);
1819 Graphics()->WrapNormal();
1820
1821 if(g_Config.m_ClTextEntities)
1822 {
1823 Graphics()->TextureSet(Texture: m_pImages->GetOverlayBottom());
1824 RenderTileLayer(LayerIndex: TileLayerCounter - 2, Color, pTileLayer: pTMap, pGroup);
1825 Graphics()->TextureSet(Texture: m_pImages->GetOverlayTop());
1826 RenderTileLayer(LayerIndex: TileLayerCounter - 1, Color, pTileLayer: pTMap, pGroup);
1827 }
1828 }
1829 }
1830 }
1831 else if(Render && EntityOverlayVal && IsTuneLayer)
1832 {
1833 CMapItemLayerTilemap *pTMap = (CMapItemLayerTilemap *)pLayer;
1834 Graphics()->TextureSet(Texture: m_pImages->GetEntities(EntityLayerType: MAP_IMAGE_ENTITY_LAYER_TYPE_ALL_EXCEPT_SWITCH));
1835
1836 CTuneTile *pTuneTiles = (CTuneTile *)m_pLayers->Map()->GetData(Index: pTMap->m_Tune);
1837 unsigned int Size = m_pLayers->Map()->GetDataSize(Index: pTMap->m_Tune);
1838
1839 if(Size >= (size_t)pTMap->m_Width * pTMap->m_Height * sizeof(CTuneTile))
1840 {
1841 const ColorRGBA Color = ColorRGBA(1.0f, 1.0f, 1.0f, EntityOverlayVal / 100.0f);
1842 if(!Graphics()->IsTileBufferingEnabled())
1843 {
1844 Graphics()->BlendNone();
1845 RenderTools()->RenderTunemap(pTune: pTuneTiles, w: pTMap->m_Width, h: pTMap->m_Height, Scale: 32.0f, Color, RenderFlags: TILERENDERFLAG_EXTEND | LAYERRENDERFLAG_OPAQUE);
1846 Graphics()->BlendNormal();
1847 RenderTools()->RenderTunemap(pTune: pTuneTiles, w: pTMap->m_Width, h: pTMap->m_Height, Scale: 32.0f, Color, RenderFlags: TILERENDERFLAG_EXTEND | LAYERRENDERFLAG_TRANSPARENT);
1848 }
1849 else
1850 {
1851 Graphics()->BlendNormal();
1852 RenderTileLayer(LayerIndex: TileLayerCounter - 1, Color, pTileLayer: pTMap, pGroup);
1853 }
1854 }
1855 }
1856 }
1857 if(!g_Config.m_GfxNoclip || m_Type == TYPE_FULL_DESIGN)
1858 Graphics()->ClipDisable();
1859 }
1860
1861 if(!g_Config.m_GfxNoclip || m_Type == TYPE_FULL_DESIGN)
1862 Graphics()->ClipDisable();
1863
1864 // reset the screen like it was before
1865 Graphics()->MapScreen(TopLeftX: Screen.x, TopLeftY: Screen.y, BottomRightX: Screen.w, BottomRightY: Screen.h);
1866}
1867