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