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 | |
18 | vec2 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 | |
39 | CCollision::CCollision() |
40 | { |
41 | m_pDoor = nullptr; |
42 | Unload(); |
43 | } |
44 | |
45 | CCollision::~CCollision() |
46 | { |
47 | Unload(); |
48 | } |
49 | |
50 | void 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 | |
151 | void 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 | |
174 | void 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 | |
189 | enum |
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 | |
199 | static 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 | |
239 | static 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 | |
253 | static 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 | |
266 | int 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 | |
315 | int 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! |
330 | int 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 | |
361 | int 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 | |
421 | int 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! |
470 | void 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 | |
508 | bool 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 | |
522 | void 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 | |
599 | int CCollision::IsSolid(int x, int y) const |
600 | { |
601 | int index = GetTile(x, y); |
602 | return index == TILE_SOLID || index == TILE_NOHOOK; |
603 | } |
604 | |
605 | bool 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 | |
616 | bool 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 | |
631 | int 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 | |
639 | int CCollision::IsNoLaser(int x, int y) const |
640 | { |
641 | return (CCollision::GetTile(x, y) == TILE_NOLASER); |
642 | } |
643 | |
644 | int CCollision::IsFNoLaser(int x, int y) const |
645 | { |
646 | return (CCollision::GetFTile(x, y) == TILE_NOLASER); |
647 | } |
648 | |
649 | int 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 | |
660 | int 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 | |
673 | bool 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 | |
680 | bool 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 | |
687 | int 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 | |
701 | int 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 | |
712 | int 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 | |
723 | int 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 | |
734 | int 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 | |
745 | void 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 | |
756 | int 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 | |
767 | int 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 | |
778 | int 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 | |
789 | int 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 | |
803 | vec2 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 | |
836 | int 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 | |
843 | bool 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 | |
865 | bool 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 | |
907 | int 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 | |
919 | std::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 | |
961 | vec2 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 | |
971 | int CCollision::GetTileIndex(int Index) const |
972 | { |
973 | if(Index < 0) |
974 | return 0; |
975 | return m_pTiles[Index].m_Index; |
976 | } |
977 | |
978 | int CCollision::GetFTileIndex(int Index) const |
979 | { |
980 | if(Index < 0 || !m_pFront) |
981 | return 0; |
982 | return m_pFront[Index].m_Index; |
983 | } |
984 | |
985 | int CCollision::GetTileFlags(int Index) const |
986 | { |
987 | if(Index < 0) |
988 | return 0; |
989 | return m_pTiles[Index].m_Flags; |
990 | } |
991 | |
992 | int CCollision::GetFTileFlags(int Index) const |
993 | { |
994 | if(Index < 0 || !m_pFront) |
995 | return 0; |
996 | return m_pFront[Index].m_Flags; |
997 | } |
998 | |
999 | int CCollision::GetIndex(int Nx, int Ny) const |
1000 | { |
1001 | return m_pTiles[Ny * m_Width + Nx].m_Index; |
1002 | } |
1003 | |
1004 | int 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 | |
1036 | int 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 | |
1043 | int 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 | |
1055 | int 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 | |
1081 | void 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 | |
1089 | void 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 | |
1101 | int 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 | |
1108 | int 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 | |
1117 | int 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 | |
1124 | void 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 | |
1156 | int 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 | |
1187 | int 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 | |
1216 | int 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 | |
1247 | int 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 | |
1258 | int 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 | |
1269 | vec2 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 | |
1300 | size_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 | |