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 <base/math.h>
4#include <base/system.h>
5#include <base/vmath.h>
6
7#include <antibot/antibot_data.h>
8
9#include <cmath>
10#include <engine/map.h>
11
12#include <game/collision.h>
13#include <game/layers.h>
14#include <game/mapitems.h>
15
16#include <engine/shared/config.h>
17
18vec2 ClampVel(int MoveRestriction, vec2 Vel)
19{
20 if(Vel.x > 0 && (MoveRestriction & CANTMOVE_RIGHT))
21 {
22 Vel.x = 0;
23 }
24 if(Vel.x < 0 && (MoveRestriction & CANTMOVE_LEFT))
25 {
26 Vel.x = 0;
27 }
28 if(Vel.y > 0 && (MoveRestriction & CANTMOVE_DOWN))
29 {
30 Vel.y = 0;
31 }
32 if(Vel.y < 0 && (MoveRestriction & CANTMOVE_UP))
33 {
34 Vel.y = 0;
35 }
36 return Vel;
37}
38
39CCollision::CCollision()
40{
41 m_pDoor = nullptr;
42 Unload();
43}
44
45CCollision::~CCollision()
46{
47 Unload();
48}
49
50void CCollision::Init(class CLayers *pLayers)
51{
52 Unload();
53
54 m_pLayers = pLayers;
55 m_Width = m_pLayers->GameLayer()->m_Width;
56 m_Height = m_pLayers->GameLayer()->m_Height;
57 m_pTiles = static_cast<CTile *>(m_pLayers->Map()->GetData(Index: m_pLayers->GameLayer()->m_Data));
58
59 if(m_pLayers->TeleLayer())
60 {
61 unsigned int Size = m_pLayers->Map()->GetDataSize(Index: m_pLayers->TeleLayer()->m_Tele);
62 if(Size >= (size_t)m_Width * m_Height * sizeof(CTeleTile))
63 m_pTele = static_cast<CTeleTile *>(m_pLayers->Map()->GetData(Index: m_pLayers->TeleLayer()->m_Tele));
64 }
65
66 if(m_pLayers->SpeedupLayer())
67 {
68 unsigned int Size = m_pLayers->Map()->GetDataSize(Index: m_pLayers->SpeedupLayer()->m_Speedup);
69 if(Size >= (size_t)m_Width * m_Height * sizeof(CSpeedupTile))
70 m_pSpeedup = static_cast<CSpeedupTile *>(m_pLayers->Map()->GetData(Index: m_pLayers->SpeedupLayer()->m_Speedup));
71 }
72
73 if(m_pLayers->SwitchLayer())
74 {
75 unsigned int Size = m_pLayers->Map()->GetDataSize(Index: m_pLayers->SwitchLayer()->m_Switch);
76 if(Size >= (size_t)m_Width * m_Height * sizeof(CSwitchTile))
77 m_pSwitch = static_cast<CSwitchTile *>(m_pLayers->Map()->GetData(Index: m_pLayers->SwitchLayer()->m_Switch));
78
79 m_pDoor = new CDoorTile[m_Width * m_Height];
80 mem_zero(block: m_pDoor, size: (size_t)m_Width * m_Height * sizeof(CDoorTile));
81 }
82
83 if(m_pLayers->TuneLayer())
84 {
85 unsigned int Size = m_pLayers->Map()->GetDataSize(Index: m_pLayers->TuneLayer()->m_Tune);
86 if(Size >= (size_t)m_Width * m_Height * sizeof(CTuneTile))
87 m_pTune = static_cast<CTuneTile *>(m_pLayers->Map()->GetData(Index: m_pLayers->TuneLayer()->m_Tune));
88 }
89
90 if(m_pLayers->FrontLayer())
91 {
92 unsigned int Size = m_pLayers->Map()->GetDataSize(Index: m_pLayers->FrontLayer()->m_Front);
93 if(Size >= (size_t)m_Width * m_Height * sizeof(CTile))
94 m_pFront = static_cast<CTile *>(m_pLayers->Map()->GetData(Index: m_pLayers->FrontLayer()->m_Front));
95 }
96
97 for(int i = 0; i < m_Width * m_Height; i++)
98 {
99 int Index;
100 if(m_pSwitch)
101 {
102 if(m_pSwitch[i].m_Number > m_HighestSwitchNumber)
103 m_HighestSwitchNumber = m_pSwitch[i].m_Number;
104
105 if(m_pSwitch[i].m_Number)
106 m_pDoor[i].m_Number = m_pSwitch[i].m_Number;
107 else
108 m_pDoor[i].m_Number = 0;
109
110 Index = m_pSwitch[i].m_Type;
111
112 if(Index <= TILE_NPH_ENABLE)
113 {
114 if((Index >= TILE_JUMP && Index <= TILE_SUBTRACT_TIME) || Index == TILE_ALLOW_TELE_GUN || Index == TILE_ALLOW_BLUE_TELE_GUN)
115 m_pSwitch[i].m_Type = Index;
116 else
117 m_pSwitch[i].m_Type = 0;
118 }
119 }
120 }
121
122 if(m_pTele)
123 {
124 for(int i = 0; i < m_Width * m_Height; i++)
125 {
126 int Number = TeleLayer()[i].m_Number;
127 int Type = TeleLayer()[i].m_Type;
128 if(Number > 0)
129 {
130 if(Type == TILE_TELEIN)
131 {
132 m_TeleIns[Number - 1].emplace_back(args: i % m_Width * 32.0f + 16.0f, args: i / m_Width * 32.0f + 16.0f);
133 }
134 else if(Type == TILE_TELEOUT)
135 {
136 m_TeleOuts[Number - 1].emplace_back(args: i % m_Width * 32.0f + 16.0f, args: i / m_Width * 32.0f + 16.0f);
137 }
138 else if(Type == TILE_TELECHECKOUT)
139 {
140 m_TeleCheckOuts[Number - 1].emplace_back(args: i % m_Width * 32.0f + 16.0f, args: i / m_Width * 32.0f + 16.0f);
141 }
142 else if(Type)
143 {
144 m_TeleOthers[Number - 1].emplace_back(args: i % m_Width * 32.0f + 16.0f, args: i / m_Width * 32.0f + 16.0f);
145 }
146 }
147 }
148 }
149}
150
151void CCollision::Unload()
152{
153 m_pTiles = nullptr;
154 m_Width = 0;
155 m_Height = 0;
156 m_pLayers = nullptr;
157
158 m_HighestSwitchNumber = 0;
159
160 m_TeleIns.clear();
161 m_TeleOuts.clear();
162 m_TeleCheckOuts.clear();
163 m_TeleOthers.clear();
164
165 m_pTele = nullptr;
166 m_pSpeedup = nullptr;
167 m_pFront = nullptr;
168 m_pSwitch = nullptr;
169 m_pTune = nullptr;
170 delete[] m_pDoor;
171 m_pDoor = nullptr;
172}
173
174void CCollision::FillAntibot(CAntibotMapData *pMapData)
175{
176 pMapData->m_Width = m_Width;
177 pMapData->m_Height = m_Height;
178 pMapData->m_pTiles = (unsigned char *)malloc(size: (size_t)m_Width * m_Height);
179 for(int i = 0; i < m_Width * m_Height; i++)
180 {
181 pMapData->m_pTiles[i] = 0;
182 if(m_pTiles[i].m_Index >= TILE_SOLID && m_pTiles[i].m_Index <= TILE_NOLASER)
183 {
184 pMapData->m_pTiles[i] = m_pTiles[i].m_Index;
185 }
186 }
187}
188
189enum
190{
191 MR_DIR_HERE = 0,
192 MR_DIR_RIGHT,
193 MR_DIR_DOWN,
194 MR_DIR_LEFT,
195 MR_DIR_UP,
196 NUM_MR_DIRS
197};
198
199static int GetMoveRestrictionsRaw(int Direction, int Tile, int Flags)
200{
201 Flags = Flags & (TILEFLAG_XFLIP | TILEFLAG_YFLIP | TILEFLAG_ROTATE);
202 switch(Tile)
203 {
204 case TILE_STOP:
205 switch(Flags)
206 {
207 case ROTATION_0: return CANTMOVE_DOWN;
208 case ROTATION_90: return CANTMOVE_LEFT;
209 case ROTATION_180: return CANTMOVE_UP;
210 case ROTATION_270: return CANTMOVE_RIGHT;
211
212 case TILEFLAG_YFLIP ^ ROTATION_0: return CANTMOVE_UP;
213 case TILEFLAG_YFLIP ^ ROTATION_90: return CANTMOVE_RIGHT;
214 case TILEFLAG_YFLIP ^ ROTATION_180: return CANTMOVE_DOWN;
215 case TILEFLAG_YFLIP ^ ROTATION_270: return CANTMOVE_LEFT;
216 }
217 break;
218 case TILE_STOPS:
219 switch(Flags)
220 {
221 case ROTATION_0:
222 case ROTATION_180:
223 case TILEFLAG_YFLIP ^ ROTATION_0:
224 case TILEFLAG_YFLIP ^ ROTATION_180:
225 return CANTMOVE_DOWN | CANTMOVE_UP;
226 case ROTATION_90:
227 case ROTATION_270:
228 case TILEFLAG_YFLIP ^ ROTATION_90:
229 case TILEFLAG_YFLIP ^ ROTATION_270:
230 return CANTMOVE_LEFT | CANTMOVE_RIGHT;
231 }
232 break;
233 case TILE_STOPA:
234 return CANTMOVE_LEFT | CANTMOVE_RIGHT | CANTMOVE_UP | CANTMOVE_DOWN;
235 }
236 return 0;
237}
238
239static int GetMoveRestrictionsMask(int Direction)
240{
241 switch(Direction)
242 {
243 case MR_DIR_HERE: return 0;
244 case MR_DIR_RIGHT: return CANTMOVE_RIGHT;
245 case MR_DIR_DOWN: return CANTMOVE_DOWN;
246 case MR_DIR_LEFT: return CANTMOVE_LEFT;
247 case MR_DIR_UP: return CANTMOVE_UP;
248 default: dbg_assert(false, "invalid dir");
249 }
250 return 0;
251}
252
253static int GetMoveRestrictions(int Direction, int Tile, int Flags)
254{
255 int Result = GetMoveRestrictionsRaw(Direction, Tile, Flags);
256 // Generally, stoppers only have an effect if they block us from moving
257 // *onto* them. The one exception is one-way blockers, they can also
258 // block us from moving if we're on top of them.
259 if(Direction == MR_DIR_HERE && Tile == TILE_STOP)
260 {
261 return Result;
262 }
263 return Result & GetMoveRestrictionsMask(Direction);
264}
265
266int CCollision::GetMoveRestrictions(CALLBACK_SWITCHACTIVE pfnSwitchActive, void *pUser, vec2 Pos, float Distance, int OverrideCenterTileIndex) const
267{
268 static const vec2 DIRECTIONS[NUM_MR_DIRS] =
269 {
270 vec2(0, 0),
271 vec2(1, 0),
272 vec2(0, 1),
273 vec2(-1, 0),
274 vec2(0, -1)};
275 dbg_assert(0.0f <= Distance && Distance <= 32.0f, "invalid distance");
276 int Restrictions = 0;
277 for(int d = 0; d < NUM_MR_DIRS; d++)
278 {
279 vec2 ModPos = Pos + DIRECTIONS[d] * Distance;
280 int ModMapIndex = GetPureMapIndex(Pos: ModPos);
281 if(d == MR_DIR_HERE && OverrideCenterTileIndex >= 0)
282 {
283 ModMapIndex = OverrideCenterTileIndex;
284 }
285 for(int Front = 0; Front < 2; Front++)
286 {
287 int Tile;
288 int Flags;
289 if(!Front)
290 {
291 Tile = GetTileIndex(Index: ModMapIndex);
292 Flags = GetTileFlags(Index: ModMapIndex);
293 }
294 else
295 {
296 Tile = GetFTileIndex(Index: ModMapIndex);
297 Flags = GetFTileFlags(Index: ModMapIndex);
298 }
299 Restrictions |= ::GetMoveRestrictions(Direction: d, Tile, Flags);
300 }
301 if(pfnSwitchActive)
302 {
303 int TeleNumber = GetDTileNumber(Index: ModMapIndex);
304 if(pfnSwitchActive(TeleNumber, pUser))
305 {
306 int Tile = GetDTileIndex(Index: ModMapIndex);
307 int Flags = GetDTileFlags(Index: ModMapIndex);
308 Restrictions |= ::GetMoveRestrictions(Direction: d, Tile, Flags);
309 }
310 }
311 }
312 return Restrictions;
313}
314
315int CCollision::GetTile(int x, int y) const
316{
317 if(!m_pTiles)
318 return 0;
319
320 int Nx = clamp(val: x / 32, lo: 0, hi: m_Width - 1);
321 int Ny = clamp(val: y / 32, lo: 0, hi: m_Height - 1);
322 int pos = Ny * m_Width + Nx;
323
324 if(m_pTiles[pos].m_Index >= TILE_SOLID && m_pTiles[pos].m_Index <= TILE_NOLASER)
325 return m_pTiles[pos].m_Index;
326 return 0;
327}
328
329// TODO: rewrite this smarter!
330int CCollision::IntersectLine(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision) const
331{
332 float Distance = distance(a: Pos0, b: Pos1);
333 int End(Distance + 1);
334 vec2 Last = Pos0;
335 for(int i = 0; i <= End; i++)
336 {
337 float a = i / (float)End;
338 vec2 Pos = mix(a: Pos0, b: Pos1, amount: a);
339 // Temporary position for checking collision
340 int ix = round_to_int(f: Pos.x);
341 int iy = round_to_int(f: Pos.y);
342
343 if(CheckPoint(x: ix, y: iy))
344 {
345 if(pOutCollision)
346 *pOutCollision = Pos;
347 if(pOutBeforeCollision)
348 *pOutBeforeCollision = Last;
349 return GetCollisionAt(x: ix, y: iy);
350 }
351
352 Last = Pos;
353 }
354 if(pOutCollision)
355 *pOutCollision = Pos1;
356 if(pOutBeforeCollision)
357 *pOutBeforeCollision = Pos1;
358 return 0;
359}
360
361int CCollision::IntersectLineTeleHook(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision, int *pTeleNr) const
362{
363 float Distance = distance(a: Pos0, b: Pos1);
364 int End(Distance + 1);
365 vec2 Last = Pos0;
366 int dx = 0, dy = 0; // Offset for checking the "through" tile
367 ThroughOffset(Pos0, Pos1, pOffsetX: &dx, pOffsetY: &dy);
368 for(int i = 0; i <= End; i++)
369 {
370 float a = i / (float)End;
371 vec2 Pos = mix(a: Pos0, b: Pos1, amount: a);
372 // Temporary position for checking collision
373 int ix = round_to_int(f: Pos.x);
374 int iy = round_to_int(f: Pos.y);
375
376 int Index = GetPureMapIndex(Pos);
377 if(pTeleNr)
378 {
379 if(g_Config.m_SvOldTeleportHook)
380 *pTeleNr = IsTeleport(Index);
381 else
382 *pTeleNr = IsTeleportHook(Index);
383 }
384 if(pTeleNr && *pTeleNr)
385 {
386 if(pOutCollision)
387 *pOutCollision = Pos;
388 if(pOutBeforeCollision)
389 *pOutBeforeCollision = Last;
390 return TILE_TELEINHOOK;
391 }
392
393 int hit = 0;
394 if(CheckPoint(x: ix, y: iy))
395 {
396 if(!IsThrough(x: ix, y: iy, xoff: dx, yoff: dy, pos0: Pos0, pos1: Pos1))
397 hit = GetCollisionAt(x: ix, y: iy);
398 }
399 else if(IsHookBlocker(x: ix, y: iy, pos0: Pos0, pos1: Pos1))
400 {
401 hit = TILE_NOHOOK;
402 }
403 if(hit)
404 {
405 if(pOutCollision)
406 *pOutCollision = Pos;
407 if(pOutBeforeCollision)
408 *pOutBeforeCollision = Last;
409 return hit;
410 }
411
412 Last = Pos;
413 }
414 if(pOutCollision)
415 *pOutCollision = Pos1;
416 if(pOutBeforeCollision)
417 *pOutBeforeCollision = Pos1;
418 return 0;
419}
420
421int CCollision::IntersectLineTeleWeapon(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision, int *pTeleNr) const
422{
423 float Distance = distance(a: Pos0, b: Pos1);
424 int End(Distance + 1);
425 vec2 Last = Pos0;
426 for(int i = 0; i <= End; i++)
427 {
428 float a = i / (float)End;
429 vec2 Pos = mix(a: Pos0, b: Pos1, amount: a);
430 // Temporary position for checking collision
431 int ix = round_to_int(f: Pos.x);
432 int iy = round_to_int(f: Pos.y);
433
434 int Index = GetPureMapIndex(Pos);
435 if(pTeleNr)
436 {
437 if(g_Config.m_SvOldTeleportWeapons)
438 *pTeleNr = IsTeleport(Index);
439 else
440 *pTeleNr = IsTeleportWeapon(Index);
441 }
442 if(pTeleNr && *pTeleNr)
443 {
444 if(pOutCollision)
445 *pOutCollision = Pos;
446 if(pOutBeforeCollision)
447 *pOutBeforeCollision = Last;
448 return TILE_TELEINWEAPON;
449 }
450
451 if(CheckPoint(x: ix, y: iy))
452 {
453 if(pOutCollision)
454 *pOutCollision = Pos;
455 if(pOutBeforeCollision)
456 *pOutBeforeCollision = Last;
457 return GetCollisionAt(x: ix, y: iy);
458 }
459
460 Last = Pos;
461 }
462 if(pOutCollision)
463 *pOutCollision = Pos1;
464 if(pOutBeforeCollision)
465 *pOutBeforeCollision = Pos1;
466 return 0;
467}
468
469// TODO: OPT: rewrite this smarter!
470void CCollision::MovePoint(vec2 *pInoutPos, vec2 *pInoutVel, float Elasticity, int *pBounces) const
471{
472 if(pBounces)
473 *pBounces = 0;
474
475 vec2 Pos = *pInoutPos;
476 vec2 Vel = *pInoutVel;
477 if(CheckPoint(Pos: Pos + Vel))
478 {
479 int Affected = 0;
480 if(CheckPoint(x: Pos.x + Vel.x, y: Pos.y))
481 {
482 pInoutVel->x *= -Elasticity;
483 if(pBounces)
484 (*pBounces)++;
485 Affected++;
486 }
487
488 if(CheckPoint(x: Pos.x, y: Pos.y + Vel.y))
489 {
490 pInoutVel->y *= -Elasticity;
491 if(pBounces)
492 (*pBounces)++;
493 Affected++;
494 }
495
496 if(Affected == 0)
497 {
498 pInoutVel->x *= -Elasticity;
499 pInoutVel->y *= -Elasticity;
500 }
501 }
502 else
503 {
504 *pInoutPos = Pos + Vel;
505 }
506}
507
508bool CCollision::TestBox(vec2 Pos, vec2 Size) const
509{
510 Size *= 0.5f;
511 if(CheckPoint(x: Pos.x - Size.x, y: Pos.y - Size.y))
512 return true;
513 if(CheckPoint(x: Pos.x + Size.x, y: Pos.y - Size.y))
514 return true;
515 if(CheckPoint(x: Pos.x - Size.x, y: Pos.y + Size.y))
516 return true;
517 if(CheckPoint(x: Pos.x + Size.x, y: Pos.y + Size.y))
518 return true;
519 return false;
520}
521
522void CCollision::MoveBox(vec2 *pInoutPos, vec2 *pInoutVel, vec2 Size, vec2 Elasticity, bool *pGrounded) const
523{
524 // do the move
525 vec2 Pos = *pInoutPos;
526 vec2 Vel = *pInoutVel;
527
528 float Distance = length(a: Vel);
529 int Max = (int)Distance;
530
531 if(Distance > 0.00001f)
532 {
533 float Fraction = 1.0f / (float)(Max + 1);
534 float ElasticityX = clamp(val: Elasticity.x, lo: -1.0f, hi: 1.0f);
535 float ElasticityY = clamp(val: Elasticity.y, lo: -1.0f, hi: 1.0f);
536
537 for(int i = 0; i <= Max; i++)
538 {
539 // Early break as optimization to stop checking for collisions for
540 // large distances after the obstacles we have already hit reduced
541 // our speed to exactly 0.
542 if(Vel == vec2(0, 0))
543 {
544 break;
545 }
546
547 vec2 NewPos = Pos + Vel * Fraction; // TODO: this row is not nice
548
549 // Fraction can be very small and thus the calculation has no effect, no
550 // reason to continue calculating.
551 if(NewPos == Pos)
552 {
553 break;
554 }
555
556 if(TestBox(Pos: vec2(NewPos.x, NewPos.y), Size))
557 {
558 int Hits = 0;
559
560 if(TestBox(Pos: vec2(Pos.x, NewPos.y), Size))
561 {
562 if(pGrounded && ElasticityY > 0 && Vel.y > 0)
563 *pGrounded = true;
564 NewPos.y = Pos.y;
565 Vel.y *= -ElasticityY;
566 Hits++;
567 }
568
569 if(TestBox(Pos: vec2(NewPos.x, Pos.y), Size))
570 {
571 NewPos.x = Pos.x;
572 Vel.x *= -ElasticityX;
573 Hits++;
574 }
575
576 // neither of the tests got a collision.
577 // this is a real _corner case_!
578 if(Hits == 0)
579 {
580 if(pGrounded && ElasticityY > 0 && Vel.y > 0)
581 *pGrounded = true;
582 NewPos.y = Pos.y;
583 Vel.y *= -ElasticityY;
584 NewPos.x = Pos.x;
585 Vel.x *= -ElasticityX;
586 }
587 }
588
589 Pos = NewPos;
590 }
591 }
592
593 *pInoutPos = Pos;
594 *pInoutVel = Vel;
595}
596
597// DDRace
598
599int CCollision::IsSolid(int x, int y) const
600{
601 int index = GetTile(x, y);
602 return index == TILE_SOLID || index == TILE_NOHOOK;
603}
604
605bool CCollision::IsThrough(int x, int y, int xoff, int yoff, vec2 pos0, vec2 pos1) const
606{
607 int pos = GetPureMapIndex(x, y);
608 if(m_pFront && (m_pFront[pos].m_Index == TILE_THROUGH_ALL || m_pFront[pos].m_Index == TILE_THROUGH_CUT))
609 return true;
610 if(m_pFront && m_pFront[pos].m_Index == TILE_THROUGH_DIR && ((m_pFront[pos].m_Flags == ROTATION_0 && pos0.y > pos1.y) || (m_pFront[pos].m_Flags == ROTATION_90 && pos0.x < pos1.x) || (m_pFront[pos].m_Flags == ROTATION_180 && pos0.y < pos1.y) || (m_pFront[pos].m_Flags == ROTATION_270 && pos0.x > pos1.x)))
611 return true;
612 int offpos = GetPureMapIndex(x: x + xoff, y: y + yoff);
613 return m_pTiles[offpos].m_Index == TILE_THROUGH || (m_pFront && m_pFront[offpos].m_Index == TILE_THROUGH);
614}
615
616bool CCollision::IsHookBlocker(int x, int y, vec2 pos0, vec2 pos1) const
617{
618 int pos = GetPureMapIndex(x, y);
619 if(m_pTiles[pos].m_Index == TILE_THROUGH_ALL || (m_pFront && m_pFront[pos].m_Index == TILE_THROUGH_ALL))
620 return true;
621 if(m_pTiles[pos].m_Index == TILE_THROUGH_DIR && ((m_pTiles[pos].m_Flags == ROTATION_0 && pos0.y < pos1.y) ||
622 (m_pTiles[pos].m_Flags == ROTATION_90 && pos0.x > pos1.x) ||
623 (m_pTiles[pos].m_Flags == ROTATION_180 && pos0.y > pos1.y) ||
624 (m_pTiles[pos].m_Flags == ROTATION_270 && pos0.x < pos1.x)))
625 return true;
626 if(m_pFront && m_pFront[pos].m_Index == TILE_THROUGH_DIR && ((m_pFront[pos].m_Flags == ROTATION_0 && pos0.y < pos1.y) || (m_pFront[pos].m_Flags == ROTATION_90 && pos0.x > pos1.x) || (m_pFront[pos].m_Flags == ROTATION_180 && pos0.y > pos1.y) || (m_pFront[pos].m_Flags == ROTATION_270 && pos0.x < pos1.x)))
627 return true;
628 return false;
629}
630
631int CCollision::IsWallJump(int Index) const
632{
633 if(Index < 0)
634 return 0;
635
636 return m_pTiles[Index].m_Index == TILE_WALLJUMP;
637}
638
639int CCollision::IsNoLaser(int x, int y) const
640{
641 return (CCollision::GetTile(x, y) == TILE_NOLASER);
642}
643
644int CCollision::IsFNoLaser(int x, int y) const
645{
646 return (CCollision::GetFTile(x, y) == TILE_NOLASER);
647}
648
649int CCollision::IsTeleport(int Index) const
650{
651 if(Index < 0 || !m_pTele)
652 return 0;
653
654 if(m_pTele[Index].m_Type == TILE_TELEIN)
655 return m_pTele[Index].m_Number;
656
657 return 0;
658}
659
660int CCollision::IsEvilTeleport(int Index) const
661{
662 if(Index < 0)
663 return 0;
664 if(!m_pTele)
665 return 0;
666
667 if(m_pTele[Index].m_Type == TILE_TELEINEVIL)
668 return m_pTele[Index].m_Number;
669
670 return 0;
671}
672
673bool CCollision::IsCheckTeleport(int Index) const
674{
675 if(Index < 0 || !m_pTele)
676 return false;
677 return m_pTele[Index].m_Type == TILE_TELECHECKIN;
678}
679
680bool CCollision::IsCheckEvilTeleport(int Index) const
681{
682 if(Index < 0 || !m_pTele)
683 return false;
684 return m_pTele[Index].m_Type == TILE_TELECHECKINEVIL;
685}
686
687int CCollision::IsTeleCheckpoint(int Index) const
688{
689 if(Index < 0)
690 return 0;
691
692 if(!m_pTele)
693 return 0;
694
695 if(m_pTele[Index].m_Type == TILE_TELECHECK)
696 return m_pTele[Index].m_Number;
697
698 return 0;
699}
700
701int CCollision::IsTeleportWeapon(int Index) const
702{
703 if(Index < 0 || !m_pTele)
704 return 0;
705
706 if(m_pTele[Index].m_Type == TILE_TELEINWEAPON)
707 return m_pTele[Index].m_Number;
708
709 return 0;
710}
711
712int CCollision::IsTeleportHook(int Index) const
713{
714 if(Index < 0 || !m_pTele)
715 return 0;
716
717 if(m_pTele[Index].m_Type == TILE_TELEINHOOK)
718 return m_pTele[Index].m_Number;
719
720 return 0;
721}
722
723int CCollision::IsSpeedup(int Index) const
724{
725 if(Index < 0 || !m_pSpeedup)
726 return 0;
727
728 if(m_pSpeedup[Index].m_Force > 0)
729 return Index;
730
731 return 0;
732}
733
734int CCollision::IsTune(int Index) const
735{
736 if(Index < 0 || !m_pTune)
737 return 0;
738
739 if(m_pTune[Index].m_Type)
740 return m_pTune[Index].m_Number;
741
742 return 0;
743}
744
745void CCollision::GetSpeedup(int Index, vec2 *pDir, int *pForce, int *pMaxSpeed) const
746{
747 if(Index < 0 || !m_pSpeedup)
748 return;
749 float Angle = m_pSpeedup[Index].m_Angle * (pi / 180.0f);
750 *pForce = m_pSpeedup[Index].m_Force;
751 *pDir = direction(angle: Angle);
752 if(pMaxSpeed)
753 *pMaxSpeed = m_pSpeedup[Index].m_MaxSpeed;
754}
755
756int CCollision::GetSwitchType(int Index) const
757{
758 if(Index < 0 || !m_pSwitch)
759 return 0;
760
761 if(m_pSwitch[Index].m_Type > 0)
762 return m_pSwitch[Index].m_Type;
763
764 return 0;
765}
766
767int CCollision::GetSwitchNumber(int Index) const
768{
769 if(Index < 0 || !m_pSwitch)
770 return 0;
771
772 if(m_pSwitch[Index].m_Type > 0 && m_pSwitch[Index].m_Number > 0)
773 return m_pSwitch[Index].m_Number;
774
775 return 0;
776}
777
778int CCollision::GetSwitchDelay(int Index) const
779{
780 if(Index < 0 || !m_pSwitch)
781 return 0;
782
783 if(m_pSwitch[Index].m_Type > 0)
784 return m_pSwitch[Index].m_Delay;
785
786 return 0;
787}
788
789int CCollision::IsMover(int x, int y, int *pFlags) const
790{
791 int Nx = clamp(val: x / 32, lo: 0, hi: m_Width - 1);
792 int Ny = clamp(val: y / 32, lo: 0, hi: m_Height - 1);
793 int Index = m_pTiles[Ny * m_Width + Nx].m_Index;
794 *pFlags = m_pTiles[Ny * m_Width + Nx].m_Flags;
795 if(Index < 0)
796 return 0;
797 if(Index == TILE_CP || Index == TILE_CP_F)
798 return Index;
799 else
800 return 0;
801}
802
803vec2 CCollision::CpSpeed(int Index, int Flags) const
804{
805 if(Index < 0)
806 return vec2(0, 0);
807 vec2 target;
808 if(Index == TILE_CP || Index == TILE_CP_F)
809 switch(Flags)
810 {
811 case ROTATION_0:
812 target.x = 0;
813 target.y = -4;
814 break;
815 case ROTATION_90:
816 target.x = 4;
817 target.y = 0;
818 break;
819 case ROTATION_180:
820 target.x = 0;
821 target.y = 4;
822 break;
823 case ROTATION_270:
824 target.x = -4;
825 target.y = 0;
826 break;
827 default:
828 target = vec2(0, 0);
829 break;
830 }
831 if(Index == TILE_CP_F)
832 target *= 4;
833 return target;
834}
835
836int CCollision::GetPureMapIndex(float x, float y) const
837{
838 int Nx = clamp(val: round_to_int(f: x) / 32, lo: 0, hi: m_Width - 1);
839 int Ny = clamp(val: round_to_int(f: y) / 32, lo: 0, hi: m_Height - 1);
840 return Ny * m_Width + Nx;
841}
842
843bool CCollision::TileExists(int Index) const
844{
845 if(Index < 0)
846 return false;
847
848 if((m_pTiles[Index].m_Index >= TILE_FREEZE && m_pTiles[Index].m_Index <= TILE_TELE_LASER_DISABLE) || (m_pTiles[Index].m_Index >= TILE_LFREEZE && m_pTiles[Index].m_Index <= TILE_LUNFREEZE))
849 return true;
850 if(m_pFront && ((m_pFront[Index].m_Index >= TILE_FREEZE && m_pFront[Index].m_Index <= TILE_TELE_LASER_DISABLE) || (m_pFront[Index].m_Index >= TILE_LFREEZE && m_pFront[Index].m_Index <= TILE_LUNFREEZE)))
851 return true;
852 if(m_pTele && (m_pTele[Index].m_Type == TILE_TELEIN || m_pTele[Index].m_Type == TILE_TELEINEVIL || m_pTele[Index].m_Type == TILE_TELECHECKINEVIL || m_pTele[Index].m_Type == TILE_TELECHECK || m_pTele[Index].m_Type == TILE_TELECHECKIN))
853 return true;
854 if(m_pSpeedup && m_pSpeedup[Index].m_Force > 0)
855 return true;
856 if(m_pDoor && m_pDoor[Index].m_Index)
857 return true;
858 if(m_pSwitch && m_pSwitch[Index].m_Type)
859 return true;
860 if(m_pTune && m_pTune[Index].m_Type)
861 return true;
862 return TileExistsNext(Index);
863}
864
865bool CCollision::TileExistsNext(int Index) const
866{
867 if(Index < 0)
868 return false;
869 int TileOnTheLeft = (Index - 1 > 0) ? Index - 1 : Index;
870 int TileOnTheRight = (Index + 1 < m_Width * m_Height) ? Index + 1 : Index;
871 int TileBelow = (Index + m_Width < m_Width * m_Height) ? Index + m_Width : Index;
872 int TileAbove = (Index - m_Width > 0) ? Index - m_Width : Index;
873
874 if((m_pTiles[TileOnTheRight].m_Index == TILE_STOP && m_pTiles[TileOnTheRight].m_Flags == ROTATION_270) || (m_pTiles[TileOnTheLeft].m_Index == TILE_STOP && m_pTiles[TileOnTheLeft].m_Flags == ROTATION_90))
875 return true;
876 if((m_pTiles[TileBelow].m_Index == TILE_STOP && m_pTiles[TileBelow].m_Flags == ROTATION_0) || (m_pTiles[TileAbove].m_Index == TILE_STOP && m_pTiles[TileAbove].m_Flags == ROTATION_180))
877 return true;
878 if(m_pTiles[TileOnTheRight].m_Index == TILE_STOPA || m_pTiles[TileOnTheLeft].m_Index == TILE_STOPA || ((m_pTiles[TileOnTheRight].m_Index == TILE_STOPS || m_pTiles[TileOnTheLeft].m_Index == TILE_STOPS)))
879 return true;
880 if(m_pTiles[TileBelow].m_Index == TILE_STOPA || m_pTiles[TileAbove].m_Index == TILE_STOPA || ((m_pTiles[TileBelow].m_Index == TILE_STOPS || m_pTiles[TileAbove].m_Index == TILE_STOPS) && m_pTiles[TileBelow].m_Flags | ROTATION_180 | ROTATION_0))
881 return true;
882 if(m_pFront)
883 {
884 if(m_pFront[TileOnTheRight].m_Index == TILE_STOPA || m_pFront[TileOnTheLeft].m_Index == TILE_STOPA || ((m_pFront[TileOnTheRight].m_Index == TILE_STOPS || m_pFront[TileOnTheLeft].m_Index == TILE_STOPS)))
885 return true;
886 if(m_pFront[TileBelow].m_Index == TILE_STOPA || m_pFront[TileAbove].m_Index == TILE_STOPA || ((m_pFront[TileBelow].m_Index == TILE_STOPS || m_pFront[TileAbove].m_Index == TILE_STOPS) && m_pFront[TileBelow].m_Flags | ROTATION_180 | ROTATION_0))
887 return true;
888 if((m_pFront[TileOnTheRight].m_Index == TILE_STOP && m_pFront[TileOnTheRight].m_Flags == ROTATION_270) || (m_pFront[TileOnTheLeft].m_Index == TILE_STOP && m_pFront[TileOnTheLeft].m_Flags == ROTATION_90))
889 return true;
890 if((m_pFront[TileBelow].m_Index == TILE_STOP && m_pFront[TileBelow].m_Flags == ROTATION_0) || (m_pFront[TileAbove].m_Index == TILE_STOP && m_pFront[TileAbove].m_Flags == ROTATION_180))
891 return true;
892 }
893 if(m_pDoor)
894 {
895 if(m_pDoor[TileOnTheRight].m_Index == TILE_STOPA || m_pDoor[TileOnTheLeft].m_Index == TILE_STOPA || ((m_pDoor[TileOnTheRight].m_Index == TILE_STOPS || m_pDoor[TileOnTheLeft].m_Index == TILE_STOPS)))
896 return true;
897 if(m_pDoor[TileBelow].m_Index == TILE_STOPA || m_pDoor[TileAbove].m_Index == TILE_STOPA || ((m_pDoor[TileBelow].m_Index == TILE_STOPS || m_pDoor[TileAbove].m_Index == TILE_STOPS) && m_pDoor[TileBelow].m_Flags | ROTATION_180 | ROTATION_0))
898 return true;
899 if((m_pDoor[TileOnTheRight].m_Index == TILE_STOP && m_pDoor[TileOnTheRight].m_Flags == ROTATION_270) || (m_pDoor[TileOnTheLeft].m_Index == TILE_STOP && m_pDoor[TileOnTheLeft].m_Flags == ROTATION_90))
900 return true;
901 if((m_pDoor[TileBelow].m_Index == TILE_STOP && m_pDoor[TileBelow].m_Flags == ROTATION_0) || (m_pDoor[TileAbove].m_Index == TILE_STOP && m_pDoor[TileAbove].m_Flags == ROTATION_180))
902 return true;
903 }
904 return false;
905}
906
907int CCollision::GetMapIndex(vec2 Pos) const
908{
909 int Nx = clamp(val: (int)Pos.x / 32, lo: 0, hi: m_Width - 1);
910 int Ny = clamp(val: (int)Pos.y / 32, lo: 0, hi: m_Height - 1);
911 int Index = Ny * m_Width + Nx;
912
913 if(TileExists(Index))
914 return Index;
915 else
916 return -1;
917}
918
919std::vector<int> CCollision::GetMapIndices(vec2 PrevPos, vec2 Pos, unsigned MaxIndices) const
920{
921 std::vector<int> vIndices;
922 float d = distance(a: PrevPos, b: Pos);
923 int End(d + 1);
924 if(!d)
925 {
926 int Nx = clamp(val: (int)Pos.x / 32, lo: 0, hi: m_Width - 1);
927 int Ny = clamp(val: (int)Pos.y / 32, lo: 0, hi: m_Height - 1);
928 int Index = Ny * m_Width + Nx;
929
930 if(TileExists(Index))
931 {
932 vIndices.push_back(x: Index);
933 return vIndices;
934 }
935 else
936 return vIndices;
937 }
938 else
939 {
940 int LastIndex = 0;
941 for(int i = 0; i < End; i++)
942 {
943 float a = i / d;
944 vec2 Tmp = mix(a: PrevPos, b: Pos, amount: a);
945 int Nx = clamp(val: (int)Tmp.x / 32, lo: 0, hi: m_Width - 1);
946 int Ny = clamp(val: (int)Tmp.y / 32, lo: 0, hi: m_Height - 1);
947 int Index = Ny * m_Width + Nx;
948 if(TileExists(Index) && LastIndex != Index)
949 {
950 if(MaxIndices && vIndices.size() > MaxIndices)
951 return vIndices;
952 vIndices.push_back(x: Index);
953 LastIndex = Index;
954 }
955 }
956
957 return vIndices;
958 }
959}
960
961vec2 CCollision::GetPos(int Index) const
962{
963 if(Index < 0)
964 return vec2(0, 0);
965
966 int x = Index % m_Width;
967 int y = Index / m_Width;
968 return vec2(x * 32 + 16, y * 32 + 16);
969}
970
971int CCollision::GetTileIndex(int Index) const
972{
973 if(Index < 0)
974 return 0;
975 return m_pTiles[Index].m_Index;
976}
977
978int CCollision::GetFTileIndex(int Index) const
979{
980 if(Index < 0 || !m_pFront)
981 return 0;
982 return m_pFront[Index].m_Index;
983}
984
985int CCollision::GetTileFlags(int Index) const
986{
987 if(Index < 0)
988 return 0;
989 return m_pTiles[Index].m_Flags;
990}
991
992int CCollision::GetFTileFlags(int Index) const
993{
994 if(Index < 0 || !m_pFront)
995 return 0;
996 return m_pFront[Index].m_Flags;
997}
998
999int CCollision::GetIndex(int Nx, int Ny) const
1000{
1001 return m_pTiles[Ny * m_Width + Nx].m_Index;
1002}
1003
1004int CCollision::GetIndex(vec2 PrevPos, vec2 Pos) const
1005{
1006 float Distance = distance(a: PrevPos, b: Pos);
1007
1008 if(!Distance)
1009 {
1010 int Nx = clamp(val: (int)Pos.x / 32, lo: 0, hi: m_Width - 1);
1011 int Ny = clamp(val: (int)Pos.y / 32, lo: 0, hi: m_Height - 1);
1012
1013 if((m_pTele) ||
1014 (m_pSpeedup && m_pSpeedup[Ny * m_Width + Nx].m_Force > 0))
1015 {
1016 return Ny * m_Width + Nx;
1017 }
1018 }
1019
1020 for(int i = 0, id = std::ceil(x: Distance); i < id; i++)
1021 {
1022 float a = (float)i / Distance;
1023 vec2 Tmp = mix(a: PrevPos, b: Pos, amount: a);
1024 int Nx = clamp(val: (int)Tmp.x / 32, lo: 0, hi: m_Width - 1);
1025 int Ny = clamp(val: (int)Tmp.y / 32, lo: 0, hi: m_Height - 1);
1026 if((m_pTele) ||
1027 (m_pSpeedup && m_pSpeedup[Ny * m_Width + Nx].m_Force > 0))
1028 {
1029 return Ny * m_Width + Nx;
1030 }
1031 }
1032
1033 return -1;
1034}
1035
1036int CCollision::GetFIndex(int Nx, int Ny) const
1037{
1038 if(!m_pFront)
1039 return 0;
1040 return m_pFront[Ny * m_Width + Nx].m_Index;
1041}
1042
1043int CCollision::GetFTile(int x, int y) const
1044{
1045 if(!m_pFront)
1046 return 0;
1047 int Nx = clamp(val: x / 32, lo: 0, hi: m_Width - 1);
1048 int Ny = clamp(val: y / 32, lo: 0, hi: m_Height - 1);
1049 if(m_pFront[Ny * m_Width + Nx].m_Index == TILE_DEATH || m_pFront[Ny * m_Width + Nx].m_Index == TILE_NOLASER)
1050 return m_pFront[Ny * m_Width + Nx].m_Index;
1051 else
1052 return 0;
1053}
1054
1055int CCollision::Entity(int x, int y, int Layer) const
1056{
1057 if(x < 0 || x >= m_Width || y < 0 || y >= m_Height)
1058 return 0;
1059
1060 const int Index = y * m_Width + x;
1061 switch(Layer)
1062 {
1063 case LAYER_GAME:
1064 return m_pTiles[Index].m_Index - ENTITY_OFFSET;
1065 case LAYER_FRONT:
1066 return m_pFront[Index].m_Index - ENTITY_OFFSET;
1067 case LAYER_SWITCH:
1068 return m_pSwitch[Index].m_Type - ENTITY_OFFSET;
1069 case LAYER_TELE:
1070 return m_pTele[Index].m_Type - ENTITY_OFFSET;
1071 case LAYER_SPEEDUP:
1072 return m_pSpeedup[Index].m_Type - ENTITY_OFFSET;
1073 case LAYER_TUNE:
1074 return m_pTune[Index].m_Type - ENTITY_OFFSET;
1075 default:
1076 dbg_assert(false, "Layer invalid");
1077 dbg_break();
1078 }
1079}
1080
1081void CCollision::SetCollisionAt(float x, float y, int id)
1082{
1083 int Nx = clamp(val: round_to_int(f: x) / 32, lo: 0, hi: m_Width - 1);
1084 int Ny = clamp(val: round_to_int(f: y) / 32, lo: 0, hi: m_Height - 1);
1085
1086 m_pTiles[Ny * m_Width + Nx].m_Index = id;
1087}
1088
1089void CCollision::SetDCollisionAt(float x, float y, int Type, int Flags, int Number)
1090{
1091 if(!m_pDoor)
1092 return;
1093 int Nx = clamp(val: round_to_int(f: x) / 32, lo: 0, hi: m_Width - 1);
1094 int Ny = clamp(val: round_to_int(f: y) / 32, lo: 0, hi: m_Height - 1);
1095
1096 m_pDoor[Ny * m_Width + Nx].m_Index = Type;
1097 m_pDoor[Ny * m_Width + Nx].m_Flags = Flags;
1098 m_pDoor[Ny * m_Width + Nx].m_Number = Number;
1099}
1100
1101int CCollision::GetDTileIndex(int Index) const
1102{
1103 if(!m_pDoor || Index < 0 || !m_pDoor[Index].m_Index)
1104 return 0;
1105 return m_pDoor[Index].m_Index;
1106}
1107
1108int CCollision::GetDTileNumber(int Index) const
1109{
1110 if(!m_pDoor || Index < 0 || !m_pDoor[Index].m_Index)
1111 return 0;
1112 if(m_pDoor[Index].m_Number)
1113 return m_pDoor[Index].m_Number;
1114 return 0;
1115}
1116
1117int CCollision::GetDTileFlags(int Index) const
1118{
1119 if(!m_pDoor || Index < 0 || !m_pDoor[Index].m_Index)
1120 return 0;
1121 return m_pDoor[Index].m_Flags;
1122}
1123
1124void ThroughOffset(vec2 Pos0, vec2 Pos1, int *pOffsetX, int *pOffsetY)
1125{
1126 float x = Pos0.x - Pos1.x;
1127 float y = Pos0.y - Pos1.y;
1128 if(absolute(a: x) > absolute(a: y))
1129 {
1130 if(x < 0)
1131 {
1132 *pOffsetX = -32;
1133 *pOffsetY = 0;
1134 }
1135 else
1136 {
1137 *pOffsetX = 32;
1138 *pOffsetY = 0;
1139 }
1140 }
1141 else
1142 {
1143 if(y < 0)
1144 {
1145 *pOffsetX = 0;
1146 *pOffsetY = -32;
1147 }
1148 else
1149 {
1150 *pOffsetX = 0;
1151 *pOffsetY = 32;
1152 }
1153 }
1154}
1155
1156int CCollision::IntersectNoLaser(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision) const
1157{
1158 float d = distance(a: Pos0, b: Pos1);
1159 vec2 Last = Pos0;
1160
1161 for(int i = 0, id = std::ceil(x: d); i < id; i++)
1162 {
1163 float a = i / d;
1164 vec2 Pos = mix(a: Pos0, b: Pos1, amount: a);
1165 int Nx = clamp(val: round_to_int(f: Pos.x) / 32, lo: 0, hi: m_Width - 1);
1166 int Ny = clamp(val: round_to_int(f: Pos.y) / 32, lo: 0, hi: m_Height - 1);
1167 if(GetIndex(Nx, Ny) == TILE_SOLID || GetIndex(Nx, Ny) == TILE_NOHOOK || GetIndex(Nx, Ny) == TILE_NOLASER || GetFIndex(Nx, Ny) == TILE_NOLASER)
1168 {
1169 if(pOutCollision)
1170 *pOutCollision = Pos;
1171 if(pOutBeforeCollision)
1172 *pOutBeforeCollision = Last;
1173 if(GetFIndex(Nx, Ny) == TILE_NOLASER)
1174 return GetFCollisionAt(x: Pos.x, y: Pos.y);
1175 else
1176 return GetCollisionAt(x: Pos.x, y: Pos.y);
1177 }
1178 Last = Pos;
1179 }
1180 if(pOutCollision)
1181 *pOutCollision = Pos1;
1182 if(pOutBeforeCollision)
1183 *pOutBeforeCollision = Pos1;
1184 return 0;
1185}
1186
1187int CCollision::IntersectNoLaserNW(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision) const
1188{
1189 float d = distance(a: Pos0, b: Pos1);
1190 vec2 Last = Pos0;
1191
1192 for(int i = 0, id = std::ceil(x: d); i < id; i++)
1193 {
1194 float a = (float)i / d;
1195 vec2 Pos = mix(a: Pos0, b: Pos1, amount: a);
1196 if(IsNoLaser(x: round_to_int(f: Pos.x), y: round_to_int(f: Pos.y)) || IsFNoLaser(x: round_to_int(f: Pos.x), y: round_to_int(f: Pos.y)))
1197 {
1198 if(pOutCollision)
1199 *pOutCollision = Pos;
1200 if(pOutBeforeCollision)
1201 *pOutBeforeCollision = Last;
1202 if(IsNoLaser(x: round_to_int(f: Pos.x), y: round_to_int(f: Pos.y)))
1203 return GetCollisionAt(x: Pos.x, y: Pos.y);
1204 else
1205 return GetFCollisionAt(x: Pos.x, y: Pos.y);
1206 }
1207 Last = Pos;
1208 }
1209 if(pOutCollision)
1210 *pOutCollision = Pos1;
1211 if(pOutBeforeCollision)
1212 *pOutBeforeCollision = Pos1;
1213 return 0;
1214}
1215
1216int CCollision::IntersectAir(vec2 Pos0, vec2 Pos1, vec2 *pOutCollision, vec2 *pOutBeforeCollision) const
1217{
1218 float d = distance(a: Pos0, b: Pos1);
1219 vec2 Last = Pos0;
1220
1221 for(int i = 0, id = std::ceil(x: d); i < id; i++)
1222 {
1223 float a = (float)i / d;
1224 vec2 Pos = mix(a: Pos0, b: Pos1, amount: a);
1225 if(IsSolid(x: round_to_int(f: Pos.x), y: round_to_int(f: Pos.y)) || (!GetTile(x: round_to_int(f: Pos.x), y: round_to_int(f: Pos.y)) && !GetFTile(x: round_to_int(f: Pos.x), y: round_to_int(f: Pos.y))))
1226 {
1227 if(pOutCollision)
1228 *pOutCollision = Pos;
1229 if(pOutBeforeCollision)
1230 *pOutBeforeCollision = Last;
1231 if(!GetTile(x: round_to_int(f: Pos.x), y: round_to_int(f: Pos.y)) && !GetFTile(x: round_to_int(f: Pos.x), y: round_to_int(f: Pos.y)))
1232 return -1;
1233 else if(!GetTile(x: round_to_int(f: Pos.x), y: round_to_int(f: Pos.y)))
1234 return GetTile(x: round_to_int(f: Pos.x), y: round_to_int(f: Pos.y));
1235 else
1236 return GetFTile(x: round_to_int(f: Pos.x), y: round_to_int(f: Pos.y));
1237 }
1238 Last = Pos;
1239 }
1240 if(pOutCollision)
1241 *pOutCollision = Pos1;
1242 if(pOutBeforeCollision)
1243 *pOutBeforeCollision = Pos1;
1244 return 0;
1245}
1246
1247int CCollision::IsTimeCheckpoint(int Index) const
1248{
1249 if(Index < 0)
1250 return -1;
1251
1252 int z = m_pTiles[Index].m_Index;
1253 if(z >= TILE_TIME_CHECKPOINT_FIRST && z <= TILE_TIME_CHECKPOINT_LAST)
1254 return z - TILE_TIME_CHECKPOINT_FIRST;
1255 return -1;
1256}
1257
1258int CCollision::IsFTimeCheckpoint(int Index) const
1259{
1260 if(Index < 0 || !m_pFront)
1261 return -1;
1262
1263 int z = m_pFront[Index].m_Index;
1264 if(z >= TILE_TIME_CHECKPOINT_FIRST && z <= TILE_TIME_CHECKPOINT_LAST)
1265 return z - TILE_TIME_CHECKPOINT_FIRST;
1266 return -1;
1267}
1268
1269vec2 CCollision::TeleAllGet(int Number, size_t Offset)
1270{
1271 if(m_TeleIns.count(x: Number) > 0)
1272 {
1273 if(m_TeleIns[Number].size() > Offset)
1274 return m_TeleIns[Number][Offset];
1275 else
1276 Offset -= m_TeleIns[Number].size();
1277 }
1278 if(m_TeleOuts.count(x: Number) > 0)
1279 {
1280 if(m_TeleOuts[Number].size() > Offset)
1281 return m_TeleOuts[Number][Offset];
1282 else
1283 Offset -= m_TeleOuts[Number].size();
1284 }
1285 if(m_TeleCheckOuts.count(x: Number) > 0)
1286 {
1287 if(m_TeleCheckOuts[Number].size() > Offset)
1288 return m_TeleCheckOuts[Number][Offset];
1289 else
1290 Offset -= m_TeleCheckOuts[Number].size();
1291 }
1292 if(m_TeleOthers.count(x: Number) > 0)
1293 {
1294 if(m_TeleOthers[Number].size() > Offset)
1295 return m_TeleOthers[Number][Offset];
1296 }
1297 return vec2(-1, -1);
1298}
1299
1300size_t CCollision::TeleAllSize(int Number)
1301{
1302 size_t Total = 0;
1303 if(m_TeleIns.count(x: Number) > 0)
1304 Total += m_TeleIns[Number].size();
1305 if(m_TeleOuts.count(x: Number) > 0)
1306 Total += m_TeleOuts[Number].size();
1307 if(m_TeleCheckOuts.count(x: Number) > 0)
1308 Total += m_TeleCheckOuts[Number].size();
1309 if(m_TeleOthers.count(x: Number) > 0)
1310 Total += m_TeleOthers[Number].size();
1311 return Total;
1312}
1313