1 #ifndef H_COLLISION
2 #define H_COLLISION
3 
4 #include "core.h"
5 #include "utils.h"
6 #include "format.h"
7 #include "controller.h"
8 
9 struct Collision {
10     enum Side { NONE, LEFT, RIGHT, FRONT, BACK, TOP, BOTTOM } side;
11 
12     struct Info {
13         int room, roomAbove, roomBelow;
14         int climb;
15         float floor, ceiling;
16     } info[4];
17 
CollisionCollision18     Collision() : side(NONE) {}
19 
CollisionCollision20     Collision(Controller *controller, int roomIndex, vec3 &pos, const vec3 &offset, const vec3 &velocity, float radius, float angle, int minHeight, int maxHeight, int maxAscent, int maxDescent) {
21         if (velocity.x > 0.0f || velocity.z > 0.0f)
22             angle = normalizeAngle(PI2 + vec2(velocity.z, velocity.x).angle());
23         pos += velocity;
24 
25         int q = angleQuadrant(angle, 0.25f);
26 
27         const vec2 v[] = {
28             vec2( -radius,  radius ),
29             vec2(  radius,  radius ),
30             vec2(  radius, -radius ),
31             vec2( -radius, -radius ),
32         };
33 
34         const vec2 &l = v[q], &r = v[(q + 1) % 4];
35 
36         vec2 f = (q %= 2) ? vec2(l.x, radius * cosf(angle)) : vec2(radius * sinf(angle), l.y),
37                 p(pos.x, pos.z),
38                 d(0.0F);
39 
40         vec3 hpos = pos + offset;
41 
42         int height = maxHeight - minHeight;
43 
44         getFloor(controller->level, roomIndex, vec3(pos.x, hpos.y, pos.z));
45 
46         if (checkHeight(controller, roomIndex, hpos, vec2(0.0f), height, 0xFFFFFF, 0xFFFFFF, side = NONE)) {
47             pos.x -= velocity.x;
48             pos.z -= velocity.z;
49             side = FRONT;
50             return;
51         }
52 
53         float hCell = info[NONE].ceiling - (hpos.y - maxHeight);
54         if (hCell > 0) {
55             if (hCell > 128) {
56                 pos.x -= velocity.x;
57                 pos.z -= velocity.z;
58                 side = FRONT;
59             } else {
60                 pos.y = info[NONE].ceiling + maxHeight - offset.y;
61                 side  = TOP;
62             }
63         }
64 
65         float hFloor = info[NONE].floor - (hpos.y + minHeight);
66         if (hFloor < 0 && hFloor > -256) {
67             pos.y = info[NONE].floor - minHeight - offset.y;
68             side  = BOTTOM;
69         }
70 
71         if (checkHeight(controller, roomIndex, hpos, f, height, maxAscent, maxDescent, FRONT)) {
72             d = vec2(-velocity.x, -velocity.z);
73             q ^= 1;
74             d[q] = getOffset(p[q] + f[q], p[q]);
75         } else if (checkHeight(controller, roomIndex, hpos, l, height, maxAscent, maxDescent, LEFT)) {
76             d[q] = getOffset(p[q] + l[q], p[q] + f[q]);
77         } else if (checkHeight(controller, roomIndex, hpos, r, height, maxAscent, maxDescent, RIGHT)) {
78             d[q] = getOffset(p[q] + r[q], p[q] + f[q]);
79         } else
80             return;
81 
82         pos += vec3(d.x, 0.0f, d.y);
83     }
84 
checkHeightCollision85     inline bool checkHeight(Controller *controller, int roomIndex, const vec3 &pos, const vec2 &offset, int height, int maxAscent, int maxDescent, Side side) {
86         TR::Level::FloorInfo info;
87         controller->getFloorInfo(roomIndex, pos + vec3(offset.x, 0.0f, offset.y), info);
88 
89         Info &inf = this->info[side];
90         inf.room      = info.roomNext != TR::NO_ROOM ? info.roomNext : roomIndex;
91         inf.roomAbove = info.roomAbove;
92         inf.roomBelow = info.roomBelow;
93         inf.floor     = info.floor;
94         inf.ceiling   = info.ceiling;
95         inf.climb     = info.climb;
96 
97         if ((info.ceiling == info.floor) ||  (info.floor - info.ceiling < height) || (pos.y - info.floor > maxAscent) || (info.floor - pos.y > maxDescent) || (info.ceiling > pos.y) ||
98             (maxAscent == maxDescent && (maxAscent <= 256 + 128) && (abs(info.slantX) > 2 || abs(info.slantZ) > 2))) {
99             this->side = side;
100             return true;
101         }
102         return false;
103     }
104 
getOffsetCollision105     inline float getOffset(float from, float to) {
106         int a = int(from) / 1024;
107         int b = int(to)   / 1024;
108 
109         from -= float(a * 1024.0f);
110 
111         if (b == a)
112             return 0.0f;
113         else if (b > a)
114             return -from + 1025.0f;
115         return -from - 1.0f;
116     }
117 
getFloorCollision118     static int getFloor(TR::Level *level, int &roomIndex, const vec3 &pos) {
119         int dx, dz, x = int(pos.x), z = int(pos.z);
120 
121         TR::Room::Sector *s = &level->getSector(roomIndex, x, z, dx, dz);
122         while (s->ceiling * 256 > pos.y && s->roomAbove != TR::NO_ROOM) {
123             roomIndex = s->roomAbove;
124             s = &level->getSector(roomIndex, x, z, dx, dz);
125         }
126 
127         return s->floor * 256;
128     }
129 };
130 
131 #endif