-
Notifications
You must be signed in to change notification settings - Fork 0
/
Person.cpp
executable file
·217 lines (149 loc) · 6.32 KB
/
Person.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#include "Person.h"
#include "GameMap.h"
bool Person::move_to(float seconds_elapsed, const Vector& direction, bool pushing) {
// New position computation
// time threshold dependent on max_speed and BlockMap::block_size
// we need to prevent crossing more than two map blocks in a step
if (seconds_elapsed > 1) return false;
Vector move_vector{0,0};
if (direction.length() != 0) move_vector = Vector::normalize(direction);
else pushing = false;
Vector new_pos = pos_ + seconds_elapsed * Vector{ velocity_.X, velocity_.Y };
float new_z = Z + seconds_elapsed * velocity_.Z;
float new_x = 0, new_y = 0;
if (!pushing && !(velocity_.X == 0 && velocity_.Y == 0)) {
Vector move_dir = Vector::normalize(Vector{ velocity_.X, velocity_.Y });
new_x = velocity_.X - move_dir.X * acceleration_ * seconds_elapsed;
new_y = velocity_.Y - move_dir.Y * acceleration_ * seconds_elapsed;
if ((velocity_.X > 0 && new_x < 0) || (velocity_.X < 0 && new_x > 0)) { new_x = 0; }
if ((velocity_.Y > 0 && new_y < 0) || (velocity_.Y < 0 && new_y > 0)) { new_y = 0; }
}
else if (pushing) {
new_x = velocity_.X + move_vector.X * acceleration_ * seconds_elapsed;
new_y = velocity_.Y + move_vector.Y * acceleration_ * seconds_elapsed;
float l = Vector{ new_x, new_y }.length() / get_max_speed();
if (l > 1) { new_x /= l; new_y /= l; }
}
velocity_.X = ( std::isinf(new_x) ? 0 : new_x );
velocity_.Y = ( std::isinf(new_y) ? 0 : new_y );
// Handling z-coordinate
if (velocity_.Z < 0) {
if (new_z < sector_->fh) {
Z = (float)sector_->fh;
velocity_.Z = 0;
falling_ = false;
} else Z = new_z;
}
if (velocity_.Z > 0) {
if (new_z + get_head_level() - Z > sector_->ch) {
velocity_.Z = 0;
falling_ = false;
} else {
// branch needed because of the "step" lower -> higher floor movement
if (Z < sector_->fh && sector_->fh <= new_z) {
Z = (float)sector_->fh;
velocity_.Z = 0;
falling_ = false;
} else Z = new_z;
}
}
if (Z != sector_->fh) { velocity_.Z -= gravity_a * seconds_elapsed; falling_ = true; }
// Collision detection
new_pos.X = std::isinf(new_pos.X) ? pos_.X : new_pos.X;
new_pos.Y = std::isinf(new_pos.Y) ? pos_.Y : new_pos.Y;
auto start_block = map_->get_block_map().get_block(pos_);
auto end_block = map_->get_block_map().get_block(new_pos);
Vector pos_memory = new_pos;
bool collision = true;
if (start_block == nullptr) new_pos = pos_;
collision_detection(start_block, new_pos);
if (&(*start_block) != &(*end_block)) {
if (end_block == nullptr) new_pos = pos_;
collision_detection(end_block, new_pos);
}
if (pos_memory != new_pos) collision = false;
// Update position
pos_ = new_pos;
return collision;
}
void Person::collision_detection(const lines_p_t * block, Vector& new_pos) {
Sector* new_sector;
collision:;
new_sector = nullptr;
if ((new_pos - pos_).length() < 0.1) { new_pos = pos_; return; }
new_pos.X = std::isinf(new_pos.X) ? pos_.X : new_pos.X;
new_pos.Y = std::isinf(new_pos.Y) ? pos_.Y : new_pos.Y;
// detect collision with objects in certain map block
for (auto it = block->begin(); it != block->end(); ++it) {
// line has opposite orientation and is not two-sided, it cannot affect anything
StraightLine line(*(*it)->start, *(*it)->end);
int line_side = line.determine_pnt_side(pos_);
if (line_side == 1 && (*it)->right == nullptr) continue;
if ((*it)->in_collision(pos_, new_pos)) {
if ((*it)->right != nullptr) {
new_sector = (&(*(*it)->left->sector) == &(*sector_) ? (*it)->right->sector : (*it)->left->sector);
int hole_low = std::max(sector_->fh, new_sector->fh);
int hole_high = std::min(sector_->ch, new_sector->ch);
int step = new_sector->fh - sector_->fh;
// the hole can be entered
if (hole_low <= get_foot_level() + (falling_ ? 0 : get_step_height()) &&
hole_high >= get_head_level() + (step <= 0 || falling_ ? 0 : step))
continue;
}
Vector n = Vector::normalize(*(*it)->end - *(*it)->start);
Vector projection = (new_pos - pos_) * n * n;
if ((new_pos - pos_ - projection).length() < 0.0001f) new_pos = pos_;
else new_pos = pos_ + projection;
// reset loop because we affected new_pos
// go to start of this whole function
goto collision;
}
}
// detect collision with things in current sector and then in new sector
for (auto it = sector_->thgs.begin(); it != sector_->thgs.end(); ++it) {
if ((*it) == this) continue;
if ((*it)->in_collision(*this, new_pos)) {
Vector center_diff = (*it)->get_position() - pos_;
Vector n = Vector::normalize({ center_diff.Y, -center_diff.X });
Vector projection = (new_pos - pos_) * n * n;
if ((new_pos - pos_ - projection).length() < 0.0001f) new_pos = pos_;
else new_pos = pos_ + projection;
// reset all loops because we affected new_pos and new_sector can be changed
// go to start of this whole function
goto collision;
}
}
if (new_sector == nullptr) return;
for (auto it = new_sector->thgs.begin(); it != new_sector->thgs.end(); ++it) {
if ((*it) == this) continue;
if ((*it)->in_collision(*this, new_pos)) {
Vector center_diff = (*it)->get_position() - pos_;
Vector n = Vector::normalize({ center_diff.Y, -center_diff.X });
Vector projection = (new_pos - pos_) * n * n;
if ((new_pos - pos_ - projection).length() < 0.01f) new_pos = pos_;
else new_pos = pos_ + projection;
// reset all loops because we affected new_pos and new_sector can be changed
// go to start of this whole function
goto collision;
}
}
// crossing to another unreachable sector when falling
if (falling_ && new_sector->fh > Z) { new_pos = pos_; return; }
// person is in the air and its vertical velocity
// must not be affected by crossing sectors
int step = new_sector->fh - sector_->fh;
if (step > 0 && Z == sector_->fh) {
velocity_.Z = 1.5f * sqrt(2.0f * gravity_a * step);
falling_ = true;
}
if (step < 0 && Z <= sector_->fh) {
velocity_.Z = -300;
falling_ = true;
}
// Update sectors list of things
for (auto i = sector_->thgs.begin(); i != sector_->thgs.end(); ++i) {
if (*i == this) { sector_->thgs.erase(i); break; }
}
new_sector->thgs.push_back(this);
sector_ = new_sector;
}