1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "sludge/fileset.h"
24 #include "sludge/floor.h"
25 #include "sludge/graphics.h"
26 #include "sludge/newfatal.h"
27 #include "sludge/people.h"
28 #include "sludge/sludge.h"
29
30 #define ANGLEFIX (180.0 / 3.14157)
31
32 namespace Sludge {
33
FloorManager(SludgeEngine * vm)34 FloorManager::FloorManager(SludgeEngine *vm) {
35 _vm = vm;
36 _currentFloor = nullptr;
37 }
38
~FloorManager()39 FloorManager::~FloorManager() {
40 kill();
41 }
42
pointInFloorPolygon(FloorPolygon & floorPoly,int x,int y)43 bool FloorManager::pointInFloorPolygon(FloorPolygon &floorPoly, int x, int y) {
44 int i = 0, j, c = 0;
45 float xp_i, yp_i;
46 float xp_j, yp_j;
47
48 for (j = floorPoly.numVertices - 1; i < floorPoly.numVertices; j = i++) {
49
50 xp_i = _currentFloor->vertex[floorPoly.vertexID[i]].x;
51 yp_i = _currentFloor->vertex[floorPoly.vertexID[i]].y;
52 xp_j = _currentFloor->vertex[floorPoly.vertexID[j]].x;
53 yp_j = _currentFloor->vertex[floorPoly.vertexID[j]].y;
54
55 if ((((yp_i <= y) && (y < yp_j)) || ((yp_j <= y) && (y < yp_i))) && (x < (xp_j - xp_i) * (y - yp_i) / (yp_j - yp_i) + xp_i)) {
56 c = !c;
57 }
58 }
59 return c;
60 }
61
getMatchingCorners(FloorPolygon & a,FloorPolygon & b,int & cornerA,int & cornerB)62 bool FloorManager::getMatchingCorners(FloorPolygon &a, FloorPolygon &b, int &cornerA, int &cornerB) {
63 int sharedVertices = 0;
64 int i, j;
65
66 for (i = 0; i < a.numVertices; i++) {
67 for (j = 0; j < b.numVertices; j++) {
68 if (a.vertexID[i] == b.vertexID[j]) {
69 if (sharedVertices++) {
70 cornerB = a.vertexID[i];
71 return true;
72 } else {
73 cornerA = a.vertexID[i];
74 }
75 }
76 }
77 }
78
79 return false;
80 }
81
polysShareSide(FloorPolygon & a,FloorPolygon & b)82 bool FloorManager::polysShareSide(FloorPolygon &a, FloorPolygon &b) {
83 int sharedVertices = 0;
84 int i, j;
85
86 for (i = 0; i < a.numVertices; i++) {
87 for (j = 0; j < b.numVertices; j++) {
88 if (a.vertexID[i] == b.vertexID[j]) {
89 if (sharedVertices++)
90 return true;
91 }
92 }
93 }
94
95 return false;
96 }
97
init()98 bool FloorManager::init() {
99 _currentFloor = new Floor;
100 if (!checkNew(_currentFloor))
101 return false;
102 _currentFloor->numPolygons = 0;
103 _currentFloor->polygon = nullptr;
104 _currentFloor->vertex = nullptr;
105 _currentFloor->matrix = nullptr;
106 return true;
107 }
108
setFloorNull()109 void FloorManager::setFloorNull() {
110 if (_currentFloor) {
111 for (int i = 0; i < _currentFloor->numPolygons; i++) {
112 delete[] _currentFloor->polygon[i].vertexID;
113 delete[] _currentFloor->matrix[i];
114 }
115 _currentFloor->numPolygons = 0;
116 delete[] _currentFloor->polygon;
117 _currentFloor->polygon = nullptr;
118 delete[] _currentFloor->vertex;
119 _currentFloor->vertex = nullptr;
120 delete[] _currentFloor->matrix;
121 _currentFloor->matrix = nullptr;
122 }
123 }
124
kill()125 void FloorManager::kill() {
126 setFloorNull();
127 if (_currentFloor) {
128 delete _currentFloor;
129 _currentFloor = nullptr;
130 }
131 }
132
setFloor(int fileNum)133 bool FloorManager::setFloor(int fileNum) {
134
135 int i, j;
136
137 setFloorNull();
138
139 setResourceForFatal(fileNum);
140
141 if (!g_sludge->_resMan->openFileFromNum(fileNum))
142 return false;
143
144 g_sludge->_resMan->dumpFile(fileNum, "floor%04d.flo.comp");
145
146 // Find out how many polygons there are and reserve memory
147
148 _currentFloor->originalNum = fileNum;
149 _currentFloor->numPolygons = g_sludge->_resMan->getData()->readByte();
150 _currentFloor->polygon = new FloorPolygon[_currentFloor->numPolygons];
151 if (!checkNew(_currentFloor->polygon))
152 return false;
153
154 // Read in each polygon
155
156 for (i = 0; i < _currentFloor->numPolygons; i++) {
157 // Find out how many vertex IDs there are and reserve memory
158 _currentFloor->polygon[i].numVertices = g_sludge->_resMan->getData()->readByte();
159 _currentFloor->polygon[i].vertexID = new int[_currentFloor->polygon[i].numVertices];
160 if (!checkNew(_currentFloor->polygon[i].vertexID))
161 return false;
162
163 // Read in each vertex ID
164 for (j = 0; j < _currentFloor->polygon[i].numVertices; j++) {
165 _currentFloor->polygon[i].vertexID[j] = g_sludge->_resMan->getData()->readUint16BE();
166 }
167 }
168
169 // Find out how many vertices there are and reserve memory
170
171 i = g_sludge->_resMan->getData()->readUint16BE();
172 _currentFloor->vertex = new Common::Point[i];
173 if (!checkNew(_currentFloor->vertex))
174 return false;
175
176 for (j = 0; j < i; j++) {
177 _currentFloor->vertex[j].x = g_sludge->_resMan->getData()->readUint16BE();
178 _currentFloor->vertex[j].y = g_sludge->_resMan->getData()->readUint16BE();
179 }
180
181 g_sludge->_resMan->finishAccess();
182
183 dumpFloor(fileNum);
184
185 // Now build the movement martix
186
187 _currentFloor->matrix = new int *[_currentFloor->numPolygons];
188 int **distanceMatrix = new int *[_currentFloor->numPolygons];
189
190 if (!checkNew(_currentFloor->matrix))
191 return false;
192
193 for (i = 0; i < _currentFloor->numPolygons; i++) {
194 _currentFloor->matrix[i] = new int[_currentFloor->numPolygons];
195 distanceMatrix[i] = new int[_currentFloor->numPolygons];
196 if (!checkNew(_currentFloor->matrix[i]))
197 return false;
198 for (j = 0; j < _currentFloor->numPolygons; j++) {
199 _currentFloor->matrix[i][j] = -1;
200 distanceMatrix[i][j] = 10000;
201 }
202 }
203
204 for (i = 0; i < _currentFloor->numPolygons; i++) {
205 for (j = 0; j < _currentFloor->numPolygons; j++) {
206 if (i != j) {
207 if (polysShareSide(_currentFloor->polygon[i], _currentFloor->polygon[j])) {
208 _currentFloor->matrix[i][j] = j;
209 distanceMatrix[i][j] = 1;
210 }
211 } else {
212 _currentFloor->matrix[i][j] = -2;
213 distanceMatrix[i][j] = 0;
214 }
215 }
216 }
217
218 bool madeChange;
219 int lookForDistance = 0;
220
221 do {
222 lookForDistance++;
223 madeChange = false;
224 for (i = 0; i < _currentFloor->numPolygons; i++) {
225 for (j = 0; j < _currentFloor->numPolygons; j++) {
226 if (_currentFloor->matrix[i][j] == -1) {
227
228 // OK, so we don't know how to get from i to j...
229 for (int d = 0; d < _currentFloor->numPolygons; d++) {
230 if (d != i && d != j) {
231 if (_currentFloor->matrix[i][d] == d && _currentFloor->matrix[d][j] >= 0 && distanceMatrix[d][j] <= lookForDistance) {
232 _currentFloor->matrix[i][j] = d;
233 distanceMatrix[i][j] = lookForDistance + 1;
234 madeChange = true;
235 }
236 }
237 }
238 }
239 }
240 }
241 } while (madeChange);
242
243 for (i = 0; i < _currentFloor->numPolygons; i++) {
244 delete[] distanceMatrix[i];
245 }
246
247 delete []distanceMatrix;
248 distanceMatrix = nullptr;
249
250 setResourceForFatal(-1);
251
252 return true;
253 }
254
dumpFloor(int fileNum)255 void FloorManager::dumpFloor(int fileNum) {
256 if (!g_sludge->_dumpScripts)
257 return;
258
259 Common::DumpFile dumpFile;
260 dumpFile.open(Common::String::format("dumps/floor%04d.flo", fileNum));
261
262 for (int i = 0; i < _currentFloor->numPolygons; i++) {
263 int nV = _currentFloor->polygon[i].numVertices;
264 if (nV > 1) {
265 int vert = _currentFloor->polygon[i].vertexID[0];
266 dumpFile.writeString(Common::String::format("* %d, %d", _currentFloor->vertex[vert].x, _currentFloor->vertex[vert].y));
267 for (int j = 1; j < nV; j++) {
268 vert = _currentFloor->polygon[i].vertexID[j];
269 dumpFile.writeString(Common::String::format("; %d, %d", _currentFloor->vertex[vert].x, _currentFloor->vertex[vert].y));
270 }
271 dumpFile.writeString("\n");
272 }
273 }
274
275 dumpFile.close();
276 }
277
drawFloor()278 void FloorManager::drawFloor() {
279 int i, j, nV;
280 for (i = 0; i < _currentFloor->numPolygons; i++) {
281 nV = _currentFloor->polygon[i].numVertices;
282 if (nV > 1) {
283 for (j = 1; j < nV; j++) {
284 g_sludge->_gfxMan->drawLine(_currentFloor->vertex[_currentFloor->polygon[i].vertexID[j - 1]].x, _currentFloor->vertex[_currentFloor->polygon[i].vertexID[j - 1]].y,
285 _currentFloor->vertex[_currentFloor->polygon[i].vertexID[j]].x, _currentFloor->vertex[_currentFloor->polygon[i].vertexID[j]].y);
286 }
287 g_sludge->_gfxMan->drawLine(_currentFloor->vertex[_currentFloor->polygon[i].vertexID[0]].x, _currentFloor->vertex[_currentFloor->polygon[i].vertexID[0]].y,
288 _currentFloor->vertex[_currentFloor->polygon[i].vertexID[nV - 1]].x, _currentFloor->vertex[_currentFloor->polygon[i].vertexID[nV - 1]].y);
289 }
290 }
291 }
292
inFloor(int x,int y)293 int FloorManager::inFloor(int x, int y) {
294 int i, r = -1;
295
296 for (i = 0; i < _currentFloor->numPolygons; i++)
297 if (pointInFloorPolygon(_currentFloor->polygon[i], x, y))
298 r = i;
299
300 return r;
301 }
302
closestPointOnLine(int & closestX,int & closestY,int x1,int y1,int x2,int y2,int xP,int yP)303 bool FloorManager::closestPointOnLine(int &closestX, int &closestY, int x1, int y1, int x2, int y2, int xP, int yP) {
304 int xDiff = x2 - x1;
305 int yDiff = y2 - y1;
306
307 double m = xDiff * (xP - x1) + yDiff * (yP - y1);
308 m /= (xDiff * xDiff) + (yDiff * yDiff);
309
310 if (m < 0) {
311 closestX = x1;
312 closestY = y1;
313 } else if (m > 1) {
314 closestX = x2;
315 closestY = y2;
316 } else {
317 closestX = x1 + m * xDiff;
318 closestY = y1 + m * yDiff;
319 return true;
320 }
321 return false;
322 }
323
handleClosestPoint(int & setX,int & setY,int & setPoly)324 bool FloorManager::handleClosestPoint(int &setX, int &setY, int &setPoly) {
325 int gotX = 320, gotY = 200, gotPoly = -1, i, j, xTest1, yTest1, xTest2, yTest2, closestX, closestY, oldJ, currentDistance = 0xFFFFF, thisDistance;
326
327 for (i = 0; i < _currentFloor->numPolygons; i++) {
328 oldJ = _currentFloor->polygon[i].numVertices - 1;
329 for (j = 0; j < _currentFloor->polygon[i].numVertices; j++) {
330 xTest1 = _currentFloor->vertex[_currentFloor->polygon[i].vertexID[j]].x;
331 yTest1 = _currentFloor->vertex[_currentFloor->polygon[i].vertexID[j]].y;
332 xTest2 = _currentFloor->vertex[_currentFloor->polygon[i].vertexID[oldJ]].x;
333 yTest2 = _currentFloor->vertex[_currentFloor->polygon[i].vertexID[oldJ]].y;
334 closestPointOnLine(closestX, closestY, xTest1, yTest1, xTest2, yTest2, setX, setY);
335 xTest1 = setX - closestX;
336 yTest1 = setY - closestY;
337 thisDistance = xTest1 * xTest1 + yTest1 * yTest1;
338
339 if (thisDistance < currentDistance) {
340 currentDistance = thisDistance;
341 gotX = closestX;
342 gotY = closestY;
343 gotPoly = i;
344 }
345 oldJ = j;
346 }
347 }
348
349 if (gotPoly == -1)
350 return false;
351 setX = gotX;
352 setY = gotY;
353 setPoly = gotPoly;
354 return true;
355 }
356
doBorderStuff(OnScreenPerson * moveMe)357 bool FloorManager::doBorderStuff(OnScreenPerson *moveMe) {
358 if (moveMe->inPoly == moveMe->walkToPoly) {
359 moveMe->inPoly = -1;
360 moveMe->thisStepX = moveMe->walkToX;
361 moveMe->thisStepY = moveMe->walkToY;
362 } else {
363 // The section in which we need to be next...
364 int newPoly = _currentFloor->matrix[moveMe->inPoly][moveMe->walkToPoly];
365 if (newPoly == -1)
366 return false;
367
368 // Grab the index of the second matching corner...
369 int ID, ID2;
370 if (!getMatchingCorners(_currentFloor->polygon[moveMe->inPoly], _currentFloor->polygon[newPoly], ID, ID2))
371 return fatal("Not a valid floor plan!");
372
373 // Remember that we're walking to the new polygon...
374 moveMe->inPoly = newPoly;
375
376 // Calculate the destination position on the coincidantal line...
377 int x1 = moveMe->x, y1 = moveMe->y;
378 int x2 = moveMe->walkToX, y2 = moveMe->walkToY;
379 int x3 = _currentFloor->vertex[ID].x, y3 = _currentFloor->vertex[ID].y;
380 int x4 = _currentFloor->vertex[ID2].x, y4 = _currentFloor->vertex[ID2].y;
381
382 int xAB = x1 - x2;
383 int yAB = y1 - y2;
384 int xCD = x4 - x3;
385 int yCD = y4 - y3;
386
387 double m = (yAB * (x3 - x1) - xAB * (y3 - y1));
388 m /= ((xAB * yCD) - (yAB * xCD));
389
390 if (m > 0 && m < 1) {
391 moveMe->thisStepX = x3 + m * xCD;
392 moveMe->thisStepY = y3 + m * yCD;
393 } else {
394 int dx13 = x1 - x3, dx14 = x1 - x4, dx23 = x2 - x3, dx24 = x2 - x4;
395 int dy13 = y1 - y3, dy14 = y1 - y4, dy23 = y2 - y3, dy24 = y2 - y4;
396
397 dx13 *= dx13;
398 dx14 *= dx14;
399 dx23 *= dx23;
400 dx24 *= dx24;
401 dy13 *= dy13;
402 dy14 *= dy14;
403 dy23 *= dy23;
404 dy24 *= dy24;
405
406 if (sqrt((double)dx13 + dy13) + sqrt((double)dx23 + dy23) < sqrt((double)dx14 + dy14) + sqrt((double)dx24 + dy24)) {
407 moveMe->thisStepX = x3;
408 moveMe->thisStepY = y3;
409 } else {
410 moveMe->thisStepX = x4;
411 moveMe->thisStepY = y4;
412 }
413 }
414 }
415
416 float yDiff = moveMe->thisStepY - moveMe->y;
417 float xDiff = moveMe->x - moveMe->thisStepX;
418 if (xDiff || yDiff) {
419 moveMe->wantAngle = 180 + ANGLEFIX * atan2(xDiff, yDiff * 2);
420 moveMe->spinning = true;
421 }
422
423 moveMe->makeTalker();
424 return true;
425 }
426
save(Common::WriteStream * stream)427 void FloorManager::save(Common::WriteStream *stream) {
428 if (_currentFloor->numPolygons) {
429 stream->writeByte(1);
430 stream->writeUint16BE(_currentFloor->originalNum);
431 } else {
432 stream->writeByte(0);
433 }
434 }
435
load(Common::SeekableReadStream * stream)436 bool FloorManager::load(Common::SeekableReadStream *stream) {
437 if (stream->readByte()) {
438 if (!setFloor(stream->readUint16BE()))
439 return false;
440 } else {
441 setFloorNull();
442 }
443 return true;
444 }
445
446 } // End of namespace Sludge
447